packetfu 1.1.9 → 1.1.10

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 (77) hide show
  1. data/bench/octets.rb +9 -9
  2. data/examples/100kpackets.rb +12 -12
  3. data/examples/ackscan.rb +16 -16
  4. data/examples/arp.rb +35 -35
  5. data/examples/arphood.rb +36 -36
  6. data/examples/dissect_thinger.rb +6 -6
  7. data/examples/new-simple-stats.rb +23 -23
  8. data/examples/packetfu-shell.rb +25 -25
  9. data/examples/simple-sniffer.rb +9 -9
  10. data/examples/simple-stats.rb +23 -23
  11. data/examples/slammer.rb +3 -3
  12. data/lib/packetfu.rb +127 -127
  13. data/lib/packetfu/capture.rb +169 -169
  14. data/lib/packetfu/config.rb +52 -52
  15. data/lib/packetfu/inject.rb +56 -56
  16. data/lib/packetfu/packet.rb +528 -528
  17. data/lib/packetfu/pcap.rb +579 -579
  18. data/lib/packetfu/protos/arp.rb +90 -90
  19. data/lib/packetfu/protos/arp/header.rb +158 -158
  20. data/lib/packetfu/protos/arp/mixin.rb +36 -36
  21. data/lib/packetfu/protos/eth.rb +44 -44
  22. data/lib/packetfu/protos/eth/header.rb +243 -243
  23. data/lib/packetfu/protos/eth/mixin.rb +3 -3
  24. data/lib/packetfu/protos/hsrp.rb +69 -69
  25. data/lib/packetfu/protos/hsrp/header.rb +107 -107
  26. data/lib/packetfu/protos/hsrp/mixin.rb +29 -29
  27. data/lib/packetfu/protos/icmp.rb +71 -71
  28. data/lib/packetfu/protos/icmp/header.rb +82 -82
  29. data/lib/packetfu/protos/icmp/mixin.rb +14 -14
  30. data/lib/packetfu/protos/invalid.rb +49 -49
  31. data/lib/packetfu/protos/ip.rb +69 -69
  32. data/lib/packetfu/protos/ip/header.rb +291 -291
  33. data/lib/packetfu/protos/ip/mixin.rb +40 -40
  34. data/lib/packetfu/protos/ipv6.rb +50 -50
  35. data/lib/packetfu/protos/ipv6/header.rb +188 -188
  36. data/lib/packetfu/protos/ipv6/mixin.rb +29 -29
  37. data/lib/packetfu/protos/tcp.rb +176 -176
  38. data/lib/packetfu/protos/tcp/ecn.rb +35 -35
  39. data/lib/packetfu/protos/tcp/flags.rb +74 -74
  40. data/lib/packetfu/protos/tcp/header.rb +268 -268
  41. data/lib/packetfu/protos/tcp/hlen.rb +32 -32
  42. data/lib/packetfu/protos/tcp/mixin.rb +46 -46
  43. data/lib/packetfu/protos/tcp/option.rb +321 -321
  44. data/lib/packetfu/protos/tcp/options.rb +95 -95
  45. data/lib/packetfu/protos/tcp/reserved.rb +35 -35
  46. data/lib/packetfu/protos/udp.rb +116 -116
  47. data/lib/packetfu/protos/udp/header.rb +91 -91
  48. data/lib/packetfu/protos/udp/mixin.rb +3 -3
  49. data/lib/packetfu/structfu.rb +280 -280
  50. data/lib/packetfu/utils.rb +226 -217
  51. data/lib/packetfu/version.rb +41 -41
  52. data/packetfu.gemspec +2 -1
  53. data/spec/ethpacket_spec.rb +48 -48
  54. data/spec/packet_spec.rb +57 -57
  55. data/spec/packet_subclasses_spec.rb +8 -8
  56. data/spec/packetfu_spec.rb +59 -59
  57. data/spec/structfu_spec.rb +268 -268
  58. data/spec/tcp_spec.rb +75 -75
  59. data/test/all_tests.rb +13 -13
  60. data/test/func_lldp.rb +3 -3
  61. data/test/ptest.rb +2 -2
  62. data/test/test_arp.rb +116 -116
  63. data/test/test_capture.rb +45 -45
  64. data/test/test_eth.rb +68 -68
  65. data/test/test_hsrp.rb +9 -9
  66. data/test/test_icmp.rb +52 -52
  67. data/test/test_inject.rb +18 -18
  68. data/test/test_invalid.rb +16 -16
  69. data/test/test_ip.rb +36 -36
  70. data/test/test_ip6.rb +48 -48
  71. data/test/test_octets.rb +21 -21
  72. data/test/test_packet.rb +154 -154
  73. data/test/test_pcap.rb +170 -170
  74. data/test/test_structfu.rb +97 -97
  75. data/test/test_tcp.rb +320 -320
  76. data/test/test_udp.rb +76 -76
  77. metadata +4 -3
