Browse Source

Let the controller specify HOP=%d as an argument to ATTACHSTREAM,
so we can exit from the middle of the circuit.


svn:r10056

Roger Dingledine 17 years ago
parent
commit
5ba4eaba1c
7 changed files with 79 additions and 25 deletions
  1. 7 3
      ChangeLog
  2. 4 1
      doc/spec/control-spec.txt
  3. 16 0
      src/or/circuitlist.c
  4. 15 9
      src/or/circuituse.c
  5. 7 6
      src/or/connection_edge.c
  6. 25 4
      src/or/control.c
  7. 5 2
      src/or/or.h

+ 7 - 3
ChangeLog

@@ -60,12 +60,16 @@ Changes in version 0.2.0.1-alpha - 2007-??-??
       documents; authorities do not yet cache them.)  [Partially implements
       proposal 104.]
 
-  o Minor features (other):
-    - Correctly report Windows 95 OSR2 and Windows 98 SE.
-    - More unit tests.
+  o Minor features (controller):
     - Add a new config option __DisablePredictedCircuits designed for
       use by the controller, when we don't want Tor to build any circuits
       preemptively.
+    - Let the controller specify HOP=%d as an argument to ATTACHSTREAM,
+      so we can exit from the middle of the circuit.
+
+  o Minor features (other):
+    - Correctly report Windows 95 OSR2 and Windows 98 SE.
+    - More unit tests.
 
   o Removed features:
     - Removed support for the old binary "version 0" controller protocol.

+ 4 - 1
doc/spec/control-spec.txt

@@ -535,7 +535,7 @@ $Id$
 3.13. ATTACHSTREAM
 
   Sent from the client to the server.  The syntax is:
-     "ATTACHSTREAM" SP StreamID SP CircuitID CRLF
+     "ATTACHSTREAM" SP StreamID SP CircuitID ["HOP=" HopNum] CRLF
 
   This message informs the server that the specified stream should be
   associated with the specified circuit.  Each stream may be associated with
@@ -547,6 +547,9 @@ $Id$
   If the circuit ID is 0, responsibility for attaching the given stream is
   returned to Tor.
 
+  If HOP=HopNum is specified, Tor will choose the HopNumth hop in the
+  circuit as the exit node, rather than the last node in the circuit.
+
   Tor responds with "250 OK" if it can attach the stream, 552 if the circuit
   or stream didn't exist, or 551 if the stream couldn't be attached for
   another reason.

+ 16 - 0
src/or/circuitlist.c

@@ -844,6 +844,22 @@ circuit_get_cpath_len(origin_circuit_t *circ)
   return n;
 }
 
+/** Return the <b>hopnum</b>th hop in <b>circ</b>->cpath, or NULL if there
+ * aren't that many hops in the list. */
+crypt_path_t *
+circuit_get_cpath_hop(origin_circuit_t *circ, int hopnum)
+{
+  if (circ && circ->cpath) {
+    crypt_path_t *cpath, *cpath_next = NULL;
+    for (cpath = circ->cpath; cpath_next != circ->cpath; cpath = cpath_next) {
+      cpath_next = cpath->next;
+      if (--hopnum <= 0)
+        return cpath;
+    }
+  }
+  return NULL;
+}
+
 /** Go through the circuitlist; mark-for-close each circuit that starts
  *  at us but has not yet been used. */
 void

+ 15 - 9
src/or/circuituse.c

