Просмотр исходного кода

r16300@catbus: nickm | 2007-10-31 15:36:41 -0400
Next patch from Karsten: rename some macros, tunnel dir connections, generate (and upload) multiple descriptors as appropriate.


svn:r12299

Nick Mathewson 17 лет назад
Родитель
Сommit
779b615bc2
7 измененных файлов с 245 добавлено и 64 удалено
  1. 1 0
      src/or/config.c
  2. 12 12
      src/or/directory.c
  3. 15 2
      src/or/or.h
  4. 11 8
      src/or/rendcommon.c
  5. 201 37
      src/or/rendservice.c
  6. 2 2
      src/or/routerlist.c
  7. 3 3
      src/or/routerparse.c

+ 1 - 0
src/or/config.c

@@ -192,6 +192,7 @@ static config_var_t _option_vars[] = {
   VAR("HiddenServiceNodes",  LINELIST_S, RendConfigLines,    NULL),
   VAR("HiddenServiceOptions",LINELIST_V, RendConfigLines,    NULL),
   VAR("HiddenServicePort",   LINELIST_S, RendConfigLines,    NULL),
+  VAR("HiddenServiceVersion",LINELIST_S, RendConfigLines,    NULL),
   V(HSAuthoritativeDir,          BOOL,     "0"),
   V(HSAuthorityRecordStats,      BOOL,     "0"),
   V(HttpProxy,                   STRING,   NULL),

+ 12 - 12
src/or/directory.c

@@ -874,7 +874,7 @@ directory_send_command(dir_connection_t *conn,
     case DIR_PURPOSE_FETCH_RENDDESC_V2:
       tor_assert(resource);
       tor_assert(!payload);
-      tor_assert(strlen(resource) <= REND_DESC_ID_V2_BASE32);
+      tor_assert(strlen(resource) <= REND_DESC_ID_V2_LEN_BASE32);
       httpcommand = "GET";
       len = strlen(resource) + 32;
       url = tor_malloc(len);
@@ -2496,10 +2496,10 @@ directory_handle_command_get(dir_connection_t *conn, const char *headers,
     /* Handle v2 rendezvous descriptor fetch request. */
     const char *descp;
     const char *query = url + strlen("/tor/rendezvous2/");
-    if (strlen(query) == REND_DESC_ID_V2_BASE32) {
+    if (strlen(query) == REND_DESC_ID_V2_LEN_BASE32) {
       log_info(LD_REND, "Got a v2 rendezvous descriptor request for ID '%s'",
                query);
-      switch (rend_cache_lookup_v2_desc(query, &descp)) {
+      switch (rend_cache_lookup_v2_desc_as_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));
@@ -3057,8 +3057,8 @@ dir_split_resource_into_fingerprints(const char *resource,
  * <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!! */
 /* XXXX020 desc_ids and desc_strs could be merged.  Should they? */
+/* I guess they should. -KL */
 void
 directory_post_to_hs_dir(smartlist_t *desc_ids, smartlist_t *desc_strs,
                          const char *service_id, int seconds_valid,
@@ -3089,11 +3089,11 @@ directory_post_to_hs_dir(smartlist_t *desc_ids, smartlist_t *desc_strs,
     tor_assert(smartlist_len(responsible_dirs) ==
                REND_NUMBER_OF_CONSECUTIVE_REPLICAS);
     for (j = 0; j < REND_NUMBER_OF_CONSECUTIVE_REPLICAS; j++) {
-      char desc_id_base32[REND_DESC_ID_V2_BASE32 + 1];
+      char desc_id_base32[REND_DESC_ID_V2_LEN_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->or_port, hs_dir->dir_port, 1,
                                  hs_dir->cache_info.identity_digest,
                                  DIR_PURPOSE_UPLOAD_RENDDESC_V2,
                                  ROUTER_PURPOSE_GENERAL,
@@ -3117,15 +3117,14 @@ directory_post_to_hs_dir(smartlist_t *desc_ids, smartlist_t *desc_strs,
 
 /** 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!! */
+ * <b>query</b> is only passed for pretty log statements. */
 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];
+  char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1];
   tor_assert(desc_id);
   tor_assert(query);
   tor_assert(strlen(query) == REND_SERVICE_ID_LEN);
@@ -3140,15 +3139,16 @@ directory_get_from_hs_dir(const char *desc_id, const char *query,
   hs_dir = smartlist_choose(responsible_dirs);
   smartlist_free(responsible_dirs);
   if (!hs_dir) {
-    /*XXXX020 log. */
+    log_warn(LD_BUG, "Could not pick one of the responsible hidden service "
+                     "directories to fetch descriptors.");
     return;
   }
   /* XXXX020 if hsdir fails, use another one... */
-  base32_encode(desc_id_base32, REND_DESC_ID_V2_BASE32 + 1,
+  base32_encode(desc_id_base32, sizeof(desc_id_base32),
                 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->or_port, hs_dir->dir_port, 1,
                              hs_dir->cache_info.identity_digest,
                              DIR_PURPOSE_FETCH_RENDDESC_V2,
                              ROUTER_PURPOSE_GENERAL,

+ 15 - 2
src/or/or.h

@@ -615,7 +615,7 @@ typedef enum {
 #define REND_NUMBER_OF_CONSECUTIVE_REPLICAS 3
 
 /** Length of v2 descriptor ID (32 base32 chars = 160 bits). */
-#define REND_DESC_ID_V2_BASE32 32
+#define REND_DESC_ID_V2_LEN_BASE32 32
 
 /** Length of the base32-encoded secret ID part of versioned hidden service
  * descriptors. */
@@ -1763,6 +1763,12 @@ typedef struct origin_circuit_t {
    */
   crypt_path_t *cpath;
 
+  /** Stores the rendezvous descriptor version if purpose is S_* to
+   * distinguish introduction and rendezvous points belonging to the same
+   * rendezvous service ID, but different descriptor versions.
+   */
+  uint8_t rend_desc_version;
+
   /** The rend_pk_digest field holds a hash of location-hidden service's
    * PK if purpose is S_ESTABLISH_INTRO or S_RENDEZVOUSING.
    */
@@ -1780,6 +1786,11 @@ typedef struct origin_circuit_t {
    */
   char rend_query[REND_SERVICE_ID_LEN+1];
 
+  /* The intro key replaces the hidden service's public key if purpose is
+   * S_ESTABLISH_INTRO or S_INTRO, provided that no unversioned rendezvous
+   * descriptor is used. */
+  crypto_pk_env_t *intro_key;
+
   /** The next stream_id that will be tried when we're attempting to
    * construct a new AP stream originating at this circuit. */
   uint16_t next_stream_id;
@@ -3470,6 +3481,8 @@ void rend_cache_init(void);
 /*XXXX020 clean *and* clean_up *and* clean_v2_dir? Rename some. */
 /*XXXX020 Call clean_up and clean_v2_dir from somewhere; nothing calls them
  * now. */
+/* Those functions were called from the (removed) replication functionality.
+ * We need to call them from somewhere periodically; main()? -KL */
 void rend_cache_clean(void);
 void rend_cache_clean_up(void);
 void rend_cache_clean_v2_dir(void);
@@ -3479,7 +3492,7 @@ 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_desc(const char *query, const char **desc);
+int rend_cache_lookup_v2_desc_as_dir(const char *query, const char **desc);
 int rend_cache_store(const char *desc, size_t desc_len, int published);
 int rend_cache_store_v2_desc_as_client(const char *desc,
                                const char *descriptor_cookie);

+ 11 - 8
src/or/rendcommon.c

@@ -58,6 +58,8 @@ rend_service_descriptor_free(rend_service_descriptor_t *desc)
 
 /** Length of a binary-encoded rendezvous service ID. */
 /*XXXX020 Rename to include "len" and maybe not "binary" */
+/* Need to change REND_SERVICE_ID_LEN 16 to REND_SERVICE_ID_LEN_BASE32
+ * before! -KL */
 #define REND_SERVICE_ID_BINARY 10
 
 /** Length of the descriptor cookie that is used for versioned hidden
@@ -360,7 +362,7 @@ rend_encode_v2_descriptors(smartlist_t *desc_strs_out,
     char secret_id_part[DIGEST_LEN];
     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 desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1];
     char *permanent_key = NULL;
     size_t permanent_key_len;
     char published[ISO_TIME_LEN+1];
@@ -381,7 +383,7 @@ rend_encode_v2_descriptors(smartlist_t *desc_strs_out,
     desc_id = tor_malloc_zero(DIGEST_LEN);
     rend_get_descriptor_id_bytes(desc_id, service_id, secret_id_part);
     smartlist_add(desc_ids_out, desc_id);
-    base32_encode(desc_id_base32, REND_DESC_ID_V2_BASE32 + 1,
+    base32_encode(desc_id_base32, sizeof(desc_id_base32),
                   desc_id, DIGEST_LEN);
     /* PEM-encode the public key */
     if (crypto_pk_write_public_key_to_string(desc->pk, &permanent_key,
@@ -687,7 +689,7 @@ rend_cache_clean_v2_dir(void)
     digestmap_iter_get(iter, &key, &val);
     ent = val;
     if (ent->parsed->timestamp < cutoff) {
-      char key_base32[REND_DESC_ID_V2_BASE32 + 1];
+      char key_base32[REND_DESC_ID_V2_LEN_BASE32 + 1];
       base32_encode(key_base32, sizeof(key_base32), key, DIGEST_LEN);
       log_info(LD_REND, "Removing descriptor with ID '%s' from cache, "
                         "because it is too old!",
@@ -746,8 +748,9 @@ rend_cache_clean_up(void)
     digestmap_iter_get(iter, &key, &val);
     ent = 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);
+      char key_base32[REND_DESC_ID_V2_LEN_BASE32 + 1];
+      base32_encode(key_base32, sizeof(key_base32),
+                    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);
@@ -818,14 +821,14 @@ rend_cache_lookup_desc(const char *query, int version, const char **desc,
  * well-formed-but-not-found, and -1 on failure.
  */
 int
-rend_cache_lookup_v2_desc(const char *desc_id, const char **desc)
+rend_cache_lookup_v2_desc_as_dir(const char *desc_id, const 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) {
+                    desc_id, REND_DESC_ID_V2_LEN_BASE32) < 0) {
     log_warn(LD_REND, "Descriptor ID contains illegal characters: %s",
              desc_id);
     return -1;
@@ -949,7 +952,7 @@ rend_cache_store_v2_desc_as_dir(const char *desc)
   char *intro_content;
   size_t intro_size;
   size_t encoded_size;
-  char desc_id_base32[REND_DESC_ID_V2_BASE32 + 1];
+  char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1];
   int number_stored = 0;
   const char *current_desc = desc;
   const char *next_desc;

+ 201 - 37
src/or/rendservice.c

@@ -59,6 +59,8 @@ typedef struct rend_service_t {
   rend_service_descriptor_t *desc;
   time_t desc_is_dirty;
   time_t next_upload_time;
+  int descriptor_versions; /**< bitmask of rendezvous descriptor versions
+                            * that will be published. */
 } rend_service_t;
 
 /** A list of rend_service_t's for services run on this OP.
@@ -132,8 +134,32 @@ add_service(rend_service_t *service)
     service->intro_prefer_nodes = tor_strdup("");
   if (!service->intro_exclude_nodes)
     service->intro_exclude_nodes = tor_strdup("");
+  if (service->descriptor_versions == 0)
+    service->descriptor_versions = 1; /* Default is v0 only. */
   service->intro_keys = strmap_new();
 
+  /* If the service is configured to publish unversioned (v0) and versioned
+   * descriptors (v2 or higher), split it up into two separate services. */
+  if (service->descriptor_versions > 1 && service->descriptor_versions & 1) {
+    rend_service_t *v0_service = tor_malloc_zero(sizeof(rend_service_t));
+    v0_service->directory = tor_strdup(service->directory);
+    v0_service->ports = smartlist_create();
+    SMARTLIST_FOREACH(service->ports, rend_service_port_config_t *, p, {
+      rend_service_port_config_t *copy =
+        tor_malloc_zero(sizeof(rend_service_port_config_t));
+      memcpy(copy, p, sizeof(rend_service_port_config_t));
+      smartlist_add(v0_service->ports, copy);
+    });
+    v0_service->intro_nodes = smartlist_create();
+    v0_service->intro_prefer_nodes = tor_strdup(service->intro_prefer_nodes);
+    v0_service->intro_exclude_nodes = tor_strdup(service->intro_exclude_nodes);
+    v0_service->intro_period_started = service->intro_period_started;
+    v0_service->descriptor_versions = 1; /* Unversioned descriptor. */
+    add_service(v0_service);
+
+    service->descriptor_versions -= 1; /* Versioned descriptor. */
+  }
+
   if (!smartlist_len(service->ports)) {
     log_warn(LD_CONFIG, "Hidden service with no ports configured; ignoring.");
     rend_service_free(service);
@@ -248,6 +274,7 @@ rend_config_services(or_options_t *options, int validate_only)
       service->ports = smartlist_create();
       service->intro_nodes = smartlist_create();
       service->intro_period_started = time(NULL);
+      service->descriptor_versions = 0;
       continue;
     }
     if (!service) {
@@ -271,8 +298,7 @@ rend_config_services(or_options_t *options, int validate_only)
         return -1;
       }
       service->intro_prefer_nodes = tor_strdup(line->value);
-    } else {
-      tor_assert(!strcasecmp(line->key, "HiddenServiceExcludeNodes"));
+    } else if (!strcasecmp(line->key, "HiddenServiceExcludeNodes")) {
       if (service->intro_exclude_nodes) {
         log_warn(LD_CONFIG,
                  "Got multiple HiddenServiceExcludedNodes lines for "
@@ -280,6 +306,24 @@ rend_config_services(or_options_t *options, int validate_only)
         return -1;
       }
       service->intro_exclude_nodes = tor_strdup(line->value);
+    } else {
+      smartlist_t *versions;
+      char *version_str;
+      int i, version;
+      tor_assert(!strcasecmp(line->key, "HiddenServiceVersion"));
+      versions = smartlist_create();
+      smartlist_split_string(versions, line->value, ",",
+                             SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
+      for (i = 0; i < smartlist_len(versions); i++) {
+        version_str = smartlist_get(versions, i);
+        if (strlen(version_str) != 1 || strspn(version_str, "02") != 1) {
+          log_warn(LD_CONFIG,
+                   "HiddenServiceVersion can only be 0 and/or 2.");
+          return -1;
+        }
+        version = atoi(version_str);
+        service->descriptor_versions |= 1 << version;
+      }
     }
   }
   if (service) {
@@ -410,14 +454,17 @@ rend_service_load_keys(void)
   return 0;
 }
 
-/** Return the service whose public key has a digest of <b>digest</b>. Return
- * NULL if no such service exists.
+/** Return the service whose public key has a digest of <b>digest</b>
+ * and which publishes descriptors of the given <b>versions</b> bitmask.
+ * Return NULL if no such service exists.
  */
 static rend_service_t *
-rend_service_get_by_pk_digest(const char* digest)
+rend_service_get_by_pk_digest_and_version(const char* digest,
+                                          uint8_t versions)
 {
   SMARTLIST_FOREACH(rend_service_list, rend_service_t*, s,
-                    if (!memcmp(s->pk_digest,digest,DIGEST_LEN)) return s);
+                    if (!memcmp(s->pk_digest,digest,DIGEST_LEN) &&
+                        s->descriptor_versions == versions) return s);
   return NULL;
 }
 
@@ -464,6 +511,8 @@ rend_service_introduce(origin_circuit_t *circuit, const char *request,
   char hexcookie[9];
   int circ_needs_uptime;
   int reason = END_CIRC_REASON_TORPROTOCOL;
+  crypto_pk_env_t *intro_key;
+  char intro_key_digest[DIGEST_LEN];
 
   base32_encode(serviceid, REND_SERVICE_ID_LEN+1,
                 circuit->rend_pk_digest,10);
@@ -485,21 +534,32 @@ rend_service_introduce(origin_circuit_t *circuit, const char *request,
     return -1;
   }
 
-  /* first DIGEST_LEN bytes of request is service pk digest */
-  service = rend_service_get_by_pk_digest(request);
+  /* look up service depending on circuit. */
+  service = rend_service_get_by_pk_digest_and_version(
+              circuit->rend_pk_digest, circuit->rend_desc_version);
   if (!service) {
     log_warn(LD_REND, "Got an INTRODUCE2 cell for an unrecognized service %s.",
              escaped(serviceid));
     return -1;
   }
-  if (memcmp(circuit->rend_pk_digest, request, DIGEST_LEN)) {
+
+  /* if descriptor is versioned, use intro key instead of service key. */
+  if (circuit->rend_desc_version & 1) {
+    intro_key = service->private_key;
+  } else {
+    intro_key = circuit->intro_key;
+  }
+
+  /* first DIGEST_LEN bytes of request is intro or service pk digest */
+  crypto_pk_get_digest(intro_key, intro_key_digest);
+  if (memcmp(intro_key_digest, request, DIGEST_LEN)) {
     base32_encode(serviceid, REND_SERVICE_ID_LEN+1, request, 10);
     log_warn(LD_REND, "Got an INTRODUCE2 cell for the wrong service (%s).",
              escaped(serviceid));
     return -1;
   }
 
-  keylen = crypto_pk_keysize(service->private_key);
+  keylen = crypto_pk_keysize(intro_key);
   if (request_len < keylen+DIGEST_LEN) {
     log_warn(LD_PROTOCOL,
              "PK-encrypted portion of INTRODUCE2 cell was truncated.");
@@ -508,7 +568,7 @@ rend_service_introduce(origin_circuit_t *circuit, const char *request,
   /* Next N bytes is encrypted with service key */
   note_crypto_pk_op(REND_SERVER);
   r = crypto_pk_private_hybrid_decrypt(
-       service->private_key,buf,request+DIGEST_LEN,request_len-DIGEST_LEN,
+       intro_key,buf,request+DIGEST_LEN,request_len-DIGEST_LEN,
        PK_PKCS1_OAEP_PADDING,1);
   if (r<0) {
     log_warn(LD_PROTOCOL, "Couldn't decrypt INTRODUCE2 cell.");
@@ -641,6 +701,7 @@ rend_service_introduce(origin_circuit_t *circuit, const char *request,
   memcpy(launched->rend_cookie, r_cookie, REND_COOKIE_LEN);
   strlcpy(launched->rend_query, service->service_id,
           sizeof(launched->rend_query));
+  launched->rend_desc_version = service->descriptor_versions;
   launched->build_state->pending_final_cpath = cpath =
     tor_malloc_zero(sizeof(crypt_path_t));
   cpath->magic = CRYPT_PATH_MAGIC;
@@ -715,6 +776,7 @@ rend_service_relaunch_rendezvous(origin_circuit_t *oldcirc)
          DIGEST_LEN);
   memcpy(newcirc->rend_cookie, oldcirc->rend_cookie,
          REND_COOKIE_LEN);
+  newcirc->rend_desc_version = oldcirc->rend_desc_version;
 }
 
 /** Launch a circuit to serve as an introduction point for the service
@@ -744,7 +806,12 @@ rend_service_launch_establish_intro(rend_service_t *service,
   strlcpy(launched->rend_query, service->service_id,
           sizeof(launched->rend_query));
   memcpy(launched->rend_pk_digest, service->pk_digest, DIGEST_LEN);
-
+  launched->rend_desc_version = service->descriptor_versions;
+  if (!(service->descriptor_versions & 1)) {
+    launched->intro_key = crypto_new_pk_env();
+    tor_assert(!crypto_pk_generate_key(launched->intro_key));
+    strmap_set(service->intro_keys, nickname, launched->intro_key);
+  }
   if (launched->_base.state == CIRCUIT_STATE_OPEN)
     rend_service_intro_has_opened(launched);
   return 0;
@@ -763,6 +830,7 @@ rend_service_intro_has_opened(origin_circuit_t *circuit)
   char auth[DIGEST_LEN + 9];
   char serviceid[REND_SERVICE_ID_LEN+1];
   int reason = END_CIRC_REASON_TORPROTOCOL;
+  crypto_pk_env_t *intro_key;
 
   tor_assert(circuit->_base.purpose == CIRCUIT_PURPOSE_S_ESTABLISH_INTRO);
   tor_assert(circuit->cpath);
@@ -770,7 +838,8 @@ rend_service_intro_has_opened(origin_circuit_t *circuit)
   base32_encode(serviceid, REND_SERVICE_ID_LEN+1,
                 circuit->rend_pk_digest,10);
 
-  service = rend_service_get_by_pk_digest(circuit->rend_pk_digest);
+  service = rend_service_get_by_pk_digest_and_version(
+              circuit->rend_pk_digest, circuit->rend_desc_version);
   if (!service) {
     log_warn(LD_REND, "Unrecognized service ID %s on introduction circuit %d.",
              serviceid, circuit->_base.n_circ_id);
@@ -782,8 +851,15 @@ rend_service_intro_has_opened(origin_circuit_t *circuit)
            "Established circuit %d as introduction point for service %s",
            circuit->_base.n_circ_id, serviceid);
 
+  /* If the introduction point will not be used in an unversioned
+   * descriptor, use the intro key instead of the service key in
+   * ESTABLISH_INTRO. */
+  if (service->descriptor_versions & 1)
+    intro_key = service->private_key;
+  else
+    intro_key = circuit->intro_key;
   /* Build the payload for a RELAY_ESTABLISH_INTRO cell. */
-  len = crypto_pk_asn1_encode(service->private_key, buf+2,
+  len = crypto_pk_asn1_encode(intro_key, buf+2,
                               RELAY_PAYLOAD_SIZE-2);
   set_uint16(buf, htons((uint16_t)len));
   len += 2;
@@ -793,7 +869,7 @@ rend_service_intro_has_opened(origin_circuit_t *circuit)
     goto err;
   len += 20;
   note_crypto_pk_op(REND_SERVER);
-  r = crypto_pk_private_sign_digest(service->private_key, buf+len, buf, len);
+  r = crypto_pk_private_sign_digest(intro_key, buf+len, buf, len);
   if (r<0) {
     log_warn(LD_BUG, "Internal error: couldn't sign introduction request.");
     reason = END_CIRC_REASON_INTERNAL;
@@ -833,7 +909,8 @@ rend_service_intro_established(origin_circuit_t *circuit, const char *request,
              "received INTRO_ESTABLISHED cell on non-intro circuit.");
     goto err;
   }
-  service = rend_service_get_by_pk_digest(circuit->rend_pk_digest);
+  service = rend_service_get_by_pk_digest_and_version(
+              circuit->rend_pk_digest, circuit->rend_desc_version);
   if (!service) {
     log_warn(LD_REND, "Unknown service on introduction circuit %d.",
              circuit->_base.n_circ_id);
@@ -882,7 +959,8 @@ rend_service_rendezvous_has_opened(origin_circuit_t *circuit)
            "cookie %s for service %s",
            circuit->_base.n_circ_id, hexcookie, serviceid);
 
-  service = rend_service_get_by_pk_digest(circuit->rend_pk_digest);
+  service = rend_service_get_by_pk_digest_and_version(
+              circuit->rend_pk_digest, circuit->rend_desc_version);
   if (!service) {
     log_warn(LD_GENERAL, "Internal error: unrecognized service ID on "
              "introduction circuit.");
@@ -966,35 +1044,120 @@ find_intro_circuit(routerinfo_t *router, const char *pk_digest)
   return NULL;
 }
 
-/** Encode and sign an up-to-date service descriptor for <b>service</b>,
- * and upload it to all the dirservers.
+/** Encode and sign up-to-date v0 and/or v2 service descriptors for
+ * <b>service</b>, and upload it/them to all the dirservers/to the
+ * responsible hidden service directories.
  */
 static void
 upload_service_descriptor(rend_service_t *service)
 {
-  char *desc;
-  size_t desc_len;
+  time_t now = time(NULL);
+  int rendpostperiod;
   char serviceid[REND_SERVICE_ID_LEN+1];
+  int uploaded = 0;
 
   /* Update the descriptor. */
   rend_service_update_descriptor(service);
-  if (rend_encode_service_descriptor(service->desc,
-                                     service->private_key,
-                                     &desc, &desc_len)<0) {
-    log_warn(LD_BUG, "Internal error: couldn't encode service descriptor; "
-             "not uploading.");
-    return;
-  }
 
-  /* Post it to the dirservers */
-  rend_get_service_id(service->desc->pk, serviceid);
-  log_info(LD_REND, "Sending publish request for hidden service %s",
+  rendpostperiod = get_options()->RendPostPeriod;
+
+  /* Upload unversioned (v0) descriptor? */
+  if (service->descriptor_versions & 1 &&
+      get_options()->PublishHidServDescriptors) {
+    char *desc;
+    size_t desc_len;
+    /* Encode the descriptor. */
+    if (rend_encode_service_descriptor(service->desc,
+                                       service->private_key,
+                                       &desc, &desc_len)<0) {
+      log_warn(LD_BUG, "Internal error: couldn't encode service descriptor; "
+               "not uploading.");
+      return;
+    }
+
+    /* Post it to the dirservers */
+    rend_get_service_id(service->desc->pk, serviceid);
+    log_info(LD_REND, "Sending publish request for hidden service %s",
              serviceid);
-  directory_post_to_dirservers(DIR_PURPOSE_UPLOAD_RENDDESC,
-                               ROUTER_PURPOSE_GENERAL,
-                               HIDSERV_AUTHORITY, desc, desc_len, 0);
-  tor_free(desc);
+    directory_post_to_dirservers(DIR_PURPOSE_UPLOAD_RENDDESC,
+                                 ROUTER_PURPOSE_GENERAL,
+                                 HIDSERV_AUTHORITY, desc, desc_len, 0);
+    tor_free(desc);
+    service->next_upload_time = now + rendpostperiod;
+    uploaded = 1;
+  }
+
+  /* Upload v2 descriptor? */
+  if (service->descriptor_versions & (1 << 2) &&
+      get_options()->PublishHidServDescriptors) {
+    smartlist_t *hs_dirs = hid_serv_create_routing_table();
+    if (hid_serv_have_enough_directories(hs_dirs)) {
+      int seconds_valid;
+      smartlist_t *desc_strs = smartlist_create();
+      smartlist_t *desc_ids = smartlist_create();
+      int i;
+      /* Encode the current descriptor. */
+      seconds_valid = rend_encode_v2_descriptors(desc_strs, desc_ids,
+                      service->desc, now, NULL, 0);
+      if (seconds_valid < 0) {
+        log_warn(LD_BUG, "Internal error: couldn't encode service descriptor; "
+                 "not uploading.");
+        smartlist_free(hs_dirs);
+        return;
+      }
+      /* Post the current descriptors to the hidden service directories. */
+      rend_get_service_id(service->desc->pk, serviceid);
+      log_info(LD_REND, "Sending publish request for hidden service %s",
+                   serviceid);
+      directory_post_to_hs_dir(desc_ids, desc_strs, serviceid, seconds_valid,
+                               hs_dirs);
+      /* Free memory for descriptors. */
+      for (i = 0; i < REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS; i++) {
+        tor_free(smartlist_get(desc_strs, i));
+        tor_free(smartlist_get(desc_ids, i));
+      }
+      smartlist_clear(desc_strs);
+      smartlist_clear(desc_ids);
+      /* Update next upload time. */
+      if (seconds_valid - REND_TIME_PERIOD_OVERLAPPING_V2_DESCS
+          > rendpostperiod)
+        service->next_upload_time = now + rendpostperiod;
+      else if (seconds_valid < REND_TIME_PERIOD_OVERLAPPING_V2_DESCS)
+        service->next_upload_time = now + seconds_valid + 1;
+      else
+        service->next_upload_time = now + seconds_valid -
+            REND_TIME_PERIOD_OVERLAPPING_V2_DESCS + 1;
+      /* Post also the next descriptors, if necessary. */
+      if (seconds_valid < REND_TIME_PERIOD_OVERLAPPING_V2_DESCS) {
+        seconds_valid = rend_encode_v2_descriptors(desc_strs, desc_ids,
+                service->desc, now, NULL, 1);
+        if (seconds_valid < 0) {
+          log_warn(LD_BUG, "Internal error: couldn't encode service "
+                   "descriptor; not uploading.");
+          smartlist_free(hs_dirs);
+          return;
+        }
+        directory_post_to_hs_dir(desc_ids, desc_strs, serviceid,
+                                 seconds_valid, hs_dirs);
+        /* Free memory for descriptors. */
+        for (i = 0; i < REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS; i++) {
+          tor_free(smartlist_get(desc_strs, i));
+          tor_free(smartlist_get(desc_ids, i));
+        }
+        smartlist_free(desc_strs);
+        smartlist_free(desc_ids);
+      }
+      smartlist_free(hs_dirs);
+      uploaded = 1;
+      log_info(LD_REND, "Successfully uploaded v2 rend descriptors!");
+    }
+  }
+
+  /* If not uploaded, try again in one minute. */
+  if (!uploaded)
+    service->next_upload_time = now + 60;
 
+  /* Unmark dirty flag of this service. */
   service->desc_is_dirty = 0;
 }
 
@@ -1043,6 +1206,7 @@ rend_services_introduce(void)
         log_info(LD_REND,"Giving up on %s as intro point for %s.",
                  intro, service->service_id);
         tor_free(intro);
+        /* XXXX020 We could also remove the intro key here... */
         smartlist_del(service->intro_nodes,j--);
         changed = 1;
         service->desc_is_dirty = now;
@@ -1143,7 +1307,6 @@ rend_consider_services_upload(time_t now)
        * descriptor and ours has been stable for 30 seconds, upload a
        * new one of each format. */
       upload_service_descriptor(service);
-      service->next_upload_time = now + rendpostperiod;
     }
   }
 }
@@ -1209,7 +1372,8 @@ rend_service_set_connection_addr_port(edge_connection_t *conn,
   log_debug(LD_REND,"beginning to hunt for addr/port");
   base32_encode(serviceid, REND_SERVICE_ID_LEN+1,
                 circ->rend_pk_digest,10);
-  service = rend_service_get_by_pk_digest(circ->rend_pk_digest);
+  service = rend_service_get_by_pk_digest_and_version(circ->rend_pk_digest,
+                                                      circ->rend_desc_version);
   if (!service) {
     log_warn(LD_REND, "Couldn't find any service associated with pk %s on "
              "rendezvous circuit %d; closing.",

+ 2 - 2
src/or/routerlist.c

@@ -4305,7 +4305,7 @@ hid_serv_get_responsible_directories(smartlist_t *responsible_dirs,
   const char *digest;
   int i;
   routerinfo_t *router;
-  char id_base32[REND_DESC_ID_V2_BASE32+1];
+  char id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1];
   tor_assert(id);
   base32_encode(id_base32, sizeof(id_base32), id, DIGEST_LEN);
   if (!hid_serv_have_enough_directories(hs_dirs)) {
@@ -4379,7 +4379,7 @@ hid_serv_acting_as_directory(const smartlist_t *hs_dirs)
   });
   if (!found_me) {
     /* not acting as HS Dir */
-    char me_base32[REND_DESC_ID_V2_BASE32 + 1];
+    char me_base32[REND_DESC_ID_V2_LEN_BASE32 + 1];
     base32_encode(me_base32, sizeof(me_base32),
                   me->cache_info.identity_digest, DIGEST_LEN);
     log_info(LD_REND, "We are not acting as hidden service directory, "

+ 3 - 3
src/or/routerparse.c

@@ -3241,13 +3241,13 @@ rend_parse_v2_service_descriptor(rend_service_descriptor_t **parsed_out,
   tor_assert(tok);
   tor_assert(tok == smartlist_get(tokens, 0));
   tor_assert(tok->n_args == 1);
-  if (strlen(tok->args[0]) != REND_DESC_ID_V2_BASE32 ||
-      strspn(tok->args[0], BASE32_CHARS) != REND_DESC_ID_V2_BASE32) {
+  if (strlen(tok->args[0]) != REND_DESC_ID_V2_LEN_BASE32 ||
+      strspn(tok->args[0], BASE32_CHARS) != REND_DESC_ID_V2_LEN_BASE32) {
     log_warn(LD_REND, "Invalid descriptor ID: '%s'", tok->args[0]);
     goto err;
   }
   if (base32_decode(desc_id_out, DIGEST_LEN,
-                    tok->args[0], REND_DESC_ID_V2_BASE32) < 0) {
+                    tok->args[0], REND_DESC_ID_V2_LEN_BASE32) < 0) {
     log_warn(LD_REND, "Descriptor ID contains illegal characters: %s",
              tok->args[0]);
     goto err;