ffi-pcap 0.2.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
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