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
@@ -1,37 +1,50 @@
1
1
  require 'ffi/pcap/common_wrapper'
2
+ require 'ffi/pcap/data_link'
2
3
 
3
4
  module FFI
4
- module PCap
5
- # A wrapper class for pcap devices opened with open_dead()
6
- class Dead < CommonWrapper
7
- attr_reader :datalink
8
-
9
- # Creates a fake pcap interface for compiling filters or opening a
10
- # capture for output.
11
- #
12
- # @param [Hash] opts
13
- # Options are ignored and passed to the super-class except those below.
14
- #
15
- # @option opts [optional, String, Symbol, Integer] :datalink
16
- # The link-layer type for pcap. nil is equivalent to 0 (aka DLT_NULL).
17
- #
18
- # @option opts [optional, Integer] :snaplen
19
- # The snapshot length for the pcap object. Defaults to FFI::PCap::DEFAULT_SNAPLEN
5
+ module PCap
20
6
  #
21
- # @return [Dead]
22
- # A FFI::PCap::Dead wrapper.
7
+ # A wrapper class for pcap devices opened with {PCap.open_dead}
23
8
  #
24
- def initialize(opts={}, &block)
25
- dl = opts[:datalink] || DataLink.new(0)
26
- @datalink = dl.kind_of?(DataLink) ? dl : DataLink.new(dl)
27
- @snaplen = opts[:snaplen] || DEFAULT_SNAPLEN
28
- @pcap = FFI::PCap.pcap_open_dead(@datalink.value, @snaplen)
29
- raise(LibError, "pcap_open_dead(): returned a null pointer") if @pcap.null?
30
- super(@pcap, opts, &block)
31
- end
32
- end
9
+ class Dead < CommonWrapper
33
10
 
34
- attach_function :pcap_open_dead, [:int, :int], :pcap_t
11
+ attr_reader :datalink
35
12
 
36
- end
13
+ #
14
+ # Creates a fake pcap interface for compiling filters or opening a
15
+ # capture for output.
16
+ #
17
+ # @param [Hash] opts
18
+ # Options are ignored and passed to the super-class except those
19
+ # below.
20
+ #
21
+ # @option opts [optional, String, Symbol, Integer] :datalink
22
+ # The link-layer type for pcap. nil is equivalent to 0
23
+ # (aka DLT_NULL).
24
+ #
25
+ # @option opts [optional, Integer] :snaplen
26
+ # The snapshot length for the pcap object.
27
+ # Defaults to FFI::PCap::DEFAULT_SNAPLEN
28
+ #
29
+ # @return [Dead]
30
+ # A FFI::PCap::Dead wrapper.
31
+ #
32
+ def initialize(opts={}, &block)
33
+ dl = opts[:datalink] || DataLink.new(0)
34
+
35
+ @datalink = dl.kind_of?(DataLink) ? dl : DataLink.new(dl)
36
+ @snaplen = opts[:snaplen] || DEFAULT_SNAPLEN
37
+ @pcap = PCap.pcap_open_dead(@datalink.value, @snaplen)
38
+
39
+ if @pcap.null?
40
+ raise(LibError,"pcap_open_dead(): returned a null pointer",caller)
41
+ end
42
+
43
+ super(@pcap, opts, &block)
44
+ end
45
+
46
+ end
47
+
48
+ attach_function :pcap_open_dead, [:int, :int], :pcap_t
49
+ end
37
50
  end
@@ -1,55 +1,53 @@
1
-
2
1
  module FFI
3
- module PCap
2
+ module PCap
3
+ #
4
+ # See `pcap_dumper_t` in `pcap.h`.
5
+ #
6
+ # A `pcap_dumper`, {Dumper} is handled opaquely so that it can
7
+ # be implemented differently on different platforms. In {PCap}, we
8
+ # simply wrap the `pcap_dumper_t` pointer with a ruby interface.
9
+ #
10
+ class Dumper
11
+
12
+ def initialize(dumper)
13
+ @dumper = dumper
14
+ end
4
15
 
