瀏覽代碼

Merge remote-tracking branch 'origin/maint-0.2.4'

Conflicts:
	src/common/crypto.c
Nick Mathewson 11 年之前
父節點
當前提交
da30adcf0f
共有 14 個文件被更改,包括 492 次插入418 次删除
  1. 5 0
      changes/less_charbuf_usage
  2. 23 0
      src/common/crypto.c
  3. 4 0
      src/common/crypto.h
  4. 60 93
      src/or/dirserv.c
  5. 2 23
      src/or/dirserv.h
  6. 65 135
      src/or/dirvote.c
  7. 1 3
      src/or/networkstatus.c
  8. 2 1
      src/or/or.h
  9. 59 89
      src/or/router.c
  10. 2 2
      src/or/router.h
  11. 50 24
      src/or/routerparse.c
  12. 3 2
      src/or/routerparse.h
  13. 2 1
      src/test/test.c
  14. 214 45
      src/test/test_dir.c

+ 5 - 0
changes/less_charbuf_usage

@@ -0,0 +1,5 @@
+  o Code simplification and refactoring:
+    - Avoid using character buffers when constructing most directory
+      objects: this approach was unweildy and error-prone.  Instead,
+      build smartlists of strings, and concatenate them when done.
+

+ 23 - 0
src/common/crypto.c

@@ -1614,6 +1614,29 @@ crypto_digest_assign(crypto_digest_t *into,
   memcpy(into,from,sizeof(crypto_digest_t));
 }
 
+/** Given a list of strings in <b>lst</b>, set the <b>len_out</b>-byte digest
+ * at <b>digest_out</b> to the hash of the concatenation of those strings,
+ * plus the optional string <b>append</b>, computed with the algorithm
+ * <b>alg</b>.
+ * <b>out_len</b> must be \<= DIGEST256_LEN. */
+void
+crypto_digest_smartlist(char *digest_out, size_t len_out,
+                        const smartlist_t *lst, const char *append,
+                        digest_algorithm_t alg)
+{
+  crypto_digest_t *d;
+  if (alg == DIGEST_SHA1)
+    d = crypto_digest_new();
+  else
+    d = crypto_digest256_new(alg);
+  SMARTLIST_FOREACH(lst, const char *, cp,
+                    crypto_digest_add_bytes(d, cp, strlen(cp)));
+  if (append)
+    crypto_digest_add_bytes(d, append, strlen(append));
+  crypto_digest_get_digest(d, digest_out, len_out);
+  crypto_digest_free(d);
+}
+
 /** Compute the HMAC-SHA-256 of the <b>msg_len</b> bytes in <b>msg</b>, using
  * the <b>key</b> of length <b>key_len</b>.  Store the DIGEST256_LEN-byte
  * result in <b>hmac_out</b>.

+ 4 - 0
src/common/crypto.h

@@ -205,6 +205,10 @@ int crypto_digest(char *digest, const char *m, size_t len);
 int crypto_digest256(char *digest, const char *m, size_t len,
                      digest_algorithm_t algorithm);
 int crypto_digest_all(digests_t *ds_out, const char *m, size_t len);
+struct smartlist_t;
+void crypto_digest_smartlist(char *digest_out, size_t len_out,
+                             const struct smartlist_t *lst, const char *append,
+                             digest_algorithm_t alg);
 const char *crypto_digest_algorithm_get_name(digest_algorithm_t alg);
 int crypto_digest_algorithm_parse_name(const char *name);
 crypto_digest_t *crypto_digest_new(void);

+ 60 - 93
src/or/dirserv.c

@@ -2311,11 +2311,10 @@ version_from_platform(const char *platform)
   return NULL;
 }
 
-/** Helper: write the router-status information in <b>rs</b> into <b>buf</b>,
- * which has at least <b>buf_len</b> free characters.  Do NUL-termination.
- * Use the same format as in network-status documents.  If <b>version</b> is
- * non-NULL, add a "v" line for the platform.  Return 0 on success, -1 on
- * failure.
+/** Helper: write the router-status information in <b>rs</b> into a newly
+ * allocated character buffer.  Use the same format as in network-status
+ * documents.  If <b>version</b> is non-NULL, add a "v" line for the platform.
+ * Return 0 on success, -1 on failure.
  *
  * The format argument has one of the following values:
  *   NS_V2 - Output an entry suitable for a V2 NS opinion document
@@ -2326,25 +2325,25 @@ version_from_platform(const char *platform)
  *        it contains additional information for the vote.
  *   NS_CONTROL_PORT - Output a NS document for the control port
  */
