[or-cvs] r12272: Patch from Karsten: Code to act as (and use) v2 hidden servi (in tor/trunk: . src/or)

nickm at seul.org nickm at seul.org
Mon Oct 29 19:10:42 UTC 2007


Author: nickm
Date: 2007-10-29 15:10:42 -0400 (Mon, 29 Oct 2007)
New Revision: 12272

Modified:
   tor/trunk/
   tor/trunk/src/or/config.c
   tor/trunk/src/or/directory.c
   tor/trunk/src/or/dirserv.c
   tor/trunk/src/or/networkstatus.c
   tor/trunk/src/or/or.h
   tor/trunk/src/or/rendcommon.c
   tor/trunk/src/or/rendservice.c
   tor/trunk/src/or/router.c
   tor/trunk/src/or/routerlist.c
   tor/trunk/src/or/routerparse.c
Log:
 r16262 at catbus:  nickm | 2007-10-29 13:21:35 -0400
 Patch from Karsten: Code to act as (and use) v2 hidden service directories.



Property changes on: tor/trunk
___________________________________________________________________
 svk:merge ticket from /tor/trunk [r16262] on 8246c3cf-6607-4228-993b-4d95d33730f1

Modified: tor/trunk/src/or/config.c
===================================================================
--- tor/trunk/src/or/config.c	2007-10-29 19:10:38 UTC (rev 12271)
+++ tor/trunk/src/or/config.c	2007-10-29 19:10:42 UTC (rev 12272)
@@ -188,6 +188,7 @@
   V(Group,                       STRING,   NULL),
   V(HardwareAccel,               BOOL,     "0"),
   V(HashedControlPassword,       STRING,   NULL),
+  V(HidServDirectoryV2,          BOOL,     "0"),
   VAR("HiddenServiceDir",    LINELIST_S, RendConfigLines,    NULL),
   VAR("HiddenServiceExcludeNodes", LINELIST_S, RendConfigLines, NULL),
   VAR("HiddenServiceNodes",  LINELIST_S, RendConfigLines,    NULL),
@@ -286,8 +287,12 @@
   VAR("VersioningAuthoritativeDirectory",BOOL,VersioningAuthoritativeDir, "0"),
   V(VirtualAddrNetwork,          STRING,   "127.192.0.0/10"),
   VAR("__AllDirActionsPrivate",  BOOL,  AllDirActionsPrivate,     "0"),
+  VAR("__ConsiderAllRoutersAsHidServDirectories", BOOL,
+      __ConsiderAllRoutersAsHidServDirectories, "0"),
   VAR("__DisablePredictedCircuits",BOOL,DisablePredictedCircuits, "0"),
   VAR("__LeaveStreamsUnattached",BOOL,  LeaveStreamsUnattached,   "0"),
+  VAR("__MinUptimeHidServDirectoryV2", INTERVAL,
+      __MinUptimeHidServDirectoryV2, "24 hours"),
 
   { NULL, CONFIG_TYPE_OBSOLETE, 0, NULL }
 };
@@ -2688,6 +2693,9 @@
     REJECT("HSAuthorityRecordStats is set but we're not running as "
            "a hidden service authority.");
 
+  if (options->HidServDirectoryV2 && !options->DirPort)
+    REJECT("Running as hidden service directory, but no DirPort set.");
+
   if (options->ConnLimit <= 0) {
     r = tor_snprintf(buf, sizeof(buf),
         "ConnLimit must be greater than 0, but was set to %d",
@@ -2820,6 +2828,12 @@
     return -1;
   }
 
+  if (options->__MinUptimeHidServDirectoryV2 < 0) {
+    log_warn(LD_CONFIG, "__MinUptimeHidServDirectoryV2 option must be at "
+                        "least 0 seconds. Changing to 0.");
+    options->__MinUptimeHidServDirectoryV2 = 0;
+  }
+
   if (options->RendPostPeriod < MIN_REND_POST_PERIOD) {
     log(LOG_WARN,LD_CONFIG,"RendPostPeriod option must be at least %d seconds."
         " Clipping.", MIN_REND_POST_PERIOD);

Modified: tor/trunk/src/or/directory.c
===================================================================
--- tor/trunk/src/or/directory.c	2007-10-29 19:10:38 UTC (rev 12271)
+++ tor/trunk/src/or/directory.c	2007-10-29 19:10:42 UTC (rev 12272)
@@ -157,6 +157,10 @@
       return "status vote fetch";
     case DIR_PURPOSE_FETCH_DETACHED_SIGNATURES:
       return "consensus signature fetch";
+    case DIR_PURPOSE_FETCH_RENDDESC_V2:
+      return "hidden-service v2 descriptor fetch";
+    case DIR_PURPOSE_UPLOAD_RENDDESC_V2:
+      return "hidden-service v2 descriptor upload";
     }
 
   log_warn(LD_BUG, "Called with unknown purpose %d", purpose);
@@ -422,7 +426,7 @@
  * upload or download a server or rendezvous
  * descriptor. <b>dir_purpose</b> determines what
  * kind of directory connection we're launching, and must be one of
- * DIR_PURPOSE_{FETCH|UPLOAD}_{DIR|RENDDESC}. <b>router_purpose</b>
+ * DIR_PURPOSE_{FETCH|UPLOAD}_{DIR|RENDDESC|RENDDESC_V2}. <b>router_purpose</b>
  * specifies the descriptor purposes we have in mind (currently only
  * used for FETCH_DIR).
  *
@@ -867,12 +871,27 @@
       url = tor_malloc(len);
       tor_snprintf(url, len, "/tor/rendezvous/%s", resource);
       break;
+    case DIR_PURPOSE_FETCH_RENDDESC_V2:
+      tor_assert(resource);
+      tor_assert(!payload);
+      tor_assert(strlen(resource) <= REND_DESC_ID_V2_BASE32);
+      httpcommand = "GET";
+      len = strlen(resource) + 32;
+      url = tor_malloc(len);
+      tor_snprintf(url, len, "/tor/rendezvous2/%s", resource);
+      break;
     case DIR_PURPOSE_UPLOAD_RENDDESC:
       tor_assert(!resource);
       tor_assert(payload);
       httpcommand = "POST";
       url = tor_strdup("/tor/rendezvous/publish");
       break;
+    case DIR_PURPOSE_UPLOAD_RENDDESC_V2:
+      tor_assert(!resource);
+      tor_assert(payload);
+      httpcommand = "POST";
+      url = tor_strdup("/tor/rendezvous2/publish");
+      break;
     default:
       tor_assert(0);
       return;
@@ -1721,8 +1740,46 @@
     }
   }
 
