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,53 +1,60 @@
1
1
  require 'ffi/pcap/capture_wrapper'
2
2
 
3
3
  module FFI
4
- module PCap
5
- # A wrapper class for pcap devices opened with open_offline()
6
- #
7
- class Offline < CaptureWrapper
8
- attr_accessor :path
9
-
10
- # Creates a pcap interface for reading saved capture files.
11
- #
12
- # @param [String] path
13
- # The path to the file to open.
14
- #
15
- # @param [Hash] opts
16
- # Options are ignored and passed to the super-class except for those
17
- # below.
18
- #
19
- # @option opts [ignored] :path
20
- # The :path option will be overridden with the value of the path
21
- # argument. If specified in opts, its value will be ignored.
4
+ module PCap
22
5
  #
23
- # @return [Offline]
24
- # A FFI::PCap::Offline wrapper.
6
+ # A wrapper class for pcap devices opened with {PCap.open_offline}.
25
7
  #
26
- # @raise [LibError]
27
- # On failure, an exception is raised with the relevant error
28
- # message from libpcap.
29
- #
30
- def initialize(path, opts={}, &block)
31
- @path = path
32
- @errbuf = ErrorBuffer.create()
33
- @pcap = FFI::PCap.pcap_open_offline(File.expand_path(@path), @errbuf)
34
- raise(LibError, "pcap_open_offline(): #{@errbuf.to_s}") if @pcap.null?
35
- super(@pcap, opts, &block)
36
- end
8
+ class Offline < CaptureWrapper
37
9
 
38
- def swapped?
39
- FFI::PCap.pcap_is_swapped(_pcap) == 1 ? true : false
40
- end
10
+ attr_accessor :path
41
11
 
42
- def file_version
43
- "#{FFI::PCap.pcap_major_version(_pcap)}.#{FFI::PCap.pcap_minor_version(_pcap)}"
44
- end
45
- end
12
+ #
13
+ # Creates a pcap interface for reading saved capture files.
14
+ #
15
+ # @param [String] path
16
+ # The path to the file to open.
17
+ #
18
+ # @param [Hash] opts
19
+ # Options are ignored and passed to the super-class except for those
20
+ # below.
21
+ #
22
+ # @option opts [ignored] :path
23
+ # The :path option will be overridden with the value of the path
24
+ # argument. If specified in opts, its value will be ignored.
25
+ #
26
+ # @return [Offline]
27
+ # A offline wrapper.
28
+ #
29
+ # @raise [LibError]
30
+ # On failure, an exception is raised with the relevant error
31
+ # message from libpcap.
32
+ #
33
+ def initialize(path, opts={}, &block)
34
+ @path = path
35
+ @errbuf = ErrorBuffer.new
36
+ @pcap = PCap.pcap_open_offline(File.expand_path(@path), @errbuf)
46
37
 
47
- attach_function :pcap_open_offline, [:string, :pointer], :pcap_t
48
- attach_function :pcap_is_swapped, [:pcap_t], :int
49
- attach_function :pcap_major_version, [:pcap_t], :int
50
- attach_function :pcap_minor_version, [:pcap_t], :int
38
+ if @pcap.null?
39
+ raise(LibError,"pcap_open_offline(): #{@errbuf}",caller)
40
+ end
51
41
 
52
- end
42
+ super(@pcap, opts, &block)
43
+ end
44
+
45
+ def swapped?
46
+ PCap.pcap_is_swapped(_pcap) == 1 ? true : false
47
+ end
48
+
49
+ def file_version
50
+ "#{PCap.pcap_major_version(_pcap)}.#{PCap.pcap_minor_version(_pcap)}"
51
+ end
52
+
53
+ end
54
+
55
+ attach_function :pcap_open_offline, [:string, :pointer], :pcap_t
56
+ attach_function :pcap_is_swapped, [:pcap_t], :int
57
+ attach_function :pcap_major_version, [:pcap_t], :int
58
+ attach_function :pcap_minor_version, [:pcap_t], :int
59
+ end
53
60
  end
@@ -1,164 +1,207 @@
1
+ require 'ffi/pcap/packet_header'
2
+
1
3
  module FFI
