1 class SocketError < StandardError
5 def self.do_not_reverse_lookup=(setting)
6 @no_reverse_lookup = setting
9 def self.do_not_reverse_lookup
10 @no_reverse_lookup ? true : false
13 def getsockopt(level, optname)
14 Socket::Foreign.getsockopt descriptor, level, optname
17 def setsockopt(level, optname, optval)
18 optval = 1 if optval == true
19 optval = 0 if optval == false
25 MemoryPointer.new :socklen_t do |val|
27 error = Socket::Foreign.setsockopt(descriptor, level, optname, val,
31 MemoryPointer.new optval.size do |val|
32 val.write_string optval
33 error = Socket::Foreign.setsockopt(descriptor, level, optname, val,
37 raise "socket option should be a String, a Fixnum, true, or false"
40 Errno.handle "Unable to set socket option" unless error == 0
45 # to doesn't do anything yet
46 def send(msg, flags, to = nil)
48 buffer = MemoryPointer.new :char, bytes + 1
49 buffer.write_string msg
50 bytes_sent = Socket::Foreign.send(descriptor, buffer, bytes, flags)
51 Errno.handle 'send(2)' if bytes_sent < 0
56 def recv(bytes_to_read, flags = 0)
57 bytes_to_read = Type.coerce_to bytes_to_read, Fixnum, :to_int
58 buffer = MemoryPointer.new :char, bytes_to_read + 1
59 bytes_read = Socket::Foreign.recv(descriptor, buffer, bytes_to_read, flags)
60 Errno.handle 'recv(2)' if bytes_read < 0
61 message = buffer.read_string
67 class Socket < BasicSocket
70 FFI.config_hash("socket").each do |name, value|
74 families = FFI.config_hash('socket').select { |name,| name =~ /^AF_/ }
75 families = families.map { |name, value| [value, name] }
77 AF_TO_FAMILY = Hash[*families.flatten]
81 class AddrInfo < FFI::Struct
82 config("rbx.platform.addrinfo", :ai_flags, :ai_family, :ai_socktype,
83 :ai_protocol, :ai_addrlen, :ai_addr, :ai_canonname, :ai_next)
86 attach_function "accept", :accept, [:int, :pointer, :pointer], :int
87 attach_function "bind", :_bind, [:int, :pointer, :socklen_t], :int
88 attach_function "close", :close, [:int], :int
89 attach_function "connect", :_connect, [:int, :pointer, :socklen_t], :int
90 attach_function "listen", :listen, [:int, :int], :int
91 attach_function "socket", :socket, [:int, :int, :int], :int
92 attach_function "send", :send, [:int, :pointer, :int, :int], :int
93 attach_function "recv", :recv, [:int, :pointer, :int, :int], :int
95 attach_function "getsockopt", :_getsockopt,
96 [:int, :int, :int, :pointer, :pointer], :int
97 attach_function "setsockopt", :setsockopt,
98 [:int, :int, :int, :pointer, :socklen_t], :int
100 attach_function "gai_strerror", :gai_strerror, [:int], :string
102 attach_function "getaddrinfo", :_getaddrinfo,
103 [:string, :string, :pointer, :pointer], :int
104 attach_function "freeaddrinfo", :freeaddrinfo, [:pointer], :void
105 attach_function "getpeername", :_getpeername,
106 [:int, :pointer, :pointer], :int
107 attach_function "getsockname", :_getsockname,
108 [:int, :pointer, :pointer], :int
110 attach_function "socketpair", :socketpair,
111 [:int, :int, :int, :pointer], :int
113 attach_function "gethostname", :gethostname, [:pointer, :size_t], :int
114 attach_function "getservbyname", :getservbyname,
115 [:pointer, :pointer], :pointer
117 attach_function "htons", :htons, [:u_int16_t], :u_int16_t
118 attach_function "ntohs", :ntohs, [:u_int16_t], :u_int16_t
120 attach_function "getnameinfo", :_getnameinfo,
121 [:pointer, :socklen_t, :pointer, :socklen_t,
122 :pointer, :socklen_t, :int], :int
124 def self.bind(descriptor, sockaddr)
125 MemoryPointer.new :char, sockaddr.length do |sockaddr_p|
126 sockaddr_p.write_string sockaddr, sockaddr.length
128 _bind descriptor, sockaddr_p, sockaddr.length
132 def self.connect(descriptor, sockaddr)
134 MemoryPointer.new :char, sockaddr.length do |sockaddr_p|
135 sockaddr_p.write_string sockaddr, sockaddr.length
137 err = _connect descriptor, sockaddr_p, sockaddr.length
140 getsockopt(descriptor, Socket::SOL_SOCKET, Socket::SO_ERROR) if err < 0
144 def self.getsockopt(descriptor, level, optname)
145 MemoryPointer.new 256 do |val| # HACK magic number
146 MemoryPointer.new :socklen_t do |length|
147 length.write_int 256 # HACK magic number
149 err = Socket::Foreign._getsockopt descriptor, level, optname, val, length
151 Errno.handle "Unable to get socket option" unless err == 0
153 return val.read_string(length.read_int)
158 def self.getaddrinfo(host, service = nil, family = 0, socktype = 0, protocol = 0, flags = 0)
159 hints = Socket::Foreign::AddrInfo.new
160 hints[:ai_family] = family
161 hints[:ai_socktype] = socktype
162 hints[:ai_protocol] = protocol
163 hints[:ai_flags] = flags
164 host = "" if host.nil?
167 if (flags & Socket::AI_PASSIVE == 1) # Passive socket
168 family == Socket::AF_INET6 ? (host = "::") : (host = "0.0.0.0") # IPv6 or IPv4
170 family == Socket::AF_INET6 ? (host = "::1") : (host = "127.0.0.1")
174 res_p = MemoryPointer.new :pointer
176 err = _getaddrinfo host, service, hints.pointer, res_p
178 raise SocketError, Socket::Foreign.gai_strerror(err) unless err == 0
180 ptr = res_p.read_pointer
184 res = Socket::Foreign::AddrInfo.new ptr
190 addrinfo << res[:ai_flags]
191 addrinfo << res[:ai_family]
192 addrinfo << res[:ai_socktype]
193 addrinfo << res[:ai_protocol]
194 addrinfo << res[:ai_addr].read_string(res[:ai_addrlen])
195 addrinfo << res[:ai_canonname]
197 addrinfos << addrinfo
199 break unless res[:ai_next]
201 res = Socket::Foreign::AddrInfo.new res[:ai_next]
209 ptr = res_p.read_pointer
211 # Be sure to feed a legit pointer to freeaddrinfo
212 if ptr and !ptr.null?
213 Socket::Foreign.freeaddrinfo ptr
219 def self.getaddress(host)
220 addrinfos = Socket::Foreign.getaddrinfo(host)
221 Socket::Foreign.unpack_sockaddr_in(addrinfos.first[4], false).first
224 def self.getnameinfo(sockaddr,
225 reverse_lookup = !BasicSocket.do_not_reverse_lookup)
229 MemoryPointer.new :char, sockaddr.length do |sockaddr_p|
230 MemoryPointer.new :char, Socket::Constants::NI_MAXHOST do |node|
231 MemoryPointer.new :char, Socket::Constants::NI_MAXSERV do |service|
232 sockaddr_p.write_string sockaddr, sockaddr.length
234 if reverse_lookup then
235 err = _getnameinfo(sockaddr_p, sockaddr.length,
236 node, Socket::Constants::NI_MAXHOST, nil, 0, 0)
239 raise SocketError, Socket::Foreign.gai_strerror(err)
242 name_info[2] = node.read_string
245 err = _getnameinfo(sockaddr_p, sockaddr.length,
246 node, Socket::Constants::NI_MAXHOST,
247 service, Socket::Constants::NI_MAXSERV,
248 Socket::Constants::NI_NUMERICHOST |
249 Socket::Constants::NI_NUMERICSERV)
252 raise SocketError, Socket::Foreign.gai_strerror(err)
255 sa_family = SockAddr_In.new(sockaddr)[:sin_family]
257 name_info[0] = Socket::Constants::AF_TO_FAMILY[sa_family]
258 name_info[1] = Integer service.read_string
259 name_info[3] = node.read_string
264 name_info[2] = name_info[3] if name_info[2].nil?
268 def self.getpeername(descriptor)
269 MemoryPointer.new :char, 128 do |sockaddr_storage_p|
270 MemoryPointer.new :socklen_t do |len_p|
273 err = _getpeername descriptor, sockaddr_storage_p, len_p
275 Errno.handle 'getpeername(2)' unless err == 0
277 sockaddr_storage_p.read_string len_p.read_int
282 def self.getsockname(descriptor)
283 MemoryPointer.new :char, 128 do |sockaddr_storage_p|
284 MemoryPointer.new :socklen_t do |len_p|
287 err = _getsockname descriptor, sockaddr_storage_p, len_p
289 Errno.handle 'getsockname(2)' unless err == 0
291 sockaddr_storage_p.read_string len_p.read_int
296 def self.pack_sockaddr_in(name, port, type, flags)
297 hints = Socket::Foreign::AddrInfo.new
298 hints[:ai_family] = Socket::AF_UNSPEC
299 hints[:ai_socktype] = type
300 hints[:ai_flags] = flags
302 res_p = MemoryPointer.new :pointer
304 err = _getaddrinfo name, port, hints.pointer, res_p
306 raise SocketError, Socket::Foreign.gai_strerror(err) unless err == 0
308 return [] if res_p.read_pointer.null?
310 res = Socket::Foreign::AddrInfo.new res_p.read_pointer
312 return res[:ai_addr].read_string(res[:ai_addrlen])
318 ptr = res_p.read_pointer
320 freeaddrinfo ptr if ptr and not ptr.null?
326 def self.unpack_sockaddr_in(sockaddr, reverse_lookup)
327 family, port, host, ip = getnameinfo sockaddr, reverse_lookup
328 # On some systems this doesn't fail for families other than AF_INET(6)
329 # so we raise manually here.
330 raise ArgumentError, 'not an AF_INET/AF_INET6 sockaddr' unless family =~ /AF_INET/
331 return host, ip, port
335 module ListenAndAccept
337 backlog = Type.coerce_to backlog, Fixnum, :to_int
339 err = Socket::Foreign.listen descriptor, backlog
341 Errno.handle 'listen(2)' unless err == 0
353 MemoryPointer.new 1024 do |sockaddr_p| # HACK from MRI
354 MemoryPointer.new :int do |size_p|
355 fd = Socket::Foreign.accept descriptor, sockaddr_p, size_p
359 Errno.handle 'accept(2)' if fd < 0
361 socket = self.class.superclass.allocate
362 socket.__send__ :from_descriptor, fd
366 include Socket::ListenAndAccept
367 include Socket::Constants
369 class SockAddr_In < FFI::Struct
370 config("rbx.platform.sockaddr_in", :sin_family, :sin_port, :sin_addr, :sin_zero)
372 def initialize(sockaddrin)
373 @p = MemoryPointer.new sockaddrin.size
374 @p.write_string(sockaddrin)
379 @p.read_string(@p.size)
384 class SockAddr_Un < FFI::Struct
385 config("rbx.platform.sockaddr_un", :sun_family, :sun_path)
387 def initialize(filename = nil)
388 maxfnsize = self.size - ( FFI.config("sockaddr_un.sun_family.size") + 1 )
390 if(filename && filename.length > maxfnsize )
391 raise ArgumentError, "too long unix socket path (max: #{fnsize}bytes)"
393 @p = MemoryPointer.new self.size
395 @p.write_string( [Socket::AF_UNIX].pack("s") + filename )
401 @p.read_string(self.size)
403 end if (FFI.config("sockaddr_un.sun_family.offset") && Socket.const_defined?(:AF_UNIX))
405 def self.getaddrinfo(host, service = nil, family = nil, socktype = nil,
406 protocol = nil, flags = nil)
407 host = '' if host.nil?
408 service = service.to_s if service
415 addrinfos = Socket::Foreign.getaddrinfo(host, service, family, socktype,
418 addrinfos.map do |ai|
420 addrinfo << Socket::Constants::AF_TO_FAMILY[ai[1]]
422 sockaddr = Socket::Foreign::unpack_sockaddr_in ai[4], true
424 addrinfo << sockaddr.pop # port
425 addrinfo.concat sockaddr # hosts
434 MemoryPointer.new :char, 1024 do |mp| #magic number 1024 comes from MRI
435 Socket::Foreign.gethostname(mp, 1024) # same here
436 return mp.read_string
440 class Servent < FFI::Struct
441 config("rbx.platform.servent", :s_name, :s_aliases, :s_port, :s_proto)
444 @p = MemoryPointer.new data.size
445 @p.write_string(data)
455 def self.getservbyname(service, proto='tcp')
456 MemoryPointer.new :char, service.length + 1 do |svc|
457 MemoryPointer.new :char, proto.length + 1 do |prot|
458 svc.write_string(service + "\0")
459 prot.write_string(proto + "\0")
460 fn = Socket::Foreign.getservbyname(svc, prot)
462 raise SocketError, "no such service #{service}/#{proto}" if fn.nil?
464 s = Servent.new(fn.read_string(Servent.size))
465 return Socket::Foreign.ntohs(s[:s_port])
470 def self.pack_sockaddr_in(port, host, type = Socket::SOCK_DGRAM, flags = 0)
471 host = "0.0.0.0" if host.empty?
472 Socket::Foreign.pack_sockaddr_in host.to_s, port.to_s, type, flags
475 def self.unpack_sockaddr_in(sockaddr)
476 host, address, port = Socket::Foreign.unpack_sockaddr_in sockaddr, false
478 return [port, address]
479 rescue SocketError => e
480 if e.message =~ /ai_family not supported/ then # HACK platform specific?
481 raise ArgumentError, 'not an AF_INET/AF_INET6 sockaddr'
487 def self.socketpair(domain, type, protocol)
488 MemoryPointer.new :int, 2 do |mp|
489 Socket::Foreign.socketpair(domain, type, protocol, mp)
490 fd0, fd1 = mp.read_array_of_int(2)
492 [ from_descriptor(fd0), from_descriptor(fd1) ]
497 alias_method :sockaddr_in, :pack_sockaddr_in
498 alias_method :pair, :socketpair
501 # Only define these methods if we support unix sockets
502 if self.const_defined?(:SockAddr_Un)
503 def self.pack_sockaddr_un(file)
504 SockAddr_Un.new(file).to_s
507 def self.unpack_sockaddr_un(addr)
509 if addr.length > FFI.config("sockaddr_un.sizeof")
510 raise TypeError, "too long sockaddr_un - #{addr.length} longer than #{FFI.config("sockaddr_un.sizeof")}"
513 struct = SockAddr_Un.new
514 struct.pointer.write_string(addr)
520 alias_method :sockaddr_un, :pack_sockaddr_un
524 def initialize(family, socket_type, protocol)
525 descriptor = Socket::Foreign.socket family, socket_type, protocol
527 Errno.handle 'socket(2)' if descriptor < 0
532 def self.from_descriptor(fixnum)
534 sock.from_descriptor(fixnum)
538 def from_descriptor(fixnum)
543 def bind(server_sockaddr)
544 err = Socket::Foreign.bind(descriptor, server_sockaddr)
545 Errno.handle 'bind(2)' unless err == 0
549 class UNIXSocket < BasicSocket
558 def unix_setup(server = false)
559 syscall = 'socket(2)'
561 sock = Socket::Foreign.socket Socket::Constants::AF_UNIX, Socket::Constants::SOCK_STREAM, 0
563 # TODO - Do we need to sync = true here?
566 Errno.handle syscall if descriptor < 0
568 sockaddr = Socket.pack_sockaddr_un(@path)
572 status = Socket::Foreign.bind descriptor, sockaddr
574 syscall = 'connect(2)'
575 status = Socket::Foreign.connect descriptor, sockaddr
579 Socket::Foreign.close descriptor
584 syscall = 'listen(2)'
585 status = Socket::Foreign.listen descriptor, 5
586 Errno.handle syscall if status < 0
594 sockaddr = Socket::Foreign.getsockname descriptor
595 _, sock_path = sockaddr.unpack('SZ*')
596 ["AF_UNIX", sock_path]
600 sockaddr = Socket::Foreign.getpeername descriptor
601 _, sock_path = sockaddr.unpack('SZ*')
602 ["AF_UNIX", sock_path]
605 def from_descriptor(descriptor)
610 private :from_descriptor
614 class UNIXServer < UNIXSocket
616 include Socket::ListenAndAccept
625 class IPSocket < BasicSocket
627 def self.getaddress(host)
628 Socket::Foreign.getaddress host
632 sockaddr = Socket::Foreign.getsockname descriptor
634 Socket::Foreign.getnameinfo sockaddr
638 sockaddr = Socket::Foreign.getpeername descriptor
640 Socket::Foreign.getnameinfo sockaddr
644 class UDPSocket < IPSocket
646 status = Socket::Foreign.socket Socket::AF_INET, Socket::SOCK_DGRAM, 0
647 Errno.handle 'socket(2)' if status < 0
652 @host = host.to_s if host
653 @port = port.to_s if port
655 addrinfos = Socket::Foreign.getaddrinfo(@host,
658 Socket::SOCK_DGRAM, 0, 0)
662 addrinfos.each do |addrinfo|
663 flags, family, socket_type, protocol, sockaddr, canonname = addrinfo
665 status = Socket::Foreign.bind descriptor, sockaddr
672 Socket::Foreign.close descriptor
678 "#<#{self.class}:0x#{object_id.to_s(16)} #{@host}:#{@port}>"
683 class TCPSocket < IPSocket
685 def self.gethostbyname(hostname)
686 addrinfos = Socket.getaddrinfo(hostname)
688 hostname = addrinfos.first[2]
689 family = addrinfos.first[4]
692 addrinfos.each do |a|
693 alternatives << a[2] unless a[2] == hostname
694 addresses << a[3] if a[4] == family
697 [hostname, alternatives.uniq, family] + addresses.uniq
700 def initialize(host, port)
704 tcp_setup @host, @port
708 def tcp_setup(remote_host, remote_service, local_host = nil,
709 local_service = nil, server = false)
712 remote_host = remote_host.to_s if remote_host
713 remote_service = remote_service.to_s if remote_service
715 flags = server ? Socket::AI_PASSIVE : 0
716 @remote_addrinfo = Socket::Foreign.getaddrinfo(remote_host,
719 Socket::SOCK_STREAM, 0,
722 if server == false and (local_host or local_service) then
723 @local_addrinfo = Socket::Foreign.getaddrinfo(local_host,
726 Socket::SOCK_STREAM, 0, 0)
729 @remote_addrinfo.each do |addrinfo|
730 flags, family, socket_type, protocol, sockaddr, canonname = addrinfo
732 status = Socket::Foreign.socket family, socket_type, protocol
733 syscall = 'socket(2)'
736 next if descriptor < 0
742 setsockopt(Socket::Constants::SOL_SOCKET,
743 Socket::Constants::SO_REUSEADDR, true)
744 rescue SystemCallError
747 status = Socket::Foreign.bind descriptor, sockaddr
750 if @local_addrinfo then
751 status = bind descriptor, @local_addrinfo.first[4]
756 status = Socket::Foreign.connect descriptor, sockaddr
757 syscall = 'connect(2)'
766 Socket::Foreign.close descriptor
770 err = Socket::Foreign.listen descriptor, 5
771 Errno.handle syscall unless err == 0
778 def from_descriptor(descriptor)
783 private :from_descriptor
787 class TCPServer < TCPSocket
789 include Socket::ListenAndAccept
791 def initialize(host, port = nil)
792 if Fixnum === host and port.nil? then
797 port = StringValue port unless port.__kind_of__ Fixnum
802 tcp_setup @host, @port, nil, nil, true