packetfu 1.1.10 → 1.1.11

Sign up to get free protection for your applications and to get access to all the features.
Files changed (98) hide show
  1. checksums.yaml +7 -0
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +2 -0
  4. data/.gitignore +3 -0
  5. data/.travis.yml +8 -0
  6. data/CONTRIBUTING.md +47 -0
  7. data/Gemfile +4 -0
  8. data/LICENSE.txt +1 -1
  9. data/README.rdoc +35 -30
  10. data/Rakefile +4 -4
  11. data/bench/octets.rb +9 -9
  12. data/examples/100kpackets.rb +12 -12
  13. data/examples/ackscan.rb +16 -16
  14. data/examples/arp.rb +35 -35
  15. data/examples/arphood.rb +36 -36
  16. data/examples/dissect_thinger.rb +6 -6
  17. data/examples/new-simple-stats.rb +23 -23
  18. data/examples/packetfu-shell.rb +25 -25
  19. data/examples/simple-sniffer.rb +9 -9
  20. data/examples/simple-stats.rb +23 -23
  21. data/examples/slammer.rb +3 -3
  22. data/gem-public_cert.pem +21 -0
  23. data/lib/packetfu.rb +149 -127
  24. data/lib/packetfu/capture.rb +169 -169
  25. data/lib/packetfu/config.rb +52 -52
  26. data/lib/packetfu/inject.rb +56 -56
  27. data/lib/packetfu/packet.rb +531 -528
  28. data/lib/packetfu/pcap.rb +579 -579
  29. data/lib/packetfu/protos/arp.rb +90 -90
  30. data/lib/packetfu/protos/arp/header.rb +158 -158
  31. data/lib/packetfu/protos/arp/mixin.rb +36 -36
  32. data/lib/packetfu/protos/eth.rb +44 -44
  33. data/lib/packetfu/protos/eth/header.rb +243 -243
  34. data/lib/packetfu/protos/eth/mixin.rb +3 -3
  35. data/lib/packetfu/protos/hsrp.rb +69 -69
  36. data/lib/packetfu/protos/hsrp/header.rb +107 -107
  37. data/lib/packetfu/protos/hsrp/mixin.rb +29 -29
  38. data/lib/packetfu/protos/icmp.rb +71 -71
  39. data/lib/packetfu/protos/icmp/header.rb +82 -82
  40. data/lib/packetfu/protos/icmp/mixin.rb +14 -14
  41. data/lib/packetfu/protos/invalid.rb +49 -49
  42. data/lib/packetfu/protos/ip.rb +69 -69
  43. data/lib/packetfu/protos/ip/header.rb +291 -291
  44. data/lib/packetfu/protos/ip/mixin.rb +40 -40
  45. data/lib/packetfu/protos/ipv6.rb +50 -50
  46. data/lib/packetfu/protos/ipv6/header.rb +188 -188
  47. data/lib/packetfu/protos/ipv6/mixin.rb +29 -29
  48. data/lib/packetfu/protos/tcp.rb +176 -176
  49. data/lib/packetfu/protos/tcp/ecn.rb +35 -35
  50. data/lib/packetfu/protos/tcp/flags.rb +74 -74
  51. data/lib/packetfu/protos/tcp/header.rb +268 -268
  52. data/lib/packetfu/protos/tcp/hlen.rb +32 -32
  53. data/lib/packetfu/protos/tcp/mixin.rb +46 -46
  54. data/lib/packetfu/protos/tcp/option.rb +321 -321
  55. data/lib/packetfu/protos/tcp/options.rb +95 -95
  56. data/lib/packetfu/protos/tcp/reserved.rb +35 -35
  57. data/lib/packetfu/protos/udp.rb +159 -123
  58. data/lib/packetfu/protos/udp/header.rb +91 -91
  59. data/lib/packetfu/protos/udp/mixin.rb +3 -3
  60. data/lib/packetfu/structfu.rb +280 -280
  61. data/lib/packetfu/utils.rb +292 -225
  62. data/lib/packetfu/version.rb +41 -41
  63. data/packetfu.gemspec +14 -3
  64. data/spec/arp_spec.rb +191 -0
  65. data/spec/eth_spec.rb +148 -0
  66. data/spec/icmp_spec.rb +97 -0
  67. data/spec/ip_spec.rb +78 -0
  68. data/spec/ipv6_spec.rb +81 -0
  69. data/spec/packet_spec.rb +61 -59
  70. data/spec/packet_subclasses_spec.rb +9 -10
  71. data/spec/packetfu_spec.rb +55 -62
  72. data/spec/sample3.pcap +0 -0
  73. data/spec/spec_helper.rb +44 -0
  74. data/spec/structfu_spec.rb +270 -271
  75. data/spec/tcp_spec.rb +76 -77
  76. data/spec/udp_spec.rb +32 -0
  77. data/spec/utils_spec.rb +95 -0
  78. data/test/all_tests.rb +14 -17
  79. data/test/func_lldp.rb +3 -3
  80. data/test/ptest.rb +2 -2
  81. data/test/test_capture.rb +45 -45
  82. data/test/test_eth.rb +70 -68
  83. data/test/test_hsrp.rb +9 -9
  84. data/test/test_inject.rb +18 -18
  85. data/test/test_invalid.rb +16 -16
  86. data/test/test_octets.rb +23 -21
  87. data/test/test_packet.rb +156 -154
  88. data/test/test_pcap.rb +172 -170
  89. data/test/test_structfu.rb +99 -97
  90. data/test/test_tcp.rb +322 -320
  91. data/test/test_udp.rb +78 -76
  92. metadata +108 -44
  93. metadata.gz.sig +2 -0
  94. data/spec/ethpacket_spec.rb +0 -74
  95. data/test/test_arp.rb +0 -135
  96. data/test/test_icmp.rb +0 -62
  97. data/test/test_ip.rb +0 -50
  98. data/test/test_ip6.rb +0 -68
@@ -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