summaryrefslogtreecommitdiff
path: root/src/or/dirvote.c
diff options
context:
space:
mode:
authorNick Mathewson <nickm@torproject.org>2009-08-24 12:51:33 -0400
committerNick Mathewson <nickm@torproject.org>2009-10-15 15:17:13 -0400
commite1ddee8bbe724e934fe9a4cb2d290719a7d6105c (patch)
tree82848930764ba4b86637f15a25b557fc6805f31a /src/or/dirvote.c
parenta8e92ba8fd65e12dbec265ccfcf0c89ac61847f2 (diff)
downloadtor-e1ddee8bbe724e934fe9a4cb2d290719a7d6105c.tar.gz
tor-e1ddee8bbe724e934fe9a4cb2d290719a7d6105c.zip
Code to generate, store, and parse microdescriptors and consensuses.
The consensus documents are not signed properly, not served, and not exchanged yet.
Diffstat (limited to 'src/or/dirvote.c')
-rw-r--r--src/or/dirvote.c200
1 files changed, 142 insertions, 58 deletions
diff --git a/src/or/dirvote.c b/src/or/dirvote.c
index f0f92d2f60..d200344ef8 100644
--- a/src/or/dirvote.c
+++ b/src/or/dirvote.c
@@ -24,14 +24,20 @@ static int dirvote_publish_consensus(void);
static char *make_consensus_method_list(int low, int high, const char *sep);
/** The highest consensus method that we currently support. */
-#define MAX_SUPPORTED_CONSENSUS_METHOD 7
+#define MAX_SUPPORTED_CONSENSUS_METHOD 8
#define MIN_METHOD_FOR_PARAMS 7
+/** Lowest consensus method that generates microdescriptors */
+#define MIN_METHOD_FOR_MICRODESC 8
+
/* =====
* 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. */
@@ -89,7 +95,7 @@ format_networkstatus_vote(crypto_pk_env_t *private_signing_key,
len = 8192;
len += strlen(version_lines);
- len += (RS_ENTRY_LEN)*smartlist_len(rl->routers);
+ len += (RS_ENTRY_LEN+MICRODESC_LINE_LEN)*smartlist_len(rl->routers);
len += v3_ns->cert->cache_info.signed_descriptor_len;
status = tor_malloc(len);
@@ -158,15 +164,25 @@ format_networkstatus_vote(crypto_pk_env_t *private_signing_key,
outp += cert->cache_info.signed_descriptor_len;
}
- SMARTLIST_FOREACH(v3_ns->routerstatus_list, vote_routerstatus_t *, vrs,
- {
+ SMARTLIST_FOREACH_BEGIN(v3_ns->routerstatus_list, vote_routerstatus_t *,
+ vrs) {
+ vote_microdesc_hash_t *h;
if (routerstatus_format_entry(outp, endp-outp, &vrs->status,
vrs->version, NS_V3_VOTE) < 0) {
log_warn(LD_BUG, "Unable to print router status.");
goto err;
}
outp += strlen(outp);
- });
+
+ 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_FOREACH_END(vrs);
{
char signing_key_fingerprint[FINGERPRINT_LEN+1];
@@ -189,7 +205,7 @@ format_networkstatus_vote(crypto_pk_env_t *private_signing_key,
outp += strlen(outp);
}
- if (router_get_networkstatus_v3_hash(status, digest)<0)
+ 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,
@@ -294,34 +310,8 @@ get_frequent_members(smartlist_t *out, smartlist_t *in, int min)
/** Given a sorted list of strings <b>lst</b>, return the member that appears
* most. Break ties in favor of later-occurring members. */
-static const char *
-get_most_frequent_member(smartlist_t *lst)
-{
- const char *most_frequent = NULL;
- int most_frequent_count = 0;
-
- const char *cur = NULL;
- int count = 0;
-
- SMARTLIST_FOREACH(lst, const char *, s,
- {
- if (cur && !strcmp(s, cur)) {
- ++count;
- } else {
- if (count >= most_frequent_count) {
- most_frequent = cur;
- most_frequent_count = count;
- }
- cur = s;
- count = 1;
- }
- });
- if (count >= most_frequent_count) {
- most_frequent = cur;
- most_frequent_count = count;
- }
- return most_frequent;
-}
+#define get_most_frequent_member(lst) \
+ smartlist_get_most_frequent_string(lst)
/** Return 0 if and only if <b>a</b> and <b>b</b> are routerstatuses
* that come from the same routerinfo, with the same derived elements.
@@ -363,7 +353,8 @@ _compare_vote_rs(const void **_a, const void **_b)
* in favor of smaller descriptor digest.
*/
static vote_routerstatus_t *
-compute_routerstatus_consensus(smartlist_t *votes)
+compute_routerstatus_consensus(smartlist_t *votes, int consensus_method,
+ char *microdesc_digest256_out)
{
vote_routerstatus_t *most = NULL, *cur = NULL;
int most_n = 0, cur_n = 0;
@@ -399,6 +390,26 @@ compute_routerstatus_consensus(smartlist_t *votes)
}
tor_assert(most);
+
+ if (consensus_method >= MIN_METHOD_FOR_MICRODESC &&
+ microdesc_digest256_out) {
+ smartlist_t *digests = smartlist_create();
+ const char *best_microdesc_digest;
+ SMARTLIST_FOREACH(votes, vote_routerstatus_t *, rs, {
+ char d[DIGEST256_LEN];
+ if (compare_vote_rs(rs, most))
+ continue;
+ if (!vote_routerstatus_find_microdesc_hash(d, rs, consensus_method))
+ smartlist_add(digests, tor_memdup(d, sizeof(d)));
+ });
+ smartlist_sort_digests256(digests);
+ best_microdesc_digest = smartlist_get_most_frequent_digest256(digests);
+ if (best_microdesc_digest)
+ memcpy(microdesc_digest256_out, best_microdesc_digest, DIGEST256_LEN);
+ SMARTLIST_FOREACH(digests, char *, cp, tor_free(cp));
+ smartlist_free(digests);
+ }
+
return most;
}
@@ -615,16 +626,20 @@ networkstatus_compute_consensus(smartlist_t *votes,
crypto_pk_env_t *identity_key,
crypto_pk_env_t *signing_key,
const char *legacy_id_key_digest,
- crypto_pk_env_t *legacy_signing_key)
+ crypto_pk_env_t *legacy_signing_key,
+ consensus_flavor_t flavor)
{
smartlist_t *chunks;
char *result = NULL;
int consensus_method;
-
time_t valid_after, fresh_until, valid_until;
int vote_seconds, dist_seconds;
char *client_versions = NULL, *server_versions = NULL;
smartlist_t *flags;
+ const routerstatus_format_type_t rs_format =
+ flavor == FLAV_NS ? NS_V3_CONSENSUS : NS_V3_CONSENSUS_MICRODESC;
+
+ tor_assert(flavor == FLAV_NS || flavor == FLAV_MICRODESC);
tor_assert(total_authorities >= smartlist_len(votes));
if (!smartlist_len(votes)) {
@@ -955,6 +970,7 @@ networkstatus_compute_consensus(smartlist_t *votes,
int n_listing = 0;
int i;
char buf[256];
+ char microdesc_digest[DIGEST256_LEN];
/* Of the next-to-be-considered digest in each voter, which is first? */
SMARTLIST_FOREACH(votes, networkstatus_t *, v, {
@@ -1021,7 +1037,9 @@ networkstatus_compute_consensus(smartlist_t *votes,
/* Figure out the most popular opinion of what the most recent
* routerinfo and its contents are. */
- rs = compute_routerstatus_consensus(matching_descs);
+ memset(microdesc_digest, 0, sizeof(microdesc_digest));
+ rs = compute_routerstatus_consensus(matching_descs, consensus_method,
+ microdesc_digest);
/* Copy bits of that into rs_out. */
tor_assert(!memcmp(lowest_id, rs->status.identity_digest, DIGEST_LEN));
memcpy(rs_out.identity_digest, lowest_id, DIGEST_LEN);
@@ -1182,9 +1200,19 @@ networkstatus_compute_consensus(smartlist_t *votes,
/* Okay!! Now we can write the descriptor... */
/* First line goes into "buf". */
routerstatus_format_entry(buf, sizeof(buf), &rs_out, NULL,
- NS_V3_CONSENSUS);
+ rs_format);
smartlist_add(chunks, tor_strdup(buf));
- /* Second line is all flags. The "\n" is missing. */
+ /* Now an m line, if applicable. */
+ if (flavor == FLAV_MICRODESC &&
+ !tor_digest256_is_zero(microdesc_digest)) {
+ char m[BASE64_DIGEST256_LEN+1], *cp;
+ const size_t mlen = BASE64_DIGEST256_LEN+5;
+ digest256_to_base64(m, microdesc_digest);
+ cp = tor_malloc(mlen);
+ tor_snprintf(cp, mlen, "m %s\n", m);
+ smartlist_add(chunks, cp);
+ }
+ /* Next line is all flags. The "\n" is missing. */
smartlist_add(chunks,
smartlist_join_strings(chosen_flags, " ", 0, NULL));
/* Now the version line. */
@@ -1206,7 +1234,7 @@ networkstatus_compute_consensus(smartlist_t *votes,
};
/* Now the exitpolicy summary line. */
- if (rs_out.has_exitsummary) {
+ if (rs_out.has_exitsummary && flavor == FLAV_NS) {
char buf[MAX_POLICY_LINE_LEN+1];
int r = tor_snprintf(buf, sizeof(buf), "p %s\n", rs_out.exitsummary);
if (r<0) {
@@ -2090,7 +2118,8 @@ dirvote_compute_consensus(void)
consensus_body = networkstatus_compute_consensus(
votes, n_voters,
my_cert->identity_key,
- get_my_v3_authority_signing_key(), legacy_id_digest, legacy_sign);
+ get_my_v3_authority_signing_key(), legacy_id_digest, legacy_sign,
+ FLAV_NS);
}
if (!consensus_body) {
log_warn(LD_DIR, "Couldn't generate a consensus at all!");
@@ -2389,14 +2418,21 @@ dirvote_get_vote(const char *fp, int flags)
return NULL;
}
-int
-dirvote_create_microdescriptor(char *out, size_t outlen,
- const routerinfo_t *ri)
+/** Construct and return a new microdescriptor from a routerinfo <b>ri</b>.
+ *
+ * XXX Right now, there is only one way to generate microdescriptors from
+ * router descriptors. This may change in future consensus methods. If so,
+ * we'll need an internal way to remember which method we used, and ask for a
+ * particular method.
+ **/
+microdesc_t *
+dirvote_create_microdescriptor(const routerinfo_t *ri)
{
+ microdesc_t *result = NULL;
char *key = NULL, *summary = NULL, *family = NULL;
+ char buf[1024];
size_t keylen;
- int result = -1;
- char *start = out, *end = out+outlen;
+ char *out = buf, *end = buf+sizeof(buf);
if (crypto_pk_write_public_key_to_string(ri->onion_pkey, &key, &keylen)<0)
goto done;
@@ -2417,7 +2453,19 @@ dirvote_create_microdescriptor(char *out, size_t outlen,
goto done;
out += strlen(out);
}
- result = out - start;
+ *out = '\0'; /* Make sure it's nul-terminated. This should be a no-op */
+
+ {
+ smartlist_t *lst = microdescs_parse_from_string(buf, out, 0, 1);
+ if (smartlist_len(lst) != 1) {
+ log_warn(LD_DIR, "We generated a microdescriptor we couldn't parse.");
+ SMARTLIST_FOREACH(lst, microdesc_t *, md, microdesc_free(md));
+ smartlist_free(lst);
+ goto done;
+ }
+ result = smartlist_get(lst, 0);
+ smartlist_free(lst);
+ }
done:
tor_free(key);
@@ -2426,28 +2474,23 @@ dirvote_create_microdescriptor(char *out, size_t outlen,
return result;
}
-/** Lowest consensus method that generates microdescriptors */
-#define MIN_CM_MICRODESC 7
/** Cached space-separated string to hold */
static char *microdesc_consensus_methods = NULL;
+/** DOCDOC */
int
-dirvote_format_microdescriptor_vote_line(char *out, size_t out_len,
- const char *microdesc,
- size_t microdescriptor_len)
+dirvote_format_microdesc_vote_line(char *out, size_t out_len,
+ const microdesc_t *md)
{
- char d[DIGEST256_LEN];
char d64[BASE64_DIGEST256_LEN];
if (!microdesc_consensus_methods) {
microdesc_consensus_methods =
- make_consensus_method_list(MIN_CM_MICRODESC,
+ make_consensus_method_list(MIN_METHOD_FOR_MICRODESC,
MAX_SUPPORTED_CONSENSUS_METHOD,
",");
tor_assert(microdesc_consensus_methods);
}
- if (crypto_digest256(d, microdesc, microdescriptor_len, DIGEST_SHA256)<0)
- return -1;
- if (digest256_to_base64(d64, d)<0)
+ if (digest256_to_base64(d64, md->digest)<0)
return -1;
if (tor_snprintf(out, out_len, "m %s sha256=%s\n",
@@ -2457,3 +2500,44 @@ dirvote_format_microdescriptor_vote_line(char *out, size_t out_len,
return strlen(out);
}
+/** DOCDOC */
+int
+vote_routerstatus_find_microdesc_hash(char *digest256_out,
+ const vote_routerstatus_t *vrs,
+ int method)
+{
+ /* XXXX only returns the sha256 method. */
+ const vote_microdesc_hash_t *h;
+ char mstr[64];
+ size_t mlen;
+
+ tor_snprintf(mstr, sizeof(mstr), "%d", method);
+ mlen = strlen(mstr);
+
+ for (h = vrs->microdesc; h; h = h->next) {
+ const char *cp = h->microdesc_hash_line;
+ size_t num_len;
+ /* cp looks like \d+(,\d+)* (digesttype=val )+ . Let's hunt for mstr in
+ * the first part. */
+ while (1) {
+ num_len = strspn(cp, "1234567890");
+ if (num_len == mlen && !memcmp(mstr, cp, mlen)) {
+ /* This is the line. */
+ char buf[BASE64_DIGEST256_LEN+1];
+ /* XXXX ignores extraneous stuff if the digest is too long. This
+ * seems harmless enough, right? */
+ cp = strstr(cp, " sha256=");
+ if (!cp)
+ return -1;
+ cp += strlen(" sha256=");
+ strlcpy(buf, cp, sizeof(buf));
+ return digest256_from_base64(digest256_out, buf);
+ }
+ if (num_len == 0 || cp[num_len] != ',')
+ break;
+ cp += num_len + 1;
+ }
+ }
+ return -1;
+}
+