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