packetfu 1.1.9 → 1.1.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. data/bench/octets.rb +9 -9
  2. data/examples/100kpackets.rb +12 -12
  3. data/examples/ackscan.rb +16 -16
  4. data/examples/arp.rb +35 -35
  5. data/examples/arphood.rb +36 -36
  6. data/examples/dissect_thinger.rb +6 -6
  7. data/examples/new-simple-stats.rb +23 -23
  8. data/examples/packetfu-shell.rb +25 -25
  9. data/examples/simple-sniffer.rb +9 -9
  10. data/examples/simple-stats.rb +23 -23
  11. data/examples/slammer.rb +3 -3
  12. data/lib/packetfu.rb +127 -127
  13. data/lib/packetfu/capture.rb +169 -169
  14. data/lib/packetfu/config.rb +52 -52
  15. data/lib/packetfu/inject.rb +56 -56
  16. data/lib/packetfu/packet.rb +528 -528
  17. data/lib/packetfu/pcap.rb +579 -579
  18. data/lib/packetfu/protos/arp.rb +90 -90
  19. data/lib/packetfu/protos/arp/header.rb +158 -158
  20. data/lib/packetfu/protos/arp/mixin.rb +36 -36
  21. data/lib/packetfu/protos/eth.rb +44 -44
  22. data/lib/packetfu/protos/eth/header.rb +243 -243
  23. data/lib/packetfu/protos/eth/mixin.rb +3 -3
  24. data/lib/packetfu/protos/hsrp.rb +69 -69
  25. data/lib/packetfu/protos/hsrp/header.rb +107 -107
  26. data/lib/packetfu/protos/hsrp/mixin.rb +29 -29
  27. data/lib/packetfu/protos/icmp.rb +71 -71
  28. data/lib/packetfu/protos/icmp/header.rb +82 -82
  29. data/lib/packetfu/protos/icmp/mixin.rb +14 -14
  30. data/lib/packetfu/protos/invalid.rb +49 -49
  31. data/lib/packetfu/protos/ip.rb +69 -69
  32. data/lib/packetfu/protos/ip/header.rb +291 -291
  33. data/lib/packetfu/protos/ip/mixin.rb +40 -40
  34. data/lib/packetfu/protos/ipv6.rb +50 -50
  35. data/lib/packetfu/protos/ipv6/header.rb +188 -188
  36. data/lib/packetfu/protos/ipv6/mixin.rb +29 -29
  37. data/lib/packetfu/protos/tcp.rb +176 -176
  38. data/lib/packetfu/protos/tcp/ecn.rb +35 -35
  39. data/lib/packetfu/protos/tcp/flags.rb +74 -74
  40. data/lib/packetfu/protos/tcp/header.rb +268 -268
  41. data/lib/packetfu/protos/tcp/hlen.rb +32 -32
  42. data/lib/packetfu/protos/tcp/mixin.rb +46 -46
  43. data/lib/packetfu/protos/tcp/option.rb +321 -321
  44. data/lib/packetfu/protos/tcp/options.rb +95 -95
  45. data/lib/packetfu/protos/tcp/reserved.rb +35 -35
  46. data/lib/packetfu/protos/udp.rb +116 -116
  47. data/lib/packetfu/protos/udp/header.rb +91 -91
  48. data/lib/packetfu/protos/udp/mixin.rb +3 -3
  49. data/lib/packetfu/structfu.rb +280 -280
  50. data/lib/packetfu/utils.rb +226 -217
  51. data/lib/packetfu/version.rb +41 -41
  52. data/packetfu.gemspec +2 -1
  53. data/spec/ethpacket_spec.rb +48 -48
  54. data/spec/packet_spec.rb +57 -57
  55. data/spec/packet_subclasses_spec.rb +8 -8
  56. data/spec/packetfu_spec.rb +59 -59
  57. data/spec/structfu_spec.rb +268 -268
  58. data/spec/tcp_spec.rb +75 -75
  59. data/test/all_tests.rb +13 -13
  60. data/test/func_lldp.rb +3 -3
  61. data/test/ptest.rb +2 -2
  62. data/test/test_arp.rb +116 -116
  63. data/test/test_capture.rb +45 -45
  64. data/test/test_eth.rb +68 -68
  65. data/test/test_hsrp.rb +9 -9
  66. data/test/test_icmp.rb +52 -52
  67. data/test/test_inject.rb +18 -18
  68. data/test/test_invalid.rb +16 -16
  69. data/test/test_ip.rb +36 -36
  70. data/test/test_ip6.rb +48 -48
  71. data/test/test_octets.rb +21 -21
  72. data/test/test_packet.rb +154 -154
  73. data/test/test_pcap.rb +170 -170
  74. data/test/test_structfu.rb +97 -97
  75. data/test/test_tcp.rb +320 -320
  76. data/test/test_udp.rb +76 -76
  77. metadata +4 -3
