From 3cddd6570c6c1e30b002893262680c3d3235d6c3 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Wed, 9 Aug 2017 13:45:03 -0400 Subject: [PATCH 1/7] Add a hashtable mapping to nodes from ed25519 ids --- src/or/nodelist.c | 126 +++++++++++++++++++++++++++++++++++++++++++++- src/or/nodelist.h | 3 ++ src/or/or.h | 9 ++++ 3 files changed, 137 insertions(+), 1 deletion(-) diff --git a/src/or/nodelist.c b/src/or/nodelist.c index 0fcaea626d..93d362d8e2 100644 --- a/src/or/nodelist.c +++ b/src/or/nodelist.c @@ -92,7 +92,14 @@ typedef struct nodelist_t { smartlist_t *nodes; /* Hash table to map from node ID digest to node. */ HT_HEAD(nodelist_map, node_t) nodes_by_id; - + /* Hash table to map from node Ed25519 ID to node. + * + * Whenever a node's routerinfo or microdescriptor is about to change, + * you should remove it from this map with node_remove_from_ed25519_map(). + * Whenever a node's routerinfo or microdescriptor has just chaned, + * you should add it to this map with node_add_to_ed25519_map(). + */ + HT_HEAD(nodelist_ed_map, node_t) nodes_by_ed_id; } nodelist_t; static inline unsigned int @@ -111,6 +118,23 @@ HT_PROTOTYPE(nodelist_map, node_t, ht_ent, node_id_hash, node_id_eq) HT_GENERATE2(nodelist_map, node_t, ht_ent, node_id_hash, node_id_eq, 0.6, tor_reallocarray_, tor_free_) +static inline unsigned int +node_ed_id_hash(const node_t *node) +{ + return (unsigned) siphash24g(node->ed25519_id.pubkey, ED25519_PUBKEY_LEN); +} + +static inline unsigned int +node_ed_id_eq(const node_t *node1, const node_t *node2) +{ + return ed25519_pubkey_eq(&node1->ed25519_id, &node2->ed25519_id); +} + +HT_PROTOTYPE(nodelist_ed_map, node_t, ed_ht_ent, node_ed_id_hash, + node_ed_id_eq) +HT_GENERATE2(nodelist_ed_map, node_t, ed_ht_ent, node_ed_id_hash, + node_ed_id_eq, 0.6, tor_reallocarray_, tor_free_) + /** The global nodelist. */ static nodelist_t *the_nodelist=NULL; @@ -121,6 +145,7 @@ init_nodelist(void) if (PREDICT_UNLIKELY(the_nodelist == NULL)) { the_nodelist = tor_malloc_zero(sizeof(nodelist_t)); HT_INIT(nodelist_map, &the_nodelist->nodes_by_id); + HT_INIT(nodelist_ed_map, &the_nodelist->nodes_by_ed_id); the_nodelist->nodes = smartlist_new(); } } @@ -138,6 +163,21 @@ node_get_mutable_by_id(const char *identity_digest) return node; } +/** As node_get_by_ed25519_id, but returns a non-const pointer */ +node_t * +node_get_mutable_by_ed25519_id(const ed25519_public_key_t *ed_id) +{ + node_t search, *node; + if (PREDICT_UNLIKELY(the_nodelist == NULL)) + return NULL; + if (BUG(ed_id == NULL) || BUG(ed25519_public_key_is_zero(ed_id))) + return NULL; + + memcpy(&search.ed25519_id, ed_id, sizeof(search.ed25519_id)); + node = HT_FIND(nodelist_ed_map, &the_nodelist->nodes_by_ed_id, &search); + return node; +} + /** Return the node_t whose identity is identity_digest, or NULL * if no such node exists. */ MOCK_IMPL(const node_t *, @@ -146,6 +186,14 @@ node_get_by_id,(const char *identity_digest)) return node_get_mutable_by_id(identity_digest); } +/** Return the node_t whose ed25519 identity is ed_id, or NULL + * if no such node exists. */ +MOCK_IMPL(const node_t *, +node_get_by_ed25519_id,(const ed25519_public_key_t *ed_id)) +{ + return node_get_mutable_by_ed25519_id(ed_id); +} + /** Internal: return the node_t whose identity_digest is * identity_digest. If none exists, create a new one, add it to the * nodelist, and return it. @@ -173,6 +221,67 @@ node_get_or_create(const char *identity_digest) return node; } +/** Remove node from the ed25519 map (if it present), and + * set its ed25519_id field to zero. */ +static int +node_remove_from_ed25519_map(node_t *node) +{ + tor_assert(the_nodelist); + tor_assert(node); + + if (ed25519_public_key_is_zero(&node->ed25519_id)) { + return 0; + } + + int rv = 0; + node_t *search = + HT_FIND(nodelist_ed_map, &the_nodelist->nodes_by_ed_id, node); + if (BUG(search != node)) { + goto clear_and_return; + } + + search = HT_REMOVE(nodelist_ed_map, &the_nodelist->nodes_by_ed_id, node); + tor_assert(search == node); + rv = 1; + + clear_and_return: + memset(&node->ed25519_id, 0, sizeof(node->ed25519_id)); + return rv; +} + +/** If node has an ed25519 id, and it is not already in the ed25519 id + * map, set its ed25519_id field, and add it to the ed25519 map. + */ +static int +node_add_to_ed25519_map(node_t *node) +{ + tor_assert(the_nodelist); + tor_assert(node); + + if (! ed25519_public_key_is_zero(&node->ed25519_id)) { + return 0; + } + + const ed25519_public_key_t *key = node_get_ed25519_id(node); + if (!key) { + return 0; + } + + node_t *old; + memcpy(&node->ed25519_id, key, sizeof(node->ed25519_id)); + old = HT_FIND(nodelist_ed_map, &the_nodelist->nodes_by_ed_id, node); + if (BUG(old)) { + /* XXXX order matters here, and this may mean that authorities aren't + * pinning. */ + if (old != node) + memset(&node->ed25519_id, 0, sizeof(node->ed25519_id)); + return 0; + } + + HT_INSERT(nodelist_ed_map, &the_nodelist->nodes_by_ed_id, node); + return 1; +} + /* For a given node for the consensus ns, set the hsdir index * for the node, both current and next if possible. This can only fails if the * node_t ed25519 identity key can't be found which would be a bug. */ @@ -262,6 +371,8 @@ nodelist_set_routerinfo(routerinfo_t *ri, routerinfo_t **ri_old_out) id_digest = ri->cache_info.identity_digest; node = node_get_or_create(id_digest); + node_remove_from_ed25519_map(node); + if (node->ri) { if (!routers_have_same_or_addrs(node->ri, ri)) { node_addrs_changed(node); @@ -275,6 +386,8 @@ nodelist_set_routerinfo(routerinfo_t *ri, routerinfo_t **ri_old_out) } node->ri = ri; + node_add_to_ed25519_map(node); + if (node->country == -1) node_set_country(node); @@ -318,8 +431,10 @@ nodelist_add_microdesc(microdesc_t *md) return NULL; node = node_get_mutable_by_id(rs->identity_digest); if (node) { + node_remove_from_ed25519_map(node); if (node->md) node->md->held_by_nodes--; + node->md = md; md->held_by_nodes++; /* Setting the HSDir index requires the ed25519 identity key which can @@ -328,7 +443,9 @@ nodelist_add_microdesc(microdesc_t *md) if (rs->supports_v3_hsdir) { node_set_hsdir_index(node, ns); } + node_add_to_ed25519_map(node); } + return node; } @@ -356,12 +473,14 @@ nodelist_set_consensus(networkstatus_t *ns) if (ns->flavor == FLAV_MICRODESC) { if (node->md == NULL || tor_memneq(node->md->digest,rs->descriptor_digest,DIGEST256_LEN)) { + node_remove_from_ed25519_map(node); if (node->md) node->md->held_by_nodes--; node->md = microdesc_cache_lookup_by_digest256(NULL, rs->descriptor_digest); if (node->md) node->md->held_by_nodes++; + node_add_to_ed25519_map(node); } } @@ -426,6 +545,9 @@ nodelist_remove_microdesc(const char *identity_digest, microdesc_t *md) if (node && node->md == md) { node->md = NULL; md->held_by_nodes--; + if (! node_get_ed25519_id(node)) { + node_remove_from_ed25519_map(node); + } } } @@ -454,6 +576,7 @@ nodelist_drop_node(node_t *node, int remove_from_ht) tmp = HT_REMOVE(nodelist_map, &the_nodelist->nodes_by_id, node); tor_assert(tmp == node); } + node_remove_from_ed25519_map(node); idx = node->nodelist_idx; tor_assert(idx >= 0); @@ -537,6 +660,7 @@ nodelist_free_all(void) return; HT_CLEAR(nodelist_map, &the_nodelist->nodes_by_id); + HT_CLEAR(nodelist_ed_map, &the_nodelist->nodes_by_ed_id); SMARTLIST_FOREACH_BEGIN(the_nodelist->nodes, node_t *, node) { node->nodelist_idx = -1; node_free(node); diff --git a/src/or/nodelist.h b/src/or/nodelist.h index 405b79d820..20df37b511 100644 --- a/src/or/nodelist.h +++ b/src/or/nodelist.h @@ -18,6 +18,9 @@ node_t *node_get_mutable_by_id(const char *identity_digest); MOCK_DECL(const node_t *, node_get_by_id, (const char *identity_digest)); +node_t *node_get_mutable_by_ed25519_id(const ed25519_public_key_t *ed_id); +MOCK_DECL(const node_t *, node_get_by_ed25519_id, + (const ed25519_public_key_t *ed_id)); const node_t *node_get_by_hex_id(const char *identity_digest); node_t *nodelist_set_routerinfo(routerinfo_t *ri, routerinfo_t **ri_old_out); node_t *nodelist_add_microdesc(microdesc_t *md); diff --git a/src/or/or.h b/src/or/or.h index ff11c72790..9449e87e11 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -2443,6 +2443,8 @@ typedef struct node_t { /** Used to look up the node_t by its identity digest. */ HT_ENTRY(node_t) ht_ent; + /** Used to look up the node_t by its ed25519 identity digest. */ + HT_ENTRY(node_t) ed_ht_ent; /** Position of the node within the list of nodes */ int nodelist_idx; @@ -2450,6 +2452,13 @@ typedef struct node_t { * identity may exist at a time. */ char identity[DIGEST_LEN]; + /** The ed25519 identity of this node_t. This field is nonzero iff we + * currently have an ed25519 identity for this node in either md or ri, + * _and_ this node has been inserted to the ed25519-to-node map in the + * nodelist. + */ + ed25519_public_key_t ed25519_id; + microdesc_t *md; routerinfo_t *ri; routerstatus_t *rs; From fc212458a67e1bc27a6456609b02eb703ef7be05 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Wed, 9 Aug 2017 13:49:34 -0400 Subject: [PATCH 2/7] Set formerly-unused fields of a stack-allocated routerinfo --- src/test/test_hs_service.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/test_hs_service.c b/src/test/test_hs_service.c index 60efca15f8..3400379551 100644 --- a/src/test/test_hs_service.c +++ b/src/test/test_hs_service.c @@ -895,6 +895,7 @@ test_service_event(void *arg) * times we should retry a circuit. For this, we need to have a node_t * that matches the identity of this IP. */ routerinfo_t ri; + memset(&ri, 0, sizeof(ri)); ip = helper_create_service_ip(); service_intro_point_add(service->desc_current->intro_points.map, ip); memset(ri.cache_info.identity_digest, 'A', DIGEST_LEN); From fed3a08d8c32f2010caba06c888f28716e3abb0a Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Wed, 9 Aug 2017 14:00:30 -0400 Subject: [PATCH 3/7] Support the ed25519 map in nodelist_assert_ok() --- src/or/nodelist.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/or/nodelist.c b/src/or/nodelist.c index 93d362d8e2..104915d293 100644 --- a/src/or/nodelist.c +++ b/src/or/nodelist.c @@ -725,9 +725,27 @@ nodelist_assert_ok(void) tor_assert(node_sl_idx == node->nodelist_idx); } SMARTLIST_FOREACH_END(node); + /* Every node listed with an ed25519 identity should be listed by that + * identity. + */ + SMARTLIST_FOREACH_BEGIN(the_nodelist->nodes, node_t *, node) { + if (!ed25519_public_key_is_zero(&node->ed25519_id)) { + tor_assert(node == node_get_by_ed25519_id(&node->ed25519_id)); + } + } SMARTLIST_FOREACH_END(node); + + node_t **idx; + HT_FOREACH(idx, nodelist_ed_map, &the_nodelist->nodes_by_ed_id) { + node_t *node = *idx; + tor_assert(node == node_get_by_ed25519_id(&node->ed25519_id)); + } + tor_assert((long)smartlist_len(the_nodelist->nodes) == (long)HT_SIZE(&the_nodelist->nodes_by_id)); + tor_assert((long)smartlist_len(the_nodelist->nodes) >= + (long)HT_SIZE(&the_nodelist->nodes_by_ed_id)); + digestmap_free(dm, NULL); } From 92b1dfd50eb9a6d3586bf4dd03890e5ba3f90fc8 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Wed, 9 Aug 2017 15:00:32 -0400 Subject: [PATCH 4/7] In node_get_ed25519_id, detect and warn on inconsistent IDs. This shouldn't actually be possible, but it's worth checking for. --- src/or/nodelist.c | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/src/or/nodelist.c b/src/or/nodelist.c index 104915d293..d2a4c8201e 100644 --- a/src/or/nodelist.c +++ b/src/or/nodelist.c @@ -879,22 +879,34 @@ node_get_by_nickname,(const char *nickname, int warn_if_unnamed)) const ed25519_public_key_t * node_get_ed25519_id(const node_t *node) { + const ed25519_public_key_t *ri_pk = NULL; + const ed25519_public_key_t *md_pk = NULL; if (node->ri) { if (node->ri->cache_info.signing_key_cert) { - const ed25519_public_key_t *pk = - &node->ri->cache_info.signing_key_cert->signing_key; - if (BUG(ed25519_public_key_is_zero(pk))) - goto try_the_md; - return pk; + ri_pk = &node->ri->cache_info.signing_key_cert->signing_key; + if (BUG(ed25519_public_key_is_zero(ri_pk))) + ri_pk = NULL; } } - try_the_md: + if (node->md) { if (node->md->ed25519_identity_pkey) { - return node->md->ed25519_identity_pkey; + md_pk = node->md->ed25519_identity_pkey; } } - return NULL; + + if (ri_pk && md_pk) { + if (ed25519_pubkey_eq(ri_pk, md_pk)) { + return ri_pk; + } else { + log_warn(LD_GENERAL, "Inconsistent ed25519 identities in the nodelist"); + return NULL; + } + } else if (ri_pk) { + return ri_pk; + } else { + return md_pk; + } } /** Return true iff this node's Ed25519 identity matches id. From 40887b4d2ec2e1865df8f381aa1387ba4ee10eba Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Tue, 22 Aug 2017 14:50:50 -0400 Subject: [PATCH 5/7] Unit tests for maintaining ed25519->node map. --- src/test/test_nodelist.c | 104 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) diff --git a/src/test/test_nodelist.c b/src/test/test_nodelist.c index 256354415c..db2a8fd02c 100644 --- a/src/test/test_nodelist.c +++ b/src/test/test_nodelist.c @@ -7,7 +7,9 @@ **/ #include "or.h" +#include "networkstatus.h" #include "nodelist.h" +#include "torcert.h" #include "test.h" /** Test the case when node_get_by_id() returns NULL, @@ -100,6 +102,107 @@ test_nodelist_node_is_dir(void *arg) return; } +static networkstatus_t *dummy_ns = NULL; +static networkstatus_t * +mock_networkstatus_get_latest_consensus(void) +{ + return dummy_ns; +} +static networkstatus_t * +mock_networkstatus_get_latest_consensus_by_flavor(consensus_flavor_t f) +{ + tor_assert(f == FLAV_MICRODESC); + return dummy_ns; +} + +static void +test_nodelist_ed_id(void *arg) +{ + routerstatus_t *rs[4]; + microdesc_t *md[4]; + routerinfo_t *ri[4]; + networkstatus_t *ns; + int i; + (void)arg; + + ns = tor_malloc_zero(sizeof(networkstatus_t)); + ns->flavor = FLAV_MICRODESC; + ns->routerstatus_list = smartlist_new(); + dummy_ns = ns; + MOCK(networkstatus_get_latest_consensus, + mock_networkstatus_get_latest_consensus); + MOCK(networkstatus_get_latest_consensus_by_flavor, + mock_networkstatus_get_latest_consensus_by_flavor); + + /* Make a bunch of dummy objects that we can play around with. Only set the + necessary fields */ + + for (i = 0; i < 4; ++i) { + rs[i] = tor_malloc_zero(sizeof(*rs[i])); + md[i] = tor_malloc_zero(sizeof(*md[i])); + ri[i] = tor_malloc_zero(sizeof(*ri[i])); + + crypto_rand(md[i]->digest, sizeof(md[i]->digest)); + md[i]->ed25519_identity_pkey = tor_malloc(sizeof(ed25519_public_key_t)); + crypto_rand((char*)md[i]->ed25519_identity_pkey, + sizeof(ed25519_public_key_t)); + crypto_rand(rs[i]->identity_digest, sizeof(rs[i]->identity_digest)); + memcpy(ri[i]->cache_info.identity_digest, rs[i]->identity_digest, + DIGEST_LEN); + memcpy(rs[i]->descriptor_digest, md[i]->digest, DIGEST256_LEN); + ri[i]->cache_info.signing_key_cert = tor_malloc_zero(sizeof(tor_cert_t)); + memcpy(&ri[i]->cache_info.signing_key_cert->signing_key, + md[i]->ed25519_identity_pkey, sizeof(ed25519_public_key_t)); + + if (i != 3) + smartlist_add(ns->routerstatus_list, rs[i]); + } + + tt_int_op(0, OP_EQ, smartlist_len(nodelist_get_list())); + + nodelist_set_consensus(ns); + + tt_int_op(3, OP_EQ, smartlist_len(nodelist_get_list())); + + /* No Ed25519 info yet, so nothing has an ED id. */ + tt_ptr_op(NULL, OP_EQ, node_get_by_ed25519_id(md[0]->ed25519_identity_pkey)); + + /* Register the first one by md, then look it up. */ + node_t *n = nodelist_add_microdesc(md[0]); + tt_ptr_op(n, OP_EQ, node_get_by_ed25519_id(md[0]->ed25519_identity_pkey)); + + /* Register the second by ri, then look it up. */ + routerinfo_t *ri_old = NULL; + n = nodelist_set_routerinfo(ri[1], &ri_old); + tt_ptr_op(n, OP_EQ, node_get_by_ed25519_id(md[1]->ed25519_identity_pkey)); + tt_ptr_op(ri_old, OP_EQ, NULL); + + /* Register it by md too. */ + node_t *n2 = nodelist_add_microdesc(md[1]); + tt_ptr_op(n2, OP_EQ, n); + tt_ptr_op(n, OP_EQ, node_get_by_ed25519_id(md[1]->ed25519_identity_pkey)); + + /* Register the 4th by ri only -- we never put it into the networkstatus, + * so it has to be independent */ + n = nodelist_set_routerinfo(ri[3], &ri_old); + tt_ptr_op(n, OP_EQ, node_get_by_ed25519_id(md[3]->ed25519_identity_pkey)); + tt_ptr_op(ri_old, OP_EQ, NULL); + tt_int_op(4, OP_EQ, smartlist_len(nodelist_get_list())); + + done: + for (i = 0; i < 4; ++i) { + tor_free(rs[i]); + tor_free(md[i]->ed25519_identity_pkey); + tor_free(md[i]); + tor_free(ri[i]->cache_info.signing_key_cert); + tor_free(ri[i]); + } + smartlist_free(ns->routerstatus_list); + tor_free(ns); + UNMOCK(networkstatus_get_latest_consensus); + UNMOCK(networkstatus_get_latest_consensus_by_flavor); +} + #define NODE(name, flags) \ { #name, test_nodelist_##name, (flags), NULL, NULL } @@ -107,6 +210,7 @@ struct testcase_t nodelist_tests[] = { NODE(node_get_verbose_nickname_by_id_null_node, TT_FORK), NODE(node_get_verbose_nickname_not_named, TT_FORK), NODE(node_is_dir, TT_FORK), + NODE(ed_id, TT_FORK), END_OF_TESTCASES }; From d7a3e336ee505bcbeb30117d91067810ad096130 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Tue, 22 Aug 2017 18:47:57 -0400 Subject: [PATCH 6/7] Remove some support for nickname-based hexdigests We once used $X=N to mean "A relay with RSA ID digest X with the Named flag and the nickname N." But authorities no longer assign the Named flag. --- src/or/circuitbuild.c | 11 +++-------- src/or/nodelist.c | 34 +++++----------------------------- src/or/nodelist.h | 1 - src/or/routerlist.c | 12 +++++++----- src/or/routerlist.h | 2 +- 5 files changed, 16 insertions(+), 44 deletions(-) diff --git a/src/or/circuitbuild.c b/src/or/circuitbuild.c index 257edab50b..fa55cae545 100644 --- a/src/or/circuitbuild.c +++ b/src/or/circuitbuild.c @@ -289,14 +289,9 @@ circuit_list_path_impl(origin_circuit_t *circ, int verbose, int verbose_names) base16_encode(elt+1, HEX_DIGEST_LEN+1, id, DIGEST_LEN); } } else { /* ! verbose_names */ - node = node_get_by_id(id); - if (node && node_is_named(node)) { - elt = tor_strdup(node_get_nickname(node)); - } else { - elt = tor_malloc(HEX_DIGEST_LEN+2); - elt[0] = '$'; - base16_encode(elt+1, HEX_DIGEST_LEN+1, id, DIGEST_LEN); - } + elt = tor_malloc(HEX_DIGEST_LEN+2); + elt[0] = '$'; + base16_encode(elt+1, HEX_DIGEST_LEN+1, id, DIGEST_LEN); } tor_assert(elt); if (verbose) { diff --git a/src/or/nodelist.c b/src/or/nodelist.c index d2a4c8201e..b1cb8d6b5d 100644 --- a/src/or/nodelist.c +++ b/src/or/nodelist.c @@ -774,16 +774,9 @@ node_get_by_hex_id(const char *hex_id) const node_t *node = node_get_by_id(digest_buf); if (!node) return NULL; - if (nn_char) { - const char *real_name = node_get_nickname(node); - if (!real_name || strcasecmp(real_name, nn_buf)) - return NULL; - if (nn_char == '=') { - const char *named_id = - networkstatus_get_router_digest_by_nickname(nn_buf); - if (!named_id || tor_memneq(named_id, digest_buf, DIGEST_LEN)) - return NULL; - } + if (nn_char == '=') { + /* "=" indicates a Named relay, but there aren't any of those now. */ + return NULL; } return node; } @@ -1017,21 +1010,6 @@ node_get_nickname(const node_t *node) return NULL; } -/** Return true iff the nickname of node is canonical, based on the - * latest consensus. */ -int -node_is_named(const node_t *node) -{ - const char *named_id; - const char *nickname = node_get_nickname(node); - if (!nickname) - return 0; - named_id = networkstatus_get_router_digest_by_nickname(nickname); - if (!named_id) - return 0; - return tor_memeq(named_id, node->identity, DIGEST_LEN); -} - /** Return true iff node appears to be a directory authority or * directory cache */ int @@ -1079,13 +1057,12 @@ node_get_verbose_nickname(const node_t *node, char *verbose_name_out) { const char *nickname = node_get_nickname(node); - int is_named = node_is_named(node); verbose_name_out[0] = '$'; base16_encode(verbose_name_out+1, HEX_DIGEST_LEN+1, node->identity, DIGEST_LEN); if (!nickname) return; - verbose_name_out[1+HEX_DIGEST_LEN] = is_named ? '=' : '~'; + verbose_name_out[1+HEX_DIGEST_LEN] = '~'; strlcpy(verbose_name_out+1+HEX_DIGEST_LEN+1, nickname, MAX_NICKNAME_LEN+1); } @@ -1640,8 +1617,7 @@ node_nickname_matches(const node_t *node, const char *nickname) return 1; return hex_digest_nickname_matches(nickname, node->identity, - n, - node_is_named(node)); + n); } /** Return true iff node is named by some nickname in lst. */ diff --git a/src/or/nodelist.h b/src/or/nodelist.h index 20df37b511..c5805c55f1 100644 --- a/src/or/nodelist.h +++ b/src/or/nodelist.h @@ -40,7 +40,6 @@ void node_get_verbose_nickname(const node_t *node, char *verbose_name_out); void node_get_verbose_nickname_by_id(const char *id_digest, char *verbose_name_out); -int node_is_named(const node_t *node); int node_is_dir(const node_t *node); int node_has_descriptor(const node_t *node); int node_get_purpose(const node_t *node); diff --git a/src/or/routerlist.c b/src/or/routerlist.c index 49caa875fe..240ee9d68c 100644 --- a/src/or/routerlist.c +++ b/src/or/routerlist.c @@ -2926,7 +2926,7 @@ hex_digest_nickname_decode(const char *hexdigest, * hexdigest is malformed, or it doesn't match. */ int hex_digest_nickname_matches(const char *hexdigest, const char *identity_digest, - const char *nickname, int is_named) + const char *nickname) { char digest[DIGEST_LEN]; char nn_char='\0'; @@ -2935,13 +2935,15 @@ hex_digest_nickname_matches(const char *hexdigest, const char *identity_digest, if (hex_digest_nickname_decode(hexdigest, digest, &nn_char, nn_buf) == -1) return 0; - if (nn_char == '=' || nn_char == '~') { - if (!nickname) + if (nn_char == '=') { + return 0; + } + + if (nn_char == '~') { + if (!nickname) // XXX This seems wrong. -NM return 0; if (strcasecmp(nn_buf, nickname)) return 0; - if (nn_char == '=' && !is_named) - return 0; } return tor_memeq(digest, identity_digest, DIGEST_LEN); diff --git a/src/or/routerlist.h b/src/or/routerlist.h index e0ed4e623a..931373f5cd 100644 --- a/src/or/routerlist.h +++ b/src/or/routerlist.h @@ -228,7 +228,7 @@ int hex_digest_nickname_decode(const char *hexdigest, char *nickname_out); int hex_digest_nickname_matches(const char *hexdigest, const char *identity_digest, - const char *nickname, int is_named); + const char *nickname); #ifdef ROUTERLIST_PRIVATE STATIC int choose_array_element_by_weight(const uint64_t *entries, From 80d3887360548b28fe2bd06501f0d51d0a1ba4f0 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Tue, 22 Aug 2017 19:04:31 -0400 Subject: [PATCH 7/7] Refactor node lookup APIs to take flags Right now there's a single warn_if_unnamed flag for router_get_consensus_status_by_nickname() and node_get_by_nickname(), that is nearly always 1. I've turned it into an 'unsigned' bitfield, and inverted its sense. I've added the flags argument to node_get_by_hex_id() too, though it does nothing there right now. I've removed the router_get_consensus_status_by_nickname() function, since it was only used in once place. This patch changes the warning behavior of GETINFO ns/name/, since all other name lookups from the controller currently warn. Later I'm going to add more flags, for ed25519 support. --- src/or/addressmap.c | 2 +- src/or/circuituse.c | 6 +++--- src/or/connection_edge.c | 9 +++++---- src/or/control.c | 16 ++++++++-------- src/or/networkstatus.c | 18 ++---------------- src/or/networkstatus.h | 3 --- src/or/nodelist.c | 20 ++++++++++++-------- src/or/nodelist.h | 8 ++++++-- src/or/rendservice.c | 2 +- src/or/router.c | 2 +- src/or/routerset.c | 2 +- src/test/test_routerset.c | 18 +++++++++--------- 12 files changed, 49 insertions(+), 57 deletions(-) diff --git a/src/or/addressmap.c b/src/or/addressmap.c index c92af38254..f278564e80 100644 --- a/src/or/addressmap.c +++ b/src/or/addressmap.c @@ -214,7 +214,7 @@ addressmap_clear_excluded_trackexithosts(const or_options_t *options) dot--; if (*dot == '.') dot++; nodename = tor_strndup(dot, len-5-(dot-target));; - node = node_get_by_nickname(nodename, 0); + node = node_get_by_nickname(nodename, NNF_NO_WARN_UNNAMED); tor_free(nodename); if (!node || (allow_nodes && !routerset_contains_node(allow_nodes, node)) || diff --git a/src/or/circuituse.c b/src/or/circuituse.c index 21cc9c540f..240e252934 100644 --- a/src/or/circuituse.c +++ b/src/or/circuituse.c @@ -2119,7 +2119,7 @@ circuit_get_open_circ_or_launch(entry_connection_t *conn, } else { /* XXXX Duplicates checks in connection_ap_handshake_attach_circuit: * refactor into a single function. */ - const node_t *node = node_get_by_nickname(conn->chosen_exit_name, 1); + const node_t *node = node_get_by_nickname(conn->chosen_exit_name, 0); int opt = conn->chosen_exit_optional; if (node && !connection_ap_can_use_exit(conn, node)) { log_fn(opt ? LOG_INFO : LOG_WARN, LD_APP, @@ -2199,7 +2199,7 @@ circuit_get_open_circ_or_launch(entry_connection_t *conn, if (conn->chosen_exit_name) { const node_t *r; int opt = conn->chosen_exit_optional; - r = node_get_by_nickname(conn->chosen_exit_name, 1); + r = node_get_by_nickname(conn->chosen_exit_name, 0); if (r && node_has_descriptor(r)) { /* We might want to connect to an IPv6 bridge for loading descriptors so we use the preferred address rather than @@ -2598,7 +2598,7 @@ connection_ap_handshake_attach_circuit(entry_connection_t *conn) * open to that exit. See what exit we meant, and whether we can use it. */ if (conn->chosen_exit_name) { - const node_t *node = node_get_by_nickname(conn->chosen_exit_name, 1); + const node_t *node = node_get_by_nickname(conn->chosen_exit_name, 0); int opt = conn->chosen_exit_optional; if (!node && !want_onehop) { /* We ran into this warning when trying to extend a circuit to a diff --git a/src/or/connection_edge.c b/src/or/connection_edge.c index 12ddc7e829..c42adbf731 100644 --- a/src/or/connection_edge.c +++ b/src/or/connection_edge.c @@ -1074,7 +1074,8 @@ circuit_discard_optional_exit_enclaves(extend_info_t *info) if (!entry_conn->chosen_exit_optional && !entry_conn->chosen_exit_retries) continue; - r1 = node_get_by_nickname(entry_conn->chosen_exit_name, 0); + r1 = node_get_by_nickname(entry_conn->chosen_exit_name, + NNF_NO_WARN_UNNAMED); r2 = node_get_by_id(info->identity_digest); if (!r1 || !r2 || r1 != r2) continue; @@ -1508,7 +1509,7 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn, if (s[1] != '\0') { /* Looks like a real .exit one. */ conn->chosen_exit_name = tor_strdup(s+1); - node = node_get_by_nickname(conn->chosen_exit_name, 1); + node = node_get_by_nickname(conn->chosen_exit_name, 0); if (exit_source == ADDRMAPSRC_TRACKEXIT) { /* We 5 tries before it expires the addressmap */ @@ -1529,7 +1530,7 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn, * form that means (foo's address).foo.exit. */ conn->chosen_exit_name = tor_strdup(socks->address); - node = node_get_by_nickname(conn->chosen_exit_name, 1); + node = node_get_by_nickname(conn->chosen_exit_name, 0); if (node) { *socks->address = 0; node_get_address_string(node, socks->address, sizeof(socks->address)); @@ -3630,7 +3631,7 @@ connection_ap_can_use_exit(const entry_connection_t *conn, */ if (conn->chosen_exit_name) { const node_t *chosen_exit = - node_get_by_nickname(conn->chosen_exit_name, 1); + node_get_by_nickname(conn->chosen_exit_name, 0); if (!chosen_exit || tor_memneq(chosen_exit->identity, exit_node->identity, DIGEST_LEN)) { /* doesn't match */ diff --git a/src/or/control.c b/src/or/control.c index 724d4b35c0..889436c679 100644 --- a/src/or/control.c +++ b/src/or/control.c @@ -1886,7 +1886,7 @@ getinfo_helper_dir(control_connection_t *control_conn, (void) control_conn; if (!strcmpstart(question, "desc/id/")) { const routerinfo_t *ri = NULL; - const node_t *node = node_get_by_hex_id(question+strlen("desc/id/")); + const node_t *node = node_get_by_hex_id(question+strlen("desc/id/"), 0); if (node) ri = node->ri; if (ri) { @@ -1905,7 +1905,7 @@ getinfo_helper_dir(control_connection_t *control_conn, /* XXX Setting 'warn_if_unnamed' here is a bit silly -- the * warning goes to the user, not to the controller. */ const node_t *node = - node_get_by_nickname(question+strlen("desc/name/"), 1); + node_get_by_nickname(question+strlen("desc/name/"), 0); if (node) ri = node->ri; if (ri) { @@ -1991,7 +1991,7 @@ getinfo_helper_dir(control_connection_t *control_conn, return -1; } } else if (!strcmpstart(question, "md/id/")) { - const node_t *node = node_get_by_hex_id(question+strlen("md/id/")); + const node_t *node = node_get_by_hex_id(question+strlen("md/id/"), 0); const microdesc_t *md = NULL; if (node) md = node->md; if (md && md->body) { @@ -2000,7 +2000,7 @@ getinfo_helper_dir(control_connection_t *control_conn, } else if (!strcmpstart(question, "md/name/")) { /* XXX Setting 'warn_if_unnamed' here is a bit silly -- the * warning goes to the user, not to the controller. */ - const node_t *node = node_get_by_nickname(question+strlen("md/name/"), 1); + const node_t *node = node_get_by_nickname(question+strlen("md/name/"), 0); /* XXXX duplicated code */ const microdesc_t *md = NULL; if (node) md = node->md; @@ -2013,7 +2013,7 @@ getinfo_helper_dir(control_connection_t *control_conn, } else if (!strcmpstart(question, "desc-annotations/id/")) { const routerinfo_t *ri = NULL; const node_t *node = - node_get_by_hex_id(question+strlen("desc-annotations/id/")); + node_get_by_hex_id(question+strlen("desc-annotations/id/"), 0); if (node) ri = node->ri; if (ri) { @@ -3394,7 +3394,7 @@ handle_control_extendcircuit(control_connection_t *conn, uint32_t len, nodes = smartlist_new(); SMARTLIST_FOREACH_BEGIN(router_nicknames, const char *, n) { - const node_t *node = node_get_by_nickname(n, 1); + const node_t *node = node_get_by_nickname(n, 0); if (!node) { connection_printf_to_buf(conn, "552 No such router \"%s\"\r\n", n); goto done; @@ -4158,7 +4158,7 @@ handle_control_hsfetch(control_connection_t *conn, uint32_t len, const char *server; server = arg + strlen(opt_server); - node = node_get_by_hex_id(server); + node = node_get_by_hex_id(server, 0); if (!node) { connection_printf_to_buf(conn, "552 Server \"%s\" not found\r\n", server); @@ -4239,7 +4239,7 @@ handle_control_hspost(control_connection_t *conn, SMARTLIST_FOREACH_BEGIN(args, const char *, arg) { if (!strcasecmpstart(arg, opt_server)) { const char *server = arg + strlen(opt_server); - const node_t *node = node_get_by_hex_id(server); + const node_t *node = node_get_by_hex_id(server, 0); if (!node || !node->rs) { connection_printf_to_buf(conn, "552 Server \"%s\" not found\r\n", diff --git a/src/or/networkstatus.c b/src/or/networkstatus.c index 69bff55cff..6263be36c3 100644 --- a/src/or/networkstatus.c +++ b/src/or/networkstatus.c @@ -794,21 +794,6 @@ router_get_consensus_status_by_id(const char *digest) return router_get_mutable_consensus_status_by_id(digest); } -/** Given a nickname (possibly verbose, possibly a hexadecimal digest), return - * the corresponding routerstatus_t, or NULL if none exists. Warn the - * user if warn_if_unnamed is set, and they have specified a router by - * nickname, but the Named flag isn't set for that router. */ -const routerstatus_t * -router_get_consensus_status_by_nickname(const char *nickname, - int warn_if_unnamed) -{ - const node_t *node = node_get_by_nickname(nickname, warn_if_unnamed); - if (node) - return node->rs; - else - return NULL; -} - /** Return the identity digest that's mapped to officially by * nickname. */ const char * @@ -2555,7 +2540,8 @@ getinfo_helper_networkstatus(control_connection_t *conn, } status = router_get_consensus_status_by_id(d); } else if (!strcmpstart(question, "ns/name/")) { - status = router_get_consensus_status_by_nickname(question+8, 0); + const node_t *n = node_get_by_nickname(question+8, 0); + status = n ? n->rs : NULL; } else if (!strcmpstart(question, "ns/purpose/")) { *answer = networkstatus_getinfo_by_purpose(question+11, time(NULL)); return *answer ? 0 : -1; diff --git a/src/or/networkstatus.h b/src/or/networkstatus.h index f9320747d2..145be37d12 100644 --- a/src/or/networkstatus.h +++ b/src/or/networkstatus.h @@ -62,9 +62,6 @@ const routerstatus_t *router_get_consensus_status_by_descriptor_digest( MOCK_DECL(routerstatus_t *, router_get_mutable_consensus_status_by_descriptor_digest, (networkstatus_t *consensus, const char *digest)); -const routerstatus_t *router_get_consensus_status_by_nickname( - const char *nickname, - int warn_if_unnamed); const char *networkstatus_get_router_digest_by_nickname(const char *nickname); int networkstatus_nickname_is_unnamed(const char *nickname); int we_want_to_fetch_flavor(const or_options_t *options, int flavor); diff --git a/src/or/nodelist.c b/src/or/nodelist.c index b1cb8d6b5d..f7cae92818 100644 --- a/src/or/nodelist.c +++ b/src/or/nodelist.c @@ -762,14 +762,16 @@ nodelist_get_list,(void)) /** Given a hex-encoded nickname of the format DIGEST, $DIGEST, $DIGEST=name, * or $DIGEST~name, return the node with the matching identity digest and * nickname (if any). Return NULL if no such node exists, or if hex_id - * is not well-formed. */ + * is not well-formed. DOCDOC flags */ const node_t * -node_get_by_hex_id(const char *hex_id) +node_get_by_hex_id(const char *hex_id, unsigned flags) { char digest_buf[DIGEST_LEN]; char nn_buf[MAX_NICKNAME_LEN+1]; char nn_char='\0'; + (void) flags; // XXXX + if (hex_digest_nickname_decode(hex_id, digest_buf, &nn_char, nn_buf)==0) { const node_t *node = node_get_by_id(digest_buf); if (!node) @@ -785,19 +787,21 @@ node_get_by_hex_id(const char *hex_id) } /** Given a nickname (possibly verbose, possibly a hexadecimal digest), return - * the corresponding node_t, or NULL if none exists. Warn the user if - * warn_if_unnamed is set, and they have specified a router by - * nickname, but the Named flag isn't set for that router. */ + * the corresponding node_t, or NULL if none exists. Warn the user if they + * have specified a router by nickname, unless the NNF_NO_WARN_UNNAMED bit is + * set in flags. */ MOCK_IMPL(const node_t *, -node_get_by_nickname,(const char *nickname, int warn_if_unnamed)) +node_get_by_nickname,(const char *nickname, unsigned flags)) { + const int warn_if_unnamed = !(flags & NNF_NO_WARN_UNNAMED); + if (!the_nodelist) return NULL; /* Handle these cases: DIGEST, $DIGEST, $DIGEST=name, $DIGEST~name. */ { const node_t *node; - if ((node = node_get_by_hex_id(nickname)) != NULL) + if ((node = node_get_by_hex_id(nickname, flags)) != NULL) return node; } @@ -1719,7 +1723,7 @@ nodelist_add_node_and_family(smartlist_t *sl, const node_t *node) SMARTLIST_FOREACH_BEGIN(declared_family, const char *, name) { const node_t *node2; const smartlist_t *family2; - if (!(node2 = node_get_by_nickname(name, 0))) + if (!(node2 = node_get_by_nickname(name, NNF_NO_WARN_UNNAMED))) continue; if (!(family2 = node_get_declared_family(node2))) continue; diff --git a/src/or/nodelist.h b/src/or/nodelist.h index c5805c55f1..79ff720c94 100644 --- a/src/or/nodelist.h +++ b/src/or/nodelist.h @@ -21,7 +21,11 @@ MOCK_DECL(const node_t *, node_get_by_id, (const char *identity_digest)); node_t *node_get_mutable_by_ed25519_id(const ed25519_public_key_t *ed_id); MOCK_DECL(const node_t *, node_get_by_ed25519_id, (const ed25519_public_key_t *ed_id)); -const node_t *node_get_by_hex_id(const char *identity_digest); + +#define NNF_NO_WARN_UNNAMED (1u<<0) + +const node_t *node_get_by_hex_id(const char *identity_digest, + unsigned flags); node_t *nodelist_set_routerinfo(routerinfo_t *ri, routerinfo_t **ri_old_out); node_t *nodelist_add_microdesc(microdesc_t *md); void nodelist_set_consensus(networkstatus_t *ns); @@ -35,7 +39,7 @@ void nodelist_free_all(void); void nodelist_assert_ok(void); MOCK_DECL(const node_t *, node_get_by_nickname, - (const char *nickname, int warn_if_unnamed)); + (const char *nickname, unsigned flags)); void node_get_verbose_nickname(const node_t *node, char *verbose_name_out); void node_get_verbose_nickname_by_id(const char *id_digest, diff --git a/src/or/rendservice.c b/src/or/rendservice.c index a205b00c6b..925373883a 100644 --- a/src/or/rendservice.c +++ b/src/or/rendservice.c @@ -2112,7 +2112,7 @@ find_rp_for_intro(const rend_intro_cell_t *intro, if (intro->version == 0 || intro->version == 1) { rp_nickname = (const char *)(intro->u.v0_v1.rp); - node = node_get_by_nickname(rp_nickname, 0); + node = node_get_by_nickname(rp_nickname, NNF_NO_WARN_UNNAMED); if (!node) { if (err_msg_out) { tor_asprintf(&err_msg, diff --git a/src/or/router.c b/src/or/router.c index 7fad572657..8629168c87 100644 --- a/src/or/router.c +++ b/src/or/router.c @@ -2279,7 +2279,7 @@ router_build_fresh_descriptor(routerinfo_t **r, extrainfo_t **e) if (!strcasecmp(name, options->Nickname)) continue; /* Don't list ourself, that's redundant */ else - member = node_get_by_nickname(name, 1); + member = node_get_by_nickname(name, 0); if (!member) { int is_legal = is_legal_nickname_or_hexdigest(name); if (!smartlist_contains_string(warned_nonexistent_family, name) && diff --git a/src/or/routerset.c b/src/or/routerset.c index 4906c6a51d..54e26ef943 100644 --- a/src/or/routerset.c +++ b/src/or/routerset.c @@ -362,7 +362,7 @@ routerset_get_all_nodes(smartlist_t *out, const routerset_t *routerset, /* No routers are specified by type; all are given by name or digest. * we can do a lookup in O(len(routerset)). */ SMARTLIST_FOREACH(routerset->list, const char *, name, { - const node_t *node = node_get_by_nickname(name, 1); + const node_t *node = node_get_by_nickname(name, 0); if (node) { if (!running_only || node->is_running) if (!routerset_contains_node(excludeset, node)) diff --git a/src/test/test_routerset.c b/src/test/test_routerset.c index 7efd042ed5..c9c69911da 100644 --- a/src/test/test_routerset.c +++ b/src/test/test_routerset.c @@ -1602,7 +1602,7 @@ NS(test_main)(void *arg) */ NS_DECL(const node_t *, node_get_by_nickname, - (const char *nickname, int warn_if_unused)); + (const char *nickname, unsigned flags)); static const char *NS(mock_nickname); static void @@ -1632,11 +1632,11 @@ NS(test_main)(void *arg) } const node_t * -NS(node_get_by_nickname)(const char *nickname, int warn_if_unused) +NS(node_get_by_nickname)(const char *nickname, unsigned flags) { CALLED(node_get_by_nickname)++; tt_str_op(nickname, OP_EQ, NS(mock_nickname)); - tt_int_op(warn_if_unused, OP_EQ, 1); + tt_uint_op(flags, OP_EQ, 0); done: return NULL; @@ -1651,7 +1651,7 @@ NS(node_get_by_nickname)(const char *nickname, int warn_if_unused) */ NS_DECL(const node_t *, node_get_by_nickname, - (const char *nickname, int warn_if_unused)); + (const char *nickname, unsigned flags)); static const char *NS(mock_nickname); static node_t NS(mock_node); @@ -1683,11 +1683,11 @@ NS(test_main)(void *arg) } const node_t * -NS(node_get_by_nickname)(const char *nickname, int warn_if_unused) +NS(node_get_by_nickname)(const char *nickname, unsigned flags) { CALLED(node_get_by_nickname)++; tt_str_op(nickname, OP_EQ, NS(mock_nickname)); - tt_int_op(warn_if_unused, OP_EQ, 1); + tt_int_op(flags, OP_EQ, 0); done: return &NS(mock_node); @@ -1701,7 +1701,7 @@ NS(node_get_by_nickname)(const char *nickname, int warn_if_unused) */ NS_DECL(const node_t *, node_get_by_nickname, - (const char *nickname, int warn_if_unused)); + (const char *nickname, unsigned flags)); static char *NS(mock_nickname); static node_t NS(mock_node); @@ -1735,11 +1735,11 @@ NS(test_main)(void *arg) } const node_t * -NS(node_get_by_nickname)(const char *nickname, int warn_if_unused) +NS(node_get_by_nickname)(const char *nickname, unsigned flags) { CALLED(node_get_by_nickname)++; tt_str_op(nickname, OP_EQ, NS(mock_nickname)); - tt_int_op(warn_if_unused, OP_EQ, 1); + tt_int_op(flags, OP_EQ, 0); done: return &NS(mock_node);