r13243@catbus: nickm | 2007-06-04 15:17:15 -0400

Start of code to compute consensus network-status stuff from a bunch of votes.  Strangely, it does not yet feel like an enormous ugly hack. 


svn:r10489
This commit is contained in:
Nick Mathewson 2007-06-04 19:19:01 +00:00
parent 32d12dcfbc
commit bb6f53d60c
8 changed files with 558 additions and 23 deletions

View File

@ -754,12 +754,12 @@ $Id$
The authority section of a vote contains the following items, followed The authority section of a vote contains the following items, followed
in turn by the authority's current key certificate: in turn by the authority's current key certificate:
"dir-source" SP nickname SP identity SP address SP IP SP dirport NL "dir-source" SP nickname SP identity SP address SP IP SP dirport SP orport NL
[Exactly once, at start] [Exactly once, at start]
Describes this authority. The nickname is a convenient identifier Describes this authority. The nickname is a convenient identifier
for the authority. The identity is a hex fingerprint of the for the authority. The identity is an uppercase hex fingerprint of the
authority's current identity key. The address is the server's authority's current identity key. The address is the server's
hostname. The IP is the server's current IP address, and dirport hostname. The IP is the server's current IP address, and dirport
is its current directory port. is its current directory port.
@ -772,11 +772,11 @@ $Id$
server's administrator. Administrators should include at least an server's administrator. Administrators should include at least an
email address and a PGP fingerprint. email address and a PGP fingerprint.
The authority section of a consensus contains groups the following The authority section of a consensus contains groups the following items,
items, in the order given, with one group for each authority that in the order given, with one group for each authority that contributed to
contributed to the consensus: the consensus, with groups sorted by authority identity digest:
"dir-source" SP nickname SP address SP IP SP dirport NL "dir-source" SP nickname SP identity SP address SP IP SP dirport SP orport NL
[Exactly once, at start] [Exactly once, at start]
@ -792,15 +792,15 @@ $Id$
[Exactly once.] [Exactly once.]
A hex fingerprint, without spaces, of the authority's current An upper-case hex fingerprint, without spaces, of the authority's
identity key. current identity key.
"vote-digest" SP digest NL "vote-digest" SP digest NL
[Exactly once.] [Exactly once.]
A digest of the vote from the authority that contributed to this A digest of the vote from the authority that contributed to this
consensus. consensus. (Hex, upper-case.)
Each router status entry contains the following items. Router status Each router status entry contains the following items. Router status
entries are sorted in ascending order by identity digest. entries are sorted in ascending order by identity digest.
@ -962,6 +962,9 @@ $Id$
authorities among the voters, breaking ties in favor of the one with authorities among the voters, breaking ties in favor of the one with
the most recent publication time. the most recent publication time.
(XXXX what to do about version, published time, IP, orport, dirport,
nickname, version?)
The signatures at the end of the document appear are sorted in The signatures at the end of the document appear are sorted in
ascending order by identity digest. ascending order by identity digest.

View File

@ -196,6 +196,18 @@ smartlist_string_isin(const smartlist_t *sl, const char *element)
return 0; return 0;
} }
/** DOCDOC */
int
smartlist_string_pos(const smartlist_t *sl, const char *element)
{
int i;
if (!sl) return -1;
for (i=0; i < sl->num_used; i++)
if (strcmp((const char*)sl->list[i],element)==0)
return i;
return -1;
}
/** Return true iff <b>sl</b> has some element E such that /** Return true iff <b>sl</b> has some element E such that
* !strcasecmp(E,<b>element</b>) * !strcasecmp(E,<b>element</b>)
*/ */

View File

@ -40,6 +40,7 @@ void smartlist_string_remove(smartlist_t *sl, const char *element);
int smartlist_isin(const smartlist_t *sl, const void *element) ATTR_PURE; int smartlist_isin(const smartlist_t *sl, const void *element) ATTR_PURE;
int smartlist_string_isin(const smartlist_t *sl, const char *element) int smartlist_string_isin(const smartlist_t *sl, const char *element)
ATTR_PURE; ATTR_PURE;
int smartlist_string_pos(const smartlist_t *, const char *elt) ATTR_PURE;
int smartlist_string_isin_case(const smartlist_t *sl, const char *element) int smartlist_string_isin_case(const smartlist_t *sl, const char *element)
ATTR_PURE; ATTR_PURE;
int smartlist_string_num_isin(const smartlist_t *sl, int num) ATTR_PURE; int smartlist_string_num_isin(const smartlist_t *sl, int num) ATTR_PURE;