5
- # See pcap_dumper_t in pcap.h
6
- #
7
- # A pcap_dumper, or FFI::PCap::Dumper is handled opaquely so that it can
8
- # be implemented differently on different platforms. In FFI::PCap, we
9
- # simply wrap the pcap_dumper_t pointer with a ruby interface.
10
- class Dumper
16
+ def _write(header, bytes)
17
+ PCap.pcap_dump(@dumper, header, bytes)
18
+ end
11
19
 
12
- def initialize(dumper)
13
- @dumper = dumper
14
- end
20
+ def write(*args)
21
+ case args.first
22
+ when Packet then write_pkt(*args)
23
+ else _write(*args)
24
+ end
25
+ end
15
26
 
16
- def _write(header, bytes)
17
- FFI::PCap.pcap_dump(@dumper, header, bytes)
18
- end
27
+ def write_pkt(pkt)
28
+ _write(pkt.header, pkt.body_ptr)
29
+ end
19
30
 
20
- def write(*args)
21
- if args.first.is_a? Packet
22
- write_pkt(*args)
23
- else
24
- _write(*args)
31
+ def tell
32
+ PCap.pcap_dump_ftell(@dumper)
25
33
  end
26
- end
27
34
 
28
- def write_pkt(pkt)
29
- _write(pkt.header, pkt.body_ptr)
30
- end
35
+ def flush
36
+ PCap.pcap_dump_flush(@dumper)
37
+ end
31
38
 
32
- def tell
33
- FFI::PCap.pcap_dump_ftell(@dumper)
34
- end
39
+ def close
40
+ PCap.pcap_dump_close(@dumper)
41
+ end
35
42
 
36
- def flush
37
- FFI::PCap.pcap_dump_flush(@dumper)
38
43
  end
39
44
 
40
- def close
41
- FFI::PCap.pcap_dump_close(@dumper)
42
- end
45
+ # XXX not sure if we even want file FILE IO stuff yet
46
+ #attach_function :pcap_dump_file, [:pcap_dumper_t], :FILE
43
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
44
52
  end
45
-
46
- # XXX not sure if we even want file FILE IO stuff yet
47
- #attach_function :pcap_dump_file, [:pcap_dumper_t], :FILE
48
-
49
- attach_function :pcap_dump_ftell, [:pcap_dumper_t], :long
50
- attach_function :pcap_dump_flush, [:pcap_dumper_t], :int
51
- attach_function :pcap_dump_close, [:pcap_dumper_t], :void
52
- attach_function :pcap_dump, [:pointer, PacketHeader, :pointer], :void
53
-
54
53
  end
55
- end
@@ -1,44 +1,29 @@
1
1
  module FFI
2
- module PCap
3
- class ErrorBuffer < FFI::MemoryPointer
2
+ module PCap
3
+ class ErrorBuffer < FFI::Buffer
4
4
 
5
- # Size of the error buffers
6
- SIZE = 256
5
+ # Size of the error buffers
6
+ SIZE = 256
7
7
 
8
- # Creates a new ErrorBuffer object. Because of wierdness in JRuby
9
- # when trying to subclass FFI::Buffer, always use this instead of
10
- # 'new()'
11
- #
12
- # See http://github.com/ffi/ffi/issues#issue/27
13
- def self.create()
14
- new(SIZE)
15
- end
16
-
17
- #
18
- # Creates a new ErrorBuffer object.
19
- # The argument is nil and is only present for compatability with JRuby.
20
- #
21
- # See http://github.com/ffi/ffi/issues#issue/27
22
- def initialize(arg=nil)
23
- super(SIZE)
24
- end
25
-
26
- #
27
- # Returns the error message within the error buffer.
28
- #
29
- def to_s
30
- get_string(0)
31
- end
8
+ #
9
+ # Creates a new {ErrorBuffer} object.
10
+ #
11
+ # @param [FFI::Pointer] ptr
12
+ # Optional pointer to an existing {ErrorBuffer}.
13
+ #
14
+ def initialize(ptr=nil)
15
+ if ptr then super(ptr)
16
+ else super(SIZE)
17
+ end
18
+ end
32
19
 
