packetfu 1.1.5 → 1.1.6

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 +5 -2
  2. data/.gitignore +1 -0
  3. data/LICENSE.txt +1 -1
  4. data/bench/after-2012-07-28.txt +25 -0
  5. data/bench/before-2012-07-28.txt +25 -0
  6. data/bench/benchit.rb +68 -0
  7. data/bench/calc_delta.rb +17 -0
  8. data/bench/octets.rb +22 -0
  9. data/bench/octets_after.txt +8 -0
  10. data/bench/octets_after_refactor.txt +8 -0
  11. data/bench/octets_before.txt +8 -0
  12. data/lib/packetfu.rb +8 -3
  13. data/lib/packetfu/packet.rb +2 -2
  14. data/lib/packetfu/pcap.rb +20 -4
  15. data/lib/packetfu/protos/arp.rb +7 -160
  16. data/lib/packetfu/protos/arp/header.rb +160 -0
  17. data/lib/packetfu/protos/arp/mixin.rb +38 -0
  18. data/lib/packetfu/protos/eth.rb +5 -247
  19. data/lib/packetfu/protos/eth/header.rb +247 -0
  20. data/lib/packetfu/protos/eth/mixin.rb +20 -0
  21. data/lib/packetfu/protos/hsrp.rb +13 -123
  22. data/lib/packetfu/protos/hsrp/header.rb +120 -0
  23. data/lib/packetfu/protos/hsrp/mixin.rb +31 -0
  24. data/lib/packetfu/protos/icmp.rb +10 -97
  25. data/lib/packetfu/protos/icmp/header.rb +93 -0
  26. data/lib/packetfu/protos/icmp/mixin.rb +17 -0
  27. data/lib/packetfu/protos/ip.rb +7 -295
  28. data/lib/packetfu/protos/ip/header.rb +335 -0
  29. data/lib/packetfu/protos/ip/mixin.rb +43 -0
  30. data/lib/packetfu/protos/ipv6.rb +7 -191
  31. data/lib/packetfu/protos/ipv6/header.rb +190 -0
  32. data/lib/packetfu/protos/ipv6/mixin.rb +31 -0
  33. data/lib/packetfu/protos/tcp.rb +13 -939
  34. data/lib/packetfu/protos/tcp/ecn.rb +42 -0
  35. data/lib/packetfu/protos/tcp/flags.rb +83 -0
  36. data/lib/packetfu/protos/tcp/header.rb +307 -0
  37. data/lib/packetfu/protos/tcp/hlen.rb +40 -0
  38. data/lib/packetfu/protos/tcp/mixin.rb +48 -0
  39. data/lib/packetfu/protos/tcp/option.rb +323 -0
  40. data/lib/packetfu/protos/tcp/options.rb +106 -0
  41. data/lib/packetfu/protos/tcp/reserved.rb +42 -0
  42. data/lib/packetfu/protos/udp.rb +12 -110
  43. data/lib/packetfu/protos/udp/header.rb +107 -0
  44. data/lib/packetfu/protos/udp/mixin.rb +23 -0
  45. data/lib/packetfu/utils.rb +24 -24
  46. data/lib/packetfu/version.rb +1 -1
  47. data/packetfu.gemspec +2 -2
  48. data/test/test_ip.rb +0 -19
  49. data/test/test_octets.rb +18 -21
  50. data/test/test_tcp.rb +10 -0
  51. data/test/test_udp.rb +17 -0
  52. metadata +79 -50