View File

@ -1563,10 +1563,12 @@ dirserv_compute_performance_thresholds(routerlist_t *rl)
* failure. */ * failure. */
int int
routerstatus_format_entry(char *buf, size_t buf_len, routerstatus_format_entry(char *buf, size_t buf_len,
routerstatus_t *rs, const char *platform) routerstatus_t *rs, const char *platform,
int first_line_only)
{ {
int r; int r;
struct in_addr in; struct in_addr in;
char *cp;
int f_authority; int f_authority;
char published[ISO_TIME_LEN+1]; char published[ISO_TIME_LEN+1];
@ -1583,16 +1585,24 @@ routerstatus_format_entry(char *buf, size_t buf_len,
f_authority = router_digest_is_trusted_dir(rs->identity_digest); f_authority = router_digest_is_trusted_dir(rs->identity_digest);
r = tor_snprintf(buf, buf_len, r = tor_snprintf(buf, buf_len,
"r %s %s %s %s %s %d %d\n" "r %s %s %s %s %s %d %d\n",
"s%s%s%s%s%s%s%s%s%s%s\n",
rs->nickname, rs->nickname,
identity64, identity64,
digest64, digest64,
published, published,
ipaddr, ipaddr,
(int)rs->or_port, (int)rs->or_port,
(int)rs->dir_port, (int)rs->dir_port);
/* These must stay in alphabetical order. */ if (r<0) {
log_warn(LD_BUG, "Not enough space in buffer.");
return -1;
}
if (first_line_only)
return 0;
cp = buf + strlen(buf);
r = tor_snprintf(cp, buf_len - (cp-buf),
"s%s%s%s%s%s%s%s%s%s%s\n",
/* These must stay in alphabetical order. */
f_authority?" Authority":"", f_authority?" Authority":"",
rs->is_bad_exit?" BadExit":"", rs->is_bad_exit?" BadExit":"",
rs->is_exit?" Exit":"", rs->is_exit?" Exit":"",
@ -1866,7 +1876,8 @@ generate_networkstatus_opinion(int v2)
rs.or_port = ri->or_port; rs.or_port = ri->or_port;
rs.dir_port = ri->dir_port; rs.dir_port = ri->dir_port;
if (routerstatus_format_entry(outp, endp-outp, &rs, ri->platform) < 0) { if (routerstatus_format_entry(outp, endp-outp, &rs,
ri->platform, 0) < 0) {
log_warn(LD_BUG, "Unable to print router status."); log_warn(LD_BUG, "Unable to print router status.");
goto done; goto done;
} }

View File

@ -46,3 +46,494 @@ networkstatus_vote_free(networkstatus_vote_t *ns)
tor_free(ns); tor_free(ns);
} }
/** DOCDOC */
static int
_compare_times(const void **_a, const void **_b)
{
const time_t *a = *_a, *b = *_b;
if (*a<*b)
return -1;
else if (*a>*b)
return 1;
else
return 0;
}
/** DOCDOC */
static int
_compare_ints(const void **_a, const void **_b)
{
const int *a = *_a, *b = *_b;
if (*a<*b)
return -1;
else if (*a>*b)
return 1;
else
return 0;
}
/** DOCDOC */
static time_t
median_time(smartlist_t *times)
{
int idx;
smartlist_sort(times, _compare_times);
idx = (smartlist_len(times)-1)/2;
return *(time_t*)smartlist_get(times, idx);
}
/** DOCDOC */
static int
median_int(smartlist_t *ints)
{
int idx;
smartlist_sort(ints, _compare_ints);
idx = (smartlist_len(ints)-1)/2;
return *(time_t*)smartlist_get(ints, idx);
}
/** DOCDOC */
static int
_compare_votes_by_authority_id(const void **_a, const void **_b)
{
const networkstatus_vote_t *a = *_a, *b = *_b;
return memcmp(a->identity_digest, b->identity_digest, DIGEST_LEN);
}
/** DOCDOC */
static void
get_frequent_members(smartlist_t *out, smartlist_t *in, int min)
{
char *cur = NULL;
int count = 0;
SMARTLIST_FOREACH(in, char *, cp,
{
if (cur && !strcmp(cp, cur)) {
++count;
} else {
if (count > min)
smartlist_add(out, cur);
cur = cp;
count = 1;
}
});
if (count > min)
smartlist_add(out, cur);
}
/** DOCDOC */
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;
}
/** DOCDOC */
static int
compare_votes(const vote_routerstatus_t *a, const vote_routerstatus_t *b)
{
int r;
if ((r = memcmp(a->status.descriptor_digest, b->status.descriptor_digest,
DIGEST_LEN)))
return r;
if ((r = (b->status.published_on - a->status.published_on)))
return r;
if ((r = (((int)b->status.or_port) - ((int)a->status.or_port))))
return r;
if ((r = (((int)b->status.dir_port) - ((int)a->status.dir_port))))
return r;
return 0;
}
/** DOCDOC */
static int
_compare_votes(const void **_a, const void **_b)
{
const vote_routerstatus_t *a = *_a, *b = *_b;
return compare_votes(a,b);
}
/** DOCDOC */
static vote_routerstatus_t *
compute_routerstatus_consensus(smartlist_t *votes)
{
vote_routerstatus_t *most = NULL, *cur = NULL;
int most_n = 0, cur_n = 0;
time_t most_published = 0;
smartlist_sort(votes, _compare_votes);
SMARTLIST_FOREACH(votes, vote_routerstatus_t *, rs,
{
if (cur && !compare_votes(cur, rs)) {
++cur_n;
} else {
if (cur_n > most_n ||
(cur && cur_n == most_n && cur->status.published_on > most_published)) {
most = cur;
most_n = cur_n;
most_published = cur->status.published_on;
}
cur_n = 1;
cur = rs;
}
});
if (cur_n > most_n ||
(cur && cur_n == most_n && cur->status.published_on > most_published)) {
most = cur;
most_n = cur_n;
most_published = cur->status.published_on;
}
tor_assert(most);
return most;
}
/** DOCDOC */
char *
networkstatus_compute_consensus(smartlist_t *votes)
{
int n_rs=0;
smartlist_t *chunks;
time_t valid_after, fresh_until, valid_until;
int vote_seconds, dist_seconds;
char *client_versions = NULL, *server_versions = NULL;
smartlist_t *flags;
int total_authorities = smartlist_len(votes); /*XXXX020 not right. */
if (!smartlist_len(votes)) {
log_warn(LD_DIR, "Can't compute a consensus from no votes.");
return NULL;
}
/* XXXX020 somebody needs to check vote authority. It could be this
* function, it could be somebody else. */
flags = smartlist_create();
/* Compute medians of time-related things, and figure out how many
* routers we might need to talk about. */
{
smartlist_t *va_times = smartlist_create();
smartlist_t *fu_times = smartlist_create();
smartlist_t *vu_times = smartlist_create();
smartlist_t *votesec_list = smartlist_create();
smartlist_t *distsec_list = smartlist_create();
int n_versioning_clients = 0, n_versioning_servers = 0;
smartlist_t *combined_client_versions = smartlist_create();
smartlist_t *combined_server_versions = smartlist_create();
int j;
SMARTLIST_FOREACH(votes, networkstatus_vote_t *, v,
{
n_rs += smartlist_len(v->routerstatus_list);
smartlist_add(va_times, &v->valid_after);
smartlist_add(fu_times, &v->fresh_until);
smartlist_add(vu_times, &v->valid_until);
smartlist_add(votesec_list, &v->vote_seconds);
smartlist_add(distsec_list, &v->dist_seconds);
if (v->client_versions) {
smartlist_t *cv = smartlist_create();
++n_versioning_clients;
smartlist_split_string(cv, v->client_versions, ",",
SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
sort_version_list(cv, 1);
smartlist_add_all(combined_client_versions, cv);
smartlist_free(cv); /* elements get freed later. */
}
if (v->server_versions) {
smartlist_t *sv = smartlist_create();
++n_versioning_servers;
smartlist_split_string(sv, v->server_versions, ",",
SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
sort_version_list(sv, 1);
smartlist_add_all(combined_server_versions, sv);
smartlist_free(sv); /* elements get freed later. */
}
for (j=0; v->known_flags[j]; ++j)
smartlist_add(flags, tor_strdup(v->known_flags[j]));
});
valid_after = median_time(va_times);
fresh_until = median_time(fu_times);
valid_until = median_time(vu_times);
vote_seconds = median_int(votesec_list);
dist_seconds = median_int(distsec_list);
for (j = 0; j < 2; ++j) {
smartlist_t *lst =
j ? combined_server_versions : combined_client_versions;
int min = (j ? n_versioning_servers : n_versioning_clients) / 2;
smartlist_t *good = smartlist_create();
char *res;
sort_version_list(lst, 0);
get_frequent_members(good, lst, min);
res = smartlist_join_strings(good, ",", 0, NULL);
if (j)
server_versions = res;
else
client_versions = res;
SMARTLIST_FOREACH(lst, char *, cp, tor_free(cp));
smartlist_free(good);
smartlist_free(lst);
}
smartlist_sort_strings(flags);
smartlist_uniq_strings(flags);
smartlist_free(va_times);
smartlist_free(fu_times);
smartlist_free(vu_times);
smartlist_free(votesec_list);
smartlist_free(distsec_list);
}
chunks = smartlist_create();
{
char buf[1024];
char va_buf[ISO_TIME_LEN+1], fu_buf[ISO_TIME_LEN+1],
vu_buf[ISO_TIME_LEN+1];
char *flaglist;
format_iso_time(va_buf, valid_after);
format_iso_time(fu_buf, fresh_until);
format_iso_time(vu_buf, valid_until);
flaglist = smartlist_join_strings(flags, " ", 0, NULL);
tor_snprintf(buf, sizeof(buf),
"network-status-version 3\n"
"vote-status consensus\n"
"valid-after %s\n"
"fresh-until %s\n"
"valid-until %s\n"
"voting-delay %d %d\n"
"client-versions %s\n"
"server-versions %s\n"
"known-flags %s\n",
va_buf, fu_buf, vu_buf,
vote_seconds, dist_seconds,
client_versions, server_versions, flaglist);
smartlist_add(chunks, tor_strdup(buf));
tor_free(flaglist);
}
/* Sort the votes. */
smartlist_sort(votes, _compare_votes_by_authority_id);
/* Add the authority sections. */
SMARTLIST_FOREACH(votes, networkstatus_vote_t *, v,
{
char buf[1024];
struct in_addr in;
char ip[INET_NTOA_BUF_LEN];
char fingerprint[HEX_DIGEST_LEN+1];
char votedigest[HEX_DIGEST_LEN+1];
in.s_addr = htonl(v->addr);
tor_inet_ntoa(&in, ip, sizeof(ip));
base16_encode(fingerprint, sizeof(fingerprint), v->identity_digest,
DIGEST_LEN);
base16_encode(votedigest, sizeof(votedigest), v->vote_digest, DIGEST_LEN);
tor_snprintf(buf, sizeof(buf),
"dir-source %s %s %s %s %d %d\n"
"contact %s\n"
"vote-digest %s\n",
v->nickname, fingerprint, v->address, ip, v->dir_port,
v->or_port,
v->contact,
votedigest);
smartlist_add(chunks, tor_strdup(buf));
});
/* Add the actual router entries. */
{
/* document these XXXX020 */
int *index;
int *size;
int *flag_counts;
int i;
smartlist_t *matching_descs = smartlist_create();
smartlist_t *chosen_flags = smartlist_create();
smartlist_t *versions = smartlist_create();
int *n_voter_flags; /* n_voter_flags[j] is the number of flags that
* votes[j] knows about. */
int *n_flag_voters; /* n_flag_voters[f] is the number of votes that care
* about flags[f]. */
int **flag_map; /* flag_map[j][b] is an index f such that flag_map[f]
* is the same flag as votes[j]->known_flags[b]. */
index = tor_malloc_zero(sizeof(int)*smartlist_len(votes));
size = tor_malloc_zero(sizeof(int)*smartlist_len(votes));
n_voter_flags = tor_malloc_zero(sizeof(int) * smartlist_len(votes));
n_flag_voters = tor_malloc_zero(sizeof(int) * smartlist_len(flags));
flag_map = tor_malloc_zero(sizeof(int*) * smartlist_len(votes));
SMARTLIST_FOREACH(votes, networkstatus_vote_t *, v,
{
for (i = 0; v->known_flags[i]; ++i) {
int p = smartlist_string_pos(flags, v->known_flags[i]);
tor_assert(p >= 0);
flag_map[v_sl_idx][i] = p;
++n_flag_voters[p];
/* XXXX020 somebody needs to make sure that there are no duplicate
* entries in anybody's flag list. */
}
tor_assert(!v->known_flags[i]);
n_voter_flags[v_sl_idx] = i;
size[v_sl_idx] = smartlist_len(v->routerstatus_list);
});
/* Now go through all the votes */
flag_counts = tor_malloc(sizeof(int) * smartlist_len(flags));
while (1) {
vote_routerstatus_t *rs;
routerstatus_t rs_out;
const char *lowest_id = NULL;
const char *chosen_version;
int n_listing = 0;
int i;
char buf[256];
SMARTLIST_FOREACH(votes, networkstatus_vote_t *, v, {
if (index[v_sl_idx] < size[v_sl_idx]) {
rs = smartlist_get(v->routerstatus_list, index[v_sl_idx]);
if (!lowest_id ||
memcmp(rs->status.identity_digest, lowest_id, DIGEST_LEN) < 0)
lowest_id = rs->status.identity_digest;
}
});
if (!lowest_id) /* we're out of routers. */
break;
memset(flag_counts, 0, sizeof(int)*smartlist_len(flags));
smartlist_clear(matching_descs);
smartlist_clear(chosen_flags);
smartlist_clear(versions);
/* Okay, go through all the entries for this digest. */
SMARTLIST_FOREACH(votes, networkstatus_vote_t *, v, {
if (index[v_sl_idx] >= size[v_sl_idx])
continue; /* out of entries. */
rs = smartlist_get(v->routerstatus_list, index[v_sl_idx]);
if (memcmp(rs->status.identity_digest, lowest_id, DIGEST_LEN))
continue; /* doesn't include this router. */
/* At this point, we know that we're looking at a routersatus with
* identity "lowest".
*/
++index[v_sl_idx];
++n_listing;
smartlist_add(matching_descs, rs);
if (rs->version && rs->version[0])
smartlist_add(versions, rs->version);
/* Tally up all the flags. */
for (i = 0; i < n_voter_flags[v_sl_idx]; ++i) {
if (rs->flags & (U64_LITERAL(1) << i))
++flag_counts[flag_map[v_sl_idx][i]];
/* XXXX020 named is special. */
}
});
/* We don't include this router at all unless more than half of
* the authorities we believe in list it. */
if (n_listing <= total_authorities/2)
continue;
/* Figure out the most popular opinion of what the most recent
* routerinfo and its contents are. */
rs = compute_routerstatus_consensus(matching_descs);
/* 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);
memcpy(rs_out.descriptor_digest, rs->status.descriptor_digest,
DIGEST_LEN);
rs_out.published_on = rs->status.published_on;
rs_out.dir_port = rs->status.dir_port;
rs_out.or_port = rs->status.or_port;
/* Set the flags. */
smartlist_add(chosen_flags, (char*)"s"); /* for the start of the line. */
SMARTLIST_FOREACH(flags, const char *, fl,
{
if (flag_counts[fl_sl_idx] > n_flag_voters[fl_sl_idx]/2)
smartlist_add(chosen_flags, (char*)fl);
/* XXXX020 named is special. */
});
/* XXXX020 set the nickname! */
/* Pick the version. */
if (smartlist_len(versions)) {
sort_version_list(versions, 0);
chosen_version = get_most_frequent_member(versions);
} else {
chosen_version = NULL;
}
/* Okay!! Now we can write the descriptor... */
/* First line goes into "buf". */
routerstatus_format_entry(buf, sizeof(buf), &rs_out, NULL, 1);
smartlist_add(chunks, tor_strdup(buf));
/* Second line is all flags. The "\n" is missing. */
smartlist_add(chunks,
smartlist_join_strings(chosen_flags, " ", 0, NULL));
/* Now the version line. */
if (chosen_version) {
/* XXXX020 fails on very big version. */
tor_snprintf(buf, sizeof(buf), "\nv %s\n", chosen_version);
smartlist_add(chunks, tor_strdup(buf));
} else {
smartlist_add(chunks, tor_strdup("\n"));
}
/* And the loop is over and we move on to the next router */
}
tor_free(index);
tor_free(size);
tor_free(n_voter_flags);
tor_free(n_flag_voters);
for (i = 0; i < smartlist_len(votes); ++i)
tor_free(flag_map[i]);
tor_free(flag_map);
tor_free(flag_counts);
smartlist_free(matching_descs);
smartlist_free(chosen_flags);
smartlist_free(versions);
}
/* Concatenate everything. */
/* Add a signature. */
/* Free everything. */
tor_free(client_versions);
tor_free(server_versions);
return NULL;
}

View File

@ -1324,6 +1324,8 @@ typedef struct networkstatus_vote_t {
char *contact; char *contact;
char vote_digest[DIGEST_LEN];
smartlist_t *routerstatus_list; smartlist_t *routerstatus_list;
} networkstatus_vote_t; } networkstatus_vote_t;
@ -2705,13 +2707,15 @@ int dirserv_statuses_are_old(smartlist_t *fps, time_t cutoff);
size_t dirserv_estimate_data_size(smartlist_t *fps, int is_serverdescs, size_t dirserv_estimate_data_size(smartlist_t *fps, int is_serverdescs,
int compressed); int compressed);
int routerstatus_format_entry(char *buf, size_t buf_len, int routerstatus_format_entry(char *buf, size_t buf_len,
routerstatus_t *rs, const char *platform); routerstatus_t *rs, const char *platform,
int first_line_only);
void dirserv_free_all(void); void dirserv_free_all(void);
void cached_dir_decref(cached_dir_t *d); void cached_dir_decref(cached_dir_t *d);
/********************************* dirvote.c ************************/ /********************************* dirvote.c ************************/
void networkstatus_vote_free(networkstatus_vote_t *ns); void networkstatus_vote_free(networkstatus_vote_t *ns);
char *networkstatus_compute_consensus(smartlist_t *votes);
/********************************* dns.c ***************************/ /********************************* dns.c ***************************/

View File

@ -3718,7 +3718,7 @@ compute_recommended_versions(time_t now, int client,
} else { } else {
if (n_seen > n_versioning/2 && current) if (n_seen > n_versioning/2 && current)
smartlist_add(recommended, current); smartlist_add(recommended, current);
n_seen = 0; n_seen = 0; /* XXXX020 shouldn't this be 1? */
current = cp; current = cp;
} }
}); });
@ -5071,7 +5071,7 @@ char *
networkstatus_getinfo_helper_single(routerstatus_t *rs) networkstatus_getinfo_helper_single(routerstatus_t *rs)
{ {
char buf[256]; char buf[256];
routerstatus_format_entry(buf, sizeof(buf), rs, NULL); routerstatus_format_entry(buf, sizeof(buf), rs, NULL, 0);
return tor_strdup(buf); return tor_strdup(buf);
} }

