mirror of
https://github.com/yrutschle/sslh.git
synced 2025-04-13 07:37:15 +03:00
sslh-select sets O_NONBLOCK *before* calling connect, which prevents hanging on an unresposive server (fix #258)
This commit is contained in:
parent
300e1916c3
commit
1ad450a444
51
common.c
51
common.c
@ -286,11 +286,28 @@ int bind_peer(int fd, int fd_from)
|
|||||||
return 0;
|
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
|
/* Connect to first address that works and returns a file descriptor, or -1 if
|
||||||
* none work.
|
* none work.
|
||||||
* If transparent proxying is on, use fd_from peer address on external address
|
* If transparent proxying is on, use fd_from peer address on external address
|
||||||
* of new file descriptor. */
|
* 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 addrinfo *a, from;
|
||||||
struct sockaddr_storage ss;
|
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));
|
setsockopt(fd, IPPROTO_TCP, TCP_FASTOPEN_CONNECT, &one, sizeof(one));
|
||||||
/* no need to check return value; if it's not supported, that's okay */
|
/* no need to check return value; if it's not supported, that's okay */
|
||||||
|
|
||||||
|
if (blocking == NON_BLOCKING) {
|
||||||
|
set_nonblock(fd);
|
||||||
|
}
|
||||||
|
|
||||||
if (transparent) {
|
if (transparent) {
|
||||||
res = bind_peer(fd, fd_from);
|
res = bind_peer(fd, fd_from);
|
||||||
CHECK_RES_RETURN(res, "bind_peer", res);
|
CHECK_RES_RETURN(res, "bind_peer", res);
|
||||||
}
|
}
|
||||||
res = connect(fd, a->ai_addr, a->ai_addrlen);
|
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:
|
/* EINPROGRESS indicates it might take time. If it eventually
|
||||||
log_message(LOG_ERR, "forward to %s failed:connect: %s\n",
|
* fails, it'll be caught as a failed read */
|
||||||
cnx->proto->name, strerror(errno));
|
if ((res == -1) && (errno != EINPROGRESS)) {
|
||||||
close(fd);
|
log_message(LOG_ERR, "forward to %s failed:connect: %s\n",
|
||||||
}
|
cnx->proto->name, strerror(errno));
|
||||||
} else {
|
close(fd);
|
||||||
if (cnx->proto->keepalive) {
|
continue; /* Try the next address */
|
||||||
res = setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char*)&one, sizeof(one));
|
|
||||||
CHECK_RES_RETURN(res, "setsockopt(SO_KEEPALIVE)", res);
|
|
||||||
}
|
|
||||||
return fd;
|
|
||||||
}
|
}
|
||||||
|
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;
|
return -1;
|
||||||
|
8
common.h
8
common.h
@ -122,10 +122,16 @@ struct connection_desc {
|
|||||||
local[MAX_NAMELENGTH], target[MAX_NAMELENGTH];
|
local[MAX_NAMELENGTH], target[MAX_NAMELENGTH];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
NON_BLOCKING = 0,
|
||||||
|
BLOCKING = 1
|
||||||
|
} connect_blocking;
|
||||||
|
|
||||||
|
|
||||||
/* common.c */
|
/* common.c */
|
||||||
void init_cnx(struct connection *cnx);
|
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);
|
int fd2fd(struct queue *target, struct queue *from);
|
||||||
char* sprintaddr(char* buf, size_t size, struct addrinfo *a);
|
char* sprintaddr(char* buf, size_t size, struct addrinfo *a);
|
||||||
void resolve_name(struct addrinfo **out, char* fullname);
|
void resolve_name(struct addrinfo **out, char* fullname);
|
||||||
|
@ -111,7 +111,7 @@ void start_shoveler(int in_socket)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Connect the target 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");
|
CHECK_RES_DIE(out_socket, "connect");
|
||||||
|
|
||||||
set_capabilities(0);
|
set_capabilities(0);
|
||||||
|
@ -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)
|
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];
|
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)) {
|
if ((q->fd != -1) && fd_is_in_range(q->fd)) {
|
||||||
log_connection(NULL, cnx);
|
log_connection(NULL, cnx);
|
||||||
set_nonblock(q->fd);
|
|
||||||
flush_deferred(q);
|
flush_deferred(q);
|
||||||
if (q->deferred_data) {
|
if (q->deferred_data) {
|
||||||
FD_SET(q->fd, &fd_info->fds_w);
|
FD_SET(q->fd, &fd_info->fds_w);
|
||||||
@ -257,7 +241,7 @@ static void connect_proxy(struct connection *cnx)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Connect the target 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");
|
CHECK_RES_DIE(out_socket, "connect");
|
||||||
|
|
||||||
cnx->q[1].fd = out_socket;
|
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:
|
/* Main loop: the idea is as follow:
|
||||||
* - fds_r and fds_w contain the file descriptors to monitor in read and write
|
* - 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
|
* - When a file descriptor goes off, process it: read from it, write the data
|
||||||
|
Loading…
x
Reference in New Issue
Block a user