packetfu 1.1.2 → 1.1.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. data/.gitignore +3 -0
  2. data/INSTALL.rdoc +40 -0
  3. data/LICENSE.txt +25 -0
  4. data/examples/100kpackets.rb +41 -0
  5. data/examples/ackscan.rb +38 -0
  6. data/examples/arp.rb +60 -0
  7. data/examples/arphood.rb +59 -0
  8. data/examples/dissect_thinger.rb +22 -0
  9. data/examples/ethernet.rb +10 -0
  10. data/examples/examples.rb +3 -0
  11. data/examples/ids.rb +4 -0
  12. data/examples/idsv2.rb +6 -0
  13. data/examples/new-simple-stats.rb +52 -0
  14. data/examples/oui.txt +84177 -0
  15. data/examples/packetfu-shell.rb +113 -0
  16. data/examples/simple-sniffer.rb +40 -0
  17. data/examples/simple-stats.rb +50 -0
  18. data/examples/slammer.rb +33 -0
  19. data/examples/uniqpcap.rb +15 -0
  20. data/lib/packetfu.rb +147 -0
  21. data/lib/packetfu/capture.rb +169 -0
  22. data/lib/packetfu/config.rb +58 -0
  23. data/lib/packetfu/inject.rb +65 -0
  24. data/lib/packetfu/packet.rb +533 -0
  25. data/lib/packetfu/pcap.rb +594 -0
  26. data/lib/packetfu/protos/arp.rb +268 -0
  27. data/lib/packetfu/protos/eth.rb +296 -0
  28. data/lib/packetfu/protos/hsrp.rb +206 -0
  29. data/lib/packetfu/protos/icmp.rb +179 -0
  30. data/lib/packetfu/protos/invalid.rb +55 -0
  31. data/lib/packetfu/protos/ip.rb +378 -0
  32. data/lib/packetfu/protos/ipv6.rb +250 -0
  33. data/lib/packetfu/protos/tcp.rb +1127 -0
  34. data/lib/packetfu/protos/udp.rb +240 -0
  35. data/lib/packetfu/structfu.rb +294 -0
  36. data/lib/packetfu/utils.rb +194 -0
  37. data/lib/packetfu/version.rb +50 -0
  38. data/packetfu.gemspec +21 -0
  39. data/setup.rb +1586 -0
  40. data/test/all_tests.rb +41 -0
  41. data/test/ethpacket_spec.rb +74 -0
  42. data/test/packet_spec.rb +73 -0
  43. data/test/packet_subclasses_spec.rb +13 -0
  44. data/test/packetfu_spec.rb +90 -0
  45. data/test/ptest.rb +16 -0
  46. data/test/sample-ipv6.pcap +0 -0
  47. data/test/sample.pcap +0 -0
  48. data/test/sample2.pcap +0 -0
  49. data/test/sample_hsrp_pcapr.cap +0 -0
  50. data/test/structfu_spec.rb +335 -0
  51. data/test/tcp_spec.rb +101 -0
  52. data/test/test_arp.rb +135 -0
  53. data/test/test_eth.rb +91 -0
  54. data/test/test_hsrp.rb +20 -0
  55. data/test/test_icmp.rb +54 -0
  56. data/test/test_inject.rb +31 -0
  57. data/test/test_invalid.rb +28 -0
  58. data/test/test_ip.rb +69 -0
  59. data/test/test_ip6.rb +68 -0
  60. data/test/test_octets.rb +37 -0
  61. data/test/test_packet.rb +174 -0
  62. data/test/test_pcap.rb +209 -0
  63. data/test/test_structfu.rb +112 -0
  64. data/test/test_tcp.rb +327 -0
  65. data/test/test_udp.rb +73 -0
  66. data/test/vlan-pcapr.cap +0 -0
  67. metadata +85 -6