-  if (conn->_base.purpose == DIR_PURPOSE_UPLOAD_RENDDESC) {
-    log_info(LD_REND,"Uploaded rendezvous descriptor (status %d (%s))",
+  if (conn->_base.purpose == DIR_PURPOSE_FETCH_RENDDESC_V2) {
+    log_info(LD_REND,"Received rendezvous descriptor (size %d, status %d "
+             "(%s))",
+             (int)body_len, status_code, escaped(reason));
+    switch (status_code) {
+      case 200:
+        if (rend_cache_store_v2_client(body, NULL) < 0) {
+          log_warn(LD_REND,"Fetching v2 rendezvous descriptor failed.");
+          /* alice's ap_stream will notice when connection_mark_for_close
+           * cleans it up */
+        } else {
+          /* success. notify pending connections about this. */
+          log_info(LD_REND, "Successfully fetched rendezvous descriptor.");
+          conn->_base.purpose = DIR_PURPOSE_HAS_FETCHED_RENDDESC;
+          rend_client_desc_here(conn->rend_query);
+        }
+        break;
+      case 404:
+        /* not there. pending connections will be notified when
+         * connection_mark_for_close cleans it up. */
+        break;
+      case 400:
+        log_warn(LD_REND, "Fetching v2 rendezvous descriptor failed: "
+                 "http status 400 (%s). Dirserver didn't like our "
+                 "v2 rendezvous query?", escaped(reason));
+        break;
+      default:
+        log_warn(LD_REND, "Fetching v2 rendezvous descriptor failed: "
+                 "http status %d (%s) response unexpected while "
+                 "fetching v2 hidden service descriptor (server '%s:%d').",
+                 status_code, escaped(reason), conn->_base.address,
+                 conn->_base.port);
+        break;
+    }
+  }
+
+  if (conn->_base.purpose == DIR_PURPOSE_UPLOAD_RENDDESC ||
+      conn->_base.purpose == DIR_PURPOSE_UPLOAD_RENDDESC_V2) {
+    log_info(LD_REND,"Uploaded rendezvous descriptor (status %d "
+             "(%s))",
              status_code, escaped(reason));
     switch (status_code) {
       case 200:
@@ -2428,6 +2485,32 @@
     goto done;
   }
 
+  if (options->HidServDirectoryV2 &&
+       !strcmpstart(url,"/tor/rendezvous2/")) {
+    /* Handle v2 rendezvous descriptor fetch request. */
+    char *descp;
+    const char *query = url + strlen("/tor/rendezvous2/");
+    if (strlen(query) == REND_DESC_ID_V2_BASE32) {
+      log_info(LD_REND, "Got a v2 rendezvous descriptor request for ID '%s'",
+               query);
+      switch (rend_cache_lookup_v2_dir(query, &descp)) {
+        case 1: /* valid */
+          write_http_response_header(conn, strlen(descp), 0, 0);
+          connection_write_to_buf(descp, strlen(descp), TO_CONN(conn));
+          break;
+        case 0: /* well-formed but not present */
+          write_http_status_line(conn, 404, "Not found");
+          break;
+        case -1: /* not well-formed */
+          write_http_status_line(conn, 400, "Bad request");
+          break;
+      }
+    } else { /* not well-formed */
+      write_http_status_line(conn, 400, "Bad request");
+    }
+    goto done;
+  }
+
   if (options->HSAuthoritativeDir && !strcmpstart(url,"/tor/rendezvous/")) {
     /* rendezvous descriptor fetch */
     const char *descp;
@@ -2546,6 +2629,27 @@
 
   conn->_base.state = DIR_CONN_STATE_SERVER_WRITING;
 
+  if (parse_http_url(headers, &url) < 0) {
+    write_http_status_line(conn, 400, "Bad request");
+    return 0;
+  }
+  log_debug(LD_DIRSERV,"rewritten url as '%s'.", url);
+
+  /* Handle v2 rendezvous service publish request. */
+  if (options->HidServDirectoryV2 &&
+      !strcmpstart(url,"/tor/rendezvous2/publish")) {
+    if (rend_cache_store_v2_dir(body) < 0) {
+      log_warn(LD_REND, "Rejected rend descriptor (length %d) from %s.",
+             (int)body_len, conn->_base.address);
+      write_http_status_line(conn, 400, "Invalid service descriptor rejected");
+      log_info(LD_REND, "Handled v2 rendezvous descriptor post: rejected");
+    } else {
+      write_http_status_line(conn, 200, "Service descriptor stored");
+      log_info(LD_REND, "Handled v2 rendezvous descriptor post: accepted");
+    }
+    goto done;
+  }
+
   if (!authdir_mode(options)) {
     /* we just provide cached directories; we don't want to
      * receive anything. */
@@ -2554,12 +2658,6 @@
     return 0;
   }
 
-  if (parse_http_url(headers, &url) < 0) {
-    write_http_status_line(conn, 400, "Bad request");
-    return 0;
-  }
-  log_debug(LD_DIRSERV,"rewritten url as '%s'.", url);
-
   if (authdir_mode_handles_descs(options) &&
       !strcmp(url,"/tor/")) { /* server descriptor post */
     const char *msg = NULL;
@@ -2948,3 +3046,105 @@
   return 0;
 }
 
+/** Determine the responsible hidden service directories for
+ * <b>desc_ids</b> and upload the appropriate descriptor from
+ * <b>desc_strs</b> to them; each smartlist must contain
+ * REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS entries; <b>service_id</b> and
+ * <b>seconds_valid</b> are only passed for logging purposes.*/
+/* XXXX020 enable tunneling when available!! */
+void
+directory_post_to_hs_dir(smartlist_t *desc_ids, smartlist_t *desc_strs,
+                         const char *service_id, int seconds_valid,
+                         smartlist_t *hs_dirs_)
+{
+  int i, j;
+  smartlist_t *responsible_dirs;
+  routerinfo_t *hs_dir;
+  if (smartlist_len(desc_ids) != REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS ||
+      smartlist_len(desc_strs) != REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS) {
+    log_warn(LD_REND, "Could not post descriptors to hidden service "
+                      "directories: Illegal number of descriptor "
+                      "IDs/strings");
+    return;
+  }
+  responsible_dirs = smartlist_create();
+  for (i = 0; i < REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS; i++) {
+    const char *desc_id = smartlist_get(desc_ids, i);
+    const char *desc_str = smartlist_get(desc_strs, i);
+    /* Determine responsible dirs. */
+    if (hid_serv_get_responsible_directories(responsible_dirs, desc_id,
+                                             hs_dirs_) < 0) {
+      log_warn(LD_REND, "Could not determine the responsible hidden service "
+                        "directories to post descriptors to.");
+      smartlist_free(responsible_dirs);
+      return;
+    }
+    for (j = 0; j < REND_NUMBER_OF_CONSECUTIVE_REPLICAS; j++) {
+      char desc_id_base32[REND_DESC_ID_V2_BASE32 + 1];
+      hs_dir = smartlist_get(responsible_dirs, j);
+      /* Send publish request. */
+      directory_initiate_command(hs_dir->address, hs_dir->addr,
+                                 hs_dir->or_port, hs_dir->dir_port, 0,
+                                 hs_dir->cache_info.identity_digest,
+                                 DIR_PURPOSE_UPLOAD_RENDDESC_V2,
+                                 ROUTER_PURPOSE_GENERAL,
+                                 1, NULL, desc_str, strlen(desc_str), 0);
+      base32_encode(desc_id_base32, REND_DESC_ID_V2_BASE32 + 1,
+                    desc_id, DIGEST_LEN);
+      log_info(LD_REND, "Sending publish request for v2 descriptor for "
+                        "service '%s' with descriptor ID '%s' with validity "
+                        "of %d seconds to hidden service directory '%s' on "
+                        "port %d.",
+               service_id,
+               desc_id_base32,
+               seconds_valid,
+               hs_dir->nickname,
+               hs_dir->dir_port);
+    }
+    smartlist_clear(responsible_dirs);
+  }
+  smartlist_free(responsible_dirs);
+}
+
+/** Determine the responsible hidden service directories for <b>desc_id</b>
+ * and fetch the descriptor belonging to this ID from one of them;
+ * <b>query</b> is only passed for pretty log statements.
+ * XXXX020 enable tunneling when available!! */
+void
+directory_get_from_hs_dir(const char *desc_id, const char *query,
+                          smartlist_t *hs_dirs_)
+{
+  smartlist_t *responsible_dirs = smartlist_create();
+  routerinfo_t *hs_dir;
+  char desc_id_base32[REND_DESC_ID_V2_BASE32 + 1];
+  int replica;
+  tor_assert(desc_id);
+  tor_assert(query);
+  tor_assert(strlen(query) == REND_SERVICE_ID_LEN);
+  /* Determine responsible dirs. */
+  if (hid_serv_get_responsible_directories(responsible_dirs, desc_id,
+                                           hs_dirs_) < 0) {
+    log_warn(LD_REND, "Could not determine the responsible hidden service "
+                      "directories to fetch descriptors.");
+    smartlist_free(responsible_dirs);
+    return;
+  }
+  replica = crypto_rand_int(REND_NUMBER_OF_CONSECUTIVE_REPLICAS);
+  hs_dir = smartlist_get(responsible_dirs, replica);
+  /* XXXX020 if hsdir fails, use another one... */
+  base32_encode(desc_id_base32, REND_DESC_ID_V2_BASE32 + 1,
+                desc_id, DIGEST_LEN);
+  /* Send fetch request. */
+  directory_initiate_command(hs_dir->address, hs_dir->addr,
+                             hs_dir->or_port, hs_dir->dir_port, 0,
+                             hs_dir->cache_info.identity_digest,
+                             DIR_PURPOSE_FETCH_RENDDESC_V2,
+                             ROUTER_PURPOSE_GENERAL,
+                             1, desc_id_base32, NULL, 0, 0);
+  log_info(LD_REND, "Sending fetch request for v2 descriptor for "
+                    "service '%s' with descriptor ID '%s' to hidden "
+                    "service directory '%s' on port %d.",
+           query, desc_id_base32, hs_dir->nickname, hs_dir->dir_port);
+  smartlist_free(responsible_dirs);
+}
+

Modified: tor/trunk/src/or/dirserv.c
===================================================================
--- tor/trunk/src/or/dirserv.c	2007-10-29 19:10:38 UTC (rev 12271)
+++ tor/trunk/src/or/dirserv.c	2007-10-29 19:10:42 UTC (rev 12272)
@@ -1526,6 +1526,21 @@
   return 0;
 }
 
+/** Return true if <b>router</b> has an uptime of at least
+ * <b>__MinUptimeHidServDirectoryV2</b> and is reachable in the last
+ * REND_HS_DIR_REACHABLE_TIMEOUT seconds, else false.
+ */
+static int
+dirserv_thinks_router_is_hs_dir(routerinfo_t *router, time_t now)
+{
+  int uptime = real_uptime(router, now);
+
+  return (router->wants_to_be_hs_dir &&
+          uptime > get_options()->__MinUptimeHidServDirectoryV2 &&
+          ((router_is_me(router) && !we_are_hibernating()) ||
+           (now < router->last_reachable + REND_HS_DIR_REACHABLE_TIMEOUT)));
+}
+
 /** Look through the routerlist, and assign the median uptime of running valid
  * servers to stable_uptime, and the relative bandwidth capacities to
  * fast_bandwidth and guard_bandwidth.  Set total_bandwidth to the total
@@ -1674,13 +1689,14 @@
     return 0;
   cp = buf + strlen(buf);
   r = tor_snprintf(cp, buf_len - (cp-buf),
-                   "s%s%s%s%s%s%s%s%s%s%s%s\n",
+                   "s%s%s%s%s%s%s%s%s%s%s%s%s\n",
                   /* These must stay in alphabetical order. */
                    rs->is_authority?" Authority":"",
                    rs->is_bad_exit?" BadExit":"",
                    rs->is_exit?" Exit":"",
                    rs->is_fast?" Fast":"",
                    rs->is_possible_guard?" Guard":"",
+                   rs->is_hs_dir?" HSDir":"",
                    rs->is_named?" Named":"",
                    rs->is_running?" Running":"",
                    rs->is_stable?" Stable":"",
@@ -1843,6 +1859,10 @@
     rs->is_possible_guard = 0;
   }
   rs->is_bad_exit = listbadexits && ri->is_bad_exit;
+  ri->is_hs_dir = dirserv_thinks_router_is_hs_dir(ri, now);
+  if (get_options()->__ConsiderAllRoutersAsHidServDirectories)
+    ri->is_hs_dir = 1; /* Override real value. */
+  rs->is_hs_dir = ri->is_hs_dir;
   /* 0.1.1.9-alpha is the first version to support fetch by descriptor
    * hash. */
   rs->is_v2_dir = ri->dir_port &&
@@ -1992,7 +2012,7 @@
   v3_out->server_versions = server_versions;
   v3_out->known_flags = smartlist_create();
   smartlist_split_string(v3_out->known_flags,
-                "Authority Exit Fast Guard Running Stable V2Dir Valid",
+                "Authority Exit Fast Guard HSDir Running Stable V2Dir Valid",
                 0, SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
   if (listbadexits)
     smartlist_add(v3_out->known_flags, tor_strdup("BadExit"));

Modified: tor/trunk/src/or/networkstatus.c
===================================================================
--- tor/trunk/src/or/networkstatus.c	2007-10-29 19:10:38 UTC (rev 12271)
+++ tor/trunk/src/or/networkstatus.c	2007-10-29 19:10:42 UTC (rev 12272)
@@ -1576,6 +1576,7 @@
       router->is_possible_guard = rs->is_possible_guard;
       router->is_exit = rs->is_exit;
       router->is_bad_exit = rs->is_bad_exit;
+      router->is_hs_dir = rs->is_hs_dir;
     }
     if (router->is_running && ds) {
       download_status_reset(&ds->v2_ns_dl_status);

Modified: tor/trunk/src/or/or.h
===================================================================
--- tor/trunk/src/or/or.h	2007-10-29 19:10:38 UTC (rev 12271)
+++ tor/trunk/src/or/or.h	2007-10-29 19:10:42 UTC (rev 12272)
@@ -351,7 +351,13 @@
 
 /** Purpose for connection at a directory server. */
 #define DIR_PURPOSE_SERVER 16
-#define _DIR_PURPOSE_MAX 16
+/** A connection to a hidden service directory server: upload a v2 rendezvous
+ * descriptor. */
+#define DIR_PURPOSE_UPLOAD_RENDDESC_V2 17
+/** A connection to a hidden service directory server: download a v2 rendezvous
+ * descriptor. */
+#define DIR_PURPOSE_FETCH_RENDDESC_V2 18
+#define _DIR_PURPOSE_MAX 18
 
 #define _EXIT_PURPOSE_MIN 1
 /** This exit stream wants to do an ordinary connect. */
@@ -608,6 +614,14 @@
 /** Length of v2 descriptor ID (32 base32 chars = 160 bits). */
 #define REND_DESC_ID_V2_BASE32 32
 
+/** Length of the base32-encoded secret ID part of versioned hidden service
+ * descriptors. */
+#define REND_SECRET_ID_PART_LEN_BASE32 32
+
+/** Length of the base32-encoded hash of an introduction point's
+ * identity key. */
+#define REND_INTRO_POINT_ID_LEN_BASE32 32
+
 #define CELL_DIRECTION_IN 1
 #define CELL_DIRECTION_OUT 2
 
@@ -1197,6 +1211,11 @@
   unsigned int is_exit:1; /**< Do we think this is an OK exit? */
   unsigned int is_bad_exit:1; /**< Do we think this exit is censored, borked,
                                * or otherwise nasty? */
+  unsigned int wants_to_be_hs_dir:1; /**< True iff this router has set a flag
+                                         to possibly act as hidden service
+                                         directory. */
+  unsigned int is_hs_dir:1; /**< True iff this router is a hidden service
+                             * directory. */
 
 /** Tor can use this router for general positions in circuits. */
 #define ROUTER_PURPOSE_GENERAL 0
@@ -1269,6 +1288,8 @@
                                * an exit node. */
   unsigned int is_bad_directory:1; /**< Do we think this directory is junky,
                                     * underpowered, or otherwise useless? */
+  unsigned int is_hs_dir:1; /** True iff this router is a hidden service
+                             * directory. */
   /** True iff we know version info for this router. (i.e., a "v" entry was
    * included.)  We'll replace all these with a big tor_version_t or a char[]
    * if the number of traits we care about ever becomes incredibly big. */
@@ -1998,6 +2019,10 @@
   int PublishHidServDescriptors;
   int FetchServerDescriptors; /**< Do we fetch server descriptors as normal? */
   int FetchHidServDescriptors; /** and hidden service descriptors? */
+  int HidServDirectoryV2; /**< Do we act as hs dir? */
+  int __MinUptimeHidServDirectoryV2; /**< Accept hs dirs after what time? */
+  int __ConsiderAllRoutersAsHidServDirectories; /**< Consider all routers as
+                                                 * hidden service dirs? */
   int FetchUselessDescriptors; /**< Do we fetch non-running descriptors too? */
   int AllDirActionsPrivate; /**< Should every directory action be sent
                              * through a Tor circuit? */
@@ -2849,6 +2874,12 @@
 char *directory_dump_request_log(void);
 int router_supports_extrainfo(const char *identity_digest, int is_authority);
 
+void directory_post_to_hs_dir(smartlist_t *desc_ids, smartlist_t *descs,
+                              const char *service_id, int seconds_valid,
+                              smartlist_t *hs_dirs);
+void directory_get_from_hs_dir(const char *desc_id, const char *query,
+                               smartlist_t *hs_dirs);
+
 time_t download_status_increment_failure(download_status_t *dls,
                                          int status_code, const char *item,
                                          int server, time_t now);
@@ -3422,13 +3453,19 @@
 
 void rend_cache_init(void);
 void rend_cache_clean(void);
+void rend_cache_clean_up(void);
+void rend_cache_clean_v2_dir(void);
 void rend_cache_free_all(void);
 int rend_valid_service_id(const char *query);
 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_lookup_v2_dir(const char *query, char **desc);
 int rend_cache_store(const char *desc, size_t desc_len, int published);
+int rend_cache_store_v2_client(const char *desc,
+                               const char *descriptor_cookie);
+int rend_cache_store_v2_dir(const char *desc);
 int rend_cache_size(void);
 int rend_encode_v2_descriptors(smartlist_t *desc_strs_out,
                                smartlist_t *desc_ids_out,
@@ -3702,6 +3739,18 @@
 const char *esc_router_info(routerinfo_t *router);
 void routers_sort_by_identity(smartlist_t *routers);
 
+smartlist_t *hid_serv_create_routing_table(void);
+int hid_serv_have_enough_directories(smartlist_t *hs_dirs);
+int hid_serv_get_responsible_directories(smartlist_t *responsible_dirs,
+                                         const char *id,
+                                         smartlist_t *hs_dirs);
+routerinfo_t *hid_serv_next_directory(const char *id,
+                                      smartlist_t *hs_dirs);
+routerinfo_t *hid_serv_previous_directory(const char *id,
+                                          smartlist_t *hs_dirs);
+int hid_serv_acting_as_directory(smartlist_t *hs_dirs);
+int hid_serv_responsible_for_desc_id(const char *id, smartlist_t *hs_dirs);
+
 /********************************* routerparse.c ************************/
 
 #define MAX_STATUS_TAG_LEN 32

Modified: tor/trunk/src/or/rendcommon.c
===================================================================
--- tor/trunk/src/or/rendcommon.c	2007-10-29 19:10:38 UTC (rev 12271)
+++ tor/trunk/src/or/rendcommon.c	2007-10-29 19:10:42 UTC (rev 12272)
@@ -60,26 +60,14 @@
 /*XXXX020 Rename to include "len" and maybe not "binary" */
 #define REND_SERVICE_ID_BINARY 10
 
-/** Length of the time period that is used to encode the secret ID part of
- * versioned hidden service descriptors. */
-/*XXXX020 Rename to include "len" and maybe not "binary" */
-#define REND_TIME_PERIOD_BINARY 4
-
 /** Length of the descriptor cookie that is used for versioned hidden
  * service descriptors. */
-/* XXXX020 rename to REND_DESC_COOKIE_(BINARY_)LEN */
-#define REND_DESC_COOKIE_BINARY 16
+#define REND_DESC_COOKIE_LEN 16
 
 /** Length of the replica number that is used to determine the secret ID
  * part of versioned hidden service descriptors. */
-/* XXXX020 rename to REND_REPLICA_(BINARY_)LEN */
-#define REND_REPLICA_BINARY 1
+#define REND_REPLICA_LEN 1
 
-/** Length of the base32-encoded secret ID part of versioned hidden service
- * descriptors. */
-/*XXXX020 Rename to include "len" */
-#define REND_SECRET_ID_PART_BASE32 32
-
 /** Compute the descriptor ID for <b>service_id</b> of length
  * <b>REND_SERVICE_ID_BINARY</b> and <b>secret_id_part</b> of length
  * <b>DIGEST_LEN</b>, and write it to <b>descriptor_id_out</b> of length
@@ -98,7 +86,7 @@
 
 /** Compute the secret ID part for time_period,
  * a <b>descriptor_cookie</b> of length
- * <b>REND_DESC_COOKIE_BINARY</b> which may also be <b>NULL</b> if no
+ * <b>REND_DESC_COOKIE_LEN</b> which may also be <b>NULL</b> if no
  * descriptor_cookie shall be used, and <b>replica</b>, and write it to
  * <b>secret_id_part</b> of length DIGEST_LEN. */
 static void
@@ -110,9 +98,9 @@
   crypto_digest_add_bytes(digest, (char*)&time_period, sizeof(uint32_t));
   if (descriptor_cookie) {
     crypto_digest_add_bytes(digest, descriptor_cookie,
-                            REND_DESC_COOKIE_BINARY);
+                            REND_DESC_COOKIE_LEN);
   }
-  crypto_digest_add_bytes(digest, (const char *)&replica, REND_REPLICA_BINARY);
+  crypto_digest_add_bytes(digest, (const char *)&replica, REND_REPLICA_LEN);
   crypto_digest_get_digest(digest, secret_id_part, DIGEST_LEN);
   crypto_free_digest_env(digest);
 }
@@ -146,7 +134,7 @@
 
 /** Compute the binary <b>desc_id_out</b> (DIGEST_LEN bytes long) for a given
  * base32-encoded <b>service_id</b> and optional unencoded
- * <b>descriptor_cookie</b> of length REND_DESC_COOKIE_BINARY,
+ * <b>descriptor_cookie</b> of length REND_DESC_COOKIE_LEN,
  * at time <b>now</b> for replica number
  * <b>replica</b>. <b>desc_id</b> needs to have <b>DIGEST_LEN</b> bytes
  * free. Return 0 for success, -1 otherwise. */
@@ -188,7 +176,7 @@
 }
 
 /* Encode the introduction points in <b>desc</b>, optionally encrypt them with
- * an optional <b>descriptor_cookie</b> of length REND_DESC_COOKIE_BINARY,
+ * an optional <b>descriptor_cookie</b> of length REND_DESC_COOKIE_LEN,
  * encode it in base64, and write it to a newly allocated string, and write a
  * pointer to it to *<b>ipos_base64</b>. Return 0 for success, -1
  * otherwise. */
@@ -207,7 +195,7 @@
   unenc_len = desc->n_intro_points * 1000; /* too long, but ok. */
   unenc = tor_malloc_zero(unenc_len);
   for (i = 0; i < desc->n_intro_points; i++) {
-    char id_base32[32 + 1]; /*XXXX020 should be a macro */
+    char id_base32[REND_INTRO_POINT_ID_LEN_BASE32 + 1];
     char *onion_key = NULL;
     size_t onion_key_len;
     crypto_pk_env_t *intro_key;
@@ -370,7 +358,7 @@
   /* Encode REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS descriptors. */
   for (k = 0; k < REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS; k++) {
     char secret_id_part[DIGEST_LEN];
-    char secret_id_part_base32[REND_SECRET_ID_PART_BASE32 + 1];
+    char secret_id_part_base32[REND_SECRET_ID_PART_LEN_BASE32 + 1];
     char *desc_id;
     char desc_id_base32[REND_DESC_ID_V2_BASE32 + 1];
     char *permanent_key = NULL;
@@ -387,7 +375,7 @@
     /* Calculate secret-id-part = h(time-period + cookie + replica). */
     get_secret_id_part_bytes(secret_id_part, time_period, descriptor_cookie,
                              k);
-    base32_encode(secret_id_part_base32, REND_SECRET_ID_PART_BASE32 + 1,
+    base32_encode(secret_id_part_base32, REND_SECRET_ID_PART_LEN_BASE32 + 1,
                   secret_id_part, DIGEST_LEN);
     /* Calculate descriptor ID. */
     desc_id = tor_malloc_zero(DIGEST_LEN);
@@ -628,12 +616,17 @@
  * rend_cache_entry_t. */
 static strmap_t *rend_cache = NULL;
 
+/** Map from descriptor id to rend_cache_entry_t; only for hidden service
+ * directories. */
+static digestmap_t *rend_cache_v2_dir = NULL;
+
 /** Initializes the service descriptor cache.
  */
 void
 rend_cache_init(void)
 {
   rend_cache = strmap_new();
+  rend_cache_v2_dir = digestmap_new();
 }
 
 /** Helper: free storage held by a single service descriptor cache entry. */
@@ -651,7 +644,9 @@
 rend_cache_free_all(void)
 {
   strmap_free(rend_cache, _rend_cache_entry_free);
+  digestmap_free(rend_cache_v2_dir, _rend_cache_entry_free);
   rend_cache = NULL;
+  rend_cache_v2_dir = NULL;
 }
 
 /** Removes all old entries from the service descriptor cache.
@@ -677,6 +672,88 @@
   }
 }
 
+/** Remove all old entries on v2 hidden service directories. */
+void
+rend_cache_clean_v2_dir(void)
+{
+  digestmap_iter_t *iter;
+  const char *key;
+  void *val;
+  rend_cache_entry_t *ent;
+  time_t cutoff;
+  cutoff = time(NULL) - REND_CACHE_MAX_AGE - REND_CACHE_MAX_SKEW;
+  for (iter = digestmap_iter_init(rend_cache_v2_dir);
+       !digestmap_iter_done(iter); ) {
+    digestmap_iter_get(iter, &key, &val);
+    ent = (rend_cache_entry_t*)val;
+    if (ent->parsed->timestamp < cutoff) {
+      char key_base32[REND_DESC_ID_V2_BASE32 + 1];
+      base32_encode(key_base32, REND_DESC_ID_V2_BASE32 + 1, key, DIGEST_LEN);
+      log_info(LD_REND, "Removing descriptor with ID '%s' from cache, "
+                        "because it is too old!",
+               key_base32);
+      iter = digestmap_iter_next_rmv(rend_cache_v2_dir, iter);
+      _rend_cache_entry_free(ent);
+    } else {
+      iter = digestmap_iter_next(rend_cache_v2_dir, iter);
+    }
+  }
+}
+
+/** Determines whether <b>a</b> is in the interval of <b>b</b> (excluded) and
+ * <b>c</b> (included) in a circular digest ring; returns 1 if this is the
+ * case, and 0 otherwise.
+ */
+int
+rend_id_is_in_interval(const char *a, const char *b, const char *c)
+{
+  tor_assert(a);
+  tor_assert(b);
+  tor_assert(c);
+  /* There are five cases in which a is outside the interval ]b,c]: */
+  if ((memcmp(a, b, DIGEST_LEN) == 0) || /* 1. a == b (b is excluded) */
+      /* 2. b == c (interval is empty) */
+      (memcmp(b, c, DIGEST_LEN) == 0) ||
+      /* 3. a b c */
+      (memcmp(a, b, DIGEST_LEN) <= 0 && memcmp(b, c, DIGEST_LEN) < 0) ||
+      /* 4. c a b */
+      (memcmp(c, a, DIGEST_LEN) < 0 && memcmp(a, b, DIGEST_LEN) <= 0) ||
+      /* 5. b c a */
+      (memcmp(b, c, DIGEST_LEN) < 0 && memcmp(c, a, DIGEST_LEN) < 0))
+    return 0;
+  /* In the other cases, a is inside the interval. */
+  else
+    return 1;
+}
+
+/** Clean up all values for which this node as hidden service directory is
+ * not responsible */
+void
+rend_cache_clean_up(void)
+{
+  digestmap_iter_t *iter;
+  const char *key;
+  void *val;
+  rend_cache_entry_t *ent;
+  smartlist_t *hs_dirs = hid_serv_create_routing_table();
+  for (iter = digestmap_iter_init(rend_cache_v2_dir);
+       !digestmap_iter_done(iter); ) {
+    digestmap_iter_get(iter, &key, &val);
+    ent = (rend_cache_entry_t*)val;
+    if (!hid_serv_responsible_for_desc_id(key, hs_dirs)) {
+      char key_base32[REND_DESC_ID_V2_BASE32 + 1];
+      base32_encode(key_base32, REND_DESC_ID_V2_BASE32 + 1, key, DIGEST_LEN);
+      log_info(LD_REND, "Removing descriptor with ID '%s' from cache, "
+                        "because we are not reponsible for it!", key_base32);
+      iter = digestmap_iter_next_rmv(rend_cache_v2_dir, iter);
+      _rend_cache_entry_free(ent);
+    } else {
+      iter = digestmap_iter_next(rend_cache_v2_dir, iter);
+    }
+  }
+  smartlist_free(hs_dirs);
+}
+
 /** Return true iff <b>query</b> is a syntactically valid service ID (as
  * generated by rend_get_service_id).  */
 int
@@ -731,6 +808,41 @@
   return 1;
 }
 
+/** Lookup the v2 service descriptor with base32-encoded <b>desc_id</b> and
+ * copy the pointer to it to <b>desc</b>.
+ */
+int
+rend_cache_lookup_v2_dir(const char *desc_id, char **desc)
+{
+  rend_cache_entry_t *e;
+  char desc_id_digest[DIGEST_LEN];
+  smartlist_t *hs_dirs;
+  tor_assert(rend_cache_v2_dir);
+  if (base32_decode(desc_id_digest, DIGEST_LEN,
+                    desc_id, REND_DESC_ID_V2_BASE32) < 0) {
+    log_warn(LD_REND, "Descriptor ID contains illegal characters: %s",
+             desc_id);
+    return -1;
+  }
+  /* Determine if we are responsible. */
+  hs_dirs = hid_serv_create_routing_table();
+  if (hid_serv_responsible_for_desc_id(desc_id_digest, hs_dirs) < 0) {
+    log_info(LD_REND, "Could not answer fetch request for v2 descriptor; "
+                      "either we are no hidden service directory, or we are "
+                      "not responsible for the requested ID.");
+    smartlist_free(hs_dirs);
+    return -1;
+  }
+  smartlist_free(hs_dirs);
+  /* Lookup descriptor and return. */
+  e = (rend_cache_entry_t*) digestmap_get(rend_cache_v2_dir, desc_id_digest);
+  if (e) {
+    *desc = e->desc;
+    return 1;
+  }
+  return 0;
+}
+
 /** Parse *desc, calculate its service id, and store it in the cache.
  * If we have a newer descriptor with the same ID, ignore this one.
  * If we have an older descriptor with the same ID, replace it.
@@ -813,6 +925,218 @@
   return 1;
 }
 
+/** Parse the v2 service descriptor(s) in <b>desc</b> and store it/them to the
+ * local rend cache. Don't attempt to decrypt the included list of introduction
+ * points (as we don't have a descriptor cookie for it).
+ *
+ * If we have a newer descriptor with the same ID, ignore this one.
+ * If we have an older descriptor with the same ID, replace it.
+ * Return -1 if it's malformed or otherwise rejected; return 0 if
+ * it's the same or older than one we've already got; return 1 if
+ * it's novel.
+ */
+int
+rend_cache_store_v2_dir(const char *desc)
+{
+  rend_service_descriptor_t *parsed;
+  char desc_id[DIGEST_LEN];
+  char *intro_content;
+  size_t intro_size;
+  size_t encoded_size;
+  char desc_id_base32[REND_DESC_ID_V2_BASE32 + 1];
+  int number_stored = 0;
+  const char *current_desc = desc;
+  const char *next_desc;
+  rend_cache_entry_t *e;
+  time_t now = time(NULL);
+  smartlist_t *hs_dirs = hid_serv_create_routing_table();
+  tor_assert(rend_cache_v2_dir);
+  tor_assert(desc);
+  if (!hid_serv_acting_as_directory(hs_dirs)) {
+    /* Cannot store descs, because we are (currently) not acting as
+     * hidden service directory. */
+    log_info(LD_REND, "Cannot store descs: Not acting as hs dir");
+    smartlist_free(hs_dirs);
+    return -1;
+  }
+  while (rend_parse_v2_service_descriptor(&parsed, desc_id, &intro_content,
+                                          &intro_size, &encoded_size,
+                                          &next_desc, current_desc) >= 0) {
+    /* We don't care about the introduction points. */
+    tor_free(intro_content);
+    /* For pretty log statements. */
+    base32_encode(desc_id_base32, REND_DESC_ID_V2_BASE32 + 1,
+                  desc_id, DIGEST_LEN);
+    /* Is desc ID in the range that we are (directly or indirectly) responsible
+     * for? */
+    if (!hid_serv_responsible_for_desc_id(desc_id, hs_dirs)) {
+      log_info(LD_REND, "Service descriptor with desc ID %s is not in "
+                        "interval that we are responsible for.",
+               desc_id_base32);
+      rend_service_descriptor_free(parsed);
+      goto skip;
+    }
+    /* Is descriptor too old? */
+    if (parsed->timestamp < now - REND_CACHE_MAX_AGE-REND_CACHE_MAX_SKEW) {
+      log_info(LD_REND, "Service descriptor with desc ID %s is too old.",
+               desc_id_base32);
+      rend_service_descriptor_free(parsed);
+      goto skip;
+    }
+    /* Is descriptor too far in the future? */
+    if (parsed->timestamp > now + REND_CACHE_MAX_SKEW) {
+      log_info(LD_REND, "Service descriptor with desc ID %s is too far in the "
+                        "future.",
+               desc_id_base32);
+      rend_service_descriptor_free(parsed);
+      goto skip;
+    }
+    /* Do we already have a newer descriptor? */
+    e = (rend_cache_entry_t *)digestmap_get(rend_cache_v2_dir, desc_id);
+    if (e && e->parsed->timestamp > parsed->timestamp) {
+      log_info(LD_REND, "We already have a newer service descriptor with the "
+                        "same desc ID %s and version.", desc_id_base32);
+      rend_service_descriptor_free(parsed);
+      goto skip;
+    }
+    /* Do we already have this descriptor? */
+    if (e && !strcmp(desc, e->desc)) {
+      log_info(LD_REND, "We already have this service descriptor with desc "
+                        "ID %s.", desc_id_base32);
+      e->received = time(NULL);
+      rend_service_descriptor_free(parsed);
+      goto skip;
+    }
+    /* Store received descriptor. */
+    if (!e) {
+      e = tor_malloc_zero(sizeof(rend_cache_entry_t));
+      digestmap_set(rend_cache_v2_dir, desc_id, e);
+    } else {
+      rend_service_descriptor_free(e->parsed);
+      tor_free(e->desc);
+    }
+    e->received = time(NULL);
+    e->parsed = parsed;
+    e->desc = tor_malloc(encoded_size + 1);
+    strlcpy(e->desc, current_desc, encoded_size + 1);
+    e->len = encoded_size;
+    log_info(LD_REND, "Successfully stored service descriptor with desc ID "
+                      "'%s' and len %d.", desc_id_base32, encoded_size);
+    number_stored++;
+ skip:
+    /* advance to next descriptor, if available. */
+    current_desc = next_desc;
+    /* check if there is a next descriptor. */
+    if (strncmp(current_desc, "rendezvous-service-descriptor ",
+                strlen("rendezvous-service-descriptor ")))
+      break;
+  }
+  log_info(LD_REND, "Parsed and added %d descriptor%s.",
+           number_stored, number_stored != 1 ? "s" : "");
+  return number_stored;
+}
+
+/** Parse the v2 service descriptor in <b>desc</b>, decrypt the included list
+ * of introduction points with <b>descriptor_cookie</b> (which may also be
+ * <b>NULL</b> if decryption is not necessary), and store the descriptor to
+ * the local cache under its version and service id.
+ *
+ * If we have a newer descriptor with the same ID, ignore this one.
+ * If we have an older descriptor with the same ID, replace it.
+ * Return -1 if it's malformed or otherwise rejected; return 0 if
+ * it's the same or older than one we've already got; return 1 if
+ * it's novel.
+ */
+int
+rend_cache_store_v2_client(const char *desc, const char *descriptor_cookie)
+{
+  rend_service_descriptor_t *parsed = NULL;
+  char desc_id[DIGEST_LEN];
+  char *intro_content = NULL;
+  size_t intro_size;
+  size_t encoded_size;
+  const char *next_desc;
+  time_t now = time(NULL);
+  char key[REND_SERVICE_ID_LEN+2];
+  char service_id[REND_SERVICE_ID_LEN+1];
+  rend_cache_entry_t *e;
+  tor_assert(rend_cache);
+  tor_assert(desc);
+  /* Parse the descriptor. */
+  if (rend_parse_v2_service_descriptor(&parsed, desc_id, &intro_content,
+                                       &intro_size, &encoded_size,
+                                       &next_desc, desc) < 0) {
+    if (parsed) rend_service_descriptor_free(parsed);
+    if (intro_content) tor_free(intro_content);
+    log_warn(LD_REND, "Could not parse descriptor.");
+    return -1;
+  }
+  /* Compute service ID from public key. */
+  if (rend_get_service_id(parsed->pk, service_id)<0) {
+    log_warn(LD_REND, "Couldn't compute service ID.");
+    rend_service_descriptor_free(parsed);
+    tor_free(intro_content);
+    return -1;
+  }
+  /* Decode/decrypt introduction points. */
+  if (rend_decrypt_introduction_points(parsed, descriptor_cookie,
+                                       intro_content, intro_size) < 0) {
+    log_warn(LD_PROTOCOL,"Couldn't decode/decrypt introduction points.");
+    rend_service_descriptor_free(parsed);
+    tor_free(intro_content);
+    return -1;
+  }
+  /* We don't need the encoded/encrypted introduction points any longer. */
+  tor_free(intro_content);
+  /* Is descriptor too old? */
+  if (parsed->timestamp < now - REND_CACHE_MAX_AGE-REND_CACHE_MAX_SKEW) {
+    log_warn(LD_REND, "Service descriptor with service ID %s is too old.",
+             service_id);
+    rend_service_descriptor_free(parsed);
+    return -1;
+  }
+  /* Is descriptor too far in the future? */
+  if (parsed->timestamp > now + REND_CACHE_MAX_SKEW) {
+    log_warn(LD_REND, "Service descriptor with service ID %s is too far in "
+                      "the future.", service_id);
+    rend_service_descriptor_free(parsed);
+    return -1;
+  }
+  /* Do we already have a newer descriptor? */
+  tor_snprintf(key, sizeof(key), "2%s", service_id);
+  e = (rend_cache_entry_t*) strmap_get_lc(rend_cache, key);
+  if (e && e->parsed->timestamp > parsed->timestamp) {
+    log_info(LD_REND, "We already have a newer service descriptor for "
+                      "service ID %s with the same desc ID and version.",
+             service_id);
+    rend_service_descriptor_free(parsed);
+    return 0;
+  }
+  /* Do we already have this descriptor? */
+  if (e && !strcmp(desc, e->desc)) {
+    log_info(LD_REND,"We already have this service descriptor %s.",
+             service_id);
+    e->received = time(NULL);
+    rend_service_descriptor_free(parsed);
+    return 0;
+  }
+  if (!e) {
+    e = tor_malloc_zero(sizeof(rend_cache_entry_t));
+    strmap_set_lc(rend_cache, key, e);
+  } else {
+    rend_service_descriptor_free(e->parsed);
+    tor_free(e->desc);
+  }
+  e->received = time(NULL);
+  e->parsed = parsed;
+  e->desc = tor_malloc_zero(encoded_size + 1);
+  strlcpy(e->desc, desc, encoded_size + 1);
+  e->len = encoded_size;
+  log_debug(LD_REND,"Successfully stored rend desc '%s', len %d.",
+            service_id, encoded_size);
+  return 1;
+}
+
 /** Called when we get a rendezvous-related relay cell on circuit
  * <b>circ</b>.  Dispatch on rendezvous relay command. */
 void

Modified: tor/trunk/src/or/rendservice.c
===================================================================
--- tor/trunk/src/or/rendservice.c	2007-10-29 19:10:38 UTC (rev 12271)
+++ tor/trunk/src/or/rendservice.c	2007-10-29 19:10:42 UTC (rev 12272)
@@ -309,7 +309,8 @@
   d = service->desc = tor_malloc_zero(sizeof(rend_service_descriptor_t));
   d->pk = crypto_pk_dup_key(service->private_key);
   d->timestamp = time(NULL);
-  d->version = 1;
+  d->version = 1; /*< XXXX020 this value is ignored by the
+                   * encode functions; do we need to set it at all? */
   n = smartlist_len(service->intro_nodes);
   d->n_intro_points = 0;
   d->intro_points = tor_malloc_zero(sizeof(char*)*n);

Modified: tor/trunk/src/or/router.c
===================================================================
--- tor/trunk/src/or/router.c	2007-10-29 19:10:38 UTC (rev 12271)
+++ tor/trunk/src/or/router.c	2007-10-29 19:10:42 UTC (rev 12272)
@@ -1577,7 +1577,7 @@
                     "opt extra-info-digest %s\n%s"
                     "onion-key\n%s"
                     "signing-key\n%s"
-                    "%s%s",
+                    "%s%s%s",
     router->nickname,
     router->address,
     router->or_port,
@@ -1593,7 +1593,9 @@
     options->DownloadExtraInfo ? "opt caches-extra-info\n" : "",
     onion_pkey, identity_pkey,
     family_line,
-    we_are_hibernating() ? "opt hibernating 1\n" : "");
+    we_are_hibernating() ? "opt hibernating 1\n" : "",
+    options->HidServDirectoryV2 ? "opt hidden-service-dir\n" : "");
+
   tor_free(family_line);
   tor_free(onion_pkey);
   tor_free(identity_pkey);

Modified: tor/trunk/src/or/routerlist.c
===================================================================
--- tor/trunk/src/or/routerlist.c	2007-10-29 19:10:38 UTC (rev 12271)
+++ tor/trunk/src/or/routerlist.c	2007-10-29 19:10:42 UTC (rev 12272)
@@ -4243,3 +4243,174 @@
   smartlist_sort(routers, _compare_routerinfo_by_id_digest);
 }
 
