ffi-pcap 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. data/.gitignore +10 -0
  2. data/ChangeLog.rdoc +27 -0
  3. data/LICENSE.txt +23 -0
  4. data/README.rdoc +30 -0
  5. data/Rakefile +26 -0
  6. data/VERSION +1 -0
  7. data/examples/ipfw_divert.rb +49 -0
  8. data/examples/print_bytes.rb +17 -0
  9. data/lib/ffi-pcap.rb +1 -0
  10. data/lib/ffi/pcap.rb +42 -0
  11. data/lib/ffi/pcap/addr.rb +21 -0
  12. data/lib/ffi/pcap/bpf.rb +106 -0
  13. data/lib/ffi/pcap/bsd.rb +98 -0
  14. data/lib/ffi/pcap/capture_wrapper.rb +289 -0
  15. data/lib/ffi/pcap/common_wrapper.rb +175 -0
  16. data/lib/ffi/pcap/copy_handler.rb +38 -0
  17. data/lib/ffi/pcap/crt.rb +15 -0
  18. data/lib/ffi/pcap/data_link.rb +173 -0
  19. data/lib/ffi/pcap/dead.rb +37 -0
  20. data/lib/ffi/pcap/dumper.rb +55 -0
  21. data/lib/ffi/pcap/error_buffer.rb +44 -0
  22. data/lib/ffi/pcap/exceptions.rb +21 -0
  23. data/lib/ffi/pcap/file_header.rb +26 -0
  24. data/lib/ffi/pcap/in_addr.rb +9 -0
  25. data/lib/ffi/pcap/interface.rb +29 -0
  26. data/lib/ffi/pcap/live.rb +303 -0
  27. data/lib/ffi/pcap/offline.rb +53 -0
  28. data/lib/ffi/pcap/packet.rb +164 -0
  29. data/lib/ffi/pcap/packet_header.rb +24 -0
  30. data/lib/ffi/pcap/pcap.rb +252 -0
  31. data/lib/ffi/pcap/stat.rb +57 -0
  32. data/lib/ffi/pcap/time_val.rb +48 -0
  33. data/lib/ffi/pcap/typedefs.rb +27 -0
  34. data/lib/ffi/pcap/version.rb +6 -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 +124 -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 +21 -0
  53. metadata +157 -0
