summaryrefslogtreecommitdiff
path: root/ext/socket/ipsocket.c
diff options
context:
space:
mode:
Diffstat (limited to 'ext/socket/ipsocket.c')
-rw-r--r--ext/socket/ipsocket.c447
1 files changed, 271 insertions, 176 deletions
diff --git a/ext/socket/ipsocket.c b/ext/socket/ipsocket.c
index 5e5b447266..e1943b8496 100644
--- a/ext/socket/ipsocket.c
+++ b/ext/socket/ipsocket.c
@@ -23,8 +23,25 @@ struct inetsock_arg
int type;
VALUE resolv_timeout;
VALUE connect_timeout;
+ VALUE open_timeout;
};
+void
+rsock_raise_user_specified_timeout(struct addrinfo *ai, VALUE host, VALUE port)
+{
+ VALUE message;
+
+ if (ai && ai->ai_addr) {
+ VALUE rai = rsock_addrinfo_new((struct sockaddr *)ai->ai_addr, (socklen_t)ai->ai_addrlen, PF_UNSPEC, 0, 0, Qnil, Qnil);
+ VALUE addr_str = rsock_addrinfo_inspect_sockaddr(rai);
+ message = rb_sprintf("user specified timeout for %" PRIsVALUE, addr_str);
+ } else {
+ message = rb_sprintf("user specified timeout for %" PRIsVALUE " port %" PRIsVALUE, host, port);
+ }
+
+ rb_exc_raise(rb_exc_new_str(rb_eIOTimeoutError, message));
+}
+
static VALUE
inetsock_cleanup(VALUE v)
{
@@ -45,6 +62,13 @@ inetsock_cleanup(VALUE v)
}
static VALUE
+current_clocktime(void)
+{
+ VALUE clock_monotnic_const = rb_const_get(rb_mProcess, rb_intern("CLOCK_MONOTONIC"));
+ return rb_funcall(rb_mProcess, rb_intern("clock_gettime"), 1, clock_monotnic_const);
+}
+
+static VALUE
init_inetsock_internal(VALUE v)
{
struct inetsock_arg *arg = (void *)v;
@@ -54,12 +78,18 @@ init_inetsock_internal(VALUE v)
int status = 0, local = 0;
int family = AF_UNSPEC;
const char *syscall = 0;
+ VALUE resolv_timeout = arg->resolv_timeout;
VALUE connect_timeout = arg->connect_timeout;
+ VALUE open_timeout = arg->open_timeout;
+ VALUE timeout;
+ VALUE starts_at;
+
+ timeout = NIL_P(open_timeout) ? resolv_timeout : open_timeout;
+ starts_at = current_clocktime();
arg->remote.res = rsock_addrinfo(arg->remote.host, arg->remote.serv,
family, SOCK_STREAM,
- (type == INET_SERVER) ? AI_PASSIVE : 0);
-
+ (type == INET_SERVER) ? AI_PASSIVE : 0, timeout);
/*
* Maybe also accept a local address
@@ -67,7 +97,7 @@ init_inetsock_internal(VALUE v)
if (type != INET_SERVER && (!NIL_P(arg->local.host) || !NIL_P(arg->local.serv))) {
arg->local.res = rsock_addrinfo(arg->local.host, arg->local.serv,
- family, SOCK_STREAM, 0);
+ family, SOCK_STREAM, 0, timeout);
}
VALUE io = Qnil;
@@ -122,8 +152,18 @@ init_inetsock_internal(VALUE v)
syscall = "bind(2)";
}
+ if (NIL_P(open_timeout)) {
+ timeout = connect_timeout;
+ } else {
+ VALUE elapsed = rb_funcall(current_clocktime(), '-', 1, starts_at);
+ timeout = rb_funcall(open_timeout, '-', 1, elapsed);
+ if (rb_funcall(timeout, '<', 1, INT2FIX(0)) == Qtrue) {
+ rsock_raise_user_specified_timeout(res, arg->remote.host, arg->remote.serv);
+ }
+ }
+
if (status >= 0) {
- status = rsock_connect(io, res->ai_addr, res->ai_addrlen, (type == INET_SOCKS), connect_timeout);
+ status = rsock_connect(io, res->ai_addr, res->ai_addrlen, (type == INET_SOCKS), timeout);
syscall = "connect(2)";
}
}
@@ -172,8 +212,16 @@ init_inetsock_internal(VALUE v)
#if FAST_FALLBACK_INIT_INETSOCK_IMPL == 0
VALUE
-rsock_init_inetsock(VALUE self, VALUE remote_host, VALUE remote_serv, VALUE local_host, VALUE local_serv, int type, VALUE resolv_timeout, VALUE connect_timeout, VALUE _fast_fallback, VALUE _test_mode_settings)
+rsock_init_inetsock(
+ VALUE self, VALUE remote_host, VALUE remote_serv,
+ VALUE local_host, VALUE local_serv, int type,
+ VALUE resolv_timeout, VALUE connect_timeout, VALUE open_timeout,
+ VALUE _fast_fallback, VALUE _test_mode_settings)
{
+ if (!NIL_P(open_timeout) && (!NIL_P(resolv_timeout) || !NIL_P(connect_timeout))) {
+ rb_raise(rb_eArgError, "Cannot specify open_timeout along with connect_timeout or resolv_timeout");
+ }
+
struct inetsock_arg arg;
arg.self = self;
arg.io = Qnil;
@@ -186,6 +234,7 @@ rsock_init_inetsock(VALUE self, VALUE remote_host, VALUE remote_serv, VALUE loca
arg.type = type;
arg.resolv_timeout = resolv_timeout;
arg.connect_timeout = connect_timeout;
+ arg.open_timeout = open_timeout;
return rb_ensure(init_inetsock_internal, (VALUE)&arg,
inetsock_cleanup, (VALUE)&arg);
}
@@ -209,6 +258,21 @@ is_specified_ip_address(const char *hostname)
inet_pton(AF_INET, hostname, &ipv4addr) == 1);
}
+static int
+is_local_port_fixed(const char *portp)
+{
+ if (!portp) return 0;
+
+ char *endp;
+ errno = 0;
+ long port = strtol(portp, &endp, 10);
+
+ if (endp == portp) return 0;
+ if (errno == ERANGE) return 0;
+
+ return port > 0;
+}
+
struct fast_fallback_inetsock_arg
{
VALUE self;
@@ -221,6 +285,7 @@ struct fast_fallback_inetsock_arg
int type;
VALUE resolv_timeout;
VALUE connect_timeout;
+ VALUE open_timeout;
const char *hostp, *portp;
int *families;
@@ -228,6 +293,7 @@ struct fast_fallback_inetsock_arg
int additional_flags;
struct fast_fallback_getaddrinfo_entry *getaddrinfo_entries[2];
struct fast_fallback_getaddrinfo_shared *getaddrinfo_shared;
+ rb_fdset_t readfds, writefds;
int wait;
int connection_attempt_fds_size;
int *connection_attempt_fds;
@@ -283,43 +349,6 @@ reallocate_connection_attempt_fds(int **fds, int current_capacity, int additiona
return new_capacity;
}
-struct wait_fast_fallback_arg
-{
- int status, nfds;
- fd_set *readfds, *writefds;
- struct timeval *delay;
- int *cancelled;
-};
-
-static void *
-wait_fast_fallback(void *ptr)
-{
- struct wait_fast_fallback_arg *arg = (struct wait_fast_fallback_arg *)ptr;
- int status;
- status = select(arg->nfds, arg->readfds, arg->writefds, NULL, arg->delay);
- arg->status = status;
- if (errno == EINTR) *arg->cancelled = true;
- return 0;
-}
-
-static void
-cancel_fast_fallback(void *ptr)
-{
- if (!ptr) return;
-
- struct fast_fallback_getaddrinfo_shared *arg = (struct fast_fallback_getaddrinfo_shared *)ptr;
-
- rb_nativethread_lock_lock(&arg->lock);
- {
- arg->cancelled = true;
- char notification = SELECT_CANCELLED;
- if (arg->notify != -1 && (write(arg->notify, &notification, 1)) < 0) {
- rb_syserr_fail(errno, "write(2)");
- }
- }
- rb_nativethread_lock_unlock(&arg->lock);
-}
-
struct hostname_resolution_result
{
struct addrinfo *ai;
@@ -331,7 +360,7 @@ struct hostname_resolution_store
{
struct hostname_resolution_result v6;
struct hostname_resolution_result v4;
- int is_all_finised;
+ int is_all_finished;
};
static int
@@ -416,12 +445,22 @@ select_expires_at(
struct timeval *resolution_delay,
struct timeval *connection_attempt_delay,
struct timeval *user_specified_resolv_timeout_at,
- struct timeval *user_specified_connect_timeout_at
-) {
+ struct timeval *user_specified_connect_timeout_at,
+ struct timeval *user_specified_open_timeout_at)
+{
if (any_addrinfos(resolution_store)) {
- return resolution_delay ? resolution_delay : connection_attempt_delay;
+ struct timeval *delay;
+ delay = resolution_delay ? resolution_delay : connection_attempt_delay;
+
+ if (user_specified_open_timeout_at &&
+ timercmp(user_specified_open_timeout_at, delay, <)) {
+ return user_specified_open_timeout_at;
+ }
+ return delay;
}
+ if (user_specified_open_timeout_at) return user_specified_open_timeout_at;
+
struct timeval *timeout = NULL;
if (user_specified_resolv_timeout_at) {
@@ -510,7 +549,8 @@ in_progress_fds(int fds_size)
}
static void
-remove_connection_attempt_fd(int *fds, int *fds_size, int removing_fd) {
+remove_connection_attempt_fd(int *fds, int *fds_size, int removing_fd)
+{
int i, j;
for (i = 0; i < *fds_size; i++) {
@@ -539,6 +579,7 @@ init_fast_fallback_inetsock_internal(VALUE v)
VALUE io = arg->io;
VALUE resolv_timeout = arg->resolv_timeout;
VALUE connect_timeout = arg->connect_timeout;
+ VALUE open_timeout = arg->open_timeout;
VALUE test_mode_settings = arg->test_mode_settings;
struct addrinfo *remote_ai = NULL, *local_ai = NULL;
int connected_fd = -1, status = 0, local_status = 0;
@@ -556,16 +597,14 @@ init_fast_fallback_inetsock_internal(VALUE v)
ssize_t resolved_type_size;
int hostname_resolution_waiter = -1, hostname_resolution_notifier = -1;
int pipefd[2];
- fd_set readfds, writefds;
- struct wait_fast_fallback_arg wait_arg;
+ int nfds = 0;
struct timeval *ends_at = NULL;
struct timeval delay = (struct timeval){ -1, -1 };
- wait_arg.writefds = &writefds;
- wait_arg.status = 0;
+ struct timeval *delay_p = NULL;
struct hostname_resolution_store resolution_store;
- resolution_store.is_all_finised = false;
+ resolution_store.is_all_finished = false;
resolution_store.v6.ai = NULL;
resolution_store.v6.finished = false;
resolution_store.v6.has_error = false;
@@ -574,33 +613,11 @@ init_fast_fallback_inetsock_internal(VALUE v)
resolution_store.v4.has_error = false;
int last_family = 0;
-
int additional_capacity = 10;
int current_capacity = additional_capacity;
arg->connection_attempt_fds = allocate_connection_attempt_fds(additional_capacity);
arg->connection_attempt_fds_size = 0;
- if (pipe(pipefd) != 0) rb_syserr_fail(errno, "pipe(2)");
- hostname_resolution_waiter = pipefd[0];
- int waiter_flags = fcntl(hostname_resolution_waiter, F_GETFL, 0);
- if (waiter_flags < 0) rb_syserr_fail(errno, "fcntl(2)");
- if ((fcntl(hostname_resolution_waiter, F_SETFL, waiter_flags | O_NONBLOCK)) < 0) {
- rb_syserr_fail(errno, "fcntl(2)");
- }
- arg->wait = hostname_resolution_waiter;
-
- hostname_resolution_notifier = pipefd[1];
- wait_arg.readfds = &readfds;
-
- arg->getaddrinfo_shared = allocate_fast_fallback_getaddrinfo_shared(arg->family_size);
- if (!arg->getaddrinfo_shared) rb_syserr_fail(errno, "calloc(3)");
-
- rb_nativethread_lock_initialize(&arg->getaddrinfo_shared->lock);
-
- arg->getaddrinfo_shared->notify = hostname_resolution_notifier;
- arg->getaddrinfo_shared->cancelled = false;
- wait_arg.cancelled = &arg->getaddrinfo_shared->cancelled;
-
struct timeval resolution_delay_storage;
struct timeval *resolution_delay_expires_at = NULL;
struct timeval connection_attempt_delay_strage;
@@ -609,20 +626,32 @@ init_fast_fallback_inetsock_internal(VALUE v)
struct timeval *user_specified_resolv_timeout_at = NULL;
struct timeval user_specified_connect_timeout_storage;
struct timeval *user_specified_connect_timeout_at = NULL;
+ struct timeval user_specified_open_timeout_storage;
+ struct timeval *user_specified_open_timeout_at = NULL;
struct timespec now = current_clocktime_ts();
+ VALUE starts_at = current_clocktime();
+
+ if (!NIL_P(open_timeout)) {
+ struct timeval open_timeout_tv = rb_time_interval(open_timeout);
+ user_specified_open_timeout_storage = add_ts_to_tv(open_timeout_tv, now);
+ user_specified_open_timeout_at = &user_specified_open_timeout_storage;
+ }
/* start of hostname resolution */
if (arg->family_size == 1) {
- arg->getaddrinfo_shared->node = NULL;
- arg->getaddrinfo_shared->service = NULL;
+ arg->wait = -1;
+ arg->getaddrinfo_shared = NULL;
int family = arg->families[0];
+ VALUE t = NIL_P(open_timeout) ? resolv_timeout : open_timeout;
+
arg->remote.res = rsock_addrinfo(
arg->remote.host,
arg->remote.serv,
family,
SOCK_STREAM,
- 0
+ 0,
+ t
);
if (family == AF_INET6) {
@@ -634,10 +663,26 @@ init_fast_fallback_inetsock_internal(VALUE v)
resolution_store.v4.finished = true;
resolution_store.v6.finished = true;
}
- resolution_store.is_all_finised = true;
+ resolution_store.is_all_finished = true;
} else {
- arg->getaddrinfo_shared->node = arg->hostp ? strdup(arg->hostp) : NULL;
- arg->getaddrinfo_shared->service = strdup(arg->portp);
+ if (pipe(pipefd) != 0) rb_syserr_fail(errno, "pipe(2)");
+ hostname_resolution_waiter = pipefd[0];
+ int waiter_flags = fcntl(hostname_resolution_waiter, F_GETFL, 0);
+ if (waiter_flags < 0) rb_syserr_fail(errno, "fcntl(2)");
+ if ((fcntl(hostname_resolution_waiter, F_SETFL, waiter_flags | O_NONBLOCK)) < 0) {
+ rb_syserr_fail(errno, "fcntl(2)");
+ }
+ arg->wait = hostname_resolution_waiter;
+ hostname_resolution_notifier = pipefd[1];
+
+ arg->getaddrinfo_shared = allocate_fast_fallback_getaddrinfo_shared(arg->family_size);
+ if (!arg->getaddrinfo_shared) rb_syserr_fail(errno, "calloc(3)");
+
+ rb_nativethread_lock_initialize(&arg->getaddrinfo_shared->lock);
+ arg->getaddrinfo_shared->notify = hostname_resolution_notifier;
+
+ arg->getaddrinfo_shared->node = arg->hostp ? ruby_strdup(arg->hostp) : NULL;
+ arg->getaddrinfo_shared->service = arg->portp ? ruby_strdup(arg->portp) : NULL;
arg->getaddrinfo_shared->refcount = arg->family_size + 1;
for (int i = 0; i < arg->family_size; i++) {
@@ -681,10 +726,9 @@ init_fast_fallback_inetsock_internal(VALUE v)
}
}
- if (raddrinfo_pthread_create(&threads[i], do_fast_fallback_getaddrinfo, arg->getaddrinfo_entries[i]) != 0) {
+ if (raddrinfo_pthread_create(&threads[i], fork_safe_do_fast_fallback_getaddrinfo, arg->getaddrinfo_entries[i]) != 0) {
rsock_raise_resolution_error("getaddrinfo(3)", EAI_AGAIN);
}
- pthread_detach(threads[i]);
}
if (NIL_P(resolv_timeout)) {
@@ -708,7 +752,7 @@ init_fast_fallback_inetsock_internal(VALUE v)
if (remote_ai->ai_family == AF_INET6) {
if (any_addrinfos(&resolution_store)) continue;
if (!in_progress_fds(arg->connection_attempt_fds_size)) break;
- if (resolution_store.is_all_finised) break;
+ if (resolution_store.is_all_finished) break;
if (local_status < 0) {
host = arg->local.host;
@@ -734,7 +778,7 @@ init_fast_fallback_inetsock_internal(VALUE v)
if (!local_ai) {
if (any_addrinfos(&resolution_store)) continue;
if (in_progress_fds(arg->connection_attempt_fds_size)) break;
- if (!resolution_store.is_all_finised) break;
+ if (!resolution_store.is_all_finished) break;
/* Use a different family local address if no choice, this
* will cause EAFNOSUPPORT. */
@@ -751,7 +795,7 @@ init_fast_fallback_inetsock_internal(VALUE v)
if (any_addrinfos(&resolution_store)) continue;
if (in_progress_fds(arg->connection_attempt_fds_size)) break;
- if (!resolution_store.is_all_finised) break;
+ if (!resolution_store.is_all_finished) break;
if (local_status < 0) {
host = arg->local.host;
@@ -787,7 +831,7 @@ init_fast_fallback_inetsock_internal(VALUE v)
if (any_addrinfos(&resolution_store)) continue;
if (in_progress_fds(arg->connection_attempt_fds_size)) break;
- if (!resolution_store.is_all_finised) break;
+ if (!resolution_store.is_all_finished) break;
if (local_status < 0) {
host = arg->local.host;
@@ -808,19 +852,31 @@ init_fast_fallback_inetsock_internal(VALUE v)
if (any_addrinfos(&resolution_store) ||
in_progress_fds(arg->connection_attempt_fds_size) ||
- !resolution_store.is_all_finised) {
+ !resolution_store.is_all_finished) {
socket_nonblock_set(fd);
status = connect(fd, remote_ai->ai_addr, remote_ai->ai_addrlen);
last_family = remote_ai->ai_family;
} else {
- if (!NIL_P(connect_timeout)) {
- user_specified_connect_timeout_storage = rb_time_interval(connect_timeout);
- user_specified_connect_timeout_at = &user_specified_connect_timeout_storage;
+ VALUE timeout = Qnil;
+
+ if (!NIL_P(open_timeout)) {
+ VALUE elapsed = rb_funcall(current_clocktime(), '-', 1, starts_at);
+ timeout = rb_funcall(open_timeout, '-', 1, elapsed);
+
+ if (rb_funcall(timeout, '<', 1, INT2FIX(0)) == Qtrue) {
+ rsock_raise_user_specified_timeout(NULL, arg->remote.host, arg->remote.serv);
+ }
+ }
+ if (NIL_P(timeout)) {
+ if (!NIL_P(connect_timeout)) {
+ user_specified_connect_timeout_storage = rb_time_interval(connect_timeout);
+ user_specified_connect_timeout_at = &user_specified_connect_timeout_storage;
+ }
+ timeout =
+ (user_specified_connect_timeout_at && is_infinity(*user_specified_connect_timeout_at)) ?
+ Qnil : tv_to_seconds(user_specified_connect_timeout_at);
}
- VALUE timeout =
- (user_specified_connect_timeout_at && is_infinity(*user_specified_connect_timeout_at)) ?
- Qnil : tv_to_seconds(user_specified_connect_timeout_at);
io = arg->io = rsock_init_sock(arg->self, fd);
status = rsock_connect(io, remote_ai->ai_addr, remote_ai->ai_addrlen, 0, timeout);
}
@@ -868,7 +924,7 @@ init_fast_fallback_inetsock_internal(VALUE v)
if (any_addrinfos(&resolution_store)) continue;
if (in_progress_fds(arg->connection_attempt_fds_size)) break;
- if (!resolution_store.is_all_finised) break;
+ if (!resolution_store.is_all_finished) break;
if (local_status < 0) {
host = arg->local.host;
@@ -892,46 +948,51 @@ init_fast_fallback_inetsock_internal(VALUE v)
resolution_delay_expires_at,
connection_attempt_delay_expires_at,
user_specified_resolv_timeout_at,
- user_specified_connect_timeout_at
+ user_specified_connect_timeout_at,
+ user_specified_open_timeout_at
);
if (ends_at) {
delay = tv_to_timeout(ends_at, now);
- wait_arg.delay = &delay;
+ delay_p = &delay;
} else {
- wait_arg.delay = NULL;
+ if (((resolution_store.v6.finished && !resolution_store.v4.finished) ||
+ (resolution_store.v4.finished && !resolution_store.v6.finished)) &&
+ !any_addrinfos(&resolution_store) &&
+ !in_progress_fds(arg->connection_attempt_fds_size)) {
+ /* A limited timeout is introduced to prevent select(2) from hanging when it is exclusively
+ * waiting for name resolution and write(2) failure occurs in a child thread. */
+ delay.tv_sec = 0;
+ delay.tv_usec = 50000;
+ delay_p = &delay;
+ } else {
+ delay_p = NULL;
+ }
}
- wait_arg.nfds = 0;
- FD_ZERO(wait_arg.writefds);
+ nfds = 0;
+ rb_fd_zero(&arg->writefds);
if (in_progress_fds(arg->connection_attempt_fds_size)) {
int n = 0;
for (int i = 0; i < arg->connection_attempt_fds_size; i++) {
int cfd = arg->connection_attempt_fds[i];
if (cfd < 0) continue;
if (cfd > n) n = cfd;
- FD_SET(cfd, wait_arg.writefds);
+ rb_fd_set(cfd, &arg->writefds);
}
if (n > 0) n++;
- wait_arg.nfds = n;
+ nfds = n;
}
- FD_ZERO(wait_arg.readfds);
- FD_SET(hostname_resolution_waiter, wait_arg.readfds);
- if ((hostname_resolution_waiter + 1) > wait_arg.nfds) {
- wait_arg.nfds = hostname_resolution_waiter + 1;
- }
+ rb_fd_zero(&arg->readfds);
+ if (arg->family_size > 1) {
+ rb_fd_set(hostname_resolution_waiter, &arg->readfds);
- rb_thread_call_without_gvl2(
- wait_fast_fallback,
- &wait_arg,
- cancel_fast_fallback,
- arg->getaddrinfo_shared
- );
- rb_thread_check_ints();
- if (errno == EINTR || arg->getaddrinfo_shared->cancelled) break;
+ if ((hostname_resolution_waiter + 1) > nfds) {
+ nfds = hostname_resolution_waiter + 1;
+ }
+ }
- status = wait_arg.status;
- syscall = "select(2)";
+ status = rb_thread_fd_select(nfds, &arg->readfds, &arg->writefds, NULL, delay_p);
now = current_clocktime_ts();
if (is_timeout_tv(resolution_delay_expires_at, now)) {
@@ -948,7 +1009,7 @@ init_fast_fallback_inetsock_internal(VALUE v)
if (in_progress_fds(arg->connection_attempt_fds_size)) {
for (int i = 0; i < arg->connection_attempt_fds_size; i++) {
int fd = arg->connection_attempt_fds[i];
- if (fd < 0 || !FD_ISSET(fd, wait_arg.writefds)) continue;
+ if (fd < 0 || !rb_fd_isset(fd, &arg->writefds)) continue;
int err;
socklen_t len = sizeof(err);
@@ -962,7 +1023,7 @@ init_fast_fallback_inetsock_internal(VALUE v)
if (any_addrinfos(&resolution_store)) continue;
if (in_progress_fds(arg->connection_attempt_fds_size)) break;
- if (!resolution_store.is_all_finised) break;
+ if (!resolution_store.is_all_finished) break;
if (local_status < 0) {
host = arg->local.host;
@@ -1001,7 +1062,7 @@ init_fast_fallback_inetsock_internal(VALUE v)
if (connected_fd >= 0) break;
if (!in_progress_fds(arg->connection_attempt_fds_size)) {
- if (!any_addrinfos(&resolution_store) && resolution_store.is_all_finised) {
+ if (!any_addrinfos(&resolution_store) && resolution_store.is_all_finished) {
if (local_status < 0) {
host = arg->local.host;
serv = arg->local.serv;
@@ -1021,7 +1082,7 @@ init_fast_fallback_inetsock_internal(VALUE v)
}
/* check for hostname resolution */
- if (!resolution_store.is_all_finised && FD_ISSET(hostname_resolution_waiter, wait_arg.readfds)) {
+ if (!resolution_store.is_all_finished && rb_fd_isset(hostname_resolution_waiter, &arg->readfds)) {
while (true) {
resolved_type_size = read(
hostname_resolution_waiter,
@@ -1037,15 +1098,17 @@ init_fast_fallback_inetsock_internal(VALUE v)
if (arg->getaddrinfo_entries[IPV6_ENTRY_POS]->err &&
arg->getaddrinfo_entries[IPV6_ENTRY_POS]->err != EAI_ADDRFAMILY) {
- last_error.type = RESOLUTION_ERROR;
- last_error.ecode = arg->getaddrinfo_entries[IPV6_ENTRY_POS]->err;
- syscall = "getaddrinfo(3)";
+ if (!resolution_store.v4.finished || resolution_store.v4.has_error) {
+ last_error.type = RESOLUTION_ERROR;
+ last_error.ecode = arg->getaddrinfo_entries[IPV6_ENTRY_POS]->err;
+ syscall = "getaddrinfo(3)";
+ }
resolution_store.v6.has_error = true;
} else {
resolution_store.v6.ai = arg->getaddrinfo_entries[IPV6_ENTRY_POS]->ai;
}
if (resolution_store.v4.finished) {
- resolution_store.is_all_finised = true;
+ resolution_store.is_all_finished = true;
resolution_delay_expires_at = NULL;
user_specified_resolv_timeout_at = NULL;
break;
@@ -1054,16 +1117,18 @@ init_fast_fallback_inetsock_internal(VALUE v)
resolution_store.v4.finished = true;
if (arg->getaddrinfo_entries[IPV4_ENTRY_POS]->err) {
- last_error.type = RESOLUTION_ERROR;
- last_error.ecode = arg->getaddrinfo_entries[IPV4_ENTRY_POS]->err;
- syscall = "getaddrinfo(3)";
+ if (!resolution_store.v6.finished || resolution_store.v6.has_error) {
+ last_error.type = RESOLUTION_ERROR;
+ last_error.ecode = arg->getaddrinfo_entries[IPV4_ENTRY_POS]->err;
+ syscall = "getaddrinfo(3)";
+ }
resolution_store.v4.has_error = true;
} else {
resolution_store.v4.ai = arg->getaddrinfo_entries[IPV4_ENTRY_POS]->ai;
}
if (resolution_store.v6.finished) {
- resolution_store.is_all_finised = true;
+ resolution_store.is_all_finished = true;
resolution_delay_expires_at = NULL;
user_specified_resolv_timeout_at = NULL;
break;
@@ -1087,24 +1152,27 @@ init_fast_fallback_inetsock_internal(VALUE v)
}
}
- status = wait_arg.status = 0;
+ status = 0;
}
- if (!resolution_store.is_all_finised) {
+ /* For cases where write(2) fails in child threads */
+ if (!resolution_store.is_all_finished) {
if (!resolution_store.v6.finished && arg->getaddrinfo_entries[IPV6_ENTRY_POS]->has_syserr) {
resolution_store.v6.finished = true;
if (arg->getaddrinfo_entries[IPV6_ENTRY_POS]->err) {
- last_error.type = RESOLUTION_ERROR;
- last_error.ecode = arg->getaddrinfo_entries[IPV6_ENTRY_POS]->err;
- syscall = "getaddrinfo(3)";
+ if (!resolution_store.v4.finished || resolution_store.v4.has_error) {
+ last_error.type = RESOLUTION_ERROR;
+ last_error.ecode = arg->getaddrinfo_entries[IPV6_ENTRY_POS]->err;
+ syscall = "getaddrinfo(3)";
+ }
resolution_store.v6.has_error = true;
} else {
resolution_store.v6.ai = arg->getaddrinfo_entries[IPV6_ENTRY_POS]->ai;
}
if (resolution_store.v4.finished) {
- resolution_store.is_all_finised = true;
+ resolution_store.is_all_finished = true;
resolution_delay_expires_at = NULL;
user_specified_resolv_timeout_at = NULL;
}
@@ -1113,16 +1181,18 @@ init_fast_fallback_inetsock_internal(VALUE v)
resolution_store.v4.finished = true;
if (arg->getaddrinfo_entries[IPV4_ENTRY_POS]->err) {
- last_error.type = RESOLUTION_ERROR;
- last_error.ecode = arg->getaddrinfo_entries[IPV4_ENTRY_POS]->err;
- syscall = "getaddrinfo(3)";
+ if (!resolution_store.v6.finished || resolution_store.v6.has_error) {
+ last_error.type = RESOLUTION_ERROR;
+ last_error.ecode = arg->getaddrinfo_entries[IPV4_ENTRY_POS]->err;
+ syscall = "getaddrinfo(3)";
+ }
resolution_store.v4.has_error = true;
} else {
resolution_store.v4.ai = arg->getaddrinfo_entries[IPV4_ENTRY_POS]->ai;
}
if (resolution_store.v6.finished) {
- resolution_store.is_all_finised = true;
+ resolution_store.is_all_finished = true;
resolution_delay_expires_at = NULL;
user_specified_resolv_timeout_at = NULL;
} else {
@@ -1132,9 +1202,13 @@ init_fast_fallback_inetsock_internal(VALUE v)
}
}
+ if (is_timeout_tv(user_specified_open_timeout_at, now)) {
+ rsock_raise_user_specified_timeout(NULL, arg->remote.host, arg->remote.serv);
+ }
+
if (!any_addrinfos(&resolution_store)) {
if (!in_progress_fds(arg->connection_attempt_fds_size) &&
- resolution_store.is_all_finised) {
+ resolution_store.is_all_finished) {
if (local_status < 0) {
host = arg->local.host;
serv = arg->local.serv;
@@ -1150,18 +1224,14 @@ init_fast_fallback_inetsock_internal(VALUE v)
}
if ((is_timeout_tv(user_specified_resolv_timeout_at, now) ||
- resolution_store.is_all_finised) &&
+ resolution_store.is_all_finished) &&
(is_timeout_tv(user_specified_connect_timeout_at, now) ||
!in_progress_fds(arg->connection_attempt_fds_size))) {
- VALUE errno_module = rb_const_get(rb_cObject, rb_intern("Errno"));
- VALUE etimedout_error = rb_const_get(errno_module, rb_intern("ETIMEDOUT"));
- rb_raise(etimedout_error, "user specified timeout");
+ rsock_raise_user_specified_timeout(NULL, arg->remote.host, arg->remote.serv);
}
}
}
- rb_thread_check_ints();
-
if (NIL_P(arg->io)) {
/* create new instance */
arg->io = rsock_init_sock(arg->self, connected_fd);
@@ -1186,33 +1256,38 @@ fast_fallback_inetsock_cleanup(VALUE v)
}
if (arg->wait != -1) close(arg->wait);
- if (getaddrinfo_shared->notify != -1) close(getaddrinfo_shared->notify);
- getaddrinfo_shared->notify = -1;
if (getaddrinfo_shared) {
- if (arg->family_size == 1) {
- free_fast_fallback_getaddrinfo_shared(&getaddrinfo_shared);
- } else {
- int shared_need_free = 0;
- int need_free[2] = { 0, 0 };
-
- rb_nativethread_lock_lock(&getaddrinfo_shared->lock);
- {
- for (int i = 0; i < arg->family_size; i++) {
- if (arg->getaddrinfo_entries[i] && --(arg->getaddrinfo_entries[i]->refcount) == 0) {
- need_free[i] = 1;
- }
- }
- if (--(getaddrinfo_shared->refcount) == 0) {
- shared_need_free = 1;
- }
- }
- rb_nativethread_lock_unlock(&getaddrinfo_shared->lock);
+ if (getaddrinfo_shared->notify != -1) close(getaddrinfo_shared->notify);
+ getaddrinfo_shared->notify = -1;
+
+ int shared_need_free = 0;
+ struct addrinfo *ais[arg->family_size];
+ for (int i = 0; i < arg->family_size; i++) ais[i] = NULL;
+ rb_nativethread_lock_lock(&getaddrinfo_shared->lock);
+ {
for (int i = 0; i < arg->family_size; i++) {
- if (need_free[i]) free_fast_fallback_getaddrinfo_entry(&arg->getaddrinfo_entries[i]);
+ struct fast_fallback_getaddrinfo_entry *getaddrinfo_entry = arg->getaddrinfo_entries[i];
+
+ if (!getaddrinfo_entry) continue;
+
+ if (--(getaddrinfo_entry->refcount) == 0) {
+ ais[i] = getaddrinfo_entry->ai;
+ getaddrinfo_entry->ai = NULL;
+ }
+ }
+ if (--(getaddrinfo_shared->refcount) == 0) {
+ shared_need_free = 1;
}
- if (shared_need_free) free_fast_fallback_getaddrinfo_shared(&getaddrinfo_shared);
+ }
+ rb_nativethread_lock_unlock(&getaddrinfo_shared->lock);
+
+ for (int i = 0; i < arg->family_size; i++) {
+ if (ais[i]) freeaddrinfo(ais[i]);
+ }
+ if (getaddrinfo_shared && shared_need_free) {
+ free_fast_fallback_getaddrinfo_shared(&getaddrinfo_shared);
}
}
@@ -1230,6 +1305,9 @@ fast_fallback_inetsock_cleanup(VALUE v)
}
}
+ if (arg->readfds.fdset) rb_fd_term(&arg->readfds);
+ if (arg->writefds.fdset) rb_fd_term(&arg->writefds);
+
if (arg->connection_attempt_fds) {
free(arg->connection_attempt_fds);
arg->connection_attempt_fds = NULL;
@@ -1239,17 +1317,27 @@ fast_fallback_inetsock_cleanup(VALUE v)
}
VALUE
-rsock_init_inetsock(VALUE self, VALUE remote_host, VALUE remote_serv, VALUE local_host, VALUE local_serv, int type, VALUE resolv_timeout, VALUE connect_timeout, VALUE fast_fallback, VALUE test_mode_settings)
+rsock_init_inetsock(
+ VALUE self, VALUE remote_host, VALUE remote_serv,
+ VALUE local_host, VALUE local_serv, int type,
+ VALUE resolv_timeout, VALUE connect_timeout, VALUE open_timeout,
+ VALUE fast_fallback, VALUE test_mode_settings)
{
+ if (!NIL_P(open_timeout) && (!NIL_P(resolv_timeout) || !NIL_P(connect_timeout))) {
+ rb_raise(rb_eArgError, "Cannot specify open_timeout along with connect_timeout or resolv_timeout");
+ }
+
if (type == INET_CLIENT && FAST_FALLBACK_INIT_INETSOCK_IMPL == 1 && RTEST(fast_fallback)) {
struct rb_addrinfo *local_res = NULL;
- char *hostp, *portp;
- char hbuf[NI_MAXHOST], pbuf[NI_MAXSERV];
+ char *hostp, *portp, *local_portp;
+ char hbuf[NI_MAXHOST], pbuf[NI_MAXSERV], local_pbuf[NI_MAXSERV];
int additional_flags = 0;
- hostp = host_str(remote_host, hbuf, sizeof(hbuf), &additional_flags);
- portp = port_str(remote_serv, pbuf, sizeof(pbuf), &additional_flags);
+ int local_flags = 0;
+ hostp = raddrinfo_host_str(remote_host, hbuf, sizeof(hbuf), &additional_flags);
+ portp = raddrinfo_port_str(remote_serv, pbuf, sizeof(pbuf), &additional_flags);
+ local_portp = raddrinfo_port_str(local_serv, local_pbuf, sizeof(local_pbuf), &local_flags);
- if (!is_specified_ip_address(hostp)) {
+ if (!is_specified_ip_address(hostp) && !is_local_port_fixed(local_portp)) {
int target_families[2] = { 0, 0 };
int resolving_family_size = 0;
@@ -1257,16 +1345,18 @@ rsock_init_inetsock(VALUE self, VALUE remote_host, VALUE remote_serv, VALUE loca
* Maybe also accept a local address
*/
if (!NIL_P(local_host) || !NIL_P(local_serv)) {
+ VALUE t = NIL_P(open_timeout) ? resolv_timeout : open_timeout;
local_res = rsock_addrinfo(
local_host,
local_serv,
AF_UNSPEC,
SOCK_STREAM,
- 0
+ 0,
+ t
);
struct addrinfo *tmp_p = local_res->ai;
- for (tmp_p; tmp_p != NULL; tmp_p = tmp_p->ai_next) {
+ for (; tmp_p != NULL; tmp_p = tmp_p->ai_next) {
if (target_families[0] == 0 && tmp_p->ai_family == AF_INET6) {
target_families[0] = AF_INET6;
resolving_family_size++;
@@ -1296,6 +1386,7 @@ rsock_init_inetsock(VALUE self, VALUE remote_host, VALUE remote_serv, VALUE loca
fast_fallback_arg.type = type;
fast_fallback_arg.resolv_timeout = resolv_timeout;
fast_fallback_arg.connect_timeout = connect_timeout;
+ fast_fallback_arg.open_timeout = open_timeout;
fast_fallback_arg.hostp = hostp;
fast_fallback_arg.portp = portp;
fast_fallback_arg.additional_flags = additional_flags;
@@ -1312,6 +1403,9 @@ rsock_init_inetsock(VALUE self, VALUE remote_host, VALUE remote_serv, VALUE loca
fast_fallback_arg.family_size = resolving_family_size;
fast_fallback_arg.test_mode_settings = test_mode_settings;
+ rb_fd_init(&fast_fallback_arg.readfds);
+ rb_fd_init(&fast_fallback_arg.writefds);
+
return rb_ensure(init_fast_fallback_inetsock_internal, (VALUE)&fast_fallback_arg,
fast_fallback_inetsock_cleanup, (VALUE)&fast_fallback_arg);
}
@@ -1329,6 +1423,7 @@ rsock_init_inetsock(VALUE self, VALUE remote_host, VALUE remote_serv, VALUE loca
arg.type = type;
arg.resolv_timeout = resolv_timeout;
arg.connect_timeout = connect_timeout;
+ arg.open_timeout = open_timeout;
return rb_ensure(init_inetsock_internal, (VALUE)&arg,
inetsock_cleanup, (VALUE)&arg);
@@ -1514,7 +1609,7 @@ static VALUE
ip_s_getaddress(VALUE obj, VALUE host)
{
union_sockaddr addr;
- struct rb_addrinfo *res = rsock_addrinfo(host, Qnil, AF_UNSPEC, SOCK_STREAM, 0);
+ struct rb_addrinfo *res = rsock_addrinfo(host, Qnil, AF_UNSPEC, SOCK_STREAM, 0, Qnil);
socklen_t len = res->ai->ai_addrlen;
/* just take the first one */