packetfu 1.1.8 → 1.1.9

Sign up to get free protection for your applications and to get access to all the features.
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