[tor-commits] [obfsproxy/master] Partially revert the last-but-one patch; separate "configuration"s from "listener"s.

nickm at torproject.org nickm at torproject.org
Fri Sep 9 17:08:58 UTC 2011


commit fa84ea22705ac578fc24fbed91813de12b56a876
Author: Zack Weinberg <zackw at panix.com>
Date:   Tue Aug 2 15:17:55 2011 -0700

    Partially revert the last-but-one patch; separate "configuration"s from "listener"s.
    
    listener_t is once again entirely internal to network.c and has no
    protocol-specific content.  It points to a 'config_t', which is
    entirely protocol-specific, but has accessors in the protocol vtable
    to retrieve listen and target address lists.  Generic code can now
    handle there being more than one listen address (open listening
    sockets for all of them; each gets its own 'listener_t') or more than
    one target address (try each in succession until one connects -- this
    may not work 100% correctly in the case of an asynchronous failure).
    The notion of a 'target address' or 'listener address' is still not
    fully decoupled from the notion of an 'upstream address' or
    'downstream address', unfortunately.  These changes also facilitate
    further simplification of the argument-parsing code in main().
---
 src/main.c                |  136 ++++++----------
 src/network.c             |  390 ++++++++++++++++++++++-----------------------
 src/network.h             |   38 ++----
 src/protocol.c            |   74 ++++++---
 src/protocol.h            |   58 +++++--
 src/protocols/dummy.c     |  125 ++++++++-------
 src/protocols/dummy.h     |   19 ++-
 src/test/unittest_dummy.c |   34 ++--
 src/util.h                |    2 +-
 9 files changed, 434 insertions(+), 442 deletions(-)