33
- # Older JRuby/ffi versions of MemoryPointer and Buffer don't have a size
34
- # method. We override it here to ensure we can use it.
35
- def size
36
- begin
37
- super()
38
- rescue NoMethodError
39
- SIZE
20
+ #
21
+ # Returns the error message within the error buffer.
22
+ #
23
+ def to_s
24
+ get_string(0)
40
25
  end
26
+
41
27
  end
42
28
  end
43
29
  end
44
- end
@@ -1,21 +1,27 @@
1
1
  module FFI
2
- module PCap
3
- # A FFI::PCap::UnsupportedDataLinkError indicates an invalid or unsupported
4
- # DataLink Layer Type (DLT) value or name.
5
- class UnsupportedDataLinkError < StandardError
6
- end
2
+ module PCap
3
+ #
4
+ # A {UnsupportedDataLinkError} indicates an invalid or
5
+ # unsupported DataLink Layer Type (DLT) value or name.
6
+ #
7
+ class UnsupportedDataLinkError < StandardError
8
+ end
7
9
 
8
- # A FFI::PCap::LibError is used to convey errors detected by the libpcap
9
- # native library.
10
- class LibError < StandardError
11
- end
10
+ #
11
+ # A {LibError} is used to convey errors detected by the libpcap
12
+ # native library.
13
+ #
14
+ class LibError < StandardError
15
+ end
12
16
 
13
- # A FFI::PCap::ReadError is a sub-class of FFI::PCap::LibError that indicates a
14
- # problem reading from a pcap device.
15
- class ReadError < LibError
16
- end
17
+ #
18
+ # A {ReadError} is a sub-class of {LibError} that
19
+ # indicates a problem reading from a pcap device.
20
+ #
21
+ class ReadError < LibError
22
+ end
17
23
 
18
- class TimeoutError < LibError
24
+ class TimeoutError < LibError
25
+ end
19
26
  end
20
27
  end
21
- end
@@ -1,26 +1,32 @@
1
+ require 'ffi/pcap/typedefs'
2
+ require 'ffi/pcap/data_link'
3
+
4
+ require 'ffi_dry'
1
5
 
2
6
  module FFI
3
- module PCap
4
- class FileHeader < FFI::Struct
5
- include FFI::DRY::StructHelper
7
+ module PCap
8
+ class FileHeader < FFI::Struct
6
9
 
7
- dsl_layout do
8
- field :magic, :bpf_uint32
9
- field :version_major, :ushort
10
- field :version_minor, :ushort
11
- field :thiszone, :bpf_int32
12
- field :sigfigs, :bpf_uint32
13
- field :snaplen, :bpf_uint32
14
- field :linktype, :bpf_uint32
15
- end
10
+ include FFI::DRY::StructHelper
16
11
 
17
- def datalink
18
- DataLink.new(self.linktype)
19
- end
12
+ dsl_layout do
13
+ field :magic, :bpf_uint32
14
+ field :version_major, :ushort
15
+ field :version_minor, :ushort
16
+ field :thiszone, :bpf_int32
17
+ field :sigfigs, :bpf_uint32
18
+ field :snaplen, :bpf_uint32
19
+ field :linktype, :bpf_uint32
20
+ end
21
+
22
+ def datalink
23
+ @data_link ||= DataLink.new(self.linktype)
24
+ end
25
+
26
+ def version
27
+ "#{self.version_major}.#{self.version_minor}"
28
+ end
20
29
 
21
- def version
22
- "#{self.version_major}.#{self.version_minor}"
23
30
  end
24
31
  end
25
32
  end
26
- end
@@ -1,9 +1,9 @@
1
1
  module FFI
2
- module PCap
3
- class InAddr < FFI::Struct
2
+ module PCap
3
+ class InAddr < FFI::Struct
4
4
 
5
- layout :s_addr, [NativeType::UINT8, 4]
5
+ layout :s_addr, [:uint8, 4]
6
6
 