@@ -0,0 +1,38 @@
1
+
2
+ module FFI
3
+ module PCap
4
+
5
+ # CopyHandler is a callback handler for use with CaptureWrapper.loop and
6
+ # CaptureWrapper.dispatch. When used, it works exactly as normal,
7
+ # passing a reference to a pcap wrapper and Packet except for one important
8
+ # difference. A copy of the Packet is yielded to the callback instead of
9
+ # the volatile one received in the pcap_loop() and pcap_dispatch()
10
+ # callbacks.
11
+ #
12
+ # The CopyHandler implements receive_callback to return a _copy_
13
+ # of the Packet object. It is necessary to make a copy to keep allocated
14
+ # references to packets supplied by pcap_loop() and pcap_dispatch()
15
+ # callbacks outside of the scope of a single callback firing on one
16
+ # packet.
17
+ #
18
+ # This handler interface is used by default by CaptureWrapper, so it is
19
+ # generally always safe to keep references to received packets after new
20
+ # packets have been received or even after a pcap interface has been
21
+ # closed. See CaptureWrapper for more information.
22
+ class CopyHandler
23
+ def receive_pcap(pcap, pkt)
24
+ return [pcap, pkt.copy]
25
+ end
26
+ end
27
+
28
+
29
+ # This class only exists for backward compatibility. Setting
30
+ # pcap handler to nil has the same effect now.
31
+ class Handler
32
+ def receive_pcap(*args)
33
+ return args
34
+ end
35
+ end
36
+ end
37
+ end
38
+
@@ -0,0 +1,15 @@
1
+
2
+ module FFI
3
+ module PCap
4
+ module CRT
5
+ extend FFI::Library
6
+
7
+ ffi_lib FFI::Library::LIBC
8
+
9
+ typedef :ulong, :size_t # not all platforms have this set for FFI
10
+
11
+ attach_function :free, [:pointer], :void
12
+ attach_function :memcpy, [:pointer, :pointer, :size_t], :pointer
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,173 @@
1
+ module FFI
2
+ module PCap
3
+
4
+ class DataLink
5
+
6
+ # Several DLT names harvested out of the pcap-bpf.h header file. These
7
+ # are in alphabetical order. Their Array index _does_ _not_ match their
8
+ # pcap DLT value.
9
+ #
10
+ # Don't use this Array for anything except quick reference. Use the
11
+ # 'lookup' class methods for actually resolving name to value
12
+ # mappings or such.
13
+ SOME_DLTS = %w[A429 A653_ICM AIRONET_HEADER APPLE_IP_OVER_IEEE1394
14
+ ARCNET ARCNET_LINUX ATM_CLIP ATM_RFC1483 AURORA AX25 BACNET_MS_TP
15
+ BLUETOOTH_HCI_H4 BLUETOOTH_HCI_H4_WITH_PHDR CAN20B CHAOS CHDLC CISCO_IOS
16
+ C_HDLC DOCSIS ECONET EN10MB EN3MB ENC ERF ERF_ETH ERF_POS FDDI FRELAY
17
+ GCOM_SERIAL GCOM_T1E1 GPF_F GPF_T GPRS_LLC HHDLC IBM_SN IBM_SP IEEE802
18
+ IEEE802_11 IEEE802_11_RADIO IEEE802_11_RADIO_AVS IEEE802_15_4
19
+ IEEE802_15_4_LINUX IEEE802_16_MAC_CPS IEEE802_16_MAC_CPS_RADIO IPFILTER
20
+ IPMB IP_OVER_FC JUNIPER_ATM1 JUNIPER_ATM2 JUNIPER_CHDLC JUNIPER_ES
21
+ JUNIPER_ETHER JUNIPER_FRELAY JUNIPER_GGSN JUNIPER_ISM JUNIPER_MFR
22
+ JUNIPER_MLFR JUNIPER_MLPPP JUNIPER_MONITOR JUNIPER_PIC_PEER JUNIPER_PPP
23
+ JUNIPER_PPPOE JUNIPER_PPPOE_ATM JUNIPER_SERVICES JUNIPER_ST JUNIPER_VP
24
+ LINUX_IRDA LINUX_LAPD LINUX_PPP_WITHDIRECTION LINUX_SLL LOOP LTALK MFR
25
+ MTP2 MTP2_WITH_PHDR MTP3 NULL OLD_PFLOG PCI_EXP PFLOG PFSYNC PPI PPP
26
+ PPP_BSDOS PPP_ETHER PPP_PPPD PPP_SERIAL PPP_WITH_DIRECTION PRISM_HEADER
27
+ PRONET RAIF1 RAW REDBACK_SMARTEDGE RIO SCCP SITA SLIP SLIP_BSDOS SUNATM
28
+ SYMANTEC_FIREWALL TZSP USB USB_LINUX USER0 USER1 USER10 USER11 USER12
29
+ USER13 USER14 USER15 USER2 USER3 USER4 USER5 USER6 USER7 USER8 USER9]
30
+
31
+ # Uses the pcap_datalnk_* functions to lookup a datalink name and value
32
+ # pair.
33
+ #
34
+ # @param [String, Symbol or Integer] l
35
+ # The name or value to lookup. A Symbol is converted to String. Names
36
+ # are case-insensitive.
37
+ #
38
+ # @return [Array]
39
+ # A 2-element array containing [value, name]. Both elements are nil
40
+ # if the lookup failed.
41
+ #
42
+ def self.lookup(l)
43
+ val, name = nil
44
+ l = l.to_s if l.kind_of? Symbol
45
+
46
+ case l
47
+ when String
48
+ if v=name_to_val(l)
49
+ name = val_to_name(v) # get the canonical name
50
+ val = v
51
+ end
52
+ when Integer
53
+ name = val_to_name(l)
54
+ val = l
55
+ else
56
+ raise(ArgumentError, "lookup takes either a String or Integer")
57
+ end
58
+ return [val, name]
59
+ end
60
+
61
+ # Translates a data link type name, which is a DLT_ name with the DLT_
62
+ # removed, to the corresponding data link type numeric value.
63
+ #
64
+ # @param [String or Symbol] n
65
+ # The name to lookup. Names are case-insensitive.
66
+ #
67
+ # @return [Integer or nil]
68
+ # The numeric value for the datalink name or nil on failure.
69
+ def self.name_to_val(n)
70
+ n = n.to_s if n.kind_of?(Symbol)
71
+ if (v=FFI::PCap.pcap_datalink_name_to_val(n)) >= 0
72
+ return v
73
+ end
74
+ end
75
+
76
+ # Translates a data link type value to the corresponding data link
77
+ # type name.
78
+ #
79
+ # @return [String or nil]
80
+ # The string name of the data-link or nil on failure.
81
+ #
82
+ def self.val_to_name(v)
83
+ FFI::PCap.pcap_datalink_val_to_name(v)
84
+ end
85
+
86
+ # @param [String, Symbol or Integer] l
87
+ # The name or value to lookup. A Symbol is converted to String. Names
88
+ # are case-insensitive.
89
+ def self.describe(l)
90
+ l = l.to_s if l.kind_of?(Symbol)
91
+ l = FFI::PCap.pcap_datalink_name_to_val(l) if l.kind_of?(String)
92
+ FFI::PCap.pcap_datalink_val_to_description(l)
93
+ end
94
+
95
+ # FFI::PCap datalink numeric value
96
+ attr_reader :value
97
+
98
+ # Creates a new DataLink object with the specified value or name.
99
+ # The canonical name, value, and description are can be looked up on
100
+ # demand.
101
+ #
102
+ # @param [String or Integer] arg
103
+ # Arg can be a string or number which will be used to look up the
104
+ # datalink.
105
+ #
106
+ # @raise [UnsupportedDataLinkError]
107
+ # An exception is raised if a name is supplied and a lookup for its
108
+ # value fails or if the arg parameter is an invalid type.
109
+ #
110
+ def initialize(arg)
111
+ if arg.kind_of? String or arg.kind_of? Symbol
112
+ unless @value = self.class.name_to_val(arg.to_s)
113
+ raise(UnsupportedDataLinkError, "Invalid DataLink: #{arg.to_s}")
114
+ end
115
+ elsif arg.kind_of? Numeric
116
+ @value = arg
117
+ else
118
+ raise(UnsupportedDataLinkError, "Invalid DataLink: #{arg.inspect}")
119
+ end
120
+ @name = self.class.val_to_name(@value)
121
+ end
122
+
123
+ # Overrides the equality operator so that quick comparisons can be
124
+ # made against other DataLinks, name by String, or value by Integer.
125
+ def ==(other)
126
+ case other
127
+ when DataLink
128
+ return (self.value == other.value)
129
+ when Numeric
130
+ return (self.value == other)
131
+ when Symbol
132
+ return (@value == self.class.name_to_val(other.to_s))
133
+ when String
134
+ return (@value == self.class.name_to_val(other))
135
+ else
136
+ return false
137
+ end
138
+ end
139
+
140
+ # Overrides the sort comparison operator to sort by DLT value.
141
+ def <=>(other)
142
+ self.value <=> other.value
143
+ end
144
+
145
+ # Returns the description of the datalink.
146
+ def description
147
+ @desc ||= self.class.describe(@value)
148
+ end
149
+
150
+ alias desc description
151
+ alias describe description
152
+
153
+ # Returns the canonical String name of the DataLink object
154
+ def name
155
+ @name
156
+ end
157
+
158
+ # Override 'inspect' we'll to always provide the name for irb,
159
+ # pretty_print, etc.
160
+ def inspect
161
+ "<#{self.class}:#{"0x%0.8x" % self.object_id} @value=#{@value}, @name=#{name().inspect}>"
162
+ end
163
+
164
+ alias to_s name
165
+ alias to_i value
166
+ end
167
+
168
+ attach_function :pcap_datalink_name_to_val, [:string], :int
169
+ attach_function :pcap_datalink_val_to_name, [:int], :string
170
+ attach_function :pcap_datalink_val_to_description, [:int], :string
171
+
172
+ end
173
+ end
@@ -0,0 +1,37 @@
1
+ require 'ffi/pcap/common_wrapper'
2
+
3
+ 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
20
+ #
21
+ # @return [Dead]
22
+ # A FFI::PCap::Dead wrapper.
23
+ #
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
33
+
34
+ attach_function :pcap_open_dead, [:int, :int], :pcap_t
35
+
36
+ end
37
+ end
@@ -0,0 +1,55 @@
1
+
2
+ module FFI
3
+ module PCap
4
+
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
11
+
12
+ def initialize(dumper)
13
+ @dumper = dumper
14
+ end
15
+
16
+ def _write(header, bytes)
17
+ FFI::PCap.pcap_dump(@dumper, header, bytes)
18
+ end
19
+
20
+ def write(*args)
21
+ if args.first.is_a? Packet
22
+ write_pkt(*args)
23
+ else
24
+ _write(*args)
25
+ end
26
+ end
27
+
28
+ def write_pkt(pkt)
29
+ _write(pkt.header, pkt.body_ptr)
30
+ end
31
+
32
+ def tell
33
+ FFI::PCap.pcap_dump_ftell(@dumper)
34
+ end
35
+
36
+ def flush
37
+ FFI::PCap.pcap_dump_flush(@dumper)
38
+ end
39
+
40
+ def close
41
+ FFI::PCap.pcap_dump_close(@dumper)
42
+ end
43
+
44
+ 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
+ end
55
+ end
@@ -0,0 +1,44 @@
1
+ module FFI
2
+ module PCap
3
+ class ErrorBuffer < FFI::MemoryPointer
4
+
5
+ # Size of the error buffers
6
+ SIZE = 256
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
32
+
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
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,21 @@
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
7
+
8
+ # A FFI::PCap::LibError is used to convey errors detected by the libpcap
9
+ # native library.
10
+ class LibError < StandardError
11
+ end
12
+
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
+ class TimeoutError < LibError
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,26 @@
1
+
2
+ module FFI
3
+ module PCap
4
+ class FileHeader < FFI::Struct
5
+ include FFI::DRY::StructHelper
6
+
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
16
+
17
+ def datalink
18
+ DataLink.new(self.linktype)
19
+ end
20
+
21
+ def version
22
+ "#{self.version_major}.#{self.version_minor}"
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,9 @@
1
+ module FFI
2
+ module PCap
3
+ class InAddr < FFI::Struct
4
+
5
+ layout :s_addr, [NativeType::UINT8, 4]
6
+
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,29 @@
1
+
2
+ module FFI
3
+ module PCap
4
+
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
10
+
11
+ # interface is loopback
12
+ LOOPBACK = 0x00000001
13
+
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
21
+
22
+ def loopback?
23
+ self.flags & LOOPBACK == LOOPBACK
24
+ end
25
+
26
+ end
27
+
28
+ end
29
+ end