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
@@ -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