7
+ end
7
8
  end
8
9
  end
9
- end
@@ -1,29 +1,31 @@
1
+ require 'ffi/pcap/typedefs'
1
2
 
2
3
  module FFI
3
- module PCap
4
+ module PCap
5
+ #
6
+ # Item in a list of interfaces.
7
+ #
8
+ # See `pcap_if` struct in `pcap.h`.
9
+ #
10
+ class Interface < FFI::Struct
4
11
 
5
- # Item in a list of interfaces.
6
- #
7
- # See pcap_if struct in pcap.h
8
- class Interface < FFI::Struct
9
- include FFI::DRY::StructHelper
12
+ include FFI::DRY::StructHelper
10
13
 
11
- # interface is loopback
12
- LOOPBACK = 0x00000001
14
+ # interface is loopback
15
+ LOOPBACK = 0x00000001
13
16
 
14
- dsl_layout do
15
- p_struct :next, ::FFI::PCap::Interface
16
- field :name, :string, :desc => 'name used by pcap_open_live()'
17
- field :description, :string, :desc => 'text description, or NULL'
18
- p_struct :addresses, ::FFI::PCap::Addr, :desc => 'address linked list'
19
- field :flags, :bpf_uint32, :desc => 'PCAP_IF_ interface flags'
20
- end
17
+ dsl_layout do
18
+ p_struct :next, ::FFI::PCap::Interface
19
+ field :name, :string, :desc => 'name used by pcap_open_live()'
20
+ field :description, :string, :desc => 'text description, or NULL'
21
+ p_struct :addresses, ::FFI::PCap::Addr, :desc => 'address linked list'
22
+ field :flags, :bpf_uint32, :desc => 'PCAP_IF_ interface flags'
23
+ end
21
24
 
22
- def loopback?
23
- self.flags & LOOPBACK == LOOPBACK
24
- end
25
+ def loopback?
26
+ self.flags & LOOPBACK == LOOPBACK
27
+ end
25
28
 
29
+ end
26
30
  end
27
-
28
- end
29
31
  end
@@ -1,303 +1,347 @@
1
1
  require 'ffi/pcap/capture_wrapper'
2
2
 
3
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
4
+ module PCap
5
+ begin
6
+ attach_function :pcap_setdirection, [:pcap_t, :pcap_direction_t], :int
7
+ rescue FFI::NotFoundError
8
+ end
9
9
 
10
- begin
11
- attach_function :pcap_sendpacket, [:pcap_t, :pointer, :int], :int
12
- rescue FFI::NotFoundError
13
- end
10
+ begin
11
+ attach_function :pcap_sendpacket, [:pcap_t, :pointer, :int], :int
12
+ rescue FFI::NotFoundError
13
+ end
14
14
 
15
- begin
16
- attach_function :pcap_inject, [:pcap_t, :pointer, :int], :int
17
- rescue FFI::NotFoundError
18
- end
15
+ begin
16
+ attach_function :pcap_inject, [:pcap_t, :pointer, :int], :int
17
+ rescue FFI::NotFoundError
18
+ end
19
19
 
