ffi-pcap 0.2.0 → 0.2.1

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.
Files changed (82) hide show
  1. data/.gitignore +2 -1
  2. data/.pkg_ignore +25 -0
  3. data/.rspec +1 -0
  4. data/.specopts +1 -0
  5. data/.yardopts +1 -0
  6. data/{ChangeLog.rdoc → ChangeLog.md} +15 -5
  7. data/LICENSE.txt +1 -4
  8. data/README.md +92 -0
  9. data/Rakefile +30 -20
  10. data/examples/em_selectable_pcap.rb +38 -0
  11. data/examples/em_timer.rb +26 -0
  12. data/examples/ipfw_divert.rb +28 -8
  13. data/examples/print_bytes.rb +5 -1
  14. data/examples/replay.rb +11 -0
  15. data/examples/selectable_pcap.rb +29 -0
  16. data/ffi-pcap.gemspec +60 -0
  17. data/gemspec.yml +23 -0
  18. data/lib/ffi/pcap.rb +7 -13
  19. data/lib/ffi/pcap/addr.rb +16 -15
  20. data/lib/ffi/pcap/bpf_instruction.rb +25 -0
  21. data/lib/ffi/pcap/bpf_program.rb +85 -0
  22. data/lib/ffi/pcap/bsd.rb +9 -98
  23. data/lib/ffi/pcap/bsd/af.rb +18 -0
  24. data/lib/ffi/pcap/bsd/in6_addr.rb +16 -0
  25. data/lib/ffi/pcap/bsd/in_addr.rb +18 -0
  26. data/lib/ffi/pcap/bsd/sock_addr.rb +19 -0
  27. data/lib/ffi/pcap/bsd/sock_addr_dl.rb +24 -0
  28. data/lib/ffi/pcap/bsd/sock_addr_family.rb +19 -0
  29. data/lib/ffi/pcap/bsd/sock_addr_in.rb +21 -0
  30. data/lib/ffi/pcap/bsd/sock_addr_in6.rb +20 -0
  31. data/lib/ffi/pcap/bsd/typedefs.rb +7 -0
  32. data/lib/ffi/pcap/capture_wrapper.rb +296 -256
  33. data/lib/ffi/pcap/common_wrapper.rb +152 -127
  34. data/lib/ffi/pcap/copy_handler.rb +32 -32
  35. data/lib/ffi/pcap/crt.rb +7 -10
  36. data/lib/ffi/pcap/data_link.rb +178 -153
  37. data/lib/ffi/pcap/dead.rb +42 -29
  38. data/lib/ffi/pcap/dumper.rb +39 -41
  39. data/lib/ffi/pcap/error_buffer.rb +21 -36
  40. data/lib/ffi/pcap/exceptions.rb +21 -15
  41. data/lib/ffi/pcap/file_header.rb +24 -18
  42. data/lib/ffi/pcap/in_addr.rb +4 -4
  43. data/lib/ffi/pcap/interface.rb +22 -20
  44. data/lib/ffi/pcap/live.rb +296 -252
  45. data/lib/ffi/pcap/offline.rb +50 -43
  46. data/lib/ffi/pcap/packet.rb +186 -143
  47. data/lib/ffi/pcap/packet_header.rb +20 -18
  48. data/lib/ffi/pcap/pcap.rb +269 -212
  49. data/lib/ffi/pcap/stat.rb +19 -49
  50. data/lib/ffi/pcap/stat_ex.rb +42 -0
  51. data/lib/ffi/pcap/time_val.rb +52 -38
  52. data/lib/ffi/pcap/typedefs.rb +16 -20
  53. data/spec/data_link_spec.rb +39 -35
  54. data/spec/dead_spec.rb +0 -4
  55. data/spec/error_buffer_spec.rb +7 -9
  56. data/spec/file_header_spec.rb +17 -14
  57. data/spec/live_spec.rb +12 -5
  58. data/spec/offline_spec.rb +10 -11
  59. data/spec/packet_behaviors.rb +20 -6
  60. data/spec/packet_injection_spec.rb +9 -8
  61. data/spec/packet_spec.rb +22 -26
  62. data/spec/pcap_spec.rb +52 -40
  63. data/spec/spec_helper.rb +16 -5
  64. data/spec/wrapper_behaviors.rb +0 -3
  65. data/tasks/doc.rake +69 -0
  66. data/tasks/gem.rake +200 -0
  67. data/tasks/git.rake +40 -0
  68. data/tasks/post_load.rake +34 -0
  69. data/tasks/rubyforge.rake +55 -0
  70. data/tasks/setup.rb +286 -0
  71. data/tasks/spec.rake +54 -0
  72. data/tasks/svn.rake +47 -0
  73. data/tasks/test.rake +40 -0
  74. metadata +142 -92
  75. data/README.rdoc +0 -30
  76. data/VERSION +0 -1
  77. data/lib/ffi/pcap/bpf.rb +0 -106
  78. data/lib/ffi/pcap/version.rb +0 -6
  79. data/tasks/rcov.rb +0 -6
  80. data/tasks/rdoc.rb +0 -17
  81. data/tasks/spec.rb +0 -9
  82. data/tasks/yard.rb +0 -21
