[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]

[or-cvs] alice can now look up bob"s service descriptor,



Update of /home/or/cvsroot/src/or
In directory moria.mit.edu:/home2/arma/work/onion/cvs/src/or

Modified Files:
	circuit.c config.c connection.c connection_edge.c dirserv.c 
	main.c or.h rendclient.c rendcommon.c routerlist.c 
Log Message:
alice can now look up bob's service descriptor,
choose an intro point, connect to it,
choose a rend point, connect to it and establish a cookie,
get an ack from the rendezvous point,
and know when both circs are ready for her.

APConns don't use conn->purpose anymore

don't initiate a renddesc lookup if one is already in progress

also fix a buffer overflow in nickname parsing (only exploitable
by the operator though)


Index: circuit.c
===================================================================
RCS file: /home/or/cvsroot/src/or/circuit.c,v
retrieving revision 1.181
retrieving revision 1.182
diff -u -d -r1.181 -r1.182
--- circuit.c	3 Apr 2004 04:05:30 -0000	1.181
+++ circuit.c	5 Apr 2004 00:47:47 -0000	1.182
@@ -13,7 +13,7 @@
 static uint16_t get_unique_circ_id_by_conn(connection_t *conn, int circ_id_type);
 static void circuit_rep_hist_note_result(circuit_t *circ);
 
-static void circuit_is_ready(circuit_t *circ);
+static void circuit_is_open(circuit_t *circ);
 static void circuit_failed(circuit_t *circ);
 static circuit_t *circuit_establish_circuit(uint8_t purpose, const char *exit_nickname);
 
@@ -250,21 +250,30 @@
   return NULL;
 }
 
-/* Find the newest circ that conn can use, preferably one which is
+/* Dear god this function needs refactoring. */
+/* Find the best circ that conn can use, preferably one which is
  * dirty. Circ must not be too old.
  * If !conn, return newest.
  *
  * If must_be_open, ignore circs not in CIRCUIT_STATE_OPEN.
  *
  * circ_purpose specifies what sort of circuit we must have.
+ * It can be C_GENERAL, C_INTRODUCING, or C_REND_JOINED.
+ *
+ * If it's REND_JOINED and must_be_open==0, then return the closest
+ * rendezvous-purposed circuit that you can find.
+ *
  * If circ_purpose is not GENERAL, then conn must be defined.
- * If circ_purpose is C_ESTABLISH_REND, then it's also ok
- * to return a C_REND_JOINED circ.
  */