20
+ class Live < CaptureWrapper
21
+
22
+ DEFAULT_TO_MS = 1000 # Default timeout for pcap_open_live()
23
+
24
+ attr_reader :device, :promisc, :timeout, :direction
25
+
26
+ #
27
+ # Creates a pcap interface for capturing from the network.
28
+ #
29
+ # @param [Hash] opts
30
+ # Options are ignored and passed to the super-class except those
31
+ # below.
32
+ #
33
+ # @option opts [String, nil] :device, :dev
34
+ # The device to open. On some platforms, this can be "any".
35
+ # If nil or unspecified {PCap.lookupdev} is called to obtain a
36
+ # default device.
37
+ #
38
+ # @option opts [Integer] :snaplen
39
+ # The snapshot length for the pcap object.
40
+ # Defaults to DEFAULT_SNAPLEN
41
+ #
42
+ # @option opts [Boolean] :promisc
43
+ # Specifies if the interface is to be put into promiscuous mode.
44
+ # Defaults to false.
45
+ #
46
+ # @option opts [Integer] :timeout
47
+ # Specifies the read timeout in milliseconds.
48
+ # Defaults to DEFAULT_TO_MS
49
+ #
50
+ # @return [Live]
51
+ # A live wrapper.
52
+ #
53
+ # @raise [LibError]
54
+ # On failure, an exception is raised with the relevant error
55
+ # message from libpcap.
56
+ #
57
+ # @raise [ArgumentError]
58
+ # May raise an exception if a :device cannot be autodetected using
59
+ # {PCap.lookupdev} for any reason. This should never happen on
60
+ # most platforms.
61
+ #
62
+ def initialize(opts=nil)
63
+ opts ||= {}
64
+
65
+ @device = (opts[:device] || opts[:dev] || PCap.lookupdev)
66
+
67
+ unless @device
68
+ raise(ArgumentError,"Couldn't detect a device. One must be specified",caller)
69
+ end
20
70
 
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
71
+ @snaplen = opts[:snaplen] || DEFAULT_SNAPLEN
72
+ @promisc = opts[:promisc] ? 1 : 0
73
+ @timeout = opts[:timeout] || DEFAULT_TO_MS
74
+ @direction = (opts[:direction] || opts[:dir])
62
75
 
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])
76
+ @errbuf = ErrorBuffer.new
77
+ @pcap = PCap.pcap_open_live(@device, @snaplen, @promisc, @timeout, @errbuf)
67
78
 
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?
79
+ if @pcap.null?
80
+ raise(LibError, "pcap_open_live(): #{@errbuf}",caller)
81
+ end
71
82
 
72
- # call super to get all our ducks in a row
73
- super(@pcap, opts)
83
+ # call super to get all our ducks in a row
84
+ super(@pcap, opts)
85
+
86
+ set_direction(@direction) if @direction
87
+
88
+ # Cache network and netmask from pcap_lookupdev.
89
+ # These pointers may be used internally (and should get autoreleased)
90
+ @netp, @maskp = nil
91
+ begin
92
+ PCap.lookupnet(@device) do |netp, maskp|
93
+ @netp = netp
94
+ @maskp = maskp
95
+ end
96
+ rescue LibError
97
+ warn "Warning: #{$!}"
98
+ end
74
99
 
75
- set_direction(@direction) if @direction
100
+ yield self if block_given?
101
+ end
76
102
 
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
103
+ #
104
+ # Returns the dotted notation string for the IPv4 network address for
105
+ # the device used by this pcap interface.
106
+ #
107
+ def network
108
+ if @netp
109
+ @network ||= @netp.get_array_of_uchar(0,4).join('.')
84
110
  end
85
- rescue LibError
86
- warn "Warning: #{$!}"
87
111
  end
88
112
 
89
- yield self if block_given?
90
- end
113
+ #
114
+ # Returns the dotted notation string for the IPv4 netmask for the
115
+ # device used by this pcap interface.
116
+ #
117
+ def netmask
118
+ if @maskp
119
+ @netmask ||= @maskp.get_array_of_uchar(0,4).join('.')
120
+ end
121
+ end
91
122
 
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
123
+ #
124
+ # Returns the 32-bit numeric representation of the IPv4 network
125
+ # address for this device.
126
+ #
127
+ def network_n32
128
+ if @netp
129
+ ::FFI::DRY::NetEndian.ntohl(@netp.get_uint32(0))
130
+ end
131
+ end
98
132
 
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
133
+ #
134
+ # Returns the 32-bit numeric representation of the IPv4 network
135
+ # address for this device.
136
+ #
137
+ def netmask_n32
138
+ if @maskp
139
+ ::FFI::DRY::NetEndian.ntohl(@maskp.get_uint32(0))
140
+ end
141
+ end
105
142
 
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
143
+ @@have_setdirection = PCap.respond_to?(:pcap_setdirection)
112
144
 
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
145
+ #
146
+ # Sets the direction for which packets will be captured.
147
+ #
148
+ # (Not supported on all platforms)
149
+ #
150
+ def set_direction(dir)
151
+ unless @@have_setdirection
152
+ raise(NotImplementedError,"pcap_setdirection() is not avaiable from your pcap library",caller)
153
+ end
119
154
 
