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

[or-cvs] Logic to implement rendezvous/introduction via unknown serv...



Update of /home/or/cvsroot/tor/src/or
In directory moria:/tmp/cvs-serv2828/src/or

Modified Files:
	circuitbuild.c circuitlist.c circuituse.c connection_edge.c 
	control.c directory.c onion.c or.h relay.c rendclient.c 
	rendcommon.c rendservice.c test.c 
Log Message:
Logic to implement rendezvous/introduction via unknown servers.

- Add a new extend_info_t datatype to hold information needed to
  extend a circuit (addr,port,keyid,onion_key).  Use it in cpath and
  build_state.  Make appropriate functions take or return it instead of
  routerinfo_t or keyid.

- #if 0 needless check in circuit_get_by_edge_conn; if nobody triggers this
  error in 0.1.0.10, nobody will trigger it.

- Implement new hidden service descriptor format, which contains "extend
  info" for introduction points, along with protocol version list.
  - Parse new format.
  - Generate new format
  - Cache old and new formats alongside each other.
  - Directories serve "old" format if asked in old way, "newest available"
    format if asked in new way.
  - Use new format to find introduction points if possible; otherwise fall
    back.  Keep nickname lists and extendinfo lists in sync.
  - Tests for new format.

- Implement new "v2" INTRODUCE cell format.
  - Accept new format
  - Use new format if we have a versioned service descriptor that says the
    server accepts the new format.

- Add documentation for functions and data types.


Index: circuitbuild.c
===================================================================
RCS file: /home/or/cvsroot/tor/src/or/circuitbuild.c,v
retrieving revision 1.120
retrieving revision 1.121
diff -u -d -r1.120 -r1.121
--- circuitbuild.c	15 Jun 2005 18:34:46 -0000	1.120
+++ circuitbuild.c	29 Jun 2005 21:46:55 -0000	1.121
@@ -21,13 +21,12 @@
 
 static int circuit_deliver_create_cell(circuit_t *circ,
                                        uint8_t cell_type, char *payload);
-static int onion_pick_cpath_exit(circuit_t *circ, routerinfo_t *exit);
+static int onion_pick_cpath_exit(circuit_t *circ, extend_info_t *exit);
 static crypt_path_t *onion_next_hop_in_cpath(crypt_path_t *cpath);
-static int onion_next_router_in_cpath(circuit_t *circ, routerinfo_t **router);
 static int onion_extend_cpath(uint8_t purpose, crypt_path_t **head_ptr,
                               cpath_build_state_t *state);
 static int count_acceptable_routers(smartlist_t *routers);
-static int onion_append_hop(crypt_path_t **head_ptr, routerinfo_t *choice);
+static int onion_append_hop(crypt_path_t **head_ptr, extend_info_t *choice);
 
 /** Iterate over values of circ_id, starting from conn-\>next_circ_id,
  * and with the high bit specified by circ_id_type (see
@@ -85,31 +84,25 @@
   elements = smartlist_create();
 
   if (verbose) {
+    const char *nickname = build_state_get_exit_nickname(circ->build_state);
     tor_snprintf(buf, sizeof(buf)-1, "%s%s circ (length %d, exit %s):",
                  circ->build_state->is_internal ? "internal" : "exit",
                  circ->build_state->need_uptime ? " (high-uptime)" : "",
                  circ->build_state->desired_path_len,
-                 circ->build_state->chosen_exit_name);
+                 nickname?nickname:"unnamed");
     smartlist_add(elements, tor_strdup(buf));
   }
 
   hop = circ->cpath;
   do {
     const char *elt;
-    routerinfo_t *r;
     if (!hop)
       break;
     if (!verbose && hop->state != CPATH_STATE_OPEN)
       break;
-    if ((r = router_get_by_digest(hop->identity_digest))) {
-      elt = r->nickname;
-    } else if (circ->purpose == CIRCUIT_PURPOSE_C_REND_JOINED) {
-      elt = "<rendezvous splice>";
-    } else {
-      buf[0]='$';
-      base16_encode(buf+1,sizeof(buf)-1,hop->identity_digest,DIGEST_LEN);
-      elt = buf;
-    }
+    if (!hop->extend_info)
+      break;
+    elt = hop->extend_info->nickname;
     if (verbose) {
       size_t len = strlen(elt)+2+strlen(states[hop->state])+1;
       char *v = tor_malloc(len);
@@ -166,7 +159,7 @@
     prev_digest = me->identity_digest;
   }
   do {
-    router = router_get_by_digest(hop->identity_digest);
+    router = router_get_by_digest(hop->extend_info->identity_digest);
     if (router) {
       if (prev_digest) {
         if (hop->state == CPATH_STATE_OPEN)
@@ -272,7 +265,7 @@
   return circ;
 }
 
-/** Build a new circuit for <b>purpose</b>. If <b>exit</b>
+/** Build a new circuit for <b>purpose</b>. If <b>info/b>
  * is defined, then use that as your exit router, else choose a suitable
  * exit node.
  *
@@ -280,14 +273,14 @@
  * it's not open already.
  */
 circuit_t *
