sslh-select sets O_NONBLOCK *before* calling connect, which prevents hanging on an unresposive server (fix #258)

This commit is contained in:
yrutschle 2021-05-28 13:38:45 +02:00
parent 300e1916c3
commit 1ad450a444
4 changed files with 45 additions and 37 deletions

View File

@ -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;

View File

@ -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);

View File

@ -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);

View File

@ -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