Browse Source

put in initial support for ".nickname.exit" addresses, to let alice
decide what exit node to use; based on a patch by geoff goodell.

needs more work: e.g. it goes bananas building new circuits when the
chosen exit node's exit policy rejects the connection.


svn:r3015

Roger Dingledine 21 years ago
parent
commit
c1dc17e6e2
6 changed files with 103 additions and 40 deletions
  1. 15 0
      src/or/circuituse.c
  2. 1 0
      src/or/connection.c
  3. 78 9
      src/or/connection_edge.c
  4. 5 2
      src/or/or.h
  5. 0 27
      src/or/rendclient.c
  6. 4 2
      src/or/test.c

+ 15 - 0
src/or/circuituse.c

@@ -706,12 +706,27 @@ circuit_get_open_circ_or_launch(connection_t *conn,
       }
       }
       if (!router_get_by_nickname(exitname)) {
       if (!router_get_by_nickname(exitname)) {
         log_fn(LOG_WARN,"Advertised intro point '%s' is not known. Closing.", exitname);
         log_fn(LOG_WARN,"Advertised intro point '%s' is not known. Closing.", exitname);
+        tor_free(exitname);
         return -1;
         return -1;
       }
       }
       /* XXX if we failed, then refetch the descriptor */
       /* XXX if we failed, then refetch the descriptor */
       log_fn(LOG_INFO,"Chose %s as intro point for %s.", exitname, conn->rend_query);
       log_fn(LOG_INFO,"Chose %s as intro point for %s.", exitname, conn->rend_query);
     }
     }
 
 
+    /* If we have specified a particular exit node for our
+     * connection, then be sure to open a circuit to that exit node.
+     */
+    if(desired_circuit_purpose == CIRCUIT_PURPOSE_C_GENERAL) {
+      if (conn->chosen_exit_name) {
+        exitname = tor_strdup(conn->chosen_exit_name);
+        if(!router_get_by_nickname(exitname)) {
+          log_fn(LOG_WARN,"Requested exit point '%s' is not known. Closing.", exitname);
+          tor_free(exitname);
+          return -1;
+        }
+      }
+    }
+
     if (desired_circuit_purpose == CIRCUIT_PURPOSE_C_REND_JOINED)
     if (desired_circuit_purpose == CIRCUIT_PURPOSE_C_REND_JOINED)
       new_circ_purpose = CIRCUIT_PURPOSE_C_ESTABLISH_REND;
       new_circ_purpose = CIRCUIT_PURPOSE_C_ESTABLISH_REND;
     else if (desired_circuit_purpose == CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT)
     else if (desired_circuit_purpose == CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT)

+ 1 - 0
src/or/connection.c

@@ -148,6 +148,7 @@ void connection_free(connection_t *conn) {
     buf_free(conn->outbuf);
     buf_free(conn->outbuf);
   }
   }
   tor_free(conn->address);
   tor_free(conn->address);
+  tor_free(conn->chosen_exit_name);
 
 
   if (connection_speaks_cells(conn)) {
   if (connection_speaks_cells(conn)) {
     if (conn->state == OR_CONN_STATE_OPEN)
     if (conn->state == OR_CONN_STATE_OPEN)

+ 78 - 9
src/or/connection_edge.c

@@ -352,6 +352,7 @@ void connection_ap_attach_pending(void)
 static int connection_ap_handshake_process_socks(connection_t *conn) {
 static int connection_ap_handshake_process_socks(connection_t *conn) {
   socks_request_t *socks;
   socks_request_t *socks;
   int sockshere;
   int sockshere;
+  int addresstype;
 
 
   tor_assert(conn);
   tor_assert(conn);
   tor_assert(conn->type == CONN_TYPE_AP);
   tor_assert(conn->type == CONN_TYPE_AP);
@@ -397,9 +398,24 @@ static int connection_ap_handshake_process_socks(connection_t *conn) {
     }
     }
   }
   }
 
 
-  /* this call _modifies_ socks->address iff it's a hidden-service request */
-  if (rend_parse_rendezvous_address(socks->address) < 0) {
-    /* normal request */
+  /* Parse the address provided by SOCKS.  Modify it in-place if it
+   * specifies a hidden-service (.onion) or particular exit node (.exit).
+   */
+  addresstype = parse_address(socks->address);
+
+  if (addresstype == 1) {
+    /* .exit -- modify conn to specify the exit node. */
+    char *s = strrchr(socks->address,'.');
+    if (!s || s[1] == '\0') {
+      log_fn(LOG_WARN,"Malformed address '%s.exit'. Refusing.", socks->address);
+      return -1;
+    }
+    conn->chosen_exit_name = tor_strdup(s+1);
+    *s = 0;
+  }
+
+  if (addresstype != 2) {
+    /* not a hidden-service request (i.e. normal or .exit) */
     if (socks->command == SOCKS_COMMAND_CONNECT && socks->port == 0) {
     if (socks->command == SOCKS_COMMAND_CONNECT && socks->port == 0) {
       log_fn(LOG_WARN,"Application asked to connect to port 0. Refusing.");
       log_fn(LOG_WARN,"Application asked to connect to port 0. Refusing.");
       return -1;
       return -1;
@@ -447,7 +463,7 @@ static int connection_ap_handshake_process_socks(connection_t *conn) {
       }
       }
     }
     }
   }
   }
