dakrone-pcap-ffi 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,6 @@
1
+ doc
2
+ pkg
3
+ tmp/*
4
+ .DS_Store
5
+ *.swp
6
+ *~
data/History.txt ADDED
@@ -0,0 +1,4 @@
1
+ === 0.1.0 / 2009-04-29
2
+
3
+ * Initial release.
4
+
data/Manifest.txt ADDED
@@ -0,0 +1,40 @@
1
+ History.txt
2
+ Manifest.txt
3
+ Rakefile
4
+ README.txt
5
+ examples/print_bytes.rb
6
+ lib/pcap.rb
7
+ lib/pcap/exceptions.rb
8
+ lib/pcap/exceptions/read_error.rb
9
+ lib/pcap/typedefs.rb
10
+ lib/pcap/time_val.rb
11
+ lib/pcap/in_addr.rb
12
+ lib/pcap/sock_addr.rb
13
+ lib/pcap/sock_addr_in.rb
14
+ lib/pcap/if.rb
15
+ lib/pcap/addr.rb
16
+ lib/pcap/file_header.rb
17
+ lib/pcap/packet_header.rb
18
+ lib/pcap/packet.rb
19
+ lib/pcap/packets.rb
20
+ lib/pcap/packets/typedefs.rb
21
+ lib/pcap/packets/ethernet.rb
22
+ lib/pcap/packets/ip.rb
23
+ lib/pcap/packets/tcp.rb
24
+ lib/pcap/stat.rb
25
+ lib/pcap/data_link.rb
26
+ lib/pcap/error_buffer.rb
27
+ lib/pcap/handler.rb
28
+ lib/pcap/dumper.rb
29
+ lib/pcap/version.rb
30
+ lib/pcap/ffi.rb
31
+ lib/pcap/pcap.rb
32
+ tasks/spec.rb
33
+ spec/spec_helper.rb
34
+ spec/helpers/dumps.rb
35
+ spec/dumps/simple_tcp.pcap
36
+ spec/error_buffer.rb
37
+ spec/data_link_spec.rb
38
+ spec/handler_examples.rb
39
+ spec/handler_spec.rb
40
+ spec/pcap_spec.rb
data/README.txt ADDED
@@ -0,0 +1,41 @@
1
+ = pcap-ffi
2
+
3
+ * http://github.com/postmodern/pcap-ffi/
4
+ * Postmodern (postmodern.mod3 at gmail.com)
5
+
6
+ == DESCRIPTION:
7
+
8
+ Ruby FFI bindings for libpcap.
9
+
10
+ == FEATURES:
11
+
12
+ == EXAMPLES:
13
+
14
+ == INSTALL:
15
+
16
+ $ sudo gem install pcap-ffi
17
+
18
+ == LICENSE:
19
+
20
+ The MIT License
21
+
22
+ Copyright (c) 2009 Hal Brodigan
23
+
24
+ Permission is hereby granted, free of charge, to any person obtaining
25
+ a copy of this software and associated documentation files (the
26
+ 'Software'), to deal in the Software without restriction, including
27
+ without limitation the rights to use, copy, modify, merge, publish,
28
+ distribute, sublicense, and/or sell copies of the Software, and to
29
+ permit persons to whom the Software is furnished to do so, subject to
30
+ the following conditions:
31
+
32
+ The above copyright notice and this permission notice shall be
33
+ included in all copies or substantial portions of the Software.
34
+
35
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
36
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
37
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
38
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
39
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
40
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
41
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,24 @@
1
+ # -*- ruby -*-
2
+
3
+ require 'rubygems'
4
+ require './lib/pcap/version.rb'
5
+ require './tasks/spec.rb'
6
+
7
+ # Generate a gem using jeweler
8
+ begin
9
+ require 'jeweler'
10
+ Jeweler::Tasks.new do |gemspec|
11
+ gemspec.rubyforge_project = 'pcap-ffi'
12
+ gemspec.name = "pcap-ffi"
13
+ gemspec.summary = "FFI bindings for libpcap"
14
+ gemspec.email = "postmodern.mod3@gmail.com"
15
+ gemspec.homepage = "http://github.com/postmodern/pcap-ffi"
16
+ gemspec.description = "Bindings to sniff packets using the FFI interface in Ruby."
17
+ gemspec.authors = ["Postmodern, Dakrone"]
18
+ end
19
+ rescue LoadError
20
+ puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
21
+ end
22
+
23
+
24
+ # vim: syntax=Ruby
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.0
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require 'pcap'
5
+
6
+ include FFI
7
+
8
+ pcap = PCap.open_live(:device => ARGV[0]) do |user,header,bytes|
9
+ puts "#{header.timestamp}:"
10
+
11
+ header.captured.times { |i|
12
+ print ' %.2x' % bytes.get_uchar(i)
13
+ }
14
+ putc "\n"
15
+ end
16
+
17
+ pcap.loop
data/lib/pcap/addr.rb ADDED
@@ -0,0 +1,37 @@
1
+ require 'pcap/typedefs'
2
+ require 'pcap/sock_addr'
3
+
4
+ require 'ffi/struct'
5
+
6
+ module FFI
7
+ module PCap
8
+ class Addr < FFI::Struct
9
+ layout :next, :pointer,
10
+ :addr, :pointer,
11
+ :netmask, :pointer,
12
+ :broadaddr, :pointer,
13
+ :dstaddr, :pointer
14
+
15
+ def next
16
+ Addr.new(self[:next])
17
+ end
18
+
19
+ def addr
20
+ SockAddr.new(self[:addr])
21
+ end
22
+
23
+ def netmask
24
+ SockAddr.new(self[:netmask])
25
+ end
26
+
27
+ def broadcast
28
+ SockAddr.new(self[:broadaddr])
29
+ end
30
+
31
+ def dest_addr
32
+ SockAddr.new(self[:destaddr])
33
+ end
34
+
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,53 @@
1
+ module FFI
2
+ module PCap
3
+ class DataLink
4
+
5
+ # PCap datalink numeric value
6
+ attr_reader :value
7
+
8
+ # DataLink name
9
+ attr_reader :name
10
+
11
+ #
12
+ # Creates a new DataLink object with the specified _value_.
13
+ #
14
+ def initialize(value)
15
+ @value = value
16
+ @name = PCap.pcap_datalink_val_to_name(@value)
17
+ end
18
+
19
+ def self.[](name)
20
+ PCap.pcap_datalink_name_to_val(name.to_s.downcase)
21
+ end
22
+
23
+ #
24
+ # Returns the description of the datalink.
25
+ #
26
+ def description
27
+ PCap.pcap_datalink_val_to_description(@value)
28
+ end
29
+
30
+ #
31
+ # Returns the numeric value of the datalink.
32
+ #
33
+ def to_i
34
+ @value
35
+ end
36
+
37
+ #
38
+ # Returns the String form of the datalink.
39
+ #
40
+ def to_s
41
+ @name
42
+ end
43
+
44
+ #
45
+ # Inspects the datalink.
46
+ #
47
+ def inspect
48
+ "#<#{self.class}: #{@name}>"
49
+ end
50
+
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,39 @@
1
+ require 'pcap/ffi'
2
+
3
+ require 'ffi'
4
+
5
+ module FFI
6
+ module PCap
7
+ class Dumper < FFI::MemoryPointer
8
+
9
+ def initialize(dumper)
10
+ @dumper = dumper
11
+ end
12
+
13
+ def write(header,bytes)
14
+ PCap.pcap_dump(@dumper,header,bytes)
15
+ end
16
+
17
+ def tell
18
+ PCap.pcap_dump_ftell(@dumper)
19
+ end
20
+
21
+ def flush
22
+ PCap.pcap_dump_flush(@dumper)
23
+ end
24
+
25
+ def close
26
+ PCap.pcap_dump_close(@dumper)
27
+ end
28
+
29
+ def to_ptr
30
+ @dumper
31
+ end
32
+
33
+ def inspect
34
+ "#<#{self.class}: 0x#{@dumper.address.to_s(16)}>"
35
+ end
36
+
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,26 @@
1
+ require 'ffi'
2
+
3
+ module FFI
4
+ module PCap
5
+ class ErrorBuffer < FFI::Buffer
6
+
7
+ # Size of the error buffers
8
+ SIZE = 256
9
+
10
+ #
11
+ # Creates a new ErrorBuffer object.
12
+ #
13
+ def initialize
14
+ super(SIZE)
15
+ end
16
+
17
+ #
18
+ # Returns the error message within the error buffer.
19
+ #
20
+ def to_s
21
+ get_string(SIZE)
22
+ end
23
+
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,6 @@
1
+ module FFI
2
+ module PCap
3
+ class ReadError < RuntimeError
4
+ end
5
+ end
6
+ end
@@ -0,0 +1 @@
1
+ require 'pcap/exceptions/read_error'
data/lib/pcap/ffi.rb ADDED
@@ -0,0 +1,78 @@
1
+ require 'pcap/typedefs'
2
+
3
+ require 'ffi'
4
+
5
+ module FFI
6
+ module PCap
7
+ extend FFI::Library
8
+
9
+ ffi_lib 'libpcap'
10
+
11
+ enum :pcap_direction, [
12
+ :pcap_d_inout,
13
+ :pcap_d_in,
14
+ :pcap_d_out
15
+ ]
16
+
17
+ callback :pcap_handler, [:pointer, :pointer, :pointer], :void
18
+
19
+ attach_function :pcap_lookupdev, [:pointer], :string
20
+ attach_function :pcap_lookupnet, [:string, :pointer, :pointer, :pointer], :int
21
+ attach_function :pcap_open_live, [:string, :int, :int, :int, :pointer], :pointer
22
+ attach_function :pcap_open_dead, [:int, :int], :pointer
23
+ attach_function :pcap_open_offline, [:string, :pointer], :pointer
24
+ attach_function :pcap_fopen_offline, [:pointer, :string], :pointer
25
+ attach_function :pcap_close, [:pointer], :void
26
+ attach_function :pcap_loop, [:pointer, :int, :pcap_handler, :pointer], :int
27
+ attach_function :pcap_dispatch, [:pointer, :int, :pcap_handler, :pointer], :int
28
+ attach_function :pcap_next, [:pointer, :pointer], :pointer
29
+ attach_function :pcap_next_ex, [:pointer, :pointer, :pointer], :int
30
+ attach_function :pcap_breakloop, [:pointer], :void
31
+ attach_function :pcap_stats, [:pointer, :pointer], :int
32
+ attach_function :pcap_setfilter, [:pointer, :pointer], :int
33
+ attach_function :pcap_setdirection, [:pointer, :pcap_direction], :int
34
+ attach_function :pcap_getnonblock, [:pointer, :pointer], :int
35
+ attach_function :pcap_setnonblock, [:pointer, :int, :pointer], :int
36
+ attach_function :pcap_perror, [:pointer, :string], :void
37
+ attach_function :pcap_inject, [:pointer, :pointer, :int], :int
38
+ attach_function :pcap_sendpacket, [:pointer, :pointer, :int], :int
39
+ attach_function :pcap_strerror, [:int], :string
40
+ attach_function :pcap_geterr, [:pointer], :string
41
+ attach_function :pcap_compile, [:pointer, :pointer, :string, :int, :bpf_uint32], :int
42
+ attach_function :pcap_compile_nopcap, [:int, :int, :pointer, :string, :int, :bpf_uint32], :int
43
+ attach_function :pcap_freecode, [:pointer], :void
44
+ attach_function :pcap_datalink, [:pointer], :int
45
+ attach_function :pcap_list_datalinks, [:pointer, :pointer], :int
46
+ attach_function :pcap_set_datalink, [:pointer, :int], :int
47
+ attach_function :pcap_datalink_name_to_val, [:string], :int
48
+ attach_function :pcap_datalink_val_to_name, [:int], :string
49
+ attach_function :pcap_datalink_val_to_description, [:int], :string
50
+ attach_function :pcap_snapshot, [:pointer], :int
51
+ attach_function :pcap_is_swapped, [:pointer], :int
52
+ attach_function :pcap_major_version, [:pointer], :int
53
+ attach_function :pcap_minor_version, [:pointer], :int
54
+
55
+ attach_function :pcap_file, [:pointer], :pointer
56
+ attach_function :pcap_fileno, [:pointer], :int
57
+
58
+ attach_function :pcap_dump_open, [:pointer, :string], :pointer
59
+ attach_function :pcap_dump_fopen, [:pointer, :pointer], :pointer
60
+ attach_function :pcap_dump_file, [:pointer], :pointer
61
+ attach_function :pcap_dump_ftell, [:pointer], :long
62
+ attach_function :pcap_dump_flush, [:pointer], :int
63
+ attach_function :pcap_dump_close, [:pointer], :void
64
+ attach_function :pcap_dump, [:pointer, :pointer, :pointer], :void
65
+
66
+ attach_function :pcap_findalldevs, [:pointer, :pointer], :int
67
+ attach_function :pcap_freealldevs, [:pointer], :void
68
+
69
+ attach_function :pcap_lib_version, [], :string
70
+
71
+ attach_function :bpf_filter, [:pointer, :pointer, :uint, :uint], :uint
72
+ attach_function :bpf_validate, [:pointer, :int], :int
73
+ attach_function :bpf_image, [:pointer, :int], :string
74
+ attach_function :bpf_dump, [:pointer, :int], :void
75
+
76
+ # TODO: WIN32/MSDOS/UN*X specific definitions
77
+ end
78
+ end
@@ -0,0 +1,17 @@
1
+ require 'pcap/typedefs'
2
+
3
+ require 'ffi/struct'
4
+
5
+ module FFI
6
+ module PCap
7
+ class FileHeader < FFI::Struct
8
+ layout :magic, :bpf_uint32,
9
+ :version_major, :ushort,
10
+ :version_minor, :ushort,
11
+ :thiszone, :bpf_int32,
12
+ :sigfigs, :bpf_uint32,
13
+ :snaplen, :bpf_uint32,
14
+ :linktype, :bpf_uint32
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,170 @@
1
+ require 'pcap/exceptions/read_error'
2
+ require 'pcap/ffi'
3
+ require 'pcap/error_buffer'
4
+ require 'pcap/data_link'
5
+ require 'pcap/packet_header'
6
+ require 'pcap/stat'
7
+
8
+ require 'ffi'
9
+
10
+ module FFI
11
+ module PCap
12
+ class Handler
13
+
14
+ include Enumerable
15
+
16
+ # Default snaplen
17
+ SNAPLEN = 65535
18
+
19
+ # Pointer to the pcap opaque type
20
+ attr_reader :pcap
21
+
22
+ # Number of packets to sniff
23
+ attr_accessor :count
24
+
25
+ def initialize(pcap,options={},&block)
26
+ @pcap = pcap
27
+ @closed = false
28
+
29
+ # Default is to infinitely loop over packets.
30
+ @count = (options[:count] || -1)
31
+
32
+ if options[:direction]
33
+ self.direction = options[:direction]
34
+ end
35
+
36
+ @callback_wrapper = Proc.new do |user,header,bytes|
37
+ if @callback
38
+ @callback.call(user,PacketHeader.new(header),bytes)
39
+ end
40
+ end
41
+
42
+ callback(&block)
43
+
44
+ trap('SIGINT') { self.close }
45
+ end
46
+
47
+ def datalink
48
+ DataLink.new(PCap.pcap_datalink(@pcap))
49
+ end
50
+
51
+ def callback(&block)
52
+ @callback = block
53
+ return @callback
54
+ end
55
+
56
+ def direction=(dir)
57
+ directions = PCap.enum_type(:pcap_direction)
58
+
59
+ return PCap.pcap_setdirection(@pcap,directions[:"pcap_d_#{dir}"])
60
+ end
61
+
62
+ def non_blocking=(mode)
63
+ errbuf = ErrorBuffer.new
64
+ mode = if mode
65
+ 1
66
+ else
67
+ 0
68
+ end
69
+
70
+ if PCap.pcap_setnonblock(@pcap,mode,errbuf) == -1
71
+ raise(RuntimeError,errbuf.to_s,caller)
72
+ end
73
+
74
+ return mode == 1
75
+ end
76
+
77
+ def non_blocking?
78
+ errbuf = ErrorBuffer.new
79
+ mode = PCap.pcap_getnonblock(@pcap,errbuf)
80
+
81
+ if mode == -1
82
+ raise(RuntimeError,errbuf.to_s,caller)
83
+ end
84
+
85
+ return mode == 1
86
+ end
87
+
88
+ def loop(data=nil,&block)
89
+ callback(&block) if block
90
+
91
+ PCap.pcap_loop(@pcap,@count,@callback_wrapper,data)
92
+ end
93
+
94
+ alias each loop
95
+
96
+ def dispatch(data=nil,&block)
97
+ callback(&block) if block
98
+
99
+ return PCap.pcap_dispatch(@pcap,@count,@callback_wrapper,data)
100
+ end
101
+
102
+ def next
103
+ header = PacketHeader.new
104
+ data = PCap.pcap_next(@pcap,header)
105
+
106
+ return [nil, nil] if data.null?
107
+ return [header, data]
108
+ end
109
+
110
+ def next_extra
111
+ header_ptr = MemoryPointer.new(:pointer)
112
+ data_ptr = MemoryPointer.new(:pointer)
113
+
114
+ case PCap.pcap_next_ex(@pcap,header_ptr,data_ptr)
115
+ when -1
116
+ raise(ReadError,"an error occurred while reading the packet",caller)
117
+ when -2
118
+ raise(ReadError,"the 'savefile' contains no more packets",caller)
119
+ end
120
+
121
+ return [header_ptr.get_pointer(0), data_ptr.get_pointer(0)]
122
+ end
123
+
124
+ def open_dump(path)
125
+ dump_ptr = PCap.pcap_dump_open(@pcap,File.expand_path(path))
126
+
127
+ if dump_ptr.null?
128
+ raise(RuntimeError,error,caller)
129
+ end
130
+
131
+ return Dumper.new(dump_ptr)
132
+ end
133
+
134
+ def stats
135
+ stats = Stat.new
136
+
137
+ PCap.pcap_stats(@pcap,stats)
138
+ return stats
139
+ end
140
+
141
+ def error
142
+ PCap.pcap_geterr(@pcap)
143
+ end
144
+
145
+ def stop
146
+ PCap.pcap_breakloop(@pcap)
147
+ end
148
+
149
+ def closed?
150
+ @closed == true
151
+ end
152
+
153
+ def close
154
+ unless @closed
155
+ @closed = true
156
+ PCap.pcap_close(@pcap)
157
+ end
158
+ end
159
+
160
+ def to_ptr
161
+ @pcap
162
+ end
163
+
164
+ def inspect
165
+ "#<#{self.class}: 0x#{@pcap.address.to_s(16)}>"
166
+ end
167
+
168
+ end
169
+ end
170
+ end
data/lib/pcap/if.rb ADDED
@@ -0,0 +1,36 @@
1
+ require 'pcap/typedefs'
2
+ require 'pcap/addr'
3
+
4
+ require 'ffi/struct'
5
+
6
+ module FFI
7
+ module PCap
8
+ class IF < FFI::Struct
9
+ # interface is loopback
10
+ LOOPBACK = 0x00000001
11
+
12
+ layout :next, :pointer,
13
+ :name, :string,
14
+ :description, :string,
15
+ :addresses, :pointer,
16
+ :flags, :bpf_uint32
17
+
18
+ def next
19
+ IF.new(self[:next])
20
+ end
21
+
22
+ def name
23
+ self[:name]
24
+ end
25
+
26
+ def addresses
27
+ Addr.new(self[:addresses])
28
+ end
29
+
30
+ def to_s
31
+ self[:name]
32
+ end
33
+
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,13 @@
1
+ require 'pcap/typedefs'
2
+
3
+ require 'ffi'
4
+
5
+ module FFI
6
+ module PCap
7
+ class InAddr < FFI::Struct
8
+
9
+ layout :s_addr, :in_addr_t
10
+
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,26 @@
1
+ require 'pcap/typedefs'
2
+ require 'pcap/time_val'
3
+
4
+ require 'ffi/struct'
5
+
6
+ module FFI
7
+ module PCap
8
+ class PacketHeader < FFI::Struct
9
+ layout :ts, TimeVal,
10
+ :caplen, :bpf_uint32,
11
+ :len, :bpf_uint32
12
+
13
+ def timestamp
14
+ self[:ts]
15
+ end
16
+
17
+ def captured
18
+ self[:caplen]
19
+ end
20
+
21
+ def length
22
+ self[:len]
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,40 @@
1
+ require 'pcap/packets/typedefs'
2
+ require 'pcap/packet'
3
+
4
+ require 'ffi'
5
+
6
+ module FFI
7
+ module PCap
8
+ module Packets
9
+ class Ethernet < FFI::Struct
10
+
11
+ include Packet
12
+
13
+ # Number of bytes for an ethernet address
14
+ ADDR_LEN = 6
15
+
16
+ # Size of an Ethernet header
17
+ SIZE = 14
18
+
19
+ layout :ether_dhost, [:uchar, ADDR_LEN],
20
+ :ether_shost, [:uchar, ADDR_LEN],
21
+ :ether_type, :ushort
22
+
23
+ #
24
+ # Returns the source MAC address.
25
+ #
26
+ def src_mac
27
+ self[:ether_shost]
28
+ end
29
+
30
+ #
31
+ # Returns the destination MAC address.
32
+ #
33
+ def dest_mac
34
+ self[:ether_dhost]
35
+ end
36
+
37
+ end
38
+ end
39
+ end
40
+ end