dnet-ffi 0.1.3

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.
data/lib/dnet/arp.rb ADDED
@@ -0,0 +1,168 @@
1
+ # Address resolution Protocol
2
+
3
+ module Dnet
4
+ module Arp
5
+ # ARP header
6
+ #
7
+ # field :hrd, :uint16, :desc => 'format of hardware address'
8
+ # field :pro, :uint16, :desc => 'format of protocol address'
9
+ # field :hln, :uint16, :desc => 'length of hw address (ETH_ADDR_LEN)'
10
+ # field :pln, :uint16, :desc => 'length of proto address (IP_ADDR_LEN)'
11
+ # field :op, :uint16, :desc => 'operation'
12
+ class Hdr < ::FFI::Struct
13
+ include ::FFI::DRY::StructHelper
14
+ include ::Dnet::NetEndianHelper
15
+
16
+ dsl_layout do
17
+ field :hrd, :uint16, :desc => 'format of hardware address'
18
+ field :pro, :uint16, :desc => 'format of protocol address'
19
+ field :hln, :uint16, :desc => 'length of hw address (ETH_ADDR_LEN)'
20
+ field :pln, :uint16, :desc => 'length of proto address (IP_ADDR_LEN)'
21
+ field :op, :uint16, :desc => 'operation'
22
+ end
23
+
24
+ # ARP operations
25
+ module Op
26
+ ::Dnet.constants.grep(/^(ARP_OP_([A-Z][A-Z0-9_]+))$/) do
27
+ self.const_set $2, ::Dnet.const_get($1)
28
+ end
29
+
30
+ module_function
31
+ def list
32
+ @@list ||= constants.inject({}){|h,c| h.merge! c => const_get(c) }
33
+ end
34
+ end # Op
35
+ end # Hdr
36
+
37
+ # Ethernet/IP ARP message
38
+ #
39
+ # array :sha, [:uint8, ETH_ADDR_LEN], :desc => 'sender hardware address'
40
+ # array :spa, [:uint8, IP_ADDR_LEN], :desc => 'sender protocol address'
41
+ # array :tha, [:uint8, ETH_ADDR_LEN], :desc => 'target hardware address'
42
+ # array :tpa, [:uint8, IP_ADDR_LEN], :desc => 'target protocol address'
43
+ #
44
+ class Ethip < ::FFI::Struct
45
+ include ::FFI::DRY::StructHelper
46
+ include ::Dnet::NetEndianHelper
47
+
48
+ dsl_layout do
49
+ array :sha, [:uint8, ETH_ADDR_LEN], :desc => 'sender hardware address'
50
+ array :spa, [:uint8, IP_ADDR_LEN], :desc => 'sender protocol address'
51
+ array :tha, [:uint8, ETH_ADDR_LEN], :desc => 'target hardware address'
52
+ array :tpa, [:uint8, IP_ADDR_LEN], :desc => 'target protocol address'
53
+ end
54
+
55
+ end # Ethip
56
+
57
+
58
+ # FFI mapping to libdnet's "arp_entry" struct.
59
+ #
60
+ # dnet(3)'s ARP cache entries are described by the following C structure:
61
+ #
62
+ # struct :pa, ::Dnet::Addr, :desc => 'protocol address'
63
+ # struct :ha, ::Dnet::Addr, :desc => 'hardware address'
64
+ #
65
+ class Entry < ::FFI::Struct
66
+ include ::FFI::DRY::StructHelper
67
+
68
+ dsl_layout do
69
+ struct :pa, ::Dnet::Addr, :desc => 'protocol address'
70
+ struct :ha, ::Dnet::Addr, :desc => 'hardware address'
71
+ end
72
+
73
+ end # Entry
74
+
75
+ # A handle for accessing the kernel arp(4) cache. This does not require
76
+ # root privileges.
77
+ class Handle < LoopableHandle
78
+
79
+ # Obtains a handle to access the kernel arp(4) cache. Uses dnet(3)'s
80
+ # arp_open() function under the hood.
81
+ def initialize
82
+ if (@handle = ::Dnet.arp_open).address == 0
83
+ raise H_ERR.new("unable to open arp handle")
84
+ end
85
+ _handle_opened!
86
+ end
87
+
88
+ # Closes the handle. Uses dnet(3)'s arp_close() function under the hood.
89
+ def close
90
+ _do_if_open { _handle_closed!; ::Dnet.arp_close(@handle) }
91
+ end
92
+
93
+ # Iterates over the kernel arp cache, yielding each entry (cast as an
94
+ # Entry) to a block. Uses dnet(3)'s arp_loop() function under the hood.
95
+ def loop &block
96
+ _loop ::Dnet, :arp_loop, Entry, &block
97
+ end
98
+
99
+ # Retrieves the ARP entry for the protocol address specified by 'addr'
100
+ # (supplied as a String). Uses dnet(3)'s addr_aton() function to parse
101
+ # 'addr' and arp_get() to retrieve a result, which is cast as an Entry
102
+ # instance.
103
+ def get(addr)
104
+ _check_open!
105
+ ae = Entry.new
106
+ return ae if ae.pa.set_string(addr) and
107
+ ::Dnet.arp_get(@handle, ae) == 0
108
+ end
109
+
110
+ # Adds a new ARP entry specified as an Entry object. Uses dnet(3)'s
111
+ # arp_add() function under the hood.
112
+ def add(entry)
113
+ _check_open!
114
+ ::Dnet.arp_add(@handle, entry)
115
+ end
116
+
117
+ # Deletes the ARP entry for the protocol address specified by
118
+ # 'entry' supplied as an Entry object. Uses dnet(3)'s arp_delete()
119
+ # function under the hood.
120
+ def delete(entry)
121
+ _check_open!
122
+ ::Dnet.arp_delete(@handle, entry)
123
+ end
124
+
125
+ end # Handle
126
+
127
+ # #define arp_pack_hdr_ethip(hdr, op, sha, spa, tha, tpa) do { \
128
+ # struct arp_hdr *pack_arp_p = (struct arp_hdr *)(hdr); \
129
+ # struct arp_ethip *pack_ethip_p = (struct arp_ethip *) \
130
+ # ((uint8_t *)(hdr) + ARP_HDR_LEN); \
131
+ # pack_arp_p->ar_hrd = htons(ARP_HRD_ETH); \
132
+ # pack_arp_p->ar_pro = htons(ARP_PRO_IP); \
133
+ # pack_arp_p->ar_hln = ETH_ADDR_LEN; \
134
+ # pack_arp_p->ar_pln = IP_ADDR_LEN; \
135
+ # pack_arp_p->ar_op = htons(op); \
136
+ # memmove(pack_ethip_p->ar_sha, &(sha), ETH_ADDR_LEN); \
137
+ # memmove(pack_ethip_p->ar_spa, &(spa), IP_ADDR_LEN); \
138
+ # memmove(pack_ethip_p->ar_tha, &(tha), ETH_ADDR_LEN); \
139
+ # memmove(pack_ethip_p->ar_tpa, &(tpa), IP_ADDR_LEN); \
140
+ # } while (0)
141
+
142
+ def self.open(*args)
143
+ Handle.open(*args) {|*y| yield(*y) if block_given? }
144
+ end
145
+
146
+ def self.each_entry(*args)
147
+ Handle.each_entry(*args) {|*y| yield(*y) }
148
+ end
149
+
150
+ def self.entries(*args)
151
+ Handle.entries(*args)
152
+ end
153
+
154
+ end # Arp
155
+
156
+ # This is just an alias for Arp::Handle
157
+ ArpHandle = Arp::Handle
158
+
159
+ typedef :pointer, :arp_t
160
+ callback :arp_handler, [Arp::Entry, :ulong], :int
161
+ attach_function :arp_open, [], :arp_t
162
+ attach_function :arp_add, [:arp_t, Arp::Entry], :int
163
+ attach_function :arp_delete, [:arp_t, Arp::Entry], :int
164
+ attach_function :arp_get, [:arp_t, Arp::Entry], :int
165
+ attach_function :arp_loop, [:arp_t, :arp_handler, :ulong], :int
166
+ attach_function :arp_close, [:arp_t], :arp_t
167
+
168
+ end
data/lib/dnet/blob.rb ADDED
@@ -0,0 +1,246 @@
1
+
2
+ ## blob is dnet(3)'s name for binary buffers
3
+
4
+ module Dnet
5
+
6
+ # FFI mapping to dnet(3)'s "blob_t" binary buffer struct.
7
+ #
8
+ # field :base, :pointer, :desc => 'start of data'
9
+ # field :off, :pointer, :desc => 'offset into data'
10
+ # field :end, :pointer, :desc => 'end of data'
11
+ # field :size, :pointer, :desc => 'size of allocation'
12
+ #
13
+ class Blob < FFI::ManagedStruct
14
+ include ::FFI::DRY::StructHelper
15
+ include HandleHelpers
16
+
17
+ dsl_layout do
18
+ field :base, :pointer, :desc => 'start of data'
19
+ field :off, :int, :desc => 'offset into data'
20
+ field :end, :int, :desc => 'end of data'
21
+ field :size, :int, :desc => 'size of allocation'
22
+ end
23
+
24
+ # Initializes a new Blob using dnet(3)'s blob_new under the hood.
25
+ #
26
+ # blob_new is used to allocate a new dynamic binary buffer, returning
27
+ # NULL on failure.
28
+ #
29
+ # Below is the dnet(3) C function definition:
30
+ #
31
+ # blob_t * blob_new(void);
32
+ #
33
+ def initialize
34
+ super(::Dnet.blob_new())
35
+ _handle_opened!
36
+ end
37
+
38
+ # Called by the garbage collector for ::FFI:ManagedStruct objects
39
+ def self.release(blob)
40
+ blob.release()
41
+ end
42
+
43
+ def base_ptr
44
+ self[:base]
45
+ end
46
+
47
+ def curr_ptr
48
+ safe[:base] + self[:off]
49
+ end
50
+
51
+ def end_ptr
52
+ self[:base] + self[:end]
53
+ end
54
+
55
+ # This method calls dnet(3)'s blob_free behind the scenes. It should
56
+ # automatically get run by the garbage collector when a blob is no longer
57
+ # referenced.
58
+ #
59
+ # blob_free deallocates the memory associated with blob b and returns NULL.
60
+ def release
61
+ _do_if_open { _handle_closed! ; ::Dnet.blob_free(self) }
62
+ end
63
+
64
+ # blob_pack converts and writes, and blob_unpack() reads and converts data
65
+ # in blob b according to the given format fmt as described below, returning
66
+ # 0 on success, and -1 on failure.
67
+ #
68
+ # The format string is composed of zero or more directives: ordinary
69
+ # characters (not % ), which are copied to / read from the blob, and
70
+ # conversion specifications, each of which results in reading / writing
71
+ # zero or more subsequent arguments.
72
+ #
73
+ # Each conversion specification is introduced by the character %, and may
74
+ # be prefixed by length specifier. The arguments must correspond properly
75
+ # (after type promotion) with the length and conversion specifiers.
76
+ #
77
+ # The length specifier is either a a decimal digit string specifying the
78
+ # length of the following argument, or the literal character * indicating
79
+ # that the length should be read from an integer argument for the argument
80
+ # following it.
81
+ #
82
+ # The conversion specifiers and their meanings are:
83
+ #
84
+ # D An unsigned 32-bit integer in network byte order.
85
+ # H An unsigned 16-bit integer in network byte order.
86
+ # b A binary buffer (length specifier required).
87
+ # c An unsigned character.
88
+ # d An unsigned 32-bit integer in host byte order.
89
+ # h An unsigned 16-bit integer in host byte order.
90
+ # s A C-style null-terminated string, whose maximum length must be
91
+ # specified when unpacking.
92
+ #
93
+ # Custom conversion routines and their specifiers may be registered via
94
+ # blob_register_pack, currently undocumented.
95
+ #
96
+ # TODO add ruby wrapper for blob_register_pack.
97
+ #
98
+ # XXX want to wrap varargs FFI for easier type casting?
99
+ def pack(sfmt, *args)
100
+ _check_open!
101
+ (fmt = ::FFI::MemoryPointer.from_string(sfmt)).autorelease=true
102
+ ::Dnet.blob_pack(self, fmt, *args)
103
+ end
104
+
105
+ # Uses dnet(3)s 'blob_unpack' under the hood. See pack() for more
106
+ # information.
107
+ #
108
+ # XXX TODO - want to wrap varargs FFI for easier type casting?
109
+ def unpack(sfmt, *args)
110
+ _check_open!
111
+ (fmt = ::FFI::MemoryPointer.from_string(sfmt)).autorelease=true
112
+ ::Dnet.blob_unpack(self, fmt, *args)
113
+ end
114
+
115
+ # Writes the data supplied from a string or pointer to the blob at the
116
+ # current offset. If a pointer is supplied, a size must accompany it.
117
+ # Uses dnet(3)'s "blob_write" under the hood.
118
+ def write(buf, bsz=nil)
119
+ _check_open!
120
+ ptr, psz = ::Dnet::Util.derive_pointer(buf, bsz)
121
+ ::Dnet.blob_write(self, ptr, psz)
122
+ end
123
+
124
+ # Reads 'len' bytes out of the blob from the current offset. If len is nil
125
+ # (the default) then all remaining bytes are read. Moves the offset
126
+ # accordingly. This method uses dnet(3)'s "blob_read" under the hood.
127
+ def read(len=nil)
128
+ _check_open!
129
+ len ||= self[:end] - self[:off]
130
+ (buf = ::FFI::MemoryPointer.new("\x00", len)).autorelease = true
131
+ if rlen=::Dnet.blob_read(self, buf, len)
132
+ return buf.read_string_length(rlen)
133
+ else
134
+ nil
135
+ end
136
+ end
137
+
138
+ # Returns the entirety of the blob from beginning to end.
139
+ # Note, this will also move the offset to the end of the buffer.
140
+ def string()
141
+ _check_open!
142
+ rewind()
143
+ read()
144
+ end
145
+
146
+ # rewinds the blob buffer offset to the beginning
147
+ def rewind
148
+ _check_open!
149
+ ::Dnet.blob_seek(self, 0, 0)
150
+ end
151
+
152
+ # sets the blob buffer offset to p. returns -1 on failure
153
+ def pos=(p)
154
+ _check_open!
155
+ ::Dnet.blob_seek(self, p.to_i, 0)
156
+ end
157
+
158
+ # base pointer - start of data
159
+ def base;
160
+ _check_open!
161
+ self[:base]
162
+ end
163
+
164
+ # size of allocated data - aka self[:size]
165
+ def blob_size;
166
+ _check_open!
167
+ self[:size]
168
+ end
169
+
170
+ # returns the current position offset of the blob buffer - aka self[:off]
171
+ def pos
172
+ _check_open!
173
+ self[:off]
174
+ end
175
+
176
+ # returns the end of the blob buffer in use - aka self[:end]
177
+ def blob_end
178
+ _check_open!
179
+ self[:end]
180
+ end
181
+
182
+ # This method calls dnet(3)'s blob_seek under the hood.
183
+ #
184
+ # blob_seek repositions the offset within blob b to off, according to the
185
+ # directive whence (see lseek(2) for details), returning the new absolute
186
+ # offset, or -1 on failure.
187
+ def seek(off, whence=0)
188
+ _check_open!
189
+ ::Dnet.blob_seek(self, off.to_i, whence.to_i)
190
+ end
191
+
192
+ # Uses dnet(3)'s "blob_index" under the hood. An additional 'rewind'
193
+ # argument was added which can be used to force a rewind before searching.
194
+ # The default value for "rewind" is "false". nil is returned on a failed
195
+ # search.
196
+ def index(bstr, rewind=false)
197
+ _check_open!
198
+ self.rewind() if rewind
199
+ (buf = ::FFI::MemoryPointer.from_string(bstr)).autorelease=true
200
+ if (i=::Dnet.blob_index(self, buf, bstr.size)) > -1
201
+ return i
202
+ else
203
+ return nil
204
+ end
205
+ end
206
+
207
+ # Uses dnet(3)'s "blob_rindex" under the hood. An additional 'rewind'
208
+ # argument was added which can be used to force a rewind before searching.
209
+ # The default value for "rewind" is "false". nil is returned on a failed
210
+ # search.
211
+ def rindex(buf, rewind=false)
212
+ _check_open!
213
+ self.rewind() if rewind
214
+ (buf = ::FFI::MemoryPointer.from_string(bstr)).autorelease=true
215
+ if (i=::Dnet.blob_rindex(self, buf, bstr.size)) > -1
216
+ return i
217
+ else
218
+ return nil
219
+ end
220
+ end
221
+
222
+ # Prints a hexdump on standard output using dnet(3)'s "blob_print" under
223
+ # the hood. Can optionally rewind the blob before dumping using the
224
+ # 'rewind' argument (default: rewind=true).
225
+ #
226
+ # NOTE: len does not appear to do anything at all for blob_print
227
+ def print_dump(rewind=true, len=nil)
228
+ _check_open!
229
+ self.rewind if rewind==true
230
+ len ||= self[:end] - self[:off]
231
+ Dnet.blob_print(self, "hexl", len.to_i)
232
+ end
233
+ end
234
+
235
+ attach_function :blob_new, [], :pointer
236
+ attach_function :blob_read, [Blob, :pointer, :int], :int
237
+ attach_function :blob_write, [Blob, :pointer, :int], :int
238
+ attach_function :blob_seek, [Blob, :int, :int], :int
239
+ attach_function :blob_index, [Blob, :pointer, :int], :int
240
+ attach_function :blob_rindex, [Blob, :string, :int], :int
241
+ attach_function :blob_pack, [Blob, :string, :varargs], :int
242
+ attach_function :blob_unpack, [Blob, :string, :varargs], :int
243
+ attach_function :blob_print, [Blob, :string, :int], :int
244
+ attach_function :blob_free, [Blob], :pointer
245
+ end
246
+
data/lib/dnet/bsd.rb ADDED
@@ -0,0 +1,123 @@
1
+ # Here's where various BSD sockets typedefs and structures go
2
+ # ... good to have around
3
+
4
+ require 'socket'
5
+
6
+ module Dnet
7
+ typedef :uint8, :sa_family_t
8
+ typedef :uint32, :in_addr_t
9
+ typedef :uint16, :in_port_t
10
+
11
+ # contains AF_* constants culled from Ruby's ::Socket
12
+ module AF
13
+ include ::FFI::DRY::ConstMap
14
+ slurp_constants(::Socket, "AF_")
15
+ def self.list; @@list ||= super() ; end
16
+ end
17
+
18
+ # Common superclass for all sockaddr struct classes
19
+ #
20
+ class SockAddrFamily < ::FFI::Struct
21
+ include ::FFI::DRY::StructHelper
22
+
23
+ # returns an address family name for the :family struct member value
24
+ def lookup_family
25
+ ::Dnet::AF[ self[:family] ]
26
+ end
27
+ end
28
+
29
+ # generic sockaddr, always good to have around
30
+ #
31
+ # field :len, :uint8, :desc => 'total length of struct'
32
+ # field :family, :sa_family_t, :desc => 'address family (AF_*)'
33
+ # field :data, :char, :desc => 'variable length bound by :len'
34
+ #
35
+ class SockAddr < SockAddrFamily
36
+ dsl_layout do
37
+ field :len, :uint8, :desc => 'total length of struct'
38
+ field :family, :sa_family_t, :desc => 'address family (AF_*)'
39
+ field :data, :char, :desc => 'variable length bound by :len'
40
+ end
41
+ end
42
+
43
+
44
+ # Used to represent a 32-bit IPv4 address in a sock_addr_in structure
45
+ #
46
+ # field :in_addr, :in_addr_t, :desc => 'inet address' }
47
+ #
48
+ class InAddr < ::FFI::Struct
49
+ include ::FFI::DRY::StructHelper
50
+ dsl_layout { field :in_addr, :in_addr_t, :desc => 'inet address' }
51
+ end
52
+
53
+ # sockaddr inet, always good to have around
54
+ #
55
+ # field :len, :uint8, :desc => 'length of structure (16)'
56
+ # field :family, :sa_family_t, :desc => 'address family (AF_INET)'
57
+ # field :port, :in_port_t, :desc => '16-bit TCP or UDP port number'
58
+ # field :addr, :in_addr_t, :desc => '32-bit IPv4 address'
59
+ # field :_sa_zero, [:uint8,8], :desc => 'unused'
60
+ #
61
+ class SockAddrIn < SockAddrFamily
62
+ dsl_layout do
63
+ field :len, :uint8, :desc => 'length of structure (16)'
64
+ field :family, :sa_family_t, :desc => 'address family (AF_INET)'
65
+ field :port, :in_port_t, :desc => '16-bit TCP or UDP port number'
66
+ field :addr, :in_addr_t, :desc => '32-bit IPv4 address'
67
+ array :_sa_zero, [:uint8,8], :desc => 'unused'
68
+ end
69
+ end
70
+
71
+ # Used to represent an IPv6 address in a sock_addr_in6 structure
72
+ #
73
+ # field :s6_addr, [:uint, 16], :desc => 'IPv6 address'
74
+ #
75
+ class In6Addr < ::FFI::Struct
76
+ include ::FFI::DRY::StructHelper
77
+ dsl_layout { array :s6_addr, [:uint8, 16], :desc => 'IPv6 address' }
78
+ end
79
+
80
+ # IPv6 socket address
81
+ #
82
+ # field :len, :uint8, :desc => 'length of structure(24)'
83
+ # field :family, :sa_family_t, :desc => 'address family (AF_INET6)'
84
+ # field :port, :in_port_t, :desc => 'transport layer port'
85
+ # field :flowinfo, :uint32, :desc => 'priority & flow label'
86
+ # struct :addr, In6Addr :desc => 'IPv6 address'
87
+ #
88
+ class SockAddrIn6 < SockAddrFamily
89
+ dsl_layout do
90
+ field :len, :uint8, :desc => 'length of structure(24)'
91
+ field :family, :sa_family_t, :desc => 'address family (AF_INET6)'
92
+ field :port, :in_port_t, :desc => 'transport layer port'
93
+ field :flowinfo, :uint32, :desc => 'priority & flow label'
94
+ struct :addr, In6Addr, :desc => 'IPv6 address'
95
+ end
96
+ end
97
+
98
+
99
+ # data-link socket address
100
+ #
101
+ # field :len, :uint8, :desc => 'length of structure(variable)'
102
+ # field :family,:sa_family_t, :desc => 'address family (AF_LINK)'
103
+ # field :sdl_index, :uint16, :desc => 'system assigned index, if > 0'
104
+ # field :dltype, :uint8, :desc => 'IFT_ETHER, etc. from net/if_types.h'
105
+ # field :nlen, :uint8, :desc => 'name length, from :_data'
106
+ # field :alen, :uint8, :desc => 'link-layer addres-length'
107
+ # field :slen, :uint8, :desc => 'link-layer selector length'
108
+ # field :_data, :char, :desc => 'minimum work area=12, can be larger'
109
+ #
110
+ class SockAddrDl < SockAddrFamily
111
+ dsl_layout do
112
+ field :len, :uint8, :desc => 'length of structure(variable)'
113
+ field :family, :sa_family_t, :desc => 'address family (AF_LINK)'
114
+ field :sdl_index, :uint16, :desc => 'system assigned index, if > 0'
115
+ field :dltype, :uint8, :desc => 'IFT_ETHER, etc. from net/if_types.h'
116
+ field :nlen, :uint8, :desc => 'name length, from :_data'
117
+ field :alen, :uint8, :desc => 'link-layer addres-length'
118
+ field :slen, :uint8, :desc => 'link-layer selector length'
119
+ field :_data, :char, :desc => 'minimum work area=12, can be larger'
120
+ end
121
+ end
122
+
123
+ end