LCOV - code coverage report
Current view: top level - feature/dirparse - ns_parse.c (source / functions) Hit Total Coverage
Test: lcov.info Lines: 723 857 84.4 %
Date: 2021-11-24 03:28:48 Functions: 12 12 100.0 %

          Line data    Source code
       1             : /* Copyright (c) 2001 Matej Pfajfar.
       2             :  * Copyright (c) 2001-2004, Roger Dingledine.
       3             :  * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
       4             :  * Copyright (c) 2007-2021, The Tor Project, Inc. */
       5             : /* See LICENSE for licensing information */
       6             : 
       7             : /**
       8             :  * \file routerparse.c
       9             :  * \brief Code to parse and validate consensus documents and votes.
      10             :  */
      11             : 
      12             : #define NS_PARSE_PRIVATE
      13             : 
      14             : #include "core/or/or.h"
      15             : #include "app/config/config.h"
      16             : #include "core/or/protover.h"
      17             : #include "core/or/versions.h"
      18             : #include "feature/client/entrynodes.h"
      19             : #include "feature/dirauth/dirvote.h"
      20             : #include "feature/dirparse/authcert_parse.h"
      21             : #include "feature/dirparse/ns_parse.h"
      22             : #include "feature/dirparse/parsecommon.h"
      23             : #include "feature/dirparse/routerparse.h"
      24             : #include "feature/dirparse/sigcommon.h"
      25             : #include "feature/dirparse/unparseable.h"
      26             : #include "feature/hs_common/shared_random_client.h"
      27             : #include "feature/nodelist/authcert.h"
      28             : #include "feature/nodelist/describe.h"
      29             : #include "feature/nodelist/networkstatus.h"
      30             : #include "feature/nodelist/nickname.h"
      31             : #include "lib/crypt_ops/crypto_format.h"
      32             : #include "lib/memarea/memarea.h"
      33             : 
      34             : #include "feature/dirauth/vote_microdesc_hash_st.h"
      35             : #include "feature/nodelist/authority_cert_st.h"
      36             : #include "feature/nodelist/document_signature_st.h"
      37             : #include "feature/nodelist/networkstatus_st.h"
      38             : #include "feature/nodelist/networkstatus_voter_info_st.h"
      39             : #include "feature/nodelist/vote_routerstatus_st.h"
      40             : #include "feature/dirparse/authcert_members.h"
      41             : 
      42             : #undef log
      43             : #include <math.h>
      44             : 
      45             : /** List of tokens recognized in the body part of v3 networkstatus
      46             :  * documents. */
      47             : // clang-format off
      48             : static token_rule_t rtrstatus_token_table[] = {
      49             :   T01("p",                   K_P,               CONCAT_ARGS, NO_OBJ ),
      50             :   T1( "r",                   K_R,                   GE(7),   NO_OBJ ),
      51             :   T0N("a",                   K_A,                   GE(1),   NO_OBJ ),
      52             :   T1( "s",                   K_S,                   ARGS,    NO_OBJ ),
      53             :   T01("v",                   K_V,               CONCAT_ARGS, NO_OBJ ),
      54             :   T01("w",                   K_W,                   ARGS,    NO_OBJ ),
      55             :   T0N("m",                   K_M,               CONCAT_ARGS, NO_OBJ ),
      56             :   T0N("id",                  K_ID,                  GE(2),   NO_OBJ ),
      57             :   T1("pr",                   K_PROTO,           CONCAT_ARGS, NO_OBJ ),
      58             :   T0N("opt",                 K_OPT,             CONCAT_ARGS, OBJ_OK ),
      59             :   END_OF_TABLE
      60             : };
      61             : // clang-format on
      62             : 
      63             : /** List of tokens recognized in V3 networkstatus votes. */
      64             : // clang-format off
      65             : static token_rule_t networkstatus_token_table[] = {
      66             :   T1_START("network-status-version", K_NETWORK_STATUS_VERSION,
      67             :                                                    GE(1),       NO_OBJ ),
      68             :   T1("vote-status",            K_VOTE_STATUS,      GE(1),       NO_OBJ ),
      69             :   T1("published",              K_PUBLISHED,        CONCAT_ARGS, NO_OBJ ),
      70             :   T1("valid-after",            K_VALID_AFTER,      CONCAT_ARGS, NO_OBJ ),
      71             :   T1("fresh-until",            K_FRESH_UNTIL,      CONCAT_ARGS, NO_OBJ ),
      72             :   T1("valid-until",            K_VALID_UNTIL,      CONCAT_ARGS, NO_OBJ ),
      73             :   T1("voting-delay",           K_VOTING_DELAY,     GE(2),       NO_OBJ ),
      74             :   T1("known-flags",            K_KNOWN_FLAGS,      ARGS,        NO_OBJ ),
      75             :   T01("params",                K_PARAMS,           ARGS,        NO_OBJ ),
      76             :   T( "fingerprint",            K_FINGERPRINT,      CONCAT_ARGS, NO_OBJ ),
      77             :   T01("signing-ed25519",       K_SIGNING_CERT_ED,  NO_ARGS ,    NEED_OBJ ),
      78             :   T01("shared-rand-participate",K_SR_FLAG,         NO_ARGS,     NO_OBJ ),
      79             :   T0N("shared-rand-commit",    K_COMMIT,           GE(3),       NO_OBJ ),
      80             :   T01("shared-rand-previous-value", K_PREVIOUS_SRV,EQ(2),       NO_OBJ ),
      81             :   T01("shared-rand-current-value",  K_CURRENT_SRV, EQ(2),       NO_OBJ ),
      82             :   T0N("package",               K_PACKAGE,          CONCAT_ARGS, NO_OBJ ),
      83             :   T01("recommended-client-protocols", K_RECOMMENDED_CLIENT_PROTOCOLS,
      84             :       CONCAT_ARGS, NO_OBJ ),
      85             :   T01("recommended-relay-protocols", K_RECOMMENDED_RELAY_PROTOCOLS,
      86             :       CONCAT_ARGS, NO_OBJ ),
      87             :   T01("required-client-protocols",    K_REQUIRED_CLIENT_PROTOCOLS,
      88             :       CONCAT_ARGS, NO_OBJ ),
      89             :   T01("required-relay-protocols",    K_REQUIRED_RELAY_PROTOCOLS,
      90             :       CONCAT_ARGS, NO_OBJ ),
      91             : 
      92             :   AUTHCERT_MEMBERS,
      93             : 
      94             :   T0N("opt",                 K_OPT,             CONCAT_ARGS, OBJ_OK ),
      95             :   T1( "contact",             K_CONTACT,         CONCAT_ARGS, NO_OBJ ),
      96             :   T1( "dir-source",          K_DIR_SOURCE,      GE(6),       NO_OBJ ),
      97             :   T01("legacy-dir-key",      K_LEGACY_DIR_KEY,  GE(1),       NO_OBJ ),
      98             :   T1( "known-flags",         K_KNOWN_FLAGS,     CONCAT_ARGS, NO_OBJ ),
      99             :   T01("client-versions",     K_CLIENT_VERSIONS, CONCAT_ARGS, NO_OBJ ),
     100             :   T01("server-versions",     K_SERVER_VERSIONS, CONCAT_ARGS, NO_OBJ ),
     101             :   T1( "consensus-methods",   K_CONSENSUS_METHODS, GE(1),     NO_OBJ ),
     102             : 
     103             :   END_OF_TABLE
     104             : };
     105             : // clang-format on
     106             : 
     107             : /** List of tokens recognized in V3 networkstatus consensuses. */
     108             : // clang-format off
     109             : static token_rule_t networkstatus_consensus_token_table[] = {
     110             :   T1_START("network-status-version", K_NETWORK_STATUS_VERSION,
     111             :                                                    GE(1),       NO_OBJ ),
     112             :   T1("vote-status",            K_VOTE_STATUS,      GE(1),       NO_OBJ ),
     113             :   T1("valid-after",            K_VALID_AFTER,      CONCAT_ARGS, NO_OBJ ),
     114             :   T1("fresh-until",            K_FRESH_UNTIL,      CONCAT_ARGS, NO_OBJ ),
     115             :   T1("valid-until",            K_VALID_UNTIL,      CONCAT_ARGS, NO_OBJ ),
     116             :   T1("voting-delay",           K_VOTING_DELAY,     GE(2),       NO_OBJ ),
     117             : 
     118             :   T0N("opt",                 K_OPT,             CONCAT_ARGS, OBJ_OK ),
     119             : 
     120             :   T1N("dir-source",          K_DIR_SOURCE,          GE(6),   NO_OBJ ),
     121             :   T1N("contact",             K_CONTACT,         CONCAT_ARGS, NO_OBJ ),
     122             :   T1N("vote-digest",         K_VOTE_DIGEST,         GE(1),   NO_OBJ ),
     123             : 
     124             :   T1( "known-flags",         K_KNOWN_FLAGS,     CONCAT_ARGS, NO_OBJ ),
     125             : 
     126             :   T01("client-versions",     K_CLIENT_VERSIONS, CONCAT_ARGS, NO_OBJ ),
     127             :   T01("server-versions",     K_SERVER_VERSIONS, CONCAT_ARGS, NO_OBJ ),
     128             :   T01("consensus-method",    K_CONSENSUS_METHOD,    EQ(1),   NO_OBJ),
     129             :   T01("params",                K_PARAMS,           ARGS,        NO_OBJ ),
     130             : 
     131             :   T01("shared-rand-previous-value", K_PREVIOUS_SRV, EQ(2),   NO_OBJ ),
     132             :   T01("shared-rand-current-value",  K_CURRENT_SRV,  EQ(2),   NO_OBJ ),
     133             : 
     134             :   T01("recommended-client-protocols", K_RECOMMENDED_CLIENT_PROTOCOLS,
     135             :       CONCAT_ARGS, NO_OBJ ),
     136             :   T01("recommended-relay-protocols", K_RECOMMENDED_RELAY_PROTOCOLS,
     137             :       CONCAT_ARGS, NO_OBJ ),
     138             :   T01("required-client-protocols",    K_REQUIRED_CLIENT_PROTOCOLS,
     139             :       CONCAT_ARGS, NO_OBJ ),
     140             :   T01("required-relay-protocols",    K_REQUIRED_RELAY_PROTOCOLS,
     141             :       CONCAT_ARGS, NO_OBJ ),
     142             : 
     143             :   END_OF_TABLE
     144             : };
     145             : // clang-format on
     146             : 
     147             : /** List of tokens recognized in the footer of v1 directory footers. */
     148             : // clang-format off
     149             : static token_rule_t networkstatus_vote_footer_token_table[] = {
     150             :   T01("directory-footer",    K_DIRECTORY_FOOTER,    NO_ARGS,   NO_OBJ ),
     151             :   T01("bandwidth-weights",   K_BW_WEIGHTS,          ARGS,      NO_OBJ ),
     152             :   T(  "directory-signature", K_DIRECTORY_SIGNATURE, GE(2),     NEED_OBJ ),
     153             :   END_OF_TABLE
     154             : };
     155             : // clang-format on
     156             : 
     157             : /** Try to find the start and end of the signed portion of a networkstatus
     158             :  * document in <b>s</b>. On success, set <b>start_out</b> to the first
     159             :  * character of the document, and <b>end_out</b> to a position one after the
     160             :  * final character of the signed document, and return 0.  On failure, return
     161             :  * -1. */
     162             : int
     163        2877 : router_get_networkstatus_v3_signed_boundaries(const char *s,
     164             :                                               size_t len,
     165             :                                               const char **start_out,
     166             :                                               const char **end_out)
     167             : {
     168        2877 :   return router_get_hash_impl_helper(s, len,
     169             :                                      "network-status-version",
     170             :                                      "\ndirectory-signature",
     171             :                                      ' ', LOG_INFO,
     172             :                                      start_out, end_out);
     173             : }
     174             : 
     175             : /** Set <b>digest_out</b> to the SHA3-256 digest of the signed portion of the
     176             :  * networkstatus vote in <b>s</b> -- or of the entirety of <b>s</b> if no
     177             :  * signed portion can be identified.  Return 0 on success, -1 on failure. */
     178             : int
     179        2849 : router_get_networkstatus_v3_sha3_as_signed(uint8_t *digest_out,
     180             :                                            const char *s, size_t len)
     181             : {
     182        2849 :   const char *start, *end;
     183        2849 :   if (router_get_networkstatus_v3_signed_boundaries(s, len,
     184             :                                                     &start, &end) < 0) {
     185         234 :     start = s;
     186         234 :     end = s + len;
     187             :   }
     188        2849 :   tor_assert(start);
     189        2849 :   tor_assert(end);
     190        2849 :   return crypto_digest256((char*)digest_out, start, end-start,
     191             :                           DIGEST_SHA3_256);
     192             : }
     193             : 
     194             : /** Set <b>digests</b> to all the digests of the consensus document in
     195             :  * <b>s</b> */
     196             : int
     197        2500 : router_get_networkstatus_v3_hashes(const char *s, size_t len,
     198             :                                    common_digests_t *digests)
     199             : {
     200        2500 :   return router_get_hashes_impl(s, len, digests,
     201             :                                 "network-status-version",
     202             :                                 "\ndirectory-signature",
     203             :                                 ' ');
     204             : }
     205             : 
     206             : /** Helper: given a string <b>s</b>, return the start of the next router-status
     207             :  * object (starting with "r " at the start of a line).  If none is found,
     208             :  * return the start of the directory footer, or the next directory signature.
     209             :  * If none is found, return the end of the string. */
     210             : static inline const char *
     211        4811 : find_start_of_next_routerstatus(const char *s, const char *s_eos)
     212             : {
     213        4811 :   const char *eos, *footer, *sig;
     214        4811 :   if ((eos = tor_memstr(s, s_eos - s, "\nr ")))
     215        3666 :     ++eos;
     216             :   else
     217             :     eos = s_eos;
     218             : 
     219        4811 :   footer = tor_memstr(s, eos-s, "\ndirectory-footer");
     220        4811 :   sig = tor_memstr(s, eos-s, "\ndirectory-signature");
     221             : 
     222        4811 :   if (footer && sig)
     223         136 :     return MIN(footer, sig) + 1;
     224        4675 :   else if (footer)
     225           5 :     return footer+1;
     226        4670 :   else if (sig)
     227        1009 :     return sig+1;
     228             :   else
     229             :     return eos;
     230             : }
     231             : 
     232             : /** Parse the GuardFraction string from a consensus or vote.
     233             :  *
     234             :  *  If <b>vote</b> or <b>vote_rs</b> are set the document getting
     235             :  *  parsed is a vote routerstatus. Otherwise it's a consensus. This is
     236             :  *  the same semantic as in routerstatus_parse_entry_from_string(). */
     237             : STATIC int
     238           4 : routerstatus_parse_guardfraction(const char *guardfraction_str,
     239             :                                  networkstatus_t *vote,
     240             :                                  vote_routerstatus_t *vote_rs,
     241             :                                  routerstatus_t *rs)
     242             : {
     243           4 :   int ok;
     244           4 :   const char *end_of_header = NULL;
     245           4 :   int is_consensus = !vote_rs;
     246           4 :   uint32_t guardfraction;
     247             : 
     248           4 :   tor_assert(bool_eq(vote, vote_rs));
     249             : 
     250             :   /* If this info comes from a consensus, but we shouldn't apply
     251             :      guardfraction, just exit. */
     252           4 :   if (is_consensus && !should_apply_guardfraction(NULL)) {
     253             :     return 0;
     254             :   }
     255             : 
     256           4 :   end_of_header = strchr(guardfraction_str, '=');
     257           4 :   if (!end_of_header) {
     258             :     return -1;
     259             :   }
     260             : 
     261           4 :   guardfraction = (uint32_t)tor_parse_ulong(end_of_header+1,
     262             :                                             10, 0, 100, &ok, NULL);
     263           4 :   if (!ok) {
     264           2 :     log_warn(LD_DIR, "Invalid GuardFraction %s", escaped(guardfraction_str));
     265           2 :     return -1;
     266             :   }
     267             : 
     268           2 :   log_debug(LD_GENERAL, "[*] Parsed %s guardfraction '%s' for '%s'.",
     269             :             is_consensus ? "consensus" : "vote",
     270             :             guardfraction_str, rs->nickname);
     271             : 
     272           2 :   if (!is_consensus) { /* We are parsing a vote */
     273           0 :     vote_rs->status.guardfraction_percentage = guardfraction;
     274           0 :     vote_rs->status.has_guardfraction = 1;
     275             :   } else {
     276             :     /* We are parsing a consensus. Only apply guardfraction to guards. */
     277           2 :     if (rs->is_possible_guard) {
     278           1 :       rs->guardfraction_percentage = guardfraction;
     279           1 :       rs->has_guardfraction = 1;
     280             :     } else {
     281           1 :       log_warn(LD_BUG, "Got GuardFraction for non-guard %s. "
     282             :                "This is not supposed to happen. Not applying. ", rs->nickname);
     283             :     }
     284             :   }
     285             : 
     286             :   return 0;
     287             : }
     288             : 
     289             : /** Given a string at *<b>s</b>, containing a routerstatus object, and an
     290             :  * empty smartlist at <b>tokens</b>, parse and return the first router status
     291             :  * object in the string, and advance *<b>s</b> to just after the end of the
     292             :  * router status.  Return NULL and advance *<b>s</b> on error.
     293             :  *
     294             :  * If <b>vote</b> and <b>vote_rs</b> are provided, don't allocate a fresh
     295             :  * routerstatus but use <b>vote_rs</b> instead.
     296             :  *
     297             :  * If <b>consensus_method</b> is nonzero, this routerstatus is part of a
     298             :  * consensus, and we should parse it according to the method used to
     299             :  * make that consensus.
     300             :  *
     301             :  * Parse according to the syntax used by the consensus flavor <b>flav</b>.
     302             :  **/
     303             : STATIC routerstatus_t *
     304        2347 : routerstatus_parse_entry_from_string(memarea_t *area,
     305             :                                      const char **s, const char *s_eos,
     306             :                                      smartlist_t *tokens,
     307             :                                      networkstatus_t *vote,
     308             :                                      vote_routerstatus_t *vote_rs,
     309             :                                      int consensus_method,
     310             :                                      consensus_flavor_t flav)
     311             : {
     312        2347 :   const char *eos, *s_dup = *s;
     313        2347 :   routerstatus_t *rs = NULL;
     314        2347 :   directory_token_t *tok;
     315        2347 :   char timebuf[ISO_TIME_LEN+1];
     316        2347 :   struct in_addr in;
     317        2347 :   int offset = 0;
     318        2347 :   tor_assert(tokens);
     319        2347 :   tor_assert(bool_eq(vote, vote_rs));
     320             : 
     321        2347 :   if (!consensus_method)
     322             :     flav = FLAV_NS;
     323        1845 :   tor_assert(flav == FLAV_NS || flav == FLAV_MICRODESC);
     324             : 
     325        2347 :   eos = find_start_of_next_routerstatus(*s, s_eos);
     326             : 
     327        2347 :   if (tokenize_string(area,*s, eos, tokens, rtrstatus_token_table,0)) {
     328        1286 :     log_warn(LD_DIR, "Error tokenizing router status");
     329        1286 :     goto err;
     330             :   }
     331        1061 :   if (smartlist_len(tokens) < 1) {
     332           0 :     log_warn(LD_DIR, "Impossibly short router status");
     333           0 :     goto err;
     334             :   }
     335        1061 :   tok = find_by_keyword(tokens, K_R);
     336        1061 :   tor_assert(tok->n_args >= 7); /* guaranteed by GE(7) in K_R setup */
     337        1061 :   if (flav == FLAV_NS) {
     338         392 :     if (tok->n_args < 8) {
     339           0 :       log_warn(LD_DIR, "Too few arguments to r");
     340           0 :       goto err;
     341             :     }
     342             :   } else if (flav == FLAV_MICRODESC) {
     343             :     offset = -1; /* There is no descriptor digest in an md consensus r line */
     344             :   }
     345             : 
     346        1061 :   if (vote_rs) {
     347         332 :     rs = &vote_rs->status;
     348             :   } else {
     349         729 :     rs = tor_malloc_zero(sizeof(routerstatus_t));
     350             :   }
     351             : 
     352        1061 :   if (!is_legal_nickname(tok->args[0])) {
     353           0 :     log_warn(LD_DIR,
     354             :              "Invalid nickname %s in router status; skipping.",
     355             :              escaped(tok->args[0]));
     356           0 :     goto err;
     357             :   }
     358        1061 :   strlcpy(rs->nickname, tok->args[0], sizeof(rs->nickname));
     359             : 
     360        1061 :   if (digest_from_base64(rs->identity_digest, tok->args[1])) {
     361           0 :     log_warn(LD_DIR, "Error decoding identity digest %s",
     362             :              escaped(tok->args[1]));
     363           0 :     goto err;
     364             :   }
     365             : 
     366        1061 :   if (flav == FLAV_NS) {
     367         392 :     if (digest_from_base64(rs->descriptor_digest, tok->args[2])) {
     368           0 :       log_warn(LD_DIR, "Error decoding descriptor digest %s",
     369             :                escaped(tok->args[2]));
     370           0 :       goto err;
     371             :     }
     372             :   }
     373             : 
     374        1061 :   if (tor_snprintf(timebuf, sizeof(timebuf), "%s %s",
     375        2121 :                    tok->args[3+offset], tok->args[4+offset]) < 0 ||
     376        1060 :       parse_iso_time(timebuf, &rs->published_on)<0) {
     377          10 :     log_warn(LD_DIR, "Error parsing time '%s %s' [%d %d]",
     378             :              tok->args[3+offset], tok->args[4+offset],
     379             :              offset, (int)flav);
     380          10 :     goto err;
     381             :   }
     382             : 
     383        1051 :   if (tor_inet_aton(tok->args[5+offset], &in) == 0) {
     384          10 :     log_warn(LD_DIR, "Error parsing router address in network-status %s",
     385             :              escaped(tok->args[5+offset]));
     386          10 :     goto err;
     387             :   }
     388        1041 :   tor_addr_from_in(&rs->ipv4_addr, &in);
     389             : 
     390        1041 :   rs->ipv4_orport = (uint16_t) tor_parse_long(tok->args[6+offset],
     391             :                                               10,0,65535,NULL,NULL);
     392        1041 :   rs->ipv4_dirport = (uint16_t) tor_parse_long(tok->args[7+offset],
     393             :                                                10,0,65535,NULL,NULL);
     394             : 
     395             :   {
     396        1041 :     smartlist_t *a_lines = find_all_by_keyword(tokens, K_A);
     397        1041 :     if (a_lines) {
     398         132 :       find_single_ipv6_orport(a_lines, &rs->ipv6_addr, &rs->ipv6_orport);
     399         132 :       smartlist_free(a_lines);
     400             :     }
     401             :   }
     402             : 
     403        1041 :   tok = find_opt_by_keyword(tokens, K_S);
     404        1041 :   if (tok && vote) {
     405         332 :     int i;
     406         332 :     vote_rs->flags = 0;
     407        1828 :     for (i=0; i < tok->n_args; ++i) {
     408        1496 :       int p = smartlist_string_pos(vote->known_flags, tok->args[i]);
     409        1496 :       if (p >= 0) {
     410        1496 :         vote_rs->flags |= (UINT64_C(1)<<p);
     411             :       } else {
     412           0 :         log_warn(LD_DIR, "Flags line had a flag %s not listed in known_flags.",
     413             :                  escaped(tok->args[i]));
     414           0 :         goto err;
     415             :       }
     416             :     }
     417         709 :   } else if (tok) {
     418             :     /* This is a consensus, not a vote. */
     419             :     int i;
     420        2035 :     for (i=0; i < tok->n_args; ++i) {
     421        1326 :       if (!strcmp(tok->args[i], "Exit"))
     422          86 :         rs->is_exit = 1;
     423        1240 :       else if (!strcmp(tok->args[i], "Stable"))
     424          88 :         rs->is_stable = 1;
     425        1152 :       else if (!strcmp(tok->args[i], "Fast"))
     426          88 :         rs->is_fast = 1;
     427        1064 :       else if (!strcmp(tok->args[i], "Running"))
     428         158 :         rs->is_flagged_running = 1;
     429         906 :       else if (!strcmp(tok->args[i], "Named"))
     430           1 :         rs->is_named = 1;
     431         905 :       else if (!strcmp(tok->args[i], "Valid"))
     432         305 :         rs->is_valid = 1;
     433         600 :       else if (!strcmp(tok->args[i], "Guard"))
     434          88 :         rs->is_possible_guard = 1;
     435         512 :       else if (!strcmp(tok->args[i], "BadExit"))
     436           1 :         rs->is_bad_exit = 1;
     437         511 :       else if (!strcmp(tok->args[i], "Authority"))
     438          38 :         rs->is_authority = 1;
     439         473 :       else if (!strcmp(tok->args[i], "Unnamed") &&
     440             :                consensus_method >= 2) {
     441             :         /* Unnamed is computed right by consensus method 2 and later. */
     442           1 :         rs->is_unnamed = 1;
     443         472 :       } else if (!strcmp(tok->args[i], "HSDir")) {
     444         132 :         rs->is_hs_dir = 1;
     445         340 :       } else if (!strcmp(tok->args[i], "V2Dir")) {
     446          63 :         rs->is_v2_dir = 1;
     447         277 :       } else if (!strcmp(tok->args[i], "StaleDesc")) {
     448           1 :         rs->is_staledesc = 1;
     449         276 :       } else if (!strcmp(tok->args[i], "Sybil")) {
     450           0 :         rs->is_sybil = 1;
     451             :       }
     452             :     }
     453             :     /* These are implied true by having been included in a consensus made
     454             :      * with a given method */
     455         709 :     rs->is_flagged_running = 1; /* Starting with consensus method 4. */
     456         709 :     rs->is_valid = 1; /* Starting with consensus method 24. */
     457             :   }
     458             :   {
     459        1041 :     const char *protocols = NULL, *version = NULL;
     460        1041 :     if ((tok = find_opt_by_keyword(tokens, K_PROTO))) {
     461        1041 :       tor_assert(tok->n_args == 1);
     462        1041 :       protocols = tok->args[0];
     463             :     }
     464        1041 :     if ((tok = find_opt_by_keyword(tokens, K_V))) {
     465         547 :       tor_assert(tok->n_args == 1);
     466         547 :       version = tok->args[0];
     467         547 :       if (vote_rs) {
     468         331 :         vote_rs->version = tor_strdup(tok->args[0]);
     469             :       }
     470             :     }
     471             : 
     472             :     // If the protover line is malformed, reject this routerstatus.
     473        1041 :     if (protocols && protover_list_is_invalid(protocols)) {
     474         126 :       goto err;
     475             :     }
     476         915 :     summarize_protover_flags(&rs->pv, protocols, version);
     477             :   }
     478             : 
     479             :   /* handle weighting/bandwidth info */
     480         915 :   if ((tok = find_opt_by_keyword(tokens, K_W))) {
     481             :     int i;
     482        1151 :     for (i=0; i < tok->n_args; ++i) {
     483         669 :       if (!strcmpstart(tok->args[i], "Bandwidth=")) {
     484         481 :         int ok;
     485         962 :         rs->bandwidth_kb =
     486         481 :           (uint32_t)tor_parse_ulong(strchr(tok->args[i], '=')+1,
     487             :                                     10, 0, UINT32_MAX,
     488             :                                     &ok, NULL);
     489         481 :         if (!ok) {
     490           0 :           log_warn(LD_DIR, "Invalid Bandwidth %s", escaped(tok->args[i]));
     491           0 :           goto err;
     492             :         }
     493         481 :         rs->has_bandwidth = 1;
     494         188 :       } else if (!strcmpstart(tok->args[i], "Measured=") && vote_rs) {
     495          78 :         int ok;
     496         156 :         vote_rs->measured_bw_kb =
     497          78 :             (uint32_t)tor_parse_ulong(strchr(tok->args[i], '=')+1,
     498             :                                       10, 0, UINT32_MAX, &ok, NULL);
     499          78 :         if (!ok) {
     500           0 :           log_warn(LD_DIR, "Invalid Measured Bandwidth %s",
     501             :                    escaped(tok->args[i]));
     502           0 :           goto err;
     503             :         }
     504          78 :         vote_rs->has_measured_bw = 1;
     505          78 :         vote->has_measured_bws = 1;
     506         110 :       } else if (!strcmpstart(tok->args[i], "Unmeasured=1")) {
     507         108 :         rs->bw_is_unmeasured = 1;
     508           2 :       } else if (!strcmpstart(tok->args[i], "GuardFraction=")) {
     509           0 :         if (routerstatus_parse_guardfraction(tok->args[i],
     510             :                                              vote, vote_rs, rs) < 0) {
     511           0 :           goto err;
     512             :         }
     513             :       }
     514             :     }
     515             :   }
     516             : 
     517             :   /* parse exit policy summaries */
     518         915 :   if ((tok = find_opt_by_keyword(tokens, K_P))) {
     519         385 :     tor_assert(tok->n_args == 1);
     520         770 :     if (strcmpstart(tok->args[0], "accept ") &&
     521         385 :         strcmpstart(tok->args[0], "reject ")) {
     522           0 :       log_warn(LD_DIR, "Unknown exit policy summary type %s.",
     523             :                escaped(tok->args[0]));
     524           0 :       goto err;
     525             :     }
     526             :     /* XXX weasel: parse this into ports and represent them somehow smart,
     527             :      * maybe not here but somewhere on if we need it for the client.
     528             :      * we should still parse it here to check it's valid tho.
     529             :      */
     530         385 :     rs->exitsummary = tor_strdup(tok->args[0]);
     531         385 :     rs->has_exitsummary = 1;
     532             :   }
     533             : 
     534         915 :   if (vote_rs) {
     535        3221 :     SMARTLIST_FOREACH_BEGIN(tokens, directory_token_t *, t) {
     536        2897 :       if (t->tp == K_M && t->n_args) {
     537         324 :         vote_microdesc_hash_t *line =
     538         324 :           tor_malloc(sizeof(vote_microdesc_hash_t));
     539         324 :         line->next = vote_rs->microdesc;
     540         324 :         line->microdesc_hash_line = tor_strdup(t->args[0]);
     541         324 :         vote_rs->microdesc = line;
     542             :       }
     543        2897 :       if (t->tp == K_ID) {
     544         325 :         tor_assert(t->n_args >= 2);
     545         325 :         if (!strcmp(t->args[0], "ed25519")) {
     546         325 :           vote_rs->has_ed25519_listing = 1;
     547         326 :           if (strcmp(t->args[1], "none") &&
     548           1 :               digest256_from_base64((char*)vote_rs->ed25519_id,
     549             :                                     t->args[1])<0) {
     550           1 :             log_warn(LD_DIR, "Bogus ed25519 key in networkstatus vote");
     551           1 :             goto err;
     552             :           }
     553             :         }
     554             :       }
     555        2896 :       if (t->tp == K_PROTO) {
     556         325 :         tor_assert(t->n_args == 1);
     557         325 :         vote_rs->protocols = tor_strdup(t->args[0]);
     558             :       }
     559        2896 :     } SMARTLIST_FOREACH_END(t);
     560         590 :   } else if (flav == FLAV_MICRODESC) {
     561         530 :     tok = find_opt_by_keyword(tokens, K_M);
     562         530 :     if (tok) {
     563         111 :       tor_assert(tok->n_args);
     564         111 :       if (digest256_from_base64(rs->descriptor_digest, tok->args[0])) {
     565           0 :         log_warn(LD_DIR, "Error decoding microdescriptor digest %s",
     566             :                  escaped(tok->args[0]));
     567           0 :         goto err;
     568             :       }
     569             :     } else {
     570         419 :       log_info(LD_BUG, "Found an entry in networkstatus with no "
     571             :                "microdescriptor digest. (Router %s ($%s) at %s:%d.)",
     572             :                rs->nickname, hex_str(rs->identity_digest, DIGEST_LEN),
     573             :                fmt_addr(&rs->ipv4_addr), rs->ipv4_orport);
     574             :     }
     575             :   }
     576             : 
     577         914 :   if (!strcasecmp(rs->nickname, UNNAMED_ROUTER_NICKNAME))
     578           0 :     rs->is_named = 0;
     579             : 
     580         914 :   goto done;
     581        1433 :  err:
     582        1433 :   dump_desc(s_dup, "routerstatus entry");
     583        1433 :   if (rs && !vote_rs)
     584         139 :     routerstatus_free(rs);
     585             :   rs = NULL;
     586        2347 :  done:
     587       36088 :   SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_clear(t));
     588        2347 :   smartlist_clear(tokens);
     589        2347 :   if (area) {
     590        2347 :     DUMP_AREA(area, "routerstatus entry");
     591        2347 :     memarea_clear(area);
     592             :   }
     593        2347 :   *s = eos;
     594             : 
     595        2347 :   return rs;
     596             : }
     597             : 
     598             : int
     599           1 : compare_vote_routerstatus_entries(const void **_a, const void **_b)
     600             : {
     601           1 :   const vote_routerstatus_t *a = *_a, *b = *_b;
     602           1 :   return fast_memcmp(a->status.identity_digest, b->status.identity_digest,
     603             :                      DIGEST_LEN);
     604             : }
     605             : 
     606             : /** Verify the bandwidth weights of a network status document */
     607             : int
     608          24 : networkstatus_verify_bw_weights(networkstatus_t *ns, int consensus_method)
     609             : {
     610          24 :   int64_t G=0, M=0, E=0, D=0, T=0;
     611          24 :   double Wgg, Wgm, Wgd, Wmg, Wmm, Wme, Wmd, Weg, Wem, Wee, Wed;
     612          24 :   double Gtotal=0, Mtotal=0, Etotal=0;
     613          24 :   const char *casename = NULL;
     614          24 :   int valid = 1;
     615          24 :   (void) consensus_method;
     616             : 
     617          24 :   const int64_t weight_scale = networkstatus_get_weight_scale_param(ns);
     618          24 :   tor_assert(weight_scale >= 1);
     619          24 :   Wgg = networkstatus_get_bw_weight(ns, "Wgg", -1);
     620          24 :   Wgm = networkstatus_get_bw_weight(ns, "Wgm", -1);
     621          24 :   Wgd = networkstatus_get_bw_weight(ns, "Wgd", -1);
     622          24 :   Wmg = networkstatus_get_bw_weight(ns, "Wmg", -1);
     623          24 :   Wmm = networkstatus_get_bw_weight(ns, "Wmm", -1);
     624          24 :   Wme = networkstatus_get_bw_weight(ns, "Wme", -1);
     625          24 :   Wmd = networkstatus_get_bw_weight(ns, "Wmd", -1);
     626          24 :   Weg = networkstatus_get_bw_weight(ns, "Weg", -1);
     627          24 :   Wem = networkstatus_get_bw_weight(ns, "Wem", -1);
     628          24 :   Wee = networkstatus_get_bw_weight(ns, "Wee", -1);
     629          24 :   Wed = networkstatus_get_bw_weight(ns, "Wed", -1);
     630             : 
     631          24 :   if (Wgg<0 || Wgm<0 || Wgd<0 || Wmg<0 || Wmm<0 || Wme<0 || Wmd<0 || Weg<0
     632          24 :           || Wem<0 || Wee<0 || Wed<0) {
     633           0 :     log_warn(LD_BUG, "No bandwidth weights produced in consensus!");
     634           0 :     return 0;
     635             :   }
     636             : 
     637             :   // First, sanity check basic summing properties that hold for all cases
     638             :   // We use > 1 as the check for these because they are computed as integers.
     639             :   // Sometimes there are rounding errors.
     640          24 :   if (fabs(Wmm - weight_scale) > 1) {
     641           0 :     log_warn(LD_BUG, "Wmm=%f != %"PRId64,
     642             :              Wmm, (weight_scale));
     643           0 :     valid = 0;
     644             :   }
     645             : 
     646          24 :   if (fabs(Wem - Wee) > 1) {
     647           0 :     log_warn(LD_BUG, "Wem=%f != Wee=%f", Wem, Wee);
     648           0 :     valid = 0;
     649             :   }
     650             : 
     651          24 :   if (fabs(Wgm - Wgg) > 1) {
     652           0 :     log_warn(LD_BUG, "Wgm=%f != Wgg=%f", Wgm, Wgg);
     653           0 :     valid = 0;
     654             :   }
     655             : 
     656          24 :   if (fabs(Weg - Wed) > 1) {
     657           0 :     log_warn(LD_BUG, "Wed=%f != Weg=%f", Wed, Weg);
     658           0 :     valid = 0;
     659             :   }
     660             : 
     661          24 :   if (fabs(Wgg + Wmg - weight_scale) > 0.001*weight_scale) {
     662           0 :     log_warn(LD_BUG, "Wgg=%f != %"PRId64" - Wmg=%f", Wgg,
     663             :              (weight_scale), Wmg);
     664           0 :     valid = 0;
     665             :   }
     666             : 
     667          24 :   if (fabs(Wee + Wme - weight_scale) > 0.001*weight_scale) {
     668           0 :     log_warn(LD_BUG, "Wee=%f != %"PRId64" - Wme=%f", Wee,
     669             :              (weight_scale), Wme);
     670           0 :     valid = 0;
     671             :   }
     672             : 
     673          24 :   if (fabs(Wgd + Wmd + Wed - weight_scale) > 0.001*weight_scale) {
     674           0 :     log_warn(LD_BUG, "Wgd=%f + Wmd=%f + Wed=%f != %"PRId64,
     675             :              Wgd, Wmd, Wed, (weight_scale));
     676           0 :     valid = 0;
     677             :   }
     678             : 
     679          24 :   Wgg /= weight_scale;
     680          24 :   Wgm /= weight_scale; (void) Wgm; // unused from here on.
     681          24 :   Wgd /= weight_scale;
     682             : 
     683          24 :   Wmg /= weight_scale;
     684          24 :   Wmm /= weight_scale;
     685          24 :   Wme /= weight_scale;
     686          24 :   Wmd /= weight_scale;
     687             : 
     688          24 :   Weg /= weight_scale; (void) Weg; // unused from here on.
     689          24 :   Wem /= weight_scale; (void) Wem; // unused from here on.
     690          24 :   Wee /= weight_scale;
     691          24 :   Wed /= weight_scale;
     692             : 
     693             :   // Then, gather G, M, E, D, T to determine case
     694         102 :   SMARTLIST_FOREACH_BEGIN(ns->routerstatus_list, routerstatus_t *, rs) {
     695          78 :     int is_exit = 0;
     696             :     /* Bug #2203: Don't count bad exits as exits for balancing */
     697          78 :     is_exit = rs->is_exit && !rs->is_bad_exit;
     698          78 :     if (rs->has_bandwidth) {
     699          78 :       T += rs->bandwidth_kb;
     700          78 :       if (is_exit && rs->is_possible_guard) {
     701          42 :         D += rs->bandwidth_kb;
     702          42 :         Gtotal += Wgd*rs->bandwidth_kb;
     703          42 :         Mtotal += Wmd*rs->bandwidth_kb;
     704          42 :         Etotal += Wed*rs->bandwidth_kb;
     705          36 :       } else if (is_exit) {
     706           0 :         E += rs->bandwidth_kb;
     707           0 :         Mtotal += Wme*rs->bandwidth_kb;
     708           0 :         Etotal += Wee*rs->bandwidth_kb;
     709          36 :       } else if (rs->is_possible_guard) {
     710           0 :         G += rs->bandwidth_kb;
     711           0 :         Gtotal += Wgg*rs->bandwidth_kb;
     712           0 :         Mtotal += Wmg*rs->bandwidth_kb;
     713             :       } else {
     714          36 :         M += rs->bandwidth_kb;
     715          36 :         Mtotal += Wmm*rs->bandwidth_kb;
     716             :       }
     717             :     } else {
     718           0 :       log_warn(LD_BUG, "Missing consensus bandwidth for router %s",
     719             :                routerstatus_describe(rs));
     720             :     }
     721          78 :   } SMARTLIST_FOREACH_END(rs);
     722             : 
     723             :   // Finally, check equality conditions depending upon case 1, 2 or 3
     724             :   // Full equality cases: 1, 3b
     725             :   // Partial equality cases: 2b (E=G), 3a (M=E)
     726             :   // Fully unknown: 2a
     727          24 :   if (3*E >= T && 3*G >= T) {
     728             :     // Case 1: Neither are scarce
     729          12 :     casename = "Case 1";
     730          24 :     if (fabs(Etotal-Mtotal) > 0.01*MAX(Etotal,Mtotal)) {
     731           0 :       log_warn(LD_DIR,
     732             :                "Bw Weight Failure for %s: Etotal %f != Mtotal %f. "
     733             :                "G=%"PRId64" M=%"PRId64" E=%"PRId64" D=%"PRId64
     734             :                " T=%"PRId64". "
     735             :                "Wgg=%f Wgd=%f Wmg=%f Wme=%f Wmd=%f Wee=%f Wed=%f",
     736             :                casename, Etotal, Mtotal,
     737             :                (G), (M), (E),
     738             :                (D), (T),
     739             :                Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
     740           0 :       valid = 0;
     741             :     }
     742          24 :     if (fabs(Etotal-Gtotal) > 0.01*MAX(Etotal,Gtotal)) {
     743           0 :       log_warn(LD_DIR,
     744             :                "Bw Weight Failure for %s: Etotal %f != Gtotal %f. "
     745             :                "G=%"PRId64" M=%"PRId64" E=%"PRId64" D=%"PRId64
     746             :                " T=%"PRId64". "
     747             :                "Wgg=%f Wgd=%f Wmg=%f Wme=%f Wmd=%f Wee=%f Wed=%f",
     748             :                casename, Etotal, Gtotal,
     749             :                (G), (M), (E),
     750             :                (D), (T),
     751             :                Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
     752           0 :       valid = 0;
     753             :     }
     754          24 :     if (fabs(Gtotal-Mtotal) > 0.01*MAX(Gtotal,Mtotal)) {
     755           0 :       log_warn(LD_DIR,
     756             :                "Bw Weight Failure for %s: Mtotal %f != Gtotal %f. "
     757             :                "G=%"PRId64" M=%"PRId64" E=%"PRId64" D=%"PRId64
     758             :                " T=%"PRId64". "
     759             :                "Wgg=%f Wgd=%f Wmg=%f Wme=%f Wmd=%f Wee=%f Wed=%f",
     760             :                casename, Mtotal, Gtotal,
     761             :                (G), (M), (E),
     762             :                (D), (T),
     763             :                Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
     764           0 :       valid = 0;
     765             :     }
     766          12 :   } else if (3*E < T && 3*G < T) {
     767          12 :     int64_t R = MIN(E, G);
     768          12 :     int64_t S = MAX(E, G);
     769             :     /*
     770             :      * Case 2: Both Guards and Exits are scarce
     771             :      * Balance D between E and G, depending upon
     772             :      * D capacity and scarcity. Devote no extra
     773             :      * bandwidth to middle nodes.
     774             :      */
     775          12 :     if (R+D < S) { // Subcase a
     776           0 :       double Rtotal, Stotal;
     777           0 :       if (E < G) {
     778             :         Rtotal = Etotal;
     779             :         Stotal = Gtotal;
     780             :       } else {
     781           0 :         Rtotal = Gtotal;
     782           0 :         Stotal = Etotal;
     783             :       }
     784           0 :       casename = "Case 2a";
     785             :       // Rtotal < Stotal
     786           0 :       if (Rtotal > Stotal) {
     787           0 :         log_warn(LD_DIR,
     788             :                    "Bw Weight Failure for %s: Rtotal %f > Stotal %f. "
     789             :                    "G=%"PRId64" M=%"PRId64" E=%"PRId64" D=%"PRId64
     790             :                    " T=%"PRId64". "
     791             :                    "Wgg=%f Wgd=%f Wmg=%f Wme=%f Wmd=%f Wee=%f Wed=%f",
     792             :                    casename, Rtotal, Stotal,
     793             :                    (G), (M), (E),
     794             :                    (D), (T),
     795             :                    Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
     796           0 :         valid = 0;
     797             :       }
     798             :       // Rtotal < T/3
     799           0 :       if (3*Rtotal > T) {
     800           0 :         log_warn(LD_DIR,
     801             :                    "Bw Weight Failure for %s: 3*Rtotal %f > T "
     802             :                    "%"PRId64". G=%"PRId64" M=%"PRId64" E=%"PRId64
     803             :                    " D=%"PRId64" T=%"PRId64". "
     804             :                    "Wgg=%f Wgd=%f Wmg=%f Wme=%f Wmd=%f Wee=%f Wed=%f",
     805             :                    casename, Rtotal*3, (T),
     806             :                    (G), (M), (E),
     807             :                    (D), (T),
     808             :                    Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
     809           0 :         valid = 0;
     810             :       }
     811             :       // Stotal < T/3
     812           0 :       if (3*Stotal > T) {
     813           0 :         log_warn(LD_DIR,
     814             :                    "Bw Weight Failure for %s: 3*Stotal %f > T "
     815             :                    "%"PRId64". G=%"PRId64" M=%"PRId64" E=%"PRId64
     816             :                    " D=%"PRId64" T=%"PRId64". "
     817             :                    "Wgg=%f Wgd=%f Wmg=%f Wme=%f Wmd=%f Wee=%f Wed=%f",
     818             :                    casename, Stotal*3, (T),
     819             :                    (G), (M), (E),
     820             :                    (D), (T),
     821             :                    Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
     822           0 :         valid = 0;
     823             :       }
     824             :       // Mtotal > T/3
     825           0 :       if (3*Mtotal < T) {
     826           0 :         log_warn(LD_DIR,
     827             :                    "Bw Weight Failure for %s: 3*Mtotal %f < T "
     828             :                    "%"PRId64". "
     829             :                    "G=%"PRId64" M=%"PRId64" E=%"PRId64" D=%"PRId64
     830             :                    " T=%"PRId64". "
     831             :                    "Wgg=%f Wgd=%f Wmg=%f Wme=%f Wmd=%f Wee=%f Wed=%f",
     832             :                    casename, Mtotal*3, (T),
     833             :                    (G), (M), (E),
     834             :                    (D), (T),
     835             :                    Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
     836           0 :         valid = 0;
     837             :       }
     838             :     } else { // Subcase b: R+D > S
     839          12 :       casename = "Case 2b";
     840             : 
     841             :       /* Check the rare-M redirect case. */
     842          12 :       if (D != 0 && 3*M < T) {
     843          12 :         casename = "Case 2b (balanced)";
     844          24 :         if (fabs(Etotal-Mtotal) > 0.01*MAX(Etotal,Mtotal)) {
     845           6 :           log_warn(LD_DIR,
     846             :                    "Bw Weight Failure for %s: Etotal %f != Mtotal %f. "
     847             :                    "G=%"PRId64" M=%"PRId64" E=%"PRId64" D=%"PRId64
     848             :                    " T=%"PRId64". "
     849             :                    "Wgg=%f Wgd=%f Wmg=%f Wme=%f Wmd=%f Wee=%f Wed=%f",
     850             :                    casename, Etotal, Mtotal,
     851             :                    (G), (M), (E),
     852             :                    (D), (T),
     853             :                    Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
     854           6 :           valid = 0;
     855             :         }
     856          24 :         if (fabs(Etotal-Gtotal) > 0.01*MAX(Etotal,Gtotal)) {
     857           0 :           log_warn(LD_DIR,
     858             :                    "Bw Weight Failure for %s: Etotal %f != Gtotal %f. "
     859             :                    "G=%"PRId64" M=%"PRId64" E=%"PRId64" D=%"PRId64
     860             :                    " T=%"PRId64". "
     861             :                    "Wgg=%f Wgd=%f Wmg=%f Wme=%f Wmd=%f Wee=%f Wed=%f",
     862             :                    casename, Etotal, Gtotal,
     863             :                    (G), (M), (E),
     864             :                    (D), (T),
     865             :                    Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
     866           0 :           valid = 0;
     867             :         }
     868          24 :         if (fabs(Gtotal-Mtotal) > 0.01*MAX(Gtotal,Mtotal)) {
     869           6 :           log_warn(LD_DIR,
     870             :                    "Bw Weight Failure for %s: Mtotal %f != Gtotal %f. "
     871             :                    "G=%"PRId64" M=%"PRId64" E=%"PRId64" D=%"PRId64
     872             :                    " T=%"PRId64". "
     873             :                    "Wgg=%f Wgd=%f Wmg=%f Wme=%f Wmd=%f Wee=%f Wed=%f",
     874             :                    casename, Mtotal, Gtotal,
     875             :                    (G), (M), (E),
     876             :                    (D), (T),
     877             :                    Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
     878           6 :           valid = 0;
     879             :         }
     880             :       } else {
     881           0 :         if (fabs(Etotal-Gtotal) > 0.01*MAX(Etotal,Gtotal)) {
     882           0 :           log_warn(LD_DIR,
     883             :                    "Bw Weight Failure for %s: Etotal %f != Gtotal %f. "
     884             :                    "G=%"PRId64" M=%"PRId64" E=%"PRId64" D=%"PRId64
     885             :                    " T=%"PRId64". "
     886             :                    "Wgg=%f Wgd=%f Wmg=%f Wme=%f Wmd=%f Wee=%f Wed=%f",
     887             :                    casename, Etotal, Gtotal,
     888             :                    (G), (M), (E),
     889             :                    (D), (T),
     890             :                    Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
     891           0 :           valid = 0;
     892             :         }
     893             :       }
     894             :     }
     895             :   } else { // if (E < T/3 || G < T/3) {
     896           0 :     int64_t S = MIN(E, G);
     897           0 :     int64_t NS = MAX(E, G);
     898           0 :     if (3*(S+D) < T) { // Subcase a:
     899           0 :       double Stotal;
     900           0 :       double NStotal;
     901           0 :       if (G < E) {
     902             :         casename = "Case 3a (G scarce)";
     903             :         Stotal = Gtotal;
     904             :         NStotal = Etotal;
     905             :       } else { // if (G >= E) {
     906           0 :         casename = "Case 3a (E scarce)";
     907           0 :         NStotal = Gtotal;
     908           0 :         Stotal = Etotal;
     909             :       }
     910             :       // Stotal < T/3
     911           0 :       if (3*Stotal > T) {
     912           0 :         log_warn(LD_DIR,
     913             :                    "Bw Weight Failure for %s: 3*Stotal %f > T "
     914             :                    "%"PRId64". G=%"PRId64" M=%"PRId64" E=%"PRId64
     915             :                    " D=%"PRId64" T=%"PRId64". "
     916             :                    "Wgg=%f Wgd=%f Wmg=%f Wme=%f Wmd=%f Wee=%f Wed=%f",
     917             :                    casename, Stotal*3, (T),
     918             :                    (G), (M), (E),
     919             :                    (D), (T),
     920             :                    Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
     921           0 :         valid = 0;
     922             :       }
     923           0 :       if (NS >= M) {
     924           0 :         if (fabs(NStotal-Mtotal) > 0.01*MAX(NStotal,Mtotal)) {
     925           0 :           log_warn(LD_DIR,
     926             :                    "Bw Weight Failure for %s: NStotal %f != Mtotal %f. "
     927             :                    "G=%"PRId64" M=%"PRId64" E=%"PRId64" D=%"PRId64
     928             :                    " T=%"PRId64". "
     929             :                    "Wgg=%f Wgd=%f Wmg=%f Wme=%f Wmd=%f Wee=%f Wed=%f",
     930             :                    casename, NStotal, Mtotal,
     931             :                    (G), (M), (E),
     932             :                    (D), (T),
     933             :                    Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
     934           0 :           valid = 0;
     935             :         }
     936             :       } else {
     937             :         // if NS < M, NStotal > T/3 because only one of G or E is scarce
     938           0 :         if (3*NStotal < T) {
     939           0 :           log_warn(LD_DIR,
     940             :                      "Bw Weight Failure for %s: 3*NStotal %f < T "
     941             :                      "%"PRId64". G=%"PRId64" M=%"PRId64
     942             :                      " E=%"PRId64" D=%"PRId64" T=%"PRId64". "
     943             :                      "Wgg=%f Wgd=%f Wmg=%f Wme=%f Wmd=%f Wee=%f Wed=%f",
     944             :                      casename, NStotal*3, (T),
     945             :                      (G), (M), (E),
     946             :                      (D), (T),
     947             :                      Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
     948           0 :           valid = 0;
     949             :         }
     950             :       }
     951             :     } else { // Subcase b: S+D >= T/3
     952           0 :       casename = "Case 3b";
     953           0 :       if (fabs(Etotal-Mtotal) > 0.01*MAX(Etotal,Mtotal)) {
     954           0 :         log_warn(LD_DIR,
     955             :                  "Bw Weight Failure for %s: Etotal %f != Mtotal %f. "
     956             :                  "G=%"PRId64" M=%"PRId64" E=%"PRId64" D=%"PRId64
     957             :                  " T=%"PRId64". "
     958             :                  "Wgg=%f Wgd=%f Wmg=%f Wme=%f Wmd=%f Wee=%f Wed=%f",
     959             :                  casename, Etotal, Mtotal,
     960             :                  (G), (M), (E),
     961             :                  (D), (T),
     962             :                  Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
     963           0 :         valid = 0;
     964             :       }
     965           0 :       if (fabs(Etotal-Gtotal) > 0.01*MAX(Etotal,Gtotal)) {
     966           0 :         log_warn(LD_DIR,
     967             :                  "Bw Weight Failure for %s: Etotal %f != Gtotal %f. "
     968             :                  "G=%"PRId64" M=%"PRId64" E=%"PRId64" D=%"PRId64
     969             :                  " T=%"PRId64". "
     970             :                  "Wgg=%f Wgd=%f Wmg=%f Wme=%f Wmd=%f Wee=%f Wed=%f",
     971             :                  casename, Etotal, Gtotal,
     972             :                  (G), (M), (E),
     973             :                  (D), (T),
     974             :                  Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
     975           0 :         valid = 0;
     976             :       }
     977           0 :       if (fabs(Gtotal-Mtotal) > 0.01*MAX(Gtotal,Mtotal)) {
     978           0 :         log_warn(LD_DIR,
     979             :                  "Bw Weight Failure for %s: Mtotal %f != Gtotal %f. "
     980             :                  "G=%"PRId64" M=%"PRId64" E=%"PRId64" D=%"PRId64
     981             :                  " T=%"PRId64". "
     982             :                  "Wgg=%f Wgd=%f Wmg=%f Wme=%f Wmd=%f Wee=%f Wed=%f",
     983             :                  casename, Mtotal, Gtotal,
     984             :                  (G), (M), (E),
     985             :                  (D), (T),
     986             :                  Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
     987           0 :         valid = 0;
     988             :       }
     989             :     }
     990             :   }
     991             : 
     992          24 :   if (valid)
     993          18 :     log_notice(LD_DIR, "Bandwidth-weight %s is verified and valid.",
     994             :                casename);
     995             : 
     996             :   return valid;
     997             : }
     998             : 
     999             : /** Check if a shared random value of type <b>srv_type</b> is in
    1000             :  *  <b>tokens</b>. If there is, parse it and set it to <b>srv_out</b>. Return
    1001             :  *  -1 on failure, 0 on success. The resulting srv is allocated on the heap and
    1002             :  *  it's the responsibility of the caller to free it. */
    1003             : static int
    1004        3422 : extract_one_srv(smartlist_t *tokens, directory_keyword srv_type,
    1005             :                 sr_srv_t **srv_out)
    1006             : {
    1007        3422 :   int ret = -1;
    1008        3422 :   directory_token_t *tok;
    1009        3422 :   sr_srv_t *srv = NULL;
    1010        3422 :   smartlist_t *chunks;
    1011             : 
    1012        3422 :   tor_assert(tokens);
    1013             : 
    1014        3422 :   chunks = smartlist_new();
    1015        3422 :   tok = find_opt_by_keyword(tokens, srv_type);
    1016        3422 :   if (!tok) {
    1017             :     /* That's fine, no SRV is allowed. */
    1018        3408 :     ret = 0;
    1019        3408 :     goto end;
    1020             :   }
    1021          42 :   for (int i = 0; i < tok->n_args; i++) {
    1022          28 :     smartlist_add(chunks, tok->args[i]);
    1023             :   }
    1024          14 :   srv = sr_parse_srv(chunks);
    1025          14 :   if (srv == NULL) {
    1026          13 :     log_warn(LD_DIR, "SR: Unparseable SRV %s", escaped(tok->object_body));
    1027          13 :     goto end;
    1028             :   }
    1029             :   /* All is good. */
    1030           1 :   *srv_out = srv;
    1031           1 :   ret = 0;
    1032        3422 :  end:
    1033        3422 :   smartlist_free(chunks);
    1034        3422 :   return ret;
    1035             : }
    1036             : 
    1037             : /** Extract any shared random values found in <b>tokens</b> and place them in
    1038             :  *  the networkstatus <b>ns</b>. */
    1039             : static void
    1040        1711 : extract_shared_random_srvs(networkstatus_t *ns, smartlist_t *tokens)
    1041             : {
    1042        1711 :   const char *voter_identity;
    1043        1711 :   networkstatus_voter_info_t *voter;
    1044             : 
    1045        1711 :   tor_assert(ns);
    1046        1711 :   tor_assert(tokens);
    1047             :   /* Can be only one of them else code flow. */
    1048        1711 :   tor_assert(ns->type == NS_TYPE_VOTE || ns->type == NS_TYPE_CONSENSUS);
    1049             : 
    1050        1711 :   if (ns->type == NS_TYPE_VOTE) {
    1051         320 :     voter = smartlist_get(ns->voters, 0);
    1052         320 :     tor_assert(voter);
    1053         320 :     voter_identity = hex_str(voter->identity_digest,
    1054             :                              sizeof(voter->identity_digest));
    1055             :   } else {
    1056             :     /* Consensus has multiple voters so no specific voter. */
    1057             :     voter_identity = "consensus";
    1058             :   }
    1059             : 
    1060             :   /* We extract both, and on error everything is stopped because it means
    1061             :    * the vote is malformed for the shared random value(s). */
    1062        1711 :   if (extract_one_srv(tokens, K_PREVIOUS_SRV, &ns->sr_info.previous_srv) < 0) {
    1063           8 :     log_warn(LD_DIR, "SR: Unable to parse previous SRV from %s",
    1064             :              voter_identity);
    1065             :     /* Maybe we have a chance with the current SRV so let's try it anyway. */
    1066             :   }
    1067        1711 :   if (extract_one_srv(tokens, K_CURRENT_SRV, &ns->sr_info.current_srv) < 0) {
    1068           5 :     log_warn(LD_DIR, "SR: Unable to parse current SRV from %s",
    1069             :              voter_identity);
    1070             :   }
    1071        1711 : }
    1072             : 
    1073             : /** Allocate a copy of a protover line, if present. If present but malformed,
    1074             :  * set *error to true. */
    1075             : static char *
    1076        8808 : dup_protocols_string(smartlist_t *tokens, bool *error, directory_keyword kw)
    1077             : {
    1078        8808 :   directory_token_t *tok = find_opt_by_keyword(tokens, kw);
    1079        8808 :   if (!tok)
    1080             :     return NULL;
    1081         642 :   if (protover_list_is_invalid(tok->args[0]))
    1082          47 :     *error = true;
    1083         642 :   return tor_strdup(tok->args[0]);
    1084             : }
    1085             : 
    1086             : /** Parse a v3 networkstatus vote, opinion, or consensus (depending on
    1087             :  * ns_type), from <b>s</b>, and return the result.  Return NULL on failure. */
    1088             : networkstatus_t *
    1089        2500 : networkstatus_parse_vote_from_string(const char *s,
    1090             :                                      size_t s_len,
    1091             :                                      const char **eos_out,
    1092             :                                      networkstatus_type_t ns_type)
    1093             : {
    1094        2500 :   smartlist_t *tokens = smartlist_new();
    1095        2500 :   smartlist_t *rs_tokens = NULL, *footer_tokens = NULL;
    1096        2500 :   networkstatus_voter_info_t *voter = NULL;
    1097        2500 :   networkstatus_t *ns = NULL;
    1098        2500 :   common_digests_t ns_digests;
    1099        2500 :   uint8_t sha3_as_signed[DIGEST256_LEN];
    1100        2500 :   const char *cert, *end_of_header, *end_of_footer, *s_dup = s;
    1101        2500 :   directory_token_t *tok;
    1102        2500 :   struct in_addr in;
    1103        2500 :   int i, inorder, n_signatures = 0;
    1104        2500 :   memarea_t *area = NULL, *rs_area = NULL;
    1105        2500 :   consensus_flavor_t flav = FLAV_NS;
    1106        2500 :   char *last_kwd=NULL;
    1107        2500 :   const char *eos = s + s_len;
    1108             : 
    1109        2500 :   tor_assert(s);
    1110             : 
    1111        2500 :   if (eos_out)
    1112        2398 :     *eos_out = NULL;
    1113             : 
    1114        4964 :   if (router_get_networkstatus_v3_hashes(s, s_len, &ns_digests) ||
    1115        2464 :       router_get_networkstatus_v3_sha3_as_signed(sha3_as_signed,
    1116             :                                                  s, s_len)<0) {
    1117          36 :     log_warn(LD_DIR, "Unable to compute digest of network-status");
    1118          36 :     goto err;
    1119             :   }
    1120             : 
    1121        2464 :   area = memarea_new();
    1122        2464 :   end_of_header = find_start_of_next_routerstatus(s, eos);
    1123        2914 :   if (tokenize_string(area, s, end_of_header, tokens,
    1124             :                       (ns_type == NS_TYPE_CONSENSUS) ?
    1125             :                       networkstatus_consensus_token_table :
    1126             :                       networkstatus_token_table, 0)) {
    1127         192 :     log_warn(LD_DIR, "Error tokenizing network-status header");
    1128         192 :     goto err;
    1129             :   }
    1130             : 
    1131        2272 :   ns = tor_malloc_zero(sizeof(networkstatus_t));
    1132        2272 :   memcpy(&ns->digests, &ns_digests, sizeof(ns_digests));
    1133        2272 :   memcpy(&ns->digest_sha3_as_signed, sha3_as_signed, sizeof(sha3_as_signed));
    1134             : 
    1135        2272 :   tok = find_by_keyword(tokens, K_NETWORK_STATUS_VERSION);
    1136        2272 :   tor_assert(tok);
    1137        2272 :   if (tok->n_args > 1) {
    1138        1586 :     int flavor = networkstatus_parse_flavor_name(tok->args[1]);
    1139        1586 :     if (flavor < 0) {
    1140           9 :       log_warn(LD_DIR, "Can't parse document with unknown flavor %s",
    1141             :                escaped(tok->args[1]));
    1142           9 :       goto err;
    1143             :     }
    1144        1577 :     ns->flavor = flav = flavor;
    1145             :   }
    1146        2263 :   if (flav != FLAV_NS && ns_type != NS_TYPE_CONSENSUS) {
    1147           1 :     log_warn(LD_DIR, "Flavor found on non-consensus networkstatus.");
    1148           1 :     goto err;
    1149             :   }
    1150             : 
    1151        2262 :   if (ns_type != NS_TYPE_CONSENSUS) {
    1152         421 :     const char *end_of_cert = NULL;
    1153         421 :     if (!(cert = tor_memstr(s, end_of_header - s,
    1154             :                             "\ndir-key-certificate-version")))
    1155          39 :       goto err;
    1156         420 :     ++cert;
    1157         420 :     ns->cert = authority_cert_parse_from_string(cert, end_of_header - cert,
    1158             :                                                 &end_of_cert);
    1159         420 :     if (!ns->cert || !end_of_cert || end_of_cert > end_of_header)
    1160          38 :       goto err;
    1161             :   }
    1162             : 
    1163        2223 :   tok = find_by_keyword(tokens, K_VOTE_STATUS);
    1164        2223 :   tor_assert(tok->n_args);
    1165        2223 :   if (!strcmp(tok->args[0], "vote")) {
    1166         381 :     ns->type = NS_TYPE_VOTE;
    1167        1842 :   } else if (!strcmp(tok->args[0], "consensus")) {
    1168        1833 :     ns->type = NS_TYPE_CONSENSUS;
    1169           9 :   } else if (!strcmp(tok->args[0], "opinion")) {
    1170           1 :     ns->type = NS_TYPE_OPINION;
    1171             :   } else {
    1172           8 :     log_warn(LD_DIR, "Unrecognized vote status %s in network-status",
    1173             :              escaped(tok->args[0]));
    1174           8 :     goto err;
    1175             :   }
    1176        2215 :   if (ns_type != ns->type) {
    1177           2 :     log_warn(LD_DIR, "Got the wrong kind of v3 networkstatus.");
    1178           2 :     goto err;
    1179             :   }
    1180             : 
    1181        2213 :   if (ns->type == NS_TYPE_VOTE || ns->type == NS_TYPE_OPINION) {
    1182         380 :     tok = find_by_keyword(tokens, K_PUBLISHED);
    1183         380 :     if (parse_iso_time(tok->args[0], &ns->published))
    1184           9 :       goto err;
    1185             : 
    1186         371 :     ns->supported_methods = smartlist_new();
    1187         371 :     tok = find_opt_by_keyword(tokens, K_CONSENSUS_METHODS);
    1188         371 :     if (tok) {
    1189        3216 :       for (i=0; i < tok->n_args; ++i)
    1190        2845 :         smartlist_add_strdup(ns->supported_methods, tok->args[i]);
    1191             :     } else {
    1192           0 :       smartlist_add_strdup(ns->supported_methods, "1");
    1193             :     }
    1194             :   } else {
    1195        1833 :     tok = find_opt_by_keyword(tokens, K_CONSENSUS_METHOD);
    1196        1833 :     if (tok) {
    1197         228 :       int num_ok;
    1198         228 :       ns->consensus_method = (int)tor_parse_long(tok->args[0], 10, 1, INT_MAX,
    1199             :                                                  &num_ok, NULL);
    1200         228 :       if (!num_ok)
    1201           2 :         goto err;
    1202             :     } else {
    1203        1605 :       ns->consensus_method = 1;
    1204             :     }
    1205             :   }
    1206             : 
    1207             :   // Reject the vote if any of the protocols lines are malformed.
    1208        2202 :   bool unparseable = false;
    1209        2202 :   ns->recommended_client_protocols = dup_protocols_string(tokens, &unparseable,
    1210             :                                          K_RECOMMENDED_CLIENT_PROTOCOLS);
    1211        2202 :   ns->recommended_relay_protocols = dup_protocols_string(tokens, &unparseable,
    1212             :                                          K_RECOMMENDED_RELAY_PROTOCOLS);
    1213        2202 :   ns->required_client_protocols = dup_protocols_string(tokens, &unparseable,
    1214             :                                          K_REQUIRED_CLIENT_PROTOCOLS);
    1215        2202 :   ns->required_relay_protocols = dup_protocols_string(tokens, &unparseable,
    1216             :                                          K_REQUIRED_RELAY_PROTOCOLS);
    1217        2202 :   if (unparseable)
    1218          39 :     goto err;
    1219             : 
    1220        2163 :   tok = find_by_keyword(tokens, K_VALID_AFTER);
    1221        2163 :   if (parse_iso_time(tok->args[0], &ns->valid_after))
    1222          20 :     goto err;
    1223             : 
    1224        2143 :   tok = find_by_keyword(tokens, K_FRESH_UNTIL);
    1225        2143 :   if (parse_iso_time(tok->args[0], &ns->fresh_until))
    1226          17 :     goto err;
    1227             : 
    1228        2126 :   tok = find_by_keyword(tokens, K_VALID_UNTIL);
    1229        2126 :   if (parse_iso_time(tok->args[0], &ns->valid_until))
    1230          11 :     goto err;
    1231             : 
    1232        2115 :   tok = find_by_keyword(tokens, K_VOTING_DELAY);
    1233        2115 :   tor_assert(tok->n_args >= 2);
    1234             :   {
    1235        2115 :     int ok;
    1236        4230 :     ns->vote_seconds =
    1237        2115 :       (int) tor_parse_long(tok->args[0], 10, 0, INT_MAX, &ok, NULL);
    1238        2115 :     if (!ok)
    1239           6 :       goto err;
    1240        4224 :     ns->dist_seconds =
    1241        2112 :       (int) tor_parse_long(tok->args[1], 10, 0, INT_MAX, &ok, NULL);
    1242        2112 :     if (!ok)
    1243           3 :       goto err;
    1244             :   }
    1245        4218 :   if (ns->valid_after +
    1246        2109 :       (get_options()->TestingTorNetwork ?
    1247        4218 :        MIN_VOTE_INTERVAL_TESTING : MIN_VOTE_INTERVAL) > ns->fresh_until) {
    1248         302 :     log_warn(LD_DIR, "Vote/consensus freshness interval is too short");
    1249         302 :     goto err;
    1250             :   }
    1251        3614 :   if (ns->valid_after +
    1252        1807 :       (get_options()->TestingTorNetwork ?
    1253        3614 :        MIN_VOTE_INTERVAL_TESTING : MIN_VOTE_INTERVAL)*2 > ns->valid_until) {
    1254           7 :     log_warn(LD_DIR, "Vote/consensus liveness interval is too short");
    1255           7 :     goto err;
    1256             :   }
    1257        1800 :   if (ns->vote_seconds < MIN_VOTE_SECONDS) {
    1258           1 :     log_warn(LD_DIR, "Vote seconds is too short");
    1259           1 :     goto err;
    1260             :   }
    1261        1799 :   if (ns->dist_seconds < MIN_DIST_SECONDS) {
    1262           1 :     log_warn(LD_DIR, "Dist seconds is too short");
    1263           1 :     goto err;
    1264             :   }
    1265             : 
    1266        1798 :   if ((tok = find_opt_by_keyword(tokens, K_CLIENT_VERSIONS))) {
    1267         405 :     ns->client_versions = tor_strdup(tok->args[0]);
    1268             :   }
    1269        1798 :   if ((tok = find_opt_by_keyword(tokens, K_SERVER_VERSIONS))) {
    1270         310 :     ns->server_versions = tor_strdup(tok->args[0]);
    1271             :   }
    1272             : 
    1273             :   {
    1274        1798 :     smartlist_t *package_lst = find_all_by_keyword(tokens, K_PACKAGE);
    1275        1798 :     ns->package_lines = smartlist_new();
    1276        1798 :     if (package_lst) {
    1277         204 :       SMARTLIST_FOREACH(package_lst, directory_token_t *, t,
    1278             :                     smartlist_add_strdup(ns->package_lines, t->args[0]));
    1279             :     }
    1280        1798 :     smartlist_free(package_lst);
    1281             :   }
    1282             : 
    1283        1798 :   tok = find_by_keyword(tokens, K_KNOWN_FLAGS);
    1284        1798 :   ns->known_flags = smartlist_new();
    1285        1798 :   inorder = 1;
    1286        5177 :   for (i = 0; i < tok->n_args; ++i) {
    1287        3379 :     smartlist_add_strdup(ns->known_flags, tok->args[i]);
    1288        3379 :     if (i>0 && strcmp(tok->args[i-1], tok->args[i])>= 0) {
    1289         302 :       log_warn(LD_DIR, "%s >= %s", tok->args[i-1], tok->args[i]);
    1290         302 :       inorder = 0;
    1291             :     }
    1292             :   }
    1293        1798 :   if (!inorder) {
    1294           9 :     log_warn(LD_DIR, "known-flags not in order");
    1295           9 :     goto err;
    1296             :   }
    1297        1789 :   if (ns->type != NS_TYPE_CONSENSUS &&
    1298         326 :       smartlist_len(ns->known_flags) > MAX_KNOWN_FLAGS_IN_VOTE) {
    1299             :     /* If we allowed more than 64 flags in votes, then parsing them would make
    1300             :      * us invoke undefined behavior whenever we used 1<<flagnum to do a
    1301             :      * bit-shift. This is only for votes and opinions: consensus users don't
    1302             :      * care about flags they don't recognize, and so don't build a bitfield
    1303             :      * for them. */
    1304           0 :     log_warn(LD_DIR, "Too many known-flags in consensus vote or opinion");
    1305           0 :     goto err;
    1306             :   }
    1307             : 
    1308        1789 :   tok = find_opt_by_keyword(tokens, K_PARAMS);
    1309        1789 :   if (tok) {
    1310         361 :     int any_dups = 0;
    1311         361 :     inorder = 1;
    1312         361 :     ns->net_params = smartlist_new();
    1313        3009 :     for (i = 0; i < tok->n_args; ++i) {
    1314        2657 :       int ok=0;
    1315        2657 :       char *eq = strchr(tok->args[i], '=');
    1316        2657 :       size_t eq_pos;
    1317        2657 :       if (!eq) {
    1318           5 :         log_warn(LD_DIR, "Bad element '%s' in params", escaped(tok->args[i]));
    1319           9 :         goto err;
    1320             :       }
    1321        2652 :       eq_pos = eq-tok->args[i];
    1322        2652 :       tor_parse_long(eq+1, 10, INT32_MIN, INT32_MAX, &ok, NULL);
    1323        2652 :       if (!ok) {
    1324           4 :         log_warn(LD_DIR, "Bad element '%s' in params", escaped(tok->args[i]));
    1325           4 :         goto err;
    1326             :       }
    1327        2648 :       if (i > 0 && strcmp(tok->args[i-1], tok->args[i]) >= 0) {
    1328         437 :         log_warn(LD_DIR, "%s >= %s", tok->args[i-1], tok->args[i]);
    1329         437 :         inorder = 0;
    1330             :       }
    1331        2648 :       if (last_kwd && eq_pos == strlen(last_kwd) &&
    1332         758 :           fast_memeq(last_kwd, tok->args[i], eq_pos)) {
    1333         278 :         log_warn(LD_DIR, "Duplicate value for %s parameter",
    1334             :                  escaped(tok->args[i]));
    1335         278 :         any_dups = 1;
    1336             :       }
    1337        2648 :       tor_free(last_kwd);
    1338        2648 :       last_kwd = tor_strndup(tok->args[i], eq_pos);
    1339        2648 :       smartlist_add_strdup(ns->net_params, tok->args[i]);
    1340             :     }
    1341         352 :     if (!inorder) {
    1342          13 :       log_warn(LD_DIR, "params not in order");
    1343          13 :       goto err;
    1344             :     }
    1345         339 :     if (any_dups) {
    1346           2 :       log_warn(LD_DIR, "Duplicate in parameters");
    1347           2 :       goto err;
    1348             :     }
    1349             :   }
    1350             : 
    1351        1765 :   ns->voters = smartlist_new();
    1352             : 
    1353       42762 :   SMARTLIST_FOREACH_BEGIN(tokens, directory_token_t *, _tok) {
    1354       41051 :     tok = _tok;
    1355       41051 :     if (tok->tp == K_DIR_SOURCE) {
    1356        3547 :       tor_assert(tok->n_args >= 6);
    1357             : 
    1358        3547 :       if (voter)
    1359        1789 :         smartlist_add(ns->voters, voter);
    1360        3547 :       voter = tor_malloc_zero(sizeof(networkstatus_voter_info_t));
    1361        3547 :       voter->sigs = smartlist_new();
    1362        3547 :       if (ns->type != NS_TYPE_CONSENSUS)
    1363         326 :         memcpy(voter->vote_digest, ns_digests.d[DIGEST_SHA1], DIGEST_LEN);
    1364             : 
    1365        3547 :       voter->nickname = tor_strdup(tok->args[0]);
    1366        7085 :       if (strlen(tok->args[1]) != HEX_DIGEST_LEN ||
    1367        3538 :           base16_decode(voter->identity_digest, sizeof(voter->identity_digest),
    1368             :                         tok->args[1], HEX_DIGEST_LEN)
    1369             :                         != sizeof(voter->identity_digest)) {
    1370          16 :         log_warn(LD_DIR, "Error decoding identity digest %s in "
    1371             :                  "network-status document.", escaped(tok->args[1]));
    1372          32 :         goto err;
    1373             :       }
    1374        3853 :       if (ns->type != NS_TYPE_CONSENSUS &&
    1375         322 :           tor_memneq(ns->cert->cache_info.identity_digest,
    1376             :                  voter->identity_digest, DIGEST_LEN)) {
    1377           2 :         log_warn(LD_DIR,"Mismatch between identities in certificate and vote");
    1378           2 :         goto err;
    1379             :       }
    1380        3529 :       if (ns->type != NS_TYPE_CONSENSUS) {
    1381         320 :         if (authority_cert_is_denylisted(ns->cert)) {
    1382           0 :           log_warn(LD_DIR, "Rejecting vote signature made with denylisted "
    1383             :                    "signing key %s",
    1384             :                    hex_str(ns->cert->signing_key_digest, DIGEST_LEN));
    1385           0 :           goto err;
    1386             :         }
    1387             :       }
    1388        3529 :       voter->address = tor_strdup(tok->args[2]);
    1389        3529 :       if (!tor_inet_aton(tok->args[3], &in)) {
    1390          11 :         log_warn(LD_DIR, "Error decoding IP address %s in network-status.",
    1391             :                  escaped(tok->args[3]));
    1392          11 :         goto err;
    1393             :       }
    1394        3518 :       tor_addr_from_in(&voter->ipv4_addr, &in);
    1395        3518 :       int ok;
    1396        7036 :       voter->ipv4_dirport = (uint16_t)
    1397        3518 :         tor_parse_long(tok->args[4], 10, 0, 65535, &ok, NULL);
    1398        3518 :       if (!ok)
    1399           0 :         goto err;
    1400        7036 :       voter->ipv4_orport = (uint16_t)
    1401        3518 :         tor_parse_long(tok->args[5], 10, 0, 65535, &ok, NULL);
    1402        3518 :       if (!ok)
    1403           3 :         goto err;
    1404       37504 :     } else if (tok->tp == K_CONTACT) {
    1405        2973 :       if (!voter || voter->contact) {
    1406           6 :         log_warn(LD_DIR, "contact element is out of place.");
    1407           6 :         goto err;
    1408             :       }
    1409        2967 :       voter->contact = tor_strdup(tok->args[0]);
    1410       34531 :     } else if (tok->tp == K_VOTE_DIGEST) {
    1411        2812 :       tor_assert(ns->type == NS_TYPE_CONSENSUS);
    1412        2812 :       tor_assert(tok->n_args >= 1);
    1413        2812 :       if (!voter || ! tor_digest_is_zero(voter->vote_digest)) {
    1414           7 :         log_warn(LD_DIR, "vote-digest element is out of place.");
    1415           7 :         goto err;
    1416             :       }
    1417        5602 :       if (strlen(tok->args[0]) != HEX_DIGEST_LEN ||
    1418        2797 :         base16_decode(voter->vote_digest, sizeof(voter->vote_digest),
    1419             :                       tok->args[0], HEX_DIGEST_LEN)
    1420             :                       != sizeof(voter->vote_digest)) {
    1421           9 :         log_warn(LD_DIR, "Error decoding vote digest %s in "
    1422             :                  "network-status consensus.", escaped(tok->args[0]));
    1423           9 :         goto err;
    1424             :       }
    1425             :     }
    1426       40997 :   } SMARTLIST_FOREACH_END(_tok);
    1427        1711 :   if (voter) {
    1428        1711 :     smartlist_add(ns->voters, voter);
    1429        1711 :     voter = NULL;
    1430             :   }
    1431        1711 :   if (smartlist_len(ns->voters) == 0) {
    1432           0 :     log_warn(LD_DIR, "Missing dir-source elements in a networkstatus.");
    1433           0 :     goto err;
    1434        1711 :   } else if (ns->type != NS_TYPE_CONSENSUS && smartlist_len(ns->voters) != 1) {
    1435           0 :     log_warn(LD_DIR, "Too many dir-source elements in a vote networkstatus.");
    1436           0 :     goto err;
    1437             :   }
    1438             : 
    1439        1711 :   if (ns->type != NS_TYPE_CONSENSUS &&
    1440         320 :       (tok = find_opt_by_keyword(tokens, K_LEGACY_DIR_KEY))) {
    1441          48 :     int bad = 1;
    1442          48 :     if (strlen(tok->args[0]) == HEX_DIGEST_LEN) {
    1443          47 :       networkstatus_voter_info_t *voter_0 = smartlist_get(ns->voters, 0);
    1444          47 :       if (base16_decode(voter_0->legacy_id_digest, DIGEST_LEN,
    1445             :                         tok->args[0], HEX_DIGEST_LEN) != DIGEST_LEN)
    1446             :         bad = 1;
    1447             :       else
    1448             :         bad = 0;
    1449             :     }
    1450             :     if (bad) {
    1451           2 :       log_warn(LD_DIR, "Invalid legacy key digest %s on vote.",
    1452             :                escaped(tok->args[0]));
    1453             :     }
    1454             :   }
    1455             : 
    1456             :   /* If this is a vote document, check if information about the shared
    1457             :      randomness protocol is included, and extract it. */
    1458        1711 :   if (ns->type == NS_TYPE_VOTE) {
    1459         320 :     dirvote_parse_sr_commits(ns, tokens);
    1460             :   }
    1461             :   /* For both a vote and consensus, extract the shared random values. */
    1462        1711 :   if (ns->type == NS_TYPE_VOTE || ns->type == NS_TYPE_CONSENSUS) {
    1463        1711 :     extract_shared_random_srvs(ns, tokens);
    1464             :   }
    1465             : 
    1466             :   /* Parse routerstatus lines. */
    1467        1711 :   rs_tokens = smartlist_new();
    1468        1711 :   rs_area = memarea_new();
    1469        1711 :   s = end_of_header;
    1470        1711 :   ns->routerstatus_list = smartlist_new();
    1471             : 
    1472        2610 :   while (eos - s >= 2 && fast_memeq(s, "r ", 2)) {
    1473        2332 :     if (ns->type != NS_TYPE_CONSENSUS) {
    1474         502 :       vote_routerstatus_t *rs = tor_malloc_zero(sizeof(vote_routerstatus_t));
    1475         502 :       if (routerstatus_parse_entry_from_string(rs_area, &s, eos, rs_tokens, ns,
    1476             :                                                rs, 0, 0)) {
    1477         324 :         smartlist_add(ns->routerstatus_list, rs);
    1478             :       } else {
    1479         178 :         vote_routerstatus_free(rs);
    1480         178 :         goto err; // Malformed routerstatus, reject this vote.
    1481             :       }
    1482             :     } else {
    1483        1830 :       routerstatus_t *rs;
    1484        1830 :       if ((rs = routerstatus_parse_entry_from_string(rs_area, &s, eos,
    1485             :                                                      rs_tokens,
    1486             :                                                      NULL, NULL,
    1487             :                                                      ns->consensus_method,
    1488             :                                                      flav))) {
    1489             :         /* Use exponential-backoff scheduling when downloading microdescs */
    1490         575 :         smartlist_add(ns->routerstatus_list, rs);
    1491             :       } else {
    1492        1255 :         goto err; // Malformed routerstatus, reject this vote.
    1493             :       }
    1494             :     }
    1495             :   }
    1496         634 :   for (i = 1; i < smartlist_len(ns->routerstatus_list); ++i) {
    1497         368 :     routerstatus_t *rs1, *rs2;
    1498         368 :     if (ns->type != NS_TYPE_CONSENSUS) {
    1499         243 :       vote_routerstatus_t *a = smartlist_get(ns->routerstatus_list, i-1);
    1500         243 :       vote_routerstatus_t *b = smartlist_get(ns->routerstatus_list, i);
    1501         243 :       rs1 = &a->status; rs2 = &b->status;
    1502             :     } else {
    1503         125 :       rs1 = smartlist_get(ns->routerstatus_list, i-1);
    1504         125 :       rs2 = smartlist_get(ns->routerstatus_list, i);
    1505             :     }
    1506         368 :     if (fast_memcmp(rs1->identity_digest, rs2->identity_digest, DIGEST_LEN)
    1507             :         >= 0) {
    1508          12 :       log_warn(LD_DIR, "Networkstatus entries not sorted by identity digest");
    1509          12 :       goto err;
    1510             :     }
    1511             :   }
    1512         266 :   if (ns_type != NS_TYPE_CONSENSUS) {
    1513         142 :     digest256map_t *ed_id_map = digest256map_new();
    1514         466 :     SMARTLIST_FOREACH_BEGIN(ns->routerstatus_list, vote_routerstatus_t *,
    1515             :                             vrs) {
    1516         648 :       if (! vrs->has_ed25519_listing ||
    1517         324 :           fast_mem_is_zero((const char *)vrs->ed25519_id, DIGEST256_LEN))
    1518         324 :         continue;
    1519           0 :       if (digest256map_get(ed_id_map, vrs->ed25519_id) != NULL) {
    1520           0 :         log_warn(LD_DIR, "Vote networkstatus ed25519 identities were not "
    1521             :                  "unique");
    1522           0 :         digest256map_free(ed_id_map, NULL);
    1523           0 :         goto err;
    1524             :       }
    1525           0 :       digest256map_set(ed_id_map, vrs->ed25519_id, (void*)1);
    1526         324 :     } SMARTLIST_FOREACH_END(vrs);
    1527         142 :     digest256map_free(ed_id_map, NULL);
    1528             :   }
    1529             : 
    1530             :   /* Parse footer; check signature. */
    1531         266 :   footer_tokens = smartlist_new();
    1532         266 :   if ((end_of_footer = tor_memstr(s, eos-s, "\nnetwork-status-version ")))
    1533           1 :     ++end_of_footer;
    1534             :   else
    1535             :     end_of_footer = eos;
    1536         266 :   if (tokenize_string(area,s, end_of_footer, footer_tokens,
    1537             :                       networkstatus_vote_footer_token_table, 0)) {
    1538          35 :     log_warn(LD_DIR, "Error tokenizing network-status vote footer.");
    1539          35 :     goto err;
    1540             :   }
    1541             : 
    1542             :   {
    1543         231 :     int found_sig = 0;
    1544        1118 :     SMARTLIST_FOREACH_BEGIN(footer_tokens, directory_token_t *, _tok) {
    1545         887 :       tok = _tok;
    1546         887 :       if (tok->tp == K_DIRECTORY_SIGNATURE)
    1547             :         found_sig = 1;
    1548         469 :       else if (found_sig) {
    1549           0 :         log_warn(LD_DIR, "Extraneous token after first directory-signature");
    1550           0 :         goto err;
    1551             :       }
    1552         887 :     } SMARTLIST_FOREACH_END(_tok);
    1553             :   }
    1554             : 
    1555         231 :   if ((tok = find_opt_by_keyword(footer_tokens, K_DIRECTORY_FOOTER))) {
    1556         130 :     if (tok != smartlist_get(footer_tokens, 0)) {
    1557           0 :       log_warn(LD_DIR, "Misplaced directory-footer token");
    1558           0 :       goto err;
    1559             :     }
    1560             :   }
    1561             : 
    1562         231 :   tok = find_opt_by_keyword(footer_tokens, K_BW_WEIGHTS);
    1563         231 :   if (tok) {
    1564          56 :     ns->weight_params = smartlist_new();
    1565        1135 :     for (i = 0; i < tok->n_args; ++i) {
    1566        1081 :       int ok=0;
    1567        1081 :       char *eq = strchr(tok->args[i], '=');
    1568        1081 :       if (!eq) {
    1569           1 :         log_warn(LD_DIR, "Bad element '%s' in weight params",
    1570             :                  escaped(tok->args[i]));
    1571           2 :         goto err;
    1572             :       }
    1573        1080 :       tor_parse_long(eq+1, 10, INT32_MIN, INT32_MAX, &ok, NULL);
    1574        1080 :       if (!ok) {
    1575           1 :         log_warn(LD_DIR, "Bad element '%s' in params", escaped(tok->args[i]));
    1576           1 :         goto err;
    1577             :       }
    1578        1079 :       smartlist_add_strdup(ns->weight_params, tok->args[i]);
    1579             :     }
    1580             :   }
    1581             : 
    1582        1038 :   SMARTLIST_FOREACH_BEGIN(footer_tokens, directory_token_t *, _tok) {
    1583         828 :     char declared_identity[DIGEST_LEN];
    1584         828 :     networkstatus_voter_info_t *v;
    1585         828 :     document_signature_t *sig;
    1586         828 :     const char *id_hexdigest = NULL;
    1587         828 :     const char *sk_hexdigest = NULL;
    1588         828 :     digest_algorithm_t alg = DIGEST_SHA1;
    1589         828 :     tok = _tok;
    1590         828 :     if (tok->tp != K_DIRECTORY_SIGNATURE)
    1591         593 :       continue;
    1592         378 :     tor_assert(tok->n_args >= 2);
    1593         378 :     if (tok->n_args == 2) {
    1594         160 :       id_hexdigest = tok->args[0];
    1595         160 :       sk_hexdigest = tok->args[1];
    1596             :     } else {
    1597         218 :       const char *algname = tok->args[0];
    1598         218 :       int a;
    1599         218 :       id_hexdigest = tok->args[1];
    1600         218 :       sk_hexdigest = tok->args[2];
    1601         218 :       a = crypto_digest_algorithm_parse_name(algname);
    1602         218 :       if (a<0) {
    1603           7 :         log_warn(LD_DIR, "Unknown digest algorithm %s; skipping",
    1604             :                  escaped(algname));
    1605           7 :         continue;
    1606             :       }
    1607         211 :       alg = a;
    1608             :     }
    1609             : 
    1610         371 :     if (!tok->object_type ||
    1611         371 :         strcmp(tok->object_type, "SIGNATURE") ||
    1612         370 :         tok->object_size < 128 || tok->object_size > 512) {
    1613           8 :       log_warn(LD_DIR, "Bad object type or length on directory-signature");
    1614          19 :       goto err;
    1615             :     }
    1616             : 
    1617         724 :     if (strlen(id_hexdigest) != HEX_DIGEST_LEN ||
    1618         361 :         base16_decode(declared_identity, sizeof(declared_identity),
    1619             :                       id_hexdigest, HEX_DIGEST_LEN)
    1620             :                       != sizeof(declared_identity)) {
    1621           2 :       log_warn(LD_DIR, "Error decoding declared identity %s in "
    1622             :                "network-status document.", escaped(id_hexdigest));
    1623           2 :       goto err;
    1624             :     }
    1625         361 :     if (!(v = networkstatus_get_voter_by_id(ns, declared_identity))) {
    1626           5 :       log_warn(LD_DIR, "ID on signature on network-status document does "
    1627             :                "not match any declared directory source.");
    1628           5 :       goto err;
    1629             :     }
    1630         356 :     sig = tor_malloc_zero(sizeof(document_signature_t));
    1631         356 :     memcpy(sig->identity_digest, v->identity_digest, DIGEST_LEN);
    1632         356 :     sig->alg = alg;
    1633         711 :     if (strlen(sk_hexdigest) != HEX_DIGEST_LEN ||
    1634         355 :         base16_decode(sig->signing_key_digest, sizeof(sig->signing_key_digest),
    1635             :                       sk_hexdigest, HEX_DIGEST_LEN)
    1636             :                       != sizeof(sig->signing_key_digest)) {
    1637           4 :       log_warn(LD_DIR, "Error decoding declared signing key digest %s in "
    1638             :                "network-status document.", escaped(sk_hexdigest));
    1639           4 :       tor_free(sig);
    1640           4 :       goto err;
    1641             :     }
    1642             : 
    1643         352 :     if (ns->type != NS_TYPE_CONSENSUS) {
    1644         115 :       if (tor_memneq(declared_identity, ns->cert->cache_info.identity_digest,
    1645             :                  DIGEST_LEN)) {
    1646           0 :         log_warn(LD_DIR, "Digest mismatch between declared and actual on "
    1647             :                  "network-status vote.");
    1648           0 :         tor_free(sig);
    1649           0 :         goto err;
    1650             :       }
    1651             :     }
    1652             : 
    1653         352 :     if (networkstatus_get_voter_sig_by_alg(v, sig->alg)) {
    1654             :       /* We already parsed a vote with this algorithm from this voter. Use the
    1655             :          first one. */
    1656         136 :       log_fn(LOG_PROTOCOL_WARN, LD_DIR, "We received a networkstatus "
    1657             :              "that contains two signatures from the same voter with the same "
    1658             :              "algorithm. Ignoring the second signature.");
    1659         136 :       tor_free(sig);
    1660         136 :       continue;
    1661             :     }
    1662             : 
    1663         216 :     if (ns->type != NS_TYPE_CONSENSUS) {
    1664          86 :       if (check_signature_token(ns_digests.d[DIGEST_SHA1], DIGEST_LEN,
    1665          86 :                                 tok, ns->cert->signing_key, 0,
    1666             :                                 "network-status document")) {
    1667           0 :         tor_free(sig);
    1668           0 :         goto err;
    1669             :       }
    1670          86 :       sig->good_signature = 1;
    1671             :     } else {
    1672         130 :       if (tok->object_size >= INT_MAX || tok->object_size >= SIZE_T_CEILING) {
    1673           0 :         tor_free(sig);
    1674           0 :         goto err;
    1675             :       }
    1676         130 :       sig->signature = tor_memdup(tok->object_body, tok->object_size);
    1677         130 :       sig->signature_len = (int) tok->object_size;
    1678             :     }
    1679         216 :     smartlist_add(v->sigs, sig);
    1680             : 
    1681         216 :     ++n_signatures;
    1682         809 :   } SMARTLIST_FOREACH_END(_tok);
    1683             : 
    1684         210 :   if (! n_signatures) {
    1685          70 :     log_warn(LD_DIR, "No signatures on networkstatus document.");
    1686          70 :     goto err;
    1687         140 :   } else if (ns->type == NS_TYPE_VOTE && n_signatures != 1) {
    1688           0 :     log_warn(LD_DIR, "Received more than one signature on a "
    1689             :              "network-status vote.");
    1690           0 :     goto err;
    1691             :   }
    1692             : 
    1693         140 :   if (eos_out)
    1694          38 :     *eos_out = end_of_footer;
    1695             : 
    1696         140 :   goto done;
    1697        2360 :  err:
    1698        2360 :   dump_desc(s_dup, "v3 networkstatus");
    1699        2360 :   networkstatus_vote_free(ns);
    1700        2360 :   ns = NULL;
    1701        2500 :  done:
    1702        2500 :   if (tokens) {
    1703       62869 :     SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_clear(t));
    1704        2500 :     smartlist_free(tokens);
    1705             :   }
    1706        2500 :   if (voter) {
    1707          47 :     if (voter->sigs) {
    1708          47 :       SMARTLIST_FOREACH(voter->sigs, document_signature_t *, sig,
    1709             :                         document_signature_free(sig));
    1710          47 :       smartlist_free(voter->sigs);
    1711             :     }
    1712          47 :     tor_free(voter->nickname);
    1713          47 :     tor_free(voter->address);
    1714          47 :     tor_free(voter->contact);
    1715          47 :     tor_free(voter);
    1716             :   }
    1717        2500 :   if (rs_tokens) {
    1718        1711 :     SMARTLIST_FOREACH(rs_tokens, directory_token_t *, t, token_clear(t));
    1719        1711 :     smartlist_free(rs_tokens);
    1720             :   }
    1721        2500 :   if (footer_tokens) {
    1722        1578 :     SMARTLIST_FOREACH(footer_tokens, directory_token_t *, t, token_clear(t));
    1723         266 :     smartlist_free(footer_tokens);
    1724             :   }
    1725        2500 :   if (area) {
    1726        2464 :     DUMP_AREA(area, "v3 networkstatus");
    1727        2464 :     memarea_drop_all(area);
    1728             :   }
    1729        2500 :   if (rs_area)
    1730        1711 :     memarea_drop_all(rs_area);
    1731        2500 :   tor_free(last_kwd);
    1732             : 
    1733        2500 :   return ns;
    1734             : }

Generated by: LCOV version 1.14