+/** Return the first router that is acting as hidden service directory and that
+ * has a greater ID than <b>id</b>; if all routers have smaller IDs than
+ * <b>id</b>, return the router with the smallest ID; if the router list is
+ * NULL, or has no elements, return NULL.
+ */
+routerinfo_t *
+hid_serv_next_directory(const char *id, smartlist_t *hs_dirs)
+{
+  int i;
+  if (!hs_dirs) return NULL;
+  if (smartlist_len(hs_dirs) == 0) return NULL;
+  for (i = 0; i < smartlist_len(hs_dirs); i++) {
+    routerinfo_t *router = (routerinfo_t *) smartlist_get(hs_dirs, i);
+    if (memcmp(router->cache_info.identity_digest, id, DIGEST_LEN) > 0) {
+      return router;
+    }
+  }
+  return (routerinfo_t *) smartlist_get(hs_dirs, 0);
+}
+
+/** Return the first router that is acting as hidden service directory and that
+ * has a smaller ID than <b>id</b>; if all routers have greater IDs than
+ * <b>id</b>, return the router with the highest ID; if the router list is
+ * NULL, or has no elements, return NULL.
+ */
+routerinfo_t *
+hid_serv_previous_directory(const char *id, smartlist_t *hs_dirs)
+{
+  int i;
+  if (!hs_dirs) return NULL;
+  if (smartlist_len(hs_dirs) == 0) return NULL;
+  for (i = smartlist_len(hs_dirs) - 1; i >= 0; i--) {
+    routerinfo_t *router = (routerinfo_t *) smartlist_get(hs_dirs, i);
+    if (memcmp(router->cache_info.identity_digest, id, DIGEST_LEN) < 0) {
+      return router;
+    }
+  }
+  return (routerinfo_t *)
+    smartlist_get(hs_dirs, smartlist_len(hs_dirs) - 1);
+}
+
+/** Returns true, if we are aware of enough hidden service directory to
+ * usefully perform v2 rend operations on them (publish, fetch, replicate),
+ * or false otherwise. */
+int
+hid_serv_have_enough_directories(smartlist_t *hs_dirs)
+{
+  return (smartlist_len(hs_dirs) > REND_NUMBER_OF_CONSECUTIVE_REPLICAS);
+}
+
+/** Determine the REND_NUMBER_OF_CONSECUTIVE_REPLICAS routers that are
+ * responsible for <b>id</b> (binary) and add pointers to those routers'
+ * routerstatus_t to <b>responsible_dirs</b>. If we don't have enough
+ * hidden service directories, return -1, else 0. */
+int
+hid_serv_get_responsible_directories(smartlist_t *responsible_dirs,
+                                     const char *id,
+                                     smartlist_t *hs_dirs)
+{
+  const char *digest;
+  int i;
+  routerinfo_t *router;
+  char id_base32[32+1];
+  base32_encode(id_base32, REND_DESC_ID_V2_BASE32 + 1, id, DIGEST_LEN);
+  tor_assert(id);
+  if (!hid_serv_have_enough_directories(hs_dirs)) {
+    log_warn(LD_REND, "We don't have enough hidden service directories to "
+                      "perform v2 rendezvous operations!");
+    return -1;
+  }
+  digest = id;
+  for (i = 0; i < REND_NUMBER_OF_CONSECUTIVE_REPLICAS; i++) {
+    router = hid_serv_next_directory(digest, hs_dirs);
+    digest = router->cache_info.identity_digest;
+    if (!router) {
+      log_warn(LD_REND, "Could not determine next router in "
+                        "hidden service routing table.");
+      return -1;
+    }
+    smartlist_add(responsible_dirs, router);
+  }
+  return 0;
+}
+
+/** Create a list of routerinfo_t in ascending order of identity digests
+ * containing all routers that have been assigned as hidden service
+ * directories by the directory authorities; this list can be used as
+ * hidden service routing table. */
+smartlist_t *
+hid_serv_create_routing_table(void)
+{
+  smartlist_t *hs_dirs = smartlist_create();
+  tor_assert(routerlist);
+  /* Copy the routerinfo_t's of all hidden service directories to a new
+   * smartlist. */
+  SMARTLIST_FOREACH(routerlist->routers, routerinfo_t *, r,
+  {
+    if (r->is_hs_dir)
+      smartlist_add(hs_dirs, r);
+  });
+  routers_sort_by_identity(hs_dirs);
+  return hs_dirs;
+}
+
+/** Return true if this node is currently acting as hidden service
+ * directory, false otherwise. */
+int
+hid_serv_acting_as_directory(smartlist_t *hs_dirs)
+{
+  routerinfo_t *me = routerlist_find_my_routerinfo();
+  int found_me = 0;
+  if (!me) {
+    return 0;
+  }
+  if (!get_options()->HidServDirectoryV2) {
+    log_info(LD_REND, "We are not acting as hidden service directory, "
+                      "because we have not been configured as such.");
+    return 0;
+  }
+  if (!hs_dirs) {
+    /* routing table is NULL */
+    log_info(LD_REND, "We are not acting as hidden service directory, "
+                      "because our own routing table is NULL.");
+    return 0;
+  }
+  SMARTLIST_FOREACH(hs_dirs, routerinfo_t *, router,
+  {
+    if (router_is_me(router))
+      found_me = 1;
+  });
+  if (!found_me) {
+    /* not acting as HS Dir */
+    char me_base32[REND_DESC_ID_V2_BASE32 + 1];
+    base32_encode(me_base32, REND_DESC_ID_V2_BASE32 + 1,
+                  me->cache_info.identity_digest, DIGEST_LEN);
+    log_info(LD_REND, "We are not acting as hidden service directory, "
+                      "because we are not listed as such in our own "
+                      "routing table. me=%s, num entries in RT=%d",
+                      me_base32, smartlist_len(hs_dirs));
+    return 0;
+  }
+  if (smartlist_len(hs_dirs) <= REND_NUMBER_OF_CONSECUTIVE_REPLICAS) {
+    /* too few HS Dirs -- that won't work */
+    log_info(LD_REND, "We are not acting as hidden service directory, "
+                      "because there are too few hidden service "
+                      "directories in the routing table.");
+    return 0;
+  }
+  return 1;
+}
+
+/** Return true if this node is responsible for storing the descriptor ID
+ * in <b>query</b> and false otherwise. */
+int
+hid_serv_responsible_for_desc_id(const char *query, smartlist_t *hs_dirs)
+{
+  const char *me;
+  const char *predecessor;
+  routerinfo_t *router;
+  int i;
+  if (!hid_serv_acting_as_directory(hs_dirs))
+    return 0;
+  me = router_get_my_routerinfo()->cache_info.identity_digest;
+  predecessor = me;
+  for (i = 0; i < REND_NUMBER_OF_CONSECUTIVE_REPLICAS; i++) {
+    router = hid_serv_previous_directory(predecessor, hs_dirs);
+    predecessor = router->cache_info.identity_digest;
+  }
+  return rend_id_is_in_interval(query, predecessor, me);
+}
+