2
- module PCap
3
- class Packet
4
- attr_reader :body_ptr, :header
5
-
6
- # Creates a Packet from a Ruby string object.
7
- #
8
- # see new() for more information about the arguments.
9
- def self.from_string(body, opts={})
10
- new(nil, body, opts)
11
- end
4
+ module PCap
5
+ class Packet
12
6
 
13
- # Allocates a Packet using new memory. Used primarily for pcap_loop
14
- # and pcap_dispatch to retain packets after new ones have been received
15
- # or a pcap device is closed.
16
- def self.allocate(phdr, buf)
17
- new(phdr, buf).copy()
18
- end
7
+ attr_reader :body_ptr, :header
19
8
 
20
- # @param [PacketHeader, nil] hdr
21
- # The pcap pkthdr struct for this packet or nil. hdr may only be
22
- # nil if a string is supplied for the body. A header will be
23
- # created automatically and set_body called with opts.
24
- #
25
- # @param [FFI::Pointer, String] body
26
- # A string or pointer for the body of the packet. A String may
27
- # only be specified if hdr is set to nil.
28
- #
29
- # @param [optional, Hash] opts
30
- # Specifies additional options at creation time. Only those
31
- # below are applicable for all initiatialization styles.
32
- # All other options are sent to set_body(), but only if the header
33
- # is nil and body is a String. See set_body() for more info.
34
- #
35
- # @option opts [optional, Time] :time, :timestamp
36
- # Sets the timestamp in the header.
37
- #
38
- # @raise [ArgumentError, TypeError]
39
- # An exception is raised if any of the parameter rules described
40
- # are not followed.
41
- #
42
- def initialize(hdr, body, opts={})
43
- o = opts.dup
44
- ts = o.delete(:time) || o.delete(:timestamp)
45
- case hdr
46
- when PacketHeader
47
- raise(ArgumentError, "NULL header pointer") if hdr.to_ptr.null?
48
- @header = hdr
49
- when ::FFI::Pointer
50
- raise(ArgumentError, "NULL header pointer") if hdr.null?
51
- @header = PacketHeader.new(hdr)
52
- when nil
53
- if body.is_a? String
54
- set_body(body, o)
55
- else
56
- raise(TypeError, "invalid body with nil header: #{body.class}")
57
- end
58
- else
59
- raise(TypeError, "invalid header: #{hdr.class}")
9
+ # Unmarshall a marshalled {Packet}
10
+ def self._load(s)
11
+ time, body = Marshal.load(s)
12
+ self.from_string(body, :timestamp => time)
60
13
  end
61
-
62
- @header.ts.time = ts if ts
63
14
 
64
- unless @body_ptr
65
- if body.is_a?(FFI::Pointer) and not body.null?
66
- @body_ptr = body
15
+ # Marshal this {Packet}
16
+ def _dump(lv)
17
+ Marshal.dump([self.time, self.body])
18
+ end
19
+
20
+ #
21
+ # Creates a {Packet} from a Ruby string object.
22
+ #
23
+ # @see new
24
+ #
25
+ def self.from_string(body, opts={})
26
+ new(nil, body, opts)
27
+ end
28
+
29
+ #
30
+ # Allocates a {Packet} using new memory. Used primarily for
31
+ # `pcap_loop` and `pcap_dispatch` to retain packets after new ones
32
+ # have been received or a pcap device is closed.
33
+ #
34
+ def self.allocate(phdr, buf)
35
+ new(phdr, buf).copy()
36
+ end
37
+
38
+ #
39
+ # @param [PacketHeader, nil] hdr
40
+ # The pcap pkthdr struct for this packet or `nil`. hdr may only be
41
+ # nil if a string is supplied for the body. A header will be
42
+ # created automatically and {#set_body} called with opts.
43
+ #
44
+ # @param [FFI::Pointer, String] body
45
+ # A string or pointer for the body of the packet. A String may
46
+ # only be specified if hdr is set to `nil`.
47
+ #
48
+ # @param [optional, Hash] opts
49
+ # Specifies additional options at creation time. Only those
50
+ # below are applicable for all initiatialization styles.
51
+ # All other options are sent to {#set_body}, but only if the header
52
+ # is nil and body is a String. See {#set_body} for more info.
53
+ #
54
+ # @option opts [optional, Time] :time, :timestamp
55
+ # Sets the timestamp in the header.
56
+ #
57
+ # @raise [ArgumentError, TypeError]
58
+ # An exception is raised if any of the parameter rules described
59
+ # are not followed.
60
+ #
61
+ def initialize(hdr, body, opts={})
62
+ o = opts.dup
63
+ ts = (o.delete(:time) || o.delete(:timestamp))
64
+
65
+ case hdr
66
+ when PacketHeader
67
+ if hdr.to_ptr.null?
68
+ raise(ArgumentError,"NULL header pointer",caller)
69
+ end
70
+
71
+ @header = hdr
72
+ when ::FFI::Pointer
73
+ if hdr.null?
74
+ raise(ArgumentError, "NULL header pointer",caller)
75
+ end
76
+
77
+ @header = PacketHeader.new(hdr)
78
+ when nil
79
+ if body.is_a?(String)
80
+ set_body(body, o)
81
+ else
82
+ raise(TypeError,"invalid body with nil header: #{body.class}",caller)
83
+ end
67
84
  else