-circuit_t *circuit_get_newest(connection_t *conn,
-                              int must_be_open, uint8_t circ_purpose) {
-  circuit_t *circ, *newest=NULL, *leastdirty=NULL;
+circuit_t *circuit_get_best(connection_t *conn,
+                            int must_be_open, uint8_t purpose) {
+  circuit_t *circ, *best=NULL;
   routerinfo_t *exitrouter;
+  time_t now = time(NULL);
+
+  assert(purpose == CIRCUIT_PURPOSE_C_GENERAL ||
+         purpose == CIRCUIT_PURPOSE_C_INTRODUCING ||
+         purpose == CIRCUIT_PURPOSE_C_REND_JOINED);
 
   for (circ=global_circuitlist;circ;circ = circ->next) {
     if (!circ->cpath)
@@ -274,18 +283,23 @@
     if (circ->marked_for_close)
       continue;
 
-    /* if this isn't our purpose, skip. except, if our purpose is
-     * establish_rend, keep going if circ is rend_joined.
-     */
-    if (circ->purpose != circ_purpose &&
-      (circ_purpose != CIRCUIT_PURPOSE_C_ESTABLISH_REND ||
-       circ->purpose != CIRCUIT_PURPOSE_C_REND_JOINED))
-      continue;
+    /* if this circ isn't our purpose, skip. */
+    if(purpose == CIRCUIT_PURPOSE_C_REND_JOINED) {
+      if(must_be_open && purpose != circ->purpose)
+        continue;
+      if(circ->purpose != CIRCUIT_PURPOSE_C_ESTABLISH_REND &&
+         circ->purpose != CIRCUIT_PURPOSE_C_REND_READY &&
+         circ->purpose != CIRCUIT_PURPOSE_C_REND_JOINED)
+        continue;
+    } else {
+      if(purpose != circ->purpose)
+        continue;
+    }
 
-#if 0
-    if (must_be_clean && circ->timestamp_dirty)
-      continue; /* ignore dirty circs */
-#endif
+    if(purpose == CIRCUIT_PURPOSE_C_GENERAL)
+      if(circ->timestamp_dirty &&
+         circ->timestamp_dirty+options.NewCircuitPeriod < now)
+        continue; /* too old */
 
     if(conn) {
       /* decide if this circ is suitable for this conn */
@@ -296,10 +310,12 @@
       else /* not open */
         exitrouter = router_get_by_nickname(circ->build_state->chosen_exit);
 
-      if(!exitrouter)
+      if(!exitrouter) {
+        log_fn(LOG_INFO,"Skipping broken circ (exit router vanished)");
         continue; /* this circuit is screwed and doesn't know it yet */
+      }
 
-      if(circ_purpose == CIRCUIT_PURPOSE_C_GENERAL) {
+      if(purpose == CIRCUIT_PURPOSE_C_GENERAL) {
         if(connection_ap_can_use_exit(conn, exitrouter) == ADDR_POLICY_REJECTED) {
           /* can't exit from this router */
           continue;
@@ -312,27 +328,40 @@
       }
     }
 
-    if(!newest || newest->timestamp_created < circ->timestamp_created) {
-      newest = circ;
-    }
-    if(conn && circ->timestamp_dirty &&
-       (!leastdirty || leastdirty->timestamp_dirty < circ->timestamp_dirty)) {
-      leastdirty = circ;
+    /* now this is an acceptable circ to hand back. but that doesn't
+     * mean it's the *best* circ to hand back. try to decide.
+     */
+    if(!best)
+      best = circ;
+    switch(purpose) {
+      case CIRCUIT_PURPOSE_C_GENERAL:
+        /* if it's used but less dirty it's best;
+         * else if it's more recently created it's best
+         */
+        if(best->timestamp_dirty) {
+          if(circ->timestamp_dirty &&
+             circ->timestamp_dirty > best->timestamp_dirty)
+            best = circ;
+        } else {
+          if(circ->timestamp_dirty ||
+             circ->timestamp_created > best->timestamp_created)
+            best = circ;
+        }
+        break;
+      case CIRCUIT_PURPOSE_C_INTRODUCING:
+        /* more recently created is best */
+        if(circ->timestamp_created > best->timestamp_created)
+          best = circ;
+        break;
+      case CIRCUIT_PURPOSE_C_REND_JOINED:
+        /* the closer it is to rend_joined the better it is */
+        if(circ->purpose > best->purpose)
+          best = circ;
+        break;
     }
   }
 
-  if(leastdirty &&
-     leastdirty->timestamp_dirty+options.NewCircuitPeriod > time(NULL)) {
-/*    log_fn(LOG_DEBUG,"Choosing in-use circuit %s:%d:%d.",
-           leastdirty->n_conn->address, leastdirty->n_port, leastdirty->n_circ_id); */
-    return leastdirty;
-  }
-  if(newest) {
-/*    log_fn(LOG_DEBUG,"Choosing circuit %s:%d:%d.",
-           newest->n_conn->address, newest->n_port, newest->n_circ_id); */
-    return newest;
-  }
-  return NULL;
+  return best;
 }
 
 /* Return the first circuit in global_circuitlist after 'start' whose
@@ -422,8 +451,9 @@
 }
 
 #define MIN_CIRCUITS_HANDLING_STREAM 2
-/* return 1 if at least MIN_CIRCUITS_HANDLING_STREAM non-open circuits
- * will have an acceptable exit node for conn. Else return 0.
+/* return 1 if at least MIN_CIRCUITS_HANDLING_STREAM non-open
+ * general-purpose circuits will have an acceptable exit node for
+ * conn. Else return 0.
  */
 int circuit_stream_is_being_handled(connection_t *conn) {
   circuit_t *circ;
@@ -432,7 +462,7 @@
 
   for(circ=global_circuitlist;circ;circ = circ->next) {
     if(circ->cpath && circ->state != CIRCUIT_STATE_OPEN &&
-       !circ->marked_for_close) {
+       !circ->marked_for_close && circ->purpose == CIRCUIT_PURPOSE_C_GENERAL) {
       exitrouter = router_get_by_nickname(circ->build_state->chosen_exit);
       if(exitrouter && connection_ap_can_use_exit(conn, exitrouter) != ADDR_POLICY_REJECTED)
         if(++num >= MIN_CIRCUITS_HANDLING_STREAM)
@@ -1047,22 +1077,18 @@
   smartlist_free(unused_open_circs);
 }
 
-static void circuit_is_ready(circuit_t *circ) {
+static void circuit_is_open(circuit_t *circ) {
 
-  /* should maybe break this into rend_circuit_is_ready() one day */
   switch(circ->purpose) {
-    case CIRCUIT_PURPOSE_C_GENERAL:
-      /* Tell any AP connections that have been waiting for a new
-       * circuit that one is ready. */
-      connection_ap_attach_pending();
+    case CIRCUIT_PURPOSE_C_ESTABLISH_REND:
+      rend_client_rendcirc_is_open(circ);
       break;
     case CIRCUIT_PURPOSE_C_INTRODUCING:
-      /* at Alice, connecting to intro point */
-      connection_ap_attach_pending();
+      rend_client_introcirc_is_open(circ);
       break;
-    case CIRCUIT_PURPOSE_C_ESTABLISH_REND:
-      /* at Alice, waiting for Bob */
-      /* XXXNM make and send the rendezvous cookie, and store it in circ */
+    case CIRCUIT_PURPOSE_C_GENERAL:
+      /* Tell any AP connections that have been waiting for a new
+       * circuit that one is ready. */
       connection_ap_attach_pending();
       break;
     case CIRCUIT_PURPOSE_S_ESTABLISH_INTRO:
@@ -1278,7 +1304,7 @@
         log_fn(LOG_NOTICE,"Tor has successfully opened a circuit. Looks like it's working.");
       }
       circuit_rep_hist_note_result(circ);
-      circuit_is_ready(circ); /* do other actions as necessary */
+      circuit_is_open(circ); /* do other actions as necessary */
       return 0;
     } else if (r<0) {
       log_fn(LOG_INFO,"Unable to extend circuit path.");

Index: config.c
===================================================================
RCS file: /home/or/cvsroot/src/or/config.c,v
retrieving revision 1.105
retrieving revision 1.106
diff -u -d -r1.105 -r1.106
--- config.c	2 Apr 2004 22:06:46 -0000	1.105
+++ config.c	5 Apr 2004 00:47:47 -0000	1.106
@@ -596,10 +596,17 @@
     if (options->Nickname == NULL) {
       log_fn(LOG_WARN,"Nickname required if ORPort is set, but not found.");
       result = -1;
-    } else if (strspn(options->Nickname, LEGAL_NICKNAME_CHARACTERS) !=
-               strlen(options->Nickname)) {
-      log_fn(LOG_WARN, "Nickname '%s' contains illegal characters.", options->Nickname);
-      result = -1;
+    } else {
+      if (strspn(options->Nickname, LEGAL_NICKNAME_CHARACTERS) !=
+                 strlen(options->Nickname)) {
+        log_fn(LOG_WARN, "Nickname '%s' contains illegal characters.", options->Nickname);
+        result = -1;
+      }
+      if (strlen(options->Nickname) > MAX_NICKNAME_LEN) {
+        log_fn(LOG_WARN, "Nickname '%s' has more than %d characters.",
+               options->Nickname, MAX_NICKNAME_LEN);
+        result = -1;
+      }
     }
   }
 
@@ -654,6 +661,10 @@
     result = -1;
   }
 
+  /* XXX look at the various nicknamelists and make sure they're
+   * valid and don't have hostnames that are too long.
+   */
+
   if (rend_config_services(options) < 0) {
     result = -1;
   }

Index: connection.c
===================================================================
RCS file: /home/or/cvsroot/src/or/connection.c,v
retrieving revision 1.191
retrieving revision 1.192
diff -u -d -r1.191 -r1.192
--- connection.c	3 Apr 2004 00:55:53 -0000	1.191
+++ connection.c	5 Apr 2004 00:47:47 -0000	1.192
@@ -866,6 +866,22 @@
   return best;
 }
 
+connection_t *connection_get_by_type_rendquery(int type, char *rendquery) {
+  int i, n;
+  connection_t *conn;
+  connection_t **carray;
+
+  get_connection_array(&carray,&n);
+  for(i=0;i<n;i++) {
+    conn = carray[i];
+    if(conn->type == type &&
+       !conn->marked_for_close &&
+       !rend_cmp_service_ids(rendquery, conn->rend_query))
+      return conn;
+  }
+  return NULL;
+}
+
 int connection_is_listener(connection_t *conn) {
   if(conn->type == CONN_TYPE_OR_LISTENER ||
      conn->type == CONN_TYPE_AP_LISTENER ||
@@ -1026,9 +1042,8 @@
   } else {
     assert(!conn->socks_request);
   }
-  if(conn->type != CONN_TYPE_DIR &&
-     conn->type != CONN_TYPE_AP) {
-    assert(!conn->purpose); /* only used for dir and ap types currently */
+  if(conn->type != CONN_TYPE_DIR) {
+    assert(!conn->purpose); /* only used for dir types currently */
   }
 
   switch(conn->type)

Index: connection_edge.c
===================================================================
RCS file: /home/or/cvsroot/src/or/connection_edge.c,v
retrieving revision 1.135
retrieving revision 1.136
diff -u -d -r1.135 -r1.136
--- connection_edge.c	3 Apr 2004 04:55:22 -0000	1.135
+++ connection_edge.c	5 Apr 2004 00:47:47 -0000	1.136
@@ -76,6 +76,7 @@
     case AP_CONN_STATE_OPEN:
     case EXIT_CONN_STATE_OPEN:
       if(conn->package_window <= 0) {
+        /* XXX this is still getting called rarely :( */
         log_fn(LOG_WARN,"called with package_window %d. Tell Roger.", conn->package_window);
         return 0;
       }
@@ -85,6 +86,7 @@
       }
       return 0;
     case EXIT_CONN_STATE_CONNECTING:
+    case AP_CONN_STATE_RENDDESC_WAIT:
     case AP_CONN_STATE_CIRCUIT_WAIT:
     case AP_CONN_STATE_CONNECT_WAIT:
       log_fn(LOG_INFO,"data from edge while in '%s' state. Leaving it on buffer.",
@@ -235,6 +237,7 @@
 
   if(conn && conn->state != AP_CONN_STATE_OPEN && conn->state != EXIT_CONN_STATE_OPEN) {
     if(rh.command == RELAY_COMMAND_END) {
+      circuit_log_path(LOG_INFO,circ);
       log_fn(LOG_INFO,"Edge got end (%s) before we're connected. Marking for close.",
         connection_edge_end_reason(cell->payload+RELAY_HEADER_SIZE, rh.length));
       conn->has_sent_end = 1; /* we just got an 'end', don't need to send one */
@@ -335,9 +338,7 @@
          */
         addr = ntohl(get_uint32(cell->payload+RELAY_HEADER_SIZE+1));
         client_dns_set_entry(conn->socks_request->address, addr);
-        /* conn->purpose is still set to general */
         conn->state = AP_CONN_STATE_CIRCUIT_WAIT;
-        /* attaching to a dirty circuit is fine */
         if(connection_ap_handshake_attach_circuit(conn) >= 0)
           return 0;
         /* else, conn will get closed below */
@@ -491,6 +492,7 @@
       connection_edge_consider_sending_sendme(conn);
       return 0;
     case AP_CONN_STATE_SOCKS_WAIT:
+    case AP_CONN_STATE_RENDDESC_WAIT:
     case AP_CONN_STATE_CIRCUIT_WAIT:
     case AP_CONN_STATE_CONNECT_WAIT:
       connection_stop_writing(conn);
@@ -609,7 +611,6 @@
       conn->has_sent_end = 0;
       /* move it back into 'pending' state. */
       conn->state = AP_CONN_STATE_CIRCUIT_WAIT;
-      /* conn->purpose is still set to general */
       circuit_detach_stream(circ, conn);
       /* kludge to make us not try this circuit again, yet to allow
        * current streams on it to survive if they can: make it
@@ -642,7 +643,6 @@
     if (conn->type != CONN_TYPE_AP ||
         conn->state != AP_CONN_STATE_CIRCUIT_WAIT)
       continue;
-    /* attaching to a dirty circuit is fine */
     if(connection_ap_handshake_attach_circuit(conn) < 0) {
       /* -1 means it will never work */
       /* Don't send end; there is no 'other side' yet */
@@ -709,48 +709,49 @@
   if (rend_parse_rendezvous_address(socks->address) < 0) {
     /* normal request */
     conn->state = AP_CONN_STATE_CIRCUIT_WAIT;
-    conn->purpose = AP_PURPOSE_GENERAL;
-    /* attaching to a dirty circuit is fine */
     return connection_ap_handshake_attach_circuit(conn);
   } else {
     /* it's a hidden-service request */
     const char *descp;
     int desc_len;
 
-    strcpy(conn->rend_query, socks->address);
+    strcpy(conn->rend_query, socks->address); /* this strcpy is safe -RD */
     log_fn(LOG_INFO,"Got a hidden service request for ID '%s'", conn->rend_query);
     /* see if we already have it cached */
     if (rend_cache_lookup(conn->rend_query, &descp, &desc_len) == 1) {
-      conn->purpose = AP_PURPOSE_RENDPOINT_WAIT;
+      conn->state = AP_CONN_STATE_CIRCUIT_WAIT;
       return connection_ap_handshake_attach_circuit(conn);
-      //circuit_launch_new(CIRCUIT_PURPOSE_C_ESTABLISH_REND, NULL);
     } else {
-      conn->purpose = AP_PURPOSE_RENDDESC_WAIT;
-      /* initiate a dir hidserv desc lookup */
-      directory_initiate_command(router_pick_directory_server(),
-                                 DIR_PURPOSE_FETCH_RENDDESC,
-                                 conn->rend_query, strlen(conn->rend_query));
+      conn->state = AP_CONN_STATE_RENDDESC_WAIT;
+      if(!connection_get_by_type_rendquery(CONN_TYPE_DIR, conn->rend_query)) {
+        /* not one already; initiate a dir rend desc lookup */
+        directory_initiate_command(router_pick_directory_server(),
+                                   DIR_PURPOSE_FETCH_RENDDESC,
+                                   conn->rend_query, strlen(conn->rend_query));
+      }
       return 0;
     }
   }
   return 0;
 }
 
-/* find an open circ that we're happy with: return 1. if there isn't
- * one, launch one and return 0. if it will never work, return -1.
- * write the found or launched circ into *circp.
+/* Find an open circ that we're happy with: return 1. if there isn't
+ * one, and there isn't one on the way, launch one and return 0. if it
+ * will never work, return -1.
+ * write the found or in-progress or launched circ into *circp.
  */
 static int
-get_open_circ_or_launch(connection_t *conn,
-                        uint8_t desired_circuit_purpose,
-                        circuit_t **circp) {
+circuit_get_open_circ_or_launch(connection_t *conn,
+                                uint8_t desired_circuit_purpose,
+                                circuit_t **circp) {
   circuit_t *circ;
   uint32_t addr;
 
   assert(conn);
   assert(circp);
+  assert(conn->state == AP_CONN_STATE_CIRCUIT_WAIT);
 
-  circ = circuit_get_newest(conn, 1, desired_circuit_purpose);
+  circ = circuit_get_best(conn, 1, desired_circuit_purpose);
 
   if(circ) {
     *circp = circ;
@@ -760,7 +761,7 @@
   log_fn(LOG_INFO,"No safe circuit (purpose %d) ready for edge connection; delaying.",
          desired_circuit_purpose);
 
-  if(conn->purpose == AP_PURPOSE_GENERAL) {
+  if(!*conn->rend_query) { /* general purpose circ */
     addr = client_dns_lookup_entry(conn->socks_request->address);
     if(router_exit_policy_all_routers_reject(addr, conn->socks_request->port)) {
       log_fn(LOG_WARN,"No Tor server exists that allows exit to %s:%d. Rejecting.",
@@ -768,13 +769,33 @@
       return -1;
     }
   }
-  if(!circuit_get_newest(conn, 0, desired_circuit_purpose)) {
-    /* is one already on the way? */
-    circ = circuit_launch_new(desired_circuit_purpose, NULL);
-    /* depending on purpose, store stuff into circ */
+
+  /* is one already on the way? */
+  circ = circuit_get_best(conn, 0, desired_circuit_purpose);
+  if(!circ) {
+    char *exitname=NULL;
+    uint8_t new_circ_purpose;
+
+    if(desired_circuit_purpose == CIRCUIT_PURPOSE_C_INTRODUCING) {
+      /* need to pick an intro point */
+      exitname = rend_get_random_intro(conn->rend_query);
+      if(!exitname) {
+        log_fn(LOG_WARN,"Couldn't get an intro point for '%s'. Closing conn.",
+               conn->rend_query);
+        return -1;
+      }
+      log_fn(LOG_INFO,"Chose %s as intro point for %s.", exitname, conn->rend_query);
+    }
+
+    if(desired_circuit_purpose == CIRCUIT_PURPOSE_C_REND_JOINED)
+      new_circ_purpose = CIRCUIT_PURPOSE_C_ESTABLISH_REND;
+    else
+      new_circ_purpose = desired_circuit_purpose;
+
+    circ = circuit_launch_new(new_circ_purpose, exitname);
+
     if(circ &&
-       (desired_circuit_purpose == CIRCUIT_PURPOSE_C_GENERAL ||
-        desired_circuit_purpose == CIRCUIT_PURPOSE_C_ESTABLISH_REND)) {
+       (desired_circuit_purpose != CIRCUIT_PURPOSE_C_GENERAL)) {
       /* then write the service_id into circ */
       strcpy(circ->rend_query, conn->rend_query);
     }
@@ -791,8 +812,6 @@
  * right next step, and return 1.
  */
 int connection_ap_handshake_attach_circuit(connection_t *conn) {
-  circuit_t *circ=NULL;
-  uint8_t desired_circuit_purpose;
   int retval;
 
   assert(conn);
@@ -800,60 +819,74 @@
   assert(conn->state == AP_CONN_STATE_CIRCUIT_WAIT);
   assert(conn->socks_request);
 
-  if(conn->purpose == AP_PURPOSE_RENDDESC_WAIT)
-    return 0; /* these guys don't attach to circuits directly */
+  if(!*conn->rend_query) { /* we're a general conn */
+    circuit_t *circ=NULL;
 
-  switch(conn->purpose) {
-    case AP_PURPOSE_GENERAL:
-      desired_circuit_purpose = CIRCUIT_PURPOSE_C_GENERAL;
-      break;
-    case AP_PURPOSE_RENDPOINT_WAIT:
-      desired_circuit_purpose = CIRCUIT_PURPOSE_C_ESTABLISH_REND;
-      break;
-    case AP_PURPOSE_INTROPOINT_WAIT:
-      desired_circuit_purpose = CIRCUIT_PURPOSE_C_INTRODUCING;
-      break;
-    default:
-      log_fn(LOG_ERR, "Got unexpected purpose: %d", conn->purpose);
-      assert(0); /* never reached */
-  }
+    /* find the circuit that we should use, if there is one. */
+    retval = circuit_get_open_circ_or_launch(conn, CIRCUIT_PURPOSE_C_GENERAL, &circ);
+    if(retval < 1)
+      return retval;
 
-  /* find the circuit that we should use, if there is one. */
-  retval = get_open_circ_or_launch(conn, desired_circuit_purpose, &circ);
-  if(retval < 1)
-    return retval;
+    /* We have found a suitable circuit for our conn. Hurray. */
 
-  /* We have found a suitable circuit for our conn. Hurray. */
+    /* here, print the circ's path. so people can figure out which circs are sucking. */
+    circuit_log_path(LOG_INFO,circ);
 
-  /* here, print the circ's path. so people can figure out which circs are sucking. */
-  circuit_log_path(LOG_INFO,circ);
+    if(!circ->timestamp_dirty)
+      circ->timestamp_dirty = time(NULL);
 
-  if(!circ->timestamp_dirty)
-    circ->timestamp_dirty = time(NULL);
+    /* add it into the linked list of streams on this circuit */
+    log_fn(LOG_DEBUG,"attaching new conn to circ. n_circ_id %d.", circ->n_circ_id);
+    conn->next_stream = circ->p_streams;
+    /* assert_connection_ok(conn, time(NULL)); */
+    circ->p_streams = conn;
 
-  switch(conn->purpose) {
-    case AP_PURPOSE_GENERAL:
-      /* add it into the linked list of streams on this circuit */
-      log_fn(LOG_DEBUG,"attaching new conn to circ. n_circ_id %d.", circ->n_circ_id);
-      conn->next_stream = circ->p_streams;
-      /* assert_connection_ok(conn, time(NULL)); */
-      circ->p_streams = conn;
+    assert(circ->cpath && circ->cpath->prev);
+    assert(circ->cpath->prev->state == CPATH_STATE_OPEN);
+    conn->cpath_layer = circ->cpath->prev;
 
-      assert(circ->cpath && circ->cpath->prev);
-      assert(circ->cpath->prev->state == CPATH_STATE_OPEN);
-      conn->cpath_layer = circ->cpath->prev;
+    connection_ap_handshake_send_begin(conn, circ);
 
-      connection_ap_handshake_send_begin(conn, circ);
-      break;
-    case AP_PURPOSE_RENDPOINT_WAIT:
-      rend_client_rendcirc_is_ready(conn, circ);
-      break;
-    case AP_PURPOSE_INTROPOINT_WAIT:
-      rend_client_introcirc_is_ready(conn, circ);
-      break;
-  }
+    return 1;
 
-  return 1;
+  } else { /* we're a rendezvous conn */
+    circuit_t *rendcirc=NULL, *introcirc=NULL;
+
+    /* first, find a rendezvous circuit for us */
+
+    retval = circuit_get_open_circ_or_launch(conn, CIRCUIT_PURPOSE_C_REND_JOINED, &rendcirc);
+    if(retval < 0) return -1; /* failed */
+
+    if(retval > 0) {
+      /* one is already established, attach */
+
+      log_fn(LOG_WARN,"XXX rend joined circ already here. should reuse.");
+      return -1;
+    }
+
+    if(rendcirc &&
+       rendcirc->purpose == CIRCUIT_PURPOSE_C_REND_READY &&
+       rendcirc->build_state->pending_final_cpath) {
+      log_fn(LOG_WARN,"XXX pending-join circ already here. should reuse.");
+      return -1;
+    }
+
+    /* it's on its way. find an intro circ. */
+    retval = circuit_get_open_circ_or_launch(conn, CIRCUIT_PURPOSE_C_INTRODUCING, &introcirc);
+    if(retval < 0) return -1; /* failed */
+
+    if(retval > 0) {
+      log_fn(LOG_INFO,"Intro circ is ready for us");
+      if(rendcirc &&
+         rendcirc->purpose == CIRCUIT_PURPOSE_C_REND_READY) {
+        /* then we know !pending_final_cpath, from above */
+        log_fn(LOG_WARN,"XXX intro and rend are both ready. do the magic.");
+        return -1;
+      }
+    }
+    log_fn(LOG_INFO,"Intro and rend circs are not both ready. Stalling conn.");
+    return 0;
+  }
 }
 
 /* Iterate over the two bytes of stream_id until we get one that is not
@@ -961,7 +994,6 @@
   }
 
   conn->state = AP_CONN_STATE_CIRCUIT_WAIT;
-  conn->purpose = AP_PURPOSE_GENERAL;
   connection_start_reading(conn);
 
   /* attaching to a dirty circuit is fine */

Index: dirserv.c
===================================================================
RCS file: /home/or/cvsroot/src/or/dirserv.c,v
retrieving revision 1.34
retrieving revision 1.35
diff -u -d -r1.34 -r1.35
--- dirserv.c	3 Apr 2004 04:05:11 -0000	1.34
+++ dirserv.c	5 Apr 2004 00:47:47 -0000	1.35
@@ -206,7 +206,7 @@
 
   start = strstr(*desc, "router ");
   if (!start) {
-    log(LOG_WARN, "no descriptor found.");
+    log_fn(LOG_WARN, "no 'router' line found. This is not a descriptor.");
     return -1;
   }
   if ((end = strstr(start+6, "\nrouter "))) {

Index: main.c
===================================================================
RCS file: /home/or/cvsroot/src/or/main.c,v
retrieving revision 1.223
retrieving revision 1.224
diff -u -d -r1.223 -r1.224
--- main.c	3 Apr 2004 02:14:20 -0000	1.223
+++ main.c	5 Apr 2004 00:47:47 -0000	1.224
@@ -384,7 +384,7 @@
 /* Build a new test circuit every 5 minutes */
 #define TESTING_CIRCUIT_INTERVAL 300
 
-    circ = circuit_get_newest(NULL, 1, CIRCUIT_PURPOSE_C_GENERAL);
+    circ = circuit_get_best(NULL, 1, CIRCUIT_PURPOSE_C_GENERAL);
     if(time_to_new_circuit < now) {
       client_dns_clean();
       circuit_expire_unused_circuits();

Index: or.h
===================================================================
RCS file: /home/or/cvsroot/src/or/or.h,v
retrieving revision 1.295
retrieving revision 1.296
diff -u -d -r1.295 -r1.296
--- or.h	3 Apr 2004 04:55:22 -0000	1.295
+++ or.h	5 Apr 2004 00:47:47 -0000	1.296
@@ -100,7 +100,7 @@
                               can be lowered by config file */
 
 #define DEFAULT_BANDWIDTH_OP (1024 * 1000)
-#define MAX_NICKNAME_LEN 32
+#define MAX_NICKNAME_LEN 19
 #define MAX_DIR_SIZE 500000
 
 #ifdef TOR_PERF
@@ -159,18 +159,11 @@
 /* the AP state values must be disjoint from the EXIT state values */
 #define _AP_CONN_STATE_MIN 5
 #define AP_CONN_STATE_SOCKS_WAIT 5
-#define AP_CONN_STATE_CIRCUIT_WAIT 6
-#define AP_CONN_STATE_CONNECT_WAIT 7
-#define AP_CONN_STATE_OPEN 8
-#define _AP_CONN_STATE_MAX 8
-
-/* only used if state==CIRCUIT_WAIT */
-#define _AP_PURPOSE_MIN 1
-#define AP_PURPOSE_GENERAL 1
-#define AP_PURPOSE_RENDDESC_WAIT 2
-#define AP_PURPOSE_RENDPOINT_WAIT 3
-#define AP_PURPOSE_INTROPOINT_WAIT 4
-#define _AP_PURPOSE_MAX 4
+#define AP_CONN_STATE_RENDDESC_WAIT 6
+#define AP_CONN_STATE_CIRCUIT_WAIT 7
+#define AP_CONN_STATE_CONNECT_WAIT 8
+#define AP_CONN_STATE_OPEN 9
+#define _AP_CONN_STATE_MAX 9
 
 #define _DIR_CONN_STATE_MIN 1
 #define DIR_CONN_STATE_CONNECTING 1
@@ -194,23 +187,42 @@
 #define CIRCUIT_STATE_OPEN 3 /* onionskin(s) processed, ready to send/receive cells */
 
 #define _CIRCUIT_PURPOSE_MIN 1
+
 /* these circuits were initiated elsewhere */
 #define CIRCUIT_PURPOSE_OR 1 /* normal circuit, at OR. */
 #define CIRCUIT_PURPOSE_INTRO_POINT 2 /* At OR, from Bob, waiting for intro from Alices */
 #define CIRCUIT_PURPOSE_REND_POINT_WAITING 3 /* At OR, from Alice, waiting for Bob */
 #define CIRCUIT_PURPOSE_REND_ESTABLISHED 4 /* At OR, both circuits have this purpose */
+
 /* these circuits originate at this node */
+
+/* here's how circ client-side purposes work:
+ *   normal circuits are C_GENERAL.
+ *   circuits that are c_introducing are either on their way to
+ *     becoming open, or they are open but haven't been used yet.
+ *     (as soon as they are used, they are destroyed.)
+ *   circuits that are c_establish_rend are either on their way
+ *     to becoming open, or they are open and have sent the
+ *     establish_rendezvous cell but haven't received an ack.
+ *   circuits that are c_rend_ready are open and have received an
+ *     ack, but haven't heard from bob yet. if they have a
+ *     buildstate->pending_final_cpath then they're expecting a
+ *     cell from bob, else they're not.
+ *   circuits that are c_rend_joined are open, have heard from
+ *     bob, and are talking to him.
+ */
 #define CIRCUIT_PURPOSE_C_GENERAL 5 /* normal circuit, with cpath */
 #define CIRCUIT_PURPOSE_C_INTRODUCING 6 /* at Alice, connecting to intro point */
-#define CIRCUIT_PURPOSE_C_ESTABLISH_REND 7 /* at Alice, waiting for Bob */
-#define CIRCUIT_PURPOSE_C_REND_JOINED 8 /* at Alice, rendezvous established.*/
+#define CIRCUIT_PURPOSE_C_ESTABLISH_REND 7 /* at Alice, waiting for ack */
+#define CIRCUIT_PURPOSE_C_REND_READY 8 /* at Alice, waiting for Bob */
+#define CIRCUIT_PURPOSE_C_REND_JOINED 9 /* at Alice, rendezvous established */
 
-#define CIRCUIT_PURPOSE_S_ESTABLISH_INTRO 9 /* at Bob, waiting for introductions */
-#define CIRCUIT_PURPOSE_S_INTRO 10 /* at Bob, successfully established intro */
-#define CIRCUIT_PURPOSE_S_CONNECT_REND 11 /* at Bob, connecting to rend point */
+#define CIRCUIT_PURPOSE_S_ESTABLISH_INTRO 10 /* at Bob, waiting for introductions */
+#define CIRCUIT_PURPOSE_S_INTRO 11 /* at Bob, successfully established intro */
+#define CIRCUIT_PURPOSE_S_CONNECT_REND 12 /* at Bob, connecting to rend point */
 
-#define CIRCUIT_PURPOSE_S_REND_JOINED 12 /* at Bob, rendezvous established.*/
-#define _CIRCUIT_PURPOSE_MAX 11
+#define CIRCUIT_PURPOSE_S_REND_JOINED 13 /* at Bob, rendezvous established.*/
+#define _CIRCUIT_PURPOSE_MAX 13
 
 #define RELAY_COMMAND_BEGIN 1
 #define RELAY_COMMAND_DATA 2
@@ -347,7 +359,7 @@
 
   uint8_t type;
   uint8_t state;
-  uint8_t purpose; /* only used for DIR and AP types currently */
+  uint8_t purpose; /* only used for DIR types currently */
   uint8_t wants_to_read; /* should we start reading again once
                           * the bandwidth throttler allows it?
                           */
@@ -687,8 +699,8 @@
 
 circuit_t *circuit_get_by_circ_id_conn(uint16_t circ_id, connection_t *conn);
 circuit_t *circuit_get_by_conn(connection_t *conn);
-circuit_t *circuit_get_newest(connection_t *conn,
-                              int must_be_open, uint8_t conn_purpose);
+circuit_t *circuit_get_best(connection_t *conn,
+                            int must_be_open, uint8_t purpose);
 circuit_t *circuit_get_next_by_pk_and_purpose(circuit_t *circuit,
                                               const char *servid, uint8_t purpose);
 circuit_t *circuit_get_rendezvous(const char *cookie);
@@ -803,6 +815,7 @@
 connection_t *connection_get_by_type(int type);
 connection_t *connection_get_by_type_state(int type, int state);
 connection_t *connection_get_by_type_state_lastwritten(int type, int state);
+connection_t *connection_get_by_type_rendquery(int type, char *rendquery);
 
 #define connection_speaks_cells(conn) ((conn)->type == CONN_TYPE_OR)
 #define connection_has_pending_tls_data(conn) \
@@ -1025,12 +1038,14 @@
 
 /********************************* rendclient.c ***************************/
 
-void rend_client_introcirc_is_ready(connection_t *apconn, circuit_t *circ);
-void rend_client_rendcirc_is_ready(connection_t *apconn, circuit_t *circ);
+void rend_client_introcirc_is_open(circuit_t *circ);
+void rend_client_rendcirc_is_open(circuit_t *circ);
+int rend_client_rendezvous_acked(circuit_t *circ, const char *request, int request_len);
 void rend_client_rendezvous(connection_t *apconn, circuit_t *circ);
 void rend_client_desc_fetched(char *query, int success);
 
 int rend_cmp_service_ids(char *one, char *two);
+char *rend_get_random_intro(char *query);
 int rend_parse_rendezvous_address(char *address);
 
 int rend_client_send_establish_rendezvous(circuit_t *circ);

Index: rendclient.c
===================================================================
RCS file: /home/or/cvsroot/src/or/rendclient.c,v
retrieving revision 1.7
retrieving revision 1.8
diff -u -d -r1.7 -r1.8
--- rendclient.c	3 Apr 2004 04:22:22 -0000	1.7
+++ rendclient.c	5 Apr 2004 00:47:48 -0000	1.8
@@ -6,10 +6,13 @@
 
 /* send the introduce cell */
 void
-rend_client_introcirc_is_ready(connection_t *apconn, circuit_t *circ)
+rend_client_introcirc_is_open(circuit_t *circ)
 {
+  assert(circ->purpose == CIRCUIT_PURPOSE_C_INTRODUCING);
+  assert(circ->cpath);
 
-  log_fn(LOG_WARN,"introcirc is ready");
+  log_fn(LOG_INFO,"introcirc is open");
+  connection_ap_attach_pending();
 }
 
 int
@@ -35,24 +38,33 @@
 
 /* send the rendezvous cell */
 void
-rend_client_rendcirc_is_ready(connection_t *apconn, circuit_t *circ)
+rend_client_rendcirc_is_open(circuit_t *circ)
 {
-  circuit_t *introcirc;
-
-  assert(apconn->purpose == AP_PURPOSE_RENDPOINT_WAIT);
   assert(circ->purpose == CIRCUIT_PURPOSE_C_ESTABLISH_REND);
   assert(circ->cpath);
 
-  log_fn(LOG_INFO,"rendcirc is ready");
+  log_fn(LOG_INFO,"rendcirc is open");
 
-  /* XXX generate a rendezvous cookie, store it in circ */
-  /* store rendcirc in apconn */
+  /* generate a rendezvous cookie, store it in circ */
+  if (rend_client_send_establish_rendezvous(circ) < 0) {
+    circuit_mark_for_close(circ);
+    return;
+  }
 
-  apconn->purpose = AP_PURPOSE_INTROPOINT_WAIT;
-  if (connection_ap_handshake_attach_circuit(apconn) < 0) {
-    log_fn(LOG_WARN,"failed to start intro point. Closing conn.");
-    connection_mark_for_close(apconn,0);
+  connection_ap_attach_pending();
+}
+
+int
+rend_client_rendezvous_acked(circuit_t *circ, const char *request, int request_len)
+{
+  /* we just got an ack for our establish-rendezvous. switch purposes. */
+  if(circ->purpose != CIRCUIT_PURPOSE_C_ESTABLISH_REND) {
+    log_fn(LOG_WARN,"Got a rendezvous ack when we weren't expecting one. Closing circ.");
+    circuit_mark_for_close(circ);
+    return -1;
   }
+  circ->purpose = CIRCUIT_PURPOSE_C_REND_READY;
+  return 0;
 }
 
 /* bob sent us a rendezvous cell, join the circs. */
@@ -80,16 +92,14 @@
   for (i = 0; i < n; ++i) {
     conn = carray[i];
     if (conn->type != CONN_TYPE_AP ||
-        conn->state != AP_CONN_STATE_CIRCUIT_WAIT)
-      continue;
-    if (conn->purpose != AP_PURPOSE_RENDDESC_WAIT)
+        conn->state != AP_CONN_STATE_RENDDESC_WAIT)
       continue;
     if (rend_cmp_service_ids(conn->rend_query, query))
       continue;
     /* great, this guy was waiting */
     if(success) {
-      log_fn(LOG_INFO,"Rend desc retrieved. Launching rend circ.");
-      conn->purpose = AP_PURPOSE_RENDPOINT_WAIT;
+      log_fn(LOG_INFO,"Rend desc retrieved. Launching circuits.");
+      conn->state = AP_CONN_STATE_CIRCUIT_WAIT;
       if (connection_ap_handshake_attach_circuit(conn) < 0) {
         /* it will never work */
         log_fn(LOG_WARN,"attaching to a rend circ failed. Closing conn.");
@@ -106,6 +116,40 @@
   return strcasecmp(one,two);
 }
 
+/* return a pointer to a nickname for a random introduction
+ * point of query. return NULL if error.
+ */
+char *rend_get_random_intro(char *query) {
+  const char *descp;
+  int desc_len;
+  int i;
+  smartlist_t *sl;
+  rend_service_descriptor_t *parsed;
+  char *choice;
+
+  if(rend_cache_lookup(query, &descp, &desc_len) < 1) {
+    log_fn(LOG_WARN,"query '%s' didn't have valid rend desc in cache. Failing.", query);
+    return NULL;
+  }
+
+  parsed = rend_parse_service_descriptor(descp,desc_len);
+  if (!parsed) {
+    log_fn(LOG_WARN,"Couldn't parse service descriptor");
+    return NULL;
+  }
+
+  sl = smartlist_create();
+
+  /* add the intro point nicknames */
+  for(i=0;i<parsed->n_intro_points;i++)
+    smartlist_add(sl,parsed->intro_points[i]);
+
+  choice = smartlist_choose(sl);
+  smartlist_free(sl);
+  rend_service_descriptor_free(parsed);
+  return choice;
+}
+
 /* 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.

Index: rendcommon.c
===================================================================
RCS file: /home/or/cvsroot/src/or/rendcommon.c,v
retrieving revision 1.16
retrieving revision 1.17
diff -u -d -r1.16 -r1.17
--- rendcommon.c	3 Apr 2004 04:55:22 -0000	1.16
+++ rendcommon.c	5 Apr 2004 00:47:48 -0000	1.17
@@ -61,6 +61,8 @@
   return 0;
 }
 
+/* malloc a service_descriptor_t and return it.
+ * return NULL if invalid descriptor or error */
 rend_service_descriptor_t *rend_parse_service_descriptor(
                            const char *str, int len)
 {
@@ -83,6 +85,10 @@
   cp += 4;
   if (end-cp < 2) goto truncated;
   result->n_intro_points = get_uint16(cp);
+  if(result->n_intro_points < 1) {
+    log_fn(LOG_WARN,"Service descriptor listed no intro points.");
+    goto error;
+  }
   result->intro_points = tor_malloc_zero(sizeof(char*)*result->n_intro_points);
   cp += 2;
   for (i=0;i<result->n_intro_points;++i) {
@@ -282,8 +288,7 @@
       r = rend_service_intro_established(circ,payload,length);
       break;
     case RELAY_COMMAND_RENDEZVOUS_ESTABLISHED:
-      /* r = rend_client_rendezvous_established(circ,payload,length); */
-      log_fn(LOG_NOTICE, "Ignoring a rendezvous_established cell");
+      r = rend_client_rendezvous_acked(circ,payload,length);
       break;
     default:
       assert(0);

Index: routerlist.c
===================================================================
RCS file: /home/or/cvsroot/src/or/routerlist.c,v
retrieving revision 1.50
retrieving revision 1.51
diff -u -d -r1.50 -r1.51
--- routerlist.c	3 Apr 2004 02:40:30 -0000	1.50
+++ routerlist.c	5 Apr 2004 00:47:48 -0000	1.51
@@ -193,7 +193,7 @@
 
 void add_nickname_list_to_smartlist(smartlist_t *sl, char *list) {
   char *start,*end;
-  char nick[MAX_NICKNAME_LEN];
+  char nick[MAX_NICKNAME_LEN+1];
   routerinfo_t *router;
 
   assert(sl);
@@ -918,7 +918,7 @@
     }
     router->address = tor_strdup(tok->args[1]);
     router->addr = 0;
-    
+
     if (tok->n_args == 6) {
       router->or_port = atoi(tok->args[2]);
       router->socks_port = atoi(tok->args[3]);