120
- @@have_setdirection = FFI::PCap.respond_to?(:pcap_setdirection)
155
+ dirs = PCap.enum_type(:pcap_direction_t)
121
156
 
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")
157
+ if PCap.pcap_setdirection(_pcap, dirs[:"pcap_d_#{dir}"]) == 0
158
+ return true
159
+ else
160
+ raise(LibError,"pcap_setdirection(): #{geterr}",caller)
161
+ end
129
162
  end
130
163
 
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)
164
+ alias direction= set_direction
165
+
166
+ #
167
+ # set the state of non-blocking mode on a capture device
168
+ #
169
+ # @param [Boolean] mode
170
+ #
171
+ # @raise [LibError]
172
+ # On failure, an exception is raised with the relevant error
173
+ # message from libpcap.
174
+ #
175
+ def set_non_blocking(mode)
176
+ mode = mode ? 1 : 0
177
+
178
+ if PCap.pcap_setnonblock(_pcap, mode, @errbuf) == 0
179
+ return (mode == 1)
180
+ else
181
+ raise(LibError,"pcap_setnonblock(): #{@errbuf}",caller)
182
+ end
136
183
  end
137
- end
138
184
 
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)
185
+ alias non_blocking= set_non_blocking
186
+ alias nonblocking= set_non_blocking
187
+
188
+ #
189
+ # get the state of non-blocking mode on a capture device
190
+ #
191
+ # @return [Boolean]
192
+ # non-blocking mode
193
+ #
194
+ # @raise [LibError]
195
+ # On failure, an exception is raised with the relevant error
196
+ # message from libpcap.
197
+ #
198
+ def non_blocking
199
+ mode = PCap.pcap_getnonblock(_pcap, @errbuf)
200
+
201
+ if mode == -1
202
+ raise(LibError,"pcap_getnonblock(): #{@errbuf}",caller)
203
+ else
204
+ return mode == 1
205
+ end
155
206
  end
156
- end
157
207
 
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
208
+ alias non_blocking? non_blocking
209
+
210
+ #
211
+ # Get capture statistics
212
+ #
213
+ # @return [Stats]
214
+ #
215
+ # @raise [LibError]
216
+ # On failure, an exception is raised with the relevant error
217
+ # message from libpcap.
218
+ #
219
+ def stats
220
+ stats = Stat.new
221
+
222
+ unless PCap.pcap_stats(_pcap, stats) == 0
223
+ raise(LibError,"pcap_stats(): #{geterr}",caller)
224
+ end
225
+
226
+ return stats
174
227
  end
175
- end
176
228
 
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()}")
229
+ @@have_inject = PCap.respond_to?(:pcap_inject)
230
+
231
+ #
232
+ # Transmit a packet using pcap_inject()
233
+ #
234
+ # (not available on all platforms)
235
+ #
236
+ # @param [Packet, String] obj
237
+ # The packet to send. This can be a Packet or String object.
238
+ #
239
+ # @return [Integer]
240
+ # The number of bytes sent.
241
+ #
242
+ # @raise [ArgumentError]
243
+ # An exception is raised if the pkt object type is incorrect or
244
+ # if it is a Packet and the body pointer is null.
245
+ #
246
+ # @raise [LibError]
247
+ # On failure, an exception is raised with the relevant libpcap
248
+ # error message.
249
+ #
250
+ # @raise [NotImplementedError]
251
+ # If the pcap_inject() function is not available from your libpcap
252
+ # library pcap_sendpacket will be tried, if both are missing, this
253
+ # exception will be raised.
254
+ #
255
+ def inject(pkt)
256
+ if @@have_inject
257
+ if pkt.kind_of?(Packet)
258
+ len = pkt.caplen
259
+ bufp = pkt.body_ptr
260
+
261
+ if bufp.null?
262
+ raise(ArgumentError,"packet data null pointer",caller)
263
+ end
264
+ elsif pkt.kind_of?(String)
265
+ len = pkt.size
266
+ bufp = FFI::MemoryPointer.from_string(pkt)
267
+ else
268
+ raise(ArgumentError,"Don't know how to inject #{pkt.class}",caller)
269
+ end
270
+
271
+ sent = PCap.pcap_inject(_pcap, bufp, len)
272
+
273
+ if sent < 0
274
+ raise(LibError,"pcap_inject(): #{geterr}",caller)
275
+ end
276
+
277
+ return sent
278
+ else
279
+ # fake it with sendpacket on windows
280
+ if sendpacket(pkt)
281
+ return (Packet === pkt) ? pkt.caplen : pkt.size
282
+ end
283
+ end
191
284
  end