@@ -7,84 +7,84 @@ require 'packetfu/protos/ip/mixin'
7
7
 
8
8
  module PacketFu
9
9
 
10
- # IPPacket is used to construct IP packets. They contain an EthHeader, an IPHeader, and usually
11
- # a transport-layer protocol such as UDPHeader, TCPHeader, or ICMPHeader.
12
- #
13
- # == Example
14
- #
15
- # require 'packetfu'
16
- # ip_pkt = PacketFu::IPPacket.new
17
- # ip_pkt.ip_saddr="10.20.30.40"
18
- # ip_pkt.ip_daddr="192.168.1.1"
19
- # ip_pkt.ip_proto=1
20
- # ip_pkt.ip_ttl=64
21
- # ip_pkt.ip_payload="\x00\x00\x12\x34\x00\x01\x00\x01"+
22
- # "Lovingly hand-crafted echo responses delivered directly to your door."
23
- # ip_pkt.recalc
24
- # ip_pkt.to_f('/tmp/ip.pcap')
25
- #
26
- # == Parameters
27
- #
28
- # :eth
29
- # A pre-generated EthHeader object.
30
- # :ip
31
- # A pre-generated IPHeader object.
32
- # :flavor
33
- # TODO: Sets the "flavor" of the IP packet. This might include known sets of IP options, and
34
- # certainly known starting TTLs.
35
- # :config
36
- # A hash of return address details, often the output of Utils.whoami?
37
- class IPPacket < Packet
10
+ # IPPacket is used to construct IP packets. They contain an EthHeader, an IPHeader, and usually
11
+ # a transport-layer protocol such as UDPHeader, TCPHeader, or ICMPHeader.
12
+ #
13
+ # == Example
14
+ #
15
+ # require 'packetfu'
16
+ # ip_pkt = PacketFu::IPPacket.new
17
+ # ip_pkt.ip_saddr="10.20.30.40"
18
+ # ip_pkt.ip_daddr="192.168.1.1"
19
+ # ip_pkt.ip_proto=1
20
+ # ip_pkt.ip_ttl=64
21
+ # ip_pkt.ip_payload="\x00\x00\x12\x34\x00\x01\x00\x01"+
22
+ # "Lovingly hand-crafted echo responses delivered directly to your door."
23
+ # ip_pkt.recalc
24
+ # ip_pkt.to_f('/tmp/ip.pcap')
25
+ #
26
+ # == Parameters
27
+ #
28
+ # :eth
29
+ # A pre-generated EthHeader object.
30
+ # :ip
31
+ # A pre-generated IPHeader object.
32
+ # :flavor
33
+ # TODO: Sets the "flavor" of the IP packet. This might include known sets of IP options, and
34
+ # certainly known starting TTLs.
35
+ # :config
36
+ # A hash of return address details, often the output of Utils.whoami?
37
+ class IPPacket < Packet
38
38
  include ::PacketFu::EthHeaderMixin
39
39
  include ::PacketFu::IPHeaderMixin
40
40
 
41
- attr_accessor :eth_header, :ip_header
41
+ attr_accessor :eth_header, :ip_header
42
42
 
43
- def self.can_parse?(str)
44
- return false unless str.size >= 34
45
- return false unless EthPacket.can_parse? str
46
- if str[12,2] == "\x08\x00"
47
- if 1.respond_to? :ord
48
- ipv = str[14,1][0].ord >> 4
49
- else
50
- ipv = str[14,1][0] >> 4
51
- end
52
- return true if ipv == 4
53
- else
54
- return false
55
- end
56
- end
43
+ def self.can_parse?(str)
44
+ return false unless str.size >= 34
45
+ return false unless EthPacket.can_parse? str
46
+ if str[12,2] == "\x08\x00"
47
+ if 1.respond_to? :ord
48
+ ipv = str[14,1][0].ord >> 4
49
+ else
50
+ ipv = str[14,1][0] >> 4
51
+ end
52
+ return true if ipv == 4
53
+ else
54
+ return false
55
+ end
56
+ end
57
57
 