@@ -1,41 +1,41 @@
1
1
  # -*- coding: binary -*-
2
2
  module PacketFu
3
3
  # Implements the Header Length for TCPHeader.
4
- #
5
- # ==== Header Definition
6
- #
7
- # Fixnum (4 bits) :hlen
8
- class TcpHlen < Struct.new(:hlen)
9
-
10
- include StructFu
4
+ #
5
+ # ==== Header Definition
6
+ #
7
+ # Fixnum (4 bits) :hlen
8
+ class TcpHlen < Struct.new(:hlen)
9
+
10
+ include StructFu
11
11
 
12
- def initialize(args={})
13
- super(args[:hlen])
14
- end
12
+ def initialize(args={})
13
+ super(args[:hlen])
14
+ end
15
15
 
16
- # Returns the TcpHlen field as an integer. Note these will become the high
17
- # bits at the TCP header's offset, even though the lower 4 bits
18
- # will be further chopped up.
19
- def to_i
20
- hlen.to_i & 0b1111
21
- end
16
+ # Returns the TcpHlen field as an integer. Note these will become the high
17
+ # bits at the TCP header's offset, even though the lower 4 bits
18
+ # will be further chopped up.
19
+ def to_i
20
+ hlen.to_i & 0b1111
21
+ end
22
22
 
23
- # Reads a string to populate the object.
24
- def read(str)
25
- force_binary(str)
26
- return self if str.nil? || str.size.zero?
27
- if 1.respond_to? :ord
28
- self[:hlen] = (str[0].ord & 0b11110000) >> 4
29
- else
30
- self[:hlen] = (str[0] & 0b11110000) >> 4
31
- end
32
- self
33
- end
23
+ # Reads a string to populate the object.
24
+ def read(str)
25
+ force_binary(str)
26
+ return self if str.nil? || str.size.zero?
27
+ if 1.respond_to? :ord
28
+ self[:hlen] = (str[0].ord & 0b11110000) >> 4
29
+ else
30
+ self[:hlen] = (str[0] & 0b11110000) >> 4
31
+ end
32
+ self
33
+ end
34
34
 
35
- # Returns the object in string form.
36
- def to_s
37
- [self.to_i].pack("C")
38
- end
35
+ # Returns the object in string form.
36
+ def to_s
37
+ [self.to_i].pack("C")
38
+ end
39
39
 
40
- end
40
+ end
41
41
  end
@@ -1,49 +1,49 @@
1
1
  # -*- coding: binary -*-
2
2
  module PacketFu