diff --git a/src/main.c b/src/main.c
index 14125f8..06729be 100644
--- a/src/main.c
+++ b/src/main.c
@@ -58,7 +58,7 @@ handle_signal_cb(evutil_socket_t fd, short what, void *arg)
 
   switch (signum) {
   case SIGINT:
-    free_all_listeners();
+    close_all_listeners();
     if (!got_sigint) {
       log_info("Got SIGINT. Preparing shutdown.");
       start_shutdown(0);
@@ -104,7 +104,7 @@ is_supported_protocol(const char *name)
    If it fails, it exits obfsproxy.
 */
 static int
-handle_obfsproxy_args(const char **argv)
+handle_obfsproxy_args(const char *const *argv)
 {
   int logmethod_set=0;
   int logsev_set=0;
@@ -154,83 +154,53 @@ handle_obfsproxy_args(const char **argv)
 }
 
 int
-main(int argc, const char **argv)
+main(int argc, const char *const *argv)
 {
   struct event *sig_int;
   struct event *sig_term;
-
-  /* Array of argument counts, one per listener. */
-  int *listener_argcs = NULL;
-
-  /* Array of pointers into argv. Each points to the beginning of a
-     sequence of options for a particular listener. */
-  const char *const **listener_argvs = NULL;
-
-  /* Total number of listeners requested on the command line. */
-  unsigned int n_listeners;
-
-  /* Total number of listeners successfully created. */
-  unsigned int n_good_listeners;
-
-  /* Index of the first argv string after the optional obfsproxy
-      arguments. Normally this should be where the listeners start. */
-  int start_of_listeners;
-
-  int cl, i;
+  smartlist_t *configs = smartlist_create();
+  const char *const *begin;
+  const char *const *end;
 
   /* Handle optional obfsproxy arguments. */
-  start_of_listeners = handle_obfsproxy_args(argv);
+  begin = argv + handle_obfsproxy_args(argv);
 
-  if (!is_supported_protocol(argv[start_of_listeners]))
+  /* Find the subsets of argv that define each configuration.
+     Each configuration's subset consists of the entries in argv from
+     its recognized protocol name, up to but not including the next
+     recognized protocol name. */
+  if (!*begin || !is_supported_protocol(*begin))
     usage();
 
-  /* Count number of listeners and allocate space for the listener-
-     argument arrays. We already know there's at least one. */
-  n_listeners = 1;
-  for (i = start_of_listeners+1; i < argc; i++)
-    if (is_supported_protocol(argv[i]))
-      n_listeners++;
-
-  log_debug("%d listener%s on command line.",
-            n_listeners, n_listeners == 1 ? "" : "s");
-  listener_argcs = xzalloc(n_listeners * sizeof(int));
-  listener_argvs = xzalloc(n_listeners * sizeof(char **));
-
-  /* Each listener's argument vector consists of the entries in argv
-     from its recognized protocol name, up to but not including
-     the next recognized protocol name. */
-  cl = 1;
-  listener_argvs[0] = &argv[start_of_listeners];
-  for (i = start_of_listeners + 1; i < argc; i++)
-    if (is_supported_protocol(argv[i])) {
-      listener_argcs[cl-1] = i - (listener_argvs[cl-1] - argv);
-      if (listener_argcs[cl-1] == 1)
-        log_warn("No arguments to listener %d", cl);
-
-      listener_argvs[cl] = &argv[i];
-      cl++;
-    }
-
-  listener_argcs[cl-1] = argc - (listener_argvs[cl-1] - argv);
-  if (listener_argcs[cl-1] == 1)
-    log_warn("No arguments to listener %d", cl);
-
-  obfs_assert(cl == n_listeners);
-
-  if (log_do_debug()) {
-    smartlist_t *s = smartlist_create();
-    char *joined;
-    for (cl = 0; cl < n_listeners; cl++) {
-      smartlist_clear(s);
-      for (i = 0; i < listener_argcs[cl]; i++)
-        smartlist_add(s, (void *)listener_argvs[cl][i]);
+  do {
+    end = begin+1;
+    while (*end && !is_supported_protocol(*end))
+      end++;
+    if (log_do_debug()) {
+      smartlist_t *s = smartlist_create();
+      char *joined;
+      const char *const *p;
+      for (p = begin; p < end; p++)
+        smartlist_add(s, (void *)*p);
       joined = smartlist_join_strings(s, " ", 0, NULL);
-      log_debug("Listener %d: %s", cl+1, joined);
+      log_debug("Configuration %d: %s", smartlist_len(configs)+1, joined);
+      free(joined);
+      smartlist_free(s);
     }
-    smartlist_free(s);
-  }
+    if (end == begin+1) {
+      log_warn("No arguments for configuration %d", smartlist_len(configs)+1);
+      usage();
+    } else {
+      config_t *cfg = config_create(end - begin, begin);
+      if (!cfg)
+        return 2; /* diagnostic already issued */
+      smartlist_add(configs, cfg);
+    }
+    begin = end;
+  } while (*begin);
+  obfs_assert(smartlist_len(configs) > 0);
 
-  /* argv has been chunked; proceed with initialization. */
+  /* Configurations have been established; proceed with initialization. */
 
   /* Ugly method to fix a Windows problem:
      http://archives.seul.org/libevent/users/Oct-2010/msg00049.html */
@@ -268,37 +238,31 @@ main(int argc, const char **argv)
     return 1;
   }
 
-  /* Open a new listener for each protocol. */
-  n_good_listeners = 0;
-  for (cl = 0; cl < n_listeners; cl++)
-    if (create_listener(the_event_base,
-                        listener_argcs[cl], listener_argvs[cl]))
-      n_good_listeners++;
-
-  /* If the number of usable listeners is not equal to the complete
-     set specified on the command line, we have a usage error.
-     Diagnostics have already been issued.  */
-  log_debug("%d recognized listener%s on command line, %d with valid config",
-            n_listeners, n_listeners == 1 ? "" : "s", n_good_listeners);
-  if (n_listeners != n_good_listeners)
-    return 2;
+  /* Open listeners for each configuration. */
+  SMARTLIST_FOREACH(configs, config_t *, cfg, {
+    if (!open_listeners(the_event_base, cfg)) {
+      log_error("Failed to open listeners for configuration %d", cfg_sl_idx+1);
+      return 1;
+    }
+  });
 
   /* We are go for launch. */
   event_base_dispatch(the_event_base);
 
+  /* We have landed. */
   log_info("Exiting.");
 
-  free_all_listeners();
+  close_all_listeners();
+  SMARTLIST_FOREACH(configs, config_t *, cfg, config_free(cfg));
+  smartlist_free(configs);
+
   evdns_base_free(get_evdns_base(), 0);
   event_free(sig_int);
   event_free(sig_term);
   event_base_free(the_event_base);
 
   cleanup_crypto();
-
   close_obfsproxy_logfile();
-  free(listener_argvs);
-  free(listener_argcs);
 
   return 0;
 }
diff --git a/src/network.c b/src/network.c
index d11b60f..4ae2c15 100644
--- a/src/network.c
+++ b/src/network.c
@@ -47,6 +47,15 @@
    (version 4 or 5).
 */
 
+/**
+  This struct defines the state of a listener on a particular address.
+ */
+typedef struct listener_t {
+  config_t *cfg;
+  struct evconnlistener *listener;
+  char *address;
+} listener_t;
+
 /** All our listeners. */
 static smartlist_t *listeners;
 
@@ -59,18 +68,24 @@ static int shutting_down=0;
 
 static void listener_free(listener_t *lsn);
 
-static void simple_client_listener_cb(struct evconnlistener *evcl,
-   evutil_socket_t fd, struct sockaddr *sourceaddr, int socklen, void *arg);
-static void socks_client_listener_cb(struct evconnlistener *evcl,
-   evutil_socket_t fd, struct sockaddr *sourceaddr, int socklen, void *arg);
-static void simple_server_listener_cb(struct evconnlistener *evcl,
-   evutil_socket_t fd, struct sockaddr *sourceaddr, int socklen, void *arg);
+static void listener_cb(struct evconnlistener *evcl, evutil_socket_t fd,
+                        struct sockaddr *sourceaddr, int socklen,
+                        void *closure);
+
+static void simple_client_listener_cb(conn_t *conn, struct bufferevent *buf);
+static void socks_client_listener_cb(conn_t *conn, struct bufferevent *buf);
+static void simple_server_listener_cb(conn_t *conn, struct bufferevent *buf);
 
 static void conn_free(conn_t *conn);
+static void close_conn(conn_t *conn);
 static void close_all_connections(void);
 
 static void close_conn_on_flush(struct bufferevent *bev, void *arg);
 
+static struct bufferevent *open_outbound_socket(conn_t *conn,
+                                                struct event_base *base,
+                                                bufferevent_data_cb readcb);
+
 static void upstream_read_cb(struct bufferevent *bev, void *arg);
 static void downstream_read_cb(struct bufferevent *bev, void *arg);
 static void socks_read_cb(struct bufferevent *bev, void *arg);
@@ -126,49 +141,48 @@ close_all_connections(void)
 }
 
 /**
-   This function spawns a listener configured according to the
-   provided argument subvector.  Returns 1 on success, 0 on failure.
-   (No, you can't have the listener object. It's private.)
-*/
+   This function opens listening sockets configured according to the
+   provided 'config_t'.  Returns 1 on success, 0 on failure.
+ */
 int
-create_listener(struct event_base *base, int argc, const char *const *argv)
+open_listeners(struct event_base *base, config_t *cfg)
 {
   const unsigned flags =
     LEV_OPT_CLOSE_ON_FREE|LEV_OPT_CLOSE_ON_EXEC|LEV_OPT_REUSEABLE;
-  evconnlistener_cb callback;
-  listener_t *lsn = proto_listener_create(argc, argv);
-  if (!lsn)
-    return 0;
-
-  switch (lsn->mode) {
-  case LSN_SIMPLE_CLIENT: callback = simple_client_listener_cb; break;
-  case LSN_SIMPLE_SERVER: callback = simple_server_listener_cb; break;
-  case LSN_SOCKS_CLIENT:  callback = socks_client_listener_cb;  break;
-  default: obfs_abort();
-  }
-
-  lsn->listen_addr_str = printable_address(lsn->listen_addr->ai_addr,
-                                           lsn->listen_addr->ai_addrlen);
-  lsn->listener =
-    evconnlistener_new_bind(base, callback, lsn, flags, -1,
-                            lsn->listen_addr->ai_addr,
-                            lsn->listen_addr->ai_addrlen);
-
-  if (!lsn->listener) {
-    log_warn("Could not begin listening on %s: %s",
-             lsn->listen_addr_str,
-             evutil_socket_error_to_string(EVUTIL_SOCKET_ERROR()));
-    listener_free(lsn);
-    return 0;
-  }
-
-  log_debug("Now listening on %s in mode %d, protocol %s.",
-            lsn->listen_addr_str, lsn->mode, lsn->vtable->name);
+  size_t i;
+  listener_t *lsn;
+  struct evutil_addrinfo *addrs;
 
   /* If we don't have a listener list, create one now. */
   if (!listeners)
     listeners = smartlist_create();
-  smartlist_add(listeners, lsn);
+
+  for (i = 0; ; i++) {
+    addrs = config_get_listen_addrs(cfg, i);
+    if (!addrs) break;
+    do {
+      lsn = xzalloc(sizeof(listener_t));
+      lsn->cfg = cfg;
+      lsn->address = printable_address(addrs->ai_addr, addrs->ai_addrlen);
+      lsn->listener =
+        evconnlistener_new_bind(base, listener_cb, lsn, flags, -1,
+                                addrs->ai_addr, addrs->ai_addrlen);
+
+      if (!lsn->listener) {
+        log_warn("Failed to open listening socket on %s: %s",
+                 lsn->address,
+                 evutil_socket_error_to_string(EVUTIL_SOCKET_ERROR()));
+        listener_free(lsn);
+        return 0;
+      }
+
+      smartlist_add(listeners, lsn);
+      log_debug("Now listening on %s for protocol %s.",
+                lsn->address, cfg->vtable->name);
+
+      addrs = addrs->ai_next;
+    } while (addrs);
+  }
 
   return 1;
 }
@@ -181,21 +195,16 @@ listener_free(listener_t *lsn)
 {
   if (lsn->listener)
     evconnlistener_free(lsn->listener);
-  if (lsn->listen_addr)
-    evutil_freeaddrinfo(lsn->listen_addr);
-  if (lsn->listen_addr_str)
-    free(lsn->listen_addr_str);
-  if (lsn->target_addr)
-    evutil_freeaddrinfo(lsn->target_addr);
-
-  proto_listener_free(lsn);
+  if (lsn->address)
+    free(lsn->address);
+  free(lsn);
 }
 
 /**
    Frees all active listeners.
 */
 void
-free_all_listeners(void)
+close_all_listeners(void)
 {
   if (!listeners)
     return;
@@ -208,73 +217,75 @@ free_all_listeners(void)
 }
 
 /**
-   This function is called when an upstream client connects to us in
-   simple client mode.
-*/
+   This function is called when any listener receives a connection.
+ */
 static void
-simple_client_listener_cb(struct evconnlistener *evcl,
-                          evutil_socket_t fd, struct sockaddr *sourceaddr,
-                          int socklen, void *arg)
+listener_cb(struct evconnlistener *evcl, evutil_socket_t fd,
+            struct sockaddr *peeraddr, int peerlen,
+            void *closure)
 {
-  struct event_base *base;
-  listener_t *lsn = arg;
-  conn_t *conn = proto_conn_create(lsn);
-  if (!conn) {
-    log_warn("Failed to create state object for new connection to %s",
-             lsn->listen_addr_str);
-    goto err;
+  struct event_base *base = evconnlistener_get_base(evcl);
+  listener_t *lsn = closure;
+  char *peername = printable_address(peeraddr, peerlen);
+  conn_t *conn = proto_conn_create(lsn->cfg);
+  struct bufferevent *buf = bufferevent_socket_new(base, fd,
+                                                   BEV_OPT_CLOSE_ON_FREE);
+
+  if (!conn || !buf) {
+    log_warn("%s: failed to set up new connection from %s.",
+             lsn->address, peername);
+    if (buf)
+      bufferevent_free(buf);
+    else
+      evutil_closesocket(fd);
+    if (conn)
+      proto_conn_free(conn);
+    free(peername);
+    return;
   }
 
-  conn->peername = printable_address(sourceaddr, socklen);
-  log_debug("%s: connection to %s from %s", __func__,
-            lsn->listen_addr_str, conn->peername);
+  if (!connections)
+    connections = smartlist_create();
+  smartlist_add(connections, conn);
+  log_debug("%s: new connection from %s (%d total)", lsn->address, peername,
+            smartlist_len(connections));
+
+  conn->peername = peername;
+  switch (conn->mode) {
+  case LSN_SIMPLE_CLIENT: simple_client_listener_cb(conn, buf); break;
+  case LSN_SOCKS_CLIENT:  socks_client_listener_cb(conn, buf);  break;
+  case LSN_SIMPLE_SERVER: simple_server_listener_cb(conn, buf); break;
+  default:
+    obfs_abort();
+  }
+}
 
-  conn->mode = lsn->mode;
+/**
+   This function is called when an upstream client connects to us in
+   simple client mode.
+*/
+static void
+simple_client_listener_cb(conn_t *conn, struct bufferevent *buf)
+{
+  struct event_base *base = bufferevent_get_base(buf);
+  obfs_assert(buf);
+  obfs_assert(conn);
   obfs_assert(conn->mode == LSN_SIMPLE_CLIENT);
+  log_debug("%s: simple client connection", conn->peername);
 
-  /* New bufferevent to wrap socket we received. */
-  base = evconnlistener_get_base(lsn->listener);
-  conn->upstream = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
-  if (!conn->upstream)
-    goto err;
-  fd = -1; /* prevent double-close */
-
-  bufferevent_setcb(conn->upstream,
-                    upstream_read_cb, NULL, error_cb, conn);
+  conn->upstream = buf;
+  bufferevent_setcb(conn->upstream, upstream_read_cb, NULL, error_cb, conn);
 
   /* Don't enable the upstream side for reading at this point; wait
      till the downstream side is established. */
 
-  /* New bufferevent to connect to the target address */
-  conn->downstream = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE);
-  if (!conn->downstream)
-    goto err;
-
-  bufferevent_setcb(conn->downstream,
-                    downstream_read_cb, NULL, pending_conn_cb, conn);
-
-  /* Launch the connect attempt. */
-  if (bufferevent_socket_connect(conn->downstream,
-                                 lsn->target_addr->ai_addr,
-                                 lsn->target_addr->ai_addrlen)<0)
-    goto err;
-
-  bufferevent_enable(conn->downstream, EV_READ|EV_WRITE);
-
-  /* add conn to the connection list */
-  if (!connections)
-    connections = smartlist_create();
-  smartlist_add(connections, conn);
-
-  log_debug("%s: setup completed, %d connections",
-            __func__, smartlist_len(connections));
-  return;
+  conn->downstream = open_outbound_socket(conn, base, downstream_read_cb);
+  if (!conn->downstream) {
+    close_conn(conn);
+    return;
+  }
 
- err:
-  if (conn)
-    conn_free(conn);
-  if (fd >= 0)
-    evutil_closesocket(fd);
+  log_debug("%s: setup complete", conn->peername);
 }
 
 /**
@@ -282,56 +293,24 @@ simple_client_listener_cb(struct evconnlistener *evcl,
    socks mode.
 */
 static void
-socks_client_listener_cb(struct evconnlistener *evcl,
-                         evutil_socket_t fd, struct sockaddr *sourceaddr,
-                         int socklen, void *arg)
+socks_client_listener_cb(conn_t *conn, struct bufferevent *buf)
 {
-  struct event_base *base;
-  listener_t *lsn = arg;
-  conn_t *conn = proto_conn_create(lsn);
-  if (!conn) {
-    log_warn("Failed to create state object for new connection to %s",
-             lsn->listen_addr_str);
-    goto err;
-  }
-
-  conn->peername = printable_address(sourceaddr, socklen);
-  log_debug("%s: connection to %s from %s", __func__,
-            lsn->listen_addr_str, conn->peername);
-
-  conn->mode = lsn->mode;
+  obfs_assert(buf);
+  obfs_assert(conn);
   obfs_assert(conn->mode == LSN_SOCKS_CLIENT);
+  log_debug("%s: socks client connection", conn->peername);
 
-  /* Construct SOCKS state. */
-  conn->socks_state = socks_state_new();
-
-  /* New bufferevent to wrap socket we received. */
-  base = evconnlistener_get_base(lsn->listener);
-  conn->upstream = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
-  if (!conn->upstream)
-    goto err;
-  fd = -1; /* prevent double-close */
-
+  conn->upstream = buf;
   bufferevent_setcb(conn->upstream, socks_read_cb, NULL, error_cb, conn);
   bufferevent_enable(conn->upstream, EV_READ|EV_WRITE);
 
+  /* Construct SOCKS state. */
+  conn->socks_state = socks_state_new();
+
   /* Do not create a downstream bufferevent at this time; the socks
      handler will do it after it learns the downstream peer address. */
 
-  /* add conn to the connection list */
-  if (!connections)
-    connections = smartlist_create();
-  smartlist_add(connections, conn);
-
-  log_debug("%s: setup completed, %d connections",
-            __func__, smartlist_len(connections));
-  return;
-
- err:
-  if (conn)
-    conn_free(conn);
-  if (fd >= 0)
-    evutil_closesocket(fd);
+  log_debug("%s: setup complete", conn->peername);
 }
 
 /**
@@ -339,33 +318,15 @@ socks_client_listener_cb(struct evconnlistener *evcl,
    server mode.
 */
 static void
-simple_server_listener_cb(struct evconnlistener *evcl,
-                          evutil_socket_t fd, struct sockaddr *sourceaddr,
-                          int socklen, void *arg)
+simple_server_listener_cb(conn_t *conn, struct bufferevent *buf)
 {
-  struct event_base *base;
-  listener_t *lsn = arg;
-  conn_t *conn = proto_conn_create(lsn);
-  if (!conn) {
-    log_warn("Failed to create state object for new connection to %s",
-             lsn->listen_addr_str);
-    goto err;
-  }
-
-  conn->peername = printable_address(sourceaddr, socklen);
-  log_debug("%s: connection to %s from %s", __func__,
-            lsn->listen_addr_str, conn->peername);
-
-  conn->mode = lsn->mode;
+  struct event_base *base = bufferevent_get_base(buf);
+  obfs_assert(buf);
+  obfs_assert(conn);
   obfs_assert(conn->mode == LSN_SIMPLE_SERVER);
+  log_debug("%s: server connection", conn->peername);
 
-  /* New bufferevent to wrap socket we received. */
-  base = evconnlistener_get_base(lsn->listener);
-  conn->downstream = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
-  if (!conn->downstream)
-    goto err;
-  fd = -1; /* prevent double-close */
-
+  conn->downstream = buf;
   bufferevent_setcb(conn->downstream,
                     downstream_read_cb, NULL, error_cb, conn);
 
@@ -373,35 +334,13 @@ simple_server_listener_cb(struct evconnlistener *evcl,
      till the upstream side is established. */
 
   /* New bufferevent to connect to the target address. */
-  conn->upstream = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE);
-  if (!conn->upstream)
-    goto err;
-
-  bufferevent_setcb(conn->upstream,
-                    upstream_read_cb, NULL, pending_conn_cb, conn);
-
-  /* Launch the connect attempt. */
-  if (bufferevent_socket_connect(conn->upstream,
-                                 lsn->target_addr->ai_addr,
-                                 lsn->target_addr->ai_addrlen) < 0)
-    goto err;
-
-  bufferevent_enable(conn->upstream, EV_READ|EV_WRITE);
-
-  /* add conn to the connection list */
-  if (!connections)
-    connections = smartlist_create();
-  smartlist_add(connections, conn);
-
-  log_debug("%s: setup completed, %d connections",
-            __func__, smartlist_len(connections));
-  return;
+  conn->upstream = open_outbound_socket(conn, base, upstream_read_cb);
+  if (!conn->upstream) {
+    close_conn(conn);
+    return;
+  }
 
- err:
-  if (conn)
-    conn_free(conn);
-  if (fd >= 0)
-    evutil_closesocket(fd);
+  log_debug("%s: setup complete", conn->peername);
 }
 
 /**
@@ -437,13 +376,10 @@ close_conn(conn_t *conn)
 
   /* If this was the last connection AND we are shutting down,
      finish shutdown. */
-  if (smartlist_len(connections) == 0) {
+  if (smartlist_len(connections) == 0 && shutting_down) {
     smartlist_free(connections);
-    connections = NULL;
-  }
-
-  if (!connections && shutting_down)
     finish_shutdown();
+  }
 }
 
 /**
@@ -461,6 +397,54 @@ close_conn_on_flush(struct bufferevent *bev, void *arg)
 }
 
 /**
+   Make the outbound socket for a connection.
+*/
+static struct bufferevent *
+open_outbound_socket(conn_t *conn, struct event_base *base,
+                     bufferevent_data_cb readcb)
+{
+  struct evutil_addrinfo *addr = config_get_target_addr(conn->cfg);
+  struct bufferevent *buf;
+  char *peername;
+
+  if (!addr) {
+    log_warn("%s: no target addresses available", conn->peername);
+    return NULL;
+  }
+
+  buf = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE);
+  if (!buf) {
+    log_warn("%s: unable to create outbound socket buffer", conn->peername);
+    return NULL;
+  }
+
+  bufferevent_setcb(buf, readcb, NULL, pending_conn_cb, conn);
+
+  do {
+    peername = printable_address(addr->ai_addr, addr->ai_addrlen);
+    log_info("%s (%s): trying to connect to %s",
+             conn->peername, conn->cfg->vtable->name, peername);
+    if (bufferevent_socket_connect(buf, addr->ai_addr, addr->ai_addrlen) >= 0) {
+      /* success */
+      bufferevent_enable(buf, EV_READ|EV_WRITE);
+      free(peername);
+      return buf;
+    }
+    log_info("%s: connection to %s failed: %s",
+             conn->peername, peername,
+             evutil_socket_error_to_string(EVUTIL_SOCKET_ERROR()));
+    free(peername);
+    addr = addr->ai_next;
+  } while (addr);
+
+  log_warn("%s: all outbound connection attempts failed",
+           conn->peername);
+
+  bufferevent_free(buf);
+  return NULL;
+}
+
+/**
     This callback is responsible for handling SOCKS traffic.
 */
 static void
