packetfu 1.1.10 → 1.1.11

Sign up to get free protection for your applications and to get access to all the features.
Files changed (98) hide show
  1. checksums.yaml +7 -0
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +2 -0
  4. data/.gitignore +3 -0
  5. data/.travis.yml +8 -0
  6. data/CONTRIBUTING.md +47 -0
  7. data/Gemfile +4 -0
  8. data/LICENSE.txt +1 -1
  9. data/README.rdoc +35 -30
  10. data/Rakefile +4 -4
  11. data/bench/octets.rb +9 -9
  12. data/examples/100kpackets.rb +12 -12
  13. data/examples/ackscan.rb +16 -16
  14. data/examples/arp.rb +35 -35
  15. data/examples/arphood.rb +36 -36
  16. data/examples/dissect_thinger.rb +6 -6
  17. data/examples/new-simple-stats.rb +23 -23
  18. data/examples/packetfu-shell.rb +25 -25
  19. data/examples/simple-sniffer.rb +9 -9
  20. data/examples/simple-stats.rb +23 -23
  21. data/examples/slammer.rb +3 -3
  22. data/gem-public_cert.pem +21 -0
  23. data/lib/packetfu.rb +149 -127
  24. data/lib/packetfu/capture.rb +169 -169
  25. data/lib/packetfu/config.rb +52 -52
  26. data/lib/packetfu/inject.rb +56 -56
  27. data/lib/packetfu/packet.rb +531 -528
  28. data/lib/packetfu/pcap.rb +579 -579
  29. data/lib/packetfu/protos/arp.rb +90 -90
  30. data/lib/packetfu/protos/arp/header.rb +158 -158
  31. data/lib/packetfu/protos/arp/mixin.rb +36 -36
  32. data/lib/packetfu/protos/eth.rb +44 -44
  33. data/lib/packetfu/protos/eth/header.rb +243 -243
  34. data/lib/packetfu/protos/eth/mixin.rb +3 -3
  35. data/lib/packetfu/protos/hsrp.rb +69 -69
  36. data/lib/packetfu/protos/hsrp/header.rb +107 -107
  37. data/lib/packetfu/protos/hsrp/mixin.rb +29 -29
  38. data/lib/packetfu/protos/icmp.rb +71 -71
  39. data/lib/packetfu/protos/icmp/header.rb +82 -82
  40. data/lib/packetfu/protos/icmp/mixin.rb +14 -14
  41. data/lib/packetfu/protos/invalid.rb +49 -49
  42. data/lib/packetfu/protos/ip.rb +69 -69
  43. data/lib/packetfu/protos/ip/header.rb +291 -291
  44. data/lib/packetfu/protos/ip/mixin.rb +40 -40
  45. data/lib/packetfu/protos/ipv6.rb +50 -50
  46. data/lib/packetfu/protos/ipv6/header.rb +188 -188
  47. data/lib/packetfu/protos/ipv6/mixin.rb +29 -29
  48. data/lib/packetfu/protos/tcp.rb +176 -176
  49. data/lib/packetfu/protos/tcp/ecn.rb +35 -35
  50. data/lib/packetfu/protos/tcp/flags.rb +74 -74
  51. data/lib/packetfu/protos/tcp/header.rb +268 -268
  52. data/lib/packetfu/protos/tcp/hlen.rb +32 -32
  53. data/lib/packetfu/protos/tcp/mixin.rb +46 -46
  54. data/lib/packetfu/protos/tcp/option.rb +321 -321
  55. data/lib/packetfu/protos/tcp/options.rb +95 -95
  56. data/lib/packetfu/protos/tcp/reserved.rb +35 -35
  57. data/lib/packetfu/protos/udp.rb +159 -123
  58. data/lib/packetfu/protos/udp/header.rb +91 -91
  59. data/lib/packetfu/protos/udp/mixin.rb +3 -3
  60. data/lib/packetfu/structfu.rb +280 -280
  61. data/lib/packetfu/utils.rb +292 -225
  62. data/lib/packetfu/version.rb +41 -41
  63. data/packetfu.gemspec +14 -3
  64. data/spec/arp_spec.rb +191 -0
  65. data/spec/eth_spec.rb +148 -0
  66. data/spec/icmp_spec.rb +97 -0
  67. data/spec/ip_spec.rb +78 -0
  68. data/spec/ipv6_spec.rb +81 -0
  69. data/spec/packet_spec.rb +61 -59
  70. data/spec/packet_subclasses_spec.rb +9 -10
  71. data/spec/packetfu_spec.rb +55 -62
  72. data/spec/sample3.pcap +0 -0
  73. data/spec/spec_helper.rb +44 -0
  74. data/spec/structfu_spec.rb +270 -271
  75. data/spec/tcp_spec.rb +76 -77
  76. data/spec/udp_spec.rb +32 -0
  77. data/spec/utils_spec.rb +95 -0
  78. data/test/all_tests.rb +14 -17
  79. data/test/func_lldp.rb +3 -3
  80. data/test/ptest.rb +2 -2
  81. data/test/test_capture.rb +45 -45
  82. data/test/test_eth.rb +70 -68
  83. data/test/test_hsrp.rb +9 -9
  84. data/test/test_inject.rb +18 -18
  85. data/test/test_invalid.rb +16 -16
  86. data/test/test_octets.rb +23 -21
  87. data/test/test_packet.rb +156 -154
  88. data/test/test_pcap.rb +172 -170
  89. data/test/test_structfu.rb +99 -97
  90. data/test/test_tcp.rb +322 -320
  91. data/test/test_udp.rb +78 -76
  92. metadata +108 -44
  93. metadata.gz.sig +2 -0
  94. data/spec/ethpacket_spec.rb +0 -74
  95. data/test/test_arp.rb +0 -135
  96. data/test/test_icmp.rb +0 -62
  97. data/test/test_ip.rb +0 -50
  98. data/test/test_ip6.rb +0 -68
@@ -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