packetfu 1.1.1 → 1.1.2
Sign up to get free protection for your applications and to get access to all the features.
- data/{README → README.rdoc} +2 -2
- metadata +58 -94
- data/INSTALL +0 -40
- data/LICENSE +0 -28
- data/examples/100kpackets.rb +0 -41
- data/examples/ackscan.rb +0 -38
- data/examples/arp.rb +0 -60
- data/examples/arphood.rb +0 -59
- data/examples/dissect_thinger.rb +0 -22
- data/examples/ethernet.rb +0 -10
- data/examples/examples.rb +0 -3
- data/examples/ids.rb +0 -4
- data/examples/idsv2.rb +0 -6
- data/examples/new-simple-stats.rb +0 -52
- data/examples/oui.txt +0 -84177
- data/examples/packetfu-shell.rb +0 -113
- data/examples/simple-sniffer.rb +0 -40
- data/examples/simple-stats.rb +0 -50
- data/examples/slammer.rb +0 -33
- data/examples/uniqpcap.rb +0 -15
- data/lib/packetfu.rb +0 -147
- data/lib/packetfu/capture.rb +0 -169
- data/lib/packetfu/config.rb +0 -58
- data/lib/packetfu/inject.rb +0 -65
- data/lib/packetfu/packet.rb +0 -533
- data/lib/packetfu/pcap.rb +0 -594
- data/lib/packetfu/protos/arp.rb +0 -268
- data/lib/packetfu/protos/eth.rb +0 -296
- data/lib/packetfu/protos/hsrp.rb +0 -206
- data/lib/packetfu/protos/icmp.rb +0 -179
- data/lib/packetfu/protos/invalid.rb +0 -55
- data/lib/packetfu/protos/ip.rb +0 -378
- data/lib/packetfu/protos/ipv6.rb +0 -250
- data/lib/packetfu/protos/tcp.rb +0 -1127
- data/lib/packetfu/protos/udp.rb +0 -240
- data/lib/packetfu/structfu.rb +0 -294
- data/lib/packetfu/utils.rb +0 -194
- data/lib/packetfu/version.rb +0 -50
- data/test/all_tests.rb +0 -41
- data/test/arp_test.pcap +0 -0
- data/test/eth_test.pcap +0 -0
- data/test/ethpacket_spec.rb +0 -74
- data/test/icmp_test.pcap +0 -0
- data/test/ip_test.pcap +0 -0
- data/test/packet_spec.rb +0 -73
- data/test/packet_subclasses_spec.rb +0 -13
- data/test/packetfu_spec.rb +0 -90
- data/test/ptest.rb +0 -16
- data/test/sample-ipv6.pcap +0 -0
- data/test/sample.pcap +0 -0
- data/test/sample2.pcap +0 -0
- data/test/sample_hsrp_pcapr.cap +0 -0
- data/test/structfu_spec.rb +0 -335
- data/test/tcp_spec.rb +0 -101
- data/test/tcp_test.pcap +0 -0
- data/test/test_arp.rb +0 -135
- data/test/test_eth.rb +0 -91
- data/test/test_hsrp.rb +0 -20
- data/test/test_icmp.rb +0 -54
- data/test/test_inject.rb +0 -31
- data/test/test_invalid.rb +0 -28
- data/test/test_ip.rb +0 -69
- data/test/test_ip6.rb +0 -68
- data/test/test_octets.rb +0 -37
- data/test/test_packet.rb +0 -174
- data/test/test_pcap.rb +0 -209
- data/test/test_structfu.rb +0 -112
- data/test/test_tcp.rb +0 -327
- data/test/test_udp.rb +0 -73
- data/test/udp_test.pcap +0 -0
- data/test/vlan-pcapr.cap +0 -0
data/lib/packetfu/protos/hsrp.rb
DELETED
@@ -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
|
data/lib/packetfu/protos/icmp.rb
DELETED
@@ -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
|
data/lib/packetfu/protos/ip.rb
DELETED
@@ -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
|