Modified: tor/trunk/src/or/routerparse.c
===================================================================
--- tor/trunk/src/or/routerparse.c	2007-10-29 19:10:38 UTC (rev 12271)
+++ tor/trunk/src/or/routerparse.c	2007-10-29 19:10:42 UTC (rev 12272)
@@ -57,6 +57,7 @@
   K_EXTRA_INFO,
   K_EXTRA_INFO_DIGEST,
   K_CACHES_EXTRA_INFO,
+  K_HIDDEN_SERVICE_DIR,
 
   K_DIR_KEY_CERTIFICATE_VERSION,
   K_DIR_IDENTITY_KEY,
@@ -218,6 +219,7 @@
   T01("read-history",        K_READ_HISTORY,        ARGS,    NO_OBJ ),
   T01("write-history",       K_WRITE_HISTORY,       ARGS,    NO_OBJ ),
   T01("extra-info-digest",   K_EXTRA_INFO_DIGEST,   GE(1),   NO_OBJ ),
+  T01("hidden-service-dir",  K_HIDDEN_SERVICE_DIR,  NO_ARGS, NO_OBJ ),
 
   T01("family",              K_FAMILY,              ARGS,    NO_OBJ ),
   T01("caches-extra-info",   K_CACHES_EXTRA_INFO,   NO_ARGS, NO_OBJ ),
