packetfu 1.1.1 → 1.1.2

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