packetfu 1.1.8 → 1.1.9

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