tor

The Tor anonymity network
git clone https://git.dasho.dev/tor.git
Log | Files | Refs | README | LICENSE

ns_parse.c (62248B)


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