lib/rubysl/socket.rb in rubysl-socket-2.0.1 vs lib/rubysl/socket.rb in rubysl-socket-2.1.0
- old
+ new
@@ -1,1520 +1,325 @@
-require "rubysl/socket/version"
-require "fcntl"
-
-class SocketError < StandardError
-end
-
-# @todo Socket#accept[_nonblock]
-# @todo UNIXServer#accept[_nonblock]
-# @todo UDPSocket#recvfrom
-
-class BasicSocket < IO
- FFI = Rubinius::FFI
-
- class << self
- def from_descriptor(fixnum)
- sock = allocate()
- sock.from_descriptor(fixnum)
- return sock
+module RubySL
+ module Socket
+ def self.bsd_support?
+ Rubinius.bsd? || Rubinius.darwin?
end
- alias :for_fd :from_descriptor
- end
-
- def from_descriptor(fixnum)
- IO.setup self, fixnum, nil, true
- return self
- end
-
- def self.do_not_reverse_lookup=(setting)
- @no_reverse_lookup = setting
- end
-
- def self.do_not_reverse_lookup
- @no_reverse_lookup = true unless defined? @no_reverse_lookup
- @no_reverse_lookup
- end
-
- def do_not_reverse_lookup=(setting)
- @no_reverse_lookup = setting
- end
-
- def do_not_reverse_lookup
- @no_reverse_lookup
- end
-
- def getsockopt(level, optname)
- data = Socket::Foreign.getsockopt(descriptor, level, optname)
-
- sockaddr = Socket::Foreign.getsockname(descriptor)
- family, = Socket::Foreign.getnameinfo sockaddr, Socket::Constants::NI_NUMERICHOST | Socket::Constants::NI_NUMERICSERV
- Socket::Option.new(family, level, optname, data)
- end
-
- def setsockopt(level_or_option, optname=nil, optval=nil)
- level = nil
-
- case level_or_option
- when Socket::Option
- if !optname.nil?
- raise ArgumentError, "given 2, expected 3"
- end
- level = level_or_option.level
- optname = level_or_option.optname
- optval = level_or_option.data
- else
- if level_or_option.nil? or optname.nil?
- nb_arg = 3 - [level_or_option, optname, optval].count(nil)
- raise ArgumentError, "given #{nb_arg}, expected 3"
- end
- level = level_or_option
+ def self.linux_support?
+ Rubinius.linux?
end
- optval = 1 if optval == true
- optval = 0 if optval == false
-
- error = 0
-
- sockname = Socket::Foreign.getsockname descriptor
- family = Socket::Foreign.getnameinfo(sockname).first
-
- level = level_arg(family, level)
- optname = optname_arg(level, optname)
-
- case optval
- when Fixnum then
- FFI::MemoryPointer.new :socklen_t do |val|
- val.write_int optval
- error = Socket::Foreign.setsockopt(descriptor, level,
- optname, val,
- val.total)
- end
- when String then
- FFI::MemoryPointer.new optval.bytesize do |val|
- val.write_string optval, optval.bytesize
- error = Socket::Foreign.setsockopt(descriptor, level,
- optname, val,
- optval.size)
- end
- else
- raise TypeError, "socket option should be a String, a Fixnum, true, or false"
+ def self.unix_socket_support?
+ ::Socket::Constants.const_defined?(:AF_UNIX)
end
- Errno.handle "Unable to set socket option" unless error == 0
+ def self.aliases_for_hostname(hostname)
+ pointer = Foreign.gethostbyname(hostname)
- return 0
- end
-
- def getsockname()
- return Socket::Foreign.getsockname(descriptor)
- end
-
- #
- # Obtain peername information for this socket.
- #
- # @see Socket.getpeername
- #
- def getpeername()
- Socket::Foreign.getpeername @descriptor
- end
-
- #
- #
- #
- def send(message, flags, to = nil)
- connect to if to
-
- bytes = message.bytesize
- bytes_sent = 0
-
- FFI::MemoryPointer.new :char, bytes + 1 do |buffer|
- buffer.write_string message, bytes
- bytes_sent = Socket::Foreign.send(descriptor, buffer, bytes, flags)
- Errno.handle 'send(2)' if bytes_sent < 0
+ Foreign::Hostent.new(pointer).aliases
end
- bytes_sent
- end
+ def self.sockaddr_class_for_socket(socket)
+ if socket.is_a?(::UNIXSocket)
+ return Foreign::SockaddrUn
+ end
- def recvfrom(bytes_to_read, flags = 0)
- # FIXME 0 is knowledge from io.cpp
- return socket_recv(bytes_to_read, flags, 0)
- end
-
- def recv(bytes_to_read, flags = 0)
- # FIXME 0 is knowledge from io.cpp
- return socket_recv(bytes_to_read, flags, 0)
- end
-
- def close_read
- ensure_open
-
- # If we were only in readonly mode, close it all together
- if @mode & ACCMODE == RDONLY
- return close
- end
-
- # MRI doesn't check if shutdown worked, so we don't.
- Socket::Foreign.shutdown @descriptor, 0
-
- @mode = WRONLY
-
- nil
- end
-
- def close_write
- ensure_open
-
- # If we were only in writeonly mode, close it all together
- if @mode & ACCMODE == WRONLY
- return close
- end
-
- Socket::Foreign.shutdown @descriptor, 1
-
- # Mark it as read only
- @mode = RDONLY
-
- nil
- end
-
- #
- # Sets socket nonblocking and reads up to given number of bytes.
- #
- # @todo Should EWOULDBLOCK be passed unchanged? --rue
- #
- def recv_nonblock(bytes_to_read, flags = 0)
- fcntl Fcntl::F_SETFL, Fcntl::O_NONBLOCK
- socket_recv bytes_to_read, flags, 0
- rescue Errno::EWOULDBLOCK
- raise Errno::EAGAIN
- end
-
- def shutdown(how = 2)
- err = Socket::Foreign.shutdown @descriptor, how
- Errno.handle "shutdown" unless err == 0
- end
-
- private
-
- def level_arg(family, level)
- case level
- when Symbol, String
- if Socket::Constants.const_defined?(level)
- Socket::Constants.const_get(level)
- else
- if is_ip_family?(family)
- ip_level_to_int(level)
- else
- unknown_level_to_int(level)
- end
+ # Socket created using for example Socket.unix('foo')
+ if socket.is_a?(::Socket) and
+ socket.instance_variable_get(:@family) == ::Socket::AF_UNIX
+ return Foreign::SockaddrUn
end
- else
- level
- end
- end
- def optname_arg(level, optname)
- case optname
- when Symbol, String
- if Socket::Constants.const_defined?(optname)
- Socket::Constants.const_get(optname)
+ case address_info(:getsockname, socket)[0]
+ when 'AF_INET6'
+ Foreign::SockaddrIn6
+ when 'AF_UNIX'
+ Foreign::SockaddrUn
else
- case(level)
- when Socket::Constants::SOL_SOCKET
- constant("SO", optname)
- when Socket::Constants::IPPROTO_IP
- constant("IP", optname)
- when Socket::Constants::IPPROTO_TCP
- constant("TCP", optname)
- when Socket::Constants::IPPROTO_UDP
- constant("UDP", optname)
- else
- if Socket::Constants.const_defined?(Socket::Constants::IPPROTO_IPV6) &&
- level == Socket::Constants::IPPROTO_IPV6
- constant("IPV6", optname)
- else
- optname
- end
- end
+ Foreign::SockaddrIn
end
- else
- optname
end
- end
- def is_ip_family?(family)
- family == "AF_INET" || family == "AF_INET6"
- end
+ def self.accept(source, new_class)
+ raise IOError, 'socket has been closed' if source.closed?
- def ip_level_to_int(level)
- prefixes = ["IPPROTO", "SOL"]
- prefixes.each do |prefix|
- if Socket::Constants.const_defined?("#{prefix}_#{level}")
- return Socket::Constants.const_get("#{prefix}_#{level}")
- end
- end
- end
+ sockaddr = sockaddr_class_for_socket(source).new
- def unknown_level_to_int(level)
- constant("SOL", level)
- end
+ begin
+ fd = RubySL::Socket::Foreign.memory_pointer(:int) do |size_p|
+ size_p.write_int(sockaddr.size)
- def constant(prefix, suffix)
- if Socket::Constants.const_defined?("#{prefix}_#{suffix}")
- Socket::Constants.const_get("#{prefix}_#{suffix}")
- end
- end
+ RubySL::Socket::Foreign
+ .accept(source.descriptor, sockaddr.pointer, size_p)
+ end
-end
+ Error.read_error('accept(2)', source) if fd < 0
-class Socket < BasicSocket
- FFI = Rubinius::FFI
+ socket = new_class.allocate
- # @todo Is omitting empty-value constants reasonable? --rue
- module Constants
- all_valid = FFI.config_hash("socket").reject {|name, value| value.empty? }
+ IO.setup(socket, fd, nil, true)
+ socket.binmode
- all_valid.each {|name, value| const_set name, Integer(value) }
+ socktype = source.getsockopt(:SOCKET, :TYPE).int
+ addrinfo = Addrinfo.new(sockaddr.to_s, sockaddr.family, socktype)
- # MRI compat. socket is a pretty screwed up API. All the constants in Constants
- # must also be directly accessible on Socket itself. This means it's not enough
- # to include Constants into Socket, because Socket#const_defined? must be able
- # to see constants like AF_INET6 directly on Socket, but #const_defined? doesn't
- # check inherited constants. O_o
- #
- all_valid.each {|name, value| Socket.const_set name, Integer(value) }
-
-
- afamilies = all_valid.to_a.select { |name,| name =~ /^AF_/ }
- afamilies.map! {|name, value| [value.to_i, name] }
-
- pfamilies = all_valid.to_a.select { |name,| name =~ /^PF_/ }
- pfamilies.map! {|name, value| [value.to_i, name] }
-
- AF_TO_FAMILY = Hash[*afamilies.flatten]
- PF_TO_FAMILY = Hash[*pfamilies.flatten]
- end
-
- module Foreign
- extend FFI::Library
-
- class Addrinfo < FFI::Struct
- config("rbx.platform.addrinfo", :ai_flags, :ai_family, :ai_socktype,
- :ai_protocol, :ai_addrlen, :ai_addr, :ai_canonname, :ai_next)
- end
-
- class Linger < FFI::Struct
- config("rbx.platform.linger", :l_onoff, :l_linger)
- end
-
- attach_function :_bind, "bind", [:int, :pointer, :socklen_t], :int
- attach_function :_connect, "connect", [:int, :pointer, :socklen_t], :int
-
- attach_function :accept, [:int, :pointer, :pointer], :int
- attach_function :close, [:int], :int
- attach_function :shutdown, [:int, :int], :int
- attach_function :listen, [:int, :int], :int
- attach_function :socket, [:int, :int, :int], :int
- attach_function :send, [:int, :pointer, :size_t, :int], :ssize_t
- attach_function :recv, [:int, :pointer, :size_t, :int], :ssize_t
- attach_function :recvfrom, [:int, :pointer, :size_t, :int,
- :pointer, :pointer], :int
-
- attach_function :_getsockopt,
- "getsockopt", [:int, :int, :int, :pointer, :pointer], :int
- attach_function :_getaddrinfo,
- "getaddrinfo", [:string, :string, :pointer, :pointer], :int
-
- attach_function :gai_strerror, [:int], :string
- attach_function :setsockopt, [:int, :int, :int, :pointer, :socklen_t], :int
- attach_function :freeaddrinfo, [:pointer], :void
- attach_function :_getpeername, "getpeername", [:int, :pointer, :pointer], :int
- attach_function :_getsockname, "getsockname", [:int, :pointer, :pointer], :int
-
- attach_function :socketpair, [:int, :int, :int, :pointer], :int
-
- attach_function :gethostname, [:pointer, :size_t], :int
- attach_function :getservbyname, [:pointer, :pointer], :pointer
-
- attach_function :htons, [:uint16_t], :uint16_t
- attach_function :ntohs, [:uint16_t], :uint16_t
-
- attach_function :_getnameinfo,
- "getnameinfo", [:pointer, :socklen_t, :pointer, :socklen_t,
- :pointer, :socklen_t, :int], :int
-
- def self.bind(descriptor, sockaddr)
- FFI::MemoryPointer.new :char, sockaddr.bytesize do |sockaddr_p|
- sockaddr_p.write_string sockaddr, sockaddr.bytesize
-
- _bind descriptor, sockaddr_p, sockaddr.bytesize
+ return socket, addrinfo
+ ensure
+ sockaddr.free
end
end
- def self.connect(descriptor, sockaddr)
- err = 0
- FFI::MemoryPointer.new :char, sockaddr.bytesize do |sockaddr_p|
- sockaddr_p.write_string sockaddr, sockaddr.bytesize
+ def self.accept_nonblock(source, new_class)
+ source.fcntl(::Fcntl::F_SETFL, ::Fcntl::O_NONBLOCK)
- err = _connect descriptor, sockaddr_p, sockaddr.bytesize
- end
-
- err
+ accept(source, new_class)
end
- def self.getsockopt(descriptor, level, optname)
- FFI::MemoryPointer.new 256 do |val| # HACK magic number
- FFI::MemoryPointer.new :socklen_t do |length|
- length.write_int 256 # HACK magic number
+ def self.listen(source, backlog)
+ backlog = Rubinius::Type.coerce_to(backlog, Fixnum, :to_int)
+ err = Foreign.listen(source.descriptor, backlog)
- err = _getsockopt descriptor, level, optname, val, length
+ Error.read_error('listen(2)', source) if err < 0
- Errno.handle "Unable to get socket option" unless err == 0
-
- return val.read_string(length.read_int)
- end
- end
+ 0
end
- def self.getaddrinfo(host, service = nil, family = nil, socktype = nil, protocol = nil, flags = nil)
- hints = Addrinfo.new
- hints[:ai_family] = family || 0
- hints[:ai_socktype] = socktype || 0
- hints[:ai_protocol] = protocol || 0
- hints[:ai_flags] = flags || 0
-
- if host && (host.empty? || host == '<any>')
- host = "0.0.0.0"
- elsif host == '<broadcast>'
- host = '255.255.255.255'
+ def self.family_for_sockaddr_in(sockaddr)
+ case sockaddr.bytesize
+ when 28
+ ::Socket::AF_INET6
+ when 16
+ ::Socket::AF_INET
+ # UNIX socket addresses can have a variable size as sometimes any trailing
+ # null bytes are stripped (e.g. when calling UNIXServer#getsockname).
+ else
+ ::Socket::AF_UNIX
end
-
- res_p = FFI::MemoryPointer.new :pointer
-
- err = _getaddrinfo host, service, hints.pointer, res_p
-
- raise SocketError, gai_strerror(err) unless err == 0
-
- ptr = res_p.read_pointer
-
- return [] unless ptr
-
- res = Addrinfo.new ptr
-
- addrinfos = []
-
- while true
- addrinfo = []
- addrinfo << res[:ai_flags]
- addrinfo << res[:ai_family]
- addrinfo << res[:ai_socktype]
- addrinfo << res[:ai_protocol]
- addrinfo << res[:ai_addr].read_string(res[:ai_addrlen])
- addrinfo << res[:ai_canonname]
-
- addrinfos << addrinfo
-
- break unless res[:ai_next]
-
- res = Addrinfo.new res[:ai_next]
- end
-
- return addrinfos
- ensure
- hints.free if hints
-
- if res_p
- ptr = res_p.read_pointer
-
- # Be sure to feed a legit pointer to freeaddrinfo
- if ptr and !ptr.null?
- freeaddrinfo ptr
- end
- res_p.free
- end
end
- def self.getaddress(host)
- addrinfos = getaddrinfo(host)
- unpack_sockaddr_in(addrinfos.first[4], false).first
+ def self.constant_pairs
+ Rubinius::FFI.config_hash('socket').reject { |name, value| value.empty? }
end
- def self.getnameinfo(sockaddr, flags = Socket::Constants::NI_NUMERICHOST | Socket::Constants::NI_NUMERICSERV,
- reverse_lookup = !BasicSocket.do_not_reverse_lookup)
- name_info = []
- value = nil
-
- FFI::MemoryPointer.new :char, sockaddr.bytesize do |sockaddr_p|
- FFI::MemoryPointer.new :char, Socket::Constants::NI_MAXHOST do |node|
- FFI::MemoryPointer.new :char, Socket::Constants::NI_MAXSERV do |service|
- sockaddr_p.write_string sockaddr, sockaddr.bytesize
-
- if reverse_lookup then
- err = _getnameinfo(sockaddr_p, sockaddr.bytesize,
- node, Socket::Constants::NI_MAXHOST, nil, 0, 0)
-
- name_info[2] = node.read_string if err == 0
- end
-
- err = _getnameinfo(sockaddr_p, sockaddr.bytesize,
- node, Socket::Constants::NI_MAXHOST,
- service, Socket::Constants::NI_MAXSERV,
- flags)
-
- unless err == 0 then
- raise SocketError, gai_strerror(err)
- end
-
- sa_family = SockAddr_In.new(sockaddr)[:sin_family]
-
- name_info[0] = Socket::Constants::AF_TO_FAMILY[sa_family]
- name_info[1] = service.read_string
- name_info[3] = node.read_string
- end
- end
+ def self.coerce_to_string(object)
+ if object.is_a?(String) or object.is_a?(Symbol)
+ object.to_s
+ elsif object.respond_to?(:to_str)
+ Rubinius::Type.coerce_to(object, String, :to_str)
+ else
+ raise TypeError, "no implicit conversion of #{object.inspect} into Integer"
end
-
- name_info[2] = name_info[3] if name_info[2].nil?
- name_info
end
- def self.getpeername(descriptor)
- FFI::MemoryPointer.new :char, 128 do |sockaddr_storage_p|
- FFI::MemoryPointer.new :socklen_t do |len_p|
- len_p.write_int 128
-
- err = _getpeername descriptor, sockaddr_storage_p, len_p
-
- Errno.handle 'getpeername(2)' unless err == 0
-
- sockaddr_storage_p.read_string len_p.read_int
- end
- end
+ def self.family_prefix?(family)
+ family.start_with?('AF_') || family.start_with?('PF_')
end
- def self.getsockname(descriptor)
- FFI::MemoryPointer.new :char, 128 do |sockaddr_storage_p|
- FFI::MemoryPointer.new :socklen_t do |len_p|
- len_p.write_int 128
-
- err = _getsockname descriptor, sockaddr_storage_p, len_p
-
- Errno.handle 'getsockname(2)' unless err == 0
-
- sockaddr_storage_p.read_string len_p.read_int
- end
+ def self.prefix_with(name, prefix)
+ unless name.start_with?(prefix)
+ name = "#{prefix}#{name}"
end
- end
- def self.pack_sockaddr_in(host, port, family, type, flags)
- hints = Addrinfo.new
- hints[:ai_family] = family
- hints[:ai_socktype] = type
- hints[:ai_flags] = flags
-
- if host && host.empty?
- host = "0.0.0.0"
- end
-
- res_p = FFI::MemoryPointer.new :pointer
-
- err = _getaddrinfo host, port.to_s, hints.pointer, res_p
-
- raise SocketError, gai_strerror(err) unless err == 0
-
- return [] if res_p.read_pointer.null?
-
- res = Addrinfo.new res_p.read_pointer
-
- return res[:ai_addr].read_string(res[:ai_addrlen])
-
- ensure
- hints.free if hints
-
- if res_p then
- ptr = res_p.read_pointer
-
- freeaddrinfo ptr if ptr and not ptr.null?
-
- res_p.free
- end
+ name
end
- def self.unpack_sockaddr_in(sockaddr, reverse_lookup)
- family, port, host, ip = getnameinfo sockaddr, Socket::Constants::NI_NUMERICHOST | Socket::Constants::NI_NUMERICSERV, reverse_lookup
- # On some systems this doesn't fail for families other than AF_INET(6)
- # so we raise manually here.
- raise ArgumentError, 'not an AF_INET/AF_INET6 sockaddr' unless family =~ /AF_INET/
- return host, ip, port.to_i
- end
- end
+ def self.prefixed_socket_constant(name, prefix, &block)
+ prefixed = prefix_with(name, prefix)
- module ListenAndAccept
- include IO::Socketable
-
- def listen(backlog)
- backlog = Rubinius::Type.coerce_to backlog, Fixnum, :to_int
-
- err = Socket::Foreign.listen descriptor, backlog
-
- Errno.handle 'listen(2)' unless err == 0
-
- err
+ socket_constant(prefixed, &block)
end
- def accept
- return if closed?
-
- fd = super
-
- socket = self.class.superclass.allocate
- IO.setup socket, fd, nil, true
- socket.binmode
- socket
- end
-
- #
- # Set nonblocking and accept.
- #
- def accept_nonblock
- return if closed?
-
- fcntl(Fcntl::F_SETFL, Fcntl::O_NONBLOCK)
-
- fd = nil
- sockaddr = nil
-
- FFI::MemoryPointer.new 1024 do |sockaddr_p| # HACK from MRI
- FFI::MemoryPointer.new :int do |size_p|
- fd = Socket::Foreign.accept descriptor, sockaddr_p, size_p
- end
- end
-
- Errno.handle 'accept(2)' if fd < 0
-
- # TCPServer -> TCPSocket etc. *sigh*
- socket = self.class.superclass.allocate
- IO.setup socket, fd, nil, true
- socket
- end
-
- end
-
- include Socket::ListenAndAccept
-
- class SockAddr_In < FFI::Struct
- config("rbx.platform.sockaddr_in", :sin_family, :sin_port, :sin_addr, :sin_zero)
-
- def initialize(sockaddrin)
- @p = FFI::MemoryPointer.new sockaddrin.bytesize
- @p.write_string(sockaddrin, sockaddrin.bytesize)
- super(@p)
- end
-
- def to_s
- @p.read_string(@p.total)
- end
-
- end
-
- class Option
- attr_reader :family, :level, :optname, :data
-
- def self.bool(family, level, optname, bool)
- data = [(bool ? 1 : 0)].pack('i')
- new family, level, optname, data
- end
-
- def self.int(family, level, optname, integer)
- new family, level, optname, [integer].pack('i')
- end
-
- def self.linger(onoff, secs)
- linger = Socket::Foreign::Linger.new
-
- case onoff
- when Integer
- linger[:l_onoff] = onoff
+ def self.socket_constant(name)
+ if ::Socket.const_defined?(name)
+ ::Socket.const_get(name)
else
- linger[:l_onoff] = onoff ? 1 : 0
+ raise SocketError, yield
end
- linger[:l_linger] = secs
-
- p = linger.to_ptr
- data = p.read_string(p.total)
-
- new :UNSPEC, :SOCKET, :LINGER, data
end
- def initialize(family, level, optname, data)
- @family = family_arg(family)
- @family_name = family
- @level = level_arg(@family, level)
- @level_name = level
- @optname = optname_arg(@level, optname)
- @opt_name = optname
- @data = data
- end
-
- def unpack(template)
- @data.unpack template
- end
-
- def inspect
- "#<#{self.class}: #@family_name #@level_name #@opt_name #{@data.inspect}>"
- end
-
- def bool
- unless @data.length == Rubinius::FFI.type_size(:int)
- raise TypeError, "size differ. expected as sizeof(int)=" +
- "#{Rubinius::FFI.type_size(:int)} but #{@data.length}"
- end
-
- i = @data.unpack('i').first
- i == 0 ? false : true
- end
-
- def int
- unless @data.length == Rubinius::FFI.type_size(:int)
- raise TypeError, "size differ. expected as sizeof(int)=" +
- "#{Rubinius::FFI.type_size(:int)} but #{@data.length}"
- end
- @data.unpack('i').first
- end
-
- def linger
- if @level != Socket::SOL_SOCKET || @optname != Socket::SO_LINGER
- raise TypeError, "linger socket option expected"
- end
- if @data.bytesize != FFI.config("linger.sizeof")
- raise TypeError, "size differ. expected as sizeof(struct linger)=" +
- "#{FFI.config("linger.sizeof")} but #{@data.length}"
- end
-
- linger = Socket::Foreign::Linger.new
- linger.to_ptr.write_string @data, @data.bytesize
-
- onoff = nil
- case linger[:l_onoff]
- when 0 then onoff = false
- when 1 then onoff = true
- else onoff = linger[:l_onoff].to_i
- end
-
- [onoff, linger[:l_linger].to_i]
- end
-
- alias :to_s :data
-
-
- private
-
- def family_arg(family)
+ def self.address_family(family)
case family
when Symbol, String
f = family.to_s
- if f[0..2] != 'AF_'
+
+ unless family_prefix?(f)
f = 'AF_' + f
end
- Socket.const_get f
- when Integer
- family
- else
- raise SocketError, "unknown socket domain: #{family}"
- end
- rescue NameError
- raise SocketError, "unknown socket domain: #{family}"
- end
- def level_arg(family, level)
- case level
- when Symbol, String
- if Socket::Constants.const_defined?(level)
- Socket::Constants.const_get(level)
+ if ::Socket.const_defined?(f)
+ ::Socket.const_get(f)
else
- if is_ip_family?(family)
- ip_level_to_int(level)
- else
- unknown_level_to_int(level)
- end
+ raise SocketError, "unknown socket domain: #{family}"
end
when Integer
- level
+ family
+ when NilClass
+ ::Socket::AF_UNSPEC
else
- raise SocketError, "unknown protocol level: #{level}"
- end
- rescue NameError
- raise SocketError, "unknown protocol level: #{level}"
- end
-
- def optname_arg(level, optname)
- case optname
- when Symbol, String
- if Socket::Constants.const_defined?(optname)
- Socket::Constants.const_get(optname)
+ if family.respond_to?(:to_str)
+ address_family(Rubinius::Type.coerce_to(family, String, :to_str))
else
- case(level)
- when Socket::Constants::SOL_SOCKET
- constant("SO", optname)
- when Socket::Constants::IPPROTO_IP
- constant("IP", optname)
- when Socket::Constants::IPPROTO_TCP
- constant("TCP", optname)
- when Socket::Constants::IPPROTO_UDP
- constant("UDP", optname)
- else
- if Socket::Constants.const_defined?(Socket::Constants::IPPROTO_IPV6) &&
- level == Socket::Constants::IPPROTO_IPV6
- constant("IPV6", optname)
- else
- optname
- end
- end
+ raise SocketError, "unknown socket domain: #{family}"
end
- else
- optname
end
- rescue NameError
- raise SocketError, "unknown socket level option name: #{optname}"
end
- def is_ip_family?(family)
- [Socket::AF_INET, Socket::AF_INET6].include? family
- end
+ def self.address_family_name(family_int)
+ # Both AF_LOCAL and AF_UNIX use value 1. CRuby seems to prefer AF_UNIX
+ # over AF_LOCAL.
+ if family_int == ::Socket::AF_UNIX && family_int == ::Socket::AF_LOCAL
+ return 'AF_UNIX'
+ end
- def ip_level_to_int(level)
- prefixes = ["IPPROTO", "SOL"]
- prefixes.each do |prefix|
- if Socket::Constants.const_defined?("#{prefix}_#{level}")
- return Socket::Constants.const_get("#{prefix}_#{level}")
- end
+ ::Socket.constants.grep(/^AF_/).each do |name|
+ return name.to_s if ::Socket.const_get(name) == family_int
end
- end
- def unknown_level_to_int(level)
- constant("SOL", level)
+ 'AF_UNSPEC'
end
- def constant(prefix, suffix)
- #if Socket::Constants.const_defined?("#{prefix}_#{suffix}")
- Socket::Constants.const_get("#{prefix}_#{suffix}")
- #end
- end
- end
-
- # If we have the details to support unix sockets, do so.
- if FFI.config("sockaddr_un.sun_family.offset") and Socket::Constants.const_defined?(:AF_UNIX)
- class SockAddr_Un < FFI::Struct
- config("rbx.platform.sockaddr_un", :sun_family, :sun_path)
-
- def initialize(filename = nil)
- maxfnsize = self.size - (FFI.config("sockaddr_un.sun_family.size") + 1)
-
- if filename and filename.length > maxfnsize
- raise ArgumentError, "too long unix socket path (max: #{maxfnsize}bytes)"
- end
- @p = FFI::MemoryPointer.new self.size
- if filename
- @p.write_string( [Socket::AF_UNIX].pack("s") + filename )
- end
- super @p
+ def self.protocol_family_name(family_int)
+ # Both PF_LOCAL and PF_UNIX use value 1. CRuby seems to prefer PF_UNIX
+ # over PF_LOCAL.
+ if family_int == ::Socket::PF_UNIX && family_int == ::Socket::PF_LOCAL
+ return 'PF_UNIX'
end
- def to_s
- @p.read_string self.size
+ ::Socket.constants.grep(/^PF_/).each do |name|
+ return name.to_s if ::Socket.const_get(name) == family_int
end
+
+ 'PF_UNSPEC'
end
- end
- def self.getaddrinfo(host, service, family = 0, socktype = 0,
- protocol = 0, flags = 0)
- if service
- if service.kind_of? Fixnum
- service = service.to_s
- else
- service = StringValue(service)
+ def self.protocol_name(family_int)
+ ::Socket.constants.grep(/^IPPROTO_/).each do |name|
+ return name.to_s if ::Socket.const_get(name) == family_int
end
- end
- addrinfos = Socket::Foreign.getaddrinfo(host, service, family, socktype,
- protocol, flags)
-
- addrinfos.map do |ai|
- addrinfo = []
- addrinfo << Socket::Constants::AF_TO_FAMILY[ai[1]]
-
- sockaddr = Foreign.unpack_sockaddr_in ai[4], !BasicSocket.do_not_reverse_lookup
-
- addrinfo << sockaddr.pop # port
- addrinfo.concat sockaddr # hosts
- addrinfo << ai[1]
- addrinfo << ai[2]
- addrinfo << ai[3]
- addrinfo
+ 'IPPROTO_IP'
end
- end
- def self.getnameinfo(sockaddr, flags = 0)
- port = nil
- host = nil
- family = Socket::AF_UNSPEC
- if sockaddr.is_a?(Array)
- if sockaddr.size == 3
- af = sockaddr[0]
- port = sockaddr[1]
- host = sockaddr[2]
- elsif sockaddr.size == 4
- af = sockaddr[0]
- port = sockaddr[1]
- host = sockaddr[3] || sockaddr[2]
- else
- raise ArgumentError, "array size should be 3 or 4, #{sockaddr.size} given"
+ def self.socket_type_name(socktype)
+ ::Socket.constants.grep(/^SOCK_/).each do |name|
+ return name.to_s if ::Socket.const_get(name) == socktype
end
- if family == "AF_INET"
- family = Socket::AF_INET
- elsif family == "AF_INET6"
- family = Socket::AF_INET6
- end
- sockaddr = Socket::Foreign.pack_sockaddr_in(host, port, family, Socket::SOCK_DGRAM, 0)
+ nil
end
- family, port, host, ip = Socket::Foreign.getnameinfo(sockaddr, flags)
- [host, port]
- end
+ def self.protocol_family(family)
+ case family
+ when Symbol, String
+ f = family.to_s
- def self.gethostname
- FFI::MemoryPointer.new :char, 1024 do |mp| #magic number 1024 comes from MRI
- Socket::Foreign.gethostname(mp, 1024) # same here
- return mp.read_string
- end
- end
+ unless family_prefix?(f)
+ f = 'PF_' + f
+ end
- def self.gethostbyname(hostname)
- addrinfos = Socket.getaddrinfo(hostname, nil)
-
- hostname = addrinfos.first[2]
- family = addrinfos.first[4]
- addresses = []
- alternatives = []
- addrinfos.each do |a|
- alternatives << a[2] unless a[2] == hostname
- # transform addresses to packed strings
- if a[4] == family
- sockaddr = Socket.sockaddr_in(1, a[3])
- if family == AF_INET
- # IPv4 address
- offset = FFI.config("sockaddr_in.sin_addr.offset")
- size = FFI.config("sockaddr_in.sin_addr.size")
- addresses << sockaddr.byteslice(offset, size)
- elsif family == AF_INET6
- # Ipv6 address
- offset = FFI.config("sockaddr_in6.sin6_addr.offset")
- size = FFI.config("sockaddr_in6.sin6_addr.size")
- addresses << sockaddr.byteslice(offset, size)
+ if ::Socket.const_defined?(f)
+ ::Socket.const_get(f)
else
- addresses << a[3]
+ raise SocketError, "unknown socket domain: #{family}"
end
+ when Integer
+ family
+ when NilClass
+ ::Socket::PF_UNSPEC
+ else
+ if family.respond_to?(:to_str)
+ protocol_family(Rubinius::Type.coerce_to(family, String, :to_str))
+ else
+ raise SocketError, "unknown socket domain: #{family}"
+ end
end
end
- [hostname, alternatives.uniq, family] + addresses.uniq
- end
+ def self.socket_type(type)
+ case type
+ when Symbol, String
+ t = type.to_s
+ if t[0..4] != 'SOCK_'
+ t = "SOCK_#{t}"
+ end
- class Servent < FFI::Struct
- config("rbx.platform.servent", :s_name, :s_aliases, :s_port, :s_proto)
-
- def initialize(data)
- @p = FFI::MemoryPointer.new data.bytesize
- @p.write_string(data, data.bytesize)
- super(@p)
- end
-
- def to_s
- @p.read_string(size)
- end
-
- end
-
- def self.getservbyname(service, proto='tcp')
- FFI::MemoryPointer.new :char, service.length + 1 do |svc|
- FFI::MemoryPointer.new :char, proto.length + 1 do |prot|
- svc.write_string(service + "\0")
- prot.write_string(proto + "\0")
- fn = Socket::Foreign.getservbyname(svc, prot)
-
- raise SocketError, "no such service #{service}/#{proto}" if fn.nil?
-
- s = Servent.new(fn.read_string(Servent.size))
- return Socket::Foreign.ntohs(s[:s_port])
- end
- end
- end
-
- def self.pack_sockaddr_in(port, host, type = Socket::SOCK_DGRAM, flags = 0)
- Socket::Foreign.pack_sockaddr_in host, port, Socket::AF_UNSPEC, type, flags
- end
-
- def self.unpack_sockaddr_in(sockaddr)
- host, address, port = Socket::Foreign.unpack_sockaddr_in sockaddr, false
-
- return [port, address]
- rescue SocketError => e
- if e.message =~ /ai_family not supported/ then # HACK platform specific?
- raise ArgumentError, 'not an AF_INET/AF_INET6 sockaddr'
- else
- raise e
- end
- end
-
- def self.socketpair(domain, type, protocol, klass=self)
- if domain.kind_of? String
- if domain.prefix? "AF_" or domain.prefix? "PF_"
- begin
- domain = Socket::Constants.const_get(domain)
- rescue NameError
- raise SocketError, "unknown socket domain #{domani}"
+ if ::Socket.const_defined?(t)
+ ::Socket.const_get(t)
+ else
+ raise SocketError, "unknown socket type: #{type}"
end
+ when Integer
+ type
+ when NilClass
+ 0
else
- raise SocketError, "unknown socket domain #{domani}"
+ if type.respond_to?(:to_str)
+ socket_type(Rubinius::Type.coerce_to(type, String, :to_str))
+ else
+ raise SocketError, "unknown socket type: #{type}"
+ end
end
end
- type = get_socket_type(type)
-
- FFI::MemoryPointer.new :int, 2 do |mp|
- Socket::Foreign.socketpair(domain, type, protocol, mp)
- fd0, fd1 = mp.read_array_of_int(2)
-
- [ klass.from_descriptor(fd0), klass.from_descriptor(fd1) ]
- end
- end
-
- class << self
- alias_method :sockaddr_in, :pack_sockaddr_in
- alias_method :pair, :socketpair
- end
-
- # Only define these methods if we support unix sockets
- if self.const_defined?(:SockAddr_Un)
- def self.pack_sockaddr_un(file)
- SockAddr_Un.new(file).to_s
- end
-
- def self.unpack_sockaddr_un(addr)
-
- if addr.bytesize > FFI.config("sockaddr_un.sizeof")
- raise TypeError, "too long sockaddr_un - #{addr.bytesize} longer than #{FFI.config("sockaddr_un.sizeof")}"
- end
-
- struct = SockAddr_Un.new
- struct.pointer.write_string(addr, addr.bytesize)
-
- struct[:sun_path]
- end
-
- class << self
- alias_method :sockaddr_un, :pack_sockaddr_un
- end
- end
-
- def initialize(family, socket_type, protocol=0)
- @no_reverse_lookup = self.class.do_not_reverse_lookup
- family = self.class.get_protocol_family(family)
- socket_type = self.class.get_socket_type(socket_type)
- descriptor = Socket::Foreign.socket family, socket_type, protocol
-
- Errno.handle 'socket(2)' if descriptor < 0
-
- IO.setup self, descriptor, nil, true
- end
-
- def bind(server_sockaddr)
- err = Socket::Foreign.bind(descriptor, server_sockaddr)
- Errno.handle 'bind(2)' unless err == 0
- err
- end
-
- # @todo Should this be closing the descriptor? --rue
- def connect(sockaddr, extra=nil)
- if extra
- sockaddr = Socket.pack_sockaddr_in sockaddr, extra
- else
- sockaddr = StringValue(sockaddr)
- end
-
- status = Socket::Foreign.connect descriptor, sockaddr
-
- if status < 0
- begin
- Errno.handle "connect(2)"
- rescue Errno::EISCONN
- return 0
- end
- end
-
- return 0
- end
-
- def connect_nonblock(sockaddr)
- fcntl(Fcntl::F_SETFL, Fcntl::O_NONBLOCK)
-
- status = Socket::Foreign.connect descriptor, StringValue(sockaddr)
- if status < 0
- Errno.handle "connect(2)"
- end
-
- return status
- end
-
- def self.get_protocol_family(family)
- case family
- when Fixnum
- return family
- when String
- # do nothing
- when Symbol
- family = family.to_s
- else
- family = StringValue(family)
- end
-
- family = "PF_#{family}" unless family[0, 3] == "PF_"
- Socket::Constants.const_get family
- end
-
- def self.get_socket_type(type)
- if type.kind_of? String
- if type.prefix? "SOCK_"
- begin
- type = Socket::Constants.const_get(type)
- rescue NameError
- raise SocketError, "unknown socket type #{type}"
+ def self.convert_reverse_lookup(socket = nil, reverse_lookup = nil)
+ if reverse_lookup.nil?
+ if socket
+ reverse_lookup = !socket.do_not_reverse_lookup
+ else
+ reverse_lookup = !BasicSocket.do_not_reverse_lookup
end
- else
- raise SocketError, "unknown socket type #{type}"
- end
- end
- if type.kind_of? Symbol
- begin
- type = Socket::Constants.const_get("SOCK_#{type}")
- rescue NameError
- raise SocketError, "unknown socket type #{type}"
- end
- end
+ elsif reverse_lookup == :hostname
+ reverse_lookup = true
- type
- end
-end
+ elsif reverse_lookup == :numeric
+ reverse_lookup = false
-class UNIXSocket < BasicSocket
- include IO::TransferIO
-
- # Coding to the lowest standard here.
- def recvfrom(bytes_read, flags = 0)
- # FIXME 2 is hardcoded knowledge from io.cpp
- socket_recv(bytes_read, flags, 2)
- end
-
- def initialize(path)
- @no_reverse_lookup = self.class.do_not_reverse_lookup
- @path = path
- unix_setup
- @path = "" # Client
- end
-
- def path
- unless @path
- sockaddr = Socket::Foreign.getsockname descriptor
- _, @path = sockaddr.unpack('SZ*')
- end
-
- return @path
- end
-
- def from_descriptor(fixnum)
- super
- @path = nil
- end
-
- def unix_setup(server = false)
- status = nil
- phase = 'socket(2)'
- sock = Socket::Foreign.socket Socket::Constants::AF_UNIX, Socket::Constants::SOCK_STREAM, 0
-
- Errno.handle phase if sock < 0
-
- IO.setup self, sock, 'r+', true
-
- sockaddr = Socket.pack_sockaddr_un(@path)
-
- if server then
- phase = 'bind(2)'
- status = Socket::Foreign.bind descriptor, sockaddr
- else
- phase = 'connect(2)'
- status = Socket::Foreign.connect descriptor, sockaddr
- end
-
- if status < 0 then
- close
- Errno.handle phase
- end
-
- if server then
- phase = 'listen(2)'
- status = Socket::Foreign.listen descriptor, 5
- if status < 0
- close
- Errno.handle phase
+ elsif reverse_lookup != true and reverse_lookup != false
+ raise ArgumentError,
+ "invalid reverse_lookup flag: #{reverse_lookup.inspect}"
end
- end
- return sock
- end
- private :unix_setup
-
- def addr
- sockaddr = Socket::Foreign.getsockname descriptor
- _, sock_path = sockaddr.unpack('SZ*')
- ["AF_UNIX", sock_path]
- end
-
- def peeraddr
- sockaddr = Socket::Foreign.getpeername descriptor
- _, sock_path = sockaddr.unpack('SZ*')
- ["AF_UNIX", sock_path]
- end
-
- def recv_io(klass=IO, mode=nil)
- begin
- fd = recv_fd
- rescue PrimitiveFailure
- raise SocketError, "file descriptor was not passed"
+ reverse_lookup
end
- return fd unless klass
+ def self.address_info(method, socket, reverse_lookup = nil)
+ sockaddr = Foreign.__send__(method, socket.descriptor)
- if klass < BasicSocket
- klass.for_fd(fd)
- else
- klass.for_fd(fd, mode)
- end
- end
+ reverse_lookup = convert_reverse_lookup(socket, reverse_lookup)
- class << self
- def socketpair(type=Socket::SOCK_STREAM, protocol=0)
- Socket.socketpair(Socket::PF_UNIX, type, protocol, self)
- end
+ options = ::Socket::Constants::NI_NUMERICHOST |
+ ::Socket::Constants::NI_NUMERICSERV
- alias_method :pair, :socketpair
- end
+ family, port, host, ip = Foreign
+ .getnameinfo(sockaddr, options, reverse_lookup)
-end
-
-class UNIXServer < UNIXSocket
-
- include Socket::ListenAndAccept
-
- def initialize(path)
- @no_reverse_lookup = self.class.do_not_reverse_lookup
- @path = path
- unix_setup(true)
- end
-end
-
-class IPSocket < BasicSocket
-
- def self.getaddress(host)
- Socket::Foreign.getaddress host
- end
-
- def addr(reverse_lookup=nil)
- sockaddr = Socket::Foreign.getsockname descriptor
-
- reverse_lookup = !do_not_reverse_lookup if reverse_lookup.nil?
-
- family, port, host, ip = Socket::Foreign.getnameinfo sockaddr, Socket::Constants::NI_NUMERICHOST | Socket::Constants::NI_NUMERICSERV, reverse_lookup
- [family, port.to_i, host, ip]
- end
-
- def peeraddr(reverse_lookup=nil)
- sockaddr = Socket::Foreign.getpeername descriptor
-
- reverse_lookup = !do_not_reverse_lookup if reverse_lookup.nil?
-
- family, port, host, ip = Socket::Foreign.getnameinfo sockaddr, Socket::Constants::NI_NUMERICHOST | Socket::Constants::NI_NUMERICSERV, reverse_lookup
- [family, port.to_i, host, ip]
- end
-
- def recvfrom(maxlen, flags = 0)
- # FIXME 1 is hardcoded knowledge from io.cpp
- flags = 0 if flags.nil?
- socket_recv maxlen, flags, 1
- end
-
- def recvfrom_nonblock(maxlen, flags = 0)
- # Set socket to non-blocking, if we can
- # Todo: Ensure this works in Windows! If not, I claim that's Fcntl's fault.
- fcntl(Fcntl::F_SETFL, Fcntl::O_NONBLOCK)
- flags = 0 if flags.nil?
- flags |= Socket::MSG_DONTWAIT
-
- # Wait until we have something to read
- # @todo Why? ^^ --rue
- IO.select([self])
- return recvfrom(maxlen, flags)
- end
-end
-
-class UDPSocket < IPSocket
- FFI = Rubinius::FFI
-
- def initialize(socktype = Socket::AF_INET)
- @no_reverse_lookup = self.class.do_not_reverse_lookup
- @socktype = socktype
- status = Socket::Foreign.socket @socktype,
- Socket::SOCK_DGRAM,
- Socket::IPPROTO_UDP
- Errno.handle 'socket(2)' if status < 0
-
- IO.setup self, status, nil, true
- end
-
- def bind(host, port)
- @host = host.to_s if host
- @port = port.to_s if port
-
- addrinfos = Socket::Foreign.getaddrinfo(@host,
- @port,
- @socktype,
- Socket::SOCK_DGRAM, 0,
- Socket::AI_PASSIVE)
-
- status = -1
-
- addrinfos.each do |addrinfo|
- flags, family, socket_type, protocol, sockaddr, canonname = addrinfo
-
- status = Socket::Foreign.bind descriptor, sockaddr
-
- break if status >= 0
+ [family, port.to_i, host, ip]
end
- if status < 0
- Errno.handle 'bind(2)'
- end
-
- status
- end
-
- def connect(host, port)
- sockaddr = Socket::Foreign.pack_sockaddr_in host, port, @socktype, Socket::SOCK_DGRAM, 0
-
- syscall = 'connect(2)'
- status = Socket::Foreign.connect descriptor, sockaddr
-
- if status < 0
- Errno.handle syscall
- end
-
- 0
- end
-
- def send(message, flags, *to)
- connect *to unless to.empty?
-
- bytes = message.bytesize
- bytes_sent = 0
-
- FFI::MemoryPointer.new :char, bytes + 1 do |buffer|
- buffer.write_string message, bytes
- bytes_sent = Socket::Foreign.send(descriptor, buffer, bytes, flags)
- Errno.handle 'send(2)' if bytes_sent < 0
- end
-
- bytes_sent
- end
-
- def inspect
- "#<#{self.class}:0x#{object_id.to_s(16)} #{@host}:#{@port}>"
- end
-
-end
-
-class TCPSocket < IPSocket
- FFI = Rubinius::FFI
-
- def self.gethostbyname(hostname)
- addrinfos = Socket.getaddrinfo(hostname, nil)
-
- hostname = addrinfos.first[2]
- family = addrinfos.first[4]
- addresses = []
- alternatives = []
- addrinfos.each do |a|
- alternatives << a[2] unless a[2] == hostname
- addresses << a[3] if a[4] == family
- end
-
- [hostname, alternatives.uniq, family] + addresses.uniq
- end
-
- #
- # @todo Is it correct to ignore the to? If not, does
- # the socket need to be reconnected? --rue
- #
- def send(bytes_to_read, flags, to = nil)
- super(bytes_to_read, flags)
- end
-
-
- def initialize(host, port, local_host=nil, local_service=nil)
- @no_reverse_lookup = self.class.do_not_reverse_lookup
- @host = host
- @port = port
-
- tcp_setup @host, @port, local_host, local_service
- end
-
- def tcp_setup(remote_host, remote_service, local_host = nil,
- local_service = nil, server = false)
- status = nil
- syscall = nil
- remote_host = StringValue(remote_host) if remote_host
- if remote_service
- if remote_service.kind_of? Fixnum
- remote_service = remote_service.to_s
- else
- remote_service = StringValue(remote_service)
- end
- end
-
- flags = server ? Socket::AI_PASSIVE : 0
- @remote_addrinfo = Socket::Foreign.getaddrinfo(remote_host,
- remote_service,
- Socket::AF_UNSPEC,
- Socket::SOCK_STREAM, 0,
- flags)
-
- if server == false and (local_host or local_service)
- local_host = local_host.to_s if local_host
- local_service = local_service.to_s if local_service
- @local_addrinfo = Socket::Foreign.getaddrinfo(local_host,
- local_service,
- Socket::AF_UNSPEC,
- Socket::SOCK_STREAM, 0, 0)
- end
-
- sock = nil
-
- @remote_addrinfo.each do |addrinfo|
- flags, family, socket_type, protocol, sockaddr, canonname = addrinfo
-
- sock = Socket::Foreign.socket family, socket_type, protocol
- syscall = 'socket(2)'
-
- next if sock < 0
-
- if server
- FFI::MemoryPointer.new :socklen_t do |val|
- val.write_int 1
- level = Socket::Constants::SOL_SOCKET
- optname = Socket::Constants::SO_REUSEADDR
- error = Socket::Foreign.setsockopt(sock, level,
- optname, val,
- val.total)
- # Don't check error because if this fails, we just continue
- # anyway.
+ def self.shutdown_option(how)
+ case how
+ when String, Symbol
+ prefixed_socket_constant(how.to_s, 'SHUT_') do
+ "unknown shutdown argument: #{how}"
end
-
- status = Socket::Foreign.bind sock, sockaddr
- syscall = 'bind(2)'
+ when Fixnum
+ if how == ::Socket::SHUT_RD or
+ how == ::Socket::SHUT_WR or
+ how == ::Socket::SHUT_RDWR
+ how
+ else
+ raise ArgumentError,
+ 'argument should be :SHUT_RD, :SHUT_WR, or :SHUT_RDWR'
+ end
else
- if @local_addrinfo
- # Pick a local_addrinfo for the family and type of
- # the remote side
- li = @local_addrinfo.find do |i|
- i[1] == family && i[2] == socket_type
- end
-
- if li
- status = Socket::Foreign.bind sock, li[4]
- syscall = 'bind(2)'
- else
- status = 1
- end
+ if how.respond_to?(:to_str)
+ shutdown_option(coerce_to_string(how))
else
- status = 1
+ raise TypeError,
+ "no implicit conversion of #{how.class} into Integer"
end
-
- if status >= 0
- status = Socket::Foreign.connect sock, sockaddr
- syscall = 'connect(2)'
- end
end
-
- if status < 0
- Socket::Foreign.close sock
- else
- break
- end
end
-
- if status < 0
- Errno.handle syscall
- end
-
- if server
- err = Socket::Foreign.listen sock, 5
- unless err == 0
- Socket::Foreign.close sock
- Errno.handle syscall
- end
- end
-
- # Only setup once we have found a socket we can use. Otherwise
- # because we manually close a socket fd, we can create an IO fd
- # alias condition which causes EBADF because when an IO is finalized
- # and it's fd has been closed underneith it, we close someone elses
- # fd!
- IO.setup self, sock, nil, true
end
- private :tcp_setup
-
- def from_descriptor(descriptor)
- IO.setup self, descriptor, nil, true
-
- self
- end
-end
-
-class TCPServer < TCPSocket
-
- include Socket::ListenAndAccept
-
- def initialize(host, port = nil)
- @no_reverse_lookup = self.class.do_not_reverse_lookup
-
- if Fixnum === host and port.nil? then
- port = host
- host = nil
- end
-
- if String === host and port.nil? then
- port = Integer(host)
- host = nil
- end
-
- port = StringValue port unless port.kind_of? Fixnum
-
- @host = host
- @port = port
-
- tcp_setup @host, @port, nil, nil, true
- end
-
end