/ext/socket/socket.c
C | 6607 lines | 3911 code | 575 blank | 2121 comment | 708 complexity | 3e0615e38a3c23a30f1ec0a06234c95d MD5 | raw file
Possible License(s): GPL-2.0, BSD-3-Clause, LGPL-2.1
Large files files are truncated, but you can click here to view the full file
- /************************************************
- socket.c -
- $Author: akr $
- created at: Thu Mar 31 12:21:29 JST 1994
- Copyright (C) 1993-2007 Yukihiro Matsumoto
- ************************************************/
- #include "macruby_internal.h"
- #include "ruby/io.h"
- #include "ruby/signal.h"
- #include "ruby/util.h"
- #include <stdio.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #ifdef HAVE_UNISTD_H
- #include <unistd.h>
- #endif
- #ifdef HAVE_SYS_UIO_H
- #include <sys/uio.h>
- #endif
- #ifdef HAVE_XTI_H
- #include <xti.h>
- #endif
- #ifndef _WIN32
- #if defined(__BEOS__)
- # include <net/socket.h>
- #else
- # include <sys/socket.h>
- #endif
- #include <netinet/in.h>
- #ifdef HAVE_NETINET_IN_SYSTM_H
- # include <netinet/in_systm.h>
- #endif
- #ifdef HAVE_NETINET_TCP_H
- # include <netinet/tcp.h>
- #endif
- #ifdef HAVE_NETINET_UDP_H
- # include <netinet/udp.h>
- #endif
- #ifdef HAVE_ARPA_INET_H
- # include <arpa/inet.h>
- #endif
- #include <netdb.h>
- #endif
- #include <errno.h>
- #ifdef HAVE_SYS_UN_H
- #include <sys/un.h>
- #endif
- #if defined(HAVE_FCNTL)
- #ifdef HAVE_SYS_SELECT_H
- #include <sys/select.h>
- #endif
- #ifdef HAVE_SYS_TYPES_H
- #include <sys/types.h>
- #endif
- #ifdef HAVE_SYS_TIME_H
- #include <sys/time.h>
- #endif
- #ifdef HAVE_FCNTL_H
- #include <fcntl.h>
- #endif
- #endif
- #ifdef HAVE_IFADDRS_H
- #include <ifaddrs.h>
- #endif
- #ifdef HAVE_SYS_IOCTL_H
- #include <sys/ioctl.h>
- #endif
- #ifdef HAVE_SYS_SOCKIO_H
- #include <sys/sockio.h>
- #endif
- #ifdef HAVE_NET_IF_H
- #include <net/if.h>
- #endif
- #ifdef HAVE_SYS_PARAM_H
- #include <sys/param.h>
- #endif
- #ifdef HAVE_SYS_UCRED_H
- #include <sys/ucred.h>
- #endif
- #ifdef HAVE_UCRED_H
- #include <ucred.h>
- #endif
- #ifndef EWOULDBLOCK
- #define EWOULDBLOCK EAGAIN
- #endif
- #ifndef HAVE_GETADDRINFO
- # include "addrinfo.h"
- #endif
- #include "sockport.h"
- static int do_not_reverse_lookup = 1;
- #define FMODE_NOREVLOOKUP 0x100
- VALUE rb_cBasicSocket;
- VALUE rb_cIPSocket;
- VALUE rb_cTCPSocket;
- VALUE rb_cTCPServer;
- VALUE rb_cUDPSocket;
- #ifdef AF_UNIX
- VALUE rb_cUNIXSocket;
- VALUE rb_cUNIXServer;
- #endif
- VALUE rb_cSocket;
- static VALUE rb_cAddrinfo;
- static VALUE rb_eSocket;
- #define INET_CLIENT 0
- #define INET_SERVER 1
- #define INET_SOCKS 2
- #ifndef NI_MAXHOST
- # define NI_MAXHOST 1025
- #endif
- #ifndef NI_MAXSERV
- # define NI_MAXSERV 32
- #endif
- #ifdef AF_INET6
- # define IS_IP_FAMILY(af) ((af) == AF_INET || (af) == AF_INET6)
- #else
- # define IS_IP_FAMILY(af) ((af) == AF_INET)
- #endif
- #ifndef HAVE_SOCKADDR_STORAGE
- /*
- * RFC 2553: protocol-independent placeholder for socket addresses
- */
- #define _SS_MAXSIZE 128
- #define _SS_ALIGNSIZE (sizeof(double))
- #define _SS_PAD1SIZE (_SS_ALIGNSIZE - sizeof(unsigned char) * 2)
- #define _SS_PAD2SIZE (_SS_MAXSIZE - sizeof(unsigned char) * 2 - \
- _SS_PAD1SIZE - _SS_ALIGNSIZE)
- struct sockaddr_storage {
- #ifdef HAVE_SA_LEN
- unsigned char ss_len; /* address length */
- unsigned char ss_family; /* address family */
- #else
- unsigned short ss_family;
- #endif
- char __ss_pad1[_SS_PAD1SIZE];
- double __ss_align; /* force desired structure storage alignment */
- char __ss_pad2[_SS_PAD2SIZE];
- };
- #endif
- static void sock_define_const(char *name, int value);
- static void sock_define_uconst(const char *name, unsigned int value);
- #include "constants.h"
- static int str_is_number(const char *);
- /* fix [ruby-core:29427] */
- static int
- ruby_getaddrinfo__darwin(const char *nodename, const char *servname,
- struct addrinfo *hints, struct addrinfo **res)
- {
- const char *tmp_servname;
- struct addrinfo tmp_hints;
- tmp_servname = servname;
- MEMCPY(&tmp_hints, hints, struct addrinfo, 1);
- if (nodename && servname) {
- if (str_is_number(tmp_servname) && atoi(servname) == 0) {
- tmp_servname = NULL;
- #ifdef AI_NUMERICSERV
- if (tmp_hints.ai_flags) tmp_hints.ai_flags &= ~AI_NUMERICSERV;
- #endif
- }
- }
- int error = getaddrinfo(nodename, tmp_servname, &tmp_hints, res);
- return error;
- }
- #undef getaddrinfo
- #define getaddrinfo(node,serv,hints,res) ruby_getaddrinfo__darwin((node),(serv),(hints),(res))
- #if defined(INET6) && (defined(LOOKUP_ORDER_HACK_INET) || defined(LOOKUP_ORDER_HACK_INET6))
- #define LOOKUP_ORDERS 3
- static int lookup_order_table[LOOKUP_ORDERS] = {
- #if defined(LOOKUP_ORDER_HACK_INET)
- PF_INET, PF_INET6, PF_UNSPEC,
- #elif defined(LOOKUP_ORDER_HACK_INET6)
- PF_INET6, PF_INET, PF_UNSPEC,
- #else
- /* should not happen */
- #endif
- };
- static int
- ruby_getaddrinfo(char *nodename, char *servname,
- struct addrinfo *hints, struct addrinfo **res)
- {
- struct addrinfo tmp_hints;
- int i, af, error;
- if (hints->ai_family != PF_UNSPEC) {
- return getaddrinfo(nodename, servname, hints, res);
- }
- for (i = 0; i < LOOKUP_ORDERS; i++) {
- af = lookup_order_table[i];
- MEMCPY(&tmp_hints, hints, struct addrinfo, 1);
- tmp_hints.ai_family = af;
- error = getaddrinfo(nodename, servname, &tmp_hints, res);
- if (error) {
- if (tmp_hints.ai_family == PF_UNSPEC) {
- break;
- }
- }
- else {
- break;
- }
- }
- return error;
- }
- #define getaddrinfo(node,serv,hints,res) ruby_getaddrinfo((node),(serv),(hints),(res))
- #endif
- #if defined(_AIX)
- static int
- ruby_getaddrinfo__aix(char *nodename, char *servname,
- struct addrinfo *hints, struct addrinfo **res)
- {
- int error = getaddrinfo(nodename, servname, hints, res);
- struct addrinfo *r;
- if (error)
- return error;
- for (r = *res; r != NULL; r = r->ai_next) {
- if (r->ai_addr->sa_family == 0)
- r->ai_addr->sa_family = r->ai_family;
- if (r->ai_addr->sa_len == 0)
- r->ai_addr->sa_len = r->ai_addrlen;
- }
- return 0;
- }
- #undef getaddrinfo
- #define getaddrinfo(node,serv,hints,res) ruby_getaddrinfo__aix((node),(serv),(hints),(res))
- static int
- ruby_getnameinfo__aix(const struct sockaddr *sa, size_t salen,
- char *host, size_t hostlen,
- char *serv, size_t servlen, int flags)
- {
- struct sockaddr_in6 *sa6;
- u_int32_t *a6;
- if (sa->sa_family == AF_INET6) {
- sa6 = (struct sockaddr_in6 *)sa;
- a6 = sa6->sin6_addr.u6_addr.u6_addr32;
- if (a6[0] == 0 && a6[1] == 0 && a6[2] == 0 && a6[3] == 0) {
- strncpy(host, "::", hostlen);
- snprintf(serv, servlen, "%d", sa6->sin6_port);
- return 0;
- }
- }
- return getnameinfo(sa, salen, host, hostlen, serv, servlen, flags);
- }
- #undef getnameinfo
- #define getnameinfo(sa, salen, host, hostlen, serv, servlen, flags) \
- ruby_getnameinfo__aix((sa), (salen), (host), (hostlen), (serv), (servlen), (flags))
- #ifndef CMSG_SPACE
- # define CMSG_SPACE(len) (_CMSG_ALIGN(sizeof(struct cmsghdr)) + _CMSG_ALIGN(len))
- #endif
- #ifndef CMSG_LEN
- # define CMSG_LEN(len) (_CMSG_ALIGN(sizeof(struct cmsghdr)) + (len))
- #endif
- #endif
- #ifdef __BEOS__
- #undef close
- #define close closesocket
- #endif
- #define MakeOpenFile(obj, fp) \
- do { \
- fp = ALLOC(rb_io_t); \
- GC_WB(&RFILE(obj)->fptr, fp); \
- fp->fd = fp->read_fd = fp->write_fd = -1; \
- fp->pid = -1; \
- } \
- while (0)
- static int
- constant_arg(VALUE arg, int (*str_to_int)(char*, int, int*), const char *errmsg)
- {
- VALUE tmp;
- char *ptr;
- int ret;
- if (SYMBOL_P(arg)) {
- arg = rb_sym_to_s(arg);
- goto str;
- }
- else if (!NIL_P(tmp = rb_check_string_type(arg))) {
- arg = tmp;
- str:
- rb_check_safe_obj(arg);
- ptr = RSTRING_PTR(arg);
- if (str_to_int(ptr, RSTRING_LEN(arg), &ret) == -1)
- rb_raise(rb_eSocket, "%s: %s", errmsg, ptr);
- }
- else {
- ret = NUM2INT(arg);
- }
- return ret;
- }
- static int
- family_arg(VALUE domain)
- {
- /* convert AF_INET, etc. */
- return constant_arg(domain, family_to_int, "unknown socket domain");
- }
- static int
- socktype_arg(VALUE type)
- {
- /* convert SOCK_STREAM, etc. */
- return constant_arg(type, socktype_to_int, "unknown socket type");
- }
- static int
- level_arg(VALUE level)
- {
- /* convert SOL_SOCKET, IPPROTO_TCP, etc. */
- return constant_arg(level, level_to_int, "unknown protocol level");
- }
- static int
- optname_arg(int level, VALUE optname)
- {
- switch (level) {
- case SOL_SOCKET:
- return constant_arg(optname, so_optname_to_int, "unknown socket level option name");
- case IPPROTO_IP:
- return constant_arg(optname, ip_optname_to_int, "unknown IP level option name");
- #ifdef IPPROTO_IPV6
- case IPPROTO_IPV6:
- return constant_arg(optname, ipv6_optname_to_int, "unknown IPv6 level option name");
- #endif
- case IPPROTO_TCP:
- return constant_arg(optname, tcp_optname_to_int, "unknown TCP level option name");
- case IPPROTO_UDP:
- return constant_arg(optname, udp_optname_to_int, "unknown UDP level option name");
- default:
- return NUM2INT(optname);
- }
- }
- static int
- shutdown_how_arg(VALUE how)
- {
- /* convert SHUT_RD, SHUT_WR, SHUT_RDWR. */
- return constant_arg(how, shutdown_how_to_int, "unknown shutdown argument");
- }
- static VALUE
- init_sock(VALUE sock, int fd)
- {
- rb_io_t *fp;
- #ifdef S_ISSOCK
- struct stat sbuf;
- if (fstat(fd, &sbuf) < 0) {
- rb_sys_fail(0);
- }
- if (!S_ISSOCK(sbuf.st_mode)) {
- rb_raise(rb_eArgError, "not a socket file descriptor");
- }
- #endif
- MakeOpenFile(sock, fp);
- fp->fd = fp->read_fd = fp->write_fd = fd;
- fp->mode = FMODE_READWRITE|FMODE_DUPLEX;
- rb_io_ascii8bit_binmode(sock);
- if (do_not_reverse_lookup) {
- fp->mode |= FMODE_NOREVLOOKUP;
- }
- fp->mode |= FMODE_SYNC;
- return sock;
- }
- /*
- * call-seq:
- * BasicSocket.for_fd(fd) => basicsocket
- *
- * Returns a socket object which contains the file descriptor, _fd_.
- *
- * # If invoked by inetd, STDIN/STDOUT/STDERR is a socket.
- * STDIN_SOCK = Socket.for_fd(STDIN.fileno)
- * p STDIN_SOCK.remote_address
- *
- */
- static VALUE
- bsock_s_for_fd(VALUE klass, SEL sel, VALUE fd)
- {
- rb_io_t *fptr;
- VALUE sock = init_sock(rb_obj_alloc(klass), NUM2INT(fd));
- GetOpenFile(sock, fptr);
- return sock;
- }
- /*
- * call-seq:
- * basicsocket.shutdown([how]) => 0
- *
- * Calls shutdown(2) system call.
- *
- * s.shutdown(Socket::SHUT_RD) disallows further read.
- *
- * s.shutdown(Socket::SHUT_WR) disallows further write.
- *
- * s.shutdown(Socket::SHUT_RDWR) disallows further read and write.
- *
- * _how_ can be symbol or string:
- * - :RD, :SHUT_RD, "RD" and "SHUT_RD" are accepted as Socket::SHUT_RD.
- * - :WR, :SHUT_WR, "WR" and "SHUT_WR" are accepted as Socket::SHUT_WR.
- * - :RDWR, :SHUT_RDWR, "RDWR" and "SHUT_RDWR" are accepted as Socket::SHUT_RDWR.
- *
- * UNIXSocket.pair {|s1, s2|
- * s1.puts "ping"
- * s1.shutdown(:WR)
- * p s2.read #=> "ping\n"
- * s2.puts "pong"
- * s2.close
- * p s1.read #=> "pong\n"
- * }
- *
- */
- static VALUE
- bsock_shutdown(VALUE sock, SEL sel, int argc, VALUE *argv)
- {
- VALUE howto;
- int how;
- rb_io_t *fptr;
- if (rb_safe_level() >= 4 && !OBJ_TAINTED(sock)) {
- rb_raise(rb_eSecurityError, "Insecure: can't shutdown socket");
- }
- rb_scan_args(argc, argv, "01", &howto);
- if (howto == Qnil)
- how = SHUT_RDWR;
- else {
- how = shutdown_how_arg(howto);
- if (how != SHUT_WR && how != SHUT_RD && how != SHUT_RDWR) {
- rb_raise(rb_eArgError, "`how' should be either :SHUT_RD, :SHUT_WR, :SHUT_RDWR");
- }
- }
- GetOpenFile(sock, fptr);
- if (shutdown(fptr->fd, how) == -1)
- rb_sys_fail(0);
- return INT2FIX(0);
- }
- /*
- * call-seq:
- * basicsocket.close_read => nil
- *
- * Disallows further read using shutdown system call.
- *
- * s1, s2 = UNIXSocket.pair
- * s1.close_read
- * s2.puts #=> Broken pipe (Errno::EPIPE)
- */
- static VALUE
- bsock_close_read(VALUE sock, SEL sel)
- {
- rb_io_t *fptr;
- if (rb_safe_level() >= 4 && !OBJ_TAINTED(sock)) {
- rb_raise(rb_eSecurityError, "Insecure: can't close socket");
- }
- GetOpenFile(sock, fptr);
- shutdown(fptr->fd, 0);
- if (!(fptr->mode & FMODE_WRITABLE)) {
- return rb_io_close(sock);
- }
- fptr->mode &= ~FMODE_READABLE;
- fptr->read_fd = -1;
- return Qnil;
- }
- /*
- * call-seq:
- * basicsocket.close_write => nil
- *
- * Disallows further write using shutdown system call.
- *
- * UNIXSocket.pair {|s1, s2|
- * s1.print "ping"
- * s1.close_write
- * p s2.read #=> "ping"
- * s2.print "pong"
- * s2.close
- * p s1.read #=> "pong"
- * }
- */
- static VALUE
- bsock_close_write(VALUE sock, SEL sel)
- {
- rb_io_t *fptr;
- if (rb_safe_level() >= 4 && !OBJ_TAINTED(sock)) {
- rb_raise(rb_eSecurityError, "Insecure: can't close socket");
- }
- GetOpenFile(sock, fptr);
- if (!(fptr->mode & FMODE_READABLE)) {
- return rb_io_close(sock);
- }
- shutdown(fptr->fd, 1);
- fptr->mode &= ~FMODE_WRITABLE;
- fptr->write_fd = -1;
- return Qnil;
- }
- /*
- * Document-method: setsockopt
- * call-seq:
- * setsockopt(level, optname, optval)
- * setsockopt(socketoption)
- *
- * Sets a socket option. These are protocol and system specific, see your
- * local system documentation for details.
- *
- * === Parameters
- * * +level+ is an integer, usually one of the SOL_ constants such as
- * Socket::SOL_SOCKET, or a protocol level.
- * A string or symbol of the name, possibly without prefix, is also
- * accepted.
- * * +optname+ is an integer, usually one of the SO_ constants, such
- * as Socket::SO_REUSEADDR.
- * A string or symbol of the name, possibly without prefix, is also
- * accepted.
- * * +optval+ is the value of the option, it is passed to the underlying
- * setsockopt() as a pointer to a certain number of bytes. How this is
- * done depends on the type:
- * - Fixnum: value is assigned to an int, and a pointer to the int is
- * passed, with length of sizeof(int).
- * - true or false: 1 or 0 (respectively) is assigned to an int, and the
- * int is passed as for a Fixnum. Note that +false+ must be passed,
- * not +nil+.
- * - String: the string's data and length is passed to the socket.
- * * +socketoption+ is an instance of Socket::Option
- *
- * === Examples
- *
- * Some socket options are integers with boolean values, in this case
- * #setsockopt could be called like this:
- * sock.setsockopt(:SOCKET, :REUSEADDR, true)
- * sock.setsockopt(Socket::SOL_SOCKET,Socket::SO_REUSEADDR, true)
- * sock.setsockopt(Socket::Option.bool(:INET, :SOCKET, :REUSEADDR, true))
- *
- * Some socket options are integers with numeric values, in this case
- * #setsockopt could be called like this:
- * sock.setsockopt(:IP, :TTL, 255)
- * sock.setsockopt(Socket::IPPROTO_IP, Socket::IP_TTL, 255)
- * sock.setsockopt(Socket::Option.int(:INET, :IP, :TTL, 255))
- *
- * Option values may be structs. Passing them can be complex as it involves
- * examining your system headers to determine the correct definition. An
- * example is an +ip_mreq+, which may be defined in your system headers as:
- * struct ip_mreq {
- * struct in_addr imr_multiaddr;
- * struct in_addr imr_interface;
- * };
- *
- * In this case #setsockopt could be called like this:
- * optval = IPAddr.new("224.0.0.251").hton +
- * IPAddr.new(Socket::INADDR_ANY, Socket::AF_INET).hton
- * sock.setsockopt(Socket::IPPROTO_IP, Socket::IP_ADD_MEMBERSHIP, optval)
- *
- */
- static VALUE
- bsock_setsockopt(VALUE sock, SEL sel, VALUE lev, VALUE optname, VALUE val)
- {
- int level, option;
- rb_io_t *fptr;
- int i;
- const char *v;
- int vlen;
- rb_secure(2);
- level = level_arg(lev);
- option = optname_arg(level, optname);
- switch (TYPE(val)) {
- case T_FIXNUM:
- i = FIX2INT(val);
- goto numval;
- case T_FALSE:
- i = 0;
- goto numval;
- case T_TRUE:
- i = 1;
- numval:
- v = (char*)&i; vlen = (int)sizeof(i);
- break;
- default:
- StringValue(val);
- v = RSTRING_PTR(val);
- vlen = RSTRING_LENINT(val);
- break;
- }
- #define rb_sys_fail_path(path) rb_sys_fail(path == 0 ? NULL : RSTRING_PTR(path));
- GetOpenFile(sock, fptr);
- if (setsockopt(fptr->fd, level, option, v, vlen) < 0)
- rb_sys_fail_path(fptr->path);
- return INT2FIX(0);
- }
- /*
- * Document-method: getsockopt
- * call-seq:
- * getsockopt(level, optname) => socketoption
- *
- * Gets a socket option. These are protocol and system specific, see your
- * local system documentation for details. The option is returned as
- * a Socket::Option object.
- *
- * === Parameters
- * * +level+ is an integer, usually one of the SOL_ constants such as
- * Socket::SOL_SOCKET, or a protocol level.
- * A string or symbol of the name, possibly without prefix, is also
- * accepted.
- * * +optname+ is an integer, usually one of the SO_ constants, such
- * as Socket::SO_REUSEADDR.
- * A string or symbol of the name, possibly without prefix, is also
- * accepted.
- *
- * === Examples
- *
- * Some socket options are integers with boolean values, in this case
- * #getsockopt could be called like this:
- *
- * reuseaddr = sock.getsockopt(:SOCKET, :REUSEADDR).bool
- *
- * optval = sock.getsockopt(Socket::SOL_SOCKET,Socket::SO_REUSEADDR)
- * optval = optval.unpack "i"
- * reuseaddr = optval[0] == 0 ? false : true
- *
- * Some socket options are integers with numeric values, in this case
- * #getsockopt could be called like this:
- *
- * ipttl = sock.getsockopt(:IP, :TTL).int
- *
- * optval = sock.getsockopt(Socket::IPPROTO_IP, Socket::IP_TTL)
- * ipttl = optval.unpack("i")[0]
- *
- * Option values may be structs. Decoding them can be complex as it involves
- * examining your system headers to determine the correct definition. An
- * example is a +struct linger+, which may be defined in your system headers
- * as:
- * struct linger {
- * int l_onoff;
- * int l_linger;
- * };
- *
- * In this case #getsockopt could be called like this:
- *
- * # Socket::Option knows linger structure.
- * onoff, linger = sock.getsockopt(:SOCKET, :LINGER).linger
- *
- * optval = sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_LINGER)
- * onoff, linger = optval.unpack "ii"
- * onoff = onoff == 0 ? false : true
- */
- static VALUE
- bsock_getsockopt(VALUE sock, SEL sel, VALUE lev, VALUE optname)
- {
- #if !defined(__BEOS__)
- int level, option;
- socklen_t len;
- char *buf;
- rb_io_t *fptr;
- level = level_arg(lev);
- option = optname_arg(level, optname);
- len = 256;
- buf = ALLOCA_N(char,len);
- GetOpenFile(sock, fptr);
- if (getsockopt(fptr->fd, level, option, buf, &len) < 0)
- rb_sys_fail_path(fptr->path);
- return rb_str_new(buf, len);
- #else
- rb_notimplement();
- #endif
- }
- /*
- * call-seq:
- * basicsocket.getsockname => sockaddr
- *
- * Returns the local address of the socket as a sockaddr string.
- *
- * TCPServer.open("127.0.0.1", 15120) {|serv|
- * p serv.getsockname #=> "\x02\x00;\x10\x7F\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00"
- * }
- *
- * If Addrinfo object is preferred over the binary string,
- * use BasicSocket#local_address.
- */
- static VALUE
- bsock_getsockname(VALUE sock, SEL sel)
- {
- struct sockaddr_storage buf;
- socklen_t len = (socklen_t)sizeof buf;
- rb_io_t *fptr;
- GetOpenFile(sock, fptr);
- if (getsockname(fptr->fd, (struct sockaddr*)&buf, &len) < 0)
- rb_sys_fail("getsockname(2)");
- return rb_str_new((char*)&buf, len);
- }
- /*
- * call-seq:
- * basicsocket.getpeername => sockaddr
- *
- * Returns the remote address of the socket as a sockaddr string.
- *
- * TCPServer.open("127.0.0.1", 1440) {|serv|
- * c = TCPSocket.new("127.0.0.1", 1440)
- * s = serv.accept
- * p s.getpeername #=> "\x02\x00\x82u\x7F\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00"
- * }
- *
- * If Addrinfo object is preferred over the binary string,
- * use BasicSocket#remote_address.
- *
- */
- static VALUE
- bsock_getpeername(VALUE sock, SEL sel)
- {
- struct sockaddr_storage buf;
- socklen_t len = (socklen_t)sizeof buf;
- rb_io_t *fptr;
- GetOpenFile(sock, fptr);
- if (getpeername(fptr->fd, (struct sockaddr*)&buf, &len) < 0)
- rb_sys_fail("getpeername(2)");
- return rb_str_new((char*)&buf, len);
- }
- #if defined(HAVE_GETPEEREID) || defined(SO_PEERCRED) || defined(HAVE_GETPEERUCRED)
- /*
- * call-seq:
- * basicsocket.getpeereid => [euid, egid]
- *
- * Returns the user and group on the peer of the UNIX socket.
- * The result is a two element array which contains the effective uid and the effective gid.
- *
- * Socket.unix_server_loop("/tmp/sock") {|s|
- * begin
- * euid, egid = s.getpeereid
- *
- * # Check the connected client is myself or not.
- * next if euid != Process.uid
- *
- * # do something about my resource.
- *
- * ensure
- * s.close
- * end
- * }
- *
- */
- static VALUE
- bsock_getpeereid(VALUE self, SEL sel)
- {
- #if defined(HAVE_GETPEEREID)
- rb_io_t *fptr;
- uid_t euid;
- gid_t egid;
- GetOpenFile(self, fptr);
- if (getpeereid(fptr->fd, &euid, &egid) == -1)
- rb_sys_fail("getpeereid");
- return rb_assoc_new(UIDT2NUM(euid), GIDT2NUM(egid));
- #elif defined(SO_PEERCRED) /* GNU/Linux */
- rb_io_t *fptr;
- struct ucred cred;
- socklen_t len = sizeof(cred);
- GetOpenFile(self, fptr);
- if (getsockopt(fptr->fd, SOL_SOCKET, SO_PEERCRED, &cred, &len) == -1)
- rb_sys_fail("getsockopt(SO_PEERCRED)");
- return rb_assoc_new(UIDT2NUM(cred.uid), GIDT2NUM(cred.gid));
- #elif defined(HAVE_GETPEERUCRED) /* Solaris */
- rb_io_t *fptr;
- ucred_t *uc = NULL;
- VALUE ret;
- GetOpenFile(self, fptr);
- if (getpeerucred(fptr->fd, &uc) == -1)
- rb_sys_fail("getpeerucred");
- ret = rb_assoc_new(UIDT2NUM(ucred_geteuid(uc)), GIDT2NUM(ucred_getegid(uc)));
- ucred_free(uc);
- return ret;
- #endif
- }
- #else
- #define bsock_getpeereid rb_f_notimplement
- #endif
- static VALUE addrinfo_new(struct sockaddr *addr, socklen_t len, int family, int socktype, int protocol, VALUE canonname, VALUE inspectname);
- static VALUE fd_socket_addrinfo(int fd, struct sockaddr *addr, socklen_t len);
- static VALUE io_socket_addrinfo(VALUE io, struct sockaddr *addr, socklen_t len);
- /*
- * call-seq:
- * bsock.local_address => addrinfo
- *
- * Returns an Addrinfo object for local address obtained by getsockname.
- *
- * Note that addrinfo.protocol is filled by 0.
- *
- * TCPSocket.open("www.ruby-lang.org", 80) {|s|
- * p s.local_address #=> #<Addrinfo: 192.168.0.129:36873 TCP>
- * }
- *
- * TCPServer.open("127.0.0.1", 1512) {|serv|
- * p serv.local_address #=> #<Addrinfo: 127.0.0.1:1512 TCP>
- * }
- *
- */
- static VALUE
- bsock_local_address(VALUE sock, SEL sel)
- {
- struct sockaddr_storage buf;
- socklen_t len = (socklen_t)sizeof buf;
- rb_io_t *fptr;
- GetOpenFile(sock, fptr);
- if (getsockname(fptr->fd, (struct sockaddr*)&buf, &len) < 0)
- rb_sys_fail("getsockname(2)");
- return fd_socket_addrinfo(fptr->fd, (struct sockaddr *)&buf, len);
- }
- /*
- * call-seq:
- * bsock.remote_address => addrinfo
- *
- * Returns an Addrinfo object for remote address obtained by getpeername.
- *
- * Note that addrinfo.protocol is filled by 0.
- *
- * TCPSocket.open("www.ruby-lang.org", 80) {|s|
- * p s.remote_address #=> #<Addrinfo: 221.186.184.68:80 TCP>
- * }
- *
- * TCPServer.open("127.0.0.1", 1728) {|serv|
- * c = TCPSocket.new("127.0.0.1", 1728)
- * s = serv.accept
- * p s.remote_address #=> #<Addrinfo: 127.0.0.1:36504 TCP>
- * }
- *
- */
- static VALUE
- bsock_remote_address(VALUE sock, SEL sel)
- {
- struct sockaddr_storage buf;
- socklen_t len = (socklen_t)sizeof buf;
- rb_io_t *fptr;
- GetOpenFile(sock, fptr);
- if (getpeername(fptr->fd, (struct sockaddr*)&buf, &len) < 0)
- rb_sys_fail("getpeername(2)");
- return fd_socket_addrinfo(fptr->fd, (struct sockaddr *)&buf, len);
- }
- #define SockAddrStringValue(v) sockaddr_string_value(&(v))
- #define SockAddrStringValuePtr(v) sockaddr_string_value_ptr(&(v))
- static VALUE sockaddr_string_value(volatile VALUE *);
- static char *sockaddr_string_value_ptr(volatile VALUE *);
- /*
- * call-seq:
- * basicsocket.send(mesg, flags [, dest_sockaddr]) => numbytes_sent
- *
- * send _mesg_ via _basicsocket_.
- *
- * _mesg_ should be a string.
- *
- * _flags_ should be a bitwise OR of Socket::MSG_* constants.
- *
- * _dest_sockaddr_ should be a packed sockaddr string or an addrinfo.
- *
- * TCPSocket.open("localhost", 80) {|s|
- * s.send "GET / HTTP/1.0\r\n\r\n", 0
- * p s.read
- * }
- */
- static VALUE
- bsock_send(VALUE sock, SEL sel, int argc, VALUE *argv)
- {
- VALUE mesg, to;
- VALUE flags;
- rb_io_t *fptr;
- int fd, n;
- rb_secure(4);
- rb_scan_args(argc, argv, "21", &mesg, &flags, &to);
- StringValue(mesg);
- if (!NIL_P(to)) SockAddrStringValue(to);
- GetOpenFile(sock, fptr);
- fd = fptr->fd;
- retry:
- rb_thread_fd_writable(fd);
- if (!NIL_P(to)) {
- n = sendto(fd, RSTRING_PTR(mesg), RSTRING_LEN(mesg), NUM2INT(flags),
- (struct sockaddr*)RSTRING_PTR(to), RSTRING_LEN(to));
- }
- else {
- n = send(fd, RSTRING_PTR(mesg), RSTRING_LEN(mesg), NUM2INT(flags));
- }
- if (n < 0) {
- if (rb_io_wait_writable(fd)) {
- goto retry;
- }
- rb_sys_fail("send(2)");
- }
- return INT2FIX(n);
- }
- /*
- * call-seq:
- * basicsocket.do_not_reverse_lookup => true or false
- *
- * Gets the do_not_reverse_lookup flag of _basicsocket_.
- *
- * TCPSocket.open("www.ruby-lang.org", 80) {|sock|
- * p sock.do_not_reverse_lookup #=> false
- * p sock.peeraddr #=> ["AF_INET", 80, "carbon.ruby-lang.org", "221.186.184.68"]
- * sock.do_not_reverse_lookup = true
- * p sock.peeraddr #=> ["AF_INET", 80, "221.186.184.68", "221.186.184.68"]
- * }
- */
- static VALUE
- bsock_do_not_reverse_lookup(VALUE sock, SEL sel)
- {
- rb_io_t *fptr;
- GetOpenFile(sock, fptr);
- return (fptr->mode & FMODE_NOREVLOOKUP) ? Qtrue : Qfalse;
- }
- /*
- * call-seq:
- * basicsocket.do_not_reverse_lookup = bool
- *
- * Sets the do_not_reverse_lookup flag of _basicsocket_.
- *
- * BasicSocket.do_not_reverse_lookup = false
- * p TCPSocket.new("127.0.0.1", 80).do_not_reverse_lookup #=> false
- * BasicSocket.do_not_reverse_lookup = true
- * p TCPSocket.new("127.0.0.1", 80).do_not_reverse_lookup #=> true
- *
- */
- static VALUE
- bsock_do_not_reverse_lookup_set(VALUE sock, SEL sel, VALUE state)
- {
- rb_io_t *fptr;
- rb_secure(4);
- GetOpenFile(sock, fptr);
- if (RTEST(state)) {
- fptr->mode |= FMODE_NOREVLOOKUP;
- }
- else {
- fptr->mode &= ~FMODE_NOREVLOOKUP;
- }
- return sock;
- }
- static VALUE ipaddr(struct sockaddr*, int);
- #ifdef HAVE_SYS_UN_H
- static VALUE unixaddr(struct sockaddr_un*, socklen_t);
- #endif
- enum sock_recv_type {
- RECV_RECV, /* BasicSocket#recv(no from) */
- RECV_IP, /* IPSocket#recvfrom */
- RECV_UNIX, /* UNIXSocket#recvfrom */
- RECV_SOCKET /* Socket#recvfrom */
- };
- static VALUE
- s_recvfrom(VALUE sock, int argc, VALUE *argv, enum sock_recv_type from)
- {
- rb_io_t *fptr;
- VALUE str;
- struct sockaddr_storage buf;
- socklen_t alen = (socklen_t)sizeof buf;
- VALUE len, flg;
- long buflen;
- long slen;
- int fd, flags;
- rb_scan_args(argc, argv, "11", &len, &flg);
- if (flg == Qnil) flags = 0;
- else flags = NUM2INT(flg);
- buflen = NUM2INT(len);
- GetOpenFile(sock, fptr);
- if (rb_io_read_pending(fptr)) {
- rb_raise(rb_eIOError, "recv for buffered IO");
- }
- fd = fptr->fd;
- str = rb_bstr_new();
- rb_bstr_resize(str, buflen);
- retry:
- rb_thread_wait_fd(fd);
- rb_io_check_closed(fptr);
- if (rb_bstr_length(str) != buflen) {
- rb_raise(rb_eRuntimeError, "buffer string modified");
- }
- slen = recvfrom(fd, rb_bstr_bytes(str), buflen, flags,
- (struct sockaddr *)&buf, &alen);
- if (slen < 0) {
- if (rb_io_wait_readable(fd)) {
- goto retry;
- }
- rb_sys_fail("recvfrom(2)");
- }
- if (slen < rb_bstr_length(str)) {
- rb_bstr_resize(str, slen);
- }
- rb_obj_taint(str);
- switch (from) {
- case RECV_RECV:
- return (VALUE)str;
- case RECV_IP:
- #if 0
- if (alen != sizeof(struct sockaddr_in)) {
- rb_raise(rb_eTypeError, "sockaddr size differs - should not happen");
- }
- #endif
- if (alen && alen != sizeof(buf)) /* OSX doesn't return a from result for connection-oriented sockets */
- return rb_assoc_new(str, ipaddr((struct sockaddr*)&buf, fptr->mode & FMODE_NOREVLOOKUP));
- else
- return rb_assoc_new(str, Qnil);
- #ifdef HAVE_SYS_UN_H
- case RECV_UNIX:
- return rb_assoc_new(str, unixaddr((struct sockaddr_un*)&buf, alen));
- #endif
- case RECV_SOCKET:
- return rb_assoc_new(str, io_socket_addrinfo(sock, (struct sockaddr*)&buf, alen));
- default:
- rb_bug("s_recvfrom called with bad value");
- }
- }
- static VALUE
- s_recvfrom_nonblock(VALUE sock, int argc, VALUE *argv, enum sock_recv_type from)
- {
- rb_io_t *fptr;
- VALUE str;
- struct sockaddr_storage buf;
- socklen_t alen = (socklen_t)sizeof buf;
- VALUE len, flg;
- long buflen;
- long slen;
- int fd, flags;
- VALUE addr = Qnil;
- rb_scan_args(argc, argv, "11", &len, &flg);
- if (flg == Qnil) flags = 0;
- else flags = NUM2INT(flg);
- buflen = NUM2INT(len);
- #ifdef MSG_DONTWAIT
- /* MSG_DONTWAIT avoids the race condition between fcntl and recvfrom.
- It is not portable, though. */
- flags |= MSG_DONTWAIT;
- #endif
- GetOpenFile(sock, fptr);
- if (rb_io_read_pending(fptr)) {
- rb_raise(rb_eIOError, "recvfrom for buffered IO");
- }
- fd = fptr->fd;
- str = rb_bstr_new();
- rb_bstr_resize(str, buflen);
- rb_io_check_closed(fptr);
- rb_io_set_nonblock(fptr);
- slen = recvfrom(fd, rb_bstr_bytes(str), buflen, flags,
- (struct sockaddr *)&buf, &alen);
- if (slen < 0) {
- switch (errno) {
- case EAGAIN:
- #if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
- case EWOULDBLOCK:
- #endif
- rb_mod_sys_fail(rb_mWaitReadable, "recvfrom(2) would block");
- }
- rb_sys_fail("recvfrom(2)");
- }
- if (slen < rb_bstr_length(str)) {
- rb_bstr_resize(str, slen);
- }
- rb_obj_taint(str);
- switch (from) {
- case RECV_RECV:
- return str;
- case RECV_IP:
- if (alen && alen != sizeof(buf)) /* connection-oriented socket may not return a from result */
- addr = ipaddr((struct sockaddr*)&buf, fptr->mode & FMODE_NOREVLOOKUP);
- break;
- case RECV_SOCKET:
- addr = io_socket_addrinfo(sock, (struct sockaddr*)&buf, alen);
- break;
- default:
- rb_bug("s_recvfrom_nonblock called with bad value");
- }
- return rb_assoc_new(str, addr);
- }
- /*
- * call-seq:
- * basicsocket.recv(maxlen) => mesg
- * basicsocket.recv(maxlen, flags) => mesg
- *
- * Receives a message.
- *
- * _maxlen_ is the maximum number of bytes to receive.
- *
- * _flags_ should be a bitwise OR of Socket::MSG_* constants.
- *
- * UNIXSocket.pair {|s1, s2|
- * s1.puts "Hello World"
- * p s2.recv(4) #=> "Hell"
- * p s2.recv(4, Socket::MSG_PEEK) #=> "o Wo"
- * p s2.recv(4) #=> "o Wo"
- * p s2.recv(10) #=> "rld\n"
- * }
- */
- static VALUE
- bsock_recv(VALUE sock, SEL sel, int argc, VALUE *argv)
- {
- return s_recvfrom(sock, argc, argv, RECV_RECV);
- }
- /*
- * call-seq:
- * basicsocket.recv_nonblock(maxlen) => mesg
- * basicsocket.recv_nonblock(maxlen, flags) => mesg
- *
- * Receives up to _maxlen_ bytes from +socket+ using recvfrom(2) after
- * O_NONBLOCK is set for the underlying file descriptor.
- * _flags_ is zero or more of the +MSG_+ options.
- * The result, _mesg_, is the data received.
- *
- * When recvfrom(2) returns 0, Socket#recv_nonblock returns
- * an empty string as data.
- * The meaning depends on the socket: EOF on TCP, empty packet on UDP, etc.
- *
- * === Parameters
- * * +maxlen+ - the number of bytes to receive from the socket
- * * +flags+ - zero or more of the +MSG_+ options
- *
- * === Example
- * serv = TCPServer.new("127.0.0.1", 0)
- * af, port, host, addr = serv.addr
- * c = TCPSocket.new(addr, port)
- * s = serv.accept
- * c.send "aaa", 0
- * begin # emulate blocking recv.
- * p s.recv_nonblock(10) #=> "aaa"
- * rescue IO::WaitReadable
- * IO.select([s])
- * retry
- * end
- *
- * Refer to Socket#recvfrom for the exceptions that may be thrown if the call
- * to _recv_nonblock_ fails.
- *
- * BasicSocket#recv_nonblock may raise any error corresponding to recvfrom(2) failure,
- * including Errno::EWOULDBLOCK.
- *
- * If the exception is Errno::EWOULDBLOCK or Errno::AGAIN,
- * it is extended by IO::WaitReadable.
- * So IO::WaitReadable can be used to rescue the exceptions for retrying recv_nonblock.
- *
- * === See
- * * Socket#recvfrom
- */
- static VALUE
- bsock_recv_nonblock(VALUE sock, SEL sel, int argc, VALUE *argv)
- {
- return s_recvfrom_nonblock(sock, argc, argv, RECV_RECV);
- }
- /*
- * call-seq:
- * BasicSocket.do_not_reverse_lookup => true or false
- *
- * Gets the global do_not_reverse_lookup flag.
- *
- * BasicSocket.do_not_reverse_lookup #=> false
- */
- static VALUE
- bsock_do_not_rev_lookup(VALUE self, SEL sel)
- {
- return do_not_reverse_lookup ? Qtrue : Qfalse;
- }
- /*
- * call-seq:
- * BasicSocket.do_not_reverse_lookup = bool
- *
- * Sets the global do_not_reverse_lookup flag.
- *
- * The flag is used for initial value of do_not_reverse_lookup for each socket.
- *
- * s1 = TCPSocket.new("localhost", 80)
- * p s1.do_not_reverse_lookup #=> true
- * BasicSocket.do_not_reverse_lookup = false
- * s2 = TCPSocket.new("localhost", 80)
- * p s2.do_not_reverse_lookup #=> false
- * p s1.do_not_reverse_lookup #=> true
- *
- */
- static VALUE
- bsock_do_not_rev_lookup_set(VALUE self, SEL sel, VALUE val)
- {
- rb_secure(4);
- do_not_reverse_lookup = RTEST(val);
- return val;
- }
- NORETURN(static void raise_socket_error(char *, int));
- static void
- raise_socket_error(char *reason, int error)
- {
- #ifdef EAI_SYSTEM
- if (error == EAI_SYSTEM) rb_sys_fail(reason);
- #endif
- rb_raise(rb_eSocket, "%s: %s", reason, gai_strerror(error));
- }
- static void
- make_ipaddr0(struct sockaddr *addr, char *buf, size_t len)
- {
- int error;
- error = getnameinfo(addr, SA_LEN(addr), buf, len, NULL, 0, NI_NUMERICHOST);
- if (error) {
- raise_socket_error("getnameinfo", error);
- }
- }
- static VALUE
- make_ipaddr(struct sockaddr *addr)
- {
- char hbuf[1024];
- make_ipaddr0(addr, hbuf, sizeof(hbuf));
- return rb_str_new2(hbuf);
- }
- static void
- make_inetaddr(long host, char *buf, size_t len)
- {
- struct sockaddr_in sin;
- MEMZERO(&sin, struct sockaddr_in, 1);
- sin.sin_family = AF_INET;
- SET_SIN_LEN(&sin, sizeof(sin));
- sin.sin_addr.s_addr = host;
- make_ipaddr0((struct sockaddr*)&sin, buf, len);
- }
- static int
- str_is_number(const char *p)
- {
- char *ep;
- if (!p || *p == '\0')
- return 0;
- ep = NULL;
- (void)STRTOUL(p, &ep, 10);
- if (ep && *ep == '\0')
- return 1;
- else
- return 0;
- }
- static char*
- host_str(VALUE host, char *hbuf, size_t len, int *flags_ptr)
- {
- if (NIL_P(host)) {
- return NULL;
- }
- else if (rb_obj_is_kind_of(host, rb_cInteger)) {
- unsigned int i = NUM2UINT(host);
- make_inetaddr(htonl(i), hbuf, len);
- if (flags_ptr) *flags_ptr |= AI_NUMERICHOST;
- return hbuf;
- }
- else {
- const char *name;
- SafeStringValue(host);
- name = RSTRING_PTR(host);
- if (!name || *name == 0 || (name[0] == '<' && strcmp(name, "<any>") == 0)) {
- make_inetaddr(INADDR_ANY, hbuf, len);
- if (flags_ptr) *flags_ptr |= AI_NUMERICHOST;
- }
- else if (name[0] == '<' && strcmp(name, "<broadcast>") == 0) {
- make_inetaddr(INADDR_BROADCAST, hbuf, len);
- if (flags_ptr) *flags_ptr |= AI_NUMERICHOST;
- }
- else if (strlen(name) >= len) {
- rb_raise(rb_eArgError, "hostname too long (%ld)", strlen(name));
- }
- else {
- strcpy(hbuf, name);
- }
- return hbuf;
- }
- }
- static char*
- port_str(VALUE port, char *pbuf, size_t len, int *flags_ptr)
- {
- if (NIL_P(port)) {
- return 0;
- }
- else if (FIXNUM_P(port)) {
- snprintf(pbuf, len, "%ld", FIX2LONG(port));
- #ifdef AI_NUMERICSERV
- if (flags_ptr) *flags_ptr |= AI_NUMERICSERV;
- #endif
- return pbuf;
- }
- else {
- const char *serv;
- SafeStringValue(port);
- serv = RSTRING_PTR(port);
- if (strlen(serv) >= len) {
- rb_raise(rb_eArgError, "service name too long (%ld)", strlen(serv));
- }
- strcpy(pbuf, serv);
- return pbuf;
- }
- }
- static struct addrinfo*
- sock_getaddrinfo(VALUE host, VALUE port, struct addrinfo *hints, int socktype_hack)
- {
- struct addrinfo* res = NULL;
- char *hostp, *portp;
- int error;
- char hbuf[NI_MAXHOST], pbuf[NI_MAXSERV];
- int additional_flags = 0;
- hostp = host_str(host, hbuf, sizeof(hbuf), &additional_flags);
- portp = port_str(port, pbuf, sizeof(pbuf), &additional_flags);
- if (socktype_hack && hints->ai_socktype == 0 && str_is_number(portp)) {
- hints->ai_socktype = SOCK_DGRAM;
- }
- hints->ai_flags |= additional_flags;
- error = getaddrinfo(hostp, portp, hints, &res);
- if (error) {
- if (hostp && hostp[strlen(hostp)-1] == '\n') {
- rb_raise(rb_eSocket, "newline at the end of hostname");
- }
- raise_socket_error("getaddrinfo", error);
- }
- #if defined(__APPLE__) && defined(__MACH__)
- {
- struct addrinfo *r;
- r = res;
- while (r) {
- if (! r->ai_socktype) r->ai_socktype = hints->ai_socktype;
- if (! r->ai_protocol) {
- if (r->ai_socktype == SOCK_DGRAM) {
- r->ai_protocol = IPPROTO_UDP;
- }
- else if (r->ai_socktype == SOCK_STREAM) {
- r->ai_protocol = IPPROTO_TCP;
- }
- }
- r = r->ai_next;
- }
- }
- #endif
- return res;
- }
- static struct addrinfo*
- sock_addrinfo(VALUE host, VALUE port, int socktype, int flags)
- {
- struct addrinfo hints;
- MEMZERO(&hints, struct addrinfo, 1);
- hints.ai_family = AF_UNSPEC;
- hints.ai_socktype = socktype;
- hints.ai_flags = flags;
- return sock_getaddrinfo(host, port, &hints, 1);
- }
- static VALUE
- ipaddr(struct sockaddr *sockaddr, int norevlookup)
- {
- VALUE family, port, addr1, addr2;
- VALUE ary;
- int error;
- char hbuf[1024], pbuf[1024];
- ID id;
- id = intern_family(sockaddr->sa_family);
- if (id) {
- family = rb_str_dup(rb_id2str(id));
- }
- else {
- sprintf(pbuf, "unknown:%d", sockaddr->sa_family);
- family = rb_str_new2(pbuf);
- }
- addr1 = Qnil;
- if (!norevlookup) {
- error = getnameinfo(sockaddr, SA_LEN(sockaddr), hbuf, sizeof(hbuf),
- NULL, 0, 0);
- if (! error) {
- addr1 = rb_str_new2(hbuf);
- }
- }
- error = getnameinfo(sockaddr, SA_LEN(sockaddr), hbuf, sizeof(hbuf),
- pbuf, sizeof(pbuf), NI_NUMERICHOST | NI_NUMERICSERV);
- if (error) {
- raise_socket_error("getnameinfo", error);
- }
- addr2 = rb_str_new2(hbuf);
- if (addr1 == Qnil) {
- addr1 = addr2;
- }
- port = INT2FIX(atoi(pbuf));
- ary = rb_ary_new3(4, family, port, addr1, addr2);
- return ary;
- }
- static int
- ruby_socket(int domain, int type, int proto)
- {
- int fd;
- fd = socket(domain, type, proto);
- if (fd < 0) {
- if (errno == EMFILE || errno == ENFILE) {
- rb_gc();
- fd = socket(domain, type, proto);
- }
- }
- return fd;
- }
- static int
- wait_connectable0(int fd, rb_fdset_t *fds_w, rb_fdset_t *fds_e)
- {
- int sockerr;
- socklen_t sockerrlen;
- for (;;) {
- rb_fd_zero(fds_w);
- rb_fd_zero(fds_e);
- rb_fd_set(fd, fds_w);
- rb_fd_set(fd, fds_e);
- rb_thread_select(fd+1, 0, rb_fd_ptr(fds_w), rb_fd_ptr(fds_e), 0);
- if (rb_fd_isset(fd, fds_w)) {
- return 0;
- }
- else if (rb_fd_isset(fd, fds_e)) {
- sockerrlen = (socklen_t)sizeof(sockerr);
- if (getsockopt(fd, SOL_SOCKET, SO_ERROR, (void *)&sockerr,
- &sockerrlen) == 0) {
- if (sockerr == 0)
- continue; /* workaround for winsock */
- errno = sockerr;
- }
- return -1;
- }
- }
- }
- struct wait_connectable_arg {
- int fd;
- rb_fdset_t *fds_w;
- rb_fdset_t *fds_e;
- };
- #ifdef HAVE_RB_FD_INIT
- static VALUE
- try_wait_connectable(VALUE arg)
- {
- struct wait_connectable_arg *p = (struct wait_connectable_arg *)arg;
- return (VALUE)wait_connectable0(p->fd, p->fds_w, p->fds_e);
- }
- static VALUE
- wait_connectable_ensure(VALUE arg)
- {
- struct wait_connectable_arg *p = (struct wait_connectable_arg *)arg;
- rb_fd_term(p->fds_w);
- rb_fd_term(p->fds_e);
- return Qnil;
- }
- #endif
- static int
- wait_connectable(int fd)
- {
- struct wait_connectable_arg *arg;
- arg = (void *)xmalloc(sizeof(struct wait_connectable_arg));
- GC_WB(&arg->fds_w, xmalloc(sizeof(rb_fdset_t)));
- GC_WB(&arg->fds_e, xmalloc(sizeof(rb_fdset_t)));
- rb_fd_init(arg->fds_w);
- rb_fd_init(arg->fds_e);
- #ifdef HAVE_RB_FD_INIT
- arg->fd = fd;
- return (int)rb_ensure(try_wait_connectable, (VALUE)arg,
- wait_connectable_ensure,(VALUE)arg);
- #else
- return wait_connectable0(fd, arg->fds_w, arg->fds_e);
- #endif
- }
- #ifdef __CYGWIN__
- #define WAIT_IN_PROGRESS 10
- #endif
- #ifdef __APPLE__
- #define WAIT_IN_PROGRESS 10
- #endif
- #ifdef __linux__
- /* returns correct error */
- #define WAIT_IN_PROGRESS 0
- #endif
- #ifndef WAIT_IN_PROGRESS
- /* BSD origin code apparently has a problem */
- #define WAIT_IN_PROGRESS 1
- #endif
- static int
- ruby_connect(int fd, struct sockaddr *sockaddr, int len, int socks)
- {
- int status;
- #if WAIT_IN_PROGRESS > 0
- int wait_in_progress = -1;
- int sockerr;
- socklen_t sockerrlen;
- #endif
- for (;;) {
- status = connect(fd, sockaddr, len);
- if (status < 0) {
- switch (errno) {
- case EINTR:
- #if defined(ERESTART)
- case ERESTART:
- #endif
- continue;
- case EAGAIN:
- #ifdef EINPROGRESS
- case EINPROGRESS:
- #endif
- #if WAIT_IN_PROGRESS > 0
- sockerrlen = (socklen_t)sizeof(sockerr);
- status = getsockopt(fd, SOL_SOCKET, SO_ERROR, (void *)&sockerr, &sockerrlen);
- if (status) break;
- if (sockerr) {
- status = -1;
- errno = sockerr;
- break;
- }
- #endif
- #ifdef EALREADY
- case EALREADY:
- #endif
- #if WAIT_IN_PROGRESS > 0
- wait_in_progress = WAIT_IN_PROGRESS;
- #endif
- status = wait_connectable(fd);
- if (status) {
- break;
- }
- errno = 0;
- continue;
- #if WAIT_IN_PROGRESS > 0
- case EINVAL:
- if (wait_in_progress-- > 0) {
- /*
- * connect() after EINPROGRESS returns EINVAL on
- * some platforms, need to check true error
- * status.
- */
- sockerrlen = (socklen_t)sizeof(sockerr);
- status = getsockopt(fd, SOL_SOCKET, SO_ERROR, (void *)&sockerr, &sockerrlen);
- if (!status && !sockerr) {
- struct timeval tv = {0, 100000};
- rb_thread_wait_for(tv);
- continue;
- }
- status = -1;
- errno = sockerr;
- }
- break;
- #endif
- #ifdef EISCONN
- case EISCONN:
- status = 0;
- errno = 0;
- break;
- #endif
- default:
- break;
- }
- }
- return status;
- }
- }
- struct inetsock_arg
- {
- VALUE sock;
- struct {
- VALUE host, serv;
- struct addrinfo *res;
- } remote, local;
- int type;
- int fd;
- };
- static VALUE
- inetsock_cleanup(struct inetsock_arg *arg)
- {
- if (arg->remote.res) {
- freeaddrinfo(arg->remote.res);
- arg->remote.res = 0;
- }
- if (arg->local.res) {
- freeaddrinfo(arg->local.res);
- arg->local.res = 0;
- }
- if (arg->fd >= 0) {
- close(arg->fd);
- }
- return Qnil;
- }
- static VALUE
- init_inetsock_internal(struct inetsock_arg *arg)
- {
- int type = arg->type;
- struct addrinfo *res;
- int fd, status = 0;
- char *syscall = NULL;
- arg->remote.res = sock_addrinfo(arg->remote.host, arg->remote.serv, SOCK_STREAM,
- (type == INET_SERVER) ? AI_PASSIVE : 0);
- /*
- * Maybe also accept a local address
- */
- if (type != INET_SERVER && (!NIL_P(arg->local.host) || !NIL_P(arg->local.serv))) {
- arg->local.res = sock_addrinfo(arg->local.host, arg->local.serv, SOCK_STREAM, 0);
- }
- arg->fd = fd = -1;
- for (res = arg->remote.res; res; res = res->ai_next) {
- #if !defined(INET6) && defined(AF_INET6)
- if (res->ai_family == AF_INET6)
- continue;
- #endif
- status = ruby_socket(res->ai_family,res->ai_socktype,res->ai_protocol);
- syscall = "socket(2)";
- fd = status;
- if (fd < 0) {
- continue;
- }
- arg->fd = fd;
- if (type == INET_SERVER) {
- #if !defined(_WIN32) && !defined(__CYGWIN__)
- status = 1;
- setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
- (char*)&status, sizeof(status));
- #endif
- status = bind(fd, res->ai_addr, res->ai_addrlen);
- syscall = "bind(2)";
- }
- else {
- if (arg->local.res) {
- status = bind(fd, arg->local.res->ai_addr, arg->local.res->ai_addrlen);
- syscall = "bind(2)";
- }
- if (status >= 0) {
- status = ruby_connect(fd, res->ai_addr, res->ai_addrlen,
- (type == INET_SOCKS));
- syscall = "connect(2)";
- }
- }
- if (status < 0) {
- close(fd);
- arg->fd = fd = -1;
- continue;
- } else
- break;
- }
- if (status < 0) {
- rb_sys_fail(syscall);
- }
- arg->fd = -1;
- if (type == INET_SERVER) {
- status = listen(fd, 5);
- if (status < 0) {
- close(fd);
- syscall = "listen(2)";
- }
- }
- /* create new instance */
- return init_sock(arg->sock, fd);
- }
- static VALUE
- init_inetsock(VALUE sock, VALUE remote_host, VALUE remote_serv,
- VALUE local_host, VALUE local_serv, int type)
- {
- struct inetsock_arg arg;
- arg.sock = sock;
- arg.remote.host = remote_host;
- arg.remote.serv = remote_serv;
- arg.remote.res = 0;
- arg.local.host = local_host;
- arg.local.serv = local_serv;
- arg.local.res = 0;
- arg.type = type;
- arg.fd = -1;
- return rb_ensure(init_inetsock_internal, (VALUE)&arg,
- inetsock_cleanup, (VALUE)&arg);
- }
- /*
- * call-seq:
- * TCPSocket.new(remote_host, remote_port, local_host=nil, local_port=nil)
- *
- * Opens a TCP connection to +remote_host+ on +remote_port+. If +local_host+
- * and +local_port+ are specified, then those parameters are used on the local
- * end to establish the connection.
- */
- static VALUE
- tcp_init(VALUE sock, SEL sel, int argc, VALUE *argv)
- {
- VALUE remote_host, remote_serv;
- VALUE local_host, local_serv;
- rb_scan_args(argc, argv, "22", &remote_host, &remote_serv,
- &local_host, &local_serv);
- return init_inetsock(sock, remote_host, remote_serv,
- local_host, local_serv, INET_CLIENT);
- }
- struct hostent_arg {
- VALUE host;
- struct addrinfo* addr;
- VALUE (*ipaddr)(struct sockaddr*, size_t);
- };
- static VALUE
- make_hostent_internal(struct hostent_arg *arg)
- {
- VALUE host = arg->host;
- struct addrinfo* addr = arg->addr;
- VALUE (*ipaddr)(struct sockaddr*, size_t) = arg->ipaddr;
- struct addrinfo *ai;
- struct hostent *h;
- VALUE ary, names;
- char **pch;
- const char* hostp;
- char hbuf[NI_MAXHOST];
- ary = rb_ary_new();
- if (addr->ai_canonname) {
- hostp = addr->ai_canonname;
- }
- else {
- hostp = host_str(host, hbuf, sizeof(hbuf), NULL);
- }
- rb_ary_push(ary, rb_str_new2(hostp));
- if (addr->ai_canonname && (h = gethostbyname(addr->ai_canonname))) {
- names = rb_ary_new();
- if (h->h_aliases != NULL) {
- for (pch = h->h_aliases; *pch; pch++) {
- rb_ary_push(names, rb_str_new2(*pch));
- }
- }
- }
- else {
- names = rb_ary_new2(0);
- }
- rb_ary_push(ary, names);
- rb_ary_push(ary, INT2NUM(addr->ai_family));
- for (ai = addr; ai; ai = ai->ai_next) {
- rb_ary_push(ary, (*ipaddr)(ai->ai_addr, ai->ai_addrlen));
- }
- return ary;
- }
- static VALUE
- make_hostent(VALUE host, struct addrinfo *addr, VALUE (*ipaddr)(struct sockaddr *, size_t))
- {
- struct hostent_arg arg;
- arg.host = host;
- arg.addr = addr;
- arg.ipaddr = ipaddr;
- return rb_ensure(make_hostent_internal, (VALUE)&arg,
- RUBY_METHOD_FUNC(freeaddrinfo), (VALUE)addr);
- }
- static VALUE
- tcp_sockaddr(struct sockaddr *addr, size_t len)
- {
- return make_ipaddr(addr);
- }
- /*
- * call-seq:
- * TCPSocket.gethostbyname(hostname) => [official_hostname, alias_hostnames, address_family, *address_list]
- *
- * Lookups host information by _hostname_.
- *
- * TCPSocket.gethostbyname("localhost")
- * #=> ["localhost", ["hal"], 2, "127.0.0.1"]
- *
- */
- static VALUE
- tcp_s_gethostbyname(VALUE obj, SEL sel, VALUE host)
- {
- rb_secure(3);
- return make_hostent(host, sock_addrinfo(host, Qnil, SOCK_STREAM,
- AI_CANONNAME), tcp_sockaddr);
- }
- /*
- * call-seq:
- * TCPServer.new([hostname,] port) => tcpserver
- *
- * Creates a new server socket bound to _port_.
- *
- * If _hostname_ is given, the socket is bound to it.
- *
- * serv = TCPServer.new("127.0.0.1", 28561)
- * s = serv.accept
- * s.puts Time.now
- * s.close
- */
- static VALUE
- tcp_svr_init(VALUE sock, SEL sel, int argc, VALUE *argv)
- {
- VALUE arg1, arg2;
- if (rb_scan_args(argc, argv, "11", &arg1, &arg2) == 2)
- return init_inetsock(sock, arg1, arg2, Qnil, Qnil, INET_SERVER);
- else
- return init_inetsock(sock, Qnil, arg1, Qnil, Qnil, INET_SERVER);
- }
- static void
- make_fd_nonblock(int fd)
- {
- int flags;
- #ifdef F_GETFL
- flags = fcntl(fd, F_GETFL);
- if (flags == -1) {
- rb_sys_fail(0);
- }
- #else
- flags = 0;
- #endif
- flags |= O_NONBLOCK;
- if (fcntl(fd, F_SETFL, flags) == -1) {
- rb_sys_fail(0);
- }
- }
- static VALUE
- s_accept_nonblock(VALUE klass, rb_io_t *fptr, struct sockaddr *sockaddr, socklen_t *len)
- {
- int fd2;
- rb_secure(3);
- rb_io_set_nonblock(fptr);
- fd2 = accept(fptr->fd, (struct sockaddr*)sockaddr, len);
- if (fd2 < 0) {
- switch (errno) {
- case EAGAIN:
- #if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
- case EWOULDBLOCK:
- #endif
- case ECONNABORTED:
- #if defined EPROTO
- case EPROTO:
- #endif
- rb_mod_sys_fail(rb_mWaitReadable, "accept(2) would block");
- }
- rb_sys_fail("accept(2)");
- }
- make_fd_nonblock(fd2);
- return init_sock(rb_obj_alloc(klass), fd2);
- }
- static VALUE
- s_accept(VALUE klass, int fd, struct sockaddr *sockaddr, socklen_t *len)
- {
- int fd2;
- int retry = 0;
- rb_secure(3);
- retry:
- rb_thread_wait_fd(fd);
- #if defined(_nec_ews)
- fd2 = accept(fd, sockaddr, len);
- #else
- fd2 = accept(fd, sockaddr, len);
- #endif
- if (fd2 < 0) {
- switch (errno) {
- case EMFILE:
- case ENFILE:
- if (retry) break;
- rb_gc();
- retry = 1;
- goto retry;
- default:
- if (!rb_io_wait_readable(fd)) break;
- retry = 0;
- goto retry;
- }
- rb_sys_fail(0);
- }
- if (!klass) return INT2NUM(fd2);
- return init_sock(rb_obj_alloc(klass), fd2);
- }
- /*
- * call-seq:
- * tcpserver.accept => tcpsocket
- *
- * TCPServer.open("127.0.0.1", 14641) {|serv|
- * s = serv.accept
- * s.puts Time.now
- * s.close
- * }
- *
- */
- static VALUE
- tcp_accept(VALUE soc…
Large files files are truncated, but you can click here to view the full file