capp 1.0
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.
- 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
|
+
|