diff --git a/src/network.h b/src/network.h
index 17b0e6b..8fb41ef 100644
--- a/src/network.h
+++ b/src/network.h
@@ -6,30 +6,12 @@
 #define NETWORK_H
 
 /* returns 1 on success, 0 on failure */
-int create_listener(struct event_base *base, int argc, const char *const *argv);
-void free_all_listeners(void);
+int open_listeners(struct event_base *base, config_t *cfg);
+void close_all_listeners(void);
 
 void start_shutdown(int barbaric);
 
 /**
-  This struct defines the state of a listener on a particular address.
-  Each protocol may extend this structure with additional private data
-  by embedding it as the first member of a larger structure (standard
-  fake-inheritance-in-C technique).  The protocol's listener_create()
-  method is responsible for filling in the |vtable|, |listen_addr|,
-  |target_addr|, and |mode| fields of this structure, but should leave
-  the |listener| and |listen_addr_str| fields alone.
- */
-struct listener_t {
-  const protocol_vtable  *vtable;
-  struct evconnlistener  *listener;
-  struct evutil_addrinfo *listen_addr;
-  char                   *listen_addr_str;
-  struct evutil_addrinfo *target_addr;
-  enum listen_mode        mode;
-};
-
-/**
    This struct defines the state of a connection between "upstream"
    and "downstream" peers (it's really two connections at the socket
    level).  Again, each protocol may extend this structure with
@@ -39,14 +21,14 @@ struct listener_t {
    private data of course.
  */
 struct conn_t {
-  const protocol_vtable *vtable;
-  char                  *peername;
-  socks_state_t         *socks_state;
-  struct bufferevent    *upstream;
-  struct bufferevent    *downstream;
-  enum listen_mode       mode     : 30;
-  unsigned int           flushing : 1;
-  unsigned int           is_open  : 1;
+  config_t           *cfg;
+  char               *peername;
+  socks_state_t      *socks_state;
+  struct bufferevent *upstream;
+  struct bufferevent *downstream;
+  enum listen_mode    mode     : 30;
+  unsigned int        flushing : 1;
+  unsigned int        is_open  : 1;
 };
 
 #endif