@@ -0,0 +1,21 @@
1
+ require 'ffi/pcap/bsd/typedefs'
2
+ require 'ffi/pcap/bsd/sock_addr_family'
3
+
4
+ module FFI
5
+ module PCap
6
+ #
7
+ # sockaddr inet, always good to have around
8
+ #
9
+ class SockAddrIn < SockAddrFamily
10
+
11
+ dsl_layout do
12
+ field :len, :uint8, :desc => 'length of structure (16)'
13
+ field :family, :sa_family_t, :desc => 'address family (AF_INET)'
14
+ field :port, :in_port_t, :desc => '16-bit TCP or UDP port number'
15
+ field :addr, :in_addr_t, :desc => '32-bit IPv4 address'
16
+ array :_sa_zero, [:uint8,8], :desc => 'unused'
17
+ end
18
+
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,20 @@
1
+ require 'ffi/pcap/bsd/typedefs'
2
+
3
+ module FFI
4
+ module PCap
5
+ #
6
+ # IPv6 socket address
7
+ #
8
+ class SockAddrIn6 < SockAddrFamily
9
+
10
+ dsl_layout do
11
+ field :len, :uint8, :desc => 'length of structure(24)'
12
+ field :family, :sa_family_t, :desc => 'address family (AF_INET6)'
13
+ field :port, :in_port_t, :desc => 'transport layer port'
14
+ field :flowinfo, :uint32, :desc => 'priority & flow label'
15
+ struct :addr, ::FFI::PCap::In6Addr, :desc => 'IPv6 address'
16
+ end
17
+
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,7 @@
1
+ module FFI
2
+ module PCap
3
+ typedef :uint8, :sa_family_t
4
+ typedef :uint32, :in_addr_t
5
+ typedef :uint16, :in_port_t
6
+ end
7
+ end
@@ -2,288 +2,328 @@ require 'ffi/pcap/common_wrapper'
2
2
  require 'ffi/pcap/copy_handler'
3
3
 
4
4
  module FFI