58
- def read(str=nil, args={})
59
- raise "Cannot parse `#{str}'" unless self.class.can_parse?(str)
60
- @eth_header.read(str)
61
- super(args)
62
- self
63
- end
58
+ def read(str=nil, args={})
59
+ raise "Cannot parse `#{str}'" unless self.class.can_parse?(str)
60
+ @eth_header.read(str)
61
+ super(args)
62
+ self
63
+ end
64
64
 
65
- # Creates a new IPPacket object.
66
- def initialize(args={})
67
- @eth_header = EthHeader.new(args).read(args[:eth])
68
- @ip_header = IPHeader.new(args).read(args[:ip])
69
- @eth_header.body=@ip_header
65
+ # Creates a new IPPacket object.
66
+ def initialize(args={})
67
+ @eth_header = EthHeader.new(args).read(args[:eth])
68
+ @ip_header = IPHeader.new(args).read(args[:ip])
69
+ @eth_header.body=@ip_header
70
70
 
71
- @headers = [@eth_header, @ip_header]
72
- super
73
- end
71
+ @headers = [@eth_header, @ip_header]
72
+ super
73
+ end
74
74
 
75
- # Peek provides summary data on packet contents.
76
- def peek_format
77
- peek_data = ["I "]
78
- peek_data << "%-5d" % to_s.size
79
- peek_data << "%-21s" % "#{ip_saddr}"
80
- peek_data << "->"
81
- peek_data << "%21s" % "#{ip_daddr}"
82
- peek_data << "%23s" % "I:"
83
- peek_data << "%04x" % ip_id.to_i
84
- peek_data.join
85
- end
75
+ # Peek provides summary data on packet contents.
76
+ def peek_format
77
+ peek_data = ["I "]
78
+ peek_data << "%-5d" % to_s.size
79
+ peek_data << "%-21s" % "#{ip_saddr}"
80
+ peek_data << "->"
81
+ peek_data << "%21s" % "#{ip_daddr}"
82
+ peek_data << "%23s" % "I:"
83
+ peek_data << "%04x" % ip_id.to_i
84
+ peek_data.join
85
+ end
86
86
 
87
- end
87
+ end
88
88
 
89
89
  end
90
90
 
@@ -2,65 +2,65 @@
2
2
  require 'ipaddr'
3
3
 
4
4
  module PacketFu
5
- # Octets implements the addressing scheme for IP.
6
- #
7
- # ==== Header Definition
8
- #
9
- # Int32 :ip_addr
10
- class Octets < Struct.new(:ip_addr)
11
- include StructFu
12
-
13
- IPV4_RE = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/
14
- def initialize(args={})
15
- super(
16
- Int32.new(args[:ip_addr]))
17
- end
18
-
19
- # Returns the object in string form.
20
- def to_s
5
+ # Octets implements the addressing scheme for IP.
6
+ #
7
+ # ==== Header Definition
8
+ #
9
+ # Int32 :ip_addr
10
+ class Octets < Struct.new(:ip_addr)
11
+ include StructFu
12
+
13
+ IPV4_RE = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/
14
+ def initialize(args={})
15
+ super(
16
+ Int32.new(args[:ip_addr]))
17
+ end
18
+
19
+ # Returns the object in string form.
20
+ def to_s
21
21
  [self[:ip_addr].to_i].pack("N")
22
- end
23
-
24
- # Reads a string to populate the object.
25
- def read(str)
26
- force_binary(str)
27
- return self if str.nil?
28
- self[:ip_addr].read str[0,4]
29
- self
30
- end
31
-
32
- # Returns an address in dotted-quad format.
33
- def to_x
22
+ end
23
+
24
+ # Reads a string to populate the object.
25
+ def read(str)
26
+ force_binary(str)
27
+ return self if str.nil?
28
+ self[:ip_addr].read str[0,4]
29
+ self
30
+ end
31
+
32
+ # Returns an address in dotted-quad format.
33
+ def to_x
34
34
  # This could be slightly faster if we reproduced the code in
35
35
  # 'octets()' and didn't have to map to strings.
36
36
  self.octets.map(&:to_s).join('.')
37
- end
37
+ end
38
38
 
39
- # Returns an address in numerical format.
40
- def to_i
39
+ # Returns an address in numerical format.
40
+ def to_i
41
41
  self[:ip_addr].to_i