3
- # This Mixin simplifies access to the TCPHeaders. Mix this in with your
4
- # packet interface, and it will add methods that essentially delegate to
5
- # the 'tcp_header' method (assuming that it is a TCPHeader object)
6
- module TCPHeaderMixin
7
- def tcp_src=(v); self.tcp_header.tcp_src= v; end
8
- def tcp_src; self.tcp_header.tcp_src; end
9
- def tcp_dst=(v); self.tcp_header.tcp_dst= v; end
10
- def tcp_dst; self.tcp_header.tcp_dst; end
11
- def tcp_seq=(v); self.tcp_header.tcp_seq= v; end
12
- def tcp_seq; self.tcp_header.tcp_seq; end
13
- def tcp_ack=(v); self.tcp_header.tcp_ack= v; end
14
- def tcp_ack; self.tcp_header.tcp_ack; end
15
- def tcp_win=(v); self.tcp_header.tcp_win= v; end
16
- def tcp_win; self.tcp_header.tcp_win; end
17
- def tcp_sum=(v); self.tcp_header.tcp_sum= v; end
18
- def tcp_sum; self.tcp_header.tcp_sum; end
19
- def tcp_urg=(v); self.tcp_header.tcp_urg= v; end
20
- def tcp_urg; self.tcp_header.tcp_urg; end
21
- def tcp_hlen; self.tcp_header.tcp_hlen; end
22
- def tcp_hlen=(v); self.tcp_header.tcp_hlen= v; end
23
- def tcp_reserved; self.tcp_header.tcp_reserved; end
24
- def tcp_reserved=(v); self.tcp_header.tcp_reserved= v; end
25
- def tcp_ecn; self.tcp_header.tcp_ecn; end
26
- def tcp_ecn=(v); self.tcp_header.tcp_ecn= v; end
27
- def tcp_opts; self.tcp_header.tcp_opts; end
28
- def tcp_opts=(v); self.tcp_header.tcp_opts= v; end
29
- def tcp_calc_seq; self.tcp_header.tcp_calc_seq; end
30
- def tcp_calc_src; self.tcp_header.tcp_calc_src; end
31
- def tcp_opts_len; self.tcp_header.tcp_opts_len; end
32
- def tcp_calc_hlen; self.tcp_header.tcp_calc_hlen; end
33
- def tcp_options; self.tcp_header.tcp_options; end
34
- def tcp_flags_dotmap; self.tcp_header.tcp_flags_dotmap; end
35
- def tcp_options=(v); self.tcp_header.tcp_options= v; end
36
- def tcp_sport; self.tcp_header.tcp_sport; end
37
- def tcp_sport=(v); self.tcp_header.tcp_sport= v; end
38
- def tcp_dport; self.tcp_header.tcp_dport; end
39
- def tcp_dport=(v); self.tcp_header.tcp_dport= v; end
40
- def tcp_recalc(*v); self.tcp_header.tcp_recalc(*v); end
41
- def tcp_flags_readable; self.tcp_header.tcp_flags_readable; end
42
- def tcp_ack_readable; self.tcp_header.tcp_ack_readable; end
43
- def tcp_seq_readable; self.tcp_header.tcp_seq_readable; end
44
- def tcp_sum_readable; self.tcp_header.tcp_sum_readable; end
45
- def tcp_opts_readable; self.tcp_header.tcp_opts_readable; end
46
- def tcp_flags; self.tcp_header.tcp_flags; end
47
- def tcp_flags=(v); self.tcp_header.tcp_flags= v; end
48
- end
3
+ # This Mixin simplifies access to the TCPHeaders. Mix this in with your
4
+ # packet interface, and it will add methods that essentially delegate to
5
+ # the 'tcp_header' method (assuming that it is a TCPHeader object)
6
+ module TCPHeaderMixin
7
+ def tcp_src=(v); self.tcp_header.tcp_src= v; end
8
+ def tcp_src; self.tcp_header.tcp_src; end
9
+ def tcp_dst=(v); self.tcp_header.tcp_dst= v; end
10
+ def tcp_dst; self.tcp_header.tcp_dst; end
11
+ def tcp_seq=(v); self.tcp_header.tcp_seq= v; end
12
+ def tcp_seq; self.tcp_header.tcp_seq; end
13
+ def tcp_ack=(v); self.tcp_header.tcp_ack= v; end
14
+ def tcp_ack; self.tcp_header.tcp_ack; end
15
+ def tcp_win=(v); self.tcp_header.tcp_win= v; end
16
+ def tcp_win; self.tcp_header.tcp_win; end
17
+ def tcp_sum=(v); self.tcp_header.tcp_sum= v; end
18
+ def tcp_sum; self.tcp_header.tcp_sum; end
19
+ def tcp_urg=(v); self.tcp_header.tcp_urg= v; end
20
+ def tcp_urg; self.tcp_header.tcp_urg; end
21
+ def tcp_hlen; self.tcp_header.tcp_hlen; end
22
+ def tcp_hlen=(v); self.tcp_header.tcp_hlen= v; end
23
+ def tcp_reserved; self.tcp_header.tcp_reserved; end
24
+ def tcp_reserved=(v); self.tcp_header.tcp_reserved= v; end
25
+ def tcp_ecn; self.tcp_header.tcp_ecn; end
26
+ def tcp_ecn=(v); self.tcp_header.tcp_ecn= v; end
27
+ def tcp_opts; self.tcp_header.tcp_opts; end
28
+ def tcp_opts=(v); self.tcp_header.tcp_opts= v; end
29
+ def tcp_calc_seq; self.tcp_header.tcp_calc_seq; end
30
+ def tcp_calc_src; self.tcp_header.tcp_calc_src; end
31
+ def tcp_opts_len; self.tcp_header.tcp_opts_len; end
32
+ def tcp_calc_hlen; self.tcp_header.tcp_calc_hlen; end
33
+ def tcp_options; self.tcp_header.tcp_options; end
34
+ def tcp_flags_dotmap; self.tcp_header.tcp_flags_dotmap; end
35
+ def tcp_options=(v); self.tcp_header.tcp_options= v; end
36
+ def tcp_sport; self.tcp_header.tcp_sport; end
37
+ def tcp_sport=(v); self.tcp_header.tcp_sport= v; end
38
+ def tcp_dport; self.tcp_header.tcp_dport; end
39
+ def tcp_dport=(v); self.tcp_header.tcp_dport= v; end
40
+ def tcp_recalc(*v); self.tcp_header.tcp_recalc(*v); end
41
+ def tcp_flags_readable; self.tcp_header.tcp_flags_readable; end
42
+ def tcp_ack_readable; self.tcp_header.tcp_ack_readable; end
43
+ def tcp_seq_readable; self.tcp_header.tcp_seq_readable; end
44
+ def tcp_sum_readable; self.tcp_header.tcp_sum_readable; end
45
+ def tcp_opts_readable; self.tcp_header.tcp_opts_readable; end
46
+ def tcp_flags; self.tcp_header.tcp_flags; end
47
+ def tcp_flags=(v); self.tcp_header.tcp_flags= v; end
48
+ end
49
49
  end
