packetfu 1.1.8 → 1.1.9

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