5
- module PCap
6
- # A superclass for both offline and live interfaces, but not dead interfaces
7
- # This class provides all the features necessary for receiving packets
8
- # through libpcap.
9
- #
10
- #
11
- # The loop and dispatch methods default to using a CopyHandler object
12
- # when preparing values to the callback block. This is done to safely
13
- # provide references to packets outside of the callback blocks.
14
- # See CopyHandler for more information.
15
- #
16
- # Note that for performance reasons, you may not need or want to incur
17
- # the extra overhead of creating a copy for every Packet. You can supply
18
- # a nil value for the loop handler which will simply pass volatile
19
- # references to packets directly to your block. You can also write custom
20
- # handlers which implement the 'receive_pcap' method and implement custom
21
- # defined behaviors.
22
- class CaptureWrapper < CommonWrapper
23
- include Enumerable
24
-
25
- attr_accessor :handler
26
-
27
- # Adds an extra parameter :handler for specifying a capture handler
28
- # when using loop or dispatch. The handler defaults to CopyHandler,
29
- # which always yields a copy of each packet to a block. Setting :handler
30
- # to nil will pass packets directly to a block without copying them,
31
- # which may be desirable if the packets are only ever processed within
32
- # the block, and code does not need to retain a reference to them
33
- # elsewhere.
34
- def initialize(pcap, opts={}, &block)
35
- if opts.has_key?(opts[:handler])
36
- @handler = opts[:handler]
37
- else
38
- @handler = CopyHandler
5
+ module PCap
6
+ #
7
+ # A superclass for both offline and live interfaces, but not dead
8
+ # interfaces. This class provides all the features necessary for
9
+ # receiving packets through libpcap.
10
+ #
11
+ # The loop and dispatch methods default to using a {CopyHandler} object
12
+ # when preparing values to the callback block. This is done to safely
13
+ # provide references to packets outside of the callback blocks.
14
+ # See CopyHandler for more information.
15
+ #
16
+ # Note that for performance reasons, you may not need or want to incur
17
+ # the extra overhead of creating a copy for every Packet. You can supply
18
+ # a `nil` value for the loop handler which will simply pass volatile
19
+ # references to packets directly to your block. You can also write
20
+ # custom handlers which implement the `receive_pcap` method and
21
+ # implement custom defined behaviors.
22
+ #
23
+ class CaptureWrapper < CommonWrapper
24
+
25
+ include Enumerable
26
+
27
+ # Default packet count (-1: infinite loop)
28
+ DEFAULT_COUNT = -1
29
+
30
+ attr_accessor :handler
31
+
32
+ #
33
+ # Adds an extra parameter :handler for specifying a capture handler
34
+ # when using loop or dispatch. The handler defaults to {CopyHandler},
35
+ # which always yields a copy of each packet to a block.
36
+ #
37
+ # Setting :handler to `nil` will pass packets directly to a block
38
+ # without copying them, which may be desirable if the packets are
39
+ # only ever processed within the block, and code does not need to
40
+ # retain a reference to them elsewhere.
41
+ #
42
+ def initialize(pcap, opts={}, &block)
43
+ @handler = opts.fetch(handler,CopyHandler)
44
+
45
+ trap('INT') do
46
+ stop()
47
+ close()
48
+
49
+ raise(SignalException,'INT',caller)
50
+ end
51
+
52
+ trap('TERM') do
53
+ stop()
54
+ close()
55
+
56
+ raise(SignalException,'TERM',caller)
57
+ end
58
+
59
+ super(pcap, opts, &block)
39
60
  end
40
61
 
