diff options
Diffstat (limited to 'ext/socket/raddrinfo.c')
-rw-r--r-- | ext/socket/raddrinfo.c | 817 |
1 files changed, 616 insertions, 201 deletions
diff --git a/ext/socket/raddrinfo.c b/ext/socket/raddrinfo.c index d94e96a2bc..7ec700075e 100644 --- a/ext/socket/raddrinfo.c +++ b/ext/socket/raddrinfo.c @@ -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, - const 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); } @@ -102,7 +118,7 @@ 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) + const struct addrinfo *hints, struct addrinfo **res) { /* fix [ruby-core:29427] */ const char *tmp_servname; @@ -112,12 +128,12 @@ ruby_getaddrinfo__darwin(const char *nodename, const char *servname, 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; + 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; + if (tmp_hints.ai_flags) tmp_hints.ai_flags &= ~AI_NUMERICSERV; #endif - } + } } error = getaddrinfo(nodename, tmp_servname, &tmp_hints, res); @@ -173,32 +189,6 @@ parse_numeric_port(const char *service, int *portp) } #endif -#ifndef GETADDRINFO_EMU -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; -} -#endif - static int numeric_getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, @@ -212,15 +202,15 @@ numeric_getaddrinfo(const char *node, const char *service, 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; + 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; @@ -287,8 +277,9 @@ numeric_getaddrinfo(const char *node, const char *service, void rb_freeaddrinfo(struct rb_addrinfo *ai) { - if (!ai->allocated_by_malloc) - freeaddrinfo(ai->ai); + if (!ai->allocated_by_malloc) { + if (ai->ai) freeaddrinfo(ai->ai); + } else { struct addrinfo *ai1, *ai2; ai1 = ai->ai; @@ -302,7 +293,277 @@ rb_freeaddrinfo(struct rb_addrinfo *ai) xfree(ai); } -#ifndef GETADDRINFO_EMU +#if GETADDRINFO_IMPL == 0 + +static int +rb_getaddrinfo(const char *hostp, const char *portp, const struct addrinfo *hints, struct addrinfo **ai) +{ + 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) +{ + 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; + rb_nativethread_lock_t lock; + rb_nativethread_cond_t cond; +}; + +static struct getaddrinfo_arg * +allocate_getaddrinfo_arg(const char *hostp, const char *portp, const struct addrinfo *hints) +{ + 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; + strcpy(arg->node, hostp); + } + else { + arg->node = NULL; + } + + if (portp) { + arg->service = buf + portp_offset; + strcpy(arg->service, portp); + } + else { + arg->service = NULL; + } + + arg->hints = *hints; + arg->ai = NULL; + + 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_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) { + 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); +} + +static int +do_pthread_create(pthread_t *th, void *(*start_routine) (void *), void *arg) +{ + int limit = 3, ret; + 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, 0, start_routine, arg); + } while (ret == EAGAIN && limit-- > 0); + 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 retry; + struct getaddrinfo_arg *arg; + int err = 0, gai_errno = 0; + +start: + retry = 0; + + arg = allocate_getaddrinfo_arg(hostp, portp, hints); + if (!arg) { + return EAI_MEMORY; + } + + pthread_t th; + if (do_pthread_create(&th, fork_safe_do_getaddrinfo, arg) != 0) { + int err = errno; + free_getaddrinfo_arg(arg); + errno = err; + return EAI_SYSTEM; + } + pthread_detach(th); + + 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; + } + 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 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 + +#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, hostlen, serv, servlen, flags); +} + +#elif GETADDRINFO_IMPL == 1 + struct getnameinfo_arg { const struct sockaddr *sa; @@ -319,20 +580,15 @@ 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); + 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 struct getnameinfo_arg arg; int ret; arg.sa = sa; @@ -344,17 +600,187 @@ rb_getnameinfo(const struct sockaddr *sa, socklen_t salen, arg.flags = flags; 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 +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, gni_errno = 0; + +start: + retry = 0; + + arg = allocate_getnameinfo_arg(sa, salen, hostlen, servlen, flags); + if (!arg) { + return EAI_MEMORY; + } + + pthread_t th; + if (do_pthread_create(&th, do_getnameinfo, arg) != 0) { + int err = errno; + free_getnameinfo_arg(arg); + errno = err; + return EAI_SYSTEM; + } + pthread_detach(th); + + 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_socket_error("getnameinfo", error); + rsock_raise_resolution_error("getnameinfo", error); } } @@ -550,17 +976,7 @@ rsock_getaddrinfo(VALUE host, VALUE port, struct addrinfo *hints, int socktype_h } if (!resolved) { -#ifdef GETADDRINFO_EMU - error = getaddrinfo(hostp, portp, hints, &ai); -#else - struct getaddrinfo_arg arg; - MEMZERO(&arg, struct getaddrinfo_arg, 1); - arg.node = hostp; - arg.service = portp; - arg.hints = hints; - arg.res = &ai; - error = (int)(VALUE)rb_thread_call_without_gvl(nogvl_getaddrinfo, &arg, RUBY_UBF_IO, 0); -#endif + error = rb_getaddrinfo(hostp, portp, hints, &ai); if (error == 0) { res = (struct rb_addrinfo *)xmalloc(sizeof(struct rb_addrinfo)); res->allocated_by_malloc = 0; @@ -573,7 +989,7 @@ rsock_getaddrinfo(VALUE host, VALUE port, struct addrinfo *hints, int socktype_h 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); } return res; @@ -587,7 +1003,7 @@ rsock_fd_family(int fd) 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 AF_UNSPEC; } return sa.sa_family; } @@ -618,8 +1034,7 @@ rsock_ipaddr(struct sockaddr *sockaddr, socklen_t sockaddrlen, 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; @@ -633,7 +1048,7 @@ rsock_ipaddr(struct sockaddr *sockaddr, socklen_t sockaddrlen, int norevlookup) 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) { @@ -645,7 +1060,7 @@ rsock_ipaddr(struct sockaddr *sockaddr, socklen_t sockaddrlen, int norevlookup) return ary; } -#ifdef HAVE_SYS_UN_H +#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN static long unixsocket_len(const struct sockaddr_un *su, socklen_t socklen) { @@ -677,19 +1092,19 @@ 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); + /* 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. */ + /* 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); + return (socklen_t) offsetof(struct sockaddr_un, sun_path) + + RSTRING_SOCKLEN(path); } else { #endif - return (socklen_t) sizeof(struct sockaddr_un); + return (socklen_t) sizeof(struct sockaddr_un); #ifdef __linux__ } #endif @@ -727,7 +1142,7 @@ make_hostent_internal(VALUE v) rb_ary_push(ary, rb_str_new2(hostp)); if (addr->ai_canonname && strlen(addr->ai_canonname) < NI_MAXHOST && - (h = gethostbyname(addr->ai_canonname))) { + (h = gethostbyname(addr->ai_canonname))) { names = rb_ary_new(); if (h->h_aliases != NULL) { for (pch = h->h_aliases; *pch; pch++) { @@ -875,19 +1290,19 @@ call_getaddrinfo(VALUE node, VALUE service, 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); if (res == NULL) - rb_raise(rb_eSocket, "host not found"); + rb_raise(rb_eSocket, "host not found"); return res; } @@ -1018,7 +1433,7 @@ addrinfo_list_new(VALUE node, VALUE service, VALUE family, VALUE socktype, VALUE } -#ifdef HAVE_SYS_UN_H +#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN static void init_unix_addrinfo(rb_addrinfo_t *rai, VALUE path, int socktype) { @@ -1037,7 +1452,7 @@ init_unix_addrinfo(rb_addrinfo_t *rai, VALUE path, int socktype) len = rsock_unix_sockaddr_len(path); init_addrinfo(rai, (struct sockaddr *)&un, len, - PF_UNIX, socktype, 0, Qnil, Qnil); + PF_UNIX, socktype, 0, Qnil, Qnil); } static long @@ -1065,13 +1480,13 @@ rai_unixsocket_len(const rb_addrinfo_t *rai) * 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. @@ -1119,7 +1534,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 @@ -1147,7 +1562,7 @@ addrinfo_initialize(int argc, VALUE *argv, VALUE self) 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); @@ -1209,45 +1624,45 @@ rsock_inspect_sockaddr(struct sockaddr *sockaddr_arg, socklen_t socklen, VALUE r else { switch (sockaddr->addr.sa_family) { case AF_UNSPEC: - { - rb_str_cat2(ret, "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)); + 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; } @@ -1267,11 +1682,11 @@ rsock_inspect_sockaddr(struct sockaddr *sockaddr_arg, socklen_t socklen, VALUE r * RFC 4007: IPv6 Scoped Address Architecture * draft-ietf-ipv6-scope-api-00.txt: Scoped Address Extensions to the IPv6 Basic Socket API */ - error = getnameinfo(&sockaddr->addr, socklen, - hbuf, (socklen_t)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); @@ -1287,7 +1702,7 @@ rsock_inspect_sockaddr(struct sockaddr *sockaddr_arg, socklen_t socklen, VALUE r } #endif -#ifdef HAVE_SYS_UN_H +#ifdef HAVE_TYPE_STRUCT_SOCKADDR_UN case AF_UNIX: { struct sockaddr_un *addr = &sockaddr->un; @@ -1398,20 +1813,20 @@ rsock_inspect_sockaddr(struct sockaddr *sockaddr_arg, socklen_t socklen, VALUE r #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. + /* 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). + { + /* + * 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; - */ + * + * 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; @@ -1438,14 +1853,14 @@ rsock_inspect_sockaddr(struct sockaddr *sockaddr_arg, socklen_t socklen, VALUE r alen = (int)(endp - ap); } - CATSEP; + CATSEP; if (np) rb_str_catf(ret, "%.*s", nlen, np); else rb_str_cat2(ret, "?"); if (ap && 0 < alen) { - CATSEP; + CATSEP; for (i = 0; i < alen; i++) rb_str_catf(ret, "%s%02x", i == 0 ? "" : ":", (unsigned char)ap[i]); } @@ -1456,10 +1871,10 @@ rsock_inspect_sockaddr(struct sockaddr *sockaddr_arg, socklen_t socklen, VALUE r /* 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; + 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 @@ -1623,7 +2038,7 @@ 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: { sockaddr = rb_str_new(rai->addr.un.sun_path, rai_unixsocket_len(rai)); @@ -1635,11 +2050,11 @@ addrinfo_mdump(VALUE self) { char hbuf[NI_MAXHOST], pbuf[NI_MAXSERV]; int error; - error = getnameinfo(&rai->addr.addr, rai->sockaddr_len, - hbuf, (socklen_t)sizeof(hbuf), pbuf, (socklen_t)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; @@ -1716,7 +2131,7 @@ 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; @@ -1981,11 +2396,11 @@ addrinfo_getnameinfo(int argc, VALUE *argv, VALUE self) if (rai->socktype == SOCK_DGRAM) flags |= NI_DGRAM; - error = getnameinfo(&rai->addr.addr, rai->sockaddr_len, - hbuf, (socklen_t)sizeof(hbuf), pbuf, (socklen_t)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)); @@ -2009,7 +2424,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); @@ -2036,7 +2451,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); @@ -2062,9 +2477,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 } @@ -2084,7 +2499,7 @@ addrinfo_ip_port(VALUE self) #endif default: - goto bad_family; + goto bad_family; } return INT2NUM(port); @@ -2192,7 +2607,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 @@ -2204,7 +2619,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 @@ -2344,7 +2759,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 @@ -2362,7 +2777,7 @@ addrinfo_unix_path(VALUE self) long n; if (family != AF_UNIX) - rb_raise(rb_eSocket, "need AF_UNIX address"); + rb_raise(rb_eSocket, "need AF_UNIX address"); addr = &rai->addr.un; @@ -2429,10 +2844,10 @@ 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); + &protocol, &flags, &opts); rb_get_kwargs(opts, &id_timeout, 0, 1, &timeout); if (timeout == Qundef) { - timeout = Qnil; + timeout = Qnil; } return addrinfo_list_new(node, service, family, socktype, protocol, flags, timeout); @@ -2492,7 +2907,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: @@ -2615,12 +3030,12 @@ rsock_io_socket_addrinfo(VALUE io, struct sockaddr *addr, socklen_t len) 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. */ - id_timeout = rb_intern("timeout"); - 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); @@ -2630,7 +3045,7 @@ rsock_init_addrinfo(void) 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 @@ -2671,7 +3086,7 @@ rsock_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 |