diff --git a/src/protocol.c b/src/protocol.c
index eddb9a3..e5d1818 100644
--- a/src/protocol.c
+++ b/src/protocol.c
@@ -22,18 +22,18 @@ const size_t n_supported_protocols =
   sizeof(supported_protocols)/sizeof(supported_protocols[0]);
 
 /**
-   This function dispatches (by name) creation of a |listener_t|
+   This function dispatches (by name) creation of a |config_t|
    to the appropriate protocol-specific initalization function.
  */
-listener_t *
-proto_listener_create(int n_options, const char *const *options)
+config_t *
+config_create(int n_options, const char *const *options)
 {
   size_t i;
   for (i = 0; i < n_supported_protocols; i++)
     if (!strcmp(*options, supported_protocols[i]->name))
       /* Remove the first element of 'options' (which is always the
          protocol name) from the list passed to the init method. */
-      return supported_protocols[i]->listener_create(n_options - 1, options + 1);
+      return supported_protocols[i]->config_create(n_options - 1, options + 1);
 
   return NULL;
 }
@@ -42,12 +42,30 @@ proto_listener_create(int n_options, const char *const *options)
    This function destroys the protocol-specific part of a listener object.
 */
 void
-proto_listener_free(listener_t *lsn)
+config_free(config_t *cfg)
 {
-  obfs_assert(lsn);
-  obfs_assert(lsn->vtable);
-  obfs_assert(lsn->vtable->listener_free);
-  lsn->vtable->listener_free(lsn);
+  obfs_assert(cfg);
+  obfs_assert(cfg->vtable);
+  obfs_assert(cfg->vtable->config_free);
+  cfg->vtable->config_free(cfg);
+}
+
+struct evutil_addrinfo *
+config_get_listen_addrs(config_t *cfg, size_t n)
+{
+  obfs_assert(cfg);
+  obfs_assert(cfg->vtable);
+  obfs_assert(cfg->vtable->config_get_listen_addrs);
+  return cfg->vtable->config_get_listen_addrs(cfg, n);
+}
+
+struct evutil_addrinfo *
+config_get_target_addr(config_t *cfg)
+{
+  obfs_assert(cfg);
+  obfs_assert(cfg->vtable);
+  obfs_assert(cfg->vtable->config_get_target_addr);
+  return cfg->vtable->config_get_target_addr(cfg);
 }
 
 /**
@@ -57,12 +75,12 @@ proto_listener_free(listener_t *lsn)
    Return a 'protocol_t' if successful, NULL otherwise.
 */
 conn_t *
