3 class SocketError < StandardError
7 def self.do_not_reverse_lookup=(setting)
8 @no_reverse_lookup = setting
11 def self.do_not_reverse_lookup
12 @no_reverse_lookup ? true : false
15 def getsockopt(level, optname)
16 Socket::Foreign.getsockopt descriptor, level, optname
19 def setsockopt(level, optname, optval)
20 optval = 1 if optval == true
21 optval = 0 if optval == false
27 MemoryPointer.new :socklen_t do |val|
29 error = Socket::Foreign.setsockopt(descriptor, level, optname, val,
33 MemoryPointer.new optval.size do |val|
34 val.write_string optval
35 error = Socket::Foreign.setsockopt(descriptor, level, optname, val,
39 raise "socket option should be a String, a Fixnum, true, or false"
42 Errno.handle "Unable to set socket option" unless error == 0
48 return Socket::Foreign.getsockname(descriptor)
51 def send(msg, flags, *rest)
52 if ((rest.size != 2) && (rest.size != 0))
53 raise ArgumentError, '#send takes 0 or 2 arguments, passed #{rest.size}'
56 connect(*rest) if rest.size == 2
59 MemoryPointer.new :char, bytes + 1 do |buffer|
60 buffer.write_string msg
61 bytes_sent = Socket::Foreign.send(descriptor, buffer, bytes, flags)
62 Errno.handle 'send(2)' if bytes_sent < 0
67 def recv(bytes_to_read, flags = 0)
68 bytes_to_read = Type.coerce_to bytes_to_read, Fixnum, :to_int
69 buffer = MemoryPointer.new :char, bytes_to_read + 1
70 # Wait until we have something to read, so we don't block other threads
72 bytes_read = Socket::Foreign.recv(descriptor, buffer, bytes_to_read, flags)
73 Errno.handle 'recv(2)' if bytes_read < 0
74 message = buffer.read_string(bytes_read)
80 class Socket < BasicSocket
83 FFI.config_hash("socket").each do |name, value|
87 families = FFI.config_hash('socket').select { |name,| name =~ /^AF_/ }
88 families = families.map { |name, value| [value, name] }
90 AF_TO_FAMILY = Hash[*families.flatten]
94 class AddrInfo < FFI::Struct
95 config("rbx.platform.addrinfo", :ai_flags, :ai_family, :ai_socktype,
96 :ai_protocol, :ai_addrlen, :ai_addr, :ai_canonname, :ai_next)
99 attach_function "accept", :accept, [:int, :pointer, :pointer], :int
100 attach_function "bind", :_bind, [:int, :pointer, :socklen_t], :int
101 attach_function "close", :close, [:int], :int
102 attach_function "connect", :_connect, [:int, :pointer, :socklen_t], :int
103 attach_function "listen", :listen, [:int, :int], :int
104 attach_function "socket", :socket, [:int, :int, :int], :int
105 attach_function "send", :send, [:int, :pointer, :int, :int], :int
106 attach_function "recv", :recv, [:int, :pointer, :int, :int], :int
107 attach_function "recvfrom", :recvfrom, [:int, :pointer, :size_t, :int,
108 :pointer, :pointer], :int
110 attach_function "getsockopt", :_getsockopt,
111 [:int, :int, :int, :pointer, :pointer], :int
112 attach_function "setsockopt", :setsockopt,
113 [:int, :int, :int, :pointer, :socklen_t], :int
115 attach_function "gai_strerror", :gai_strerror, [:int], :string
117 attach_function "getaddrinfo", :_getaddrinfo,
118 [:string, :string, :pointer, :pointer], :int
119 attach_function "freeaddrinfo", :freeaddrinfo, [:pointer], :void
120 attach_function "getpeername", :_getpeername,
121 [:int, :pointer, :pointer], :int
122 attach_function "getsockname", :_getsockname,
123 [:int, :pointer, :pointer], :int
125 attach_function "socketpair", :socketpair,
126 [:int, :int, :int, :pointer], :int
128 attach_function "gethostname", :gethostname, [:pointer, :size_t], :int
129 attach_function "getservbyname", :getservbyname,
130 [:pointer, :pointer], :pointer
132 attach_function "htons", :htons, [:u_int16_t], :u_int16_t
133 attach_function "ntohs", :ntohs, [:u_int16_t], :u_int16_t
135 attach_function "getnameinfo", :_getnameinfo,
136 [:pointer, :socklen_t, :pointer, :socklen_t,
137 :pointer, :socklen_t, :int], :int
139 def self.bind(descriptor, sockaddr)
140 MemoryPointer.new :char, sockaddr.length do |sockaddr_p|
141 sockaddr_p.write_string sockaddr, sockaddr.length
143 _bind descriptor, sockaddr_p, sockaddr.length
147 def self.connect(descriptor, sockaddr)
149 MemoryPointer.new :char, sockaddr.length do |sockaddr_p|
150 sockaddr_p.write_string sockaddr, sockaddr.length
152 err = _connect descriptor, sockaddr_p, sockaddr.length
155 getsockopt(descriptor, Socket::SOL_SOCKET, Socket::SO_ERROR) if err < 0
159 def self.getsockopt(descriptor, level, optname)
160 MemoryPointer.new 256 do |val| # HACK magic number
161 MemoryPointer.new :socklen_t do |length|
162 length.write_int 256 # HACK magic number
164 err = Socket::Foreign._getsockopt descriptor, level, optname, val, length
166 Errno.handle "Unable to get socket option" unless err == 0
168 return val.read_string(length.read_int)
173 def self.getaddrinfo(host, service = nil, family = 0, socktype = 0, protocol = 0, flags = 0)
174 hints = Socket::Foreign::AddrInfo.new
175 hints[:ai_family] = family
176 hints[:ai_socktype] = socktype
177 hints[:ai_protocol] = protocol
178 hints[:ai_flags] = flags
179 host = "" if host.nil?
182 if (flags & Socket::AI_PASSIVE == 1) # Passive socket
183 family == Socket::AF_INET6 ? (host = "::") : (host = "0.0.0.0") # IPv6 or IPv4
185 family == Socket::AF_INET6 ? (host = "::1") : (host = "127.0.0.1")
189 res_p = MemoryPointer.new :pointer
191 err = _getaddrinfo host, service, hints.pointer, res_p
193 raise SocketError, Socket::Foreign.gai_strerror(err) unless err == 0
195 ptr = res_p.read_pointer
199 res = Socket::Foreign::AddrInfo.new ptr
205 addrinfo << res[:ai_flags]
206 addrinfo << res[:ai_family]
207 addrinfo << res[:ai_socktype]
208 addrinfo << res[:ai_protocol]
209 addrinfo << res[:ai_addr].read_string(res[:ai_addrlen])
210 addrinfo << res[:ai_canonname]
212 addrinfos << addrinfo
214 break unless res[:ai_next]
216 res = Socket::Foreign::AddrInfo.new res[:ai_next]
224 ptr = res_p.read_pointer
226 # Be sure to feed a legit pointer to freeaddrinfo
227 if ptr and !ptr.null?
228 Socket::Foreign.freeaddrinfo ptr
234 def self.getaddress(host)
235 addrinfos = Socket::Foreign.getaddrinfo(host)
236 Socket::Foreign.unpack_sockaddr_in(addrinfos.first[4], false).first
239 def self.getnameinfo(sockaddr,
240 reverse_lookup = !BasicSocket.do_not_reverse_lookup)
244 MemoryPointer.new :char, sockaddr.length do |sockaddr_p|
245 MemoryPointer.new :char, Socket::Constants::NI_MAXHOST do |node|
246 MemoryPointer.new :char, Socket::Constants::NI_MAXSERV do |service|
247 sockaddr_p.write_string sockaddr, sockaddr.length
249 if reverse_lookup then
250 err = _getnameinfo(sockaddr_p, sockaddr.length,
251 node, Socket::Constants::NI_MAXHOST, nil, 0, 0)
254 raise SocketError, Socket::Foreign.gai_strerror(err)
257 name_info[2] = node.read_string
260 err = _getnameinfo(sockaddr_p, sockaddr.length,
261 node, Socket::Constants::NI_MAXHOST,
262 service, Socket::Constants::NI_MAXSERV,
263 Socket::Constants::NI_NUMERICHOST |
264 Socket::Constants::NI_NUMERICSERV)
267 raise SocketError, Socket::Foreign.gai_strerror(err)
270 sa_family = SockAddr_In.new(sockaddr)[:sin_family]
272 name_info[0] = Socket::Constants::AF_TO_FAMILY[sa_family]
273 name_info[1] = Integer service.read_string
274 name_info[3] = node.read_string
279 name_info[2] = name_info[3] if name_info[2].nil?
283 def self.getpeername(descriptor)
284 MemoryPointer.new :char, 128 do |sockaddr_storage_p|
285 MemoryPointer.new :socklen_t do |len_p|
288 err = _getpeername descriptor, sockaddr_storage_p, len_p
290 Errno.handle 'getpeername(2)' unless err == 0
292 sockaddr_storage_p.read_string len_p.read_int
297 def self.getsockname(descriptor)
298 MemoryPointer.new :char, 128 do |sockaddr_storage_p|
299 MemoryPointer.new :socklen_t do |len_p|
302 err = _getsockname descriptor, sockaddr_storage_p, len_p
304 Errno.handle 'getsockname(2)' unless err == 0
306 sockaddr_storage_p.read_string len_p.read_int
311 def self.pack_sockaddr_in(name, port, type, flags)
312 hints = Socket::Foreign::AddrInfo.new
313 hints[:ai_family] = Socket::AF_INET
314 hints[:ai_socktype] = type
315 hints[:ai_flags] = flags
317 res_p = MemoryPointer.new :pointer
319 err = _getaddrinfo name, port, hints.pointer, res_p
321 raise SocketError, Socket::Foreign.gai_strerror(err) unless err == 0
323 return [] if res_p.read_pointer.null?
325 res = Socket::Foreign::AddrInfo.new res_p.read_pointer
327 return res[:ai_addr].read_string(res[:ai_addrlen])
333 ptr = res_p.read_pointer
335 freeaddrinfo ptr if ptr and not ptr.null?
341 def self.unpack_sockaddr_in(sockaddr, reverse_lookup)
342 family, port, host, ip = getnameinfo sockaddr, reverse_lookup
343 # On some systems this doesn't fail for families other than AF_INET(6)
344 # so we raise manually here.
345 raise ArgumentError, 'not an AF_INET/AF_INET6 sockaddr' unless family =~ /AF_INET/
346 return host, ip, port
350 module ListenAndAccept
352 backlog = Type.coerce_to backlog, Fixnum, :to_int
354 err = Socket::Foreign.listen descriptor, backlog
356 Errno.handle 'listen(2)' unless err == 0
368 MemoryPointer.new 1024 do |sockaddr_p| # HACK from MRI
369 MemoryPointer.new :int do |size_p|
370 fd = Socket::Foreign.accept descriptor, sockaddr_p, size_p
374 Errno.handle 'accept(2)' if fd < 0
376 socket = self.class.superclass.allocate
377 socket.__send__ :from_descriptor, fd
381 include Socket::ListenAndAccept
382 include Socket::Constants
384 class SockAddr_In < FFI::Struct
385 config("rbx.platform.sockaddr_in", :sin_family, :sin_port, :sin_addr, :sin_zero)
387 def initialize(sockaddrin)
388 @p = MemoryPointer.new sockaddrin.size
389 @p.write_string(sockaddrin)
394 @p.read_string(@p.size)
399 class SockAddr_Un < FFI::Struct
400 config("rbx.platform.sockaddr_un", :sun_family, :sun_path)
402 def initialize(filename = nil)
403 maxfnsize = self.size - ( FFI.config("sockaddr_un.sun_family.size") + 1 )
405 if(filename && filename.length > maxfnsize )
406 raise ArgumentError, "too long unix socket path (max: #{fnsize}bytes)"
408 @p = MemoryPointer.new self.size
410 @p.write_string( [Socket::AF_UNIX].pack("s") + filename )
416 @p.read_string(self.size)
418 end if (FFI.config("sockaddr_un.sun_family.offset") && Socket.const_defined?(:AF_UNIX))
420 def self.getaddrinfo(host, service = nil, family = nil, socktype = nil,
421 protocol = nil, flags = nil)
422 host = '' if host.nil?
423 service = service.to_s if service
430 addrinfos = Socket::Foreign.getaddrinfo(host, service, family, socktype,
433 addrinfos.map do |ai|
435 addrinfo << Socket::Constants::AF_TO_FAMILY[ai[1]]
437 sockaddr = Socket::Foreign::unpack_sockaddr_in ai[4], true
439 addrinfo << sockaddr.pop # port
440 addrinfo.concat sockaddr # hosts
449 MemoryPointer.new :char, 1024 do |mp| #magic number 1024 comes from MRI
450 Socket::Foreign.gethostname(mp, 1024) # same here
451 return mp.read_string
455 class Servent < FFI::Struct
456 config("rbx.platform.servent", :s_name, :s_aliases, :s_port, :s_proto)
459 @p = MemoryPointer.new data.size
460 @p.write_string(data)
470 def self.getservbyname(service, proto='tcp')
471 MemoryPointer.new :char, service.length + 1 do |svc|
472 MemoryPointer.new :char, proto.length + 1 do |prot|
473 svc.write_string(service + "\0")
474 prot.write_string(proto + "\0")
475 fn = Socket::Foreign.getservbyname(svc, prot)
477 raise SocketError, "no such service #{service}/#{proto}" if fn.nil?
479 s = Servent.new(fn.read_string(Servent.size))
480 return Socket::Foreign.ntohs(s[:s_port])
485 def self.pack_sockaddr_in(port, host, type = Socket::SOCK_DGRAM, flags = 0)
486 host = "0.0.0.0" if host.empty?
487 Socket::Foreign.pack_sockaddr_in host.to_s, port.to_s, type, flags
490 def self.unpack_sockaddr_in(sockaddr)
491 host, address, port = Socket::Foreign.unpack_sockaddr_in sockaddr, false
493 return [port, address]
494 rescue SocketError => e
495 if e.message =~ /ai_family not supported/ then # HACK platform specific?
496 raise ArgumentError, 'not an AF_INET/AF_INET6 sockaddr'
502 def self.socketpair(domain, type, protocol)
503 MemoryPointer.new :int, 2 do |mp|
504 Socket::Foreign.socketpair(domain, type, protocol, mp)
505 fd0, fd1 = mp.read_array_of_int(2)
507 [ from_descriptor(fd0), from_descriptor(fd1) ]
512 alias_method :sockaddr_in, :pack_sockaddr_in
513 alias_method :pair, :socketpair
516 # Only define these methods if we support unix sockets
517 if self.const_defined?(:SockAddr_Un)
518 def self.pack_sockaddr_un(file)
519 SockAddr_Un.new(file).to_s
522 def self.unpack_sockaddr_un(addr)
524 if addr.length > FFI.config("sockaddr_un.sizeof")
525 raise TypeError, "too long sockaddr_un - #{addr.length} longer than #{FFI.config("sockaddr_un.sizeof")}"
528 struct = SockAddr_Un.new
529 struct.pointer.write_string(addr)
535 alias_method :sockaddr_un, :pack_sockaddr_un
539 def initialize(family, socket_type, protocol)
540 descriptor = Socket::Foreign.socket family, socket_type, protocol
542 Errno.handle 'socket(2)' if descriptor < 0
547 def self.from_descriptor(fixnum)
549 sock.from_descriptor(fixnum)
553 def from_descriptor(fixnum)
558 def bind(server_sockaddr)
559 err = Socket::Foreign.bind(descriptor, server_sockaddr)
560 Errno.handle 'bind(2)' unless err == 0
564 class UNIXSocket < BasicSocket
573 def unix_setup(server = false)
574 syscall = 'socket(2)'
576 sock = Socket::Foreign.socket Socket::Constants::AF_UNIX, Socket::Constants::SOCK_STREAM, 0
578 # TODO - Do we need to sync = true here?
581 Errno.handle syscall if descriptor < 0
583 sockaddr = Socket.pack_sockaddr_un(@path)
587 status = Socket::Foreign.bind descriptor, sockaddr
589 syscall = 'connect(2)'
590 status = Socket::Foreign.connect descriptor, sockaddr
594 Socket::Foreign.close descriptor
599 syscall = 'listen(2)'
600 status = Socket::Foreign.listen descriptor, 5
601 Errno.handle syscall if status < 0
609 sockaddr = Socket::Foreign.getsockname descriptor
610 _, sock_path = sockaddr.unpack('SZ*')
611 ["AF_UNIX", sock_path]
615 sockaddr = Socket::Foreign.getpeername descriptor
616 _, sock_path = sockaddr.unpack('SZ*')
617 ["AF_UNIX", sock_path]
620 def from_descriptor(descriptor)
625 private :from_descriptor
629 class UNIXServer < UNIXSocket
631 include Socket::ListenAndAccept
640 class IPSocket < BasicSocket
642 def self.getaddress(host)
643 Socket::Foreign.getaddress host
647 sockaddr = Socket::Foreign.getsockname descriptor
649 Socket::Foreign.getnameinfo sockaddr
653 sockaddr = Socket::Foreign.getpeername descriptor
655 Socket::Foreign.getnameinfo sockaddr
658 def recvfrom(maxlen, flags = 0)
659 maxlen = Type.coerce_to maxlen, Fixnum, :to_int
661 sender_sockaddr = nil
663 MemoryPointer.new(:char, maxlen + 1) do |buffer_p|
664 MemoryPointer.new :char, 128 do |sockaddr_storage_p|
665 MemoryPointer.new :socklen_t do |len_p|
667 bytes_read = Socket::Foreign.recvfrom(descriptor, buffer_p, maxlen,
668 flags, sockaddr_storage_p, len_p)
669 Errno.handle 'recvfrom(2)' if bytes_read < 0
671 mesg = buffer_p.read_string
672 sockaddr = sockaddr_storage_p.read_string(len_p.read_int)
673 sockaddr = Socket::Foreign.unpack_sockaddr_in(sockaddr, false)
674 sender_sockaddr = [ "AF_INET", sockaddr[2], sockaddr[0], sockaddr[1] ]
679 return [mesg, sender_sockaddr]
682 def recvfrom_nonblock(maxlen, flags = 0)
683 # Set socket to non-blocking, if we can
684 # Todo: Ensure this works in Windows! If not, I claim that's Fcntl's fault.
685 fcntl(Fcntl::F_SETFL, Fcntl::O_NONBLOCK)
687 # Wait until we have something to read
689 return recvfrom(maxlen, flags)
693 class UDPSocket < IPSocket
694 def initialize(*args)
695 socktype = Socket::AF_INET
696 raise ArgumentError, 'too many arguments' if args.size > 1
697 socktype = args[0] if args.size == 1
698 status = Socket::Foreign.socket socktype, Socket::SOCK_DGRAM,
700 Errno.handle 'socket(2)' if status < 0
705 @host = host.to_s if host
706 @port = port.to_s if port
708 addrinfos = Socket::Foreign.getaddrinfo(@host,
711 Socket::SOCK_DGRAM, 0,
716 addrinfos.each do |addrinfo|
717 flags, family, socket_type, protocol, sockaddr, canonname = addrinfo
719 status = Socket::Foreign.bind descriptor, sockaddr
726 Socket::Foreign.close descriptor
731 def connect(host, port)
732 sockaddr = Socket.pack_sockaddr_in(port, host)
733 syscall = 'connect(2)'
734 status = Socket::Foreign.connect descriptor, sockaddr
737 Socket::Foreign.close descriptor
744 "#<#{self.class}:0x#{object_id.to_s(16)} #{@host}:#{@port}>"
749 class TCPSocket < IPSocket
751 def self.gethostbyname(hostname)
752 addrinfos = Socket.getaddrinfo(hostname)
754 hostname = addrinfos.first[2]
755 family = addrinfos.first[4]
758 addrinfos.each do |a|
759 alternatives << a[2] unless a[2] == hostname
760 addresses << a[3] if a[4] == family
763 [hostname, alternatives.uniq, family] + addresses.uniq
766 def initialize(host, port)
770 tcp_setup @host, @port
774 def tcp_setup(remote_host, remote_service, local_host = nil,
775 local_service = nil, server = false)
778 remote_host = remote_host.to_s if remote_host
779 remote_service = remote_service.to_s if remote_service
781 flags = server ? Socket::AI_PASSIVE : 0
782 @remote_addrinfo = Socket::Foreign.getaddrinfo(remote_host,
785 Socket::SOCK_STREAM, 0,
788 if server == false and (local_host or local_service) then
789 @local_addrinfo = Socket::Foreign.getaddrinfo(local_host,
792 Socket::SOCK_STREAM, 0, 0)
795 @remote_addrinfo.each do |addrinfo|
796 flags, family, socket_type, protocol, sockaddr, canonname = addrinfo
798 status = Socket::Foreign.socket family, socket_type, protocol
799 syscall = 'socket(2)'
802 next if descriptor < 0
808 setsockopt(Socket::Constants::SOL_SOCKET,
809 Socket::Constants::SO_REUSEADDR, true)
810 rescue SystemCallError
813 status = Socket::Foreign.bind descriptor, sockaddr
816 if @local_addrinfo then
817 status = bind descriptor, @local_addrinfo.first[4]
822 status = Socket::Foreign.connect descriptor, sockaddr
823 syscall = 'connect(2)'
832 Socket::Foreign.close descriptor
836 err = Socket::Foreign.listen descriptor, 5
837 Errno.handle syscall unless err == 0
844 def from_descriptor(descriptor)
849 private :from_descriptor
853 class TCPServer < TCPSocket
855 include Socket::ListenAndAccept
857 def initialize(host, port = nil)
858 if Fixnum === host and port.nil? then
863 port = StringValue port unless port.__kind_of__ Fixnum
868 tcp_setup @host, @port, nil, nil, true