Imported File#ftype spec from rubyspecs.
[rbx.git] / lib / socket.rb
blob5de2c5e44c1ca35b6d9e632049c7290ae992cd5b
1 class SocketError < StandardError
2 end
4 class BasicSocket < IO
5   def self.do_not_reverse_lookup=(setting)
6     @no_reverse_lookup = setting
7   end
9   def self.do_not_reverse_lookup
10     @no_reverse_lookup ? true : false
11   end
13   def getsockopt(level, optname)
14     Socket::Foreign.getsockopt descriptor, level, optname
15   end
17   def setsockopt(level, optname, optval)
18     optval = 1 if optval == true
19     optval = 0 if optval == false
21     error = 0
23     case optval
24     when Fixnum then
25       MemoryPointer.new :socklen_t do |val|
26         val.write_int optval
27         error = Socket::Foreign.setsockopt(descriptor, level, optname, val,
28                                            val.size)
29       end
30     when String then
31       MemoryPointer.new optval.size do |val|
32         val.write_string optval
33         error = Socket::Foreign.setsockopt(descriptor, level, optname, val,
34                                            optval.size)
35       end
36     else
37       raise "socket option should be a String, a Fixnum, true, or false"
38     end
40     Errno.handle "Unable to set socket option" unless error == 0
42     return 0
43   end
45   # to doesn't do anything yet
46   def send(msg, flags, to = nil)
47     bytes = msg.length
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
52     buffer.free
53     return bytes_sent
54   end
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
62     buffer.free
63     return message
64   end
65 end
67 class Socket < BasicSocket
69   module Constants
70     FFI.config_hash("socket").each do |name, value|
71       const_set name, value
72     end
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]
78   end
80   module Foreign
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)
84     end
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
129       end
130     end
132     def self.connect(descriptor, sockaddr)
133       err = 0
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
138       end
140       getsockopt(descriptor, Socket::SOL_SOCKET, Socket::SO_ERROR) if err < 0
141       err
142     end
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)
154         end
155       end
156     end
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?
166       if host.empty?
167         if (flags & Socket::AI_PASSIVE == 1) # Passive socket
168           family == Socket::AF_INET6 ? (host = "::") : (host = "0.0.0.0") # IPv6 or IPv4
169         else
170           family == Socket::AF_INET6 ? (host = "::1") : (host = "127.0.0.1")
171         end
172       end
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
182       return [] unless ptr
184       res = Socket::Foreign::AddrInfo.new ptr
186       addrinfos = []
188       loop do
189         addrinfo = []
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]
202       end
204       return addrinfos
205     ensure
206       hints.free if hints
208       if res_p then
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
214         end
215         res_p.free
216       end
217     end
219     def self.getaddress(host)
220       addrinfos = Socket::Foreign.getaddrinfo(host)
221       Socket::Foreign.unpack_sockaddr_in(addrinfos.first[4], false).first
222     end
224     def self.getnameinfo(sockaddr,
225                          reverse_lookup = !BasicSocket.do_not_reverse_lookup)
226       name_info = []
227       value = nil
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)
238               unless err == 0 then
239                 raise SocketError, Socket::Foreign.gai_strerror(err)
240               end
242               name_info[2] = node.read_string
243             end
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)
251             unless err == 0 then
252               raise SocketError, Socket::Foreign.gai_strerror(err)
253             end
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
260           end
261         end
262       end
264       name_info[2] = name_info[3] if name_info[2].nil?
265       name_info
266     end
268     def self.getpeername(descriptor)
269       MemoryPointer.new :char, 128 do |sockaddr_storage_p|
270         MemoryPointer.new :socklen_t do |len_p|
271           len_p.write_int 128
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
278         end
279       end
280     end
282     def self.getsockname(descriptor)
283       MemoryPointer.new :char, 128 do |sockaddr_storage_p|
284         MemoryPointer.new :socklen_t do |len_p|
285           len_p.write_int 128
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
292         end
293       end
294     end
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])
314     ensure
315       hints.free if hints
317       if res_p then
318         ptr = res_p.read_pointer
320         freeaddrinfo ptr if ptr and not ptr.null?
322         res_p.free
323       end
324     end
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
332     end
333   end
335   module ListenAndAccept
336     def listen(backlog)
337       backlog = Type.coerce_to backlog, Fixnum, :to_int
339       err = Socket::Foreign.listen descriptor, backlog
341       Errno.handle 'listen(2)' unless err == 0
343       err
344     end
346     def accept
347       return if closed?
348       wait_til_readable
350       fd = nil
351       sockaddr = nil
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
356         end
357       end
359       Errno.handle 'accept(2)' if fd < 0
361       socket = self.class.superclass.allocate
362       socket.__send__ :from_descriptor, fd
363     end
364   end
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)
375       super(@p)
376     end
378     def to_s
379       @p.read_string(@p.size)
380     end
382   end
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)"
392       end
393       @p = MemoryPointer.new self.size
394       if filename
395         @p.write_string( [Socket::AF_UNIX].pack("s") + filename )
396       end
397       super(@p)
398     end
400     def to_s
401       @p.read_string(self.size)
402     end
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
410     family ||= 0
411     socktype ||= 0
412     protocol ||= 0
413     flags ||= 0
415     addrinfos = Socket::Foreign.getaddrinfo(host, service, family, socktype,
416                                             protocol, flags)
418     addrinfos.map do |ai|
419       addrinfo = []
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
426       addrinfo << ai[1]
427       addrinfo << ai[2]
428       addrinfo << ai[3]
429       addrinfo
430     end
431   end
433   def self.gethostname
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
437     end
438   end
440   class Servent < FFI::Struct
441     config("rbx.platform.servent", :s_name, :s_aliases, :s_port, :s_proto)
443     def initialize(data)
444       @p = MemoryPointer.new data.size
445       @p.write_string(data)
446       super(@p)
447     end
449     def to_s
450       @p.read_string(size)
451     end
453   end
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])
466       end
467     end
468   end
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
473   end
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'
482     else
483       raise e
484     end
485   end
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) ]
493     end
494   end
496   class << self
497     alias_method :sockaddr_in, :pack_sockaddr_in
498     alias_method :pair, :socketpair
499   end
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
505     end
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")}"
511       end
513       struct = SockAddr_Un.new
514       struct.pointer.write_string(addr)
516       struct[:sun_path]
517     end
519     class << self
520       alias_method :sockaddr_un, :pack_sockaddr_un
521     end
522   end
524   def initialize(family, socket_type, protocol)
525     descriptor = Socket::Foreign.socket family, socket_type, protocol
527     Errno.handle 'socket(2)' if descriptor < 0
529     setup descriptor
530   end
532   def self.from_descriptor(fixnum)
533     sock = allocate()
534     sock.from_descriptor(fixnum)
535     return sock
536   end
538   def from_descriptor(fixnum)
539     setup(fixnum)
540     return self
541   end
543   def bind(server_sockaddr)
544     err = Socket::Foreign.bind(descriptor, server_sockaddr)
545     Errno.handle 'bind(2)' unless err == 0
546   end
549 class UNIXSocket < BasicSocket
550   attr_accessor :path
552   def initialize(path)
553     @path = path
554     unix_setup
555   end
556   private :initialize
558   def unix_setup(server = false)
559     syscall = 'socket(2)'
560     status = nil
561     sock = Socket::Foreign.socket Socket::Constants::AF_UNIX, Socket::Constants::SOCK_STREAM, 0
563     # TODO - Do we need to sync = true here?
564     setup sock, 'rw'
566     Errno.handle syscall if descriptor < 0
568     sockaddr = Socket.pack_sockaddr_un(@path)
570     if server then
571       syscall = 'bind(2)'
572       status = Socket::Foreign.bind descriptor, sockaddr
573     else
574       syscall = 'connect(2)'
575       status = Socket::Foreign.connect descriptor, sockaddr
576     end
578     if status < 0 then
579       Socket::Foreign.close descriptor
580       Errno.handle syscall
581     end
583     if server then
584       syscall = 'listen(2)'
585       status = Socket::Foreign.listen descriptor, 5
586       Errno.handle syscall if status < 0
587     end
589     return sock
590   end
591   private :unix_setup
593   def addr
594     sockaddr = Socket::Foreign.getsockname descriptor
595     _, sock_path = sockaddr.unpack('SZ*')
596     ["AF_UNIX", sock_path]
597   end
599   def peeraddr
600     sockaddr = Socket::Foreign.getpeername descriptor
601     _, sock_path = sockaddr.unpack('SZ*')
602     ["AF_UNIX", sock_path]
603   end
605   def from_descriptor(descriptor)
606     setup descriptor
608     self
609   end
610   private :from_descriptor
614 class UNIXServer < UNIXSocket
616   include Socket::ListenAndAccept
618   def initialize(path)
619     @path = path
620     unix_setup(true)
621   end
622   private :initialize
625 class IPSocket < BasicSocket
627   def self.getaddress(host)
628     Socket::Foreign.getaddress host
629   end
631   def addr
632     sockaddr = Socket::Foreign.getsockname descriptor
634     Socket::Foreign.getnameinfo sockaddr
635   end
637   def peeraddr
638     sockaddr = Socket::Foreign.getpeername descriptor
640     Socket::Foreign.getnameinfo sockaddr
641   end
644 class UDPSocket < IPSocket
645   def initialize
646     status = Socket::Foreign.socket Socket::AF_INET, Socket::SOCK_DGRAM, 0
647     Errno.handle 'socket(2)' if status < 0
648     setup status
649   end
651   def bind(host, port)
652     @host = host.to_s if host
653     @port = port.to_s if port
655     addrinfos = Socket::Foreign.getaddrinfo(@host,
656                                            @port,
657                                            Socket::AF_UNSPEC,
658                                            Socket::SOCK_DGRAM, 0, 0)
660     status = -1
662     addrinfos.each do |addrinfo|
663       flags, family, socket_type, protocol, sockaddr, canonname = addrinfo
665       status = Socket::Foreign.bind descriptor, sockaddr
666       syscall = 'bind(2)'
668       break if status >= 0
669     end
670     if status < 0
671       Errno.handle syscall
672       Socket::Foreign.close descriptor
673     end
674     status
675   end
677   def inspect
678     "#<#{self.class}:0x#{object_id.to_s(16)} #{@host}:#{@port}>"
679   end
683 class TCPSocket < IPSocket
685   def self.gethostbyname(hostname)
686     addrinfos = Socket.getaddrinfo(hostname)
688     hostname     = addrinfos.first[2]
689     family       = addrinfos.first[4]
690     addresses    = []
691     alternatives = []
692     addrinfos.each do |a|
693       alternatives << a[2] unless a[2] == hostname
694       addresses    << a[3] if a[4] == family
695     end
697     [hostname, alternatives.uniq, family] + addresses.uniq
698   end
700   def initialize(host, port)
701     @host = host
702     @port = port
704     tcp_setup @host, @port
705   end
706   private :initialize
708   def tcp_setup(remote_host, remote_service, local_host = nil,
709                 local_service = nil, server = false)
710     status = nil
711     syscall = nil
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,
717                                                    remote_service,
718                                                    Socket::AF_UNSPEC,
719                                                    Socket::SOCK_STREAM, 0,
720                                                    flags)
722     if server == false and (local_host or local_service) then
723       @local_addrinfo = Socket::Foreign.getaddrinfo(local_host,
724                                                     local_service,
725                                                     Socket::AF_UNSPEC,
726                                                     Socket::SOCK_STREAM, 0, 0)
727     end
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)'
734       setup status
736       next if descriptor < 0
738       if server then
739         status = 1
741         begin
742           setsockopt(Socket::Constants::SOL_SOCKET,
743                      Socket::Constants::SO_REUSEADDR, true)
744         rescue SystemCallError
745         end
747         status = Socket::Foreign.bind descriptor, sockaddr
748         syscall = 'bind(2)'
749       else
750         if @local_addrinfo then
751           status = bind descriptor, @local_addrinfo.first[4]
752           syscall = 'bind(2)'
753         end
755         if status >= 0 then
756           status = Socket::Foreign.connect descriptor, sockaddr
757           syscall = 'connect(2)'
758         end
759       end
761       break if status >= 0
762     end
764     if status < 0
765       Errno.handle syscall
766       Socket::Foreign.close descriptor
767     end
769     if server then
770       err = Socket::Foreign.listen descriptor, 5
771       Errno.handle syscall unless err == 0
772     end
774     setup descriptor
775   end
776   private :tcp_setup
778   def from_descriptor(descriptor)
779     setup descriptor
781     self
782   end
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
793       port = host
794       host = nil
795     end
797     port = StringValue port unless port.__kind_of__ Fixnum
799     @host = host
800     @port = port
802     tcp_setup @host, @port, nil, nil, true
803   end