41
- trap('INT') {stop(); close(); raise(SignalException, 'INT')}
42
- trap('TERM') {stop(); close(); raise(SignalException, 'TERM')}
62
+ #
63
+ # Processes packets from a live capture or savefile until cnt packets
64
+ # are processed, the end of the savefile is reached (when reading
65
+ # from a savefile), `pcap_breakloop()` is called, or an error occurs.
66
+ #
67
+ # It does not return when live read timeouts occur. A value of -1 or
68
+ # 0 for cnt is equivalent to infinity, so that packets are processed
69
+ # until another ending condition occurs.
70
+ #
71
+ # (In older versions of libpcap, the behavior when cnt was 0 was
72
+ # undefined; different platforms and devices behaved differently, so
73
+ # code that must work with older versions of libpcap should use -1,
74
+ # nor 0, as the value of cnt.)
75
+ #
76
+ # @param [Hash] opts
77
+ # Receive options.
78
+ #
79
+ # @option [optional, Integer] :count
80
+ # Limit to :count number of packets. Default is infinite.
81
+ #
82
+ # @yield [self, pkt]
83
+ #
84
+ # @yieldparam [CaptureWrapper] self
85
+ # A reference to self is passed to the block.
86
+ #
87
+ # @yieldparam [Packet] pkt
88
+ # A packet object is yielded which references the header and bytes.
89
+ #
90
+ #
91
+ # @return [Integer, nil]
92
+ # Returns 0 if cnt is exhausted, or `nil` if the loop terminated due
93
+ # to a call to `pcap_breakloop()` before any packets were processed.
94
+ # It does not return when live read timeouts occur; instead,
95
+ # it attempts to read more packets.
96
+ #
97
+ # @raise [ReadError]
98
+ # An exception is raised if an error occurs or if libpcap returns
99
+ # an unexpected value.
100
+ #
101
+ def loop(opts={}, &block)
102
+ cnt = (opts[:count] || DEFAULT_COUNT)
103
+ h = opts[:handler]
43
104
 
44
- super(pcap, opts, &block)
45
- end
105
+ ret = PCap.pcap_loop(_pcap, cnt, _wrap_callback(h, block), nil)
46
106
 
47
- private
48
- def _wrap_callback(h, blk)
49
- h ||= @handler
50
- if h
51
- h = h.new() if h.kind_of?(Class)
52
- if ! h.respond_to?(:receive_pcap)
53
- raise(NoMethodError,
54
- "The handler #{h.class} has no receive_pcap method")
55
- end
56
- return lambda do |usr,phdr,body|
57
- yld = h.receive_pcap(self, Packet.new(phdr,body))
58
- blk.call(*yld) if blk and yld
59
- end
60
- elsif blk.kind_of?(Proc) or blk.kind_of?(Method)
61
- return lambda do |usr,phdr,body|
62
- blk.call(pcap, Packet.new(phdr,body))
63
- end
107
+ if ret == -1
108
+ raise(ReadError,"pcap_loop(): #{geterr}",caller)
109
+ elsif ret -2
110
+ return nil
111
+ elsif ret > -1
112
+ return ret
64
113
  else
65
- raise(ArgumentError, "Neither a handler nor block were provided")
114
+ raise(ReadError,"unexpected return from pcap_loop(): #{ret}",caller)
66
115
  end
67
116
  end
68
- public
69
117
 
118
+ alias each loop
70
119
 
