ffi-pcap 0.2.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.
- data/.gitignore +10 -0
- data/ChangeLog.rdoc +27 -0
- data/LICENSE.txt +23 -0
- data/README.rdoc +30 -0
- data/Rakefile +26 -0
- data/VERSION +1 -0
- data/examples/ipfw_divert.rb +49 -0
- data/examples/print_bytes.rb +17 -0
- data/lib/ffi-pcap.rb +1 -0
- data/lib/ffi/pcap.rb +42 -0
- data/lib/ffi/pcap/addr.rb +21 -0
- data/lib/ffi/pcap/bpf.rb +106 -0
- data/lib/ffi/pcap/bsd.rb +98 -0
- data/lib/ffi/pcap/capture_wrapper.rb +289 -0
- data/lib/ffi/pcap/common_wrapper.rb +175 -0
- data/lib/ffi/pcap/copy_handler.rb +38 -0
- data/lib/ffi/pcap/crt.rb +15 -0
- data/lib/ffi/pcap/data_link.rb +173 -0
- data/lib/ffi/pcap/dead.rb +37 -0
- data/lib/ffi/pcap/dumper.rb +55 -0
- data/lib/ffi/pcap/error_buffer.rb +44 -0
- data/lib/ffi/pcap/exceptions.rb +21 -0
- data/lib/ffi/pcap/file_header.rb +26 -0
- data/lib/ffi/pcap/in_addr.rb +9 -0
- data/lib/ffi/pcap/interface.rb +29 -0
- data/lib/ffi/pcap/live.rb +303 -0
- data/lib/ffi/pcap/offline.rb +53 -0
- data/lib/ffi/pcap/packet.rb +164 -0
- data/lib/ffi/pcap/packet_header.rb +24 -0
- data/lib/ffi/pcap/pcap.rb +252 -0
- data/lib/ffi/pcap/stat.rb +57 -0
- data/lib/ffi/pcap/time_val.rb +48 -0
- data/lib/ffi/pcap/typedefs.rb +27 -0
- data/lib/ffi/pcap/version.rb +6 -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 +124 -0
- data/tasks/rcov.rb +6 -0
- data/tasks/rdoc.rb +17 -0
- data/tasks/spec.rb +9 -0
- data/tasks/yard.rb +21 -0
- metadata +157 -0
@@ -0,0 +1,303 @@
|
|
1
|
+
require 'ffi/pcap/capture_wrapper'
|
2
|
+
|
3
|
+
module FFI
|
4
|
+
module PCap
|
5
|
+
begin
|
6
|
+
attach_function :pcap_setdirection, [:pcap_t, :pcap_direction_t], :int
|
7
|
+
rescue FFI::NotFoundError
|
8
|
+
end
|
9
|
+
|
10
|
+
begin
|
11
|
+
attach_function :pcap_sendpacket, [:pcap_t, :pointer, :int], :int
|
12
|
+
rescue FFI::NotFoundError
|
13
|
+
end
|
14
|
+
|
15
|
+
begin
|
16
|
+
attach_function :pcap_inject, [:pcap_t, :pointer, :int], :int
|
17
|
+
rescue FFI::NotFoundError
|
18
|
+
end
|
19
|
+
|
20
|
+
|
21
|
+
# Creates a pcap interface for capturing from the network.
|
22
|
+
#
|
23
|
+
# @param [Hash] opts
|
24
|
+
# Options are ignored and passed to the super-class except those below.
|
25
|
+
#
|
26
|
+
# @option opts [String, nil] :device, :dev
|
27
|
+
# The device to open. On some platforms, this can be "any". If nil or
|
28
|
+
# unspecified FFI::PCap.lookupdev() is called to obtain a default device.
|
29
|
+
#
|
30
|
+
# @option opts [Integer] :snaplen
|
31
|
+
# The snapshot length for the pcap object. Defaults to DEFAULT_SNAPLEN
|
32
|
+
#
|
33
|
+
# @option opts [Boolean] :promisc
|
34
|
+
# Specifies if the interface is to be put into promiscuous mode. Defaults
|
35
|
+
# to false.
|
36
|
+
#
|
37
|
+
# @option opts [Integer] :timeout
|
38
|
+
# Specifies the read timeout in milliseconds. Defaults to DEFAULT_TO_MS
|
39
|
+
#
|
40
|
+
# @return [Live]
|
41
|
+
# A FFI::PCap::Live wrapper.
|
42
|
+
#
|
43
|
+
# @raise [LibError]
|
44
|
+
# On failure, an exception is raised with the relevant error
|
45
|
+
# message from libpcap.
|
46
|
+
#
|
47
|
+
# @raise [ArgumentError]
|
48
|
+
# May raise an exception if a :device cannot be autodetected using
|
49
|
+
# FFI::PCap.lookupdev() for any reason. This should never happen on most platforms.
|
50
|
+
#
|
51
|
+
class Live < CaptureWrapper
|
52
|
+
DEFAULT_TO_MS = 1000 # Default timeout for pcap_open_live()
|
53
|
+
|
54
|
+
attr_reader :device, :promisc, :timeout, :direction
|
55
|
+
|
56
|
+
def initialize(opts=nil)
|
57
|
+
opts ||= {}
|
58
|
+
@device = opts[:device] || opts[:dev] || FFI::PCap.lookupdev()
|
59
|
+
unless @device
|
60
|
+
raise(ArgumentError, "Couldn't detect a device. One must be specified.")
|
61
|
+
end
|
62
|
+
|
63
|
+
@snaplen = opts[:snaplen] || DEFAULT_SNAPLEN
|
64
|
+
@promisc = opts[:promisc] ? 1 : 0
|
65
|
+
@timeout = opts[:timeout] || DEFAULT_TO_MS
|
66
|
+
@direction = (opts[:direction] || opts[:dir])
|
67
|
+
|
68
|
+
@errbuf = ErrorBuffer.create()
|
69
|
+
@pcap = FFI::PCap.pcap_open_live(@device, @snaplen, @promisc, @timeout, @errbuf)
|
70
|
+
raise(LibError, "pcap_open_live(): #{@errbuf.to_s}") if @pcap.null?
|
71
|
+
|
72
|
+
# call super to get all our ducks in a row
|
73
|
+
super(@pcap, opts)
|
74
|
+
|
75
|
+
set_direction(@direction) if @direction
|
76
|
+
|
77
|
+
# Cache network and netmask from pcap_lookupdev.
|
78
|
+
# These pointers may be used internally (and should get autoreleased)
|
79
|
+
@netp, @maskp = nil
|
80
|
+
begin
|
81
|
+
FFI::PCap.lookupnet(@device) do |netp, maskp|
|
82
|
+
@netp = netp
|
83
|
+
@maskp = maskp
|
84
|
+
end
|
85
|
+
rescue LibError
|
86
|
+
warn "Warning: #{$!}"
|
87
|
+
end
|
88
|
+
|
89
|
+
yield self if block_given?
|
90
|
+
end
|
91
|
+
|
92
|
+
# Returns the dotted notation string for the IPv4 network address for
|
93
|
+
# the device used by this pcap interface.
|
94
|
+
def network
|
95
|
+
return nil unless @netp
|
96
|
+
@network ||= @netp.get_array_of_uchar(0,4).join('.')
|
97
|
+
end
|
98
|
+
|
99
|
+
# Returns the dotted notation string for the IPv4 netmask for the device
|
100
|
+
# used by this pcap interface.
|
101
|
+
def netmask
|
102
|
+
return nil unless @maskp
|
103
|
+
@netmask ||= @maskp.get_array_of_uchar(0,4).join('.')
|
104
|
+
end
|
105
|
+
|
106
|
+
# Returns the 32-bit numeric representation of the IPv4 network address
|
107
|
+
# for this device.
|
108
|
+
def network_n32
|
109
|
+
return nil unless @netp
|
110
|
+
::FFI::DRY::NetEndian.ntohl(@netp.get_uint32(0))
|
111
|
+
end
|
112
|
+
|
113
|
+
# Returns the 32-bit numeric representation of the IPv4 network address
|
114
|
+
# for this device.
|
115
|
+
def netmask_n32
|
116
|
+
return nil unless @maskp
|
117
|
+
::FFI::DRY::NetEndian.ntohl(@maskp.get_uint32(0))
|
118
|
+
end
|
119
|
+
|
120
|
+
@@have_setdirection = FFI::PCap.respond_to?(:pcap_setdirection)
|
121
|
+
|
122
|
+
# Sets the direction for which packets will be captured.
|
123
|
+
#
|
124
|
+
# (Not supported on all platforms)
|
125
|
+
def set_direction(dir)
|
126
|
+
unless @@have_setdirection
|
127
|
+
raise(NotImplementedError,
|
128
|
+
"pcap_setdirection() is not avaiable from your pcap library")
|
129
|
+
end
|
130
|
+
|
131
|
+
dirs = FFI::PCap.enum_type(:pcap_direction_t)
|
132
|
+
if FFI::PCap.pcap_setdirection(_pcap, dirs[:"pcap_d_#{dir}"]) == 0
|
133
|
+
return true
|
134
|
+
else
|
135
|
+
raise(LibError, "pcap_setdirection(): #{geterr()}", caller)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
alias direction= set_direction
|
140
|
+
|
141
|
+
# set the state of non-blocking mode on a capture device
|
142
|
+
#
|
143
|
+
# @param [Boolean] mode
|
144
|
+
#
|
145
|
+
# @raise [LibError]
|
146
|
+
# On failure, an exception is raised with the relevant error message
|
147
|
+
# from libpcap.
|
148
|
+
#
|
149
|
+
def set_non_blocking(mode)
|
150
|
+
mode = mode ? 1 : 0
|
151
|
+
if FFI::PCap.pcap_setnonblock(_pcap, mode, @errbuf) == 0
|
152
|
+
return mode == 1
|
153
|
+
else
|
154
|
+
raise(LibError, "pcap_setnonblock(): #{@errbuf.to_s}", caller)
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
alias non_blocking= set_non_blocking
|
159
|
+
|
160
|
+
# get the state of non-blocking mode on a capture device
|
161
|
+
#
|
162
|
+
# @return [Boolean]
|
163
|
+
# non-blocking mode
|
164
|
+
#
|
165
|
+
# @raise [LibError]
|
166
|
+
# On failure, an exception is raised with the relevant error message
|
167
|
+
# from libpcap.
|
168
|
+
#
|
169
|
+
def non_blocking
|
170
|
+
if (mode=FFI::PCap.pcap_getnonblock(_pcap, @errbuf)) == -1
|
171
|
+
raise(LibError, "pcap_getnonblock(): #{@errbuf.to_s}", caller)
|
172
|
+
else
|
173
|
+
return mode == 1
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
alias non_blocking? non_blocking
|
178
|
+
|
179
|
+
# Get capture statistics
|
180
|
+
#
|
181
|
+
# @return [Stats]
|
182
|
+
#
|
183
|
+
# @raise [LibError]
|
184
|
+
# On failure, an exception is raised with the relevant error message
|
185
|
+
# from libpcap.
|
186
|
+
#
|
187
|
+
def stats
|
188
|
+
stats = Stat.new
|
189
|
+
unless FFI::PCap.pcap_stats(_pcap, stats) == 0
|
190
|
+
raise(LibError, "pcap_stats(): #{geterr()}")
|
191
|
+
end
|
192
|
+
return stats
|
193
|
+
end
|
194
|
+
|
195
|
+
|
196
|
+
@@have_inject = FFI::PCap.respond_to?(:pcap_inject)
|
197
|
+
|
198
|
+
# Transmit a packet using pcap_inject()
|
199
|
+
#
|
200
|
+
# (not available on all platforms)
|
201
|
+
#
|
202
|
+
# @param [Packet, String] obj
|
203
|
+
# The packet to send. This can be a Packet or String object.
|
204
|
+
#
|
205
|
+
# @return [Integer]
|
206
|
+
# The number of bytes sent.
|
207
|
+
#
|
208
|
+
# @raise [ArgumentError]
|
209
|
+
# An exception is raised if the pkt object type is incorrect or
|
210
|
+
# if it is a Packet and the body pointer is null.
|
211
|
+
#
|
212
|
+
# @raise [LibError]
|
213
|
+
# On failure, an exception is raised with the relevant libpcap
|
214
|
+
# error message.
|
215
|
+
#
|
216
|
+
# @raise [NotImplementedError]
|
217
|
+
# If the pcap_inject() function is not available from your libpcap
|
218
|
+
# library pcap_sendpacket will be tried, if both are missing, this
|
219
|
+
# exception will be raised.
|
220
|
+
#
|
221
|
+
def inject(pkt)
|
222
|
+
if @@have_inject
|
223
|
+
if pkt.kind_of? Packet
|
224
|
+
len = pkt.caplen
|
225
|
+
bufp = pkt.body_ptr
|
226
|
+
raise(ArgumentError, "packet data null pointer") if bufp.null?
|
227
|
+
elsif pkt.kind_of? String
|
228
|
+
len = pkt.size
|
229
|
+
bufp = FFI::MemoryPointer.from_string(pkt)
|
230
|
+
else
|
231
|
+
raise(ArgumentError, "Don't know how to inject #{pkt.class}")
|
232
|
+
end
|
233
|
+
|
234
|
+
if (sent=FFI::PCap.pcap_inject(_pcap, bufp, len)) < 0
|
235
|
+
raise(LibError, "pcap_inject(): #{geterr()}")
|
236
|
+
end
|
237
|
+
return sent
|
238
|
+
else
|
239
|
+
# fake it with sendpacket on windows
|
240
|
+
if sendpacket(pkt)
|
241
|
+
return (Packet === pkt)? pkt.caplen : pkt.size
|
242
|
+
end
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
@@have_sendpacket = FFI::PCap.respond_to?(:pcap_sendpacket)
|
247
|
+
|
248
|
+
# Transmit a packet using pcap_sendpacket()
|
249
|
+
#
|
250
|
+
# (not available on all platforms)
|
251
|
+
#
|
252
|
+
# @param [Packet, String] obj
|
253
|
+
# The packet to send. This can be a Packet or String object.
|
254
|
+
#
|
255
|
+
# @return [True]
|
256
|
+
# True is returned on success. Otherwise an exception is raised.
|
257
|
+
#
|
258
|
+
# @raise [ArgumentError]
|
259
|
+
# An exception is raised if the pkt object type is incorrect or
|
260
|
+
# if it is a Packet and the body pointer is null.
|
261
|
+
#
|
262
|
+
# @raise [LibError]
|
263
|
+
# On failure, an exception is raised with the relevant libpcap
|
264
|
+
# error message.
|
265
|
+
#
|
266
|
+
# @raise [NotImplementedError]
|
267
|
+
# If the pcap_sendpacket() function is not available from your libpcap
|
268
|
+
# library this exception will be raised.
|
269
|
+
#
|
270
|
+
def sendpacket(pkt)
|
271
|
+
unless @@have_sendpacket
|
272
|
+
raise(NotImplementedError,
|
273
|
+
"packet injectors are not avaiable from your pcap library")
|
274
|
+
end
|
275
|
+
|
276
|
+
if pkt.kind_of? Packet
|
277
|
+
len = pkt.caplen
|
278
|
+
bufp = pkt.body_ptr
|
279
|
+
raise(ArgumentError, "packet data null pointer") if bufp.null?
|
280
|
+
elsif pkt.kind_of? String
|
281
|
+
len = pkt.size
|
282
|
+
bufp = FFI::MemoryPointer.from_string(pkt)
|
283
|
+
else
|
284
|
+
raise(ArgumentError, "Don't know how to send #{pkt.class}")
|
285
|
+
end
|
286
|
+
|
287
|
+
if FFI::PCap.pcap_sendpacket(_pcap, bufp, len) != 0
|
288
|
+
raise(LibError, "pcap_sendpacket(): #{geterr()}")
|
289
|
+
end
|
290
|
+
return true
|
291
|
+
end
|
292
|
+
|
293
|
+
alias send_packet sendpacket
|
294
|
+
|
295
|
+
end
|
296
|
+
|
297
|
+
attach_function :pcap_open_live, [:string, :int, :int, :int, :pointer], :pcap_t
|
298
|
+
attach_function :pcap_getnonblock, [:pcap_t, :pointer], :int
|
299
|
+
attach_function :pcap_setnonblock, [:pcap_t, :int, :pointer], :int
|
300
|
+
attach_function :pcap_stats, [:pcap_t, Stat], :int
|
301
|
+
|
302
|
+
end
|
303
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'ffi/pcap/capture_wrapper'
|
2
|
+
|
3
|
+
module FFI
|
4
|
+
module PCap
|
5
|
+
# A wrapper class for pcap devices opened with open_offline()
|
6
|
+
#
|
7
|
+
class Offline < CaptureWrapper
|
8
|
+
attr_accessor :path
|
9
|
+
|
10
|
+
# Creates a pcap interface for reading saved capture files.
|
11
|
+
#
|
12
|
+
# @param [String] path
|
13
|
+
# The path to the file to open.
|
14
|
+
#
|
15
|
+
# @param [Hash] opts
|
16
|
+
# Options are ignored and passed to the super-class except for those
|
17
|
+
# below.
|
18
|
+
#
|
19
|
+
# @option opts [ignored] :path
|
20
|
+
# The :path option will be overridden with the value of the path
|
21
|
+
# argument. If specified in opts, its value will be ignored.
|
22
|
+
#
|
23
|
+
# @return [Offline]
|
24
|
+
# A FFI::PCap::Offline wrapper.
|
25
|
+
#
|
26
|
+
# @raise [LibError]
|
27
|
+
# On failure, an exception is raised with the relevant error
|
28
|
+
# message from libpcap.
|
29
|
+
#
|
30
|
+
def initialize(path, opts={}, &block)
|
31
|
+
@path = path
|
32
|
+
@errbuf = ErrorBuffer.create()
|
33
|
+
@pcap = FFI::PCap.pcap_open_offline(File.expand_path(@path), @errbuf)
|
34
|
+
raise(LibError, "pcap_open_offline(): #{@errbuf.to_s}") if @pcap.null?
|
35
|
+
super(@pcap, opts, &block)
|
36
|
+
end
|
37
|
+
|
38
|
+
def swapped?
|
39
|
+
FFI::PCap.pcap_is_swapped(_pcap) == 1 ? true : false
|
40
|
+
end
|
41
|
+
|
42
|
+
def file_version
|
43
|
+
"#{FFI::PCap.pcap_major_version(_pcap)}.#{FFI::PCap.pcap_minor_version(_pcap)}"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
attach_function :pcap_open_offline, [:string, :pointer], :pcap_t
|
48
|
+
attach_function :pcap_is_swapped, [:pcap_t], :int
|
49
|
+
attach_function :pcap_major_version, [:pcap_t], :int
|
50
|
+
attach_function :pcap_minor_version, [:pcap_t], :int
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,164 @@
|
|
1
|
+
module FFI
|
2
|
+
module PCap
|
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
|
164
|
+
end
|