-proto_conn_create(listener_t *lsn)
+proto_conn_create(config_t *cfg)
 {
-  obfs_assert(lsn);
-  obfs_assert(lsn->vtable);
-  obfs_assert(lsn->vtable->conn_create);
-  return lsn->vtable->conn_create(lsn);
+  obfs_assert(cfg);
+  obfs_assert(cfg->vtable);
+  obfs_assert(cfg->vtable->conn_create);
+  return cfg->vtable->conn_create(cfg);
 }
 
 /**
@@ -72,9 +90,10 @@ proto_conn_create(listener_t *lsn)
 int
 proto_handshake(conn_t *conn, void *buf) {
   obfs_assert(conn);
-  obfs_assert(conn->vtable);
-  obfs_assert(conn->vtable->handshake);
-  return conn->vtable->handshake(conn, buf);
+  obfs_assert(conn->cfg);
+  obfs_assert(conn->cfg->vtable);
+  obfs_assert(conn->cfg->vtable->handshake);
+  return conn->cfg->vtable->handshake(conn, buf);
 }
 
 /**
@@ -83,9 +102,10 @@ proto_handshake(conn_t *conn, void *buf) {
 int
 proto_send(conn_t *conn, void *source, void *dest) {
   obfs_assert(conn);
-  obfs_assert(conn->vtable);
-  obfs_assert(conn->vtable->send);
-  return conn->vtable->send(conn, source, dest);
+  obfs_assert(conn->cfg);
+  obfs_assert(conn->cfg->vtable);
+  obfs_assert(conn->cfg->vtable->send);
+  return conn->cfg->vtable->send(conn, source, dest);
 }
 
 /**
@@ -94,9 +114,10 @@ proto_send(conn_t *conn, void *source, void *dest) {
 enum recv_ret
 proto_recv(conn_t *conn, void *source, void *dest) {
   obfs_assert(conn);
-  obfs_assert(conn->vtable);
-  obfs_assert(conn->vtable->recv);
-  return conn->vtable->recv(conn, source, dest);
+  obfs_assert(conn->cfg);
+  obfs_assert(conn->cfg->vtable);
+  obfs_assert(conn->cfg->vtable->recv);
+  return conn->cfg->vtable->recv(conn, source, dest);
 }
 
 /**
@@ -106,7 +127,8 @@ proto_recv(conn_t *conn, void *source, void *dest) {
 void
 proto_conn_free(conn_t *conn) {
   obfs_assert(conn);
-  obfs_assert(conn->vtable);
-  obfs_assert(conn->vtable->conn_free);
-  conn->vtable->conn_free(conn);
+  obfs_assert(conn->cfg);
+  obfs_assert(conn->cfg->vtable);
+  obfs_assert(conn->cfg->vtable->conn_free);
+  conn->cfg->vtable->conn_free(conn);
 }
diff --git a/src/protocol.h b/src/protocol.h
index 6dfb04a..989b6db 100644
--- a/src/protocol.h
+++ b/src/protocol.h
@@ -6,6 +6,18 @@
 #define PROTOCOL_H
 
 /**
+   This struct defines a "configuration" of the proxy.
+   A configuration is a set of addresses to listen on, and what to do
+   when connections are received.  Almost all of a configuration is
+   protocol-private data, stored in the larger structure in which this
+   struct is embedded.
+ */
+struct config_t
+{
+  const struct protocol_vtable *vtable;
+};
+
+/**
    This struct defines a protocol and its methods; note that not all
    of them are methods on the same object in the C++ sense.
 
@@ -18,20 +30,29 @@ struct protocol_vtable
   /** The short name of this protocol. */
   const char *name;
 
