caper 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +9 -0
- data/History.rdoc +31 -0
- data/LICENSE.ffi-pcap +23 -0
- data/README.rdoc +55 -0
- data/Rakefile +26 -0
- data/VERSION +1 -0
- data/examples/ipfw_divert.rb +45 -0
- data/examples/print_bytes.rb +17 -0
- data/examples/test_loop.rb +48 -0
- data/lib/caper.rb +40 -0
- data/lib/caper/addr.rb +19 -0
- data/lib/caper/bpf.rb +104 -0
- data/lib/caper/bsd.rb +96 -0
- data/lib/caper/capture_wrapper.rb +287 -0
- data/lib/caper/common_wrapper.rb +173 -0
- data/lib/caper/copy_handler.rb +36 -0
- data/lib/caper/crt.rb +13 -0
- data/lib/caper/data_link.rb +171 -0
- data/lib/caper/dead.rb +35 -0
- data/lib/caper/dumper.rb +53 -0
- data/lib/caper/error_buffer.rb +42 -0
- data/lib/caper/exceptions.rb +19 -0
- data/lib/caper/file_header.rb +24 -0
- data/lib/caper/in_addr.rb +7 -0
- data/lib/caper/interface.rb +27 -0
- data/lib/caper/live.rb +301 -0
- data/lib/caper/offline.rb +51 -0
- data/lib/caper/packet.rb +163 -0
- data/lib/caper/packet_header.rb +23 -0
- data/lib/caper/pcap.rb +250 -0
- data/lib/caper/stat.rb +56 -0
- data/lib/caper/time_val.rb +46 -0
- data/lib/caper/typedefs.rb +25 -0
- data/lib/caper/version.rb +4 -0
- data/spec/data_link_spec.rb +65 -0
- data/spec/dead_spec.rb +34 -0
- data/spec/dumps/http.pcap +0 -0
- data/spec/dumps/simple_tcp.pcap +0 -0
- data/spec/error_buffer_spec.rb +17 -0
- data/spec/file_header_spec.rb +28 -0
- data/spec/live_spec.rb +87 -0
- data/spec/offline_spec.rb +61 -0
- data/spec/packet_behaviors.rb +68 -0
- data/spec/packet_injection_spec.rb +38 -0
- data/spec/packet_spec.rb +111 -0
- data/spec/pcap_spec.rb +149 -0
- data/spec/spec_helper.rb +31 -0
- data/spec/wrapper_behaviors.rb +110 -0
- data/tasks/rcov.rb +6 -0
- data/tasks/rdoc.rb +17 -0
- data/tasks/spec.rb +9 -0
- data/tasks/yard.rb +16 -0
- metadata +140 -0
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'caper/capture_wrapper'
|
2
|
+
|
3
|
+
module Caper
|
4
|
+
# A wrapper class for pcap devices opened with open_offline()
|
5
|
+
#
|
6
|
+
class Offline < CaptureWrapper
|
7
|
+
attr_accessor :path
|
8
|
+
|
9
|
+
# Creates a pcap interface for reading saved capture files.
|
10
|
+
#
|
11
|
+
# @param [String] path
|
12
|
+
# The path to the file to open.
|
13
|
+
#
|
14
|
+
# @param [Hash] opts
|
15
|
+
# Options are ignored and passed to the super-class except for those
|
16
|
+
# below.
|
17
|
+
#
|
18
|
+
# @option opts [ignored] :path
|
19
|
+
# The :path option will be overridden with the value of the path
|
20
|
+
# argument. If specified in opts, its value will be ignored.
|
21
|
+
#
|
22
|
+
# @return [Offline]
|
23
|
+
# A Caper::Offline wrapper.
|
24
|
+
#
|
25
|
+
# @raise [LibError]
|
26
|
+
# On failure, an exception is raised with the relevant error
|
27
|
+
# message from libpcap.
|
28
|
+
#
|
29
|
+
def initialize(path, opts={}, &block)
|
30
|
+
@path = path
|
31
|
+
@errbuf = ErrorBuffer.create()
|
32
|
+
@pcap = Caper.pcap_open_offline(File.expand_path(@path), @errbuf)
|
33
|
+
raise(LibError, "pcap_open_offline(): #{@errbuf.to_s}") if @pcap.null?
|
34
|
+
super(@pcap, opts, &block)
|
35
|
+
end
|
36
|
+
|
37
|
+
def swapped?
|
38
|
+
Caper.pcap_is_swapped(_pcap) == 1 ? true : false
|
39
|
+
end
|
40
|
+
|
41
|
+
def file_version
|
42
|
+
"#{Caper.pcap_major_version(_pcap)}.#{Caper.pcap_minor_version(_pcap)}"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
attach_function :pcap_open_offline, [:string, :pointer], :pcap_t
|
47
|
+
attach_function :pcap_is_swapped, [:pcap_t], :int
|
48
|
+
attach_function :pcap_major_version, [:pcap_t], :int
|
49
|
+
attach_function :pcap_minor_version, [:pcap_t], :int
|
50
|
+
|
51
|
+
end
|
data/lib/caper/packet.rb
ADDED
@@ -0,0 +1,163 @@
|
|
1
|
+
|
2
|
+
module Caper
|
3
|
+
class Packet
|
4
|
+
attr_reader :body_ptr, :header
|
5
|
+
|
6
|
+
# Creates a Packet from a Ruby string object.
|
7
|
+
#
|
8
|
+
# see new() for more information about the arguments.
|
9
|
+
def self.from_string(body, opts={})
|
10
|
+
new(nil, body, opts)
|
11
|
+
end
|
12
|
+
|
13
|
+
# Allocates a Packet using new memory. Used primarily for pcap_loop
|
14
|
+
# and pcap_dispatch to retain packets after new ones have been received
|
15
|
+
# or a pcap device is closed.
|
16
|
+
def self.allocate(phdr, buf)
|
17
|
+
new(phdr, buf).copy()
|
18
|
+
end
|
19
|
+
|
20
|
+
# @param [PacketHeader, nil] hdr
|
21
|
+
# The pcap pkthdr struct for this packet or nil. hdr may only be
|
22
|
+
# nil if a string is supplied for the body. A header will be
|
23
|
+
# created automatically and set_body called with opts.
|
24
|
+
#
|
25
|
+
# @param [FFI::Pointer, String] body
|
26
|
+
# A string or pointer for the body of the packet. A String may
|
27
|
+
# only be specified if hdr is set to nil.
|
28
|
+
#
|
29
|
+
# @param [optional, Hash] opts
|
30
|
+
# Specifies additional options at creation time. Only those
|
31
|
+
# below are applicable for all initiatialization styles.
|
32
|
+
# All other options are sent to set_body(), but only if the header
|
33
|
+
# is nil and body is a String. See set_body() for more info.
|
34
|
+
#
|
35
|
+
# @option opts [optional, Time] :time, :timestamp
|
36
|
+
# Sets the timestamp in the header.
|
37
|
+
#
|
38
|
+
# @raise [ArgumentError, TypeError]
|
39
|
+
# An exception is raised if any of the parameter rules described
|
40
|
+
# are not followed.
|
41
|
+
#
|
42
|
+
def initialize(hdr, body, opts={})
|
43
|
+
o = opts.dup
|
44
|
+
ts = o.delete(:time) || o.delete(:timestamp)
|
45
|
+
case hdr
|
46
|
+
when PacketHeader
|
47
|
+
raise(ArgumentError, "NULL header pointer") if hdr.to_ptr.null?
|
48
|
+
@header = hdr
|
49
|
+
when ::FFI::Pointer
|
50
|
+
raise(ArgumentError, "NULL header pointer") if hdr.null?
|
51
|
+
@header = PacketHeader.new(hdr)
|
52
|
+
when nil
|
53
|
+
if body.is_a? String
|
54
|
+
set_body(body, o)
|
55
|
+
else
|
56
|
+
raise(TypeError, "invalid body with nil header: #{body.class}")
|
57
|
+
end
|
58
|
+
else
|
59
|
+
raise(TypeError, "invalid header: #{hdr.class}")
|
60
|
+
end
|
61
|
+
|
62
|
+
@header.ts.time = ts if ts
|
63
|
+
|
64
|
+
unless @body_ptr
|
65
|
+
if body.is_a?(FFI::Pointer) and not body.null?
|
66
|
+
@body_ptr = body
|
67
|
+
else
|
68
|
+
raise(TypeError, "invalid body for header: #{body.class}")
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# Sets the body from a string. A pointer is automatically derived from
|
74
|
+
#
|
75
|
+
# @param [String] data
|
76
|
+
# The body to set
|
77
|
+
#
|
78
|
+
# @param [Hash] opts
|
79
|
+
# Body length options.
|
80
|
+
#
|
81
|
+
# @option opts [optional, Integer] :caplen, :captured
|
82
|
+
# The captured length (or snaplen) for this packet.
|
83
|
+
# Length of data portion present. Defaults to body.size(). If
|
84
|
+
# caplen is larger than the body, then it is overridden with body.size.
|
85
|
+
#
|
86
|
+
# @option opts [optional, Integer] :len, :length
|
87
|
+
# The total length of the packet (off wire). Defaults to caplen. If
|
88
|
+
# If :length is less than the :caplen, it is overridden as :caplen.
|
89
|
+
#
|
90
|
+
#
|
91
|
+
# @return [String]
|
92
|
+
# Returns the data as supplied per attr_writer convention.
|
93
|
+
#
|
94
|
+
def set_body(data, opts={})
|
95
|
+
cl = opts[:caplen] || opts[:captured] || data.size
|
96
|
+
l = opts[:length] || opts[:len] || cl
|
97
|
+
clen = (cl < data.size) ? cl : data.size
|
98
|
+
len = (l < clen) ? clen : l
|
99
|
+
|
100
|
+
@header ||= PacketHeader.new
|
101
|
+
@header.caplen = len || @header.caplen
|
102
|
+
@header.len = len || @header.caplen
|
103
|
+
@body_ptr = FFI::MemoryPointer.from_string(data)
|
104
|
+
return self
|
105
|
+
end
|
106
|
+
|
107
|
+
alias body= set_body
|
108
|
+
|
109
|
+
# @return [String]
|
110
|
+
# A String representation of the packet data.
|
111
|
+
# The reference to the string is not kept by the object and changes
|
112
|
+
# won't affect the data in this packet.
|
113
|
+
def body
|
114
|
+
@body_ptr.read_string(@header.caplen)
|
115
|
+
end
|
116
|
+
|
117
|
+
# @return [Time]
|
118
|
+
# Returns the pcap timestamp as a Time object
|
119
|
+
def time
|
120
|
+
@header.ts.time
|
121
|
+
end
|
122
|
+
|
123
|
+
alias timestamp time
|
124
|
+
|
125
|
+
# Sets the pcap timestamp.
|
126
|
+
def time=(t)
|
127
|
+
@header.ts.time=(t)
|
128
|
+
end
|
129
|
+
|
130
|
+
def caplen
|
131
|
+
@header.caplen
|
132
|
+
end
|
133
|
+
|
134
|
+
alias captured caplen
|
135
|
+
|
136
|
+
def len
|
137
|
+
@header.len
|
138
|
+
end
|
139
|
+
|
140
|
+
alias length len
|
141
|
+
|
142
|
+
# An optimized copy which allocates new memory for a PacketHeader and
|
143
|
+
# body.
|
144
|
+
#
|
145
|
+
# DANGEROUS: This method uses direct FFI bindings for the copy and
|
146
|
+
# may crash Ruby if the packet header or body is incorrect.
|
147
|
+
#
|
148
|
+
# @raise [StandardError]
|
149
|
+
# An exception is raised if the header or body is a NULL pointer.
|
150
|
+
#
|
151
|
+
def copy
|
152
|
+
raise(StandardError, "header is a NULL pointer") if @header.to_ptr.null?
|
153
|
+
raise(StandardError, "body is a NULL pointer") if body_ptr.null?
|
154
|
+
cpy_hdr = PacketHeader.new
|
155
|
+
cpy_buf = FFI::MemoryPointer.new(@header.caplen)
|
156
|
+
CRT.memcpy(cpy_hdr, @header, PacketHeader.size)
|
157
|
+
CRT.memcpy(cpy_buf, @body_ptr, @header.caplen)
|
158
|
+
self.class.new( cpy_hdr, cpy_buf )
|
159
|
+
end
|
160
|
+
|
161
|
+
end
|
162
|
+
|
163
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Caper
|
2
|
+
|
3
|
+
# Generic per-packet information, as supplied by libpcap. This structure
|
4
|
+
# is used to track only the libpcap header and just contains the timestamp
|
5
|
+
# and length information used by libpcap.
|
6
|
+
#
|
7
|
+
# See pcap_pkthdr struct in pcap.h
|
8
|
+
class PacketHeader < FFI::Struct
|
9
|
+
include FFI::DRY::StructHelper
|
10
|
+
|
11
|
+
dsl_layout do
|
12
|
+
struct :ts, ::Caper::TimeVal, :desc => 'time stamp'
|
13
|
+
field :caplen, :bpf_uint32, :desc => 'length of portion present'
|
14
|
+
field :len, :bpf_uint32, :desc => 'length of packet (off wire)'
|
15
|
+
end
|
16
|
+
|
17
|
+
alias timestamp ts
|
18
|
+
alias captured caplen
|
19
|
+
alias length len
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
data/lib/caper/pcap.rb
ADDED
@@ -0,0 +1,250 @@
|
|
1
|
+
require 'enumerator'
|
2
|
+
|
3
|
+
module Caper
|
4
|
+
DEFAULT_SNAPLEN = 65535 # Default snapshot length for packets
|
5
|
+
|
6
|
+
attach_function :pcap_lookupdev, [:pointer], :string
|
7
|
+
|
8
|
+
# Find the default device on which to capture.
|
9
|
+
#
|
10
|
+
# @return [String]
|
11
|
+
# Name of default device
|
12
|
+
#
|
13
|
+
# @raise [LibError]
|
14
|
+
# On failure, an exception is raised with the relevant error
|
15
|
+
# message from libpcap.
|
16
|
+
#
|
17
|
+
def Caper.lookupdev
|
18
|
+
e = ErrorBuffer.create()
|
19
|
+
unless name = Caper.pcap_lookupdev(e)
|
20
|
+
raise(LibError, "pcap_lookupdev(): #{e.to_s}")
|
21
|
+
end
|
22
|
+
return name
|
23
|
+
end
|
24
|
+
|
25
|
+
|
26
|
+
attach_function :pcap_lookupnet, [:string, :pointer, :pointer, :pointer], :int
|
27
|
+
|
28
|
+
# Determine the IPv4 network number and mask relevant with a network
|
29
|
+
# device.
|
30
|
+
#
|
31
|
+
# @param [String] device
|
32
|
+
# The name of the device to look up.
|
33
|
+
#
|
34
|
+
# @yield [netp, maskp]
|
35
|
+
#
|
36
|
+
# @yieldparam [FFI::MemoryPointer] netp
|
37
|
+
# A pointer to the network return value.
|
38
|
+
#
|
39
|
+
# @yieldparam [FFI::MemoryPointer] maskp
|
40
|
+
# A pointer to the netmask return value.
|
41
|
+
#
|
42
|
+
# @return [nil, String]
|
43
|
+
# The IPv4 network number and mask presented as "n.n.n.n/m.m.m.m".
|
44
|
+
# nil is returned when a block is specified.
|
45
|
+
#
|
46
|
+
# @raise [LibError]
|
47
|
+
# On failure, an exception is raised with the relevant error message
|
48
|
+
# from libpcap.
|
49
|
+
#
|
50
|
+
def Caper.lookupnet(device)
|
51
|
+
netp = FFI::MemoryPointer.new(find_type(:bpf_uint32))
|
52
|
+
maskp = FFI::MemoryPointer.new(find_type(:bpf_uint32))
|
53
|
+
errbuf = ErrorBuffer.create()
|
54
|
+
unless Caper.pcap_lookupnet(device, netp, maskp, errbuf) == 0
|
55
|
+
raise(LibError, "pcap_lookupnet(): #{errbuf.to_s}")
|
56
|
+
end
|
57
|
+
if block_given?
|
58
|
+
yield(netp, maskp)
|
59
|
+
else
|
60
|
+
return( netp.get_array_of_uchar(0,4).join('.') << "/" <<
|
61
|
+
maskp.get_array_of_uchar(0,4).join('.') )
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
|
66
|
+
# Opens a new Live device for capturing from the network. See Live.new()
|
67
|
+
# for arguments.
|
68
|
+
#
|
69
|
+
# If passed a block, the block is passed to Live.new() and the Live
|
70
|
+
# object is closed after completion of the block
|
71
|
+
def Caper.open_live(opts={},&block)
|
72
|
+
ret = Live.new(opts, &block)
|
73
|
+
return block_given? ? ret.close : ret
|
74
|
+
end
|
75
|
+
|
76
|
+
|
77
|
+
# Opens a new Dead pcap interface for compiling filters or opening
|
78
|
+
# a capture for output. See Dead.new() for arguments.
|
79
|
+
def Caper.open_dead(opts={}, &block)
|
80
|
+
ret = Dead.new(opts, &block)
|
81
|
+
return block_given? ? ret.close : ret
|
82
|
+
end
|
83
|
+
|
84
|
+
|
85
|
+
# Opens a saved capture file for reading. See Offline.new for arguments.
|
86
|
+
def Caper.open_offline(path, opts={}, &block)
|
87
|
+
ret = Offline.new(path, opts={}, &block)
|
88
|
+
return block_given? ? ret.close : ret
|
89
|
+
end
|
90
|
+
|
91
|
+
# Same as open_offline
|
92
|
+
def Caper.open_file(path, opts={}, &block)
|
93
|
+
open_offline(path, opts, &block)
|
94
|
+
end
|
95
|
+
|
96
|
+
attach_function :pcap_findalldevs, [:pointer, :pointer], :int
|
97
|
+
attach_function :pcap_freealldevs, [Interface], :void
|
98
|
+
|
99
|
+
# List all capture devices and yield them each to a block
|
100
|
+
#
|
101
|
+
# @yield [dev]
|
102
|
+
#
|
103
|
+
# @yieldparam [Interface] dev
|
104
|
+
# An Interface structure for each device.
|
105
|
+
#
|
106
|
+
# @return [nil]
|
107
|
+
#
|
108
|
+
# @raise [LibError]
|
109
|
+
# On failure, an exception is raised with the relevant error
|
110
|
+
# message from libpcap.
|
111
|
+
#
|
112
|
+
def Caper.each_device
|
113
|
+
devices = ::FFI::MemoryPointer.new(:pointer)
|
114
|
+
errbuf = ErrorBuffer.create()
|
115
|
+
|
116
|
+
Caper.pcap_findalldevs(devices, errbuf)
|
117
|
+
node = devices.get_pointer(0)
|
118
|
+
|
119
|
+
if node.null?
|
120
|
+
raise(LibError, "pcap_findalldevs(): #{errbuf.to_s}")
|
121
|
+
end
|
122
|
+
|
123
|
+
device = Interface.new(node)
|
124
|
+
|
125
|
+
while device
|
126
|
+
yield(device)
|
127
|
+
device = device.next
|
128
|
+
end
|
129
|
+
|
130
|
+
Caper.pcap_freealldevs(node)
|
131
|
+
return nil
|
132
|
+
end
|
133
|
+
|
134
|
+
|
135
|
+
# Returns an array of device name and network/netmask pairs for
|
136
|
+
# each interface found on the system.
|
137
|
+
#
|
138
|
+
# If an interface does not have an address assigned, its network/netmask
|
139
|
+
# value is returned as a nil value.
|
140
|
+
def Caper.dump_devices
|
141
|
+
Caper.enum_for(:each_device).map do |dev|
|
142
|
+
net = begin; Caper.lookupnet(dev.name); rescue LibError; end
|
143
|
+
[dev.name, net]
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
|
148
|
+
# Returns an array of device names for each interface found on the system.
|
149
|
+
def Caper.device_names
|
150
|
+
Caper.enum_for(:each_device).map {|dev| dev.name }
|
151
|
+
end
|
152
|
+
|
153
|
+
attach_function :pcap_lib_version, [], :string
|
154
|
+
|
155
|
+
|
156
|
+
# Get the version information for libpcap.
|
157
|
+
#
|
158
|
+
# @return [String]
|
159
|
+
# Information about the version of the libpcap library being used;
|
160
|
+
# note that it contains more information than just a version number.
|
161
|
+
#
|
162
|
+
def Caper.lib_version
|
163
|
+
Caper.pcap_lib_version
|
164
|
+
end
|
165
|
+
|
166
|
+
|
167
|
+
# Extract just the version number from the lib_version string.
|
168
|
+
#
|
169
|
+
# @return [String]
|
170
|
+
# Version number.
|
171
|
+
#
|
172
|
+
def Caper.lib_version_number
|
173
|
+
if lib_version() =~ /libpcap version (\d+\.\d+.\d+)/
|
174
|
+
return $1
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
|
179
|
+
# Unix Only:
|
180
|
+
begin
|
181
|
+
|
182
|
+
attach_function :pcap_get_selectable_fd, [:pcap_t], :int
|
183
|
+
|
184
|
+
|
185
|
+
# Drops privileges back to the uid of the SUDO_USER environment
|
186
|
+
# variable.
|
187
|
+
#
|
188
|
+
# Only available on Unix.
|
189
|
+
#
|
190
|
+
# This is useful for the paranoid when sudo is used to run a
|
191
|
+
# ruby pcap program as root.
|
192
|
+
#
|
193
|
+
# This method can generally be called right after a call to
|
194
|
+
# open_live() has returned a pcap handle or another privileged
|
195
|
+
# call has completed. Note, however, that once privileges are
|
196
|
+
# dropped, pcap functions that a require higher privilege will
|
197
|
+
# no longer work.
|
198
|
+
#
|
199
|
+
# @raise [StandardError]
|
200
|
+
# An error is raised if privileges cannot be dropped for
|
201
|
+
# some reason. This may be because the SUDO_USER environment
|
202
|
+
# variable is not set, because we already have a lower
|
203
|
+
# privilige and the SUDO_USER id is not the current uid,
|
204
|
+
# or because the SUDO_USER environment variable is not
|
205
|
+
# a valid user.
|
206
|
+
#
|
207
|
+
def Caper.drop_sudo_privs
|
208
|
+
if ENV["SUDO_USER"] and pwent=Etc.getpwnam(ENV["SUDO_USER"])
|
209
|
+
Process::Sys.setgid(pwent.gid)
|
210
|
+
Process::Sys.setegid(pwent.gid)
|
211
|
+
Process::Sys.setuid(pwent.uid)
|
212
|
+
Process::Sys.seteuid(pwent.uid)
|
213
|
+
return true if (
|
214
|
+
Process::Sys.getuid == pwent.uid and
|
215
|
+
Process::Sys.geteuid == pwent.uid and
|
216
|
+
Process::Sys.getgid == pwent.gid and
|
217
|
+
Process::Sys.getegid == pwent.gid )
|
218
|
+
end
|
219
|
+
raise(StandardError, "Unable to drop privileges")
|
220
|
+
end
|
221
|
+
|
222
|
+
rescue FFI::NotFoundError
|
223
|
+
$pcap_not_unix=true
|
224
|
+
end
|
225
|
+
|
226
|
+
# Win32 only:
|
227
|
+
begin
|
228
|
+
attach_function :pcap_setbuff, [:pcap_t, :int], :int
|
229
|
+
attach_function :pcap_setmode, [:pcap_t, :pcap_w32_modes_enum], :int
|
230
|
+
attach_function :pcap_setmintocopy, [:pcap_t, :int], :int
|
231
|
+
rescue FFI::NotFoundError
|
232
|
+
$pcap_not_win32=true
|
233
|
+
end if $pcap_not_unix
|
234
|
+
|
235
|
+
# MSDOS only???:
|
236
|
+
begin
|
237
|
+
attach_function :pcap_stats_ex, [:pcap_t, StatEx], :int
|
238
|
+
attach_function :pcap_set_wait, [:pcap_t, :pointer, :int], :void
|
239
|
+
attach_function :pcap_mac_packets, [], :ulong
|
240
|
+
rescue FFI::NotFoundError
|
241
|
+
end if $pcap_not_win32
|
242
|
+
|
243
|
+
|
244
|
+
#### XXX not sure if we even want FILE io stuff yet (or ever).
|
245
|
+
|
246
|
+
#attach_function :pcap_fopen_offline, [:FILE, :pointer], :pcap_t
|
247
|
+
#attach_function :pcap_file, [:pcap_t], :FILE
|
248
|
+
#attach_function :pcap_dump_fopen, [:pcap_t, :FILE], :pcap_dumper_t
|
249
|
+
#attach_function :pcap_fileno, [:pcap_t], :int
|
250
|
+
end
|