-circuit_establish_circuit(uint8_t purpose, routerinfo_t *exit,
+circuit_establish_circuit(uint8_t purpose, extend_info_t *info,
                           int need_uptime, int need_capacity, int internal)
 {
   circuit_t *circ;
 
   circ = circuit_init(purpose, need_uptime, need_capacity, internal);
 
-  if (onion_pick_cpath_exit(circ, exit) < 0 ||
+  if (onion_pick_cpath_exit(circ, info) < 0 ||
       onion_populate_cpath(circ) < 0) {
     circuit_mark_for_close(circ);
     return NULL;
@@ -309,26 +302,32 @@
 int
 circuit_handle_first_hop(circuit_t *circ)
 {
-  routerinfo_t *firsthop;
+  crypt_path_t *firsthop;
   connection_t *n_conn;
+  char tmpbuf[INET_NTOA_BUF_LEN+1];
+  struct in_addr in;
 
-  onion_next_router_in_cpath(circ, &firsthop);
+  firsthop = onion_next_hop_in_cpath(circ->cpath);
   tor_assert(firsthop);
 
   /* now see if we're already connected to the first OR in 'route' */
-  log_fn(LOG_DEBUG,"Looking for firsthop '%s:%u'",
-      firsthop->address,firsthop->or_port);
+  in.s_addr = htonl(firsthop->extend_info->addr);
+  tor_inet_ntoa(&in, tmpbuf, sizeof(tmpbuf));
+  log_fn(LOG_DEBUG,"Looking for firsthop '%s:%u'",tmpbuf,
+         firsthop->extend_info->port);
   /* imprint the circuit with its future n_conn->id */
-  memcpy(circ->n_conn_id_digest, firsthop->identity_digest, DIGEST_LEN);
-  n_conn = connection_get_by_identity_digest(firsthop->identity_digest,
-                                             CONN_TYPE_OR);
+  memcpy(circ->n_conn_id_digest, firsthop->extend_info->identity_digest,
+         DIGEST_LEN);
+  n_conn = connection_get_by_identity_digest(
+         firsthop->extend_info->identity_digest, CONN_TYPE_OR);
   if (!n_conn || n_conn->state != OR_CONN_STATE_OPEN) { /* not currently connected */
-    circ->n_addr = firsthop->addr;
-    circ->n_port = firsthop->or_port;
+    circ->n_addr = firsthop->extend_info->addr;
+    circ->n_port = firsthop->extend_info->port;
 
     if (!n_conn) { /* launch the connection */
-      n_conn = connection_or_connect(firsthop->addr, firsthop->or_port,
-                                     firsthop->identity_digest);
+      n_conn = connection_or_connect(firsthop->extend_info->addr,
+                                     firsthop->extend_info->port,
+                                     firsthop->extend_info->identity_digest);
       if (!n_conn) { /* connect failed, forget the whole thing */
         log_fn(LOG_INFO,"connect to firsthop failed. Closing.");
         return -1;
@@ -452,7 +451,6 @@
 {
   crypt_path_t *hop;
   routerinfo_t *router;
-  int r;
   char payload[2+4+DIGEST_LEN+ONIONSKIN_CHALLENGE_LEN];
   char *onionskin;
   size_t payload_len;
@@ -465,20 +463,15 @@
     log_fn(LOG_DEBUG,"First skin; sending create cell.");
 
     router = router_get_by_digest(circ->n_conn->identity_digest);
-    if (!router) {
-      log_fn(LOG_WARN,"Couldn't find routerinfo for %s",
-             circ->n_conn->nickname);
-      return -1;
-    }
 
     if (1 || /* Disable this '1' once we believe CREATE_FAST works. XXXX */
-        (get_options()->ORPort || !router->platform ||
+        (get_options()->ORPort || !router || !router->platform ||
          !tor_version_as_new_as(router->platform, "0.1.0.6-rc"))) {
       /* We are an OR, or we are connecting to an old Tor: we should
        * send an old slow create cell.
        */
       cell_type = CELL_CREATE;
-      if (onion_skin_create(router->onion_pkey,
+      if (onion_skin_create(circ->cpath->extend_info->onion_key,
                             &(circ->cpath->dh_handshake_state),
                             payload) < 0) {
         log_fn(LOG_WARN,"onion_skin_create (first hop) failed.");
@@ -505,8 +498,8 @@
     tor_assert(circ->cpath->state == CPATH_STATE_OPEN);
     tor_assert(circ->state == CIRCUIT_STATE_BUILDING);
     log_fn(LOG_DEBUG,"starting to send subsequent skin.");
-    r = onion_next_router_in_cpath(circ, &router);
-    if (r > 0) {
+    hop = onion_next_hop_in_cpath(circ->cpath);
+    if (!hop) {
       /* done building the circuit. whew. */
       circ->state = CIRCUIT_STATE_OPEN;
       log_fn(LOG_INFO,"circuit built!");
@@ -524,19 +517,17 @@
       circuit_rep_hist_note_result(circ);
       circuit_has_opened(circ); /* do other actions as necessary */
       return 0;
-    } else if (r < 0) {
-      return -1;
     }
-    hop = onion_next_hop_in_cpath(circ->cpath);
 
-    *(uint32_t*)payload = htonl(hop->addr);
-    *(uint16_t*)(payload+4) = htons(hop->port);
+    *(uint32_t*)payload = htonl(hop->extend_info->addr);
+    *(uint16_t*)(payload+4) = htons(hop->extend_info->port);
 
     onionskin = payload+2+4;
-    memcpy(payload+2+4+ONIONSKIN_CHALLENGE_LEN, hop->identity_digest, DIGEST_LEN);
+    memcpy(payload+2+4+ONIONSKIN_CHALLENGE_LEN, hop->extend_info->identity_digest, DIGEST_LEN);
     payload_len = 2+4+ONIONSKIN_CHALLENGE_LEN+DIGEST_LEN;
 
-    if (onion_skin_create(router->onion_pkey, &(hop->dh_handshake_state), onionskin) < 0) {
+    if (onion_skin_create(hop->extend_info->onion_key,
+                          &(hop->dh_handshake_state), onionskin) < 0) {
       log_fn(LOG_WARN,"onion_skin_create failed.");
       return -1;
     }
@@ -1218,10 +1209,9 @@
  * router (or use <b>exit</b> if provided). Store these in the
  * cpath. Return 0 if ok, -1 if circuit should be closed. */
 static int
-onion_pick_cpath_exit(circuit_t *circ, routerinfo_t *exit)
+onion_pick_cpath_exit(circuit_t *circ, extend_info_t *exit)
 {
   cpath_build_state_t *state = circ->build_state;
-
   routerlist_t *rl;
   int r;
 
@@ -1237,16 +1227,17 @@
 
   if (exit) { /* the circuit-builder pre-requested one */
     log_fn(LOG_INFO,"Using requested exit node '%s'", exit->nickname);
+    exit = extend_info_dup(exit);
   } else { /* we have to decide one */
-    exit = choose_good_exit_server(circ->purpose, rl,
+    routerinfo_t *router = choose_good_exit_server(circ->purpose, rl,
                                    state->need_uptime, state->need_capacity);
-    if (!exit) {
+    if (!router) {
       log_fn(LOG_WARN,"failed to choose an exit server");
       return -1;
     }
+    exit = extend_info_from_router(router);
   }
-  memcpy(state->chosen_exit_digest, exit->identity_digest, DIGEST_LEN);
-  state->chosen_exit_name = tor_strdup(exit->nickname);
+  state->chosen_exit = exit;
   return 0;
 }
 
@@ -1255,30 +1246,32 @@
  * the caller will do this if it wants to.
  */
 int
-circuit_append_new_exit(circuit_t *circ, routerinfo_t *exit)
+circuit_append_new_exit(circuit_t *circ, extend_info_t *info)
 {
-  tor_assert(exit);
+  cpath_build_state_t *state;
+  tor_assert(info);
   tor_assert(circ && CIRCUIT_IS_ORIGIN(circ));
-  tor_free(circ->build_state->chosen_exit_name);
-  circ->build_state->chosen_exit_name = tor_strdup(exit->nickname);
-  memcpy(circ->build_state->chosen_exit_digest, exit->identity_digest, DIGEST_LEN);
+
+  state = circ->build_state;
+  tor_assert(state);
+  if (state->chosen_exit)
+    extend_info_free(state->chosen_exit);
+  state->chosen_exit = extend_info_dup(info);
+
   ++circ->build_state->desired_path_len;
-  onion_append_hop(&circ->cpath, exit);
+  onion_append_hop(&circ->cpath, info);
   return 0;
 }
 
-/** Take the open circ originating here, give it a new exit destination
- * to <b>exit</b>, and get it to send the next extend cell. If you can't
- * send the extend cell, mark the circuit for close and return -1, else
- * return 0. */
+/** DOCDOC */
 int
-circuit_extend_to_new_exit(circuit_t *circ, routerinfo_t *exit)
+circuit_extend_to_new_exit(circuit_t *circ, extend_info_t *info)
 {
-  circuit_append_new_exit(circ, exit);
+  circuit_append_new_exit(circ, info);
   circ->state = CIRCUIT_STATE_BUILDING;
   if (circuit_send_next_onion_skin(circ)<0) {
     log_fn(LOG_WARN, "Couldn't extend circuit to new point '%s'.",
-           circ->build_state->chosen_exit_name);
+           info->nickname);
     circuit_mark_for_close(circ);
     return -1;
   }
@@ -1350,7 +1343,7 @@
 
   log_fn(LOG_DEBUG, "Contemplating intermediate hop: random choice.");
   excluded = smartlist_create();
-  if ((r = router_get_by_digest(state->chosen_exit_digest))) {
+  if ((r = build_state_get_exit_router(state))) {
     smartlist_add(excluded, r);
     routerlist_add_family(excluded, r);
   }
@@ -1359,7 +1352,7 @@
     routerlist_add_family(excluded, r);
   }
   for (i = 0, cpath = head; i < cur_len; ++i, cpath=cpath->next) {
-    if ((r = router_get_by_digest(cpath->identity_digest))) {
+    if ((r = router_get_by_digest(cpath->extend_info->identity_digest))) {
       smartlist_add(excluded, r);
       routerlist_add_family(excluded, r);
     }
@@ -1379,7 +1372,7 @@
   smartlist_t *excluded = smartlist_create();
   or_options_t *options = get_options();
 
-  if ((r = router_get_by_digest(state->chosen_exit_digest))) {
+  if ((r = build_state_get_exit_router(state))) {
     smartlist_add(excluded, r);
     routerlist_add_family(excluded, r);
   }
@@ -1424,27 +1417,6 @@
   return NULL;
 }
 
-/** Find the router corresponding to the first non-open hop in
- * circ->cpath. Make sure it's state closed. Return 1 if all
- * hops are open (the circuit is complete), 0 if we find a router
- * (and set it to *router), and -1 if we fail to lookup the router.
- */
-static int
-onion_next_router_in_cpath(circuit_t *circ, routerinfo_t **router) {
-  routerinfo_t *r;
-  crypt_path_t *hop = onion_next_hop_in_cpath(circ->cpath);
-  if (!hop) /* all hops are open */
-    return 1;
-  tor_assert(hop->state == CPATH_STATE_CLOSED);
-  r = router_get_by_digest(hop->identity_digest);
-  if (!r) {
-    log_fn(LOG_WARN,"Circuit intended to extend to a hop whose routerinfo we've lost. Cancelling circuit.");
-    return -1;
-  }
-  *router = r;
-  return 0;
-}
-
 /** Choose a suitable next hop in the cpath <b>head_ptr</b>,
  * based on <b>state</b>. Append the hop info to head_ptr.
  */
@@ -1454,7 +1426,7 @@
 {
   int cur_len;
   crypt_path_t *cpath;
-  routerinfo_t *choice;
+  extend_info_t *info = NULL;
   smartlist_t *excludednodes;
 
   tor_assert(head_ptr);
@@ -1481,23 +1453,29 @@
   add_nickname_list_to_smartlist(excludednodes,get_options()->ExcludeNodes,0);
 
   if (cur_len == state->desired_path_len - 1) { /* Picking last node */
-    choice = router_get_by_digest(state->chosen_exit_digest);
+    info = extend_info_dup(state->chosen_exit);
   } else if (cur_len == 0) { /* picking first node */
-    choice = choose_good_entry_server(state);
+    routerinfo_t *r = choose_good_entry_server(state);
+    if (r)
+      info = extend_info_from_router(r);
   } else {
-    choice = choose_good_middle_server(purpose, state, *head_ptr, cur_len);
+    routerinfo_t *r =
+      choose_good_middle_server(purpose, state, *head_ptr, cur_len);
+    if (r)
+      info = extend_info_from_router(r);
   }
 
   smartlist_free(excludednodes);
-  if (!choice) {
+  if (!info) {
     log_fn(LOG_WARN,"Failed to find node for hop %d of our path. Discarding this circuit.", cur_len);
     return -1;
   }
 
   log_fn(LOG_DEBUG,"Chose router %s for hop %d (exit is %s)",
-         choice->nickname, cur_len+1, state->chosen_exit_name);
+         info->nickname, cur_len+1, build_state_get_exit_nickname(state));
 
-  onion_append_hop(head_ptr, choice);
+  onion_append_hop(head_ptr, info);
+  extend_info_free(info);
   return 0;
 }
 
@@ -1505,7 +1483,7 @@
  * corresponding router <b>choice</b>, and append it to the
  * end of the cpath <b>head_ptr</b>. */
 static int
-onion_append_hop(crypt_path_t **head_ptr, routerinfo_t *choice)
+onion_append_hop(crypt_path_t **head_ptr, extend_info_t *choice)
 {
   crypt_path_t *hop = tor_malloc_zero(sizeof(crypt_path_t));
 
@@ -1515,9 +1493,7 @@
   hop->magic = CRYPT_PATH_MAGIC;
   hop->state = CPATH_STATE_CLOSED;
 
-  hop->port = choice->or_port;
-  hop->addr = choice->addr;
-  memcpy(hop->identity_digest, choice->identity_digest, DIGEST_LEN);
+  hop->extend_info = extend_info_dup(choice);
 
   hop->package_window = CIRCWINDOW_START;
   hop->deliver_window = CIRCWINDOW_START;
@@ -1525,3 +1501,67 @@
   return 0;
 }
 
+/** Allocate and return a new extend_info_t that can be used to build a
+ * circuit to or through the router <b>r</b>. */
+extend_info_t *
+extend_info_from_router(routerinfo_t *r)
+{
+  extend_info_t *info;
+  tor_assert(r);
+  info = tor_malloc_zero(sizeof(extend_info_t));
+  strlcpy(info->nickname, r->nickname, sizeof(info->nickname));
+  memcpy(info->identity_digest, r->identity_digest, DIGEST_LEN);
+  info->onion_key = crypto_pk_dup_key(r->onion_pkey);
+  info->addr = r->addr;
+  info->port = r->or_port;
+  return info;
+}
+
+/** Release storage held by an extend_info_t struct. */
+void
+extend_info_free(extend_info_t *info)
+{
+  tor_assert(info);
+  crypto_free_pk_env(info->onion_key);
+  tor_free(info);
+}
+
+/** Allocate and return a new extend_info_t with the same contents as
+ * <b>info</b>. */
+extend_info_t *
+extend_info_dup(extend_info_t *info)
+{
+  extend_info_t *newinfo;
+  tor_assert(info);
+  newinfo = tor_malloc(sizeof(extend_info_t));
+  memcpy(newinfo, info, sizeof(extend_info_t));
+  newinfo->onion_key = crypto_pk_dup_key(info->onion_key);
+  return newinfo;
+}
+
+/**
+ * Return the routerinfo_t for the chosen exit router in <b>state</b>.  If
+ * there is no chosen exit, or if we don't know the routerinfo_t for the
+ * chosen exit, return NULL.
+ */
+routerinfo_t *
+build_state_get_exit_router(cpath_build_state_t *state)
+{
+  if (!state || !state->chosen_exit)
+    return NULL;
+  return router_get_by_digest(state->chosen_exit->identity_digest);
+}
+
+/**
+ * Return the nickname for the chosen exit router in <b>state</b>.  If
+ * there is no chosen exit, or if we don't know the routerinfo_t for the
+ * chosen exit, return NULL.
+ */
+const char *
+build_state_get_exit_nickname(cpath_build_state_t *state)
+{
+  if (!state || !state->chosen_exit)
+    return NULL;
+  return state->chosen_exit->nickname;
+}
+

Index: circuitlist.c
===================================================================
RCS file: /home/or/cvsroot/tor/src/or/circuitlist.c,v
retrieving revision 1.51
retrieving revision 1.52
diff -u -d -r1.51 -r1.52
--- circuitlist.c	19 Jun 2005 20:40:41 -0000	1.51
+++ circuitlist.c	29 Jun 2005 21:46:55 -0000	1.52
@@ -24,7 +24,8 @@
 
 /********* END VARIABLES ************/
 
-/** DOCDOC This whole section */
+/** A map from OR connection and circuit ID to circuit.  (Lookup performance is
+ * very important here, since we need to do it every time a cell arrives.) */
 struct orconn_circid_circuit_map_t {
   RB_ENTRY(orconn_circid_circuit_map_t) node;
   connection_t *or_conn;
@@ -32,7 +33,9 @@
   circuit_t *circuit;
 };
 
-/** DOCDOC */
+/** helper for RB tree: compare the OR connection and circuit ID for a and b,
+ * and return less than, equal to, or greater than zero appropriately.
+ */
 static INLINE int
 compare_orconn_circid_entries(struct orconn_circid_circuit_map_t *a,
                               struct orconn_circid_circuit_map_t *b)
@@ -49,9 +52,15 @@
 RB_PROTOTYPE(orconn_circid_tree, orconn_circid_circuit_map_t, node, compare_orconn_circid_entries);
 RB_GENERATE(orconn_circid_tree, orconn_circid_circuit_map_t, node, compare_orconn_circid_entries);
 
+/** The most recently returned entyr from circuit_get_by_circid_orconn; used
+ * to improve performance when many cells arrive in a row from the same circuit.
+ */
+/* (We tried using splay trees, but round-robin turned out to make them suck.) */
 struct orconn_circid_circuit_map_t *_last_circid_orconn_ent = NULL;
 
-/** DOCDOC */
+/** Set the p_conn or n_conn field of a circuit <b>circ</b>, along with the
+ * corresponding circuit ID, and add the circuit as appropriate to the
+ * (orconn,id)-\>circuit map. */
 void
 circuit_set_circid_orconn(circuit_t *circ, uint16_t id,
                           connection_t *conn,
@@ -154,7 +163,7 @@
   }
 }
 
-/** DOCDOC **/
+/** Return the head of the global linked list of circuits. **/
 circuit_t *
 _circuit_get_global_list(void)
 {
@@ -227,7 +236,8 @@
   if (circ->p_digest)
     crypto_free_digest_env(circ->p_digest);
   if (circ->build_state) {
-    tor_free(circ->build_state->chosen_exit_name);
+    if (circ->build_state->chosen_exit)
+      extend_info_free(circ->build_state->chosen_exit);
     if (circ->build_state->pending_final_cpath)
       circuit_free_cpath_node(circ->build_state->pending_final_cpath);
   }
@@ -296,6 +306,9 @@
     crypto_free_digest_env(victim->b_digest);
   if (victim->dh_handshake_state)
     crypto_dh_free(victim->dh_handshake_state);
+  if (victim->extend_info)
+    extend_info_free(victim->extend_info);
+
   victim->magic = 0xDEADBEEFu;
   tor_free(victim);
 }
@@ -367,7 +380,7 @@
 
 }
 
-/** DOCDOC */
+/** Return the circuit that a given edge connection is using. */
 circuit_t *
 circuit_get_by_edge_conn(connection_t *conn)
 {
@@ -385,8 +398,9 @@
   }
 
   circ = conn->on_circuit;
-  /* All this stuff here is sanity-checking. */
   tor_assert(circ->magic == CIRCUIT_MAGIC);
+#if 0
+  /* All this stuff here is sanity-checking. */
   for (tmpconn = circ->p_streams; tmpconn; tmpconn=tmpconn->next_stream)
     if (tmpconn == conn)
       return circ;
@@ -398,6 +412,8 @@
       return circ;
 
   tor_assert(0);
+#endif
+  return circ;
 }
 
 /** Return a circ such that circ is attached to <b>conn</b>, either as
@@ -592,10 +608,14 @@
   }
   if (circ->purpose == CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT) {
     tor_assert(circ->state == CIRCUIT_STATE_OPEN);
+    tor_assert(circ->build_state->chosen_exit);
     /* treat this like getting a nack from it */
     log_fn(LOG_INFO,"Failed intro circ %s to %s (awaiting ack). Removing from descriptor.",
-           safe_str(circ->rend_query), safe_str(circ->build_state->chosen_exit_name));
-    rend_client_remove_intro_point(circ->build_state->chosen_exit_name, circ->rend_query);
+           safe_str(circ->rend_query),
+           safe_str(build_state_get_exit_nickname(circ->build_state)));
+    // XXXX NM
+    rend_client_remove_intro_point(circ->build_state->chosen_exit,
+                                   circ->rend_query);
   }
 
   if (circ->n_conn)

Index: circuituse.c
===================================================================
RCS file: /home/or/cvsroot/tor/src/or/circuituse.c,v
retrieving revision 1.75
retrieving revision 1.76
diff -u -d -r1.75 -r1.76
--- circuituse.c	11 Jun 2005 18:52:11 -0000	1.75
+++ circuituse.c	29 Jun 2005 21:46:55 -0000	1.76
@@ -74,12 +74,7 @@
    * circuit, it's the magical extra bob hop. so just check the nickname
    * of the one we meant to finish at.
    */
-  exitrouter = router_get_by_digest(circ->build_state->chosen_exit_digest);
-
-  if (!exitrouter) {
-    log_fn(LOG_INFO,"Skipping broken circ (exit router vanished)");
-    return 0; /* this circuit is screwed and doesn't know it yet */
-  }
+  exitrouter = build_state_get_exit_router(circ->build_state);
 
   if (!circ->build_state->need_uptime &&
       smartlist_string_num_isin(get_options()->LongLivedPorts,
@@ -87,6 +82,11 @@
     return 0;
 
   if (purpose == CIRCUIT_PURPOSE_C_GENERAL) {
+    if (!exitrouter) {
+      log_fn(LOG_DEBUG,"Not considering circuit with unknown router.");
+      return 0; /* this circuit is screwed and doesn't know it yet,
+                 * or is a rendezvous circuit. */
+    }
     if (!connection_ap_can_use_exit(conn, exitrouter)) {
       /* can't exit from this router */
       return 0;
@@ -296,7 +296,7 @@
         circ->purpose == CIRCUIT_PURPOSE_C_GENERAL &&
         (!circ->timestamp_dirty ||
          circ->timestamp_dirty + get_options()->MaxCircuitDirtiness < now)) {
-      exitrouter = router_get_by_digest(circ->build_state->chosen_exit_digest);
+      exitrouter = build_state_get_exit_router(circ->build_state);
       if (exitrouter &&
           (!need_uptime || circ->build_state->need_uptime)) {
         int ok;
@@ -682,7 +682,7 @@
       /* Don't increment failure count, since Alice may have picked
        * the rendezvous point maliciously */
       log_fn(LOG_INFO,"Couldn't connect to Alice's chosen rend point %s (%s hop failed).",
-             circ->build_state->chosen_exit_name,
+             build_state_get_exit_nickname(circ->build_state),
              failed_at_last_hop?"last":"non-last");
       rend_service_relaunch_rendezvous(circ);
       break;
@@ -703,12 +703,34 @@
  * success. */
 #define MAX_CIRCUIT_FAILURES 5
 
-/** Launch a new circuit based on our arguments. */
+/** Launch a new circuit; see circuit_launch_by_extend_info for details on
+ * arguments. */
 circuit_t *
 circuit_launch_by_router(uint8_t purpose, routerinfo_t *exit,
                          int need_uptime, int need_capacity, int internal)
 {
   circuit_t *circ;
+  extend_info_t *info = NULL;
+  if (exit)
+    info = extend_info_from_router(exit);
+  circ = circuit_launch_by_extend_info(purpose, info, need_uptime, need_capacity,
+                                       internal);
+  if (info)
+    extend_info_free(info);
+  return circ;
+}
+
+/** Launch a new circuit with purpose <b>purpose</b> and exit node <b>info</b>
+ * (or NULL to select a random exit node).  If <b>need_uptime</b> is true,
+ * choose among routers with high uptime.  If <b>need_capacity</b> is true,
+ * choose among routers with high bandwidth.  If <b>internal</b> is true, the
+ * last hop need not be an exit node. Return the newly allocated circuit on
+ * success, or NULL on failure. */
+circuit_t *
+circuit_launch_by_extend_info(uint8_t purpose, extend_info_t *info,
+               int need_uptime, int need_capacity, int internal)
+{
+  circuit_t *circ;
 
   if (!has_fetched_directory) {
     log_fn(LOG_DEBUG,"Haven't fetched directory yet; canceling circuit launch.");
@@ -721,7 +743,7 @@
     if ((circ = circuit_get_clean_open(CIRCUIT_PURPOSE_C_GENERAL, need_uptime,
                                        need_capacity, internal))) {
       log_fn(LOG_INFO,"Cannibalizing circ '%s' for purpose %d",
-             circ->build_state->chosen_exit_name, purpose);
+             build_state_get_exit_nickname(circ->build_state), purpose);
       circ->purpose = purpose;
       /* reset the birth date of this circ, else expire_building
        * will see it and think it's been trying to build since it
@@ -740,8 +762,8 @@
         case CIRCUIT_PURPOSE_C_INTRODUCING:
         case CIRCUIT_PURPOSE_S_CONNECT_REND:
           /* need to add a new hop */
-          tor_assert(exit);
-          if (circuit_extend_to_new_exit(circ, exit) < 0)
+          tor_assert(info);
+          if (circuit_extend_to_new_exit(circ, info) < 0)
             return NULL;
           break;
         default:
@@ -762,11 +784,12 @@
   }
 
   /* try a circ. if it fails, circuit_mark_for_close will increment n_circuit_failures */
-  return circuit_establish_circuit(purpose, exit,
+  return circuit_establish_circuit(purpose, info,
                                    need_uptime, need_capacity, internal);
 }
 
-/** Launch a new circuit and return a pointer to it. Return NULL if you failed. */
+/** Launch a new circuit; see circuit_launch_by_extend_info for details on
+ * arguments. */
 circuit_t *
 circuit_launch_by_nickname(uint8_t purpose, const char *exit_nickname,
                            int need_uptime, int need_capacity, int internal)
@@ -867,30 +890,22 @@
   /* is one already on the way? */
   circ = circuit_get_best(conn, 0, desired_circuit_purpose);
   if (!circ) {
-    char *exitname=NULL;
+    extend_info_t *extend_info=NULL;
     uint8_t new_circ_purpose;
     int is_internal;
 
     if (desired_circuit_purpose == CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT) {
       /* need to pick an intro point */
-try_an_intro_point:
-      exitname = rend_client_get_random_intro(conn->rend_query);
-      if (!exitname) {
+      extend_info = rend_client_get_random_intro(conn->rend_query);
+      if (!extend_info) {
         log_fn(LOG_INFO,"No intro points for '%s': refetching service descriptor.",
                safe_str(conn->rend_query));
         rend_client_refetch_renddesc(conn->rend_query);
         conn->state = AP_CONN_STATE_RENDDESC_WAIT;
         return 0;
       }
-      if (!router_get_by_nickname(exitname)) {
-        log_fn(LOG_NOTICE,"Advertised intro point '%s' is not recognized for hidserv address '%s'. Skipping over.",
-               exitname, safe_str(conn->rend_query));
-        rend_client_remove_intro_point(exitname, conn->rend_query);
-        tor_free(exitname);
-        goto try_an_intro_point;
-      }
       log_fn(LOG_INFO,"Chose %s as intro point for %s.",
-             exitname, safe_str(conn->rend_query));
+             extend_info->nickname, safe_str(conn->rend_query));
     }
 
     /* If we have specified a particular exit node for our
@@ -898,13 +913,13 @@
      */
     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)) {
+        routerinfo_t *r;
+        if (!(r = router_get_by_nickname(conn->chosen_exit_name))) {
           log_fn(LOG_NOTICE,"Requested exit point '%s' is not known. Closing.",
-                 exitname);
-          tor_free(exitname);
+                 conn->chosen_exit_name);
           return -1;
         }
+        extend_info = extend_info_from_router(r);
       }
     }
 
@@ -916,9 +931,10 @@
       new_circ_purpose = desired_circuit_purpose;
 
     is_internal = (new_circ_purpose != CIRCUIT_PURPOSE_C_GENERAL || is_resolve);
-    circ = circuit_launch_by_nickname(new_circ_purpose, exitname, need_uptime,
-                                      1, is_internal);
-    tor_free(exitname);
+    circ = circuit_launch_by_extend_info(
+              new_circ_purpose, extend_info, need_uptime, 1, is_internal);
+    if (extend_info)
+      extend_info_free(extend_info);
 
     if (desired_circuit_purpose != CIRCUIT_PURPOSE_C_GENERAL) {
       /* help predict this next time */
@@ -989,18 +1005,18 @@
     }
   });
 
-  if (!found_needle)
+  if (!found_needle || !circ->build_state->chosen_exit)
     return;
 
   /* Add this exit/hostname pair to the addressmap. */
   len = strlen(conn->socks_request->address) + 1 /* '.' */ +
-        strlen(circ->build_state->chosen_exit_name) + 1 /* '.' */ +
+        strlen(circ->build_state->chosen_exit->nickname) + 1 /* '.' */ +
         strlen("exit") + 1 /* '\0' */;
   new_address = tor_malloc(len);
 
   tor_snprintf(new_address, len, "%s.%s.exit",
                conn->socks_request->address,
-               circ->build_state->chosen_exit_name);
+               circ->build_state->chosen_exit->nickname);
 
   addressmap_register(conn->socks_request->address, new_address,
                       time(NULL) + options->TrackHostExitsExpire);

Index: connection_edge.c
===================================================================
RCS file: /home/or/cvsroot/tor/src/or/connection_edge.c,v
retrieving revision 1.333
retrieving revision 1.334
diff -u -d -r1.333 -r1.334
--- connection_edge.c	19 Jun 2005 20:40:41 -0000	1.333
+++ connection_edge.c	29 Jun 2005 21:46:55 -0000	1.334
@@ -1051,7 +1051,7 @@
     log_fn(LOG_INFO,"Got a hidden service request for ID '%s'",
            safe_str(conn->rend_query));
     /* see if we already have it cached */
-    r = rend_cache_lookup_entry(conn->rend_query, &entry);
+    r = rend_cache_lookup_entry(conn->rend_query, -1, &entry);
     if (r<0) {
       log_fn(LOG_WARN,"Invalid service descriptor %s",
              safe_str(conn->rend_query));

Index: control.c
===================================================================
RCS file: /home/or/cvsroot/tor/src/or/control.c,v
retrieving revision 1.97
retrieving revision 1.98
diff -u -d -r1.97 -r1.98
--- control.c	27 Jun 2005 23:35:04 -0000	1.97
+++ control.c	29 Jun 2005 21:46:55 -0000	1.98
@@ -1402,7 +1402,9 @@
   /* now circ refers to something that is ready to be extended */
   SMARTLIST_FOREACH(routers, routerinfo_t *, r,
   {
-    circuit_append_new_exit(circ, r);
+    extend_info_t *info = extend_info_from_router(r);
+    circuit_append_new_exit(circ, info);
+    extend_info_free(info);
   });
 
   /* now that we've populated the cpath, start extending */

Index: directory.c
===================================================================
RCS file: /home/or/cvsroot/tor/src/or/directory.c,v
retrieving revision 1.236
retrieving revision 1.237
diff -u -d -r1.236 -r1.237
--- directory.c	22 Jun 2005 21:59:11 -0000	1.236
+++ directory.c	29 Jun 2005 21:46:55 -0000	1.237
@@ -435,6 +435,9 @@
 
       httpcommand = "GET";
       tor_snprintf(url, sizeof(url), "/tor/rendezvous/%s", resource);
+      /* XXXX011 Once directories understand versioned descriptors, switch to this
+       * URL in order to get the most recent version */
+      // tor_snprintf(url, sizeof(url), "/tor/rendezvous1/%s", resource);
 
       break;
     case DIR_PURPOSE_UPLOAD_RENDDESC:
@@ -1004,10 +1007,13 @@
     return 0;
   }
 
-  if (!strcmpstart(url,"/tor/rendezvous/")) {
+  if (!strcmpstart(url,"/tor/rendezvous/") ||
+      !strcmpstart(url,"/tor/rendezvous1/")) {
     /* rendezvous descriptor fetch */
     const char *descp;
     size_t desc_len;
+    int versioned = !strcmpstart(url,"/tor/rendezvous1/");
+    const char *query = url+strlen("/tor/rendezvous/")+(versioned?1:0);
 
     if (!authdir_mode(get_options())) {
       /* We don't hand out rend descs. In fact, it could be a security
@@ -1019,7 +1025,7 @@
       tor_free(url);
       return 0;
     }
-    switch (rend_cache_lookup_desc(url+strlen("/tor/rendezvous/"), &descp, &desc_len)) {
+    switch (rend_cache_lookup_desc(query, versioned?-1:0, &descp, &desc_len)) {
       case 1: /* valid */
         format_rfc1123_time(date, time(NULL));
         tor_snprintf(tmp, sizeof(tmp), "HTTP/1.0 200 OK\r\nDate: %s\r\nContent-Length: %d\r\nContent-Type: application/octet-stream\r\n\r\n",

Index: onion.c
===================================================================
RCS file: /home/or/cvsroot/tor/src/or/onion.c,v
retrieving revision 1.182
retrieving revision 1.183
diff -u -d -r1.182 -r1.183
--- onion.c	13 Jun 2005 17:33:12 -0000	1.182
+++ onion.c	29 Jun 2005 21:46:55 -0000	1.183
@@ -153,6 +153,9 @@
   crypto_dh_env_t *dh = NULL;
   int dhbytes, pkbytes;
 
+  tor_assert(dest_router_key);
+  tor_assert(handshake_state_out);
+  tor_assert(onion_skin_out);
   *handshake_state_out = NULL;
   memset(onion_skin_out, 0, ONIONSKIN_CHALLENGE_LEN);
 

Index: or.h
===================================================================
RCS file: /home/or/cvsroot/tor/src/or/or.h,v
retrieving revision 1.621
retrieving revision 1.622
diff -u -d -r1.621 -r1.622
--- or.h	27 Jun 2005 23:35:04 -0000	1.621
+++ or.h	29 Jun 2005 21:46:55 -0000	1.622
@@ -664,7 +664,8 @@
   int done_receiving; /**< For half-open connections; not used currently. */
   char has_sent_end; /**< For debugging: set once we've set the stream end,
                         and check in circuit_about_to_close_connection(). */
-  struct circuit_t *on_circuit; /**< DOCDOC */
+  struct circuit_t *on_circuit; /**< The circuit (if any) that this edge
+                                 * connection is using. */
 
   /* Used only by AP connections */
   socks_request_t *socks_request; /**< SOCKS structure describing request (AP
@@ -766,6 +767,17 @@
   char *signing_router;
 } routerlist_t;
 
+/** Informetation on router used when extending a circuit.  (We don't need a
+ * full routerinfo_t to extend: we only need addr:port:keyid to build an OR
+ * connection, and onion_key to create the onionskin.) */
+typedef struct extend_info_t {
+  char nickname[MAX_HEX_NICKNAME_LEN+1]; /**< This router's nickname for display*/
+  char identity_digest[DIGEST_LEN]; /**< Hash of this router's identity key */
+  uint32_t addr; /**< IP address in host order */
+  uint16_t port; /**< OR port */
+  crypto_pk_env_t *onion_key; /**< Current onionskin key */
+} extend_info_t;
+
 #define CRYPT_PATH_MAGIC 0x70127012u
 
 /** Holds accounting information for a single step in the layered encryption
@@ -793,12 +805,8 @@
   /** Negotiated key material shared with the OR at this step. */
   char handshake_digest[DIGEST_LEN];/* KH in tor-spec.txt */
 
-  /** IP4 address of the OR at this step. */
-  uint32_t addr;
-  /** Port of the OR at this step. */
-  uint16_t port;
-  /** Identity key digest of the OR at this step. */
-  char identity_digest[DIGEST_LEN];
+  /** Information to extend to the OR at this step. */
+  extend_info_t *extend_info;
 
   /** Is the circuit built to this step?  Must be one of:
    *    - CPATH_STATE_CLOSED (The circuit has not been extended to this step)
@@ -836,10 +844,8 @@
 typedef struct {
   /** Intended length of the final circuit. */
   int desired_path_len;
-  /** Nickname of planned exit node. */
-  char *chosen_exit_name;
-  /** Identity of planned exit node. */
-  char chosen_exit_digest[DIGEST_LEN];
+  /** How to extend to the planned exit node. */
+  extend_info_t *chosen_exit;
   /** Whether every node in the circ must have adequate uptime. */
   int need_uptime;
   /** Whether every node in the circ must have adequate capacity. */
@@ -1220,7 +1226,7 @@
 void circuit_dump_by_conn(connection_t *conn, int severity);
 circuit_t *circuit_init(uint8_t purpose, int need_uptime,
                         int need_capacity, int internal);
-circuit_t *circuit_establish_circuit(uint8_t purpose, routerinfo_t *exit,
+circuit_t *circuit_establish_circuit(uint8_t purpose, extend_info_t *exit,
                                      int need_uptime, int need_capacity, int internal);
 int circuit_handle_first_hop(circuit_t *circ);
 void circuit_n_conn_done(connection_t *or_conn, int status);
@@ -1234,9 +1240,14 @@
 int circuit_all_predicted_ports_handled(time_t now, int *need_uptime,
                                         int *need_capacity);
 
-int circuit_append_new_exit(circuit_t *circ, routerinfo_t *exit);
-int circuit_extend_to_new_exit(circuit_t *circ, routerinfo_t *exit);
+int circuit_append_new_exit(circuit_t *circ, extend_info_t *info);
+int circuit_extend_to_new_exit(circuit_t *circ, extend_info_t *info);
 void onion_append_to_cpath(crypt_path_t **head_ptr, crypt_path_t *new_hop);
+extend_info_t *extend_info_from_router(routerinfo_t *r);
+extend_info_t *extend_info_dup(extend_info_t *info);
+void extend_info_free(extend_info_t *info);
+routerinfo_t *build_state_get_exit_router(cpath_build_state_t *state);
+const char *build_state_get_exit_nickname(cpath_build_state_t *state);
 
 /********************************* circuitlist.c ***********************/
 
@@ -1280,6 +1291,9 @@
 void circuit_build_failed(circuit_t *circ);
 circuit_t *circuit_launch_by_nickname(uint8_t purpose, const char *exit_nickname,
                                       int need_uptime, int need_capacity, int is_internal);
+circuit_t *circuit_launch_by_extend_info(uint8_t purpose,
+                                         extend_info_t *info,
+                                         int need_uptime, int need_capacity, int is_internal);
 circuit_t *circuit_launch_by_router(uint8_t purpose, routerinfo_t *exit,
                                     int need_uptime, int need_capacity, int is_internal);
 void circuit_reset_failure_count(int timeout);
@@ -1707,22 +1721,34 @@
 void rend_client_rendcirc_has_opened(circuit_t *circ);
 int rend_client_introduction_acked(circuit_t *circ, const char *request, size_t request_len);
 void rend_client_refetch_renddesc(const char *query);
-int rend_client_remove_intro_point(char *failed_intro, const char *query);
+int rend_client_remove_intro_point(extend_info_t *failed_intro, const char *query);
 int rend_client_rendezvous_acked(circuit_t *circ, const char *request, size_t request_len);
 int rend_client_receive_rendezvous(circuit_t *circ, const char *request, size_t request_len);
-void rend_client_desc_here(char *query);
+void rend_client_desc_here(const char *query);
 
-char *rend_client_get_random_intro(char *query);
+extend_info_t *rend_client_get_random_intro(const char *query);
 
 int rend_client_send_introduction(circuit_t *introcirc, circuit_t *rendcirc);
 
 /********************************* rendcommon.c ***************************/
 
+/** Information used to connect to a  hidden service. */
 typedef struct rend_service_descriptor_t {
-  crypto_pk_env_t *pk;
-  time_t timestamp;
-  int n_intro_points;
+  crypto_pk_env_t *pk; /**< This service's public key. */
+  int version; /**< 0 or 1 */
+  time_t timestamp; /**< Time when the descriptor was generated. */
+  uint16_t protocols; /**< Bitmask: which rendezvous protocols are supporeted?
+                       * (We allow bits '0', '1', and '2' to be set.) */
+  int n_intro_points; /**< Number of introduction points. */
+  /** Array of n_intro_points elements for this service's introduction points'
+   * nicknames.  Elements are removed from this array if introduction attempts
+   * fail. */
   char **intro_points;
+  /** Array of n_intro_points elements for this service's introduction points'
+   * extend_infos, or NULL if this descriptor is V0.  Elements are removed
+   * from this array if introduction attempts fail.  If this array is present,
+   * its elements correspond to the elements of intro_points. */
+  extend_info_t **intro_point_extend_info;
 } rend_service_descriptor_t;
 
 int rend_cmp_service_ids(const char *one, const char *two);
@@ -1732,6 +1758,7 @@
 
 void rend_service_descriptor_free(rend_service_descriptor_t *desc);
 int rend_encode_service_descriptor(rend_service_descriptor_t *desc,
+                                   int version,
                                    crypto_pk_env_t *key,
                                    char **str_out,
                                    size_t *len_out);
@@ -1740,7 +1767,7 @@
 
 typedef struct rend_cache_entry_t {
   size_t len; /* Length of desc */
-  time_t received; /* When did we get the descriptor? */
+  time_t received; /* When was the descriptor received? */
   char *desc; /* Service descriptor */
   rend_service_descriptor_t *parsed; /* Parsed value of 'desc' */
 } rend_cache_entry_t;
@@ -1749,8 +1776,8 @@
 void rend_cache_clean(void);
 void rend_cache_free_all(void);
 int rend_valid_service_id(const char *query);
-int rend_cache_lookup_desc(const char *query, const char **desc, size_t *desc_len);
-int rend_cache_lookup_entry(const char *query, rend_cache_entry_t **entry_out);
+int rend_cache_lookup_desc(const char *query, int version, const char **desc, size_t *desc_len);
+int rend_cache_lookup_entry(const char *query, int version, rend_cache_entry_t **entry_out);
 int rend_cache_store(const char *desc, size_t desc_len);
 
 /********************************* rendservice.c ***************************/

Index: relay.c
===================================================================
RCS file: /home/or/cvsroot/tor/src/or/relay.c,v
retrieving revision 1.73
retrieving revision 1.74
diff -u -d -r1.73 -r1.74
--- relay.c	19 Jun 2005 08:35:16 -0000	1.73
+++ relay.c	29 Jun 2005 21:46:55 -0000	1.74
@@ -633,11 +633,8 @@
     log_fn(LOG_INFO,"Address '%s' refused due to '%s'. Considering retrying.",
            safe_str(conn->socks_request->address),
            connection_edge_end_reason_str(reason));
-    exitrouter = router_get_by_digest(circ->build_state->chosen_exit_digest);
-    if (!exitrouter) {
-      log_fn(LOG_INFO,"Skipping broken circ (exit router vanished)");
-      return 0; /* this circuit is screwed and doesn't know it yet */
-    }
+    exitrouter =
+      router_get_by_digest(circ->build_state->chosen_exit->identity_digest);
     switch (reason) {
       case END_STREAM_REASON_EXITPOLICY:
         if (rh->length >= 5) {
@@ -652,15 +649,15 @@
                                     conn->chosen_exit_name);
         }
         /* check if he *ought* to have allowed it */
-        if (rh->length < 5 ||
-            (!tor_inet_aton(conn->socks_request->address, &in) &&
-             !conn->chosen_exit_name)) {
+        if (exitrouter &&
+            (rh->length < 5 ||
+             (!tor_inet_aton(conn->socks_request->address, &in) &&
+              !conn->chosen_exit_name))) {
           log_fn(LOG_NOTICE,"Exitrouter '%s' seems to be more restrictive than its exit policy. Not using this router as exit for now.", exitrouter->nickname);
           addr_policy_free(exitrouter->exit_policy);
           exitrouter->exit_policy =
             router_parse_addr_policy_from_string("reject *:*");
         }
-
         if (connection_ap_detach_retriable(conn, circ) >= 0)
           return 0;
         /* else, conn will get closed below */
@@ -683,10 +680,11 @@
         break;
       case END_STREAM_REASON_HIBERNATING:
       case END_STREAM_REASON_RESOURCELIMIT:
-        addr_policy_free(exitrouter->exit_policy);
-        exitrouter->exit_policy =
-          router_parse_addr_policy_from_string("reject *:*");
-
+        if (exitrouter) {
+          addr_policy_free(exitrouter->exit_policy);
+          exitrouter->exit_policy =
+            router_parse_addr_policy_from_string("reject *:*");
+        }
         if (connection_ap_detach_retriable(conn, circ) >= 0)
           return 0;
         /* else, will close below */

Index: rendclient.c
===================================================================
RCS file: /home/or/cvsroot/tor/src/or/rendclient.c,v
retrieving revision 1.89
retrieving revision 1.90
diff -u -d -r1.89 -r1.90
--- rendclient.c	11 Jun 2005 18:52:11 -0000	1.89
+++ rendclient.c	29 Jun 2005 21:46:55 -0000	1.90
@@ -58,15 +58,16 @@
   size_t payload_len;
   int r;
   char payload[RELAY_PAYLOAD_SIZE];
-  char tmp[1+(MAX_HEX_NICKNAME_LEN+1)+REND_COOKIE_LEN+DH_KEY_LEN];
+  char tmp[RELAY_PAYLOAD_SIZE];
   rend_cache_entry_t *entry;
   crypt_path_t *cpath;
+  off_t dh_offset;
 
   tor_assert(introcirc->purpose == CIRCUIT_PURPOSE_C_INTRODUCING);
   tor_assert(rendcirc->purpose == CIRCUIT_PURPOSE_C_REND_READY);
   tor_assert(!rend_cmp_service_ids(introcirc->rend_query, rendcirc->rend_query));
 
-  if (rend_cache_lookup_entry(introcirc->rend_query, &entry) < 1) {
+  if (rend_cache_lookup_entry(introcirc->rend_query, -1, &entry) < 1) {
     log_fn(LOG_WARN,"query '%s' didn't have valid rend desc in cache. Failing.",
            safe_str(introcirc->rend_query));
     goto err;
@@ -95,22 +96,28 @@
   }
 
   /* write the remaining items into tmp */
-#if 0
-  tmp[0] = 1; /* version 1 of the cell format */
-  /* nul pads */
-  strncpy(tmp+1, rendcirc->build_state->chosen_exit_name, (MAX_HEX_NICKNAME_LEN+1));
-  memcpy(tmp+1+MAX_HEX_NICKNAME_LEN+1, rendcirc->rend_cookie, REND_COOKIE_LEN);
-#else
-  strncpy(tmp, rendcirc->build_state->chosen_exit_name, (MAX_NICKNAME_LEN+1)); /* nul pads */
-  memcpy(tmp+MAX_NICKNAME_LEN+1, rendcirc->rend_cookie, REND_COOKIE_LEN);
-#endif
-  if (crypto_dh_get_public(cpath->dh_handshake_state,
-#if 0
-                           tmp+1+MAX_HEX_NICKNAME_LEN+1+REND_COOKIE_LEN,
-#else
-                           tmp+MAX_NICKNAME_LEN+1+REND_COOKIE_LEN,
-#endif
+  if (entry->parsed->protocols & (1<<2)) {
+    /* version 2 format */
+    extend_info_t *extend_info = rendcirc->build_state->chosen_exit;
+    int klen;
+    tmp[0] = 2; /* version 2 of the cell format */
+    /* nul pads */
+    set_uint32(tmp+1, htonl(extend_info->addr));
+    set_uint16(tmp+5, htons(extend_info->port));
+    memcpy(tmp+7, extend_info->identity_digest, DIGEST_LEN);
+    klen = crypto_pk_asn1_encode(extend_info->onion_key, tmp+7+DIGEST_LEN+2,
+                                 sizeof(tmp)-(7+DIGEST_LEN+2));
+    set_uint16(tmp+7+DIGEST_LEN, htons(klen));
+    memcpy(tmp+7+DIGEST_LEN+2+klen, rendcirc->rend_cookie, REND_COOKIE_LEN);
+    dh_offset = 7+DIGEST_LEN+2+klen+REND_COOKIE_LEN;
+  } else {
+    /* Version 0. */
+    strncpy(tmp, rendcirc->build_state->chosen_exit->nickname, (MAX_NICKNAME_LEN+1)); /* nul pads */
+    memcpy(tmp+MAX_NICKNAME_LEN+1, rendcirc->rend_cookie, REND_COOKIE_LEN);
+    dh_offset = MAX_NICKNAME_LEN+1+REND_COOKIE_LEN;
+  }
 
+  if (crypto_dh_get_public(cpath->dh_handshake_state, tmp+dh_offset,
                            DH_KEY_LEN)<0) {
     log_fn(LOG_WARN, "Couldn't extract g^x");
     goto err;
@@ -119,11 +126,7 @@
   /*XXX maybe give crypto_pk_public_hybrid_encrypt a max_len arg,
    * to avoid buffer overflows? */
   r = crypto_pk_public_hybrid_encrypt(entry->parsed->pk, payload+DIGEST_LEN, tmp,
-#if 0
-                           1+MAX_HEX_NICKNAME_LEN+1+REND_COOKIE_LEN+DH_KEY_LEN,
-#else
-                           MAX_NICKNAME_LEN+1+REND_COOKIE_LEN+DH_KEY_LEN,
-#endif
+                                      dh_offset+DH_KEY_LEN,
                                       PK_PKCS1_OAEP_PADDING, 0);
   if (r<0) {
     log_fn(LOG_WARN,"hybrid pk encrypt failed.");
@@ -174,7 +177,6 @@
 rend_client_introduction_acked(circuit_t *circ,
                                const char *request, size_t request_len)
 {
-  char *nickname;
   circuit_t *rendcirc;
 
   if (circ->purpose != CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT) {
@@ -184,7 +186,8 @@
     return -1;
   }
 
-  tor_assert(circ->build_state->chosen_exit_name);
+  tor_assert(circ->build_state->chosen_exit);
+  tor_assert(circ->build_state->chosen_exit->nickname);
 
   if (request_len == 0) {
     /* It's an ACK; the introduction point relayed our introduction request. */
@@ -209,27 +212,26 @@
      * points. If any remain, extend to a new one and try again.
      * If none remain, refetch the service descriptor.
      */
-    if (rend_client_remove_intro_point(circ->build_state->chosen_exit_name,
+    if (rend_client_remove_intro_point(circ->build_state->chosen_exit,
                                        circ->rend_query) > 0) {
       /* There are introduction points left. re-extend the circuit to
        * another intro point and try again. */
-      routerinfo_t *r;
-      nickname = rend_client_get_random_intro(circ->rend_query);
-      tor_assert(nickname);
-      log_fn(LOG_INFO,"Got nack for %s from %s, extending to %s.",
-             safe_str(circ->rend_query),
-             circ->build_state->chosen_exit_name, nickname);
-      if (!(r = router_get_by_nickname(nickname))) {
-        log_fn(LOG_WARN, "Advertised intro point '%s' for %s is not known. Closing.",
-               nickname, safe_str(circ->rend_query));
-        tor_free(nickname);
+      extend_info_t *info;
+      int result;
+      info = rend_client_get_random_intro(circ->rend_query);
+      if (!info) {
+        log_fn(LOG_WARN, "No introduction points left for %s. Closing.",
+               safe_str(circ->rend_query));
         circuit_mark_for_close(circ);
         return -1;
       }
-      log_fn(LOG_INFO, "Chose new intro point %s for %s (circ %d)",
-             nickname, safe_str(circ->rend_query), circ->n_circ_id);
-      tor_free(nickname);
-      return circuit_extend_to_new_exit(circ, r);
+      log_fn(LOG_INFO,"Got nack for %s from %s, extending circ %d to %s.",
+             safe_str(circ->rend_query),
+             circ->build_state->chosen_exit->nickname, circ->n_circ_id,
+             info->nickname);
+      result = circuit_extend_to_new_exit(circ, info);
+      extend_info_free(info);
+      return result;
     }
   }
   return 0;
@@ -257,13 +259,13 @@
  * unrecognized, 1 if recognized and some intro points remain.
  */
 int
-rend_client_remove_intro_point(char *failed_intro, const char *query)
+rend_client_remove_intro_point(extend_info_t *failed_intro, const char *query)
 {
   int i, r;
   rend_cache_entry_t *ent;
   connection_t *conn;
 
-  r = rend_cache_lookup_entry(query, &ent);
+  r = rend_cache_lookup_entry(query, -1, &ent);
   if (r<0) {
     log_fn(LOG_WARN, "Malformed service ID '%s'", safe_str(query));
     return -1;
@@ -275,12 +277,31 @@
     return 0;
   }
 
-  for (i=0; i < ent->parsed->n_intro_points; ++i) {
-    if (!strcasecmp(ent->parsed->intro_points[i], failed_intro)) {
-      tor_free(ent->parsed->intro_points[i]);
-      ent->parsed->intro_points[i] =
-        ent->parsed->intro_points[--ent->parsed->n_intro_points];
-      break;
+  if (ent->parsed->intro_point_extend_info) {
+    for (i=0; i < ent->parsed->n_intro_points; ++i) {
+      if (!memcmp(failed_intro->identity_digest,
+                  ent->parsed->intro_point_extend_info[i]->identity_digest,
+                  DIGEST_LEN)) {
+        tor_assert(!strcmp(ent->parsed->intro_points[i],
+                           ent->parsed->intro_point_extend_info[i]->nickname));
+        tor_free(ent->parsed->intro_points[i]);
+        extend_info_free(ent->parsed->intro_point_extend_info[i]);
+        --ent->parsed->n_intro_points;
+        ent->parsed->intro_points[i] =
+          ent->parsed->intro_points[ent->parsed->n_intro_points];
+        ent->parsed->intro_point_extend_info[i] =
+          ent->parsed->intro_point_extend_info[ent->parsed->n_intro_points];
+        break;
+      }
+    }
+  } else {
+    for (i=0; i < ent->parsed->n_intro_points; ++i) {
+      if (!strcasecmp(ent->parsed->intro_points[i], failed_intro->nickname)) {
+        tor_free(ent->parsed->intro_points[i]);
+        ent->parsed->intro_points[i] =
+          ent->parsed->intro_points[--ent->parsed->n_intro_points];
+        break;
+      }
     }
   }
 
@@ -385,7 +406,7 @@
  * else fail them.
  */
 void
-rend_client_desc_here(char *query)
+rend_client_desc_here(const char *query)
 {
   connection_t *conn;
   rend_cache_entry_t *entry;
@@ -393,7 +414,7 @@
 
   while ((conn = connection_get_by_type_state_rendquery(CONN_TYPE_AP,
                                  AP_CONN_STATE_RENDDESC_WAIT, query))) {
-    if (rend_cache_lookup_entry(conn->rend_query, &entry) == 1 &&
+    if (rend_cache_lookup_entry(conn->rend_query, -1, &entry) == 1 &&
         entry->parsed->n_intro_points > 0) {
       /* either this fetch worked, or it failed but there was a
        * valid entry from before which we should reuse */
@@ -419,37 +440,42 @@
   }
 }
 
-/** strdup a nickname for a random introduction
- * point of query. return NULL if error.
+/** Return a newly allocated extend_info_t* for a randomly chosen introduction
+ * point for the named hidden service.  Return NULL if all introduction points
+ * have been tried and failed.
  */
-char *
-rend_client_get_random_intro(char *query)
+extend_info_t *
+rend_client_get_random_intro(const char *query)
 {
   int i;
-  smartlist_t *sl;
-  char *choice;
-  char *nickname;
   rend_cache_entry_t *entry;
 
-  if (rend_cache_lookup_entry(query, &entry) < 1) {
+  if (rend_cache_lookup_entry(query, -1, &entry) < 1) {
     log_fn(LOG_WARN,"query '%s' didn't have valid rend desc in cache. Failing.",
            safe_str(query));
     return NULL;
   }
 
-  sl = smartlist_create();
+ again:
+  if (!entry->parsed->n_intro_points)
+    return NULL;
 
-  /* add the intro point nicknames */
-  for (i=0;i<entry->parsed->n_intro_points;i++)
-    smartlist_add(sl,entry->parsed->intro_points[i]);
+  i = crypto_pseudo_rand_int(entry->parsed->n_intro_points);
 
-  choice = smartlist_choose(sl);
-  if (!choice) {
-    smartlist_free(sl);
-    return NULL;
+  if (entry->parsed->intro_point_extend_info) {
+    return extend_info_dup(entry->parsed->intro_point_extend_info[i]);
+  } else {
+    /* add the intro point nicknames */
+    char *choice = entry->parsed->intro_points[i];
+    routerinfo_t *router = router_get_by_nickname(choice);
+    if (!router) {
+      log_fn(LOG_WARN, "Unknown router with nickname %s; trying another.",choice);
+      tor_free(choice);
+      entry->parsed->intro_points[i] =
+        entry->parsed->intro_points[--entry->parsed->n_intro_points];
+      goto again;
+    }
+    return extend_info_from_router(router);
   }
-  nickname = tor_strdup(choice);
-  smartlist_free(sl);
-  return nickname;
 }
 

Index: rendcommon.c
===================================================================
RCS file: /home/or/cvsroot/tor/src/or/rendcommon.c,v
retrieving revision 1.50
retrieving revision 1.51
diff -u -d -r1.50 -r1.51
--- rendcommon.c	11 Jun 2005 18:52:11 -0000	1.50
+++ rendcommon.c	29 Jun 2005 21:46:55 -0000	1.51
@@ -29,6 +29,13 @@
     }
     tor_free(desc->intro_points);
   }
+  if (desc->intro_point_extend_info) {
+    for (i=0; i < desc->n_intro_points; ++i) {
+      if (desc->intro_point_extend_info[i])
+        extend_info_free(desc->intro_point_extend_info[i]);
+    }
+    tor_free(desc->intro_point_extend_info);
+  }
   tor_free(desc);
 }
 
@@ -38,38 +45,52 @@
  */
 int
 rend_encode_service_descriptor(rend_service_descriptor_t *desc,
+                               int version,
                                crypto_pk_env_t *key,
                                char **str_out, size_t *len_out)
 {
-  char *buf, *cp, *ipoint;
+  char *cp;
+  char *end;
   int i;
-  size_t keylen, asn1len;
-  keylen = crypto_pk_keysize(desc->pk);
-  buf = tor_malloc(keylen*2); /* Too long, but that's okay. */
-  i = crypto_pk_asn1_encode(desc->pk, buf, keylen*2);
-  if (i<0) {
-    tor_free(buf);
-    return -1;
-  }
-  asn1len = i;
-  *len_out = 2 + asn1len + 4 + 2 + keylen;
-  for (i = 0; i < desc->n_intro_points; ++i) {
-    *len_out += strlen(desc->intro_points[i]) + 1;
+  size_t asn1len;
+  cp = *str_out = tor_malloc(PK_BYTES*2*(desc->n_intro_points+1)); /*Too long, but ok*/
+  end = cp + PK_BYTES*2*(desc->n_intro_points+1);
+  if (version) {
+    *(uint8_t*)cp = (uint8_t)0xff;
+    *(uint8_t*)(cp+1) = (uint8_t)version;
+    cp += 2;
   }
-  cp = *str_out = tor_malloc(*len_out);
+  asn1len = crypto_pk_asn1_encode(desc->pk, cp+2, end-(cp+2));
   set_uint16(cp, htons((uint16_t)asn1len));
-  cp += 2;
-  memcpy(cp, buf, asn1len);
-  tor_free(buf);
-  cp += asn1len;
+  cp += 2+asn1len;
   set_uint32(cp, htonl((uint32_t)desc->timestamp));
   cp += 4;
+  if (version == 1) {
+    set_uint16(cp, htons(desc->protocols));
+    cp += 2;
+  }
   set_uint16(cp, htons((uint16_t)desc->n_intro_points));
   cp += 2;
-  for (i=0; i < desc->n_intro_points; ++i) {
-    ipoint = (char*)desc->intro_points[i];
-    strlcpy(cp, ipoint, *len_out-(cp-*str_out));
-    cp += strlen(ipoint)+1;
+  if (version == 0) {
+    tor_assert(desc->intro_points);
+    for (i=0; i < desc->n_intro_points; ++i) {
+      char *ipoint = (char*)desc->intro_points[i];
+      strlcpy(cp, ipoint, *len_out-(cp-*str_out));
+      cp += strlen(ipoint)+1;
+    }
+  } else {
+    tor_assert(desc->intro_point_extend_info);
+    for (i=0; i < desc->n_intro_points; ++i) {
+      extend_info_t *info = desc->intro_point_extend_info[i];
+      int klen;
+      set_uint32(cp, htonl(info->addr));
+      set_uint16(cp+4, htons(info->port));
+      memcpy(cp+6, info->identity_digest, DIGEST_LEN);
+      klen = crypto_pk_asn1_encode(info->onion_key, cp+6+DIGEST_LEN+2,
+                                   (end-(cp+6+DIGEST_LEN+2)));
+      set_uint16(cp+6+DIGEST_LEN, htons((uint16_t)klen));
+      cp += 6+DIGEST_LEN+2+klen;
+    }
   }
   i = crypto_pk_private_sign_digest(key, cp, *str_out, cp-*str_out);
   if (i<0) {
@@ -77,7 +98,7 @@
     return -1;
   }
   cp += i;
-  tor_assert(*len_out == (size_t)(cp-*str_out));
+  *len_out = (size_t)(cp-*str_out);
   return 0;
 }
 
@@ -92,10 +113,18 @@
   int i;
   size_t keylen, asn1len;
   const char *end, *cp, *eos;
+  int version = 0;
 
   result = tor_malloc_zero(sizeof(rend_service_descriptor_t));
   cp = str;
   end = str+len;
+  if (end-cp<2) goto truncated;
+  if (*(uint8_t*)cp == 0xff) {
+    result->version = version = *(uint8_t*)(cp+1);
+    cp += 2;
+  } else {
+    result->version = version = 0;
+  }
   if (end-cp < 2) goto truncated;
   asn1len = ntohs(get_uint16(cp));
   cp += 2;
@@ -106,16 +135,51 @@
   if (end-cp < 4) goto truncated;
   result->timestamp = (time_t) ntohl(get_uint32(cp));
   cp += 4;
+  if (version == 1) {
+    if (end-cp < 2) goto truncated;
+    result->protocols = ntohs(get_uint16(cp));
+    cp += 2;
+  } else {
+    result->protocols = 1;
+  }
   if (end-cp < 2) goto truncated;
   result->n_intro_points = ntohs(get_uint16(cp));
-  result->intro_points = tor_malloc_zero(sizeof(char*)*result->n_intro_points);
+
   cp += 2;
-  for (i=0;i<result->n_intro_points;++i) {
-    if (end-cp < 2) goto truncated;
-    eos = (const char *)memchr(cp,'\0',end-cp);
-    if (!eos) goto truncated;
-    result->intro_points[i] = tor_strdup(cp);
-    cp = eos+1;
+  if (version == 0) {
+    result->intro_points = tor_malloc_zero(sizeof(char*)*result->n_intro_points);
+    for (i=0;i<result->n_intro_points;++i) {
+      if (end-cp < 2) goto truncated;
+      eos = (const char *)memchr(cp,'\0',end-cp);
+      if (!eos) goto truncated;
+      result->intro_points[i] = tor_strdup(cp);
+      cp = eos+1;
+    }
+  } else {
+    result->intro_point_extend_info =
+      tor_malloc_zero(sizeof(extend_info_t*)*result->n_intro_points);
+    result->intro_points = tor_malloc_zero(sizeof(char*)*result->n_intro_points);
+    for (i=0;i<result->n_intro_points;++i) {
+      extend_info_t *info = result->intro_point_extend_info[i] =
+        tor_malloc_zero(sizeof(extend_info_t));
+      int klen;
+      if (end-cp < 8+DIGEST_LEN) goto truncated;
+      info->addr = ntohl(get_uint32(cp));
+      info->port = ntohs(get_uint16(cp+4));
+      memcpy(info->identity_digest, cp+6, DIGEST_LEN);
+      info->nickname[0] = '$';
+      base16_encode(info->nickname+1, sizeof(info->nickname)-1,
+                    info->identity_digest, DIGEST_LEN);
+      result->intro_points[i] = tor_strdup(info->nickname);
+      klen = ntohs(get_uint16(cp+6+DIGEST_LEN));
+      cp += 8+DIGEST_LEN;
+      if (end-cp < klen) goto truncated;
+      if (!(info->onion_key = crypto_pk_asn1_decode(cp,klen))) {
+        log_fn(LOG_WARN, "error decoding onion key for intro point");
+        goto error;
+      }
+      cp += klen;
+    }
   }
   keylen = crypto_pk_keysize(result->pk);
   tor_assert(end-cp >= 0);
@@ -227,16 +291,28 @@
   return 1;
 }
 
-/** If we have a cached rend_cache_entry_t for the service ID <b>query</b>, set
- * *<b>e</b> to that entry and return 1.  Else return 0.
+/** If we have a cached rend_cache_entry_t for the service ID <b>query</b>,
+ * set *<b>e</b> to that entry and return 1.  Else return 0.  If
+ * <b>version</b> is nonnegative, only return an entry in that descriptor
+ * format version. Otherwise (if <b>version</b> is negative), return the most
+ * recent format we have.
  */
 int
-rend_cache_lookup_entry(const char *query, rend_cache_entry_t **e)
+rend_cache_lookup_entry(const char *query, int version, rend_cache_entry_t **e)
 {
+  char key[REND_SERVICE_ID_LEN+2];
   tor_assert(rend_cache);
   if (!rend_valid_service_id(query))
     return -1;
-  *e = strmap_get_lc(rend_cache, query);
+  *e = NULL;
+  if (version != 0) {
+    tor_snprintf(key, sizeof(key), "1%s", query);
+    *e = strmap_get_lc(rend_cache, key);
+  }
+  if (!*e && version != 1) {
+    tor_snprintf(key, sizeof(key), "0%s", query);
+    *e = strmap_get_lc(rend_cache, key);
+  }
   if (!*e)
     return 0;
   return 1;
@@ -251,11 +327,11 @@
  * *desc.
  */
 int
-rend_cache_lookup_desc(const char *query, const char **desc, size_t *desc_len)
+rend_cache_lookup_desc(const char *query, int version, const char **desc, size_t *desc_len)
 {
   rend_cache_entry_t *e;
   int r;
-  r = rend_cache_lookup_entry(query,&e);
+  r = rend_cache_lookup_entry(query,version,&e);
   if (r <= 0) return r;
   *desc = e->desc;
   *desc_len = e->len;
@@ -275,6 +351,7 @@
   rend_cache_entry_t *e;
   rend_service_descriptor_t *parsed;
   char query[REND_SERVICE_ID_LEN+1];
+  char key[REND_SERVICE_ID_LEN+2];
   time_t now;
 
   tor_assert(rend_cache);
@@ -288,6 +365,7 @@
     rend_service_descriptor_free(parsed);
     return -1;
   }
+  tor_snprintf(key, sizeof(key), "%c%s", parsed->version?'1':'0', query);
   now = time(NULL);
   if (parsed->timestamp < now-REND_CACHE_MAX_AGE-REND_CACHE_MAX_SKEW) {
     log_fn(LOG_WARN,"Service descriptor %s is too old", safe_str(query));
@@ -300,9 +378,9 @@
     rend_service_descriptor_free(parsed);
     return -1;
   }
-  e = (rend_cache_entry_t*) strmap_get_lc(rend_cache, query);
+  e = (rend_cache_entry_t*) strmap_get_lc(rend_cache, key);
   if (e && e->parsed->timestamp > parsed->timestamp) {
-    log_fn(LOG_INFO,"We already have a newer service descriptor %s with the same ID", safe_str(query));
+    log_fn(LOG_INFO,"We already have a newer service descriptor %s with the same ID and version", safe_str(query));
     rend_service_descriptor_free(parsed);
     return 0;
   }
@@ -314,7 +392,7 @@
   }
   if (!e) {
     e = tor_malloc_zero(sizeof(rend_cache_entry_t));
-    strmap_set_lc(rend_cache, query, e);
+    strmap_set_lc(rend_cache, key, e);
   } else {
     rend_service_descriptor_free(e->parsed);
     tor_free(e->desc);

Index: rendservice.c
===================================================================
RCS file: /home/or/cvsroot/tor/src/or/rendservice.c,v
retrieving revision 1.126
retrieving revision 1.127
diff -u -d -r1.126 -r1.127
--- rendservice.c	11 Jun 2005 18:52:11 -0000	1.126
+++ rendservice.c	29 Jun 2005 21:46:55 -0000	1.127
@@ -272,9 +272,12 @@
   d = service->desc = tor_malloc(sizeof(rend_service_descriptor_t));
   d->pk = crypto_pk_dup_key(service->private_key);
   d->timestamp = time(NULL);
+  d->version = 1;
   n = smartlist_len(service->intro_nodes);
   d->n_intro_points = 0;
-  d->intro_points = tor_malloc(sizeof(char*)*n);
+  d->intro_points = tor_malloc_zero(sizeof(char*)*n);
+  d->intro_point_extend_info = tor_malloc_zero(sizeof(extend_info_t*)*n);
+  d->protocols = (1<<2) | (1<<0); /* We support protocol 2 and protocol 0. */
   for (i=0; i < n; ++i) {
     router = router_get_by_nickname(smartlist_get(service->intro_nodes, i));
     if (!router) {
@@ -285,7 +288,10 @@
     circ = find_intro_circuit(router, service->pk_digest);
     if (circ && circ->purpose == CIRCUIT_PURPOSE_S_INTRO) {
       /* We have an entirely established intro circuit. */
-      d->intro_points[d->n_intro_points++] = tor_strdup(router->nickname);
+      d->intro_points[d->n_intro_points] = tor_strdup(router->nickname);
+      d->intro_point_extend_info[d->n_intro_points] =
+        extend_info_from_router(router);
+      d->n_intro_points++;
     }
   }
 }
@@ -379,7 +385,8 @@
 int
 rend_service_introduce(circuit_t *circuit, const char *request, size_t request_len)
 {
-  char *ptr, *rp_nickname, *r_cookie;
+  char *ptr, *r_cookie;
+  extend_info_t *extend_info = NULL;
   char buf[RELAY_PAYLOAD_SIZE];
   char keys[DIGEST_LEN+CPATH_KEY_MATERIAL_LEN]; /* Holds KH, Df, Db, Kf, Kb */
   rend_service_t *service;
@@ -390,8 +397,6 @@
   crypt_path_t *cpath = NULL;
   char serviceid[REND_SERVICE_ID_LEN+1];
   char hexcookie[9];
-  int version;
-  size_t nickname_field_len;
   int circ_needs_uptime;
 
   base32_encode(serviceid, REND_SERVICE_ID_LEN+1,
@@ -441,34 +446,73 @@
     return -1;
   }
   len = r;
-  if (*buf == 1) {
-    rp_nickname = buf+1;
-    nickname_field_len = MAX_HEX_NICKNAME_LEN+1;
-    version = 1;
+  if (*buf == 2) {
+    /* Version 2 INTRODUCE2 cell. */
+    int klen;
+    extend_info = tor_malloc_zero(sizeof(extend_info_t));
+    extend_info->addr = ntohl(get_uint32(buf+1));
+    extend_info->port = ntohs(get_uint16(buf+5));
+    memcpy(extend_info->identity_digest, buf+7, DIGEST_LEN);
+    extend_info->nickname[0] = '$';
+    base16_encode(extend_info->nickname+1, sizeof(extend_info->nickname)-1,
+                  extend_info->identity_digest, DIGEST_LEN);
+
+    klen = ntohs(get_uint16(buf+7+DIGEST_LEN));
+    if (len != 7+DIGEST_LEN+2+klen+20+128) {
+      log_fn(LOG_WARN, "Bad length %u for version 2 INTRODUCE2 cell.", (int)len);
+      goto err;
+    }
+    extend_info->onion_key = crypto_pk_asn1_decode(buf+7+DIGEST_LEN+2, klen);
+    if (!extend_info->onion_key) {
+      log_fn(LOG_WARN, "Error decoding onion key in version 2 INTRODUCE2 cell.");
+      goto err;
+    }
+    ptr = buf+7+DIGEST_LEN+2+klen;
+    len -= 7+DIGEST_LEN+2+klen;
   } else {
-    nickname_field_len = MAX_NICKNAME_LEN+1;
-    rp_nickname = buf;
-    version = 0;
-  }
-  /* XXX when 0.0.9.x is obsolete, change this to reject version != 1. */
-  ptr=memchr(rp_nickname,0,nickname_field_len);
-  if (!ptr || ptr == rp_nickname) {
-    log_fn(LOG_WARN, "Couldn't find a null-padded nickname in INTRODUCE2 cell");
-    return -1;
-  }
-  if ((version == 0 && !is_legal_nickname(rp_nickname)) ||
-      (version == 1 && !is_legal_nickname_or_hexdigest(rp_nickname))) {
-    log_fn(LOG_WARN, "Bad nickname in INTRODUCE2 cell.");
-    return -1;
+    char *rp_nickname;
+    size_t nickname_field_len;
+    routerinfo_t *router;
+    int version;
+    if (*buf == 1) {
+      rp_nickname = buf+1;
+      nickname_field_len = MAX_HEX_NICKNAME_LEN+1;
+      version = 1;
+    } else {
+      nickname_field_len = MAX_NICKNAME_LEN+1;
+      rp_nickname = buf;
+      version = 0;
+    }
+    /* XXX when 0.1.0.x is obsolete, change this to reject version != 2. */
+    ptr=memchr(rp_nickname,0,nickname_field_len);
+    if (!ptr || ptr == rp_nickname) {
+      log_fn(LOG_WARN, "Couldn't find a null-padded nickname in INTRODUCE2 cell");
+      return -1;
+    }
+    if ((version == 0 && !is_legal_nickname(rp_nickname)) ||
+        (version == 1 && !is_legal_nickname_or_hexdigest(rp_nickname))) {
+      log_fn(LOG_WARN, "Bad nickname in INTRODUCE2 cell.");
+      return -1;
+    }
+    /* Okay, now we know that a nickname is at the start of the buffer. */
+    ptr = rp_nickname+nickname_field_len;
+    len -= nickname_field_len;
+    len -= rp_nickname - buf; /* also remove header space used by version, if any */
+    router = router_get_by_nickname(rp_nickname);
+    if (!router) {
+      log_fn(LOG_WARN, "Couldn't found router '%s' named in rendezvous cell.",
+             rp_nickname);
+      goto err;
+    }
+
+    extend_info = extend_info_from_router(router);
   }
-  /* Okay, now we know that a nickname is at the start of the buffer. */
-  ptr = rp_nickname+nickname_field_len;
-  len -= nickname_field_len;
-  len -= rp_nickname - buf; /* also remove header space used by version, if any */
+
   if (len != REND_COOKIE_LEN+DH_KEY_LEN) {
     log_fn(LOG_WARN, "Bad length %u for INTRODUCE2 cell.", (int)len);
     return -1;
   }
+
   r_cookie = ptr;
   base16_encode(hexcookie,9,r_cookie,4);
 
@@ -492,19 +536,20 @@
   /* Launch a circuit to alice's chosen rendezvous point.
    */
   for (i=0;i<MAX_REND_FAILURES;i++) {
-    launched = circuit_launch_by_nickname(CIRCUIT_PURPOSE_S_CONNECT_REND, rp_nickname,
-                                          circ_needs_uptime, 1, 1);
+    launched = circuit_launch_by_extend_info(
+          CIRCUIT_PURPOSE_S_CONNECT_REND, extend_info, circ_needs_uptime, 1, 1);
+
     if (launched)
       break;
   }
   if (!launched) { /* give up */
     log_fn(LOG_WARN,"Giving up launching first hop of circuit to rendezvous point '%s' for service %s",
-           rp_nickname, serviceid);
+           extend_info->nickname, serviceid);
     goto err;
   }
   log_fn(LOG_INFO,
         "Accepted intro; launching circuit to '%s' (cookie %s) for service %s",
-         rp_nickname, hexcookie, serviceid);
+         extend_info->nickname, hexcookie, serviceid);
   tor_assert(launched->build_state);
   /* Fill in the circuit's state. */
   memcpy(launched->rend_pk_digest, circuit->rend_pk_digest,
@@ -522,11 +567,13 @@
   if (circuit_init_cpath_crypto(cpath,keys+DIGEST_LEN,1)<0)
     goto err;
   memcpy(cpath->handshake_digest, keys, DIGEST_LEN);
+  if (extend_info) extend_info_free(extend_info);
 
   return 0;
  err:
   if (dh) crypto_dh_free(dh);
   if (launched) circuit_mark_for_close(launched);
+  if (extend_info) extend_info_free(extend_info);
   return -1;
 }
 
@@ -545,7 +592,7 @@
       oldcirc->build_state->failure_count > MAX_REND_FAILURES ||
       oldcirc->build_state->expiry_time < time(NULL)) {
     log_fn(LOG_INFO,"Attempt to build circuit to %s for rendezvous has failed too many times or expired; giving up.",
-           oldcirc->build_state->chosen_exit_name);
+           oldcirc->build_state->chosen_exit->nickname);
     return;
   }
 
@@ -558,13 +605,13 @@
   }
 
   log_fn(LOG_INFO,"Reattempting rendezvous circuit to %s",
-         oldstate->chosen_exit_name);
+         oldstate->chosen_exit->nickname);
 
-  newcirc = circuit_launch_by_nickname(CIRCUIT_PURPOSE_S_CONNECT_REND,
-                               oldstate->chosen_exit_name, 0, 1, 1);
+  newcirc = circuit_launch_by_extend_info(CIRCUIT_PURPOSE_S_CONNECT_REND,
+                               oldstate->chosen_exit, 0, 1, 1);
   if (!newcirc) {
     log_fn(LOG_WARN,"Couldn't relaunch rendezvous circuit to %s",
-           oldstate->chosen_exit_name);
+           oldstate->chosen_exit->nickname);
     return;
   }
   newstate = newcirc->build_state;
@@ -783,8 +830,8 @@
   while ((circ = circuit_get_next_by_pk_and_purpose(circ,pk_digest,
                                                   CIRCUIT_PURPOSE_S_INTRO))) {
     tor_assert(circ->cpath);
-    if (circ->build_state->chosen_exit_name &&
-        !strcasecmp(circ->build_state->chosen_exit_name, router->nickname)) {
+    if (circ->build_state->chosen_exit->nickname &&
+        !strcasecmp(circ->build_state->chosen_exit->nickname, router->nickname)) {
       return circ;
     }
   }
@@ -793,8 +840,8 @@
   while ((circ = circuit_get_next_by_pk_and_purpose(circ,pk_digest,
                                         CIRCUIT_PURPOSE_S_ESTABLISH_INTRO))) {
     tor_assert(circ->cpath);
-    if (circ->build_state->chosen_exit_name &&
-        !strcasecmp(circ->build_state->chosen_exit_name, router->nickname)) {
+    if (circ->build_state->chosen_exit->nickname &&
+        !strcasecmp(circ->build_state->chosen_exit->nickname, router->nickname)) {
       return circ;
     }
   }
@@ -805,7 +852,7 @@
  * and upload it to all the dirservers.
  */
 static void
-upload_service_descriptor(rend_service_t *service)
+upload_service_descriptor(rend_service_t *service, int version)
 {
   char *desc;
   size_t desc_len;
@@ -813,6 +860,7 @@
   /* Update the descriptor. */
   rend_service_update_descriptor(service);
   if (rend_encode_service_descriptor(service->desc,
+                                     version,
                                      service->private_key,
                                      &desc, &desc_len)<0) {
     log_fn(LOG_WARN, "Couldn't encode service descriptor; not uploading");
@@ -963,7 +1011,10 @@
       /* if it's time, or if the directory servers have a wrong service
        * descriptor and ours has been stable for 5 seconds, upload a
        * new one. */
-      upload_service_descriptor(service);
+      upload_service_descriptor(service, 0);
+      /* XXXX011 NM Once directories understand versioned descriptors, enable
+       * this. */
+      // upload_service_descriptor(service, 1);
       service->next_upload_time = now + rendpostperiod;
     }
   }

Index: test.c
===================================================================
RCS file: /home/or/cvsroot/tor/src/or/test.c,v
retrieving revision 1.183
retrieving revision 1.184
diff -u -d -r1.183 -r1.184
--- test.c	27 Jun 2005 22:25:09 -0000	1.183
+++ test.c	29 Jun 2005 21:46:55 -0000	1.184
@@ -1400,30 +1400,89 @@
   rend_service_descriptor_t *d1, *d2;
   char *encoded;
   size_t len;
-  crypto_pk_env_t *pk1;
+  crypto_pk_env_t *pk1, *pk2;
   time_t now;
   pk1 = crypto_new_pk_env();
-
+  pk2 = crypto_new_pk_env();
   test_assert(!crypto_pk_generate_key(pk1));
+  test_assert(!crypto_pk_generate_key(pk2));
+
+  /* Test unversioned descriptor */
   d1 = tor_malloc_zero(sizeof(rend_service_descriptor_t));
-  d1->pk = pk1;
+  d1->pk = crypto_pk_dup_key(pk1);
   now = time(NULL);
   d1->timestamp = now;
   d1->n_intro_points = 3;
+  d1->version = 0;
   d1->intro_points = tor_malloc(sizeof(char*)*3);
   d1->intro_points[0] = tor_strdup("tom");
   d1->intro_points[1] = tor_strdup("crow");
   d1->intro_points[2] = tor_strdup("joel");
-  test_assert(! rend_encode_service_descriptor(d1, pk1, &encoded, &len));
+  test_assert(! rend_encode_service_descriptor(d1, 0, pk1, &encoded, &len));
   d2 = rend_parse_service_descriptor(encoded, len);
   test_assert(d2);
 
   test_assert(!crypto_pk_cmp_keys(d1->pk, d2->pk));
   test_eq(d2->timestamp, now);
+  test_eq(d2->version, 0);
+  test_eq(d2->protocols, 1);
   test_eq(d2->n_intro_points, 3);
   test_streq(d2->intro_points[0], "tom");
   test_streq(d2->intro_points[1], "crow");
   test_streq(d2->intro_points[2], "joel");
+  test_eq(NULL, d2->intro_point_extend_info);
+
+  rend_service_descriptor_free(d1);
+  rend_service_descriptor_free(d2);
+  tor_free(encoded);
+
+  /* Test versioned descriptor. */
+  d1 = tor_malloc_zero(sizeof(rend_service_descriptor_t));
+  d1->pk = crypto_pk_dup_key(pk1);
+  now = time(NULL);
+  d1->timestamp = now;
+  d1->n_intro_points = 2;
+  d1->version = 1;
+  d1->protocols = 60;
+  d1->intro_points = tor_malloc(sizeof(char*)*2);
+  d1->intro_point_extend_info = tor_malloc(sizeof(extend_info_t*)*2);
+  d1->intro_points[0] = tor_strdup("tom");
+  d1->intro_points[1] = tor_strdup("crow");
+  d1->intro_point_extend_info[0] = tor_malloc_zero(sizeof(extend_info_t));
+  strcpy(d1->intro_point_extend_info[0]->nickname, "tom");
+  d1->intro_point_extend_info[0]->addr = 1234;
+  d1->intro_point_extend_info[0]->port = 4567;
+  d1->intro_point_extend_info[0]->onion_key = crypto_pk_dup_key(pk1);
+  memset(d1->intro_point_extend_info[0]->identity_digest, 'a', DIGEST_LEN);
+
+  d1->intro_point_extend_info[1] = tor_malloc_zero(sizeof(extend_info_t));
+  strcpy(d1->intro_point_extend_info[1]->nickname, "crow");
+  d1->intro_point_extend_info[1]->addr = 6060842;
+  d1->intro_point_extend_info[1]->port = 8000;
+  d1->intro_point_extend_info[1]->onion_key = crypto_pk_dup_key(pk2);
+  memset(d1->intro_point_extend_info[1]->identity_digest, 'b', DIGEST_LEN);
+
+  test_assert(! rend_encode_service_descriptor(d1, 1, pk1, &encoded, &len));
+  d2 = rend_parse_service_descriptor(encoded, len);
+  test_assert(d2);
+
+  test_assert(!crypto_pk_cmp_keys(d1->pk, d2->pk));
+  test_eq(d2->timestamp, now);
+  test_eq(d2->version, 1);
+  test_eq(d2->protocols, 60);
+  test_eq(d2->n_intro_points, 2);
+  test_streq(d2->intro_points[0], d2->intro_point_extend_info[0]->nickname);
+  test_streq(d2->intro_points[1], d2->intro_point_extend_info[1]->nickname);
+  test_eq(d2->intro_point_extend_info[0]->addr, 1234);
+  test_eq(d2->intro_point_extend_info[0]->port, 4567);
+  test_assert(!crypto_pk_cmp_keys(pk1,d2->intro_point_extend_info[0]->onion_key));
+  test_memeq(d2->intro_point_extend_info[0]->identity_digest,
+             d1->intro_point_extend_info[0]->identity_digest, DIGEST_LEN);
+  test_eq(d2->intro_point_extend_info[1]->addr, 6060842);
+  test_eq(d2->intro_point_extend_info[1]->port, 8000);
+
+  test_memeq(d2->intro_point_extend_info[1]->identity_digest,
+             d1->intro_point_extend_info[1]->identity_digest, DIGEST_LEN);
 
   test_eq(BAD_HOSTNAME, parse_extended_hostname(address1));
   test_eq(ONION_HOSTNAME, parse_extended_hostname(address2));
@@ -1432,6 +1491,8 @@
 
   rend_service_descriptor_free(d1);
   rend_service_descriptor_free(d2);
+  crypto_free_pk_env(pk1);
+  crypto_free_pk_env(pk2);
 }
 
 int