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
data/ext/capp/extconf.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'mkmf'
|
2
|
+
|
3
|
+
$CFLAGS << " #{ENV['CFLAGS']}" if ENV['CFLAGS']
|
4
|
+
$LIBS << " #{ENV['LIBS']}" if ENV['LIBS']
|
5
|
+
|
6
|
+
have_library 'pcap' or abort 'missing pcap library'
|
7
|
+
|
8
|
+
def require_header header
|
9
|
+
have_header header or abort "missing #{header}"
|
10
|
+
end
|
11
|
+
|
12
|
+
require_header 'pcap/pcap.h'
|
13
|
+
|
14
|
+
require_header 'sys/socket.h'
|
15
|
+
require_header 'net/ethernet.h'
|
16
|
+
require_header 'net/if_arp.h'
|
17
|
+
require_header 'netinet/ip.h'
|
18
|
+
require_header 'netinet/ip6.h'
|
19
|
+
require_header 'netinet/ip_icmp.h'
|
20
|
+
require_header 'arpa/inet.h'
|
21
|
+
|
22
|
+
have_header 'net/if_dl.h'
|
23
|
+
have_header 'netinet/ether.h'
|
24
|
+
|
25
|
+
have_header 'ruby/thread.h'
|
26
|
+
|
27
|
+
have_macro 'RETURN_ENUMERATOR' or abort 'missing C enumerator support'
|
28
|
+
|
29
|
+
have_macro 'PCAP_WARNING_TSTAMP_TYPE_NOTSUP'
|
30
|
+
have_macro 'PCAP_ERROR_PROMISC_PERM_DENIED'
|
31
|
+
|
32
|
+
create_header
|
33
|
+
create_makefile 'capp/capp'
|
34
|
+
|
data/ext/capp/structs.h
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
/*
|
2
|
+
* The following items are copied from tcpdump.
|
3
|
+
*
|
4
|
+
* Redistribution and use in source and binary forms, with or without
|
5
|
+
* modification, are permitted provided that the following conditions
|
6
|
+
* are met:
|
7
|
+
*
|
8
|
+
* 1. Redistributions of source code must retain the above copyright
|
9
|
+
* notice, this list of conditions and the following disclaimer.
|
10
|
+
* 2. Redistributions in binary form must reproduce the above copyright
|
11
|
+
* notice, this list of conditions and the following disclaimer in
|
12
|
+
* the documentation and/or other materials provided with the
|
13
|
+
* distribution.
|
14
|
+
* 3. The names of the authors may not be used to endorse or promote
|
15
|
+
* products derived from this software without specific prior
|
16
|
+
* written permission.
|
17
|
+
*
|
18
|
+
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
|
19
|
+
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
|
20
|
+
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
21
|
+
*/
|
22
|
+
|
23
|
+
typedef u_int32_t tcp_seq;
|
24
|
+
|
25
|
+
/*
|
26
|
+
* TCP header.
|
27
|
+
* Per RFC 793, September, 1981.
|
28
|
+
*/
|
29
|
+
struct tcphdr {
|
30
|
+
u_int16_t th_sport; /* source port */
|
31
|
+
u_int16_t th_dport; /* destination port */
|
32
|
+
tcp_seq th_seq; /* sequence number */
|
33
|
+
tcp_seq th_ack; /* acknowledgement number */
|
34
|
+
u_int8_t th_offx2; /* data offset, rsvd */
|
35
|
+
u_int8_t th_flags;
|
36
|
+
u_int16_t th_win; /* window */
|
37
|
+
u_int16_t th_sum; /* checksum */
|
38
|
+
u_int16_t th_urp; /* urgent pointer */
|
39
|
+
};
|
40
|
+
|
41
|
+
#define TH_OFF(th) (((th)->th_offx2 & 0xf0) >> 4)
|
42
|
+
|
43
|
+
#define TH_FIN 0x01
|
44
|
+
#define TH_SYN 0x02
|
45
|
+
#define TH_RST 0x04
|
46
|
+
#define TH_PUSH 0x08
|
47
|
+
#define TH_ACK 0x10
|
48
|
+
#define TH_URG 0x20
|
49
|
+
#define TH_ECE 0x40
|
50
|
+
#define TH_CWR 0x80
|
51
|
+
|
52
|
+
/*
|
53
|
+
* Udp protocol header.
|
54
|
+
* Per RFC 768, September, 1981.
|
55
|
+
*/
|
56
|
+
struct udphdr {
|
57
|
+
u_int16_t uh_sport; /* source port */
|
58
|
+
u_int16_t uh_dport; /* destination port */
|
59
|
+
u_int16_t uh_ulen; /* udp length */
|
60
|
+
u_int16_t uh_sum; /* udp checksum */
|
61
|
+
};
|
62
|
+
|
63
|
+
/*
|
64
|
+
* Byte-swap a 32-bit number.
|
65
|
+
* ("htonl()" or "ntohl()" won't work - we want to byte-swap even on
|
66
|
+
* big-endian platforms.)
|
67
|
+
*/
|
68
|
+
#define SWAPLONG(y) \
|
69
|
+
((((y)&0xff)<<24) | (((y)&0xff00)<<8) | (((y)&0xff0000)>>8) | (((y)>>24)&0xff))
|
70
|
+
|
71
|
+
/*
|
72
|
+
* BSD AF_ values.
|
73
|
+
*
|
74
|
+
* Unfortunately, the BSDs don't all use the same value for AF_INET6,
|
75
|
+
* so, because we want to be able to read captures from all of the BSDs,
|
76
|
+
* we check for all of them.
|
77
|
+
*/
|
78
|
+
#define BSD_AFNUM_INET 2
|
79
|
+
#define BSD_AFNUM_NS 6 /* XEROX NS protocols */
|
80
|
+
#define BSD_AFNUM_ISO 7
|
81
|
+
#define BSD_AFNUM_APPLETALK 16
|
82
|
+
#define BSD_AFNUM_IPX 23
|
83
|
+
#define BSD_AFNUM_INET6_BSD 24 /* OpenBSD (and probably NetBSD), BSD/OS */
|
84
|
+
#define BSD_AFNUM_INET6_FREEBSD 28
|
85
|
+
#define BSD_AFNUM_INET6_DARWIN 30
|
86
|
+
|
data/lib/capp.rb
ADDED
@@ -0,0 +1,168 @@
|
|
1
|
+
require 'socket'
|
2
|
+
|
3
|
+
##
|
4
|
+
# Capp is a GVL-friendly libpcap wrapper library.
|
5
|
+
#
|
6
|
+
# To create a packet capture device:
|
7
|
+
#
|
8
|
+
# capp = Capp.live
|
9
|
+
#
|
10
|
+
# This listens on the default device. You can list devices with Capp.devices.
|
11
|
+
#
|
12
|
+
# To start capture use #loop:
|
13
|
+
#
|
14
|
+
# capp.loop do |packet|
|
15
|
+
# # ...
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# #loop yields a Capp::Packet object for each captured packet.
|
19
|
+
#
|
20
|
+
# To stop capturing packets return (or break) from the loop, or call #stop on
|
21
|
+
# the Capp instance. You can resume capturing packets by calling #loop again
|
22
|
+
# after #stop.
|
23
|
+
#
|
24
|
+
# To set a filter for only udp port 7647 (Rinda::RingFinger packets):
|
25
|
+
#
|
26
|
+
# capp.filter = 'udp port 7647'
|
27
|
+
#
|
28
|
+
# The format for a filter rule is the same as for tcpdump. See the
|
29
|
+
# pcap-filter(7) man page for the filter syntax.
|
30
|
+
#
|
31
|
+
# You can use a Queue to capture packets in one thread and process them in
|
32
|
+
# another:
|
33
|
+
#
|
34
|
+
# require 'capp'
|
35
|
+
# require 'thread'
|
36
|
+
#
|
37
|
+
# q = Queue.new
|
38
|
+
#
|
39
|
+
# Thread.new do
|
40
|
+
# while packet = q.deq do
|
41
|
+
# # ...
|
42
|
+
# end
|
43
|
+
# end
|
44
|
+
#
|
45
|
+
# capp = Capp.live.loop do |packet|
|
46
|
+
# q.enq packet
|
47
|
+
# end
|
48
|
+
|
49
|
+
class Capp
|
50
|
+
|
51
|
+
##
|
52
|
+
# The version of Capp you are using
|
53
|
+
|
54
|
+
VERSION = '1.0'
|
55
|
+
|
56
|
+
##
|
57
|
+
# Error class for Capp errors
|
58
|
+
|
59
|
+
class Error < RuntimeError
|
60
|
+
end
|
61
|
+
|
62
|
+
##
|
63
|
+
# An address for a Device which is returned by Capp::devices
|
64
|
+
|
65
|
+
Address = Struct.new :address, :netmask, :broadcast, :destination
|
66
|
+
|
67
|
+
##
|
68
|
+
# A device which Capp can listen on, returned by Capp::devices
|
69
|
+
|
70
|
+
Device = Struct.new :name, :description, :addresses, :flags do
|
71
|
+
|
72
|
+
##
|
73
|
+
# Creates a new packet capture device for this device sending the given
|
74
|
+
# +args+ to Capp.open.
|
75
|
+
|
76
|
+
def open *args
|
77
|
+
Capp.open name, *args
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
|
82
|
+
##
|
83
|
+
# Device name packets are being captured from. Only set for live packet
|
84
|
+
# captures.
|
85
|
+
|
86
|
+
attr_reader :device
|
87
|
+
|
88
|
+
##
|
89
|
+
# Drops root privileges to the given +run_as_user+ and optionally chroots to
|
90
|
+
# +run_as_directory+. Use this method after creating a packet capture
|
91
|
+
# instance to improve security.
|
92
|
+
#
|
93
|
+
# Returns true if privileges are dropped, raises a Capp::Error if privileges
|
94
|
+
# could not be dropped and returns a false value if there was no need to
|
95
|
+
# drop privileges.
|
96
|
+
#
|
97
|
+
# You will be able to start and stop packet capture but not create new
|
98
|
+
# packet capture instances after dropping privileges.
|
99
|
+
|
100
|
+
def self.drop_privileges run_as_user, run_as_directory = nil
|
101
|
+
return unless Process.uid.zero? and Process.euid.zero?
|
102
|
+
return unless run_as_user or run_as_directory
|
103
|
+
|
104
|
+
raise Capp::Error, 'chroot without dropping root is insecure' if
|
105
|
+
run_as_directory and not run_as_user
|
106
|
+
|
107
|
+
require 'etc'
|
108
|
+
|
109
|
+
begin
|
110
|
+
pw = if Integer === run_as_user then
|
111
|
+
Etc.getpwuid run_as_user
|
112
|
+
else
|
113
|
+
Etc.getpwnam run_as_user
|
114
|
+
end
|
115
|
+
rescue ArgumentError => e
|
116
|
+
raise Capp::Error, "could not find user #{run_as_user}"
|
117
|
+
end
|
118
|
+
|
119
|
+
if run_as_directory then
|
120
|
+
begin
|
121
|
+
Dir.chroot run_as_directory
|
122
|
+
Dir.chdir '/'
|
123
|
+
rescue Errno::ENOENT => e
|
124
|
+
raise Capp::Error, "could not chroot to #{run_as_directory} " +
|
125
|
+
"or change to chroot directory"
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
begin
|
130
|
+
Process.gid = pw.gid
|
131
|
+
Process.uid = pw.uid
|
132
|
+
rescue Errno::EPERM => e
|
133
|
+
raise Capp::Error, "unable to drop privileges to #{run_as_user} " +
|
134
|
+
"(#{e.message})"
|
135
|
+
end
|
136
|
+
|
137
|
+
true
|
138
|
+
end
|
139
|
+
|
140
|
+
##
|
141
|
+
# Opens +device_or_file+ as an offline device if it is an IO or an existing
|
142
|
+
# file. +args+ are ignored (as ::offline does not support any).
|
143
|
+
#
|
144
|
+
# Opens +device_or_file+ as a live device otherwise, along with +args+. See
|
145
|
+
# ::live for documentation on the additional arguments.
|
146
|
+
|
147
|
+
def self.open device_or_file, *args
|
148
|
+
if IO === device_or_file or File.exist? device_or_file then
|
149
|
+
offline device_or_file, *args
|
150
|
+
else
|
151
|
+
live device_or_file, *args
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
##
|
156
|
+
# When called on a capture instance created from a savefile, returns the
|
157
|
+
# version of the savefile. When called on a live capture instance it
|
158
|
+
# returns a meaningless value.
|
159
|
+
|
160
|
+
def savefile_version
|
161
|
+
"#{savefile_major_version}.#{savefile_minor_version}"
|
162
|
+
end
|
163
|
+
|
164
|
+
end
|
165
|
+
|
166
|
+
require 'capp/packet'
|
167
|
+
require 'capp/capp'
|
168
|
+
|
data/lib/capp/packet.rb
ADDED
@@ -0,0 +1,402 @@
|
|
1
|
+
# coding: BINARY
|
2
|
+
|
3
|
+
##
|
4
|
+
# Capp::Packet provides convenient extraction of data from packets.
|
5
|
+
#
|
6
|
+
# Packet objects are automatically created when a packet is read from the
|
7
|
+
# opened interface. Unfortunately Capp does not understand every type of
|
8
|
+
# packet. If Capp doesn't understand your packet the layer 3 payload can be
|
9
|
+
# retrieved from unknown_layer3_header.
|
10
|
+
#
|
11
|
+
# If Capp doesn't understand your packets you can extract the data by editing
|
12
|
+
# capp.c and submitting a patch. See README for the source code location.
|
13
|
+
#
|
14
|
+
# To look up IP source and destination names Resolv (from the ruby standard
|
15
|
+
# library, require 'resolv') to avoid blocking on name lookups in a
|
16
|
+
# cross-platform manner.
|
17
|
+
|
18
|
+
class Capp::Packet
|
19
|
+
|
20
|
+
ADDRESS_CACHE = {} # :nodoc:
|
21
|
+
|
22
|
+
##
|
23
|
+
# ARP header. See RFC 826
|
24
|
+
|
25
|
+
ARPHeader = Struct.new :hardware, :protocol, :operation,
|
26
|
+
:sender_hardware_address, :sender_protocol_address,
|
27
|
+
:target_hardware_address, :target_protocol_address do
|
28
|
+
alias sha sender_hardware_address
|
29
|
+
alias spa sender_protocol_address
|
30
|
+
alias tha target_hardware_address
|
31
|
+
alias tpa target_protocol_address
|
32
|
+
end
|
33
|
+
|
34
|
+
##
|
35
|
+
# 802.3 Ethernet header
|
36
|
+
|
37
|
+
EthernetHeader = Struct.new :destination, :source, :type
|
38
|
+
|
39
|
+
##
|
40
|
+
# ICMP header. See RFC 792
|
41
|
+
|
42
|
+
ICMPHeader = Struct.new :type, :code, :checksum, :data
|
43
|
+
|
44
|
+
##
|
45
|
+
# IPv4 header. See RFC 791
|
46
|
+
|
47
|
+
IPv4Header = Struct.new :version, :ihl, :tos, :length,
|
48
|
+
:id, :offset,
|
49
|
+
:ttl, :protocol, :checksum,
|
50
|
+
:source, :destination
|
51
|
+
|
52
|
+
##
|
53
|
+
# IPv6 header. See RFC 2460
|
54
|
+
|
55
|
+
IPv6Header = Struct.new :version, :traffic_class, :flow_label,
|
56
|
+
:payload_length, :next_header, :hop_limit,
|
57
|
+
:source, :destination
|
58
|
+
|
59
|
+
##
|
60
|
+
# TCP header. See RFC 793
|
61
|
+
|
62
|
+
TCPHeader = Struct.new :source_port, :destination_port,
|
63
|
+
:seq_number, :ack_number,
|
64
|
+
:offset, :flags, :window, :checksum, :urgent do
|
65
|
+
|
66
|
+
alias source source_port
|
67
|
+
alias destination destination_port
|
68
|
+
|
69
|
+
##
|
70
|
+
# Is the acknowledgment flag set?
|
71
|
+
|
72
|
+
def ack?
|
73
|
+
Capp::TCP_ACK == flags & Capp::TCP_ACK
|
74
|
+
end
|
75
|
+
|
76
|
+
##
|
77
|
+
# Is the congestion window reduced flag set?
|
78
|
+
|
79
|
+
def cwr?
|
80
|
+
Capp::TCP_CWR == flags & Capp::TCP_CWR
|
81
|
+
end
|
82
|
+
|
83
|
+
##
|
84
|
+
# Is the explicit congestion notification echo flag set?
|
85
|
+
|
86
|
+
def ece?
|
87
|
+
Capp::TCP_ECE == flags & Capp::TCP_ECE
|
88
|
+
end
|
89
|
+
|
90
|
+
##
|
91
|
+
# Is the no-more-data flag set?
|
92
|
+
|
93
|
+
def fin?
|
94
|
+
Capp::TCP_FIN == flags & Capp::TCP_FIN
|
95
|
+
end
|
96
|
+
|
97
|
+
##
|
98
|
+
# Is the push flag set?
|
99
|
+
|
100
|
+
def push?
|
101
|
+
Capp::TCP_PUSH == flags & Capp::TCP_PUSH
|
102
|
+
end
|
103
|
+
|
104
|
+
##
|
105
|
+
# Is the reset flag set?
|
106
|
+
|
107
|
+
def rst?
|
108
|
+
Capp::TCP_RST == flags & Capp::TCP_RST
|
109
|
+
end
|
110
|
+
|
111
|
+
##
|
112
|
+
# Is the synchronize flag set?
|
113
|
+
|
114
|
+
def syn?
|
115
|
+
Capp::TCP_SYN == flags & Capp::TCP_SYN
|
116
|
+
end
|
117
|
+
|
118
|
+
##
|
119
|
+
# Is the urgent flag set?
|
120
|
+
|
121
|
+
def urg?
|
122
|
+
Capp::TCP_URG == flags & Capp::TCP_URG
|
123
|
+
end
|
124
|
+
|
125
|
+
end
|
126
|
+
|
127
|
+
##
|
128
|
+
# UDP header. See RFC 768
|
129
|
+
|
130
|
+
UDPHeader = Struct.new :source_port, :destination_port, :length, :checksum do
|
131
|
+
alias source source_port
|
132
|
+
alias destination destination_port
|
133
|
+
end
|
134
|
+
|
135
|
+
##
|
136
|
+
# Fake header for an unknown layer 3 protocol. See also
|
137
|
+
# Capp::Packet#unknown_layer3_header
|
138
|
+
|
139
|
+
UnknownLayer3Header = Struct.new :payload_offset
|
140
|
+
|
141
|
+
##
|
142
|
+
# Length of packet that was captured
|
143
|
+
|
144
|
+
attr_reader :capture_length
|
145
|
+
|
146
|
+
##
|
147
|
+
# Captured portion of the entire packet including datalink layer.
|
148
|
+
|
149
|
+
attr_reader :captured
|
150
|
+
|
151
|
+
##
|
152
|
+
# The ARP header if this is an ARP packet.
|
153
|
+
|
154
|
+
attr_reader :arp_header
|
155
|
+
|
156
|
+
##
|
157
|
+
# The Ethernet header if this is an Ethernet packet.
|
158
|
+
|
159
|
+
attr_reader :ethernet_header
|
160
|
+
|
161
|
+
##
|
162
|
+
# Array of protocol names in this packet. This list is ordered from lowest
|
163
|
+
# to highest level.
|
164
|
+
|
165
|
+
attr_reader :protocols
|
166
|
+
|
167
|
+
##
|
168
|
+
# ICMP header if this is an ICMP (v4) packet.
|
169
|
+
|
170
|
+
attr_reader :icmp_header
|
171
|
+
|
172
|
+
##
|
173
|
+
# IPv4 header if this is an IPv4 packet.
|
174
|
+
|
175
|
+
attr_reader :ipv4_header
|
176
|
+
|
177
|
+
##
|
178
|
+
# IPv6 header if this is an IPv6 packet.
|
179
|
+
|
180
|
+
attr_reader :ipv6_header
|
181
|
+
|
182
|
+
##
|
183
|
+
# Total length of packet including the portion not captured.
|
184
|
+
|
185
|
+
attr_reader :length
|
186
|
+
|
187
|
+
##
|
188
|
+
# TCP header if this is a TCP packet.
|
189
|
+
|
190
|
+
attr_reader :tcp_header
|
191
|
+
|
192
|
+
##
|
193
|
+
# Packet capture timestamp
|
194
|
+
|
195
|
+
attr_reader :timestamp
|
196
|
+
|
197
|
+
##
|
198
|
+
# UDP header if this is a UDP packet.
|
199
|
+
|
200
|
+
attr_reader :udp_header
|
201
|
+
|
202
|
+
##
|
203
|
+
# Fake header for unknown layer 3 protocols. The datalink type will
|
204
|
+
# indicate the layer 3 protocol. For an Ethernet packet see the
|
205
|
+
# ethernet_header for the type, etc. This method only provides the payload
|
206
|
+
# offset of the packet content.
|
207
|
+
|
208
|
+
attr_reader :unknown_layer3_header
|
209
|
+
|
210
|
+
##
|
211
|
+
# Creates a new packet. Ordinarily this is performed from Capp#loop. The
|
212
|
+
# +timestamp+ is the packet capture timestamp, +length+ is the total length
|
213
|
+
# of the packet, +capture_length+ is the number of captured bytes from the
|
214
|
+
# packet. The +datalink+ is the type of link the packet was captured on.
|
215
|
+
# +headers+ is a Hash of parsed headers.
|
216
|
+
|
217
|
+
def initialize timestamp, length, capture_length, captured, datalink, headers
|
218
|
+
@capture_length = capture_length
|
219
|
+
@captured = captured
|
220
|
+
@datalink = datalink
|
221
|
+
@length = length
|
222
|
+
@protocols = headers.keys
|
223
|
+
@timestamp = timestamp
|
224
|
+
|
225
|
+
@arp_header = headers[:arp]
|
226
|
+
@ethernet_header = headers[:ethernet]
|
227
|
+
@icmp_header = headers[:icmp]
|
228
|
+
@ipv4_header = headers[:ipv4]
|
229
|
+
@ipv6_header = headers[:ipv6]
|
230
|
+
@tcp_header = headers[:tcp]
|
231
|
+
@udp_header = headers[:udp]
|
232
|
+
@unknown_layer3_header = headers[:unknown_layer3]
|
233
|
+
end
|
234
|
+
|
235
|
+
##
|
236
|
+
# Returns the destination of the packet regardless of protocol
|
237
|
+
#
|
238
|
+
# If a Resolv-compatible +resolver+ is given the name will be looked up.
|
239
|
+
|
240
|
+
def destination resolver = nil
|
241
|
+
destination =
|
242
|
+
if ipv4? then
|
243
|
+
@ipv4_header
|
244
|
+
elsif ipv6? then
|
245
|
+
@ipv6_header
|
246
|
+
else
|
247
|
+
raise NotImplementedError
|
248
|
+
end.destination
|
249
|
+
|
250
|
+
destination = resolve destination, resolver
|
251
|
+
|
252
|
+
if tcp? then
|
253
|
+
destination << ".#{@tcp_header.destination_port}"
|
254
|
+
elsif udp? then
|
255
|
+
destination << ".#{@udp_header.destination_port}"
|
256
|
+
end
|
257
|
+
|
258
|
+
destination
|
259
|
+
end
|
260
|
+
|
261
|
+
##
|
262
|
+
# Returns the captured bytes with non-printing characters replaced by "."
|
263
|
+
|
264
|
+
def dump
|
265
|
+
@captured.tr "\000-\037\177-\377", "."
|
266
|
+
end
|
267
|
+
|
268
|
+
##
|
269
|
+
# Dumps the captured packet from +offset+ with offsets, hexadecimal output
|
270
|
+
# for the bytes and the ASCII content with non-printing characters replaced
|
271
|
+
# by "."
|
272
|
+
|
273
|
+
def hexdump offset = 0
|
274
|
+
data = @captured[offset, @capture_length]
|
275
|
+
|
276
|
+
data.scan(/.{,16}/m).map.with_index do |chunk, index|
|
277
|
+
next nil if chunk.empty?
|
278
|
+
hex = chunk.unpack('C*').map { |byte| '%02x' % byte }
|
279
|
+
dump = chunk.tr "\000-\037\177-\377", "."
|
280
|
+
|
281
|
+
length = hex.length
|
282
|
+
hex.fill ' ', length, 16 - length if length < 16
|
283
|
+
|
284
|
+
"\t0x%04x: %s%s %s%s %s%s %s%s %s%s %s%s %s%s %s%s %s" % [
|
285
|
+
index * 16, *hex, dump
|
286
|
+
]
|
287
|
+
end.join "\n"
|
288
|
+
end
|
289
|
+
|
290
|
+
##
|
291
|
+
# The payload of the packet.
|
292
|
+
#
|
293
|
+
# For example, for a UDP packet captured from an Ethernet interface this is
|
294
|
+
# payload after the Ethernet, IP and UDP headers
|
295
|
+
|
296
|
+
def payload
|
297
|
+
@captured[payload_offset, @capture_length]
|
298
|
+
end
|
299
|
+
|
300
|
+
##
|
301
|
+
# The offset into the captured data where the payload starts.
|
302
|
+
#
|
303
|
+
# Note that this method does not work properly for IPv6 packets with options
|
304
|
+
# set, but I have yet to encounter such an example in the wild.
|
305
|
+
|
306
|
+
def payload_offset
|
307
|
+
offset =
|
308
|
+
case @datalink
|
309
|
+
when Capp::DLT_NULL then
|
310
|
+
4
|
311
|
+
when Capp::DLT_EN10MB then
|
312
|
+
14
|
313
|
+
end
|
314
|
+
|
315
|
+
case
|
316
|
+
when ipv4? then offset += @ipv4_header.ihl * 4
|
317
|
+
when ipv6? then offset += 40
|
318
|
+
else raise NotImplementedError
|
319
|
+
end
|
320
|
+
|
321
|
+
case
|
322
|
+
when tcp? then offset += @tcp_header.offset * 4
|
323
|
+
when udp? then offset += 8
|
324
|
+
else raise NotImplementedError
|
325
|
+
end
|
326
|
+
|
327
|
+
offset
|
328
|
+
end
|
329
|
+
|
330
|
+
def resolve address, resolver # :nodoc:
|
331
|
+
return address.dup unless resolver
|
332
|
+
|
333
|
+
if name = ADDRESS_CACHE[address] then
|
334
|
+
return name.dup
|
335
|
+
end
|
336
|
+
|
337
|
+
name = resolver.getname address
|
338
|
+
|
339
|
+
ADDRESS_CACHE[address] = name
|
340
|
+
|
341
|
+
name.dup
|
342
|
+
rescue Resolv::ResolvError
|
343
|
+
ADDRESS_CACHE[address] = address
|
344
|
+
address.dup
|
345
|
+
end
|
346
|
+
|
347
|
+
##
|
348
|
+
# Returns the source of the packet regardless of protocol.
|
349
|
+
#
|
350
|
+
# If a Resolv-compatible +resolver+ is given the name will be looked up.
|
351
|
+
|
352
|
+
def source resolver = nil
|
353
|
+
source =
|
354
|
+
if ipv4? then
|
355
|
+
@ipv4_header
|
356
|
+
elsif ipv6? then
|
357
|
+
@ipv6_header
|
358
|
+
else
|
359
|
+
raise NotImplementedError
|
360
|
+
end.source.dup
|
361
|
+
|
362
|
+
source = resolve source, resolver
|
363
|
+
|
364
|
+
if tcp? then
|
365
|
+
source << ".#{@tcp_header.source_port}"
|
366
|
+
elsif udp? then
|
367
|
+
source << ".#{@udp_header.source_port}"
|
368
|
+
end
|
369
|
+
|
370
|
+
source
|
371
|
+
end
|
372
|
+
|
373
|
+
##
|
374
|
+
# Is this an IPv4 packet?
|
375
|
+
|
376
|
+
def ipv4?
|
377
|
+
@ipv4_header
|
378
|
+
end
|
379
|
+
|
380
|
+
##
|
381
|
+
# Is this an IPv6 packet?
|
382
|
+
|
383
|
+
def ipv6?
|
384
|
+
@ipv6_header
|
385
|
+
end
|
386
|
+
|
387
|
+
##
|
388
|
+
# Is this a TCP packet?
|
389
|
+
|
390
|
+
def tcp?
|
391
|
+
@tcp_header
|
392
|
+
end
|
393
|
+
|
394
|
+
##
|
395
|
+
# Is this a UDP packet?
|
396
|
+
|
397
|
+
def udp?
|
398
|
+
@udp_header
|
399
|
+
end
|
400
|
+
|
401
|
+
end
|
402
|
+
|