packetfu 1.0.0

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 (52) hide show
  1. data/.document +4 -0
  2. data/CHANGES +36 -0
  3. data/INSTALL +40 -0
  4. data/LICENSE +28 -0
  5. data/README +25 -0
  6. data/TODO +25 -0
  7. data/examples/ackscan.rb +38 -0
  8. data/examples/arp.rb +60 -0
  9. data/examples/arphood.rb +56 -0
  10. data/examples/ethernet.rb +10 -0
  11. data/examples/examples.rb +3 -0
  12. data/examples/ids.rb +4 -0
  13. data/examples/idsv2.rb +6 -0
  14. data/examples/oui.txt +84177 -0
  15. data/examples/packetfu-shell.rb +111 -0
  16. data/examples/simple-stats.rb +42 -0
  17. data/examples/slammer.rb +33 -0
  18. data/examples/uniqpcap.rb +15 -0
  19. data/lib/packetfu.rb +108 -0
  20. data/lib/packetfu/arp.rb +239 -0
  21. data/lib/packetfu/capture.rb +169 -0
  22. data/lib/packetfu/config.rb +55 -0
  23. data/lib/packetfu/eth.rb +264 -0
  24. data/lib/packetfu/icmp.rb +153 -0
  25. data/lib/packetfu/inject.rb +65 -0
  26. data/lib/packetfu/invalid.rb +41 -0
  27. data/lib/packetfu/ip.rb +318 -0
  28. data/lib/packetfu/ipv6.rb +230 -0
  29. data/lib/packetfu/packet.rb +492 -0
  30. data/lib/packetfu/pcap.rb +502 -0
  31. data/lib/packetfu/structfu.rb +274 -0
  32. data/lib/packetfu/tcp.rb +1061 -0
  33. data/lib/packetfu/udp.rb +210 -0
  34. data/lib/packetfu/utils.rb +182 -0
  35. data/test/all_tests.rb +37 -0
  36. data/test/ptest.rb +10 -0
  37. data/test/sample.pcap +0 -0
  38. data/test/sample2.pcap +0 -0
  39. data/test/test_arp.rb +135 -0
  40. data/test/test_eth.rb +90 -0
  41. data/test/test_icmp.rb +54 -0
  42. data/test/test_inject.rb +33 -0
  43. data/test/test_invalid.rb +28 -0
  44. data/test/test_ip.rb +69 -0
  45. data/test/test_ip6.rb +68 -0
  46. data/test/test_octets.rb +37 -0
  47. data/test/test_packet.rb +41 -0
  48. data/test/test_pcap.rb +210 -0
  49. data/test/test_structfu.rb +112 -0
  50. data/test/test_tcp.rb +327 -0
  51. data/test/test_udp.rb +73 -0
  52. metadata +144 -0
