📄 dirvote.c
字号:
/* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
* Copyright (c) 2007-2008, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/* $Id$ */
const char dirvote_c_id[] =
"$Id$";
#define DIRVOTE_PRIVATE
#include "or.h"
/**
* \file dirvote.c
* \brief Functions to compute directory consensus, and schedule voting.
**/
static int dirvote_add_signatures_to_pending_consensus(
const char *detached_signatures_body,
const char **msg_out);
static char *list_v3_auth_ids(void);
static void dirvote_fetch_missing_votes(void);
static void dirvote_fetch_missing_signatures(void);
static int dirvote_perform_vote(void);
static void dirvote_clear_votes(int all_votes);
static int dirvote_compute_consensus(void);
static int dirvote_publish_consensus(void);
/* =====
* Voting
* =====*/
/** 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. */
char *
format_networkstatus_vote(crypto_pk_env_t *private_signing_key,
networkstatus_t *v3_ns)
{
size_t len;
char *status = NULL;
const char *client_versions = NULL, *server_versions = NULL;
char *outp, *endp;
char fingerprint[FINGERPRINT_LEN+1];
char ipaddr[INET_NTOA_BUF_LEN];
char digest[DIGEST_LEN];
struct in_addr in;
uint32_t addr;
routerlist_t *rl = router_get_routerlist();
char *version_lines = NULL;
networkstatus_voter_info_t *voter;
tor_assert(private_signing_key);
voter = smartlist_get(v3_ns->voters, 0);
addr = voter->addr;
in.s_addr = htonl(addr);
tor_inet_ntoa(&in, ipaddr, sizeof(ipaddr));
base16_encode(fingerprint, sizeof(fingerprint),
v3_ns->cert->cache_info.identity_digest, DIGEST_LEN);
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) {
tor_snprintf(cp, v_len-(cp-version_lines),
"client-versions %s\n", client_versions);
cp += strlen(cp);
}
if (server_versions)
tor_snprintf(cp, v_len-(cp-version_lines),
"server-versions %s\n", server_versions);
} else {
version_lines = tor_strdup("");
}
len = 8192;
len += strlen(version_lines);
len += (RS_ENTRY_LEN)*smartlist_len(rl->routers);
len += v3_ns->cert->cache_info.signed_descriptor_len;
status = tor_malloc(len);
{
char published[ISO_TIME_LEN+1];
char va[ISO_TIME_LEN+1];
char fu[ISO_TIME_LEN+1];
char vu[ISO_TIME_LEN+1];
char *flags = smartlist_join_strings(v3_ns->known_flags, " ", 0, NULL);
authority_cert_t *cert = v3_ns->cert;
format_iso_time(published, v3_ns->published);
format_iso_time(va, v3_ns->valid_after);
format_iso_time(fu, v3_ns->fresh_until);
format_iso_time(vu, v3_ns->valid_until);
tor_assert(cert);
tor_snprintf(status, len,
"network-status-version 3\n"
"vote-status vote\n"
"consensus-methods 1 2\n"
"published %s\n"
"valid-after %s\n"
"fresh-until %s\n"
"valid-until %s\n"
"voting-delay %d %d\n"
"%s" /* versions */
"known-flags %s\n"
"dir-source %s %s %s %s %d %d\n"
"contact %s\n",
published, va, fu, vu,
v3_ns->vote_seconds, v3_ns->dist_seconds,
version_lines,
flags,
voter->nickname, fingerprint, voter->address,
ipaddr, voter->dir_port, voter->or_port, voter->contact);
tor_free(flags);
outp = status + strlen(status);
endp = status + len;
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_FOREACH(v3_ns->routerstatus_list, vote_routerstatus_t *, vrs,
{
if (routerstatus_format_entry(outp, endp-outp, &vrs->status,
vrs->version, 0) < 0) {
log_warn(LD_BUG, "Unable to print router status.");
goto err;
}
outp += strlen(outp);
});
{
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);
}
if (router_get_networkstatus_v3_hash(status, digest)<0)
goto err;
note_crypto_pk_op(SIGN_DIR);
if (router_append_dirobj_signature(outp,endp-outp,digest,
private_signing_key)<0) {
log_warn(LD_BUG, "Unable to sign networkstatus vote.");
goto err;
}
{
networkstatus_t *v;
if (!(v = networkstatus_parse_vote_from_string(status, NULL, 1))) {
log_err(LD_BUG,"Generated a networkstatus vote we couldn't parse: "
"<<%s>>", status);
goto err;
}
networkstatus_vote_free(v);
}
goto done;
err:
tor_free(status);
done:
tor_free(version_lines);
return status;
}
/* =====
* Consensus generation
* ===== */
/** Given a vote <b>vote</b> (not a consensus!), return its associated
* networkstatus_voter_info_t. */
static networkstatus_voter_info_t *
get_voter(const networkstatus_t *vote)
{
tor_assert(vote);
tor_assert(vote->is_vote);
tor_assert(vote->voters);
tor_assert(smartlist_len(vote->voters) == 1);
return smartlist_get(vote->voters, 0);
}
/** Helper for sorting networkstatus_t votes (not consensuses) by the
* hash of their voters' identity digests. */
static int
_compare_votes_by_authority_id(const void **_a, const void **_b)
{
const networkstatus_t *a = *_a, *b = *_b;
return memcmp(get_voter(a)->identity_digest,
get_voter(b)->identity_digest, DIGEST_LEN);
}
/** Given a sorted list of strings <b>in</b>, add every member to <b>out</b>
* that occurs more than <b>min</b> times. */
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);
}
/** 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;
}
/** 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.
*/
static int
compare_vote_rs(const vote_routerstatus_t *a, const vote_routerstatus_t *b)
{
int r;
if ((r = memcmp(a->status.identity_digest, b->status.identity_digest,
DIGEST_LEN)))
return r;
if ((r = memcmp(a->status.descriptor_digest, b->status.descriptor_digest,
DIGEST_LEN)))
return r;
if ((r = (int)(b->status.published_on - a->status.published_on)))
return r;
if ((r = strcmp(b->status.nickname, a->status.nickname)))
return r;
if ((r = (((int)b->status.addr) - ((int)a->status.addr))))
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;
}
/** Helper for sorting routerlists based on compare_vote_rs. */
static int
_compare_vote_rs(const void **_a, const void **_b)
{
const vote_routerstatus_t *a = *_a, *b = *_b;
return compare_vote_rs(a,b);
}
/** Given a list of vote_routerstatus_t, all for the same router identity,
* return whichever is most frequent, breaking ties in favor of more
* recently published vote_routerstatus_t.
*/
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_vote_rs);
SMARTLIST_FOREACH(votes, vote_routerstatus_t *, rs,
{
if (cur && !compare_vote_rs(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;
}
/** Given a list of strings in <b>lst</b>, set the DIGEST_LEN-byte digest at
* <b>digest_out</b> to the hash of the concatenation of those strings. */
static void
hash_list_members(char *digest_out, smartlist_t *lst)
{
crypto_digest_env_t *d = crypto_new_digest_env();
SMARTLIST_FOREACH(lst, const char *, cp,
crypto_digest_add_bytes(d, cp, strlen(cp)));
crypto_digest_get_digest(d, digest_out, DIGEST_LEN);
crypto_free_digest_env(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.) */
static int
_cmp_int_strings(const void **_a, const void **_b)
{
const char *a = *_a, *b = *_b;
int ai = (int)tor_parse_long(a, 10, 1, INT_MAX, NULL, NULL);
int bi = (int)tor_parse_long(b, 10, 1, INT_MAX, NULL, NULL);
if (ai<bi) {
return -1;
} else if (ai==bi) {
if (ai == 0) /* Parsing failed. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -