packetfu 1.1.1 → 1.1.2

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 (71) hide show
  1. data/{README → README.rdoc} +2 -2
  2. metadata +58 -94
  3. data/INSTALL +0 -40
  4. data/LICENSE +0 -28
  5. data/examples/100kpackets.rb +0 -41
  6. data/examples/ackscan.rb +0 -38
  7. data/examples/arp.rb +0 -60
  8. data/examples/arphood.rb +0 -59
  9. data/examples/dissect_thinger.rb +0 -22
  10. data/examples/ethernet.rb +0 -10
  11. data/examples/examples.rb +0 -3
  12. data/examples/ids.rb +0 -4
  13. data/examples/idsv2.rb +0 -6
  14. data/examples/new-simple-stats.rb +0 -52
  15. data/examples/oui.txt +0 -84177
  16. data/examples/packetfu-shell.rb +0 -113
  17. data/examples/simple-sniffer.rb +0 -40
  18. data/examples/simple-stats.rb +0 -50
  19. data/examples/slammer.rb +0 -33
  20. data/examples/uniqpcap.rb +0 -15
  21. data/lib/packetfu.rb +0 -147
  22. data/lib/packetfu/capture.rb +0 -169
  23. data/lib/packetfu/config.rb +0 -58
  24. data/lib/packetfu/inject.rb +0 -65
  25. data/lib/packetfu/packet.rb +0 -533
  26. data/lib/packetfu/pcap.rb +0 -594
  27. data/lib/packetfu/protos/arp.rb +0 -268
  28. data/lib/packetfu/protos/eth.rb +0 -296
  29. data/lib/packetfu/protos/hsrp.rb +0 -206
  30. data/lib/packetfu/protos/icmp.rb +0 -179
  31. data/lib/packetfu/protos/invalid.rb +0 -55
  32. data/lib/packetfu/protos/ip.rb +0 -378
  33. data/lib/packetfu/protos/ipv6.rb +0 -250
  34. data/lib/packetfu/protos/tcp.rb +0 -1127
  35. data/lib/packetfu/protos/udp.rb +0 -240
  36. data/lib/packetfu/structfu.rb +0 -294
  37. data/lib/packetfu/utils.rb +0 -194
  38. data/lib/packetfu/version.rb +0 -50
  39. data/test/all_tests.rb +0 -41
  40. data/test/arp_test.pcap +0 -0
  41. data/test/eth_test.pcap +0 -0
  42. data/test/ethpacket_spec.rb +0 -74
  43. data/test/icmp_test.pcap +0 -0
  44. data/test/ip_test.pcap +0 -0
  45. data/test/packet_spec.rb +0 -73
  46. data/test/packet_subclasses_spec.rb +0 -13
  47. data/test/packetfu_spec.rb +0 -90
  48. data/test/ptest.rb +0 -16
  49. data/test/sample-ipv6.pcap +0 -0
  50. data/test/sample.pcap +0 -0
  51. data/test/sample2.pcap +0 -0
  52. data/test/sample_hsrp_pcapr.cap +0 -0
  53. data/test/structfu_spec.rb +0 -335
  54. data/test/tcp_spec.rb +0 -101
  55. data/test/tcp_test.pcap +0 -0
  56. data/test/test_arp.rb +0 -135
  57. data/test/test_eth.rb +0 -91
  58. data/test/test_hsrp.rb +0 -20
  59. data/test/test_icmp.rb +0 -54
  60. data/test/test_inject.rb +0 -31
  61. data/test/test_invalid.rb +0 -28
  62. data/test/test_ip.rb +0 -69
  63. data/test/test_ip6.rb +0 -68
  64. data/test/test_octets.rb +0 -37
  65. data/test/test_packet.rb +0 -174
  66. data/test/test_pcap.rb +0 -209
  67. data/test/test_structfu.rb +0 -112
  68. data/test/test_tcp.rb +0 -327
  69. data/test/test_udp.rb +0 -73
  70. data/test/udp_test.pcap +0 -0
  71. data/test/vlan-pcapr.cap +0 -0