-  /** Allocate a 'listener_t' object and fill it in from the provided
+  /** Allocate a 'config_t' object and fill it in from the provided
       'options' array. */
-  listener_t *(*listener_create)(int n_options, const char *const *options);
+  config_t *(*config_create)(int n_options, const char *const *options);
+
+  /** Destroy the provided 'config_t' object.  */
+  void (*config_free)(config_t *cfg);
 
-  /** Destroy the provided 'listener_t' object.  This function is
-      responsible for deallocating any data that the protocol's
-      extended structure points to, and deallocating the object
-      itself.  But it is *not* responsible for deallocating the data
-      pointed to by the generic 'listener_t'; that's already been done
-      by generic code.  */
-  void (*listener_free)(listener_t *params);
+  /** Return a set of addresses to listen on, in the form of an
+      'evutil_addrinfo' linked list.  There may be more than one list;
+      users of this function should call it repeatedly with successive
+      values of N, starting from zero, until it returns NULL, and
+      create listeners for every address returned. */
+  struct evutil_addrinfo *(*config_get_listen_addrs)(config_t *cfg, size_t n);
 
-  /** Allocate per-connection, protocol-specific state. */
-  conn_t *(*conn_create)(listener_t *params);
+  /** Return a set of addresses to attempt an outbound connection to,
+      in the form of an 'evutil_addrinfo' linked list.  There is only
+      one such list. */
+  struct evutil_addrinfo *(*config_get_target_addr)(config_t *cfg);
+
+  /** A connection has just been made to one of 'cfg's listener
+      addresses.  Return an extended 'conn_t' object, filling in the
+      'cfg' and 'mode' fields of the generic structure.  */
+  conn_t *(*conn_create)(config_t *cfg);
 
   /** Destroy per-connection, protocol-specific state.  */
   void (*conn_free)(conn_t *state);
@@ -59,17 +80,22 @@ struct protocol_vtable
 #define DEFINE_PROTOCOL_VTABLE(name)            \
   const protocol_vtable name##_vtable = {       \
     #name,                                      \
-    name##_listener_create,                     \
-    name##_listener_free,                       \
+    name##_config_create,                       \
+    name##_config_free,                         \
+    name##_config_get_listen_addrs,             \
+    name##_config_get_target_addr,              \
     name##_conn_create,                         \
     name##_conn_free,                           \
     name##_handshake, name##_send, name##_recv  \
   }
 
