|
@@ -193,110 +193,6 @@ static token_rule_t extrainfo_token_table[] = {
|
|
END_OF_TABLE
|
|
END_OF_TABLE
|
|
};
|
|
};
|
|
|
|
|
|
-/** List of tokens recognized in the body part of v3 networkstatus
|
|
|
|
- * documents. */
|
|
|
|
-static token_rule_t rtrstatus_token_table[] = {
|
|
|
|
- T01("p", K_P, CONCAT_ARGS, NO_OBJ ),
|
|
|
|
- T1( "r", K_R, GE(7), NO_OBJ ),
|
|
|
|
- T0N("a", K_A, GE(1), NO_OBJ ),
|
|
|
|
- T1( "s", K_S, ARGS, NO_OBJ ),
|
|
|
|
- T01("v", K_V, CONCAT_ARGS, NO_OBJ ),
|
|
|
|
- T01("w", K_W, ARGS, NO_OBJ ),
|
|
|
|
- T0N("m", K_M, CONCAT_ARGS, NO_OBJ ),
|
|
|
|
- T0N("id", K_ID, GE(2), NO_OBJ ),
|
|
|
|
- T01("pr", K_PROTO, CONCAT_ARGS, NO_OBJ ),
|
|
|
|
- T0N("opt", K_OPT, CONCAT_ARGS, OBJ_OK ),
|
|
|
|
- END_OF_TABLE
|
|
|
|
-};
|
|
|
|
-
|
|
|
|
-/** List of tokens recognized in V3 networkstatus votes. */
|
|
|
|
-static token_rule_t networkstatus_token_table[] = {
|
|
|
|
- T1_START("network-status-version", K_NETWORK_STATUS_VERSION,
|
|
|
|
- GE(1), NO_OBJ ),
|
|
|
|
- T1("vote-status", K_VOTE_STATUS, GE(1), NO_OBJ ),
|
|
|
|
- T1("published", K_PUBLISHED, CONCAT_ARGS, NO_OBJ ),
|
|
|
|
- T1("valid-after", K_VALID_AFTER, CONCAT_ARGS, NO_OBJ ),
|
|
|
|
- T1("fresh-until", K_FRESH_UNTIL, CONCAT_ARGS, NO_OBJ ),
|
|
|
|
- T1("valid-until", K_VALID_UNTIL, CONCAT_ARGS, NO_OBJ ),
|
|
|
|
- T1("voting-delay", K_VOTING_DELAY, GE(2), NO_OBJ ),
|
|
|
|
- T1("known-flags", K_KNOWN_FLAGS, ARGS, NO_OBJ ),
|
|
|
|
- T01("params", K_PARAMS, ARGS, NO_OBJ ),
|
|
|
|
- T( "fingerprint", K_FINGERPRINT, CONCAT_ARGS, NO_OBJ ),
|
|
|
|
- T01("signing-ed25519", K_SIGNING_CERT_ED, NO_ARGS , NEED_OBJ ),
|
|
|
|
- T01("shared-rand-participate",K_SR_FLAG, NO_ARGS, NO_OBJ ),
|
|
|
|
- T0N("shared-rand-commit", K_COMMIT, GE(3), NO_OBJ ),
|
|
|
|
- T01("shared-rand-previous-value", K_PREVIOUS_SRV,EQ(2), NO_OBJ ),
|
|
|
|
- T01("shared-rand-current-value", K_CURRENT_SRV, EQ(2), NO_OBJ ),
|
|
|
|
- T0N("package", K_PACKAGE, CONCAT_ARGS, NO_OBJ ),
|
|
|
|
- T01("recommended-client-protocols", K_RECOMMENDED_CLIENT_PROTOCOLS,
|
|
|
|
- CONCAT_ARGS, NO_OBJ ),
|
|
|
|
- T01("recommended-relay-protocols", K_RECOMMENDED_RELAY_PROTOCOLS,
|
|
|
|
- CONCAT_ARGS, NO_OBJ ),
|
|
|
|
- T01("required-client-protocols", K_REQUIRED_CLIENT_PROTOCOLS,
|
|
|
|
- CONCAT_ARGS, NO_OBJ ),
|
|
|
|
- T01("required-relay-protocols", K_REQUIRED_RELAY_PROTOCOLS,
|
|
|
|
- CONCAT_ARGS, NO_OBJ ),
|
|
|
|
-
|
|
|
|
-#include "feature/dirparse/authcert_members.i"
|
|
|
|
-
|
|
|
|
- T0N("opt", K_OPT, CONCAT_ARGS, OBJ_OK ),
|
|
|
|
- T1( "contact", K_CONTACT, CONCAT_ARGS, NO_OBJ ),
|
|
|
|
- T1( "dir-source", K_DIR_SOURCE, GE(6), NO_OBJ ),
|
|
|
|
- T01("legacy-dir-key", K_LEGACY_DIR_KEY, GE(1), NO_OBJ ),
|
|
|
|
- T1( "known-flags", K_KNOWN_FLAGS, CONCAT_ARGS, NO_OBJ ),
|
|
|
|
- T01("client-versions", K_CLIENT_VERSIONS, CONCAT_ARGS, NO_OBJ ),
|
|
|
|
- T01("server-versions", K_SERVER_VERSIONS, CONCAT_ARGS, NO_OBJ ),
|
|
|
|
- T1( "consensus-methods", K_CONSENSUS_METHODS, GE(1), NO_OBJ ),
|
|
|
|
-
|
|
|
|
- END_OF_TABLE
|
|
|
|
-};
|
|
|
|
-
|
|
|
|
-/** List of tokens recognized in V3 networkstatus consensuses. */
|
|
|
|
-static token_rule_t networkstatus_consensus_token_table[] = {
|
|
|
|
- T1_START("network-status-version", K_NETWORK_STATUS_VERSION,
|
|
|
|
- GE(1), NO_OBJ ),
|
|
|
|
- T1("vote-status", K_VOTE_STATUS, GE(1), NO_OBJ ),
|
|
|
|
- T1("valid-after", K_VALID_AFTER, CONCAT_ARGS, NO_OBJ ),
|
|
|
|
- T1("fresh-until", K_FRESH_UNTIL, CONCAT_ARGS, NO_OBJ ),
|
|
|
|
- T1("valid-until", K_VALID_UNTIL, CONCAT_ARGS, NO_OBJ ),
|
|
|
|
- T1("voting-delay", K_VOTING_DELAY, GE(2), NO_OBJ ),
|
|
|
|
-
|
|
|
|
- T0N("opt", K_OPT, CONCAT_ARGS, OBJ_OK ),
|
|
|
|
-
|
|
|
|
- T1N("dir-source", K_DIR_SOURCE, GE(6), NO_OBJ ),
|
|
|
|
- T1N("contact", K_CONTACT, CONCAT_ARGS, NO_OBJ ),
|
|
|
|
- T1N("vote-digest", K_VOTE_DIGEST, GE(1), NO_OBJ ),
|
|
|
|
-
|
|
|
|
- T1( "known-flags", K_KNOWN_FLAGS, CONCAT_ARGS, NO_OBJ ),
|
|
|
|
-
|
|
|
|
- T01("client-versions", K_CLIENT_VERSIONS, CONCAT_ARGS, NO_OBJ ),
|
|
|
|
- T01("server-versions", K_SERVER_VERSIONS, CONCAT_ARGS, NO_OBJ ),
|
|
|
|
- T01("consensus-method", K_CONSENSUS_METHOD, EQ(1), NO_OBJ),
|
|
|
|
- T01("params", K_PARAMS, ARGS, NO_OBJ ),
|
|
|
|
-
|
|
|
|
- T01("shared-rand-previous-value", K_PREVIOUS_SRV, EQ(2), NO_OBJ ),
|
|
|
|
- T01("shared-rand-current-value", K_CURRENT_SRV, EQ(2), NO_OBJ ),
|
|
|
|
-
|
|
|
|
- T01("recommended-client-protocols", K_RECOMMENDED_CLIENT_PROTOCOLS,
|
|
|
|
- CONCAT_ARGS, NO_OBJ ),
|
|
|
|
- T01("recommended-relay-protocols", K_RECOMMENDED_RELAY_PROTOCOLS,
|
|
|
|
- CONCAT_ARGS, NO_OBJ ),
|
|
|
|
- T01("required-client-protocols", K_REQUIRED_CLIENT_PROTOCOLS,
|
|
|
|
- CONCAT_ARGS, NO_OBJ ),
|
|
|
|
- T01("required-relay-protocols", K_REQUIRED_RELAY_PROTOCOLS,
|
|
|
|
- CONCAT_ARGS, NO_OBJ ),
|
|
|
|
-
|
|
|
|
- END_OF_TABLE
|
|
|
|
-};
|
|
|
|
-
|
|
|
|
-/** List of tokens recognized in the footer of v1 directory footers. */
|
|
|
|
-static token_rule_t networkstatus_vote_footer_token_table[] = {
|
|
|
|
- T01("directory-footer", K_DIRECTORY_FOOTER, NO_ARGS, NO_OBJ ),
|
|
|
|
- T01("bandwidth-weights", K_BW_WEIGHTS, ARGS, NO_OBJ ),
|
|
|
|
- T( "directory-signature", K_DIRECTORY_SIGNATURE, GE(2), NEED_OBJ ),
|
|
|
|
- END_OF_TABLE
|
|
|
|
-};
|
|
|
|
-
|
|
|
|
#undef T
|
|
#undef T
|
|
|
|
|
|
/* static function prototypes */
|
|
/* static function prototypes */
|
|
@@ -318,52 +214,6 @@ router_get_router_hash(const char *s, size_t s_len, char *digest)
|
|
DIGEST_SHA1);
|
|
DIGEST_SHA1);
|
|
}
|
|
}
|
|
|
|
|
|
-/** Try to find the start and end of the signed portion of a networkstatus
|
|
|
|
- * document in <b>s</b>. On success, set <b>start_out</b> to the first
|
|
|
|
- * character of the document, and <b>end_out</b> to a position one after the
|
|
|
|
- * final character of the signed document, and return 0. On failure, return
|
|
|
|
- * -1. */
|
|
|
|
-int
|
|
|
|
-router_get_networkstatus_v3_signed_boundaries(const char *s,
|
|
|
|
- const char **start_out,
|
|
|
|
- const char **end_out)
|
|
|
|
-{
|
|
|
|
- return router_get_hash_impl_helper(s, strlen(s),
|
|
|
|
- "network-status-version",
|
|
|
|
- "\ndirectory-signature",
|
|
|
|
- ' ', LOG_INFO,
|
|
|
|
- start_out, end_out);
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-/** Set <b>digest_out</b> to the SHA3-256 digest of the signed portion of the
|
|
|
|
- * networkstatus vote in <b>s</b> -- or of the entirety of <b>s</b> if no
|
|
|
|
- * signed portion can be identified. Return 0 on success, -1 on failure. */
|
|
|
|
-int
|
|
|
|
-router_get_networkstatus_v3_sha3_as_signed(uint8_t *digest_out,
|
|
|
|
- const char *s)
|
|
|
|
-{
|
|
|
|
- const char *start, *end;
|
|
|
|
- if (router_get_networkstatus_v3_signed_boundaries(s, &start, &end) < 0) {
|
|
|
|
- start = s;
|
|
|
|
- end = s + strlen(s);
|
|
|
|
- }
|
|
|
|
- tor_assert(start);
|
|
|
|
- tor_assert(end);
|
|
|
|
- return crypto_digest256((char*)digest_out, start, end-start,
|
|
|
|
- DIGEST_SHA3_256);
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-/** Set <b>digests</b> to all the digests of the consensus document in
|
|
|
|
- * <b>s</b> */
|
|
|
|
-int
|
|
|
|
-router_get_networkstatus_v3_hashes(const char *s, common_digests_t *digests)
|
|
|
|
-{
|
|
|
|
- return router_get_hashes_impl(s,strlen(s),digests,
|
|
|
|
- "network-status-version",
|
|
|
|
- "\ndirectory-signature",
|
|
|
|
- ' ');
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
/** Set <b>digest</b> to the SHA-1 digest of the hash of the <b>s_len</b>-byte
|
|
/** 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. */
|
|
* extrainfo string at <b>s</b>. Return 0 on success, -1 on failure. */
|
|
int
|
|
int
|
|
@@ -1351,93 +1201,10 @@ extrainfo_parse_entry_from_string(const char *s, const char *end,
|
|
return extrainfo;
|
|
return extrainfo;
|
|
}
|
|
}
|
|
|
|
|
|
-/** Helper: given a string <b>s</b>, return the start of the next router-status
|
|
|
|
- * object (starting with "r " at the start of a line). If none is found,
|
|
|
|
- * return the start of the directory footer, or the next directory signature.
|
|
|
|
- * If none is found, return the end of the string. */
|
|
|
|
-static inline const char *
|
|
|
|
-find_start_of_next_routerstatus(const char *s)
|
|
|
|
-{
|
|
|
|
- const char *eos, *footer, *sig;
|
|
|
|
- if ((eos = strstr(s, "\nr ")))
|
|
|
|
- ++eos;
|
|
|
|
- else
|
|
|
|
- eos = s + strlen(s);
|
|
|
|
-
|
|
|
|
- footer = tor_memstr(s, eos-s, "\ndirectory-footer");
|
|
|
|
- sig = tor_memstr(s, eos-s, "\ndirectory-signature");
|
|
|
|
-
|
|
|
|
- if (footer && sig)
|
|
|
|
- return MIN(footer, sig) + 1;
|
|
|
|
- else if (footer)
|
|
|
|
- return footer+1;
|
|
|
|
- else if (sig)
|
|
|
|
- return sig+1;
|
|
|
|
- else
|
|
|
|
- return eos;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-/** Parse the GuardFraction string from a consensus or vote.
|
|
|
|
- *
|
|
|
|
- * If <b>vote</b> or <b>vote_rs</b> are set the document getting
|
|
|
|
- * parsed is a vote routerstatus. Otherwise it's a consensus. This is
|
|
|
|
- * the same semantic as in routerstatus_parse_entry_from_string(). */
|
|
|
|
-STATIC int
|
|
|
|
-routerstatus_parse_guardfraction(const char *guardfraction_str,
|
|
|
|
- networkstatus_t *vote,
|
|
|
|
- vote_routerstatus_t *vote_rs,
|
|
|
|
- routerstatus_t *rs)
|
|
|
|
-{
|
|
|
|
- int ok;
|
|
|
|
- const char *end_of_header = NULL;
|
|
|
|
- int is_consensus = !vote_rs;
|
|
|
|
- uint32_t guardfraction;
|
|
|
|
-
|
|
|
|
- tor_assert(bool_eq(vote, vote_rs));
|
|
|
|
-
|
|
|
|
- /* If this info comes from a consensus, but we should't apply
|
|
|
|
- guardfraction, just exit. */
|
|
|
|
- if (is_consensus && !should_apply_guardfraction(NULL)) {
|
|
|
|
- return 0;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- end_of_header = strchr(guardfraction_str, '=');
|
|
|
|
- if (!end_of_header) {
|
|
|
|
- return -1;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- guardfraction = (uint32_t)tor_parse_ulong(end_of_header+1,
|
|
|
|
- 10, 0, 100, &ok, NULL);
|
|
|
|
- if (!ok) {
|
|
|
|
- log_warn(LD_DIR, "Invalid GuardFraction %s", escaped(guardfraction_str));
|
|
|
|
- return -1;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- log_debug(LD_GENERAL, "[*] Parsed %s guardfraction '%s' for '%s'.",
|
|
|
|
- is_consensus ? "consensus" : "vote",
|
|
|
|
- guardfraction_str, rs->nickname);
|
|
|
|
-
|
|
|
|
- if (!is_consensus) { /* We are parsing a vote */
|
|
|
|
- vote_rs->status.guardfraction_percentage = guardfraction;
|
|
|
|
- vote_rs->status.has_guardfraction = 1;
|
|
|
|
- } else {
|
|
|
|
- /* We are parsing a consensus. Only apply guardfraction to guards. */
|
|
|
|
- if (rs->is_possible_guard) {
|
|
|
|
- rs->guardfraction_percentage = guardfraction;
|
|
|
|
- rs->has_guardfraction = 1;
|
|
|
|
- } else {
|
|
|
|
- log_warn(LD_BUG, "Got GuardFraction for non-guard %s. "
|
|
|
|
- "This is not supposed to happen. Not applying. ", rs->nickname);
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- return 0;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
/** Summarize the protocols listed in <b>protocols</b> into <b>out</b>,
|
|
/** Summarize the protocols listed in <b>protocols</b> into <b>out</b>,
|
|
* falling back or correcting them based on <b>version</b> as appropriate.
|
|
* falling back or correcting them based on <b>version</b> as appropriate.
|
|
*/
|
|
*/
|
|
-STATIC void
|
|
|
|
|
|
+void
|
|
summarize_protover_flags(protover_summary_flags_t *out,
|
|
summarize_protover_flags(protover_summary_flags_t *out,
|
|
const char *protocols,
|
|
const char *protocols,
|
|
const char *version)
|
|
const char *version)
|
|
@@ -1478,1417 +1245,6 @@ summarize_protover_flags(protover_summary_flags_t *out,
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
-/** Given a string at *<b>s</b>, containing a routerstatus object, and an
|
|
|
|
- * empty smartlist at <b>tokens</b>, parse and return the first router status
|
|
|
|
- * object in the string, and advance *<b>s</b> to just after the end of the
|
|
|
|
- * router status. Return NULL and advance *<b>s</b> on error.
|
|
|
|
- *
|
|
|
|
- * If <b>vote</b> and <b>vote_rs</b> are provided, don't allocate a fresh
|
|
|
|
- * routerstatus but use <b>vote_rs</b> instead.
|
|
|
|
- *
|
|
|
|
- * If <b>consensus_method</b> is nonzero, this routerstatus is part of a
|
|
|
|
- * consensus, and we should parse it according to the method used to
|
|
|
|
- * make that consensus.
|
|
|
|
- *
|
|
|
|
- * Parse according to the syntax used by the consensus flavor <b>flav</b>.
|
|
|
|
- **/
|
|
|
|
-STATIC routerstatus_t *
|
|
|
|
-routerstatus_parse_entry_from_string(memarea_t *area,
|
|
|
|
- const char **s, smartlist_t *tokens,
|
|
|
|
- networkstatus_t *vote,
|
|
|
|
- vote_routerstatus_t *vote_rs,
|
|
|
|
- int consensus_method,
|
|
|
|
- consensus_flavor_t flav)
|
|
|
|
-{
|
|
|
|
- const char *eos, *s_dup = *s;
|
|
|
|
- routerstatus_t *rs = NULL;
|
|
|
|
- directory_token_t *tok;
|
|
|
|
- char timebuf[ISO_TIME_LEN+1];
|
|
|
|
- struct in_addr in;
|
|
|
|
- int offset = 0;
|
|
|
|
- tor_assert(tokens);
|
|
|
|
- tor_assert(bool_eq(vote, vote_rs));
|
|
|
|
-
|
|
|
|
- if (!consensus_method)
|
|
|
|
- flav = FLAV_NS;
|
|
|
|
- tor_assert(flav == FLAV_NS || flav == FLAV_MICRODESC);
|
|
|
|
-
|
|
|
|
- eos = find_start_of_next_routerstatus(*s);
|
|
|
|
-
|
|
|
|
- if (tokenize_string(area,*s, eos, tokens, rtrstatus_token_table,0)) {
|
|
|
|
- log_warn(LD_DIR, "Error tokenizing router status");
|
|
|
|
- goto err;
|
|
|
|
- }
|
|
|
|
- if (smartlist_len(tokens) < 1) {
|
|
|
|
- log_warn(LD_DIR, "Impossibly short router status");
|
|
|
|
- goto err;
|
|
|
|
- }
|
|
|
|
- tok = find_by_keyword(tokens, K_R);
|
|
|
|
- tor_assert(tok->n_args >= 7); /* guaranteed by GE(7) in K_R setup */
|
|
|
|
- if (flav == FLAV_NS) {
|
|
|
|
- if (tok->n_args < 8) {
|
|
|
|
- log_warn(LD_DIR, "Too few arguments to r");
|
|
|
|
- goto err;
|
|
|
|
- }
|
|
|
|
- } else if (flav == FLAV_MICRODESC) {
|
|
|
|
- offset = -1; /* There is no descriptor digest in an md consensus r line */
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (vote_rs) {
|
|
|
|
- rs = &vote_rs->status;
|
|
|
|
- } else {
|
|
|
|
- rs = tor_malloc_zero(sizeof(routerstatus_t));
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (!is_legal_nickname(tok->args[0])) {
|
|
|
|
- log_warn(LD_DIR,
|
|
|
|
- "Invalid nickname %s in router status; skipping.",
|
|
|
|
- escaped(tok->args[0]));
|
|
|
|
- goto err;
|
|
|
|
- }
|
|
|
|
- strlcpy(rs->nickname, tok->args[0], sizeof(rs->nickname));
|
|
|
|
-
|
|
|
|
- if (digest_from_base64(rs->identity_digest, tok->args[1])) {
|
|
|
|
- log_warn(LD_DIR, "Error decoding identity digest %s",
|
|
|
|
- escaped(tok->args[1]));
|
|
|
|
- goto err;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (flav == FLAV_NS) {
|
|
|
|
- if (digest_from_base64(rs->descriptor_digest, tok->args[2])) {
|
|
|
|
- log_warn(LD_DIR, "Error decoding descriptor digest %s",
|
|
|
|
- escaped(tok->args[2]));
|
|
|
|
- goto err;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (tor_snprintf(timebuf, sizeof(timebuf), "%s %s",
|
|
|
|
- tok->args[3+offset], tok->args[4+offset]) < 0 ||
|
|
|
|
- parse_iso_time(timebuf, &rs->published_on)<0) {
|
|
|
|
- log_warn(LD_DIR, "Error parsing time '%s %s' [%d %d]",
|
|
|
|
- tok->args[3+offset], tok->args[4+offset],
|
|
|
|
- offset, (int)flav);
|
|
|
|
- goto err;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (tor_inet_aton(tok->args[5+offset], &in) == 0) {
|
|
|
|
- log_warn(LD_DIR, "Error parsing router address in network-status %s",
|
|
|
|
- escaped(tok->args[5+offset]));
|
|
|
|
- goto err;
|
|
|
|
- }
|
|
|
|
- rs->addr = ntohl(in.s_addr);
|
|
|
|
-
|
|
|
|
- rs->or_port = (uint16_t) tor_parse_long(tok->args[6+offset],
|
|
|
|
- 10,0,65535,NULL,NULL);
|
|
|
|
- rs->dir_port = (uint16_t) tor_parse_long(tok->args[7+offset],
|
|
|
|
- 10,0,65535,NULL,NULL);
|
|
|
|
-
|
|
|
|
- {
|
|
|
|
- smartlist_t *a_lines = find_all_by_keyword(tokens, K_A);
|
|
|
|
- if (a_lines) {
|
|
|
|
- find_single_ipv6_orport(a_lines, &rs->ipv6_addr, &rs->ipv6_orport);
|
|
|
|
- smartlist_free(a_lines);
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- tok = find_opt_by_keyword(tokens, K_S);
|
|
|
|
- if (tok && vote) {
|
|
|
|
- int i;
|
|
|
|
- vote_rs->flags = 0;
|
|
|
|
- for (i=0; i < tok->n_args; ++i) {
|
|
|
|
- int p = smartlist_string_pos(vote->known_flags, tok->args[i]);
|
|
|
|
- if (p >= 0) {
|
|
|
|
- vote_rs->flags |= (UINT64_C(1)<<p);
|
|
|
|
- } else {
|
|
|
|
- log_warn(LD_DIR, "Flags line had a flag %s not listed in known_flags.",
|
|
|
|
- escaped(tok->args[i]));
|
|
|
|
- goto err;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- } else if (tok) {
|
|
|
|
- /* This is a consensus, not a vote. */
|
|
|
|
- int i;
|
|
|
|
- for (i=0; i < tok->n_args; ++i) {
|
|
|
|
- if (!strcmp(tok->args[i], "Exit"))
|
|
|
|
- rs->is_exit = 1;
|
|
|
|
- else if (!strcmp(tok->args[i], "Stable"))
|
|
|
|
- rs->is_stable = 1;
|
|
|
|
- else if (!strcmp(tok->args[i], "Fast"))
|
|
|
|
- rs->is_fast = 1;
|
|
|
|
- else if (!strcmp(tok->args[i], "Running"))
|
|
|
|
- rs->is_flagged_running = 1;
|
|
|
|
- else if (!strcmp(tok->args[i], "Named"))
|
|
|
|
- rs->is_named = 1;
|
|
|
|
- else if (!strcmp(tok->args[i], "Valid"))
|
|
|
|
- rs->is_valid = 1;
|
|
|
|
- else if (!strcmp(tok->args[i], "Guard"))
|
|
|
|
- rs->is_possible_guard = 1;
|
|
|
|
- else if (!strcmp(tok->args[i], "BadExit"))
|
|
|
|
- rs->is_bad_exit = 1;
|
|
|
|
- else if (!strcmp(tok->args[i], "Authority"))
|
|
|
|
- rs->is_authority = 1;
|
|
|
|
- else if (!strcmp(tok->args[i], "Unnamed") &&
|
|
|
|
- consensus_method >= 2) {
|
|
|
|
- /* Unnamed is computed right by consensus method 2 and later. */
|
|
|
|
- rs->is_unnamed = 1;
|
|
|
|
- } else if (!strcmp(tok->args[i], "HSDir")) {
|
|
|
|
- rs->is_hs_dir = 1;
|
|
|
|
- } else if (!strcmp(tok->args[i], "V2Dir")) {
|
|
|
|
- rs->is_v2_dir = 1;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- /* These are implied true by having been included in a consensus made
|
|
|
|
- * with a given method */
|
|
|
|
- rs->is_flagged_running = 1; /* Starting with consensus method 4. */
|
|
|
|
- rs->is_valid = 1; /* Starting with consensus method 24. */
|
|
|
|
- }
|
|
|
|
- {
|
|
|
|
- const char *protocols = NULL, *version = NULL;
|
|
|
|
- if ((tok = find_opt_by_keyword(tokens, K_PROTO))) {
|
|
|
|
- tor_assert(tok->n_args == 1);
|
|
|
|
- protocols = tok->args[0];
|
|
|
|
- }
|
|
|
|
- if ((tok = find_opt_by_keyword(tokens, K_V))) {
|
|
|
|
- tor_assert(tok->n_args == 1);
|
|
|
|
- version = tok->args[0];
|
|
|
|
- if (vote_rs) {
|
|
|
|
- vote_rs->version = tor_strdup(tok->args[0]);
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- summarize_protover_flags(&rs->pv, protocols, version);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /* handle weighting/bandwidth info */
|
|
|
|
- if ((tok = find_opt_by_keyword(tokens, K_W))) {
|
|
|
|
- int i;
|
|
|
|
- for (i=0; i < tok->n_args; ++i) {
|
|
|
|
- if (!strcmpstart(tok->args[i], "Bandwidth=")) {
|
|
|
|
- int ok;
|
|
|
|
- rs->bandwidth_kb =
|
|
|
|
- (uint32_t)tor_parse_ulong(strchr(tok->args[i], '=')+1,
|
|
|
|
- 10, 0, UINT32_MAX,
|
|
|
|
- &ok, NULL);
|
|
|
|
- if (!ok) {
|
|
|
|
- log_warn(LD_DIR, "Invalid Bandwidth %s", escaped(tok->args[i]));
|
|
|
|
- goto err;
|
|
|
|
- }
|
|
|
|
- rs->has_bandwidth = 1;
|
|
|
|
- } else if (!strcmpstart(tok->args[i], "Measured=") && vote_rs) {
|
|
|
|
- int ok;
|
|
|
|
- vote_rs->measured_bw_kb =
|
|
|
|
- (uint32_t)tor_parse_ulong(strchr(tok->args[i], '=')+1,
|
|
|
|
- 10, 0, UINT32_MAX, &ok, NULL);
|
|
|
|
- if (!ok) {
|
|
|
|
- log_warn(LD_DIR, "Invalid Measured Bandwidth %s",
|
|
|
|
- escaped(tok->args[i]));
|
|
|
|
- goto err;
|
|
|
|
- }
|
|
|
|
- vote_rs->has_measured_bw = 1;
|
|
|
|
- vote->has_measured_bws = 1;
|
|
|
|
- } else if (!strcmpstart(tok->args[i], "Unmeasured=1")) {
|
|
|
|
- rs->bw_is_unmeasured = 1;
|
|
|
|
- } else if (!strcmpstart(tok->args[i], "GuardFraction=")) {
|
|
|
|
- if (routerstatus_parse_guardfraction(tok->args[i],
|
|
|
|
- vote, vote_rs, rs) < 0) {
|
|
|
|
- goto err;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /* parse exit policy summaries */
|
|
|
|
- if ((tok = find_opt_by_keyword(tokens, K_P))) {
|
|
|
|
- tor_assert(tok->n_args == 1);
|
|
|
|
- if (strcmpstart(tok->args[0], "accept ") &&
|
|
|
|
- strcmpstart(tok->args[0], "reject ")) {
|
|
|
|
- log_warn(LD_DIR, "Unknown exit policy summary type %s.",
|
|
|
|
- escaped(tok->args[0]));
|
|
|
|
- goto err;
|
|
|
|
- }
|
|
|
|
- /* XXX weasel: parse this into ports and represent them somehow smart,
|
|
|
|
- * maybe not here but somewhere on if we need it for the client.
|
|
|
|
- * we should still parse it here to check it's valid tho.
|
|
|
|
- */
|
|
|
|
- rs->exitsummary = tor_strdup(tok->args[0]);
|
|
|
|
- rs->has_exitsummary = 1;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (vote_rs) {
|
|
|
|
- SMARTLIST_FOREACH_BEGIN(tokens, directory_token_t *, t) {
|
|
|
|
- if (t->tp == K_M && t->n_args) {
|
|
|
|
- vote_microdesc_hash_t *line =
|
|
|
|
- tor_malloc(sizeof(vote_microdesc_hash_t));
|
|
|
|
- line->next = vote_rs->microdesc;
|
|
|
|
- line->microdesc_hash_line = tor_strdup(t->args[0]);
|
|
|
|
- vote_rs->microdesc = line;
|
|
|
|
- }
|
|
|
|
- if (t->tp == K_ID) {
|
|
|
|
- tor_assert(t->n_args >= 2);
|
|
|
|
- if (!strcmp(t->args[0], "ed25519")) {
|
|
|
|
- vote_rs->has_ed25519_listing = 1;
|
|
|
|
- if (strcmp(t->args[1], "none") &&
|
|
|
|
- digest256_from_base64((char*)vote_rs->ed25519_id,
|
|
|
|
- t->args[1])<0) {
|
|
|
|
- log_warn(LD_DIR, "Bogus ed25519 key in networkstatus vote");
|
|
|
|
- goto err;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- if (t->tp == K_PROTO) {
|
|
|
|
- tor_assert(t->n_args == 1);
|
|
|
|
- vote_rs->protocols = tor_strdup(t->args[0]);
|
|
|
|
- }
|
|
|
|
- } SMARTLIST_FOREACH_END(t);
|
|
|
|
- } else if (flav == FLAV_MICRODESC) {
|
|
|
|
- tok = find_opt_by_keyword(tokens, K_M);
|
|
|
|
- if (tok) {
|
|
|
|
- tor_assert(tok->n_args);
|
|
|
|
- if (digest256_from_base64(rs->descriptor_digest, tok->args[0])) {
|
|
|
|
- log_warn(LD_DIR, "Error decoding microdescriptor digest %s",
|
|
|
|
- escaped(tok->args[0]));
|
|
|
|
- goto err;
|
|
|
|
- }
|
|
|
|
- } else {
|
|
|
|
- log_info(LD_BUG, "Found an entry in networkstatus with no "
|
|
|
|
- "microdescriptor digest. (Router %s ($%s) at %s:%d.)",
|
|
|
|
- rs->nickname, hex_str(rs->identity_digest, DIGEST_LEN),
|
|
|
|
- fmt_addr32(rs->addr), rs->or_port);
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (!strcasecmp(rs->nickname, UNNAMED_ROUTER_NICKNAME))
|
|
|
|
- rs->is_named = 0;
|
|
|
|
-
|
|
|
|
- goto done;
|
|
|
|
- err:
|
|
|
|
- dump_desc(s_dup, "routerstatus entry");
|
|
|
|
- if (rs && !vote_rs)
|
|
|
|
- routerstatus_free(rs);
|
|
|
|
- rs = NULL;
|
|
|
|
- done:
|
|
|
|
- SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_clear(t));
|
|
|
|
- smartlist_clear(tokens);
|
|
|
|
- if (area) {
|
|
|
|
- DUMP_AREA(area, "routerstatus entry");
|
|
|
|
- memarea_clear(area);
|
|
|
|
- }
|
|
|
|
- *s = eos;
|
|
|
|
-
|
|
|
|
- return rs;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-int
|
|
|
|
-compare_vote_routerstatus_entries(const void **_a, const void **_b)
|
|
|
|
-{
|
|
|
|
- const vote_routerstatus_t *a = *_a, *b = *_b;
|
|
|
|
- return fast_memcmp(a->status.identity_digest, b->status.identity_digest,
|
|
|
|
- DIGEST_LEN);
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-/** Verify the bandwidth weights of a network status document */
|
|
|
|
-int
|
|
|
|
-networkstatus_verify_bw_weights(networkstatus_t *ns, int consensus_method)
|
|
|
|
-{
|
|
|
|
- int64_t G=0, M=0, E=0, D=0, T=0;
|
|
|
|
- double Wgg, Wgm, Wgd, Wmg, Wmm, Wme, Wmd, Weg, Wem, Wee, Wed;
|
|
|
|
- double Gtotal=0, Mtotal=0, Etotal=0;
|
|
|
|
- const char *casename = NULL;
|
|
|
|
- int valid = 1;
|
|
|
|
- (void) consensus_method;
|
|
|
|
-
|
|
|
|
- const int64_t weight_scale = networkstatus_get_weight_scale_param(ns);
|
|
|
|
- tor_assert(weight_scale >= 1);
|
|
|
|
- Wgg = networkstatus_get_bw_weight(ns, "Wgg", -1);
|
|
|
|
- Wgm = networkstatus_get_bw_weight(ns, "Wgm", -1);
|
|
|
|
- Wgd = networkstatus_get_bw_weight(ns, "Wgd", -1);
|
|
|
|
- Wmg = networkstatus_get_bw_weight(ns, "Wmg", -1);
|
|
|
|
- Wmm = networkstatus_get_bw_weight(ns, "Wmm", -1);
|
|
|
|
- Wme = networkstatus_get_bw_weight(ns, "Wme", -1);
|
|
|
|
- Wmd = networkstatus_get_bw_weight(ns, "Wmd", -1);
|
|
|
|
- Weg = networkstatus_get_bw_weight(ns, "Weg", -1);
|
|
|
|
- Wem = networkstatus_get_bw_weight(ns, "Wem", -1);
|
|
|
|
- Wee = networkstatus_get_bw_weight(ns, "Wee", -1);
|
|
|
|
- Wed = networkstatus_get_bw_weight(ns, "Wed", -1);
|
|
|
|
-
|
|
|
|
- if (Wgg<0 || Wgm<0 || Wgd<0 || Wmg<0 || Wmm<0 || Wme<0 || Wmd<0 || Weg<0
|
|
|
|
- || Wem<0 || Wee<0 || Wed<0) {
|
|
|
|
- log_warn(LD_BUG, "No bandwidth weights produced in consensus!");
|
|
|
|
- return 0;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- // First, sanity check basic summing properties that hold for all cases
|
|
|
|
- // We use > 1 as the check for these because they are computed as integers.
|
|
|
|
- // Sometimes there are rounding errors.
|
|
|
|
- if (fabs(Wmm - weight_scale) > 1) {
|
|
|
|
- log_warn(LD_BUG, "Wmm=%f != %"PRId64,
|
|
|
|
- Wmm, (weight_scale));
|
|
|
|
- valid = 0;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (fabs(Wem - Wee) > 1) {
|
|
|
|
- log_warn(LD_BUG, "Wem=%f != Wee=%f", Wem, Wee);
|
|
|
|
- valid = 0;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (fabs(Wgm - Wgg) > 1) {
|
|
|
|
- log_warn(LD_BUG, "Wgm=%f != Wgg=%f", Wgm, Wgg);
|
|
|
|
- valid = 0;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (fabs(Weg - Wed) > 1) {
|
|
|
|
- log_warn(LD_BUG, "Wed=%f != Weg=%f", Wed, Weg);
|
|
|
|
- valid = 0;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (fabs(Wgg + Wmg - weight_scale) > 0.001*weight_scale) {
|
|
|
|
- log_warn(LD_BUG, "Wgg=%f != %"PRId64" - Wmg=%f", Wgg,
|
|
|
|
- (weight_scale), Wmg);
|
|
|
|
- valid = 0;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (fabs(Wee + Wme - weight_scale) > 0.001*weight_scale) {
|
|
|
|
- log_warn(LD_BUG, "Wee=%f != %"PRId64" - Wme=%f", Wee,
|
|
|
|
- (weight_scale), Wme);
|
|
|
|
- valid = 0;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (fabs(Wgd + Wmd + Wed - weight_scale) > 0.001*weight_scale) {
|
|
|
|
- log_warn(LD_BUG, "Wgd=%f + Wmd=%f + Wed=%f != %"PRId64,
|
|
|
|
- Wgd, Wmd, Wed, (weight_scale));
|
|
|
|
- valid = 0;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- Wgg /= weight_scale;
|
|
|
|
- Wgm /= weight_scale; (void) Wgm; // unused from here on.
|
|
|
|
- Wgd /= weight_scale;
|
|
|
|
-
|
|
|
|
- Wmg /= weight_scale;
|
|
|
|
- Wmm /= weight_scale;
|
|
|
|
- Wme /= weight_scale;
|
|
|
|
- Wmd /= weight_scale;
|
|
|
|
-
|
|
|
|
- Weg /= weight_scale; (void) Weg; // unused from here on.
|
|
|
|
- Wem /= weight_scale; (void) Wem; // unused from here on.
|
|
|
|
- Wee /= weight_scale;
|
|
|
|
- Wed /= weight_scale;
|
|
|
|
-
|
|
|
|
- // Then, gather G, M, E, D, T to determine case
|
|
|
|
- SMARTLIST_FOREACH_BEGIN(ns->routerstatus_list, routerstatus_t *, rs) {
|
|
|
|
- int is_exit = 0;
|
|
|
|
- /* Bug #2203: Don't count bad exits as exits for balancing */
|
|
|
|
- is_exit = rs->is_exit && !rs->is_bad_exit;
|
|
|
|
- if (rs->has_bandwidth) {
|
|
|
|
- T += rs->bandwidth_kb;
|
|
|
|
- if (is_exit && rs->is_possible_guard) {
|
|
|
|
- D += rs->bandwidth_kb;
|
|
|
|
- Gtotal += Wgd*rs->bandwidth_kb;
|
|
|
|
- Mtotal += Wmd*rs->bandwidth_kb;
|
|
|
|
- Etotal += Wed*rs->bandwidth_kb;
|
|
|
|
- } else if (is_exit) {
|
|
|
|
- E += rs->bandwidth_kb;
|
|
|
|
- Mtotal += Wme*rs->bandwidth_kb;
|
|
|
|
- Etotal += Wee*rs->bandwidth_kb;
|
|
|
|
- } else if (rs->is_possible_guard) {
|
|
|
|
- G += rs->bandwidth_kb;
|
|
|
|
- Gtotal += Wgg*rs->bandwidth_kb;
|
|
|
|
- Mtotal += Wmg*rs->bandwidth_kb;
|
|
|
|
- } else {
|
|
|
|
- M += rs->bandwidth_kb;
|
|
|
|
- Mtotal += Wmm*rs->bandwidth_kb;
|
|
|
|
- }
|
|
|
|
- } else {
|
|
|
|
- log_warn(LD_BUG, "Missing consensus bandwidth for router %s",
|
|
|
|
- routerstatus_describe(rs));
|
|
|
|
- }
|
|
|
|
- } SMARTLIST_FOREACH_END(rs);
|
|
|
|
-
|
|
|
|
- // Finally, check equality conditions depending upon case 1, 2 or 3
|
|
|
|
- // Full equality cases: 1, 3b
|
|
|
|
- // Partial equality cases: 2b (E=G), 3a (M=E)
|
|
|
|
- // Fully unknown: 2a
|
|
|
|
- if (3*E >= T && 3*G >= T) {
|
|
|
|
- // Case 1: Neither are scarce
|
|
|
|
- casename = "Case 1";
|
|
|
|
- if (fabs(Etotal-Mtotal) > 0.01*MAX(Etotal,Mtotal)) {
|
|
|
|
- log_warn(LD_DIR,
|
|
|
|
- "Bw Weight Failure for %s: Etotal %f != Mtotal %f. "
|
|
|
|
- "G=%"PRId64" M=%"PRId64" E=%"PRId64" D=%"PRId64
|
|
|
|
- " T=%"PRId64". "
|
|
|
|
- "Wgg=%f Wgd=%f Wmg=%f Wme=%f Wmd=%f Wee=%f Wed=%f",
|
|
|
|
- casename, Etotal, Mtotal,
|
|
|
|
- (G), (M), (E),
|
|
|
|
- (D), (T),
|
|
|
|
- Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
|
|
|
|
- valid = 0;
|
|
|
|
- }
|
|
|
|
- if (fabs(Etotal-Gtotal) > 0.01*MAX(Etotal,Gtotal)) {
|
|
|
|
- log_warn(LD_DIR,
|
|
|
|
- "Bw Weight Failure for %s: Etotal %f != Gtotal %f. "
|
|
|
|
- "G=%"PRId64" M=%"PRId64" E=%"PRId64" D=%"PRId64
|
|
|
|
- " T=%"PRId64". "
|
|
|
|
- "Wgg=%f Wgd=%f Wmg=%f Wme=%f Wmd=%f Wee=%f Wed=%f",
|
|
|
|
- casename, Etotal, Gtotal,
|
|
|
|
- (G), (M), (E),
|
|
|
|
- (D), (T),
|
|
|
|
- Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
|
|
|
|
- valid = 0;
|
|
|
|
- }
|
|
|
|
- if (fabs(Gtotal-Mtotal) > 0.01*MAX(Gtotal,Mtotal)) {
|
|
|
|
- log_warn(LD_DIR,
|
|
|
|
- "Bw Weight Failure for %s: Mtotal %f != Gtotal %f. "
|
|
|
|
- "G=%"PRId64" M=%"PRId64" E=%"PRId64" D=%"PRId64
|
|
|
|
- " T=%"PRId64". "
|
|
|
|
- "Wgg=%f Wgd=%f Wmg=%f Wme=%f Wmd=%f Wee=%f Wed=%f",
|
|
|
|
- casename, Mtotal, Gtotal,
|
|
|
|
- (G), (M), (E),
|
|
|
|
- (D), (T),
|
|
|
|
- Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
|
|
|
|
- valid = 0;
|
|
|
|
- }
|
|
|
|
- } else if (3*E < T && 3*G < T) {
|
|
|
|
- int64_t R = MIN(E, G);
|
|
|
|
- int64_t S = MAX(E, G);
|
|
|
|
- /*
|
|
|
|
- * Case 2: Both Guards and Exits are scarce
|
|
|
|
- * Balance D between E and G, depending upon
|
|
|
|
- * D capacity and scarcity. Devote no extra
|
|
|
|
- * bandwidth to middle nodes.
|
|
|
|
- */
|
|
|
|
- if (R+D < S) { // Subcase a
|
|
|
|
- double Rtotal, Stotal;
|
|
|
|
- if (E < G) {
|
|
|
|
- Rtotal = Etotal;
|
|
|
|
- Stotal = Gtotal;
|
|
|
|
- } else {
|
|
|
|
- Rtotal = Gtotal;
|
|
|
|
- Stotal = Etotal;
|
|
|
|
- }
|
|
|
|
- casename = "Case 2a";
|
|
|
|
- // Rtotal < Stotal
|
|
|
|
- if (Rtotal > Stotal) {
|
|
|
|
- log_warn(LD_DIR,
|
|
|
|
- "Bw Weight Failure for %s: Rtotal %f > Stotal %f. "
|
|
|
|
- "G=%"PRId64" M=%"PRId64" E=%"PRId64" D=%"PRId64
|
|
|
|
- " T=%"PRId64". "
|
|
|
|
- "Wgg=%f Wgd=%f Wmg=%f Wme=%f Wmd=%f Wee=%f Wed=%f",
|
|
|
|
- casename, Rtotal, Stotal,
|
|
|
|
- (G), (M), (E),
|
|
|
|
- (D), (T),
|
|
|
|
- Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
|
|
|
|
- valid = 0;
|
|
|
|
- }
|
|
|
|
- // Rtotal < T/3
|
|
|
|
- if (3*Rtotal > T) {
|
|
|
|
- log_warn(LD_DIR,
|
|
|
|
- "Bw Weight Failure for %s: 3*Rtotal %f > T "
|
|
|
|
- "%"PRId64". G=%"PRId64" M=%"PRId64" E=%"PRId64
|
|
|
|
- " D=%"PRId64" T=%"PRId64". "
|
|
|
|
- "Wgg=%f Wgd=%f Wmg=%f Wme=%f Wmd=%f Wee=%f Wed=%f",
|
|
|
|
- casename, Rtotal*3, (T),
|
|
|
|
- (G), (M), (E),
|
|
|
|
- (D), (T),
|
|
|
|
- Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
|
|
|
|
- valid = 0;
|
|
|
|
- }
|
|
|
|
- // Stotal < T/3
|
|
|
|
- if (3*Stotal > T) {
|
|
|
|
- log_warn(LD_DIR,
|
|
|
|
- "Bw Weight Failure for %s: 3*Stotal %f > T "
|
|
|
|
- "%"PRId64". G=%"PRId64" M=%"PRId64" E=%"PRId64
|
|
|
|
- " D=%"PRId64" T=%"PRId64". "
|
|
|
|
- "Wgg=%f Wgd=%f Wmg=%f Wme=%f Wmd=%f Wee=%f Wed=%f",
|
|
|
|
- casename, Stotal*3, (T),
|
|
|
|
- (G), (M), (E),
|
|
|
|
- (D), (T),
|
|
|
|
- Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
|
|
|
|
- valid = 0;
|
|
|
|
- }
|
|
|
|
- // Mtotal > T/3
|
|
|
|
- if (3*Mtotal < T) {
|
|
|
|
- log_warn(LD_DIR,
|
|
|
|
- "Bw Weight Failure for %s: 3*Mtotal %f < T "
|
|
|
|
- "%"PRId64". "
|
|
|
|
- "G=%"PRId64" M=%"PRId64" E=%"PRId64" D=%"PRId64
|
|
|
|
- " T=%"PRId64". "
|
|
|
|
- "Wgg=%f Wgd=%f Wmg=%f Wme=%f Wmd=%f Wee=%f Wed=%f",
|
|
|
|
- casename, Mtotal*3, (T),
|
|
|
|
- (G), (M), (E),
|
|
|
|
- (D), (T),
|
|
|
|
- Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
|
|
|
|
- valid = 0;
|
|
|
|
- }
|
|
|
|
- } else { // Subcase b: R+D > S
|
|
|
|
- casename = "Case 2b";
|
|
|
|
-
|
|
|
|
- /* Check the rare-M redirect case. */
|
|
|
|
- if (D != 0 && 3*M < T) {
|
|
|
|
- casename = "Case 2b (balanced)";
|
|
|
|
- if (fabs(Etotal-Mtotal) > 0.01*MAX(Etotal,Mtotal)) {
|
|
|
|
- log_warn(LD_DIR,
|
|
|
|
- "Bw Weight Failure for %s: Etotal %f != Mtotal %f. "
|
|
|
|
- "G=%"PRId64" M=%"PRId64" E=%"PRId64" D=%"PRId64
|
|
|
|
- " T=%"PRId64". "
|
|
|
|
- "Wgg=%f Wgd=%f Wmg=%f Wme=%f Wmd=%f Wee=%f Wed=%f",
|
|
|
|
- casename, Etotal, Mtotal,
|
|
|
|
- (G), (M), (E),
|
|
|
|
- (D), (T),
|
|
|
|
- Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
|
|
|
|
- valid = 0;
|
|
|
|
- }
|
|
|
|
- if (fabs(Etotal-Gtotal) > 0.01*MAX(Etotal,Gtotal)) {
|
|
|
|
- log_warn(LD_DIR,
|
|
|
|
- "Bw Weight Failure for %s: Etotal %f != Gtotal %f. "
|
|
|
|
- "G=%"PRId64" M=%"PRId64" E=%"PRId64" D=%"PRId64
|
|
|
|
- " T=%"PRId64". "
|
|
|
|
- "Wgg=%f Wgd=%f Wmg=%f Wme=%f Wmd=%f Wee=%f Wed=%f",
|
|
|
|
- casename, Etotal, Gtotal,
|
|
|
|
- (G), (M), (E),
|
|
|
|
- (D), (T),
|
|
|
|
- Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
|
|
|
|
- valid = 0;
|
|
|
|
- }
|
|
|
|
- if (fabs(Gtotal-Mtotal) > 0.01*MAX(Gtotal,Mtotal)) {
|
|
|
|
- log_warn(LD_DIR,
|
|
|
|
- "Bw Weight Failure for %s: Mtotal %f != Gtotal %f. "
|
|
|
|
- "G=%"PRId64" M=%"PRId64" E=%"PRId64" D=%"PRId64
|
|
|
|
- " T=%"PRId64". "
|
|
|
|
- "Wgg=%f Wgd=%f Wmg=%f Wme=%f Wmd=%f Wee=%f Wed=%f",
|
|
|
|
- casename, Mtotal, Gtotal,
|
|
|
|
- (G), (M), (E),
|
|
|
|
- (D), (T),
|
|
|
|
- Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
|
|
|
|
- valid = 0;
|
|
|
|
- }
|
|
|
|
- } else {
|
|
|
|
- if (fabs(Etotal-Gtotal) > 0.01*MAX(Etotal,Gtotal)) {
|
|
|
|
- log_warn(LD_DIR,
|
|
|
|
- "Bw Weight Failure for %s: Etotal %f != Gtotal %f. "
|
|
|
|
- "G=%"PRId64" M=%"PRId64" E=%"PRId64" D=%"PRId64
|
|
|
|
- " T=%"PRId64". "
|
|
|
|
- "Wgg=%f Wgd=%f Wmg=%f Wme=%f Wmd=%f Wee=%f Wed=%f",
|
|
|
|
- casename, Etotal, Gtotal,
|
|
|
|
- (G), (M), (E),
|
|
|
|
- (D), (T),
|
|
|
|
- Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
|
|
|
|
- valid = 0;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- } else { // if (E < T/3 || G < T/3) {
|
|
|
|
- int64_t S = MIN(E, G);
|
|
|
|
- int64_t NS = MAX(E, G);
|
|
|
|
- if (3*(S+D) < T) { // Subcase a:
|
|
|
|
- double Stotal;
|
|
|
|
- double NStotal;
|
|
|
|
- if (G < E) {
|
|
|
|
- casename = "Case 3a (G scarce)";
|
|
|
|
- Stotal = Gtotal;
|
|
|
|
- NStotal = Etotal;
|
|
|
|
- } else { // if (G >= E) {
|
|
|
|
- casename = "Case 3a (E scarce)";
|
|
|
|
- NStotal = Gtotal;
|
|
|
|
- Stotal = Etotal;
|
|
|
|
- }
|
|
|
|
- // Stotal < T/3
|
|
|
|
- if (3*Stotal > T) {
|
|
|
|
- log_warn(LD_DIR,
|
|
|
|
- "Bw Weight Failure for %s: 3*Stotal %f > T "
|
|
|
|
- "%"PRId64". G=%"PRId64" M=%"PRId64" E=%"PRId64
|
|
|
|
- " D=%"PRId64" T=%"PRId64". "
|
|
|
|
- "Wgg=%f Wgd=%f Wmg=%f Wme=%f Wmd=%f Wee=%f Wed=%f",
|
|
|
|
- casename, Stotal*3, (T),
|
|
|
|
- (G), (M), (E),
|
|
|
|
- (D), (T),
|
|
|
|
- Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
|
|
|
|
- valid = 0;
|
|
|
|
- }
|
|
|
|
- if (NS >= M) {
|
|
|
|
- if (fabs(NStotal-Mtotal) > 0.01*MAX(NStotal,Mtotal)) {
|
|
|
|
- log_warn(LD_DIR,
|
|
|
|
- "Bw Weight Failure for %s: NStotal %f != Mtotal %f. "
|
|
|
|
- "G=%"PRId64" M=%"PRId64" E=%"PRId64" D=%"PRId64
|
|
|
|
- " T=%"PRId64". "
|
|
|
|
- "Wgg=%f Wgd=%f Wmg=%f Wme=%f Wmd=%f Wee=%f Wed=%f",
|
|
|
|
- casename, NStotal, Mtotal,
|
|
|
|
- (G), (M), (E),
|
|
|
|
- (D), (T),
|
|
|
|
- Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
|
|
|
|
- valid = 0;
|
|
|
|
- }
|
|
|
|
- } else {
|
|
|
|
- // if NS < M, NStotal > T/3 because only one of G or E is scarce
|
|
|
|
- if (3*NStotal < T) {
|
|
|
|
- log_warn(LD_DIR,
|
|
|
|
- "Bw Weight Failure for %s: 3*NStotal %f < T "
|
|
|
|
- "%"PRId64". G=%"PRId64" M=%"PRId64
|
|
|
|
- " E=%"PRId64" D=%"PRId64" T=%"PRId64". "
|
|
|
|
- "Wgg=%f Wgd=%f Wmg=%f Wme=%f Wmd=%f Wee=%f Wed=%f",
|
|
|
|
- casename, NStotal*3, (T),
|
|
|
|
- (G), (M), (E),
|
|
|
|
- (D), (T),
|
|
|
|
- Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
|
|
|
|
- valid = 0;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- } else { // Subcase b: S+D >= T/3
|
|
|
|
- casename = "Case 3b";
|
|
|
|
- if (fabs(Etotal-Mtotal) > 0.01*MAX(Etotal,Mtotal)) {
|
|
|
|
- log_warn(LD_DIR,
|
|
|
|
- "Bw Weight Failure for %s: Etotal %f != Mtotal %f. "
|
|
|
|
- "G=%"PRId64" M=%"PRId64" E=%"PRId64" D=%"PRId64
|
|
|
|
- " T=%"PRId64". "
|
|
|
|
- "Wgg=%f Wgd=%f Wmg=%f Wme=%f Wmd=%f Wee=%f Wed=%f",
|
|
|
|
- casename, Etotal, Mtotal,
|
|
|
|
- (G), (M), (E),
|
|
|
|
- (D), (T),
|
|
|
|
- Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
|
|
|
|
- valid = 0;
|
|
|
|
- }
|
|
|
|
- if (fabs(Etotal-Gtotal) > 0.01*MAX(Etotal,Gtotal)) {
|
|
|
|
- log_warn(LD_DIR,
|
|
|
|
- "Bw Weight Failure for %s: Etotal %f != Gtotal %f. "
|
|
|
|
- "G=%"PRId64" M=%"PRId64" E=%"PRId64" D=%"PRId64
|
|
|
|
- " T=%"PRId64". "
|
|
|
|
- "Wgg=%f Wgd=%f Wmg=%f Wme=%f Wmd=%f Wee=%f Wed=%f",
|
|
|
|
- casename, Etotal, Gtotal,
|
|
|
|
- (G), (M), (E),
|
|
|
|
- (D), (T),
|
|
|
|
- Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
|
|
|
|
- valid = 0;
|
|
|
|
- }
|
|
|
|
- if (fabs(Gtotal-Mtotal) > 0.01*MAX(Gtotal,Mtotal)) {
|
|
|
|
- log_warn(LD_DIR,
|
|
|
|
- "Bw Weight Failure for %s: Mtotal %f != Gtotal %f. "
|
|
|
|
- "G=%"PRId64" M=%"PRId64" E=%"PRId64" D=%"PRId64
|
|
|
|
- " T=%"PRId64". "
|
|
|
|
- "Wgg=%f Wgd=%f Wmg=%f Wme=%f Wmd=%f Wee=%f Wed=%f",
|
|
|
|
- casename, Mtotal, Gtotal,
|
|
|
|
- (G), (M), (E),
|
|
|
|
- (D), (T),
|
|
|
|
- Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
|
|
|
|
- valid = 0;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (valid)
|
|
|
|
- log_notice(LD_DIR, "Bandwidth-weight %s is verified and valid.",
|
|
|
|
- casename);
|
|
|
|
-
|
|
|
|
- return valid;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-/** Check if a shared random value of type <b>srv_type</b> is in
|
|
|
|
- * <b>tokens</b>. If there is, parse it and set it to <b>srv_out</b>. Return
|
|
|
|
- * -1 on failure, 0 on success. The resulting srv is allocated on the heap and
|
|
|
|
- * it's the responsibility of the caller to free it. */
|
|
|
|
-static int
|
|
|
|
-extract_one_srv(smartlist_t *tokens, directory_keyword srv_type,
|
|
|
|
- sr_srv_t **srv_out)
|
|
|
|
-{
|
|
|
|
- int ret = -1;
|
|
|
|
- directory_token_t *tok;
|
|
|
|
- sr_srv_t *srv = NULL;
|
|
|
|
- smartlist_t *chunks;
|
|
|
|
-
|
|
|
|
- tor_assert(tokens);
|
|
|
|
-
|
|
|
|
- chunks = smartlist_new();
|
|
|
|
- tok = find_opt_by_keyword(tokens, srv_type);
|
|
|
|
- if (!tok) {
|
|
|
|
- /* That's fine, no SRV is allowed. */
|
|
|
|
- ret = 0;
|
|
|
|
- goto end;
|
|
|
|
- }
|
|
|
|
- for (int i = 0; i < tok->n_args; i++) {
|
|
|
|
- smartlist_add(chunks, tok->args[i]);
|
|
|
|
- }
|
|
|
|
- srv = sr_parse_srv(chunks);
|
|
|
|
- if (srv == NULL) {
|
|
|
|
- log_warn(LD_DIR, "SR: Unparseable SRV %s", escaped(tok->object_body));
|
|
|
|
- goto end;
|
|
|
|
- }
|
|
|
|
- /* All is good. */
|
|
|
|
- *srv_out = srv;
|
|
|
|
- ret = 0;
|
|
|
|
- end:
|
|
|
|
- smartlist_free(chunks);
|
|
|
|
- return ret;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-/** Extract any shared random values found in <b>tokens</b> and place them in
|
|
|
|
- * the networkstatus <b>ns</b>. */
|
|
|
|
-static void
|
|
|
|
-extract_shared_random_srvs(networkstatus_t *ns, smartlist_t *tokens)
|
|
|
|
-{
|
|
|
|
- const char *voter_identity;
|
|
|
|
- networkstatus_voter_info_t *voter;
|
|
|
|
-
|
|
|
|
- tor_assert(ns);
|
|
|
|
- tor_assert(tokens);
|
|
|
|
- /* Can be only one of them else code flow. */
|
|
|
|
- tor_assert(ns->type == NS_TYPE_VOTE || ns->type == NS_TYPE_CONSENSUS);
|
|
|
|
-
|
|
|
|
- if (ns->type == NS_TYPE_VOTE) {
|
|
|
|
- voter = smartlist_get(ns->voters, 0);
|
|
|
|
- tor_assert(voter);
|
|
|
|
- voter_identity = hex_str(voter->identity_digest,
|
|
|
|
- sizeof(voter->identity_digest));
|
|
|
|
- } else {
|
|
|
|
- /* Consensus has multiple voters so no specific voter. */
|
|
|
|
- voter_identity = "consensus";
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /* We extract both, and on error everything is stopped because it means
|
|
|
|
- * the vote is malformed for the shared random value(s). */
|
|
|
|
- if (extract_one_srv(tokens, K_PREVIOUS_SRV, &ns->sr_info.previous_srv) < 0) {
|
|
|
|
- log_warn(LD_DIR, "SR: Unable to parse previous SRV from %s",
|
|
|
|
- voter_identity);
|
|
|
|
- /* Maybe we have a chance with the current SRV so let's try it anyway. */
|
|
|
|
- }
|
|
|
|
- if (extract_one_srv(tokens, K_CURRENT_SRV, &ns->sr_info.current_srv) < 0) {
|
|
|
|
- log_warn(LD_DIR, "SR: Unable to parse current SRV from %s",
|
|
|
|
- voter_identity);
|
|
|
|
- }
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-/** Parse a v3 networkstatus vote, opinion, or consensus (depending on
|
|
|
|
- * ns_type), from <b>s</b>, and return the result. Return NULL on failure. */
|
|
|
|
-networkstatus_t *
|
|
|
|
-networkstatus_parse_vote_from_string(const char *s, const char **eos_out,
|
|
|
|
- networkstatus_type_t ns_type)
|
|
|
|
-{
|
|
|
|
- smartlist_t *tokens = smartlist_new();
|
|
|
|
- smartlist_t *rs_tokens = NULL, *footer_tokens = NULL;
|
|
|
|
- networkstatus_voter_info_t *voter = NULL;
|
|
|
|
- networkstatus_t *ns = NULL;
|
|
|
|
- common_digests_t ns_digests;
|
|
|
|
- uint8_t sha3_as_signed[DIGEST256_LEN];
|
|
|
|
- const char *cert, *end_of_header, *end_of_footer, *s_dup = s;
|
|
|
|
- directory_token_t *tok;
|
|
|
|
- struct in_addr in;
|
|
|
|
- int i, inorder, n_signatures = 0;
|
|
|
|
- memarea_t *area = NULL, *rs_area = NULL;
|
|
|
|
- consensus_flavor_t flav = FLAV_NS;
|
|
|
|
- char *last_kwd=NULL;
|
|
|
|
-
|
|
|
|
- tor_assert(s);
|
|
|
|
-
|
|
|
|
- if (eos_out)
|
|
|
|
- *eos_out = NULL;
|
|
|
|
-
|
|
|
|
- if (router_get_networkstatus_v3_hashes(s, &ns_digests) ||
|
|
|
|
- router_get_networkstatus_v3_sha3_as_signed(sha3_as_signed, s)<0) {
|
|
|
|
- log_warn(LD_DIR, "Unable to compute digest of network-status");
|
|
|
|
- goto err;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- area = memarea_new();
|
|
|
|
- end_of_header = find_start_of_next_routerstatus(s);
|
|
|
|
- if (tokenize_string(area, s, end_of_header, tokens,
|
|
|
|
- (ns_type == NS_TYPE_CONSENSUS) ?
|
|
|
|
- networkstatus_consensus_token_table :
|
|
|
|
- networkstatus_token_table, 0)) {
|
|
|
|
- log_warn(LD_DIR, "Error tokenizing network-status header");
|
|
|
|
- goto err;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- ns = tor_malloc_zero(sizeof(networkstatus_t));
|
|
|
|
- memcpy(&ns->digests, &ns_digests, sizeof(ns_digests));
|
|
|
|
- memcpy(&ns->digest_sha3_as_signed, sha3_as_signed, sizeof(sha3_as_signed));
|
|
|
|
-
|
|
|
|
- tok = find_by_keyword(tokens, K_NETWORK_STATUS_VERSION);
|
|
|
|
- tor_assert(tok);
|
|
|
|
- if (tok->n_args > 1) {
|
|
|
|
- int flavor = networkstatus_parse_flavor_name(tok->args[1]);
|
|
|
|
- if (flavor < 0) {
|
|
|
|
- log_warn(LD_DIR, "Can't parse document with unknown flavor %s",
|
|
|
|
- escaped(tok->args[1]));
|
|
|
|
- goto err;
|
|
|
|
- }
|
|
|
|
- ns->flavor = flav = flavor;
|
|
|
|
- }
|
|
|
|
- if (flav != FLAV_NS && ns_type != NS_TYPE_CONSENSUS) {
|
|
|
|
- log_warn(LD_DIR, "Flavor found on non-consensus networkstatus.");
|
|
|
|
- goto err;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (ns_type != NS_TYPE_CONSENSUS) {
|
|
|
|
- const char *end_of_cert = NULL;
|
|
|
|
- if (!(cert = strstr(s, "\ndir-key-certificate-version")))
|
|
|
|
- goto err;
|
|
|
|
- ++cert;
|
|
|
|
- ns->cert = authority_cert_parse_from_string(cert, &end_of_cert);
|
|
|
|
- if (!ns->cert || !end_of_cert || end_of_cert > end_of_header)
|
|
|
|
- goto err;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- tok = find_by_keyword(tokens, K_VOTE_STATUS);
|
|
|
|
- tor_assert(tok->n_args);
|
|
|
|
- if (!strcmp(tok->args[0], "vote")) {
|
|
|
|
- ns->type = NS_TYPE_VOTE;
|
|
|
|
- } else if (!strcmp(tok->args[0], "consensus")) {
|
|
|
|
- ns->type = NS_TYPE_CONSENSUS;
|
|
|
|
- } else if (!strcmp(tok->args[0], "opinion")) {
|
|
|
|
- ns->type = NS_TYPE_OPINION;
|
|
|
|
- } else {
|
|
|
|
- log_warn(LD_DIR, "Unrecognized vote status %s in network-status",
|
|
|
|
- escaped(tok->args[0]));
|
|
|
|
- goto err;
|
|
|
|
- }
|
|
|
|
- if (ns_type != ns->type) {
|
|
|
|
- log_warn(LD_DIR, "Got the wrong kind of v3 networkstatus.");
|
|
|
|
- goto err;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (ns->type == NS_TYPE_VOTE || ns->type == NS_TYPE_OPINION) {
|
|
|
|
- tok = find_by_keyword(tokens, K_PUBLISHED);
|
|
|
|
- if (parse_iso_time(tok->args[0], &ns->published))
|
|
|
|
- goto err;
|
|
|
|
-
|
|
|
|
- ns->supported_methods = smartlist_new();
|
|
|
|
- tok = find_opt_by_keyword(tokens, K_CONSENSUS_METHODS);
|
|
|
|
- if (tok) {
|
|
|
|
- for (i=0; i < tok->n_args; ++i)
|
|
|
|
- smartlist_add_strdup(ns->supported_methods, tok->args[i]);
|
|
|
|
- } else {
|
|
|
|
- smartlist_add_strdup(ns->supported_methods, "1");
|
|
|
|
- }
|
|
|
|
- } else {
|
|
|
|
- tok = find_opt_by_keyword(tokens, K_CONSENSUS_METHOD);
|
|
|
|
- if (tok) {
|
|
|
|
- int num_ok;
|
|
|
|
- ns->consensus_method = (int)tor_parse_long(tok->args[0], 10, 1, INT_MAX,
|
|
|
|
- &num_ok, NULL);
|
|
|
|
- if (!num_ok)
|
|
|
|
- goto err;
|
|
|
|
- } else {
|
|
|
|
- ns->consensus_method = 1;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if ((tok = find_opt_by_keyword(tokens, K_RECOMMENDED_CLIENT_PROTOCOLS)))
|
|
|
|
- ns->recommended_client_protocols = tor_strdup(tok->args[0]);
|
|
|
|
- if ((tok = find_opt_by_keyword(tokens, K_RECOMMENDED_RELAY_PROTOCOLS)))
|
|
|
|
- ns->recommended_relay_protocols = tor_strdup(tok->args[0]);
|
|
|
|
- if ((tok = find_opt_by_keyword(tokens, K_REQUIRED_CLIENT_PROTOCOLS)))
|
|
|
|
- ns->required_client_protocols = tor_strdup(tok->args[0]);
|
|
|
|
- if ((tok = find_opt_by_keyword(tokens, K_REQUIRED_RELAY_PROTOCOLS)))
|
|
|
|
- ns->required_relay_protocols = tor_strdup(tok->args[0]);
|
|
|
|
-
|
|
|
|
- tok = find_by_keyword(tokens, K_VALID_AFTER);
|
|
|
|
- if (parse_iso_time(tok->args[0], &ns->valid_after))
|
|
|
|
- goto err;
|
|
|
|
-
|
|
|
|
- tok = find_by_keyword(tokens, K_FRESH_UNTIL);
|
|
|
|
- if (parse_iso_time(tok->args[0], &ns->fresh_until))
|
|
|
|
- goto err;
|
|
|
|
-
|
|
|
|
- tok = find_by_keyword(tokens, K_VALID_UNTIL);
|
|
|
|
- if (parse_iso_time(tok->args[0], &ns->valid_until))
|
|
|
|
- goto err;
|
|
|
|
-
|
|
|
|
- tok = find_by_keyword(tokens, K_VOTING_DELAY);
|
|
|
|
- tor_assert(tok->n_args >= 2);
|
|
|
|
- {
|
|
|
|
- int ok;
|
|
|
|
- ns->vote_seconds =
|
|
|
|
- (int) tor_parse_long(tok->args[0], 10, 0, INT_MAX, &ok, NULL);
|
|
|
|
- if (!ok)
|
|
|
|
- goto err;
|
|
|
|
- ns->dist_seconds =
|
|
|
|
- (int) tor_parse_long(tok->args[1], 10, 0, INT_MAX, &ok, NULL);
|
|
|
|
- if (!ok)
|
|
|
|
- goto err;
|
|
|
|
- }
|
|
|
|
- if (ns->valid_after +
|
|
|
|
- (get_options()->TestingTorNetwork ?
|
|
|
|
- MIN_VOTE_INTERVAL_TESTING : MIN_VOTE_INTERVAL) > ns->fresh_until) {
|
|
|
|
- log_warn(LD_DIR, "Vote/consensus freshness interval is too short");
|
|
|
|
- goto err;
|
|
|
|
- }
|
|
|
|
- if (ns->valid_after +
|
|
|
|
- (get_options()->TestingTorNetwork ?
|
|
|
|
- MIN_VOTE_INTERVAL_TESTING : MIN_VOTE_INTERVAL)*2 > ns->valid_until) {
|
|
|
|
- log_warn(LD_DIR, "Vote/consensus liveness interval is too short");
|
|
|
|
- goto err;
|
|
|
|
- }
|
|
|
|
- if (ns->vote_seconds < MIN_VOTE_SECONDS) {
|
|
|
|
- log_warn(LD_DIR, "Vote seconds is too short");
|
|
|
|
- goto err;
|
|
|
|
- }
|
|
|
|
- if (ns->dist_seconds < MIN_DIST_SECONDS) {
|
|
|
|
- log_warn(LD_DIR, "Dist seconds is too short");
|
|
|
|
- goto err;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if ((tok = find_opt_by_keyword(tokens, K_CLIENT_VERSIONS))) {
|
|
|
|
- ns->client_versions = tor_strdup(tok->args[0]);
|
|
|
|
- }
|
|
|
|
- if ((tok = find_opt_by_keyword(tokens, K_SERVER_VERSIONS))) {
|
|
|
|
- ns->server_versions = tor_strdup(tok->args[0]);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- {
|
|
|
|
- smartlist_t *package_lst = find_all_by_keyword(tokens, K_PACKAGE);
|
|
|
|
- ns->package_lines = smartlist_new();
|
|
|
|
- if (package_lst) {
|
|
|
|
- SMARTLIST_FOREACH(package_lst, directory_token_t *, t,
|
|
|
|
- smartlist_add_strdup(ns->package_lines, t->args[0]));
|
|
|
|
- }
|
|
|
|
- smartlist_free(package_lst);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- tok = find_by_keyword(tokens, K_KNOWN_FLAGS);
|
|
|
|
- ns->known_flags = smartlist_new();
|
|
|
|
- inorder = 1;
|
|
|
|
- for (i = 0; i < tok->n_args; ++i) {
|
|
|
|
- smartlist_add_strdup(ns->known_flags, tok->args[i]);
|
|
|
|
- if (i>0 && strcmp(tok->args[i-1], tok->args[i])>= 0) {
|
|
|
|
- log_warn(LD_DIR, "%s >= %s", tok->args[i-1], tok->args[i]);
|
|
|
|
- inorder = 0;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- if (!inorder) {
|
|
|
|
- log_warn(LD_DIR, "known-flags not in order");
|
|
|
|
- goto err;
|
|
|
|
- }
|
|
|
|
- if (ns->type != NS_TYPE_CONSENSUS &&
|
|
|
|
- smartlist_len(ns->known_flags) > MAX_KNOWN_FLAGS_IN_VOTE) {
|
|
|
|
- /* If we allowed more than 64 flags in votes, then parsing them would make
|
|
|
|
- * us invoke undefined behavior whenever we used 1<<flagnum to do a
|
|
|
|
- * bit-shift. This is only for votes and opinions: consensus users don't
|
|
|
|
- * care about flags they don't recognize, and so don't build a bitfield
|
|
|
|
- * for them. */
|
|
|
|
- log_warn(LD_DIR, "Too many known-flags in consensus vote or opinion");
|
|
|
|
- goto err;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- tok = find_opt_by_keyword(tokens, K_PARAMS);
|
|
|
|
- if (tok) {
|
|
|
|
- int any_dups = 0;
|
|
|
|
- inorder = 1;
|
|
|
|
- ns->net_params = smartlist_new();
|
|
|
|
- for (i = 0; i < tok->n_args; ++i) {
|
|
|
|
- int ok=0;
|
|
|
|
- char *eq = strchr(tok->args[i], '=');
|
|
|
|
- size_t eq_pos;
|
|
|
|
- if (!eq) {
|
|
|
|
- log_warn(LD_DIR, "Bad element '%s' in params", escaped(tok->args[i]));
|
|
|
|
- goto err;
|
|
|
|
- }
|
|
|
|
- eq_pos = eq-tok->args[i];
|
|
|
|
- tor_parse_long(eq+1, 10, INT32_MIN, INT32_MAX, &ok, NULL);
|
|
|
|
- if (!ok) {
|
|
|
|
- log_warn(LD_DIR, "Bad element '%s' in params", escaped(tok->args[i]));
|
|
|
|
- goto err;
|
|
|
|
- }
|
|
|
|
- if (i > 0 && strcmp(tok->args[i-1], tok->args[i]) >= 0) {
|
|
|
|
- log_warn(LD_DIR, "%s >= %s", tok->args[i-1], tok->args[i]);
|
|
|
|
- inorder = 0;
|
|
|
|
- }
|
|
|
|
- if (last_kwd && eq_pos == strlen(last_kwd) &&
|
|
|
|
- fast_memeq(last_kwd, tok->args[i], eq_pos)) {
|
|
|
|
- log_warn(LD_DIR, "Duplicate value for %s parameter",
|
|
|
|
- escaped(tok->args[i]));
|
|
|
|
- any_dups = 1;
|
|
|
|
- }
|
|
|
|
- tor_free(last_kwd);
|
|
|
|
- last_kwd = tor_strndup(tok->args[i], eq_pos);
|
|
|
|
- smartlist_add_strdup(ns->net_params, tok->args[i]);
|
|
|
|
- }
|
|
|
|
- if (!inorder) {
|
|
|
|
- log_warn(LD_DIR, "params not in order");
|
|
|
|
- goto err;
|
|
|
|
- }
|
|
|
|
- if (any_dups) {
|
|
|
|
- log_warn(LD_DIR, "Duplicate in parameters");
|
|
|
|
- goto err;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- ns->voters = smartlist_new();
|
|
|
|
-
|
|
|
|
- SMARTLIST_FOREACH_BEGIN(tokens, directory_token_t *, _tok) {
|
|
|
|
- tok = _tok;
|
|
|
|
- if (tok->tp == K_DIR_SOURCE) {
|
|
|
|
- tor_assert(tok->n_args >= 6);
|
|
|
|
-
|
|
|
|
- if (voter)
|
|
|
|
- smartlist_add(ns->voters, voter);
|
|
|
|
- voter = tor_malloc_zero(sizeof(networkstatus_voter_info_t));
|
|
|
|
- voter->sigs = smartlist_new();
|
|
|
|
- if (ns->type != NS_TYPE_CONSENSUS)
|
|
|
|
- memcpy(voter->vote_digest, ns_digests.d[DIGEST_SHA1], DIGEST_LEN);
|
|
|
|
-
|
|
|
|
- voter->nickname = tor_strdup(tok->args[0]);
|
|
|
|
- if (strlen(tok->args[1]) != HEX_DIGEST_LEN ||
|
|
|
|
- base16_decode(voter->identity_digest, sizeof(voter->identity_digest),
|
|
|
|
- tok->args[1], HEX_DIGEST_LEN)
|
|
|
|
- != sizeof(voter->identity_digest)) {
|
|
|
|
- log_warn(LD_DIR, "Error decoding identity digest %s in "
|
|
|
|
- "network-status document.", escaped(tok->args[1]));
|
|
|
|
- goto err;
|
|
|
|
- }
|
|
|
|
- if (ns->type != NS_TYPE_CONSENSUS &&
|
|
|
|
- tor_memneq(ns->cert->cache_info.identity_digest,
|
|
|
|
- voter->identity_digest, DIGEST_LEN)) {
|
|
|
|
- log_warn(LD_DIR,"Mismatch between identities in certificate and vote");
|
|
|
|
- goto err;
|
|
|
|
- }
|
|
|
|
- if (ns->type != NS_TYPE_CONSENSUS) {
|
|
|
|
- if (authority_cert_is_blacklisted(ns->cert)) {
|
|
|
|
- log_warn(LD_DIR, "Rejecting vote signature made with blacklisted "
|
|
|
|
- "signing key %s",
|
|
|
|
- hex_str(ns->cert->signing_key_digest, DIGEST_LEN));
|
|
|
|
- goto err;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- voter->address = tor_strdup(tok->args[2]);
|
|
|
|
- if (!tor_inet_aton(tok->args[3], &in)) {
|
|
|
|
- log_warn(LD_DIR, "Error decoding IP address %s in network-status.",
|
|
|
|
- escaped(tok->args[3]));
|
|
|
|
- goto err;
|
|
|
|
- }
|
|
|
|
- voter->addr = ntohl(in.s_addr);
|
|
|
|
- int ok;
|
|
|
|
- voter->dir_port = (uint16_t)
|
|
|
|
- tor_parse_long(tok->args[4], 10, 0, 65535, &ok, NULL);
|
|
|
|
- if (!ok)
|
|
|
|
- goto err;
|
|
|
|
- voter->or_port = (uint16_t)
|
|
|
|
- tor_parse_long(tok->args[5], 10, 0, 65535, &ok, NULL);
|
|
|
|
- if (!ok)
|
|
|
|
- goto err;
|
|
|
|
- } else if (tok->tp == K_CONTACT) {
|
|
|
|
- if (!voter || voter->contact) {
|
|
|
|
- log_warn(LD_DIR, "contact element is out of place.");
|
|
|
|
- goto err;
|
|
|
|
- }
|
|
|
|
- voter->contact = tor_strdup(tok->args[0]);
|
|
|
|
- } else if (tok->tp == K_VOTE_DIGEST) {
|
|
|
|
- tor_assert(ns->type == NS_TYPE_CONSENSUS);
|
|
|
|
- tor_assert(tok->n_args >= 1);
|
|
|
|
- if (!voter || ! tor_digest_is_zero(voter->vote_digest)) {
|
|
|
|
- log_warn(LD_DIR, "vote-digest element is out of place.");
|
|
|
|
- goto err;
|
|
|
|
- }
|
|
|
|
- if (strlen(tok->args[0]) != HEX_DIGEST_LEN ||
|
|
|
|
- base16_decode(voter->vote_digest, sizeof(voter->vote_digest),
|
|
|
|
- tok->args[0], HEX_DIGEST_LEN)
|
|
|
|
- != sizeof(voter->vote_digest)) {
|
|
|
|
- log_warn(LD_DIR, "Error decoding vote digest %s in "
|
|
|
|
- "network-status consensus.", escaped(tok->args[0]));
|
|
|
|
- goto err;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- } SMARTLIST_FOREACH_END(_tok);
|
|
|
|
- if (voter) {
|
|
|
|
- smartlist_add(ns->voters, voter);
|
|
|
|
- voter = NULL;
|
|
|
|
- }
|
|
|
|
- if (smartlist_len(ns->voters) == 0) {
|
|
|
|
- log_warn(LD_DIR, "Missing dir-source elements in a networkstatus.");
|
|
|
|
- goto err;
|
|
|
|
- } else if (ns->type != NS_TYPE_CONSENSUS && smartlist_len(ns->voters) != 1) {
|
|
|
|
- log_warn(LD_DIR, "Too many dir-source elements in a vote networkstatus.");
|
|
|
|
- goto err;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (ns->type != NS_TYPE_CONSENSUS &&
|
|
|
|
- (tok = find_opt_by_keyword(tokens, K_LEGACY_DIR_KEY))) {
|
|
|
|
- int bad = 1;
|
|
|
|
- if (strlen(tok->args[0]) == HEX_DIGEST_LEN) {
|
|
|
|
- networkstatus_voter_info_t *voter_0 = smartlist_get(ns->voters, 0);
|
|
|
|
- if (base16_decode(voter_0->legacy_id_digest, DIGEST_LEN,
|
|
|
|
- tok->args[0], HEX_DIGEST_LEN) != DIGEST_LEN)
|
|
|
|
- bad = 1;
|
|
|
|
- else
|
|
|
|
- bad = 0;
|
|
|
|
- }
|
|
|
|
- if (bad) {
|
|
|
|
- log_warn(LD_DIR, "Invalid legacy key digest %s on vote.",
|
|
|
|
- escaped(tok->args[0]));
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /* If this is a vote document, check if information about the shared
|
|
|
|
- randomness protocol is included, and extract it. */
|
|
|
|
- if (ns->type == NS_TYPE_VOTE) {
|
|
|
|
- dirvote_parse_sr_commits(ns, tokens);
|
|
|
|
- }
|
|
|
|
- /* For both a vote and consensus, extract the shared random values. */
|
|
|
|
- if (ns->type == NS_TYPE_VOTE || ns->type == NS_TYPE_CONSENSUS) {
|
|
|
|
- extract_shared_random_srvs(ns, tokens);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /* Parse routerstatus lines. */
|
|
|
|
- rs_tokens = smartlist_new();
|
|
|
|
- rs_area = memarea_new();
|
|
|
|
- s = end_of_header;
|
|
|
|
- ns->routerstatus_list = smartlist_new();
|
|
|
|
-
|
|
|
|
- while (!strcmpstart(s, "r ")) {
|
|
|
|
- if (ns->type != NS_TYPE_CONSENSUS) {
|
|
|
|
- vote_routerstatus_t *rs = tor_malloc_zero(sizeof(vote_routerstatus_t));
|
|
|
|
- if (routerstatus_parse_entry_from_string(rs_area, &s, rs_tokens, ns,
|
|
|
|
- rs, 0, 0)) {
|
|
|
|
- smartlist_add(ns->routerstatus_list, rs);
|
|
|
|
- } else {
|
|
|
|
- vote_routerstatus_free(rs);
|
|
|
|
- }
|
|
|
|
- } else {
|
|
|
|
- routerstatus_t *rs;
|
|
|
|
- if ((rs = routerstatus_parse_entry_from_string(rs_area, &s, rs_tokens,
|
|
|
|
- NULL, NULL,
|
|
|
|
- ns->consensus_method,
|
|
|
|
- flav))) {
|
|
|
|
- /* Use exponential-backoff scheduling when downloading microdescs */
|
|
|
|
- smartlist_add(ns->routerstatus_list, rs);
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- for (i = 1; i < smartlist_len(ns->routerstatus_list); ++i) {
|
|
|
|
- routerstatus_t *rs1, *rs2;
|
|
|
|
- if (ns->type != NS_TYPE_CONSENSUS) {
|
|
|
|
- vote_routerstatus_t *a = smartlist_get(ns->routerstatus_list, i-1);
|
|
|
|
- vote_routerstatus_t *b = smartlist_get(ns->routerstatus_list, i);
|
|
|
|
- rs1 = &a->status; rs2 = &b->status;
|
|
|
|
- } else {
|
|
|
|
- rs1 = smartlist_get(ns->routerstatus_list, i-1);
|
|
|
|
- rs2 = smartlist_get(ns->routerstatus_list, i);
|
|
|
|
- }
|
|
|
|
- if (fast_memcmp(rs1->identity_digest, rs2->identity_digest, DIGEST_LEN)
|
|
|
|
- >= 0) {
|
|
|
|
- log_warn(LD_DIR, "Networkstatus entries not sorted by identity digest");
|
|
|
|
- goto err;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- if (ns_type != NS_TYPE_CONSENSUS) {
|
|
|
|
- digest256map_t *ed_id_map = digest256map_new();
|
|
|
|
- SMARTLIST_FOREACH_BEGIN(ns->routerstatus_list, vote_routerstatus_t *,
|
|
|
|
- vrs) {
|
|
|
|
- if (! vrs->has_ed25519_listing ||
|
|
|
|
- tor_mem_is_zero((const char *)vrs->ed25519_id, DIGEST256_LEN))
|
|
|
|
- continue;
|
|
|
|
- if (digest256map_get(ed_id_map, vrs->ed25519_id) != NULL) {
|
|
|
|
- log_warn(LD_DIR, "Vote networkstatus ed25519 identities were not "
|
|
|
|
- "unique");
|
|
|
|
- digest256map_free(ed_id_map, NULL);
|
|
|
|
- goto err;
|
|
|
|
- }
|
|
|
|
- digest256map_set(ed_id_map, vrs->ed25519_id, (void*)1);
|
|
|
|
- } SMARTLIST_FOREACH_END(vrs);
|
|
|
|
- digest256map_free(ed_id_map, NULL);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /* Parse footer; check signature. */
|
|
|
|
- footer_tokens = smartlist_new();
|
|
|
|
- if ((end_of_footer = strstr(s, "\nnetwork-status-version ")))
|
|
|
|
- ++end_of_footer;
|
|
|
|
- else
|
|
|
|
- end_of_footer = s + strlen(s);
|
|
|
|
- if (tokenize_string(area,s, end_of_footer, footer_tokens,
|
|
|
|
- networkstatus_vote_footer_token_table, 0)) {
|
|
|
|
- log_warn(LD_DIR, "Error tokenizing network-status vote footer.");
|
|
|
|
- goto err;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- {
|
|
|
|
- int found_sig = 0;
|
|
|
|
- SMARTLIST_FOREACH_BEGIN(footer_tokens, directory_token_t *, _tok) {
|
|
|
|
- tok = _tok;
|
|
|
|
- if (tok->tp == K_DIRECTORY_SIGNATURE)
|
|
|
|
- found_sig = 1;
|
|
|
|
- else if (found_sig) {
|
|
|
|
- log_warn(LD_DIR, "Extraneous token after first directory-signature");
|
|
|
|
- goto err;
|
|
|
|
- }
|
|
|
|
- } SMARTLIST_FOREACH_END(_tok);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if ((tok = find_opt_by_keyword(footer_tokens, K_DIRECTORY_FOOTER))) {
|
|
|
|
- if (tok != smartlist_get(footer_tokens, 0)) {
|
|
|
|
- log_warn(LD_DIR, "Misplaced directory-footer token");
|
|
|
|
- goto err;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- tok = find_opt_by_keyword(footer_tokens, K_BW_WEIGHTS);
|
|
|
|
- if (tok) {
|
|
|
|
- ns->weight_params = smartlist_new();
|
|
|
|
- for (i = 0; i < tok->n_args; ++i) {
|
|
|
|
- int ok=0;
|
|
|
|
- char *eq = strchr(tok->args[i], '=');
|
|
|
|
- if (!eq) {
|
|
|
|
- log_warn(LD_DIR, "Bad element '%s' in weight params",
|
|
|
|
- escaped(tok->args[i]));
|
|
|
|
- goto err;
|
|
|
|
- }
|
|
|
|
- tor_parse_long(eq+1, 10, INT32_MIN, INT32_MAX, &ok, NULL);
|
|
|
|
- if (!ok) {
|
|
|
|
- log_warn(LD_DIR, "Bad element '%s' in params", escaped(tok->args[i]));
|
|
|
|
- goto err;
|
|
|
|
- }
|
|
|
|
- smartlist_add_strdup(ns->weight_params, tok->args[i]);
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- SMARTLIST_FOREACH_BEGIN(footer_tokens, directory_token_t *, _tok) {
|
|
|
|
- char declared_identity[DIGEST_LEN];
|
|
|
|
- networkstatus_voter_info_t *v;
|
|
|
|
- document_signature_t *sig;
|
|
|
|
- const char *id_hexdigest = NULL;
|
|
|
|
- const char *sk_hexdigest = NULL;
|
|
|
|
- digest_algorithm_t alg = DIGEST_SHA1;
|
|
|
|
- tok = _tok;
|
|
|
|
- if (tok->tp != K_DIRECTORY_SIGNATURE)
|
|
|
|
- continue;
|
|
|
|
- tor_assert(tok->n_args >= 2);
|
|
|
|
- if (tok->n_args == 2) {
|
|
|
|
- id_hexdigest = tok->args[0];
|
|
|
|
- sk_hexdigest = tok->args[1];
|
|
|
|
- } else {
|
|
|
|
- const char *algname = tok->args[0];
|
|
|
|
- int a;
|
|
|
|
- id_hexdigest = tok->args[1];
|
|
|
|
- sk_hexdigest = tok->args[2];
|
|
|
|
- a = crypto_digest_algorithm_parse_name(algname);
|
|
|
|
- if (a<0) {
|
|
|
|
- log_warn(LD_DIR, "Unknown digest algorithm %s; skipping",
|
|
|
|
- escaped(algname));
|
|
|
|
- continue;
|
|
|
|
- }
|
|
|
|
- alg = a;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (!tok->object_type ||
|
|
|
|
- strcmp(tok->object_type, "SIGNATURE") ||
|
|
|
|
- tok->object_size < 128 || tok->object_size > 512) {
|
|
|
|
- log_warn(LD_DIR, "Bad object type or length on directory-signature");
|
|
|
|
- goto err;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (strlen(id_hexdigest) != HEX_DIGEST_LEN ||
|
|
|
|
- base16_decode(declared_identity, sizeof(declared_identity),
|
|
|
|
- id_hexdigest, HEX_DIGEST_LEN)
|
|
|
|
- != sizeof(declared_identity)) {
|
|
|
|
- log_warn(LD_DIR, "Error decoding declared identity %s in "
|
|
|
|
- "network-status document.", escaped(id_hexdigest));
|
|
|
|
- goto err;
|
|
|
|
- }
|
|
|
|
- if (!(v = networkstatus_get_voter_by_id(ns, declared_identity))) {
|
|
|
|
- log_warn(LD_DIR, "ID on signature on network-status document does "
|
|
|
|
- "not match any declared directory source.");
|
|
|
|
- goto err;
|
|
|
|
- }
|
|
|
|
- sig = tor_malloc_zero(sizeof(document_signature_t));
|
|
|
|
- memcpy(sig->identity_digest, v->identity_digest, DIGEST_LEN);
|
|
|
|
- sig->alg = alg;
|
|
|
|
- if (strlen(sk_hexdigest) != HEX_DIGEST_LEN ||
|
|
|
|
- base16_decode(sig->signing_key_digest, sizeof(sig->signing_key_digest),
|
|
|
|
- sk_hexdigest, HEX_DIGEST_LEN)
|
|
|
|
- != sizeof(sig->signing_key_digest)) {
|
|
|
|
- log_warn(LD_DIR, "Error decoding declared signing key digest %s in "
|
|
|
|
- "network-status document.", escaped(sk_hexdigest));
|
|
|
|
- tor_free(sig);
|
|
|
|
- goto err;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (ns->type != NS_TYPE_CONSENSUS) {
|
|
|
|
- if (tor_memneq(declared_identity, ns->cert->cache_info.identity_digest,
|
|
|
|
- DIGEST_LEN)) {
|
|
|
|
- log_warn(LD_DIR, "Digest mismatch between declared and actual on "
|
|
|
|
- "network-status vote.");
|
|
|
|
- tor_free(sig);
|
|
|
|
- goto err;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (networkstatus_get_voter_sig_by_alg(v, sig->alg)) {
|
|
|
|
- /* We already parsed a vote with this algorithm from this voter. Use the
|
|
|
|
- first one. */
|
|
|
|
- log_fn(LOG_PROTOCOL_WARN, LD_DIR, "We received a networkstatus "
|
|
|
|
- "that contains two signatures from the same voter with the same "
|
|
|
|
- "algorithm. Ignoring the second signature.");
|
|
|
|
- tor_free(sig);
|
|
|
|
- continue;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (ns->type != NS_TYPE_CONSENSUS) {
|
|
|
|
- if (check_signature_token(ns_digests.d[DIGEST_SHA1], DIGEST_LEN,
|
|
|
|
- tok, ns->cert->signing_key, 0,
|
|
|
|
- "network-status document")) {
|
|
|
|
- tor_free(sig);
|
|
|
|
- goto err;
|
|
|
|
- }
|
|
|
|
- sig->good_signature = 1;
|
|
|
|
- } else {
|
|
|
|
- if (tok->object_size >= INT_MAX || tok->object_size >= SIZE_T_CEILING) {
|
|
|
|
- tor_free(sig);
|
|
|
|
- goto err;
|
|
|
|
- }
|
|
|
|
- sig->signature = tor_memdup(tok->object_body, tok->object_size);
|
|
|
|
- sig->signature_len = (int) tok->object_size;
|
|
|
|
- }
|
|
|
|
- smartlist_add(v->sigs, sig);
|
|
|
|
-
|
|
|
|
- ++n_signatures;
|
|
|
|
- } SMARTLIST_FOREACH_END(_tok);
|
|
|
|
-
|
|
|
|
- if (! n_signatures) {
|
|
|
|
- log_warn(LD_DIR, "No signatures on networkstatus document.");
|
|
|
|
- goto err;
|
|
|
|
- } else if (ns->type == NS_TYPE_VOTE && n_signatures != 1) {
|
|
|
|
- log_warn(LD_DIR, "Received more than one signature on a "
|
|
|
|
- "network-status vote.");
|
|
|
|
- goto err;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (eos_out)
|
|
|
|
- *eos_out = end_of_footer;
|
|
|
|
-
|
|
|
|
- goto done;
|
|
|
|
- err:
|
|
|
|
- dump_desc(s_dup, "v3 networkstatus");
|
|
|
|
- networkstatus_vote_free(ns);
|
|
|
|
- ns = NULL;
|
|
|
|
- done:
|
|
|
|
- if (tokens) {
|
|
|
|
- SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_clear(t));
|
|
|
|
- smartlist_free(tokens);
|
|
|
|
- }
|
|
|
|
- if (voter) {
|
|
|
|
- if (voter->sigs) {
|
|
|
|
- SMARTLIST_FOREACH(voter->sigs, document_signature_t *, sig,
|
|
|
|
- document_signature_free(sig));
|
|
|
|
- smartlist_free(voter->sigs);
|
|
|
|
- }
|
|
|
|
- tor_free(voter->nickname);
|
|
|
|
- tor_free(voter->address);
|
|
|
|
- tor_free(voter->contact);
|
|
|
|
- tor_free(voter);
|
|
|
|
- }
|
|
|
|
- if (rs_tokens) {
|
|
|
|
- SMARTLIST_FOREACH(rs_tokens, directory_token_t *, t, token_clear(t));
|
|
|
|
- smartlist_free(rs_tokens);
|
|
|
|
- }
|
|
|
|
- if (footer_tokens) {
|
|
|
|
- SMARTLIST_FOREACH(footer_tokens, directory_token_t *, t, token_clear(t));
|
|
|
|
- smartlist_free(footer_tokens);
|
|
|
|
- }
|
|
|
|
- if (area) {
|
|
|
|
- DUMP_AREA(area, "v3 networkstatus");
|
|
|
|
- memarea_drop_all(area);
|
|
|
|
- }
|
|
|
|
- if (rs_area)
|
|
|
|
- memarea_drop_all(rs_area);
|
|
|
|
- tor_free(last_kwd);
|
|
|
|
-
|
|
|
|
- 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.
|