View File

@ -303,7 +303,7 @@ static token_rule_t networkstatus_vote_token_table[] = {
T0N("opt", K_OPT, CONCAT_ARGS, OBJ_OK ), T0N("opt", K_OPT, CONCAT_ARGS, OBJ_OK ),
T1( "contact", K_CONTACT, CONCAT_ARGS, NO_OBJ ), T1( "contact", K_CONTACT, CONCAT_ARGS, NO_OBJ ),
T1( "dir-source", K_DIR_SOURCE, GE(3), NO_OBJ ), T1( "dir-source", K_DIR_SOURCE, GE(6), NO_OBJ ),
T1( "dir-options", K_DIR_OPTIONS, ARGS, NO_OBJ ), T1( "dir-options", K_DIR_OPTIONS, ARGS, NO_OBJ ),
T1( "known-flags", K_KNOWN_FLAGS, CONCAT_ARGS, NO_OBJ ), T1( "known-flags", K_KNOWN_FLAGS, CONCAT_ARGS, NO_OBJ ),
T01("client-versions", K_CLIENT_VERSIONS, CONCAT_ARGS, NO_OBJ ), T01("client-versions", K_CLIENT_VERSIONS, CONCAT_ARGS, NO_OBJ ),
@ -351,7 +351,6 @@ static token_rule_t networkstatus_vote_footer_token_table[] = {
END_OF_TABLE END_OF_TABLE
}; };
#undef T #undef T
/* static function prototypes */ /* static function prototypes */
@ -1798,6 +1797,7 @@ networkstatus_parse_vote_from_string(const char *s)
directory_token_t *tok; directory_token_t *tok;
int ok; int ok;
struct in_addr in; struct in_addr in;
int i;
if (router_get_networkstatus_v3_hash(s, ns_digest)) { if (router_get_networkstatus_v3_hash(s, ns_digest)) {
log_warn(LD_DIR, "Unable to compute digest of network-status"); log_warn(LD_DIR, "Unable to compute digest of network-status");
@ -1872,7 +1872,7 @@ networkstatus_parse_vote_from_string(const char *s)
tok = find_first_by_keyword(tokens, K_DIR_SOURCE); tok = find_first_by_keyword(tokens, K_DIR_SOURCE);
tor_assert(tok); tor_assert(tok);
tor_assert(tok->n_args >= 5); tor_assert(tok->n_args >= 6);
ns->nickname = tor_strdup(tok->args[0]); ns->nickname = tor_strdup(tok->args[0]);
if (strlen(tok->args[1]) != HEX_DIGEST_LEN || if (strlen(tok->args[1]) != HEX_DIGEST_LEN ||
base16_decode(ns->identity_digest, sizeof(ns->identity_digest), base16_decode(ns->identity_digest, sizeof(ns->identity_digest),
@ -1891,6 +1891,10 @@ networkstatus_parse_vote_from_string(const char *s)
(int) tor_parse_long(tok->args[4], 10, 0, 65535, &ok, NULL); (int) tor_parse_long(tok->args[4], 10, 0, 65535, &ok, NULL);
if (!ok) if (!ok)
goto err; goto err;
ns->or_port = (uint64_t)
(int) tor_parse_long(tok->args[5], 10, 0, 65535, &ok, NULL);
if (!ok)
goto err;
tok = find_first_by_keyword(tokens, K_CONTACT); tok = find_first_by_keyword(tokens, K_CONTACT);
if (tok) { if (tok) {
@ -1910,6 +1914,16 @@ networkstatus_parse_vote_from_string(const char *s)
tor_free(rs); tor_free(rs);
} }
} }
for (i = 1; i < smartlist_len(ns->routerstatus_list); ++i) {
vote_routerstatus_t *a = smartlist_get(ns->routerstatus_list, i-1);
vote_routerstatus_t *b = smartlist_get(ns->routerstatus_list, i);
if (memcmp(a->status.identity_digest, b->status.identity_digest,
DIGEST_LEN) >= 0) {
log_warn(LD_DIR, "Vote networkstatus entries not sorted by identity "
"digest");
goto err;
}
}
/* Parse footer; check signature. */ /* Parse footer; check signature. */
footer_tokens = smartlist_create(); footer_tokens = smartlist_create();
@ -1938,6 +1952,7 @@ networkstatus_parse_vote_from_string(const char *s)
"network-status vote.", escaped(tok->args[1])); "network-status vote.", escaped(tok->args[1]));
goto err; goto err;
} }
memcpy(ns->vote_digest, declared_digest, DIGEST_LEN);
if (memcmp(declared_identity, ns->cert->cache_info.identity_digest, if (memcmp(declared_identity, ns->cert->cache_info.identity_digest,
DIGEST_LEN)) { DIGEST_LEN)) {
log_warn(LD_DIR, "Digest mismatch between declared and actual on " log_warn(LD_DIR, "Digest mismatch between declared and actual on "
@ -1974,11 +1989,9 @@ networkstatus_parse_vote_from_string(const char *s)
smartlist_free(footer_tokens); smartlist_free(footer_tokens);
} }
return ns; return ns;
} }
/** Parse the addr policy in the string <b>s</b> and return it. If /** Parse the addr policy in the string <b>s</b> and return it. If
* assume_action is nonnegative, then insert its action (ADDR_POLICY_ACCEPT or * assume_action is nonnegative, then insert its action (ADDR_POLICY_ACCEPT or
* ADDR_POLICY_REJECT) for items that specify no action. * ADDR_POLICY_REJECT) for items that specify no action.