-listener_t *proto_listener_create(int n_options, const char *const *options);
-void proto_listener_free(listener_t *lsn);
+config_t *config_create(int n_options, const char *const *options);
+void config_free(config_t *cfg);
+
+struct evutil_addrinfo *config_get_listen_addrs(config_t *cfg, size_t n);
+struct evutil_addrinfo *config_get_target_addr(config_t *cfg);
 
-conn_t *proto_conn_create(listener_t *lsn);
+conn_t *proto_conn_create(config_t *cfg);
 void proto_conn_free(conn_t *conn);
 
 int proto_handshake(conn_t *conn, void *buf);
diff --git a/src/protocols/dummy.c b/src/protocols/dummy.c
index 30b0d16..e55699d 100644
--- a/src/protocols/dummy.c
+++ b/src/protocols/dummy.c
@@ -10,10 +10,10 @@
 #include <event2/buffer.h>
 
 /* type-safe downcast wrappers */
-static inline dummy_listener_t *
-downcast_listener(listener_t *p)
+static inline dummy_config_t *
+downcast_config(config_t *p)
 {
-  return DOWNCAST(dummy_listener_t, super, p);
+  return DOWNCAST(dummy_config_t, super, p);
 }
 
 static inline dummy_conn_t *
@@ -22,51 +22,12 @@ downcast_conn(conn_t *p)
   return DOWNCAST(dummy_conn_t, super, p);
 }
 