@@ -1,324 +1,324 @@
1
1
  # -*- coding: binary -*-
2
2
  module PacketFu
3
- # TcpOption is the base class for all TCP options. Note that TcpOption#len
4
- # returns the size of the entire option, while TcpOption#optlen is the struct
5
- # for the TCP Option Length field.
6
- #
7
- # Subclassed options should set the correct TcpOption#kind by redefining
8
- # initialize. They should also deal with various value types there by setting
9
- # them explicitly with an accompanying StructFu#typecast for the setter.
10
- #
11
- # By default, values are presumed to be strings, unless they are Numeric, in
12
- # which case a guess is made to the width of the Numeric based on the given
13
- # optlen.
14
- #
15
- # Note that normally, optlen is /not/ enforced for directly setting values,
16
- # so the user is perfectly capable of setting incorrect lengths.
17
- class TcpOption < Struct.new(:kind, :optlen, :value)
18
-
19
- include StructFu
20
-
21
- def initialize(args={})
22
- super(
23
- Int8.new(args[:kind]),
24
- Int8.new(args[:optlen])
25
- )
26
- if args[:value].kind_of? Numeric
27
- self[:value] = case args[:optlen]
28
- when 3; Int8.new(args[:value])
29
- when 4; Int16.new(args[:value])
30
- when 6; Int32.new(args[:value])
31
- else; StructFu::String.new.read(args[:value])
32
- end
33
- else
34
- self[:value] = StructFu::String.new.read(args[:value])
35
- end
36
- end
37
-
38
- # Returns the object in string form.
39
- def to_s
40
- self[:kind].to_s +
41
- (self[:optlen].value.nil? ? nil : self[:optlen]).to_s +
42
- (self[:value].nil? ? nil : self[:value]).to_s
43
- end
44
-
45
- # Reads a string to populate the object.
46
- def read(str)
47
- force_binary(str)
48
- return self if str.nil?
49
- self[:kind].read(str[0,1])
50
- if str[1,1]
51
- self[:optlen].read(str[1,1])
52
- if str[2,1] && optlen.value > 2
53
- self[:value].read(str[2,optlen.value-2])
54
- end
55
- end
56
- self
57
- end
58
-
59
- # The default decode for an unknown option. Known options should redefine this.
60
- def decode
61
- unk = "unk-#{self.kind.to_i}"
62
- (self[:optlen].to_i > 2 && self[:value].to_s.size > 1) ? [unk,self[:value]].join(":") : unk
63
- end
64
-
65
- # Setter for the "kind" byte of this option.
66
- def kind=(i); typecast i; end
67
- # Setter for the "option length" byte for this option.
68
- def optlen=(i); typecast i; end
69
-
70
- # Setter for the value of this option.
71
- def value=(i)
72
- if i.kind_of? Numeric
73
- typecast i
74
- elsif i.respond_to? :to_s
75
- self[:value] = i
76
- else
77
- self[:value] = ''
78
- end
79
- end
80
-
81
- # Generally, encoding a value is going to be just a read. Some
82
- # options will treat things a little differently; TS for example,
83
- # takes two values and concatenates them.
84
- def encode(str)
85
- self[:value] = self.class.new(:value => str).value
86
- end
87
-
88
- # Returns true if this option has an optlen. Some don't.
89
- def has_optlen?
90
- (kind.value && kind.value < 2) ? false : true
91
- end
92
-
93
- # Returns true if this option has a value. Some don't.
94
- def has_value?
95
- (value.respond_to? :to_s && value.to_s.size > 0) ? false : true
96
- end
97
-
98
- # End of Line option. Usually used to terminate a string of options.
99
- #
100
- # http://www.networksorcery.com/enp/protocol/tcp/option000.htm
101
- class EOL < TcpOption
102
- def initialize(args={})
103
- super(
104
- args.merge(:kind => 0)
105
- )
106
- end
107
-
108
- def decode
109
- "EOL"
110
- end
111
-
112
- end
113
-
114
- # No Operation option. Usually used to pad out options to fit a 4-byte alignment.
115
- #
116
- # http://www.networksorcery.com/enp/protocol/tcp/option001.htm
117
- class NOP < TcpOption
118
- def initialize(args={})
119
- super(
120
- args.merge(:kind => 1)
121
- )
122
- end
123
-
124
- def decode
125
- "NOP"
126
- end
127
-
128
- end
129
-
130
- # Maximum Segment Size option.
131
- #
132
- # http://www.networksorcery.com/enp/protocol/tcp/option002.htm
133
- class MSS < TcpOption
134
- def initialize(args={})
135
- super(
136
- args.merge(:kind => 2,
137
- :optlen => 4
138
- )
139
- )
140
- self[:value] = Int16.new(args[:value])
141
- end
142
-
143
- def value=(i); typecast i; end
144
-
145
- # MSS options with lengths other than 4 are malformed.
146
- def decode
147
- if self[:optlen].to_i == 4
148
- "MSS:#{self[:value].to_i}"
149
- else
150
- "MSS-bad:#{self[:value]}"
151
- end
152
- end
153
-
154
- end
155
-
156
- # Window Size option.
157
- #
158
- # http://www.networksorcery.com/enp/protocol/tcp/option003.htm
159
- class WS < TcpOption
160
- def initialize(args={})
161
- super(
162
- args.merge(:kind => 3,
163
- :optlen => 3
164
- )
165
- )
166
- self[:value] = Int8.new(args[:value])
167
- end
168
-
169
- def value=(i); typecast i; end
170
-
171
- # WS options with lengths other than 3 are malformed.
172
- def decode
173
- if self[:optlen].to_i == 3
174
- "WS:#{self[:value].to_i}"
175
- else
176
- "WS-bad:#{self[:value]}"
177
- end
178
- end
179
-
180
- end
181
-
182
- # Selective Acknowlegment OK option.
183
- #
184
- # http://www.networksorcery.com/enp/protocol/tcp/option004.htm
185
- class SACKOK < TcpOption
186
- def initialize(args={})
187
- super(
188
- args.merge(:kind => 4,
189
- :optlen => 2)
190
- )
191
- end
192
-
193
- # SACKOK options with sizes other than 2 are malformed.
194
- def decode
195
- if self[:optlen].to_i == 2
196
- "SACKOK"
197
- else
198
- "SACKOK-bad:#{self[:value]}"
199
- end
200
- end
201
-
202
- end
203
-
204
- # Selective Acknowledgement option.
205
- #
206
- # http://www.networksorcery.com/enp/protocol/tcp/option004.htm
207
- #
208
- # Note that SACK always takes its optlen from the size of the string.
209
- class SACK < TcpOption
210
- def initialize(args={})
211
- super(
212
- args.merge(:kind => 5,
213
- :optlen => ((args[:value] || "").size + 2)
214
- )
215
- )
216
- end
217
-
218
- def optlen=(i); typecast i; end
219
-
220
- def value=(i)
221
- self[:optlen] = Int8.new(i.to_s.size + 2)
222
- self[:value] = StructFu::String.new(i)
223
- end
224
-
225
- def decode
226
- "SACK:#{self[:value]}"
227
- end
228
-
229
- def encode(str)
230
- temp_obj = self.class.new(:value => str)
231
- self[:value] = temp_obj.value
232
- self[:optlen] = temp_obj.optlen.value
233
- self
234
- end
235
-
236
- end
237
-
238
- # Echo option.
239
- #
240
- # http://www.networksorcery.com/enp/protocol/tcp/option006.htm
241
- class ECHO < TcpOption
242
- def initialize(args={})
243
- super(
244
- args.merge(:kind => 6,
245
- :optlen => 6
246
- )
247
- )
248
- end
249
-
250
- # ECHO options with lengths other than 6 are malformed.
251
- def decode
252
- if self[:optlen].to_i == 6
253
- "ECHO:#{self[:value]}"
254
- else
255
- "ECHO-bad:#{self[:value]}"
256
- end
257
- end
258
-
259
- end
260
-
261
- # Echo Reply option.
262
- #
263
- # http://www.networksorcery.com/enp/protocol/tcp/option007.htm
264
- class ECHOREPLY < TcpOption
265
- def initialize(args={})
266
- super(
267
- args.merge(:kind => 7,
268
- :optlen => 6
269
- )
270
- )
271
- end
272
-
273
- # ECHOREPLY options with lengths other than 6 are malformed.
274
- def decode
275
- if self[:optlen].to_i == 6
276
- "ECHOREPLY:#{self[:value]}"
277
- else
278
- "ECHOREPLY-bad:#{self[:value]}"
279
- end
280
- end
281
-
282
- end
283
-
284
- # Timestamp option
285
- #
286
- # http://www.networksorcery.com/enp/protocol/tcp/option008.htm
287
- class TS < TcpOption
288
- def initialize(args={})
289
- super(
290
- args.merge(:kind => 8,
291
- :optlen => 10
292
- )
293
- )
294
- self[:value] = StructFu::String.new.read(args[:value] || "\x00" * 8)
295
- end
296
-
297
- # TS options with lengths other than 10 are malformed.
298
- def decode
299
- if self[:optlen].to_i == 10
300
- val1,val2 = self[:value].unpack("NN")
301
- "TS:#{val1};#{val2}"
302
- else
303
- "TS-bad:#{self[:value]}"
304
- end
305
- end
306
-
307
- # TS options are in the format of "TS:[timestamp value];[timestamp secret]" Both
308
- # should be written as decimal numbers.
309
- def encode(str)
310
- if str =~ /^([0-9]+);([0-9]+)$/
311
- tsval,tsecr = str.split(";").map {|x| x.to_i}
312
- if tsval <= 0xffffffff && tsecr <= 0xffffffff
313
- self[:value] = StructFu::String.new([tsval,tsecr].pack("NN"))
314
- else
315
- self[:value] = StructFu::String.new(str)
316
- end
317
- else
318
- self[:value] = StructFu::String.new(str)
319
- end
320
- end
321
-
322
- end
323
- end
3
+ # TcpOption is the base class for all TCP options. Note that TcpOption#len
4
+ # returns the size of the entire option, while TcpOption#optlen is the struct
5
+ # for the TCP Option Length field.
6
+ #
7
+ # Subclassed options should set the correct TcpOption#kind by redefining
8
+ # initialize. They should also deal with various value types there by setting
9
+ # them explicitly with an accompanying StructFu#typecast for the setter.
10
+ #
11
+ # By default, values are presumed to be strings, unless they are Numeric, in
12
+ # which case a guess is made to the width of the Numeric based on the given
13
+ # optlen.
14
+ #
15
+ # Note that normally, optlen is /not/ enforced for directly setting values,
16
+ # so the user is perfectly capable of setting incorrect lengths.
17
+ class TcpOption < Struct.new(:kind, :optlen, :value)
18
+
19
+ include StructFu
20
+
21
+ def initialize(args={})
22
+ super(
23
+ Int8.new(args[:kind]),
24
+ Int8.new(args[:optlen])
25
+ )
26
+ if args[:value].kind_of? Numeric
27
+ self[:value] = case args[:optlen]
28
+ when 3; Int8.new(args[:value])
29
+ when 4; Int16.new(args[:value])
30
+ when 6; Int32.new(args[:value])
31
+ else; StructFu::String.new.read(args[:value])
32
+ end
33
+ else
34
+ self[:value] = StructFu::String.new.read(args[:value])
35
+ end
36
+ end
37
+
38
+ # Returns the object in string form.
39
+ def to_s
40
+ self[:kind].to_s +
41
+ (self[:optlen].value.nil? ? nil : self[:optlen]).to_s +
42
+ (self[:value].nil? ? nil : self[:value]).to_s
43
+ end
44
+
45
+ # Reads a string to populate the object.
46
+ def read(str)
47
+ force_binary(str)
48
+ return self if str.nil?
49
+ self[:kind].read(str[0,1])
50
+ if str[1,1]
51
+ self[:optlen].read(str[1,1])
52
+ if str[2,1] && optlen.value > 2
53
+ self[:value].read(str[2,optlen.value-2])
54
+ end
55
+ end
56
+ self
57
+ end
58
+
59
+ # The default decode for an unknown option. Known options should redefine this.
60
+ def decode
61
+ unk = "unk-#{self.kind.to_i}"
62
+ (self[:optlen].to_i > 2 && self[:value].to_s.size > 1) ? [unk,self[:value]].join(":") : unk
63
+ end
64
+
65
+ # Setter for the "kind" byte of this option.
66
+ def kind=(i); typecast i; end
67
+ # Setter for the "option length" byte for this option.
68
+ def optlen=(i); typecast i; end
69
+
70
+ # Setter for the value of this option.
71
+ def value=(i)
72
+ if i.kind_of? Numeric
73
+ typecast i
74
+ elsif i.respond_to? :to_s
75
+ self[:value] = i
76
+ else
77
+ self[:value] = ''
78
+ end
79
+ end
80
+
81
+ # Generally, encoding a value is going to be just a read. Some
82
+ # options will treat things a little differently; TS for example,
83
+ # takes two values and concatenates them.
84
+ def encode(str)
85
+ self[:value] = self.class.new(:value => str).value
86
+ end
87
+
88
+ # Returns true if this option has an optlen. Some don't.
89
+ def has_optlen?
90
+ (kind.value && kind.value < 2) ? false : true
91
+ end
92
+
93
+ # Returns true if this option has a value. Some don't.
94
+ def has_value?
95
+ (value.respond_to? :to_s && value.to_s.size > 0) ? false : true
96
+ end
97
+
98
+ # End of Line option. Usually used to terminate a string of options.
99
+ #
100
+ # http://www.networksorcery.com/enp/protocol/tcp/option000.htm
101
+ class EOL < TcpOption
102
+ def initialize(args={})
103
+ super(
104
+ args.merge(:kind => 0)
105
+ )
106
+ end
107
+
108
+ def decode
109
+ "EOL"
110
+ end
111
+
112
+ end
113
+
114
+ # No Operation option. Usually used to pad out options to fit a 4-byte alignment.
115
+ #
116
+ # http://www.networksorcery.com/enp/protocol/tcp/option001.htm
117
+ class NOP < TcpOption
118
+ def initialize(args={})
119
+ super(
120
+ args.merge(:kind => 1)
121
+ )
122
+ end
123
+
124
+ def decode
125
+ "NOP"
126
+ end
127
+
128
+ end
129
+
130
+ # Maximum Segment Size option.
131
+ #
132
+ # http://www.networksorcery.com/enp/protocol/tcp/option002.htm
133
+ class MSS < TcpOption
134
+ def initialize(args={})
135
+ super(
136
+ args.merge(:kind => 2,
137
+ :optlen => 4
138
+ )
139
+ )
140
+ self[:value] = Int16.new(args[:value])
141
+ end
142
+
143
+ def value=(i); typecast i; end
144
+
145
+ # MSS options with lengths other than 4 are malformed.
146
+ def decode
147
+ if self[:optlen].to_i == 4
148
+ "MSS:#{self[:value].to_i}"
149
+ else
150
+ "MSS-bad:#{self[:value]}"
151
+ end
152
+ end
153
+
154
+ end
155
+
156
+ # Window Size option.
157
+ #
158
+ # http://www.networksorcery.com/enp/protocol/tcp/option003.htm
159
+ class WS < TcpOption
160
+ def initialize(args={})
161
+ super(
162
+ args.merge(:kind => 3,
163
+ :optlen => 3
164
+ )
165
+ )
166
+ self[:value] = Int8.new(args[:value])
167
+ end
168
+
169
+ def value=(i); typecast i; end
170
+
171
+ # WS options with lengths other than 3 are malformed.
172
+ def decode
173
+ if self[:optlen].to_i == 3
174
+ "WS:#{self[:value].to_i}"
175
+ else
176
+ "WS-bad:#{self[:value]}"
177
+ end
178
+ end
179
+
180
+ end
181
+
182
+ # Selective Acknowlegment OK option.
183
+ #
184
+ # http://www.networksorcery.com/enp/protocol/tcp/option004.htm
185
+ class SACKOK < TcpOption
186
+ def initialize(args={})
187
+ super(
188
+ args.merge(:kind => 4,
189
+ :optlen => 2)
190
+ )
191
+ end
192
+
193
+ # SACKOK options with sizes other than 2 are malformed.
194
+ def decode
195
+ if self[:optlen].to_i == 2
196
+ "SACKOK"
197
+ else
198
+ "SACKOK-bad:#{self[:value]}"
199
+ end
200
+ end
201
+
202
+ end
203
+
204
+ # Selective Acknowledgement option.
205
+ #
206
+ # http://www.networksorcery.com/enp/protocol/tcp/option004.htm
207
+ #
208
+ # Note that SACK always takes its optlen from the size of the string.
209
+ class SACK < TcpOption
210
+ def initialize(args={})
211
+ super(
212
+ args.merge(:kind => 5,
213
+ :optlen => ((args[:value] || "").size + 2)
214
+ )
215
+ )
216
+ end
217
+
218
+ def optlen=(i); typecast i; end
219
+
220
+ def value=(i)
221
+ self[:optlen] = Int8.new(i.to_s.size + 2)
222
+ self[:value] = StructFu::String.new(i)
223
+ end
224
+
225
+ def decode
226
+ "SACK:#{self[:value]}"
227
+ end
228
+
229
+ def encode(str)
230
+ temp_obj = self.class.new(:value => str)
231
+ self[:value] = temp_obj.value
232
+ self[:optlen] = temp_obj.optlen.value
233
+ self
234
+ end
235
+
236
+ end
237
+
238
+ # Echo option.
239
+ #
240
+ # http://www.networksorcery.com/enp/protocol/tcp/option006.htm
241
+ class ECHO < TcpOption
242
+ def initialize(args={})
243
+ super(
244
+ args.merge(:kind => 6,
245
+ :optlen => 6
246
+ )
247
+ )
248
+ end
249
+
250
+ # ECHO options with lengths other than 6 are malformed.
251
+ def decode
252
+ if self[:optlen].to_i == 6
253
+ "ECHO:#{self[:value]}"
254
+ else
255
+ "ECHO-bad:#{self[:value]}"
256
+ end
257
+ end
258
+
259
+ end
260
+
261
+ # Echo Reply option.
262
+ #
263
+ # http://www.networksorcery.com/enp/protocol/tcp/option007.htm
264
+ class ECHOREPLY < TcpOption
265
+ def initialize(args={})
266
+ super(
267
+ args.merge(:kind => 7,
268
+ :optlen => 6
269
+ )
270
+ )
271
+ end
272
+
273
+ # ECHOREPLY options with lengths other than 6 are malformed.
274
+ def decode
275
+ if self[:optlen].to_i == 6
276
+ "ECHOREPLY:#{self[:value]}"
277
+ else
278
+ "ECHOREPLY-bad:#{self[:value]}"
279
+ end
280
+ end
281
+
282
+ end
283
+
284
+ # Timestamp option
285
+ #
286
+ # http://www.networksorcery.com/enp/protocol/tcp/option008.htm
287
+ class TS < TcpOption
288
+ def initialize(args={})
289
+ super(
290
+ args.merge(:kind => 8,
291
+ :optlen => 10
292
+ )
293
+ )
294
+ self[:value] = StructFu::String.new.read(args[:value] || "\x00" * 8)
295
+ end
296
+
297
+ # TS options with lengths other than 10 are malformed.
298
+ def decode
299
+ if self[:optlen].to_i == 10
300
+ val1,val2 = self[:value].unpack("NN")
301
+ "TS:#{val1};#{val2}"
302
+ else
303
+ "TS-bad:#{self[:value]}"
304
+ end
305
+ end
306
+
307
+ # TS options are in the format of "TS:[timestamp value];[timestamp secret]" Both
308
+ # should be written as decimal numbers.
309
+ def encode(str)
310
+ if str =~ /^([0-9]+);([0-9]+)$/
311
+ tsval,tsecr = str.split(";").map {|x| x.to_i}
312
+ if tsval <= 0xffffffff && tsecr <= 0xffffffff
313
+ self[:value] = StructFu::String.new([tsval,tsecr].pack("NN"))
314
+ else
315
+ self[:value] = StructFu::String.new(str)
316
+ end
317
+ else
318
+ self[:value] = StructFu::String.new(str)
319
+ end
320
+ end
321
+
322
+ end
323
+ end
324
324
  end