71
- # Processes packets from a live capture or savefile until cnt packets
72
- # are processed, the end of the savefile is reached (when reading from a
73
- # savefile), pcap_breakloop() is called, or an error occurs.
74
- #
75
- # It does not return when live read timeouts occur. A value of -1 or 0
76
- # for cnt is equivalent to infinity, so that packets are processed until
77
- # another ending condition occurs.
78
- #
79
- # (In older versions of libpcap, the behavior when cnt was 0 was
80
- # undefined; different platforms and devices behaved differently, so
81
- # code that must work with older versions of libpcap should use -1, nor
82
- # 0, as the value of cnt.)
83
- #
84
- # @param [Hash] opts
85
- # Receive options.
86
- #
87
- # @option [optional, Integer] :count
88
- # Limit to :count number of packets. Default is infinite.
89
- #
90
- # @yield [self, pkt]
91
- #
92
- # @yieldparam [CaptureWrapper] self
93
- # A reference to self is passed to the block.
94
- #
95
- # @yieldparam [Packet] pkt
96
- # A packet object is yielded which references the header and bytes.
97
- #
98
- #
99
- # @return [Integer, nil]
100
- # returns 0 if cnt is exhausted, or nil if the loop terminated due to
101
- # a call to pcap_breakloop() before any packets were processed. It
102
- # does not return when live read timeouts occur; instead, it attempts
103
- # to read more packets.
104
- #
105
- # @raise [ReadError]
106
- # An exception is raised if an error occurs or if libpcap returns
107
- # an unexpected value.
108
- #
109
- def loop(opts={}, &block)
110
- cnt = opts[:count] || -1 # default to infinite loop
111
- h = opts[:handler]
112
-
113
- ret = FFI::PCap.pcap_loop(_pcap, cnt, _wrap_callback(h, block), nil)
114
- if ret == -1
115
- raise(ReadError, "pcap_loop(): #{geterr()}")
116
- elsif ret -2
117
- return nil
118
- elsif ret > -1
119
- return ret
120
- else
121
- raise(ReadError, "unexpected return from pcap_loop(): #{ret}")
120
+ #
121
+ # Processes packets from a live capture or savefile until cnt packets
122
+ # are processed, the end of the current bufferful of packets is
123
+ # reached when doing a live capture, the end of the savefile is
124
+ # reached (when reading from a savefile), `pcap_breakloop()` is called,
125
+ # or an error occurs.
126
+ #
127
+ # Thus, when doing a live capture, cnt is the maximum number of
128
+ # packets to process before returning, but is not a minimum number;
129
+ # when reading a live capture, only one bufferful of packets is read
130
+ # at a time, so fewer than cnt packets may be processed. A value of
131
+ # -1 or 0 for cnt causes all the packets received in one buffer to be
132
+ # processed when reading a live capture, and causes all the packets
133
+ # in the file to be processed when reading a savefile.
134
+ #
135
+ # Note: In older versions of libpcap, the behavior when cnt was 0 was
136
+ # undefined; different platforms and devices behaved differently, so
137
+ # code that must work with older versions of libpcap should use -1,
138
+ # nor 0, as the value of cnt.
139
+ #
140
+ # @yield [self, pkt]
141
+ #
142
+ # @yieldparam [CaptureWrapper] self
143
+ # A reference to self is passed to the block.
144
+ #
145
+ # @yieldparam [Packet] pkt
146
+ # A packet object is yielded which references the header and bytes.
147
+ #
148
+ # @return [Integer, nil]
149
+ # Returns the number of packets processed on success; this can be 0
150
+ # if no packets were read from a live capture or if no more packets
151
+ # are available in a savefile. It returns `nil` if the loop
152
+ # terminated due to a call to {CommonWrapper#stop} before any
153
+ # packets were processed.
154
+ #
155
+ # @raise [ReadError]
156
+ # An exception is raised if an error occurs or if libpcap returns
157
+ # an unexpected value.
158
+ #
159
+ def dispatch(opts={}, &block)
160
+ cnt = (opts[:count] || DEFAULT_COUNT) # default to infinite loop
161
+ h = opts[:handler]
162
+
163
+ ret = PCap.pcap_dispatch(_pcap, cnt, _wrap_callback(h, block),nil)
164
+
165
+ if ret == -1
166
+ raise(ReadError,"pcap_dispatch(): #{geterr}",caller)
167
+ elsif ret -2
168
+ return nil
169
+ elsif ret > -1
170
+ return ret
171
+ else
172
+ raise(ReadError,"unexpected return from pcap_dispatch() -> #{ret}",caller)
173
+ end
122
174
  end
123
- end
124
175
 