@@ -0,0 +1,274 @@
1
+ # StructFu, a nifty way to leverage Ruby's built in Struct class
2
+ # to create meaningful binary data.
3
+
4
+ module StructFu
5
+ # Normally, self.size and self.length will refer to the Struct
6
+ # size as an array. It's a hassle to redefine, so this introduces some
7
+ # shorthand to get at the size of the resultant string.
8
+ def sz
9
+ self.to_s.size
10
+ end
11
+
12
+ alias len sz
13
+
14
+ # Typecast is used mostly by packet header classes, such as IPHeader,
15
+ # TCPHeader, and the like. It takes an argument, and casts it to the
16
+ # expected type for that element.
17
+ def typecast(i)
18
+ c = caller[0].match(/.*`([^']+)='/)[1]
19
+ self[c.intern].read i
20
+ end
21
+
22
+ # Used like typecast(), but specifically for casting Strings to StructFu::Strings.
23
+ def body=(i)
24
+ if i.kind_of? ::String
25
+ typecast(i)
26
+ elsif i.kind_of? StructFu
27
+ self[:body] = i
28
+ elsif i.nil?
29
+ self[:body] = StructFu::String.new.read("")
30
+ else
31
+ raise ArgumentError, "Can't cram a #{i.class} into a StructFu :body"
32
+ end
33
+ end
34
+
35
+ # Ints all have a value, an endianness, and a default value.
36
+ # Note that the signedness of Int values are implicit as
37
+ # far as the subclasses are concerned; to_i and to_f will
38
+ # return Integer/Float versions of the input value, instead
39
+ # of attempting to unpack the pack value. (This can be a useful
40
+ # hint to other functions).
41
+ #
42
+ # ==== Header Definition
43
+ #
44
+ # Fixnum :value
45
+ # Symbol :endian
46
+ # Fixnum :width
47
+ # Fixnum :default
48
+ class Int < Struct.new(:value, :endian, :width, :default)
49
+ alias :v= :value=
50
+ alias :v :value
51
+ alias :e= :endian=
52
+ alias :e :endian
53
+ alias :w= :width=
54
+ alias :w :width
55
+ alias :d= :default=
56
+ alias :d :default
57
+
58
+ # This is a parent class definition and should not be used directly.
59
+ def to_s
60
+ raise StandardError, "StructFu::Int#to_s accessed, must be redefined."
61
+ end
62
+
63
+ # Returns the Int as an Integer.
64
+ def to_i
65
+ (self.v || self.d).to_i
66
+ end
67
+
68
+ # Returns the Int as a Float.
69
+ def to_f
70
+ (self.v || self.d).to_f
71
+ end
72
+
73
+ def initialize(value=nil, endian=nil, width=nil, default=nil)
74
+ super(value,endian,width,default=0)
75
+ end
76
+
77
+ # Reads either an Integer or a packed string, and populates the value accordingly.
78
+ def read(i)
79
+ self.v = i.kind_of?(Integer) ? i.to_i : i.to_s.unpack(@packstr).first
80
+ self
81
+ end
82
+
83
+ end
84
+
85
+ # Int8 is a one byte value.
86
+ class Int8 < Int
87
+
88
+ def initialize(v=nil)
89
+ super(v,nil,w=1)
90
+ @packstr = "C"
91
+ end
92
+
93
+ # Returns a one byte value as a packed string.
94
+ def to_s
95
+ [(self.v || self.d)].pack("C")
96
+ end
97
+
98
+ end
99
+
100
+ # Int16 is a two byte value.
101
+ class Int16 < Int
102
+ def initialize(v=nil, e=:big)
103
+ super(v,e,w=2)
104
+ @packstr = (self.e == :big) ? "n" : "v"
105
+ end
106
+
107
+ # Returns a two byte value as a packed string.
108
+ def to_s
109
+ [(self.v || self.d)].pack(@packstr)
110
+ end
111
+
112
+ end
113
+
114
+ # Int16be is a two byte value in big-endian format.
115
+ class Int16be < Int16
116
+ end
117
+
118
+ # Int16le is a two byte value in little-endian format.
119
+ class Int16le < Int16
120
+ def initialize(v=nil, e=:little)
121
+ super(v,e)
122
+ @packstr = (self.e == :big) ? "n" : "v"
123
+ end
124
+ end
125
+
126
+ # Int32 is a four byte value.
127
+ class Int32 < Int
128
+ def initialize(v=nil, e=:big)
129
+ super(v,e,w=4)
130
+ @packstr = (self.e == :big) ? "N" : "V"
131
+ end
132
+
133
+ # Returns a four byte value as a packed string.
134
+ def to_s
135
+ [(self.v || self.d)].pack(@packstr)
136
+ end
137
+
138
+ end
139
+
140
+ # Int32be is a four byte value in big-endian format.
141
+ class Int32be < Int32
142
+ end
143
+
144
+ # Int32le is a four byte value in little-endian format.
145
+ class Int32le < Int32
146
+ def initialize(v=nil, e=:little)
147
+ super(v,e)
148
+ end
149
+ end
150
+
151
+ # Strings are just like regular strings, except it comes with a read() function
152
+ # so that it behaves like other StructFu elements.
153
+ class String < ::String
154
+ def read(str)
155
+ str = str.to_s
156
+ self.replace str
157
+ self
158
+ end
159
+ end
160
+
161
+ # Provides a primitive for creating strings, preceeded by
162
+ # an Int type of length. By default, a string of length zero with
163
+ # a one-byte length is presumed.
164
+ #
165
+ # Note that IntStrings aren't used for much, but it seemed like a good idea at the time.
166
+ class IntString < Struct.new(:int, :string, :mode)
167
+
168
+ def initialize(string='',int=Int8,mode=nil)
169
+ unless int.respond_to?(:ancestors) && int.ancestors.include?(StructFu::Int)
170
+ raise StandardError, "Invalid length (#{int.inspect}) associated with this String."
171
+ else
172
+ super(int.new,string,mode)
173
+ calc
174
+ end
175
+ end
176
+
177
+ # Calculates the size of a string, and sets it as the value.
178
+ def calc
179
+ int.v = string.to_s.size
180
+ self.to_s
181
+ end
182
+
183
+ # Returns the object as a string, depending on the mode set upon object creation.
184
+ def to_s
185
+ if mode == :parse
186
+ "#{int}" + [string].pack("a#{len}")
187
+ elsif mode == :fix
188
+ self.int.v = string.size
189
+ "#{int}#{string}"
190
+ else
191
+ "#{int}#{string}"
192
+ end
193
+ end
194
+
195
+ # By redefining #string=, we can ensure the correct value
196
+ # is calculated upon assignment. If you'd prefer to have
197
+ # an incorrect value, use the syntax, obj[:string]="value"
198
+ # instead. Note, by using the alternate form, you must
199
+ # #calc before you can trust the int's value. Think of the
200
+ # = assignment as "set to equal," while the []= assignment
201
+ # as "boxing in" the value. Maybe.
202
+ def string=(s)
203
+ self[:string] = s
204
+ calc
205
+ end
206
+
207
+ # Shorthand for querying a length. Note that the usual "length"
208
+ # and "size" refer to the number of elements of this struct.
209
+ def len
210
+ self[:int].value
211
+ end
212
+
213
+ # Override the size, if you must.
214
+ def len=(i)
215
+ self[:int].value=i
216
+ end
217
+
218
+ # Read takes a string, assumes an int width as previously
219
+ # defined upon initialization, but makes no guarantees
220
+ # the int value isn't lying. You're on your own to test
221
+ # for that (or use parse() with a :mode set).
222
+ def read(s)
223
+ unless s[0,int.width].size == int.width
224
+ raise StandardError, "String is too short for type #{int.class}"
225
+ else
226
+ int.read(s[0,int.width])
227
+ self[:string] = s[int.width,s.size]
228
+ end
229
+ self.to_s
230
+ end
231
+
232
+ # parse() is like read(), except that it interprets the string, either
233
+ # based on the declared length, or the actual length. Which strategy
234
+ # is used is dependant on which :mode is set (with self.mode).
235
+ #
236
+ # :parse : Read the length, and then read in that many bytes of the string.
237
+ # The string may be truncated or padded out with nulls, as dictated by the value.
238
+ # :fix : Skip the length, read the rest of the string, then set the length to what it ought to be.
239
+ # else : If neither of these modes are set, just perfom a normal read(). This is the default.
240
+ def parse(s)
241
+ unless s[0,int.width].size == int.width
242
+ raise StandardError, "String is too short for type #{int.class}"
243
+ else
244
+ case mode
245
+ when :parse
246
+ int.read(s[0,int.width])
247
+ self[:string] = s[int.width,int.value]
248
+ if string.size < int.value
249
+ self[:string] += ("\x00" * (int.value - self[:string].size))
250
+ end
251
+ when :fix
252
+ self.string = s[int.width,s.size]
253
+ else
254
+ return read(s)
255
+ end
256
+ end
257
+ self.to_s
258
+ end
259
+
260
+ end
261
+
262
+ end
263
+
264
+ class Struct
265
+
266
+ # Monkeypatch for Struct to include some string safety -- anything that uses
267
+ # Struct is going to presume binary strings anyway.
268
+ def force_binary(str)
269
+ str.force_encoding "binary" if str.respond_to? :force_encoding
270
+ end
271
+
272
+ end
273
+
274
+ # vim: nowrap sw=2 sts=0 ts=2 ff=unix ft=ruby
@@ -0,0 +1,1061 @@
1
+ module PacketFu
2
+
3
+ # Implements the Explict Congestion Notification for TCPHeader.
4
+ #
5
+ # ==== Header Definition
6
+ #
7
+ #
8
+ # Fixnum (1 bit) :n
9
+ # Fixnum (1 bit) :c
10
+ # Fixnum (1 bit) :e
11
+ class TcpEcn < Struct.new(:n, :c, :e)
12
+
13
+ include StructFu
14
+
15
+ def initialize(args={})
16
+ super(args[:n], args[:c], args[:e]) if args
17
+ end
18
+
19
+ # Returns the TcpEcn field as an integer... even though it's going
20
+ # to be split across a byte boundary.
21
+ def to_i
22
+ (n.to_i << 2) + (c.to_i << 1) + e.to_i
23
+ end
24
+
25
+ # Reads a string to populate the object.
26
+ def read(str)
27
+ force_binary(str)
28
+ return self if str.nil? || str.size < 2
29
+ if 1.respond_to? :ord
30
+ byte1 = str[0].ord
31
+ byte2 = str[1].ord
32
+ else
33
+ byte1 = str[0]
34
+ byte2 = str[1]
35
+ end
36
+ self[:n] = byte1 & 0b00000001 == 0b00000001 ? 1 : 0
37
+ self[:c] = byte2 & 0b10000000 == 0b10000000 ? 1 : 0
38
+ self[:e] = byte2 & 0b01000000 == 0b01000000 ? 1 : 0
39
+ self
40
+ end
41
+
42
+ end
43
+
44
+ # Implements the Header Length for TCPHeader.
45
+ #
46
+ # ==== Header Definition
47
+ #
48
+ # Fixnum (4 bits) :hlen
49
+ class TcpHlen < Struct.new(:hlen)
50
+
51
+ include StructFu
52
+
53
+ def initialize(args={})
54
+ super(args[:hlen])
55
+ end
56
+
57
+ # Returns the TcpHlen field as an integer. Note these will become the high
58
+ # bits at the TCP header's offset, even though the lower 4 bits
59
+ # will be further chopped up.
60
+ def to_i
61
+ hlen.to_i & 0b1111
62
+ end
63
+
64
+ # Reads a string to populate the object.
65
+ def read(str)
66
+ force_binary(str)
67
+ return self if str.nil? || str.size.zero?
68
+ if 1.respond_to? :ord
69
+ self[:hlen] = (str[0].ord & 0b11110000) >> 4
70
+ else
71
+ self[:hlen] = (str[0] & 0b11110000) >> 4
72
+ end
73
+ self
74
+ end
75
+
76
+ # Returns the object in string form.
77
+ def to_s
78
+ [self.to_i].pack("C")
79
+ end
80
+
81
+ end
82
+
83
+ # Implements the Reserved bits for TCPHeader.
84
+ #
85
+ # ==== Header Definition
86
+ #
87
+ #
88
+ # Fixnum (1 bit) :r1
89
+ # Fixnum (1 bit) :r2
90
+ # Fixnum (1 bit) :r3
91
+ class TcpReserved < Struct.new(:r1, :r2, :r3)
92
+
93
+ include StructFu
94
+
95
+ def initialize(args={})
96
+ super(
97
+ args[:r1] || 0,
98
+ args[:r2] || 0,
99
+ args[:r3] || 0) if args.kind_of? Hash
100
+ end
101
+
102
+ # Returns the Reserved field as an integer.
103
+ def to_i
104
+ (r1.to_i << 2) + (r2.to_i << 1) + r3.to_i
105
+ end
106
+
107
+ # Reads a string to populate the object.
108
+ def read(str)
109
+ force_binary(str)
110
+ return self if str.nil? || str.size.zero?
111
+ if 1.respond_to? :ord
112
+ byte = str[0].ord
113
+ else
114
+ byte = str[0]
115
+ end
116
+ self[:r1] = byte & 0b00000100 == 0b00000100 ? 1 : 0
117
+ self[:r2] = byte & 0b00000010 == 0b00000010 ? 1 : 0
118
+ self[:r3] = byte & 0b00000001 == 0b00000001 ? 1 : 0
119
+ self
120
+ end
121
+
122
+ end
123
+
124
+ # Implements flags for TCPHeader.
125
+ #
126
+ # ==== Header Definition
127
+ #
128
+ # Fixnum (1 bit) :urg
129
+ # Fixnum (1 bit) :ack
130
+ # Fixnum (1 bit) :psh
131
+ # Fixnum (1 bit) :rst
132
+ # Fixnum (1 bit) :syn
133
+ # Fixnum (1 bit) :fin
134
+ #
135
+ # Flags can typically be set by setting them either to 1 or 0, or to true or false.
136
+ class TcpFlags < Struct.new(:urg, :ack, :psh, :rst, :syn, :fin)
137
+
138
+ include StructFu
139
+
140
+ def initialize(args={})
141
+ # This technique attemts to ensure that flags are always 0 (off)
142
+ # or 1 (on). Statements like nil and false shouldn't be lurking in here.
143
+ if args.nil? || args.size.zero?
144
+ super( 0, 0, 0, 0, 0, 0)
145
+ else
146
+ super(
147
+ (args[:urg] ? 1 : 0),
148
+ (args[:ack] ? 1 : 0),
149
+ (args[:psh] ? 1 : 0),
150
+ (args[:rst] ? 1 : 0),
151
+ (args[:syn] ? 1 : 0),
152
+ (args[:fin] ? 1 : 0)
153
+ )
154
+ end
155
+ end
156
+
157
+ # Returns the TcpFlags as an integer.
158
+ # Also not a great candidate for to_s due to the short bitspace.
159
+ def to_i
160
+ (urg.to_i << 5) + (ack.to_i << 4) + (psh.to_i << 3) +
161
+ (rst.to_i << 2) + (syn.to_i << 1) + fin.to_i
162
+ end
163
+
164
+ # Helper to determine if this flag is a 1 or a 0.
165
+ def zero_or_one(i=0)
166
+ if i == 0 || i == false || i == nil
167
+ 0
168
+ else
169
+ 1
170
+ end
171
+ end
172
+
173
+ # Setter for the Urgent flag.
174
+ def urg=(i); self[:urg] = zero_or_one(i); end
175
+ # Setter for the Acknowlege flag.
176
+ def ack=(i); self[:ack] = zero_or_one(i); end
177
+ # Setter for the Push flag.
178
+ def psh=(i); self[:psh] = zero_or_one(i); end
179
+ # Setter for the Reset flag.
180
+ def rst=(i); self[:rst] = zero_or_one(i); end
181
+ # Setter for the Synchronize flag.
182
+ def syn=(i); self[:syn] = zero_or_one(i); end
183
+ # Setter for the Finish flag.
184
+ def fin=(i); self[:fin] = zero_or_one(i); end
185
+
186
+ # Reads a string to populate the object.
187
+ def read(str)
188
+ force_binary(str)
189
+ return self if str.nil?
190
+ if 1.respond_to? :ord
191
+ byte = str[0].ord
192
+ else
193
+ byte = str[0]
194
+ end
195
+ self[:urg] = byte & 0b00100000 == 0b00100000 ? 1 : 0
196
+ self[:ack] = byte & 0b00010000 == 0b00010000 ? 1 : 0
197
+ self[:psh] = byte & 0b00001000 == 0b00001000 ? 1 : 0
198
+ self[:rst] = byte & 0b00000100 == 0b00000100 ? 1 : 0
199
+ self[:syn] = byte & 0b00000010 == 0b00000010 ? 1 : 0
200
+ self[:fin] = byte & 0b00000001 == 0b00000001 ? 1 : 0
201
+ self
202
+ end
203
+
204
+ end
205
+
206
+ end
207
+
208
+ module PacketFu
209
+
210
+ # TcpOption is the base class for all TCP options. Note that TcpOption#len
211
+ # returns the size of the entire option, while TcpOption#optlen is the struct
212
+ # for the TCP Option Length field.
213
+ #
214
+ # Subclassed options should set the correct TcpOption#kind by redefining
215
+ # initialize. They should also deal with various value types there by setting
216
+ # them explicitly with an accompanying StructFu#typecast for the setter.
217
+ #
218
+ # By default, values are presumed to be strings, unless they are Numeric, in
219
+ # which case a guess is made to the width of the Numeric based on the given
220
+ # optlen.
221
+ #
222
+ # Note that normally, optlen is /not/ enforced for directly setting values,
223
+ # so the user is perfectly capable of setting incorrect lengths.
224
+ class TcpOption < Struct.new(:kind, :optlen, :value)
225
+
226
+ include StructFu
227
+
228
+ def initialize(args={})
229
+ super(
230
+ Int8.new(args[:kind]),
231
+ Int8.new(args[:optlen])
232
+ )
233
+ if args[:value].kind_of? Numeric
234
+ self[:value] = case args[:optlen]
235
+ when 3; Int8.new(args[:value])
236
+ when 4; Int16.new(args[:value])
237
+ when 6; Int32.new(args[:value])
238
+ else; StructFu::String.new.read(args[:value])
239
+ end
240
+ else
241
+ self[:value] = StructFu::String.new.read(args[:value])
242
+ end
243
+ end
244
+
245
+ # Returns the object in string form.
246
+ def to_s
247
+ self[:kind].to_s +
248
+ (self[:optlen].value.nil? ? nil : self[:optlen]).to_s +
249
+ (self[:value].nil? ? nil : self[:value]).to_s
250
+ end
251
+
252
+ # Reads a string to populate the object.
253
+ def read(str)
254
+ force_binary(str)
255
+ return self if str.nil?
256
+ self[:kind].read(str[0,1])
257
+ if str[1,1]
258
+ self[:optlen].read(str[1,1])
259
+ if str[2,1] && optlen.value > 2
260
+ self[:value].read(str[2,optlen.value-2])
261
+ end
262
+ end
263
+ self
264
+ end
265
+
266
+ # The default decode for an unknown option. Known options should redefine this.
267
+ def decode
268
+ unk = "unk-#{self.kind.to_i}"
269
+ (self[:optlen].to_i > 2 && self[:value].to_s.size > 1) ? [unk,self[:value]].join(":") : unk
270
+ end
271
+
272
+ # Setter for the "kind" byte of this option.
273
+ def kind=(i); typecast i; end
274
+ # Setter for the "option length" byte for this option.
275
+ def optlen=(i); typecast i; end
276
+
277
+ # Setter for the value of this option.
278
+ def value=(i)
279
+ if i.kind_of? Numeric
280
+ typecast i
281
+ elsif i.respond_to? :to_s
282
+ self[:value] = i
283
+ else
284
+ self[:value] = ''
285
+ end
286
+ end
287
+
288
+ # Generally, encoding a value is going to be just a read. Some
289
+ # options will treat things a little differently; TS for example,
290
+ # takes two values and concatenates them.
291
+ def encode(str)
292
+ self[:value] = self.class.new(:value => str).value
293
+ end
294
+
295
+ # Returns true if this option has an optlen. Some don't.
296
+ def has_optlen?
297
+ (kind.value && kind.value < 2) ? false : true
298
+ end
299
+
300
+ # Returns true if this option has a value. Some don't.
301
+ def has_value?
302
+ (value.respond_to? :to_s && value.to_s.size > 0) ? false : true
303
+ end
304
+
305
+ # End of Line option. Usually used to terminate a string of options.
306
+ #
307
+ # http://www.networksorcery.com/enp/protocol/tcp/option000.htm
308
+ class EOL < TcpOption
309
+ def initialize(args={})
310
+ super(
311
+ args.merge(:kind => 0)
312
+ )
313
+ end
314
+
315
+ def decode
316
+ "EOL"
317
+ end
318
+
319
+ end
320
+
321
+ # No Operation option. Usually used to pad out options to fit a 4-byte alignment.
322
+ #
323
+ # http://www.networksorcery.com/enp/protocol/tcp/option001.htm
324
+ class NOP < TcpOption
325
+ def initialize(args={})
326
+ super(
327
+ args.merge(:kind => 1)
328
+ )
329
+ end
330
+
331
+ def decode
332
+ "NOP"
333
+ end
334
+
335
+ end
336
+
337
+ # Maximum Segment Size option.
338
+ #
339
+ # http://www.networksorcery.com/enp/protocol/tcp/option002.htm
340
+ class MSS < TcpOption
341
+ def initialize(args={})
342
+ super(
343
+ args.merge(:kind => 2,
344
+ :optlen => 4
345
+ )
346
+ )
347
+ self[:value] = Int16.new(args[:value])
348
+ end
349
+
350
+ def value=(i); typecast i; end
351
+
352
+ # MSS options with lengths other than 4 are malformed.
353
+ def decode
354
+ if self[:optlen].to_i == 4
355
+ "MSS:#{self[:value].to_i}"
356
+ else
357
+ "MSS-bad:#{self[:value]}"
358
+ end
359
+ end
360
+
361
+ end
362
+
363
+ # Window Size option.
364
+ #
365
+ # http://www.networksorcery.com/enp/protocol/tcp/option003.htm
366
+ class WS < TcpOption
367
+ def initialize(args={})
368
+ super(
369
+ args.merge(:kind => 3,
370
+ :optlen => 3
371
+ )
372
+ )
373
+ self[:value] = Int8.new(args[:value])
374
+ end
375
+
376
+ def value=(i); typecast i; end
377
+
378
+ # WS options with lengths other than 3 are malformed.
379
+ def decode
380
+ if self[:optlen].to_i == 3
381
+ "WS:#{self[:value].to_i}"
382
+ else
383
+ "WS-bad:#{self[:value]}"
384
+ end
385
+ end
386
+
387
+ end
388
+
389
+ # Selective Acknowlegment OK option.
390
+ #
391
+ # http://www.networksorcery.com/enp/protocol/tcp/option004.htm
392
+ class SACKOK < TcpOption
393
+ def initialize(args={})
394
+ super(
395
+ args.merge(:kind => 4,
396
+ :optlen => 2)
397
+ )
398
+ end
399
+
400
+ # SACKOK options with sizes other than 2 are malformed.
401
+ def decode
402
+ if self[:optlen].to_i == 2
403
+ "SACKOK"
404
+ else
405
+ "SACKOK-bad:#{self[:value]}"
406
+ end
407
+ end
408
+
409
+ end
410
+
411
+ # Selective Acknowledgement option.
412
+ #
413
+ # http://www.networksorcery.com/enp/protocol/tcp/option004.htm
414
+ #
415
+ # Note that SACK always takes its optlen from the size of the string.
416
+ class SACK < TcpOption
417
+ def initialize(args={})
418
+ super(
419
+ args.merge(:kind => 5,
420
+ :optlen => ((args[:value] || "").size + 2)
421
+ )
422
+ )
423
+ end
424
+
425
+ def optlen=(i); typecast i; end
426
+
427
+ def value=(i)
428
+ self[:optlen] = Int8.new(i.to_s.size + 2)
429
+ self[:value] = StructFu::String.new(i)
430
+ end
431
+
432
+ def decode
433
+ "SACK:#{self[:value]}"
434
+ end
435
+
436
+ def encode(str)
437
+ temp_obj = self.class.new(:value => str)
438
+ self[:value] = temp_obj.value
439
+ self[:optlen] = temp_obj.optlen.value
440
+ self
441
+ end
442
+
443
+ end
444
+
445
+ # Echo option.
446
+ #
447
+ # http://www.networksorcery.com/enp/protocol/tcp/option006.htm
448
+ class ECHO < TcpOption
449
+ def initialize(args={})
450
+ super(
451
+ args.merge(:kind => 6,
452
+ :optlen => 6
453
+ )
454
+ )
455
+ end
456
+
457
+ # ECHO options with lengths other than 6 are malformed.
458
+ def decode
459
+ if self[:optlen].to_i == 6
460
+ "ECHO:#{self[:value]}"
461
+ else
462
+ "ECHO-bad:#{self[:value]}"
463
+ end
464
+ end
465
+
466
+ end
467
+
468
+ # Echo Reply option.
469
+ #
470
+ # http://www.networksorcery.com/enp/protocol/tcp/option007.htm
471
+ class ECHOREPLY < TcpOption
472
+ def initialize(args={})
473
+ super(
474
+ args.merge(:kind => 7,
475
+ :optlen => 6
476
+ )
477
+ )
478
+ end
479
+
480
+ # ECHOREPLY options with lengths other than 6 are malformed.
481
+ def decode
482
+ if self[:optlen].to_i == 6
483
+ "ECHOREPLY:#{self[:value]}"
484
+ else
485
+ "ECHOREPLY-bad:#{self[:value]}"
486
+ end
487
+ end
488
+
489
+ end
490
+
491
+ # Timestamp option
492
+ #
493
+ # http://www.networksorcery.com/enp/protocol/tcp/option008.htm
494
+ class TS < TcpOption
495
+ def initialize(args={})
496
+ super(
497
+ args.merge(:kind => 8,
498
+ :optlen => 10
499
+ )
500
+ )
501
+ self[:value] = StructFu::String.new.read(args[:value] || "\x00" * 8)
502
+ end
503
+
504
+ # TS options with lengths other than 10 are malformed.
505
+ def decode
506
+ if self[:optlen].to_i == 10
507
+ val1,val2 = self[:value].unpack("NN")
508
+ "TS:#{val1};#{val2}"
509
+ else
510
+ "TS-bad:#{self[:value]}"
511
+ end
512
+ end
513
+
514
+ # TS options are in the format of "TS:[timestamp value];[timestamp secret]" Both
515
+ # should be written as decimal numbers.
516
+ def encode(str)
517
+ if str =~ /^([0-9]+);([0-9]+)$/
518
+ tsval,tsecr = str.split(";").map {|x| x.to_i}
519
+ if tsval <= 0xffffffff && tsecr <= 0xffffffff
520
+ self[:value] = StructFu::String.new([tsval,tsecr].pack("NN"))
521
+ else
522
+ self[:value] = StructFu::String.new(str)
523
+ end
524
+ else
525
+ self[:value] = StructFu::String.new(str)
526
+ end
527
+ end
528
+
529
+ end
530
+
531
+ end
532
+
533
+ class TcpOptions < Array
534
+
535
+ include StructFu
536
+
537
+ # If args[:pad] is set, the options line is automatically padded out
538
+ # with NOPs.
539
+ def to_s(args={})
540
+ opts = self.map {|x| x.to_s}.join
541
+ if args[:pad]
542
+ unless (opts.size % 4).zero?
543
+ (4 - (opts.size % 4)).times { opts << "\x01" }
544
+ end
545
+ end
546
+ opts
547
+ end
548
+
549
+ def force_binary(str)
550
+ str.force_encoding "binary" if str.respond_to? :force_encoding
551
+ end
552
+
553
+ # Reads a string to populate the object.
554
+ def read(str)
555
+ self.clear if self.size > 0
556
+ force_binary(str)
557
+ return self if(!str.respond_to? :to_s || str.nil?)
558
+ i = 0
559
+ while i < str.to_s.size
560
+ this_opt = case str[i,1].unpack("C").first
561
+ when 0; TcpOption::EOL.new
562
+ when 1; TcpOption::NOP.new
563
+ when 2; TcpOption::MSS.new
564
+ when 3; TcpOption::WS.new
565
+ when 4; TcpOption::SACKOK.new
566
+ when 5; TcpOption::SACK.new
567
+ when 6; TcpOption::ECHO.new
568
+ when 7; TcpOption::ECHOREPLY.new
569
+ when 8; TcpOption::TS.new
570
+ else; TcpOption.new
571
+ end
572
+ this_opt.read str[i,str.size]
573
+ unless this_opt.has_optlen?
574
+ this_opt.value = nil
575
+ this_opt.optlen = nil
576
+ end
577
+ self << this_opt
578
+ i += this_opt.sz
579
+ end
580
+ self
581
+ end
582
+
583
+ # Decode parses the TcpOptions object's member options, and produces a
584
+ # human-readable string by iterating over each element's decode() function.
585
+ # If TcpOptions elements were not initially created as TcpOptions, an
586
+ # attempt will be made to convert them.
587
+ #
588
+ # The output of decode is suitable as input for TcpOptions#encode.
589
+ def decode
590
+ decoded = self.map do |x|
591
+ if x.kind_of? TcpOption
592
+ x.decode
593
+ else
594
+ x = TcpOptions.new.read(x).decode
595
+ end
596
+ end
597
+ decoded.join(",")
598
+ end
599
+
600
+ # Encode takes a human-readable string and appends the corresponding
601
+ # binary options to the TcpOptions object. To completely replace the contents
602
+ # of the object, use TcpOptions#encode! instead.
603
+ #
604
+ # Options are comma-delimited, and are identical to the output of the
605
+ # TcpOptions#decode function. Note that the syntax can be unforgiving, so
606
+ # it may be easier to create the subclassed TcpOptions themselves directly,
607
+ # but this method can be less typing if you know what you're doing.
608
+ #
609
+ # Note that by using TcpOptions#encode, strings supplied as values which
610
+ # can be converted to numbers will be converted first.
611
+ #
612
+ # == Example
613
+ #
614
+ # t = TcpOptions.new
615
+ # t.encode("MS:1460,WS:6")
616
+ # t.to_s # => "\002\004\005\264\002\003\006"
617
+ # t.encode("NOP")
618
+ # t.to_s # => "\002\004\005\264\002\003\006\001"
619
+ def encode(str)
620
+ opts = str.split(/[\s]*,[\s]*/)
621
+ opts.each do |o|
622
+ kind,value = o.split(/[\s]*:[\s]*/)
623
+ klass = TcpOption.const_get(kind.upcase)
624
+ value = value.to_i if value =~ /^[0-9]+$/
625
+ this_opt = klass.new
626
+ this_opt.encode(value)
627
+ self << this_opt
628
+ end
629
+ self
630
+ end
631
+
632
+ # Like TcpOption#encode, except the entire contents are replaced.
633
+ def encode!(str)
634
+ self.clear if self.size > 0
635
+ encode(str)
636
+ end
637
+
638
+ end
639
+
640
+ end
641
+
642
+ module PacketFu
643
+
644
+ # TCPHeader is a complete TCP struct, used in TCPPacket. Most IP traffic is TCP-based, by
645
+ # volume.
646
+ #
647
+ # For more on TCP packets, see http://www.networksorcery.com/enp/protocol/tcp.htm
648
+ #
649
+ # ==== Header Definition
650
+ #
651
+ # Int16 :tcp_src Default: random
652
+ # Int16 :tcp_dst
653
+ # Int32 :tcp_seq Default: random
654
+ # Int32 :tcp_ack
655
+ # TcpHlen :tcp_hlen Default: 5 # Must recalc as options are set.
656
+ # TcpReserved :tcp_reserved Default: 0
657
+ # TcpEcn :tcp_ecn
658
+ # TcpFlags :tcp_flags
659
+ # Int16 :tcp_win, Default: 0 # WinXP's default syn packet
660
+ # Int16 :tcp_sum, Default: calculated # Must set this upon generation.
661
+ # Int16 :tcp_urg
662
+ # TcpOptions :tcp_opts
663
+ # String :body
664
+ #
665
+ # See also TcpHlen, TcpReserved, TcpEcn, TcpFlags, TcpOpts
666
+ class TCPHeader < Struct.new(:tcp_src, :tcp_dst,
667
+ :tcp_seq,
668
+ :tcp_ack,
669
+ :tcp_hlen, :tcp_reserved, :tcp_ecn, :tcp_flags, :tcp_win,
670
+ :tcp_sum, :tcp_urg,
671
+ :tcp_opts, :body)
672
+ include StructFu
673
+
674
+ def initialize(args={})
675
+ @random_seq = rand(0xffffffff)
676
+ @random_src = rand_port
677
+ super(
678
+ Int16.new(args[:tcp_src] || tcp_calc_src),
679
+ Int16.new(args[:tcp_dst]),
680
+ Int32.new(args[:tcp_seq] || tcp_calc_seq),
681
+ Int32.new(args[:tcp_ack]),
682
+ TcpHlen.new(:hlen => (args[:tcp_hlen] || 5)),
683
+ TcpReserved.new(args[:tcp_reserved] || 0),
684
+ TcpEcn.new(args[:tcp_ecn]),
685
+ TcpFlags.new(args[:tcp_flags]),
686
+ Int16.new(args[:tcp_win] || 0x4000),
687
+ Int16.new(args[:tcp_sum] || 0),
688
+ Int16.new(args[:tcp_urg]),
689
+ TcpOptions.new.read(args[:tcp_opts]),
690
+ StructFu::String.new.read(args[:body])
691
+ )
692
+ end
693
+
694
+ attr_accessor :flavor
695
+
696
+ # Helper function to create the string for Hlen, Reserved, ECN, and Flags.
697
+ def bits_to_s
698
+ bytes = []
699
+ bytes[0] = (self[:tcp_hlen].to_i << 4) +
700
+ (self[:tcp_reserved].to_i << 1) +
701
+ self[:tcp_ecn].n.to_i
702
+ bytes[1] = (self[:tcp_ecn].c.to_i << 7) +
703
+ (self[:tcp_ecn].e.to_i << 6) +
704
+ self[:tcp_flags].to_i
705
+ bytes.pack("CC")
706
+ end
707
+
708
+ # Returns the object in string form.
709
+ def to_s
710
+ hdr = self.to_a.map do |x|
711
+ if x.kind_of? TcpHlen
712
+ bits_to_s
713
+ elsif x.kind_of? TcpReserved
714
+ next
715
+ elsif x.kind_of? TcpEcn
716
+ next
717
+ elsif x.kind_of? TcpFlags
718
+ next
719
+ else
720
+ x.to_s
721
+ end
722
+ end
723
+ hdr.flatten.join
724
+ end
725
+
726
+ # Reads a string to populate the object.
727
+ def read(str)
728
+ force_binary(str)
729
+ return self if str.nil?
730
+ self[:tcp_src].read(str[0,2])
731
+ self[:tcp_dst].read(str[2,2])
732
+ self[:tcp_seq].read(str[4,4])
733
+ self[:tcp_ack].read(str[8,4])
734
+ self[:tcp_hlen].read(str[12,1])
735
+ self[:tcp_reserved].read(str[12,1])
736
+ self[:tcp_ecn].read(str[12,2])
737
+ self[:tcp_flags].read(str[13,1])
738
+ self[:tcp_win].read(str[14,2])
739
+ self[:tcp_sum].read(str[16,2])
740
+ self[:tcp_urg].read(str[18,2])
741
+ self[:tcp_opts].read(str[20,((self[:tcp_hlen].to_i * 4) - 20)])
742
+ self[:body].read(str[(self[:tcp_hlen].to_i * 4),str.size])
743
+ self
744
+ end
745
+
746
+ # Setter for the TCP source port.
747
+ def tcp_src=(i); typecast i; end
748
+ # Getter for the TCP source port.
749
+ def tcp_src; self[:tcp_src].to_i; end
750
+ # Setter for the TCP destination port.
751
+ def tcp_dst=(i); typecast i; end
752
+ # Getter for the TCP destination port.
753
+ def tcp_dst; self[:tcp_dst].to_i; end
754
+ # Setter for the TCP sequence number.
755
+ def tcp_seq=(i); typecast i; end
756
+ # Getter for the TCP sequence number.
757
+ def tcp_seq; self[:tcp_seq].to_i; end
758
+ # Setter for the TCP ackowlegement number.
759
+ def tcp_ack=(i); typecast i; end
760
+ # Getter for the TCP ackowlegement number.
761
+ def tcp_ack; self[:tcp_ack].to_i; end
762
+ # Setter for the TCP window size number.
763
+ def tcp_win=(i); typecast i; end
764
+ # Getter for the TCP window size number.
765
+ def tcp_win; self[:tcp_win].to_i; end
766
+ # Setter for the TCP checksum.
767
+ def tcp_sum=(i); typecast i; end
768
+ # Getter for the TCP checksum.
769
+ def tcp_sum; self[:tcp_sum].to_i; end
770
+ # Setter for the TCP urgent field.
771
+ def tcp_urg=(i); typecast i; end
772
+ # Getter for the TCP urgent field.
773
+ def tcp_urg; self[:tcp_urg].to_i; end
774
+
775
+ # Getter for the TCP Header Length value.
776
+ def tcp_hlen; self[:tcp_hlen].to_i; end
777
+ # Setter for the TCP Header Length value.
778
+ def tcp_hlen=(i)
779
+ if i.kind_of? PacketFu::TcpHlen
780
+ self[:tcp_hlen]=i
781
+ else
782
+ self[:tcp_hlen].read(i)
783
+ end
784
+ end
785
+
786
+ # Getter for the TCP Reserved field.
787
+ def tcp_reserved; self[:tcp_reserved].to_i; end
788
+ # Setter for the TCP Reserved field.
789
+ def tcp_reserved=(i)
790
+ if i.kind_of? PacketFu::TcpReserved
791
+ self[:tcp_reserved]=i
792
+ else
793
+ self[:tcp_reserved].read(i)
794
+ end
795
+ end
796
+
797
+ # Getter for the ECN bits.
798
+ def tcp_ecn; self[:tcp_ecn].to_i; end
799
+ # Setter for the ECN bits.
800
+ def tcp_ecn=(i)
801
+ if i.kind_of? PacketFu::TcpEcn
802
+ self[:tcp_ecn]=i
803
+ else
804
+ self[:tcp_ecn].read(i)
805
+ end
806
+ end
807
+
808
+ # Getter for TCP Options.
809
+ def tcp_opts; self[:tcp_opts].to_s; end
810
+ # Setter for TCP Options.
811
+ def tcp_opts=(i)
812
+ if i.kind_of? PacketFu::TcpOptions
813
+ self[:tcp_opts]=i
814
+ else
815
+ self[:tcp_opts].read(i)
816
+ end
817
+ end
818
+
819
+ # Resets the sequence number to a new random number.
820
+ def tcp_calc_seq; @random_seq; end
821
+ # Resets the source port to a new random number.
822
+ def tcp_calc_src; @random_src; end
823
+
824
+ # Returns the actual length of the TCP options.
825
+ def tcp_opts_len
826
+ self[:tcp_opts].to_s.size
827
+ end
828
+
829
+ # Sets and returns the true length of the TCP Header.
830
+ # TODO: Think about making all the option stuff safer.
831
+ def tcp_calc_hlen
832
+ self[:tcp_hlen] = TcpHlen.new(:hlen => ((20 + tcp_opts_len) / 4))
833
+ end
834
+
835
+ # Generates a random high port. This is affected by packet flavor.
836
+ def rand_port
837
+ rand(0xffff - 1025) + 1025
838
+ end
839
+
840
+ # Gets a more readable option list.
841
+ def tcp_options
842
+ self[:tcp_opts].decode
843
+ end
844
+
845
+ # Sets a more readable option list.
846
+ def tcp_options=(arg)
847
+ self[:tcp_opts].encode arg
848
+ end
849
+
850
+ # Equivalent to tcp_src.
851
+ def tcp_sport
852
+ self.tcp_src.to_i
853
+ end
854
+
855
+ # Equivalent to tcp_src=.
856
+ def tcp_sport=(arg)
857
+ self.tcp_src=(arg)
858
+ end
859
+
860
+ # Equivalent to tcp_dst.
861
+ def tcp_dport
862
+ self.tcp_dst.to_i
863
+ end
864
+
865
+ # Equivalent to tcp_dst=.
866
+ def tcp_dport=(arg)
867
+ self.tcp_dst=(arg)
868
+ end
869
+
870
+ # Recalculates calculated fields for TCP (except checksum which is at the Packet level).
871
+ def tcp_recalc(arg=:all)
872
+ case arg
873
+ when :tcp_hlen
874
+ tcp_calc_hlen
875
+ when :tcp_src
876
+ @random_tcp_src = rand_port
877
+ when :tcp_sport
878
+ @random_tcp_src = rand_port
879
+ when :tcp_seq
880
+ @random_tcp_seq = rand(0xffffffff)
881
+ when :all
882
+ tcp_calc_hlen
883
+ @random_tcp_src = rand_port
884
+ @random_tcp_seq = rand(0xffffffff)
885
+ else
886
+ raise ArgumentError, "No such field `#{arg}'"
887
+ end
888
+ end
889
+
890
+ end
891
+
892
+ # TCPPacket is used to construct TCP packets. They contain an EthHeader, an IPHeader, and a TCPHeader.
893
+ #
894
+ # == Example
895
+ #
896
+ # tcp_pkt = PacketFu::TCPPacket.new
897
+ # tcp_pkt.tcp_flags.syn=1
898
+ # tcp_pkt.tcp_dst=80
899
+ # tcp_pkt.tcp_win=5840
900
+ # tcp_pkt.tcp_options="mss:1460,sack.ok,ts:#{rand(0xffffffff)};0,nop,ws:7"
901
+ #
902
+ # tcp_pkt.ip_saddr=[rand(0xff),rand(0xff),rand(0xff),rand(0xff)].join('.')
903
+ # tcp_pkt.ip_daddr=[rand(0xff),rand(0xff),rand(0xff),rand(0xff)].join('.')
904
+ #
905
+ # tcp_pkt.recalc
906
+ # tcp_pkt.to_f('/tmp/tcp.pcap')
907
+ #
908
+ # == Parameters
909
+ # :eth
910
+ # A pre-generated EthHeader object.
911
+ # :ip
912
+ # A pre-generated IPHeader object.
913
+ # :flavor
914
+ # TODO: Sets the "flavor" of the TCP packet. This will include TCP options and the initial window
915
+ # size, per stack. There is a lot of variety here, and it's one of the most useful methods to
916
+ # remotely fingerprint devices. :flavor will span both ip and tcp for consistency.
917
+ # :type
918
+ # TODO: Set up particular types of packets (syn, psh_ack, rst, etc). This can change the initial flavor.
919
+ # :config
920
+ # A hash of return address details, often the output of Utils.whoami?
921
+ class TCPPacket < Packet
922
+
923
+ attr_accessor :eth_header, :ip_header, :tcp_header, :headers
924
+
925
+ def initialize(args={})
926
+ @eth_header = (args[:eth] || EthHeader.new)
927
+ @ip_header = (args[:ip] || IPHeader.new)
928
+ @tcp_header = (args[:tcp] || TCPHeader.new)
929
+ @tcp_header.flavor = args[:flavor].to_s.downcase
930
+
931
+ @ip_header.body = @tcp_header
932
+ @eth_header.body = @ip_header
933
+ @headers = [@eth_header, @ip_header, @tcp_header]
934
+
935
+ @ip_header.ip_proto=0x06
936
+ super
937
+ if args[:flavor]
938
+ tcp_calc_flavor(@tcp_header.flavor)
939
+ else
940
+ tcp_calc_sum
941
+ end
942
+ end
943
+
944
+ # Sets the correct flavor for TCP Packets. Recognized flavors are:
945
+ # windows, linux, freebsd
946
+ def tcp_calc_flavor(str)
947
+ ts_val = Time.now.to_i + rand(0x4fffffff)
948
+ ts_sec = rand(0xffffff)
949
+ case @tcp_header.flavor = str.to_s.downcase
950
+ when "windows" # WinXP's default syn
951
+ @tcp_header.tcp_win = 0x4000
952
+ @tcp_header.tcp_options="MSS:1460,NOP,NOP,SACKOK"
953
+ @tcp_header.tcp_src = rand(5000 - 1026) + 1026
954
+ @ip_header.ip_ttl = 64
955
+ when "linux" # Ubuntu Linux 2.6.24-19-generic default syn
956
+ @tcp_header.tcp_win = 5840
957
+ @tcp_header.tcp_options="MSS:1460,SACKOK,TS:#{ts_val};0,NOP,WS:7"
958
+ @tcp_header.tcp_src = rand(61_000 - 32_000) + 32_000
959
+ @ip_header.ip_ttl = 64
960
+ when "freebsd" # Freebsd
961
+ @tcp_header.tcp_win = 0xffff
962
+ @tcp_header.tcp_options="MSS:1460,NOP,WS:3,NOP,NOP,TS:#{ts_val};#{ts_sec},SACKOK,EOL,EOL"
963
+ @ip_header.ip_ttl = 64
964
+ else
965
+ @tcp_header.tcp_options="MSS:1460,NOP,NOP,SACKOK"
966
+ end
967
+ tcp_calc_sum
968
+ end
969
+
970
+ # tcp_calc_sum() computes the TCP checksum, and is called upon intialization. It usually
971
+ # should be called just prior to dropping packets to a file or on the wire.
972
+ #--
973
+ # This is /not/ delegated down to @tcp_header since we need info
974
+ # from the IP header, too.
975
+ #++
976
+ def tcp_calc_sum
977
+ checksum = (ip_src.to_i >> 16)
978
+ checksum += (ip_src.to_i & 0xffff)
979
+ checksum += (ip_dst.to_i >> 16)
980
+ checksum += (ip_dst.to_i & 0xffff)
981
+ checksum += 0x06 # TCP Protocol.
982
+ checksum += (ip_len.to_i - ((ip_hl.to_i) * 4))
983
+ checksum += tcp_src
984
+ checksum += tcp_dst
985
+ checksum += (tcp_seq.to_i >> 16)
986
+ checksum += (tcp_seq.to_i & 0xffff)
987
+ checksum += (tcp_ack.to_i >> 16)
988
+ checksum += (tcp_ack.to_i & 0xffff)
989
+ checksum += ((tcp_hlen << 12) +
990
+ (tcp_reserved << 9) +
991
+ (tcp_ecn.to_i << 6) +
992
+ tcp_flags.to_i
993
+ )
994
+ checksum += tcp_win
995
+ checksum += tcp_urg
996
+
997
+ chk_tcp_opts = (tcp_opts.to_s.size % 2 == 0 ? tcp_opts.to_s : tcp_opts.to_s + "\x00")
998
+ chk_tcp_opts.unpack("n*").each {|x| checksum = checksum + x }
999
+ if (ip_len - ((ip_hl + tcp_hlen) * 4)) >= 0
1000
+ real_tcp_payload = payload[0,( ip_len - ((ip_hl + tcp_hlen) * 4) )] # Can't forget those pesky FCSes!
1001
+ else
1002
+ real_tcp_payload = payload # Something's amiss here so don't bother figuring out where the real payload is.
1003
+ end
1004
+ chk_payload = (real_tcp_payload.size % 2 == 0 ? real_tcp_payload : real_tcp_payload + "\x00") # Null pad if it's odd.
1005
+ chk_payload.unpack("n*").each {|x| checksum = checksum+x }
1006
+ checksum = checksum % 0xffff
1007
+ checksum = 0xffff - checksum
1008
+ checksum == 0 ? 0xffff : checksum
1009
+ @tcp_header.tcp_sum = checksum
1010
+ end
1011
+
1012
+ # Recalculates various fields of the TCP packet.
1013
+ #
1014
+ # ==== Parameters
1015
+ #
1016
+ # :all
1017
+ # Recomputes all calculated fields.
1018
+ # :tcp_sum
1019
+ # Recomputes the TCP checksum.
1020
+ # :tcp_hlen
1021
+ # Recomputes the TCP header length. Useful after options are added.
1022
+ def tcp_recalc(arg=:all)
1023
+ case arg
1024
+ when :tcp_sum
1025
+ tcp_calc_sum
1026
+ when :tcp_hlen
1027
+ @tcp_header.tcp_recalc :tcp_hlen
1028
+ when :all
1029
+ @tcp_header.tcp_recalc :all
1030
+ tcp_calc_sum
1031
+ else
1032
+ raise ArgumentError, "No such field `#{arg}'"
1033
+ end
1034
+ end
1035
+
1036
+ # Peek provides summary data on packet contents.
1037
+ def peek(args={})
1038
+ peek_data = ["T "]
1039
+ peek_data << "%-5d" % self.to_s.size
1040
+ peek_data << "%-21s" % "#{self.ip_saddr}:#{self.tcp_src}"
1041
+ peek_data << "->"
1042
+ peek_data << "%21s" % "#{self.ip_daddr}:#{self.tcp_dst}"
1043
+ flags = ' ['
1044
+ flags << (self.tcp_flags.urg.zero? ? "." : "U")
1045
+ flags << (self.tcp_flags.ack.zero? ? "." : "A")
1046
+ flags << (self.tcp_flags.psh.zero? ? "." : "P")
1047
+ flags << (self.tcp_flags.rst.zero? ? "." : "R")
1048
+ flags << (self.tcp_flags.syn.zero? ? "." : "S")
1049
+ flags << (self.tcp_flags.fin.zero? ? "." : "F")
1050
+ flags << '] '
1051
+ peek_data << flags
1052
+ peek_data << "S:"
1053
+ peek_data << "%08x" % self.tcp_seq
1054
+ peek_data << "|I:"
1055
+ peek_data << "%04x" % self.ip_id
1056
+ peek_data.join
1057
+ end
1058
+
1059
+ end
1060
+
1061
+ end