@@ -0,0 +1,250 @@
1
+ module PacketFu
2
+
3
+ # AddrIpv6 handles addressing for IPv6Header
4
+ #
5
+ # ==== Header Definition
6
+ #
7
+ # Int32 :a1
8
+ # Int32 :a2
9
+ # Int32 :a3
10
+ # Int32 :a4
11
+ class AddrIpv6 < Struct.new(:a1, :a2, :a3, :a4)
12
+
13
+ include StructFu
14
+
15
+ def initialize(args={})
16
+ super(
17
+ Int32.new(args[:a1]),
18
+ Int32.new(args[:a2]),
19
+ Int32.new(args[:a3]),
20
+ Int32.new(args[:a4]))
21
+ end
22
+
23
+ # Returns the address in string format.
24
+ def to_s
25
+ self.to_a.map {|x| x.to_s}.join
26
+ end
27
+
28
+ # Returns the address as a fairly ginormous integer.
29
+ def to_i
30
+ (a1.to_i << 96) + (a2.to_i << 64) + (a3.to_i << 32) + a4.to_i
31
+ end
32
+
33
+ # Returns the address as a colon-delimited hex string.
34
+ def to_x
35
+ IPAddr.new(self.to_i, Socket::AF_INET6).to_s
36
+ end
37
+
38
+ # Reads in a string and casts it as an IPv6 address
39
+ def read(str)
40
+ force_binary(str)
41
+ return self if str.nil?
42
+ self[:a1].read str[0,4]
43
+ self[:a2].read str[4,4]
44
+ self[:a3].read str[8,4]
45
+ self[:a4].read str[12,4]
46
+ self
47
+ end
48
+
49
+ # Reads in a colon-delimited hex string and casts it as an IPv6 address.
50
+ def read_x(str)
51
+ addr = IPAddr.new(str).to_i
52
+ self[:a1]=Int32.new(addr >> 96)
53
+ self[:a2]=Int32.new((addr & 0x00000000ffffffff0000000000000000) >> 64)
54
+ self[:a3]=Int32.new((addr & 0x0000000000000000ffffffff00000000) >> 32)
55
+ self[:a4]=Int32.new(addr & 0x000000000000000000000000ffffffff)
56
+ self
57
+ end
58
+
59
+ end
60
+
61
+ # IPv6Header is complete IPv6 struct, used in IPv6Packet.
62
+ #
63
+ # ==== Header Definition
64
+ #
65
+ # Fixnum (4 bits) :ipv6_v Default: 6 # Versiom
66
+ # Fixnum (8 bits) :ipv6_class Defualt: 0 # Class
67
+ # Fixnum (20 bits) :ipv6_label Defualt: 0 # Label
68
+ # Int16 :ipv6_len Default: calc # Payload length
69
+ # Int8 :ipv6_next # Next Header
70
+ # Int8 :ipv6_hop Default: 0xff # Hop limit
71
+ # AddrIpv6 :ipv6_src
72
+ # AddrIpv6 :ipv6_dst
73
+ # String :body
74
+ class IPv6Header < Struct.new(:ipv6_v, :ipv6_class, :ipv6_label,
75
+ :ipv6_len, :ipv6_next, :ipv6_hop,
76
+ :ipv6_src, :ipv6_dst, :body)
77
+ include StructFu
78
+
79
+ def initialize(args={})
80
+ super(
81
+ (args[:ipv6_v] || 6),
82
+ (args[:ipv6_class] || 0),
83
+ (args[:ipv6_label] || 0),
84
+ Int16.new(args[:ipv6_len]),
85
+ Int8.new(args[:ipv6_next]),
86
+ Int8.new(args[:ipv6_hop] || 0xff),
87
+ AddrIpv6.new.read(args[:ipv6_src] || ("\x00" * 16)),
88
+ AddrIpv6.new.read(args[:ipv6_dst] || ("\x00" * 16)),
89
+ StructFu::String.new.read(args[:body])
90
+ )
91
+ end
92
+
93
+ # Returns the object in string form.
94
+ def to_s
95
+ bytes_v_class_label = [(self.ipv6_v << 28) +
96
+ (self.ipv6_class << 20) +
97
+ self.ipv6_label].pack("N")
98
+ bytes_v_class_label + (self.to_a[3,6].map {|x| x.to_s}.join)
99
+ end
100
+
101
+ # Reads a string to populate the object.
102
+ def read(str)
103
+ force_binary(str)
104
+ return self if str.nil?
105
+ self[:ipv6_v] = str[0,1].unpack("C").first >> 4
106
+ self[:ipv6_class] = (str[0,2].unpack("n").first & 0x0ff0) >> 4
107
+ self[:ipv6_label] = str[0,4].unpack("N").first & 0x000fffff
108
+ self[:ipv6_len].read(str[4,2])
109
+ self[:ipv6_next].read(str[6,1])
110
+ self[:ipv6_hop].read(str[7,1])
111
+ self[:ipv6_src].read(str[8,16])
112
+ self[:ipv6_dst].read(str[24,16])
113
+ self[:body].read(str[40,str.size]) if str.size > 40
114
+ self
115
+ end
116
+
117
+ # Setter for the version (usually, 6).
118
+ def ipv6_v=(i); self[:ip_v] = i.to_i; end
119
+ # Getter for the version (usually, 6).
120
+ def ipv6_v; self[:ipv6_v].to_i; end
121
+ # Setter for the traffic class.
122
+ def ipv6_class=(i); self[:ip_class] = i.to_i; end
123
+ # Getter for the traffic class.
124
+ def ipv6_class; self[:ipv6_class].to_i; end
125
+ # Setter for the flow label.
126
+ def ipv6_label=(i); self[:ip_label] = i.to_i; end
127
+ # Getter for the flow label.
128
+ def ipv6_label; self[:ipv6_label].to_i; end
129
+ # Setter for the payload length.
130
+ def ipv6_len=(i); typecast i; end
131
+ # Getter for the payload length.
132
+ def ipv6_len; self[:ipv6_len].to_i; end
133
+ # Setter for the next protocol header.
134
+ def ipv6_next=(i); typecast i; end
135
+ # Getter for the next protocol header.
136
+ def ipv6_next; self[:ipv6_next].to_i; end
137
+ # Setter for the hop limit.
138
+ def ipv6_hop=(i); typecast i; end
139
+ # Getter for the hop limit.
140
+ def ipv6_hop; self[:ipv6_hop].to_i; end
141
+ # Setter for the source address.
142
+ def ipv6_src=(i); typecast i; end
143
+ # Getter for the source address.
144
+ def ipv6_src; self[:ipv6_src].to_i; end
145
+ # Setter for the destination address.
146
+ def ipv6_dst=(i); typecast i; end
147
+ # Getter for the destination address.
148
+ def ipv6_dst; self[:ipv6_dst].to_i; end
149
+
150
+ # Calculates the payload length.
151
+ def ipv6_calc_len
152
+ self[:ipv6_len] = body.to_s.length
153
+ end
154
+
155
+ # Recalculates the calculatable fields for this object.
156
+ def ipv6_recalc(arg=:all)
157
+ case arg
158
+ when :ipv6_len
159
+ ipv6_calc_len
160
+ when :all
161
+ ipv6_recalc(:len)
162
+ end
163
+ end
164
+
165
+ # Get the source address in a more readable form.
166
+ def ipv6_saddr
167
+ self[:ipv6_src].to_x
168
+ end
169
+
170
+ # Set the source address in a more readable form.
171
+ def ipv6_saddr=(str)
172
+ self[:ipv6_src].read_x(str)
173
+ end
174
+
175
+ # Get the destination address in a more readable form.
176
+ def ipv6_daddr
177
+ self[:ipv6_dst].to_x
178
+ end
179
+
180
+ # Set the destination address in a more readable form.
181
+ def ipv6_daddr=(str)
182
+ self[:ipv6_dst].read_x(str)
183
+ end
184
+
185
+ # Readability aliases
186
+
187
+ alias :ipv6_src_readable :ipv6_saddr
188
+ alias :ipv6_dst_readable :ipv6_daddr
189
+
190
+ end # class IPv6Header
191
+
192
+ # IPv6Packet is used to construct IPv6 Packets. They contain an EthHeader and an IPv6Header, and in
193
+ # the distant, unknowable future, will take interesting IPv6ish payloads.
194
+ #
195
+ # This mostly complete, but not very useful. It's intended primarily as an example protocol.
196
+ #
197
+ # == Parameters
198
+ #
199
+ # :eth
200
+ # A pre-generated EthHeader object.
201
+ # :ip
202
+ # A pre-generated IPHeader object.
203
+ # :flavor
204
+ # TODO: Sets the "flavor" of the IPv6 packet. No idea what this will look like, haven't done much IPv6 fingerprinting.
205
+ # :config
206
+ # A hash of return address details, often the output of Utils.whoami?
207
+ class IPv6Packet < Packet
208
+
209
+ attr_accessor :eth_header, :ipv6_header
210
+
211
+ def self.can_parse?(str)
212
+ return false unless EthPacket.can_parse? str
213
+ return false unless str.size >= 54
214
+ return false unless str[12,2] == "\x86\xdd"
215
+ true
216
+ end
217
+
218
+ def read(str=nil,args={})
219
+ raise "Cannot parse `#{str}'" unless self.class.can_parse?(str)
220
+ @eth_header.read(str)
221
+ @ipv6_header.read(str[14,str.size])
222
+ @eth_header.body = @ipv6_header
223
+ super(args)
224
+ self
225
+ end
226
+
227
+ def initialize(args={})
228
+ @eth_header = (args[:eth] || EthHeader.new)
229
+ @ipv6_header = (args[:ipv6] || IPv6Header.new)
230
+ @eth_header.eth_proto = 0x86dd
231
+ @eth_header.body=@ipv6_header
232
+ @headers = [@eth_header, @ipv6_header]
233
+ super
234
+ end
235
+
236
+ # Peek provides summary data on packet contents.
237
+ def peek(args={})
238
+ peek_data = ["6 "]
239
+ peek_data << "%-5d" % self.to_s.size
240
+ peek_data << "%-31s" % self.ipv6_saddr
241
+ peek_data << "-> "
242
+ peek_data << "%-31s" % self.ipv6_daddr
243
+ peek_data << " N:"
244
+ peek_data << self.ipv6_next.to_s(16)
245
+ peek_data.join
246
+ end
247
+
248
+ end
249
+
250
+ end
@@ -0,0 +1,1127 @@
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
+ # Reads a string to populate the object.
550
+ def read(str)
551
+ self.clear
552
+ PacketFu.force_binary(str)
553
+ return self if(!str.respond_to? :to_s || str.nil?)
554
+ i = 0
555
+ while i < str.to_s.size
556
+ this_opt = case str[i,1].unpack("C").first
557
+ when 0; TcpOption::EOL.new
558
+ when 1; TcpOption::NOP.new
559
+ when 2; TcpOption::MSS.new
560
+ when 3; TcpOption::WS.new
561
+ when 4; TcpOption::SACKOK.new
562
+ when 5; TcpOption::SACK.new
563
+ when 6; TcpOption::ECHO.new
564
+ when 7; TcpOption::ECHOREPLY.new
565
+ when 8; TcpOption::TS.new
566
+ else; TcpOption.new
567
+ end
568
+ this_opt.read str[i,str.size]
569
+ unless this_opt.has_optlen?
570
+ this_opt.value = nil
571
+ this_opt.optlen = nil
572
+ end
573
+ self << this_opt
574
+ i += this_opt.sz
575
+ end
576
+ self
577
+ end
578
+
579
+ # Decode parses the TcpOptions object's member options, and produces a
580
+ # human-readable string by iterating over each element's decode() function.
581
+ # If TcpOptions elements were not initially created as TcpOptions, an
582
+ # attempt will be made to convert them.
583
+ #
584
+ # The output of decode is suitable as input for TcpOptions#encode.
585
+ def decode
586
+ decoded = self.map do |x|
587
+ if x.kind_of? TcpOption
588
+ x.decode
589
+ else
590
+ x = TcpOptions.new.read(x).decode
591
+ end
592
+ end
593
+ decoded.join(",")
594
+ end
595
+
596
+ # Encode takes a human-readable string and appends the corresponding
597
+ # binary options to the TcpOptions object. To completely replace the contents
598
+ # of the object, use TcpOptions#encode! instead.
599
+ #
600
+ # Options are comma-delimited, and are identical to the output of the
601
+ # TcpOptions#decode function. Note that the syntax can be unforgiving, so
602
+ # it may be easier to create the subclassed TcpOptions themselves directly,
603
+ # but this method can be less typing if you know what you're doing.
604
+ #
605
+ # Note that by using TcpOptions#encode, strings supplied as values which
606
+ # can be converted to numbers will be converted first.
607
+ #
608
+ # === Example
609
+ #
610
+ # t = TcpOptions.new
611
+ # t.encode("MS:1460,WS:6")
612
+ # t.to_s # => "\002\004\005\264\002\003\006"
613
+ # t.encode("NOP")
614
+ # t.to_s # => "\002\004\005\264\002\003\006\001"
615
+ def encode(str)
616
+ opts = str.split(/[\s]*,[\s]*/)
617
+ opts.each do |o|
618
+ kind,value = o.split(/[\s]*:[\s]*/)
619
+ klass = TcpOption.const_get(kind.upcase)
620
+ value = value.to_i if value =~ /^[0-9]+$/
621
+ this_opt = klass.new
622
+ this_opt.encode(value)
623
+ self << this_opt
624
+ end
625
+ self
626
+ end
627
+
628
+ # Like TcpOption#encode, except the entire contents are replaced.
629
+ def encode!(str)
630
+ self.clear if self.size > 0
631
+ encode(str)
632
+ end
633
+
634
+ end
635
+
636
+ end
637
+
638
+ module PacketFu
639
+
640
+ # TCPHeader is a complete TCP struct, used in TCPPacket. Most IP traffic is TCP-based, by
641
+ # volume.
642
+ #
643
+ # For more on TCP packets, see http://www.networksorcery.com/enp/protocol/tcp.htm
644
+ #
645
+ # ==== Header Definition
646
+ #
647
+ # Int16 :tcp_src Default: random
648
+ # Int16 :tcp_dst
649
+ # Int32 :tcp_seq Default: random
650
+ # Int32 :tcp_ack
651
+ # TcpHlen :tcp_hlen Default: 5 # Must recalc as options are set.
652
+ # TcpReserved :tcp_reserved Default: 0
653
+ # TcpEcn :tcp_ecn
654
+ # TcpFlags :tcp_flags
655
+ # Int16 :tcp_win, Default: 0 # WinXP's default syn packet
656
+ # Int16 :tcp_sum, Default: calculated # Must set this upon generation.
657
+ # Int16 :tcp_urg
658
+ # TcpOptions :tcp_opts
659
+ # String :body
660
+ #
661
+ # See also TcpHlen, TcpReserved, TcpEcn, TcpFlags, TcpOpts
662
+ class TCPHeader < Struct.new(:tcp_src, :tcp_dst,
663
+ :tcp_seq,
664
+ :tcp_ack,
665
+ :tcp_hlen, :tcp_reserved, :tcp_ecn, :tcp_flags, :tcp_win,
666
+ :tcp_sum, :tcp_urg,
667
+ :tcp_opts, :body)
668
+ include StructFu
669
+
670
+ def initialize(args={})
671
+ @random_seq = rand(0xffffffff)
672
+ @random_src = rand_port
673
+ super(
674
+ Int16.new(args[:tcp_src] || tcp_calc_src),
675
+ Int16.new(args[:tcp_dst]),
676
+ Int32.new(args[:tcp_seq] || tcp_calc_seq),
677
+ Int32.new(args[:tcp_ack]),
678
+ TcpHlen.new(:hlen => (args[:tcp_hlen] || 5)),
679
+ TcpReserved.new(args[:tcp_reserved] || 0),
680
+ TcpEcn.new(args[:tcp_ecn]),
681
+ TcpFlags.new(args[:tcp_flags]),
682
+ Int16.new(args[:tcp_win] || 0x4000),
683
+ Int16.new(args[:tcp_sum] || 0),
684
+ Int16.new(args[:tcp_urg]),
685
+ TcpOptions.new.read(args[:tcp_opts]),
686
+ StructFu::String.new.read(args[:body])
687
+ )
688
+ end
689
+
690
+ attr_accessor :flavor
691
+
692
+ # Helper function to create the string for Hlen, Reserved, ECN, and Flags.
693
+ def bits_to_s
694
+ bytes = []
695
+ bytes[0] = (self[:tcp_hlen].to_i << 4) +
696
+ (self[:tcp_reserved].to_i << 1) +
697
+ self[:tcp_ecn].n.to_i
698
+ bytes[1] = (self[:tcp_ecn].c.to_i << 7) +
699
+ (self[:tcp_ecn].e.to_i << 6) +
700
+ self[:tcp_flags].to_i
701
+ bytes.pack("CC")
702
+ end
703
+
704
+ # Returns the object in string form.
705
+ def to_s
706
+ hdr = self.to_a.map do |x|
707
+ if x.kind_of? TcpHlen
708
+ bits_to_s
709
+ elsif x.kind_of? TcpReserved
710
+ next
711
+ elsif x.kind_of? TcpEcn
712
+ next
713
+ elsif x.kind_of? TcpFlags
714
+ next
715
+ else
716
+ x.to_s
717
+ end
718
+ end
719
+ hdr.flatten.join
720
+ end
721
+
722
+ # Reads a string to populate the object.
723
+ def read(str)
724
+ force_binary(str)
725
+ return self if str.nil?
726
+ self[:tcp_src].read(str[0,2])
727
+ self[:tcp_dst].read(str[2,2])
728
+ self[:tcp_seq].read(str[4,4])
729
+ self[:tcp_ack].read(str[8,4])
730
+ self[:tcp_hlen].read(str[12,1])
731
+ self[:tcp_reserved].read(str[12,1])
732
+ self[:tcp_ecn].read(str[12,2])
733
+ self[:tcp_flags].read(str[13,1])
734
+ self[:tcp_win].read(str[14,2])
735
+ self[:tcp_sum].read(str[16,2])
736
+ self[:tcp_urg].read(str[18,2])
737
+ self[:tcp_opts].read(str[20,((self[:tcp_hlen].to_i * 4) - 20)])
738
+ self[:body].read(str[(self[:tcp_hlen].to_i * 4),str.size])
739
+ self
740
+ end
741
+
742
+ # Setter for the TCP source port.
743
+ def tcp_src=(i); typecast i; end
744
+ # Getter for the TCP source port.
745
+ def tcp_src; self[:tcp_src].to_i; end
746
+ # Setter for the TCP destination port.
747
+ def tcp_dst=(i); typecast i; end
748
+ # Getter for the TCP destination port.
749
+ def tcp_dst; self[:tcp_dst].to_i; end
750
+ # Setter for the TCP sequence number.
751
+ def tcp_seq=(i); typecast i; end
752
+ # Getter for the TCP sequence number.
753
+ def tcp_seq; self[:tcp_seq].to_i; end
754
+ # Setter for the TCP ackowlegement number.
755
+ def tcp_ack=(i); typecast i; end
756
+ # Getter for the TCP ackowlegement number.
757
+ def tcp_ack; self[:tcp_ack].to_i; end
758
+ # Setter for the TCP window size number.
759
+ def tcp_win=(i); typecast i; end
760
+ # Getter for the TCP window size number.
761
+ def tcp_win; self[:tcp_win].to_i; end
762
+ # Setter for the TCP checksum.
763
+ def tcp_sum=(i); typecast i; end
764
+ # Getter for the TCP checksum.
765
+ def tcp_sum; self[:tcp_sum].to_i; end
766
+ # Setter for the TCP urgent field.
767
+ def tcp_urg=(i); typecast i; end
768
+ # Getter for the TCP urgent field.
769
+ def tcp_urg; self[:tcp_urg].to_i; end
770
+
771
+ # Getter for the TCP Header Length value.
772
+ def tcp_hlen; self[:tcp_hlen].to_i; end
773
+ # Setter for the TCP Header Length value. Can take
774
+ # either a string or an integer. Note that if it's
775
+ # a string, the top four bits are used.
776
+ def tcp_hlen=(i)
777
+ case i
778
+ when PacketFu::TcpHlen
779
+ self[:tcp_hlen] = i
780
+ when Numeric
781
+ self[:tcp_hlen] = TcpHlen.new(:hlen => i.to_i)
782
+ else
783
+ self[:tcp_hlen].read(i)
784
+ end
785
+ end
786
+
787
+ # Getter for the TCP Reserved field.
788
+ def tcp_reserved; self[:tcp_reserved].to_i; end
789
+ # Setter for the TCP Reserved field.
790
+ def tcp_reserved=(i)
791
+ case i
792
+ when PacketFu::TcpReserved
793
+ self[:tcp_reserved]=i
794
+ when Numeric
795
+ args = {}
796
+ args[:r1] = (i & 0b100) >> 2
797
+ args[:r2] = (i & 0b010) >> 1
798
+ args[:r3] = (i & 0b001)
799
+ self[:tcp_reserved] = TcpReserved.new(args)
800
+ else
801
+ self[:tcp_reserved].read(i)
802
+ end
803
+ end
804
+
805
+ # Getter for the ECN bits.
806
+ def tcp_ecn; self[:tcp_ecn].to_i; end
807
+ # Setter for the ECN bits.
808
+ def tcp_ecn=(i)
809
+ case i
810
+ when PacketFu::TcpEcn
811
+ self[:tcp_ecn]=i
812
+ when Numeric
813
+ args = {}
814
+ args[:n] = (i & 0b100) >> 2
815
+ args[:c] = (i & 0b010) >> 1
816
+ args[:e] = (i & 0b001)
817
+ self[:tcp_ecn] = TcpEcn.new(args)
818
+ else
819
+ self[:tcp_ecn].read(i)
820
+ end
821
+ end
822
+
823
+ # Getter for TCP Options.
824
+ def tcp_opts; self[:tcp_opts].to_s; end
825
+ # Setter for TCP Options.
826
+ def tcp_opts=(i)
827
+ case i
828
+ when PacketFu::TcpOptions
829
+ self[:tcp_opts]=i
830
+ else
831
+ self[:tcp_opts].read(i)
832
+ end
833
+ end
834
+
835
+ # Resets the sequence number to a new random number.
836
+ def tcp_calc_seq; @random_seq; end
837
+ # Resets the source port to a new random number.
838
+ def tcp_calc_src; @random_src; end
839
+
840
+ # Returns the actual length of the TCP options.
841
+ def tcp_opts_len
842
+ self[:tcp_opts].to_s.size
843
+ end
844
+
845
+ # Sets and returns the true length of the TCP Header.
846
+ # TODO: Think about making all the option stuff safer.
847
+ def tcp_calc_hlen
848
+ self[:tcp_hlen] = TcpHlen.new(:hlen => ((20 + tcp_opts_len) / 4))
849
+ end
850
+
851
+ # Generates a random high port. This is affected by packet flavor.
852
+ def rand_port
853
+ rand(0xffff - 1025) + 1025
854
+ end
855
+
856
+ # Gets a more readable option list.
857
+ def tcp_options
858
+ self[:tcp_opts].decode
859
+ end
860
+
861
+ # Gets a more readable flags list
862
+ def tcp_flags_dotmap
863
+ dotmap = tcp_flags.members.map do |flag|
864
+ status = self.tcp_flags.send flag
865
+ status == 0 ? "." : flag.to_s.upcase[0].chr
866
+ end
867
+ dotmap.join
868
+ end
869
+
870
+ # Sets a more readable option list.
871
+ def tcp_options=(arg)
872
+ self[:tcp_opts].encode arg
873
+ end
874
+
875
+ # Equivalent to tcp_src.
876
+ def tcp_sport
877
+ self.tcp_src.to_i
878
+ end
879
+
880
+ # Equivalent to tcp_src=.
881
+ def tcp_sport=(arg)
882
+ self.tcp_src=(arg)
883
+ end
884
+
885
+ # Equivalent to tcp_dst.
886
+ def tcp_dport
887
+ self.tcp_dst.to_i
888
+ end
889
+
890
+ # Equivalent to tcp_dst=.
891
+ def tcp_dport=(arg)
892
+ self.tcp_dst=(arg)
893
+ end
894
+
895
+ # Recalculates calculated fields for TCP (except checksum which is at the Packet level).
896
+ def tcp_recalc(arg=:all)
897
+ case arg
898
+ when :tcp_hlen
899
+ tcp_calc_hlen
900
+ when :tcp_src
901
+ @random_tcp_src = rand_port
902
+ when :tcp_sport
903
+ @random_tcp_src = rand_port
904
+ when :tcp_seq
905
+ @random_tcp_seq = rand(0xffffffff)
906
+ when :all
907
+ tcp_calc_hlen
908
+ @random_tcp_src = rand_port
909
+ @random_tcp_seq = rand(0xffffffff)
910
+ else
911
+ raise ArgumentError, "No such field `#{arg}'"
912
+ end
913
+ end
914
+
915
+ # Readability aliases
916
+
917
+ alias :tcp_flags_readable :tcp_flags_dotmap
918
+
919
+ def tcp_ack_readable
920
+ "0x%08x" % tcp_ack
921
+ end
922
+
923
+ def tcp_seq_readable
924
+ "0x%08x" % tcp_seq
925
+ end
926
+
927
+ def tcp_sum_readable
928
+ "0x%04x" % tcp_sum
929
+ end
930
+
931
+ def tcp_opts_readable
932
+ tcp_options
933
+ end
934
+
935
+ end
936
+
937
+ # TCPPacket is used to construct TCP packets. They contain an EthHeader, an IPHeader, and a TCPHeader.
938
+ #
939
+ # == Example
940
+ #
941
+ # tcp_pkt = PacketFu::TCPPacket.new
942
+ # tcp_pkt.tcp_flags.syn=1
943
+ # tcp_pkt.tcp_dst=80
944
+ # tcp_pkt.tcp_win=5840
945
+ # tcp_pkt.tcp_options="mss:1460,sack.ok,ts:#{rand(0xffffffff)};0,nop,ws:7"
946
+ #
947
+ # tcp_pkt.ip_saddr=[rand(0xff),rand(0xff),rand(0xff),rand(0xff)].join('.')
948
+ # tcp_pkt.ip_daddr=[rand(0xff),rand(0xff),rand(0xff),rand(0xff)].join('.')
949
+ #
950
+ # tcp_pkt.recalc
951
+ # tcp_pkt.to_f('/tmp/tcp.pcap')
952
+ #
953
+ # == Parameters
954
+ # :eth
955
+ # A pre-generated EthHeader object.
956
+ # :ip
957
+ # A pre-generated IPHeader object.
958
+ # :flavor
959
+ # TODO: Sets the "flavor" of the TCP packet. This will include TCP options and the initial window
960
+ # size, per stack. There is a lot of variety here, and it's one of the most useful methods to
961
+ # remotely fingerprint devices. :flavor will span both ip and tcp for consistency.
962
+ # :type
963
+ # TODO: Set up particular types of packets (syn, psh_ack, rst, etc). This can change the initial flavor.
964
+ # :config
965
+ # A hash of return address details, often the output of Utils.whoami?
966
+ class TCPPacket < Packet
967
+
968
+ attr_accessor :eth_header, :ip_header, :tcp_header
969
+
970
+ def self.can_parse?(str)
971
+ return false unless str.size >= 54
972
+ return false unless EthPacket.can_parse? str
973
+ return false unless IPPacket.can_parse? str
974
+ return false unless str[23,1] == "\x06"
975
+ return true
976
+ end
977
+
978
+ def read(str=nil, args={})
979
+ raise "Cannot parse `#{str}'" unless self.class.can_parse?(str)
980
+ @eth_header.read(str)
981
+ @ip_header.read(str[14,str.size])
982
+ @eth_header.body = @ip_header
983
+ if args[:strip]
984
+ tcp_len = str[16,2].unpack("n")[0] - 20
985
+ @tcp_header.read(str[14+(@ip_header.ip_hlen),tcp_len])
986
+ else
987
+ @tcp_header.read(str[14+(@ip_header.ip_hlen),str.size])
988
+ end
989
+ @ip_header.body = @tcp_header
990
+ super(args)
991
+ self
992
+ end
993
+
994
+ def initialize(args={})
995
+ @eth_header = (args[:eth] || EthHeader.new)
996
+ @ip_header = (args[:ip] || IPHeader.new)
997
+ @tcp_header = (args[:tcp] || TCPHeader.new)
998
+ @tcp_header.flavor = args[:flavor].to_s.downcase
999
+
1000
+ @ip_header.body = @tcp_header
1001
+ @eth_header.body = @ip_header
1002
+ @headers = [@eth_header, @ip_header, @tcp_header]
1003
+
1004
+ @ip_header.ip_proto=0x06
1005
+ super
1006
+ if args[:flavor]
1007
+ tcp_calc_flavor(@tcp_header.flavor)
1008
+ else
1009
+ tcp_calc_sum
1010
+ end
1011
+ end
1012
+
1013
+ # Sets the correct flavor for TCP Packets. Recognized flavors are:
1014
+ # windows, linux, freebsd
1015
+ def tcp_calc_flavor(str)
1016
+ ts_val = Time.now.to_i + rand(0x4fffffff)
1017
+ ts_sec = rand(0xffffff)
1018
+ case @tcp_header.flavor = str.to_s.downcase
1019
+ when "windows" # WinXP's default syn
1020
+ @tcp_header.tcp_win = 0x4000
1021
+ @tcp_header.tcp_options="MSS:1460,NOP,NOP,SACKOK"
1022
+ @tcp_header.tcp_src = rand(5000 - 1026) + 1026
1023
+ @ip_header.ip_ttl = 64
1024
+ when "linux" # Ubuntu Linux 2.6.24-19-generic default syn
1025
+ @tcp_header.tcp_win = 5840
1026
+ @tcp_header.tcp_options="MSS:1460,SACKOK,TS:#{ts_val};0,NOP,WS:7"
1027
+ @tcp_header.tcp_src = rand(61_000 - 32_000) + 32_000
1028
+ @ip_header.ip_ttl = 64
1029
+ when "freebsd" # Freebsd
1030
+ @tcp_header.tcp_win = 0xffff
1031
+ @tcp_header.tcp_options="MSS:1460,NOP,WS:3,NOP,NOP,TS:#{ts_val};#{ts_sec},SACKOK,EOL,EOL"
1032
+ @ip_header.ip_ttl = 64
1033
+ else
1034
+ @tcp_header.tcp_options="MSS:1460,NOP,NOP,SACKOK"
1035
+ end
1036
+ tcp_calc_sum
1037
+ end
1038
+
1039
+ # tcp_calc_sum() computes the TCP checksum, and is called upon intialization. It usually
1040
+ # should be called just prior to dropping packets to a file or on the wire.
1041
+ #--
1042
+ # This is /not/ delegated down to @tcp_header since we need info
1043
+ # from the IP header, too.
1044
+ #++
1045
+ def tcp_calc_sum
1046
+ checksum = (ip_src.to_i >> 16)
1047
+ checksum += (ip_src.to_i & 0xffff)
1048
+ checksum += (ip_dst.to_i >> 16)
1049
+ checksum += (ip_dst.to_i & 0xffff)
1050
+ checksum += 0x06 # TCP Protocol.
1051
+ checksum += (ip_len.to_i - ((ip_hl.to_i) * 4))
1052
+ checksum += tcp_src
1053
+ checksum += tcp_dst
1054
+ checksum += (tcp_seq.to_i >> 16)
1055
+ checksum += (tcp_seq.to_i & 0xffff)
1056
+ checksum += (tcp_ack.to_i >> 16)
1057
+ checksum += (tcp_ack.to_i & 0xffff)
1058
+ checksum += ((tcp_hlen << 12) +
1059
+ (tcp_reserved << 9) +
1060
+ (tcp_ecn.to_i << 6) +
1061
+ tcp_flags.to_i
1062
+ )
1063
+ checksum += tcp_win
1064
+ checksum += tcp_urg
1065
+
1066
+ chk_tcp_opts = (tcp_opts.to_s.size % 2 == 0 ? tcp_opts.to_s : tcp_opts.to_s + "\x00")
1067
+ chk_tcp_opts.unpack("n*").each {|x| checksum = checksum + x }
1068
+ if (ip_len - ((ip_hl + tcp_hlen) * 4)) >= 0
1069
+ real_tcp_payload = payload[0,( ip_len - ((ip_hl + tcp_hlen) * 4) )] # Can't forget those pesky FCSes!
1070
+ else
1071
+ real_tcp_payload = payload # Something's amiss here so don't bother figuring out where the real payload is.
1072
+ end
1073
+ chk_payload = (real_tcp_payload.size % 2 == 0 ? real_tcp_payload : real_tcp_payload + "\x00") # Null pad if it's odd.
1074
+ chk_payload.unpack("n*").each {|x| checksum = checksum+x }
1075
+ checksum = checksum % 0xffff
1076
+ checksum = 0xffff - checksum
1077
+ checksum == 0 ? 0xffff : checksum
1078
+ @tcp_header.tcp_sum = checksum
1079
+ end
1080
+
1081
+ # Recalculates various fields of the TCP packet.
1082
+ #
1083
+ # ==== Parameters
1084
+ #
1085
+ # :all
1086
+ # Recomputes all calculated fields.
1087
+ # :tcp_sum
1088
+ # Recomputes the TCP checksum.
1089
+ # :tcp_hlen
1090
+ # Recomputes the TCP header length. Useful after options are added.
1091
+ def tcp_recalc(arg=:all)
1092
+ case arg
1093
+ when :tcp_sum
1094
+ tcp_calc_sum
1095
+ when :tcp_hlen
1096
+ @tcp_header.tcp_recalc :tcp_hlen
1097
+ when :all
1098
+ @tcp_header.tcp_recalc :all
1099
+ tcp_calc_sum
1100
+ else
1101
+ raise ArgumentError, "No such field `#{arg}'"
1102
+ end
1103
+ end
1104
+
1105
+ # TCP packets are denoted by a "T ", followed by size,
1106
+ # source and dest information, packet flags, sequence
1107
+ # number, and IPID.
1108
+ def peek_format
1109
+ peek_data = ["T "]
1110
+ peek_data << "%-5d" % self.to_s.size
1111
+ peek_data << "%-21s" % "#{self.ip_saddr}:#{self.tcp_src}"
1112
+ peek_data << "->"
1113
+ peek_data << "%21s" % "#{self.ip_daddr}:#{self.tcp_dst}"
1114
+ flags = ' ['
1115
+ flags << self.tcp_flags_dotmap
1116
+ flags << '] '
1117
+ peek_data << flags
1118
+ peek_data << "S:"
1119
+ peek_data << "%08x" % self.tcp_seq
1120
+ peek_data << "|I:"
1121
+ peek_data << "%04x" % self.ip_id
1122
+ peek_data.join
1123
+ end
1124
+
1125
+ end
1126
+
1127
+ end