125
- alias each loop
126
-
127
-
128
- # Processes packets from a live capture or savefile until cnt packets
129
- # are processed, the end of the current bufferful of packets is reached
130
- # when doing a live capture, the end of the savefile is reached (when
131
- # reading from a savefile), pcap_breakloop() is called, or an error
132
- # occurs.
133
- #
134
- # Thus, when doing a live capture, cnt is the maximum number of packets
135
- # to process before returning, but is not a minimum number; when reading
136
- # a live capture, only one bufferful of packets is read at a time, so
137
- # fewer than cnt packets may be processed. A value of -1 or 0 for cnt
138
- # causes all the packets received in one buffer to be processed when
139
- # reading a live capture, and causes all the packets in the file to be
140
- # processed when reading a savefile.
141
- #
142
- # Note: In older versions of libpcap, the behavior when cnt was 0 was
143
- # undefined; different platforms and devices behaved differently, so
144
- # code that must work with older versions of libpcap should use -1, nor
145
- # 0, as the value of cnt.
146
- #
147
- # @yield [self, pkt]
148
- #
149
- # @yieldparam [CaptureWrapper] self
150
- # A reference to self is passed to the block.
151
- #
152
- # @yieldparam [Packet] pkt
153
- # A packet object is yielded which references the header and bytes.
154
- #
155
- # @return [Integer, nil]
156
- # Returns the number of packets processed on success; this can be 0 if
157
- # no packets were read from a live capture or if no more packets are
158
- # available in a savefile. It returns nil if the loop terminated due
159
- # to a call to CommonWrapper.stop() before any packets were processed.
160
- #
161
- # @raise [ReadError]
162
- # An exception is raised if an error occurs or if libpcap returns
163
- # an unexpected value.
164
- #
165
- def dispatch(opts={}, &block)
166
- cnt = opts[:count] || -1 # default to infinite loop
167
- h = opts[:handler]
168
-
169
- ret = FFI::PCap.pcap_loop(_pcap, cnt, _wrap_callback(h, block),nil)
170
- if ret == -1
171
- raise(ReadError, "pcap_dispatch(): #{geterr()}")
172
- elsif ret -2
173
- return nil
174
- elsif ret > -1
175
- return ret
176
- else
177
- raise(ReadError, "unexpected return from pcap_dispatch() -> #{ret}")
176
+ #
177
+ # This method uses the older `pcap_next()` function which has been
178
+ # deprecated in favor of `pcap_next_ex()`. It is included only for
179
+ # backward compatability purposes.
180
+ #
181
+ # Important Note. According to libpcap documentation:
182
+ #
183
+ # Unfortunately, there is no way to determine whether an error
184
+ # occured or not when using pcap_next().
185
+ #
186
+ def old_next
187
+ header = PacketHeader.new
188
+ bytes = PCap.pcap_next(_pcap, header)
189
+
190
+ return Packet.new(header, bytes) unless bytes.null?
178
191
  end
179
- end
180
192
 
193
+ #
194
+ # Reads the next packet from a pcap device and returns a
195
+ # success/failure indication.
196
+ #
197
+ # @return [Packet, nil]
198
+ # A packet is returned on success or a `nil` if the timeout expired
199
+ # or all packets in a dump file have been exhausted when reading
200
+ # from a savefile.
201
+ #
202
+ # @raise [ReadError]
203
+ # This exception is raised if there was an error calling
204
+ # `pcap_next_ex()`.
205
+ #
206
+ # @raise [TimeoutError]
207
+ # This exception is raised if the timeout expires
208
+ #
209
+ def next
210
+ hdr_p = MemoryPointer.new(:pointer)
211
+ buf_p = MemoryPointer.new(:pointer)
181
212
 
182
- # This method uses the older pcap_next() function which has been
183
- # deprecated in favor of pcap_next_ex(). It is included only for
184
- # backward compatability purposes.
185
- #
186
- # Important Note. According to libpcap documentation:
187
- #
188
- # Unfortunately, there is no way to determine whether an error
189
- # occured or not when using pcap_next().
190
- #
191
- def old_next
192
- header = PacketHeader.new
193
- bytes = FFI::PCap.pcap_next(_pcap, header)
194
- if bytes.null?
195
- return nil # or raise an exception?
196
- else
197
- return Packet.new(header, bytes)
213
+ case PCap.pcap_next_ex(_pcap, hdr_p, buf_p)
214
+ when -1 # error
215
+ raise(ReadError,"pcap_next_ex(): #{geterr}",caller)
216
+ when 0 # live capture read timeout expired
217
+ return nil
218
+ when -2 # savefile packets exhausted
219
+ return nil
220
+ when 1
221
+ hdr = PacketHeader.new(hdr_p.get_pointer(0))
222
+ return Packet.new(hdr, buf_p.get_pointer(0))
223
+ end
198
224
  end
