Skip to content

Commit

Permalink
sslh-select sets O_NONBLOCK *before* calling connect, which prevents …
Browse files Browse the repository at this point in the history
…hanging on an unresposive server (fix #258)
  • Loading branch information
yrutschle committed May 28, 2021
1 parent 300e191 commit 1ad450a
Show file tree
Hide file tree
Showing 4 changed files with 45 additions and 37 deletions.
51 changes: 34 additions & 17 deletions common.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
8 changes: 7 additions & 1 deletion common.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion sslh-fork.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
21 changes: 3 additions & 18 deletions sslh-select.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit 1ad450a

Please sign in to comment.