Browse Source

Merge branch 'bug5040_4773_rebase_3'

Nick Mathewson 10 years ago
parent
commit
74262f1571

+ 4 - 0
changes/bug5040

@@ -0,0 +1,4 @@
+  o Minor features:
+    - Bridges now track the usage of their pluggable transports and
+      report statistics in their extra-info descriptors. Resolves
+      ticket 5040.

+ 2 - 2
src/common/crypto.c

@@ -2404,8 +2404,8 @@ crypto_seed_rng(int startup)
 /** Write <b>n</b> bytes of strong random data to <b>to</b>. Return 0 on
  * success, -1 on failure.
  */
-int
-crypto_rand(char *to, size_t n)
+MOCK_IMPL(int,
+crypto_rand, (char *to, size_t n))
 {
   int r;
   tor_assert(n < INT_MAX);

+ 1 - 1
src/common/crypto.h

@@ -248,7 +248,7 @@ int crypto_expand_key_material_rfc5869_sha256(
 
 /* random numbers */
 int crypto_seed_rng(int startup);
-int crypto_rand(char *to, size_t n);
+MOCK_DECL(int,crypto_rand,(char *to, size_t n));
 int crypto_strongest_rand(uint8_t *out, size_t out_len);
 int crypto_rand_int(unsigned int max);
 uint64_t crypto_rand_uint64(uint64_t max);

+ 3 - 3
src/common/util.c

@@ -2216,9 +2216,9 @@ write_bytes_to_file_impl(const char *fname, const char *str, size_t len,
 
 /** As write_str_to_file, but does not assume a NUL-terminated
  * string. Instead, we write <b>len</b> bytes, starting at <b>str</b>. */
-int
-write_bytes_to_file(const char *fname, const char *str, size_t len,
-                    int bin)
+MOCK_IMPL(int,
+write_bytes_to_file,(const char *fname, const char *str, size_t len,
+                     int bin))
 {
   return write_bytes_to_file_impl(fname, str, len,
                                   OPEN_FLAGS_REPLACE|(bin?O_BINARY:O_TEXT));

+ 3 - 2
src/common/util.h

@@ -355,8 +355,9 @@ FILE *fdopen_file(open_file_t *file_data);
 int finish_writing_to_file(open_file_t *file_data);
 int abort_writing_to_file(open_file_t *file_data);
 int write_str_to_file(const char *fname, const char *str, int bin);
-int write_bytes_to_file(const char *fname, const char *str, size_t len,
-                        int bin);
+MOCK_DECL(int,
+write_bytes_to_file,(const char *fname, const char *str, size_t len,
+                     int bin));
 /** An ad-hoc type to hold a string of characters and a count; used by
  * write_chunks_to_file. */
 typedef struct sized_chunk_t {

+ 59 - 0
src/or/buffers.c

@@ -19,6 +19,7 @@
 #include "connection_or.h"
 #include "control.h"
 #include "reasons.h"
+#include "ext_orport.h"
 #include "../common/util.h"
 #include "../common/torlog.h"
 #ifdef HAVE_UNISTD_H
@@ -1702,6 +1703,64 @@ fetch_from_evbuffer_socks(struct evbuffer *buf, socks_request_t *req,
 }
 #endif
 
+/** The size of the header of an Extended ORPort message: 2 bytes for
+ *  COMMAND, 2 bytes for BODYLEN */
+#define EXT_OR_CMD_HEADER_SIZE 4
+
+/** Read <b>buf</b>, which should contain an Extended ORPort message
+ *  from a transport proxy. If well-formed, create and populate
+ *  <b>out</b> with the Extended ORport message. Return 0 if the
+ *  buffer was incomplete, 1 if it was well-formed and -1 if we
+ *  encountered an error while parsing it.  */
+int
+fetch_ext_or_command_from_buf(buf_t *buf, ext_or_cmd_t **out)
+{
+  char hdr[EXT_OR_CMD_HEADER_SIZE];
+  uint16_t len;
+
+  check();
+  if (buf->datalen < EXT_OR_CMD_HEADER_SIZE)
+    return 0;
+  peek_from_buf(hdr, sizeof(hdr), buf);
+  len = ntohs(get_uint16(hdr+2));
+  if (buf->datalen < (unsigned)len + EXT_OR_CMD_HEADER_SIZE)
+    return 0;
+  *out = ext_or_cmd_new(len);
+  (*out)->cmd = ntohs(get_uint16(hdr));
+  (*out)->len = len;
+  buf_remove_from_front(buf, EXT_OR_CMD_HEADER_SIZE);
+  fetch_from_buf((*out)->body, len, buf);
+  return 1;
+}
+
+#ifdef USE_BUFFEREVENTS
+/** Read <b>buf</b>, which should contain an Extended ORPort message
+ *  from a transport proxy. If well-formed, create and populate
+ *  <b>out</b> with the Extended ORport message. Return 0 if the
+ *  buffer was incomplete, 1 if it was well-formed and -1 if we
+ *  encountered an error while parsing it.  */
+int
+fetch_ext_or_command_from_evbuffer(struct evbuffer *buf, ext_or_cmd_t **out)
+{
+  char hdr[EXT_OR_CMD_HEADER_SIZE];
+  uint16_t len;
+  size_t buf_len = evbuffer_get_length(buf);
+
+  if (buf_len < EXT_OR_CMD_HEADER_SIZE)
+    return 0;
+  evbuffer_copyout(buf, hdr, EXT_OR_CMD_HEADER_SIZE);
+  len = ntohs(get_uint16(hdr+2));
+  if (buf_len < (unsigned)len + EXT_OR_CMD_HEADER_SIZE)
+    return 0;
+  *out = ext_or_cmd_new(len);
+  (*out)->cmd = ntohs(get_uint16(hdr));
+  (*out)->len = len;
+  evbuffer_drain(buf, EXT_OR_CMD_HEADER_SIZE);
+  evbuffer_remove(buf, (*out)->body, len);
+  return 1;
+}
+#endif
+
 /** Implementation helper to implement fetch_from_*_socks.  Instead of looking
  * at a buffer's contents, we look at the <b>datalen</b> bytes of data in
  * <b>data</b>. Instead of removing data from the buffer, we set

+ 8 - 0
src/or/buffers.h

@@ -53,6 +53,8 @@ int fetch_from_buf_line(buf_t *buf, char *data_out, size_t *data_len);
 
 int peek_buf_has_control0_command(buf_t *buf);
 
+int fetch_ext_or_command_from_buf(buf_t *buf, ext_or_cmd_t **out);
+
 #ifdef USE_BUFFEREVENTS
 int fetch_var_cell_from_evbuffer(struct evbuffer *buf, var_cell_t **out,
                                  int linkproto);
@@ -68,6 +70,8 @@ int peek_evbuffer_has_control0_command(struct evbuffer *buf);
 int write_to_evbuffer_zlib(struct evbuffer *buf, tor_zlib_state_t *state,
                            const char *data, size_t data_len,
                            int done);
+int fetch_ext_or_command_from_evbuffer(struct evbuffer *buf,
+                                       ext_or_cmd_t **out);
 #endif
 
 #ifdef USE_BUFFEREVENTS
@@ -77,6 +81,8 @@ int write_to_evbuffer_zlib(struct evbuffer *buf, tor_zlib_state_t *state,
 #define generic_buffer_get(b,buf,buflen) evbuffer_remove((b),(buf),(buflen))
 #define generic_buffer_clear(b) evbuffer_drain((b), evbuffer_get_length((b)))
 #define generic_buffer_free(b) evbuffer_free((b))
+#define generic_buffer_fetch_ext_or_cmd(b, out) \
+  fetch_ext_or_command_from_evbuffer((b), (out))
 #else
 #define generic_buffer_new() buf_new()
 #define generic_buffer_len(b) buf_datalen((b))
@@ -84,6 +90,8 @@ int write_to_evbuffer_zlib(struct evbuffer *buf, tor_zlib_state_t *state,
 #define generic_buffer_get(b,buf,buflen) fetch_from_buf((buf),(buflen),(b))
 #define generic_buffer_clear(b) buf_clear((b))
 #define generic_buffer_free(b) buf_free((b))
+#define generic_buffer_fetch_ext_or_cmd(b, out) \
+  fetch_ext_or_command_from_buf((b), (out))
 #endif
 int generic_buffer_set_to_copy(generic_buffer_t **output,
                                const generic_buffer_t *input);

+ 7 - 1
src/or/channel.c

@@ -2379,8 +2379,14 @@ channel_do_open_actions(channel_t *chan)
     /* only report it to the geoip module if it's not a known router */
     if (!router_get_by_id_digest(chan->identity_digest)) {
       if (channel_get_addr_if_possible(chan, &remote_addr)) {
-        geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &remote_addr,
+        char *transport_name = NULL;
+        if (chan->get_transport_name(chan, &transport_name) < 0)
+          transport_name = NULL;
+
+        geoip_note_client_seen(GEOIP_CLIENT_CONNECT,
+                               &remote_addr, transport_name,
                                now);
+        tor_free(transport_name);
       }
       /* Otherwise the underlying transport can't tell us this, so skip it */
     }

+ 2 - 0
src/or/channel.h

@@ -84,6 +84,8 @@ struct channel_s {
    * available.
    */
   int (*get_remote_addr)(channel_t *, tor_addr_t *);
+  int (*get_transport_name)(channel_t *chan, char **transport_out);
+
 #define GRD_FLAG_ORIGINAL 1
 #define GRD_FLAG_ADDR_ONLY 2
   /*

+ 27 - 0
src/or/channeltls.c

@@ -55,6 +55,8 @@ static void channel_tls_close_method(channel_t *chan);
 static const char * channel_tls_describe_transport_method(channel_t *chan);
 static int
 channel_tls_get_remote_addr_method(channel_t *chan, tor_addr_t *addr_out);
+static int
+channel_tls_get_transport_name_method(channel_t *chan, char **transport_out);
 static const char *
 channel_tls_get_remote_descr_method(channel_t *chan, int flags);
 static int channel_tls_has_queued_writes_method(channel_t *chan);
@@ -114,6 +116,7 @@ channel_tls_common_init(channel_tls_t *tlschan)
   chan->describe_transport = channel_tls_describe_transport_method;
   chan->get_remote_addr = channel_tls_get_remote_addr_method;
   chan->get_remote_descr = channel_tls_get_remote_descr_method;
+  chan->get_transport_name = channel_tls_get_transport_name_method;
   chan->has_queued_writes = channel_tls_has_queued_writes_method;
   chan->is_canonical = channel_tls_is_canonical_method;
   chan->matches_extend_info = channel_tls_matches_extend_info_method;
@@ -405,6 +408,30 @@ channel_tls_get_remote_addr_method(channel_t *chan, tor_addr_t *addr_out)
   return 1;
 }
 
+/**
+ * Get the name of the pluggable transport used by a channel_tls_t.
+ *
+ * This implements the get_transport_name for channel_tls_t. If the
+ * channel uses a pluggable transport, copy its name to
+ * <b>transport_out</b> and return 0. If the channel did not use a
+ * pluggable transport, return -1. */
+
+static int
+channel_tls_get_transport_name_method(channel_t *chan, char **transport_out)
+{
+  channel_tls_t *tlschan = BASE_CHAN_TO_TLS(chan);
+
+  tor_assert(tlschan);
+  tor_assert(transport_out);
+  tor_assert(tlschan->conn);
+
+  if (!tlschan->conn->ext_or_transport)
+    return -1;
+
+  *transport_out = tor_strdup(tlschan->conn->ext_or_transport);
+  return 0;
+}
+
 /**
  * Get endpoint description of a channel_tls_t
  *

+ 101 - 2
src/or/config.c

@@ -45,6 +45,7 @@
 #include "routerset.h"
 #include "statefile.h"
 #include "transports.h"
+#include "ext_orport.h"
 #ifdef _WIN32
 #include <shlobj.h>
 #endif
@@ -230,6 +231,7 @@ static config_var_t option_vars_[] = {
   V(ExitPolicyRejectPrivate,     BOOL,     "1"),
   V(ExitPortStatistics,          BOOL,     "0"),
   V(ExtendAllowPrivateAddresses, BOOL,     "0"),
+  VPORT(ExtORPort,               LINELIST, NULL),
   V(ExtraInfoStatistics,         BOOL,     "1"),
   V(FallbackDir,                 LINELIST, NULL),
 
@@ -1473,8 +1475,14 @@ options_act(const or_options_t *old_options)
     return -1;
   }
 
-  if (init_cookie_authentication(options->CookieAuthentication) < 0) {
-    log_warn(LD_CONFIG,"Error creating cookie authentication file.");
+  if (init_control_cookie_authentication(options->CookieAuthentication) < 0) {
+    log_warn(LD_CONFIG,"Error creating control cookie authentication file.");
+    return -1;
+  }
+
+  /* If we have an ExtORPort, initialize its auth cookie. */
+  if (init_ext_or_cookie_authentication(!!options->ExtORPort_lines) < 0) {
+    log_warn(LD_CONFIG,"Error creating Extended ORPort cookie file.");
     return -1;
   }
 
@@ -5093,6 +5101,27 @@ warn_nonlocal_client_ports(const smartlist_t *ports, const char *portname,
   } SMARTLIST_FOREACH_END(port);
 }
 
+/** Warn for every Extended ORPort port in <b>ports</b> that is on a
+ *  publicly routable address. */
+static void
+warn_nonlocal_ext_orports(const smartlist_t *ports, const char *portname)
+{
+  SMARTLIST_FOREACH_BEGIN(ports, const port_cfg_t *, port) {
+    if (port->type != CONN_TYPE_EXT_OR_LISTENER)
+      continue;
+    if (port->is_unix_addr)
+      continue;
+    /* XXX maybe warn even if address is RFC1918? */
+    if (!tor_addr_is_internal(&port->addr, 1)) {
+      log_warn(LD_CONFIG, "You specified a public address '%s' for %sPort. "
+               "This is not advised; this address is supposed to only be "
+               "exposed on localhost so that your pluggable transport "
+               "proxies can connect to it.",
+               fmt_addrport(&port->addr, port->port), portname);
+    }
+  } SMARTLIST_FOREACH_END(port);
+}
+
 /** Given a list of port_cfg_t in <b>ports</b>, warn any controller port there
  * is listening on any non-loopback address.  If <b>forbid</b> is true,
  * then emit a stronger warning and remove the port from the list.
@@ -5193,6 +5222,7 @@ parse_port_config(smartlist_t *out,
   smartlist_t *elts;
   int retval = -1;
   const unsigned is_control = (listener_type == CONN_TYPE_CONTROL_LISTENER);
+  const unsigned is_ext_orport = (listener_type == CONN_TYPE_EXT_OR_LISTENER);
   const unsigned allow_no_options = flags & CL_PORT_NO_OPTIONS;
   const unsigned use_server_options = flags & CL_PORT_SERVER_OPTIONS;
   const unsigned warn_nonlocal = flags & CL_PORT_WARN_NONLOCAL;
@@ -5270,6 +5300,8 @@ parse_port_config(smartlist_t *out,
     if (warn_nonlocal && out) {
       if (is_control)
         warn_nonlocal_controller_ports(out, forbid_nonlocal);
+      else if (is_ext_orport)
+        warn_nonlocal_ext_orports(out, portname);
       else
         warn_nonlocal_client_ports(out, portname, listener_type);
     }
@@ -5543,6 +5575,8 @@ parse_port_config(smartlist_t *out,
   if (warn_nonlocal && out) {
     if (is_control)
       warn_nonlocal_controller_ports(out, forbid_nonlocal);
+    else if (is_ext_orport)
+      warn_nonlocal_ext_orports(out, portname);
     else
       warn_nonlocal_client_ports(out, portname, listener_type);
   }
@@ -5688,6 +5722,14 @@ parse_ports(or_options_t *options, int validate_only,
       *msg = tor_strdup("Invalid ORPort/ORListenAddress configuration");
       goto err;
     }
+    if (parse_port_config(ports,
+                          options->ExtORPort_lines, NULL,
+                          "ExtOR", CONN_TYPE_EXT_OR_LISTENER,
+                          "127.0.0.1", 0,
+                          CL_PORT_SERVER_OPTIONS|CL_PORT_WARN_NONLOCAL) < 0) {
+      *msg = tor_strdup("Invalid ExtORPort configuration");
+      goto err;
+    }
     if (parse_port_config(ports,
                           options->DirPort_lines, options->DirListenAddress,
                           "Dir", CONN_TYPE_DIR_LISTENER,
@@ -5723,6 +5765,8 @@ parse_ports(or_options_t *options, int validate_only,
     !! count_real_listeners(ports, CONN_TYPE_DIR_LISTENER);
   options->DNSPort_set =
     !! count_real_listeners(ports, CONN_TYPE_AP_DNS_LISTENER);
+  options->ExtORPort_set =
+    !! count_real_listeners(ports, CONN_TYPE_EXT_OR_LISTENER);
 
   if (!validate_only) {
     if (configured_ports) {
@@ -6421,3 +6465,58 @@ config_maybe_load_geoip_files_(const or_options_t *options,
     config_load_geoip_file_(AF_INET6, options->GeoIPv6File, "geoip6");
 }
 
+/** Initialize cookie authentication (used so far by the ControlPort
+ *  and Extended ORPort).
+ *
+ *  Allocate memory and create a cookie (of length <b>cookie_len</b>)
+ *  in <b>cookie_out</b>.
+ *  Then write it down to <b>fname</b> and prepend it with <b>header</b>.
+ *
+ *  If the whole procedure was successful, set
+ *  <b>cookie_is_set_out</b> to True. */
+int
+init_cookie_authentication(const char *fname, const char *header,
+                           int cookie_len,
+                           uint8_t **cookie_out, int *cookie_is_set_out)
+{
+  char cookie_file_str_len = strlen(header) + cookie_len;
+  char *cookie_file_str = tor_malloc(cookie_file_str_len);
+  int retval = -1;
+
+  /* We don't want to generate a new cookie every time we call
+   * options_act(). One should be enough. */
+  if (*cookie_is_set_out) {
+    retval = 0; /* we are all set */
+    goto done;
+  }
+
+  /* If we've already set the cookie, free it before re-setting
+     it. This can happen if we previously generated a cookie, but
+     couldn't write it to a disk. */
+  if (*cookie_out)
+    tor_free(*cookie_out);
+
+  /* Generate the cookie */
+  *cookie_out = tor_malloc(cookie_len);
+  if (crypto_rand((char *)*cookie_out, cookie_len) < 0)
+    goto done;
+
+  /* Create the string that should be written on the file. */
+  memcpy(cookie_file_str, header, strlen(header));
+  memcpy(cookie_file_str+strlen(header), *cookie_out, cookie_len);
+  if (write_bytes_to_file(fname, cookie_file_str, cookie_file_str_len, 1)) {
+    log_warn(LD_FS,"Error writing auth cookie to %s.", escaped(fname));
+    goto done;
+  }
+
+  /* Success! */
+  log_info(LD_GENERAL, "Generated auth cookie file in '%s'.", escaped(fname));
+  *cookie_is_set_out = 1;
+  retval = 0;
+
+ done:
+  memwipe(cookie_file_str, 0, cookie_file_str_len);
+  tor_free(cookie_file_str);
+  return retval;
+}
+

+ 4 - 0
src/or/config.h

@@ -90,6 +90,10 @@ uint32_t get_effective_bwburst(const or_options_t *options);
 
 char *get_transport_bindaddr_from_config(const char *transport);
 
+int init_cookie_authentication(const char *fname, const char *header,
+                               int cookie_len,
+                               uint8_t **cookie_out, int *cookie_is_set_out);
+
 or_options_t *options_new(void);
 
 void config_register_addressmaps(const or_options_t *options);

+ 58 - 7
src/or/connection.c

@@ -10,6 +10,7 @@
  * on connections.
  **/
 
+#define CONNECTION_PRIVATE
 #include "or.h"
 #include "buffers.h"
 /*
@@ -33,6 +34,7 @@
 #include "dns.h"
 #include "dnsserv.h"
 #include "entrynodes.h"
+#include "ext_orport.h"
 #include "geoip.h"
 #include "main.h"
 #include "policies.h"
@@ -98,6 +100,7 @@ static smartlist_t *outgoing_addrs = NULL;
 
 #define CASE_ANY_LISTENER_TYPE \
     case CONN_TYPE_OR_LISTENER: \
+    case CONN_TYPE_EXT_OR_LISTENER: \
     case CONN_TYPE_AP_LISTENER: \
     case CONN_TYPE_DIR_LISTENER: \
     case CONN_TYPE_CONTROL_LISTENER: \
@@ -129,6 +132,8 @@ conn_type_to_string(int type)
     case CONN_TYPE_CPUWORKER: return "CPU worker";
     case CONN_TYPE_CONTROL_LISTENER: return "Control listener";
     case CONN_TYPE_CONTROL: return "Control";
+    case CONN_TYPE_EXT_OR: return "Extended OR";
+    case CONN_TYPE_EXT_OR_LISTENER: return "Extended OR listener";
     default:
       log_warn(LD_BUG, "unknown connection type %d", type);
       tor_snprintf(buf, sizeof(buf), "unknown [%d]", type);
@@ -165,6 +170,18 @@ conn_state_to_string(int type, int state)
         case OR_CONN_STATE_OPEN: return "open";
       }
       break;
+    case CONN_TYPE_EXT_OR:
+      switch (state) {
+        case EXT_OR_CONN_STATE_AUTH_WAIT_AUTH_TYPE:
+          return "waiting for authentication type";
+        case EXT_OR_CONN_STATE_AUTH_WAIT_CLIENT_NONCE:
+          return "waiting for client nonce";
+        case EXT_OR_CONN_STATE_AUTH_WAIT_CLIENT_HASH:
+          return "waiting for client hash";
+        case EXT_OR_CONN_STATE_OPEN: return "open";
+        case EXT_OR_CONN_STATE_FLUSHING: return "flushing final OKAY";
+      }
+      break;
     case CONN_TYPE_EXIT:
       switch (state) {
         case EXIT_CONN_STATE_RESOLVING: return "waiting for dest info";
@@ -229,6 +246,7 @@ connection_type_uses_bufferevent(connection_t *conn)
     case CONN_TYPE_DIR:
     case CONN_TYPE_CONTROL:
     case CONN_TYPE_OR:
+    case CONN_TYPE_EXT_OR:
     case CONN_TYPE_CPUWORKER:
       return 1;
     default:
@@ -259,14 +277,18 @@ dir_connection_new(int socket_family)
  * Set active_circuit_pqueue_last_recalibrated to current cell_ewma tick.
  */
 or_connection_t *
-or_connection_new(int socket_family)
+or_connection_new(int type, int socket_family)
 {
   or_connection_t *or_conn = tor_malloc_zero(sizeof(or_connection_t));
   time_t now = time(NULL);
-  connection_init(now, TO_CONN(or_conn), CONN_TYPE_OR, socket_family);
+  tor_assert(type == CONN_TYPE_OR || type == CONN_TYPE_EXT_OR);
+  connection_init(now, TO_CONN(or_conn), type, socket_family);
 
   or_conn->timestamp_last_added_nonpadding = time(NULL);
 
+  if (type == CONN_TYPE_EXT_OR)
+    connection_or_set_ext_or_identifier(or_conn);
+
   return or_conn;
 }
 
@@ -335,7 +357,8 @@ connection_new(int type, int socket_family)
 {
   switch (type) {
     case CONN_TYPE_OR:
-      return TO_CONN(or_connection_new(socket_family));
+    case CONN_TYPE_EXT_OR:
+      return TO_CONN(or_connection_new(type, socket_family));
 
     case CONN_TYPE_EXIT:
       return TO_CONN(edge_connection_new(type, socket_family));
@@ -377,6 +400,7 @@ connection_init(time_t now, connection_t *conn, int type, int socket_family)
 
   switch (type) {
     case CONN_TYPE_OR:
+    case CONN_TYPE_EXT_OR:
       conn->magic = OR_CONNECTION_MAGIC;
       break;
     case CONN_TYPE_EXIT:
@@ -435,7 +459,7 @@ connection_link_connections(connection_t *conn_a, connection_t *conn_b)
  * necessary, close its socket if necessary, and mark the directory as dirty
  * if <b>conn</b> is an OR or OP connection.
  */
-static void
+STATIC void
 connection_free_(connection_t *conn)
 {
   void *mem;
@@ -445,6 +469,7 @@ connection_free_(connection_t *conn)
 
   switch (conn->type) {
     case CONN_TYPE_OR:
+    case CONN_TYPE_EXT_OR:
       tor_assert(conn->magic == OR_CONNECTION_MAGIC);
       mem = TO_OR_CONN(conn);
       memlen = sizeof(or_connection_t);
@@ -575,6 +600,13 @@ connection_free_(connection_t *conn)
     log_warn(LD_BUG, "called on OR conn with non-zeroed identity_digest");
     connection_or_remove_from_identity_map(TO_OR_CONN(conn));
   }
+  if (conn->type == CONN_TYPE_OR || conn->type == CONN_TYPE_EXT_OR) {
+    connection_or_remove_from_ext_or_id_map(TO_OR_CONN(conn));
+    tor_free(TO_OR_CONN(conn)->ext_or_conn_id);
+    tor_free(TO_OR_CONN(conn)->ext_or_auth_correct_client_hash);
+    tor_free(TO_OR_CONN(conn)->ext_or_transport);
+  }
+
 #ifdef USE_BUFFEREVENTS
   if (conn->type == CONN_TYPE_OR && TO_OR_CONN(conn)->bucket_cfg) {
     ev_token_bucket_cfg_free(TO_OR_CONN(conn)->bucket_cfg);
@@ -638,6 +670,7 @@ connection_about_to_close_connection(connection_t *conn)
       connection_dir_about_to_close(TO_DIR_CONN(conn));
       break;
     case CONN_TYPE_OR:
+    case CONN_TYPE_EXT_OR:
       connection_or_about_to_close(TO_OR_CONN(conn));
       break;
     case CONN_TYPE_AP:
@@ -1367,6 +1400,9 @@ connection_init_accepted_conn(connection_t *conn,
   connection_start_reading(conn);
 
   switch (conn->type) {
+    case CONN_TYPE_EXT_OR:
+      /* Initiate Extended ORPort authentication. */
+      return connection_ext_or_start_auth(TO_OR_CONN(conn));
     case CONN_TYPE_OR:
       control_event_or_conn_status(TO_OR_CONN(conn), OR_CONN_EVENT_NEW, 0);
       rv = connection_tls_start_handshake(TO_OR_CONN(conn), 1);
@@ -2873,6 +2909,8 @@ connection_handle_read_impl(connection_t *conn)
   switch (conn->type) {
     case CONN_TYPE_OR_LISTENER:
       return connection_handle_listener_read(conn, CONN_TYPE_OR);
+    case CONN_TYPE_EXT_OR_LISTENER:
+      return connection_handle_listener_read(conn, CONN_TYPE_EXT_OR);
     case CONN_TYPE_AP_LISTENER:
     case CONN_TYPE_AP_TRANS_LISTENER:
     case CONN_TYPE_AP_NATD_LISTENER:
@@ -3663,9 +3701,9 @@ connection_flush(connection_t *conn)
  * it all, so we don't end up with many megabytes of controller info queued at
  * once.
  */
-void
-connection_write_to_buf_impl_(const char *string, size_t len,
-                              connection_t *conn, int zlib)
+MOCK_IMPL(void,
+connection_write_to_buf_impl_,(const char *string, size_t len,
+                               connection_t *conn, int zlib))
 {
   /* XXXX This function really needs to return -1 on failure. */
   int r;
@@ -3905,6 +3943,7 @@ int
 connection_is_listener(connection_t *conn)
 {
   if (conn->type == CONN_TYPE_OR_LISTENER ||
+      conn->type == CONN_TYPE_EXT_OR_LISTENER ||
       conn->type == CONN_TYPE_AP_LISTENER ||
       conn->type == CONN_TYPE_AP_TRANS_LISTENER ||
       conn->type == CONN_TYPE_AP_DNS_LISTENER ||
@@ -3927,6 +3966,7 @@ connection_state_is_open(connection_t *conn)
     return 0;
 
   if ((conn->type == CONN_TYPE_OR && conn->state == OR_CONN_STATE_OPEN) ||
+      (conn->type == CONN_TYPE_EXT_OR) ||
       (conn->type == CONN_TYPE_AP && conn->state == AP_CONN_STATE_OPEN) ||
       (conn->type == CONN_TYPE_EXIT && conn->state == EXIT_CONN_STATE_OPEN) ||
       (conn->type == CONN_TYPE_CONTROL &&
@@ -4096,6 +4136,8 @@ connection_process_inbuf(connection_t *conn, int package_partial)
   switch (conn->type) {
     case CONN_TYPE_OR:
       return connection_or_process_inbuf(TO_OR_CONN(conn));
+    case CONN_TYPE_EXT_OR:
+      return connection_ext_or_process_inbuf(TO_OR_CONN(conn));
     case CONN_TYPE_EXIT:
     case CONN_TYPE_AP:
       return connection_edge_process_inbuf(TO_EDGE_CONN(conn),
@@ -4156,6 +4198,8 @@ connection_finished_flushing(connection_t *conn)
   switch (conn->type) {
     case CONN_TYPE_OR:
       return connection_or_finished_flushing(TO_OR_CONN(conn));
+    case CONN_TYPE_EXT_OR:
+      return connection_ext_or_finished_flushing(TO_OR_CONN(conn));
     case CONN_TYPE_AP:
     case CONN_TYPE_EXIT:
       return connection_edge_finished_flushing(TO_EDGE_CONN(conn));
@@ -4211,6 +4255,7 @@ connection_reached_eof(connection_t *conn)
 {
   switch (conn->type) {
     case CONN_TYPE_OR:
+    case CONN_TYPE_EXT_OR:
       return connection_or_reached_eof(TO_OR_CONN(conn));
     case CONN_TYPE_AP:
     case CONN_TYPE_EXIT:
@@ -4297,6 +4342,7 @@ assert_connection_ok(connection_t *conn, time_t now)
 
   switch (conn->type) {
     case CONN_TYPE_OR:
+    case CONN_TYPE_EXT_OR:
       tor_assert(conn->magic == OR_CONNECTION_MAGIC);
       break;
     case CONN_TYPE_AP:
@@ -4402,6 +4448,10 @@ assert_connection_ok(connection_t *conn, time_t now)
       tor_assert(conn->state >= OR_CONN_STATE_MIN_);
       tor_assert(conn->state <= OR_CONN_STATE_MAX_);
       break;
+    case CONN_TYPE_EXT_OR:
+      tor_assert(conn->state >= EXT_OR_CONN_STATE_MIN_);
+      tor_assert(conn->state <= EXT_OR_CONN_STATE_MAX_);
+      break;
     case CONN_TYPE_EXIT:
       tor_assert(conn->state >= EXIT_CONN_STATE_MIN_);
       tor_assert(conn->state <= EXIT_CONN_STATE_MAX_);
@@ -4534,6 +4584,7 @@ connection_free_all(void)
 
   /* Unlink everything from the identity map. */
   connection_or_clear_identity_map();
+  connection_or_clear_ext_or_id_map();
 
   /* Clear out our list of broken connections */
   clear_broken_connection_map(0);

+ 7 - 3
src/or/connection.h

@@ -19,7 +19,7 @@ const char *conn_type_to_string(int type);
 const char *conn_state_to_string(int type, int state);
 
 dir_connection_t *dir_connection_new(int socket_family);
-or_connection_t *or_connection_new(int socket_family);
+or_connection_t *or_connection_new(int type, int socket_family);
 edge_connection_t *edge_connection_new(int type, int socket_family);
 entry_connection_t *entry_connection_new(int type, int socket_family);
 control_connection_t *control_connection_new(int socket_family);
@@ -130,8 +130,8 @@ int connection_outbuf_too_full(connection_t *conn);
 int connection_handle_write(connection_t *conn, int force);
 int connection_flush(connection_t *conn);
 
-void connection_write_to_buf_impl_(const char *string, size_t len,
-                                   connection_t *conn, int zlib);
+MOCK_DECL(void, connection_write_to_buf_impl_,
+          (const char *string, size_t len, connection_t *conn, int zlib));
 /* DOCDOC connection_write_to_buf */
 static void connection_write_to_buf(const char *string, size_t len,
                                     connection_t *conn);
@@ -214,5 +214,9 @@ void connection_enable_rate_limiting(connection_t *conn);
 #define connection_type_uses_bufferevent(c) (0)
 #endif
 
+#ifdef CONNECTION_PRIVATE
+STATIC void connection_free_(connection_t *conn);
+#endif
+
 #endif
 

+ 76 - 6
src/or/connection_or.c

@@ -37,7 +37,7 @@
 #include "rephist.h"
 #include "router.h"
 #include "routerlist.h"
-
+#include "ext_orport.h"
 #ifdef USE_BUFFEREVENTS
 #include <event2/bufferevent_ssl.h>
 #endif
@@ -75,6 +75,10 @@ static void connection_or_handle_event_cb(struct bufferevent *bufev,
  * they form a linked list, with next_with_same_id as the next pointer. */
 static digestmap_t *orconn_identity_map = NULL;
 
+/** Global map between Extended ORPort identifiers and OR
+ *  connections. */
+static digestmap_t *orconn_ext_or_id_map = NULL;
+
 /** If conn is listed in orconn_identity_map, remove it, and clear
  * conn->identity_digest.  Otherwise do nothing. */
 void
@@ -174,6 +178,71 @@ connection_or_set_identity_digest(or_connection_t *conn, const char *digest)
 #endif
 }
 
+/** Remove the Extended ORPort identifier of <b>conn</b> from the
+ *  global identifier list. Also, clear the identifier from the
+ *  connection itself. */
+void
+connection_or_remove_from_ext_or_id_map(or_connection_t *conn)
+{
+  or_connection_t *tmp;
+  if (!orconn_ext_or_id_map)
+    return;
+  if (!conn->ext_or_conn_id)
+    return;
+
+  tmp = digestmap_remove(orconn_ext_or_id_map, conn->ext_or_conn_id);
+  if (!tor_digest_is_zero(conn->ext_or_conn_id))
+    tor_assert(tmp == conn);
+
+  memset(conn->ext_or_conn_id, 0, EXT_OR_CONN_ID_LEN);
+}
+
+/** Return the connection whose ext_or_id is <b>id</b>. Return NULL if no such
+ * connection is found. */
+or_connection_t *
+connection_or_get_by_ext_or_id(const char *id)
+{
+  if (!orconn_ext_or_id_map)
+    return NULL;
+  return digestmap_get(orconn_ext_or_id_map, id);
+}
+
+/** Deallocate the global Extended ORPort identifier list */
+void
+connection_or_clear_ext_or_id_map(void)
+{
+  digestmap_free(orconn_ext_or_id_map, NULL);
+  orconn_ext_or_id_map = NULL;
+}
+
+/** Creates an Extended ORPort identifier for <b>conn<b/> and deposits
+ *  it into the global list of identifiers. */
+void
+connection_or_set_ext_or_identifier(or_connection_t *conn)
+{
+  char random_id[EXT_OR_CONN_ID_LEN];
+  or_connection_t *tmp;
+
+  if (!orconn_ext_or_id_map)
+    orconn_ext_or_id_map = digestmap_new();
+
+  /* Remove any previous identifiers: */
+  if (conn->ext_or_conn_id && !tor_digest_is_zero(conn->ext_or_conn_id))
+      connection_or_remove_from_ext_or_id_map(conn);
+
+  do {
+    crypto_rand(random_id, sizeof(random_id));
+  } while (digestmap_get(orconn_ext_or_id_map, random_id));
+
+  if (!conn->ext_or_conn_id)
+    conn->ext_or_conn_id = tor_malloc_zero(EXT_OR_CONN_ID_LEN);
+
+  memcpy(conn->ext_or_conn_id, random_id, EXT_OR_CONN_ID_LEN);
+
+  tmp = digestmap_set(orconn_ext_or_id_map, random_id, conn);
+  tor_assert(!tmp);
+}
+
 /**************************************************************/
 
 /** Map from a string describing what a non-open OR connection was doing when
@@ -228,7 +297,7 @@ connection_or_get_state_description(or_connection_t *orconn,
   const char *conn_state;
   char tls_state[256];
 
-  tor_assert(conn->type == CONN_TYPE_OR);
+  tor_assert(conn->type == CONN_TYPE_OR || conn->type == CONN_TYPE_EXT_OR);
 
   conn_state = conn_state_to_string(conn->type, conn->state);
   tor_tls_get_state_description(orconn->tls, tls_state, sizeof(tls_state));
@@ -1077,7 +1146,7 @@ connection_or_connect(const tor_addr_t *_addr, uint16_t port,
     return NULL;
   }
 
-  conn = or_connection_new(tor_addr_family(&addr));
+  conn = or_connection_new(CONN_TYPE_OR, tor_addr_family(&addr));
 
   /*
    * Set up conn so it's got all the data we need to remember for channels
@@ -1212,8 +1281,8 @@ connection_or_close_for_error(or_connection_t *orconn, int flush)
  *
  * Return -1 if <b>conn</b> is broken, else return 0.
  */
-int
-connection_tls_start_handshake(or_connection_t *conn, int receiving)
+MOCK_IMPL(int,
+connection_tls_start_handshake,(or_connection_t *conn, int receiving))
 {
   channel_listener_t *chan_listener;
   channel_t *chan;
@@ -1470,7 +1539,8 @@ connection_or_handle_event_cb(struct bufferevent *bufev, short event,
 int
 connection_or_nonopen_was_started_here(or_connection_t *conn)
 {
-  tor_assert(conn->base_.type == CONN_TYPE_OR);
+  tor_assert(conn->base_.type == CONN_TYPE_OR ||
+             conn->base_.type == CONN_TYPE_EXT_OR);
   if (!conn->tls)
     return 1; /* it's still in proxy states or something */
   if (conn->handshake_state)

+ 2 - 1
src/or/connection_or.h

@@ -45,7 +45,8 @@ void connection_or_close_for_error(or_connection_t *orconn, int flush);
 
 void connection_or_report_broken_states(int severity, int domain);
 
-int connection_tls_start_handshake(or_connection_t *conn, int receiving);
+MOCK_DECL(int,connection_tls_start_handshake,(or_connection_t *conn,
+                                              int receiving));
 int connection_tls_continue_handshake(or_connection_t *conn);
 
 int connection_init_or_handshake_state(or_connection_t *conn,

+ 23 - 32
src/or/control.c

@@ -115,7 +115,7 @@ static int authentication_cookie_is_set = 0;
 /** If authentication_cookie_is_set, a secret cookie that we've stored to disk
  * and which we're using to authenticate controllers.  (If the controller can
  * read it off disk, it has permission to connect.) */
-static char authentication_cookie[AUTHENTICATION_COOKIE_LEN];
+static uint8_t *authentication_cookie = NULL;
 
 #define SAFECOOKIE_SERVER_TO_CONTROLLER_CONSTANT \
   "Tor safe cookie authentication server-to-controller hash"
@@ -4446,44 +4446,27 @@ get_cookie_file(void)
   }
 }
 
-/** Choose a random authentication cookie and write it to disk.
- * Anybody who can read the cookie from disk will be considered
- * authorized to use the control connection. Return -1 if we can't
- * write the file, or 0 on success. */
+/* Initialize the cookie-based authentication system of the
+ * ControlPort. If <b>enabled</b> is 0, then disable the cookie
+ * authentication system.  */
 int
-init_cookie_authentication(int enabled)
+init_control_cookie_authentication(int enabled)
 {
-  char *fname;
+  char *fname = NULL;
+  int retval;
+
   if (!enabled) {
     authentication_cookie_is_set = 0;
     return 0;
   }
 
-  /* We don't want to generate a new cookie every time we call
-   * options_act(). One should be enough. */
-  if (authentication_cookie_is_set)
-    return 0; /* all set */
-
   fname = get_cookie_file();
-  crypto_rand(authentication_cookie, AUTHENTICATION_COOKIE_LEN);
-  authentication_cookie_is_set = 1;
-  if (write_bytes_to_file(fname, authentication_cookie,
-                          AUTHENTICATION_COOKIE_LEN, 1)) {
-    log_warn(LD_FS,"Error writing authentication cookie to %s.",
-             escaped(fname));
-    tor_free(fname);
-    return -1;
-  }
-#ifndef _WIN32
-  if (get_options()->CookieAuthFileGroupReadable) {
-    if (chmod(fname, 0640)) {
-      log_warn(LD_FS,"Unable to make %s group-readable.", escaped(fname));
-    }
-  }
-#endif
-
+  retval = init_cookie_authentication(fname, "", /* no header */
+                                      AUTHENTICATION_COOKIE_LEN,
+                                      &authentication_cookie,
+                                      &authentication_cookie_is_set);
   tor_free(fname);
-  return 0;
+  return retval;
 }
 
 /** A copy of the process specifier of Tor's owning controller, or
@@ -4699,8 +4682,8 @@ control_event_bootstrap(bootstrap_status_t status, int progress)
  * that indicates a problem. <b>warn</b> gives a hint as to why, and
  * <b>reason</b> provides an "or_conn_end_reason" tag.
  */
-void
-control_event_bootstrap_problem(const char *warn, int reason)
+MOCK_IMPL(void,
+control_event_bootstrap_problem, (const char *warn, int reason))
 {
   int status = bootstrap_percent;
   const char *tag, *summary;
@@ -4767,3 +4750,11 @@ control_event_clients_seen(const char *controller_str)
     "650 CLIENTS_SEEN %s\r\n", controller_str);
 }
 
+/** Free any leftover allocated memory of the control.c subsystem. */
+void
+control_free_all(void)
+{
+  if (authentication_cookie) /* Free the auth cookie */
+    tor_free(authentication_cookie);
+}
+

+ 4 - 2
src/or/control.h

@@ -77,7 +77,7 @@ int control_event_buildtimeout_set(const circuit_build_times_t *cbt,
                                    buildtimeout_set_event_t type);
 int control_event_signal(uintptr_t signal);
 
-int init_cookie_authentication(int enabled);
+int init_control_cookie_authentication(int enabled);
 smartlist_t *decode_hashed_passwords(config_line_t *passwords);
 void disable_control_logging(void);
 void enable_control_logging(void);
@@ -85,9 +85,11 @@ void enable_control_logging(void);
 void monitor_owning_controller_process(const char *process_spec);
 
 void control_event_bootstrap(bootstrap_status_t status, int progress);
-void control_event_bootstrap_problem(const char *warn, int reason);
+MOCK_DECL(void, control_event_bootstrap_problem,(const char *warn,
+                                                 int reason));
 
 void control_event_clients_seen(const char *controller_str);
+void control_free_all(void);
 
 #ifdef CONTROL_PRIVATE
 /* Used only by control.c and test.c */

+ 3 - 1
src/or/directory.c

@@ -2966,7 +2966,9 @@ directory_handle_command_get(dir_connection_t *conn, const char *headers,
       tor_addr_t addr;
       if (tor_inet_aton((TO_CONN(conn))->address, &in)) {
         tor_addr_from_ipv4h(&addr, ntohl(in.s_addr));
-        geoip_note_client_seen(GEOIP_CLIENT_NETWORKSTATUS, &addr, time(NULL));
+        geoip_note_client_seen(GEOIP_CLIENT_NETWORKSTATUS,
+                               &addr, NULL,
+                               time(NULL));
         geoip_note_ns_response(GEOIP_SUCCESS);
         /* Note that a request for a network status has started, so that we
          * can measure the download time later on. */

+ 648 - 0
src/or/ext_orport.c

@@ -0,0 +1,648 @@
+/* Copyright (c) 2012, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file ext_orport.c
+ * \brief Code implementing the Extended ORPort.
+*/
+
+#define EXT_ORPORT_PRIVATE
+#include "or.h"
+#include "connection.h"
+#include "connection_or.h"
+#include "ext_orport.h"
+#include "control.h"
+#include "config.h"
+#include "util.h"
+#include "main.h"
+
+/** Allocate and return a structure capable of holding an Extended
+ *  ORPort message of body length <b>len</b>. */
+ext_or_cmd_t *
+ext_or_cmd_new(uint16_t len)
+{
+  size_t size = STRUCT_OFFSET(ext_or_cmd_t, body) + len;
+  ext_or_cmd_t *cmd = tor_malloc(size);
+  cmd->len = len;
+  return cmd;
+}
+
+/** Deallocate the Extended ORPort message in <b>cmd</b>. */
+void
+ext_or_cmd_free(ext_or_cmd_t *cmd)
+{
+  tor_free(cmd);
+}
+
+/** Get an Extended ORPort message from <b>conn</b>, and place it in
+ *  <b>out</b>. Return -1 on fail, 0 if we need more data, and 1 if we
+ *  successfully extracted an Extended ORPort command from the
+ *  buffer.  */
+static int
+connection_fetch_ext_or_cmd_from_buf(connection_t *conn, ext_or_cmd_t **out)
+{
+  IF_HAS_BUFFEREVENT(conn, {
+    struct evbuffer *input = bufferevent_get_input(conn->bufev);
+    return fetch_ext_or_command_from_evbuffer(input, out);
+  }) ELSE_IF_NO_BUFFEREVENT {
+    return fetch_ext_or_command_from_buf(conn->inbuf, out);
+  }
+}
+
+/** Write an Extended ORPort message to <b>conn</b>. Use
+ *  <b>command</b> as the command type, <b>bodylen</b> as the body
+ *  length, and <b>body</b>, if it's present, as the body of the
+ *  message. */
+STATIC int
+connection_write_ext_or_command(connection_t *conn,
+                                uint16_t command,
+                                const char *body,
+                                size_t bodylen)
+{
+  char header[4];
+  if (bodylen > UINT16_MAX)
+    return -1;
+  set_uint16(header, htons(command));
+  set_uint16(header+2, htons(bodylen));
+  connection_write_to_buf(header, 4, conn);
+  if (bodylen) {
+    tor_assert(body);
+    connection_write_to_buf(body, bodylen, conn);
+  }
+  return 0;
+}
+
+/** Transition from an Extended ORPort which accepts Extended ORPort
+ *  messages, to an Extended ORport which accepts OR traffic. */
+static void
+connection_ext_or_transition(or_connection_t *conn)
+{
+  tor_assert(conn->base_.type == CONN_TYPE_EXT_OR);
+
+  conn->base_.type = CONN_TYPE_OR;
+  TO_CONN(conn)->state = 0; // set the state to a neutral value
+  control_event_or_conn_status(conn, OR_CONN_EVENT_NEW, 0);
+  connection_tls_start_handshake(conn, 1);
+}
+
+/** Length of authentication cookie. */
+#define EXT_OR_PORT_AUTH_COOKIE_LEN 32
+/** Length of the header of the cookie file. */
+#define EXT_OR_PORT_AUTH_COOKIE_HEADER_LEN 32
+/** Static cookie file header. */
+#define EXT_OR_PORT_AUTH_COOKIE_HEADER "! Extended ORPort Auth Cookie !\x0a"
+/** Length of safe-cookie protocol hashes. */
+#define EXT_OR_PORT_AUTH_HASH_LEN DIGEST256_LEN
+/** Length of safe-cookie protocol nonces. */
+#define EXT_OR_PORT_AUTH_NONCE_LEN 32
+/** Safe-cookie protocol constants. */
+#define EXT_OR_PORT_AUTH_SERVER_TO_CLIENT_CONST \
+  "ExtORPort authentication server-to-client hash"
+#define EXT_OR_PORT_AUTH_CLIENT_TO_SERVER_CONST \
+  "ExtORPort authentication client-to-server hash"
+
+/* Code to indicate cookie authentication */
+#define EXT_OR_AUTHTYPE_SAFECOOKIE 0x01
+
+/** If true, we've set ext_or_auth_cookie to a secret code and stored
+ * it to disk. */
+STATIC int ext_or_auth_cookie_is_set = 0;
+/** If ext_or_auth_cookie_is_set, a secret cookie that we've stored to disk
+ * and which we're using to authenticate controllers.  (If the controller can
+ * read it off disk, it has permission to connect.) */
+STATIC uint8_t *ext_or_auth_cookie = NULL;
+
+/** Helper: Return a newly allocated string containing a path to the
+ * file where we store our authentication cookie. */
+char *
+get_ext_or_auth_cookie_file_name(void)
+{
+  const or_options_t *options = get_options();
+  if (options->ExtORPortCookieAuthFile &&
+      strlen(options->ExtORPortCookieAuthFile)) {
+    return tor_strdup(options->ExtORPortCookieAuthFile);
+  } else {
+    return get_datadir_fname("extended_orport_auth_cookie");
+  }
+}
+
+/* Initialize the cookie-based authentication system of the
+ * Extended ORPort. If <b>is_enabled</b> is 0, then disable the cookie
+ * authentication system. */
+int
+init_ext_or_cookie_authentication(int is_enabled)
+{
+  char *fname = NULL;
+  int retval;
+
+  if (!is_enabled) {
+    ext_or_auth_cookie_is_set = 0;
+    return 0;
+  }
+
+  fname = get_ext_or_auth_cookie_file_name();
+  retval = init_cookie_authentication(fname, EXT_OR_PORT_AUTH_COOKIE_HEADER,
+                                      EXT_OR_PORT_AUTH_COOKIE_HEADER_LEN,
+                                      &ext_or_auth_cookie,
+                                      &ext_or_auth_cookie_is_set);
+  tor_free(fname);
+  return retval;
+}
+
+/** Read data from <b>conn</b> and see if the client sent us the
+ *  authentication type that she prefers to use in this session.
+ *
+ *  Return -1 if we received corrupted data or if we don't support the
+ *  authentication type. Return 0 if we need more data in
+ *  <b>conn</b>. Return 1 if the authentication type negotiation was
+ *  successful. */
+static int
+connection_ext_or_auth_neg_auth_type(connection_t *conn)
+{
+  char authtype[1] = {0};
+
+  if (connection_get_inbuf_len(conn) < 1)
+    return 0;
+
+  if (connection_fetch_from_buf(authtype, 1, conn) < 0)
+    return -1;
+
+  log_debug(LD_GENERAL, "Client wants us to use %d auth type", authtype[0]);
+  if (authtype[0] != EXT_OR_AUTHTYPE_SAFECOOKIE) {
+    /* '1' is the only auth type supported atm */
+    return -1;
+  }
+
+  conn->state = EXT_OR_CONN_STATE_AUTH_WAIT_CLIENT_NONCE;
+  return 1;
+}
+
+/** DOCDOC */
+STATIC int
+handle_client_auth_nonce(const char *client_nonce, size_t client_nonce_len,
+                         char **client_hash_out,
+                         char **reply_out, size_t *reply_len_out)
+{
+  char server_hash[EXT_OR_PORT_AUTH_HASH_LEN] = {0};
+  char server_nonce[EXT_OR_PORT_AUTH_NONCE_LEN] = {0};
+  char *reply;
+  size_t reply_len;
+
+  if (client_nonce_len != EXT_OR_PORT_AUTH_NONCE_LEN)
+    return -1;
+
+  /* Get our nonce */
+  if (crypto_rand(server_nonce, EXT_OR_PORT_AUTH_NONCE_LEN) < 0)
+    return -1;
+
+  { /* set up macs */
+    size_t hmac_s_msg_len = strlen(EXT_OR_PORT_AUTH_SERVER_TO_CLIENT_CONST) +
+      2*EXT_OR_PORT_AUTH_NONCE_LEN;
+    size_t hmac_c_msg_len = strlen(EXT_OR_PORT_AUTH_CLIENT_TO_SERVER_CONST) +
+      2*EXT_OR_PORT_AUTH_NONCE_LEN;
+
+    char *hmac_s_msg = tor_malloc_zero(hmac_s_msg_len);
+    char *hmac_c_msg = tor_malloc_zero(hmac_c_msg_len);
+    char *correct_client_hash = tor_malloc_zero(EXT_OR_PORT_AUTH_HASH_LEN);
+
+    memcpy(hmac_s_msg,
+           EXT_OR_PORT_AUTH_SERVER_TO_CLIENT_CONST,
+           strlen(EXT_OR_PORT_AUTH_SERVER_TO_CLIENT_CONST));
+    memcpy(hmac_s_msg + strlen(EXT_OR_PORT_AUTH_SERVER_TO_CLIENT_CONST),
+           client_nonce, EXT_OR_PORT_AUTH_NONCE_LEN);
+    memcpy(hmac_s_msg + strlen(EXT_OR_PORT_AUTH_SERVER_TO_CLIENT_CONST) +
+           EXT_OR_PORT_AUTH_NONCE_LEN,
+           server_nonce, EXT_OR_PORT_AUTH_NONCE_LEN);
+
+    memcpy(hmac_c_msg,
+           EXT_OR_PORT_AUTH_CLIENT_TO_SERVER_CONST,
+           strlen(EXT_OR_PORT_AUTH_CLIENT_TO_SERVER_CONST));
+    memcpy(hmac_c_msg + strlen(EXT_OR_PORT_AUTH_CLIENT_TO_SERVER_CONST),
+           client_nonce, EXT_OR_PORT_AUTH_NONCE_LEN);
+    memcpy(hmac_c_msg + strlen(EXT_OR_PORT_AUTH_CLIENT_TO_SERVER_CONST) +
+           EXT_OR_PORT_AUTH_NONCE_LEN,
+           server_nonce, EXT_OR_PORT_AUTH_NONCE_LEN);
+
+    crypto_hmac_sha256(server_hash,
+                       (char*)ext_or_auth_cookie,
+                       EXT_OR_PORT_AUTH_COOKIE_LEN,
+                       hmac_s_msg,
+                       hmac_s_msg_len);
+
+    crypto_hmac_sha256(correct_client_hash,
+                       (char*)ext_or_auth_cookie,
+                       EXT_OR_PORT_AUTH_COOKIE_LEN,
+                       hmac_c_msg,
+                       hmac_c_msg_len);
+
+    /* Store the client hash we generated. We will need to compare it
+       with the hash sent by the client. */
+    *client_hash_out = correct_client_hash;
+
+    memwipe(hmac_s_msg, 0, hmac_s_msg_len);
+    memwipe(hmac_c_msg, 0, hmac_c_msg_len);
+
+    tor_free(hmac_s_msg);
+    tor_free(hmac_c_msg);
+  }
+
+  { /* debug logging */ /* XXX disable this codepath if not logging on debug?*/
+    char server_hash_encoded[(2*EXT_OR_PORT_AUTH_HASH_LEN) + 1];
+    char server_nonce_encoded[(2*EXT_OR_PORT_AUTH_NONCE_LEN) + 1];
+    char client_nonce_encoded[(2*EXT_OR_PORT_AUTH_NONCE_LEN) + 1];
+
+    base16_encode(server_hash_encoded, sizeof(server_hash_encoded),
+                  server_hash, sizeof(server_hash));
+    base16_encode(server_nonce_encoded, sizeof(server_nonce_encoded),
+                  server_nonce, sizeof(server_nonce));
+    base16_encode(client_nonce_encoded, sizeof(client_nonce_encoded),
+                  client_nonce, sizeof(client_nonce));
+
+    log_debug(LD_GENERAL,
+              "server_hash: '%s'\nserver_nonce: '%s'\nclient_nonce: '%s'",
+              server_hash_encoded, server_nonce_encoded, client_nonce_encoded);
+
+    memwipe(server_hash_encoded, 0, sizeof(server_hash_encoded));
+    memwipe(server_nonce_encoded, 0, sizeof(server_nonce_encoded));
+    memwipe(client_nonce_encoded, 0, sizeof(client_nonce_encoded));
+  }
+
+  { /* write reply: (server_hash, server_nonce) */
+
+    reply_len = EXT_OR_PORT_AUTH_COOKIE_LEN+EXT_OR_PORT_AUTH_NONCE_LEN;
+    reply = tor_malloc_zero(reply_len);
+    memcpy(reply, server_hash, EXT_OR_PORT_AUTH_HASH_LEN);
+    memcpy(reply + EXT_OR_PORT_AUTH_HASH_LEN, server_nonce,
+           EXT_OR_PORT_AUTH_NONCE_LEN);
+  }
+
+  *reply_out = reply;
+  *reply_len_out = reply_len;
+
+  return 0;
+}
+
+/** Read the client's nonce out of <b>conn</b>, setup the safe-cookie
+ *  crypto, and then send our own hash and nonce to the client
+ *
+ *  Return -1 if there was an error; return 0 if we need more data in
+ *  <b>conn</b>, and return 1 if we successfully retrieved the
+ *  client's nonce and sent our own. */
+static int
+connection_ext_or_auth_handle_client_nonce(connection_t *conn)
+{
+  char client_nonce[EXT_OR_PORT_AUTH_NONCE_LEN];
+  char *reply=NULL;
+  size_t reply_len=0;
+
+  if (!ext_or_auth_cookie_is_set) { /* this should not happen */
+    log_warn(LD_BUG, "Extended ORPort authentication cookie was not set. "
+             "That's weird since we should have done that on startup. "
+             "This might be a Tor bug, please file a bug report. ");
+    return -1;
+  }
+
+  if (connection_get_inbuf_len(conn) < EXT_OR_PORT_AUTH_NONCE_LEN)
+    return 0;
+
+  if (connection_fetch_from_buf(client_nonce,
+                                EXT_OR_PORT_AUTH_NONCE_LEN, conn) < 0)
+    return -1;
+
+  /* We extract the ClientNonce from the received data, and use it to
+     calculate ServerHash and ServerNonce according to proposal 217.
+
+     We also calculate our own ClientHash value and save it in the
+     connection state. We validate it later against the ClientHash
+     sent by the client.  */
+  if (handle_client_auth_nonce(client_nonce, sizeof(client_nonce),
+                            &TO_OR_CONN(conn)->ext_or_auth_correct_client_hash,
+                            &reply, &reply_len) < 0)
+    return -1;
+
+  connection_write_to_buf(reply, reply_len, conn);
+
+  memwipe(reply, 0, reply_len);
+  tor_free(reply);
+
+  log_debug(LD_GENERAL, "Got client nonce, and sent our own nonce and hash.");
+
+  conn->state = EXT_OR_CONN_STATE_AUTH_WAIT_CLIENT_HASH;
+  return 1;
+}
+
+#define connection_ext_or_auth_send_result_success(c)  \
+  connection_ext_or_auth_send_result(c, 1)
+#define connection_ext_or_auth_send_result_fail(c)  \
+  connection_ext_or_auth_send_result(c, 0)
+
+/** Send authentication results to <b>conn</b>. Successful results if
+ *  <b>success</b> is set; failure results otherwise. */
+static void
+connection_ext_or_auth_send_result(connection_t *conn, int success)
+{
+  if (success)
+    connection_write_to_buf("\x01", 1, conn);
+  else
+    connection_write_to_buf("\x00", 1, conn);
+}
+
+/** Receive the client's hash from <b>conn</b>, validate that it's
+ *  correct, and then send the authentication results to the client.
+ *
+ *  Return -1 if there was an error during validation; return 0 if we
+ *  need more data in <b>conn</b>, and return 1 if we successfully
+ *  validated the client's hash and sent a happy authentication
+ *  result. */
+static int
+connection_ext_or_auth_handle_client_hash(connection_t *conn)
+{
+  char provided_client_hash[EXT_OR_PORT_AUTH_HASH_LEN] = {0};
+
+  if (connection_get_inbuf_len(conn) < EXT_OR_PORT_AUTH_HASH_LEN)
+    return 0;
+
+  if (connection_fetch_from_buf(provided_client_hash,
+                                EXT_OR_PORT_AUTH_HASH_LEN, conn) < 0)
+    return -1;
+
+  if (tor_memneq(TO_OR_CONN(conn)->ext_or_auth_correct_client_hash,
+                 provided_client_hash, EXT_OR_PORT_AUTH_HASH_LEN)) {
+    log_warn(LD_GENERAL, "Incorrect client hash. Authentication failed.");
+    connection_ext_or_auth_send_result_fail(conn);
+    return -1;
+  }
+
+  log_debug(LD_GENERAL, "Got client's hash and it was legit.");
+
+  /* send positive auth result */
+  connection_ext_or_auth_send_result_success(conn);
+  conn->state = EXT_OR_CONN_STATE_OPEN;
+  return 1;
+}
+
+/** Handle data from <b>or_conn</b> received on Extended ORPort.
+ *  Return -1 on error. 0 on unsufficient data. 1 on correct. */
+static int
+connection_ext_or_auth_process_inbuf(or_connection_t *or_conn)
+{
+  connection_t *conn = TO_CONN(or_conn);
+
+  /* State transitions of the Extended ORPort authentication protocol:
+
+     EXT_OR_CONN_STATE_AUTH_WAIT_AUTH_TYPE (start state) ->
+     EXT_OR_CONN_STATE_AUTH_WAIT_CLIENT_NONCE ->
+     EXT_OR_CONN_STATE_AUTH_WAIT_CLIENT_HASH ->
+     EXT_OR_CONN_STATE_OPEN
+
+     During EXT_OR_CONN_STATE_OPEN, data is handled by
+     connection_ext_or_process_inbuf().
+  */
+
+  switch (conn->state) { /* Functionify */
+  case EXT_OR_CONN_STATE_AUTH_WAIT_AUTH_TYPE:
+    return connection_ext_or_auth_neg_auth_type(conn);
+
+  case EXT_OR_CONN_STATE_AUTH_WAIT_CLIENT_NONCE:
+    return connection_ext_or_auth_handle_client_nonce(conn);
+
+  case EXT_OR_CONN_STATE_AUTH_WAIT_CLIENT_HASH:
+    return connection_ext_or_auth_handle_client_hash(conn);
+
+  default:
+    log_warn(LD_BUG, "Encountered unexpected connection state %d while trying "
+             "to process Extended ORPort authentication data.", conn->state);
+    return -1;
+  }
+}
+
+/** Extended ORPort commands (Transport-to-Bridge) */
+#define EXT_OR_CMD_TB_DONE 0x0000
+#define EXT_OR_CMD_TB_USERADDR 0x0001
+#define EXT_OR_CMD_TB_TRANSPORT 0x0002
+
+/** Extended ORPort commands (Bridge-to-Transport) */
+#define EXT_OR_CMD_BT_OKAY 0x1000
+#define EXT_OR_CMD_BT_DENY 0x1001
+#define EXT_OR_CMD_BT_CONTROL 0x1002
+
+/** Process a USERADDR command from the Extended
+ *  ORPort. <b>payload</b> is a payload of size <b>len</b>.
+ *
+ *  If the USERADDR command was well formed, change the address of
+ *  <b>conn</b> to the address on the USERADDR command.
+ *
+ *  Return 0 on success and -1 on error. */
+static int
+connection_ext_or_handle_cmd_useraddr(connection_t *conn,
+                                      const char *payload, uint16_t len)
+{
+  /* Copy address string. */
+  tor_addr_t addr;
+  uint16_t port;
+  char *addr_str;
+  char *address_part=NULL;
+  int res;
+  if (memchr(payload, '\0', len)) {
+    log_fn(LOG_PROTOCOL_WARN, LD_NET, "Unexpected NUL in ExtORPort UserAddr");
+    return -1;
+  }
+
+  addr_str = tor_memdup_nulterm(payload, len);
+
+  res = tor_addr_port_split(LOG_INFO, addr_str, &address_part, &port);
+  tor_free(addr_str);
+  if (res<0)
+    return -1;
+
+  res = tor_addr_parse(&addr, address_part);
+  tor_free(address_part);
+  if (res<0)
+    return -1;
+
+  { /* do some logging */
+    char *old_address = tor_dup_addr(&conn->addr);
+    char *new_address = tor_dup_addr(&addr);
+
+    log_debug(LD_NET, "Received USERADDR."
+             "We rewrite our address from '%s:%u' to '%s:%u'.",
+             safe_str(old_address), conn->port, safe_str(new_address), port);
+
+    tor_free(old_address);
+    tor_free(new_address);
+  }
+
+  /* record the address */
+  tor_addr_copy(&conn->addr, &addr);
+  conn->port = port;
+  if (conn->address) {
+    tor_free(conn->address);
+  }
+  conn->address = tor_dup_addr(&addr);
+
+  return 0;
+}
+
+/** Process a TRANSPORT command from the Extended
+ *  ORPort. <b>payload</b> is a payload of size <b>len</b>.
+ *
+ *  If the TRANSPORT command was well formed, register the name of the
+ *  transport on <b>conn</b>.
+ *
+ *  Return 0 on success and -1 on error. */
+static int
+connection_ext_or_handle_cmd_transport(or_connection_t *conn,
+                                       const char *payload, uint16_t len)
+{
+  char *transport_str;
+  if (memchr(payload, '\0', len)) {
+    log_fn(LOG_PROTOCOL_WARN, LD_NET, "Unexpected NUL in ExtORPort Transport");
+    return -1;
+  }
+
+  transport_str = tor_memdup_nulterm(payload, len);
+
+  /* Transport names MUST be C-identifiers. */
+  if (!string_is_C_identifier(transport_str)) {
+    tor_free(transport_str);
+    return -1;
+  }
+
+  /* If ext_or_transport is already occupied (because the PT sent two
+   *  TRANSPORT commands), deallocate the old name and keep the new
+   *  one */
+  if (conn->ext_or_transport)
+    tor_free(conn->ext_or_transport);
+
+  conn->ext_or_transport = transport_str;
+  return 0;
+}
+
+#define EXT_OR_CONN_STATE_IS_AUTHENTICATING(st) \
+  ((st) <= EXT_OR_CONN_STATE_AUTH_MAX)
+
+/** Process Extended ORPort messages from <b>or_conn</b>. */
+int
+connection_ext_or_process_inbuf(or_connection_t *or_conn)
+{
+  connection_t *conn = TO_CONN(or_conn);
+  ext_or_cmd_t *command;
+  int r;
+
+  /* DOCDOC Document the state machine and transitions in this function */
+
+  /* If we are still in the authentication stage, process traffic as
+     authentication data: */
+  while (EXT_OR_CONN_STATE_IS_AUTHENTICATING(conn->state)) {
+    log_debug(LD_GENERAL, "Got Extended ORPort authentication data (%u).",
+              (unsigned int) connection_get_inbuf_len(conn));
+    r = connection_ext_or_auth_process_inbuf(or_conn);
+    if (r < 0) {
+      connection_mark_for_close(conn);
+      return -1;
+    } else if (r == 0) {
+      return 0;
+    }
+    /* if r > 0, loop and process more data (if any). */
+  }
+
+  while (1) {
+    log_debug(LD_GENERAL, "Got Extended ORPort data.");
+    command = NULL;
+    r = connection_fetch_ext_or_cmd_from_buf(conn, &command);
+    if (r < 0)
+      goto err;
+    else if (r == 0)
+      return 0; /* need to wait for more data */
+
+    /* Got a command! */
+    tor_assert(command);
+
+    if (command->cmd == EXT_OR_CMD_TB_DONE) {
+      if (connection_get_inbuf_len(conn)) {
+        /* The inbuf isn't empty; the client is misbehaving. */
+        goto err;
+      }
+
+      log_debug(LD_NET, "Received DONE.");
+
+      /* If the transport proxy did not use the TRANSPORT command to
+       * specify the transport name, mark this as unknown transport. */
+      if (!or_conn->ext_or_transport) {
+        /* We write this string this way to avoid ??>, which is a C
+         * trigraph. */
+        or_conn->ext_or_transport = tor_strdup("<?" "?>");
+      }
+
+      connection_write_ext_or_command(conn, EXT_OR_CMD_BT_OKAY, NULL, 0);
+
+      /* can't transition immediately; need to flush first. */
+      conn->state = EXT_OR_CONN_STATE_FLUSHING;
+      connection_stop_reading(conn);
+    } else if (command->cmd == EXT_OR_CMD_TB_USERADDR) {
+      if (connection_ext_or_handle_cmd_useraddr(conn,
+                                            command->body, command->len) < 0)
+        goto err;
+    } else if (command->cmd == EXT_OR_CMD_TB_TRANSPORT) {
+      if (connection_ext_or_handle_cmd_transport(or_conn,
+                                             command->body, command->len) < 0)
+        goto err;
+    } else {
+      log_notice(LD_NET,"Got Extended ORPort command we don't regognize (%u).",
+                 command->cmd);
+    }
+
+    ext_or_cmd_free(command);
+  }
+
+  return 0;
+
+ err:
+  ext_or_cmd_free(command);
+  connection_mark_for_close(conn);
+  return -1;
+}
+
+/** <b>conn</b> finished flushing Extended ORPort messages to the
+ *  network, and is now ready to accept OR traffic. This function
+ *  does the transition. */
+int
+connection_ext_or_finished_flushing(or_connection_t *conn)
+{
+  if (conn->base_.state == EXT_OR_CONN_STATE_FLUSHING) {
+    connection_start_reading(TO_CONN(conn));
+    connection_ext_or_transition(conn);
+  }
+  return 0;
+}
+
+/** Initiate Extended ORPort authentication, by sending the list of
+ *  supported authentication types to the client. */
+int
+connection_ext_or_start_auth(or_connection_t *or_conn)
+{
+  connection_t *conn = TO_CONN(or_conn);
+  const uint8_t authtypes[] = {
+    /* We only support authtype '1' for now. */
+    EXT_OR_AUTHTYPE_SAFECOOKIE,
+    /* Marks the end of the list. */
+    0
+  };
+
+  log_debug(LD_GENERAL,
+           "ExtORPort authentication: Sending supported authentication types");
+
+  connection_write_to_buf((const char *)authtypes, sizeof(authtypes), conn);
+  conn->state = EXT_OR_CONN_STATE_AUTH_WAIT_AUTH_TYPE;
+
+  return 0;
+}
+
+/** Free any leftover allocated memory of the ext_orport.c subsystem. */
+void
+ext_orport_free_all(void)
+{
+  if (ext_or_auth_cookie) /* Free the auth cookie */
+    tor_free(ext_or_auth_cookie);
+}
+

+ 42 - 0
src/or/ext_orport.h

@@ -0,0 +1,42 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#ifndef EXT_ORPORT_H
+#define EXT_ORPORT_H
+
+int connection_ext_or_start_auth(or_connection_t *or_conn);
+
+ext_or_cmd_t *ext_or_cmd_new(uint16_t len);
+void ext_or_cmd_free(ext_or_cmd_t *cmd);
+void connection_or_set_ext_or_identifier(or_connection_t *conn);
+void connection_or_remove_from_ext_or_id_map(or_connection_t *conn);
+void connection_or_clear_ext_or_id_map(void);
+or_connection_t *connection_or_get_by_ext_or_id(const char *id);
+
+int connection_ext_or_finished_flushing(or_connection_t *conn);
+int connection_ext_or_process_inbuf(or_connection_t *or_conn);
+
+int init_ext_or_cookie_authentication(int is_enabled);
+char *get_ext_or_auth_cookie_file_name(void);
+void ext_orport_free_all(void);
+
+#ifdef EXT_ORPORT_PRIVATE
+STATIC int connection_write_ext_or_command(connection_t *conn,
+                                           uint16_t command,
+                                           const char *body,
+                                           size_t bodylen);
+STATIC int handle_client_auth_nonce(const char *client_nonce,
+                         size_t client_nonce_len,
+                         char **client_hash_out,
+                         char **reply_out, size_t *reply_len_out);
+#ifdef TOR_UNIT_TESTS
+extern uint8_t *ext_or_auth_cookie;
+extern int ext_or_auth_cookie_is_set;
+#endif
+#endif
+
+#endif
+

+ 159 - 9
src/or/geoip.c

@@ -461,6 +461,10 @@ geoip_db_digest(sa_family_t family)
 typedef struct clientmap_entry_t {
   HT_ENTRY(clientmap_entry_t) node;
   tor_addr_t addr;
+ /* Name of pluggable transport used by this client. NULL if no
+    pluggable transport was used. */
+  char *transport_name;
+
   /** Time when we last saw this IP address, in MINUTES since the epoch.
    *
    * (This will run out of space around 4011 CE.  If Tor is still in use around
@@ -482,12 +486,18 @@ static HT_HEAD(clientmap, clientmap_entry_t) client_history =
 static INLINE unsigned
 clientmap_entry_hash(const clientmap_entry_t *a)
 {
-  return ht_improve_hash(tor_addr_hash(&a->addr));
+  unsigned h = tor_addr_hash(&a->addr);
+  if (a->transport_name)
+    h += ht_string_hash(a->transport_name);
+  return ht_improve_hash(h);
 }
 /** Hashtable helper: compare two clientmap_entry_t values for equality. */
 static INLINE int
 clientmap_entries_eq(const clientmap_entry_t *a, const clientmap_entry_t *b)
 {
+  if (strcmp_opt(a->transport_name, b->transport_name))
+    return 0;
+
   return !tor_addr_compare(&a->addr, &b->addr, CMP_EXACT) &&
          a->action == b->action;
 }
@@ -497,6 +507,17 @@ HT_PROTOTYPE(clientmap, clientmap_entry_t, node, clientmap_entry_hash,
 HT_GENERATE(clientmap, clientmap_entry_t, node, clientmap_entry_hash,
             clientmap_entries_eq, 0.6, malloc, realloc, free);
 
+/** Free all storage held by <b>ent</b>. */
+static void
+clientmap_entry_free(clientmap_entry_t *ent)
+{
+  if (!ent)
+    return;
+
+  tor_free(ent->transport_name);
+  tor_free(ent);
+}
+
 /** Clear history of connecting clients used by entry and bridge stats. */
 static void
 client_history_clear(void)
@@ -507,7 +528,7 @@ client_history_clear(void)
     if ((*ent)->action == GEOIP_CLIENT_CONNECT) {
       this = *ent;
       next = HT_NEXT_RMV(clientmap, &client_history, ent);
-      tor_free(this);
+      clientmap_entry_free(this);
     } else {
       next = HT_NEXT(clientmap, &client_history, ent);
     }
@@ -519,10 +540,14 @@ client_history_clear(void)
  * configured accordingly. */
 void
 geoip_note_client_seen(geoip_client_action_t action,
-                       const tor_addr_t *addr, time_t now)
+                       const tor_addr_t *addr,
+                       const char *transport_name,
+                       time_t now)
 {
   const or_options_t *options = get_options();
   clientmap_entry_t lookup, *ent;
+  memset(&lookup, 0, sizeof(clientmap_entry_t));
+
   if (action == GEOIP_CLIENT_CONNECT) {
     /* Only remember statistics as entry guard or as bridge. */
     if (!options->EntryStatistics &&
@@ -534,12 +559,20 @@ geoip_note_client_seen(geoip_client_action_t action,
       return;
   }
 
+  log_debug(LD_GENERAL, "Seen client from '%s' with transport '%s'.",
+            safe_str_client(fmt_addr((addr))),
+            transport_name ? transport_name : "<no transport>");
+
   tor_addr_copy(&lookup.addr, addr);
   lookup.action = (int)action;
+  lookup.transport_name = (char*) transport_name;
   ent = HT_FIND(clientmap, &client_history, &lookup);
+
   if (! ent) {
     ent = tor_malloc_zero(sizeof(clientmap_entry_t));
     tor_addr_copy(&ent->addr, addr);
+    if (transport_name)
+      ent->transport_name = tor_strdup(transport_name);
     ent->action = (int)action;
     HT_INSERT(clientmap, &client_history, ent);
   }
@@ -566,7 +599,7 @@ remove_old_client_helper_(struct clientmap_entry_t *ent, void *_cutoff)
 {
   time_t cutoff = *(time_t*)_cutoff / 60;
   if (ent->last_seen_in_minutes < cutoff) {
-    tor_free(ent);
+    clientmap_entry_free(ent);
     return 1;
   } else {
     return 0;
@@ -769,6 +802,106 @@ geoip_change_dirreq_state(uint64_t dirreq_id, dirreq_type_t type,
   }
 }
 
+/** Return the bridge-ip-transports string that should be inserted in
+ *  our extra-info descriptor. Return NULL if the bridge-ip-transports
+ *  line should be empty.  */
+char *
+geoip_get_transport_history(void)
+{
+  unsigned granularity = IP_GRANULARITY;
+  /** String hash table <name of transport> -> <number of users>. */
+  strmap_t *transport_counts = strmap_new();
+
+  /** Smartlist that contains copies of the names of the transports
+      that have been used. */
+  smartlist_t *transports_used = smartlist_new();
+
+  /* Special string to signify that no transport was used for this
+     connection. Pluggable transport names can't have symbols in their
+     names, so this string will never collide with a real transport. */
+  static const char* no_transport_str = "<OR>";
+
+  clientmap_entry_t **ent;
+  const char *transport_name = NULL;
+  smartlist_t *string_chunks = smartlist_new();
+  char *the_string = NULL;
+
+  /* If we haven't seen any clients yet, return NULL. */
+  if (HT_EMPTY(&client_history))
+    goto done;
+
+  /** We do the following steps to form the transport history string:
+   *  a) Foreach client that uses a pluggable transport, we increase the
+   *  times that transport was used by one. If the client did not use
+   *  a transport, we increase the number of times someone connected
+   *  without obfuscation.
+   *  b) Foreach transport we observed, we write its transport history
+   *  string and push it to string_chunks. So, for example, if we've
+   *  seen 665 obfs2 clients, we write "obfs2=665".
+   *  c) We concatenate string_chunks to form the final string.
+   */
+
+  log_debug(LD_GENERAL,"Starting iteration for transport history. %d clients.",
+            HT_SIZE(&client_history));
+
+  /* Loop through all clients. */
+  HT_FOREACH(ent, clientmap, &client_history) {
+    uintptr_t val;
+    void *ptr;
+    transport_name = (*ent)->transport_name;
+    if (!transport_name)
+      transport_name = no_transport_str;
+
+    /* Increase the count for this transport name. */
+    ptr = strmap_get(transport_counts, transport_name);
+    val = (uintptr_t)ptr;
+    val++;
+    ptr = (void*)val;
+    strmap_set(transport_counts, transport_name, ptr);
+
+    /* If it's the first time we see this transport, note it. */
+    if (val == 1)
+      smartlist_add(transports_used, tor_strdup(transport_name));
+
+    log_debug(LD_GENERAL, "Client from '%s' with transport '%s'. "
+              "I've now seen %d clients.",
+              safe_str_client(fmt_addr(&(*ent)->addr)),
+              transport_name ? transport_name : "<no transport>",
+              (int)val);
+  }
+
+  /* Sort the transport names (helps with unit testing). */
+  smartlist_sort_strings(transports_used);
+
+  /* Loop through all seen transports. */
+  SMARTLIST_FOREACH_BEGIN(transports_used, const char *, transport_name) {
+    void *transport_count_ptr = strmap_get(transport_counts, transport_name);
+    uintptr_t transport_count = (uintptr_t) transport_count_ptr;
+
+    log_debug(LD_GENERAL, "We got "U64_FORMAT" clients with transport '%s'.",
+              U64_PRINTF_ARG((uint64_t)transport_count), transport_name);
+
+    smartlist_add_asprintf(string_chunks, "%s="U64_FORMAT,
+                           transport_name,
+                           U64_PRINTF_ARG(round_uint64_to_next_multiple_of(
+                                               (uint64_t)transport_count,
+                                               granularity)));
+  } SMARTLIST_FOREACH_END(transport_name);
+
+  the_string = smartlist_join_strings(string_chunks, ",", 0, NULL);
+
+  log_debug(LD_GENERAL, "Final bridge-ip-transports string: '%s'", the_string);
+
+ done:
+  strmap_free(transport_counts, NULL);
+  SMARTLIST_FOREACH(transports_used, char *, s, tor_free(s));
+  smartlist_free(transports_used);
+  SMARTLIST_FOREACH(string_chunks, char *, s, tor_free(s));
+  smartlist_free(string_chunks);
+
+  return the_string;
+}
+
 /** Return a newly allocated comma-separated string containing statistics
  * on network status downloads. The string contains the number of completed
  * requests, timeouts, and still running requests as well as the download
@@ -1037,7 +1170,7 @@ geoip_reset_dirreq_stats(time_t now)
       if ((*ent)->action == GEOIP_CLIENT_NETWORKSTATUS) {
         this = *ent;
         next = HT_NEXT_RMV(clientmap, &client_history, ent);
-        tor_free(this);
+        clientmap_entry_free(this);
       } else {
         next = HT_NEXT(clientmap, &client_history, ent);
       }
@@ -1189,6 +1322,8 @@ validate_bridge_stats(const char *stats_str, time_t now)
   const char *BRIDGE_STATS_END = "bridge-stats-end ";
   const char *BRIDGE_IPS = "bridge-ips ";
   const char *BRIDGE_IPS_EMPTY_LINE = "bridge-ips\n";
+  const char *BRIDGE_TRANSPORTS = "bridge-ip-transports ";
+  const char *BRIDGE_TRANSPORTS_EMPTY_LINE = "bridge-ip-transports\n";
   const char *tmp;
   time_t stats_end_time;
   int seconds;
@@ -1223,6 +1358,15 @@ validate_bridge_stats(const char *stats_str, time_t now)
       return 0;
   }
 
+  /* Parse: "bridge-ip-transports PT=N,PT=N,..." */
+  tmp = find_str_at_start_of_line(stats_str, BRIDGE_TRANSPORTS);
+  if (!tmp) {
+    /* Look if there is an empty "bridge-ip-transports" line */
+    tmp = find_str_at_start_of_line(stats_str, BRIDGE_TRANSPORTS_EMPTY_LINE);
+    if (!tmp)
+      return 0;
+  }
+
   return 1;
 }
 
@@ -1236,7 +1380,8 @@ static char *bridge_stats_extrainfo = NULL;
 char *
 geoip_format_bridge_stats(time_t now)
 {
-  char *out = NULL, *country_data = NULL, *ipver_data = NULL;
+  char *out = NULL;
+  char *country_data = NULL, *ipver_data = NULL, *transport_data = NULL;
   long duration = now - start_of_bridge_stats_interval;
   char written[ISO_TIME_LEN+1];
 
@@ -1247,16 +1392,20 @@ geoip_format_bridge_stats(time_t now)
 
   format_iso_time(written, now);
   geoip_get_client_history(GEOIP_CLIENT_CONNECT, &country_data, &ipver_data);
+  transport_data = geoip_get_transport_history();
 
   tor_asprintf(&out,
                "bridge-stats-end %s (%ld s)\n"
                "bridge-ips %s\n"
-               "bridge-ip-versions %s\n",
+               "bridge-ip-versions %s\n"
+               "bridge-ip-transports %s\n",
                written, duration,
                country_data ? country_data : "",
-               ipver_data ? ipver_data : "");
+               ipver_data ? ipver_data : "",
+               transport_data ? transport_data : "");
   tor_free(country_data);
   tor_free(ipver_data);
+  tor_free(transport_data);
 
   return out;
 }
@@ -1515,7 +1664,7 @@ geoip_free_all(void)
     for (ent = HT_START(clientmap, &client_history); ent != NULL; ent = next) {
       this = *ent;
       next = HT_NEXT_RMV(clientmap, &client_history, ent);
-      tor_free(this);
+      clientmap_entry_free(this);
     }
     HT_CLEAR(clientmap, &client_history);
   }
@@ -1530,5 +1679,6 @@ geoip_free_all(void)
   }
 
   clear_geoip_db();
+  tor_free(bridge_stats_extrainfo);
 }
 

+ 3 - 1
src/or/geoip.h

@@ -29,10 +29,12 @@ const char *geoip_db_digest(sa_family_t family);
 country_t geoip_get_country(const char *countrycode);
 
 void geoip_note_client_seen(geoip_client_action_t action,
-                            const tor_addr_t *addr, time_t now);
+                            const tor_addr_t *addr, const char *transport_name,
+                            time_t now);
 void geoip_remove_old_clients(time_t cutoff);
 
 void geoip_note_ns_response(geoip_ns_response_t response);
+char *geoip_get_transport_history(void);
 int geoip_get_client_history(geoip_client_action_t action,
                              char **country_str, char **ipver_str);
 char *geoip_get_request_history(void);

+ 2 - 0
src/or/include.am

@@ -56,6 +56,7 @@ LIBTOR_A_SOURCES = \
         src/or/fp_pair.c				\
 	src/or/geoip.c					\
 	src/or/entrynodes.c				\
+	src/or/ext_orport.c				\
 	src/or/hibernate.c				\
 	src/or/main.c					\
 	src/or/microdesc.c				\
@@ -153,6 +154,7 @@ ORHEADERS = \
 	src/or/dns.h					\
 	src/or/dnsserv.h				\
 	src/or/eventdns_tor.h				\
+	src/or/ext_orport.h				\
 	src/or/fp_pair.h				\
 	src/or/geoip.h					\
 	src/or/entrynodes.h				\

+ 27 - 15
src/or/main.c

@@ -10,6 +10,7 @@
  * connections, implements main loop, and drives scheduled events.
  **/
 
+#define MAIN_PRIVATE
 #include "or.h"
 #include "addressmap.h"
 #include "buffers.h"
@@ -51,6 +52,7 @@
 #include "routerparse.h"
 #include "statefile.h"
 #include "status.h"
+#include "ext_orport.h"
 #ifdef USE_DMALLOC
 #include <dmalloc.h>
 #include <openssl/crypto.h>
@@ -412,6 +414,19 @@ connection_unlink(connection_t *conn)
   connection_free(conn);
 }
 
+/** Initialize the global connection list, closeable connection list,
+ * and active connection list. */
+STATIC void
+init_connection_lists(void)
+{
+  if (!connection_array)
+    connection_array = smartlist_new();
+  if (!closeable_connection_lst)
+    closeable_connection_lst = smartlist_new();
+  if (!active_linked_connection_lst)
+    active_linked_connection_lst = smartlist_new();
+}
+
 /** Schedule <b>conn</b> to be closed. **/
 void
 add_connection_to_closeable_list(connection_t *conn)
@@ -505,8 +520,8 @@ connection_is_reading(connection_t *conn)
 }
 
 /** Tell the main loop to stop notifying <b>conn</b> of any read events. */
-void
-connection_stop_reading(connection_t *conn)
+MOCK_IMPL(void,
+connection_stop_reading,(connection_t *conn))
 {
   tor_assert(conn);
 
@@ -530,8 +545,8 @@ connection_stop_reading(connection_t *conn)
 }
 
 /** Tell the main loop to start notifying <b>conn</b> of any read events. */
-void
-connection_start_reading(connection_t *conn)
+MOCK_IMPL(void,
+connection_start_reading,(connection_t *conn))
 {
   tor_assert(conn);
 
@@ -570,8 +585,8 @@ connection_is_writing(connection_t *conn)
 }
 
 /** Tell the main loop to stop notifying <b>conn</b> of any write events. */
-void
-connection_stop_writing(connection_t *conn)
+MOCK_IMPL(void,
+connection_stop_writing,(connection_t *conn))
 {
   tor_assert(conn);
 
@@ -596,8 +611,8 @@ connection_stop_writing(connection_t *conn)
 }
 
 /** Tell the main loop to start notifying <b>conn</b> of any write events. */
-void
-connection_start_writing(connection_t *conn)
+MOCK_IMPL(void,
+connection_start_writing,(connection_t *conn))
 {
   tor_assert(conn);
 
@@ -685,7 +700,7 @@ connection_stop_reading_from_linked_conn(connection_t *conn)
 }
 
 /** Close all connections that have been scheduled to get closed. */
-static void
+STATIC void
 close_closeable_connections(void)
 {
   int i;
@@ -2307,12 +2322,7 @@ tor_init(int argc, char *argv[])
   char buf[256];
   int i, quiet = 0;
   time_of_process_start = time(NULL);
-  if (!connection_array)
-    connection_array = smartlist_new();
-  if (!closeable_connection_lst)
-    closeable_connection_lst = smartlist_new();
-  if (!active_linked_connection_lst)
-    active_linked_connection_lst = smartlist_new();
+  init_connection_lists();
   /* Have the log set up with our application name. */
   tor_snprintf(buf, sizeof(buf), "Tor %s", get_version());
   log_set_application_name(buf);
@@ -2501,6 +2511,8 @@ tor_free_all(int postfork)
   memarea_clear_freelist();
   nodelist_free_all();
   microdesc_free_all();
+  ext_orport_free_all();
+  control_free_all();
   if (!postfork) {
     config_free_all();
     or_state_free_all();

+ 9 - 4
src/or/main.h

@@ -36,12 +36,12 @@ typedef enum watchable_events {
 } watchable_events_t;
 void connection_watch_events(connection_t *conn, watchable_events_t events);
 int connection_is_reading(connection_t *conn);
-void connection_stop_reading(connection_t *conn);
-void connection_start_reading(connection_t *conn);
+MOCK_DECL(void,connection_stop_reading,(connection_t *conn));
+MOCK_DECL(void,connection_start_reading,(connection_t *conn));
 
 int connection_is_writing(connection_t *conn);
-void connection_stop_writing(connection_t *conn);
-void connection_start_writing(connection_t *conn);
+MOCK_DECL(void,connection_stop_writing,(connection_t *conn));
+MOCK_DECL(void,connection_start_writing,(connection_t *conn));
 
 void connection_stop_reading_from_linked_conn(connection_t *conn);
 
@@ -69,5 +69,10 @@ int tor_main(int argc, char *argv[]);
 int do_main_loop(void);
 int tor_init(int argc, char **argv);
 
+#ifdef MAIN_PRIVATE
+STATIC void init_connection_lists(void);
+STATIC void close_closeable_connections(void);
+#endif
+
 #endif
 

+ 59 - 4
src/or/or.h

@@ -228,8 +228,14 @@ typedef enum {
 #define CONN_TYPE_AP_NATD_LISTENER 14
 /** Type for sockets listening for DNS requests. */
 #define CONN_TYPE_AP_DNS_LISTENER 15
-#define CONN_TYPE_MAX_ 15
-/* !!!! If CONN_TYPE_MAX_ is ever over 15, we must grow the type field in
+
+/** Type for connections from the Extended ORPort. */
+#define CONN_TYPE_EXT_OR 16
+/** Type for sockets listening for Extended ORPort connections. */
+#define CONN_TYPE_EXT_OR_LISTENER 17
+
+#define CONN_TYPE_MAX_ 17
+/* !!!! If _CONN_TYPE_MAX is ever over 31, we must grow the type field in
  * connection_t. */
 
 /* Proxy client types */
@@ -309,6 +315,25 @@ typedef enum {
 #define OR_CONN_STATE_OPEN 8
 #define OR_CONN_STATE_MAX_ 8
 
+/** States of the Extended ORPort protocol. Be careful before changing
+ *  the numbers: they matter. */
+#define EXT_OR_CONN_STATE_MIN_ 1
+/** Extended ORPort authentication is waiting for the authentication
+ *  type selected by the client. */
+#define EXT_OR_CONN_STATE_AUTH_WAIT_AUTH_TYPE 1
+/** Extended ORPort authentication is waiting for the client nonce. */
+#define EXT_OR_CONN_STATE_AUTH_WAIT_CLIENT_NONCE 2
+/** Extended ORPort authentication is waiting for the client hash. */
+#define EXT_OR_CONN_STATE_AUTH_WAIT_CLIENT_HASH 3
+#define EXT_OR_CONN_STATE_AUTH_MAX 3
+/** Authentication finished and the Extended ORPort is now accepting
+ *  traffic. */
+#define EXT_OR_CONN_STATE_OPEN 4
+/** Extended ORPort is flushing its last messages and preparing to
+ *  start accepting OR connections. */
+#define EXT_OR_CONN_STATE_FLUSHING 5
+#define EXT_OR_CONN_STATE_MAX_ 5
+
 #define EXIT_CONN_STATE_MIN_ 1
 /** State for an exit connection: waiting for response from DNS farm. */
 #define EXIT_CONN_STATE_RESOLVING 1
@@ -1082,6 +1107,13 @@ typedef struct var_cell_t {
   uint8_t payload[FLEXIBLE_ARRAY_MEMBER];
 } var_cell_t;
 
+/** A parsed Extended ORPort message. */
+typedef struct ext_or_cmd_t {
+  uint16_t cmd; /** Command type */
+  uint16_t len; /** Body length */
+  char body[FLEXIBLE_ARRAY_MEMBER]; /** Message body */
+} ext_or_cmd_t;
+
 /** A cell as packed for writing to the network. */
 typedef struct packed_cell_t {
   /** Next cell queued on this circuit. */
@@ -1163,7 +1195,7 @@ typedef struct connection_t {
                    * *_CONNECTION_MAGIC. */
 
   uint8_t state; /**< Current state of this connection. */
-  unsigned int type:4; /**< What kind of connection is this? */
+  unsigned int type:5; /**< What kind of connection is this? */
   unsigned int purpose:5; /**< Only used for DIR and EXIT types currently. */
 
   /* The next fields are all one-bit booleans. Some are only applicable to
@@ -1405,6 +1437,9 @@ typedef struct or_handshake_state_t {
   /**@}*/
 } or_handshake_state_t;
 
+/** Length of Extended ORPort connection identifier. */
+#define EXT_OR_CONN_ID_LEN DIGEST_LEN /* 20 */
+
 /** Subtype of connection_t for an "OR connection" -- that is, one that speaks
  * cells over TLS. */
 typedef struct or_connection_t {
@@ -1413,6 +1448,20 @@ typedef struct or_connection_t {
   /** Hash of the public RSA key for the other side's identity key, or zeroes
    * if the other side hasn't shown us a valid identity key. */
   char identity_digest[DIGEST_LEN];
+
+  /** Extended ORPort connection identifier. */
+  char *ext_or_conn_id;
+  /** This is the ClientHash value we expect to receive from the
+   *  client during the Extended ORPort authentication protocol. We
+   *  compute it upon receiving the ClientNoce from the client, and we
+   *  compare it with the acual ClientHash value sent by the
+   *  client. */
+  char *ext_or_auth_correct_client_hash;
+  /** String carrying the name of the pluggable transport
+   *  (e.g. "obfs2") that is obfuscating this connection. If no
+   *  pluggable transports are used, it's NULL. */
+  char *ext_or_transport;
+
   char *nickname; /**< Nickname of OR on other side (if any). */
 
   tor_tls_t *tls; /**< TLS connection state. */
@@ -3428,6 +3477,8 @@ typedef struct {
   char *User; /**< Name of user to run Tor as. */
   char *Group; /**< Name of group to run Tor as. */
   config_line_t *ORPort_lines; /**< Ports to listen on for OR connections. */
+  /** Ports to listen on for extended OR connections. */
+  config_line_t *ExtORPort_lines;
   /** Ports to listen on for SOCKS connections. */
   config_line_t *SocksPort_lines;
   /** Ports to listen on for transparent pf/netfilter connections. */
@@ -3463,6 +3514,7 @@ typedef struct {
   unsigned int ControlPort_set : 1;
   unsigned int DirPort_set : 1;
   unsigned int DNSPort_set : 1;
+  unsigned int ExtORPort_set : 1;
   /**@}*/
 
   int AssumeReachable; /**< Whether to publish our descriptor regardless. */
@@ -3742,7 +3794,10 @@ typedef struct {
 
   int CookieAuthentication; /**< Boolean: do we enable cookie-based auth for
                              * the control system? */
-  char *CookieAuthFile; /**< Location of a cookie authentication file. */
+  char *CookieAuthFile; /**< Filesystem location of a ControlPort
+                         *   authentication cookie. */
+  char *ExtORPortCookieAuthFile; /**< Filesystem location of Extended
+                                 *   ORPort authentication cookie. */
   int CookieAuthFileGroupReadable; /**< Boolean: Is the CookieAuthFile g+r? */
   int LeaveStreamsUnattached; /**< Boolean: Does Tor attach new streams to
                           * circuits itself (0), or does it expect a controller

+ 21 - 1
src/or/transports.c

@@ -96,6 +96,8 @@
 #include "router.h"
 #include "statefile.h"
 #include "entrynodes.h"
+#include "connection_or.h"
+#include "ext_orport.h"
 
 static process_environment_t *
 create_managed_proxy_environment(const managed_proxy_t *mp);
@@ -1198,6 +1200,8 @@ get_bindaddr_for_server_proxy(const managed_proxy_t *mp)
 static process_environment_t *
 create_managed_proxy_environment(const managed_proxy_t *mp)
 {
+  const or_options_t *options = get_options();
+
   /* Environment variables to be added to or set in mp's environment. */
   smartlist_t *envs = smartlist_new();
   /* XXXX The next time someone touches this code, shorten the name of
@@ -1261,7 +1265,23 @@ create_managed_proxy_environment(const managed_proxy_t *mp)
      * (If we remove this line entirely, some joker will stick this
      * variable in Tor's environment and crash PTs that try to parse
      * it even when not run in server mode.) */
-    smartlist_add(envs, tor_strdup("TOR_PT_EXTENDED_SERVER_PORT="));
+
+    if (options->ExtORPort_lines) {
+      char *ext_or_addrport_tmp =
+        get_first_listener_addrport_string(CONN_TYPE_EXT_OR_LISTENER);
+      char *cookie_file_loc = get_ext_or_auth_cookie_file_name();
+
+      smartlist_add_asprintf(envs, "TOR_PT_EXTENDED_SERVER_PORT=%s",
+                             ext_or_addrport_tmp);
+      smartlist_add_asprintf(envs, "TOR_PT_AUTH_COOKIE_FILE=%s",
+                             cookie_file_loc);
+
+      tor_free(ext_or_addrport_tmp);
+      tor_free(cookie_file_loc);
+
+    } else {
+      smartlist_add_asprintf(envs, "TOR_PT_EXTENDED_SERVER_PORT=");
+    }
   }
 
   SMARTLIST_FOREACH_BEGIN(envs, const char *, env_var) {

+ 3 - 0
src/test/include.am

@@ -18,6 +18,7 @@ src_test_AM_CPPFLAGS = -DSHARE_DATADIR="\"$(datadir)\"" \
 src_test_test_SOURCES = \
 	src/test/test.c \
 	src/test/test_addr.c \
+	src/test/test_buffers.c \
 	src/test/test_cell_formats.c \
 	src/test/test_circuitlist.c \
 	src/test/test_circuitmux.c \
@@ -26,11 +27,13 @@ src_test_test_SOURCES = \
 	src/test/test_cell_queue.c \
 	src/test/test_data.c \
 	src/test/test_dir.c \
+	src/test/test_extorport.c \
 	src/test/test_introduce.c \
 	src/test/test_microdesc.c \
 	src/test/test_options.c \
 	src/test/test_pt.c \
 	src/test/test_replay.c \
+	src/test/test_socks.c \
 	src/test/test_util.c \
 	src/test/test_config.c \
 	src/ext/tinytest.c

+ 120 - 683
src/test/test.c

@@ -28,7 +28,6 @@ const char tor_git_revision[] = "";
 
 /* These macros pull in declarations for some functions and structures that
  * are typically file-private. */
-#define BUFFERS_PRIVATE
 #define GEOIP_PRIVATE
 #define ROUTER_PRIVATE
 #define CIRCUITSTATS_PRIVATE
@@ -42,7 +41,6 @@ long int lround(double x);
 double fabs(double x);
 
 #include "or.h"
-#include "buffers.h"
 #include "circuitstats.h"
 #include "config.h"
 #include "connection_edge.h"
@@ -215,622 +213,6 @@ free_pregenerated_keys(void)
   }
 }
 
-typedef struct socks_test_data_t {
-  socks_request_t *req;
-  buf_t *buf;
-} socks_test_data_t;
-
-static void *
-socks_test_setup(const struct testcase_t *testcase)
-{
-  socks_test_data_t *data = tor_malloc(sizeof(socks_test_data_t));
-  (void)testcase;
-  data->buf = buf_new_with_capacity(256);
-  data->req = socks_request_new();
-  config_register_addressmaps(get_options());
-  return data;
-}
-static int
-socks_test_cleanup(const struct testcase_t *testcase, void *ptr)
-{
-  socks_test_data_t *data = ptr;
-  (void)testcase;
-  buf_free(data->buf);
-  socks_request_free(data->req);
-  tor_free(data);
-  return 1;
-}
-
-const struct testcase_setup_t socks_setup = {
-  socks_test_setup, socks_test_cleanup
-};
-
-#define SOCKS_TEST_INIT()                       \
-  socks_test_data_t *testdata = ptr;            \
-  buf_t *buf = testdata->buf;                   \
-  socks_request_t *socks = testdata->req;
-#define ADD_DATA(buf, s)                                        \
-  write_to_buf(s, sizeof(s)-1, buf)
-
-static void
-socks_request_clear(socks_request_t *socks)
-{
-  tor_free(socks->username);
-  tor_free(socks->password);
-  memset(socks, 0, sizeof(socks_request_t));
-}
-
-/** Perform unsupported SOCKS 4 commands */
-static void
-test_socks_4_unsupported_commands(void *ptr)
-{
-  SOCKS_TEST_INIT();
-
-  /* SOCKS 4 Send BIND [02] to IP address 2.2.2.2:4369 */
-  ADD_DATA(buf, "\x04\x02\x11\x11\x02\x02\x02\x02\x00");
-  test_assert(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
-                                   get_options()->SafeSocks) == -1);
-  test_eq(4, socks->socks_version);
-  test_eq(0, socks->replylen); /* XXX: shouldn't tor reply? */
-
- done:
-  ;
-}
-
-/** Perform supported SOCKS 4 commands */
-static void
-test_socks_4_supported_commands(void *ptr)
-{
-  SOCKS_TEST_INIT();
-
-  test_eq(0, buf_datalen(buf));
-
-  /* SOCKS 4 Send CONNECT [01] to IP address 2.2.2.2:4370 */
-  ADD_DATA(buf, "\x04\x01\x11\x12\x02\x02\x02\x03\x00");
-  test_assert(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
-                                   get_options()->SafeSocks) == 1);
-  test_eq(4, socks->socks_version);
-  test_eq(0, socks->replylen); /* XXX: shouldn't tor reply? */
-  test_eq(SOCKS_COMMAND_CONNECT, socks->command);
-  test_streq("2.2.2.3", socks->address);
-  test_eq(4370, socks->port);
-  test_assert(socks->got_auth == 0);
-  test_assert(! socks->username);
-
-  test_eq(0, buf_datalen(buf));
-  socks_request_clear(socks);
-
-  /* SOCKS 4 Send CONNECT [01] to IP address 2.2.2.2:4369 with userid*/
-  ADD_DATA(buf, "\x04\x01\x11\x12\x02\x02\x02\x04me\x00");
-  test_assert(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
-                                   get_options()->SafeSocks) == 1);
-  test_eq(4, socks->socks_version);
-  test_eq(0, socks->replylen); /* XXX: shouldn't tor reply? */
-  test_eq(SOCKS_COMMAND_CONNECT, socks->command);
-  test_streq("2.2.2.4", socks->address);
-  test_eq(4370, socks->port);
-  test_assert(socks->got_auth == 1);
-  test_assert(socks->username);
-  test_eq(2, socks->usernamelen);
-  test_memeq("me", socks->username, 2);
-
-  test_eq(0, buf_datalen(buf));
-  socks_request_clear(socks);
-
-  /* SOCKS 4a Send RESOLVE [F0] request for torproject.org */
-  ADD_DATA(buf, "\x04\xF0\x01\x01\x00\x00\x00\x02me\x00torproject.org\x00");
-  test_assert(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
-                                   get_options()->SafeSocks) == 1);
-  test_eq(4, socks->socks_version);
-  test_eq(0, socks->replylen); /* XXX: shouldn't tor reply? */
-  test_streq("torproject.org", socks->address);
-
-  test_eq(0, buf_datalen(buf));
-
- done:
-  ;
-}
-
-/**  Perform unsupported SOCKS 5 commands */
-static void
-test_socks_5_unsupported_commands(void *ptr)
-{
-  SOCKS_TEST_INIT();
-
-  /* SOCKS 5 Send unsupported BIND [02] command */
-  ADD_DATA(buf, "\x05\x02\x00\x01");
-
-  test_eq(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
-                               get_options()->SafeSocks), 0);
-  test_eq(0, buf_datalen(buf));
-  test_eq(5, socks->socks_version);
-  test_eq(2, socks->replylen);
-  test_eq(5, socks->reply[0]);
-  test_eq(0, socks->reply[1]);
-  ADD_DATA(buf, "\x05\x02\x00\x01\x02\x02\x02\x01\x01\x01");
-  test_eq(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
-                               get_options()->SafeSocks), -1);
-  /* XXX: shouldn't tor reply 'command not supported' [07]? */
-
-  buf_clear(buf);
-  socks_request_clear(socks);
-
-  /* SOCKS 5 Send unsupported UDP_ASSOCIATE [03] command */
-  ADD_DATA(buf, "\x05\x03\x00\x01\x02");
-  test_eq(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
-                               get_options()->SafeSocks), 0);
-  test_eq(5, socks->socks_version);
-  test_eq(2, socks->replylen);
-  test_eq(5, socks->reply[0]);
-  test_eq(2, socks->reply[1]);
-  ADD_DATA(buf, "\x05\x03\x00\x01\x02\x02\x02\x01\x01\x01");
-  test_eq(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
-                               get_options()->SafeSocks), -1);
-  /* XXX: shouldn't tor reply 'command not supported' [07]? */
-
- done:
-  ;
-}
-
-/** Perform supported SOCKS 5 commands */
-static void
-test_socks_5_supported_commands(void *ptr)
-{
-  SOCKS_TEST_INIT();
-
-  /* SOCKS 5 Send CONNECT [01] to IP address 2.2.2.2:4369 */
-  ADD_DATA(buf, "\x05\x01\x00");
-  test_eq(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
-                                   get_options()->SafeSocks), 0);
-  test_eq(5, socks->socks_version);
-  test_eq(2, socks->replylen);
-  test_eq(5, socks->reply[0]);
-  test_eq(0, socks->reply[1]);
-
-  ADD_DATA(buf, "\x05\x01\x00\x01\x02\x02\x02\x02\x11\x11");
-  test_eq(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
-                                   get_options()->SafeSocks), 1);
-  test_streq("2.2.2.2", socks->address);
-  test_eq(4369, socks->port);
-
-  test_eq(0, buf_datalen(buf));
-  socks_request_clear(socks);
-
-  /* SOCKS 5 Send CONNECT [01] to FQDN torproject.org:4369 */
-  ADD_DATA(buf, "\x05\x01\x00");
-  ADD_DATA(buf, "\x05\x01\x00\x03\x0Etorproject.org\x11\x11");
-  test_eq(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
-                                   get_options()->SafeSocks), 1);
-
-  test_eq(5, socks->socks_version);
-  test_eq(2, socks->replylen);
-  test_eq(5, socks->reply[0]);
-  test_eq(0, socks->reply[1]);
-  test_streq("torproject.org", socks->address);
-  test_eq(4369, socks->port);
-
-  test_eq(0, buf_datalen(buf));
-  socks_request_clear(socks);
-
-  /* SOCKS 5 Send RESOLVE [F0] request for torproject.org:4369 */
-  ADD_DATA(buf, "\x05\x01\x00");
-  ADD_DATA(buf, "\x05\xF0\x00\x03\x0Etorproject.org\x01\x02");
-  test_assert(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
-                                   get_options()->SafeSocks) == 1);
-  test_eq(5, socks->socks_version);
-  test_eq(2, socks->replylen);
-  test_eq(5, socks->reply[0]);
-  test_eq(0, socks->reply[1]);
-  test_streq("torproject.org", socks->address);
-
-  test_eq(0, buf_datalen(buf));
-  socks_request_clear(socks);
-
-  /* SOCKS 5 Send RESOLVE_PTR [F1] for IP address 2.2.2.5 */
-  ADD_DATA(buf, "\x05\x01\x00");
-  ADD_DATA(buf, "\x05\xF1\x00\x01\x02\x02\x02\x05\x01\x03");
-  test_assert(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
-                                   get_options()->SafeSocks) == 1);
-  test_eq(5, socks->socks_version);
-  test_eq(2, socks->replylen);
-  test_eq(5, socks->reply[0]);
-  test_eq(0, socks->reply[1]);
-  test_streq("2.2.2.5", socks->address);
-
-  test_eq(0, buf_datalen(buf));
-
- done:
-  ;
-}
-
-/**  Perform SOCKS 5 authentication */
-static void
-test_socks_5_no_authenticate(void *ptr)
-{
-  SOCKS_TEST_INIT();
-
-  /*SOCKS 5 No Authentication */
-  ADD_DATA(buf,"\x05\x01\x00");
-  test_assert(!fetch_from_buf_socks(buf, socks,
-                                    get_options()->TestSocks,
-                                    get_options()->SafeSocks));
-  test_eq(2, socks->replylen);
-  test_eq(5, socks->reply[0]);
-  test_eq(SOCKS_NO_AUTH, socks->reply[1]);
-
-  test_eq(0, buf_datalen(buf));
-
-  /*SOCKS 5 Send username/password anyway - pretend to be broken */
-  ADD_DATA(buf,"\x01\x02\x01\x01\x02\x01\x01");
-  test_assert(!fetch_from_buf_socks(buf, socks,
-                                    get_options()->TestSocks,
-                                    get_options()->SafeSocks));
-  test_eq(5, socks->socks_version);
-  test_eq(2, socks->replylen);
-  test_eq(1, socks->reply[0]);
-  test_eq(0, socks->reply[1]);
-
-  test_eq(2, socks->usernamelen);
-  test_eq(2, socks->passwordlen);
-
-  test_memeq("\x01\x01", socks->username, 2);
-  test_memeq("\x01\x01", socks->password, 2);
-
- done:
-  ;
-}
-
-/** Perform SOCKS 5 authentication */
-static void
-test_socks_5_authenticate(void *ptr)
-{
-  SOCKS_TEST_INIT();
-
-  /* SOCKS 5 Negotiate username/password authentication */
-  ADD_DATA(buf, "\x05\x01\x02");
-
-  test_assert(!fetch_from_buf_socks(buf, socks,
-                                   get_options()->TestSocks,
-                                   get_options()->SafeSocks));
-  test_eq(2, socks->replylen);
-  test_eq(5, socks->reply[0]);
-  test_eq(SOCKS_USER_PASS, socks->reply[1]);
-  test_eq(5, socks->socks_version);
-
-  test_eq(0, buf_datalen(buf));
-
-  /* SOCKS 5 Send username/password */
-  ADD_DATA(buf, "\x01\x02me\x08mypasswd");
-  test_assert(!fetch_from_buf_socks(buf, socks,
-                                   get_options()->TestSocks,
-                                   get_options()->SafeSocks));
-  test_eq(5, socks->socks_version);
-  test_eq(2, socks->replylen);
-  test_eq(1, socks->reply[0]);
-  test_eq(0, socks->reply[1]);
-
-  test_eq(2, socks->usernamelen);
-  test_eq(8, socks->passwordlen);
-
-  test_memeq("me", socks->username, 2);
-  test_memeq("mypasswd", socks->password, 8);
-
- done:
-  ;
-}
-
-/** Perform SOCKS 5 authentication and send data all in one go */
-static void
-test_socks_5_authenticate_with_data(void *ptr)
-{
-  SOCKS_TEST_INIT();
-
-  /* SOCKS 5 Negotiate username/password authentication */
-  ADD_DATA(buf, "\x05\x01\x02");
-
-  test_assert(!fetch_from_buf_socks(buf, socks,
-                                   get_options()->TestSocks,
-                                   get_options()->SafeSocks));
-  test_eq(2, socks->replylen);
-  test_eq(5, socks->reply[0]);
-  test_eq(SOCKS_USER_PASS, socks->reply[1]);
-  test_eq(5, socks->socks_version);
-
-  test_eq(0, buf_datalen(buf));
-
-  /* SOCKS 5 Send username/password */
-  /* SOCKS 5 Send CONNECT [01] to IP address 2.2.2.2:4369 */
-  ADD_DATA(buf, "\x01\x02me\x03you\x05\x01\x00\x01\x02\x02\x02\x02\x11\x11");
-  test_assert(fetch_from_buf_socks(buf, socks,
-                                   get_options()->TestSocks,
-                                   get_options()->SafeSocks) == 1);
-  test_eq(5, socks->socks_version);
-  test_eq(2, socks->replylen);
-  test_eq(1, socks->reply[0]);
-  test_eq(0, socks->reply[1]);
-
-  test_streq("2.2.2.2", socks->address);
-  test_eq(4369, socks->port);
-
-  test_eq(2, socks->usernamelen);
-  test_eq(3, socks->passwordlen);
-  test_memeq("me", socks->username, 2);
-  test_memeq("you", socks->password, 3);
-
- done:
-  ;
-}
-
-/** Perform SOCKS 5 authentication before method negotiated */
-static void
-test_socks_5_auth_before_negotiation(void *ptr)
-{
-  SOCKS_TEST_INIT();
-
-  /* SOCKS 5 Send username/password */
-  ADD_DATA(buf, "\x01\x02me\x02me");
-  test_assert(fetch_from_buf_socks(buf, socks,
-                                   get_options()->TestSocks,
-                                   get_options()->SafeSocks) == -1);
-  test_eq(0, socks->socks_version);
-  test_eq(0, socks->replylen);
-  test_eq(0, socks->reply[0]);
-  test_eq(0, socks->reply[1]);
-
- done:
-  ;
-}
-
-static void
-test_buffer_copy(void *arg)
-{
-  generic_buffer_t *buf=NULL, *buf2=NULL;
-  const char *s;
-  size_t len;
-  char b[256];
-  int i;
-  (void)arg;
-
-  buf = generic_buffer_new();
-  tt_assert(buf);
-
-  /* Copy an empty buffer. */
-  tt_int_op(0, ==, generic_buffer_set_to_copy(&buf2, buf));
-  tt_assert(buf2);
-  tt_int_op(0, ==, generic_buffer_len(buf2));
-
-  /* Now try with a short buffer. */
-  s = "And now comes an act of enormous enormance!";
-  len = strlen(s);
-  generic_buffer_add(buf, s, len);
-  tt_int_op(len, ==, generic_buffer_len(buf));
-  /* Add junk to buf2 so we can test replacing.*/
-  generic_buffer_add(buf2, "BLARG", 5);
-  tt_int_op(0, ==, generic_buffer_set_to_copy(&buf2, buf));
-  tt_int_op(len, ==, generic_buffer_len(buf2));
-  generic_buffer_get(buf2, b, len);
-  test_mem_op(b, ==, s, len);
-  /* Now free buf2 and retry so we can test allocating */
-  generic_buffer_free(buf2);
-  buf2 = NULL;
-  tt_int_op(0, ==, generic_buffer_set_to_copy(&buf2, buf));
-  tt_int_op(len, ==, generic_buffer_len(buf2));
-  generic_buffer_get(buf2, b, len);
-  test_mem_op(b, ==, s, len);
-  /* Clear buf for next test */
-  generic_buffer_get(buf, b, len);
-  tt_int_op(generic_buffer_len(buf),==,0);
-
-  /* Okay, now let's try a bigger buffer. */
-  s = "Quis autem vel eum iure reprehenderit qui in ea voluptate velit "
-    "esse quam nihil molestiae consequatur, vel illum qui dolorem eum "
-    "fugiat quo voluptas nulla pariatur?";
-  len = strlen(s);
-  for (i = 0; i < 256; ++i) {
-    b[0]=i;
-    generic_buffer_add(buf, b, 1);
-    generic_buffer_add(buf, s, len);
-  }
-  tt_int_op(0, ==, generic_buffer_set_to_copy(&buf2, buf));
-  tt_int_op(generic_buffer_len(buf2), ==, generic_buffer_len(buf));
-  for (i = 0; i < 256; ++i) {
-    generic_buffer_get(buf2, b, len+1);
-    tt_int_op((unsigned char)b[0],==,i);
-    test_mem_op(b+1, ==, s, len);
-  }
-
- done:
-  if (buf)
-    generic_buffer_free(buf);
-  if (buf2)
-    generic_buffer_free(buf2);
-}
-
-/** Run unit tests for buffers.c */
-static void
-test_buffers(void)
-{
-  char str[256];
-  char str2[256];
-
-  buf_t *buf = NULL, *buf2 = NULL;
-  const char *cp;
-
-  int j;
-  size_t r;
-
-  /****
-   * buf_new
-   ****/
-  if (!(buf = buf_new()))
-    test_fail();
-
-  //test_eq(buf_capacity(buf), 4096);
-  test_eq(buf_datalen(buf), 0);
-
-  /****
-   * General pointer frobbing
-   */
-  for (j=0;j<256;++j) {
-    str[j] = (char)j;
-  }
-  write_to_buf(str, 256, buf);
-  write_to_buf(str, 256, buf);
-  test_eq(buf_datalen(buf), 512);
-  fetch_from_buf(str2, 200, buf);
-  test_memeq(str, str2, 200);
-  test_eq(buf_datalen(buf), 312);
-  memset(str2, 0, sizeof(str2));
-
-  fetch_from_buf(str2, 256, buf);
-  test_memeq(str+200, str2, 56);
-  test_memeq(str, str2+56, 200);
-  test_eq(buf_datalen(buf), 56);
-  memset(str2, 0, sizeof(str2));
-  /* Okay, now we should be 512 bytes into the 4096-byte buffer.  If we add
-   * another 3584 bytes, we hit the end. */
-  for (j=0;j<15;++j) {
-    write_to_buf(str, 256, buf);
-  }
-  assert_buf_ok(buf);
-  test_eq(buf_datalen(buf), 3896);
-  fetch_from_buf(str2, 56, buf);
-  test_eq(buf_datalen(buf), 3840);
-  test_memeq(str+200, str2, 56);
-  for (j=0;j<15;++j) {
-    memset(str2, 0, sizeof(str2));
-    fetch_from_buf(str2, 256, buf);
-    test_memeq(str, str2, 256);
-  }
-  test_eq(buf_datalen(buf), 0);
-  buf_free(buf);
-  buf = NULL;
-
-  /* Okay, now make sure growing can work. */
-  buf = buf_new_with_capacity(16);
-  //test_eq(buf_capacity(buf), 16);
-  write_to_buf(str+1, 255, buf);
-  //test_eq(buf_capacity(buf), 256);
-  fetch_from_buf(str2, 254, buf);
-  test_memeq(str+1, str2, 254);
-  //test_eq(buf_capacity(buf), 256);
-  assert_buf_ok(buf);
-  write_to_buf(str, 32, buf);
-  //test_eq(buf_capacity(buf), 256);
-  assert_buf_ok(buf);
-  write_to_buf(str, 256, buf);
-  assert_buf_ok(buf);
-  //test_eq(buf_capacity(buf), 512);
-  test_eq(buf_datalen(buf), 33+256);
-  fetch_from_buf(str2, 33, buf);
-  test_eq(*str2, str[255]);
-
-  test_memeq(str2+1, str, 32);
-  //test_eq(buf_capacity(buf), 512);
-  test_eq(buf_datalen(buf), 256);
-  fetch_from_buf(str2, 256, buf);
-  test_memeq(str, str2, 256);
-
-  /* now try shrinking: case 1. */
-  buf_free(buf);
-  buf = buf_new_with_capacity(33668);
-  for (j=0;j<67;++j) {
-    write_to_buf(str,255, buf);
-  }
-  //test_eq(buf_capacity(buf), 33668);
-  test_eq(buf_datalen(buf), 17085);
-  for (j=0; j < 40; ++j) {
-    fetch_from_buf(str2, 255,buf);
-    test_memeq(str2, str, 255);
-  }
-
-  /* now try shrinking: case 2. */
-  buf_free(buf);
-  buf = buf_new_with_capacity(33668);
-  for (j=0;j<67;++j) {
-    write_to_buf(str,255, buf);
-  }
-  for (j=0; j < 20; ++j) {
-    fetch_from_buf(str2, 255,buf);
-    test_memeq(str2, str, 255);
-  }
-  for (j=0;j<80;++j) {
-    write_to_buf(str,255, buf);
-  }
-  //test_eq(buf_capacity(buf),33668);
-  for (j=0; j < 120; ++j) {
-    fetch_from_buf(str2, 255,buf);
-    test_memeq(str2, str, 255);
-  }
-
-  /* Move from buf to buf. */
-  buf_free(buf);
-  buf = buf_new_with_capacity(4096);
-  buf2 = buf_new_with_capacity(4096);
-  for (j=0;j<100;++j)
-    write_to_buf(str, 255, buf);
-  test_eq(buf_datalen(buf), 25500);
-  for (j=0;j<100;++j) {
-    r = 10;
-    move_buf_to_buf(buf2, buf, &r);
-    test_eq(r, 0);
-  }
-  test_eq(buf_datalen(buf), 24500);
-  test_eq(buf_datalen(buf2), 1000);
-  for (j=0;j<3;++j) {
-    fetch_from_buf(str2, 255, buf2);
-    test_memeq(str2, str, 255);
-  }
-  r = 8192; /*big move*/
-  move_buf_to_buf(buf2, buf, &r);
-  test_eq(r, 0);
-  r = 30000; /* incomplete move */
-  move_buf_to_buf(buf2, buf, &r);
-  test_eq(r, 13692);
-  for (j=0;j<97;++j) {
-    fetch_from_buf(str2, 255, buf2);
-    test_memeq(str2, str, 255);
-  }
-  buf_free(buf);
-  buf_free(buf2);
-  buf = buf2 = NULL;
-
-  buf = buf_new_with_capacity(5);
-  cp = "Testing. This is a moderately long Testing string.";
-  for (j = 0; cp[j]; j++)
-    write_to_buf(cp+j, 1, buf);
-  test_eq(0, buf_find_string_offset(buf, "Testing", 7));
-  test_eq(1, buf_find_string_offset(buf, "esting", 6));
-  test_eq(1, buf_find_string_offset(buf, "est", 3));
-  test_eq(39, buf_find_string_offset(buf, "ing str", 7));
-  test_eq(35, buf_find_string_offset(buf, "Testing str", 11));
-  test_eq(32, buf_find_string_offset(buf, "ng ", 3));
-  test_eq(43, buf_find_string_offset(buf, "string.", 7));
-  test_eq(-1, buf_find_string_offset(buf, "shrdlu", 6));
-  test_eq(-1, buf_find_string_offset(buf, "Testing thing", 13));
-  test_eq(-1, buf_find_string_offset(buf, "ngx", 3));
-  buf_free(buf);
-  buf = NULL;
-
-  /* Try adding a string too long for any freelist. */
-  {
-    char *cp = tor_malloc_zero(65536);
-    buf = buf_new();
-    write_to_buf(cp, 65536, buf);
-    tor_free(cp);
-
-    tt_int_op(buf_datalen(buf), ==, 65536);
-    buf_free(buf);
-    buf = NULL;
-  }
-
- done:
-  if (buf)
-    buf_free(buf);
-  if (buf2)
-    buf_free(buf2);
-}
-
 /** Run unit tests for the onion handshake code. */
 static void
 test_onion_handshake(void)
@@ -1620,6 +1002,34 @@ test_rend_fns(void)
   tor_free(intro_points_encrypted);
 }
 
+  /* Record odd numbered fake-IPs using ipv6, even numbered fake-IPs
+   * using ipv4.  Since our fake geoip database is the same between
+   * ipv4 and ipv6, we should get the same result no matter which
+   * address family we pick for each IP. */
+#define SET_TEST_ADDRESS(i) do {                \
+    if ((i) & 1) {                              \
+      SET_TEST_IPV6(i);                         \
+      tor_addr_from_in6(&addr, &in6);           \
+    } else {                                    \
+      tor_addr_from_ipv4h(&addr, (uint32_t) i); \
+    }                                           \
+  } while (0)
+
+  /* Make sure that country ID actually works. */
+#define SET_TEST_IPV6(i) \
+  do {                                                          \
+    set_uint32(in6.s6_addr + 12, htonl((uint32_t) (i)));        \
+  } while (0)
+#define CHECK_COUNTRY(country, val) do {                                \
+    /* test ipv4 country lookup */                                      \
+    test_streq(country,                                                 \
+               geoip_get_country_name(geoip_get_country_by_ipv4(val))); \
+    /* test ipv6 country lookup */                                      \
+    SET_TEST_IPV6(val);                                                 \
+    test_streq(country,                                                 \
+               geoip_get_country_name(geoip_get_country_by_ipv6(&in6))); \
+  } while (0)
+
 /** Run unit tests for GeoIP code. */
 static void
 test_geoip(void)
@@ -1630,7 +1040,8 @@ test_geoip(void)
   const char *bridge_stats_1 =
       "bridge-stats-end 2010-08-12 13:27:30 (86400 s)\n"
       "bridge-ips zz=24,xy=8\n"
-      "bridge-ip-versions v4=16,v6=16\n",
+      "bridge-ip-versions v4=16,v6=16\n"
+      "bridge-ip-transports <OR>=24\n",
   *dirreq_stats_1 =
       "dirreq-stats-end 2010-08-12 13:27:30 (86400 s)\n"
       "dirreq-v3-ips ab=8\n"
@@ -1694,21 +1105,6 @@ test_geoip(void)
   test_eq(4, geoip_get_n_countries());
   memset(&in6, 0, sizeof(in6));
 
-  /* Make sure that country ID actually works. */
-#define SET_TEST_IPV6(i) \
-  do {                                                          \
-    set_uint32(in6.s6_addr + 12, htonl((uint32_t) (i)));        \
-  } while (0)
-#define CHECK_COUNTRY(country, val) do {                                \
-    /* test ipv4 country lookup */                                      \
-    test_streq(country,                                                 \
-               geoip_get_country_name(geoip_get_country_by_ipv4(val))); \
-    /* test ipv6 country lookup */                                      \
-    SET_TEST_IPV6(val);                                                 \
-    test_streq(country,                                                 \
-               geoip_get_country_name(geoip_get_country_by_ipv6(&in6))); \
-  } while (0)
-
   CHECK_COUNTRY("??", 3);
   CHECK_COUNTRY("ab", 32);
   CHECK_COUNTRY("??", 5);
@@ -1721,40 +1117,25 @@ test_geoip(void)
   SET_TEST_IPV6(3);
   test_eq(0, geoip_get_country_by_ipv6(&in6));
 
-#undef CHECK_COUNTRY
-
-  /* Record odd numbered fake-IPs using ipv6, even numbered fake-IPs
-   * using ipv4.  Since our fake geoip database is the same between
-   * ipv4 and ipv6, we should get the same result no matter which
-   * address family we pick for each IP. */
-#define SET_TEST_ADDRESS(i) do {                \
-    if ((i) & 1) {                              \
-      SET_TEST_IPV6(i);                         \
-      tor_addr_from_in6(&addr, &in6);           \
-    } else {                                    \
-      tor_addr_from_ipv4h(&addr, (uint32_t) i); \
-    }                                           \
-  } while (0)
-
   get_options_mutable()->BridgeRelay = 1;
   get_options_mutable()->BridgeRecordUsageByCountry = 1;
   /* Put 9 observations in AB... */
   for (i=32; i < 40; ++i) {
     SET_TEST_ADDRESS(i);
-    geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, now-7200);
+    geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, NULL, now-7200);
   }
   SET_TEST_ADDRESS(225);
-  geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, now-7200);
+  geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, NULL, now-7200);
   /* and 3 observations in XY, several times. */
   for (j=0; j < 10; ++j)
     for (i=52; i < 55; ++i) {
       SET_TEST_ADDRESS(i);
-      geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, now-3600);
+      geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, NULL, now-3600);
     }
   /* and 17 observations in ZZ... */
   for (i=110; i < 127; ++i) {
     SET_TEST_ADDRESS(i);
-    geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, now);
+    geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, NULL, now);
   }
   geoip_get_client_history(GEOIP_CLIENT_CONNECT, &s, &v);
   test_assert(s);
@@ -1803,7 +1184,7 @@ test_geoip(void)
   /* Start testing dirreq statistics by making sure that we don't collect
    * dirreq stats without initializing them. */
   SET_TEST_ADDRESS(100);
-  geoip_note_client_seen(GEOIP_CLIENT_NETWORKSTATUS, &addr, now);
+  geoip_note_client_seen(GEOIP_CLIENT_NETWORKSTATUS, &addr, NULL, now);
   s = geoip_format_dirreq_stats(now + 86400);
   test_assert(!s);
 
@@ -1811,7 +1192,7 @@ test_geoip(void)
    * dirreq-stats history string. */
   geoip_dirreq_stats_init(now);
   SET_TEST_ADDRESS(100);
-  geoip_note_client_seen(GEOIP_CLIENT_NETWORKSTATUS, &addr, now);
+  geoip_note_client_seen(GEOIP_CLIENT_NETWORKSTATUS, &addr, NULL, now);
   s = geoip_format_dirreq_stats(now + 86400);
   test_streq(dirreq_stats_1, s);
   tor_free(s);
@@ -1820,7 +1201,7 @@ test_geoip(void)
    * don't generate a history string. */
   geoip_dirreq_stats_term();
   SET_TEST_ADDRESS(101);
-  geoip_note_client_seen(GEOIP_CLIENT_NETWORKSTATUS, &addr, now);
+  geoip_note_client_seen(GEOIP_CLIENT_NETWORKSTATUS, &addr, NULL, now);
   s = geoip_format_dirreq_stats(now + 86400);
   test_assert(!s);
 
@@ -1828,7 +1209,7 @@ test_geoip(void)
    * that we get an all empty history string. */
   geoip_dirreq_stats_init(now);
   SET_TEST_ADDRESS(100);
-  geoip_note_client_seen(GEOIP_CLIENT_NETWORKSTATUS, &addr, now);
+  geoip_note_client_seen(GEOIP_CLIENT_NETWORKSTATUS, &addr, NULL, now);
   geoip_reset_dirreq_stats(now);
   s = geoip_format_dirreq_stats(now + 86400);
   test_streq(dirreq_stats_2, s);
@@ -1855,7 +1236,7 @@ test_geoip(void)
   /* Start testing entry statistics by making sure that we don't collect
    * anything without initializing entry stats. */
   SET_TEST_ADDRESS(100);
-  geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, now);
+  geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, NULL, now);
   s = geoip_format_entry_stats(now + 86400);
   test_assert(!s);
 
@@ -1863,7 +1244,7 @@ test_geoip(void)
    * entry-stats history string. */
   geoip_entry_stats_init(now);
   SET_TEST_ADDRESS(100);
-  geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, now);
+  geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, NULL, now);
   s = geoip_format_entry_stats(now + 86400);
   test_streq(entry_stats_1, s);
   tor_free(s);
@@ -1872,7 +1253,7 @@ test_geoip(void)
    * don't generate a history string. */
   geoip_entry_stats_term();
   SET_TEST_ADDRESS(101);
-  geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, now);
+  geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, NULL, now);
   s = geoip_format_entry_stats(now + 86400);
   test_assert(!s);
 
@@ -1880,15 +1261,12 @@ test_geoip(void)
    * that we get an all empty history string. */
   geoip_entry_stats_init(now);
   SET_TEST_ADDRESS(100);
-  geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, now);
+  geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, NULL, now);
   geoip_reset_entry_stats(now);
   s = geoip_format_entry_stats(now + 86400);
   test_streq(entry_stats_2, s);
   tor_free(s);
 
-#undef SET_TEST_ADDRESS
-#undef SET_TEST_IPV6
-
   /* Stop collecting entry statistics. */
   geoip_entry_stats_term();
   get_options_mutable()->EntryStatistics = 0;
@@ -1898,6 +1276,78 @@ test_geoip(void)
   tor_free(v);
 }
 
+static void
+test_geoip_with_pt(void)
+{
+  time_t now = 1281533250; /* 2010-08-11 13:27:30 UTC */
+  char *s = NULL;
+  int i;
+  tor_addr_t addr;
+  struct in6_addr in6;
+
+  get_options_mutable()->BridgeRelay = 1;
+  get_options_mutable()->BridgeRecordUsageByCountry = 1;
+
+  /* No clients seen yet. */
+  s = geoip_get_transport_history();
+  tor_assert(!s);
+
+  /* 4 connections without a pluggable transport */
+  for (i=0; i < 4; ++i) {
+    SET_TEST_ADDRESS(i);
+    geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, NULL, now-7200);
+  }
+
+  /* 9 connections with "alpha" */
+  for (i=4; i < 13; ++i) {
+    SET_TEST_ADDRESS(i);
+    geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, "alpha", now-7200);
+  }
+
+  /* one connection with "beta" */
+  SET_TEST_ADDRESS(13);
+  geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, "beta", now-7200);
+
+  /* 14 connections with "charlie" */
+  for (i=14; i < 28; ++i) {
+    SET_TEST_ADDRESS(i);
+    geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, "charlie", now-7200);
+  }
+
+  /* 131 connections with "ddr" */
+  for (i=28; i < 159; ++i) {
+    SET_TEST_ADDRESS(i);
+    geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, "ddr", now-7200);
+  }
+
+  /* 8 connections with "entropy" */
+  for (i=159; i < 167; ++i) {
+    SET_TEST_ADDRESS(i);
+    geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, "entropy", now-7200);
+  }
+
+  /* 2 connections from the same IP with two different transports. */
+  SET_TEST_ADDRESS(++i);
+  geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, "fire", now-7200);
+  geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &addr, "google", now-7200);
+
+  /* Test the transport history string. */
+  s = geoip_get_transport_history();
+  tor_assert(s);
+  test_streq(s, "<OR>=8,alpha=16,beta=8,charlie=16,ddr=136,entropy=8,fire=8,google=8");
+
+  /* Stop collecting entry statistics. */
+  geoip_entry_stats_term();
+  get_options_mutable()->EntryStatistics = 0;
+
+ done:
+  tor_free(s);
+}
+
+#undef SET_TEST_ADDRESS
+#undef SET_TEST_IPV6
+#undef CHECK_COUNTRY
+
 /** Run unit tests for stats code. */
 static void
 test_stats(void)