42
- end
43
-
44
- # Set the IP Address by reading a dotted-quad address.
45
- def read_quad(str)
46
- match = IPV4_RE.match(str)
47
- if match.nil?
48
- raise ArgumentError.new("str is not a valid IPV4 address")
49
- end
50
- a = match[1].to_i
51
- b = match[2].to_i
52
- c = match[3].to_i
53
- d = match[4].to_i
54
- unless (a >= 0 && a <= 255 &&
55
- b >= 0 && b <= 255 &&
56
- c >= 0 && c <= 255 &&
57
- d >= 0 && d <= 255)
58
- raise ArgumentError.new("str is not a valid IPV4 address")
59
- end
42
+ end
43
+
44
+ # Set the IP Address by reading a dotted-quad address.
45
+ def read_quad(str)
46
+ match = IPV4_RE.match(str)
47
+ if match.nil?
48
+ raise ArgumentError.new("str is not a valid IPV4 address")
49
+ end
50
+ a = match[1].to_i
51
+ b = match[2].to_i
52
+ c = match[3].to_i
53
+ d = match[4].to_i
54
+ unless (a >= 0 && a <= 255 &&
55
+ b >= 0 && b <= 255 &&
56
+ c >= 0 && c <= 255 &&
57
+ d >= 0 && d <= 255)
58
+ raise ArgumentError.new("str is not a valid IPV4 address")
59
+ end
60
60
 
61
61
  self[:ip_addr].value = (a<<24) + (b<<16) + (c<<8) + d
62
- self
63
- end
62
+ self
63
+ end
64
64
 
65
65
  # Returns the IP address as 4 octets
66
66
  def octets
@@ -93,244 +93,244 @@ module PacketFu
93
93
  self.to_i & 0xff
94
94
  end
95
95
 
