diff --git a/lib/linux/netlink/nlsocket.rb b/lib/linux/netlink/nlsocket.rb index a31d7ed..50ff0f6 100644 --- a/lib/linux/netlink/nlsocket.rb +++ b/lib/linux/netlink/nlsocket.rb @@ -2,6 +2,7 @@ require 'socket' require 'linux/constants' require 'linux/error' require 'linux/netlink/message' +require 'linux/sendmsg' module Linux module Netlink @@ -194,7 +195,8 @@ module Netlink if select([@socket], nil, nil, timeout) mesg, sender, rflags, controls = @socket.recvmsg raise EOFError unless mesg - NLSocket.check_sockaddr(sender.to_sockaddr) + sender = sender.to_sockaddr if sender.respond_to? :to_sockaddr + NLSocket.check_sockaddr(sender) mesg else raise "Timeout" diff --git a/lib/linux/sendmsg.rb b/lib/linux/sendmsg.rb new file mode 100644 index 0000000..329c98e --- /dev/null +++ b/lib/linux/sendmsg.rb @@ -0,0 +1,80 @@ +# Patchup to add Socket#sendmsg and Socket#recvmsg for ruby 1.8 + +if BasicSocket.instance_methods.grep(/^sendmsg$/).empty? + require 'ffi' + + class BasicSocket + module FFIExt + extend FFI::Library + ffi_lib FFI::Library::LIBC + attach_function :sendmsg, [:int, :buffer_in, :int], :ssize_t + attach_function :recvmsg, [:int, :buffer_inout, :int], :ssize_t + end + + class Msghdr < FFI::Struct + layout :name, :pointer, + :namelen, :socklen_t, + :iov, :pointer, + :iovlen, :size_t, + :control, :pointer, + :controllen, :socklen_t, + :flags, :int + end + + class IOVec < FFI::Struct + layout :base, :pointer, + :len, :size_t + end + + def sendmsg(mesg, flags=0, dest_sockaddr=nil, *controls) + data = FFI::MemoryPointer.new(mesg.bytesize, 1, false) + data.put_bytes(0, mesg) + + iov = IOVec.new + iov[:base] = data + iov[:len] = mesg.bytesize + + header = Msghdr.new + header[:name] = nil # TODO: dest_sockaddr + header[:namelen] = 0 # dest_sockaddr + header[:iov] = iov + header[:iovlen] = 1 + header[:control] = nil # TODO: controls + header[:controllen] = 0 # controls + header[:flags] = flags + + Kernel.select(nil, [self]) + res = FFIExt.sendmsg(fileno, header, flags) + raise "sendmsg error: #{res}" if res < 0 # FIXME: read errno + # return numbytes_sent + res + end + + def recvmsg(maxmesglen=nil, flags=0, maxcontrollen=nil, opts={}) + data = FFI::MemoryPointer.new(maxmesglen || 4096, 1, false) + namebuf = FFI::MemoryPointer.new(64, 1, false) + + iov = IOVec.new + iov[:base] = data + iov[:len] = data.size + + header = Msghdr.new + header[:name] = namebuf + header[:namelen] = namebuf.size + header[:iov] = iov + header[:iovlen] = 1 + header[:flags] = flags + + Kernel.select([self]) + res = FFIExt.recvmsg(fileno, header, flags) + raise "recvmsg error: #{res}" if res < 0 # FIXME: read errno + # return [mesg, sender_addrinfo, rflags, *controls] + return [ + data.get_bytes(0, res), + namebuf.get_bytes(0, header[:namelen]), + header[:flags] + # TODO: controls + ] + end + end +end