Bladeren bron

Merge remote-tracking branch 'public/bug2841'

Conflicts:
	src/or/config.c
Nick Mathewson 13 jaren geleden
bovenliggende
commit
e006aa5dfa
8 gewijzigde bestanden met toevoegingen van 493 en 69 verwijderingen
  1. 178 22
      src/or/circuitbuild.c
  2. 21 1
      src/or/circuitbuild.h
  3. 136 14
      src/or/config.c
  4. 101 0
      src/or/connection.c
  5. 3 0
      src/or/connection.h
  6. 21 25
      src/or/connection_or.c
  7. 7 0
      src/or/main.c
  8. 26 7
      src/or/or.h

+ 178 - 22
src/or/circuitbuild.c

@@ -79,6 +79,28 @@ typedef struct {
                           * at which we last failed to connect to it. */
 } entry_guard_t;
 
+/** Information about a configured bridge. Currently this just matches the
+ * ones in the torrc file, but one day we may be able to learn about new
+ * bridges on our own, and remember them in the state file. */
+typedef struct {
+  /** Address of the bridge. */
+  tor_addr_t addr;
+  /** TLS port for the bridge. */
+  uint16_t port;
+  /** Boolean: We are re-parsing our bridge list, and we are going to remove
+   * this one if we don't find it in the list of configured bridges. */
+  unsigned marked_for_removal : 1;
+  /** Expected identity digest, or all zero bytes if we don't know what the
+   * digest should be. */
+  char identity[DIGEST_LEN];
+
+  /** Name of pluggable transport protocol taken from its config line. */
+  char *transport_name;
+
+  /** When should we next try to fetch a descriptor for this bridge? */
+  download_status_t fetch_status;
+} bridge_info_t;
+
 /** A list of our chosen entry guards. */
 static smartlist_t *entry_guards = NULL;
 /** A value of 1 means that the entry_guards list has changed
@@ -101,6 +123,10 @@ static int onion_append_hop(crypt_path_t **head_ptr, extend_info_t *choice);
 
 static void entry_guards_changed(void);
 
+static const transport_t *transport_get_by_name(const char *name);
+static void transport_free(transport_t *transport);
+static void bridge_free(bridge_info_t *bridge);
+
 /**
  * This function decides if CBT learning should be disabled. It returns
  * true if one or more of the following four conditions are met:
@@ -4496,24 +4522,6 @@ getinfo_helper_entry_guards(control_connection_t *conn,
   return 0;
 }
 
-/** Information about a configured bridge. Currently this just matches the
- * ones in the torrc file, but one day we may be able to learn about new
- * bridges on our own, and remember them in the state file. */
-typedef struct {
-  /** Address of the bridge. */
-  tor_addr_t addr;
-  /** TLS port for the bridge. */
-  uint16_t port;
-  /** Boolean: We are re-parsing our bridge list, and we are going to remove
-   * this one if we don't find it in the list of configured bridges. */
-  unsigned marked_for_removal : 1;
-  /** Expected identity digest, or all zero bytes if we don't know what the
-   * digest should be. */
-  char identity[DIGEST_LEN];
-  /** When should we next try to fetch a descriptor for this bridge? */
-  download_status_t fetch_status;
-} bridge_info_t;
-
 /** A list of configured bridges. Whenever we actually get a descriptor
  * for one, we add it as an entry guard.  Note that the order of bridges
  * in this list does not necessarily correspond to the order of bridges
@@ -4541,7 +4549,7 @@ sweep_bridge_list(void)
   SMARTLIST_FOREACH_BEGIN(bridge_list, bridge_info_t *, b) {
     if (b->marked_for_removal) {
       SMARTLIST_DEL_CURRENT(bridge_list, b);
-      tor_free(b);
+      bridge_free(b);
     }
   } SMARTLIST_FOREACH_END(b);
 }
@@ -4552,10 +4560,115 @@ clear_bridge_list(void)
 {
   if (!bridge_list)
     bridge_list = smartlist_create();
-  SMARTLIST_FOREACH(bridge_list, bridge_info_t *, b, tor_free(b));
+  SMARTLIST_FOREACH(bridge_list, bridge_info_t *, b, bridge_free(b));
   smartlist_clear(bridge_list);
 }
 
+/** Free the bridge <b>bridge</b>. */
+static void
+bridge_free(bridge_info_t *bridge)
+{
+  if (!bridge)
+    return;
+
+  tor_free(bridge->transport_name);
+  tor_free(bridge);
+}
+
+/** A list of pluggable transports found in torrc. */
+static smartlist_t *transport_list = NULL;
+
+/** Initialize the pluggable transports list to empty, creating it if
+ *  needed. */
+void
+clear_transport_list(void)
+{
+  if (!transport_list)
+    transport_list = smartlist_create();
+  SMARTLIST_FOREACH(transport_list, transport_t *, t, transport_free(t));
+  smartlist_clear(transport_list);
+}
+
+/** Free the pluggable transport struct <b>transport</b>. */
+static void
+transport_free(transport_t *transport)
+{
+  if (!transport)
+    return;
+
+  tor_free(transport->name);
+  tor_free(transport);
+}
+
+/** Returns the transport in our transport list that has the name <b>name</b>.
+ *  Else returns NULL. */
+static const transport_t *
+transport_get_by_name(const char *name)
+{
+  tor_assert(name);
+
+  if (!transport_list)
+    return NULL;
+
+  SMARTLIST_FOREACH_BEGIN(transport_list, const transport_t *, transport) {
+    if (!strcmp(transport->name, name))
+      return transport;
+  } SMARTLIST_FOREACH_END(transport);
+
+  return NULL;
+}
+
+/** Remember a new pluggable transport proxy at <b>addr</b>:<b>port</b>.
+ *  <b>name</b> is set to the name of the protocol this proxy uses.
+ *  <b>socks_ver</b> is set to the SOCKS version of the proxy.
+ *
+ *  Returns 0 on success, -1 on fail. */
+int
+transport_add_from_config(const tor_addr_t *addr, uint16_t port,
+                          const char *name, int socks_ver)
+{
+  transport_t *t;
+
+  if (transport_get_by_name(name)) { /* check for duplicate names */
+    log_warn(LD_CONFIG, "More than one transport has '%s' as "
+             "its name.", name);
+    return -1;
+  }
+
+  t = tor_malloc_zero(sizeof(transport_t));
+  tor_addr_copy(&t->addr, addr);
+  t->port = port;
+  t->name = tor_strdup(name);
+  t->socks_version = socks_ver;
+
+  if (!transport_list)
+    transport_list = smartlist_create();
+
+  smartlist_add(transport_list, t);
+  return 0;
+}
+
+/** Warns the user of possible pluggable transport misconfiguration. */
+void
+validate_pluggable_transports_config(void)
+{
+  if (bridge_list) {
+    SMARTLIST_FOREACH_BEGIN(bridge_list, bridge_info_t *, b) {
+      /* Skip bridges without transports. */
+      if (!b->transport_name)
+        continue;
+      /* See if the user has Bridges that specify nonexistent
+         pluggable transports. We should warn the user in such case,
+         since it's probably misconfiguration. */
+      if (!transport_get_by_name(b->transport_name))
+        log_warn(LD_CONFIG, "You have a Bridge line using the %s "
+                 "pluggable transport, but there doesn't seem to be a "
+                 "corresponding ClientTransportPlugin line.",
+                 b->transport_name);
+    } SMARTLIST_FOREACH_END(b);
+  }
+}
+
 /** Return a bridge pointer if <b>ri</b> is one of our known bridges
  * (either by comparing keys if possible, else by comparing addr/port).
  * Else return NULL. */
@@ -4634,10 +4747,12 @@ learned_router_identity(const tor_addr_t *addr, uint16_t port,
 
 /** Remember a new bridge at <b>addr</b>:<b>port</b>. If <b>digest</b>
  * is set, it tells us the identity key too.  If we already had the
- * bridge in our list, unmark it, and don't actually add anything new. */
+ * bridge in our list, unmark it, and don't actually add anything new.
+ * If <b>transport_name</b> is non-NULL - the bridge is associated with a
+ * pluggable transport - we assign the transport to the bridge. */
 void
 bridge_add_from_config(const tor_addr_t *addr, uint16_t port,
-                       const char *digest)
+                       const char *digest, const char *transport_name)
 {
   bridge_info_t *b;
 
@@ -4651,6 +4766,8 @@ bridge_add_from_config(const tor_addr_t *addr, uint16_t port,
   b->port = port;
   if (digest)
     memcpy(b->identity, digest, DIGEST_LEN);
+  if (transport_name)
+    b->transport_name = tor_strdup(transport_name);
   b->fetch_status.schedule = DL_SCHED_BRIDGE;
   if (!bridge_list)
     bridge_list = smartlist_create();
@@ -4688,6 +4805,42 @@ find_bridge_by_digest(const char *digest)
   return NULL;
 }
 
+/** If <b>addr</b> and <b>port</b> match the address and port of a
+ * bridge of ours that uses pluggable transports, place its transport
+ * in <b>transport</b>.
+ *
+ * Return 0 on success (found a transport, or found a bridge with no
+ * transport, or found no bridge); return -1 if we should be using a
+ * transport, but the transport could not be found.
+ */
+int
+find_transport_by_bridge_addrport(const tor_addr_t *addr, uint16_t port,
+                                  const transport_t **transport)
+{
+  *transport = NULL;
+  if (!bridge_list)
+    return 0;
+
+  SMARTLIST_FOREACH_BEGIN(bridge_list, const bridge_info_t *, bridge) {
+    if (tor_addr_eq(&bridge->addr, addr) &&
+        (bridge->port == port)) { /* bridge matched */
+      if (bridge->transport_name) { /* it also uses pluggable transports */
+        *transport = transport_get_by_name(bridge->transport_name);
+        if (*transport == NULL) { /* it uses pluggable transports, but
+                                     the transport could not be found! */
+          return -1;
+        }
+        return 0;
+      } else { /* bridge matched, but it doesn't use transports. */
+        break;
+      }
+    }
+  } SMARTLIST_FOREACH_END(bridge);
+
+  *transport = NULL;
+  return 0;
+}
+
 /** We need to ask <b>bridge</b> for its server descriptor. */
 static void
 launch_direct_bridge_descriptor_fetch(bridge_info_t *bridge)
@@ -4993,7 +5146,10 @@ entry_guards_free_all(void)
     entry_guards = NULL;
   }
   clear_bridge_list();
+  clear_transport_list();
   smartlist_free(bridge_list);
+  smartlist_free(transport_list);
   bridge_list = NULL;
+  transport_list = NULL;
 }
 

+ 21 - 1
src/or/circuitbuild.h

@@ -12,6 +12,18 @@
 #ifndef _TOR_CIRCUITBUILD_H
 #define _TOR_CIRCUITBUILD_H
 
+/** Represents a pluggable transport proxy used by a bridge. */
+typedef struct {
+  /** SOCKS version: One of PROXY_SOCKS4, PROXY_SOCKS5. */
+  int socks_version;
+  /** Name of pluggable transport protocol */
+  char *name;
+  /** Address of proxy */
+  tor_addr_t addr;
+  /** Port of proxy */
+  uint16_t port;
+} transport_t;
+
 char *circuit_list_path(origin_circuit_t *circ, int verbose);
 char *circuit_list_path_for_controller(origin_circuit_t *circ);
 void circuit_log_path(int severity, unsigned int domain,
@@ -70,7 +82,8 @@ int node_is_a_configured_bridge(const node_t *node);
 void learned_router_identity(const tor_addr_t *addr, uint16_t port,
                              const char *digest);
 void bridge_add_from_config(const tor_addr_t *addr, uint16_t port,
-                            const char *digest);
+                            const char *digest,
+                            const char *transport_name);
 void retry_bridge_descriptor_fetch_directly(const char *digest);
 void fetch_bridge_descriptors(const or_options_t *options, time_t now);
 void learned_bridge_descriptor(routerinfo_t *ri, int from_cache);
@@ -126,5 +139,12 @@ void circuit_build_times_network_circ_success(circuit_build_times_t *cbt);
 
 int circuit_build_times_get_bw_scale(networkstatus_t *ns);
 
+void clear_transport_list(void);
+int transport_add_from_config(const tor_addr_t *addr, uint16_t port,
+                               const char *name, int socks_ver);
+int find_transport_by_bridge_addrport(const tor_addr_t *addr, uint16_t port,
+                                      const transport_t **transport);
+void validate_pluggable_transports_config(void);
+
 #endif
 

+ 136 - 14
src/or/config.c