96
- end
97
-
98
- # IPHeader is a complete IP struct, used in IPPacket. Most traffic on most networks today is IP-based.
99
- #
100
- # For more on IP packets, see http://www.networksorcery.com/enp/protocol/ip.htm
101
- #
102
- # ==== Header Definition
103
- #
104
- # Fixnum (4 bits) :ip_v, Default: 4
105
- # Fixnum (4 bits) :ip_hl, Default: 5
106
- # Int8 :ip_tos, Default: 0 # TODO: Break out the bits
107
- # Int16 :ip_len, Default: calculated
108
- # Int16 :ip_id, Default: calculated # IRL, hardly random.
109
- # Int16 :ip_frag, Default: 0 # TODO: Break out the bits
110
- # Int8 :ip_ttl, Default: 0xff # Changes per flavor
111
- # Int8 :ip_proto, Default: 0x01 # TCP: 0x06, UDP 0x11, ICMP 0x01
112
- # Int16 :ip_sum, Default: calculated
113
- # Octets :ip_src
114
- # Octets :ip_dst
115
- # String :body
116
- #
117
- # Note that IPPackets will always be somewhat incorrect upon initalization,
118
- # and want an IPHeader#recalc() to become correct before a
119
- # Packet#to_f or Packet#to_w.
120
- class IPHeader < Struct.new(:ip_v, :ip_hl, :ip_tos, :ip_len,
121
- :ip_id, :ip_frag, :ip_ttl, :ip_proto,
122
- :ip_sum, :ip_src, :ip_dst, :body)
123
- include StructFu
124
-
125
- def initialize(args={})
126
- @random_id = rand(0xffff)
127
- super(
128
- (args[:ip_v] || 4),
129
- (args[:ip_hl] || 5),
130
- Int8.new(args[:ip_tos]),
131
- Int16.new(args[:ip_len] || 20),
132
- Int16.new(args[:ip_id] || ip_calc_id),
133
- Int16.new(args[:ip_frag]),
134
- Int8.new(args[:ip_ttl] || 32),
135
- Int8.new(args[:ip_proto]),
136
- Int16.new(args[:ip_sum] || ip_calc_sum),
137
- Octets.new.read(args[:ip_src] || "\x00\x00\x00\x00"),
138
- Octets.new.read(args[:ip_dst] || "\x00\x00\x00\x00"),
139
- StructFu::String.new.read(args[:body])
140
- )
141
- end
142
-
143
- # Returns the object in string form.
144
- def to_s
145
- byte_v_hl = [(self.ip_v << 4) + self.ip_hl].pack("C")
146
- byte_v_hl + (self.to_a[2,10].map {|x| x.to_s}.join)
147
- end
148
-
149
- # Reads a string to populate the object.
150
- def read(str)
151
- force_binary(str)
152
- return self if str.nil?
153
- self[:ip_v] = str[0,1].unpack("C").first >> 4
154
- self[:ip_hl] = str[0,1].unpack("C").first.to_i & 0x0f
155
- self[:ip_tos].read(str[1,1])
156
- self[:ip_len].read(str[2,2])
157
- self[:ip_id].read(str[4,2])
158
- self[:ip_frag].read(str[6,2])
159
- self[:ip_ttl].read(str[8,1])
160
- self[:ip_proto].read(str[9,1])
161
- self[:ip_sum].read(str[10,2])
162
- self[:ip_src].read(str[12,4])
163
- self[:ip_dst].read(str[16,4])
164
- self[:body].read(str[20,str.size]) if str.size > 20
165
- self
166
- end
167
-
168
- # Setter for the version.
169
- def ip_v=(i); self[:ip_v] = i.to_i; end
170
- # Getter for the version.
171
- def ip_v; self[:ip_v].to_i; end
172
- # Setter for the header length (divide by 4)
173
- def ip_hl=(i); self[:ip_hl] = i.to_i; end
174
- # Getter for the header length (multiply by 4)
175
- def ip_hl; self[:ip_hl].to_i; end
176
- # Setter for the differentiated services
177
- def ip_tos=(i); typecast i; end
178
- # Getter for the differentiated services
179
- def ip_tos; self[:ip_tos].to_i; end
180
- # Setter for total length.
181
- def ip_len=(i); typecast i; end
182
- # Getter for total length.
183
- def ip_len; self[:ip_len].to_i; end
184
- # Setter for the identication number.
185
- def ip_id=(i); typecast i; end
186
- # Getter for the identication number.
187
- def ip_id; self[:ip_id].to_i; end
188
- # Setter for the fragmentation ID.
189
- def ip_frag=(i); typecast i; end
190
- # Getter for the fragmentation ID.
191
- def ip_frag; self[:ip_frag].to_i; end
192
- # Setter for the time to live.
193
- def ip_ttl=(i); typecast i; end
194
- # Getter for the time to live.
195
- def ip_ttl; self[:ip_ttl].to_i; end
196
- # Setter for the protocol number.
197
- def ip_proto=(i); typecast i; end
198
- # Getter for the protocol number.
199
- def ip_proto; self[:ip_proto].to_i; end
200
- # Setter for the checksum.
201
- def ip_sum=(i); typecast i; end
202
- # Getter for the checksum.
203
- def ip_sum; self[:ip_sum].to_i; end
204
- # Setter for the source IP address.
205
- def ip_src=(i)
206
- case i
207
- when Numeric
208
- self[:ip_src] = Octets.new.read([i].pack("N"))
209
- when Octets
210
- self[:ip_src] = i
211
- else
212
- typecast i
213
- end
214
- end
215
- # Getter for the source IP address.
216
- def ip_src; self[:ip_src].to_i; end
217
- # Setter for the destination IP address.
218
- def ip_dst=(i)
219
- case i
220
- when Numeric
221
- self[:ip_dst] = Octets.new.read([i].pack("N"))
222
- when Octets
223
- self[:ip_dst] = i
224
- else
225
- typecast i
226
- end
227
- end
228
- # Getter for the destination IP address.
229
- def ip_dst; self[:ip_dst].to_i; end
230
-
231
- # Calulcate the true length of the packet.
232
- def ip_calc_len
233
- (ip_hl * 4) + body.to_s.length
234
- end
235
-
236
- # Return the claimed header length
237
- def ip_hlen
238
- (ip_hl * 4)
239
- end
240
-
241
- # Calculate the true checksum of the packet.
242
- # (Yes, this is the long way to do it, but it's e-z-2-read for mathtards like me.)
243
- def ip_calc_sum
244
- checksum = (((self.ip_v << 4) + self.ip_hl) << 8) + self.ip_tos
245
- checksum += self.ip_len
246
- checksum += self.ip_id
247
- checksum += self.ip_frag
248
- checksum += (self.ip_ttl << 8) + self.ip_proto
249
- checksum += (self.ip_src >> 16)
250
- checksum += (self.ip_src & 0xffff)
251
- checksum += (self.ip_dst >> 16)
252
- checksum += (self.ip_dst & 0xffff)
253
- checksum = checksum % 0xffff
254
- checksum = 0xffff - checksum
255
- checksum == 0 ? 0xffff : checksum
256
- end
257
-
258
- # Retrieve the IP ID
259
- def ip_calc_id
260
- @random_id
261
- end
262
-
263
- # Sets a more readable IP address. If you wants to manipulate individual octets,
264
- # (eg, for host scanning in one network), it would be better use ip_src.o1 through
265
- # ip_src.o4 instead.
266
- def ip_saddr=(addr)
267
- self[:ip_src].read_quad(addr)
268
- end
269
-
270
- # Returns a more readable IP source address.
271
- def ip_saddr
272
- self[:ip_src].to_x
273
- end
274
-
275
- # Sets a more readable IP address.
276
- def ip_daddr=(addr)
277
- self[:ip_dst].read_quad(addr)
278
- end
279
-
280
- # Returns a more readable IP destination address.
281
- def ip_daddr
282
- self[:ip_dst].to_x
283
- end
284
-
285
- # Translate various formats of IPv4 Addresses to an array of digits.
286
- def self.octet_array(addr)
287
- if addr.class == String
288
- oa = addr.split('.').collect {|x| x.to_i}
289
- elsif addr.class == Fixnum
290
- oa = IPAddr.new(addr, Socket::AF_INET).to_s.split('.')
291
- elsif addr.class == Bignum
292
- oa = IPAddr.new(addr, Socket::AF_INET).to_s.split('.')
293
- elsif addr.class == Array
294
- oa = addr
295
- else
296
- raise ArgumentError, "IP Address should be a dotted quad string, an array of ints, or a bignum"
297
- end
298
- end
299
-
300
- # Recalculate the calculated IP fields. Valid arguments are:
301
- # :all
302
- # :ip_len
303
- # :ip_sum
304
- # :ip_id
305
- def ip_recalc(arg=:all)
306
- case arg
307
- when :ip_len
308
- self.ip_len=ip_calc_len
309
- when :ip_sum
310
- self.ip_sum=ip_calc_sum
311
- when :ip_id
312
- @random_id = rand(0xffff)
313
- when :all
314
- self.ip_id= ip_calc_id
315
- self.ip_len= ip_calc_len
316
- self.ip_sum= ip_calc_sum
317
- else
318
- raise ArgumentError, "No such field `#{arg}'"
319
- end
320
- end
321
-
322
- # Readability aliases
323
-
324
- alias :ip_src_readable :ip_saddr
325
- alias :ip_dst_readable :ip_daddr
326
-
327
- def ip_id_readable
328
- "0x%04x" % ip_id
329
- end
330
-
331
- def ip_sum_readable
332
- "0x%04x" % ip_sum
333
- end
334
-
335
- end
96
+ end
97
+
98
+ # IPHeader is a complete IP struct, used in IPPacket. Most traffic on most networks today is IP-based.
99
+ #
100
+ # For more on IP packets, see http://www.networksorcery.com/enp/protocol/ip.htm
101
+ #
102
+ # ==== Header Definition
103
+ #
104
+ # Fixnum (4 bits) :ip_v, Default: 4
105
+ # Fixnum (4 bits) :ip_hl, Default: 5
106
+ # Int8 :ip_tos, Default: 0 # TODO: Break out the bits
107
+ # Int16 :ip_len, Default: calculated
108
+ # Int16 :ip_id, Default: calculated # IRL, hardly random.
109
+ # Int16 :ip_frag, Default: 0 # TODO: Break out the bits
110
+ # Int8 :ip_ttl, Default: 0xff # Changes per flavor
111
+ # Int8 :ip_proto, Default: 0x01 # TCP: 0x06, UDP 0x11, ICMP 0x01
112
+ # Int16 :ip_sum, Default: calculated
113
+ # Octets :ip_src
114
+ # Octets :ip_dst
115
+ # String :body
116
+ #
117
+ # Note that IPPackets will always be somewhat incorrect upon initalization,
118
+ # and want an IPHeader#recalc() to become correct before a
119
+ # Packet#to_f or Packet#to_w.
120
+ class IPHeader < Struct.new(:ip_v, :ip_hl, :ip_tos, :ip_len,
121
+ :ip_id, :ip_frag, :ip_ttl, :ip_proto,
122
+ :ip_sum, :ip_src, :ip_dst, :body)
123
+ include StructFu
124
+
125
+ def initialize(args={})
126
+ @random_id = rand(0xffff)
127
+ super(
128
+ (args[:ip_v] || 4),
129
+ (args[:ip_hl] || 5),
130
+ Int8.new(args[:ip_tos]),
131
+ Int16.new(args[:ip_len] || 20),
132
+ Int16.new(args[:ip_id] || ip_calc_id),
133
+ Int16.new(args[:ip_frag]),
134
+ Int8.new(args[:ip_ttl] || 32),
135
+ Int8.new(args[:ip_proto]),
136
+ Int16.new(args[:ip_sum] || ip_calc_sum),
137
+ Octets.new.read(args[:ip_src] || "\x00\x00\x00\x00"),
138
+ Octets.new.read(args[:ip_dst] || "\x00\x00\x00\x00"),
139
+ StructFu::String.new.read(args[:body])
140
+ )
141
+ end
142
+
143
+ # Returns the object in string form.
144
+ def to_s
145
+ byte_v_hl = [(self.ip_v << 4) + self.ip_hl].pack("C")
146
+ byte_v_hl + (self.to_a[2,10].map {|x| x.to_s}.join)
147
+ end
148
+
149
+ # Reads a string to populate the object.
150
+ def read(str)
151
+ force_binary(str)
152
+ return self if str.nil?
153
+ self[:ip_v] = str[0,1].unpack("C").first >> 4
154
+ self[:ip_hl] = str[0,1].unpack("C").first.to_i & 0x0f
155
+ self[:ip_tos].read(str[1,1])
156
+ self[:ip_len].read(str[2,2])
157
+ self[:ip_id].read(str[4,2])
158
+ self[:ip_frag].read(str[6,2])
159
+ self[:ip_ttl].read(str[8,1])
160
+ self[:ip_proto].read(str[9,1])
161
+ self[:ip_sum].read(str[10,2])
162
+ self[:ip_src].read(str[12,4])
163
+ self[:ip_dst].read(str[16,4])
164
+ self[:body].read(str[20,str.size]) if str.size > 20
165
+ self
166
+ end
167
+
168
+ # Setter for the version.
169
+ def ip_v=(i); self[:ip_v] = i.to_i; end
170
+ # Getter for the version.
171
+ def ip_v; self[:ip_v].to_i; end
172
+ # Setter for the header length (divide by 4)
173
+ def ip_hl=(i); self[:ip_hl] = i.to_i; end
174
+ # Getter for the header length (multiply by 4)
175
+ def ip_hl; self[:ip_hl].to_i; end
176
+ # Setter for the differentiated services
177
+ def ip_tos=(i); typecast i; end
178
+ # Getter for the differentiated services
179
+ def ip_tos; self[:ip_tos].to_i; end
180
+ # Setter for total length.
181
+ def ip_len=(i); typecast i; end
182
+ # Getter for total length.
183
+ def ip_len; self[:ip_len].to_i; end
184
+ # Setter for the identication number.
185
+ def ip_id=(i); typecast i; end
186
+ # Getter for the identication number.
187
+ def ip_id; self[:ip_id].to_i; end
188
+ # Setter for the fragmentation ID.
189
+ def ip_frag=(i); typecast i; end
190
+ # Getter for the fragmentation ID.
191
+ def ip_frag; self[:ip_frag].to_i; end
192
+ # Setter for the time to live.
193
+ def ip_ttl=(i); typecast i; end
194
+ # Getter for the time to live.
195
+ def ip_ttl; self[:ip_ttl].to_i; end
196
+ # Setter for the protocol number.
197
+ def ip_proto=(i); typecast i; end
198
+ # Getter for the protocol number.
199
+ def ip_proto; self[:ip_proto].to_i; end
200
+ # Setter for the checksum.
201
+ def ip_sum=(i); typecast i; end
202
+ # Getter for the checksum.
203
+ def ip_sum; self[:ip_sum].to_i; end
204
+ # Setter for the source IP address.
205
+ def ip_src=(i)
206
+ case i
207
+ when Numeric
208
+ self[:ip_src] = Octets.new.read([i].pack("N"))
209
+ when Octets
210
+ self[:ip_src] = i
211
+ else
212
+ typecast i
213
+ end
214
+ end
215
+ # Getter for the source IP address.
216
+ def ip_src; self[:ip_src].to_i; end
217
+ # Setter for the destination IP address.
218
+ def ip_dst=(i)
219
+ case i
220
+ when Numeric
221
+ self[:ip_dst] = Octets.new.read([i].pack("N"))
222
+ when Octets
223
+ self[:ip_dst] = i
224
+ else
225
+ typecast i
226
+ end
227
+ end
228
+ # Getter for the destination IP address.
229
+ def ip_dst; self[:ip_dst].to_i; end
230
+
231
+ # Calulcate the true length of the packet.
232
+ def ip_calc_len
233
+ (ip_hl * 4) + body.to_s.length
234
+ end
235
+
236
+ # Return the claimed header length
237
+ def ip_hlen
238
+ (ip_hl * 4)
239
+ end
240
+
241
+ # Calculate the true checksum of the packet.
242
+ # (Yes, this is the long way to do it, but it's e-z-2-read for mathtards like me.)
243
+ def ip_calc_sum
244
+ checksum = (((self.ip_v << 4) + self.ip_hl) << 8) + self.ip_tos
245
+ checksum += self.ip_len
246
+ checksum += self.ip_id
247
+ checksum += self.ip_frag
248
+ checksum += (self.ip_ttl << 8) + self.ip_proto
249
+ checksum += (self.ip_src >> 16)
250
+ checksum += (self.ip_src & 0xffff)
251
+ checksum += (self.ip_dst >> 16)
252
+ checksum += (self.ip_dst & 0xffff)
253
+ checksum = checksum % 0xffff
254
+ checksum = 0xffff - checksum
255
+ checksum == 0 ? 0xffff : checksum
256
+ end
257
+
258
+ # Retrieve the IP ID
259
+ def ip_calc_id
260
+ @random_id
261
+ end
262
+
263
+ # Sets a more readable IP address. If you wants to manipulate individual octets,
264
+ # (eg, for host scanning in one network), it would be better use ip_src.o1 through
265
+ # ip_src.o4 instead.
266
+ def ip_saddr=(addr)
267
+ self[:ip_src].read_quad(addr)
268
+ end
269
+
270
+ # Returns a more readable IP source address.
271
+ def ip_saddr
272
+ self[:ip_src].to_x
273
+ end
274
+
275
+ # Sets a more readable IP address.
276
+ def ip_daddr=(addr)
277
+ self[:ip_dst].read_quad(addr)
278
+ end
279
+
280
+ # Returns a more readable IP destination address.
281
+ def ip_daddr
282
+ self[:ip_dst].to_x
283
+ end
284
+
285
+ # Translate various formats of IPv4 Addresses to an array of digits.
286
+ def self.octet_array(addr)
287
+ if addr.class == String
288
+ oa = addr.split('.').collect {|x| x.to_i}
289
+ elsif addr.class == Fixnum
290
+ oa = IPAddr.new(addr, Socket::AF_INET).to_s.split('.')
291
+ elsif addr.class == Bignum
292
+ oa = IPAddr.new(addr, Socket::AF_INET).to_s.split('.')
293
+ elsif addr.class == Array
294
+ oa = addr
295
+ else
296
+ raise ArgumentError, "IP Address should be a dotted quad string, an array of ints, or a bignum"
297
+ end
298
+ end
299
+
300
+ # Recalculate the calculated IP fields. Valid arguments are:
301
+ # :all
302
+ # :ip_len
303
+ # :ip_sum
304
+ # :ip_id
305
+ def ip_recalc(arg=:all)
306
+ case arg
307
+ when :ip_len
308
+ self.ip_len=ip_calc_len
309
+ when :ip_sum
310
+ self.ip_sum=ip_calc_sum
311
+ when :ip_id
312
+ @random_id = rand(0xffff)
313
+ when :all
314
+ self.ip_id= ip_calc_id
315
+ self.ip_len= ip_calc_len
316
+ self.ip_sum= ip_calc_sum
317
+ else
318
+ raise ArgumentError, "No such field `#{arg}'"
319
+ end
320
+ end
321
+
322
+ # Readability aliases
323
+
324
+ alias :ip_src_readable :ip_saddr
325
+ alias :ip_dst_readable :ip_daddr
326
+
327
+ def ip_id_readable
328
+ "0x%04x" % ip_id
329
+ end
330
+
331
+ def ip_sum_readable
332
+ "0x%04x" % ip_sum
333
+ end
334
+
335
+ end
336
336
  end