diff --git a/common.c b/common.c index e0999f3d..909729ce 100644 --- a/common.c +++ b/common.c @@ -286,11 +286,28 @@ int bind_peer(int fd, int fd_from) return 0; } +/* Make the file descriptor non-block */ +int set_nonblock(int fd) +{ + int flags; + + flags = fcntl(fd, F_GETFL); + CHECK_RES_RETURN(flags, "fcntl", -1); + + flags |= O_NONBLOCK; + + flags = fcntl(fd, F_SETFL, flags); + CHECK_RES_RETURN(flags, "fcntl", -1); + + return flags; +} + + /* Connect to first address that works and returns a file descriptor, or -1 if * none work. * If transparent proxying is on, use fd_from peer address on external address * of new file descriptor. */ -int connect_addr(struct connection *cnx, int fd_from) +int connect_addr(struct connection *cnx, int fd_from, connect_blocking blocking) { struct addrinfo *a, from; struct sockaddr_storage ss; @@ -324,29 +341,29 @@ int connect_addr(struct connection *cnx, int fd_from) setsockopt(fd, IPPROTO_TCP, TCP_FASTOPEN_CONNECT, &one, sizeof(one)); /* no need to check return value; if it's not supported, that's okay */ + if (blocking == NON_BLOCKING) { + set_nonblock(fd); + } + if (transparent) { res = bind_peer(fd, fd_from); CHECK_RES_RETURN(res, "bind_peer", res); } res = connect(fd, a->ai_addr, a->ai_addrlen); - if (res == -1) { - switch (errno) { - case EINPROGRESS: - /* Can't be done yet, or TFO already done */ - break; - default: - log_message(LOG_ERR, "forward to %s failed:connect: %s\n", - cnx->proto->name, strerror(errno)); - close(fd); - } - } else { - if (cnx->proto->keepalive) { - res = setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char*)&one, sizeof(one)); - CHECK_RES_RETURN(res, "setsockopt(SO_KEEPALIVE)", res); - } - return fd; + /* EINPROGRESS indicates it might take time. If it eventually + * fails, it'll be caught as a failed read */ + if ((res == -1) && (errno != EINPROGRESS)) { + log_message(LOG_ERR, "forward to %s failed:connect: %s\n", + cnx->proto->name, strerror(errno)); + close(fd); + continue; /* Try the next address */ + } + if (cnx->proto->keepalive) { + res = setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char*)&one, sizeof(one)); + CHECK_RES_RETURN(res, "setsockopt(SO_KEEPALIVE)", res); } + return fd; } } return -1; diff --git a/common.h b/common.h index 54199cd4..02611dfb 100644 --- a/common.h +++ b/common.h @@ -122,10 +122,16 @@ struct connection_desc { local[MAX_NAMELENGTH], target[MAX_NAMELENGTH]; }; +typedef enum { + NON_BLOCKING = 0, + BLOCKING = 1 +} connect_blocking; + /* common.c */ void init_cnx(struct connection *cnx); -int connect_addr(struct connection *cnx, int fd_from); +int set_nonblock(int fd); +int connect_addr(struct connection *cnx, int fd_from, connect_blocking blocking); int fd2fd(struct queue *target, struct queue *from); char* sprintaddr(char* buf, size_t size, struct addrinfo *a); void resolve_name(struct addrinfo **out, char* fullname); diff --git a/sslh-fork.c b/sslh-fork.c index 0ac678bc..bbf2fc1a 100644 --- a/sslh-fork.c +++ b/sslh-fork.c @@ -111,7 +111,7 @@ void start_shoveler(int in_socket) } /* Connect the target socket */ - out_socket = connect_addr(&cnx, in_socket); + out_socket = connect_addr(&cnx, in_socket, BLOCKING); CHECK_RES_DIE(out_socket, "connect"); set_capabilities(0); diff --git a/sslh-select.c b/sslh-select.c index f5830d32..acd318a8 100644 --- a/sslh-select.c +++ b/sslh-select.c @@ -53,21 +53,6 @@ struct select_info { }; -/* Make the file descriptor non-block */ -static int set_nonblock(int fd) -{ - int flags; - - flags = fcntl(fd, F_GETFL); - CHECK_RES_RETURN(flags, "fcntl", -1); - - flags |= O_NONBLOCK; - - flags = fcntl(fd, F_SETFL, flags); - CHECK_RES_RETURN(flags, "fcntl", -1); - - return flags; -} static int tidy_connection(struct connection *cnx, struct select_info* fd_info) { @@ -141,10 +126,9 @@ static int connect_queue(struct connection* cnx, { struct queue *q = &cnx->q[1]; - q->fd = connect_addr(cnx, cnx->q[0].fd); + q->fd = connect_addr(cnx, cnx->q[0].fd, NON_BLOCKING); if ((q->fd != -1) && fd_is_in_range(q->fd)) { log_connection(NULL, cnx); - set_nonblock(q->fd); flush_deferred(q); if (q->deferred_data) { FD_SET(q->fd, &fd_info->fds_w); @@ -257,7 +241,7 @@ static void connect_proxy(struct connection *cnx) } /* Connect the target socket */ - out_socket = connect_addr(cnx, in_socket); + out_socket = connect_addr(cnx, in_socket, BLOCKING); CHECK_RES_DIE(out_socket, "connect"); cnx->q[1].fd = out_socket; @@ -430,6 +414,7 @@ void cnx_accept_process(struct select_info* fd_info, int fd) } } + /* Main loop: the idea is as follow: * - fds_r and fds_w contain the file descriptors to monitor in read and write * - When a file descriptor goes off, process it: read from it, write the data