diff options
Diffstat (limited to 'mrbgems/mruby-socket/mrblib/socket.rb')
| -rw-r--r-- | mrbgems/mruby-socket/mrblib/socket.rb | 620 |
1 files changed, 620 insertions, 0 deletions
diff --git a/mrbgems/mruby-socket/mrblib/socket.rb b/mrbgems/mruby-socket/mrblib/socket.rb new file mode 100644 index 000000000..53c20e3cc --- /dev/null +++ b/mrbgems/mruby-socket/mrblib/socket.rb @@ -0,0 +1,620 @@ +class Addrinfo + def initialize(sockaddr, family=Socket::PF_UNSPEC, socktype=0, protocol=0) + @hostname = nil + if sockaddr.is_a? Array + sary = sockaddr + if sary[0] == 'AF_INET' || sary[0] == 'AF_INET6' + @sockaddr = Socket.sockaddr_in(sary[1], sary[3]) + @hostname = sary[2] + elsif sary[0] == 'AF_UNIX' + @sockaddr = Socket.sockaddr_un(sary[1]) + end + else + @sockaddr = sockaddr.dup + end + if family == Socket::PF_UNSPEC or family == nil + @family = Socket._sockaddr_family(@sockaddr) + else + @family = family + end + @socktype = socktype + @protocol = protocol + @canonname = nil + end + + def self.foreach(nodename, service, family=nil, socktype=nil, protocol=nil, flags=0, &block) + a = self.getaddrinfo(nodename, service, family, socktype, protocol, flags) + a.each { |ai| block.call(ai) } + a + end + + def self.ip(host) + Addrinfo.new(Socket.sockaddr_in(0, host)) + end + + def self.tcp(host, port) + Addrinfo.getaddrinfo(host, port, nil, Socket::SOCK_STREAM, Socket::IPPROTO_TCP)[0] + end + + def self.udp(host, port) + Addrinfo.getaddrinfo(host, port, nil, Socket::SOCK_DGRAM, Socket::IPPROTO_UDP)[0] + end + + def self.unix(path, socktype=Socket::SOCK_STREAM) + Addrinfo.new(Socket.sockaddr_un(path), Socket::AF_UNIX, socktype) + end + + def afamily + @family + end + + #def bind + + attr_reader :canonname + + #def connect + #def connect_from + #def connect_to + + #def family_addrinfo(host, port=nil) + #def getnameinfo(flags=0) + # Socket.getnameinfo + #end + + def inspect + if ipv4? or ipv6? + if @protocol == Socket::IPPROTO_TCP or (@socktype == Socket::SOCK_STREAM and @protocol == 0) + proto = 'TCP' + elsif @protocol == Socket::IPPROTO_UDP or (@socktype == Socket::SOCK_DGRAM and @protocol == 0) + proto = 'UDP' + else + proto = '???' + end + "#<Addrinfo: #{inspect_sockaddr} #{proto}>" + else + "#<Addrinfo: #{self.unix_path} SOCK_STREAM>" + end + end + + def inspect_sockaddr + if ipv4? + a, p = ip_unpack + "#{a}:#{p}" + elsif ipv6? + a, p = ip_unpack + "[#{a}]:#{p}" + elsif unix? + unix_path + else + '???' + end + end + + def ip? + ipv4? or ipv6? + end + + def ip_address + ip_unpack[0] + end + + def ip_port + ip_unpack[1] + end + + def ip_unpack + h, p = getnameinfo(Socket::NI_NUMERICHOST|Socket::NI_NUMERICSERV) + [ h, p.to_i ] + end + + def ipv4? + @family == Socket::AF_INET + end + + #def ipv4_loopback? + #def ipv4_multicast? + #def ipv4_private? + + def ipv6? + @family == Socket::AF_INET6 + end + + #def ipv6_loopback? + #def ipv6_mc_global? + #def ipv6_mc_linklocal? + #def ipv6_mc_nodelocal? + #def ipv6_mc_orilocal? + #def ipv6_mc_sitelocal? + #def ipv6_multicast? + #def ipv6_to_ipv4 + #def ipv6_unspecified + #def ipv6_v4compat? + #def ipv6_v4mapped? + #def listen(backlog=5) + + def pfamily + @family + end + + attr_reader :protocol + attr_reader :socktype + + def _to_array + case @family + when Socket::AF_INET + s = "AF_INET" + when Socket::AF_INET6 + s = "AF_INET6" + when Socket::AF_UNIX + s = "AF_UNIX" + else + s = "(unknown AF)" + end + addr, port = self.getnameinfo(Socket::NI_NUMERICHOST|Socket::NI_NUMERICSERV) + [ s, port.to_i, addr, addr ] + end + + def to_sockaddr + @sockaddr + end + + alias to_s to_sockaddr + + def unix? + @family == Socket::AF_UNIX + end +end + +class BasicSocket + @@do_not_reverse_lookup = true + + def self.do_not_reverse_lookup + @@do_not_reverse_lookup + end + + def self.do_not_reverse_lookup=(val) + @@do_not_reverse_lookup = val ? true : false + end + + def initialize(*args) + super(*args) + @do_not_reverse_lookup = @@do_not_reverse_lookup + end + + def self.for_fd(fd) + super(fd, "r+") + end + + #def connect_address + + def local_address + Addrinfo.new self.getsockname + end + + def recv_nonblock(maxlen, flags=0) + begin + _setnonblock(true) + recv(maxlen, flags) + ensure + _setnonblock(false) + end + end + + def remote_address + Addrinfo.new self.getpeername + end + + attr_accessor :do_not_reverse_lookup +end + +class IPSocket + def self.getaddress(host) + Addrinfo.ip(host).ip_address + end + + def addr + Addrinfo.new(self.getsockname)._to_array + end + + def peeraddr + Addrinfo.new(self.getpeername)._to_array + end + + def recvfrom(maxlen, flags=0) + msg, sa = _recvfrom(maxlen, flags) + [ msg, Addrinfo.new(sa)._to_array ] + end +end + +class TCPSocket + def initialize(host, service, local_host=nil, local_service=nil) + if @init_with_fd + super(host, service) + else + s = nil + e = SocketError + Addrinfo.foreach(host, service) { |ai| + begin + s = Socket._socket(ai.afamily, Socket::SOCK_STREAM, 0) + if local_host or local_service + local_host ||= (ai.afamily == Socket::AF_INET) ? "0.0.0.0" : "::" + local_service ||= "0" + bi = Addrinfo.getaddrinfo(local_host, local_service, ai.afamily, ai.socktype)[0] + Socket._bind(s, bi.to_sockaddr) + end + Socket._connect(s, ai.to_sockaddr) + super(s, "r+") + return + rescue => e0 + e = e0 + end + } + raise e + end + end + + def self.new_with_prelude pre, *args + o = self._allocate + o.instance_eval(&pre) + o.initialize(*args) + o + end + + #def self.gethostbyname(host) +end + +class TCPServer + def initialize(host=nil, service) + ai = Addrinfo.getaddrinfo(host, service, nil, nil, nil, Socket::AI_PASSIVE)[0] + @init_with_fd = true + super(Socket._socket(ai.afamily, Socket::SOCK_STREAM, 0), "r+") + if Socket.const_defined?(:SO_REUSEADDR) + self.setsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEADDR, true) + end + Socket._bind(self.fileno, ai.to_sockaddr) + listen(5) + self + end + + def accept + fd = self.sysaccept + begin + TCPSocket.new_with_prelude(proc { @init_with_fd = true }, fd, "r+") + rescue + IO._sysclose(fd) rescue nil + raise + end + end + + def accept_nonblock + begin + self._setnonblock(true) + self.accept + ensure + self._setnonblock(false) + end + end + + def listen(backlog) + Socket._listen(self.fileno, backlog) + 0 + end + + def sysaccept + Socket._accept(self.fileno)[0] + end +end + +class UDPSocket + def initialize(af=Socket::AF_INET) + super(Socket._socket(af, Socket::SOCK_DGRAM, 0), "r+") + @af = af + self + end + + def bind(host, port) + Socket._bind(self.fileno, _sockaddr_in(port, host)) + 0 + end + + def connect(host, port) + Socket._connect(self.fileno, _sockaddr_in(port, host)) + 0 + end + + def recvfrom_nonblock(*args) + s = self + begin + self._setnonblock(true) + self.recvfrom(*args) + ensure + # XXX: self is a SystemcallException here! (should be bug) + s._setnonblock(false) + end + end + + def send(mesg, flags, host=nil, port=nil) + if port + super(mesg, flags, _sockaddr_in(port, host)) + elsif host + super(mesg, flags, host) + else + super(mesg, flags) + end + end + + def _sockaddr_in(port, host) + ai = Addrinfo.getaddrinfo(host, port, @af, Socket::SOCK_DGRAM)[0] + ai.to_sockaddr + end +end + +class Socket + def initialize(domain, type, protocol=0) + super(Socket._socket(domain, type, protocol), "r+") + end + + #def self.accept_loop + + def self.getaddrinfo(nodename, servname, family=nil, socktype=nil, protocol=nil, flags=0) + Addrinfo.getaddrinfo(nodename, servname, family, socktype, protocol, flags).map { |ai| + ary = ai._to_array + ary[2] = nodename + ary[4] = ai.afamily + ary[5] = ai.socktype + ary[6] = ai.protocol + ary + } + end + + #def self.getnameinfo + #def self.ip_address_list + + def self.open(*args) + new(args) + end + + def self.sockaddr_in(port, host) + ai = Addrinfo.getaddrinfo(host, port, nil, Socket::SOCK_DGRAM)[0] + ai.to_sockaddr + end + + #def self.tcp + #def self.tcp_server_loop + #def self.tcp_server_sockets + #def self.udp_server_loop + #def self.udp_server_loop_on + #def self.udp_server_recv + #def self.udp_server_sockets + #def self.unix(path) + #def self.unix_server_loop + #def self.unix_server_socket + + def self.unpack_sockaddr_in(sa) + Addrinfo.new(sa).ip_unpack.reverse + end + + def self.unpack_sockaddr_un(sa) + Addrinfo.new(sa).unix_path + end + + class << self + alias pack_sockaddr_in sockaddr_in + alias pack_sockaddr_un sockaddr_un + alias pair socketpair + end + + def accept + fd, addr = self.sysaccept + [ Socket.for_fd(fd), addr ] + end + + def accept_nonblock + begin + self._setnonblock(true) + self.accept + ensure + self._setnonblock(false) + end + end + + def bind(sockaddr) + sockaddr = sockaddr.to_sockaddr if sockaddr.is_a? Addrinfo + Socket._bind(self.fileno, sockaddr) + 0 + end + + def connect(sockaddr) + sockaddr = sockaddr.to_sockaddr if sockaddr.is_a? Addrinfo + Socket._connect(self.fileno, sockaddr) + 0 + end + + def connect_nonblock(sockaddr) + begin + self._setnonblock(true) + self.connect(sockaddr) + ensure + self._setnonblock(false) + end + end + + #def ipv6only! + + def listen(backlog) + Socket._listen(self.fileno, backlog) + 0 + end + + def recvfrom(maxlen, flags=0) + msg, sa = _recvfrom(maxlen, flags) + socktype = self.getsockopt(Socket::SOL_SOCKET, Socket::SO_TYPE).int + [ msg, Addrinfo.new(sa, Socket::PF_UNSPEC, socktype) ] + end + + def recvfrom_nonblock(*args) + begin + self._setnonblock(true) + self._recvfrom(*args) + ensure + self._setnonblock(false) + end + end + + def sysaccept + Socket._accept(self.fileno)[0] + end +end + +class UNIXSocket < BasicSocket + def initialize(path, &block) + if self.is_a? UNIXServer + super(path, "r") + else + super(Socket._socket(Socket::AF_UNIX, Socket::SOCK_STREAM, 0), "r+") + Socket._connect(self.fileno, Socket.sockaddr_un(path)) + + if block_given? + begin + yield self + ensure + begin + self.close unless self.closed? + rescue StandardError + end + end + end + end + end + + def self.socketpair(type=Socket::SOCK_STREAM, protocol=0) + a = Socket.socketpair(Socket::AF_UNIX, type, protocol) + [ UNIXSocket.for_fd(a[0]), UNIXSocket.for_fd(a[1]) ] + end + + class << self + alias pair socketpair + end + + def addr + [ "AF_UNIX", path ] + end + + def path + Addrinfo.new(self.getsockname).unix_path + end + + def peeraddr + [ "AF_UNIX", Addrinfo.new(self.getpeername).unix_path ] + end + + #def recv_io + + def recvfrom(maxlen, flags=0) + msg, sa = _recvfrom(maxlen, flags) + path = (sa.size > 0) ? Addrinfo.new(sa).unix_path : "" + [ msg, [ "AF_UNIX", path ] ] + end + + #def send_io +end + +class UNIXServer < UNIXSocket + def initialize(path) + fd = Socket._socket(Socket::AF_UNIX, Socket::SOCK_STREAM, 0) + begin + super(fd) + Socket._bind(fd, Socket.pack_sockaddr_un(path)) + self.listen(5) + rescue => e + IO._sysclose(fd) rescue nil + raise e + end + + if block_given? + begin + yield self + ensure + self.close rescue nil unless self.closed? + end + end + end + + def accept + fd = self.sysaccept + begin + sock = UNIXSocket.for_fd(fd) + rescue + IO._sysclose(fd) rescue nil + end + sock + end + + def accept_nonblock + begin + self._setnonblock(true) + self.accept + ensure + self._setnonblock(false) + end + end + + def listen(backlog) + Socket._listen(self.fileno, backlog) + 0 + end + + def sysaccept + Socket._accept(self.fileno)[0] + end +end + +class Socket + include Constants +end + +class Socket + class Option + def initialize(family, level, optname, data) + @family = family + @level = level + @optname = optname + @data = data + end + + def self.bool(family, level, optname, bool) + self.new(family, level, optname, [(bool ? 1 : 0)].pack('i')) + end + + def self.int(family, level, optname, integer) + self.new(family, level, optname, [integer].pack('i')) + end + + #def self.linger(family, level, optname, integer) + #end + + attr_reader :data, :family, :level, :optname + + def bool + @data.unpack('i')[0] != 0 + end + + def inspect + "#<Socket::Option: family:#{@family} level:#{@level} optname:#{@optname} #{@data.inspect}>" + end + + def int + @data.unpack('i')[0] + end + + def linger + raise NotImplementedError.new + end + + def unpack(template) + raise NotImplementedError.new + end + end +end + +class SocketError < StandardError; end |
