mirror of
https://github.com/yrutschle/sslh.git
synced 2026-10-22 18:12:10 +03:00
close file descriptors upon forking a connection shoveler (fix #521)
This commit is contained in:
1 parent
ecc76071c1
commit
e2d01a7440
8 files changed
+59
-9
No files matched your search
@@ -12,6 +12,12 @@ vNEXT:
|
||||
`msg_probe_error` to `msg_probe_info`: beware if you
|
||||
happen to use them...
|
||||
|
||||
Close unused file descriptors upon forking a worker
|
||||
for a connection (in sslh-ev and sslh-select). In
|
||||
particular, this prevents forked processes to
|
||||
inherit the listen sockets and block a new main
|
||||
process from starting.
|
||||
|
||||
v2.3.0:
|
||||
Added `max_connections` setting to `listen` and
|
||||
`protocol` configuration; see the
|
||||
|
||||
+10
-2
@@ -28,6 +28,7 @@
|
||||
/* Info to keep track of all connections */
|
||||
struct cnx_collection {
|
||||
gap_array* fd2cnx; /* Array indexed by file descriptor to things in cnx[] */
|
||||
int max_fd;
|
||||
};
|
||||
|
||||
/* Allocates and initialises a new collection of connections with at least
|
||||
@@ -42,6 +43,7 @@ cnx_collection* collection_init(int len)
|
||||
memset(collection, 0, sizeof(*collection));
|
||||
|
||||
collection->fd2cnx = gap_init(len);
|
||||
collection->max_fd = 0;
|
||||
|
||||
return collection;
|
||||
}
|
||||
@@ -54,10 +56,17 @@ void collection_destroy(cnx_collection* collection)
|
||||
free(collection);
|
||||
}
|
||||
|
||||
int collection_max_fd(cnx_collection* collection)
|
||||
{
|
||||
return collection->max_fd;
|
||||
}
|
||||
|
||||
/* Points the file descriptor to the specified connection index */
|
||||
int collection_add_fd(cnx_collection* collection, struct connection* cnx, int fd)
|
||||
{
|
||||
gap_set(collection->fd2cnx, fd, cnx);
|
||||
if (fd > collection->max_fd)
|
||||
collection->max_fd = fd;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -74,8 +83,7 @@ struct connection* collection_alloc_cnx_from_fd(struct cnx_collection* collectio
|
||||
cnx->state = ST_PROBING;
|
||||
cnx->probe_timeout = time(NULL) + cfg.timeout;
|
||||
|
||||
gap_set(collection->fd2cnx, fd, cnx);
|
||||
|
||||
collection_add_fd(collection, cnx, fd);
|
||||
return cnx;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ void collection_destroy(cnx_collection* collection);
|
||||
|
||||
struct connection* collection_alloc_cnx_from_fd(cnx_collection* collection, int fd);
|
||||
int collection_add_fd(cnx_collection* collection, struct connection* cnx, int fd);
|
||||
int collection_max_fd(cnx_collection* collection);
|
||||
|
||||
/* Remove a connection from the collection */
|
||||
int collection_remove_cnx(cnx_collection* collection, struct connection *cnx);
|
||||
|
||||
+2
-1
@@ -196,7 +196,7 @@ static int next_power_of_two(int n)
|
||||
return n + 1;
|
||||
}
|
||||
|
||||
void loop_init(struct loop_info* loop, int num_addr_listen)
|
||||
void loop_init(struct loop_info* loop, struct listen_endpoint* listen_sockets, int num_addr_listen)
|
||||
{
|
||||
memset(loop, 0, sizeof(*loop));
|
||||
loop->collection = collection_init(0);
|
||||
@@ -206,6 +206,7 @@ void loop_init(struct loop_info* loop, int num_addr_listen)
|
||||
udp_init(loop);
|
||||
tcp_init();
|
||||
|
||||
loop->listen_sockets = listen_sockets;
|
||||
loop->num_addr_listen = num_addr_listen;
|
||||
|
||||
int max_forks = next_power_of_two(max_forking_connections());
|
||||
|
||||
+5
-1
@@ -23,6 +23,8 @@ struct loop_info {
|
||||
hash* pid2proto; /* to follow which forked PID is processing what protocol for connection count */
|
||||
|
||||
watchers* watchers;
|
||||
|
||||
struct listen_endpoint* listen_sockets;
|
||||
int num_addr_listen; /* How many listen endpoints do we have here */
|
||||
|
||||
cnx_collection* collection; /* Collection of connections linked to this loop */
|
||||
@@ -32,7 +34,7 @@ void cnx_read_process(struct loop_info* fd_info, int fd);
|
||||
struct connection* cnx_accept_process(struct loop_info* fd_info, struct listen_endpoint* listen_socket);
|
||||
|
||||
int tidy_connection(struct connection *cnx, struct loop_info* fd_info);
|
||||
void loop_init(struct loop_info* loop, int num_addr_listen);
|
||||
void loop_init(struct loop_info* loop, struct listen_endpoint* listen_sockets, int num_addr_listen);
|
||||
|
||||
void remember_child_data(struct loop_info* fd_info,
|
||||
struct connection* cnx, pid_t pid);
|
||||
@@ -47,4 +49,6 @@ void watchers_del_write(watchers* w, int fd);
|
||||
|
||||
void watcher_sigchld(struct loop_info* fd_info, struct connection* cnx, pid_t pid);
|
||||
|
||||
void close_listen_endpoints(struct loop_info* loop);
|
||||
|
||||
#endif
|
||||
@@ -207,13 +207,12 @@ void watcher_sigchld(struct loop_info* fd_info, struct connection* cnx, pid_t pi
|
||||
ev_child_start(EV_DEFAULT_ cw);
|
||||
}
|
||||
|
||||
|
||||
void main_loop(struct listen_endpoint listen_sockets[], int num_addr_listen)
|
||||
{
|
||||
struct loop_info ev_info;
|
||||
loop = EV_DEFAULT;
|
||||
|
||||
loop_init(&ev_info, num_addr_listen);
|
||||
loop_init(&ev_info, listen_sockets, num_addr_listen);
|
||||
|
||||
watchers_init(&ev_info.watchers, listen_sockets, num_addr_listen);
|
||||
ev_set_userdata(EV_A_ &ev_info);
|
||||
|
||||
+1
-1
@@ -204,7 +204,7 @@ void main_loop(struct listen_endpoint listen_sockets[], int num_addr_listen)
|
||||
int i, res;
|
||||
|
||||
setup_sigalrm();
|
||||
loop_init(&fd_info, num_addr_listen);
|
||||
loop_init(&fd_info, listen_sockets, num_addr_listen);
|
||||
|
||||
watchers_init(&fd_info.watchers, listen_sockets, num_addr_listen);
|
||||
|
||||
|
||||
+33
-2
@@ -296,6 +296,37 @@ static void connect_proxy(struct connection *cnx)
|
||||
}
|
||||
|
||||
|
||||
/* This ensures the kernel will close the listening socket when the
|
||||
* parent process closes; otherwise, the listening socket is kept
|
||||
* around by the inactive file descriptor in the child */
|
||||
void close_listen_endpoints(struct loop_info* loop)
|
||||
{
|
||||
struct listen_endpoint* listen_sockets = loop->listen_sockets;
|
||||
watchers* w = loop->watchers;
|
||||
|
||||
for (int i = 0; i < loop->num_addr_listen; i++) {
|
||||
int fd = listen_sockets[i].socketfd;
|
||||
watchers_del_read(w, fd);
|
||||
watchers_del_write(w, fd);
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Close all connections except specified.
|
||||
* This saves file descriptors and allocated memory when forking */
|
||||
void tidy_other_connections(struct loop_info* fd_info, struct connection* keep_cnx)
|
||||
{
|
||||
struct connection* cnx;
|
||||
cnx_collection* cnx_coll = fd_info->collection;
|
||||
|
||||
for (int i = 0; i < collection_max_fd(cnx_coll); i++) {
|
||||
cnx = collection_get_cnx_from_fd(cnx_coll, i);
|
||||
if (cnx && (cnx != keep_cnx))
|
||||
tidy_connection(cnx, fd_info);
|
||||
}
|
||||
}
|
||||
|
||||
/* Forks a shoveler process for one protocol */
|
||||
void fork_shoveling_process(struct loop_info* fd_info, struct connection* cnx) {
|
||||
pid_t pid;
|
||||
@@ -307,8 +338,8 @@ void fork_shoveling_process(struct loop_info* fd_info, struct connection* cnx) {
|
||||
|
||||
switch (pid = fork()) {
|
||||
case 0: /* child */
|
||||
/* TODO: close all file descriptors except 2 */
|
||||
/* free(cnx); */
|
||||
close_listen_endpoints(fd_info);
|
||||
tidy_other_connections(fd_info, cnx);
|
||||
connect_proxy(cnx);
|
||||
exit(0);
|
||||
case -1: print_message(msg_system_error, "fork failed: err %d: %s\n", errno, strerror(errno));
|
||||
|
||||
Reference in new issue
Block a user