diff options
Diffstat (limited to 'ext/socket/raddrinfo.c')
| -rw-r--r-- | ext/socket/raddrinfo.c | 1838 |
1 files changed, 1469 insertions, 369 deletions
diff --git a/ext/socket/raddrinfo.c b/ext/socket/raddrinfo.c index de8c7dc768..f1fbcc35de 100644 --- a/ext/socket/raddrinfo.c +++ b/ext/socket/raddrinfo.c @@ -1,6 +1,6 @@ /************************************************ - ainfo.c - + raddrinfo.c - created at: Thu Mar 31 12:21:29 JST 1994 @@ -10,6 +10,22 @@ #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[] = { @@ -24,28 +40,28 @@ static const int lookup_order_table[] = { static int ruby_getaddrinfo(const char *nodename, const char *servname, - const struct addrinfo *hints, struct addrinfo **res) + 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); + 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; - } + 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; @@ -56,17 +72,17 @@ ruby_getaddrinfo(const char *nodename, const char *servname, #if defined(_AIX) static int ruby_getaddrinfo__aix(const char *nodename, const char *servname, - struct addrinfo *hints, struct addrinfo **res) + const struct addrinfo *hints, struct addrinfo **res) { int error = getaddrinfo(nodename, servname, hints, res); struct addrinfo *r; if (error) - return 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; + 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; } @@ -74,21 +90,21 @@ ruby_getaddrinfo__aix(const char *nodename, const char *servname, #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) + 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; + 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; - } + 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); } @@ -97,7 +113,208 @@ ruby_getnameinfo__aix(const struct sockaddr *sa, size_t salen, ruby_getnameinfo__aix((sa), (salen), (host), (hostlen), (serv), (servlen), (flags)) #endif -#ifndef GETADDRINFO_EMU +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; @@ -106,65 +323,348 @@ struct getaddrinfo_arg struct addrinfo **res; }; -static VALUE +static void * nogvl_getaddrinfo(void *arg) { + int ret; struct getaddrinfo_arg *ptr = arg; - return getaddrinfo(ptr->node, ptr->service, - ptr->hints, ptr->res); -} + 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; +} -int -rb_getaddrinfo(const char *node, const char *service, - const struct addrinfo *hints, - struct addrinfo **res) +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) { -#ifdef GETADDRINFO_EMU - return getaddrinfo(node, service, hints, res); -#else struct getaddrinfo_arg arg; - int ret; - arg.node = node; - arg.service = service; + MEMZERO(&arg, struct getaddrinfo_arg, 1); + arg.node = hostp; + arg.service = portp; arg.hints = hints; - arg.res = res; - ret = BLOCKING_REGION(nogvl_getaddrinfo, &arg); + 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) { + 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); } -#ifndef GETADDRINFO_EMU +#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; - int flags; }; -static VALUE +static void * nogvl_getnameinfo(void *arg) { struct getnameinfo_arg *ptr = arg; - return getnameinfo(ptr->sa, ptr->salen, - ptr->host, ptr->hostlen, - ptr->serv, ptr->servlen, - ptr->flags); + return (void *)(VALUE)getnameinfo(ptr->sa, ptr->salen, + ptr->host, (socklen_t)ptr->hostlen, + ptr->serv, (socklen_t)ptr->servlen, + ptr->flags); } -#endif - int rb_getnameinfo(const struct sockaddr *sa, socklen_t salen, - char *host, size_t hostlen, - char *serv, size_t servlen, int flags) + char *host, size_t hostlen, + char *serv, size_t servlen, int flags) { -#ifdef GETADDRINFO_EMU - return getnameinfo(sa, salen, host, hostlen, serv, servlen, flags); -#else + 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; @@ -174,182 +674,434 @@ rb_getnameinfo(const struct sockaddr *sa, socklen_t salen, arg.serv = serv; arg.servlen = servlen; arg.flags = flags; - ret = BLOCKING_REGION(nogvl_getnameinfo, &arg); + ret = (int)(VALUE)rb_thread_call_without_gvl(nogvl_getnameinfo, &arg, RUBY_UBF_IO, 0); return ret; -#endif +} + +#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 -make_ipaddr0(struct sockaddr *addr, char *buf, size_t len) +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, SA_LEN(addr), buf, len, NULL, 0, NI_NUMERICHOST); + error = rb_getnameinfo(addr, addrlen, buf, buflen, NULL, 0, NI_NUMERICHOST); if (error) { - rsock_raise_socket_error("getnameinfo", error); + rsock_raise_resolution_error("getnameinfo", error); } } VALUE -rsock_make_ipaddr(struct sockaddr *addr) +rsock_make_ipaddr(struct sockaddr *addr, socklen_t addrlen) { char hbuf[1024]; - make_ipaddr0(addr, hbuf, sizeof(hbuf)); + make_ipaddr0(addr, addrlen, hbuf, sizeof(hbuf)); return rb_str_new2(hbuf); } static void -make_inetaddr(long host, char *buf, size_t len) +make_inetaddr(unsigned int host, char *buf, size_t buflen) { struct sockaddr_in sin; - MEMZERO(&sin, struct sockaddr_in, 1); - sin.sin_family = AF_INET; - SET_SIN_LEN(&sin, sizeof(sin)); + INIT_SOCKADDR_IN(&sin, sizeof(sin)); sin.sin_addr.s_addr = host; - make_ipaddr0((struct sockaddr*)&sin, buf, len); + make_ipaddr0((struct sockaddr*)&sin, sizeof(sin), buf, buflen); } static int -str_isnumber(const char *p) +str_is_number(const char *p) { char *ep; if (!p || *p == '\0') - return 0; + return 0; ep = NULL; (void)STRTOUL(p, &ep, 10); if (ep && *ep == '\0') - return 1; + return 1; else - return 0; + return 0; } -static char* -host_str(VALUE host, char *hbuf, size_t len, int *flags_ptr) +#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 long i = NUM2ULONG(host); + unsigned int i = NUM2UINT(host); - make_inetaddr(htonl(i), hbuf, len); + make_inetaddr(htonl(i), hbuf, hbuflen); if (flags_ptr) *flags_ptr |= AI_NUMERICHOST; return hbuf; } else { - char *name; + const char *name; + size_t len; - SafeStringValue(host); - name = RSTRING_PTR(host); - if (!name || *name == 0 || (name[0] == '<' && strcmp(name, "<any>") == 0)) { - make_inetaddr(INADDR_ANY, hbuf, 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 (name[0] == '<' && strcmp(name, "<broadcast>") == 0) { - make_inetaddr(INADDR_BROADCAST, hbuf, len); + else if (str_equal(name, len, "<broadcast>")) { + make_inetaddr(INADDR_BROADCAST, hbuf, hbuflen); if (flags_ptr) *flags_ptr |= AI_NUMERICHOST; } - else if (strlen(name) >= len) { + else if (len >= hbuflen) { rb_raise(rb_eArgError, "hostname too long (%"PRIuSIZE")", - strlen(name)); + len); } else { - strcpy(hbuf, name); + memcpy(hbuf, name, len); + hbuf[len] = '\0'; } return hbuf; } } -static char* -port_str(VALUE port, char *pbuf, size_t len, int *flags_ptr) +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, len, "%ld", FIX2LONG(port)); + snprintf(pbuf, pbuflen, "%ld", FIX2LONG(port)); #ifdef AI_NUMERICSERV if (flags_ptr) *flags_ptr |= AI_NUMERICSERV; #endif return pbuf; } else { - char *serv; + const char *serv; + size_t len; - SafeStringValue(port); - serv = RSTRING_PTR(port); - if (strlen(serv) >= len) { + StringValueCStr(port); + RSTRING_GETMEM(port, serv, len); + if (len >= pbuflen) { rb_raise(rb_eArgError, "service name too long (%"PRIuSIZE")", - strlen(serv)); + len); } - strcpy(pbuf, serv); + memcpy(pbuf, serv, len); + pbuf[len] = '\0'; return pbuf; } } -struct addrinfo* -rsock_getaddrinfo(VALUE host, VALUE port, struct addrinfo *hints, int socktype_hack) +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 addrinfo* res = NULL; + struct rb_addrinfo* res = NULL; + struct addrinfo *ai; char *hostp, *portp; - int error; + int error = 0; char hbuf[NI_MAXHOST], pbuf[NI_MAXSERV]; int additional_flags = 0; - hostp = host_str(host, hbuf, sizeof(hbuf), &additional_flags); - portp = port_str(port, pbuf, sizeof(pbuf), &additional_flags); + 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_isnumber(portp)) { - hints->ai_socktype = SOCK_DGRAM; + if (socktype_hack && hints->ai_socktype == 0 && str_is_number(portp)) { + hints->ai_socktype = SOCK_DGRAM; } hints->ai_flags |= additional_flags; - error = rb_getaddrinfo(hostp, portp, hints, &res); + 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_socket_error("getaddrinfo", error); + rsock_raise_resolution_error("getaddrinfo", error); } -#if defined(__APPLE__) && defined(__MACH__) - { - struct addrinfo *r; - r = res; - while (r) { - if (! r->ai_socktype) r->ai_socktype = hints->ai_socktype; - if (! r->ai_protocol) { - if (r->ai_socktype == SOCK_DGRAM) { - r->ai_protocol = IPPROTO_UDP; - } - else if (r->ai_socktype == SOCK_STREAM) { - r->ai_protocol = IPPROTO_TCP; - } - } - r = r->ai_next; - } - } -#endif return res; } -struct addrinfo* -rsock_addrinfo(VALUE host, VALUE port, int socktype, int flags) +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 = AF_UNSPEC; + hints.ai_family = family; hints.ai_socktype = socktype; hints.ai_flags = flags; - return rsock_getaddrinfo(host, port, &hints, 1); + return rsock_getaddrinfo(host, port, &hints, 1, timeout); } VALUE -rsock_ipaddr(struct sockaddr *sockaddr, int norevlookup) +rsock_ipaddr(struct sockaddr *sockaddr, socklen_t sockaddrlen, int norevlookup) { VALUE family, port, addr1, addr2; VALUE ary; @@ -362,22 +1114,21 @@ rsock_ipaddr(struct sockaddr *sockaddr, int norevlookup) family = rb_str_dup(rb_id2str(id)); } else { - sprintf(pbuf, "unknown:%d", sockaddr->sa_family); - family = rb_str_new2(pbuf); + family = rb_sprintf("unknown:%d", sockaddr->sa_family); } addr1 = Qnil; if (!norevlookup) { - error = rb_getnameinfo(sockaddr, SA_LEN(sockaddr), hbuf, sizeof(hbuf), + error = rb_getnameinfo(sockaddr, sockaddrlen, hbuf, sizeof(hbuf), NULL, 0, 0); if (! error) { addr1 = rb_str_new2(hbuf); } } - error = rb_getnameinfo(sockaddr, SA_LEN(sockaddr), hbuf, sizeof(hbuf), + error = rb_getnameinfo(sockaddr, sockaddrlen, hbuf, sizeof(hbuf), pbuf, sizeof(pbuf), NI_NUMERICHOST | NI_NUMERICSERV); if (error) { - rsock_raise_socket_error("getnameinfo", error); + rsock_raise_resolution_error("getnameinfo", error); } addr2 = rb_str_new2(hbuf); if (addr1 == Qnil) { @@ -389,36 +1140,70 @@ rsock_ipaddr(struct sockaddr *sockaddr, int norevlookup) return ary; } -#ifdef HAVE_SYS_UN_H -const char* -rsock_unixpath(struct sockaddr_un *sockaddr, socklen_t len) +#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN +static long +unixsocket_len(const struct sockaddr_un *su, socklen_t socklen) { - if (sockaddr->sun_path < (char*)sockaddr + len) - return sockaddr->sun_path; + 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 ""; + return rb_str_new2(""); } VALUE rsock_unixaddr(struct sockaddr_un *sockaddr, socklen_t len) { return rb_assoc_new(rb_str_new2("AF_UNIX"), - rb_str_new2(rsock_unixpath(sockaddr, len))); + 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 addrinfo* addr; - VALUE (*ipaddr)(struct sockaddr*, size_t); + struct rb_addrinfo* addr; + VALUE (*ipaddr)(struct sockaddr*, socklen_t); }; static VALUE -make_hostent_internal(struct hostent_arg *arg) +make_hostent_internal(VALUE v) { + struct hostent_arg *arg = (void *)v; VALUE host = arg->host; - struct addrinfo* addr = arg->addr; - VALUE (*ipaddr)(struct sockaddr*, size_t) = arg->ipaddr; + struct addrinfo* addr = arg->addr->ai; + VALUE (*ipaddr)(struct sockaddr*, socklen_t) = arg->ipaddr; struct addrinfo *ai; struct hostent *h; @@ -432,11 +1217,12 @@ make_hostent_internal(struct hostent_arg *arg) hostp = addr->ai_canonname; } else { - hostp = host_str(host, hbuf, sizeof(hbuf), NULL); + hostp = raddrinfo_host_str(host, hbuf, sizeof(hbuf), NULL); } rb_ary_push(ary, rb_str_new2(hostp)); - if (addr->ai_canonname && (h = gethostbyname(addr->ai_canonname))) { + 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++) { @@ -457,7 +1243,15 @@ make_hostent_internal(struct hostent_arg *arg) } VALUE -rsock_make_hostent(VALUE host, struct addrinfo *addr, VALUE (*ipaddr)(struct sockaddr *, size_t)) +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; @@ -465,7 +1259,7 @@ rsock_make_hostent(VALUE host, struct addrinfo *addr, VALUE (*ipaddr)(struct soc arg.addr = addr; arg.ipaddr = ipaddr; return rb_ensure(make_hostent_internal, (VALUE)&arg, - RUBY_METHOD_FUNC(freeaddrinfo), (VALUE)addr); + rsock_freeaddrinfo, (VALUE)addr); } typedef struct { @@ -474,18 +1268,16 @@ typedef struct { int pfamily; int socktype; int protocol; - size_t sockaddr_len; - struct sockaddr_storage addr; + socklen_t sockaddr_len; + union_sockaddr addr; } rb_addrinfo_t; static void addrinfo_mark(void *ptr) { rb_addrinfo_t *rai = ptr; - if (rai) { - rb_gc_mark(rai->inspectname); - rb_gc_mark(rai->canonname); - } + rb_gc_mark(rai->inspectname); + rb_gc_mark(rai->canonname); } #define addrinfo_free RUBY_TYPED_DEFAULT_FREE @@ -493,12 +1285,13 @@ addrinfo_mark(void *ptr) static size_t addrinfo_memsize(const void *ptr) { - return ptr ? sizeof(rb_addrinfo_t) : 0; + return sizeof(rb_addrinfo_t); } static const rb_data_type_t addrinfo_type = { "socket/addrinfo", - addrinfo_mark, addrinfo_free, addrinfo_memsize, + {addrinfo_mark, addrinfo_free, addrinfo_memsize,}, + 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_FROZEN_SHAREABLE | RUBY_TYPED_WB_PROTECTED, }; static VALUE @@ -507,7 +1300,7 @@ 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) +#define IS_ADDRINFO(obj) rb_typeddata_is_kind_of((obj), &addrinfo_type) static inline rb_addrinfo_t * check_addrinfo(VALUE self) { @@ -527,21 +1320,20 @@ get_addrinfo(VALUE self) static rb_addrinfo_t * -alloc_addrinfo() +alloc_addrinfo(void) { - rb_addrinfo_t *rai = ALLOC(rb_addrinfo_t); - memset(rai, 0, sizeof(rb_addrinfo_t)); + rb_addrinfo_t *rai = ZALLOC(rb_addrinfo_t); rai->inspectname = Qnil; rai->canonname = Qnil; return rai; } static void -init_addrinfo(rb_addrinfo_t *rai, struct sockaddr *sa, size_t len, +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 (sizeof(rai->addr) < len) + 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; @@ -549,8 +1341,8 @@ init_addrinfo(rb_addrinfo_t *rai, struct sockaddr *sa, size_t len, rai->pfamily = pfamily; rai->socktype = socktype; rai->protocol = protocol; - rai->canonname = canonname; - rai->inspectname = inspectname; + RB_OBJ_WRITE(self, &rai->canonname, canonname); + RB_OBJ_WRITE(self, &rai->inspectname, inspectname); } VALUE @@ -563,58 +1355,60 @@ rsock_addrinfo_new(struct sockaddr *addr, socklen_t len, a = addrinfo_s_allocate(rb_cAddrinfo); DATA_PTR(a) = rai = alloc_addrinfo(); - init_addrinfo(rai, addr, len, family, socktype, protocol, canonname, inspectname); + init_addrinfo(a, rai, addr, len, family, socktype, protocol, canonname, inspectname); return a; } -static struct addrinfo * +static struct rb_addrinfo * call_getaddrinfo(VALUE node, VALUE service, VALUE family, VALUE socktype, VALUE protocol, VALUE flags, - int socktype_hack) + int socktype_hack, VALUE timeout) { - struct addrinfo hints, *res; + 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); + 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); } - res = rsock_getaddrinfo(node, service, &hints, socktype_hack); + + res = rsock_getaddrinfo(node, service, &hints, socktype_hack, timeout); if (res == NULL) - rb_raise(rb_eSocket, "host not found"); + 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(rb_addrinfo_t *rai, VALUE node, VALUE service, +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 addrinfo *res = call_getaddrinfo(node, service, family, socktype, protocol, flags, 1); + 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); + VALUE inspectname = rb_str_equal(node, inspectnode) ? Qnil : make_inspectname(inspectnode, inspectservice, res->ai); canonname = Qnil; - if (res->ai_canonname) { - canonname = rb_tainted_str_new_cstr(res->ai_canonname); + if (res->ai->ai_canonname) { + canonname = rb_str_new_cstr(res->ai->ai_canonname); OBJ_FREEZE(canonname); } - init_addrinfo(rai, res->ai_addr, res->ai_addrlen, + init_addrinfo(self, rai, res->ai->ai_addr, res->ai->ai_addrlen, NUM2INT(family), NUM2INT(socktype), NUM2INT(protocol), canonname, inspectname); - freeaddrinfo(res); + rb_freeaddrinfo(res); } static VALUE @@ -623,31 +1417,32 @@ 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 (TYPE(node) == T_STRING && strcmp(hbuf, RSTRING_PTR(node)) == 0) + if (RB_TYPE_P(node, T_STRING) && strcmp(hbuf, RSTRING_PTR(node)) == 0) node = Qnil; - if (TYPE(service) == T_STRING && strcmp(pbuf, RSTRING_PTR(service)) == 0) + if (RB_TYPE_P(service, T_STRING) && strcmp(pbuf, RSTRING_PTR(service)) == 0) service = Qnil; - else if (TYPE(service) == T_FIXNUM && atoi(pbuf) == FIX2INT(service)) + else if (RB_TYPE_P(service, T_FIXNUM) && atoi(pbuf) == FIX2INT(service)) service = Qnil; } } - if (TYPE(node) == T_STRING) { + if (RB_TYPE_P(node, T_STRING)) { inspectname = rb_str_dup(node); } - if (TYPE(service) == T_STRING) { + 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 (TYPE(service) == T_FIXNUM && FIX2INT(service) != 0) + else if (RB_TYPE_P(service, T_FIXNUM) && FIX2INT(service) != 0) { if (NIL_P(inspectname)) inspectname = rb_sprintf(":%d", FIX2INT(service)); @@ -655,8 +1450,6 @@ make_inspectname(VALUE node, VALUE service, struct addrinfo *res) rb_str_catf(inspectname, ":%d", FIX2INT(service)); } if (!NIL_P(inspectname)) { - OBJ_INFECT(inspectname, node); - OBJ_INFECT(inspectname, service); OBJ_FREEZE(inspectname); } return inspectname; @@ -669,42 +1462,43 @@ addrinfo_firstonly_new(VALUE node, VALUE service, VALUE family, VALUE socktype, VALUE canonname; VALUE inspectname; - struct addrinfo *res = call_getaddrinfo(node, service, family, socktype, protocol, flags, 0); + struct rb_addrinfo *res = call_getaddrinfo(node, service, family, socktype, protocol, flags, 0, Qnil); - inspectname = make_inspectname(node, service, res); + inspectname = make_inspectname(node, service, res->ai); canonname = Qnil; - if (res->ai_canonname) { - canonname = rb_tainted_str_new_cstr(res->ai_canonname); + if (res->ai->ai_canonname) { + canonname = rb_str_new_cstr(res->ai->ai_canonname); OBJ_FREEZE(canonname); } - ret = rsock_addrinfo_new(res->ai_addr, res->ai_addrlen, - res->ai_family, res->ai_socktype, res->ai_protocol, + 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); - freeaddrinfo(res); + rb_freeaddrinfo(res); return ret; } static VALUE -addrinfo_list_new(VALUE node, VALUE service, VALUE family, VALUE socktype, VALUE protocol, VALUE flags) +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 addrinfo *res = call_getaddrinfo(node, service, family, socktype, protocol, flags, 0); + struct rb_addrinfo *res = call_getaddrinfo(node, service, family, socktype, protocol, flags, 0, timeout); - inspectname = make_inspectname(node, service, res); + inspectname = make_inspectname(node, service, res->ai); ret = rb_ary_new(); - for (r = res; r; r = r->ai_next) { + for (r = res->ai; r; r = r->ai_next) { VALUE addr; VALUE canonname = Qnil; if (r->ai_canonname) { - canonname = rb_tainted_str_new_cstr(r->ai_canonname); + canonname = rb_str_new_cstr(r->ai_canonname); OBJ_FREEZE(canonname); } @@ -715,29 +1509,37 @@ addrinfo_list_new(VALUE node, VALUE service, VALUE family, VALUE socktype, VALUE rb_ary_push(ret, addr); } - freeaddrinfo(res); + rb_freeaddrinfo(res); return ret; } -#ifdef HAVE_SYS_UN_H +#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN static void -init_unix_addrinfo(rb_addrinfo_t *rai, VALUE path, int socktype) +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 (max: %dbytes)", - (int)sizeof(un.sun_path)-1); - - MEMZERO(&un, struct sockaddr_un, 1); + 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)); - un.sun_family = AF_UNIX; + INIT_SOCKADDR_UN(&un, sizeof(struct sockaddr_un)); memcpy((void*)&un.sun_path, RSTRING_PTR(path), RSTRING_LEN(path)); - - init_addrinfo(rai, (struct sockaddr *)&un, sizeof(un), PF_UNIX, socktype, 0, Qnil, Qnil); + + 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 @@ -759,13 +1561,13 @@ init_unix_addrinfo(rb_addrinfo_t *rai, VALUE path, int socktype) * Socket.sockaddr_in or Socket.unpack_sockaddr_un. * * sockaddr examples: - * - ["AF_INET", 46102, "localhost.localdomain", "127.0.0.1"] - * - ["AF_INET6", 42304, "ip6-localhost", "::1"] - * - ["AF_UNIX", "/tmp/sock"] - * - Socket.sockaddr_in("smtp", "2001:DB8::1") - * - Socket.sockaddr_in(80, "172.18.22.42") - * - Socket.sockaddr_in(80, "www.ruby-lang.org") - * - Socket.sockaddr_un("/tmp/sock") + * - <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. @@ -794,7 +1596,7 @@ addrinfo_initialize(int argc, VALUE *argv, VALUE self) VALUE sockaddr_arg, sockaddr_ary, pfamily, socktype, protocol; int i_pfamily, i_socktype, i_protocol; struct sockaddr *sockaddr_ptr; - size_t sockaddr_len; + socklen_t sockaddr_len; VALUE canonname = Qnil, inspectname = Qnil; if (check_addrinfo(self)) @@ -813,7 +1615,7 @@ addrinfo_initialize(int argc, VALUE *argv, VALUE self) 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)); + 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 @@ -834,19 +1636,19 @@ addrinfo_initialize(int argc, VALUE *argv, VALUE self) flags |= AI_NUMERICSERV; #endif - init_addrinfo_getaddrinfo(rai, numericnode, service, + 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_SYS_UN_H +#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(rai, path, SOCK_STREAM); + init_unix_addrinfo(self, rai, path, SOCK_STREAM); break; } #endif @@ -858,8 +1660,8 @@ addrinfo_initialize(int argc, VALUE *argv, VALUE self) else { StringValue(sockaddr_arg); sockaddr_ptr = (struct sockaddr *)RSTRING_PTR(sockaddr_arg); - sockaddr_len = RSTRING_LEN(sockaddr_arg); - init_addrinfo(rai, sockaddr_ptr, sockaddr_len, + sockaddr_len = RSTRING_SOCKLEN(sockaddr_arg); + init_addrinfo(self, rai, sockaddr_ptr, sockaddr_len, i_pfamily, i_socktype, i_protocol, canonname, inspectname); } @@ -868,52 +1670,80 @@ addrinfo_initialize(int argc, VALUE *argv, VALUE self) } static int -get_afamily(struct sockaddr *addr, socklen_t len) +get_afamily(const struct sockaddr *addr, socklen_t len) { - if ((char*)&addr->sa_family + sizeof(addr->sa_family) - (char*)addr <= 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(rb_addrinfo_t *rai) +ai_get_afamily(const rb_addrinfo_t *rai) { - return get_afamily((struct sockaddr *)&rai->addr, rai->sockaddr_len); + 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); +} - if (rai->sockaddr_len == 0) { +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)rai->sockaddr_len < ((char*)&rai->addr.ss_family + sizeof(rai->addr.ss_family)) - (char*)&rai->addr) + 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 (rai->addr.ss_family) { + switch (sockaddr->addr.sa_family) { + case AF_UNSPEC: + { + rb_str_cat2(ret, "UNSPEC"); + break; + } + case AF_INET: { struct sockaddr_in *addr; int port; - if (rai->sockaddr_len < sizeof(struct sockaddr_in)) { - rb_str_cat2(ret, "too-short-AF_INET-sockaddr"); - } - else { - addr = (struct sockaddr_in *)&rai->addr; - rb_str_catf(ret, "%d.%d.%d.%d", - ((unsigned char*)&addr->sin_addr)[0], - ((unsigned char*)&addr->sin_addr)[1], - ((unsigned char*)&addr->sin_addr)[2], - ((unsigned char*)&addr->sin_addr)[3]); + 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); - if (sizeof(struct sockaddr_in) < rai->sockaddr_len) - rb_str_catf(ret, "(sockaddr %d bytes too long)", (int)(rai->sockaddr_len - sizeof(struct sockaddr_in))); } + 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; } @@ -924,20 +1754,20 @@ inspect_sockaddr(VALUE addrinfo, VALUE ret) char hbuf[1024]; int port; int error; - if (rai->sockaddr_len < sizeof(struct sockaddr_in6)) { - rb_str_cat2(ret, "too-short-AF_INET6-sockaddr"); + if (socklen < (socklen_t)sizeof(struct sockaddr_in6)) { + rb_str_catf(ret, "too-short-AF_INET6-sockaddr %d bytes", (int)socklen); } else { - addr = (struct sockaddr_in6 *)&rai->addr; + 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 = getnameinfo((struct sockaddr *)&rai->addr, rai->sockaddr_len, - hbuf, sizeof(hbuf), NULL, 0, - NI_NUMERICHOST|NI_NUMERICSERV); + error = rb_getnameinfo(&sockaddr->addr, socklen, + hbuf, (socklen_t)sizeof(hbuf), NULL, 0, + NI_NUMERICHOST|NI_NUMERICSERV); if (error) { - rsock_raise_socket_error("getnameinfo", error); + rsock_raise_resolution_error("getnameinfo", error); } if (addr->sin6_port == 0) { rb_str_cat2(ret, hbuf); @@ -946,65 +1776,198 @@ inspect_sockaddr(VALUE addrinfo, VALUE ret) port = ntohs(addr->sin6_port); rb_str_catf(ret, "[%s]:%d", hbuf, port); } - if (sizeof(struct sockaddr_in6) < rai->sockaddr_len) - rb_str_catf(ret, "(sockaddr %d bytes too long)", (int)(rai->sockaddr_len - sizeof(struct sockaddr_in6))); + 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_SYS_UN_H +#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN case AF_UNIX: { - struct sockaddr_un *addr = (struct sockaddr_un *)&rai->addr; - char *p, *s, *t, *e; + struct sockaddr_un *addr = &sockaddr->un; + char *p, *s, *e; + long len = unixsocket_len(addr, socklen); s = addr->sun_path; - e = (char*)addr + rai->sockaddr_len; - if (e < s) + if (len < 0) rb_str_cat2(ret, "too-short-AF_UNIX-sockaddr"); - else if (s == e) + 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 && *p != '\0') { + while (p < e) { printable_only = printable_only && ISPRINT(*p) && !ISSPACE(*p); p++; } - t = p; - while (p < e && *p == '\0') - p++; - if (printable_only && /* only printable, no space */ - t < e && /* NUL terminated */ - p == e) { /* no data after NUL */ - if (s == t) - rb_str_cat2(ret, "empty-path-AF_UNIX-sockaddr"); - else if (s[0] == '/') /* absolute path */ - rb_str_cat2(ret, s); - else - rb_str_catf(ret, "AF_UNIX %s", s); + 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, "AF_UNIX"); - e = (char *)addr->sun_path + sizeof(addr->sun_path); - while (s < e && *(e-1) == '\0') - e--; + rb_str_cat2(ret, "UNIX"); while (s < e) rb_str_catf(ret, ":%02x", (unsigned char)*s++); } - if (addr->sun_path + sizeof(addr->sun_path) < (char*)&rai->addr + rai->sockaddr_len) - rb_str_catf(ret, "(sockaddr %d bytes too long)", - (int)(rai->sockaddr_len - (addr->sun_path + sizeof(addr->sun_path) - (char*)&rai->addr))); } 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(rai->addr.ss_family); + ID id = rsock_intern_family(sockaddr->addr.sa_family); if (id == 0) - rb_str_catf(ret, "unknown address family %d", rai->addr.ss_family); + 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; @@ -1021,7 +1984,7 @@ inspect_sockaddr(VALUE addrinfo, VALUE ret) * * returns a string which shows addrinfo in human-readable form. * - * Addrinfo.tcp("localhost", 80).inspect #=> "#<Addrinfo: 127.0.0.1:80 TCP (localhost:80)>" + * Addrinfo.tcp("localhost", 80).inspect #=> "#<Addrinfo: 127.0.0.1:80 TCP (localhost)>" * Addrinfo.unix("/tmp/sock").inspect #=> "#<Addrinfo: /tmp/sock SOCK_STREAM>" * */ @@ -1105,8 +2068,8 @@ addrinfo_inspect(VALUE self) * Addrinfo.unix("/tmp/sock").inspect_sockaddr #=> "/tmp/sock" * */ -static VALUE -addrinfo_inspect_sockaddr(VALUE self) +VALUE +rsock_addrinfo_inspect_sockaddr(VALUE self) { return inspect_sockaddr(self, rb_str_new("", 0)); } @@ -1156,16 +2119,10 @@ addrinfo_mdump(VALUE self) afamily = rb_id2str(id); switch(afamily_int) { -#ifdef HAVE_SYS_UN_H +#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN case AF_UNIX: { - struct sockaddr_un *su = (struct sockaddr_un *)&rai->addr; - char *s, *e; - s = su->sun_path; - e = (char*)s + sizeof(su->sun_path); - while (s < e && *(e-1) == '\0') - e--; - sockaddr = rb_str_new(s, e-s); + sockaddr = rb_str_new(rai->addr.un.sun_path, rai_unixsocket_len(rai)); break; } #endif @@ -1174,11 +2131,11 @@ addrinfo_mdump(VALUE self) { char hbuf[NI_MAXHOST], pbuf[NI_MAXSERV]; int error; - error = getnameinfo((struct sockaddr *)&rai->addr, rai->sockaddr_len, - hbuf, sizeof(hbuf), pbuf, sizeof(pbuf), - NI_NUMERICHOST|NI_NUMERICSERV); + 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_socket_error("getnameinfo", error); + rsock_raise_resolution_error("getnameinfo", error); } sockaddr = rb_assoc_new(rb_str_new_cstr(hbuf), rb_str_new_cstr(pbuf)); break; @@ -1195,8 +2152,8 @@ addrinfo_mload(VALUE self, VALUE ary) VALUE v; VALUE canonname, inspectname; int afamily, pfamily, socktype, protocol; - struct sockaddr_storage ss; - size_t len; + union_sockaddr ss; + socklen_t len; rb_addrinfo_t *rai; if (check_addrinfo(self)) @@ -1255,18 +2212,19 @@ addrinfo_mload(VALUE self, VALUE ary) v = rb_ary_entry(ary, 1); switch(afamily) { -#ifdef HAVE_SYS_UN_H +#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN case AF_UNIX: { struct sockaddr_un uaddr; - memset(&uaddr, 0, sizeof(uaddr)); - uaddr.sun_family = AF_UNIX; + 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"); + 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 = sizeof(uaddr); + len = (socklen_t)sizeof(uaddr); memcpy(&ss, &uaddr, len); break; } @@ -1275,23 +2233,24 @@ addrinfo_mload(VALUE self, VALUE ary) default: { VALUE pair = rb_convert_type(v, T_ARRAY, "Array", "to_ary"); - struct addrinfo *res; + 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); + INT2NUM(flags), 1, Qnil); - len = res->ai_addrlen; - memcpy(&ss, res->ai_addr, res->ai_addrlen); + 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(rai, (struct sockaddr *)&ss, len, + init_addrinfo(self, rai, &ss.addr, len, pfamily, socktype, protocol, canonname, inspectname); return self; @@ -1364,10 +2323,11 @@ addrinfo_protocol(VALUE self) /* * 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 + * Addrinfo.tcp("localhost", 80).to_sockaddr * #=> "\x02\x00\x00P\x7F\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00" * */ @@ -1377,7 +2337,6 @@ addrinfo_to_sockaddr(VALUE self) rb_addrinfo_t *rai = get_addrinfo(self); VALUE ret; ret = rb_str_new((char*)&rai->addr, rai->sockaddr_len); - OBJ_INFECT(ret, self); return ret; } @@ -1385,14 +2344,14 @@ addrinfo_to_sockaddr(VALUE self) * call-seq: * addrinfo.canonname => string or nil * - * returns the canonical name as an string. + * 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:80)> + * 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" * */ @@ -1518,11 +2477,11 @@ addrinfo_getnameinfo(int argc, VALUE *argv, VALUE self) if (rai->socktype == SOCK_DGRAM) flags |= NI_DGRAM; - error = getnameinfo((struct sockaddr *)&rai->addr, rai->sockaddr_len, - hbuf, sizeof(hbuf), pbuf, sizeof(pbuf), - flags); + error = rb_getnameinfo(&rai->addr.addr, rai->sockaddr_len, + hbuf, (socklen_t)sizeof(hbuf), pbuf, (socklen_t)sizeof(pbuf), + flags); if (error) { - rsock_raise_socket_error("getnameinfo", error); + rsock_raise_resolution_error("getnameinfo", error); } return rb_assoc_new(rb_str_new2(hbuf), rb_str_new2(pbuf)); @@ -1546,7 +2505,7 @@ addrinfo_ip_unpack(VALUE self) VALUE ret, portstr; if (!IS_IP_FAMILY(family)) - rb_raise(rb_eSocket, "need IPv4 or IPv6 address"); + rb_raise(rb_eSocket, "need IPv4 or IPv6 address"); vflags = INT2NUM(NI_NUMERICHOST|NI_NUMERICSERV); ret = addrinfo_getnameinfo(1, &vflags, self); @@ -1573,7 +2532,7 @@ addrinfo_ip_address(VALUE self) VALUE ret; if (!IS_IP_FAMILY(family)) - rb_raise(rb_eSocket, "need IPv4 or IPv6 address"); + rb_raise(rb_eSocket, "need IPv4 or IPv6 address"); vflags = INT2NUM(NI_NUMERICHOST|NI_NUMERICSERV); ret = addrinfo_getnameinfo(1, &vflags, self); @@ -1599,9 +2558,9 @@ addrinfo_ip_port(VALUE self) if (!IS_IP_FAMILY(family)) { bad_family: #ifdef AF_INET6 - rb_raise(rb_eSocket, "need IPv4 or IPv6 address"); + rb_raise(rb_eSocket, "need IPv4 or IPv6 address"); #else - rb_raise(rb_eSocket, "need IPv4 address"); + rb_raise(rb_eSocket, "need IPv4 address"); #endif } @@ -1609,19 +2568,19 @@ addrinfo_ip_port(VALUE self) case AF_INET: if (rai->sockaddr_len != sizeof(struct sockaddr_in)) rb_raise(rb_eSocket, "unexpected sockaddr size for IPv4"); - port = ntohs(((struct sockaddr_in *)&rai->addr)->sin_port); + 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(((struct sockaddr_in6 *)&rai->addr)->sin6_port); + port = ntohs(rai->addr.in6.sin6_port); break; #endif default: - goto bad_family; + goto bad_family; } return INT2NUM(port); @@ -1633,7 +2592,7 @@ 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(((struct sockaddr_in *)&rai->addr)->sin_addr.s_addr); + *addrp = ntohl(rai->addr.in.sin_addr.s_addr); return 1; } @@ -1689,7 +2648,7 @@ 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 &((struct sockaddr_in6 *)&rai->addr)->sin6_addr; + return &rai->addr.in6.sin6_addr; } /* @@ -1729,7 +2688,7 @@ addrinfo_ipv6_multicast_p(VALUE self) } /* - * Returns true for IPv6 link local address (ff80::/10). + * Returns true for IPv6 link local address (fe80::/10). * It returns false otherwise. */ static VALUE @@ -1741,7 +2700,7 @@ addrinfo_ipv6_linklocal_p(VALUE self) } /* - * Returns true for IPv6 site local address (ffc0::/10). + * Returns true for IPv6 site local address (fec0::/10). * It returns false otherwise. */ static VALUE @@ -1753,6 +2712,18 @@ addrinfo_ipv6_sitelocal_p(VALUE self) } /* + * 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. */ @@ -1853,14 +2824,12 @@ addrinfo_ipv6_to_ipv4(VALUE self) struct in6_addr *addr; int family = ai_get_afamily(rai); if (family != AF_INET6) return Qnil; - addr = &((struct sockaddr_in6 *)&rai->addr)->sin6_addr; + addr = &rai->addr.in6.sin6_addr; if (IN6_IS_ADDR_V4MAPPED(addr) || IN6_IS_ADDR_V4COMPAT(addr)) { struct sockaddr_in sin4; - MEMZERO(&sin4, struct sockaddr_in, 1); - sin4.sin_family = AF_INET; - SET_SIN_LEN(&sin4, sizeof(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, sizeof(sin4), + return rsock_addrinfo_new((struct sockaddr *)&sin4, (socklen_t)sizeof(sin4), PF_INET, rai->socktype, rai->protocol, rai->canonname, rai->inspectname); } @@ -1871,7 +2840,7 @@ addrinfo_ipv6_to_ipv4(VALUE self) #endif -#ifdef HAVE_SYS_UN_H +#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN /* * call-seq: * addrinfo.unix_path => path @@ -1886,25 +2855,27 @@ addrinfo_unix_path(VALUE self) rb_addrinfo_t *rai = get_addrinfo(self); int family = ai_get_afamily(rai); struct sockaddr_un *addr; - char *s, *e; + long n; if (family != AF_UNIX) - rb_raise(rb_eSocket, "need AF_UNIX address"); + rb_raise(rb_eSocket, "need AF_UNIX address"); - addr = (struct sockaddr_un *)&rai->addr; + addr = &rai->addr.un; - s = addr->sun_path; - e = (char*)addr + rai->sockaddr_len; - if (e < s) - rb_raise(rb_eSocket, "too short AF_UNIX address"); - if (addr->sun_path + sizeof(addr->sun_path) < e) - rb_raise(rb_eSocket, "too long AF_UNIX address"); - while (s < e && *(e-1) == '\0') - e--; - return rb_str_new(s, e-s); + 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, ...] @@ -1921,31 +2892,46 @@ addrinfo_unix_path(VALUE self) * 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, + * 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. + * 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:80)>, - * # #<Addrinfo: [2001:200:0:8002:203:47ff:fea5:3085]:80 TCP (www.kame.net:80)>] + * #=> [#<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; + 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; + } - rb_scan_args(argc, argv, "24", &node, &service, &family, &socktype, &protocol, &flags); - return addrinfo_list_new(node, service, family, socktype, protocol, flags); + return addrinfo_list_new(node, service, family, socktype, protocol, flags, timeout); } /* @@ -2002,7 +2988,7 @@ addrinfo_s_udp(VALUE self, VALUE host, VALUE port) INT2NUM(PF_UNSPEC), INT2NUM(SOCK_DGRAM), INT2NUM(IPPROTO_UDP), INT2FIX(0)); } -#ifdef HAVE_SYS_UN_H +#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN /* * call-seq: @@ -2032,8 +3018,7 @@ addrinfo_s_unix(int argc, VALUE *argv, VALUE self) addr = addrinfo_s_allocate(rb_cAddrinfo); DATA_PTR(addr) = rai = alloc_addrinfo(); - init_unix_addrinfo(rai, path, socktype); - OBJ_INFECT(addr, path); + init_unix_addrinfo(self, rai, path, socktype); return addr; } @@ -2050,6 +3035,19 @@ rsock_sockaddr_string_value(volatile VALUE *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) { @@ -2071,7 +3069,7 @@ rsock_fd_socket_addrinfo(int fd, struct sockaddr *addr, socklen_t len) int family; int socktype; int ret; - socklen_t optlen = sizeof(socktype); + socklen_t optlen = (socklen_t)sizeof(socktype); /* assumes protocol family and address family are identical */ family = get_afamily(addr, len); @@ -2103,24 +3101,125 @@ rsock_io_socket_addrinfo(VALUE io, struct sockaddr *addr, socklen_t 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 -Init_addrinfo(void) +rsock_init_addrinfo(void) { - rb_cAddrinfo = rb_define_class("Addrinfo", rb_cData); + 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", addrinfo_inspect_sockaddr, 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_SYS_UN_H +#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN rb_define_singleton_method(rb_cAddrinfo, "unix", addrinfo_s_unix, -1); #endif @@ -2149,6 +3248,7 @@ Init_addrinfo(void) 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); @@ -2160,7 +3260,7 @@ Init_addrinfo(void) rb_define_method(rb_cAddrinfo, "ipv6_to_ipv4", addrinfo_ipv6_to_ipv4, 0); #endif -#ifdef HAVE_SYS_UN_H +#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN rb_define_method(rb_cAddrinfo, "unix_path", addrinfo_unix_path, 0); #endif |
