diff --git a/sniproxy.conf b/sniproxy.conf index ac3cfa21..c1a8882a 100644 --- a/sniproxy.conf +++ b/sniproxy.conf @@ -8,6 +8,11 @@ group nogroup # PID file, needs to be placed in directory writable by user pidfile /var/run/sniproxy.pid +# This will return a list of connections that aren't in the access_log yet. +# It doesn't matter what request you send, it will always repond with a http ok +# header followed by the list +statuslistener 8080 + # The DNS resolver is required for tables configured using wildcard or hostname # targets. If no resolver is specified, the nameserver and search domain are # loaded from /etc/resolv.conf. diff --git a/src/config.c b/src/config.c index 4ff23127..e003c9a1 100644 --- a/src/config.c +++ b/src/config.c @@ -197,6 +197,12 @@ static struct Keyword global_grammar[] = { .block_grammar=table_stanza_grammar, .finalize=(int(*)(void *, void *))end_table_stanza, }, + { + .keyword="statuslistener", + .create=(void *(*)())new_statuslistener, + .parse_arg=(int(*)(void *, const char *))accept_listener_arg, + .finalize=(int(*)(void *, void *))end_listener_stanza, + }, { .keyword = NULL, }, @@ -378,8 +384,6 @@ accept_pidfile(struct Config *config, const char *pidfile) { static int end_listener_stanza(struct Config *config, struct Listener *listener) { - listener->accept_cb = &accept_connection; - if (valid_listener(listener) <= 0) { err("Invalid listener"); print_listener_config(stderr, listener); diff --git a/src/connection.c b/src/connection.c index c10a1090..e63dace6 100644 --- a/src/connection.c +++ b/src/connection.c @@ -84,7 +84,7 @@ static struct Connection *new_connection(struct ev_loop *); static void log_connection(struct Connection *); static void log_bad_request(struct Connection *, const char *, size_t, int); static void free_connection(struct Connection *); -static void print_connection(FILE *, const struct Connection *); +static void print_connection(int, const struct Connection *); static void free_resolv_cb_data(struct resolv_cb_data *); @@ -161,6 +161,42 @@ accept_connection(struct Listener *listener, struct ev_loop *loop) { return 1; } +/** + * Accept a new incoming connection that will report the status + * + * Returns 1 on success or 0 on error; + */ +int +accept_statusconnection(struct Listener *listener, struct ev_loop * loop) { + (void)loop; + struct sockaddr_storage addr; + socklen_t addr_len = sizeof(addr); + + #ifdef HAVE_ACCEPT4 + int sockfd = accept4(listener->watcher.fd, + (struct sockaddr *)&addr, + &addr_len, + SOCK_NONBLOCK); + #else + int sockfd = accept(listener->watcher.fd, + (struct sockaddr *)&addr, + &addr_len); + #endif + if (sockfd < 0) { + int saved_errno = errno; + + warn("accept failed: %s", strerror(errno)); + + errno = saved_errno; + return 0; + } + const char *header = "HTTP/1.1 200 OK\n\n"; + write(sockfd , header , strlen(header)); + print_connections(sockfd); + close(sockfd); + return 1; +} + /* * Close and free all connections */ @@ -174,9 +210,18 @@ free_connections(struct ev_loop *loop) { } } -/* dumps a list of all connections for debugging */ +/* dumps a list of all connections to the file descriptor*/ +void +print_connections(int fd) { + struct Connection *iter; + TAILQ_FOREACH(iter, &connections, entries) + print_connection(fd, iter); + return; +} + +/* dumps a list of all connections for debugging to a temporary file */ void -print_connections() { +print_connections_file() { char filename[] = "/tmp/sniproxy-connections-XXXXXX"; int fd = mkstemp(filename); @@ -185,19 +230,11 @@ print_connections() { return; } - FILE *temp = fdopen(fd, "w"); - if (temp == NULL) { - warn("fdopen failed: %s", strerror(errno)); - return; - } + dprintf(fd, "Running connections:\n"); + print_connections(fd); - fprintf(temp, "Running connections:\n"); - struct Connection *iter; - TAILQ_FOREACH(iter, &connections, entries) - print_connection(temp, iter); - - if (fclose(temp) < 0) - warn("fclose failed: %s", strerror(errno)); + if (close(fd) < 0) + warn("close failed: %s", strerror(errno)); notice("Dumped connections to %s", filename); } @@ -896,53 +933,54 @@ free_connection(struct Connection *con) { } static void -print_connection(FILE *file, const struct Connection *con) { +print_connection(int file, const struct Connection *con) { char client[INET6_ADDRSTRLEN + 8]; char server[INET6_ADDRSTRLEN + 8]; switch (con->state) { case NEW: - fprintf(file, "NEW -\t-\n"); + dprintf(file, "NEW\t-\t-\n"); break; case ACCEPTED: - fprintf(file, "ACCEPTED %s %zu/%zu\t-\n", + dprintf(file, "ACCEPTED\t%s %zu/%zu\t-\n", display_sockaddr(&con->client.addr, client, sizeof(client)), buffer_len(con->client.buffer), buffer_size(con->client.buffer)); break; case PARSED: - fprintf(file, "PARSED %s %zu/%zu\t-\n", + dprintf(file, "PARSED\t%s %zu/%zu\t-\n", display_sockaddr(&con->client.addr, client, sizeof(client)), buffer_len(con->client.buffer), buffer_size(con->client.buffer)); break; case RESOLVING: - fprintf(file, "RESOLVING %s %zu/%zu\t-\n", + dprintf(file, "RESOLVING\t%s %zu/%zu\t-\n", display_sockaddr(&con->client.addr, client, sizeof(client)), buffer_len(con->client.buffer), buffer_size(con->client.buffer)); break; case RESOLVED: - fprintf(file, "RESOLVED %s %zu/%zu\t-\n", + dprintf(file, "RESOLVED\t%s %zu/%zu\t-\n", display_sockaddr(&con->client.addr, client, sizeof(client)), buffer_len(con->client.buffer), buffer_size(con->client.buffer)); break; case CONNECTED: - fprintf(file, "CONNECTED %s %zu/%zu\t%s %zu/%zu\n", + dprintf(file, "CONNECTED\t%s %zu/%zu\t%s %zu/%zu\t%s\n", display_sockaddr(&con->client.addr, client, sizeof(client)), buffer_len(con->client.buffer), buffer_size(con->client.buffer), display_sockaddr(&con->server.addr, server, sizeof(server)), - buffer_len(con->server.buffer), buffer_size(con->server.buffer)); + buffer_len(con->server.buffer), buffer_size(con->server.buffer), + con->hostname); break; case SERVER_CLOSED: - fprintf(file, "SERVER_CLOSED %s %zu/%zu\t-\n", + dprintf(file, "SERVER_CLOSED\t%s %zu/%zu\t-\n", display_sockaddr(&con->client.addr, client, sizeof(client)), buffer_len(con->client.buffer), buffer_size(con->client.buffer)); break; case CLIENT_CLOSED: - fprintf(file, "CLIENT_CLOSED -\t%s %zu/%zu\n", + dprintf(file, "CLIENT_CLOSED\t-\t%s %zu/%zu\n", display_sockaddr(&con->server.addr, server, sizeof(server)), buffer_len(con->server.buffer), buffer_size(con->server.buffer)); break; case CLOSED: - fprintf(file, "CLOSED -\t-\n"); + dprintf(file, "CLOSED\t-\t-\n"); break; } } diff --git a/src/connection.h b/src/connection.h index 013f046f..0f167661 100644 --- a/src/connection.h +++ b/src/connection.h @@ -64,7 +64,9 @@ struct Connection { void init_connections(); int accept_connection(struct Listener *, struct ev_loop *); +int accept_statusconnection(struct Listener *, struct ev_loop *); void free_connections(struct ev_loop *); -void print_connections(); +void print_connections_file(); +void print_connections(int); #endif diff --git a/src/listener.c b/src/listener.c index ce5956da..54ea963c 100644 --- a/src/listener.c +++ b/src/listener.c @@ -47,6 +47,7 @@ #include "protocol.h" #include "tls.h" #include "http.h" +#include "connection.h" static void close_listener(struct ev_loop *, struct Listener *); static void accept_cb(struct ev_loop *, struct ev_io *, int); @@ -226,10 +227,18 @@ new_listener() { ev_io_init(&listener->watcher, accept_cb, -1, EV_READ); ev_timer_init(&listener->backoff_timer, backoff_timer_cb, 0.0, 0.0); listener->table = NULL; + listener->accept_cb = &accept_connection; return listener; } +struct Listener * +new_statuslistener() { + struct Listener * listener = new_listener(); + listener->accept_cb = &accept_statusconnection; + return listener; +} + int accept_listener_arg(struct Listener *listener, const char *arg) { if (listener->address == NULL && !is_numeric(arg)) { @@ -493,13 +502,17 @@ static int init_listener(struct Listener *listener, const struct Table_head *tables, struct ev_loop *loop) { char address[ADDRESS_BUFFER_SIZE]; - struct Table *table = table_lookup(tables, listener->table_name); - if (table == NULL) { - err("Table \"%s\" not defined", listener->table_name); - return -1; + + if (listener->accept_cb != &accept_statusconnection) { + struct Table *table = table_lookup(tables, listener->table_name); + if (table == NULL) { + err("Table \"%s\" not defined", listener->table_name); + return -1; + } + + init_table(table); + listener->table = table_ref_get(table); } - init_table(table); - listener->table = table_ref_get(table); /* If no port was specified on the fallback address, inherit the address * from the listening address */ diff --git a/src/listener.h b/src/listener.h index c6fb5514..617a2e00 100644 --- a/src/listener.h +++ b/src/listener.h @@ -53,6 +53,7 @@ struct Listener { struct Listener *new_listener(); +struct Listener *new_statuslistener(); int accept_listener_arg(struct Listener *, const char *); int accept_listener_table_name(struct Listener *, const char *); int accept_listener_fallback_address(struct Listener *, const char *); diff --git a/src/sniproxy.c b/src/sniproxy.c index 9484294b..a946403a 100644 --- a/src/sniproxy.c +++ b/src/sniproxy.c @@ -280,7 +280,7 @@ signal_cb(struct ev_loop *loop, struct ev_signal *w, int revents) { reload_config(config, loop); break; case SIGUSR1: - print_connections(); + print_connections_file(); break; case SIGINT: case SIGTERM: