[tor-commits] [obfsproxy/master] Break up the listener callback by listener mode, and delay creating the output buffer in socks mode till we know where to connect

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


commit d0cf5ae1f52fc3bf9daa4c3fb754cc77e4b74159
Author: Zack Weinberg <zackw at panix.com>
Date:   Sun Jul 24 18:32:18 2011 -0700

    Break up the listener callback by listener mode, and delay creating the output buffer in socks mode till we know where to connect
---
 src/network.c |  227 +++++++++++++++++++++++++++++++++++++++++++--------------
 src/network.h |    5 +-
 2 files changed, 173 insertions(+), 59 deletions(-)

diff --git a/src/network.c b/src/network.c
index 75d90ef..32d14ee 100644
--- a/src/network.c
+++ b/src/network.c
@@ -40,7 +40,11 @@ static smartlist_t *connections;
     connections and shutdowns when the last connection is closed. */
 static int shutting_down=0;
 
-static void simple_listener_cb(struct evconnlistener *evcl,
+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 conn_free(conn_t *conn);
@@ -108,23 +112,29 @@ close_all_connections(void)
 */
 listener_t *
 listener_new(struct event_base *base,
-             protocol_params_t *proto_params)
+             protocol_params_t *params)
 {
   const unsigned flags =
     LEV_OPT_CLOSE_ON_FREE|LEV_OPT_CLOSE_ON_EXEC|LEV_OPT_REUSEABLE;
-
+  evconnlistener_cb callback;
   listener_t *lsn = xzalloc(sizeof(listener_t));
 
-  lsn->proto_params = proto_params;
+  switch (params->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->proto_params = params;
   lsn->listener =
-    evconnlistener_new_bind(base, simple_listener_cb, lsn, flags, -1,
-                            proto_params->listen_addr->ai_addr,
-                            proto_params->listen_addr->ai_addrlen);
+    evconnlistener_new_bind(base, callback, lsn, flags, -1,
+                            params->listen_addr->ai_addr,
+                            params->listen_addr->ai_addrlen);
 
   if (!lsn->listener) {
     log_warn("Failed to create listener!");
-    proto_params_free(lsn->proto_params);
+    proto_params_free(params);
     free(lsn);
     return NULL;
   }
@@ -167,23 +177,22 @@ free_all_listeners(void)
 }
 
 /**
-   This function is called when a new connection is received.
-
-   It initializes the protocol we are using, sets up the necessary
-   callbacks for input/output and does the protocol handshake.
+   This function is called when an upstream client connects to us in
+   simple client mode.
 */
 static void
-simple_listener_cb(struct evconnlistener *evcl,
-                   evutil_socket_t fd, struct sockaddr *sourceaddr,
-                   int socklen, void *arg)
+simple_client_listener_cb(struct evconnlistener *evcl,
+                          evutil_socket_t fd, struct sockaddr *sourceaddr,
+                          int socklen, void *arg)
 {
   listener_t *lsn = arg;
   struct event_base *base;
   conn_t *conn = xzalloc(sizeof(conn_t));
 
-  log_debug("Got a connection attempt.");
+  log_debug("%s: connection attempt.", __func__);
 
   conn->mode = lsn->proto_params->mode;
+  obfs_assert(conn->mode == LSN_SIMPLE_CLIENT);
 
   conn->proto = proto_create(lsn->proto_params);
   if (!conn->proto) {
@@ -191,67 +200,161 @@ simple_listener_cb(struct evconnlistener *evcl,
     goto err;
   }
 
-  if (conn->mode == LSN_SOCKS_CLIENT) {
-    /* Construct SOCKS state. */
-    conn->socks_state = socks_state_new();
+  /* New bufferevent to wrap socket we received. */
+  base = evconnlistener_get_base(lsn->listener);
+  conn->input = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
+  if (!conn->input)
+    goto err;
+  fd = -1; /* prevent double-close */
+
+  /* New bufferevent to connect to the target address */
+  conn->output = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE);
+  if (!conn->output)
+    goto err;
+
+  bufferevent_setcb(conn->input, plaintext_read_cb, NULL, input_event_cb, conn);
+  bufferevent_enable(conn->input, EV_READ|EV_WRITE);
+
+  bufferevent_setcb(conn->output,
+                    obfuscated_read_cb, NULL, output_event_cb, conn);
+
+  /* Queue output right now. */
+  if (proto_handshake(conn->proto, bufferevent_get_output(conn->output)) < 0)
+    goto err;
+
+  /* Launch the connect attempt. */
+  if (bufferevent_socket_connect(conn->output,
+                                 lsn->proto_params->target_addr->ai_addr,
+                                 lsn->proto_params->target_addr->ai_addrlen)<0)
+    goto err;
+
+  bufferevent_enable(conn->output, 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;
+
+ err:
+  if (conn)
+    conn_free(conn);
+  if (fd >= 0)
+    evutil_closesocket(fd);
+}
+
+/**
+   This function is called when an upstream client connects to us in
+   socks mode.
+*/
+static void
+socks_client_listener_cb(struct evconnlistener *evcl,
+                         evutil_socket_t fd, struct sockaddr *sourceaddr,
+                         int socklen, void *arg)
+{
+  listener_t *lsn = arg;
+  struct event_base *base;
+  conn_t *conn = xzalloc(sizeof(conn_t));
+
+  log_debug("%s: connection attempt.", __func__);
+
+  conn->mode = lsn->proto_params->mode;
+  obfs_assert(conn->mode == LSN_SOCKS_CLIENT);
+
+  conn->proto = proto_create(lsn->proto_params);
+  if (!conn->proto) {
+    log_warn("Creation of protocol object failed! Closing connection.");
+    goto err;
   }
 
+  /* Construct SOCKS state. */
+  conn->socks_state = socks_state_new();
+
   /* New bufferevent to wrap socket we received. */
   base = evconnlistener_get_base(lsn->listener);
-  conn->input = bufferevent_socket_new(base,
-                                       fd,
-                                       BEV_OPT_CLOSE_ON_FREE);
+  conn->input = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
   if (!conn->input)
     goto err;
   fd = -1; /* prevent double-close */
 
-  if (conn->mode == LSN_SIMPLE_SERVER) {
-    bufferevent_setcb(conn->input,
-                      obfuscated_read_cb, NULL, input_event_cb, conn);
-  } else if (conn->mode == LSN_SIMPLE_CLIENT) {
-    bufferevent_setcb(conn->input,
-                      plaintext_read_cb, NULL, input_event_cb, conn);
-  } else {
-    obfs_assert(conn->mode == LSN_SOCKS_CLIENT);
-    bufferevent_setcb(conn->input,
-                      socks_read_cb, NULL, input_event_cb, conn);
+  bufferevent_setcb(conn->input, socks_read_cb, NULL, input_event_cb, conn);
+  bufferevent_enable(conn->input, EV_READ|EV_WRITE);
+
+  /* Do not create a target bufferevent at this time; the socks
+     handler will do it after we know where we're connecting */
+
+  /* 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);
+}
+
+/**
+   This function is called when a remote client connects to us in
+   server mode.
+*/
+static void
+simple_server_listener_cb(struct evconnlistener *evcl,
+                          evutil_socket_t fd, struct sockaddr *sourceaddr,
+                          int socklen, void *arg)
+{
+  listener_t *lsn = arg;
+  struct event_base *base;
+  conn_t *conn = xzalloc(sizeof(conn_t));
+
+  log_debug("%s: connection attempt.", __func__);
+
+  conn->mode = lsn->proto_params->mode;
+  obfs_assert(conn->mode == LSN_SIMPLE_SERVER);
+
+  conn->proto = proto_create(lsn->proto_params);
+  if (!conn->proto) {
+    log_warn("Creation of protocol object failed! Closing connection.");
+    goto err;
   }
 
+  /* New bufferevent to wrap socket we received. */
+  base = evconnlistener_get_base(lsn->listener);
+  conn->input = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
+  if (!conn->input)
+    goto err;
+  fd = -1; /* prevent double-close */
+
+  bufferevent_setcb(conn->input, obfuscated_read_cb, NULL, input_event_cb, conn);
   bufferevent_enable(conn->input, EV_READ|EV_WRITE);
 
   /* New bufferevent to connect to the target address */
-  conn->output = bufferevent_socket_new(base,
-                                        -1,
-                                        BEV_OPT_CLOSE_ON_FREE);
+  conn->output = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE);
   if (!conn->output)
     goto err;
 
-  if (conn->mode == LSN_SIMPLE_SERVER)
-    bufferevent_setcb(conn->output,
-                      plaintext_read_cb, NULL, output_event_cb, conn);
-  else
-    bufferevent_setcb(conn->output,
-                      obfuscated_read_cb, NULL, output_event_cb, conn);
-
-  /* Queue output right now. */
-  struct bufferevent *encrypted =
-    conn->mode == LSN_SIMPLE_SERVER ? conn->input : conn->output;
+  bufferevent_setcb(conn->output, plaintext_read_cb, NULL,
+                    output_event_cb, conn);
 
-  /* ASN Will all protocols need to handshake here? Don't think so. */
+  /* Queue handshake, if any, before connecting. */
   if (proto_handshake(conn->proto,
-                      bufferevent_get_output(encrypted))<0)
+                      bufferevent_get_output(conn->input))<0)
     goto err;
 
-  if (conn->mode == LSN_SIMPLE_SERVER || conn->mode == LSN_SIMPLE_CLIENT) {
-    /* Launch the connect attempt. */
-    if (bufferevent_socket_connect(conn->output,
-                                   lsn->proto_params->target_addr->ai_addr,
-                                   lsn->proto_params->target_addr->ai_addrlen)
-        < 0)
-      goto err;
+  if (bufferevent_socket_connect(conn->output,
+                                 lsn->proto_params->target_addr->ai_addr,
+                                 lsn->proto_params->target_addr->ai_addrlen)<0)
+    goto err;
 
-    bufferevent_enable(conn->output, EV_READ|EV_WRITE);
-  }
+  bufferevent_enable(conn->output, EV_READ|EV_WRITE);
 
   /* add conn to the connection list */
   if (!connections)
@@ -346,6 +449,18 @@ socks_read_cb(struct bufferevent *bev, void *arg)
       const char *addr=NULL;
       r = socks_state_get_address(conn->socks_state, &af, &addr, &port);
       obfs_assert(r==0);
+      conn->output = bufferevent_socket_new(bufferevent_get_base(conn->input),
+                                            -1,
+                                            BEV_OPT_CLOSE_ON_FREE);
+
+      /* queue handshake, if any, before connecting */
+      if (proto_handshake(conn->proto,
+                          bufferevent_get_output(conn->output))<0) {
+        /* XXXX send socks reply */
+        close_conn(conn);
+        return;
+      }
+
       r = bufferevent_socket_connect_hostname(conn->output,
                                               get_evdns_base(),
                                               af, addr, port);
diff --git a/src/network.h b/src/network.h
index 7ba9afc..49580c3 100644
--- a/src/network.h
+++ b/src/network.h
@@ -43,12 +43,11 @@ struct socks_state_t;
 struct protocol_t;
 
 typedef struct conn_t {
+  struct protocol_t *proto;
   struct socks_state_t *socks_state;
-  struct protocol_t *proto; /* ASN Do we like this here? We probably don't.
-                               But it's so convenient!! So convenient! */
-  int mode;
   struct bufferevent *input;
   struct bufferevent *output;
+  unsigned int mode : 30;
   unsigned int flushing : 1;
   unsigned int is_open : 1;
 } conn_t;





More information about the tor-commits mailing list