-static int parse_and_set_options(int n_options,
-                                 const char *const *options,
-                                 dummy_listener_t *lsn);
-
 /**
-   This function populates 'lsn' according to 'options' and sets up
-   the protocol vtable.
-
-   'options' is an array like this:
-   {"dummy","socks","127.0.0.1:6666"}
-*/
-static listener_t *
-dummy_listener_create(int n_options, const char *const *options)
-{
-  dummy_listener_t *lsn = xzalloc(sizeof(dummy_listener_t));
-  lsn->super.vtable = &dummy_vtable;
-
-  if (parse_and_set_options(n_options, options, lsn) == 0)
-    return &lsn->super;
-
-  if (lsn->super.listen_addr)
-    evutil_freeaddrinfo(lsn->super.listen_addr);
-  if (lsn->super.target_addr)
-    evutil_freeaddrinfo(lsn->super.target_addr);
-  free(lsn);
-
-  log_warn("dummy syntax:\n"
-           "\tdummy <mode> <listen_address> [<target_address>]\n"
-           "\t\tmode ~ server|client|socks\n"
-           "\t\tlisten_address, target_address ~ host:port\n"
-           "\ttarget_address is required for server and client mode,\n"
-           "\tand forbidden for socks mode.\n"
-           "Examples:\n"
-           "\tobfsproxy dummy socks 127.0.0.1:5000\n"
-           "\tobfsproxy dummy client 127.0.0.1:5000 192.168.1.99:11253\n"
-           "\tobfsproxy dummy server 192.168.1.99:11253 127.0.0.1:9005");
-  return NULL;
-}
-
-/**
-   Helper: Parses 'options' and fills 'lsn'.
+   Helper: Parses 'options' and fills 'cfg'.
 */
 static int
 parse_and_set_options(int n_options, const char *const *options,
-                      dummy_listener_t *lsn)
+                      dummy_config_t *cfg)
 {
   const char* defport;
 
@@ -75,36 +36,85 @@ parse_and_set_options(int n_options, const char *const *options,
 
   if (!strcmp(options[0], "client")) {
     defport = "48988"; /* bf5c */
-    lsn->super.mode = LSN_SIMPLE_CLIENT;
+    cfg->mode = LSN_SIMPLE_CLIENT;
   } else if (!strcmp(options[0], "socks")) {
     defport = "23548"; /* 5bf5 */
-    lsn->super.mode = LSN_SOCKS_CLIENT;
+    cfg->mode = LSN_SOCKS_CLIENT;
   } else if (!strcmp(options[0], "server")) {
     defport = "11253"; /* 2bf5 */
-    lsn->super.mode = LSN_SIMPLE_SERVER;
+    cfg->mode = LSN_SIMPLE_SERVER;
   } else
     return -1;
 
-  if (n_options != (lsn->super.mode == LSN_SOCKS_CLIENT ? 2 : 3))
+  if (n_options != (cfg->mode == LSN_SOCKS_CLIENT ? 2 : 3))
       return -1;
 
-  lsn->super.listen_addr = resolve_address_port(options[1], 1, 1, defport);
-  if (!lsn->super.listen_addr)
+  cfg->listen_addr = resolve_address_port(options[1], 1, 1, defport);
+  if (!cfg->listen_addr)
     return -1;
 
-  if (lsn->super.mode != LSN_SOCKS_CLIENT) {
-    lsn->super.target_addr = resolve_address_port(options[2], 1, 0, NULL);
-    if (!lsn->super.target_addr)
+  if (cfg->mode != LSN_SOCKS_CLIENT) {
+    cfg->target_addr = resolve_address_port(options[2], 1, 0, NULL);
+    if (!cfg->target_addr)
       return -1;
   }
 
   return 0;
 }
 
+/* Deallocate 'cfg'. */
 static void
-dummy_listener_free(listener_t *lsn)
+dummy_config_free(config_t *c)
+{
+  dummy_config_t *cfg = downcast_config(c);
+  if (cfg->listen_addr)
+    evutil_freeaddrinfo(cfg->listen_addr);
+  if (cfg->target_addr)
+    evutil_freeaddrinfo(cfg->target_addr);
+  free(cfg);
+}
+
+/**
+   Populate 'cfg' according to 'options', which is an array like this:
+   {"socks","127.0.0.1:6666"}
+*/
+static config_t *
+dummy_config_create(int n_options, const char *const *options)
+{
+  dummy_config_t *cfg = xzalloc(sizeof(dummy_config_t));
+  cfg->super.vtable = &dummy_vtable;
+
+  if (parse_and_set_options(n_options, options, cfg) == 0)
+    return &cfg->super;
+
+  dummy_config_free(&cfg->super);
+  log_warn("dummy syntax:\n"
+           "\tdummy <mode> <listen_address> [<target_address>]\n"
+           "\t\tmode ~ server|client|socks\n"
+           "\t\tlisten_address, target_address ~ host:port\n"
+           "\ttarget_address is required for server and client mode,\n"
+           "\tand forbidden for socks mode.\n"
+           "Examples:\n"
+           "\tobfsproxy dummy socks 127.0.0.1:5000\n"
+           "\tobfsproxy dummy client 127.0.0.1:5000 192.168.1.99:11253\n"
+           "\tobfsproxy dummy server 192.168.1.99:11253 127.0.0.1:9005");
+  return NULL;
+}
+
+/** Retrieve the 'n'th set of listen addresses for this configuration. */
+static struct evutil_addrinfo *
+dummy_config_get_listen_addrs(config_t *cfg, size_t n)
+{
+  if (n > 0)
+    return 0;
+  return downcast_config(cfg)->listen_addr;
+}
+
+/* Retrieve the target address for this configuration. */
+static struct evutil_addrinfo *
+dummy_config_get_target_addr(config_t *cfg)
 {
-  free(downcast_listener(lsn));
+  return downcast_config(cfg)->target_addr;
 }
 
 /*
@@ -113,10 +123,11 @@ dummy_listener_free(listener_t *lsn)
 */
 
 static conn_t *
-dummy_conn_create(listener_t *lsn)
+dummy_conn_create(config_t *cfg)
 {
   dummy_conn_t *proto = xzalloc(sizeof(dummy_conn_t));
-  proto->super.vtable = &dummy_vtable;
+  proto->super.cfg = cfg;
+  proto->super.mode = downcast_config(cfg)->mode;
   return &proto->super;
 }
 
diff --git a/src/protocols/dummy.h b/src/protocols/dummy.h
index e7a450e..6a957bb 100644
--- a/src/protocols/dummy.h
+++ b/src/protocols/dummy.h
@@ -17,14 +17,17 @@ extern const protocol_vtable dummy_vtable;
 #include "../network.h"
 #include "../protocol.h"
 
-/* Dummy presently needs no extensions to the generic protocol
-   structures, but we have shims for future expansion, and also
-   because, if you're using dummy as a template, you probably will
-   want to extend the generic structures. */
-
-typedef struct dummy_listener_t {
-  listener_t super;
-} dummy_listener_t;
+/* Dummy presently needs only the obligatory extensions to the generic
+   protocol structures, but we have shims for future expansion, and
+   also because, if you're using dummy as a template, you probably
+   will want to extend the generic structures. */
+
+typedef struct dummy_config_t {
+  config_t super;
+  struct evutil_addrinfo *listen_addr;
+  struct evutil_addrinfo *target_addr;
+  enum listen_mode mode;
+} dummy_config_t;
 
 typedef struct dummy_conn_t {
   conn_t super;
diff --git a/src/test/unittest_dummy.c b/src/test/unittest_dummy.c
index ec09a82..0eb133c 100644
--- a/src/test/unittest_dummy.c
+++ b/src/test/unittest_dummy.c
@@ -12,7 +12,7 @@ static void
 test_dummy_option_parsing(void *unused)
 {
   struct option_parsing_case {
-    listener_t *result;
+    config_t *result;
     short should_succeed;
     short n_opts;
     const char *const opts[4];
@@ -45,7 +45,7 @@ test_dummy_option_parsing(void *unused)
 
   struct option_parsing_case *c;
   for (c = cases; c->n_opts; c++) {
-    c->result = proto_listener_create(c->n_opts, c->opts);
+    c->result = config_create(c->n_opts, c->opts);
     if (c->should_succeed)
       tt_ptr_op(c->result, !=, NULL);
     else
@@ -55,7 +55,7 @@ test_dummy_option_parsing(void *unused)
  end:
   for (c = cases; c->n_opts; c++)
     if (c->result)
-      proto_listener_free(c->result);
+      config_free(c->result);
 
   /* Unsuspend logging */
   log_set_method(LOG_METHOD_STDERR, NULL);
@@ -64,8 +64,8 @@ test_dummy_option_parsing(void *unused)
 /* All the tests below use this test environment: */
 struct test_dummy_state
 {
-  listener_t *lsn_client;
-  listener_t *lsn_server;
+  config_t *cfg_client;
+  config_t *cfg_server;
   conn_t *conn_client;
   conn_t *conn_server;
   struct evbuffer *output_buffer;
@@ -82,10 +82,10 @@ cleanup_dummy_state(const struct testcase_t *unused, void *state)
   if (s->conn_server)
       proto_conn_free(s->conn_server);
 
-  if (s->lsn_client)
-    proto_listener_free(s->lsn_client);
-  if (s->lsn_server)
-    proto_listener_free(s->lsn_server);
+  if (s->cfg_client)
+    config_free(s->cfg_client);
+  if (s->cfg_server)
+    config_free(s->cfg_server);
 
   if (s->output_buffer)
     evbuffer_free(s->output_buffer);
@@ -109,18 +109,18 @@ setup_dummy_state(const struct testcase_t *unused)
 {
   struct test_dummy_state *s = xzalloc(sizeof(struct test_dummy_state));
 
-  s->lsn_client =
-    proto_listener_create(ALEN(options_client), options_client);
-  tt_assert(s->lsn_client);
+  s->cfg_client =
+    config_create(ALEN(options_client), options_client);
+  tt_assert(s->cfg_client);
 
-  s->lsn_server =
-    proto_listener_create(ALEN(options_server), options_server);
-  tt_assert(s->lsn_server);
+  s->cfg_server =
+    config_create(ALEN(options_server), options_server);
+  tt_assert(s->cfg_server);
 
-  s->conn_client = proto_conn_create(s->lsn_client);
+  s->conn_client = proto_conn_create(s->cfg_client);
   tt_assert(s->conn_client);
 
-  s->conn_server = proto_conn_create(s->lsn_server);
+  s->conn_server = proto_conn_create(s->cfg_server);
   tt_assert(s->conn_server);
 
   s->output_buffer = evbuffer_new();
diff --git a/src/util.h b/src/util.h
index 8b6f3b4..8228790 100644
--- a/src/util.h
+++ b/src/util.h
@@ -62,8 +62,8 @@ unsigned int ui64_log2(uint64_t u64);
 
 /***** Network types and functions. *****/
 
+typedef struct config_t config_t;
 typedef struct conn_t conn_t;
-typedef struct listener_t listener_t;
 typedef struct protocol_vtable protocol_vtable;
 typedef struct socks_state_t socks_state_t;
 





More information about the tor-commits mailing list