-  return 0;
+  return 0; /* unreached but keeps the compiler happy */
 }
 }
 
 
 /** Iterate over the two bytes of stream_id until we get one that is not
 /** Iterate over the two bytes of stream_id until we get one that is not
@@ -991,18 +1007,34 @@ int connection_ap_can_use_exit(connection_t *conn, routerinfo_t *exit)
   tor_assert(conn);
   tor_assert(conn);
   tor_assert(conn->type == CONN_TYPE_AP);
   tor_assert(conn->type == CONN_TYPE_AP);
   tor_assert(conn->socks_request);
   tor_assert(conn->socks_request);
+  tor_assert(exit);
 
 
   log_fn(LOG_DEBUG,"considering nickname %s, for address %s / port %d:",
   log_fn(LOG_DEBUG,"considering nickname %s, for address %s / port %d:",
          exit->nickname, conn->socks_request->address,
          exit->nickname, conn->socks_request->address,
          conn->socks_request->port);
          conn->socks_request->port);
+
+  /* If a particular exit node has been requested for the new connection,
+   * make sure the exit node of the existing circuit matches exactly.
+   */
+  if (conn->chosen_exit_name) {
+    if (router_get_by_nickname(conn->chosen_exit_name) != exit) {
+      /* doesn't match */
+      log_fn(LOG_DEBUG,"Requested node '%s', considering node '%s'. No.",
+             conn->chosen_exit_name, exit->nickname);
+      return 0;
+    }
+  }
+
   if (conn->socks_request->command == SOCKS_COMMAND_RESOLVE) {
   if (conn->socks_request->command == SOCKS_COMMAND_RESOLVE) {
     /* 0.0.8 servers have buggy resolve support. */
     /* 0.0.8 servers have buggy resolve support. */
-    return tor_version_as_new_as(exit->platform, "0.0.9pre1");
+    if (!tor_version_as_new_as(exit->platform, "0.0.9pre1"))
+      return 0;
+  } else {
+    addr = client_dns_lookup_entry(conn->socks_request->address);
+    if (router_compare_addr_to_addr_policy(addr, conn->socks_request->port,
+                                           exit->exit_policy) < 0)
+      return 0;
   }
   }
-  addr = client_dns_lookup_entry(conn->socks_request->address);
-  if (router_compare_addr_to_addr_policy(addr, conn->socks_request->port,
-                                         exit->exit_policy) < 0)
-    return 0;
   return 1;
   return 1;
 }
 }
 
 
@@ -1211,3 +1243,40 @@ set_exit_redirects(smartlist_t *lst)
   redirect_exit_list = lst;
   redirect_exit_list = lst;
 }
 }
 
 
+/** If address is of the form "y.onion" with a well-formed handle y:
+ *     Put a '\0' after y, lower-case it, and return 2.
+ *
+ * If address is of the form "y.exit":
+ *     Put a '\0' after y and return 1.
+ *
+ * Otherwise:
+ *     Return 0 and change nothing.
+ */
+int parse_address(char *address) {
+    char *s;
+    char query[REND_SERVICE_ID_LEN+1];
+
+    s = strrchr(address,'.');
+    if (!s) return 0; /* no dot, thus normal */
+    if (!strcasecmp(s+1,"exit")) {
+      *s = 0; /* null-terminate it */
+      return 1; /* .exit */
+    }
+    if (strcasecmp(s+1,"onion"))
+      return 0; /* neither .exit nor .onion, thus normal */
+
+    /* so it is .onion */
+    *s = 0; /* null-terminate it */
+    if (strlcpy(query, address, REND_SERVICE_ID_LEN+1) >= REND_SERVICE_ID_LEN+1)
+      goto failed;
+    tor_strlower(query);
+    if (rend_valid_service_id(query)) {
+      tor_strlower(address);
+      return 2; /* success */
+    }
+failed:
+    /* otherwise, return to previous state and return 0 */
+    *s = '.';
+    return 0;
+}
+

+ 5 - 2
src/or/or.h