@@ -2088,8 +1538,6 @@ const struct testcase_setup_t legacy_setup = {
   { #name, legacy_test_helper, TT_FORK, &legacy_setup, test_ ## name }
 
 static struct testcase_t test_array[] = {
-  ENT(buffers),
-  { "buffer_copy", test_buffer_copy, 0, NULL, NULL },
   ENT(onion_handshake),
   { "bad_onion_handshake", test_bad_onion_handshake, 0, NULL, NULL },
 #ifdef CURVE25519_ENABLED
@@ -2099,29 +1547,14 @@ static struct testcase_t test_array[] = {
   ENT(policies),
   ENT(rend_fns),
   ENT(geoip),
+  FORK(geoip_with_pt),
   FORK(stats),
 
   END_OF_TESTCASES
 };
 
-#define SOCKSENT(name)                                  \
-  { #name, test_socks_##name, TT_FORK, &socks_setup, NULL }
-
-static struct testcase_t socks_tests[] = {
-  SOCKSENT(4_unsupported_commands),
-  SOCKSENT(4_supported_commands),
-
-  SOCKSENT(5_unsupported_commands),
-  SOCKSENT(5_supported_commands),
-  SOCKSENT(5_no_authenticate),
-  SOCKSENT(5_auth_before_negotiation),
-  SOCKSENT(5_authenticate),
-  SOCKSENT(5_authenticate_with_data),
-
-  END_OF_TESTCASES
-};
-
 extern struct testcase_t addr_tests[];
+extern struct testcase_t buffer_tests[];
 extern struct testcase_t crypto_tests[];
 extern struct testcase_t container_tests[];
 extern struct testcase_t util_tests[];
@@ -2136,9 +1569,12 @@ extern struct testcase_t circuitlist_tests[];
 extern struct testcase_t circuitmux_tests[];
 extern struct testcase_t cell_queue_tests[];
 extern struct testcase_t options_tests[];
+extern struct testcase_t socks_tests[];
+extern struct testcase_t extorport_tests[];
 
 static struct testgroup_t testgroups[] = {
   { "", test_array },
+  { "buffer/", buffer_tests },
   { "socks/", socks_tests },
   { "addr/", addr_tests },
   { "crypto/", crypto_tests },
@@ -2155,6 +1591,7 @@ static struct testgroup_t testgroups[] = {
   { "circuitlist/", circuitlist_tests },
   { "circuitmux/", circuitmux_tests },
   { "options/", options_tests },
+  { "extorport/", extorport_tests },
   END_OF_GROUPS
 };
 

+ 342 - 0
src/test/test_buffers.c

@@ -0,0 +1,342 @@
+/* Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#define BUFFERS_PRIVATE
+#include "or.h"
+#include "buffers.h"
+#include "ext_orport.h"
+#include "test.h"
+
+/** Run unit tests for buffers.c */
+static void
+test_buffers_basic(void *arg)
+{
+  char str[256];
+  char str2[256];
+
+  buf_t *buf = NULL, *buf2 = NULL;
+  const char *cp;
+
+  int j;
+  size_t r;
+  (void) arg;
+
+  /****
+   * buf_new
+   ****/
+  if (!(buf = buf_new()))
+    test_fail();
+
+  //test_eq(buf_capacity(buf), 4096);
+  test_eq(buf_datalen(buf), 0);
+
+  /****
+   * General pointer frobbing
+   */
+  for (j=0;j<256;++j) {
+    str[j] = (char)j;
+  }
+  write_to_buf(str, 256, buf);
+  write_to_buf(str, 256, buf);
+  test_eq(buf_datalen(buf), 512);
+  fetch_from_buf(str2, 200, buf);
+  test_memeq(str, str2, 200);
+  test_eq(buf_datalen(buf), 312);
+  memset(str2, 0, sizeof(str2));
+
+  fetch_from_buf(str2, 256, buf);
+  test_memeq(str+200, str2, 56);
+  test_memeq(str, str2+56, 200);
+  test_eq(buf_datalen(buf), 56);
+  memset(str2, 0, sizeof(str2));
+  /* Okay, now we should be 512 bytes into the 4096-byte buffer.  If we add
+   * another 3584 bytes, we hit the end. */
+  for (j=0;j<15;++j) {
+    write_to_buf(str, 256, buf);
+  }
+  assert_buf_ok(buf);
+  test_eq(buf_datalen(buf), 3896);
+  fetch_from_buf(str2, 56, buf);
+  test_eq(buf_datalen(buf), 3840);
+  test_memeq(str+200, str2, 56);
+  for (j=0;j<15;++j) {
+    memset(str2, 0, sizeof(str2));
+    fetch_from_buf(str2, 256, buf);
+    test_memeq(str, str2, 256);
+  }
+  test_eq(buf_datalen(buf), 0);
+  buf_free(buf);
+  buf = NULL;
+
+  /* Okay, now make sure growing can work. */
+  buf = buf_new_with_capacity(16);
+  //test_eq(buf_capacity(buf), 16);
+  write_to_buf(str+1, 255, buf);
+  //test_eq(buf_capacity(buf), 256);
+  fetch_from_buf(str2, 254, buf);
+  test_memeq(str+1, str2, 254);
+  //test_eq(buf_capacity(buf), 256);
+  assert_buf_ok(buf);
+  write_to_buf(str, 32, buf);
+  //test_eq(buf_capacity(buf), 256);
+  assert_buf_ok(buf);
+  write_to_buf(str, 256, buf);
+  assert_buf_ok(buf);
+  //test_eq(buf_capacity(buf), 512);
+  test_eq(buf_datalen(buf), 33+256);
+  fetch_from_buf(str2, 33, buf);
+  test_eq(*str2, str[255]);
+
+  test_memeq(str2+1, str, 32);
+  //test_eq(buf_capacity(buf), 512);
+  test_eq(buf_datalen(buf), 256);
+  fetch_from_buf(str2, 256, buf);
+  test_memeq(str, str2, 256);
+
+  /* now try shrinking: case 1. */
+  buf_free(buf);
+  buf = buf_new_with_capacity(33668);
+  for (j=0;j<67;++j) {
+    write_to_buf(str,255, buf);
+  }
+  //test_eq(buf_capacity(buf), 33668);
+  test_eq(buf_datalen(buf), 17085);
+  for (j=0; j < 40; ++j) {
+    fetch_from_buf(str2, 255,buf);
+    test_memeq(str2, str, 255);
+  }
+
+  /* now try shrinking: case 2. */
+  buf_free(buf);
+  buf = buf_new_with_capacity(33668);
+  for (j=0;j<67;++j) {
+    write_to_buf(str,255, buf);
+  }
+  for (j=0; j < 20; ++j) {
+    fetch_from_buf(str2, 255,buf);
+    test_memeq(str2, str, 255);
+  }
+  for (j=0;j<80;++j) {
+    write_to_buf(str,255, buf);
+  }
+  //test_eq(buf_capacity(buf),33668);
+  for (j=0; j < 120; ++j) {
+    fetch_from_buf(str2, 255,buf);
+    test_memeq(str2, str, 255);
+  }
+
+  /* Move from buf to buf. */
+  buf_free(buf);
+  buf = buf_new_with_capacity(4096);
+  buf2 = buf_new_with_capacity(4096);
+  for (j=0;j<100;++j)
+    write_to_buf(str, 255, buf);
+  test_eq(buf_datalen(buf), 25500);
+  for (j=0;j<100;++j) {
+    r = 10;
+    move_buf_to_buf(buf2, buf, &r);
+    test_eq(r, 0);
+  }
+  test_eq(buf_datalen(buf), 24500);
+  test_eq(buf_datalen(buf2), 1000);
+  for (j=0;j<3;++j) {
+    fetch_from_buf(str2, 255, buf2);
+    test_memeq(str2, str, 255);
+  }
+  r = 8192; /*big move*/
+  move_buf_to_buf(buf2, buf, &r);
+  test_eq(r, 0);
+  r = 30000; /* incomplete move */
+  move_buf_to_buf(buf2, buf, &r);
+  test_eq(r, 13692);
+  for (j=0;j<97;++j) {
+    fetch_from_buf(str2, 255, buf2);
+    test_memeq(str2, str, 255);
+  }
+  buf_free(buf);
+  buf_free(buf2);
+  buf = buf2 = NULL;
+
+  buf = buf_new_with_capacity(5);
+  cp = "Testing. This is a moderately long Testing string.";
+  for (j = 0; cp[j]; j++)
+    write_to_buf(cp+j, 1, buf);
+  test_eq(0, buf_find_string_offset(buf, "Testing", 7));
+  test_eq(1, buf_find_string_offset(buf, "esting", 6));
+  test_eq(1, buf_find_string_offset(buf, "est", 3));
+  test_eq(39, buf_find_string_offset(buf, "ing str", 7));
+  test_eq(35, buf_find_string_offset(buf, "Testing str", 11));
+  test_eq(32, buf_find_string_offset(buf, "ng ", 3));
+  test_eq(43, buf_find_string_offset(buf, "string.", 7));
+  test_eq(-1, buf_find_string_offset(buf, "shrdlu", 6));
+  test_eq(-1, buf_find_string_offset(buf, "Testing thing", 13));
+  test_eq(-1, buf_find_string_offset(buf, "ngx", 3));
+  buf_free(buf);
+  buf = NULL;
+
+  /* Try adding a string too long for any freelist. */
+  {
+    char *cp = tor_malloc_zero(65536);
+    buf = buf_new();
+    write_to_buf(cp, 65536, buf);
+    tor_free(cp);
+
+    tt_int_op(buf_datalen(buf), ==, 65536);
+    buf_free(buf);
+    buf = NULL;
+  }
+
+ done:
+  if (buf)
+    buf_free(buf);
+  if (buf2)
+    buf_free(buf2);
+}
+static void
+test_buffer_copy(void *arg)
+{
+  generic_buffer_t *buf=NULL, *buf2=NULL;
+  const char *s;
+  size_t len;
+  char b[256];
+  int i;
+  (void)arg;
+
+  buf = generic_buffer_new();
+  tt_assert(buf);
+
+  /* Copy an empty buffer. */
+  tt_int_op(0, ==, generic_buffer_set_to_copy(&buf2, buf));
+  tt_assert(buf2);
+  tt_int_op(0, ==, generic_buffer_len(buf2));
+
+  /* Now try with a short buffer. */
+  s = "And now comes an act of enormous enormance!";
+  len = strlen(s);
+  generic_buffer_add(buf, s, len);
+  tt_int_op(len, ==, generic_buffer_len(buf));
+  /* Add junk to buf2 so we can test replacing.*/
+  generic_buffer_add(buf2, "BLARG", 5);
+  tt_int_op(0, ==, generic_buffer_set_to_copy(&buf2, buf));
+  tt_int_op(len, ==, generic_buffer_len(buf2));
+  generic_buffer_get(buf2, b, len);
+  test_mem_op(b, ==, s, len);
+  /* Now free buf2 and retry so we can test allocating */
+  generic_buffer_free(buf2);
+  buf2 = NULL;
+  tt_int_op(0, ==, generic_buffer_set_to_copy(&buf2, buf));
+  tt_int_op(len, ==, generic_buffer_len(buf2));
+  generic_buffer_get(buf2, b, len);
+  test_mem_op(b, ==, s, len);
+  /* Clear buf for next test */
+  generic_buffer_get(buf, b, len);
+  tt_int_op(generic_buffer_len(buf),==,0);
+
+  /* Okay, now let's try a bigger buffer. */
+  s = "Quis autem vel eum iure reprehenderit qui in ea voluptate velit "
+    "esse quam nihil molestiae consequatur, vel illum qui dolorem eum "
+    "fugiat quo voluptas nulla pariatur?";
+  len = strlen(s);
+  for (i = 0; i < 256; ++i) {
+    b[0]=i;
+    generic_buffer_add(buf, b, 1);
+    generic_buffer_add(buf, s, len);
+  }
+  tt_int_op(0, ==, generic_buffer_set_to_copy(&buf2, buf));
+  tt_int_op(generic_buffer_len(buf2), ==, generic_buffer_len(buf));
+  for (i = 0; i < 256; ++i) {
+    generic_buffer_get(buf2, b, len+1);
+    tt_int_op((unsigned char)b[0],==,i);
+    test_mem_op(b+1, ==, s, len);
+  }
+
+ done:
+  if (buf)
+    generic_buffer_free(buf);
+  if (buf2)
+    generic_buffer_free(buf2);
+}
+
+static void
+test_buffer_ext_or_cmd(void *arg)
+{
+  ext_or_cmd_t *cmd = NULL;
+  generic_buffer_t *buf = generic_buffer_new();
+  char *tmp = NULL;
+  (void) arg;
+
+  /* Empty -- should give "not there. */
+  tt_int_op(0, ==, generic_buffer_fetch_ext_or_cmd(buf, &cmd));
+  tt_ptr_op(NULL, ==, cmd);
+
+  /* Three bytes: shouldn't work. */
+  generic_buffer_add(buf, "\x00\x20\x00", 3);
+  tt_int_op(0, ==, generic_buffer_fetch_ext_or_cmd(buf, &cmd));
+  tt_ptr_op(NULL, ==, cmd);
+  tt_int_op(3, ==, generic_buffer_len(buf));
+
+  /* 0020 0000: That's a nil command. It should work. */
+  generic_buffer_add(buf, "\x00", 1);
+  tt_int_op(1, ==, generic_buffer_fetch_ext_or_cmd(buf, &cmd));
+  tt_ptr_op(NULL, !=, cmd);
+  tt_int_op(0x20, ==, cmd->cmd);
+  tt_int_op(0, ==, cmd->len);
+  tt_int_op(0, ==, generic_buffer_len(buf));
+  ext_or_cmd_free(cmd);
+  cmd = NULL;
+
+  /* Now try a length-6 command with one byte missing. */
+  generic_buffer_add(buf, "\x10\x21\x00\x06""abcde", 9);
+  tt_int_op(0, ==, generic_buffer_fetch_ext_or_cmd(buf, &cmd));
+  tt_ptr_op(NULL, ==, cmd);
+  generic_buffer_add(buf, "f", 1);
+  tt_int_op(1, ==, generic_buffer_fetch_ext_or_cmd(buf, &cmd));
+  tt_ptr_op(NULL, !=, cmd);
+  tt_int_op(0x1021, ==, cmd->cmd);
+  tt_int_op(6, ==, cmd->len);
+  test_mem_op("abcdef", ==, cmd->body, 6);
+  tt_int_op(0, ==, generic_buffer_len(buf));
+  ext_or_cmd_free(cmd);
+  cmd = NULL;
+
+  /* Now try a length-10 command with 4 extra bytes. */
+  generic_buffer_add(buf, "\xff\xff\x00\x0a"
+                     "loremipsum\x10\x00\xff\xff", 18);
+  tt_int_op(1, ==, generic_buffer_fetch_ext_or_cmd(buf, &cmd));
+  tt_ptr_op(NULL, !=, cmd);
+  tt_int_op(0xffff, ==, cmd->cmd);
+  tt_int_op(10, ==, cmd->len);
+  test_mem_op("loremipsum", ==, cmd->body, 10);
+  tt_int_op(4, ==, generic_buffer_len(buf));
+  ext_or_cmd_free(cmd);
+  cmd = NULL;
+
+  /* Finally, let's try a maximum-length command. We already have the header
+   * waiting. */
+  tt_int_op(0, ==, generic_buffer_fetch_ext_or_cmd(buf, &cmd));
+  tmp = tor_malloc_zero(65535);
+  generic_buffer_add(buf, tmp, 65535);
+  tt_int_op(1, ==, generic_buffer_fetch_ext_or_cmd(buf, &cmd));
+  tt_ptr_op(NULL, !=, cmd);
+  tt_int_op(0x1000, ==, cmd->cmd);
+  tt_int_op(0xffff, ==, cmd->len);
+  test_mem_op(tmp, ==, cmd->body, 65535);
+  tt_int_op(0, ==, generic_buffer_len(buf));
+  ext_or_cmd_free(cmd);
+  cmd = NULL;
+
+ done:
+  ext_or_cmd_free(cmd);
+  generic_buffer_free(buf);
+  tor_free(tmp);
+}
+
+struct testcase_t buffer_tests[] = {
+  { "basic", test_buffers_basic, 0, NULL, NULL },
+  { "copy", test_buffer_copy, 0, NULL, NULL },
+  { "ext_or_cmd", test_buffer_ext_or_cmd, 0, NULL, NULL },
+  END_OF_TESTCASES
+};
+

+ 604 - 0
src/test/test_extorport.c

@@ -0,0 +1,604 @@
+/* Copyright (c) 2013, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#define CONNECTION_PRIVATE
+#define EXT_ORPORT_PRIVATE
+#define MAIN_PRIVATE
+#include "or.h"
+#include "buffers.h"
+#include "connection.h"
+#include "connection_or.h"
+#include "config.h"
+#include "control.h"
+#include "ext_orport.h"
+#include "main.h"
+#include "test.h"
+
+/* Test connection_or_remove_from_ext_or_id_map and
+ * connection_or_set_ext_or_identifier */
+static void
+test_ext_or_id_map(void *arg)
+{
+  or_connection_t *c1 = NULL, *c2 = NULL, *c3 = NULL;
+  char *idp = NULL, *idp2 = NULL;
+  (void)arg;
+
+  /* pre-initialization */
+  tt_ptr_op(NULL, ==, connection_or_get_by_ext_or_id("xxxxxxxxxxxxxxxxxxxx"));
+
+  c1 = or_connection_new(CONN_TYPE_EXT_OR, AF_INET);
+  c2 = or_connection_new(CONN_TYPE_EXT_OR, AF_INET);
+  c3 = or_connection_new(CONN_TYPE_OR, AF_INET);
+
+  tt_ptr_op(c1->ext_or_conn_id, !=, NULL);
+  tt_ptr_op(c2->ext_or_conn_id, !=, NULL);
+  tt_ptr_op(c3->ext_or_conn_id, ==, NULL);
+
+  tt_ptr_op(c1, ==, connection_or_get_by_ext_or_id(c1->ext_or_conn_id));
+  tt_ptr_op(c2, ==, connection_or_get_by_ext_or_id(c2->ext_or_conn_id));
+  tt_ptr_op(NULL, ==, connection_or_get_by_ext_or_id("xxxxxxxxxxxxxxxxxxxx"));
+
+  idp = tor_memdup(c2->ext_or_conn_id, EXT_OR_CONN_ID_LEN);
+
+  /* Give c2 a new ID. */
+  connection_or_set_ext_or_identifier(c2);
+  test_mem_op(idp, !=, c2->ext_or_conn_id, EXT_OR_CONN_ID_LEN);
+  idp2 = tor_memdup(c2->ext_or_conn_id, EXT_OR_CONN_ID_LEN);
+  tt_assert(!tor_digest_is_zero(idp2));
+
+  tt_ptr_op(NULL, ==, connection_or_get_by_ext_or_id(idp));
+  tt_ptr_op(c2, ==, connection_or_get_by_ext_or_id(idp2));
+
+  /* Now remove it. */
+  connection_or_remove_from_ext_or_id_map(c2);
+  tt_ptr_op(NULL, ==, connection_or_get_by_ext_or_id(idp));
+  tt_ptr_op(NULL, ==, connection_or_get_by_ext_or_id(idp2));
+
+ done:
+  if (c1)
+    connection_free_(TO_CONN(c1));
+  if (c2)
+    connection_free_(TO_CONN(c2));
+  if (c3)
+    connection_free_(TO_CONN(c3));
+  tor_free(idp);
+  tor_free(idp2);
+  connection_or_clear_ext_or_id_map();
+}
+
+/* Simple connection_write_to_buf_impl_ replacement that unconditionally
+ * writes to outbuf. */
+static void
+connection_write_to_buf_impl_replacement(const char *string, size_t len,
+                                         connection_t *conn, int zlib)
+{
+  (void) zlib;
+
+  tor_assert(string);
+  tor_assert(conn);
+  write_to_buf(string, len, conn->outbuf);
+}
+
+static char *
+buf_get_contents(buf_t *buf, size_t *sz_out)
+{
+  char *out;
+  *sz_out = buf_datalen(buf);
+  if (*sz_out >= ULONG_MAX)
+    return NULL; /* C'mon, really? */
+  out = tor_malloc(*sz_out + 1);
+  if (fetch_from_buf(out, (unsigned long)*sz_out, buf) != 0) {
+    tor_free(out);
+    return NULL;
+  }
+  out[*sz_out] = '\0'; /* Hopefully gratuitous. */
+  return out;
+}
+
+static void
+test_ext_or_write_command(void *arg)
+{
+  or_connection_t *c1;
+  char *cp = NULL;
+  char *buf = NULL;
+  size_t sz;
+
+  (void) arg;
+  MOCK(connection_write_to_buf_impl_,
+       connection_write_to_buf_impl_replacement);
+
+  c1 = or_connection_new(CONN_TYPE_EXT_OR, AF_INET);
+  tt_assert(c1);
+
+  /* Length too long */
+  tt_int_op(connection_write_ext_or_command(TO_CONN(c1), 100, "X", 100000),
+            <, 0);
+
+  /* Empty command */
+  tt_int_op(connection_write_ext_or_command(TO_CONN(c1), 0x99, NULL, 0),
+            ==, 0);
+  cp = buf_get_contents(TO_CONN(c1)->outbuf, &sz);
+  tt_int_op(sz, ==, 4);
+  test_mem_op(cp, ==, "\x00\x99\x00\x00", 4);
+  tor_free(cp);
+
+  /* Medium command. */
+  tt_int_op(connection_write_ext_or_command(TO_CONN(c1), 0x99,
+                                            "Wai\0Hello", 9), ==, 0);
+  cp = buf_get_contents(TO_CONN(c1)->outbuf, &sz);
+  tt_int_op(sz, ==, 13);
+  test_mem_op(cp, ==, "\x00\x99\x00\x09Wai\x00Hello", 13);
+  tor_free(cp);
+
+  /* Long command */
+  buf = tor_malloc(65535);
+  memset(buf, 'x', 65535);
+  tt_int_op(connection_write_ext_or_command(TO_CONN(c1), 0xf00d,
+                                            buf, 65535), ==, 0);
+  cp = buf_get_contents(TO_CONN(c1)->outbuf, &sz);
+  tt_int_op(sz, ==, 65539);
+  test_mem_op(cp, ==, "\xf0\x0d\xff\xff", 4);
+  test_mem_op(cp+4, ==, buf, 65535);
+  tor_free(cp);
+
+ done:
+  if (c1)
+    connection_free_(TO_CONN(c1));
+  tor_free(cp);
+  tor_free(buf);
+  UNMOCK(connection_write_to_buf_impl_);
+}
+
+static int
+write_bytes_to_file_fail(const char *fname, const char *str, size_t len,
+                         int bin)
+{
+  (void) fname;
+  (void) str;
+  (void) len;
+  (void) bin;
+
+  return -1;
+}
+
+static void
+test_ext_or_init_auth(void *arg)
+{
+  or_options_t *options = get_options_mutable();
+  const char *fn;
+  char *cp = NULL;
+  struct stat st;
+  char cookie0[32];
+  (void)arg;
+
+  /* Check default filename location */
+  options->DataDirectory = tor_strdup("foo");
+  cp = get_ext_or_auth_cookie_file_name();
+  tt_str_op(cp, ==, "foo"PATH_SEPARATOR"extended_orport_auth_cookie");
+  tor_free(cp);
+
+  /* Shouldn't be initialized already, or our tests will be a bit
+   * meaningless */
+  ext_or_auth_cookie = tor_malloc_zero(32);
+  test_assert(tor_mem_is_zero((char*)ext_or_auth_cookie, 32));
+
+  /* Now make sure we use a temporary file */
+  fn = get_fname("ext_cookie_file");
+  options->ExtORPortCookieAuthFile = tor_strdup(fn);
+  cp = get_ext_or_auth_cookie_file_name();
+  tt_str_op(cp, ==, fn);
+  tor_free(cp);
+
+  /* Test the initialization function with a broken
+     write_bytes_to_file(). See if the problem is handled properly. */
+  MOCK(write_bytes_to_file, write_bytes_to_file_fail);
+  tt_int_op(-1, ==, init_ext_or_cookie_authentication(1));
+  tt_int_op(ext_or_auth_cookie_is_set, ==, 0);
+  UNMOCK(write_bytes_to_file);
+
+  /* Now do the actual initialization. */
+  tt_int_op(0, ==, init_ext_or_cookie_authentication(1));
+  tt_int_op(ext_or_auth_cookie_is_set, ==, 1);
+  cp = read_file_to_str(fn, RFTS_BIN, &st);
+  tt_ptr_op(cp, !=, NULL);
+  tt_int_op(st.st_size, ==, 64);
+  test_memeq(cp, "! Extended ORPort Auth Cookie !\x0a", 32);
+  test_memeq(cp+32, ext_or_auth_cookie, 32);
+  memcpy(cookie0, ext_or_auth_cookie, 32);
+  test_assert(!tor_mem_is_zero((char*)ext_or_auth_cookie, 32));
+
+  /* Operation should be idempotent. */
+  tt_int_op(0, ==, init_ext_or_cookie_authentication(1));
+  test_memeq(cookie0, ext_or_auth_cookie, 32);
+
+ done:
+  tor_free(cp);
+  ext_orport_free_all();
+}
+
+static void
+test_ext_or_cookie_auth(void *arg)
+{
+  char *reply=NULL, *reply2=NULL, *client_hash=NULL, *client_hash2=NULL;
+  size_t reply_len=0;
+  char hmac1[32], hmac2[32];
+
+  const char client_nonce[32] =
+    "Who is the third who walks alway";
+  char server_hash_input[] =
+    "ExtORPort authentication server-to-client hash"
+    "Who is the third who walks alway"
+    "................................";
+  char client_hash_input[] =
+    "ExtORPort authentication client-to-server hash"
+    "Who is the third who walks alway"
+    "................................";
+
+  (void)arg;
+
+  tt_int_op(strlen(client_hash_input), ==, 46+32+32);
+  tt_int_op(strlen(server_hash_input), ==, 46+32+32);
+
+  ext_or_auth_cookie = tor_malloc_zero(32);
+  memcpy(ext_or_auth_cookie, "s beside you? When I count, ther", 32);
+  ext_or_auth_cookie_is_set = 1;
+
+  /* For this authentication, the client sends 32 random bytes (ClientNonce)
+   * The server replies with 32 byte ServerHash and 32 byte ServerNonce,
+   * where ServerHash is:
+   * HMAC-SHA256(CookieString,
+   *   "ExtORPort authentication server-to-client hash" | ClientNonce |
+   *    ServerNonce)"
+   * The client must reply with 32-byte ClientHash, which we compute as:
+   *   ClientHash is computed as:
+   *        HMAC-SHA256(CookieString,
+   *           "ExtORPort authentication client-to-server hash" | ClientNonce |
+   *            ServerNonce)
+   */
+
+  /* Wrong length */
+  tt_int_op(-1, ==,
+            handle_client_auth_nonce(client_nonce, 33, &client_hash, &reply,
+                                     &reply_len));
+  tt_int_op(-1, ==,
+            handle_client_auth_nonce(client_nonce, 31, &client_hash, &reply,
+                                     &reply_len));
+
+  /* Now let's try this for real! */
+  tt_int_op(0, ==,
+            handle_client_auth_nonce(client_nonce, 32, &client_hash, &reply,
+                                     &reply_len));
+  tt_int_op(reply_len, ==, 64);
+  tt_ptr_op(reply, !=, NULL);
+  tt_ptr_op(client_hash, !=, NULL);
+  /* Fill in the server nonce into the hash inputs... */
+  memcpy(server_hash_input+46+32, reply+32, 32);
+  memcpy(client_hash_input+46+32, reply+32, 32);
+  /* Check the HMACs are correct... */
+  crypto_hmac_sha256(hmac1, (char*)ext_or_auth_cookie, 32, server_hash_input,
+                     46+32+32);
+  crypto_hmac_sha256(hmac2, (char*)ext_or_auth_cookie, 32, client_hash_input,
+                     46+32+32);
+  test_memeq(hmac1, reply, 32);
+  test_memeq(hmac2, client_hash, 32);
+
+  /* Now do it again and make sure that the results are *different* */
+  tt_int_op(0, ==,
+            handle_client_auth_nonce(client_nonce, 32, &client_hash2, &reply2,
+                                     &reply_len));
+  test_memneq(reply2, reply, reply_len);
+  test_memneq(client_hash2, client_hash, 32);
+  /* But that this one checks out too. */
+  memcpy(server_hash_input+46+32, reply2+32, 32);
+  memcpy(client_hash_input+46+32, reply2+32, 32);
+  /* Check the HMACs are correct... */
+  crypto_hmac_sha256(hmac1, (char*)ext_or_auth_cookie, 32, server_hash_input,
+                     46+32+32);
+  crypto_hmac_sha256(hmac2, (char*)ext_or_auth_cookie, 32, client_hash_input,
+                     46+32+32);
+  test_memeq(hmac1, reply2, 32);
+  test_memeq(hmac2, client_hash2, 32);
+
+ done:
+  tor_free(reply);
+  tor_free(client_hash);
+  tor_free(reply2);
+  tor_free(client_hash2);
+}
+
+static int
+crypto_rand_return_tse_str(char *to, size_t n)
+{
+  if (n != 32) {
+    TT_FAIL(("Asked for %d bytes, not 32", (int)n));
+    return -1;
+  }
+  memcpy(to, "te road There is always another ", 32);
+  return 0;
+}
+
+static void
+test_ext_or_cookie_auth_testvec(void *arg)
+{
+  char *reply=NULL, *client_hash=NULL;
+  size_t reply_len;
+  char *mem_op_hex_tmp=NULL;
+
+  const char client_nonce[] = "But when I look ahead up the whi";
+  (void)arg;
+
+  ext_or_auth_cookie = tor_malloc_zero(32);
+  memcpy(ext_or_auth_cookie, "Gliding wrapt in a brown mantle," , 32);
+  ext_or_auth_cookie_is_set = 1;
+
+  MOCK(crypto_rand, crypto_rand_return_tse_str);
+
+  tt_int_op(0, ==,
+            handle_client_auth_nonce(client_nonce, 32, &client_hash, &reply,
+                                     &reply_len));
+  tt_ptr_op(reply, !=, NULL );
+  tt_ptr_op(reply_len, ==, 64);
+  test_memeq(reply+32, "te road There is always another ", 32);
+  /* HMACSHA256("Gliding wrapt in a brown mantle,"
+   *     "ExtORPort authentication server-to-client hash"
+   *     "But when I look ahead up the write road There is always another ");
+   */
+  test_memeq_hex(reply,
+                 "ec80ed6e546d3b36fdfc22fe1315416b"
+                 "029f1ade7610d910878b62eeb7403821");
+  /* HMACSHA256("Gliding wrapt in a brown mantle,"
+   *     "ExtORPort authentication client-to-server hash"
+   *     "But when I look ahead up the write road There is always another ");
+   * (Both values computed using Python CLI.)
+   */
+  test_memeq_hex(client_hash,
+                 "ab391732dd2ed968cd40c087d1b1f25b"
+                 "33b3cd77ff79bd80c2074bbf438119a2");
+
+ done:
+  UNMOCK(crypto_rand);
+  tor_free(reply);
+  tor_free(client_hash);
+  tor_free(mem_op_hex_tmp);
+}
+
+static void
+ignore_bootstrap_problem(const char *warn, int reason)
+{
+  (void)warn;
+  (void)reason;
+}
+
+static int is_reading = 1;
+static int handshake_start_called = 0;
+
+static void
+note_read_stopped(connection_t *conn)
+{
+  (void)conn;
+  is_reading=0;
+}
+static void
+note_read_started(connection_t *conn)
+{
+  (void)conn;
+  is_reading=1;
+}
+static int
+handshake_start(or_connection_t *conn, int receiving)
+{
+  if (!conn || !receiving)
+    TT_FAIL(("Bad arguments to handshake_start"));
+  handshake_start_called = 1;
+  return 0;
+}
+
+#define WRITE(s,n)                                                      \
+  do {                                                                  \
+    write_to_buf((s), (n), TO_CONN(conn)->inbuf);                       \
+  } while (0)
+#define CONTAINS(s,n)                                           \
+  do {                                                          \
+    tt_int_op((n), <=, sizeof(b));                              \
+    tt_int_op(buf_datalen(TO_CONN(conn)->outbuf), ==, (n));     \
+    if ((n)) {                                                  \
+      fetch_from_buf(b, (n), TO_CONN(conn)->outbuf);            \
+      test_memeq(b, (s), (n));                                  \
+    }                                                           \
+  } while (0)
+
+/* Helper: Do a successful Extended ORPort authentication handshake. */
+static void
+do_ext_or_handshake(or_connection_t *conn)
+{
+  char b[256];
+
+  tt_int_op(0, ==, connection_ext_or_start_auth(conn));
+  CONTAINS("\x01\x00", 2);
+  WRITE("\x01", 1);
+  WRITE("But when I look ahead up the whi", 32);
+  MOCK(crypto_rand, crypto_rand_return_tse_str);
+  tt_int_op(0, ==, connection_ext_or_process_inbuf(conn));
+  UNMOCK(crypto_rand);
+  tt_int_op(TO_CONN(conn)->state, ==, EXT_OR_CONN_STATE_AUTH_WAIT_CLIENT_HASH);
+  CONTAINS("\xec\x80\xed\x6e\x54\x6d\x3b\x36\xfd\xfc\x22\xfe\x13\x15\x41\x6b"
+           "\x02\x9f\x1a\xde\x76\x10\xd9\x10\x87\x8b\x62\xee\xb7\x40\x38\x21"
+           "te road There is always another ", 64);
+  /* Send the right response this time. */
+  WRITE("\xab\x39\x17\x32\xdd\x2e\xd9\x68\xcd\x40\xc0\x87\xd1\xb1\xf2\x5b"
+        "\x33\xb3\xcd\x77\xff\x79\xbd\x80\xc2\x07\x4b\xbf\x43\x81\x19\xa2",
+        32);
+  tt_int_op(0, ==, connection_ext_or_process_inbuf(conn));
+  CONTAINS("\x01", 1);
+  tt_assert(! TO_CONN(conn)->marked_for_close);
+  tt_int_op(TO_CONN(conn)->state, ==, EXT_OR_CONN_STATE_OPEN);
+
+ done: ;
+}
+
+static void
+test_ext_or_handshake(void *arg)
+{
+  or_connection_t *conn=NULL;
+  char b[256];
+
+  (void) arg;
+  MOCK(connection_write_to_buf_impl_,
+       connection_write_to_buf_impl_replacement);
+  /* Use same authenticators as for test_ext_or_cookie_auth_testvec */
+  ext_or_auth_cookie = tor_malloc_zero(32);
+  memcpy(ext_or_auth_cookie, "Gliding wrapt in a brown mantle," , 32);
+  ext_or_auth_cookie_is_set = 1;
+
+  init_connection_lists();
+
+  conn = or_connection_new(CONN_TYPE_EXT_OR, AF_INET);
+  tt_int_op(0, ==, connection_ext_or_start_auth(conn));
+  /* The server starts by telling us about the one supported authtype. */
+  CONTAINS("\x01\x00", 2);
+  /* Say the client hasn't responded yet. */
+  tt_int_op(0, ==, connection_ext_or_process_inbuf(conn));
+  /* Let's say the client replies badly. */
+  WRITE("\x99", 1);
+  tt_int_op(-1, ==, connection_ext_or_process_inbuf(conn));
+  CONTAINS("", 0);
+  tt_assert(TO_CONN(conn)->marked_for_close);
+  close_closeable_connections();
+  conn = NULL;
+
+  /* Okay, try again. */
+  conn = or_connection_new(CONN_TYPE_EXT_OR, AF_INET);
+  tt_int_op(0, ==, connection_ext_or_start_auth(conn));
+  CONTAINS("\x01\x00", 2);
+  /* Let's say the client replies sensibly this time. "Yes, AUTHTYPE_COOKIE
+   * sounds delicious. Let's have some of that!" */
+  WRITE("\x01", 1);
+  /* Let's say that the client also sends part of a nonce. */
+  WRITE("But when I look ", 16);
+  tt_int_op(0, ==, connection_ext_or_process_inbuf(conn));
+  CONTAINS("", 0);
+  tt_int_op(TO_CONN(conn)->state, ==,
+            EXT_OR_CONN_STATE_AUTH_WAIT_CLIENT_NONCE);
+  /* Pump it again. Nothing should happen. */
+  tt_int_op(0, ==, connection_ext_or_process_inbuf(conn));
+  /* send the rest of the nonce. */
+  WRITE("ahead up the whi", 16);
+  MOCK(crypto_rand, crypto_rand_return_tse_str);
+  tt_int_op(0, ==, connection_ext_or_process_inbuf(conn));
+  UNMOCK(crypto_rand);
+  /* We should get the right reply from the server. */
+  CONTAINS("\xec\x80\xed\x6e\x54\x6d\x3b\x36\xfd\xfc\x22\xfe\x13\x15\x41\x6b"
+           "\x02\x9f\x1a\xde\x76\x10\xd9\x10\x87\x8b\x62\xee\xb7\x40\x38\x21"
+           "te road There is always another ", 64);
+  /* Send the wrong response. */
+  WRITE("not with a bang but a whimper...", 32);
+  MOCK(control_event_bootstrap_problem, ignore_bootstrap_problem);
+  tt_int_op(-1, ==, connection_ext_or_process_inbuf(conn));
+  CONTAINS("\x00", 1);
+  tt_assert(TO_CONN(conn)->marked_for_close);
+  /* XXXX Hold-open-until-flushed. */
+  close_closeable_connections();
+  conn = NULL;
+  UNMOCK(control_event_bootstrap_problem);
+
+  MOCK(connection_start_reading, note_read_started);
+  MOCK(connection_stop_reading, note_read_stopped);
+  MOCK(connection_tls_start_handshake, handshake_start);
+
+  /* Okay, this time let's succeed. */
+  conn = or_connection_new(CONN_TYPE_EXT_OR, AF_INET);
+  do_ext_or_handshake(conn);
+
+  /* Now let's run through some messages. */
+  /* First let's send some junk and make sure it's ignored. */
+  WRITE("\xff\xf0\x00\x03""ABC", 7);
+  tt_int_op(0, ==, connection_ext_or_process_inbuf(conn));
+  CONTAINS("", 0);
+  /* Now let's send a USERADDR command. */
+  WRITE("\x00\x01\x00\x0c""1.2.3.4:5678", 16);
+  tt_int_op(0, ==, connection_ext_or_process_inbuf(conn));
+  tt_int_op(TO_CONN(conn)->port, ==, 5678);
+  tt_int_op(tor_addr_to_ipv4h(&TO_CONN(conn)->addr), ==, 0x01020304);
+  /* Now let's send a TRANSPORT command. */
+  WRITE("\x00\x02\x00\x07""rfc1149", 11);
+  tt_int_op(0, ==, connection_ext_or_process_inbuf(conn));
+  tt_ptr_op(NULL, !=, conn->ext_or_transport);
+  tt_str_op("rfc1149", ==, conn->ext_or_transport);
+  tt_int_op(is_reading,==,1);
+  tt_int_op(TO_CONN(conn)->state, ==, EXT_OR_CONN_STATE_OPEN);
+  /* DONE */
+  WRITE("\x00\x00\x00\x00", 4);
+  tt_int_op(0, ==, connection_ext_or_process_inbuf(conn));
+  tt_int_op(TO_CONN(conn)->state, ==, EXT_OR_CONN_STATE_FLUSHING);
+  tt_int_op(is_reading,==,0);
+  CONTAINS("\x10\x00\x00\x00", 4);
+  tt_int_op(handshake_start_called,==,0);
+  tt_int_op(0, ==, connection_ext_or_finished_flushing(conn));
+  tt_int_op(is_reading,==,1);
+  tt_int_op(handshake_start_called,==,1);
+  tt_int_op(TO_CONN(conn)->type, ==, CONN_TYPE_OR);
+  tt_int_op(TO_CONN(conn)->state, ==, 0);
+  close_closeable_connections();
+  conn = NULL;
+
+  /* Okay, this time let's succeed the handshake but fail the USERADDR
+     command. */
+  conn = or_connection_new(CONN_TYPE_EXT_OR, AF_INET);
+  do_ext_or_handshake(conn);
+  /* USERADDR command with an extra NUL byte */
+  WRITE("\x00\x01\x00\x0d""1.2.3.4:5678\x00", 17);
+  MOCK(control_event_bootstrap_problem, ignore_bootstrap_problem);
+  tt_int_op(-1, ==, connection_ext_or_process_inbuf(conn));
+  CONTAINS("", 0);
+  tt_assert(TO_CONN(conn)->marked_for_close);
+  close_closeable_connections();
+  conn = NULL;
+  UNMOCK(control_event_bootstrap_problem);
+
+  /* Now fail the TRANSPORT command. */
+  conn = or_connection_new(CONN_TYPE_EXT_OR, AF_INET);
+  do_ext_or_handshake(conn);
+  /* TRANSPORT command with an extra NUL byte */
+  WRITE("\x00\x02\x00\x08""rfc1149\x00", 12);
+  MOCK(control_event_bootstrap_problem, ignore_bootstrap_problem);
+  tt_int_op(-1, ==, connection_ext_or_process_inbuf(conn));
+  CONTAINS("", 0);
+  tt_assert(TO_CONN(conn)->marked_for_close);
+  close_closeable_connections();
+  conn = NULL;
+  UNMOCK(control_event_bootstrap_problem);
+
+  /* Now fail the TRANSPORT command. */
+  conn = or_connection_new(CONN_TYPE_EXT_OR, AF_INET);
+  do_ext_or_handshake(conn);
+  /* TRANSPORT command with transport name with symbols (not a
+     C-identifier) */
+  WRITE("\x00\x02\x00\x07""rf*1149", 11);
+  MOCK(control_event_bootstrap_problem, ignore_bootstrap_problem);
+  tt_int_op(-1, ==, connection_ext_or_process_inbuf(conn));
+  CONTAINS("", 0);
+  tt_assert(TO_CONN(conn)->marked_for_close);
+  close_closeable_connections();
+  conn = NULL;
+  UNMOCK(control_event_bootstrap_problem);
+
+ done:
+  UNMOCK(connection_write_to_buf_impl_);
+  UNMOCK(crypto_rand);
+  if (conn)
+    connection_free_(TO_CONN(conn));
+#undef CONTAINS
+#undef WRITE
+}
+
+struct testcase_t extorport_tests[] = {
+  { "id_map", test_ext_or_id_map, TT_FORK, NULL, NULL },
+  { "write_command", test_ext_or_write_command, TT_FORK, NULL, NULL },
+  { "init_auth", test_ext_or_init_auth, TT_FORK, NULL, NULL },
+  { "cookie_auth", test_ext_or_cookie_auth, TT_FORK, NULL, NULL },
+  { "cookie_auth_testvec", test_ext_or_cookie_auth_testvec, TT_FORK,
+    NULL, NULL },
+  { "handshake", test_ext_or_handshake, TT_FORK, NULL, NULL },
+  END_OF_TESTCASES
+};
+

+ 2 - 1
src/test/test_options.c

@@ -148,6 +148,8 @@ test_options_validate(void *arg)
   (void)arg;
   setup_log_callback();
 
+  WANT_ERR("ExtORPort 500000", "Invalid ExtORPort");
+
   WANT_ERR_LOG("ServerTransportOptions trebuchet",
                "ServerTransportOptions did not parse",
                LOG_WARN, "Too few arguments");
@@ -157,7 +159,6 @@ test_options_validate(void *arg)
                "ServerTransportOptions did not parse",
                LOG_WARN, "\"slingsnappy\" is not a k=v");
 
-// done:
   clear_log_messages();
   return;
 }

+ 393 - 0
src/test/test_socks.c

@@ -0,0 +1,393 @@
+/* Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2013, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "or.h"
+#include "buffers.h"
+#include "config.h"
+#include "test.h"
+
+typedef struct socks_test_data_t {
+  socks_request_t *req;
+  buf_t *buf;
+} socks_test_data_t;
+
+static void *
+socks_test_setup(const struct testcase_t *testcase)
+{
+  socks_test_data_t *data = tor_malloc(sizeof(socks_test_data_t));
+  (void)testcase;
+  data->buf = buf_new_with_capacity(256);
+  data->req = socks_request_new();
+  config_register_addressmaps(get_options());
+  return data;
+}
+static int
+socks_test_cleanup(const struct testcase_t *testcase, void *ptr)
+{
+  socks_test_data_t *data = ptr;
+  (void)testcase;
+  buf_free(data->buf);
+  socks_request_free(data->req);
+  tor_free(data);
+  return 1;
+}
+
+const struct testcase_setup_t socks_setup = {
+  socks_test_setup, socks_test_cleanup
+};
+
+#define SOCKS_TEST_INIT()                       \
+  socks_test_data_t *testdata = ptr;            \
+  buf_t *buf = testdata->buf;                   \
+  socks_request_t *socks = testdata->req;
+#define ADD_DATA(buf, s)                                        \
+  write_to_buf(s, sizeof(s)-1, buf)
+
+static void
+socks_request_clear(socks_request_t *socks)
+{
+  tor_free(socks->username);
+  tor_free(socks->password);
+  memset(socks, 0, sizeof(socks_request_t));
+}
+
+/** Perform unsupported SOCKS 4 commands */
+static void
+test_socks_4_unsupported_commands(void *ptr)
+{
+  SOCKS_TEST_INIT();
+
+  /* SOCKS 4 Send BIND [02] to IP address 2.2.2.2:4369 */
+  ADD_DATA(buf, "\x04\x02\x11\x11\x02\x02\x02\x02\x00");
+  test_assert(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
+                                   get_options()->SafeSocks) == -1);
+  test_eq(4, socks->socks_version);
+  test_eq(0, socks->replylen); /* XXX: shouldn't tor reply? */
+
+ done:
+  ;
+}
+
+/** Perform supported SOCKS 4 commands */
+static void
+test_socks_4_supported_commands(void *ptr)
+{
+  SOCKS_TEST_INIT();
+
+  test_eq(0, buf_datalen(buf));
+
+  /* SOCKS 4 Send CONNECT [01] to IP address 2.2.2.2:4370 */
+  ADD_DATA(buf, "\x04\x01\x11\x12\x02\x02\x02\x03\x00");
+  test_assert(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
+                                   get_options()->SafeSocks) == 1);
+  test_eq(4, socks->socks_version);
+  test_eq(0, socks->replylen); /* XXX: shouldn't tor reply? */
+  test_eq(SOCKS_COMMAND_CONNECT, socks->command);
+  test_streq("2.2.2.3", socks->address);
+  test_eq(4370, socks->port);
+  test_assert(socks->got_auth == 0);
+  test_assert(! socks->username);
+
+  test_eq(0, buf_datalen(buf));
+  socks_request_clear(socks);
+
+  /* SOCKS 4 Send CONNECT [01] to IP address 2.2.2.2:4369 with userid*/
+  ADD_DATA(buf, "\x04\x01\x11\x12\x02\x02\x02\x04me\x00");
+  test_assert(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
+                                   get_options()->SafeSocks) == 1);
+  test_eq(4, socks->socks_version);
+  test_eq(0, socks->replylen); /* XXX: shouldn't tor reply? */
+  test_eq(SOCKS_COMMAND_CONNECT, socks->command);
+  test_streq("2.2.2.4", socks->address);
+  test_eq(4370, socks->port);
+  test_assert(socks->got_auth == 1);
+  test_assert(socks->username);
+  test_eq(2, socks->usernamelen);
+  test_memeq("me", socks->username, 2);
+
+  test_eq(0, buf_datalen(buf));
+  socks_request_clear(socks);
+
+  /* SOCKS 4a Send RESOLVE [F0] request for torproject.org */
+  ADD_DATA(buf, "\x04\xF0\x01\x01\x00\x00\x00\x02me\x00torproject.org\x00");
+  test_assert(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
+                                   get_options()->SafeSocks) == 1);
+  test_eq(4, socks->socks_version);
+  test_eq(0, socks->replylen); /* XXX: shouldn't tor reply? */
+  test_streq("torproject.org", socks->address);
+
+  test_eq(0, buf_datalen(buf));
+
+ done:
+  ;
+}
+
+/**  Perform unsupported SOCKS 5 commands */
+static void
+test_socks_5_unsupported_commands(void *ptr)
+{
+  SOCKS_TEST_INIT();
+
+  /* SOCKS 5 Send unsupported BIND [02] command */
+  ADD_DATA(buf, "\x05\x02\x00\x01");
+
+  test_eq(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
+                               get_options()->SafeSocks), 0);
+  test_eq(0, buf_datalen(buf));
+  test_eq(5, socks->socks_version);
+  test_eq(2, socks->replylen);
+  test_eq(5, socks->reply[0]);
+  test_eq(0, socks->reply[1]);
+  ADD_DATA(buf, "\x05\x02\x00\x01\x02\x02\x02\x01\x01\x01");
+  test_eq(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
+                               get_options()->SafeSocks), -1);
+  /* XXX: shouldn't tor reply 'command not supported' [07]? */
+
+  buf_clear(buf);
+  socks_request_clear(socks);
+
+  /* SOCKS 5 Send unsupported UDP_ASSOCIATE [03] command */
+  ADD_DATA(buf, "\x05\x03\x00\x01\x02");
+  test_eq(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
+                               get_options()->SafeSocks), 0);
+  test_eq(5, socks->socks_version);
+  test_eq(2, socks->replylen);
+  test_eq(5, socks->reply[0]);
+  test_eq(2, socks->reply[1]);
+  ADD_DATA(buf, "\x05\x03\x00\x01\x02\x02\x02\x01\x01\x01");
+  test_eq(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
+                               get_options()->SafeSocks), -1);
+  /* XXX: shouldn't tor reply 'command not supported' [07]? */
+
+ done:
+  ;
+}
+
+/** Perform supported SOCKS 5 commands */
+static void
+test_socks_5_supported_commands(void *ptr)
+{
+  SOCKS_TEST_INIT();
+
+  /* SOCKS 5 Send CONNECT [01] to IP address 2.2.2.2:4369 */
+  ADD_DATA(buf, "\x05\x01\x00");
+  test_eq(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
+                                   get_options()->SafeSocks), 0);
+  test_eq(5, socks->socks_version);
+  test_eq(2, socks->replylen);
+  test_eq(5, socks->reply[0]);
+  test_eq(0, socks->reply[1]);
+
+  ADD_DATA(buf, "\x05\x01\x00\x01\x02\x02\x02\x02\x11\x11");
+  test_eq(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
+                                   get_options()->SafeSocks), 1);
+  test_streq("2.2.2.2", socks->address);
+  test_eq(4369, socks->port);
+
+  test_eq(0, buf_datalen(buf));
+  socks_request_clear(socks);
+
+  /* SOCKS 5 Send CONNECT [01] to FQDN torproject.org:4369 */
+  ADD_DATA(buf, "\x05\x01\x00");
+  ADD_DATA(buf, "\x05\x01\x00\x03\x0Etorproject.org\x11\x11");
+  test_eq(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
+                                   get_options()->SafeSocks), 1);
+
+  test_eq(5, socks->socks_version);
+  test_eq(2, socks->replylen);
+  test_eq(5, socks->reply[0]);
+  test_eq(0, socks->reply[1]);
+  test_streq("torproject.org", socks->address);
+  test_eq(4369, socks->port);
+
+  test_eq(0, buf_datalen(buf));
+  socks_request_clear(socks);
+
+  /* SOCKS 5 Send RESOLVE [F0] request for torproject.org:4369 */
+  ADD_DATA(buf, "\x05\x01\x00");
+  ADD_DATA(buf, "\x05\xF0\x00\x03\x0Etorproject.org\x01\x02");
+  test_assert(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
+                                   get_options()->SafeSocks) == 1);
+  test_eq(5, socks->socks_version);
+  test_eq(2, socks->replylen);
+  test_eq(5, socks->reply[0]);
+  test_eq(0, socks->reply[1]);
+  test_streq("torproject.org", socks->address);
+
+  test_eq(0, buf_datalen(buf));
+  socks_request_clear(socks);
+
+  /* SOCKS 5 Send RESOLVE_PTR [F1] for IP address 2.2.2.5 */
+  ADD_DATA(buf, "\x05\x01\x00");
+  ADD_DATA(buf, "\x05\xF1\x00\x01\x02\x02\x02\x05\x01\x03");
+  test_assert(fetch_from_buf_socks(buf, socks, get_options()->TestSocks,
+                                   get_options()->SafeSocks) == 1);
+  test_eq(5, socks->socks_version);
+  test_eq(2, socks->replylen);
+  test_eq(5, socks->reply[0]);
+  test_eq(0, socks->reply[1]);
+  test_streq("2.2.2.5", socks->address);
+
+  test_eq(0, buf_datalen(buf));
+
+ done:
+  ;
+}
+
+/**  Perform SOCKS 5 authentication */
+static void
+test_socks_5_no_authenticate(void *ptr)
+{
+  SOCKS_TEST_INIT();
+
+  /*SOCKS 5 No Authentication */
+  ADD_DATA(buf,"\x05\x01\x00");
+  test_assert(!fetch_from_buf_socks(buf, socks,
+                                    get_options()->TestSocks,
+                                    get_options()->SafeSocks));
+  test_eq(2, socks->replylen);
+  test_eq(5, socks->reply[0]);
+  test_eq(SOCKS_NO_AUTH, socks->reply[1]);
+
+  test_eq(0, buf_datalen(buf));
+
+  /*SOCKS 5 Send username/password anyway - pretend to be broken */
+  ADD_DATA(buf,"\x01\x02\x01\x01\x02\x01\x01");
+  test_assert(!fetch_from_buf_socks(buf, socks,
+                                    get_options()->TestSocks,
+                                    get_options()->SafeSocks));
+  test_eq(5, socks->socks_version);
+  test_eq(2, socks->replylen);
+  test_eq(1, socks->reply[0]);
+  test_eq(0, socks->reply[1]);
+
+  test_eq(2, socks->usernamelen);
+  test_eq(2, socks->passwordlen);
+
+  test_memeq("\x01\x01", socks->username, 2);
+  test_memeq("\x01\x01", socks->password, 2);
+
+ done:
+  ;
+}
+
+/** Perform SOCKS 5 authentication */
+static void
+test_socks_5_authenticate(void *ptr)
+{
+  SOCKS_TEST_INIT();
+
+  /* SOCKS 5 Negotiate username/password authentication */
+  ADD_DATA(buf, "\x05\x01\x02");
+
+  test_assert(!fetch_from_buf_socks(buf, socks,
+                                   get_options()->TestSocks,
+                                   get_options()->SafeSocks));
+  test_eq(2, socks->replylen);
+  test_eq(5, socks->reply[0]);
+  test_eq(SOCKS_USER_PASS, socks->reply[1]);
+  test_eq(5, socks->socks_version);
+
+  test_eq(0, buf_datalen(buf));
+
+  /* SOCKS 5 Send username/password */
+  ADD_DATA(buf, "\x01\x02me\x08mypasswd");
+  test_assert(!fetch_from_buf_socks(buf, socks,
+                                   get_options()->TestSocks,
+                                   get_options()->SafeSocks));
+  test_eq(5, socks->socks_version);
+  test_eq(2, socks->replylen);
+  test_eq(1, socks->reply[0]);
+  test_eq(0, socks->reply[1]);
+
+  test_eq(2, socks->usernamelen);
+  test_eq(8, socks->passwordlen);
+
+  test_memeq("me", socks->username, 2);
+  test_memeq("mypasswd", socks->password, 8);
+
+ done:
+  ;
+}
+
+/** Perform SOCKS 5 authentication and send data all in one go */
+static void
+test_socks_5_authenticate_with_data(void *ptr)
+{
+  SOCKS_TEST_INIT();
+
+  /* SOCKS 5 Negotiate username/password authentication */
+  ADD_DATA(buf, "\x05\x01\x02");
+
+  test_assert(!fetch_from_buf_socks(buf, socks,
+                                   get_options()->TestSocks,
+                                   get_options()->SafeSocks));
+  test_eq(2, socks->replylen);
+  test_eq(5, socks->reply[0]);
+  test_eq(SOCKS_USER_PASS, socks->reply[1]);
+  test_eq(5, socks->socks_version);
+
+  test_eq(0, buf_datalen(buf));
+
+  /* SOCKS 5 Send username/password */
+  /* SOCKS 5 Send CONNECT [01] to IP address 2.2.2.2:4369 */
+  ADD_DATA(buf, "\x01\x02me\x03you\x05\x01\x00\x01\x02\x02\x02\x02\x11\x11");
+  test_assert(fetch_from_buf_socks(buf, socks,
+                                   get_options()->TestSocks,
+                                   get_options()->SafeSocks) == 1);
+  test_eq(5, socks->socks_version);
+  test_eq(2, socks->replylen);
+  test_eq(1, socks->reply[0]);
+  test_eq(0, socks->reply[1]);
+
+  test_streq("2.2.2.2", socks->address);
+  test_eq(4369, socks->port);
+
+  test_eq(2, socks->usernamelen);
+  test_eq(3, socks->passwordlen);
+  test_memeq("me", socks->username, 2);
+  test_memeq("you", socks->password, 3);
+
+ done:
+  ;
+}
+
+/** Perform SOCKS 5 authentication before method negotiated */
+static void
+test_socks_5_auth_before_negotiation(void *ptr)
+{
+  SOCKS_TEST_INIT();
+
+  /* SOCKS 5 Send username/password */
+  ADD_DATA(buf, "\x01\x02me\x02me");
+  test_assert(fetch_from_buf_socks(buf, socks,
+                                   get_options()->TestSocks,
+                                   get_options()->SafeSocks) == -1);
+  test_eq(0, socks->socks_version);
+  test_eq(0, socks->replylen);
+  test_eq(0, socks->reply[0]);
+  test_eq(0, socks->reply[1]);
+
+ done:
+  ;
+}
+
+#define SOCKSENT(name)                                  \
+  { #name, test_socks_##name, TT_FORK, &socks_setup, NULL }
+
+struct testcase_t socks_tests[] = {
+  SOCKSENT(4_unsupported_commands),
+  SOCKSENT(4_supported_commands),
+
+  SOCKSENT(5_unsupported_commands),
+  SOCKSENT(5_supported_commands),
+  SOCKSENT(5_no_authenticate),
+  SOCKSENT(5_auth_before_negotiation),
+  SOCKSENT(5_authenticate),
+  SOCKSENT(5_authenticate_with_data),
+
+  END_OF_TESTCASES
+};
+