diff options
Diffstat (limited to 'ext/socket')
| -rw-r--r-- | ext/socket/.cvsignore | 3 | ||||
| -rw-r--r-- | ext/socket/.document | 17 | ||||
| -rw-r--r-- | ext/socket/addrinfo.h | 79 | ||||
| -rw-r--r-- | ext/socket/ancdata.c | 1742 | ||||
| -rw-r--r-- | ext/socket/basicsocket.c | 792 | ||||
| -rw-r--r-- | ext/socket/constants.c | 144 | ||||
| -rw-r--r-- | ext/socket/depend | 3257 | ||||
| -rw-r--r-- | ext/socket/extconf.rb | 737 | ||||
| -rw-r--r-- | ext/socket/getaddrinfo.c | 981 | ||||
| -rw-r--r-- | ext/socket/getnameinfo.c | 280 | ||||
| -rw-r--r-- | ext/socket/ifaddr.c | 480 | ||||
| -rw-r--r-- | ext/socket/init.c | 843 | ||||
| -rw-r--r-- | ext/socket/ipsocket.c | 1640 | ||||
| -rw-r--r-- | ext/socket/lib/socket.rb | 1820 | ||||
| -rw-r--r-- | ext/socket/mkconstants.rb | 854 | ||||
| -rw-r--r-- | ext/socket/option.c | 1477 | ||||
| -rw-r--r-- | ext/socket/raddrinfo.c | 3278 | ||||
| -rw-r--r-- | ext/socket/rubysocket.h | 514 | ||||
| -rw-r--r-- | ext/socket/socket.c | 5427 | ||||
| -rw-r--r-- | ext/socket/sockport.h | 84 | ||||
| -rw-r--r-- | ext/socket/sockssocket.c | 75 | ||||
| -rw-r--r-- | ext/socket/tcpserver.c | 140 | ||||
| -rw-r--r-- | ext/socket/tcpsocket.c | 144 | ||||
| -rw-r--r-- | ext/socket/udpsocket.c | 247 | ||||
| -rw-r--r-- | ext/socket/unixserver.c | 121 | ||||
| -rw-r--r-- | ext/socket/unixsocket.c | 598 |
26 files changed, 20878 insertions, 4896 deletions
diff --git a/ext/socket/.cvsignore b/ext/socket/.cvsignore deleted file mode 100644 index 4088712231..0000000000 --- a/ext/socket/.cvsignore +++ /dev/null @@ -1,3 +0,0 @@ -Makefile -mkmf.log -*.def diff --git a/ext/socket/.document b/ext/socket/.document new file mode 100644 index 0000000000..53cfac0b10 --- /dev/null +++ b/ext/socket/.document @@ -0,0 +1,17 @@ +ancdata.c +basicsocket.c +constants.c +constdefs.c +ifaddr.c +init.c +ipsocket.c +option.c +raddrinfo.c +socket.c +sockssocket.c +tcpserver.c +tcpsocket.c +udpsocket.c +unixserver.c +unixsocket.c +lib diff --git a/ext/socket/addrinfo.h b/ext/socket/addrinfo.h index a22615ee00..eb9eb8ae0e 100644 --- a/ext/socket/addrinfo.h +++ b/ext/socket/addrinfo.h @@ -1,7 +1,7 @@ /* * Copyright (C) 1995, 1996, 1997, 1998, and 1999 WIDE Project. * All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -13,7 +13,7 @@ * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. - * + * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE @@ -29,7 +29,6 @@ #ifndef ADDR_INFO_H #define ADDR_INFO_H -#ifndef HAVE_GETADDRINFO /* special compatibility hack */ #undef EAI_ADDRFAMILY @@ -50,6 +49,7 @@ #undef AI_PASSIVE #undef AI_CANONNAME #undef AI_NUMERICHOST +#undef AI_NUMERICSERV #undef AI_ALL #undef AI_ADDRCONFIG #undef AI_V4MAPPED @@ -61,12 +61,6 @@ #undef NI_NUMERICSERV #undef NI_DGRAM -#define addrinfo addrinfo__compat -#define getaddrinfo getaddrinfo__compat -#define getnameinfo getnameinfo__compat -#define freehostent freehostent__compat -#define freeaddrinfo freeaddrinfo__compat - #ifndef __P # ifdef HAVE_PROTOTYPES # define __P(args) args @@ -102,8 +96,12 @@ #define AI_PASSIVE 0x00000001 /* get address to use bind() */ #define AI_CANONNAME 0x00000002 /* fill ai_canonname */ #define AI_NUMERICHOST 0x00000004 /* prevent name resolution */ +#define AI_NUMERICSERV 0x00000008 /* prevent service name resolution */ /* valid flags for addrinfo */ -#define AI_MASK (AI_PASSIVE | AI_CANONNAME | AI_NUMERICHOST) +#ifndef __HAIKU__ +#undef AI_MASK +#define AI_MASK (AI_PASSIVE | AI_CANONNAME | AI_NUMERICHOST | AI_NUMERICSERV) +#endif #define AI_ALL 0x00000100 /* IPv6 and IPv4-mapped (with AI_V4MAPPED) */ #define AI_V4MAPPED_CFG 0x00000200 /* accept IPv4-mapped if kernel supports */ @@ -129,37 +127,57 @@ #define NI_NUMERICSERV 0x00000008 #define NI_DGRAM 0x00000010 +#ifndef HAVE_TYPE_STRUCT_ADDRINFO struct addrinfo { - int ai_flags; /* AI_PASSIVE, AI_CANONNAME */ - int ai_family; /* PF_xxx */ - int ai_socktype; /* SOCK_xxx */ - int ai_protocol; /* 0 or IPPROTO_xxx for IPv4 and IPv6 */ - size_t ai_addrlen; /* length of ai_addr */ - char *ai_canonname; /* canonical name for hostname */ - struct sockaddr *ai_addr; /* binary address */ - struct addrinfo *ai_next; /* next structure in linked list */ + int ai_flags; /* AI_PASSIVE, AI_CANONNAME */ + int ai_family; /* PF_xxx */ + int ai_socktype; /* SOCK_xxx */ + int ai_protocol; /* 0 or IPPROTO_xxx for IPv4 and IPv6 */ + size_t ai_addrlen; /* length of ai_addr */ + char *ai_canonname; /* canonical name for hostname */ + struct sockaddr *ai_addr; /* binary address */ + struct addrinfo *ai_next; /* next structure in linked list */ }; +#endif + +#ifndef HAVE_GETADDRINFO +#undef getaddrinfo +#define getaddrinfo getaddrinfo__compat +#endif +#ifndef HAVE_GETNAMEINFO +#undef getnameinfo +#define getnameinfo getnameinfo__compat +#endif +#ifndef HAVE_FREEHOSTENT +#undef freehostent +#define freehostent freehostent__compat +#endif +#ifndef HAVE_FREEADDRINFO +#undef freeaddrinfo +#define freeaddrinfo freeaddrinfo__compat +#endif extern int getaddrinfo __P(( - const char *hostname, const char *servname, - const struct addrinfo *hints, - struct addrinfo **res)); + const char *hostname, const char *servname, + const struct addrinfo *hints, + struct addrinfo **res)); extern int getnameinfo __P(( - const struct sockaddr *sa, - size_t salen, - char *host, - size_t hostlen, - char *serv, - size_t servlen, - int flags)); + const struct sockaddr *sa, + socklen_t salen, + char *host, + socklen_t hostlen, + char *serv, + socklen_t servlen, + int flags)); extern void freehostent __P((struct hostent *)); extern void freeaddrinfo __P((struct addrinfo *)); -#if defined __UCLIBC__ +extern +#ifdef GAI_STRERROR_CONST const #endif -extern char *gai_strerror __P((int)); +char *gai_strerror __P((int)); /* In case there is no definition of offsetof() provided - though any proper Standard C system should have one. */ @@ -169,4 +187,3 @@ Standard C system should have one. */ #endif #endif -#endif diff --git a/ext/socket/ancdata.c b/ext/socket/ancdata.c new file mode 100644 index 0000000000..f1e9e42524 --- /dev/null +++ b/ext/socket/ancdata.c @@ -0,0 +1,1742 @@ +#include "rubysocket.h" + +#include <time.h> + +static VALUE sym_wait_readable, sym_wait_writable; + +#if defined(HAVE_STRUCT_MSGHDR_MSG_CONTROL) +static VALUE rb_cAncillaryData; + +static VALUE +constant_to_sym(int constant, ID (*intern_const)(int)) +{ + ID name = intern_const(constant); + if (name) { + return ID2SYM(name); + } + + return INT2NUM(constant); +} + +static VALUE +ip_cmsg_type_to_sym(int level, int cmsg_type) +{ + switch (level) { + case SOL_SOCKET: + return constant_to_sym(cmsg_type, rsock_intern_scm_optname); + case IPPROTO_IP: + return constant_to_sym(cmsg_type, rsock_intern_ip_optname); +#ifdef IPPROTO_IPV6 + case IPPROTO_IPV6: + return constant_to_sym(cmsg_type, rsock_intern_ipv6_optname); +#endif + case IPPROTO_TCP: + return constant_to_sym(cmsg_type, rsock_intern_tcp_optname); + case IPPROTO_UDP: + return constant_to_sym(cmsg_type, rsock_intern_udp_optname); + default: + return INT2NUM(cmsg_type); + } +} + +/* + * call-seq: + * Socket::AncillaryData.new(family, cmsg_level, cmsg_type, cmsg_data) -> ancillarydata + * + * _family_ should be an integer, a string or a symbol. + * - Socket::AF_INET, "AF_INET", "INET", :AF_INET, :INET + * - Socket::AF_UNIX, "AF_UNIX", "UNIX", :AF_UNIX, :UNIX + * - etc. + * + * _cmsg_level_ should be an integer, a string or a symbol. + * - Socket::SOL_SOCKET, "SOL_SOCKET", "SOCKET", :SOL_SOCKET and :SOCKET + * - Socket::IPPROTO_IP, "IP" and :IP + * - Socket::IPPROTO_IPV6, "IPV6" and :IPV6 + * - Socket::IPPROTO_TCP, "TCP" and :TCP + * - etc. + * + * _cmsg_type_ should be an integer, a string or a symbol. + * If a string/symbol is specified, it is interpreted depend on _cmsg_level_. + * - Socket::SCM_RIGHTS, "SCM_RIGHTS", "RIGHTS", :SCM_RIGHTS, :RIGHTS for SOL_SOCKET + * - Socket::IP_RECVTTL, "RECVTTL" and :RECVTTL for IPPROTO_IP + * - Socket::IPV6_PKTINFO, "PKTINFO" and :PKTINFO for IPPROTO_IPV6 + * - etc. + * + * _cmsg_data_ should be a string. + * + * p Socket::AncillaryData.new(:INET, :TCP, :NODELAY, "") + * #=> #<Socket::AncillaryData: INET TCP NODELAY ""> + * + * p Socket::AncillaryData.new(:INET6, :IPV6, :PKTINFO, "") + * #=> #<Socket::AncillaryData: INET6 IPV6 PKTINFO ""> + * + */ +static VALUE +ancillary_initialize(VALUE self, VALUE vfamily, VALUE vlevel, VALUE vtype, VALUE data) +{ + int family = rsock_family_arg(vfamily); + int level = rsock_level_arg(family, vlevel); + int type = rsock_cmsg_type_arg(family, level, vtype); + StringValue(data); + rb_ivar_set(self, rb_intern("family"), INT2NUM(family)); + rb_ivar_set(self, rb_intern("level"), INT2NUM(level)); + rb_ivar_set(self, rb_intern("type"), INT2NUM(type)); + rb_ivar_set(self, rb_intern("data"), data); + return self; +} + +static VALUE +ancdata_new(int family, int level, int type, VALUE data) +{ + VALUE obj = rb_obj_alloc(rb_cAncillaryData); + StringValue(data); + ancillary_initialize(obj, INT2NUM(family), INT2NUM(level), INT2NUM(type), data); + return (VALUE)obj; +} + +static int +ancillary_family(VALUE self) +{ + VALUE v = rb_attr_get(self, rb_intern("family")); + return NUM2INT(v); +} + +/* + * call-seq: + * ancillarydata.family => integer + * + * returns the socket family as an integer. + * + * p Socket::AncillaryData.new(:INET6, :IPV6, :PKTINFO, "").family + * #=> 10 + */ +static VALUE +ancillary_family_m(VALUE self) +{ + return INT2NUM(ancillary_family(self)); +} + +static int +ancillary_level(VALUE self) +{ + VALUE v = rb_attr_get(self, rb_intern("level")); + return NUM2INT(v); +} + +/* + * call-seq: + * ancillarydata.level => integer + * + * returns the cmsg level as an integer. + * + * p Socket::AncillaryData.new(:INET6, :IPV6, :PKTINFO, "").level + * #=> 41 + */ +static VALUE +ancillary_level_m(VALUE self) +{ + return INT2NUM(ancillary_level(self)); +} + +static int +ancillary_type(VALUE self) +{ + VALUE v = rb_attr_get(self, rb_intern("type")); + return NUM2INT(v); +} + +/* + * call-seq: + * ancillarydata.type => integer + * + * returns the cmsg type as an integer. + * + * p Socket::AncillaryData.new(:INET6, :IPV6, :PKTINFO, "").type + * #=> 2 + */ +static VALUE +ancillary_type_m(VALUE self) +{ + return INT2NUM(ancillary_type(self)); +} + +/* + * call-seq: + * ancillarydata.data => string + * + * returns the cmsg data as a string. + * + * p Socket::AncillaryData.new(:INET6, :IPV6, :PKTINFO, "").data + * #=> "" + */ +static VALUE +ancillary_data(VALUE self) +{ + VALUE v = rb_attr_get(self, rb_intern("data")); + StringValue(v); + return v; +} + +#ifdef SCM_RIGHTS +/* + * call-seq: + * Socket::AncillaryData.unix_rights(io1, io2, ...) => ancillarydata + * + * Creates a new Socket::AncillaryData object which contains file descriptors as data. + * + * p Socket::AncillaryData.unix_rights(STDERR) + * #=> #<Socket::AncillaryData: UNIX SOCKET RIGHTS 2> + */ +static VALUE +ancillary_s_unix_rights(int argc, VALUE *argv, VALUE klass) +{ + VALUE result, str, ary; + int i; + + ary = rb_ary_new(); + + for (i = 0 ; i < argc; i++) { + VALUE obj = argv[i]; + if (!RB_TYPE_P(obj, T_FILE)) { + rb_raise(rb_eTypeError, "IO expected"); + } + rb_ary_push(ary, obj); + } + + str = rb_str_buf_new(sizeof(int) * argc); + + for (i = 0 ; i < argc; i++) { + VALUE obj = RARRAY_AREF(ary, i); + rb_io_t *fptr; + int fd; + GetOpenFile(obj, fptr); + fd = fptr->fd; + rb_str_buf_cat(str, (char *)&fd, sizeof(int)); + } + + result = ancdata_new(AF_UNIX, SOL_SOCKET, SCM_RIGHTS, str); + rb_ivar_set(result, rb_intern("unix_rights"), ary); + return result; +} +#else +#define ancillary_s_unix_rights rb_f_notimplement +#endif + +#ifdef SCM_RIGHTS +/* + * call-seq: + * ancillarydata.unix_rights => array-of-IOs or nil + * + * returns the array of IO objects for SCM_RIGHTS control message in UNIX domain socket. + * + * The class of the IO objects in the array is IO or Socket. + * + * The array is attached to _ancillarydata_ when it is instantiated. + * For example, BasicSocket#recvmsg attach the array when + * receives a SCM_RIGHTS control message and :scm_rights=>true option is given. + * + * # recvmsg needs :scm_rights=>true for unix_rights + * s1, s2 = UNIXSocket.pair + * p s1 #=> #<UNIXSocket:fd 3> + * s1.sendmsg "stdin and a socket", 0, nil, Socket::AncillaryData.unix_rights(STDIN, s1) + * _, _, _, ctl = s2.recvmsg(:scm_rights=>true) + * p ctl #=> #<Socket::AncillaryData: UNIX SOCKET RIGHTS 6 7> + * p ctl.unix_rights #=> [#<IO:fd 6>, #<Socket:fd 7>] + * p File.identical?(STDIN, ctl.unix_rights[0]) #=> true + * p File.identical?(s1, ctl.unix_rights[1]) #=> true + * + * # If :scm_rights=>true is not given, unix_rights returns nil + * s1, s2 = UNIXSocket.pair + * s1.sendmsg "stdin and a socket", 0, nil, Socket::AncillaryData.unix_rights(STDIN, s1) + * _, _, _, ctl = s2.recvmsg + * p ctl #=> #<Socket::AncillaryData: UNIX SOCKET RIGHTS 6 7> + * p ctl.unix_rights #=> nil + * + */ +static VALUE +ancillary_unix_rights(VALUE self) +{ + int level, type; + + level = ancillary_level(self); + type = ancillary_type(self); + + if (level != SOL_SOCKET || type != SCM_RIGHTS) + rb_raise(rb_eTypeError, "SCM_RIGHTS ancillary data expected"); + + return rb_attr_get(self, rb_intern("unix_rights")); +} +#else +#define ancillary_unix_rights rb_f_notimplement +#endif + +#if defined(SCM_TIMESTAMP) || defined(SCM_TIMESTAMPNS) || defined(SCM_BINTIME) +/* + * call-seq: + * ancillarydata.timestamp => time + * + * returns the timestamp as a time object. + * + * _ancillarydata_ should be one of following type: + * - SOL_SOCKET/SCM_TIMESTAMP (microsecond) GNU/Linux, FreeBSD, NetBSD, OpenBSD, Solaris, MacOS X + * - SOL_SOCKET/SCM_TIMESTAMPNS (nanosecond) GNU/Linux + * - SOL_SOCKET/SCM_BINTIME (2**(-64) second) FreeBSD + * + * Addrinfo.udp("127.0.0.1", 0).bind {|s1| + * Addrinfo.udp("127.0.0.1", 0).bind {|s2| + * s1.setsockopt(:SOCKET, :TIMESTAMP, true) + * s2.send "a", 0, s1.local_address + * ctl = s1.recvmsg.last + * p ctl #=> #<Socket::AncillaryData: INET SOCKET TIMESTAMP 2009-02-24 17:35:46.775581> + * t = ctl.timestamp + * p t #=> 2009-02-24 17:35:46 +0900 + * p t.usec #=> 775581 + * p t.nsec #=> 775581000 + * } + * } + * + */ +static VALUE +ancillary_timestamp(VALUE self) +{ + int level, type; + VALUE data; + VALUE result = Qnil; + + level = ancillary_level(self); + type = ancillary_type(self); + data = ancillary_data(self); + +# ifdef SCM_TIMESTAMP + if (level == SOL_SOCKET && type == SCM_TIMESTAMP && + RSTRING_LEN(data) == sizeof(struct timeval)) { + struct timeval tv; + memcpy((char*)&tv, RSTRING_PTR(data), sizeof(tv)); + result = rb_time_new(tv.tv_sec, tv.tv_usec); + } +# endif + +# ifdef SCM_TIMESTAMPNS + if (level == SOL_SOCKET && type == SCM_TIMESTAMPNS && + RSTRING_LEN(data) == sizeof(struct timespec)) { + struct timespec ts; + memcpy((char*)&ts, RSTRING_PTR(data), sizeof(ts)); + result = rb_time_nano_new(ts.tv_sec, ts.tv_nsec); + } +# endif + +#define add(x,y) (rb_funcall((x), '+', 1, (y))) +#define mul(x,y) (rb_funcall((x), '*', 1, (y))) +#define quo(x,y) (rb_funcall((x), rb_intern("quo"), 1, (y))) + +# ifdef SCM_BINTIME + if (level == SOL_SOCKET && type == SCM_BINTIME && + RSTRING_LEN(data) == sizeof(struct bintime)) { + struct bintime bt; + VALUE d, timev; + memcpy((char*)&bt, RSTRING_PTR(data), sizeof(bt)); + d = ULL2NUM(0x100000000ULL); + d = mul(d,d); + timev = add(TIMET2NUM(bt.sec), quo(ULL2NUM(bt.frac), d)); + result = rb_time_num_new(timev, Qnil); + } +# endif + + if (result == Qnil) + rb_raise(rb_eTypeError, "timestamp ancillary data expected"); + + return result; +} +#else +#define ancillary_timestamp rb_f_notimplement +#endif + +/* + * call-seq: + * Socket::AncillaryData.int(family, cmsg_level, cmsg_type, integer) => ancillarydata + * + * Creates a new Socket::AncillaryData object which contains a int as data. + * + * The size and endian is dependent on the host. + * + * require 'socket' + * + * p Socket::AncillaryData.int(:UNIX, :SOCKET, :RIGHTS, STDERR.fileno) + * #=> #<Socket::AncillaryData: UNIX SOCKET RIGHTS 2> + */ +static VALUE +ancillary_s_int(VALUE klass, VALUE vfamily, VALUE vlevel, VALUE vtype, VALUE integer) +{ + int family = rsock_family_arg(vfamily); + int level = rsock_level_arg(family, vlevel); + int type = rsock_cmsg_type_arg(family, level, vtype); + int i = NUM2INT(integer); + return ancdata_new(family, level, type, rb_str_new((char*)&i, sizeof(i))); +} + +/* + * call-seq: + * ancillarydata.int => integer + * + * Returns the data in _ancillarydata_ as an int. + * + * The size and endian is dependent on the host. + * + * ancdata = Socket::AncillaryData.int(:UNIX, :SOCKET, :RIGHTS, STDERR.fileno) + * p ancdata.int #=> 2 + */ +static VALUE +ancillary_int(VALUE self) +{ + VALUE data; + int i; + data = ancillary_data(self); + if (RSTRING_LEN(data) != sizeof(int)) + rb_raise(rb_eTypeError, "size differ. expected as sizeof(int)=%d but %ld", (int)sizeof(int), (long)RSTRING_LEN(data)); + memcpy((char*)&i, RSTRING_PTR(data), sizeof(int)); + return INT2NUM(i); +} + +#if defined(IPPROTO_IP) && defined(IP_PKTINFO) && defined(HAVE_STRUCT_IN_PKTINFO_IPI_SPEC_DST) /* GNU/Linux */ +/* + * call-seq: + * Socket::AncillaryData.ip_pktinfo(addr, ifindex) => ancdata + * Socket::AncillaryData.ip_pktinfo(addr, ifindex, spec_dst) => ancdata + * + * Returns new ancillary data for IP_PKTINFO. + * + * If spec_dst is not given, addr is used. + * + * IP_PKTINFO is not standard. + * + * Supported platform: GNU/Linux + * + * addr = Addrinfo.ip("127.0.0.1") + * ifindex = 0 + * spec_dst = Addrinfo.ip("127.0.0.1") + * p Socket::AncillaryData.ip_pktinfo(addr, ifindex, spec_dst) + * #=> #<Socket::AncillaryData: INET IP PKTINFO 127.0.0.1 ifindex:0 spec_dst:127.0.0.1> + * + */ +static VALUE +ancillary_s_ip_pktinfo(int argc, VALUE *argv, VALUE self) +{ + VALUE v_addr, v_ifindex, v_spec_dst; + unsigned int ifindex; + struct sockaddr_in sa; + struct in_pktinfo pktinfo; + + rb_scan_args(argc, argv, "21", &v_addr, &v_ifindex, &v_spec_dst); + + SockAddrStringValue(v_addr); + ifindex = NUM2UINT(v_ifindex); + if (NIL_P(v_spec_dst)) + v_spec_dst = v_addr; + else + SockAddrStringValue(v_spec_dst); + + memset(&pktinfo, 0, sizeof(pktinfo)); + + memset(&sa, 0, sizeof(sa)); + if (RSTRING_LEN(v_addr) != sizeof(sa)) + rb_raise(rb_eArgError, "addr size different to AF_INET sockaddr"); + memcpy(&sa, RSTRING_PTR(v_addr), sizeof(sa)); + if (sa.sin_family != AF_INET) + rb_raise(rb_eArgError, "addr is not AF_INET sockaddr"); + memcpy(&pktinfo.ipi_addr, &sa.sin_addr, sizeof(pktinfo.ipi_addr)); + + pktinfo.ipi_ifindex = ifindex; + + memset(&sa, 0, sizeof(sa)); + if (RSTRING_LEN(v_spec_dst) != sizeof(sa)) + rb_raise(rb_eArgError, "spec_dat size different to AF_INET sockaddr"); + memcpy(&sa, RSTRING_PTR(v_spec_dst), sizeof(sa)); + if (sa.sin_family != AF_INET) + rb_raise(rb_eArgError, "spec_dst is not AF_INET sockaddr"); + memcpy(&pktinfo.ipi_spec_dst, &sa.sin_addr, sizeof(pktinfo.ipi_spec_dst)); + + return ancdata_new(AF_INET, IPPROTO_IP, IP_PKTINFO, rb_str_new((char *)&pktinfo, sizeof(pktinfo))); +} +#else +#define ancillary_s_ip_pktinfo rb_f_notimplement +#endif + +#if defined(IPPROTO_IP) && defined(IP_PKTINFO) && defined(HAVE_STRUCT_IN_PKTINFO_IPI_SPEC_DST) /* GNU/Linux */ +/* + * call-seq: + * ancdata.ip_pktinfo => [addr, ifindex, spec_dst] + * + * Extracts addr, ifindex and spec_dst from IP_PKTINFO ancillary data. + * + * IP_PKTINFO is not standard. + * + * Supported platform: GNU/Linux + * + * addr = Addrinfo.ip("127.0.0.1") + * ifindex = 0 + * spec_dest = Addrinfo.ip("127.0.0.1") + * ancdata = Socket::AncillaryData.ip_pktinfo(addr, ifindex, spec_dest) + * p ancdata.ip_pktinfo + * #=> [#<Addrinfo: 127.0.0.1>, 0, #<Addrinfo: 127.0.0.1>] + * + * + */ +static VALUE +ancillary_ip_pktinfo(VALUE self) +{ + int level, type; + VALUE data; + struct in_pktinfo pktinfo; + struct sockaddr_in sa; + VALUE v_spec_dst, v_addr; + + level = ancillary_level(self); + type = ancillary_type(self); + data = ancillary_data(self); + + if (level != IPPROTO_IP || type != IP_PKTINFO || + RSTRING_LEN(data) != sizeof(struct in_pktinfo)) { + rb_raise(rb_eTypeError, "IP_PKTINFO ancillary data expected"); + } + + memcpy(&pktinfo, RSTRING_PTR(data), sizeof(struct in_pktinfo)); + memset(&sa, 0, sizeof(sa)); + + sa.sin_family = AF_INET; + memcpy(&sa.sin_addr, &pktinfo.ipi_addr, sizeof(sa.sin_addr)); + v_addr = rsock_addrinfo_new((struct sockaddr *)&sa, sizeof(sa), PF_INET, 0, 0, Qnil, Qnil); + + sa.sin_family = AF_INET; + memcpy(&sa.sin_addr, &pktinfo.ipi_spec_dst, sizeof(sa.sin_addr)); + v_spec_dst = rsock_addrinfo_new((struct sockaddr *)&sa, sizeof(sa), PF_INET, 0, 0, Qnil, Qnil); + + return rb_ary_new3(3, v_addr, UINT2NUM(pktinfo.ipi_ifindex), v_spec_dst); +} +#else +#define ancillary_ip_pktinfo rb_f_notimplement +#endif + +#if defined(IPPROTO_IPV6) && defined(IPV6_PKTINFO) /* IPv6 RFC3542 */ +/* + * call-seq: + * Socket::AncillaryData.ipv6_pktinfo(addr, ifindex) => ancdata + * + * Returns new ancillary data for IPV6_PKTINFO. + * + * IPV6_PKTINFO is defined by RFC 3542. + * + * addr = Addrinfo.ip("::1") + * ifindex = 0 + * p Socket::AncillaryData.ipv6_pktinfo(addr, ifindex) + * #=> #<Socket::AncillaryData: INET6 IPV6 PKTINFO ::1 ifindex:0> + * + */ +static VALUE +ancillary_s_ipv6_pktinfo(VALUE self, VALUE v_addr, VALUE v_ifindex) +{ + unsigned int ifindex; + struct sockaddr_in6 sa; + struct in6_pktinfo pktinfo; + + SockAddrStringValue(v_addr); + ifindex = NUM2UINT(v_ifindex); + + memset(&pktinfo, 0, sizeof(pktinfo)); + + memset(&sa, 0, sizeof(sa)); + if (RSTRING_LEN(v_addr) != sizeof(sa)) + rb_raise(rb_eArgError, "addr size different to AF_INET6 sockaddr"); + memcpy(&sa, RSTRING_PTR(v_addr), sizeof(sa)); + if (sa.sin6_family != AF_INET6) + rb_raise(rb_eArgError, "addr is not AF_INET6 sockaddr"); + memcpy(&pktinfo.ipi6_addr, &sa.sin6_addr, sizeof(pktinfo.ipi6_addr)); + + pktinfo.ipi6_ifindex = ifindex; + + return ancdata_new(AF_INET6, IPPROTO_IPV6, IPV6_PKTINFO, rb_str_new((char *)&pktinfo, sizeof(pktinfo))); +} +#else +#define ancillary_s_ipv6_pktinfo rb_f_notimplement +#endif + +#if defined(IPPROTO_IPV6) && defined(IPV6_PKTINFO) /* IPv6 RFC3542 */ +static void +extract_ipv6_pktinfo(VALUE self, struct in6_pktinfo *pktinfo_ptr, struct sockaddr_in6 *sa_ptr) +{ + int level, type; + VALUE data; + + level = ancillary_level(self); + type = ancillary_type(self); + data = ancillary_data(self); + + if (level != IPPROTO_IPV6 || type != IPV6_PKTINFO || + RSTRING_LEN(data) != sizeof(struct in6_pktinfo)) { + rb_raise(rb_eTypeError, "IPV6_PKTINFO ancillary data expected"); + } + + memcpy(pktinfo_ptr, RSTRING_PTR(data), sizeof(*pktinfo_ptr)); + + INIT_SOCKADDR((struct sockaddr *)sa_ptr, AF_INET6, sizeof(*sa_ptr)); + memcpy(&sa_ptr->sin6_addr, &pktinfo_ptr->ipi6_addr, sizeof(sa_ptr->sin6_addr)); + if (IN6_IS_ADDR_LINKLOCAL(&sa_ptr->sin6_addr)) + sa_ptr->sin6_scope_id = pktinfo_ptr->ipi6_ifindex; +} +#endif + +#if defined(IPPROTO_IPV6) && defined(IPV6_PKTINFO) /* IPv6 RFC3542 */ +/* + * call-seq: + * ancdata.ipv6_pktinfo => [addr, ifindex] + * + * Extracts addr and ifindex from IPV6_PKTINFO ancillary data. + * + * IPV6_PKTINFO is defined by RFC 3542. + * + * addr = Addrinfo.ip("::1") + * ifindex = 0 + * ancdata = Socket::AncillaryData.ipv6_pktinfo(addr, ifindex) + * p ancdata.ipv6_pktinfo #=> [#<Addrinfo: ::1>, 0] + * + */ +static VALUE +ancillary_ipv6_pktinfo(VALUE self) +{ + struct in6_pktinfo pktinfo; + struct sockaddr_in6 sa; + VALUE v_addr; + + extract_ipv6_pktinfo(self, &pktinfo, &sa); + v_addr = rsock_addrinfo_new((struct sockaddr *)&sa, (socklen_t)sizeof(sa), PF_INET6, 0, 0, Qnil, Qnil); + return rb_ary_new3(2, v_addr, UINT2NUM(pktinfo.ipi6_ifindex)); +} +#else +#define ancillary_ipv6_pktinfo rb_f_notimplement +#endif + +#if defined(IPPROTO_IPV6) && defined(IPV6_PKTINFO) /* IPv6 RFC3542 */ +/* + * call-seq: + * ancdata.ipv6_pktinfo_addr => addr + * + * Extracts addr from IPV6_PKTINFO ancillary data. + * + * IPV6_PKTINFO is defined by RFC 3542. + * + * addr = Addrinfo.ip("::1") + * ifindex = 0 + * ancdata = Socket::AncillaryData.ipv6_pktinfo(addr, ifindex) + * p ancdata.ipv6_pktinfo_addr #=> #<Addrinfo: ::1> + * + */ +static VALUE +ancillary_ipv6_pktinfo_addr(VALUE self) +{ + struct in6_pktinfo pktinfo; + struct sockaddr_in6 sa; + extract_ipv6_pktinfo(self, &pktinfo, &sa); + return rsock_addrinfo_new((struct sockaddr *)&sa, (socklen_t)sizeof(sa), PF_INET6, 0, 0, Qnil, Qnil); +} +#else +#define ancillary_ipv6_pktinfo_addr rb_f_notimplement +#endif + +#if defined(IPPROTO_IPV6) && defined(IPV6_PKTINFO) /* IPv6 RFC3542 */ +/* + * call-seq: + * ancdata.ipv6_pktinfo_ifindex => addr + * + * Extracts ifindex from IPV6_PKTINFO ancillary data. + * + * IPV6_PKTINFO is defined by RFC 3542. + * + * addr = Addrinfo.ip("::1") + * ifindex = 0 + * ancdata = Socket::AncillaryData.ipv6_pktinfo(addr, ifindex) + * p ancdata.ipv6_pktinfo_ifindex #=> 0 + * + */ +static VALUE +ancillary_ipv6_pktinfo_ifindex(VALUE self) +{ + struct in6_pktinfo pktinfo; + struct sockaddr_in6 sa; + extract_ipv6_pktinfo(self, &pktinfo, &sa); + return UINT2NUM(pktinfo.ipi6_ifindex); +} +#else +#define ancillary_ipv6_pktinfo_ifindex rb_f_notimplement +#endif + +#if defined(SOL_SOCKET) && defined(SCM_RIGHTS) /* 4.4BSD */ +static int +anc_inspect_socket_rights(int level, int type, VALUE data, VALUE ret) +{ + if (level == SOL_SOCKET && type == SCM_RIGHTS && + 0 < RSTRING_LEN(data) && (RSTRING_LEN(data) % sizeof(int) == 0)) { + long off; + for (off = 0; off < RSTRING_LEN(data); off += sizeof(int)) { + int fd; + memcpy((char*)&fd, RSTRING_PTR(data)+off, sizeof(int)); + rb_str_catf(ret, " %d", fd); + } + return 1; + } + else { + return 0; + } +} +#endif + +#if defined(SCM_CREDENTIALS) /* GNU/Linux */ +static int +anc_inspect_passcred_credentials(int level, int type, VALUE data, VALUE ret) +{ + if (level == SOL_SOCKET && type == SCM_CREDENTIALS && + RSTRING_LEN(data) == sizeof(struct ucred)) { + struct ucred cred; + memcpy(&cred, RSTRING_PTR(data), sizeof(struct ucred)); + rb_str_catf(ret, " pid=%u uid=%u gid=%u", cred.pid, cred.uid, cred.gid); + rb_str_cat2(ret, " (ucred)"); + return 1; + } + else { + return 0; + } +} +#endif + +#if defined(SCM_CREDS) +#define INSPECT_SCM_CREDS +static int +anc_inspect_socket_creds(int level, int type, VALUE data, VALUE ret) +{ + if (level != SOL_SOCKET && type != SCM_CREDS) + return 0; + + /* + * FreeBSD has struct cmsgcred and struct sockcred. + * They use both SOL_SOCKET/SCM_CREDS in the ancillary message. + * They are not ambiguous from the view of the caller + * because struct sockcred is sent if and only if the caller sets LOCAL_CREDS socket option. + * But inspect method doesn't know it. + * So they are ambiguous from the view of inspect. + * This function distinguish them by the size of the ancillary message. + * This heuristics works well except when sc_ngroups == CMGROUP_MAX. + */ + +#if defined(HAVE_TYPE_STRUCT_CMSGCRED) /* FreeBSD */ + if (RSTRING_LEN(data) == sizeof(struct cmsgcred)) { + struct cmsgcred cred; + memcpy(&cred, RSTRING_PTR(data), sizeof(struct cmsgcred)); + rb_str_catf(ret, " pid=%u", cred.cmcred_pid); + rb_str_catf(ret, " uid=%u", cred.cmcred_uid); + rb_str_catf(ret, " euid=%u", cred.cmcred_euid); + rb_str_catf(ret, " gid=%u", cred.cmcred_gid); + if (cred.cmcred_ngroups) { + int i; + const char *sep = " groups="; + for (i = 0; i < cred.cmcred_ngroups; i++) { + rb_str_catf(ret, "%s%u", sep, cred.cmcred_groups[i]); + sep = ","; + } + } + rb_str_cat2(ret, " (cmsgcred)"); + return 1; + } +#endif +#if defined(HAVE_TYPE_STRUCT_SOCKCRED) /* FreeBSD, NetBSD */ + if ((size_t)RSTRING_LEN(data) >= SOCKCREDSIZE(0)) { + struct sockcred cred0, *cred; + memcpy(&cred0, RSTRING_PTR(data), SOCKCREDSIZE(0)); + if ((size_t)RSTRING_LEN(data) == SOCKCREDSIZE(cred0.sc_ngroups)) { + cred = (struct sockcred *)ALLOCA_N(char, SOCKCREDSIZE(cred0.sc_ngroups)); + memcpy(cred, RSTRING_PTR(data), SOCKCREDSIZE(cred0.sc_ngroups)); + rb_str_catf(ret, " uid=%u", cred->sc_uid); + rb_str_catf(ret, " euid=%u", cred->sc_euid); + rb_str_catf(ret, " gid=%u", cred->sc_gid); + rb_str_catf(ret, " egid=%u", cred->sc_egid); + if (cred0.sc_ngroups) { + int i; + const char *sep = " groups="; + for (i = 0; i < cred0.sc_ngroups; i++) { + rb_str_catf(ret, "%s%u", sep, cred->sc_groups[i]); + sep = ","; + } + } + rb_str_cat2(ret, " (sockcred)"); + return 1; + } + } +#endif + return 0; +} +#endif + +#if defined(IPPROTO_IP) && defined(IP_RECVDSTADDR) /* 4.4BSD */ +static int +anc_inspect_ip_recvdstaddr(int level, int type, VALUE data, VALUE ret) +{ + if (level == IPPROTO_IP && type == IP_RECVDSTADDR && + RSTRING_LEN(data) == sizeof(struct in_addr)) { + struct in_addr addr; + char addrbuf[INET_ADDRSTRLEN]; + memcpy(&addr, RSTRING_PTR(data), sizeof(addr)); + if (inet_ntop(AF_INET, &addr, addrbuf, (socklen_t)sizeof(addrbuf)) == NULL) + rb_str_cat2(ret, " invalid-address"); + else + rb_str_catf(ret, " %s", addrbuf); + return 1; + } + else { + return 0; + } +} +#endif + +#if defined(IPPROTO_IP) && defined(IP_PKTINFO) && defined(HAVE_STRUCT_IN_PKTINFO_IPI_SPEC_DST) /* GNU/Linux */ +static int +anc_inspect_ip_pktinfo(int level, int type, VALUE data, VALUE ret) +{ + if (level == IPPROTO_IP && type == IP_PKTINFO && + RSTRING_LEN(data) == sizeof(struct in_pktinfo)) { + struct in_pktinfo pktinfo; + char buf[INET_ADDRSTRLEN > IFNAMSIZ ? INET_ADDRSTRLEN : IFNAMSIZ]; + memcpy(&pktinfo, RSTRING_PTR(data), sizeof(pktinfo)); + if (inet_ntop(AF_INET, &pktinfo.ipi_addr, buf, sizeof(buf)) == NULL) + rb_str_cat2(ret, " invalid-address"); + else + rb_str_catf(ret, " %s", buf); + if (if_indextoname(pktinfo.ipi_ifindex, buf) == NULL) + rb_str_catf(ret, " ifindex:%d", pktinfo.ipi_ifindex); + else + rb_str_catf(ret, " %s", buf); + if (inet_ntop(AF_INET, &pktinfo.ipi_spec_dst, buf, sizeof(buf)) == NULL) + rb_str_cat2(ret, " spec_dst:invalid-address"); + else + rb_str_catf(ret, " spec_dst:%s", buf); + return 1; + } + else { + return 0; + } +} +#endif + +#if defined(IPPROTO_IPV6) && defined(IPV6_PKTINFO) && defined(HAVE_TYPE_STRUCT_IN6_PKTINFO) /* IPv6 RFC3542 */ +static int +anc_inspect_ipv6_pktinfo(int level, int type, VALUE data, VALUE ret) +{ + if (level == IPPROTO_IPV6 && type == IPV6_PKTINFO && + RSTRING_LEN(data) == sizeof(struct in6_pktinfo)) { + struct in6_pktinfo *pktinfo = (struct in6_pktinfo *)RSTRING_PTR(data); + struct in6_addr addr; + unsigned int ifindex; + char addrbuf[INET6_ADDRSTRLEN], ifbuf[IFNAMSIZ]; + memcpy(&addr, &pktinfo->ipi6_addr, sizeof(addr)); + memcpy(&ifindex, &pktinfo->ipi6_ifindex, sizeof(ifindex)); + if (inet_ntop(AF_INET6, &addr, addrbuf, (socklen_t)sizeof(addrbuf)) == NULL) + rb_str_cat2(ret, " invalid-address"); + else + rb_str_catf(ret, " %s", addrbuf); + if (if_indextoname(ifindex, ifbuf) == NULL) + rb_str_catf(ret, " ifindex:%d", ifindex); + else + rb_str_catf(ret, " %s", ifbuf); + return 1; + } + else { + return 0; + } +} +#endif + +#ifdef HAVE_GMTIME_R +# define LOCALTIME(time, tm) localtime_r(&(time), &(tm)) +#else +# define LOCALTIME(time, tm) ((tm) = *localtime(&(time))) +#endif + +#if defined(SCM_TIMESTAMP) /* GNU/Linux, FreeBSD, NetBSD, OpenBSD, MacOS X, Solaris */ +static int +inspect_timeval_as_abstime(int level, int optname, VALUE data, VALUE ret) +{ + if (RSTRING_LEN(data) == sizeof(struct timeval)) { + struct timeval tv; + time_t time; + struct tm tm; + char buf[32]; + memcpy((char*)&tv, RSTRING_PTR(data), sizeof(tv)); + time = tv.tv_sec; + LOCALTIME(time, tm); + strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", &tm); + rb_str_catf(ret, " %s.%06ld", buf, (long)tv.tv_usec); + return 1; + } + else { + return 0; + } +} +#endif + +#if defined(SCM_TIMESTAMPNS) /* GNU/Linux */ +static int +inspect_timespec_as_abstime(int level, int optname, VALUE data, VALUE ret) +{ + if (RSTRING_LEN(data) == sizeof(struct timespec)) { + struct timespec ts; + struct tm tm; + char buf[32]; + memcpy((char*)&ts, RSTRING_PTR(data), sizeof(ts)); + LOCALTIME(ts.tv_sec, tm); + strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", &tm); + rb_str_catf(ret, " %s.%09ld", buf, (long)ts.tv_nsec); + return 1; + } + else { + return 0; + } +} +#endif + +#if defined(SCM_BINTIME) /* FreeBSD */ +static int +inspect_bintime_as_abstime(int level, int optname, VALUE data, VALUE ret) +{ + if (RSTRING_LEN(data) == sizeof(struct bintime)) { + struct bintime bt; + struct tm tm; + uint64_t frac_h, frac_l; + uint64_t scale_h, scale_l; + uint64_t tmp1, tmp2; + uint64_t res_h, res_l; + char buf[32]; + memcpy((char*)&bt, RSTRING_PTR(data), sizeof(bt)); + LOCALTIME(bt.sec, tm); + strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", &tm); + + /* res_h = frac * 10**19 / 2**64 */ + + frac_h = bt.frac >> 32; + frac_l = bt.frac & 0xffffffff; + + scale_h = 0x8ac72304; /* 0x8ac7230489e80000 == 10**19 */ + scale_l = 0x89e80000; + + res_h = frac_h * scale_h; + res_l = frac_l * scale_l; + + tmp1 = frac_h * scale_l; + res_h += tmp1 >> 32; + tmp2 = res_l; + res_l += tmp1 & 0xffffffff; + if (res_l < tmp2) res_h++; + + tmp1 = frac_l * scale_h; + res_h += tmp1 >> 32; + tmp2 = res_l; + res_l += tmp1 & 0xffffffff; + if (res_l < tmp2) res_h++; + + rb_str_catf(ret, " %s.%019"PRIu64, buf, res_h); + return 1; + } + else { + return 0; + } +} +#endif + +/* + * call-seq: + * ancillarydata.inspect => string + * + * returns a string which shows ancillarydata in human-readable form. + * + * p Socket::AncillaryData.new(:INET6, :IPV6, :PKTINFO, "").inspect + * #=> "#<Socket::AncillaryData: INET6 IPV6 PKTINFO \"\">" + */ +static VALUE +ancillary_inspect(VALUE self) +{ + VALUE ret; + int family, level, type; + VALUE data; + ID family_id, level_id, type_id; + VALUE vtype; + int inspected; + + family = ancillary_family(self); + level = ancillary_level(self); + type = ancillary_type(self); + data = ancillary_data(self); + + ret = rb_sprintf("#<%s:", rb_obj_classname(self)); + + family_id = rsock_intern_family_noprefix(family); + if (family_id) + rb_str_catf(ret, " %s", rb_id2name(family_id)); + else + rb_str_catf(ret, " family:%d", family); + + if (level == SOL_SOCKET) { + rb_str_cat2(ret, " SOCKET"); + + type_id = rsock_intern_scm_optname(type); + if (type_id) + rb_str_catf(ret, " %s", rb_id2name(type_id)); + else + rb_str_catf(ret, " cmsg_type:%d", type); + } + else if (IS_IP_FAMILY(family)) { + level_id = rsock_intern_iplevel(level); + if (level_id) + rb_str_catf(ret, " %s", rb_id2name(level_id)); + else + rb_str_catf(ret, " cmsg_level:%d", level); + + vtype = ip_cmsg_type_to_sym(level, type); + if (SYMBOL_P(vtype)) + rb_str_catf(ret, " %"PRIsVALUE, rb_sym2str(vtype)); + else + rb_str_catf(ret, " cmsg_type:%d", type); + } + else { + rb_str_catf(ret, " cmsg_level:%d", level); + rb_str_catf(ret, " cmsg_type:%d", type); + } + + inspected = 0; + + if (level == SOL_SOCKET) + family = AF_UNSPEC; + + switch (family) { + case AF_UNSPEC: + switch (level) { +# if defined(SOL_SOCKET) + case SOL_SOCKET: + switch (type) { +# if defined(SCM_TIMESTAMP) /* GNU/Linux, FreeBSD, NetBSD, OpenBSD, MacOS X, Solaris */ + case SCM_TIMESTAMP: inspected = inspect_timeval_as_abstime(level, type, data, ret); break; +# endif +# if defined(SCM_TIMESTAMPNS) /* GNU/Linux */ + case SCM_TIMESTAMPNS: inspected = inspect_timespec_as_abstime(level, type, data, ret); break; +# endif +# if defined(SCM_BINTIME) /* FreeBSD */ + case SCM_BINTIME: inspected = inspect_bintime_as_abstime(level, type, data, ret); break; +# endif +# if defined(SCM_RIGHTS) /* 4.4BSD */ + case SCM_RIGHTS: inspected = anc_inspect_socket_rights(level, type, data, ret); break; +# endif +# if defined(SCM_CREDENTIALS) /* GNU/Linux */ + case SCM_CREDENTIALS: inspected = anc_inspect_passcred_credentials(level, type, data, ret); break; +# endif +# if defined(INSPECT_SCM_CREDS) /* NetBSD */ + case SCM_CREDS: inspected = anc_inspect_socket_creds(level, type, data, ret); break; +# endif + } + break; +# endif + } + break; + + case AF_INET: +#ifdef INET6 + case AF_INET6: +#endif + switch (level) { +# if defined(IPPROTO_IP) + case IPPROTO_IP: + switch (type) { +# if defined(IP_RECVDSTADDR) /* 4.4BSD */ + case IP_RECVDSTADDR: inspected = anc_inspect_ip_recvdstaddr(level, type, data, ret); break; +# endif +# if defined(IP_PKTINFO) && defined(HAVE_STRUCT_IN_PKTINFO_IPI_SPEC_DST) /* GNU/Linux */ + case IP_PKTINFO: inspected = anc_inspect_ip_pktinfo(level, type, data, ret); break; +# endif + } + break; +# endif + +# if defined(IPPROTO_IPV6) + case IPPROTO_IPV6: + switch (type) { +# if defined(IPV6_PKTINFO) && defined(HAVE_TYPE_STRUCT_IN6_PKTINFO) /* RFC 3542 */ + case IPV6_PKTINFO: inspected = anc_inspect_ipv6_pktinfo(level, type, data, ret); break; +# endif + } + break; +# endif + } + break; + } + + if (!inspected) { + rb_str_cat2(ret, " "); + rb_str_append(ret, rb_str_dump(data)); + } + + rb_str_cat2(ret, ">"); + + return ret; +} + +/* + * call-seq: + * ancillarydata.cmsg_is?(level, type) => true or false + * + * tests the level and type of _ancillarydata_. + * + * ancdata = Socket::AncillaryData.new(:INET6, :IPV6, :PKTINFO, "") + * ancdata.cmsg_is?(Socket::IPPROTO_IPV6, Socket::IPV6_PKTINFO) #=> true + * ancdata.cmsg_is?(:IPV6, :PKTINFO) #=> true + * ancdata.cmsg_is?(:IP, :PKTINFO) #=> false + * ancdata.cmsg_is?(:SOCKET, :RIGHTS) #=> false + */ +static VALUE +ancillary_cmsg_is_p(VALUE self, VALUE vlevel, VALUE vtype) +{ + int family = ancillary_family(self); + int level = rsock_level_arg(family, vlevel); + int type = rsock_cmsg_type_arg(family, level, vtype); + + if (ancillary_level(self) == level && + ancillary_type(self) == type) + return Qtrue; + else + return Qfalse; +} + +#endif + +#if defined(HAVE_SENDMSG) +struct sendmsg_args_struct { + int fd; + int flags; + const struct msghdr *msg; +}; + +static void * +nogvl_sendmsg_func(void *ptr) +{ + struct sendmsg_args_struct *args = ptr; + return (void *)(VALUE)sendmsg(args->fd, args->msg, args->flags); +} + +static ssize_t +rb_sendmsg(int fd, const struct msghdr *msg, int flags) +{ + struct sendmsg_args_struct args; + args.fd = fd; + args.msg = msg; + args.flags = flags; + return (ssize_t)rb_thread_call_without_gvl(nogvl_sendmsg_func, &args, RUBY_UBF_IO, 0); +} + +static VALUE +bsock_sendmsg_internal(VALUE sock, VALUE data, VALUE vflags, + VALUE dest_sockaddr, VALUE controls, VALUE ex, + int nonblock) +{ + rb_io_t *fptr; + struct msghdr mh; + struct iovec iov; + VALUE tmp; + int controls_num; +#if defined(HAVE_STRUCT_MSGHDR_MSG_CONTROL) + VALUE controls_str = 0; + int family; +#endif + int flags; + ssize_t ss; + + GetOpenFile(sock, fptr); +#if defined(HAVE_STRUCT_MSGHDR_MSG_CONTROL) + family = rsock_getfamily(fptr); +#endif + + StringValue(data); + tmp = rb_str_tmp_frozen_acquire(data); + + if (!RB_TYPE_P(controls, T_ARRAY)) { + controls = rb_ary_new(); + } + controls_num = RARRAY_LENINT(controls); + + if (controls_num) { +#if defined(HAVE_STRUCT_MSGHDR_MSG_CONTROL) + int i; + size_t last_pad = 0; + const VALUE *controls_ptr = RARRAY_CONST_PTR(controls); +#if defined(__NetBSD__) + int last_level = 0; + int last_type = 0; +#endif + controls_str = rb_str_tmp_new(0); + for (i = 0; i < controls_num; i++) { + VALUE elt = controls_ptr[i], v; + VALUE vlevel, vtype; + int level, type; + VALUE cdata; + long oldlen; + struct cmsghdr cmh; + char *cmsg; + size_t cspace; + v = rb_check_convert_type(elt, T_ARRAY, "Array", "to_ary"); + if (!NIL_P(v)) { + elt = v; + if (RARRAY_LEN(elt) != 3) + rb_raise(rb_eArgError, "an element of controls should be 3-elements array"); + vlevel = rb_ary_entry(elt, 0); + vtype = rb_ary_entry(elt, 1); + cdata = rb_ary_entry(elt, 2); + } + else { + vlevel = rb_funcall(elt, rb_intern("level"), 0); + vtype = rb_funcall(elt, rb_intern("type"), 0); + cdata = rb_funcall(elt, rb_intern("data"), 0); + } + level = rsock_level_arg(family, vlevel); + type = rsock_cmsg_type_arg(family, level, vtype); + StringValue(cdata); + oldlen = RSTRING_LEN(controls_str); + cspace = CMSG_SPACE(RSTRING_LEN(cdata)); + rb_str_resize(controls_str, oldlen + cspace); + cmsg = RSTRING_PTR(controls_str)+oldlen; + memset((char *)cmsg, 0, cspace); + memset((char *)&cmh, 0, sizeof(cmh)); + cmh.cmsg_level = level; + cmh.cmsg_type = type; + cmh.cmsg_len = (socklen_t)CMSG_LEN(RSTRING_LEN(cdata)); + MEMCPY(cmsg, &cmh, char, sizeof(cmh)); + MEMCPY(cmsg+((char*)CMSG_DATA(&cmh)-(char*)&cmh), RSTRING_PTR(cdata), char, RSTRING_LEN(cdata)); +#if defined(__NetBSD__) + last_level = cmh.cmsg_level; + last_type = cmh.cmsg_type; +#endif + last_pad = cspace - cmh.cmsg_len; + } + if (last_pad) { + /* + * This code removes the last padding from msg_controllen. + * + * 4.3BSD-Reno reject the padding for SCM_RIGHTS. (There was no 64bit environments in those days?) + * RFC 2292 require the padding. + * RFC 3542 relaxes the condition - implementation must accept both as valid. + * + * Actual problems: + * + * - NetBSD 4.0.1 + * SCM_RIGHTS with padding causes EINVAL + * IPV6_PKTINFO without padding causes "page fault trap" + * http://www.netbsd.org/cgi-bin/query-pr-single.pl?number=40661 + * + * - OpenBSD 4.4 + * IPV6_PKTINFO without padding causes EINVAL + * + * Basically, msg_controllen should contains the padding. + * So the padding is removed only if a problem really exists. + */ +#if defined(__NetBSD__) + if (last_level == SOL_SOCKET && last_type == SCM_RIGHTS) + rb_str_set_len(controls_str, RSTRING_LEN(controls_str)-last_pad); +#endif + } + RB_GC_GUARD(controls); +#else + rb_raise(rb_eNotImpError, "control message for sendmsg is unimplemented"); +#endif + } + + flags = NIL_P(vflags) ? 0 : NUM2INT(vflags); +#ifdef MSG_DONTWAIT + if (nonblock) + flags |= MSG_DONTWAIT; +#endif + + if (!NIL_P(dest_sockaddr)) + SockAddrStringValue(dest_sockaddr); + + rb_io_check_closed(fptr); + + retry: + memset(&mh, 0, sizeof(mh)); + if (!NIL_P(dest_sockaddr)) { + mh.msg_name = RSTRING_PTR(dest_sockaddr); + mh.msg_namelen = RSTRING_SOCKLEN(dest_sockaddr); + } + mh.msg_iovlen = 1; + mh.msg_iov = &iov; + iov.iov_base = RSTRING_PTR(tmp); + iov.iov_len = RSTRING_LEN(tmp); +#if defined(HAVE_STRUCT_MSGHDR_MSG_CONTROL) + if (controls_str) { + mh.msg_control = RSTRING_PTR(controls_str); + mh.msg_controllen = RSTRING_SOCKLEN(controls_str); + } +#endif + + rb_io_check_closed(fptr); + if (nonblock && !MSG_DONTWAIT_RELIABLE) + rb_io_set_nonblock(fptr); + + ss = rb_sendmsg(fptr->fd, &mh, flags); + + if (ss == -1) { + int e; + if (!nonblock && rb_io_maybe_wait_writable(errno, fptr->self, RUBY_IO_TIMEOUT_DEFAULT)) { + rb_io_check_closed(fptr); + goto retry; + } + e = errno; + if (nonblock && (e == EWOULDBLOCK || e == EAGAIN)) { + if (ex == Qfalse) { + return sym_wait_writable; + } + rb_readwrite_syserr_fail(RB_IO_WAIT_WRITABLE, e, + "sendmsg(2) would block"); + } + rb_syserr_fail(e, "sendmsg(2)"); + } +#if defined(HAVE_STRUCT_MSGHDR_MSG_CONTROL) + RB_GC_GUARD(controls_str); +#endif + rb_str_tmp_frozen_release(data, tmp); + + return SSIZET2NUM(ss); +} +#endif + +#if defined(HAVE_SENDMSG) +VALUE +rsock_bsock_sendmsg(VALUE sock, VALUE data, VALUE flags, VALUE dest_sockaddr, + VALUE controls) +{ + return bsock_sendmsg_internal(sock, data, flags, dest_sockaddr, controls, + Qtrue, 0); +} +#endif + +#if defined(HAVE_SENDMSG) +VALUE +rsock_bsock_sendmsg_nonblock(VALUE sock, VALUE data, VALUE flags, + VALUE dest_sockaddr, VALUE controls, VALUE ex) +{ + return bsock_sendmsg_internal(sock, data, flags, dest_sockaddr, + controls, ex, 1); +} +#endif + +#if defined(HAVE_RECVMSG) +struct recvmsg_args_struct { + int fd; + int flags; + struct msghdr *msg; +}; + +ssize_t +rsock_recvmsg(int socket, struct msghdr *message, int flags) +{ + ssize_t ret; + socklen_t len0; +#ifdef MSG_CMSG_CLOEXEC + /* MSG_CMSG_CLOEXEC is available since Linux 2.6.23. Linux 2.6.18 silently ignore it. */ + flags |= MSG_CMSG_CLOEXEC; +#endif + len0 = message->msg_namelen; + ret = recvmsg(socket, message, flags); + if (ret != -1 && len0 < message->msg_namelen) + message->msg_namelen = len0; + return ret; +} + +static void * +nogvl_recvmsg_func(void *ptr) +{ + struct recvmsg_args_struct *args = ptr; + int flags = args->flags; + return (void *)rsock_recvmsg(args->fd, args->msg, flags); +} + +static ssize_t +rb_recvmsg(int fd, struct msghdr *msg, int flags) +{ + struct recvmsg_args_struct args; + args.fd = fd; + args.msg = msg; + args.flags = flags; + return (ssize_t)rb_thread_call_without_gvl(nogvl_recvmsg_func, &args, RUBY_UBF_IO, 0); +} + +#if defined(HAVE_STRUCT_MSGHDR_MSG_CONTROL) +static void +discard_cmsg(struct cmsghdr *cmh, char *msg_end, int msg_peek_p) +{ +# if !defined(FD_PASSING_WORK_WITH_RECVMSG_MSG_PEEK) + /* + * FreeBSD 8.2.0, NetBSD 5 and MacOS X Snow Leopard doesn't + * allocate fds by recvmsg with MSG_PEEK. + * [ruby-dev:44189] + * http://bugs.ruby-lang.org/issues/5075 + * + * Linux 2.6.38 allocate fds by recvmsg with MSG_PEEK. + */ + if (msg_peek_p) + return; +# endif + if (cmh->cmsg_level == SOL_SOCKET && cmh->cmsg_type == SCM_RIGHTS) { + int *fdp = (int *)CMSG_DATA(cmh); + int *end = (int *)((char *)cmh + cmh->cmsg_len); + while ((char *)fdp + sizeof(int) <= (char *)end && + (char *)fdp + sizeof(int) <= msg_end) { + rb_update_max_fd(*fdp); + close(*fdp); + fdp++; + } + } +} +#endif + +void +rsock_discard_cmsg_resource(struct msghdr *mh, int msg_peek_p) +{ +#if defined(HAVE_STRUCT_MSGHDR_MSG_CONTROL) + struct cmsghdr *cmh; + char *msg_end; + + if (mh->msg_controllen == 0) + return; + + msg_end = (char *)mh->msg_control + mh->msg_controllen; + + for (cmh = CMSG_FIRSTHDR(mh); cmh != NULL; cmh = CMSG_NXTHDR(mh, cmh)) { + discard_cmsg(cmh, msg_end, msg_peek_p); + } +#endif +} + +#if defined(HAVE_STRUCT_MSGHDR_MSG_CONTROL) +static void +make_io_for_unix_rights(VALUE ctl, struct cmsghdr *cmh, char *msg_end) +{ + if (cmh->cmsg_level == SOL_SOCKET && cmh->cmsg_type == SCM_RIGHTS) { + int *fdp, *end; + VALUE ary = rb_ary_new(); + rb_ivar_set(ctl, rb_intern("unix_rights"), ary); + fdp = (int *)CMSG_DATA(cmh); + end = (int *)((char *)cmh + cmh->cmsg_len); + while ((char *)fdp + sizeof(int) <= (char *)end && + (char *)fdp + sizeof(int) <= msg_end) { + int fd = *fdp; + struct stat stbuf; + VALUE io; + if (fstat(fd, &stbuf) == -1) + rb_raise(rb_eSocket, "invalid fd in SCM_RIGHTS"); + rb_update_max_fd(fd); + rb_maygvl_fd_fix_cloexec(fd); + if (S_ISSOCK(stbuf.st_mode)) + io = rsock_init_sock(rb_obj_alloc(rb_cSocket), fd); + else + io = rb_io_fdopen(fd, O_RDWR, NULL); + ary = rb_attr_get(ctl, rb_intern("unix_rights")); + rb_ary_push(ary, io); + fdp++; + } + OBJ_FREEZE(ary); + } +} +#endif + +static VALUE +bsock_recvmsg_internal(VALUE sock, + VALUE vmaxdatlen, VALUE vflags, VALUE vmaxctllen, + VALUE scm_rights, VALUE ex, int nonblock) +{ + rb_io_t *fptr; + int grow_buffer; + size_t maxdatlen; + int flags, orig_flags; + struct msghdr mh; + struct iovec iov; + union_sockaddr namebuf; + char *datbuf; + VALUE dat_str = Qnil; + VALUE ret; + ssize_t ss; + int request_scm_rights; +#if defined(HAVE_STRUCT_MSGHDR_MSG_CONTROL) + struct cmsghdr *cmh; + size_t maxctllen; + char *ctlbuf; + VALUE ctl_str = Qnil; + int family; + int gc_done = 0; +#endif + + maxdatlen = NIL_P(vmaxdatlen) ? 4096 : NUM2SIZET(vmaxdatlen); +#if defined(HAVE_STRUCT_MSGHDR_MSG_CONTROL) + maxctllen = NIL_P(vmaxctllen) ? 4096 : NUM2SIZET(vmaxctllen); +#else + if (!NIL_P(vmaxctllen)) + rb_raise(rb_eArgError, "control message not supported"); +#endif + flags = NUM2INT(vflags); +#ifdef MSG_DONTWAIT + if (nonblock) + flags |= MSG_DONTWAIT; +#endif + orig_flags = flags; + + grow_buffer = NIL_P(vmaxdatlen) || NIL_P(vmaxctllen); + + request_scm_rights = 0; + if (RTEST(scm_rights)) + request_scm_rights = 1; +#if !defined(HAVE_STRUCT_MSGHDR_MSG_CONTROL) + if (request_scm_rights) + rb_raise(rb_eNotImpError, "control message for recvmsg is unimplemented"); +#endif + + GetOpenFile(sock, fptr); + if (rb_io_read_pending(fptr)) { + rb_raise(rb_eIOError, "recvmsg for buffered IO"); + } + +#if !defined(HAVE_STRUCT_MSGHDR_MSG_CONTROL) + if (grow_buffer) { + int socktype; + socklen_t optlen = (socklen_t)sizeof(socktype); + if (getsockopt(fptr->fd, SOL_SOCKET, SO_TYPE, (void*)&socktype, &optlen) == -1) { + rb_sys_fail("getsockopt(SO_TYPE)"); + } + if (socktype == SOCK_STREAM) + grow_buffer = 0; + } +#endif + + retry: + if (NIL_P(dat_str)) + dat_str = rb_str_tmp_new(maxdatlen); + else + rb_str_resize(dat_str, maxdatlen); + datbuf = RSTRING_PTR(dat_str); + +#if defined(HAVE_STRUCT_MSGHDR_MSG_CONTROL) + if (NIL_P(ctl_str)) + ctl_str = rb_str_tmp_new(maxctllen); + else + rb_str_resize(ctl_str, maxctllen); + ctlbuf = RSTRING_PTR(ctl_str); +#endif + + memset(&mh, 0, sizeof(mh)); + + memset(&namebuf, 0, sizeof(namebuf)); + mh.msg_name = &namebuf.addr; + mh.msg_namelen = (socklen_t)sizeof(namebuf); + + mh.msg_iov = &iov; + mh.msg_iovlen = 1; + iov.iov_base = datbuf; + iov.iov_len = maxdatlen; + +#if defined(HAVE_STRUCT_MSGHDR_MSG_CONTROL) + mh.msg_control = ctlbuf; + mh.msg_controllen = (socklen_t)maxctllen; +#endif + + if (grow_buffer) + flags |= MSG_PEEK; + + rb_io_check_closed(fptr); + if (nonblock && !MSG_DONTWAIT_RELIABLE) + rb_io_set_nonblock(fptr); + + ss = rb_recvmsg(fptr->fd, &mh, flags); + + if (ss == 0 && !rsock_is_dgram(fptr)) { + return Qnil; + } + + if (ss == -1) { + int e; + if (!nonblock && rb_io_maybe_wait_readable(errno, fptr->self, RUBY_IO_TIMEOUT_DEFAULT)) { + rb_io_check_closed(fptr); + goto retry; + } + e = errno; + if (nonblock && (e == EWOULDBLOCK || e == EAGAIN)) { + if (ex == Qfalse) { + return sym_wait_readable; + } + rb_readwrite_syserr_fail(RB_IO_WAIT_READABLE, e, "recvmsg(2) would block"); + } +#if defined(HAVE_STRUCT_MSGHDR_MSG_CONTROL) + if (!gc_done && (e == EMFILE || e == EMSGSIZE)) { + /* + * When SCM_RIGHTS hit the file descriptors limit: + * - Linux 2.6.18 causes success with MSG_CTRUNC + * - MacOS X 10.4 causes EMSGSIZE (and lost file descriptors?) + * - Solaris 11 causes EMFILE + */ + gc_and_retry: + rb_gc(); + gc_done = 1; + goto retry; + } +#else + if (NIL_P(vmaxdatlen) && grow_buffer && e == EMSGSIZE) + ss = (ssize_t)iov.iov_len; + else +#endif + rb_syserr_fail(e, "recvmsg(2)"); + } + + if (grow_buffer) { + int grown = 0; + if (NIL_P(vmaxdatlen) && ss != -1 && ss == (ssize_t)iov.iov_len) { + if (SIZE_MAX/2 < maxdatlen) + rb_raise(rb_eArgError, "max data length too big"); + maxdatlen *= 2; + grown = 1; + } +#if defined(HAVE_STRUCT_MSGHDR_MSG_CONTROL) + if (NIL_P(vmaxctllen) && (mh.msg_flags & MSG_CTRUNC)) { +#define BIG_ENOUGH_SPACE 65536 + if (BIG_ENOUGH_SPACE < maxctllen && + (socklen_t)mh.msg_controllen < (socklen_t)(maxctllen - BIG_ENOUGH_SPACE)) { + /* there are big space bug truncated. + * file descriptors limit? */ + if (!gc_done) { + rsock_discard_cmsg_resource(&mh, (flags & MSG_PEEK) != 0); + goto gc_and_retry; + } + } + else { + if (SIZE_MAX/2 < maxctllen) + rb_raise(rb_eArgError, "max control message length too big"); + maxctllen *= 2; + grown = 1; + } +#undef BIG_ENOUGH_SPACE + } +#endif + if (grown) { + rsock_discard_cmsg_resource(&mh, (flags & MSG_PEEK) != 0); + goto retry; + } + else { + grow_buffer = 0; + if (flags != orig_flags) { + rsock_discard_cmsg_resource(&mh, (flags & MSG_PEEK) != 0); + flags = orig_flags; + goto retry; + } + } + } + + if (NIL_P(dat_str)) + dat_str = rb_str_new(datbuf, ss); + else { + rb_str_resize(dat_str, ss); + rb_obj_reveal(dat_str, rb_cString); + } + +#if defined(HAVE_STRUCT_MSGHDR_MSG_CONTROL) + VALUE msg_flags = INT2NUM(mh.msg_flags); +#else + VALUE msg_flags = Qnil; +#endif + ret = rb_ary_new3(3, dat_str, + rsock_io_socket_addrinfo(sock, mh.msg_name, mh.msg_namelen), + msg_flags); + +#if defined(HAVE_STRUCT_MSGHDR_MSG_CONTROL) + family = rsock_getfamily(fptr); + if (mh.msg_controllen) { + char *msg_end = (char *)mh.msg_control + mh.msg_controllen; + for (cmh = CMSG_FIRSTHDR(&mh); cmh != NULL; cmh = CMSG_NXTHDR(&mh, cmh)) { + VALUE ctl; + char *ctl_end; + size_t clen; + if (cmh->cmsg_len == 0) { + rb_raise(rb_eTypeError, "invalid control message (cmsg_len == 0)"); + } + ctl_end = (char*)cmh + cmh->cmsg_len; + clen = (ctl_end <= msg_end ? ctl_end : msg_end) - (char*)CMSG_DATA(cmh); + ctl = ancdata_new(family, cmh->cmsg_level, cmh->cmsg_type, rb_str_new((char*)CMSG_DATA(cmh), clen)); + if (request_scm_rights) + make_io_for_unix_rights(ctl, cmh, msg_end); + else + discard_cmsg(cmh, msg_end, (flags & MSG_PEEK) != 0); + rb_ary_push(ret, ctl); + } + RB_GC_GUARD(ctl_str); + } +#endif + + return ret; +} +#endif + +#if defined(HAVE_RECVMSG) +VALUE +rsock_bsock_recvmsg(VALUE sock, VALUE dlen, VALUE flags, VALUE clen, + VALUE scm_rights) +{ + VALUE ex = Qtrue; + return bsock_recvmsg_internal(sock, dlen, flags, clen, scm_rights, ex, 0); +} +#endif + +#if defined(HAVE_RECVMSG) +VALUE +rsock_bsock_recvmsg_nonblock(VALUE sock, VALUE dlen, VALUE flags, VALUE clen, + VALUE scm_rights, VALUE ex) +{ + return bsock_recvmsg_internal(sock, dlen, flags, clen, scm_rights, ex, 1); +} +#endif + +void +rsock_init_ancdata(void) +{ +#if defined(HAVE_STRUCT_MSGHDR_MSG_CONTROL) + /* + * Document-class: Socket::AncillaryData + * + * Socket::AncillaryData represents the ancillary data (control information) + * used by sendmsg and recvmsg system call. It contains socket #family, + * control message (cmsg) #level, cmsg #type and cmsg #data. + */ + rb_cAncillaryData = rb_define_class_under(rb_cSocket, "AncillaryData", rb_cObject); + rb_define_method(rb_cAncillaryData, "initialize", ancillary_initialize, 4); + rb_define_method(rb_cAncillaryData, "inspect", ancillary_inspect, 0); + rb_define_method(rb_cAncillaryData, "family", ancillary_family_m, 0); + rb_define_method(rb_cAncillaryData, "level", ancillary_level_m, 0); + rb_define_method(rb_cAncillaryData, "type", ancillary_type_m, 0); + rb_define_method(rb_cAncillaryData, "data", ancillary_data, 0); + + rb_define_method(rb_cAncillaryData, "cmsg_is?", ancillary_cmsg_is_p, 2); + + rb_define_singleton_method(rb_cAncillaryData, "int", ancillary_s_int, 4); + rb_define_method(rb_cAncillaryData, "int", ancillary_int, 0); + + rb_define_singleton_method(rb_cAncillaryData, "unix_rights", ancillary_s_unix_rights, -1); + rb_define_method(rb_cAncillaryData, "unix_rights", ancillary_unix_rights, 0); + + rb_define_method(rb_cAncillaryData, "timestamp", ancillary_timestamp, 0); + + rb_define_singleton_method(rb_cAncillaryData, "ip_pktinfo", ancillary_s_ip_pktinfo, -1); + rb_define_method(rb_cAncillaryData, "ip_pktinfo", ancillary_ip_pktinfo, 0); + + rb_define_singleton_method(rb_cAncillaryData, "ipv6_pktinfo", ancillary_s_ipv6_pktinfo, 2); + rb_define_method(rb_cAncillaryData, "ipv6_pktinfo", ancillary_ipv6_pktinfo, 0); + rb_define_method(rb_cAncillaryData, "ipv6_pktinfo_addr", ancillary_ipv6_pktinfo_addr, 0); + rb_define_method(rb_cAncillaryData, "ipv6_pktinfo_ifindex", ancillary_ipv6_pktinfo_ifindex, 0); +#endif +#undef rb_intern + sym_wait_readable = ID2SYM(rb_intern("wait_readable")); + sym_wait_writable = ID2SYM(rb_intern("wait_writable")); +} diff --git a/ext/socket/basicsocket.c b/ext/socket/basicsocket.c new file mode 100644 index 0000000000..2fcae8eb54 --- /dev/null +++ b/ext/socket/basicsocket.c @@ -0,0 +1,792 @@ +/************************************************ + + basicsocket.c - + + created at: Thu Mar 31 12:21:29 JST 1994 + + Copyright (C) 1993-2007 Yukihiro Matsumoto + +************************************************/ + +#include "rubysocket.h" + +#ifdef _WIN32 +#define is_socket(fd) rb_w32_is_socket(fd) +#else +static int +is_socket(int fd) +{ + struct stat sbuf; + + if (fstat(fd, &sbuf) < 0) + rb_sys_fail("fstat(2)"); + return S_ISSOCK(sbuf.st_mode); +} +#endif + +static void +rsock_validate_descriptor(int descriptor) +{ + if (!is_socket(descriptor) || rb_reserved_fd_p(descriptor)) { + rb_syserr_fail(EBADF, "not a socket file descriptor"); + } +} + +/* + * 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, VALUE _descriptor) +{ + rb_io_t *fptr; + + int descriptor = RB_NUM2INT(_descriptor); + rsock_validate_descriptor(descriptor); + + VALUE sock = rsock_init_sock(rb_obj_alloc(klass), descriptor); + + 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(int argc, VALUE *argv, VALUE sock) +{ + VALUE howto; + int how; + rb_io_t *fptr; + + rb_scan_args(argc, argv, "01", &howto); + if (howto == Qnil) + how = SHUT_RDWR; + else { + how = rsock_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("shutdown(2)"); + + 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) +{ + rb_io_t *fptr; + + GetOpenFile(sock, fptr); + shutdown(fptr->fd, SHUT_RD); + if (!(fptr->mode & FMODE_WRITABLE)) { + return rb_io_close(sock); + } + fptr->mode &= ~FMODE_READABLE; + + 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) +{ + rb_io_t *fptr; + + GetOpenFile(sock, fptr); + if (!(fptr->mode & FMODE_READABLE)) { + return rb_io_close(sock); + } + shutdown(fptr->fd, SHUT_WR); + fptr->mode &= ~FMODE_WRITABLE; + + 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: + * - Integer: 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 an Integer. 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(int argc, VALUE *argv, VALUE sock) +{ + VALUE lev, optname, val; + int family, level, option; + rb_io_t *fptr; + int i; + char *v; + int vlen; + + if (argc == 1) { + lev = rb_funcall(argv[0], rb_intern("level"), 0); + optname = rb_funcall(argv[0], rb_intern("optname"), 0); + val = rb_funcall(argv[0], rb_intern("data"), 0); + } + else { + rb_scan_args(argc, argv, "30", &lev, &optname, &val); + } + + GetOpenFile(sock, fptr); + family = rsock_getfamily(fptr); + level = rsock_level_arg(family, lev); + option = rsock_optname_arg(family, 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_SOCKLEN(val); + break; + } + + rb_io_check_closed(fptr); + if (setsockopt(fptr->fd, level, option, v, vlen) < 0) + rsock_sys_fail_path("setsockopt(2)", fptr->pathv); + + 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.unpack1("i") + * + * 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, VALUE lev, VALUE optname) +{ + int level, option; + socklen_t len; + char *buf; + rb_io_t *fptr; + int family; + + GetOpenFile(sock, fptr); + family = rsock_getfamily(fptr); + level = rsock_level_arg(family, lev); + option = rsock_optname_arg(family, level, optname); + len = 256; +#ifdef _AIX + switch (option) { + case SO_DEBUG: + case SO_REUSEADDR: + case SO_KEEPALIVE: + case SO_DONTROUTE: + case SO_BROADCAST: + case SO_OOBINLINE: + /* AIX doesn't set len for boolean options */ + len = sizeof(int); + } +#endif + buf = ALLOCA_N(char,len); + + rb_io_check_closed(fptr); + + if (getsockopt(fptr->fd, level, option, buf, &len) < 0) + rsock_sys_fail_path("getsockopt(2)", fptr->pathv); + + return rsock_sockopt_new(family, level, option, rb_str_new(buf, len)); +} + +/* + * 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) +{ + union_sockaddr buf; + socklen_t len = (socklen_t)sizeof buf; + socklen_t len0 = len; + rb_io_t *fptr; + + GetOpenFile(sock, fptr); + if (getsockname(fptr->fd, &buf.addr, &len) < 0) + rb_sys_fail("getsockname(2)"); + if (len0 < len) len = len0; + 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) +{ + union_sockaddr buf; + socklen_t len = (socklen_t)sizeof buf; + socklen_t len0 = len; + rb_io_t *fptr; + + GetOpenFile(sock, fptr); + if (getpeername(fptr->fd, &buf.addr, &len) < 0) + rb_sys_fail("getpeername(2)"); + if (len0 < len) len = len0; + 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) +{ +#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(3)"); + 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(3C)"); + 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 + +/* + * 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) +{ + union_sockaddr buf; + socklen_t len = (socklen_t)sizeof buf; + socklen_t len0 = len; + rb_io_t *fptr; + + GetOpenFile(sock, fptr); + if (getsockname(fptr->fd, &buf.addr, &len) < 0) + rb_sys_fail("getsockname(2)"); + if (len0 < len) len = len0; + return rsock_fd_socket_addrinfo(fptr->fd, &buf.addr, 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) +{ + union_sockaddr buf; + socklen_t len = (socklen_t)sizeof buf; + socklen_t len0 = len; + rb_io_t *fptr; + + GetOpenFile(sock, fptr); + if (getpeername(fptr->fd, &buf.addr, &len) < 0) + rb_sys_fail("getpeername(2)"); + if (len0 < len) len = len0; + return rsock_fd_socket_addrinfo(fptr->fd, &buf.addr, len); +} + +/* + * 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 + * } + */ +VALUE +rsock_bsock_send(int argc, VALUE *argv, VALUE socket) +{ + struct rsock_send_arg arg; + VALUE flags, to; + rb_io_t *fptr; + rb_blocking_function_t *func; + const char *funcname; + + rb_scan_args(argc, argv, "21", &arg.mesg, &flags, &to); + + StringValue(arg.mesg); + if (!NIL_P(to)) { + SockAddrStringValue(to); + to = rb_str_new4(to); + arg.to = (struct sockaddr *)RSTRING_PTR(to); + arg.tolen = RSTRING_SOCKLEN(to); + func = rsock_sendto_blocking; + funcname = "sendto(2)"; + } + else { + func = rsock_send_blocking; + funcname = "send(2)"; + } + + RB_IO_POINTER(socket, fptr); + + arg.fd = fptr->fd; + arg.flags = NUM2INT(flags); + + while (true) { +#ifdef RSOCK_WAIT_BEFORE_BLOCKING + rb_io_wait(socket, RB_INT2NUM(RUBY_IO_WRITABLE), Qnil); +#endif + + ssize_t n = (ssize_t)rb_io_blocking_region(fptr, func, &arg); + + if (n >= 0) return SSIZET2NUM(n); + + if (rb_io_maybe_wait_writable(errno, socket, RUBY_IO_TIMEOUT_DEFAULT)) { + continue; + } + + rb_sys_fail(funcname); + } +} + +/* + * call-seq: + * basicsocket.do_not_reverse_lookup => true or false + * + * Gets the do_not_reverse_lookup flag of _basicsocket_. + * + * require 'socket' + * + * BasicSocket.do_not_reverse_lookup = false + * TCPSocket.open("www.ruby-lang.org", 80) {|sock| + * p sock.do_not_reverse_lookup #=> false + * } + * BasicSocket.do_not_reverse_lookup = true + * TCPSocket.open("www.ruby-lang.org", 80) {|sock| + * p sock.do_not_reverse_lookup #=> true + * } + */ +static VALUE +bsock_do_not_reverse_lookup(VALUE sock) +{ + 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_. + * + * TCPSocket.open("www.ruby-lang.org", 80) {|sock| + * p sock.do_not_reverse_lookup #=> true + * p sock.peeraddr #=> ["AF_INET", 80, "221.186.184.68", "221.186.184.68"] + * sock.do_not_reverse_lookup = false + * p sock.peeraddr #=> ["AF_INET", 80, "carbon.ruby-lang.org", "54.163.249.195"] + * } + * + */ +static VALUE +bsock_do_not_reverse_lookup_set(VALUE sock, VALUE state) +{ + rb_io_t *fptr; + + GetOpenFile(sock, fptr); + if (RTEST(state)) { + fptr->mode |= FMODE_NOREVLOOKUP; + } + else { + fptr->mode &= ~FMODE_NOREVLOOKUP; + } + return sock; +} + +/* + * call-seq: + * basicsocket.recv(maxlen[, flags[, outbuf]]) => mesg + * + * Receives a message. + * + * _maxlen_ is the maximum number of bytes to receive. + * + * _flags_ should be a bitwise OR of Socket::MSG_* constants. + * + * _outbuf_ will contain only the received data after the method call + * even if it is not empty at the beginning. + * + * 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(int argc, VALUE *argv, VALUE sock) +{ + return rsock_s_recvfrom(sock, argc, argv, RECV_RECV); +} + +/* :nodoc: */ +static VALUE +bsock_recv_nonblock(VALUE sock, VALUE len, VALUE flg, VALUE str, VALUE ex) +{ + return rsock_s_recvfrom_nonblock(sock, len, flg, str, ex, 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 _) +{ + return rsock_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, VALUE val) +{ + rsock_do_not_reverse_lookup = RTEST(val); + return val; +} + +void +rsock_init_basicsocket(void) +{ + /* + * Document-class: BasicSocket < IO + * + * BasicSocket is the super class for all the Socket classes. + */ + rb_cBasicSocket = rb_define_class("BasicSocket", rb_cIO); + rb_undef_method(rb_cBasicSocket, "initialize"); + + rb_define_singleton_method(rb_cBasicSocket, "do_not_reverse_lookup", + bsock_do_not_rev_lookup, 0); + rb_define_singleton_method(rb_cBasicSocket, "do_not_reverse_lookup=", + bsock_do_not_rev_lookup_set, 1); + rb_define_singleton_method(rb_cBasicSocket, "for_fd", bsock_s_for_fd, 1); + + rb_define_method(rb_cBasicSocket, "close_read", bsock_close_read, 0); + rb_define_method(rb_cBasicSocket, "close_write", bsock_close_write, 0); + rb_define_method(rb_cBasicSocket, "shutdown", bsock_shutdown, -1); + rb_define_method(rb_cBasicSocket, "setsockopt", bsock_setsockopt, -1); + rb_define_method(rb_cBasicSocket, "getsockopt", bsock_getsockopt, 2); + rb_define_method(rb_cBasicSocket, "getsockname", bsock_getsockname, 0); + rb_define_method(rb_cBasicSocket, "getpeername", bsock_getpeername, 0); + rb_define_method(rb_cBasicSocket, "getpeereid", bsock_getpeereid, 0); + rb_define_method(rb_cBasicSocket, "local_address", bsock_local_address, 0); + rb_define_method(rb_cBasicSocket, "remote_address", bsock_remote_address, 0); + rb_define_method(rb_cBasicSocket, "send", rsock_bsock_send, -1); + rb_define_method(rb_cBasicSocket, "recv", bsock_recv, -1); + + rb_define_method(rb_cBasicSocket, "do_not_reverse_lookup", bsock_do_not_reverse_lookup, 0); + rb_define_method(rb_cBasicSocket, "do_not_reverse_lookup=", bsock_do_not_reverse_lookup_set, 1); + + /* for ext/socket/lib/socket.rb use only: */ + rb_define_private_method(rb_cBasicSocket, + "__recv_nonblock", bsock_recv_nonblock, 4); + +#if MSG_DONTWAIT_RELIABLE + rb_define_private_method(rb_cBasicSocket, + "__read_nonblock", rsock_read_nonblock, 3); + rb_define_private_method(rb_cBasicSocket, + "__write_nonblock", rsock_write_nonblock, 2); +#endif + + /* in ancdata.c */ + rb_define_private_method(rb_cBasicSocket, "__sendmsg", + rsock_bsock_sendmsg, 4); + rb_define_private_method(rb_cBasicSocket, "__sendmsg_nonblock", + rsock_bsock_sendmsg_nonblock, 5); + rb_define_private_method(rb_cBasicSocket, "__recvmsg", + rsock_bsock_recvmsg, 4); + rb_define_private_method(rb_cBasicSocket, "__recvmsg_nonblock", + rsock_bsock_recvmsg_nonblock, 5); + +} diff --git a/ext/socket/constants.c b/ext/socket/constants.c new file mode 100644 index 0000000000..1213f2ae17 --- /dev/null +++ b/ext/socket/constants.c @@ -0,0 +1,144 @@ +/************************************************ + + constants.c - + + created at: Thu Mar 31 12:21:29 JST 1994 + + Copyright (C) 1993-2007 Yukihiro Matsumoto + +************************************************/ + +#include "rubysocket.h" + +static VALUE rb_mSockConst; + +#include "constdefs.c" + +static int +constant_arg(VALUE arg, int (*str_to_int)(const char*, long, int*), const char *errmsg) +{ + VALUE tmp; + char *ptr; + int ret; + + if (SYMBOL_P(arg)) { + arg = rb_sym2str(arg); + goto str; + } + else if (!NIL_P(tmp = rb_check_string_type(arg))) { + arg = tmp; + str: + 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; +} + +int +rsock_family_arg(VALUE domain) +{ + /* convert AF_INET, etc. */ + return constant_arg(domain, rsock_family_to_int, "unknown socket domain"); +} + +int +rsock_socktype_arg(VALUE type) +{ + /* convert SOCK_STREAM, etc. */ + return constant_arg(type, rsock_socktype_to_int, "unknown socket type"); +} + +int +rsock_level_arg(int family, VALUE level) +{ + /* convert SOL_SOCKET, IPPROTO_TCP, etc. */ + if (IS_IP_FAMILY(family)) { + return constant_arg(level, rsock_ip_level_to_int, "unknown protocol level"); + } + else { + return constant_arg(level, rsock_unknown_level_to_int, "unknown protocol level"); + } +} + +int +rsock_optname_arg(int family, int level, VALUE optname) +{ + if (IS_IP_FAMILY(family)) { + switch (level) { + case SOL_SOCKET: + return constant_arg(optname, rsock_so_optname_to_int, "unknown socket level option name"); + case IPPROTO_IP: + return constant_arg(optname, rsock_ip_optname_to_int, "unknown IP level option name"); +#ifdef IPPROTO_IPV6 + case IPPROTO_IPV6: + return constant_arg(optname, rsock_ipv6_optname_to_int, "unknown IPv6 level option name"); +#endif + case IPPROTO_TCP: + return constant_arg(optname, rsock_tcp_optname_to_int, "unknown TCP level option name"); + case IPPROTO_UDP: + return constant_arg(optname, rsock_udp_optname_to_int, "unknown UDP level option name"); + default: + return NUM2INT(optname); + } + } + else { + switch (level) { + case SOL_SOCKET: + return constant_arg(optname, rsock_so_optname_to_int, "unknown socket level option name"); + default: + return NUM2INT(optname); + } + } +} + +int +rsock_cmsg_type_arg(int family, int level, VALUE type) +{ + if (IS_IP_FAMILY(family)) { + switch (level) { + case SOL_SOCKET: + return constant_arg(type, rsock_scm_optname_to_int, "unknown UNIX control message"); + case IPPROTO_IP: + return constant_arg(type, rsock_ip_optname_to_int, "unknown IP control message"); +#ifdef IPPROTO_IPV6 + case IPPROTO_IPV6: + return constant_arg(type, rsock_ipv6_optname_to_int, "unknown IPv6 control message"); +#endif + case IPPROTO_TCP: + return constant_arg(type, rsock_tcp_optname_to_int, "unknown TCP control message"); + case IPPROTO_UDP: + return constant_arg(type, rsock_udp_optname_to_int, "unknown UDP control message"); + default: + return NUM2INT(type); + } + } + else { + switch (level) { + case SOL_SOCKET: + return constant_arg(type, rsock_scm_optname_to_int, "unknown UNIX control message"); + default: + return NUM2INT(type); + } + } +} + +int +rsock_shutdown_how_arg(VALUE how) +{ + /* convert SHUT_RD, SHUT_WR, SHUT_RDWR. */ + return constant_arg(how, rsock_shutdown_how_to_int, "unknown shutdown argument"); +} + +/* + * Socket::Constants module + */ +void +rsock_init_socket_constants(void) +{ + /* constants */ + init_constants(); +} diff --git a/ext/socket/depend b/ext/socket/depend index cca6d4e62a..77f6239a3d 100644 --- a/ext/socket/depend +++ b/ext/socket/depend @@ -1,3 +1,3254 @@ -socket.o : socket.c $(hdrdir)/ruby.h $(topdir)/config.h $(hdrdir)/defines.h $(hdrdir)/rubyio.h $(hdrdir)/rubysig.h sockport.h -getnameinfo.o: getnameinfo.c $(topdir)/config.h addrinfo.h sockport.h -getaddrinfo.o: getaddrinfo.c $(topdir)/config.h addrinfo.h sockport.h +srcs: constdefs.h constdefs.c + +getnameinfo.o: getnameinfo.c $(arch_hdrdir)/ruby/config.h addrinfo.h sockport.h rubysocket.h +getaddrinfo.o: getaddrinfo.c $(arch_hdrdir)/ruby/config.h addrinfo.h sockport.h + +constdefs.h: $(srcdir)/mkconstants.rb + @echo "generating constant definitions" + @$(RUBY) $(srcdir)/mkconstants.rb -H constdefs.h -o constdefs.c + +constdefs.c: constdefs.h + +# AUTOGENERATED DEPENDENCIES START +ancdata.o: $(RUBY_EXTCONF_H) +ancdata.o: $(arch_hdrdir)/ruby/config.h +ancdata.o: $(hdrdir)/ruby/assert.h +ancdata.o: $(hdrdir)/ruby/atomic.h +ancdata.o: $(hdrdir)/ruby/backward.h +ancdata.o: $(hdrdir)/ruby/backward/2/assume.h +ancdata.o: $(hdrdir)/ruby/backward/2/attributes.h +ancdata.o: $(hdrdir)/ruby/backward/2/bool.h +ancdata.o: $(hdrdir)/ruby/backward/2/gcc_version_since.h +ancdata.o: $(hdrdir)/ruby/backward/2/inttypes.h +ancdata.o: $(hdrdir)/ruby/backward/2/limits.h +ancdata.o: $(hdrdir)/ruby/backward/2/long_long.h +ancdata.o: $(hdrdir)/ruby/backward/2/stdalign.h +ancdata.o: $(hdrdir)/ruby/backward/2/stdarg.h +ancdata.o: $(hdrdir)/ruby/defines.h +ancdata.o: $(hdrdir)/ruby/encoding.h +ancdata.o: $(hdrdir)/ruby/fiber/scheduler.h +ancdata.o: $(hdrdir)/ruby/intern.h +ancdata.o: $(hdrdir)/ruby/internal/abi.h +ancdata.o: $(hdrdir)/ruby/internal/anyargs.h +ancdata.o: $(hdrdir)/ruby/internal/arithmetic.h +ancdata.o: $(hdrdir)/ruby/internal/arithmetic/char.h +ancdata.o: $(hdrdir)/ruby/internal/arithmetic/double.h +ancdata.o: $(hdrdir)/ruby/internal/arithmetic/fixnum.h +ancdata.o: $(hdrdir)/ruby/internal/arithmetic/gid_t.h +ancdata.o: $(hdrdir)/ruby/internal/arithmetic/int.h +ancdata.o: $(hdrdir)/ruby/internal/arithmetic/intptr_t.h +ancdata.o: $(hdrdir)/ruby/internal/arithmetic/long.h +ancdata.o: $(hdrdir)/ruby/internal/arithmetic/long_long.h +ancdata.o: $(hdrdir)/ruby/internal/arithmetic/mode_t.h +ancdata.o: $(hdrdir)/ruby/internal/arithmetic/off_t.h +ancdata.o: $(hdrdir)/ruby/internal/arithmetic/pid_t.h +ancdata.o: $(hdrdir)/ruby/internal/arithmetic/short.h +ancdata.o: $(hdrdir)/ruby/internal/arithmetic/size_t.h +ancdata.o: $(hdrdir)/ruby/internal/arithmetic/st_data_t.h +ancdata.o: $(hdrdir)/ruby/internal/arithmetic/uid_t.h +ancdata.o: $(hdrdir)/ruby/internal/assume.h +ancdata.o: $(hdrdir)/ruby/internal/attr/alloc_size.h +ancdata.o: $(hdrdir)/ruby/internal/attr/artificial.h +ancdata.o: $(hdrdir)/ruby/internal/attr/cold.h +ancdata.o: $(hdrdir)/ruby/internal/attr/const.h +ancdata.o: $(hdrdir)/ruby/internal/attr/constexpr.h +ancdata.o: $(hdrdir)/ruby/internal/attr/deprecated.h +ancdata.o: $(hdrdir)/ruby/internal/attr/diagnose_if.h +ancdata.o: $(hdrdir)/ruby/internal/attr/enum_extensibility.h +ancdata.o: $(hdrdir)/ruby/internal/attr/error.h +ancdata.o: $(hdrdir)/ruby/internal/attr/flag_enum.h +ancdata.o: $(hdrdir)/ruby/internal/attr/forceinline.h +ancdata.o: $(hdrdir)/ruby/internal/attr/format.h +ancdata.o: $(hdrdir)/ruby/internal/attr/maybe_unused.h +ancdata.o: $(hdrdir)/ruby/internal/attr/noalias.h +ancdata.o: $(hdrdir)/ruby/internal/attr/nodiscard.h +ancdata.o: $(hdrdir)/ruby/internal/attr/noexcept.h +ancdata.o: $(hdrdir)/ruby/internal/attr/noinline.h +ancdata.o: $(hdrdir)/ruby/internal/attr/nonnull.h +ancdata.o: $(hdrdir)/ruby/internal/attr/noreturn.h +ancdata.o: $(hdrdir)/ruby/internal/attr/packed_struct.h +ancdata.o: $(hdrdir)/ruby/internal/attr/pure.h +ancdata.o: $(hdrdir)/ruby/internal/attr/restrict.h +ancdata.o: $(hdrdir)/ruby/internal/attr/returns_nonnull.h +ancdata.o: $(hdrdir)/ruby/internal/attr/warning.h +ancdata.o: $(hdrdir)/ruby/internal/attr/weakref.h +ancdata.o: $(hdrdir)/ruby/internal/cast.h +ancdata.o: $(hdrdir)/ruby/internal/compiler_is.h +ancdata.o: $(hdrdir)/ruby/internal/compiler_is/apple.h +ancdata.o: $(hdrdir)/ruby/internal/compiler_is/clang.h +ancdata.o: $(hdrdir)/ruby/internal/compiler_is/gcc.h +ancdata.o: $(hdrdir)/ruby/internal/compiler_is/intel.h +ancdata.o: $(hdrdir)/ruby/internal/compiler_is/msvc.h +ancdata.o: $(hdrdir)/ruby/internal/compiler_is/sunpro.h +ancdata.o: $(hdrdir)/ruby/internal/compiler_since.h +ancdata.o: $(hdrdir)/ruby/internal/config.h +ancdata.o: $(hdrdir)/ruby/internal/constant_p.h +ancdata.o: $(hdrdir)/ruby/internal/core.h +ancdata.o: $(hdrdir)/ruby/internal/core/rarray.h +ancdata.o: $(hdrdir)/ruby/internal/core/rbasic.h +ancdata.o: $(hdrdir)/ruby/internal/core/rbignum.h +ancdata.o: $(hdrdir)/ruby/internal/core/rclass.h +ancdata.o: $(hdrdir)/ruby/internal/core/rdata.h +ancdata.o: $(hdrdir)/ruby/internal/core/rfile.h +ancdata.o: $(hdrdir)/ruby/internal/core/rhash.h +ancdata.o: $(hdrdir)/ruby/internal/core/robject.h +ancdata.o: $(hdrdir)/ruby/internal/core/rregexp.h +ancdata.o: $(hdrdir)/ruby/internal/core/rstring.h +ancdata.o: $(hdrdir)/ruby/internal/core/rstruct.h +ancdata.o: $(hdrdir)/ruby/internal/core/rtypeddata.h +ancdata.o: $(hdrdir)/ruby/internal/ctype.h +ancdata.o: $(hdrdir)/ruby/internal/dllexport.h +ancdata.o: $(hdrdir)/ruby/internal/dosish.h +ancdata.o: $(hdrdir)/ruby/internal/encoding/coderange.h +ancdata.o: $(hdrdir)/ruby/internal/encoding/ctype.h +ancdata.o: $(hdrdir)/ruby/internal/encoding/encoding.h +ancdata.o: $(hdrdir)/ruby/internal/encoding/pathname.h +ancdata.o: $(hdrdir)/ruby/internal/encoding/re.h +ancdata.o: $(hdrdir)/ruby/internal/encoding/sprintf.h +ancdata.o: $(hdrdir)/ruby/internal/encoding/string.h +ancdata.o: $(hdrdir)/ruby/internal/encoding/symbol.h +ancdata.o: $(hdrdir)/ruby/internal/encoding/transcode.h +ancdata.o: $(hdrdir)/ruby/internal/error.h +ancdata.o: $(hdrdir)/ruby/internal/eval.h +ancdata.o: $(hdrdir)/ruby/internal/event.h +ancdata.o: $(hdrdir)/ruby/internal/fl_type.h +ancdata.o: $(hdrdir)/ruby/internal/gc.h +ancdata.o: $(hdrdir)/ruby/internal/glob.h +ancdata.o: $(hdrdir)/ruby/internal/globals.h +ancdata.o: $(hdrdir)/ruby/internal/has/attribute.h +ancdata.o: $(hdrdir)/ruby/internal/has/builtin.h +ancdata.o: $(hdrdir)/ruby/internal/has/c_attribute.h +ancdata.o: $(hdrdir)/ruby/internal/has/cpp_attribute.h +ancdata.o: $(hdrdir)/ruby/internal/has/declspec_attribute.h +ancdata.o: $(hdrdir)/ruby/internal/has/extension.h +ancdata.o: $(hdrdir)/ruby/internal/has/feature.h +ancdata.o: $(hdrdir)/ruby/internal/has/warning.h +ancdata.o: $(hdrdir)/ruby/internal/intern/array.h +ancdata.o: $(hdrdir)/ruby/internal/intern/bignum.h +ancdata.o: $(hdrdir)/ruby/internal/intern/class.h +ancdata.o: $(hdrdir)/ruby/internal/intern/compar.h +ancdata.o: $(hdrdir)/ruby/internal/intern/complex.h +ancdata.o: $(hdrdir)/ruby/internal/intern/cont.h +ancdata.o: $(hdrdir)/ruby/internal/intern/dir.h +ancdata.o: $(hdrdir)/ruby/internal/intern/enum.h +ancdata.o: $(hdrdir)/ruby/internal/intern/enumerator.h +ancdata.o: $(hdrdir)/ruby/internal/intern/error.h +ancdata.o: $(hdrdir)/ruby/internal/intern/eval.h +ancdata.o: $(hdrdir)/ruby/internal/intern/file.h +ancdata.o: $(hdrdir)/ruby/internal/intern/hash.h +ancdata.o: $(hdrdir)/ruby/internal/intern/io.h +ancdata.o: $(hdrdir)/ruby/internal/intern/load.h +ancdata.o: $(hdrdir)/ruby/internal/intern/marshal.h +ancdata.o: $(hdrdir)/ruby/internal/intern/numeric.h +ancdata.o: $(hdrdir)/ruby/internal/intern/object.h +ancdata.o: $(hdrdir)/ruby/internal/intern/parse.h +ancdata.o: $(hdrdir)/ruby/internal/intern/proc.h +ancdata.o: $(hdrdir)/ruby/internal/intern/process.h +ancdata.o: $(hdrdir)/ruby/internal/intern/random.h +ancdata.o: $(hdrdir)/ruby/internal/intern/range.h +ancdata.o: $(hdrdir)/ruby/internal/intern/rational.h +ancdata.o: $(hdrdir)/ruby/internal/intern/re.h +ancdata.o: $(hdrdir)/ruby/internal/intern/ruby.h +ancdata.o: $(hdrdir)/ruby/internal/intern/select.h +ancdata.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +ancdata.o: $(hdrdir)/ruby/internal/intern/set.h +ancdata.o: $(hdrdir)/ruby/internal/intern/signal.h +ancdata.o: $(hdrdir)/ruby/internal/intern/sprintf.h +ancdata.o: $(hdrdir)/ruby/internal/intern/string.h +ancdata.o: $(hdrdir)/ruby/internal/intern/struct.h +ancdata.o: $(hdrdir)/ruby/internal/intern/thread.h +ancdata.o: $(hdrdir)/ruby/internal/intern/time.h +ancdata.o: $(hdrdir)/ruby/internal/intern/variable.h +ancdata.o: $(hdrdir)/ruby/internal/intern/vm.h +ancdata.o: $(hdrdir)/ruby/internal/interpreter.h +ancdata.o: $(hdrdir)/ruby/internal/iterator.h +ancdata.o: $(hdrdir)/ruby/internal/memory.h +ancdata.o: $(hdrdir)/ruby/internal/method.h +ancdata.o: $(hdrdir)/ruby/internal/module.h +ancdata.o: $(hdrdir)/ruby/internal/newobj.h +ancdata.o: $(hdrdir)/ruby/internal/scan_args.h +ancdata.o: $(hdrdir)/ruby/internal/special_consts.h +ancdata.o: $(hdrdir)/ruby/internal/static_assert.h +ancdata.o: $(hdrdir)/ruby/internal/stdalign.h +ancdata.o: $(hdrdir)/ruby/internal/stdbool.h +ancdata.o: $(hdrdir)/ruby/internal/stdckdint.h +ancdata.o: $(hdrdir)/ruby/internal/symbol.h +ancdata.o: $(hdrdir)/ruby/internal/value.h +ancdata.o: $(hdrdir)/ruby/internal/value_type.h +ancdata.o: $(hdrdir)/ruby/internal/variable.h +ancdata.o: $(hdrdir)/ruby/internal/warning_push.h +ancdata.o: $(hdrdir)/ruby/internal/xmalloc.h +ancdata.o: $(hdrdir)/ruby/io.h +ancdata.o: $(hdrdir)/ruby/missing.h +ancdata.o: $(hdrdir)/ruby/onigmo.h +ancdata.o: $(hdrdir)/ruby/oniguruma.h +ancdata.o: $(hdrdir)/ruby/ruby.h +ancdata.o: $(hdrdir)/ruby/st.h +ancdata.o: $(hdrdir)/ruby/subst.h +ancdata.o: $(hdrdir)/ruby/thread.h +ancdata.o: $(hdrdir)/ruby/thread_native.h +ancdata.o: $(hdrdir)/ruby/util.h +ancdata.o: $(hdrdir)/ruby/version.h +ancdata.o: $(top_srcdir)/ccan/check_type/check_type.h +ancdata.o: $(top_srcdir)/ccan/container_of/container_of.h +ancdata.o: $(top_srcdir)/ccan/list/list.h +ancdata.o: $(top_srcdir)/ccan/str/str.h +ancdata.o: $(top_srcdir)/encindex.h +ancdata.o: $(top_srcdir)/id_table.h +ancdata.o: $(top_srcdir)/internal.h +ancdata.o: $(top_srcdir)/internal/array.h +ancdata.o: $(top_srcdir)/internal/basic_operators.h +ancdata.o: $(top_srcdir)/internal/box.h +ancdata.o: $(top_srcdir)/internal/compilers.h +ancdata.o: $(top_srcdir)/internal/error.h +ancdata.o: $(top_srcdir)/internal/gc.h +ancdata.o: $(top_srcdir)/internal/imemo.h +ancdata.o: $(top_srcdir)/internal/io.h +ancdata.o: $(top_srcdir)/internal/sanitizers.h +ancdata.o: $(top_srcdir)/internal/serial.h +ancdata.o: $(top_srcdir)/internal/set_table.h +ancdata.o: $(top_srcdir)/internal/static_assert.h +ancdata.o: $(top_srcdir)/internal/string.h +ancdata.o: $(top_srcdir)/internal/thread.h +ancdata.o: $(top_srcdir)/internal/vm.h +ancdata.o: $(top_srcdir)/internal/warnings.h +ancdata.o: $(top_srcdir)/method.h +ancdata.o: $(top_srcdir)/node.h +ancdata.o: $(top_srcdir)/ruby_assert.h +ancdata.o: $(top_srcdir)/ruby_atomic.h +ancdata.o: $(top_srcdir)/rubyparser.h +ancdata.o: $(top_srcdir)/shape.h +ancdata.o: $(top_srcdir)/thread_pthread.h +ancdata.o: $(top_srcdir)/vm_core.h +ancdata.o: $(top_srcdir)/vm_opts.h +ancdata.o: ancdata.c +ancdata.o: constdefs.h +ancdata.o: rubysocket.h +ancdata.o: sockport.h +ancdata.o: {$(VPATH)}id.h +basicsocket.o: $(RUBY_EXTCONF_H) +basicsocket.o: $(arch_hdrdir)/ruby/config.h +basicsocket.o: $(hdrdir)/ruby/assert.h +basicsocket.o: $(hdrdir)/ruby/atomic.h +basicsocket.o: $(hdrdir)/ruby/backward.h +basicsocket.o: $(hdrdir)/ruby/backward/2/assume.h +basicsocket.o: $(hdrdir)/ruby/backward/2/attributes.h +basicsocket.o: $(hdrdir)/ruby/backward/2/bool.h +basicsocket.o: $(hdrdir)/ruby/backward/2/gcc_version_since.h +basicsocket.o: $(hdrdir)/ruby/backward/2/inttypes.h +basicsocket.o: $(hdrdir)/ruby/backward/2/limits.h +basicsocket.o: $(hdrdir)/ruby/backward/2/long_long.h +basicsocket.o: $(hdrdir)/ruby/backward/2/stdalign.h +basicsocket.o: $(hdrdir)/ruby/backward/2/stdarg.h +basicsocket.o: $(hdrdir)/ruby/defines.h +basicsocket.o: $(hdrdir)/ruby/encoding.h +basicsocket.o: $(hdrdir)/ruby/fiber/scheduler.h +basicsocket.o: $(hdrdir)/ruby/intern.h +basicsocket.o: $(hdrdir)/ruby/internal/abi.h +basicsocket.o: $(hdrdir)/ruby/internal/anyargs.h +basicsocket.o: $(hdrdir)/ruby/internal/arithmetic.h +basicsocket.o: $(hdrdir)/ruby/internal/arithmetic/char.h +basicsocket.o: $(hdrdir)/ruby/internal/arithmetic/double.h +basicsocket.o: $(hdrdir)/ruby/internal/arithmetic/fixnum.h +basicsocket.o: $(hdrdir)/ruby/internal/arithmetic/gid_t.h +basicsocket.o: $(hdrdir)/ruby/internal/arithmetic/int.h +basicsocket.o: $(hdrdir)/ruby/internal/arithmetic/intptr_t.h +basicsocket.o: $(hdrdir)/ruby/internal/arithmetic/long.h +basicsocket.o: $(hdrdir)/ruby/internal/arithmetic/long_long.h +basicsocket.o: $(hdrdir)/ruby/internal/arithmetic/mode_t.h +basicsocket.o: $(hdrdir)/ruby/internal/arithmetic/off_t.h +basicsocket.o: $(hdrdir)/ruby/internal/arithmetic/pid_t.h +basicsocket.o: $(hdrdir)/ruby/internal/arithmetic/short.h +basicsocket.o: $(hdrdir)/ruby/internal/arithmetic/size_t.h +basicsocket.o: $(hdrdir)/ruby/internal/arithmetic/st_data_t.h +basicsocket.o: $(hdrdir)/ruby/internal/arithmetic/uid_t.h +basicsocket.o: $(hdrdir)/ruby/internal/assume.h +basicsocket.o: $(hdrdir)/ruby/internal/attr/alloc_size.h +basicsocket.o: $(hdrdir)/ruby/internal/attr/artificial.h +basicsocket.o: $(hdrdir)/ruby/internal/attr/cold.h +basicsocket.o: $(hdrdir)/ruby/internal/attr/const.h +basicsocket.o: $(hdrdir)/ruby/internal/attr/constexpr.h +basicsocket.o: $(hdrdir)/ruby/internal/attr/deprecated.h +basicsocket.o: $(hdrdir)/ruby/internal/attr/diagnose_if.h +basicsocket.o: $(hdrdir)/ruby/internal/attr/enum_extensibility.h +basicsocket.o: $(hdrdir)/ruby/internal/attr/error.h +basicsocket.o: $(hdrdir)/ruby/internal/attr/flag_enum.h +basicsocket.o: $(hdrdir)/ruby/internal/attr/forceinline.h +basicsocket.o: $(hdrdir)/ruby/internal/attr/format.h +basicsocket.o: $(hdrdir)/ruby/internal/attr/maybe_unused.h +basicsocket.o: $(hdrdir)/ruby/internal/attr/noalias.h +basicsocket.o: $(hdrdir)/ruby/internal/attr/nodiscard.h +basicsocket.o: $(hdrdir)/ruby/internal/attr/noexcept.h +basicsocket.o: $(hdrdir)/ruby/internal/attr/noinline.h +basicsocket.o: $(hdrdir)/ruby/internal/attr/nonnull.h +basicsocket.o: $(hdrdir)/ruby/internal/attr/noreturn.h +basicsocket.o: $(hdrdir)/ruby/internal/attr/packed_struct.h +basicsocket.o: $(hdrdir)/ruby/internal/attr/pure.h +basicsocket.o: $(hdrdir)/ruby/internal/attr/restrict.h +basicsocket.o: $(hdrdir)/ruby/internal/attr/returns_nonnull.h +basicsocket.o: $(hdrdir)/ruby/internal/attr/warning.h +basicsocket.o: $(hdrdir)/ruby/internal/attr/weakref.h +basicsocket.o: $(hdrdir)/ruby/internal/cast.h +basicsocket.o: $(hdrdir)/ruby/internal/compiler_is.h +basicsocket.o: $(hdrdir)/ruby/internal/compiler_is/apple.h +basicsocket.o: $(hdrdir)/ruby/internal/compiler_is/clang.h +basicsocket.o: $(hdrdir)/ruby/internal/compiler_is/gcc.h +basicsocket.o: $(hdrdir)/ruby/internal/compiler_is/intel.h +basicsocket.o: $(hdrdir)/ruby/internal/compiler_is/msvc.h +basicsocket.o: $(hdrdir)/ruby/internal/compiler_is/sunpro.h +basicsocket.o: $(hdrdir)/ruby/internal/compiler_since.h +basicsocket.o: $(hdrdir)/ruby/internal/config.h +basicsocket.o: $(hdrdir)/ruby/internal/constant_p.h +basicsocket.o: $(hdrdir)/ruby/internal/core.h +basicsocket.o: $(hdrdir)/ruby/internal/core/rarray.h +basicsocket.o: $(hdrdir)/ruby/internal/core/rbasic.h +basicsocket.o: $(hdrdir)/ruby/internal/core/rbignum.h +basicsocket.o: $(hdrdir)/ruby/internal/core/rclass.h +basicsocket.o: $(hdrdir)/ruby/internal/core/rdata.h +basicsocket.o: $(hdrdir)/ruby/internal/core/rfile.h +basicsocket.o: $(hdrdir)/ruby/internal/core/rhash.h +basicsocket.o: $(hdrdir)/ruby/internal/core/robject.h +basicsocket.o: $(hdrdir)/ruby/internal/core/rregexp.h +basicsocket.o: $(hdrdir)/ruby/internal/core/rstring.h +basicsocket.o: $(hdrdir)/ruby/internal/core/rstruct.h +basicsocket.o: $(hdrdir)/ruby/internal/core/rtypeddata.h +basicsocket.o: $(hdrdir)/ruby/internal/ctype.h +basicsocket.o: $(hdrdir)/ruby/internal/dllexport.h +basicsocket.o: $(hdrdir)/ruby/internal/dosish.h +basicsocket.o: $(hdrdir)/ruby/internal/encoding/coderange.h +basicsocket.o: $(hdrdir)/ruby/internal/encoding/ctype.h +basicsocket.o: $(hdrdir)/ruby/internal/encoding/encoding.h +basicsocket.o: $(hdrdir)/ruby/internal/encoding/pathname.h +basicsocket.o: $(hdrdir)/ruby/internal/encoding/re.h +basicsocket.o: $(hdrdir)/ruby/internal/encoding/sprintf.h +basicsocket.o: $(hdrdir)/ruby/internal/encoding/string.h +basicsocket.o: $(hdrdir)/ruby/internal/encoding/symbol.h +basicsocket.o: $(hdrdir)/ruby/internal/encoding/transcode.h +basicsocket.o: $(hdrdir)/ruby/internal/error.h +basicsocket.o: $(hdrdir)/ruby/internal/eval.h +basicsocket.o: $(hdrdir)/ruby/internal/event.h +basicsocket.o: $(hdrdir)/ruby/internal/fl_type.h +basicsocket.o: $(hdrdir)/ruby/internal/gc.h +basicsocket.o: $(hdrdir)/ruby/internal/glob.h +basicsocket.o: $(hdrdir)/ruby/internal/globals.h +basicsocket.o: $(hdrdir)/ruby/internal/has/attribute.h +basicsocket.o: $(hdrdir)/ruby/internal/has/builtin.h +basicsocket.o: $(hdrdir)/ruby/internal/has/c_attribute.h +basicsocket.o: $(hdrdir)/ruby/internal/has/cpp_attribute.h +basicsocket.o: $(hdrdir)/ruby/internal/has/declspec_attribute.h +basicsocket.o: $(hdrdir)/ruby/internal/has/extension.h +basicsocket.o: $(hdrdir)/ruby/internal/has/feature.h +basicsocket.o: $(hdrdir)/ruby/internal/has/warning.h +basicsocket.o: $(hdrdir)/ruby/internal/intern/array.h +basicsocket.o: $(hdrdir)/ruby/internal/intern/bignum.h +basicsocket.o: $(hdrdir)/ruby/internal/intern/class.h +basicsocket.o: $(hdrdir)/ruby/internal/intern/compar.h +basicsocket.o: $(hdrdir)/ruby/internal/intern/complex.h +basicsocket.o: $(hdrdir)/ruby/internal/intern/cont.h +basicsocket.o: $(hdrdir)/ruby/internal/intern/dir.h +basicsocket.o: $(hdrdir)/ruby/internal/intern/enum.h +basicsocket.o: $(hdrdir)/ruby/internal/intern/enumerator.h +basicsocket.o: $(hdrdir)/ruby/internal/intern/error.h +basicsocket.o: $(hdrdir)/ruby/internal/intern/eval.h +basicsocket.o: $(hdrdir)/ruby/internal/intern/file.h +basicsocket.o: $(hdrdir)/ruby/internal/intern/hash.h +basicsocket.o: $(hdrdir)/ruby/internal/intern/io.h +basicsocket.o: $(hdrdir)/ruby/internal/intern/load.h +basicsocket.o: $(hdrdir)/ruby/internal/intern/marshal.h +basicsocket.o: $(hdrdir)/ruby/internal/intern/numeric.h +basicsocket.o: $(hdrdir)/ruby/internal/intern/object.h +basicsocket.o: $(hdrdir)/ruby/internal/intern/parse.h +basicsocket.o: $(hdrdir)/ruby/internal/intern/proc.h +basicsocket.o: $(hdrdir)/ruby/internal/intern/process.h +basicsocket.o: $(hdrdir)/ruby/internal/intern/random.h +basicsocket.o: $(hdrdir)/ruby/internal/intern/range.h +basicsocket.o: $(hdrdir)/ruby/internal/intern/rational.h +basicsocket.o: $(hdrdir)/ruby/internal/intern/re.h +basicsocket.o: $(hdrdir)/ruby/internal/intern/ruby.h +basicsocket.o: $(hdrdir)/ruby/internal/intern/select.h +basicsocket.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +basicsocket.o: $(hdrdir)/ruby/internal/intern/set.h +basicsocket.o: $(hdrdir)/ruby/internal/intern/signal.h +basicsocket.o: $(hdrdir)/ruby/internal/intern/sprintf.h +basicsocket.o: $(hdrdir)/ruby/internal/intern/string.h +basicsocket.o: $(hdrdir)/ruby/internal/intern/struct.h +basicsocket.o: $(hdrdir)/ruby/internal/intern/thread.h +basicsocket.o: $(hdrdir)/ruby/internal/intern/time.h +basicsocket.o: $(hdrdir)/ruby/internal/intern/variable.h +basicsocket.o: $(hdrdir)/ruby/internal/intern/vm.h +basicsocket.o: $(hdrdir)/ruby/internal/interpreter.h +basicsocket.o: $(hdrdir)/ruby/internal/iterator.h +basicsocket.o: $(hdrdir)/ruby/internal/memory.h +basicsocket.o: $(hdrdir)/ruby/internal/method.h +basicsocket.o: $(hdrdir)/ruby/internal/module.h +basicsocket.o: $(hdrdir)/ruby/internal/newobj.h +basicsocket.o: $(hdrdir)/ruby/internal/scan_args.h +basicsocket.o: $(hdrdir)/ruby/internal/special_consts.h +basicsocket.o: $(hdrdir)/ruby/internal/static_assert.h +basicsocket.o: $(hdrdir)/ruby/internal/stdalign.h +basicsocket.o: $(hdrdir)/ruby/internal/stdbool.h +basicsocket.o: $(hdrdir)/ruby/internal/stdckdint.h +basicsocket.o: $(hdrdir)/ruby/internal/symbol.h +basicsocket.o: $(hdrdir)/ruby/internal/value.h +basicsocket.o: $(hdrdir)/ruby/internal/value_type.h +basicsocket.o: $(hdrdir)/ruby/internal/variable.h +basicsocket.o: $(hdrdir)/ruby/internal/warning_push.h +basicsocket.o: $(hdrdir)/ruby/internal/xmalloc.h +basicsocket.o: $(hdrdir)/ruby/io.h +basicsocket.o: $(hdrdir)/ruby/missing.h +basicsocket.o: $(hdrdir)/ruby/onigmo.h +basicsocket.o: $(hdrdir)/ruby/oniguruma.h +basicsocket.o: $(hdrdir)/ruby/ruby.h +basicsocket.o: $(hdrdir)/ruby/st.h +basicsocket.o: $(hdrdir)/ruby/subst.h +basicsocket.o: $(hdrdir)/ruby/thread.h +basicsocket.o: $(hdrdir)/ruby/thread_native.h +basicsocket.o: $(hdrdir)/ruby/util.h +basicsocket.o: $(hdrdir)/ruby/version.h +basicsocket.o: $(top_srcdir)/ccan/check_type/check_type.h +basicsocket.o: $(top_srcdir)/ccan/container_of/container_of.h +basicsocket.o: $(top_srcdir)/ccan/list/list.h +basicsocket.o: $(top_srcdir)/ccan/str/str.h +basicsocket.o: $(top_srcdir)/encindex.h +basicsocket.o: $(top_srcdir)/id_table.h +basicsocket.o: $(top_srcdir)/internal.h +basicsocket.o: $(top_srcdir)/internal/array.h +basicsocket.o: $(top_srcdir)/internal/basic_operators.h +basicsocket.o: $(top_srcdir)/internal/box.h +basicsocket.o: $(top_srcdir)/internal/compilers.h +basicsocket.o: $(top_srcdir)/internal/error.h +basicsocket.o: $(top_srcdir)/internal/gc.h +basicsocket.o: $(top_srcdir)/internal/imemo.h +basicsocket.o: $(top_srcdir)/internal/io.h +basicsocket.o: $(top_srcdir)/internal/sanitizers.h +basicsocket.o: $(top_srcdir)/internal/serial.h +basicsocket.o: $(top_srcdir)/internal/set_table.h +basicsocket.o: $(top_srcdir)/internal/static_assert.h +basicsocket.o: $(top_srcdir)/internal/string.h +basicsocket.o: $(top_srcdir)/internal/thread.h +basicsocket.o: $(top_srcdir)/internal/vm.h +basicsocket.o: $(top_srcdir)/internal/warnings.h +basicsocket.o: $(top_srcdir)/method.h +basicsocket.o: $(top_srcdir)/node.h +basicsocket.o: $(top_srcdir)/ruby_assert.h +basicsocket.o: $(top_srcdir)/ruby_atomic.h +basicsocket.o: $(top_srcdir)/rubyparser.h +basicsocket.o: $(top_srcdir)/shape.h +basicsocket.o: $(top_srcdir)/thread_pthread.h +basicsocket.o: $(top_srcdir)/vm_core.h +basicsocket.o: $(top_srcdir)/vm_opts.h +basicsocket.o: basicsocket.c +basicsocket.o: constdefs.h +basicsocket.o: rubysocket.h +basicsocket.o: sockport.h +basicsocket.o: {$(VPATH)}id.h +constants.o: $(RUBY_EXTCONF_H) +constants.o: $(arch_hdrdir)/ruby/config.h +constants.o: $(hdrdir)/ruby/assert.h +constants.o: $(hdrdir)/ruby/atomic.h +constants.o: $(hdrdir)/ruby/backward.h +constants.o: $(hdrdir)/ruby/backward/2/assume.h +constants.o: $(hdrdir)/ruby/backward/2/attributes.h +constants.o: $(hdrdir)/ruby/backward/2/bool.h +constants.o: $(hdrdir)/ruby/backward/2/gcc_version_since.h +constants.o: $(hdrdir)/ruby/backward/2/inttypes.h +constants.o: $(hdrdir)/ruby/backward/2/limits.h +constants.o: $(hdrdir)/ruby/backward/2/long_long.h +constants.o: $(hdrdir)/ruby/backward/2/stdalign.h +constants.o: $(hdrdir)/ruby/backward/2/stdarg.h +constants.o: $(hdrdir)/ruby/defines.h +constants.o: $(hdrdir)/ruby/encoding.h +constants.o: $(hdrdir)/ruby/fiber/scheduler.h +constants.o: $(hdrdir)/ruby/intern.h +constants.o: $(hdrdir)/ruby/internal/abi.h +constants.o: $(hdrdir)/ruby/internal/anyargs.h +constants.o: $(hdrdir)/ruby/internal/arithmetic.h +constants.o: $(hdrdir)/ruby/internal/arithmetic/char.h +constants.o: $(hdrdir)/ruby/internal/arithmetic/double.h +constants.o: $(hdrdir)/ruby/internal/arithmetic/fixnum.h +constants.o: $(hdrdir)/ruby/internal/arithmetic/gid_t.h +constants.o: $(hdrdir)/ruby/internal/arithmetic/int.h +constants.o: $(hdrdir)/ruby/internal/arithmetic/intptr_t.h +constants.o: $(hdrdir)/ruby/internal/arithmetic/long.h +constants.o: $(hdrdir)/ruby/internal/arithmetic/long_long.h +constants.o: $(hdrdir)/ruby/internal/arithmetic/mode_t.h +constants.o: $(hdrdir)/ruby/internal/arithmetic/off_t.h +constants.o: $(hdrdir)/ruby/internal/arithmetic/pid_t.h +constants.o: $(hdrdir)/ruby/internal/arithmetic/short.h +constants.o: $(hdrdir)/ruby/internal/arithmetic/size_t.h +constants.o: $(hdrdir)/ruby/internal/arithmetic/st_data_t.h +constants.o: $(hdrdir)/ruby/internal/arithmetic/uid_t.h +constants.o: $(hdrdir)/ruby/internal/assume.h +constants.o: $(hdrdir)/ruby/internal/attr/alloc_size.h +constants.o: $(hdrdir)/ruby/internal/attr/artificial.h +constants.o: $(hdrdir)/ruby/internal/attr/cold.h +constants.o: $(hdrdir)/ruby/internal/attr/const.h +constants.o: $(hdrdir)/ruby/internal/attr/constexpr.h +constants.o: $(hdrdir)/ruby/internal/attr/deprecated.h +constants.o: $(hdrdir)/ruby/internal/attr/diagnose_if.h +constants.o: $(hdrdir)/ruby/internal/attr/enum_extensibility.h +constants.o: $(hdrdir)/ruby/internal/attr/error.h +constants.o: $(hdrdir)/ruby/internal/attr/flag_enum.h +constants.o: $(hdrdir)/ruby/internal/attr/forceinline.h +constants.o: $(hdrdir)/ruby/internal/attr/format.h +constants.o: $(hdrdir)/ruby/internal/attr/maybe_unused.h +constants.o: $(hdrdir)/ruby/internal/attr/noalias.h +constants.o: $(hdrdir)/ruby/internal/attr/nodiscard.h +constants.o: $(hdrdir)/ruby/internal/attr/noexcept.h +constants.o: $(hdrdir)/ruby/internal/attr/noinline.h +constants.o: $(hdrdir)/ruby/internal/attr/nonnull.h +constants.o: $(hdrdir)/ruby/internal/attr/noreturn.h +constants.o: $(hdrdir)/ruby/internal/attr/packed_struct.h +constants.o: $(hdrdir)/ruby/internal/attr/pure.h +constants.o: $(hdrdir)/ruby/internal/attr/restrict.h +constants.o: $(hdrdir)/ruby/internal/attr/returns_nonnull.h +constants.o: $(hdrdir)/ruby/internal/attr/warning.h +constants.o: $(hdrdir)/ruby/internal/attr/weakref.h +constants.o: $(hdrdir)/ruby/internal/cast.h +constants.o: $(hdrdir)/ruby/internal/compiler_is.h +constants.o: $(hdrdir)/ruby/internal/compiler_is/apple.h +constants.o: $(hdrdir)/ruby/internal/compiler_is/clang.h +constants.o: $(hdrdir)/ruby/internal/compiler_is/gcc.h +constants.o: $(hdrdir)/ruby/internal/compiler_is/intel.h +constants.o: $(hdrdir)/ruby/internal/compiler_is/msvc.h +constants.o: $(hdrdir)/ruby/internal/compiler_is/sunpro.h +constants.o: $(hdrdir)/ruby/internal/compiler_since.h +constants.o: $(hdrdir)/ruby/internal/config.h +constants.o: $(hdrdir)/ruby/internal/constant_p.h +constants.o: $(hdrdir)/ruby/internal/core.h +constants.o: $(hdrdir)/ruby/internal/core/rarray.h +constants.o: $(hdrdir)/ruby/internal/core/rbasic.h +constants.o: $(hdrdir)/ruby/internal/core/rbignum.h +constants.o: $(hdrdir)/ruby/internal/core/rclass.h +constants.o: $(hdrdir)/ruby/internal/core/rdata.h +constants.o: $(hdrdir)/ruby/internal/core/rfile.h +constants.o: $(hdrdir)/ruby/internal/core/rhash.h +constants.o: $(hdrdir)/ruby/internal/core/robject.h +constants.o: $(hdrdir)/ruby/internal/core/rregexp.h +constants.o: $(hdrdir)/ruby/internal/core/rstring.h +constants.o: $(hdrdir)/ruby/internal/core/rstruct.h +constants.o: $(hdrdir)/ruby/internal/core/rtypeddata.h +constants.o: $(hdrdir)/ruby/internal/ctype.h +constants.o: $(hdrdir)/ruby/internal/dllexport.h +constants.o: $(hdrdir)/ruby/internal/dosish.h +constants.o: $(hdrdir)/ruby/internal/encoding/coderange.h +constants.o: $(hdrdir)/ruby/internal/encoding/ctype.h +constants.o: $(hdrdir)/ruby/internal/encoding/encoding.h +constants.o: $(hdrdir)/ruby/internal/encoding/pathname.h +constants.o: $(hdrdir)/ruby/internal/encoding/re.h +constants.o: $(hdrdir)/ruby/internal/encoding/sprintf.h +constants.o: $(hdrdir)/ruby/internal/encoding/string.h +constants.o: $(hdrdir)/ruby/internal/encoding/symbol.h +constants.o: $(hdrdir)/ruby/internal/encoding/transcode.h +constants.o: $(hdrdir)/ruby/internal/error.h +constants.o: $(hdrdir)/ruby/internal/eval.h +constants.o: $(hdrdir)/ruby/internal/event.h +constants.o: $(hdrdir)/ruby/internal/fl_type.h +constants.o: $(hdrdir)/ruby/internal/gc.h +constants.o: $(hdrdir)/ruby/internal/glob.h +constants.o: $(hdrdir)/ruby/internal/globals.h +constants.o: $(hdrdir)/ruby/internal/has/attribute.h +constants.o: $(hdrdir)/ruby/internal/has/builtin.h +constants.o: $(hdrdir)/ruby/internal/has/c_attribute.h +constants.o: $(hdrdir)/ruby/internal/has/cpp_attribute.h +constants.o: $(hdrdir)/ruby/internal/has/declspec_attribute.h +constants.o: $(hdrdir)/ruby/internal/has/extension.h +constants.o: $(hdrdir)/ruby/internal/has/feature.h +constants.o: $(hdrdir)/ruby/internal/has/warning.h +constants.o: $(hdrdir)/ruby/internal/intern/array.h +constants.o: $(hdrdir)/ruby/internal/intern/bignum.h +constants.o: $(hdrdir)/ruby/internal/intern/class.h +constants.o: $(hdrdir)/ruby/internal/intern/compar.h +constants.o: $(hdrdir)/ruby/internal/intern/complex.h +constants.o: $(hdrdir)/ruby/internal/intern/cont.h +constants.o: $(hdrdir)/ruby/internal/intern/dir.h +constants.o: $(hdrdir)/ruby/internal/intern/enum.h +constants.o: $(hdrdir)/ruby/internal/intern/enumerator.h +constants.o: $(hdrdir)/ruby/internal/intern/error.h +constants.o: $(hdrdir)/ruby/internal/intern/eval.h +constants.o: $(hdrdir)/ruby/internal/intern/file.h +constants.o: $(hdrdir)/ruby/internal/intern/hash.h +constants.o: $(hdrdir)/ruby/internal/intern/io.h +constants.o: $(hdrdir)/ruby/internal/intern/load.h +constants.o: $(hdrdir)/ruby/internal/intern/marshal.h +constants.o: $(hdrdir)/ruby/internal/intern/numeric.h +constants.o: $(hdrdir)/ruby/internal/intern/object.h +constants.o: $(hdrdir)/ruby/internal/intern/parse.h +constants.o: $(hdrdir)/ruby/internal/intern/proc.h +constants.o: $(hdrdir)/ruby/internal/intern/process.h +constants.o: $(hdrdir)/ruby/internal/intern/random.h +constants.o: $(hdrdir)/ruby/internal/intern/range.h +constants.o: $(hdrdir)/ruby/internal/intern/rational.h +constants.o: $(hdrdir)/ruby/internal/intern/re.h +constants.o: $(hdrdir)/ruby/internal/intern/ruby.h +constants.o: $(hdrdir)/ruby/internal/intern/select.h +constants.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +constants.o: $(hdrdir)/ruby/internal/intern/set.h +constants.o: $(hdrdir)/ruby/internal/intern/signal.h +constants.o: $(hdrdir)/ruby/internal/intern/sprintf.h +constants.o: $(hdrdir)/ruby/internal/intern/string.h +constants.o: $(hdrdir)/ruby/internal/intern/struct.h +constants.o: $(hdrdir)/ruby/internal/intern/thread.h +constants.o: $(hdrdir)/ruby/internal/intern/time.h +constants.o: $(hdrdir)/ruby/internal/intern/variable.h +constants.o: $(hdrdir)/ruby/internal/intern/vm.h +constants.o: $(hdrdir)/ruby/internal/interpreter.h +constants.o: $(hdrdir)/ruby/internal/iterator.h +constants.o: $(hdrdir)/ruby/internal/memory.h +constants.o: $(hdrdir)/ruby/internal/method.h +constants.o: $(hdrdir)/ruby/internal/module.h +constants.o: $(hdrdir)/ruby/internal/newobj.h +constants.o: $(hdrdir)/ruby/internal/scan_args.h +constants.o: $(hdrdir)/ruby/internal/special_consts.h +constants.o: $(hdrdir)/ruby/internal/static_assert.h +constants.o: $(hdrdir)/ruby/internal/stdalign.h +constants.o: $(hdrdir)/ruby/internal/stdbool.h +constants.o: $(hdrdir)/ruby/internal/stdckdint.h +constants.o: $(hdrdir)/ruby/internal/symbol.h +constants.o: $(hdrdir)/ruby/internal/value.h +constants.o: $(hdrdir)/ruby/internal/value_type.h +constants.o: $(hdrdir)/ruby/internal/variable.h +constants.o: $(hdrdir)/ruby/internal/warning_push.h +constants.o: $(hdrdir)/ruby/internal/xmalloc.h +constants.o: $(hdrdir)/ruby/io.h +constants.o: $(hdrdir)/ruby/missing.h +constants.o: $(hdrdir)/ruby/onigmo.h +constants.o: $(hdrdir)/ruby/oniguruma.h +constants.o: $(hdrdir)/ruby/ruby.h +constants.o: $(hdrdir)/ruby/st.h +constants.o: $(hdrdir)/ruby/subst.h +constants.o: $(hdrdir)/ruby/thread.h +constants.o: $(hdrdir)/ruby/thread_native.h +constants.o: $(hdrdir)/ruby/util.h +constants.o: $(hdrdir)/ruby/version.h +constants.o: $(top_srcdir)/ccan/check_type/check_type.h +constants.o: $(top_srcdir)/ccan/container_of/container_of.h +constants.o: $(top_srcdir)/ccan/list/list.h +constants.o: $(top_srcdir)/ccan/str/str.h +constants.o: $(top_srcdir)/encindex.h +constants.o: $(top_srcdir)/id_table.h +constants.o: $(top_srcdir)/internal.h +constants.o: $(top_srcdir)/internal/array.h +constants.o: $(top_srcdir)/internal/basic_operators.h +constants.o: $(top_srcdir)/internal/box.h +constants.o: $(top_srcdir)/internal/compilers.h +constants.o: $(top_srcdir)/internal/error.h +constants.o: $(top_srcdir)/internal/gc.h +constants.o: $(top_srcdir)/internal/imemo.h +constants.o: $(top_srcdir)/internal/io.h +constants.o: $(top_srcdir)/internal/sanitizers.h +constants.o: $(top_srcdir)/internal/serial.h +constants.o: $(top_srcdir)/internal/set_table.h +constants.o: $(top_srcdir)/internal/static_assert.h +constants.o: $(top_srcdir)/internal/string.h +constants.o: $(top_srcdir)/internal/thread.h +constants.o: $(top_srcdir)/internal/vm.h +constants.o: $(top_srcdir)/internal/warnings.h +constants.o: $(top_srcdir)/method.h +constants.o: $(top_srcdir)/node.h +constants.o: $(top_srcdir)/ruby_assert.h +constants.o: $(top_srcdir)/ruby_atomic.h +constants.o: $(top_srcdir)/rubyparser.h +constants.o: $(top_srcdir)/shape.h +constants.o: $(top_srcdir)/thread_pthread.h +constants.o: $(top_srcdir)/vm_core.h +constants.o: $(top_srcdir)/vm_opts.h +constants.o: constants.c +constants.o: constdefs.c +constants.o: constdefs.h +constants.o: rubysocket.h +constants.o: sockport.h +constants.o: {$(VPATH)}id.h +ifaddr.o: $(RUBY_EXTCONF_H) +ifaddr.o: $(arch_hdrdir)/ruby/config.h +ifaddr.o: $(hdrdir)/ruby/assert.h +ifaddr.o: $(hdrdir)/ruby/atomic.h +ifaddr.o: $(hdrdir)/ruby/backward.h +ifaddr.o: $(hdrdir)/ruby/backward/2/assume.h +ifaddr.o: $(hdrdir)/ruby/backward/2/attributes.h +ifaddr.o: $(hdrdir)/ruby/backward/2/bool.h +ifaddr.o: $(hdrdir)/ruby/backward/2/gcc_version_since.h +ifaddr.o: $(hdrdir)/ruby/backward/2/inttypes.h +ifaddr.o: $(hdrdir)/ruby/backward/2/limits.h +ifaddr.o: $(hdrdir)/ruby/backward/2/long_long.h +ifaddr.o: $(hdrdir)/ruby/backward/2/stdalign.h +ifaddr.o: $(hdrdir)/ruby/backward/2/stdarg.h +ifaddr.o: $(hdrdir)/ruby/defines.h +ifaddr.o: $(hdrdir)/ruby/encoding.h +ifaddr.o: $(hdrdir)/ruby/fiber/scheduler.h +ifaddr.o: $(hdrdir)/ruby/intern.h +ifaddr.o: $(hdrdir)/ruby/internal/abi.h +ifaddr.o: $(hdrdir)/ruby/internal/anyargs.h +ifaddr.o: $(hdrdir)/ruby/internal/arithmetic.h +ifaddr.o: $(hdrdir)/ruby/internal/arithmetic/char.h +ifaddr.o: $(hdrdir)/ruby/internal/arithmetic/double.h +ifaddr.o: $(hdrdir)/ruby/internal/arithmetic/fixnum.h +ifaddr.o: $(hdrdir)/ruby/internal/arithmetic/gid_t.h +ifaddr.o: $(hdrdir)/ruby/internal/arithmetic/int.h +ifaddr.o: $(hdrdir)/ruby/internal/arithmetic/intptr_t.h +ifaddr.o: $(hdrdir)/ruby/internal/arithmetic/long.h +ifaddr.o: $(hdrdir)/ruby/internal/arithmetic/long_long.h +ifaddr.o: $(hdrdir)/ruby/internal/arithmetic/mode_t.h +ifaddr.o: $(hdrdir)/ruby/internal/arithmetic/off_t.h +ifaddr.o: $(hdrdir)/ruby/internal/arithmetic/pid_t.h +ifaddr.o: $(hdrdir)/ruby/internal/arithmetic/short.h +ifaddr.o: $(hdrdir)/ruby/internal/arithmetic/size_t.h +ifaddr.o: $(hdrdir)/ruby/internal/arithmetic/st_data_t.h +ifaddr.o: $(hdrdir)/ruby/internal/arithmetic/uid_t.h +ifaddr.o: $(hdrdir)/ruby/internal/assume.h +ifaddr.o: $(hdrdir)/ruby/internal/attr/alloc_size.h +ifaddr.o: $(hdrdir)/ruby/internal/attr/artificial.h +ifaddr.o: $(hdrdir)/ruby/internal/attr/cold.h +ifaddr.o: $(hdrdir)/ruby/internal/attr/const.h +ifaddr.o: $(hdrdir)/ruby/internal/attr/constexpr.h +ifaddr.o: $(hdrdir)/ruby/internal/attr/deprecated.h +ifaddr.o: $(hdrdir)/ruby/internal/attr/diagnose_if.h +ifaddr.o: $(hdrdir)/ruby/internal/attr/enum_extensibility.h +ifaddr.o: $(hdrdir)/ruby/internal/attr/error.h +ifaddr.o: $(hdrdir)/ruby/internal/attr/flag_enum.h +ifaddr.o: $(hdrdir)/ruby/internal/attr/forceinline.h +ifaddr.o: $(hdrdir)/ruby/internal/attr/format.h +ifaddr.o: $(hdrdir)/ruby/internal/attr/maybe_unused.h +ifaddr.o: $(hdrdir)/ruby/internal/attr/noalias.h +ifaddr.o: $(hdrdir)/ruby/internal/attr/nodiscard.h +ifaddr.o: $(hdrdir)/ruby/internal/attr/noexcept.h +ifaddr.o: $(hdrdir)/ruby/internal/attr/noinline.h +ifaddr.o: $(hdrdir)/ruby/internal/attr/nonnull.h +ifaddr.o: $(hdrdir)/ruby/internal/attr/noreturn.h +ifaddr.o: $(hdrdir)/ruby/internal/attr/packed_struct.h +ifaddr.o: $(hdrdir)/ruby/internal/attr/pure.h +ifaddr.o: $(hdrdir)/ruby/internal/attr/restrict.h +ifaddr.o: $(hdrdir)/ruby/internal/attr/returns_nonnull.h +ifaddr.o: $(hdrdir)/ruby/internal/attr/warning.h +ifaddr.o: $(hdrdir)/ruby/internal/attr/weakref.h +ifaddr.o: $(hdrdir)/ruby/internal/cast.h +ifaddr.o: $(hdrdir)/ruby/internal/compiler_is.h +ifaddr.o: $(hdrdir)/ruby/internal/compiler_is/apple.h +ifaddr.o: $(hdrdir)/ruby/internal/compiler_is/clang.h +ifaddr.o: $(hdrdir)/ruby/internal/compiler_is/gcc.h +ifaddr.o: $(hdrdir)/ruby/internal/compiler_is/intel.h +ifaddr.o: $(hdrdir)/ruby/internal/compiler_is/msvc.h +ifaddr.o: $(hdrdir)/ruby/internal/compiler_is/sunpro.h +ifaddr.o: $(hdrdir)/ruby/internal/compiler_since.h +ifaddr.o: $(hdrdir)/ruby/internal/config.h +ifaddr.o: $(hdrdir)/ruby/internal/constant_p.h +ifaddr.o: $(hdrdir)/ruby/internal/core.h +ifaddr.o: $(hdrdir)/ruby/internal/core/rarray.h +ifaddr.o: $(hdrdir)/ruby/internal/core/rbasic.h +ifaddr.o: $(hdrdir)/ruby/internal/core/rbignum.h +ifaddr.o: $(hdrdir)/ruby/internal/core/rclass.h +ifaddr.o: $(hdrdir)/ruby/internal/core/rdata.h +ifaddr.o: $(hdrdir)/ruby/internal/core/rfile.h +ifaddr.o: $(hdrdir)/ruby/internal/core/rhash.h +ifaddr.o: $(hdrdir)/ruby/internal/core/robject.h +ifaddr.o: $(hdrdir)/ruby/internal/core/rregexp.h +ifaddr.o: $(hdrdir)/ruby/internal/core/rstring.h +ifaddr.o: $(hdrdir)/ruby/internal/core/rstruct.h +ifaddr.o: $(hdrdir)/ruby/internal/core/rtypeddata.h +ifaddr.o: $(hdrdir)/ruby/internal/ctype.h +ifaddr.o: $(hdrdir)/ruby/internal/dllexport.h +ifaddr.o: $(hdrdir)/ruby/internal/dosish.h +ifaddr.o: $(hdrdir)/ruby/internal/encoding/coderange.h +ifaddr.o: $(hdrdir)/ruby/internal/encoding/ctype.h +ifaddr.o: $(hdrdir)/ruby/internal/encoding/encoding.h +ifaddr.o: $(hdrdir)/ruby/internal/encoding/pathname.h +ifaddr.o: $(hdrdir)/ruby/internal/encoding/re.h +ifaddr.o: $(hdrdir)/ruby/internal/encoding/sprintf.h +ifaddr.o: $(hdrdir)/ruby/internal/encoding/string.h +ifaddr.o: $(hdrdir)/ruby/internal/encoding/symbol.h +ifaddr.o: $(hdrdir)/ruby/internal/encoding/transcode.h +ifaddr.o: $(hdrdir)/ruby/internal/error.h +ifaddr.o: $(hdrdir)/ruby/internal/eval.h +ifaddr.o: $(hdrdir)/ruby/internal/event.h +ifaddr.o: $(hdrdir)/ruby/internal/fl_type.h +ifaddr.o: $(hdrdir)/ruby/internal/gc.h +ifaddr.o: $(hdrdir)/ruby/internal/glob.h +ifaddr.o: $(hdrdir)/ruby/internal/globals.h +ifaddr.o: $(hdrdir)/ruby/internal/has/attribute.h +ifaddr.o: $(hdrdir)/ruby/internal/has/builtin.h +ifaddr.o: $(hdrdir)/ruby/internal/has/c_attribute.h +ifaddr.o: $(hdrdir)/ruby/internal/has/cpp_attribute.h +ifaddr.o: $(hdrdir)/ruby/internal/has/declspec_attribute.h +ifaddr.o: $(hdrdir)/ruby/internal/has/extension.h +ifaddr.o: $(hdrdir)/ruby/internal/has/feature.h +ifaddr.o: $(hdrdir)/ruby/internal/has/warning.h +ifaddr.o: $(hdrdir)/ruby/internal/intern/array.h +ifaddr.o: $(hdrdir)/ruby/internal/intern/bignum.h +ifaddr.o: $(hdrdir)/ruby/internal/intern/class.h +ifaddr.o: $(hdrdir)/ruby/internal/intern/compar.h +ifaddr.o: $(hdrdir)/ruby/internal/intern/complex.h +ifaddr.o: $(hdrdir)/ruby/internal/intern/cont.h +ifaddr.o: $(hdrdir)/ruby/internal/intern/dir.h +ifaddr.o: $(hdrdir)/ruby/internal/intern/enum.h +ifaddr.o: $(hdrdir)/ruby/internal/intern/enumerator.h +ifaddr.o: $(hdrdir)/ruby/internal/intern/error.h +ifaddr.o: $(hdrdir)/ruby/internal/intern/eval.h +ifaddr.o: $(hdrdir)/ruby/internal/intern/file.h +ifaddr.o: $(hdrdir)/ruby/internal/intern/hash.h +ifaddr.o: $(hdrdir)/ruby/internal/intern/io.h +ifaddr.o: $(hdrdir)/ruby/internal/intern/load.h +ifaddr.o: $(hdrdir)/ruby/internal/intern/marshal.h +ifaddr.o: $(hdrdir)/ruby/internal/intern/numeric.h +ifaddr.o: $(hdrdir)/ruby/internal/intern/object.h +ifaddr.o: $(hdrdir)/ruby/internal/intern/parse.h +ifaddr.o: $(hdrdir)/ruby/internal/intern/proc.h +ifaddr.o: $(hdrdir)/ruby/internal/intern/process.h +ifaddr.o: $(hdrdir)/ruby/internal/intern/random.h +ifaddr.o: $(hdrdir)/ruby/internal/intern/range.h +ifaddr.o: $(hdrdir)/ruby/internal/intern/rational.h +ifaddr.o: $(hdrdir)/ruby/internal/intern/re.h +ifaddr.o: $(hdrdir)/ruby/internal/intern/ruby.h +ifaddr.o: $(hdrdir)/ruby/internal/intern/select.h +ifaddr.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +ifaddr.o: $(hdrdir)/ruby/internal/intern/set.h +ifaddr.o: $(hdrdir)/ruby/internal/intern/signal.h +ifaddr.o: $(hdrdir)/ruby/internal/intern/sprintf.h +ifaddr.o: $(hdrdir)/ruby/internal/intern/string.h +ifaddr.o: $(hdrdir)/ruby/internal/intern/struct.h +ifaddr.o: $(hdrdir)/ruby/internal/intern/thread.h +ifaddr.o: $(hdrdir)/ruby/internal/intern/time.h +ifaddr.o: $(hdrdir)/ruby/internal/intern/variable.h +ifaddr.o: $(hdrdir)/ruby/internal/intern/vm.h +ifaddr.o: $(hdrdir)/ruby/internal/interpreter.h +ifaddr.o: $(hdrdir)/ruby/internal/iterator.h +ifaddr.o: $(hdrdir)/ruby/internal/memory.h +ifaddr.o: $(hdrdir)/ruby/internal/method.h +ifaddr.o: $(hdrdir)/ruby/internal/module.h +ifaddr.o: $(hdrdir)/ruby/internal/newobj.h +ifaddr.o: $(hdrdir)/ruby/internal/scan_args.h +ifaddr.o: $(hdrdir)/ruby/internal/special_consts.h +ifaddr.o: $(hdrdir)/ruby/internal/static_assert.h +ifaddr.o: $(hdrdir)/ruby/internal/stdalign.h +ifaddr.o: $(hdrdir)/ruby/internal/stdbool.h +ifaddr.o: $(hdrdir)/ruby/internal/stdckdint.h +ifaddr.o: $(hdrdir)/ruby/internal/symbol.h +ifaddr.o: $(hdrdir)/ruby/internal/value.h +ifaddr.o: $(hdrdir)/ruby/internal/value_type.h +ifaddr.o: $(hdrdir)/ruby/internal/variable.h +ifaddr.o: $(hdrdir)/ruby/internal/warning_push.h +ifaddr.o: $(hdrdir)/ruby/internal/xmalloc.h +ifaddr.o: $(hdrdir)/ruby/io.h +ifaddr.o: $(hdrdir)/ruby/missing.h +ifaddr.o: $(hdrdir)/ruby/onigmo.h +ifaddr.o: $(hdrdir)/ruby/oniguruma.h +ifaddr.o: $(hdrdir)/ruby/ruby.h +ifaddr.o: $(hdrdir)/ruby/st.h +ifaddr.o: $(hdrdir)/ruby/subst.h +ifaddr.o: $(hdrdir)/ruby/thread.h +ifaddr.o: $(hdrdir)/ruby/thread_native.h +ifaddr.o: $(hdrdir)/ruby/util.h +ifaddr.o: $(hdrdir)/ruby/version.h +ifaddr.o: $(top_srcdir)/ccan/check_type/check_type.h +ifaddr.o: $(top_srcdir)/ccan/container_of/container_of.h +ifaddr.o: $(top_srcdir)/ccan/list/list.h +ifaddr.o: $(top_srcdir)/ccan/str/str.h +ifaddr.o: $(top_srcdir)/encindex.h +ifaddr.o: $(top_srcdir)/id_table.h +ifaddr.o: $(top_srcdir)/internal.h +ifaddr.o: $(top_srcdir)/internal/array.h +ifaddr.o: $(top_srcdir)/internal/basic_operators.h +ifaddr.o: $(top_srcdir)/internal/box.h +ifaddr.o: $(top_srcdir)/internal/compilers.h +ifaddr.o: $(top_srcdir)/internal/error.h +ifaddr.o: $(top_srcdir)/internal/gc.h +ifaddr.o: $(top_srcdir)/internal/imemo.h +ifaddr.o: $(top_srcdir)/internal/io.h +ifaddr.o: $(top_srcdir)/internal/sanitizers.h +ifaddr.o: $(top_srcdir)/internal/serial.h +ifaddr.o: $(top_srcdir)/internal/set_table.h +ifaddr.o: $(top_srcdir)/internal/static_assert.h +ifaddr.o: $(top_srcdir)/internal/string.h +ifaddr.o: $(top_srcdir)/internal/thread.h +ifaddr.o: $(top_srcdir)/internal/vm.h +ifaddr.o: $(top_srcdir)/internal/warnings.h +ifaddr.o: $(top_srcdir)/method.h +ifaddr.o: $(top_srcdir)/node.h +ifaddr.o: $(top_srcdir)/ruby_assert.h +ifaddr.o: $(top_srcdir)/ruby_atomic.h +ifaddr.o: $(top_srcdir)/rubyparser.h +ifaddr.o: $(top_srcdir)/shape.h +ifaddr.o: $(top_srcdir)/thread_pthread.h +ifaddr.o: $(top_srcdir)/vm_core.h +ifaddr.o: $(top_srcdir)/vm_opts.h +ifaddr.o: constdefs.h +ifaddr.o: ifaddr.c +ifaddr.o: rubysocket.h +ifaddr.o: sockport.h +ifaddr.o: {$(VPATH)}id.h +init.o: $(RUBY_EXTCONF_H) +init.o: $(arch_hdrdir)/ruby/config.h +init.o: $(hdrdir)/ruby/assert.h +init.o: $(hdrdir)/ruby/atomic.h +init.o: $(hdrdir)/ruby/backward.h +init.o: $(hdrdir)/ruby/backward/2/assume.h +init.o: $(hdrdir)/ruby/backward/2/attributes.h +init.o: $(hdrdir)/ruby/backward/2/bool.h +init.o: $(hdrdir)/ruby/backward/2/gcc_version_since.h +init.o: $(hdrdir)/ruby/backward/2/inttypes.h +init.o: $(hdrdir)/ruby/backward/2/limits.h +init.o: $(hdrdir)/ruby/backward/2/long_long.h +init.o: $(hdrdir)/ruby/backward/2/stdalign.h +init.o: $(hdrdir)/ruby/backward/2/stdarg.h +init.o: $(hdrdir)/ruby/defines.h +init.o: $(hdrdir)/ruby/encoding.h +init.o: $(hdrdir)/ruby/fiber/scheduler.h +init.o: $(hdrdir)/ruby/intern.h +init.o: $(hdrdir)/ruby/internal/abi.h +init.o: $(hdrdir)/ruby/internal/anyargs.h +init.o: $(hdrdir)/ruby/internal/arithmetic.h +init.o: $(hdrdir)/ruby/internal/arithmetic/char.h +init.o: $(hdrdir)/ruby/internal/arithmetic/double.h +init.o: $(hdrdir)/ruby/internal/arithmetic/fixnum.h +init.o: $(hdrdir)/ruby/internal/arithmetic/gid_t.h +init.o: $(hdrdir)/ruby/internal/arithmetic/int.h +init.o: $(hdrdir)/ruby/internal/arithmetic/intptr_t.h +init.o: $(hdrdir)/ruby/internal/arithmetic/long.h +init.o: $(hdrdir)/ruby/internal/arithmetic/long_long.h +init.o: $(hdrdir)/ruby/internal/arithmetic/mode_t.h +init.o: $(hdrdir)/ruby/internal/arithmetic/off_t.h +init.o: $(hdrdir)/ruby/internal/arithmetic/pid_t.h +init.o: $(hdrdir)/ruby/internal/arithmetic/short.h +init.o: $(hdrdir)/ruby/internal/arithmetic/size_t.h +init.o: $(hdrdir)/ruby/internal/arithmetic/st_data_t.h +init.o: $(hdrdir)/ruby/internal/arithmetic/uid_t.h +init.o: $(hdrdir)/ruby/internal/assume.h +init.o: $(hdrdir)/ruby/internal/attr/alloc_size.h +init.o: $(hdrdir)/ruby/internal/attr/artificial.h +init.o: $(hdrdir)/ruby/internal/attr/cold.h +init.o: $(hdrdir)/ruby/internal/attr/const.h +init.o: $(hdrdir)/ruby/internal/attr/constexpr.h +init.o: $(hdrdir)/ruby/internal/attr/deprecated.h +init.o: $(hdrdir)/ruby/internal/attr/diagnose_if.h +init.o: $(hdrdir)/ruby/internal/attr/enum_extensibility.h +init.o: $(hdrdir)/ruby/internal/attr/error.h +init.o: $(hdrdir)/ruby/internal/attr/flag_enum.h +init.o: $(hdrdir)/ruby/internal/attr/forceinline.h +init.o: $(hdrdir)/ruby/internal/attr/format.h +init.o: $(hdrdir)/ruby/internal/attr/maybe_unused.h +init.o: $(hdrdir)/ruby/internal/attr/noalias.h +init.o: $(hdrdir)/ruby/internal/attr/nodiscard.h +init.o: $(hdrdir)/ruby/internal/attr/noexcept.h +init.o: $(hdrdir)/ruby/internal/attr/noinline.h +init.o: $(hdrdir)/ruby/internal/attr/nonnull.h +init.o: $(hdrdir)/ruby/internal/attr/noreturn.h +init.o: $(hdrdir)/ruby/internal/attr/packed_struct.h +init.o: $(hdrdir)/ruby/internal/attr/pure.h +init.o: $(hdrdir)/ruby/internal/attr/restrict.h +init.o: $(hdrdir)/ruby/internal/attr/returns_nonnull.h +init.o: $(hdrdir)/ruby/internal/attr/warning.h +init.o: $(hdrdir)/ruby/internal/attr/weakref.h +init.o: $(hdrdir)/ruby/internal/cast.h +init.o: $(hdrdir)/ruby/internal/compiler_is.h +init.o: $(hdrdir)/ruby/internal/compiler_is/apple.h +init.o: $(hdrdir)/ruby/internal/compiler_is/clang.h +init.o: $(hdrdir)/ruby/internal/compiler_is/gcc.h +init.o: $(hdrdir)/ruby/internal/compiler_is/intel.h +init.o: $(hdrdir)/ruby/internal/compiler_is/msvc.h +init.o: $(hdrdir)/ruby/internal/compiler_is/sunpro.h +init.o: $(hdrdir)/ruby/internal/compiler_since.h +init.o: $(hdrdir)/ruby/internal/config.h +init.o: $(hdrdir)/ruby/internal/constant_p.h +init.o: $(hdrdir)/ruby/internal/core.h +init.o: $(hdrdir)/ruby/internal/core/rarray.h +init.o: $(hdrdir)/ruby/internal/core/rbasic.h +init.o: $(hdrdir)/ruby/internal/core/rbignum.h +init.o: $(hdrdir)/ruby/internal/core/rclass.h +init.o: $(hdrdir)/ruby/internal/core/rdata.h +init.o: $(hdrdir)/ruby/internal/core/rfile.h +init.o: $(hdrdir)/ruby/internal/core/rhash.h +init.o: $(hdrdir)/ruby/internal/core/robject.h +init.o: $(hdrdir)/ruby/internal/core/rregexp.h +init.o: $(hdrdir)/ruby/internal/core/rstring.h +init.o: $(hdrdir)/ruby/internal/core/rstruct.h +init.o: $(hdrdir)/ruby/internal/core/rtypeddata.h +init.o: $(hdrdir)/ruby/internal/ctype.h +init.o: $(hdrdir)/ruby/internal/dllexport.h +init.o: $(hdrdir)/ruby/internal/dosish.h +init.o: $(hdrdir)/ruby/internal/encoding/coderange.h +init.o: $(hdrdir)/ruby/internal/encoding/ctype.h +init.o: $(hdrdir)/ruby/internal/encoding/encoding.h +init.o: $(hdrdir)/ruby/internal/encoding/pathname.h +init.o: $(hdrdir)/ruby/internal/encoding/re.h +init.o: $(hdrdir)/ruby/internal/encoding/sprintf.h +init.o: $(hdrdir)/ruby/internal/encoding/string.h +init.o: $(hdrdir)/ruby/internal/encoding/symbol.h +init.o: $(hdrdir)/ruby/internal/encoding/transcode.h +init.o: $(hdrdir)/ruby/internal/error.h +init.o: $(hdrdir)/ruby/internal/eval.h +init.o: $(hdrdir)/ruby/internal/event.h +init.o: $(hdrdir)/ruby/internal/fl_type.h +init.o: $(hdrdir)/ruby/internal/gc.h +init.o: $(hdrdir)/ruby/internal/glob.h +init.o: $(hdrdir)/ruby/internal/globals.h +init.o: $(hdrdir)/ruby/internal/has/attribute.h +init.o: $(hdrdir)/ruby/internal/has/builtin.h +init.o: $(hdrdir)/ruby/internal/has/c_attribute.h +init.o: $(hdrdir)/ruby/internal/has/cpp_attribute.h +init.o: $(hdrdir)/ruby/internal/has/declspec_attribute.h +init.o: $(hdrdir)/ruby/internal/has/extension.h +init.o: $(hdrdir)/ruby/internal/has/feature.h +init.o: $(hdrdir)/ruby/internal/has/warning.h +init.o: $(hdrdir)/ruby/internal/intern/array.h +init.o: $(hdrdir)/ruby/internal/intern/bignum.h +init.o: $(hdrdir)/ruby/internal/intern/class.h +init.o: $(hdrdir)/ruby/internal/intern/compar.h +init.o: $(hdrdir)/ruby/internal/intern/complex.h +init.o: $(hdrdir)/ruby/internal/intern/cont.h +init.o: $(hdrdir)/ruby/internal/intern/dir.h +init.o: $(hdrdir)/ruby/internal/intern/enum.h +init.o: $(hdrdir)/ruby/internal/intern/enumerator.h +init.o: $(hdrdir)/ruby/internal/intern/error.h +init.o: $(hdrdir)/ruby/internal/intern/eval.h +init.o: $(hdrdir)/ruby/internal/intern/file.h +init.o: $(hdrdir)/ruby/internal/intern/hash.h +init.o: $(hdrdir)/ruby/internal/intern/io.h +init.o: $(hdrdir)/ruby/internal/intern/load.h +init.o: $(hdrdir)/ruby/internal/intern/marshal.h +init.o: $(hdrdir)/ruby/internal/intern/numeric.h +init.o: $(hdrdir)/ruby/internal/intern/object.h +init.o: $(hdrdir)/ruby/internal/intern/parse.h +init.o: $(hdrdir)/ruby/internal/intern/proc.h +init.o: $(hdrdir)/ruby/internal/intern/process.h +init.o: $(hdrdir)/ruby/internal/intern/random.h +init.o: $(hdrdir)/ruby/internal/intern/range.h +init.o: $(hdrdir)/ruby/internal/intern/rational.h +init.o: $(hdrdir)/ruby/internal/intern/re.h +init.o: $(hdrdir)/ruby/internal/intern/ruby.h +init.o: $(hdrdir)/ruby/internal/intern/select.h +init.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +init.o: $(hdrdir)/ruby/internal/intern/set.h +init.o: $(hdrdir)/ruby/internal/intern/signal.h +init.o: $(hdrdir)/ruby/internal/intern/sprintf.h +init.o: $(hdrdir)/ruby/internal/intern/string.h +init.o: $(hdrdir)/ruby/internal/intern/struct.h +init.o: $(hdrdir)/ruby/internal/intern/thread.h +init.o: $(hdrdir)/ruby/internal/intern/time.h +init.o: $(hdrdir)/ruby/internal/intern/variable.h +init.o: $(hdrdir)/ruby/internal/intern/vm.h +init.o: $(hdrdir)/ruby/internal/interpreter.h +init.o: $(hdrdir)/ruby/internal/iterator.h +init.o: $(hdrdir)/ruby/internal/memory.h +init.o: $(hdrdir)/ruby/internal/method.h +init.o: $(hdrdir)/ruby/internal/module.h +init.o: $(hdrdir)/ruby/internal/newobj.h +init.o: $(hdrdir)/ruby/internal/scan_args.h +init.o: $(hdrdir)/ruby/internal/special_consts.h +init.o: $(hdrdir)/ruby/internal/static_assert.h +init.o: $(hdrdir)/ruby/internal/stdalign.h +init.o: $(hdrdir)/ruby/internal/stdbool.h +init.o: $(hdrdir)/ruby/internal/stdckdint.h +init.o: $(hdrdir)/ruby/internal/symbol.h +init.o: $(hdrdir)/ruby/internal/value.h +init.o: $(hdrdir)/ruby/internal/value_type.h +init.o: $(hdrdir)/ruby/internal/variable.h +init.o: $(hdrdir)/ruby/internal/warning_push.h +init.o: $(hdrdir)/ruby/internal/xmalloc.h +init.o: $(hdrdir)/ruby/io.h +init.o: $(hdrdir)/ruby/missing.h +init.o: $(hdrdir)/ruby/onigmo.h +init.o: $(hdrdir)/ruby/oniguruma.h +init.o: $(hdrdir)/ruby/ruby.h +init.o: $(hdrdir)/ruby/st.h +init.o: $(hdrdir)/ruby/subst.h +init.o: $(hdrdir)/ruby/thread.h +init.o: $(hdrdir)/ruby/thread_native.h +init.o: $(hdrdir)/ruby/util.h +init.o: $(hdrdir)/ruby/version.h +init.o: $(top_srcdir)/ccan/check_type/check_type.h +init.o: $(top_srcdir)/ccan/container_of/container_of.h +init.o: $(top_srcdir)/ccan/list/list.h +init.o: $(top_srcdir)/ccan/str/str.h +init.o: $(top_srcdir)/encindex.h +init.o: $(top_srcdir)/id_table.h +init.o: $(top_srcdir)/internal.h +init.o: $(top_srcdir)/internal/array.h +init.o: $(top_srcdir)/internal/basic_operators.h +init.o: $(top_srcdir)/internal/box.h +init.o: $(top_srcdir)/internal/compilers.h +init.o: $(top_srcdir)/internal/error.h +init.o: $(top_srcdir)/internal/gc.h +init.o: $(top_srcdir)/internal/imemo.h +init.o: $(top_srcdir)/internal/io.h +init.o: $(top_srcdir)/internal/sanitizers.h +init.o: $(top_srcdir)/internal/serial.h +init.o: $(top_srcdir)/internal/set_table.h +init.o: $(top_srcdir)/internal/static_assert.h +init.o: $(top_srcdir)/internal/string.h +init.o: $(top_srcdir)/internal/thread.h +init.o: $(top_srcdir)/internal/vm.h +init.o: $(top_srcdir)/internal/warnings.h +init.o: $(top_srcdir)/method.h +init.o: $(top_srcdir)/node.h +init.o: $(top_srcdir)/ruby_assert.h +init.o: $(top_srcdir)/ruby_atomic.h +init.o: $(top_srcdir)/rubyparser.h +init.o: $(top_srcdir)/shape.h +init.o: $(top_srcdir)/thread_pthread.h +init.o: $(top_srcdir)/vm_core.h +init.o: $(top_srcdir)/vm_opts.h +init.o: constdefs.h +init.o: init.c +init.o: rubysocket.h +init.o: sockport.h +init.o: {$(VPATH)}id.h +ipsocket.o: $(RUBY_EXTCONF_H) +ipsocket.o: $(arch_hdrdir)/ruby/config.h +ipsocket.o: $(hdrdir)/ruby/assert.h +ipsocket.o: $(hdrdir)/ruby/atomic.h +ipsocket.o: $(hdrdir)/ruby/backward.h +ipsocket.o: $(hdrdir)/ruby/backward/2/assume.h +ipsocket.o: $(hdrdir)/ruby/backward/2/attributes.h +ipsocket.o: $(hdrdir)/ruby/backward/2/bool.h +ipsocket.o: $(hdrdir)/ruby/backward/2/gcc_version_since.h +ipsocket.o: $(hdrdir)/ruby/backward/2/inttypes.h +ipsocket.o: $(hdrdir)/ruby/backward/2/limits.h +ipsocket.o: $(hdrdir)/ruby/backward/2/long_long.h +ipsocket.o: $(hdrdir)/ruby/backward/2/stdalign.h +ipsocket.o: $(hdrdir)/ruby/backward/2/stdarg.h +ipsocket.o: $(hdrdir)/ruby/defines.h +ipsocket.o: $(hdrdir)/ruby/encoding.h +ipsocket.o: $(hdrdir)/ruby/fiber/scheduler.h +ipsocket.o: $(hdrdir)/ruby/intern.h +ipsocket.o: $(hdrdir)/ruby/internal/abi.h +ipsocket.o: $(hdrdir)/ruby/internal/anyargs.h +ipsocket.o: $(hdrdir)/ruby/internal/arithmetic.h +ipsocket.o: $(hdrdir)/ruby/internal/arithmetic/char.h +ipsocket.o: $(hdrdir)/ruby/internal/arithmetic/double.h +ipsocket.o: $(hdrdir)/ruby/internal/arithmetic/fixnum.h +ipsocket.o: $(hdrdir)/ruby/internal/arithmetic/gid_t.h +ipsocket.o: $(hdrdir)/ruby/internal/arithmetic/int.h +ipsocket.o: $(hdrdir)/ruby/internal/arithmetic/intptr_t.h +ipsocket.o: $(hdrdir)/ruby/internal/arithmetic/long.h +ipsocket.o: $(hdrdir)/ruby/internal/arithmetic/long_long.h +ipsocket.o: $(hdrdir)/ruby/internal/arithmetic/mode_t.h +ipsocket.o: $(hdrdir)/ruby/internal/arithmetic/off_t.h +ipsocket.o: $(hdrdir)/ruby/internal/arithmetic/pid_t.h +ipsocket.o: $(hdrdir)/ruby/internal/arithmetic/short.h +ipsocket.o: $(hdrdir)/ruby/internal/arithmetic/size_t.h +ipsocket.o: $(hdrdir)/ruby/internal/arithmetic/st_data_t.h +ipsocket.o: $(hdrdir)/ruby/internal/arithmetic/uid_t.h +ipsocket.o: $(hdrdir)/ruby/internal/assume.h +ipsocket.o: $(hdrdir)/ruby/internal/attr/alloc_size.h +ipsocket.o: $(hdrdir)/ruby/internal/attr/artificial.h +ipsocket.o: $(hdrdir)/ruby/internal/attr/cold.h +ipsocket.o: $(hdrdir)/ruby/internal/attr/const.h +ipsocket.o: $(hdrdir)/ruby/internal/attr/constexpr.h +ipsocket.o: $(hdrdir)/ruby/internal/attr/deprecated.h +ipsocket.o: $(hdrdir)/ruby/internal/attr/diagnose_if.h +ipsocket.o: $(hdrdir)/ruby/internal/attr/enum_extensibility.h +ipsocket.o: $(hdrdir)/ruby/internal/attr/error.h +ipsocket.o: $(hdrdir)/ruby/internal/attr/flag_enum.h +ipsocket.o: $(hdrdir)/ruby/internal/attr/forceinline.h +ipsocket.o: $(hdrdir)/ruby/internal/attr/format.h +ipsocket.o: $(hdrdir)/ruby/internal/attr/maybe_unused.h +ipsocket.o: $(hdrdir)/ruby/internal/attr/noalias.h +ipsocket.o: $(hdrdir)/ruby/internal/attr/nodiscard.h +ipsocket.o: $(hdrdir)/ruby/internal/attr/noexcept.h +ipsocket.o: $(hdrdir)/ruby/internal/attr/noinline.h +ipsocket.o: $(hdrdir)/ruby/internal/attr/nonnull.h +ipsocket.o: $(hdrdir)/ruby/internal/attr/noreturn.h +ipsocket.o: $(hdrdir)/ruby/internal/attr/packed_struct.h +ipsocket.o: $(hdrdir)/ruby/internal/attr/pure.h +ipsocket.o: $(hdrdir)/ruby/internal/attr/restrict.h +ipsocket.o: $(hdrdir)/ruby/internal/attr/returns_nonnull.h +ipsocket.o: $(hdrdir)/ruby/internal/attr/warning.h +ipsocket.o: $(hdrdir)/ruby/internal/attr/weakref.h +ipsocket.o: $(hdrdir)/ruby/internal/cast.h +ipsocket.o: $(hdrdir)/ruby/internal/compiler_is.h +ipsocket.o: $(hdrdir)/ruby/internal/compiler_is/apple.h +ipsocket.o: $(hdrdir)/ruby/internal/compiler_is/clang.h +ipsocket.o: $(hdrdir)/ruby/internal/compiler_is/gcc.h +ipsocket.o: $(hdrdir)/ruby/internal/compiler_is/intel.h +ipsocket.o: $(hdrdir)/ruby/internal/compiler_is/msvc.h +ipsocket.o: $(hdrdir)/ruby/internal/compiler_is/sunpro.h +ipsocket.o: $(hdrdir)/ruby/internal/compiler_since.h +ipsocket.o: $(hdrdir)/ruby/internal/config.h +ipsocket.o: $(hdrdir)/ruby/internal/constant_p.h +ipsocket.o: $(hdrdir)/ruby/internal/core.h +ipsocket.o: $(hdrdir)/ruby/internal/core/rarray.h +ipsocket.o: $(hdrdir)/ruby/internal/core/rbasic.h +ipsocket.o: $(hdrdir)/ruby/internal/core/rbignum.h +ipsocket.o: $(hdrdir)/ruby/internal/core/rclass.h +ipsocket.o: $(hdrdir)/ruby/internal/core/rdata.h +ipsocket.o: $(hdrdir)/ruby/internal/core/rfile.h +ipsocket.o: $(hdrdir)/ruby/internal/core/rhash.h +ipsocket.o: $(hdrdir)/ruby/internal/core/robject.h +ipsocket.o: $(hdrdir)/ruby/internal/core/rregexp.h +ipsocket.o: $(hdrdir)/ruby/internal/core/rstring.h +ipsocket.o: $(hdrdir)/ruby/internal/core/rstruct.h +ipsocket.o: $(hdrdir)/ruby/internal/core/rtypeddata.h +ipsocket.o: $(hdrdir)/ruby/internal/ctype.h +ipsocket.o: $(hdrdir)/ruby/internal/dllexport.h +ipsocket.o: $(hdrdir)/ruby/internal/dosish.h +ipsocket.o: $(hdrdir)/ruby/internal/encoding/coderange.h +ipsocket.o: $(hdrdir)/ruby/internal/encoding/ctype.h +ipsocket.o: $(hdrdir)/ruby/internal/encoding/encoding.h +ipsocket.o: $(hdrdir)/ruby/internal/encoding/pathname.h +ipsocket.o: $(hdrdir)/ruby/internal/encoding/re.h +ipsocket.o: $(hdrdir)/ruby/internal/encoding/sprintf.h +ipsocket.o: $(hdrdir)/ruby/internal/encoding/string.h +ipsocket.o: $(hdrdir)/ruby/internal/encoding/symbol.h +ipsocket.o: $(hdrdir)/ruby/internal/encoding/transcode.h +ipsocket.o: $(hdrdir)/ruby/internal/error.h +ipsocket.o: $(hdrdir)/ruby/internal/eval.h +ipsocket.o: $(hdrdir)/ruby/internal/event.h +ipsocket.o: $(hdrdir)/ruby/internal/fl_type.h +ipsocket.o: $(hdrdir)/ruby/internal/gc.h +ipsocket.o: $(hdrdir)/ruby/internal/glob.h +ipsocket.o: $(hdrdir)/ruby/internal/globals.h +ipsocket.o: $(hdrdir)/ruby/internal/has/attribute.h +ipsocket.o: $(hdrdir)/ruby/internal/has/builtin.h +ipsocket.o: $(hdrdir)/ruby/internal/has/c_attribute.h +ipsocket.o: $(hdrdir)/ruby/internal/has/cpp_attribute.h +ipsocket.o: $(hdrdir)/ruby/internal/has/declspec_attribute.h +ipsocket.o: $(hdrdir)/ruby/internal/has/extension.h +ipsocket.o: $(hdrdir)/ruby/internal/has/feature.h +ipsocket.o: $(hdrdir)/ruby/internal/has/warning.h +ipsocket.o: $(hdrdir)/ruby/internal/intern/array.h +ipsocket.o: $(hdrdir)/ruby/internal/intern/bignum.h +ipsocket.o: $(hdrdir)/ruby/internal/intern/class.h +ipsocket.o: $(hdrdir)/ruby/internal/intern/compar.h +ipsocket.o: $(hdrdir)/ruby/internal/intern/complex.h +ipsocket.o: $(hdrdir)/ruby/internal/intern/cont.h +ipsocket.o: $(hdrdir)/ruby/internal/intern/dir.h +ipsocket.o: $(hdrdir)/ruby/internal/intern/enum.h +ipsocket.o: $(hdrdir)/ruby/internal/intern/enumerator.h +ipsocket.o: $(hdrdir)/ruby/internal/intern/error.h +ipsocket.o: $(hdrdir)/ruby/internal/intern/eval.h +ipsocket.o: $(hdrdir)/ruby/internal/intern/file.h +ipsocket.o: $(hdrdir)/ruby/internal/intern/hash.h +ipsocket.o: $(hdrdir)/ruby/internal/intern/io.h +ipsocket.o: $(hdrdir)/ruby/internal/intern/load.h +ipsocket.o: $(hdrdir)/ruby/internal/intern/marshal.h +ipsocket.o: $(hdrdir)/ruby/internal/intern/numeric.h +ipsocket.o: $(hdrdir)/ruby/internal/intern/object.h +ipsocket.o: $(hdrdir)/ruby/internal/intern/parse.h +ipsocket.o: $(hdrdir)/ruby/internal/intern/proc.h +ipsocket.o: $(hdrdir)/ruby/internal/intern/process.h +ipsocket.o: $(hdrdir)/ruby/internal/intern/random.h +ipsocket.o: $(hdrdir)/ruby/internal/intern/range.h +ipsocket.o: $(hdrdir)/ruby/internal/intern/rational.h +ipsocket.o: $(hdrdir)/ruby/internal/intern/re.h +ipsocket.o: $(hdrdir)/ruby/internal/intern/ruby.h +ipsocket.o: $(hdrdir)/ruby/internal/intern/select.h +ipsocket.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +ipsocket.o: $(hdrdir)/ruby/internal/intern/set.h +ipsocket.o: $(hdrdir)/ruby/internal/intern/signal.h +ipsocket.o: $(hdrdir)/ruby/internal/intern/sprintf.h +ipsocket.o: $(hdrdir)/ruby/internal/intern/string.h +ipsocket.o: $(hdrdir)/ruby/internal/intern/struct.h +ipsocket.o: $(hdrdir)/ruby/internal/intern/thread.h +ipsocket.o: $(hdrdir)/ruby/internal/intern/time.h +ipsocket.o: $(hdrdir)/ruby/internal/intern/variable.h +ipsocket.o: $(hdrdir)/ruby/internal/intern/vm.h +ipsocket.o: $(hdrdir)/ruby/internal/interpreter.h +ipsocket.o: $(hdrdir)/ruby/internal/iterator.h +ipsocket.o: $(hdrdir)/ruby/internal/memory.h +ipsocket.o: $(hdrdir)/ruby/internal/method.h +ipsocket.o: $(hdrdir)/ruby/internal/module.h +ipsocket.o: $(hdrdir)/ruby/internal/newobj.h +ipsocket.o: $(hdrdir)/ruby/internal/scan_args.h +ipsocket.o: $(hdrdir)/ruby/internal/special_consts.h +ipsocket.o: $(hdrdir)/ruby/internal/static_assert.h +ipsocket.o: $(hdrdir)/ruby/internal/stdalign.h +ipsocket.o: $(hdrdir)/ruby/internal/stdbool.h +ipsocket.o: $(hdrdir)/ruby/internal/stdckdint.h +ipsocket.o: $(hdrdir)/ruby/internal/symbol.h +ipsocket.o: $(hdrdir)/ruby/internal/value.h +ipsocket.o: $(hdrdir)/ruby/internal/value_type.h +ipsocket.o: $(hdrdir)/ruby/internal/variable.h +ipsocket.o: $(hdrdir)/ruby/internal/warning_push.h +ipsocket.o: $(hdrdir)/ruby/internal/xmalloc.h +ipsocket.o: $(hdrdir)/ruby/io.h +ipsocket.o: $(hdrdir)/ruby/missing.h +ipsocket.o: $(hdrdir)/ruby/onigmo.h +ipsocket.o: $(hdrdir)/ruby/oniguruma.h +ipsocket.o: $(hdrdir)/ruby/ruby.h +ipsocket.o: $(hdrdir)/ruby/st.h +ipsocket.o: $(hdrdir)/ruby/subst.h +ipsocket.o: $(hdrdir)/ruby/thread.h +ipsocket.o: $(hdrdir)/ruby/thread_native.h +ipsocket.o: $(hdrdir)/ruby/util.h +ipsocket.o: $(hdrdir)/ruby/version.h +ipsocket.o: $(top_srcdir)/ccan/check_type/check_type.h +ipsocket.o: $(top_srcdir)/ccan/container_of/container_of.h +ipsocket.o: $(top_srcdir)/ccan/list/list.h +ipsocket.o: $(top_srcdir)/ccan/str/str.h +ipsocket.o: $(top_srcdir)/encindex.h +ipsocket.o: $(top_srcdir)/id_table.h +ipsocket.o: $(top_srcdir)/internal.h +ipsocket.o: $(top_srcdir)/internal/array.h +ipsocket.o: $(top_srcdir)/internal/basic_operators.h +ipsocket.o: $(top_srcdir)/internal/box.h +ipsocket.o: $(top_srcdir)/internal/compilers.h +ipsocket.o: $(top_srcdir)/internal/error.h +ipsocket.o: $(top_srcdir)/internal/gc.h +ipsocket.o: $(top_srcdir)/internal/imemo.h +ipsocket.o: $(top_srcdir)/internal/io.h +ipsocket.o: $(top_srcdir)/internal/sanitizers.h +ipsocket.o: $(top_srcdir)/internal/serial.h +ipsocket.o: $(top_srcdir)/internal/set_table.h +ipsocket.o: $(top_srcdir)/internal/static_assert.h +ipsocket.o: $(top_srcdir)/internal/string.h +ipsocket.o: $(top_srcdir)/internal/thread.h +ipsocket.o: $(top_srcdir)/internal/vm.h +ipsocket.o: $(top_srcdir)/internal/warnings.h +ipsocket.o: $(top_srcdir)/method.h +ipsocket.o: $(top_srcdir)/node.h +ipsocket.o: $(top_srcdir)/ruby_assert.h +ipsocket.o: $(top_srcdir)/ruby_atomic.h +ipsocket.o: $(top_srcdir)/rubyparser.h +ipsocket.o: $(top_srcdir)/shape.h +ipsocket.o: $(top_srcdir)/thread_pthread.h +ipsocket.o: $(top_srcdir)/vm_core.h +ipsocket.o: $(top_srcdir)/vm_opts.h +ipsocket.o: constdefs.h +ipsocket.o: ipsocket.c +ipsocket.o: rubysocket.h +ipsocket.o: sockport.h +ipsocket.o: {$(VPATH)}id.h +option.o: $(RUBY_EXTCONF_H) +option.o: $(arch_hdrdir)/ruby/config.h +option.o: $(hdrdir)/ruby/assert.h +option.o: $(hdrdir)/ruby/atomic.h +option.o: $(hdrdir)/ruby/backward.h +option.o: $(hdrdir)/ruby/backward/2/assume.h +option.o: $(hdrdir)/ruby/backward/2/attributes.h +option.o: $(hdrdir)/ruby/backward/2/bool.h +option.o: $(hdrdir)/ruby/backward/2/gcc_version_since.h +option.o: $(hdrdir)/ruby/backward/2/inttypes.h +option.o: $(hdrdir)/ruby/backward/2/limits.h +option.o: $(hdrdir)/ruby/backward/2/long_long.h +option.o: $(hdrdir)/ruby/backward/2/stdalign.h +option.o: $(hdrdir)/ruby/backward/2/stdarg.h +option.o: $(hdrdir)/ruby/defines.h +option.o: $(hdrdir)/ruby/encoding.h +option.o: $(hdrdir)/ruby/fiber/scheduler.h +option.o: $(hdrdir)/ruby/intern.h +option.o: $(hdrdir)/ruby/internal/abi.h +option.o: $(hdrdir)/ruby/internal/anyargs.h +option.o: $(hdrdir)/ruby/internal/arithmetic.h +option.o: $(hdrdir)/ruby/internal/arithmetic/char.h +option.o: $(hdrdir)/ruby/internal/arithmetic/double.h +option.o: $(hdrdir)/ruby/internal/arithmetic/fixnum.h +option.o: $(hdrdir)/ruby/internal/arithmetic/gid_t.h +option.o: $(hdrdir)/ruby/internal/arithmetic/int.h +option.o: $(hdrdir)/ruby/internal/arithmetic/intptr_t.h +option.o: $(hdrdir)/ruby/internal/arithmetic/long.h +option.o: $(hdrdir)/ruby/internal/arithmetic/long_long.h +option.o: $(hdrdir)/ruby/internal/arithmetic/mode_t.h +option.o: $(hdrdir)/ruby/internal/arithmetic/off_t.h +option.o: $(hdrdir)/ruby/internal/arithmetic/pid_t.h +option.o: $(hdrdir)/ruby/internal/arithmetic/short.h +option.o: $(hdrdir)/ruby/internal/arithmetic/size_t.h +option.o: $(hdrdir)/ruby/internal/arithmetic/st_data_t.h +option.o: $(hdrdir)/ruby/internal/arithmetic/uid_t.h +option.o: $(hdrdir)/ruby/internal/assume.h +option.o: $(hdrdir)/ruby/internal/attr/alloc_size.h +option.o: $(hdrdir)/ruby/internal/attr/artificial.h +option.o: $(hdrdir)/ruby/internal/attr/cold.h +option.o: $(hdrdir)/ruby/internal/attr/const.h +option.o: $(hdrdir)/ruby/internal/attr/constexpr.h +option.o: $(hdrdir)/ruby/internal/attr/deprecated.h +option.o: $(hdrdir)/ruby/internal/attr/diagnose_if.h +option.o: $(hdrdir)/ruby/internal/attr/enum_extensibility.h +option.o: $(hdrdir)/ruby/internal/attr/error.h +option.o: $(hdrdir)/ruby/internal/attr/flag_enum.h +option.o: $(hdrdir)/ruby/internal/attr/forceinline.h +option.o: $(hdrdir)/ruby/internal/attr/format.h +option.o: $(hdrdir)/ruby/internal/attr/maybe_unused.h +option.o: $(hdrdir)/ruby/internal/attr/noalias.h +option.o: $(hdrdir)/ruby/internal/attr/nodiscard.h +option.o: $(hdrdir)/ruby/internal/attr/noexcept.h +option.o: $(hdrdir)/ruby/internal/attr/noinline.h +option.o: $(hdrdir)/ruby/internal/attr/nonnull.h +option.o: $(hdrdir)/ruby/internal/attr/noreturn.h +option.o: $(hdrdir)/ruby/internal/attr/packed_struct.h +option.o: $(hdrdir)/ruby/internal/attr/pure.h +option.o: $(hdrdir)/ruby/internal/attr/restrict.h +option.o: $(hdrdir)/ruby/internal/attr/returns_nonnull.h +option.o: $(hdrdir)/ruby/internal/attr/warning.h +option.o: $(hdrdir)/ruby/internal/attr/weakref.h +option.o: $(hdrdir)/ruby/internal/cast.h +option.o: $(hdrdir)/ruby/internal/compiler_is.h +option.o: $(hdrdir)/ruby/internal/compiler_is/apple.h +option.o: $(hdrdir)/ruby/internal/compiler_is/clang.h +option.o: $(hdrdir)/ruby/internal/compiler_is/gcc.h +option.o: $(hdrdir)/ruby/internal/compiler_is/intel.h +option.o: $(hdrdir)/ruby/internal/compiler_is/msvc.h +option.o: $(hdrdir)/ruby/internal/compiler_is/sunpro.h +option.o: $(hdrdir)/ruby/internal/compiler_since.h +option.o: $(hdrdir)/ruby/internal/config.h +option.o: $(hdrdir)/ruby/internal/constant_p.h +option.o: $(hdrdir)/ruby/internal/core.h +option.o: $(hdrdir)/ruby/internal/core/rarray.h +option.o: $(hdrdir)/ruby/internal/core/rbasic.h +option.o: $(hdrdir)/ruby/internal/core/rbignum.h +option.o: $(hdrdir)/ruby/internal/core/rclass.h +option.o: $(hdrdir)/ruby/internal/core/rdata.h +option.o: $(hdrdir)/ruby/internal/core/rfile.h +option.o: $(hdrdir)/ruby/internal/core/rhash.h +option.o: $(hdrdir)/ruby/internal/core/robject.h +option.o: $(hdrdir)/ruby/internal/core/rregexp.h +option.o: $(hdrdir)/ruby/internal/core/rstring.h +option.o: $(hdrdir)/ruby/internal/core/rstruct.h +option.o: $(hdrdir)/ruby/internal/core/rtypeddata.h +option.o: $(hdrdir)/ruby/internal/ctype.h +option.o: $(hdrdir)/ruby/internal/dllexport.h +option.o: $(hdrdir)/ruby/internal/dosish.h +option.o: $(hdrdir)/ruby/internal/encoding/coderange.h +option.o: $(hdrdir)/ruby/internal/encoding/ctype.h +option.o: $(hdrdir)/ruby/internal/encoding/encoding.h +option.o: $(hdrdir)/ruby/internal/encoding/pathname.h +option.o: $(hdrdir)/ruby/internal/encoding/re.h +option.o: $(hdrdir)/ruby/internal/encoding/sprintf.h +option.o: $(hdrdir)/ruby/internal/encoding/string.h +option.o: $(hdrdir)/ruby/internal/encoding/symbol.h +option.o: $(hdrdir)/ruby/internal/encoding/transcode.h +option.o: $(hdrdir)/ruby/internal/error.h +option.o: $(hdrdir)/ruby/internal/eval.h +option.o: $(hdrdir)/ruby/internal/event.h +option.o: $(hdrdir)/ruby/internal/fl_type.h +option.o: $(hdrdir)/ruby/internal/gc.h +option.o: $(hdrdir)/ruby/internal/glob.h +option.o: $(hdrdir)/ruby/internal/globals.h +option.o: $(hdrdir)/ruby/internal/has/attribute.h +option.o: $(hdrdir)/ruby/internal/has/builtin.h +option.o: $(hdrdir)/ruby/internal/has/c_attribute.h +option.o: $(hdrdir)/ruby/internal/has/cpp_attribute.h +option.o: $(hdrdir)/ruby/internal/has/declspec_attribute.h +option.o: $(hdrdir)/ruby/internal/has/extension.h +option.o: $(hdrdir)/ruby/internal/has/feature.h +option.o: $(hdrdir)/ruby/internal/has/warning.h +option.o: $(hdrdir)/ruby/internal/intern/array.h +option.o: $(hdrdir)/ruby/internal/intern/bignum.h +option.o: $(hdrdir)/ruby/internal/intern/class.h +option.o: $(hdrdir)/ruby/internal/intern/compar.h +option.o: $(hdrdir)/ruby/internal/intern/complex.h +option.o: $(hdrdir)/ruby/internal/intern/cont.h +option.o: $(hdrdir)/ruby/internal/intern/dir.h +option.o: $(hdrdir)/ruby/internal/intern/enum.h +option.o: $(hdrdir)/ruby/internal/intern/enumerator.h +option.o: $(hdrdir)/ruby/internal/intern/error.h +option.o: $(hdrdir)/ruby/internal/intern/eval.h +option.o: $(hdrdir)/ruby/internal/intern/file.h +option.o: $(hdrdir)/ruby/internal/intern/hash.h +option.o: $(hdrdir)/ruby/internal/intern/io.h +option.o: $(hdrdir)/ruby/internal/intern/load.h +option.o: $(hdrdir)/ruby/internal/intern/marshal.h +option.o: $(hdrdir)/ruby/internal/intern/numeric.h +option.o: $(hdrdir)/ruby/internal/intern/object.h +option.o: $(hdrdir)/ruby/internal/intern/parse.h +option.o: $(hdrdir)/ruby/internal/intern/proc.h +option.o: $(hdrdir)/ruby/internal/intern/process.h +option.o: $(hdrdir)/ruby/internal/intern/random.h +option.o: $(hdrdir)/ruby/internal/intern/range.h +option.o: $(hdrdir)/ruby/internal/intern/rational.h +option.o: $(hdrdir)/ruby/internal/intern/re.h +option.o: $(hdrdir)/ruby/internal/intern/ruby.h +option.o: $(hdrdir)/ruby/internal/intern/select.h +option.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +option.o: $(hdrdir)/ruby/internal/intern/set.h +option.o: $(hdrdir)/ruby/internal/intern/signal.h +option.o: $(hdrdir)/ruby/internal/intern/sprintf.h +option.o: $(hdrdir)/ruby/internal/intern/string.h +option.o: $(hdrdir)/ruby/internal/intern/struct.h +option.o: $(hdrdir)/ruby/internal/intern/thread.h +option.o: $(hdrdir)/ruby/internal/intern/time.h +option.o: $(hdrdir)/ruby/internal/intern/variable.h +option.o: $(hdrdir)/ruby/internal/intern/vm.h +option.o: $(hdrdir)/ruby/internal/interpreter.h +option.o: $(hdrdir)/ruby/internal/iterator.h +option.o: $(hdrdir)/ruby/internal/memory.h +option.o: $(hdrdir)/ruby/internal/method.h +option.o: $(hdrdir)/ruby/internal/module.h +option.o: $(hdrdir)/ruby/internal/newobj.h +option.o: $(hdrdir)/ruby/internal/scan_args.h +option.o: $(hdrdir)/ruby/internal/special_consts.h +option.o: $(hdrdir)/ruby/internal/static_assert.h +option.o: $(hdrdir)/ruby/internal/stdalign.h +option.o: $(hdrdir)/ruby/internal/stdbool.h +option.o: $(hdrdir)/ruby/internal/stdckdint.h +option.o: $(hdrdir)/ruby/internal/symbol.h +option.o: $(hdrdir)/ruby/internal/value.h +option.o: $(hdrdir)/ruby/internal/value_type.h +option.o: $(hdrdir)/ruby/internal/variable.h +option.o: $(hdrdir)/ruby/internal/warning_push.h +option.o: $(hdrdir)/ruby/internal/xmalloc.h +option.o: $(hdrdir)/ruby/io.h +option.o: $(hdrdir)/ruby/missing.h +option.o: $(hdrdir)/ruby/onigmo.h +option.o: $(hdrdir)/ruby/oniguruma.h +option.o: $(hdrdir)/ruby/ruby.h +option.o: $(hdrdir)/ruby/st.h +option.o: $(hdrdir)/ruby/subst.h +option.o: $(hdrdir)/ruby/thread.h +option.o: $(hdrdir)/ruby/thread_native.h +option.o: $(hdrdir)/ruby/util.h +option.o: $(hdrdir)/ruby/version.h +option.o: $(top_srcdir)/ccan/check_type/check_type.h +option.o: $(top_srcdir)/ccan/container_of/container_of.h +option.o: $(top_srcdir)/ccan/list/list.h +option.o: $(top_srcdir)/ccan/str/str.h +option.o: $(top_srcdir)/encindex.h +option.o: $(top_srcdir)/id_table.h +option.o: $(top_srcdir)/internal.h +option.o: $(top_srcdir)/internal/array.h +option.o: $(top_srcdir)/internal/basic_operators.h +option.o: $(top_srcdir)/internal/box.h +option.o: $(top_srcdir)/internal/compilers.h +option.o: $(top_srcdir)/internal/error.h +option.o: $(top_srcdir)/internal/gc.h +option.o: $(top_srcdir)/internal/imemo.h +option.o: $(top_srcdir)/internal/io.h +option.o: $(top_srcdir)/internal/sanitizers.h +option.o: $(top_srcdir)/internal/serial.h +option.o: $(top_srcdir)/internal/set_table.h +option.o: $(top_srcdir)/internal/static_assert.h +option.o: $(top_srcdir)/internal/string.h +option.o: $(top_srcdir)/internal/thread.h +option.o: $(top_srcdir)/internal/vm.h +option.o: $(top_srcdir)/internal/warnings.h +option.o: $(top_srcdir)/method.h +option.o: $(top_srcdir)/node.h +option.o: $(top_srcdir)/ruby_assert.h +option.o: $(top_srcdir)/ruby_atomic.h +option.o: $(top_srcdir)/rubyparser.h +option.o: $(top_srcdir)/shape.h +option.o: $(top_srcdir)/thread_pthread.h +option.o: $(top_srcdir)/vm_core.h +option.o: $(top_srcdir)/vm_opts.h +option.o: constdefs.h +option.o: option.c +option.o: rubysocket.h +option.o: sockport.h +option.o: {$(VPATH)}id.h +raddrinfo.o: $(RUBY_EXTCONF_H) +raddrinfo.o: $(arch_hdrdir)/ruby/config.h +raddrinfo.o: $(hdrdir)/ruby/assert.h +raddrinfo.o: $(hdrdir)/ruby/atomic.h +raddrinfo.o: $(hdrdir)/ruby/backward.h +raddrinfo.o: $(hdrdir)/ruby/backward/2/assume.h +raddrinfo.o: $(hdrdir)/ruby/backward/2/attributes.h +raddrinfo.o: $(hdrdir)/ruby/backward/2/bool.h +raddrinfo.o: $(hdrdir)/ruby/backward/2/gcc_version_since.h +raddrinfo.o: $(hdrdir)/ruby/backward/2/inttypes.h +raddrinfo.o: $(hdrdir)/ruby/backward/2/limits.h +raddrinfo.o: $(hdrdir)/ruby/backward/2/long_long.h +raddrinfo.o: $(hdrdir)/ruby/backward/2/stdalign.h +raddrinfo.o: $(hdrdir)/ruby/backward/2/stdarg.h +raddrinfo.o: $(hdrdir)/ruby/defines.h +raddrinfo.o: $(hdrdir)/ruby/encoding.h +raddrinfo.o: $(hdrdir)/ruby/fiber/scheduler.h +raddrinfo.o: $(hdrdir)/ruby/intern.h +raddrinfo.o: $(hdrdir)/ruby/internal/abi.h +raddrinfo.o: $(hdrdir)/ruby/internal/anyargs.h +raddrinfo.o: $(hdrdir)/ruby/internal/arithmetic.h +raddrinfo.o: $(hdrdir)/ruby/internal/arithmetic/char.h +raddrinfo.o: $(hdrdir)/ruby/internal/arithmetic/double.h +raddrinfo.o: $(hdrdir)/ruby/internal/arithmetic/fixnum.h +raddrinfo.o: $(hdrdir)/ruby/internal/arithmetic/gid_t.h +raddrinfo.o: $(hdrdir)/ruby/internal/arithmetic/int.h +raddrinfo.o: $(hdrdir)/ruby/internal/arithmetic/intptr_t.h +raddrinfo.o: $(hdrdir)/ruby/internal/arithmetic/long.h +raddrinfo.o: $(hdrdir)/ruby/internal/arithmetic/long_long.h +raddrinfo.o: $(hdrdir)/ruby/internal/arithmetic/mode_t.h +raddrinfo.o: $(hdrdir)/ruby/internal/arithmetic/off_t.h +raddrinfo.o: $(hdrdir)/ruby/internal/arithmetic/pid_t.h +raddrinfo.o: $(hdrdir)/ruby/internal/arithmetic/short.h +raddrinfo.o: $(hdrdir)/ruby/internal/arithmetic/size_t.h +raddrinfo.o: $(hdrdir)/ruby/internal/arithmetic/st_data_t.h +raddrinfo.o: $(hdrdir)/ruby/internal/arithmetic/uid_t.h +raddrinfo.o: $(hdrdir)/ruby/internal/assume.h +raddrinfo.o: $(hdrdir)/ruby/internal/attr/alloc_size.h +raddrinfo.o: $(hdrdir)/ruby/internal/attr/artificial.h +raddrinfo.o: $(hdrdir)/ruby/internal/attr/cold.h +raddrinfo.o: $(hdrdir)/ruby/internal/attr/const.h +raddrinfo.o: $(hdrdir)/ruby/internal/attr/constexpr.h +raddrinfo.o: $(hdrdir)/ruby/internal/attr/deprecated.h +raddrinfo.o: $(hdrdir)/ruby/internal/attr/diagnose_if.h +raddrinfo.o: $(hdrdir)/ruby/internal/attr/enum_extensibility.h +raddrinfo.o: $(hdrdir)/ruby/internal/attr/error.h +raddrinfo.o: $(hdrdir)/ruby/internal/attr/flag_enum.h +raddrinfo.o: $(hdrdir)/ruby/internal/attr/forceinline.h +raddrinfo.o: $(hdrdir)/ruby/internal/attr/format.h +raddrinfo.o: $(hdrdir)/ruby/internal/attr/maybe_unused.h +raddrinfo.o: $(hdrdir)/ruby/internal/attr/noalias.h +raddrinfo.o: $(hdrdir)/ruby/internal/attr/nodiscard.h +raddrinfo.o: $(hdrdir)/ruby/internal/attr/noexcept.h +raddrinfo.o: $(hdrdir)/ruby/internal/attr/noinline.h +raddrinfo.o: $(hdrdir)/ruby/internal/attr/nonnull.h +raddrinfo.o: $(hdrdir)/ruby/internal/attr/noreturn.h +raddrinfo.o: $(hdrdir)/ruby/internal/attr/packed_struct.h +raddrinfo.o: $(hdrdir)/ruby/internal/attr/pure.h +raddrinfo.o: $(hdrdir)/ruby/internal/attr/restrict.h +raddrinfo.o: $(hdrdir)/ruby/internal/attr/returns_nonnull.h +raddrinfo.o: $(hdrdir)/ruby/internal/attr/warning.h +raddrinfo.o: $(hdrdir)/ruby/internal/attr/weakref.h +raddrinfo.o: $(hdrdir)/ruby/internal/cast.h +raddrinfo.o: $(hdrdir)/ruby/internal/compiler_is.h +raddrinfo.o: $(hdrdir)/ruby/internal/compiler_is/apple.h +raddrinfo.o: $(hdrdir)/ruby/internal/compiler_is/clang.h +raddrinfo.o: $(hdrdir)/ruby/internal/compiler_is/gcc.h +raddrinfo.o: $(hdrdir)/ruby/internal/compiler_is/intel.h +raddrinfo.o: $(hdrdir)/ruby/internal/compiler_is/msvc.h +raddrinfo.o: $(hdrdir)/ruby/internal/compiler_is/sunpro.h +raddrinfo.o: $(hdrdir)/ruby/internal/compiler_since.h +raddrinfo.o: $(hdrdir)/ruby/internal/config.h +raddrinfo.o: $(hdrdir)/ruby/internal/constant_p.h +raddrinfo.o: $(hdrdir)/ruby/internal/core.h +raddrinfo.o: $(hdrdir)/ruby/internal/core/rarray.h +raddrinfo.o: $(hdrdir)/ruby/internal/core/rbasic.h +raddrinfo.o: $(hdrdir)/ruby/internal/core/rbignum.h +raddrinfo.o: $(hdrdir)/ruby/internal/core/rclass.h +raddrinfo.o: $(hdrdir)/ruby/internal/core/rdata.h +raddrinfo.o: $(hdrdir)/ruby/internal/core/rfile.h +raddrinfo.o: $(hdrdir)/ruby/internal/core/rhash.h +raddrinfo.o: $(hdrdir)/ruby/internal/core/robject.h +raddrinfo.o: $(hdrdir)/ruby/internal/core/rregexp.h +raddrinfo.o: $(hdrdir)/ruby/internal/core/rstring.h +raddrinfo.o: $(hdrdir)/ruby/internal/core/rstruct.h +raddrinfo.o: $(hdrdir)/ruby/internal/core/rtypeddata.h +raddrinfo.o: $(hdrdir)/ruby/internal/ctype.h +raddrinfo.o: $(hdrdir)/ruby/internal/dllexport.h +raddrinfo.o: $(hdrdir)/ruby/internal/dosish.h +raddrinfo.o: $(hdrdir)/ruby/internal/encoding/coderange.h +raddrinfo.o: $(hdrdir)/ruby/internal/encoding/ctype.h +raddrinfo.o: $(hdrdir)/ruby/internal/encoding/encoding.h +raddrinfo.o: $(hdrdir)/ruby/internal/encoding/pathname.h +raddrinfo.o: $(hdrdir)/ruby/internal/encoding/re.h +raddrinfo.o: $(hdrdir)/ruby/internal/encoding/sprintf.h +raddrinfo.o: $(hdrdir)/ruby/internal/encoding/string.h +raddrinfo.o: $(hdrdir)/ruby/internal/encoding/symbol.h +raddrinfo.o: $(hdrdir)/ruby/internal/encoding/transcode.h +raddrinfo.o: $(hdrdir)/ruby/internal/error.h +raddrinfo.o: $(hdrdir)/ruby/internal/eval.h +raddrinfo.o: $(hdrdir)/ruby/internal/event.h +raddrinfo.o: $(hdrdir)/ruby/internal/fl_type.h +raddrinfo.o: $(hdrdir)/ruby/internal/gc.h +raddrinfo.o: $(hdrdir)/ruby/internal/glob.h +raddrinfo.o: $(hdrdir)/ruby/internal/globals.h +raddrinfo.o: $(hdrdir)/ruby/internal/has/attribute.h +raddrinfo.o: $(hdrdir)/ruby/internal/has/builtin.h +raddrinfo.o: $(hdrdir)/ruby/internal/has/c_attribute.h +raddrinfo.o: $(hdrdir)/ruby/internal/has/cpp_attribute.h +raddrinfo.o: $(hdrdir)/ruby/internal/has/declspec_attribute.h +raddrinfo.o: $(hdrdir)/ruby/internal/has/extension.h +raddrinfo.o: $(hdrdir)/ruby/internal/has/feature.h +raddrinfo.o: $(hdrdir)/ruby/internal/has/warning.h +raddrinfo.o: $(hdrdir)/ruby/internal/intern/array.h +raddrinfo.o: $(hdrdir)/ruby/internal/intern/bignum.h +raddrinfo.o: $(hdrdir)/ruby/internal/intern/class.h +raddrinfo.o: $(hdrdir)/ruby/internal/intern/compar.h +raddrinfo.o: $(hdrdir)/ruby/internal/intern/complex.h +raddrinfo.o: $(hdrdir)/ruby/internal/intern/cont.h +raddrinfo.o: $(hdrdir)/ruby/internal/intern/dir.h +raddrinfo.o: $(hdrdir)/ruby/internal/intern/enum.h +raddrinfo.o: $(hdrdir)/ruby/internal/intern/enumerator.h +raddrinfo.o: $(hdrdir)/ruby/internal/intern/error.h +raddrinfo.o: $(hdrdir)/ruby/internal/intern/eval.h +raddrinfo.o: $(hdrdir)/ruby/internal/intern/file.h +raddrinfo.o: $(hdrdir)/ruby/internal/intern/hash.h +raddrinfo.o: $(hdrdir)/ruby/internal/intern/io.h +raddrinfo.o: $(hdrdir)/ruby/internal/intern/load.h +raddrinfo.o: $(hdrdir)/ruby/internal/intern/marshal.h +raddrinfo.o: $(hdrdir)/ruby/internal/intern/numeric.h +raddrinfo.o: $(hdrdir)/ruby/internal/intern/object.h +raddrinfo.o: $(hdrdir)/ruby/internal/intern/parse.h +raddrinfo.o: $(hdrdir)/ruby/internal/intern/proc.h +raddrinfo.o: $(hdrdir)/ruby/internal/intern/process.h +raddrinfo.o: $(hdrdir)/ruby/internal/intern/random.h +raddrinfo.o: $(hdrdir)/ruby/internal/intern/range.h +raddrinfo.o: $(hdrdir)/ruby/internal/intern/rational.h +raddrinfo.o: $(hdrdir)/ruby/internal/intern/re.h +raddrinfo.o: $(hdrdir)/ruby/internal/intern/ruby.h +raddrinfo.o: $(hdrdir)/ruby/internal/intern/select.h +raddrinfo.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +raddrinfo.o: $(hdrdir)/ruby/internal/intern/set.h +raddrinfo.o: $(hdrdir)/ruby/internal/intern/signal.h +raddrinfo.o: $(hdrdir)/ruby/internal/intern/sprintf.h +raddrinfo.o: $(hdrdir)/ruby/internal/intern/string.h +raddrinfo.o: $(hdrdir)/ruby/internal/intern/struct.h +raddrinfo.o: $(hdrdir)/ruby/internal/intern/thread.h +raddrinfo.o: $(hdrdir)/ruby/internal/intern/time.h +raddrinfo.o: $(hdrdir)/ruby/internal/intern/variable.h +raddrinfo.o: $(hdrdir)/ruby/internal/intern/vm.h +raddrinfo.o: $(hdrdir)/ruby/internal/interpreter.h +raddrinfo.o: $(hdrdir)/ruby/internal/iterator.h +raddrinfo.o: $(hdrdir)/ruby/internal/memory.h +raddrinfo.o: $(hdrdir)/ruby/internal/method.h +raddrinfo.o: $(hdrdir)/ruby/internal/module.h +raddrinfo.o: $(hdrdir)/ruby/internal/newobj.h +raddrinfo.o: $(hdrdir)/ruby/internal/scan_args.h +raddrinfo.o: $(hdrdir)/ruby/internal/special_consts.h +raddrinfo.o: $(hdrdir)/ruby/internal/static_assert.h +raddrinfo.o: $(hdrdir)/ruby/internal/stdalign.h +raddrinfo.o: $(hdrdir)/ruby/internal/stdbool.h +raddrinfo.o: $(hdrdir)/ruby/internal/stdckdint.h +raddrinfo.o: $(hdrdir)/ruby/internal/symbol.h +raddrinfo.o: $(hdrdir)/ruby/internal/value.h +raddrinfo.o: $(hdrdir)/ruby/internal/value_type.h +raddrinfo.o: $(hdrdir)/ruby/internal/variable.h +raddrinfo.o: $(hdrdir)/ruby/internal/warning_push.h +raddrinfo.o: $(hdrdir)/ruby/internal/xmalloc.h +raddrinfo.o: $(hdrdir)/ruby/io.h +raddrinfo.o: $(hdrdir)/ruby/missing.h +raddrinfo.o: $(hdrdir)/ruby/onigmo.h +raddrinfo.o: $(hdrdir)/ruby/oniguruma.h +raddrinfo.o: $(hdrdir)/ruby/ruby.h +raddrinfo.o: $(hdrdir)/ruby/st.h +raddrinfo.o: $(hdrdir)/ruby/subst.h +raddrinfo.o: $(hdrdir)/ruby/thread.h +raddrinfo.o: $(hdrdir)/ruby/thread_native.h +raddrinfo.o: $(hdrdir)/ruby/util.h +raddrinfo.o: $(hdrdir)/ruby/version.h +raddrinfo.o: $(top_srcdir)/ccan/check_type/check_type.h +raddrinfo.o: $(top_srcdir)/ccan/container_of/container_of.h +raddrinfo.o: $(top_srcdir)/ccan/list/list.h +raddrinfo.o: $(top_srcdir)/ccan/str/str.h +raddrinfo.o: $(top_srcdir)/encindex.h +raddrinfo.o: $(top_srcdir)/id_table.h +raddrinfo.o: $(top_srcdir)/internal.h +raddrinfo.o: $(top_srcdir)/internal/array.h +raddrinfo.o: $(top_srcdir)/internal/basic_operators.h +raddrinfo.o: $(top_srcdir)/internal/box.h +raddrinfo.o: $(top_srcdir)/internal/compilers.h +raddrinfo.o: $(top_srcdir)/internal/error.h +raddrinfo.o: $(top_srcdir)/internal/gc.h +raddrinfo.o: $(top_srcdir)/internal/imemo.h +raddrinfo.o: $(top_srcdir)/internal/io.h +raddrinfo.o: $(top_srcdir)/internal/sanitizers.h +raddrinfo.o: $(top_srcdir)/internal/serial.h +raddrinfo.o: $(top_srcdir)/internal/set_table.h +raddrinfo.o: $(top_srcdir)/internal/static_assert.h +raddrinfo.o: $(top_srcdir)/internal/string.h +raddrinfo.o: $(top_srcdir)/internal/thread.h +raddrinfo.o: $(top_srcdir)/internal/vm.h +raddrinfo.o: $(top_srcdir)/internal/warnings.h +raddrinfo.o: $(top_srcdir)/method.h +raddrinfo.o: $(top_srcdir)/node.h +raddrinfo.o: $(top_srcdir)/ruby_assert.h +raddrinfo.o: $(top_srcdir)/ruby_atomic.h +raddrinfo.o: $(top_srcdir)/rubyparser.h +raddrinfo.o: $(top_srcdir)/shape.h +raddrinfo.o: $(top_srcdir)/thread_pthread.h +raddrinfo.o: $(top_srcdir)/vm_core.h +raddrinfo.o: $(top_srcdir)/vm_opts.h +raddrinfo.o: constdefs.h +raddrinfo.o: raddrinfo.c +raddrinfo.o: rubysocket.h +raddrinfo.o: sockport.h +raddrinfo.o: {$(VPATH)}id.h +socket.o: $(RUBY_EXTCONF_H) +socket.o: $(arch_hdrdir)/ruby/config.h +socket.o: $(hdrdir)/ruby/assert.h +socket.o: $(hdrdir)/ruby/atomic.h +socket.o: $(hdrdir)/ruby/backward.h +socket.o: $(hdrdir)/ruby/backward/2/assume.h +socket.o: $(hdrdir)/ruby/backward/2/attributes.h +socket.o: $(hdrdir)/ruby/backward/2/bool.h +socket.o: $(hdrdir)/ruby/backward/2/gcc_version_since.h +socket.o: $(hdrdir)/ruby/backward/2/inttypes.h +socket.o: $(hdrdir)/ruby/backward/2/limits.h +socket.o: $(hdrdir)/ruby/backward/2/long_long.h +socket.o: $(hdrdir)/ruby/backward/2/stdalign.h +socket.o: $(hdrdir)/ruby/backward/2/stdarg.h +socket.o: $(hdrdir)/ruby/defines.h +socket.o: $(hdrdir)/ruby/encoding.h +socket.o: $(hdrdir)/ruby/fiber/scheduler.h +socket.o: $(hdrdir)/ruby/intern.h +socket.o: $(hdrdir)/ruby/internal/abi.h +socket.o: $(hdrdir)/ruby/internal/anyargs.h +socket.o: $(hdrdir)/ruby/internal/arithmetic.h +socket.o: $(hdrdir)/ruby/internal/arithmetic/char.h +socket.o: $(hdrdir)/ruby/internal/arithmetic/double.h +socket.o: $(hdrdir)/ruby/internal/arithmetic/fixnum.h +socket.o: $(hdrdir)/ruby/internal/arithmetic/gid_t.h +socket.o: $(hdrdir)/ruby/internal/arithmetic/int.h +socket.o: $(hdrdir)/ruby/internal/arithmetic/intptr_t.h +socket.o: $(hdrdir)/ruby/internal/arithmetic/long.h +socket.o: $(hdrdir)/ruby/internal/arithmetic/long_long.h +socket.o: $(hdrdir)/ruby/internal/arithmetic/mode_t.h +socket.o: $(hdrdir)/ruby/internal/arithmetic/off_t.h +socket.o: $(hdrdir)/ruby/internal/arithmetic/pid_t.h +socket.o: $(hdrdir)/ruby/internal/arithmetic/short.h +socket.o: $(hdrdir)/ruby/internal/arithmetic/size_t.h +socket.o: $(hdrdir)/ruby/internal/arithmetic/st_data_t.h +socket.o: $(hdrdir)/ruby/internal/arithmetic/uid_t.h +socket.o: $(hdrdir)/ruby/internal/assume.h +socket.o: $(hdrdir)/ruby/internal/attr/alloc_size.h +socket.o: $(hdrdir)/ruby/internal/attr/artificial.h +socket.o: $(hdrdir)/ruby/internal/attr/cold.h +socket.o: $(hdrdir)/ruby/internal/attr/const.h +socket.o: $(hdrdir)/ruby/internal/attr/constexpr.h +socket.o: $(hdrdir)/ruby/internal/attr/deprecated.h +socket.o: $(hdrdir)/ruby/internal/attr/diagnose_if.h +socket.o: $(hdrdir)/ruby/internal/attr/enum_extensibility.h +socket.o: $(hdrdir)/ruby/internal/attr/error.h +socket.o: $(hdrdir)/ruby/internal/attr/flag_enum.h +socket.o: $(hdrdir)/ruby/internal/attr/forceinline.h +socket.o: $(hdrdir)/ruby/internal/attr/format.h +socket.o: $(hdrdir)/ruby/internal/attr/maybe_unused.h +socket.o: $(hdrdir)/ruby/internal/attr/noalias.h +socket.o: $(hdrdir)/ruby/internal/attr/nodiscard.h +socket.o: $(hdrdir)/ruby/internal/attr/noexcept.h +socket.o: $(hdrdir)/ruby/internal/attr/noinline.h +socket.o: $(hdrdir)/ruby/internal/attr/nonnull.h +socket.o: $(hdrdir)/ruby/internal/attr/noreturn.h +socket.o: $(hdrdir)/ruby/internal/attr/packed_struct.h +socket.o: $(hdrdir)/ruby/internal/attr/pure.h +socket.o: $(hdrdir)/ruby/internal/attr/restrict.h +socket.o: $(hdrdir)/ruby/internal/attr/returns_nonnull.h +socket.o: $(hdrdir)/ruby/internal/attr/warning.h +socket.o: $(hdrdir)/ruby/internal/attr/weakref.h +socket.o: $(hdrdir)/ruby/internal/cast.h +socket.o: $(hdrdir)/ruby/internal/compiler_is.h +socket.o: $(hdrdir)/ruby/internal/compiler_is/apple.h +socket.o: $(hdrdir)/ruby/internal/compiler_is/clang.h +socket.o: $(hdrdir)/ruby/internal/compiler_is/gcc.h +socket.o: $(hdrdir)/ruby/internal/compiler_is/intel.h +socket.o: $(hdrdir)/ruby/internal/compiler_is/msvc.h +socket.o: $(hdrdir)/ruby/internal/compiler_is/sunpro.h +socket.o: $(hdrdir)/ruby/internal/compiler_since.h +socket.o: $(hdrdir)/ruby/internal/config.h +socket.o: $(hdrdir)/ruby/internal/constant_p.h +socket.o: $(hdrdir)/ruby/internal/core.h +socket.o: $(hdrdir)/ruby/internal/core/rarray.h +socket.o: $(hdrdir)/ruby/internal/core/rbasic.h +socket.o: $(hdrdir)/ruby/internal/core/rbignum.h +socket.o: $(hdrdir)/ruby/internal/core/rclass.h +socket.o: $(hdrdir)/ruby/internal/core/rdata.h +socket.o: $(hdrdir)/ruby/internal/core/rfile.h +socket.o: $(hdrdir)/ruby/internal/core/rhash.h +socket.o: $(hdrdir)/ruby/internal/core/robject.h +socket.o: $(hdrdir)/ruby/internal/core/rregexp.h +socket.o: $(hdrdir)/ruby/internal/core/rstring.h +socket.o: $(hdrdir)/ruby/internal/core/rstruct.h +socket.o: $(hdrdir)/ruby/internal/core/rtypeddata.h +socket.o: $(hdrdir)/ruby/internal/ctype.h +socket.o: $(hdrdir)/ruby/internal/dllexport.h +socket.o: $(hdrdir)/ruby/internal/dosish.h +socket.o: $(hdrdir)/ruby/internal/encoding/coderange.h +socket.o: $(hdrdir)/ruby/internal/encoding/ctype.h +socket.o: $(hdrdir)/ruby/internal/encoding/encoding.h +socket.o: $(hdrdir)/ruby/internal/encoding/pathname.h +socket.o: $(hdrdir)/ruby/internal/encoding/re.h +socket.o: $(hdrdir)/ruby/internal/encoding/sprintf.h +socket.o: $(hdrdir)/ruby/internal/encoding/string.h +socket.o: $(hdrdir)/ruby/internal/encoding/symbol.h +socket.o: $(hdrdir)/ruby/internal/encoding/transcode.h +socket.o: $(hdrdir)/ruby/internal/error.h +socket.o: $(hdrdir)/ruby/internal/eval.h +socket.o: $(hdrdir)/ruby/internal/event.h +socket.o: $(hdrdir)/ruby/internal/fl_type.h +socket.o: $(hdrdir)/ruby/internal/gc.h +socket.o: $(hdrdir)/ruby/internal/glob.h +socket.o: $(hdrdir)/ruby/internal/globals.h +socket.o: $(hdrdir)/ruby/internal/has/attribute.h +socket.o: $(hdrdir)/ruby/internal/has/builtin.h +socket.o: $(hdrdir)/ruby/internal/has/c_attribute.h +socket.o: $(hdrdir)/ruby/internal/has/cpp_attribute.h +socket.o: $(hdrdir)/ruby/internal/has/declspec_attribute.h +socket.o: $(hdrdir)/ruby/internal/has/extension.h +socket.o: $(hdrdir)/ruby/internal/has/feature.h +socket.o: $(hdrdir)/ruby/internal/has/warning.h +socket.o: $(hdrdir)/ruby/internal/intern/array.h +socket.o: $(hdrdir)/ruby/internal/intern/bignum.h +socket.o: $(hdrdir)/ruby/internal/intern/class.h +socket.o: $(hdrdir)/ruby/internal/intern/compar.h +socket.o: $(hdrdir)/ruby/internal/intern/complex.h +socket.o: $(hdrdir)/ruby/internal/intern/cont.h +socket.o: $(hdrdir)/ruby/internal/intern/dir.h +socket.o: $(hdrdir)/ruby/internal/intern/enum.h +socket.o: $(hdrdir)/ruby/internal/intern/enumerator.h +socket.o: $(hdrdir)/ruby/internal/intern/error.h +socket.o: $(hdrdir)/ruby/internal/intern/eval.h +socket.o: $(hdrdir)/ruby/internal/intern/file.h +socket.o: $(hdrdir)/ruby/internal/intern/hash.h +socket.o: $(hdrdir)/ruby/internal/intern/io.h +socket.o: $(hdrdir)/ruby/internal/intern/load.h +socket.o: $(hdrdir)/ruby/internal/intern/marshal.h +socket.o: $(hdrdir)/ruby/internal/intern/numeric.h +socket.o: $(hdrdir)/ruby/internal/intern/object.h +socket.o: $(hdrdir)/ruby/internal/intern/parse.h +socket.o: $(hdrdir)/ruby/internal/intern/proc.h +socket.o: $(hdrdir)/ruby/internal/intern/process.h +socket.o: $(hdrdir)/ruby/internal/intern/random.h +socket.o: $(hdrdir)/ruby/internal/intern/range.h +socket.o: $(hdrdir)/ruby/internal/intern/rational.h +socket.o: $(hdrdir)/ruby/internal/intern/re.h +socket.o: $(hdrdir)/ruby/internal/intern/ruby.h +socket.o: $(hdrdir)/ruby/internal/intern/select.h +socket.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +socket.o: $(hdrdir)/ruby/internal/intern/set.h +socket.o: $(hdrdir)/ruby/internal/intern/signal.h +socket.o: $(hdrdir)/ruby/internal/intern/sprintf.h +socket.o: $(hdrdir)/ruby/internal/intern/string.h +socket.o: $(hdrdir)/ruby/internal/intern/struct.h +socket.o: $(hdrdir)/ruby/internal/intern/thread.h +socket.o: $(hdrdir)/ruby/internal/intern/time.h +socket.o: $(hdrdir)/ruby/internal/intern/variable.h +socket.o: $(hdrdir)/ruby/internal/intern/vm.h +socket.o: $(hdrdir)/ruby/internal/interpreter.h +socket.o: $(hdrdir)/ruby/internal/iterator.h +socket.o: $(hdrdir)/ruby/internal/memory.h +socket.o: $(hdrdir)/ruby/internal/method.h +socket.o: $(hdrdir)/ruby/internal/module.h +socket.o: $(hdrdir)/ruby/internal/newobj.h +socket.o: $(hdrdir)/ruby/internal/scan_args.h +socket.o: $(hdrdir)/ruby/internal/special_consts.h +socket.o: $(hdrdir)/ruby/internal/static_assert.h +socket.o: $(hdrdir)/ruby/internal/stdalign.h +socket.o: $(hdrdir)/ruby/internal/stdbool.h +socket.o: $(hdrdir)/ruby/internal/stdckdint.h +socket.o: $(hdrdir)/ruby/internal/symbol.h +socket.o: $(hdrdir)/ruby/internal/value.h +socket.o: $(hdrdir)/ruby/internal/value_type.h +socket.o: $(hdrdir)/ruby/internal/variable.h +socket.o: $(hdrdir)/ruby/internal/warning_push.h +socket.o: $(hdrdir)/ruby/internal/xmalloc.h +socket.o: $(hdrdir)/ruby/io.h +socket.o: $(hdrdir)/ruby/missing.h +socket.o: $(hdrdir)/ruby/onigmo.h +socket.o: $(hdrdir)/ruby/oniguruma.h +socket.o: $(hdrdir)/ruby/ruby.h +socket.o: $(hdrdir)/ruby/st.h +socket.o: $(hdrdir)/ruby/subst.h +socket.o: $(hdrdir)/ruby/thread.h +socket.o: $(hdrdir)/ruby/thread_native.h +socket.o: $(hdrdir)/ruby/util.h +socket.o: $(hdrdir)/ruby/version.h +socket.o: $(top_srcdir)/ccan/check_type/check_type.h +socket.o: $(top_srcdir)/ccan/container_of/container_of.h +socket.o: $(top_srcdir)/ccan/list/list.h +socket.o: $(top_srcdir)/ccan/str/str.h +socket.o: $(top_srcdir)/encindex.h +socket.o: $(top_srcdir)/id_table.h +socket.o: $(top_srcdir)/internal.h +socket.o: $(top_srcdir)/internal/array.h +socket.o: $(top_srcdir)/internal/basic_operators.h +socket.o: $(top_srcdir)/internal/box.h +socket.o: $(top_srcdir)/internal/compilers.h +socket.o: $(top_srcdir)/internal/error.h +socket.o: $(top_srcdir)/internal/gc.h +socket.o: $(top_srcdir)/internal/imemo.h +socket.o: $(top_srcdir)/internal/io.h +socket.o: $(top_srcdir)/internal/sanitizers.h +socket.o: $(top_srcdir)/internal/serial.h +socket.o: $(top_srcdir)/internal/set_table.h +socket.o: $(top_srcdir)/internal/static_assert.h +socket.o: $(top_srcdir)/internal/string.h +socket.o: $(top_srcdir)/internal/thread.h +socket.o: $(top_srcdir)/internal/vm.h +socket.o: $(top_srcdir)/internal/warnings.h +socket.o: $(top_srcdir)/method.h +socket.o: $(top_srcdir)/node.h +socket.o: $(top_srcdir)/ruby_assert.h +socket.o: $(top_srcdir)/ruby_atomic.h +socket.o: $(top_srcdir)/rubyparser.h +socket.o: $(top_srcdir)/shape.h +socket.o: $(top_srcdir)/thread_pthread.h +socket.o: $(top_srcdir)/vm_core.h +socket.o: $(top_srcdir)/vm_opts.h +socket.o: constdefs.h +socket.o: rubysocket.h +socket.o: socket.c +socket.o: sockport.h +socket.o: {$(VPATH)}id.h +sockssocket.o: $(RUBY_EXTCONF_H) +sockssocket.o: $(arch_hdrdir)/ruby/config.h +sockssocket.o: $(hdrdir)/ruby/assert.h +sockssocket.o: $(hdrdir)/ruby/atomic.h +sockssocket.o: $(hdrdir)/ruby/backward.h +sockssocket.o: $(hdrdir)/ruby/backward/2/assume.h +sockssocket.o: $(hdrdir)/ruby/backward/2/attributes.h +sockssocket.o: $(hdrdir)/ruby/backward/2/bool.h +sockssocket.o: $(hdrdir)/ruby/backward/2/gcc_version_since.h +sockssocket.o: $(hdrdir)/ruby/backward/2/inttypes.h +sockssocket.o: $(hdrdir)/ruby/backward/2/limits.h +sockssocket.o: $(hdrdir)/ruby/backward/2/long_long.h +sockssocket.o: $(hdrdir)/ruby/backward/2/stdalign.h +sockssocket.o: $(hdrdir)/ruby/backward/2/stdarg.h +sockssocket.o: $(hdrdir)/ruby/defines.h +sockssocket.o: $(hdrdir)/ruby/encoding.h +sockssocket.o: $(hdrdir)/ruby/fiber/scheduler.h +sockssocket.o: $(hdrdir)/ruby/intern.h +sockssocket.o: $(hdrdir)/ruby/internal/abi.h +sockssocket.o: $(hdrdir)/ruby/internal/anyargs.h +sockssocket.o: $(hdrdir)/ruby/internal/arithmetic.h +sockssocket.o: $(hdrdir)/ruby/internal/arithmetic/char.h +sockssocket.o: $(hdrdir)/ruby/internal/arithmetic/double.h +sockssocket.o: $(hdrdir)/ruby/internal/arithmetic/fixnum.h +sockssocket.o: $(hdrdir)/ruby/internal/arithmetic/gid_t.h +sockssocket.o: $(hdrdir)/ruby/internal/arithmetic/int.h +sockssocket.o: $(hdrdir)/ruby/internal/arithmetic/intptr_t.h +sockssocket.o: $(hdrdir)/ruby/internal/arithmetic/long.h +sockssocket.o: $(hdrdir)/ruby/internal/arithmetic/long_long.h +sockssocket.o: $(hdrdir)/ruby/internal/arithmetic/mode_t.h +sockssocket.o: $(hdrdir)/ruby/internal/arithmetic/off_t.h +sockssocket.o: $(hdrdir)/ruby/internal/arithmetic/pid_t.h +sockssocket.o: $(hdrdir)/ruby/internal/arithmetic/short.h +sockssocket.o: $(hdrdir)/ruby/internal/arithmetic/size_t.h +sockssocket.o: $(hdrdir)/ruby/internal/arithmetic/st_data_t.h +sockssocket.o: $(hdrdir)/ruby/internal/arithmetic/uid_t.h +sockssocket.o: $(hdrdir)/ruby/internal/assume.h +sockssocket.o: $(hdrdir)/ruby/internal/attr/alloc_size.h +sockssocket.o: $(hdrdir)/ruby/internal/attr/artificial.h +sockssocket.o: $(hdrdir)/ruby/internal/attr/cold.h +sockssocket.o: $(hdrdir)/ruby/internal/attr/const.h +sockssocket.o: $(hdrdir)/ruby/internal/attr/constexpr.h +sockssocket.o: $(hdrdir)/ruby/internal/attr/deprecated.h +sockssocket.o: $(hdrdir)/ruby/internal/attr/diagnose_if.h +sockssocket.o: $(hdrdir)/ruby/internal/attr/enum_extensibility.h +sockssocket.o: $(hdrdir)/ruby/internal/attr/error.h +sockssocket.o: $(hdrdir)/ruby/internal/attr/flag_enum.h +sockssocket.o: $(hdrdir)/ruby/internal/attr/forceinline.h +sockssocket.o: $(hdrdir)/ruby/internal/attr/format.h +sockssocket.o: $(hdrdir)/ruby/internal/attr/maybe_unused.h +sockssocket.o: $(hdrdir)/ruby/internal/attr/noalias.h +sockssocket.o: $(hdrdir)/ruby/internal/attr/nodiscard.h +sockssocket.o: $(hdrdir)/ruby/internal/attr/noexcept.h +sockssocket.o: $(hdrdir)/ruby/internal/attr/noinline.h +sockssocket.o: $(hdrdir)/ruby/internal/attr/nonnull.h +sockssocket.o: $(hdrdir)/ruby/internal/attr/noreturn.h +sockssocket.o: $(hdrdir)/ruby/internal/attr/packed_struct.h +sockssocket.o: $(hdrdir)/ruby/internal/attr/pure.h +sockssocket.o: $(hdrdir)/ruby/internal/attr/restrict.h +sockssocket.o: $(hdrdir)/ruby/internal/attr/returns_nonnull.h +sockssocket.o: $(hdrdir)/ruby/internal/attr/warning.h +sockssocket.o: $(hdrdir)/ruby/internal/attr/weakref.h +sockssocket.o: $(hdrdir)/ruby/internal/cast.h +sockssocket.o: $(hdrdir)/ruby/internal/compiler_is.h +sockssocket.o: $(hdrdir)/ruby/internal/compiler_is/apple.h +sockssocket.o: $(hdrdir)/ruby/internal/compiler_is/clang.h +sockssocket.o: $(hdrdir)/ruby/internal/compiler_is/gcc.h +sockssocket.o: $(hdrdir)/ruby/internal/compiler_is/intel.h +sockssocket.o: $(hdrdir)/ruby/internal/compiler_is/msvc.h +sockssocket.o: $(hdrdir)/ruby/internal/compiler_is/sunpro.h +sockssocket.o: $(hdrdir)/ruby/internal/compiler_since.h +sockssocket.o: $(hdrdir)/ruby/internal/config.h +sockssocket.o: $(hdrdir)/ruby/internal/constant_p.h +sockssocket.o: $(hdrdir)/ruby/internal/core.h +sockssocket.o: $(hdrdir)/ruby/internal/core/rarray.h +sockssocket.o: $(hdrdir)/ruby/internal/core/rbasic.h +sockssocket.o: $(hdrdir)/ruby/internal/core/rbignum.h +sockssocket.o: $(hdrdir)/ruby/internal/core/rclass.h +sockssocket.o: $(hdrdir)/ruby/internal/core/rdata.h +sockssocket.o: $(hdrdir)/ruby/internal/core/rfile.h +sockssocket.o: $(hdrdir)/ruby/internal/core/rhash.h +sockssocket.o: $(hdrdir)/ruby/internal/core/robject.h +sockssocket.o: $(hdrdir)/ruby/internal/core/rregexp.h +sockssocket.o: $(hdrdir)/ruby/internal/core/rstring.h +sockssocket.o: $(hdrdir)/ruby/internal/core/rstruct.h +sockssocket.o: $(hdrdir)/ruby/internal/core/rtypeddata.h +sockssocket.o: $(hdrdir)/ruby/internal/ctype.h +sockssocket.o: $(hdrdir)/ruby/internal/dllexport.h +sockssocket.o: $(hdrdir)/ruby/internal/dosish.h +sockssocket.o: $(hdrdir)/ruby/internal/encoding/coderange.h +sockssocket.o: $(hdrdir)/ruby/internal/encoding/ctype.h +sockssocket.o: $(hdrdir)/ruby/internal/encoding/encoding.h +sockssocket.o: $(hdrdir)/ruby/internal/encoding/pathname.h +sockssocket.o: $(hdrdir)/ruby/internal/encoding/re.h +sockssocket.o: $(hdrdir)/ruby/internal/encoding/sprintf.h +sockssocket.o: $(hdrdir)/ruby/internal/encoding/string.h +sockssocket.o: $(hdrdir)/ruby/internal/encoding/symbol.h +sockssocket.o: $(hdrdir)/ruby/internal/encoding/transcode.h +sockssocket.o: $(hdrdir)/ruby/internal/error.h +sockssocket.o: $(hdrdir)/ruby/internal/eval.h +sockssocket.o: $(hdrdir)/ruby/internal/event.h +sockssocket.o: $(hdrdir)/ruby/internal/fl_type.h +sockssocket.o: $(hdrdir)/ruby/internal/gc.h +sockssocket.o: $(hdrdir)/ruby/internal/glob.h +sockssocket.o: $(hdrdir)/ruby/internal/globals.h +sockssocket.o: $(hdrdir)/ruby/internal/has/attribute.h +sockssocket.o: $(hdrdir)/ruby/internal/has/builtin.h +sockssocket.o: $(hdrdir)/ruby/internal/has/c_attribute.h +sockssocket.o: $(hdrdir)/ruby/internal/has/cpp_attribute.h +sockssocket.o: $(hdrdir)/ruby/internal/has/declspec_attribute.h +sockssocket.o: $(hdrdir)/ruby/internal/has/extension.h +sockssocket.o: $(hdrdir)/ruby/internal/has/feature.h +sockssocket.o: $(hdrdir)/ruby/internal/has/warning.h +sockssocket.o: $(hdrdir)/ruby/internal/intern/array.h +sockssocket.o: $(hdrdir)/ruby/internal/intern/bignum.h +sockssocket.o: $(hdrdir)/ruby/internal/intern/class.h +sockssocket.o: $(hdrdir)/ruby/internal/intern/compar.h +sockssocket.o: $(hdrdir)/ruby/internal/intern/complex.h +sockssocket.o: $(hdrdir)/ruby/internal/intern/cont.h +sockssocket.o: $(hdrdir)/ruby/internal/intern/dir.h +sockssocket.o: $(hdrdir)/ruby/internal/intern/enum.h +sockssocket.o: $(hdrdir)/ruby/internal/intern/enumerator.h +sockssocket.o: $(hdrdir)/ruby/internal/intern/error.h +sockssocket.o: $(hdrdir)/ruby/internal/intern/eval.h +sockssocket.o: $(hdrdir)/ruby/internal/intern/file.h +sockssocket.o: $(hdrdir)/ruby/internal/intern/hash.h +sockssocket.o: $(hdrdir)/ruby/internal/intern/io.h +sockssocket.o: $(hdrdir)/ruby/internal/intern/load.h +sockssocket.o: $(hdrdir)/ruby/internal/intern/marshal.h +sockssocket.o: $(hdrdir)/ruby/internal/intern/numeric.h +sockssocket.o: $(hdrdir)/ruby/internal/intern/object.h +sockssocket.o: $(hdrdir)/ruby/internal/intern/parse.h +sockssocket.o: $(hdrdir)/ruby/internal/intern/proc.h +sockssocket.o: $(hdrdir)/ruby/internal/intern/process.h +sockssocket.o: $(hdrdir)/ruby/internal/intern/random.h +sockssocket.o: $(hdrdir)/ruby/internal/intern/range.h +sockssocket.o: $(hdrdir)/ruby/internal/intern/rational.h +sockssocket.o: $(hdrdir)/ruby/internal/intern/re.h +sockssocket.o: $(hdrdir)/ruby/internal/intern/ruby.h +sockssocket.o: $(hdrdir)/ruby/internal/intern/select.h +sockssocket.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +sockssocket.o: $(hdrdir)/ruby/internal/intern/set.h +sockssocket.o: $(hdrdir)/ruby/internal/intern/signal.h +sockssocket.o: $(hdrdir)/ruby/internal/intern/sprintf.h +sockssocket.o: $(hdrdir)/ruby/internal/intern/string.h +sockssocket.o: $(hdrdir)/ruby/internal/intern/struct.h +sockssocket.o: $(hdrdir)/ruby/internal/intern/thread.h +sockssocket.o: $(hdrdir)/ruby/internal/intern/time.h +sockssocket.o: $(hdrdir)/ruby/internal/intern/variable.h +sockssocket.o: $(hdrdir)/ruby/internal/intern/vm.h +sockssocket.o: $(hdrdir)/ruby/internal/interpreter.h +sockssocket.o: $(hdrdir)/ruby/internal/iterator.h +sockssocket.o: $(hdrdir)/ruby/internal/memory.h +sockssocket.o: $(hdrdir)/ruby/internal/method.h +sockssocket.o: $(hdrdir)/ruby/internal/module.h +sockssocket.o: $(hdrdir)/ruby/internal/newobj.h +sockssocket.o: $(hdrdir)/ruby/internal/scan_args.h +sockssocket.o: $(hdrdir)/ruby/internal/special_consts.h +sockssocket.o: $(hdrdir)/ruby/internal/static_assert.h +sockssocket.o: $(hdrdir)/ruby/internal/stdalign.h +sockssocket.o: $(hdrdir)/ruby/internal/stdbool.h +sockssocket.o: $(hdrdir)/ruby/internal/stdckdint.h +sockssocket.o: $(hdrdir)/ruby/internal/symbol.h +sockssocket.o: $(hdrdir)/ruby/internal/value.h +sockssocket.o: $(hdrdir)/ruby/internal/value_type.h +sockssocket.o: $(hdrdir)/ruby/internal/variable.h +sockssocket.o: $(hdrdir)/ruby/internal/warning_push.h +sockssocket.o: $(hdrdir)/ruby/internal/xmalloc.h +sockssocket.o: $(hdrdir)/ruby/io.h +sockssocket.o: $(hdrdir)/ruby/missing.h +sockssocket.o: $(hdrdir)/ruby/onigmo.h +sockssocket.o: $(hdrdir)/ruby/oniguruma.h +sockssocket.o: $(hdrdir)/ruby/ruby.h +sockssocket.o: $(hdrdir)/ruby/st.h +sockssocket.o: $(hdrdir)/ruby/subst.h +sockssocket.o: $(hdrdir)/ruby/thread.h +sockssocket.o: $(hdrdir)/ruby/thread_native.h +sockssocket.o: $(hdrdir)/ruby/util.h +sockssocket.o: $(hdrdir)/ruby/version.h +sockssocket.o: $(top_srcdir)/ccan/check_type/check_type.h +sockssocket.o: $(top_srcdir)/ccan/container_of/container_of.h +sockssocket.o: $(top_srcdir)/ccan/list/list.h +sockssocket.o: $(top_srcdir)/ccan/str/str.h +sockssocket.o: $(top_srcdir)/encindex.h +sockssocket.o: $(top_srcdir)/id_table.h +sockssocket.o: $(top_srcdir)/internal.h +sockssocket.o: $(top_srcdir)/internal/array.h +sockssocket.o: $(top_srcdir)/internal/basic_operators.h +sockssocket.o: $(top_srcdir)/internal/box.h +sockssocket.o: $(top_srcdir)/internal/compilers.h +sockssocket.o: $(top_srcdir)/internal/error.h +sockssocket.o: $(top_srcdir)/internal/gc.h +sockssocket.o: $(top_srcdir)/internal/imemo.h +sockssocket.o: $(top_srcdir)/internal/io.h +sockssocket.o: $(top_srcdir)/internal/sanitizers.h +sockssocket.o: $(top_srcdir)/internal/serial.h +sockssocket.o: $(top_srcdir)/internal/set_table.h +sockssocket.o: $(top_srcdir)/internal/static_assert.h +sockssocket.o: $(top_srcdir)/internal/string.h +sockssocket.o: $(top_srcdir)/internal/thread.h +sockssocket.o: $(top_srcdir)/internal/vm.h +sockssocket.o: $(top_srcdir)/internal/warnings.h +sockssocket.o: $(top_srcdir)/method.h +sockssocket.o: $(top_srcdir)/node.h +sockssocket.o: $(top_srcdir)/ruby_assert.h +sockssocket.o: $(top_srcdir)/ruby_atomic.h +sockssocket.o: $(top_srcdir)/rubyparser.h +sockssocket.o: $(top_srcdir)/shape.h +sockssocket.o: $(top_srcdir)/thread_pthread.h +sockssocket.o: $(top_srcdir)/vm_core.h +sockssocket.o: $(top_srcdir)/vm_opts.h +sockssocket.o: constdefs.h +sockssocket.o: rubysocket.h +sockssocket.o: sockport.h +sockssocket.o: sockssocket.c +sockssocket.o: {$(VPATH)}id.h +tcpserver.o: $(RUBY_EXTCONF_H) +tcpserver.o: $(arch_hdrdir)/ruby/config.h +tcpserver.o: $(hdrdir)/ruby/assert.h +tcpserver.o: $(hdrdir)/ruby/atomic.h +tcpserver.o: $(hdrdir)/ruby/backward.h +tcpserver.o: $(hdrdir)/ruby/backward/2/assume.h +tcpserver.o: $(hdrdir)/ruby/backward/2/attributes.h +tcpserver.o: $(hdrdir)/ruby/backward/2/bool.h +tcpserver.o: $(hdrdir)/ruby/backward/2/gcc_version_since.h +tcpserver.o: $(hdrdir)/ruby/backward/2/inttypes.h +tcpserver.o: $(hdrdir)/ruby/backward/2/limits.h +tcpserver.o: $(hdrdir)/ruby/backward/2/long_long.h +tcpserver.o: $(hdrdir)/ruby/backward/2/stdalign.h +tcpserver.o: $(hdrdir)/ruby/backward/2/stdarg.h +tcpserver.o: $(hdrdir)/ruby/defines.h +tcpserver.o: $(hdrdir)/ruby/encoding.h +tcpserver.o: $(hdrdir)/ruby/fiber/scheduler.h +tcpserver.o: $(hdrdir)/ruby/intern.h +tcpserver.o: $(hdrdir)/ruby/internal/abi.h +tcpserver.o: $(hdrdir)/ruby/internal/anyargs.h +tcpserver.o: $(hdrdir)/ruby/internal/arithmetic.h +tcpserver.o: $(hdrdir)/ruby/internal/arithmetic/char.h +tcpserver.o: $(hdrdir)/ruby/internal/arithmetic/double.h +tcpserver.o: $(hdrdir)/ruby/internal/arithmetic/fixnum.h +tcpserver.o: $(hdrdir)/ruby/internal/arithmetic/gid_t.h +tcpserver.o: $(hdrdir)/ruby/internal/arithmetic/int.h +tcpserver.o: $(hdrdir)/ruby/internal/arithmetic/intptr_t.h +tcpserver.o: $(hdrdir)/ruby/internal/arithmetic/long.h +tcpserver.o: $(hdrdir)/ruby/internal/arithmetic/long_long.h +tcpserver.o: $(hdrdir)/ruby/internal/arithmetic/mode_t.h +tcpserver.o: $(hdrdir)/ruby/internal/arithmetic/off_t.h +tcpserver.o: $(hdrdir)/ruby/internal/arithmetic/pid_t.h +tcpserver.o: $(hdrdir)/ruby/internal/arithmetic/short.h +tcpserver.o: $(hdrdir)/ruby/internal/arithmetic/size_t.h +tcpserver.o: $(hdrdir)/ruby/internal/arithmetic/st_data_t.h +tcpserver.o: $(hdrdir)/ruby/internal/arithmetic/uid_t.h +tcpserver.o: $(hdrdir)/ruby/internal/assume.h +tcpserver.o: $(hdrdir)/ruby/internal/attr/alloc_size.h +tcpserver.o: $(hdrdir)/ruby/internal/attr/artificial.h +tcpserver.o: $(hdrdir)/ruby/internal/attr/cold.h +tcpserver.o: $(hdrdir)/ruby/internal/attr/const.h +tcpserver.o: $(hdrdir)/ruby/internal/attr/constexpr.h +tcpserver.o: $(hdrdir)/ruby/internal/attr/deprecated.h +tcpserver.o: $(hdrdir)/ruby/internal/attr/diagnose_if.h +tcpserver.o: $(hdrdir)/ruby/internal/attr/enum_extensibility.h +tcpserver.o: $(hdrdir)/ruby/internal/attr/error.h +tcpserver.o: $(hdrdir)/ruby/internal/attr/flag_enum.h +tcpserver.o: $(hdrdir)/ruby/internal/attr/forceinline.h +tcpserver.o: $(hdrdir)/ruby/internal/attr/format.h +tcpserver.o: $(hdrdir)/ruby/internal/attr/maybe_unused.h +tcpserver.o: $(hdrdir)/ruby/internal/attr/noalias.h +tcpserver.o: $(hdrdir)/ruby/internal/attr/nodiscard.h +tcpserver.o: $(hdrdir)/ruby/internal/attr/noexcept.h +tcpserver.o: $(hdrdir)/ruby/internal/attr/noinline.h +tcpserver.o: $(hdrdir)/ruby/internal/attr/nonnull.h +tcpserver.o: $(hdrdir)/ruby/internal/attr/noreturn.h +tcpserver.o: $(hdrdir)/ruby/internal/attr/packed_struct.h +tcpserver.o: $(hdrdir)/ruby/internal/attr/pure.h +tcpserver.o: $(hdrdir)/ruby/internal/attr/restrict.h +tcpserver.o: $(hdrdir)/ruby/internal/attr/returns_nonnull.h +tcpserver.o: $(hdrdir)/ruby/internal/attr/warning.h +tcpserver.o: $(hdrdir)/ruby/internal/attr/weakref.h +tcpserver.o: $(hdrdir)/ruby/internal/cast.h +tcpserver.o: $(hdrdir)/ruby/internal/compiler_is.h +tcpserver.o: $(hdrdir)/ruby/internal/compiler_is/apple.h +tcpserver.o: $(hdrdir)/ruby/internal/compiler_is/clang.h +tcpserver.o: $(hdrdir)/ruby/internal/compiler_is/gcc.h +tcpserver.o: $(hdrdir)/ruby/internal/compiler_is/intel.h +tcpserver.o: $(hdrdir)/ruby/internal/compiler_is/msvc.h +tcpserver.o: $(hdrdir)/ruby/internal/compiler_is/sunpro.h +tcpserver.o: $(hdrdir)/ruby/internal/compiler_since.h +tcpserver.o: $(hdrdir)/ruby/internal/config.h +tcpserver.o: $(hdrdir)/ruby/internal/constant_p.h +tcpserver.o: $(hdrdir)/ruby/internal/core.h +tcpserver.o: $(hdrdir)/ruby/internal/core/rarray.h +tcpserver.o: $(hdrdir)/ruby/internal/core/rbasic.h +tcpserver.o: $(hdrdir)/ruby/internal/core/rbignum.h +tcpserver.o: $(hdrdir)/ruby/internal/core/rclass.h +tcpserver.o: $(hdrdir)/ruby/internal/core/rdata.h +tcpserver.o: $(hdrdir)/ruby/internal/core/rfile.h +tcpserver.o: $(hdrdir)/ruby/internal/core/rhash.h +tcpserver.o: $(hdrdir)/ruby/internal/core/robject.h +tcpserver.o: $(hdrdir)/ruby/internal/core/rregexp.h +tcpserver.o: $(hdrdir)/ruby/internal/core/rstring.h +tcpserver.o: $(hdrdir)/ruby/internal/core/rstruct.h +tcpserver.o: $(hdrdir)/ruby/internal/core/rtypeddata.h +tcpserver.o: $(hdrdir)/ruby/internal/ctype.h +tcpserver.o: $(hdrdir)/ruby/internal/dllexport.h +tcpserver.o: $(hdrdir)/ruby/internal/dosish.h +tcpserver.o: $(hdrdir)/ruby/internal/encoding/coderange.h +tcpserver.o: $(hdrdir)/ruby/internal/encoding/ctype.h +tcpserver.o: $(hdrdir)/ruby/internal/encoding/encoding.h +tcpserver.o: $(hdrdir)/ruby/internal/encoding/pathname.h +tcpserver.o: $(hdrdir)/ruby/internal/encoding/re.h +tcpserver.o: $(hdrdir)/ruby/internal/encoding/sprintf.h +tcpserver.o: $(hdrdir)/ruby/internal/encoding/string.h +tcpserver.o: $(hdrdir)/ruby/internal/encoding/symbol.h +tcpserver.o: $(hdrdir)/ruby/internal/encoding/transcode.h +tcpserver.o: $(hdrdir)/ruby/internal/error.h +tcpserver.o: $(hdrdir)/ruby/internal/eval.h +tcpserver.o: $(hdrdir)/ruby/internal/event.h +tcpserver.o: $(hdrdir)/ruby/internal/fl_type.h +tcpserver.o: $(hdrdir)/ruby/internal/gc.h +tcpserver.o: $(hdrdir)/ruby/internal/glob.h +tcpserver.o: $(hdrdir)/ruby/internal/globals.h +tcpserver.o: $(hdrdir)/ruby/internal/has/attribute.h +tcpserver.o: $(hdrdir)/ruby/internal/has/builtin.h +tcpserver.o: $(hdrdir)/ruby/internal/has/c_attribute.h +tcpserver.o: $(hdrdir)/ruby/internal/has/cpp_attribute.h +tcpserver.o: $(hdrdir)/ruby/internal/has/declspec_attribute.h +tcpserver.o: $(hdrdir)/ruby/internal/has/extension.h +tcpserver.o: $(hdrdir)/ruby/internal/has/feature.h +tcpserver.o: $(hdrdir)/ruby/internal/has/warning.h +tcpserver.o: $(hdrdir)/ruby/internal/intern/array.h +tcpserver.o: $(hdrdir)/ruby/internal/intern/bignum.h +tcpserver.o: $(hdrdir)/ruby/internal/intern/class.h +tcpserver.o: $(hdrdir)/ruby/internal/intern/compar.h +tcpserver.o: $(hdrdir)/ruby/internal/intern/complex.h +tcpserver.o: $(hdrdir)/ruby/internal/intern/cont.h +tcpserver.o: $(hdrdir)/ruby/internal/intern/dir.h +tcpserver.o: $(hdrdir)/ruby/internal/intern/enum.h +tcpserver.o: $(hdrdir)/ruby/internal/intern/enumerator.h +tcpserver.o: $(hdrdir)/ruby/internal/intern/error.h +tcpserver.o: $(hdrdir)/ruby/internal/intern/eval.h +tcpserver.o: $(hdrdir)/ruby/internal/intern/file.h +tcpserver.o: $(hdrdir)/ruby/internal/intern/hash.h +tcpserver.o: $(hdrdir)/ruby/internal/intern/io.h +tcpserver.o: $(hdrdir)/ruby/internal/intern/load.h +tcpserver.o: $(hdrdir)/ruby/internal/intern/marshal.h +tcpserver.o: $(hdrdir)/ruby/internal/intern/numeric.h +tcpserver.o: $(hdrdir)/ruby/internal/intern/object.h +tcpserver.o: $(hdrdir)/ruby/internal/intern/parse.h +tcpserver.o: $(hdrdir)/ruby/internal/intern/proc.h +tcpserver.o: $(hdrdir)/ruby/internal/intern/process.h +tcpserver.o: $(hdrdir)/ruby/internal/intern/random.h +tcpserver.o: $(hdrdir)/ruby/internal/intern/range.h +tcpserver.o: $(hdrdir)/ruby/internal/intern/rational.h +tcpserver.o: $(hdrdir)/ruby/internal/intern/re.h +tcpserver.o: $(hdrdir)/ruby/internal/intern/ruby.h +tcpserver.o: $(hdrdir)/ruby/internal/intern/select.h +tcpserver.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +tcpserver.o: $(hdrdir)/ruby/internal/intern/set.h +tcpserver.o: $(hdrdir)/ruby/internal/intern/signal.h +tcpserver.o: $(hdrdir)/ruby/internal/intern/sprintf.h +tcpserver.o: $(hdrdir)/ruby/internal/intern/string.h +tcpserver.o: $(hdrdir)/ruby/internal/intern/struct.h +tcpserver.o: $(hdrdir)/ruby/internal/intern/thread.h +tcpserver.o: $(hdrdir)/ruby/internal/intern/time.h +tcpserver.o: $(hdrdir)/ruby/internal/intern/variable.h +tcpserver.o: $(hdrdir)/ruby/internal/intern/vm.h +tcpserver.o: $(hdrdir)/ruby/internal/interpreter.h +tcpserver.o: $(hdrdir)/ruby/internal/iterator.h +tcpserver.o: $(hdrdir)/ruby/internal/memory.h +tcpserver.o: $(hdrdir)/ruby/internal/method.h +tcpserver.o: $(hdrdir)/ruby/internal/module.h +tcpserver.o: $(hdrdir)/ruby/internal/newobj.h +tcpserver.o: $(hdrdir)/ruby/internal/scan_args.h +tcpserver.o: $(hdrdir)/ruby/internal/special_consts.h +tcpserver.o: $(hdrdir)/ruby/internal/static_assert.h +tcpserver.o: $(hdrdir)/ruby/internal/stdalign.h +tcpserver.o: $(hdrdir)/ruby/internal/stdbool.h +tcpserver.o: $(hdrdir)/ruby/internal/stdckdint.h +tcpserver.o: $(hdrdir)/ruby/internal/symbol.h +tcpserver.o: $(hdrdir)/ruby/internal/value.h +tcpserver.o: $(hdrdir)/ruby/internal/value_type.h +tcpserver.o: $(hdrdir)/ruby/internal/variable.h +tcpserver.o: $(hdrdir)/ruby/internal/warning_push.h +tcpserver.o: $(hdrdir)/ruby/internal/xmalloc.h +tcpserver.o: $(hdrdir)/ruby/io.h +tcpserver.o: $(hdrdir)/ruby/missing.h +tcpserver.o: $(hdrdir)/ruby/onigmo.h +tcpserver.o: $(hdrdir)/ruby/oniguruma.h +tcpserver.o: $(hdrdir)/ruby/ruby.h +tcpserver.o: $(hdrdir)/ruby/st.h +tcpserver.o: $(hdrdir)/ruby/subst.h +tcpserver.o: $(hdrdir)/ruby/thread.h +tcpserver.o: $(hdrdir)/ruby/thread_native.h +tcpserver.o: $(hdrdir)/ruby/util.h +tcpserver.o: $(hdrdir)/ruby/version.h +tcpserver.o: $(top_srcdir)/ccan/check_type/check_type.h +tcpserver.o: $(top_srcdir)/ccan/container_of/container_of.h +tcpserver.o: $(top_srcdir)/ccan/list/list.h +tcpserver.o: $(top_srcdir)/ccan/str/str.h +tcpserver.o: $(top_srcdir)/encindex.h +tcpserver.o: $(top_srcdir)/id_table.h +tcpserver.o: $(top_srcdir)/internal.h +tcpserver.o: $(top_srcdir)/internal/array.h +tcpserver.o: $(top_srcdir)/internal/basic_operators.h +tcpserver.o: $(top_srcdir)/internal/box.h +tcpserver.o: $(top_srcdir)/internal/compilers.h +tcpserver.o: $(top_srcdir)/internal/error.h +tcpserver.o: $(top_srcdir)/internal/gc.h +tcpserver.o: $(top_srcdir)/internal/imemo.h +tcpserver.o: $(top_srcdir)/internal/io.h +tcpserver.o: $(top_srcdir)/internal/sanitizers.h +tcpserver.o: $(top_srcdir)/internal/serial.h +tcpserver.o: $(top_srcdir)/internal/set_table.h +tcpserver.o: $(top_srcdir)/internal/static_assert.h +tcpserver.o: $(top_srcdir)/internal/string.h +tcpserver.o: $(top_srcdir)/internal/thread.h +tcpserver.o: $(top_srcdir)/internal/vm.h +tcpserver.o: $(top_srcdir)/internal/warnings.h +tcpserver.o: $(top_srcdir)/method.h +tcpserver.o: $(top_srcdir)/node.h +tcpserver.o: $(top_srcdir)/ruby_assert.h +tcpserver.o: $(top_srcdir)/ruby_atomic.h +tcpserver.o: $(top_srcdir)/rubyparser.h +tcpserver.o: $(top_srcdir)/shape.h +tcpserver.o: $(top_srcdir)/thread_pthread.h +tcpserver.o: $(top_srcdir)/vm_core.h +tcpserver.o: $(top_srcdir)/vm_opts.h +tcpserver.o: constdefs.h +tcpserver.o: rubysocket.h +tcpserver.o: sockport.h +tcpserver.o: tcpserver.c +tcpserver.o: {$(VPATH)}id.h +tcpsocket.o: $(RUBY_EXTCONF_H) +tcpsocket.o: $(arch_hdrdir)/ruby/config.h +tcpsocket.o: $(hdrdir)/ruby/assert.h +tcpsocket.o: $(hdrdir)/ruby/atomic.h +tcpsocket.o: $(hdrdir)/ruby/backward.h +tcpsocket.o: $(hdrdir)/ruby/backward/2/assume.h +tcpsocket.o: $(hdrdir)/ruby/backward/2/attributes.h +tcpsocket.o: $(hdrdir)/ruby/backward/2/bool.h +tcpsocket.o: $(hdrdir)/ruby/backward/2/gcc_version_since.h +tcpsocket.o: $(hdrdir)/ruby/backward/2/inttypes.h +tcpsocket.o: $(hdrdir)/ruby/backward/2/limits.h +tcpsocket.o: $(hdrdir)/ruby/backward/2/long_long.h +tcpsocket.o: $(hdrdir)/ruby/backward/2/stdalign.h +tcpsocket.o: $(hdrdir)/ruby/backward/2/stdarg.h +tcpsocket.o: $(hdrdir)/ruby/defines.h +tcpsocket.o: $(hdrdir)/ruby/encoding.h +tcpsocket.o: $(hdrdir)/ruby/fiber/scheduler.h +tcpsocket.o: $(hdrdir)/ruby/intern.h +tcpsocket.o: $(hdrdir)/ruby/internal/abi.h +tcpsocket.o: $(hdrdir)/ruby/internal/anyargs.h +tcpsocket.o: $(hdrdir)/ruby/internal/arithmetic.h +tcpsocket.o: $(hdrdir)/ruby/internal/arithmetic/char.h +tcpsocket.o: $(hdrdir)/ruby/internal/arithmetic/double.h +tcpsocket.o: $(hdrdir)/ruby/internal/arithmetic/fixnum.h +tcpsocket.o: $(hdrdir)/ruby/internal/arithmetic/gid_t.h +tcpsocket.o: $(hdrdir)/ruby/internal/arithmetic/int.h +tcpsocket.o: $(hdrdir)/ruby/internal/arithmetic/intptr_t.h +tcpsocket.o: $(hdrdir)/ruby/internal/arithmetic/long.h +tcpsocket.o: $(hdrdir)/ruby/internal/arithmetic/long_long.h +tcpsocket.o: $(hdrdir)/ruby/internal/arithmetic/mode_t.h +tcpsocket.o: $(hdrdir)/ruby/internal/arithmetic/off_t.h +tcpsocket.o: $(hdrdir)/ruby/internal/arithmetic/pid_t.h +tcpsocket.o: $(hdrdir)/ruby/internal/arithmetic/short.h +tcpsocket.o: $(hdrdir)/ruby/internal/arithmetic/size_t.h +tcpsocket.o: $(hdrdir)/ruby/internal/arithmetic/st_data_t.h +tcpsocket.o: $(hdrdir)/ruby/internal/arithmetic/uid_t.h +tcpsocket.o: $(hdrdir)/ruby/internal/assume.h +tcpsocket.o: $(hdrdir)/ruby/internal/attr/alloc_size.h +tcpsocket.o: $(hdrdir)/ruby/internal/attr/artificial.h +tcpsocket.o: $(hdrdir)/ruby/internal/attr/cold.h +tcpsocket.o: $(hdrdir)/ruby/internal/attr/const.h +tcpsocket.o: $(hdrdir)/ruby/internal/attr/constexpr.h +tcpsocket.o: $(hdrdir)/ruby/internal/attr/deprecated.h +tcpsocket.o: $(hdrdir)/ruby/internal/attr/diagnose_if.h +tcpsocket.o: $(hdrdir)/ruby/internal/attr/enum_extensibility.h +tcpsocket.o: $(hdrdir)/ruby/internal/attr/error.h +tcpsocket.o: $(hdrdir)/ruby/internal/attr/flag_enum.h +tcpsocket.o: $(hdrdir)/ruby/internal/attr/forceinline.h +tcpsocket.o: $(hdrdir)/ruby/internal/attr/format.h +tcpsocket.o: $(hdrdir)/ruby/internal/attr/maybe_unused.h +tcpsocket.o: $(hdrdir)/ruby/internal/attr/noalias.h +tcpsocket.o: $(hdrdir)/ruby/internal/attr/nodiscard.h +tcpsocket.o: $(hdrdir)/ruby/internal/attr/noexcept.h +tcpsocket.o: $(hdrdir)/ruby/internal/attr/noinline.h +tcpsocket.o: $(hdrdir)/ruby/internal/attr/nonnull.h +tcpsocket.o: $(hdrdir)/ruby/internal/attr/noreturn.h +tcpsocket.o: $(hdrdir)/ruby/internal/attr/packed_struct.h +tcpsocket.o: $(hdrdir)/ruby/internal/attr/pure.h +tcpsocket.o: $(hdrdir)/ruby/internal/attr/restrict.h +tcpsocket.o: $(hdrdir)/ruby/internal/attr/returns_nonnull.h +tcpsocket.o: $(hdrdir)/ruby/internal/attr/warning.h +tcpsocket.o: $(hdrdir)/ruby/internal/attr/weakref.h +tcpsocket.o: $(hdrdir)/ruby/internal/cast.h +tcpsocket.o: $(hdrdir)/ruby/internal/compiler_is.h +tcpsocket.o: $(hdrdir)/ruby/internal/compiler_is/apple.h +tcpsocket.o: $(hdrdir)/ruby/internal/compiler_is/clang.h +tcpsocket.o: $(hdrdir)/ruby/internal/compiler_is/gcc.h +tcpsocket.o: $(hdrdir)/ruby/internal/compiler_is/intel.h +tcpsocket.o: $(hdrdir)/ruby/internal/compiler_is/msvc.h +tcpsocket.o: $(hdrdir)/ruby/internal/compiler_is/sunpro.h +tcpsocket.o: $(hdrdir)/ruby/internal/compiler_since.h +tcpsocket.o: $(hdrdir)/ruby/internal/config.h +tcpsocket.o: $(hdrdir)/ruby/internal/constant_p.h +tcpsocket.o: $(hdrdir)/ruby/internal/core.h +tcpsocket.o: $(hdrdir)/ruby/internal/core/rarray.h +tcpsocket.o: $(hdrdir)/ruby/internal/core/rbasic.h +tcpsocket.o: $(hdrdir)/ruby/internal/core/rbignum.h +tcpsocket.o: $(hdrdir)/ruby/internal/core/rclass.h +tcpsocket.o: $(hdrdir)/ruby/internal/core/rdata.h +tcpsocket.o: $(hdrdir)/ruby/internal/core/rfile.h +tcpsocket.o: $(hdrdir)/ruby/internal/core/rhash.h +tcpsocket.o: $(hdrdir)/ruby/internal/core/robject.h +tcpsocket.o: $(hdrdir)/ruby/internal/core/rregexp.h +tcpsocket.o: $(hdrdir)/ruby/internal/core/rstring.h +tcpsocket.o: $(hdrdir)/ruby/internal/core/rstruct.h +tcpsocket.o: $(hdrdir)/ruby/internal/core/rtypeddata.h +tcpsocket.o: $(hdrdir)/ruby/internal/ctype.h +tcpsocket.o: $(hdrdir)/ruby/internal/dllexport.h +tcpsocket.o: $(hdrdir)/ruby/internal/dosish.h +tcpsocket.o: $(hdrdir)/ruby/internal/encoding/coderange.h +tcpsocket.o: $(hdrdir)/ruby/internal/encoding/ctype.h +tcpsocket.o: $(hdrdir)/ruby/internal/encoding/encoding.h +tcpsocket.o: $(hdrdir)/ruby/internal/encoding/pathname.h +tcpsocket.o: $(hdrdir)/ruby/internal/encoding/re.h +tcpsocket.o: $(hdrdir)/ruby/internal/encoding/sprintf.h +tcpsocket.o: $(hdrdir)/ruby/internal/encoding/string.h +tcpsocket.o: $(hdrdir)/ruby/internal/encoding/symbol.h +tcpsocket.o: $(hdrdir)/ruby/internal/encoding/transcode.h +tcpsocket.o: $(hdrdir)/ruby/internal/error.h +tcpsocket.o: $(hdrdir)/ruby/internal/eval.h +tcpsocket.o: $(hdrdir)/ruby/internal/event.h +tcpsocket.o: $(hdrdir)/ruby/internal/fl_type.h +tcpsocket.o: $(hdrdir)/ruby/internal/gc.h +tcpsocket.o: $(hdrdir)/ruby/internal/glob.h +tcpsocket.o: $(hdrdir)/ruby/internal/globals.h +tcpsocket.o: $(hdrdir)/ruby/internal/has/attribute.h +tcpsocket.o: $(hdrdir)/ruby/internal/has/builtin.h +tcpsocket.o: $(hdrdir)/ruby/internal/has/c_attribute.h +tcpsocket.o: $(hdrdir)/ruby/internal/has/cpp_attribute.h +tcpsocket.o: $(hdrdir)/ruby/internal/has/declspec_attribute.h +tcpsocket.o: $(hdrdir)/ruby/internal/has/extension.h +tcpsocket.o: $(hdrdir)/ruby/internal/has/feature.h +tcpsocket.o: $(hdrdir)/ruby/internal/has/warning.h +tcpsocket.o: $(hdrdir)/ruby/internal/intern/array.h +tcpsocket.o: $(hdrdir)/ruby/internal/intern/bignum.h +tcpsocket.o: $(hdrdir)/ruby/internal/intern/class.h +tcpsocket.o: $(hdrdir)/ruby/internal/intern/compar.h +tcpsocket.o: $(hdrdir)/ruby/internal/intern/complex.h +tcpsocket.o: $(hdrdir)/ruby/internal/intern/cont.h +tcpsocket.o: $(hdrdir)/ruby/internal/intern/dir.h +tcpsocket.o: $(hdrdir)/ruby/internal/intern/enum.h +tcpsocket.o: $(hdrdir)/ruby/internal/intern/enumerator.h +tcpsocket.o: $(hdrdir)/ruby/internal/intern/error.h +tcpsocket.o: $(hdrdir)/ruby/internal/intern/eval.h +tcpsocket.o: $(hdrdir)/ruby/internal/intern/file.h +tcpsocket.o: $(hdrdir)/ruby/internal/intern/hash.h +tcpsocket.o: $(hdrdir)/ruby/internal/intern/io.h +tcpsocket.o: $(hdrdir)/ruby/internal/intern/load.h +tcpsocket.o: $(hdrdir)/ruby/internal/intern/marshal.h +tcpsocket.o: $(hdrdir)/ruby/internal/intern/numeric.h +tcpsocket.o: $(hdrdir)/ruby/internal/intern/object.h +tcpsocket.o: $(hdrdir)/ruby/internal/intern/parse.h +tcpsocket.o: $(hdrdir)/ruby/internal/intern/proc.h +tcpsocket.o: $(hdrdir)/ruby/internal/intern/process.h +tcpsocket.o: $(hdrdir)/ruby/internal/intern/random.h +tcpsocket.o: $(hdrdir)/ruby/internal/intern/range.h +tcpsocket.o: $(hdrdir)/ruby/internal/intern/rational.h +tcpsocket.o: $(hdrdir)/ruby/internal/intern/re.h +tcpsocket.o: $(hdrdir)/ruby/internal/intern/ruby.h +tcpsocket.o: $(hdrdir)/ruby/internal/intern/select.h +tcpsocket.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +tcpsocket.o: $(hdrdir)/ruby/internal/intern/set.h +tcpsocket.o: $(hdrdir)/ruby/internal/intern/signal.h +tcpsocket.o: $(hdrdir)/ruby/internal/intern/sprintf.h +tcpsocket.o: $(hdrdir)/ruby/internal/intern/string.h +tcpsocket.o: $(hdrdir)/ruby/internal/intern/struct.h +tcpsocket.o: $(hdrdir)/ruby/internal/intern/thread.h +tcpsocket.o: $(hdrdir)/ruby/internal/intern/time.h +tcpsocket.o: $(hdrdir)/ruby/internal/intern/variable.h +tcpsocket.o: $(hdrdir)/ruby/internal/intern/vm.h +tcpsocket.o: $(hdrdir)/ruby/internal/interpreter.h +tcpsocket.o: $(hdrdir)/ruby/internal/iterator.h +tcpsocket.o: $(hdrdir)/ruby/internal/memory.h +tcpsocket.o: $(hdrdir)/ruby/internal/method.h +tcpsocket.o: $(hdrdir)/ruby/internal/module.h +tcpsocket.o: $(hdrdir)/ruby/internal/newobj.h +tcpsocket.o: $(hdrdir)/ruby/internal/scan_args.h +tcpsocket.o: $(hdrdir)/ruby/internal/special_consts.h +tcpsocket.o: $(hdrdir)/ruby/internal/static_assert.h +tcpsocket.o: $(hdrdir)/ruby/internal/stdalign.h +tcpsocket.o: $(hdrdir)/ruby/internal/stdbool.h +tcpsocket.o: $(hdrdir)/ruby/internal/stdckdint.h +tcpsocket.o: $(hdrdir)/ruby/internal/symbol.h +tcpsocket.o: $(hdrdir)/ruby/internal/value.h +tcpsocket.o: $(hdrdir)/ruby/internal/value_type.h +tcpsocket.o: $(hdrdir)/ruby/internal/variable.h +tcpsocket.o: $(hdrdir)/ruby/internal/warning_push.h +tcpsocket.o: $(hdrdir)/ruby/internal/xmalloc.h +tcpsocket.o: $(hdrdir)/ruby/io.h +tcpsocket.o: $(hdrdir)/ruby/missing.h +tcpsocket.o: $(hdrdir)/ruby/onigmo.h +tcpsocket.o: $(hdrdir)/ruby/oniguruma.h +tcpsocket.o: $(hdrdir)/ruby/ruby.h +tcpsocket.o: $(hdrdir)/ruby/st.h +tcpsocket.o: $(hdrdir)/ruby/subst.h +tcpsocket.o: $(hdrdir)/ruby/thread.h +tcpsocket.o: $(hdrdir)/ruby/thread_native.h +tcpsocket.o: $(hdrdir)/ruby/util.h +tcpsocket.o: $(hdrdir)/ruby/version.h +tcpsocket.o: $(top_srcdir)/ccan/check_type/check_type.h +tcpsocket.o: $(top_srcdir)/ccan/container_of/container_of.h +tcpsocket.o: $(top_srcdir)/ccan/list/list.h +tcpsocket.o: $(top_srcdir)/ccan/str/str.h +tcpsocket.o: $(top_srcdir)/encindex.h +tcpsocket.o: $(top_srcdir)/id_table.h +tcpsocket.o: $(top_srcdir)/internal.h +tcpsocket.o: $(top_srcdir)/internal/array.h +tcpsocket.o: $(top_srcdir)/internal/basic_operators.h +tcpsocket.o: $(top_srcdir)/internal/box.h +tcpsocket.o: $(top_srcdir)/internal/compilers.h +tcpsocket.o: $(top_srcdir)/internal/error.h +tcpsocket.o: $(top_srcdir)/internal/gc.h +tcpsocket.o: $(top_srcdir)/internal/imemo.h +tcpsocket.o: $(top_srcdir)/internal/io.h +tcpsocket.o: $(top_srcdir)/internal/sanitizers.h +tcpsocket.o: $(top_srcdir)/internal/serial.h +tcpsocket.o: $(top_srcdir)/internal/set_table.h +tcpsocket.o: $(top_srcdir)/internal/static_assert.h +tcpsocket.o: $(top_srcdir)/internal/string.h +tcpsocket.o: $(top_srcdir)/internal/thread.h +tcpsocket.o: $(top_srcdir)/internal/vm.h +tcpsocket.o: $(top_srcdir)/internal/warnings.h +tcpsocket.o: $(top_srcdir)/method.h +tcpsocket.o: $(top_srcdir)/node.h +tcpsocket.o: $(top_srcdir)/ruby_assert.h +tcpsocket.o: $(top_srcdir)/ruby_atomic.h +tcpsocket.o: $(top_srcdir)/rubyparser.h +tcpsocket.o: $(top_srcdir)/shape.h +tcpsocket.o: $(top_srcdir)/thread_pthread.h +tcpsocket.o: $(top_srcdir)/vm_core.h +tcpsocket.o: $(top_srcdir)/vm_opts.h +tcpsocket.o: constdefs.h +tcpsocket.o: rubysocket.h +tcpsocket.o: sockport.h +tcpsocket.o: tcpsocket.c +tcpsocket.o: {$(VPATH)}id.h +udpsocket.o: $(RUBY_EXTCONF_H) +udpsocket.o: $(arch_hdrdir)/ruby/config.h +udpsocket.o: $(hdrdir)/ruby/assert.h +udpsocket.o: $(hdrdir)/ruby/atomic.h +udpsocket.o: $(hdrdir)/ruby/backward.h +udpsocket.o: $(hdrdir)/ruby/backward/2/assume.h +udpsocket.o: $(hdrdir)/ruby/backward/2/attributes.h +udpsocket.o: $(hdrdir)/ruby/backward/2/bool.h +udpsocket.o: $(hdrdir)/ruby/backward/2/gcc_version_since.h +udpsocket.o: $(hdrdir)/ruby/backward/2/inttypes.h +udpsocket.o: $(hdrdir)/ruby/backward/2/limits.h +udpsocket.o: $(hdrdir)/ruby/backward/2/long_long.h +udpsocket.o: $(hdrdir)/ruby/backward/2/stdalign.h +udpsocket.o: $(hdrdir)/ruby/backward/2/stdarg.h +udpsocket.o: $(hdrdir)/ruby/defines.h +udpsocket.o: $(hdrdir)/ruby/encoding.h +udpsocket.o: $(hdrdir)/ruby/fiber/scheduler.h +udpsocket.o: $(hdrdir)/ruby/intern.h +udpsocket.o: $(hdrdir)/ruby/internal/abi.h +udpsocket.o: $(hdrdir)/ruby/internal/anyargs.h +udpsocket.o: $(hdrdir)/ruby/internal/arithmetic.h +udpsocket.o: $(hdrdir)/ruby/internal/arithmetic/char.h +udpsocket.o: $(hdrdir)/ruby/internal/arithmetic/double.h +udpsocket.o: $(hdrdir)/ruby/internal/arithmetic/fixnum.h +udpsocket.o: $(hdrdir)/ruby/internal/arithmetic/gid_t.h +udpsocket.o: $(hdrdir)/ruby/internal/arithmetic/int.h +udpsocket.o: $(hdrdir)/ruby/internal/arithmetic/intptr_t.h +udpsocket.o: $(hdrdir)/ruby/internal/arithmetic/long.h +udpsocket.o: $(hdrdir)/ruby/internal/arithmetic/long_long.h +udpsocket.o: $(hdrdir)/ruby/internal/arithmetic/mode_t.h +udpsocket.o: $(hdrdir)/ruby/internal/arithmetic/off_t.h +udpsocket.o: $(hdrdir)/ruby/internal/arithmetic/pid_t.h +udpsocket.o: $(hdrdir)/ruby/internal/arithmetic/short.h +udpsocket.o: $(hdrdir)/ruby/internal/arithmetic/size_t.h +udpsocket.o: $(hdrdir)/ruby/internal/arithmetic/st_data_t.h +udpsocket.o: $(hdrdir)/ruby/internal/arithmetic/uid_t.h +udpsocket.o: $(hdrdir)/ruby/internal/assume.h +udpsocket.o: $(hdrdir)/ruby/internal/attr/alloc_size.h +udpsocket.o: $(hdrdir)/ruby/internal/attr/artificial.h +udpsocket.o: $(hdrdir)/ruby/internal/attr/cold.h +udpsocket.o: $(hdrdir)/ruby/internal/attr/const.h +udpsocket.o: $(hdrdir)/ruby/internal/attr/constexpr.h +udpsocket.o: $(hdrdir)/ruby/internal/attr/deprecated.h +udpsocket.o: $(hdrdir)/ruby/internal/attr/diagnose_if.h +udpsocket.o: $(hdrdir)/ruby/internal/attr/enum_extensibility.h +udpsocket.o: $(hdrdir)/ruby/internal/attr/error.h +udpsocket.o: $(hdrdir)/ruby/internal/attr/flag_enum.h +udpsocket.o: $(hdrdir)/ruby/internal/attr/forceinline.h +udpsocket.o: $(hdrdir)/ruby/internal/attr/format.h +udpsocket.o: $(hdrdir)/ruby/internal/attr/maybe_unused.h +udpsocket.o: $(hdrdir)/ruby/internal/attr/noalias.h +udpsocket.o: $(hdrdir)/ruby/internal/attr/nodiscard.h +udpsocket.o: $(hdrdir)/ruby/internal/attr/noexcept.h +udpsocket.o: $(hdrdir)/ruby/internal/attr/noinline.h +udpsocket.o: $(hdrdir)/ruby/internal/attr/nonnull.h +udpsocket.o: $(hdrdir)/ruby/internal/attr/noreturn.h +udpsocket.o: $(hdrdir)/ruby/internal/attr/packed_struct.h +udpsocket.o: $(hdrdir)/ruby/internal/attr/pure.h +udpsocket.o: $(hdrdir)/ruby/internal/attr/restrict.h +udpsocket.o: $(hdrdir)/ruby/internal/attr/returns_nonnull.h +udpsocket.o: $(hdrdir)/ruby/internal/attr/warning.h +udpsocket.o: $(hdrdir)/ruby/internal/attr/weakref.h +udpsocket.o: $(hdrdir)/ruby/internal/cast.h +udpsocket.o: $(hdrdir)/ruby/internal/compiler_is.h +udpsocket.o: $(hdrdir)/ruby/internal/compiler_is/apple.h +udpsocket.o: $(hdrdir)/ruby/internal/compiler_is/clang.h +udpsocket.o: $(hdrdir)/ruby/internal/compiler_is/gcc.h +udpsocket.o: $(hdrdir)/ruby/internal/compiler_is/intel.h +udpsocket.o: $(hdrdir)/ruby/internal/compiler_is/msvc.h +udpsocket.o: $(hdrdir)/ruby/internal/compiler_is/sunpro.h +udpsocket.o: $(hdrdir)/ruby/internal/compiler_since.h +udpsocket.o: $(hdrdir)/ruby/internal/config.h +udpsocket.o: $(hdrdir)/ruby/internal/constant_p.h +udpsocket.o: $(hdrdir)/ruby/internal/core.h +udpsocket.o: $(hdrdir)/ruby/internal/core/rarray.h +udpsocket.o: $(hdrdir)/ruby/internal/core/rbasic.h +udpsocket.o: $(hdrdir)/ruby/internal/core/rbignum.h +udpsocket.o: $(hdrdir)/ruby/internal/core/rclass.h +udpsocket.o: $(hdrdir)/ruby/internal/core/rdata.h +udpsocket.o: $(hdrdir)/ruby/internal/core/rfile.h +udpsocket.o: $(hdrdir)/ruby/internal/core/rhash.h +udpsocket.o: $(hdrdir)/ruby/internal/core/robject.h +udpsocket.o: $(hdrdir)/ruby/internal/core/rregexp.h +udpsocket.o: $(hdrdir)/ruby/internal/core/rstring.h +udpsocket.o: $(hdrdir)/ruby/internal/core/rstruct.h +udpsocket.o: $(hdrdir)/ruby/internal/core/rtypeddata.h +udpsocket.o: $(hdrdir)/ruby/internal/ctype.h +udpsocket.o: $(hdrdir)/ruby/internal/dllexport.h +udpsocket.o: $(hdrdir)/ruby/internal/dosish.h +udpsocket.o: $(hdrdir)/ruby/internal/encoding/coderange.h +udpsocket.o: $(hdrdir)/ruby/internal/encoding/ctype.h +udpsocket.o: $(hdrdir)/ruby/internal/encoding/encoding.h +udpsocket.o: $(hdrdir)/ruby/internal/encoding/pathname.h +udpsocket.o: $(hdrdir)/ruby/internal/encoding/re.h +udpsocket.o: $(hdrdir)/ruby/internal/encoding/sprintf.h +udpsocket.o: $(hdrdir)/ruby/internal/encoding/string.h +udpsocket.o: $(hdrdir)/ruby/internal/encoding/symbol.h +udpsocket.o: $(hdrdir)/ruby/internal/encoding/transcode.h +udpsocket.o: $(hdrdir)/ruby/internal/error.h +udpsocket.o: $(hdrdir)/ruby/internal/eval.h +udpsocket.o: $(hdrdir)/ruby/internal/event.h +udpsocket.o: $(hdrdir)/ruby/internal/fl_type.h +udpsocket.o: $(hdrdir)/ruby/internal/gc.h +udpsocket.o: $(hdrdir)/ruby/internal/glob.h +udpsocket.o: $(hdrdir)/ruby/internal/globals.h +udpsocket.o: $(hdrdir)/ruby/internal/has/attribute.h +udpsocket.o: $(hdrdir)/ruby/internal/has/builtin.h +udpsocket.o: $(hdrdir)/ruby/internal/has/c_attribute.h +udpsocket.o: $(hdrdir)/ruby/internal/has/cpp_attribute.h +udpsocket.o: $(hdrdir)/ruby/internal/has/declspec_attribute.h +udpsocket.o: $(hdrdir)/ruby/internal/has/extension.h +udpsocket.o: $(hdrdir)/ruby/internal/has/feature.h +udpsocket.o: $(hdrdir)/ruby/internal/has/warning.h +udpsocket.o: $(hdrdir)/ruby/internal/intern/array.h +udpsocket.o: $(hdrdir)/ruby/internal/intern/bignum.h +udpsocket.o: $(hdrdir)/ruby/internal/intern/class.h +udpsocket.o: $(hdrdir)/ruby/internal/intern/compar.h +udpsocket.o: $(hdrdir)/ruby/internal/intern/complex.h +udpsocket.o: $(hdrdir)/ruby/internal/intern/cont.h +udpsocket.o: $(hdrdir)/ruby/internal/intern/dir.h +udpsocket.o: $(hdrdir)/ruby/internal/intern/enum.h +udpsocket.o: $(hdrdir)/ruby/internal/intern/enumerator.h +udpsocket.o: $(hdrdir)/ruby/internal/intern/error.h +udpsocket.o: $(hdrdir)/ruby/internal/intern/eval.h +udpsocket.o: $(hdrdir)/ruby/internal/intern/file.h +udpsocket.o: $(hdrdir)/ruby/internal/intern/hash.h +udpsocket.o: $(hdrdir)/ruby/internal/intern/io.h +udpsocket.o: $(hdrdir)/ruby/internal/intern/load.h +udpsocket.o: $(hdrdir)/ruby/internal/intern/marshal.h +udpsocket.o: $(hdrdir)/ruby/internal/intern/numeric.h +udpsocket.o: $(hdrdir)/ruby/internal/intern/object.h +udpsocket.o: $(hdrdir)/ruby/internal/intern/parse.h +udpsocket.o: $(hdrdir)/ruby/internal/intern/proc.h +udpsocket.o: $(hdrdir)/ruby/internal/intern/process.h +udpsocket.o: $(hdrdir)/ruby/internal/intern/random.h +udpsocket.o: $(hdrdir)/ruby/internal/intern/range.h +udpsocket.o: $(hdrdir)/ruby/internal/intern/rational.h +udpsocket.o: $(hdrdir)/ruby/internal/intern/re.h +udpsocket.o: $(hdrdir)/ruby/internal/intern/ruby.h +udpsocket.o: $(hdrdir)/ruby/internal/intern/select.h +udpsocket.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +udpsocket.o: $(hdrdir)/ruby/internal/intern/set.h +udpsocket.o: $(hdrdir)/ruby/internal/intern/signal.h +udpsocket.o: $(hdrdir)/ruby/internal/intern/sprintf.h +udpsocket.o: $(hdrdir)/ruby/internal/intern/string.h +udpsocket.o: $(hdrdir)/ruby/internal/intern/struct.h +udpsocket.o: $(hdrdir)/ruby/internal/intern/thread.h +udpsocket.o: $(hdrdir)/ruby/internal/intern/time.h +udpsocket.o: $(hdrdir)/ruby/internal/intern/variable.h +udpsocket.o: $(hdrdir)/ruby/internal/intern/vm.h +udpsocket.o: $(hdrdir)/ruby/internal/interpreter.h +udpsocket.o: $(hdrdir)/ruby/internal/iterator.h +udpsocket.o: $(hdrdir)/ruby/internal/memory.h +udpsocket.o: $(hdrdir)/ruby/internal/method.h +udpsocket.o: $(hdrdir)/ruby/internal/module.h +udpsocket.o: $(hdrdir)/ruby/internal/newobj.h +udpsocket.o: $(hdrdir)/ruby/internal/scan_args.h +udpsocket.o: $(hdrdir)/ruby/internal/special_consts.h +udpsocket.o: $(hdrdir)/ruby/internal/static_assert.h +udpsocket.o: $(hdrdir)/ruby/internal/stdalign.h +udpsocket.o: $(hdrdir)/ruby/internal/stdbool.h +udpsocket.o: $(hdrdir)/ruby/internal/stdckdint.h +udpsocket.o: $(hdrdir)/ruby/internal/symbol.h +udpsocket.o: $(hdrdir)/ruby/internal/value.h +udpsocket.o: $(hdrdir)/ruby/internal/value_type.h +udpsocket.o: $(hdrdir)/ruby/internal/variable.h +udpsocket.o: $(hdrdir)/ruby/internal/warning_push.h +udpsocket.o: $(hdrdir)/ruby/internal/xmalloc.h +udpsocket.o: $(hdrdir)/ruby/io.h +udpsocket.o: $(hdrdir)/ruby/missing.h +udpsocket.o: $(hdrdir)/ruby/onigmo.h +udpsocket.o: $(hdrdir)/ruby/oniguruma.h +udpsocket.o: $(hdrdir)/ruby/ruby.h +udpsocket.o: $(hdrdir)/ruby/st.h +udpsocket.o: $(hdrdir)/ruby/subst.h +udpsocket.o: $(hdrdir)/ruby/thread.h +udpsocket.o: $(hdrdir)/ruby/thread_native.h +udpsocket.o: $(hdrdir)/ruby/util.h +udpsocket.o: $(hdrdir)/ruby/version.h +udpsocket.o: $(top_srcdir)/ccan/check_type/check_type.h +udpsocket.o: $(top_srcdir)/ccan/container_of/container_of.h +udpsocket.o: $(top_srcdir)/ccan/list/list.h +udpsocket.o: $(top_srcdir)/ccan/str/str.h +udpsocket.o: $(top_srcdir)/encindex.h +udpsocket.o: $(top_srcdir)/id_table.h +udpsocket.o: $(top_srcdir)/internal.h +udpsocket.o: $(top_srcdir)/internal/array.h +udpsocket.o: $(top_srcdir)/internal/basic_operators.h +udpsocket.o: $(top_srcdir)/internal/box.h +udpsocket.o: $(top_srcdir)/internal/compilers.h +udpsocket.o: $(top_srcdir)/internal/error.h +udpsocket.o: $(top_srcdir)/internal/gc.h +udpsocket.o: $(top_srcdir)/internal/imemo.h +udpsocket.o: $(top_srcdir)/internal/io.h +udpsocket.o: $(top_srcdir)/internal/sanitizers.h +udpsocket.o: $(top_srcdir)/internal/serial.h +udpsocket.o: $(top_srcdir)/internal/set_table.h +udpsocket.o: $(top_srcdir)/internal/static_assert.h +udpsocket.o: $(top_srcdir)/internal/string.h +udpsocket.o: $(top_srcdir)/internal/thread.h +udpsocket.o: $(top_srcdir)/internal/vm.h +udpsocket.o: $(top_srcdir)/internal/warnings.h +udpsocket.o: $(top_srcdir)/method.h +udpsocket.o: $(top_srcdir)/node.h +udpsocket.o: $(top_srcdir)/ruby_assert.h +udpsocket.o: $(top_srcdir)/ruby_atomic.h +udpsocket.o: $(top_srcdir)/rubyparser.h +udpsocket.o: $(top_srcdir)/shape.h +udpsocket.o: $(top_srcdir)/thread_pthread.h +udpsocket.o: $(top_srcdir)/vm_core.h +udpsocket.o: $(top_srcdir)/vm_opts.h +udpsocket.o: constdefs.h +udpsocket.o: rubysocket.h +udpsocket.o: sockport.h +udpsocket.o: udpsocket.c +udpsocket.o: {$(VPATH)}id.h +unixserver.o: $(RUBY_EXTCONF_H) +unixserver.o: $(arch_hdrdir)/ruby/config.h +unixserver.o: $(hdrdir)/ruby/assert.h +unixserver.o: $(hdrdir)/ruby/atomic.h +unixserver.o: $(hdrdir)/ruby/backward.h +unixserver.o: $(hdrdir)/ruby/backward/2/assume.h +unixserver.o: $(hdrdir)/ruby/backward/2/attributes.h +unixserver.o: $(hdrdir)/ruby/backward/2/bool.h +unixserver.o: $(hdrdir)/ruby/backward/2/gcc_version_since.h +unixserver.o: $(hdrdir)/ruby/backward/2/inttypes.h +unixserver.o: $(hdrdir)/ruby/backward/2/limits.h +unixserver.o: $(hdrdir)/ruby/backward/2/long_long.h +unixserver.o: $(hdrdir)/ruby/backward/2/stdalign.h +unixserver.o: $(hdrdir)/ruby/backward/2/stdarg.h +unixserver.o: $(hdrdir)/ruby/defines.h +unixserver.o: $(hdrdir)/ruby/encoding.h +unixserver.o: $(hdrdir)/ruby/fiber/scheduler.h +unixserver.o: $(hdrdir)/ruby/intern.h +unixserver.o: $(hdrdir)/ruby/internal/abi.h +unixserver.o: $(hdrdir)/ruby/internal/anyargs.h +unixserver.o: $(hdrdir)/ruby/internal/arithmetic.h +unixserver.o: $(hdrdir)/ruby/internal/arithmetic/char.h +unixserver.o: $(hdrdir)/ruby/internal/arithmetic/double.h +unixserver.o: $(hdrdir)/ruby/internal/arithmetic/fixnum.h +unixserver.o: $(hdrdir)/ruby/internal/arithmetic/gid_t.h +unixserver.o: $(hdrdir)/ruby/internal/arithmetic/int.h +unixserver.o: $(hdrdir)/ruby/internal/arithmetic/intptr_t.h +unixserver.o: $(hdrdir)/ruby/internal/arithmetic/long.h +unixserver.o: $(hdrdir)/ruby/internal/arithmetic/long_long.h +unixserver.o: $(hdrdir)/ruby/internal/arithmetic/mode_t.h +unixserver.o: $(hdrdir)/ruby/internal/arithmetic/off_t.h +unixserver.o: $(hdrdir)/ruby/internal/arithmetic/pid_t.h +unixserver.o: $(hdrdir)/ruby/internal/arithmetic/short.h +unixserver.o: $(hdrdir)/ruby/internal/arithmetic/size_t.h +unixserver.o: $(hdrdir)/ruby/internal/arithmetic/st_data_t.h +unixserver.o: $(hdrdir)/ruby/internal/arithmetic/uid_t.h +unixserver.o: $(hdrdir)/ruby/internal/assume.h +unixserver.o: $(hdrdir)/ruby/internal/attr/alloc_size.h +unixserver.o: $(hdrdir)/ruby/internal/attr/artificial.h +unixserver.o: $(hdrdir)/ruby/internal/attr/cold.h +unixserver.o: $(hdrdir)/ruby/internal/attr/const.h +unixserver.o: $(hdrdir)/ruby/internal/attr/constexpr.h +unixserver.o: $(hdrdir)/ruby/internal/attr/deprecated.h +unixserver.o: $(hdrdir)/ruby/internal/attr/diagnose_if.h +unixserver.o: $(hdrdir)/ruby/internal/attr/enum_extensibility.h +unixserver.o: $(hdrdir)/ruby/internal/attr/error.h +unixserver.o: $(hdrdir)/ruby/internal/attr/flag_enum.h +unixserver.o: $(hdrdir)/ruby/internal/attr/forceinline.h +unixserver.o: $(hdrdir)/ruby/internal/attr/format.h +unixserver.o: $(hdrdir)/ruby/internal/attr/maybe_unused.h +unixserver.o: $(hdrdir)/ruby/internal/attr/noalias.h +unixserver.o: $(hdrdir)/ruby/internal/attr/nodiscard.h +unixserver.o: $(hdrdir)/ruby/internal/attr/noexcept.h +unixserver.o: $(hdrdir)/ruby/internal/attr/noinline.h +unixserver.o: $(hdrdir)/ruby/internal/attr/nonnull.h +unixserver.o: $(hdrdir)/ruby/internal/attr/noreturn.h +unixserver.o: $(hdrdir)/ruby/internal/attr/packed_struct.h +unixserver.o: $(hdrdir)/ruby/internal/attr/pure.h +unixserver.o: $(hdrdir)/ruby/internal/attr/restrict.h +unixserver.o: $(hdrdir)/ruby/internal/attr/returns_nonnull.h +unixserver.o: $(hdrdir)/ruby/internal/attr/warning.h +unixserver.o: $(hdrdir)/ruby/internal/attr/weakref.h +unixserver.o: $(hdrdir)/ruby/internal/cast.h +unixserver.o: $(hdrdir)/ruby/internal/compiler_is.h +unixserver.o: $(hdrdir)/ruby/internal/compiler_is/apple.h +unixserver.o: $(hdrdir)/ruby/internal/compiler_is/clang.h +unixserver.o: $(hdrdir)/ruby/internal/compiler_is/gcc.h +unixserver.o: $(hdrdir)/ruby/internal/compiler_is/intel.h +unixserver.o: $(hdrdir)/ruby/internal/compiler_is/msvc.h +unixserver.o: $(hdrdir)/ruby/internal/compiler_is/sunpro.h +unixserver.o: $(hdrdir)/ruby/internal/compiler_since.h +unixserver.o: $(hdrdir)/ruby/internal/config.h +unixserver.o: $(hdrdir)/ruby/internal/constant_p.h +unixserver.o: $(hdrdir)/ruby/internal/core.h +unixserver.o: $(hdrdir)/ruby/internal/core/rarray.h +unixserver.o: $(hdrdir)/ruby/internal/core/rbasic.h +unixserver.o: $(hdrdir)/ruby/internal/core/rbignum.h +unixserver.o: $(hdrdir)/ruby/internal/core/rclass.h +unixserver.o: $(hdrdir)/ruby/internal/core/rdata.h +unixserver.o: $(hdrdir)/ruby/internal/core/rfile.h +unixserver.o: $(hdrdir)/ruby/internal/core/rhash.h +unixserver.o: $(hdrdir)/ruby/internal/core/robject.h +unixserver.o: $(hdrdir)/ruby/internal/core/rregexp.h +unixserver.o: $(hdrdir)/ruby/internal/core/rstring.h +unixserver.o: $(hdrdir)/ruby/internal/core/rstruct.h +unixserver.o: $(hdrdir)/ruby/internal/core/rtypeddata.h +unixserver.o: $(hdrdir)/ruby/internal/ctype.h +unixserver.o: $(hdrdir)/ruby/internal/dllexport.h +unixserver.o: $(hdrdir)/ruby/internal/dosish.h +unixserver.o: $(hdrdir)/ruby/internal/encoding/coderange.h +unixserver.o: $(hdrdir)/ruby/internal/encoding/ctype.h +unixserver.o: $(hdrdir)/ruby/internal/encoding/encoding.h +unixserver.o: $(hdrdir)/ruby/internal/encoding/pathname.h +unixserver.o: $(hdrdir)/ruby/internal/encoding/re.h +unixserver.o: $(hdrdir)/ruby/internal/encoding/sprintf.h +unixserver.o: $(hdrdir)/ruby/internal/encoding/string.h +unixserver.o: $(hdrdir)/ruby/internal/encoding/symbol.h +unixserver.o: $(hdrdir)/ruby/internal/encoding/transcode.h +unixserver.o: $(hdrdir)/ruby/internal/error.h +unixserver.o: $(hdrdir)/ruby/internal/eval.h +unixserver.o: $(hdrdir)/ruby/internal/event.h +unixserver.o: $(hdrdir)/ruby/internal/fl_type.h +unixserver.o: $(hdrdir)/ruby/internal/gc.h +unixserver.o: $(hdrdir)/ruby/internal/glob.h +unixserver.o: $(hdrdir)/ruby/internal/globals.h +unixserver.o: $(hdrdir)/ruby/internal/has/attribute.h +unixserver.o: $(hdrdir)/ruby/internal/has/builtin.h +unixserver.o: $(hdrdir)/ruby/internal/has/c_attribute.h +unixserver.o: $(hdrdir)/ruby/internal/has/cpp_attribute.h +unixserver.o: $(hdrdir)/ruby/internal/has/declspec_attribute.h +unixserver.o: $(hdrdir)/ruby/internal/has/extension.h +unixserver.o: $(hdrdir)/ruby/internal/has/feature.h +unixserver.o: $(hdrdir)/ruby/internal/has/warning.h +unixserver.o: $(hdrdir)/ruby/internal/intern/array.h +unixserver.o: $(hdrdir)/ruby/internal/intern/bignum.h +unixserver.o: $(hdrdir)/ruby/internal/intern/class.h +unixserver.o: $(hdrdir)/ruby/internal/intern/compar.h +unixserver.o: $(hdrdir)/ruby/internal/intern/complex.h +unixserver.o: $(hdrdir)/ruby/internal/intern/cont.h +unixserver.o: $(hdrdir)/ruby/internal/intern/dir.h +unixserver.o: $(hdrdir)/ruby/internal/intern/enum.h +unixserver.o: $(hdrdir)/ruby/internal/intern/enumerator.h +unixserver.o: $(hdrdir)/ruby/internal/intern/error.h +unixserver.o: $(hdrdir)/ruby/internal/intern/eval.h +unixserver.o: $(hdrdir)/ruby/internal/intern/file.h +unixserver.o: $(hdrdir)/ruby/internal/intern/hash.h +unixserver.o: $(hdrdir)/ruby/internal/intern/io.h +unixserver.o: $(hdrdir)/ruby/internal/intern/load.h +unixserver.o: $(hdrdir)/ruby/internal/intern/marshal.h +unixserver.o: $(hdrdir)/ruby/internal/intern/numeric.h +unixserver.o: $(hdrdir)/ruby/internal/intern/object.h +unixserver.o: $(hdrdir)/ruby/internal/intern/parse.h +unixserver.o: $(hdrdir)/ruby/internal/intern/proc.h +unixserver.o: $(hdrdir)/ruby/internal/intern/process.h +unixserver.o: $(hdrdir)/ruby/internal/intern/random.h +unixserver.o: $(hdrdir)/ruby/internal/intern/range.h +unixserver.o: $(hdrdir)/ruby/internal/intern/rational.h +unixserver.o: $(hdrdir)/ruby/internal/intern/re.h +unixserver.o: $(hdrdir)/ruby/internal/intern/ruby.h +unixserver.o: $(hdrdir)/ruby/internal/intern/select.h +unixserver.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +unixserver.o: $(hdrdir)/ruby/internal/intern/set.h +unixserver.o: $(hdrdir)/ruby/internal/intern/signal.h +unixserver.o: $(hdrdir)/ruby/internal/intern/sprintf.h +unixserver.o: $(hdrdir)/ruby/internal/intern/string.h +unixserver.o: $(hdrdir)/ruby/internal/intern/struct.h +unixserver.o: $(hdrdir)/ruby/internal/intern/thread.h +unixserver.o: $(hdrdir)/ruby/internal/intern/time.h +unixserver.o: $(hdrdir)/ruby/internal/intern/variable.h +unixserver.o: $(hdrdir)/ruby/internal/intern/vm.h +unixserver.o: $(hdrdir)/ruby/internal/interpreter.h +unixserver.o: $(hdrdir)/ruby/internal/iterator.h +unixserver.o: $(hdrdir)/ruby/internal/memory.h +unixserver.o: $(hdrdir)/ruby/internal/method.h +unixserver.o: $(hdrdir)/ruby/internal/module.h +unixserver.o: $(hdrdir)/ruby/internal/newobj.h +unixserver.o: $(hdrdir)/ruby/internal/scan_args.h +unixserver.o: $(hdrdir)/ruby/internal/special_consts.h +unixserver.o: $(hdrdir)/ruby/internal/static_assert.h +unixserver.o: $(hdrdir)/ruby/internal/stdalign.h +unixserver.o: $(hdrdir)/ruby/internal/stdbool.h +unixserver.o: $(hdrdir)/ruby/internal/stdckdint.h +unixserver.o: $(hdrdir)/ruby/internal/symbol.h +unixserver.o: $(hdrdir)/ruby/internal/value.h +unixserver.o: $(hdrdir)/ruby/internal/value_type.h +unixserver.o: $(hdrdir)/ruby/internal/variable.h +unixserver.o: $(hdrdir)/ruby/internal/warning_push.h +unixserver.o: $(hdrdir)/ruby/internal/xmalloc.h +unixserver.o: $(hdrdir)/ruby/io.h +unixserver.o: $(hdrdir)/ruby/missing.h +unixserver.o: $(hdrdir)/ruby/onigmo.h +unixserver.o: $(hdrdir)/ruby/oniguruma.h +unixserver.o: $(hdrdir)/ruby/ruby.h +unixserver.o: $(hdrdir)/ruby/st.h +unixserver.o: $(hdrdir)/ruby/subst.h +unixserver.o: $(hdrdir)/ruby/thread.h +unixserver.o: $(hdrdir)/ruby/thread_native.h +unixserver.o: $(hdrdir)/ruby/util.h +unixserver.o: $(hdrdir)/ruby/version.h +unixserver.o: $(top_srcdir)/ccan/check_type/check_type.h +unixserver.o: $(top_srcdir)/ccan/container_of/container_of.h +unixserver.o: $(top_srcdir)/ccan/list/list.h +unixserver.o: $(top_srcdir)/ccan/str/str.h +unixserver.o: $(top_srcdir)/encindex.h +unixserver.o: $(top_srcdir)/id_table.h +unixserver.o: $(top_srcdir)/internal.h +unixserver.o: $(top_srcdir)/internal/array.h +unixserver.o: $(top_srcdir)/internal/basic_operators.h +unixserver.o: $(top_srcdir)/internal/box.h +unixserver.o: $(top_srcdir)/internal/compilers.h +unixserver.o: $(top_srcdir)/internal/error.h +unixserver.o: $(top_srcdir)/internal/gc.h +unixserver.o: $(top_srcdir)/internal/imemo.h +unixserver.o: $(top_srcdir)/internal/io.h +unixserver.o: $(top_srcdir)/internal/sanitizers.h +unixserver.o: $(top_srcdir)/internal/serial.h +unixserver.o: $(top_srcdir)/internal/set_table.h +unixserver.o: $(top_srcdir)/internal/static_assert.h +unixserver.o: $(top_srcdir)/internal/string.h +unixserver.o: $(top_srcdir)/internal/thread.h +unixserver.o: $(top_srcdir)/internal/vm.h +unixserver.o: $(top_srcdir)/internal/warnings.h +unixserver.o: $(top_srcdir)/method.h +unixserver.o: $(top_srcdir)/node.h +unixserver.o: $(top_srcdir)/ruby_assert.h +unixserver.o: $(top_srcdir)/ruby_atomic.h +unixserver.o: $(top_srcdir)/rubyparser.h +unixserver.o: $(top_srcdir)/shape.h +unixserver.o: $(top_srcdir)/thread_pthread.h +unixserver.o: $(top_srcdir)/vm_core.h +unixserver.o: $(top_srcdir)/vm_opts.h +unixserver.o: constdefs.h +unixserver.o: rubysocket.h +unixserver.o: sockport.h +unixserver.o: unixserver.c +unixserver.o: {$(VPATH)}id.h +unixsocket.o: $(RUBY_EXTCONF_H) +unixsocket.o: $(arch_hdrdir)/ruby/config.h +unixsocket.o: $(hdrdir)/ruby/assert.h +unixsocket.o: $(hdrdir)/ruby/atomic.h +unixsocket.o: $(hdrdir)/ruby/backward.h +unixsocket.o: $(hdrdir)/ruby/backward/2/assume.h +unixsocket.o: $(hdrdir)/ruby/backward/2/attributes.h +unixsocket.o: $(hdrdir)/ruby/backward/2/bool.h +unixsocket.o: $(hdrdir)/ruby/backward/2/gcc_version_since.h +unixsocket.o: $(hdrdir)/ruby/backward/2/inttypes.h +unixsocket.o: $(hdrdir)/ruby/backward/2/limits.h +unixsocket.o: $(hdrdir)/ruby/backward/2/long_long.h +unixsocket.o: $(hdrdir)/ruby/backward/2/stdalign.h +unixsocket.o: $(hdrdir)/ruby/backward/2/stdarg.h +unixsocket.o: $(hdrdir)/ruby/defines.h +unixsocket.o: $(hdrdir)/ruby/encoding.h +unixsocket.o: $(hdrdir)/ruby/fiber/scheduler.h +unixsocket.o: $(hdrdir)/ruby/intern.h +unixsocket.o: $(hdrdir)/ruby/internal/abi.h +unixsocket.o: $(hdrdir)/ruby/internal/anyargs.h +unixsocket.o: $(hdrdir)/ruby/internal/arithmetic.h +unixsocket.o: $(hdrdir)/ruby/internal/arithmetic/char.h +unixsocket.o: $(hdrdir)/ruby/internal/arithmetic/double.h +unixsocket.o: $(hdrdir)/ruby/internal/arithmetic/fixnum.h +unixsocket.o: $(hdrdir)/ruby/internal/arithmetic/gid_t.h +unixsocket.o: $(hdrdir)/ruby/internal/arithmetic/int.h +unixsocket.o: $(hdrdir)/ruby/internal/arithmetic/intptr_t.h +unixsocket.o: $(hdrdir)/ruby/internal/arithmetic/long.h +unixsocket.o: $(hdrdir)/ruby/internal/arithmetic/long_long.h +unixsocket.o: $(hdrdir)/ruby/internal/arithmetic/mode_t.h +unixsocket.o: $(hdrdir)/ruby/internal/arithmetic/off_t.h +unixsocket.o: $(hdrdir)/ruby/internal/arithmetic/pid_t.h +unixsocket.o: $(hdrdir)/ruby/internal/arithmetic/short.h +unixsocket.o: $(hdrdir)/ruby/internal/arithmetic/size_t.h +unixsocket.o: $(hdrdir)/ruby/internal/arithmetic/st_data_t.h +unixsocket.o: $(hdrdir)/ruby/internal/arithmetic/uid_t.h +unixsocket.o: $(hdrdir)/ruby/internal/assume.h +unixsocket.o: $(hdrdir)/ruby/internal/attr/alloc_size.h +unixsocket.o: $(hdrdir)/ruby/internal/attr/artificial.h +unixsocket.o: $(hdrdir)/ruby/internal/attr/cold.h +unixsocket.o: $(hdrdir)/ruby/internal/attr/const.h +unixsocket.o: $(hdrdir)/ruby/internal/attr/constexpr.h +unixsocket.o: $(hdrdir)/ruby/internal/attr/deprecated.h +unixsocket.o: $(hdrdir)/ruby/internal/attr/diagnose_if.h +unixsocket.o: $(hdrdir)/ruby/internal/attr/enum_extensibility.h +unixsocket.o: $(hdrdir)/ruby/internal/attr/error.h +unixsocket.o: $(hdrdir)/ruby/internal/attr/flag_enum.h +unixsocket.o: $(hdrdir)/ruby/internal/attr/forceinline.h +unixsocket.o: $(hdrdir)/ruby/internal/attr/format.h +unixsocket.o: $(hdrdir)/ruby/internal/attr/maybe_unused.h +unixsocket.o: $(hdrdir)/ruby/internal/attr/noalias.h +unixsocket.o: $(hdrdir)/ruby/internal/attr/nodiscard.h +unixsocket.o: $(hdrdir)/ruby/internal/attr/noexcept.h +unixsocket.o: $(hdrdir)/ruby/internal/attr/noinline.h +unixsocket.o: $(hdrdir)/ruby/internal/attr/nonnull.h +unixsocket.o: $(hdrdir)/ruby/internal/attr/noreturn.h +unixsocket.o: $(hdrdir)/ruby/internal/attr/packed_struct.h +unixsocket.o: $(hdrdir)/ruby/internal/attr/pure.h +unixsocket.o: $(hdrdir)/ruby/internal/attr/restrict.h +unixsocket.o: $(hdrdir)/ruby/internal/attr/returns_nonnull.h +unixsocket.o: $(hdrdir)/ruby/internal/attr/warning.h +unixsocket.o: $(hdrdir)/ruby/internal/attr/weakref.h +unixsocket.o: $(hdrdir)/ruby/internal/cast.h +unixsocket.o: $(hdrdir)/ruby/internal/compiler_is.h +unixsocket.o: $(hdrdir)/ruby/internal/compiler_is/apple.h +unixsocket.o: $(hdrdir)/ruby/internal/compiler_is/clang.h +unixsocket.o: $(hdrdir)/ruby/internal/compiler_is/gcc.h +unixsocket.o: $(hdrdir)/ruby/internal/compiler_is/intel.h +unixsocket.o: $(hdrdir)/ruby/internal/compiler_is/msvc.h +unixsocket.o: $(hdrdir)/ruby/internal/compiler_is/sunpro.h +unixsocket.o: $(hdrdir)/ruby/internal/compiler_since.h +unixsocket.o: $(hdrdir)/ruby/internal/config.h +unixsocket.o: $(hdrdir)/ruby/internal/constant_p.h +unixsocket.o: $(hdrdir)/ruby/internal/core.h +unixsocket.o: $(hdrdir)/ruby/internal/core/rarray.h +unixsocket.o: $(hdrdir)/ruby/internal/core/rbasic.h +unixsocket.o: $(hdrdir)/ruby/internal/core/rbignum.h +unixsocket.o: $(hdrdir)/ruby/internal/core/rclass.h +unixsocket.o: $(hdrdir)/ruby/internal/core/rdata.h +unixsocket.o: $(hdrdir)/ruby/internal/core/rfile.h +unixsocket.o: $(hdrdir)/ruby/internal/core/rhash.h +unixsocket.o: $(hdrdir)/ruby/internal/core/robject.h +unixsocket.o: $(hdrdir)/ruby/internal/core/rregexp.h +unixsocket.o: $(hdrdir)/ruby/internal/core/rstring.h +unixsocket.o: $(hdrdir)/ruby/internal/core/rstruct.h +unixsocket.o: $(hdrdir)/ruby/internal/core/rtypeddata.h +unixsocket.o: $(hdrdir)/ruby/internal/ctype.h +unixsocket.o: $(hdrdir)/ruby/internal/dllexport.h +unixsocket.o: $(hdrdir)/ruby/internal/dosish.h +unixsocket.o: $(hdrdir)/ruby/internal/encoding/coderange.h +unixsocket.o: $(hdrdir)/ruby/internal/encoding/ctype.h +unixsocket.o: $(hdrdir)/ruby/internal/encoding/encoding.h +unixsocket.o: $(hdrdir)/ruby/internal/encoding/pathname.h +unixsocket.o: $(hdrdir)/ruby/internal/encoding/re.h +unixsocket.o: $(hdrdir)/ruby/internal/encoding/sprintf.h +unixsocket.o: $(hdrdir)/ruby/internal/encoding/string.h +unixsocket.o: $(hdrdir)/ruby/internal/encoding/symbol.h +unixsocket.o: $(hdrdir)/ruby/internal/encoding/transcode.h +unixsocket.o: $(hdrdir)/ruby/internal/error.h +unixsocket.o: $(hdrdir)/ruby/internal/eval.h +unixsocket.o: $(hdrdir)/ruby/internal/event.h +unixsocket.o: $(hdrdir)/ruby/internal/fl_type.h +unixsocket.o: $(hdrdir)/ruby/internal/gc.h +unixsocket.o: $(hdrdir)/ruby/internal/glob.h +unixsocket.o: $(hdrdir)/ruby/internal/globals.h +unixsocket.o: $(hdrdir)/ruby/internal/has/attribute.h +unixsocket.o: $(hdrdir)/ruby/internal/has/builtin.h +unixsocket.o: $(hdrdir)/ruby/internal/has/c_attribute.h +unixsocket.o: $(hdrdir)/ruby/internal/has/cpp_attribute.h +unixsocket.o: $(hdrdir)/ruby/internal/has/declspec_attribute.h +unixsocket.o: $(hdrdir)/ruby/internal/has/extension.h +unixsocket.o: $(hdrdir)/ruby/internal/has/feature.h +unixsocket.o: $(hdrdir)/ruby/internal/has/warning.h +unixsocket.o: $(hdrdir)/ruby/internal/intern/array.h +unixsocket.o: $(hdrdir)/ruby/internal/intern/bignum.h +unixsocket.o: $(hdrdir)/ruby/internal/intern/class.h +unixsocket.o: $(hdrdir)/ruby/internal/intern/compar.h +unixsocket.o: $(hdrdir)/ruby/internal/intern/complex.h +unixsocket.o: $(hdrdir)/ruby/internal/intern/cont.h +unixsocket.o: $(hdrdir)/ruby/internal/intern/dir.h +unixsocket.o: $(hdrdir)/ruby/internal/intern/enum.h +unixsocket.o: $(hdrdir)/ruby/internal/intern/enumerator.h +unixsocket.o: $(hdrdir)/ruby/internal/intern/error.h +unixsocket.o: $(hdrdir)/ruby/internal/intern/eval.h +unixsocket.o: $(hdrdir)/ruby/internal/intern/file.h +unixsocket.o: $(hdrdir)/ruby/internal/intern/hash.h +unixsocket.o: $(hdrdir)/ruby/internal/intern/io.h +unixsocket.o: $(hdrdir)/ruby/internal/intern/load.h +unixsocket.o: $(hdrdir)/ruby/internal/intern/marshal.h +unixsocket.o: $(hdrdir)/ruby/internal/intern/numeric.h +unixsocket.o: $(hdrdir)/ruby/internal/intern/object.h +unixsocket.o: $(hdrdir)/ruby/internal/intern/parse.h +unixsocket.o: $(hdrdir)/ruby/internal/intern/proc.h +unixsocket.o: $(hdrdir)/ruby/internal/intern/process.h +unixsocket.o: $(hdrdir)/ruby/internal/intern/random.h +unixsocket.o: $(hdrdir)/ruby/internal/intern/range.h +unixsocket.o: $(hdrdir)/ruby/internal/intern/rational.h +unixsocket.o: $(hdrdir)/ruby/internal/intern/re.h +unixsocket.o: $(hdrdir)/ruby/internal/intern/ruby.h +unixsocket.o: $(hdrdir)/ruby/internal/intern/select.h +unixsocket.o: $(hdrdir)/ruby/internal/intern/select/largesize.h +unixsocket.o: $(hdrdir)/ruby/internal/intern/set.h +unixsocket.o: $(hdrdir)/ruby/internal/intern/signal.h +unixsocket.o: $(hdrdir)/ruby/internal/intern/sprintf.h +unixsocket.o: $(hdrdir)/ruby/internal/intern/string.h +unixsocket.o: $(hdrdir)/ruby/internal/intern/struct.h +unixsocket.o: $(hdrdir)/ruby/internal/intern/thread.h +unixsocket.o: $(hdrdir)/ruby/internal/intern/time.h +unixsocket.o: $(hdrdir)/ruby/internal/intern/variable.h +unixsocket.o: $(hdrdir)/ruby/internal/intern/vm.h +unixsocket.o: $(hdrdir)/ruby/internal/interpreter.h +unixsocket.o: $(hdrdir)/ruby/internal/iterator.h +unixsocket.o: $(hdrdir)/ruby/internal/memory.h +unixsocket.o: $(hdrdir)/ruby/internal/method.h +unixsocket.o: $(hdrdir)/ruby/internal/module.h +unixsocket.o: $(hdrdir)/ruby/internal/newobj.h +unixsocket.o: $(hdrdir)/ruby/internal/scan_args.h +unixsocket.o: $(hdrdir)/ruby/internal/special_consts.h +unixsocket.o: $(hdrdir)/ruby/internal/static_assert.h +unixsocket.o: $(hdrdir)/ruby/internal/stdalign.h +unixsocket.o: $(hdrdir)/ruby/internal/stdbool.h +unixsocket.o: $(hdrdir)/ruby/internal/stdckdint.h +unixsocket.o: $(hdrdir)/ruby/internal/symbol.h +unixsocket.o: $(hdrdir)/ruby/internal/value.h +unixsocket.o: $(hdrdir)/ruby/internal/value_type.h +unixsocket.o: $(hdrdir)/ruby/internal/variable.h +unixsocket.o: $(hdrdir)/ruby/internal/warning_push.h +unixsocket.o: $(hdrdir)/ruby/internal/xmalloc.h +unixsocket.o: $(hdrdir)/ruby/io.h +unixsocket.o: $(hdrdir)/ruby/missing.h +unixsocket.o: $(hdrdir)/ruby/onigmo.h +unixsocket.o: $(hdrdir)/ruby/oniguruma.h +unixsocket.o: $(hdrdir)/ruby/ruby.h +unixsocket.o: $(hdrdir)/ruby/st.h +unixsocket.o: $(hdrdir)/ruby/subst.h +unixsocket.o: $(hdrdir)/ruby/thread.h +unixsocket.o: $(hdrdir)/ruby/thread_native.h +unixsocket.o: $(hdrdir)/ruby/util.h +unixsocket.o: $(hdrdir)/ruby/version.h +unixsocket.o: $(top_srcdir)/ccan/check_type/check_type.h +unixsocket.o: $(top_srcdir)/ccan/container_of/container_of.h +unixsocket.o: $(top_srcdir)/ccan/list/list.h +unixsocket.o: $(top_srcdir)/ccan/str/str.h +unixsocket.o: $(top_srcdir)/encindex.h +unixsocket.o: $(top_srcdir)/id_table.h +unixsocket.o: $(top_srcdir)/internal.h +unixsocket.o: $(top_srcdir)/internal/array.h +unixsocket.o: $(top_srcdir)/internal/basic_operators.h +unixsocket.o: $(top_srcdir)/internal/box.h +unixsocket.o: $(top_srcdir)/internal/compilers.h +unixsocket.o: $(top_srcdir)/internal/error.h +unixsocket.o: $(top_srcdir)/internal/gc.h +unixsocket.o: $(top_srcdir)/internal/imemo.h +unixsocket.o: $(top_srcdir)/internal/io.h +unixsocket.o: $(top_srcdir)/internal/sanitizers.h +unixsocket.o: $(top_srcdir)/internal/serial.h +unixsocket.o: $(top_srcdir)/internal/set_table.h +unixsocket.o: $(top_srcdir)/internal/static_assert.h +unixsocket.o: $(top_srcdir)/internal/string.h +unixsocket.o: $(top_srcdir)/internal/thread.h +unixsocket.o: $(top_srcdir)/internal/vm.h +unixsocket.o: $(top_srcdir)/internal/warnings.h +unixsocket.o: $(top_srcdir)/method.h +unixsocket.o: $(top_srcdir)/node.h +unixsocket.o: $(top_srcdir)/ruby_assert.h +unixsocket.o: $(top_srcdir)/ruby_atomic.h +unixsocket.o: $(top_srcdir)/rubyparser.h +unixsocket.o: $(top_srcdir)/shape.h +unixsocket.o: $(top_srcdir)/thread_pthread.h +unixsocket.o: $(top_srcdir)/vm_core.h +unixsocket.o: $(top_srcdir)/vm_opts.h +unixsocket.o: constdefs.h +unixsocket.o: rubysocket.h +unixsocket.o: sockport.h +unixsocket.o: unixsocket.c +unixsocket.o: {$(VPATH)}id.h +# AUTOGENERATED DEPENDENCIES END diff --git a/ext/socket/extconf.rb b/ext/socket/extconf.rb index 8a13ddba73..a814e21c3a 100644 --- a/ext/socket/extconf.rb +++ b/ext/socket/extconf.rb @@ -1,127 +1,20 @@ +# frozen_string_literal: false require 'mkmf' -case RUBY_PLATFORM -when /bccwin32/ - test_func = "WSACleanup" - have_library("ws2_32", "WSACleanup") -when /mswin32|mingw/ - test_func = "WSACleanup" - if with_config("winsock2") - have_library("ws2_32", "WSACleanup") - else - have_library("wsock32", "WSACleanup") - end -when /cygwin/ - test_func = "socket" -when /beos/ - test_func = "socket" - have_library("net", "socket") - have_func("closesocket") -when /i386-os2_emx/ - test_func = "socket" - have_library("socket", "socket") -else - test_func = "socket" - have_library("nsl", "t_open") - have_library("socket", "socket") -end - -unless $mswin or $bccwin or $mingw - headers = %w<sys/types.h netdb.h string.h sys/socket.h netinet/in.h> -end -if /solaris/ =~ RUBY_PLATFORM and !try_compile("") - # bug of gcc 3.0 on Solaris 8 ? - headers << "sys/feature_tests.h" -end -if have_header("arpa/inet.h") - headers << "arpa/inet.h" -end - -ipv6 = false -default_ipv6 = /cygwin/ !~ RUBY_PLATFORM -if enable_config("ipv6", default_ipv6) - if checking_for("ipv6") {try_link(<<EOF)} +AF_INET6_SOCKET_CREATION_TEST = <<EOF #include <sys/types.h> +#ifndef _WIN32 #include <sys/socket.h> -main() +#endif +int +main(void) { socket(AF_INET6, SOCK_STREAM, 0); + return 0; } EOF - $defs << "-DENABLE_IPV6" << "-DINET6" - ipv6 = true - end -end - -if ipv6 - ipv6lib = nil - class << (fmt = "unknown") - def %(s) s || self end - end - idirs, ldirs = dir_config("inet6", %w[/usr/inet6 /usr/local/v6].find {|d| File.directory?(d)}) - checking_for("ipv6 type", fmt) do - if have_macro("IPV6_INRIA_VERSION", "netinet/in.h") - "inria" - elsif have_macro("__KAME__", "netinet/in.h") - have_library(ipv6lib = "inet6") - "kame" - elsif have_macro("_TOSHIBA_INET6", "sys/param.h") - have_library(ipv6lib = "inet6") and "toshiba" - elsif have_macro("__V6D__", "sys/v6config.h") - have_library(ipv6lib = "v6") and "v6d" - elsif have_macro("_ZETA_MINAMI_INET6", "sys/param.h") - have_library(ipv6lib = "inet6") and "zeta" - elsif ipv6lib = with_config("ipv6-lib") - warn <<EOS ---with-ipv6-lib and --with-ipv6-libdir option will be obsolete, use ---with-inet6lib and --with-inet6-{include,lib} options instead. -EOS - find_library(ipv6lib, nil, with_config("ipv6-libdir", ldirs)) and - ipv6lib - elsif have_library("inet6") - "inet6" - end - end or not ipv6lib or abort <<EOS -Fatal: no #{ipv6lib} library found. cannot continue. -You need to fetch lib#{ipv6lib}.a from appropriate -ipv6 kit and compile beforehand. -EOS -end - -if have_struct_member("struct sockaddr_in", "sin_len", headers) - $defs[-1] = "-DHAVE_SIN_LEN" -end - -# doug's fix, NOW add -Dss_family... only if required! -[nil, " -Dss_family=__ss_family -Dss_len=__ss_len"].each do |flags| - if flags - cppflags = $CPPFLAGS - $CPPFLAGS += flags - end - if have_struct_member("struct sockaddr_storage", "ss_family", headers) - $defs[-1] = "-DHAVE_SOCKADDR_STORAGE" - break - elsif flags - $CPPFLAGS = cppflags - end -end - -if have_struct_member("struct sockaddr", "sa_len", headers) - $defs[-1] = "-DHAVE_SA_LEN " -end - -have_header("netinet/tcp.h") if not /cygwin/ =~ RUBY_PLATFORM # for cygwin 1.1.5 -have_header("netinet/udp.h") - -if have_func("sendmsg") | have_func("recvmsg") - have_struct_member('struct msghdr', 'msg_control', ['sys/types.h', 'sys/socket.h']) - have_struct_member('struct msghdr', 'msg_accrights', ['sys/types.h', 'sys/socket.h']) -end - -getaddr_info_ok = enable_config("wide-getaddrinfo") do - checking_for("wide getaddrinfo") {try_run(<<EOF)} -#{cpp_include(headers)} +GETADDRINFO_GETNAMEINFO_TEST = <<EOF #include <stdlib.h> #ifndef EXIT_SUCCESS @@ -135,11 +28,17 @@ getaddr_info_ok = enable_config("wide-getaddrinfo") do #define AF_LOCAL AF_UNIX #endif -main() +int +main(void) { int passive, gaierr, inet4 = 0, inet6 = 0; struct addrinfo hints, *ai, *aitop; char straddr[INET6_ADDRSTRLEN], strport[16]; +#ifdef _WIN32 + WSADATA retdata; + + WSAStartup(MAKEWORD(2, 0), &retdata); +#endif for (passive = 0; passive <= 1; passive++) { memset(&hints, 0, sizeof(hints)); @@ -214,75 +113,599 @@ main() if (aitop) freeaddrinfo(aitop); - exit(EXIT_SUCCESS); + return EXIT_SUCCESS; bad: if (aitop) freeaddrinfo(aitop); - exit(EXIT_FAILURE); + return EXIT_FAILURE; } EOF + +RECVMSG_WITH_MSG_PEEK_ALLOCATE_FD_TEST = <<'EOF' +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <unistd.h> + +int main(int argc, char *argv[]) +{ + int ps[2], sv[2]; + int ret; + ssize_t ss; + int s_fd, r_fd; + struct msghdr s_msg, r_msg; + union { + struct cmsghdr hdr; + char dummy[CMSG_SPACE(sizeof(int))]; + } s_cmsg, r_cmsg; + struct iovec s_iov, r_iov; + char s_buf[1], r_buf[1]; + struct stat s_statbuf, r_statbuf; + + ret = pipe(ps); + if (ret == -1) { perror("pipe"); exit(EXIT_FAILURE); } + + s_fd = ps[0]; + + ret = socketpair(AF_UNIX, SOCK_DGRAM, 0, sv); + if (ret == -1) { perror("socketpair"); exit(EXIT_FAILURE); } + + s_msg.msg_name = NULL; + s_msg.msg_namelen = 0; + s_msg.msg_iov = &s_iov; + s_msg.msg_iovlen = 1; + s_msg.msg_control = &s_cmsg; + s_msg.msg_controllen = CMSG_SPACE(sizeof(int));; + s_msg.msg_flags = 0; + + s_iov.iov_base = &s_buf; + s_iov.iov_len = sizeof(s_buf); + + s_buf[0] = 'a'; + + s_cmsg.hdr.cmsg_len = CMSG_LEN(sizeof(int)); + s_cmsg.hdr.cmsg_level = SOL_SOCKET; + s_cmsg.hdr.cmsg_type = SCM_RIGHTS; + memcpy(CMSG_DATA(&s_cmsg.hdr), (char *)&s_fd, sizeof(int)); + + ss = sendmsg(sv[0], &s_msg, 0); + if (ss == -1) { perror("sendmsg"); exit(EXIT_FAILURE); } + + r_msg.msg_name = NULL; + r_msg.msg_namelen = 0; + r_msg.msg_iov = &r_iov; + r_msg.msg_iovlen = 1; + r_msg.msg_control = &r_cmsg; + r_msg.msg_controllen = CMSG_SPACE(sizeof(int)); + r_msg.msg_flags = 0; + + r_iov.iov_base = &r_buf; + r_iov.iov_len = sizeof(r_buf); + + r_buf[0] = '0'; + + memset(&r_cmsg, 0xff, CMSG_SPACE(sizeof(int))); + + ss = recvmsg(sv[1], &r_msg, MSG_PEEK); + if (ss == -1) { perror("recvmsg"); exit(EXIT_FAILURE); } + + if (ss != 1) { + fprintf(stderr, "unexpected return value from recvmsg: %ld\n", (long)ss); + exit(EXIT_FAILURE); + } + if (r_buf[0] != 'a') { + fprintf(stderr, "unexpected return data from recvmsg: 0x%02x\n", r_buf[0]); + exit(EXIT_FAILURE); + } + + if (r_msg.msg_controllen < CMSG_LEN(sizeof(int))) { + fprintf(stderr, "unexpected: r_msg.msg_controllen < CMSG_LEN(sizeof(int)) not hold: %ld\n", + (long)r_msg.msg_controllen); + exit(EXIT_FAILURE); + } + if (r_cmsg.hdr.cmsg_len < CMSG_LEN(sizeof(int))) { + fprintf(stderr, "unexpected: r_cmsg.hdr.cmsg_len < CMSG_LEN(sizeof(int)) not hold: %ld\n", + (long)r_cmsg.hdr.cmsg_len); + exit(EXIT_FAILURE); + } + memcpy((char *)&r_fd, CMSG_DATA(&r_cmsg.hdr), sizeof(int)); + + if (r_fd < 0) { + fprintf(stderr, "negative r_fd: %d\n", r_fd); + exit(EXIT_FAILURE); + } + + if (r_fd == s_fd) { + fprintf(stderr, "r_fd and s_fd is same: %d\n", r_fd); + exit(EXIT_FAILURE); + } + + ret = fstat(s_fd, &s_statbuf); + if (ret == -1) { perror("fstat(s_fd)"); exit(EXIT_FAILURE); } + + ret = fstat(r_fd, &r_statbuf); + if (ret == -1) { perror("fstat(r_fd)"); exit(EXIT_FAILURE); } + + if (s_statbuf.st_dev != r_statbuf.st_dev || + s_statbuf.st_ino != r_statbuf.st_ino) { + fprintf(stderr, "dev/ino doesn't match: s_fd:%ld/%ld r_fd:%ld/%ld\n", + (long)s_statbuf.st_dev, (long)s_statbuf.st_ino, + (long)r_statbuf.st_dev, (long)r_statbuf.st_ino); + exit(EXIT_FAILURE); + } + + return EXIT_SUCCESS; +} +EOF + +def test_recvmsg_with_msg_peek_creates_fds(headers) + case RUBY_PLATFORM + when /linux/ + # Linux 2.6.38 allocate fds by recvmsg with MSG_PEEK. + close_fds = true + when /bsd|darwin/ + # FreeBSD 8.2.0, NetBSD 5 and MacOS X Snow Leopard doesn't + # allocate fds by recvmsg with MSG_PEEK. + # [ruby-dev:44189] + # http://bugs.ruby-lang.org/issues/5075 + close_fds = false + when /cygwin/ + # Cygwin doesn't support fd passing. + # http://cygwin.com/ml/cygwin/2003-09/msg01808.html + close_fds = false + else + close_fds = nil + end + if !CROSS_COMPILING + if checking_for("recvmsg() with MSG_PEEK allocate file descriptors") { + try_run(cpp_include(headers) + RECVMSG_WITH_MSG_PEEK_ALLOCATE_FD_TEST) + } + if close_fds == false + warn "unexpected fd-passing recvmsg() with MSG_PEEK behavor on #{RUBY_PLATFORM}: fd allocation unexpected." + elsif close_fds == nil + puts "info: #{RUBY_PLATFORM} recvmsg() with MSG_PEEK allocates fds on fd-passing." + end + close_fds = true + else + if close_fds == true + warn "unexpected fd-passing recvmsg() with MSG_PEEK behavor on #{RUBY_PLATFORM}: fd allocation expected." + elsif close_fds == nil + puts "info: #{RUBY_PLATFORM}: recvmsg() with MSG_PEEK doesn't allocates fds on fd-passing." + end + close_fds = false + end + end + if close_fds == nil + abort <<EOS +Fatal: cannot test fd-passing recvmsg() with MSG_PEEK behavor +because cross-compilation for #{RUBY_PLATFORM}. +If recvmsg() with MSG_PEEK allocates fds on fd passing: +--enable-close-fds-by-recvmsg-with-peek +If recvmsg() with MSG_PEEK doesn't allocate fds on fd passing: +--disable-close-fds-by-recvmsg-with-peek +EOS + end + close_fds +end + +$INCFLAGS << " -I$(topdir) -I$(top_srcdir)" + +if /darwin/ =~ RUBY_PLATFORM + # For IPv6 extension header access on OS X 10.7+ [Bug #8517] + $CFLAGS << " -D__APPLE_USE_RFC_3542" +end + +headers = [] +unless $mswin or $mingw + headers = %w<sys/types.h netdb.h string.h sys/socket.h netinet/in.h> +end + +%w[ + sys/uio.h + xti.h + netinet/in_systm.h + netinet/tcp.h + netinet/tcp_fsm.h + netinet/udp.h + arpa/inet.h + netpacket/packet.h + net/ethernet.h + sys/un.h + afunix.h + ifaddrs.h + sys/ioctl.h + sys/sockio.h + net/if.h + sys/param.h + sys/ucred.h + ucred.h + net/if_dl.h + arpa/nameser.h + resolv.h + pthread.h + sched.h +].each {|h| + if have_header(h, headers) + headers << h + end +} + +have_struct_member("struct sockaddr", "sa_len", headers) # 4.4BSD +have_struct_member("struct sockaddr_in", "sin_len", headers) # 4.4BSD +have_struct_member("struct sockaddr_in6", "sin6_len", headers) # 4.4BSD + +if have_type("struct sockaddr_un", headers) # POSIX + have_struct_member("struct sockaddr_un", "sun_len", headers) # 4.4BSD +end + +have_type("struct sockaddr_dl", headers) # AF_LINK address. 4.4BSD since Net2 + +have_type("struct sockaddr_storage", headers) + +have_type("struct addrinfo", headers) + +def check_socklen(headers) + def (fmt = "none").%(x) + x || self + end + s = checking_for("RSTRING_SOCKLEN", fmt) do + if try_static_assert("sizeof(socklen_t) >= sizeof(long)", headers) + "RSTRING_LEN" + else + "RSTRING_LENINT" + end + end + $defs << "-DRSTRING_SOCKLEN=(socklen_t)"+s +end + +if have_type("socklen_t", headers) + check_socklen(headers) +end + +have_type("struct in_pktinfo", headers) {|src| + src.sub(%r'^/\*top\*/', '\&'"\n#if defined(IPPROTO_IP) && defined(IP_PKTINFO)") << + "#else\n" << "#error\n" << ">>>>>> no in_pktinfo <<<<<<\n" << "#endif\n" +} and have_struct_member("struct in_pktinfo", "ipi_spec_dst", headers) +have_type("struct in6_pktinfo", headers) {|src| + src.sub(%r'^/\*top\*/', '\&'"\n#if defined(IPPROTO_IPV6) && defined(IPV6_PKTINFO)") << + "#else\n" << "#error\n" << ">>>>>> no in6_pktinfo <<<<<<\n" << "#endif\n" +} + +have_type("struct sockcred", headers) +have_type("struct cmsgcred", headers) + +have_type("struct ip_mreq", headers) # 4.4BSD +have_type("struct ip_mreqn", headers) # Linux 2.4 +have_type("struct ipv6_mreq", headers) # RFC 3493 + +have_msg_control = nil +have_msg_control = have_struct_member('struct msghdr', 'msg_control', headers) unless $mswin or $mingw +have_struct_member('struct msghdr', 'msg_accrights', headers) + +if have_type("struct tcp_info", headers) + have_const("TCP_ESTABLISHED", headers) + have_const("TCP_SYN_SENT", headers) + have_const("TCP_SYN_RECV", headers) + have_const("TCP_FIN_WAIT1", headers) + have_const("TCP_FIN_WAIT2", headers) + have_const("TCP_TIME_WAIT", headers) + have_const("TCP_CLOSE", headers) + have_const("TCP_CLOSE_WAIT", headers) + have_const("TCP_LAST_ACK", headers) + have_const("TCP_LISTEN", headers) + have_const("TCP_CLOSING", headers) + have_struct_member('struct tcp_info', 'tcpi_state', headers) + if /solaris/ !~ RUBY_PLATFORM + have_struct_member('struct tcp_info', 'tcpi_ca_state', headers) + end + have_struct_member('struct tcp_info', 'tcpi_retransmits', headers) + have_struct_member('struct tcp_info', 'tcpi_probes', headers) + have_struct_member('struct tcp_info', 'tcpi_backoff', headers) + have_struct_member('struct tcp_info', 'tcpi_options', headers) + have_struct_member('struct tcp_info', 'tcpi_snd_wscale', headers) + have_struct_member('struct tcp_info', 'tcpi_rcv_wscale', headers) + have_struct_member('struct tcp_info', 'tcpi_rto', headers) + have_struct_member('struct tcp_info', 'tcpi_ato', headers) + have_struct_member('struct tcp_info', 'tcpi_snd_mss', headers) + have_struct_member('struct tcp_info', 'tcpi_rcv_mss', headers) + have_struct_member('struct tcp_info', 'tcpi_unacked', headers) + have_struct_member('struct tcp_info', 'tcpi_sacked', headers) + have_struct_member('struct tcp_info', 'tcpi_lost', headers) + have_struct_member('struct tcp_info', 'tcpi_retrans', headers) + have_struct_member('struct tcp_info', 'tcpi_fackets', headers) + have_struct_member('struct tcp_info', 'tcpi_last_data_sent', headers) + have_struct_member('struct tcp_info', 'tcpi_last_ack_sent', headers) + have_struct_member('struct tcp_info', 'tcpi_last_data_recv', headers) + have_struct_member('struct tcp_info', 'tcpi_last_ack_recv', headers) + have_struct_member('struct tcp_info', 'tcpi_pmtu', headers) + have_struct_member('struct tcp_info', 'tcpi_rcv_ssthresh', headers) + have_struct_member('struct tcp_info', 'tcpi_rtt', headers) + have_struct_member('struct tcp_info', 'tcpi_rttvar', headers) + have_struct_member('struct tcp_info', 'tcpi_snd_ssthresh', headers) + have_struct_member('struct tcp_info', 'tcpi_snd_cwnd', headers) + have_struct_member('struct tcp_info', 'tcpi_advmss', headers) + have_struct_member('struct tcp_info', 'tcpi_reordering', headers) + have_struct_member('struct tcp_info', 'tcpi_rcv_rtt', headers) + have_struct_member('struct tcp_info', 'tcpi_rcv_space', headers) + have_struct_member('struct tcp_info', 'tcpi_total_retrans', headers) + + # FreeBSD extension + have_struct_member('struct tcp_info', 'tcpi_snd_wnd', headers) + have_struct_member('struct tcp_info', 'tcpi_snd_bwnd', headers) + have_struct_member('struct tcp_info', 'tcpi_snd_nxt', headers) + have_struct_member('struct tcp_info', 'tcpi_rcv_nxt', headers) + have_struct_member('struct tcp_info', 'tcpi_toe_tid', headers) + have_struct_member('struct tcp_info', 'tcpi_snd_rexmitpack', headers) + have_struct_member('struct tcp_info', 'tcpi_rcv_ooopack', headers) + have_struct_member('struct tcp_info', 'tcpi_snd_zerowin', headers) end -if ipv6 and not getaddr_info_ok - abort <<EOS + +case RUBY_PLATFORM +when /mswin(32|64)|mingw/ + test_func = "WSACleanup" + have_library("iphlpapi") + have_library("ws2_32", "WSACleanup", headers) +when /cygwin/ + test_func = "socket(0,0,0)" +when /haiku/ + test_func = "socket(0,0,0)" + have_library("network", "socket(0,0,0)", headers) +else + test_func = "socket(0,0,0)" + have_library("nsl", 't_open("", 0, (struct t_info *)NULL)', headers) # SunOS + have_library("socket", "socket(0,0,0)", headers) # SunOS +end + +if have_func(test_func, headers) + + have_func("sendmsg(0, (struct msghdr *)NULL, 0)", headers) # POSIX + have_recvmsg = have_func("recvmsg(0, (struct msghdr *)NULL, 0)", headers) # POSIX + + have_func("freehostent((struct hostent *)NULL)", headers) # RFC 2553 + have_func("freeaddrinfo((struct addrinfo *)NULL)", headers) # RFC 2553 + + if /haiku/ !~ RUBY_PLATFORM and + have_func("gai_strerror(0)", headers) # POSIX + if checking_for("gai_strerror() returns const pointer") {!try_compile(<<EOF)} +#{cpp_include(headers)} +#include <stdlib.h> +void +conftest_gai_strerror_is_const() +{ + *gai_strerror(0) = 0; +} +EOF + $defs << "-DGAI_STRERROR_CONST" + end + end + + have_func("accept4", headers) + + have_func('inet_ntop(0, (const void *)0, (char *)0, 0)', headers) or + have_func("inet_ntoa(*(struct in_addr *)NULL)", headers) + have_func('inet_pton(0, "", (void *)0)', headers) or + have_func('inet_aton("", (struct in_addr *)0)', headers) + have_func('getservbyport(0, "")', headers) + have_func("getifaddrs((struct ifaddrs **)NULL)", headers) + have_struct_member("struct if_data", "ifi_vhid", headers) # FreeBSD + + have_func("getpeereid", headers) + + have_func("getpeerucred(0, (ucred_t **)NULL)", headers) # SunOS + + have_func_decl = proc do |name, headers| + # check if there is a declaration of <name> by trying to declare + # both "int <name>(void)" and "void <name>(void)" + # (at least one attempt should fail if there is a declaration) + if !checking_for("declaration of #{name}()") {!%w[int void].all? {|ret| try_compile(<<EOF)}} +#{cpp_include(headers)} +#{ret} #{name}(void); +EOF + $defs << "-DNEED_#{name.tr_cpp}_DECL" + end + end + if have_func('if_indextoname(0, "")', headers) + have_func_decl["if_indextoname", headers] + end + if have_func('if_nametoindex("")', headers) + have_func_decl["if_nametoindex", headers] + end + + have_func("hsterror", headers) + have_func('getipnodebyname("", 0, 0, (int *)0)', headers) # RFC 2553 + have_func('gethostbyname2("", 0)', headers) # RFC 2133 + have_func("socketpair(0, 0, 0, 0)", headers) + unless have_func("gethostname((char *)0, 0)", headers) + have_func("uname((struct utsname *)NULL)", headers) + end + + ipv6 = false + default_ipv6 = /haiku/ !~ RUBY_PLATFORM + if enable_config("ipv6", default_ipv6) + if checking_for("ipv6") {try_link(AF_INET6_SOCKET_CREATION_TEST)} + $defs << "-DENABLE_IPV6" << "-DINET6" + ipv6 = true + end + end + + if ipv6 + if $mingw + $CPPFLAGS << " -D_WIN32_WINNT=0x501" unless $CPPFLAGS.include?("_WIN32_WINNT") + end + ipv6lib = nil + class << (fmt = "unknown") + def %(s) s || self end + end + idirs, ldirs = dir_config("inet6", %w[/usr/inet6 /usr/local/v6].find {|d| File.directory?(d)}) + checking_for("ipv6 type", fmt) do + if have_macro("IPV6_INRIA_VERSION", "netinet/in.h") + "inria" + elsif have_macro("__KAME__", "netinet/in.h") + have_library(ipv6lib = "inet6") + "kame" + elsif have_macro("_TOSHIBA_INET6", "sys/param.h") + have_library(ipv6lib = "inet6") and "toshiba" + elsif have_macro("__V6D__", "sys/v6config.h") + have_library(ipv6lib = "v6") and "v6d" + elsif have_macro("_ZETA_MINAMI_INET6", "sys/param.h") + have_library(ipv6lib = "inet6") and "zeta" + elsif have_library("inet6") + "inet6" + end + end or not ipv6lib or abort <<EOS + +Fatal: no #{ipv6lib} library found. cannot continue. +You need to fetch lib#{ipv6lib}.a from appropriate +ipv6 kit and compile beforehand. +EOS + end + + if !have_macro("IPPROTO_IPV6", headers) && have_const("IPPROTO_IPV6", headers) + File.read(File.join(File.dirname(__FILE__), "mkconstants.rb")).sub(/\A.*^__END__$/m, '').split(/\r?\n/).grep(/\AIPPROTO_\w*/){$&}.each {|name| + have_const(name, headers) unless $defs.include?("-DHAVE_CONST_#{name.upcase}") + } + end + + if enable_config("close-fds-by-recvmsg-with-peek") { + have_msg_control && have_recvmsg && + have_const('AF_UNIX', headers) && have_const('SCM_RIGHTS', headers) && + test_recvmsg_with_msg_peek_creates_fds(headers) + } + $defs << "-DFD_PASSING_WORK_WITH_RECVMSG_MSG_PEEK" + end + + case enable_config("wide-getaddrinfo") + when true + getaddr_info_ok = :wide + when nil, false + getaddr_info_ok = (:wide if getaddr_info_ok.nil?) + if have_func("getnameinfo", headers) and have_func("getaddrinfo", headers) + if CROSS_COMPILING || + $mingw || $mswin || + checking_for("system getaddrinfo working") { + try_run(cpp_include(headers) + GETADDRINFO_GETNAMEINFO_TEST) + } + getaddr_info_ok = :os + end + end + else + raise "unexpected enable_config() value" + end + + if ipv6 and not getaddr_info_ok + abort <<EOS Fatal: --enable-ipv6 is specified, and your OS seems to support IPv6 feature. But your getaddrinfo() and getnameinfo() are appeared to be broken. Sorry, you cannot compile IPv6 socket classes with broken these functions. You can try --enable-wide-getaddrinfo. EOS -end + end -case with_config("lookup-order-hack", "UNSPEC") -when "INET" - $defs << "-DLOOKUP_ORDER_HACK_INET" -when "INET6" - $defs << "-DLOOKUP_ORDER_HACK_INET6" -when "UNSPEC" - # nothing special -else - abort <<EOS + case with_config("lookup-order-hack", "UNSPEC") + when "INET" + $defs << "-DLOOKUP_ORDER_HACK_INET" + when "INET6" + $defs << "-DLOOKUP_ORDER_HACK_INET6" + when "UNSPEC" + # nothing special + else + abort <<EOS Fatal: invalid value for --with-lookup-order-hack (expected INET, INET6 or UNSPEC) EOS -end + end -$objs = ["socket.#{$OBJEXT}"] + $objs = [ + "init.#{$OBJEXT}", + "constants.#{$OBJEXT}", + "basicsocket.#{$OBJEXT}", + "socket.#{$OBJEXT}", + "ipsocket.#{$OBJEXT}", + "tcpsocket.#{$OBJEXT}", + "tcpserver.#{$OBJEXT}", + "sockssocket.#{$OBJEXT}", + "udpsocket.#{$OBJEXT}", + "unixsocket.#{$OBJEXT}", + "unixserver.#{$OBJEXT}", + "option.#{$OBJEXT}", + "ancdata.#{$OBJEXT}", + "raddrinfo.#{$OBJEXT}", + "ifaddr.#{$OBJEXT}" + ] -unless getaddr_info_ok and have_func("getnameinfo", "netdb.h") and have_func("getaddrinfo", "netdb.h") - if have_struct_member("struct in6_addr", "s6_addr8", headers) - $defs[-1] = "-DHAVE_ADDR8" + if getaddr_info_ok == :wide + if !have_type("struct in6_addr", headers) and have_type("struct in_addr6", headers) + $defs.pop(2) + $defs << "-Din_addr6=in6_addr" + end + if have_struct_member("struct in6_addr", "s6_addr8", headers) + $defs[-1] = "-Ds6_addr=s6_addr8" + end + if ipv6 == "kame" && have_struct_member("struct in6_addr", "s6_addr32", headers) + $defs[-1] = "-DFAITH" + end + $CPPFLAGS="-I. "+$CPPFLAGS + $objs += ["getaddrinfo.#{$OBJEXT}"] + $objs += ["getnameinfo.#{$OBJEXT}"] + $defs << "-DGETADDRINFO_EMU" end - $CPPFLAGS="-I. "+$CPPFLAGS - $objs += ["getaddrinfo.#{$OBJEXT}"] - $objs += ["getnameinfo.#{$OBJEXT}"] - have_func("inet_ntop") or have_func("inet_ntoa") - have_func("inet_pton") or have_func("inet_aton") - have_func("getservbyport") - have_header("arpa/nameser.h") - have_header("resolv.h") -end -unless have_type("socklen_t", headers) - $defs << "-Dsocklen_t=int" -end + # workaround for recent Windows SDK + $defs << "-DIPPROTO_IPV6=IPPROTO_IPV6" if $defs.include?("-DHAVE_CONST_IPPROTO_IPV6") && !have_macro("IPPROTO_IPV6") -have_header("sys/un.h") -have_header("sys/uio.h") + $distcleanfiles << "constants.h" << "constdefs.*" -if have_func(test_func) - have_func("hsterror") - have_func("getipnodebyname") or have_func("gethostbyname2") - have_func("socketpair") - unless have_func("gethostname") - have_func("uname") - end if enable_config("socks", ENV["SOCKS_SERVER"]) if have_library("socks5", "SOCKSinit") $defs << "-DSOCKS5" << "-DSOCKS" - elsif have_library("socks", "Rconnect") + elsif have_library("socksd", "Rconnect") || have_library("socks", "Rconnect") $defs << "-DSOCKS" end end + + hdr = "netinet6/in6.h" + /darwin/ =~ RUBY_PLATFORM and + checking_for("if apple's #{hdr} needs s6_addr patch") {!try_compile(<<"SRC", nil, :werror=>true)} and +#include <netinet/in.h> +int t(struct in6_addr *addr) {return IN6_IS_ADDR_UNSPECIFIED(addr);} +SRC + checking_for("fixing apple's #{hdr}", "%s") do + file = xpopen(%w"clang -include netinet/in.h -E -xc -", in: IO::NULL) do |f| + re = %r[^# *\d+ *"(.*/netinet/in\.h)"] + Logging.message " grep(#{re})\n" + f.read[re, 1] + end + Logging.message "Substitute from #{file}\n" + + in6 = File.read(file) + if in6.gsub!(/\*\(const\s+__uint32_t\s+\*\)\(const\s+void\s+\*\)\(&(\(\w+\))->s6_addr\[(\d+)\]\)/) do + i, r = $2.to_i.divmod(4) + if r.zero? + "#$1->__u6_addr.__u6_addr32[#{i}]" + else + $& + end + end + FileUtils.mkdir_p(File.dirname(hdr)) + File.write(hdr, in6) + $distcleanfiles << hdr + $distcleandirs << File.dirname(hdr) + "done" + else + "not needed" + end + end + + have_func("pthread_create") + have_func("pthread_detach") + have_func("pthread_attr_setdetachstate") + + $VPATH << '$(topdir)' << '$(top_srcdir)' create_makefile("socket") end diff --git a/ext/socket/getaddrinfo.c b/ext/socket/getaddrinfo.c index 9fb4ebcb06..9a65490b1d 100644 --- a/ext/socket/getaddrinfo.c +++ b/ext/socket/getaddrinfo.c @@ -1,7 +1,7 @@ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -13,7 +13,7 @@ * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. - * + * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE @@ -38,15 +38,14 @@ * - PF_UNSPEC case would be handled in getipnodebyname() with the AI_ALL flag. */ -#include "config.h" +#include "ruby/config.h" +#ifdef RUBY_EXTCONF_H +#include RUBY_EXTCONF_H +#endif #include <sys/types.h> -#if !defined(_WIN32) && !defined(__VMS) +#ifndef _WIN32 #include <sys/param.h> -#if defined(__BEOS__) -# include <net/socket.h> -#else -# include <sys/socket.h> -#endif +#include <sys/socket.h> #include <netinet/in.h> #if defined(HAVE_ARPA_INET_H) #include <arpa/inet.h> @@ -62,13 +61,9 @@ #include <resolv.h> #endif #include <unistd.h> -#elif defined(__VMS ) -#include <socket.h> -#include <inet.h> -#include <in.h> -#include <netdb.h> #else #include <winsock2.h> +#include <ws2tcpip.h> #include <io.h> #endif #include <string.h> @@ -81,13 +76,13 @@ #include <socks.h> #endif +#ifndef HAVE_TYPE_SOCKLEN_T +typedef int socklen_t; +#endif + #include "addrinfo.h" #include "sockport.h" -#if defined(__KAME__) && defined(INET6) -# define FAITH -#endif - #define SUCCESS 0 #define ANY 0 #define YES 1 @@ -100,42 +95,42 @@ static struct in6_addr faith_prefix = IN6ADDR_ANY_INIT; static const char in_addrany[] = { 0, 0, 0, 0 }; static const char in6_addrany[] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; -static const char in_loopback[] = { 127, 0, 0, 1 }; +static const char in_loopback[] = { 127, 0, 0, 1 }; static const char in6_loopback[] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }; struct sockinet { - u_char si_len; - u_char si_family; - u_short si_port; + u_char si_len; + u_char si_family; + u_short si_port; }; static const struct afd { - int a_af; - int a_addrlen; - int a_socklen; - int a_off; - const char *a_addrany; - const char *a_loopback; + int a_af; + int a_addrlen; + int a_socklen; + int a_off; + const char *a_addrany; + const char *a_loopback; } afdl [] = { #ifdef INET6 #define N_INET6 0 - {PF_INET6, sizeof(struct in6_addr), - sizeof(struct sockaddr_in6), - offsetof(struct sockaddr_in6, sin6_addr), - in6_addrany, in6_loopback}, + {PF_INET6, sizeof(struct in6_addr), + sizeof(struct sockaddr_in6), + offsetof(struct sockaddr_in6, sin6_addr), + in6_addrany, in6_loopback}, #define N_INET 1 #else #define N_INET 0 #endif - {PF_INET, sizeof(struct in_addr), - sizeof(struct sockaddr_in), - offsetof(struct sockaddr_in, sin_addr), - in_addrany, in_loopback}, - {0, 0, 0, 0, NULL, NULL}, + {PF_INET, sizeof(struct in_addr), + sizeof(struct sockaddr_in), + offsetof(struct sockaddr_in, sin_addr), + in_addrany, in_loopback}, + {0, 0, 0, 0, NULL, NULL}, }; #ifdef INET6 @@ -145,554 +140,532 @@ static const struct afd { #endif static int get_name __P((const char *, const struct afd *, - struct addrinfo **, char *, struct addrinfo *, - int)); + struct addrinfo **, char *, struct addrinfo *, + int)); static int get_addr __P((const char *, int, struct addrinfo **, - struct addrinfo *, int)); + struct addrinfo *, int)); static int str_isnumber __P((const char *)); - + +#ifndef HAVE_GAI_STRERROR static const char *const ai_errlist[] = { - "success.", - "address family for hostname not supported.", /* EAI_ADDRFAMILY */ - "temporary failure in name resolution.", /* EAI_AGAIN */ - "invalid value for ai_flags.", /* EAI_BADFLAGS */ - "non-recoverable failure in name resolution.", /* EAI_FAIL */ - "ai_family not supported.", /* EAI_FAMILY */ - "memory allocation failure.", /* EAI_MEMORY */ - "no address associated with hostname.", /* EAI_NODATA */ - "hostname nor servname provided, or not known.",/* EAI_NONAME */ - "servname not supported for ai_socktype.", /* EAI_SERVICE */ - "ai_socktype not supported.", /* EAI_SOCKTYPE */ - "system error returned in errno.", /* EAI_SYSTEM */ - "invalid value for hints.", /* EAI_BADHINTS */ - "resolved protocol is unknown.", /* EAI_PROTOCOL */ - "unknown error.", /* EAI_MAX */ + "success.", + "address family for hostname not supported.", /* EAI_ADDRFAMILY */ + "temporary failure in name resolution.", /* EAI_AGAIN */ + "invalid value for ai_flags.", /* EAI_BADFLAGS */ + "non-recoverable failure in name resolution.", /* EAI_FAIL */ + "ai_family not supported.", /* EAI_FAMILY */ + "memory allocation failure.", /* EAI_MEMORY */ + "no address associated with hostname.", /* EAI_NODATA */ + "hostname nor servname provided, or not known.",/* EAI_NONAME */ + "servname not supported for ai_socktype.", /* EAI_SERVICE */ + "ai_socktype not supported.", /* EAI_SOCKTYPE */ + "system error returned in errno.", /* EAI_SYSTEM */ + "invalid value for hints.", /* EAI_BADHINTS */ + "resolved protocol is unknown.", /* EAI_PROTOCOL */ + "unknown error.", /* EAI_MAX */ }; +#endif #define GET_CANONNAME(ai, str) \ if (pai->ai_flags & AI_CANONNAME) {\ - if (((ai)->ai_canonname = (char *)malloc(strlen(str) + 1)) != NULL) {\ - strcpy((ai)->ai_canonname, (str));\ - } else {\ - error = EAI_MEMORY;\ - goto free;\ - }\ + if (((ai)->ai_canonname = strdup(str)) == NULL) {\ + error = EAI_MEMORY;\ + goto free;\ + }\ } #define GET_AI(ai, afd, addr, port) {\ - char *p;\ - if (((ai) = (struct addrinfo *)malloc(sizeof(struct addrinfo) +\ - ((afd)->a_socklen)))\ - == NULL) {\ - error = EAI_MEMORY;\ - goto free;\ - }\ - memcpy(ai, pai, sizeof(struct addrinfo));\ - (ai)->ai_addr = (struct sockaddr *)((ai) + 1);\ - memset((ai)->ai_addr, 0, (afd)->a_socklen);\ - SET_SA_LEN((ai)->ai_addr, (ai)->ai_addrlen = (afd)->a_socklen);\ - (ai)->ai_addr->sa_family = (ai)->ai_family = (afd)->a_af;\ - ((struct sockinet *)(ai)->ai_addr)->si_port = port;\ - p = (char *)((ai)->ai_addr);\ - memcpy(p + (afd)->a_off, (addr), (afd)->a_addrlen);\ + char *p;\ + if (((ai) = (struct addrinfo *)malloc(sizeof(struct addrinfo) +\ + ((afd)->a_socklen)))\ + == NULL) {\ + error = EAI_MEMORY;\ + goto free;\ + }\ + memcpy((ai), pai, sizeof(struct addrinfo));\ + (ai)->ai_addr = (struct sockaddr *)((ai) + 1);\ + (ai)->ai_family = (afd)->a_af;\ + (ai)->ai_addrlen = (afd)->a_socklen;\ + INIT_SOCKADDR((ai)->ai_addr, (afd)->a_af, (afd)->a_socklen);\ + ((struct sockinet *)(ai)->ai_addr)->si_port = (port);\ + p = (char *)((ai)->ai_addr);\ + memcpy(p + (afd)->a_off, (addr), (afd)->a_addrlen);\ } #define ERR(err) { error = (err); goto bad; } -#if defined __UCLIBC__ +#ifndef HAVE_GAI_STRERROR +#ifdef GAI_STRERROR_CONST const #endif char * -gai_strerror(ecode) - int ecode; +gai_strerror(int ecode) { - if (ecode < 0 || ecode > EAI_MAX) - ecode = EAI_MAX; - return (char *)ai_errlist[ecode]; + if (ecode < 0 || ecode > EAI_MAX) + ecode = EAI_MAX; + return (char *)ai_errlist[ecode]; } +#endif void -freeaddrinfo(ai) - struct addrinfo *ai; +freeaddrinfo(struct addrinfo *ai) { - struct addrinfo *next; - - do { - next = ai->ai_next; - if (ai->ai_canonname) - free(ai->ai_canonname); - /* no need to free(ai->ai_addr) */ - free(ai); - } while ((ai = next) != NULL); + struct addrinfo *next; + + do { + next = ai->ai_next; + free(ai->ai_canonname); + /* no need to free(ai->ai_addr) */ + free(ai); + } while ((ai = next) != NULL); } static int -str_isnumber(p) - const char *p; +str_isnumber(const char *p) { - char *q = (char *)p; - while (*q) { - if (! isdigit(*q)) - return NO; - q++; - } - return YES; + char *q = (char *)p; + while (*q) { + if (! isdigit(*q)) + return NO; + q++; + } + return YES; } #ifndef HAVE_INET_PTON static int -inet_pton(af, hostname, pton) - int af; - const char *hostname; - void *pton; +inet_pton(int af, const char *hostname, void *pton) { - struct in_addr in; + struct in_addr in; #ifdef HAVE_INET_ATON - if (!inet_aton(hostname, &in)) - return 0; + if (!inet_aton(hostname, &in)) + return 0; #else - int d1, d2, d3, d4; - char ch; - - if (sscanf(hostname, "%d.%d.%d.%d%c", &d1, &d2, &d3, &d4, &ch) == 4 && - 0 <= d1 && d1 <= 255 && 0 <= d2 && d2 <= 255 && - 0 <= d3 && d3 <= 255 && 0 <= d4 && d4 <= 255) { - in.s_addr = htonl( - ((long) d1 << 24) | ((long) d2 << 16) | - ((long) d3 << 8) | ((long) d4 << 0)); - } - else { - return 0; - } -#endif - memcpy(pton, &in, sizeof(in)); - return 1; + int d1, d2, d3, d4; + char ch; + + if (sscanf(hostname, "%d.%d.%d.%d%c", &d1, &d2, &d3, &d4, &ch) == 4 && + 0 <= d1 && d1 <= 255 && 0 <= d2 && d2 <= 255 && + 0 <= d3 && d3 <= 255 && 0 <= d4 && d4 <= 255) { + in.s_addr = htonl( + ((long) d1 << 24) | ((long) d2 << 16) | + ((long) d3 << 8) | ((long) d4 << 0)); + } + else { + return 0; + } +#endif + memcpy(pton, &in, sizeof(in)); + return 1; } #endif int -getaddrinfo(hostname, servname, hints, res) - const char *hostname, *servname; - const struct addrinfo *hints; - struct addrinfo **res; +getaddrinfo(const char *hostname, const char *servname, const struct addrinfo *hints, struct addrinfo **res) { - struct addrinfo sentinel; - struct addrinfo *top = NULL; - struct addrinfo *cur; - int i, error = 0; - char pton[PTON_MAX]; - struct addrinfo ai; - struct addrinfo *pai; - u_short port; + struct addrinfo sentinel; + struct addrinfo *top = NULL; + struct addrinfo *cur; + int i, error = 0; + char pton[PTON_MAX]; + struct addrinfo ai; + struct addrinfo *pai; + u_short port; #ifdef FAITH - static int firsttime = 1; - - if (firsttime) { - /* translator hack */ - { - char *q = getenv("GAI"); - if (q && inet_pton(AF_INET6, q, &faith_prefix) == 1) - translate = YES; - } - firsttime = 0; - } -#endif - - /* initialize file static vars */ - sentinel.ai_next = NULL; - cur = &sentinel; - pai = &ai; - pai->ai_flags = 0; - pai->ai_family = PF_UNSPEC; - pai->ai_socktype = ANY; - pai->ai_protocol = ANY; - pai->ai_addrlen = 0; - pai->ai_canonname = NULL; - pai->ai_addr = NULL; - pai->ai_next = NULL; - port = ANY; - - if (hostname == NULL && servname == NULL) - return EAI_NONAME; - if (hints) { - /* error check for hints */ - if (hints->ai_addrlen || hints->ai_canonname || - hints->ai_addr || hints->ai_next) - ERR(EAI_BADHINTS); /* xxx */ - if (hints->ai_flags & ~AI_MASK) - ERR(EAI_BADFLAGS); - switch (hints->ai_family) { - case PF_UNSPEC: - case PF_INET: + static int firsttime = 1; + + if (firsttime) { + /* translator hack */ + { + char *q = getenv("GAI"); + if (q && inet_pton(AF_INET6, q, &faith_prefix) == 1) + translate = YES; + } + firsttime = 0; + } +#endif + + /* initialize file static vars */ + sentinel.ai_next = NULL; + cur = &sentinel; + pai = &ai; + pai->ai_flags = 0; + pai->ai_family = PF_UNSPEC; + pai->ai_socktype = ANY; + pai->ai_protocol = ANY; + pai->ai_addrlen = 0; + pai->ai_canonname = NULL; + pai->ai_addr = NULL; + pai->ai_next = NULL; + port = ANY; + + if (hostname == NULL && servname == NULL) + return EAI_NONAME; + if (hints) { + /* error check for hints */ + if (hints->ai_addrlen || hints->ai_canonname || + hints->ai_addr || hints->ai_next) + ERR(EAI_BADHINTS); /* xxx */ + if (hints->ai_flags & ~AI_MASK) + ERR(EAI_BADFLAGS); + switch (hints->ai_family) { + case PF_UNSPEC: + case PF_INET: #ifdef INET6 - case PF_INET6: -#endif - break; - default: - ERR(EAI_FAMILY); - } - memcpy(pai, hints, sizeof(*pai)); - switch (pai->ai_socktype) { - case ANY: - switch (pai->ai_protocol) { - case ANY: - break; - case IPPROTO_UDP: - pai->ai_socktype = SOCK_DGRAM; - break; - case IPPROTO_TCP: - pai->ai_socktype = SOCK_STREAM; - break; - default: + case PF_INET6: +#endif + break; + default: + ERR(EAI_FAMILY); + } + memcpy(pai, hints, sizeof(*pai)); + switch (pai->ai_socktype) { + case ANY: + switch (pai->ai_protocol) { + case ANY: + break; + case IPPROTO_UDP: + pai->ai_socktype = SOCK_DGRAM; + break; + case IPPROTO_TCP: + pai->ai_socktype = SOCK_STREAM; + break; + default: #if defined(SOCK_RAW) - pai->ai_socktype = SOCK_RAW; + pai->ai_socktype = SOCK_RAW; #endif - break; - } - break; + break; + } + break; #if defined(SOCK_RAW) - case SOCK_RAW: - break; -#endif - case SOCK_DGRAM: - if (pai->ai_protocol != IPPROTO_UDP && - pai->ai_protocol != ANY) - ERR(EAI_BADHINTS); /*xxx*/ - pai->ai_protocol = IPPROTO_UDP; - break; - case SOCK_STREAM: - if (pai->ai_protocol != IPPROTO_TCP && - pai->ai_protocol != ANY) - ERR(EAI_BADHINTS); /*xxx*/ - pai->ai_protocol = IPPROTO_TCP; - break; - default: - ERR(EAI_SOCKTYPE); - break; - } - } - - /* - * service port - */ - if (servname) { - if (str_isnumber(servname)) { - if (pai->ai_socktype == ANY) { - /* caller accept *ANY* socktype */ - pai->ai_socktype = SOCK_DGRAM; - pai->ai_protocol = IPPROTO_UDP; - } - port = htons((unsigned short)atoi(servname)); - } else { - struct servent *sp; - char *proto; - - proto = NULL; - switch (pai->ai_socktype) { - case ANY: - proto = NULL; - break; - case SOCK_DGRAM: - proto = "udp"; - break; - case SOCK_STREAM: - proto = "tcp"; - break; - default: - fprintf(stderr, "panic!\n"); - break; - } - if ((sp = getservbyname((char*)servname, proto)) == NULL) - ERR(EAI_SERVICE); - port = sp->s_port; - if (pai->ai_socktype == ANY) - if (strcmp(sp->s_proto, "udp") == 0) { - pai->ai_socktype = SOCK_DGRAM; - pai->ai_protocol = IPPROTO_UDP; - } else if (strcmp(sp->s_proto, "tcp") == 0) { - pai->ai_socktype = SOCK_STREAM; - pai->ai_protocol = IPPROTO_TCP; - } else - ERR(EAI_PROTOCOL); /*xxx*/ - } - } - - /* - * hostname == NULL. - * passive socket -> anyaddr (0.0.0.0 or ::) - * non-passive socket -> localhost (127.0.0.1 or ::1) - */ - if (hostname == NULL) { - const struct afd *afd; - int s; - - for (afd = &afdl[0]; afd->a_af; afd++) { - if (!(pai->ai_family == PF_UNSPEC - || pai->ai_family == afd->a_af)) { - continue; - } - - /* - * filter out AFs that are not supported by the kernel - * XXX errno? - */ - s = socket(afd->a_af, SOCK_DGRAM, 0); - if (s < 0) - continue; -#if defined(HAVE_CLOSESOCKET) - closesocket(s); -#else - close(s); -#endif - - if (pai->ai_flags & AI_PASSIVE) { - GET_AI(cur->ai_next, afd, afd->a_addrany, port); - /* xxx meaningless? - * GET_CANONNAME(cur->ai_next, "anyaddr"); - */ - } else { - GET_AI(cur->ai_next, afd, afd->a_loopback, - port); - /* xxx meaningless? - * GET_CANONNAME(cur->ai_next, "localhost"); - */ - } - cur = cur->ai_next; - } - top = sentinel.ai_next; - if (top) - goto good; - else - ERR(EAI_FAMILY); - } - - /* hostname as numeric name */ - for (i = 0; afdl[i].a_af; i++) { - if (inet_pton(afdl[i].a_af, hostname, pton)) { - u_long v4a; + case SOCK_RAW: + break; +#endif + case SOCK_DGRAM: + if (pai->ai_protocol != IPPROTO_UDP && + pai->ai_protocol != ANY) + ERR(EAI_BADHINTS); /*xxx*/ + pai->ai_protocol = IPPROTO_UDP; + break; + case SOCK_STREAM: + if (pai->ai_protocol != IPPROTO_TCP && + pai->ai_protocol != ANY) + ERR(EAI_BADHINTS); /*xxx*/ + pai->ai_protocol = IPPROTO_TCP; + break; + default: + ERR(EAI_SOCKTYPE); + break; + } + } + + /* + * service port + */ + if (servname) { + if (str_isnumber(servname)) { + if (pai->ai_socktype == ANY) { + /* caller accept *ANY* socktype */ + pai->ai_socktype = SOCK_DGRAM; + pai->ai_protocol = IPPROTO_UDP; + } + port = htons((unsigned short)atoi(servname)); + } else if (pai->ai_flags & AI_NUMERICSERV) { + ERR(EAI_NONAME); + } else { + struct servent *sp; + const char *proto; + + proto = NULL; + switch (pai->ai_socktype) { + case ANY: + proto = NULL; + break; + case SOCK_DGRAM: + proto = "udp"; + break; + case SOCK_STREAM: + proto = "tcp"; + break; + default: + fprintf(stderr, "panic!\n"); + break; + } + if ((sp = getservbyname((char*)servname, proto)) == NULL) + ERR(EAI_SERVICE); + port = sp->s_port; + if (pai->ai_socktype == ANY) + if (strcmp(sp->s_proto, "udp") == 0) { + pai->ai_socktype = SOCK_DGRAM; + pai->ai_protocol = IPPROTO_UDP; + } else if (strcmp(sp->s_proto, "tcp") == 0) { + pai->ai_socktype = SOCK_STREAM; + pai->ai_protocol = IPPROTO_TCP; + } else + ERR(EAI_PROTOCOL); /*xxx*/ + } + } + + /* + * hostname == NULL. + * passive socket -> anyaddr (0.0.0.0 or ::) + * non-passive socket -> localhost (127.0.0.1 or ::1) + */ + if (hostname == NULL) { + const struct afd *afd; + int s; + + for (afd = &afdl[0]; afd->a_af; afd++) { + if (!(pai->ai_family == PF_UNSPEC + || pai->ai_family == afd->a_af)) { + continue; + } + + /* + * filter out AFs that are not supported by the kernel + * XXX errno? + */ + s = socket(afd->a_af, SOCK_DGRAM, 0); + if (s < 0) + continue; + + close(s); + + if (pai->ai_flags & AI_PASSIVE) { + GET_AI(cur->ai_next, afd, afd->a_addrany, port); + /* xxx meaningless? + * GET_CANONNAME(cur->ai_next, "anyaddr"); + */ + } else { + GET_AI(cur->ai_next, afd, afd->a_loopback, + port); + /* xxx meaningless? + * GET_CANONNAME(cur->ai_next, "localhost"); + */ + } + cur = cur->ai_next; + } + top = sentinel.ai_next; + if (top) + goto good; + else + ERR(EAI_FAMILY); + } + + /* hostname as numeric name */ + for (i = 0; afdl[i].a_af; i++) { + if (inet_pton(afdl[i].a_af, hostname, pton)) { + u_long v4a; #ifdef INET6 - u_char pfx; -#endif - - switch (afdl[i].a_af) { - case AF_INET: - v4a = ((struct in_addr *)pton)->s_addr; - if (IN_MULTICAST(v4a) || IN_EXPERIMENTAL(v4a)) - pai->ai_flags &= ~AI_CANONNAME; - v4a >>= IN_CLASSA_NSHIFT; - if (v4a == 0 || v4a == IN_LOOPBACKNET) - pai->ai_flags &= ~AI_CANONNAME; - break; + u_char pfx; +#endif + + switch (afdl[i].a_af) { + case AF_INET: + v4a = ((struct in_addr *)pton)->s_addr; + if (IN_MULTICAST(v4a) || IN_EXPERIMENTAL(v4a)) + pai->ai_flags &= ~AI_CANONNAME; + v4a >>= IN_CLASSA_NSHIFT; + if (v4a == 0 || v4a == IN_LOOPBACKNET) + pai->ai_flags &= ~AI_CANONNAME; + break; #ifdef INET6 - case AF_INET6: -#ifdef HAVE_ADDR8 - pfx = ((struct in6_addr *)pton)->s6_addr8[0]; -#else - pfx = ((struct in6_addr *)pton)->s6_addr[0]; -#endif - if (pfx == 0 || pfx == 0xfe || pfx == 0xff) - pai->ai_flags &= ~AI_CANONNAME; - break; -#endif - } - - if (pai->ai_family == afdl[i].a_af || - pai->ai_family == PF_UNSPEC) { - if (! (pai->ai_flags & AI_CANONNAME)) { - GET_AI(top, &afdl[i], pton, port); - goto good; - } - /* - * if AI_CANONNAME and if reverse lookup - * fail, return ai anyway to pacify - * calling application. - * - * XXX getaddrinfo() is a name->address - * translation function, and it looks strange - * that we do addr->name translation here. - */ - get_name(pton, &afdl[i], &top, pton, pai, port); - goto good; - } else - ERR(EAI_FAMILY); /*xxx*/ - } - } - - if (pai->ai_flags & AI_NUMERICHOST) - ERR(EAI_NONAME); - - /* hostname as alphabetical name */ - error = get_addr(hostname, pai->ai_family, &top, pai, port); - if (error == 0) { - if (top) { + case AF_INET6: + pfx = ((struct in6_addr *)pton)->s6_addr[0]; + if (pfx == 0 || pfx == 0xfe || pfx == 0xff) + pai->ai_flags &= ~AI_CANONNAME; + break; +#endif + } + + if (pai->ai_family == afdl[i].a_af || + pai->ai_family == PF_UNSPEC) { + if (! (pai->ai_flags & AI_CANONNAME)) { + GET_AI(top, &afdl[i], pton, port); + goto good; + } + /* + * if AI_CANONNAME and if reverse lookup + * fail, return ai anyway to pacify + * calling application. + * + * XXX getaddrinfo() is a name->address + * translation function, and it looks strange + * that we do addr->name translation here. + */ + get_name(pton, &afdl[i], &top, pton, pai, port); + goto good; + } else + ERR(EAI_FAMILY); /*xxx*/ + } + } + + if (pai->ai_flags & AI_NUMERICHOST) + ERR(EAI_NONAME); + + /* hostname as alphabetical name */ + error = get_addr(hostname, pai->ai_family, &top, pai, port); + if (error == 0) { + if (top) { good: - *res = top; - return SUCCESS; - } else - error = EAI_FAIL; - } + *res = top; + return SUCCESS; + } else + error = EAI_FAIL; + } free: - if (top) - freeaddrinfo(top); + if (top) + freeaddrinfo(top); bad: - *res = NULL; - return error; + *res = NULL; + return error; } static int -get_name(addr, afd, res, numaddr, pai, port0) - const char *addr; - const struct afd *afd; - struct addrinfo **res; - char *numaddr; - struct addrinfo *pai; - int port0; +get_name(const char *addr, const struct afd *afd, struct addrinfo **res, char *numaddr, struct addrinfo *pai, int port0) { - u_short port = port0 & 0xffff; - struct hostent *hp; - struct addrinfo *cur; - int error = 0; + u_short port = port0 & 0xffff; + struct hostent *hp; + struct addrinfo *cur; + int error = 0; #ifdef INET6 - int h_error; + int h_error; #endif #ifdef INET6 - hp = getipnodebyaddr(addr, afd->a_addrlen, afd->a_af, &h_error); + hp = getipnodebyaddr(addr, afd->a_addrlen, afd->a_af, &h_error); #else - hp = gethostbyaddr((char*)addr, afd->a_addrlen, AF_INET); -#endif - if (hp && hp->h_name && hp->h_name[0] && hp->h_addr_list[0]) { - GET_AI(cur, afd, hp->h_addr_list[0], port); - GET_CANONNAME(cur, hp->h_name); - } else - GET_AI(cur, afd, numaddr, port); - + hp = gethostbyaddr((char*)addr, afd->a_addrlen, AF_INET); +#endif + if (hp && hp->h_name && hp->h_name[0] && hp->h_addr_list[0]) { + GET_AI(cur, afd, hp->h_addr_list[0], port); + GET_CANONNAME(cur, hp->h_name); + } else + GET_AI(cur, afd, numaddr, port); + #ifdef INET6 - if (hp) - freehostent(hp); + if (hp) + freehostent(hp); #endif - *res = cur; - return SUCCESS; + *res = cur; + return SUCCESS; free: - if (cur) - freeaddrinfo(cur); + if (cur) + freeaddrinfo(cur); #ifdef INET6 - if (hp) - freehostent(hp); + if (hp) + freehostent(hp); #endif /* bad: */ - *res = NULL; - return error; + *res = NULL; + return error; } static int -get_addr(hostname, af, res, pai, port0) - const char *hostname; - int af; - struct addrinfo **res; - struct addrinfo *pai; - int port0; +get_addr(const char *hostname, int af, struct addrinfo **res, struct addrinfo *pai, int port0) { - u_short port = port0 & 0xffff; - struct addrinfo sentinel; - struct hostent *hp; - struct addrinfo *top, *cur; - const struct afd *afd; - int i, error = 0, h_error; - char *ap; - - top = NULL; - sentinel.ai_next = NULL; - cur = &sentinel; + u_short port = port0 & 0xffff; + struct addrinfo sentinel; + struct hostent *hp; + struct addrinfo *top, *cur; + const struct afd *afd; + int i, error = 0, h_error; + char *ap; + + top = NULL; + sentinel.ai_next = NULL; + cur = &sentinel; #ifdef INET6 - if (af == AF_UNSPEC) { - hp = getipnodebyname(hostname, AF_INET6, - AI_ADDRCONFIG|AI_ALL|AI_V4MAPPED, &h_error); - } else - hp = getipnodebyname(hostname, af, AI_ADDRCONFIG, &h_error); + if (af == AF_UNSPEC) { + hp = getipnodebyname(hostname, AF_INET6, + AI_ADDRCONFIG|AI_ALL|AI_V4MAPPED, &h_error); + } else + hp = getipnodebyname(hostname, af, AI_ADDRCONFIG, &h_error); #else - hp = gethostbyname((char*)hostname); - h_error = h_errno; -#endif - if (hp == NULL) { - switch (h_error) { - case HOST_NOT_FOUND: - case NO_DATA: - error = EAI_NODATA; - break; - case TRY_AGAIN: - error = EAI_AGAIN; - break; - case NO_RECOVERY: - default: - error = EAI_FAIL; - break; - } - goto bad; - } - - if ((hp->h_name == NULL) || (hp->h_name[0] == 0) || - (hp->h_addr_list[0] == NULL)) - ERR(EAI_FAIL); - - for (i = 0; (ap = hp->h_addr_list[i]) != NULL; i++) { - switch (af) { + if (strlen(hostname) >= NI_MAXHOST) ERR(EAI_NODATA); + hp = gethostbyname((char*)hostname); + h_error = h_errno; +#endif + if (hp == NULL) { + switch (h_error) { + case HOST_NOT_FOUND: + case NO_DATA: + error = EAI_NODATA; + break; + case TRY_AGAIN: + error = EAI_AGAIN; + break; + case NO_RECOVERY: + default: + error = EAI_FAIL; + break; + } + goto bad; + } + + if ((hp->h_name == NULL) || (hp->h_name[0] == 0) || + (hp->h_addr_list[0] == NULL)) + ERR(EAI_FAIL); + + for (i = 0; (ap = hp->h_addr_list[i]) != NULL; i++) { + switch (af) { #ifdef INET6 - case AF_INET6: - afd = &afdl[N_INET6]; - break; + case AF_INET6: + afd = &afdl[N_INET6]; + break; #endif #ifndef INET6 - default: /* AF_UNSPEC */ + default: /* AF_UNSPEC */ #endif - case AF_INET: - afd = &afdl[N_INET]; - break; + case AF_INET: + afd = &afdl[N_INET]; + break; #ifdef INET6 - default: /* AF_UNSPEC */ - if (IN6_IS_ADDR_V4MAPPED((struct in6_addr *)ap)) { - ap += sizeof(struct in6_addr) - - sizeof(struct in_addr); - afd = &afdl[N_INET]; - } else - afd = &afdl[N_INET6]; - break; -#endif - } + default: /* AF_UNSPEC */ + if (IN6_IS_ADDR_V4MAPPED((struct in6_addr *)ap)) { + ap += sizeof(struct in6_addr) - + sizeof(struct in_addr); + afd = &afdl[N_INET]; + } else + afd = &afdl[N_INET6]; + break; +#endif + } #ifdef FAITH - if (translate && afd->a_af == AF_INET) { - struct in6_addr *in6; - - GET_AI(cur->ai_next, &afdl[N_INET6], ap, port); - in6 = &((struct sockaddr_in6 *)cur->ai_next->ai_addr)->sin6_addr; - memcpy(&in6->s6_addr32[0], &faith_prefix, - sizeof(struct in6_addr) - sizeof(struct in_addr)); - memcpy(&in6->s6_addr32[3], ap, sizeof(struct in_addr)); - } else + if (translate && afd->a_af == AF_INET) { + struct in6_addr *in6; + + GET_AI(cur->ai_next, &afdl[N_INET6], ap, port); + in6 = &((struct sockaddr_in6 *)cur->ai_next->ai_addr)->sin6_addr; + memcpy(&in6->s6_addr, &faith_prefix, + sizeof(struct in6_addr) - sizeof(struct in_addr)); + memcpy(&in6->s6_addr + sizeof(struct in_addr), ap, + sizeof(struct in_addr)); + } else #endif /* FAITH */ - GET_AI(cur->ai_next, afd, ap, port); - if (cur == &sentinel) { - top = cur->ai_next; - GET_CANONNAME(top, hp->h_name); - } - cur = cur->ai_next; - } + GET_AI(cur->ai_next, afd, ap, port); + if (cur == &sentinel) { + top = cur->ai_next; + GET_CANONNAME(top, hp->h_name); + } + cur = cur->ai_next; + } #ifdef INET6 - freehostent(hp); + freehostent(hp); #endif - *res = top; - return SUCCESS; + *res = top; + return SUCCESS; free: - if (top) - freeaddrinfo(top); + if (top) + freeaddrinfo(top); #ifdef INET6 - if (hp) - freehostent(hp); + if (hp) + freehostent(hp); #endif bad: - *res = NULL; - return error; + *res = NULL; + return error; } diff --git a/ext/socket/getnameinfo.c b/ext/socket/getnameinfo.c index 66f7e8818a..98da8c1647 100644 --- a/ext/socket/getnameinfo.c +++ b/ext/socket/getnameinfo.c @@ -1,7 +1,7 @@ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -13,7 +13,7 @@ * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. - * + * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE @@ -34,15 +34,14 @@ * but INRIA implementation returns EAI_xxx defined for getaddrinfo(). */ -#include "config.h" +#include "ruby/config.h" +#ifdef RUBY_EXTCONF_H +#include RUBY_EXTCONF_H +#endif #include <stdio.h> #include <sys/types.h> #ifndef _WIN32 -#if defined(__BEOS__) -# include <net/socket.h> -#else -# include <sys/socket.h> -#endif +#include <sys/socket.h> #include <netinet/in.h> #if defined(HAVE_ARPA_INET_H) #include <arpa/inet.h> @@ -57,6 +56,7 @@ #endif #ifdef _WIN32 #include <winsock2.h> +#include <ws2tcpip.h> #define snprintf _snprintf #endif @@ -67,8 +67,13 @@ #include <socks.h> #endif +#ifndef HAVE_TYPE_SOCKLEN_T +typedef int socklen_t; +#endif + #include "addrinfo.h" #include "sockport.h" +#include "rubysocket.h" #define SUCCESS 0 #define ANY 0 @@ -76,30 +81,30 @@ #define NO 0 struct sockinet { - u_char si_len; - u_char si_family; - u_short si_port; + u_char si_len; + u_char si_family; + u_short si_port; }; static struct afd { - int a_af; - int a_addrlen; - int a_socklen; - int a_off; + int a_af; + int a_addrlen; + int a_socklen; + int a_off; } afdl [] = { #ifdef INET6 #define N_INET6 0 - {PF_INET6, sizeof(struct in6_addr), - sizeof(struct sockaddr_in6), - offsetof(struct sockaddr_in6, sin6_addr)}, + {PF_INET6, sizeof(struct in6_addr), + sizeof(struct sockaddr_in6), + offsetof(struct sockaddr_in6, sin6_addr)}, #define N_INET 1 #else #define N_INET 0 #endif - {PF_INET, sizeof(struct in_addr), - sizeof(struct sockaddr_in), - offsetof(struct sockaddr_in, sin_addr)}, - {0, 0, 0, 0}, + {PF_INET, sizeof(struct in_addr), + sizeof(struct sockaddr_in), + offsetof(struct sockaddr_in, sin_addr)}, + {0, 0, 0, 0}, }; #define ENI_NOSOCKET 0 @@ -110,156 +115,121 @@ static struct afd { #define ENI_FAMILY 5 #define ENI_SALEN 6 -#ifndef HAVE_INET_NTOP -static const char * -inet_ntop(af, addr, numaddr, numaddr_len) - int af; - const void *addr; - char *numaddr; - size_t numaddr_len; -{ -#ifdef HAVE_INET_NTOA - struct in_addr in; - memcpy(&in.s_addr, addr, sizeof(in.s_addr)); - snprintf(numaddr, numaddr_len, "%s", inet_ntoa(in)); -#else - unsigned long x = ntohl(*(unsigned long*)addr); - snprintf(numaddr, numaddr_len, "%d.%d.%d.%d", - (int) (x>>24) & 0xff, (int) (x>>16) & 0xff, - (int) (x>> 8) & 0xff, (int) (x>> 0) & 0xff); -#endif - return numaddr; -} -#endif - int -getnameinfo(sa, salen, host, hostlen, serv, servlen, flags) - const struct sockaddr *sa; - size_t salen; - char *host; - size_t hostlen; - char *serv; - size_t servlen; - int flags; +getnameinfo(const struct sockaddr *sa, socklen_t salen, char *host, socklen_t hostlen, char *serv, socklen_t servlen, int flags) { - struct afd *afd; - struct servent *sp; - struct hostent *hp; - u_short port; - int family, len, i; - char *addr, *p; - u_long v4a; + struct afd *afd; + struct hostent *hp; + u_short port; + int family, len, i; + char *addr, *p; + u_long v4a; #ifdef INET6 - u_char pfx; + u_char pfx; #endif - int h_error; - char numserv[512]; - char numaddr[512]; + int h_error; + char numserv[512]; + char numaddr[512]; - if (sa == NULL) - return ENI_NOSOCKET; + if (sa == NULL) + return ENI_NOSOCKET; - len = SA_LEN(sa); - if (len != salen) return ENI_SALEN; - - family = sa->sa_family; - for (i = 0; afdl[i].a_af; i++) - if (afdl[i].a_af == family) { - afd = &afdl[i]; - goto found; - } - return ENI_FAMILY; - - found: - if (len != afd->a_socklen) return ENI_SALEN; - - port = ((struct sockinet *)sa)->si_port; /* network byte order */ - addr = (char *)sa + afd->a_off; + if (!VALIDATE_SOCKLEN(sa, salen)) return ENI_SALEN; + len = salen; + + family = sa->sa_family; + for (i = 0; afdl[i].a_af; i++) + if (afdl[i].a_af == family) { + afd = &afdl[i]; + goto found; + } + return ENI_FAMILY; - if (serv == NULL || servlen == 0) { - /* what we should do? */ - } else if (flags & NI_NUMERICSERV) { - snprintf(numserv, sizeof(numserv), "%d", ntohs(port)); - if (strlen(numserv) + 1 > servlen) - return ENI_MEMORY; - strcpy(serv, numserv); - } else { + found: + if (len != afd->a_socklen) return ENI_SALEN; + + port = ((struct sockinet *)sa)->si_port; /* network byte order */ + addr = (char *)sa + afd->a_off; + + if (serv == NULL || servlen == 0) { + /* what we should do? */ + } else if (flags & NI_NUMERICSERV) { + snprintf(numserv, sizeof(numserv), "%d", ntohs(port)); + if (strlcpy(serv, numserv, servlen) >= servlen) + return ENI_MEMORY; + } else { #if defined(HAVE_GETSERVBYPORT) - sp = getservbyport(port, (flags & NI_DGRAM) ? "udp" : "tcp"); - if (sp) { - if (strlen(sp->s_name) + 1 > servlen) - return ENI_MEMORY; - strcpy(serv, sp->s_name); - } else - return ENI_NOSERVNAME; + struct servent *sp = getservbyport(port, (flags & NI_DGRAM) ? "udp" : "tcp"); + if (sp) { + if (strlcpy(serv, sp->s_name, servlen) >= servlen) + return ENI_MEMORY; + } else + return ENI_NOSERVNAME; #else - return ENI_NOSERVNAME; -#endif - } - - switch (sa->sa_family) { - case AF_INET: - v4a = ntohl(((struct sockaddr_in *)sa)->sin_addr.s_addr); - if (IN_MULTICAST(v4a) || IN_EXPERIMENTAL(v4a)) - flags |= NI_NUMERICHOST; - v4a >>= IN_CLASSA_NSHIFT; - if (v4a == 0) - flags |= NI_NUMERICHOST; - break; + return ENI_NOSERVNAME; +#endif + } + + switch (sa->sa_family) { + case AF_INET: + v4a = ntohl(((struct sockaddr_in *)sa)->sin_addr.s_addr); + if (IN_MULTICAST(v4a) || IN_EXPERIMENTAL(v4a)) + flags |= NI_NUMERICHOST; + v4a >>= IN_CLASSA_NSHIFT; + if (v4a == 0) + flags |= NI_NUMERICHOST; + break; #ifdef INET6 - case AF_INET6: + case AF_INET6: #ifdef HAVE_ADDR8 - pfx = ((struct sockaddr_in6 *)sa)->sin6_addr.s6_addr8[0]; + pfx = ((struct sockaddr_in6 *)sa)->sin6_addr.s6_addr8[0]; #else - pfx = ((struct sockaddr_in6 *)sa)->sin6_addr.s6_addr[0]; -#endif - if (pfx == 0 || pfx == 0xfe || pfx == 0xff) - flags |= NI_NUMERICHOST; - break; -#endif - } - if (host == NULL || hostlen == 0) { - /* what should we do? */ - } else if (flags & NI_NUMERICHOST) { - if (inet_ntop(afd->a_af, addr, numaddr, sizeof(numaddr)) - == NULL) - return ENI_SYSTEM; - if (strlen(numaddr) > hostlen) - return ENI_MEMORY; - strcpy(host, numaddr); - } else { + pfx = ((struct sockaddr_in6 *)sa)->sin6_addr.s6_addr[0]; +#endif + if (pfx == 0 || pfx == 0xfe || pfx == 0xff) + flags |= NI_NUMERICHOST; + break; +#endif + } + if (host == NULL || hostlen == 0) { + /* what should we do? */ + } else if (flags & NI_NUMERICHOST) { + if (inet_ntop(afd->a_af, addr, numaddr, sizeof(numaddr)) + == NULL) + return ENI_SYSTEM; + if (strlcpy(host, numaddr, hostlen) >= hostlen) + return ENI_MEMORY; + } else { #ifdef INET6 - hp = getipnodebyaddr(addr, afd->a_addrlen, afd->a_af, &h_error); + hp = getipnodebyaddr(addr, afd->a_addrlen, afd->a_af, &h_error); #else - hp = gethostbyaddr(addr, afd->a_addrlen, afd->a_af); - h_error = h_errno; + hp = gethostbyaddr(addr, afd->a_addrlen, afd->a_af); + h_error = h_errno; #endif - if (hp) { - if (flags & NI_NOFQDN) { - p = strchr(hp->h_name, '.'); - if (p) *p = '\0'; - } - if (strlen(hp->h_name) + 1 > hostlen) { + if (hp) { + if (flags & NI_NOFQDN) { + p = strchr(hp->h_name, '.'); + if (p) *p = '\0'; + } + if (strlcpy(host, hp->h_name, hostlen) >= hostlen) { #ifdef INET6 - freehostent(hp); + freehostent(hp); #endif - return ENI_MEMORY; - } - strcpy(host, hp->h_name); + return ENI_MEMORY; + } #ifdef INET6 - freehostent(hp); -#endif - } else { - if (flags & NI_NAMEREQD) - return ENI_NOHOSTNAME; - if (inet_ntop(afd->a_af, addr, numaddr, sizeof(numaddr)) - == NULL) - return ENI_NOHOSTNAME; - if (strlen(numaddr) > hostlen) - return ENI_MEMORY; - strcpy(host, numaddr); - } - } - return SUCCESS; + freehostent(hp); +#endif + } else { + if (flags & NI_NAMEREQD) + return ENI_NOHOSTNAME; + if (inet_ntop(afd->a_af, addr, numaddr, sizeof(numaddr)) + == NULL) + return ENI_NOHOSTNAME; + if (strlcpy(host, numaddr, hostlen) >= hostlen) + return ENI_MEMORY; + } + } + return SUCCESS; } diff --git a/ext/socket/ifaddr.c b/ext/socket/ifaddr.c new file mode 100644 index 0000000000..3596c40a11 --- /dev/null +++ b/ext/socket/ifaddr.c @@ -0,0 +1,480 @@ +#include "rubysocket.h" + +#ifdef HAVE_GETIFADDRS + +/* + * ifa_flags is usually unsigned int. + * However it is uint64_t on SunOS 5.11 (OpenIndiana). + */ +#ifdef HAVE_LONG_LONG +typedef unsigned LONG_LONG ifa_flags_t; +#define PRIxIFAFLAGS PRI_LL_PREFIX"x" +#define IFAFLAGS2NUM(flags) ULL2NUM(flags) +#else +typedef unsigned int ifa_flags_t; +#define PRIxIFAFLAGS "x" +#define IFAFLAGS2NUM(flags) UINT2NUM(flags) +#endif + +VALUE rb_cSockIfaddr; + +typedef struct rb_ifaddr_tag rb_ifaddr_t; +typedef struct rb_ifaddr_root_tag rb_ifaddr_root_t; + +struct rb_ifaddr_tag { + int ord; + struct ifaddrs *ifaddr; +}; + +struct rb_ifaddr_root_tag { + int refcount; + int numifaddrs; + rb_ifaddr_t ary[1]; +}; + +static rb_ifaddr_root_t * +get_root(const rb_ifaddr_t *ifaddr) +{ + return (rb_ifaddr_root_t *)((char *)&ifaddr[-ifaddr->ord] - + offsetof(rb_ifaddr_root_t, ary)); +} + +static void +ifaddr_free(void *ptr) +{ + rb_ifaddr_t *ifaddr = ptr; + rb_ifaddr_root_t *root = get_root(ifaddr); + root->refcount--; + if (root->refcount == 0) { + freeifaddrs(root->ary[0].ifaddr); + xfree(root); + } +} + +static size_t +ifaddr_memsize(const void *ptr) +{ + size_t size = offsetof(rb_ifaddr_root_t, ary); + const rb_ifaddr_t *ifaddr; + ifaddr = ptr; + if (ifaddr->ord == 0) size = sizeof(rb_ifaddr_root_t); + size += sizeof(struct ifaddrs); + return size; +} + +static const rb_data_type_t ifaddr_type = { + "socket/ifaddr", + {0, ifaddr_free, ifaddr_memsize,}, +}; + +static inline rb_ifaddr_t * +check_ifaddr(VALUE self) +{ + return rb_check_typeddata(self, &ifaddr_type); +} + +static rb_ifaddr_t * +get_ifaddr(VALUE self) +{ + rb_ifaddr_t *rifaddr = check_ifaddr(self); + + if (!rifaddr) { + rb_raise(rb_eTypeError, "uninitialized ifaddr"); + } + return rifaddr; +} + +static struct ifaddrs * +get_ifaddrs(VALUE self) +{ + return get_ifaddr(self)->ifaddr; +} + +static VALUE +rsock_getifaddrs(void) +{ + int ret; + int numifaddrs, i; + struct ifaddrs *ifaddrs, *ifa; + rb_ifaddr_root_t *root; + VALUE result, addr; + + ret = getifaddrs(&ifaddrs); + if (ret == -1) + rb_sys_fail("getifaddrs"); + + if (!ifaddrs) { + return rb_ary_new(); + } + + numifaddrs = 0; + for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) + numifaddrs++; + + addr = TypedData_Wrap_Struct(rb_cSockIfaddr, &ifaddr_type, 0); + root = xmalloc(offsetof(rb_ifaddr_root_t, ary) + numifaddrs * sizeof(rb_ifaddr_t)); + root->refcount = 0; + root->numifaddrs = numifaddrs; + + ifa = ifaddrs; + for (i = 0; i < numifaddrs; i++) { + root->ary[i].ord = i; + root->ary[i].ifaddr = ifa; + ifa = ifa->ifa_next; + } + RTYPEDDATA_DATA(addr) = &root->ary[0]; + root->refcount++; + + result = rb_ary_new2(numifaddrs); + rb_ary_push(result, addr); + for (i = 1; i < numifaddrs; i++) { + addr = TypedData_Wrap_Struct(rb_cSockIfaddr, &ifaddr_type, &root->ary[i]); + root->refcount++; + rb_ary_push(result, addr); + } + + return result; +} + +/* + * call-seq: + * ifaddr.name => string + * + * Returns the interface name of _ifaddr_. + */ + +static VALUE +ifaddr_name(VALUE self) +{ + struct ifaddrs *ifa = get_ifaddrs(self); + return rb_str_new_cstr(ifa->ifa_name); +} + +#ifdef HAVE_IF_NAMETOINDEX +/* + * call-seq: + * ifaddr.ifindex => integer + * + * Returns the interface index of _ifaddr_. + */ + +static VALUE +ifaddr_ifindex(VALUE self) +{ + struct ifaddrs *ifa = get_ifaddrs(self); + unsigned int ifindex = if_nametoindex(ifa->ifa_name); + if (ifindex == 0) { + rb_raise(rb_eArgError, "invalid interface name: %s", ifa->ifa_name); + } + return UINT2NUM(ifindex); +} +#else +#define ifaddr_ifindex rb_f_notimplement +#endif + +/* + * call-seq: + * ifaddr.flags => integer + * + * Returns the flags of _ifaddr_. + * + * The value is bitwise-or of Socket::IFF_* constants such as Socket::IFF_LOOPBACK. + */ + +static VALUE +ifaddr_flags(VALUE self) +{ + struct ifaddrs *ifa = get_ifaddrs(self); + return IFAFLAGS2NUM(ifa->ifa_flags); +} + +/* + * call-seq: + * ifaddr.addr => addrinfo + * + * Returns the address of _ifaddr_. + * nil is returned if address is not available in _ifaddr_. + */ + +static VALUE +ifaddr_addr(VALUE self) +{ + struct ifaddrs *ifa = get_ifaddrs(self); + if (ifa->ifa_addr) + return rsock_sockaddr_obj(ifa->ifa_addr, rsock_sockaddr_len(ifa->ifa_addr)); + return Qnil; +} + +/* + * call-seq: + * ifaddr.netmask => addrinfo + * + * Returns the netmask address of _ifaddr_. + * nil is returned if netmask is not available in _ifaddr_. + */ + +static VALUE +ifaddr_netmask(VALUE self) +{ + struct ifaddrs *ifa = get_ifaddrs(self); + if (ifa->ifa_netmask) + return rsock_sockaddr_obj(ifa->ifa_netmask, rsock_sockaddr_len(ifa->ifa_netmask)); + return Qnil; +} + +/* + * call-seq: + * ifaddr.broadaddr => addrinfo + * + * Returns the broadcast address of _ifaddr_. + * nil is returned if the flags doesn't have IFF_BROADCAST. + */ + +static VALUE +ifaddr_broadaddr(VALUE self) +{ + struct ifaddrs *ifa = get_ifaddrs(self); + if ((ifa->ifa_flags & IFF_BROADCAST) && ifa->ifa_broadaddr) + return rsock_sockaddr_obj(ifa->ifa_broadaddr, rsock_sockaddr_len(ifa->ifa_broadaddr)); + return Qnil; +} + +/* + * call-seq: + * ifaddr.dstaddr => addrinfo + * + * Returns the destination address of _ifaddr_. + * nil is returned if the flags doesn't have IFF_POINTOPOINT. + */ + +static VALUE +ifaddr_dstaddr(VALUE self) +{ + struct ifaddrs *ifa = get_ifaddrs(self); + if ((ifa->ifa_flags & IFF_POINTOPOINT) && ifa->ifa_dstaddr) + return rsock_sockaddr_obj(ifa->ifa_dstaddr, rsock_sockaddr_len(ifa->ifa_dstaddr)); + return Qnil; +} + +#ifdef HAVE_STRUCT_IF_DATA_IFI_VHID +/* + * call-seq: + * ifaddr.vhid => Integer + * + * Returns the vhid address of _ifaddr_. + * nil is returned if there is no vhid. + */ + +static VALUE +ifaddr_vhid(VALUE self) +{ + struct ifaddrs *ifa = get_ifaddrs(self); + if (ifa->ifa_data) + return (INT2FIX(((struct if_data*)ifa->ifa_data)->ifi_vhid)); + else + return Qnil; +} +#endif + +static void +ifaddr_inspect_flags(ifa_flags_t flags, VALUE result) +{ + const char *sep = " "; +#define INSPECT_BIT(bit, name) \ + if (flags & (bit)) { rb_str_catf(result, "%s" name, sep); flags &= ~(ifa_flags_t)(bit); sep = ","; } +#ifdef IFF_UP + INSPECT_BIT(IFF_UP, "UP") +#endif +#ifdef IFF_BROADCAST + INSPECT_BIT(IFF_BROADCAST, "BROADCAST") +#endif +#ifdef IFF_DEBUG + INSPECT_BIT(IFF_DEBUG, "DEBUG") +#endif +#ifdef IFF_LOOPBACK + INSPECT_BIT(IFF_LOOPBACK, "LOOPBACK") +#endif +#ifdef IFF_POINTOPOINT + INSPECT_BIT(IFF_POINTOPOINT, "POINTOPOINT") +#endif +#ifdef IFF_RUNNING + INSPECT_BIT(IFF_RUNNING, "RUNNING") +#endif +#ifdef IFF_NOARP + INSPECT_BIT(IFF_NOARP, "NOARP") +#endif +#ifdef IFF_PROMISC + INSPECT_BIT(IFF_PROMISC, "PROMISC") +#endif +#ifdef IFF_NOTRAILERS + INSPECT_BIT(IFF_NOTRAILERS, "NOTRAILERS") +#endif +#ifdef IFF_ALLMULTI + INSPECT_BIT(IFF_ALLMULTI, "ALLMULTI") +#endif +#ifdef IFF_SIMPLEX + INSPECT_BIT(IFF_SIMPLEX, "SIMPLEX") +#endif +#ifdef IFF_MASTER + INSPECT_BIT(IFF_MASTER, "MASTER") +#endif +#ifdef IFF_SLAVE + INSPECT_BIT(IFF_SLAVE, "SLAVE") +#endif +#ifdef IFF_MULTICAST + INSPECT_BIT(IFF_MULTICAST, "MULTICAST") +#endif +#ifdef IFF_PORTSEL + INSPECT_BIT(IFF_PORTSEL, "PORTSEL") +#endif +#ifdef IFF_AUTOMEDIA + INSPECT_BIT(IFF_AUTOMEDIA, "AUTOMEDIA") +#endif +#ifdef IFF_DYNAMIC + INSPECT_BIT(IFF_DYNAMIC, "DYNAMIC") +#endif +#ifdef IFF_LOWER_UP + INSPECT_BIT(IFF_LOWER_UP, "LOWER_UP") +#endif +#ifdef IFF_DORMANT + INSPECT_BIT(IFF_DORMANT, "DORMANT") +#endif +#ifdef IFF_ECHO + INSPECT_BIT(IFF_ECHO, "ECHO") +#endif +#undef INSPECT_BIT + if (flags) { + rb_str_catf(result, "%s%#"PRIxIFAFLAGS, sep, flags); + } +} + +/* + * call-seq: + * ifaddr.inspect => string + * + * Returns a string to show contents of _ifaddr_. + */ + +static VALUE +ifaddr_inspect(VALUE self) +{ + struct ifaddrs *ifa = get_ifaddrs(self); + VALUE result; + + result = rb_str_new_cstr("#<"); + + rb_str_append(result, rb_class_name(CLASS_OF(self))); + rb_str_cat2(result, " "); + rb_str_cat2(result, ifa->ifa_name); + + if (ifa->ifa_flags) + ifaddr_inspect_flags(ifa->ifa_flags, result); + + if (ifa->ifa_addr) { + rb_str_cat2(result, " "); + rsock_inspect_sockaddr(ifa->ifa_addr, + rsock_sockaddr_len(ifa->ifa_addr), + result); + } + if (ifa->ifa_netmask) { + rb_str_cat2(result, " netmask="); + rsock_inspect_sockaddr(ifa->ifa_netmask, + rsock_sockaddr_len(ifa->ifa_netmask), + result); + } + + if ((ifa->ifa_flags & IFF_BROADCAST) && ifa->ifa_broadaddr) { + rb_str_cat2(result, " broadcast="); + rsock_inspect_sockaddr(ifa->ifa_broadaddr, + rsock_sockaddr_len(ifa->ifa_broadaddr), + result); + } + + if ((ifa->ifa_flags & IFF_POINTOPOINT) && ifa->ifa_dstaddr) { + rb_str_cat2(result, " dstaddr="); + rsock_inspect_sockaddr(ifa->ifa_dstaddr, + rsock_sockaddr_len(ifa->ifa_dstaddr), + result); + } + + rb_str_cat2(result, ">"); + return result; +} +#endif + +#ifdef HAVE_GETIFADDRS +/* + * call-seq: + * Socket.getifaddrs => [ifaddr1, ...] + * + * Returns an array of interface addresses. + * An element of the array is an instance of Socket::Ifaddr. + * + * This method can be used to find multicast-enabled interfaces: + * + * pp Socket.getifaddrs.reject {|ifaddr| + * !ifaddr.addr.ip? || (ifaddr.flags & Socket::IFF_MULTICAST == 0) + * }.map {|ifaddr| [ifaddr.name, ifaddr.ifindex, ifaddr.addr] } + * #=> [["eth0", 2, #<Addrinfo: 221.186.184.67>], + * # ["eth0", 2, #<Addrinfo: fe80::216:3eff:fe95:88bb%eth0>]] + * + * Example result on GNU/Linux: + * pp Socket.getifaddrs + * #=> [#<Socket::Ifaddr lo UP,LOOPBACK,RUNNING,0x10000 PACKET[protocol=0 lo hatype=772 HOST hwaddr=00:00:00:00:00:00]>, + * # #<Socket::Ifaddr eth0 UP,BROADCAST,RUNNING,MULTICAST,0x10000 PACKET[protocol=0 eth0 hatype=1 HOST hwaddr=00:16:3e:95:88:bb] broadcast=PACKET[protocol=0 eth0 hatype=1 HOST hwaddr=ff:ff:ff:ff:ff:ff]>, + * # #<Socket::Ifaddr sit0 NOARP PACKET[protocol=0 sit0 hatype=776 HOST hwaddr=00:00:00:00]>, + * # #<Socket::Ifaddr lo UP,LOOPBACK,RUNNING,0x10000 127.0.0.1 netmask=255.0.0.0>, + * # #<Socket::Ifaddr eth0 UP,BROADCAST,RUNNING,MULTICAST,0x10000 221.186.184.67 netmask=255.255.255.240 broadcast=221.186.184.79>, + * # #<Socket::Ifaddr lo UP,LOOPBACK,RUNNING,0x10000 ::1 netmask=ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff>, + * # #<Socket::Ifaddr eth0 UP,BROADCAST,RUNNING,MULTICAST,0x10000 fe80::216:3eff:fe95:88bb%eth0 netmask=ffff:ffff:ffff:ffff::>] + * + * Example result on FreeBSD: + * pp Socket.getifaddrs + * #=> [#<Socket::Ifaddr usbus0 UP,0x10000 LINK[usbus0]>, + * # #<Socket::Ifaddr re0 UP,BROADCAST,RUNNING,MULTICAST,0x800 LINK[re0 3a:d0:40:9a:fe:e8]>, + * # #<Socket::Ifaddr re0 UP,BROADCAST,RUNNING,MULTICAST,0x800 10.250.10.18 netmask=255.255.255.? (7 bytes for 16 bytes sockaddr_in) broadcast=10.250.10.255>, + * # #<Socket::Ifaddr re0 UP,BROADCAST,RUNNING,MULTICAST,0x800 fe80:2::38d0:40ff:fe9a:fee8 netmask=ffff:ffff:ffff:ffff::>, + * # #<Socket::Ifaddr re0 UP,BROADCAST,RUNNING,MULTICAST,0x800 2001:2e8:408:10::12 netmask=UNSPEC>, + * # #<Socket::Ifaddr plip0 POINTOPOINT,MULTICAST,0x800 LINK[plip0]>, + * # #<Socket::Ifaddr lo0 UP,LOOPBACK,RUNNING,MULTICAST LINK[lo0]>, + * # #<Socket::Ifaddr lo0 UP,LOOPBACK,RUNNING,MULTICAST ::1 netmask=ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff>, + * # #<Socket::Ifaddr lo0 UP,LOOPBACK,RUNNING,MULTICAST fe80:4::1 netmask=ffff:ffff:ffff:ffff::>, + * # #<Socket::Ifaddr lo0 UP,LOOPBACK,RUNNING,MULTICAST 127.0.0.1 netmask=255.?.?.? (5 bytes for 16 bytes sockaddr_in)>] + * + */ + +static VALUE +socket_s_getifaddrs(VALUE self) +{ + return rsock_getifaddrs(); +} +#else +#define socket_s_getifaddrs rb_f_notimplement +#endif + +void +rsock_init_sockifaddr(void) +{ +#ifdef HAVE_GETIFADDRS + /* + * Document-class: Socket::Ifaddr + * + * Socket::Ifaddr represents a result of getifaddrs() function. + */ + rb_cSockIfaddr = rb_define_class_under(rb_cSocket, "Ifaddr", rb_cObject); + rb_undef_alloc_func(rb_cSockIfaddr); + rb_define_method(rb_cSockIfaddr, "inspect", ifaddr_inspect, 0); + rb_define_method(rb_cSockIfaddr, "name", ifaddr_name, 0); + rb_define_method(rb_cSockIfaddr, "ifindex", ifaddr_ifindex, 0); + rb_define_method(rb_cSockIfaddr, "flags", ifaddr_flags, 0); + rb_define_method(rb_cSockIfaddr, "addr", ifaddr_addr, 0); + rb_define_method(rb_cSockIfaddr, "netmask", ifaddr_netmask, 0); + rb_define_method(rb_cSockIfaddr, "broadaddr", ifaddr_broadaddr, 0); + rb_define_method(rb_cSockIfaddr, "dstaddr", ifaddr_dstaddr, 0); +#ifdef HAVE_STRUCT_IF_DATA_IFI_VHID + rb_define_method(rb_cSockIfaddr, "vhid", ifaddr_vhid, 0); +#endif +#endif + + rb_define_singleton_method(rb_cSocket, "getifaddrs", socket_s_getifaddrs, 0); +} diff --git a/ext/socket/init.c b/ext/socket/init.c new file mode 100644 index 0000000000..b761d601c3 --- /dev/null +++ b/ext/socket/init.c @@ -0,0 +1,843 @@ +/************************************************ + + init.c - + + created at: Thu Mar 31 12:21:29 JST 1994 + + Copyright (C) 1993-2007 Yukihiro Matsumoto + +************************************************/ + +#include "rubysocket.h" + +#ifdef _WIN32 +VALUE rb_w32_conv_from_wchar(const WCHAR *wstr, rb_encoding *enc); +#endif + +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; +VALUE rb_cAddrinfo; + +VALUE rb_eSocket; +VALUE rb_eResolution; + +#ifdef SOCKS +VALUE rb_cSOCKSSocket; +#endif + +int rsock_do_not_reverse_lookup = 1; +static VALUE sym_wait_readable; +static ID id_error_code; + +void +rsock_raise_resolution_error(const char *reason, int error) +{ +#ifdef EAI_SYSTEM + int e; + if (error == EAI_SYSTEM && (e = errno) != 0) + rb_syserr_fail(e, reason); +#endif +#ifdef _WIN32 + rb_encoding *enc = rb_default_internal_encoding(); + VALUE msg = rb_sprintf("%s: ", reason); + if (!enc) enc = rb_default_internal_encoding(); + rb_str_concat(msg, rb_w32_conv_from_wchar(gai_strerrorW(error), enc)); +#else + VALUE msg = rb_sprintf("%s: %s", reason, gai_strerror(error)); +#endif + + StringValue(msg); + VALUE self = rb_class_new_instance(1, &msg, rb_eResolution); + rb_ivar_set(self, id_error_code, INT2NUM(error)); + rb_exc_raise(self); +} + +#if defined __APPLE__ +# define do_write_retry(code) do {ret = code;} while (ret == -1 && errno == EPROTOTYPE) +#else +# define do_write_retry(code) ret = code +#endif + +VALUE +rsock_init_sock(VALUE sock, int fd) +{ + rb_io_t *fp; + + rb_update_max_fd(fd); + MakeOpenFile(sock, fp); + fp->fd = fd; + fp->mode = FMODE_READWRITE|FMODE_DUPLEX; + rb_io_ascii8bit_binmode(sock); + if (rsock_do_not_reverse_lookup) { + fp->mode |= FMODE_NOREVLOOKUP; + } + rb_io_synchronized(fp); + + return sock; +} + +VALUE +rsock_sendto_blocking(void *data) +{ + struct rsock_send_arg *arg = data; + VALUE mesg = arg->mesg; + ssize_t ret; + do_write_retry(sendto(arg->fd, RSTRING_PTR(mesg), RSTRING_LEN(mesg), + arg->flags, arg->to, arg->tolen)); + return (VALUE)ret; +} + +VALUE +rsock_send_blocking(void *data) +{ + struct rsock_send_arg *arg = data; + VALUE mesg = arg->mesg; + ssize_t ret; + do_write_retry(send(arg->fd, RSTRING_PTR(mesg), RSTRING_LEN(mesg), + arg->flags)); + return (VALUE)ret; +} + +struct recvfrom_arg { + rb_io_t *fptr; + int fd, flags; + VALUE str; + size_t length; + socklen_t alen; + union_sockaddr buf; +}; + +static VALUE +recvfrom_blocking(void *data) +{ + struct recvfrom_arg *arg = data; + socklen_t len0 = arg->alen; + ssize_t ret; + ret = recvfrom(arg->fd, RSTRING_PTR(arg->str), arg->length, + arg->flags, &arg->buf.addr, &arg->alen); + + if (ret != -1 && len0 < arg->alen) + arg->alen = len0; + + return (VALUE)ret; +} + +static VALUE +rsock_strbuf(VALUE str, long buflen) +{ + long len; + + if (NIL_P(str)) return rb_str_new(0, buflen); + + StringValue(str); + len = RSTRING_LEN(str); + if (len >= buflen) { + rb_str_modify(str); + } else { + rb_str_modify_expand(str, buflen - len); + } + return str; +} + +static VALUE +recvfrom_locktmp(VALUE v) +{ + struct recvfrom_arg *arg = (struct recvfrom_arg *)v; + + return rb_io_blocking_region(arg->fptr, recvfrom_blocking, arg); +} + +int +rsock_is_dgram(rb_io_t *fptr) +{ + int socktype; + socklen_t optlen = (socklen_t)sizeof(socktype); + int ret = getsockopt(fptr->fd, SOL_SOCKET, SO_TYPE, (void*)&socktype, &optlen); + if (ret == -1) { + rb_sys_fail("getsockopt(SO_TYPE)"); + } + return socktype == SOCK_DGRAM; +} + +VALUE +rsock_s_recvfrom(VALUE socket, int argc, VALUE *argv, enum sock_recv_type from) +{ + rb_io_t *fptr; + VALUE str; + struct recvfrom_arg arg; + VALUE len, flg; + long buflen; + long slen; + + rb_scan_args(argc, argv, "12", &len, &flg, &str); + + if (flg == Qnil) + arg.flags = 0; + else + arg.flags = NUM2INT(flg); + + buflen = NUM2INT(len); + str = rsock_strbuf(str, buflen); + + RB_IO_POINTER(socket, fptr); + + if (rb_io_read_pending(fptr)) { + rb_raise(rb_eIOError, "recv for buffered IO"); + } + + arg.fptr = fptr; + arg.fd = fptr->fd; + arg.alen = (socklen_t)sizeof(arg.buf); + arg.str = str; + arg.length = buflen; + + while (true) { + rb_io_check_closed(fptr); + +#ifdef RSOCK_WAIT_BEFORE_BLOCKING + rb_io_wait(fptr->self, RB_INT2NUM(RUBY_IO_READABLE), Qnil); +#endif + + rb_str_locktmp(str); + slen = (long)rb_ensure(recvfrom_locktmp, (VALUE)&arg, rb_str_unlocktmp, str); + + if (slen == 0 && !rsock_is_dgram(fptr)) { + return Qnil; + } + if (slen >= 0) break; + + if (!rb_io_maybe_wait_readable(errno, socket, RUBY_IO_TIMEOUT_DEFAULT)) + rb_sys_fail("recvfrom(2)"); + } + + /* Resize the string to the amount of data received */ + rb_str_set_len(str, slen); + switch (from) { + case RECV_RECV: + return str; + case RECV_IP: +#if 0 + if (arg.alen != sizeof(struct sockaddr_in)) { + rb_raise(rb_eTypeError, "sockaddr size differs - should not happen"); + } +#endif + if (arg.alen && arg.alen != sizeof(arg.buf)) /* OSX doesn't return a from result for connection-oriented sockets */ + return rb_assoc_new(str, rsock_ipaddr(&arg.buf.addr, arg.alen, fptr->mode & FMODE_NOREVLOOKUP)); + else + return rb_assoc_new(str, Qnil); + +#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN + case RECV_UNIX: + return rb_assoc_new(str, rsock_unixaddr(&arg.buf.un, arg.alen)); +#endif + case RECV_SOCKET: + return rb_assoc_new(str, rsock_io_socket_addrinfo(socket, &arg.buf.addr, arg.alen)); + default: + rb_bug("rsock_s_recvfrom called with bad value"); + } +} + +VALUE +rsock_s_recvfrom_nonblock(VALUE sock, VALUE len, VALUE flg, VALUE str, + VALUE ex, enum sock_recv_type from) +{ + rb_io_t *fptr; + union_sockaddr buf; + socklen_t alen = (socklen_t)sizeof buf; + long buflen; + long slen; + int fd, flags; + VALUE addr = Qnil; + socklen_t len0; + + flags = NUM2INT(flg); + buflen = NUM2INT(len); + str = rsock_strbuf(str, buflen); + +#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; + + rb_io_check_closed(fptr); + + if (!MSG_DONTWAIT_RELIABLE) + rb_io_set_nonblock(fptr); + + len0 = alen; + slen = recvfrom(fd, RSTRING_PTR(str), buflen, flags, &buf.addr, &alen); + if (slen != -1 && len0 < alen) + alen = len0; + + if (slen == 0 && !rsock_is_dgram(fptr)) { + return Qnil; + } + + if (slen < 0) { + int e = errno; + switch (e) { + case EAGAIN: +#if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN + case EWOULDBLOCK: +#endif + if (ex == Qfalse) + return sym_wait_readable; + rb_readwrite_syserr_fail(RB_IO_WAIT_READABLE, e, "recvfrom(2) would block"); + } + rb_syserr_fail(e, "recvfrom(2)"); + } + if (slen != RSTRING_LEN(str)) { + rb_str_set_len(str, slen); + } + 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 = rsock_ipaddr(&buf.addr, alen, fptr->mode & FMODE_NOREVLOOKUP); + break; + + case RECV_SOCKET: + addr = rsock_io_socket_addrinfo(sock, &buf.addr, alen); + break; + + default: + rb_bug("rsock_s_recvfrom_nonblock called with bad value"); + } + return rb_assoc_new(str, addr); +} + +#if MSG_DONTWAIT_RELIABLE +static VALUE sym_wait_writable; + +/* copied from io.c :< */ +static long +read_buffered_data(char *ptr, long len, rb_io_t *fptr) +{ + int n = fptr->rbuf.len; + + if (n <= 0) return 0; + if (n > len) n = (int)len; + MEMMOVE(ptr, fptr->rbuf.ptr+fptr->rbuf.off, char, n); + fptr->rbuf.off += n; + fptr->rbuf.len -= n; + return n; +} + +/* :nodoc: */ +VALUE +rsock_read_nonblock(VALUE sock, VALUE length, VALUE buf, VALUE ex) +{ + rb_io_t *fptr; + long n; + long len = NUM2LONG(length); + VALUE str = rsock_strbuf(buf, len); + char *ptr; + + GetOpenFile(sock, fptr); + + if (len == 0) { + rb_str_set_len(str, 0); + return str; + } + + ptr = RSTRING_PTR(str); + n = read_buffered_data(ptr, len, fptr); + if (n <= 0) { + n = (long)recv(fptr->fd, ptr, len, MSG_DONTWAIT); + if (n < 0) { + int e = errno; + if ((e == EWOULDBLOCK || e == EAGAIN)) { + if (ex == Qfalse) return sym_wait_readable; + rb_readwrite_syserr_fail(RB_IO_WAIT_READABLE, + e, "read would block"); + } + rb_syserr_fail_path(e, fptr->pathv); + } + } + if (n != RSTRING_LEN(str)) { + rb_str_modify(str); + rb_str_set_len(str, n); + } + if (n == 0) { + if (ex == Qfalse) return Qnil; + rb_eof_error(); + } + + return str; +} + +/* :nodoc: */ +VALUE +rsock_write_nonblock(VALUE sock, VALUE str, VALUE ex) +{ + rb_io_t *fptr; + long n; + + if (!RB_TYPE_P(str, T_STRING)) + str = rb_obj_as_string(str); + + sock = rb_io_get_write_io(sock); + GetOpenFile(sock, fptr); + rb_io_check_writable(fptr); + + /* + * As with IO#write_nonblock, we may block if somebody is relying on + * buffered I/O; but nobody actually hits this because pipes and sockets + * are not userspace-buffered in Ruby by default. + */ + if (fptr->wbuf.len > 0) { + rb_io_flush(sock); + } + +#ifdef __APPLE__ + again: +#endif + n = (long)send(fptr->fd, RSTRING_PTR(str), RSTRING_LEN(str), MSG_DONTWAIT); + if (n < 0) { + int e = errno; + +#ifdef __APPLE__ + if (e == EPROTOTYPE) { + goto again; + } +#endif + if (e == EWOULDBLOCK || e == EAGAIN) { + if (ex == Qfalse) return sym_wait_writable; + rb_readwrite_syserr_fail(RB_IO_WAIT_WRITABLE, e, + "write would block"); + } + rb_syserr_fail_path(e, fptr->pathv); + } + + return LONG2FIX(n); +} +#endif /* MSG_DONTWAIT_RELIABLE */ + +static int +rsock_socket0(int domain, int type, int proto) +{ +#ifdef SOCK_CLOEXEC + type |= SOCK_CLOEXEC; +#endif + +#ifdef SOCK_NONBLOCK + type |= SOCK_NONBLOCK; +#endif + + int result = socket(domain, type, proto); + + if (result == -1) + return -1; + + rb_fd_fix_cloexec(result); + +#ifndef SOCK_NONBLOCK + rsock_make_fd_nonblock(result); +#endif + + return result; +} + +int +rsock_socket(int domain, int type, int proto) +{ + int fd; + + fd = rsock_socket0(domain, type, proto); + if (fd < 0) { + if (rb_gc_for_fd(errno)) { + fd = rsock_socket0(domain, type, proto); + } + } + if (0 <= fd) + rb_update_max_fd(fd); + return fd; +} + +/* emulate blocking connect behavior on EINTR or non-blocking socket */ +static int +wait_connectable(VALUE self, VALUE timeout, const struct sockaddr *sockaddr, int len) +{ + int sockerr; + socklen_t sockerrlen; + int fd = rb_io_descriptor(self); + + sockerrlen = (socklen_t)sizeof(sockerr); + if (getsockopt(fd, SOL_SOCKET, SO_ERROR, (void *)&sockerr, &sockerrlen) < 0) + return -1; + + /* necessary for non-blocking sockets (at least ECONNREFUSED) */ + switch (sockerr) { + case 0: + break; +#ifdef EALREADY + case EALREADY: +#endif +#ifdef EISCONN + case EISCONN: +#endif +#ifdef ECONNREFUSED + case ECONNREFUSED: +#endif +#ifdef EHOSTUNREACH + case EHOSTUNREACH: +#endif + errno = sockerr; + return -1; + } + + /* + * Stevens book says, successful finish turn on RB_WAITFD_OUT and + * failure finish turn on both RB_WAITFD_IN and RB_WAITFD_OUT. + * So it's enough to wait only RB_WAITFD_OUT and check the pending error + * by getsockopt(). + * + * Note: rb_wait_for_single_fd already retries on EINTR/ERESTART + */ + VALUE result = rb_io_wait(self, RB_INT2NUM(RUBY_IO_READABLE|RUBY_IO_WRITABLE), timeout); + + if (result == Qfalse) { + VALUE rai = rsock_addrinfo_new((struct sockaddr *)sockaddr, len, PF_UNSPEC, 0, 0, Qnil, Qnil); + VALUE addr_str = rsock_addrinfo_inspect_sockaddr(rai); + VALUE message = rb_sprintf("user specified timeout for %" PRIsVALUE, addr_str); + rb_raise(rb_eIOTimeoutError, "%" PRIsVALUE, message); + } + + int revents = RB_NUM2INT(result); + + if (revents < 0) + return -1; + + sockerrlen = (socklen_t)sizeof(sockerr); + if (getsockopt(fd, SOL_SOCKET, SO_ERROR, (void *)&sockerr, &sockerrlen) < 0) + return -1; + + switch (sockerr) { + case 0: + /* + * be defensive in case some platforms set SO_ERROR on the original, + * interrupted connect() + */ + case EINTR: +#ifdef ERESTART + case ERESTART: +#endif + case EAGAIN: +#ifdef EINPROGRESS + case EINPROGRESS: +#endif +#ifdef EALREADY + case EALREADY: +#endif +#ifdef EISCONN + case EISCONN: +#endif + return 0; /* success */ + default: + /* likely (but not limited to): ECONNREFUSED, ETIMEDOUT, EHOSTUNREACH */ + errno = sockerr; + return -1; + } + + return 0; +} + +struct connect_arg { + int fd; + socklen_t len; + const struct sockaddr *sockaddr; +}; + +static VALUE +connect_blocking(void *data) +{ + struct connect_arg *arg = data; + return (VALUE)connect(arg->fd, arg->sockaddr, arg->len); +} + +#if defined(SOCKS) && !defined(SOCKS5) +static VALUE +socks_connect_blocking(void *data) +{ + struct connect_arg *arg = data; + return (VALUE)Rconnect(arg->fd, arg->sockaddr, arg->len); +} +#endif + +int +rsock_connect(VALUE self, const struct sockaddr *sockaddr, int len, int socks, VALUE timeout) +{ + int descriptor = rb_io_descriptor(self); + rb_blocking_function_t *func = connect_blocking; + struct connect_arg arg = {.fd = descriptor, .sockaddr = sockaddr, .len = len}; + + rb_io_t *fptr; + RB_IO_POINTER(self, fptr); + +#if defined(SOCKS) && !defined(SOCKS5) + if (socks) func = socks_connect_blocking; +#endif + int status = (int)rb_io_blocking_region(fptr, func, &arg); + + if (status < 0) { + switch (errno) { + case EINTR: +#ifdef ERESTART + case ERESTART: +#endif + case EAGAIN: +#ifdef EINPROGRESS + case EINPROGRESS: +#endif + return wait_connectable(self, timeout, sockaddr, len); + } + } + return status; +} + +void +rsock_make_fd_nonblock(int fd) +{ +#ifdef _WIN32 + return; +#endif + + int flags; +#ifdef F_GETFL + flags = fcntl(fd, F_GETFL); + if (flags == -1) { + rb_sys_fail("fnctl(2)"); + } +#else + flags = 0; +#endif + flags |= O_NONBLOCK; + if (fcntl(fd, F_SETFL, flags) == -1) { + rb_sys_fail("fnctl(2)"); + } +} + +static int +cloexec_accept(int socket, struct sockaddr *address, socklen_t *address_len) +{ + socklen_t len0 = 0; + if (address_len) len0 = *address_len; + +#ifdef HAVE_ACCEPT4 + int flags = SOCK_CLOEXEC; + +#ifdef SOCK_NONBLOCK + flags |= SOCK_NONBLOCK; +#endif + + int result = accept4(socket, address, address_len, flags); + if (result == -1) return -1; + +#ifndef SOCK_NONBLOCK + rsock_make_fd_nonblock(result); +#endif +#else + int result = accept(socket, address, address_len); + if (result == -1) return -1; + + rb_maygvl_fd_fix_cloexec(result); + rsock_make_fd_nonblock(result); +#endif + + if (address_len && len0 < *address_len) *address_len = len0; + return result; +} + +VALUE +rsock_s_accept_nonblock(VALUE klass, VALUE ex, rb_io_t *fptr, + struct sockaddr *sockaddr, socklen_t *len) +{ + int fd2; + + rb_io_set_nonblock(fptr); + fd2 = cloexec_accept(fptr->fd, (struct sockaddr*)sockaddr, len); + if (fd2 < 0) { + int e = errno; + switch (e) { + case EAGAIN: +#if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN + case EWOULDBLOCK: +#endif + case ECONNABORTED: +#if defined EPROTO + case EPROTO: +#endif + if (ex == Qfalse) + return sym_wait_readable; + rb_readwrite_syserr_fail(RB_IO_WAIT_READABLE, e, "accept(2) would block"); + } + rb_syserr_fail(e, "accept(2)"); + } + rb_update_max_fd(fd2); + return rsock_init_sock(rb_obj_alloc(klass), fd2); +} + +struct accept_arg { + int fd; + struct sockaddr *sockaddr; + socklen_t *len; +}; + +static VALUE +accept_blocking(void *data) +{ + struct accept_arg *arg = data; + return (VALUE)cloexec_accept(arg->fd, arg->sockaddr, arg->len); +} + +VALUE +rsock_s_accept(VALUE klass, VALUE io, struct sockaddr *sockaddr, socklen_t *len) +{ + rb_io_t *fptr = NULL; + RB_IO_POINTER(io, fptr); + + struct accept_arg accept_arg = { + .fd = fptr->fd, + .sockaddr = sockaddr, + .len = len + }; + + int retry = 0, peer; + + retry: +#ifdef RSOCK_WAIT_BEFORE_BLOCKING + rb_io_wait(fptr->self, RB_INT2NUM(RUBY_IO_READABLE), Qnil); +#endif + peer = (int)rb_io_blocking_region(fptr, accept_blocking, &accept_arg); + if (peer < 0) { + int error = errno; + + switch (error) { + case EMFILE: + case ENFILE: + case ENOMEM: + if (retry) break; + rb_gc(); + retry = 1; + goto retry; + default: + if (!rb_io_maybe_wait_readable(error, io, RUBY_IO_TIMEOUT_DEFAULT)) break; + retry = 0; + goto retry; + } + + rb_syserr_fail(error, "accept(2)"); + } + + rb_update_max_fd(peer); + + if (!klass) return INT2NUM(peer); + + return rsock_init_sock(rb_obj_alloc(klass), peer); +} + +int +rsock_getfamily(rb_io_t *fptr) +{ + union_sockaddr ss; + socklen_t sslen = (socklen_t)sizeof(ss); + int cached = fptr->mode & FMODE_SOCK; + + if (cached) { + switch (cached) { +#ifdef AF_UNIX + case FMODE_UNIX: return AF_UNIX; +#endif + case FMODE_INET: return AF_INET; + case FMODE_INET6: return AF_INET6; + } + } + + ss.addr.sa_family = AF_UNSPEC; + if (getsockname(fptr->fd, &ss.addr, &sslen) < 0) + return AF_UNSPEC; + + switch (ss.addr.sa_family) { +#ifdef AF_UNIX + case AF_UNIX: fptr->mode |= FMODE_UNIX; break; +#endif + case AF_INET: fptr->mode |= FMODE_INET; break; + case AF_INET6: fptr->mode |= FMODE_INET6; break; + } + + return ss.addr.sa_family; +} + +/* + * call-seq: + * error_code -> integer + * + * Returns the raw error code indicating the cause of the hostname resolution failure. + * + * begin + * Addrinfo.getaddrinfo("ruby-lang.org", nil) + * rescue Socket::ResolutionError => e + * if e.error_code == Socket::EAI_AGAIN + * puts "Temporary failure in name resolution." + * end + * end + * + * Note that error codes depend on the operating system. + */ +static VALUE +sock_resolv_error_code(VALUE self) +{ + return rb_attr_get(self, id_error_code); +} + +void +rsock_init_socket_init(void) +{ + /* + * SocketError is the error class for socket. + */ + rb_eSocket = rb_define_class("SocketError", rb_eStandardError); + /* + * Socket::ResolutionError is the error class for hostname resolution. + */ + rb_eResolution = rb_define_class_under(rb_cSocket, "ResolutionError", rb_eSocket); + rb_define_method(rb_eResolution, "error_code", sock_resolv_error_code, 0); + rsock_init_ipsocket(); + rsock_init_tcpsocket(); + rsock_init_tcpserver(); + rsock_init_sockssocket(); + rsock_init_udpsocket(); + rsock_init_unixsocket(); + rsock_init_unixserver(); + rsock_init_sockopt(); + rsock_init_ancdata(); + rsock_init_addrinfo(); + rsock_init_sockifaddr(); + rsock_init_socket_constants(); + + id_error_code = rb_intern_const("error_code"); + +#undef rb_intern + sym_wait_readable = ID2SYM(rb_intern("wait_readable")); + +#if MSG_DONTWAIT_RELIABLE + sym_wait_writable = ID2SYM(rb_intern("wait_writable")); +#endif +} diff --git a/ext/socket/ipsocket.c b/ext/socket/ipsocket.c new file mode 100644 index 0000000000..931a1a629c --- /dev/null +++ b/ext/socket/ipsocket.c @@ -0,0 +1,1640 @@ +/************************************************ + + ipsocket.c - + + created at: Thu Mar 31 12:21:29 JST 1994 + + Copyright (C) 1993-2007 Yukihiro Matsumoto + +************************************************/ + +#include "rubysocket.h" +#include <stdio.h> + +struct inetsock_arg +{ + VALUE self; + VALUE io; + + struct { + VALUE host, serv; + struct rb_addrinfo *res; + } remote, local; + int type; + VALUE resolv_timeout; + VALUE connect_timeout; + VALUE open_timeout; +}; + +void +rsock_raise_user_specified_timeout(struct addrinfo *ai, VALUE host, VALUE port) +{ + VALUE message; + + if (ai && ai->ai_addr) { + VALUE rai = rsock_addrinfo_new((struct sockaddr *)ai->ai_addr, (socklen_t)ai->ai_addrlen, PF_UNSPEC, 0, 0, Qnil, Qnil); + VALUE addr_str = rsock_addrinfo_inspect_sockaddr(rai); + message = rb_sprintf("user specified timeout for %" PRIsVALUE, addr_str); + } else { + message = rb_sprintf("user specified timeout for %" PRIsVALUE " port %" PRIsVALUE, host, port); + } + + rb_raise(rb_eIOTimeoutError, "%" PRIsVALUE, message); +} + +static VALUE +inetsock_cleanup(VALUE v) +{ + struct inetsock_arg *arg = (void *)v; + if (arg->remote.res) { + rb_freeaddrinfo(arg->remote.res); + arg->remote.res = 0; + } + if (arg->local.res) { + rb_freeaddrinfo(arg->local.res); + arg->local.res = 0; + } + if (arg->io != Qnil) { + rb_io_close(arg->io); + arg->io = Qnil; + } + return Qnil; +} + +static VALUE +current_clocktime(void) +{ + VALUE clock_monotnic_const = rb_const_get(rb_mProcess, rb_intern("CLOCK_MONOTONIC")); + return rb_funcall(rb_mProcess, rb_intern("clock_gettime"), 1, clock_monotnic_const); +} + +static VALUE +init_inetsock_internal(VALUE v) +{ + struct inetsock_arg *arg = (void *)v; + int error = 0; + int type = arg->type; + struct addrinfo *res, *lres; + int status = 0, local = 0; + int family = AF_UNSPEC; + const char *syscall = 0; + VALUE resolv_timeout = arg->resolv_timeout; + VALUE connect_timeout = arg->connect_timeout; + VALUE open_timeout = arg->open_timeout; + VALUE timeout; + VALUE starts_at; + + timeout = NIL_P(open_timeout) ? resolv_timeout : open_timeout; + starts_at = current_clocktime(); + + arg->remote.res = rsock_addrinfo(arg->remote.host, arg->remote.serv, + family, SOCK_STREAM, + (type == INET_SERVER) ? AI_PASSIVE : 0, timeout); + + /* + * Maybe also accept a local address + */ + + if (type != INET_SERVER && (!NIL_P(arg->local.host) || !NIL_P(arg->local.serv))) { + arg->local.res = rsock_addrinfo(arg->local.host, arg->local.serv, + family, SOCK_STREAM, 0, timeout); + } + + VALUE io = Qnil; + + for (res = arg->remote.res->ai; res; res = res->ai_next) { +#if !defined(INET6) && defined(AF_INET6) + if (res->ai_family == AF_INET6) + continue; +#endif + lres = NULL; + if (arg->local.res) { + for (lres = arg->local.res->ai; lres; lres = lres->ai_next) { + if (lres->ai_family == res->ai_family) + break; + } + if (!lres) { + if (res->ai_next || status < 0) + continue; + /* Use a different family local address if no choice, this + * will cause EAFNOSUPPORT. */ + lres = arg->local.res->ai; + } + } + status = rsock_socket(res->ai_family,res->ai_socktype,res->ai_protocol); + syscall = "socket(2)"; + if (status < 0) { + error = errno; + continue; + } + + int fd = status; + io = arg->io = rsock_init_sock(arg->self, fd); + + if (type == INET_SERVER) { +#if !defined(_WIN32) && !defined(__CYGWIN__) + status = 1; + setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, + (char*)&status, (socklen_t)sizeof(status)); +#endif + status = bind(fd, res->ai_addr, res->ai_addrlen); + syscall = "bind(2)"; + } + else { + if (lres) { +#if !defined(_WIN32) && !defined(__CYGWIN__) + status = 1; + setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, + (char*)&status, (socklen_t)sizeof(status)); +#endif + status = bind(fd, lres->ai_addr, lres->ai_addrlen); + local = status; + syscall = "bind(2)"; + } + + if (NIL_P(open_timeout)) { + timeout = connect_timeout; + } else { + VALUE elapsed = rb_funcall(current_clocktime(), '-', 1, starts_at); + timeout = rb_funcall(open_timeout, '-', 1, elapsed); + if (rb_funcall(timeout, '<', 1, INT2FIX(0)) == Qtrue) { + rsock_raise_user_specified_timeout(res, arg->remote.host, arg->remote.serv); + } + } + + if (status >= 0) { + status = rsock_connect(io, res->ai_addr, res->ai_addrlen, (type == INET_SOCKS), timeout); + syscall = "connect(2)"; + } + } + + if (status < 0) { + error = errno; + arg->io = Qnil; + rb_io_close(io); + io = Qnil; + continue; + } else { + break; + } + } + + if (status < 0) { + VALUE host, port; + + if (local < 0) { + host = arg->local.host; + port = arg->local.serv; + } else { + host = arg->remote.host; + port = arg->remote.serv; + } + + rsock_syserr_fail_host_port(error, syscall, host, port); + } + + // Don't close the socket in `inetsock_cleanup` if we are returning it: + arg->io = Qnil; + + if (type == INET_SERVER && io != Qnil) { + status = listen(rb_io_descriptor(io), SOMAXCONN); + if (status < 0) { + error = errno; + rb_io_close(io); + rb_syserr_fail(error, "listen(2)"); + } + } + + /* create new instance */ + return io; +} + +#if FAST_FALLBACK_INIT_INETSOCK_IMPL == 0 + +VALUE +rsock_init_inetsock( + VALUE self, VALUE remote_host, VALUE remote_serv, + VALUE local_host, VALUE local_serv, int type, + VALUE resolv_timeout, VALUE connect_timeout, VALUE open_timeout, + VALUE _fast_fallback, VALUE _test_mode_settings) +{ + if (!NIL_P(open_timeout) && (!NIL_P(resolv_timeout) || !NIL_P(connect_timeout))) { + rb_raise(rb_eArgError, "Cannot specify open_timeout along with connect_timeout or resolv_timeout"); + } + + struct inetsock_arg arg; + arg.self = self; + arg.io = Qnil; + 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.resolv_timeout = resolv_timeout; + arg.connect_timeout = connect_timeout; + arg.open_timeout = open_timeout; + return rb_ensure(init_inetsock_internal, (VALUE)&arg, + inetsock_cleanup, (VALUE)&arg); +} + +#elif FAST_FALLBACK_INIT_INETSOCK_IMPL == 1 + +#define IPV6_ENTRY_POS 0 +#define IPV4_ENTRY_POS 1 +#define RESOLUTION_ERROR 0 +#define SYSCALL_ERROR 1 + +static int +is_specified_ip_address(const char *hostname) +{ + if (!hostname) return false; + + struct in_addr ipv4addr; + struct in6_addr ipv6addr; + + return (inet_pton(AF_INET6, hostname, &ipv6addr) == 1 || + inet_pton(AF_INET, hostname, &ipv4addr) == 1); +} + +static int +is_local_port_fixed(const char *portp) +{ + if (!portp) return 0; + + char *endp; + errno = 0; + long port = strtol(portp, &endp, 10); + + if (endp == portp) return 0; + if (errno == ERANGE) return 0; + + return port > 0; +} + +struct fast_fallback_inetsock_arg +{ + VALUE self; + VALUE io; + + struct { + VALUE host, serv; + struct rb_addrinfo *res; + } remote, local; + int type; + VALUE resolv_timeout; + VALUE connect_timeout; + VALUE open_timeout; + + const char *hostp, *portp; + int *families; + int family_size; + int additional_flags; + struct fast_fallback_getaddrinfo_entry *getaddrinfo_entries[2]; + struct fast_fallback_getaddrinfo_shared *getaddrinfo_shared; + rb_fdset_t readfds, writefds; + int wait; + int connection_attempt_fds_size; + int *connection_attempt_fds; + VALUE test_mode_settings; +}; + +static struct fast_fallback_getaddrinfo_shared * +allocate_fast_fallback_getaddrinfo_shared(int family_size) +{ + struct fast_fallback_getaddrinfo_shared *shared; + + shared = (struct fast_fallback_getaddrinfo_shared *)calloc( + 1, + sizeof(struct fast_fallback_getaddrinfo_shared) + (family_size == 1 ? 0 : 2) * sizeof(struct fast_fallback_getaddrinfo_entry) + ); + + return shared; +} + +static void +allocate_fast_fallback_getaddrinfo_hints(struct addrinfo *hints, int family, int remote_addrinfo_hints, int additional_flags) +{ + MEMZERO(hints, struct addrinfo, 1); + hints->ai_family = family; + hints->ai_socktype = SOCK_STREAM; + hints->ai_protocol = IPPROTO_TCP; + hints->ai_flags = remote_addrinfo_hints; + hints->ai_flags |= additional_flags; +} + +static int* +allocate_connection_attempt_fds(int additional_capacity) +{ + int *fds = (int *)malloc(additional_capacity * sizeof(int)); + if (!fds) rb_syserr_fail(errno, "malloc(3)"); + for (int i = 0; i < additional_capacity; i++) fds[i] = -1; + return fds; +} + +static int +reallocate_connection_attempt_fds(int **fds, int current_capacity, int additional_capacity) +{ + int new_capacity = current_capacity + additional_capacity; + int *new_fds; + + new_fds = realloc(*fds, new_capacity * sizeof(int)); + if (new_fds == NULL) { + rb_syserr_fail(errno, "realloc(3)"); + } + *fds = new_fds; + + for (int i = current_capacity; i < new_capacity; i++) (*fds)[i] = -1; + return new_capacity; +} + +struct hostname_resolution_result +{ + struct addrinfo *ai; + int finished; + int has_error; +}; + +struct hostname_resolution_store +{ + struct hostname_resolution_result v6; + struct hostname_resolution_result v4; + int is_all_finished; +}; + +static int +any_addrinfos(struct hostname_resolution_store *resolution_store) +{ + return resolution_store->v6.ai || resolution_store->v4.ai; +} + +static struct timespec +current_clocktime_ts(void) +{ + struct timespec ts; + if ((clock_gettime(CLOCK_MONOTONIC, &ts)) < 0) { + rb_syserr_fail(errno, "clock_gettime(2)"); + } + return ts; +} + +static void +set_timeout_tv(struct timeval *tv, long ms, struct timespec from) +{ + long sec = ms / 1000; + long nsec = (ms % 1000) * 1000000; + long result_sec = from.tv_sec + sec; + long result_nsec = from.tv_nsec + nsec; + + result_sec += result_nsec / 1000000000; + result_nsec = result_nsec % 1000000000; + + tv->tv_sec = result_sec; + tv->tv_usec = (int)(result_nsec / 1000); +} + +static struct timeval +add_ts_to_tv(struct timeval tv, struct timespec ts) +{ + long ts_usec = ts.tv_nsec / 1000; + tv.tv_sec += ts.tv_sec; + tv.tv_usec += ts_usec; + + if (tv.tv_usec >= 1000000) { + tv.tv_sec += tv.tv_usec / 1000000; + tv.tv_usec = tv.tv_usec % 1000000; + } + + return tv; +} + +static VALUE +tv_to_seconds(struct timeval *timeout) { + if (timeout == NULL) return Qnil; + + double seconds = (double)timeout->tv_sec + (double)timeout->tv_usec / 1000000.0; + + return DBL2NUM(seconds); +} + +static int +is_infinity(struct timeval tv) +{ + // { -1, -1 } as infinity + return tv.tv_sec == -1 || tv.tv_usec == -1; +} + +static int +is_timeout_tv(struct timeval *timeout_tv, struct timespec now) { + if (!timeout_tv) return false; + if (timeout_tv->tv_sec == -1 && timeout_tv->tv_usec == -1) return false; + + struct timespec ts; + ts.tv_sec = timeout_tv->tv_sec; + ts.tv_nsec = timeout_tv->tv_usec * 1000; + + if (now.tv_sec > ts.tv_sec) return true; + if (now.tv_sec == ts.tv_sec && now.tv_nsec >= ts.tv_nsec) return true; + return false; +} + +static struct timeval * +select_expires_at( + struct hostname_resolution_store *resolution_store, + struct timeval *resolution_delay, + struct timeval *connection_attempt_delay, + struct timeval *user_specified_resolv_timeout_at, + struct timeval *user_specified_connect_timeout_at, + struct timeval *user_specified_open_timeout_at) +{ + if (any_addrinfos(resolution_store)) { + struct timeval *delay; + delay = resolution_delay ? resolution_delay : connection_attempt_delay; + + if (user_specified_open_timeout_at && + timercmp(user_specified_open_timeout_at, delay, <)) { + return user_specified_open_timeout_at; + } + return delay; + } + + if (user_specified_open_timeout_at) return user_specified_open_timeout_at; + + struct timeval *timeout = NULL; + + if (user_specified_resolv_timeout_at) { + if (is_infinity(*user_specified_resolv_timeout_at)) return NULL; + timeout = user_specified_resolv_timeout_at; + } + + if (user_specified_connect_timeout_at) { + if (is_infinity(*user_specified_connect_timeout_at)) return NULL; + if (!timeout || timercmp(user_specified_connect_timeout_at, timeout, >)) { + return user_specified_connect_timeout_at; + } + } + + return timeout; +} + +static struct timeval +tv_to_timeout(struct timeval *ends_at, struct timespec now) +{ + struct timeval delay; + struct timespec expires_at; + expires_at.tv_sec = ends_at->tv_sec; + expires_at.tv_nsec = ends_at->tv_usec * 1000; + + struct timespec diff; + diff.tv_sec = expires_at.tv_sec - now.tv_sec; + + if (expires_at.tv_nsec >= now.tv_nsec) { + diff.tv_nsec = expires_at.tv_nsec - now.tv_nsec; + } else { + diff.tv_sec -= 1; + diff.tv_nsec = (1000000000 + expires_at.tv_nsec) - now.tv_nsec; + } + + delay.tv_sec = diff.tv_sec; + delay.tv_usec = (int)diff.tv_nsec / 1000; + + return delay; +} + +static struct addrinfo * +pick_addrinfo(struct hostname_resolution_store *resolution_store, int last_family) +{ + int priority_on_v6[2] = { AF_INET6, AF_INET }; + int priority_on_v4[2] = { AF_INET, AF_INET6 }; + int *precedences = last_family == AF_INET6 ? priority_on_v4 : priority_on_v6; + struct addrinfo *selected_ai = NULL; + + for (int i = 0; i < 2; i++) { + if (precedences[i] == AF_INET6) { + selected_ai = resolution_store->v6.ai; + if (selected_ai) { + resolution_store->v6.ai = selected_ai->ai_next; + break; + } + } else { + selected_ai = resolution_store->v4.ai; + if (selected_ai) { + resolution_store->v4.ai = selected_ai->ai_next; + break; + } + } + } + return selected_ai; +} + +static void +socket_nonblock_set(int fd) +{ + int flags = fcntl(fd, F_GETFL); + + if (flags < 0) rb_syserr_fail(errno, "fcntl(2)"); + if ((flags & O_NONBLOCK) != 0) return; + + flags |= O_NONBLOCK; + + if (fcntl(fd, F_SETFL, flags) < 0) rb_syserr_fail(errno, "fcntl(2)"); + return; +} + +static int +in_progress_fds(int fds_size) +{ + return fds_size > 0; +} + +static void +remove_connection_attempt_fd(int *fds, int *fds_size, int removing_fd) +{ + int i, j; + + for (i = 0; i < *fds_size; i++) { + if (fds[i] != removing_fd) continue; + + for (j = i; j < *fds_size - 1; j++) { + fds[j] = fds[j + 1]; + } + + (*fds_size)--; + fds[*fds_size] = -1; + break; + } +} + +struct fast_fallback_error +{ + int type; + int ecode; +}; + +static VALUE +init_fast_fallback_inetsock_internal(VALUE v) +{ + struct fast_fallback_inetsock_arg *arg = (void *)v; + VALUE io = arg->io; + VALUE resolv_timeout = arg->resolv_timeout; + VALUE connect_timeout = arg->connect_timeout; + VALUE open_timeout = arg->open_timeout; + VALUE test_mode_settings = arg->test_mode_settings; + struct addrinfo *remote_ai = NULL, *local_ai = NULL; + int connected_fd = -1, status = 0, local_status = 0; + int remote_addrinfo_hints = 0; + struct fast_fallback_error last_error = { 0, 0 }; + const char *syscall = 0; + VALUE host, serv; + + #ifdef HAVE_CONST_AI_ADDRCONFIG + remote_addrinfo_hints |= AI_ADDRCONFIG; + #endif + + pthread_t threads[arg->family_size]; + char resolved_type[2]; + ssize_t resolved_type_size; + int hostname_resolution_waiter = -1, hostname_resolution_notifier = -1; + int pipefd[2]; + + int nfds = 0; + struct timeval *ends_at = NULL; + struct timeval delay = (struct timeval){ -1, -1 }; + struct timeval *delay_p = NULL; + + struct hostname_resolution_store resolution_store; + resolution_store.is_all_finished = false; + resolution_store.v6.ai = NULL; + resolution_store.v6.finished = false; + resolution_store.v6.has_error = false; + resolution_store.v4.ai = NULL; + resolution_store.v4.finished = false; + resolution_store.v4.has_error = false; + + int last_family = 0; + int additional_capacity = 10; + int current_capacity = additional_capacity; + arg->connection_attempt_fds = allocate_connection_attempt_fds(additional_capacity); + arg->connection_attempt_fds_size = 0; + + struct timeval resolution_delay_storage; + struct timeval *resolution_delay_expires_at = NULL; + struct timeval connection_attempt_delay_strage; + struct timeval *connection_attempt_delay_expires_at = NULL; + struct timeval user_specified_resolv_timeout_storage; + struct timeval *user_specified_resolv_timeout_at = NULL; + struct timeval user_specified_connect_timeout_storage; + struct timeval *user_specified_connect_timeout_at = NULL; + struct timeval user_specified_open_timeout_storage; + struct timeval *user_specified_open_timeout_at = NULL; + struct timespec now = current_clocktime_ts(); + VALUE starts_at = current_clocktime(); + + if (!NIL_P(open_timeout)) { + struct timeval open_timeout_tv = rb_time_interval(open_timeout); + user_specified_open_timeout_storage = add_ts_to_tv(open_timeout_tv, now); + user_specified_open_timeout_at = &user_specified_open_timeout_storage; + } + + /* start of hostname resolution */ + if (arg->family_size == 1) { + arg->wait = -1; + arg->getaddrinfo_shared = NULL; + + int family = arg->families[0]; + VALUE t = NIL_P(open_timeout) ? resolv_timeout : open_timeout; + + arg->remote.res = rsock_addrinfo( + arg->remote.host, + arg->remote.serv, + family, + SOCK_STREAM, + 0, + t + ); + + if (family == AF_INET6) { + resolution_store.v6.ai = arg->remote.res->ai; + resolution_store.v6.finished = true; + resolution_store.v4.finished = true; + } else if (family == AF_INET) { + resolution_store.v4.ai = arg->remote.res->ai; + resolution_store.v4.finished = true; + resolution_store.v6.finished = true; + } + resolution_store.is_all_finished = true; + } else { + if (pipe(pipefd) != 0) rb_syserr_fail(errno, "pipe(2)"); + hostname_resolution_waiter = pipefd[0]; + int waiter_flags = fcntl(hostname_resolution_waiter, F_GETFL, 0); + if (waiter_flags < 0) rb_syserr_fail(errno, "fcntl(2)"); + if ((fcntl(hostname_resolution_waiter, F_SETFL, waiter_flags | O_NONBLOCK)) < 0) { + rb_syserr_fail(errno, "fcntl(2)"); + } + arg->wait = hostname_resolution_waiter; + hostname_resolution_notifier = pipefd[1]; + + arg->getaddrinfo_shared = allocate_fast_fallback_getaddrinfo_shared(arg->family_size); + if (!arg->getaddrinfo_shared) rb_syserr_fail(errno, "calloc(3)"); + + rb_nativethread_lock_initialize(&arg->getaddrinfo_shared->lock); + arg->getaddrinfo_shared->notify = hostname_resolution_notifier; + + arg->getaddrinfo_shared->node = arg->hostp ? ruby_strdup(arg->hostp) : NULL; + arg->getaddrinfo_shared->service = arg->portp ? ruby_strdup(arg->portp) : NULL; + arg->getaddrinfo_shared->refcount = arg->family_size + 1; + + for (int i = 0; i < arg->family_size; i++) { + arg->getaddrinfo_entries[i] = &arg->getaddrinfo_shared->getaddrinfo_entries[i]; + arg->getaddrinfo_entries[i]->shared = arg->getaddrinfo_shared; + + struct addrinfo getaddrinfo_hints[arg->family_size]; + + allocate_fast_fallback_getaddrinfo_hints( + &getaddrinfo_hints[i], + arg->families[i], + remote_addrinfo_hints, + arg->additional_flags + ); + + arg->getaddrinfo_entries[i]->hints = getaddrinfo_hints[i]; + arg->getaddrinfo_entries[i]->ai = NULL; + arg->getaddrinfo_entries[i]->family = arg->families[i]; + arg->getaddrinfo_entries[i]->refcount = 2; + arg->getaddrinfo_entries[i]->has_syserr = false; + arg->getaddrinfo_entries[i]->test_sleep_ms = 0; + arg->getaddrinfo_entries[i]->test_ecode = 0; + + /* for testing HEv2 */ + if (!NIL_P(test_mode_settings) && RB_TYPE_P(test_mode_settings, T_HASH)) { + const char *family_sym = arg->families[i] == AF_INET6 ? "ipv6" : "ipv4"; + + VALUE test_delay_setting = rb_hash_aref(test_mode_settings, ID2SYM(rb_intern("delay"))); + if (!NIL_P(test_delay_setting)) { + VALUE rb_test_delay_ms = rb_hash_aref(test_delay_setting, ID2SYM(rb_intern(family_sym))); + long test_delay_ms = NIL_P(rb_test_delay_ms) ? 0 : rb_test_delay_ms; + arg->getaddrinfo_entries[i]->test_sleep_ms = test_delay_ms; + } + + VALUE test_error_setting = rb_hash_aref(test_mode_settings, ID2SYM(rb_intern("error"))); + if (!NIL_P(test_error_setting)) { + VALUE rb_test_ecode = rb_hash_aref(test_error_setting, ID2SYM(rb_intern(family_sym))); + if (!NIL_P(rb_test_ecode)) { + arg->getaddrinfo_entries[i]->test_ecode = NUM2INT(rb_test_ecode); + } + } + } + + if (raddrinfo_pthread_create(&threads[i], fork_safe_do_fast_fallback_getaddrinfo, arg->getaddrinfo_entries[i]) != 0) { + rsock_raise_resolution_error("getaddrinfo(3)", EAI_AGAIN); + } + } + + if (NIL_P(resolv_timeout)) { + user_specified_resolv_timeout_storage = (struct timeval){ -1, -1 }; + } else { + struct timeval resolv_timeout_tv = rb_time_interval(resolv_timeout); + user_specified_resolv_timeout_storage = add_ts_to_tv(resolv_timeout_tv, now); + } + user_specified_resolv_timeout_at = &user_specified_resolv_timeout_storage; + } + + while (true) { + /* start of connection */ + if (any_addrinfos(&resolution_store) && + !resolution_delay_expires_at && + !connection_attempt_delay_expires_at) { + while ((remote_ai = pick_addrinfo(&resolution_store, last_family))) { + int fd = -1; + + #if !defined(INET6) && defined(AF_INET6) + if (remote_ai->ai_family == AF_INET6) { + if (any_addrinfos(&resolution_store)) continue; + if (!in_progress_fds(arg->connection_attempt_fds_size)) break; + if (resolution_store.is_all_finished) break; + + if (local_status < 0) { + host = arg->local.host; + serv = arg->local.serv; + } else { + host = arg->remote.host; + serv = arg->remote.serv; + } + if (last_error.type == RESOLUTION_ERROR) { + rsock_raise_resolution_error(syscall, last_error.ecode); + } else { + rsock_syserr_fail_host_port(last_error.ecode, syscall, host, serv); + } + } + #endif + + local_ai = NULL; + + if (arg->local.res) { + for (local_ai = arg->local.res->ai; local_ai; local_ai = local_ai->ai_next) { + if (local_ai->ai_family == remote_ai->ai_family) break; + } + if (!local_ai) { + if (any_addrinfos(&resolution_store)) continue; + if (in_progress_fds(arg->connection_attempt_fds_size)) break; + if (!resolution_store.is_all_finished) break; + + /* Use a different family local address if no choice, this + * will cause EAFNOSUPPORT. */ + rsock_syserr_fail_host_port(EAFNOSUPPORT, syscall, arg->local.host, arg->local.serv); + } + } + + status = rsock_socket(remote_ai->ai_family, remote_ai->ai_socktype, remote_ai->ai_protocol); + syscall = "socket(2)"; + + if (status < 0) { + last_error.type = SYSCALL_ERROR; + last_error.ecode = errno; + + if (any_addrinfos(&resolution_store)) continue; + if (in_progress_fds(arg->connection_attempt_fds_size)) break; + if (!resolution_store.is_all_finished) break; + + if (local_status < 0) { + host = arg->local.host; + serv = arg->local.serv; + } else { + host = arg->remote.host; + serv = arg->remote.serv; + } + if (last_error.type == RESOLUTION_ERROR) { + rsock_raise_resolution_error(syscall, last_error.ecode); + } else { + rsock_syserr_fail_host_port(last_error.ecode, syscall, host, serv); + } + } + + fd = status; + + if (local_ai) { + #if !defined(_WIN32) && !defined(__CYGWIN__) + status = 1; + if ((setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&status, (socklen_t)sizeof(status))) < 0) { + rb_syserr_fail(errno, "setsockopt(2)"); + } + #endif + status = bind(fd, local_ai->ai_addr, local_ai->ai_addrlen); + local_status = status; + syscall = "bind(2)"; + + if (status < 0) { + last_error.type = SYSCALL_ERROR; + last_error.ecode = errno; + close(fd); + + if (any_addrinfos(&resolution_store)) continue; + if (in_progress_fds(arg->connection_attempt_fds_size)) break; + if (!resolution_store.is_all_finished) break; + + if (local_status < 0) { + host = arg->local.host; + serv = arg->local.serv; + } else { + host = arg->remote.host; + serv = arg->remote.serv; + } + if (last_error.type == RESOLUTION_ERROR) { + rsock_raise_resolution_error(syscall, last_error.ecode); + } else { + rsock_syserr_fail_host_port(last_error.ecode, syscall, host, serv); + } + } + } + + syscall = "connect(2)"; + + if (any_addrinfos(&resolution_store) || + in_progress_fds(arg->connection_attempt_fds_size) || + !resolution_store.is_all_finished) { + socket_nonblock_set(fd); + status = connect(fd, remote_ai->ai_addr, remote_ai->ai_addrlen); + last_family = remote_ai->ai_family; + } else { + VALUE timeout = Qnil; + + if (!NIL_P(open_timeout)) { + VALUE elapsed = rb_funcall(current_clocktime(), '-', 1, starts_at); + timeout = rb_funcall(open_timeout, '-', 1, elapsed); + + if (rb_funcall(timeout, '<', 1, INT2FIX(0)) == Qtrue) { + rsock_raise_user_specified_timeout(NULL, arg->remote.host, arg->remote.serv); + } + } + if (NIL_P(timeout)) { + if (!NIL_P(connect_timeout)) { + user_specified_connect_timeout_storage = rb_time_interval(connect_timeout); + user_specified_connect_timeout_at = &user_specified_connect_timeout_storage; + } + timeout = + (user_specified_connect_timeout_at && is_infinity(*user_specified_connect_timeout_at)) ? + Qnil : tv_to_seconds(user_specified_connect_timeout_at); + } + + io = arg->io = rsock_init_sock(arg->self, fd); + status = rsock_connect(io, remote_ai->ai_addr, remote_ai->ai_addrlen, 0, timeout); + } + + if (status == 0) { + connected_fd = fd; + break; + } + + if (errno == EINPROGRESS) { + if (current_capacity == arg->connection_attempt_fds_size) { + current_capacity = reallocate_connection_attempt_fds( + &arg->connection_attempt_fds, + current_capacity, + additional_capacity + ); + } + arg->connection_attempt_fds[arg->connection_attempt_fds_size] = fd; + (arg->connection_attempt_fds_size)++; + + set_timeout_tv(&connection_attempt_delay_strage, 250, now); + connection_attempt_delay_expires_at = &connection_attempt_delay_strage; + + if (!any_addrinfos(&resolution_store)) { + if (NIL_P(connect_timeout)) { + user_specified_connect_timeout_storage = (struct timeval){ -1, -1 }; + } else { + struct timeval connect_timeout_tv = rb_time_interval(connect_timeout); + user_specified_connect_timeout_storage = add_ts_to_tv(connect_timeout_tv, now); + } + user_specified_connect_timeout_at = &user_specified_connect_timeout_storage; + } + + break; + } + + last_error.type = SYSCALL_ERROR; + last_error.ecode = errno; + + if (NIL_P(io)) { + close(fd); + } else { + rb_io_close(io); + } + + if (any_addrinfos(&resolution_store)) continue; + if (in_progress_fds(arg->connection_attempt_fds_size)) break; + if (!resolution_store.is_all_finished) break; + + if (local_status < 0) { + host = arg->local.host; + serv = arg->local.serv; + } else { + host = arg->remote.host; + serv = arg->remote.serv; + } + if (last_error.type == RESOLUTION_ERROR) { + rsock_raise_resolution_error(syscall, last_error.ecode); + } else { + rsock_syserr_fail_host_port(last_error.ecode, syscall, host, serv); + } + } + } + + if (connected_fd >= 0) break; + + ends_at = select_expires_at( + &resolution_store, + resolution_delay_expires_at, + connection_attempt_delay_expires_at, + user_specified_resolv_timeout_at, + user_specified_connect_timeout_at, + user_specified_open_timeout_at + ); + if (ends_at) { + delay = tv_to_timeout(ends_at, now); + delay_p = &delay; + } else { + if (((resolution_store.v6.finished && !resolution_store.v4.finished) || + (resolution_store.v4.finished && !resolution_store.v6.finished)) && + !any_addrinfos(&resolution_store) && + !in_progress_fds(arg->connection_attempt_fds_size)) { + /* A limited timeout is introduced to prevent select(2) from hanging when it is exclusively + * waiting for name resolution and write(2) failure occurs in a child thread. */ + delay.tv_sec = 0; + delay.tv_usec = 50000; + delay_p = &delay; + } else { + delay_p = NULL; + } + } + + nfds = 0; + rb_fd_zero(&arg->writefds); + if (in_progress_fds(arg->connection_attempt_fds_size)) { + int n = 0; + for (int i = 0; i < arg->connection_attempt_fds_size; i++) { + int cfd = arg->connection_attempt_fds[i]; + if (cfd < 0) continue; + if (cfd > n) n = cfd; + rb_fd_set(cfd, &arg->writefds); + } + if (n > 0) n++; + nfds = n; + } + + rb_fd_zero(&arg->readfds); + if (arg->family_size > 1) { + rb_fd_set(hostname_resolution_waiter, &arg->readfds); + + if ((hostname_resolution_waiter + 1) > nfds) { + nfds = hostname_resolution_waiter + 1; + } + } + + status = rb_thread_fd_select(nfds, &arg->readfds, &arg->writefds, NULL, delay_p); + + now = current_clocktime_ts(); + if (is_timeout_tv(resolution_delay_expires_at, now)) { + resolution_delay_expires_at = NULL; + } + if (is_timeout_tv(connection_attempt_delay_expires_at, now)) { + connection_attempt_delay_expires_at = NULL; + } + + if (status < 0 && (errno && errno != EINTR)) rb_syserr_fail(errno, "select(2)"); + + if (status > 0) { + /* check for connection */ + if (in_progress_fds(arg->connection_attempt_fds_size)) { + for (int i = 0; i < arg->connection_attempt_fds_size; i++) { + int fd = arg->connection_attempt_fds[i]; + if (fd < 0 || !rb_fd_isset(fd, &arg->writefds)) continue; + + int err; + socklen_t len = sizeof(err); + + status = getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &len); + + if (status < 0) { + last_error.type = SYSCALL_ERROR; + last_error.ecode = errno; + close(fd); + + if (any_addrinfos(&resolution_store)) continue; + if (in_progress_fds(arg->connection_attempt_fds_size)) break; + if (!resolution_store.is_all_finished) break; + + if (local_status < 0) { + host = arg->local.host; + serv = arg->local.serv; + } else { + host = arg->remote.host; + serv = arg->remote.serv; + } + if (last_error.type == RESOLUTION_ERROR) { + rsock_raise_resolution_error(syscall, last_error.ecode); + } else { + rsock_syserr_fail_host_port(last_error.ecode, syscall, host, serv); + } + } + + if (err == 0) { /* success */ + remove_connection_attempt_fd( + arg->connection_attempt_fds, + &arg->connection_attempt_fds_size, + fd + ); + connected_fd = fd; + break; + } else { /* fail */ + close(fd); + remove_connection_attempt_fd( + arg->connection_attempt_fds, + &arg->connection_attempt_fds_size, + fd + ); + last_error.type = SYSCALL_ERROR; + last_error.ecode = err; + } + } + + if (connected_fd >= 0) break; + + if (!in_progress_fds(arg->connection_attempt_fds_size)) { + if (!any_addrinfos(&resolution_store) && resolution_store.is_all_finished) { + if (local_status < 0) { + host = arg->local.host; + serv = arg->local.serv; + } else { + host = arg->remote.host; + serv = arg->remote.serv; + } + if (last_error.type == RESOLUTION_ERROR) { + rsock_raise_resolution_error(syscall, last_error.ecode); + } else { + rsock_syserr_fail_host_port(last_error.ecode, syscall, host, serv); + } + } + connection_attempt_delay_expires_at = NULL; + user_specified_connect_timeout_at = NULL; + } + } + + /* check for hostname resolution */ + if (!resolution_store.is_all_finished && rb_fd_isset(hostname_resolution_waiter, &arg->readfds)) { + while (true) { + resolved_type_size = read( + hostname_resolution_waiter, + resolved_type, + sizeof(resolved_type) - 1 + ); + + if (resolved_type_size > 0) { + resolved_type[resolved_type_size] = '\0'; + + if (resolved_type[0] == IPV6_HOSTNAME_RESOLVED) { + resolution_store.v6.finished = true; + + if (arg->getaddrinfo_entries[IPV6_ENTRY_POS]->err && + arg->getaddrinfo_entries[IPV6_ENTRY_POS]->err != EAI_ADDRFAMILY) { + if (!resolution_store.v4.finished || resolution_store.v4.has_error) { + last_error.type = RESOLUTION_ERROR; + last_error.ecode = arg->getaddrinfo_entries[IPV6_ENTRY_POS]->err; + syscall = "getaddrinfo(3)"; + } + resolution_store.v6.has_error = true; + } else { + resolution_store.v6.ai = arg->getaddrinfo_entries[IPV6_ENTRY_POS]->ai; + } + if (resolution_store.v4.finished) { + resolution_store.is_all_finished = true; + resolution_delay_expires_at = NULL; + user_specified_resolv_timeout_at = NULL; + break; + } + } else if (resolved_type[0] == IPV4_HOSTNAME_RESOLVED) { + resolution_store.v4.finished = true; + + if (arg->getaddrinfo_entries[IPV4_ENTRY_POS]->err) { + if (!resolution_store.v6.finished || resolution_store.v6.has_error) { + last_error.type = RESOLUTION_ERROR; + last_error.ecode = arg->getaddrinfo_entries[IPV4_ENTRY_POS]->err; + syscall = "getaddrinfo(3)"; + } + resolution_store.v4.has_error = true; + } else { + resolution_store.v4.ai = arg->getaddrinfo_entries[IPV4_ENTRY_POS]->ai; + } + + if (resolution_store.v6.finished) { + resolution_store.is_all_finished = true; + resolution_delay_expires_at = NULL; + user_specified_resolv_timeout_at = NULL; + break; + } + } else { + /* Retry to read from hostname_resolution_waiter */ + } + } else if (resolved_type_size < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) { + errno = 0; + break; + } else { + /* Retry to read from hostname_resolution_waiter */ + } + + if (!resolution_store.v6.finished && + resolution_store.v4.finished && + !resolution_store.v4.has_error) { + set_timeout_tv(&resolution_delay_storage, 50, now); + resolution_delay_expires_at = &resolution_delay_storage; + } + } + } + + status = 0; + } + + /* For cases where write(2) fails in child threads */ + if (!resolution_store.is_all_finished) { + if (!resolution_store.v6.finished && arg->getaddrinfo_entries[IPV6_ENTRY_POS]->has_syserr) { + resolution_store.v6.finished = true; + + if (arg->getaddrinfo_entries[IPV6_ENTRY_POS]->err) { + if (!resolution_store.v4.finished || resolution_store.v4.has_error) { + last_error.type = RESOLUTION_ERROR; + last_error.ecode = arg->getaddrinfo_entries[IPV6_ENTRY_POS]->err; + syscall = "getaddrinfo(3)"; + } + resolution_store.v6.has_error = true; + } else { + resolution_store.v6.ai = arg->getaddrinfo_entries[IPV6_ENTRY_POS]->ai; + } + + if (resolution_store.v4.finished) { + resolution_store.is_all_finished = true; + resolution_delay_expires_at = NULL; + user_specified_resolv_timeout_at = NULL; + } + } + if (!resolution_store.v4.finished && arg->getaddrinfo_entries[IPV4_ENTRY_POS]->has_syserr) { + resolution_store.v4.finished = true; + + if (arg->getaddrinfo_entries[IPV4_ENTRY_POS]->err) { + if (!resolution_store.v6.finished || resolution_store.v6.has_error) { + last_error.type = RESOLUTION_ERROR; + last_error.ecode = arg->getaddrinfo_entries[IPV4_ENTRY_POS]->err; + syscall = "getaddrinfo(3)"; + } + resolution_store.v4.has_error = true; + } else { + resolution_store.v4.ai = arg->getaddrinfo_entries[IPV4_ENTRY_POS]->ai; + } + + if (resolution_store.v6.finished) { + resolution_store.is_all_finished = true; + resolution_delay_expires_at = NULL; + user_specified_resolv_timeout_at = NULL; + } else { + set_timeout_tv(&resolution_delay_storage, 50, now); + resolution_delay_expires_at = &resolution_delay_storage; + } + } + } + + if (is_timeout_tv(user_specified_open_timeout_at, now)) { + rsock_raise_user_specified_timeout(NULL, arg->remote.host, arg->remote.serv); + } + + if (!any_addrinfos(&resolution_store)) { + if (!in_progress_fds(arg->connection_attempt_fds_size) && + resolution_store.is_all_finished) { + if (local_status < 0) { + host = arg->local.host; + serv = arg->local.serv; + } else { + host = arg->remote.host; + serv = arg->remote.serv; + } + if (last_error.type == RESOLUTION_ERROR) { + rsock_raise_resolution_error(syscall, last_error.ecode); + } else { + rsock_syserr_fail_host_port(last_error.ecode, syscall, host, serv); + } + } + + if ((is_timeout_tv(user_specified_resolv_timeout_at, now) || + resolution_store.is_all_finished) && + (is_timeout_tv(user_specified_connect_timeout_at, now) || + !in_progress_fds(arg->connection_attempt_fds_size))) { + rsock_raise_user_specified_timeout(NULL, arg->remote.host, arg->remote.serv); + } + } + } + + if (NIL_P(arg->io)) { + /* create new instance */ + arg->io = rsock_init_sock(arg->self, connected_fd); + } + + return arg->io; +} + +static VALUE +fast_fallback_inetsock_cleanup(VALUE v) +{ + struct fast_fallback_inetsock_arg *arg = (void *)v; + struct fast_fallback_getaddrinfo_shared *getaddrinfo_shared = arg->getaddrinfo_shared; + + if (arg->remote.res) { + rb_freeaddrinfo(arg->remote.res); + arg->remote.res = 0; + } + if (arg->local.res) { + rb_freeaddrinfo(arg->local.res); + arg->local.res = 0; + } + + if (arg->wait != -1) close(arg->wait); + + if (getaddrinfo_shared) { + if (getaddrinfo_shared->notify != -1) close(getaddrinfo_shared->notify); + getaddrinfo_shared->notify = -1; + + int shared_need_free = 0; + struct addrinfo *ais[arg->family_size]; + for (int i = 0; i < arg->family_size; i++) ais[i] = NULL; + + rb_nativethread_lock_lock(&getaddrinfo_shared->lock); + { + for (int i = 0; i < arg->family_size; i++) { + struct fast_fallback_getaddrinfo_entry *getaddrinfo_entry = arg->getaddrinfo_entries[i]; + + if (!getaddrinfo_entry) continue; + + if (--(getaddrinfo_entry->refcount) == 0) { + ais[i] = getaddrinfo_entry->ai; + getaddrinfo_entry->ai = NULL; + } + } + if (--(getaddrinfo_shared->refcount) == 0) { + shared_need_free = 1; + } + } + rb_nativethread_lock_unlock(&getaddrinfo_shared->lock); + + for (int i = 0; i < arg->family_size; i++) { + if (ais[i]) freeaddrinfo(ais[i]); + } + if (getaddrinfo_shared && shared_need_free) { + free_fast_fallback_getaddrinfo_shared(&getaddrinfo_shared); + } + } + + int connection_attempt_fd; + + for (int i = 0; i < arg->connection_attempt_fds_size; i++) { + connection_attempt_fd = arg->connection_attempt_fds[i]; + + if (connection_attempt_fd >= 0) { + int error = 0; + socklen_t len = sizeof(error); + getsockopt(connection_attempt_fd, SOL_SOCKET, SO_ERROR, &error, &len); + if (error == 0) shutdown(connection_attempt_fd, SHUT_RDWR); + close(connection_attempt_fd); + } + } + + if (arg->readfds.fdset) rb_fd_term(&arg->readfds); + if (arg->writefds.fdset) rb_fd_term(&arg->writefds); + + if (arg->connection_attempt_fds) { + free(arg->connection_attempt_fds); + arg->connection_attempt_fds = NULL; + } + + return Qnil; +} + +VALUE +rsock_init_inetsock( + VALUE self, VALUE remote_host, VALUE remote_serv, + VALUE local_host, VALUE local_serv, int type, + VALUE resolv_timeout, VALUE connect_timeout, VALUE open_timeout, + VALUE fast_fallback, VALUE test_mode_settings) +{ + if (!NIL_P(open_timeout) && (!NIL_P(resolv_timeout) || !NIL_P(connect_timeout))) { + rb_raise(rb_eArgError, "Cannot specify open_timeout along with connect_timeout or resolv_timeout"); + } + + if (type == INET_CLIENT && FAST_FALLBACK_INIT_INETSOCK_IMPL == 1 && RTEST(fast_fallback)) { + struct rb_addrinfo *local_res = NULL; + char *hostp, *portp, *local_portp; + char hbuf[NI_MAXHOST], pbuf[NI_MAXSERV], local_pbuf[NI_MAXSERV]; + int additional_flags = 0; + int local_flags = 0; + hostp = raddrinfo_host_str(remote_host, hbuf, sizeof(hbuf), &additional_flags); + portp = raddrinfo_port_str(remote_serv, pbuf, sizeof(pbuf), &additional_flags); + local_portp = raddrinfo_port_str(local_serv, local_pbuf, sizeof(local_pbuf), &local_flags); + + if (!is_specified_ip_address(hostp) && !is_local_port_fixed(local_portp)) { + int target_families[2] = { 0, 0 }; + int resolving_family_size = 0; + + /* + * Maybe also accept a local address + */ + if (!NIL_P(local_host) || !NIL_P(local_serv)) { + VALUE t = NIL_P(open_timeout) ? resolv_timeout : open_timeout; + local_res = rsock_addrinfo( + local_host, + local_serv, + AF_UNSPEC, + SOCK_STREAM, + 0, + t + ); + + struct addrinfo *tmp_p = local_res->ai; + for (; tmp_p != NULL; tmp_p = tmp_p->ai_next) { + if (target_families[0] == 0 && tmp_p->ai_family == AF_INET6) { + target_families[0] = AF_INET6; + resolving_family_size++; + } + if (target_families[1] == 0 && tmp_p->ai_family == AF_INET) { + target_families[1] = AF_INET; + resolving_family_size++; + } + } + } else { + resolving_family_size = 2; + target_families[0] = AF_INET6; + target_families[1] = AF_INET; + } + + struct fast_fallback_inetsock_arg fast_fallback_arg; + memset(&fast_fallback_arg, 0, sizeof(fast_fallback_arg)); + + fast_fallback_arg.self = self; + fast_fallback_arg.io = Qnil; + fast_fallback_arg.remote.host = remote_host; + fast_fallback_arg.remote.serv = remote_serv; + fast_fallback_arg.remote.res = 0; + fast_fallback_arg.local.host = local_host; + fast_fallback_arg.local.serv = local_serv; + fast_fallback_arg.local.res = local_res; + fast_fallback_arg.type = type; + fast_fallback_arg.resolv_timeout = resolv_timeout; + fast_fallback_arg.connect_timeout = connect_timeout; + fast_fallback_arg.open_timeout = open_timeout; + fast_fallback_arg.hostp = hostp; + fast_fallback_arg.portp = portp; + fast_fallback_arg.additional_flags = additional_flags; + + int resolving_families[resolving_family_size]; + int resolving_family_index = 0; + for (int i = 0; 2 > i; i++) { + if (target_families[i] != 0) { + resolving_families[resolving_family_index] = target_families[i]; + resolving_family_index++; + } + } + fast_fallback_arg.families = resolving_families; + fast_fallback_arg.family_size = resolving_family_size; + fast_fallback_arg.test_mode_settings = test_mode_settings; + + rb_fd_init(&fast_fallback_arg.readfds); + rb_fd_init(&fast_fallback_arg.writefds); + + return rb_ensure(init_fast_fallback_inetsock_internal, (VALUE)&fast_fallback_arg, + fast_fallback_inetsock_cleanup, (VALUE)&fast_fallback_arg); + } + } + + struct inetsock_arg arg; + arg.self = self; + arg.io = Qnil; + 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.resolv_timeout = resolv_timeout; + arg.connect_timeout = connect_timeout; + arg.open_timeout = open_timeout; + + return rb_ensure(init_inetsock_internal, (VALUE)&arg, + inetsock_cleanup, (VALUE)&arg); +} + +#endif + +static ID id_numeric, id_hostname; + +int +rsock_revlookup_flag(VALUE revlookup, int *norevlookup) +{ +#define return_norevlookup(x) {*norevlookup = (x); return 1;} + ID id; + + switch (revlookup) { + case Qtrue: return_norevlookup(0); + case Qfalse: return_norevlookup(1); + case Qnil: break; + default: + Check_Type(revlookup, T_SYMBOL); + id = SYM2ID(revlookup); + if (id == id_numeric) return_norevlookup(1); + if (id == id_hostname) return_norevlookup(0); + rb_raise(rb_eArgError, "invalid reverse_lookup flag: :%s", rb_id2name(id)); + } + return 0; +#undef return_norevlookup +} + +/* + * call-seq: + * ipsocket.inspect -> string + * + * Return a string describing this IPSocket object. + */ +static VALUE +ip_inspect(VALUE sock) +{ + VALUE str = rb_call_super(0, 0); + rb_io_t *fptr = RFILE(sock)->fptr; + union_sockaddr addr; + socklen_t len = (socklen_t)sizeof addr; + ID id; + if (fptr && fptr->fd >= 0 && + getsockname(fptr->fd, &addr.addr, &len) >= 0 && + (id = rsock_intern_family(addr.addr.sa_family)) != 0) { + VALUE family = rb_id2str(id); + char hbuf[1024], pbuf[1024]; + long slen = RSTRING_LEN(str); + const char last = (slen > 1 && RSTRING_PTR(str)[slen - 1] == '>') ? + (--slen, '>') : 0; + str = rb_str_subseq(str, 0, slen); + rb_str_cat_cstr(str, ", "); + rb_str_append(str, family); + if (!rb_getnameinfo(&addr.addr, len, hbuf, sizeof(hbuf), + pbuf, sizeof(pbuf), NI_NUMERICHOST | NI_NUMERICSERV)) { + rb_str_cat_cstr(str, ", "); + rb_str_cat_cstr(str, hbuf); + rb_str_cat_cstr(str, ", "); + rb_str_cat_cstr(str, pbuf); + } + if (last) rb_str_cat(str, &last, 1); + } + return str; +} + +/* + * call-seq: + * ipsocket.addr([reverse_lookup]) => [address_family, port, hostname, numeric_address] + * + * Returns the local address as an array which contains + * address_family, port, hostname and numeric_address. + * + * If +reverse_lookup+ is +true+ or +:hostname+, + * hostname is obtained from numeric_address using reverse lookup. + * Or if it is +false+, or +:numeric+, + * hostname is the same as numeric_address. + * Or if it is +nil+ or omitted, obeys to +ipsocket.do_not_reverse_lookup+. + * See +Socket.getaddrinfo+ also. + * + * TCPSocket.open("www.ruby-lang.org", 80) {|sock| + * p sock.addr #=> ["AF_INET", 49429, "hal", "192.168.0.128"] + * p sock.addr(true) #=> ["AF_INET", 49429, "hal", "192.168.0.128"] + * p sock.addr(false) #=> ["AF_INET", 49429, "192.168.0.128", "192.168.0.128"] + * p sock.addr(:hostname) #=> ["AF_INET", 49429, "hal", "192.168.0.128"] + * p sock.addr(:numeric) #=> ["AF_INET", 49429, "192.168.0.128", "192.168.0.128"] + * } + * + */ +static VALUE +ip_addr(int argc, VALUE *argv, VALUE sock) +{ + union_sockaddr addr; + socklen_t len = (socklen_t)sizeof addr; + int norevlookup; + + if (argc < 1 || !rsock_revlookup_flag(argv[0], &norevlookup)) + norevlookup = rb_io_mode(sock) & FMODE_NOREVLOOKUP; + if (getsockname(rb_io_descriptor(sock), &addr.addr, &len) < 0) + rb_sys_fail("getsockname(2)"); + return rsock_ipaddr(&addr.addr, len, norevlookup); +} + +/* + * call-seq: + * ipsocket.peeraddr([reverse_lookup]) => [address_family, port, hostname, numeric_address] + * + * Returns the remote address as an array which contains + * address_family, port, hostname and numeric_address. + * It is defined for connection oriented socket such as TCPSocket. + * + * If +reverse_lookup+ is +true+ or +:hostname+, + * hostname is obtained from numeric_address using reverse lookup. + * Or if it is +false+, or +:numeric+, + * hostname is the same as numeric_address. + * Or if it is +nil+ or omitted, obeys to +ipsocket.do_not_reverse_lookup+. + * See +Socket.getaddrinfo+ also. + * + * TCPSocket.open("www.ruby-lang.org", 80) {|sock| + * p sock.peeraddr #=> ["AF_INET", 80, "carbon.ruby-lang.org", "221.186.184.68"] + * p sock.peeraddr(true) #=> ["AF_INET", 80, "carbon.ruby-lang.org", "221.186.184.68"] + * p sock.peeraddr(false) #=> ["AF_INET", 80, "221.186.184.68", "221.186.184.68"] + * p sock.peeraddr(:hostname) #=> ["AF_INET", 80, "carbon.ruby-lang.org", "221.186.184.68"] + * p sock.peeraddr(:numeric) #=> ["AF_INET", 80, "221.186.184.68", "221.186.184.68"] + * } + * + */ +static VALUE +ip_peeraddr(int argc, VALUE *argv, VALUE sock) +{ + union_sockaddr addr; + socklen_t len = (socklen_t)sizeof addr; + int norevlookup; + + if (argc < 1 || !rsock_revlookup_flag(argv[0], &norevlookup)) + norevlookup = rb_io_mode(sock) & FMODE_NOREVLOOKUP; + if (getpeername(rb_io_descriptor(sock), &addr.addr, &len) < 0) + rb_sys_fail("getpeername(2)"); + return rsock_ipaddr(&addr.addr, len, norevlookup); +} + +/* + * call-seq: + * ipsocket.recvfrom(maxlen) => [mesg, ipaddr] + * ipsocket.recvfrom(maxlen, flags) => [mesg, ipaddr] + * + * Receives a message and return the message as a string and + * an address which the message come from. + * + * _maxlen_ is the maximum number of bytes to receive. + * + * _flags_ should be a bitwise OR of Socket::MSG_* constants. + * + * ipaddr is the same as IPSocket#{peeraddr,addr}. + * + * u1 = UDPSocket.new + * u1.bind("127.0.0.1", 4913) + * u2 = UDPSocket.new + * u2.send "uuuu", 0, "127.0.0.1", 4913 + * p u1.recvfrom(10) #=> ["uuuu", ["AF_INET", 33230, "localhost", "127.0.0.1"]] + * + */ +static VALUE +ip_recvfrom(int argc, VALUE *argv, VALUE sock) +{ + return rsock_s_recvfrom(sock, argc, argv, RECV_IP); +} + +/* + * call-seq: + * IPSocket.getaddress(host) => ipaddress + * + * Lookups the IP address of _host_. + * + * require 'socket' + * + * IPSocket.getaddress("localhost") #=> "127.0.0.1" + * IPSocket.getaddress("ip6-localhost") #=> "::1" + * + */ +static VALUE +ip_s_getaddress(VALUE obj, VALUE host) +{ + union_sockaddr addr; + struct rb_addrinfo *res = rsock_addrinfo(host, Qnil, AF_UNSPEC, SOCK_STREAM, 0, Qnil); + socklen_t len = res->ai->ai_addrlen; + + /* just take the first one */ + memcpy(&addr, res->ai->ai_addr, len); + rb_freeaddrinfo(res); + + return rsock_make_ipaddr(&addr.addr, len); +} + +void +rsock_init_ipsocket(void) +{ + /* + * Document-class: IPSocket < BasicSocket + * + * IPSocket is the super class of TCPSocket and UDPSocket. + */ + rb_cIPSocket = rb_define_class("IPSocket", rb_cBasicSocket); + rb_define_method(rb_cIPSocket, "inspect", ip_inspect, 0); + rb_define_method(rb_cIPSocket, "addr", ip_addr, -1); + rb_define_method(rb_cIPSocket, "peeraddr", ip_peeraddr, -1); + rb_define_method(rb_cIPSocket, "recvfrom", ip_recvfrom, -1); + rb_define_singleton_method(rb_cIPSocket, "getaddress", ip_s_getaddress, 1); + rb_undef_method(rb_cIPSocket, "getpeereid"); + + id_numeric = rb_intern_const("numeric"); + id_hostname = rb_intern_const("hostname"); +} diff --git a/ext/socket/lib/socket.rb b/ext/socket/lib/socket.rb new file mode 100644 index 0000000000..36fcceaee9 --- /dev/null +++ b/ext/socket/lib/socket.rb @@ -0,0 +1,1820 @@ +# frozen_string_literal: true + +require 'socket.so' + +unless IO.method_defined?(:wait_writable, false) + # It's only required on older Rubies < v3.2: + require 'io/wait' +end + +class Addrinfo + # creates an Addrinfo object from the arguments. + # + # The arguments are interpreted as similar to self. + # + # Addrinfo.tcp("0.0.0.0", 4649).family_addrinfo("www.ruby-lang.org", 80) + # #=> #<Addrinfo: 221.186.184.68:80 TCP (www.ruby-lang.org:80)> + # + # Addrinfo.unix("/tmp/sock").family_addrinfo("/tmp/sock2") + # #=> #<Addrinfo: /tmp/sock2 SOCK_STREAM> + # + def family_addrinfo(*args) + if args.empty? + raise ArgumentError, "no address specified" + elsif Addrinfo === args.first + raise ArgumentError, "too many arguments" if args.length != 1 + addrinfo = args.first + if (self.pfamily != addrinfo.pfamily) || + (self.socktype != addrinfo.socktype) + raise ArgumentError, "Addrinfo type mismatch" + end + addrinfo + elsif self.ip? + raise ArgumentError, "IP address needs host and port but #{args.length} arguments given" if args.length != 2 + host, port = args + Addrinfo.getaddrinfo(host, port, self.pfamily, self.socktype, self.protocol)[0] + elsif self.unix? + raise ArgumentError, "UNIX socket needs single path argument but #{args.length} arguments given" if args.length != 1 + path, = args + Addrinfo.unix(path) + else + raise ArgumentError, "unexpected family" + end + end + + # creates a new Socket connected to the address of +local_addrinfo+. + # + # If _local_addrinfo_ is nil, the address of the socket is not bound. + # + # The _timeout_ specify the seconds for timeout. + # Errno::ETIMEDOUT is raised when timeout occur. + # + # If a block is given the created socket is yielded for each address. + # + def connect_internal(local_addrinfo, timeout=nil) # :yields: socket + sock = Socket.new(self.pfamily, self.socktype, self.protocol) + begin + sock.ipv6only! if self.ipv6? + sock.bind local_addrinfo if local_addrinfo + if timeout + case sock.connect_nonblock(self, exception: false) + when 0 # success or EISCONN, other errors raise + break + when :wait_writable + sock.wait_writable(timeout) or + raise Errno::ETIMEDOUT, "user specified timeout for #{self.ip_address}:#{self.ip_port}" + end while true + else + sock.connect(self) + end + rescue Exception + sock.close + raise + end + if block_given? + begin + yield sock + ensure + sock.close + end + else + sock + end + end + protected :connect_internal + + # :call-seq: + # addrinfo.connect_from([local_addr_args], [opts]) {|socket| ... } + # addrinfo.connect_from([local_addr_args], [opts]) + # + # creates a socket connected to the address of self. + # + # If one or more arguments given as _local_addr_args_, + # it is used as the local address of the socket. + # _local_addr_args_ is given for family_addrinfo to obtain actual address. + # + # If _local_addr_args_ is not given, the local address of the socket is not bound. + # + # The optional last argument _opts_ is options represented by a hash. + # _opts_ may have following options: + # + # [:timeout] specify the timeout in seconds. + # + # If a block is given, it is called with the socket and the value of the block is returned. + # The socket is returned otherwise. + # + # Addrinfo.tcp("www.ruby-lang.org", 80).connect_from("0.0.0.0", 4649) {|s| + # s.print "GET / HTTP/1.0\r\nHost: www.ruby-lang.org\r\n\r\n" + # puts s.read + # } + # + # # Addrinfo object can be taken for the argument. + # Addrinfo.tcp("www.ruby-lang.org", 80).connect_from(Addrinfo.tcp("0.0.0.0", 4649)) {|s| + # s.print "GET / HTTP/1.0\r\nHost: www.ruby-lang.org\r\n\r\n" + # puts s.read + # } + # + def connect_from(*args, timeout: nil, &block) + connect_internal(family_addrinfo(*args), timeout, &block) + end + + # :call-seq: + # addrinfo.connect([opts]) {|socket| ... } + # addrinfo.connect([opts]) + # + # creates a socket connected to the address of self. + # + # The optional argument _opts_ is options represented by a hash. + # _opts_ may have following options: + # + # [:timeout] specify the timeout in seconds. + # + # If a block is given, it is called with the socket and the value of the block is returned. + # The socket is returned otherwise. + # + # Addrinfo.tcp("www.ruby-lang.org", 80).connect {|s| + # s.print "GET / HTTP/1.0\r\nHost: www.ruby-lang.org\r\n\r\n" + # puts s.read + # } + # + def connect(timeout: nil, &block) + connect_internal(nil, timeout, &block) + end + + # :call-seq: + # addrinfo.connect_to([remote_addr_args], [opts]) {|socket| ... } + # addrinfo.connect_to([remote_addr_args], [opts]) + # + # creates a socket connected to _remote_addr_args_ and bound to self. + # + # The optional last argument _opts_ is options represented by a hash. + # _opts_ may have following options: + # + # [:timeout] specify the timeout in seconds. + # + # If a block is given, it is called with the socket and the value of the block is returned. + # The socket is returned otherwise. + # + # Addrinfo.tcp("0.0.0.0", 4649).connect_to("www.ruby-lang.org", 80) {|s| + # s.print "GET / HTTP/1.0\r\nHost: www.ruby-lang.org\r\n\r\n" + # puts s.read + # } + # + def connect_to(*args, timeout: nil, &block) + remote_addrinfo = family_addrinfo(*args) + remote_addrinfo.connect_internal(self, timeout, &block) + end + + # creates a socket bound to self. + # + # If a block is given, it is called with the socket and the value of the block is returned. + # The socket is returned otherwise. + # + # Addrinfo.udp("0.0.0.0", 9981).bind {|s| + # s.local_address.connect {|s| s.send "hello", 0 } + # p s.recv(10) #=> "hello" + # } + # + def bind + sock = Socket.new(self.pfamily, self.socktype, self.protocol) + begin + sock.ipv6only! if self.ipv6? + sock.setsockopt(:SOCKET, :REUSEADDR, 1) + sock.bind(self) + rescue Exception + sock.close + raise + end + if block_given? + begin + yield sock + ensure + sock.close + end + else + sock + end + end + + # creates a listening socket bound to self. + def listen(backlog=Socket::SOMAXCONN) + sock = Socket.new(self.pfamily, self.socktype, self.protocol) + begin + sock.ipv6only! if self.ipv6? + sock.setsockopt(:SOCKET, :REUSEADDR, 1) unless self.pfamily == Socket::PF_UNIX + sock.bind(self) + sock.listen(backlog) + rescue Exception + sock.close + raise + end + if block_given? + begin + yield sock + ensure + sock.close + end + else + sock + end + end + + # iterates over the list of Addrinfo objects obtained by Addrinfo.getaddrinfo. + # + # Addrinfo.foreach(nil, 80) {|x| p x } + # #=> #<Addrinfo: 127.0.0.1:80 TCP (:80)> + # # #<Addrinfo: 127.0.0.1:80 UDP (:80)> + # # #<Addrinfo: [::1]:80 TCP (:80)> + # # #<Addrinfo: [::1]:80 UDP (:80)> + # + def self.foreach(nodename, service, family=nil, socktype=nil, protocol=nil, flags=nil, timeout: nil, &block) + Addrinfo.getaddrinfo(nodename, service, family, socktype, protocol, flags, timeout: timeout).each(&block) + end +end + +class BasicSocket < IO + # Returns an address of the socket suitable for connect in the local machine. + # + # This method returns _self_.local_address, except following condition. + # + # - IPv4 unspecified address (0.0.0.0) is replaced by IPv4 loopback address (127.0.0.1). + # - IPv6 unspecified address (::) is replaced by IPv6 loopback address (::1). + # + # If the local address is not suitable for connect, SocketError is raised. + # IPv4 and IPv6 address which port is 0 is not suitable for connect. + # Unix domain socket which has no path is not suitable for connect. + # + # Addrinfo.tcp("0.0.0.0", 0).listen {|serv| + # p serv.connect_address #=> #<Addrinfo: 127.0.0.1:53660 TCP> + # serv.connect_address.connect {|c| + # s, _ = serv.accept + # p [c, s] #=> [#<Socket:fd 4>, #<Socket:fd 6>] + # } + # } + # + def connect_address + addr = local_address + afamily = addr.afamily + if afamily == Socket::AF_INET + raise SocketError, "unbound IPv4 socket" if addr.ip_port == 0 + if addr.ip_address == "0.0.0.0" + addr = Addrinfo.new(["AF_INET", addr.ip_port, nil, "127.0.0.1"], addr.pfamily, addr.socktype, addr.protocol) + end + elsif defined?(Socket::AF_INET6) && afamily == Socket::AF_INET6 + raise SocketError, "unbound IPv6 socket" if addr.ip_port == 0 + if addr.ip_address == "::" + addr = Addrinfo.new(["AF_INET6", addr.ip_port, nil, "::1"], addr.pfamily, addr.socktype, addr.protocol) + elsif addr.ip_address == "0.0.0.0" # MacOS X 10.4 returns "a.b.c.d" for IPv4-mapped IPv6 address. + addr = Addrinfo.new(["AF_INET6", addr.ip_port, nil, "::1"], addr.pfamily, addr.socktype, addr.protocol) + elsif addr.ip_address == "::ffff:0.0.0.0" # MacOS X 10.6 returns "::ffff:a.b.c.d" for IPv4-mapped IPv6 address. + addr = Addrinfo.new(["AF_INET6", addr.ip_port, nil, "::1"], addr.pfamily, addr.socktype, addr.protocol) + end + elsif defined?(Socket::AF_UNIX) && afamily == Socket::AF_UNIX + raise SocketError, "unbound Unix socket" if addr.unix_path == "" + end + addr + end + + # call-seq: + # basicsocket.sendmsg(mesg, flags=0, dest_sockaddr=nil, *controls) => numbytes_sent + # + # sendmsg sends a message using sendmsg(2) system call in blocking manner. + # + # _mesg_ is a string to send. + # + # _flags_ is bitwise OR of MSG_* constants such as Socket::MSG_OOB. + # + # _dest_sockaddr_ is a destination socket address for connection-less socket. + # It should be a sockaddr such as a result of Socket.sockaddr_in. + # An Addrinfo object can be used too. + # + # _controls_ is a list of ancillary data. + # The element of _controls_ should be Socket::AncillaryData or + # 3-elements array. + # The 3-element array should contains cmsg_level, cmsg_type and data. + # + # The return value, _numbytes_sent_ is an integer which is the number of bytes sent. + # + # sendmsg can be used to implement send_io as follows: + # + # # use Socket::AncillaryData. + # ancdata = Socket::AncillaryData.int(:UNIX, :SOCKET, :RIGHTS, io.fileno) + # sock.sendmsg("a", 0, nil, ancdata) + # + # # use 3-element array. + # ancdata = [:SOCKET, :RIGHTS, [io.fileno].pack("i!")] + # sock.sendmsg("\0", 0, nil, ancdata) + def sendmsg(mesg, flags = 0, dest_sockaddr = nil, *controls) + __sendmsg(mesg, flags, dest_sockaddr, controls) + end + + # call-seq: + # basicsocket.sendmsg_nonblock(mesg, flags=0, dest_sockaddr=nil, *controls, opts={}) => numbytes_sent + # + # sendmsg_nonblock sends a message using sendmsg(2) system call in non-blocking manner. + # + # It is similar to BasicSocket#sendmsg + # but the non-blocking flag is set before the system call + # and it doesn't retry the system call. + # + # By specifying a keyword argument _exception_ to +false+, you can indicate + # that sendmsg_nonblock should not raise an IO::WaitWritable exception, but + # return the symbol +:wait_writable+ instead. + def sendmsg_nonblock(mesg, flags = 0, dest_sockaddr = nil, *controls, + exception: true) + __sendmsg_nonblock(mesg, flags, dest_sockaddr, controls, exception) + end + + # call-seq: + # basicsocket.recv_nonblock(maxlen [, flags [, buf [, options ]]]) => 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 nil. + # In most cases it means the connection was closed, but for UDP connections + # it may mean an empty packet was received, as the underlying API makes + # it impossible to distinguish these two cases. + # + # === Parameters + # * +maxlen+ - the number of bytes to receive from the socket + # * +flags+ - zero or more of the +MSG_+ options + # * +buf+ - destination String buffer + # * +options+ - keyword hash, supporting `exception: false` + # + # === 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::EAGAIN, + # it is extended by IO::WaitReadable. + # So IO::WaitReadable can be used to rescue the exceptions for retrying recv_nonblock. + # + # By specifying a keyword argument _exception_ to +false+, you can indicate + # that recv_nonblock should not raise an IO::WaitReadable exception, but + # return the symbol +:wait_readable+ instead. + # + # === See + # * Socket#recvfrom + def recv_nonblock(len, flag = 0, str = nil, exception: true) + __recv_nonblock(len, flag, str, exception) + end + + # call-seq: + # basicsocket.recvmsg(maxmesglen=nil, flags=0, maxcontrollen=nil, opts={}) => [mesg, sender_addrinfo, rflags, *controls] + # + # recvmsg receives a message using recvmsg(2) system call in blocking manner. + # + # _maxmesglen_ is the maximum length of mesg to receive. + # + # _flags_ is bitwise OR of MSG_* constants such as Socket::MSG_PEEK. + # + # _maxcontrollen_ is the maximum length of controls (ancillary data) to receive. + # + # _opts_ is option hash. + # Currently :scm_rights=>bool is the only option. + # + # :scm_rights option specifies that application expects SCM_RIGHTS control message. + # If the value is nil or false, application don't expects SCM_RIGHTS control message. + # In this case, recvmsg closes the passed file descriptors immediately. + # This is the default behavior. + # + # If :scm_rights value is neither nil nor false, application expects SCM_RIGHTS control message. + # In this case, recvmsg creates IO objects for each file descriptors for + # Socket::AncillaryData#unix_rights method. + # + # The return value is 4-elements array. + # + # _mesg_ is a string of the received message. + # + # _sender_addrinfo_ is a sender socket address for connection-less socket. + # It is an Addrinfo object. + # For connection-oriented socket such as TCP, sender_addrinfo is platform dependent. + # + # _rflags_ is a flags on the received message which is bitwise OR of MSG_* constants such as Socket::MSG_TRUNC. + # It will be nil if the system uses 4.3BSD style old recvmsg system call. + # + # _controls_ is ancillary data which is an array of Socket::AncillaryData objects such as: + # + # #<Socket::AncillaryData: AF_UNIX SOCKET RIGHTS 7> + # + # _maxmesglen_ and _maxcontrollen_ can be nil. + # In that case, the buffer will be grown until the message is not truncated. + # Internally, MSG_PEEK is used. + # Buffer full and MSG_CTRUNC are checked for truncation. + # + # recvmsg can be used to implement recv_io as follows: + # + # mesg, sender_sockaddr, rflags, *controls = sock.recvmsg(:scm_rights=>true) + # controls.each {|ancdata| + # if ancdata.cmsg_is?(:SOCKET, :RIGHTS) + # return ancdata.unix_rights[0] + # end + # } + def recvmsg(dlen = nil, flags = 0, clen = nil, scm_rights: false) + __recvmsg(dlen, flags, clen, scm_rights) + end + + # call-seq: + # basicsocket.recvmsg_nonblock(maxdatalen=nil, flags=0, maxcontrollen=nil, opts={}) => [data, sender_addrinfo, rflags, *controls] + # + # recvmsg receives a message using recvmsg(2) system call in non-blocking manner. + # + # It is similar to BasicSocket#recvmsg + # but non-blocking flag is set before the system call + # and it doesn't retry the system call. + # + # By specifying a keyword argument _exception_ to +false+, you can indicate + # that recvmsg_nonblock should not raise an IO::WaitReadable exception, but + # return the symbol +:wait_readable+ instead. + def recvmsg_nonblock(dlen = nil, flags = 0, clen = nil, + scm_rights: false, exception: true) + __recvmsg_nonblock(dlen, flags, clen, scm_rights, exception) + end + + # Linux-specific optimizations to avoid fcntl for IO#read_nonblock + # and IO#write_nonblock using MSG_DONTWAIT + # Do other platforms support MSG_DONTWAIT reliably? + if RUBY_PLATFORM =~ /linux/ && Socket.const_defined?(:MSG_DONTWAIT) + def read_nonblock(len, str = nil, exception: true) # :nodoc: + __read_nonblock(len, str, exception) + end + + def write_nonblock(buf, exception: true) # :nodoc: + __write_nonblock(buf, exception) + end + end +end + +class Socket < BasicSocket + # enable the socket option IPV6_V6ONLY if IPV6_V6ONLY is available. + def ipv6only! + if defined? Socket::IPV6_V6ONLY + self.setsockopt(:IPV6, :V6ONLY, 1) + end + end + + # call-seq: + # socket.recvfrom_nonblock(maxlen[, flags[, outbuf[, opts]]]) => [mesg, sender_addrinfo] + # + # 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 first element of the results, _mesg_, is the data received. + # The second element, _sender_addrinfo_, contains protocol-specific address + # information of the sender. + # + # When recvfrom(2) returns 0, Socket#recv_nonblock returns nil. + # In most cases it means the connection was closed, but for UDP connections + # it may mean an empty packet was received, as the underlying API makes + # it impossible to distinguish these two cases. + # + # === Parameters + # * +maxlen+ - the maximum number of bytes to receive from the socket + # * +flags+ - zero or more of the +MSG_+ options + # * +outbuf+ - destination String buffer + # * +opts+ - keyword hash, supporting `exception: false` + # + # === Example + # # In one file, start this first + # require 'socket' + # include Socket::Constants + # socket = Socket.new(AF_INET, SOCK_STREAM, 0) + # sockaddr = Socket.sockaddr_in(2200, 'localhost') + # socket.bind(sockaddr) + # socket.listen(5) + # client, client_addrinfo = socket.accept + # begin # emulate blocking recvfrom + # pair = client.recvfrom_nonblock(20) + # rescue IO::WaitReadable + # IO.select([client]) + # retry + # end + # data = pair[0].chomp + # puts "I only received 20 bytes '#{data}'" + # sleep 1 + # socket.close + # + # # In another file, start this second + # require 'socket' + # include Socket::Constants + # socket = Socket.new(AF_INET, SOCK_STREAM, 0) + # sockaddr = Socket.sockaddr_in(2200, 'localhost') + # socket.connect(sockaddr) + # socket.puts "Watch this get cut short!" + # socket.close + # + # Refer to Socket#recvfrom for the exceptions that may be thrown if the call + # to _recvfrom_nonblock_ fails. + # + # Socket#recvfrom_nonblock may raise any error corresponding to recvfrom(2) failure, + # including Errno::EWOULDBLOCK. + # + # If the exception is Errno::EWOULDBLOCK or Errno::EAGAIN, + # it is extended by IO::WaitReadable. + # So IO::WaitReadable can be used to rescue the exceptions for retrying + # recvfrom_nonblock. + # + # By specifying a keyword argument _exception_ to +false+, you can indicate + # that recvfrom_nonblock should not raise an IO::WaitReadable exception, but + # return the symbol +:wait_readable+ instead. + # + # === See + # * Socket#recvfrom + def recvfrom_nonblock(len, flag = 0, str = nil, exception: true) + __recvfrom_nonblock(len, flag, str, exception) + end + + # call-seq: + # socket.accept_nonblock([options]) => [client_socket, client_addrinfo] + # + # Accepts an incoming connection using accept(2) after + # O_NONBLOCK is set for the underlying file descriptor. + # It returns an array containing the accepted socket + # for the incoming connection, _client_socket_, + # and an Addrinfo, _client_addrinfo_. + # + # === Example + # # In one script, start this first + # require 'socket' + # include Socket::Constants + # socket = Socket.new(AF_INET, SOCK_STREAM, 0) + # sockaddr = Socket.sockaddr_in(2200, 'localhost') + # socket.bind(sockaddr) + # socket.listen(5) + # begin # emulate blocking accept + # client_socket, client_addrinfo = socket.accept_nonblock + # rescue IO::WaitReadable, Errno::EINTR + # IO.select([socket]) + # retry + # end + # puts "The client said, '#{client_socket.readline.chomp}'" + # client_socket.puts "Hello from script one!" + # socket.close + # + # # In another script, start this second + # require 'socket' + # include Socket::Constants + # socket = Socket.new(AF_INET, SOCK_STREAM, 0) + # sockaddr = Socket.sockaddr_in(2200, 'localhost') + # socket.connect(sockaddr) + # socket.puts "Hello from script 2." + # puts "The server said, '#{socket.readline.chomp}'" + # socket.close + # + # Refer to Socket#accept for the exceptions that may be thrown if the call + # to _accept_nonblock_ fails. + # + # Socket#accept_nonblock may raise any error corresponding to accept(2) failure, + # including Errno::EWOULDBLOCK. + # + # If the exception is Errno::EWOULDBLOCK, Errno::EAGAIN, Errno::ECONNABORTED or Errno::EPROTO, + # it is extended by IO::WaitReadable. + # So IO::WaitReadable can be used to rescue the exceptions for retrying accept_nonblock. + # + # By specifying a keyword argument _exception_ to +false+, you can indicate + # that accept_nonblock should not raise an IO::WaitReadable exception, but + # return the symbol +:wait_readable+ instead. + # + # === See + # * Socket#accept + def accept_nonblock(exception: true) + __accept_nonblock(exception) + end + + # :stopdoc: + RESOLUTION_DELAY = 0.05 + private_constant :RESOLUTION_DELAY + + CONNECTION_ATTEMPT_DELAY = 0.25 + private_constant :CONNECTION_ATTEMPT_DELAY + + ADDRESS_FAMILIES = { + ipv6: Socket::AF_INET6, + ipv4: Socket::AF_INET + }.freeze + private_constant :ADDRESS_FAMILIES + + HOSTNAME_RESOLUTION_QUEUE_UPDATED = 0 + private_constant :HOSTNAME_RESOLUTION_QUEUE_UPDATED + + IPV6_ADDRESS_FORMAT = /\A(?i:(?:(?:[0-9A-F]{1,4}:){7}(?:[0-9A-F]{1,4}|:)|(?:[0-9A-F]{1,4}:){6}(?:[0-9A-F]{1,4}|:(?:[0-9A-F]{1,4}:){1,5}[0-9A-F]{1,4}|:)|(?:[0-9A-F]{1,4}:){5}(?:(?::[0-9A-F]{1,4}){1,2}|:(?:[0-9A-F]{1,4}:){1,4}[0-9A-F]{1,4}|:)|(?:[0-9A-F]{1,4}:){4}(?:(?::[0-9A-F]{1,4}){1,3}|:(?:[0-9A-F]{1,4}:){1,3}[0-9A-F]{1,4}|:)|(?:[0-9A-F]{1,4}:){3}(?:(?::[0-9A-F]{1,4}){1,4}|:(?:[0-9A-F]{1,4}:){1,2}[0-9A-F]{1,4}|:)|(?:[0-9A-F]{1,4}:){2}(?:(?::[0-9A-F]{1,4}){1,5}|:(?:[0-9A-F]{1,4}:)[0-9A-F]{1,4}|:)|(?:[0-9A-F]{1,4}:){1}(?:(?::[0-9A-F]{1,4}){1,6}|:(?:[0-9A-F]{1,4}:){0,5}[0-9A-F]{1,4}|:)|(?:::(?:[0-9A-F]{1,4}:){0,7}[0-9A-F]{1,4}|::)))(?:%.+)?\z/ + private_constant :IPV6_ADDRESS_FORMAT + # :startdoc: + + # :call-seq: + # Socket.tcp(host, port, local_host=nil, local_port=nil, [opts]) {|socket| ... } + # Socket.tcp(host, port, local_host=nil, local_port=nil, [opts]) + # + # creates a new socket object connected to host:port using TCP/IP. + # + # Starting from Ruby 3.4, this method operates according to the + # Happy Eyeballs Version 2 ({RFC 8305}[https://datatracker.ietf.org/doc/html/rfc8305]) + # algorithm by default. + # + # For details on Happy Eyeballs Version 2, + # see {Socket.tcp_fast_fallback=}[rdoc-ref:Socket.tcp_fast_fallback=]. + # + # To make it behave the same as in Ruby 3.3 and earlier, + # explicitly specify the option fast_fallback:false. + # Or, setting Socket.tcp_fast_fallback=false will disable + # Happy Eyeballs Version 2 not only for this method but for all Socket globally. + # + # If local_host:local_port is given, + # the socket is bound to it. + # + # The optional last argument _opts_ is options represented by a hash. + # _opts_ may have following options: + # + # [:resolv_timeout] Specifies the timeout in seconds from when the hostname resolution starts. + # [:connect_timeout] This method sequentially attempts connecting to all candidate destination addresses.<br>The +connect_timeout+ specifies the timeout in seconds from the start of the connection attempt to the last candidate.<br>By default, all connection attempts continue until the timeout occurs.<br>When +fast_fallback:false+ is explicitly specified,<br>a timeout is set for each connection attempt and any connection attempt that exceeds its timeout will be canceled. + # [:open_timeout] Specifies the timeout in seconds from the start of the method execution.<br>If this timeout is reached while there are still addresses that have not yet been attempted for connection, no further attempts will be made.<br>If this option is specified together with other timeout options, an +ArgumentError+ will be raised. + # [:fast_fallback] Enables the Happy Eyeballs Version 2 algorithm (enabled by default). + # + # If a block is given, the block is called with the socket. + # The value of the block is returned. + # The socket is closed when this method returns. + # + # If no block is given, the socket is returned. + # + # Socket.tcp("www.ruby-lang.org", 80) {|sock| + # sock.print "GET / HTTP/1.0\r\nHost: www.ruby-lang.org\r\n\r\n" + # sock.close_write + # puts sock.read + # } + def self.tcp(host, port, local_host = nil, local_port = nil, connect_timeout: nil, resolv_timeout: nil, open_timeout: nil, fast_fallback: tcp_fast_fallback, &) # :yield: socket + if open_timeout && (connect_timeout || resolv_timeout) + raise ArgumentError, "Cannot specify open_timeout along with connect_timeout or resolv_timeout" + end + + sock = if fast_fallback && !(host && ip_address?(host)) && !(local_port && local_port.to_i != 0) + tcp_with_fast_fallback(host, port, local_host, local_port, connect_timeout:, resolv_timeout:, open_timeout:) + else + tcp_without_fast_fallback(host, port, local_host, local_port, connect_timeout:, resolv_timeout:, open_timeout:) + end + + if block_given? + begin + yield sock + ensure + sock.close + end + else + sock + end + end + + # :stopdoc: + def self.tcp_with_fast_fallback(host, port, local_host = nil, local_port = nil, connect_timeout: nil, resolv_timeout: nil, open_timeout: nil) + if local_host || local_port + local_addrinfos = Addrinfo.getaddrinfo(local_host, local_port, nil, :STREAM, timeout: open_timeout || resolv_timeout) + resolving_family_names = local_addrinfos.map { |lai| ADDRESS_FAMILIES.key(lai.afamily) }.uniq + else + local_addrinfos = [] + resolving_family_names = ADDRESS_FAMILIES.keys + end + + hostname_resolution_threads = [] + resolution_store = HostnameResolutionStore.new(resolving_family_names) + connecting_sockets = {} + is_windows_environment ||= (RUBY_PLATFORM =~ /mswin|mingw|cygwin/) + + now = current_clock_time + starts_at = now + resolution_delay_expires_at = nil + connection_attempt_delay_expires_at = nil + user_specified_connect_timeout_at = nil + user_specified_open_timeout_at = open_timeout ? now + open_timeout : nil + last_error = nil + last_error_from_thread = false + + if resolving_family_names.size == 1 + family_name = resolving_family_names.first + addrinfos = Addrinfo.getaddrinfo(host, port, ADDRESS_FAMILIES[:family_name], :STREAM, timeout: open_timeout || resolv_timeout) + resolution_store.add_resolved(family_name, addrinfos) + hostname_resolution_result = nil + hostname_resolution_notifier = nil + user_specified_resolv_timeout_at = nil + else + hostname_resolution_result = HostnameResolutionResult.new(resolving_family_names.size) + hostname_resolution_notifier = hostname_resolution_result.notifier + + hostname_resolution_threads.concat( + resolving_family_names.map { |family| + thread_args = [family, host, port, hostname_resolution_result] + thread = Thread.new(*thread_args) { |*thread_args| resolve_hostname(*thread_args) } + Thread.pass + thread + } + ) + user_specified_resolv_timeout_at = resolv_timeout ? now + resolv_timeout : Float::INFINITY + end + + loop do + if resolution_store.any_addrinfos? && + !resolution_delay_expires_at && + !connection_attempt_delay_expires_at + while (addrinfo = resolution_store.get_addrinfo) + if local_addrinfos.any? + local_addrinfo = local_addrinfos.find { |lai| lai.afamily == addrinfo.afamily } + + if local_addrinfo.nil? + if resolution_store.any_addrinfos? + # Try other Addrinfo in next "while" + next + elsif connecting_sockets.any? || resolution_store.any_unresolved_family? + # Exit this "while" and wait for connections to be established or hostname resolution in next loop + # Or exit this "while" and wait for hostname resolution in next loop + break + else + raise SocketError.new 'no appropriate local address' + end + end + end + + begin + if resolution_store.any_addrinfos? || + connecting_sockets.any? || + resolution_store.any_unresolved_family? + socket = Socket.new(addrinfo.pfamily, addrinfo.socktype, addrinfo.protocol) + socket.bind(local_addrinfo) if local_addrinfo + result = socket.connect_nonblock(addrinfo, exception: false) + else + timeout = + if open_timeout + t = open_timeout - (current_clock_time - starts_at) + t.negative? ? 0 : t + else + connect_timeout + end + result = socket = local_addrinfo ? + addrinfo.connect_from(local_addrinfo, timeout:) : + addrinfo.connect(timeout:) + end + + if result == :wait_writable + connection_attempt_delay_expires_at = now + CONNECTION_ATTEMPT_DELAY + if resolution_store.empty_addrinfos? + user_specified_connect_timeout_at = connect_timeout ? now + connect_timeout : Float::INFINITY + end + + connecting_sockets[socket] = addrinfo + break + else + return socket # connection established + end + rescue SystemCallError => e + socket&.close + last_error = e + + if resolution_store.any_addrinfos? + # Try other Addrinfo in next "while" + next + elsif connecting_sockets.any? || resolution_store.any_unresolved_family? + # Exit this "while" and wait for connections to be established or hostname resolution in next loop + # Or exit this "while" and wait for hostname resolution in next loop + break + else + raise last_error + end + end + end + end + + ends_at = + if resolution_store.any_addrinfos? + [(resolution_delay_expires_at || connection_attempt_delay_expires_at), + user_specified_open_timeout_at].compact.min + elsif user_specified_open_timeout_at + user_specified_open_timeout_at + else + [user_specified_resolv_timeout_at, user_specified_connect_timeout_at].compact.max + end + + hostname_resolved, writable_sockets, except_sockets = IO.select( + hostname_resolution_notifier, + connecting_sockets.keys, + # Use errorfds to wait for non-blocking connect failures on Windows + is_windows_environment ? connecting_sockets.keys : nil, + second_to_timeout(current_clock_time, ends_at), + ) + now = current_clock_time + resolution_delay_expires_at = nil if expired?(now, resolution_delay_expires_at) + connection_attempt_delay_expires_at = nil if expired?(now, connection_attempt_delay_expires_at) + + if writable_sockets&.any? + while (writable_socket = writable_sockets.pop) + is_connected = is_windows_environment || ( + sockopt = writable_socket.getsockopt(Socket::SOL_SOCKET, Socket::SO_ERROR) + sockopt.int.zero? + ) + + if is_connected + connecting_sockets.delete writable_socket + return writable_socket + else + failed_ai = connecting_sockets.delete writable_socket + writable_socket.close + ip_address = failed_ai.ipv6? ? "[#{failed_ai.ip_address}]" : failed_ai.ip_address + last_error = SystemCallError.new("connect(2) for #{ip_address}:#{failed_ai.ip_port}", sockopt.int) + + if writable_sockets.any? || connecting_sockets.any? + # Try other writable socket in next "while" + # Or exit this "while" and wait for connections to be established or hostname resolution in next loop + elsif resolution_store.any_addrinfos? || resolution_store.any_unresolved_family? + # Exit this "while" and try other connection attempt + # Or exit this "while" and wait for hostname resolution in next loop + connection_attempt_delay_expires_at = nil + user_specified_connect_timeout_at = nil + else + raise last_error + end + end + end + end + + if except_sockets&.any? + except_sockets.each do |except_socket| + failed_ai = connecting_sockets.delete except_socket + sockopt = except_socket.getsockopt(Socket::SOL_SOCKET, Socket::SO_ERROR) + except_socket.close + ip_address = failed_ai.ipv6? ? "[#{failed_ai.ip_address}]" : failed_ai.ip_address + last_error = SystemCallError.new("connect(2) for #{ip_address}:#{failed_ai.ip_port}", sockopt.int) + + if except_sockets.any? || connecting_sockets.any? + # Cleanup other except socket in next "each" + # Or exit this "while" and wait for connections to be established or hostname resolution in next loop + elsif resolution_store.any_addrinfos? || resolution_store.any_unresolved_family? + # Exit this "while" and try other connection attempt + # Or exit this "while" and wait for hostname resolution in next loop + connection_attempt_delay_expires_at = nil + user_specified_connect_timeout_at = nil + else + raise last_error + end + end + end + + if hostname_resolved&.any? + while (family_and_result = hostname_resolution_result.get) + family_name, result = family_and_result + + if result.is_a? Exception + resolution_store.add_error(family_name, result) + + unless (Socket.const_defined?(:EAI_ADDRFAMILY)) && + (result.is_a?(Socket::ResolutionError)) && + (result.error_code == Socket::EAI_ADDRFAMILY) + other = family_name == :ipv6 ? :ipv4 : :ipv6 + if !resolution_store.resolved?(other) || !resolution_store.resolved_successfully?(other) + last_error = result + last_error_from_thread = true + end + end + else + resolution_store.add_resolved(family_name, result) + end + end + + if resolution_store.resolved?(:ipv4) + if resolution_store.resolved?(:ipv6) + hostname_resolution_notifier = nil + resolution_delay_expires_at = nil + user_specified_resolv_timeout_at = nil + elsif resolution_store.resolved_successfully?(:ipv4) + resolution_delay_expires_at = now + RESOLUTION_DELAY + end + end + end + + if expired?(now, user_specified_open_timeout_at) + raise(IO::TimeoutError, "user specified timeout for #{host}:#{port}") + end + + if resolution_store.empty_addrinfos? + if connecting_sockets.empty? && resolution_store.resolved_all_families? + if last_error_from_thread + raise last_error.class, last_error.message, cause: last_error + else + raise last_error + end + end + + if (expired?(now, user_specified_resolv_timeout_at) || resolution_store.resolved_all_families?) && + (expired?(now, user_specified_connect_timeout_at) || connecting_sockets.empty?) + raise(IO::TimeoutError, "user specified timeout for #{host}:#{port}") + end + end + end + ensure + hostname_resolution_threads.each do |thread| + thread.exit + end + + hostname_resolution_result&.close + + connecting_sockets.each_key do |connecting_socket| + connecting_socket.close + end + end + private_class_method :tcp_with_fast_fallback + + def self.tcp_without_fast_fallback(host, port, local_host, local_port, connect_timeout:, resolv_timeout:, open_timeout:) + last_error = nil + ret = nil + + local_addr_list = nil + if local_host != nil || local_port != nil + local_addr_list = Addrinfo.getaddrinfo(local_host, local_port, nil, :STREAM, nil, timeout: open_timeout || resolv_timeout) + end + + timeout = open_timeout ? open_timeout : resolv_timeout + starts_at = current_clock_time + + Addrinfo.foreach(host, port, nil, :STREAM, timeout:) {|ai| + if local_addr_list + local_addr = local_addr_list.find {|local_ai| local_ai.afamily == ai.afamily } + next unless local_addr + else + local_addr = nil + end + begin + timeout = + if open_timeout + t = open_timeout - (current_clock_time - starts_at) + t.negative? ? 0 : t + else + connect_timeout + end + + sock = local_addr ? + ai.connect_from(local_addr, timeout:) : + ai.connect(timeout:) + rescue SystemCallError + last_error = $! + next + end + ret = sock + break + } + unless ret + if last_error + raise last_error + else + raise SocketError, "no appropriate local address" + end + end + + ret + end + private_class_method :tcp_without_fast_fallback + + def self.ip_address?(hostname) + hostname.match?(IPV6_ADDRESS_FORMAT) || hostname.match?(/\A([0-9]{1,3}\.){3}[0-9]{1,3}\z/) + end + private_class_method :ip_address? + + def self.resolve_hostname(family, host, port, hostname_resolution_result) + begin + resolved_addrinfos = Addrinfo.getaddrinfo(host, port, ADDRESS_FAMILIES[family], :STREAM) + hostname_resolution_result.add(family, resolved_addrinfos) + rescue => e + hostname_resolution_result.add(family, e) + end + end + private_class_method :resolve_hostname + + def self.current_clock_time + Process.clock_gettime(Process::CLOCK_MONOTONIC) + end + private_class_method :current_clock_time + + def self.second_to_timeout(started_at, ends_at) + return nil if ends_at == Float::INFINITY || ends_at.nil? + + remaining = (ends_at - started_at) + remaining.negative? ? 0 : remaining + end + private_class_method :second_to_timeout + + def self.expired?(started_at, ends_at) + second_to_timeout(started_at, ends_at)&.zero? + end + private_class_method :expired? + + class HostnameResolutionResult + def initialize(size) + @size = size + @taken_count = 0 + @rpipe, @wpipe = IO.pipe + @results = [] + @mutex = Mutex.new + end + + def notifier + [@rpipe] + end + + def add(family, result) + @mutex.synchronize do + @results.push [family, result] + @wpipe.putc HOSTNAME_RESOLUTION_QUEUE_UPDATED + end + end + + def get + return nil if @results.empty? + + res = nil + + @mutex.synchronize do + @rpipe.getbyte + res = @results.shift + end + + @taken_count += 1 + close if @taken_count == @size + res + end + + def close + @rpipe.close + @wpipe.close + end + end + private_constant :HostnameResolutionResult + + class HostnameResolutionStore + PRIORITY_ON_V6 = [:ipv6, :ipv4].freeze + PRIORITY_ON_V4 = [:ipv4, :ipv6].freeze + + def initialize(family_names) + @family_names = family_names + @addrinfo_dict = {} + @error_dict = {} + @last_family = nil + end + + def add_resolved(family_name, addrinfos) + @addrinfo_dict[family_name] = addrinfos + end + + def add_error(family_name, error) + @addrinfo_dict[family_name] = [] + @error_dict[family_name] = error + end + + def get_addrinfo + precedences = + case @last_family + when :ipv4, nil then PRIORITY_ON_V6 + when :ipv6 then PRIORITY_ON_V4 + end + + precedences.each do |family_name| + addrinfo = @addrinfo_dict[family_name]&.shift + next unless addrinfo + + @last_family = family_name + return addrinfo + end + + nil + end + + def empty_addrinfos? + @addrinfo_dict.all? { |_, addrinfos| addrinfos.empty? } + end + + def any_addrinfos? + !empty_addrinfos? + end + + def resolved?(family) + @addrinfo_dict.has_key? family + end + + def resolved_successfully?(family) + resolved?(family) && !@error_dict[family] + end + + def resolved_all_families? + (@family_names - @addrinfo_dict.keys).empty? + end + + def any_unresolved_family? + !resolved_all_families? + end + end + private_constant :HostnameResolutionStore + + def self.ip_sockets_port0(ai_list, reuseaddr) + sockets = [] + begin + sockets.clear + port = nil + ai_list.each {|ai| + begin + s = Socket.new(ai.pfamily, ai.socktype, ai.protocol) + rescue SystemCallError + next + end + sockets << s + s.ipv6only! if ai.ipv6? + if reuseaddr + s.setsockopt(:SOCKET, :REUSEADDR, 1) + end + unless port + s.bind(ai) + port = s.local_address.ip_port + else + s.bind(ai.family_addrinfo(ai.ip_address, port)) + end + } + rescue Errno::EADDRINUSE + sockets.each(&:close) + retry + rescue Exception + sockets.each(&:close) + raise + end + sockets + end + private_class_method :ip_sockets_port0 + + def self.tcp_server_sockets_port0(host) + ai_list = Addrinfo.getaddrinfo(host, 0, nil, :STREAM, nil, Socket::AI_PASSIVE) + sockets = ip_sockets_port0(ai_list, true) + begin + sockets.each {|s| + s.listen(Socket::SOMAXCONN) + } + rescue Exception + sockets.each(&:close) + raise + end + sockets + end + class << self + private :tcp_server_sockets_port0 + end + # :startdoc: + + # creates TCP/IP server sockets for _host_ and _port_. + # _host_ is optional. + # + # If no block given, + # it returns an array of listening sockets. + # + # If a block is given, the block is called with the sockets. + # The value of the block is returned. + # The socket is closed when this method returns. + # + # If _port_ is 0, actual port number is chosen dynamically. + # However all sockets in the result has same port number. + # + # # tcp_server_sockets returns two sockets. + # sockets = Socket.tcp_server_sockets(1296) + # p sockets #=> [#<Socket:fd 3>, #<Socket:fd 4>] + # + # # The sockets contains IPv6 and IPv4 sockets. + # sockets.each {|s| p s.local_address } + # #=> #<Addrinfo: [::]:1296 TCP> + # # #<Addrinfo: 0.0.0.0:1296 TCP> + # + # # IPv6 and IPv4 socket has same port number, 53114, even if it is chosen dynamically. + # sockets = Socket.tcp_server_sockets(0) + # sockets.each {|s| p s.local_address } + # #=> #<Addrinfo: [::]:53114 TCP> + # # #<Addrinfo: 0.0.0.0:53114 TCP> + # + # # The block is called with the sockets. + # Socket.tcp_server_sockets(0) {|sockets| + # p sockets #=> [#<Socket:fd 3>, #<Socket:fd 4>] + # } + # + def self.tcp_server_sockets(host=nil, port) + if port == 0 + sockets = tcp_server_sockets_port0(host) + else + last_error = nil + sockets = [] + begin + Addrinfo.foreach(host, port, nil, :STREAM, nil, Socket::AI_PASSIVE) {|ai| + begin + s = ai.listen + rescue SystemCallError + last_error = $! + next + end + sockets << s + } + if sockets.empty? + raise last_error + end + rescue Exception + sockets.each(&:close) + raise + end + end + if block_given? + begin + yield sockets + ensure + sockets.each(&:close) + end + else + sockets + end + end + + # yield socket and client address for each a connection accepted via given sockets. + # + # The arguments are a list of sockets. + # The individual argument should be a socket or an array of sockets. + # + # This method yields the block sequentially. + # It means that the next connection is not accepted until the block returns. + # So concurrent mechanism, thread for example, should be used to service multiple clients at a time. + # + def self.accept_loop(*sockets) # :yield: socket, client_addrinfo + sockets.flatten!(1) + if sockets.empty? + raise ArgumentError, "no sockets" + end + loop { + readable, _, _ = IO.select(sockets) + readable.each {|r| + sock, addr = r.accept_nonblock(exception: false) + next if sock == :wait_readable + yield sock, addr + } + } + end + + # creates a TCP/IP server on _port_ and calls the block for each connection accepted. + # The block is called with a socket and a client_address as an Addrinfo object. + # + # If _host_ is specified, it is used with _port_ to determine the server addresses. + # + # The socket is *not* closed when the block returns. + # So application should close it explicitly. + # + # This method calls the block sequentially. + # It means that the next connection is not accepted until the block returns. + # So concurrent mechanism, thread for example, should be used to service multiple clients at a time. + # + # Note that Addrinfo.getaddrinfo is used to determine the server socket addresses. + # When Addrinfo.getaddrinfo returns two or more addresses, + # IPv4 and IPv6 address for example, + # all of them are used. + # Socket.tcp_server_loop succeeds if one socket can be used at least. + # + # # Sequential echo server. + # # It services only one client at a time. + # Socket.tcp_server_loop(16807) {|sock, client_addrinfo| + # begin + # IO.copy_stream(sock, sock) + # ensure + # sock.close + # end + # } + # + # # Threaded echo server + # # It services multiple clients at a time. + # # Note that it may accept connections too much. + # Socket.tcp_server_loop(16807) {|sock, client_addrinfo| + # Thread.new { + # begin + # IO.copy_stream(sock, sock) + # ensure + # sock.close + # end + # } + # } + # + def self.tcp_server_loop(host=nil, port, &b) # :yield: socket, client_addrinfo + tcp_server_sockets(host, port) {|sockets| + accept_loop(sockets, &b) + } + end + + # :call-seq: + # Socket.udp_server_sockets([host, ] port) + # + # Creates UDP/IP sockets for a UDP server. + # + # If no block given, it returns an array of sockets. + # + # If a block is given, the block is called with the sockets. + # The value of the block is returned. + # The sockets are closed when this method returns. + # + # If _port_ is zero, some port is chosen. + # But the chosen port is used for the all sockets. + # + # # UDP/IP echo server + # Socket.udp_server_sockets(0) {|sockets| + # p sockets.first.local_address.ip_port #=> 32963 + # Socket.udp_server_loop_on(sockets) {|msg, msg_src| + # msg_src.reply msg + # } + # } + # + def self.udp_server_sockets(host=nil, port) + last_error = nil + sockets = [] + + ipv6_recvpktinfo = nil + if defined? Socket::AncillaryData + if defined? Socket::IPV6_RECVPKTINFO # RFC 3542 + ipv6_recvpktinfo = Socket::IPV6_RECVPKTINFO + elsif defined? Socket::IPV6_PKTINFO # RFC 2292 + ipv6_recvpktinfo = Socket::IPV6_PKTINFO + end + end + + local_addrs = Socket.ip_address_list + + ip_list = [] + Addrinfo.foreach(host, port, nil, :DGRAM, nil, Socket::AI_PASSIVE) {|ai| + if ai.ipv4? && ai.ip_address == "0.0.0.0" + local_addrs.each {|a| + next unless a.ipv4? + ip_list << Addrinfo.new(a.to_sockaddr, :INET, :DGRAM, 0); + } + elsif ai.ipv6? && ai.ip_address == "::" && !ipv6_recvpktinfo + local_addrs.each {|a| + next unless a.ipv6? + ip_list << Addrinfo.new(a.to_sockaddr, :INET6, :DGRAM, 0); + } + else + ip_list << ai + end + } + ip_list.uniq!(&:to_sockaddr) + + if port == 0 + sockets = ip_sockets_port0(ip_list, false) + else + ip_list.each {|ip| + ai = Addrinfo.udp(ip.ip_address, port) + begin + s = ai.bind + rescue SystemCallError + last_error = $! + next + end + sockets << s + } + if sockets.empty? + raise last_error + end + end + + sockets.each {|s| + ai = s.local_address + if ipv6_recvpktinfo && ai.ipv6? && ai.ip_address == "::" + s.setsockopt(:IPV6, ipv6_recvpktinfo, 1) + end + } + + if block_given? + begin + yield sockets + ensure + sockets.each(&:close) if sockets + end + else + sockets + end + end + + # :call-seq: + # Socket.udp_server_recv(sockets) {|msg, msg_src| ... } + # + # Receive UDP/IP packets from the given _sockets_. + # For each packet received, the block is called. + # + # The block receives _msg_ and _msg_src_. + # _msg_ is a string which is the payload of the received packet. + # _msg_src_ is a Socket::UDPSource object which is used for reply. + # + # Socket.udp_server_loop can be implemented using this method as follows. + # + # udp_server_sockets(host, port) {|sockets| + # loop { + # readable, _, _ = IO.select(sockets) + # udp_server_recv(readable) {|msg, msg_src| ... } + # } + # } + # + def self.udp_server_recv(sockets) + sockets.each {|r| + msg, sender_addrinfo, _, *controls = r.recvmsg_nonblock(exception: false) + next if msg == :wait_readable + ai = r.local_address + if ai.ipv6? and pktinfo = controls.find {|c| c.cmsg_is?(:IPV6, :PKTINFO) } + ai = Addrinfo.udp(pktinfo.ipv6_pktinfo_addr.ip_address, ai.ip_port) + yield msg, UDPSource.new(sender_addrinfo, ai) {|reply_msg| + r.sendmsg reply_msg, 0, sender_addrinfo, pktinfo + } + else + yield msg, UDPSource.new(sender_addrinfo, ai) {|reply_msg| + r.send reply_msg, 0, sender_addrinfo + } + end + } + end + + # :call-seq: + # Socket.udp_server_loop_on(sockets) {|msg, msg_src| ... } + # + # Run UDP/IP server loop on the given sockets. + # + # The return value of Socket.udp_server_sockets is appropriate for the argument. + # + # It calls the block for each message received. + # + def self.udp_server_loop_on(sockets, &b) # :yield: msg, msg_src + loop { + readable, _, _ = IO.select(sockets) + udp_server_recv(readable, &b) + } + end + + # :call-seq: + # Socket.udp_server_loop(port) {|msg, msg_src| ... } + # Socket.udp_server_loop(host, port) {|msg, msg_src| ... } + # + # creates a UDP/IP server on _port_ and calls the block for each message arrived. + # The block is called with the message and its source information. + # + # This method allocates sockets internally using _port_. + # If _host_ is specified, it is used conjunction with _port_ to determine the server addresses. + # + # The _msg_ is a string. + # + # The _msg_src_ is a Socket::UDPSource object. + # It is used for reply. + # + # # UDP/IP echo server. + # Socket.udp_server_loop(9261) {|msg, msg_src| + # msg_src.reply msg + # } + # + def self.udp_server_loop(host=nil, port, &b) # :yield: message, message_source + udp_server_sockets(host, port) {|sockets| + udp_server_loop_on(sockets, &b) + } + end + + # UDP/IP address information used by Socket.udp_server_loop. + class UDPSource + # +remote_address+ is an Addrinfo object. + # + # +local_address+ is an Addrinfo object. + # + # +reply_proc+ is a Proc used to send reply back to the source. + def initialize(remote_address, local_address, &reply_proc) + @remote_address = remote_address + @local_address = local_address + @reply_proc = reply_proc + end + + # Address of the source + attr_reader :remote_address + + # Local address + attr_reader :local_address + + def inspect # :nodoc: + "\#<#{self.class}: #{@remote_address.inspect_sockaddr} to #{@local_address.inspect_sockaddr}>".dup + end + + # Sends the String +msg+ to the source + def reply(msg) + @reply_proc.call msg + end + end + + # creates a new socket connected to path using UNIX socket socket. + # + # If a block is given, the block is called with the socket. + # The value of the block is returned. + # The socket is closed when this method returns. + # + # If no block is given, the socket is returned. + # + # # talk to /tmp/sock socket. + # Socket.unix("/tmp/sock") {|sock| + # t = Thread.new { IO.copy_stream(sock, STDOUT) } + # IO.copy_stream(STDIN, sock) + # t.join + # } + # + def self.unix(path) # :yield: socket + addr = Addrinfo.unix(path) + sock = addr.connect + if block_given? + begin + yield sock + ensure + sock.close + end + else + sock + end + end + + # creates a UNIX server socket on _path_ + # + # If no block given, it returns a listening socket. + # + # If a block is given, it is called with the socket and the block value is returned. + # When the block exits, the socket is closed and the socket file is removed. + # + # socket = Socket.unix_server_socket("/tmp/s") + # p socket #=> #<Socket:fd 3> + # p socket.local_address #=> #<Addrinfo: /tmp/s SOCK_STREAM> + # + # Socket.unix_server_socket("/tmp/sock") {|s| + # p s #=> #<Socket:fd 3> + # p s.local_address #=> # #<Addrinfo: /tmp/sock SOCK_STREAM> + # } + # + def self.unix_server_socket(path) + unless unix_socket_abstract_name?(path) + begin + st = File.lstat(path) + rescue Errno::ENOENT + end + if st&.socket? && st.owned? + File.unlink path + end + end + s = Addrinfo.unix(path).listen + if block_given? + begin + yield s + ensure + s.close + unless unix_socket_abstract_name?(path) + File.unlink path + end + end + else + s + end + end + + # :stopdoc: + if RUBY_PLATFORM.include?("linux") + def self.unix_socket_abstract_name?(path) + path.empty? or path.start_with?("\0") + end + else + def self.unix_socket_abstract_name?(path) + false + end + end + private_class_method :unix_socket_abstract_name? + # :startdoc: + + # creates a UNIX socket server on _path_. + # It calls the block for each socket accepted. + # + # If _host_ is specified, it is used with _port_ to determine the server ports. + # + # The socket is *not* closed when the block returns. + # So application should close it. + # + # This method deletes the socket file pointed by _path_ at first if + # the file is a socket file and it is owned by the user of the application. + # This is safe only if the directory of _path_ is not changed by a malicious user. + # So don't use /tmp/malicious-users-directory/socket. + # Note that /tmp/socket and /tmp/your-private-directory/socket is safe assuming that /tmp has sticky bit. + # + # # Sequential echo server. + # # It services only one client at a time. + # Socket.unix_server_loop("/tmp/sock") {|sock, client_addrinfo| + # begin + # IO.copy_stream(sock, sock) + # ensure + # sock.close + # end + # } + # + def self.unix_server_loop(path, &b) # :yield: socket, client_addrinfo + unix_server_socket(path) {|serv| + accept_loop(serv, &b) + } + end + + # call-seq: + # socket.connect_nonblock(remote_sockaddr, [options]) => 0 + # + # Requests a connection to be made on the given +remote_sockaddr+ after + # O_NONBLOCK is set for the underlying file descriptor. + # Returns 0 if successful, otherwise an exception is raised. + # + # === Parameter + # * +remote_sockaddr+ - the +struct+ sockaddr contained in a string or Addrinfo object + # + # === Example: + # # Pull down Google's web page + # require 'socket' + # include Socket::Constants + # socket = Socket.new(AF_INET, SOCK_STREAM, 0) + # sockaddr = Socket.sockaddr_in(80, 'www.google.com') + # begin # emulate blocking connect + # socket.connect_nonblock(sockaddr) + # rescue IO::WaitWritable + # IO.select(nil, [socket]) # wait 3-way handshake completion + # begin + # socket.connect_nonblock(sockaddr) # check connection failure + # rescue Errno::EISCONN + # end + # end + # socket.write("GET / HTTP/1.0\r\n\r\n") + # results = socket.read + # + # Refer to Socket#connect for the exceptions that may be thrown if the call + # to _connect_nonblock_ fails. + # + # Socket#connect_nonblock may raise any error corresponding to connect(2) failure, + # including Errno::EINPROGRESS. + # + # If the exception is Errno::EINPROGRESS, + # it is extended by IO::WaitWritable. + # So IO::WaitWritable can be used to rescue the exceptions for retrying connect_nonblock. + # + # By specifying a keyword argument _exception_ to +false+, you can indicate + # that connect_nonblock should not raise an IO::WaitWritable exception, but + # return the symbol +:wait_writable+ instead. + # + # === See + # * Socket#connect + def connect_nonblock(addr, exception: true) + __connect_nonblock(addr, exception) + end +end + +class UDPSocket < IPSocket + + # call-seq: + # udpsocket.recvfrom_nonblock(maxlen [, flags[, outbuf [, options]]]) => [mesg, sender_inet_addr] + # + # Receives up to _maxlen_ bytes from +udpsocket+ using recvfrom(2) after + # O_NONBLOCK is set for the underlying file descriptor. + # _flags_ is zero or more of the +MSG_+ options. + # The first element of the results, _mesg_, is the data received. + # The second element, _sender_inet_addr_, is an array to represent the sender address. + # + # When recvfrom(2) returns 0, Socket#recv_nonblock returns nil. + # In most cases it means the connection was closed, but it may also mean + # an empty packet was received, as the underlying API makes + # it impossible to distinguish these two cases. + # + # === Parameters + # * +maxlen+ - the number of bytes to receive from the socket + # * +flags+ - zero or more of the +MSG_+ options + # * +outbuf+ - destination String buffer + # * +options+ - keyword hash, supporting `exception: false` + # + # === Example + # require 'socket' + # s1 = UDPSocket.new + # s1.bind("127.0.0.1", 0) + # s2 = UDPSocket.new + # s2.bind("127.0.0.1", 0) + # s2.connect(*s1.addr.values_at(3,1)) + # s1.connect(*s2.addr.values_at(3,1)) + # s1.send "aaa", 0 + # begin # emulate blocking recvfrom + # p s2.recvfrom_nonblock(10) #=> ["aaa", ["AF_INET", 33302, "localhost.localdomain", "127.0.0.1"]] + # rescue IO::WaitReadable + # IO.select([s2]) + # retry + # end + # + # Refer to Socket#recvfrom for the exceptions that may be thrown if the call + # to _recvfrom_nonblock_ fails. + # + # UDPSocket#recvfrom_nonblock may raise any error corresponding to recvfrom(2) failure, + # including Errno::EWOULDBLOCK. + # + # If the exception is Errno::EWOULDBLOCK or Errno::EAGAIN, + # it is extended by IO::WaitReadable. + # So IO::WaitReadable can be used to rescue the exceptions for retrying recvfrom_nonblock. + # + # By specifying a keyword argument _exception_ to +false+, you can indicate + # that recvfrom_nonblock should not raise an IO::WaitReadable exception, but + # return the symbol +:wait_readable+ instead. + # + # === See + # * Socket#recvfrom + def recvfrom_nonblock(len, flag = 0, outbuf = nil, exception: true) + __recvfrom_nonblock(len, flag, outbuf, exception) + end +end + +class TCPServer < TCPSocket + + # call-seq: + # tcpserver.accept_nonblock([options]) => tcpsocket + # + # Accepts an incoming connection using accept(2) after + # O_NONBLOCK is set for the underlying file descriptor. + # It returns an accepted TCPSocket for the incoming connection. + # + # === Example + # require 'socket' + # serv = TCPServer.new(2202) + # begin # emulate blocking accept + # sock = serv.accept_nonblock + # rescue IO::WaitReadable, Errno::EINTR + # IO.select([serv]) + # retry + # end + # # sock is an accepted socket. + # + # Refer to Socket#accept for the exceptions that may be thrown if the call + # to TCPServer#accept_nonblock fails. + # + # TCPServer#accept_nonblock may raise any error corresponding to accept(2) failure, + # including Errno::EWOULDBLOCK. + # + # If the exception is Errno::EWOULDBLOCK, Errno::EAGAIN, Errno::ECONNABORTED, Errno::EPROTO, + # it is extended by IO::WaitReadable. + # So IO::WaitReadable can be used to rescue the exceptions for retrying accept_nonblock. + # + # By specifying a keyword argument _exception_ to +false+, you can indicate + # that accept_nonblock should not raise an IO::WaitReadable exception, but + # return the symbol +:wait_readable+ instead. + # + # === See + # * TCPServer#accept + # * Socket#accept + def accept_nonblock(exception: true) + __accept_nonblock(exception) + end +end + +class UNIXServer < UNIXSocket + # call-seq: + # unixserver.accept_nonblock([options]) => unixsocket + # + # Accepts an incoming connection using accept(2) after + # O_NONBLOCK is set for the underlying file descriptor. + # It returns an accepted UNIXSocket for the incoming connection. + # + # === Example + # require 'socket' + # serv = UNIXServer.new("/tmp/sock") + # begin # emulate blocking accept + # sock = serv.accept_nonblock + # rescue IO::WaitReadable, Errno::EINTR + # IO.select([serv]) + # retry + # end + # # sock is an accepted socket. + # + # Refer to Socket#accept for the exceptions that may be thrown if the call + # to UNIXServer#accept_nonblock fails. + # + # UNIXServer#accept_nonblock may raise any error corresponding to accept(2) failure, + # including Errno::EWOULDBLOCK. + # + # If the exception is Errno::EWOULDBLOCK, Errno::EAGAIN, Errno::ECONNABORTED or Errno::EPROTO, + # it is extended by IO::WaitReadable. + # So IO::WaitReadable can be used to rescue the exceptions for retrying accept_nonblock. + # + # By specifying a keyword argument _exception_ to +false+, you can indicate + # that accept_nonblock should not raise an IO::WaitReadable exception, but + # return the symbol +:wait_readable+ instead. + # + # === See + # * UNIXServer#accept + # * Socket#accept + def accept_nonblock(exception: true) + __accept_nonblock(exception) + end +end if defined?(UNIXSocket) diff --git a/ext/socket/mkconstants.rb b/ext/socket/mkconstants.rb new file mode 100644 index 0000000000..4271e40cd8 --- /dev/null +++ b/ext/socket/mkconstants.rb @@ -0,0 +1,854 @@ +# frozen_string_literal: false +require 'optparse' +require 'erb' + +opt = OptionParser.new + +opt.def_option('-h', 'help') { + puts opt + exit 0 +} + +opt_o = nil +opt.def_option('-o FILE', 'specify output file') {|filename| + opt_o = filename +} + +opt_H = nil +opt.def_option('-H FILE', 'specify output header file') {|filename| + opt_H = filename +} + +C_ESC = { + "\\" => "\\\\", + '"' => '\"', + "\n" => '\n', +} + +0x00.upto(0x1f) {|ch| C_ESC[[ch].pack("C")] ||= "\\%03o" % ch } +0x7f.upto(0xff) {|ch| C_ESC[[ch].pack("C")] = "\\%03o" % ch } +C_ESC_PAT = Regexp.union(*C_ESC.keys) + +def c_str(str) + '"' + str.gsub(C_ESC_PAT) {|s| C_ESC[s]} + '"' +end + +opt.parse! + + + +h = {} +COMMENTS = Hash.new { |h, name| h[name] = name } + +DATA.each_line {|s| + name, default_value, comment = s.chomp.split(/\s+/, 3) + next unless name && name[0] != ?# + + default_value = nil if default_value == 'nil' + + if h.has_key? name + warn "#{$.}: warning: duplicate name: #{name}" + next + end + h[name] = default_value + if comment + # Stop unintentional references + COMMENTS[name] = comment.gsub(/\b(Data|Kernel|Process|Set|Socket|Time)\b/, '\\\\\\&') + end +} +DEFS = h.to_a + +def each_const + DEFS.each {|name, default_value| + guard = nil + if /\A(AF_INET6|PF_INET6|IPV6_.*)\z/ =~ name + # IPv6 is not supported although AF_INET6 is defined on mingw + guard = "defined(INET6)" + end + yield guard, name, default_value + } +end + +def each_name(pat) + DEFS.each {|name, default_value| + next if pat !~ name + yield name + } +end + +erb_new = lambda do |src, trim| + ERB.new(src, trim_mode: trim) +end + +erb_new.call(<<'EOS', '%').def_method(Object, "gen_const_decls") +% each_const {|guard, name, default_value| +#if !defined(<%=name%>) +# if defined(HAVE_CONST_<%=name.upcase%>) +# define <%=name%> <%=name%> +%if default_value +# else +# define <%=name%> <%=default_value%> +%end +# endif +#endif +% } +EOS + +erb_new.call(<<'EOS', '%').def_method(Object, "gen_const_defs_in_guard(name, default_value)") +#if defined(<%=name%>) + /* <%= COMMENTS[name] %> */ + rb_define_const(rb_cSocket, <%=c_str name%>, INTEGER2NUM(<%=name%>)); + /* <%= COMMENTS[name] %> */ + rb_define_const(rb_mSockConst, <%=c_str name%>, INTEGER2NUM(<%=name%>)); +#endif +EOS + +erb_new.call(<<'EOS', '%').def_method(Object, "gen_const_defs") +% each_const {|guard, name, default_value| +% if guard +#if <%=guard%> +<%= gen_const_defs_in_guard(name, default_value).chomp %> +#endif +% else +<%= gen_const_defs_in_guard(name, default_value).chomp %> +% end +% } +EOS + +def reverse_each_name(pat) + DEFS.reverse_each {|name, default_value| + next if pat !~ name + yield name + } +end + +def each_names_with_len(pat, prefix_optional=nil) + h = {} + DEFS.each {|name, default_value| + next if pat !~ name + (h[name.length] ||= []) << [name, name] + } + if prefix_optional + if Regexp === prefix_optional + prefix_pat = prefix_optional + else + prefix_pat = /\A#{Regexp.escape prefix_optional}/ + end + DEFS.each {|const, default_value| + next if pat !~ const + next if prefix_pat !~ const + name = $' + (h[name.length] ||= []) << [name, const] + } + end + hh = {} + h.each {|len, pairs| + pairs.each {|name, const| + raise "name crash: #{name}" if hh[name] + hh[name] = true + } + } + h.keys.sort.each {|len| + yield h[len], len + } +end + +erb_new.call(<<'EOS', '%').def_method(Object, "gen_name_to_int_decl(funcname, pat, prefix_optional, guard=nil)") +%if guard +#ifdef <%=guard%> +int <%=funcname%>(const char *str, long len, int *valp); +#endif +%else +int <%=funcname%>(const char *str, long len, int *valp); +%end +EOS + +erb_new.call(<<'EOS', '%').def_method(Object, "gen_name_to_int_func_in_guard(funcname, pat, prefix_optional, guard=nil)") +int +<%=funcname%>(const char *str, long len, int *valp) +{ + switch (len) { +% each_names_with_len(pat, prefix_optional) {|pairs, len| + case <%=len%>: +% pairs.each {|name, const| +#ifdef <%=const%> + if (memcmp(str, <%=c_str name%>, <%=len%>) == 0) { *valp = <%=const%>; return 0; } +#endif +% } + return -1; + +% } + default: + if (!str || !valp) {/* wrong argument */} + return -1; + } +} +EOS + +erb_new.call(<<'EOS', '%').def_method(Object, "gen_name_to_int_func(funcname, pat, prefix_optional, guard=nil)") +%if guard +#ifdef <%=guard%> +<%=gen_name_to_int_func_in_guard(funcname, pat, prefix_optional, guard)%> +#endif +%else +<%=gen_name_to_int_func_in_guard(funcname, pat, prefix_optional, guard)%> +%end +EOS + +NAME_TO_INT_DEFS = [] +def def_name_to_int(funcname, pat, prefix_optional, guard=nil) + decl = gen_name_to_int_decl(funcname, pat, prefix_optional, guard) + func = gen_name_to_int_func(funcname, pat, prefix_optional, guard) + NAME_TO_INT_DEFS << [decl, func] +end + +def reverse_each_name_with_prefix_optional(pat, prefix_pat) + reverse_each_name(pat) {|n| + yield n, n + } + if prefix_pat + reverse_each_name(pat) {|n| + next if prefix_pat !~ n + yield n, $' + } + end +end + +erb_new.call(<<'EOS', '%').def_method(Object, "gen_int_to_name_hash(hash_var, pat, prefix_pat)") + <%=hash_var%> = st_init_numtable(); +% reverse_each_name_with_prefix_optional(pat, prefix_pat) {|n,s| +#ifdef <%=n%> + st_insert(<%=hash_var%>, (st_data_t)<%=n%>, (st_data_t)rb_intern2(<%=c_str s%>, <%=s.length%>)); +#endif +% } +EOS + +erb_new.call(<<'EOS', '%').def_method(Object, "gen_int_to_name_func(func_name, hash_var)") +ID +<%=func_name%>(int val) +{ + st_data_t name; + if (st_lookup(<%=hash_var%>, (st_data_t)val, &name)) + return (ID)name; + return 0; +} +EOS + +erb_new.call(<<'EOS', '%').def_method(Object, "gen_int_to_name_decl(func_name, hash_var)") +ID <%=func_name%>(int val); +EOS + +INTERN_DEFS = [] +def def_intern(func_name, pat, prefix_optional=nil) + prefix_pat = nil + if prefix_optional + if Regexp === prefix_optional + prefix_pat = prefix_optional + else + prefix_pat = /\A#{Regexp.escape prefix_optional}/ + end + end + hash_var = "#{func_name}_hash" + vardef = "static st_table *#{hash_var};" + gen_hash = gen_int_to_name_hash(hash_var, pat, prefix_pat) + decl = gen_int_to_name_decl(func_name, hash_var) + func = gen_int_to_name_func(func_name, hash_var) + INTERN_DEFS << [vardef, gen_hash, decl, func] +end + +def_name_to_int("rsock_family_to_int", /\A(AF_|PF_)/, "AF_") +def_name_to_int("rsock_socktype_to_int", /\ASOCK_/, "SOCK_") +def_name_to_int("rsock_ipproto_to_int", /\AIPPROTO_/, "IPPROTO_") +def_name_to_int("rsock_unknown_level_to_int", /\ASOL_SOCKET\z/, "SOL_") +def_name_to_int("rsock_ip_level_to_int", /\A(SOL_SOCKET\z|IPPROTO_)/, /\A(SOL_|IPPROTO_)/) +def_name_to_int("rsock_so_optname_to_int", /\ASO_/, "SO_") +def_name_to_int("rsock_ip_optname_to_int", /\AIP_/, "IP_") +def_name_to_int("rsock_ipv6_optname_to_int", /\AIPV6_/, "IPV6_", "IPPROTO_IPV6") +def_name_to_int("rsock_tcp_optname_to_int", /\ATCP_/, "TCP_") +def_name_to_int("rsock_udp_optname_to_int", /\AUDP_/, "UDP_") +def_name_to_int("rsock_shutdown_how_to_int", /\ASHUT_/, "SHUT_") +def_name_to_int("rsock_scm_optname_to_int", /\ASCM_/, "SCM_") + +def_intern('rsock_intern_family', /\AAF_/) +def_intern('rsock_intern_family_noprefix', /\AAF_/, "AF_") +def_intern('rsock_intern_protocol_family', /\APF_/) +def_intern('rsock_intern_socktype', /\ASOCK_/) +def_intern('rsock_intern_ipproto', /\AIPPROTO_/) +def_intern('rsock_intern_iplevel', /\A(SOL_SOCKET\z|IPPROTO_)/, /\A(SOL_|IPPROTO_)/) +def_intern('rsock_intern_so_optname', /\ASO_/, "SO_") +def_intern('rsock_intern_ip_optname', /\AIP_/, "IP_") +def_intern('rsock_intern_ipv6_optname', /\AIPV6_/, "IPV6_") +def_intern('rsock_intern_tcp_optname', /\ATCP_/, "TCP_") +def_intern('rsock_intern_udp_optname', /\AUDP_/, "UDP_") +def_intern('rsock_intern_scm_optname', /\ASCM_/, "SCM_") +def_intern('rsock_intern_local_optname', /\ALOCAL_/, "LOCAL_") + +result = erb_new.call(<<'EOS', '%').result(binding) +/* autogenerated file */ + +<%= INTERN_DEFS.map {|vardef, gen_hash, decl, func| vardef }.join("\n") %> + +#ifdef HAVE_LONG_LONG +#define INTEGER2NUM(n) \ + (FIXNUM_MAX < (n) ? ULL2NUM(n) : \ + FIXNUM_MIN > (LONG_LONG)(n) ? LL2NUM(n) : \ + LONG2FIX(n)) +#else +#define INTEGER2NUM(n) \ + (FIXNUM_MAX < (n) ? ULONG2NUM(n) : \ + FIXNUM_MIN > (long)(n) ? LONG2NUM(n) : \ + LONG2FIX(n)) +#endif + +static void +init_constants(void) +{ + /* + * Document-module: Socket::Constants + * + * Socket::Constants provides socket-related constants. All possible + * socket constants are listed in the documentation but they may not all + * be present on your platform. + * + * If the underlying platform doesn't define a constant the corresponding + * Ruby constant is not defined. + * + */ + rb_mSockConst = rb_define_module_under(rb_cSocket, "Constants"); + +<%= gen_const_defs %> +<%= INTERN_DEFS.map {|vardef, gen_hash, decl, func| gen_hash }.join("\n") %> +} + +<%= NAME_TO_INT_DEFS.map {|decl, func| func }.join("\n") %> + +<%= INTERN_DEFS.map {|vardef, gen_hash, decl, func| func }.join("\n") %> + +EOS + +header_result = erb_new.call(<<'EOS', '%').result(binding) +/* autogenerated file */ +<%= gen_const_decls %> +<%= NAME_TO_INT_DEFS.map {|decl, func| decl }.join("\n") %> +<%= INTERN_DEFS.map {|vardef, gen_hash, decl, func| decl }.join("\n") %> +EOS + +if opt_H + File.open(opt_H, 'w') {|f| + f << header_result + } +else + result = header_result + result +end + +if opt_o + File.open(opt_o, 'w') {|f| + f << result + } +else + $stdout << result +end + +__END__ + +SOCK_STREAM nil A stream socket provides a sequenced, reliable two-way connection for a byte stream +SOCK_DGRAM nil A datagram socket provides connectionless, unreliable messaging +SOCK_RAW nil A raw socket provides low-level access for direct access or implementing network protocols +SOCK_RDM nil A reliable datagram socket provides reliable delivery of messages +SOCK_SEQPACKET nil A sequential packet socket provides sequenced, reliable two-way connection for datagrams +SOCK_PACKET nil Device-level packet access +SOCK_NONBLOCK nil Set the O_NONBLOCK file status flag on the open file description (see open(2)) referred to by the new file descriptor. +SOCK_CLOEXEC nil Set the close-on-exec (FD_CLOEXEC) flag on the new file descriptor. + +AF_UNSPEC nil Unspecified protocol, any supported address family +PF_UNSPEC nil Unspecified protocol, any supported address family +AF_INET nil IPv4 protocol +PF_INET nil IPv4 protocol +AF_INET6 nil IPv6 protocol +PF_INET6 nil IPv6 protocol +AF_UNIX nil UNIX sockets +PF_UNIX nil UNIX sockets +AF_AX25 nil AX.25 protocol +PF_AX25 nil AX.25 protocol +AF_IPX nil IPX protocol +PF_IPX nil IPX protocol +AF_APPLETALK nil AppleTalk protocol +PF_APPLETALK nil AppleTalk protocol +AF_LOCAL nil Host-internal protocols +PF_LOCAL nil Host-internal protocols +AF_IMPLINK nil ARPANET IMP protocol +PF_IMPLINK nil ARPANET IMP protocol +AF_PUP nil PARC Universal Packet protocol +PF_PUP nil PARC Universal Packet protocol +AF_CHAOS nil MIT CHAOS protocols +PF_CHAOS nil MIT CHAOS protocols +AF_NS nil XEROX NS protocols +PF_NS nil XEROX NS protocols +AF_ISO nil ISO Open Systems Interconnection protocols +PF_ISO nil ISO Open Systems Interconnection protocols +AF_OSI nil ISO Open Systems Interconnection protocols +PF_OSI nil ISO Open Systems Interconnection protocols +AF_ECMA nil European Computer Manufacturers protocols +PF_ECMA nil European Computer Manufacturers protocols +AF_DATAKIT nil Datakit protocol +PF_DATAKIT nil Datakit protocol +AF_CCITT nil CCITT (now ITU-T) protocols +PF_CCITT nil CCITT (now ITU-T) protocols +AF_SNA nil IBM SNA protocol +PF_SNA nil IBM SNA protocol +AF_DEC nil DECnet protocol +PF_DEC nil DECnet protocol +AF_DECnet nil DECnet protocol +PF_DECnet nil DECnet protocol +AF_DLI nil DEC Direct Data Link Interface protocol +PF_DLI nil DEC Direct Data Link Interface protocol +AF_LAT nil Local Area Transport protocol +PF_LAT nil Local Area Transport protocol +AF_HYLINK nil NSC Hyperchannel protocol +PF_HYLINK nil NSC Hyperchannel protocol +AF_ROUTE nil Internal routing protocol +PF_ROUTE nil Internal routing protocol +AF_LINK nil Link layer interface +PF_LINK nil Link layer interface +AF_COIP nil Connection-oriented IP +PF_COIP nil Connection-oriented IP +AF_CNT nil Computer Network Technology +PF_CNT nil Computer Network Technology +AF_SIP nil Simple Internet Protocol +PF_SIP nil Simple Internet Protocol +AF_NDRV nil Network driver raw access +PF_NDRV nil Network driver raw access +AF_ISDN nil Integrated Services Digital Network +PF_ISDN nil Integrated Services Digital Network +AF_NATM nil Native ATM access +PF_NATM nil Native ATM access +AF_SYSTEM nil Kernel event messages +PF_SYSTEM nil Kernel event messages +AF_NETBIOS nil NetBIOS +PF_NETBIOS nil NetBIOS +AF_PPP nil Point-to-Point Protocol +PF_PPP nil Point-to-Point Protocol +AF_ATM nil Asynchronous Transfer Mode +PF_ATM nil Asynchronous Transfer Mode +AF_NETGRAPH nil Netgraph sockets +PF_NETGRAPH nil Netgraph sockets +AF_MAX nil Maximum address family for this platform +PF_MAX nil Maximum address family for this platform +AF_PACKET nil Direct link-layer access +PF_PACKET nil Direct link-layer access + +AF_E164 nil CCITT (ITU-T) E.164 recommendation +PF_XTP nil eXpress Transfer Protocol +PF_RTIP nil Help Identify RTIP packets +PF_PIP nil Help Identify PIP packets +AF_KEY nil Key management protocol, originally developed for usage with IPsec +PF_KEY nil Key management protocol, originally developed for usage with IPsec +AF_NETLINK nil Kernel user interface device +PF_NETLINK nil Kernel user interface device +AF_RDS nil Reliable Datagram Sockets (RDS) protocol +PF_RDS nil Reliable Datagram Sockets (RDS) protocol +AF_PPPOX nil Generic PPP transport layer, for setting up L2 tunnels (L2TP and PPPoE) +PF_PPPOX nil Generic PPP transport layer, for setting up L2 tunnels (L2TP and PPPoE) +AF_LLC nil Logical link control (IEEE 802.2 LLC) protocol +PF_LLC nil Logical link control (IEEE 802.2 LLC) protocol +AF_IB nil InfiniBand native addressing +PF_IB nil InfiniBand native addressing +AF_MPLS nil Multiprotocol Label Switching +PF_MPLS nil Multiprotocol Label Switching +AF_CAN nil Controller Area Network automotive bus protocol +PF_CAN nil Controller Area Network automotive bus protocol +AF_TIPC nil TIPC, "cluster domain sockets" protocol +PF_TIPC nil TIPC, "cluster domain sockets" protocol +AF_BLUETOOTH nil Bluetooth low-level socket protocol +PF_BLUETOOTH nil Bluetooth low-level socket protocol +AF_ALG nil Interface to kernel crypto API +PF_ALG nil Interface to kernel crypto API +AF_VSOCK nil VSOCK (originally "VMWare VSockets") protocol for hypervisor-guest communication +PF_VSOCK nil VSOCK (originally "VMWare VSockets") protocol for hypervisor-guest communication +AF_KCM nil KCM (kernel connection multiplexor) interface +PF_KCM nil KCM (kernel connection multiplexor) interface +AF_XDP nil XDP (express data path) interface +PF_XDP nil XDP (express data path) interface + +MSG_OOB nil Process out-of-band data +MSG_PEEK nil Peek at incoming message +MSG_DONTROUTE nil Send without using the routing tables +MSG_EOR nil Data completes record +MSG_TRUNC nil Data discarded before delivery +MSG_CTRUNC nil Control data lost before delivery +MSG_WAITALL nil Wait for full request or error +MSG_DONTWAIT nil This message should be non-blocking +MSG_EOF nil Data completes connection +MSG_FLUSH nil Start of a hold sequence. Dumps to so_temp +MSG_HOLD nil Hold fragment in so_temp +MSG_SEND nil Send the packet in so_temp +MSG_HAVEMORE nil Data ready to be read +MSG_RCVMORE nil Data remains in the current packet +MSG_COMPAT nil End of record +MSG_PROXY nil Wait for full request +MSG_FIN +MSG_SYN +MSG_CONFIRM nil Confirm path validity +MSG_RST +MSG_ERRQUEUE nil Fetch message from error queue +MSG_NOSIGNAL nil Do not generate SIGPIPE +MSG_MORE nil Sender will send more +MSG_FASTOPEN nil Reduce step of the handshake process + +SOL_SOCKET nil Socket-level options +SOL_IP nil IP socket options +SOL_IPX nil IPX socket options +SOL_AX25 nil AX.25 socket options +SOL_ATALK nil AppleTalk socket options +SOL_TCP nil TCP socket options +SOL_UDP nil UDP socket options + +IPPROTO_IP 0 Dummy protocol for IP +IPPROTO_ICMP 1 Control message protocol +IPPROTO_IGMP nil Group Management Protocol +IPPROTO_GGP nil Gateway to Gateway Protocol +IPPROTO_TCP 6 TCP +IPPROTO_EGP nil Exterior Gateway Protocol +IPPROTO_PUP nil PARC Universal Packet protocol +IPPROTO_UDP 17 UDP +IPPROTO_IDP nil XNS IDP +IPPROTO_HELLO nil "hello" routing protocol +IPPROTO_ND nil Sun net disk protocol +IPPROTO_TP nil ISO transport protocol class 4 +IPPROTO_XTP nil Xpress Transport Protocol +IPPROTO_EON nil ISO cnlp +IPPROTO_BIP +IPPROTO_AH nil IP6 auth header +IPPROTO_DSTOPTS nil IP6 destination option +IPPROTO_ESP nil IP6 Encapsulated Security Payload +IPPROTO_FRAGMENT nil IP6 fragmentation header +IPPROTO_HOPOPTS nil IP6 hop-by-hop options +IPPROTO_ICMPV6 nil ICMP6 +IPPROTO_IPV6 nil IP6 header +IPPROTO_NONE nil IP6 no next header +IPPROTO_ROUTING nil IP6 routing header + +IPPROTO_RAW 255 Raw IP packet +IPPROTO_MAX nil Maximum IPPROTO constant + +# Some port configuration +IPPORT_RESERVED 1024 Default minimum address for bind or connect +IPPORT_USERRESERVED 5000 Default maximum address for bind or connect + +# Some reserved IP v.4 addresses +INADDR_ANY 0x00000000 A socket bound to INADDR_ANY receives packets from all interfaces and sends from the default IP address +INADDR_BROADCAST 0xffffffff The network broadcast address +INADDR_LOOPBACK 0x7F000001 The loopback address +INADDR_UNSPEC_GROUP 0xe0000000 The reserved multicast group +INADDR_ALLHOSTS_GROUP 0xe0000001 Multicast group for all systems on this subset +INADDR_MAX_LOCAL_GROUP 0xe00000ff The last local network multicast group +INADDR_NONE 0xffffffff A bitmask for matching no valid IP address + +# IP [gs]etsockopt options +IP_OPTIONS nil IP options to be included in packets +IP_HDRINCL nil Header is included with data +IP_TOS nil IP type-of-service +IP_TTL nil IP time-to-live +IP_RECVOPTS nil Receive all IP options with datagram +IP_RECVRETOPTS nil Receive all IP options for response +IP_RECVDSTADDR nil Receive IP destination address with datagram +IP_RETOPTS nil IP options to be included in datagrams +IP_MINTTL nil Minimum TTL allowed for received packets +IP_DONTFRAG nil Don't fragment packets +IP_SENDSRCADDR nil Source address for outgoing UDP datagrams +IP_ONESBCAST nil Force outgoing broadcast datagrams to have the undirected broadcast address +IP_RECVTTL nil Receive IP TTL with datagrams +IP_RECVIF nil Receive interface information with datagrams +IP_RECVSLLA nil Receive link-layer address with datagrams +IP_PORTRANGE nil Set the port range for sockets with unspecified port numbers +IP_MULTICAST_IF nil IP multicast interface +IP_MULTICAST_TTL nil IP multicast TTL +IP_MULTICAST_LOOP nil IP multicast loopback +IP_ADD_MEMBERSHIP nil Add a multicast group membership +IP_DROP_MEMBERSHIP nil Drop a multicast group membership +IP_DEFAULT_MULTICAST_TTL nil Default multicast TTL +IP_DEFAULT_MULTICAST_LOOP nil Default multicast loopback +IP_MAX_MEMBERSHIPS nil Maximum number multicast groups a socket can join +IP_ROUTER_ALERT nil Notify transit routers to more closely examine the contents of an IP packet +IP_PKTINFO nil Receive packet information with datagrams +IP_PKTOPTIONS nil Receive packet options with datagrams +IP_MTU_DISCOVER nil Path MTU discovery +IP_RECVERR nil Enable extended reliable error message passing +IP_RECVTOS nil Receive TOS with incoming packets +IP_MTU nil The Maximum Transmission Unit of the socket +IP_FREEBIND nil Allow binding to nonexistent IP addresses +IP_IPSEC_POLICY nil IPsec security policy +IP_XFRM_POLICY +IP_PASSSEC nil Retrieve security context with datagram +IP_TRANSPARENT nil Transparent proxy +IP_PMTUDISC_DONT nil Never send DF frames +IP_PMTUDISC_WANT nil Use per-route hints +IP_PMTUDISC_DO nil Always send DF frames +IP_UNBLOCK_SOURCE nil Unblock IPv4 multicast packets with a give source address +IP_BLOCK_SOURCE nil Block IPv4 multicast packets with a give source address +IP_ADD_SOURCE_MEMBERSHIP nil Add a multicast group membership +IP_DROP_SOURCE_MEMBERSHIP nil Drop a multicast group membership +IP_MSFILTER nil Multicast source filtering + +MCAST_JOIN_GROUP nil Join a multicast group +MCAST_BLOCK_SOURCE nil Block multicast packets from this source +MCAST_UNBLOCK_SOURCE nil Unblock multicast packets from this source +MCAST_LEAVE_GROUP nil Leave a multicast group +MCAST_JOIN_SOURCE_GROUP nil Join a multicast source group +MCAST_LEAVE_SOURCE_GROUP nil Leave a multicast source group +MCAST_MSFILTER nil Multicast source filtering +MCAST_EXCLUDE nil Exclusive multicast source filter +MCAST_INCLUDE nil Inclusive multicast source filter + +SO_DEBUG nil Debug info recording +SO_REUSEADDR nil Allow local address reuse +SO_REUSEPORT nil Allow local address and port reuse +SO_TYPE nil Get the socket type +SO_ERROR nil Get and clear the error status +SO_DONTROUTE nil Use interface addresses +SO_BROADCAST nil Permit sending of broadcast messages +SO_SNDBUF nil Send buffer size +SO_RCVBUF nil Receive buffer size +SO_SNDBUFFORCE nil Send buffer size without wmem_max limit (Linux 2.6.14) +SO_RCVBUFFORCE nil Receive buffer size without rmem_max limit (Linux 2.6.14) +SO_KEEPALIVE nil Keep connections alive +SO_OOBINLINE nil Leave received out-of-band data in-line +SO_NO_CHECK nil Disable checksums +SO_PRIORITY nil The protocol-defined priority for all packets on this socket +SO_LINGER nil Linger on close if data is present +SO_PASSCRED nil Receive SCM_CREDENTIALS messages +SO_PEERCRED nil The credentials of the foreign process connected to this socket +SO_RCVLOWAT nil Receive low-water mark +SO_SNDLOWAT nil Send low-water mark +SO_RCVTIMEO nil Receive timeout +SO_SNDTIMEO nil Send timeout +SO_ACCEPTCONN nil Socket has had listen() called on it +SO_USELOOPBACK nil Bypass hardware when possible +SO_ACCEPTFILTER nil There is an accept filter +SO_USER_COOKIE nil Setting an identifier for ipfw purpose mainly +SO_DONTTRUNC nil Retain unread data +SO_WANTMORE nil Give a hint when more data is ready +SO_WANTOOBFLAG nil OOB data is wanted in MSG_FLAG on receive +SO_NREAD nil Get first packet byte count +SO_NKE nil Install socket-level Network Kernel Extension +SO_NOSIGPIPE nil Don't SIGPIPE on EPIPE +SO_SECURITY_AUTHENTICATION +SO_SECURITY_ENCRYPTION_TRANSPORT +SO_SECURITY_ENCRYPTION_NETWORK +SO_BINDTODEVICE nil Only send packets from the given interface +SO_ATTACH_FILTER nil Attach an accept filter +SO_DETACH_FILTER nil Detach an accept filter +SO_GET_FILTER nil Obtain filter set by SO_ATTACH_FILTER (Linux 3.8) +SO_PEERNAME nil Name of the connecting user +SO_TIMESTAMP nil Receive timestamp with datagrams (timeval) +SO_TIMESTAMPNS nil Receive nanosecond timestamp with datagrams (timespec) +SO_BINTIME nil Receive timestamp with datagrams (bintime) +SO_RECVUCRED nil Receive user credentials with datagram +SO_MAC_EXEMPT nil Mandatory Access Control exemption for unlabeled peers +SO_ALLZONES nil Bypass zone boundaries +SO_PEERSEC nil Obtain the security credentials (Linux 2.6.2) +SO_PASSSEC nil Toggle security context passing (Linux 2.6.18) +SO_MARK nil Set the mark for mark-based routing (Linux 2.6.25) +SO_TIMESTAMPING nil Time stamping of incoming and outgoing packets (Linux 2.6.30) +SO_PROTOCOL nil Protocol given for socket() (Linux 2.6.32) +SO_DOMAIN nil Domain given for socket() (Linux 2.6.32) +SO_RXQ_OVFL nil Toggle cmsg for number of packets dropped (Linux 2.6.33) +SO_WIFI_STATUS nil Toggle cmsg for wifi status (Linux 3.3) +SO_PEEK_OFF nil Set the peek offset (Linux 3.4) +SO_NOFCS nil Set netns of a socket (Linux 3.4) +SO_LOCK_FILTER nil Lock the filter attached to a socket (Linux 3.9) +SO_SELECT_ERR_QUEUE nil Make select() detect socket error queue with errorfds (Linux 3.10) +SO_BUSY_POLL nil Set the threshold in microseconds for low latency polling (Linux 3.11) +SO_MAX_PACING_RATE nil Cap the rate computed by transport layer. [bytes per second] (Linux 3.13) +SO_BPF_EXTENSIONS nil Query supported BPF extensions (Linux 3.14) +SO_SETFIB nil Set the associated routing table for the socket (FreeBSD) +SO_RTABLE nil Set the routing table for this socket (OpenBSD) +SO_INCOMING_CPU nil Receive the cpu attached to the socket (Linux 3.19) +SO_INCOMING_NAPI_ID nil Receive the napi ID attached to a RX queue (Linux 4.12) +SO_CONNECT_TIME nil Returns the number of seconds a socket has been connected. This option is only valid for connection-oriented protocols (Windows) + +SOPRI_INTERACTIVE nil Interactive socket priority +SOPRI_NORMAL nil Normal socket priority +SOPRI_BACKGROUND nil Background socket priority + +IPX_TYPE + +TCP_NODELAY nil Don't delay sending to coalesce packets +TCP_MAXSEG nil Set maximum segment size +TCP_CONNECTION_INFO nil Retrieve information about this socket (macOS) +TCP_CORK nil Don't send partial frames (Linux 2.2, glibc 2.2) +TCP_DEFER_ACCEPT nil Don't notify a listening socket until data is ready (Linux 2.4, glibc 2.2) +TCP_INFO nil Retrieve information about this socket (Linux 2.4, glibc 2.2) +TCP_KEEPALIVE nil Idle time before keepalive probes are sent (macOS) +TCP_KEEPCNT nil Maximum number of keepalive probes allowed before dropping a connection (Linux 2.4, glibc 2.2) +TCP_KEEPIDLE nil Idle time before keepalive probes are sent (Linux 2.4, glibc 2.2) +TCP_KEEPINTVL nil Time between keepalive probes (Linux 2.4, glibc 2.2) +TCP_LINGER2 nil Lifetime of orphaned FIN_WAIT2 sockets (Linux 2.4, glibc 2.2) +TCP_MD5SIG nil Use MD5 digests (RFC2385, Linux 2.6.20, glibc 2.7) +TCP_NOOPT nil Don't use TCP options +TCP_NOPUSH nil Don't push the last block of write +TCP_QUICKACK nil Enable quickack mode (Linux 2.4.4, glibc 2.3) +TCP_SYNCNT nil Number of SYN retransmits before a connection is dropped (Linux 2.4, glibc 2.2) +TCP_WINDOW_CLAMP nil Clamp the size of the advertised window (Linux 2.4, glibc 2.2) +TCP_FASTOPEN nil Reduce step of the handshake process (Linux 3.7, glibc 2.18) +TCP_CONGESTION nil TCP congestion control algorithm (Linux 2.6.13, glibc 2.6) +TCP_COOKIE_TRANSACTIONS nil TCP Cookie Transactions (Linux 2.6.33, glibc 2.18) +TCP_QUEUE_SEQ nil Sequence of a queue for repair mode (Linux 3.5, glibc 2.18) +TCP_REPAIR nil Repair mode (Linux 3.5, glibc 2.18) +TCP_REPAIR_OPTIONS nil Options for repair mode (Linux 3.5, glibc 2.18) +TCP_REPAIR_QUEUE nil Queue for repair mode (Linux 3.5, glibc 2.18) +TCP_THIN_DUPACK nil Duplicated acknowledgments handling for thin-streams (Linux 2.6.34, glibc 2.18) +TCP_THIN_LINEAR_TIMEOUTS nil Linear timeouts for thin-streams (Linux 2.6.34, glibc 2.18) +TCP_TIMESTAMP nil TCP timestamp (Linux 3.9, glibc 2.18) +TCP_USER_TIMEOUT nil Max timeout before a TCP connection is aborted (Linux 2.6.37, glibc 2.18) + +UDP_CORK nil Don't send partial frames (Linux 2.5.44, glibc 2.11) + +EAI_ADDRFAMILY nil Address family for hostname not supported +EAI_AGAIN nil Temporary failure in name resolution +EAI_BADFLAGS nil Invalid flags +EAI_FAIL nil Non-recoverable failure in name resolution +EAI_FAMILY nil Address family not supported +EAI_MEMORY nil Memory allocation failure +EAI_NODATA nil No address associated with hostname +EAI_NONAME nil Hostname nor servname, or not known +EAI_OVERFLOW nil Argument buffer overflow +EAI_SERVICE nil Servname not supported for socket type +EAI_SOCKTYPE nil Socket type not supported +EAI_SYSTEM nil System error returned in errno +EAI_BADHINTS nil Invalid value for hints +EAI_PROTOCOL nil Resolved protocol is unknown +EAI_MAX nil Maximum error code from getaddrinfo + +AI_PASSIVE nil Get address to use with bind() +AI_CANONNAME nil Fill in the canonical name +AI_NUMERICHOST nil Prevent host name resolution +AI_NUMERICSERV nil Prevent service name resolution +AI_MASK nil Valid flag mask for getaddrinfo (not for application use) +AI_ALL nil Allow all addresses +AI_V4MAPPED_CFG nil Accept IPv4 mapped addresses if the kernel supports it +AI_ADDRCONFIG nil Accept only if any address is assigned +AI_V4MAPPED nil Accept IPv4-mapped IPv6 addresses +AI_DEFAULT nil Default flags for getaddrinfo + +NI_MAXHOST nil Maximum length of a hostname +NI_MAXSERV nil Maximum length of a service name +NI_NOFQDN nil An FQDN is not required for local hosts, return only the local part +NI_NUMERICHOST nil Return a numeric address +NI_NAMEREQD nil A name is required +NI_NUMERICSERV nil Return the service name as a digit string +NI_DGRAM nil The service specified is a datagram service (looks up UDP ports) + +SHUT_RD 0 Shut down the reading side of the socket +SHUT_WR 1 Shut down the writing side of the socket +SHUT_RDWR 2 Shut down the both sides of the socket + +IPV6_JOIN_GROUP nil Join a group membership +IPV6_LEAVE_GROUP nil Leave a group membership +IPV6_MTU_DISCOVER nil Path MTU discovery +IPV6_MULTICAST_HOPS nil IP6 multicast hops +IPV6_MULTICAST_IF nil IP6 multicast interface +IPV6_MULTICAST_LOOP nil IP6 multicast loopback +IPV6_UNICAST_HOPS nil IP6 unicast hops +IPV6_V6ONLY nil Only bind IPv6 with a wildcard bind +IPV6_CHECKSUM nil Checksum offset for raw sockets +IPV6_DONTFRAG nil Don't fragment packets +IPV6_DSTOPTS nil Destination option +IPV6_HOPLIMIT nil Hop limit +IPV6_HOPOPTS nil Hop-by-hop option +IPV6_NEXTHOP nil Next hop address +IPV6_PATHMTU nil Retrieve current path MTU +IPV6_PKTINFO nil Receive packet information with datagram +IPV6_RECVDSTOPTS nil Receive all IP6 options for response +IPV6_RECVERR nil Enable extended reliable error message passing +IPV6_RECVHOPLIMIT nil Receive hop limit with datagram +IPV6_RECVHOPOPTS nil Receive hop-by-hop options +IPV6_RECVPKTINFO nil Receive destination IP address and incoming interface +IPV6_RECVRTHDR nil Receive routing header +IPV6_RECVTCLASS nil Receive traffic class +IPV6_RTHDR nil Allows removal of sticky routing headers +IPV6_RTHDRDSTOPTS nil Allows removal of sticky destination options header +IPV6_RTHDR_TYPE_0 nil Routing header type 0 +IPV6_RECVPATHMTU nil Receive current path MTU with datagram +IPV6_TCLASS nil Specify the traffic class +IPV6_USE_MIN_MTU nil Use the minimum MTU size + +INET_ADDRSTRLEN 16 Maximum length of an IPv4 address string +INET6_ADDRSTRLEN 46 Maximum length of an IPv6 address string +IFNAMSIZ nil Maximum interface name size +IF_NAMESIZE nil Maximum interface name size + +SOMAXCONN 5 Maximum connection requests that may be queued for a socket + +SCM_RIGHTS nil Access rights +SCM_TIMESTAMP nil Timestamp (timeval) +SCM_TIMESTAMPNS nil Timespec (timespec) +SCM_TIMESTAMPING nil Timestamp (timespec list) (Linux 2.6.30) +SCM_BINTIME nil Timestamp (bintime) +SCM_CREDENTIALS nil The sender's credentials +SCM_CREDS nil Process credentials +SCM_UCRED nil User credentials +SCM_WIFI_STATUS nil Wifi status (Linux 3.3) + +LOCAL_PEERCRED nil Retrieve peer credentials +LOCAL_CREDS nil Pass credentials to receiver +LOCAL_CONNWAIT nil Connect blocks until accepted + +IFF_802_1Q_VLAN nil 802.1Q VLAN device +IFF_ALLMULTI nil receive all multicast packets +IFF_ALTPHYS nil use alternate physical connection +IFF_AUTOMEDIA nil auto media select active +IFF_BONDING nil bonding master or slave +IFF_BRIDGE_PORT nil device used as bridge port +IFF_BROADCAST nil broadcast address valid +IFF_CANTCONFIG nil unconfigurable using ioctl(2) +IFF_DEBUG nil turn on debugging +IFF_DISABLE_NETPOLL nil disable netpoll at run-time +IFF_DONT_BRIDGE nil disallow bridging this ether dev +IFF_DORMANT nil driver signals dormant +IFF_DRV_OACTIVE nil tx hardware queue is full +IFF_DRV_RUNNING nil resources allocated +IFF_DYING nil interface is winding down +IFF_DYNAMIC nil dialup device with changing addresses +IFF_EBRIDGE nil ethernet bridging device +IFF_ECHO nil echo sent packets +IFF_ISATAP nil ISATAP interface (RFC4214) +IFF_LINK0 nil per link layer defined bit 0 +IFF_LINK1 nil per link layer defined bit 1 +IFF_LINK2 nil per link layer defined bit 2 +IFF_LIVE_ADDR_CHANGE nil hardware address change when it's running +IFF_LOOPBACK nil loopback net +IFF_LOWER_UP nil driver signals L1 up +IFF_MACVLAN_PORT nil device used as macvlan port +IFF_MASTER nil master of a load balancer +IFF_MASTER_8023AD nil bonding master, 802.3ad. +IFF_MASTER_ALB nil bonding master, balance-alb. +IFF_MASTER_ARPMON nil bonding master, ARP mon in use +IFF_MONITOR nil user-requested monitor mode +IFF_MULTICAST nil supports multicast +IFF_NOARP nil no address resolution protocol +IFF_NOTRAILERS nil avoid use of trailers +IFF_OACTIVE nil transmission in progress +IFF_OVS_DATAPATH nil device used as Open vSwitch datapath port +IFF_POINTOPOINT nil point-to-point link +IFF_PORTSEL nil can set media type +IFF_PPROMISC nil user-requested promisc mode +IFF_PROMISC nil receive all packets +IFF_RENAMING nil interface is being renamed +IFF_ROUTE nil routing entry installed +IFF_RUNNING nil resources allocated +IFF_SIMPLEX nil can't hear own transmissions +IFF_SLAVE nil slave of a load balancer +IFF_SLAVE_INACTIVE nil bonding slave not the curr. active +IFF_SLAVE_NEEDARP nil need ARPs for validation +IFF_SMART nil interface manages own routes +IFF_STATICARP nil static ARP +IFF_SUPP_NOFCS nil sending custom FCS +IFF_TEAM_PORT nil used as team port +IFF_TX_SKB_SHARING nil sharing skbs on transmit +IFF_UNICAST_FLT nil unicast filtering +IFF_UP nil interface is up +IFF_WAN_HDLC nil WAN HDLC device +IFF_XMIT_DST_RELEASE nil dev_hard_start_xmit() is allowed to release skb->dst +IFF_VOLATILE nil volatile flags +IFF_CANTCHANGE nil flags not changeable diff --git a/ext/socket/option.c b/ext/socket/option.c new file mode 100644 index 0000000000..0d818d0c70 --- /dev/null +++ b/ext/socket/option.c @@ -0,0 +1,1477 @@ +#include "rubysocket.h" + +VALUE rb_cSockOpt; + +#define pack_var(v) rb_str_new((const char *)&(v), sizeof(v)) + +#define CAT(x,y) x##y +#define XCAT(x,y) CAT(x,y) + +#if defined(__linux__) || \ + defined(__GNU__) /* GNU/Hurd */ || \ + defined(__FreeBSD__) || \ + defined(__DragonFly__) || \ + defined(__APPLE__) || \ + defined(_WIN32) || \ + defined(__CYGWIN__) +# define TYPE_IP_MULTICAST_LOOP int +# define TYPE_IP_MULTICAST_TTL int +#else +/* The original IP multicast implementation by Steve Deering + * NetBSD + * OpenBSD + * SunOS + */ +# define TYPE_IP_MULTICAST_LOOP byte +# define TYPE_IP_MULTICAST_TTL byte +# define USE_INSPECT_BYTE 1 +#endif + +#define check_size(len, size) \ + ((len) == (size) ? \ + (void)0 : \ + rb_raise(rb_eTypeError, "size differ. expected as "#size"=%d but %ld", \ + (int)size, (long)(len))) + +static VALUE +sockopt_pack_byte(VALUE value) +{ + char i = NUM2CHR(rb_to_int(value)); + return pack_var(i); +} + +static VALUE +sockopt_pack_int(VALUE value) +{ + int i = NUM2INT(rb_to_int(value)); + return pack_var(i); +} + +static VALUE +constant_to_sym(int constant, ID (*intern_const)(int)) +{ + ID name = intern_const(constant); + if (name) { + return ID2SYM(name); + } + + return INT2NUM(constant); +} + +static VALUE +optname_to_sym(int level, int optname) +{ + switch (level) { + case SOL_SOCKET: + return constant_to_sym(optname, rsock_intern_so_optname); + case IPPROTO_IP: + return constant_to_sym(optname, rsock_intern_ip_optname); +#ifdef IPPROTO_IPV6 + case IPPROTO_IPV6: + return constant_to_sym(optname, rsock_intern_ipv6_optname); +#endif + case IPPROTO_TCP: + return constant_to_sym(optname, rsock_intern_tcp_optname); + case IPPROTO_UDP: + return constant_to_sym(optname, rsock_intern_udp_optname); + default: + return INT2NUM(optname); + } +} + +/* + * call-seq: + * Socket::Option.new(family, level, optname, data) => sockopt + * + * Returns a new Socket::Option object. + * + * sockopt = Socket::Option.new(:INET, :SOCKET, :KEEPALIVE, [1].pack("i")) + * p sockopt #=> #<Socket::Option: INET SOCKET KEEPALIVE 1> + * + */ +static VALUE +sockopt_initialize(VALUE self, VALUE vfamily, VALUE vlevel, VALUE voptname, VALUE data) +{ + int family = rsock_family_arg(vfamily); + int level = rsock_level_arg(family, vlevel); + int optname = rsock_optname_arg(family, level, voptname); + StringValue(data); + rb_ivar_set(self, rb_intern("family"), INT2NUM(family)); + rb_ivar_set(self, rb_intern("level"), INT2NUM(level)); + rb_ivar_set(self, rb_intern("optname"), INT2NUM(optname)); + rb_ivar_set(self, rb_intern("data"), data); + return self; +} + +VALUE +rsock_sockopt_new(int family, int level, int optname, VALUE data) +{ + VALUE obj = rb_obj_alloc(rb_cSockOpt); + StringValue(data); + sockopt_initialize(obj, INT2NUM(family), INT2NUM(level), INT2NUM(optname), data); + return (VALUE)obj; +} + +/* + * call-seq: + * sockopt.family => integer + * + * returns the socket family as an integer. + * + * p Socket::Option.new(:INET6, :IPV6, :RECVPKTINFO, [1].pack("i!")).family + * #=> 10 + */ +static VALUE +sockopt_family_m(VALUE self) +{ + return rb_attr_get(self, rb_intern("family")); +} + +static int +sockopt_level(VALUE self) +{ + return NUM2INT(rb_attr_get(self, rb_intern("level"))); +} + +/* + * call-seq: + * sockopt.level => integer + * + * returns the socket level as an integer. + * + * p Socket::Option.new(:INET6, :IPV6, :RECVPKTINFO, [1].pack("i!")).level + * #=> 41 + */ +static VALUE +sockopt_level_m(VALUE self) +{ + return INT2NUM(sockopt_level(self)); +} + +static int +sockopt_optname(VALUE self) +{ + return NUM2INT(rb_attr_get(self, rb_intern("optname"))); +} + +/* + * call-seq: + * sockopt.optname => integer + * + * returns the socket option name as an integer. + * + * p Socket::Option.new(:INET6, :IPV6, :RECVPKTINFO, [1].pack("i!")).optname + * #=> 2 + */ +static VALUE +sockopt_optname_m(VALUE self) +{ + return INT2NUM(sockopt_optname(self)); +} + +/* + * call-seq: + * sockopt.data => string + * sockopt.to_s => string + * + * returns the socket option data as a string. + * + * p Socket::Option.new(:INET6, :IPV6, :RECVPKTINFO, [1].pack("i!")).data + * #=> "\x01\x00\x00\x00" + */ +static VALUE +sockopt_data(VALUE self) +{ + VALUE v = rb_attr_get(self, rb_intern("data")); + StringValue(v); + return v; +} + +/* + * call-seq: + * Socket::Option.byte(family, level, optname, integer) => sockopt + * + * Creates a new Socket::Option object which contains a byte as data. + * + * p Socket::Option.byte(:INET, :SOCKET, :KEEPALIVE, 1) + * #=> #<Socket::Option: INET SOCKET KEEPALIVE 1> + */ +static VALUE +sockopt_s_byte(VALUE klass, VALUE vfamily, VALUE vlevel, VALUE voptname, VALUE vint) +{ + int family = rsock_family_arg(vfamily); + int level = rsock_level_arg(family, vlevel); + int optname = rsock_optname_arg(family, level, voptname); + return rsock_sockopt_new(family, level, optname, sockopt_pack_byte(vint)); +} + +/* + * call-seq: + * sockopt.byte => integer + * + * Returns the data in _sockopt_ as an byte. + * + * sockopt = Socket::Option.byte(:INET, :SOCKET, :KEEPALIVE, 1) + * p sockopt.byte => 1 + */ +static VALUE +sockopt_byte(VALUE self) +{ + VALUE data = sockopt_data(self); + StringValue(data); + check_size(RSTRING_LEN(data), sizeof(char)); + return CHR2FIX(*RSTRING_PTR(data)); +} + +/* + * call-seq: + * Socket::Option.int(family, level, optname, integer) => sockopt + * + * Creates a new Socket::Option object which contains an int as data. + * + * The size and endian is dependent on the platform. + * + * p Socket::Option.int(:INET, :SOCKET, :KEEPALIVE, 1) + * #=> #<Socket::Option: INET SOCKET KEEPALIVE 1> + */ +static VALUE +sockopt_s_int(VALUE klass, VALUE vfamily, VALUE vlevel, VALUE voptname, VALUE vint) +{ + int family = rsock_family_arg(vfamily); + int level = rsock_level_arg(family, vlevel); + int optname = rsock_optname_arg(family, level, voptname); + return rsock_sockopt_new(family, level, optname, sockopt_pack_int(vint)); +} + +/* + * call-seq: + * sockopt.int => integer + * + * Returns the data in _sockopt_ as an int. + * + * The size and endian is dependent on the platform. + * + * sockopt = Socket::Option.int(:INET, :SOCKET, :KEEPALIVE, 1) + * p sockopt.int => 1 + */ +static VALUE +sockopt_int(VALUE self) +{ + int i; + VALUE data = sockopt_data(self); + StringValue(data); + check_size(RSTRING_LEN(data), sizeof(int)); + memcpy((char*)&i, RSTRING_PTR(data), sizeof(int)); + return INT2NUM(i); +} + +/* + * call-seq: + * Socket::Option.bool(family, level, optname, bool) => sockopt + * + * Creates a new Socket::Option object which contains boolean as data. + * Actually 0 or 1 as int is used. + * + * require 'socket' + * + * p Socket::Option.bool(:INET, :SOCKET, :KEEPALIVE, true) + * #=> #<Socket::Option: INET SOCKET KEEPALIVE 1> + * + * p Socket::Option.bool(:INET, :SOCKET, :KEEPALIVE, false) + * #=> #<Socket::Option: AF_INET SOCKET KEEPALIVE 0> + * + */ +static VALUE +sockopt_s_bool(VALUE klass, VALUE vfamily, VALUE vlevel, VALUE voptname, VALUE vbool) +{ + int family = rsock_family_arg(vfamily); + int level = rsock_level_arg(family, vlevel); + int optname = rsock_optname_arg(family, level, voptname); + int i = RTEST(vbool) ? 1 : 0; + return rsock_sockopt_new(family, level, optname, pack_var(i)); +} + +/* + * call-seq: + * sockopt.bool => true or false + * + * Returns the data in _sockopt_ as an boolean value. + * + * sockopt = Socket::Option.int(:INET, :SOCKET, :KEEPALIVE, 1) + * p sockopt.bool => true + */ +static VALUE +sockopt_bool(VALUE self) +{ + int i; + long len; + VALUE data = sockopt_data(self); + StringValue(data); + len = RSTRING_LEN(data); + if (len == 1) { + return *RSTRING_PTR(data) == 0 ? Qfalse : Qtrue; + } + check_size(len, sizeof(int)); + memcpy((char*)&i, RSTRING_PTR(data), len); + return i == 0 ? Qfalse : Qtrue; +} + +/* + * call-seq: + * Socket::Option.linger(onoff, secs) => sockopt + * + * Creates a new Socket::Option object for SOL_SOCKET/SO_LINGER. + * + * _onoff_ should be an integer or a boolean. + * + * _secs_ should be the number of seconds. + * + * p Socket::Option.linger(true, 10) + * #=> #<Socket::Option: UNSPEC SOCKET LINGER on 10sec> + * + */ +static VALUE +sockopt_s_linger(VALUE klass, VALUE vonoff, VALUE vsecs) +{ + VALUE tmp; + struct linger l; + memset(&l, 0, sizeof(l)); + if (!NIL_P(tmp = rb_check_to_integer(vonoff, "to_int"))) + l.l_onoff = NUM2INT(tmp); + else + l.l_onoff = RTEST(vonoff) ? 1 : 0; + l.l_linger = NUM2INT(vsecs); + return rsock_sockopt_new(AF_UNSPEC, SOL_SOCKET, SO_LINGER, pack_var(l)); +} + +/* + * call-seq: + * sockopt.linger => [bool, seconds] + * + * Returns the linger data in _sockopt_ as a pair of boolean and integer. + * + * sockopt = Socket::Option.linger(true, 10) + * p sockopt.linger => [true, 10] + */ +static VALUE +sockopt_linger(VALUE self) +{ + int level = sockopt_level(self); + int optname = sockopt_optname(self); + VALUE data = sockopt_data(self); + struct linger l; + VALUE vonoff, vsecs; + + if (level != SOL_SOCKET || optname != SO_LINGER) + rb_raise(rb_eTypeError, "linger socket option expected"); + check_size(RSTRING_LEN(data), sizeof(struct linger)); + memcpy((char*)&l, RSTRING_PTR(data), sizeof(struct linger)); + switch (l.l_onoff) { + case 0: vonoff = Qfalse; break; + case 1: vonoff = Qtrue; break; + default: vonoff = INT2NUM(l.l_onoff); break; + } + vsecs = INT2NUM(l.l_linger); + return rb_assoc_new(vonoff, vsecs); +} + +/* + * call-seq: + * Socket::Option.ipv4_multicast_loop(integer) => sockopt + * + * Creates a new Socket::Option object for IP_MULTICAST_LOOP. + * + * The size is dependent on the platform. + * + * sockopt = Socket::Option.int(:INET, :IPPROTO_IP, :IP_MULTICAST_LOOP, 1) + * p sockopt.int => 1 + * + * p Socket::Option.ipv4_multicast_loop(10) + * #=> #<Socket::Option: INET IP MULTICAST_LOOP 10> + * + */ +static VALUE +sockopt_s_ipv4_multicast_loop(VALUE klass, VALUE value) +{ + +#if defined(IPPROTO_IP) && defined(IP_MULTICAST_LOOP) + VALUE o = XCAT(sockopt_pack_,TYPE_IP_MULTICAST_LOOP)(value); + return rsock_sockopt_new(AF_INET, IPPROTO_IP, IP_MULTICAST_LOOP, o); +#else +# error IPPROTO_IP or IP_MULTICAST_LOOP is not implemented +#endif +} + +/* + * call-seq: + * sockopt.ipv4_multicast_loop => integer + * + * Returns the ipv4_multicast_loop data in _sockopt_ as an integer. + * + * sockopt = Socket::Option.ipv4_multicast_loop(10) + * p sockopt.ipv4_multicast_loop => 10 + */ +static VALUE +sockopt_ipv4_multicast_loop(VALUE self) +{ + int family = NUM2INT(sockopt_family_m(self)); + int level = sockopt_level(self); + int optname = sockopt_optname(self); + +#if defined(IPPROTO_IP) && defined(IP_MULTICAST_LOOP) + if (family == AF_INET && level == IPPROTO_IP && optname == IP_MULTICAST_LOOP) { + return XCAT(sockopt_,TYPE_IP_MULTICAST_LOOP)(self); + } +#endif + rb_raise(rb_eTypeError, "ipv4_multicast_loop socket option expected"); + UNREACHABLE_RETURN(Qnil); +} + +#define inspect_ipv4_multicast_loop(a,b,c,d) \ + XCAT(inspect_,TYPE_IP_MULTICAST_LOOP)(a,b,c,d) + +/* + * call-seq: + * Socket::Option.ipv4_multicast_ttl(integer) => sockopt + * + * Creates a new Socket::Option object for IP_MULTICAST_TTL. + * + * The size is dependent on the platform. + * + * p Socket::Option.ipv4_multicast_ttl(10) + * #=> #<Socket::Option: INET IP MULTICAST_TTL 10> + * + */ +static VALUE +sockopt_s_ipv4_multicast_ttl(VALUE klass, VALUE value) +{ +#if defined(IPPROTO_IP) && defined(IP_MULTICAST_TTL) + VALUE o = XCAT(sockopt_pack_,TYPE_IP_MULTICAST_TTL)(value); + return rsock_sockopt_new(AF_INET, IPPROTO_IP, IP_MULTICAST_TTL, o); +#else +# error IPPROTO_IP or IP_MULTICAST_TTL is not implemented +#endif +} + +/* + * call-seq: + * sockopt.ipv4_multicast_ttl => integer + * + * Returns the ipv4_multicast_ttl data in _sockopt_ as an integer. + * + * sockopt = Socket::Option.ipv4_multicast_ttl(10) + * p sockopt.ipv4_multicast_ttl => 10 + */ +static VALUE +sockopt_ipv4_multicast_ttl(VALUE self) +{ + int family = NUM2INT(sockopt_family_m(self)); + int level = sockopt_level(self); + int optname = sockopt_optname(self); + +#if defined(IPPROTO_IP) && defined(IP_MULTICAST_TTL) + if (family == AF_INET && level == IPPROTO_IP && optname == IP_MULTICAST_TTL) { + return XCAT(sockopt_,TYPE_IP_MULTICAST_TTL)(self); + } +#endif + rb_raise(rb_eTypeError, "ipv4_multicast_ttl socket option expected"); + UNREACHABLE_RETURN(Qnil); +} + +#define inspect_ipv4_multicast_ttl(a,b,c,d) \ + XCAT(inspect_,TYPE_IP_MULTICAST_TTL)(a,b,c,d) + +static int +inspect_int(int level, int optname, VALUE data, VALUE ret) +{ + if (RSTRING_LEN(data) == sizeof(int)) { + int i; + memcpy((char*)&i, RSTRING_PTR(data), sizeof(int)); + rb_str_catf(ret, " %d", i); + return 1; + } + else { + return 0; + } +} + +#ifdef USE_INSPECT_BYTE +static int +inspect_byte(int level, int optname, VALUE data, VALUE ret) +{ + if (RSTRING_LEN(data) == sizeof(unsigned char)) { + rb_str_catf(ret, " %d", (unsigned char)*RSTRING_PTR(data)); + return 1; + } + else { + return 0; + } +} +#endif + +static int +inspect_errno(int level, int optname, VALUE data, VALUE ret) +{ + if (RSTRING_LEN(data) == sizeof(int)) { + int i; + char *err; + memcpy((char*)&i, RSTRING_PTR(data), sizeof(int)); + err = strerror(i); + rb_str_catf(ret, " %s (%d)", err, i); + return 1; + } + else { + return 0; + } +} + +#if defined(IPV6_MULTICAST_LOOP) +static int +inspect_uint(int level, int optname, VALUE data, VALUE ret) +{ + if (RSTRING_LEN(data) == sizeof(int)) { + unsigned int i; + memcpy((char*)&i, RSTRING_PTR(data), sizeof(unsigned int)); + rb_str_catf(ret, " %u", i); + return 1; + } + else { + return 0; + } +} +#endif + +#if defined(SOL_SOCKET) && defined(SO_LINGER) /* POSIX */ +static int +inspect_linger(int level, int optname, VALUE data, VALUE ret) +{ + if (RSTRING_LEN(data) == sizeof(struct linger)) { + struct linger s; + memcpy((char*)&s, RSTRING_PTR(data), sizeof(s)); + switch (s.l_onoff) { + case 0: rb_str_cat2(ret, " off"); break; + case 1: rb_str_cat2(ret, " on"); break; + default: rb_str_catf(ret, " on(%d)", s.l_onoff); break; + } + rb_str_catf(ret, " %dsec", s.l_linger); + return 1; + } + else { + return 0; + } +} +#endif + +#if defined(SOL_SOCKET) && defined(SO_TYPE) /* POSIX */ +static int +inspect_socktype(int level, int optname, VALUE data, VALUE ret) +{ + if (RSTRING_LEN(data) == sizeof(int)) { + int i; + ID id; + memcpy((char*)&i, RSTRING_PTR(data), sizeof(int)); + id = rsock_intern_socktype(i); + if (id) + rb_str_catf(ret, " %s", rb_id2name(id)); + else + rb_str_catf(ret, " %d", i); + return 1; + } + else { + return 0; + } +} +#endif + +static int +inspect_timeval_as_interval(int level, int optname, VALUE data, VALUE ret) +{ + if (RSTRING_LEN(data) == sizeof(struct timeval)) { + struct timeval s; + memcpy((char*)&s, RSTRING_PTR(data), sizeof(s)); + rb_str_catf(ret, " %ld.%06ldsec", (long)s.tv_sec, (long)s.tv_usec); + return 1; + } + else { + return 0; + } +} + +/* + * socket option for IPv4 multicast is bit confusing. + * + * IP Multicast is implemented by Steve Deering at first: + * IP Multicast Extensions for 4.3BSD UNIX and related systems + * (MULTICAST 1.2 Release) + * http://www.kohala.com/start/mcast.api.txt + * + * There are 2 socket options which takes a u_char (unsigned char). + * + * IP_MULTICAST_TTL + * IP_MULTICAST_LOOP + * + * However Linux and FreeBSD setsockname accepts int argument + * as well as u_char. + * Their getsockname returns int. + * + * There are 3 socket options which takes a struct. + * + * IP_MULTICAST_IF: struct in_addr + * IP_ADD_MEMBERSHIP: struct ip_mreq + * IP_DROP_MEMBERSHIP: struct ip_mreq + * + * But they uses an IP address to specify an interface. + * This means the API cannot specify an unnumbered interface. + * + * Linux 2.4 introduces struct ip_mreqn to fix this problem. + * struct ip_mreqn has imr_ifindex field to specify interface index. + * + * IP_MULTICAST_IF: struct ip_mreqn + * IP_ADD_MEMBERSHIP: struct ip_mreqn + * IP_DROP_MEMBERSHIP: struct ip_mreqn + * + * FreeBSD 7 obtained struct ip_mreqn for IP_MULTICAST_IF. + * http://www.FreeBSD.org/cgi/cvsweb.cgi/src/sys/netinet/in.h.diff?r1=1.99;r2=1.100 + * + * Another hackish workaround is "RFC 1724 hack". + * RFC 1724 section 3.3 suggests unnumbered interfaces + * specified by pseudo address 0.0.0.0/8. + * NetBSD 4 and FreeBSD 5 documented it. + * http://cvsweb.netbsd.org/cgi-bin/cvsweb.cgi/src/share/man/man4/ip.4.diff?r1=1.16&r2=1.17 + * http://www.FreeBSD.org/cgi/cvsweb.cgi/src/share/man/man4/ip.4.diff?r1=1.37;r2=1.38 + * FreeBSD 7.0 removed it. + * http://www.FreeBSD.org/cgi/cvsweb.cgi/src/share/man/man4/ip.4.diff?r1=1.49;r2=1.50 + * + * RFC 1724 hack is not supported by Socket::Option#inspect because + * it is not distinguishable by the size. + */ + +#if !defined HAVE_INET_NTOP && ! defined _WIN32 +const char * +inet_ntop(int af, const void *addr, char *numaddr, size_t numaddr_len) +{ +#ifdef HAVE_INET_NTOA + struct in_addr in; + memcpy(&in.s_addr, addr, sizeof(in.s_addr)); + snprintf(numaddr, numaddr_len, "%s", inet_ntoa(in)); +#else + unsigned long x = ntohl(*(unsigned long*)addr); + snprintf(numaddr, numaddr_len, "%d.%d.%d.%d", + (int) (x>>24) & 0xff, (int) (x>>16) & 0xff, + (int) (x>> 8) & 0xff, (int) (x>> 0) & 0xff); +#endif + return numaddr; +} +#endif + +/* Although the buffer size needed depends on the prefixes, "%u" may generate "4294967295". */ +static int +rb_if_indextoname(const char *succ_prefix, const char *fail_prefix, unsigned int ifindex, char *buf, size_t len) +{ +#if defined(HAVE_IF_INDEXTONAME) + char ifbuf[IFNAMSIZ]; + if (if_indextoname(ifindex, ifbuf)) + return snprintf(buf, len, "%s%s", succ_prefix, ifbuf); + else + return snprintf(buf, len, "%s%u", fail_prefix, ifindex); +#else +# ifndef IFNAMSIZ +# define IFNAMSIZ (sizeof(unsigned int)*3+1) +# endif + return snprintf(buf, len, "%s%u", fail_prefix, ifindex); +#endif +} + +#if defined(IPPROTO_IP) && defined(HAVE_TYPE_STRUCT_IP_MREQ) /* 4.4BSD, GNU/Linux */ +static int +inspect_ipv4_mreq(int level, int optname, VALUE data, VALUE ret) +{ + if (RSTRING_LEN(data) == sizeof(struct ip_mreq)) { + struct ip_mreq s; + char addrbuf[INET_ADDRSTRLEN]; + memcpy((char*)&s, RSTRING_PTR(data), sizeof(s)); + if (inet_ntop(AF_INET, &s.imr_multiaddr, addrbuf, (socklen_t)sizeof(addrbuf)) == NULL) + rb_str_cat2(ret, " invalid-address"); + else + rb_str_catf(ret, " %s", addrbuf); + if (inet_ntop(AF_INET, &s.imr_interface, addrbuf, (socklen_t)sizeof(addrbuf)) == NULL) + rb_str_catf(ret, " invalid-address"); + else + rb_str_catf(ret, " %s", addrbuf); + return 1; + } + else { + return 0; + } +} +#endif + +#if defined(IPPROTO_IP) && defined(HAVE_TYPE_STRUCT_IP_MREQN) /* GNU/Linux, FreeBSD 7 */ +static int +inspect_ipv4_mreqn(int level, int optname, VALUE data, VALUE ret) +{ + if (RSTRING_LEN(data) == sizeof(struct ip_mreqn)) { + struct ip_mreqn s; + char addrbuf[INET_ADDRSTRLEN], ifbuf[32+IFNAMSIZ]; + memcpy((char*)&s, RSTRING_PTR(data), sizeof(s)); + if (inet_ntop(AF_INET, &s.imr_multiaddr, addrbuf, (socklen_t)sizeof(addrbuf)) == NULL) + rb_str_cat2(ret, " invalid-address"); + else + rb_str_catf(ret, " %s", addrbuf); + if (inet_ntop(AF_INET, &s.imr_address, addrbuf, (socklen_t)sizeof(addrbuf)) == NULL) + rb_str_catf(ret, " invalid-address"); + else + rb_str_catf(ret, " %s", addrbuf); + rb_if_indextoname(" ", " ifindex:", s.imr_ifindex, ifbuf, sizeof(ifbuf)); + rb_str_cat2(ret, ifbuf); + return 1; + } + else { + return 0; + } +} +#endif + +#if defined(IPPROTO_IP) && defined(HAVE_TYPE_STRUCT_IP_MREQ) /* 4.4BSD, GNU/Linux */ +static int +inspect_ipv4_add_drop_membership(int level, int optname, VALUE data, VALUE ret) +{ + if (RSTRING_LEN(data) == sizeof(struct ip_mreq)) + return inspect_ipv4_mreq(level, optname, data, ret); +# if defined(HAVE_TYPE_STRUCT_IP_MREQN) + else if (RSTRING_LEN(data) == sizeof(struct ip_mreqn)) + return inspect_ipv4_mreqn(level, optname, data, ret); +# endif + else + return 0; +} +#endif + +#if defined(IPPROTO_IP) && defined(IP_MULTICAST_IF) && defined(HAVE_TYPE_STRUCT_IP_MREQN) /* 4.4BSD, GNU/Linux */ +static int +inspect_ipv4_multicast_if(int level, int optname, VALUE data, VALUE ret) +{ + if (RSTRING_LEN(data) == sizeof(struct in_addr)) { + struct in_addr s; + char addrbuf[INET_ADDRSTRLEN]; + memcpy((char*)&s, RSTRING_PTR(data), sizeof(s)); + if (inet_ntop(AF_INET, &s, addrbuf, (socklen_t)sizeof(addrbuf)) == NULL) + rb_str_cat2(ret, " invalid-address"); + else + rb_str_catf(ret, " %s", addrbuf); + return 1; + } + else if (RSTRING_LEN(data) == sizeof(struct ip_mreqn)) { + return inspect_ipv4_mreqn(level, optname, data, ret); + } + else { + return 0; + } +} +#endif + +#if defined(IPV6_MULTICAST_IF) /* POSIX, RFC 3493 */ +static int +inspect_ipv6_multicast_if(int level, int optname, VALUE data, VALUE ret) +{ + if (RSTRING_LEN(data) == sizeof(int)) { + char ifbuf[32+IFNAMSIZ]; + unsigned int ifindex; + memcpy((char*)&ifindex, RSTRING_PTR(data), sizeof(unsigned int)); + rb_if_indextoname(" ", " ", ifindex, ifbuf, sizeof(ifbuf)); + rb_str_cat2(ret, ifbuf); + return 1; + } + else { + return 0; + } +} +#endif + +#if defined(IPPROTO_IPV6) && defined(HAVE_TYPE_STRUCT_IPV6_MREQ) /* POSIX, RFC 3493 */ +static int +inspect_ipv6_mreq(int level, int optname, VALUE data, VALUE ret) +{ + if (RSTRING_LEN(data) == sizeof(struct ipv6_mreq)) { + struct ipv6_mreq s; + char addrbuf[INET6_ADDRSTRLEN], ifbuf[32+IFNAMSIZ]; + memcpy((char*)&s, RSTRING_PTR(data), sizeof(s)); + if (inet_ntop(AF_INET6, &s.ipv6mr_multiaddr, addrbuf, (socklen_t)sizeof(addrbuf)) == NULL) + rb_str_cat2(ret, " invalid-address"); + else + rb_str_catf(ret, " %s", addrbuf); + rb_if_indextoname(" ", " interface:", s.ipv6mr_interface, ifbuf, sizeof(ifbuf)); + rb_str_cat2(ret, ifbuf); + return 1; + } + else { + return 0; + } +} +#endif + +#if defined(IPPROTO_TCP) && defined(TCP_INFO) && defined(HAVE_TYPE_STRUCT_TCP_INFO) + +#ifdef __FreeBSD__ +# ifndef HAVE_CONST_TCP_ESTABLISHED +# define TCP_ESTABLISHED TCPS_ESTABLISHED +# endif +# ifndef HAVE_CONST_TCP_SYN_SENT +# define TCP_SYN_SENT TCPS_SYN_SENT +# endif +# ifndef HAVE_CONST_TCP_SYN_RECV +# define TCP_SYN_RECV TCPS_SYN_RECEIVED +# endif +# ifndef HAVE_CONST_TCP_FIN_WAIT1 +# define TCP_FIN_WAIT1 TCPS_FIN_WAIT_1 +# endif +# ifndef HAVE_CONST_TCP_FIN_WAIT2 +# define TCP_FIN_WAIT2 TCPS_FIN_WAIT_2 +# endif +# ifndef HAVE_CONST_TCP_TIME_WAIT +# define TCP_TIME_WAIT TCPS_TIME_WAIT +# endif +# ifndef HAVE_CONST_TCP_CLOSE +# define TCP_CLOSE TCPS_CLOSED +# endif +# ifndef HAVE_CONST_TCP_CLOSE_WAIT +# define TCP_CLOSE_WAIT TCPS_CLOSE_WAIT +# endif +# ifndef HAVE_CONST_TCP_LAST_ACK +# define TCP_LAST_ACK TCPS_LAST_ACK +# endif +# ifndef HAVE_CONST_TCP_LISTEN +# define TCP_LISTEN TCPS_LISTEN +# endif +# ifndef HAVE_CONST_TCP_CLOSING +# define TCP_CLOSING TCPS_CLOSING +# endif +#endif + +#if defined(HAVE_CONST_TCP_ESTABLISHED) && !defined(TCP_ESTABLISHED) +# define TCP_ESTABLISHED TCP_ESTABLISHED +#endif +#if defined(HAVE_CONST_TCP_SYN_SENT) && !defined(TCP_SYN_SENT) +# define TCP_SYN_SENT TCP_SYN_SENT +#endif +#if defined(HAVE_CONST_TCP_SYN_RECV) && !defined(TCP_SYN_RECV) +# define TCP_SYN_RECV TCP_SYN_RECV +#endif +#if defined(HAVE_CONST_TCP_FIN_WAIT1) && !defined(TCP_FIN_WAIT1) +# define TCP_FIN_WAIT1 TCP_FIN_WAIT1 +#endif +#if defined(HAVE_CONST_TCP_FIN_WAIT2) && !defined(TCP_FIN_WAIT2) +# define TCP_FIN_WAIT2 TCP_FIN_WAIT2 +#endif +#if defined(HAVE_CONST_TCP_TIME_WAIT) && !defined(TCP_TIME_WAIT) +# define TCP_TIME_WAIT TCP_TIME_WAIT +#endif +#if defined(HAVE_CONST_TCP_CLOSE) && !defined(TCP_CLOSE) +# define TCP_CLOSE TCP_CLOSE +#endif +#if defined(HAVE_CONST_TCP_CLOSE_WAIT) && !defined(TCP_CLOSE_WAIT) +# define TCP_CLOSE_WAIT TCP_CLOSE_WAIT +#endif +#if defined(HAVE_CONST_TCP_LAST_ACK) && !defined(TCP_LAST_ACK) +# define TCP_LAST_ACK TCP_LAST_ACK +#endif +#if defined(HAVE_CONST_TCP_LISTEN) && !defined(TCP_LISTEN) +# define TCP_LISTEN TCP_LISTEN +#endif +#if defined(HAVE_CONST_TCP_CLOSING) && !defined(TCP_CLOSING) +# define TCP_CLOSING TCP_CLOSING +#endif + +static void +inspect_tcpi_options(VALUE ret, uint8_t options) +{ + int sep = '='; + + rb_str_cat2(ret, " options"); +#define INSPECT_TCPI_OPTION(optval, name) \ + if (options & (optval)) { \ + options &= ~(uint8_t)(optval); \ + rb_str_catf(ret, "%c%s", sep, name); \ + sep = ','; \ + } +#ifdef TCPI_OPT_TIMESTAMPS /* GNU/Linux, FreeBSD */ + INSPECT_TCPI_OPTION(TCPI_OPT_TIMESTAMPS, "TIMESTAMPS"); +#endif +#ifdef TCPI_OPT_SACK /* GNU/Linux, FreeBSD */ + INSPECT_TCPI_OPTION(TCPI_OPT_SACK, "SACK"); +#endif +#ifdef TCPI_OPT_WSCALE /* GNU/Linux, FreeBSD */ + INSPECT_TCPI_OPTION(TCPI_OPT_WSCALE, "WSCALE"); +#endif +#ifdef TCPI_OPT_ECN /* GNU/Linux, FreeBSD */ + INSPECT_TCPI_OPTION(TCPI_OPT_ECN, "ECN"); +#endif +#ifdef TCPI_OPT_ECN_SEEN /* GNU/Linux */ + INSPECT_TCPI_OPTION(TCPI_OPT_ECN_SEEN, "ECN_SEEN"); +#endif +#ifdef TCPI_OPT_SYN_DATA /* GNU/Linux */ + INSPECT_TCPI_OPTION(TCPI_OPT_SYN_DATA, "SYN_DATA"); +#endif +#ifdef TCPI_OPT_TOE /* FreeBSD */ + INSPECT_TCPI_OPTION(TCPI_OPT_TOE, "TOE"); +#endif +#undef INSPECT_TCPI_OPTION + + if (options || sep == '=') { + rb_str_catf(ret, "%c%u", sep, options); + } +} + +static void +inspect_tcpi_usec(VALUE ret, const char *prefix, uint32_t t) +{ + rb_str_catf(ret, "%s%u.%06us", prefix, t / 1000000, t % 1000000); +} + +#if !defined __FreeBSD__ && ( \ + defined HAVE_STRUCT_TCP_INFO_TCPI_LAST_DATA_SENT || \ + defined HAVE_STRUCT_TCP_INFO_TCPI_LAST_DATA_RECV || \ + defined HAVE_STRUCT_TCP_INFO_TCPI_LAST_ACK_SENT || \ + defined HAVE_STRUCT_TCP_INFO_TCPI_LAST_ACK_RECV || \ + 0) +static void +inspect_tcpi_msec(VALUE ret, const char *prefix, uint32_t t) +{ + rb_str_catf(ret, "%s%u.%03us", prefix, t / 1000, t % 1000); +} +#endif + +#ifdef __FreeBSD__ +# define inspect_tcpi_rto(ret, t) inspect_tcpi_usec(ret, " rto=", t) +# define inspect_tcpi_last_data_recv(ret, t) inspect_tcpi_usec(ret, " last_data_recv=", t) +# define inspect_tcpi_rtt(ret, t) inspect_tcpi_usec(ret, " rtt=", t) +# define inspect_tcpi_rttvar(ret, t) inspect_tcpi_usec(ret, " rttvar=", t) +#else +# define inspect_tcpi_rto(ret, t) inspect_tcpi_usec(ret, " rto=", t) +# define inspect_tcpi_ato(ret, t) inspect_tcpi_usec(ret, " ato=", t) +# define inspect_tcpi_last_data_sent(ret, t) inspect_tcpi_msec(ret, " last_data_sent=", t) +# define inspect_tcpi_last_data_recv(ret, t) inspect_tcpi_msec(ret, " last_data_recv=", t) +# define inspect_tcpi_last_ack_sent(ret, t) inspect_tcpi_msec(ret, " last_ack_sent=", t) +# define inspect_tcpi_last_ack_recv(ret, t) inspect_tcpi_msec(ret, " last_ack_recv=", t) +# define inspect_tcpi_rtt(ret, t) inspect_tcpi_usec(ret, " rtt=", t) +# define inspect_tcpi_rttvar(ret, t) inspect_tcpi_usec(ret, " rttvar=", t) +# define inspect_tcpi_rcv_rtt(ret, t) inspect_tcpi_usec(ret, " rcv_rtt=", t) +#endif + +static int +inspect_tcp_info(int level, int optname, VALUE data, VALUE ret) +{ + size_t actual_size = RSTRING_LEN(data); + if (sizeof(struct tcp_info) <= actual_size) { + struct tcp_info s; + memcpy((char*)&s, RSTRING_PTR(data), sizeof(s)); +#ifdef HAVE_STRUCT_TCP_INFO_TCPI_STATE + switch (s.tcpi_state) { +# ifdef TCP_ESTABLISHED + case TCP_ESTABLISHED: rb_str_cat_cstr(ret, " state=ESTABLISHED"); break; +# endif +# ifdef TCP_SYN_SENT + case TCP_SYN_SENT: rb_str_cat_cstr(ret, " state=SYN_SENT"); break; +# endif +# ifdef TCP_SYN_RECV + case TCP_SYN_RECV: rb_str_cat_cstr(ret, " state=SYN_RECV"); break; +# endif +# ifdef TCP_FIN_WAIT1 + case TCP_FIN_WAIT1: rb_str_cat_cstr(ret, " state=FIN_WAIT1"); break; +# endif +# ifdef TCP_FIN_WAIT2 + case TCP_FIN_WAIT2: rb_str_cat_cstr(ret, " state=FIN_WAIT2"); break; +# endif +# ifdef TCP_TIME_WAIT + case TCP_TIME_WAIT: rb_str_cat_cstr(ret, " state=TIME_WAIT"); break; +# endif +# ifdef TCP_CLOSE + case TCP_CLOSE: rb_str_cat_cstr(ret, " state=CLOSED"); break; /* RFC 793 uses "CLOSED", not "CLOSE" */ +# endif +# ifdef TCP_CLOSE_WAIT + case TCP_CLOSE_WAIT: rb_str_cat_cstr(ret, " state=CLOSE_WAIT"); break; +# endif +# ifdef TCP_LAST_ACK + case TCP_LAST_ACK: rb_str_cat_cstr(ret, " state=LAST_ACK"); break; +# endif +# ifdef TCP_LISTEN + case TCP_LISTEN: rb_str_cat_cstr(ret, " state=LISTEN"); break; +# endif +# ifdef TCP_CLOSING + case TCP_CLOSING: rb_str_cat_cstr(ret, " state=CLOSING"); break; +# endif + default: rb_str_catf(ret, " state=%u", s.tcpi_state); break; + } +#endif +#ifdef HAVE_STRUCT_TCP_INFO_TCPI_CA_STATE + switch (s.tcpi_ca_state) { + case TCP_CA_Open: rb_str_cat_cstr(ret, " ca_state=Open"); break; + case TCP_CA_Disorder: rb_str_cat_cstr(ret, " ca_state=Disorder"); break; + case TCP_CA_CWR: rb_str_cat_cstr(ret, " ca_state=CWR"); break; + case TCP_CA_Recovery: rb_str_cat_cstr(ret, " ca_state=Recovery"); break; + case TCP_CA_Loss: rb_str_cat_cstr(ret, " ca_state=Loss"); break; + default: rb_str_catf(ret, " ca_state=%u", s.tcpi_ca_state); break; + } +#endif +#ifdef HAVE_STRUCT_TCP_INFO_TCPI_RETRANSMITS + rb_str_catf(ret, " retransmits=%u", s.tcpi_retransmits); +#endif +#ifdef HAVE_STRUCT_TCP_INFO_TCPI_PROBES + rb_str_catf(ret, " probes=%u", s.tcpi_probes); +#endif +#ifdef HAVE_STRUCT_TCP_INFO_TCPI_BACKOFF + rb_str_catf(ret, " backoff=%u", s.tcpi_backoff); +#endif +#ifdef HAVE_STRUCT_TCP_INFO_TCPI_OPTIONS + inspect_tcpi_options(ret, s.tcpi_options); +#endif +#ifdef HAVE_STRUCT_TCP_INFO_TCPI_SND_WSCALE + rb_str_catf(ret, " snd_wscale=%u", s.tcpi_snd_wscale); +#endif +#ifdef HAVE_STRUCT_TCP_INFO_TCPI_RCV_WSCALE + rb_str_catf(ret, " rcv_wscale=%u", s.tcpi_rcv_wscale); +#endif +#ifdef HAVE_STRUCT_TCP_INFO_TCPI_RTO + inspect_tcpi_rto(ret, s.tcpi_rto); +#endif +#ifdef HAVE_STRUCT_TCP_INFO_TCPI_ATO + inspect_tcpi_ato(ret, s.tcpi_ato); +#endif +#ifdef HAVE_STRUCT_TCP_INFO_TCPI_SND_MSS + rb_str_catf(ret, " snd_mss=%u", s.tcpi_snd_mss); +#endif +#ifdef HAVE_STRUCT_TCP_INFO_TCPI_RCV_MSS + rb_str_catf(ret, " rcv_mss=%u", s.tcpi_rcv_mss); +#endif +#ifdef HAVE_STRUCT_TCP_INFO_TCPI_UNACKED + rb_str_catf(ret, " unacked=%u", s.tcpi_unacked); +#endif +#ifdef HAVE_STRUCT_TCP_INFO_TCPI_SACKED + rb_str_catf(ret, " sacked=%u", s.tcpi_sacked); +#endif +#ifdef HAVE_STRUCT_TCP_INFO_TCPI_LOST + rb_str_catf(ret, " lost=%u", s.tcpi_lost); +#endif +#ifdef HAVE_STRUCT_TCP_INFO_TCPI_RETRANS + rb_str_catf(ret, " retrans=%u", s.tcpi_retrans); +#endif +#ifdef HAVE_STRUCT_TCP_INFO_TCPI_FACKETS + rb_str_catf(ret, " fackets=%u", s.tcpi_fackets); +#endif +#ifdef HAVE_STRUCT_TCP_INFO_TCPI_LAST_DATA_SENT + inspect_tcpi_last_data_sent(ret, s.tcpi_last_data_sent); +#endif +#ifdef HAVE_STRUCT_TCP_INFO_TCPI_LAST_ACK_SENT + inspect_tcpi_last_ack_sent(ret, s.tcpi_last_ack_sent); +#endif +#ifdef HAVE_STRUCT_TCP_INFO_TCPI_LAST_DATA_RECV + inspect_tcpi_last_data_recv(ret, s.tcpi_last_data_recv); +#endif +#ifdef HAVE_STRUCT_TCP_INFO_TCPI_LAST_ACK_RECV + inspect_tcpi_last_ack_recv(ret, s.tcpi_last_ack_recv); +#endif +#ifdef HAVE_STRUCT_TCP_INFO_TCPI_PMTU + rb_str_catf(ret, " pmtu=%u", s.tcpi_pmtu); +#endif +#ifdef HAVE_STRUCT_TCP_INFO_TCPI_RCV_SSTHRESH + rb_str_catf(ret, " rcv_ssthresh=%u", s.tcpi_rcv_ssthresh); +#endif +#ifdef HAVE_STRUCT_TCP_INFO_TCPI_RTT + inspect_tcpi_rtt(ret, s.tcpi_rtt); +#endif +#ifdef HAVE_STRUCT_TCP_INFO_TCPI_RTTVAR + inspect_tcpi_rttvar(ret, s.tcpi_rttvar); +#endif +#ifdef HAVE_STRUCT_TCP_INFO_TCPI_SND_SSTHRESH + rb_str_catf(ret, " snd_ssthresh=%u", s.tcpi_snd_ssthresh); +#endif +#ifdef HAVE_STRUCT_TCP_INFO_TCPI_SND_CWND + rb_str_catf(ret, " snd_cwnd=%u", s.tcpi_snd_cwnd); +#endif +#ifdef HAVE_STRUCT_TCP_INFO_TCPI_ADVMSS + rb_str_catf(ret, " advmss=%u", s.tcpi_advmss); +#endif +#ifdef HAVE_STRUCT_TCP_INFO_TCPI_REORDERING + rb_str_catf(ret, " reordering=%u", s.tcpi_reordering); +#endif +#ifdef HAVE_STRUCT_TCP_INFO_TCPI_RCV_RTT + inspect_tcpi_rcv_rtt(ret, s.tcpi_rcv_rtt); +#endif +#ifdef HAVE_STRUCT_TCP_INFO_TCPI_RCV_SPACE + rb_str_catf(ret, " rcv_space=%u", s.tcpi_rcv_space); +#endif +#ifdef HAVE_STRUCT_TCP_INFO_TCPI_TOTAL_RETRANS + rb_str_catf(ret, " total_retrans=%u", s.tcpi_total_retrans); +#endif +#ifdef HAVE_STRUCT_TCP_INFO_TCPI_SND_WND + rb_str_catf(ret, " snd_wnd=%u", s.tcpi_snd_wnd); /* FreeBSD */ +#endif +#ifdef HAVE_STRUCT_TCP_INFO_TCPI_SND_BWND + rb_str_catf(ret, " snd_bwnd=%u", s.tcpi_snd_bwnd); /* FreeBSD */ +#endif +#ifdef HAVE_STRUCT_TCP_INFO_TCPI_SND_NXT + rb_str_catf(ret, " snd_nxt=%u", s.tcpi_snd_nxt); /* FreeBSD */ +#endif +#ifdef HAVE_STRUCT_TCP_INFO_TCPI_RCV_NXT + rb_str_catf(ret, " rcv_nxt=%u", s.tcpi_rcv_nxt); /* FreeBSD */ +#endif +#ifdef HAVE_STRUCT_TCP_INFO_TCPI_TOE_TID + rb_str_catf(ret, " toe_tid=%u", s.tcpi_toe_tid); /* FreeBSD */ +#endif +#ifdef HAVE_STRUCT_TCP_INFO_TCPI_SND_REXMITPACK + rb_str_catf(ret, " snd_rexmitpack=%u", s.tcpi_snd_rexmitpack); /* FreeBSD */ +#endif +#ifdef HAVE_STRUCT_TCP_INFO_TCPI_RCV_OOOPACK + rb_str_catf(ret, " rcv_ooopack=%u", s.tcpi_rcv_ooopack); /* FreeBSD */ +#endif +#ifdef HAVE_STRUCT_TCP_INFO_TCPI_SND_ZEROWIN + rb_str_catf(ret, " snd_zerowin=%u", s.tcpi_snd_zerowin); /* FreeBSD */ +#endif + if (sizeof(struct tcp_info) < actual_size) + rb_str_catf(ret, " (%u bytes too long)", (unsigned)(actual_size - sizeof(struct tcp_info))); + return 1; + } + else { + return 0; + } +} +#endif + +#if defined(SOL_SOCKET) && defined(SO_PEERCRED) /* GNU/Linux, OpenBSD */ +#if defined(__OpenBSD__) +#define RUBY_SOCK_PEERCRED struct sockpeercred +#else +#define RUBY_SOCK_PEERCRED struct ucred +#endif +static int +inspect_peercred(int level, int optname, VALUE data, VALUE ret) +{ + if (RSTRING_LEN(data) == sizeof(RUBY_SOCK_PEERCRED)) { + RUBY_SOCK_PEERCRED cred; + memcpy(&cred, RSTRING_PTR(data), sizeof(RUBY_SOCK_PEERCRED)); + rb_str_catf(ret, " pid=%u euid=%u egid=%u", + (unsigned)cred.pid, (unsigned)cred.uid, (unsigned)cred.gid); + rb_str_cat2(ret, " (ucred)"); + return 1; + } + else { + return 0; + } +} +#endif + +#if defined(LOCAL_PEERCRED) /* FreeBSD, MacOS X */ +static int +inspect_local_peercred(int level, int optname, VALUE data, VALUE ret) +{ + if (RSTRING_LEN(data) == sizeof(struct xucred)) { + struct xucred cred; + memcpy(&cred, RSTRING_PTR(data), sizeof(struct xucred)); + if (cred.cr_version != XUCRED_VERSION) + return 0; + rb_str_catf(ret, " version=%u", cred.cr_version); + rb_str_catf(ret, " euid=%u", cred.cr_uid); + if (cred.cr_ngroups) { + int i; + const char *sep = " groups="; + for (i = 0; i < cred.cr_ngroups; i++) { + rb_str_catf(ret, "%s%u", sep, cred.cr_groups[i]); + sep = ","; + } + } + rb_str_cat2(ret, " (xucred)"); + return 1; + } + else { + return 0; + } +} +#endif + + +/* + * call-seq: + * sockopt.inspect => string + * + * Returns a string which shows sockopt in human-readable form. + * + * p Socket::Option.new(:INET, :SOCKET, :KEEPALIVE, [1].pack("i")).inspect + * #=> "#<Socket::Option: INET SOCKET KEEPALIVE 1>" + * + */ +static VALUE +sockopt_inspect(VALUE self) +{ + int family = NUM2INT(sockopt_family_m(self)); + int level = NUM2INT(sockopt_level_m(self)); + int optname = NUM2INT(sockopt_optname_m(self)); + VALUE data = sockopt_data(self); + VALUE v, ret; + ID family_id, level_id, optname_id; + int inspected; + + StringValue(data); + + ret = rb_sprintf("#<%s:", rb_obj_classname(self)); + + family_id = rsock_intern_family_noprefix(family); + if (family_id) + rb_str_catf(ret, " %s", rb_id2name(family_id)); + else + rb_str_catf(ret, " family:%d", family); + + if (level == SOL_SOCKET) { + rb_str_cat2(ret, " SOCKET"); + + optname_id = rsock_intern_so_optname(optname); + if (optname_id) + rb_str_catf(ret, " %s", rb_id2name(optname_id)); + else + rb_str_catf(ret, " optname:%d", optname); + } +#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN + else if (family == AF_UNIX) { + rb_str_catf(ret, " level:%d", level); + + optname_id = rsock_intern_local_optname(optname); + if (optname_id) + rb_str_catf(ret, " %s", rb_id2name(optname_id)); + else + rb_str_catf(ret, " optname:%d", optname); + } +#endif + else if (IS_IP_FAMILY(family)) { + level_id = rsock_intern_iplevel(level); + if (level_id) + rb_str_catf(ret, " %s", rb_id2name(level_id)); + else + rb_str_catf(ret, " level:%d", level); + + v = optname_to_sym(level, optname); + if (SYMBOL_P(v)) + rb_str_catf(ret, " %"PRIsVALUE, rb_sym2str(v)); + else + rb_str_catf(ret, " optname:%d", optname); + } + else { + rb_str_catf(ret, " level:%d", level); + rb_str_catf(ret, " optname:%d", optname); + } + + inspected = 0; + + if (level == SOL_SOCKET) + family = AF_UNSPEC; + switch (family) { + case AF_UNSPEC: + switch (level) { + case SOL_SOCKET: + switch (optname) { +# if defined(SO_DEBUG) /* POSIX */ + case SO_DEBUG: inspected = inspect_int(level, optname, data, ret); break; +# endif +# if defined(SO_ERROR) /* POSIX */ + case SO_ERROR: inspected = inspect_errno(level, optname, data, ret); break; +# endif +# if defined(SO_TYPE) /* POSIX */ + case SO_TYPE: inspected = inspect_socktype(level, optname, data, ret); break; +# endif +# if defined(SO_ACCEPTCONN) /* POSIX */ + case SO_ACCEPTCONN: inspected = inspect_int(level, optname, data, ret); break; +# endif +# if defined(SO_BROADCAST) /* POSIX */ + case SO_BROADCAST: inspected = inspect_int(level, optname, data, ret); break; +# endif +# if defined(SO_REUSEADDR) /* POSIX */ + case SO_REUSEADDR: inspected = inspect_int(level, optname, data, ret); break; +# endif +# if defined(SO_KEEPALIVE) /* POSIX */ + case SO_KEEPALIVE: inspected = inspect_int(level, optname, data, ret); break; +# endif +# if defined(SO_OOBINLINE) /* POSIX */ + case SO_OOBINLINE: inspected = inspect_int(level, optname, data, ret); break; +# endif +# if defined(SO_SNDBUF) /* POSIX */ + case SO_SNDBUF: inspected = inspect_int(level, optname, data, ret); break; +# endif +# if defined(SO_RCVBUF) /* POSIX */ + case SO_RCVBUF: inspected = inspect_int(level, optname, data, ret); break; +# endif +# if defined(SO_DONTROUTE) /* POSIX */ + case SO_DONTROUTE: inspected = inspect_int(level, optname, data, ret); break; +# endif +# if defined(SO_RCVLOWAT) /* POSIX */ + case SO_RCVLOWAT: inspected = inspect_int(level, optname, data, ret); break; +# endif +# if defined(SO_SNDLOWAT) /* POSIX */ + case SO_SNDLOWAT: inspected = inspect_int(level, optname, data, ret); break; +# endif +# if defined(SO_LINGER) /* POSIX */ + case SO_LINGER: inspected = inspect_linger(level, optname, data, ret); break; +# endif +# if defined(SO_RCVTIMEO) /* POSIX */ + case SO_RCVTIMEO: inspected = inspect_timeval_as_interval(level, optname, data, ret); break; +# endif +# if defined(SO_SNDTIMEO) /* POSIX */ + case SO_SNDTIMEO: inspected = inspect_timeval_as_interval(level, optname, data, ret); break; +# endif +# if defined(SO_PEERCRED) /* GNU/Linux, OpenBSD */ + case SO_PEERCRED: inspected = inspect_peercred(level, optname, data, ret); break; +# endif + } + break; + } + break; + + case AF_INET: +#ifdef INET6 + case AF_INET6: +#endif + switch (level) { +# if defined(IPPROTO_IP) + case IPPROTO_IP: + switch (optname) { +# if defined(IP_MULTICAST_IF) && defined(HAVE_TYPE_STRUCT_IP_MREQN) /* 4.4BSD, GNU/Linux */ + case IP_MULTICAST_IF: inspected = inspect_ipv4_multicast_if(level, optname, data, ret); break; +# endif +# if defined(IP_ADD_MEMBERSHIP) /* 4.4BSD, GNU/Linux */ + case IP_ADD_MEMBERSHIP: inspected = inspect_ipv4_add_drop_membership(level, optname, data, ret); break; +# endif +# if defined(IP_DROP_MEMBERSHIP) /* 4.4BSD, GNU/Linux */ + case IP_DROP_MEMBERSHIP: inspected = inspect_ipv4_add_drop_membership(level, optname, data, ret); break; +# endif +# if defined(IP_MULTICAST_LOOP) /* 4.4BSD, GNU/Linux */ + case IP_MULTICAST_LOOP: inspected = inspect_ipv4_multicast_loop(level, optname, data, ret); break; +# endif +# if defined(IP_MULTICAST_TTL) /* 4.4BSD, GNU/Linux */ + case IP_MULTICAST_TTL: inspected = inspect_ipv4_multicast_ttl(level, optname, data, ret); break; +# endif + } + break; +# endif + +# if defined(IPPROTO_IPV6) + case IPPROTO_IPV6: + switch (optname) { +# if defined(IPV6_MULTICAST_HOPS) /* POSIX */ + case IPV6_MULTICAST_HOPS: inspected = inspect_int(level, optname, data, ret); break; +# endif +# if defined(IPV6_MULTICAST_IF) /* POSIX */ + case IPV6_MULTICAST_IF: inspected = inspect_ipv6_multicast_if(level, optname, data, ret); break; +# endif +# if defined(IPV6_MULTICAST_LOOP) /* POSIX */ + case IPV6_MULTICAST_LOOP: inspected = inspect_uint(level, optname, data, ret); break; +# endif +# if defined(IPV6_JOIN_GROUP) /* POSIX */ + case IPV6_JOIN_GROUP: inspected = inspect_ipv6_mreq(level, optname, data, ret); break; +# endif +# if defined(IPV6_LEAVE_GROUP) /* POSIX */ + case IPV6_LEAVE_GROUP: inspected = inspect_ipv6_mreq(level, optname, data, ret); break; +# endif +# if defined(IPV6_UNICAST_HOPS) /* POSIX */ + case IPV6_UNICAST_HOPS: inspected = inspect_int(level, optname, data, ret); break; +# endif +# if defined(IPV6_V6ONLY) /* POSIX */ + case IPV6_V6ONLY: inspected = inspect_int(level, optname, data, ret); break; +# endif + } + break; +# endif + +# if defined(IPPROTO_TCP) + case IPPROTO_TCP: + switch (optname) { +# if defined(TCP_NODELAY) /* POSIX */ + case TCP_NODELAY: inspected = inspect_int(level, optname, data, ret); break; +# endif +# if defined(TCP_INFO) && defined(HAVE_TYPE_STRUCT_TCP_INFO) /* Linux, FreeBSD */ + case TCP_INFO: inspected = inspect_tcp_info(level, optname, data, ret); break; +# endif + } + break; +# endif + } + break; + +#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN + case AF_UNIX: + switch (level) { + case 0: + switch (optname) { +# if defined(LOCAL_PEERCRED) + case LOCAL_PEERCRED: inspected = inspect_local_peercred(level, optname, data, ret); break; +# endif + } + break; + } + break; +#endif + } + + if (!inspected) { + rb_str_cat2(ret, " "); + rb_str_append(ret, rb_str_dump(data)); + } + + rb_str_cat2(ret, ">"); + + return ret; +} + +/* + * call-seq: + * sockopt.unpack(template) => array + * + * Calls String#unpack on sockopt.data. + * + * sockopt = Socket::Option.new(:INET, :SOCKET, :KEEPALIVE, [1].pack("i")) + * p sockopt.unpack("i") #=> [1] + * p sockopt.data.unpack("i") #=> [1] + */ +static VALUE +sockopt_unpack(VALUE self, VALUE template) +{ + return rb_funcall(sockopt_data(self), rb_intern("unpack"), 1, template); +} + +void +rsock_init_sockopt(void) +{ + /* + * Document-class: Socket::Option + * + * Socket::Option represents a socket option used by + * BasicSocket#getsockopt and BasicSocket#setsockopt. A socket option + * contains the socket #family, protocol #level, option name #optname and + * option value #data. + */ + rb_cSockOpt = rb_define_class_under(rb_cSocket, "Option", rb_cObject); + rb_define_method(rb_cSockOpt, "initialize", sockopt_initialize, 4); + rb_define_method(rb_cSockOpt, "family", sockopt_family_m, 0); + rb_define_method(rb_cSockOpt, "level", sockopt_level_m, 0); + rb_define_method(rb_cSockOpt, "optname", sockopt_optname_m, 0); + rb_define_method(rb_cSockOpt, "data", sockopt_data, 0); + rb_define_method(rb_cSockOpt, "inspect", sockopt_inspect, 0); + + rb_define_singleton_method(rb_cSockOpt, "int", sockopt_s_int, 4); + rb_define_method(rb_cSockOpt, "int", sockopt_int, 0); + + rb_define_singleton_method(rb_cSockOpt, "byte", sockopt_s_byte, 4); + rb_define_method(rb_cSockOpt, "byte", sockopt_byte, 0); + + rb_define_singleton_method(rb_cSockOpt, "bool", sockopt_s_bool, 4); + rb_define_method(rb_cSockOpt, "bool", sockopt_bool, 0); + + rb_define_singleton_method(rb_cSockOpt, "linger", sockopt_s_linger, 2); + rb_define_method(rb_cSockOpt, "linger", sockopt_linger, 0); + + rb_define_singleton_method(rb_cSockOpt, "ipv4_multicast_ttl", sockopt_s_ipv4_multicast_ttl, 1); + rb_define_method(rb_cSockOpt, "ipv4_multicast_ttl", sockopt_ipv4_multicast_ttl, 0); + + rb_define_singleton_method(rb_cSockOpt, "ipv4_multicast_loop", sockopt_s_ipv4_multicast_loop, 1); + rb_define_method(rb_cSockOpt, "ipv4_multicast_loop", sockopt_ipv4_multicast_loop, 0); + + rb_define_method(rb_cSockOpt, "unpack", sockopt_unpack, 1); + + rb_define_method(rb_cSockOpt, "to_s", sockopt_data, 0); /* compatibility for ruby before 1.9.2 */ +} diff --git a/ext/socket/raddrinfo.c b/ext/socket/raddrinfo.c new file mode 100644 index 0000000000..6cdf5c6abc --- /dev/null +++ b/ext/socket/raddrinfo.c @@ -0,0 +1,3278 @@ +/************************************************ + + raddrinfo.c - + + created at: Thu Mar 31 12:21:29 JST 1994 + + Copyright (C) 1993-2007 Yukihiro Matsumoto + +************************************************/ + +#include "rubysocket.h" + +// GETADDRINFO_IMPL == 0 : call getaddrinfo/getnameinfo directly +// GETADDRINFO_IMPL == 1 : call getaddrinfo/getnameinfo without gvl (but uncancellable) +// GETADDRINFO_IMPL == 2 : call getaddrinfo/getnameinfo in a dedicated pthread +// (and if the call is interrupted, the pthread is detached) + +#ifndef GETADDRINFO_IMPL +# ifdef GETADDRINFO_EMU +# define GETADDRINFO_IMPL 0 +# elif !defined(HAVE_PTHREAD_CREATE) || !defined(HAVE_PTHREAD_DETACH) || defined(__MINGW32__) || defined(__MINGW64__) +# define GETADDRINFO_IMPL 1 +# else +# define GETADDRINFO_IMPL 2 +# include "ruby/thread_native.h" +# endif +#endif + +#if defined(INET6) && (defined(LOOKUP_ORDER_HACK_INET) || defined(LOOKUP_ORDER_HACK_INET6)) +#define LOOKUP_ORDERS (sizeof(lookup_order_table) / sizeof(lookup_order_table[0])) +static const int lookup_order_table[] = { +#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(const char *nodename, const char *servname, + const 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(const char *nodename, const char *servname, + const 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)) +#endif + +static int str_is_number(const char *); + +#if defined(__APPLE__) +static int +ruby_getaddrinfo__darwin(const char *nodename, const char *servname, + const struct addrinfo *hints, struct addrinfo **res) +{ + /* fix [ruby-core:29427] */ + const char *tmp_servname; + struct addrinfo tmp_hints; + int error; + + 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 + } + } + + error = getaddrinfo(nodename, tmp_servname, &tmp_hints, res); + if (error == 0) { + /* [ruby-dev:23164] */ + 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; + } + } + + return error; +} +#undef getaddrinfo +#define getaddrinfo(node,serv,hints,res) ruby_getaddrinfo__darwin((node),(serv),(hints),(res)) +#endif + +#ifdef HAVE_INET_PTON +static int +parse_numeric_port(const char *service, int *portp) +{ + unsigned long u; + + if (!service) { + *portp = 0; + return 1; + } + + if (strspn(service, "0123456789") != strlen(service)) + return 0; + + errno = 0; + u = STRTOUL(service, NULL, 10); + if (errno) + return 0; + + if (0x10000 <= u) + return 0; + + *portp = (int)u; + + return 1; +} +#endif + +static int +numeric_getaddrinfo(const char *node, const char *service, + const struct addrinfo *hints, + struct addrinfo **res) +{ +#ifdef HAVE_INET_PTON +# if defined __MINGW64__ +# define inet_pton(f,s,d) rb_w32_inet_pton(f,s,d) +# endif + + int port; + + if (node && parse_numeric_port(service, &port)) { + static const struct { + int socktype; + int protocol; + } list[] = { + { SOCK_STREAM, IPPROTO_TCP }, + { SOCK_DGRAM, IPPROTO_UDP }, + { SOCK_RAW, 0 } + }; + struct addrinfo *ai = NULL; + int hint_family = hints ? hints->ai_family : PF_UNSPEC; + int hint_socktype = hints ? hints->ai_socktype : 0; + int hint_protocol = hints ? hints->ai_protocol : 0; + char ipv4addr[4]; +#ifdef AF_INET6 + char ipv6addr[16]; + if ((hint_family == PF_UNSPEC || hint_family == PF_INET6) && + strspn(node, "0123456789abcdefABCDEF.:") == strlen(node) && + inet_pton(AF_INET6, node, ipv6addr)) { + int i; + for (i = numberof(list)-1; 0 <= i; i--) { + if ((hint_socktype == 0 || hint_socktype == list[i].socktype) && + (hint_protocol == 0 || list[i].protocol == 0 || hint_protocol == list[i].protocol)) { + struct addrinfo *ai0 = xcalloc(1, sizeof(struct addrinfo)); + struct sockaddr_in6 *sa = xmalloc(sizeof(struct sockaddr_in6)); + INIT_SOCKADDR_IN6(sa, sizeof(struct sockaddr_in6)); + memcpy(&sa->sin6_addr, ipv6addr, sizeof(ipv6addr)); + sa->sin6_port = htons(port); + ai0->ai_family = PF_INET6; + ai0->ai_socktype = list[i].socktype; + ai0->ai_protocol = hint_protocol ? hint_protocol : list[i].protocol; + ai0->ai_addrlen = sizeof(struct sockaddr_in6); + ai0->ai_addr = (struct sockaddr *)sa; + ai0->ai_canonname = NULL; + ai0->ai_next = ai; + ai = ai0; + } + } + } + else +#endif + if ((hint_family == PF_UNSPEC || hint_family == PF_INET) && + strspn(node, "0123456789.") == strlen(node) && + inet_pton(AF_INET, node, ipv4addr)) { + int i; + for (i = numberof(list)-1; 0 <= i; i--) { + if ((hint_socktype == 0 || hint_socktype == list[i].socktype) && + (hint_protocol == 0 || list[i].protocol == 0 || hint_protocol == list[i].protocol)) { + struct addrinfo *ai0 = xcalloc(1, sizeof(struct addrinfo)); + struct sockaddr_in *sa = xmalloc(sizeof(struct sockaddr_in)); + INIT_SOCKADDR_IN(sa, sizeof(struct sockaddr_in)); + memcpy(&sa->sin_addr, ipv4addr, sizeof(ipv4addr)); + sa->sin_port = htons(port); + ai0->ai_family = PF_INET; + ai0->ai_socktype = list[i].socktype; + ai0->ai_protocol = hint_protocol ? hint_protocol : list[i].protocol; + ai0->ai_addrlen = sizeof(struct sockaddr_in); + ai0->ai_addr = (struct sockaddr *)sa; + ai0->ai_canonname = NULL; + ai0->ai_next = ai; + ai = ai0; + } + } + } + if (ai) { + *res = ai; + return 0; + } + } +#endif + return EAI_FAIL; +} + +void +rb_freeaddrinfo(struct rb_addrinfo *ai) +{ + if (!ai->allocated_by_malloc) { + if (ai->ai) freeaddrinfo(ai->ai); + } + else { + struct addrinfo *ai1, *ai2; + ai1 = ai->ai; + while (ai1) { + ai2 = ai1->ai_next; + xfree(ai1->ai_addr); + xfree(ai1); + ai1 = ai2; + } + } + xfree(ai); +} + +static int +rsock_value_timeout_to_msec(VALUE timeout) +{ + double seconds = NUM2DBL(timeout); + if (seconds < 0) rb_raise(rb_eArgError, "timeout must not be negative"); + + double msec = seconds * 1000.0; + if (msec > UINT_MAX) rb_raise(rb_eArgError, "timeout too large"); + + return (unsigned int)(msec + 0.5); +} + +#if GETADDRINFO_IMPL == 0 + +static int +rb_getaddrinfo(const char *hostp, const char *portp, const struct addrinfo *hints, struct addrinfo **ai, int _timeout) +{ + return getaddrinfo(hostp, portp, hints, ai); +} + +#elif GETADDRINFO_IMPL == 1 + +struct getaddrinfo_arg +{ + const char *node; + const char *service; + const struct addrinfo *hints; + struct addrinfo **res; +}; + +static void * +nogvl_getaddrinfo(void *arg) +{ + int ret; + struct getaddrinfo_arg *ptr = arg; + ret = getaddrinfo(ptr->node, ptr->service, ptr->hints, ptr->res); +#ifdef __linux__ + /* On Linux (mainly Ubuntu 13.04) /etc/nsswitch.conf has mdns4 and + * it cause getaddrinfo to return EAI_SYSTEM/ENOENT. [ruby-list:49420] + */ + if (ret == EAI_SYSTEM && errno == ENOENT) + ret = EAI_NONAME; +#endif + return (void *)(VALUE)ret; +} + +static void * +fork_safe_getaddrinfo(void *arg) +{ + return rb_thread_prevent_fork(nogvl_getaddrinfo, arg); +} + +static int +rb_getaddrinfo(const char *hostp, const char *portp, const struct addrinfo *hints, struct addrinfo **ai, int _timeout) +{ + struct getaddrinfo_arg arg; + MEMZERO(&arg, struct getaddrinfo_arg, 1); + arg.node = hostp; + arg.service = portp; + arg.hints = hints; + arg.res = ai; + return (int)(VALUE)rb_thread_call_without_gvl(fork_safe_getaddrinfo, &arg, RUBY_UBF_IO, 0); +} + +#elif GETADDRINFO_IMPL == 2 + +struct getaddrinfo_arg +{ + char *node, *service; + struct addrinfo hints; + struct addrinfo *ai; + int err, gai_errno, refcount, done, cancelled, timedout; + rb_nativethread_lock_t lock; + rb_nativethread_cond_t cond; + int timeout; +}; + +static struct getaddrinfo_arg * +allocate_getaddrinfo_arg(const char *hostp, const char *portp, const struct addrinfo *hints, int timeout) +{ + size_t hostp_offset = sizeof(struct getaddrinfo_arg); + size_t portp_offset = hostp_offset + (hostp ? strlen(hostp) + 1 : 0); + size_t bufsize = portp_offset + (portp ? strlen(portp) + 1 : 0); + + char *buf = malloc(bufsize); + if (!buf) { + rb_gc(); + buf = malloc(bufsize); + if (!buf) return NULL; + } + struct getaddrinfo_arg *arg = (struct getaddrinfo_arg *)buf; + + if (hostp) { + arg->node = buf + hostp_offset; + memcpy(arg->node, hostp, portp_offset - hostp_offset); + } + else { + arg->node = NULL; + } + + if (portp) { + arg->service = buf + portp_offset; + memcpy(arg->service, portp, bufsize - portp_offset); + } + else { + arg->service = NULL; + } + + arg->hints = *hints; + arg->ai = NULL; + + arg->refcount = 2; + arg->done = arg->cancelled = arg->timedout = 0; + arg->timeout = timeout; + + rb_nativethread_lock_initialize(&arg->lock); + rb_native_cond_initialize(&arg->cond); + + return arg; +} + +static void +free_getaddrinfo_arg(struct getaddrinfo_arg *arg) +{ + rb_native_cond_destroy(&arg->cond); + rb_nativethread_lock_destroy(&arg->lock); + free(arg); +} + +static void * +do_getaddrinfo(void *ptr) +{ + struct getaddrinfo_arg *arg = (struct getaddrinfo_arg *)ptr; + + int err, gai_errno; + err = getaddrinfo(arg->node, arg->service, &arg->hints, &arg->ai); + gai_errno = errno; +#ifdef __linux__ + /* On Linux (mainly Ubuntu 13.04) /etc/nsswitch.conf has mdns4 and + * it cause getaddrinfo to return EAI_SYSTEM/ENOENT. [ruby-list:49420] + */ + if (err == EAI_SYSTEM && errno == ENOENT) + err = EAI_NONAME; +#endif + + int need_free = 0; + rb_nativethread_lock_lock(&arg->lock); + { + arg->err = err; + arg->gai_errno = gai_errno; + if (arg->cancelled) { + if (arg->ai) freeaddrinfo(arg->ai); + } + else { + arg->done = 1; + rb_native_cond_signal(&arg->cond); + } + if (--arg->refcount == 0) need_free = 1; + } + rb_nativethread_lock_unlock(&arg->lock); + + if (need_free) free_getaddrinfo_arg(arg); + + return 0; +} + +static void * +wait_getaddrinfo(void *ptr) +{ + struct getaddrinfo_arg *arg = (struct getaddrinfo_arg *)ptr; + rb_nativethread_lock_lock(&arg->lock); + while (!arg->done && !arg->cancelled) { + long msec = arg->timeout; + if (msec == 0) { + arg->cancelled = 1; + arg->timedout = 1; + } else if (msec > 0) { + rb_native_cond_timedwait(&arg->cond, &arg->lock, msec); + if (!arg->done) { + arg->cancelled = 1; + arg->timedout = 1; + } + } else { + rb_native_cond_wait(&arg->cond, &arg->lock); + } + } + rb_nativethread_lock_unlock(&arg->lock); + return 0; +} + +static void +cancel_getaddrinfo(void *ptr) +{ + struct getaddrinfo_arg *arg = (struct getaddrinfo_arg *)ptr; + rb_nativethread_lock_lock(&arg->lock); + { + arg->cancelled = 1; + rb_native_cond_signal(&arg->cond); + } + rb_nativethread_lock_unlock(&arg->lock); +} + +int +raddrinfo_pthread_create(pthread_t *th, void *(*start_routine) (void *), void *arg) +{ + int limit = 3, ret; + int saved_errno; +#ifdef HAVE_PTHREAD_ATTR_SETDETACHSTATE + pthread_attr_t attr; + pthread_attr_t *attr_p = &attr; + int err; + int init_retries = 0; + int init_retries_max = 3; +retry_attr_init: + if ((err = pthread_attr_init(attr_p)) != 0) { + if (err == ENOMEM && init_retries < init_retries_max) { + init_retries++; + rb_gc(); + goto retry_attr_init; + } + return err; + } + if ((err = pthread_attr_setdetachstate(attr_p, PTHREAD_CREATE_DETACHED)) != 0) { + saved_errno = errno; + pthread_attr_destroy(attr_p); + errno = saved_errno; + return err; // EINVAL - shouldn't happen + } +#else + pthread_attr_t *attr_p = NULL; +#endif + do { + // It is said that pthread_create may fail spuriously, so we follow the JDK and retry several times. + // + // https://bugs.openjdk.org/browse/JDK-8268605 + // https://github.com/openjdk/jdk/commit/e35005d5ce383ddd108096a3079b17cb0bcf76f1 + ret = pthread_create(th, attr_p, start_routine, arg); + } while (ret == EAGAIN && limit-- > 0); +#ifdef HAVE_PTHREAD_ATTR_SETDETACHSTATE + saved_errno = errno; + pthread_attr_destroy(attr_p); + if (ret != 0) { + errno = saved_errno; + } +#else + if (ret == 0) { + pthread_detach(th); // this can race with shutdown routine of thread in some glibc versions + } +#endif + return ret; +} + +static void * +fork_safe_do_getaddrinfo(void *ptr) +{ + return rb_thread_prevent_fork(do_getaddrinfo, ptr); +} + +static int +rb_getaddrinfo(const char *hostp, const char *portp, const struct addrinfo *hints, struct addrinfo **ai, int timeout) +{ + int retry; + struct getaddrinfo_arg *arg; + int err = 0, gai_errno = 0, timedout = 0; + +start: + retry = 0; + + arg = allocate_getaddrinfo_arg(hostp, portp, hints, timeout); + if (!arg) { + return EAI_MEMORY; + } + + pthread_t th; + if (raddrinfo_pthread_create(&th, fork_safe_do_getaddrinfo, arg) != 0) { + int err = errno; + free_getaddrinfo_arg(arg); + errno = err; + return EAI_SYSTEM; + } + + rb_thread_call_without_gvl2(wait_getaddrinfo, arg, cancel_getaddrinfo, arg); + + int need_free = 0; + rb_nativethread_lock_lock(&arg->lock); + { + if (arg->done) { + err = arg->err; + gai_errno = arg->gai_errno; + if (err == 0) *ai = arg->ai; + } + else if (arg->cancelled) { + retry = 1; + timedout = arg->timedout; + } + else { + // If already interrupted, rb_thread_call_without_gvl2 may return without calling wait_getaddrinfo. + // In this case, it could be !arg->done && !arg->cancelled. + arg->cancelled = 1; // to make do_getaddrinfo call freeaddrinfo + retry = 1; + } + if (--arg->refcount == 0) need_free = 1; + } + rb_nativethread_lock_unlock(&arg->lock); + + if (need_free) free_getaddrinfo_arg(arg); + + if (timedout) { + if (arg->ai) { + rsock_raise_user_specified_timeout(arg->ai, Qnil, Qnil); + } else { + VALUE host = rb_str_new_cstr(hostp); + VALUE port = rb_str_new_cstr(portp); + rsock_raise_user_specified_timeout(NULL, host, port); + } + } + + // If the current thread is interrupted by asynchronous exception, the following raises the exception. + // But if the current thread is interrupted by timer thread, the following returns; we need to manually retry. + rb_thread_check_ints(); + if (retry) goto start; + + /* Because errno is threadlocal, the errno value we got from the call to getaddrinfo() in the thread + * (in case of EAI_SYSTEM return value) is not propagated to the caller of _this_ function. Set errno + * explicitly, as round-tripped through struct getaddrinfo_arg, to deal with that */ + if (gai_errno) errno = gai_errno; + return err; +} + +#endif + +#define GETNAMEINFO_WONT_BLOCK(host, serv, flags) \ + ((!(host) || ((flags) & NI_NUMERICHOST)) && \ + (!(serv) || ((flags) & NI_NUMERICSERV))) + +#if GETADDRINFO_IMPL == 0 + +int +rb_getnameinfo(const struct sockaddr *sa, socklen_t salen, + char *host, size_t hostlen, + char *serv, size_t servlen, int flags) +{ + return getnameinfo(sa, salen, host, (socklen_t)hostlen, serv, (socklen_t)servlen, flags); +} + +#elif GETADDRINFO_IMPL == 1 + +struct getnameinfo_arg +{ + const struct sockaddr *sa; + socklen_t salen; + int flags; + char *host; + size_t hostlen; + char *serv; + size_t servlen; +}; + +static void * +nogvl_getnameinfo(void *arg) +{ + struct getnameinfo_arg *ptr = arg; + return (void *)(VALUE)getnameinfo(ptr->sa, ptr->salen, + ptr->host, (socklen_t)ptr->hostlen, + ptr->serv, (socklen_t)ptr->servlen, + ptr->flags); +} +int +rb_getnameinfo(const struct sockaddr *sa, socklen_t salen, + char *host, size_t hostlen, + char *serv, size_t servlen, int flags) +{ + if (GETNAMEINFO_WONT_BLOCK(host, serv, flags)) { + return getnameinfo(sa, salen, host, hostlen, serv, servlen, flags); + } + + struct getnameinfo_arg arg; + int ret; + arg.sa = sa; + arg.salen = salen; + arg.host = host; + arg.hostlen = hostlen; + arg.serv = serv; + arg.servlen = servlen; + arg.flags = flags; + ret = (int)(VALUE)rb_thread_call_without_gvl(nogvl_getnameinfo, &arg, RUBY_UBF_IO, 0); + return ret; +} + +#elif GETADDRINFO_IMPL == 2 + +struct getnameinfo_arg +{ + struct sockaddr *sa; + socklen_t salen; + int flags; + char *host; + size_t hostlen; + char *serv; + size_t servlen; + int err, gni_errno, refcount, done, cancelled; + rb_nativethread_lock_t lock; + rb_nativethread_cond_t cond; +}; + +static struct getnameinfo_arg * +allocate_getnameinfo_arg(const struct sockaddr *sa, socklen_t salen, size_t hostlen, size_t servlen, int flags) +{ + size_t sa_offset = sizeof(struct getnameinfo_arg); + size_t host_offset = sa_offset + salen; + size_t serv_offset = host_offset + hostlen; + size_t bufsize = serv_offset + servlen; + + char *buf = malloc(bufsize); + if (!buf) { + rb_gc(); + buf = malloc(bufsize); + if (!buf) return NULL; + } + struct getnameinfo_arg *arg = (struct getnameinfo_arg *)buf; + + arg->sa = (struct sockaddr *)(buf + sa_offset); + memcpy(arg->sa, sa, salen); + arg->salen = salen; + arg->host = buf + host_offset; + arg->hostlen = hostlen; + arg->serv = buf + serv_offset; + arg->servlen = servlen; + arg->flags = flags; + + arg->refcount = 2; + arg->done = arg->cancelled = 0; + + rb_nativethread_lock_initialize(&arg->lock); + rb_native_cond_initialize(&arg->cond); + + return arg; +} + +static void +free_getnameinfo_arg(struct getnameinfo_arg *arg) +{ + rb_native_cond_destroy(&arg->cond); + rb_nativethread_lock_destroy(&arg->lock); + + free(arg); +} + +static void * +do_getnameinfo(void *ptr) +{ + struct getnameinfo_arg *arg = (struct getnameinfo_arg *)ptr; + + int err, gni_errno; + err = getnameinfo(arg->sa, arg->salen, arg->host, (socklen_t)arg->hostlen, arg->serv, (socklen_t)arg->servlen, arg->flags); + gni_errno = errno; + + int need_free = 0; + rb_nativethread_lock_lock(&arg->lock); + arg->err = err; + arg->gni_errno = gni_errno; + if (!arg->cancelled) { + arg->done = 1; + rb_native_cond_signal(&arg->cond); + } + if (--arg->refcount == 0) need_free = 1; + rb_nativethread_lock_unlock(&arg->lock); + + if (need_free) free_getnameinfo_arg(arg); + + return 0; +} + +static void * +wait_getnameinfo(void *ptr) +{ + struct getnameinfo_arg *arg = (struct getnameinfo_arg *)ptr; + rb_nativethread_lock_lock(&arg->lock); + while (!arg->done && !arg->cancelled) { + rb_native_cond_wait(&arg->cond, &arg->lock); + } + rb_nativethread_lock_unlock(&arg->lock); + return 0; +} + +static void +cancel_getnameinfo(void *ptr) +{ + struct getnameinfo_arg *arg = (struct getnameinfo_arg *)ptr; + rb_nativethread_lock_lock(&arg->lock); + arg->cancelled = 1; + rb_native_cond_signal(&arg->cond); + rb_nativethread_lock_unlock(&arg->lock); +} + +int +rb_getnameinfo(const struct sockaddr *sa, socklen_t salen, + char *host, size_t hostlen, + char *serv, size_t servlen, int flags) +{ + int retry; + struct getnameinfo_arg *arg; + int err = 0, gni_errno = 0; + + if (GETNAMEINFO_WONT_BLOCK(host, serv, flags)) { + return getnameinfo(sa, salen, host, (socklen_t)hostlen, serv, (socklen_t)servlen, flags); + } + +start: + retry = 0; + + arg = allocate_getnameinfo_arg(sa, salen, hostlen, servlen, flags); + if (!arg) { + return EAI_MEMORY; + } + + pthread_t th; + if (raddrinfo_pthread_create(&th, do_getnameinfo, arg) != 0) { + int err = errno; + free_getnameinfo_arg(arg); + errno = err; + return EAI_SYSTEM; + } + + rb_thread_call_without_gvl2(wait_getnameinfo, arg, cancel_getnameinfo, arg); + + int need_free = 0; + rb_nativethread_lock_lock(&arg->lock); + if (arg->done) { + err = arg->err; + gni_errno = arg->gni_errno; + if (err == 0) { + if (host) memcpy(host, arg->host, hostlen); + if (serv) memcpy(serv, arg->serv, servlen); + } + } + else if (arg->cancelled) { + retry = 1; + } + else { + // If already interrupted, rb_thread_call_without_gvl2 may return without calling wait_getnameinfo. + // In this case, it could be !arg->done && !arg->cancelled. + arg->cancelled = 1; + retry = 1; + } + if (--arg->refcount == 0) need_free = 1; + rb_nativethread_lock_unlock(&arg->lock); + + if (need_free) free_getnameinfo_arg(arg); + + // If the current thread is interrupted by asynchronous exception, the following raises the exception. + // But if the current thread is interrupted by timer thread, the following returns; we need to manually retry. + rb_thread_check_ints(); + if (retry) goto start; + + /* Make sure we copy the thread-local errno value from the getnameinfo thread back to this thread, so + * calling code sees the correct errno */ + if (gni_errno) errno = gni_errno; + return err; +} + +#endif + +static void +make_ipaddr0(struct sockaddr *addr, socklen_t addrlen, char *buf, size_t buflen) +{ + int error; + + error = rb_getnameinfo(addr, addrlen, buf, buflen, NULL, 0, NI_NUMERICHOST); + if (error) { + rsock_raise_resolution_error("getnameinfo", error); + } +} + +VALUE +rsock_make_ipaddr(struct sockaddr *addr, socklen_t addrlen) +{ + char hbuf[1024]; + + make_ipaddr0(addr, addrlen, hbuf, sizeof(hbuf)); + return rb_str_new2(hbuf); +} + +static void +make_inetaddr(unsigned int host, char *buf, size_t buflen) +{ + struct sockaddr_in sin; + + INIT_SOCKADDR_IN(&sin, sizeof(sin)); + sin.sin_addr.s_addr = host; + make_ipaddr0((struct sockaddr*)&sin, sizeof(sin), buf, buflen); +} + +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; +} + +#define str_equal(ptr, len, name) \ + ((ptr)[0] == name[0] && \ + rb_strlen_lit(name) == (len) && memcmp(ptr, name, len) == 0) + +char* +raddrinfo_host_str(VALUE host, char *hbuf, size_t hbuflen, 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, hbuflen); + if (flags_ptr) *flags_ptr |= AI_NUMERICHOST; + return hbuf; + } + else { + const char *name; + size_t len; + + StringValueCStr(host); + RSTRING_GETMEM(host, name, len); + if (!len || str_equal(name, len, "<any>")) { + make_inetaddr(INADDR_ANY, hbuf, hbuflen); + if (flags_ptr) *flags_ptr |= AI_NUMERICHOST; + } + else if (str_equal(name, len, "<broadcast>")) { + make_inetaddr(INADDR_BROADCAST, hbuf, hbuflen); + if (flags_ptr) *flags_ptr |= AI_NUMERICHOST; + } + else if (len >= hbuflen) { + rb_raise(rb_eArgError, "hostname too long (%"PRIuSIZE")", + len); + } + else { + memcpy(hbuf, name, len); + hbuf[len] = '\0'; + } + return hbuf; + } +} + +char* +raddrinfo_port_str(VALUE port, char *pbuf, size_t pbuflen, int *flags_ptr) +{ + if (NIL_P(port)) { + return 0; + } + else if (FIXNUM_P(port)) { + snprintf(pbuf, pbuflen, "%ld", FIX2LONG(port)); +#ifdef AI_NUMERICSERV + if (flags_ptr) *flags_ptr |= AI_NUMERICSERV; +#endif + return pbuf; + } + else { + const char *serv; + size_t len; + + StringValueCStr(port); + RSTRING_GETMEM(port, serv, len); + if (len >= pbuflen) { + rb_raise(rb_eArgError, "service name too long (%"PRIuSIZE")", + len); + } + memcpy(pbuf, serv, len); + pbuf[len] = '\0'; + return pbuf; + } +} + +static int +rb_scheduler_getaddrinfo(VALUE scheduler, VALUE host, const char *service, + const struct addrinfo *hints, struct rb_addrinfo **res) +{ + int error, res_allocated = 0, _additional_flags = 0; + long i, len; + struct addrinfo *ai, *ai_tail = NULL; + char *hostp; + char _hbuf[NI_MAXHOST]; + VALUE ip_addresses_array, ip_address; + + ip_addresses_array = rb_fiber_scheduler_address_resolve(scheduler, host); + + if (ip_addresses_array == Qundef) { + // Returns EAI_FAIL if the scheduler hook is not implemented: + return EAI_FAIL; + } else if (ip_addresses_array == Qnil) { + len = 0; + } else { + len = RARRAY_LEN(ip_addresses_array); + } + + for(i=0; i<len; i++) { + ip_address = rb_ary_entry(ip_addresses_array, i); + hostp = raddrinfo_host_str(ip_address, _hbuf, sizeof(_hbuf), &_additional_flags); + error = numeric_getaddrinfo(hostp, service, hints, &ai); + if (error == 0) { + if (!res_allocated) { + res_allocated = 1; + *res = (struct rb_addrinfo *)xmalloc(sizeof(struct rb_addrinfo)); + (*res)->allocated_by_malloc = 1; + (*res)->ai = ai; + ai_tail = ai; + } else { + while (ai_tail->ai_next) { + ai_tail = ai_tail->ai_next; + } + ai_tail->ai_next = ai; + ai_tail = ai; + } + } + } + + if (res_allocated) { // At least one valid result. + return 0; + } else { + return EAI_NONAME; + } +} + +struct rb_addrinfo* +rsock_getaddrinfo(VALUE host, VALUE port, struct addrinfo *hints, int socktype_hack, VALUE timeout) +{ + struct rb_addrinfo* res = NULL; + struct addrinfo *ai; + char *hostp, *portp; + int error = 0; + char hbuf[NI_MAXHOST], pbuf[NI_MAXSERV]; + int additional_flags = 0; + + hostp = raddrinfo_host_str(host, hbuf, sizeof(hbuf), &additional_flags); + portp = raddrinfo_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 = numeric_getaddrinfo(hostp, portp, hints, &ai); + if (error == 0) { + res = (struct rb_addrinfo *)xmalloc(sizeof(struct rb_addrinfo)); + res->allocated_by_malloc = 1; + res->ai = ai; + } else { + VALUE scheduler = rb_fiber_scheduler_current(); + int resolved = 0; + + if (scheduler != Qnil && hostp && !(hints->ai_flags & AI_NUMERICHOST)) { + error = rb_scheduler_getaddrinfo(scheduler, host, portp, hints, &res); + + if (error != EAI_FAIL) { + resolved = 1; + } + } + + if (!resolved) { + int t = NIL_P(timeout) ? -1 : rsock_value_timeout_to_msec(timeout); + error = rb_getaddrinfo(hostp, portp, hints, &ai, t); + if (error == 0) { + res = (struct rb_addrinfo *)xmalloc(sizeof(struct rb_addrinfo)); + res->allocated_by_malloc = 0; + res->ai = ai; + } + } + } + + if (error) { + if (hostp && hostp[strlen(hostp)-1] == '\n') { + rb_raise(rb_eSocket, "newline at the end of hostname"); + } + rsock_raise_resolution_error("getaddrinfo", error); + } + + return res; +} + +int +rsock_fd_family(int fd) +{ + struct sockaddr sa = { 0 }; + socklen_t sa_len = sizeof(sa); + + if (fd < 0 || getsockname(fd, &sa, &sa_len) != 0 || + (size_t)sa_len < offsetof(struct sockaddr, sa_family) + sizeof(sa.sa_family)) { + return AF_UNSPEC; + } + return sa.sa_family; +} + +struct rb_addrinfo* +rsock_addrinfo(VALUE host, VALUE port, int family, int socktype, int flags, VALUE timeout) +{ + struct addrinfo hints; + + MEMZERO(&hints, struct addrinfo, 1); + hints.ai_family = family; + hints.ai_socktype = socktype; + hints.ai_flags = flags; + return rsock_getaddrinfo(host, port, &hints, 1, timeout); +} + +VALUE +rsock_ipaddr(struct sockaddr *sockaddr, socklen_t sockaddrlen, int norevlookup) +{ + VALUE family, port, addr1, addr2; + VALUE ary; + int error; + char hbuf[1024], pbuf[1024]; + ID id; + + id = rsock_intern_family(sockaddr->sa_family); + if (id) { + family = rb_str_dup(rb_id2str(id)); + } + else { + family = rb_sprintf("unknown:%d", sockaddr->sa_family); + } + + addr1 = Qnil; + if (!norevlookup) { + error = rb_getnameinfo(sockaddr, sockaddrlen, hbuf, sizeof(hbuf), + NULL, 0, 0); + if (! error) { + addr1 = rb_str_new2(hbuf); + } + } + error = rb_getnameinfo(sockaddr, sockaddrlen, hbuf, sizeof(hbuf), + pbuf, sizeof(pbuf), NI_NUMERICHOST | NI_NUMERICSERV); + if (error) { + rsock_raise_resolution_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; +} + +#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN +static long +unixsocket_len(const struct sockaddr_un *su, socklen_t socklen) +{ + const char *s = su->sun_path, *e = (const char*)su + socklen; + while (s < e && *(e-1) == '\0') + e--; + return e - s; +} + +VALUE +rsock_unixpath_str(struct sockaddr_un *sockaddr, socklen_t len) +{ + long n = unixsocket_len(sockaddr, len); + if (n >= 0) + return rb_str_new(sockaddr->sun_path, n); + else + return rb_str_new2(""); +} + +VALUE +rsock_unixaddr(struct sockaddr_un *sockaddr, socklen_t len) +{ + return rb_assoc_new(rb_str_new2("AF_UNIX"), + rsock_unixpath_str(sockaddr, len)); +} + +socklen_t +rsock_unix_sockaddr_len(VALUE path) +{ +#ifdef __linux__ + if (RSTRING_LEN(path) == 0) { + /* autobind; see unix(7) for details. */ + return (socklen_t) sizeof(sa_family_t); + } + else if (RSTRING_PTR(path)[0] == '\0') { + /* abstract namespace; see unix(7) for details. */ + if (SOCKLEN_MAX - offsetof(struct sockaddr_un, sun_path) < (size_t)RSTRING_LEN(path)) + rb_raise(rb_eArgError, "Linux abstract socket too long"); + return (socklen_t) offsetof(struct sockaddr_un, sun_path) + + RSTRING_SOCKLEN(path); + } + else { +#endif + return (socklen_t) sizeof(struct sockaddr_un); +#ifdef __linux__ + } +#endif +} +#endif + +struct hostent_arg { + VALUE host; + struct rb_addrinfo* addr; + VALUE (*ipaddr)(struct sockaddr*, socklen_t); +}; + +static VALUE +make_hostent_internal(VALUE v) +{ + struct hostent_arg *arg = (void *)v; + VALUE host = arg->host; + struct addrinfo* addr = arg->addr->ai; + VALUE (*ipaddr)(struct sockaddr*, socklen_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 = raddrinfo_host_str(host, hbuf, sizeof(hbuf), NULL); + } + rb_ary_push(ary, rb_str_new2(hostp)); + + if (addr->ai_canonname && strlen(addr->ai_canonname) < NI_MAXHOST && + (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; +} + +VALUE +rsock_freeaddrinfo(VALUE arg) +{ + struct rb_addrinfo *addr = (struct rb_addrinfo *)arg; + rb_freeaddrinfo(addr); + return Qnil; +} + +VALUE +rsock_make_hostent(VALUE host, struct rb_addrinfo *addr, VALUE (*ipaddr)(struct sockaddr *, socklen_t)) +{ + struct hostent_arg arg; + + arg.host = host; + arg.addr = addr; + arg.ipaddr = ipaddr; + return rb_ensure(make_hostent_internal, (VALUE)&arg, + rsock_freeaddrinfo, (VALUE)addr); +} + +typedef struct { + VALUE inspectname; + VALUE canonname; + int pfamily; + int socktype; + int protocol; + socklen_t sockaddr_len; + union_sockaddr addr; +} rb_addrinfo_t; + +static void +addrinfo_mark(void *ptr) +{ + rb_addrinfo_t *rai = ptr; + rb_gc_mark(rai->inspectname); + rb_gc_mark(rai->canonname); +} + +#define addrinfo_free RUBY_TYPED_DEFAULT_FREE + +static size_t +addrinfo_memsize(const void *ptr) +{ + return sizeof(rb_addrinfo_t); +} + +static const rb_data_type_t addrinfo_type = { + "socket/addrinfo", + {addrinfo_mark, addrinfo_free, addrinfo_memsize,}, + 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_FROZEN_SHAREABLE | RUBY_TYPED_WB_PROTECTED, +}; + +static VALUE +addrinfo_s_allocate(VALUE klass) +{ + return TypedData_Wrap_Struct(klass, &addrinfo_type, 0); +} + +#define IS_ADDRINFO(obj) rb_typeddata_is_kind_of((obj), &addrinfo_type) +static inline rb_addrinfo_t * +check_addrinfo(VALUE self) +{ + return rb_check_typeddata(self, &addrinfo_type); +} + +static rb_addrinfo_t * +get_addrinfo(VALUE self) +{ + rb_addrinfo_t *rai = check_addrinfo(self); + + if (!rai) { + rb_raise(rb_eTypeError, "uninitialized socket address"); + } + return rai; +} + + +static rb_addrinfo_t * +alloc_addrinfo(void) +{ + rb_addrinfo_t *rai = ZALLOC(rb_addrinfo_t); + rai->inspectname = Qnil; + rai->canonname = Qnil; + return rai; +} + +static void +init_addrinfo(VALUE self, rb_addrinfo_t *rai, struct sockaddr *sa, socklen_t len, + int pfamily, int socktype, int protocol, + VALUE canonname, VALUE inspectname) +{ + if ((socklen_t)sizeof(rai->addr) < len) + rb_raise(rb_eArgError, "sockaddr string too big"); + memcpy((void *)&rai->addr, (void *)sa, len); + rai->sockaddr_len = len; + + rai->pfamily = pfamily; + rai->socktype = socktype; + rai->protocol = protocol; + RB_OBJ_WRITE(self, &rai->canonname, canonname); + RB_OBJ_WRITE(self, &rai->inspectname, inspectname); +} + +VALUE +rsock_addrinfo_new(struct sockaddr *addr, socklen_t len, + int family, int socktype, int protocol, + VALUE canonname, VALUE inspectname) +{ + VALUE a; + rb_addrinfo_t *rai; + + a = addrinfo_s_allocate(rb_cAddrinfo); + DATA_PTR(a) = rai = alloc_addrinfo(); + init_addrinfo(a, rai, addr, len, family, socktype, protocol, canonname, inspectname); + return a; +} + +static struct rb_addrinfo * +call_getaddrinfo(VALUE node, VALUE service, + VALUE family, VALUE socktype, VALUE protocol, VALUE flags, + int socktype_hack, VALUE timeout) +{ + struct addrinfo hints; + struct rb_addrinfo *res; + + MEMZERO(&hints, struct addrinfo, 1); + hints.ai_family = NIL_P(family) ? PF_UNSPEC : rsock_family_arg(family); + + if (!NIL_P(socktype)) { + hints.ai_socktype = rsock_socktype_arg(socktype); + } + if (!NIL_P(protocol)) { + hints.ai_protocol = NUM2INT(protocol); + } + if (!NIL_P(flags)) { + hints.ai_flags = NUM2INT(flags); + } + + res = rsock_getaddrinfo(node, service, &hints, socktype_hack, timeout); + + if (res == NULL) + rb_raise(rb_eSocket, "host not found"); + return res; +} + +static VALUE make_inspectname(VALUE node, VALUE service, struct addrinfo *res); + +static void +init_addrinfo_getaddrinfo(VALUE self, rb_addrinfo_t *rai, VALUE node, VALUE service, + VALUE family, VALUE socktype, VALUE protocol, VALUE flags, + VALUE inspectnode, VALUE inspectservice) +{ + struct rb_addrinfo *res = call_getaddrinfo(node, service, family, socktype, protocol, flags, 1, Qnil); + VALUE canonname; + VALUE inspectname = rb_str_equal(node, inspectnode) ? Qnil : make_inspectname(inspectnode, inspectservice, res->ai); + + canonname = Qnil; + if (res->ai->ai_canonname) { + canonname = rb_str_new_cstr(res->ai->ai_canonname); + OBJ_FREEZE(canonname); + } + + init_addrinfo(self, rai, res->ai->ai_addr, res->ai->ai_addrlen, + NUM2INT(family), NUM2INT(socktype), NUM2INT(protocol), + canonname, inspectname); + + rb_freeaddrinfo(res); +} + +static VALUE +make_inspectname(VALUE node, VALUE service, struct addrinfo *res) +{ + VALUE inspectname = Qnil; + + if (res) { + /* drop redundant information which also shown in address:port part. */ + char hbuf[NI_MAXHOST], pbuf[NI_MAXSERV]; + int ret; + ret = rb_getnameinfo(res->ai_addr, res->ai_addrlen, hbuf, + sizeof(hbuf), pbuf, sizeof(pbuf), + NI_NUMERICHOST|NI_NUMERICSERV); + if (ret == 0) { + if (RB_TYPE_P(node, T_STRING) && strcmp(hbuf, RSTRING_PTR(node)) == 0) + node = Qnil; + if (RB_TYPE_P(service, T_STRING) && strcmp(pbuf, RSTRING_PTR(service)) == 0) + service = Qnil; + else if (RB_TYPE_P(service, T_FIXNUM) && atoi(pbuf) == FIX2INT(service)) + service = Qnil; + } + } + + if (RB_TYPE_P(node, T_STRING)) { + inspectname = rb_str_dup(node); + } + if (RB_TYPE_P(service, T_STRING)) { + if (NIL_P(inspectname)) + inspectname = rb_sprintf(":%s", StringValueCStr(service)); + else + rb_str_catf(inspectname, ":%s", StringValueCStr(service)); + } + else if (RB_TYPE_P(service, T_FIXNUM) && FIX2INT(service) != 0) + { + if (NIL_P(inspectname)) + inspectname = rb_sprintf(":%d", FIX2INT(service)); + else + rb_str_catf(inspectname, ":%d", FIX2INT(service)); + } + if (!NIL_P(inspectname)) { + OBJ_FREEZE(inspectname); + } + return inspectname; +} + +static VALUE +addrinfo_firstonly_new(VALUE node, VALUE service, VALUE family, VALUE socktype, VALUE protocol, VALUE flags) +{ + VALUE ret; + VALUE canonname; + VALUE inspectname; + + struct rb_addrinfo *res = call_getaddrinfo(node, service, family, socktype, protocol, flags, 0, Qnil); + + inspectname = make_inspectname(node, service, res->ai); + + canonname = Qnil; + if (res->ai->ai_canonname) { + canonname = rb_str_new_cstr(res->ai->ai_canonname); + OBJ_FREEZE(canonname); + } + + ret = rsock_addrinfo_new(res->ai->ai_addr, res->ai->ai_addrlen, + res->ai->ai_family, res->ai->ai_socktype, + res->ai->ai_protocol, + canonname, inspectname); + + rb_freeaddrinfo(res); + return ret; +} + +static VALUE +addrinfo_list_new(VALUE node, VALUE service, VALUE family, VALUE socktype, VALUE protocol, VALUE flags, VALUE timeout) +{ + VALUE ret; + struct addrinfo *r; + VALUE inspectname; + + struct rb_addrinfo *res = call_getaddrinfo(node, service, family, socktype, protocol, flags, 0, timeout); + + inspectname = make_inspectname(node, service, res->ai); + + ret = rb_ary_new(); + for (r = res->ai; r; r = r->ai_next) { + VALUE addr; + VALUE canonname = Qnil; + + if (r->ai_canonname) { + canonname = rb_str_new_cstr(r->ai_canonname); + OBJ_FREEZE(canonname); + } + + addr = rsock_addrinfo_new(r->ai_addr, r->ai_addrlen, + r->ai_family, r->ai_socktype, r->ai_protocol, + canonname, inspectname); + + rb_ary_push(ret, addr); + } + + rb_freeaddrinfo(res); + return ret; +} + + +#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN +static void +init_unix_addrinfo(VALUE self, rb_addrinfo_t *rai, VALUE path, int socktype) +{ + struct sockaddr_un un; + socklen_t len; + + StringValue(path); + + if (sizeof(un.sun_path) < (size_t)RSTRING_LEN(path)) + rb_raise(rb_eArgError, + "too long unix socket path (%"PRIuSIZE" bytes given but %"PRIuSIZE" bytes max)", + (size_t)RSTRING_LEN(path), sizeof(un.sun_path)); + + INIT_SOCKADDR_UN(&un, sizeof(struct sockaddr_un)); + memcpy((void*)&un.sun_path, RSTRING_PTR(path), RSTRING_LEN(path)); + + len = rsock_unix_sockaddr_len(path); + init_addrinfo(self, rai, (struct sockaddr *)&un, len, + PF_UNIX, socktype, 0, Qnil, Qnil); +} + +static long +rai_unixsocket_len(const rb_addrinfo_t *rai) +{ + return unixsocket_len(&rai->addr.un, rai->sockaddr_len); +} +#endif + +/* + * call-seq: + * Addrinfo.new(sockaddr) => addrinfo + * Addrinfo.new(sockaddr, family) => addrinfo + * Addrinfo.new(sockaddr, family, socktype) => addrinfo + * Addrinfo.new(sockaddr, family, socktype, protocol) => addrinfo + * + * returns a new instance of Addrinfo. + * The instance contains sockaddr, family, socktype, protocol. + * sockaddr means struct sockaddr which can be used for connect(2), etc. + * family, socktype and protocol are integers which is used for arguments of socket(2). + * + * sockaddr is specified as an array or a string. + * The array should be compatible to the value of IPSocket#addr or UNIXSocket#addr. + * The string should be struct sockaddr as generated by + * Socket.sockaddr_in or Socket.unpack_sockaddr_un. + * + * sockaddr examples: + * - <code>["AF_INET", 46102, "localhost.localdomain", "127.0.0.1"]</code> + * - <code>["AF_INET6", 42304, "ip6-localhost", "::1"]</code> + * - <code>["AF_UNIX", "/tmp/sock"]</code> + * - <code>Socket.sockaddr_in("smtp", "2001:DB8::1")</code> + * - <code>Socket.sockaddr_in(80, "172.18.22.42")</code> + * - <code>Socket.sockaddr_in(80, "www.ruby-lang.org")</code> + * - <code>Socket.sockaddr_un("/tmp/sock")</code> + * + * In an AF_INET/AF_INET6 sockaddr array, the 4th element, + * numeric IP address, is used to construct socket address in the Addrinfo instance. + * If the 3rd element, textual host name, is non-nil, it is also recorded but used only for Addrinfo#inspect. + * + * family is specified as an integer to specify the protocol family such as Socket::PF_INET. + * It can be a symbol or a string which is the constant name + * with or without PF_ prefix such as :INET, :INET6, :UNIX, "PF_INET", etc. + * If omitted, PF_UNSPEC is assumed. + * + * socktype is specified as an integer to specify the socket type such as Socket::SOCK_STREAM. + * It can be a symbol or a string which is the constant name + * with or without SOCK_ prefix such as :STREAM, :DGRAM, :RAW, "SOCK_STREAM", etc. + * If omitted, 0 is assumed. + * + * protocol is specified as an integer to specify the protocol such as Socket::IPPROTO_TCP. + * It must be an integer, unlike family and socktype. + * If omitted, 0 is assumed. + * Note that 0 is reasonable value for most protocols, except raw socket. + * + */ +static VALUE +addrinfo_initialize(int argc, VALUE *argv, VALUE self) +{ + rb_addrinfo_t *rai; + VALUE sockaddr_arg, sockaddr_ary, pfamily, socktype, protocol; + int i_pfamily, i_socktype, i_protocol; + struct sockaddr *sockaddr_ptr; + socklen_t sockaddr_len; + VALUE canonname = Qnil, inspectname = Qnil; + + if (check_addrinfo(self)) + rb_raise(rb_eTypeError, "already initialized socket address"); + DATA_PTR(self) = rai = alloc_addrinfo(); + + rb_scan_args(argc, argv, "13", &sockaddr_arg, &pfamily, &socktype, &protocol); + + i_pfamily = NIL_P(pfamily) ? PF_UNSPEC : rsock_family_arg(pfamily); + i_socktype = NIL_P(socktype) ? 0 : rsock_socktype_arg(socktype); + i_protocol = NIL_P(protocol) ? 0 : NUM2INT(protocol); + + sockaddr_ary = rb_check_array_type(sockaddr_arg); + if (!NIL_P(sockaddr_ary)) { + VALUE afamily = rb_ary_entry(sockaddr_ary, 0); + int af; + StringValue(afamily); + if (rsock_family_to_int(RSTRING_PTR(afamily), RSTRING_LEN(afamily), &af) == -1) + rb_raise(rb_eSocket, "unknown address family: %s", StringValueCStr(afamily)); + switch (af) { + case AF_INET: /* ["AF_INET", 46102, "localhost.localdomain", "127.0.0.1"] */ +#ifdef INET6 + case AF_INET6: /* ["AF_INET6", 42304, "ip6-localhost", "::1"] */ +#endif + { + VALUE service = rb_ary_entry(sockaddr_ary, 1); + VALUE nodename = rb_ary_entry(sockaddr_ary, 2); + VALUE numericnode = rb_ary_entry(sockaddr_ary, 3); + int flags; + + service = INT2NUM(NUM2INT(service)); + if (!NIL_P(nodename)) + StringValue(nodename); + StringValue(numericnode); + flags = AI_NUMERICHOST; +#ifdef AI_NUMERICSERV + flags |= AI_NUMERICSERV; +#endif + + init_addrinfo_getaddrinfo(self, rai, numericnode, service, + INT2NUM(i_pfamily ? i_pfamily : af), INT2NUM(i_socktype), INT2NUM(i_protocol), + INT2NUM(flags), + nodename, service); + break; + } + +#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN + case AF_UNIX: /* ["AF_UNIX", "/tmp/sock"] */ + { + VALUE path = rb_ary_entry(sockaddr_ary, 1); + StringValue(path); + init_unix_addrinfo(self, rai, path, SOCK_STREAM); + break; + } +#endif + + default: + rb_raise(rb_eSocket, "unexpected address family"); + } + } + else { + StringValue(sockaddr_arg); + sockaddr_ptr = (struct sockaddr *)RSTRING_PTR(sockaddr_arg); + sockaddr_len = RSTRING_SOCKLEN(sockaddr_arg); + init_addrinfo(self, rai, sockaddr_ptr, sockaddr_len, + i_pfamily, i_socktype, i_protocol, + canonname, inspectname); + } + + return self; +} + +static int +get_afamily(const struct sockaddr *addr, socklen_t len) +{ + if ((socklen_t)((const char*)&addr->sa_family + sizeof(addr->sa_family) - (char*)addr) <= len) + return addr->sa_family; + else + return AF_UNSPEC; +} + +static int +ai_get_afamily(const rb_addrinfo_t *rai) +{ + return get_afamily(&rai->addr.addr, rai->sockaddr_len); +} + +static VALUE +inspect_sockaddr(VALUE addrinfo, VALUE ret) +{ + rb_addrinfo_t *rai = get_addrinfo(addrinfo); + union_sockaddr *sockaddr = &rai->addr; + socklen_t socklen = rai->sockaddr_len; + return rsock_inspect_sockaddr((struct sockaddr *)sockaddr, socklen, ret); +} + +VALUE +rsock_inspect_sockaddr(struct sockaddr *sockaddr_arg, socklen_t socklen, VALUE ret) +{ + union_sockaddr *sockaddr = (union_sockaddr *)sockaddr_arg; + if (socklen == 0) { + rb_str_cat2(ret, "empty-sockaddr"); + } + else if ((long)socklen < ((char*)&sockaddr->addr.sa_family + sizeof(sockaddr->addr.sa_family)) - (char*)sockaddr) + rb_str_cat2(ret, "too-short-sockaddr"); + else { + switch (sockaddr->addr.sa_family) { + case AF_UNSPEC: + { + rb_str_cat2(ret, "UNSPEC"); + break; + } + + case AF_INET: + { + struct sockaddr_in *addr; + int port; + addr = &sockaddr->in; + if ((socklen_t)(((char*)&addr->sin_addr)-(char*)addr+0+1) <= socklen) + rb_str_catf(ret, "%d", ((unsigned char*)&addr->sin_addr)[0]); + else + rb_str_cat2(ret, "?"); + if ((socklen_t)(((char*)&addr->sin_addr)-(char*)addr+1+1) <= socklen) + rb_str_catf(ret, ".%d", ((unsigned char*)&addr->sin_addr)[1]); + else + rb_str_cat2(ret, ".?"); + if ((socklen_t)(((char*)&addr->sin_addr)-(char*)addr+2+1) <= socklen) + rb_str_catf(ret, ".%d", ((unsigned char*)&addr->sin_addr)[2]); + else + rb_str_cat2(ret, ".?"); + if ((socklen_t)(((char*)&addr->sin_addr)-(char*)addr+3+1) <= socklen) + rb_str_catf(ret, ".%d", ((unsigned char*)&addr->sin_addr)[3]); + else + rb_str_cat2(ret, ".?"); + + if ((socklen_t)(((char*)&addr->sin_port)-(char*)addr+(int)sizeof(addr->sin_port)) < socklen) { + port = ntohs(addr->sin_port); + if (port) + rb_str_catf(ret, ":%d", port); + } + else { + rb_str_cat2(ret, ":?"); + } + if ((socklen_t)sizeof(struct sockaddr_in) != socklen) + rb_str_catf(ret, " (%d bytes for %d bytes sockaddr_in)", + (int)socklen, + (int)sizeof(struct sockaddr_in)); + break; + } + +#ifdef AF_INET6 + case AF_INET6: + { + struct sockaddr_in6 *addr; + char hbuf[1024]; + int port; + int error; + if (socklen < (socklen_t)sizeof(struct sockaddr_in6)) { + rb_str_catf(ret, "too-short-AF_INET6-sockaddr %d bytes", (int)socklen); + } + else { + addr = &sockaddr->in6; + /* use getnameinfo for scope_id. + * RFC 4007: IPv6 Scoped Address Architecture + * draft-ietf-ipv6-scope-api-00.txt: Scoped Address Extensions to the IPv6 Basic Socket API + */ + error = rb_getnameinfo(&sockaddr->addr, socklen, + hbuf, (socklen_t)sizeof(hbuf), NULL, 0, + NI_NUMERICHOST|NI_NUMERICSERV); + if (error) { + rsock_raise_resolution_error("getnameinfo", error); + } + if (addr->sin6_port == 0) { + rb_str_cat2(ret, hbuf); + } + else { + port = ntohs(addr->sin6_port); + rb_str_catf(ret, "[%s]:%d", hbuf, port); + } + if ((socklen_t)sizeof(struct sockaddr_in6) < socklen) + rb_str_catf(ret, "(sockaddr %d bytes too long)", (int)(socklen - sizeof(struct sockaddr_in6))); + } + break; + } +#endif + +#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN + case AF_UNIX: + { + struct sockaddr_un *addr = &sockaddr->un; + char *p, *s, *e; + long len = unixsocket_len(addr, socklen); + s = addr->sun_path; + if (len < 0) + rb_str_cat2(ret, "too-short-AF_UNIX-sockaddr"); + else if (len == 0) + rb_str_cat2(ret, "empty-path-AF_UNIX-sockaddr"); + else { + int printable_only = 1; + e = s + len; + p = s; + while (p < e) { + printable_only = printable_only && ISPRINT(*p) && !ISSPACE(*p); + p++; + } + if (printable_only) { /* only printable, no space */ + if (s[0] != '/') /* relative path */ + rb_str_cat2(ret, "UNIX "); + rb_str_cat(ret, s, p - s); + } + else { + rb_str_cat2(ret, "UNIX"); + while (s < e) + rb_str_catf(ret, ":%02x", (unsigned char)*s++); + } + } + break; + } +#endif + +#if defined(AF_PACKET) && defined(__linux__) + /* GNU/Linux */ + case AF_PACKET: + { + struct sockaddr_ll *addr; + const char *sep = "["; +#define CATSEP do { rb_str_cat2(ret, sep); sep = " "; } while (0); + + addr = (struct sockaddr_ll *)sockaddr; + + rb_str_cat2(ret, "PACKET"); + + if (offsetof(struct sockaddr_ll, sll_protocol) + sizeof(addr->sll_protocol) <= (size_t)socklen) { + CATSEP; + rb_str_catf(ret, "protocol=%d", ntohs(addr->sll_protocol)); + } + if (offsetof(struct sockaddr_ll, sll_ifindex) + sizeof(addr->sll_ifindex) <= (size_t)socklen) { + char buf[IFNAMSIZ]; + CATSEP; + if (if_indextoname(addr->sll_ifindex, buf) == NULL) + rb_str_catf(ret, "ifindex=%d", addr->sll_ifindex); + else + rb_str_catf(ret, "%s", buf); + } + if (offsetof(struct sockaddr_ll, sll_hatype) + sizeof(addr->sll_hatype) <= (size_t)socklen) { + CATSEP; + rb_str_catf(ret, "hatype=%d", addr->sll_hatype); + } + if (offsetof(struct sockaddr_ll, sll_pkttype) + sizeof(addr->sll_pkttype) <= (size_t)socklen) { + CATSEP; + if (addr->sll_pkttype == PACKET_HOST) + rb_str_cat2(ret, "HOST"); + else if (addr->sll_pkttype == PACKET_BROADCAST) + rb_str_cat2(ret, "BROADCAST"); + else if (addr->sll_pkttype == PACKET_MULTICAST) + rb_str_cat2(ret, "MULTICAST"); + else if (addr->sll_pkttype == PACKET_OTHERHOST) + rb_str_cat2(ret, "OTHERHOST"); + else if (addr->sll_pkttype == PACKET_OUTGOING) + rb_str_cat2(ret, "OUTGOING"); + else + rb_str_catf(ret, "pkttype=%d", addr->sll_pkttype); + } + if (socklen != (socklen_t)(offsetof(struct sockaddr_ll, sll_addr) + addr->sll_halen)) { + CATSEP; + if (offsetof(struct sockaddr_ll, sll_halen) + sizeof(addr->sll_halen) <= (size_t)socklen) { + rb_str_catf(ret, "halen=%d", addr->sll_halen); + } + } + if (offsetof(struct sockaddr_ll, sll_addr) < (size_t)socklen) { + socklen_t len, i; + CATSEP; + rb_str_cat2(ret, "hwaddr"); + len = addr->sll_halen; + if ((size_t)socklen < offsetof(struct sockaddr_ll, sll_addr) + len) + len = socklen - offsetof(struct sockaddr_ll, sll_addr); + for (i = 0; i < len; i++) { + rb_str_cat2(ret, i == 0 ? "=" : ":"); + rb_str_catf(ret, "%02x", addr->sll_addr[i]); + } + } + + if (socklen < (socklen_t)(offsetof(struct sockaddr_ll, sll_halen) + sizeof(addr->sll_halen)) || + (socklen_t)(offsetof(struct sockaddr_ll, sll_addr) + addr->sll_halen) != socklen) { + CATSEP; + rb_str_catf(ret, "(%d bytes for %d bytes sockaddr_ll)", + (int)socklen, (int)sizeof(struct sockaddr_ll)); + } + + rb_str_cat2(ret, "]"); +#undef CATSEP + + break; + } +#endif + +#if defined(AF_LINK) && defined(HAVE_TYPE_STRUCT_SOCKADDR_DL) + /* AF_LINK is defined in 4.4BSD derivations since Net2. + link_ntoa is also defined at Net2. + However Debian GNU/kFreeBSD defines AF_LINK but + don't have link_ntoa. */ + case AF_LINK: + { + /* + * Simple implementation using link_ntoa(): + * This doesn't work on Debian GNU/kFreeBSD 6.0.7 (squeeze). + * Also, the format is bit different. + * + * rb_str_catf(ret, "LINK %s", link_ntoa(&sockaddr->dl)); + * break; + */ + struct sockaddr_dl *addr = &sockaddr->dl; + char *np = NULL, *ap = NULL, *endp; + int nlen = 0, alen = 0; + int i, off; + const char *sep = "["; +#define CATSEP do { rb_str_cat2(ret, sep); sep = " "; } while (0); + + rb_str_cat2(ret, "LINK"); + + endp = ((char *)addr) + socklen; + + if (offsetof(struct sockaddr_dl, sdl_data) < socklen) { + np = addr->sdl_data; + nlen = addr->sdl_nlen; + if (endp - np < nlen) + nlen = (int)(endp - np); + } + off = addr->sdl_nlen; + + if (offsetof(struct sockaddr_dl, sdl_data) + off < socklen) { + ap = addr->sdl_data + off; + alen = addr->sdl_alen; + if (endp - ap < alen) + alen = (int)(endp - ap); + } + + CATSEP; + if (np) + rb_str_catf(ret, "%.*s", nlen, np); + else + rb_str_cat2(ret, "?"); + + if (ap && 0 < alen) { + CATSEP; + for (i = 0; i < alen; i++) + rb_str_catf(ret, "%s%02x", i == 0 ? "" : ":", (unsigned char)ap[i]); + } + + if (socklen < (socklen_t)(offsetof(struct sockaddr_dl, sdl_nlen) + sizeof(addr->sdl_nlen)) || + socklen < (socklen_t)(offsetof(struct sockaddr_dl, sdl_alen) + sizeof(addr->sdl_alen)) || + socklen < (socklen_t)(offsetof(struct sockaddr_dl, sdl_slen) + sizeof(addr->sdl_slen)) || + /* longer length is possible behavior because struct sockaddr_dl has "minimum work area, can be larger" as the last field. + * cf. Net2:/usr/src/sys/net/if_dl.h. */ + socklen < (socklen_t)(offsetof(struct sockaddr_dl, sdl_data) + addr->sdl_nlen + addr->sdl_alen + addr->sdl_slen)) { + CATSEP; + rb_str_catf(ret, "(%d bytes for %d bytes sockaddr_dl)", + (int)socklen, (int)sizeof(struct sockaddr_dl)); + } + + rb_str_cat2(ret, "]"); +#undef CATSEP + break; + } +#endif + + default: + { + ID id = rsock_intern_family(sockaddr->addr.sa_family); + if (id == 0) + rb_str_catf(ret, "unknown address family %d", sockaddr->addr.sa_family); + else + rb_str_catf(ret, "%s address format unknown", rb_id2name(id)); + break; + } + } + } + + return ret; +} + +/* + * call-seq: + * addrinfo.inspect => string + * + * returns a string which shows addrinfo in human-readable form. + * + * Addrinfo.tcp("localhost", 80).inspect #=> "#<Addrinfo: 127.0.0.1:80 TCP (localhost)>" + * Addrinfo.unix("/tmp/sock").inspect #=> "#<Addrinfo: /tmp/sock SOCK_STREAM>" + * + */ +static VALUE +addrinfo_inspect(VALUE self) +{ + rb_addrinfo_t *rai = get_addrinfo(self); + int internet_p; + VALUE ret; + + ret = rb_sprintf("#<%s: ", rb_obj_classname(self)); + + inspect_sockaddr(self, ret); + + if (rai->pfamily && ai_get_afamily(rai) != rai->pfamily) { + ID id = rsock_intern_protocol_family(rai->pfamily); + if (id) + rb_str_catf(ret, " %s", rb_id2name(id)); + else + rb_str_catf(ret, " PF_\?\?\?(%d)", rai->pfamily); + } + + internet_p = rai->pfamily == PF_INET; +#ifdef INET6 + internet_p = internet_p || rai->pfamily == PF_INET6; +#endif + if (internet_p && rai->socktype == SOCK_STREAM && + (rai->protocol == 0 || rai->protocol == IPPROTO_TCP)) { + rb_str_cat2(ret, " TCP"); + } + else if (internet_p && rai->socktype == SOCK_DGRAM && + (rai->protocol == 0 || rai->protocol == IPPROTO_UDP)) { + rb_str_cat2(ret, " UDP"); + } + else { + if (rai->socktype) { + ID id = rsock_intern_socktype(rai->socktype); + if (id) + rb_str_catf(ret, " %s", rb_id2name(id)); + else + rb_str_catf(ret, " SOCK_\?\?\?(%d)", rai->socktype); + } + + if (rai->protocol) { + if (internet_p) { + ID id = rsock_intern_ipproto(rai->protocol); + if (id) + rb_str_catf(ret, " %s", rb_id2name(id)); + else + goto unknown_protocol; + } + else { + unknown_protocol: + rb_str_catf(ret, " UNKNOWN_PROTOCOL(%d)", rai->protocol); + } + } + } + + if (!NIL_P(rai->canonname)) { + VALUE name = rai->canonname; + rb_str_catf(ret, " %s", StringValueCStr(name)); + } + + if (!NIL_P(rai->inspectname)) { + VALUE name = rai->inspectname; + rb_str_catf(ret, " (%s)", StringValueCStr(name)); + } + + rb_str_buf_cat2(ret, ">"); + return ret; +} + +/* + * call-seq: + * addrinfo.inspect_sockaddr => string + * + * returns a string which shows the sockaddr in _addrinfo_ with human-readable form. + * + * Addrinfo.tcp("localhost", 80).inspect_sockaddr #=> "127.0.0.1:80" + * Addrinfo.tcp("ip6-localhost", 80).inspect_sockaddr #=> "[::1]:80" + * Addrinfo.unix("/tmp/sock").inspect_sockaddr #=> "/tmp/sock" + * + */ +VALUE +rsock_addrinfo_inspect_sockaddr(VALUE self) +{ + return inspect_sockaddr(self, rb_str_new("", 0)); +} + +/* :nodoc: */ +static VALUE +addrinfo_mdump(VALUE self) +{ + rb_addrinfo_t *rai = get_addrinfo(self); + VALUE sockaddr, afamily, pfamily, socktype, protocol, canonname, inspectname; + int afamily_int = ai_get_afamily(rai); + ID id; + + id = rsock_intern_protocol_family(rai->pfamily); + if (id == 0) + rb_raise(rb_eSocket, "unknown protocol family: %d", rai->pfamily); + pfamily = rb_id2str(id); + + if (rai->socktype == 0) + socktype = INT2FIX(0); + else { + id = rsock_intern_socktype(rai->socktype); + if (id == 0) + rb_raise(rb_eSocket, "unknown socktype: %d", rai->socktype); + socktype = rb_id2str(id); + } + + if (rai->protocol == 0) + protocol = INT2FIX(0); + else if (IS_IP_FAMILY(afamily_int)) { + id = rsock_intern_ipproto(rai->protocol); + if (id == 0) + rb_raise(rb_eSocket, "unknown IP protocol: %d", rai->protocol); + protocol = rb_id2str(id); + } + else { + rb_raise(rb_eSocket, "unknown protocol: %d", rai->protocol); + } + + canonname = rai->canonname; + + inspectname = rai->inspectname; + + id = rsock_intern_family(afamily_int); + if (id == 0) + rb_raise(rb_eSocket, "unknown address family: %d", afamily_int); + afamily = rb_id2str(id); + + switch(afamily_int) { +#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN + case AF_UNIX: + { + sockaddr = rb_str_new(rai->addr.un.sun_path, rai_unixsocket_len(rai)); + break; + } +#endif + + default: + { + char hbuf[NI_MAXHOST], pbuf[NI_MAXSERV]; + int error; + error = rb_getnameinfo(&rai->addr.addr, rai->sockaddr_len, + hbuf, (socklen_t)sizeof(hbuf), pbuf, (socklen_t)sizeof(pbuf), + NI_NUMERICHOST|NI_NUMERICSERV); + if (error) { + rsock_raise_resolution_error("getnameinfo", error); + } + sockaddr = rb_assoc_new(rb_str_new_cstr(hbuf), rb_str_new_cstr(pbuf)); + break; + } + } + + return rb_ary_new3(7, afamily, sockaddr, pfamily, socktype, protocol, canonname, inspectname); +} + +/* :nodoc: */ +static VALUE +addrinfo_mload(VALUE self, VALUE ary) +{ + VALUE v; + VALUE canonname, inspectname; + int afamily, pfamily, socktype, protocol; + union_sockaddr ss; + socklen_t len; + rb_addrinfo_t *rai; + + if (check_addrinfo(self)) + rb_raise(rb_eTypeError, "already initialized socket address"); + + ary = rb_convert_type(ary, T_ARRAY, "Array", "to_ary"); + + v = rb_ary_entry(ary, 0); + StringValue(v); + if (rsock_family_to_int(RSTRING_PTR(v), RSTRING_LEN(v), &afamily) == -1) + rb_raise(rb_eTypeError, "unexpected address family"); + + v = rb_ary_entry(ary, 2); + StringValue(v); + if (rsock_family_to_int(RSTRING_PTR(v), RSTRING_LEN(v), &pfamily) == -1) + rb_raise(rb_eTypeError, "unexpected protocol family"); + + v = rb_ary_entry(ary, 3); + if (v == INT2FIX(0)) + socktype = 0; + else { + StringValue(v); + if (rsock_socktype_to_int(RSTRING_PTR(v), RSTRING_LEN(v), &socktype) == -1) + rb_raise(rb_eTypeError, "unexpected socktype"); + } + + v = rb_ary_entry(ary, 4); + if (v == INT2FIX(0)) + protocol = 0; + else { + StringValue(v); + if (IS_IP_FAMILY(afamily)) { + if (rsock_ipproto_to_int(RSTRING_PTR(v), RSTRING_LEN(v), &protocol) == -1) + rb_raise(rb_eTypeError, "unexpected protocol"); + } + else { + rb_raise(rb_eTypeError, "unexpected protocol"); + } + } + + v = rb_ary_entry(ary, 5); + if (NIL_P(v)) + canonname = Qnil; + else { + StringValue(v); + canonname = v; + } + + v = rb_ary_entry(ary, 6); + if (NIL_P(v)) + inspectname = Qnil; + else { + StringValue(v); + inspectname = v; + } + + v = rb_ary_entry(ary, 1); + switch(afamily) { +#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN + case AF_UNIX: + { + struct sockaddr_un uaddr; + INIT_SOCKADDR_UN(&uaddr, sizeof(struct sockaddr_un)); + + StringValue(v); + if (sizeof(uaddr.sun_path) < (size_t)RSTRING_LEN(v)) + rb_raise(rb_eSocket, + "too long AF_UNIX path (%"PRIuSIZE" bytes given but %"PRIuSIZE" bytes max)", + (size_t)RSTRING_LEN(v), sizeof(uaddr.sun_path)); + memcpy(uaddr.sun_path, RSTRING_PTR(v), RSTRING_LEN(v)); + len = (socklen_t)sizeof(uaddr); + memcpy(&ss, &uaddr, len); + break; + } +#endif + + default: + { + VALUE pair = rb_convert_type(v, T_ARRAY, "Array", "to_ary"); + struct rb_addrinfo *res; + int flags = AI_NUMERICHOST; +#ifdef AI_NUMERICSERV + flags |= AI_NUMERICSERV; +#endif + res = call_getaddrinfo(rb_ary_entry(pair, 0), rb_ary_entry(pair, 1), + INT2NUM(pfamily), INT2NUM(socktype), INT2NUM(protocol), + INT2NUM(flags), 1, Qnil); + + len = res->ai->ai_addrlen; + memcpy(&ss, res->ai->ai_addr, res->ai->ai_addrlen); + rb_freeaddrinfo(res); + break; + } + } + + DATA_PTR(self) = rai = alloc_addrinfo(); + init_addrinfo(self, rai, &ss.addr, len, + pfamily, socktype, protocol, + canonname, inspectname); + return self; +} + +/* + * call-seq: + * addrinfo.afamily => integer + * + * returns the address family as an integer. + * + * Addrinfo.tcp("localhost", 80).afamily == Socket::AF_INET #=> true + * + */ +static VALUE +addrinfo_afamily(VALUE self) +{ + rb_addrinfo_t *rai = get_addrinfo(self); + return INT2NUM(ai_get_afamily(rai)); +} + +/* + * call-seq: + * addrinfo.pfamily => integer + * + * returns the protocol family as an integer. + * + * Addrinfo.tcp("localhost", 80).pfamily == Socket::PF_INET #=> true + * + */ +static VALUE +addrinfo_pfamily(VALUE self) +{ + rb_addrinfo_t *rai = get_addrinfo(self); + return INT2NUM(rai->pfamily); +} + +/* + * call-seq: + * addrinfo.socktype => integer + * + * returns the socket type as an integer. + * + * Addrinfo.tcp("localhost", 80).socktype == Socket::SOCK_STREAM #=> true + * + */ +static VALUE +addrinfo_socktype(VALUE self) +{ + rb_addrinfo_t *rai = get_addrinfo(self); + return INT2NUM(rai->socktype); +} + +/* + * call-seq: + * addrinfo.protocol => integer + * + * returns the socket type as an integer. + * + * Addrinfo.tcp("localhost", 80).protocol == Socket::IPPROTO_TCP #=> true + * + */ +static VALUE +addrinfo_protocol(VALUE self) +{ + rb_addrinfo_t *rai = get_addrinfo(self); + return INT2NUM(rai->protocol); +} + +/* + * call-seq: + * addrinfo.to_sockaddr => string + * addrinfo.to_s => string + * + * returns the socket address as packed struct sockaddr string. + * + * Addrinfo.tcp("localhost", 80).to_sockaddr + * #=> "\x02\x00\x00P\x7F\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00" + * + */ +static VALUE +addrinfo_to_sockaddr(VALUE self) +{ + rb_addrinfo_t *rai = get_addrinfo(self); + VALUE ret; + ret = rb_str_new((char*)&rai->addr, rai->sockaddr_len); + return ret; +} + +/* + * call-seq: + * addrinfo.canonname => string or nil + * + * returns the canonical name as a string. + * + * nil is returned if no canonical name. + * + * The canonical name is set by Addrinfo.getaddrinfo when AI_CANONNAME is specified. + * + * list = Addrinfo.getaddrinfo("www.ruby-lang.org", 80, :INET, :STREAM, nil, Socket::AI_CANONNAME) + * p list[0] #=> #<Addrinfo: 221.186.184.68:80 TCP carbon.ruby-lang.org (www.ruby-lang.org)> + * p list[0].canonname #=> "carbon.ruby-lang.org" + * + */ +static VALUE +addrinfo_canonname(VALUE self) +{ + rb_addrinfo_t *rai = get_addrinfo(self); + return rai->canonname; +} + +/* + * call-seq: + * addrinfo.ip? => true or false + * + * returns true if addrinfo is internet (IPv4/IPv6) address. + * returns false otherwise. + * + * Addrinfo.tcp("127.0.0.1", 80).ip? #=> true + * Addrinfo.tcp("::1", 80).ip? #=> true + * Addrinfo.unix("/tmp/sock").ip? #=> false + * + */ +static VALUE +addrinfo_ip_p(VALUE self) +{ + rb_addrinfo_t *rai = get_addrinfo(self); + int family = ai_get_afamily(rai); + return IS_IP_FAMILY(family) ? Qtrue : Qfalse; +} + +/* + * call-seq: + * addrinfo.ipv4? => true or false + * + * returns true if addrinfo is IPv4 address. + * returns false otherwise. + * + * Addrinfo.tcp("127.0.0.1", 80).ipv4? #=> true + * Addrinfo.tcp("::1", 80).ipv4? #=> false + * Addrinfo.unix("/tmp/sock").ipv4? #=> false + * + */ +static VALUE +addrinfo_ipv4_p(VALUE self) +{ + rb_addrinfo_t *rai = get_addrinfo(self); + return ai_get_afamily(rai) == AF_INET ? Qtrue : Qfalse; +} + +/* + * call-seq: + * addrinfo.ipv6? => true or false + * + * returns true if addrinfo is IPv6 address. + * returns false otherwise. + * + * Addrinfo.tcp("127.0.0.1", 80).ipv6? #=> false + * Addrinfo.tcp("::1", 80).ipv6? #=> true + * Addrinfo.unix("/tmp/sock").ipv6? #=> false + * + */ +static VALUE +addrinfo_ipv6_p(VALUE self) +{ +#ifdef AF_INET6 + rb_addrinfo_t *rai = get_addrinfo(self); + return ai_get_afamily(rai) == AF_INET6 ? Qtrue : Qfalse; +#else + return Qfalse; +#endif +} + +/* + * call-seq: + * addrinfo.unix? => true or false + * + * returns true if addrinfo is UNIX address. + * returns false otherwise. + * + * Addrinfo.tcp("127.0.0.1", 80).unix? #=> false + * Addrinfo.tcp("::1", 80).unix? #=> false + * Addrinfo.unix("/tmp/sock").unix? #=> true + * + */ +static VALUE +addrinfo_unix_p(VALUE self) +{ + rb_addrinfo_t *rai = get_addrinfo(self); +#ifdef AF_UNIX + return ai_get_afamily(rai) == AF_UNIX ? Qtrue : Qfalse; +#else + return Qfalse; +#endif +} + +/* + * call-seq: + * addrinfo.getnameinfo => [nodename, service] + * addrinfo.getnameinfo(flags) => [nodename, service] + * + * returns nodename and service as a pair of strings. + * This converts struct sockaddr in addrinfo to textual representation. + * + * flags should be bitwise OR of Socket::NI_??? constants. + * + * Addrinfo.tcp("127.0.0.1", 80).getnameinfo #=> ["localhost", "www"] + * + * Addrinfo.tcp("127.0.0.1", 80).getnameinfo(Socket::NI_NUMERICSERV) + * #=> ["localhost", "80"] + */ +static VALUE +addrinfo_getnameinfo(int argc, VALUE *argv, VALUE self) +{ + rb_addrinfo_t *rai = get_addrinfo(self); + VALUE vflags; + char hbuf[1024], pbuf[1024]; + int flags, error; + + rb_scan_args(argc, argv, "01", &vflags); + + flags = NIL_P(vflags) ? 0 : NUM2INT(vflags); + + if (rai->socktype == SOCK_DGRAM) + flags |= NI_DGRAM; + + error = rb_getnameinfo(&rai->addr.addr, rai->sockaddr_len, + hbuf, (socklen_t)sizeof(hbuf), pbuf, (socklen_t)sizeof(pbuf), + flags); + if (error) { + rsock_raise_resolution_error("getnameinfo", error); + } + + return rb_assoc_new(rb_str_new2(hbuf), rb_str_new2(pbuf)); +} + +/* + * call-seq: + * addrinfo.ip_unpack => [addr, port] + * + * Returns the IP address and port number as 2-element array. + * + * Addrinfo.tcp("127.0.0.1", 80).ip_unpack #=> ["127.0.0.1", 80] + * Addrinfo.tcp("::1", 80).ip_unpack #=> ["::1", 80] + */ +static VALUE +addrinfo_ip_unpack(VALUE self) +{ + rb_addrinfo_t *rai = get_addrinfo(self); + int family = ai_get_afamily(rai); + VALUE vflags; + VALUE ret, portstr; + + if (!IS_IP_FAMILY(family)) + rb_raise(rb_eSocket, "need IPv4 or IPv6 address"); + + vflags = INT2NUM(NI_NUMERICHOST|NI_NUMERICSERV); + ret = addrinfo_getnameinfo(1, &vflags, self); + portstr = rb_ary_entry(ret, 1); + rb_ary_store(ret, 1, INT2NUM(atoi(StringValueCStr(portstr)))); + return ret; +} + +/* + * call-seq: + * addrinfo.ip_address => string + * + * Returns the IP address as a string. + * + * Addrinfo.tcp("127.0.0.1", 80).ip_address #=> "127.0.0.1" + * Addrinfo.tcp("::1", 80).ip_address #=> "::1" + */ +static VALUE +addrinfo_ip_address(VALUE self) +{ + rb_addrinfo_t *rai = get_addrinfo(self); + int family = ai_get_afamily(rai); + VALUE vflags; + VALUE ret; + + if (!IS_IP_FAMILY(family)) + rb_raise(rb_eSocket, "need IPv4 or IPv6 address"); + + vflags = INT2NUM(NI_NUMERICHOST|NI_NUMERICSERV); + ret = addrinfo_getnameinfo(1, &vflags, self); + return rb_ary_entry(ret, 0); +} + +/* + * call-seq: + * addrinfo.ip_port => port + * + * Returns the port number as an integer. + * + * Addrinfo.tcp("127.0.0.1", 80).ip_port #=> 80 + * Addrinfo.tcp("::1", 80).ip_port #=> 80 + */ +static VALUE +addrinfo_ip_port(VALUE self) +{ + rb_addrinfo_t *rai = get_addrinfo(self); + int family = ai_get_afamily(rai); + int port; + + if (!IS_IP_FAMILY(family)) { + bad_family: +#ifdef AF_INET6 + rb_raise(rb_eSocket, "need IPv4 or IPv6 address"); +#else + rb_raise(rb_eSocket, "need IPv4 address"); +#endif + } + + switch (family) { + case AF_INET: + if (rai->sockaddr_len != sizeof(struct sockaddr_in)) + rb_raise(rb_eSocket, "unexpected sockaddr size for IPv4"); + port = ntohs(rai->addr.in.sin_port); + break; + +#ifdef AF_INET6 + case AF_INET6: + if (rai->sockaddr_len != sizeof(struct sockaddr_in6)) + rb_raise(rb_eSocket, "unexpected sockaddr size for IPv6"); + port = ntohs(rai->addr.in6.sin6_port); + break; +#endif + + default: + goto bad_family; + } + + return INT2NUM(port); +} + +static int +extract_in_addr(VALUE self, uint32_t *addrp) +{ + rb_addrinfo_t *rai = get_addrinfo(self); + int family = ai_get_afamily(rai); + if (family != AF_INET) return 0; + *addrp = ntohl(rai->addr.in.sin_addr.s_addr); + return 1; +} + +/* + * Returns true for IPv4 private address (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16). + * It returns false otherwise. + */ +static VALUE +addrinfo_ipv4_private_p(VALUE self) +{ + uint32_t a; + if (!extract_in_addr(self, &a)) return Qfalse; + if ((a & 0xff000000) == 0x0a000000 || /* 10.0.0.0/8 */ + (a & 0xfff00000) == 0xac100000 || /* 172.16.0.0/12 */ + (a & 0xffff0000) == 0xc0a80000) /* 192.168.0.0/16 */ + return Qtrue; + return Qfalse; +} + +/* + * Returns true for IPv4 loopback address (127.0.0.0/8). + * It returns false otherwise. + */ +static VALUE +addrinfo_ipv4_loopback_p(VALUE self) +{ + uint32_t a; + if (!extract_in_addr(self, &a)) return Qfalse; + if ((a & 0xff000000) == 0x7f000000) /* 127.0.0.0/8 */ + return Qtrue; + return Qfalse; +} + +/* + * Returns true for IPv4 multicast address (224.0.0.0/4). + * It returns false otherwise. + */ +static VALUE +addrinfo_ipv4_multicast_p(VALUE self) +{ + uint32_t a; + if (!extract_in_addr(self, &a)) return Qfalse; + if ((a & 0xf0000000) == 0xe0000000) /* 224.0.0.0/4 */ + return Qtrue; + return Qfalse; +} + +#ifdef INET6 + +static struct in6_addr * +extract_in6_addr(VALUE self) +{ + rb_addrinfo_t *rai = get_addrinfo(self); + int family = ai_get_afamily(rai); + if (family != AF_INET6) return NULL; + return &rai->addr.in6.sin6_addr; +} + +/* + * Returns true for IPv6 unspecified address (::). + * It returns false otherwise. + */ +static VALUE +addrinfo_ipv6_unspecified_p(VALUE self) +{ + struct in6_addr *addr = extract_in6_addr(self); + if (addr && IN6_IS_ADDR_UNSPECIFIED(addr)) return Qtrue; + return Qfalse; +} + +/* + * Returns true for IPv6 loopback address (::1). + * It returns false otherwise. + */ +static VALUE +addrinfo_ipv6_loopback_p(VALUE self) +{ + struct in6_addr *addr = extract_in6_addr(self); + if (addr && IN6_IS_ADDR_LOOPBACK(addr)) return Qtrue; + return Qfalse; +} + +/* + * Returns true for IPv6 multicast address (ff00::/8). + * It returns false otherwise. + */ +static VALUE +addrinfo_ipv6_multicast_p(VALUE self) +{ + struct in6_addr *addr = extract_in6_addr(self); + if (addr && IN6_IS_ADDR_MULTICAST(addr)) return Qtrue; + return Qfalse; +} + +/* + * Returns true for IPv6 link local address (fe80::/10). + * It returns false otherwise. + */ +static VALUE +addrinfo_ipv6_linklocal_p(VALUE self) +{ + struct in6_addr *addr = extract_in6_addr(self); + if (addr && IN6_IS_ADDR_LINKLOCAL(addr)) return Qtrue; + return Qfalse; +} + +/* + * Returns true for IPv6 site local address (fec0::/10). + * It returns false otherwise. + */ +static VALUE +addrinfo_ipv6_sitelocal_p(VALUE self) +{ + struct in6_addr *addr = extract_in6_addr(self); + if (addr && IN6_IS_ADDR_SITELOCAL(addr)) return Qtrue; + return Qfalse; +} + +/* + * Returns true for IPv6 unique local address (fc00::/7, RFC4193). + * It returns false otherwise. + */ +static VALUE +addrinfo_ipv6_unique_local_p(VALUE self) +{ + struct in6_addr *addr = extract_in6_addr(self); + if (addr && IN6_IS_ADDR_UNIQUE_LOCAL(addr)) return Qtrue; + return Qfalse; +} + +/* + * Returns true for IPv4-mapped IPv6 address (::ffff:0:0/80). + * It returns false otherwise. + */ +static VALUE +addrinfo_ipv6_v4mapped_p(VALUE self) +{ + struct in6_addr *addr = extract_in6_addr(self); + if (addr && IN6_IS_ADDR_V4MAPPED(addr)) return Qtrue; + return Qfalse; +} + +/* + * Returns true for IPv4-compatible IPv6 address (::/80). + * It returns false otherwise. + */ +static VALUE +addrinfo_ipv6_v4compat_p(VALUE self) +{ + struct in6_addr *addr = extract_in6_addr(self); + if (addr && IN6_IS_ADDR_V4COMPAT(addr)) return Qtrue; + return Qfalse; +} + +/* + * Returns true for IPv6 multicast node-local scope address. + * It returns false otherwise. + */ +static VALUE +addrinfo_ipv6_mc_nodelocal_p(VALUE self) +{ + struct in6_addr *addr = extract_in6_addr(self); + if (addr && IN6_IS_ADDR_MC_NODELOCAL(addr)) return Qtrue; + return Qfalse; +} + +/* + * Returns true for IPv6 multicast link-local scope address. + * It returns false otherwise. + */ +static VALUE +addrinfo_ipv6_mc_linklocal_p(VALUE self) +{ + struct in6_addr *addr = extract_in6_addr(self); + if (addr && IN6_IS_ADDR_MC_LINKLOCAL(addr)) return Qtrue; + return Qfalse; +} + +/* + * Returns true for IPv6 multicast site-local scope address. + * It returns false otherwise. + */ +static VALUE +addrinfo_ipv6_mc_sitelocal_p(VALUE self) +{ + struct in6_addr *addr = extract_in6_addr(self); + if (addr && IN6_IS_ADDR_MC_SITELOCAL(addr)) return Qtrue; + return Qfalse; +} + +/* + * Returns true for IPv6 multicast organization-local scope address. + * It returns false otherwise. + */ +static VALUE +addrinfo_ipv6_mc_orglocal_p(VALUE self) +{ + struct in6_addr *addr = extract_in6_addr(self); + if (addr && IN6_IS_ADDR_MC_ORGLOCAL(addr)) return Qtrue; + return Qfalse; +} + +/* + * Returns true for IPv6 multicast global scope address. + * It returns false otherwise. + */ +static VALUE +addrinfo_ipv6_mc_global_p(VALUE self) +{ + struct in6_addr *addr = extract_in6_addr(self); + if (addr && IN6_IS_ADDR_MC_GLOBAL(addr)) return Qtrue; + return Qfalse; +} + +/* + * Returns IPv4 address of IPv4 mapped/compatible IPv6 address. + * It returns nil if +self+ is not IPv4 mapped/compatible IPv6 address. + * + * Addrinfo.ip("::192.0.2.3").ipv6_to_ipv4 #=> #<Addrinfo: 192.0.2.3> + * Addrinfo.ip("::ffff:192.0.2.3").ipv6_to_ipv4 #=> #<Addrinfo: 192.0.2.3> + * Addrinfo.ip("::1").ipv6_to_ipv4 #=> nil + * Addrinfo.ip("192.0.2.3").ipv6_to_ipv4 #=> nil + * Addrinfo.unix("/tmp/sock").ipv6_to_ipv4 #=> nil + */ +static VALUE +addrinfo_ipv6_to_ipv4(VALUE self) +{ + rb_addrinfo_t *rai = get_addrinfo(self); + struct in6_addr *addr; + int family = ai_get_afamily(rai); + if (family != AF_INET6) return Qnil; + addr = &rai->addr.in6.sin6_addr; + if (IN6_IS_ADDR_V4MAPPED(addr) || IN6_IS_ADDR_V4COMPAT(addr)) { + struct sockaddr_in sin4; + INIT_SOCKADDR_IN(&sin4, sizeof(sin4)); + memcpy(&sin4.sin_addr, (char*)addr + sizeof(*addr) - sizeof(sin4.sin_addr), sizeof(sin4.sin_addr)); + return rsock_addrinfo_new((struct sockaddr *)&sin4, (socklen_t)sizeof(sin4), + PF_INET, rai->socktype, rai->protocol, + rai->canonname, rai->inspectname); + } + else { + return Qnil; + } +} + +#endif + +#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN +/* + * call-seq: + * addrinfo.unix_path => path + * + * Returns the socket path as a string. + * + * Addrinfo.unix("/tmp/sock").unix_path #=> "/tmp/sock" + */ +static VALUE +addrinfo_unix_path(VALUE self) +{ + rb_addrinfo_t *rai = get_addrinfo(self); + int family = ai_get_afamily(rai); + struct sockaddr_un *addr; + long n; + + if (family != AF_UNIX) + rb_raise(rb_eSocket, "need AF_UNIX address"); + + addr = &rai->addr.un; + + n = rai_unixsocket_len(rai); + if (n < 0) + rb_raise(rb_eSocket, "too short AF_UNIX address: %"PRIuSIZE" bytes given for minimum %"PRIuSIZE" bytes.", + (size_t)rai->sockaddr_len, offsetof(struct sockaddr_un, sun_path)); + if ((long)sizeof(addr->sun_path) < n) + rb_raise(rb_eSocket, + "too long AF_UNIX path (%"PRIuSIZE" bytes given but %"PRIuSIZE" bytes max)", + (size_t)n, sizeof(addr->sun_path)); + return rb_str_new(addr->sun_path, n); +} +#endif + +static ID id_timeout; + +/* + * call-seq: + * Addrinfo.getaddrinfo(nodename, service, family, socktype, protocol, flags) => [addrinfo, ...] + * Addrinfo.getaddrinfo(nodename, service, family, socktype, protocol) => [addrinfo, ...] + * Addrinfo.getaddrinfo(nodename, service, family, socktype) => [addrinfo, ...] + * Addrinfo.getaddrinfo(nodename, service, family) => [addrinfo, ...] + * Addrinfo.getaddrinfo(nodename, service) => [addrinfo, ...] + * + * returns a list of addrinfo objects as an array. + * + * This method converts nodename (hostname) and service (port) to addrinfo. + * Since the conversion is not unique, the result is a list of addrinfo objects. + * + * nodename or service can be nil if no conversion intended. + * + * family, socktype and protocol are hint for preferred protocol. + * If the result will be used for a socket with SOCK_STREAM, + * SOCK_STREAM should be specified as socktype. + * If so, Addrinfo.getaddrinfo returns addrinfo list appropriate for SOCK_STREAM. + * If they are omitted or nil is given, the result is not restricted. + * + * Similarly, PF_INET6 as family restricts for IPv6. + * + * flags should be bitwise OR of Socket::AI_??? constants such as follows. + * Note that the exact list of the constants depends on OS. + * + * AI_PASSIVE Get address to use with bind() + * AI_CANONNAME Fill in the canonical name + * AI_NUMERICHOST Prevent host name resolution + * AI_NUMERICSERV Prevent service name resolution + * AI_V4MAPPED Accept IPv4-mapped IPv6 addresses + * AI_ALL Allow all addresses + * AI_ADDRCONFIG Accept only if any address is assigned + * + * Note that socktype should be specified whenever application knows the usage of the address. + * Some platform causes an error when socktype is omitted and servname is specified as an integer + * because some port numbers, 512 for example, are ambiguous without socktype. + * + * Addrinfo.getaddrinfo("www.kame.net", 80, nil, :STREAM) + * #=> [#<Addrinfo: 203.178.141.194:80 TCP (www.kame.net)>, + * # #<Addrinfo: [2001:200:dff:fff1:216:3eff:feb1:44d7]:80 TCP (www.kame.net)>] + * + */ +static VALUE +addrinfo_s_getaddrinfo(int argc, VALUE *argv, VALUE self) +{ + VALUE node, service, family, socktype, protocol, flags, opts, timeout; + + rb_scan_args(argc, argv, "24:", &node, &service, &family, &socktype, + &protocol, &flags, &opts); + rb_get_kwargs(opts, &id_timeout, 0, 1, &timeout); + if (timeout == Qundef) { + timeout = Qnil; + } + + return addrinfo_list_new(node, service, family, socktype, protocol, flags, timeout); +} + +/* + * call-seq: + * Addrinfo.ip(host) => addrinfo + * + * returns an addrinfo object for IP address. + * + * The port, socktype, protocol of the result is filled by zero. + * So, it is not appropriate to create a socket. + * + * Addrinfo.ip("localhost") #=> #<Addrinfo: 127.0.0.1 (localhost)> + */ +static VALUE +addrinfo_s_ip(VALUE self, VALUE host) +{ + VALUE ret; + rb_addrinfo_t *rai; + ret = addrinfo_firstonly_new(host, Qnil, + INT2NUM(PF_UNSPEC), INT2FIX(0), INT2FIX(0), INT2FIX(0)); + rai = get_addrinfo(ret); + rai->socktype = 0; + rai->protocol = 0; + return ret; +} + +/* + * call-seq: + * Addrinfo.tcp(host, port) => addrinfo + * + * returns an addrinfo object for TCP address. + * + * Addrinfo.tcp("localhost", "smtp") #=> #<Addrinfo: 127.0.0.1:25 TCP (localhost:smtp)> + */ +static VALUE +addrinfo_s_tcp(VALUE self, VALUE host, VALUE port) +{ + return addrinfo_firstonly_new(host, port, + INT2NUM(PF_UNSPEC), INT2NUM(SOCK_STREAM), INT2NUM(IPPROTO_TCP), INT2FIX(0)); +} + +/* + * call-seq: + * Addrinfo.udp(host, port) => addrinfo + * + * returns an addrinfo object for UDP address. + * + * Addrinfo.udp("localhost", "daytime") #=> #<Addrinfo: 127.0.0.1:13 UDP (localhost:daytime)> + */ +static VALUE +addrinfo_s_udp(VALUE self, VALUE host, VALUE port) +{ + return addrinfo_firstonly_new(host, port, + INT2NUM(PF_UNSPEC), INT2NUM(SOCK_DGRAM), INT2NUM(IPPROTO_UDP), INT2FIX(0)); +} + +#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN + +/* + * call-seq: + * Addrinfo.unix(path [, socktype]) => addrinfo + * + * returns an addrinfo object for UNIX socket address. + * + * _socktype_ specifies the socket type. + * If it is omitted, :STREAM is used. + * + * Addrinfo.unix("/tmp/sock") #=> #<Addrinfo: /tmp/sock SOCK_STREAM> + * Addrinfo.unix("/tmp/sock", :DGRAM) #=> #<Addrinfo: /tmp/sock SOCK_DGRAM> + */ +static VALUE +addrinfo_s_unix(int argc, VALUE *argv, VALUE self) +{ + VALUE path, vsocktype, addr; + int socktype; + rb_addrinfo_t *rai; + + rb_scan_args(argc, argv, "11", &path, &vsocktype); + + if (NIL_P(vsocktype)) + socktype = SOCK_STREAM; + else + socktype = rsock_socktype_arg(vsocktype); + + addr = addrinfo_s_allocate(rb_cAddrinfo); + DATA_PTR(addr) = rai = alloc_addrinfo(); + init_unix_addrinfo(self, rai, path, socktype); + return addr; +} + +#endif + +VALUE +rsock_sockaddr_string_value(volatile VALUE *v) +{ + VALUE val = *v; + if (IS_ADDRINFO(val)) { + *v = addrinfo_to_sockaddr(val); + } + StringValue(*v); + return *v; +} + +VALUE +rsock_sockaddr_string_value_with_addrinfo(volatile VALUE *v, VALUE *rai_ret) +{ + VALUE val = *v; + *rai_ret = Qnil; + if (IS_ADDRINFO(val)) { + *v = addrinfo_to_sockaddr(val); + *rai_ret = val; + } + StringValue(*v); + return *v; +} + +char * +rsock_sockaddr_string_value_ptr(volatile VALUE *v) +{ + rsock_sockaddr_string_value(v); + return RSTRING_PTR(*v); +} + +VALUE +rb_check_sockaddr_string_type(VALUE val) +{ + if (IS_ADDRINFO(val)) + return addrinfo_to_sockaddr(val); + return rb_check_string_type(val); +} + +VALUE +rsock_fd_socket_addrinfo(int fd, struct sockaddr *addr, socklen_t len) +{ + int family; + int socktype; + int ret; + socklen_t optlen = (socklen_t)sizeof(socktype); + + /* assumes protocol family and address family are identical */ + family = get_afamily(addr, len); + + ret = getsockopt(fd, SOL_SOCKET, SO_TYPE, (void*)&socktype, &optlen); + if (ret == -1) { + rb_sys_fail("getsockopt(SO_TYPE)"); + } + + return rsock_addrinfo_new(addr, len, family, socktype, 0, Qnil, Qnil); +} + +VALUE +rsock_io_socket_addrinfo(VALUE io, struct sockaddr *addr, socklen_t len) +{ + rb_io_t *fptr; + + switch (TYPE(io)) { + case T_FIXNUM: + return rsock_fd_socket_addrinfo(FIX2INT(io), addr, len); + + case T_BIGNUM: + return rsock_fd_socket_addrinfo(NUM2INT(io), addr, len); + + case T_FILE: + GetOpenFile(io, fptr); + return rsock_fd_socket_addrinfo(fptr->fd, addr, len); + + default: + rb_raise(rb_eTypeError, "neither IO nor file descriptor"); + } + + UNREACHABLE_RETURN(Qnil); +} + +#if FAST_FALLBACK_INIT_INETSOCK_IMPL == 1 + +void +free_fast_fallback_getaddrinfo_shared(struct fast_fallback_getaddrinfo_shared **shared) +{ + xfree((*shared)->node); + (*shared)->node = NULL; + xfree((*shared)->service); + (*shared)->service = NULL; + rb_nativethread_lock_destroy(&(*shared)->lock); + free(*shared); + *shared = NULL; +} + +static void * +do_fast_fallback_getaddrinfo(void *ptr) +{ + struct fast_fallback_getaddrinfo_entry *entry = (struct fast_fallback_getaddrinfo_entry *)ptr; + struct fast_fallback_getaddrinfo_shared *shared = entry->shared; + int err = 0, shared_need_free = 0; + struct addrinfo *ai = NULL; + + sigset_t set; + sigemptyset(&set); + sigaddset(&set, SIGPIPE); + pthread_sigmask(SIG_BLOCK, &set, NULL); + + err = numeric_getaddrinfo(shared->node, shared->service, &entry->hints, &entry->ai); + + if (err != 0) { + err = getaddrinfo(shared->node, shared->service, &entry->hints, &entry->ai); + #ifdef __linux__ + /* On Linux (mainly Ubuntu 13.04) /etc/nsswitch.conf has mdns4 and + * it cause getaddrinfo to return EAI_SYSTEM/ENOENT. [ruby-list:49420] + */ + if (err == EAI_SYSTEM && errno == ENOENT) + err = EAI_NONAME; + #endif + } + + /* for testing HEv2 */ + if (entry->test_sleep_ms > 0) { + struct timespec sleep_ts; + sleep_ts.tv_sec = entry->test_sleep_ms / 1000; + sleep_ts.tv_nsec = (entry->test_sleep_ms % 1000) * 1000000L; + if (sleep_ts.tv_nsec >= 1000000000L) { + sleep_ts.tv_sec += sleep_ts.tv_nsec / 1000000000L; + sleep_ts.tv_nsec = sleep_ts.tv_nsec % 1000000000L; + } + nanosleep(&sleep_ts, NULL); + } + if (entry->test_ecode != 0) { + err = entry->test_ecode; + if (entry->ai) { + freeaddrinfo(entry->ai); + entry->ai = NULL; + } + } + + rb_nativethread_lock_lock(&shared->lock); + { + entry->err = err; + const char notification = entry->family == AF_INET6 ? + IPV6_HOSTNAME_RESOLVED : IPV4_HOSTNAME_RESOLVED; + + if (shared->notify != -1 && (write(shared->notify, ¬ification, 1)) < 0) { + entry->err = errno; + entry->has_syserr = true; + } + if (--(entry->refcount) == 0) { + ai = entry->ai; + entry->ai = NULL; + } + if (--(shared->refcount) == 0) shared_need_free = 1; + } + rb_nativethread_lock_unlock(&shared->lock); + + if (ai) freeaddrinfo(ai); + if (shared_need_free && shared) { + free_fast_fallback_getaddrinfo_shared(&shared); + } + + return 0; +} + +void * +fork_safe_do_fast_fallback_getaddrinfo(void *ptr) +{ + return rb_thread_prevent_fork(do_fast_fallback_getaddrinfo, ptr); +} + +#endif + +/* + * Addrinfo class + */ +void +rsock_init_addrinfo(void) +{ + id_timeout = rb_intern("timeout"); + + /* + * The Addrinfo class maps <tt>struct addrinfo</tt> to ruby. This + * structure identifies an Internet host and a service. + */ + rb_cAddrinfo = rb_define_class("Addrinfo", rb_cObject); + rb_define_alloc_func(rb_cAddrinfo, addrinfo_s_allocate); + rb_define_method(rb_cAddrinfo, "initialize", addrinfo_initialize, -1); + rb_define_method(rb_cAddrinfo, "inspect", addrinfo_inspect, 0); + rb_define_method(rb_cAddrinfo, "inspect_sockaddr", rsock_addrinfo_inspect_sockaddr, 0); + rb_define_singleton_method(rb_cAddrinfo, "getaddrinfo", addrinfo_s_getaddrinfo, -1); + rb_define_singleton_method(rb_cAddrinfo, "ip", addrinfo_s_ip, 1); + rb_define_singleton_method(rb_cAddrinfo, "tcp", addrinfo_s_tcp, 2); + rb_define_singleton_method(rb_cAddrinfo, "udp", addrinfo_s_udp, 2); +#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN + rb_define_singleton_method(rb_cAddrinfo, "unix", addrinfo_s_unix, -1); +#endif + + rb_define_method(rb_cAddrinfo, "afamily", addrinfo_afamily, 0); + rb_define_method(rb_cAddrinfo, "pfamily", addrinfo_pfamily, 0); + rb_define_method(rb_cAddrinfo, "socktype", addrinfo_socktype, 0); + rb_define_method(rb_cAddrinfo, "protocol", addrinfo_protocol, 0); + rb_define_method(rb_cAddrinfo, "canonname", addrinfo_canonname, 0); + + rb_define_method(rb_cAddrinfo, "ipv4?", addrinfo_ipv4_p, 0); + rb_define_method(rb_cAddrinfo, "ipv6?", addrinfo_ipv6_p, 0); + rb_define_method(rb_cAddrinfo, "unix?", addrinfo_unix_p, 0); + + rb_define_method(rb_cAddrinfo, "ip?", addrinfo_ip_p, 0); + rb_define_method(rb_cAddrinfo, "ip_unpack", addrinfo_ip_unpack, 0); + rb_define_method(rb_cAddrinfo, "ip_address", addrinfo_ip_address, 0); + rb_define_method(rb_cAddrinfo, "ip_port", addrinfo_ip_port, 0); + + rb_define_method(rb_cAddrinfo, "ipv4_private?", addrinfo_ipv4_private_p, 0); + rb_define_method(rb_cAddrinfo, "ipv4_loopback?", addrinfo_ipv4_loopback_p, 0); + rb_define_method(rb_cAddrinfo, "ipv4_multicast?", addrinfo_ipv4_multicast_p, 0); + +#ifdef INET6 + rb_define_method(rb_cAddrinfo, "ipv6_unspecified?", addrinfo_ipv6_unspecified_p, 0); + rb_define_method(rb_cAddrinfo, "ipv6_loopback?", addrinfo_ipv6_loopback_p, 0); + rb_define_method(rb_cAddrinfo, "ipv6_multicast?", addrinfo_ipv6_multicast_p, 0); + rb_define_method(rb_cAddrinfo, "ipv6_linklocal?", addrinfo_ipv6_linklocal_p, 0); + rb_define_method(rb_cAddrinfo, "ipv6_sitelocal?", addrinfo_ipv6_sitelocal_p, 0); + rb_define_method(rb_cAddrinfo, "ipv6_unique_local?", addrinfo_ipv6_unique_local_p, 0); + rb_define_method(rb_cAddrinfo, "ipv6_v4mapped?", addrinfo_ipv6_v4mapped_p, 0); + rb_define_method(rb_cAddrinfo, "ipv6_v4compat?", addrinfo_ipv6_v4compat_p, 0); + rb_define_method(rb_cAddrinfo, "ipv6_mc_nodelocal?", addrinfo_ipv6_mc_nodelocal_p, 0); + rb_define_method(rb_cAddrinfo, "ipv6_mc_linklocal?", addrinfo_ipv6_mc_linklocal_p, 0); + rb_define_method(rb_cAddrinfo, "ipv6_mc_sitelocal?", addrinfo_ipv6_mc_sitelocal_p, 0); + rb_define_method(rb_cAddrinfo, "ipv6_mc_orglocal?", addrinfo_ipv6_mc_orglocal_p, 0); + rb_define_method(rb_cAddrinfo, "ipv6_mc_global?", addrinfo_ipv6_mc_global_p, 0); + + rb_define_method(rb_cAddrinfo, "ipv6_to_ipv4", addrinfo_ipv6_to_ipv4, 0); +#endif + +#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN + rb_define_method(rb_cAddrinfo, "unix_path", addrinfo_unix_path, 0); +#endif + + rb_define_method(rb_cAddrinfo, "to_sockaddr", addrinfo_to_sockaddr, 0); + rb_define_method(rb_cAddrinfo, "to_s", addrinfo_to_sockaddr, 0); /* compatibility for ruby before 1.9.2 */ + + rb_define_method(rb_cAddrinfo, "getnameinfo", addrinfo_getnameinfo, -1); + + rb_define_method(rb_cAddrinfo, "marshal_dump", addrinfo_mdump, 0); + rb_define_method(rb_cAddrinfo, "marshal_load", addrinfo_mload, 1); +} diff --git a/ext/socket/rubysocket.h b/ext/socket/rubysocket.h new file mode 100644 index 0000000000..2ec3ab335a --- /dev/null +++ b/ext/socket/rubysocket.h @@ -0,0 +1,514 @@ +#ifndef RUBY_SOCKET_H +#define RUBY_SOCKET_H 1 + +#include "ruby/config.h" +#include RUBY_EXTCONF_H + +#if defined(__sun) || defined(_AIX) +/* (Recent?) Solaris' <nfs/nfs.h> have conflicting definition of T_DATA. Let + * us honour system definition by undefining ours. + * + * See also [ruby-core:4261] + */ +# include "ruby/ruby.h" +# undef T_DATA +#endif + +#include <errno.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 + +#ifdef _WIN32 +# include <winsock2.h> +# include <ws2tcpip.h> +# include <mswsock.h> +# include <iphlpapi.h> +# if defined(_MSC_VER) +# undef HAVE_TYPE_STRUCT_SOCKADDR_DL +# endif +#else +# include <sys/socket.h> +# 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_TCP_FSM_H +# include <netinet/tcp_fsm.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 + +#ifdef HAVE_NETPACKET_PACKET_H +# include <netpacket/packet.h> +#endif + +#ifdef HAVE_NET_ETHERNET_H +# include <net/ethernet.h> +#endif + +#ifdef HAVE_SYS_UN_H +# include <sys/un.h> +#endif + +#ifdef HAVE_AFUNIX_H +// Windows doesn't have sys/un.h, but it does have afunix.h just to be special: +# include <afunix.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 +# ifdef __HAIKU__ +# define _BSD_SOURCE +# endif +# 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 + +#ifdef HAVE_NET_IF_DL_H +# include <net/if_dl.h> +#endif + +#ifdef SOCKS5 +# include <socks.h> +#endif + +#ifndef HAVE_GETADDRINFO +# include "addrinfo.h" +#endif + +#include "internal.h" +#include "internal/array.h" +#include "internal/compilers.h" +#include "internal/error.h" +#include "internal/gc.h" +#include "internal/io.h" +#include "internal/thread.h" +#include "internal/vm.h" +#include "ruby/io.h" +#include "ruby/ruby.h" +#include "ruby/thread.h" +#include "ruby/util.h" +#include "sockport.h" +#include "ruby/fiber/scheduler.h" + +#ifndef HAVE_TYPE_SOCKLEN_T +typedef int socklen_t; +#endif + +#ifdef NEED_IF_INDEXTONAME_DECL +char *if_indextoname(unsigned int, char *); +#endif +#ifdef NEED_IF_NAMETOINDEX_DECL +unsigned int if_nametoindex(const char *); +#endif + +#define SOCKLEN_MAX \ + (0 < (socklen_t)-1 ? \ + ~(socklen_t)0 : \ + (((((socklen_t)1) << (sizeof(socklen_t) * CHAR_BIT - 2)) - 1) * 2 + 1)) + +#ifndef RSTRING_SOCKLEN +# define RSTRING_SOCKLEN (socklen_t)RSTRING_LENINT +#endif + +#ifndef EWOULDBLOCK +# define EWOULDBLOCK EAGAIN +#endif + +/* + * workaround for NetBSD, OpenBSD and etc. + * The problem is since 4.4BSD-Lite. + * FreeBSD fix the problem at FreeBSD 2.2.0. + * NetBSD fix the problem at NetBSD 3.0 by kern/29624. + * OpenBSD fix the problem at OpenBSD 3.8. + */ +#define pseudo_AF_FTIP pseudo_AF_RTIP + + +#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 IN6_IS_ADDR_UNIQUE_LOCAL +# define IN6_IS_ADDR_UNIQUE_LOCAL(a) (((a)->s6_addr[0] == 0xfc) || ((a)->s6_addr[0] == 0xfd)) +#endif + +#ifndef HAVE_TYPE_STRUCT_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_STRUCT_SOCKADDR_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 + +typedef union { + struct sockaddr addr; + struct sockaddr_in in; +#ifdef AF_INET6 + struct sockaddr_in6 in6; +#endif +#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN + struct sockaddr_un un; +#endif +#ifdef HAVE_TYPE_STRUCT_SOCKADDR_DL + struct sockaddr_dl dl; /* AF_LINK */ +#endif + struct sockaddr_storage storage; + char place_holder[2048]; /* sockaddr_storage is not enough for Unix domain sockets on SunOS and Darwin. */ +} union_sockaddr; + +#ifdef __APPLE__ +/* + * CMSG_ macros are broken on 64bit darwin, because __DARWIN_ALIGN + * aligns up to __darwin_size_t which is 64bit, but CMSG_DATA is + * 32bit-aligned. + */ +# undef __DARWIN_ALIGNBYTES +# define __DARWIN_ALIGNBYTES (sizeof(unsigned int) - 1) +#endif + +#if defined(_AIX) +# 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 + +#define INET_CLIENT 0 +#define INET_SERVER 1 +#define INET_SOCKS 2 + +extern int rsock_do_not_reverse_lookup; +#define FMODE_NOREVLOOKUP 0x100 + +/* common socket families only */ +#define FMODE_UNIX 0x00200000 +#define FMODE_INET 0x00400000 +#define FMODE_INET6 0x00800000 +#define FMODE_SOCK (FMODE_UNIX|FMODE_INET|FMODE_INET6) + +extern VALUE rb_cBasicSocket; +extern VALUE rb_cIPSocket; +extern VALUE rb_cTCPSocket; +extern VALUE rb_cTCPServer; +extern VALUE rb_cUDPSocket; +#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN +extern VALUE rb_cUNIXSocket; +extern VALUE rb_cUNIXServer; +#endif +extern VALUE rb_cSocket; +extern VALUE rb_cAddrinfo; +extern VALUE rb_cSockOpt; + +extern VALUE rb_eSocket; +extern VALUE rb_eResolution; + +#ifdef SOCKS +extern VALUE rb_cSOCKSSocket; +# ifndef SOCKS5 +void SOCKSinit(char *); +int Rconnect(int, const struct sockaddr *, socklen_t); +# endif +#endif + +#include "constdefs.h" + +#define SockAddrStringValue(v) rsock_sockaddr_string_value(&(v)) +#define SockAddrStringValuePtr(v) rsock_sockaddr_string_value_ptr(&(v)) +#define SockAddrStringValueWithAddrinfo(v, rai_ret) rsock_sockaddr_string_value_with_addrinfo(&(v), &(rai_ret)) +VALUE rsock_sockaddr_string_value(volatile VALUE *); +char *rsock_sockaddr_string_value_ptr(volatile VALUE *); +VALUE rsock_sockaddr_string_value_with_addrinfo(volatile VALUE *v, VALUE *ai_ret); + +VALUE rb_check_sockaddr_string_type(VALUE); + +NORETURN(void rsock_raise_resolution_error(const char *, int)); + +int rsock_family_arg(VALUE domain); +int rsock_socktype_arg(VALUE type); +int rsock_level_arg(int family, VALUE level); +int rsock_optname_arg(int family, int level, VALUE optname); +int rsock_cmsg_type_arg(int family, int level, VALUE type); +int rsock_shutdown_how_arg(VALUE how); + +int rsock_getfamily(rb_io_t *fptr); + +struct rb_addrinfo { + struct addrinfo *ai; + int allocated_by_malloc; +}; +void rb_freeaddrinfo(struct rb_addrinfo *ai); +VALUE rsock_freeaddrinfo(VALUE arg); +int rb_getnameinfo(const struct sockaddr *sa, socklen_t salen, char *host, size_t hostlen, char *serv, size_t servlen, int flags); +int rsock_fd_family(int fd); +struct rb_addrinfo *rsock_addrinfo(VALUE host, VALUE port, int family, int socktype, int flags, VALUE timeout); +struct rb_addrinfo *rsock_getaddrinfo(VALUE host, VALUE port, struct addrinfo *hints, int socktype_hack, VALUE timeout); + +VALUE rsock_fd_socket_addrinfo(int fd, struct sockaddr *addr, socklen_t len); +VALUE rsock_io_socket_addrinfo(VALUE io, struct sockaddr *addr, socklen_t len); + +VALUE rsock_addrinfo_new(struct sockaddr *addr, socklen_t len, int family, int socktype, int protocol, VALUE canonname, VALUE inspectname); +VALUE rsock_addrinfo_inspect_sockaddr(VALUE rai); + +VALUE rsock_make_ipaddr(struct sockaddr *addr, socklen_t addrlen); +VALUE rsock_ipaddr(struct sockaddr *sockaddr, socklen_t sockaddrlen, int norevlookup); +VALUE rsock_make_hostent(VALUE host, struct rb_addrinfo *addr, VALUE (*ipaddr)(struct sockaddr *, socklen_t)); +VALUE rsock_inspect_sockaddr(struct sockaddr *addr, socklen_t socklen, VALUE ret); +socklen_t rsock_sockaddr_len(struct sockaddr *addr); +VALUE rsock_sockaddr_obj(struct sockaddr *addr, socklen_t len); + +int rsock_revlookup_flag(VALUE revlookup, int *norevlookup); + +#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN +VALUE rsock_unixpath_str(struct sockaddr_un *sockaddr, socklen_t len); +VALUE rsock_unixaddr(struct sockaddr_un *sockaddr, socklen_t len); +socklen_t rsock_unix_sockaddr_len(VALUE path); +#endif + +int rsock_socket(int domain, int type, int proto); +int rsock_detect_cloexec(int fd); +VALUE rsock_init_sock(VALUE sock, int fd); +VALUE rsock_sock_s_socketpair(int argc, VALUE *argv, VALUE klass); +VALUE rsock_init_inetsock(VALUE sock, VALUE remote_host, VALUE remote_serv, VALUE local_host, VALUE local_serv, int type, VALUE resolv_timeout, VALUE connect_timeout, VALUE open_timeout, VALUE fast_fallback, VALUE test_mode_settings); +VALUE rsock_init_unixsock(VALUE sock, VALUE path, int server); + +struct rsock_send_arg { + int fd, flags; + VALUE mesg; + struct sockaddr *to; + socklen_t tolen; +}; + +VALUE rsock_sendto_blocking(void *data); +VALUE rsock_send_blocking(void *data); +VALUE rsock_bsock_send(int argc, VALUE *argv, VALUE sock); + +enum sock_recv_type { + RECV_RECV, /* BasicSocket#recv(no from) */ + RECV_IP, /* IPSocket#recvfrom */ + RECV_UNIX, /* UNIXSocket#recvfrom */ + RECV_SOCKET /* Socket#recvfrom */ +}; + +VALUE rsock_s_recvfrom_nonblock(VALUE sock, VALUE len, VALUE flg, VALUE str, + VALUE ex, enum sock_recv_type from); +VALUE rsock_s_recvfrom(VALUE sock, int argc, VALUE *argv, enum sock_recv_type from); + +int rsock_connect(VALUE self, const struct sockaddr *sockaddr, int len, int socks, VALUE timeout); + +VALUE rsock_s_accept(VALUE klass, VALUE io, struct sockaddr *sockaddr, socklen_t *len); +VALUE rsock_s_accept_nonblock(VALUE klass, VALUE ex, rb_io_t *fptr, + struct sockaddr *sockaddr, socklen_t *len); +VALUE rsock_sock_listen(VALUE sock, VALUE log); + +VALUE rsock_sockopt_new(int family, int level, int optname, VALUE data); + +#if defined(HAVE_SENDMSG) +VALUE rsock_bsock_sendmsg(VALUE sock, VALUE data, VALUE flags, + VALUE dest_sockaddr, VALUE controls); +VALUE rsock_bsock_sendmsg_nonblock(VALUE sock, VALUE data, VALUE flags, + VALUE dest_sockaddr, VALUE controls, VALUE ex); +#else +#define rsock_bsock_sendmsg rb_f_notimplement +#define rsock_bsock_sendmsg_nonblock rb_f_notimplement +#endif + +#if defined(HAVE_RECVMSG) +VALUE rsock_bsock_recvmsg(VALUE sock, VALUE dlen, VALUE clen, VALUE flags, + VALUE scm_rights); +VALUE rsock_bsock_recvmsg_nonblock(VALUE sock, VALUE dlen, VALUE clen, + VALUE flags, VALUE scm_rights, VALUE ex); +ssize_t rsock_recvmsg(int socket, struct msghdr *message, int flags); +#else +#define rsock_bsock_recvmsg rb_f_notimplement +#define rsock_bsock_recvmsg_nonblock rb_f_notimplement +#endif + +#ifdef HAVE_STRUCT_MSGHDR_MSG_CONTROL +void rsock_discard_cmsg_resource(struct msghdr *mh, int msg_peek_p); +#endif + +char *raddrinfo_host_str(VALUE host, char *hbuf, size_t hbuflen, int *flags_ptr); +char *raddrinfo_port_str(VALUE port, char *pbuf, size_t pbuflen, int *flags_ptr); + +#ifndef FAST_FALLBACK_INIT_INETSOCK_IMPL +# if !defined(HAVE_PTHREAD_CREATE) || !defined(HAVE_PTHREAD_DETACH) || defined(__MINGW32__) || defined(__MINGW64__) +# define FAST_FALLBACK_INIT_INETSOCK_IMPL 0 +# else +# include "ruby/thread_native.h" +# define FAST_FALLBACK_INIT_INETSOCK_IMPL 1 +# define IPV6_HOSTNAME_RESOLVED '1' +# define IPV4_HOSTNAME_RESOLVED '2' +# define SELECT_CANCELLED '3' + +struct fast_fallback_getaddrinfo_entry +{ + int family, err, refcount; + struct addrinfo hints; + struct addrinfo *ai; + struct fast_fallback_getaddrinfo_shared *shared; + int has_syserr; + long test_sleep_ms; + int test_ecode; +}; + +struct fast_fallback_getaddrinfo_shared +{ + int notify, refcount; + char *node, *service; + rb_nativethread_lock_t lock; + struct fast_fallback_getaddrinfo_entry getaddrinfo_entries[FLEX_ARY_LEN]; +}; + +int raddrinfo_pthread_create(pthread_t *th, void *(*start_routine) (void *), void *arg); +void *fork_safe_do_fast_fallback_getaddrinfo(void *ptr); +void free_fast_fallback_getaddrinfo_entry(struct fast_fallback_getaddrinfo_entry **entry); +void free_fast_fallback_getaddrinfo_shared(struct fast_fallback_getaddrinfo_shared **shared); +# endif +#endif + +NORETURN(void rsock_raise_user_specified_timeout(struct addrinfo *ai, VALUE host, VALUE port)); + +void rsock_init_basicsocket(void); +void rsock_init_ipsocket(void); +void rsock_init_tcpsocket(void); +void rsock_init_tcpserver(void); +void rsock_init_sockssocket(void); +void rsock_init_udpsocket(void); +void rsock_init_unixsocket(void); +void rsock_init_unixserver(void); +void rsock_init_socket_constants(void); +void rsock_init_ancdata(void); +void rsock_init_addrinfo(void); +void rsock_init_sockopt(void); +void rsock_init_sockifaddr(void); +void rsock_init_socket_init(void); + +NORETURN(void rsock_syserr_fail_host_port(int err, const char *, VALUE, VALUE)); +NORETURN(void rsock_syserr_fail_path(int err, const char *, VALUE)); +NORETURN(void rsock_syserr_fail_sockaddr(int err, const char *mesg, struct sockaddr *addr, socklen_t len)); +NORETURN(void rsock_syserr_fail_raddrinfo(int err, const char *mesg, VALUE rai)); +NORETURN(void rsock_syserr_fail_raddrinfo_or_sockaddr(int err, const char *mesg, VALUE addr, VALUE rai)); + +NORETURN(void rsock_sys_fail_host_port(const char *, VALUE, VALUE)); +NORETURN(void rsock_sys_fail_path(const char *, VALUE)); +NORETURN(void rsock_sys_fail_sockaddr(const char *, struct sockaddr *addr, socklen_t len)); +NORETURN(void rsock_sys_fail_raddrinfo(const char *, VALUE rai)); +NORETURN(void rsock_sys_fail_raddrinfo_or_sockaddr(const char *, VALUE addr, VALUE rai)); + +#if defined(__MINGW32__) || defined(_WIN32) +#define RSOCK_WAIT_BEFORE_BLOCKING +#endif + +/* + * some OSes may support MSG_DONTWAIT inconsistently depending on socket + * type, we only expect Linux to support it consistently for all socket types. + */ +#if defined(MSG_DONTWAIT) && defined(__linux__) +# define MSG_DONTWAIT_RELIABLE 1 +#else +# define MSG_DONTWAIT_RELIABLE 0 +#endif + +VALUE rsock_read_nonblock(VALUE sock, VALUE length, VALUE buf, VALUE ex); +VALUE rsock_write_nonblock(VALUE sock, VALUE buf, VALUE ex); + +void rsock_make_fd_nonblock(int fd); + +int rsock_is_dgram(rb_io_t *fptr); + +extern ID tcp_fast_fallback; + +#if !defined HAVE_INET_NTOP && ! defined _WIN32 +const char *inet_ntop(int, const void *, char *, size_t); +#elif defined __MINGW32__ +# define inet_ntop(f,a,n,l) rb_w32_inet_ntop(f,a,n,l) +#endif + +#endif diff --git a/ext/socket/socket.c b/ext/socket/socket.c index 1dcc7160e9..a8e5ae8119 100644 --- a/ext/socket/socket.c +++ b/ext/socket/socket.c @@ -2,2444 +2,310 @@ socket.c - - $Author$ - $Date$ created at: Thu Mar 31 12:21:29 JST 1994 - Copyright (C) 1993-2001 Yukihiro Matsumoto + Copyright (C) 1993-2007 Yukihiro Matsumoto ************************************************/ -#include "ruby.h" -#include "rubyio.h" -#include "rubysig.h" -#include "util.h" -#include <stdio.h> -#include <sys/types.h> +#include "rubysocket.h" -#ifdef HAVE_UNISTD_H -#include <unistd.h> -#endif - -#ifdef HAVE_SYS_UIO_H -#include <sys/uio.h> -#endif - -#ifndef _WIN32 -#if defined(__BEOS__) -# include <net/socket.h> -#else -# include <sys/socket.h> -# define pseudo_AF_FTIP pseudo_AF_RTIP /* workaround for NetBSD and etc. */ -#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 +static VALUE sym_wait_writable; -#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 -#ifndef EWOULDBLOCK -#define EWOULDBLOCK EAGAIN -#endif -#ifndef HAVE_GETADDRINFO -# include "addrinfo.h" -#endif -#include "sockport.h" +static VALUE sock_s_unpack_sockaddr_in(VALUE, VALUE); -#if defined(__vms) -#include <tcp.h> -#endif +ID tcp_fast_fallback; -static int do_not_reverse_lookup = 0; - -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_eSocket; - -#ifdef SOCKS -VALUE rb_cSOCKSSocket; -#ifdef SOCKS5 -#include <socks.h> -#else -void SOCKSinit(); -int Rconnect(); -#endif -#endif - -#define INET_CLIENT 0 -#define INET_SERVER 1 -#define INET_SOCKS 2 - -#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 - -#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(nodename, servname, hints, res) - char *nodename; - char *servname; - struct addrinfo *hints; - struct addrinfo **res; +void +rsock_sys_fail_host_port(const char *mesg, VALUE host, VALUE port) { - 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; + rsock_syserr_fail_host_port(errno, mesg, host, port); } -#define getaddrinfo(node,serv,hints,res) ruby_getaddrinfo((node),(serv),(hints),(res)) -#endif -#if defined(_AIX) -static int -ruby_getaddrinfo__aix(nodename, servname, hints, res) - 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(sa, salen, host, hostlen, serv, servlen, flags) - const struct sockaddr *sa; - size_t salen; - char *host; - size_t hostlen; - char *serv; - size_t servlen; - int flags; +void +rsock_syserr_fail_host_port(int err, const char *mesg, VALUE host, VALUE port) { - struct sockaddr_in6 *sa6; - u_int32_t *a6; + VALUE message; - if (sa->sa_family == AF_INET6) { - sa6 = (struct sockaddr_in6 *)sa; - a6 = sa6->sin6_addr.u6_addr.u6_addr32; + message = rb_sprintf("%s for %+"PRIsVALUE" port % "PRIsVALUE"", + mesg, host, port); - 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; + if (err == ETIMEDOUT) { + rb_exc_raise(rb_exc_new3(rb_eIOTimeoutError, message)); } - } - 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 HAVE_CLOSESOCKET -#undef close -#define close closesocket -#endif - -static VALUE -init_sock(sock, fd) - VALUE sock; - int fd; -{ - rb_io_t *fp; - MakeOpenFile(sock, fp); - fp->f = rb_fdopen(fd, "r"); - fp->f2 = rb_fdopen(fd, "w"); - fp->mode = FMODE_READWRITE; - rb_io_synchronized(fp); - - return sock; + rb_syserr_fail_str(err, message); } -static VALUE -bsock_s_for_fd(klass, fd) - VALUE klass, fd; +void +rsock_sys_fail_path(const char *mesg, VALUE path) { - rb_io_t *fptr; - VALUE sock = init_sock(rb_obj_alloc(klass), NUM2INT(fd)); - - GetOpenFile(sock, fptr); - - return sock; + rsock_syserr_fail_path(errno, mesg, path); } -static VALUE -bsock_shutdown(argc, argv, sock) - int argc; - VALUE *argv; - VALUE sock; +void +rsock_syserr_fail_path(int err, const char *mesg, VALUE path) { - VALUE howto; - int how; - rb_io_t *fptr; + VALUE message; - if (rb_safe_level() >= 4 && !OBJ_TAINTED(sock)) { - rb_raise(rb_eSecurityError, "Insecure: can't shutdown socket"); + if (RB_TYPE_P(path, T_STRING)) { + message = rb_sprintf("%s for % "PRIsVALUE"", mesg, path); + rb_syserr_fail_str(err, message); } - rb_scan_args(argc, argv, "01", &howto); - if (howto == Qnil) - how = 2; else { - how = NUM2INT(howto); - if (how < 0 || 2 < how) { - rb_raise(rb_eArgError, "`how' should be either 0, 1, 2"); - } + rb_syserr_fail(err, mesg); } - GetOpenFile(sock, fptr); - if (shutdown(fileno(fptr->f), how) == -1) - rb_sys_fail(0); - - return INT2FIX(0); -} - -static VALUE -bsock_close_read(sock) - VALUE sock; -{ - 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(fileno(fptr->f), 0); - if (!(fptr->mode & FMODE_WRITABLE)) { - return rb_io_close(sock); - } - fptr->mode &= ~FMODE_READABLE; - - return Qnil; } -static VALUE -bsock_close_write(sock) - VALUE sock; +void +rsock_sys_fail_sockaddr(const char *mesg, struct sockaddr *addr, socklen_t len) { - 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(fileno(fptr->f2), 1); - fptr->mode &= ~FMODE_WRITABLE; - - return Qnil; + rsock_syserr_fail_sockaddr(errno, mesg, addr, len); } -/* - * Document-method: setsockopt - * call-seq: setsockopt(level, optname, optval) - * - * Sets a socket option. These are protocol and system specific, see your - * local sytem documentation for details. - * - * === Parameters - * * +level+ is an integer, usually one of the SOL_ constants such as - * Socket::SOL_SOCKET, or a protocol level. - * * +optname+ is an integer, usually one of the SO_ constants, such - * as Socket::SO_REUSEADDR. - * * +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. - * - * === Examples - * - * Some socket options are integers with boolean values, in this case - * #setsockopt could be called like this: - * sock.setsockopt(Socket::SOL_SOCKET,Socket::SO_REUSEADDR, true) - * - * Some socket options are integers with numeric values, in this case - * #setsockopt could be called like this: - * sock.setsockopt(Socket::IPPROTO_IP, Socket::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") + Socket::INADDR_ANY - * sock.setsockopt(Socket::IPPROTO_IP, Socket::IP_ADD_MEMBERSHIP, optval) - * -*/ -static VALUE -bsock_setsockopt(sock, lev, optname, val) - VALUE sock, lev, optname, val; +void +rsock_syserr_fail_sockaddr(int err, const char *mesg, struct sockaddr *addr, socklen_t len) { - int level, option; - rb_io_t *fptr; - int i; - char *v; - int vlen; - - rb_secure(2); - level = NUM2INT(lev); - option = NUM2INT(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 = sizeof(i); - break; - default: - StringValue(val); - v = RSTRING(val)->ptr; - vlen = RSTRING(val)->len; - break; - } + VALUE rai; - GetOpenFile(sock, fptr); - if (setsockopt(fileno(fptr->f), level, option, v, vlen) < 0) - rb_sys_fail(fptr->path); + rai = rsock_addrinfo_new(addr, len, PF_UNSPEC, 0, 0, Qnil, Qnil); - return INT2FIX(0); + rsock_syserr_fail_raddrinfo(err, mesg, rai); } -/* - * Document-method: getsockopt - * call-seq: getsockopt(level, optname) - * - * Gets a socket option. These are protocol and system specific, see your - * local sytem documentation for details. The option is returned as - * a String with the data being the binary value of the socket option. - * - * === Parameters - * * +level+ is an integer, usually one of the SOL_ constants such as - * Socket::SOL_SOCKET, or a protocol level. - * * +optname+ is an integer, usually one of the SO_ constants, such - * as Socket::SO_REUSEADDR. - * - * === Examples - * - * Some socket options are integers with boolean values, in this case - * #getsockopt could be called like this: - * 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: - * 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: - * optval = sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_LINGER) - * onoff, linger = optval.unpack "ii" -*/ -static VALUE -bsock_getsockopt(sock, lev, optname) - VALUE sock, lev, optname; +void +rsock_sys_fail_raddrinfo(const char *mesg, VALUE rai) { -#if !defined(__BEOS__) - int level, option; - socklen_t len; - char *buf; - rb_io_t *fptr; - - level = NUM2INT(lev); - option = NUM2INT(optname); - len = 256; - buf = ALLOCA_N(char,len); - GetOpenFile(sock, fptr); - - GetOpenFile(sock, fptr); - if (getsockopt(fileno(fptr->f), level, option, buf, &len) < 0) - rb_sys_fail(fptr->path); - - return rb_str_new(buf, len); -#else - rb_notimplement(); -#endif + rsock_syserr_fail_raddrinfo(errno, mesg, rai); } -static VALUE -bsock_getsockname(sock) - VALUE sock; +void +rsock_syserr_fail_raddrinfo(int err, const char *mesg, VALUE rai) { - char buf[1024]; - socklen_t len = sizeof buf; - rb_io_t *fptr; + VALUE str, message; - GetOpenFile(sock, fptr); - if (getsockname(fileno(fptr->f), (struct sockaddr*)buf, &len) < 0) - rb_sys_fail("getsockname(2)"); - return rb_str_new(buf, len); -} + str = rsock_addrinfo_inspect_sockaddr(rai); + message = rb_sprintf("%s for %"PRIsVALUE"", mesg, str); -static VALUE -bsock_getpeername(sock) - VALUE sock; -{ - char buf[1024]; - socklen_t len = sizeof buf; - rb_io_t *fptr; - - GetOpenFile(sock, fptr); - if (getpeername(fileno(fptr->f), (struct sockaddr*)buf, &len) < 0) - rb_sys_fail("getpeername(2)"); - return rb_str_new(buf, len); + rb_syserr_fail_str(err, message); } -static VALUE -bsock_send(argc, argv, sock) - int argc; - VALUE *argv; - VALUE sock; +void +rsock_sys_fail_raddrinfo_or_sockaddr(const char *mesg, VALUE addr, VALUE rai) { - VALUE mesg, to; - VALUE flags; - rb_io_t *fptr; - FILE *f; - int fd, n; - - rb_secure(4); - rb_scan_args(argc, argv, "21", &mesg, &flags, &to); - - StringValue(mesg); - if (!NIL_P(to)) StringValue(to); - GetOpenFile(sock, fptr); - f = GetWriteFile(fptr); - fd = fileno(f); - rb_thread_fd_writable(fd); - retry: - if (!NIL_P(to)) { - TRAP_BEG; - n = sendto(fd, RSTRING(mesg)->ptr, RSTRING(mesg)->len, NUM2INT(flags), - (struct sockaddr*)RSTRING(to)->ptr, RSTRING(to)->len); - TRAP_END; - } - else { - TRAP_BEG; - n = send(fd, RSTRING(mesg)->ptr, RSTRING(mesg)->len, NUM2INT(flags)); - TRAP_END; - } - if (n < 0) { - if (rb_io_wait_writable(fd)) { - goto retry; - } - rb_sys_fail("send(2)"); - } - return INT2FIX(n); + rsock_syserr_fail_raddrinfo_or_sockaddr(errno, mesg, addr, rai); } -static VALUE ipaddr _((struct sockaddr*)); -#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(sock, argc, argv, from) - VALUE sock; - int argc; - VALUE *argv; - enum sock_recv_type from; -{ - rb_io_t *fptr; - VALUE str; - char buf[1024]; - socklen_t alen = 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_read_pending(fptr->f)) { - rb_raise(rb_eIOError, "recv for buffered IO"); - } - fd = fileno(fptr->f); - - str = rb_tainted_str_new(0, buflen); - - retry: - rb_str_locktmp(str); - rb_thread_wait_fd(fd); - TRAP_BEG; - slen = recvfrom(fd, RSTRING(str)->ptr, buflen, flags, (struct sockaddr*)buf, &alen); - TRAP_END; - rb_str_unlocktmp(str); - - if (slen < 0) { - if (rb_io_wait_readable(fd)) { - goto retry; - } - rb_sys_fail("recvfrom(2)"); - } - if (slen < RSTRING(str)->len) { - RSTRING(str)->len = slen; - RSTRING(str)->ptr[slen] = '\0'; - } - 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 from recvfrom for connection-oriented sockets */ - return rb_assoc_new(str, ipaddr((struct sockaddr*)buf)); - 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, rb_str_new(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) +void +rsock_syserr_fail_raddrinfo_or_sockaddr(int err, const char *mesg, VALUE addr, VALUE rai) { - rb_io_t *fptr; - VALUE str; - char buf[1024]; - socklen_t alen = 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_read_pending(fptr->f)) { - rb_raise(rb_eIOError, "recvfrom for buffered IO"); - } - fd = fileno(fptr->f); - - str = rb_tainted_str_new(0, buflen); + if (NIL_P(rai)) { + StringValue(addr); - rb_io_check_closed(fptr); - rb_io_set_nonblock(fptr); - slen = recvfrom(fd, RSTRING(str)->ptr, buflen, flags, (struct sockaddr*)buf, &alen); - - if (slen < 0) { - rb_sys_fail("recvfrom(2)"); - } - if (slen < RSTRING(str)->len) { - RSTRING(str)->len = slen; - RSTRING(str)->ptr[slen] = '\0'; + rsock_syserr_fail_sockaddr(err, mesg, + (struct sockaddr *)RSTRING_PTR(addr), + (socklen_t)RSTRING_LEN(addr)); /* overflow should be checked already */ } - 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); - break; - - case RECV_SOCKET: - addr = rb_str_new(buf, alen); - break; - - default: - rb_bug("s_recvfrom_nonblock called with bad value"); - } - return rb_assoc_new(str, addr); + else + rsock_syserr_fail_raddrinfo(err, mesg, rai); } -static VALUE -bsock_recv(argc, argv, sock) - int argc; - VALUE *argv; - VALUE sock; +static void +setup_domain_and_type(VALUE domain, int *dv, VALUE type, int *tv) { - return s_recvfrom(sock, argc, argv, RECV_RECV); + *dv = rsock_family_arg(domain); + *tv = rsock_socktype_arg(type); } /* * 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. + * Socket.new(domain, socktype [, protocol]) => socket * - * 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 - * IO.select([s]) - * p s.recv_nonblock(10) #=> "aaa" + * Creates a new socket object. * - * Refer to Socket#recvfrom for the exceptions that may be thrown if the call - * to _recv_nonblock_ fails. + * _domain_ should be a communications domain such as: :INET, :INET6, :UNIX, etc. * - * BasicSocket#recv_nonblock may raise any error corresponding to recvfrom(2) failure, - * including Errno::EAGAIN. + * _socktype_ should be a socket type such as: :STREAM, :DGRAM, :RAW, etc. * - * === See - * * Socket#recvfrom + * _protocol_ is optional and should be a protocol defined in the domain. + * If protocol is not given, 0 is used internally. + * + * Socket.new(:INET, :STREAM) # TCP socket + * Socket.new(:INET, :DGRAM) # UDP socket + * Socket.new(:UNIX, :STREAM) # UNIX stream socket + * Socket.new(:UNIX, :DGRAM) # UNIX datagram socket */ - static VALUE -bsock_recv_nonblock(argc, argv, sock) - int argc; - VALUE *argv; - VALUE sock; +sock_initialize(int argc, VALUE *argv, VALUE sock) { - return s_recvfrom_nonblock(sock, argc, argv, RECV_RECV); -} - -static VALUE -bsock_do_not_rev_lookup() -{ - return do_not_reverse_lookup?Qtrue:Qfalse; -} + VALUE domain, type, protocol; + int fd; + int d, t; -static VALUE -bsock_do_not_rev_lookup_set(self, val) - VALUE self, val; -{ - rb_secure(4); - do_not_reverse_lookup = RTEST(val); - return val; -} + rb_scan_args(argc, argv, "21", &domain, &type, &protocol); + if (NIL_P(protocol)) + protocol = INT2FIX(0); -static void -make_ipaddr0(addr, buf, len) - struct sockaddr *addr; - char *buf; - size_t len; -{ - int error; + setup_domain_and_type(domain, &d, type, &t); + fd = rsock_socket(d, t, NUM2INT(protocol)); + if (fd < 0) rb_sys_fail("socket(2)"); - error = getnameinfo(addr, SA_LEN(addr), buf, len, NULL, 0, NI_NUMERICHOST); - if (error) { - rb_raise(rb_eSocket, "getnameinfo: %s", gai_strerror(error)); - } + return rsock_init_sock(sock, fd); } +#if defined HAVE_SOCKETPAIR static VALUE -make_ipaddr(addr) - struct sockaddr *addr; -{ - char buf[1024]; - - make_ipaddr0(addr, buf, sizeof(buf)); - return rb_str_new2(buf); -} - -static void -make_inetaddr(host, buf, len) - 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_isnumber(p) - 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(host, hbuf, len) - VALUE host; - char *hbuf; - size_t len; -{ - if (NIL_P(host)) { - return NULL; - } - else if (rb_obj_is_kind_of(host, rb_cInteger)) { - long i = NUM2LONG(host); - - make_inetaddr(htonl(i), hbuf, len); - return hbuf; - } - else { - char *name; - - SafeStringValue(host); - name = RSTRING(host)->ptr; - if (!name || *name == 0 || (name[0] == '<' && strcmp(name, "<any>") == 0)) { - make_inetaddr(INADDR_ANY, hbuf, len); - } - else if (name[0] == '<' && strcmp(name, "<broadcast>") == 0) { - make_inetaddr(INADDR_BROADCAST, hbuf, len); - } - else if (strlen(name) >= len) { - rb_raise(rb_eArgError, "hostname too long (%d)", strlen(name)); - } - else { - strcpy(hbuf, name); - } - return hbuf; - } -} - -static char * -port_str(port, pbuf, len) - VALUE port; - char *pbuf; - size_t len; +io_call_close(VALUE io) { - if (NIL_P(port)) { - return 0; - } - else if (FIXNUM_P(port)) { - snprintf(pbuf, len, "%ld", FIX2LONG(port)); - return pbuf; - } - else { - char *serv; - - SafeStringValue(port); - serv = RSTRING(port)->ptr; - if (strlen(serv) >= len) { - rb_raise(rb_eArgError, "service name too long (%d)", strlen(serv)); - } - strcpy(pbuf, serv); - return pbuf; - } -} - -#ifndef NI_MAXHOST -# define 1025 -#endif -#ifndef NI_MAXSERV -# define 32 -#endif - -static struct addrinfo* -sock_addrinfo(host, port, socktype, flags) - VALUE host, port; - int socktype, flags; -{ - struct addrinfo hints; - struct addrinfo* res = NULL; - char *hostp, *portp; - int error; - char hbuf[NI_MAXHOST], pbuf[NI_MAXSERV]; - - hostp = host_str(host, hbuf, sizeof(hbuf)); - portp = port_str(port, pbuf, sizeof(pbuf)); - - if (socktype == 0 && flags == 0 && str_isnumber(portp)) { - socktype = SOCK_DGRAM; - } - - MEMZERO(&hints, struct addrinfo, 1); - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = socktype; - hints.ai_flags = 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"); - } - rb_raise(rb_eSocket, "getaddrinfo: %s", gai_strerror(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; + return rb_funcallv(io, rb_intern("close"), 0, 0); } static VALUE -ipaddr(sockaddr) - struct sockaddr *sockaddr; -{ - VALUE family, port, addr1, addr2; - VALUE ary; - int error; - char hbuf[1024], pbuf[1024]; - - switch (sockaddr->sa_family) { - case AF_UNSPEC: - family = rb_str_new2("AF_UNSPEC"); - break; - case AF_INET: - family = rb_str_new2("AF_INET"); - break; -#ifdef INET6 - case AF_INET6: - family = rb_str_new2("AF_INET6"); - break; -#endif -#ifdef AF_LOCAL - case AF_LOCAL: - family = rb_str_new2("AF_LOCAL"); - break; -#elif AF_UNIX - case AF_UNIX: - family = rb_str_new2("AF_UNIX"); - break; -#endif - default: - sprintf(pbuf, "unknown:%d", sockaddr->sa_family); - family = rb_str_new2(pbuf); - break; - } - addr1 = Qnil; - if (!do_not_reverse_lookup) { - 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) { - rb_raise(rb_eSocket, "getnameinfo: %s", gai_strerror(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(domain, type, proto) - int domain, type, proto; +io_close(VALUE io) { - int fd; - - fd = socket(domain, type, proto); - if (fd < 0) { - if (errno == EMFILE || errno == ENFILE) { - rb_gc(); - fd = socket(domain, type, proto); - } - } - return fd; + return rb_rescue(io_call_close, io, 0, 0); } -static int -wait_connectable(fd) - int fd; +static VALUE +pair_yield(VALUE pair) { - int sockerr; - socklen_t sockerrlen; - fd_set fds_w; - fd_set fds_e; - - for (;;) { - FD_ZERO(&fds_w); - FD_ZERO(&fds_e); - - FD_SET(fd, &fds_w); - FD_SET(fd, &fds_e); - - rb_thread_select(fd+1, 0, &fds_w, &fds_e, 0); - - if (FD_ISSET(fd, &fds_w)) { - return 0; - } - else if (FD_ISSET(fd, &fds_e)) { - sockerrlen = sizeof(sockerr); - if (getsockopt(fd, SOL_SOCKET, SO_ERROR, (void *)&sockerr, - &sockerrlen) == 0) { - if (sockerr == 0) - continue; /* workaround for winsock */ - errno = sockerr; - } - return -1; - } - } - - return 0; + return rb_ensure(rb_yield, pair, io_close, rb_ary_entry(pair, 1)); } - -#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 +#if defined HAVE_SOCKETPAIR static int -ruby_connect(fd, sockaddr, len, socks) - int fd; - struct sockaddr *sockaddr; - int len; - int socks; +rsock_socketpair0(int domain, int type, int protocol, int descriptors[2]) { - int status; - int mode; -#if WAIT_IN_PROGRESS > 0 - int wait_in_progress = -1; - int sockerr; - socklen_t sockerrlen; -#endif - -#if defined(HAVE_FCNTL) -# if defined(F_GETFL) - mode = fcntl(fd, F_GETFL, 0); -# else - mode = 0; -# endif - -#ifdef O_NDELAY -# define NONBLOCKING O_NDELAY -#else -#ifdef O_NBIO -# define NONBLOCKING O_NBIO -#else -# define NONBLOCKING O_NONBLOCK -#endif -#endif -#ifdef SOCKS5 - if (!socks) -#endif - fcntl(fd, F_SETFL, mode|NONBLOCKING); -#endif /* HAVE_FCNTL */ - - for (;;) { -#if defined(SOCKS) && !defined(SOCKS5) - if (socks) { - status = Rconnect(fd, sockaddr, len); - } - else -#endif - { - status = connect(fd, sockaddr, len); - } - if (status < 0) { - switch (errno) { - case EAGAIN: -#ifdef EINPROGRESS - case EINPROGRESS: -#endif -#if WAIT_IN_PROGRESS > 0 - sockerrlen = 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 = 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; +#ifdef SOCK_CLOEXEC + type |= SOCK_CLOEXEC; #endif -#ifdef EISCONN - case EISCONN: - status = 0; - errno = 0; - break; -#endif - default: - break; - } - } -#ifdef HAVE_FCNTL - fcntl(fd, F_SETFL, mode); +#ifdef SOCK_NONBLOCK + type |= SOCK_NONBLOCK; #endif - return status; - } -} - -struct inetsock_arg -{ - VALUE sock; - struct { - VALUE host, serv; - struct addrinfo *res; - } remote, local; - int type; - int fd; -}; - -static VALUE -inetsock_cleanup(arg) - 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(arg) - struct inetsock_arg *arg; -{ - int type = arg->type; - struct addrinfo *res; - int fd, status = 0; - char *syscall; + int result = socketpair(domain, type, protocol, descriptors); - 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 (result == -1) + return -1; - 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) { - 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)); +#ifndef SOCK_CLOEXEC + rb_fd_fix_cloexec(descriptors[0]); + rb_fd_fix_cloexec(descriptors[1]); #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) - listen(fd, 5); - - /* create new instance */ - return init_sock(arg->sock, fd); -} - -static VALUE -init_inetsock(sock, remote_host, remote_serv, local_host, local_serv, type) - VALUE sock, remote_host, remote_serv, local_host, 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(argc, argv, sock) - int argc; - VALUE *argv; - VALUE sock; -{ - 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); -} - -#ifdef SOCKS -static VALUE -socks_init(sock, host, serv) - VALUE sock, host, serv; -{ - static init = 0; - - if (init == 0) { - SOCKSinit("ruby"); - init = 1; - } - return init_inetsock(sock, host, serv, Qnil, Qnil, INET_SOCKS); -} - -#ifdef SOCKS5 -static VALUE -socks_s_close(sock) - VALUE sock; -{ - 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(fileno(fptr->f), 2); - shutdown(fileno(fptr->f2), 2); - return rb_io_close(sock); -} -#endif +#ifndef SOCK_NONBLOCK + rsock_make_fd_nonblock(descriptors[0]); + rsock_make_fd_nonblock(descriptors[1]); #endif -struct hostent_arg { - VALUE host; - struct addrinfo* addr; - VALUE (*ipaddr)_((struct sockaddr*, size_t)); -}; - -static VALUE -make_hostent_internal(arg) - 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)); - } - 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) { - /* Pushing all addresses regardless of address family is not the - * behaviour expected of gethostbyname(). All the addresses in struct - * hostent->h_addr_list must be of the same family. - */ - if(ai->ai_family == addr->ai_family) { - rb_ary_push(ary, (*ipaddr)(ai->ai_addr, ai->ai_addrlen)); - } - } - - return ary; + return result; } -static VALUE -make_hostent(host, addr, ipaddr) - 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); -} - -VALUE -tcp_sockaddr(addr, len) - struct sockaddr *addr; - size_t len; -{ - return make_ipaddr(addr); -} - -static VALUE -tcp_s_gethostbyname(obj, host) - VALUE obj, host; -{ - rb_secure(3); - return make_hostent(host, sock_addrinfo(host, Qnil, SOCK_STREAM, AI_CANONNAME), tcp_sockaddr); -} - -static VALUE -tcp_svr_init(argc, argv, sock) - int argc; - VALUE *argv; - VALUE sock; -{ - 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) +static int +rsock_socketpair(int domain, int type, int protocol, int descriptors[2]) { - int fd2; + int result; - rb_secure(3); - rb_io_set_nonblock(fptr); - fd2 = accept(fileno(fptr->f), (struct sockaddr*)sockaddr, len); - if (fd2 < 0) { - rb_sys_fail("accept(2)"); - } - make_fd_nonblock(fd2); - return init_sock(rb_obj_alloc(klass), fd2); -} + result = rsock_socketpair0(domain, type, protocol, descriptors); -static VALUE -s_accept(klass, fd, sockaddr, len) - 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 - TRAP_BEG; - fd2 = accept(fd, sockaddr, len); - TRAP_END; -#endif - if (fd2 < 0) { - switch (errno) { - case EMFILE: - case ENFILE: - if (retry) break; - rb_gc(); - retry = 1; - goto retry; - case EWOULDBLOCK: - break; - default: - if (!rb_io_wait_readable(fd)) break; - retry = 0; - goto retry; - } - rb_sys_fail(0); + if (result < 0 && rb_gc_for_fd(errno)) { + result = rsock_socketpair0(domain, type, protocol, descriptors); } - if (!klass) return INT2NUM(fd2); - return init_sock(rb_obj_alloc(klass), fd2); -} - -static VALUE -tcp_accept(sock) - VALUE sock; -{ - rb_io_t *fptr; - struct sockaddr_storage from; - socklen_t fromlen; - GetOpenFile(sock, fptr); - fromlen = sizeof(from); - return s_accept(rb_cTCPSocket, fileno(fptr->f), - (struct sockaddr*)&from, &fromlen); + return result; } /* * call-seq: - * tcpserver.accept_nonblock => tcpsocket - * - * Accepts an incoming connection using accept(2) after - * O_NONBLOCK is set for the underlying file descriptor. - * It returns an accepted TCPSocket for the incoming connection. - * - * === Example - * require 'socket' - * serv = TCPServer.new(2202) - * begin - * sock = serv.accept_nonblock - * rescue Errno::EAGAIN, Errno::EWOULDBLOCK, Errno::ECONNABORTED, Errno::EPROTO, Errno::EINTR - * IO.select([serv]) - * retry - * end - * # sock is an accepted socket. - * - * Refer to Socket#accept for the exceptions that may be thrown if the call - * to TCPServer#accept_nonblock fails. + * Socket.pair(domain, type, protocol) => [socket1, socket2] + * Socket.socketpair(domain, type, protocol) => [socket1, socket2] * - * TCPServer#accept_nonblock may raise any error corresponding to accept(2) failure, - * including Errno::EAGAIN. - * - * === See - * * TCPServer#accept - * * Socket#accept - */ -static VALUE -tcp_accept_nonblock(sock) - VALUE sock; -{ - rb_io_t *fptr; - struct sockaddr_storage from; - socklen_t fromlen; - - GetOpenFile(sock, fptr); - fromlen = sizeof(from); - return s_accept_nonblock(rb_cTCPSocket, fptr, - (struct sockaddr *)&from, &fromlen); -} - -static VALUE -tcp_sysaccept(sock) - VALUE sock; -{ - rb_io_t *fptr; - struct sockaddr_storage from; - socklen_t fromlen; - - GetOpenFile(sock, fptr); - fromlen = sizeof(from); - return s_accept(0, fileno(fptr->f), (struct sockaddr*)&from, &fromlen); -} - -#ifdef HAVE_SYS_UN_H -struct unixsock_arg { - struct sockaddr_un *sockaddr; - int fd; -}; - -static VALUE -unixsock_connect_internal(arg) - struct unixsock_arg *arg; -{ - return (VALUE)ruby_connect(arg->fd, arg->sockaddr, sizeof(*arg->sockaddr), - 0); -} - -static VALUE -init_unixsock(sock, path, server) - VALUE sock; - VALUE path; - int server; -{ - struct sockaddr_un sockaddr; - int fd, status; - rb_io_t *fptr; - - SafeStringValue(path); - fd = ruby_socket(AF_UNIX, SOCK_STREAM, 0); - if (fd < 0) { - rb_sys_fail("socket(2)"); - } - - MEMZERO(&sockaddr, struct sockaddr_un, 1); - sockaddr.sun_family = AF_UNIX; - if (sizeof(sockaddr.sun_path) <= RSTRING(path)->len) { - rb_raise(rb_eArgError, "too long unix socket path (max: %dbytes)", - (int)sizeof(sockaddr.sun_path)-1); - } - strcpy(sockaddr.sun_path, StringValueCStr(path)); - - if (server) { - status = bind(fd, (struct sockaddr*)&sockaddr, sizeof(sockaddr)); - } - else { - int prot; - struct unixsock_arg arg; - arg.sockaddr = &sockaddr; - arg.fd = fd; - status = rb_protect(unixsock_connect_internal, (VALUE)&arg, &prot); - if (prot) { - close(fd); - rb_jump_tag(prot); - } - } - - if (status < 0) { - close(fd); - rb_sys_fail(sockaddr.sun_path); - } - - if (server) listen(fd, 5); - - init_sock(sock, fd); - GetOpenFile(sock, fptr); - if (server) { - fptr->path = strdup(RSTRING(path)->ptr); - } - - return sock; -} -#endif - -static VALUE -ip_addr(sock) - VALUE sock; -{ - rb_io_t *fptr; - struct sockaddr_storage addr; - socklen_t len = sizeof addr; - - GetOpenFile(sock, fptr); - - if (getsockname(fileno(fptr->f), (struct sockaddr*)&addr, &len) < 0) - rb_sys_fail("getsockname(2)"); - return ipaddr((struct sockaddr*)&addr); -} - -static VALUE -ip_peeraddr(sock) - VALUE sock; -{ - rb_io_t *fptr; - struct sockaddr_storage addr; - socklen_t len = sizeof addr; - - GetOpenFile(sock, fptr); - - if (getpeername(fileno(fptr->f), (struct sockaddr*)&addr, &len) < 0) - rb_sys_fail("getpeername(2)"); - return ipaddr((struct sockaddr*)&addr); -} - -static VALUE -ip_recvfrom(argc, argv, sock) - int argc; - VALUE *argv; - VALUE sock; -{ - return s_recvfrom(sock, argc, argv, RECV_IP); -} - -static VALUE -ip_s_getaddress(obj, host) - VALUE obj, host; -{ - struct sockaddr_storage addr; - struct addrinfo *res = sock_addrinfo(host, Qnil, SOCK_STREAM, 0); - - /* just take the first one */ - memcpy(&addr, res->ai_addr, res->ai_addrlen); - freeaddrinfo(res); - - return make_ipaddr((struct sockaddr*)&addr); -} - -static VALUE -udp_init(argc, argv, sock) - int argc; - VALUE *argv; - VALUE sock; -{ - VALUE arg; - int socktype = AF_INET; - int fd; - - rb_secure(3); - if (rb_scan_args(argc, argv, "01", &arg) == 1) { - socktype = NUM2INT(arg); - } - fd = ruby_socket(socktype, SOCK_DGRAM, 0); - if (fd < 0) { - rb_sys_fail("socket(2) - udp"); - } - - return init_sock(sock, fd); -} - -struct udp_arg -{ - struct addrinfo *res; - int fd; -}; - -static VALUE -udp_connect_internal(arg) - struct udp_arg *arg; -{ - int fd = arg->fd; - struct addrinfo *res; - - for (res = arg->res; res; res = res->ai_next) { - if (ruby_connect(fd, res->ai_addr, res->ai_addrlen, 0) >= 0) { - return Qtrue; - } - } - return Qfalse; -} - -static VALUE -udp_connect(sock, host, port) - VALUE sock, host, port; -{ - rb_io_t *fptr; - struct udp_arg arg; - VALUE ret; - - rb_secure(3); - arg.res = sock_addrinfo(host, port, SOCK_DGRAM, 0); - GetOpenFile(sock, fptr); - arg.fd = fileno(fptr->f); - ret = rb_ensure(udp_connect_internal, (VALUE)&arg, - RUBY_METHOD_FUNC(freeaddrinfo), (VALUE)arg.res); - if (!ret) rb_sys_fail("connect(2)"); - return INT2FIX(0); -} - -static VALUE -udp_bind(sock, host, port) - VALUE sock, host, port; -{ - rb_io_t *fptr; - struct addrinfo *res0, *res; - - rb_secure(3); - res0 = sock_addrinfo(host, port, SOCK_DGRAM, 0); - GetOpenFile(sock, fptr); - for (res = res0; res; res = res->ai_next) { - if (bind(fileno(fptr->f), res->ai_addr, res->ai_addrlen) < 0) { - continue; - } - freeaddrinfo(res0); - return INT2FIX(0); - } - freeaddrinfo(res0); - rb_sys_fail("bind(2)"); - return INT2FIX(0); -} - -static VALUE -udp_send(argc, argv, sock) - int argc; - VALUE *argv; - VALUE sock; -{ - VALUE mesg, flags, host, port; - rb_io_t *fptr; - FILE *f; - int n; - struct addrinfo *res0, *res; - - if (argc == 2 || argc == 3) { - return bsock_send(argc, argv, sock); - } - rb_secure(4); - rb_scan_args(argc, argv, "4", &mesg, &flags, &host, &port); - - StringValue(mesg); - res0 = sock_addrinfo(host, port, SOCK_DGRAM, 0); - GetOpenFile(sock, fptr); - f = GetWriteFile(fptr); - for (res = res0; res; res = res->ai_next) { - retry: - n = sendto(fileno(f), RSTRING(mesg)->ptr, RSTRING(mesg)->len, NUM2INT(flags), - res->ai_addr, res->ai_addrlen); - if (n >= 0) { - freeaddrinfo(res0); - return INT2FIX(n); - } - if (rb_io_wait_writable(fileno(f))) { - goto retry; - } - } - freeaddrinfo(res0); - rb_sys_fail("sendto(2)"); - return INT2FIX(n); -} - -/* - * call-seq: - * udpsocket.recvfrom_nonblock(maxlen) => [mesg, sender_inet_addr] - * udpsocket.recvfrom_nonblock(maxlen, flags) => [mesg, sender_inet_addr] - * - * Receives up to _maxlen_ bytes from +udpsocket+ using recvfrom(2) after - * O_NONBLOCK is set for the underlying file descriptor. - * _flags_ is zero or more of the +MSG_+ options. - * The first element of the results, _mesg_, is the data received. - * The second element, _sender_inet_addr_, is an array to represent the sender address. + * Creates a pair of sockets connected each other. * - * When recvfrom(2) returns 0, - * Socket#recvfrom_nonblock returns an empty string as data. - * It means an empty packet. - * - * === Parameters - * * +maxlen+ - the number of bytes to receive from the socket - * * +flags+ - zero or more of the +MSG_+ options - * - * === Example - * require 'socket' - * s1 = UDPSocket.new - * s1.bind("127.0.0.1", 0) - * s2 = UDPSocket.new - * s2.bind("127.0.0.1", 0) - * s2.connect(*s1.addr.values_at(3,1)) - * s1.connect(*s2.addr.values_at(3,1)) - * s1.send "aaa", 0 - * IO.select([s2]) - * p s2.recvfrom_nonblock(10) #=> ["aaa", ["AF_INET", 33302, "localhost.localdomain", "127.0.0.1"]] + * _domain_ should be a communications domain such as: :INET, :INET6, :UNIX, etc. * - * Refer to Socket#recvfrom for the exceptions that may be thrown if the call - * to _recvfrom_nonblock_ fails. + * _socktype_ should be a socket type such as: :STREAM, :DGRAM, :RAW, etc. * - * UDPSocket#recvfrom_nonblock may raise any error corresponding to recvfrom(2) failure, - * including Errno::EAGAIN. + * _protocol_ should be a protocol defined in the domain, + * defaults to 0 for the domain. * - * === See - * * Socket#recvfrom - */ -static VALUE -udp_recvfrom_nonblock(int argc, VALUE *argv, VALUE sock) -{ - return s_recvfrom_nonblock(sock, argc, argv, RECV_IP); -} - -#ifdef HAVE_SYS_UN_H -static VALUE -unix_init(sock, path) - VALUE sock, path; -{ - return init_unixsock(sock, path, 0); -} - -static char * -unixpath(struct sockaddr_un *sockaddr, socklen_t len) -{ - if (sockaddr->sun_path < (char*)sockaddr + len) - return sockaddr->sun_path; - else - return ""; -} - -static VALUE -unix_path(sock) - VALUE sock; -{ - rb_io_t *fptr; - - GetOpenFile(sock, fptr); - if (fptr->path == 0) { - struct sockaddr_un addr; - socklen_t len = sizeof(addr); - if (getsockname(fileno(fptr->f), (struct sockaddr*)&addr, &len) < 0) - rb_sys_fail(0); - fptr->path = strdup(unixpath(&addr, len)); - } - return rb_str_new2(fptr->path); -} - -static VALUE -unix_svr_init(sock, path) - VALUE sock, path; -{ - return init_unixsock(sock, path, 1); -} - -static VALUE -unix_recvfrom(argc, argv, sock) - int argc; - VALUE *argv; - VALUE sock; -{ - return s_recvfrom(sock, argc, argv, RECV_UNIX); -} - -#if defined(HAVE_ST_MSG_CONTROL) && defined(SCM_RIGHTS) -#define FD_PASSING_BY_MSG_CONTROL 1 -#else -#define FD_PASSING_BY_MSG_CONTROL 0 -#endif - -#if defined(HAVE_ST_MSG_ACCRIGHTS) -#define FD_PASSING_BY_MSG_ACCRIGHTS 1 -#else -#define FD_PASSING_BY_MSG_ACCRIGHTS 0 -#endif - -static VALUE -unix_send_io(sock, val) - VALUE sock, val; -{ -#if defined(HAVE_SENDMSG) && (FD_PASSING_BY_MSG_CONTROL || FD_PASSING_BY_MSG_ACCRIGHTS) - int fd; - rb_io_t *fptr; - struct msghdr msg; - struct iovec vec[1]; - char buf[1]; - -#if FD_PASSING_BY_MSG_CONTROL - struct { - struct cmsghdr hdr; - char pad[8+sizeof(int)+8]; - } cmsg; -#endif - - if (rb_obj_is_kind_of(val, rb_cIO)) { - rb_io_t *valfptr; - GetOpenFile(val, valfptr); - fd = fileno(valfptr->f); - } - else if (FIXNUM_P(val)) { - fd = FIX2INT(val); - } - else { - rb_raise(rb_eTypeError, "neither IO nor file descriptor"); - } - - GetOpenFile(sock, fptr); - - msg.msg_name = NULL; - msg.msg_namelen = 0; - - /* Linux and Solaris doesn't work if msg_iov is NULL. */ - buf[0] = '\0'; - vec[0].iov_base = buf; - vec[0].iov_len = 1; - msg.msg_iov = vec; - msg.msg_iovlen = 1; - -#if FD_PASSING_BY_MSG_CONTROL - msg.msg_control = (caddr_t)&cmsg; - msg.msg_controllen = CMSG_LEN(sizeof(int)); - msg.msg_flags = 0; - MEMZERO((char*)&cmsg, char, sizeof(cmsg)); - cmsg.hdr.cmsg_len = CMSG_LEN(sizeof(int)); - cmsg.hdr.cmsg_level = SOL_SOCKET; - cmsg.hdr.cmsg_type = SCM_RIGHTS; - *(int *)CMSG_DATA(&cmsg.hdr) = fd; -#else - msg.msg_accrights = (caddr_t)&fd; - msg.msg_accrightslen = sizeof(fd); -#endif - - if (sendmsg(fileno(fptr->f), &msg, 0) == -1) - rb_sys_fail("sendmsg(2)"); - - return Qnil; -#else - rb_notimplement(); - return Qnil; /* not reached */ -#endif -} - -#if defined(HAVE_RECVMSG) && (FD_PASSING_BY_MSG_CONTROL || FD_PASSING_BY_MSG_ACCRIGHTS) -static void -thread_read_select(fd) - int fd; -{ - fd_set fds; - - FD_ZERO(&fds); - FD_SET(fd, &fds); - rb_thread_select(fd+1, &fds, 0, 0, 0); -} -#endif - -static VALUE -unix_recv_io(argc, argv, sock) - int argc; - VALUE *argv; - VALUE sock; -{ -#if defined(HAVE_RECVMSG) && (FD_PASSING_BY_MSG_CONTROL || FD_PASSING_BY_MSG_ACCRIGHTS) - VALUE klass, mode; - rb_io_t *fptr; - struct msghdr msg; - struct iovec vec[2]; - char buf[1]; - - int fd; -#if FD_PASSING_BY_MSG_CONTROL - struct { - struct cmsghdr hdr; - char pad[8+sizeof(int)+8]; - } cmsg; -#endif - - rb_scan_args(argc, argv, "02", &klass, &mode); - if (argc == 0) - klass = rb_cIO; - if (argc <= 1) - mode = Qnil; - - GetOpenFile(sock, fptr); - - thread_read_select(fileno(fptr->f)); - - msg.msg_name = NULL; - msg.msg_namelen = 0; - - vec[0].iov_base = buf; - vec[0].iov_len = sizeof(buf); - msg.msg_iov = vec; - msg.msg_iovlen = 1; - -#if FD_PASSING_BY_MSG_CONTROL - msg.msg_control = (caddr_t)&cmsg; - msg.msg_controllen = CMSG_SPACE(sizeof(int)); - msg.msg_flags = 0; - cmsg.hdr.cmsg_len = CMSG_LEN(sizeof(int)); - cmsg.hdr.cmsg_level = SOL_SOCKET; - cmsg.hdr.cmsg_type = SCM_RIGHTS; - *(int *)CMSG_DATA(&cmsg.hdr) = -1; -#else - msg.msg_accrights = (caddr_t)&fd; - msg.msg_accrightslen = sizeof(fd); - fd = -1; -#endif - - if (recvmsg(fileno(fptr->f), &msg, 0) == -1) - rb_sys_fail("recvmsg(2)"); - -#if FD_PASSING_BY_MSG_CONTROL - if (msg.msg_controllen != CMSG_SPACE(sizeof(int))) { - rb_raise(rb_eSocket, - "file descriptor was not passed (msg_controllen=%d, %d expected)", - msg.msg_controllen, CMSG_SPACE(sizeof(int))); - } - if (cmsg.hdr.cmsg_len != CMSG_LEN(sizeof(int))) { - rb_raise(rb_eSocket, - "file descriptor was not passed (cmsg_len=%d, %d expected)", - cmsg.hdr.cmsg_len, CMSG_LEN(sizeof(int))); - } - if (cmsg.hdr.cmsg_level != SOL_SOCKET) { - rb_raise(rb_eSocket, - "file descriptor was not passed (cmsg_level=%d, %d expected)", - cmsg.hdr.cmsg_level, SOL_SOCKET); - } - if (cmsg.hdr.cmsg_type != SCM_RIGHTS) { - rb_raise(rb_eSocket, - "file descriptor was not passed (cmsg_type=%d, %d expected)", - cmsg.hdr.cmsg_type, SCM_RIGHTS); - } -#else - if (msg.msg_accrightslen != sizeof(fd)) { - rb_raise(rb_eSocket, - "file descriptor was not passed (accrightslen) : %d != %d", - msg.msg_accrightslen, sizeof(fd)); - } -#endif - -#if FD_PASSING_BY_MSG_CONTROL - fd = *(int *)CMSG_DATA(&cmsg.hdr); -#endif - - if (klass == Qnil) - return INT2FIX(fd); - else { - static ID for_fd = 0; - int ff_argc; - VALUE ff_argv[2]; - if (!for_fd) - for_fd = rb_intern("for_fd"); - ff_argc = mode == Qnil ? 1 : 2; - ff_argv[0] = INT2FIX(fd); - ff_argv[1] = mode; - return rb_funcall2(klass, for_fd, ff_argc, ff_argv); - } -#else - rb_notimplement(); - return Qnil; /* not reached */ -#endif -} - -static VALUE -unix_accept(sock) - VALUE sock; -{ - rb_io_t *fptr; - struct sockaddr_un from; - socklen_t fromlen; - - GetOpenFile(sock, fptr); - fromlen = sizeof(struct sockaddr_un); - return s_accept(rb_cUNIXSocket, fileno(fptr->f), - (struct sockaddr*)&from, &fromlen); -} - -/* - * call-seq: - * unixserver.accept_nonblock => unixsocket - * - * Accepts an incoming connection using accept(2) after - * O_NONBLOCK is set for the underlying file descriptor. - * It returns an accepted UNIXSocket for the incoming connection. - * - * === Example - * require 'socket' - * serv = UNIXServer.new("/tmp/sock") - * begin - * sock = serv.accept_nonblock - * rescue Errno::EAGAIN, Errno::EWOULDBLOCK, Errno::ECONNABORTED, Errno::EPROTO, Errno::EINTR - * IO.select([serv]) - * retry - * end - * # sock is an accepted socket. - * - * Refer to Socket#accept for the exceptions that may be thrown if the call - * to UNIXServer#accept_nonblock fails. + * s1, s2 = Socket.pair(:UNIX, :STREAM, 0) + * s1.send "a", 0 + * s1.send "b", 0 + * s1.close + * p s2.recv(10) #=> "ab" + * p s2.recv(10) #=> "" + * p s2.recv(10) #=> "" + * + * s1, s2 = Socket.pair(:UNIX, :DGRAM, 0) + * s1.send "a", 0 + * s1.send "b", 0 + * p s2.recv(10) #=> "a" + * p s2.recv(10) #=> "b" * - * UNIXServer#accept_nonblock may raise any error corresponding to accept(2) failure, - * including Errno::EAGAIN. - * - * === See - * * UNIXServer#accept - * * Socket#accept */ -static VALUE -unix_accept_nonblock(sock) - VALUE sock; -{ - rb_io_t *fptr; - struct sockaddr_un from; - socklen_t fromlen; - - GetOpenFile(sock, fptr); - fromlen = sizeof(from); - return s_accept_nonblock(rb_cUNIXSocket, fptr, - (struct sockaddr *)&from, &fromlen); -} - -static VALUE -unix_sysaccept(sock) - VALUE sock; -{ - rb_io_t *fptr; - struct sockaddr_un from; - socklen_t fromlen; - - GetOpenFile(sock, fptr); - fromlen = sizeof(struct sockaddr_un); - return s_accept(0, fileno(fptr->f), (struct sockaddr*)&from, &fromlen); -} - -static VALUE -unixaddr(sockaddr, len) - struct sockaddr_un *sockaddr; - socklen_t len; -{ - return rb_assoc_new(rb_str_new2("AF_UNIX"), - rb_str_new2(unixpath(sockaddr, len))); -} - -static VALUE -unix_addr(sock) - VALUE sock; -{ - rb_io_t *fptr; - struct sockaddr_un addr; - socklen_t len = sizeof addr; - - GetOpenFile(sock, fptr); - - if (getsockname(fileno(fptr->f), (struct sockaddr*)&addr, &len) < 0) - rb_sys_fail("getsockname(2)"); - return unixaddr(&addr, len); -} - -static VALUE -unix_peeraddr(sock) - VALUE sock; -{ - rb_io_t *fptr; - struct sockaddr_un addr; - socklen_t len = sizeof addr; - - GetOpenFile(sock, fptr); - - if (getpeername(fileno(fptr->f), (struct sockaddr*)&addr, &len) < 0) - rb_sys_fail("getpeername(2)"); - return unixaddr(&addr, len); -} -#endif - -static void -setup_domain_and_type(domain, dv, type, tv) - VALUE domain, type; - int *dv, *tv; -{ - VALUE tmp; - char *ptr; - - tmp = rb_check_string_type(domain); - if (!NIL_P(tmp)) { - domain = tmp; - rb_check_safe_obj(domain); - ptr = RSTRING(domain)->ptr; - if (strcmp(ptr, "AF_INET") == 0) - *dv = AF_INET; -#ifdef AF_UNIX - else if (strcmp(ptr, "AF_UNIX") == 0) - *dv = AF_UNIX; -#endif -#ifdef AF_ISO - else if (strcmp(ptr, "AF_ISO") == 0) - *dv = AF_ISO; -#endif -#ifdef AF_NS - else if (strcmp(ptr, "AF_NS") == 0) - *dv = AF_NS; -#endif -#ifdef AF_IMPLINK - else if (strcmp(ptr, "AF_IMPLINK") == 0) - *dv = AF_IMPLINK; -#endif -#ifdef PF_INET - else if (strcmp(ptr, "PF_INET") == 0) - *dv = PF_INET; -#endif -#ifdef PF_UNIX - else if (strcmp(ptr, "PF_UNIX") == 0) - *dv = PF_UNIX; -#endif -#ifdef PF_IMPLINK - else if (strcmp(ptr, "PF_IMPLINK") == 0) - *dv = PF_IMPLINK; - else if (strcmp(ptr, "AF_IMPLINK") == 0) - *dv = AF_IMPLINK; -#endif -#ifdef PF_AX25 - else if (strcmp(ptr, "PF_AX25") == 0) - *dv = PF_AX25; -#endif -#ifdef PF_IPX - else if (strcmp(ptr, "PF_IPX") == 0) - *dv = PF_IPX; -#endif - else - rb_raise(rb_eSocket, "unknown socket domain %s", ptr); - } - else { - *dv = NUM2INT(domain); - } - tmp = rb_check_string_type(type); - if (!NIL_P(tmp)) { - type = tmp; - rb_check_safe_obj(type); - ptr = RSTRING(type)->ptr; - if (strcmp(ptr, "SOCK_STREAM") == 0) - *tv = SOCK_STREAM; - else if (strcmp(ptr, "SOCK_DGRAM") == 0) - *tv = SOCK_DGRAM; -#ifdef SOCK_RAW - else if (strcmp(ptr, "SOCK_RAW") == 0) - *tv = SOCK_RAW; -#endif -#ifdef SOCK_SEQPACKET - else if (strcmp(ptr, "SOCK_SEQPACKET") == 0) - *tv = SOCK_SEQPACKET; -#endif -#ifdef SOCK_RDM - else if (strcmp(ptr, "SOCK_RDM") == 0) - *tv = SOCK_RDM; -#endif -#ifdef SOCK_PACKET - else if (strcmp(ptr, "SOCK_PACKET") == 0) - *tv = SOCK_PACKET; -#endif - else - rb_raise(rb_eSocket, "unknown socket type %s", ptr); - } - else { - *tv = NUM2INT(type); - } -} - -static VALUE -sock_initialize(sock, domain, type, protocol) - VALUE sock, domain, type, protocol; -{ - int fd; - int d, t; - - rb_secure(3); - setup_domain_and_type(domain, &d, type, &t); - fd = ruby_socket(d, t, NUM2INT(protocol)); - if (fd < 0) rb_sys_fail("socket(2)"); - - return init_sock(sock, fd); -} - -static VALUE -sock_s_socketpair(klass, domain, type, protocol) - VALUE klass, domain, type, protocol; +VALUE +rsock_sock_s_socketpair(int argc, VALUE *argv, VALUE klass) { -#if defined HAVE_SOCKETPAIR + VALUE domain, type, protocol; int d, t, p, sp[2]; int ret; + VALUE s1, s2, r; + + rb_scan_args(argc, argv, "21", &domain, &type, &protocol); + if (NIL_P(protocol)) + protocol = INT2FIX(0); setup_domain_and_type(domain, &d, type, &t); p = NUM2INT(protocol); - ret = socketpair(d, t, p, sp); - if (ret < 0 && (errno == EMFILE || errno == ENFILE)) { - rb_gc(); - ret = socketpair(d, t, p, sp); - } + ret = rsock_socketpair(d, t, p, sp); if (ret < 0) { - rb_sys_fail("socketpair(2)"); + rb_sys_fail("socketpair(2)"); } - return rb_assoc_new(init_sock(rb_obj_alloc(klass), sp[0]), - init_sock(rb_obj_alloc(klass), sp[1])); -#else - rb_notimplement(); -#endif -} - -#ifdef HAVE_SYS_UN_H -static VALUE -unix_s_socketpair(argc, argv, klass) - int argc; - VALUE *argv; - VALUE klass; -{ - VALUE domain, type, protocol; - domain = INT2FIX(PF_UNIX); - - rb_scan_args(argc, argv, "02", &type, &protocol); - if (argc == 0) - type = INT2FIX(SOCK_STREAM); - if (argc <= 1) - protocol = INT2FIX(0); - - return sock_s_socketpair(klass, domain, type, protocol); + s1 = rsock_init_sock(rb_obj_alloc(klass), sp[0]); + s2 = rsock_init_sock(rb_obj_alloc(klass), sp[1]); + r = rb_assoc_new(s1, s2); + if (rb_block_given_p()) { + return rb_ensure(pair_yield, r, io_close, s1); + } + return r; } +#else +#define rsock_sock_s_socketpair rb_f_notimplement #endif /* * call-seq: - * socket.connect(server_sockaddr) => 0 - * - * Requests a connection to be made on the given +server_sockaddr+. Returns 0 if + * socket.connect(remote_sockaddr) => 0 + * + * Requests a connection to be made on the given +remote_sockaddr+. Returns 0 if * successful, otherwise an exception is raised. - * + * * === Parameter - * * +server_sockaddr+ - the +struct+ sockaddr contained in a string - * + * * +remote_sockaddr+ - the +struct+ sockaddr contained in a string or Addrinfo object + * * === Example: - * # Pull down Google's web page - * require 'socket' - * include Socket::Constants - * socket = Socket.new( AF_INET, SOCK_STREAM, 0 ) - * sockaddr = Socket.pack_sockaddr_in( 80, 'www.google.com' ) - * socket.connect( sockaddr ) - * socket.write( "GET / HTTP/1.0\r\n\r\n" ) - * results = socket.read - * + * # Pull down Google's web page + * require 'socket' + * include Socket::Constants + * socket = Socket.new( AF_INET, SOCK_STREAM, 0 ) + * sockaddr = Socket.pack_sockaddr_in( 80, 'www.google.com' ) + * socket.connect( sockaddr ) + * socket.write( "GET / HTTP/1.0\r\n\r\n" ) + * results = socket.read + * * === Unix-based Exceptions - * On unix-based systems the following system exceptions may be raised if + * On unix-based systems the following system exceptions may be raised if * the call to _connect_ fails: * * Errno::EACCES - search permission is denied for a component of the prefix - * path or write access to the +socket+ is denided + * path or write access to the +socket+ is denied * * Errno::EADDRINUSE - the _sockaddr_ is already in use * * Errno::EADDRNOTAVAIL - the specified _sockaddr_ is not available from the * local machine - * * Errno::EAFNOSUPPORT - the specified _sockaddr_ is not a valid address for + * * Errno::EAFNOSUPPORT - the specified _sockaddr_ is not a valid address for * the address family of the specified +socket+ * * Errno::EALREADY - a connection is already in progress for the specified * socket @@ -2448,56 +314,56 @@ unix_s_socketpair(argc, argv, klass) * refused the connection request * * Errno::ECONNRESET - the remote host reset the connection request * * Errno::EFAULT - the _sockaddr_ cannot be accessed - * * Errno::EHOSTUNREACH - the destination host cannot be reached (probably + * * Errno::EHOSTUNREACH - the destination host cannot be reached (probably * because the host is down or a remote router cannot reach it) * * Errno::EINPROGRESS - the O_NONBLOCK is set for the +socket+ and the - * connection cnanot be immediately established; the connection will be + * connection cannot be immediately established; the connection will be * established asynchronously * * Errno::EINTR - the attempt to establish the connection was interrupted by * delivery of a signal that was caught; the connection will be established * asynchronously * * Errno::EISCONN - the specified +socket+ is already connected * * Errno::EINVAL - the address length used for the _sockaddr_ is not a valid - * length for the address family or there is an invalid family in _sockaddr_ + * length for the address family or there is an invalid family in _sockaddr_ * * Errno::ENAMETOOLONG - the pathname resolved had a length which exceeded * PATH_MAX * * Errno::ENETDOWN - the local interface used to reach the destination is down * * Errno::ENETUNREACH - no route to the network is present * * Errno::ENOBUFS - no buffer space is available - * * Errno::ENOSR - there were insufficient STREAMS resources available to + * * Errno::ENOSR - there were insufficient STREAMS resources available to * complete the operation * * Errno::ENOTSOCK - the +socket+ argument does not refer to a socket * * Errno::EOPNOTSUPP - the calling +socket+ is listening and cannot be connected - * * Errno::EPROTOTYPE - the _sockaddr_ has a different type than the socket + * * Errno::EPROTOTYPE - the _sockaddr_ has a different type than the socket * bound to the specified peer address - * * Errno::ETIMEDOUT - the attempt to connect time out before a connection + * * Errno::ETIMEDOUT - the attempt to connect timed out before a connection * was made. - * + * * On unix-based systems if the address family of the calling +socket+ is * AF_UNIX the follow exceptions may be raised if the call to _connect_ * fails: - * * Errno::EIO - an i/o error occured while reading from or writing to the + * * Errno::EIO - an i/o error occurred while reading from or writing to the * file system * * Errno::ELOOP - too many symbolic links were encountered in translating * the pathname in _sockaddr_ - * * Errno::ENAMETOOLLONG - a component of a pathname exceeded NAME_MAX - * characters, or an entired pathname exceeded PATH_MAX characters + * * Errno::ENAMETOOLLONG - a component of a pathname exceeded NAME_MAX + * characters, or an entire pathname exceeded PATH_MAX characters * * Errno::ENOENT - a component of the pathname does not name an existing file * or the pathname is an empty string * * Errno::ENOTDIR - a component of the path prefix of the pathname in _sockaddr_ - * is not a directory - * + * is not a directory + * * === Windows Exceptions - * On Windows systems the following system exceptions may be raised if + * On Windows systems the following system exceptions may be raised if * the call to _connect_ fails: * * Errno::ENETDOWN - the network is down * * Errno::EADDRINUSE - the socket's local address is already in use * * Errno::EINTR - the socket was cancelled * * Errno::EINPROGRESS - a blocking socket is in progress or the service provider - * is still processing a callback function. Or a nonblocking connect call is + * is still processing a callback function. Or a nonblocking connect call is * in progress on the +socket+. * * Errno::EALREADY - see Errno::EINVAL - * * Errno::EADDRNOTAVAIL - the remote address is not a valid address, such as + * * Errno::EADDRNOTAVAIL - the remote address is not a valid address, such as * ADDR_ANY TODO check ADDRANY TO INADDR_ANY * * Errno::EAFNOSUPPORT - addresses in the specified family cannot be used with * with this +socket+ @@ -2511,87 +377,61 @@ unix_s_socketpair(argc, argv, klass) * * Errno::EHOSTUNREACH - no route to the network is present * * Errno::ENOBUFS - no buffer space is available * * Errno::ENOTSOCK - the +socket+ argument does not refer to a socket - * * Errno::ETIMEDOUT - the attempt to connect time out before a connection + * * Errno::ETIMEDOUT - the attempt to connect timed out before a connection * was made. - * * Errno::EWOULDBLOCK - the socket is marked as nonblocking and the + * * Errno::EWOULDBLOCK - the socket is marked as nonblocking and the * connection cannot be completed immediately - * * Errno::EACCES - the attempt to connect the datagram socket to the + * * Errno::EACCES - the attempt to connect the datagram socket to the * broadcast address failed - * + * * === See * * connect manual pages on unix-based systems * * connect function in Microsoft's Winsock functions reference */ static VALUE -sock_connect(sock, addr) - VALUE sock, addr; +sock_connect(VALUE self, VALUE addr) { - rb_io_t *fptr; - int fd; + VALUE rai; - StringValue(addr); + SockAddrStringValueWithAddrinfo(addr, rai); addr = rb_str_new4(addr); - GetOpenFile(sock, fptr); - fd = fileno(fptr->f); - if (ruby_connect(fd, (struct sockaddr*)RSTRING(addr)->ptr, RSTRING(addr)->len, 0) < 0) { - rb_sys_fail("connect(2)"); + + int result = rsock_connect(self, (struct sockaddr*)RSTRING_PTR(addr), RSTRING_SOCKLEN(addr), 0, RUBY_IO_TIMEOUT_DEFAULT); + + if (result < 0) { + rsock_sys_fail_raddrinfo_or_sockaddr("connect(2)", addr, rai); } - return INT2FIX(0); + return INT2FIX(result); } -/* - * call-seq: - * socket.connect_nonblock(server_sockaddr) => 0 - * - * Requests a connection to be made on the given +server_sockaddr+ after - * O_NONBLOCK is set for the underlying file descriptor. - * Returns 0 if successful, otherwise an exception is raised. - * - * === Parameter - * * +server_sockaddr+ - the +struct+ sockaddr contained in a string - * - * === Example: - * # Pull down Google's web page - * require 'socket' - * include Socket::Constants - * socket = Socket.new(AF_INET, SOCK_STREAM, 0) - * sockaddr = Socket.sockaddr_in(80, 'www.google.com') - * begin - * socket.connect_nonblock(sockaddr) - * rescue Errno::EINPROGRESS - * IO.select(nil, [socket]) - * begin - * socket.connect_nonblock(sockaddr) - * rescue Errno::EISCONN - * end - * end - * socket.write("GET / HTTP/1.0\r\n\r\n") - * results = socket.read - * - * Refer to Socket#connect for the exceptions that may be thrown if the call - * to _connect_nonblock_ fails. - * - * Socket#connect_nonblock may raise any error corresponding to connect(2) failure, - * including Errno::EINPROGRESS. - * - * === See - * * Socket#connect - */ +/* :nodoc: */ static VALUE -sock_connect_nonblock(sock, addr) - VALUE sock, addr; +sock_connect_nonblock(VALUE sock, VALUE addr, VALUE ex) { + VALUE rai; rb_io_t *fptr; int n; - StringValue(addr); + SockAddrStringValueWithAddrinfo(addr, rai); addr = rb_str_new4(addr); GetOpenFile(sock, fptr); rb_io_set_nonblock(fptr); - n = connect(fileno(fptr->f), (struct sockaddr*)RSTRING(addr)->ptr, RSTRING(addr)->len); + n = connect(fptr->fd, (struct sockaddr*)RSTRING_PTR(addr), RSTRING_SOCKLEN(addr)); if (n < 0) { - rb_sys_fail("connect(2)"); + int e = errno; + if (e == EINPROGRESS) { + if (ex == Qfalse) { + return sym_wait_writable; + } + rb_readwrite_syserr_fail(RB_IO_WAIT_WRITABLE, e, "connect(2) would block"); + } + if (e == EISCONN) { + if (ex == Qfalse) { + return INT2FIX(0); + } + } + rsock_syserr_fail_raddrinfo_or_sockaddr(e, "connect(2)", addr, rai); } return INT2FIX(n); @@ -2599,33 +439,40 @@ sock_connect_nonblock(sock, addr) /* * call-seq: - * socket.bind(server_sockaddr) => 0 - * - * Binds to the given +struct+ sockaddr. - * + * socket.bind(local_sockaddr) => 0 + * + * Binds to the given local address. + * * === Parameter - * * +server_sockaddr+ - the +struct+ sockaddr contained in a string + * * +local_sockaddr+ - the +struct+ sockaddr contained in a string or an Addrinfo object * * === Example - * require 'socket' - * include Socket::Constants - * socket = Socket.new( AF_INET, SOCK_STREAM, 0 ) - * sockaddr = Socket.pack_sockaddr_in( 2200, 'localhost' ) - * socket.bind( sockaddr ) - * + * require 'socket' + * + * # use Addrinfo + * socket = Socket.new(:INET, :STREAM, 0) + * socket.bind(Addrinfo.tcp("127.0.0.1", 2222)) + * p socket.local_address #=> #<Addrinfo: 127.0.0.1:2222 TCP> + * + * # use struct sockaddr + * include Socket::Constants + * socket = Socket.new( AF_INET, SOCK_STREAM, 0 ) + * sockaddr = Socket.pack_sockaddr_in( 2200, 'localhost' ) + * socket.bind( sockaddr ) + * * === Unix-based Exceptions - * On unix-based based systems the following system exceptions may be raised if + * On unix-based based systems the following system exceptions may be raised if * the call to _bind_ fails: * * Errno::EACCES - the specified _sockaddr_ is protected and the current * user does not have permission to bind to it * * Errno::EADDRINUSE - the specified _sockaddr_ is already in use * * Errno::EADDRNOTAVAIL - the specified _sockaddr_ is not available from the * local machine - * * Errno::EAFNOSUPPORT - the specified _sockaddr_ isnot a valid address for + * * Errno::EAFNOSUPPORT - the specified _sockaddr_ is not a valid address for * the family of the calling +socket+ * * Errno::EBADF - the _sockaddr_ specified is not a valid file descriptor * * Errno::EFAULT - the _sockaddr_ argument cannot be accessed - * * Errno::EINVAL - the +socket+ is already bound to an address, and the + * * Errno::EINVAL - the +socket+ is already bound to an address, and the * protocol does not support binding to the new _sockaddr_ or the +socket+ * has been shut down. * * Errno::EINVAL - the address length is not a valid length for the address @@ -2633,35 +480,35 @@ sock_connect_nonblock(sock, addr) * * Errno::ENAMETOOLONG - the pathname resolved had a length which exceeded * PATH_MAX * * Errno::ENOBUFS - no buffer space is available - * * Errno::ENOSR - there were insufficient STREAMS resources available to + * * Errno::ENOSR - there were insufficient STREAMS resources available to * complete the operation * * Errno::ENOTSOCK - the +socket+ does not refer to a socket - * * Errno::EOPNOTSUPP - the socket type of the +socket+ does not support + * * Errno::EOPNOTSUPP - the socket type of the +socket+ does not support * binding to an address - * + * * On unix-based based systems if the address family of the calling +socket+ is * Socket::AF_UNIX the follow exceptions may be raised if the call to _bind_ * fails: * * Errno::EACCES - search permission is denied for a component of the prefix - * path or write access to the +socket+ is denided + * path or write access to the +socket+ is denied * * Errno::EDESTADDRREQ - the _sockaddr_ argument is a null pointer * * Errno::EISDIR - same as Errno::EDESTADDRREQ * * Errno::EIO - an i/o error occurred * * Errno::ELOOP - too many symbolic links were encountered in translating * the pathname in _sockaddr_ - * * Errno::ENAMETOOLLONG - a component of a pathname exceeded NAME_MAX - * characters, or an entired pathname exceeded PATH_MAX characters + * * Errno::ENAMETOOLLONG - a component of a pathname exceeded NAME_MAX + * characters, or an entire pathname exceeded PATH_MAX characters * * Errno::ENOENT - a component of the pathname does not name an existing file * or the pathname is an empty string * * Errno::ENOTDIR - a component of the path prefix of the pathname in _sockaddr_ * is not a directory * * Errno::EROFS - the name would reside on a read only filesystem - * + * * === Windows Exceptions - * On Windows systems the following system exceptions may be raised if + * On Windows systems the following system exceptions may be raised if * the call to _bind_ fails: * * Errno::ENETDOWN-- the network is down - * * Errno::EACCES - the attempt to connect the datagram socket to the + * * Errno::EACCES - the attempt to connect the datagram socket to the * broadcast address failed * * Errno::EADDRINUSE - the socket's local address is already in use * * Errno::EADDRNOTAVAIL - the specified address is not a valid address for this @@ -2671,80 +518,80 @@ sock_connect_nonblock(sock, addr) * * Errno::EINVAL - the +socket+ is already bound to an address * * Errno::ENOBUFS - no buffer space is available * * Errno::ENOTSOCK - the +socket+ argument does not refer to a socket - * + * * === See * * bind manual pages on unix-based systems * * bind function in Microsoft's Winsock functions reference - */ + */ static VALUE -sock_bind(sock, addr) - VALUE sock, addr; +sock_bind(VALUE sock, VALUE addr) { + VALUE rai; rb_io_t *fptr; - StringValue(addr); + SockAddrStringValueWithAddrinfo(addr, rai); GetOpenFile(sock, fptr); - if (bind(fileno(fptr->f), (struct sockaddr*)RSTRING(addr)->ptr, RSTRING(addr)->len) < 0) - rb_sys_fail("bind(2)"); + if (bind(fptr->fd, (struct sockaddr*)RSTRING_PTR(addr), RSTRING_SOCKLEN(addr)) < 0) + rsock_sys_fail_raddrinfo_or_sockaddr("bind(2)", addr, rai); return INT2FIX(0); } /* * call-seq: - * socket.listen( int ) => 0 - * + * socket.listen( int ) => 0 + * * Listens for connections, using the specified +int+ as the backlog. A call - * to _listen_ only applies if the +socket+ is of type SOCK_STREAM or + * to _listen_ only applies if the +socket+ is of type SOCK_STREAM or * SOCK_SEQPACKET. - * + * * === Parameter * * +backlog+ - the maximum length of the queue for pending connections. - * + * * === Example 1 - * require 'socket' - * include Socket::Constants - * socket = Socket.new( AF_INET, SOCK_STREAM, 0 ) - * sockaddr = Socket.pack_sockaddr_in( 2200, 'localhost' ) - * socket.bind( sockaddr ) - * socket.listen( 5 ) - * - * === Example 2 (listening on an arbitary port, unix-based systems only): - * require 'socket' - * include Socket::Constants - * socket = Socket.new( AF_INET, SOCK_STREAM, 0 ) - * socket.listen( 1 ) - * + * require 'socket' + * include Socket::Constants + * socket = Socket.new( AF_INET, SOCK_STREAM, 0 ) + * sockaddr = Socket.pack_sockaddr_in( 2200, 'localhost' ) + * socket.bind( sockaddr ) + * socket.listen( 5 ) + * + * === Example 2 (listening on an arbitrary port, unix-based systems only): + * require 'socket' + * include Socket::Constants + * socket = Socket.new( AF_INET, SOCK_STREAM, 0 ) + * socket.listen( 1 ) + * * === Unix-based Exceptions * On unix based systems the above will work because a new +sockaddr+ struct * is created on the address ADDR_ANY, for an arbitrary port number as handed * off by the kernel. It will not work on Windows, because Windows requires that * the +socket+ is bound by calling _bind_ before it can _listen_. - * + * * If the _backlog_ amount exceeds the implementation-dependent maximum * queue length, the implementation's maximum queue length will be used. - * + * * On unix-based based systems the following system exceptions may be raised if the * call to _listen_ fails: * * Errno::EBADF - the _socket_ argument is not a valid file descriptor - * * Errno::EDESTADDRREQ - the _socket_ is not bound to a local address, and + * * Errno::EDESTADDRREQ - the _socket_ is not bound to a local address, and * the protocol does not support listening on an unbound socket * * Errno::EINVAL - the _socket_ is already connected * * Errno::ENOTSOCK - the _socket_ argument does not refer to a socket * * Errno::EOPNOTSUPP - the _socket_ protocol does not support listen - * * Errno::EACCES - the calling process does not have approriate privileges + * * Errno::EACCES - the calling process does not have appropriate privileges * * Errno::EINVAL - the _socket_ has been shut down - * * Errno::ENOBUFS - insufficient resources are available in the system to + * * Errno::ENOBUFS - insufficient resources are available in the system to * complete the call - * + * * === Windows Exceptions - * On Windows systems the following system exceptions may be raised if + * On Windows systems the following system exceptions may be raised if * the call to _listen_ fails: * * Errno::ENETDOWN - the network is down - * * Errno::EADDRINUSE - the socket's local address is already in use. This + * * Errno::EADDRINUSE - the socket's local address is already in use. This * usually occurs during the execution of _bind_ but could be delayed * if the call to _bind_ was to a partially wildcard address (involving - * ADDR_ANY) and if a specific address needs to be commmitted at the + * ADDR_ANY) and if a specific address needs to be committed at the * time of the call to _listen_ * * Errno::EINPROGRESS - a Windows Sockets 1.1 call is in progress or the * service provider is still processing a callback function @@ -2755,85 +602,83 @@ sock_bind(sock, addr) * * Errno::ENOTSOC - +socket+ is not a socket * * Errno::EOPNOTSUPP - the referenced +socket+ is not a type that supports * the _listen_ method - * + * * === See * * listen manual pages on unix-based systems * * listen function in Microsoft's Winsock functions reference */ -static VALUE -sock_listen(sock, log) - VALUE sock, log; +VALUE +rsock_sock_listen(VALUE sock, VALUE log) { rb_io_t *fptr; int backlog; - rb_secure(4); backlog = NUM2INT(log); GetOpenFile(sock, fptr); - if (listen(fileno(fptr->f), backlog) < 0) - rb_sys_fail("listen(2)"); + if (listen(fptr->fd, backlog) < 0) + rb_sys_fail("listen(2)"); return INT2FIX(0); } /* * call-seq: - * socket.recvfrom(maxlen) => [mesg, sender_sockaddr] - * socket.recvfrom(maxlen, flags) => [mesg, sender_sockaddr] - * + * socket.recvfrom(maxlen) => [mesg, sender_addrinfo] + * socket.recvfrom(maxlen, flags) => [mesg, sender_addrinfo] + * * Receives up to _maxlen_ bytes from +socket+. _flags_ is zero or more * of the +MSG_+ options. The first element of the results, _mesg_, is the data - * received. The second element, _sender_sockaddr_, contains protocol-specific information - * on the sender. - * + * received. The second element, _sender_addrinfo_, contains protocol-specific + * address information of the sender. + * * === Parameters - * * +maxlen+ - the number of bytes to receive from the socket - * * +flags+ - zero or more of the +MSG_+ options - * + * * +maxlen+ - the maximum number of bytes to receive from the socket + * * +flags+ - zero or more of the +MSG_+ options + * * === Example - * # In one file, start this first - * require 'socket' - * include Socket::Constants - * socket = Socket.new( AF_INET, SOCK_STREAM, 0 ) - * sockaddr = Socket.pack_sockaddr_in( 2200, 'localhost' ) - * socket.bind( sockaddr ) - * socket.listen( 5 ) - * client, client_sockaddr = socket.accept - * data = client.recvfrom( 20 )[0].chomp - * puts "I only received 20 bytes '#{data}'" - * sleep 1 - * socket.close - * - * # In another file, start this second - * require 'socket' - * include Socket::Constants - * socket = Socket.new( AF_INET, SOCK_STREAM, 0 ) - * sockaddr = Socket.pack_sockaddr_in( 2200, 'localhost' ) - * socket.connect( sockaddr ) - * socket.puts "Watch this get cut short!" - * socket.close - * + * # In one file, start this first + * require 'socket' + * include Socket::Constants + * socket = Socket.new( AF_INET, SOCK_STREAM, 0 ) + * sockaddr = Socket.pack_sockaddr_in( 2200, 'localhost' ) + * socket.bind( sockaddr ) + * socket.listen( 5 ) + * client, client_addrinfo = socket.accept + * data = client.recvfrom( 20 )[0].chomp + * puts "I only received 20 bytes '#{data}'" + * sleep 1 + * socket.close + * + * # In another file, start this second + * require 'socket' + * include Socket::Constants + * socket = Socket.new( AF_INET, SOCK_STREAM, 0 ) + * sockaddr = Socket.pack_sockaddr_in( 2200, 'localhost' ) + * socket.connect( sockaddr ) + * socket.puts "Watch this get cut short!" + * socket.close + * * === Unix-based Exceptions * On unix-based based systems the following system exceptions may be raised if the * call to _recvfrom_ fails: * * Errno::EAGAIN - the +socket+ file descriptor is marked as O_NONBLOCK and no * data is waiting to be received; or MSG_OOB is set and no out-of-band data - * is available and either the +socket+ file descriptor is marked as - * O_NONBLOCK or the +socket+ does not support blocking to wait for + * is available and either the +socket+ file descriptor is marked as + * O_NONBLOCK or the +socket+ does not support blocking to wait for * out-of-band-data * * Errno::EWOULDBLOCK - see Errno::EAGAIN * * Errno::EBADF - the +socket+ is not a valid file descriptor * * Errno::ECONNRESET - a connection was forcibly closed by a peer - * * Errno::EFAULT - the socket's internal buffer, address or address length + * * Errno::EFAULT - the socket's internal buffer, address or address length * cannot be accessed or written - * * Errno::EINTR - a signal interupted _recvfrom_ before any data was available + * * Errno::EINTR - a signal interrupted _recvfrom_ before any data was available * * Errno::EINVAL - the MSG_OOB flag is set and no out-of-band data is available - * * Errno::EIO - an i/o error occurred while reading from or writing to the + * * Errno::EIO - an i/o error occurred while reading from or writing to the * filesystem - * * Errno::ENOBUFS - insufficient resources were available in the system to + * * Errno::ENOBUFS - insufficient resources were available in the system to * perform the operation * * Errno::ENOMEM - insufficient memory was available to fulfill the request - * * Errno::ENOSR - there were insufficient STREAMS resources available to + * * Errno::ENOSR - there were insufficient STREAMS resources available to * complete the operation * * Errno::ENOTCONN - a receive is attempted on a connection-mode socket that * is not connected @@ -2841,345 +686,195 @@ sock_listen(sock, log) * * Errno::EOPNOTSUPP - the specified flags are not supported for this socket type * * Errno::ETIMEDOUT - the connection timed out during connection establishment * or due to a transmission timeout on an active connection - * + * * === Windows Exceptions - * On Windows systems the following system exceptions may be raised if + * On Windows systems the following system exceptions may be raised if * the call to _recvfrom_ fails: * * Errno::ENETDOWN - the network is down * * Errno::EFAULT - the internal buffer and from parameters on +socket+ are not * part of the user address space, or the internal fromlen parameter is - * too small to accomodate the peer address + * too small to accommodate the peer address * * Errno::EINTR - the (blocking) call was cancelled by an internal call to * the WinSock function WSACancelBlockingCall - * * Errno::EINPROGRESS - a blocking Windows Sockets 1.1 call is in progress or + * * Errno::EINPROGRESS - a blocking Windows Sockets 1.1 call is in progress or * the service provider is still processing a callback function * * Errno::EINVAL - +socket+ has not been bound with a call to _bind_, or an * unknown flag was specified, or MSG_OOB was specified for a socket with * SO_OOBINLINE enabled, or (for byte stream-style sockets only) the internal * len parameter on +socket+ was zero or negative * * Errno::EISCONN - +socket+ is already connected. The call to _recvfrom_ is - * not permitted with a connected socket on a socket that is connetion + * not permitted with a connected socket on a socket that is connection * oriented or connectionless. - * * Errno::ENETRESET - the connection has been broken due to the keep-alive + * * Errno::ENETRESET - the connection has been broken due to the keep-alive * activity detecting a failure while the operation was in progress. * * Errno::EOPNOTSUPP - MSG_OOB was specified, but +socket+ is not stream-style * such as type SOCK_STREAM. OOB data is not supported in the communication - * domain associated with +socket+, or +socket+ is unidirectional and + * domain associated with +socket+, or +socket+ is unidirectional and * supports only send operations - * * Errno::ESHUTDOWN - +socket+ has been shutdown. It is not possible to + * * Errno::ESHUTDOWN - +socket+ has been shutdown. It is not possible to * call _recvfrom_ on a socket after _shutdown_ has been invoked. - * * Errno::EWOULDBLOCK - +socket+ is marked as nonblocking and a call to + * * Errno::EWOULDBLOCK - +socket+ is marked as nonblocking and a call to * _recvfrom_ would block. * * Errno::EMSGSIZE - the message was too large to fit into the specified buffer * and was truncated. * * Errno::ETIMEDOUT - the connection has been dropped, because of a network * failure or because the system on the other end went down without * notice - * * Errno::ECONNRESET - the virtual circuit was reset by the remote side + * * Errno::ECONNRESET - the virtual circuit was reset by the remote side * executing a hard or abortive close. The application should close the * socket; it is no longer usable. On a UDP-datagram socket this error * indicates a previous send operation resulted in an ICMP Port Unreachable * message. */ static VALUE -sock_recvfrom(argc, argv, sock) - int argc; - VALUE *argv; - VALUE sock; +sock_recvfrom(int argc, VALUE *argv, VALUE sock) { - return s_recvfrom(sock, argc, argv, RECV_SOCKET); + return rsock_s_recvfrom(sock, argc, argv, RECV_SOCKET); } -/* - * call-seq: - * socket.recvfrom_nonblock(maxlen) => [mesg, sender_sockaddr] - * socket.recvfrom_nonblock(maxlen, flags) => [mesg, sender_sockaddr] - * - * 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 first element of the results, _mesg_, is the data received. - * The second element, _sender_sockaddr_, contains protocol-specific information - * on the sender. - * - * When recvfrom(2) returns 0, Socket#recvfrom_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 - * # In one file, start this first - * require 'socket' - * include Socket::Constants - * socket = Socket.new(AF_INET, SOCK_STREAM, 0) - * sockaddr = Socket.sockaddr_in(2200, 'localhost') - * socket.bind(sockaddr) - * socket.listen(5) - * client, client_sockaddr = socket.accept - * begin - * pair = client.recvfrom_nonblock(20) - * rescue Errno::EAGAIN, Errno::EWOULDBLOCK - * IO.select([client]) - * retry - * end - * data = pair[0].chomp - * puts "I only received 20 bytes '#{data}'" - * sleep 1 - * socket.close - * - * # In another file, start this second - * require 'socket' - * include Socket::Constants - * socket = Socket.new(AF_INET, SOCK_STREAM, 0) - * sockaddr = Socket.sockaddr_in(2200, 'localhost') - * socket.connect(sockaddr) - * socket.puts "Watch this get cut short!" - * socket.close - * - * Refer to Socket#recvfrom for the exceptions that may be thrown if the call - * to _recvfrom_nonblock_ fails. - * - * Socket#recvfrom_nonblock may raise any error corresponding to recvfrom(2) failure, - * including Errno::EAGAIN. - * - * === See - * * Socket#recvfrom - */ +/* :nodoc: */ static VALUE -sock_recvfrom_nonblock(int argc, VALUE *argv, VALUE sock) +sock_recvfrom_nonblock(VALUE sock, VALUE len, VALUE flg, VALUE str, VALUE ex) { - return s_recvfrom_nonblock(sock, argc, argv, RECV_SOCKET); + return rsock_s_recvfrom_nonblock(sock, len, flg, str, ex, RECV_SOCKET); } /* * call-seq: - * socket.accept => [ socket, string ] - * - * Accepts an incoming connection returning an array containing a new - * Socket object and a string holding the +struct+ sockaddr information about - * the caller. - * - * === Example - * # In one script, start this first - * require 'socket' - * include Socket::Constants - * socket = Socket.new( AF_INET, SOCK_STREAM, 0 ) - * sockaddr = Socket.pack_sockaddr_in( 2200, 'localhost' ) - * socket.bind( sockaddr ) - * socket.listen( 5 ) - * client, client_sockaddr = socket.accept - * puts "The client said, '#{client.readline.chomp}'" - * client.puts "Hello from script one!" - * socket.close - * - * # In another script, start this second - * require 'socket' - * include Socket::Constants - * socket = Socket.new( AF_INET, SOCK_STREAM, 0 ) - * sockaddr = Socket.pack_sockaddr_in( 2200, 'localhost' ) - * socket.connect( sockaddr ) - * socket.puts "Hello from script 2." - * puts "The server said, '#{socket.readline.chomp}'" - * socket.close - * - * === Unix-based Exceptions - * On unix-based based systems the following system exceptions may be raised if the - * call to _accept_ fails: - * * Errno::EAGAIN - O_NONBLOCK is set for the +socket+ file descriptor and no - * connections are parent to be accepted - * * Errno::EWOULDBLOCK - same as Errno::EAGAIN - * * Errno::EBADF - the +socket+ is not a valid file descriptor - * * Errno::ECONNABORTED - a connection has been aborted - * * Errno::EFAULT - the socket's internal address or address length parameter - * cannot be access or written - * * Errno::EINTR - the _accept_ method was interrupted by a signal that was - * caught before a valid connection arrived - * * Errno::EINVAL - the +socket+ is not accepting connections - * * Errno::EMFILE - OPEN_MAX file descriptors are currently open in the calling - * process - * * Errno::ENOBUFS - no buffer space is available - * * Errno::ENOMEM - there was insufficient memory available to complete the - * operation - * * Errno::ENOSR - there was insufficient STREAMS resources available to - * complete the operation - * * Errno::ENFILE - the maximum number of file descriptors in the system are - * already open - * * Errno::ENOTSOCK - the +socket+ does not refer to a socket - * * Errno::EOPNOTSUPP - the socket type for the calling +socket+ does not - * support accept connections - * * Errno::EPROTO - a protocol error has occurred - * - * === Windows Exceptions - * On Windows systems the following system exceptions may be raised if - * the call to _accept_ fails: - * * Errno::ECONNRESET - an incoming connection was indicated, but was - * terminated by the remote peer prior to accepting the connection - * * Errno::EFAULT - the socket's internal address or address length parameter - * is too small or is not a valid part of the user space address - * * Errno::EINVAL - the _listen_ method was not invoked prior to calling _accept_ - * * Errno::EINPROGRESS - a blocking Windows Sockets 1.1 call is in progress or - * the service provider is still processing a callback function - * * Errno::EMFILE - the queue is not empty, upong etry to _accept_ and there are - * no socket descriptors available - * * Errno::ENETDOWN - the network is down - * * Errno::ENOBUFS - no buffer space is available - * * Errno::ENOTSOCK - +socket+ is not a socket - * * Errno::EOPNOTSUPP - +socket+ is not a type that supports connection-oriented - * service. - * * Errno::EWOULDBLOCK - +socket+ is marked as nonblocking and no connections are - * present to be accepted - * - * === See - * * accept manual pages on unix-based systems - * * accept function in Microsoft's Winsock functions reference + * socket.accept => [client_socket, client_addrinfo] + * + * Accepts a next connection. + * Returns a new Socket object and Addrinfo object. + * + * serv = Socket.new(:INET, :STREAM, 0) + * serv.listen(5) + * c = Socket.new(:INET, :STREAM, 0) + * c.connect(serv.connect_address) + * p serv.accept #=> [#<Socket:fd 6>, #<Addrinfo: 127.0.0.1:48555 TCP>] + * */ static VALUE -sock_accept(sock) - VALUE sock; +sock_accept(VALUE server) { - rb_io_t *fptr; - VALUE sock2; - char buf[1024]; - socklen_t len = sizeof buf; + union_sockaddr buffer; + socklen_t length = (socklen_t)sizeof(buffer); - GetOpenFile(sock, fptr); - sock2 = s_accept(rb_cSocket,fileno(fptr->f),(struct sockaddr*)buf,&len); + VALUE peer = rsock_s_accept(rb_cSocket, server, &buffer.addr, &length); - return rb_assoc_new(sock2, rb_str_new(buf, len)); + return rb_assoc_new(peer, rsock_io_socket_addrinfo(peer, &buffer.addr, length)); } -/* - * call-seq: - * socket.accept_nonblock => [client_socket, client_sockaddr] - * - * Accepts an incoming connection using accept(2) after - * O_NONBLOCK is set for the underlying file descriptor. - * It returns an array containg the accpeted socket - * for the incoming connection, _client_socket_, - * and a string that contains the +struct+ sockaddr information - * about the caller, _client_sockaddr_. - * - * === Example - * # In one script, start this first - * require 'socket' - * include Socket::Constants - * socket = Socket.new(AF_INET, SOCK_STREAM, 0) - * sockaddr = Socket.sockaddr_in(2200, 'localhost') - * socket.bind(sockaddr) - * socket.listen(5) - * begin - * client_socket, client_sockaddr = socket.accept_nonblock - * rescue Errno::EAGAIN, Errno::EWOULDBLOCK, Errno::ECONNABORTED, Errno::EPROTO, Errno::EINTR - * IO.select([socket]) - * retry - * end - * puts "The client said, '#{client_socket.readline.chomp}'" - * client_socket.puts "Hello from script one!" - * socket.close - * - * # In another script, start this second - * require 'socket' - * include Socket::Constants - * socket = Socket.new(AF_INET, SOCK_STREAM, 0) - * sockaddr = Socket.sockaddr_in(2200, 'localhost') - * socket.connect(sockaddr) - * socket.puts "Hello from script 2." - * puts "The server said, '#{socket.readline.chomp}'" - * socket.close - * - * Refer to Socket#accept for the exceptions that may be thrown if the call - * to _accept_nonblock_ fails. - * - * Socket#accept_nonblock may raise any error corresponding to accept(2) failure, - * including Errno::EAGAIN. - * - * === See - * * Socket#accept - */ +/* :nodoc: */ static VALUE -sock_accept_nonblock(sock) - VALUE sock; +sock_accept_nonblock(VALUE sock, VALUE ex) { rb_io_t *fptr; VALUE sock2; - char buf[1024]; - socklen_t len = sizeof buf; + union_sockaddr buf; + struct sockaddr *addr = &buf.addr; + socklen_t len = (socklen_t)sizeof buf; GetOpenFile(sock, fptr); - sock2 = s_accept_nonblock(rb_cSocket, fptr, (struct sockaddr *)buf, &len); - return rb_assoc_new(sock2, rb_str_new(buf, len)); + sock2 = rsock_s_accept_nonblock(rb_cSocket, ex, fptr, addr, &len); + + if (SYMBOL_P(sock2)) /* :wait_readable */ + return sock2; + return rb_assoc_new(sock2, rsock_io_socket_addrinfo(sock2, &buf.addr, len)); } /* * call-seq: - * socket.sysaccept => [client_socket_fd, client_sockaddr] - * - * Accepts an incoming connection returnings an array containg the (integer) + * socket.sysaccept => [client_socket_fd, client_addrinfo] + * + * Accepts an incoming connection returning an array containing the (integer) * file descriptor for the incoming connection, _client_socket_fd_, - * and a string that contains the +struct+ sockaddr information - * about the caller, _client_sockaddr_. - * + * and an Addrinfo, _client_addrinfo_. + * * === Example - * # In one script, start this first - * require 'socket' - * include Socket::Constants - * socket = Socket.new( AF_INET, SOCK_STREAM, 0 ) - * sockaddr = Socket.pack_sockaddr_in( 2200, 'localhost' ) - * socket.bind( sockaddr ) - * socket.listen( 5 ) - * client_fd, client_sockaddr = socket.sysaccept - * client_socket = Socket.for_fd( client_fd ) - * puts "The client said, '#{client_socket.readline.chomp}'" - * client_socket.puts "Hello from script one!" - * socket.close - * - * # In another script, start this second - * require 'socket' - * include Socket::Constants - * socket = Socket.new( AF_INET, SOCK_STREAM, 0 ) - * sockaddr = Socket.pack_sockaddr_in( 2200, 'localhost' ) - * socket.connect( sockaddr ) - * socket.puts "Hello from script 2." - * puts "The server said, '#{socket.readline.chomp}'" - * socket.close - * + * # In one script, start this first + * require 'socket' + * include Socket::Constants + * socket = Socket.new( AF_INET, SOCK_STREAM, 0 ) + * sockaddr = Socket.pack_sockaddr_in( 2200, 'localhost' ) + * socket.bind( sockaddr ) + * socket.listen( 5 ) + * client_fd, client_addrinfo = socket.sysaccept + * client_socket = Socket.for_fd( client_fd ) + * puts "The client said, '#{client_socket.readline.chomp}'" + * client_socket.puts "Hello from script one!" + * socket.close + * + * # In another script, start this second + * require 'socket' + * include Socket::Constants + * socket = Socket.new( AF_INET, SOCK_STREAM, 0 ) + * sockaddr = Socket.pack_sockaddr_in( 2200, 'localhost' ) + * socket.connect( sockaddr ) + * socket.puts "Hello from script 2." + * puts "The server said, '#{socket.readline.chomp}'" + * socket.close + * * Refer to Socket#accept for the exceptions that may be thrown if the call - * to _sysaccept_ fails. - * + * to _sysaccept_ fails. + * * === See * * Socket#accept */ static VALUE -sock_sysaccept(sock) - VALUE sock; +sock_sysaccept(VALUE server) { - rb_io_t *fptr; - VALUE sock2; - char buf[1024]; - socklen_t len = sizeof buf; + union_sockaddr buffer; + socklen_t length = (socklen_t)sizeof(buffer); - GetOpenFile(sock, fptr); - sock2 = s_accept(0,fileno(fptr->f),(struct sockaddr*)buf,&len); + VALUE peer = rsock_s_accept(0, server, &buffer.addr, &length); - return rb_assoc_new(sock2, rb_str_new(buf, len)); + return rb_assoc_new(peer, rsock_io_socket_addrinfo(peer, &buffer.addr, length)); } #ifdef HAVE_GETHOSTNAME +/* + * call-seq: + * Socket.gethostname => hostname + * + * Returns the hostname. + * + * p Socket.gethostname #=> "hal" + * + * Note that it is not guaranteed to be able to convert to IP address using gethostbyname, getaddrinfo, etc. + * If you need local IP address, use Socket.ip_address_list. + */ static VALUE -sock_gethostname(obj) - VALUE obj; +sock_gethostname(VALUE obj) { - char buf[1024]; +#if defined(NI_MAXHOST) +# define RUBY_MAX_HOST_NAME_LEN NI_MAXHOST +#elif defined(HOST_NAME_MAX) +# define RUBY_MAX_HOST_NAME_LEN HOST_NAME_MAX +#else +# define RUBY_MAX_HOST_NAME_LEN 1024 +#endif - rb_secure(3); - if (gethostname(buf, (int)sizeof buf - 1) < 0) - rb_sys_fail("gethostname"); + long len = RUBY_MAX_HOST_NAME_LEN; + VALUE name; - buf[sizeof buf - 1] = '\0'; - return rb_str_new2(buf); + name = rb_str_new(0, len); + while (gethostname(RSTRING_PTR(name), len) < 0) { + int e = errno; + switch (e) { + case ENAMETOOLONG: +#ifdef __linux__ + case EINVAL: + /* glibc before version 2.1 uses EINVAL instead of ENAMETOOLONG */ +#endif + break; + default: + rb_syserr_fail(e, "gethostname(3)"); + } + rb_str_modify_expand(name, len); + len += len; + } + rb_str_resize(name, strlen(RSTRING_PTR(name))); + return name; } #else #ifdef HAVE_UNAME @@ -3187,205 +882,146 @@ sock_gethostname(obj) #include <sys/utsname.h> static VALUE -sock_gethostname(obj) - VALUE obj; +sock_gethostname(VALUE obj) { struct utsname un; - rb_secure(3); uname(&un); return rb_str_new2(un.nodename); } #else -static VALUE -sock_gethostname(obj) - VALUE obj; -{ - rb_notimplement(); -} +#define sock_gethostname rb_f_notimplement #endif #endif static VALUE -make_addrinfo(res0) - struct addrinfo *res0; +make_addrinfo(struct rb_addrinfo *res0, int norevlookup) { VALUE base, ary; struct addrinfo *res; if (res0 == NULL) { - rb_raise(rb_eSocket, "host not found"); + rb_raise(rb_eSocket, "host not found"); } base = rb_ary_new(); - for (res = res0; res; res = res->ai_next) { - ary = ipaddr(res->ai_addr); - rb_ary_push(ary, INT2FIX(res->ai_family)); - rb_ary_push(ary, INT2FIX(res->ai_socktype)); - rb_ary_push(ary, INT2FIX(res->ai_protocol)); - rb_ary_push(base, ary); + for (res = res0->ai; res; res = res->ai_next) { + ary = rsock_ipaddr(res->ai_addr, res->ai_addrlen, norevlookup); + if (res->ai_canonname) { + RARRAY_ASET(ary, 2, rb_str_new2(res->ai_canonname)); + } + rb_ary_push(ary, INT2FIX(res->ai_family)); + rb_ary_push(ary, INT2FIX(res->ai_socktype)); + rb_ary_push(ary, INT2FIX(res->ai_protocol)); + rb_ary_push(base, ary); } return base; } -/* Returns a String containing the binary value of a struct sockaddr. */ -VALUE -sock_sockaddr(addr, len) - struct sockaddr *addr; - size_t len; +static VALUE +sock_sockaddr(struct sockaddr *addr, socklen_t len) { char *ptr; switch (addr->sa_family) { case AF_INET: - ptr = (char*)&((struct sockaddr_in*)addr)->sin_addr.s_addr; - len = sizeof(((struct sockaddr_in*)addr)->sin_addr.s_addr); - break; -#ifdef INET6 + ptr = (char*)&((struct sockaddr_in*)addr)->sin_addr.s_addr; + len = (socklen_t)sizeof(((struct sockaddr_in*)addr)->sin_addr.s_addr); + break; +#ifdef AF_INET6 case AF_INET6: - ptr = (char*)&((struct sockaddr_in6*)addr)->sin6_addr.s6_addr; - len = sizeof(((struct sockaddr_in6*)addr)->sin6_addr.s6_addr); - break; + ptr = (char*)&((struct sockaddr_in6*)addr)->sin6_addr.s6_addr; + len = (socklen_t)sizeof(((struct sockaddr_in6*)addr)->sin6_addr.s6_addr); + break; #endif default: rb_raise(rb_eSocket, "unknown socket family:%d", addr->sa_family); - break; + break; } return rb_str_new(ptr, len); } /* - * Document-class: IPSocket + * call-seq: + * Socket.gethostbyname(hostname) => [official_hostname, alias_hostnames, address_family, *address_list] * - * IPSocket is the parent of TCPSocket and UDPSocket and implements - * functionality common to them. + * Use Addrinfo.getaddrinfo instead. + * This method is deprecated for the following reasons: * - * A number of APIs in IPSocket, Socket, and their descendants return an - * address as an array. The members of that array are: - * - address family: A string like "AF_INET" or "AF_INET6" if it is one of the - * commonly used families, the string "unknown:#" (where `#' is the address - * family number) if it is not one of the common ones. The strings map to - * the Socket::AF_* constants. - * - port: The port number. - * - name: Either the canonical name from looking the address up in the DNS, or - * the address in presentation format - * - address: The address in presentation format (a dotted decimal string for - * IPv4, a hex string for IPv6). + * - The 3rd element of the result is the address family of the first address. + * The address families of the rest of the addresses are not returned. + * - Uncommon address representation: + * 4/16-bytes binary string to represent IPv4/IPv6 address. + * - gethostbyname() may take a long time and it may block other threads. + * (GVL cannot be released since gethostbyname() is not thread safe.) + * - This method uses gethostbyname() function already removed from POSIX. * - * The address and port can be used directly to create sockets and to bind or - * connect them to the address. - */ - -/* - * Document-class: Socket + * This method obtains the host information for _hostname_. + * + * p Socket.gethostbyname("hal") #=> ["localhost", ["hal"], 2, "\x7F\x00\x00\x01"] * - * Socket contains a number of generally useful singleton methods and - * constants, as well as offering low-level interfaces that can be used to - * develop socket applications using protocols other than TCP, UDP, and UNIX - * domain sockets. */ +static VALUE +sock_s_gethostbyname(VALUE obj, VALUE host) +{ + rb_warn("Socket.gethostbyname is deprecated; use Addrinfo.getaddrinfo instead."); + struct rb_addrinfo *res = + rsock_addrinfo(host, Qnil, AF_UNSPEC, SOCK_STREAM, AI_CANONNAME, Qnil); + return rsock_make_hostent(host, res, sock_sockaddr); +} /* - * Document-method: gethostbyname - * call-seq: Socket.gethostbyname(host) => hostent - * - * Resolve +host+ and return name and address information for it, similarly to - * gethostbyname(3). +host+ can be a domain name or the presentation format of - * an address. - * - * Returns an array of information similar to that found in a +struct hostent+: - * - cannonical name: the cannonical name for host in the DNS, or a - * string representing the address - * - aliases: an array of aliases for the canonical name, there may be no aliases - * - address family: usually one of Socket::AF_INET or Socket::AF_INET6 - * - address: a string, the binary value of the +struct sockaddr+ for this name, in - * the indicated address family - * - ...: if there are multiple addresses for this host, a series of - * strings/+struct sockaddr+s may follow, not all necessarily in the same - * address family. Note that the fact that they may not be all in the same - * address family is a departure from the behaviour of gethostbyname(3). - * - * Note: I believe that the fact that the multiple addresses returned are not - * necessarily in the same address family may be a bug, since if this function - * actually called gethostbyname(3), ALL the addresses returned in the trailing - * address list (h_addr_list from struct hostent) would be of the same address - * family! Examples from my system, OS X 10.3: - * - * ["localhost", [], 30, "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\001", "\177\000\000\001"] - * and - * ["ensemble.local", [], 30, "\376\200\000\004\000\000\000\000\002\003\223\377\376\255\010\214", "\300\250{\232" ] - * - * Similar information can be returned by Socket.getaddrinfo if called as: - * - * Socket.getaddrinfo(+host+, 0, Socket::AF_UNSPEC, Socket::SOCK_STREAM, nil, Socket::AI_CANONNAME) + * call-seq: + * Socket.gethostbyaddr(address_string [, address_family]) => hostent * - * == Examples - * - * Socket.gethostbyname "example.com" - * => ["example.com", [], 2, "\300\000\"\246"] - * - * This name has no DNS aliases, and a single IPv4 address. - * - * Socket.gethostbyname "smtp.telus.net" - * => ["smtp.svc.telus.net", ["smtp.telus.net"], 2, "\307\271\334\371"] - * - * This name is an an alias so the canonical name is returned, as well as the - * alias and a single IPv4 address. - * - * Socket.gethostbyname "localhost" - * => ["localhost", [], 30, "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\001", "\177\000\000\001"] - * - * This machine has no aliases, returns an IPv6 address, and has an additional IPv4 address. + * Use Addrinfo#getnameinfo instead. + * This method is deprecated for the following reasons: * - * +host+ can also be an IP address in presentation format, in which case a - * reverse lookup is done on the address: + * - Uncommon address representation: + * 4/16-bytes binary string to represent IPv4/IPv6 address. + * - gethostbyaddr() may take a long time and it may block other threads. + * (GVL cannot be released since gethostbyname() is not thread safe.) + * - This method uses gethostbyname() function already removed from POSIX. * - * Socket.gethostbyname("127.0.0.1") - * => ["localhost", [], 2, "\177\000\000\001"] + * This method obtains the host information for _address_. * - * Socket.gethostbyname("192.0.34.166") - * => ["www.example.com", [], 2, "\300\000\"\246"] + * p Socket.gethostbyaddr([221,186,184,68].pack("CCCC")) + * #=> ["carbon.ruby-lang.org", [], 2, "\xDD\xBA\xB8D"] * + * p Socket.gethostbyaddr([127,0,0,1].pack("CCCC")) + * ["localhost", [], 2, "\x7F\x00\x00\x01"] + * p Socket.gethostbyaddr(([0]*15+[1]).pack("C"*16)) + * #=> ["localhost", ["ip6-localhost", "ip6-loopback"], 10, + * "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01"] * - * == See - * See: Socket.getaddrinfo */ static VALUE -sock_s_gethostbyname(obj, host) - VALUE obj, host; +sock_s_gethostbyaddr(int argc, VALUE *argv, VALUE _) { - rb_secure(3); - return make_hostent(host, sock_addrinfo(host, Qnil, SOCK_STREAM, AI_CANONNAME), sock_sockaddr); -} - -static VALUE -sock_s_gethostbyaddr(argc, argv) - int argc; - VALUE *argv; -{ - VALUE addr, type; + VALUE addr, family; struct hostent *h; - struct sockaddr *sa; char **pch; VALUE ary, names; int t = AF_INET; - rb_scan_args(argc, argv, "11", &addr, &type); - sa = (struct sockaddr*)StringValuePtr(addr); - if (!NIL_P(type)) { - t = NUM2INT(type); + rb_warn("Socket.gethostbyaddr is deprecated; use Addrinfo#getnameinfo instead."); + + rb_scan_args(argc, argv, "11", &addr, &family); + StringValue(addr); + if (!NIL_P(family)) { + t = rsock_family_arg(family); } -#ifdef INET6 - else if (RSTRING(addr)->len == 16) { - t = AF_INET6; +#ifdef AF_INET6 + else if (RSTRING_LEN(addr) == 16) { + t = AF_INET6; } #endif - h = gethostbyaddr(RSTRING(addr)->ptr, RSTRING(addr)->len, t); + h = gethostbyaddr(RSTRING_PTR(addr), RSTRING_SOCKLEN(addr), t); if (h == NULL) { #ifdef HAVE_HSTRERROR - extern int h_errno; - rb_raise(rb_eSocket, "%s", (char*)hstrerror(h_errno)); + extern int h_errno; + rb_raise(rb_eSocket, "%s", (char*)hstrerror(h_errno)); #else - rb_raise(rb_eSocket, "host not found"); + rb_raise(rb_eSocket, "host not found"); #endif } ary = rb_ary_new(); @@ -3393,14 +1029,14 @@ sock_s_gethostbyaddr(argc, argv) names = rb_ary_new(); rb_ary_push(ary, names); if (h->h_aliases != NULL) { - for (pch = h->h_aliases; *pch; pch++) { - rb_ary_push(names, rb_str_new2(*pch)); - } + for (pch = h->h_aliases; *pch; pch++) { + rb_ary_push(names, rb_str_new2(*pch)); + } } rb_ary_push(ary, INT2NUM(h->h_addrtype)); #ifdef h_addr for (pch = h->h_addr_list; *pch; pch++) { - rb_ary_push(ary, rb_str_new(*pch, h->h_length)); + rb_ary_push(ary, rb_str_new(*pch, h->h_length)); } #else rb_ary_push(ary, rb_str_new(h->h_addr, h->h_length)); @@ -3410,398 +1046,328 @@ sock_s_gethostbyaddr(argc, argv) } /* - * Document-method: getservbyname - * call-seq: Socket.getservbyname(name, proto="tcp") => port + * call-seq: + * Socket.getservbyname(service_name) => port_number + * Socket.getservbyname(service_name, protocol_name) => port_number + * + * Obtains the port number for _service_name_. * - * +name+ is a service name ("ftp", "telnet", ...) and proto is a protocol name - * ("udp", "tcp", ...). '/etc/services' (or your system's equivalent) is - * searched for a service for +name+ and +proto+, and the port number is - * returned. + * If _protocol_name_ is not given, "tcp" is assumed. * - * Note that unlike Socket.getaddrinfo, +proto+ may not be specified using the - * Socket::SOCK_* constants, a string must must be used. + * Socket.getservbyname("smtp") #=> 25 + * Socket.getservbyname("shell") #=> 514 + * Socket.getservbyname("syslog", "udp") #=> 514 */ static VALUE -sock_s_getservbyaname(argc, argv) - int argc; - VALUE *argv; +sock_s_getservbyname(int argc, VALUE *argv, VALUE _) { VALUE service, proto; struct servent *sp; - int port; + long port; + const char *servicename, *protoname = "tcp"; rb_scan_args(argc, argv, "11", &service, &proto); - if (NIL_P(proto)) proto = rb_str_new2("tcp"); StringValue(service); - StringValue(proto); - - sp = getservbyname(StringValueCStr(service), StringValueCStr(proto)); + if (!NIL_P(proto)) StringValue(proto); + servicename = StringValueCStr(service); + if (!NIL_P(proto)) protoname = StringValueCStr(proto); + sp = getservbyname(servicename, protoname); if (sp) { - port = ntohs(sp->s_port); + port = ntohs(sp->s_port); } else { - char *s = RSTRING(service)->ptr; - char *end; + char *end; - port = strtoul(s, &end, 0); - if (*end != '\0') { - rb_raise(rb_eSocket, "no such service %s/%s", s, RSTRING(proto)->ptr); - } + port = STRTOUL(servicename, &end, 0); + if (*end != '\0') { + rb_raise(rb_eSocket, "no such service %s/%s", servicename, protoname); + } } return INT2FIX(port); } /* -Documentation should explain the following: - - $ pp Socket.getaddrinfo("", 1, Socket::AF_UNSPEC, Socket::SOCK_STREAM, 0, Socket::AI_PASSIVE) - [["AF_INET", 1, "0.0.0.0", "0.0.0.0", 2, 1, 6]] - - $ pp Socket.getaddrinfo(nil, 1, Socket::AF_UNSPEC, Socket::SOCK_STREAM, 0, Socket::AI_PASSIVE) - [["AF_INET6", 1, "::", "::", 30, 1, 6], - ["AF_INET", 1, "0.0.0.0", "0.0.0.0", 2, 1, 6]] - - $ pp Socket.getaddrinfo("localhost", 1, Socket::AF_UNSPEC, Socket::SOCK_STREAM, 0, Socket::AI_PASSIVE) - [["AF_INET6", 1, "localhost", "::1", 30, 1, 6], - ["AF_INET", 1, "localhost", "127.0.0.1", 2, 1, 6]] - - $ pp Socket.getaddrinfo("ensemble.local.", 1, Socket::AF_UNSPEC, Socket::SOCK_STREAM, 0, Socket::AI_PASSIVE) - [["AF_INET", 1, "localhost", "192.168.123.154", 2, 1, 6]] + * call-seq: + * Socket.getservbyport(port [, protocol_name]) => service + * + * Obtains the port number for _port_. + * + * If _protocol_name_ is not given, "tcp" is assumed. + * + * Socket.getservbyport(80) #=> "www" + * Socket.getservbyport(514, "tcp") #=> "shell" + * Socket.getservbyport(514, "udp") #=> "syslog" + * + */ +static VALUE +sock_s_getservbyport(int argc, VALUE *argv, VALUE _) +{ + VALUE port, proto; + struct servent *sp; + long portnum; + const char *protoname = "tcp"; -Does it? + rb_scan_args(argc, argv, "11", &port, &proto); + portnum = NUM2LONG(port); + if (portnum != (uint16_t)portnum) { + const char *s = portnum > 0 ? "big" : "small"; + rb_raise(rb_eRangeError, "integer %ld too %s to convert into `int16_t'", portnum, s); + } + if (!NIL_P(proto)) protoname = StringValueCStr(proto); -API suggestion: this method has too many arguments, it would be backwards compatible and easier -to understand if limit args were accepted as :family=>..., :flags=>... -*/ + sp = getservbyport((int)htons((uint16_t)portnum), protoname); + if (!sp) { + rb_raise(rb_eSocket, "no such service for port %d/%s", (int)portnum, protoname); + } + return rb_str_new2(sp->s_name); +} /* - * Document-method: getaddrinfo - * call-seq: Socket.getaddrinfo(host, service, family=nil, socktype=nil, protocol=nil, flags=nil) => addrinfo - * - * Return address information for +host+ and +port+. The remaining arguments - * are hints that limit the address information returned. + * call-seq: + * Socket.getaddrinfo(nodename, servname[, family[, socktype[, protocol[, flags[, reverse_lookup]]]]]) => array * - * This method corresponds closely to the POSIX.1g getaddrinfo() definition. + * Obtains address information for _nodename_:_servname_. * - * === Parameters - * - +host+ is a host name or an address string (dotted decimal for IPv4, or a hex string - * for IPv6) for which to return information. A nil is also allowed, its meaning - * depends on +flags+, see below. - * - +service+ is a service name ("http", "ssh", ...), or - * a port number (80, 22, ...), see Socket.getservbyname for more - * information. A nil is also allowed, meaning zero. - * - +family+ limits the output to a specific address family, one of the - * Socket::AF_* constants. Socket::AF_INET (IPv4) and Socket::AF_INET6 (IPv6) - * are the most commonly used families. You will usually pass either nil or - * Socket::AF_UNSPEC, allowing the IPv6 information to be returned first if - * +host+ is reachable via IPv6, and IPv4 information otherwise. The two - * strings "AF_INET" or "AF_INET6" are also allowed, they are converted to - * their respective Socket::AF_* constants. - * - +socktype+ limits the output to a specific type of socket, one of the - * Socket::SOCK_* constants. Socket::SOCK_STREAM (for TCP) and - * Socket::SOCK_DGRAM (for UDP) are the most commonly used socket types. If - * nil, then information for all types of sockets supported by +service+ will - * be returned. You will usually know what type of socket you intend to - * create, and should pass that socket type in. - * - +protocol+ limits the output to a specific protocol numpber, one of the - * Socket::IPPROTO_* constants. It is usually implied by the socket type - * (Socket::SOCK_STREAM => Socket::IPPROTO_TCP, ...), if you pass other than - * nil you already know what this is for. - * - +flags+ is one of the Socket::AI_* constants. They mean: - * - Socket::AI_PASSIVE: when set, if +host+ is nil the 'any' address will be - * returned, Socket::INADDR_ANY or 0 for IPv4, "0::0" or "::" for IPv6. This - * address is suitable for use by servers that will bind their socket and do - * a passive listen, thus the name of the flag. Otherwise the local or - * loopback address will be returned, this is "127.0.0.1" for IPv4 and "::1' - * for IPv6. - * - ... + * Note that Addrinfo.getaddrinfo provides the same functionality in + * an object oriented style. * + * _family_ should be an address family such as: :INET, :INET6, etc. * - * === Returns + * _socktype_ should be a socket type such as: :STREAM, :DGRAM, :RAW, etc. * - * Returns an array of arrays, where each subarray contains: - * - address family, a string like "AF_INET" or "AF_INET6" - * - port number, the port number for +service+ - * - host name, either a canonical name for +host+, or it's address in presentation - * format if the address could not be looked up. - * - host IP, the address of +host+ in presentation format - * - address family, as a numeric value (one of the Socket::AF_* constants). - * - socket type, as a numeric value (one of the Socket::SOCK_* constants). - * - protocol number, as a numeric value (one of the Socket::IPPROTO_* constants). + * _protocol_ should be a protocol defined in the family, + * and defaults to 0 for the family. * - * The first four values are identical to what is commonly returned as an - * address array, see IPSocket for more information. + * _flags_ should be bitwise OR of Socket::AI_* constants. * - * === Examples + * Socket.getaddrinfo("www.ruby-lang.org", "http", nil, :STREAM) + * #=> [["AF_INET", 80, "carbon.ruby-lang.org", "221.186.184.68", 2, 1, 6]] # PF_INET/SOCK_STREAM/IPPROTO_TCP * - * Not all input combinations are valid, and while there are many combinations, - * only a few cases are common. + * Socket.getaddrinfo("localhost", nil) + * #=> [["AF_INET", 0, "localhost", "127.0.0.1", 2, 1, 6], # PF_INET/SOCK_STREAM/IPPROTO_TCP + * # ["AF_INET", 0, "localhost", "127.0.0.1", 2, 2, 17], # PF_INET/SOCK_DGRAM/IPPROTO_UDP + * # ["AF_INET", 0, "localhost", "127.0.0.1", 2, 3, 0]] # PF_INET/SOCK_RAW/IPPROTO_IP * - * A typical client will call getaddrinfo with the +host+ and +service+ it - * wants to connect to. It knows that it will attempt to connect with either - * TCP or UDP, and specifies +socktype+ accordingly. It loops through all - * returned addresses, and try to connect to them in turn: + * _reverse_lookup_ directs the form of the third element, and has to + * be one of below. If _reverse_lookup_ is omitted, the default value is +nil+. * - * addrinfo = Socket::getaddrinfo('www.example.com', 'www', nil, Socket::SOCK_STREAM) - * addrinfo.each do |af, port, name, addr| - * begin - * sock = TCPSocket.new(addr, port) - * # ... - * exit 1 - * rescue - * end - * end + * +true+, +:hostname+: hostname is obtained from numeric address using reverse lookup, which may take a time. + * +false+, +:numeric+: hostname is the same as numeric address. + * +nil+: obey to the current +do_not_reverse_lookup+ flag. * - * With UDP you don't know if connect suceeded, but if communication fails, - * the next address can be tried. - * - * A typical server will call getaddrinfo with a +host+ of nil, the +service+ - * it listens to, and a +flags+ of Socket::AI_PASSIVE. It will listen for - * connections on the first returned address: - * addrinfo = Socket::getaddrinfo(nil, 'www', nil, Socket::SOCK_STREAM, nil, Socket::AI_PASSIVE) - * af, port, name, addr = addrinfo.first - * sock = TCPServer(addr, port) - * while( client = s.accept ) - * # ... - * end + * If Addrinfo object is preferred, use Addrinfo.getaddrinfo. */ static VALUE -sock_s_getaddrinfo(argc, argv) - int argc; - VALUE *argv; +sock_s_getaddrinfo(int argc, VALUE *argv, VALUE _) { - VALUE host, port, family, socktype, protocol, flags, ret; - char hbuf[1024], pbuf[1024]; - char *hptr, *pptr, *ap; - struct addrinfo hints, *res; - int error; - - host = port = family = socktype = protocol = flags = Qnil; - rb_scan_args(argc, argv, "24", &host, &port, &family, &socktype, &protocol, &flags); - if (NIL_P(host)) { - hptr = NULL; - } - else { - strncpy(hbuf, StringValuePtr(host), sizeof(hbuf)); - hbuf[sizeof(hbuf) - 1] = '\0'; - hptr = hbuf; - } - if (NIL_P(port)) { - pptr = NULL; - } - else if (FIXNUM_P(port)) { - snprintf(pbuf, sizeof(pbuf), "%ld", FIX2LONG(port)); - pptr = pbuf; - } - else { - strncpy(pbuf, StringValuePtr(port), sizeof(pbuf)); - pbuf[sizeof(pbuf) - 1] = '\0'; - pptr = pbuf; - } + VALUE host, port, family, socktype, protocol, flags, ret, revlookup; + struct addrinfo hints; + struct rb_addrinfo *res; + int norevlookup; + + rb_scan_args(argc, argv, "25", &host, &port, &family, &socktype, &protocol, &flags, &revlookup); MEMZERO(&hints, struct addrinfo, 1); - if (NIL_P(family)) { - hints.ai_family = PF_UNSPEC; - } - else if (FIXNUM_P(family)) { - hints.ai_family = FIX2INT(family); - } - else if ((ap = StringValuePtr(family)) != 0) { - if (strcmp(ap, "AF_INET") == 0) { - hints.ai_family = PF_INET; - } -#ifdef INET6 - else if (strcmp(ap, "AF_INET6") == 0) { - hints.ai_family = PF_INET6; - } -#endif - } + hints.ai_family = NIL_P(family) ? PF_UNSPEC : rsock_family_arg(family); if (!NIL_P(socktype)) { - hints.ai_socktype = NUM2INT(socktype); + hints.ai_socktype = rsock_socktype_arg(socktype); } if (!NIL_P(protocol)) { - hints.ai_protocol = NUM2INT(protocol); + hints.ai_protocol = NUM2INT(protocol); } if (!NIL_P(flags)) { - hints.ai_flags = NUM2INT(flags); + hints.ai_flags = NUM2INT(flags); } - error = getaddrinfo(hptr, pptr, &hints, &res); - if (error) { - rb_raise(rb_eSocket, "getaddrinfo: %s", gai_strerror(error)); + if (NIL_P(revlookup) || !rsock_revlookup_flag(revlookup, &norevlookup)) { + norevlookup = rsock_do_not_reverse_lookup; } - ret = make_addrinfo(res); - freeaddrinfo(res); + res = rsock_getaddrinfo(host, port, &hints, 0, Qnil); + + ret = make_addrinfo(res, norevlookup); + rb_freeaddrinfo(res); return ret; } +/* + * call-seq: + * Socket.getnameinfo(sockaddr [, flags]) => [hostname, servicename] + * + * Obtains name information for _sockaddr_. + * + * _sockaddr_ should be one of follows. + * - packed sockaddr string such as Socket.sockaddr_in(80, "127.0.0.1") + * - 3-elements array such as ["AF_INET", 80, "127.0.0.1"] + * - 4-elements array such as ["AF_INET", 80, ignored, "127.0.0.1"] + * + * _flags_ should be bitwise OR of Socket::NI_* constants. + * + * Note: + * The last form is compatible with IPSocket#addr and IPSocket#peeraddr. + * + * Socket.getnameinfo(Socket.sockaddr_in(80, "127.0.0.1")) #=> ["localhost", "www"] + * Socket.getnameinfo(["AF_INET", 80, "127.0.0.1"]) #=> ["localhost", "www"] + * Socket.getnameinfo(["AF_INET", 80, "localhost", "127.0.0.1"]) #=> ["localhost", "www"] + * + * If Addrinfo object is preferred, use Addrinfo#getnameinfo. + */ static VALUE -sock_s_getnameinfo(argc, argv) - int argc; - VALUE *argv; +sock_s_getnameinfo(int argc, VALUE *argv, VALUE _) { VALUE sa, af = Qnil, host = Qnil, port = Qnil, flags, tmp; - char *hptr, *pptr; char hbuf[1024], pbuf[1024]; int fl; - struct addrinfo hints, *res = NULL, *r; - int error; - struct sockaddr_storage ss; + struct rb_addrinfo *res = NULL; + struct addrinfo hints, *r; + int error, saved_errno; + union_sockaddr ss; struct sockaddr *sap; - char *ap; + socklen_t salen; sa = flags = Qnil; rb_scan_args(argc, argv, "11", &sa, &flags); fl = 0; if (!NIL_P(flags)) { - fl = NUM2INT(flags); + fl = NUM2INT(flags); } - tmp = rb_check_string_type(sa); + tmp = rb_check_sockaddr_string_type(sa); if (!NIL_P(tmp)) { - sa = tmp; - if (sizeof(ss) < RSTRING(sa)->len) { - rb_raise(rb_eTypeError, "sockaddr length too big"); - } - memcpy(&ss, RSTRING(sa)->ptr, RSTRING(sa)->len); - if (RSTRING(sa)->len != SA_LEN((struct sockaddr*)&ss)) { - rb_raise(rb_eTypeError, "sockaddr size differs - should not happen"); - } - sap = (struct sockaddr*)&ss; - goto call_nameinfo; + sa = tmp; + if (sizeof(ss) < (size_t)RSTRING_LEN(sa)) { + rb_raise(rb_eTypeError, "sockaddr length too big"); + } + memcpy(&ss, RSTRING_PTR(sa), RSTRING_LEN(sa)); + if (!VALIDATE_SOCKLEN(&ss.addr, RSTRING_LEN(sa))) { + rb_raise(rb_eTypeError, "sockaddr size differs - should not happen"); + } + sap = &ss.addr; + salen = RSTRING_SOCKLEN(sa); + goto call_nameinfo; } tmp = rb_check_array_type(sa); if (!NIL_P(tmp)) { - sa = tmp; - MEMZERO(&hints, struct addrinfo, 1); - if (RARRAY(sa)->len == 3) { - af = RARRAY(sa)->ptr[0]; - port = RARRAY(sa)->ptr[1]; - host = RARRAY(sa)->ptr[2]; - } - else if (RARRAY(sa)->len >= 4) { - af = RARRAY(sa)->ptr[0]; - port = RARRAY(sa)->ptr[1]; - host = RARRAY(sa)->ptr[3]; - if (NIL_P(host)) { - host = RARRAY(sa)->ptr[2]; - } - else { - /* - * 4th element holds numeric form, don't resolve. - * see ipaddr(). - */ + sa = tmp; + MEMZERO(&hints, struct addrinfo, 1); + if (RARRAY_LEN(sa) == 3) { + af = RARRAY_AREF(sa, 0); + port = RARRAY_AREF(sa, 1); + host = RARRAY_AREF(sa, 2); + } + else if (RARRAY_LEN(sa) >= 4) { + af = RARRAY_AREF(sa, 0); + port = RARRAY_AREF(sa, 1); + host = RARRAY_AREF(sa, 3); + if (NIL_P(host)) { + host = RARRAY_AREF(sa, 2); + } + else { + /* + * 4th element holds numeric form, don't resolve. + * see rsock_ipaddr(). + */ #ifdef AI_NUMERICHOST /* AIX 4.3.3 doesn't have AI_NUMERICHOST. */ - hints.ai_flags |= AI_NUMERICHOST; -#endif - } - } - else { - rb_raise(rb_eArgError, "array size should be 3 or 4, %ld given", - RARRAY(sa)->len); - } - /* host */ - if (NIL_P(host)) { - hptr = NULL; - } - else { - strncpy(hbuf, StringValuePtr(host), sizeof(hbuf)); - hbuf[sizeof(hbuf) - 1] = '\0'; - hptr = hbuf; - } - /* port */ - if (NIL_P(port)) { - strcpy(pbuf, "0"); - pptr = NULL; - } - else if (FIXNUM_P(port)) { - snprintf(pbuf, sizeof(pbuf), "%ld", NUM2LONG(port)); - pptr = pbuf; - } - else { - strncpy(pbuf, StringValuePtr(port), sizeof(pbuf)); - pbuf[sizeof(pbuf) - 1] = '\0'; - pptr = pbuf; - } - hints.ai_socktype = (fl & NI_DGRAM) ? SOCK_DGRAM : SOCK_STREAM; - /* af */ - if (NIL_P(af)) { - hints.ai_family = PF_UNSPEC; - } - else if (FIXNUM_P(af)) { - hints.ai_family = FIX2INT(af); - } - else if ((ap = StringValuePtr(af)) != 0) { - if (strcmp(ap, "AF_INET") == 0) { - hints.ai_family = PF_INET; - } -#ifdef INET6 - else if (strcmp(ap, "AF_INET6") == 0) { - hints.ai_family = PF_INET6; - } + hints.ai_flags |= AI_NUMERICHOST; #endif - } - error = getaddrinfo(hptr, pptr, &hints, &res); - if (error) goto error_exit_addr; - sap = res->ai_addr; + } + } + else { + rb_raise(rb_eArgError, "array size should be 3 or 4, %ld given", + RARRAY_LEN(sa)); + } + hints.ai_socktype = (fl & NI_DGRAM) ? SOCK_DGRAM : SOCK_STREAM; + /* af */ + hints.ai_family = NIL_P(af) ? PF_UNSPEC : rsock_family_arg(af); + res = rsock_getaddrinfo(host, port, &hints, 0, Qnil); + sap = res->ai->ai_addr; + salen = res->ai->ai_addrlen; } else { - rb_raise(rb_eTypeError, "expecting String or Array"); + rb_raise(rb_eTypeError, "expecting String or Array"); } call_nameinfo: - error = getnameinfo(sap, SA_LEN(sap), hbuf, sizeof(hbuf), - pbuf, sizeof(pbuf), fl); + error = rb_getnameinfo(sap, salen, hbuf, sizeof(hbuf), + pbuf, sizeof(pbuf), fl); if (error) goto error_exit_name; if (res) { - for (r = res->ai_next; r; r = r->ai_next) { - char hbuf2[1024], pbuf2[1024]; - - sap = r->ai_addr; - error = getnameinfo(sap, SA_LEN(sap), hbuf2, sizeof(hbuf2), - pbuf2, sizeof(pbuf2), fl); - if (error) goto error_exit_name; - if (strcmp(hbuf, hbuf2) != 0|| strcmp(pbuf, pbuf2) != 0) { - freeaddrinfo(res); - rb_raise(rb_eSocket, "sockaddr resolved to multiple nodename"); - } - } - freeaddrinfo(res); + for (r = res->ai->ai_next; r; r = r->ai_next) { + char hbuf2[1024], pbuf2[1024]; + + sap = r->ai_addr; + salen = r->ai_addrlen; + error = rb_getnameinfo(sap, salen, hbuf2, sizeof(hbuf2), + pbuf2, sizeof(pbuf2), fl); + if (error) goto error_exit_name; + if (strcmp(hbuf, hbuf2) != 0|| strcmp(pbuf, pbuf2) != 0) { + rb_freeaddrinfo(res); + rb_raise(rb_eSocket, "sockaddr resolved to multiple nodename"); + } + } + rb_freeaddrinfo(res); } return rb_assoc_new(rb_str_new2(hbuf), rb_str_new2(pbuf)); - error_exit_addr: - if (res) freeaddrinfo(res); - rb_raise(rb_eSocket, "getaddrinfo: %s", gai_strerror(error)); - error_exit_name: - if (res) freeaddrinfo(res); - rb_raise(rb_eSocket, "getnameinfo: %s", gai_strerror(error)); + saved_errno = errno; + if (res) rb_freeaddrinfo(res); + errno = saved_errno; + rsock_raise_resolution_error("getnameinfo", error); + + UNREACHABLE_RETURN(Qnil); } +/* + * call-seq: + * Socket.sockaddr_in(port, host) => sockaddr + * Socket.pack_sockaddr_in(port, host) => sockaddr + * + * Packs _port_ and _host_ as an AF_INET/AF_INET6 sockaddr string. + * + * Socket.sockaddr_in(80, "127.0.0.1") + * #=> "\x02\x00\x00P\x7F\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00" + * + * Socket.sockaddr_in(80, "::1") + * #=> "\n\x00\x00P\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00" + * + */ static VALUE -sock_s_pack_sockaddr_in(self, port, host) - VALUE self, port, host; +sock_s_pack_sockaddr_in(VALUE self, VALUE port, VALUE host) { - struct addrinfo *res = sock_addrinfo(host, port, 0, 0); - VALUE addr = rb_str_new((char*)res->ai_addr, res->ai_addrlen); + struct rb_addrinfo *res = rsock_addrinfo(host, port, AF_UNSPEC, 0, 0, Qnil); + VALUE addr = rb_str_new((char*)res->ai->ai_addr, res->ai->ai_addrlen); - freeaddrinfo(res); - OBJ_INFECT(addr, port); - OBJ_INFECT(addr, host); + rb_freeaddrinfo(res); return addr; } +/* + * call-seq: + * Socket.unpack_sockaddr_in(sockaddr) => [port, ip_address] + * + * Unpacks _sockaddr_ into port and ip_address. + * + * _sockaddr_ should be a string or an addrinfo for AF_INET/AF_INET6. + * + * sockaddr = Socket.sockaddr_in(80, "127.0.0.1") + * p sockaddr #=> "\x02\x00\x00P\x7F\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00" + * p Socket.unpack_sockaddr_in(sockaddr) #=> [80, "127.0.0.1"] + * + */ static VALUE -sock_s_unpack_sockaddr_in(self, addr) - VALUE self, addr; +sock_s_unpack_sockaddr_in(VALUE self, VALUE addr) { struct sockaddr_in * sockaddr; VALUE host; - sockaddr = (struct sockaddr_in*)StringValuePtr(addr); + sockaddr = (struct sockaddr_in*)SockAddrStringValuePtr(addr); + if (RSTRING_LEN(addr) < + (char*)&((struct sockaddr *)sockaddr)->sa_family + + sizeof(((struct sockaddr *)sockaddr)->sa_family) - + (char*)sockaddr) + rb_raise(rb_eArgError, "too short sockaddr"); if (((struct sockaddr *)sockaddr)->sa_family != AF_INET #ifdef INET6 && ((struct sockaddr *)sockaddr)->sa_family != AF_INET6 @@ -3813,909 +1379,728 @@ sock_s_unpack_sockaddr_in(self, addr) rb_raise(rb_eArgError, "not an AF_INET sockaddr"); #endif } - host = make_ipaddr((struct sockaddr*)sockaddr); - OBJ_INFECT(host, addr); + host = rsock_make_ipaddr((struct sockaddr*)sockaddr, RSTRING_SOCKLEN(addr)); return rb_assoc_new(INT2NUM(ntohs(sockaddr->sin_port)), host); } -#ifdef HAVE_SYS_UN_H +#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN + +/* + * call-seq: + * Socket.sockaddr_un(path) => sockaddr + * Socket.pack_sockaddr_un(path) => sockaddr + * + * Packs _path_ as an AF_UNIX sockaddr string. + * + * Socket.sockaddr_un("/tmp/sock") #=> "\x01\x00/tmp/sock\x00\x00..." + * + */ static VALUE -sock_s_pack_sockaddr_un(self, path) - VALUE self, path; +sock_s_pack_sockaddr_un(VALUE self, VALUE path) { struct sockaddr_un sockaddr; - char *sun_path; VALUE addr; - MEMZERO(&sockaddr, struct sockaddr_un, 1); - sockaddr.sun_family = AF_UNIX; - sun_path = StringValueCStr(path); - if (sizeof(sockaddr.sun_path) <= strlen(sun_path)) { - rb_raise(rb_eArgError, "too long unix socket path (max: %dbytes)", - (int)sizeof(sockaddr.sun_path)-1); + StringValue(path); + INIT_SOCKADDR_UN(&sockaddr, sizeof(struct sockaddr_un)); + if (sizeof(sockaddr.sun_path) < (size_t)RSTRING_LEN(path)) { + rb_raise(rb_eArgError, "too long unix socket path (%"PRIuSIZE" bytes given but %"PRIuSIZE" bytes max)", + (size_t)RSTRING_LEN(path), sizeof(sockaddr.sun_path)); } - strncpy(sockaddr.sun_path, sun_path, sizeof(sockaddr.sun_path)-1); - addr = rb_str_new((char*)&sockaddr, sizeof(sockaddr)); - OBJ_INFECT(addr, path); + memcpy(sockaddr.sun_path, RSTRING_PTR(path), RSTRING_LEN(path)); + addr = rb_str_new((char*)&sockaddr, rsock_unix_sockaddr_len(path)); return addr; } +/* + * call-seq: + * Socket.unpack_sockaddr_un(sockaddr) => path + * + * Unpacks _sockaddr_ into path. + * + * _sockaddr_ should be a string or an addrinfo for AF_UNIX. + * + * sockaddr = Socket.sockaddr_un("/tmp/sock") + * p Socket.unpack_sockaddr_un(sockaddr) #=> "/tmp/sock" + * + */ static VALUE -sock_s_unpack_sockaddr_un(self, addr) - VALUE self, addr; +sock_s_unpack_sockaddr_un(VALUE self, VALUE addr) { struct sockaddr_un * sockaddr; - char *sun_path; VALUE path; - sockaddr = (struct sockaddr_un*)StringValuePtr(addr); + sockaddr = (struct sockaddr_un*)SockAddrStringValuePtr(addr); + if (RSTRING_LEN(addr) < + (char*)&((struct sockaddr *)sockaddr)->sa_family + + sizeof(((struct sockaddr *)sockaddr)->sa_family) - + (char*)sockaddr) + rb_raise(rb_eArgError, "too short sockaddr"); if (((struct sockaddr *)sockaddr)->sa_family != AF_UNIX) { rb_raise(rb_eArgError, "not an AF_UNIX sockaddr"); } - if (sizeof(struct sockaddr_un) < RSTRING(addr)->len) { + if (sizeof(struct sockaddr_un) < (size_t)RSTRING_LEN(addr)) { rb_raise(rb_eTypeError, "too long sockaddr_un - %ld longer than %d", - RSTRING(addr)->len, sizeof(struct sockaddr_un)); - } - sun_path = unixpath(sockaddr, RSTRING(addr)->len); - if (sizeof(struct sockaddr_un) == RSTRING(addr)->len && - sun_path == sockaddr->sun_path && - sun_path + strlen(sun_path) == RSTRING(addr)->ptr + RSTRING(addr)->len) { - rb_raise(rb_eArgError, "sockaddr_un.sun_path not NUL terminated"); + RSTRING_LEN(addr), (int)sizeof(struct sockaddr_un)); } - path = rb_str_new2(sun_path); - OBJ_INFECT(path, addr); + path = rsock_unixpath_str(sockaddr, RSTRING_SOCKLEN(addr)); return path; } #endif -static VALUE mConst; +#if defined(HAVE_GETIFADDRS) || defined(SIOCGLIFCONF) || defined(SIOCGIFCONF) || defined(_WIN32) -static void -sock_define_const(name, value) - char *name; - int value; +static socklen_t +sockaddr_len(struct sockaddr *addr) { - rb_define_const(rb_cSocket, name, INT2FIX(value)); - rb_define_const(mConst, name, INT2FIX(value)); -} + if (addr == NULL) + return 0; -/* - * Class +Socket+ provides access to the underlying operating system - * socket implementations. It can be used to provide more operating system - * specific functionality than the protocol-specific socket classes but at the - * expense of greater complexity. In particular, the class handles addresses - * using +struct sockaddr+ structures packed into Ruby strings, which can be - * a joy to manipulate. - * - * === Exception Handling - * Ruby's implementation of +Socket+ causes an exception to be raised - * based on the error generated by the system dependent implementation. - * This is why the methods are documented in a way that isolate - * Unix-based system exceptions from Windows based exceptions. If more - * information on particular exception is needed please refer to the - * Unix manual pages or the Windows WinSock reference. - * - * - * === Documentation by - * * Zach Dennis - * * Sam Roberts - * * <em>Programming Ruby</em> from The Pragmatic Bookshelf. - * - * Much material in this documentation is taken with permission from - * <em>Programming Ruby</em> from The Pragmatic Bookshelf. - */ -void -Init_socket() -{ - rb_eSocket = rb_define_class("SocketError", rb_eStandardError); - - rb_cBasicSocket = rb_define_class("BasicSocket", rb_cIO); - rb_undef_method(rb_cBasicSocket, "initialize"); - - rb_define_singleton_method(rb_cBasicSocket, "do_not_reverse_lookup", - bsock_do_not_rev_lookup, 0); - rb_define_singleton_method(rb_cBasicSocket, "do_not_reverse_lookup=", - bsock_do_not_rev_lookup_set, 1); - rb_define_singleton_method(rb_cBasicSocket, "for_fd", bsock_s_for_fd, 1); - - rb_define_method(rb_cBasicSocket, "close_read", bsock_close_read, 0); - rb_define_method(rb_cBasicSocket, "close_write", bsock_close_write, 0); - rb_define_method(rb_cBasicSocket, "shutdown", bsock_shutdown, -1); - rb_define_method(rb_cBasicSocket, "setsockopt", bsock_setsockopt, 3); - rb_define_method(rb_cBasicSocket, "getsockopt", bsock_getsockopt, 2); - rb_define_method(rb_cBasicSocket, "getsockname", bsock_getsockname, 0); - rb_define_method(rb_cBasicSocket, "getpeername", bsock_getpeername, 0); - rb_define_method(rb_cBasicSocket, "send", bsock_send, -1); - rb_define_method(rb_cBasicSocket, "recv", bsock_recv, -1); - rb_define_method(rb_cBasicSocket, "recv_nonblock", bsock_recv_nonblock, -1); - - rb_cIPSocket = rb_define_class("IPSocket", rb_cBasicSocket); - rb_define_global_const("IPsocket", rb_cIPSocket); - rb_define_method(rb_cIPSocket, "addr", ip_addr, 0); - rb_define_method(rb_cIPSocket, "peeraddr", ip_peeraddr, 0); - rb_define_method(rb_cIPSocket, "recvfrom", ip_recvfrom, -1); - rb_define_singleton_method(rb_cIPSocket, "getaddress", ip_s_getaddress, 1); - - rb_cTCPSocket = rb_define_class("TCPSocket", rb_cIPSocket); - rb_define_global_const("TCPsocket", rb_cTCPSocket); - rb_define_singleton_method(rb_cTCPSocket, "gethostbyname", tcp_s_gethostbyname, 1); - rb_define_method(rb_cTCPSocket, "initialize", tcp_init, -1); - -#ifdef SOCKS - rb_cSOCKSSocket = rb_define_class("SOCKSSocket", rb_cTCPSocket); - rb_define_global_const("SOCKSsocket", rb_cSOCKSSocket); - rb_define_method(rb_cSOCKSSocket, "initialize", socks_init, 2); -#ifdef SOCKS5 - rb_define_method(rb_cSOCKSSocket, "close", socks_s_close, 0); +#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN + if (addr->sa_len != 0) + return addr->sa_len; #endif + + switch (addr->sa_family) { + case AF_INET: + return (socklen_t)sizeof(struct sockaddr_in); + +#ifdef AF_INET6 + case AF_INET6: + return (socklen_t)sizeof(struct sockaddr_in6); #endif - rb_cTCPServer = rb_define_class("TCPServer", rb_cTCPSocket); - rb_define_global_const("TCPserver", rb_cTCPServer); - rb_define_method(rb_cTCPServer, "accept", tcp_accept, 0); - rb_define_method(rb_cTCPServer, "accept_nonblock", tcp_accept_nonblock, 0); - rb_define_method(rb_cTCPServer, "sysaccept", tcp_sysaccept, 0); - rb_define_method(rb_cTCPServer, "initialize", tcp_svr_init, -1); - rb_define_method(rb_cTCPServer, "listen", sock_listen, 1); - - rb_cUDPSocket = rb_define_class("UDPSocket", rb_cIPSocket); - rb_define_global_const("UDPsocket", rb_cUDPSocket); - rb_define_method(rb_cUDPSocket, "initialize", udp_init, -1); - rb_define_method(rb_cUDPSocket, "connect", udp_connect, 2); - rb_define_method(rb_cUDPSocket, "bind", udp_bind, 2); - rb_define_method(rb_cUDPSocket, "send", udp_send, -1); - rb_define_method(rb_cUDPSocket, "recvfrom_nonblock", udp_recvfrom_nonblock, -1); - -#ifdef HAVE_SYS_UN_H - rb_cUNIXSocket = rb_define_class("UNIXSocket", rb_cBasicSocket); - rb_define_global_const("UNIXsocket", rb_cUNIXSocket); - rb_define_method(rb_cUNIXSocket, "initialize", unix_init, 1); - rb_define_method(rb_cUNIXSocket, "path", unix_path, 0); - rb_define_method(rb_cUNIXSocket, "addr", unix_addr, 0); - rb_define_method(rb_cUNIXSocket, "peeraddr", unix_peeraddr, 0); - rb_define_method(rb_cUNIXSocket, "recvfrom", unix_recvfrom, -1); - rb_define_method(rb_cUNIXSocket, "send_io", unix_send_io, 1); - rb_define_method(rb_cUNIXSocket, "recv_io", unix_recv_io, -1); - rb_define_singleton_method(rb_cUNIXSocket, "socketpair", unix_s_socketpair, -1); - rb_define_singleton_method(rb_cUNIXSocket, "pair", unix_s_socketpair, -1); - - rb_cUNIXServer = rb_define_class("UNIXServer", rb_cUNIXSocket); - rb_define_global_const("UNIXserver", rb_cUNIXServer); - rb_define_method(rb_cUNIXServer, "initialize", unix_svr_init, 1); - rb_define_method(rb_cUNIXServer, "accept", unix_accept, 0); - rb_define_method(rb_cUNIXServer, "accept_nonblock", unix_accept_nonblock, 0); - rb_define_method(rb_cUNIXServer, "sysaccept", unix_sysaccept, 0); - rb_define_method(rb_cUNIXServer, "listen", sock_listen, 1); +#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN + case AF_UNIX: + return (socklen_t)sizeof(struct sockaddr_un); #endif - rb_cSocket = rb_define_class("Socket", rb_cBasicSocket); +#ifdef AF_PACKET + case AF_PACKET: + return (socklen_t)(offsetof(struct sockaddr_ll, sll_addr) + ((struct sockaddr_ll *)addr)->sll_halen); +#endif - rb_define_method(rb_cSocket, "initialize", sock_initialize, 3); - rb_define_method(rb_cSocket, "connect", sock_connect, 1); - rb_define_method(rb_cSocket, "connect_nonblock", sock_connect_nonblock, 1); - rb_define_method(rb_cSocket, "bind", sock_bind, 1); - rb_define_method(rb_cSocket, "listen", sock_listen, 1); - rb_define_method(rb_cSocket, "accept", sock_accept, 0); - rb_define_method(rb_cSocket, "accept_nonblock", sock_accept_nonblock, 0); - rb_define_method(rb_cSocket, "sysaccept", sock_sysaccept, 0); + default: + return (socklen_t)(offsetof(struct sockaddr, sa_family) + sizeof(addr->sa_family)); + } +} - rb_define_method(rb_cSocket, "recvfrom", sock_recvfrom, -1); - rb_define_method(rb_cSocket, "recvfrom_nonblock", sock_recvfrom_nonblock, -1); +socklen_t +rsock_sockaddr_len(struct sockaddr *addr) +{ + return sockaddr_len(addr); +} - rb_define_singleton_method(rb_cSocket, "socketpair", sock_s_socketpair, 3); - rb_define_singleton_method(rb_cSocket, "pair", sock_s_socketpair, 3); - rb_define_singleton_method(rb_cSocket, "gethostname", sock_gethostname, 0); - rb_define_singleton_method(rb_cSocket, "gethostbyname", sock_s_gethostbyname, 1); - rb_define_singleton_method(rb_cSocket, "gethostbyaddr", sock_s_gethostbyaddr, -1); - rb_define_singleton_method(rb_cSocket, "getservbyname", sock_s_getservbyaname, -1); - rb_define_singleton_method(rb_cSocket, "getaddrinfo", sock_s_getaddrinfo, -1); - rb_define_singleton_method(rb_cSocket, "getnameinfo", sock_s_getnameinfo, -1); - rb_define_singleton_method(rb_cSocket, "sockaddr_in", sock_s_pack_sockaddr_in, 2); - rb_define_singleton_method(rb_cSocket, "pack_sockaddr_in", sock_s_pack_sockaddr_in, 2); - rb_define_singleton_method(rb_cSocket, "unpack_sockaddr_in", sock_s_unpack_sockaddr_in, 1); -#ifdef HAVE_SYS_UN_H - rb_define_singleton_method(rb_cSocket, "sockaddr_un", sock_s_pack_sockaddr_un, 1); - rb_define_singleton_method(rb_cSocket, "pack_sockaddr_un", sock_s_pack_sockaddr_un, 1); - rb_define_singleton_method(rb_cSocket, "unpack_sockaddr_un", sock_s_unpack_sockaddr_un, 1); +static VALUE +sockaddr_obj(struct sockaddr *addr, socklen_t len) +{ +#if defined(AF_INET6) && defined(__KAME__) + struct sockaddr_in6 addr6; #endif - /* constants */ - mConst = rb_define_module_under(rb_cSocket, "Constants"); - sock_define_const("SOCK_STREAM", SOCK_STREAM); - sock_define_const("SOCK_DGRAM", SOCK_DGRAM); -#ifdef SOCK_RAW - sock_define_const("SOCK_RAW", SOCK_RAW); -#endif -#ifdef SOCK_RDM - sock_define_const("SOCK_RDM", SOCK_RDM); -#endif -#ifdef SOCK_SEQPACKET - sock_define_const("SOCK_SEQPACKET", SOCK_SEQPACKET); -#endif -#ifdef SOCK_PACKET - sock_define_const("SOCK_PACKET", SOCK_PACKET); -#endif + if (addr == NULL) + return Qnil; - sock_define_const("AF_INET", AF_INET); -#ifdef PF_INET - sock_define_const("PF_INET", PF_INET); -#endif -#ifdef AF_UNIX - sock_define_const("AF_UNIX", AF_UNIX); - sock_define_const("PF_UNIX", PF_UNIX); -#endif -#ifdef AF_AX25 - sock_define_const("AF_AX25", AF_AX25); - sock_define_const("PF_AX25", PF_AX25); -#endif -#ifdef AF_IPX - sock_define_const("AF_IPX", AF_IPX); - sock_define_const("PF_IPX", PF_IPX); -#endif -#ifdef AF_APPLETALK - sock_define_const("AF_APPLETALK", AF_APPLETALK); - sock_define_const("PF_APPLETALK", PF_APPLETALK); -#endif -#ifdef AF_UNSPEC - sock_define_const("AF_UNSPEC", AF_UNSPEC); - sock_define_const("PF_UNSPEC", PF_UNSPEC); -#endif -#ifdef INET6 - sock_define_const("AF_INET6", AF_INET6); -#endif -#ifdef INET6 - sock_define_const("PF_INET6", PF_INET6); -#endif -#ifdef AF_LOCAL - sock_define_const("AF_LOCAL", AF_LOCAL); -#endif -#ifdef PF_LOCAL - sock_define_const("PF_LOCAL", PF_LOCAL); -#endif -#ifdef AF_IMPLINK - sock_define_const("AF_IMPLINK", AF_IMPLINK); -#endif -#ifdef PF_IMPLINK - sock_define_const("PF_IMPLINK", PF_IMPLINK); -#endif -#ifdef AF_PUP - sock_define_const("AF_PUP", AF_PUP); -#endif -#ifdef PF_PUP - sock_define_const("PF_PUP", PF_PUP); -#endif -#ifdef AF_CHAOS - sock_define_const("AF_CHAOS", AF_CHAOS); -#endif -#ifdef PF_CHAOS - sock_define_const("PF_CHAOS", PF_CHAOS); -#endif -#ifdef AF_NS - sock_define_const("AF_NS", AF_NS); -#endif -#ifdef PF_NS - sock_define_const("PF_NS", PF_NS); -#endif -#ifdef AF_ISO - sock_define_const("AF_ISO", AF_ISO); -#endif -#ifdef PF_ISO - sock_define_const("PF_ISO", PF_ISO); -#endif -#ifdef AF_OSI - sock_define_const("AF_OSI", AF_OSI); -#endif -#ifdef PF_OSI - sock_define_const("PF_OSI", PF_OSI); -#endif -#ifdef AF_ECMA - sock_define_const("AF_ECMA", AF_ECMA); -#endif -#ifdef PF_ECMA - sock_define_const("PF_ECMA", PF_ECMA); -#endif -#ifdef AF_DATAKIT - sock_define_const("AF_DATAKIT", AF_DATAKIT); -#endif -#ifdef PF_DATAKIT - sock_define_const("PF_DATAKIT", PF_DATAKIT); -#endif -#ifdef AF_CCITT - sock_define_const("AF_CCITT", AF_CCITT); -#endif -#ifdef PF_CCITT - sock_define_const("PF_CCITT", PF_CCITT); -#endif -#ifdef AF_SNA - sock_define_const("AF_SNA", AF_SNA); -#endif -#ifdef PF_SNA - sock_define_const("PF_SNA", PF_SNA); -#endif -#ifdef AF_DEC - sock_define_const("AF_DEC", AF_DEC); -#endif -#ifdef PF_DEC - sock_define_const("PF_DEC", PF_DEC); -#endif -#ifdef AF_DLI - sock_define_const("AF_DLI", AF_DLI); -#endif -#ifdef PF_DLI - sock_define_const("PF_DLI", PF_DLI); -#endif -#ifdef AF_LAT - sock_define_const("AF_LAT", AF_LAT); -#endif -#ifdef PF_LAT - sock_define_const("PF_LAT", PF_LAT); -#endif -#ifdef AF_HYLINK - sock_define_const("AF_HYLINK", AF_HYLINK); -#endif -#ifdef PF_HYLINK - sock_define_const("PF_HYLINK", PF_HYLINK); -#endif -#ifdef AF_ROUTE - sock_define_const("AF_ROUTE", AF_ROUTE); -#endif -#ifdef PF_ROUTE - sock_define_const("PF_ROUTE", PF_ROUTE); -#endif -#ifdef AF_LINK - sock_define_const("AF_LINK", AF_LINK); -#endif -#ifdef PF_LINK - sock_define_const("PF_LINK", PF_LINK); -#endif -#ifdef AF_COIP - sock_define_const("AF_COIP", AF_COIP); -#endif -#ifdef PF_COIP - sock_define_const("PF_COIP", PF_COIP); -#endif -#ifdef AF_CNT - sock_define_const("AF_CNT", AF_CNT); -#endif -#ifdef PF_CNT - sock_define_const("PF_CNT", PF_CNT); -#endif -#ifdef AF_SIP - sock_define_const("AF_SIP", AF_SIP); -#endif -#ifdef PF_SIP - sock_define_const("PF_SIP", PF_SIP); -#endif -#ifdef AF_NDRV - sock_define_const("AF_NDRV", AF_NDRV); -#endif -#ifdef PF_NDRV - sock_define_const("PF_NDRV", PF_NDRV); -#endif -#ifdef AF_ISDN - sock_define_const("AF_ISDN", AF_ISDN); -#endif -#ifdef PF_ISDN - sock_define_const("PF_ISDN", PF_ISDN); -#endif -#ifdef AF_NATM - sock_define_const("AF_NATM", AF_NATM); -#endif -#ifdef PF_NATM - sock_define_const("PF_NATM", PF_NATM); -#endif -#ifdef AF_SYSTEM - sock_define_const("AF_SYSTEM", AF_SYSTEM); -#endif -#ifdef PF_SYSTEM - sock_define_const("PF_SYSTEM", PF_SYSTEM); -#endif -#ifdef AF_NETBIOS - sock_define_const("AF_NETBIOS", AF_NETBIOS); -#endif -#ifdef PF_NETBIOS - sock_define_const("PF_NETBIOS", PF_NETBIOS); -#endif -#ifdef AF_PPP - sock_define_const("AF_PPP", AF_PPP); -#endif -#ifdef PF_PPP - sock_define_const("PF_PPP", PF_PPP); -#endif -#ifdef AF_ATM - sock_define_const("AF_ATM", AF_ATM); -#endif -#ifdef PF_ATM - sock_define_const("PF_ATM", PF_ATM); -#endif -#ifdef AF_NETGRAPH - sock_define_const("AF_NETGRAPH", AF_NETGRAPH); -#endif -#ifdef PF_NETGRAPH - sock_define_const("PF_NETGRAPH", PF_NETGRAPH); -#endif -#ifdef AF_MAX - sock_define_const("AF_MAX", AF_MAX); -#endif -#ifdef PF_MAX - sock_define_const("PF_MAX", PF_MAX); -#endif -#ifdef AF_E164 - sock_define_const("AF_E164", AF_E164); -#endif -#ifdef PF_XTP - sock_define_const("PF_XTP", PF_XTP); -#endif -#ifdef PF_RTIP - sock_define_const("PF_RTIP", PF_RTIP); -#endif -#ifdef PF_PIP - sock_define_const("PF_PIP", PF_PIP); -#endif -#ifdef PF_KEY - sock_define_const("PF_KEY", PF_KEY); -#endif + len = sockaddr_len(addr); - sock_define_const("MSG_OOB", MSG_OOB); -#ifdef MSG_PEEK - sock_define_const("MSG_PEEK", MSG_PEEK); -#endif -#ifdef MSG_DONTROUTE - sock_define_const("MSG_DONTROUTE", MSG_DONTROUTE); -#endif -#ifdef MSG_EOR - sock_define_const("MSG_EOR", MSG_EOR); -#endif -#ifdef MSG_TRUNC - sock_define_const("MSG_TRUNC", MSG_TRUNC); -#endif -#ifdef MSG_CTRUNC - sock_define_const("MSG_CTRUNC", MSG_CTRUNC); -#endif -#ifdef MSG_WAITALL - sock_define_const("MSG_WAITALL", MSG_WAITALL); -#endif -#ifdef MSG_DONTWAIT - sock_define_const("MSG_DONTWAIT", MSG_DONTWAIT); -#endif -#ifdef MSG_EOF - sock_define_const("MSG_EOF", MSG_EOF); -#endif -#ifdef MSG_FLUSH - sock_define_const("MSG_FLUSH", MSG_FLUSH); -#endif -#ifdef MSG_HOLD - sock_define_const("MSG_HOLD", MSG_HOLD); -#endif -#ifdef MSG_SEND - sock_define_const("MSG_SEND", MSG_SEND); -#endif -#ifdef MSG_HAVEMORE - sock_define_const("MSG_HAVEMORE", MSG_HAVEMORE); -#endif -#ifdef MSG_RCVMORE - sock_define_const("MSG_RCVMORE", MSG_RCVMORE); -#endif -#ifdef MSG_COMPAT - sock_define_const("MSG_COMPAT", MSG_COMPAT); +#if defined(__KAME__) && defined(AF_INET6) + if (addr->sa_family == AF_INET6) { + /* KAME uses the 2nd 16bit word of link local IPv6 address as interface index internally */ + /* http://orange.kame.net/dev/cvsweb.cgi/kame/IMPLEMENTATION */ + /* convert fe80:1::1 to fe80::1%1 */ + len = (socklen_t)sizeof(struct sockaddr_in6); + memcpy(&addr6, addr, len); + addr = (struct sockaddr *)&addr6; + if (IN6_IS_ADDR_LINKLOCAL(&addr6.sin6_addr) && + addr6.sin6_scope_id == 0 && + (addr6.sin6_addr.s6_addr[2] || addr6.sin6_addr.s6_addr[3])) { + addr6.sin6_scope_id = (addr6.sin6_addr.s6_addr[2] << 8) | addr6.sin6_addr.s6_addr[3]; + addr6.sin6_addr.s6_addr[2] = 0; + addr6.sin6_addr.s6_addr[3] = 0; + } + } #endif - sock_define_const("SOL_SOCKET", SOL_SOCKET); -#ifdef SOL_IP - sock_define_const("SOL_IP", SOL_IP); -#endif -#ifdef SOL_IPX - sock_define_const("SOL_IPX", SOL_IPX); -#endif -#ifdef SOL_AX25 - sock_define_const("SOL_AX25", SOL_AX25); -#endif -#ifdef SOL_ATALK - sock_define_const("SOL_ATALK", SOL_ATALK); -#endif -#ifdef SOL_TCP - sock_define_const("SOL_TCP", SOL_TCP); -#endif -#ifdef SOL_UDP - sock_define_const("SOL_UDP", SOL_UDP); -#endif + return rsock_addrinfo_new(addr, len, addr->sa_family, 0, 0, Qnil, Qnil); +} -#ifdef IPPROTO_IP - sock_define_const("IPPROTO_IP", IPPROTO_IP); -#else - sock_define_const("IPPROTO_IP", 0); -#endif -#ifdef IPPROTO_ICMP - sock_define_const("IPPROTO_ICMP", IPPROTO_ICMP); -#else - sock_define_const("IPPROTO_ICMP", 1); -#endif -#ifdef IPPROTO_IGMP - sock_define_const("IPPROTO_IGMP", IPPROTO_IGMP); -#endif -#ifdef IPPROTO_GGP - sock_define_const("IPPROTO_GGP", IPPROTO_GGP); -#endif -#ifdef IPPROTO_TCP - sock_define_const("IPPROTO_TCP", IPPROTO_TCP); -#else - sock_define_const("IPPROTO_TCP", 6); -#endif -#ifdef IPPROTO_EGP - sock_define_const("IPPROTO_EGP", IPPROTO_EGP); -#endif -#ifdef IPPROTO_PUP - sock_define_const("IPPROTO_PUP", IPPROTO_PUP); -#endif -#ifdef IPPROTO_UDP - sock_define_const("IPPROTO_UDP", IPPROTO_UDP); -#else - sock_define_const("IPPROTO_UDP", 17); -#endif -#ifdef IPPROTO_IDP - sock_define_const("IPPROTO_IDP", IPPROTO_IDP); -#endif -#ifdef IPPROTO_HELLO - sock_define_const("IPPROTO_HELLO", IPPROTO_HELLO); -#endif -#ifdef IPPROTO_ND - sock_define_const("IPPROTO_ND", IPPROTO_ND); -#endif -#ifdef IPPROTO_TP - sock_define_const("IPPROTO_TP", IPPROTO_TP); -#endif -#ifdef IPPROTO_XTP - sock_define_const("IPPROTO_XTP", IPPROTO_XTP); -#endif -#ifdef IPPROTO_EON - sock_define_const("IPPROTO_EON", IPPROTO_EON); -#endif -#ifdef IPPROTO_BIP - sock_define_const("IPPROTO_BIP", IPPROTO_BIP); -#endif -/**/ -#ifdef IPPROTO_RAW - sock_define_const("IPPROTO_RAW", IPPROTO_RAW); -#else - sock_define_const("IPPROTO_RAW", 255); -#endif -#ifdef IPPROTO_MAX - sock_define_const("IPPROTO_MAX", IPPROTO_MAX); -#endif +VALUE +rsock_sockaddr_obj(struct sockaddr *addr, socklen_t len) +{ + return sockaddr_obj(addr, len); +} - /* Some port configuration */ -#ifdef IPPORT_RESERVED - sock_define_const("IPPORT_RESERVED", IPPORT_RESERVED); -#else - sock_define_const("IPPORT_RESERVED", 1024); #endif -#ifdef IPPORT_USERRESERVED - sock_define_const("IPPORT_USERRESERVED", IPPORT_USERRESERVED); -#else - sock_define_const("IPPORT_USERRESERVED", 5000); -#endif - /* Some reserved IP v.4 addresses */ -#ifdef INADDR_ANY - sock_define_const("INADDR_ANY", INADDR_ANY); -#else - sock_define_const("INADDR_ANY", 0x00000000); -#endif -#ifdef INADDR_BROADCAST - sock_define_const("INADDR_BROADCAST", INADDR_BROADCAST); -#else - sock_define_const("INADDR_BROADCAST", 0xffffffff); + +#if defined(HAVE_GETIFADDRS) || (defined(SIOCGLIFCONF) && defined(SIOCGLIFNUM)) || defined(SIOCGIFCONF) || defined(_WIN32) +/* + * call-seq: + * Socket.ip_address_list => array + * + * Returns local IP addresses as an array. + * + * The array contains Addrinfo objects. + * + * pp Socket.ip_address_list + * #=> [#<Addrinfo: 127.0.0.1>, + * #<Addrinfo: 192.168.0.128>, + * #<Addrinfo: ::1>, + * ...] + * + */ +static VALUE +socket_s_ip_address_list(VALUE self) +{ +#if defined(HAVE_GETIFADDRS) + struct ifaddrs *ifp = NULL; + struct ifaddrs *p; + int ret; + VALUE list; + + ret = getifaddrs(&ifp); + if (ret == -1) { + rb_sys_fail("getifaddrs"); + } + + list = rb_ary_new(); + for (p = ifp; p; p = p->ifa_next) { + if (p->ifa_addr != NULL && IS_IP_FAMILY(p->ifa_addr->sa_family)) { + struct sockaddr *addr = p->ifa_addr; +#if defined(AF_INET6) && defined(__sun) + /* + * OpenIndiana SunOS 5.11 getifaddrs() returns IPv6 link local + * address with sin6_scope_id == 0. + * So fill it from the interface name (ifa_name). + */ + struct sockaddr_in6 addr6; + if (addr->sa_family == AF_INET6) { + socklen_t len = (socklen_t)sizeof(struct sockaddr_in6); + memcpy(&addr6, addr, len); + addr = (struct sockaddr *)&addr6; + if (IN6_IS_ADDR_LINKLOCAL(&addr6.sin6_addr) && + addr6.sin6_scope_id == 0) { + unsigned int ifindex = if_nametoindex(p->ifa_name); + if (ifindex != 0) { + addr6.sin6_scope_id = ifindex; + } + } + } #endif -#ifdef INADDR_LOOPBACK - sock_define_const("INADDR_LOOPBACK", INADDR_LOOPBACK); + rb_ary_push(list, sockaddr_obj(addr, sockaddr_len(addr))); + } + } + + freeifaddrs(ifp); + + return list; +#elif defined(SIOCGLIFCONF) && defined(SIOCGLIFNUM) + /* Solaris if_tcp(7P) */ + int fd = -1; + int ret; + struct lifnum ln; + struct lifconf lc; + const char *reason = NULL; + int save_errno; + int i; + VALUE list = Qnil; + + lc.lifc_buf = NULL; + + fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd == -1) + rb_sys_fail("socket(2)"); + + memset(&ln, 0, sizeof(ln)); + ln.lifn_family = AF_UNSPEC; + + ret = ioctl(fd, SIOCGLIFNUM, &ln); + if (ret == -1) { + reason = "SIOCGLIFNUM"; + goto finish; + } + + memset(&lc, 0, sizeof(lc)); + lc.lifc_family = AF_UNSPEC; + lc.lifc_flags = 0; + lc.lifc_len = sizeof(struct lifreq) * ln.lifn_count; + lc.lifc_req = xmalloc(lc.lifc_len); + + ret = ioctl(fd, SIOCGLIFCONF, &lc); + if (ret == -1) { + reason = "SIOCGLIFCONF"; + goto finish; + } + + list = rb_ary_new(); + for (i = 0; i < ln.lifn_count; i++) { + struct lifreq *req = &lc.lifc_req[i]; + if (IS_IP_FAMILY(req->lifr_addr.ss_family)) { + if (req->lifr_addr.ss_family == AF_INET6 && + IN6_IS_ADDR_LINKLOCAL(&((struct sockaddr_in6 *)(&req->lifr_addr))->sin6_addr) && + ((struct sockaddr_in6 *)(&req->lifr_addr))->sin6_scope_id == 0) { + struct lifreq req2; + memcpy(req2.lifr_name, req->lifr_name, LIFNAMSIZ); + ret = ioctl(fd, SIOCGLIFINDEX, &req2); + if (ret == -1) { + reason = "SIOCGLIFINDEX"; + goto finish; + } + ((struct sockaddr_in6 *)(&req->lifr_addr))->sin6_scope_id = req2.lifr_index; + } + rb_ary_push(list, sockaddr_obj((struct sockaddr *)&req->lifr_addr, req->lifr_addrlen)); + } + } + + finish: + save_errno = errno; + xfree(lc.lifc_req); + if (fd != -1) + close(fd); + errno = save_errno; + + if (reason) + rb_syserr_fail(save_errno, reason); + return list; + +#elif defined(SIOCGIFCONF) + int fd = -1; + int ret; +#define EXTRA_SPACE ((int)(sizeof(struct ifconf) + sizeof(union_sockaddr))) + char initbuf[4096+EXTRA_SPACE]; + char *buf = initbuf; + int bufsize; + struct ifconf conf; + struct ifreq *req; + VALUE list = Qnil; + const char *reason = NULL; + int save_errno; + + fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd == -1) + rb_sys_fail("socket(2)"); + + bufsize = sizeof(initbuf); + buf = initbuf; + + retry: + conf.ifc_len = bufsize; + conf.ifc_req = (struct ifreq *)buf; + + /* fprintf(stderr, "bufsize: %d\n", bufsize); */ + + ret = ioctl(fd, SIOCGIFCONF, &conf); + if (ret == -1) { + reason = "SIOCGIFCONF"; + goto finish; + } + + /* fprintf(stderr, "conf.ifc_len: %d\n", conf.ifc_len); */ + + if (bufsize - EXTRA_SPACE < conf.ifc_len) { + if (bufsize < conf.ifc_len) { + /* NetBSD returns required size for all interfaces. */ + bufsize = conf.ifc_len + EXTRA_SPACE; + } + else { + bufsize = bufsize << 1; + } + if (buf == initbuf) + buf = NULL; + buf = xrealloc(buf, bufsize); + goto retry; + } + + close(fd); + fd = -1; + + list = rb_ary_new(); + req = conf.ifc_req; + while ((char*)req < (char*)conf.ifc_req + conf.ifc_len) { + struct sockaddr *addr = &req->ifr_addr; + if (IS_IP_FAMILY(addr->sa_family)) { + rb_ary_push(list, sockaddr_obj(addr, sockaddr_len(addr))); + } +#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN +# ifndef _SIZEOF_ADDR_IFREQ +# define _SIZEOF_ADDR_IFREQ(r) \ + (sizeof(struct ifreq) + \ + (sizeof(struct sockaddr) < (r).ifr_addr.sa_len ? \ + (r).ifr_addr.sa_len - sizeof(struct sockaddr) : \ + 0)) +# endif + req = (struct ifreq *)((char*)req + _SIZEOF_ADDR_IFREQ(*req)); #else - sock_define_const("INADDR_LOOPBACK", 0x7F000001); -#endif -#ifdef INADDR_UNSPEC_GROUP - sock_define_const("INADDR_UNSPEC_GROUP", INADDR_UNSPEC_GROUP); + req = (struct ifreq *)((char*)req + sizeof(struct ifreq)); +#endif + } + + finish: + + save_errno = errno; + if (buf != initbuf) + xfree(buf); + if (fd != -1) + close(fd); + errno = save_errno; + + if (reason) + rb_syserr_fail(save_errno, reason); + return list; + +#undef EXTRA_SPACE +#elif defined(_WIN32) + typedef struct ip_adapter_unicast_address_st { + unsigned LONG_LONG dummy0; + struct ip_adapter_unicast_address_st *Next; + struct { + struct sockaddr *lpSockaddr; + int iSockaddrLength; + } Address; + int dummy1; + int dummy2; + int dummy3; + long dummy4; + long dummy5; + long dummy6; + } ip_adapter_unicast_address_t; + typedef struct ip_adapter_anycast_address_st { + unsigned LONG_LONG dummy0; + struct ip_adapter_anycast_address_st *Next; + struct { + struct sockaddr *lpSockaddr; + int iSockaddrLength; + } Address; + } ip_adapter_anycast_address_t; + typedef struct ip_adapter_addresses_st { + unsigned LONG_LONG dummy0; + struct ip_adapter_addresses_st *Next; + void *dummy1; + ip_adapter_unicast_address_t *FirstUnicastAddress; + ip_adapter_anycast_address_t *FirstAnycastAddress; + void *dummy2; + void *dummy3; + void *dummy4; + void *dummy5; + void *dummy6; + BYTE dummy7[8]; + DWORD dummy8; + DWORD dummy9; + DWORD dummy10; + DWORD IfType; + int OperStatus; + DWORD dummy12; + DWORD dummy13[16]; + void *dummy14; + } ip_adapter_addresses_t; + typedef ULONG (WINAPI *GetAdaptersAddresses_t)(ULONG, ULONG, PVOID, ip_adapter_addresses_t *, PULONG); + HMODULE h; + GetAdaptersAddresses_t pGetAdaptersAddresses; + ULONG len; + DWORD ret; + ip_adapter_addresses_t *adapters; + VALUE list; + + h = LoadLibrary("iphlpapi.dll"); + if (!h) + rb_notimplement(); + pGetAdaptersAddresses = (GetAdaptersAddresses_t)GetProcAddress(h, "GetAdaptersAddresses"); + if (!pGetAdaptersAddresses) { + FreeLibrary(h); + rb_notimplement(); + } + + ret = pGetAdaptersAddresses(AF_UNSPEC, 0, NULL, NULL, &len); + if (ret != ERROR_SUCCESS && ret != ERROR_BUFFER_OVERFLOW) { + errno = rb_w32_map_errno(ret); + FreeLibrary(h); + rb_sys_fail("GetAdaptersAddresses"); + } + adapters = (ip_adapter_addresses_t *)ALLOCA_N(BYTE, len); + ret = pGetAdaptersAddresses(AF_UNSPEC, 0, NULL, adapters, &len); + if (ret != ERROR_SUCCESS) { + errno = rb_w32_map_errno(ret); + FreeLibrary(h); + rb_sys_fail("GetAdaptersAddresses"); + } + + list = rb_ary_new(); + for (; adapters; adapters = adapters->Next) { + ip_adapter_unicast_address_t *uni; + ip_adapter_anycast_address_t *any; + if (adapters->OperStatus != 1) /* 1 means IfOperStatusUp */ + continue; + for (uni = adapters->FirstUnicastAddress; uni; uni = uni->Next) { +#ifndef INET6 + if (uni->Address.lpSockaddr->sa_family == AF_INET) #else - sock_define_const("INADDR_UNSPEC_GROUP", 0xe0000000); + if (IS_IP_FAMILY(uni->Address.lpSockaddr->sa_family)) #endif -#ifdef INADDR_ALLHOSTS_GROUP - sock_define_const("INADDR_ALLHOSTS_GROUP", INADDR_ALLHOSTS_GROUP); + rb_ary_push(list, sockaddr_obj(uni->Address.lpSockaddr, uni->Address.iSockaddrLength)); + } + for (any = adapters->FirstAnycastAddress; any; any = any->Next) { +#ifndef INET6 + if (any->Address.lpSockaddr->sa_family == AF_INET) #else - sock_define_const("INADDR_ALLHOSTS_GROUP", 0xe0000001); + if (IS_IP_FAMILY(any->Address.lpSockaddr->sa_family)) #endif -#ifdef INADDR_MAX_LOCAL_GROUP - sock_define_const("INADDR_MAX_LOCAL_GROUP", INADDR_MAX_LOCAL_GROUP); -#else - sock_define_const("INADDR_MAX_LOCAL_GROUP", 0xe00000ff); + rb_ary_push(list, sockaddr_obj(any->Address.lpSockaddr, any->Address.iSockaddrLength)); + } + } + + FreeLibrary(h); + return list; #endif -#ifdef INADDR_NONE - sock_define_const("INADDR_NONE", INADDR_NONE); +} #else - sock_define_const("INADDR_NONE", 0xffffffff); -#endif - /* IP [gs]etsockopt options */ -#ifdef IP_OPTIONS - sock_define_const("IP_OPTIONS", IP_OPTIONS); -#endif -#ifdef IP_HDRINCL - sock_define_const("IP_HDRINCL", IP_HDRINCL); -#endif -#ifdef IP_TOS - sock_define_const("IP_TOS", IP_TOS); -#endif -#ifdef IP_TTL - sock_define_const("IP_TTL", IP_TTL); -#endif -#ifdef IP_RECVOPTS - sock_define_const("IP_RECVOPTS", IP_RECVOPTS); -#endif -#ifdef IP_RECVRETOPTS - sock_define_const("IP_RECVRETOPTS", IP_RECVRETOPTS); -#endif -#ifdef IP_RECVDSTADDR - sock_define_const("IP_RECVDSTADDR", IP_RECVDSTADDR); -#endif -#ifdef IP_RETOPTS - sock_define_const("IP_RETOPTS", IP_RETOPTS); -#endif -#ifdef IP_MULTICAST_IF - sock_define_const("IP_MULTICAST_IF", IP_MULTICAST_IF); -#endif -#ifdef IP_MULTICAST_TTL - sock_define_const("IP_MULTICAST_TTL", IP_MULTICAST_TTL); -#endif -#ifdef IP_MULTICAST_LOOP - sock_define_const("IP_MULTICAST_LOOP", IP_MULTICAST_LOOP); -#endif -#ifdef IP_ADD_MEMBERSHIP - sock_define_const("IP_ADD_MEMBERSHIP", IP_ADD_MEMBERSHIP); -#endif -#ifdef IP_DROP_MEMBERSHIP - sock_define_const("IP_DROP_MEMBERSHIP", IP_DROP_MEMBERSHIP); -#endif -#ifdef IP_DEFAULT_MULTICAST_TTL - sock_define_const("IP_DEFAULT_MULTICAST_TTL", IP_DEFAULT_MULTICAST_TTL); -#endif -#ifdef IP_DEFAULT_MULTICAST_LOOP - sock_define_const("IP_DEFAULT_MULTICAST_LOOP", IP_DEFAULT_MULTICAST_LOOP); -#endif -#ifdef IP_MAX_MEMBERSHIPS - sock_define_const("IP_MAX_MEMBERSHIPS", IP_MAX_MEMBERSHIPS); -#endif -#ifdef SO_DEBUG - sock_define_const("SO_DEBUG", SO_DEBUG); -#endif - sock_define_const("SO_REUSEADDR", SO_REUSEADDR); -#ifdef SO_REUSEPORT - sock_define_const("SO_REUSEPORT", SO_REUSEPORT); -#endif -#ifdef SO_TYPE - sock_define_const("SO_TYPE", SO_TYPE); -#endif -#ifdef SO_ERROR - sock_define_const("SO_ERROR", SO_ERROR); -#endif -#ifdef SO_DONTROUTE - sock_define_const("SO_DONTROUTE", SO_DONTROUTE); -#endif -#ifdef SO_BROADCAST - sock_define_const("SO_BROADCAST", SO_BROADCAST); -#endif -#ifdef SO_SNDBUF - sock_define_const("SO_SNDBUF", SO_SNDBUF); -#endif -#ifdef SO_RCVBUF - sock_define_const("SO_RCVBUF", SO_RCVBUF); -#endif -#ifdef SO_KEEPALIVE - sock_define_const("SO_KEEPALIVE", SO_KEEPALIVE); -#endif -#ifdef SO_OOBINLINE - sock_define_const("SO_OOBINLINE", SO_OOBINLINE); -#endif -#ifdef SO_NO_CHECK - sock_define_const("SO_NO_CHECK", SO_NO_CHECK); -#endif -#ifdef SO_PRIORITY - sock_define_const("SO_PRIORITY", SO_PRIORITY); -#endif -#ifdef SO_LINGER - sock_define_const("SO_LINGER", SO_LINGER); -#endif -#ifdef SO_PASSCRED - sock_define_const("SO_PASSCRED", SO_PASSCRED); -#endif -#ifdef SO_PEERCRED - sock_define_const("SO_PEERCRED", SO_PEERCRED); -#endif -#ifdef SO_RCVLOWAT - sock_define_const("SO_RCVLOWAT", SO_RCVLOWAT); -#endif -#ifdef SO_SNDLOWAT - sock_define_const("SO_SNDLOWAT", SO_SNDLOWAT); -#endif -#ifdef SO_RCVTIMEO - sock_define_const("SO_RCVTIMEO", SO_RCVTIMEO); -#endif -#ifdef SO_SNDTIMEO - sock_define_const("SO_SNDTIMEO", SO_SNDTIMEO); -#endif -#ifdef SO_ACCEPTCONN - sock_define_const("SO_ACCEPTCONN", SO_ACCEPTCONN); -#endif -#ifdef SO_USELOOPBACK - sock_define_const("SO_USELOOPBACK", SO_USELOOPBACK); -#endif -#ifdef SO_ACCEPTFILTER - sock_define_const("SO_ACCEPTFILTER", SO_ACCEPTFILTER); -#endif -#ifdef SO_DONTTRUNC - sock_define_const("SO_DONTTRUNC", SO_DONTTRUNC); -#endif -#ifdef SO_WANTMORE - sock_define_const("SO_WANTMORE", SO_WANTMORE); -#endif -#ifdef SO_WANTOOBFLAG - sock_define_const("SO_WANTOOBFLAG", SO_WANTOOBFLAG); -#endif -#ifdef SO_NREAD - sock_define_const("SO_NREAD", SO_NREAD); -#endif -#ifdef SO_NKE - sock_define_const("SO_NKE", SO_NKE); -#endif -#ifdef SO_NOSIGPIPE - sock_define_const("SO_NOSIGPIPE", SO_NOSIGPIPE); +#define socket_s_ip_address_list rb_f_notimplement #endif -#ifdef SO_SECURITY_AUTHENTICATION - sock_define_const("SO_SECURITY_AUTHENTICATION", SO_SECURITY_AUTHENTICATION); -#endif -#ifdef SO_SECURITY_ENCRYPTION_TRANSPORT - sock_define_const("SO_SECURITY_ENCRYPTION_TRANSPORT", SO_SECURITY_ENCRYPTION_TRANSPORT); -#endif -#ifdef SO_SECURITY_ENCRYPTION_NETWORK - sock_define_const("SO_SECURITY_ENCRYPTION_NETWORK", SO_SECURITY_ENCRYPTION_NETWORK); -#endif +/* + * call-seq: + * Socket.tcp_fast_fallback -> true or false + * + * Returns whether Happy Eyeballs Version 2 ({RFC 8305}[https://datatracker.ietf.org/doc/html/rfc8305]), + * which is provided starting from Ruby 3.4 when using TCPSocket.new and Socket.tcp, + * is enabled or disabled. + * + * If true, it is enabled for TCPSocket.new and Socket.tcp. + * (Note: Happy Eyeballs Version 2 is not provided when using TCPSocket.new on Windows.) + * + * If false, Happy Eyeballs Version 2 is disabled. + * + * For details on Happy Eyeballs Version 2, + * see {Socket.tcp_fast_fallback=}[rdoc-ref:Socket.tcp_fast_fallback=]. + */ +VALUE socket_s_tcp_fast_fallback(VALUE self) { + return rb_ivar_get(rb_cSocket, tcp_fast_fallback); +} -#ifdef SO_BINDTODEVICE - sock_define_const("SO_BINDTODEVICE", SO_BINDTODEVICE); -#endif -#ifdef SO_ATTACH_FILTER - sock_define_const("SO_ATTACH_FILTER", SO_ATTACH_FILTER); -#endif -#ifdef SO_DETACH_FILTER - sock_define_const("SO_DETACH_FILTER", SO_DETACH_FILTER); -#endif -#ifdef SO_PEERNAME - sock_define_const("SO_PEERNAME", SO_PEERNAME); -#endif -#ifdef SO_TIMESTAMP - sock_define_const("SO_TIMESTAMP", SO_TIMESTAMP); -#endif +/* + * call-seq: + * Socket.tcp_fast_fallback= -> true or false + * + * Enable or disable Happy Eyeballs Version 2 ({RFC 8305}[https://datatracker.ietf.org/doc/html/rfc8305]) + * globally, which is provided starting from Ruby 3.4 when using TCPSocket.new and Socket.tcp. + * + * When set to true, the feature is enabled for both `TCPSocket.new` and `Socket.tcp`. + * (Note: This feature is not available when using TCPSocket.new on Windows.) + * + * When set to false, the behavior reverts to that of Ruby 3.3 or earlier. + * + * The default value is true if no value is explicitly set by calling this method. + * However, when the environment variable RUBY_TCP_NO_FAST_FALLBACK=1 is set, + * the default is false. + * + * To control the setting on a per-method basis, use the fast_fallback keyword argument for each method. + * + * === Happy Eyeballs Version 2 + * Happy Eyeballs Version 2 ({RFC 8305}[https://datatracker.ietf.org/doc/html/rfc8305]) + * is an algorithm designed to improve client socket connectivity.<br> + * It aims for more reliable and efficient connections by performing hostname resolution + * and connection attempts in parallel, instead of serially. + * + * Starting from Ruby 3.4, this method operates as follows with this algorithm: + * + * 1. Start resolving both IPv6 and IPv4 addresses concurrently. + * 2. Start connecting to the one of the addresses that are obtained first.<br>If IPv4 addresses are obtained first, + * the method waits 50 ms for IPv6 name resolution to prioritize IPv6 connections. + * 3. After starting a connection attempt, wait 250 ms for the connection to be established.<br> + * If no connection is established within this time, a new connection is started every 250 ms<br> + * until a connection is established or there are no more candidate addresses.<br> + * (Although RFC 8305 strictly specifies sorting addresses,<br> + * this method only alternates between IPv6 / IPv4 addresses due to the performance concerns) + * 4. Once a connection is established, all remaining connection attempts are canceled. + */ +VALUE socket_s_tcp_fast_fallback_set(VALUE self, VALUE value) { + rb_ivar_set(rb_cSocket, tcp_fast_fallback, value); + return value; +} -#ifdef SOPRI_INTERACTIVE - sock_define_const("SOPRI_INTERACTIVE", SOPRI_INTERACTIVE); -#endif -#ifdef SOPRI_NORMAL - sock_define_const("SOPRI_NORMAL", SOPRI_NORMAL); -#endif -#ifdef SOPRI_BACKGROUND - sock_define_const("SOPRI_BACKGROUND", SOPRI_BACKGROUND); -#endif +void +Init_socket(void) +{ + rb_ext_ractor_safe(true); -#ifdef IPX_TYPE - sock_define_const("IPX_TYPE", IPX_TYPE); -#endif + rsock_init_basicsocket(); -#ifdef TCP_NODELAY - sock_define_const("TCP_NODELAY", TCP_NODELAY); -#endif -#ifdef TCP_MAXSEG - sock_define_const("TCP_MAXSEG", TCP_MAXSEG); -#endif + /* + * Document-class: Socket < BasicSocket + * + * Class +Socket+ provides access to the underlying operating system + * socket implementations. It can be used to provide more operating system + * specific functionality than the protocol-specific socket classes. + * + * The constants defined under Socket::Constants are also defined under + * Socket. For example, Socket::AF_INET is usable as well as + * Socket::Constants::AF_INET. See Socket::Constants for the list of + * constants. + * + * === What's a socket? + * + * Sockets are endpoints of a bidirectional communication channel. + * Sockets can communicate within a process, between processes on the same + * machine or between different machines. There are many types of socket: + * TCPSocket, UDPSocket or UNIXSocket for example. + * + * Sockets have their own vocabulary: + * + * *domain:* + * The family of protocols: + * * Socket::PF_INET + * * Socket::PF_INET6 + * * Socket::PF_UNIX + * * etc. + * + * *type:* + * The type of communications between the two endpoints, typically + * * Socket::SOCK_STREAM + * * Socket::SOCK_DGRAM. + * + * *protocol:* + * Typically _zero_. + * This may be used to identify a variant of a protocol. + * + * *hostname:* + * The identifier of a network interface: + * * a string (hostname, IPv4 or IPv6 address or +broadcast+ + * which specifies a broadcast address) + * * a zero-length string which specifies INADDR_ANY + * * an integer (interpreted as binary address in host byte order). + * + * === Quick start + * + * Many of the classes, such as TCPSocket, UDPSocket or UNIXSocket, + * ease the use of sockets comparatively to the equivalent C programming interface. + * + * Let's create an internet socket using the IPv4 protocol in a C-like manner: + * + * require 'socket' + * + * s = Socket.new Socket::AF_INET, Socket::SOCK_STREAM + * s.connect Socket.pack_sockaddr_in(80, 'example.com') + * + * You could also use the TCPSocket class: + * + * s = TCPSocket.new 'example.com', 80 + * + * A simple server might look like this: + * + * require 'socket' + * + * server = TCPServer.new 2000 # Server bound to port 2000 + * + * loop do + * client = server.accept # Wait for a client to connect + * client.puts "Hello !" + * client.puts "Time is #{Time.now}" + * client.close + * end + * + * A simple client may look like this: + * + * require 'socket' + * + * s = TCPSocket.new 'localhost', 2000 + * + * while line = s.gets # Read lines from socket + * puts line # and print them + * end + * + * s.close # close socket when done + * + * === Exception Handling + * + * Ruby's Socket implementation raises exceptions based on the error + * generated by the system dependent implementation. This is why the + * methods are documented in a way that isolate Unix-based system + * exceptions from Windows based exceptions. If more information on a + * particular exception is needed, please refer to the Unix manual pages or + * the Windows WinSock reference. + * + * === Convenience methods + * + * Although the general way to create socket is Socket.new, + * there are several methods of socket creation for most cases. + * + * TCP client socket:: + * Socket.tcp, TCPSocket.open + * TCP server socket:: + * Socket.tcp_server_loop, TCPServer.open + * UNIX client socket:: + * Socket.unix, UNIXSocket.open + * UNIX server socket:: + * Socket.unix_server_loop, UNIXServer.open + * + * === Documentation by + * + * * Zach Dennis + * * Sam Roberts + * * <em>Programming Ruby</em> from The Pragmatic Bookshelf. + * + * Much material in this documentation is taken with permission from + * <em>Programming Ruby</em> from The Pragmatic Bookshelf. + */ + rb_cSocket = rb_define_class("Socket", rb_cBasicSocket); -#ifdef EAI_ADDRFAMILY - sock_define_const("EAI_ADDRFAMILY", EAI_ADDRFAMILY); -#endif -#ifdef EAI_AGAIN - sock_define_const("EAI_AGAIN", EAI_AGAIN); -#endif -#ifdef EAI_BADFLAGS - sock_define_const("EAI_BADFLAGS", EAI_BADFLAGS); -#endif -#ifdef EAI_FAIL - sock_define_const("EAI_FAIL", EAI_FAIL); -#endif -#ifdef EAI_FAMILY - sock_define_const("EAI_FAMILY", EAI_FAMILY); -#endif -#ifdef EAI_MEMORY - sock_define_const("EAI_MEMORY", EAI_MEMORY); -#endif -#ifdef EAI_NODATA - sock_define_const("EAI_NODATA", EAI_NODATA); -#endif -#ifdef EAI_NONAME - sock_define_const("EAI_NONAME", EAI_NONAME); -#endif -#ifdef EAI_SERVICE - sock_define_const("EAI_SERVICE", EAI_SERVICE); -#endif -#ifdef EAI_SOCKTYPE - sock_define_const("EAI_SOCKTYPE", EAI_SOCKTYPE); -#endif -#ifdef EAI_SYSTEM - sock_define_const("EAI_SYSTEM", EAI_SYSTEM); -#endif -#ifdef EAI_BADHINTS - sock_define_const("EAI_BADHINTS", EAI_BADHINTS); -#endif -#ifdef EAI_PROTOCOL - sock_define_const("EAI_PROTOCOL", EAI_PROTOCOL); -#endif -#ifdef EAI_MAX - sock_define_const("EAI_MAX", EAI_MAX); -#endif -#ifdef AI_PASSIVE - sock_define_const("AI_PASSIVE", AI_PASSIVE); -#endif -#ifdef AI_CANONNAME - sock_define_const("AI_CANONNAME", AI_CANONNAME); -#endif -#ifdef AI_NUMERICHOST - sock_define_const("AI_NUMERICHOST", AI_NUMERICHOST); -#endif -#ifdef AI_MASK - sock_define_const("AI_MASK", AI_MASK); -#endif -#ifdef AI_ALL - sock_define_const("AI_ALL", AI_ALL); -#endif -#ifdef AI_V4MAPPED_CFG - sock_define_const("AI_V4MAPPED_CFG", AI_V4MAPPED_CFG); -#endif -#ifdef AI_ADDRCONFIG - sock_define_const("AI_ADDRCONFIG", AI_ADDRCONFIG); -#endif -#ifdef AI_V4MAPPED - sock_define_const("AI_V4MAPPED", AI_V4MAPPED); -#endif -#ifdef AI_DEFAULT - sock_define_const("AI_DEFAULT", AI_DEFAULT); -#endif -#ifdef NI_MAXHOST - sock_define_const("NI_MAXHOST", NI_MAXHOST); -#endif -#ifdef NI_MAXSERV - sock_define_const("NI_MAXSERV", NI_MAXSERV); -#endif -#ifdef NI_NOFQDN - sock_define_const("NI_NOFQDN", NI_NOFQDN); -#endif -#ifdef NI_NUMERICHOST - sock_define_const("NI_NUMERICHOST", NI_NUMERICHOST); -#endif -#ifdef NI_NAMEREQD - sock_define_const("NI_NAMEREQD", NI_NAMEREQD); -#endif -#ifdef NI_NUMERICSERV - sock_define_const("NI_NUMERICSERV", NI_NUMERICSERV); -#endif -#ifdef NI_DGRAM - sock_define_const("NI_DGRAM", NI_DGRAM); -#endif -#ifdef SHUT_RD - sock_define_const("SHUT_RD", SHUT_RD); -#else - sock_define_const("SHUT_RD", 0); -#endif -#ifdef SHUT_WR - sock_define_const("SHUT_WR", SHUT_WR); -#else - sock_define_const("SHUT_WR", 1); -#endif -#ifdef SHUT_RDWR - sock_define_const("SHUT_RDWR", SHUT_RDWR); -#else - sock_define_const("SHUT_RDWR", 2); + rsock_init_socket_init(); + + const char *tcp_no_fast_fallback_config = getenv("RUBY_TCP_NO_FAST_FALLBACK"); + VALUE fast_fallback_default; + if (tcp_no_fast_fallback_config == NULL || strcmp(tcp_no_fast_fallback_config, "0") == 0) { + fast_fallback_default = Qtrue; + } else { + fast_fallback_default = Qfalse; + } + tcp_fast_fallback = rb_intern_const("tcp_fast_fallback"); + rb_ivar_set(rb_cSocket, tcp_fast_fallback, fast_fallback_default); + + rb_define_method(rb_cSocket, "initialize", sock_initialize, -1); + rb_define_method(rb_cSocket, "connect", sock_connect, 1); + + /* for ext/socket/lib/socket.rb use only: */ + rb_define_private_method(rb_cSocket, + "__connect_nonblock", sock_connect_nonblock, 2); + + rb_define_method(rb_cSocket, "bind", sock_bind, 1); + rb_define_method(rb_cSocket, "listen", rsock_sock_listen, 1); + rb_define_method(rb_cSocket, "accept", sock_accept, 0); + + /* for ext/socket/lib/socket.rb use only: */ + rb_define_private_method(rb_cSocket, + "__accept_nonblock", sock_accept_nonblock, 1); + + rb_define_method(rb_cSocket, "sysaccept", sock_sysaccept, 0); + + rb_define_method(rb_cSocket, "recvfrom", sock_recvfrom, -1); + + /* for ext/socket/lib/socket.rb use only: */ + rb_define_private_method(rb_cSocket, + "__recvfrom_nonblock", sock_recvfrom_nonblock, 4); + + rb_define_singleton_method(rb_cSocket, "socketpair", rsock_sock_s_socketpair, -1); + rb_define_singleton_method(rb_cSocket, "pair", rsock_sock_s_socketpair, -1); + rb_define_singleton_method(rb_cSocket, "gethostname", sock_gethostname, 0); + rb_define_singleton_method(rb_cSocket, "gethostbyname", sock_s_gethostbyname, 1); + rb_define_singleton_method(rb_cSocket, "gethostbyaddr", sock_s_gethostbyaddr, -1); + rb_define_singleton_method(rb_cSocket, "getservbyname", sock_s_getservbyname, -1); + rb_define_singleton_method(rb_cSocket, "getservbyport", sock_s_getservbyport, -1); + rb_define_singleton_method(rb_cSocket, "getaddrinfo", sock_s_getaddrinfo, -1); + rb_define_singleton_method(rb_cSocket, "getnameinfo", sock_s_getnameinfo, -1); + rb_define_singleton_method(rb_cSocket, "sockaddr_in", sock_s_pack_sockaddr_in, 2); + rb_define_singleton_method(rb_cSocket, "pack_sockaddr_in", sock_s_pack_sockaddr_in, 2); + rb_define_singleton_method(rb_cSocket, "unpack_sockaddr_in", sock_s_unpack_sockaddr_in, 1); +#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN + rb_define_singleton_method(rb_cSocket, "sockaddr_un", sock_s_pack_sockaddr_un, 1); + rb_define_singleton_method(rb_cSocket, "pack_sockaddr_un", sock_s_pack_sockaddr_un, 1); + rb_define_singleton_method(rb_cSocket, "unpack_sockaddr_un", sock_s_unpack_sockaddr_un, 1); #endif + + rb_define_singleton_method(rb_cSocket, "ip_address_list", socket_s_ip_address_list, 0); + + rb_define_singleton_method(rb_cSocket, "tcp_fast_fallback", socket_s_tcp_fast_fallback, 0); + rb_define_singleton_method(rb_cSocket, "tcp_fast_fallback=", socket_s_tcp_fast_fallback_set, 1); + +#undef rb_intern + sym_wait_writable = ID2SYM(rb_intern("wait_writable")); } diff --git a/ext/socket/sockport.h b/ext/socket/sockport.h index e1cddf53f4..2b58958ae7 100644 --- a/ext/socket/sockport.h +++ b/ext/socket/sockport.h @@ -3,7 +3,6 @@ sockport.h - $Author$ - $Date$ created at: Fri Apr 30 23:19:34 JST 1999 ************************************************/ @@ -11,33 +10,74 @@ #ifndef SOCKPORT_H #define SOCKPORT_H -#ifndef SA_LEN -# ifdef HAVE_SA_LEN -# define SA_LEN(sa) (sa)->sa_len -# else -# ifdef INET6 -# define SA_LEN(sa) \ - (((sa)->sa_family == AF_INET6) ? sizeof(struct sockaddr_in6) \ - : sizeof(struct sockaddr)) -# else - /* by tradition, sizeof(struct sockaddr) covers most of the sockaddrs */ -# define SA_LEN(sa) (sizeof(struct sockaddr)) -# endif -# endif +#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN +# define VALIDATE_SOCKLEN(addr, len) ((addr)->sa_len == (len)) +#else +# define VALIDATE_SOCKLEN(addr, len) ((void)(addr), (void)(len), 1) +#endif + +#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN +# define SET_SA_LEN(sa, len) (void)((sa)->sa_len = (len)) +#else +# define SET_SA_LEN(sa, len) (void)(len) #endif -#ifdef HAVE_SA_LEN -# define SET_SA_LEN(sa, len) (sa)->sa_len = (len) +/* for strict-aliasing rule */ +#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN +# define SET_SIN_LEN(sa, len) (void)((sa)->sin_len = (len)) #else -# define SET_SA_LEN(sa, len) (len) +# define SET_SIN_LEN(sa, len) SET_SA_LEN((struct sockaddr *)(sa), (len)) #endif -#ifdef HAVE_SIN_LEN -# define SIN_LEN(si) (si)->sin_len -# define SET_SIN_LEN(si,len) (si)->sin_len = (len) +#ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_LEN +# define SET_SIN6_LEN(sa, len) (void)((sa)->sin6_len = (len)) #else -# define SIN_LEN(si) sizeof(struct sockaddr_in) -# define SET_SIN_LEN(si,len) +# define SET_SIN6_LEN(sa, len) SET_SA_LEN((struct sockaddr *)(sa), (len)) +#endif + +#define INIT_SOCKADDR(addr, family, len) \ + do { \ + struct sockaddr *init_sockaddr_ptr = (addr); \ + socklen_t init_sockaddr_len = (len); \ + memset(init_sockaddr_ptr, 0, init_sockaddr_len); \ + init_sockaddr_ptr->sa_family = (family); \ + SET_SA_LEN(init_sockaddr_ptr, init_sockaddr_len); \ + } while (0) + +#define INIT_SOCKADDR_IN(addr, len) \ + do { \ + struct sockaddr_in *init_sockaddr_ptr = (addr); \ + socklen_t init_sockaddr_len = (len); \ + memset(init_sockaddr_ptr, 0, init_sockaddr_len); \ + init_sockaddr_ptr->sin_family = AF_INET; \ + SET_SIN_LEN(init_sockaddr_ptr, init_sockaddr_len); \ + } while (0) + +#define INIT_SOCKADDR_IN6(addr, len) \ + do { \ + struct sockaddr_in6 *init_sockaddr_ptr = (addr); \ + socklen_t init_sockaddr_len = (len); \ + memset(init_sockaddr_ptr, 0, init_sockaddr_len); \ + init_sockaddr_ptr->sin6_family = AF_INET6; \ + SET_SIN6_LEN(init_sockaddr_ptr, init_sockaddr_len); \ + } while (0) + + +/* for strict-aliasing rule */ +#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN +# ifdef HAVE_STRUCT_SOCKADDR_IN_SUN_LEN +# define SET_SUN_LEN(sa, len) (void)((sa)->sun_len = (len)) +# else +# define SET_SUN_LEN(sa, len) SET_SA_LEN((struct sockaddr *)(sa), (len)) +# endif +# define INIT_SOCKADDR_UN(addr, len) \ + do { \ + struct sockaddr_un *init_sockaddr_ptr = (addr); \ + socklen_t init_sockaddr_len = (len); \ + memset(init_sockaddr_ptr, 0, init_sockaddr_len); \ + init_sockaddr_ptr->sun_family = AF_UNIX; \ + SET_SUN_LEN(init_sockaddr_ptr, init_sockaddr_len); \ + } while (0) #endif #ifndef IN_MULTICAST diff --git a/ext/socket/sockssocket.c b/ext/socket/sockssocket.c new file mode 100644 index 0000000000..30860ea257 --- /dev/null +++ b/ext/socket/sockssocket.c @@ -0,0 +1,75 @@ +/************************************************ + + sockssocket.c - + + created at: Thu Mar 31 12:21:29 JST 1994 + + Copyright (C) 1993-2007 Yukihiro Matsumoto + +************************************************/ + +#include "rubysocket.h" + +#ifdef SOCKS +/* + * call-seq: + * SOCKSSocket.new(host, port) => socket + * + * Opens a SOCKS connection to +host+ via the SOCKS server. + * + * The SOCKS server configuration varies by implementation + * + * When using the Dante libsocks/libsocksd implementation it is configured as SOCKS_SERVER env var. + * + * See: https://manpages.debian.org/testing/dante-client/socksify.1.en.html for full env variable support. + * + */ +static VALUE +socks_init(VALUE sock, VALUE host, VALUE port) +{ + static int init = 0; + + if (init == 0) { + char progname[] = "ruby"; + SOCKSinit(progname); + init = 1; + } + + return rsock_init_inetsock(sock, host, port, Qnil, Qnil, INET_SOCKS, Qnil, Qnil, Qnil, Qfalse, Qnil); +} + +#ifdef SOCKS5 +/* + * Closes the SOCKS connection. + * + */ +static VALUE +socks_s_close(VALUE sock) +{ + rb_io_t *fptr; + + GetOpenFile(sock, fptr); + shutdown(fptr->fd, SHUT_RDWR); + return rb_io_close(sock); +} +#endif +#endif + +void +rsock_init_sockssocket(void) +{ +#ifdef SOCKS + /* + * Document-class: SOCKSSocket < TCPSocket + * + * SOCKS is an Internet protocol that routes packets between a client and + * a server through a proxy server. SOCKS5, if supported, additionally + * provides authentication so only authorized users may access a server. + */ + rb_cSOCKSSocket = rb_define_class("SOCKSSocket", rb_cTCPSocket); + rb_define_method(rb_cSOCKSSocket, "initialize", socks_init, 2); +#ifdef SOCKS5 + rb_define_method(rb_cSOCKSSocket, "close", socks_s_close, 0); +#endif +#endif +} diff --git a/ext/socket/tcpserver.c b/ext/socket/tcpserver.c new file mode 100644 index 0000000000..0069f3c703 --- /dev/null +++ b/ext/socket/tcpserver.c @@ -0,0 +1,140 @@ +/************************************************ + + tcpserver.c - + + created at: Thu Mar 31 12:21:29 JST 1994 + + Copyright (C) 1993-2007 Yukihiro Matsumoto + +************************************************/ + +#include "rubysocket.h" + +/* + * 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 + * + * Internally, TCPServer.new calls getaddrinfo() function to + * obtain addresses. + * If getaddrinfo() returns multiple addresses, + * TCPServer.new tries to create a server socket for each address + * and returns first one that is successful. + * + */ +static VALUE +tcp_svr_init(int argc, VALUE *argv, VALUE sock) +{ + VALUE hostname, port; + + rb_scan_args(argc, argv, "011", &hostname, &port); + return rsock_init_inetsock(sock, hostname, port, Qnil, Qnil, INET_SERVER, Qnil, Qnil, Qnil, Qfalse, Qnil); +} + +/* + * call-seq: + * tcpserver.accept => tcpsocket + * + * Accepts an incoming connection. It returns a new TCPSocket object. + * + * TCPServer.open("127.0.0.1", 14641) {|serv| + * s = serv.accept + * s.puts Time.now + * s.close + * } + * + */ +static VALUE +tcp_accept(VALUE server) +{ + union_sockaddr buffer; + socklen_t length = sizeof(buffer); + + return rsock_s_accept(rb_cTCPSocket, server, &buffer.addr, &length); +} + +/* :nodoc: */ +static VALUE +tcp_accept_nonblock(VALUE sock, VALUE ex) +{ + rb_io_t *fptr; + union_sockaddr from; + socklen_t len = (socklen_t)sizeof(from); + + GetOpenFile(sock, fptr); + return rsock_s_accept_nonblock(rb_cTCPSocket, ex, fptr, &from.addr, &len); +} + +/* + * call-seq: + * tcpserver.sysaccept => file_descriptor + * + * Returns a file descriptor of a accepted connection. + * + * TCPServer.open("127.0.0.1", 28561) {|serv| + * fd = serv.sysaccept + * s = IO.for_fd(fd) + * s.puts Time.now + * s.close + * } + * + */ +static VALUE +tcp_sysaccept(VALUE server) +{ + union_sockaddr buffer; + socklen_t length = sizeof(buffer); + + return rsock_s_accept(0, server, &buffer.addr, &length); +} + +void +rsock_init_tcpserver(void) +{ + /* + * Document-class: TCPServer < TCPSocket + * + * TCPServer represents a TCP/IP server socket. + * + * A simple TCP server may look like: + * + * require 'socket' + * + * server = TCPServer.new 2000 # Server bind to port 2000 + * loop do + * client = server.accept # Wait for a client to connect + * client.puts "Hello !" + * client.puts "Time is #{Time.now}" + * client.close + * end + * + * A more usable server (serving multiple clients): + * + * require 'socket' + * + * server = TCPServer.new 2000 + * loop do + * Thread.start(server.accept) do |client| + * client.puts "Hello !" + * client.puts "Time is #{Time.now}" + * client.close + * end + * end + * + */ + rb_cTCPServer = rb_define_class("TCPServer", rb_cTCPSocket); + rb_define_method(rb_cTCPServer, "accept", tcp_accept, 0); + rb_define_private_method(rb_cTCPServer, + "__accept_nonblock", tcp_accept_nonblock, 1); + rb_define_method(rb_cTCPServer, "sysaccept", tcp_sysaccept, 0); + rb_define_method(rb_cTCPServer, "initialize", tcp_svr_init, -1); + rb_define_method(rb_cTCPServer, "listen", rsock_sock_listen, 1); /* in socket.c */ +} diff --git a/ext/socket/tcpsocket.c b/ext/socket/tcpsocket.c new file mode 100644 index 0000000000..7ce536e0af --- /dev/null +++ b/ext/socket/tcpsocket.c @@ -0,0 +1,144 @@ +/************************************************ + + tcpsocket.c - + + created at: Thu Mar 31 12:21:29 JST 1994 + + Copyright (C) 1993-2007 Yukihiro Matsumoto + +************************************************/ + +#include "rubysocket.h" + +/* + * call-seq: + * TCPSocket.new(remote_host, remote_port, local_host=nil, local_port=nil, resolv_timeout: nil, connect_timeout: nil, open_timeout: nil, fast_fallback: true) + * + * 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. + * + * Starting from Ruby 3.4, this method operates according to the + * Happy Eyeballs Version 2 ({RFC 8305}[https://datatracker.ietf.org/doc/html/rfc8305]) + * algorithm by default, except on Windows. + * + * For details on Happy Eyeballs Version 2, + * see {Socket.tcp_fast_fallback=}[rdoc-ref:Socket.tcp_fast_fallback=]. + * + * To make it behave the same as in Ruby 3.3 and earlier, + * explicitly specify the option fast_fallback:false. + * Or, setting Socket.tcp_fast_fallback=false will disable + * Happy Eyeballs Version 2 not only for this method but for all Socket globally. + * + * When using TCPSocket.new on Windows, Happy Eyeballs Version 2 is not provided, + * and it behaves the same as in Ruby 3.3 and earlier. + * + * [:resolv_timeout] Specifies the timeout in seconds from when the hostname resolution starts. + * [:connect_timeout] This method sequentially attempts connecting to all candidate destination addresses.<br>The +connect_timeout+ specifies the timeout in seconds from the start of the connection attempt to the last candidate.<br>By default, all connection attempts continue until the timeout occurs.<br>When +fast_fallback:false+ is explicitly specified,<br>a timeout is set for each connection attempt and any connection attempt that exceeds its timeout will be canceled. + * [:open_timeout] Specifies the timeout in seconds from the start of the method execution.<br>If this timeout is reached while there are still addresses that have not yet been attempted for connection, no further attempts will be made.<br>If this option is specified together with other timeout options, an +ArgumentError+ will be raised. + * [:fast_fallback] Enables the Happy Eyeballs Version 2 algorithm (enabled by default). + */ +static VALUE +tcp_init(int argc, VALUE *argv, VALUE sock) +{ + VALUE remote_host, remote_serv; + VALUE local_host, local_serv; + VALUE opt; + static ID keyword_ids[5]; + VALUE kwargs[5]; + VALUE resolv_timeout = Qnil; + VALUE connect_timeout = Qnil; + VALUE open_timeout = Qnil; + VALUE fast_fallback = Qnil; + VALUE test_mode_settings = Qnil; + + if (!keyword_ids[0]) { + CONST_ID(keyword_ids[0], "resolv_timeout"); + CONST_ID(keyword_ids[1], "connect_timeout"); + CONST_ID(keyword_ids[2], "open_timeout"); + CONST_ID(keyword_ids[3], "fast_fallback"); + CONST_ID(keyword_ids[4], "test_mode_settings"); + } + + rb_scan_args(argc, argv, "22:", &remote_host, &remote_serv, + &local_host, &local_serv, &opt); + + if (!NIL_P(opt)) { + rb_get_kwargs(opt, keyword_ids, 0, 5, kwargs); + if (kwargs[0] != Qundef) { resolv_timeout = kwargs[0]; } + if (kwargs[1] != Qundef) { connect_timeout = kwargs[1]; } + if (kwargs[2] != Qundef) { open_timeout = kwargs[2]; } + if (kwargs[3] != Qundef) { fast_fallback = kwargs[3]; } + if (kwargs[4] != Qundef) { test_mode_settings = kwargs[4]; } + } + + if (fast_fallback == Qnil) { + fast_fallback = rb_ivar_get(rb_cSocket, tcp_fast_fallback); + if (fast_fallback == Qnil) fast_fallback = Qtrue; + } + + return rsock_init_inetsock(sock, remote_host, remote_serv, + local_host, local_serv, INET_CLIENT, + resolv_timeout, connect_timeout, open_timeout, + fast_fallback, test_mode_settings); +} + +static VALUE +tcp_sockaddr(struct sockaddr *addr, socklen_t len) +{ + return rsock_make_ipaddr(addr, len); +} + +/* + * call-seq: + * TCPSocket.gethostbyname(hostname) => [official_hostname, alias_hostnames, address_family, *address_list] + * + * Use Addrinfo.getaddrinfo instead. + * This method is deprecated for the following reasons: + * + * - The 3rd element of the result is the address family of the first address. + * The address families of the rest of the addresses are not returned. + * - gethostbyname() may take a long time and it may block other threads. + * (GVL cannot be released since gethostbyname() is not thread safe.) + * - This method uses gethostbyname() function already removed from POSIX. + * + * This method lookups host information by _hostname_. + * + * TCPSocket.gethostbyname("localhost") + * #=> ["localhost", ["hal"], 2, "127.0.0.1"] + * + */ +static VALUE +tcp_s_gethostbyname(VALUE obj, VALUE host) +{ + rb_warn("TCPSocket.gethostbyname is deprecated; use Addrinfo.getaddrinfo instead."); + struct rb_addrinfo *res = + rsock_addrinfo(host, Qnil, AF_UNSPEC, SOCK_STREAM, AI_CANONNAME, Qnil); + return rsock_make_hostent(host, res, tcp_sockaddr); +} + +void +rsock_init_tcpsocket(void) +{ + /* + * Document-class: TCPSocket < IPSocket + * + * TCPSocket represents a TCP/IP client socket. + * + * A simple client may look like: + * + * require 'socket' + * + * s = TCPSocket.new 'localhost', 2000 + * + * while line = s.gets # Read lines from socket + * puts line # and print them + * end + * + * s.close # close socket when done + * + */ + rb_cTCPSocket = rb_define_class("TCPSocket", rb_cIPSocket); + rb_define_singleton_method(rb_cTCPSocket, "gethostbyname", tcp_s_gethostbyname, 1); + rb_define_method(rb_cTCPSocket, "initialize", tcp_init, -1); +} diff --git a/ext/socket/udpsocket.c b/ext/socket/udpsocket.c new file mode 100644 index 0000000000..b2bc925538 --- /dev/null +++ b/ext/socket/udpsocket.c @@ -0,0 +1,247 @@ +/************************************************ + + udpsocket.c - + + created at: Thu Mar 31 12:21:29 JST 1994 + + Copyright (C) 1993-2007 Yukihiro Matsumoto + +************************************************/ + +#include "rubysocket.h" + +/* + * call-seq: + * UDPSocket.new([address_family]) => socket + * + * Creates a new UDPSocket object. + * + * _address_family_ should be an integer, a string or a symbol: + * Socket::AF_INET, "AF_INET", :INET, etc. + * + * require 'socket' + * + * UDPSocket.new #=> #<UDPSocket:fd 3> + * UDPSocket.new(Socket::AF_INET6) #=> #<UDPSocket:fd 4> + * + */ +static VALUE +udp_init(int argc, VALUE *argv, VALUE sock) +{ + VALUE arg; + int family = AF_INET; + int fd; + + if (rb_scan_args(argc, argv, "01", &arg) == 1) { + family = rsock_family_arg(arg); + } + fd = rsock_socket(family, SOCK_DGRAM, 0); + if (fd < 0) { + rb_sys_fail("socket(2) - udp"); + } + + return rsock_init_sock(sock, fd); +} + +struct udp_arg +{ + VALUE io; + struct rb_addrinfo *res; +}; + +static VALUE +udp_connect_internal(VALUE v) +{ + struct udp_arg *arg = (void *)v; + struct addrinfo *res; + + for (res = arg->res->ai; res; res = res->ai_next) { + if (rsock_connect(arg->io, res->ai_addr, res->ai_addrlen, 0, RUBY_IO_TIMEOUT_DEFAULT) >= 0) { + return Qtrue; + } + } + return Qfalse; +} + +/* + * call-seq: + * udpsocket.connect(host, port) => 0 + * + * Connects _udpsocket_ to _host_:_port_. + * + * This makes possible to send without destination address. + * + * u1 = UDPSocket.new + * u1.bind("127.0.0.1", 4913) + * u2 = UDPSocket.new + * u2.connect("127.0.0.1", 4913) + * u2.send "uuuu", 0 + * p u1.recvfrom(10) #=> ["uuuu", ["AF_INET", 33230, "localhost", "127.0.0.1"]] + * + */ +static VALUE +udp_connect(VALUE self, VALUE host, VALUE port) +{ + struct udp_arg arg = {.io = self}; + + arg.res = rsock_addrinfo(host, port, rsock_fd_family(rb_io_descriptor(self)), SOCK_DGRAM, 0, Qnil); + + int result = (int)rb_ensure(udp_connect_internal, (VALUE)&arg, rsock_freeaddrinfo, (VALUE)arg.res); + if (!result) { + rsock_sys_fail_host_port("connect(2)", host, port); + } + + return INT2FIX(0); +} + +static VALUE +udp_bind_internal(VALUE v) +{ + struct udp_arg *arg = (void *)v; + struct addrinfo *res; + + rb_io_t *fptr; + RB_IO_POINTER(arg->io, fptr); + + for (res = arg->res->ai; res; res = res->ai_next) { + if (bind(fptr->fd, res->ai_addr, res->ai_addrlen) < 0) { + continue; + } + return Qtrue; + } + return Qfalse; +} + +/* + * call-seq: + * udpsocket.bind(host, port) #=> 0 + * + * Binds _udpsocket_ to _host_:_port_. + * + * u1 = UDPSocket.new + * u1.bind("127.0.0.1", 4913) + * u1.send "message-to-self", 0, "127.0.0.1", 4913 + * p u1.recvfrom(10) #=> ["message-to", ["AF_INET", 4913, "localhost", "127.0.0.1"]] + * + */ +static VALUE +udp_bind(VALUE self, VALUE host, VALUE port) +{ + struct udp_arg arg = {.io = self}; + + arg.res = rsock_addrinfo(host, port, rsock_fd_family(rb_io_descriptor(self)), SOCK_DGRAM, 0, Qnil); + + VALUE result = rb_ensure(udp_bind_internal, (VALUE)&arg, rsock_freeaddrinfo, (VALUE)arg.res); + if (!result) { + rsock_sys_fail_host_port("bind(2)", host, port); + } + + return INT2FIX(0); +} + +struct udp_send_arg { + rb_io_t *fptr; + struct rb_addrinfo *res; + struct rsock_send_arg sarg; +}; + +static VALUE +udp_send_internal(VALUE v) +{ + struct udp_send_arg *arg = (void *)v; + rb_io_t *fptr; + struct addrinfo *res; + + rb_io_check_closed(fptr = arg->fptr); + for (res = arg->res->ai; res; res = res->ai_next) { + retry: + arg->sarg.fd = fptr->fd; + arg->sarg.to = res->ai_addr; + arg->sarg.tolen = res->ai_addrlen; + +#ifdef RSOCK_WAIT_BEFORE_BLOCKING + rb_io_wait(fptr->self, RB_INT2NUM(RUBY_IO_WRITABLE), Qnil); +#endif + + ssize_t n = (ssize_t)rb_io_blocking_region(fptr, rsock_sendto_blocking, &arg->sarg); + + if (n >= 0) return RB_SSIZE2NUM(n); + + if (rb_io_maybe_wait_writable(errno, fptr->self, RUBY_IO_TIMEOUT_DEFAULT)) { + goto retry; + } + } + return Qfalse; +} + +/* + * call-seq: + * udpsocket.send(mesg, flags, host, port) => numbytes_sent + * udpsocket.send(mesg, flags, sockaddr_to) => numbytes_sent + * udpsocket.send(mesg, flags) => numbytes_sent + * + * Sends _mesg_ via _udpsocket_. + * + * _flags_ should be a bitwise OR of Socket::MSG_* constants. + * + * u1 = UDPSocket.new + * u1.bind("127.0.0.1", 4913) + * + * u2 = UDPSocket.new + * u2.send "hi", 0, "127.0.0.1", 4913 + * + * mesg, addr = u1.recvfrom(10) + * u1.send mesg, 0, addr[3], addr[1] + * + * p u2.recv(100) #=> "hi" + * + */ +static VALUE +udp_send(int argc, VALUE *argv, VALUE sock) +{ + VALUE flags, host, port; + struct udp_send_arg arg; + VALUE ret; + + if (argc == 2 || argc == 3) { + return rsock_bsock_send(argc, argv, sock); + } + rb_scan_args(argc, argv, "4", &arg.sarg.mesg, &flags, &host, &port); + + StringValue(arg.sarg.mesg); + GetOpenFile(sock, arg.fptr); + arg.sarg.fd = arg.fptr->fd; + arg.sarg.flags = NUM2INT(flags); + arg.res = rsock_addrinfo(host, port, rsock_fd_family(arg.fptr->fd), SOCK_DGRAM, 0, Qnil); + ret = rb_ensure(udp_send_internal, (VALUE)&arg, + rsock_freeaddrinfo, (VALUE)arg.res); + if (!ret) rsock_sys_fail_host_port("sendto(2)", host, port); + return ret; +} + +/* :nodoc: */ +static VALUE +udp_recvfrom_nonblock(VALUE sock, VALUE len, VALUE flg, VALUE str, VALUE ex) +{ + return rsock_s_recvfrom_nonblock(sock, len, flg, str, ex, RECV_IP); +} + +void +rsock_init_udpsocket(void) +{ + /* + * Document-class: UDPSocket < IPSocket + * + * UDPSocket represents a UDP/IP socket. + * + */ + rb_cUDPSocket = rb_define_class("UDPSocket", rb_cIPSocket); + rb_define_method(rb_cUDPSocket, "initialize", udp_init, -1); + rb_define_method(rb_cUDPSocket, "connect", udp_connect, 2); + rb_define_method(rb_cUDPSocket, "bind", udp_bind, 2); + rb_define_method(rb_cUDPSocket, "send", udp_send, -1); + + /* for ext/socket/lib/socket.rb use only: */ + rb_define_private_method(rb_cUDPSocket, + "__recvfrom_nonblock", udp_recvfrom_nonblock, 4); +} diff --git a/ext/socket/unixserver.c b/ext/socket/unixserver.c new file mode 100644 index 0000000000..0ea5ac083c --- /dev/null +++ b/ext/socket/unixserver.c @@ -0,0 +1,121 @@ +/************************************************ + + unixserver.c - + + created at: Thu Mar 31 12:21:29 JST 1994 + + Copyright (C) 1993-2007 Yukihiro Matsumoto + +************************************************/ + +#include "rubysocket.h" + +#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN +/* + * call-seq: + * UNIXServer.new(path) => unixserver + * + * Creates a new UNIX server socket bound to _path_. + * + * require 'socket' + * + * serv = UNIXServer.new("/tmp/sock") + * s = serv.accept + * p s.read + */ +static VALUE +unix_svr_init(VALUE sock, VALUE path) +{ + return rsock_init_unixsock(sock, path, 1); +} + +/* + * call-seq: + * unixserver.accept => unixsocket + * + * Accepts an incoming connection. + * It returns a new UNIXSocket object. + * + * UNIXServer.open("/tmp/sock") {|serv| + * UNIXSocket.open("/tmp/sock") {|c| + * s = serv.accept + * s.puts "hi" + * s.close + * p c.read #=> "hi\n" + * } + * } + * + */ +static VALUE +unix_accept(VALUE server) +{ + struct sockaddr_un buffer; + socklen_t length = sizeof(buffer); + + return rsock_s_accept(rb_cUNIXSocket, server, (struct sockaddr*)&buffer, &length); +} + +/* :nodoc: */ +static VALUE +unix_accept_nonblock(VALUE sock, VALUE ex) +{ + rb_io_t *fptr; + struct sockaddr_un from; + socklen_t fromlen; + + GetOpenFile(sock, fptr); + fromlen = (socklen_t)sizeof(from); + return rsock_s_accept_nonblock(rb_cUNIXSocket, ex, fptr, + (struct sockaddr *)&from, &fromlen); +} + +/* + * call-seq: + * unixserver.sysaccept => file_descriptor + * + * Accepts a new connection. + * It returns the new file descriptor which is an integer. + * + * UNIXServer.open("/tmp/sock") {|serv| + * UNIXSocket.open("/tmp/sock") {|c| + * fd = serv.sysaccept + * s = IO.new(fd) + * s.puts "hi" + * s.close + * p c.read #=> "hi\n" + * } + * } + * + */ +static VALUE +unix_sysaccept(VALUE server) +{ + struct sockaddr_un buffer; + socklen_t length = sizeof(buffer); + + return rsock_s_accept(0, server, (struct sockaddr*)&buffer, &length); +} + +#endif + +void +rsock_init_unixserver(void) +{ +#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN + /* + * Document-class: UNIXServer < UNIXSocket + * + * UNIXServer represents a UNIX domain stream server socket. + * + */ + rb_cUNIXServer = rb_define_class("UNIXServer", rb_cUNIXSocket); + rb_define_method(rb_cUNIXServer, "initialize", unix_svr_init, 1); + rb_define_method(rb_cUNIXServer, "accept", unix_accept, 0); + + rb_define_private_method(rb_cUNIXServer, + "__accept_nonblock", unix_accept_nonblock, 1); + + rb_define_method(rb_cUNIXServer, "sysaccept", unix_sysaccept, 0); + rb_define_method(rb_cUNIXServer, "listen", rsock_sock_listen, 1); /* in socket.c */ +#endif +} diff --git a/ext/socket/unixsocket.c b/ext/socket/unixsocket.c new file mode 100644 index 0000000000..2ec9376074 --- /dev/null +++ b/ext/socket/unixsocket.c @@ -0,0 +1,598 @@ +/************************************************ + + unixsocket.c - + + created at: Thu Mar 31 12:21:29 JST 1994 + + Copyright (C) 1993-2007 Yukihiro Matsumoto + +************************************************/ + +#include "rubysocket.h" + +#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN +struct unixsock_arg { + struct sockaddr_un *sockaddr; + socklen_t sockaddrlen; + VALUE io; +}; + +static VALUE +unixsock_connect_internal(VALUE a) +{ + struct unixsock_arg *arg = (struct unixsock_arg *)a; + return (VALUE)rsock_connect(arg->io, (struct sockaddr*)arg->sockaddr, arg->sockaddrlen, 0, RUBY_IO_TIMEOUT_DEFAULT); +} + +static VALUE +unixsock_path_value(VALUE path) +{ +#ifdef __linux__ +#define TO_STR_FOR_LINUX_ABSTRACT_NAMESPACE 0 + + VALUE name = path; +#if TO_STR_FOR_LINUX_ABSTRACT_NAMESPACE + const int isstr = !NIL_P(name = rb_check_string_type(name)); +#else + const int isstr = RB_TYPE_P(name, T_STRING); +#endif + if (isstr) { + if (RSTRING_LEN(name) == 0 || RSTRING_PTR(name)[0] == '\0') { + return name; /* ignore encoding */ + } + } +#endif + path = rb_get_path(path); +#ifdef _WIN32 + /* UNIXSocket requires UTF-8 per spec. */ + path = rb_str_export_to_enc(path, rb_utf8_encoding()); +#endif + return path; +} + +VALUE +rsock_init_unixsock(VALUE self, VALUE path, int server) +{ + struct sockaddr_un sockaddr; + socklen_t sockaddrlen; + int fd, status; + rb_io_t *fptr; + + path = unixsock_path_value(path); + + INIT_SOCKADDR_UN(&sockaddr, sizeof(struct sockaddr_un)); + if (sizeof(sockaddr.sun_path) < (size_t)RSTRING_LEN(path)) { + rb_raise(rb_eArgError, "too long unix socket path (%ldbytes given but %dbytes max)", + RSTRING_LEN(path), (int)sizeof(sockaddr.sun_path)); + } + memcpy(sockaddr.sun_path, RSTRING_PTR(path), RSTRING_LEN(path)); + sockaddrlen = rsock_unix_sockaddr_len(path); + + fd = rsock_socket(AF_UNIX, SOCK_STREAM, 0); + if (fd < 0) { + rsock_sys_fail_path("socket(2)", path); + } + + VALUE io = rsock_init_sock(self, fd); + RB_IO_POINTER(io, fptr); + + if (server) { + status = bind(fd, (struct sockaddr*)&sockaddr, sockaddrlen); + } + else { + int error_tag; + struct unixsock_arg arg; + arg.sockaddr = &sockaddr; + arg.sockaddrlen = sockaddrlen; + arg.io = io; + + status = (int)rb_protect(unixsock_connect_internal, (VALUE)&arg, &error_tag); + + if (error_tag) { + rb_io_close(io); + rb_jump_tag(error_tag); + } + } + + if (status < 0) { + int e = errno; + rb_io_close(io); + rsock_syserr_fail_path(e, "connect(2)", path); + } + + if (server) { + if (listen(fd, SOMAXCONN) < 0) { + int e = errno; + rb_io_close(io); + rsock_syserr_fail_path(e, "listen(2)", path); + } + } + + if (server) { + fptr->pathv = rb_str_new_frozen(path); + } + + return io; +} + +/* + * call-seq: + * UNIXSocket.new(path) => unixsocket + * + * Creates a new UNIX client socket connected to _path_. + * + * require 'socket' + * + * s = UNIXSocket.new("/tmp/sock") + * s.send "hello", 0 + * + */ +static VALUE +unix_init(VALUE self, VALUE path) +{ + return rsock_init_unixsock(self, path, 0); +} + +/* + * call-seq: + * unixsocket.path => path + * + * Returns the path of the local address of unixsocket. + * + * s = UNIXServer.new("/tmp/sock") + * p s.path #=> "/tmp/sock" + * + */ +static VALUE +unix_path(VALUE sock) +{ + rb_io_t *fptr; + + GetOpenFile(sock, fptr); + if (NIL_P(fptr->pathv)) { + struct sockaddr_un addr; + socklen_t len = (socklen_t)sizeof(addr); + socklen_t len0 = len; + if (getsockname(fptr->fd, (struct sockaddr*)&addr, &len) < 0) + rsock_sys_fail_path("getsockname(2)", fptr->pathv); + if (len0 < len) len = len0; + fptr->pathv = rb_obj_freeze(rsock_unixpath_str(&addr, len)); + } + return rb_str_dup(fptr->pathv); +} + +/* + * call-seq: + * unixsocket.recvfrom(maxlen [, flags[, outbuf]]) => [mesg, unixaddress] + * + * Receives a message via _unixsocket_. + * + * _maxlen_ is the maximum number of bytes to receive. + * + * _flags_ should be a bitwise OR of Socket::MSG_* constants. + * + * _outbuf_ will contain only the received data after the method call + * even if it is not empty at the beginning. + * + * s1 = Socket.new(:UNIX, :DGRAM, 0) + * s1_ai = Addrinfo.unix("/tmp/sock1") + * s1.bind(s1_ai) + * + * s2 = Socket.new(:UNIX, :DGRAM, 0) + * s2_ai = Addrinfo.unix("/tmp/sock2") + * s2.bind(s2_ai) + * s3 = UNIXSocket.for_fd(s2.fileno) + * + * s1.send "a", 0, s2_ai + * p s3.recvfrom(10) #=> ["a", ["AF_UNIX", "/tmp/sock1"]] + * + */ +static VALUE +unix_recvfrom(int argc, VALUE *argv, VALUE sock) +{ + return rsock_s_recvfrom(sock, argc, argv, RECV_UNIX); +} + +#if defined(HAVE_STRUCT_MSGHDR_MSG_CONTROL) && defined(SCM_RIGHTS) +#define FD_PASSING_BY_MSG_CONTROL 1 +#else +#define FD_PASSING_BY_MSG_CONTROL 0 +#endif + +#if defined(HAVE_STRUCT_MSGHDR_MSG_ACCRIGHTS) +#define FD_PASSING_BY_MSG_ACCRIGHTS 1 +#else +#define FD_PASSING_BY_MSG_ACCRIGHTS 0 +#endif + +struct iomsg_arg { + int fd; + struct msghdr msg; +}; + +#if defined(HAVE_SENDMSG) && (FD_PASSING_BY_MSG_CONTROL || FD_PASSING_BY_MSG_ACCRIGHTS) +static VALUE +sendmsg_blocking(void *data) +{ + struct iomsg_arg *arg = data; + return sendmsg(arg->fd, &arg->msg, 0); +} + +/* + * call-seq: + * unixsocket.send_io(io) => nil + * + * Sends _io_ as file descriptor passing. + * + * s1, s2 = UNIXSocket.pair + * + * s1.send_io STDOUT + * stdout = s2.recv_io + * + * p STDOUT.fileno #=> 1 + * p stdout.fileno #=> 6 + * + * stdout.puts "hello" # outputs "hello\n" to standard output. + * + * _io_ may be any kind of IO object or integer file descriptor. + */ +static VALUE +unix_send_io(VALUE sock, VALUE val) +{ + int fd; + rb_io_t *fptr; + struct iomsg_arg arg; + struct iovec vec[1]; + char buf[1]; + +#if FD_PASSING_BY_MSG_CONTROL + union { + struct cmsghdr hdr; + char pad[sizeof(struct cmsghdr)+8+sizeof(int)+8]; + } cmsg; +#endif + + if (rb_obj_is_kind_of(val, rb_cIO)) { + rb_io_t *valfptr; + GetOpenFile(val, valfptr); + fd = valfptr->fd; + } + else if (FIXNUM_P(val)) { + fd = FIX2INT(val); + } + else { + rb_raise(rb_eTypeError, "neither IO nor file descriptor"); + } + + GetOpenFile(sock, fptr); + + arg.msg.msg_name = NULL; + arg.msg.msg_namelen = 0; + + /* Linux and Solaris doesn't work if msg_iov is NULL. */ + buf[0] = '\0'; + vec[0].iov_base = buf; + vec[0].iov_len = 1; + arg.msg.msg_iov = vec; + arg.msg.msg_iovlen = 1; + +#if FD_PASSING_BY_MSG_CONTROL + arg.msg.msg_control = (caddr_t)&cmsg; + arg.msg.msg_controllen = (socklen_t)CMSG_LEN(sizeof(int)); + arg.msg.msg_flags = 0; + MEMZERO((char*)&cmsg, char, sizeof(cmsg)); + cmsg.hdr.cmsg_len = (socklen_t)CMSG_LEN(sizeof(int)); + cmsg.hdr.cmsg_level = SOL_SOCKET; + cmsg.hdr.cmsg_type = SCM_RIGHTS; + memcpy(CMSG_DATA(&cmsg.hdr), &fd, sizeof(int)); +#else + arg.msg.msg_accrights = (caddr_t)&fd; + arg.msg.msg_accrightslen = sizeof(fd); +#endif + + arg.fd = fptr->fd; + while ((int)rb_io_blocking_region(fptr, sendmsg_blocking, &arg) == -1) { + if (!rb_io_wait_writable(arg.fd)) + rsock_sys_fail_path("sendmsg(2)", fptr->pathv); + } + + return Qnil; +} +#else +#define unix_send_io rb_f_notimplement +#endif + +#if defined(HAVE_RECVMSG) && (FD_PASSING_BY_MSG_CONTROL || FD_PASSING_BY_MSG_ACCRIGHTS) +static VALUE +recvmsg_blocking(void *data) +{ + struct iomsg_arg *arg = data; + int flags = 0; + return rsock_recvmsg(arg->fd, &arg->msg, flags); +} + +/* + * call-seq: + * unixsocket.recv_io([klass [, mode]]) => io + * + * Example + * + * UNIXServer.open("/tmp/sock") {|serv| + * UNIXSocket.open("/tmp/sock") {|c| + * s = serv.accept + * + * c.send_io STDOUT + * stdout = s.recv_io + * + * p STDOUT.fileno #=> 1 + * p stdout.fileno #=> 7 + * + * stdout.puts "hello" # outputs "hello\n" to standard output. + * } + * } + * + * _klass_ will determine the class of _io_ returned (using the + * IO.for_fd singleton method or similar). + * If _klass_ is +nil+, an integer file descriptor is returned. + * + * _mode_ is the same as the argument passed to IO.for_fd + */ +static VALUE +unix_recv_io(int argc, VALUE *argv, VALUE sock) +{ + VALUE klass, mode; + rb_io_t *fptr; + struct iomsg_arg arg; + struct iovec vec[2]; + char buf[1]; + unsigned int gc_reason = 0; + enum { + GC_REASON_EMSGSIZE = 0x1, + GC_REASON_TRUNCATE = 0x2, + GC_REASON_ENOMEM = 0x4 + }; + + int fd; +#if FD_PASSING_BY_MSG_CONTROL + union { + struct cmsghdr hdr; + char pad[sizeof(struct cmsghdr)+8+sizeof(int)+8]; + } cmsg; +#endif + + rb_scan_args(argc, argv, "02", &klass, &mode); + if (argc == 0) + klass = rb_cIO; + if (argc <= 1) + mode = Qnil; + +retry: + GetOpenFile(sock, fptr); + + arg.msg.msg_name = NULL; + arg.msg.msg_namelen = 0; + + vec[0].iov_base = buf; + vec[0].iov_len = sizeof(buf); + arg.msg.msg_iov = vec; + arg.msg.msg_iovlen = 1; + +#if FD_PASSING_BY_MSG_CONTROL + arg.msg.msg_control = (caddr_t)&cmsg; + arg.msg.msg_controllen = (socklen_t)CMSG_SPACE(sizeof(int)); + arg.msg.msg_flags = 0; + cmsg.hdr.cmsg_len = (socklen_t)CMSG_LEN(sizeof(int)); + cmsg.hdr.cmsg_level = SOL_SOCKET; + cmsg.hdr.cmsg_type = SCM_RIGHTS; + fd = -1; + memcpy(CMSG_DATA(&cmsg.hdr), &fd, sizeof(int)); +#else + arg.msg.msg_accrights = (caddr_t)&fd; + arg.msg.msg_accrightslen = sizeof(fd); + fd = -1; +#endif + + arg.fd = fptr->fd; + while ((int)rb_io_blocking_region(fptr, recvmsg_blocking, &arg) == -1) { + int e = errno; + if (e == EMSGSIZE && !(gc_reason & GC_REASON_EMSGSIZE)) { + /* FreeBSD gets here when we're out of FDs */ + gc_reason |= GC_REASON_EMSGSIZE; + rb_gc_for_fd(EMFILE); + goto retry; + } + else if (e == ENOMEM && !(gc_reason & GC_REASON_ENOMEM)) { + /* ENOMEM is documented in recvmsg manpages */ + gc_reason |= GC_REASON_ENOMEM; + rb_gc_for_fd(e); + goto retry; + } + if (!rb_io_wait_readable(arg.fd)) + rsock_syserr_fail_path(e, "recvmsg(2)", fptr->pathv); + } + +#if FD_PASSING_BY_MSG_CONTROL + if (arg.msg.msg_controllen < (socklen_t)sizeof(struct cmsghdr)) { + /* FreeBSD and Linux both get here when we're out of FDs */ + if (!(gc_reason & GC_REASON_TRUNCATE)) { + gc_reason |= GC_REASON_TRUNCATE; + rb_gc_for_fd(EMFILE); + goto retry; + } + rb_raise(rb_eSocket, + "file descriptor was not passed (msg_controllen=%d smaller than sizeof(struct cmsghdr)=%d)", + (int)arg.msg.msg_controllen, (int)sizeof(struct cmsghdr)); + } + if (cmsg.hdr.cmsg_level != SOL_SOCKET) { + rb_raise(rb_eSocket, + "file descriptor was not passed (cmsg_level=%d, %d expected)", + cmsg.hdr.cmsg_level, SOL_SOCKET); + } + if (cmsg.hdr.cmsg_type != SCM_RIGHTS) { + rb_raise(rb_eSocket, + "file descriptor was not passed (cmsg_type=%d, %d expected)", + cmsg.hdr.cmsg_type, SCM_RIGHTS); + } + if (arg.msg.msg_controllen < (socklen_t)CMSG_LEN(sizeof(int))) { + rb_raise(rb_eSocket, + "file descriptor was not passed (msg_controllen=%d smaller than CMSG_LEN(sizeof(int))=%d)", + (int)arg.msg.msg_controllen, (int)CMSG_LEN(sizeof(int))); + } + if ((socklen_t)CMSG_SPACE(sizeof(int)) < arg.msg.msg_controllen) { + rb_raise(rb_eSocket, + "file descriptor was not passed (msg_controllen=%d bigger than CMSG_SPACE(sizeof(int))=%d)", + (int)arg.msg.msg_controllen, (int)CMSG_SPACE(sizeof(int))); + } + if (cmsg.hdr.cmsg_len != CMSG_LEN(sizeof(int))) { + rsock_discard_cmsg_resource(&arg.msg, 0); + rb_raise(rb_eSocket, + "file descriptor was not passed (cmsg_len=%d, %d expected)", + (int)cmsg.hdr.cmsg_len, (int)CMSG_LEN(sizeof(int))); + } +#else + if (arg.msg.msg_accrightslen != sizeof(fd)) { + rb_raise(rb_eSocket, + "file descriptor was not passed (accrightslen=%d, %d expected)", + arg.msg.msg_accrightslen, (int)sizeof(fd)); + } +#endif + +#if FD_PASSING_BY_MSG_CONTROL + memcpy(&fd, CMSG_DATA(&cmsg.hdr), sizeof(int)); +#endif + + rb_update_max_fd(fd); + rb_maygvl_fd_fix_cloexec(fd); + + if (klass == Qnil) + return INT2FIX(fd); + else { + ID for_fd; + int ff_argc; + VALUE ff_argv[2]; + CONST_ID(for_fd, "for_fd"); + ff_argc = mode == Qnil ? 1 : 2; + ff_argv[0] = INT2FIX(fd); + ff_argv[1] = mode; + return rb_funcallv(klass, for_fd, ff_argc, ff_argv); + } +} +#else +#define unix_recv_io rb_f_notimplement +#endif + +/* + * call-seq: + * unixsocket.addr => [address_family, unix_path] + * + * Returns the local address as an array which contains + * address_family and unix_path. + * + * Example + * serv = UNIXServer.new("/tmp/sock") + * p serv.addr #=> ["AF_UNIX", "/tmp/sock"] + */ +static VALUE +unix_addr(VALUE sock) +{ + rb_io_t *fptr; + struct sockaddr_un addr; + socklen_t len = (socklen_t)sizeof addr; + socklen_t len0 = len; + + GetOpenFile(sock, fptr); + + if (getsockname(fptr->fd, (struct sockaddr*)&addr, &len) < 0) + rsock_sys_fail_path("getsockname(2)", fptr->pathv); + if (len0 < len) len = len0; + return rsock_unixaddr(&addr, len); +} + +/* + * call-seq: + * unixsocket.peeraddr => [address_family, unix_path] + * + * Returns the remote address as an array which contains + * address_family and unix_path. + * + * Example + * serv = UNIXServer.new("/tmp/sock") + * c = UNIXSocket.new("/tmp/sock") + * p c.peeraddr #=> ["AF_UNIX", "/tmp/sock"] + */ +static VALUE +unix_peeraddr(VALUE sock) +{ + rb_io_t *fptr; + struct sockaddr_un addr; + socklen_t len = (socklen_t)sizeof addr; + socklen_t len0 = len; + + GetOpenFile(sock, fptr); + + if (getpeername(fptr->fd, (struct sockaddr*)&addr, &len) < 0) + rsock_sys_fail_path("getpeername(2)", fptr->pathv); + if (len0 < len) len = len0; + return rsock_unixaddr(&addr, len); +} + +/* + * call-seq: + * UNIXSocket.pair([type [, protocol]]) => [unixsocket1, unixsocket2] + * UNIXSocket.socketpair([type [, protocol]]) => [unixsocket1, unixsocket2] + * + * Creates a pair of sockets connected to each other. + * + * _type_ should be a socket type such as: :STREAM, :DGRAM, :RAW, etc. + * + * _protocol_ should be a protocol defined in the domain. + * 0 is default protocol for the domain. + * + * s1, s2 = UNIXSocket.pair + * s1.send "a", 0 + * s1.send "b", 0 + * p s2.recv(10) #=> "ab" + * + */ +static VALUE +unix_s_socketpair(int argc, VALUE *argv, VALUE klass) +{ + VALUE domain, type, protocol; + VALUE args[3]; + + domain = INT2FIX(PF_UNIX); + rb_scan_args(argc, argv, "02", &type, &protocol); + if (argc == 0) + type = INT2FIX(SOCK_STREAM); + if (argc <= 1) + protocol = INT2FIX(0); + + args[0] = domain; + args[1] = type; + args[2] = protocol; + + return rsock_sock_s_socketpair(3, args, klass); +} +#endif + +void +rsock_init_unixsocket(void) +{ +#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN + /* + * Document-class: UNIXSocket < BasicSocket + * + * UNIXSocket represents a UNIX domain stream client socket. + */ + rb_cUNIXSocket = rb_define_class("UNIXSocket", rb_cBasicSocket); + rb_define_method(rb_cUNIXSocket, "initialize", unix_init, 1); + rb_define_method(rb_cUNIXSocket, "path", unix_path, 0); + rb_define_method(rb_cUNIXSocket, "addr", unix_addr, 0); + rb_define_method(rb_cUNIXSocket, "peeraddr", unix_peeraddr, 0); + rb_define_method(rb_cUNIXSocket, "recvfrom", unix_recvfrom, -1); + rb_define_method(rb_cUNIXSocket, "send_io", unix_send_io, 1); + rb_define_method(rb_cUNIXSocket, "recv_io", unix_recv_io, -1); + rb_define_singleton_method(rb_cUNIXSocket, "socketpair", unix_s_socketpair, -1); + rb_define_singleton_method(rb_cUNIXSocket, "pair", unix_s_socketpair, -1); +#endif +} |
