Merge pull request #143 from astiob/select-fork

sslh-select: support forking for particular protocols
This commit is contained in:
yrutschle 2018-01-02 22:26:58 +01:00 committed by GitHub
commit d26eab728c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 121 additions and 12 deletions

20
probe.c
View File

@ -45,16 +45,16 @@ static int is_true(const char *p, int len, struct proto* proto) { return 1; }
/* Table of protocols that have a built-in probe
*/
static struct proto builtins[] = {
/* description service saddr log_level keepalive probe */
{ "ssh", "sshd", NULL, 1, 0, is_ssh_protocol},
{ "openvpn", NULL, NULL, 1, 0, is_openvpn_protocol },
{ "tinc", NULL, NULL, 1, 0, is_tinc_protocol },
{ "xmpp", NULL, NULL, 1, 0, is_xmpp_protocol },
{ "http", NULL, NULL, 1, 0, is_http_protocol },
{ "ssl", NULL, NULL, 1, 0, is_tls_protocol },
{ "tls", NULL, NULL, 1, 0, is_tls_protocol },
{ "adb", NULL, NULL, 1, 0, is_adb_protocol },
{ "anyprot", NULL, NULL, 1, 0, is_true }
/* description service saddr log_level keepalive fork probe */
{ "ssh", "sshd", NULL, 1, 0, 1, is_ssh_protocol},
{ "openvpn", NULL, NULL, 1, 0, 1, is_openvpn_protocol },
{ "tinc", NULL, NULL, 1, 0, 1, is_tinc_protocol },
{ "xmpp", NULL, NULL, 1, 0, 0, is_xmpp_protocol },
{ "http", NULL, NULL, 1, 0, 0, is_http_protocol },
{ "ssl", NULL, NULL, 1, 0, 0, is_tls_protocol },
{ "tls", NULL, NULL, 1, 0, 0, is_tls_protocol },
{ "adb", NULL, NULL, 1, 0, 0, is_adb_protocol },
{ "anyprot", NULL, NULL, 1, 0, 0, is_true }
};
static struct proto *protocols;

View File

@ -24,6 +24,7 @@ struct proto {
* 1: Log incoming connection
*/
int keepalive; /* 0: No keepalive ; 1: Set Keepalive for this connection */
int fork; /* 0: Connection can run within shared process ; 1: Separate process required for this connection */
/* function to probe that protocol; parameters are buffer and length
* containing the data to probe, and a pointer to the protocol structure */

View File

@ -123,14 +123,15 @@ static void printsettings(void)
for (p = get_first_protocol(); p; p = p->next) {
fprintf(stderr,
"%s addr: %s. libwrap service: %s log_level: %d family %d %d [%s]\n",
"%s addr: %s. libwrap service: %s log_level: %d family %d %d [%s] [%s]\n",
p->description,
sprintaddr(buf, sizeof(buf), p->saddr),
p->service,
p->log_level,
p->saddr->ai_family,
p->saddr->ai_addr->sa_family,
p->keepalive ? "keepalive" : "");
p->keepalive ? "keepalive" : "",
p->fork ? "fork" : "");
}
fprintf(stderr, "listening on:\n");
for (a = addr_listen; a; a = a->ai_next) {
@ -307,6 +308,7 @@ static int config_protocols(config_t *config, struct proto **prots)
p->description = name;
config_setting_lookup_string(prot, "service", &(p->service));
config_setting_lookup_bool(prot, "keepalive", &p->keepalive);
config_setting_lookup_bool(prot, "fork", &p->fork);
if (config_setting_lookup_int(prot, "log_level", &p->log_level) == CONFIG_FALSE) {
p->log_level = 1;

View File

@ -27,6 +27,8 @@
const char* server_type = "sslh-select";
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
/* cnx_num_alloc is the number of connection to allocate at once (at start-up,
* and then every time we get too many simultaneous connections: e.g. start
* with 100 slots, then if we get more than 100 connections allocate another
@ -186,6 +188,93 @@ void shovel(struct connection *cnx, int active_fd,
}
}
/* shovels data from one fd to the other and vice-versa
returns after one socket closed
*/
void shovel_single(struct connection *cnx)
{
fd_set fds_r, fds_w;
int res, i;
int max_fd = MAX(cnx->q[0].fd, cnx->q[1].fd) + 1;
FD_ZERO(&fds_r);
FD_ZERO(&fds_w);
while (1) {
for (i = 0; i < 2; i++) {
if (cnx->q[i].deferred_data_size) {
FD_SET(cnx->q[i].fd, &fds_w);
FD_CLR(cnx->q[1-i].fd, &fds_r);
} else {
FD_CLR(cnx->q[i].fd, &fds_w);
FD_SET(cnx->q[1-i].fd, &fds_r);
}
}
res = select(
max_fd,
&fds_r,
&fds_w,
NULL,
NULL
);
CHECK_RES_DIE(res, "select");
for (i = 0; i < 2; i++) {
if (FD_ISSET(cnx->q[i].fd, &fds_w)) {
res = flush_deferred(&cnx->q[i]);
if ((res == -1) && ((errno == EPIPE) || (errno == ECONNRESET))) {
if (verbose)
fprintf(stderr, "%s socket closed\n", i ? "server" : "client");
return;
}
}
if (FD_ISSET(cnx->q[i].fd, &fds_r)) {
res = fd2fd(&cnx->q[1-i], &cnx->q[i]);
if (!res) {
if (verbose)
fprintf(stderr, "socket closed\n");
return;
}
}
}
}
}
/* Child process that makes internal connection and proxies
*/
void connect_proxy(struct connection *cnx)
{
int in_socket;
int out_socket;
/* Minimize the file descriptor value to help select() */
in_socket = dup(cnx->q[0].fd);
if (in_socket == -1) {
in_socket = cnx->q[0].fd;
} else {
close(cnx->q[0].fd);
cnx->q[0].fd = in_socket;
}
/* Connect the target socket */
out_socket = connect_addr(cnx, in_socket);
CHECK_RES_DIE(out_socket, "connect");
cnx->q[1].fd = out_socket;
log_connection(cnx);
shovel_single(cnx);
close(in_socket);
close(out_socket);
if (verbose)
fprintf(stderr, "connection closed down\n");
exit(0);
}
/* returns true if specified fd is initialised and present in fd_set */
int is_fd_active(int fd, fd_set* set)
{
@ -326,6 +415,23 @@ void main_loop(int listen_sockets[], int num_addr_listen)
check_access_rights(in_socket, cnx[i].proto->service)) {
tidy_connection(&cnx[i], &fds_r, &fds_w);
res = -1;
} else if (cnx[i].proto->fork) {
if (!fork()) {
struct connection *pcnx_i = &cnx[i];
struct connection cnx_i = *pcnx_i;
for (i = 0; i < num_addr_listen; i++)
close(listen_sockets[i]);
for (i = 0; i < num_cnx; i++)
if (&cnx[i] != pcnx_i)
for (j = 0; j < 2; j++)
if (cnx[i].q[j].fd != -1)
close(cnx[i].q[j].fd);
free(cnx);
connect_proxy(&cnx_i);
exit(0);
}
tidy_connection(&cnx[i], &fds_r, &fds_w);
res = -1;
} else {
res = connect_queue(&cnx[i], &fds_r, &fds_w);
}