68
- raise(TypeError, "invalid body for header: #{body.class}")
85
+ raise(TypeError,"invalid header: #{hdr.class}",caller)
86
+ end
87
+
88
+ @header.ts.time = ts if ts
89
+
90
+ unless @body_ptr
91
+ if (body.is_a?(FFI::Pointer) && !(body.null?))
92
+ @body_ptr = body
93
+ else
94
+ raise(TypeError,"invalid body for header: #{body.class}",caller)
95
+ end
69
96
  end
70
97
  end
71
- end
72
98
 
73
- # Sets the body from a string. A pointer is automatically derived from
74
- #
75
- # @param [String] data
76
- # The body to set
77
- #
78
- # @param [Hash] opts
79
- # Body length options.
80
- #
81
- # @option opts [optional, Integer] :caplen, :captured
82
- # The captured length (or snaplen) for this packet.
83
- # Length of data portion present. Defaults to body.size(). If
84
- # caplen is larger than the body, then it is overridden with body.size.
85
- #
86
- # @option opts [optional, Integer] :len, :length
87
- # The total length of the packet (off wire). Defaults to caplen. If
88
- # If :length is less than the :caplen, it is overridden as :caplen.
89
- #
90
- #
91
- # @return [String]
92
- # Returns the data as supplied per attr_writer convention.
93
- #
94
- def set_body(data, opts={})
95
- cl = opts[:caplen] || opts[:captured] || data.size
96
- l = opts[:length] || opts[:len] || cl
97
- clen = (cl < data.size) ? cl : data.size
98
- len = (l < clen) ? clen : l
99
-
100
- @header ||= PacketHeader.new
101
- @header.caplen = len || @header.caplen
102
- @header.len = len || @header.caplen
103
- @body_ptr = FFI::MemoryPointer.from_string(data)
104
- return self
105
- end
99
+ #
100
+ # Sets the body from a string. A pointer is automatically derived
101
+ # from.
102
+ #
103
+ # @param [String] data
104
+ # The body to set
105
+ #
106
+ # @param [Hash] opts
107
+ # Body length options.
108
+ #
109
+ # @option opts [optional, Integer] :caplen, :captured
110
+ # The captured length (or snaplen) for this packet.
111
+ # Length of data portion present. Defaults to `body.size()`. If
112
+ # caplen is larger than the body, then it is overridden with
113
+ # `body.size`.
114
+ #
115
+ # @option opts [optional, Integer] :len, :length
116
+ # The total length of the packet (off wire). Defaults to caplen.
117
+ # If :length is less than the :caplen, it is overridden as :caplen.
118
+ #
119
+ # @return [String]
120
+ # Returns the data as supplied per `attr_writer` convention.
121
+ #
122
+ def set_body(data, opts={})
123
+ cl = (opts[:caplen] || opts[:captured] || data.size)
124
+ l = (opts[:length] || opts[:len] || cl)
125
+
126
+ clen = [cl, data.size].min
127
+ len = [l, clen].max
128
+
129
+ @header ||= PacketHeader.new
130
+ @header.caplen = len || @header.caplen
131
+ @header.len = len || @header.caplen
132
+ @body_ptr = MemoryPointer.from_string(data)
133
+ return self
134
+ end
106
135
 
107
- alias body= set_body
136
+ alias body= set_body
108
137
 
109
- # @return [String]
110
- # A String representation of the packet data.
111
- # The reference to the string is not kept by the object and changes
112
- # won't affect the data in this packet.
113
- def body
114
- @body_ptr.read_string(@header.caplen)
115
- end
138
+ #
139
+ # @return [String]
140
+ # A String representation of the packet data.
141
+ # The reference to the string is not kept by the object and changes
142
+ # won't affect the data in this packet.
143
+ #
144
+ def body
145
+ @body_ptr.read_string(@header.caplen)
146
+ end
116
147
 
117
- # @return [Time]
118
- # Returns the pcap timestamp as a Time object
119
- def time
120
- @header.ts.time
121
- end
148
+ #
149
+ # @return [Time]
150
+ # Returns the pcap timestamp as a Time object
151
+ #
152
+ def time
153
+ @header.ts.time
154
+ end
122
155
 
123
- alias timestamp time
156
+ alias timestamp time
124
157
 
125
- # Sets the pcap timestamp.
126
- def time=(t)
127
- @header.ts.time=(t)
128
- end
158
+ #
159
+ # Sets the pcap timestamp.
160
+ #
161
+ def time=(t)
162
+ @header.ts.time = t
163
+ end
129
164
 
130
- def caplen
131
- @header.caplen
132
- end
165
+ def caplen
166
+ @header.caplen
167
+ end
133
168
 
134
- alias captured caplen
169
+ alias captured caplen
135
170
 
136
- def len
137
- @header.len
138
- end
171
+ def len
172
+ @header.len
173
+ end
139
174
 
140
- alias length len
141
-
142
- # An optimized copy which allocates new memory for a PacketHeader and
143
- # body.
144
- #
145
- # DANGEROUS: This method uses direct FFI bindings for the copy and
146
- # may crash Ruby if the packet header or body is incorrect.
147
- #
148
- # @raise [StandardError]
149
- # An exception is raised if the header or body is a NULL pointer.
150
- #
151
- def copy
152
- raise(StandardError, "header is a NULL pointer") if @header.to_ptr.null?
153
- raise(StandardError, "body is a NULL pointer") if body_ptr.null?
154
- cpy_hdr = PacketHeader.new
155
- cpy_buf = FFI::MemoryPointer.new(@header.caplen)
156
- CRT.memcpy(cpy_hdr, @header, PacketHeader.size)
157
- CRT.memcpy(cpy_buf, @body_ptr, @header.caplen)
158
- self.class.new( cpy_hdr, cpy_buf )
159
- end
175
+ alias length len
176
+
177
+ #
178
+ # An optimized copy which allocates new memory for a {PacketHeader}
179
+ # and body.
180
+ #
181
+ # DANGEROUS: This method uses direct FFI bindings for the copy and
182
+ # may crash Ruby if the packet header or body is incorrect.
183
+ #
184
+ # @raise [StandardError]
185
+ # An exception is raised if the header or body is a `NULL` pointer.
186
+ #
187
+ def copy
188
+ if @header.to_ptr.null?
189
+ raise(StandardError,"header is a NULL pointer",caller)
190
+ end
160
191
 
161
- end
192
+ if body_ptr.null?
193
+ raise(StandardError,"body is a NULL pointer",caller)
194
+ end
162
195
 
163
- end
196
+ cpy_hdr = PacketHeader.new
197
+ cpy_buf = FFI::MemoryPointer.new(@header.caplen)
198
+
199
+ CRT.memcpy(cpy_hdr, @header, PacketHeader.size)
200
+ CRT.memcpy(cpy_buf, @body_ptr, @header.caplen)
201
+
202
+ return self.class.new( cpy_hdr, cpy_buf )
203
+ end
204
+
205
+ end
206
+ end
164
207
  end