@@ -1101,7 +1101,8 @@ circuit_get_open_circ_or_launch(edge_connection_t *conn,
  * circ's cpath.
  */
 static void
-link_apconn_to_circ(edge_connection_t *apconn, origin_circuit_t *circ)
+link_apconn_to_circ(edge_connection_t *apconn, origin_circuit_t *circ,
+                    crypt_path_t *cpath)
 {
   /* add it into the linked list of streams on this circuit */
   log_debug(LD_APP|LD_CIRC, "attaching new conn to circ. n_circ_id %d.",
@@ -1113,10 +1114,14 @@ link_apconn_to_circ(edge_connection_t *apconn, origin_circuit_t *circ)
   /* assert_connection_ok(conn, time(NULL)); */
   circ->p_streams = apconn;
 
-  tor_assert(circ->cpath);
-  tor_assert(circ->cpath->prev);
-  tor_assert(circ->cpath->prev->state == CPATH_STATE_OPEN);
-  apconn->cpath_layer = circ->cpath->prev;
+  if (cpath) { /* we were given one; use it */
+    apconn->cpath_layer = cpath;
+  } else { /* use the last hop in the circuit */
+    tor_assert(circ->cpath);
+    tor_assert(circ->cpath->prev);
+    tor_assert(circ->cpath->prev->state == CPATH_STATE_OPEN);
+    apconn->cpath_layer = circ->cpath->prev;
+  }
 }
 
 /** If an exit wasn't specifically chosen, save the history for future
@@ -1172,7 +1177,8 @@ consider_recording_trackhost(edge_connection_t *conn, origin_circuit_t *circ)
  * for connection_ap_handshake_attach_circuit. */
 int
 connection_ap_handshake_attach_chosen_circuit(edge_connection_t *conn,
-                                              origin_circuit_t *circ)
+                                              origin_circuit_t *circ,
+                                              crypt_path_t *cpath)
 {
   tor_assert(conn);
   tor_assert(conn->_base.state == AP_CONN_STATE_CIRCUIT_WAIT ||
@@ -1186,7 +1192,7 @@ connection_ap_handshake_attach_chosen_circuit(edge_connection_t *conn,
   if (!circ->_base.timestamp_dirty)
     circ->_base.timestamp_dirty = time(NULL);
 
-  link_apconn_to_circ(conn, circ);
+  link_apconn_to_circ(conn, circ, cpath);
   tor_assert(conn->socks_request);
   switch (conn->socks_request->command) {
     case SOCKS_COMMAND_CONNECT:
@@ -1269,7 +1275,7 @@ connection_ap_handshake_attach_circuit(edge_connection_t *conn)
     circuit_log_path(LOG_INFO,LD_APP|LD_CIRC,circ);
 
     /* We have found a suitable circuit for our conn. Hurray. */
-    return connection_ap_handshake_attach_chosen_circuit(conn, circ);
+    return connection_ap_handshake_attach_chosen_circuit(conn, circ, NULL);
 
   } else { /* we're a rendezvous conn */
     origin_circuit_t *rendcirc=NULL, *introcirc=NULL;
@@ -1295,7 +1301,7 @@ connection_ap_handshake_attach_circuit(edge_connection_t *conn)
        * feasibility, at this point.
        */
       rendcirc->_base.timestamp_dirty = time(NULL);
-      link_apconn_to_circ(conn, rendcirc);
+      link_apconn_to_circ(conn, rendcirc, NULL);
       if (connection_ap_handshake_send_begin(conn) < 0)
         return 0; /* already marked, let them fade away */
       return 1;

+ 7 - 6
src/or/connection_edge.c

@@ -1181,7 +1181,8 @@ addressmap_get_mappings(smartlist_t *sl, time_t min_expires,
  */
 int
 connection_ap_handshake_rewrite_and_attach(edge_connection_t *conn,
-                                           origin_circuit_t *circ)
+                                           origin_circuit_t *circ,
+                                           crypt_path_t *cpath)
 {
   socks_request_t *socks = conn->socks_request;
   hostname_type_t addresstype;
@@ -1337,8 +1338,8 @@ connection_ap_handshake_rewrite_and_attach(edge_connection_t *conn,
       tor_fragile_assert();
     }
     conn->_base.state = AP_CONN_STATE_CIRCUIT_WAIT;
-    if ((circ &&
-         connection_ap_handshake_attach_chosen_circuit(conn, circ) < 0) ||
+    if ((circ && connection_ap_handshake_attach_chosen_circuit(
+                   conn, circ, cpath) < 0) ||
         (!circ &&
          connection_ap_handshake_attach_circuit(conn) < 0)) {
       connection_mark_unattached_ap(conn, END_STREAM_REASON_CANT_ATTACH);
@@ -1583,7 +1584,7 @@ connection_ap_handshake_process_socks(edge_connection_t *conn)
     conn->_base.state = AP_CONN_STATE_CONTROLLER_WAIT;
     return 0;
   }
-  return connection_ap_handshake_rewrite_and_attach(conn, NULL);
+  return connection_ap_handshake_rewrite_and_attach(conn, NULL, NULL);
 }
 
 /** connection_init_accepted_conn() found a new trans AP conn.
@@ -1625,7 +1626,7 @@ connection_ap_process_transparent(edge_connection_t *conn)
     conn->_base.state = AP_CONN_STATE_CONTROLLER_WAIT;
     return 0;
   }
-  return connection_ap_handshake_rewrite_and_attach(conn, NULL);
+  return connection_ap_handshake_rewrite_and_attach(conn, NULL, NULL);
 }
 
 /** connection_edge_process_inbuf() found a conn in state natd_wait. See if
@@ -1704,7 +1705,7 @@ connection_ap_process_natd(edge_connection_t *conn)
   }
   conn->_base.state = AP_CONN_STATE_CIRCUIT_WAIT;
 
-  return connection_ap_handshake_rewrite_and_attach(conn, NULL);
+  return connection_ap_handshake_rewrite_and_attach(conn, NULL, NULL);
 }
 
 /** Iterate over the two bytes of stream_id until we get one that is not

+ 25 - 4
src/or/control.c

@@ -1897,6 +1897,8 @@ handle_control_attachstream(control_connection_t *conn, uint32_t len,
   origin_circuit_t *circ = NULL;
   int zero_circ;
   smartlist_t *args;
+  crypt_path_t *cpath=NULL;
+  int hop=0, hop_line_ok=1;
   (void) len;
 
   args = getargs_helper("ATTACHSTREAM", conn, body, 2, -1);
@@ -1911,10 +1913,20 @@ handle_control_attachstream(control_connection_t *conn, uint32_t len,
   } else if (!zero_circ && !(circ = get_circ(smartlist_get(args, 1)))) {
     connection_printf_to_buf(conn, "552 Unknown circuit \"%s\"\r\n",
                              (char*)smartlist_get(args, 1));
+  } else if (circ && smartlist_len(args) > 2) {
+    char *hopstring = smartlist_get(args, 2);
+    if (!strcasecmpstart(hopstring, "HOP=")) {
+      hopstring += strlen("HOP=");
+      hop = tor_parse_ulong(hopstring, 10, 0, ULONG_MAX,
+                            &hop_line_ok, NULL);
+      if (!hop_line_ok) { /* broken hop line */
+        connection_printf_to_buf(conn, "552 Bad value hop=%s\r\n", hopstring);
+      }
+    }
   }
   SMARTLIST_FOREACH(args, char *, cp, tor_free(cp));
   smartlist_free(args);
-  if (!ap_conn || (!zero_circ && !circ))
+  if (!ap_conn || (!zero_circ && !circ) || !hop_line_ok)
     return 0;
 
   if (ap_conn->_base.state != AP_CONN_STATE_CONTROLLER_WAIT &&
@@ -1940,16 +1952,25 @@ handle_control_attachstream(control_connection_t *conn, uint32_t len,
 
   if (circ && (circ->_base.state != CIRCUIT_STATE_OPEN)) {
     connection_write_str_to_buf(
-                    "551 Can't attach stream to non-open, origin circuit\r\n",
+                    "551 Can't attach stream to non-open origin circuit\r\n",
                     conn);
     return 0;
   }
-  if (circ && circuit_get_cpath_len(circ) < 2) {
+  if (circ && (circuit_get_cpath_len(circ)<2 || hop==1)) {
     connection_write_str_to_buf(
                     "551 Can't attach stream to one-hop circuit.\r\n", conn);
     return 0;
   }
-  if (connection_ap_handshake_rewrite_and_attach(ap_conn, circ) < 0) {
+  if (circ && hop>0) {
+    /* find this hop in the circuit, and set cpath */
+    cpath = circuit_get_cpath_hop(circ, hop);
+    if (!cpath) {
+      connection_printf_to_buf(conn,
+                               "551 Circuit doesn't have %d hops.\r\n", hop);
+      return 0;
+    }
+  }
+  if (connection_ap_handshake_rewrite_and_attach(ap_conn, circ, cpath) < 0) {
     connection_write_str_to_buf("551 Unable to attach stream\r\n", conn);
     return 0;
   }

+ 5 - 2
src/or/or.h

@@ -2085,6 +2085,7 @@ void circuit_expire_all_dirty_circs(void);
 void _circuit_mark_for_close(circuit_t *circ, int reason,
                              int line, const char *file);
 int circuit_get_cpath_len(origin_circuit_t *circ);
+crypt_path_t *circuit_get_cpath_hop(origin_circuit_t *circ, int hopnum);
 void circuit_get_all_pending_on_or_conn(smartlist_t *out,
                                         or_connection_t *or_conn);
 int circuit_count_pending_on_or_conn(or_connection_t *or_conn);
@@ -2127,7 +2128,8 @@ origin_circuit_t *circuit_launch_by_router(uint8_t purpose,
                                     int is_internal);
 void circuit_reset_failure_count(int timeout);
 int connection_ap_handshake_attach_chosen_circuit(edge_connection_t *conn,
-                                                  origin_circuit_t *circ);
+                                                  origin_circuit_t *circ,
+                                                  crypt_path_t *cpath);
 int connection_ap_handshake_attach_circuit(edge_connection_t *conn);
 
 /********************************* command.c ***************************/
@@ -2322,7 +2324,8 @@ const char *addressmap_register_virtual_address(int type, char *new_address);
 void addressmap_get_mappings(smartlist_t *sl, time_t min_expires,
                              time_t max_expires);
 int connection_ap_handshake_rewrite_and_attach(edge_connection_t *conn,
-                                               origin_circuit_t *circ);
+                                               origin_circuit_t *circ,
+                                               crypt_path_t *cpath);
 
 void set_exit_redirects(smartlist_t *lst);
 typedef enum hostname_type_t {