diff --git a/mrbgems/mruby-socket/mrblib/socket.rb b/mrbgems/mruby-socket/mrblib/socket.rb index 90ece42da..7b7d986db 100644 --- a/mrbgems/mruby-socket/mrblib/socket.rb +++ b/mrbgems/mruby-socket/mrblib/socket.rb @@ -1,4 +1,15 @@ class Addrinfo + # + # call-seq: + # Addrinfo.new(sockaddr, family=Socket::PF_UNSPEC, socktype=0, protocol=0) -> addrinfo + # + # Creates a new Addrinfo object from socket address information. + # sockaddr can be a packed sockaddr string or an array representation. + # + # Addrinfo.new(Socket.sockaddr_in(80, "127.0.0.1")) + # Addrinfo.new(["AF_INET", 80, "localhost", "127.0.0.1"]) + # Addrinfo.new(["AF_UNIX", "/tmp/socket"]) + # def initialize(sockaddr, family=Socket::PF_UNSPEC, socktype=0, protocol=0) @hostname = nil if sockaddr.is_a? Array @@ -21,28 +32,93 @@ class Addrinfo @protocol = protocol end + # + # call-seq: + # Addrinfo.foreach(nodename, service, family=nil, socktype=nil, protocol=nil, flags=0) { |addrinfo| block } -> array + # + # Iterates over all address information for the given nodename and service. + # Returns an array of Addrinfo objects. + # + # Addrinfo.foreach("www.example.com", "http") { |ai| puts ai.ip_address } + # Addrinfo.foreach("localhost", 80) { |ai| puts ai.inspect } + # 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 + # + # call-seq: + # Addrinfo.ip(host) -> addrinfo + # + # Creates an Addrinfo object for the given host with port 0. + # Useful for creating address info without specifying a port. + # + # Addrinfo.ip("127.0.0.1") #=> # + # Addrinfo.ip("::1") #=> # + # def self.ip(host) Addrinfo.new(Socket.sockaddr_in(0, host)) end + # + # call-seq: + # Addrinfo.tcp(host, port) -> addrinfo + # + # Creates an Addrinfo object for TCP connection to the given host and port. + # + # Addrinfo.tcp("localhost", 80) #=> # + # Addrinfo.tcp("www.example.com", 443) #=> # + # def self.tcp(host, port) Addrinfo.getaddrinfo(host, port, nil, Socket::SOCK_STREAM, Socket::IPPROTO_TCP)[0] end + # + # call-seq: + # Addrinfo.udp(host, port) -> addrinfo + # + # Creates an Addrinfo object for UDP connection to the given host and port. + # + # Addrinfo.udp("localhost", 53) #=> # + # Addrinfo.udp("8.8.8.8", 53) #=> # + # def self.udp(host, port) Addrinfo.getaddrinfo(host, port, nil, Socket::SOCK_DGRAM, Socket::IPPROTO_UDP)[0] end + # + # call-seq: + # Addrinfo.unix(path, socktype=Socket::SOCK_STREAM) -> addrinfo + # + # Creates an Addrinfo object for Unix domain socket at the given path. + # + # Addrinfo.unix("/tmp/socket") #=> # + # Addrinfo.unix("/var/run/daemon.sock", Socket::SOCK_DGRAM) #=> # + # def self.unix(path, socktype=Socket::SOCK_STREAM) Addrinfo.new(Socket.sockaddr_un(path), Socket::AF_UNIX, socktype) end + # + # call-seq: + # addrinfo.afamily -> integer + # + # Returns the address family of the socket address. + # + # Addrinfo.tcp("localhost", 80).afamily #=> 2 (AF_INET) + # Addrinfo.unix("/tmp/sock").afamily #=> 1 (AF_UNIX) + # + # + # call-seq: + # addrinfo.afamily -> integer + # + # Returns the address family of the socket address. + # + # Addrinfo.tcp("localhost", 80).afamily #=> 2 (AF_INET) + # Addrinfo.unix("/tmp/sock").afamily #=> 1 (AF_UNIX) + # def afamily @family end @@ -58,6 +134,15 @@ class Addrinfo # Socket.getnameinfo #end + # + # call-seq: + # addrinfo.inspect -> string + # + # Returns a string representation of the Addrinfo object. + # + # Addrinfo.tcp("localhost", 80).inspect #=> "#" + # Addrinfo.unix("/tmp/sock").inspect #=> "#" + # def inspect if ipv4? or ipv6? if @protocol == Socket::IPPROTO_TCP or (@socktype == Socket::SOCK_STREAM and @protocol == 0) @@ -73,6 +158,15 @@ class Addrinfo "#" end + # + # call-seq: + # addrinfo.inspect_sockaddr -> string + # + # Returns a string representation of the socket address portion. + # + # Addrinfo.tcp("localhost", 80).inspect_sockaddr #=> "127.0.0.1:80" + # Addrinfo.unix("/tmp/sock").inspect_sockaddr #=> "/tmp/sock" + # def inspect_sockaddr if ipv4? a, p = ip_unpack @@ -87,23 +181,68 @@ class Addrinfo end end + # + # call-seq: + # addrinfo.ip? -> true or false + # + # Returns true if the address is an IP address (IPv4 or IPv6). + # + # Addrinfo.tcp("localhost", 80).ip? #=> true + # Addrinfo.unix("/tmp/sock").ip? #=> false + # def ip? ipv4? or ipv6? end + # + # call-seq: + # addrinfo.ip_address -> string + # + # Returns the IP address as a string. Raises an exception if not an IP address. + # + # Addrinfo.tcp("localhost", 80).ip_address #=> "127.0.0.1" + # Addrinfo.udp("::1", 53).ip_address #=> "::1" + # def ip_address ip_unpack[0] end + # + # call-seq: + # addrinfo.ip_port -> integer + # + # Returns the port number. Raises an exception if not an IP address. + # + # Addrinfo.tcp("localhost", 80).ip_port #=> 80 + # Addrinfo.udp("127.0.0.1", 53).ip_port #=> 53 + # def ip_port ip_unpack[1] end + # + # call-seq: + # addrinfo.ip_unpack -> [ip_address, port] + # + # Returns an array containing the IP address and port number. + # + # Addrinfo.tcp("localhost", 80).ip_unpack #=> ["127.0.0.1", 80] + # Addrinfo.udp("::1", 53).ip_unpack #=> ["::1", 53] + # def ip_unpack h, p = getnameinfo(Socket::NI_NUMERICHOST|Socket::NI_NUMERICSERV) [ h, p.to_i ] end + # + # call-seq: + # addrinfo.ipv4? -> true or false + # + # Returns true if the address is an IPv4 address. + # + # Addrinfo.tcp("127.0.0.1", 80).ipv4? #=> true + # Addrinfo.tcp("::1", 80).ipv4? #=> false + # def ipv4? @family == Socket::AF_INET end @@ -112,6 +251,15 @@ class Addrinfo #def ipv4_multicast? #def ipv4_private? + # + # call-seq: + # addrinfo.ipv6? -> true or false + # + # Returns true if the address is an IPv6 address. + # + # Addrinfo.tcp("::1", 80).ipv6? #=> true + # Addrinfo.tcp("127.0.0.1", 80).ipv6? #=> false + # def ipv6? @family == Socket::AF_INET6 end @@ -129,13 +277,48 @@ class Addrinfo #def ipv6_v4mapped? #def listen(backlog=5) + # + # call-seq: + # addrinfo.pfamily -> integer + # + # Returns the protocol family (same as afamily). + # + # Addrinfo.tcp("localhost", 80).pfamily #=> 2 (PF_INET) + # Addrinfo.unix("/tmp/sock").pfamily #=> 1 (PF_UNIX) + # def pfamily @family end + # + # call-seq: + # addrinfo.protocol -> integer + # + # Returns the protocol number. + # + # Addrinfo.tcp("localhost", 80).protocol #=> 6 (IPPROTO_TCP) + # Addrinfo.udp("localhost", 53).protocol #=> 17 (IPPROTO_UDP) + # attr_reader :protocol + + # + # call-seq: + # addrinfo.socktype -> integer + # + # Returns the socket type. + # + # Addrinfo.tcp("localhost", 80).socktype #=> 1 (SOCK_STREAM) + # Addrinfo.udp("localhost", 53).socktype #=> 2 (SOCK_DGRAM) + # attr_reader :socktype + # + # call-seq: + # addrinfo._to_array -> array + # + # Internal method that returns the address information as an array. + # Used internally by socket operations. + # def _to_array case @family when Socket::AF_INET @@ -151,12 +334,30 @@ class Addrinfo [ s, port.to_i, addr, addr ] end + # + # call-seq: + # addrinfo.to_sockaddr -> string + # + # Returns the socket address as a packed string. + # + # ai = Addrinfo.tcp("localhost", 80) + # ai.to_sockaddr #=> packed sockaddr string + # def to_sockaddr @sockaddr end alias to_s to_sockaddr + # + # call-seq: + # addrinfo.unix? -> true or false + # + # Returns true if the address is a Unix domain socket address. + # + # Addrinfo.unix("/tmp/sock").unix? #=> true + # Addrinfo.tcp("localhost", 80).unix? #=> false + # def unix? @family == Socket::AF_UNIX end @@ -165,30 +366,77 @@ end class BasicSocket < IO @@do_not_reverse_lookup = true + # + # call-seq: + # BasicSocket.do_not_reverse_lookup -> true or false + # + # Returns the current setting for reverse DNS lookups. + # + # BasicSocket.do_not_reverse_lookup #=> false + # def self.do_not_reverse_lookup @@do_not_reverse_lookup end + # + # call-seq: + # BasicSocket.do_not_reverse_lookup = boolean -> boolean + # + # Sets whether to perform reverse DNS lookups. + # + # BasicSocket.do_not_reverse_lookup = true + # def self.do_not_reverse_lookup=(val) @@do_not_reverse_lookup = val ? true : false end + # + # call-seq: + # BasicSocket.new(*args) -> basicsocket + # + # Creates a new BasicSocket object. This is typically called by subclasses. + # def initialize(*args) super(*args) self._is_socket = true @do_not_reverse_lookup = @@do_not_reverse_lookup end + # + # call-seq: + # BasicSocket.for_fd(fd) -> basicsocket + # + # Creates a BasicSocket object from an existing file descriptor. + # + # sock = BasicSocket.for_fd(3) + # def self.for_fd(fd) super(fd, "r+") end #def connect_address + # + # call-seq: + # basicsocket.local_address -> addrinfo + # + # Returns an Addrinfo object for the local address of the socket. + # + # sock.local_address #=> # + # def local_address Addrinfo.new self.getsockname end + # + # call-seq: + # basicsocket.recv_nonblock(maxlen, flags=0) -> string + # + # Receives data from the socket without blocking. May raise an exception + # if no data is available. + # + # data = sock.recv_nonblock(1024) + # def recv_nonblock(maxlen, flags=0) begin _setnonblock(true) @@ -198,6 +446,14 @@ class BasicSocket < IO end end + # + # call-seq: + # basicsocket.remote_address -> addrinfo + # + # Returns an Addrinfo object for the remote address of the socket. + # + # sock.remote_address #=> # + # def remote_address Addrinfo.new self.getpeername end @@ -206,18 +462,52 @@ class BasicSocket < IO end class IPSocket < BasicSocket + # + # call-seq: + # IPSocket.getaddress(host) -> string + # + # Returns the IP address of the given hostname as a string. + # + # IPSocket.getaddress("localhost") #=> "127.0.0.1" + # IPSocket.getaddress("www.ruby-lang.org") #=> "150.95.145.38" + # def self.getaddress(host) Addrinfo.ip(host).ip_address end + # + # call-seq: + # ipsocket.addr -> [family, port, hostname, ip_address] + # + # Returns the local address information as an array. + # + # sock.addr #=> ["AF_INET", 12345, "localhost", "127.0.0.1"] + # def addr Addrinfo.new(self.getsockname)._to_array end + # + # call-seq: + # ipsocket.peeraddr -> [family, port, hostname, ip_address] + # + # Returns the remote address information as an array. + # + # sock.peeraddr #=> ["AF_INET", 80, "example.com", "93.184.216.34"] + # def peeraddr Addrinfo.new(self.getpeername)._to_array end + # + # call-seq: + # ipsocket.recvfrom(maxlen, flags=0) -> [data, addrinfo] + # + # Receives data and sender information from the IP socket. + # + # data, addr = sock.recvfrom(1024) + # data, addr = sock.recvfrom(512, 0) + # def recvfrom(maxlen, flags=0) msg, sa = _recvfrom(maxlen, flags) [ msg, Addrinfo.new(sa)._to_array ] @@ -225,6 +515,17 @@ class IPSocket < BasicSocket end class TCPSocket < IPSocket + # + # call-seq: + # TCPSocket.new(host, service, local_host=nil, local_service=nil) -> tcpsocket + # + # Creates a new TCP socket connected to the given host and service. + # Optionally binds to local_host and local_service first. + # + # sock = TCPSocket.new("localhost", 80) + # sock = TCPSocket.new("www.example.com", "http") + # sock = TCPSocket.new("remote", 80, "127.0.0.1", 12345) + # def initialize(host, service, local_host=nil, local_service=nil) if @init_with_fd super(host, service) @@ -254,6 +555,17 @@ class TCPSocket < IPSocket end class TCPServer < TCPSocket + # + # call-seq: + # TCPServer.new(host=nil, service) -> tcpserver + # + # Creates a new TCP server socket bound to the given host and service. + # If host is nil, binds to all available interfaces. + # + # server = TCPServer.new("localhost", 8080) + # server = TCPServer.new(nil, 3000) # binds to all interfaces + # server = TCPServer.new("0.0.0.0", "http") + # def initialize(host=nil, service) ai = Addrinfo.getaddrinfo(host, service, nil, nil, nil, Socket::AI_PASSIVE)[0] @init_with_fd = true @@ -266,6 +578,15 @@ class TCPServer < TCPSocket self end + # + # call-seq: + # tcpserver.accept -> tcpsocket + # + # Accepts an incoming connection and returns a new TCPSocket. + # + # server = TCPServer.new(8080) + # client = server.accept + # def accept fd = self.sysaccept begin @@ -281,6 +602,15 @@ class TCPServer < TCPSocket end end + # + # call-seq: + # tcpserver.accept_nonblock -> unixsocket + # + # Accepts an incoming connection without blocking. May raise an exception + # if no connection is available. + # + # client = server.accept_nonblock + # def accept_nonblock begin self._setnonblock(true) @@ -290,33 +620,86 @@ class TCPServer < TCPSocket end end + # + # call-seq: + # unixserver.listen(backlog) -> 0 + # + # Sets the socket to listen for incoming connections with the given backlog. + # + # server.listen(5) + # server.listen(128) + # def listen(backlog) Socket._listen(self.fileno, backlog) 0 end + # + # call-seq: + # tcpserver.sysaccept -> integer + # + # Accepts an incoming connection and returns the file descriptor. + # + # fd = server.sysaccept + # def sysaccept Socket._accept(self.fileno) end end class UDPSocket < IPSocket + # + # call-seq: + # UDPSocket.new(af=Socket::AF_INET) -> udpsocket + # + # Creates a new UDP socket for the given address family. + # + # sock = UDPSocket.new + # sock = UDPSocket.new(Socket::AF_INET6) + # def initialize(af=Socket::AF_INET) super(Socket._socket(af, Socket::SOCK_DGRAM, 0), "r+") @af = af self end + # + # call-seq: + # ipsocket.bind(host, port) -> 0 + # + # Binds the socket to the given host and port. + # + # sock.bind("127.0.0.1", 8080) + # sock.bind("0.0.0.0", 3000) + # def bind(host, port) Socket._bind(self.fileno, _sockaddr_in(port, host)) 0 end + # + # call-seq: + # ipsocket.connect(host, port) -> 0 + # + # Connects the socket to the given host and port. + # + # sock.connect("127.0.0.1", 80) + # sock.connect("www.example.com", 443) + # def connect(host, port) Socket._connect(self.fileno, _sockaddr_in(port, host)) 0 end + # + # call-seq: + # udpsocket.recvfrom_nonblock(maxlen, flags=0) -> [data, addrinfo] + # + # Receives data and sender information without blocking. + # May raise an exception if no data is available. + # + # data, addr = sock.recvfrom_nonblock(1024) + # def recvfrom_nonblock(*args) s = self begin @@ -328,6 +711,15 @@ class UDPSocket < IPSocket end end + # + # call-seq: + # ipsocket.send(mesg, flags, host=nil, port=nil) -> integer + # + # Sends data through the socket. Returns the number of bytes sent. + # + # sock.send("Hello", 0) + # sock.send("Data", 0, "127.0.0.1", 8080) + # def send(mesg, flags, host=nil, port=nil) if port super(mesg, flags, _sockaddr_in(port, host)) @@ -338,6 +730,13 @@ class UDPSocket < IPSocket end end + # + # call-seq: + # udpsocket._sockaddr_in(port, host) -> string + # + # Internal method to create a sockaddr_in structure for the given port and host. + # Uses the socket's address family. + # def _sockaddr_in(port, host) ai = Addrinfo.getaddrinfo(host, port, @af, Socket::SOCK_DGRAM)[0] ai.to_sockaddr @@ -345,12 +744,30 @@ class UDPSocket < IPSocket end class Socket < BasicSocket + # + # call-seq: + # Socket.new(domain, type, protocol=0) -> socket + # + # Creates a new socket with the given domain, type, and protocol. + # + # sock = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0) + # sock = Socket.new(Socket::AF_UNIX, Socket::SOCK_DGRAM) + # def initialize(domain, type, protocol=0) super(Socket._socket(domain, type, protocol), "r+") end #def self.accept_loop + # + # call-seq: + # Socket.getaddrinfo(nodename, servname, family=nil, socktype=nil, protocol=nil, flags=0) -> array + # + # Returns an array of Addrinfo objects for the given nodename and servname. + # + # Addrinfo.getaddrinfo("localhost", "http") + # Addrinfo.getaddrinfo("www.example.com", 80, Socket::AF_INET) + # 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 @@ -365,10 +782,27 @@ class Socket < BasicSocket #def self.getnameinfo #def self.ip_address_list + # + # call-seq: + # Socket.open(domain, type, protocol=0) -> socket + # + # Creates a new socket. Alias for Socket.new. + # + # sock = Socket.open(Socket::AF_INET, Socket::SOCK_STREAM) + # def self.open(*args) new(args) end + # + # call-seq: + # Socket.sockaddr_in(port, host) -> string + # + # Returns a packed sockaddr_in structure for the given port and host. + # + # Socket.sockaddr_in(80, "127.0.0.1") + # Socket.sockaddr_in(443, "localhost") + # def self.sockaddr_in(port, host) ai = Addrinfo.getaddrinfo(host, port, nil, Socket::SOCK_DGRAM)[0] ai.to_sockaddr @@ -385,10 +819,26 @@ class Socket < BasicSocket #def self.unix_server_loop #def self.unix_server_socket + # + # call-seq: + # Socket.unpack_sockaddr_in(sockaddr) -> [port, ip_address] + # + # Unpacks a packed sockaddr_in structure and returns port and IP address. + # + # port, addr = Socket.unpack_sockaddr_in(sockaddr) + # def self.unpack_sockaddr_in(sa) Addrinfo.new(sa).ip_unpack.reverse end + # + # call-seq: + # Socket.unpack_sockaddr_un(sockaddr) -> path + # + # Unpacks a packed sockaddr_un structure and returns the Unix socket path. + # + # path = Socket.unpack_sockaddr_un(sockaddr) + # def self.unpack_sockaddr_un(sa) Addrinfo.new(sa).unix_path end @@ -413,18 +863,45 @@ class Socket < BasicSocket end end + # + # call-seq: + # socket.bind(sockaddr) -> 0 + # + # Binds the socket to the given socket address. + # + # sock.bind(Socket.sockaddr_in(8080, "127.0.0.1")) + # sock.bind(addrinfo) + # def bind(sockaddr) sockaddr = sockaddr.to_sockaddr if sockaddr.is_a? Addrinfo Socket._bind(self.fileno, sockaddr) 0 end + # + # call-seq: + # socket.connect(sockaddr) -> 0 + # + # Connects the socket to the given socket address. + # + # sock.connect(Socket.sockaddr_in(80, "127.0.0.1")) + # sock.connect(addrinfo) + # def connect(sockaddr) sockaddr = sockaddr.to_sockaddr if sockaddr.is_a? Addrinfo Socket._connect(self.fileno, sockaddr) 0 end + # + # call-seq: + # socket.connect_nonblock(sockaddr) -> 0 + # + # Connects the socket to the given address without blocking. + # May raise an exception if the connection cannot be completed immediately. + # + # sock.connect_nonblock(sockaddr) + # def connect_nonblock(sockaddr) begin self._setnonblock(true) @@ -462,6 +939,17 @@ class Socket < BasicSocket end class UNIXSocket < BasicSocket + # + # call-seq: + # UNIXSocket.new(path) -> unixsocket + # UNIXSocket.new(path) { |sock| block } -> obj + # + # Creates a new Unix domain socket connected to the given path. + # If a block is given, yields the socket and closes it when done. + # + # sock = UNIXSocket.new("/tmp/socket") + # UNIXSocket.new("/tmp/socket") { |s| s.write("data") } + # def initialize(path, &block) if self.is_a? UNIXServer super(path, "r") @@ -483,6 +971,15 @@ class UNIXSocket < BasicSocket end class << self + # + # call-seq: + # UNIXSocket.socketpair(type=Socket::SOCK_STREAM, protocol=0) -> [socket1, socket2] + # + # Creates a pair of connected Unix domain sockets. + # + # sock1, sock2 = UNIXSocket.socketpair + # sock1, sock2 = UNIXSocket.socketpair(Socket::SOCK_DGRAM) + # def 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]) ] @@ -491,14 +988,38 @@ class UNIXSocket < BasicSocket alias pair socketpair end + # + # call-seq: + # unixsocket.addr -> [family, path] + # + # Returns the local address information as an array. + # + # sock.addr #=> ["AF_UNIX", "/tmp/socket"] + # def addr [ "AF_UNIX", path ] end + # + # call-seq: + # unixsocket.path -> string + # + # Returns the path of the Unix domain socket. + # + # sock.path #=> "/tmp/socket" + # def path Addrinfo.new(self.getsockname).unix_path end + # + # call-seq: + # unixsocket.peeraddr -> [family, path] + # + # Returns the remote address information as an array. + # + # sock.peeraddr #=> ["AF_UNIX", "/tmp/peer_socket"] + # def peeraddr [ "AF_UNIX", Addrinfo.new(self.getpeername).unix_path ] end @@ -515,6 +1036,14 @@ class UNIXSocket < BasicSocket end class UNIXServer < UNIXSocket + # + # call-seq: + # UNIXServer.new(path) -> unixserver + # + # Creates a new Unix domain server socket bound to the given path. + # + # server = UNIXServer.new("/tmp/server_socket") + # def initialize(path) fd = Socket._socket(Socket::AF_UNIX, Socket::SOCK_STREAM, 0) begin @@ -535,6 +1064,15 @@ class UNIXServer < UNIXSocket end end + # + # call-seq: + # unixserver.accept -> unixsocket + # + # Accepts an incoming connection and returns a new UNIXSocket. + # + # server = UNIXServer.new("/tmp/server") + # client = server.accept + # def accept fd = self.sysaccept begin @@ -545,6 +1083,15 @@ class UNIXServer < UNIXSocket sock end + # + # call-seq: + # unixserver.accept_nonblock -> unixsocket + # + # Accepts an incoming connection without blocking. May raise an exception + # if no connection is available. + # + # client = server.accept_nonblock + # def accept_nonblock begin self._setnonblock(true) @@ -554,11 +1101,28 @@ class UNIXServer < UNIXSocket end end + # + # call-seq: + # unixserver.listen(backlog) -> 0 + # + # Sets the socket to listen for incoming connections with the given backlog. + # + # server.listen(5) + # server.listen(128) + # def listen(backlog) Socket._listen(self.fileno, backlog) 0 end + # + # call-seq: + # unixserver.sysaccept -> integer + # + # Accepts an incoming connection and returns the file descriptor. + # + # fd = server.sysaccept + # def sysaccept Socket._accept(self.fileno) end diff --git a/mrbgems/mruby-socket/src/socket.c b/mrbgems/mruby-socket/src/socket.c index d7707c58b..b0c91694d 100644 --- a/mrbgems/mruby-socket/src/socket.c +++ b/mrbgems/mruby-socket/src/socket.c @@ -54,6 +54,7 @@ #define E_SOCKET_ERROR mrb_class_get_id(mrb, MRB_SYM(SocketError)) #ifdef _WIN32 +/* Windows implementation of inet_ntop - converts network address to string */ static const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt) { if (af == AF_INET) { @@ -77,6 +78,7 @@ static const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt) return NULL; } +/* Windows implementation of inet_pton - converts string address to network format */ static int inet_pton(int af, const char *src, void *dst) { struct addrinfo hints = {0}; @@ -103,6 +105,7 @@ struct gen_addrinfo_args { struct addrinfo *addrinfo; }; +/* Helper to generate array of Addrinfo objects from addrinfo linked list */ static mrb_value gen_addrinfo(mrb_state *mrb, mrb_value args) { @@ -120,6 +123,7 @@ gen_addrinfo(mrb_state *mrb, mrb_value args) return ary; } +/* Helper to free addrinfo structure - used with mrb_ensure */ static mrb_value free_addrinfo(mrb_state *mrb, mrb_value addrinfo) { @@ -127,6 +131,15 @@ free_addrinfo(mrb_state *mrb, mrb_value addrinfo) return mrb_nil_value(); } +/* + * call-seq: + * Addrinfo.getaddrinfo(nodename, servname, family=nil, socktype=nil, protocol=nil, flags=0) -> array + * + * Returns an array of Addrinfo objects for the given nodename and servname. + * + * Addrinfo.getaddrinfo("localhost", "http") + * Addrinfo.getaddrinfo("www.example.com", 80, Socket::AF_INET) + */ static mrb_value mrb_addrinfo_getaddrinfo(mrb_state *mrb, mrb_value klass) { @@ -175,6 +188,15 @@ mrb_addrinfo_getaddrinfo(mrb_state *mrb, mrb_value klass) return mrb_ensure(mrb, gen_addrinfo, mrb_cptr_value(mrb, &args), free_addrinfo, mrb_cptr_value(mrb, addr)); } +/* + * call-seq: + * addrinfo.getnameinfo(flags=0) -> [hostname, service] + * + * Returns the hostname and service name for the address. + * + * addr.getnameinfo #=> ["localhost", "http"] + * addr.getnameinfo(Socket::NI_NUMERICHOST) #=> ["127.0.0.1", "80"] + */ static mrb_value mrb_addrinfo_getnameinfo(mrb_state *mrb, mrb_value self) { @@ -201,6 +223,14 @@ mrb_addrinfo_getnameinfo(mrb_state *mrb, mrb_value self) } #ifndef _WIN32 +/* + * call-seq: + * addrinfo.unix_path -> string + * + * Returns the Unix domain socket path. + * + * addr.unix_path #=> "/tmp/socket" + */ static mrb_value mrb_addrinfo_unix_path(mrb_state *mrb, mrb_value self) { @@ -217,6 +247,7 @@ mrb_addrinfo_unix_path(mrb_state *mrb, mrb_value self) } #endif +/* Helper to convert sockaddr to address list array [family, port, host, host] */ static mrb_value sa2addrlist(mrb_state *mrb, const struct sockaddr *sa, socklen_t salen) { @@ -252,12 +283,14 @@ sa2addrlist(mrb_state *mrb, const struct sockaddr *sa, socklen_t salen) int mrb_io_fileno(mrb_state *mrb, mrb_value io); +/* Helper to extract file descriptor from socket object */ static int socket_fd(mrb_state *mrb, mrb_value sock) { return mrb_io_fileno(mrb, sock); } +/* Helper to get address family of socket by file descriptor */ static int socket_family(int s) { @@ -269,6 +302,15 @@ socket_family(int s) return ss.ss_family; } +/* + * call-seq: + * basicsocket.getpeereid -> [euid, egid] + * + * Returns the effective user ID and group ID of the peer process. + * Only available on systems that support getpeereid(). + * + * euid, egid = sock.getpeereid + */ static mrb_value mrb_basicsocket_getpeereid(mrb_state *mrb, mrb_value self) { @@ -289,6 +331,14 @@ mrb_basicsocket_getpeereid(mrb_state *mrb, mrb_value self) #endif } +/* + * call-seq: + * basicsocket.getpeername -> string + * + * Returns the remote socket address as a packed sockaddr string. + * + * sockaddr = sock.getpeername + */ static mrb_value mrb_basicsocket_getpeername(mrb_state *mrb, mrb_value self) { @@ -301,6 +351,14 @@ mrb_basicsocket_getpeername(mrb_state *mrb, mrb_value self) return mrb_str_new(mrb, (char*)&ss, salen); } +/* + * call-seq: + * basicsocket.getsockname -> string + * + * Returns the local socket address as a packed sockaddr string. + * + * sockaddr = sock.getsockname + */ static mrb_value mrb_basicsocket_getsockname(mrb_state *mrb, mrb_value self) { @@ -313,12 +371,21 @@ mrb_basicsocket_getsockname(mrb_state *mrb, mrb_value self) return mrb_str_new(mrb, (char*)&ss, salen); } +/* Helper to get Socket::Option class reference */ static struct RClass * socket_option_class(mrb_state *mrb) { return mrb_class_get_under_id(mrb, mrb_class_get_id(mrb, MRB_SYM(Socket)), MRB_SYM(Option)); } +/* + * call-seq: + * Socket::Option.new(family, level, optname, data) -> socket_option + * + * Creates a new Socket::Option object with the given parameters. + * + * opt = Socket::Option.new(Socket::AF_INET, Socket::SOL_SOCKET, Socket::SO_REUSEADDR, [1].pack("i")) + */ static mrb_value socket_option_init(mrb_state *mrb, mrb_value self) { @@ -334,6 +401,14 @@ socket_option_init(mrb_state *mrb, mrb_value self) return self; } +/* + * call-seq: + * Socket::Option.bool(family, level, optname, bool) -> socket_option + * + * Creates a new Socket::Option object from a boolean value. + * + * opt = Socket::Option.bool(Socket::AF_INET, Socket::SOL_SOCKET, Socket::SO_REUSEADDR, true) + */ static mrb_value socket_option_s_bool(mrb_state *mrb, mrb_value klass) { @@ -347,6 +422,14 @@ socket_option_s_bool(mrb_state *mrb, mrb_value klass) return mrb_obj_new(mrb, mrb_class_ptr(klass), 4, args); } +/* + * call-seq: + * Socket::Option.int(family, level, optname, integer) -> socket_option + * + * Creates a new Socket::Option object from an integer value. + * + * opt = Socket::Option.int(Socket::AF_INET, Socket::SOL_SOCKET, Socket::SO_KEEPALIVE, 1) + */ static mrb_value socket_option_s_int(mrb_state *mrb, mrb_value klass) { @@ -360,30 +443,63 @@ socket_option_s_int(mrb_state *mrb, mrb_value klass) return mrb_obj_new(mrb, mrb_class_ptr(klass), 4, args); } +/* + * call-seq: + * socket_option.family -> integer + * + * Returns the address family of the socket option. + * + * opt.family #=> Socket::AF_INET + */ static mrb_value socket_option_family(mrb_state *mrb, mrb_value self) { return mrb_iv_get(mrb, self, MRB_SYM(family)); } +/* + * call-seq: + * socket_option.level -> integer + * + * Returns the protocol level of the socket option. + * + * opt.level #=> Socket::SOL_SOCKET + */ static mrb_value socket_option_level(mrb_state *mrb, mrb_value self) { return mrb_iv_get(mrb, self, MRB_SYM(level)); } +/* + * call-seq: + * socket_option.optname -> integer + * + * Returns the option name of the socket option. + * + * opt.optname #=> Socket::SO_REUSEADDR + */ static mrb_value socket_option_optname(mrb_state *mrb, mrb_value self) { return mrb_iv_get(mrb, self, MRB_SYM(optname)); } +/* + * call-seq: + * socket_option.data -> string + * + * Returns the raw data of the socket option as a string. + * + * opt.data #=> "\x01\x00\x00\x00" + */ static mrb_value socket_option_data(mrb_state *mrb, mrb_value self) { return mrb_iv_get(mrb, self, MRB_SYM(data)); } +/* Helper to extract integer value from Socket::Option data */ static int option_int(mrb_state *mrb, mrb_value self) { @@ -398,6 +514,14 @@ option_int(mrb_state *mrb, mrb_value self) return tmp; } +/* + * call-seq: + * socket_option.int -> integer + * + * Returns the socket option data as an integer value. + * + * opt.int #=> 1 + */ static mrb_value socket_option_int(mrb_state *mrb, mrb_value self) { @@ -405,6 +529,14 @@ socket_option_int(mrb_state *mrb, mrb_value self) return mrb_int_value(mrb, (mrb_int)i); } +/* + * call-seq: + * socket_option.bool -> true or false + * + * Returns the socket option data as a boolean value. + * + * opt.bool #=> true + */ static mrb_value socket_option_bool(mrb_state *mrb, mrb_value self) { @@ -412,6 +544,7 @@ socket_option_bool(mrb_state *mrb, mrb_value self) return mrb_bool_value((mrb_bool)i); } +/* Helper to raise not implemented error for unimplemented Socket::Option methods */ static mrb_value socket_option_notimp(mrb_state *mrb, mrb_value self) { @@ -419,6 +552,14 @@ socket_option_notimp(mrb_state *mrb, mrb_value self) return mrb_nil_value(); } +/* + * call-seq: + * socket_option.inspect -> string + * + * Returns a string representation of the socket option for debugging. + * + * opt.inspect #=> "#" + */ static mrb_value socket_option_inspect(mrb_state *mrb, mrb_value self) { @@ -475,6 +616,14 @@ socket_option_inspect(mrb_state *mrb, mrb_value self) return str; } +/* + * call-seq: + * basicsocket.getsockopt(level, optname) -> string + * + * Gets a socket option. Returns the option value as a string. + * + * val = sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEADDR) + */ static mrb_value mrb_basicsocket_getsockopt(mrb_state *mrb, mrb_value self) { @@ -494,6 +643,15 @@ mrb_basicsocket_getsockopt(mrb_state *mrb, mrb_value self) return mrb_obj_new(mrb, socket_option_class(mrb), 4, args); } +/* + * call-seq: + * basicsocket.recv(maxlen, flags=0) -> string + * + * Receives data from the socket. + * + * data = sock.recv(1024) + * data = sock.recv(512, 0) + */ static mrb_value mrb_basicsocket_recv(mrb_state *mrb, mrb_value self) { @@ -509,6 +667,13 @@ mrb_basicsocket_recv(mrb_state *mrb, mrb_value self) return buf; } +/* + * call-seq: + * basicsocket._recvfrom(maxlen, flags=0) -> [data, sockaddr] + * + * Internal method to receive data and sender address from socket. + * Returns data and packed sockaddr. + */ static mrb_value mrb_basicsocket_recvfrom(mrb_state *mrb, mrb_value self) { @@ -531,6 +696,14 @@ mrb_basicsocket_recvfrom(mrb_state *mrb, mrb_value self) return ary; } +/* + * call-seq: + * basicsocket.send(mesg, flags) -> integer + * + * Sends data through the socket. Returns the number of bytes sent. + * + * bytes_sent = sock.send("Hello", 0) + */ static mrb_value mrb_basicsocket_send(mrb_state *mrb, mrb_value self) { @@ -552,6 +725,15 @@ mrb_basicsocket_send(mrb_state *mrb, mrb_value self) return mrb_fixnum_value((mrb_int)n); } +/* + * call-seq: + * basicsocket._setnonblock(flag) -> nil + * + * Internal method to set or unset non-blocking mode on the socket. + * + * sock._setnonblock(true) # enable non-blocking + * sock._setnonblock(false) # disable non-blocking + */ static mrb_value mrb_basicsocket_setnonblock(mrb_state *mrb, mrb_value self) { @@ -580,6 +762,14 @@ mrb_basicsocket_setnonblock(mrb_state *mrb, mrb_value self) return mrb_nil_value(); } +/* + * call-seq: + * basicsocket.setsockopt(level, optname, optval) -> 0 + * + * Sets a socket option. Level and optname are constants, optval is the value. + * + * sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEADDR, 1) + */ static mrb_value mrb_basicsocket_setsockopt(mrb_state *mrb, mrb_value self) { @@ -629,6 +819,16 @@ mrb_basicsocket_setsockopt(mrb_state *mrb, mrb_value self) return mrb_fixnum_value(0); } +/* + * call-seq: + * basicsocket.shutdown(how=Socket::SHUT_RDWR) -> 0 + * + * Shuts down part of the socket connection. + * + * sock.shutdown(Socket::SHUT_RD) # shutdown reading + * sock.shutdown(Socket::SHUT_WR) # shutdown writing + * sock.shutdown(Socket::SHUT_RDWR) # shutdown both (default) + */ static mrb_value mrb_basicsocket_shutdown(mrb_state *mrb, mrb_value self) { @@ -640,6 +840,7 @@ mrb_basicsocket_shutdown(mrb_state *mrb, mrb_value self) return mrb_fixnum_value(0); } +/* Helper to set socket flag on IO object */ static mrb_value mrb_basicsocket_set_is_socket(mrb_state *mrb, mrb_value self) { @@ -654,6 +855,14 @@ mrb_basicsocket_set_is_socket(mrb_state *mrb, mrb_value self) return mrb_bool_value(b); } +/* + * call-seq: + * IPSocket.ntop(af, addr) -> string + * + * Converts a network address to a string representation. + * + * IPSocket.ntop(Socket::AF_INET, "\x7f\x00\x00\x01") #=> "127.0.0.1" + */ static mrb_value mrb_ipsocket_ntop(mrb_state *mrb, mrb_value klass) { @@ -668,6 +877,14 @@ mrb_ipsocket_ntop(mrb_state *mrb, mrb_value klass) return mrb_str_new_cstr(mrb, buf); } +/* + * call-seq: + * IPSocket.pton(af, hostname) -> string + * + * Converts a string representation of an address to network format. + * + * IPSocket.pton(Socket::AF_INET, "127.0.0.1") #=> "\x7f\x00\x00\x01" + */ static mrb_value mrb_ipsocket_pton(mrb_state *mrb, mrb_value klass) { @@ -701,6 +918,15 @@ invalid: return mrb_nil_value(); /* dummy */ } +/* + * call-seq: + * ipsocket.recvfrom(maxlen, flags=0) -> [data, [family, port, hostname, ip]] + * + * Receives data from the socket and returns sender address information. + * + * data, addr = sock.recvfrom(1024) + * # addr => ["AF_INET", 12345, "hostname", "192.168.1.1"] + */ static mrb_value mrb_ipsocket_recvfrom(mrb_state *mrb, mrb_value self) { @@ -727,6 +953,14 @@ mrb_ipsocket_recvfrom(mrb_state *mrb, mrb_value self) return pair; } +/* + * call-seq: + * Socket.gethostname -> string + * + * Returns the hostname of the current machine. + * + * Socket.gethostname #=> "localhost" + */ static mrb_value mrb_socket_gethostname(mrb_state *mrb, mrb_value cls) { @@ -743,6 +977,13 @@ mrb_socket_gethostname(mrb_state *mrb, mrb_value cls) return buf; } +/* + * call-seq: + * Socket._accept(fd) -> [new_fd, sockaddr] + * + * Internal method to accept a connection on a socket file descriptor. + * Returns the new file descriptor and remote address. + */ static mrb_value mrb_socket_accept(mrb_state *mrb, mrb_value klass) { @@ -778,6 +1019,12 @@ mrb_socket_accept2(mrb_state *mrb, mrb_value klass) return ary; } +/* + * call-seq: + * Socket._bind(fd, sockaddr) -> 0 + * + * Internal method to bind a socket file descriptor to the given address. + */ static mrb_value mrb_socket_bind(mrb_state *mrb, mrb_value klass) { @@ -791,6 +1038,12 @@ mrb_socket_bind(mrb_state *mrb, mrb_value klass) return mrb_nil_value(); } +/* + * call-seq: + * Socket._connect(fd, sockaddr) -> 0 + * + * Internal method to connect a socket file descriptor to the given address. + */ static mrb_value mrb_socket_connect(mrb_state *mrb, mrb_value klass) { @@ -804,6 +1057,12 @@ mrb_socket_connect(mrb_state *mrb, mrb_value klass) return mrb_nil_value(); } +/* + * call-seq: + * Socket._listen(fd, backlog) -> 0 + * + * Internal method to set a socket file descriptor to listen for connections. + */ static mrb_value mrb_socket_listen(mrb_state *mrb, mrb_value klass) { @@ -830,6 +1089,15 @@ mrb_socket_sockaddr_family(mrb_state *mrb, mrb_value klass) return mrb_fixnum_value(sa->sa_family); } +/* + * call-seq: + * Socket.sockaddr_un(path) -> string + * + * Returns a packed sockaddr_un structure for the given Unix socket path. + * + * Socket.sockaddr_un("/tmp/socket") + * Socket.sockaddr_un("/var/run/daemon.sock") + */ static mrb_value mrb_socket_sockaddr_un(mrb_state *mrb, mrb_value klass) { @@ -857,6 +1125,16 @@ mrb_socket_sockaddr_un(mrb_state *mrb, mrb_value klass) #endif } +/* + * call-seq: + * Socket.socketpair(domain, type, protocol=0) -> [socket1, socket2] + * Socket.pair(domain, type, protocol=0) -> [socket1, socket2] + * + * Creates a pair of connected sockets. + * + * sock1, sock2 = Socket.socketpair(Socket::AF_UNIX, Socket::SOCK_STREAM) + * sock1, sock2 = Socket.pair(Socket::AF_UNIX, Socket::SOCK_DGRAM) + */ static mrb_value mrb_socket_socketpair(mrb_state *mrb, mrb_value klass) { @@ -880,6 +1158,14 @@ mrb_socket_socketpair(mrb_state *mrb, mrb_value klass) #endif } +/* + * call-seq: + * Socket._socket(domain, type, protocol) -> fd + * + * Internal method to create a new socket and return its file descriptor. + * + * fd = Socket._socket(Socket::AF_INET, Socket::SOCK_STREAM, 0) + */ static mrb_value mrb_socket_socket(mrb_state *mrb, mrb_value klass) { @@ -893,6 +1179,7 @@ mrb_socket_socket(mrb_state *mrb, mrb_value klass) return mrb_fixnum_value(s); } +/* Helper to allocate TCPSocket object */ static mrb_value mrb_tcpsocket_allocate(mrb_state *mrb, mrb_value klass) { @@ -910,6 +1197,13 @@ mrb_tcpsocket_allocate(mrb_state *mrb, mrb_value klass) * will break on socket descriptors. */ #ifdef _WIN32 +/* + * call-seq: + * basicsocket.close -> nil + * + * Windows-specific implementation to close socket using closesocket(). + * Overrides IO#close for socket objects on Windows. + */ static mrb_value mrb_win32_basicsocket_close(mrb_state *mrb, mrb_value self) { @@ -918,6 +1212,13 @@ mrb_win32_basicsocket_close(mrb_state *mrb, mrb_value self) return mrb_nil_value(); } +/* + * call-seq: + * basicsocket.sysread(maxlen, outbuf=nil) -> string + * + * Windows-specific implementation to read from socket using recv(). + * Overrides IO#sysread for socket objects on Windows. + */ static mrb_value mrb_win32_basicsocket_sysread(mrb_state *mrb, mrb_value self) { @@ -961,6 +1262,13 @@ mrb_win32_basicsocket_sysread(mrb_state *mrb, mrb_value self) return buf; } +/* + * call-seq: + * basicsocket.sysseek(offset, whence) -> integer + * + * Windows-specific implementation that raises NotImplementedError. + * Sockets don't support seeking operations. + */ static mrb_value mrb_win32_basicsocket_sysseek(mrb_state *mrb, mrb_value self) { @@ -968,6 +1276,13 @@ mrb_win32_basicsocket_sysseek(mrb_state *mrb, mrb_value self) return mrb_nil_value(); } +/* + * call-seq: + * basicsocket.syswrite(string) -> integer + * + * Windows-specific implementation to write to socket using send(). + * Overrides IO#syswrite for socket objects on Windows. + */ static mrb_value mrb_win32_basicsocket_syswrite(mrb_state *mrb, mrb_value self) {