@@ -1255,6 +1257,10 @@
     }
   }
 
+  if ((tok = find_first_by_keyword(tokens, K_HIDDEN_SERVICE_DIR))) {
+    router->wants_to_be_hs_dir = 1;
+  }
+
   tok = find_first_by_keyword(tokens, K_ROUTER_SIGNATURE);
   tor_assert(tok);
   note_crypto_pk_op(VERIFY_RTR);
@@ -1698,6 +1704,8 @@
                consensus_method >= 2) {
         /* Unnamed is computed right by consensus method 2 and later. */
         rs->is_unnamed = 1;
+      } else if (!strcmp(tok->args[i], "HSDir")) {
+        rs->is_hs_dir = 1;
       }
     }
   }
@@ -3172,7 +3180,7 @@
  * *<b>intro_points_encrypted_out</b>, their encrypted size to
  * *<b>intro_points_encrypted_size_out</b>, the size of the encoded descriptor
  * to *<b>encoded_size_out</b>, and a pointer to the possibly next
- * descriptor to *<b>next_now</b>; return 0 for success (including validation)
+ * descriptor to *<b>next_out</b>; return 0 for success (including validation)
  * and -1 for failure.
  */
 int
@@ -3228,15 +3236,13 @@
     log_warn(LD_REND, "Impossibly short descriptor.");
     goto err;
   }