-int
-routerstatus_format_entry(char *buf, size_t buf_len,
-                          const routerstatus_t *rs, const char *version,
+char *
+routerstatus_format_entry(const routerstatus_t *rs, const char *version,
                           routerstatus_format_type_t format,
                           const vote_routerstatus_t *vrs)
 {
-  int r;
-  char *cp;
   char *summary;
+  char *result = NULL;
 
   char published[ISO_TIME_LEN+1];
   char identity64[BASE64_DIGEST_LEN+1];
   char digest64[BASE64_DIGEST_LEN+1];
+  smartlist_t *chunks = NULL;
 
   format_iso_time(published, rs->published_on);
   digest_to_base64(identity64, rs->identity_digest);
   digest_to_base64(digest64, rs->descriptor_digest);
 
-  r = tor_snprintf(buf, buf_len,
+  chunks = smartlist_new();
+  smartlist_add_asprintf(chunks,
                    "r %s %s %s%s%s %s %d %d\n",
                    rs->nickname,
                    identity64,
@@ -2354,11 +2353,6 @@ routerstatus_format_entry(char *buf, size_t buf_len,
                    fmt_addr32(rs->addr),
                    (int)rs->or_port,
                    (int)rs->dir_port);
-  if (r<0) {
-    log_warn(LD_BUG, "Not enough space in buffer.");
-    return -1;
-  }
-  cp = buf + strlen(buf);
 
   /* TODO: Maybe we want to pass in what we need to build the rest of
    * this here, instead of in the caller. Then we could use the
@@ -2367,25 +2361,18 @@ routerstatus_format_entry(char *buf, size_t buf_len,
 
   /* V3 microdesc consensuses don't have "a" lines. */
   if (format == NS_V3_CONSENSUS_MICRODESC)
-    return 0;
+    goto done;
 
   /* Possible "a" line. At most one for now. */
   if (!tor_addr_is_null(&rs->ipv6_addr)) {
-    r = tor_snprintf(cp, buf_len - (cp-buf),
-                     "a %s\n",
-                     fmt_addrport(&rs->ipv6_addr, rs->ipv6_orport));
-    if (r<0) {
-      log_warn(LD_BUG, "Not enough space in buffer.");
-      return -1;
-    }
-    cp += strlen(cp);
+    smartlist_add_asprintf(chunks, "a %s\n",
+                           fmt_addrport(&rs->ipv6_addr, rs->ipv6_orport));
   }
 
   if (format == NS_V3_CONSENSUS)
-    return 0;
+    goto done;
 
-  /* NOTE: Whenever this list expands, be sure to increase MAX_FLAG_LINE_LEN*/
-  r = tor_snprintf(cp, buf_len - (cp-buf),
+  smartlist_add_asprintf(chunks,
                    "s%s%s%s%s%s%s%s%s%s%s%s%s%s\n",
                   /* These must stay in alphabetical order. */
                    rs->is_authority?" Authority":"",
@@ -2401,20 +2388,11 @@ routerstatus_format_entry(char *buf, size_t buf_len,
                    rs->is_unnamed?" Unnamed":"",
                    rs->is_v2_dir?" V2Dir":"",
                    rs->is_valid?" Valid":"");
-  if (r<0) {
-    log_warn(LD_BUG, "Not enough space in buffer.");
-    return -1;
-  }
-  cp += strlen(cp);
 
   /* length of "opt v \n" */
 #define V_LINE_OVERHEAD 7
   if (version && strlen(version) < MAX_V_LINE_LEN - V_LINE_OVERHEAD) {
-    if (tor_snprintf(cp, buf_len - (cp-buf), "v %s\n", version)<0) {
-      log_warn(LD_BUG, "Unable to print router version.");
-      return -1;
-    }
-    cp += strlen(cp);
+    smartlist_add_asprintf(chunks, "v %s\n", version);
   }
 
   if (format != NS_V2) {
@@ -2434,7 +2412,7 @@ routerstatus_format_entry(char *buf, size_t buf_len,
         log_warn(LD_BUG, "Cannot get any descriptor for %s "
             "(wanted descriptor %s).",
             id, dd);
-        return -1;
+        goto err;
       }
 
       /* This assert could fire for the control port, because
@@ -2469,39 +2447,32 @@ routerstatus_format_entry(char *buf, size_t buf_len,
       tor_assert(desc);
       bw_kb = router_get_advertised_bandwidth_capped(desc) / 1000;
     }
-    r = tor_snprintf(cp, buf_len - (cp-buf),
-                     "w Bandwidth=%d\n", bw_kb);
+    smartlist_add_asprintf(chunks,
+                     "w Bandwidth=%d", bw_kb);
 
-    if (r<0) {
-      log_warn(LD_BUG, "Not enough space in buffer.");
-      return -1;
-    }
-    cp += strlen(cp);
     if (format == NS_V3_VOTE && vrs && vrs->has_measured_bw) {
-      *--cp = '\0'; /* Kill "\n" */
-      r = tor_snprintf(cp, buf_len - (cp-buf),
-                       " Measured=%d\n", vrs->measured_bw_kb);
-      if (r<0) {
-        log_warn(LD_BUG, "Not enough space in buffer for weight line.");
-        return -1;
-      }
-      cp += strlen(cp);
+      smartlist_add_asprintf(chunks,
+                       " Measured=%d", vrs->measured_bw_kb);
     }
+    smartlist_add(chunks, tor_strdup("\n"));
 
     if (desc) {
       summary = policy_summarize(desc->exit_policy, AF_INET);
-      r = tor_snprintf(cp, buf_len - (cp-buf), "p %s\n", summary);
-      if (r<0) {
-        log_warn(LD_BUG, "Not enough space in buffer.");
-        tor_free(summary);
-        return -1;
-      }
-      cp += strlen(cp);
+      smartlist_add_asprintf(chunks, "p %s\n", summary);
       tor_free(summary);
     }
   }
 
-  return 0;
+ done:
+  result = smartlist_join_strings(chunks, "", 0, NULL);
+
+ err:
+  if (chunks) {
+    SMARTLIST_FOREACH(chunks, char *, cp, tor_free(cp));
+    smartlist_free(chunks);
+  }
+
+  return result;
 }
 
 /** Helper for sorting: compares two routerinfos first by address, and then by
@@ -3191,14 +3162,13 @@ dirserv_generate_networkstatus_vote_obj(crypto_pk_t *private_key,
 /** For v2 authoritative directories only: Replace the contents of
  * <b>the_v2_networkstatus</b> with a newly generated network status
  * object.  */
-static cached_dir_t *
+cached_dir_t *
 generate_v2_networkstatus_opinion(void)
 {
   cached_dir_t *r = NULL;
-  size_t len, identity_pkey_len;
+  size_t identity_pkey_len;
   char *status = NULL, *client_versions = NULL, *server_versions = NULL,
     *identity_pkey = NULL, *hostname = NULL;
-  char *outp, *endp;
   const or_options_t *options = get_options();
   char fingerprint[FINGERPRINT_LEN+1];
   char published[ISO_TIME_LEN+1];
@@ -3217,6 +3187,7 @@ generate_v2_networkstatus_opinion(void)
   char *version_lines = NULL;
   smartlist_t *routers = NULL;
   digestmap_t *omit_as_sybil = NULL;
+  smartlist_t *chunks = NULL;
 
   private_key = get_server_identity_key();
 
@@ -3255,12 +3226,8 @@ generate_v2_networkstatus_opinion(void)
     version_lines = tor_strdup("");
   }
 
-  len = 4096+strlen(client_versions)+strlen(server_versions);
-  len += identity_pkey_len*2;
-  len += (RS_ENTRY_LEN)*smartlist_len(rl->routers);
-
-  status = tor_malloc(len);
-  tor_snprintf(status, len,
+  chunks = smartlist_new();
+  smartlist_add_asprintf(chunks,
                "network-status-version 2\n"
                "dir-source %s %s %d\n"
                "fingerprint %s\n"
@@ -3280,8 +3247,6 @@ generate_v2_networkstatus_opinion(void)
                versioning ? " Versions" : "",
                version_lines,
                identity_pkey);
-  outp = status + strlen(status);
-  endp = status + len;
 
   /* precompute this part, since we need it to decide what "stable"
    * means. */
@@ -3312,35 +3277,33 @@ generate_v2_networkstatus_opinion(void)
       if (digestmap_get(omit_as_sybil, ri->cache_info.identity_digest))
         clear_status_flags_on_sybil(&rs);
 
-      if (routerstatus_format_entry(outp, endp-outp, &rs, version, NS_V2,
-                                    NULL)) {
-        log_warn(LD_BUG, "Unable to print router status.");
-        tor_free(version);
-        goto done;
+      {
+        char *rsf = routerstatus_format_entry(&rs, version, NS_V2, NULL);
+        if (rsf)
+          smartlist_add(chunks, rsf);
       }
       tor_free(version);
-      outp += strlen(outp);
     }
   } SMARTLIST_FOREACH_END(ri);
 
-  if (tor_snprintf(outp, endp-outp, "directory-signature %s\n",
-                   options->Nickname)<0) {
-    log_warn(LD_BUG, "Unable to write signature line.");
-    goto done;
-  }
-  if (router_get_networkstatus_v2_hash(status, digest)<0) {
-    log_warn(LD_BUG, "Unable to hash network status");
-    goto done;
-  }
-  outp += strlen(outp);
+  smartlist_add_asprintf(chunks, "directory-signature %s\n",
+                         options->Nickname);
+
+  crypto_digest_smartlist(digest, DIGEST_LEN, chunks, "", DIGEST_SHA1);
 
   note_crypto_pk_op(SIGN_DIR);
-  if (router_append_dirobj_signature(outp,endp-outp,digest,DIGEST_LEN,
-                                     private_key)<0) {
-    log_warn(LD_BUG, "Unable to sign router status.");
-    goto done;
+  {
+    char *sig;
+    if (!(sig = router_get_dirobj_signature(digest,DIGEST_LEN,
+                                            private_key))) {
+      log_warn(LD_BUG, "Unable to sign router status.");
+      goto done;
+    }
+    smartlist_add(chunks, sig);
   }
 
+  status = smartlist_join_strings(chunks, "", 0, NULL);
+
   {
     networkstatus_v2_t *ns;
     if (!(ns = networkstatus_v2_parse_from_string(status))) {
@@ -3362,6 +3325,10 @@ generate_v2_networkstatus_opinion(void)
   }
 
  done:
+  if (chunks) {
+    SMARTLIST_FOREACH(chunks, char *, cp, tor_free(cp));
+    smartlist_free(chunks);
+  }
   tor_free(client_versions);
   tor_free(server_versions);
   tor_free(version_lines);

+ 2 - 23
src/or/dirserv.h

@@ -29,28 +29,6 @@
 
 /** Maximum allowable length of a version line in a networkstatus. */
 #define MAX_V_LINE_LEN 128
-/** Length of "r Authority BadDirectory BadExit Exit Fast Guard HSDir Named
- * Running Stable Unnamed V2Dir Valid\n". */
-#define MAX_FLAG_LINE_LEN 96
-/** Length of "w" line for weighting.  Currently at most
- * "w Bandwidth=<uint32t> Measured=<uint32t>\n" */
-#define MAX_WEIGHT_LINE_LEN (12+10+10+10+1)
-/** Maximum length of an exit policy summary line. */
-#define MAX_POLICY_LINE_LEN (3+MAX_EXITPOLICY_SUMMARY_LEN)
-/** Amount of space to allocate for each entry: r, s, and v lines. */
-#define RS_ENTRY_LEN                                                    \
-  ( /* first line */                                                    \
-   MAX_NICKNAME_LEN+BASE64_DIGEST_LEN*2+ISO_TIME_LEN+INET_NTOA_BUF_LEN+ \
-   5*2 /* ports */ + 10 /* punctuation */ +                             \
-   /* second line */                                                    \
-   MAX_FLAG_LINE_LEN +                                                  \
-   /* weight line */                                                    \
-   MAX_WEIGHT_LINE_LEN +                                                \
-   /* p line. */                                                        \
-   MAX_POLICY_LINE_LEN +                                                \
-   /* v line. */                                                        \
-   MAX_V_LINE_LEN                                                       \
-   )
 
 int connection_dirserv_flushed_some(dir_connection_t *conn);
 
@@ -128,7 +106,7 @@ size_t dirserv_estimate_data_size(smartlist_t *fps, int is_serverdescs,
                                   int compressed);
 size_t dirserv_estimate_microdesc_size(const smartlist_t *fps, int compressed);
 
-int routerstatus_format_entry(char *buf, size_t buf_len,
+char *routerstatus_format_entry(
                               const routerstatus_t *rs, const char *platform,
                               routerstatus_format_type_t format,
                               const vote_routerstatus_t *vrs);
@@ -154,6 +132,7 @@ int dirserv_get_measured_bw_cache_size(void);
 int dirserv_query_measured_bw_cache_kb(const char *node_id, long *bw_out,
                                        time_t *as_of_out);
 int dirserv_has_measured_bw(const char *node_id);
+cached_dir_t *generate_v2_networkstatus_opinion(void);
 #endif
 
 int dirserv_read_measured_bandwidths(const char *from_file,

+ 65 - 135
src/or/dirvote.c

@@ -57,9 +57,6 @@ static char *make_consensus_method_list(int low, int high, const char *sep);
  * Voting
  * =====*/
 
-/* Overestimated. */
-#define MICRODESC_LINE_LEN 80
-
 /** Return a new string containing the string representation of the vote in
  * <b>v3_ns</b>, signed with our v3 signing key <b>private_signing_key</b>.
  * For v3 authorities. */
@@ -67,17 +64,14 @@ char *
 format_networkstatus_vote(crypto_pk_t *private_signing_key,
                           networkstatus_t *v3_ns)
 {
-  size_t len;
-  char *status = NULL;
+  smartlist_t *chunks;
   const char *client_versions = NULL, *server_versions = NULL;
-  char *outp, *endp;
   char fingerprint[FINGERPRINT_LEN+1];
   char digest[DIGEST_LEN];
   uint32_t addr;
-  routerlist_t *rl = router_get_routerlist();
-  char *version_lines = NULL;
-  int r;
+  char *client_versions_line = NULL, *server_versions_line = NULL;
   networkstatus_voter_info_t *voter;
+  char *status = NULL;
 
   tor_assert(private_signing_key);
   tor_assert(v3_ns->type == NS_TYPE_VOTE || v3_ns->type == NS_TYPE_OPINION);
@@ -91,43 +85,20 @@ format_networkstatus_vote(crypto_pk_t *private_signing_key,
   client_versions = v3_ns->client_versions;
   server_versions = v3_ns->server_versions;
 
-  if (client_versions || server_versions) {
-    size_t v_len = 64;
-    char *cp;
-    if (client_versions)
-      v_len += strlen(client_versions);
-    if (server_versions)
-      v_len += strlen(server_versions);
-    version_lines = tor_malloc(v_len);
-    cp = version_lines;
-    if (client_versions) {
-      r = tor_snprintf(cp, v_len-(cp-version_lines),
-                   "client-versions %s\n", client_versions);
-      if (r < 0) {
-        log_err(LD_BUG, "Insufficient memory for client-versions line");
-        tor_assert(0);
-      }
-      cp += strlen(cp);
-    }
-    if (server_versions) {
-      r = tor_snprintf(cp, v_len-(cp-version_lines),
-                   "server-versions %s\n", server_versions);
-      if (r < 0) {
-        log_err(LD_BUG, "Insufficient memory for server-versions line");
-        tor_assert(0);
-      }
-    }
+  if (client_versions) {
+    tor_asprintf(&client_versions_line, "client-versions %s\n",
+                 client_versions);
   } else {
-    version_lines = tor_strdup("");
+    client_versions_line = tor_strdup("");
+  }
+  if (server_versions) {
+    tor_asprintf(&server_versions_line, "server-versions %s\n",
+                 server_versions);
+  } else {
+    server_versions_line = tor_strdup("");
   }
 
-  len = 8192;
-  len += strlen(version_lines);
-  len += (RS_ENTRY_LEN+MICRODESC_LINE_LEN)*smartlist_len(rl->routers);
-  len += strlen("\ndirectory-footer\n");
-  len += v3_ns->cert->cache_info.signed_descriptor_len;
-
-  status = tor_malloc(len);
+  chunks = smartlist_new();
   {
     char published[ISO_TIME_LEN+1];
     char va[ISO_TIME_LEN+1];
@@ -151,7 +122,7 @@ format_networkstatus_vote(crypto_pk_t *private_signing_key,
       params = tor_strdup("");
 
     tor_assert(cert);
-    r = tor_snprintf(status, len,
+    smartlist_add_asprintf(chunks,
                  "network-status-version 3\n"
                  "vote-status %s\n"
                  "consensus-methods %s\n"
@@ -160,7 +131,7 @@ format_networkstatus_vote(crypto_pk_t *private_signing_key,
                  "fresh-until %s\n"
                  "valid-until %s\n"
                  "voting-delay %d %d\n"
-                 "%s" /* versions */
+                 "%s%s" /* versions */
                  "known-flags %s\n"
                  "flag-thresholds %s\n"
                  "params %s\n"
@@ -170,7 +141,8 @@ format_networkstatus_vote(crypto_pk_t *private_signing_key,
                  methods,
                  published, va, fu, vu,
                  v3_ns->vote_seconds, v3_ns->dist_seconds,
-                 version_lines,
+                 client_versions_line,
+                 server_versions_line,
                  flags,
                  flag_thresholds,
                  params,
@@ -178,93 +150,67 @@ format_networkstatus_vote(crypto_pk_t *private_signing_key,
                  fmt_addr32(addr), voter->dir_port, voter->or_port,
                  voter->contact);
 
-    if (r < 0) {
-      log_err(LD_BUG, "Insufficient memory for network status line");
-      tor_assert(0);
-    }
-
     tor_free(params);
     tor_free(flags);
     tor_free(flag_thresholds);
     tor_free(methods);
-    outp = status + strlen(status);
-    endp = status + len;
 
     if (!tor_digest_is_zero(voter->legacy_id_digest)) {
       char fpbuf[HEX_DIGEST_LEN+1];
       base16_encode(fpbuf, sizeof(fpbuf), voter->legacy_id_digest, DIGEST_LEN);
-      r = tor_snprintf(outp, endp-outp, "legacy-dir-key %s\n", fpbuf);
-      if (r < 0) {
-        log_err(LD_BUG, "Insufficient memory for legacy-dir-key line");
-        tor_assert(0);
-      }
-      outp += strlen(outp);
+      smartlist_add_asprintf(chunks, "legacy-dir-key %s\n", fpbuf);
     }
 
-    tor_assert(outp + cert->cache_info.signed_descriptor_len < endp);
-    memcpy(outp, cert->cache_info.signed_descriptor_body,
-           cert->cache_info.signed_descriptor_len);
-
-    outp += cert->cache_info.signed_descriptor_len;
+    smartlist_add(chunks, tor_strndup(cert->cache_info.signed_descriptor_body,
+                                      cert->cache_info.signed_descriptor_len));
   }
 
   SMARTLIST_FOREACH_BEGIN(v3_ns->routerstatus_list, vote_routerstatus_t *,
                           vrs) {
+    char *rsf;
     vote_microdesc_hash_t *h;
-    if (routerstatus_format_entry(outp, endp-outp, &vrs->status,
-                                  vrs->version, NS_V3_VOTE, vrs) < 0) {
-      log_warn(LD_BUG, "Unable to print router status.");
-      goto err;
-    }
-    outp += strlen(outp);
+    rsf = routerstatus_format_entry(&vrs->status,
+                                    vrs->version, NS_V3_VOTE, vrs);
+    if (rsf)
+      smartlist_add(chunks, rsf);
 
     for (h = vrs->microdesc; h; h = h->next) {
-      size_t mlen = strlen(h->microdesc_hash_line);
-      if (outp+mlen >= endp) {
-        log_warn(LD_BUG, "Can't fit microdesc line in vote.");
-      }
-      memcpy(outp, h->microdesc_hash_line, mlen+1);
-      outp += strlen(outp);
+      smartlist_add(chunks, tor_strdup(h->microdesc_hash_line));
     }
   } SMARTLIST_FOREACH_END(vrs);
 
-  r = tor_snprintf(outp, endp-outp, "directory-footer\n");
-  if (r < 0) {
-    log_err(LD_BUG, "Insufficient memory for directory-footer line");
-    tor_assert(0);
-  }
-  outp += strlen(outp);
+  smartlist_add(chunks, tor_strdup("directory-footer\n"));
+
+  /* The digest includes everything up through the space after
+   * directory-signature.  (Yuck.) */
+  crypto_digest_smartlist(digest, DIGEST_LEN, chunks,
+                          "directory-signature ", DIGEST_SHA1);
 
   {
     char signing_key_fingerprint[FINGERPRINT_LEN+1];
-    if (tor_snprintf(outp, endp-outp, "directory-signature ")<0) {
-      log_warn(LD_BUG, "Unable to start signature line.");
-      goto err;
-    }
-    outp += strlen(outp);
-
     if (crypto_pk_get_fingerprint(private_signing_key,
                                   signing_key_fingerprint, 0)<0) {
       log_warn(LD_BUG, "Unable to get fingerprint for signing key");
       goto err;
     }
-    if (tor_snprintf(outp, endp-outp, "%s %s\n", fingerprint,
-                     signing_key_fingerprint)<0) {
-      log_warn(LD_BUG, "Unable to end signature line.");
-      goto err;
-    }
-    outp += strlen(outp);
+
+    smartlist_add_asprintf(chunks, "directory-signature %s %s\n", fingerprint,
+                           signing_key_fingerprint);
   }
 
-  if (router_get_networkstatus_v3_hash(status, digest, DIGEST_SHA1)<0)
-    goto err;
   note_crypto_pk_op(SIGN_DIR);
-  if (router_append_dirobj_signature(outp,endp-outp,digest, DIGEST_LEN,
-                                     private_signing_key)<0) {
-    log_warn(LD_BUG, "Unable to sign networkstatus vote.");
-    goto err;
+  {
+    char *sig = router_get_dirobj_signature(digest, DIGEST_LEN,
+                                            private_signing_key);
+    if (!sig) {
+      log_warn(LD_BUG, "Unable to sign networkstatus vote.");
+      goto err;
+    }
+    smartlist_add(chunks, sig);
   }
 
+  status = smartlist_join_strings(chunks, "", 0, NULL);
+
   {
     networkstatus_t *v;
     if (!(v = networkstatus_parse_vote_from_string(status, NULL,
@@ -282,7 +228,12 @@ format_networkstatus_vote(crypto_pk_t *private_signing_key,
  err:
   tor_free(status);
  done:
-  tor_free(version_lines);
+  tor_free(client_versions_line);
+  tor_free(server_versions_line);
+  if (chunks) {
+    SMARTLIST_FOREACH(chunks, char *, cp, tor_free(cp));
+    smartlist_free(chunks);
+  }
   return status;
 }
 
@@ -526,24 +477,6 @@ compute_routerstatus_consensus(smartlist_t *votes, int consensus_method,
   return most;
 }
 
-/** Given a list of strings in <b>lst</b>, set the <b>len_out</b>-byte digest
- * at <b>digest_out</b> to the hash of the concatenation of those strings,
- * computed with the algorithm <b>alg</b>. */
-static void
-hash_list_members(char *digest_out, size_t len_out,
-                  smartlist_t *lst, digest_algorithm_t alg)
-{
-  crypto_digest_t *d;
-  if (alg == DIGEST_SHA1)
-    d = crypto_digest_new();
-  else
-    d = crypto_digest256_new(alg);
-  SMARTLIST_FOREACH(lst, const char *, cp,
-                    crypto_digest_add_bytes(d, cp, strlen(cp)));
-  crypto_digest_get_digest(d, digest_out, len_out);
-  crypto_digest_free(d);
-}
-
 /** Sorting helper: compare two strings based on their values as base-ten
  * positive integers. (Non-integers are treated as prior to all integers, and
  * compared lexically.) */
@@ -2027,12 +1960,12 @@ networkstatus_compute_consensus(smartlist_t *votes,
       }
 
       {
-        char buf[4096];
+        char *buf;
         /* Okay!! Now we can write the descriptor... */
         /*     First line goes into "buf". */
-        routerstatus_format_entry(buf, sizeof(buf), &rs_out, NULL,
-                                  rs_format, NULL);
-        smartlist_add(chunks, tor_strdup(buf));
+        buf = routerstatus_format_entry(&rs_out, NULL, rs_format, NULL);
+        if (buf)
+          smartlist_add(chunks, buf);
       }
       /*     Now an m line, if applicable. */
       if (flavor == FLAV_MICRODESC &&
@@ -2144,12 +2077,12 @@ networkstatus_compute_consensus(smartlist_t *votes,
     size_t digest_len =
       flavor == FLAV_NS ? DIGEST_LEN : DIGEST256_LEN;
     const char *algname = crypto_digest_algorithm_get_name(digest_alg);
-    char sigbuf[4096];
+    char *signature;
 
     smartlist_add(chunks, tor_strdup("directory-signature "));
 
     /* Compute the hash of the chunks. */
-    hash_list_members(digest, digest_len, chunks, digest_alg);
+    crypto_digest_smartlist(digest, digest_len, chunks, "", digest_alg);
 
     /* Get the fingerprints */
     crypto_pk_get_fingerprint(identity_key, fingerprint, 0);
@@ -2165,14 +2098,12 @@ networkstatus_compute_consensus(smartlist_t *votes,
                    signing_key_fingerprint);
     }
     /* And the signature. */
-    sigbuf[0] = '\0';
-    if (router_append_dirobj_signature(sigbuf, sizeof(sigbuf),
-                                       digest, digest_len,
-                                       signing_key)) {
+    if (!(signature = router_get_dirobj_signature(digest, digest_len,
+                                                  signing_key))) {
       log_warn(LD_BUG, "Couldn't sign consensus networkstatus.");
       goto done;
     }
-    smartlist_add(chunks, tor_strdup(sigbuf));
+    smartlist_add(chunks, signature);
 
     if (legacy_id_key_digest && legacy_signing_key && consensus_method >= 3) {
       smartlist_add(chunks, tor_strdup("directory-signature "));
@@ -2188,14 +2119,13 @@ networkstatus_compute_consensus(smartlist_t *votes,
                      algname, fingerprint,
                      signing_key_fingerprint);
       }
-      sigbuf[0] = '\0';
-      if (router_append_dirobj_signature(sigbuf, sizeof(sigbuf),
-                                         digest, digest_len,
-                                         legacy_signing_key)) {
+
+      if (!(signature = router_get_dirobj_signature(digest, digest_len,
+                                                    legacy_signing_key))) {
         log_warn(LD_BUG, "Couldn't sign consensus networkstatus.");
         goto done;
       }
-      smartlist_add(chunks, tor_strdup(sigbuf));
+      smartlist_add(chunks, signature);
     }
   }
 

+ 1 - 3
src/or/networkstatus.c

@@ -2115,9 +2115,7 @@ signed_descs_update_status_from_consensus_networkstatus(smartlist_t *descs)
 char *
 networkstatus_getinfo_helper_single(const routerstatus_t *rs)
 {
-  char buf[RS_ENTRY_LEN+1];
-  routerstatus_format_entry(buf, sizeof(buf), rs, NULL, NS_CONTROL_PORT, NULL);
-  return tor_strdup(buf);
+  return routerstatus_format_entry(rs, NULL, NS_CONTROL_PORT, NULL);
 }
 
 /** Alloc and return a string describing routerstatuses for the most

+ 2 - 1
src/or/or.h

@@ -2331,7 +2331,8 @@ typedef struct networkstatus_v2_t {
 typedef struct vote_microdesc_hash_t {
   /** Next element in the list, or NULL. */
   struct vote_microdesc_hash_t *next;
-  /** The raw contents of the microdesc hash line, excluding the "m". */
+  /** The raw contents of the microdesc hash line, from the "m" through the
+   * newline. */
   char *microdesc_hash_line;
 } vote_microdesc_hash_t;
 

+ 59 - 89
src/or/router.c

@@ -1944,9 +1944,8 @@ router_rebuild_descriptor(int force)
     /* ri was allocated with tor_malloc_zero, so there is no need to
      * zero ri->cache_info.extra_info_digest here. */
   }
-  ri->cache_info.signed_descriptor_body = tor_malloc(8192);
-  if (router_dump_router_to_string(ri->cache_info.signed_descriptor_body, 8192,
-                                   ri, get_server_identity_key()) < 0) {
+  if (! (ri->cache_info.signed_descriptor_body = router_dump_router_to_string(
+                                           ri, get_server_identity_key()))) {
     log_warn(LD_BUG, "Couldn't generate router descriptor.");
     routerinfo_free(ri);
     extrainfo_free(ei);
@@ -2246,54 +2245,54 @@ get_platform_str(char *platform, size_t len)
 #define DEBUG_ROUTER_DUMP_ROUTER_TO_STRING
 
 /** OR only: Given a routerinfo for this router, and an identity key to sign
- * with, encode the routerinfo as a signed server descriptor and write the
- * result into <b>s</b>, using at most <b>maxlen</b> bytes.  Return -1 on
- * failure, and the number of bytes used on success.
+ * with, encode the routerinfo as a signed server descriptor and return a new
+ * string encoding the result, or NULL on failure.
  */
-int
-router_dump_router_to_string(char *s, size_t maxlen, routerinfo_t *router,
+char *
+router_dump_router_to_string(routerinfo_t *router,
                              crypto_pk_t *ident_key)
 {
-  char *onion_pkey; /* Onion key, PEM-encoded. */
-  char *identity_pkey; /* Identity key, PEM-encoded. */
+  /* XXXX025 Make this look entirely at its arguments, and not at globals.
+   */
+  char *onion_pkey = NULL; /* Onion key, PEM-encoded. */
+  char *identity_pkey = NULL; /* Identity key, PEM-encoded. */
   char digest[DIGEST_LEN];
   char published[ISO_TIME_LEN+1];
   char fingerprint[FINGERPRINT_LEN+1];
   int has_extra_info_digest;
   char extra_info_digest[HEX_DIGEST_LEN+1];
   size_t onion_pkeylen, identity_pkeylen;
-  size_t written;
-  int result=0;
-  char *family_line;
+  char *family_line = NULL;
   char *extra_or_address = NULL;
   const or_options_t *options = get_options();
+  smartlist_t *chunks = NULL;
+  char *output = NULL;
 
   /* Make sure the identity key matches the one in the routerinfo. */
   if (!crypto_pk_eq_keys(ident_key, router->identity_pkey)) {
     log_warn(LD_BUG,"Tried to sign a router with a private key that didn't "
              "match router's public key!");
-    return -1;
+    goto err;
   }
 
   /* record our fingerprint, so we can include it in the descriptor */
   if (crypto_pk_get_fingerprint(router->identity_pkey, fingerprint, 1)<0) {
     log_err(LD_BUG,"Error computing fingerprint");
-    return -1;
+    goto err;
   }
 
   /* PEM-encode the onion key */
   if (crypto_pk_write_public_key_to_string(router->onion_pkey,
                                            &onion_pkey,&onion_pkeylen)<0) {
     log_warn(LD_BUG,"write onion_pkey to string failed!");
-    return -1;
+    goto err;
   }
 
   /* PEM-encode the identity key */
   if (crypto_pk_write_public_key_to_string(router->identity_pkey,
                                         &identity_pkey,&identity_pkeylen)<0) {
     log_warn(LD_BUG,"write identity_pkey to string failed!");
-    tor_free(onion_pkey);
-    return -1;
+    goto err;
   }
 
   /* Encode the publication time. */
@@ -2328,8 +2327,9 @@ router_dump_router_to_string(char *s, size_t maxlen, routerinfo_t *router,
     }
   }
 
+  chunks = smartlist_new();
   /* Generate the easy portion of the router descriptor. */
-  result = tor_snprintf(s, maxlen,
+  smartlist_add_asprintf(chunks,
                     "router %s %s %d 0 %d\n"
                     "%s"
                     "platform %s\n"
@@ -2364,28 +2364,11 @@ router_dump_router_to_string(char *s, size_t maxlen, routerinfo_t *router,
     options->HidServDirectoryV2 ? "hidden-service-dir\n" : "",
     options->AllowSingleHopExits ? "allow-single-hop-exits\n" : "");
 
-  tor_free(family_line);
-  tor_free(onion_pkey);
-  tor_free(identity_pkey);
-  tor_free(extra_or_address);
-
-  if (result < 0) {
-    log_warn(LD_BUG,"descriptor snprintf #1 ran out of room!");
-    return -1;
-  }
-  /* From now on, we use 'written' to remember the current length of 's'. */
-  written = result;
-
   if (options->ContactInfo && strlen(options->ContactInfo)) {
     const char *ci = options->ContactInfo;
     if (strchr(ci, '\n') || strchr(ci, '\r'))
       ci = escaped(ci);
-    result = tor_snprintf(s+written,maxlen-written, "contact %s\n", ci);
-    if (result<0) {
-      log_warn(LD_BUG,"descriptor snprintf #2 ran out of room!");
-      return -1;
-    }
-    written += result;
+    smartlist_add_asprintf(chunks, "contact %s\n", ci);
   }
 
 #ifdef CURVE25519_ENABLED
@@ -2394,105 +2377,92 @@ router_dump_router_to_string(char *s, size_t maxlen, routerinfo_t *router,
     base64_encode(kbuf, sizeof(kbuf),
                   (const char *)router->onion_curve25519_pkey->public_key,
                   CURVE25519_PUBKEY_LEN);
-    result = tor_snprintf(s+written,maxlen-written, "ntor-onion-key %s",
-                          kbuf);
-    if (result<0) {
-      log_warn(LD_BUG,"descriptor snprintf ran out of room!");
-      return -1;
-    }
-    written += result;
+    smartlist_add_asprintf(chunks, "ntor-onion-key %s", kbuf);
   }
 #endif
 
   /* Write the exit policy to the end of 's'. */
   if (!router->exit_policy || !smartlist_len(router->exit_policy)) {
-    strlcat(s+written, "reject *:*\n", maxlen-written);
-    written += strlen("reject *:*\n");
+    smartlist_add(chunks, tor_strdup("reject *:*\n"));
   } else if (router->exit_policy) {
     int i;
     for (i = 0; i < smartlist_len(router->exit_policy); ++i) {
+      char pbuf[POLICY_BUF_LEN];
       addr_policy_t *tmpe = smartlist_get(router->exit_policy, i);
+      int result;
       if (tor_addr_family(&tmpe->addr) == AF_INET6)
         continue; /* Don't include IPv6 parts of address policy */
-      result = policy_write_item(s+written, maxlen-written, tmpe, 1);
+      result = policy_write_item(pbuf, POLICY_BUF_LEN, tmpe, 1);
       if (result < 0) {
         log_warn(LD_BUG,"descriptor policy_write_item ran out of room!");
-        return -1;
-      }
-      tor_assert(result == (int)strlen(s+written));
-      written += result;
-      if (written+2 > maxlen) {
-        log_warn(LD_BUG,"descriptor policy_write_item ran out of room (2)!");
-        return -1;
+        goto err;
       }
-      s[written++] = '\n';
+      smartlist_add_asprintf(chunks, "%s\n", pbuf);
     }
   }
 
   if (router->ipv6_exit_policy) {
     char *p6 = write_short_policy(router->ipv6_exit_policy);
     if (p6 && strcmp(p6, "reject 1-65535")) {
-      result = tor_snprintf(s+written, maxlen-written,
+      smartlist_add_asprintf(chunks,
                             "ipv6-policy %s\n", p6);
-      if (result<0) {
-        log_warn(LD_BUG,"Descriptor printf of policy ran out of room");
-        tor_free(p6);
-        return -1;
-      }
-      written += result;
     }
     tor_free(p6);
   }
 
-  if (written + DIROBJ_MAX_SIG_LEN > maxlen) {
-    /* Not enough room for signature. */
-    log_warn(LD_BUG,"not enough room left in descriptor for signature!");
-    return -1;
-  }
-
   /* Sign the descriptor */
-  strlcpy(s+written, "router-signature\n", maxlen-written);
-  written += strlen(s+written);
-  s[written] = '\0';
-  if (router_get_router_hash(s, strlen(s), digest) < 0) {
-    return -1;
-  }
+  smartlist_add(chunks, tor_strdup("router-signature\n"));
+
+  crypto_digest_smartlist(digest, DIGEST_LEN, chunks, "", DIGEST_SHA1);
 
   note_crypto_pk_op(SIGN_RTR);
-  if (router_append_dirobj_signature(s+written,maxlen-written,
-                                     digest,DIGEST_LEN,ident_key)<0) {
-    log_warn(LD_BUG, "Couldn't sign router descriptor");
-    return -1;
+  {
+    char *sig;
+    if (!(sig = router_get_dirobj_signature(digest, DIGEST_LEN, ident_key))) {
+      log_warn(LD_BUG, "Couldn't sign router descriptor");
+      goto err;
+    }
+    smartlist_add(chunks, sig);
   }
-  written += strlen(s+written);
 
-  if (written+2 > maxlen) {
-    log_warn(LD_BUG,"Not enough room to finish descriptor.");
-    return -1;
-  }
   /* include a last '\n' */
-  s[written] = '\n';
-  s[written+1] = 0;
+  smartlist_add(chunks, tor_strdup("\n"));
+
+  output = smartlist_join_strings(chunks, "", 0, NULL);
 
 #ifdef DEBUG_ROUTER_DUMP_ROUTER_TO_STRING
   {
     char *s_dup;
     const char *cp;
     routerinfo_t *ri_tmp;
-    cp = s_dup = tor_strdup(s);
+    cp = s_dup = tor_strdup(output);
     ri_tmp = router_parse_entry_from_string(cp, NULL, 1, 0, NULL);
     if (!ri_tmp) {
       log_err(LD_BUG,
               "We just generated a router descriptor we can't parse.");
-      log_err(LD_BUG, "Descriptor was: <<%s>>", s);
-      return -1;
+      log_err(LD_BUG, "Descriptor was: <<%s>>", output);
+      goto err;
     }
     tor_free(s_dup);
     routerinfo_free(ri_tmp);
   }
 #endif
 
-  return (int)written+1;
+  goto done;
+
+ err:
+  tor_free(output); /* sets output to NULL */
+ done:
+  if (chunks) {
+    SMARTLIST_FOREACH(chunks, char *, cp, tor_free(cp));
+    smartlist_free(chunks);
+  }
+  tor_free(family_line);
+  tor_free(onion_pkey);
+  tor_free(identity_pkey);
+  tor_free(extra_or_address);
+
+  return output;
 }
 
 /** Copy the primary (IPv4) OR port (IP address and TCP port) for

+ 2 - 2
src/or/router.h

@@ -90,8 +90,8 @@ int router_is_me(const routerinfo_t *router);
 int router_fingerprint_is_me(const char *fp);
 int router_pick_published_address(const or_options_t *options, uint32_t *addr);
 int router_rebuild_descriptor(int force);
-int router_dump_router_to_string(char *s, size_t maxlen, routerinfo_t *router,
-                                 crypto_pk_t *ident_key);
+char *router_dump_router_to_string(routerinfo_t *router,
+                                   crypto_pk_t *ident_key);
 void router_get_prim_orport(const routerinfo_t *router,
                             tor_addr_port_t *addr_port_out);
 void router_get_pref_orport(const routerinfo_t *router,

+ 50 - 24
src/or/routerparse.c

@@ -662,18 +662,6 @@ router_get_networkstatus_v3_hashes(const char *s, digests_t *digests)
                                 ' ');
 }
 
-/** Set <b>digest</b> to the SHA-1 digest of the hash of the network-status
- * string in <b>s</b>.  Return 0 on success, -1 on failure. */
-int
-router_get_networkstatus_v3_hash(const char *s, char *digest,
-                                 digest_algorithm_t alg)
-{
-  return router_get_hash_impl(s, strlen(s), digest,
-                              "network-status-version",
-                              "\ndirectory-signature",
-                              ' ', alg);
-}
-
 /** Set <b>digest</b> to the SHA-1 digest of the hash of the <b>s_len</b>-byte
  * extrainfo string at <b>s</b>.  Return 0 on success, -1 on failure. */
 int
@@ -684,19 +672,23 @@ router_get_extrainfo_hash(const char *s, size_t s_len, char *digest)
 }
 
 /** Helper: used to generate signatures for routers, directories and
- * network-status objects.  Given a digest in <b>digest</b> and a secret
- * <b>private_key</b>, generate an PKCS1-padded signature, BASE64-encode it,
- * surround it with -----BEGIN/END----- pairs, and write it to the
- * <b>buf_len</b>-byte buffer at <b>buf</b>.  Return 0 on success, -1 on
- * failure.
+ * network-status objects.  Given a <b>digest_len</b>-byte digest in
+ * <b>digest</b> and a secret <b>private_key</b>, generate an PKCS1-padded
+ * signature, BASE64-encode it, surround it with -----BEGIN/END----- pairs,
+ * and return the new signature on success or NULL on failure.
  */
-int
-router_append_dirobj_signature(char *buf, size_t buf_len, const char *digest,
-                               size_t digest_len, crypto_pk_t *private_key)
+char *
+router_get_dirobj_signature(const char *digest,
+                            size_t digest_len,
+                            crypto_pk_t *private_key)
 {
   char *signature;
   size_t i, keysize;
   int siglen;
+  char *buf = NULL;
+  size_t buf_len;
+  /* overestimate of BEGIN/END lines total len. */
+#define BEGIN_END_OVERHEAD_LEN 64
 
   keysize = crypto_pk_keysize(private_key);
   signature = tor_malloc(keysize);
@@ -706,7 +698,12 @@ router_append_dirobj_signature(char *buf, size_t buf_len, const char *digest,
     log_warn(LD_BUG,"Couldn't sign digest.");
     goto err;
   }
-  if (strlcat(buf, "-----BEGIN SIGNATURE-----\n", buf_len) >= buf_len)
+
+  /* The *2 here is a ridiculous overestimate of base-64 overhead. */
+  buf_len = (siglen * 2) + BEGIN_END_OVERHEAD_LEN;
+  buf = tor_malloc(buf_len);
+
+  if (strlcpy(buf, "-----BEGIN SIGNATURE-----\n", buf_len) >= buf_len)
     goto truncated;
 
   i = strlen(buf);
@@ -719,13 +716,42 @@ router_append_dirobj_signature(char *buf, size_t buf_len, const char *digest,
     goto truncated;
 
   tor_free(signature);
-  return 0;
+  return buf;
 
  truncated:
   log_warn(LD_BUG,"tried to exceed string length.");
  err:
   tor_free(signature);
-  return -1;
+  tor_free(buf);
+  return NULL;
+}
+
+/** Helper: used to generate signatures for routers, directories and
+ * network-status objects.  Given a digest in <b>digest</b> and a secret
+ * <b>private_key</b>, generate an PKCS1-padded signature, BASE64-encode it,
+ * surround it with -----BEGIN/END----- pairs, and write it to the
+ * <b>buf_len</b>-byte buffer at <b>buf</b>.  Return 0 on success, -1 on
+ * failure.
+ */
+int
+router_append_dirobj_signature(char *buf, size_t buf_len, const char *digest,
+                               size_t digest_len, crypto_pk_t *private_key)
+{
+  size_t sig_len, s_len;
+  char *sig = router_get_dirobj_signature(digest, digest_len, private_key);
+  if (!sig) {
+    log_warn(LD_BUG, "No signature generated");
+    return -1;
+  }
+  sig_len = strlen(sig);
+  s_len = strlen(buf);
+  if (sig_len + s_len + 1 > buf_len) {
+    log_warn(LD_BUG, "Not enough room for signature");
+    tor_free(sig);
+    return -1;
+  }
+  memcpy(buf+s_len, sig, sig_len+1);
+  return 0;
 }
 
 /** Return VS_RECOMMENDED if <b>myversion</b> is contained in
@@ -1494,7 +1520,7 @@ extrainfo_parse_entry_from_string(const char *s, const char *end,
   extrainfo = tor_malloc_zero(sizeof(extrainfo_t));
   extrainfo->cache_info.is_extrainfo = 1;
   if (cache_copy)
-    extrainfo->cache_info.signed_descriptor_body = tor_memdup_nulterm(s, end-s);
+    extrainfo->cache_info.signed_descriptor_body = tor_memdup_nulterm(s,end-s);
   extrainfo->cache_info.signed_descriptor_len = end-s;
   memcpy(extrainfo->cache_info.signed_descriptor_digest, digest, DIGEST_LEN);
 

+ 3 - 2
src/or/routerparse.h

@@ -16,11 +16,12 @@ int router_get_router_hash(const char *s, size_t s_len, char *digest);
 int router_get_dir_hash(const char *s, char *digest);
 int router_get_runningrouters_hash(const char *s, char *digest);
 int router_get_networkstatus_v2_hash(const char *s, char *digest);
-int router_get_networkstatus_v3_hash(const char *s, char *digest,
-                                     digest_algorithm_t algorithm);
 int router_get_networkstatus_v3_hashes(const char *s, digests_t *digests);
 int router_get_extrainfo_hash(const char *s, size_t s_len, char *digest);
 #define DIROBJ_MAX_SIG_LEN 256
+char *router_get_dirobj_signature(const char *digest,
+                                  size_t digest_len,
+                                  crypto_pk_t *private_key);
 int router_append_dirobj_signature(char *buf, size_t buf_len,
                                    const char *digest,
                                    size_t digest_len,

+ 2 - 1
src/test/test.c

@@ -108,7 +108,8 @@ setup_directory(void)
     r = mkdir(temp_dir);
   }
 #else
-  tor_snprintf(temp_dir, sizeof(temp_dir), "/tmp/tor_test_%d_%s", (int) getpid(), rnd32);
+  tor_snprintf(temp_dir, sizeof(temp_dir), "/tmp/tor_test_%d_%s",
+               (int) getpid(), rnd32);
   r = mkdir(temp_dir, 0700);
 #endif
   if (r) {

+ 214 - 45
src/test/test_dir.c

@@ -12,6 +12,7 @@
 #define ROUTERLIST_PRIVATE
 #define HIBERNATE_PRIVATE
 #include "or.h"
+#include "config.h"
 #include "directory.h"
 #include "dirserv.h"
 #include "dirvote.h"
@@ -73,22 +74,24 @@ test_dir_nicknames(void)
 static void
 test_dir_formats(void)
 {
-  char buf[8192], buf2[8192];
+  char *buf = NULL;
+  char buf2[8192];
   char platform[256];
   char fingerprint[FINGERPRINT_LEN+1];
-  char *pk1_str = NULL, *pk2_str = NULL, *pk3_str = NULL, *cp;
-  size_t pk1_str_len, pk2_str_len, pk3_str_len;
+  char *pk1_str = NULL, *pk2_str = NULL, *cp;
+  size_t pk1_str_len, pk2_str_len;
   routerinfo_t *r1=NULL, *r2=NULL;
-  crypto_pk_t *pk1 = NULL, *pk2 = NULL, *pk3 = NULL;
-  routerinfo_t *rp1 = NULL;
+  crypto_pk_t *pk1 = NULL, *pk2 = NULL;
+  routerinfo_t *rp1 = NULL, *rp2 = NULL;
   addr_policy_t *ex1, *ex2;
   routerlist_t *dir1 = NULL, *dir2 = NULL;
+  or_options_t *options = get_options_mutable();
+  const addr_policy_t *p;
 
   pk1 = pk_generate(0);
   pk2 = pk_generate(1);
-  pk3 = pk_generate(2);
 
-  test_assert(pk1 && pk2 && pk3);
+  test_assert(pk1 && pk2);
 
   hibernate_set_state_for_testing_(HIBERNATE_STATE_LIVE);
 
@@ -128,22 +131,27 @@ test_dir_formats(void)
   r2->or_port = 9005;
   r2->dir_port = 0;
   r2->onion_pkey = crypto_pk_dup_key(pk2);
+  r2->onion_curve25519_pkey = tor_malloc_zero(sizeof(curve25519_public_key_t));
+  curve25519_public_from_base64(r2->onion_curve25519_pkey,
+                                "skyinAnvardNostarsNomoonNowindormistsorsnow");
   r2->identity_pkey = crypto_pk_dup_key(pk1);
   r2->bandwidthrate = r2->bandwidthburst = r2->bandwidthcapacity = 3000;
   r2->exit_policy = smartlist_new();
-  smartlist_add(r2->exit_policy, ex2);
   smartlist_add(r2->exit_policy, ex1);
+  smartlist_add(r2->exit_policy, ex2);
   r2->nickname = tor_strdup("Fred");
 
   test_assert(!crypto_pk_write_public_key_to_string(pk1, &pk1_str,
                                                     &pk1_str_len));
   test_assert(!crypto_pk_write_public_key_to_string(pk2 , &pk2_str,
                                                     &pk2_str_len));
-  test_assert(!crypto_pk_write_public_key_to_string(pk3 , &pk3_str,
-                                                    &pk3_str_len));
 
-  memset(buf, 0, 2048);
-  test_assert(router_dump_router_to_string(buf, 2048, r1, pk2)>0);
+  /* XXXX025 router_dump_to_string should really take this from ri.*/
+  options->ContactInfo = tor_strdup("Magri White "
+                                    "<magri@elsewhere.example.com>");
+  buf = router_dump_router_to_string(r1, pk2);
+  tor_free(options->ContactInfo);
+  test_assert(buf);
 
   strlcpy(buf2, "router Magri 18.244.0.1 9000 0 9003\n"
           "or-address [1:2:3:4::]:9999\n"
@@ -165,13 +173,17 @@ test_dir_formats(void)
   strlcat(buf2, "signing-key\n", sizeof(buf2));
   strlcat(buf2, pk2_str, sizeof(buf2));
   strlcat(buf2, "hidden-service-dir\n", sizeof(buf2));
+  strlcat(buf2, "contact Magri White <magri@elsewhere.example.com>\n",
+          sizeof(buf2));
   strlcat(buf2, "reject *:*\nrouter-signature\n", sizeof(buf2));
   buf[strlen(buf2)] = '\0'; /* Don't compare the sig; it's never the same
                              * twice */
 
   test_streq(buf, buf2);
+  tor_free(buf);
 
-  test_assert(router_dump_router_to_string(buf, 2048, r1, pk2)>0);
+  buf = router_dump_router_to_string(r1, pk2);
+  test_assert(buf);
   cp = buf;
   rp1 = router_parse_entry_from_string((const char*)cp,NULL,1,0,NULL);
   test_assert(rp1);
@@ -185,35 +197,67 @@ test_dir_formats(void)
   test_assert(crypto_pk_cmp_keys(rp1->identity_pkey, pk2) == 0);
   //test_assert(rp1->exit_policy == NULL);
 
-#if 0
-  /* XXX Once we have exit policies, test this again. XXX */
-  strlcpy(buf2, "router tor.tor.tor 9005 0 0 3000\n", sizeof(buf2));
+  strlcpy(buf2,
+          "router Fred 1.1.1.1 9005 0 0\n"
+          "platform Tor "VERSION" on ", sizeof(buf2));
+  strlcat(buf2, get_uname(), sizeof(buf2));
+  strlcat(buf2, "\n"
+          "protocols Link 1 2 Circuit 1\n"
+          "published 1970-01-01 00:00:05\n"
+          "fingerprint ", sizeof(buf2));
+  test_assert(!crypto_pk_get_fingerprint(pk1, fingerprint, 1));
+  strlcat(buf2, fingerprint, sizeof(buf2));
+  strlcat(buf2, "\nuptime 0\n"
+          "bandwidth 3000 3000 3000\n", sizeof(buf2));
+  strlcat(buf2, "onion-key\n", sizeof(buf2));
   strlcat(buf2, pk2_str, sizeof(buf2));
   strlcat(buf2, "signing-key\n", sizeof(buf2));
   strlcat(buf2, pk1_str, sizeof(buf2));
-  strlcat(buf2, "accept *:80\nreject 18.*:24\n\n", sizeof(buf2));
-  test_assert(router_dump_router_to_string(buf, 2048, &r2, pk2)>0);
+  strlcat(buf2, "hidden-service-dir\n", sizeof(buf2));
+  strlcat(buf2, "ntor-onion-key "
+          "skyinAnvardNostarsNomoonNowindormistsorsnow=\n", sizeof(buf2));
+  strlcat(buf2, "accept *:80\nreject 18.0.0.0/8:24\n", sizeof(buf2));
+  strlcat(buf2, "router-signature\n", sizeof(buf2));
+
+  buf = router_dump_router_to_string(r2, pk1);
+  buf[strlen(buf2)] = '\0'; /* Don't compare the sig; it's never the same
+                             * twice */
   test_streq(buf, buf2);
+  tor_free(buf);
 
+  buf = router_dump_router_to_string(r2, pk1);
   cp = buf;
-  rp2 = router_parse_entry_from_string(&cp,1);
+  rp2 = router_parse_entry_from_string((const char*)cp,NULL,1,0,NULL);
   test_assert(rp2);
-  test_streq(rp2->address, r2.address);
-  test_eq(rp2->or_port, r2.or_port);
-  test_eq(rp2->dir_port, r2.dir_port);
-  test_eq(rp2->bandwidth, r2.bandwidth);
+  test_streq(rp2->address, r2->address);
+  test_eq(rp2->or_port, r2->or_port);
+  test_eq(rp2->dir_port, r2->dir_port);
+  test_eq(rp2->bandwidthrate, r2->bandwidthrate);
+  test_eq(rp2->bandwidthburst, r2->bandwidthburst);
+  test_eq(rp2->bandwidthcapacity, r2->bandwidthcapacity);
+  test_memeq(rp2->onion_curve25519_pkey->public_key,
+             r2->onion_curve25519_pkey->public_key,
+             CURVE25519_PUBKEY_LEN);
   test_assert(crypto_pk_cmp_keys(rp2->onion_pkey, pk2) == 0);
   test_assert(crypto_pk_cmp_keys(rp2->identity_pkey, pk1) == 0);
-  test_eq(rp2->exit_policy->policy_type, EXIT_POLICY_ACCEPT);
-  test_streq(rp2->exit_policy->string, "accept *:80");
-  test_streq(rp2->exit_policy->address, "*");
-  test_streq(rp2->exit_policy->port, "80");
-  test_eq(rp2->exit_policy->next->policy_type, EXIT_POLICY_REJECT);
-  test_streq(rp2->exit_policy->next->string, "reject 18.*:24");
-  test_streq(rp2->exit_policy->next->address, "18.*");
-  test_streq(rp2->exit_policy->next->port, "24");
-  test_assert(rp2->exit_policy->next->next == NULL);
 
+  test_eq(smartlist_len(rp2->exit_policy), 2);
+
+  p = smartlist_get(rp2->exit_policy, 0);
+  test_eq(p->policy_type, ADDR_POLICY_ACCEPT);
+  test_assert(tor_addr_is_null(&p->addr));
+  test_eq(p->maskbits, 0);
+  test_eq(p->prt_min, 80);
+  test_eq(p->prt_max, 80);
+
+  p = smartlist_get(rp2->exit_policy, 1);
+  test_eq(p->policy_type, ADDR_POLICY_REJECT);
+  test_assert(tor_addr_eq(&p->addr, &ex2->addr));
+  test_eq(p->maskbits, 8);
+  test_eq(p->prt_min, 24);
+  test_eq(p->prt_max, 24);
+
+#if 0
   /* Okay, now for the directories. */
   {
     fingerprint_list = smartlist_new();
@@ -232,12 +276,11 @@ test_dir_formats(void)
   if (r2)
     routerinfo_free(r2);
 
+  tor_free(buf);
   tor_free(pk1_str);
   tor_free(pk2_str);
-  tor_free(pk3_str);
   if (pk1) crypto_pk_free(pk1);
   if (pk2) crypto_pk_free(pk2);
-  if (pk3) crypto_pk_free(pk3);
   if (rp1) routerinfo_free(rp1);
   tor_free(dir1); /* XXXX And more !*/
   tor_free(dir2); /* And more !*/
@@ -611,7 +654,7 @@ test_dir_measured_bw_kb_cache(void)
   test_assert(dirserv_query_measured_bw_cache_kb(mbwl[0].node_id,NULL, NULL));
   test_assert(dirserv_query_measured_bw_cache_kb(mbwl[0].node_id,&bw, NULL));
   test_eq(bw, 20);
-  test_assert(dirserv_query_measured_bw_cache_kb(mbwl[0].node_id,NULL, &as_of));
+  test_assert(dirserv_query_measured_bw_cache_kb(mbwl[0].node_id,NULL,&as_of));
   test_eq(as_of, MBWC_INIT_TIME);
   /* Now expire it */
   curr += MAX_MEASUREMENT_AGE + 1;
@@ -619,7 +662,7 @@ test_dir_measured_bw_kb_cache(void)
   /* Check that the cache is empty */
   test_eq(dirserv_get_measured_bw_cache_size(), 0);
   /* Check that we can't retrieve it */
-  test_assert(!dirserv_query_measured_bw_cache_kb(mbwl[0].node_id, NULL, NULL));
+  test_assert(!dirserv_query_measured_bw_cache_kb(mbwl[0].node_id, NULL,NULL));
   /* Try caching a few things now */
   dirserv_cache_measured_bw(&(mbwl[0]), curr);
   test_eq(dirserv_get_measured_bw_cache_size(), 1);
@@ -934,6 +977,13 @@ gen_routerstatus_for_v3ns(int idx, time_t now)
       /* Shouldn't happen */
       test_assert(0);
   }
+  if (vrs) {
+    vrs->microdesc = tor_malloc_zero(sizeof(vote_microdesc_hash_t));
+    tor_asprintf(&vrs->microdesc->microdesc_hash_line,
+                 "m 9,10,11,12,13,14,15,16,17 "
+                 "sha256=xyzajkldsdsajdadlsdjaslsdksdjlsdjsdaskdaaa%d\n",
+                 idx);
+  }
 
  done:
   return vrs;
@@ -1903,6 +1953,13 @@ gen_routerstatus_for_umbw(int idx, time_t now)
       /* Shouldn't happen */
       test_assert(0);
   }
+  if (vrs) {
+    vrs->microdesc = tor_malloc_zero(sizeof(vote_microdesc_hash_t));
+    tor_asprintf(&vrs->microdesc->microdesc_hash_line,
+                 "m 9,10,11,12,13,14,15,16,17 "
+                 "sha256=xyzajkldsdsajdadlsdjaslsdksdjlsdjsdaskdaaa%d\n",
+                 idx);
+  }
 
  done:
   return vrs;
@@ -2050,7 +2107,7 @@ test_consensus_for_umbw(networkstatus_t *con, time_t now)
 
   test_assert(con);
   test_assert(!con->cert);
-  /* test_assert(con->consensus_method >= MIN_METHOD_TO_CLIP_UNMEASURED_BW_KB); */
+  // test_assert(con->consensus_method >= MIN_METHOD_TO_CLIP_UNMEASURED_BW_KB);
   test_assert(con->consensus_method >= 16);
   test_eq(4, smartlist_len(con->routerstatus_list));
   /* There should be four listed routers; all voters saw the same in this */
@@ -2176,8 +2233,8 @@ test_dir_clip_unmeasured_bw_kb(void)
 }
 
 /**
- * This version of test_dir_clip_unmeasured_bw_kb() uses a non-default choice of
- * clip bandwidth.
+ * This version of test_dir_clip_unmeasured_bw_kb() uses a non-default choice
+ * of clip bandwidth.
  */
 
 static void
@@ -2197,26 +2254,138 @@ test_dir_clip_unmeasured_bw_kb_alt(void)
                        test_routerstatus_for_umbw);
 }
 
+extern time_t time_of_process_start; /* from main.c */
+
+static void
+test_dir_v2_dir(void *arg)
+{
+  /* Runs in a forked process: acts like a v2 directory just enough to make and
+   * sign a v2 networkstatus opinion */
+
+  cached_dir_t *v2 = NULL;
+  or_options_t *options = get_options_mutable();
+  crypto_pk_t *id_key = pk_generate(4);
+  (void) arg;
+
+  options->ORPort_set = 1; /* So we believe we're a server. */
+  options->DirPort_set = 1;
+  options->Address = tor_strdup("99.99.99.99");
+  options->Nickname = tor_strdup("TestV2Auth");
+  options->ContactInfo = tor_strdup("TestV2Auth <testv2auth@example.com>");
+  {
+    /* Give it a DirPort */
+    smartlist_t *ports = (smartlist_t *)get_configured_ports();
+    port_cfg_t *port = tor_malloc_zero(sizeof(port_cfg_t));
+    port->type = CONN_TYPE_DIR_LISTENER;
+    port->port = 9999;
+    smartlist_add(ports, port);
+  }
+  set_server_identity_key(id_key);
+  set_client_identity_key(id_key);
+
+  /* Add a router. */
+  {
+    was_router_added_t wra;
+    const char *msg = NULL;
+    routerinfo_t *r1 = tor_malloc_zero(sizeof(routerinfo_t));
+    r1->address = tor_strdup("18.244.0.1");
+    r1->addr = 0xc0a80001u; /* 192.168.0.1 */
+    r1->cache_info.published_on = time(NULL)-60;
+    r1->or_port = 9000;
+    r1->dir_port = 9003;
+    tor_addr_parse(&r1->ipv6_addr, "1:2:3:4::");
+    r1->ipv6_orport = 9999;
+    r1->onion_pkey = pk_generate(1);
+    r1->identity_pkey = pk_generate(2);
+    r1->bandwidthrate = 1000;
+    r1->bandwidthburst = 5000;
+    r1->bandwidthcapacity = 10000;
+    r1->exit_policy = NULL;
+    r1->nickname = tor_strdup("Magri");
+    r1->platform = tor_strdup("Tor 0.2.7.7-gamma");
+    r1->cache_info.routerlist_index = -1;
+    r1->cache_info.signed_descriptor_body =
+      router_dump_router_to_string(r1, r1->identity_pkey);
+    r1->cache_info.signed_descriptor_len =
+      strlen(r1->cache_info.signed_descriptor_body);
+    wra = router_add_to_routerlist(r1, &msg, 0, 0);
+    tt_int_op(wra, ==, ROUTER_ADDED_SUCCESSFULLY);
+  }
+
+  /* Prevent call of rep_hist_note_router_unreachable(). */
+  time_of_process_start = time(NULL);
+
+  /* Make a directory so there's somewhere to store the thing */
+#ifdef _WIN32
+  mkdir(get_fname("cached-status"));
+#else
+  mkdir(get_fname("cached-status"), 0700);
+#endif
+
+  v2 = generate_v2_networkstatus_opinion();
+  tt_assert(v2);
+
+ done:
+  crypto_pk_free(id_key);
+  cached_dir_decref(v2);
+}
+
+static void
+test_dir_fmt_control_ns(void *arg)
+{
+  char *s = NULL;
+  routerstatus_t rs;
+  (void)arg;
+
+  memset(&rs, 0, sizeof(rs));
+  rs.published_on = 1364925198;
+  strlcpy(rs.nickname, "TetsuoMilk", sizeof(rs.nickname));
+  memcpy(rs.identity_digest, "Stately, plump Buck ", DIGEST_LEN);
+  memcpy(rs.descriptor_digest, "Mulligan came up fro", DIGEST_LEN);
+  rs.addr = 0x20304050;
+  rs.or_port = 9001;
+  rs.dir_port = 9002;
+  rs.is_exit = 1;
+  rs.is_fast = 1;
+  rs.is_flagged_running = 1;
+  rs.has_bandwidth = 1;
+  rs.bandwidth_kb = 1000;
+
+  s = networkstatus_getinfo_helper_single(&rs);
+  tt_assert(s);
+  tt_str_op(s, ==,
+            "r TetsuoMilk U3RhdGVseSwgcGx1bXAgQnVjayA "
+               "TXVsbGlnYW4gY2FtZSB1cCBmcm8 2013-04-02 17:53:18 "
+               "32.48.64.80 9001 9002\n"
+            "s Exit Fast Running\n"
+            "w Bandwidth=1000\n");
+
+ done:
+  tor_free(s);
+}
+
 #define DIR_LEGACY(name)                                                   \
   { #name, legacy_test_helper, TT_FORK, &legacy_setup, test_dir_ ## name }
 
-#define DIR(name)                               \
-  { #name, test_dir_##name, 0, NULL, NULL }
+#define DIR(name,flags)                              \
+  { #name, test_dir_##name, (flags), NULL, NULL }
 
 struct testcase_t dir_tests[] = {
   DIR_LEGACY(nicknames),
   DIR_LEGACY(formats),
   DIR_LEGACY(versions),
   DIR_LEGACY(fp_pairs),
-  DIR(split_fps),
+  DIR(split_fps, 0),
   DIR_LEGACY(measured_bw_kb),
+  DIR_LEGACY(measured_bw_kb_cache),
   DIR_LEGACY(param_voting),
   DIR_LEGACY(v3_networkstatus),
-  DIR(random_weighted),
-  DIR(scale_bw),
+  DIR(random_weighted, 0),
+  DIR(scale_bw, 0),
   DIR_LEGACY(clip_unmeasured_bw_kb),
   DIR_LEGACY(clip_unmeasured_bw_kb_alt),
-  DIR_LEGACY(measured_bw_kb_cache),
+  DIR(v2_dir, TT_FORK),
+  DIR(fmt_control_ns, 0),
   END_OF_TESTCASES
 };