commit 874b61dfcd82c05a518d676f135ea52c7a190b78 Author: George Kadianakis desnacked@gmail.com Date: Tue Jun 28 03:35:10 2011 +0200
We now handle signals according to 180-pluggable-transport.txt. --- src/main.c | 70 ++++++++++++++++++------ src/main.h | 10 ++++ src/network.c | 163 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++- src/network.h | 2 + 4 files changed, 227 insertions(+), 18 deletions(-)
diff --git a/src/main.c b/src/main.c index a8347c2..81e8c31 100644 --- a/src/main.c +++ b/src/main.c @@ -33,6 +33,8 @@ static int handle_obfsproxy_args(const char **argv); extern char *supported_protocols[]; extern int n_supported_protocols;
+static struct event_base *the_event_base=NULL; + /** Prints the obfsproxy usage instructions then exits. */ @@ -56,17 +58,46 @@ usage(void) }
/** - This is called on SIGINT. It kills the event base loop, so that we - start exiting. + This is called when we receive a signal. + It figures out the signal type and acts accordingly. + + Current behavior: + SIGINT: On a single SIGINT we stop accepting new connections, + keep the already existing connections open, + and terminate when they all close. + On a second SIGINT we shut down immediately but cleanly. + SIGTERM: Shut down obfsproxy immediately but cleanly. */ static void handle_signal_cb(evutil_socket_t fd, short what, void *arg) { - struct event_base *base = arg; - /* int signum = (int) fd; */ - - log_info("Caught SIGINT."); - event_base_loopexit(base, NULL); + int signum = (int) fd; + static int got_sigint=0; + + switch (signum) { + case SIGINT: + if (!got_sigint) { + start_shutdown(0); + got_sigint++; + } else { + start_shutdown(1); + } + break; + case SIGTERM: + start_shutdown(1); + break; + } +} + +/** + Stops obfsproxy's event loop. + + Final cleanup happens in main(). +*/ +void +finish_shutdown(void) +{ + event_base_loopexit(the_event_base, NULL); }
/** @@ -159,8 +190,8 @@ handle_obfsproxy_args(const char **argv) int main(int argc, const char **argv) { - struct event_base *base; - struct event *sigevent; + struct event *sig_int; + struct event *sig_term;
/* Yes, these are three stars right there. This is an array of arrays of strings! Every element of the array is an array of @@ -290,14 +321,14 @@ main(int argc, const char **argv) #endif
/* Initialize libevent */ - base = event_base_new(); - if (!base) { + the_event_base = event_base_new(); + if (!the_event_base) { log_warn("Can't initialize Libevent; failing"); return 1; }
/* ASN should this happen only when SOCKS is enabled? */ - if (init_evdns_base(base) < 0) { + if (init_evdns_base(the_event_base) < 0) { log_warn("Can't initialize evdns; failing"); return 1; } @@ -306,9 +337,11 @@ main(int argc, const char **argv) #ifdef SIGPIPE signal(SIGPIPE, SIG_IGN); #endif - sigevent = evsignal_new(base, SIGINT, - handle_signal_cb, (void*) base); - if (event_add(sigevent,NULL)) { + sig_int = evsignal_new(the_event_base, SIGINT, + handle_signal_cb, NULL); + sig_term = evsignal_new(the_event_base, SIGTERM, + handle_signal_cb, NULL); + if (event_add(sig_int,NULL) || event_add(sig_term,NULL)) { log_warn("We can't even add events for signals! Exiting."); return 1; } @@ -337,7 +370,7 @@ main(int argc, const char **argv) continue; }
- temp_listener = listener_new(base, proto_params); + temp_listener = listener_new(the_event_base, proto_params);
/** Free the space allocated for this protocol's options. */ free(protocol_options[h]); @@ -358,7 +391,7 @@ main(int argc, const char **argv)
/* run the event loop if at least a listener was created. */ if (n_listeners) - event_base_dispatch(base); + event_base_dispatch(the_event_base);
log_info("Exiting.");
@@ -368,6 +401,9 @@ main(int argc, const char **argv) /* We are exiting. Clean everything. */ for (h=0;h<n_listeners;h++) listener_free(listeners[h]); + if (listeners) + free(listeners); + free(protocol_options); free(n_options_array); free(protocols); diff --git a/src/main.h b/src/main.h new file mode 100644 index 0000000..8814499 --- /dev/null +++ b/src/main.h @@ -0,0 +1,10 @@ +/* Copyright 2011 Nick Mathewson, George Kadianakis + See LICENSE for other credits and copying information +*/ + +#ifndef MAIN_H +#define MAIN_H + +void finish_shutdown(void); + +#endif diff --git a/src/network.c b/src/network.c index ed78104..fc53564 100644 --- a/src/network.c +++ b/src/network.c @@ -8,6 +8,7 @@ #include "socks.h" #include "protocol.h" #include "socks.h" +#include "main.h"
#include <assert.h> #include <stdlib.h> @@ -25,10 +26,31 @@ struct listener_t { protocol_params_t *proto_params; };
+/** + Linked list carrying all currently active connections. + [linked list code was copied off tor.] +*/ +typedef struct conn_list_t { + conn_t *conn; + struct conn_list_t *next; +} conn_list_t; + +/** First and last elements in the linked list of active + connections. */ +static conn_list_t *cl_list=NULL; +static conn_list_t *cl_tail=NULL; +/** Active connection counter */ +static int n_connections=0; + +/** Flag toggled when obfsproxy is shutting down. It blocks new + connections and shutdowns when the last connection is closed. */ +static int shutting_down=0; + static void simple_listener_cb(struct evconnlistener *evcl, evutil_socket_t fd, struct sockaddr *sourceaddr, int socklen, void *arg);
static void conn_free(conn_t *conn); +static void close_all_connections(void);
static void close_conn_on_flush(struct bufferevent *bev, void *arg); static void plaintext_read_cb(struct bufferevent *bev, void *arg); @@ -40,6 +62,122 @@ static void input_event_cb(struct bufferevent *bev, short what, void *arg); static void output_event_cb(struct bufferevent *bev, short what, void *arg);
/** + Puts obfsproxy's networking subsystem on "closing time" mode. This + means that we stop accepting new connections and we shutdown when + the last connection is closed. + + If 'barbaric' is set, we forcefully close all open connections and + finish shutdown. +*/ +void +start_shutdown(int barbaric) +{ + if (!shutting_down) + shutting_down=1; + + if (!n_connections) { + finish_shutdown(); + return; + } + + if (barbaric) { + if (n_connections) + close_all_connections(); + finish_shutdown(); + return; + } +} + +/** + Closes all open connections. +*/ +static void +close_all_connections(void) +{ + conn_t *conn; + + while (cl_list) { + assert(cl_list->conn); + assert(n_connections > 0); + conn = cl_list->conn; + conn_free(conn); + } +} + +/** + Places 'conn' to the end of the linked list of active connections. + Returns 1 on success, -1 on fail. +*/ +static int +cl_add(conn_t *conn) +{ + conn_list_t *cl; + + cl = calloc(1, sizeof(conn_list_t)); + if (!cl) + return -1; + + cl->conn = conn; + + if (!cl_tail) { + assert(!cl_list); + assert(!n_connections); + cl_list=cl; + cl_tail=cl; + } else { + assert(cl_list); + assert(!cl_tail->next); + cl_tail->next = cl; + cl_tail = cl; + } + + n_connections++; + + return 1; +} + +/** + Removes 'conn' from the linked list of connections: + It frees the list node carrying 'conn', but leaves 'conn' itself intact. +*/ +static void +cl_remove(conn_t *conn) +{ + conn_list_t *tmpo, *victim; + + if (!cl_list) + return; /* nothing here. */ + + /* first check to see if it's the first entry */ + tmpo = cl_list; + if (tmpo->conn == conn) { + /* it's the first one. remove it from the list. */ + cl_list = tmpo->next; + if (!cl_list) + cl_tail = NULL; + n_connections--; + victim = tmpo; + } else { /* we need to hunt through the rest of the list */ + for ( ;tmpo->next && tmpo->next->conn != conn; tmpo=tmpo->next) ; + if (!tmpo->next) { + return; + } + /* now we know tmpo->next->conn == conn */ + victim = tmpo->next; + tmpo->next = victim->next; + if (cl_tail == victim) + cl_tail = tmpo; + n_connections--; + } + + assert(n_connections>=0); + + /* now victim points to the element that needs to be removed */ + free(victim); + victim = NULL; +} + +/** This function sets up the protocol defined by 'options' and attempts to bind a new listener for it.
@@ -91,13 +229,19 @@ static void simple_listener_cb(struct evconnlistener *evcl, evutil_socket_t fd, struct sockaddr *sourceaddr, int socklen, void *arg) { + if (shutting_down) { + if (fd >= 0) + evutil_closesocket(fd); + return ; + } + listener_t *lsn = arg; struct event_base *base; conn_t *conn = calloc(1, sizeof(conn_t)); if (!conn) goto err;
- log_debug("Got a connection"); + log_debug("Got a connection attempt.");
conn->mode = lsn->proto_params->mode;
@@ -170,6 +314,13 @@ simple_listener_cb(struct evconnlistener *evcl, bufferevent_enable(conn->output, EV_READ|EV_WRITE); }
+ /* add conn to the linked list of connections */ + if (cl_add(conn)<0) + goto err; + + log_debug("Connection setup completed. " + "We currently have %d connections!", n_connections); + return; err: if (conn) @@ -190,7 +341,17 @@ conn_free(conn_t *conn) if (conn->output) bufferevent_free(conn->output); memset(conn, 0x99, sizeof(conn_t)); + /* remove conn from the linked list of connections */ + cl_remove(conn); free(conn); + + log_debug("Connection destroyed. " + "We currently have %d connections!", n_connections); + + /** If this was the last connection AND we are shutting down, + finish shutdown. */ + if (!n_connections && shutting_down) + finish_shutdown(); }
static void diff --git a/src/network.h b/src/network.h index 2a1daeb..28a0d7d 100644 --- a/src/network.h +++ b/src/network.h @@ -47,6 +47,8 @@ listener_t *listener_new(struct event_base *base, struct protocol_params_t *params); void listener_free(listener_t *listener);
+void start_shutdown(int barbaric); + #ifdef NETWORK_PRIVATE typedef struct conn_t { struct socks_state_t *socks_state;