caper 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.
Files changed (53) hide show
  1. data/.gitignore +9 -0
  2. data/History.rdoc +31 -0
  3. data/LICENSE.ffi-pcap +23 -0
  4. data/README.rdoc +55 -0
  5. data/Rakefile +26 -0
  6. data/VERSION +1 -0
  7. data/examples/ipfw_divert.rb +45 -0
  8. data/examples/print_bytes.rb +17 -0
  9. data/examples/test_loop.rb +48 -0
  10. data/lib/caper.rb +40 -0
  11. data/lib/caper/addr.rb +19 -0
  12. data/lib/caper/bpf.rb +104 -0
  13. data/lib/caper/bsd.rb +96 -0
  14. data/lib/caper/capture_wrapper.rb +287 -0
  15. data/lib/caper/common_wrapper.rb +173 -0
  16. data/lib/caper/copy_handler.rb +36 -0
  17. data/lib/caper/crt.rb +13 -0
  18. data/lib/caper/data_link.rb +171 -0
  19. data/lib/caper/dead.rb +35 -0
  20. data/lib/caper/dumper.rb +53 -0
  21. data/lib/caper/error_buffer.rb +42 -0
  22. data/lib/caper/exceptions.rb +19 -0
  23. data/lib/caper/file_header.rb +24 -0
  24. data/lib/caper/in_addr.rb +7 -0
  25. data/lib/caper/interface.rb +27 -0
  26. data/lib/caper/live.rb +301 -0
  27. data/lib/caper/offline.rb +51 -0
  28. data/lib/caper/packet.rb +163 -0
  29. data/lib/caper/packet_header.rb +23 -0
  30. data/lib/caper/pcap.rb +250 -0
  31. data/lib/caper/stat.rb +56 -0
  32. data/lib/caper/time_val.rb +46 -0
  33. data/lib/caper/typedefs.rb +25 -0
  34. data/lib/caper/version.rb +4 -0
  35. data/spec/data_link_spec.rb +65 -0
  36. data/spec/dead_spec.rb +34 -0
  37. data/spec/dumps/http.pcap +0 -0
  38. data/spec/dumps/simple_tcp.pcap +0 -0
  39. data/spec/error_buffer_spec.rb +17 -0
  40. data/spec/file_header_spec.rb +28 -0
  41. data/spec/live_spec.rb +87 -0
  42. data/spec/offline_spec.rb +61 -0
  43. data/spec/packet_behaviors.rb +68 -0
  44. data/spec/packet_injection_spec.rb +38 -0
  45. data/spec/packet_spec.rb +111 -0
  46. data/spec/pcap_spec.rb +149 -0
  47. data/spec/spec_helper.rb +31 -0
  48. data/spec/wrapper_behaviors.rb +110 -0
  49. data/tasks/rcov.rb +6 -0
  50. data/tasks/rdoc.rb +17 -0
  51. data/tasks/spec.rb +9 -0
  52. data/tasks/yard.rb +16 -0
  53. metadata +140 -0
@@ -0,0 +1,36 @@
1
+
2
+ module Caper
3
+
4
+ # CopyHandler is a callback handler for use with CaptureWrapper.loop and
5
+ # CaptureWrapper.dispatch. When used, it works exactly as normal,
6
+ # passing a reference to a pcap wrapper and Packet except for one important
7
+ # difference. A copy of the Packet is yielded to the callback instead of
8
+ # the volatile one received in the pcap_loop() and pcap_dispatch()
9
+ # callbacks.
10
+ #
11
+ # The CopyHandler implements receive_callback to return a _copy_
12
+ # of the Packet object. It is necessary to make a copy to keep allocated
13
+ # references to packets supplied by pcap_loop() and pcap_dispatch()
14
+ # callbacks outside of the scope of a single callback firing on one
15
+ # packet.
16
+ #
17
+ # This handler interface is used by default by CaptureWrapper, so it is
18
+ # generally always safe to keep references to received packets after new
19
+ # packets have been received or even after a pcap interface has been
20
+ # closed. See CaptureWrapper for more information.
21
+ class CopyHandler
22
+ def receive_pcap(pcap, pkt)
23
+ return [pcap, pkt.copy]
24
+ end
25
+ end
26
+
27
+
28
+ # This class only exists for backward compatibility. Setting
29
+ # pcap handler to nil has the same effect now.
30
+ class Handler
31
+ def receive_pcap(*args)
32
+ return args
33
+ end
34
+ end
35
+ end
36
+
@@ -0,0 +1,13 @@
1
+
2
+ module Caper
3
+ module CRT
4
+ extend FFI::Library
5
+
6
+ ffi_lib FFI::Library::LIBC
7
+
8
+ typedef :ulong, :size_t # not all platforms have this set for FFI
9
+
10
+ attach_function :free, [:pointer], :void
11
+ attach_function :memcpy, [:pointer, :pointer, :size_t], :pointer
12
+ end
13
+ end
@@ -0,0 +1,171 @@
1
+ module Caper
2
+
3
+ class DataLink
4
+
5
+ # Several DLT names harvested out of the pcap-bpf.h header file. These
6
+ # are in alphabetical order. Their Array index _does_ _not_ match their
7
+ # pcap DLT value.
8
+ #
9
+ # Don't use this Array for anything except quick reference. Use the
10
+ # 'lookup' class methods for actually resolving name to value
11
+ # mappings or such.
12
+ SOME_DLTS = %w[A429 A653_ICM AIRONET_HEADER APPLE_IP_OVER_IEEE1394
13
+ ARCNET ARCNET_LINUX ATM_CLIP ATM_RFC1483 AURORA AX25 BACNET_MS_TP
14
+ BLUETOOTH_HCI_H4 BLUETOOTH_HCI_H4_WITH_PHDR CAN20B CHAOS CHDLC CISCO_IOS
15
+ C_HDLC DOCSIS ECONET EN10MB EN3MB ENC ERF ERF_ETH ERF_POS FDDI FRELAY
16
+ GCOM_SERIAL GCOM_T1E1 GPF_F GPF_T GPRS_LLC HHDLC IBM_SN IBM_SP IEEE802
17
+ IEEE802_11 IEEE802_11_RADIO IEEE802_11_RADIO_AVS IEEE802_15_4
18
+ IEEE802_15_4_LINUX IEEE802_16_MAC_CPS IEEE802_16_MAC_CPS_RADIO IPFILTER
19
+ IPMB IP_OVER_FC JUNIPER_ATM1 JUNIPER_ATM2 JUNIPER_CHDLC JUNIPER_ES
20
+ JUNIPER_ETHER JUNIPER_FRELAY JUNIPER_GGSN JUNIPER_ISM JUNIPER_MFR
21
+ JUNIPER_MLFR JUNIPER_MLPPP JUNIPER_MONITOR JUNIPER_PIC_PEER JUNIPER_PPP
22
+ JUNIPER_PPPOE JUNIPER_PPPOE_ATM JUNIPER_SERVICES JUNIPER_ST JUNIPER_VP
23
+ LINUX_IRDA LINUX_LAPD LINUX_PPP_WITHDIRECTION LINUX_SLL LOOP LTALK MFR
24
+ MTP2 MTP2_WITH_PHDR MTP3 NULL OLD_PFLOG PCI_EXP PFLOG PFSYNC PPI PPP
25
+ PPP_BSDOS PPP_ETHER PPP_PPPD PPP_SERIAL PPP_WITH_DIRECTION PRISM_HEADER
26
+ PRONET RAIF1 RAW REDBACK_SMARTEDGE RIO SCCP SITA SLIP SLIP_BSDOS SUNATM
27
+ SYMANTEC_FIREWALL TZSP USB USB_LINUX USER0 USER1 USER10 USER11 USER12
28
+ USER13 USER14 USER15 USER2 USER3 USER4 USER5 USER6 USER7 USER8 USER9]
29
+
30
+ # Uses the pcap_datalnk_* functions to lookup a datalink name and value
31
+ # pair.
32
+ #
33
+ # @param [String, Symbol or Integer] l
34
+ # The name or value to lookup. A Symbol is converted to String. Names
35
+ # are case-insensitive.
36
+ #
37
+ # @return [Array]
38
+ # A 2-element array containing [value, name]. Both elements are nil
39
+ # if the lookup failed.
40
+ #
41
+ def self.lookup(l)
42
+ val, name = nil
43
+ l = l.to_s if l.kind_of? Symbol
44
+
45
+ case l
46
+ when String
47
+ if v=name_to_val(l)
48
+ name = val_to_name(v) # get the canonical name
49
+ val = v
50
+ end
51
+ when Integer
52
+ name = val_to_name(l)
53
+ val = l
54
+ else
55
+ raise(ArgumentError, "lookup takes either a String or Integer")
56
+ end
57
+ return [val, name]
58
+ end
59
+
60
+ # Translates a data link type name, which is a DLT_ name with the DLT_
61
+ # removed, to the corresponding data link type numeric value.
62
+ #
63
+ # @param [String or Symbol] n
64
+ # The name to lookup. Names are case-insensitive.
65
+ #
66
+ # @return [Integer or nil]
67
+ # The numeric value for the datalink name or nil on failure.
68
+ def self.name_to_val(n)
69
+ n = n.to_s if n.kind_of?(Symbol)
70
+ if (v=Caper.pcap_datalink_name_to_val(n)) >= 0
71
+ return v
72
+ end
73
+ end
74
+
75
+ # Translates a data link type value to the corresponding data link
76
+ # type name.
77
+ #
78
+ # @return [String or nil]
79
+ # The string name of the data-link or nil on failure.
80
+ #
81
+ def self.val_to_name(v)
82
+ Caper.pcap_datalink_val_to_name(v)
83
+ end
84
+
85
+ # @param [String, Symbol or Integer] l
86
+ # The name or value to lookup. A Symbol is converted to String. Names
87
+ # are case-insensitive.
88
+ def self.describe(l)
89
+ l = l.to_s if l.kind_of?(Symbol)
90
+ l = Caper.pcap_datalink_name_to_val(l) if l.kind_of?(String)
91
+ Caper.pcap_datalink_val_to_description(l)
92
+ end
93
+
94
+ # Caper datalink numeric value
95
+ attr_reader :value
96
+
97
+ # Creates a new DataLink object with the specified value or name.
98
+ # The canonical name, value, and description are can be looked up on
99
+ # demand.
100
+ #
101
+ # @param [String or Integer] arg
102
+ # Arg can be a string or number which will be used to look up the
103
+ # datalink.
104
+ #
105
+ # @raise [UnsupportedDataLinkError]
106
+ # An exception is raised if a name is supplied and a lookup for its
107
+ # value fails or if the arg parameter is an invalid type.
108
+ #
109
+ def initialize(arg)
110
+ if arg.kind_of? String or arg.kind_of? Symbol
111
+ unless @value = self.class.name_to_val(arg.to_s)
112
+ raise(UnsupportedDataLinkError, "Invalid DataLink: #{arg.to_s}")
113
+ end
114
+ elsif arg.kind_of? Numeric
115
+ @value = arg
116
+ else
117
+ raise(UnsupportedDataLinkError, "Invalid DataLink: #{arg.inspect}")
118
+ end
119
+ @name = self.class.val_to_name(@value)
120
+ end
121
+
122
+ # Overrides the equality operator so that quick comparisons can be
123
+ # made against other DataLinks, name by String, or value by Integer.
124
+ def ==(other)
125
+ case other
126
+ when DataLink
127
+ return (self.value == other.value)
128
+ when Numeric
129
+ return (self.value == other)
130
+ when Symbol
131
+ return (@value == self.class.name_to_val(other.to_s))
132
+ when String
133
+ return (@value == self.class.name_to_val(other))
134
+ else
135
+ return false
136
+ end
137
+ end
138
+
139
+ # Overrides the sort comparison operator to sort by DLT value.
140
+ def <=>(other)
141
+ self.value <=> other.value
142
+ end
143
+
144
+ # Returns the description of the datalink.
145
+ def description
146
+ @desc ||= self.class.describe(@value)
147
+ end
148
+
149
+ alias desc description
150
+ alias describe description
151
+
152
+ # Returns the canonical String name of the DataLink object
153
+ def name
154
+ @name
155
+ end
156
+
157
+ # Override 'inspect' we'll to always provide the name for irb,
158
+ # pretty_print, etc.
159
+ def inspect
160
+ "<#{self.class}:#{"0x%0.8x" % self.object_id} @value=#{@value}, @name=#{name().inspect}>"
161
+ end
162
+
163
+ alias to_s name
164
+ alias to_i value
165
+ end
166
+
167
+ attach_function :pcap_datalink_name_to_val, [:string], :int
168
+ attach_function :pcap_datalink_val_to_name, [:int], :string
169
+ attach_function :pcap_datalink_val_to_description, [:int], :string
170
+
171
+ end
@@ -0,0 +1,35 @@
1
+ require 'caper/common_wrapper'
2
+
3
+ module Caper
4
+ # A wrapper class for pcap devices opened with open_dead()
5
+ class Dead < CommonWrapper
6
+ attr_reader :datalink
7
+
8
+ # Creates a fake pcap interface for compiling filters or opening a
9
+ # capture for output.
10
+ #
11
+ # @param [Hash] opts
12
+ # Options are ignored and passed to the super-class except those below.
13
+ #
14
+ # @option opts [optional, String, Symbol, Integer] :datalink
15
+ # The link-layer type for pcap. nil is equivalent to 0 (aka DLT_NULL).
16
+ #
17
+ # @option opts [optional, Integer] :snaplen
18
+ # The snapshot length for the pcap object. Defaults to Caper::DEFAULT_SNAPLEN
19
+ #
20
+ # @return [Dead]
21
+ # A Caper::Dead wrapper.
22
+ #
23
+ def initialize(opts={}, &block)
24
+ dl = opts[:datalink] || DataLink.new(0)
25
+ @datalink = dl.kind_of?(DataLink) ? dl : DataLink.new(dl)
26
+ @snaplen = opts[:snaplen] || DEFAULT_SNAPLEN
27
+ @pcap = Caper.pcap_open_dead(@datalink.value, @snaplen)
28
+ raise(LibError, "pcap_open_dead(): returned a null pointer") if @pcap.null?
29
+ super(@pcap, opts, &block)
30
+ end
31
+ end
32
+
33
+ attach_function :pcap_open_dead, [:int, :int], :pcap_t
34
+
35
+ end
@@ -0,0 +1,53 @@
1
+
2
+ module Caper
3
+
4
+ # See pcap_dumper_t in pcap.h
5
+ #
6
+ # A pcap_dumper, or Caper::Dumper is handled opaquely so that it can
7
+ # be implemented differently on different platforms. In Caper, we
8
+ # simply wrap the pcap_dumper_t pointer with a ruby interface.
9
+ class Dumper
10
+
11
+ def initialize(dumper)
12
+ @dumper = dumper
13
+ end
14
+
15
+ def _write(header, bytes)
16
+ Caper.pcap_dump(@dumper, header, bytes)
17
+ end
18
+
19
+ def write(*args)
20
+ if args.first.is_a? Packet
21
+ write_pkt(*args)
22
+ else
23
+ _write(*args)
24
+ end
25
+ end
26
+
27
+ def write_pkt(pkt)
28
+ _write(pkt.header, pkt.body_ptr)
29
+ end
30
+
31
+ def tell
32
+ Caper.pcap_dump_ftell(@dumper)
33
+ end
34
+
35
+ def flush
36
+ Caper.pcap_dump_flush(@dumper)
37
+ end
38
+
39
+ def close
40
+ Caper.pcap_dump_close(@dumper)
41
+ end
42
+
43
+ end
44
+
45
+ # XXX not sure if we even want file FILE IO stuff yet
46
+ #attach_function :pcap_dump_file, [:pcap_dumper_t], :FILE
47
+
48
+ attach_function :pcap_dump_ftell, [:pcap_dumper_t], :long
49
+ attach_function :pcap_dump_flush, [:pcap_dumper_t], :int
50
+ attach_function :pcap_dump_close, [:pcap_dumper_t], :void
51
+ attach_function :pcap_dump, [:pointer, PacketHeader, :pointer], :void
52
+
53
+ end
@@ -0,0 +1,42 @@
1
+ module Caper
2
+ class ErrorBuffer < FFI::MemoryPointer
3
+
4
+ # Size of the error buffers
5
+ SIZE = 256
6
+
7
+ # Creates a new ErrorBuffer object. Because of wierdness in JRuby
8
+ # when trying to subclass FFI::Buffer, always use this instead of
9
+ # 'new()'
10
+ #
11
+ # See http://github.com/ffi/ffi/issues#issue/27
12
+ def self.create()
13
+ new(SIZE)
14
+ end
15
+
16
+ #
17
+ # Creates a new ErrorBuffer object.
18
+ # The argument is nil and is only present for compatability with JRuby.
19
+ #
20
+ # See http://github.com/ffi/ffi/issues#issue/27
21
+ def initialize(arg=nil)
22
+ super(SIZE)
23
+ end
24
+
25
+ #
26
+ # Returns the error message within the error buffer.
27
+ #
28
+ def to_s
29
+ get_string(0)
30
+ end
31
+
32
+ # Older JRuby/ffi versions of MemoryPointer and Buffer don't have a size
33
+ # method. We override it here to ensure we can use it.
34
+ def size
35
+ begin
36
+ super()
37
+ rescue NoMethodError
38
+ SIZE
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,19 @@
1
+ module Caper
2
+ # A Caper::UnsupportedDataLinkError indicates an invalid or unsupported
3
+ # DataLink Layer Type (DLT) value or name.
4
+ class UnsupportedDataLinkError < StandardError
5
+ end
6
+
7
+ # A Caper::LibError is used to convey errors detected by the libpcap
8
+ # native library.
9
+ class LibError < StandardError
10
+ end
11
+
12
+ # A Caper::ReadError is a sub-class of Caper::LibError that indicates a
13
+ # problem reading from a pcap device.
14
+ class ReadError < LibError
15
+ end
16
+
17
+ class TimeoutError < LibError
18
+ end
19
+ end
@@ -0,0 +1,24 @@
1
+
2
+ module Caper
3
+ class FileHeader < FFI::Struct
4
+ include FFI::DRY::StructHelper
5
+
6
+ dsl_layout do
7
+ field :magic, :bpf_uint32
8
+ field :version_major, :ushort
9
+ field :version_minor, :ushort
10
+ field :thiszone, :bpf_int32
11
+ field :sigfigs, :bpf_uint32
12
+ field :snaplen, :bpf_uint32
13
+ field :linktype, :bpf_uint32
14
+ end
15
+
16
+ def datalink
17
+ DataLink.new(self.linktype)
18
+ end
19
+
20
+ def version
21
+ "#{self.version_major}.#{self.version_minor}"
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,7 @@
1
+ module Caper
2
+ class InAddr < FFI::Struct
3
+
4
+ layout :s_addr, [NativeType::UINT8, 4]
5
+
6
+ end
7
+ end
@@ -0,0 +1,27 @@
1
+
2
+ module Caper
3
+
4
+ # Item in a list of interfaces.
5
+ #
6
+ # See pcap_if struct in pcap.h
7
+ class Interface < FFI::Struct
8
+ include FFI::DRY::StructHelper
9
+
10
+ # interface is loopback
11
+ LOOPBACK = 0x00000001
12
+
13
+ dsl_layout do
14
+ p_struct :next, ::Caper::Interface
15
+ field :name, :string, :desc => 'name used by pcap_open_live()'
16
+ field :description, :string, :desc => 'text description, or NULL'
17
+ p_struct :addresses, ::Caper::Addr, :desc => 'address linked list'
18
+ field :flags, :bpf_uint32, :desc => 'PCAP_IF_ interface flags'
19
+ end
20
+
21
+ def loopback?
22
+ self.flags & LOOPBACK == LOOPBACK
23
+ end
24
+
25
+ end
26
+
27
+ end
@@ -0,0 +1,301 @@
1
+ require 'caper/capture_wrapper'
2
+
3
+ module Caper
4
+ begin
5
+ attach_function :pcap_setdirection, [:pcap_t, :pcap_direction_t], :int
6
+ rescue FFI::NotFoundError
7
+ end
8
+
9
+ begin
10
+ attach_function :pcap_sendpacket, [:pcap_t, :pointer, :int], :int
11
+ rescue FFI::NotFoundError
12
+ end
13
+
14
+ begin
15
+ attach_function :pcap_inject, [:pcap_t, :pointer, :int], :int
16
+ rescue FFI::NotFoundError
17
+ end
18
+
19
+
20
+ # Creates a pcap interface for capturing from the network.
21
+ #
22
+ # @param [Hash] opts
23
+ # Options are ignored and passed to the super-class except those below.
24
+ #
25
+ # @option opts [optional, String, nil] :device, :dev
26
+ # The device to open. On some platforms, this can be "any". If nil or
27
+ # unspecified Caper.lookupdev() is called to obtain a default device.
28
+ #
29
+ # @option opts [optional, Integer] :snaplen
30
+ # The snapshot length for the pcap object. Defaults to DEFAULT_SNAPLEN
31
+ #
32
+ # @option opts [optional, Boolean] :promisc
33
+ # Specifies if the interface is to be put into promiscuous mode. Defaults
34
+ # to false.
35
+ #
36
+ # @option opts [optional, Integer] :timeout
37
+ # Specifies the read timeout in milliseconds. Defaults to DEFAULT_TO_MS
38
+ #
39
+ # @return [Live]
40
+ # A Caper::Live wrapper.
41
+ #
42
+ # @raise [LibError]
43
+ # On failure, an exception is raised with the relevant error
44
+ # message from libpcap.
45
+ #
46
+ # @raise [ArgumentError]
47
+ # May raise an exception if a :device cannot be autodetected using
48
+ # Caper.lookupdev() for any reason. This should never happen on most platforms.
49
+ #
50
+ class Live < CaptureWrapper
51
+ DEFAULT_TO_MS = 1000 # Default timeout for pcap_open_live()
52
+
53
+ attr_reader :device, :promisc, :timeout, :direction
54
+
55
+ def initialize(opts=nil)
56
+ opts ||= {}
57
+ @device = opts[:device] || opts[:dev] || Caper.lookupdev()
58
+ unless @device
59
+ raise(ArgumentError, "Couldn't detect a device. One must be specified.")
60
+ end
61
+
62
+ @snaplen = opts[:snaplen] || DEFAULT_SNAPLEN
63
+ @promisc = opts[:promisc] ? 1 : 0
64
+ @timeout = opts[:timeout] || DEFAULT_TO_MS
65
+ @direction = (opts[:direction] || opts[:dir])
66
+
67
+ @errbuf = ErrorBuffer.create()
68
+ @pcap = Caper.pcap_open_live(@device, @snaplen, @promisc, @timeout, @errbuf)
69
+ raise(LibError, "pcap_open_live(): #{@errbuf.to_s}") if @pcap.null?
70
+
71
+ # call super to get all our ducks in a row
72
+ super(@pcap, opts)
73
+
74
+ set_direction(@direction) if @direction
75
+
76
+ # Cache network and netmask from pcap_lookupdev.
77
+ # These pointers may be used internally (and should get autoreleased)
78
+ @netp, @maskp = nil
79
+ begin
80
+ Caper.lookupnet(@device) do |netp, maskp|
81
+ @netp = netp
82
+ @maskp = maskp
83
+ end
84
+ rescue LibError
85
+ warn "Warning: #{$!}"
86
+ end
87
+
88
+ yield self if block_given?
89
+ end
90
+
91
+ # Returns the dotted notation string for the IPv4 network address for
92
+ # the device used by this pcap interface.
93
+ def network
94
+ return nil unless @netp
95
+ @network ||= @netp.get_array_of_uchar(0,4).join('.')
96
+ end
97
+
98
+ # Returns the dotted notation string for the IPv4 netmask for the device
99
+ # used by this pcap interface.
100
+ def netmask
101
+ return nil unless @maskp
102
+ @netmask ||= @maskp.get_array_of_uchar(0,4).join('.')
103
+ end
104
+
105
+ # Returns the 32-bit numeric representation of the IPv4 network address
106
+ # for this device.
107
+ def network_n32
108
+ return nil unless @netp
109
+ ::FFI::DRY::NetEndian.ntohl(@netp.get_uint32(0))
110
+ end
111
+
112
+ # Returns the 32-bit numeric representation of the IPv4 network address
113
+ # for this device.
114
+ def netmask_n32
115
+ return nil unless @maskp
116
+ ::FFI::DRY::NetEndian.ntohl(@maskp.get_uint32(0))
117
+ end
118
+
119
+ @@have_setdirection = Caper.respond_to?(:pcap_setdirection)
120
+
121
+ # Sets the direction for which packets will be captured.
122
+ #
123
+ # (Not supported on all platforms)
124
+ def set_direction(dir)
125
+ unless @@have_setdirection
126
+ raise(NotImplementedError,
127
+ "pcap_setdirection() is not avaiable from your pcap library")
128
+ end
129
+
130
+ dirs = Caper.enum_type(:pcap_direction_t)
131
+ if Caper.pcap_setdirection(_pcap, dirs[:"pcap_d_#{dir}"]) == 0
132
+ return true
133
+ else
134
+ raise(LibError, "pcap_setdirection(): #{geterr()}", caller)
135
+ end
136
+ end
137
+
138
+ alias direction= set_direction
139
+
140
+ # set the state of non-blocking mode on a capture device
141
+ #
142
+ # @param [Boolean] mode
143
+ #
144
+ # @raise [LibError]
145
+ # On failure, an exception is raised with the relevant error message
146
+ # from libpcap.
147
+ #
148
+ def set_non_blocking(mode)
149
+ mode = mode ? 1 : 0
150
+ if Caper.pcap_setnonblock(_pcap, mode, @errbuf) == 0
151
+ return mode == 1
152
+ else
153
+ raise(LibError, "pcap_setnonblock(): #{@errbuf.to_s}", caller)
154
+ end
155
+ end
156
+
157
+ alias non_blocking= set_non_blocking
158
+
159
+ # get the state of non-blocking mode on a capture device
160
+ #
161
+ # @return [Boolean]
162
+ # non-blocking mode
163
+ #
164
+ # @raise [LibError]
165
+ # On failure, an exception is raised with the relevant error message
166
+ # from libpcap.
167
+ #
168
+ def non_blocking
169
+ if (mode=Caper.pcap_getnonblock(_pcap, @errbuf)) == -1
170
+ raise(LibError, "pcap_getnonblock(): #{@errbuf.to_s}", caller)
171
+ else
172
+ return mode == 1
173
+ end
174
+ end
175
+
176
+ alias non_blocking? non_blocking
177
+
178
+ # Get capture statistics
179
+ #
180
+ # @return [Stats]
181
+ #
182
+ # @raise [LibError]
183
+ # On failure, an exception is raised with the relevant error message
184
+ # from libpcap.
185
+ #
186
+ def stats
187
+ stats = Stat.new
188
+ unless Caper.pcap_stats(_pcap, stats) == 0
189
+ raise(LibError, "pcap_stats(): #{geterr()}")
190
+ end
191
+ return stats
192
+ end
193
+
194
+
195
+ @@have_inject = Caper.respond_to?(:pcap_inject)
196
+
197
+ # Transmit a packet using pcap_inject()
198
+ #
199
+ # (not available on all platforms)
200
+ #
201
+ # @param [Packet, String] obj
202
+ # The packet to send. This can be a Packet or String object.
203
+ #
204
+ # @return [Integer]
205
+ # The number of bytes sent.
206
+ #
207
+ # @raise [ArgumentError]
208
+ # An exception is raised if the pkt object type is incorrect or
209
+ # if it is a Packet and the body pointer is null.
210
+ #
211
+ # @raise [LibError]
212
+ # On failure, an exception is raised with the relevant libpcap
213
+ # error message.
214
+ #
215
+ # @raise [NotImplementedError]
216
+ # If the pcap_inject() function is not available from your libpcap
217
+ # library pcap_sendpacket will be tried, if both are missing, this
218
+ # exception will be raised.
219
+ #
220
+ def inject(pkt)
221
+ if @@have_inject
222
+ if pkt.kind_of? Packet
223
+ len = pkt.caplen
224
+ bufp = pkt.body_ptr
225
+ raise(ArgumentError, "packet data null pointer") if bufp.null?
226
+ elsif pkt.kind_of? String
227
+ len = pkt.size
228
+ bufp = FFI::MemoryPointer.from_string(pkt)
229
+ else
230
+ raise(ArgumentError, "Don't know how to inject #{pkt.class}")
231
+ end
232
+
233
+ if (sent=Caper.pcap_inject(_pcap, bufp, len)) < 0
234
+ raise(LibError, "pcap_inject(): #{geterr()}")
235
+ end
236
+ return sent
237
+ else
238
+ # fake it with sendpacket on windows
239
+ if sendpacket(pkt)
240
+ return (Packet === pkt)? pkt.caplen : pkt.size
241
+ end
242
+ end
243
+ end
244
+
245
+ @@have_sendpacket = Caper.respond_to?(:pcap_sendpacket)
246
+
247
+ # Transmit a packet using pcap_sendpacket()
248
+ #
249
+ # (not available on all platforms)
250
+ #
251
+ # @param [Packet, String] obj
252
+ # The packet to send. This can be a Packet or String object.
253
+ #
254
+ # @return [True]
255
+ # True is returned on success. Otherwise an exception is raised.
256
+ #
257
+ # @raise [ArgumentError]
258
+ # An exception is raised if the pkt object type is incorrect or
259
+ # if it is a Packet and the body pointer is null.
260
+ #
261
+ # @raise [LibError]
262
+ # On failure, an exception is raised with the relevant libpcap
263
+ # error message.
264
+ #
265
+ # @raise [NotImplementedError]
266
+ # If the pcap_sendpacket() function is not available from your libpcap
267
+ # library this exception will be raised.
268
+ #
269
+ def sendpacket(pkt)
270
+ unless @@have_sendpacket
271
+ raise(NotImplementedError,
272
+ "packet injectors are not avaiable from your pcap library")
273
+ end
274
+
275
+ if pkt.kind_of? Packet
276
+ len = pkt.caplen
277
+ bufp = pkt.body_ptr
278
+ raise(ArgumentError, "packet data null pointer") if bufp.null?
279
+ elsif pkt.kind_of? String
280
+ len = pkt.size
281
+ bufp = FFI::MemoryPointer.from_string(pkt)
282
+ else
283
+ raise(ArgumentError, "Don't know how to send #{pkt.class}")
284
+ end
285
+
286
+ if Caper.pcap_sendpacket(_pcap, bufp, len) != 0
287
+ raise(LibError, "pcap_sendpacket(): #{geterr()}")
288
+ end
289
+ return true
290
+ end
291
+
292
+ alias send_packet sendpacket
293
+
294
+ end
295
+
296
+ attach_function :pcap_open_live, [:string, :int, :int, :int, :pointer], :pcap_t
297
+ attach_function :pcap_getnonblock, [:pcap_t, :pointer], :int
298
+ attach_function :pcap_setnonblock, [:pcap_t, :int, :pointer], :int
299
+ attach_function :pcap_stats, [:pcap_t, Stat], :int
300
+
301
+ end