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 18 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
       documents; authorities do not yet cache them.)  [Partially implements
       proposal 104.]
       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
     - Add a new config option __DisablePredictedCircuits designed for
       use by the controller, when we don't want Tor to build any circuits
       use by the controller, when we don't want Tor to build any circuits
       preemptively.
       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:
   o Removed features:
     - Removed support for the old binary "version 0" controller protocol.
     - 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
 3.13. ATTACHSTREAM
 
 
   Sent from the client to the server.  The syntax is:
   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
   This message informs the server that the specified stream should be
   associated with the specified circuit.  Each stream may be associated with
   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
   If the circuit ID is 0, responsibility for attaching the given stream is
   returned to Tor.
   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
   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
   or stream didn't exist, or 551 if the stream couldn't be attached for
   another reason.
   another reason.

+ 16 - 0
src/or/circuitlist.c

@@ -844,6 +844,22 @@ circuit_get_cpath_len(origin_circuit_t *circ)
   return n;
   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
 /** Go through the circuitlist; mark-for-close each circuit that starts
  *  at us but has not yet been used. */
  *  at us but has not yet been used. */
 void
 void

+ 15 - 9
src/or/circuituse.c

@@ -1101,7 +1101,8 @@ circuit_get_open_circ_or_launch(edge_connection_t *conn,
  * circ's cpath.
  * circ's cpath.
  */
  */
 static void
 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 */
   /* 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.",
   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)); */
   /* assert_connection_ok(conn, time(NULL)); */
   circ->p_streams = apconn;
   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
 /** 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. */
  * for connection_ap_handshake_attach_circuit. */
 int
 int
 connection_ap_handshake_attach_chosen_circuit(edge_connection_t *conn,
 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);
   tor_assert(conn->_base.state == AP_CONN_STATE_CIRCUIT_WAIT ||
   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)
   if (!circ->_base.timestamp_dirty)
     circ->_base.timestamp_dirty = time(NULL);
     circ->_base.timestamp_dirty = time(NULL);
 
 
-  link_apconn_to_circ(conn, circ);
+  link_apconn_to_circ(conn, circ, cpath);
   tor_assert(conn->socks_request);
   tor_assert(conn->socks_request);
   switch (conn->socks_request->command) {
   switch (conn->socks_request->command) {
     case SOCKS_COMMAND_CONNECT:
     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);
     circuit_log_path(LOG_INFO,LD_APP|LD_CIRC,circ);
 
 
     /* We have found a suitable circuit for our conn. Hurray. */
     /* 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 */
   } else { /* we're a rendezvous conn */
     origin_circuit_t *rendcirc=NULL, *introcirc=NULL;
     origin_circuit_t *rendcirc=NULL, *introcirc=NULL;
@@ -1295,7 +1301,7 @@ connection_ap_handshake_attach_circuit(edge_connection_t *conn)
        * feasibility, at this point.
        * feasibility, at this point.
        */
        */
       rendcirc->_base.timestamp_dirty = time(NULL);
       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)
       if (connection_ap_handshake_send_begin(conn) < 0)
         return 0; /* already marked, let them fade away */
         return 0; /* already marked, let them fade away */
       return 1;
       return 1;

+ 7 - 6
src/or/connection_edge.c

@@ -1181,7 +1181,8 @@ addressmap_get_mappings(smartlist_t *sl, time_t min_expires,
  */
  */
 int
 int
 connection_ap_handshake_rewrite_and_attach(edge_connection_t *conn,
 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;
   socks_request_t *socks = conn->socks_request;
   hostname_type_t addresstype;
   hostname_type_t addresstype;
@@ -1337,8 +1338,8 @@ connection_ap_handshake_rewrite_and_attach(edge_connection_t *conn,
       tor_fragile_assert();
       tor_fragile_assert();
     }
     }
     conn->_base.state = AP_CONN_STATE_CIRCUIT_WAIT;
     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 &&
         (!circ &&
          connection_ap_handshake_attach_circuit(conn) < 0)) {
          connection_ap_handshake_attach_circuit(conn) < 0)) {
       connection_mark_unattached_ap(conn, END_STREAM_REASON_CANT_ATTACH);
       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;
     conn->_base.state = AP_CONN_STATE_CONTROLLER_WAIT;
     return 0;
     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.
 /** 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;
     conn->_base.state = AP_CONN_STATE_CONTROLLER_WAIT;
     return 0;
     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
 /** 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;
   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
 /** 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;
   origin_circuit_t *circ = NULL;
   int zero_circ;
   int zero_circ;
   smartlist_t *args;
   smartlist_t *args;
+  crypt_path_t *cpath=NULL;
+  int hop=0, hop_line_ok=1;
   (void) len;
   (void) len;
 
 
   args = getargs_helper("ATTACHSTREAM", conn, body, 2, -1);
   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)))) {
   } else if (!zero_circ && !(circ = get_circ(smartlist_get(args, 1)))) {
     connection_printf_to_buf(conn, "552 Unknown circuit \"%s\"\r\n",
     connection_printf_to_buf(conn, "552 Unknown circuit \"%s\"\r\n",
                              (char*)smartlist_get(args, 1));
                              (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_FOREACH(args, char *, cp, tor_free(cp));
   smartlist_free(args);
   smartlist_free(args);
-  if (!ap_conn || (!zero_circ && !circ))
+  if (!ap_conn || (!zero_circ && !circ) || !hop_line_ok)
     return 0;
     return 0;
 
 
   if (ap_conn->_base.state != AP_CONN_STATE_CONTROLLER_WAIT &&
   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)) {
   if (circ && (circ->_base.state != CIRCUIT_STATE_OPEN)) {
     connection_write_str_to_buf(
     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);
                     conn);
     return 0;
     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(
     connection_write_str_to_buf(
                     "551 Can't attach stream to one-hop circuit.\r\n", conn);
                     "551 Can't attach stream to one-hop circuit.\r\n", conn);
     return 0;
     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);
     connection_write_str_to_buf("551 Unable to attach stream\r\n", conn);
     return 0;
     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,
 void _circuit_mark_for_close(circuit_t *circ, int reason,
                              int line, const char *file);
                              int line, const char *file);
 int circuit_get_cpath_len(origin_circuit_t *circ);
 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,
 void circuit_get_all_pending_on_or_conn(smartlist_t *out,
                                         or_connection_t *or_conn);
                                         or_connection_t *or_conn);
 int circuit_count_pending_on_or_conn(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);
                                     int is_internal);
 void circuit_reset_failure_count(int timeout);
 void circuit_reset_failure_count(int timeout);
 int connection_ap_handshake_attach_chosen_circuit(edge_connection_t *conn,
 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);
 int connection_ap_handshake_attach_circuit(edge_connection_t *conn);
 
 
 /********************************* command.c ***************************/
 /********************************* 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,
 void addressmap_get_mappings(smartlist_t *sl, time_t min_expires,
                              time_t max_expires);
                              time_t max_expires);
 int connection_ap_handshake_rewrite_and_attach(edge_connection_t *conn,
 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);
 void set_exit_redirects(smartlist_t *lst);
 typedef enum hostname_type_t {
 typedef enum hostname_type_t {