-  /* Check whether descriptor starts correctly. */
   /* Parse base32-encoded descriptor ID. */
   tok = find_first_by_keyword(tokens, R_RENDEZVOUS_SERVICE_DESCRIPTOR);
   tor_assert(tok);
   tor_assert(tok == smartlist_get(tokens, 0));
   tor_assert(tok->n_args == 1);
-  /*XXXX020 magic 32. */
-  if (strlen(tok->args[0]) != 32 ||
-      strspn(tok->args[0], BASE32_CHARS) != 32) {
+  if (strlen(tok->args[0]) != REND_DESC_ID_V2_BASE32 ||
+      strspn(tok->args[0], BASE32_CHARS) != REND_DESC_ID_V2_BASE32) {
     log_warn(LD_REND, "Invalid descriptor ID: '%s'", tok->args[0]);
     goto err;
   }
@@ -3252,6 +3258,8 @@
   tor_assert(tok->n_args == 1);
   result->version = atoi(tok->args[0]);
   if (result->version < 2) { /*XXXX020 what if > 2? */
+                             /* Good question: should higher versions
+                              * be rejected by directories? -KL */
     log_warn(LD_REND, "Wrong descriptor version: %d", result->version);
     goto err;
   }
@@ -3264,9 +3272,8 @@
   tok = find_first_by_keyword(tokens, R_SECRET_ID_PART);
   tor_assert(tok);
   tor_assert(tok->n_args == 1);
-  /* XXXX020 magic 32. */
-  if (strlen(tok->args[0]) != 32 ||
-      strspn(tok->args[0], BASE32_CHARS) != 32) {
+  if (strlen(tok->args[0]) != REND_SECRET_ID_PART_LEN_BASE32 ||
+      strspn(tok->args[0], BASE32_CHARS) != REND_SECRET_ID_PART_LEN_BASE32) {
     log_warn(LD_REND, "Invalid secret ID part: '%s'", tok->args[0]);
     goto err;
   }
@@ -3418,9 +3425,8 @@
     /* Parse identifier. */
     tok = find_first_by_keyword(tokens, R_IPO_IDENTIFIER);
     tor_assert(tok);
-    /* XXXX020 magic 32. */
     if (base32_decode(info->identity_digest, DIGEST_LEN,
-                      tok->args[0], 32) < 0) {
+                      tok->args[0], REND_INTRO_POINT_ID_LEN_BASE32) < 0) {
       log_warn(LD_REND, "Identity digest contains illegal characters: %s",
                tok->args[0]);
       tor_free(info);



More information about the tor-commits mailing list