capp 1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/.autotest +12 -0
- data/.gemtest +0 -0
- data/.hoerc +2 -0
- data/History.rdoc +4 -0
- data/Manifest.txt +24 -0
- data/README.rdoc +84 -0
- data/Rakefile +57 -0
- data/ext/capp/capp.c +1382 -0
- data/ext/capp/extconf.rb +34 -0
- data/ext/capp/structs.h +86 -0
- data/lib/capp.rb +168 -0
- data/lib/capp/packet.rb +402 -0
- data/lib/capp/test_case.rb +61 -0
- data/test/802.1X.pcap +0 -0
- data/test/arp.pcap +0 -0
- data/test/icmp4.pcap +0 -0
- data/test/icmp6.pcap +0 -0
- data/test/tcp4.pcap +0 -0
- data/test/tcp6.pcap +0 -0
- data/test/test_capp.rb +273 -0
- data/test/test_capp_packet.rb +160 -0
- data/test/test_capp_packet_tcp_header.rb +103 -0
- data/test/test_capp_root.rb +194 -0
- data/test/udp4.pcap +0 -0
- data/test/udp6.pcap +0 -0
- metadata +161 -0
- metadata.gz.sig +0 -0
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'capp'
|
3
|
+
|
4
|
+
##
|
5
|
+
# Capp::TestCase contains some useful methods for testing parts of Capp.
|
6
|
+
#
|
7
|
+
# The _DUMP constants are created from pcap files in the test directory. You
|
8
|
+
# can create your own capture from tcpdump:
|
9
|
+
#
|
10
|
+
# tcpdump -r test/my.pcap [your specific capture arguments]
|
11
|
+
|
12
|
+
class Capp::TestCase < MiniTest::Unit::TestCase
|
13
|
+
|
14
|
+
##
|
15
|
+
# An ARP packet
|
16
|
+
|
17
|
+
ARP_DUMP = File.expand_path '../../../test/arp.pcap', __FILE__
|
18
|
+
|
19
|
+
##
|
20
|
+
# An EAP 802.1X packet
|
21
|
+
|
22
|
+
EAP_802_1X_DUMP = File.expand_path '../../../test/802.1X.pcap', __FILE__
|
23
|
+
|
24
|
+
##
|
25
|
+
# An ICMPv4 packet
|
26
|
+
|
27
|
+
ICMP4_DUMP = File.expand_path '../../../test/icmp4.pcap', __FILE__
|
28
|
+
|
29
|
+
##
|
30
|
+
# An ICMPv6 packet
|
31
|
+
|
32
|
+
ICMP6_DUMP = File.expand_path '../../../test/icmp6.pcap', __FILE__
|
33
|
+
|
34
|
+
##
|
35
|
+
# A TCPv4 packet
|
36
|
+
|
37
|
+
TCP4_DUMP = File.expand_path '../../../test/tcp4.pcap', __FILE__
|
38
|
+
|
39
|
+
##
|
40
|
+
# A TCPv6 packet
|
41
|
+
|
42
|
+
TCP6_DUMP = File.expand_path '../../../test/tcp6.pcap', __FILE__
|
43
|
+
|
44
|
+
## A UDPv4 packet
|
45
|
+
|
46
|
+
UDP4_DUMP = File.expand_path '../../../test/udp4.pcap', __FILE__
|
47
|
+
|
48
|
+
##
|
49
|
+
# A UDPv6 packet
|
50
|
+
|
51
|
+
UDP6_DUMP = File.expand_path '../../../test/udp6.pcap', __FILE__
|
52
|
+
|
53
|
+
##
|
54
|
+
# Returns the first packet in +dump+
|
55
|
+
|
56
|
+
def packet dump
|
57
|
+
Capp.offline(dump).loop.first
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
|
data/test/802.1X.pcap
ADDED
Binary file
|
data/test/arp.pcap
ADDED
Binary file
|
data/test/icmp4.pcap
ADDED
Binary file
|
data/test/icmp6.pcap
ADDED
Binary file
|
data/test/tcp4.pcap
ADDED
Binary file
|
data/test/tcp6.pcap
ADDED
Binary file
|
data/test/test_capp.rb
ADDED
@@ -0,0 +1,273 @@
|
|
1
|
+
require 'capp/test_case'
|
2
|
+
|
3
|
+
class TestCapp < Capp::TestCase
|
4
|
+
|
5
|
+
def test_class_drop_privileges_not_root
|
6
|
+
dir = Dir.pwd
|
7
|
+
orig = Etc.getpwuid
|
8
|
+
|
9
|
+
skip 'you are root' if Process.uid.zero? and Process.euid.zero?
|
10
|
+
|
11
|
+
Capp.drop_privileges 'nobody'
|
12
|
+
|
13
|
+
user = Etc.getpwuid
|
14
|
+
|
15
|
+
assert_equal orig.uid, user.uid
|
16
|
+
assert_equal orig.gid, user.gid
|
17
|
+
|
18
|
+
assert_equal dir, Dir.pwd
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_class_offline_file
|
22
|
+
open ICMP4_DUMP do |io|
|
23
|
+
capp = Capp.offline io
|
24
|
+
|
25
|
+
assert capp.loop.first
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_class_offline_filename
|
30
|
+
capp = Capp.offline ICMP4_DUMP
|
31
|
+
|
32
|
+
assert capp.loop.first
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_class_open_file
|
36
|
+
open ICMP4_DUMP do |io|
|
37
|
+
capp = Capp.open io
|
38
|
+
|
39
|
+
assert capp.loop.first
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_class_open_filename
|
44
|
+
capp = Capp.open ICMP4_DUMP
|
45
|
+
|
46
|
+
assert capp.loop.first
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_class_pcap_lib_version
|
50
|
+
lib_version = Capp.pcap_lib_version
|
51
|
+
|
52
|
+
assert_match 'libpcap', lib_version
|
53
|
+
assert_match %r%\d\.%, lib_version
|
54
|
+
end
|
55
|
+
|
56
|
+
def test_datalink_equals
|
57
|
+
capp = Capp.offline UDP4_DUMP
|
58
|
+
links = capp.datalinks
|
59
|
+
|
60
|
+
capp.datalink = links.last
|
61
|
+
|
62
|
+
# this test might be useless for offline capture
|
63
|
+
assert_equal links.last, capp.datalink
|
64
|
+
end
|
65
|
+
|
66
|
+
def test_datalinks
|
67
|
+
links = Capp.offline(UDP4_DUMP).datalinks
|
68
|
+
|
69
|
+
assert_equal %w[EN10MB], links
|
70
|
+
end
|
71
|
+
|
72
|
+
def test_ethernet_header
|
73
|
+
capp = Capp.offline UDP4_DUMP
|
74
|
+
|
75
|
+
packet = capp.loop.first
|
76
|
+
|
77
|
+
header = packet.ethernet_header
|
78
|
+
|
79
|
+
assert_equal 'ff:ff:ff:ff:ff:ff', header.destination
|
80
|
+
assert_equal '20:c9:d0:48:eb:73', header.source
|
81
|
+
assert_equal Capp::ETHERTYPE_IP, header.type
|
82
|
+
end
|
83
|
+
|
84
|
+
def test_filter_equals
|
85
|
+
capp = Capp.offline ICMP4_DUMP
|
86
|
+
|
87
|
+
capp.filter = 'icmp[icmptype] = icmp-echo'
|
88
|
+
|
89
|
+
assert_equal 2, capp.loop.count
|
90
|
+
end
|
91
|
+
|
92
|
+
def test_filter_equals_garbage
|
93
|
+
capp = Capp.offline ICMP4_DUMP
|
94
|
+
|
95
|
+
assert_raises Capp::Error do
|
96
|
+
capp.filter = 'garbage'
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def test_arp_header
|
101
|
+
capp = Capp.offline ARP_DUMP
|
102
|
+
|
103
|
+
packet = capp.loop.first
|
104
|
+
|
105
|
+
header = packet.arp_header
|
106
|
+
|
107
|
+
assert_equal Capp::ARPHRD_ETHER, header.hardware
|
108
|
+
assert_equal Capp::ETHERTYPE_IP, header.protocol
|
109
|
+
assert_equal Capp::ARPOP_REQUEST, header.operation
|
110
|
+
assert_match %r%\A0?2:c0:de:0?1:0?1:0?1\z%, header.sender_hardware_address
|
111
|
+
assert_equal '10.0.2.1', header.sender_protocol_address
|
112
|
+
assert_equal 'ff:ff:ff:ff:ff:ff', header.target_hardware_address
|
113
|
+
assert_equal '10.0.0.101', header.target_protocol_address
|
114
|
+
end
|
115
|
+
|
116
|
+
def test_ipv4_header
|
117
|
+
capp = Capp.offline ICMP4_DUMP
|
118
|
+
|
119
|
+
packet = capp.loop.first
|
120
|
+
|
121
|
+
header = packet.ipv4_header
|
122
|
+
|
123
|
+
assert_equal 4, header.version
|
124
|
+
assert_equal 5, header.ihl
|
125
|
+
assert_equal 0, header.tos
|
126
|
+
assert_equal 56, header.length
|
127
|
+
assert_equal 40436, header.id
|
128
|
+
assert_equal 0, header.offset
|
129
|
+
assert_equal 64, header.ttl
|
130
|
+
assert_equal 1, header.protocol
|
131
|
+
assert_equal 36729, header.checksum
|
132
|
+
assert_equal '10.101.28.65', header.source
|
133
|
+
assert_equal '10.101.28.77', header.destination
|
134
|
+
end
|
135
|
+
|
136
|
+
def test_ipv6_header
|
137
|
+
capp = Capp.offline ICMP6_DUMP
|
138
|
+
|
139
|
+
packet = capp.loop.first
|
140
|
+
|
141
|
+
header = packet.ipv6_header
|
142
|
+
|
143
|
+
assert_equal 6, header.version
|
144
|
+
assert_equal 0, header.traffic_class
|
145
|
+
assert_equal 1610612736, header.flow_label
|
146
|
+
assert_equal 24, header.payload_length
|
147
|
+
assert_equal 58, header.next_header
|
148
|
+
assert_equal 255, header.hop_limit
|
149
|
+
assert_equal '::', header.source
|
150
|
+
assert_equal 'ff02::1:ff48:eb73', header.destination
|
151
|
+
end
|
152
|
+
|
153
|
+
def test_icmp4_header
|
154
|
+
capp = Capp.offline ICMP4_DUMP
|
155
|
+
|
156
|
+
packet = capp.loop.first
|
157
|
+
|
158
|
+
header = packet.icmp_header
|
159
|
+
|
160
|
+
assert_equal 3, header.type
|
161
|
+
assert_equal 3, header.code
|
162
|
+
assert_equal 19056, header.checksum
|
163
|
+
end
|
164
|
+
|
165
|
+
def test_icmp6_header
|
166
|
+
capp = Capp.offline ICMP6_DUMP
|
167
|
+
|
168
|
+
packet = capp.loop.first
|
169
|
+
|
170
|
+
header = packet.icmp_header
|
171
|
+
|
172
|
+
assert_equal 135, header.type
|
173
|
+
assert_equal 0, header.code
|
174
|
+
assert_equal 45797, header.checksum
|
175
|
+
end
|
176
|
+
|
177
|
+
def test_loop
|
178
|
+
capp = Capp.offline ICMP4_DUMP
|
179
|
+
|
180
|
+
packets = []
|
181
|
+
|
182
|
+
capp.loop do |packet|
|
183
|
+
packets << packet
|
184
|
+
end
|
185
|
+
|
186
|
+
assert_equal 4, packets.size
|
187
|
+
end
|
188
|
+
|
189
|
+
def test_savefile_major_version
|
190
|
+
major_version = Capp.offline(UDP4_DUMP).savefile_major_version
|
191
|
+
|
192
|
+
assert_equal 2, major_version
|
193
|
+
end
|
194
|
+
|
195
|
+
def test_savefile_minor_version
|
196
|
+
minor_version = Capp.offline(UDP4_DUMP).savefile_minor_version
|
197
|
+
|
198
|
+
assert_equal 4, minor_version
|
199
|
+
end
|
200
|
+
|
201
|
+
def test_savefile_version
|
202
|
+
version = Capp.offline(UDP4_DUMP).savefile_version
|
203
|
+
|
204
|
+
assert_equal '2.4', version
|
205
|
+
end
|
206
|
+
|
207
|
+
def test_stats
|
208
|
+
capp = Capp.offline ICMP4_DUMP
|
209
|
+
|
210
|
+
capp.loop.to_a
|
211
|
+
|
212
|
+
assert_raises Capp::Error do
|
213
|
+
capp.stats
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
def test_stop
|
218
|
+
capp = Capp.offline ICMP4_DUMP
|
219
|
+
|
220
|
+
packets = []
|
221
|
+
|
222
|
+
capp.loop do |packet|
|
223
|
+
packets << packet
|
224
|
+
|
225
|
+
capp.stop
|
226
|
+
end
|
227
|
+
|
228
|
+
assert_equal 1, packets.size
|
229
|
+
end
|
230
|
+
|
231
|
+
def test_tcp4_header
|
232
|
+
capp = Capp.offline TCP4_DUMP
|
233
|
+
|
234
|
+
packet = capp.loop.first
|
235
|
+
|
236
|
+
header = packet.tcp_header
|
237
|
+
|
238
|
+
assert_equal 49475, header.source_port
|
239
|
+
assert_equal 9091, header.destination_port
|
240
|
+
assert_equal 192875902, header.seq_number
|
241
|
+
assert_equal 0, header.ack_number
|
242
|
+
assert_equal 11, header.offset
|
243
|
+
assert_equal 2, header.flags
|
244
|
+
assert_equal 65535, header.window
|
245
|
+
assert_equal 7778, header.checksum
|
246
|
+
assert_equal 0, header.urgent
|
247
|
+
end
|
248
|
+
|
249
|
+
def test_udp4_header
|
250
|
+
capp = Capp.offline UDP4_DUMP
|
251
|
+
|
252
|
+
packet = capp.loop.first
|
253
|
+
|
254
|
+
header = packet.udp_header
|
255
|
+
|
256
|
+
assert_equal 54938, header.source_port
|
257
|
+
assert_equal 7647, header.destination_port
|
258
|
+
assert_equal 105, header.length
|
259
|
+
assert_equal 3147, header.checksum
|
260
|
+
end
|
261
|
+
|
262
|
+
def test_unknown_layer3_header
|
263
|
+
capp = Capp.offline EAP_802_1X_DUMP
|
264
|
+
|
265
|
+
packet = capp.loop.first
|
266
|
+
|
267
|
+
header = packet.unknown_layer3_header
|
268
|
+
|
269
|
+
assert_equal 14, header.payload_offset
|
270
|
+
end
|
271
|
+
|
272
|
+
end
|
273
|
+
|
@@ -0,0 +1,160 @@
|
|
1
|
+
# encoding: BINARY
|
2
|
+
|
3
|
+
require 'capp/test_case'
|
4
|
+
require 'resolv'
|
5
|
+
require 'tempfile'
|
6
|
+
|
7
|
+
class TestCappPacket < Capp::TestCase
|
8
|
+
|
9
|
+
def setup
|
10
|
+
super
|
11
|
+
|
12
|
+
@CP = Capp::Packet
|
13
|
+
|
14
|
+
@timestamp = Time.now
|
15
|
+
@captured =
|
16
|
+
"\x01\x00\x5e\x00\x00\xfb\x20\xc9\xd0\x48\xeb\x73\x08\x00\x45\x00" +
|
17
|
+
"\x00\x39\xef\x92\x00\x00\x01\x11\xc2\x74\x0a\x65\x1c\x4d\xe0\x00" +
|
18
|
+
"\x00\xfb\xfa\x0a\x14\xe9\x00\x25\x3a\x49\x02\x28\x01\x00\x00\x01" +
|
19
|
+
"\x00\x00\x00\x00\x00\x00\x05\x6b\x61\x75\x6c\x74\x05\x6c\x6f\x63" +
|
20
|
+
"\x61\x6c\x00\x00\x01\x00\x01"
|
21
|
+
|
22
|
+
|
23
|
+
length = @captured.length
|
24
|
+
|
25
|
+
@headers = {
|
26
|
+
ethernet:
|
27
|
+
@CP::EthernetHeader.new(0x01_00_5e_00_00_fb, 0x20_c9_d0_48_eb_73,
|
28
|
+
0x0800),
|
29
|
+
ipv4:
|
30
|
+
@CP::IPv4Header.new(4, 5, 0, 57, 61330, 0, 1, 17, 49780,
|
31
|
+
'10.101.28.77', '224.0.0.251'),
|
32
|
+
udp:
|
33
|
+
@CP::UDPHeader.new(64010, 5353, 37, 14921),
|
34
|
+
}
|
35
|
+
|
36
|
+
@packet =
|
37
|
+
@CP.new @timestamp, length, length, @captured, Capp::DLT_EN10MB, @headers
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_destination
|
41
|
+
assert_equal '224.0.0.251.5353', @packet.destination
|
42
|
+
assert_equal '224.0.0.251.5353', @packet.destination
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_destination_resolver
|
46
|
+
assert_equal 'mdns.mcast.net.5353', @packet.destination(resolver)
|
47
|
+
|
48
|
+
@packet.ipv4_header.destination = '192.0.2.1'
|
49
|
+
|
50
|
+
assert_equal '192.0.2.1.5353', @packet.destination(resolver)
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_destination_udp4
|
54
|
+
assert_equal '224.0.0.251.5353', @packet.destination
|
55
|
+
end
|
56
|
+
|
57
|
+
def test_dump
|
58
|
+
expected = '..^... ..H'
|
59
|
+
assert_equal expected, @packet.dump[0, 10]
|
60
|
+
end
|
61
|
+
|
62
|
+
def test_ethernet_header
|
63
|
+
header = @packet.ethernet_header
|
64
|
+
|
65
|
+
assert_equal 0x01_00_5e_00_00_fb, header.destination, 'destination'
|
66
|
+
assert_equal 0x20_c9_d0_48_eb_73, header.source, 'source'
|
67
|
+
assert_equal 0x0800, header.type, 'type'
|
68
|
+
end
|
69
|
+
|
70
|
+
def test_hexdump
|
71
|
+
expected = <<-EXPECTED
|
72
|
+
\t0x0000: 0100 5e00 00fb 20c9 d048 eb73 0800 4500 ..^... ..H.s..E.
|
73
|
+
\t0x0010: 0039 ef92 0000 0111 c274 0a65 1c4d e000 .9.......t.e.M..
|
74
|
+
\t0x0020: 00fb fa0a 14e9 0025 3a49 0228 0100 0001 .......%:I.(....
|
75
|
+
\t0x0030: 0000 0000 0000 056b 6175 6c74 056c 6f63 .......kault.loc
|
76
|
+
\t0x0040: 616c 0000 0100 01 al.....
|
77
|
+
EXPECTED
|
78
|
+
|
79
|
+
assert_equal expected, @packet.hexdump
|
80
|
+
end
|
81
|
+
|
82
|
+
def test_hexdump_offset
|
83
|
+
expected = <<-EXPECTED
|
84
|
+
\t0x0000: 4500 0039 ef92 0000 0111 c274 0a65 1c4d E..9.......t.e.M
|
85
|
+
\t0x0010: e000 00fb fa0a 14e9 0025 3a49 0228 0100 .........%:I.(..
|
86
|
+
\t0x0020: 0001 0000 0000 0000 056b 6175 6c74 056c .........kault.l
|
87
|
+
\t0x0030: 6f63 616c 0000 0100 01 ocal.....
|
88
|
+
EXPECTED
|
89
|
+
|
90
|
+
assert_equal expected, @packet.hexdump(14)
|
91
|
+
end
|
92
|
+
|
93
|
+
def test_ipv4_eh
|
94
|
+
assert @packet.ipv4?
|
95
|
+
end
|
96
|
+
|
97
|
+
def test_ipv6_eh
|
98
|
+
refute @packet.ipv6?
|
99
|
+
end
|
100
|
+
|
101
|
+
def test_payload
|
102
|
+
expected = dump @captured[42, @captured.length]
|
103
|
+
assert_equal expected, dump(@packet.payload)
|
104
|
+
end
|
105
|
+
|
106
|
+
def test_payload_offset
|
107
|
+
assert_equal 78, packet(TCP4_DUMP).payload_offset
|
108
|
+
assert_equal 42, packet(UDP4_DUMP).payload_offset
|
109
|
+
|
110
|
+
assert_equal 88, packet(TCP6_DUMP).payload_offset
|
111
|
+
assert_equal 62, packet(UDP6_DUMP).payload_offset
|
112
|
+
|
113
|
+
assert_raises NotImplementedError do
|
114
|
+
packet(ARP_DUMP).payload_offset
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def test_protocols
|
119
|
+
assert_equal [:ethernet, :ipv4, :udp], @packet.protocols
|
120
|
+
end
|
121
|
+
|
122
|
+
def test_source
|
123
|
+
assert_equal '10.101.28.77.64010', @packet.source
|
124
|
+
assert_equal '10.101.28.77.64010', @packet.source
|
125
|
+
end
|
126
|
+
|
127
|
+
def test_source_resolver
|
128
|
+
assert_equal 'kault.64010', @packet.source(resolver)
|
129
|
+
|
130
|
+
@packet.ipv4_header.source = '192.0.2.1'
|
131
|
+
|
132
|
+
assert_equal '192.0.2.1.64010', @packet.source(resolver)
|
133
|
+
end
|
134
|
+
|
135
|
+
def test_source_udp4
|
136
|
+
assert_equal '10.101.28.77.64010', @packet.source
|
137
|
+
end
|
138
|
+
|
139
|
+
def test_udp_eh
|
140
|
+
assert @packet.udp?
|
141
|
+
end
|
142
|
+
|
143
|
+
def dump str
|
144
|
+
str.tr "\000-\037\177-\377", "."
|
145
|
+
end
|
146
|
+
|
147
|
+
def resolver
|
148
|
+
Tempfile.open 'hosts' do |io|
|
149
|
+
io.puts '224.0.0.251 mdns.mcast.net'
|
150
|
+
io.puts '10.101.28.77 kault'
|
151
|
+
io.flush
|
152
|
+
|
153
|
+
resolver = Resolv::Hosts.new io.path
|
154
|
+
resolver.getname '224.0.0.251' # initialize
|
155
|
+
resolver
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
end
|
160
|
+
|