@@ -1,206 +0,0 @@
1
- module PacketFu
2
-
3
- # HSRPHeader is a complete HSRP struct, used in HSRPPacket. HSRP is typically used for
4
- # fault-tolerant default gateway in IP routing environment.
5
- #
6
- # For more on HSRP packets, see http://www.networksorcery.com/enp/protocol/hsrp.htm
7
- #
8
- # Submitted by fropert@packetfault.org. Thanks, Francois!
9
- #
10
- # ==== Header Definition
11
- #
12
- # Int8 :hsrp_version Default: 0 # Version
13
- # Int8 :hsrp_opcode # Opcode
14
- # Int8 :hsrp_state # State
15
- # Int8 :hsrp_hellotime Default: 3 # Hello Time
16
- # Int8 :hsrp_holdtime Default: 10 # Hold Time
17
- # Int8 :hsrp_priority # Priority
18
- # Int8 :hsrp_group # Group
19
- # Int8 :hsrp_reserved Default: 0 # Reserved
20
- # String :hsrp_password # Authentication Data
21
- # Octets :hsrp_vip # Virtual IP Address
22
- # String :body
23
- class HSRPHeader < Struct.new(:hsrp_version, :hsrp_opcode, :hsrp_state,
24
- :hsrp_hellotime, :hsrp_holdtime,
25
- :hsrp_priority, :hsrp_group,
26
- :hsrp_reserved, :hsrp_password,
27
- :hsrp_vip, :body)
28
-
29
- include StructFu
30
-
31
- def initialize(args={})
32
- super(
33
- Int8.new(args[:hsrp_version] || 0),
34
- Int8.new(args[:hsrp_opcode]),
35
- Int8.new(args[:hsrp_state]),
36
- Int8.new(args[:hsrp_hellotime] || 3),
37
- Int8.new(args[:hsrp_holdtime] || 10),
38
- Int8.new(args[:hsrp_priority]),
39
- Int8.new(args[:hsrp_group]),
40
- Int8.new(args[:hsrp_reserved] || 0),
41
- StructFu::String.new.read(args[:hsrp_password] || "cisco\x00\x00\x00"),
42
- Octets.new.read(args[:hsrp_vip] || ("\x00" * 4)),
43
- StructFu::String.new.read(args[:body])
44
- )
45
- end
46
-
47
- # Returns the object in string form.
48
- def to_s
49
- self.to_a.map {|x| x.to_s}.join
50
- end
51
-
52
- # Reads a string to populate the object.
53
- def read(str)
54
- force_binary(str)
55
- return self if str.nil?
56
- self[:hsrp_version].read(str[0,1])
57
- self[:hsrp_opcode].read(str[1,1])
58
- self[:hsrp_state].read(str[2,1])
59
- self[:hsrp_hellotime].read(str[3,1])
60
- self[:hsrp_holdtime].read(str[4,1])
61
- self[:hsrp_priority].read(str[5,1])
62
- self[:hsrp_group].read(str[6,1])
63
- self[:hsrp_reserved].read(str[7,1])
64
- self[:hsrp_password].read(str[8,8])
65
- self[:hsrp_vip].read(str[16,4])
66
- self[:body].read(str[20,str.size]) if str.size > 20
67
- self
68
- end
69
-
70
- # Setter for the type.
71
- def hsrp_version=(i); typecast i; end
72
- # Getter for the type.
73
- def hsrp_version; self[:hsrp_version].to_i; end
74
- # Setter for the type.
75
- def hsrp_opcode=(i); typecast i; end
76
- # Getter for the type.
77
- def hsrp_opcode; self[:hsrp_opcode].to_i; end
78
- # Setter for the type.
79
- def hsrp_state=(i); typecast i; end
80
- # Getter for the type.
81
- def hsrp_state; self[:hsrp_state].to_i; end
82
- # Setter for the type.
83
- def hsrp_hellotime=(i); typecast i; end
84
- # Getter for the type.
85
- def hsrp_hellotime; self[:hsrp_hellotime].to_i; end
86
- # Setter for the type.
87
- def hsrp_holdtime=(i); typecast i; end
88
- # Getter for the type.
89
- def hsrp_holdtime; self[:hsrp_holdtime].to_i; end
90
- # Setter for the type.
91
- def hsrp_priority=(i); typecast i; end
92
- # Getter for the type.
93
- def hsrp_priority; self[:hsrp_priority].to_i; end
94
- # Setter for the type.
95
- def hsrp_group=(i); typecast i; end
96
- # Getter for the type.
97
- def hsrp_group; self[:hsrp_group].to_i; end
98
- # Setter for the type.
99
- def hsrp_reserved=(i); typecast i; end
100
- # Getter for the type.
101
- def hsrp_reserved; self[:hsrp_reserved].to_i; end
102
-
103
- def hsrp_addr=(addr)
104
- self[:hsrp_vip].read_quad(addr)
105
- end
106
-
107
- # Returns a more readable IP source address.
108
- def hsrp_addr
109
- self[:hsrp_vip].to_x
110
- end
111
-
112
- # Readability aliases
113
-
114
- alias :hsrp_vip_readable :hsrp_addr
115
-
116
- def hsrp_password_readable
117
- hsrp_password.to_s.inspect
118
- end
119
-
120
- end
121
-
122
- # HSRPPacket is used to construct HSRP Packets. They contain an EthHeader, an IPHeader, and a UDPHeader.
123
- #
124
- # == Example
125
- #
126
- # hsrp_pkt.new
127
- # hsrp_pkt.hsrp_opcode = 0
128
- # hsrp_pkt.hsrp_state = 16
129
- # hsrp_pkt.hsrp_priority = 254
130
- # hsrp_pkt.hsrp_group = 1
131
- # hsrp_pkt.hsrp_vip = 10.100.100.254
132
- # hsrp_pkt.recalc
133
- # hsrp_pkt.to_f('/tmp/hsrp.pcap')
134
- #
135
- # == Parameters
136
- #
137
- # :eth
138
- # A pre-generated EthHeader object.
139
- # :ip
140
- # A pre-generated IPHeader object.
141
- # :udp
142
- # A pre-generated UDPHeader object.
143
- # :flavor
144
- # TODO: HSRP packets don't tend have any flavor.
145
- # :config
146
- # A hash of return address details, often the output of Utils.whoami?
147
- class HSRPPacket < Packet
148
-
149
- attr_accessor :eth_header, :ip_header, :udp_header, :hsrp_header
150
-
151
- def self.can_parse?(str)
152
- return false unless str.size >= 54
153
- return false unless EthPacket.can_parse? str
154
- return false unless IPPacket.can_parse? str
155
- return false unless UDPPacket.can_parse? str
156
- temp_packet = UDPPacket.new
157
- temp_packet.read(str)
158
- if temp_packet.ip_ttl == 1 and [temp_packet.udp_sport,temp_packet.udp_dport] == [1985,1985]
159
- return true
160
- else
161
- return false
162
- end
163
- end
164
-
165
- def read(str=nil, args={})
166
- raise "Cannot parse `#{str}'" unless self.class.can_parse?(str)
167
- @eth_header.read(str)
168
- @ip_header.read(str[14,str.size])
169
- @eth_header.body = @ip_header
170
- @udp_header.read(str[14+(@ip_header.ip_hlen),str.size])
171
- @ip_header.body = @udp_header
172
- @hsrp_header.read(str[14+(@ip_header.ip_hlen)+8,str.size])
173
- @udp_header.body = @hsrp_header
174
- super(args)
175
- self
176
- end
177
-
178
- def initialize(args={})
179
- @eth_header = EthHeader.new(args).read(args[:eth])
180
- @ip_header = IPHeader.new(args).read(args[:ip])
181
- @ip_header.ip_proto = 0x11
182
- @udp_header = UDPHeader.new(args).read(args[:udp])
183
- @hsrp_header = HSRPHeader.new(args).read(args[:hsrp])
184
- @udp_header.body = @hsrp_header
185
- @ip_header.body = @udp_header
186
- @eth_header.body = @ip_header
187
- @headers = [@eth_header, @ip_header, @udp_header, @hsrp_header]
188
- super
189
- end
190
-
191
- # Peek provides summary data on packet contents.
192
- def peek_format
193
- peek_data = ["UH "]
194
- peek_data << "%-5d" % self.to_s.size
195
- peek_data << "%-16s" % self.hsrp_addr
196
- peek_data << "%-4d" % self.hsrp_group
197
- peek_data << "%-35s" % self.hsrp_password_readable
198
- peek_data << "%-15s" % self.ip_saddr
199
- peek_data.join
200
- end
201
-
202
- end
203
-
204
- end
205
-
206
- # vim: nowrap sw=2 sts=0 ts=2 ff=unix ft=ruby
@@ -1,179 +0,0 @@
1
- module PacketFu
2
-
3
- # ICMPHeader is a complete ICMP struct, used in ICMPPacket. ICMP is
4
- # typically used for network administration and connectivity testing.
5
- #
6
- # For more on ICMP packets, see
7
- # http://www.networksorcery.com/enp/protocol/icmp.htm
8
- #
9
- # ==== Header Definition
10
- #
11
- # Int8 :icmp_type # Type
12
- # Int8 :icmp_code # Code
13
- # Int16 :icmp_sum Default: calculated # Checksum
14
- # String :body
15
- class ICMPHeader < Struct.new(:icmp_type, :icmp_code, :icmp_sum, :body)
16
-
17
- include StructFu
18
-
19
- def initialize(args={})
20
- super(
21
- Int8.new(args[:icmp_type]),
22
- Int8.new(args[:icmp_code]),
23
- Int16.new(args[:icmp_sum] || icmp_calc_sum),
24
- StructFu::String.new.read(args[:body])
25
- )
26
- end
27
-
28
- # Returns the object in string form.
29
- def to_s
30
- self.to_a.map {|x| x.to_s}.join
31
- end
32
-
33
- # Reads a string to populate the object.
34
- def read(str)
35
- force_binary(str)
36
- return self if str.nil?
37
- self[:icmp_type].read(str[0,1])
38
- self[:icmp_code].read(str[1,1])
39
- self[:icmp_sum].read(str[2,2])
40
- self[:body].read(str[4,str.size])
41
- self
42
- end
43
-
44
- # Setter for the type.
45
- def icmp_type=(i); typecast i; end
46
- # Getter for the type.
47
- def icmp_type; self[:icmp_type].to_i; end
48
- # Setter for the code.
49
- def icmp_code=(i); typecast i; end
50
- # Getter for the code.
51
- def icmp_code; self[:icmp_code].to_i; end
52
- # Setter for the checksum. Note, this is calculated automatically with
53
- # icmp_calc_sum.
54
- def icmp_sum=(i); typecast i; end
55
- # Getter for the checksum.
56
- def icmp_sum; self[:icmp_sum].to_i; end
57
-
58
- # Calculates and sets the checksum for the object.
59
- def icmp_calc_sum
60
- checksum = (icmp_type.to_i << 8) + icmp_code.to_i
61
- chk_body = (body.to_s.size % 2 == 0 ? body.to_s : body.to_s + "\x00")
62
- if 1.respond_to? :ord
63
- chk_body.scan(/../).map { |x| (x[0].ord << 8) + x[1].ord }.each { |y| checksum += y }
64
- else
65
- chk_body.scan(/../).map { |x| (x[0] << 8) + x[1] }.each { |y| checksum += y }
66
- end
67
- checksum = checksum % 0xffff
68
- checksum = 0xffff - checksum
69
- checksum == 0 ? 0xffff : checksum
70
- end
71
-
72
- # Recalculates the calculatable fields for ICMP.
73
- def icmp_recalc(arg=:all)
74
- # How silly is this, you can't intern a symbol in ruby 1.8.7pl72?
75
- # I'm this close to monkey patching Symbol so you can force it...
76
- arg = arg.intern if arg.respond_to? :intern
77
- case arg
78
- when :icmp_sum
79
- self.icmp_sum=icmp_calc_sum
80
- when :all
81
- self.icmp_sum=icmp_calc_sum
82
- else
83
- raise ArgumentError, "No such field `#{arg}'"
84
- end
85
- end
86
-
87
- # Readability aliases
88
-
89
- def icmp_sum_readable
90
- "0x%04x" % icmp_sum
91
- end
92
-
93
- end
94
-
95
- # ICMPPacket is used to construct ICMP Packets. They contain an EthHeader, an IPHeader, and a ICMPHeader.
96
- #
97
- # == Example
98
- #
99
- # icmp_pkt.new
100
- # icmp_pkt.icmp_type = 8
101
- # icmp_pkt.icmp_code = 0
102
- # icmp_pkt.payload = "ABC, easy as 123. As simple as do-re-mi. ABC, 123, baby, you and me!"
103
- #
104
- # icmp_pkt.ip_saddr="1.2.3.4"
105
- # icmp_pkt.ip_daddr="5.6.7.8"
106
- #
107
- # icmp_pkt.recalc
108
- # icmp_pkt.to_f('/tmp/icmp.pcap')
109
- #
110
- # == Parameters
111
- #
112
- # :eth
113
- # A pre-generated EthHeader object.
114
- # :ip
115
- # A pre-generated IPHeader object.
116
- # :flavor
117
- # TODO: Sets the "flavor" of the ICMP packet. Pings, in particular, often betray their true
118
- # OS.
119
- # :config
120
- # A hash of return address details, often the output of Utils.whoami?
121
- class ICMPPacket < Packet
122
-
123
- attr_accessor :eth_header, :ip_header, :icmp_header
124
-
125
- def self.can_parse?(str)
126
- return false unless str.size >= 54
127
- return false unless EthPacket.can_parse? str
128
- return false unless IPPacket.can_parse? str
129
- return false unless str[23,1] == "\x01"
130
- return true
131
- end
132
-
133
- def read(str=nil, args={})
134
- raise "Cannot parse `#{str}'" unless self.class.can_parse?(str)
135
- @eth_header.read(str)
136
- @ip_header.read(str[14,str.size])
137
- @eth_header.body = @ip_header
138
- @icmp_header.read(str[14+(@ip_header.ip_hlen),str.size])
139
- @ip_header.body = @icmp_header
140
- super(args)
141
- self
142
- end
143
-
144
- def initialize(args={})
145
- @eth_header = EthHeader.new(args).read(args[:eth])
146
- @ip_header = IPHeader.new(args).read(args[:ip])
147
- @ip_header.ip_proto = 1
148
- @icmp_header = ICMPHeader.new(args).read(args[:icmp])
149
-
150
- @ip_header.body = @icmp_header
151
- @eth_header.body = @ip_header
152
-
153
- @headers = [@eth_header, @ip_header, @icmp_header]
154
- super
155
- end
156
-
157
- # Peek provides summary data on packet contents.
158
- def peek_format
159
- peek_data = ["IC "] # I is taken by IP
160
- peek_data << "%-5d" % self.to_s.size
161
- type = case self.icmp_type.to_i
162
- when 8
163
- "ping"
164
- when 0
165
- "pong"
166
- else
167
- "%02x-%02x" % [self.icmp_type, self.icmp_code]
168
- end
169
- peek_data << "%-21s" % "#{self.ip_saddr}:#{type}"
170
- peek_data << "->"
171
- peek_data << "%21s" % "#{self.ip_daddr}"
172
- peek_data << "%23s" % "I:"
173
- peek_data << "%04x" % self.ip_id
174
- peek_data.join
175
- end
176
-
177
- end
178
-
179
- end
@@ -1,55 +0,0 @@
1
- module PacketFu
2
-
3
- # InvalidHeader catches all packets that we don't already have a Struct for,
4
- # or for whatever reason, violates some basic packet rules for other packet
5
- # types.
6
- class InvalidHeader < Struct.new(:body)
7
- include StructFu
8
-
9
- def initialize(args={})
10
- args[:body] ||= StructFu::String.new
11
- super(args[:body])
12
- end
13
-
14
- # Returns the object in string form.
15
- def to_s
16
- self.to_a.map {|x| x.to_s}.join
17
- end
18
-
19
- # Reads a string to populate the object.
20
- def read(str)
21
- force_binary(str)
22
- return self if str.nil?
23
- self[:body].read str
24
- self
25
- end
26
-
27
- end
28
-
29
- # You probably don't want to write invalid packets on purpose.
30
- class InvalidPacket < Packet
31
- attr_accessor :invalid_header
32
-
33
- # Any packet is potentially an invalid packet
34
- def self.can_parse?(str)
35
- true
36
- end
37
-
38
- def self.layer
39
- 0
40
- end
41
-
42
- def read(str=nil,args={})
43
- @invalid_header.read(str)
44
- self
45
- end
46
-
47
- def initialize(args={})
48
- @invalid_header = (args[:invalid] || InvalidHeader.new)
49
- @headers = [@invalid_header]
50
- end
51
- end
52
-
53
- end # module PacketFu
54
-
55
- # vim: nowrap sw=2 sts=0 ts=2 ff=unix ft=ruby
@@ -1,378 +0,0 @@
1
- require 'ipaddr'
2
- module PacketFu
3
-
4
- # Octets implements the addressing scheme for IP.
5
- #
6
- # ==== Header Definition
7
- #
8
- # Int8 :o1
9
- # Int8 :o2
10
- # Int8 :o3
11
- # Int8 :o4
12
- class Octets < Struct.new(:o1, :o2, :o3, :o4)
13
- include StructFu
14
-
15
- def initialize(args={})
16
- super(
17
- Int8.new(args[:o1]),
18
- Int8.new(args[:o2]),
19
- Int8.new(args[:o3]),
20
- Int8.new(args[:o4]))
21
- end
22
-
23
- # Returns the object in string form.
24
- def to_s
25
- self.to_a.map {|x| x.to_s}.join
26
- end
27
-
28
- # Reads a string to populate the object.
29
- def read(str)
30
- force_binary(str)
31
- return self if str.nil?
32
- self[:o1].read str[0,1]
33
- self[:o2].read str[1,1]
34
- self[:o3].read str[2,1]
35
- self[:o4].read str[3,1]
36
- self
37
- end
38
-
39
- # Returns an address in dotted-quad format.
40
- def to_x
41
- ip_str = [o1, o2, o3, o4].map {|x| x.to_i.to_s}.join('.')
42
- IPAddr.new(ip_str).to_s
43
- end
44
-
45
- # Returns an address in numerical format.
46
- def to_i
47
- ip_str = [o1, o2, o3, o4].map {|x| x.to_i.to_s}.join('.')
48
- IPAddr.new(ip_str).to_i
49
- end
50
-
51
- # Set the IP Address by reading a dotted-quad address.
52
- def read_quad(str)
53
- read([IPAddr.new(str).to_i].pack("N"))
54
- end
55
-
56
- end
57
-
58
- # IPHeader is a complete IP struct, used in IPPacket. Most traffic on most networks today is IP-based.
59
- #
60
- # For more on IP packets, see http://www.networksorcery.com/enp/protocol/ip.htm
61
- #
62
- # ==== Header Definition
63
- #
64
- # Fixnum (4 bits) :ip_v, Default: 4
65
- # Fixnum (4 bits) :ip_hl, Default: 5
66
- # Int8 :ip_tos, Default: 0 # TODO: Break out the bits
67
- # Int16 :ip_len, Default: calculated
68
- # Int16 :ip_id, Default: calculated # IRL, hardly random.
69
- # Int16 :ip_frag, Default: 0 # TODO: Break out the bits
70
- # Int8 :ip_ttl, Default: 0xff # Changes per flavor
71
- # Int8 :ip_proto, Default: 0x01 # TCP: 0x06, UDP 0x11, ICMP 0x01
72
- # Int16 :ip_sum, Default: calculated
73
- # Octets :ip_src
74
- # Octets :ip_dst
75
- # String :body
76
- #
77
- # Note that IPPackets will always be somewhat incorrect upon initalization,
78
- # and want an IPHeader#recalc() to become correct before a
79
- # Packet#to_f or Packet#to_w.
80
- class IPHeader < Struct.new(:ip_v, :ip_hl, :ip_tos, :ip_len,
81
- :ip_id, :ip_frag, :ip_ttl, :ip_proto,
82
- :ip_sum, :ip_src, :ip_dst, :body)
83
- include StructFu
84
-
85
- def initialize(args={})
86
- @random_id = rand(0xffff)
87
- super(
88
- (args[:ip_v] || 4),
89
- (args[:ip_hl] || 5),
90
- Int8.new(args[:ip_tos]),
91
- Int16.new(args[:ip_len] || 20),
92
- Int16.new(args[:ip_id] || ip_calc_id),
93
- Int16.new(args[:ip_frag]),
94
- Int8.new(args[:ip_ttl] || 32),
95
- Int8.new(args[:ip_proto]),
96
- Int16.new(args[:ip_sum] || ip_calc_sum),
97
- Octets.new.read(args[:ip_src] || "\x00\x00\x00\x00"),
98
- Octets.new.read(args[:ip_dst] || "\x00\x00\x00\x00"),
99
- StructFu::String.new.read(args[:body])
100
- )
101
- end
102
-
103
- # Returns the object in string form.
104
- def to_s
105
- byte_v_hl = [(self.ip_v << 4) + self.ip_hl].pack("C")
106
- byte_v_hl + (self.to_a[2,10].map {|x| x.to_s}.join)
107
- end
108
-
109
- # Reads a string to populate the object.
110
- def read(str)
111
- force_binary(str)
112
- return self if str.nil?
113
- self[:ip_v] = str[0,1].unpack("C").first >> 4
114
- self[:ip_hl] = str[0,1].unpack("C").first.to_i & 0x0f
115
- self[:ip_tos].read(str[1,1])
116
- self[:ip_len].read(str[2,2])
117
- self[:ip_id].read(str[4,2])
118
- self[:ip_frag].read(str[6,2])
119
- self[:ip_ttl].read(str[8,1])
120
- self[:ip_proto].read(str[9,1])
121
- self[:ip_sum].read(str[10,2])
122
- self[:ip_src].read(str[12,4])
123
- self[:ip_dst].read(str[16,4])
124
- self[:body].read(str[20,str.size]) if str.size > 20
125
- self
126
- end
127
-
128
- # Setter for the version.
129
- def ip_v=(i); self[:ip_v] = i.to_i; end
130
- # Getter for the version.
131
- def ip_v; self[:ip_v].to_i; end
132
- # Setter for the header length (divide by 4)
133
- def ip_hl=(i); self[:ip_hl] = i.to_i; end
134
- # Getter for the header length (multiply by 4)
135
- def ip_hl; self[:ip_hl].to_i; end
136
- # Setter for the differentiated services
137
- def ip_tos=(i); typecast i; end
138
- # Getter for the differentiated services
139
- def ip_tos; self[:ip_tos].to_i; end
140
- # Setter for total length.
141
- def ip_len=(i); typecast i; end
142
- # Getter for total length.
143
- def ip_len; self[:ip_len].to_i; end
144
- # Setter for the identication number.
145
- def ip_id=(i); typecast i; end
146
- # Getter for the identication number.
147
- def ip_id; self[:ip_id].to_i; end
148
- # Setter for the fragmentation ID.
149
- def ip_frag=(i); typecast i; end
150
- # Getter for the fragmentation ID.
151
- def ip_frag; self[:ip_frag].to_i; end
152
- # Setter for the time to live.
153
- def ip_ttl=(i); typecast i; end
154
- # Getter for the time to live.
155
- def ip_ttl; self[:ip_ttl].to_i; end
156
- # Setter for the protocol number.
157
- def ip_proto=(i); typecast i; end
158
- # Getter for the protocol number.
159
- def ip_proto; self[:ip_proto].to_i; end
160
- # Setter for the checksum.
161
- def ip_sum=(i); typecast i; end
162
- # Getter for the checksum.
163
- def ip_sum; self[:ip_sum].to_i; end
164
- # Setter for the source IP address.
165
- def ip_src=(i)
166
- case i
167
- when Numeric
168
- self[:ip_src] = Octets.new.read([i].pack("N"))
169
- when Octets
170
- self[:ip_src] = i
171
- else
172
- typecast i
173
- end
174
- end
175
- # Getter for the source IP address.
176
- def ip_src; self[:ip_src].to_i; end
177
- # Setter for the destination IP address.
178
- def ip_dst=(i)
179
- case i
180
- when Numeric
181
- self[:ip_dst] = Octets.new.read([i].pack("N"))
182
- when Octets
183
- self[:ip_dst] = i
184
- else
185
- typecast i
186
- end
187
- end
188
- # Getter for the destination IP address.
189
- def ip_dst; self[:ip_dst].to_i; end
190
-
191
- # Calulcate the true length of the packet.
192
- def ip_calc_len
193
- (ip_hl * 4) + body.to_s.length
194
- end
195
-
196
- # Return the claimed header length
197
- def ip_hlen
198
- (ip_hl * 4)
199
- end
200
-
201
- # Calculate the true checksum of the packet.
202
- # (Yes, this is the long way to do it, but it's e-z-2-read for mathtards like me.)
203
- def ip_calc_sum
204
- checksum = (((self.ip_v << 4) + self.ip_hl) << 8) + self.ip_tos
205
- checksum += self.ip_len
206
- checksum += self.ip_id
207
- checksum += self.ip_frag
208
- checksum += (self.ip_ttl << 8) + self.ip_proto
209
- checksum += (self.ip_src >> 16)
210
- checksum += (self.ip_src & 0xffff)
211
- checksum += (self.ip_dst >> 16)
212
- checksum += (self.ip_dst & 0xffff)
213
- checksum = checksum % 0xffff
214
- checksum = 0xffff - checksum
215
- checksum == 0 ? 0xffff : checksum
216
- end
217
-
218
- # Retrieve the IP ID
219
- def ip_calc_id
220
- @random_id
221
- end
222
-
223
- # Sets a more readable IP address. If you wants to manipulate individual octets,
224
- # (eg, for host scanning in one network), it would be better use ip_src.o1 through
225
- # ip_src.o4 instead.
226
- def ip_saddr=(addr)
227
- self[:ip_src].read_quad(addr)
228
- end
229
-
230
- # Returns a more readable IP source address.
231
- def ip_saddr
232
- self[:ip_src].to_x
233
- end
234
-
235
- # Sets a more readable IP address.
236
- def ip_daddr=(addr)
237
- self[:ip_dst].read_quad(addr)
238
- end
239
-
240
- # Returns a more readable IP destination address.
241
- def ip_daddr
242
- self[:ip_dst].to_x
243
- end
244
-
245
- # Translate various formats of IPv4 Addresses to an array of digits.
246
- def self.octet_array(addr)
247
- if addr.class == String
248
- oa = addr.split('.').collect {|x| x.to_i}
249
- elsif addr.class == Fixnum
250
- oa = IPAddr.new(addr, Socket::AF_INET).to_s.split('.')
251
- elsif addr.class == Bignum
252
- oa = IPAddr.new(addr, Socket::AF_INET).to_s.split('.')
253
- elsif addr.class == Array
254
- oa = addr
255
- else
256
- raise ArgumentError, "IP Address should be a dotted quad string, an array of ints, or a bignum"
257
- end
258
- end
259
-
260
- # Recalculate the calculated IP fields. Valid arguments are:
261
- # :all
262
- # :ip_len
263
- # :ip_sum
264
- # :ip_id
265
- def ip_recalc(arg=:all)
266
- case arg
267
- when :ip_len
268
- self.ip_len=ip_calc_len
269
- when :ip_sum
270
- self.ip_sum=ip_calc_sum
271
- when :ip_id
272
- @random_id = rand(0xffff)
273
- when :all
274
- self.ip_id= ip_calc_id
275
- self.ip_len= ip_calc_len
276
- self.ip_sum= ip_calc_sum
277
- else
278
- raise ArgumentError, "No such field `#{arg}'"
279
- end
280
- end
281
-
282
- # Readability aliases
283
-
284
- alias :ip_src_readable :ip_saddr
285
- alias :ip_dst_readable :ip_daddr
286
-
287
- def ip_id_readable
288
- "0x%04x" % ip_id
289
- end
290
-
291
- def ip_sum_readable
292
- "0x%04x" % ip_sum
293
- end
294
-
295
- end
296
-
297
- # IPPacket is used to construct IP packets. They contain an EthHeader, an IPHeader, and usually
298
- # a transport-layer protocol such as UDPHeader, TCPHeader, or ICMPHeader.
299
- #
300
- # == Example
301
- #
302
- # require 'packetfu'
303
- # ip_pkt = PacketFu::IPPacket.new
304
- # ip_pkt.ip_saddr="10.20.30.40"
305
- # ip_pkt.ip_daddr="192.168.1.1"
306
- # ip_pkt.ip_proto=1
307
- # ip_pkt.ip_ttl=64
308
- # ip_pkt.ip_payload="\x00\x00\x12\x34\x00\x01\x00\x01"+
309
- # "Lovingly hand-crafted echo responses delivered directly to your door."
310
- # ip_pkt.recalc
311
- # ip_pkt.to_f('/tmp/ip.pcap')
312
- #
313
- # == Parameters
314
- #
315
- # :eth
316
- # A pre-generated EthHeader object.
317
- # :ip
318
- # A pre-generated IPHeader object.
319
- # :flavor
320
- # TODO: Sets the "flavor" of the IP packet. This might include known sets of IP options, and
321
- # certainly known starting TTLs.
322
- # :config
323
- # A hash of return address details, often the output of Utils.whoami?
324
- class IPPacket < Packet
325
-
326
- attr_accessor :eth_header, :ip_header
327
-
328
- def self.can_parse?(str)
329
- return false unless str.size >= 34
330
- return false unless EthPacket.can_parse? str
331
- if str[12,2] == "\x08\x00"
332
- if 1.respond_to? :ord
333
- ipv = str[14,1][0].ord >> 4
334
- else
335
- ipv = str[14,1][0] >> 4
336
- end
337
- return true if ipv == 4
338
- else
339
- return false
340
- end
341
- end
342
-
343
- def read(str=nil, args={})
344
- raise "Cannot parse `#{str}'" unless self.class.can_parse?(str)
345
- @eth_header.read(str)
346
- @ip_header.read(str[14,str.size])
347
- @eth_header.body = @ip_header
348
- super(args)
349
- self
350
- end
351
-
352
- # Creates a new IPPacket object.
353
- def initialize(args={})
354
- @eth_header = EthHeader.new(args).read(args[:eth])
355
- @ip_header = IPHeader.new(args).read(args[:ip])
356
- @eth_header.body=@ip_header
357
-
358
- @headers = [@eth_header, @ip_header]
359
- super
360
- end
361
-
362
- # Peek provides summary data on packet contents.
363
- def peek_format
364
- peek_data = ["I "]
365
- peek_data << "%-5d" % to_s.size
366
- peek_data << "%-21s" % "#{ip_saddr}"
367
- peek_data << "->"
368
- peek_data << "%21s" % "#{ip_daddr}"
369
- peek_data << "%23s" % "I:"
370
- peek_data << "%04x" % ip_id.to_i
371
- peek_data.join
372
- end
373
-
374
- end
375
-
376
- end
377
-
378
- # vim: nowrap sw=2 sts=0 ts=2 ff=unix ft=ruby