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