[tor-commits] [obfsproxy/master] We now handle signals according to 180-pluggable-transport.txt.

nickm at torproject.org nickm at torproject.org
Thu Jul 14 15:39:26 UTC 2011


commit 874b61dfcd82c05a518d676f135ea52c7a190b78
Author: George Kadianakis <desnacked at 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;





More information about the tor-commits mailing list