ffi-pcap 0.2.0

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 (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
data/spec/pcap_spec.rb ADDED
@@ -0,0 +1,149 @@
1
+ require 'spec_helper'
2
+
3
+ describe FFI::PCap do
4
+ it "should define a VERSION constant" do
5
+ FFI::PCap.const_defined?('VERSION').should == true
6
+ end
7
+
8
+ it ".lib_version() should expose the libpcap version banner" do
9
+ FFI::PCap.lib_version.should_not be_nil
10
+ FFI::PCap.lib_version.should_not be_empty
11
+ end
12
+
13
+ it ".lib_version_number() should expose the libpcap version number only" do
14
+ FFI::PCap.lib_version_number.should_not be_nil
15
+ FFI::PCap.lib_version_number.should_not be_empty
16
+ FFI::PCap.lib_version_number.should =~ /^\d+\.\d+\.\d+$/
17
+ end
18
+
19
+ it ".lookupdev() should return a device deafult device" do
20
+ dev = FFI::PCap.lookupdev
21
+ dev.should_not be_nil
22
+ dev.should_not be_empty
23
+ end
24
+
25
+ it ".each_device() should enumerate over all usable interfaces" do
26
+ i = 0
27
+ FFI::PCap.each_device do |dev|
28
+ dev.should_not be_nil
29
+ Interface.should === dev
30
+ [true,false].include?(dev.loopback?).should == true
31
+ i+=1
32
+ end
33
+ i.should_not == 0
34
+ end
35
+
36
+ it ".device_names() should return names for all network interfaces" do
37
+ devs = FFI::PCap.device_names
38
+ Array.should === devs
39
+ i = 0
40
+ devs.each do |dev|
41
+ String.should === dev
42
+ dev.should_not be_nil
43
+ dev.should_not be_empty
44
+ i+=1
45
+ end
46
+ i.should_not == 0
47
+ devs.include?(PCAP_DEV).should == true
48
+ end
49
+
50
+ it ".dump_devices() should return name/network pairs for all interfaces" do
51
+ i = 0
52
+ devs = FFI::PCap.dump_devices
53
+ Array.should === devs
54
+ devs.each do |y|
55
+ y.size.should == 2
56
+ dev, net = y
57
+ String.should === dev
58
+ dev.should_not be_nil
59
+ dev.should_not be_empty
60
+ i+=1
61
+ end
62
+ i.should_not == 0
63
+ devs.select{|dev,net| not net.nil? }.should_not be_empty
64
+ devs.map{|dev,net| dev}.include?(PCAP_DEV).should == true
65
+ end
66
+
67
+ it ".open_live() should open a live pcap handler given a chosen device" do
68
+ lambda {
69
+ pcap = FFI::PCap.open_live(:device => PCAP_DEV)
70
+ pcap.device.should == PCAP_DEV
71
+ pcap.close
72
+ }.should_not raise_error(Exception)
73
+ end
74
+
75
+ it ".open_live() should open a live pcap handler using a default device" do
76
+ lambda {
77
+ # XXX Using Vista and wpcap.dll this breaks on me.
78
+ # The lookupdev for a default adapter result is '\', which is just
79
+ # wrong.
80
+ pcap = FFI::PCap.open_live()
81
+ pcap.should be_ready
82
+ pcap.close
83
+ }.should_not raise_error(Exception)
84
+ end
85
+
86
+ it ".open_dead() should open a dead pcap handler" do
87
+ lambda {
88
+ pcap = FFI::PCap.open_dead()
89
+ pcap.should be_ready
90
+ pcap.close
91
+ }.should_not raise_error(Exception)
92
+ end
93
+
94
+ it ".open_offline() should open a pcap dump file" do
95
+ lambda {
96
+ pcap = FFI::PCap.open_offline(PCAP_TESTFILE)
97
+ pcap.should be_ready
98
+ pcap.close
99
+ }.should_not raise_error(Exception)
100
+ end
101
+
102
+ it ".open_file() should work the same as .open_offline()" do
103
+ lambda {
104
+ pcap = FFI::PCap.open_offline(PCAP_TESTFILE)
105
+ pcap.should be_ready
106
+ pcap.close
107
+ }.should_not raise_error(Exception)
108
+ end
109
+
110
+ it ".open_live() should take a block and close the device after calling it" do
111
+ pcap = nil
112
+ ret = FFI::PCap.open_live(:device => PCAP_DEV) {|this|
113
+ Live.should === this
114
+ this.should be_ready
115
+ this.should_not be_closed
116
+ pcap = this
117
+ }
118
+ ret.should be_nil
119
+ pcap.should_not be_ready
120
+ pcap.should be_closed
121
+ end
122
+
123
+ it ".open_dead() should take a block and close the device after calling it" do
124
+ pcap = nil
125
+ ret = FFI::PCap.open_dead() {|this|
126
+ Dead.should === this
127
+ this.should be_ready
128
+ this.should_not be_closed
129
+ pcap = this
130
+ }
131
+ ret.should be_nil
132
+ pcap.should_not be_ready
133
+ ret.should be_nil
134
+ end
135
+
136
+ it ".open_file() should take a block and close the device after calling it" do
137
+ pcap = nil
138
+ ret = FFI::PCap.open_file(PCAP_TESTFILE) {|this|
139
+ Offline.should === this
140
+ this.should be_ready
141
+ this.should_not be_closed
142
+ pcap = this
143
+ }
144
+ ret.should be_nil
145
+ pcap.should_not be_ready
146
+ ret.should be_nil
147
+ end
148
+
149
+ end
@@ -0,0 +1,31 @@
1
+ require 'rubygems'
2
+ gem 'rspec', '>=1.3.0'
3
+ require 'spec'
4
+
5
+ require 'ffi/pcap'
6
+
7
+ include FFI
8
+ include FFI::PCap
9
+
10
+ PCAP_DEV = ENV['PCAP_DEV'] || 'lo0'
11
+ PCAP_TESTFILE = ENV['PCAP_TESTFILE'] || File.expand_path(File.join(File.dirname(__FILE__), 'dumps', 'simple_tcp.pcap'))
12
+ PCAP_TESTADDR = ENV['PCAP_TESTADDR'] || '127.0.0.1'
13
+
14
+ $test_ping_pid = nil
15
+
16
+ def start_traffic_generator
17
+ begin
18
+ if $test_ping_pid.nil?
19
+ $test_ping_pid = Process.fork{ `ping #{PCAP_TESTADDR}` }
20
+ end
21
+ rescue NotImplementedError
22
+ $test_ping_pid = nil
23
+ end
24
+ end
25
+
26
+ def stop_traffic_generator
27
+ if $test_ping_pid
28
+ Process.kill('TERM', $test_ping_pid)
29
+ $test_ping_pid = nil
30
+ end
31
+ end
@@ -0,0 +1,124 @@
1
+ require 'spec_helper'
2
+ require 'tempfile'
3
+
4
+ shared_examples_for "FFI::PCap::CommonWrapper" do
5
+ it "should indicate readiness" do
6
+ @pcap.ready?.should == true
7
+ end
8
+
9
+ it "should have a datalink" do
10
+ datalink = @pcap.datalink
11
+ datalink.value.should_not be_nil
12
+ Numeric.should === datalink.value
13
+ datalink.name.should_not be_empty
14
+ end
15
+
16
+ it "should be able to open a dump file" do
17
+ lambda {
18
+ dumper = @pcap.open_dump(Tempfile.new(rand(0xffff).to_s).path)
19
+ Dumper.should === dumper
20
+ dumper.close
21
+ }.should_not raise_error(Exception)
22
+ end
23
+
24
+ it "should be able to write packets to a dump file" do
25
+ tmpfile = Tempfile.new(rand(0xffff).to_s).path
26
+ dumper = @pcap.open_dump(tmpfile)
27
+ dumper.write_pkt( Packet.from_string("i want to be a packet when i grow up") )
28
+ dumper.flush
29
+ dumper.close
30
+
31
+ chk_pcap = Offline.new(tmpfile)
32
+ pkt = chk_pcap.next
33
+ pkt.should be_kind_of Packet
34
+ pkt.body.should == "i want to be a packet when i grow up"
35
+ chk_pcap.close
36
+ end
37
+
38
+ it "should raise an exception when opening a bad dump file" do
39
+ lambda {
40
+ @pcap.open_dump(File.join('','obviously','not','there'))
41
+ }.should raise_error(Exception)
42
+ end
43
+
44
+ it "should return an empty String when an error has not occurred" do
45
+ @pcap.error.should be_empty
46
+ end
47
+
48
+ it "should be able to compile a filter" do
49
+ filter = @pcap.compile("ip")
50
+ filter.should_not be_nil
51
+ BPFProgram.should === filter
52
+ filter.bf_len.should > 0
53
+ end
54
+
55
+ it "should detect invalid filter syntax when compiling" do
56
+ lambda {
57
+ @pcap.compile("ip and totally bogus")
58
+ }.should raise_error(LibError)
59
+ end
60
+
61
+ it "should prevent double closes" do
62
+ @pcap.close
63
+ @pcap.should be_closed
64
+ @pcap.should_not be_ready
65
+
66
+ lambda {
67
+ @pcap.close
68
+ }.should_not raise_error(Exception)
69
+ end
70
+
71
+ end
72
+
73
+ shared_examples_for "FFI::PCap::CaptureWrapper" do
74
+
75
+ it "should pass packets to a block using loop()" do
76
+ i = 0
77
+ @pkt = nil
78
+ @pcap.loop(:count => 2) do |this, pkt|
79
+ this.should == @pcap
80
+ pkt.should_not be_nil
81
+ Packet.should === pkt
82
+ i+=1
83
+ end
84
+ i.should == 2
85
+ end
86
+
87
+ it "should be able to get the next packet" do
88
+ pkt = @pcap.next
89
+ pkt.should_not be_nil
90
+ end
91
+
92
+ it "should be able to break out of a pcap loop()" do
93
+ stopped = false
94
+ i = 0
95
+
96
+ @pcap.loop(:count => 3) do |this, pkt|
97
+ stopped = true
98
+ i+=1
99
+ this.stop
100
+ end
101
+
102
+ i.should == 1
103
+ stopped.should == true
104
+ end
105
+
106
+ it "should consume packets without a block passed to loop()" do
107
+ lambda { @pcap.loop(:count => 3) }.should_not raise_error(Exception)
108
+ end
109
+
110
+ it "should be able to set a filter" do
111
+ lambda {
112
+ @pcap.set_filter("ip")
113
+ }.should_not raise_error(Exception)
114
+ end
115
+
116
+ it "should detect invalid filter syntax in set_filter" do
117
+ lambda {
118
+ @pcap.set_filter("ip and totally bogus")
119
+ }.should raise_error(LibError)
120
+ end
121
+
122
+ it_should_behave_like "FFI::PCap::CommonWrapper"
123
+ end
124
+
data/tasks/rcov.rb ADDED
@@ -0,0 +1,6 @@
1
+ require 'spec/rake/spectask'
2
+ Spec::Rake::SpecTask.new(:rcov) do |spec|
3
+ spec.libs << 'lib' << 'spec'
4
+ spec.pattern = 'spec/**/*_spec.rb'
5
+ spec.rcov = true
6
+ end
data/tasks/rdoc.rb ADDED
@@ -0,0 +1,17 @@
1
+
2
+ require 'rake/rdoctask'
3
+
4
+ Rake::RDocTask.new do |rdoc|
5
+ if File.exist?('VERSION')
6
+ version = "- #{File.read('VERSION')}"
7
+ else
8
+ version = ""
9
+ end
10
+
11
+ rdoc.rdoc_dir = 'rdoc'
12
+ rdoc.title = "FFI PCap Documentation #{version}"
13
+ rdoc.rdoc_files.include('README*')
14
+ rdoc.rdoc_files.include('ChangeLog*')
15
+ rdoc.rdoc_files.include('lib/**/*.rb')
16
+ end
17
+
data/tasks/spec.rb ADDED
@@ -0,0 +1,9 @@
1
+ require 'spec/rake/spectask'
2
+
3
+ desc "Run all specifications"
4
+ Spec::Rake::SpecTask.new(:spec) do |t|
5
+ t.libs += ['lib', 'spec']
6
+ t.spec_opts = ['--colour', '--format', 'specdoc']
7
+ end
8
+
9
+ task :default => :spec
data/tasks/yard.rb ADDED
@@ -0,0 +1,21 @@
1
+ begin
2
+ require 'yard'
3
+
4
+ YARD::Rake::YardocTask.new do |t|
5
+ if File.exist?('VERSION')
6
+ version = "- #{File.read('VERSION')}"
7
+ else
8
+ version = ""
9
+ end
10
+
11
+ t.files = ['ChangeLog*','LICENSE*','lib/**/*.rb']
12
+ t.options = [
13
+ '--title',"FFI PCap Documentation #{version}",
14
+ '--protected',
15
+ ]
16
+ end
17
+
18
+ task :docs => :yard
19
+ rescue LoadError
20
+ end
21
+
metadata ADDED
@@ -0,0 +1,157 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ffi-pcap
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 2
8
+ - 0
9
+ version: 0.2.0
10
+ platform: ruby
11
+ authors:
12
+ - Postmodern
13
+ - Dakrone
14
+ - Eric Monti
15
+ autorequire:
16
+ bindir: bin
17
+ cert_chain: []
18
+
19
+ date: 2010-05-10 00:00:00 -05:00
20
+ default_executable:
21
+ dependencies:
22
+ - !ruby/object:Gem::Dependency
23
+ name: ffi
24
+ prerelease: false
25
+ requirement: &id001 !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ segments:
30
+ - 0
31
+ - 5
32
+ - 0
33
+ version: 0.5.0
34
+ type: :runtime
35
+ version_requirements: *id001
36
+ - !ruby/object:Gem::Dependency
37
+ name: ffi_dry
38
+ prerelease: false
39
+ requirement: &id002 !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ segments:
44
+ - 0
45
+ - 1
46
+ - 9
47
+ version: 0.1.9
48
+ type: :runtime
49
+ version_requirements: *id002
50
+ description: Bindings to libpcap via FFI interface in Ruby.
51
+ email: postmodern.mod3@gmail.com
52
+ executables: []
53
+
54
+ extensions: []
55
+
56
+ extra_rdoc_files:
57
+ - ChangeLog.rdoc
58
+ - LICENSE.txt
59
+ - README.rdoc
60
+ files:
61
+ - .gitignore
62
+ - ChangeLog.rdoc
63
+ - LICENSE.txt
64
+ - README.rdoc
65
+ - Rakefile
66
+ - VERSION
67
+ - examples/ipfw_divert.rb
68
+ - examples/print_bytes.rb
69
+ - lib/ffi-pcap.rb
70
+ - lib/ffi/pcap.rb
71
+ - lib/ffi/pcap/addr.rb
72
+ - lib/ffi/pcap/bpf.rb
73
+ - lib/ffi/pcap/bsd.rb
74
+ - lib/ffi/pcap/capture_wrapper.rb
75
+ - lib/ffi/pcap/common_wrapper.rb
76
+ - lib/ffi/pcap/copy_handler.rb
77
+ - lib/ffi/pcap/crt.rb
78
+ - lib/ffi/pcap/data_link.rb
79
+ - lib/ffi/pcap/dead.rb
80
+ - lib/ffi/pcap/dumper.rb
81
+ - lib/ffi/pcap/error_buffer.rb
82
+ - lib/ffi/pcap/exceptions.rb
83
+ - lib/ffi/pcap/file_header.rb
84
+ - lib/ffi/pcap/in_addr.rb
85
+ - lib/ffi/pcap/interface.rb
86
+ - lib/ffi/pcap/live.rb
87
+ - lib/ffi/pcap/offline.rb
88
+ - lib/ffi/pcap/packet.rb
89
+ - lib/ffi/pcap/packet_header.rb
90
+ - lib/ffi/pcap/pcap.rb
91
+ - lib/ffi/pcap/stat.rb
92
+ - lib/ffi/pcap/time_val.rb
93
+ - lib/ffi/pcap/typedefs.rb
94
+ - lib/ffi/pcap/version.rb
95
+ - spec/data_link_spec.rb
96
+ - spec/dead_spec.rb
97
+ - spec/dumps/http.pcap
98
+ - spec/dumps/simple_tcp.pcap
99
+ - spec/error_buffer_spec.rb
100
+ - spec/file_header_spec.rb
101
+ - spec/live_spec.rb
102
+ - spec/offline_spec.rb
103
+ - spec/packet_behaviors.rb
104
+ - spec/packet_injection_spec.rb
105
+ - spec/packet_spec.rb
106
+ - spec/pcap_spec.rb
107
+ - spec/spec_helper.rb
108
+ - spec/wrapper_behaviors.rb
109
+ - tasks/rcov.rb
110
+ - tasks/rdoc.rb
111
+ - tasks/spec.rb
112
+ - tasks/yard.rb
113
+ has_rdoc: true
114
+ homepage: http://github.com/sophsec/ffi-pcap
115
+ licenses: []
116
+
117
+ post_install_message:
118
+ rdoc_options:
119
+ - --charset=UTF-8
120
+ require_paths:
121
+ - lib
122
+ required_ruby_version: !ruby/object:Gem::Requirement
123
+ requirements:
124
+ - - ">="
125
+ - !ruby/object:Gem::Version
126
+ segments:
127
+ - 0
128
+ version: "0"
129
+ required_rubygems_version: !ruby/object:Gem::Requirement
130
+ requirements:
131
+ - - ">="
132
+ - !ruby/object:Gem::Version
133
+ segments:
134
+ - 0
135
+ version: "0"
136
+ requirements: []
137
+
138
+ rubyforge_project: ffi-pcap
139
+ rubygems_version: 1.3.6
140
+ signing_key:
141
+ specification_version: 3
142
+ summary: FFI bindings for libpcap
143
+ test_files:
144
+ - spec/data_link_spec.rb
145
+ - spec/dead_spec.rb
146
+ - spec/error_buffer_spec.rb
147
+ - spec/file_header_spec.rb
148
+ - spec/live_spec.rb
149
+ - spec/offline_spec.rb
150
+ - spec/packet_behaviors.rb
151
+ - spec/packet_injection_spec.rb
152
+ - spec/packet_spec.rb
153
+ - spec/pcap_spec.rb
154
+ - spec/spec_helper.rb
155
+ - spec/wrapper_behaviors.rb
156
+ - examples/ipfw_divert.rb
157
+ - examples/print_bytes.rb