packetfu 1.1.8 → 1.1.9

Sign up to get free protection for your applications and to get access to all the features.
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