summaryrefslogtreecommitdiff
path: root/ext/socket/raddrinfo.c
diff options
context:
space:
mode:
Diffstat (limited to 'ext/socket/raddrinfo.c')
-rw-r--r--ext/socket/raddrinfo.c195
1 files changed, 137 insertions, 58 deletions
diff --git a/ext/socket/raddrinfo.c b/ext/socket/raddrinfo.c
index ef8226b66c..f1fbcc35de 100644
--- a/ext/socket/raddrinfo.c
+++ b/ext/socket/raddrinfo.c
@@ -293,10 +293,22 @@ rb_freeaddrinfo(struct rb_addrinfo *ai)
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)
+rb_getaddrinfo(const char *hostp, const char *portp, const struct addrinfo *hints, struct addrinfo **ai, int _timeout)
{
return getaddrinfo(hostp, portp, hints, ai);
}
@@ -334,7 +346,7 @@ fork_safe_getaddrinfo(void *arg)
}
static int
-rb_getaddrinfo(const char *hostp, const char *portp, const struct addrinfo *hints, struct addrinfo **ai)
+rb_getaddrinfo(const char *hostp, const char *portp, const struct addrinfo *hints, struct addrinfo **ai, int _timeout)
{
struct getaddrinfo_arg arg;
MEMZERO(&arg, struct getaddrinfo_arg, 1);
@@ -352,13 +364,14 @@ struct getaddrinfo_arg
char *node, *service;
struct addrinfo hints;
struct addrinfo *ai;
- int err, gai_errno, refcount, done, cancelled;
+ 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)
+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);
@@ -374,7 +387,7 @@ allocate_getaddrinfo_arg(const char *hostp, const char *portp, const struct addr
if (hostp) {
arg->node = buf + hostp_offset;
- strcpy(arg->node, hostp);
+ memcpy(arg->node, hostp, portp_offset - hostp_offset);
}
else {
arg->node = NULL;
@@ -382,7 +395,7 @@ allocate_getaddrinfo_arg(const char *hostp, const char *portp, const struct addr
if (portp) {
arg->service = buf + portp_offset;
- strcpy(arg->service, portp);
+ memcpy(arg->service, portp, bufsize - portp_offset);
}
else {
arg->service = NULL;
@@ -392,7 +405,8 @@ allocate_getaddrinfo_arg(const char *hostp, const char *portp, const struct addr
arg->ai = NULL;
arg->refcount = 2;
- arg->done = arg->cancelled = 0;
+ arg->done = arg->cancelled = arg->timedout = 0;
+ arg->timeout = timeout;
rb_nativethread_lock_initialize(&arg->lock);
rb_native_cond_initialize(&arg->cond);
@@ -451,7 +465,19 @@ 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);
+ 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;
@@ -473,13 +499,49 @@ 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, 0, start_routine, arg);
+ 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;
}
@@ -490,16 +552,16 @@ fork_safe_do_getaddrinfo(void *ptr)
}
static int
-rb_getaddrinfo(const char *hostp, const char *portp, const struct addrinfo *hints, struct addrinfo **ai)
+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;
+ int err = 0, gai_errno = 0, timedout = 0;
start:
retry = 0;
- arg = allocate_getaddrinfo_arg(hostp, portp, hints);
+ arg = allocate_getaddrinfo_arg(hostp, portp, hints, timeout);
if (!arg) {
return EAI_MEMORY;
}
@@ -511,7 +573,6 @@ start:
errno = err;
return EAI_SYSTEM;
}
- pthread_detach(th);
rb_thread_call_without_gvl2(wait_getaddrinfo, arg, cancel_getaddrinfo, arg);
@@ -525,6 +586,7 @@ start:
}
else if (arg->cancelled) {
retry = 1;
+ timedout = arg->timedout;
}
else {
// If already interrupted, rb_thread_call_without_gvl2 may return without calling wait_getaddrinfo.
@@ -538,6 +600,12 @@ start:
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();
@@ -552,6 +620,10 @@ start:
#endif
+#define GETNAMEINFO_WONT_BLOCK(host, serv, flags) \
+ ((!(host) || ((flags) & NI_NUMERICHOST)) && \
+ (!(serv) || ((flags) & NI_NUMERICSERV)))
+
#if GETADDRINFO_IMPL == 0
int
@@ -559,7 +631,7 @@ 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);
+ return getnameinfo(sa, salen, host, (socklen_t)hostlen, serv, (socklen_t)servlen, flags);
}
#elif GETADDRINFO_IMPL == 1
@@ -589,6 +661,10 @@ rb_getnameinfo(const struct sockaddr *sa, socklen_t salen,
char *host, size_t hostlen,
char *serv, size_t servlen, int flags)
{
+ if (GETNAMEINFO_WONT_BLOCK(host, serv, flags)) {
+ return getnameinfo(sa, salen, host, hostlen, serv, servlen, flags);
+ }
+
struct getnameinfo_arg arg;
int ret;
arg.sa = sa;
@@ -715,7 +791,11 @@ rb_getnameinfo(const struct sockaddr *sa, socklen_t salen,
{
int retry;
struct getnameinfo_arg *arg;
- int err, gni_errno = 0;
+ 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;
@@ -732,7 +812,6 @@ start:
errno = err;
return EAI_SYSTEM;
}
- pthread_detach(th);
rb_thread_call_without_gvl2(wait_getnameinfo, arg, cancel_getnameinfo, arg);
@@ -823,7 +902,7 @@ str_is_number(const char *p)
rb_strlen_lit(name) == (len) && memcmp(ptr, name, len) == 0)
char*
-host_str(VALUE host, char *hbuf, size_t hbuflen, int *flags_ptr)
+raddrinfo_host_str(VALUE host, char *hbuf, size_t hbuflen, int *flags_ptr)
{
if (NIL_P(host)) {
return NULL;
@@ -862,7 +941,7 @@ host_str(VALUE host, char *hbuf, size_t hbuflen, int *flags_ptr)
}
char*
-port_str(VALUE port, char *pbuf, size_t pbuflen, int *flags_ptr)
+raddrinfo_port_str(VALUE port, char *pbuf, size_t pbuflen, int *flags_ptr)
{
if (NIL_P(port)) {
return 0;
@@ -914,7 +993,7 @@ rb_scheduler_getaddrinfo(VALUE scheduler, VALUE host, const char *service,
for(i=0; i<len; i++) {
ip_address = rb_ary_entry(ip_addresses_array, i);
- hostp = host_str(ip_address, _hbuf, sizeof(_hbuf), &_additional_flags);
+ hostp = raddrinfo_host_str(ip_address, _hbuf, sizeof(_hbuf), &_additional_flags);
error = numeric_getaddrinfo(hostp, service, hints, &ai);
if (error == 0) {
if (!res_allocated) {
@@ -941,7 +1020,7 @@ rb_scheduler_getaddrinfo(VALUE scheduler, VALUE host, const char *service,
}
struct rb_addrinfo*
-rsock_getaddrinfo(VALUE host, VALUE port, struct addrinfo *hints, int socktype_hack)
+rsock_getaddrinfo(VALUE host, VALUE port, struct addrinfo *hints, int socktype_hack, VALUE timeout)
{
struct rb_addrinfo* res = NULL;
struct addrinfo *ai;
@@ -950,8 +1029,8 @@ rsock_getaddrinfo(VALUE host, VALUE port, struct addrinfo *hints, int socktype_h
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_is_number(portp)) {
hints->ai_socktype = SOCK_DGRAM;
@@ -976,7 +1055,8 @@ rsock_getaddrinfo(VALUE host, VALUE port, struct addrinfo *hints, int socktype_h
}
if (!resolved) {
- error = rb_getaddrinfo(hostp, portp, hints, &ai);
+ 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;
@@ -1009,7 +1089,7 @@ rsock_fd_family(int fd)
}
struct rb_addrinfo*
-rsock_addrinfo(VALUE host, VALUE port, int family, int socktype, int flags)
+rsock_addrinfo(VALUE host, VALUE port, int family, int socktype, int flags, VALUE timeout)
{
struct addrinfo hints;
@@ -1017,7 +1097,7 @@ rsock_addrinfo(VALUE host, VALUE port, int family, int socktype, int flags)
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
@@ -1137,7 +1217,7 @@ make_hostent_internal(VALUE v)
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));
@@ -1211,6 +1291,7 @@ addrinfo_memsize(const void *ptr)
static const rb_data_type_t addrinfo_type = {
"socket/addrinfo",
{addrinfo_mark, addrinfo_free, addrinfo_memsize,},
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_FROZEN_SHAREABLE | RUBY_TYPED_WB_PROTECTED,
};
static VALUE
@@ -1248,7 +1329,7 @@ alloc_addrinfo(void)
}
static void
-init_addrinfo(rb_addrinfo_t *rai, struct sockaddr *sa, socklen_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)
{
@@ -1260,8 +1341,8 @@ init_addrinfo(rb_addrinfo_t *rai, struct sockaddr *sa, socklen_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
@@ -1274,7 +1355,7 @@ 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;
}
@@ -1299,7 +1380,7 @@ call_getaddrinfo(VALUE node, VALUE service,
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");
@@ -1309,7 +1390,7 @@ call_getaddrinfo(VALUE node, VALUE service,
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)
{
@@ -1323,7 +1404,7 @@ init_addrinfo_getaddrinfo(rb_addrinfo_t *rai, VALUE node, VALUE service,
OBJ_FREEZE(canonname);
}
- init_addrinfo(rai, res->ai->ai_addr, res->ai->ai_addrlen,
+ init_addrinfo(self, rai, res->ai->ai_addr, res->ai->ai_addrlen,
NUM2INT(family), NUM2INT(socktype), NUM2INT(protocol),
canonname, inspectname);
@@ -1435,7 +1516,7 @@ addrinfo_list_new(VALUE node, VALUE service, VALUE family, VALUE socktype, VALUE
#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;
@@ -1451,7 +1532,7 @@ init_unix_addrinfo(rb_addrinfo_t *rai, VALUE path, int socktype)
memcpy((void*)&un.sun_path, RSTRING_PTR(path), RSTRING_LEN(path));
len = rsock_unix_sockaddr_len(path);
- init_addrinfo(rai, (struct sockaddr *)&un, len,
+ init_addrinfo(self, rai, (struct sockaddr *)&un, len,
PF_UNIX, socktype, 0, Qnil, Qnil);
}
@@ -1555,7 +1636,7 @@ 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);
@@ -1567,7 +1648,7 @@ addrinfo_initialize(int argc, VALUE *argv, VALUE self)
{
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
@@ -1580,7 +1661,7 @@ addrinfo_initialize(int argc, VALUE *argv, VALUE self)
StringValue(sockaddr_arg);
sockaddr_ptr = (struct sockaddr *)RSTRING_PTR(sockaddr_arg);
sockaddr_len = RSTRING_SOCKLEN(sockaddr_arg);
- init_addrinfo(rai, sockaddr_ptr, sockaddr_len,
+ init_addrinfo(self, rai, sockaddr_ptr, sockaddr_len,
i_pfamily, i_socktype, i_protocol,
canonname, inspectname);
}
@@ -2169,7 +2250,7 @@ addrinfo_mload(VALUE self, VALUE ary)
}
DATA_PTR(self) = rai = alloc_addrinfo();
- init_addrinfo(rai, &ss.addr, len,
+ init_addrinfo(self, rai, &ss.addr, len,
pfamily, socktype, protocol,
canonname, inspectname);
return self;
@@ -2937,7 +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);
+ init_unix_addrinfo(self, rai, path, socktype);
return addr;
}
@@ -3029,31 +3110,22 @@ rsock_io_socket_addrinfo(VALUE io, struct sockaddr *addr, socklen_t len)
void
free_fast_fallback_getaddrinfo_shared(struct fast_fallback_getaddrinfo_shared **shared)
{
- free((*shared)->node);
+ xfree((*shared)->node);
(*shared)->node = NULL;
- free((*shared)->service);
+ xfree((*shared)->service);
(*shared)->service = NULL;
rb_nativethread_lock_destroy(&(*shared)->lock);
free(*shared);
*shared = NULL;
}
-void
-free_fast_fallback_getaddrinfo_entry(struct fast_fallback_getaddrinfo_entry **entry)
-{
- if ((*entry)->ai) {
- freeaddrinfo((*entry)->ai);
- (*entry)->ai = NULL;
- }
- *entry = NULL;
-}
-
-void *
+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, need_free = 0, shared_need_free = 0;
+ int err = 0, shared_need_free = 0;
+ struct addrinfo *ai = NULL;
sigset_t set;
sigemptyset(&set);
@@ -3102,14 +3174,15 @@ do_fast_fallback_getaddrinfo(void *ptr)
entry->err = errno;
entry->has_syserr = true;
}
- if (--(entry->refcount) == 0) need_free = 1;
+ 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 (need_free && entry) {
- free_fast_fallback_getaddrinfo_entry(&entry);
- }
+ if (ai) freeaddrinfo(ai);
if (shared_need_free && shared) {
free_fast_fallback_getaddrinfo_shared(&shared);
}
@@ -3117,6 +3190,12 @@ do_fast_fallback_getaddrinfo(void *ptr)
return 0;
}
+void *
+fork_safe_do_fast_fallback_getaddrinfo(void *ptr)
+{
+ return rb_thread_prevent_fork(do_fast_fallback_getaddrinfo, ptr);
+}
+
#endif
/*