199
- end
200
225
 
226
+ alias next_extra next
227
+ alias next_ex next
201
228
 
202
- # Reads the next packet from a pcap device and returns a success/failure
203
- # indication.
204
- #
205
- # @return [Packet, nil]
206
- # A packet is returned on success or a nil if the timeout expired or
207
- # all packets in a dump file have been exhausted when reading from
208
- # a savefile.
209
- #
210
- # @raise [ReadError]
211
- # This exception is raised if there was an error calling
212
- # pcap_next_ex().
213
- #
214
- # @raise [TimeoutError]
215
- # This exception is raised if the timeout expires
216
- #
217
- def next
218
- hdr_p = MemoryPointer.new(:pointer)
219
- buf_p = MemoryPointer.new(:pointer)
220
-
221
- case FFI::PCap.pcap_next_ex(_pcap, hdr_p, buf_p)
222
- when -1 # error
223
- raise(ReadError, "pcap_next_ex(): #{geterr()}")
224
- when 0 # live capture read timeout expired
225
- return nil
226
- when -2 # savefile packets exhausted
227
- return nil
228
- when 1
229
- hdr = PacketHeader.new( hdr_p.get_pointer(0) )
230
- return Packet.new(hdr, buf_p.get_pointer(0))
229
+ #
230
+ # Sets a flag that will force {#dispatch} or {#loop} to return rather
231
+ # than looping; they will return the number of packets that have been
232
+ # processed so far, or `nil` if no packets have been processed so far.
233
+ #
234
+ # breakloop does not guarantee that no further packets will be
235
+ # processed by {#dispatch} or {#loop} after it is called. At most
236
+ # one more packet may be processed.
237
+ #
238
+ def breakloop
239
+ PCap.pcap_breakloop(_pcap)
231
240
  end
232
- end
233
241
 
234
- alias next_extra next
235
- alias next_ex next
242
+ alias stop breakloop
236
243
 
244
+ #
245
+ # Used to specify a pcap filter for the pcap interface. This method
246
+ # compiles a filter expression and applies it on the wrapped pcap
247
+ # interface.
248
+ #
249
+ # @param [String] expression
250
+ # A pcap filter expression. See pcap-filter(7) manpage for syntax.
251
+ #
252
+ # @param [Hash] opts
253
+ # Compile options. See compile()
254
+ #
255
+ # @raise [LibError]
256
+ # On failure, an exception is raised with the relevant error
257
+ # message from libpcap.
258
+ #
259
+ def set_filter(expression, opts={})
260
+ code = compile(expression, opts)
261
+ ret = PCap.pcap_setfilter(_pcap, code)
237
262
 
238
- # Sets a flag that will force dispatch() or loop() to return rather
239
- # than looping; they will return the number of packets that have been
240
- # processed so far, or nil if no packets have been processed so far.
241
- #
242
- # breakloop does not guarantee that no further packets will be
243
- # processed by dispatch() or loop() after it is called. At most
244
- # one more packet may be processed.
245
- #
246
- def breakloop
247
- FFI::PCap.pcap_breakloop(_pcap)
248
- end
263
+ # done with this, we can free it
264
+ code.free!
249
265
 
250
- alias stop breakloop
266
+ if ret < 0
267
+ raise(LibError, "pcap_setfilter(): #{geterr}",caller)
268
+ end
251
269
 
270
+ return expression
271
+ end
252
272
 
