📄 dirvote.c
字号:
tor_strdup(hex_str(ds->v3_identity_digest, DIGEST_LEN))));
keys = smartlist_join_strings(known_v3_keys, ", ", 0, NULL);
SMARTLIST_FOREACH(known_v3_keys, char *, cp, tor_free(cp));
smartlist_free(known_v3_keys);
return keys;
}
/** Called when we have received a networkstatus vote in <b>vote_body</b>.
* Parse and validate it, and on success store it as a pending vote (which we
* then return). Return NULL on failure. Sets *<b>msg_out</b> and
* *<b>status_out</b> to an HTTP response and status code. (V3 authority
* only) */
pending_vote_t *
dirvote_add_vote(const char *vote_body, const char **msg_out, int *status_out)
{
networkstatus_t *vote;
networkstatus_voter_info_t *vi;
trusted_dir_server_t *ds;
pending_vote_t *pending_vote = NULL;
const char *end_of_vote = NULL;
int any_failed = 0;
tor_assert(vote_body);
tor_assert(msg_out);
tor_assert(status_out);
if (!pending_vote_list)
pending_vote_list = smartlist_create();
*status_out = 0;
*msg_out = NULL;
again:
vote = networkstatus_parse_vote_from_string(vote_body, &end_of_vote, 1);
if (!end_of_vote)
end_of_vote = vote_body + strlen(vote_body);
if (!vote) {
log_warn(LD_DIR, "Couldn't parse vote: length was %d",
(int)strlen(vote_body));
*msg_out = "Unable to parse vote";
goto err;
}
tor_assert(smartlist_len(vote->voters) == 1);
vi = get_voter(vote);
tor_assert(vi->good_signature == 1);
ds = trusteddirserver_get_by_v3_auth_digest(vi->identity_digest);
if (!ds) {
char *keys = list_v3_auth_ids();
log_warn(LD_DIR, "Got a vote from an authority (nickname %s, address %s) "
"with authority key ID %s. "
"This key ID is not recognized. Known v3 key IDs are: %s",
vi->nickname, vi->address,
hex_str(vi->identity_digest, DIGEST_LEN), keys);
tor_free(keys);
*msg_out = "Vote not from a recognized v3 authority";
goto err;
}
tor_assert(vote->cert);
if (!authority_cert_get_by_digests(vote->cert->cache_info.identity_digest,
vote->cert->signing_key_digest)) {
/* Hey, it's a new cert! */
trusted_dirs_load_certs_from_string(
vote->cert->cache_info.signed_descriptor_body,
0 /* from_store */, 1 /*flush*/);
if (!authority_cert_get_by_digests(vote->cert->cache_info.identity_digest,
vote->cert->signing_key_digest)) {
log_warn(LD_BUG, "We added a cert, but still couldn't find it.");
}
}
/* Is it for the right period? */
if (vote->valid_after != voting_schedule.interval_starts) {
char tbuf1[ISO_TIME_LEN+1], tbuf2[ISO_TIME_LEN+1];
format_iso_time(tbuf1, vote->valid_after);
format_iso_time(tbuf2, voting_schedule.interval_starts);
log_warn(LD_DIR, "Rejecting vote from %s with valid-after time of %s; "
"we were expecting %s", vi->address, tbuf1, tbuf2);
*msg_out = "Bad valid-after time";
goto err;
}
/* Now see whether we already h<ave a vote from this authority.*/
SMARTLIST_FOREACH(pending_vote_list, pending_vote_t *, v, {
if (! memcmp(v->vote->cert->cache_info.identity_digest,
vote->cert->cache_info.identity_digest,
DIGEST_LEN)) {
networkstatus_voter_info_t *vi_old = get_voter(v->vote);
if (!memcmp(vi_old->vote_digest, vi->vote_digest, DIGEST_LEN)) {
/* Ah, it's the same vote. Not a problem. */
log_info(LD_DIR, "Discarding a vote we already have.");
if (*status_out < 200)
*status_out = 200;
goto discard;
} else if (v->vote->published < vote->published) {
log_notice(LD_DIR, "Replacing an older pending vote from this "
"directory.");
cached_dir_decref(v->vote_body);
networkstatus_vote_free(v->vote);
v->vote_body = new_cached_dir(tor_strndup(vote_body,
end_of_vote-vote_body),
vote->published);
v->vote = vote;
if (end_of_vote &&
!strcmpstart(end_of_vote, "network-status-version"))
goto again;
if (*status_out < 200)
*status_out = 200;
if (!*msg_out)
*msg_out = "OK";
return v;
} else {
*msg_out = "Already have a newer pending vote";
goto err;
}
}
});
pending_vote = tor_malloc_zero(sizeof(pending_vote_t));
pending_vote->vote_body = new_cached_dir(tor_strndup(vote_body,
end_of_vote-vote_body),
vote->published);
pending_vote->vote = vote;
smartlist_add(pending_vote_list, pending_vote);
if (!strcmpstart(end_of_vote, "network-status-version ")) {
vote_body = end_of_vote;
goto again;
}
goto done;
err:
any_failed = 1;
if (!*msg_out)
*msg_out = "Error adding vote";
if (*status_out < 400)
*status_out = 400;
discard:
if (vote)
networkstatus_vote_free(vote);
if (end_of_vote && !strcmpstart(end_of_vote, "network-status-version ")) {
vote_body = end_of_vote;
goto again;
}
done:
if (*status_out < 200)
*status_out = 200;
if (!*msg_out) {
if (!any_failed && !pending_vote) {
*msg_out = "Duplicate discarded";
} else {
*msg_out = "ok";
}
}
return any_failed ? NULL : pending_vote;
}
/** Try to compute a v3 networkstatus consensus from the currently pending
* votes. Return 0 on success, -1 on failure. Store the consensus in
* pending_consensus: it won't be ready to be published until we have
* everybody else's signatures collected too. (V3 Authoritity only) */
static int
dirvote_compute_consensus(void)
{
/* Have we got enough votes to try? */
int n_votes, n_voters;
smartlist_t *votes = NULL;
char *consensus_body = NULL, *signatures = NULL;
networkstatus_t *consensus = NULL;
authority_cert_t *my_cert;
if (!pending_vote_list)
pending_vote_list = smartlist_create();
n_voters = get_n_authorities(V3_AUTHORITY);
n_votes = smartlist_len(pending_vote_list);
if (n_votes <= n_voters/2) {
log_warn(LD_DIR, "We don't have enough votes to generate a consensus: "
"%d of %d", n_votes, n_voters/2);
goto err;
}
if (!(my_cert = get_my_v3_authority_cert())) {
log_warn(LD_DIR, "Can't generate consensus without a certificate.");
goto err;
}
votes = smartlist_create();
SMARTLIST_FOREACH(pending_vote_list, pending_vote_t *, v,
smartlist_add(votes, v->vote));
consensus_body = networkstatus_compute_consensus(
votes, n_voters,
my_cert->identity_key,
get_my_v3_authority_signing_key());
if (!consensus_body) {
log_warn(LD_DIR, "Couldn't generate a consensus at all!");
goto err;
}
consensus = networkstatus_parse_vote_from_string(consensus_body, NULL, 0);
if (!consensus) {
log_warn(LD_DIR, "Couldn't parse consensus we generated!");
goto err;
}
/* 'Check' our own signature, to mark it valid. */
networkstatus_check_consensus_signature(consensus, -1);
signatures = networkstatus_get_detached_signatures(consensus);
if (!signatures) {
log_warn(LD_DIR, "Couldn't extract signatures.");
goto err;
}
tor_free(pending_consensus_body);
pending_consensus_body = consensus_body;
tor_free(pending_consensus_signatures);
pending_consensus_signatures = signatures;
if (pending_consensus)
networkstatus_vote_free(pending_consensus);
pending_consensus = consensus;
if (pending_consensus_signature_list) {
int n_sigs = 0;
/* we may have gotten signatures for this consensus before we built
* it ourself. Add them now. */
SMARTLIST_FOREACH(pending_consensus_signature_list, char *, sig,
{
const char *msg = NULL;
int r = dirvote_add_signatures_to_pending_consensus(sig, &msg);
if (r >= 0)
n_sigs += r;
else
log_warn(LD_DIR,
"Could not add queued signature to new consensus: %s",
msg);
tor_free(sig);
});
if (n_sigs)
log_notice(LD_DIR, "Added %d pending signatures while building "
"consensus.", n_sigs);
smartlist_clear(pending_consensus_signature_list);
}
log_notice(LD_DIR, "Consensus computed; uploading signature(s)");
directory_post_to_dirservers(DIR_PURPOSE_UPLOAD_SIGNATURES,
ROUTER_PURPOSE_GENERAL,
V3_AUTHORITY,
pending_consensus_signatures,
strlen(pending_consensus_signatures), 0);
log_notice(LD_DIR, "Signature(s) posted.");
return 0;
err:
if (votes)
smartlist_free(votes);
tor_free(consensus_body);
tor_free(signatures);
networkstatus_vote_free(consensus);
return -1;
}
/** Helper: we just got the <b>detached_signatures_body</b> sent to us as
* signatures on the currently pending consensus. Add them to the consensus
* as appropriate. Return the number of signatures added. (?) */
static int
dirvote_add_signatures_to_pending_consensus(
const char *detached_signatures_body,
const char **msg_out)
{
ns_detached_signatures_t *sigs = NULL;
int r = -1;
tor_assert(detached_signatures_body);
tor_assert(msg_out);
/* Only call if we have a pending consensus right now. */
tor_assert(pending_consensus);
tor_assert(pending_consensus_body);
tor_assert(pending_consensus_signatures);
*msg_out = NULL;
if (!(sigs = networkstatus_parse_detached_signatures(
detached_signatures_body, NULL))) {
*msg_out = "Couldn't parse detached signatures.";
goto err;
}
log_info(LD_DIR, "Have %d signatures for adding to consensus.",
smartlist_len(sigs->signatures));
r = networkstatus_add_detached_signatures(pending_consensus,
sigs, msg_out);
log_info(LD_DIR,"Added %d signatures to consensus.", r);
if (r >= 0) {
char *new_detached =
networkstatus_get_detached_signatures(pending_consensus);
const char *src;
char *dst, *dst_end;
size_t new_consensus_len =
strlen(pending_consensus_body) + strlen(new_detached) + 1;
pending_consensus_body = tor_realloc(pending_consensus_body,
new_consensus_len);
dst_end = pending_consensus_body + new_consensus_len;
dst = strstr(pending_consensus_body, "directory-signature ");
tor_assert(dst);
src = strstr(new_detached, "directory-signature ");
tor_assert(src);
strlcpy(dst, src, dst_end-dst);
/* We remove this block once it has failed to crash for a while. But
* unless it shows up in profiles, we're probably better leaving it in,
* just in case we break detached signature processing at some point. */
{
ns_detached_signatures_t *sigs =
networkstatus_parse_detached_signatures(new_detached, NULL);
networkstatus_t *v = networkstatus_parse_vote_from_string(
pending_consensus_body, NULL, 0);
tor_assert(sigs);
ns_detached_signatures_free(sigs);
tor_assert(v);
networkstatus_vote_free(v);
}
tor_free(pending_consensus_signatures);
pending_consensus_signatures = new_detached;
*msg_out = "Signatures added";
} else {
goto err;
}
goto done;
err:
if (!msg_out)
*msg_out = "Unrecognized error while adding detached signatures.";
done:
if (sigs)
ns_detached_signatures_free(sigs);
return r;
}
/** Helper: we just got the <b>deteached_signatures_body</b> sent to us as
* signatures on the currently pending consensus. Add them to the pending
* consensus (if we have one); otherwise queue them until we have a
* consensus. Return negative on failure, nonnegative on success. */
int
dirvote_add_signatures(const char *detached_signatures_body,
const char *source,
const char **msg)
{
if (pending_consensus) {
log_notice(LD_DIR, "Got a signature from %s. "
"Adding it to the pending consensus.", source);
return dirvote_add_signatures_to_pending_consensus(
detached_signatures_body, msg);
} else {
log_notice(LD_DIR, "Got a signature from %s. "
"Queueing it for the next consensus.", source);
if (!pending_consensus_signature_list)
pending_consensus_signature_list = smartlist_create();
smartlist_add(pending_consensus_signature_list,
tor_strdup(d
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -