From dbafd6510db30d8f5e43098132dc923497e92cab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Kuzn=C3=ADk?= Date: Mon, 23 Sep 2013 23:30:34 +0100 Subject: [PATCH] Allow probes to say they cannot decide yet --- common.c | 2 ++ common.h | 2 +- probe.c | 18 +++++++++++------- probe.h | 2 +- sslh-fork.c | 36 ++++++++++++++++++++---------------- sslh-select.c | 20 +++++++++++--------- 6 files changed, 46 insertions(+), 34 deletions(-) diff --git a/common.c b/common.c index 9f7edaa..e507d1b 100644 --- a/common.c +++ b/common.c @@ -8,6 +8,7 @@ #include #include "common.h" +#include "probe.h" /* Added to make the code compilable under CYGWIN * */ @@ -215,6 +216,7 @@ void init_cnx(struct connection *cnx) memset(cnx, 0, sizeof(*cnx)); cnx->q[0].fd = -1; cnx->q[1].fd = -1; + cnx->proto = get_first_protocol(); } void dump_connection(struct connection *cnx) diff --git a/common.h b/common.h index 7d49b0f..c77e51a 100644 --- a/common.h +++ b/common.h @@ -69,6 +69,7 @@ struct queue { struct connection { enum connection_state state; time_t probe_timeout; + struct proto *proto; /* q[0]: queue for external connection (client); * q[1]: queue for internal connection (httpd or sshd); @@ -87,7 +88,6 @@ int connect_addr(struct addrinfo *addr, int fd_from, const char* cnx_name); 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); -struct proto* probe_client_protocol(struct connection *cnx); void log_connection(struct connection *cnx); int check_access_rights(int in_socket, const char* service); void setup_signals(void); diff --git a/probe.c b/probe.c index 39b5f8c..9790c38 100644 --- a/probe.c +++ b/probe.c @@ -238,7 +238,7 @@ static int regex_probe(const char *p, int len, struct proto *proto) * write buffer of the connection and returns a pointer to the protocol * structure */ -struct proto* probe_client_protocol(struct connection *cnx) +int probe_client_protocol(struct connection *cnx) { char buffer[BUFSIZ]; struct proto *p; @@ -251,16 +251,19 @@ struct proto* probe_client_protocol(struct connection *cnx) * function does not have to deal with a specific failure condition (the * connection will just fail later normally). */ if (n > 0) { + int res = PROBE_NEXT; + defer_write(&cnx->q[1], buffer, n); - for (p = protocols; p; p = p->next) { + for (p = cnx->proto; p && res == PROBE_NEXT; p = p->next) { if (! p->probe) continue; if (verbose) fprintf(stderr, "probing for %s\n", p->description); - if (p->probe(buffer, n, p)) { - if (verbose) fprintf(stderr, "probe %s successful\n", p->description); - return p; - } + + cnx->proto = p; + res = p->probe(cnx->q[1].defered_data, cnx->q[1].defered_data_size, p); } + if (res != PROBE_NEXT) + return res; } if (verbose) @@ -270,7 +273,8 @@ struct proto* probe_client_protocol(struct connection *cnx) /* If none worked, return the first one affected (that's completely * arbitrary) */ - return protocols; + cnx->proto = protocols; + return PROBE_MATCH; } /* Returns the structure for specified protocol or NULL if not found */ diff --git a/probe.h b/probe.h index 55ba322..edc3aac 100644 --- a/probe.h +++ b/probe.h @@ -49,7 +49,7 @@ void set_protocol_list(struct proto*); * write buffer of the connection and returns a pointer to the protocol * structure */ -struct proto* probe_client_protocol(struct connection *cnx); +int probe_client_protocol(struct connection *cnx); /* set the protocol to connect to in case of timeout */ void set_ontimeout(const char* name); diff --git a/sslh-fork.c b/sslh-fork.c index c3afc84..05c0c42 100644 --- a/sslh-fork.c +++ b/sslh-fork.c @@ -70,39 +70,43 @@ void start_shoveler(int in_socket) fd_set fds; struct timeval tv; struct addrinfo *saddr; - int res; + int res = PROBE_AGAIN; int out_socket; struct connection cnx; - struct proto *prot; init_cnx(&cnx); + cnx.q[0].fd = in_socket; FD_ZERO(&fds); FD_SET(in_socket, &fds); memset(&tv, 0, sizeof(tv)); tv.tv_sec = probing_timeout; - res = select(in_socket + 1, &fds, NULL, NULL, &tv); - if (res == -1) - perror("select"); - cnx.q[0].fd = in_socket; + while (res == PROBE_AGAIN) { + /* POSIX does not guarantee that tv will be updated, but the client can + * only postpone the inevitable for so long */ + res = select(in_socket + 1, &fds, NULL, NULL, &tv); + if (res == -1) + perror("select"); - if (FD_ISSET(in_socket, &fds)) { - /* Received data: figure out what protocol it is */ - prot = probe_client_protocol(&cnx); - } else { - /* Timed out: it's necessarily SSH */ - prot = timeout_protocol(); + if (FD_ISSET(in_socket, &fds)) { + /* Received data: figure out what protocol it is */ + res = probe_client_protocol(&cnx); + } else { + /* Timed out: it's necessarily SSH */ + cnx.proto = timeout_protocol(); + break; + } } - saddr = prot->saddr; - if (prot->service && - check_access_rights(in_socket, prot->service)) { + saddr = cnx.proto->saddr; + if (cnx.proto->service && + check_access_rights(in_socket, cnx.proto->service)) { exit(0); } /* Connect the target socket */ - out_socket = connect_addr(saddr, in_socket, prot->description); + out_socket = connect_addr(saddr, in_socket, cnx.proto->description); CHECK_RES_DIE(out_socket, "connect"); cnx.q[1].fd = out_socket; diff --git a/sslh-select.c b/sslh-select.c index 14b3716..02f7cad 100644 --- a/sslh-select.c +++ b/sslh-select.c @@ -210,7 +210,6 @@ void main_loop(int listen_sockets[], int num_addr_listen) struct timeval tv; int max_fd, in_socket, i, j, res; struct connection *cnx; - struct proto *prot; int num_cnx; /* Number of connections in *cnx */ int num_probing = 0; /* Number of connections currently probing * We use this to know if we need to time out of @@ -303,26 +302,29 @@ void main_loop(int listen_sockets[], int num_addr_listen) dump_connection(&cnx[i]); exit(1); } - num_probing--; - cnx[i].state = ST_SHOVELING; /* If timed out it's SSH, otherwise the client sent * data so probe the protocol */ if ((cnx[i].probe_timeout < time(NULL))) { - prot = timeout_protocol(); + cnx[i].proto = timeout_protocol(); } else { - prot = probe_client_protocol(&cnx[i]); + res = probe_client_protocol(&cnx[i]); + if (res == PROBE_AGAIN) + continue; } + num_probing--; + cnx[i].state = ST_SHOVELING; + /* libwrap check if required for this protocol */ - if (prot->service && - check_access_rights(in_socket, prot->service)) { + if (cnx[i].proto->service && + check_access_rights(in_socket, cnx[i].proto->service)) { tidy_connection(&cnx[i], &fds_r, &fds_w); res = -1; } else { res = connect_queue(&cnx[i], - prot->saddr, - prot->description, + cnx[i].proto->saddr, + cnx[i].proto->description, &fds_r, &fds_w); }