253
- # Used to specify a pcap filter for the pcap interface. This method
254
- # compiles a filter expression and applies it on the wrapped pcap
255
- # interface.
256
- #
257
- # @param [String] expression
258
- # A pcap filter expression. See pcap-filter(7) manpage for syntax.
259
- #
260
- # @param [Hash] opts
261
- # Compile options. See compile()
262
- #
263
- # @raise [LibError]
264
- # On failure, an exception is raised with the relevant error message
265
- # from libpcap.
266
- #
267
- def set_filter(expression, opts={})
268
- code = compile(expression, opts)
269
- ret = FFI::PCap.pcap_setfilter(_pcap, code)
270
- code.free! # done with this, we can free it
271
- raise(LibError, "pcap_setfilter(): #{geterr()}") if ret < 0
272
- return expression
273
- end
273
+ alias setfilter set_filter
274
+ alias filter= set_filter
274
275
 
275
- alias setfilter set_filter
276
- alias filter= set_filter
276
+ def fileno
277
+ PCap.pcap_fileno(_pcap)
278
+ end
277
279
 
278
- end
280
+ def selectable_fd
281
+ if PCap.respond_to?(:pcap_get_selectable_fd)
282
+ PCap.pcap_get_selectable_fd(pcap)
283
+ else
284
+ raise(NotImplementedError, "selectable pcap IO is not available for your platform")
285
+ end
286
+ end
279
287
 
280
- callback :pcap_handler, [:pointer, PacketHeader, :pointer], :void
281
- attach_function :pcap_loop, [:pcap_t, :int, :pcap_handler, :pointer], :int
282
- attach_function :pcap_dispatch, [:pcap_t, :int, :pcap_handler, :pointer], :int
283
- attach_function :pcap_next, [:pcap_t, PacketHeader], :pointer
284
- attach_function :pcap_next_ex, [:pcap_t, :pointer, :pointer], :int
285
- attach_function :pcap_breakloop, [:pcap_t], :void
286
- attach_function :pcap_setfilter, [:pcap_t, BPFProgram], :int
288
+ def selectable_io
289
+ ::IO.new(self.selectable_fd, 'r')
290
+ end
287
291
 
288
- end
292
+ def _wrap_callback(h, block)
293
+ h ||= @handler
294
+
295
+ if h
296
+ h = h.new() if h.kind_of?(Class)
297
+
298
+ unless h.respond_to?(:receive_pcap)
299
+ raise(NoMethodError, "The handler #{h.class} has no receive_pcap method",caller)
300
+ end
301
+
302
+ return lambda { |usr,phdr,body|
303
+ yld = h.receive_pcap(self, Packet.new(phdr,body))
304
+
305
+ block.call(*yld) if (block && yld)
306
+ }
307
+ elsif (block.kind_of?(Proc) || block.kind_of?(Method))
308
+ return lambda { |usr,phdr,body|
309
+ block.call(pcap,Packet.new(phdr,body))
310
+ }
311
+ else
312
+ raise(ArgumentError,"Neither a handler nor block were provided",caller)
313
+ end
314
+ end
315
+
316
+ private :_wrap_callback
317
+
318
+ end
319
+
320
+ callback :pcap_handler, [:pointer, PacketHeader, :pointer], :void
321
+ attach_function :pcap_loop, [:pcap_t, :int, :pcap_handler, :pointer], :int
322
+ attach_function :pcap_dispatch, [:pcap_t, :int, :pcap_handler, :pointer], :int
323
+ attach_function :pcap_next, [:pcap_t, PacketHeader], :pointer
324
+ attach_function :pcap_next_ex, [:pcap_t, :pointer, :pointer], :int
325
+ attach_function :pcap_breakloop, [:pcap_t], :void
326
+ attach_function :pcap_setfilter, [:pcap_t, BPFProgram], :int
327
+ attach_function :pcap_fileno, [:pcap_t], :int
328
+ end
289
329
  end