192
- return stats
193
- end
194
285
 
286
+ @@have_sendpacket = PCap.respond_to?(:pcap_sendpacket)
287
+
288
+ #
289
+ # Transmit a packet using pcap_sendpacket()
290
+ #
291
+ # (not available on all platforms)
292
+ #
293
+ # @param [Packet, String] obj
294
+ # The packet to send. This can be a Packet or String object.
295
+ #
296
+ # @return [True]
297
+ # True is returned on success. Otherwise an exception is raised.
298
+ #
299
+ # @raise [ArgumentError]
300
+ # An exception is raised if the pkt object type is incorrect or
301
+ # if it is a Packet and the body pointer is null.
302
+ #
303
+ # @raise [LibError]
304
+ # On failure, an exception is raised with the relevant libpcap
305
+ # error message.
306
+ #
307
+ # @raise [NotImplementedError]
308
+ # If the pcap_sendpacket() function is not available from your
309
+ # libpcap library this exception will be raised.
310
+ #
311
+ def sendpacket(pkt)
312
+ unless @@have_sendpacket
313
+ raise(NotImplementedError,"packet injectors are not avaiable from your pcap library",caller)
314
+ end
195
315
 
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
316
  if pkt.kind_of? Packet
224
317
  len = pkt.caplen
225
318
  bufp = pkt.body_ptr
226
- raise(ArgumentError, "packet data null pointer") if bufp.null?
319
+
320
+ if bufp.null?
321
+ raise(ArgumentError,"packet data null pointer",caller)
322
+ end
227
323
  elsif pkt.kind_of? String
228
324
  len = pkt.size
229
325
  bufp = FFI::MemoryPointer.from_string(pkt)
230
326
  else
231
- raise(ArgumentError, "Don't know how to inject #{pkt.class}")
327
+ raise(ArgumentError,"Don't know how to send #{pkt.class}",caller)
232
328
  end
233
329
 
234
- if (sent=FFI::PCap.pcap_inject(_pcap, bufp, len)) < 0
235
- raise(LibError, "pcap_inject(): #{geterr()}")
330
+ if PCap.pcap_sendpacket(_pcap, bufp, len) != 0
331
+ raise(LibError,"pcap_sendpacket(): #{geterr}",caller)
236
332
  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
333
 
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")
334
+ return true
274
335
  end
275
336
 
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
337
+ alias send_packet sendpacket
286
338
 
287
- if FFI::PCap.pcap_sendpacket(_pcap, bufp, len) != 0
288
- raise(LibError, "pcap_sendpacket(): #{geterr()}")
289
- end
290
- return true
291
339
  end
292
340
 
293
- alias send_packet sendpacket
341
+ attach_function :pcap_open_live, [:string, :int, :int, :int, :pointer], :pcap_t
342
+ attach_function :pcap_getnonblock, [:pcap_t, :pointer], :int
343
+ attach_function :pcap_setnonblock, [:pcap_t, :int, :pointer], :int
344
+ attach_function :pcap_stats, [:pcap_t, Stat], :int
294
345
 
295
346
  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
347
  end