@@ -0,0 +1,190 @@
1
+ module PacketFu
2
+ # AddrIpv6 handles addressing for IPv6Header
3
+ #
4
+ # ==== Header Definition
5
+ #
6
+ # Int32 :a1
7
+ # Int32 :a2
8
+ # Int32 :a3
9
+ # Int32 :a4
10
+ class AddrIpv6 < Struct.new(:a1, :a2, :a3, :a4)
11
+
12
+ include StructFu
13
+
14
+ def initialize(args={})
15
+ super(
16
+ Int32.new(args[:a1]),
17
+ Int32.new(args[:a2]),
18
+ Int32.new(args[:a3]),
19
+ Int32.new(args[:a4]))
20
+ end
21
+
22
+ # Returns the address in string format.
23
+ def to_s
24
+ self.to_a.map {|x| x.to_s}.join
25
+ end
26
+
27
+ # Returns the address as a fairly ginormous integer.
28
+ def to_i
29
+ (a1.to_i << 96) + (a2.to_i << 64) + (a3.to_i << 32) + a4.to_i
30
+ end
31
+
32
+ # Returns the address as a colon-delimited hex string.
33
+ def to_x
34
+ IPAddr.new(self.to_i, Socket::AF_INET6).to_s
35
+ end
36
+
37
+ # Reads in a string and casts it as an IPv6 address
38
+ def read(str)
39
+ force_binary(str)
40
+ return self if str.nil?
41
+ self[:a1].read str[0,4]
42
+ self[:a2].read str[4,4]
43
+ self[:a3].read str[8,4]
44
+ self[:a4].read str[12,4]
45
+ self
46
+ end
47
+
48
+ # Reads in a colon-delimited hex string and casts it as an IPv6 address.
49
+ def read_x(str)
50
+ addr = IPAddr.new(str).to_i
51
+ self[:a1]=Int32.new(addr >> 96)
52
+ self[:a2]=Int32.new((addr & 0x00000000ffffffff0000000000000000) >> 64)
53
+ self[:a3]=Int32.new((addr & 0x0000000000000000ffffffff00000000) >> 32)
54
+ self[:a4]=Int32.new(addr & 0x000000000000000000000000ffffffff)
55
+ self
56
+ end
57
+
58
+ end
59
+
60
+ # IPv6Header is complete IPv6 struct, used in IPv6Packet.
61
+ #
62
+ # ==== Header Definition
63
+ #
64
+ # Fixnum (4 bits) :ipv6_v Default: 6 # Versiom
65
+ # Fixnum (8 bits) :ipv6_class Defualt: 0 # Class
66
+ # Fixnum (20 bits) :ipv6_label Defualt: 0 # Label
67
+ # Int16 :ipv6_len Default: calc # Payload length
68
+ # Int8 :ipv6_next # Next Header
69
+ # Int8 :ipv6_hop Default: 0xff # Hop limit
70
+ # AddrIpv6 :ipv6_src
71
+ # AddrIpv6 :ipv6_dst
72
+ # String :body
73
+ class IPv6Header < Struct.new(:ipv6_v, :ipv6_class, :ipv6_label,
74
+ :ipv6_len, :ipv6_next, :ipv6_hop,
75
+ :ipv6_src, :ipv6_dst, :body)
76
+ include StructFu
77
+
78
+ def initialize(args={})
79
+ super(
80
+ (args[:ipv6_v] || 6),
81
+ (args[:ipv6_class] || 0),
82
+ (args[:ipv6_label] || 0),
83
+ Int16.new(args[:ipv6_len]),
84
+ Int8.new(args[:ipv6_next]),
85
+ Int8.new(args[:ipv6_hop] || 0xff),
86
+ AddrIpv6.new.read(args[:ipv6_src] || ("\x00" * 16)),
87
+ AddrIpv6.new.read(args[:ipv6_dst] || ("\x00" * 16)),
88
+ StructFu::String.new.read(args[:body])
89
+ )
90
+ end
91
+
92
+ # Returns the object in string form.
93
+ def to_s
94
+ bytes_v_class_label = [(self.ipv6_v << 28) +
95
+ (self.ipv6_class << 20) +
96
+ self.ipv6_label].pack("N")
97
+ bytes_v_class_label + (self.to_a[3,6].map {|x| x.to_s}.join)
98
+ end
99
+
100
+ # Reads a string to populate the object.
101
+ def read(str)
102
+ force_binary(str)
103
+ return self if str.nil?
104
+ self[:ipv6_v] = str[0,1].unpack("C").first >> 4
105
+ self[:ipv6_class] = (str[0,2].unpack("n").first & 0x0ff0) >> 4
106
+ self[:ipv6_label] = str[0,4].unpack("N").first & 0x000fffff
107
+ self[:ipv6_len].read(str[4,2])
108
+ self[:ipv6_next].read(str[6,1])
109
+ self[:ipv6_hop].read(str[7,1])
110
+ self[:ipv6_src].read(str[8,16])
111
+ self[:ipv6_dst].read(str[24,16])
112
+ self[:body].read(str[40,str.size]) if str.size > 40
113
+ self
114
+ end
115
+
116
+ # Setter for the version (usually, 6).
117
+ def ipv6_v=(i); self[:ip_v] = i.to_i; end
118
+ # Getter for the version (usually, 6).
119
+ def ipv6_v; self[:ipv6_v].to_i; end
120
+ # Setter for the traffic class.
121
+ def ipv6_class=(i); self[:ip_class] = i.to_i; end
122
+ # Getter for the traffic class.
123
+ def ipv6_class; self[:ipv6_class].to_i; end
124
+ # Setter for the flow label.
125
+ def ipv6_label=(i); self[:ip_label] = i.to_i; end
126
+ # Getter for the flow label.
127
+ def ipv6_label; self[:ipv6_label].to_i; end
128
+ # Setter for the payload length.
129
+ def ipv6_len=(i); typecast i; end
130
+ # Getter for the payload length.
131
+ def ipv6_len; self[:ipv6_len].to_i; end
132
+ # Setter for the next protocol header.
133
+ def ipv6_next=(i); typecast i; end
134
+ # Getter for the next protocol header.
135
+ def ipv6_next; self[:ipv6_next].to_i; end
136
+ # Setter for the hop limit.
137
+ def ipv6_hop=(i); typecast i; end
138
+ # Getter for the hop limit.
139
+ def ipv6_hop; self[:ipv6_hop].to_i; end
140
+ # Setter for the source address.
141
+ def ipv6_src=(i); typecast i; end
142
+ # Getter for the source address.
143
+ def ipv6_src; self[:ipv6_src].to_i; end
144
+ # Setter for the destination address.
145
+ def ipv6_dst=(i); typecast i; end
146
+ # Getter for the destination address.
147
+ def ipv6_dst; self[:ipv6_dst].to_i; end
148
+
149
+ # Calculates the payload length.
150
+ def ipv6_calc_len
151
+ self[:ipv6_len] = body.to_s.length
152
+ end
153
+
154
+ # Recalculates the calculatable fields for this object.
155
+ def ipv6_recalc(arg=:all)
156
+ case arg
157
+ when :ipv6_len
158
+ ipv6_calc_len
159
+ when :all
160
+ ipv6_recalc(:len)
161
+ end
162
+ end
163
+
164
+ # Get the source address in a more readable form.
165
+ def ipv6_saddr
166
+ self[:ipv6_src].to_x
167
+ end
168
+
169
+ # Set the source address in a more readable form.
170
+ def ipv6_saddr=(str)
171
+ self[:ipv6_src].read_x(str)
172
+ end
173
+
174
+ # Get the destination address in a more readable form.
175
+ def ipv6_daddr
176
+ self[:ipv6_dst].to_x
177
+ end
178
+
179
+ # Set the destination address in a more readable form.
180
+ def ipv6_daddr=(str)
181
+ self[:ipv6_dst].read_x(str)
182
+ end
183
+
184
+ # Readability aliases
185
+
186
+ alias :ipv6_src_readable :ipv6_saddr
187
+ alias :ipv6_dst_readable :ipv6_daddr
188
+
189
+ end # class IPv6Header
190
+ end
@@ -0,0 +1,31 @@
1
+ module PacketFu
2
+ # This Mixin simplifies access to the IPv6Headers. Mix this in with your
3
+ # packet interface, and it will add methods that essentially delegate to
4
+ # the 'ipv6_header' method (assuming that it is a IPv6Header object)
5
+ module IPv6HeaderMixin
6
+ def ipv6_v=(v); self.ipv6_header.ipv6_v= v; end
7
+ def ipv6_v; self.ipv6_header.ipv6_v; end
8
+ def ipv6_class=(v); self.ipv6_header.ipv6_class= v; end
9
+ def ipv6_class; self.ipv6_header.ipv6_class; end
10
+ def ipv6_label=(v); self.ipv6_header.ipv6_label= v; end
11
+ def ipv6_label; self.ipv6_header.ipv6_label; end
12
+ def ipv6_len=(v); self.ipv6_header.ipv6_len= v; end
13
+ def ipv6_len; self.ipv6_header.ipv6_len; end
14
+ def ipv6_next=(v); self.ipv6_header.ipv6_next= v; end
15
+ def ipv6_next; self.ipv6_header.ipv6_next; end
16
+ def ipv6_hop=(v); self.ipv6_header.ipv6_hop= v; end
17
+ def ipv6_hop; self.ipv6_header.ipv6_hop; end
18
+ def ipv6_src=(v); self.ipv6_header.ipv6_src= v; end
19
+ def ipv6_src; self.ipv6_header.ipv6_src; end
20
+ def ipv6_dst=(v); self.ipv6_header.ipv6_dst= v; end
21
+ def ipv6_dst; self.ipv6_header.ipv6_dst; end
22
+ def ipv6_calc_len; self.ipv6_header.ipv6_calc_len; end
23
+ def ipv6_recalc(*v); self.ipv6_header.ipv6_recalc(*v); end
24
+ def ipv6_saddr; self.ipv6_header.ipv6_saddr; end
25
+ def ipv6_saddr=(v); self.ipv6_header.ipv6_saddr= v; end
26
+ def ipv6_daddr; self.ipv6_header.ipv6_daddr; end
27
+ def ipv6_daddr=(v); self.ipv6_header.ipv6_daddr= v; end
28
+ def ipv6_src_readable; self.ipv6_header.ipv6_src_readable; end
29
+ def ipv6_dst_readable; self.ipv6_header.ipv6_dst_readable; end
30
+ end
31
+ end
@@ -1,939 +1,13 @@
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
1
+ require 'packetfu/protos/eth/header'
2
+ require 'packetfu/protos/eth/mixin'
408
3
 
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
4
+ require 'packetfu/protos/tcp/header'
5
+ require 'packetfu/protos/tcp/mixin'
627
6
 
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
7
+ require 'packetfu/protos/ip/header'
8
+ require 'packetfu/protos/ip/mixin'
637
9
 