@@ -203,8 +203,9 @@ static config_var_t _option_vars[] = {
   V(CircuitStreamTimeout,        INTERVAL, "0"),
   V(CircuitPriorityHalflife,     DOUBLE,  "-100.0"), /*negative:'Use default'*/
   V(ClientDNSRejectInternalAddresses, BOOL,"1"),
-  V(ClientRejectInternalAddresses, BOOL,   "1"),
   V(ClientOnly,                  BOOL,     "0"),
+  V(ClientRejectInternalAddresses, BOOL,   "1"),
+  V(ClientTransportPlugin,       LINELIST, NULL),
   V(ConsensusParams,             STRING,   NULL),
   V(ConnLimit,                   UINT,     "1000"),
   V(ConnDirectionStatistics,     BOOL,     "0"),
@@ -573,6 +574,7 @@ static int check_nickname_list(const char *lst, const char *name, char **msg);
 static void config_register_addressmaps(const or_options_t *options);
 
 static int parse_bridge_line(const char *line, int validate_only);
+static int parse_client_transport_line(const char *line, int validate_only);
 static int parse_dir_server_line(const char *line,
                                  dirinfo_type_t required_type,
                                  int validate_only);
@@ -1218,6 +1220,18 @@ options_act(const or_options_t *old_options)
   if (consider_adding_dir_authorities(options, old_options) < 0)
     return -1;
 
+  clear_transport_list();
+  if (options->ClientTransportPlugin) {
+    for (cl = options->ClientTransportPlugin; cl; cl = cl->next) {
+      if (parse_client_transport_line(cl->value, 0)<0) {
+        log_warn(LD_BUG,
+                 "Previously validated ClientTransportPlugin line "
+                 "could not be added!");
+        return -1;
+      }
+    }
+  }
+
   if (options->Bridges) {
     mark_bridge_list();
     for (cl = options->Bridges; cl; cl = cl->next) {
@@ -1230,6 +1244,11 @@ options_act(const or_options_t *old_options)
     sweep_bridge_list();
   }
 
+  /* If we have pluggable transport related options enabled, see if we
+     should warn the user about potential configuration problems. */
+  if (options->Bridges || options->ClientTransportPlugin)
+    validate_pluggable_transports_config();
+
   if (running_tor && rend_config_services(options, 0)<0) {
     log_warn(LD_BUG,
        "Previously validated hidden services line could not be added!");
@@ -3558,8 +3577,11 @@ options_validate(or_options_t *old_options, or_options_t *options,
     }
   }
 
-  if (options->Socks4Proxy && options->Socks5Proxy)
-    REJECT("You cannot specify both Socks4Proxy and SOCKS5Proxy");
+  /* Check if more than one proxy type has been enabled. */
+  if (!!options->Socks4Proxy + !!options->Socks5Proxy +
+      !!options->HTTPSProxy + !!options->ClientTransportPlugin > 1)
+    REJECT("You have configured more than one proxy type. "
+           "(Socks4Proxy|Socks5Proxy|HTTPSProxy|ClientTransportPlugin)");
 
   if (options->Socks5ProxyUsername) {
     size_t len;
@@ -3680,11 +3702,15 @@ options_validate(or_options_t *old_options, or_options_t *options,
     REJECT("If you set UseBridges, you must specify at least one bridge.");
   if (options->UseBridges && !options->TunnelDirConns)
     REJECT("If you set UseBridges, you must set TunnelDirConns.");
-  if (options->Bridges) {
-    for (cl = options->Bridges; cl; cl = cl->next) {
-      if (parse_bridge_line(cl->value, 1)<0)
-        REJECT("Bridge line did not parse. See logs for details.");
-    }
+
+  for (cl = options->ClientTransportPlugin; cl; cl = cl->next) {
+    if (parse_client_transport_line(cl->value, 1)<0)
+      REJECT("Transport line did not parse. See logs for details.");
+  }
+
+  for (cl = options->Bridges; cl; cl = cl->next) {
+    if (parse_bridge_line(cl->value, 1)<0)
+      REJECT("Bridge line did not parse. See logs for details.");
   }
 
   if (options->ConstrainedSockets) {
@@ -4572,6 +4598,8 @@ parse_bridge_line(const char *line, int validate_only)
   smartlist_t *items = NULL;
   int r;
   char *addrport=NULL, *fingerprint=NULL;
+  char *transport_name=NULL;
+  char *field1=NULL;
   tor_addr_t addr;
   uint16_t port = 0;
   char digest[DIGEST_LEN];
@@ -4583,8 +4611,24 @@ parse_bridge_line(const char *line, int validate_only)
     log_warn(LD_CONFIG, "Too few arguments to Bridge line.");
     goto err;
   }
-  addrport = smartlist_get(items, 0);
+
+  /* field1 is either a transport name or addrport */
+  field1 = smartlist_get(items, 0);
   smartlist_del_keeporder(items, 0);
+
+  if (!(strstr(field1, ".") || strstr(field1, ":"))) {
+    /* new-style bridge line */
+    transport_name = field1;
+    if (smartlist_len(items) < 1) {
+      log_warn(LD_CONFIG, "Too few items to Bridge line.");
+      goto err;
+    }
+    addrport = smartlist_get(items, 0);
+    smartlist_del_keeporder(items, 0);
+  } else {
+    addrport = field1;
+  }
+
   if (tor_addr_port_parse(addrport, &addr, &port)<0) {
     log_warn(LD_CONFIG, "Error parsing Bridge address '%s'", addrport);
     goto err;
@@ -4609,23 +4653,101 @@ parse_bridge_line(const char *line, int validate_only)
   }
 
   if (!validate_only) {
-    log_debug(LD_DIR, "Bridge at %s:%d (%s)", fmt_addr(&addr),
-              (int)port,
+    log_debug(LD_DIR, "Bridge at %s:%d (transport: %s) (%s)",
+              fmt_addr(&addr), (int)port,
+              transport_name ? transport_name : "no transport",
               fingerprint ? fingerprint : "no key listed");
-    bridge_add_from_config(&addr, port, fingerprint ? digest : NULL);
+    bridge_add_from_config(&addr, port,
+                           fingerprint ? digest : NULL, transport_name);
   }
 
   r = 0;
   goto done;
 
-  err:
+ err:
   r = -1;
 
-  done:
+ done:
   SMARTLIST_FOREACH(items, char*, s, tor_free(s));
   smartlist_free(items);
   tor_free(addrport);
   tor_free(fingerprint);
+  tor_free(transport_name);
+  return r;
+}
+
+/** Read the contents of a ClientTransportPlugin line from
+ * <b>line</b>. Return 0 if the line is well-formed, and -1 if it
+ * isn't. If <b>validate_only</b> is 0, and the line is well-formed,
+ * then add the transport described in the line to our internal
+ * transport list.
+*/
+static int
+parse_client_transport_line(const char *line, int validate_only)
+{
+  smartlist_t *items = NULL;
+  int r;
+  char *socks_ver_str=NULL;
+  char *name=NULL;
+  char *addrport=NULL;
+  int socks_ver;
+  tor_addr_t addr;
+  uint16_t port = 0;
+
+  items = smartlist_create();
+  smartlist_split_string(items, line, NULL,
+                         SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, -1);
+
+  if (smartlist_len(items) < 3) {
+    log_warn(LD_CONFIG, "Too few arguments on ClientTransportPlugin line.");
+    goto err;
+  }
+
+  name = smartlist_get(items, 0);
+
+  socks_ver_str = smartlist_get(items, 1);
+
+  if (!strcmp(socks_ver_str,"socks4"))
+    socks_ver = PROXY_SOCKS4;
+  else if (!strcmp(socks_ver_str,"socks5"))
+    socks_ver = PROXY_SOCKS5;
+  else {
+    log_warn(LD_CONFIG, "Strange ClientTransportPlugin proxy type '%s'.",
+             socks_ver_str);
+    goto err;
+  }
+
+  addrport = smartlist_get(items, 2);
+
+  if (tor_addr_port_parse(addrport, &addr, &port)<0) {
+    log_warn(LD_CONFIG, "Error parsing transport "
+              "address '%s'", addrport);
+    goto err;
+  }
+
+  if (!port) {
+    log_warn(LD_CONFIG,
+              "Transport address '%s' has no port.", addrport);
+    goto err;
+  }
+
+  if (!validate_only) {
+    log_debug(LD_DIR, "Transport %s found at %s:%d", name,
+              fmt_addr(&addr), (int)port);
+
+    if (transport_add_from_config(&addr, port, name, socks_ver) < 0)
+      goto err;
+  }
+
+  r = 0;
+  goto done;
+
+ err:
+  r = -1;
+
+ done:
+  SMARTLIST_FOREACH(items, char*, s, tor_free(s));
+  smartlist_free(items);
   return r;
 }
 

+ 101 - 0
src/or/connection.c

@@ -66,6 +66,8 @@ static void set_constrained_socket_buffers(tor_socket_t sock, int size);
 static const char *connection_proxy_state_to_string(int state);
 static int connection_read_https_proxy_response(connection_t *conn);
 static void connection_send_socks5_connect(connection_t *conn);
+static const char *proxy_type_to_string(int proxy_type);
+static int get_proxy_type(void);
 
 /** The last IPv4 address that our network interface seemed to have been
  * binding to, in host order.  We use this to detect when our IP changes. */
@@ -1467,6 +1469,7 @@ connection_proxy_state_to_string(int state)
   static const char *unknown = "???";
   static const char *states[] = {
     "PROXY_NONE",
+    "PROXY_INFANT",
     "PROXY_HTTPS_WANT_CONNECT_OK",
     "PROXY_SOCKS4_WANT_CONNECT_OK",
     "PROXY_SOCKS5_WANT_AUTH_METHOD_NONE",
@@ -4126,3 +4129,101 @@ assert_connection_ok(connection_t *conn, time_t now)
   }
 }
 
+/** Fills <b>addr</b> and <b>port</b> with the details of the global
+ *  proxy server we are using.
+ *  <b>conn</b> contains the connection we are using the proxy for.
+ *
+ *  Return 0 on success, -1 on failure.
+ */
+int
+get_proxy_addrport(tor_addr_t *addr, uint16_t *port, int *proxy_type,
+                   const connection_t *conn)
+{
+  or_options_t *options = get_options();
+
+  if (options->HTTPSProxy) {
+    tor_addr_copy(addr, &options->HTTPSProxyAddr);
+    *port = options->HTTPSProxyPort;
+    *proxy_type = PROXY_CONNECT;
+    return 0;
+  } else if (options->Socks4Proxy) {
+    tor_addr_copy(addr, &options->Socks4ProxyAddr);
+    *port = options->Socks4ProxyPort;
+    *proxy_type = PROXY_SOCKS4;
+    return 0;
+  } else if (options->Socks5Proxy) {
+    tor_addr_copy(addr, &options->Socks5ProxyAddr);
+    *port = options->Socks5ProxyPort;
+    *proxy_type = PROXY_SOCKS5;
+    return 0;
+  } else if (options->ClientTransportPlugin ||
+             options->Bridges) {
+    const transport_t *transport = NULL;
+    int r;
+    r = find_transport_by_bridge_addrport(&conn->addr, conn->port, &transport);
+    if (r<0)
+      return -1;
+    if (transport) { /* transport found */
+      tor_addr_copy(addr, &transport->addr);
+      *port = transport->port;
+      *proxy_type = transport->socks_version;
+      return 0;
+    }
+  }
+
+  *proxy_type = PROXY_NONE;
+  return 0;
+}
+
+/** Returns the global proxy type used by tor. */
+static int
+get_proxy_type(void)
+{
+  or_options_t *options = get_options();
+
+  if (options->HTTPSProxy)
+    return PROXY_CONNECT;
+  else if (options->Socks4Proxy)
+    return PROXY_SOCKS4;
+  else if (options->Socks5Proxy)
+    return PROXY_SOCKS5;
+  else if (options->ClientTransportPlugin)
+    return PROXY_PLUGGABLE;
+  else
+    return PROXY_NONE;
+}
+
+/** Log a failed connection to a proxy server.
+ *  <b>conn</b> is the connection we use the proxy server for. */
+void
+log_failed_proxy_connection(connection_t *conn)
+{
+  tor_addr_t proxy_addr;
+  uint16_t proxy_port;
+  int proxy_type;
+
+  if (get_proxy_addrport(&proxy_addr, &proxy_port, &proxy_type, conn) != 0)
+    return; /* if we have no proxy set up, leave this function. */
+
+  log_warn(LD_NET,
+           "The connection to the %s proxy server at %s:%u just failed. "
+           "Make sure that the proxy server is up and running.",
+           proxy_type_to_string(get_proxy_type()), fmt_addr(&proxy_addr),
+           proxy_port);
+}
+
+/** Return string representation of <b>proxy_type</b>. */
+static const char *
+proxy_type_to_string(int proxy_type)
+{
+  switch (proxy_type) {
+  case PROXY_CONNECT:   return "HTTP";
+  case PROXY_SOCKS4:    return "SOCKS4";
+  case PROXY_SOCKS5:    return "SOCKS5";
+  case PROXY_PLUGGABLE: return "pluggable transports SOCKS";
+  case PROXY_NONE:      return "NULL";
+  default:              tor_assert(0);
+  }
+  return NULL; /*Unreached*/
+}
+

+ 3 - 0
src/or/connection.h

@@ -57,6 +57,9 @@ int connection_connect(connection_t *conn, const char *address,
 
 int connection_proxy_connect(connection_t *conn, int type);
 int connection_read_proxy_handshake(connection_t *conn);
+void log_failed_proxy_connection(connection_t *conn);
+int get_proxy_addrport(tor_addr_t *addr, uint16_t *port, int *proxy_type,
+                       const connection_t *conn);
 
 int retry_all_listeners(smartlist_t *replaced_conns,
                         smartlist_t *new_conns);

+ 21 - 25
src/or/connection_or.c

@@ -317,7 +317,7 @@ connection_or_finished_flushing(or_connection_t *conn)
 int
 connection_or_finished_connecting(or_connection_t *or_conn)
 {
-  int proxy_type;
+  const int proxy_type = or_conn->proxy_type;
   connection_t *conn;
   tor_assert(or_conn);
   conn = TO_CONN(or_conn);
@@ -327,15 +327,6 @@ connection_or_finished_connecting(or_connection_t *or_conn)
             conn->address,conn->port);
   control_event_bootstrap(BOOTSTRAP_STATUS_HANDSHAKE, 0);
 
-  proxy_type = PROXY_NONE;
-
-  if (get_options()->HTTPSProxy)
-    proxy_type = PROXY_CONNECT;
-  else if (get_options()->Socks4Proxy)
-    proxy_type = PROXY_SOCKS4;
-  else if (get_options()->Socks5Proxy)
-    proxy_type = PROXY_SOCKS5;
-
   if (proxy_type != PROXY_NONE) {
     /* start proxy handshake */
     if (connection_proxy_connect(conn, proxy_type) < 0) {
@@ -830,9 +821,13 @@ connection_or_connect(const tor_addr_t *_addr, uint16_t port,
   or_connection_t *conn;
   const or_options_t *options = get_options();
   int socket_error = 0;
-  int using_proxy = 0;
   tor_addr_t addr;
 
+  int r;
+  tor_addr_t proxy_addr;
+  uint16_t proxy_port;
+  int proxy_type;
+
   tor_assert(_addr);
   tor_assert(id_digest);
   tor_addr_copy(&addr, _addr);
@@ -849,19 +844,20 @@ connection_or_connect(const tor_addr_t *_addr, uint16_t port,
   conn->_base.state = OR_CONN_STATE_CONNECTING;
   control_event_or_conn_status(conn, OR_CONN_EVENT_LAUNCHED, 0);
 
-  /* use a proxy server if available */
-  if (options->HTTPSProxy) {
-    using_proxy = 1;
-    tor_addr_copy(&addr, &options->HTTPSProxyAddr);
-    port = options->HTTPSProxyPort;
-  } else if (options->Socks4Proxy) {
-    using_proxy = 1;
-    tor_addr_copy(&addr, &options->Socks4ProxyAddr);
-    port = options->Socks4ProxyPort;
-  } else if (options->Socks5Proxy) {
-    using_proxy = 1;
-    tor_addr_copy(&addr, &options->Socks5ProxyAddr);
-    port = options->Socks5ProxyPort;
+  /* If we are using a proxy server, find it and use it. */
+  r = get_proxy_addrport(&proxy_addr, &proxy_port, &proxy_type, TO_CONN(conn));
+  if (r == 0) {
+    conn->proxy_type = proxy_type;
+    if (proxy_type != PROXY_NONE) {
+      tor_addr_copy(&addr, &proxy_addr);
+      port = proxy_port;
+      conn->_base.proxy_state = PROXY_INFANT;
+    }
+  } else {
+    log_warn(LD_GENERAL, "Tried to connect through proxy, but proxy address "
+             "could not be found.");
+    connection_free(TO_CONN(conn));
+    return NULL;
   }
 
   switch (connection_connect(TO_CONN(conn), conn->_base.address,
@@ -869,7 +865,7 @@ connection_or_connect(const tor_addr_t *_addr, uint16_t port,
     case -1:
       /* If the connection failed immediately, and we're using
        * a proxy, our proxy is down. Don't blame the Tor server. */
-      if (!using_proxy)
+      if (conn->_base.proxy_state == PROXY_INFANT)
         entry_guard_register_connect_status(conn->identity_digest,
                                             0, 1, time(NULL));
       connection_or_connect_failed(conn,

+ 7 - 0
src/or/main.c

@@ -757,6 +757,13 @@ conn_close_if_marked(int i)
 #endif
 
   log_debug(LD_NET,"Cleaning up connection (fd %d).",conn->s);
+
+  /* If the connection we are about to close was trying to connect to
+  a proxy server and failed, the client won't be able to use that
+  proxy. We should warn the user about this. */
+  if (conn->proxy_state == PROXY_INFANT)
+    log_failed_proxy_connection(conn);
+
   IF_HAS_BUFFEREVENT(conn, goto unlink);
   if ((SOCKET_OK(conn->s) || conn->linked_conn) &&
       connection_wants_to_flush(conn)) {

+ 26 - 7
src/or/or.h

@@ -230,15 +230,30 @@ typedef enum {
 #define PROXY_CONNECT 1
 #define PROXY_SOCKS4 2
 #define PROXY_SOCKS5 3
+/* !!!! If there is ever a PROXY_* type over 2, we must grow the proxy_type
+ * field in or_connection_t */
+/* pluggable transports proxy type */
+#define PROXY_PLUGGABLE 4
 
 /* Proxy client handshake states */
-#define PROXY_HTTPS_WANT_CONNECT_OK 1
-#define PROXY_SOCKS4_WANT_CONNECT_OK 2
-#define PROXY_SOCKS5_WANT_AUTH_METHOD_NONE 3
-#define PROXY_SOCKS5_WANT_AUTH_METHOD_RFC1929 4
-#define PROXY_SOCKS5_WANT_AUTH_RFC1929_OK 5
-#define PROXY_SOCKS5_WANT_CONNECT_OK 6
-#define PROXY_CONNECTED 7
+/* We use a proxy but we haven't even connected to it yet. */
+#define PROXY_INFANT 1
+/* We use an HTTP proxy and we've sent the CONNECT command. */
+#define PROXY_HTTPS_WANT_CONNECT_OK 2
+/* We use a SOCKS4 proxy and we've sent the CONNECT command. */
+#define PROXY_SOCKS4_WANT_CONNECT_OK 3
+/* We use a SOCKS5 proxy and we try to negotiate without
+   any authentication . */
+#define PROXY_SOCKS5_WANT_AUTH_METHOD_NONE 4
+/* We use a SOCKS5 proxy and we try to negotiate with
+   Username/Password authentication . */
+#define PROXY_SOCKS5_WANT_AUTH_METHOD_RFC1929 5
+/* We use a SOCKS5 proxy and we just sent our credentials. */
+#define PROXY_SOCKS5_WANT_AUTH_RFC1929_OK 6
+/* We use a SOCKS5 proxy and we just sent our CONNECT command. */
+#define PROXY_SOCKS5_WANT_CONNECT_OK 7
+/* We use a proxy and we CONNECTed successfully!. */
+#define PROXY_CONNECTED 8
 
 /** True iff <b>x</b> is an edge connection. */
 #define CONN_IS_EDGE(x) \
@@ -1084,6 +1099,7 @@ typedef struct or_connection_t {
    * router itself has a problem.
    */
   unsigned int is_bad_for_new_circs:1;
+  unsigned int proxy_type:2; /**< One of PROXY_NONE...PROXY_SOCKS5 */
   uint8_t link_proto; /**< What protocol version are we using? 0 for
                        * "none negotiated yet." */
   circid_t next_circ_id; /**< Which circ_id do we try to use next on
@@ -2654,6 +2670,9 @@ typedef struct {
   int UseBridges; /**< Boolean: should we start all circuits with a bridge? */
   config_line_t *Bridges; /**< List of bootstrap bridge addresses. */
 
+  config_line_t *ClientTransportPlugin; /**< List of client
+                                           transport plugins. */
+
   int BridgeRelay; /**< Boolean: are we acting as a bridge relay? We make
                     * this explicit so we can change how we behave in the
                     * future. */