@@ -526,6 +526,9 @@ struct connection_t {
   char identity_digest[DIGEST_LEN]; /**< Hash of identity_pkey */
   char identity_digest[DIGEST_LEN]; /**< Hash of identity_pkey */
   char *nickname; /**< Nickname of OR on other side (if any). */
   char *nickname; /**< Nickname of OR on other side (if any). */
 
 
+  /** Nickname of planned exit node -- to be used with .exit support. */
+  char *chosen_exit_name;
+
 /* Used only by OR connections: */
 /* Used only by OR connections: */
   tor_tls *tls; /**< TLS connection state (OR only.) */
   tor_tls *tls; /**< TLS connection state (OR only.) */
   uint16_t next_circ_id; /**< Which circ_id do we try to use next on
   uint16_t next_circ_id; /**< Which circ_id do we try to use next on
@@ -1223,6 +1226,7 @@ int connection_ap_can_use_exit(connection_t *conn, routerinfo_t *exit);
 void connection_ap_expire_beginning(void);
 void connection_ap_expire_beginning(void);
 void connection_ap_attach_pending(void);
 void connection_ap_attach_pending(void);
 
 
+void parse_socks_policy(void);
 int socks_policy_permits_address(uint32_t addr);
 int socks_policy_permits_address(uint32_t addr);
 
 
 void client_dns_init(void);
 void client_dns_init(void);
@@ -1231,7 +1235,7 @@ int client_dns_incr_failures(const char *address);
 void client_dns_set_entry(const char *address, uint32_t val);
 void client_dns_set_entry(const char *address, uint32_t val);
 void client_dns_clean(void);
 void client_dns_clean(void);
 void set_exit_redirects(smartlist_t *lst);
 void set_exit_redirects(smartlist_t *lst);
-void parse_socks_policy(void);
+int parse_address(char *address);
 
 
 /********************************* connection_or.c ***************************/
 /********************************* connection_or.c ***************************/
 
 
@@ -1453,7 +1457,6 @@ int rend_client_receive_rendezvous(circuit_t *circ, const char *request, size_t
 void rend_client_desc_fetched(char *query, int status);
 void rend_client_desc_fetched(char *query, int status);
 
 
 char *rend_client_get_random_intro(char *query);
 char *rend_client_get_random_intro(char *query);
-int rend_parse_rendezvous_address(char *address);
 
 
 int rend_client_send_introduction(circuit_t *introcirc, circuit_t *rendcirc);
 int rend_client_send_introduction(circuit_t *introcirc, circuit_t *rendcirc);
 
 

+ 0 - 27
src/or/rendclient.c

@@ -439,30 +439,3 @@ char *rend_client_get_random_intro(char *query) {
   return nickname;
   return nickname;
 }
 }
 
 
-/** If address is of the form "y.onion" with a well-formed handle y,
- * then put a '\0' after y, lower-case it, and return 0.
- * Else return -1 and change nothing.
- */
-int rend_parse_rendezvous_address(char *address) {
-  char *s;
-  char query[REND_SERVICE_ID_LEN+1];
-
-  s = strrchr(address,'.');
-  if (!s) return -1; /* no dot */
-  if (strcasecmp(s+1,"onion"))
-    return -1; /* not .onion */
-
-  *s = 0; /* null terminate it */
-  if (strlcpy(query, address, REND_SERVICE_ID_LEN+1) >= REND_SERVICE_ID_LEN+1)
-    goto failed;
-  tor_strlower(query);
-  if (rend_valid_service_id(query)) {
-    tor_strlower(address);
-    return 0; /* success */
-  }
-failed:
-  /* otherwise, return to previous state and return -1 */
-  *s = '.';
-  return -1;
-}
-

+ 4 - 2
src/or/test.c

@@ -1182,6 +1182,7 @@ test_rend_fns(void)
 {
 {
   char address1[] = "fooaddress.onion";
   char address1[] = "fooaddress.onion";
   char address2[] = "aaaaaaaaaaaaaaaa.onion";
   char address2[] = "aaaaaaaaaaaaaaaa.onion";
+  char address3[] = "fooaddress.exit";
   rend_service_descriptor_t *d1, *d2;
   rend_service_descriptor_t *d1, *d2;
   char *encoded;
   char *encoded;
   size_t len;
   size_t len;
@@ -1210,8 +1211,9 @@ test_rend_fns(void)
   test_streq(d2->intro_points[1], "crow");
   test_streq(d2->intro_points[1], "crow");
   test_streq(d2->intro_points[2], "joel");
   test_streq(d2->intro_points[2], "joel");
 
 
-  test_eq(-1, rend_parse_rendezvous_address(address1));
-  test_eq( 0, rend_parse_rendezvous_address(address2));
+  test_eq(0, parse_address(address1));
+  test_eq(2, parse_address(address2));
+  test_eq(1, parse_address(address3));
 
 
   rend_service_descriptor_free(d1);
   rend_service_descriptor_free(d1);
   rend_service_descriptor_free(d2);
   rend_service_descriptor_free(d2);