638
10
  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
11
  # TCPPacket is used to construct TCP packets. They contain an EthHeader, an IPHeader, and a TCPHeader.
938
12
  #
939
13
  # == Example
@@ -964,6 +38,9 @@ module PacketFu
964
38
  # :config
965
39
  # A hash of return address details, often the output of Utils.whoami?
966
40
  class TCPPacket < Packet
41
+ include ::PacketFu::EthHeaderMixin
42
+ include ::PacketFu::IPHeaderMixin
43
+ include ::PacketFu::TCPHeaderMixin
967
44
 
968
45
  attr_accessor :eth_header, :ip_header, :tcp_header
969
46
 
@@ -978,15 +55,12 @@ module PacketFu
978
55
  def read(str=nil, args={})
979
56
  raise "Cannot parse `#{str}'" unless self.class.can_parse?(str)
980
57
  @eth_header.read(str)
981
- @ip_header.read(str[14,str.size])
982
- @eth_header.body = @ip_header
58
+
59
+ # Strip off any extra data, if we are asked to do so.
983
60
  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])
61
+ tcp_body_len = self.ip_len - self.ip_hlen - (self.tcp_hlen * 4)
62
+ @tcp_header.body.read(@tcp_header.body.to_s[0,tcp_body_len])
988
63
  end
989
- @ip_header.body = @tcp_header
990
64
  super(args)
991
65
  self
992
66
  end