diff options
Diffstat (limited to 'src/feature/nodelist')
46 files changed, 2386 insertions, 927 deletions
diff --git a/src/feature/nodelist/.may_include b/src/feature/nodelist/.may_include new file mode 100644 index 0000000000..424c745c12 --- /dev/null +++ b/src/feature/nodelist/.may_include @@ -0,0 +1 @@ +*.h diff --git a/src/feature/nodelist/authcert.c b/src/feature/nodelist/authcert.c index 7a065662a7..c5b31be9e3 100644 --- a/src/feature/nodelist/authcert.c +++ b/src/feature/nodelist/authcert.c @@ -1,7 +1,7 @@ /* Copyright (c) 2001 Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2019, The Tor Project, Inc. */ + * Copyright (c) 2007-2020, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -46,7 +46,7 @@ #include "feature/nodelist/networkstatus_voter_info_st.h" #include "feature/nodelist/node_st.h" -DECLARE_TYPED_DIGESTMAP_FNS(dsmap_, digest_ds_map_t, download_status_t) +DECLARE_TYPED_DIGESTMAP_FNS(dsmap, digest_ds_map_t, download_status_t) #define DSMAP_FOREACH(map, keyvar, valvar) \ DIGESTMAP_FOREACH(dsmap_to_digestmap(map), keyvar, download_status_t *, \ valvar) @@ -380,7 +380,8 @@ trusted_dirs_load_certs_from_string(const char *contents, int source, int added_trusted_cert = 0; for (s = contents; *s; s = eos) { - authority_cert_t *cert = authority_cert_parse_from_string(s, &eos); + authority_cert_t *cert = authority_cert_parse_from_string(s, strlen(s), + &eos); cert_list_t *cl; if (!cert) { failure_code = -1; @@ -459,17 +460,15 @@ trusted_dirs_load_certs_from_string(const char *contents, int source, if (ds && cert->cache_info.published_on > ds->addr_current_at) { /* Check to see whether we should update our view of the authority's * address. */ - if (cert->addr && cert->dir_port && - (ds->addr != cert->addr || - ds->dir_port != cert->dir_port)) { - char *a = tor_dup_ip(cert->addr); + if (!tor_addr_is_null(&cert->ipv4_addr) && cert->ipv4_dirport && + (!tor_addr_eq(&ds->ipv4_addr, &cert->ipv4_addr) || + ds->ipv4_dirport != cert->ipv4_dirport)) { log_notice(LD_DIR, "Updating address for directory authority %s " - "from %s:%d to %s:%d based on certificate.", - ds->nickname, ds->address, (int)ds->dir_port, - a, cert->dir_port); - tor_free(a); - ds->addr = cert->addr; - ds->dir_port = cert->dir_port; + "from %s:%"PRIu16" to %s:%"PRIu16" based on certificate.", + ds->nickname, ds->address, ds->ipv4_dirport, + fmt_addr(&cert->ipv4_addr), cert->ipv4_dirport); + tor_addr_copy(&ds->ipv4_addr, &cert->ipv4_addr); + ds->ipv4_dirport = cert->ipv4_dirport; } ds->addr_current_at = cert->cache_info.published_on; } @@ -742,7 +741,7 @@ static const char *BAD_SIGNING_KEYS[] = { * which, because of the old openssl heartbleed vulnerability, should * never be trusted. */ int -authority_cert_is_blacklisted(const authority_cert_t *cert) +authority_cert_is_denylisted(const authority_cert_t *cert) { char hex_digest[HEX_DIGEST_LEN+1]; int i; @@ -809,7 +808,7 @@ authority_certs_fetch_resource_impl(const char *resource, /* clients always make OR connections to bridges */ tor_addr_port_t or_ap; /* we are willing to use a non-preferred address if we need to */ - fascist_firewall_choose_address_node(node, FIREWALL_OR_CONNECTION, 0, + reachable_addr_choose_from_node(node, FIREWALL_OR_CONNECTION, 0, &or_ap); req = directory_request_new(DIR_PURPOSE_FETCH_CERTIFICATE); diff --git a/src/feature/nodelist/authcert.h b/src/feature/nodelist/authcert.h index 2effdb06e6..4c3d79ceed 100644 --- a/src/feature/nodelist/authcert.h +++ b/src/feature/nodelist/authcert.h @@ -1,6 +1,6 @@ /* Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2019, The Tor Project, Inc. */ + * Copyright (c) 2007-2020, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -41,7 +41,7 @@ void authority_cert_dl_failed(const char *id_digest, void authority_certs_fetch_missing(networkstatus_t *status, time_t now, const char *dir_hint); int authority_cert_dl_looks_uncertain(const char *id_digest); -int authority_cert_is_blacklisted(const authority_cert_t *cert); +int authority_cert_is_denylisted(const authority_cert_t *cert); void authority_cert_free_(authority_cert_t *cert); #define authority_cert_free(cert) \ @@ -57,4 +57,4 @@ MOCK_DECL(download_status_t *, download_status_for_authority_id_and_sk, void authcert_free_all(void); -#endif +#endif /* !defined(TOR_AUTHCERT_H) */ diff --git a/src/feature/nodelist/authority_cert_st.h b/src/feature/nodelist/authority_cert_st.h index 68a84bc452..aa9831d12e 100644 --- a/src/feature/nodelist/authority_cert_st.h +++ b/src/feature/nodelist/authority_cert_st.h @@ -1,9 +1,14 @@ /* Copyright (c) 2001 Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2019, The Tor Project, Inc. */ + * Copyright (c) 2007-2020, The Tor Project, Inc. */ /* See LICENSE for licensing information */ +/** + * @file authority_cert_st.h + * @brief Authority certificate structure. + **/ + #ifndef AUTHORITY_CERT_ST_H #define AUTHORITY_CERT_ST_H @@ -22,11 +27,10 @@ struct authority_cert_t { char signing_key_digest[DIGEST_LEN]; /** The listed expiration time of this certificate. */ time_t expires; - /** This authority's IPv4 address, in host order. */ - uint32_t addr; + /** This authority's IPv4 address. */ + tor_addr_t ipv4_addr; /** This authority's directory port. */ - uint16_t dir_port; + uint16_t ipv4_dirport; }; -#endif - +#endif /* !defined(AUTHORITY_CERT_ST_H) */ diff --git a/src/feature/nodelist/desc_store_st.h b/src/feature/nodelist/desc_store_st.h index b04a1abc7d..5f35a490a5 100644 --- a/src/feature/nodelist/desc_store_st.h +++ b/src/feature/nodelist/desc_store_st.h @@ -1,9 +1,14 @@ /* Copyright (c) 2001 Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2019, The Tor Project, Inc. */ + * Copyright (c) 2007-2020, The Tor Project, Inc. */ /* See LICENSE for licensing information */ +/** + * @file desc_store_st.h + * @brief Routerinfo/extrainfo storage structure. + **/ + #ifndef DESC_STORE_ST_H #define DESC_STORE_ST_H @@ -36,4 +41,4 @@ struct desc_store_t { size_t bytes_dropped; }; -#endif +#endif /* !defined(DESC_STORE_ST_H) */ diff --git a/src/feature/nodelist/describe.c b/src/feature/nodelist/describe.c index 5c376408c0..b6a0fe74f7 100644 --- a/src/feature/nodelist/describe.c +++ b/src/feature/nodelist/describe.c @@ -1,7 +1,7 @@ /* Copyright (c) 2001 Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2019, The Tor Project, Inc. */ + * Copyright (c) 2007-2020, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -9,66 +9,122 @@ * \brief Format short descriptions of relays. */ +#define DESCRIBE_PRIVATE + #include "core/or/or.h" +#include "core/or/extendinfo.h" #include "feature/nodelist/describe.h" +#include "feature/nodelist/nodelist.h" #include "feature/nodelist/routerinfo.h" +#include "lib/crypt_ops/crypto_ed25519.h" +#include "lib/crypt_ops/crypto_format.h" #include "core/or/extend_info_st.h" #include "feature/nodelist/node_st.h" #include "feature/nodelist/routerinfo_st.h" #include "feature/nodelist/routerstatus_st.h" - -/** - * Longest allowed output of format_node_description, plus 1 character for - * NUL. This allows space for: - * "$FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF~xxxxxxxxxxxxxxxxxxx at" - * " [ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255]" - * plus a terminating NUL. - */ -#define NODE_DESC_BUF_LEN (MAX_VERBOSE_NICKNAME_LEN+4+TOR_ADDR_BUF_LEN) +#include "feature/nodelist/microdesc_st.h" /** Use <b>buf</b> (which must be at least NODE_DESC_BUF_LEN bytes long) to * hold a human-readable description of a node with identity digest - * <b>id_digest</b>, named-status <b>is_named</b>, nickname <b>nickname</b>, - * and address <b>addr</b> or <b>addr32h</b>. + * <b>id_digest</b>, nickname <b>nickname</b>, and addresses <b>addr32h</b> and + * <b>addr</b>. * - * The <b>nickname</b> and <b>addr</b> fields are optional and may be set to - * NULL. The <b>addr32h</b> field is optional and may be set to 0. + * The <b>nickname</b>, <b>ipv6_addr</b> and <b>ipv4_addr</b> fields are + * optional and may be set to NULL or the null address. * * Return a pointer to the front of <b>buf</b>. + * If buf is NULL, return a string constant describing the error. */ -static const char * +STATIC const char * format_node_description(char *buf, - const char *id_digest, - int is_named, + const char *rsa_id_digest, + const ed25519_public_key_t *ed25519_id, const char *nickname, - const tor_addr_t *addr, - uint32_t addr32h) + const tor_addr_t *ipv4_addr, + const tor_addr_t *ipv6_addr) { - char *cp; + size_t rv = 0; + bool has_ipv6 = ipv6_addr && !tor_addr_is_null(ipv6_addr); + bool valid_ipv4 = false; if (!buf) return "<NULL BUFFER>"; - buf[0] = '$'; - base16_encode(buf+1, HEX_DIGEST_LEN+1, id_digest, DIGEST_LEN); - cp = buf+1+HEX_DIGEST_LEN; + memset(buf, 0, NODE_DESC_BUF_LEN); + + if (!rsa_id_digest) { + /* strlcpy() returns the length of the source string it attempted to copy, + * ignoring any required truncation due to the buffer length. */ + rv = strlcpy(buf, "<NULL ID DIGEST>", NODE_DESC_BUF_LEN); + tor_assert_nonfatal(rv < NODE_DESC_BUF_LEN); + return buf; + } + + /* strlcat() returns the length of the concatenated string it attempted to + * create, ignoring any required truncation due to the buffer length. */ + rv = strlcat(buf, "$", NODE_DESC_BUF_LEN); + tor_assert_nonfatal(rv < NODE_DESC_BUF_LEN); + + { + char hex_digest[HEX_DIGEST_LEN+1]; + memset(hex_digest, 0, sizeof(hex_digest)); + + base16_encode(hex_digest, sizeof(hex_digest), + rsa_id_digest, DIGEST_LEN); + rv = strlcat(buf, hex_digest, NODE_DESC_BUF_LEN); + tor_assert_nonfatal(rv < NODE_DESC_BUF_LEN); + } + if (nickname) { - buf[1+HEX_DIGEST_LEN] = is_named ? '=' : '~'; - strlcpy(buf+1+HEX_DIGEST_LEN+1, nickname, MAX_NICKNAME_LEN+1); - cp += strlen(cp); + rv = strlcat(buf, "~", NODE_DESC_BUF_LEN); + tor_assert_nonfatal(rv < NODE_DESC_BUF_LEN); + rv = strlcat(buf, nickname, NODE_DESC_BUF_LEN); + tor_assert_nonfatal(rv < NODE_DESC_BUF_LEN); } - if (addr32h || addr) { - memcpy(cp, " at ", 4); - cp += 4; - if (addr) { - tor_addr_to_str(cp, addr, TOR_ADDR_BUF_LEN, 0); - } else { - struct in_addr in; - in.s_addr = htonl(addr32h); - tor_inet_ntoa(&in, cp, INET_NTOA_BUF_LEN); + if (ed25519_id) { + char ed_base64[ED25519_BASE64_LEN+1]; + ed25519_public_to_base64(ed_base64, ed25519_id); + rv = strlcat(buf, " [", NODE_DESC_BUF_LEN); + tor_assert_nonfatal(rv < NODE_DESC_BUF_LEN); + rv = strlcat(buf, ed_base64, NODE_DESC_BUF_LEN); + tor_assert_nonfatal(rv < NODE_DESC_BUF_LEN); + rv = strlcat(buf, "]", NODE_DESC_BUF_LEN); + tor_assert_nonfatal(rv < NODE_DESC_BUF_LEN); + } + if (ipv4_addr || has_ipv6) { + rv = strlcat(buf, " at ", NODE_DESC_BUF_LEN); + tor_assert_nonfatal(rv < NODE_DESC_BUF_LEN); + } + if (ipv4_addr) { + const char *str_rv = NULL; + char addr_str[TOR_ADDR_BUF_LEN]; + memset(addr_str, 0, sizeof(addr_str)); + + str_rv = tor_addr_to_str(addr_str, ipv4_addr, sizeof(addr_str), 0); + if (str_rv) { + rv = strlcat(buf, addr_str, NODE_DESC_BUF_LEN); + tor_assert_nonfatal(rv < NODE_DESC_BUF_LEN); + valid_ipv4 = true; + } + } + /* Both addresses are valid */ + if (valid_ipv4 && has_ipv6) { + rv = strlcat(buf, " and ", NODE_DESC_BUF_LEN); + tor_assert_nonfatal(rv < NODE_DESC_BUF_LEN); + } + if (has_ipv6) { + const char *str_rv = NULL; + char addr_str[TOR_ADDR_BUF_LEN]; + memset(addr_str, 0, sizeof(addr_str)); + + str_rv = tor_addr_to_str(addr_str, ipv6_addr, sizeof(addr_str), 1); + if (str_rv) { + rv = strlcat(buf, addr_str, NODE_DESC_BUF_LEN); + tor_assert_nonfatal(rv < NODE_DESC_BUF_LEN); } } + return buf; } @@ -84,12 +140,15 @@ router_describe(const routerinfo_t *ri) if (!ri) return "<null>"; + + const ed25519_public_key_t *ed25519_id = routerinfo_get_ed25519_id(ri); + return format_node_description(buf, ri->cache_info.identity_digest, - 0, + ed25519_id, ri->nickname, - NULL, - ri->addr); + &ri->ipv4_addr, + &ri->ipv6_addr); } /** Return a human-readable description of the node_t <b>node</b>. @@ -102,27 +161,37 @@ node_describe(const node_t *node) { static char buf[NODE_DESC_BUF_LEN]; const char *nickname = NULL; - uint32_t addr32h = 0; - int is_named = 0; + const tor_addr_t *ipv6_addr = NULL, *ipv4_addr = NULL; if (!node) return "<null>"; if (node->rs) { nickname = node->rs->nickname; - is_named = node->rs->is_named; - addr32h = node->rs->addr; + ipv4_addr = &node->rs->ipv4_addr; + ipv6_addr = &node->rs->ipv6_addr; + /* Support consensus versions less than 28, when IPv6 addresses were in + * microdescs. This code can be removed when 0.2.9 is no longer supported, + * and the MIN_METHOD_FOR_NO_A_LINES_IN_MICRODESC macro is removed. */ + if (node->md && tor_addr_is_null(ipv6_addr)) { + ipv6_addr = &node->md->ipv6_addr; + } } else if (node->ri) { nickname = node->ri->nickname; - addr32h = node->ri->addr; + ipv4_addr = &node->ri->ipv4_addr; + ipv6_addr = &node->ri->ipv6_addr; + } else { + return "<null rs and ri>"; } + const ed25519_public_key_t *ed25519_id = node_get_ed25519_id(node); + return format_node_description(buf, node->identity, - is_named, + ed25519_id, nickname, - NULL, - addr32h); + ipv4_addr, + ipv6_addr); } /** Return a human-readable description of the routerstatus_t <b>rs</b>. @@ -137,12 +206,13 @@ routerstatus_describe(const routerstatus_t *rs) if (!rs) return "<null>"; + return format_node_description(buf, rs->identity_digest, - rs->is_named, - rs->nickname, NULL, - rs->addr); + rs->nickname, + &rs->ipv4_addr, + &rs->ipv6_addr); } /** Return a human-readable description of the extend_info_t <b>ei</b>. @@ -157,12 +227,22 @@ extend_info_describe(const extend_info_t *ei) if (!ei) return "<null>"; + + const tor_addr_port_t *ap4 = extend_info_get_orport(ei, AF_INET); + const tor_addr_port_t *ap6 = extend_info_get_orport(ei, AF_INET6); + const tor_addr_t *addr4 = ap4 ? &ap4->addr : NULL; + const tor_addr_t *addr6 = ap6 ? &ap6->addr : NULL; + + const ed25519_public_key_t *ed25519_id = &ei->ed_identity; + if (ed25519_public_key_is_zero(ed25519_id)) + ed25519_id = NULL; + return format_node_description(buf, ei->identity_digest, - 0, + ed25519_id, ei->nickname, - &ei->addr, - 0); + addr4, + addr6); } /** Set <b>buf</b> (which must have MAX_VERBOSE_NICKNAME_LEN+1 bytes) to the @@ -175,9 +255,39 @@ extend_info_describe(const extend_info_t *ei) void router_get_verbose_nickname(char *buf, const routerinfo_t *router) { - buf[0] = '$'; - base16_encode(buf+1, HEX_DIGEST_LEN+1, router->cache_info.identity_digest, - DIGEST_LEN); - buf[1+HEX_DIGEST_LEN] = '~'; - strlcpy(buf+1+HEX_DIGEST_LEN+1, router->nickname, MAX_NICKNAME_LEN+1); + size_t rv = 0; + + if (!buf) + return; + + memset(buf, 0, MAX_VERBOSE_NICKNAME_LEN+1); + + if (!router) { + /* strlcpy() returns the length of the source string it attempted to copy, + * ignoring any required truncation due to the buffer length. */ + rv = strlcpy(buf, "<null>", MAX_VERBOSE_NICKNAME_LEN+1); + tor_assert_nonfatal(rv < MAX_VERBOSE_NICKNAME_LEN+1); + return; + } + + /* strlcat() returns the length of the concatenated string it attempted to + * create, ignoring any required truncation due to the buffer length. */ + rv = strlcat(buf, "$", MAX_VERBOSE_NICKNAME_LEN+1); + tor_assert_nonfatal(rv < MAX_VERBOSE_NICKNAME_LEN+1); + + { + char hex_digest[HEX_DIGEST_LEN+1]; + memset(hex_digest, 0, sizeof(hex_digest)); + + base16_encode(hex_digest, sizeof(hex_digest), + router->cache_info.identity_digest, DIGEST_LEN); + rv = strlcat(buf, hex_digest, MAX_VERBOSE_NICKNAME_LEN+1); + tor_assert_nonfatal(rv < MAX_VERBOSE_NICKNAME_LEN+1); + } + + rv = strlcat(buf, "~", MAX_VERBOSE_NICKNAME_LEN+1); + tor_assert_nonfatal(rv < MAX_VERBOSE_NICKNAME_LEN+1); + + rv = strlcat(buf, router->nickname, MAX_VERBOSE_NICKNAME_LEN+1); + tor_assert_nonfatal(rv < MAX_VERBOSE_NICKNAME_LEN+1); } diff --git a/src/feature/nodelist/describe.h b/src/feature/nodelist/describe.h index 018af6470e..898b5c943b 100644 --- a/src/feature/nodelist/describe.h +++ b/src/feature/nodelist/describe.h @@ -1,7 +1,7 @@ /* Copyright (c) 2001 Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2019, The Tor Project, Inc. */ + * Copyright (c) 2007-2020, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -22,4 +22,42 @@ const char *node_describe(const struct node_t *node); const char *router_describe(const struct routerinfo_t *ri); const char *routerstatus_describe(const struct routerstatus_t *ri); -#endif +void router_get_verbose_nickname(char *buf, const routerinfo_t *router); + +#if defined(DESCRIBE_PRIVATE) || defined(TOR_UNIT_TESTS) + +/** + * Longest allowed output for an IPv4 address "255.255.255.255", with NO + * terminating NUL. + */ +#define IPV4_BUF_LEN_NO_NUL 15 + +/** + * Longest allowed output of format_node_description, plus 1 character for + * NUL. This allows space for: + * "$FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF~xxxxxxxxxxxxxxxxxxx " + * "[+++++++++++++++++++++++++++++++++++++++++++] at" + * " 255.255.255.255 and [ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255]" + * plus a terminating NUL. + */ +#define NODE_DESC_BUF_LEN \ + (MAX_VERBOSE_NICKNAME_LEN+4 \ + + ED25519_BASE64_LEN+3 \ + + IPV4_BUF_LEN_NO_NUL+5 \ + + TOR_ADDR_BUF_LEN) + +#endif /* defined(DESCRIBE_PRIVATE) || defined(TOR_UNIT_TESTS) */ + +#ifdef TOR_UNIT_TESTS +struct ed25519_public_key_t; + +STATIC const char *format_node_description(char *buf, + const char *rsa_id_digest, + const struct ed25519_public_key_t *ed25519_id, + const char *nickname, + const tor_addr_t *ipv4_addr, + const tor_addr_t *ipv6_addr); + +#endif /* defined(TOR_UNIT_TESTS) */ + +#endif /* !defined(TOR_DESCRIBE_H) */ diff --git a/src/feature/nodelist/dirlist.c b/src/feature/nodelist/dirlist.c index b4abffad67..423c4106e2 100644 --- a/src/feature/nodelist/dirlist.c +++ b/src/feature/nodelist/dirlist.c @@ -1,7 +1,7 @@ /* Copyright (c) 2001 Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2019, The Tor Project, Inc. */ + * Copyright (c) 2007-2020, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -27,8 +27,9 @@ #include "core/or/or.h" #include "app/config/config.h" +#include "app/config/resolve_addr.h" #include "core/or/policies.h" -#include "feature/control/control.h" +#include "feature/control/control_events.h" #include "feature/dirauth/authmode.h" #include "feature/dircommon/directory.h" #include "feature/nodelist/dirlist.h" @@ -54,15 +55,13 @@ static smartlist_t *fallback_dir_servers = NULL; static void add_trusted_dir_to_nodelist_addr_set(const dir_server_t *dir) { - tor_addr_t tmp_addr; - tor_assert(dir); tor_assert(dir->is_authority); /* Add IPv4 and then IPv6 if applicable. For authorities, we add the ORPort * and DirPort so re-entry into the network back to them is not possible. */ - tor_addr_from_ipv4h(&tmp_addr, dir->addr); - nodelist_add_addr_to_address_set(&tmp_addr, dir->or_port, dir->dir_port); + nodelist_add_addr_to_address_set(&dir->ipv4_addr, dir->ipv4_orport, + dir->ipv4_dirport); if (!tor_addr_is_null(&dir->ipv6_addr)) { /* IPv6 DirPort is not a thing yet for authorities. */ nodelist_add_addr_to_address_set(&dir->ipv6_addr, dir->ipv6_orport, 0); @@ -240,8 +239,8 @@ mark_all_dirservers_up(smartlist_t *server_list) /** Return true iff <b>digest</b> is the digest of the identity key of a * trusted directory matching at least one bit of <b>type</b>. If <b>type</b> * is zero (NO_DIRINFO), or ALL_DIRINFO, any authority is okay. */ -int -router_digest_is_trusted_dir_type(const char *digest, dirinfo_type_t type) +MOCK_IMPL(int, router_digest_is_trusted_dir_type, + (const char *digest, dirinfo_type_t type)) { if (!trusted_dir_servers) return 0; @@ -254,6 +253,34 @@ router_digest_is_trusted_dir_type(const char *digest, dirinfo_type_t type) return 0; } +/** Return true iff the given address matches a trusted directory that matches + * at least one bit of type. + * + * If type is NO_DIRINFO or ALL_DIRINFO, any authority is matched. */ +bool +router_addr_is_trusted_dir_type(const tor_addr_t *addr, dirinfo_type_t type) +{ + int family = tor_addr_family(addr); + + if (!trusted_dir_servers) { + return false; + } + + SMARTLIST_FOREACH_BEGIN(trusted_dir_servers, dir_server_t *, ent) { + /* Ignore entries that don't match the given type. */ + if (type != NO_DIRINFO && (type & ent->type) == 0) { + continue; + } + /* Match IPv4 or IPv6 address. */ + if ((family == AF_INET && tor_addr_eq(addr, &ent->ipv4_addr)) || + (family == AF_INET6 && tor_addr_eq(addr, &ent->ipv6_addr))) { + return true; + } + } SMARTLIST_FOREACH_END(ent); + + return false; +} + /** Create a directory server at <b>address</b>:<b>port</b>, with OR identity * key <b>digest</b> which has DIGEST_LEN bytes. If <b>address</b> is NULL, * add ourself. If <b>is_authority</b>, this is a directory authority. Return @@ -261,16 +288,15 @@ router_digest_is_trusted_dir_type(const char *digest, dirinfo_type_t type) static dir_server_t * dir_server_new(int is_authority, const char *nickname, - const tor_addr_t *addr, + const tor_addr_t *ipv4_addr, const char *hostname, - uint16_t dir_port, uint16_t or_port, + uint16_t ipv4_dirport, uint16_t ipv4_orport, const tor_addr_port_t *addrport_ipv6, const char *digest, const char *v3_auth_digest, dirinfo_type_t type, double weight) { dir_server_t *ent; - uint32_t a; char *hostname_ = NULL; tor_assert(digest); @@ -278,27 +304,26 @@ dir_server_new(int is_authority, if (weight < 0) return NULL; - if (tor_addr_family(addr) == AF_INET) - a = tor_addr_to_ipv4h(addr); - else + if (!ipv4_addr) { return NULL; + } if (!hostname) - hostname_ = tor_addr_to_str_dup(addr); + hostname_ = tor_addr_to_str_dup(ipv4_addr); else hostname_ = tor_strdup(hostname); ent = tor_malloc_zero(sizeof(dir_server_t)); ent->nickname = nickname ? tor_strdup(nickname) : NULL; ent->address = hostname_; - ent->addr = a; - ent->dir_port = dir_port; - ent->or_port = or_port; + tor_addr_copy(&ent->ipv4_addr, ipv4_addr); + ent->ipv4_dirport = ipv4_dirport; + ent->ipv4_orport = ipv4_orport; ent->is_running = 1; ent->is_authority = is_authority; ent->type = type; ent->weight = weight; - if (addrport_ipv6) { + if (addrport_ipv6 && tor_addr_port_is_valid_ap(addrport_ipv6, 0)) { if (tor_addr_family(&addrport_ipv6->addr) != AF_INET6) { log_warn(LD_BUG, "Hey, I got a non-ipv6 addr as addrport_ipv6."); tor_addr_make_unspec(&ent->ipv6_addr); @@ -315,13 +340,13 @@ dir_server_new(int is_authority, memcpy(ent->v3_identity_digest, v3_auth_digest, DIGEST_LEN); if (nickname) - tor_asprintf(&ent->description, "directory server \"%s\" at %s:%d", - nickname, hostname_, (int)dir_port); + tor_asprintf(&ent->description, "directory server \"%s\" at %s:%" PRIu16, + nickname, hostname_, ipv4_dirport); else - tor_asprintf(&ent->description, "directory server at %s:%d", - hostname_, (int)dir_port); + tor_asprintf(&ent->description, "directory server at %s:%" PRIu16, + hostname_, ipv4_dirport); - ent->fake_status.addr = ent->addr; + tor_addr_copy(&ent->fake_status.ipv4_addr, &ent->ipv4_addr); tor_addr_copy(&ent->fake_status.ipv6_addr, &ent->ipv6_addr); memcpy(ent->fake_status.identity_digest, digest, DIGEST_LEN); if (nickname) @@ -329,41 +354,43 @@ dir_server_new(int is_authority, sizeof(ent->fake_status.nickname)); else ent->fake_status.nickname[0] = '\0'; - ent->fake_status.dir_port = ent->dir_port; - ent->fake_status.or_port = ent->or_port; + ent->fake_status.ipv4_dirport = ent->ipv4_dirport; + ent->fake_status.ipv4_orport = ent->ipv4_orport; ent->fake_status.ipv6_orport = ent->ipv6_orport; return ent; } -/** Create an authoritative directory server at - * <b>address</b>:<b>port</b>, with identity key <b>digest</b>. If - * <b>address</b> is NULL, add ourself. Return the new trusted directory - * server entry on success or NULL if we couldn't add it. */ +/** Create an authoritative directory server at <b>address</b>:<b>port</b>, + * with identity key <b>digest</b>. If <b>ipv4_addr_str</b> is NULL, add + * ourself. Return the new trusted directory server entry on success or NULL + * if we couldn't add it. */ dir_server_t * trusted_dir_server_new(const char *nickname, const char *address, - uint16_t dir_port, uint16_t or_port, + uint16_t ipv4_dirport, uint16_t ipv4_orport, const tor_addr_port_t *ipv6_addrport, const char *digest, const char *v3_auth_digest, dirinfo_type_t type, double weight) { - uint32_t a; - tor_addr_t addr; + tor_addr_t ipv4_addr; char *hostname=NULL; dir_server_t *result; if (!address) { /* The address is us; we should guess. */ - if (resolve_my_address(LOG_WARN, get_options(), - &a, NULL, &hostname) < 0) { + if (!find_my_address(get_options(), AF_INET, LOG_WARN, &ipv4_addr, + NULL, &hostname)) { log_warn(LD_CONFIG, "Couldn't find a suitable address when adding ourself as a " "trusted directory server."); return NULL; } if (!hostname) - hostname = tor_dup_ip(a); + hostname = tor_addr_to_str_dup(&ipv4_addr); + + if (!hostname) + return NULL; } else { - if (tor_lookup_hostname(address, &a)) { + if (tor_addr_lookup(address, AF_INET, &ipv4_addr)) { log_warn(LD_CONFIG, "Unable to lookup address for directory server at '%s'", address); @@ -371,10 +398,9 @@ trusted_dir_server_new(const char *nickname, const char *address, } hostname = tor_strdup(address); } - tor_addr_from_ipv4h(&addr, a); - result = dir_server_new(1, nickname, &addr, hostname, - dir_port, or_port, + result = dir_server_new(1, nickname, &ipv4_addr, hostname, + ipv4_dirport, ipv4_orport, ipv6_addrport, digest, v3_auth_digest, type, weight); @@ -386,15 +412,13 @@ trusted_dir_server_new(const char *nickname, const char *address, * <b>addr</b>:<b>or_port</b>/<b>dir_port</b>, with identity key digest * <b>id_digest</b> */ dir_server_t * -fallback_dir_server_new(const tor_addr_t *addr, - uint16_t dir_port, uint16_t or_port, +fallback_dir_server_new(const tor_addr_t *ipv4_addr, + uint16_t ipv4_dirport, uint16_t ipv4_orport, const tor_addr_port_t *addrport_ipv6, const char *id_digest, double weight) { - return dir_server_new(0, NULL, addr, NULL, dir_port, or_port, - addrport_ipv6, - id_digest, - NULL, ALL_DIRINFO, weight); + return dir_server_new(0, NULL, ipv4_addr, NULL, ipv4_dirport, ipv4_orport, + addrport_ipv6, id_digest, NULL, ALL_DIRINFO, weight); } /** Add a directory server to the global list(s). */ diff --git a/src/feature/nodelist/dirlist.h b/src/feature/nodelist/dirlist.h index 527af35427..ae3debf4e5 100644 --- a/src/feature/nodelist/dirlist.h +++ b/src/feature/nodelist/dirlist.h @@ -1,6 +1,6 @@ /* Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2019, The Tor Project, Inc. */ + * Copyright (c) 2007-2020, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -25,8 +25,14 @@ int router_digest_is_fallback_dir(const char *digest); MOCK_DECL(dir_server_t *, trusteddirserver_get_by_v3_auth_digest, (const char *d)); -int router_digest_is_trusted_dir_type(const char *digest, - dirinfo_type_t type); +MOCK_DECL(int, router_digest_is_trusted_dir_type, + (const char *digest, dirinfo_type_t type)); + +bool router_addr_is_trusted_dir_type(const tor_addr_t *addr, + dirinfo_type_t type); +#define router_addr_is_trusted_dir(d) \ + router_addr_is_trusted_dir_type((d), NO_DIRINFO) + #define router_digest_is_trusted_dir(d) \ router_digest_is_trusted_dir_type((d), NO_DIRINFO) @@ -46,4 +52,4 @@ void dirlist_free_all(void); MOCK_DECL(void, dirlist_add_trusted_dir_addresses, (void)); -#endif +#endif /* !defined(TOR_DIRLIST_H) */ diff --git a/src/feature/nodelist/document_signature_st.h b/src/feature/nodelist/document_signature_st.h index 66e32c422f..4bde9d89ec 100644 --- a/src/feature/nodelist/document_signature_st.h +++ b/src/feature/nodelist/document_signature_st.h @@ -1,9 +1,14 @@ /* Copyright (c) 2001 Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2019, The Tor Project, Inc. */ + * Copyright (c) 2007-2020, The Tor Project, Inc. */ /* See LICENSE for licensing information */ +/** + * @file document_signature_st.h + * @brief Authority signature structure + **/ + #ifndef DOCUMENT_SIGNATURE_ST_H #define DOCUMENT_SIGNATURE_ST_H @@ -25,5 +30,4 @@ struct document_signature_t { * as good. */ }; -#endif - +#endif /* !defined(DOCUMENT_SIGNATURE_ST_H) */ diff --git a/src/feature/nodelist/extrainfo_st.h b/src/feature/nodelist/extrainfo_st.h index c54277b05e..6bd6232cd8 100644 --- a/src/feature/nodelist/extrainfo_st.h +++ b/src/feature/nodelist/extrainfo_st.h @@ -1,9 +1,14 @@ /* Copyright (c) 2001 Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2019, The Tor Project, Inc. */ + * Copyright (c) 2007-2020, The Tor Project, Inc. */ /* See LICENSE for licensing information */ +/** + * @file extrainfo_st.h + * @brief A relay's extra-info structure. + **/ + #ifndef EXTRAINFO_ST_H #define EXTRAINFO_ST_H @@ -26,5 +31,4 @@ struct extrainfo_t { size_t pending_sig_len; }; -#endif - +#endif /* !defined(EXTRAINFO_ST_H) */ diff --git a/src/feature/nodelist/feature_nodelist.md b/src/feature/nodelist/feature_nodelist.md new file mode 100644 index 0000000000..9d715308c2 --- /dev/null +++ b/src/feature/nodelist/feature_nodelist.md @@ -0,0 +1,2 @@ +@dir /feature/nodelist +@brief feature/nodelist: Download and manage a list of relays diff --git a/src/feature/nodelist/fmt_routerstatus.c b/src/feature/nodelist/fmt_routerstatus.c index 75cab7a0af..252b2e61fe 100644 --- a/src/feature/nodelist/fmt_routerstatus.c +++ b/src/feature/nodelist/fmt_routerstatus.c @@ -1,6 +1,6 @@ /* Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2019, The Tor Project, Inc. */ + * Copyright (c) 2007-2020, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -14,64 +14,19 @@ #include "core/or/or.h" #include "feature/nodelist/fmt_routerstatus.h" -/* #include "lib/container/buffers.h" */ -/* #include "app/config/config.h" */ -/* #include "app/config/confparse.h" */ -/* #include "core/or/channel.h" */ -/* #include "core/or/channeltls.h" */ -/* #include "core/or/command.h" */ -/* #include "core/mainloop/connection.h" */ -/* #include "core/or/connection_or.h" */ -/* #include "feature/dircache/conscache.h" */ -/* #include "feature/dircache/consdiffmgr.h" */ -/* #include "feature/control/control.h" */ -/* #include "feature/dircache/directory.h" */ -/* #include "feature/dircache/dirserv.h" */ -/* #include "feature/hibernate/hibernate.h" */ -/* #include "feature/dirauth/keypin.h" */ -/* #include "core/mainloop/mainloop.h" */ -/* #include "feature/nodelist/microdesc.h" */ -/* #include "feature/nodelist/networkstatus.h" */ -/* #include "feature/nodelist/nodelist.h" */ #include "core/or/policies.h" -/* #include "core/or/protover.h" */ -/* #include "feature/stats/rephist.h" */ -/* #include "feature/relay/router.h" */ -/* #include "feature/nodelist/dirlist.h" */ #include "feature/nodelist/routerlist.h" - -/* #include "feature/nodelist/routerparse.h" */ -/* #include "feature/nodelist/routerset.h" */ -/* #include "feature/nodelist/torcert.h" */ -/* #include "feature/dircommon/voting_schedule.h" */ - #include "feature/dirauth/dirvote.h" -/* #include "feature/dircache/cached_dir_st.h" */ -/* #include "feature/dircommon/dir_connection_st.h" */ -/* #include "feature/nodelist/extrainfo_st.h" */ -/* #include "feature/nodelist/microdesc_st.h" */ -/* #include "feature/nodelist/node_st.h" */ #include "feature/nodelist/routerinfo_st.h" -/* #include "feature/nodelist/routerlist_st.h" */ -/* #include "core/or/tor_version_st.h" */ #include "feature/nodelist/vote_routerstatus_st.h" -/* #include "lib/compress/compress.h" */ -/* #include "lib/container/order.h" */ #include "lib/crypt_ops/crypto_format.h" -/* #include "lib/encoding/confline.h" */ - -/* #include "lib/encoding/keyval.h" */ /** Helper: write the router-status information in <b>rs</b> into a newly * allocated character buffer. Use the same format as in network-status * documents. If <b>version</b> is non-NULL, add a "v" line for the platform. * - * consensus_method is the current consensus method when format is - * NS_V3_CONSENSUS or NS_V3_CONSENSUS_MICRODESC. It is ignored for other - * formats: pass ROUTERSTATUS_FORMAT_NO_CONSENSUS_METHOD. - * * Return 0 on success, -1 on failure. * * The format argument has one of the following values: @@ -88,7 +43,6 @@ char * routerstatus_format_entry(const routerstatus_t *rs, const char *version, const char *protocols, routerstatus_format_type_t format, - int consensus_method, const vote_routerstatus_t *vrs) { char *summary; @@ -99,32 +53,30 @@ routerstatus_format_entry(const routerstatus_t *rs, const char *version, char digest64[BASE64_DIGEST_LEN+1]; smartlist_t *chunks = smartlist_new(); + const char *ip_str = fmt_addr(&rs->ipv4_addr); + if (ip_str[0] == '\0') + goto err; + format_iso_time(published, rs->published_on); digest_to_base64(identity64, rs->identity_digest); digest_to_base64(digest64, rs->descriptor_digest); smartlist_add_asprintf(chunks, - "r %s %s %s%s%s %s %d %d\n", + "r %s %s %s%s%s %s %" PRIu16 " %" PRIu16 "\n", rs->nickname, identity64, (format==NS_V3_CONSENSUS_MICRODESC)?"":digest64, (format==NS_V3_CONSENSUS_MICRODESC)?"":" ", published, - fmt_addr32(rs->addr), - (int)rs->or_port, - (int)rs->dir_port); + ip_str, + rs->ipv4_orport, + rs->ipv4_dirport); /* TODO: Maybe we want to pass in what we need to build the rest of * this here, instead of in the caller. Then we could use the * networkstatus_type_t values, with an additional control port value * added -MP */ - /* V3 microdesc consensuses only have "a" lines in later consensus methods - */ - if (format == NS_V3_CONSENSUS_MICRODESC && - consensus_method < MIN_METHOD_FOR_A_LINES_IN_MICRODESC_CONSENSUS) - goto done; - /* Possible "a" line. At most one for now. */ if (!tor_addr_is_null(&rs->ipv6_addr)) { smartlist_add_asprintf(chunks, "a %s\n", @@ -135,7 +87,7 @@ routerstatus_format_entry(const routerstatus_t *rs, const char *version, goto done; smartlist_add_asprintf(chunks, - "s%s%s%s%s%s%s%s%s%s%s\n", + "s%s%s%s%s%s%s%s%s%s%s%s\n", /* These must stay in alphabetical order. */ rs->is_authority?" Authority":"", rs->is_bad_exit?" BadExit":"", @@ -145,6 +97,7 @@ routerstatus_format_entry(const routerstatus_t *rs, const char *version, rs->is_hs_dir?" HSDir":"", rs->is_flagged_running?" Running":"", rs->is_stable?" Stable":"", + rs->is_staledesc?" StaleDesc":"", rs->is_v2_dir?" V2Dir":"", rs->is_valid?" Valid":""); @@ -164,6 +117,8 @@ routerstatus_format_entry(const routerstatus_t *rs, const char *version, if (format != NS_CONTROL_PORT) { /* Blow up more or less nicely if we didn't get anything or not the * thing we expected. + * This should be kept in sync with the function + * routerstatus_has_visibly_changed and the struct routerstatus_t */ if (!desc) { char id[HEX_DIGEST_LEN+1]; @@ -232,7 +187,7 @@ routerstatus_format_entry(const routerstatus_t *rs, const char *version, } if (format == NS_V3_VOTE && vrs) { - if (tor_mem_is_zero((char*)vrs->ed25519_id, ED25519_PUBKEY_LEN)) { + if (fast_mem_is_zero((char*)vrs->ed25519_id, ED25519_PUBKEY_LEN)) { smartlist_add_strdup(chunks, "id ed25519 none\n"); } else { char ed_b64[BASE64_DIGEST256_LEN+1]; diff --git a/src/feature/nodelist/fmt_routerstatus.h b/src/feature/nodelist/fmt_routerstatus.h index ddd7a7cf37..a007989af3 100644 --- a/src/feature/nodelist/fmt_routerstatus.h +++ b/src/feature/nodelist/fmt_routerstatus.h @@ -1,7 +1,7 @@ /* Copyright (c) 2001 Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2019, The Tor Project, Inc. */ + * Copyright (c) 2007-2020, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -35,7 +35,6 @@ char *routerstatus_format_entry( const char *version, const char *protocols, routerstatus_format_type_t format, - int consensus_method, const vote_routerstatus_t *vrs); #endif /* !defined(TOR_FMT_ROUTERSTATUS_H) */ diff --git a/src/feature/nodelist/include.am b/src/feature/nodelist/include.am new file mode 100644 index 0000000000..2f5d58ec1c --- /dev/null +++ b/src/feature/nodelist/include.am @@ -0,0 +1,49 @@ + +# ADD_C_FILE: INSERT SOURCES HERE. +LIBTOR_APP_A_SOURCES += \ + src/feature/nodelist/authcert.c \ + src/feature/nodelist/describe.c \ + src/feature/nodelist/dirlist.c \ + src/feature/nodelist/microdesc.c \ + src/feature/nodelist/networkstatus.c \ + src/feature/nodelist/nickname.c \ + src/feature/nodelist/nodefamily.c \ + src/feature/nodelist/nodelist.c \ + src/feature/nodelist/node_select.c \ + src/feature/nodelist/routerinfo.c \ + src/feature/nodelist/routerlist.c \ + src/feature/nodelist/routerset.c \ + src/feature/nodelist/fmt_routerstatus.c \ + src/feature/nodelist/torcert.c + +# ADD_C_FILE: INSERT HEADERS HERE. +noinst_HEADERS += \ + src/feature/nodelist/authcert.h \ + src/feature/nodelist/authority_cert_st.h \ + src/feature/nodelist/describe.h \ + src/feature/nodelist/desc_store_st.h \ + src/feature/nodelist/dirlist.h \ + src/feature/nodelist/document_signature_st.h \ + src/feature/nodelist/extrainfo_st.h \ + src/feature/nodelist/microdesc.h \ + src/feature/nodelist/microdesc_st.h \ + src/feature/nodelist/networkstatus.h \ + src/feature/nodelist/networkstatus_sr_info_st.h \ + src/feature/nodelist/networkstatus_st.h \ + src/feature/nodelist/networkstatus_voter_info_st.h \ + src/feature/nodelist/nickname.h \ + src/feature/nodelist/node_st.h \ + src/feature/nodelist/nodefamily.h \ + src/feature/nodelist/nodefamily_st.h \ + src/feature/nodelist/nodelist.h \ + src/feature/nodelist/node_select.h \ + src/feature/nodelist/routerinfo.h \ + src/feature/nodelist/routerinfo_st.h \ + src/feature/nodelist/routerlist.h \ + src/feature/nodelist/routerlist_st.h \ + src/feature/nodelist/routerset.h \ + src/feature/nodelist/fmt_routerstatus.h \ + src/feature/nodelist/routerstatus_st.h \ + src/feature/nodelist/signed_descriptor_st.h \ + src/feature/nodelist/torcert.h \ + src/feature/nodelist/vote_routerstatus_st.h diff --git a/src/feature/nodelist/microdesc.c b/src/feature/nodelist/microdesc.c index dafaabb5e5..01dccd160b 100644 --- a/src/feature/nodelist/microdesc.c +++ b/src/feature/nodelist/microdesc.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2009-2019, The Tor Project, Inc. */ +/* Copyright (c) 2009-2020, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -18,11 +18,13 @@ #include "feature/client/entrynodes.h" #include "feature/dircache/dirserv.h" #include "feature/dirclient/dlstatus.h" +#include "feature/dirclient/dirclient_modes.h" #include "feature/dircommon/directory.h" #include "feature/dirparse/microdesc_parse.h" #include "feature/nodelist/dirlist.h" #include "feature/nodelist/microdesc.h" #include "feature/nodelist/networkstatus.h" +#include "feature/nodelist/nodefamily.h" #include "feature/nodelist/nodelist.h" #include "feature/nodelist/routerlist.h" #include "feature/relay/router.h" @@ -69,6 +71,8 @@ struct microdesc_cache_t { }; static microdesc_cache_t *get_microdesc_cache_noload(void); +static void warn_if_nul_found(const char *inp, size_t len, int64_t offset, + const char *activity); /** Helper: computes a hash of <b>md</b> to place it in a hash table. */ static inline unsigned int @@ -86,10 +90,10 @@ microdesc_eq_(microdesc_t *a, microdesc_t *b) } HT_PROTOTYPE(microdesc_map, microdesc_t, node, - microdesc_hash_, microdesc_eq_) + microdesc_hash_, microdesc_eq_); HT_GENERATE2(microdesc_map, microdesc_t, node, microdesc_hash_, microdesc_eq_, 0.6, - tor_reallocarray_, tor_free_) + tor_reallocarray_, tor_free_); /************************* md fetch fail cache *****************************/ @@ -110,8 +114,9 @@ microdesc_note_outdated_dirserver(const char *relay_digest) /* If we have a reasonably live consensus, then most of our dirservers should * still be caching all the microdescriptors in it. Reasonably live - * consensuses are up to a day old. But microdescriptors expire 7 days after - * the last consensus that referenced them. */ + * consensuses are up to a day old (or a day in the future). But + * microdescriptors expire 7 days after the last consensus that referenced + * them. */ if (!networkstatus_get_reasonably_live_consensus(approx_time(), FLAV_MICRODESC)) { return; @@ -124,8 +129,9 @@ microdesc_note_outdated_dirserver(const char *relay_digest) tor_assert(outdated_dirserver_list); /* If the list grows too big, clean it up */ - if (BUG(smartlist_len(outdated_dirserver_list) > - TOO_MANY_OUTDATED_DIRSERVERS)) { + if (smartlist_len(outdated_dirserver_list) > TOO_MANY_OUTDATED_DIRSERVERS) { + log_info(LD_GENERAL,"Too many outdated directory servers (%d). Resetting.", + smartlist_len(outdated_dirserver_list)); microdesc_reset_outdated_dirservers_list(); } @@ -221,6 +227,8 @@ dump_microdescriptor(int fd, microdesc_t *md, size_t *annotation_len_out) } md->off = tor_fd_getpos(fd); + warn_if_nul_found(md->body, md->bodylen, (int64_t) md->off, + "dumping a microdescriptor"); written = write_all_to_fd(fd, md->body, md->bodylen); if (written != (ssize_t)md->bodylen) { written = written < 0 ? 0 : written; @@ -480,6 +488,27 @@ microdesc_cache_clear(microdesc_cache_t *cache) cache->bytes_dropped = 0; } +static void +warn_if_nul_found(const char *inp, size_t len, int64_t offset, + const char *activity) +{ + const char *nul_found = memchr(inp, 0, len); + if (BUG(nul_found)) { + log_warn(LD_BUG, "Found unexpected NUL while %s, offset %"PRId64 + "at position %"TOR_PRIuSZ"/%"TOR_PRIuSZ".", + activity, offset, (nul_found - inp), len); + const char *start_excerpt_at, *eos = inp + len; + if ((nul_found - inp) >= 16) + start_excerpt_at = nul_found - 16; + else + start_excerpt_at = inp; + size_t excerpt_len = MIN(32, eos - start_excerpt_at); + char tmp[65]; + base16_encode(tmp, sizeof(tmp), start_excerpt_at, excerpt_len); + log_warn(LD_BUG, " surrounding string: %s", tmp); + } +} + /** Reload the contents of <b>cache</b> from disk. If it is empty, load it * for the first time. Return 0 on success, -1 on failure. */ int @@ -497,6 +526,7 @@ microdesc_cache_reload(microdesc_cache_t *cache) mm = cache->cache_content = tor_mmap_file(cache->cache_fname); if (mm) { + warn_if_nul_found(mm->data, mm->size, 0, "scanning microdesc cache"); added = microdescs_add_to_cache(cache, mm->data, mm->data+mm->size, SAVED_IN_CACHE, 0, -1, NULL); if (added) { @@ -508,7 +538,9 @@ microdesc_cache_reload(microdesc_cache_t *cache) journal_content = read_file_to_str(cache->journal_fname, RFTS_IGNORE_MISSING, &st); if (journal_content) { - cache->journal_len = (size_t) st.st_size; + cache->journal_len = strlen(journal_content); + warn_if_nul_found(journal_content, (size_t)st.st_size, 0, + "reading microdesc journal"); added = microdescs_add_to_cache(cache, journal_content, journal_content+st.st_size, SAVED_IN_JOURNAL, 0, -1, NULL); @@ -544,8 +576,8 @@ microdesc_cache_clean(microdesc_cache_t *cache, time_t cutoff, int force) size_t bytes_dropped = 0; time_t now = time(NULL); - /* If we don't know a live consensus, don't believe last_listed values: we - * might be starting up after being down for a while. */ + /* If we don't know a reasonably live consensus, don't believe last_listed + * values: we might be starting up after being down for a while. */ if (! force && ! networkstatus_get_reasonably_live_consensus(now, FLAV_MICRODESC)) return; @@ -884,10 +916,7 @@ microdesc_free_(microdesc_t *md, const char *fname, int lineno) if (md->body && md->saved_location != SAVED_IN_CACHE) tor_free(md->body); - if (md->family) { - SMARTLIST_FOREACH(md->family, char *, cp, tor_free(cp)); - smartlist_free(md->family); - } + nodefamily_free(md->family); short_policy_free(md->exit_policy); short_policy_free(md->ipv6_exit_policy); @@ -943,7 +972,7 @@ microdesc_list_missing_digest256(networkstatus_t *ns, microdesc_cache_t *cache, continue; if (skip && digest256map_get(skip, (const uint8_t*)rs->descriptor_digest)) continue; - if (tor_mem_is_zero(rs->descriptor_digest, DIGEST256_LEN)) + if (fast_mem_is_zero(rs->descriptor_digest, DIGEST256_LEN)) continue; /* XXXX Also skip if we're a noncache and wouldn't use this router. * XXXX NM Microdesc @@ -970,9 +999,10 @@ update_microdesc_downloads(time_t now) if (should_delay_dir_fetches(options, NULL)) return; - if (directory_too_idle_to_fetch_descriptors(options, now)) + if (dirclient_too_idle_to_fetch_descriptors(options, now)) return; + /* Give up if we don't have a reasonably live consensus. */ consensus = networkstatus_get_reasonably_live_consensus(now, FLAV_MICRODESC); if (!consensus) return; diff --git a/src/feature/nodelist/microdesc.h b/src/feature/nodelist/microdesc.h index c18099d540..b352f58e34 100644 --- a/src/feature/nodelist/microdesc.h +++ b/src/feature/nodelist/microdesc.h @@ -1,7 +1,7 @@ /* Copyright (c) 2001 Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2019, The Tor Project, Inc. */ + * Copyright (c) 2007-2020, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** diff --git a/src/feature/nodelist/microdesc_st.h b/src/feature/nodelist/microdesc_st.h index bb8b23d664..410403e965 100644 --- a/src/feature/nodelist/microdesc_st.h +++ b/src/feature/nodelist/microdesc_st.h @@ -1,16 +1,24 @@ /* Copyright (c) 2001 Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2019, The Tor Project, Inc. */ + * Copyright (c) 2007-2020, The Tor Project, Inc. */ /* See LICENSE for licensing information */ +/** + * @file microdesc_st.h + * @brief Microdescriptor structure + **/ + #ifndef MICRODESC_ST_H #define MICRODESC_ST_H struct curve25519_public_key_t; struct ed25519_public_key_t; +struct nodefamily_t; struct short_policy_t; +#include "ext/ht.h" + /** A microdescriptor is the smallest amount of information needed to build a * circuit through a router. They are generated by the directory authorities, * using information from the uploaded routerinfo documents. They are not @@ -32,6 +40,8 @@ struct microdesc_t { unsigned int no_save : 1; /** If true, this microdesc has an entry in the microdesc_map */ unsigned int held_in_map : 1; + /** True iff the exit policy for this router rejects everything. */ + unsigned int policy_is_reject_star : 1; /** Reference count: how many node_ts have a reference to this microdesc? */ unsigned int held_by_nodes; @@ -69,12 +79,12 @@ struct microdesc_t { tor_addr_t ipv6_addr; /** As routerinfo_t.ipv6_orport */ uint16_t ipv6_orport; - /** As routerinfo_t.family */ - smartlist_t *family; + /** As routerinfo_t.family, with readable members parsed. */ + struct nodefamily_t *family; /** IPv4 exit policy summary */ struct short_policy_t *exit_policy; /** IPv6 exit policy summary */ struct short_policy_t *ipv6_exit_policy; }; -#endif +#endif /* !defined(MICRODESC_ST_H) */ diff --git a/src/feature/nodelist/networkstatus.c b/src/feature/nodelist/networkstatus.c index c74acd8b74..80940e6092 100644 --- a/src/feature/nodelist/networkstatus.c +++ b/src/feature/nodelist/networkstatus.c @@ -1,7 +1,7 @@ /* Copyright (c) 2001 Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2019, The Tor Project, Inc. */ + * Copyright (c) 2007-2020, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -44,6 +44,7 @@ #include "core/mainloop/netstatus.h" #include "core/or/channel.h" #include "core/or/channelpadding.h" +#include "core/or/circuitpadding.h" #include "core/or/circuitmux.h" #include "core/or/circuitmux_ewma.h" #include "core/or/circuitstats.h" @@ -57,16 +58,18 @@ #include "feature/client/bridges.h" #include "feature/client/entrynodes.h" #include "feature/client/transports.h" -#include "feature/control/control.h" +#include "feature/control/control_events.h" #include "feature/dirauth/reachability.h" #include "feature/dircache/consdiffmgr.h" #include "feature/dircache/dirserv.h" #include "feature/dirclient/dirclient.h" +#include "feature/dirclient/dirclient_modes.h" #include "feature/dirclient/dlstatus.h" #include "feature/dircommon/directory.h" -#include "feature/dircommon/voting_schedule.h" +#include "feature/dirauth/voting_schedule.h" #include "feature/dirparse/ns_parse.h" #include "feature/hibernate/hibernate.h" +#include "feature/hs/hs_dos.h" #include "feature/nodelist/authcert.h" #include "feature/nodelist/dirlist.h" #include "feature/nodelist/fmt_routerstatus.h" @@ -81,6 +84,7 @@ #include "lib/crypt_ops/crypto_rand.h" #include "lib/crypt_ops/crypto_util.h" +#include "feature/dirauth/dirauth_periodic.h" #include "feature/dirauth/dirvote.h" #include "feature/dirauth/authmode.h" #include "feature/dirauth/shared_random.h" @@ -98,6 +102,7 @@ #include "feature/nodelist/routerlist_st.h" #include "feature/dirauth/vote_microdesc_hash_st.h" #include "feature/nodelist/vote_routerstatus_st.h" +#include "feature/nodelist/routerstatus_st.h" #ifdef HAVE_UNISTD_H #include <unistd.h> @@ -116,8 +121,6 @@ STATIC networkstatus_t *current_md_consensus = NULL; typedef struct consensus_waiting_for_certs_t { /** The consensus itself. */ networkstatus_t *consensus; - /** The encoded version of the consensus, nul-terminated. */ - char *body; /** When did we set the current value of consensus_waiting_for_certs? If * this is too recent, we shouldn't try to fetch a new consensus for a * little while, to give ourselves time to get certificates for this one. */ @@ -179,6 +182,10 @@ static void update_consensus_bootstrap_multiple_downloads( static int networkstatus_check_required_protocols(const networkstatus_t *ns, int client_mode, char **warning_out); +static int reload_consensus_from_file(const char *fname, + const char *flavor, + unsigned flags, + const char *source_dir); /** Forget that we've warned about anything networkstatus-related, so we will * give fresh warnings if the same behavior happens again. */ @@ -210,14 +217,11 @@ networkstatus_reset_download_failures(void) download_status_reset(&consensus_bootstrap_dl_status[i]); } -/** - * Read and and return the cached consensus of type <b>flavorname</b>. If - * <b>unverified</b> is true, get the one we haven't verified. Return NULL if - * the file isn't there. */ -static char * -networkstatus_read_cached_consensus_impl(int flav, - const char *flavorname, - int unverified_consensus) +/** Return the filename used to cache the consensus of a given flavor */ +MOCK_IMPL(char *, +networkstatus_get_cache_fname,(int flav, + const char *flavorname, + int unverified_consensus)) { char buf[128]; const char *prefix; @@ -232,21 +236,35 @@ networkstatus_read_cached_consensus_impl(int flav, tor_snprintf(buf, sizeof(buf), "%s-%s-consensus", prefix, flavorname); } - char *filename = get_cachedir_fname(buf); - char *result = read_file_to_str(filename, RFTS_IGNORE_MISSING, NULL); + return get_cachedir_fname(buf); +} + +/** + * Read and and return the cached consensus of type <b>flavorname</b>. If + * <b>unverified</b> is false, get the one we haven't verified. Return NULL if + * the file isn't there. */ +static tor_mmap_t * +networkstatus_map_cached_consensus_impl(int flav, + const char *flavorname, + int unverified_consensus) +{ + char *filename = networkstatus_get_cache_fname(flav, + flavorname, + unverified_consensus); + tor_mmap_t *result = tor_mmap_file(filename); tor_free(filename); return result; } -/** Return a new string containing the current cached consensus of flavor - * <b>flavorname</b>. */ -char * -networkstatus_read_cached_consensus(const char *flavorname) - { +/** Map the file containing the current cached consensus of flavor + * <b>flavorname</b> */ +tor_mmap_t * +networkstatus_map_cached_consensus(const char *flavorname) +{ int flav = networkstatus_parse_flavor_name(flavorname); if (flav < 0) return NULL; - return networkstatus_read_cached_consensus_impl(flav, flavorname, 0); + return networkstatus_map_cached_consensus_impl(flav, flavorname, 0); } /** Read every cached v3 consensus networkstatus from the disk. */ @@ -259,25 +277,15 @@ router_reload_consensus_networkstatus(void) /* FFFF Suppress warnings if cached consensus is bad? */ for (flav = 0; flav < N_CONSENSUS_FLAVORS; ++flav) { const char *flavor = networkstatus_get_flavor_name(flav); - char *s = networkstatus_read_cached_consensus_impl(flav, flavor, 0); - if (s) { - if (networkstatus_set_current_consensus(s, flavor, flags, NULL) < -1) { - log_warn(LD_FS, "Couldn't load consensus %s networkstatus from cache", - flavor); - } - tor_free(s); - } + char *fname = networkstatus_get_cache_fname(flav, flavor, 0); + reload_consensus_from_file(fname, flavor, flags, NULL); + tor_free(fname); - s = networkstatus_read_cached_consensus_impl(flav, flavor, 1); - if (s) { - if (networkstatus_set_current_consensus(s, flavor, - flags | NSSET_WAS_WAITING_FOR_CERTS, - NULL)) { - log_info(LD_FS, "Couldn't load unverified consensus %s networkstatus " - "from cache", flavor); - } - tor_free(s); - } + fname = networkstatus_get_cache_fname(flav, flavor, 1); + reload_consensus_from_file(fname, flavor, + flags | NSSET_WAS_WAITING_FOR_CERTS, + NULL); + tor_free(fname); } update_certificate_downloads(time(NULL)); @@ -463,8 +471,8 @@ networkstatus_check_document_signature(const networkstatus_t *consensus, DIGEST_LEN)) return -1; - if (authority_cert_is_blacklisted(cert)) { - /* We implement blacklisting for authority signing keys by treating + if (authority_cert_is_denylisted(cert)) { + /* We implement denylisting for authority signing keys by treating * all their signatures as always bad. That way we don't get into * crazy loops of dropping and re-fetching signatures. */ log_warn(LD_DIR, "Ignoring a consensus signature made with deprecated" @@ -600,25 +608,25 @@ networkstatus_check_consensus_signature(networkstatus_t *consensus, SMARTLIST_FOREACH(unrecognized, networkstatus_voter_info_t *, voter, { tor_log(severity, LD_DIR, "Consensus includes unrecognized authority " - "'%s' at %s:%d (contact %s; identity %s)", - voter->nickname, voter->address, (int)voter->dir_port, + "'%s' at %s:%" PRIu16 " (contact %s; identity %s)", + voter->nickname, voter->address, voter->ipv4_dirport, voter->contact?voter->contact:"n/a", hex_str(voter->identity_digest, DIGEST_LEN)); }); SMARTLIST_FOREACH(need_certs_from, networkstatus_voter_info_t *, voter, { tor_log(severity, LD_DIR, "Looks like we need to download a new " - "certificate from authority '%s' at %s:%d (contact %s; " - "identity %s)", - voter->nickname, voter->address, (int)voter->dir_port, + "certificate from authority '%s' at %s:%" PRIu16 + " (contact %s; identity %s)", + voter->nickname, voter->address, voter->ipv4_dirport, voter->contact?voter->contact:"n/a", hex_str(voter->identity_digest, DIGEST_LEN)); }); SMARTLIST_FOREACH(missing_authorities, dir_server_t *, ds, { tor_log(severity, LD_DIR, "Consensus does not include configured " - "authority '%s' at %s:%d (identity %s)", - ds->nickname, ds->address, (int)ds->dir_port, + "authority '%s' at %s:%" PRIu16 " (identity %s)", + ds->nickname, ds->address, ds->ipv4_dirport, hex_str(ds->v3_identity_digest, DIGEST_LEN)); }); { @@ -713,8 +721,8 @@ networkstatus_vote_find_mutable_entry(networkstatus_t *ns, const char *digest) /** Return the entry in <b>ns</b> for the identity digest <b>digest</b>, or * NULL if none was found. */ -const routerstatus_t * -networkstatus_vote_find_entry(networkstatus_t *ns, const char *digest) +MOCK_IMPL(const routerstatus_t *, +networkstatus_vote_find_entry,(networkstatus_t *ns, const char *digest)) { return networkstatus_vote_find_mutable_entry(ns, digest); } @@ -1156,7 +1164,7 @@ update_consensus_networkstatus_fetch_time_impl(time_t now, int flav) } } - if (directory_fetches_dir_info_early(options)) { + if (dirclient_fetches_dir_info_early(options)) { /* We want to cache the next one at some point after this one * is no longer fresh... */ start = (time_t)(c->fresh_until + min_sec_before_caching); @@ -1178,7 +1186,7 @@ update_consensus_networkstatus_fetch_time_impl(time_t now, int flav) /* If we're a bridge user, make use of the numbers we just computed * to choose the rest of the interval *after* them. */ - if (directory_fetches_dir_info_later(options)) { + if (dirclient_fetches_dir_info_later(options)) { /* Give all the *clients* enough time to download the consensus. */ start = (time_t)(start + dl_interval + min_sec_before_caching); /* But try to get it before ours actually expires. */ @@ -1377,7 +1385,7 @@ networkstatus_get_dl_status_by_flavor_running,(consensus_flavor_t flavor)) } /** Return the most recent consensus that we have downloaded, or NULL if we - * don't have one. */ + * don't have one. May return future or expired consensuses. */ MOCK_IMPL(networkstatus_t *, networkstatus_get_latest_consensus,(void)) { @@ -1388,7 +1396,7 @@ networkstatus_get_latest_consensus,(void)) } /** Return the latest consensus we have whose flavor matches <b>f</b>, or NULL - * if we don't have one. */ + * if we don't have one. May return future or expired consensuses. */ MOCK_IMPL(networkstatus_t *, networkstatus_get_latest_consensus_by_flavor,(consensus_flavor_t f)) { @@ -1422,10 +1430,11 @@ networkstatus_is_live(const networkstatus_t *ns, time_t now) return (ns->valid_after <= now && now <= ns->valid_until); } -/** Determine if <b>consensus</b> is valid or expired recently enough that - * we can still use it. +/** Determine if <b>consensus</b> is valid, or expired recently enough, or not + * too far in the future, so that we can still use it. * - * Return 1 if the consensus is reasonably live, or 0 if it is too old. + * Return 1 if the consensus is reasonably live, or 0 if it is too old or + * too new. */ int networkstatus_consensus_reasonably_live(const networkstatus_t *consensus, @@ -1434,29 +1443,42 @@ networkstatus_consensus_reasonably_live(const networkstatus_t *consensus, if (BUG(!consensus)) return 0; - return networkstatus_valid_until_is_reasonably_live(consensus->valid_until, + return networkstatus_valid_after_is_reasonably_live(consensus->valid_after, + now) && + networkstatus_valid_until_is_reasonably_live(consensus->valid_until, now); } +#define REASONABLY_LIVE_TIME (24*60*60) + +/** As networkstatus_consensus_reasonably_live, but takes a valid_after + * time, and checks to see if it is in the past, or not too far in the future. + */ +int +networkstatus_valid_after_is_reasonably_live(time_t valid_after, + time_t now) +{ + return (now >= valid_after - REASONABLY_LIVE_TIME); +} + /** As networkstatus_consensus_reasonably_live, but takes a valid_until - * time rather than an entire consensus. */ + * time, and checks to see if it is in the future, or not too far in the past. + */ int networkstatus_valid_until_is_reasonably_live(time_t valid_until, time_t now) { -#define REASONABLY_LIVE_TIME (24*60*60) return (now <= valid_until + REASONABLY_LIVE_TIME); } /** As networkstatus_get_live_consensus(), but is way more tolerant of expired - * consensuses. */ + * and future consensuses. */ MOCK_IMPL(networkstatus_t *, networkstatus_get_reasonably_live_consensus,(time_t now, int flavor)) { networkstatus_t *consensus = networkstatus_get_latest_consensus_by_flavor(flavor); if (consensus && - consensus->valid_after <= now && networkstatus_consensus_reasonably_live(consensus, now)) return consensus; else @@ -1517,7 +1539,7 @@ networkstatus_consensus_can_use_extra_fallbacks,(const or_options_t *options)) >= smartlist_len(router_get_trusted_dir_servers())); /* If we don't fetch from the authorities, and we have additional mirrors, * we can use them. */ - return (!directory_fetches_from_authorities(options) + return (!dirclient_fetches_from_authorities(options) && (smartlist_len(router_get_fallback_dir_servers()) > smartlist_len(router_get_trusted_dir_servers()))); } @@ -1557,44 +1579,24 @@ networkstatus_consensus_is_already_downloading(const char *resource) return answer; } -/* Does the current, reasonably live consensus have IPv6 addresses? - * Returns 1 if there is a reasonably live consensus and its consensus method - * includes IPv6 addresses in the consensus. - * Otherwise, if there is no consensus, or the method does not include IPv6 - * addresses, returns 0. */ -int -networkstatus_consensus_has_ipv6(const or_options_t* options) -{ - const networkstatus_t *cons = networkstatus_get_reasonably_live_consensus( - approx_time(), - usable_consensus_flavor()); - - /* If we have no consensus, we have no IPv6 in it */ - if (!cons) { - return 0; - } - - /* Different flavours of consensus gained IPv6 at different times */ - if (we_use_microdescriptors_for_circuits(options)) { - return - cons->consensus_method >= MIN_METHOD_FOR_A_LINES_IN_MICRODESC_CONSENSUS; - } else { - return 1; - } -} - -/** Given two router status entries for the same router identity, return 1 if - * if the contents have changed between them. Otherwise, return 0. */ -static int -routerstatus_has_changed(const routerstatus_t *a, const routerstatus_t *b) +/** Given two router status entries for the same router identity, return 1 + * if the contents have changed between them. Otherwise, return 0. + * It only checks for fields that are output by control port. + * This should be kept in sync with the struct routerstatus_t + * and the printing function routerstatus_format_entry in + * NS_CONTROL_PORT mode. + **/ +STATIC int +routerstatus_has_visibly_changed(const routerstatus_t *a, + const routerstatus_t *b) { tor_assert(tor_memeq(a->identity_digest, b->identity_digest, DIGEST_LEN)); return strcmp(a->nickname, b->nickname) || fast_memneq(a->descriptor_digest, b->descriptor_digest, DIGEST_LEN) || - a->addr != b->addr || - a->or_port != b->or_port || - a->dir_port != b->dir_port || + !tor_addr_eq(&a->ipv4_addr, &b->ipv4_addr) || + a->ipv4_orport != b->ipv4_orport || + a->ipv4_dirport != b->ipv4_dirport || a->is_authority != b->is_authority || a->is_exit != b->is_exit || a->is_stable != b->is_stable || @@ -1605,9 +1607,14 @@ routerstatus_has_changed(const routerstatus_t *a, const routerstatus_t *b) a->is_valid != b->is_valid || a->is_possible_guard != b->is_possible_guard || a->is_bad_exit != b->is_bad_exit || - a->is_hs_dir != b->is_hs_dir; - // XXXX this function needs a huge refactoring; it has gotten out - // XXXX of sync with routerstatus_t, and it will do so again. + a->is_hs_dir != b->is_hs_dir || + a->is_staledesc != b->is_staledesc || + a->has_bandwidth != b->has_bandwidth || + a->published_on != b->published_on || + a->ipv6_orport != b->ipv6_orport || + a->is_v2_dir != b->is_v2_dir || + a->bandwidth_kb != b->bandwidth_kb || + tor_addr_compare(&a->ipv6_addr, &b->ipv6_addr, CMP_EXACT); } /** Notify controllers of any router status entries that changed between @@ -1639,7 +1646,7 @@ notify_control_networkstatus_changed(const networkstatus_t *old_c, tor_memcmp(rs_old->identity_digest, rs_new->identity_digest, DIGEST_LEN), smartlist_add(changed, (void*) rs_new)) { - if (routerstatus_has_changed(rs_old, rs_new)) + if (routerstatus_has_visibly_changed(rs_old, rs_new)) smartlist_add(changed, (void*)rs_new); } SMARTLIST_FOREACH_JOIN_END(rs_old, rs_new); @@ -1655,6 +1662,7 @@ notify_before_networkstatus_changes(const networkstatus_t *old_c, notify_control_networkstatus_changed(old_c, new_c); dos_consensus_has_changed(new_c); relay_consensus_has_changed(new_c); + hs_dos_consensus_has_changed(new_c); } /* Called after a new consensus has been put in the global state. It is safe @@ -1662,7 +1670,35 @@ notify_before_networkstatus_changes(const networkstatus_t *old_c, static void notify_after_networkstatus_changes(void) { + const networkstatus_t *c = networkstatus_get_latest_consensus(); + const or_options_t *options = get_options(); + const time_t now = approx_time(); + scheduler_notify_networkstatus_changed(); + + /* The "current" consensus has just been set and it is a usable flavor so + * the first thing we need to do is recalculate the voting schedule static + * object so we can use the timings in there needed by some subsystems + * such as hidden service and shared random. */ + dirauth_sched_recalculate_timing(options, now); + reschedule_dirvote(options); + + nodelist_set_consensus(c); + + update_consensus_networkstatus_fetch_time(now); + + /* Change the cell EWMA settings */ + cmux_ewma_set_options(options, c); + + /* XXXX this call might be unnecessary here: can changing the + * current consensus really alter our view of any OR's rate limits? */ + connection_or_update_token_buckets(get_connection_array(), options); + + circuit_build_times_new_consensus_params( + get_circuit_build_times_mutable(), c); + channelpadding_new_consensus_params(c); + circpad_new_consensus_params(c); + router_new_consensus_params(c); } /** Copy all the ancillary information (like router download status and so on) @@ -1725,6 +1761,44 @@ networkstatus_set_current_consensus_from_ns(networkstatus_t *c, #endif /* defined(TOR_UNIT_TESTS) */ /** + * Helper: Read the current consensus of type <b>flavor</b> from + * <b>fname</b>. Flags and return values are as for + * networkstatus_set_current_consensus(). + **/ +static int +reload_consensus_from_file(const char *fname, + const char *flavor, + unsigned flags, + const char *source_dir) +{ + tor_mmap_t *map = tor_mmap_file(fname); + if (!map) + return 0; + + int rv = networkstatus_set_current_consensus(map->data, map->size, + flavor, flags, source_dir); +#ifdef _WIN32 + if (rv < 0 && tor_memstr(map->data, map->size, "\r\n")) { + log_notice(LD_GENERAL, "Looks like the above failures are probably " + "because of a CRLF in consensus file %s; falling back to " + "read_file_to_string. Nothing to worry about: this file " + "was probably saved by an earlier version of Tor.", + escaped(fname)); + char *content = read_file_to_str(fname, RFTS_IGNORE_MISSING, NULL); + rv = networkstatus_set_current_consensus(content, strlen(content), + flavor, flags, source_dir); + tor_free(content); + } +#endif /* defined(_WIN32) */ + if (rv < -1) { + log_warn(LD_GENERAL, "Couldn't set consensus from cache file %s", + escaped(fname)); + } + tor_munmap_file(map); + return rv; +} + +/** * Helper for handle_missing_protocol_warning: handles either the * client case (if <b>is_client</b> is set) or the server case otherwise. */ @@ -1841,6 +1915,7 @@ warn_early_consensus(const networkstatus_t *c, const char *flavor, */ int networkstatus_set_current_consensus(const char *consensus, + size_t consensus_len, const char *flavor, unsigned flags, const char *source_dir) @@ -1869,7 +1944,9 @@ networkstatus_set_current_consensus(const char *consensus, } /* Make sure it's parseable. */ - c = networkstatus_parse_vote_from_string(consensus, NULL, NS_TYPE_CONSENSUS); + c = networkstatus_parse_vote_from_string(consensus, + consensus_len, + NULL, NS_TYPE_CONSENSUS); if (!c) { log_warn(LD_DIR, "Unable to parse networkstatus consensus"); result = -2; @@ -1957,14 +2034,12 @@ networkstatus_set_current_consensus(const char *consensus, c->valid_after > current_valid_after) { waiting = &consensus_waiting_for_certs[flav]; networkstatus_vote_free(waiting->consensus); - tor_free(waiting->body); waiting->consensus = c; free_consensus = 0; - waiting->body = tor_strdup(consensus); waiting->set_at = now; waiting->dl_failed = 0; if (!from_cache) { - write_str_to_file(unverified_fname, consensus, 0); + write_bytes_to_file(unverified_fname, consensus, consensus_len, 1); } if (dl_certs) authority_certs_fetch_missing(c, now, source_dir); @@ -1976,9 +2051,9 @@ networkstatus_set_current_consensus(const char *consensus, * latest consensus. */ if (was_waiting_for_certs && from_cache) if (unlink(unverified_fname) != 0) { - log_warn(LD_FS, - "Failed to unlink %s: %s", - unverified_fname, strerror(errno)); + log_debug(LD_FS, + "Failed to unlink %s: %s", + unverified_fname, strerror(errno)); } } goto done; @@ -1991,9 +2066,9 @@ networkstatus_set_current_consensus(const char *consensus, } if (was_waiting_for_certs && (r < -1) && from_cache) { if (unlink(unverified_fname) != 0) { - log_warn(LD_FS, - "Failed to unlink %s: %s", - unverified_fname, strerror(errno)); + log_debug(LD_FS, + "Failed to unlink %s: %s", + unverified_fname, strerror(errno)); } } goto done; @@ -2055,16 +2130,12 @@ networkstatus_set_current_consensus(const char *consensus, waiting->consensus->valid_after <= c->valid_after) { networkstatus_vote_free(waiting->consensus); waiting->consensus = NULL; - if (consensus != waiting->body) - tor_free(waiting->body); - else - waiting->body = NULL; waiting->set_at = 0; waiting->dl_failed = 0; if (unlink(unverified_fname) != 0) { - log_warn(LD_FS, - "Failed to unlink %s: %s", - unverified_fname, strerror(errno)); + log_debug(LD_FS, + "Failed to unlink %s: %s", + unverified_fname, strerror(errno)); } } @@ -2072,29 +2143,6 @@ networkstatus_set_current_consensus(const char *consensus, /* Notify that we just changed the consensus so the current global value * can be looked at. */ notify_after_networkstatus_changes(); - - /* The "current" consensus has just been set and it is a usable flavor so - * the first thing we need to do is recalculate the voting schedule static - * object so we can use the timings in there needed by some subsystems - * such as hidden service and shared random. */ - voting_schedule_recalculate_timing(options, now); - reschedule_dirvote(options); - - nodelist_set_consensus(c); - - /* XXXXNM Microdescs: needs a non-ns variant. ???? NM*/ - update_consensus_networkstatus_fetch_time(now); - - /* Change the cell EWMA settings */ - cmux_ewma_set_options(options, c); - - /* XXXX this call might be unnecessary here: can changing the - * current consensus really alter our view of any OR's rate limits? */ - connection_or_update_token_buckets(get_connection_array(), options); - - circuit_build_times_new_consensus_params( - get_circuit_build_times_mutable(), c); - channelpadding_new_consensus_params(c); } /* Reset the failure count only if this consensus is actually valid. */ @@ -2108,22 +2156,23 @@ networkstatus_set_current_consensus(const char *consensus, if (we_want_to_fetch_flavor(options, flav)) { if (dir_server_mode(get_options())) { dirserv_set_cached_consensus_networkstatus(consensus, + consensus_len, flavor, &c->digests, c->digest_sha3_as_signed, c->valid_after); - consdiffmgr_add_consensus(consensus, c); + consdiffmgr_add_consensus(consensus, consensus_len, c); } } if (!from_cache) { - write_str_to_file(consensus_fname, consensus, 0); + write_bytes_to_file(consensus_fname, consensus, consensus_len, 1); } warn_early_consensus(c, flavor, now); - /* We got a new consesus. Reset our md fetch fail cache */ + /* We got a new consensus. Reset our md fetch fail cache */ microdesc_reset_outdated_dirservers_list(); router_dir_info_changed(); @@ -2154,14 +2203,10 @@ networkstatus_note_certs_arrived(const char *source_dir) if (!waiting->consensus) continue; if (networkstatus_check_consensus_signature(waiting->consensus, 0)>=0) { - char *waiting_body = waiting->body; - if (!networkstatus_set_current_consensus( - waiting_body, - flavor_name, - NSSET_WAS_WAITING_FOR_CERTS, - source_dir)) { - tor_free(waiting_body); - } + char *fname = networkstatus_get_cache_fname(i, flavor_name, 1); + reload_consensus_from_file(fname, flavor_name, + NSSET_WAS_WAITING_FOR_CERTS, source_dir); + tor_free(fname); } } } @@ -2311,10 +2356,52 @@ char * networkstatus_getinfo_helper_single(const routerstatus_t *rs) { return routerstatus_format_entry(rs, NULL, NULL, NS_CONTROL_PORT, - ROUTERSTATUS_FORMAT_NO_CONSENSUS_METHOD, NULL); } +/** + * Extract status information from <b>ri</b> and from other authority + * functions and store it in <b>rs</b>. <b>rs</b> is zeroed out before it is + * set. + * + * We assume that node-\>is_running has already been set, e.g. by + * dirserv_set_router_is_running(ri, now); + */ +void +set_routerstatus_from_routerinfo(routerstatus_t *rs, + const node_t *node, + const routerinfo_t *ri) +{ + memset(rs, 0, sizeof(routerstatus_t)); + + rs->is_authority = + router_digest_is_trusted_dir(ri->cache_info.identity_digest); + + /* Set by compute_performance_thresholds or from consensus */ + rs->is_exit = node->is_exit; + rs->is_stable = node->is_stable; + rs->is_fast = node->is_fast; + rs->is_flagged_running = node->is_running; + rs->is_valid = node->is_valid; + rs->is_possible_guard = node->is_possible_guard; + rs->is_bad_exit = node->is_bad_exit; + rs->is_hs_dir = node->is_hs_dir; + rs->is_named = rs->is_unnamed = 0; + + rs->published_on = ri->cache_info.published_on; + memcpy(rs->identity_digest, node->identity, DIGEST_LEN); + memcpy(rs->descriptor_digest, ri->cache_info.signed_descriptor_digest, + DIGEST_LEN); + tor_addr_copy(&rs->ipv4_addr, &ri->ipv4_addr); + strlcpy(rs->nickname, ri->nickname, sizeof(rs->nickname)); + rs->ipv4_orport = ri->ipv4_orport; + rs->ipv4_dirport = ri->ipv4_dirport; + rs->is_v2_dir = ri->supports_tunnelled_dir_requests; + + tor_addr_copy(&rs->ipv6_addr, &ri->ipv6_addr); + rs->ipv6_orport = ri->ipv6_orport; +} + /** Alloc and return a string describing routerstatuses for the most * recent info of each router we know about that is of purpose * <b>purpose_string</b>. Return NULL if unrecognized purpose. @@ -2331,7 +2418,6 @@ networkstatus_getinfo_by_purpose(const char *purpose_string, time_t now) smartlist_t *statuses; const uint8_t purpose = router_purpose_from_string(purpose_string); routerstatus_t rs; - const int bridge_auth = authdir_mode_bridge(get_options()); if (purpose == ROUTER_PURPOSE_UNKNOWN) { log_info(LD_DIR, "Unrecognized purpose '%s' when listing router statuses.", @@ -2348,11 +2434,7 @@ networkstatus_getinfo_by_purpose(const char *purpose_string, time_t now) continue; if (ri->purpose != purpose) continue; - /* TODO: modifying the running flag in a getinfo is a bad idea */ - if (bridge_auth && ri->purpose == ROUTER_PURPOSE_BRIDGE) - dirserv_set_router_is_running(ri, now); - /* then generate and write out status lines for each of them */ - set_routerstatus_from_routerinfo(&rs, node, ri, now, 0); + set_routerstatus_from_routerinfo(&rs, node, ri); smartlist_add(statuses, networkstatus_getinfo_helper_single(&rs)); } SMARTLIST_FOREACH_END(ri); @@ -2362,42 +2444,12 @@ networkstatus_getinfo_by_purpose(const char *purpose_string, time_t now) return answer; } -/** Write out router status entries for all our bridge descriptors. */ -void -networkstatus_dump_bridge_status_to_file(time_t now) -{ - char *status = networkstatus_getinfo_by_purpose("bridge", now); - char *fname = NULL; - char *thresholds = NULL; - char *published_thresholds_and_status = NULL; - char published[ISO_TIME_LEN+1]; - const routerinfo_t *me = router_get_my_routerinfo(); - char fingerprint[FINGERPRINT_LEN+1]; - char *fingerprint_line = NULL; - - if (me && crypto_pk_get_fingerprint(me->identity_pkey, - fingerprint, 0) >= 0) { - tor_asprintf(&fingerprint_line, "fingerprint %s\n", fingerprint); - } else { - log_warn(LD_BUG, "Error computing fingerprint for bridge status."); - } - format_iso_time(published, now); - dirserv_compute_bridge_flag_thresholds(); - thresholds = dirserv_get_flag_thresholds_line(); - tor_asprintf(&published_thresholds_and_status, - "published %s\nflag-thresholds %s\n%s%s", - published, thresholds, fingerprint_line ? fingerprint_line : "", - status); - fname = get_datadir_fname("networkstatus-bridges"); - write_str_to_file(fname,published_thresholds_and_status,0); - tor_free(thresholds); - tor_free(published_thresholds_and_status); - tor_free(fname); - tor_free(status); - tor_free(fingerprint_line); -} - -/* DOCDOC get_net_param_from_list */ +/** + * Search through a smartlist of "key=int32" strings for a value beginning + * with "param_name=". If one is found, clip it to be between min_val and + * max_val inclusive and return it. If one is not found, return + * default_val. + ***/ static int32_t get_net_param_from_list(smartlist_t *net_params, const char *param_name, int32_t default_val, int32_t min_val, int32_t max_val) @@ -2668,6 +2720,16 @@ networkstatus_check_required_protocols(const networkstatus_t *ns, const char *required, *recommended; char *missing = NULL; + const bool consensus_postdates_this_release = + ns->valid_after >= tor_get_approx_release_date(); + + if (! consensus_postdates_this_release) { + // We can't meaningfully warn about this case: This consensus is from + // before we were released, so whatever is says about required or + // recommended versions may no longer be true. + return 0; + } + tor_assert(warning_out); if (client_mode) { @@ -2718,6 +2780,49 @@ networkstatus_free_all(void) networkstatus_vote_free(waiting->consensus); waiting->consensus = NULL; } - tor_free(waiting->body); } } + +/** Return the start of the next interval of size <b>interval</b> (in + * seconds) after <b>now</b>, plus <b>offset</b>. Midnight always + * starts a fresh interval, and if the last interval of a day would be + * truncated to less than half its size, it is rolled into the + * previous interval. */ +time_t +voting_sched_get_start_of_interval_after(time_t now, int interval, + int offset) +{ + struct tm tm; + time_t midnight_today=0; + time_t midnight_tomorrow; + time_t next; + + tor_gmtime_r(&now, &tm); + tm.tm_hour = 0; + tm.tm_min = 0; + tm.tm_sec = 0; + + if (tor_timegm(&tm, &midnight_today) < 0) { + // LCOV_EXCL_START + log_warn(LD_BUG, "Ran into an invalid time when trying to find midnight."); + // LCOV_EXCL_STOP + } + midnight_tomorrow = midnight_today + (24*60*60); + + next = midnight_today + ((now-midnight_today)/interval + 1)*interval; + + /* Intervals never cross midnight. */ + if (next > midnight_tomorrow) + next = midnight_tomorrow; + + /* If the interval would only last half as long as it's supposed to, then + * skip over to the next day. */ + if (next + interval/2 > midnight_tomorrow) + next = midnight_tomorrow; + + next += offset; + if (next - interval > now) + next -= interval; + + return next; +} diff --git a/src/feature/nodelist/networkstatus.h b/src/feature/nodelist/networkstatus.h index 9e7b0f1bb0..ce050aeadc 100644 --- a/src/feature/nodelist/networkstatus.h +++ b/src/feature/nodelist/networkstatus.h @@ -1,7 +1,7 @@ /* Copyright (c) 2001 Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2019, The Tor Project, Inc. */ + * Copyright (c) 2007-2020, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -16,7 +16,10 @@ void networkstatus_reset_warnings(void); void networkstatus_reset_download_failures(void); -char *networkstatus_read_cached_consensus(const char *flavorname); +MOCK_DECL(char *,networkstatus_get_cache_fname,(int flav, + const char *flavorname, + int unverified_consensus)); +tor_mmap_t *networkstatus_map_cached_consensus(const char *flavorname); int router_reload_consensus_networkstatus(void); void routerstatus_free_(routerstatus_t *rs); #define routerstatus_free(rs) \ @@ -40,8 +43,9 @@ int compare_digest_to_routerstatus_entry(const void *_key, const void **_member); int compare_digest_to_vote_routerstatus_entry(const void *_key, const void **_member); -const routerstatus_t *networkstatus_vote_find_entry(networkstatus_t *ns, - const char *digest); +MOCK_DECL(const routerstatus_t *,networkstatus_vote_find_entry,( + networkstatus_t *ns, + const char *digest)); routerstatus_t *networkstatus_vote_find_mutable_entry(networkstatus_t *ns, const char *digest); int networkstatus_vote_find_entry_idx(networkstatus_t *ns, @@ -87,6 +91,8 @@ MOCK_DECL(networkstatus_t *, networkstatus_get_live_consensus,(time_t now)); int networkstatus_is_live(const networkstatus_t *ns, time_t now); int networkstatus_consensus_reasonably_live(const networkstatus_t *consensus, time_t now); +int networkstatus_valid_after_is_reasonably_live(time_t valid_after, + time_t now); int networkstatus_valid_until_is_reasonably_live(time_t valid_until, time_t now); MOCK_DECL(networkstatus_t *,networkstatus_get_reasonably_live_consensus, @@ -98,7 +104,6 @@ int networkstatus_consensus_can_use_multiple_directories( MOCK_DECL(int, networkstatus_consensus_can_use_extra_fallbacks,( const or_options_t *options)); int networkstatus_consensus_is_already_downloading(const char *resource); -int networkstatus_consensus_has_ipv6(const or_options_t* options); #define NSSET_FROM_CACHE 1 #define NSSET_WAS_WAITING_FOR_CERTS 2 @@ -106,6 +111,7 @@ int networkstatus_consensus_has_ipv6(const or_options_t* options); #define NSSET_ACCEPT_OBSOLETE 8 #define NSSET_REQUIRE_FLAVOR 16 int networkstatus_set_current_consensus(const char *consensus, + size_t consensus_len, const char *flavor, unsigned flags, const char *source_dir); @@ -118,7 +124,6 @@ void signed_descs_update_status_from_consensus_networkstatus( char *networkstatus_getinfo_helper_single(const routerstatus_t *rs); char *networkstatus_getinfo_by_purpose(const char *purpose_string, time_t now); -void networkstatus_dump_bridge_status_to_file(time_t now); MOCK_DECL(int32_t, networkstatus_get_param, (const networkstatus_t *ns, const char *param_name, int32_t default_val, int32_t min_val, int32_t max_val)); @@ -145,6 +150,13 @@ void vote_routerstatus_free_(vote_routerstatus_t *rs); #define vote_routerstatus_free(rs) \ FREE_AND_NULL(vote_routerstatus_t, vote_routerstatus_free_, (rs)) +void set_routerstatus_from_routerinfo(routerstatus_t *rs, + const node_t *node, + const routerinfo_t *ri); +time_t voting_sched_get_start_of_interval_after(time_t now, + int interval, + int offset); + #ifdef NETWORKSTATUS_PRIVATE #ifdef TOR_UNIT_TESTS STATIC int networkstatus_set_current_consensus_from_ns(networkstatus_t *c, @@ -154,7 +166,8 @@ STATIC void warn_early_consensus(const networkstatus_t *c, const char *flavor, extern networkstatus_t *current_ns_consensus; extern networkstatus_t *current_md_consensus; #endif /* defined(TOR_UNIT_TESTS) */ +STATIC int routerstatus_has_visibly_changed(const routerstatus_t *a, + const routerstatus_t *b); #endif /* defined(NETWORKSTATUS_PRIVATE) */ #endif /* !defined(TOR_NETWORKSTATUS_H) */ - diff --git a/src/feature/nodelist/networkstatus_sr_info_st.h b/src/feature/nodelist/networkstatus_sr_info_st.h index 677d8ed811..04d0dfe8f6 100644 --- a/src/feature/nodelist/networkstatus_sr_info_st.h +++ b/src/feature/nodelist/networkstatus_sr_info_st.h @@ -1,9 +1,14 @@ /* Copyright (c) 2001 Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2019, The Tor Project, Inc. */ + * Copyright (c) 2007-2020, The Tor Project, Inc. */ /* See LICENSE for licensing information */ +/** + * @file networkstatus_sr_info_st.h + * @brief Shared-randomness structure. + **/ + #ifndef NETWORKSTATUS_SR_INFO_ST_H #define NETWORKSTATUS_SR_INFO_ST_H @@ -19,5 +24,4 @@ struct networkstatus_sr_info_t { smartlist_t *commits; }; -#endif - +#endif /* !defined(NETWORKSTATUS_SR_INFO_ST_H) */ diff --git a/src/feature/nodelist/networkstatus_st.h b/src/feature/nodelist/networkstatus_st.h index 6160f12361..021168d3ca 100644 --- a/src/feature/nodelist/networkstatus_st.h +++ b/src/feature/nodelist/networkstatus_st.h @@ -1,9 +1,14 @@ /* Copyright (c) 2001 Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2019, The Tor Project, Inc. */ + * Copyright (c) 2007-2020, The Tor Project, Inc. */ /* See LICENSE for licensing information */ +/** + * @file networkstatus_st.h + * @brief Networkstatus consensus/vote structure. + **/ + #ifndef NETWORKSTATUS_ST_H #define NETWORKSTATUS_ST_H @@ -99,6 +104,9 @@ struct networkstatus_t { /** List of key=value strings from the headers of the bandwidth list file */ smartlist_t *bw_file_headers; + + /** A SHA256 digest of the bandwidth file used in a vote. */ + uint8_t bw_file_digest256[DIGEST256_LEN]; }; -#endif +#endif /* !defined(NETWORKSTATUS_ST_H) */ diff --git a/src/feature/nodelist/networkstatus_voter_info_st.h b/src/feature/nodelist/networkstatus_voter_info_st.h index 4037fcdeca..a0fba2e1b5 100644 --- a/src/feature/nodelist/networkstatus_voter_info_st.h +++ b/src/feature/nodelist/networkstatus_voter_info_st.h @@ -1,9 +1,14 @@ /* Copyright (c) 2001 Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2019, The Tor Project, Inc. */ + * Copyright (c) 2007-2020, The Tor Project, Inc. */ /* See LICENSE for licensing information */ +/** + * @file networkstatus_voter_info_st.h + * @brief Single consensus voter structure. + **/ + #ifndef NETWORKSTATUS_VOTER_INFO_ST_H #define NETWORKSTATUS_VOTER_INFO_ST_H @@ -16,9 +21,9 @@ struct networkstatus_voter_info_t { * consensuses, we treat legacy keys as additional signers. */ char legacy_id_digest[DIGEST_LEN]; char *address; /**< Address of this voter, in string format. */ - uint32_t addr; /**< Address of this voter, in IPv4, in host order. */ - uint16_t dir_port; /**< Directory port of this voter */ - uint16_t or_port; /**< OR port of this voter */ + tor_addr_t ipv4_addr; + uint16_t ipv4_dirport; /**< Directory port of this voter */ + uint16_t ipv4_orport; /**< OR port of this voter */ char *contact; /**< Contact information for this voter. */ char vote_digest[DIGEST_LEN]; /**< Digest of this voter's vote, as signed. */ @@ -27,4 +32,4 @@ struct networkstatus_voter_info_t { smartlist_t *sigs; }; -#endif +#endif /* !defined(NETWORKSTATUS_VOTER_INFO_ST_H) */ diff --git a/src/feature/nodelist/nickname.c b/src/feature/nodelist/nickname.c index 5378b749ca..c022dd6bc4 100644 --- a/src/feature/nodelist/nickname.c +++ b/src/feature/nodelist/nickname.c @@ -1,7 +1,7 @@ /* Copyright (c) 2001 Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2019, The Tor Project, Inc. */ + * Copyright (c) 2007-2020, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** diff --git a/src/feature/nodelist/nickname.h b/src/feature/nodelist/nickname.h index 9bdc6b50e8..11c6416f3a 100644 --- a/src/feature/nodelist/nickname.h +++ b/src/feature/nodelist/nickname.h @@ -1,7 +1,7 @@ /* Copyright (c) 2001 Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2019, The Tor Project, Inc. */ + * Copyright (c) 2007-2020, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -16,4 +16,4 @@ int is_legal_nickname(const char *s); int is_legal_nickname_or_hexdigest(const char *s); int is_legal_hexdigest(const char *s); -#endif +#endif /* !defined(TOR_NICKNAME_H) */ diff --git a/src/feature/nodelist/node_select.c b/src/feature/nodelist/node_select.c index 7b9e241e5b..ecb70aef14 100644 --- a/src/feature/nodelist/node_select.c +++ b/src/feature/nodelist/node_select.c @@ -1,7 +1,7 @@ /* Copyright (c) 2001 Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2019, The Tor Project, Inc. */ + * Copyright (c) 2007-2020, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -19,6 +19,7 @@ #include "core/or/reasons.h" #include "feature/client/entrynodes.h" #include "feature/dirclient/dirclient.h" +#include "feature/dirclient/dirclient_modes.h" #include "feature/dircommon/directory.h" #include "feature/nodelist/describe.h" #include "feature/nodelist/dirlist.h" @@ -30,6 +31,7 @@ #include "feature/nodelist/routerset.h" #include "feature/relay/router.h" #include "feature/relay/routermode.h" +#include "lib/container/bitarray.h" #include "lib/crypt_ops/crypto_rand.h" #include "lib/math/fp.h" @@ -139,14 +141,14 @@ router_pick_dirserver_generic(smartlist_t *sourcelist, #define RETRY_ALTERNATE_IP_VERSION(retry_label) \ STMT_BEGIN \ if (result == NULL && try_ip_pref && options->ClientUseIPv4 \ - && fascist_firewall_use_ipv6(options) && !server_mode(options) \ + && reachable_addr_use_ipv6(options) && !server_mode(options) \ && !n_busy) { \ n_excluded = 0; \ n_busy = 0; \ try_ip_pref = 0; \ goto retry_label; \ } \ - STMT_END \ + STMT_END /* Common retry code for router_pick_directory_server_impl and * router_pick_trusteddirserver_impl. Retry without excluding nodes, but with @@ -210,18 +212,20 @@ router_picked_poor_directory_log(const routerstatus_t *rs) log_debug(LD_DIR, "Wanted to make an outgoing directory connection, but " "we couldn't find a directory that fit our criteria. " "Perhaps we will succeed next time with less strict criteria."); - } else if (!fascist_firewall_allows_rs(rs, FIREWALL_OR_CONNECTION, 1) - && !fascist_firewall_allows_rs(rs, FIREWALL_DIR_CONNECTION, 1) + } else if (!reachable_addr_allows_rs(rs, FIREWALL_OR_CONNECTION, 1) + && !reachable_addr_allows_rs(rs, FIREWALL_DIR_CONNECTION, 1) ) { /* This is rare, and might be interesting to users trying to diagnose * connection issues on dual-stack machines. */ + char *ipv4_str = tor_addr_to_str_dup(&rs->ipv4_addr); log_info(LD_DIR, "Selected a directory %s with non-preferred OR and Dir " "addresses for launching an outgoing connection: " "IPv4 %s OR %d Dir %d IPv6 %s OR %d Dir %d", routerstatus_describe(rs), - fmt_addr32(rs->addr), rs->or_port, - rs->dir_port, fmt_addr(&rs->ipv6_addr), - rs->ipv6_orport, rs->dir_port); + ipv4_str, rs->ipv4_orport, + rs->ipv4_dirport, fmt_addr(&rs->ipv6_addr), + rs->ipv6_orport, rs->ipv4_dirport); + tor_free(ipv4_str); } } @@ -264,7 +268,7 @@ router_is_already_dir_fetching(const tor_addr_port_t *ap, int serverdesc, * If so, return 1, if not, return 0. */ static int -router_is_already_dir_fetching_(uint32_t ipv4_addr, +router_is_already_dir_fetching_(const tor_addr_t *ipv4_addr, const tor_addr_t *ipv6_addr, uint16_t dir_port, int serverdesc, @@ -273,7 +277,7 @@ router_is_already_dir_fetching_(uint32_t ipv4_addr, tor_addr_port_t ipv4_dir_ap, ipv6_dir_ap; /* Assume IPv6 DirPort is the same as IPv4 DirPort */ - tor_addr_from_ipv4h(&ipv4_dir_ap.addr, ipv4_addr); + tor_addr_copy(&ipv4_dir_ap.addr, ipv4_addr); ipv4_dir_ap.port = dir_port; tor_addr_copy(&ipv6_dir_ap.addr, ipv6_addr); ipv6_dir_ap.port = dir_port; @@ -319,9 +323,13 @@ router_pick_directory_server_impl(dirinfo_type_t type, int flags, overloaded_direct = smartlist_new(); overloaded_tunnel = smartlist_new(); - const int skip_or_fw = router_skip_or_reachability(options, try_ip_pref); - const int skip_dir_fw = router_skip_dir_reachability(options, try_ip_pref); - const int must_have_or = directory_must_use_begindir(options); + const int skip_or_fw = router_or_conn_should_skip_reachable_address_check( + options, + try_ip_pref); + const int skip_dir_fw = router_dir_conn_should_skip_reachable_address_check( + options, + try_ip_pref); + const int must_have_or = dirclient_must_use_begindir(options); /* Find all the running dirservers we know about. */ SMARTLIST_FOREACH_BEGIN(nodelist_get_list(), const node_t *, node) { @@ -346,9 +354,9 @@ router_pick_directory_server_impl(dirinfo_type_t type, int flags, continue; } - if (router_is_already_dir_fetching_(status->addr, + if (router_is_already_dir_fetching_(&status->ipv4_addr, &status->ipv6_addr, - status->dir_port, + status->ipv4_dirport, no_serverdesc_fetching, no_microdesc_fetching)) { ++n_busy; @@ -366,12 +374,12 @@ router_pick_directory_server_impl(dirinfo_type_t type, int flags, * we try routers that only have one address both times.) */ if (!fascistfirewall || skip_or_fw || - fascist_firewall_allows_node(node, FIREWALL_OR_CONNECTION, + reachable_addr_allows_node(node, FIREWALL_OR_CONNECTION, try_ip_pref)) smartlist_add(is_trusted ? trusted_tunnel : is_overloaded ? overloaded_tunnel : tunnel, (void*)node); else if (!must_have_or && (skip_dir_fw || - fascist_firewall_allows_node(node, FIREWALL_DIR_CONNECTION, + reachable_addr_allows_node(node, FIREWALL_DIR_CONNECTION, try_ip_pref))) smartlist_add(is_trusted ? trusted_direct : is_overloaded ? overloaded_direct : direct, (void*)node); @@ -630,6 +638,7 @@ compute_weighted_bandwidths(const smartlist_t *sl, } weight_scale = networkstatus_get_weight_scale_param(NULL); + tor_assert(weight_scale >= 1); if (rule == WEIGHT_FOR_GUARD) { Wg = networkstatus_get_bw_weight(NULL, "Wgg", -1); @@ -871,90 +880,79 @@ routerlist_add_node_and_family(smartlist_t *sl, const routerinfo_t *router) nodelist_add_node_and_family(sl, node); } -/** Return a random running node from the nodelist. Never - * pick a node that is in - * <b>excludedsmartlist</b>, or which matches <b>excludedset</b>, - * even if they are the only nodes available. - * If <b>CRN_NEED_UPTIME</b> is set in flags and any router has more than - * a minimum uptime, return one of those. - * If <b>CRN_NEED_CAPACITY</b> is set in flags, weight your choice by the - * advertised capacity of each router. - * If <b>CRN_NEED_GUARD</b> is set in flags, consider only Guard routers. - * If <b>CRN_WEIGHT_AS_EXIT</b> is set in flags, we weight bandwidths as if - * picking an exit node, otherwise we weight bandwidths for picking a relay - * node (that is, possibly discounting exit nodes). - * If <b>CRN_NEED_DESC</b> is set in flags, we only consider nodes that - * have a routerinfo or microdescriptor -- that is, enough info to be - * used to build a circuit. - * If <b>CRN_PREF_ADDR</b> is set in flags, we only consider nodes that - * have an address that is preferred by the ClientPreferIPv6ORPort setting - * (regardless of this flag, we exclude nodes that aren't allowed by the - * firewall, including ClientUseIPv4 0 and fascist_firewall_use_ipv6() == 0). - */ -const node_t * -router_choose_random_node(smartlist_t *excludedsmartlist, - routerset_t *excludedset, - router_crn_flags_t flags) -{ /* XXXX MOVE */ - const int need_uptime = (flags & CRN_NEED_UPTIME) != 0; - const int need_capacity = (flags & CRN_NEED_CAPACITY) != 0; - const int need_guard = (flags & CRN_NEED_GUARD) != 0; - const int weight_for_exit = (flags & CRN_WEIGHT_AS_EXIT) != 0; - const int need_desc = (flags & CRN_NEED_DESC) != 0; - const int pref_addr = (flags & CRN_PREF_ADDR) != 0; - const int direct_conn = (flags & CRN_DIRECT_CONN) != 0; - const int rendezvous_v3 = (flags & CRN_RENDEZVOUS_V3) != 0; - - smartlist_t *sl=smartlist_new(), - *excludednodes=smartlist_new(); - const node_t *choice = NULL; - const routerinfo_t *r; - bandwidth_weight_rule_t rule; +/** + * Remove every node_t that appears in <b>excluded</b> from <b>sl</b>. + * + * Behaves like smartlist_subtract, but uses nodelist_idx values to deliver + * linear performance when smartlist_subtract would be quadratic. + **/ +static void +nodelist_subtract(smartlist_t *sl, const smartlist_t *excluded) +{ + const smartlist_t *nodelist = nodelist_get_list(); + const int nodelist_len = smartlist_len(nodelist); + bitarray_t *excluded_idx = bitarray_init_zero(nodelist_len); - tor_assert(!(weight_for_exit && need_guard)); - rule = weight_for_exit ? WEIGHT_FOR_EXIT : - (need_guard ? WEIGHT_FOR_GUARD : WEIGHT_FOR_MID); - - SMARTLIST_FOREACH_BEGIN(nodelist_get_list(), node_t *, node) { - if (node_allows_single_hop_exits(node)) { - /* Exclude relays that allow single hop exit circuits. This is an - * obsolete option since 0.2.9.2-alpha and done by default in - * 0.3.1.0-alpha. */ - smartlist_add(excludednodes, node); - } else if (rendezvous_v3 && - !node_supports_v3_rendezvous_point(node)) { - /* Exclude relays that do not support to rendezvous for a hidden service - * version 3. */ - smartlist_add(excludednodes, node); + /* We haven't used nodelist_idx in this way previously, so I'm going to be + * paranoid in this code, and check that nodelist_idx is correct for every + * node before we use it. If we fail, we fall back to smartlist_subtract(). + */ + + /* Set the excluded_idx bit corresponding to every excluded node... + */ + SMARTLIST_FOREACH_BEGIN(excluded, const node_t *, node) { + const int idx = node->nodelist_idx; + if (BUG(idx < 0) || BUG(idx >= nodelist_len) || + BUG(node != smartlist_get(nodelist, idx))) { + goto internal_error; } + bitarray_set(excluded_idx, idx); } SMARTLIST_FOREACH_END(node); - /* If the node_t is not found we won't be to exclude ourself but we - * won't be able to pick ourself in router_choose_random_node() so - * this is fine to at least try with our routerinfo_t object. */ - if ((r = router_get_my_routerinfo())) - routerlist_add_node_and_family(excludednodes, r); + /* Then remove them from sl. + */ + SMARTLIST_FOREACH_BEGIN(sl, const node_t *, node) { + const int idx = node->nodelist_idx; + if (BUG(idx < 0) || BUG(idx >= nodelist_len) || + BUG(node != smartlist_get(nodelist, idx))) { + goto internal_error; + } + if (bitarray_is_set(excluded_idx, idx)) { + SMARTLIST_DEL_CURRENT(sl, node); + } + } SMARTLIST_FOREACH_END(node); + + bitarray_free(excluded_idx); + return; + + internal_error: + log_warn(LD_BUG, "Internal error prevented us from using the fast method " + "for subtracting nodelists. Falling back to the quadratic way."); + smartlist_subtract(sl, excluded); + bitarray_free(excluded_idx); +} + +/* Node selection helper for router_choose_random_node(). + * + * Populates a node list based on <b>flags</b>, ignoring nodes in + * <b>excludednodes</b> and <b>excludedset</b>. Chooses the node based on + * <b>rule</b>. */ +static const node_t * +router_choose_random_node_helper(smartlist_t *excludednodes, + routerset_t *excludedset, + router_crn_flags_t flags, + bandwidth_weight_rule_t rule) +{ + smartlist_t *sl=smartlist_new(); + const node_t *choice = NULL; - router_add_running_nodes_to_smartlist(sl, need_uptime, need_capacity, - need_guard, need_desc, pref_addr, - direct_conn); + router_add_running_nodes_to_smartlist(sl, flags); log_debug(LD_CIRC, "We found %d running nodes.", smartlist_len(sl)); - smartlist_subtract(sl,excludednodes); - log_debug(LD_CIRC, - "We removed %d excludednodes, leaving %d nodes.", - smartlist_len(excludednodes), - smartlist_len(sl)); + nodelist_subtract(sl, excludednodes); - if (excludedsmartlist) { - smartlist_subtract(sl,excludedsmartlist); - log_debug(LD_CIRC, - "We removed %d excludedsmartlist, leaving %d nodes.", - smartlist_len(excludedsmartlist), - smartlist_len(sl)); - } if (excludedset) { routerset_subtract_nodes(sl,excludedset); log_debug(LD_CIRC, @@ -966,18 +964,66 @@ router_choose_random_node(smartlist_t *excludedsmartlist, choice = node_sl_choose_by_bandwidth(sl, rule); smartlist_free(sl); + + return choice; +} + +/** Return a random running node from the nodelist. Never pick a node that is + * in <b>excludedsmartlist</b>, or which matches <b>excludedset</b>, even if + * they are the only nodes available. + * + * <b>flags</b> is a set of CRN_* flags, see + * router_add_running_nodes_to_smartlist() for details. + */ +const node_t * +router_choose_random_node(smartlist_t *excludedsmartlist, + routerset_t *excludedset, + router_crn_flags_t flags) +{ + /* A limited set of flags, used for fallback node selection. + */ + const bool need_uptime = (flags & CRN_NEED_UPTIME) != 0; + const bool need_capacity = (flags & CRN_NEED_CAPACITY) != 0; + const bool need_guard = (flags & CRN_NEED_GUARD) != 0; + const bool pref_addr = (flags & CRN_PREF_ADDR) != 0; + + smartlist_t *excludednodes=smartlist_new(); + const node_t *choice = NULL; + const routerinfo_t *r; + bandwidth_weight_rule_t rule; + + rule = (need_guard ? WEIGHT_FOR_GUARD : WEIGHT_FOR_MID); + + /* If the node_t is not found we won't be to exclude ourself but we + * won't be able to pick ourself in router_choose_random_node() so + * this is fine to at least try with our routerinfo_t object. */ + if ((r = router_get_my_routerinfo())) + routerlist_add_node_and_family(excludednodes, r); + + if (excludedsmartlist) { + smartlist_add_all(excludednodes, excludedsmartlist); + } + + choice = router_choose_random_node_helper(excludednodes, + excludedset, + flags, + rule); + if (!choice && (need_uptime || need_capacity || need_guard || pref_addr)) { - /* try once more -- recurse but with fewer restrictions. */ + /* try once more, with fewer restrictions. */ log_info(LD_CIRC, - "We couldn't find any live%s%s%s routers; falling back " + "We couldn't find any live%s%s%s%s routers; falling back " "to list of all routers.", need_capacity?", fast":"", need_uptime?", stable":"", - need_guard?", guard":""); + need_guard?", guard":"", + pref_addr?", preferred address":""); flags &= ~ (CRN_NEED_UPTIME|CRN_NEED_CAPACITY|CRN_NEED_GUARD| CRN_PREF_ADDR); - choice = router_choose_random_node( - excludedsmartlist, excludedset, flags); + choice = router_choose_random_node_helper(excludednodes, + excludedset, + flags, + rule); } smartlist_free(excludednodes); if (!choice) { @@ -1072,9 +1118,13 @@ router_pick_trusteddirserver_impl(const smartlist_t *sourcelist, overloaded_direct = smartlist_new(); overloaded_tunnel = smartlist_new(); - const int skip_or_fw = router_skip_or_reachability(options, try_ip_pref); - const int skip_dir_fw = router_skip_dir_reachability(options, try_ip_pref); - const int must_have_or = directory_must_use_begindir(options); + const int skip_or_fw = router_or_conn_should_skip_reachable_address_check( + options, + try_ip_pref); + const int skip_dir_fw = router_dir_conn_should_skip_reachable_address_check( + options, + try_ip_pref); + const int must_have_or = dirclient_must_use_begindir(options); SMARTLIST_FOREACH_BEGIN(sourcelist, const dir_server_t *, d) { @@ -1095,9 +1145,9 @@ router_pick_trusteddirserver_impl(const smartlist_t *sourcelist, continue; } - if (router_is_already_dir_fetching_(d->addr, + if (router_is_already_dir_fetching_(&d->ipv4_addr, &d->ipv6_addr, - d->dir_port, + d->ipv4_dirport, no_serverdesc_fetching, no_microdesc_fetching)) { ++n_busy; @@ -1112,11 +1162,11 @@ router_pick_trusteddirserver_impl(const smartlist_t *sourcelist, * we try routers that only have one address both times.) */ if (!fascistfirewall || skip_or_fw || - fascist_firewall_allows_dir_server(d, FIREWALL_OR_CONNECTION, + reachable_addr_allows_dir_server(d, FIREWALL_OR_CONNECTION, try_ip_pref)) smartlist_add(is_overloaded ? overloaded_tunnel : tunnel, (void*)d); else if (!must_have_or && (skip_dir_fw || - fascist_firewall_allows_dir_server(d, FIREWALL_DIR_CONNECTION, + reachable_addr_allows_dir_server(d, FIREWALL_DIR_CONNECTION, try_ip_pref))) smartlist_add(is_overloaded ? overloaded_direct : direct, (void*)d); } diff --git a/src/feature/nodelist/node_select.h b/src/feature/nodelist/node_select.h index ed7450b92c..1776d8ea1a 100644 --- a/src/feature/nodelist/node_select.h +++ b/src/feature/nodelist/node_select.h @@ -1,6 +1,6 @@ /* Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2019, The Tor Project, Inc. */ + * Copyright (c) 2007-2020, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -14,20 +14,26 @@ /** Flags to be passed to control router_choose_random_node() to indicate what * kind of nodes to pick according to what algorithm. */ typedef enum router_crn_flags_t { + /* Try to choose stable nodes. */ CRN_NEED_UPTIME = 1<<0, + /* Try to choose nodes with a reasonable amount of bandwidth. */ CRN_NEED_CAPACITY = 1<<1, - CRN_NEED_GUARD = 1<<2, - /* XXXX not used, apparently. */ - CRN_WEIGHT_AS_EXIT = 1<<5, - CRN_NEED_DESC = 1<<6, - /* On clients, only provide nodes that satisfy ClientPreferIPv6OR */ - CRN_PREF_ADDR = 1<<7, + /* Only choose nodes if we have downloaded their descriptor or + * microdescriptor. */ + CRN_NEED_DESC = 1<<2, + /* Choose nodes that can be used as Guard relays. */ + CRN_NEED_GUARD = 1<<3, /* On clients, only provide nodes that we can connect to directly, based on - * our firewall rules */ - CRN_DIRECT_CONN = 1<<8, - /* On clients, only provide nodes with HSRend >= 2 protocol version which - * is required for hidden service version >= 3. */ - CRN_RENDEZVOUS_V3 = 1<<9, + * our firewall rules. */ + CRN_DIRECT_CONN = 1<<4, + /* On clients, if choosing a node for a direct connection, only provide + * nodes that satisfy ClientPreferIPv6OR. */ + CRN_PREF_ADDR = 1<<5, + /* On clients, only provide nodes with HSRend=2 protocol version which + * is required for hidden service version 3. */ + CRN_RENDEZVOUS_V3 = 1<<6, + /* On clients, only provide nodes that can initiate IPv6 extends. */ + CRN_INITIATE_IPV6_EXTEND = 1<<7, } router_crn_flags_t; /** Possible ways to weight routers when choosing one randomly. See @@ -97,6 +103,6 @@ STATIC const routerstatus_t *router_pick_directory_server_impl( int *n_busy_out); STATIC int router_is_already_dir_fetching(const tor_addr_port_t *ap, int serverdesc, int microdesc); -#endif +#endif /* defined(NODE_SELECT_PRIVATE) */ -#endif +#endif /* !defined(TOR_NODE_SELECT_H) */ diff --git a/src/feature/nodelist/node_st.h b/src/feature/nodelist/node_st.h index 53ffde29e4..3769f9dc84 100644 --- a/src/feature/nodelist/node_st.h +++ b/src/feature/nodelist/node_st.h @@ -1,14 +1,20 @@ /* Copyright (c) 2001 Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2019, The Tor Project, Inc. */ + * Copyright (c) 2007-2020, The Tor Project, Inc. */ /* See LICENSE for licensing information */ +/** + * @file node_st.h + * @brief Node information structure. + **/ + #ifndef NODE_ST_H #define NODE_ST_H #include "feature/hs/hsdir_index_st.h" #include "lib/crypt_ops/crypto_ed25519.h" +#include "ext/ht.h" /** A node_t represents a Tor router. * @@ -78,12 +84,11 @@ struct node_t { /* Local info: derived. */ - /** True if the IPv6 OR port is preferred over the IPv4 OR port. - * XX/teor - can this become out of date if the torrc changes? */ + /** True if the IPv6 OR port is preferred over the IPv4 OR port. */ unsigned int ipv6_preferred:1; /** According to the geoip db what country is this router in? */ - /* XXXprop186 what is this suppose to mean with multiple OR ports? */ + /* IPv6: what is this supposed to mean with multiple OR ports? */ country_t country; /* The below items are used only by authdirservers for @@ -99,4 +104,4 @@ struct node_t { struct hsdir_index_t hsdir_index; }; -#endif +#endif /* !defined(NODE_ST_H) */ diff --git a/src/feature/nodelist/nodefamily.c b/src/feature/nodelist/nodefamily.c new file mode 100644 index 0000000000..feaa3730dc --- /dev/null +++ b/src/feature/nodelist/nodefamily.c @@ -0,0 +1,416 @@ +/* Copyright (c) 2001 Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2020, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file nodefamily.c + * \brief Code to manipulate encoded, reference-counted node families. We + * use these tricks to save space, since these families would otherwise + * require a large number of tiny allocations. + **/ + +#include "core/or/or.h" +#include "feature/nodelist/nickname.h" +#include "feature/nodelist/nodefamily.h" +#include "feature/nodelist/nodefamily_st.h" +#include "feature/nodelist/nodelist.h" +#include "feature/relay/router.h" +#include "feature/nodelist/routerlist.h" + +#include "ht.h" +#include "siphash.h" + +#include "lib/container/smartlist.h" +#include "lib/ctime/di_ops.h" +#include "lib/defs/digest_sizes.h" +#include "lib/log/util_bug.h" + +#include <stdlib.h> +#include <string.h> + +/** + * Allocate and return a blank node family with space to hold <b>n_members</b> + * members. + */ +static nodefamily_t * +nodefamily_alloc(int n_members) +{ + size_t alloc_len = offsetof(nodefamily_t, family_members) + + NODEFAMILY_ARRAY_SIZE(n_members); + nodefamily_t *nf = tor_malloc_zero(alloc_len); + nf->n_members = n_members; + return nf; +} + +/** + * Hashtable hash implementation. + */ +static inline unsigned int +nodefamily_hash(const nodefamily_t *nf) +{ + return (unsigned) siphash24g(nf->family_members, + NODEFAMILY_ARRAY_SIZE(nf->n_members)); +} + +/** + * Hashtable equality implementation. + */ +static inline unsigned int +nodefamily_eq(const nodefamily_t *a, const nodefamily_t *b) +{ + return (a->n_members == b->n_members) && + fast_memeq(a->family_members, b->family_members, + NODEFAMILY_ARRAY_SIZE(a->n_members)); +} + +static HT_HEAD(nodefamily_map, nodefamily_t) the_node_families + = HT_INITIALIZER(); + +HT_PROTOTYPE(nodefamily_map, nodefamily_t, ht_ent, nodefamily_hash, + nodefamily_eq); +HT_GENERATE2(nodefamily_map, nodefamily_t, ht_ent, nodefamily_hash, + node_family_eq, 0.6, tor_reallocarray_, tor_free_); + +/** + * Parse the family declaration in <b>s</b>, returning the canonical + * <b>nodefamily_t</b> for its members. Return NULL on error. + * + * If <b>rsa_id_self</b> is provided, it is a DIGEST_LEN-byte digest + * for the router that declared this family: insert it into the + * family declaration if it is not there already. + * + * If NF_WARN_MALFORMED is set in <b>flags</b>, warn about any + * elements that we can't parse. (By default, we log at info.) + * + * If NF_REJECT_MALFORMED is set in <b>flags</b>, treat any unparseable + * elements as an error. (By default, we simply omit them.) + **/ +nodefamily_t * +nodefamily_parse(const char *s, const uint8_t *rsa_id_self, + unsigned flags) +{ + smartlist_t *sl = smartlist_new(); + smartlist_split_string(sl, s, NULL, SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); + nodefamily_t *result = nodefamily_from_members(sl, rsa_id_self, flags, NULL); + SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp)); + smartlist_free(sl); + return result; +} + +/** + * Canonicalize the family list <b>s</b>, returning a newly allocated string. + * + * The canonicalization rules are fully specified in dir-spec.txt, but, + * briefly: $hexid entries are put in caps, $hexid[=~]foo entries are + * truncated, nicknames are put into lowercase, unrecognized entries are left + * alone, and everything is sorted. + **/ +char * +nodefamily_canonicalize(const char *s, const uint8_t *rsa_id_self, + unsigned flags) +{ + smartlist_t *sl = smartlist_new(); + smartlist_t *result_members = smartlist_new(); + smartlist_split_string(sl, s, NULL, SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); + nodefamily_t *nf = nodefamily_from_members(sl, rsa_id_self, flags, + result_members); + + char *formatted = nodefamily_format(nf); + smartlist_split_string(result_members, formatted, NULL, + SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); + smartlist_sort_strings(result_members); + char *combined = smartlist_join_strings(result_members, " ", 0, NULL); + + nodefamily_free(nf); + SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp)); + smartlist_free(sl); + SMARTLIST_FOREACH(result_members, char *, cp, tor_free(cp)); + smartlist_free(result_members); + tor_free(formatted); + + return combined; +} + +/** + * qsort helper for encoded nodefamily elements. + **/ +static int +compare_members(const void *a, const void *b) +{ + return fast_memcmp(a, b, NODEFAMILY_MEMBER_LEN); +} + +/** + * Parse the member strings in <b>members</b>, returning their canonical + * <b>nodefamily_t</b>. Return NULL on error. + * + * If <b>rsa_id_self</b> is provided, it is a DIGEST_LEN-byte digest + * for the router that declared this family: insert it into the + * family declaration if it is not there already. + * + * The <b>flags</b> element is interpreted as in nodefamily_parse(). + * + * If <b>unrecognized</b> is provided, fill it copies of any unrecognized + * members. (Note that malformed $hexids are not considered unrecognized.) + **/ +nodefamily_t * +nodefamily_from_members(const smartlist_t *members, + const uint8_t *rsa_id_self, + unsigned flags, + smartlist_t *unrecognized_out) +{ + const int n_self = rsa_id_self ? 1 : 0; + int n_bad_elements = 0; + int n_members = smartlist_len(members) + n_self; + nodefamily_t *tmp = nodefamily_alloc(n_members); + uint8_t *ptr = NODEFAMILY_MEMBER_PTR(tmp, 0); + + SMARTLIST_FOREACH_BEGIN(members, const char *, cp) { + bool bad_element = true; + if (is_legal_nickname(cp)) { + ptr[0] = NODEFAMILY_BY_NICKNAME; + tor_assert(strlen(cp) < DIGEST_LEN); // guaranteed by is_legal_nickname + memcpy(ptr+1, cp, strlen(cp)); + tor_strlower((char*) ptr+1); + bad_element = false; + } else if (is_legal_hexdigest(cp)) { + char digest_buf[DIGEST_LEN]; + char nn_buf[MAX_NICKNAME_LEN+1]; + char nn_char=0; + if (hex_digest_nickname_decode(cp, digest_buf, &nn_char, nn_buf)==0) { + bad_element = false; + ptr[0] = NODEFAMILY_BY_RSA_ID; + memcpy(ptr+1, digest_buf, DIGEST_LEN); + } + } else { + if (unrecognized_out) + smartlist_add_strdup(unrecognized_out, cp); + } + + if (bad_element) { + const int severity = (flags & NF_WARN_MALFORMED) ? LOG_WARN : LOG_INFO; + log_fn(severity, LD_GENERAL, + "Bad element %s while parsing a node family.", + escaped(cp)); + ++n_bad_elements; + } else { + ptr += NODEFAMILY_MEMBER_LEN; + } + } SMARTLIST_FOREACH_END(cp); + + if (n_bad_elements && (flags & NF_REJECT_MALFORMED)) + goto err; + + if (rsa_id_self) { + /* Add self. */ + ptr[0] = NODEFAMILY_BY_RSA_ID; + memcpy(ptr+1, rsa_id_self, DIGEST_LEN); + } + + n_members -= n_bad_elements; + + /* Sort tmp into canonical order. */ + qsort(tmp->family_members, n_members, NODEFAMILY_MEMBER_LEN, + compare_members); + + /* Remove duplicates. */ + int i; + for (i = 0; i < n_members-1; ++i) { + uint8_t *thisptr = NODEFAMILY_MEMBER_PTR(tmp, i); + uint8_t *nextptr = NODEFAMILY_MEMBER_PTR(tmp, i+1); + if (fast_memeq(thisptr, nextptr, NODEFAMILY_MEMBER_LEN)) { + memmove(thisptr, nextptr, (n_members-i-1)*NODEFAMILY_MEMBER_LEN); + --n_members; + --i; + } + } + int n_members_alloc = tmp->n_members; + tmp->n_members = n_members; + + /* See if we already allocated this family. */ + nodefamily_t *found = HT_FIND(nodefamily_map, &the_node_families, tmp); + if (found) { + /* If we did, great: incref it and return it. */ + ++found->refcnt; + tor_free(tmp); + return found; + } else { + /* If not, insert it into the hashtable. */ + if (n_members_alloc != n_members) { + /* Compact the family if needed */ + nodefamily_t *tmp2 = nodefamily_alloc(n_members); + memcpy(tmp2->family_members, tmp->family_members, + n_members * NODEFAMILY_MEMBER_LEN); + tor_free(tmp); + tmp = tmp2; + } + + tmp->refcnt = 1; + HT_INSERT(nodefamily_map, &the_node_families, tmp); + return tmp; + } + + err: + tor_free(tmp); + return NULL; +} + +/** + * Drop our reference to <b>family</b>, freeing it if there are no more + * references. + */ +void +nodefamily_free_(nodefamily_t *family) +{ + if (family == NULL) + return; + + --family->refcnt; + + if (family->refcnt == 0) { + HT_REMOVE(nodefamily_map, &the_node_families, family); + tor_free(family); + } +} + +/** + * Return true iff <b>family</b> contains the SHA1 RSA1024 identity + * <b>rsa_id</b>. + */ +bool +nodefamily_contains_rsa_id(const nodefamily_t *family, + const uint8_t *rsa_id) +{ + if (family == NULL) + return false; + + unsigned i; + for (i = 0; i < family->n_members; ++i) { + const uint8_t *ptr = NODEFAMILY_MEMBER_PTR(family, i); + if (ptr[0] == NODEFAMILY_BY_RSA_ID && + fast_memeq(ptr+1, rsa_id, DIGEST_LEN)) { + return true; + } + } + return false; +} + +/** + * Return true iff <b>family</b> contains the nickname <b>name</b>. + */ +bool +nodefamily_contains_nickname(const nodefamily_t *family, + const char *name) +{ + if (family == NULL) + return false; + + unsigned i; + for (i = 0; i < family->n_members; ++i) { + const uint8_t *ptr = NODEFAMILY_MEMBER_PTR(family, i); + // note that the strcasecmp() is safe because there is always at least one + // NUL in the encoded nickname, because all legal nicknames are less than + // DIGEST_LEN bytes long. + if (ptr[0] == NODEFAMILY_BY_NICKNAME && !strcasecmp((char*)ptr+1, name)) { + return true; + } + } + return false; +} + +/** + * Return true if <b>family</b> contains the nickname or the RSA ID for + * <b>node</b> + **/ +bool +nodefamily_contains_node(const nodefamily_t *family, + const node_t *node) +{ + return + nodefamily_contains_nickname(family, node_get_nickname(node)) + || + nodefamily_contains_rsa_id(family, node_get_rsa_id_digest(node)); +} + +/** + * Look up every entry in <b>family</b>, and add add the corresponding + * node_t to <b>out</b>. + **/ +void +nodefamily_add_nodes_to_smartlist(const nodefamily_t *family, + smartlist_t *out) +{ + if (!family) + return; + + unsigned i; + for (i = 0; i < family->n_members; ++i) { + const uint8_t *ptr = NODEFAMILY_MEMBER_PTR(family, i); + const node_t *node = NULL; + switch (ptr[0]) { + case NODEFAMILY_BY_NICKNAME: + node = node_get_by_nickname((char*)ptr+1, NNF_NO_WARN_UNNAMED); + break; + case NODEFAMILY_BY_RSA_ID: + node = node_get_by_id((char*)ptr+1); + break; + default: + /* LCOV_EXCL_START */ + tor_assert_nonfatal_unreached(); + break; + /* LCOV_EXCL_STOP */ + } + if (node) + smartlist_add(out, (void *)node); + } +} + +/** + * Encode <b>family</b> as a space-separated string. + */ +char * +nodefamily_format(const nodefamily_t *family) +{ + if (!family) + return tor_strdup(""); + + unsigned i; + smartlist_t *sl = smartlist_new(); + for (i = 0; i < family->n_members; ++i) { + const uint8_t *ptr = NODEFAMILY_MEMBER_PTR(family, i); + switch (ptr[0]) { + case NODEFAMILY_BY_NICKNAME: + smartlist_add_strdup(sl, (char*)ptr+1); + break; + case NODEFAMILY_BY_RSA_ID: { + char buf[HEX_DIGEST_LEN+2]; + buf[0]='$'; + base16_encode(buf+1, sizeof(buf)-1, (char*)ptr+1, DIGEST_LEN); + tor_strupper(buf); + smartlist_add_strdup(sl, buf); + break; + } + default: + /* LCOV_EXCL_START */ + tor_assert_nonfatal_unreached(); + break; + /* LCOV_EXCL_STOP */ + } + } + + char *result = smartlist_join_strings(sl, " ", 0, NULL); + SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp)); + smartlist_free(sl); + return result; +} + +/** + * Free all storage held in the nodefamily map. + **/ +void +nodefamily_free_all(void) +{ + HT_CLEAR(nodefamily_map, &the_node_families); +} diff --git a/src/feature/nodelist/nodefamily.h b/src/feature/nodelist/nodefamily.h new file mode 100644 index 0000000000..16e161ba82 --- /dev/null +++ b/src/feature/nodelist/nodefamily.h @@ -0,0 +1,50 @@ +/* Copyright (c) 2001 Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2020, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file nodefamily.h + * \brief Header file for nodefamily.c. + **/ + +#ifndef TOR_NODEFAMILY_H +#define TOR_NODEFAMILY_H + +#include "lib/malloc/malloc.h" +#include <stdbool.h> + +typedef struct nodefamily_t nodefamily_t; +struct node_t; +struct smartlist_t; + +#define NF_WARN_MALFORMED (1u<<0) +#define NF_REJECT_MALFORMED (1u<<1) + +nodefamily_t *nodefamily_parse(const char *s, + const uint8_t *rsa_id_self, + unsigned flags); +nodefamily_t *nodefamily_from_members(const struct smartlist_t *members, + const uint8_t *rsa_id_self, + unsigned flags, + smartlist_t *unrecognized_out); +void nodefamily_free_(nodefamily_t *family); +#define nodefamily_free(family) \ + FREE_AND_NULL(nodefamily_t, nodefamily_free_, (family)) + +bool nodefamily_contains_rsa_id(const nodefamily_t *family, + const uint8_t *rsa_id); +bool nodefamily_contains_nickname(const nodefamily_t *family, + const char *name); +bool nodefamily_contains_node(const nodefamily_t *family, + const struct node_t *node); +void nodefamily_add_nodes_to_smartlist(const nodefamily_t *family, + struct smartlist_t *out); +char *nodefamily_format(const nodefamily_t *family); +char *nodefamily_canonicalize(const char *s, const uint8_t *rsa_id_self, + unsigned flags); + +void nodefamily_free_all(void); + +#endif /* !defined(TOR_NODEFAMILY_H) */ diff --git a/src/feature/nodelist/nodefamily_st.h b/src/feature/nodelist/nodefamily_st.h new file mode 100644 index 0000000000..c581c917a9 --- /dev/null +++ b/src/feature/nodelist/nodefamily_st.h @@ -0,0 +1,53 @@ +/* Copyright (c) 2001 Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2020, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * @file nodefamily_st.h + * @brief Compact node-family structure + **/ + +#ifndef TOR_NODEFAMILY_ST_H +#define TOR_NODEFAMILY_ST_H + +#include "orconfig.h" +#include "ht.h" + +struct nodefamily_t { + /** Entry for this nodefamily_t within the hashtable. */ + HT_ENTRY(nodefamily_t) ht_ent; + /** Reference count. (The hashtable is not treated as a reference */ + uint32_t refcnt; + /** Number of items encoded in <b>family_members</b>. */ + uint32_t n_members; + /* A byte-array encoding the members of this family. We encode each member + * as one byte to indicate whether it's a nickname or a fingerprint, plus + * DIGEST_LEN bytes of data. The entries are lexically sorted. + */ + uint8_t family_members[FLEXIBLE_ARRAY_MEMBER]; +}; + +#define NODEFAMILY_MEMBER_LEN (1+DIGEST_LEN) + +/** Tag byte, indicates that the following bytes are a RSA1024 SHA1 ID. + */ +#define NODEFAMILY_BY_RSA_ID 0 +/** Tag byte, indicates that the following bytes are a NUL-padded nickname. + */ +#define NODEFAMILY_BY_NICKNAME 1 + +/** + * Number of bytes to allocate in the array for a nodefamily_t with N members. + **/ +#define NODEFAMILY_ARRAY_SIZE(n) \ + ((n) * NODEFAMILY_MEMBER_LEN) + +/** + * Pointer to the i'th member of <b>nf</b>, as encoded. + */ +#define NODEFAMILY_MEMBER_PTR(nf, i) \ + (&((nf)->family_members[(i) * NODEFAMILY_MEMBER_LEN])) + +#endif /* !defined(TOR_NODEFAMILY_ST_H) */ diff --git a/src/feature/nodelist/nodelist.c b/src/feature/nodelist/nodelist.c index 9d56f2c3aa..03b158e68d 100644 --- a/src/feature/nodelist/nodelist.c +++ b/src/feature/nodelist/nodelist.c @@ -1,7 +1,7 @@ /* Copyright (c) 2001 Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2019, The Tor Project, Inc. */ + * Copyright (c) 2007-2020, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -49,9 +49,9 @@ #include "core/or/protover.h" #include "feature/client/bridges.h" #include "feature/client/entrynodes.h" -#include "feature/control/control.h" +#include "feature/control/control_events.h" #include "feature/dirauth/process_descs.h" -#include "feature/dircache/dirserv.h" +#include "feature/dirclient/dirclient_modes.h" #include "feature/hs/hs_client.h" #include "feature/hs/hs_common.h" #include "feature/nodelist/describe.h" @@ -59,6 +59,7 @@ #include "feature/nodelist/microdesc.h" #include "feature/nodelist/networkstatus.h" #include "feature/nodelist/node_select.h" +#include "feature/nodelist/nodefamily.h" #include "feature/nodelist/nodelist.h" #include "feature/nodelist/routerlist.h" #include "feature/nodelist/routerset.h" @@ -126,7 +127,7 @@ typedef struct nodelist_t { * * 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, + * Whenever a node's routerinfo or microdescriptor has just changed, * you should add it to this map with node_add_to_ed25519_map(). */ HT_HEAD(nodelist_ed_map, node_t) nodes_by_ed_id; @@ -156,9 +157,9 @@ node_id_eq(const node_t *node1, const node_t *node2) return tor_memeq(node1->identity, node2->identity, DIGEST_LEN); } -HT_PROTOTYPE(nodelist_map, node_t, ht_ent, node_id_hash, node_id_eq) +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_) + 0.6, tor_reallocarray_, tor_free_); static inline unsigned int node_ed_id_hash(const node_t *node) @@ -173,9 +174,9 @@ node_ed_id_eq(const node_t *node1, const node_t *node2) } HT_PROTOTYPE(nodelist_ed_map, node_t, ed_ht_ent, node_ed_id_hash, - node_ed_id_eq) + 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_) + node_ed_id_eq, 0.6, tor_reallocarray_, tor_free_); /** The global nodelist. */ static nodelist_t *the_nodelist=NULL; @@ -450,8 +451,6 @@ node_addrs_changed(node_t *node) static void node_add_to_address_set(const node_t *node) { - tor_addr_t tmp_addr; - if (!the_nodelist || !the_nodelist->node_addrs || !the_nodelist->reentry_set) return; @@ -464,19 +463,17 @@ node_add_to_address_set(const node_t *node) * test succeeds and thus the 0 value for the DirPort. */ if (node->rs) { - if (node->rs->addr) { - tor_addr_from_ipv4h(&tmp_addr, node->rs->addr); - nodelist_add_addr_to_address_set(&tmp_addr, node->rs->or_port, 0); - } + if (!tor_addr_is_null(&node->rs->ipv4_addr)) + nodelist_add_addr_to_address_set(&node->rs->ipv4_addr, + node->rs->ipv4_orport, 0); if (!tor_addr_is_null(&node->rs->ipv6_addr)) nodelist_add_addr_to_address_set(&node->rs->ipv6_addr, node->rs->ipv6_orport, 0); } if (node->ri) { - if (node->ri->addr) { - tor_addr_from_ipv4h(&tmp_addr, node->ri->addr); - nodelist_add_addr_to_address_set(&tmp_addr, node->ri->or_port, 0); - } + if (!tor_addr_is_null(&node->ri->ipv4_addr)) + nodelist_add_addr_to_address_set(&node->ri->ipv4_addr, + node->ri->ipv4_orport, 0); if (!tor_addr_is_null(&node->ri->ipv6_addr)) nodelist_add_addr_to_address_set(&node->ri->ipv6_addr, node->ri->ipv6_orport, 0); @@ -530,7 +527,7 @@ nodelist_add_addr_to_address_set(const tor_addr_t *addr, uint16_t or_port, uint16_t dir_port) { if (BUG(!addr) || tor_addr_is_null(addr) || - (!tor_addr_is_v4(addr) && tor_addr_family(addr) != AF_INET6) || + (!tor_addr_is_v4(addr) && !tor_addr_is_v6(addr)) || !the_nodelist || !the_nodelist->node_addrs || !the_nodelist->reentry_set) { return; @@ -690,7 +687,7 @@ get_estimated_address_per_node, (void)) * and grab microdescriptors into nodes as appropriate. */ void -nodelist_set_consensus(networkstatus_t *ns) +nodelist_set_consensus(const networkstatus_t *ns) { const or_options_t *options = get_options(); int authdir = authdir_mode_v3(options); @@ -702,7 +699,8 @@ nodelist_set_consensus(networkstatus_t *ns) SMARTLIST_FOREACH(the_nodelist->nodes, node_t *, node, node->rs = NULL); - /* Conservatively estimate that every node will have 2 addresses. */ + /* Conservatively estimate that every node will have 2 addresses (v4 and + * v6). Then we add the number of configured trusted authorities we have. */ int estimated_addresses = smartlist_len(ns->routerstatus_list) * get_estimated_address_per_node(); estimated_addresses += (get_n_authorities(V3_DIRINFO | BRIDGE_DIRINFO) * @@ -747,7 +745,7 @@ nodelist_set_consensus(networkstatus_t *ns) node->is_bad_exit = rs->is_bad_exit; node->is_hs_dir = rs->is_hs_dir; node->ipv6_preferred = 0; - if (fascist_firewall_prefer_ipv6_orport(options) && + if (reachable_addr_prefer_ipv6_orport(options) && (tor_addr_is_null(&rs->ipv6_addr) == 0 || (node->md && tor_addr_is_null(&node->md->ipv6_addr) == 0))) node->ipv6_preferred = 1; @@ -1026,7 +1024,7 @@ nodelist_assert_ok(void) /** Ensure that the nodelist has been created with the most recent consensus. * If that's not the case, make it so. */ void -nodelist_ensure_freshness(networkstatus_t *ns) +nodelist_ensure_freshness(const networkstatus_t *ns) { tor_assert(ns); @@ -1045,7 +1043,7 @@ nodelist_ensure_freshness(networkstatus_t *ns) /** Return a list of a node_t * for every node we know about. The caller * MUST NOT modify the list. (You can set and clear flags in the nodes if * you must, but you must not add or remove nodes.) */ -MOCK_IMPL(smartlist_t *, +MOCK_IMPL(const smartlist_t *, nodelist_get_list,(void)) { init_nodelist(); @@ -1148,8 +1146,8 @@ node_get_by_nickname,(const char *nickname, unsigned flags)) /** Return the Ed25519 identity key for the provided node, or NULL if it * doesn't have one. */ -const ed25519_public_key_t * -node_get_ed25519_id(const node_t *node) +MOCK_IMPL(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; @@ -1207,7 +1205,7 @@ node_ed25519_id_matches(const node_t *node, const ed25519_public_key_t *id) /** Dummy object that should be unreturnable. Used to ensure that * node_get_protover_summary_flags() always returns non-NULL. */ static const protover_summary_flags_t zero_protover_flags = { - 0,0,0,0,0,0,0 + 0,0,0,0,0,0,0,0,0,0,0,0 }; /** Return the protover_summary_flags for a given node. */ @@ -1232,9 +1230,9 @@ node_get_protover_summary_flags(const node_t *node) * by ed25519 ID during the link handshake. If <b>compatible_with_us</b>, * it needs to be using a link authentication method that we understand. * If not, any plausible link authentication method will do. */ -int -node_supports_ed25519_link_authentication(const node_t *node, - int compatible_with_us) +MOCK_IMPL(bool, +node_supports_ed25519_link_authentication,(const node_t *node, + bool compatible_with_us)) { if (! node_get_ed25519_id(node)) return 0; @@ -1249,7 +1247,7 @@ node_supports_ed25519_link_authentication(const node_t *node, /** Return true iff <b>node</b> supports the hidden service directory version * 3 protocol (proposal 224). */ -int +bool node_supports_v3_hsdir(const node_t *node) { tor_assert(node); @@ -1259,7 +1257,7 @@ node_supports_v3_hsdir(const node_t *node) /** Return true iff <b>node</b> supports ed25519 authentication as an hidden * service introduction point.*/ -int +bool node_supports_ed25519_hs_intro(const node_t *node) { tor_assert(node); @@ -1267,9 +1265,9 @@ node_supports_ed25519_hs_intro(const node_t *node) return node_get_protover_summary_flags(node)->supports_ed25519_hs_intro; } -/** Return true iff <b>node</b> supports to be a rendezvous point for hidden +/** Return true iff <b>node</b> can be a rendezvous point for hidden * service version 3 (HSRend=2). */ -int +bool node_supports_v3_rendezvous_point(const node_t *node) { tor_assert(node); @@ -1282,6 +1280,67 @@ node_supports_v3_rendezvous_point(const node_t *node) return node_get_protover_summary_flags(node)->supports_v3_rendezvous_point; } +/** Return true iff <b>node</b> supports the DoS ESTABLISH_INTRO cell + * extension. */ +bool +node_supports_establish_intro_dos_extension(const node_t *node) +{ + tor_assert(node); + + return node_get_protover_summary_flags(node)-> + supports_establish_intro_dos_extension; +} + +/** Return true iff <b>node</b> can initiate IPv6 extends (Relay=3). + * + * This check should only be performed by client path selection code. + * + * Extending relays should check their own IPv6 support using + * router_can_extend_over_ipv6(). Like other extends, they should not verify + * the link specifiers in the extend cell against the consensus, because it + * may be out of date. */ +bool +node_supports_initiating_ipv6_extends(const node_t *node) +{ + tor_assert(node); + + /* Relays can't initiate an IPv6 extend, unless they have an IPv6 ORPort. */ + if (!node_has_ipv6_orport(node)) { + return 0; + } + + /* Initiating relays also need to support the relevant protocol version. */ + return + node_get_protover_summary_flags(node)->supports_initiating_ipv6_extends; +} + +/** Return true iff <b>node</b> can accept IPv6 extends (Relay=2 or Relay=3) + * from other relays. If <b>need_canonical_ipv6_conn</b> is true, also check + * if the relay supports canonical IPv6 connections (Relay=3 only). + * + * This check should only be performed by client path selection code. + */ +bool +node_supports_accepting_ipv6_extends(const node_t *node, + bool need_canonical_ipv6_conn) +{ + tor_assert(node); + + /* Relays can't accept an IPv6 extend, unless they have an IPv6 ORPort. */ + if (!node_has_ipv6_orport(node)) { + return 0; + } + + /* Accepting relays also need to support the relevant protocol version. */ + if (need_canonical_ipv6_conn) { + return + node_get_protover_summary_flags(node)->supports_canonical_ipv6_conns; + } else { + return + node_get_protover_summary_flags(node)->supports_accepting_ipv6_extends; + } +} + /** Return the RSA ID key's SHA1 digest for the provided node. */ const uint8_t * node_get_rsa_id_digest(const node_t *node) @@ -1290,6 +1349,102 @@ node_get_rsa_id_digest(const node_t *node) return (const uint8_t*)node->identity; } +/* Returns a new smartlist with all possible link specifiers from node: + * - legacy ID is mandatory thus MUST be present in node; + * - include ed25519 link specifier if present in the node, and the node + * supports ed25519 link authentication, and: + * - if direct_conn is true, its link versions are compatible with us, + * - if direct_conn is false, regardless of its link versions; + * - include IPv4 link specifier, if the primary address is not IPv4, log a + * BUG() warning, and return an empty smartlist; + * - include IPv6 link specifier if present in the node. + * + * If node is NULL, returns an empty smartlist. + * + * The smartlist must be freed using link_specifier_smartlist_free(). */ +MOCK_IMPL(smartlist_t *, +node_get_link_specifier_smartlist,(const node_t *node, bool direct_conn)) +{ + link_specifier_t *ls; + tor_addr_port_t ap; + smartlist_t *lspecs = smartlist_new(); + + if (!node) + return lspecs; + + /* Get the relay's IPv4 address. */ + node_get_prim_orport(node, &ap); + + /* We expect the node's primary address to be a valid IPv4 address. + * This conforms to the protocol, which requires either an IPv4 or IPv6 + * address (or both). */ + if (BUG(!tor_addr_is_v4(&ap.addr)) || + BUG(!tor_addr_port_is_valid_ap(&ap, 0))) { + return lspecs; + } + + ls = link_specifier_new(); + link_specifier_set_ls_type(ls, LS_IPV4); + link_specifier_set_un_ipv4_addr(ls, tor_addr_to_ipv4h(&ap.addr)); + link_specifier_set_un_ipv4_port(ls, ap.port); + /* Four bytes IPv4 and two bytes port. */ + link_specifier_set_ls_len(ls, sizeof(ap.addr.addr.in_addr) + + sizeof(ap.port)); + smartlist_add(lspecs, ls); + + /* Legacy ID is mandatory and will always be present in node. */ + ls = link_specifier_new(); + link_specifier_set_ls_type(ls, LS_LEGACY_ID); + memcpy(link_specifier_getarray_un_legacy_id(ls), node->identity, + link_specifier_getlen_un_legacy_id(ls)); + link_specifier_set_ls_len(ls, link_specifier_getlen_un_legacy_id(ls)); + smartlist_add(lspecs, ls); + + /* ed25519 ID is only included if the node has it, and the node declares a + protocol version that supports ed25519 link authentication. + If direct_conn is true, we also require that the node's link version is + compatible with us. (Otherwise, we will be sending the ed25519 key + to another tor, which may support different link versions.) */ + if (!ed25519_public_key_is_zero(&node->ed25519_id) && + node_supports_ed25519_link_authentication(node, direct_conn)) { + ls = link_specifier_new(); + link_specifier_set_ls_type(ls, LS_ED25519_ID); + memcpy(link_specifier_getarray_un_ed25519_id(ls), &node->ed25519_id, + link_specifier_getlen_un_ed25519_id(ls)); + link_specifier_set_ls_len(ls, link_specifier_getlen_un_ed25519_id(ls)); + smartlist_add(lspecs, ls); + } + + /* Check for IPv6. If so, include it as well. */ + if (node_has_ipv6_orport(node)) { + ls = link_specifier_new(); + node_get_pref_ipv6_orport(node, &ap); + link_specifier_set_ls_type(ls, LS_IPV6); + size_t addr_len = link_specifier_getlen_un_ipv6_addr(ls); + const uint8_t *in6_addr = tor_addr_to_in6_addr8(&ap.addr); + uint8_t *ipv6_array = link_specifier_getarray_un_ipv6_addr(ls); + memcpy(ipv6_array, in6_addr, addr_len); + link_specifier_set_un_ipv6_port(ls, ap.port); + /* Sixteen bytes IPv6 and two bytes port. */ + link_specifier_set_ls_len(ls, addr_len + sizeof(ap.port)); + smartlist_add(lspecs, ls); + } + + return lspecs; +} + +/* Free a link specifier list. */ +void +link_specifier_smartlist_free_(smartlist_t *ls_list) +{ + if (!ls_list) + return; + + SMARTLIST_FOREACH(ls_list, link_specifier_t *, lspec, + link_specifier_free(lspec)); + smartlist_free(ls_list); +} + /** Return the nickname of <b>node</b>, or NULL if we can't find one. */ const char * node_get_nickname(const node_t *node) @@ -1429,8 +1584,7 @@ node_exit_policy_rejects_all(const node_t *node) if (node->ri) return node->ri->policy_is_reject_star; else if (node->md) - return node->md->exit_policy == NULL || - short_policy_is_reject_star(node->md->exit_policy); + return node->md->policy_is_reject_star; else return 1; } @@ -1459,32 +1613,14 @@ node_exit_policy_is_exact(const node_t *node, sa_family_t family) * "addr" is an IPv4 host-order address and port_field is a uint16_t. * r is typically a routerinfo_t or routerstatus_t. */ -#define SL_ADD_NEW_IPV4_AP(r, port_field, sl, valid) \ - STMT_BEGIN \ - if (tor_addr_port_is_valid_ipv4h((r)->addr, (r)->port_field, 0)) { \ - valid = 1; \ - tor_addr_port_t *ap = tor_malloc(sizeof(tor_addr_port_t)); \ - tor_addr_from_ipv4h(&ap->addr, (r)->addr); \ - ap->port = (r)->port_field; \ - smartlist_add((sl), ap); \ - } \ - STMT_END - -/* Check if the "addr" and port_field fields from r are a valid non-listening - * address/port. If so, set valid to true and add a newly allocated - * tor_addr_port_t containing "addr" and port_field to sl. - * "addr" is a tor_addr_t and port_field is a uint16_t. - * r is typically a routerinfo_t or routerstatus_t. - */ -#define SL_ADD_NEW_IPV6_AP(r, port_field, sl, valid) \ - STMT_BEGIN \ - if (tor_addr_port_is_valid(&(r)->ipv6_addr, (r)->port_field, 0)) { \ - valid = 1; \ - tor_addr_port_t *ap = tor_malloc(sizeof(tor_addr_port_t)); \ - tor_addr_copy(&ap->addr, &(r)->ipv6_addr); \ - ap->port = (r)->port_field; \ - smartlist_add((sl), ap); \ - } \ +#define SL_ADD_NEW_AP(r, addr_field, port_field, sl, valid) \ + STMT_BEGIN \ + if (tor_addr_port_is_valid(&(r)->addr_field, (r)->port_field, 0)) { \ + valid = 1; \ + tor_addr_port_t *ap = tor_addr_port_new(&(r)->addr_field, \ + (r)->port_field); \ + smartlist_add((sl), ap); \ + } \ STMT_END /** Return list of tor_addr_port_t with all OR ports (in the sense IP @@ -1503,33 +1639,32 @@ node_get_all_orports(const node_t *node) /* Find a valid IPv4 address and port */ if (node->ri != NULL) { - SL_ADD_NEW_IPV4_AP(node->ri, or_port, sl, valid); + SL_ADD_NEW_AP(node->ri, ipv4_addr, ipv4_orport, sl, valid); } /* If we didn't find a valid address/port in the ri, try the rs */ if (!valid && node->rs != NULL) { - SL_ADD_NEW_IPV4_AP(node->rs, or_port, sl, valid); + SL_ADD_NEW_AP(node->rs, ipv4_addr, ipv4_orport, sl, valid); } /* Find a valid IPv6 address and port */ valid = 0; if (node->ri != NULL) { - SL_ADD_NEW_IPV6_AP(node->ri, ipv6_orport, sl, valid); + SL_ADD_NEW_AP(node->ri, ipv6_addr, ipv6_orport, sl, valid); } if (!valid && node->rs != NULL) { - SL_ADD_NEW_IPV6_AP(node->rs, ipv6_orport, sl, valid); + SL_ADD_NEW_AP(node->rs, ipv6_addr, ipv6_orport, sl, valid); } if (!valid && node->md != NULL) { - SL_ADD_NEW_IPV6_AP(node->md, ipv6_orport, sl, valid); + SL_ADD_NEW_AP(node->md, ipv6_addr, ipv6_orport, sl, valid); } return sl; } -#undef SL_ADD_NEW_IPV4_AP -#undef SL_ADD_NEW_IPV6_AP +#undef SL_ADD_NEW_AP /** Wrapper around node_get_prim_orport for backward compatibility. */ @@ -1541,21 +1676,20 @@ node_get_addr(const node_t *node, tor_addr_t *addr_out) tor_addr_copy(addr_out, &ap.addr); } -/** Return the host-order IPv4 address for <b>node</b>, or 0 if it doesn't - * seem to have one. */ -uint32_t -node_get_prim_addr_ipv4h(const node_t *node) +/** Return the IPv4 address for <b>node</b>, or NULL if none found. */ +static const tor_addr_t * +node_get_prim_addr_ipv4(const node_t *node) { /* Don't check the ORPort or DirPort, as this function isn't port-specific, * and the node might have a valid IPv4 address, yet have a zero * ORPort or DirPort. */ - if (node->ri && tor_addr_is_valid_ipv4h(node->ri->addr, 0)) { - return node->ri->addr; - } else if (node->rs && tor_addr_is_valid_ipv4h(node->rs->addr, 0)) { - return node->rs->addr; + if (node->ri && tor_addr_is_valid(&node->ri->ipv4_addr, 0)) { + return &node->ri->ipv4_addr; + } else if (node->rs && tor_addr_is_valid(&node->rs->ipv4_addr, 0)) { + return &node->rs->ipv4_addr; } - return 0; + return NULL; } /** Copy a string representation of an IP address for <b>node</b> into @@ -1563,12 +1697,10 @@ node_get_prim_addr_ipv4h(const node_t *node) void node_get_address_string(const node_t *node, char *buf, size_t len) { - uint32_t ipv4_addr = node_get_prim_addr_ipv4h(node); + const tor_addr_t *ipv4_addr = node_get_prim_addr_ipv4(node); - if (tor_addr_is_valid_ipv4h(ipv4_addr, 0)) { - tor_addr_t addr; - tor_addr_from_ipv4h(&addr, ipv4_addr); - tor_addr_to_str(buf, &addr, len, 0); + if (ipv4_addr) { + tor_addr_to_str(buf, ipv4_addr, len, 0); } else if (len > 0) { buf[0] = '\0'; } @@ -1605,19 +1737,6 @@ node_is_me(const node_t *node) return router_digest_is_me(node->identity); } -/** Return <b>node</b> declared family (as a list of names), or NULL if - * the node didn't declare a family. */ -const smartlist_t * -node_get_declared_family(const node_t *node) -{ - if (node->ri && node->ri->declared_family) - return node->ri->declared_family; - else if (node->md && node->md->family) - return node->md->family; - else - return NULL; -} - /* Does this node have a valid IPv6 address? * Prefer node_has_ipv6_orport() or node_has_ipv6_dirport() for * checking specific ports. */ @@ -1666,7 +1785,7 @@ node_has_ipv6_dirport(const node_t *node) * ii) the router has no IPv4 OR address. * * If you don't have a node, consider looking it up. - * If there is no node, use fascist_firewall_prefer_ipv6_orport(). + * If there is no node, use reachable_addr_prefer_ipv6_orport(). */ int node_ipv6_or_preferred(const node_t *node) @@ -1676,10 +1795,10 @@ node_ipv6_or_preferred(const node_t *node) node_assert_ok(node); /* XX/teor - node->ipv6_preferred is set from - * fascist_firewall_prefer_ipv6_orport() each time the consensus is loaded. + * reachable_addr_prefer_ipv6_orport() each time the consensus is loaded. */ node_get_prim_orport(node, &ipv4_addr); - if (!fascist_firewall_use_ipv6(options)) { + if (!reachable_addr_use_ipv6(options)) { return 0; } else if (node->ipv6_preferred || !tor_addr_port_is_valid_ap(&ipv4_addr, 0)) { @@ -1688,12 +1807,12 @@ node_ipv6_or_preferred(const node_t *node) return 0; } -#define RETURN_IPV4_AP(r, port_field, ap_out) \ - STMT_BEGIN \ - if (r && tor_addr_port_is_valid_ipv4h((r)->addr, (r)->port_field, 0)) { \ - tor_addr_from_ipv4h(&(ap_out)->addr, (r)->addr); \ - (ap_out)->port = (r)->port_field; \ - } \ +#define RETURN_IPV4_AP(r, port_field, ap_out) \ + STMT_BEGIN \ + if (r && tor_addr_port_is_valid(&(r)->ipv4_addr, (r)->port_field, 0)) { \ + tor_addr_copy(&(ap_out)->addr, &(r)->ipv4_addr); \ + (ap_out)->port = (r)->port_field; \ + } \ STMT_END /** Copy the primary (IPv4) OR port (IP address and TCP port) for <b>node</b> @@ -1712,8 +1831,8 @@ node_get_prim_orport(const node_t *node, tor_addr_port_t *ap_out) /* Check ri first, because rewrite_node_address_for_bridge() updates * node->ri with the configured bridge address. */ - RETURN_IPV4_AP(node->ri, or_port, ap_out); - RETURN_IPV4_AP(node->rs, or_port, ap_out); + RETURN_IPV4_AP(node->ri, ipv4_orport, ap_out); + RETURN_IPV4_AP(node->rs, ipv4_orport, ap_out); /* Microdescriptors only have an IPv6 address */ } @@ -1774,7 +1893,7 @@ node_get_pref_ipv6_orport(const node_t *node, tor_addr_port_t *ap_out) * or * ii) our preference is for IPv6 Dir addresses. * - * If there is no node, use fascist_firewall_prefer_ipv6_dirport(). + * If there is no node, use reachable_addr_prefer_ipv6_dirport(). */ int node_ipv6_dir_preferred(const node_t *node) @@ -1783,15 +1902,15 @@ node_ipv6_dir_preferred(const node_t *node) tor_addr_port_t ipv4_addr; node_assert_ok(node); - /* node->ipv6_preferred is set from fascist_firewall_prefer_ipv6_orport(), + /* node->ipv6_preferred is set from reachable_addr_prefer_ipv6_orport(), * so we can't use it to determine DirPort IPv6 preference. * This means that bridge clients will use IPv4 DirPorts by default. */ node_get_prim_dirport(node, &ipv4_addr); - if (!fascist_firewall_use_ipv6(options)) { + if (!reachable_addr_use_ipv6(options)) { return 0; } else if (!tor_addr_port_is_valid_ap(&ipv4_addr, 0) - || fascist_firewall_prefer_ipv6_dirport(get_options())) { + || reachable_addr_prefer_ipv6_dirport(get_options())) { return node_has_ipv6_dirport(node); } return 0; @@ -1813,8 +1932,8 @@ node_get_prim_dirport(const node_t *node, tor_addr_port_t *ap_out) /* Check ri first, because rewrite_node_address_for_bridge() updates * node->ri with the configured bridge address. */ - RETURN_IPV4_AP(node->ri, dir_port, ap_out); - RETURN_IPV4_AP(node->rs, dir_port, ap_out); + RETURN_IPV4_AP(node->ri, ipv4_dirport, ap_out); + RETURN_IPV4_AP(node->rs, ipv4_dirport, ap_out); /* Microdescriptors only have an IPv6 address */ } @@ -1851,13 +1970,13 @@ node_get_pref_ipv6_dirport(const node_t *node, tor_addr_port_t *ap_out) /* Assume IPv4 and IPv6 dirports are the same */ if (node->ri && tor_addr_port_is_valid(&node->ri->ipv6_addr, - node->ri->dir_port, 0)) { + node->ri->ipv4_dirport, 0)) { tor_addr_copy(&ap_out->addr, &node->ri->ipv6_addr); - ap_out->port = node->ri->dir_port; + ap_out->port = node->ri->ipv4_dirport; } else if (node->rs && tor_addr_port_is_valid(&node->rs->ipv6_addr, - node->rs->dir_port, 0)) { + node->rs->ipv4_dirport, 0)) { tor_addr_copy(&ap_out->addr, &node->rs->ipv6_addr); - ap_out->port = node->rs->dir_port; + ap_out->port = node->rs->ipv4_dirport; } else { tor_addr_make_null(&ap_out->addr, AF_INET6); ap_out->port = 0; @@ -1877,7 +1996,7 @@ microdesc_has_curve25519_onion_key(const microdesc_t *md) return 0; } - if (tor_mem_is_zero((const char*)md->onion_curve25519_pkey->public_key, + if (fast_mem_is_zero((const char*)md->onion_curve25519_pkey->public_key, CURVE25519_PUBKEY_LEN)) { return 0; } @@ -1909,7 +2028,7 @@ node_get_curve25519_onion_key(const node_t *node) /* Return a newly allocacted RSA onion public key taken from the given node. * * Return NULL if node is NULL or no RSA onion public key can be found. It is - * the caller responsability to free the returned object. */ + * the caller responsibility to free the returned object. */ crypto_pk_t * node_get_rsa_onion_key(const node_t *node) { @@ -1942,22 +2061,28 @@ node_get_rsa_onion_key(const node_t *node) void node_set_country(node_t *node) { - tor_addr_t addr = TOR_ADDR_NULL; + const tor_addr_t *ipv4_addr = NULL; /* XXXXipv6 */ if (node->rs) - tor_addr_from_ipv4h(&addr, node->rs->addr); + ipv4_addr = &node->rs->ipv4_addr; else if (node->ri) - tor_addr_from_ipv4h(&addr, node->ri->addr); + ipv4_addr = &node->ri->ipv4_addr; - node->country = geoip_get_country_by_addr(&addr); + /* IPv4 is mandatory for a relay so this should not happen unless we are + * attempting to set the country code on a node without a descriptor. */ + if (BUG(!ipv4_addr)) { + node->country = -1; + return; + } + node->country = geoip_get_country_by_addr(ipv4_addr); } /** Set the country code of all routers in the routerlist. */ void nodelist_refresh_countries(void) { - smartlist_t *nodes = nodelist_get_list(); + const smartlist_t *nodes = nodelist_get_list(); SMARTLIST_FOREACH(nodes, node_t *, node, node_set_country(node)); } @@ -1965,9 +2090,12 @@ nodelist_refresh_countries(void) /** Return true iff router1 and router2 have similar enough network addresses * that we should treat them as being in the same family */ int -addrs_in_same_network_family(const tor_addr_t *a1, +router_addrs_in_same_network(const tor_addr_t *a1, const tor_addr_t *a2) { + if (tor_addr_is_null(a1) || tor_addr_is_null(a2)) + return 0; + switch (tor_addr_family(a1)) { case AF_INET: return 0 == tor_addr_compare_masked(a1, a2, 16, CMP_SEMANTIC); @@ -1983,7 +2111,7 @@ addrs_in_same_network_family(const tor_addr_t *a1, * (case-insensitive), or if <b>node's</b> identity key digest * matches a hexadecimal value stored in <b>nickname</b>. Return * false otherwise. */ -static int +STATIC int node_nickname_matches(const node_t *node, const char *nickname) { const char *n = node_get_nickname(node); @@ -1995,7 +2123,7 @@ node_nickname_matches(const node_t *node, const char *nickname) } /** Return true iff <b>node</b> is named by some nickname in <b>lst</b>. */ -static inline int +STATIC int node_in_nickname_smartlist(const smartlist_t *lst, const node_t *node) { if (!lst) return 0; @@ -2006,6 +2134,61 @@ node_in_nickname_smartlist(const smartlist_t *lst, const node_t *node) return 0; } +/** Return true iff n1's declared family contains n2. */ +STATIC int +node_family_contains(const node_t *n1, const node_t *n2) +{ + if (n1->ri && n1->ri->declared_family) { + return node_in_nickname_smartlist(n1->ri->declared_family, n2); + } else if (n1->md) { + return nodefamily_contains_node(n1->md->family, n2); + } else { + return 0; + } +} + +/** + * Return true iff <b>node</b> has declared a nonempty family. + **/ +STATIC bool +node_has_declared_family(const node_t *node) +{ + if (node->ri && node->ri->declared_family && + smartlist_len(node->ri->declared_family)) { + return true; + } + + if (node->md && node->md->family) { + return true; + } + + return false; +} + +/** + * Add to <b>out</b> every node_t that is listed by <b>node</b> as being in + * its family. (Note that these nodes are not in node's family unless they + * also agree that node is in their family.) + **/ +STATIC void +node_lookup_declared_family(smartlist_t *out, const node_t *node) +{ + if (node->ri && node->ri->declared_family && + smartlist_len(node->ri->declared_family)) { + SMARTLIST_FOREACH_BEGIN(node->ri->declared_family, const char *, name) { + const node_t *n2 = node_get_by_nickname(name, NNF_NO_WARN_UNNAMED); + if (n2) { + smartlist_add(out, (node_t *)n2); + } + } SMARTLIST_FOREACH_END(name); + return; + } + + if (node->md && node->md->family) { + nodefamily_add_nodes_to_smartlist(node->md->family, out); + } +} + /** Return true iff r1 and r2 are in the same family, but not the same * router. */ int @@ -2018,19 +2201,20 @@ nodes_in_same_family(const node_t *node1, const node_t *node2) tor_addr_t a1, a2; node_get_addr(node1, &a1); node_get_addr(node2, &a2); - if (addrs_in_same_network_family(&a1, &a2)) + + tor_addr_port_t ap6_1, ap6_2; + node_get_pref_ipv6_orport(node1, &ap6_1); + node_get_pref_ipv6_orport(node2, &ap6_2); + + if (router_addrs_in_same_network(&a1, &a2) || + router_addrs_in_same_network(&ap6_1.addr, &ap6_2.addr)) return 1; } /* Are they in the same family because the agree they are? */ - { - const smartlist_t *f1, *f2; - f1 = node_get_declared_family(node1); - f2 = node_get_declared_family(node2); - if (f1 && f2 && - node_in_nickname_smartlist(f1, node2) && - node_in_nickname_smartlist(f2, node1)) - return 1; + if (node_family_contains(node1, node2) && + node_family_contains(node2, node1)) { + return 1; } /* Are they in the same family because the user says they are? */ @@ -2058,13 +2242,10 @@ void nodelist_add_node_and_family(smartlist_t *sl, const node_t *node) { const smartlist_t *all_nodes = nodelist_get_list(); - const smartlist_t *declared_family; const or_options_t *options = get_options(); tor_assert(node); - declared_family = node_get_declared_family(node); - /* Let's make sure that we have the node itself, if it's a real node. */ { const node_t *real_node = node_get_by_id(node->identity); @@ -2075,35 +2256,35 @@ nodelist_add_node_and_family(smartlist_t *sl, const node_t *node) /* First, add any nodes with similar network addresses. */ if (options->EnforceDistinctSubnets) { tor_addr_t node_addr; + tor_addr_port_t node_ap6; node_get_addr(node, &node_addr); + node_get_pref_ipv6_orport(node, &node_ap6); SMARTLIST_FOREACH_BEGIN(all_nodes, const node_t *, node2) { tor_addr_t a; + tor_addr_port_t ap6; node_get_addr(node2, &a); - if (addrs_in_same_network_family(&a, &node_addr)) + node_get_pref_ipv6_orport(node2, &ap6); + if (router_addrs_in_same_network(&a, &node_addr) || + router_addrs_in_same_network(&ap6.addr, &node_ap6.addr)) smartlist_add(sl, (void*)node2); } SMARTLIST_FOREACH_END(node2); } - /* Now, add all nodes in the declared_family of this node, if they + /* Now, add all nodes in the declared family of this node, if they * also declare this node to be in their family. */ - if (declared_family) { + if (node_has_declared_family(node)) { + smartlist_t *declared_family = smartlist_new(); + node_lookup_declared_family(declared_family, node); + /* Add every r such that router declares familyness with node, and node * declares familyhood with router. */ - SMARTLIST_FOREACH_BEGIN(declared_family, const char *, name) { - const node_t *node2; - const smartlist_t *family2; - if (!(node2 = node_get_by_nickname(name, NNF_NO_WARN_UNNAMED))) - continue; - if (!(family2 = node_get_declared_family(node2))) - continue; - SMARTLIST_FOREACH_BEGIN(family2, const char *, name2) { - if (node_nickname_matches(node, name2)) { - smartlist_add(sl, (void*)node2); - break; - } - } SMARTLIST_FOREACH_END(name2); - } SMARTLIST_FOREACH_END(name); + SMARTLIST_FOREACH_BEGIN(declared_family, const node_t *, node2) { + if (node_family_contains(node2, node)) { + smartlist_add(sl, (void*)node2); + } + } SMARTLIST_FOREACH_END(node2); + smartlist_free(declared_family); } /* If the user declared any families locally, honor those too. */ @@ -2125,21 +2306,18 @@ nodelist_add_node_and_family(smartlist_t *sl, const node_t *node) const node_t * router_find_exact_exit_enclave(const char *address, uint16_t port) {/*XXXX MOVE*/ - uint32_t addr; struct in_addr in; - tor_addr_t a; + tor_addr_t ipv4_addr; const or_options_t *options = get_options(); if (!tor_inet_aton(address, &in)) return NULL; /* it's not an IP already */ - addr = ntohl(in.s_addr); - - tor_addr_from_ipv4h(&a, addr); + tor_addr_from_in(&ipv4_addr, &in); SMARTLIST_FOREACH(nodelist_get_list(), const node_t *, node, { - if (node_get_addr_ipv4h(node) == addr && + if (tor_addr_eq(node_get_prim_addr_ipv4(node), &ipv4_addr) && node->is_running && - compare_tor_addr_to_node_policy(&a, port, node) == + compare_tor_addr_to_node_policy(&ipv4_addr, port, node) == ADDR_POLICY_ACCEPTED && !routerset_contains_node(options->ExcludeExitNodesUnion_, node)) return node; @@ -2408,7 +2586,7 @@ compute_frac_paths_available(const networkstatus_t *consensus, const int authdir = authdir_mode_v3(options); count_usable_descriptors(num_present_out, num_usable_out, - mid, consensus, now, NULL, + mid, consensus, now, options->MiddleNodes, USABLE_DESCRIPTOR_ALL); log_debug(LD_NET, "%s: %d present, %d usable", @@ -2610,7 +2788,7 @@ count_loading_descriptors_progress(void) if (fraction > 1.0) return 0; /* it's not the number of descriptors holding us back */ return BOOTSTRAP_STATUS_LOADING_DESCRIPTORS + (int) - (fraction*(BOOTSTRAP_STATUS_CONN_OR-1 - + (fraction*(BOOTSTRAP_STATUS_ENOUGH_DIRINFO-1 - BOOTSTRAP_STATUS_LOADING_DESCRIPTORS)); } @@ -2697,14 +2875,14 @@ update_router_have_minimum_dir_info(void) /* If paths have just become available in this update. */ if (res && !have_min_dir_info) { control_event_client_status(LOG_NOTICE, "ENOUGH_DIR_INFO"); - control_event_boot_dir(BOOTSTRAP_STATUS_CONN_OR, 0); + control_event_boot_dir(BOOTSTRAP_STATUS_ENOUGH_DIRINFO, 0); log_info(LD_DIR, "We now have enough directory information to build circuits."); } /* If paths have just become unavailable in this update. */ if (!res && have_min_dir_info) { - int quiet = directory_too_idle_to_fetch_descriptors(options, now); + int quiet = dirclient_too_idle_to_fetch_descriptors(options, now); tor_log(quiet ? LOG_INFO : LOG_NOTICE, LD_DIR, "Our directory information is no longer up-to-date " "enough to build circuits: %s", dir_info_status); diff --git a/src/feature/nodelist/nodelist.h b/src/feature/nodelist/nodelist.h index bc09731ce2..44b8918b06 100644 --- a/src/feature/nodelist/nodelist.h +++ b/src/feature/nodelist/nodelist.h @@ -1,7 +1,7 @@ /* Copyright (c) 2001 Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2019, The Tor Project, Inc. */ + * Copyright (c) 2007-2020, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -32,8 +32,8 @@ 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); -void nodelist_ensure_freshness(networkstatus_t *ns); +void nodelist_set_consensus(const networkstatus_t *ns); +void nodelist_ensure_freshness(const networkstatus_t *ns); int nodelist_probably_contains_address(const tor_addr_t *addr); bool nodelist_reentry_contains(const tor_addr_t *addr, uint16_t port); void nodelist_add_addr_to_address_set(const tor_addr_t *addr, @@ -68,19 +68,29 @@ smartlist_t *node_get_all_orports(const node_t *node); int node_allows_single_hop_exits(const node_t *node); const char *node_get_nickname(const node_t *node); const char *node_get_platform(const node_t *node); -uint32_t node_get_prim_addr_ipv4h(const node_t *node); void node_get_address_string(const node_t *node, char *cp, size_t len); long node_get_declared_uptime(const node_t *node); -const smartlist_t *node_get_declared_family(const node_t *node); -const struct ed25519_public_key_t *node_get_ed25519_id(const node_t *node); +MOCK_DECL(const struct ed25519_public_key_t *,node_get_ed25519_id, + (const node_t *node)); int node_ed25519_id_matches(const node_t *node, const struct ed25519_public_key_t *id); -int node_supports_ed25519_link_authentication(const node_t *node, - int compatible_with_us); -int node_supports_v3_hsdir(const node_t *node); -int node_supports_ed25519_hs_intro(const node_t *node); -int node_supports_v3_rendezvous_point(const node_t *node); +MOCK_DECL(bool,node_supports_ed25519_link_authentication, + (const node_t *node, + bool compatible_with_us)); +bool node_supports_v3_hsdir(const node_t *node); +bool node_supports_ed25519_hs_intro(const node_t *node); +bool node_supports_v3_rendezvous_point(const node_t *node); +bool node_supports_establish_intro_dos_extension(const node_t *node); +bool node_supports_initiating_ipv6_extends(const node_t *node); +bool node_supports_accepting_ipv6_extends(const node_t *node, + bool need_canonical_ipv6_conn); + const uint8_t *node_get_rsa_id_digest(const node_t *node); +MOCK_DECL(smartlist_t *,node_get_link_specifier_smartlist,(const node_t *node, + bool direct_conn)); +void link_specifier_smartlist_free_(smartlist_t *ls_list); +#define link_specifier_smartlist_free(ls_list) \ + FREE_AND_NULL(smartlist_t, link_specifier_smartlist_free_, (ls_list)) int node_has_ipv6_addr(const node_t *node); int node_has_ipv6_orport(const node_t *node); @@ -100,11 +110,10 @@ const struct curve25519_public_key_t *node_get_curve25519_onion_key( const node_t *node); crypto_pk_t *node_get_rsa_onion_key(const node_t *node); -MOCK_DECL(smartlist_t *, nodelist_get_list, (void)); +MOCK_DECL(const smartlist_t *, nodelist_get_list, (void)); /* Temporary during transition to multiple addresses. */ void node_get_addr(const node_t *node, tor_addr_t *addr_out); -#define node_get_addr_ipv4h(n) node_get_prim_addr_ipv4h((n)) void nodelist_refresh_countries(void); void node_set_country(node_t *node); @@ -118,7 +127,7 @@ int node_is_unreliable(const node_t *router, int need_uptime, int router_exit_policy_all_nodes_reject(const tor_addr_t *addr, uint16_t port, int need_uptime); void router_set_status(const char *digest, int up); -int addrs_in_same_network_family(const tor_addr_t *a1, +int router_addrs_in_same_network(const tor_addr_t *a1, const tor_addr_t *a2); /** router_have_minimum_dir_info tests to see if we have enough @@ -158,10 +167,16 @@ int count_loading_descriptors_progress(void); #ifdef NODELIST_PRIVATE +STATIC int node_nickname_matches(const node_t *node, const char *nickname); +STATIC int node_in_nickname_smartlist(const smartlist_t *lst, + const node_t *node); +STATIC int node_family_contains(const node_t *n1, const node_t *n2); +STATIC bool node_has_declared_family(const node_t *node); +STATIC void node_lookup_declared_family(smartlist_t *out, const node_t *node); + #ifdef TOR_UNIT_TESTS -STATIC void -node_set_hsdir_index(node_t *node, const networkstatus_t *ns); +STATIC void node_set_hsdir_index(node_t *node, const networkstatus_t *ns); #endif /* defined(TOR_UNIT_TESTS) */ diff --git a/src/feature/nodelist/routerinfo.c b/src/feature/nodelist/routerinfo.c index 975b503615..eb8eb74daa 100644 --- a/src/feature/nodelist/routerinfo.c +++ b/src/feature/nodelist/routerinfo.c @@ -1,33 +1,62 @@ /* Copyright (c) 2001 Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2019, The Tor Project, Inc. */ + * Copyright (c) 2007-2020, The Tor Project, Inc. */ /* See LICENSE for licensing information */ +/** + * @file routerinfo.c + * @brief Manipulate full router descriptors. + **/ + #include "core/or/or.h" #include "feature/nodelist/nodelist.h" #include "feature/nodelist/routerinfo.h" +#include "feature/nodelist/torcert.h" #include "feature/nodelist/node_st.h" #include "feature/nodelist/routerinfo_st.h" -/** Copy the primary (IPv4) OR port (IP address and TCP port) for - * <b>router</b> into *<b>ap_out</b>. */ -void -router_get_prim_orport(const routerinfo_t *router, tor_addr_port_t *ap_out) +/** Copy the OR port (IP address and TCP port) for <b>router</b> and + * <b>family</b> into *<b>ap_out</b>. + * + * If the requested ORPort does not exist, sets *<b>ap_out</b> to the null + * address and port, and returns -1. Otherwise, returns 0. */ +int +router_get_orport(const routerinfo_t *router, + tor_addr_port_t *ap_out, + int family) { tor_assert(ap_out != NULL); - tor_addr_from_ipv4h(&ap_out->addr, router->addr); - ap_out->port = router->or_port; + if (family == AF_INET) { + tor_addr_copy(&ap_out->addr, &router->ipv4_addr); + ap_out->port = router->ipv4_orport; + return 0; + } else if (family == AF_INET6) { + /* IPv6 addresses are optional, so check if it is valid. */ + if (tor_addr_port_is_valid(&router->ipv6_addr, router->ipv6_orport, 0)) { + tor_addr_copy(&ap_out->addr, &router->ipv6_addr); + ap_out->port = router->ipv6_orport; + return 0; + } else { + tor_addr_port_make_null_ap(ap_out, AF_INET6); + return -1; + } + } else { + /* Unsupported address family */ + tor_assert_nonfatal_unreached(); + tor_addr_port_make_null_ap(ap_out, AF_UNSPEC); + return -1; + } } int router_has_orport(const routerinfo_t *router, const tor_addr_port_t *orport) { return - (tor_addr_eq_ipv4h(&orport->addr, router->addr) && - orport->port == router->or_port) || + (tor_addr_eq(&orport->addr, &router->ipv4_addr) && + orport->port == router->ipv4_orport) || (tor_addr_eq(&orport->addr, &router->ipv6_addr) && orport->port == router->ipv6_orport); } @@ -47,6 +76,21 @@ router_get_all_orports(const routerinfo_t *ri) return node_get_all_orports(&fake_node); } +/** Return the Ed25519 identity key for this routerinfo, or NULL if it + * doesn't have one. */ +const ed25519_public_key_t * +routerinfo_get_ed25519_id(const routerinfo_t *ri) +{ + if (BUG(! ri)) + return NULL; + + const tor_cert_t *cert = ri->cache_info.signing_key_cert; + if (cert && ! ed25519_public_key_is_zero(&cert->signing_key)) + return &cert->signing_key; + else + return NULL; +} + /** Given a router purpose, convert it to a string. Don't call this on * ROUTER_PURPOSE_UNKNOWN: The whole point of that value is that we don't * know its string representation. */ diff --git a/src/feature/nodelist/routerinfo.h b/src/feature/nodelist/routerinfo.h index bfa28c7754..bc78beb402 100644 --- a/src/feature/nodelist/routerinfo.h +++ b/src/feature/nodelist/routerinfo.h @@ -1,7 +1,7 @@ /* Copyright (c) 2001 Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2019, The Tor Project, Inc. */ + * Copyright (c) 2007-2020, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -12,16 +12,19 @@ #ifndef TOR_ROUTERINFO_H #define TOR_ROUTERINFO_H -void router_get_prim_orport(const routerinfo_t *router, - tor_addr_port_t *addr_port_out); +int router_get_orport(const routerinfo_t *router, + tor_addr_port_t *addr_port_out, + int family); int router_has_orport(const routerinfo_t *router, const tor_addr_port_t *orport); -void router_get_verbose_nickname(char *buf, const routerinfo_t *router); +struct ed25519_public_key_t; +const struct ed25519_public_key_t *routerinfo_get_ed25519_id( + const routerinfo_t *ri); smartlist_t *router_get_all_orports(const routerinfo_t *ri); const char *router_purpose_to_string(uint8_t p); uint8_t router_purpose_from_string(const char *s); -#endif +#endif /* !defined(TOR_ROUTERINFO_H) */ diff --git a/src/feature/nodelist/routerinfo_st.h b/src/feature/nodelist/routerinfo_st.h index 59656818c1..7197c88c18 100644 --- a/src/feature/nodelist/routerinfo_st.h +++ b/src/feature/nodelist/routerinfo_st.h @@ -1,9 +1,14 @@ /* Copyright (c) 2001 Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2019, The Tor Project, Inc. */ + * Copyright (c) 2007-2020, The Tor Project, Inc. */ /* See LICENSE for licensing information */ +/** + * @file routerinfo_st.h + * @brief Router descriptor structure. + **/ + #ifndef ROUTERINFO_ST_H #define ROUTERINFO_ST_H @@ -16,14 +21,12 @@ struct routerinfo_t { signed_descriptor_t cache_info; char *nickname; /**< Human-readable OR name. */ - uint32_t addr; /**< IPv4 address of OR, in host order. */ - uint16_t or_port; /**< Port for TLS connections. */ - uint16_t dir_port; /**< Port for HTTP directory connections. */ + /** A router's IPv4 address. */ + tor_addr_t ipv4_addr; + uint16_t ipv4_orport; + uint16_t ipv4_dirport; /** A router's IPv6 address, if it has one. */ - /* XXXXX187 Actually these should probably be part of a list of addresses, - * not just a special case. Use abstractions to access these; don't do it - * directly. */ tor_addr_t ipv6_addr; uint16_t ipv6_orport; @@ -112,4 +115,4 @@ struct routerinfo_t { uint8_t purpose; }; -#endif +#endif /* !defined(ROUTERINFO_ST_H) */ diff --git a/src/feature/nodelist/routerlist.c b/src/feature/nodelist/routerlist.c index c7fa868929..a1a348edb9 100644 --- a/src/feature/nodelist/routerlist.c +++ b/src/feature/nodelist/routerlist.c @@ -1,7 +1,7 @@ /* Copyright (c) 2001 Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2019, The Tor Project, Inc. */ + * Copyright (c) 2007-2020, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -65,14 +65,18 @@ #include "app/config/config.h" #include "core/mainloop/connection.h" #include "core/mainloop/mainloop.h" +#include "core/or/circuitlist.h" +#include "core/or/circuituse.h" +#include "core/or/extendinfo.h" #include "core/or/policies.h" #include "feature/client/bridges.h" -#include "feature/control/control.h" +#include "feature/control/control_events.h" #include "feature/dirauth/authmode.h" #include "feature/dirauth/process_descs.h" #include "feature/dirauth/reachability.h" #include "feature/dircache/dirserv.h" #include "feature/dirclient/dirclient.h" +#include "feature/dirclient/dirclient_modes.h" #include "feature/dirclient/dlstatus.h" #include "feature/dircommon/directory.h" #include "feature/nodelist/authcert.h" @@ -88,6 +92,7 @@ #include "feature/nodelist/routerset.h" #include "feature/nodelist/torcert.h" #include "feature/relay/routermode.h" +#include "feature/relay/relay_find_addr.h" #include "feature/stats/rephist.h" #include "lib/crypt_ops/crypto_format.h" #include "lib/crypt_ops/crypto_rand.h" @@ -116,9 +121,9 @@ /* Typed wrappers for different digestmap types; used to avoid type * confusion. */ -DECLARE_TYPED_DIGESTMAP_FNS(sdmap_, digest_sd_map_t, signed_descriptor_t) -DECLARE_TYPED_DIGESTMAP_FNS(rimap_, digest_ri_map_t, routerinfo_t) -DECLARE_TYPED_DIGESTMAP_FNS(eimap_, digest_ei_map_t, extrainfo_t) +DECLARE_TYPED_DIGESTMAP_FNS(sdmap, digest_sd_map_t, signed_descriptor_t) +DECLARE_TYPED_DIGESTMAP_FNS(rimap, digest_ri_map_t, routerinfo_t) +DECLARE_TYPED_DIGESTMAP_FNS(eimap, digest_ei_map_t, extrainfo_t) #define SDMAP_FOREACH(map, keyvar, valvar) \ DIGESTMAP_FOREACH(sdmap_to_digestmap(map), keyvar, signed_descriptor_t *, \ valvar) @@ -135,8 +140,6 @@ static int signed_desc_digest_is_recognized(signed_descriptor_t *desc); static const char *signed_descriptor_get_body_impl( const signed_descriptor_t *desc, int with_annotations); -static void launch_dummy_descriptor_download_as_needed(time_t now, - const or_options_t *options); /****************************************************************************/ @@ -160,7 +163,7 @@ static time_t last_descriptor_download_attempted = 0; * * From time to time, we replace "cached-descriptors" with a new file * containing only the live, non-superseded descriptors, and clear - * cached-routers.new. + * cached-descriptors.new. * * On startup, we read both files. */ @@ -464,11 +467,20 @@ router_reload_router_list(void) return 0; } -/* When iterating through the routerlist, can OR address/port preference - * and reachability checks be skipped? +/* When selecting a router for a direct connection, can OR address/port + * preference and reachability checks be skipped? + * + * Servers never check ReachableAddresses or ClientPreferIPv6. Returns + * true for servers. + * + * Otherwise, if <b>try_ip_pref</b> is true, returns false. Used to make + * clients check ClientPreferIPv6, even if ReachableAddresses is not set. + * Finally, return true if ReachableAddresses is set. */ int -router_skip_or_reachability(const or_options_t *options, int try_ip_pref) +router_or_conn_should_skip_reachable_address_check( + const or_options_t *options, + int try_ip_pref) { /* Servers always have and prefer IPv4. * And if clients are checking against the firewall for reachability only, @@ -476,11 +488,15 @@ router_skip_or_reachability(const or_options_t *options, int try_ip_pref) return server_mode(options) || (!try_ip_pref && !firewall_is_fascist_or()); } -/* When iterating through the routerlist, can Dir address/port preference +/* When selecting a router for a direct connection, can Dir address/port * and reachability checks be skipped? + * + * This function is obsolete, because clients only use ORPorts. */ int -router_skip_dir_reachability(const or_options_t *options, int try_ip_pref) +router_dir_conn_should_skip_reachable_address_check( + const or_options_t *options, + int try_ip_pref) { /* Servers always have and prefer IPv4. * And if clients are checking against the firewall for reachability only, @@ -492,45 +508,115 @@ router_skip_dir_reachability(const or_options_t *options, int try_ip_pref) int routers_have_same_or_addrs(const routerinfo_t *r1, const routerinfo_t *r2) { - return r1->addr == r2->addr && r1->or_port == r2->or_port && + return tor_addr_eq(&r1->ipv4_addr, &r2->ipv4_addr) && + r1->ipv4_orport == r2->ipv4_orport && tor_addr_eq(&r1->ipv6_addr, &r2->ipv6_addr) && r1->ipv6_orport == r2->ipv6_orport; } +/* Returns true if <b>node</b> can be chosen based on <b>flags</b>. + * + * The following conditions are applied to all nodes: + * - is running; + * - is valid; + * - supports EXTEND2 cells; + * - has an ntor circuit crypto key; and + * - does not allow single-hop exits. + * + * If the node has a routerinfo, we're checking for a direct connection, and + * we're using bridges, the following condition is applied: + * - has a bridge-purpose routerinfo; + * and for all other nodes: + * - has a general-purpose routerinfo (or no routerinfo). + * + * Nodes that don't have a routerinfo must be general-purpose nodes, because + * routerstatuses and microdescriptors only come via consensuses. + * + * The <b>flags</b> check that <b>node</b>: + * - <b>CRN_NEED_UPTIME</b>: has more than a minimum uptime; + * - <b>CRN_NEED_CAPACITY</b>: has more than a minimum capacity; + * - <b>CRN_NEED_GUARD</b>: is a Guard; + * - <b>CRN_NEED_DESC</b>: has a routerinfo or microdescriptor -- that is, + * enough info to be used to build a circuit; + * - <b>CRN_DIRECT_CONN</b>: is suitable for direct connections. Checks + * for the relevant descriptors. Checks the address + * against ReachableAddresses, ClientUseIPv4 0, and + * reachable_addr_use_ipv6() == 0); + * - <b>CRN_PREF_ADDR</b>: if we are connecting directly to the node, it has + * an address that is preferred by the + * ClientPreferIPv6ORPort setting; + * - <b>CRN_RENDEZVOUS_V3</b>: can become a v3 onion service rendezvous point; + * - <b>CRN_INITIATE_IPV6_EXTEND</b>: can initiate IPv6 extends. + */ +bool +router_can_choose_node(const node_t *node, int flags) +{ + /* The full set of flags used for node selection. */ + const bool need_uptime = (flags & CRN_NEED_UPTIME) != 0; + const bool need_capacity = (flags & CRN_NEED_CAPACITY) != 0; + const bool need_guard = (flags & CRN_NEED_GUARD) != 0; + const bool need_desc = (flags & CRN_NEED_DESC) != 0; + const bool pref_addr = (flags & CRN_PREF_ADDR) != 0; + const bool direct_conn = (flags & CRN_DIRECT_CONN) != 0; + const bool rendezvous_v3 = (flags & CRN_RENDEZVOUS_V3) != 0; + const bool initiate_ipv6_extend = (flags & CRN_INITIATE_IPV6_EXTEND) != 0; + + const or_options_t *options = get_options(); + const bool check_reach = + !router_or_conn_should_skip_reachable_address_check(options, pref_addr); + const bool direct_bridge = direct_conn && options->UseBridges; + + if (!node->is_running || !node->is_valid) + return false; + if (need_desc && !node_has_preferred_descriptor(node, direct_conn)) + return false; + if (node->ri) { + if (direct_bridge && node->ri->purpose != ROUTER_PURPOSE_BRIDGE) + return false; + else if (node->ri->purpose != ROUTER_PURPOSE_GENERAL) + return false; + } + if (node_is_unreliable(node, need_uptime, need_capacity, need_guard)) + return false; + /* Don't choose nodes if we are certain they can't do EXTEND2 cells */ + if (node->rs && !routerstatus_version_supports_extend2_cells(node->rs, 1)) + return false; + /* Don't choose nodes if we are certain they can't do ntor. */ + if ((node->ri || node->md) && !node_has_curve25519_onion_key(node)) + return false; + /* Exclude relays that allow single hop exit circuits. This is an + * obsolete option since 0.2.9.2-alpha and done by default in + * 0.3.1.0-alpha. */ + if (node_allows_single_hop_exits(node)) + return false; + /* Exclude relays that can not become a rendezvous for a hidden service + * version 3. */ + if (rendezvous_v3 && + !node_supports_v3_rendezvous_point(node)) + return false; + /* Choose a node with an OR address that matches the firewall rules */ + if (direct_conn && check_reach && + !reachable_addr_allows_node(node, + FIREWALL_OR_CONNECTION, + pref_addr)) + return false; + if (initiate_ipv6_extend && !node_supports_initiating_ipv6_extends(node)) + return false; + + return true; +} + /** Add every suitable node from our nodelist to <b>sl</b>, so that - * we can pick a node for a circuit. + * we can pick a node for a circuit based on <b>flags</b>. + * + * See router_can_choose_node() for details of <b>flags</b>. */ void -router_add_running_nodes_to_smartlist(smartlist_t *sl, int need_uptime, - int need_capacity, int need_guard, - int need_desc, int pref_addr, - int direct_conn) -{ - const int check_reach = !router_skip_or_reachability(get_options(), - pref_addr); - /* XXXX MOVE */ +router_add_running_nodes_to_smartlist(smartlist_t *sl, int flags) +{ SMARTLIST_FOREACH_BEGIN(nodelist_get_list(), const node_t *, node) { - if (!node->is_running || !node->is_valid) - continue; - if (need_desc && !node_has_preferred_descriptor(node, direct_conn)) + if (!router_can_choose_node(node, flags)) continue; - if (node->ri && node->ri->purpose != ROUTER_PURPOSE_GENERAL) - continue; - if (node_is_unreliable(node, need_uptime, need_capacity, need_guard)) - continue; - /* Don't choose nodes if we are certain they can't do EXTEND2 cells */ - if (node->rs && !routerstatus_version_supports_extend2_cells(node->rs, 1)) - continue; - /* Don't choose nodes if we are certain they can't do ntor. */ - if ((node->ri || node->md) && !node_has_curve25519_onion_key(node)) - continue; - /* Choose a node with an OR address that matches the firewall rules */ - if (direct_conn && check_reach && - !fascist_firewall_allows_node(node, - FIREWALL_OR_CONNECTION, - pref_addr)) - continue; - smartlist_add(sl, (void *)node); } SMARTLIST_FOREACH_END(node); } @@ -1463,12 +1549,13 @@ router_descriptor_is_older_than,(const routerinfo_t *router, int seconds)) } /** Add <b>router</b> to the routerlist, if we don't already have it. Replace - * older entries (if any) with the same key. Note: Callers should not hold - * their pointers to <b>router</b> if this function fails; <b>router</b> - * will either be inserted into the routerlist or freed. Similarly, even - * if this call succeeds, they should not hold their pointers to - * <b>router</b> after subsequent calls with other routerinfo's -- they - * might cause the original routerinfo to get freed. + * older entries (if any) with the same key. + * + * Note: Callers should not hold their pointers to <b>router</b> if this + * function fails; <b>router</b> will either be inserted into the routerlist or + * freed. Similarly, even if this call succeeds, they should not hold their + * pointers to <b>router</b> after subsequent calls with other routerinfo's -- + * they might cause the original routerinfo to get freed. * * Returns the status for the operation. Might set *<b>msg</b> if it wants * the poster of the router to know something. @@ -1930,6 +2017,8 @@ routerlist_remove_old_routers(void) void routerlist_descriptors_added(smartlist_t *sl, int from_cache) { + // XXXX use pubsub mechanism here. + tor_assert(sl); control_event_descriptors_changed(sl); SMARTLIST_FOREACH_BEGIN(sl, routerinfo_t *, ri) { @@ -2218,7 +2307,6 @@ update_all_descriptor_downloads(time_t now) return; update_router_descriptor_downloads(now); update_microdesc_downloads(now); - launch_dummy_descriptor_download_as_needed(now, get_options()); } /** Clear all our timeouts for fetching v3 directory stuff, and then @@ -2405,7 +2493,7 @@ max_dl_per_request(const or_options_t *options, int purpose) } /* If we're going to tunnel our connections, we can ask for a lot more * in a request. */ - if (directory_must_use_begindir(options)) { + if (dirclient_must_use_begindir(options)) { max = 500; } return max; @@ -2448,7 +2536,7 @@ launch_descriptor_downloads(int purpose, if (!n_downloadable) return; - if (!directory_fetches_dir_info_early(options)) { + if (!dirclient_fetches_dir_info_early(options)) { if (n_downloadable >= MAX_DL_TO_DELAY) { log_debug(LD_DIR, "There are enough downloadable %ss to launch requests.", @@ -2539,7 +2627,7 @@ update_consensus_router_descriptor_downloads(time_t now, int is_vote, int n_delayed=0, n_have=0, n_would_reject=0, n_wouldnt_use=0, n_inprogress=0, n_in_oldrouters=0; - if (directory_too_idle_to_fetch_descriptors(options, now)) + if (dirclient_too_idle_to_fetch_descriptors(options, now)) goto done; if (!consensus) goto done; @@ -2559,8 +2647,15 @@ update_consensus_router_descriptor_downloads(time_t now, int is_vote, map = digestmap_new(); list_pending_descriptor_downloads(map, 0); SMARTLIST_FOREACH_BEGIN(consensus->routerstatus_list, void *, rsp) { - routerstatus_t *rs = - is_vote ? &(((vote_routerstatus_t *)rsp)->status) : rsp; + routerstatus_t *rs; + vote_routerstatus_t *vrs; + if (is_vote) { + rs = &(((vote_routerstatus_t *)rsp)->status); + vrs = rsp; + } else { + rs = rsp; + vrs = NULL; + } signed_descriptor_t *sd; if ((sd = router_get_by_descriptor_digest(rs->descriptor_digest))) { const routerinfo_t *ri; @@ -2585,7 +2680,7 @@ update_consensus_router_descriptor_downloads(time_t now, int is_vote, ++n_delayed; /* Not ready for retry. */ continue; } - if (authdir && dirserv_would_reject_router(rs)) { + if (authdir && is_vote && dirserv_would_reject_router(rs, vrs)) { ++n_would_reject; continue; /* We would throw it out immediately. */ } @@ -2665,39 +2760,6 @@ update_consensus_router_descriptor_downloads(time_t now, int is_vote, smartlist_free(no_longer_old); } -/** How often should we launch a server/authority request to be sure of getting - * a guess for our IP? */ -/*XXXX+ this info should come from netinfo cells or something, or we should - * do this only when we aren't seeing incoming data. see bug 652. */ -#define DUMMY_DOWNLOAD_INTERVAL (20*60) - -/** As needed, launch a dummy router descriptor fetch to see if our - * address has changed. */ -static void -launch_dummy_descriptor_download_as_needed(time_t now, - const or_options_t *options) -{ - static time_t last_dummy_download = 0; - /* XXXX+ we could be smarter here; see notes on bug 652. */ - /* If we're a server that doesn't have a configured address, we rely on - * directory fetches to learn when our address changes. So if we haven't - * tried to get any routerdescs in a long time, try a dummy fetch now. */ - if (!options->Address && - server_mode(options) && - last_descriptor_download_attempted + DUMMY_DOWNLOAD_INTERVAL < now && - last_dummy_download + DUMMY_DOWNLOAD_INTERVAL < now) { - last_dummy_download = now; - /* XX/teor - do we want an authority here, because they are less likely - * to give us the wrong address? (See #17782) - * I'm leaving the previous behaviour intact, because I don't like - * the idea of some relays contacting an authority every 20 minutes. */ - directory_get_from_dirserver(DIR_PURPOSE_FETCH_SERVERDESC, - ROUTER_PURPOSE_GENERAL, "authority.z", - PDS_RETRY_IF_NO_SERVERS, - DL_WANT_ANY_DIRSERVER); - } -} - /** Launch downloads for router status as needed. */ void update_router_descriptor_downloads(time_t now) @@ -2871,12 +2933,12 @@ router_differences_are_cosmetic(const routerinfo_t *r1, const routerinfo_t *r2) } /* If any key fields differ, they're different. */ - if (r1->addr != r2->addr || + if (!tor_addr_eq(&r1->ipv4_addr, &r2->ipv4_addr) || strcasecmp(r1->nickname, r2->nickname) || - r1->or_port != r2->or_port || + r1->ipv4_orport != r2->ipv4_orport || !tor_addr_eq(&r1->ipv6_addr, &r2->ipv6_addr) || r1->ipv6_orport != r2->ipv6_orport || - r1->dir_port != r2->dir_port || + r1->ipv4_dirport != r2->ipv4_dirport || r1->purpose != r2->purpose || r1->onion_pkey_len != r2->onion_pkey_len || !tor_memeq(r1->onion_pkey, r2->onion_pkey, r1->onion_pkey_len) || @@ -2915,7 +2977,7 @@ router_differences_are_cosmetic(const routerinfo_t *r1, const routerinfo_t *r2) (r1->bandwidthburst != r2->bandwidthburst)) return 0; - /* Did more than 12 hours pass? */ + /* Has enough time passed between the publication times? */ if (r1->cache_info.published_on + ROUTER_MAX_COSMETIC_TIME_DIFFERENCE < r2->cache_info.published_on) return 0; @@ -2975,7 +3037,7 @@ routerinfo_incompatible_with_extrainfo(const crypto_pk_t *identity_pkey, digest256_matches = tor_memeq(ei->digest256, sd->extra_info_digest256, DIGEST256_LEN); digest256_matches |= - tor_mem_is_zero(sd->extra_info_digest256, DIGEST256_LEN); + fast_mem_is_zero(sd->extra_info_digest256, DIGEST256_LEN); /* The identity must match exactly to have been generated at the same time * by the same router. */ @@ -3059,7 +3121,7 @@ routerinfo_has_curve25519_onion_key(const routerinfo_t *ri) return 0; } - if (tor_mem_is_zero((const char*)ri->onion_curve25519_pkey->public_key, + if (fast_mem_is_zero((const char*)ri->onion_curve25519_pkey->public_key, CURVE25519_PUBKEY_LEN)) { return 0; } @@ -3227,6 +3289,8 @@ refresh_all_country_info(void) routerset_refresh_countries(options->EntryNodes); if (options->ExitNodes) routerset_refresh_countries(options->ExitNodes); + if (options->MiddleNodes) + routerset_refresh_countries(options->MiddleNodes); if (options->ExcludeNodes) routerset_refresh_countries(options->ExcludeNodes); if (options->ExcludeExitNodes) diff --git a/src/feature/nodelist/routerlist.h b/src/feature/nodelist/routerlist.h index 5771ebb1ab..98472b2771 100644 --- a/src/feature/nodelist/routerlist.h +++ b/src/feature/nodelist/routerlist.h @@ -1,6 +1,6 @@ /* Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2019, The Tor Project, Inc. */ + * Copyright (c) 2007-2020, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -37,9 +37,12 @@ typedef enum was_router_added_t { ROUTER_WAS_NOT_WANTED = -6, /* Router descriptor was rejected because it was older than * OLD_ROUTER_DESC_MAX_AGE. */ - ROUTER_WAS_TOO_OLD = -7, /* note contrast with 'NOT_NEW' */ - /* DOCDOC */ - ROUTER_CERTS_EXPIRED = -8 + ROUTER_WAS_TOO_OLD = -7, /* note contrast with 'ROUTER_IS_ALREADY_KNOWN' */ + /* Some certs on this router are expired. */ + ROUTER_CERTS_EXPIRED = -8, + /* We couldn't format the annotations for this router. This is a directory + * authority bug. */ + ROUTER_AUTHDIR_BUG_ANNOTATIONS = -10 } was_router_added_t; /** How long do we avoid using a directory server after it's given us a 503? */ @@ -47,14 +50,16 @@ typedef enum was_router_added_t { int router_reload_router_list(void); -int router_skip_or_reachability(const or_options_t *options, int try_ip_pref); -int router_skip_dir_reachability(const or_options_t *options, int try_ip_pref); +int router_or_conn_should_skip_reachable_address_check( + const or_options_t *options, + int try_ip_pref); +int router_dir_conn_should_skip_reachable_address_check( + const or_options_t *options, + int try_ip_pref); void router_reset_status_download_failures(void); int routers_have_same_or_addrs(const routerinfo_t *r1, const routerinfo_t *r2); -void router_add_running_nodes_to_smartlist(smartlist_t *sl, int need_uptime, - int need_capacity, int need_guard, - int need_desc, int pref_addr, - int direct_conn); +bool router_can_choose_node(const node_t *node, int flags); +void router_add_running_nodes_to_smartlist(smartlist_t *sl, int flags); const routerinfo_t *routerlist_find_my_routerinfo(void); uint32_t router_get_advertised_bandwidth(const routerinfo_t *router); diff --git a/src/feature/nodelist/routerlist_st.h b/src/feature/nodelist/routerlist_st.h index 7446ead3cb..ec8933c7cb 100644 --- a/src/feature/nodelist/routerlist_st.h +++ b/src/feature/nodelist/routerlist_st.h @@ -1,9 +1,14 @@ /* Copyright (c) 2001 Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2019, The Tor Project, Inc. */ + * Copyright (c) 2007-2020, The Tor Project, Inc. */ /* See LICENSE for licensing information */ +/** + * @file routerlist_st.h + * @brief Router descriptor list structure. + **/ + #ifndef ROUTERLIST_ST_H #define ROUTERLIST_ST_H @@ -36,5 +41,4 @@ struct routerlist_t { desc_store_t extrainfo_store; }; -#endif - +#endif /* !defined(ROUTERLIST_ST_H) */ diff --git a/src/feature/nodelist/routerset.c b/src/feature/nodelist/routerset.c index 55e2756959..0d123956d9 100644 --- a/src/feature/nodelist/routerset.c +++ b/src/feature/nodelist/routerset.c @@ -1,7 +1,7 @@ /* Copyright (c) 2001 Matej Pfajfar. -n * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2019, The Tor Project, Inc. */ + * Copyright (c) 2007-2020, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -17,7 +17,7 @@ n * Copyright (c) 2001-2004, Roger Dingledine. * * Routersets are typically used for user-specified restrictions, and * are created by invoking routerset_new and routerset_parse from - * config.c and confparse.c. To use a routerset, invoke one of + * config.c and confmgt.c. To use a routerset, invoke one of * routerset_contains_...() functions , or use * routerstatus_get_all_nodes() / routerstatus_subtract_nodes() to * manipulate a smartlist of node_t pointers. @@ -34,6 +34,9 @@ n * Copyright (c) 2001-2004, Roger Dingledine. #include "feature/nodelist/nickname.h" #include "feature/nodelist/nodelist.h" #include "feature/nodelist/routerset.h" +#include "lib/conf/conftypes.h" +#include "lib/confmgt/typedvar.h" +#include "lib/encoding/confline.h" #include "lib/geoip/geoip.h" #include "core/or/addr_policy_st.h" @@ -41,6 +44,7 @@ n * Copyright (c) 2001-2004, Roger Dingledine. #include "feature/nodelist/node_st.h" #include "feature/nodelist/routerinfo_st.h" #include "feature/nodelist/routerstatus_st.h" +#include "lib/confmgt/var_type_def_st.h" /** Return a new empty routerset. */ routerset_t * @@ -52,6 +56,7 @@ routerset_new(void) result->digests = digestmap_new(); result->policies = smartlist_new(); result->country_names = smartlist_new(); + result->fragile = 0; return result; } @@ -219,11 +224,11 @@ routerset_len(const routerset_t *set) * * (If country is -1, then we take the country * from addr.) */ -STATIC int -routerset_contains(const routerset_t *set, const tor_addr_t *addr, - uint16_t orport, - const char *nickname, const char *id_digest, - country_t country) +static int +routerset_contains2(const routerset_t *set, const tor_addr_t *addr, + uint16_t orport, const tor_addr_t *addr2, + uint16_t orport2, const char *nickname, + const char *id_digest, country_t country) { if (!set || !set->list) return 0; @@ -234,6 +239,9 @@ routerset_contains(const routerset_t *set, const tor_addr_t *addr, if (addr && compare_tor_addr_to_addr_policy(addr, orport, set->policies) == ADDR_POLICY_REJECTED) return 3; + if (addr2 && compare_tor_addr_to_addr_policy(addr2, orport2, set->policies) + == ADDR_POLICY_REJECTED) + return 3; if (set->countries) { if (country < 0 && addr) country = geoip_get_country_by_addr(addr); @@ -245,6 +253,17 @@ routerset_contains(const routerset_t *set, const tor_addr_t *addr, return 0; } +/** Helper. Like routerset_contains2() but for a single IP/port combo. + */ +STATIC int +routerset_contains(const routerset_t *set, const tor_addr_t *addr, + uint16_t orport, const char *nickname, + const char *id_digest, country_t country) +{ + return routerset_contains2(set, addr, orport, NULL, 0, + nickname, id_digest, country); +} + /** If *<b>setp</b> includes at least one country code, or if * <b>only_some_cc_set</b> is 0, add the ?? and A1 country codes to * *<b>setp</b>, creating it as needed. Return true iff *<b>setp</b> changed. @@ -288,12 +307,19 @@ routerset_add_unknown_ccs(routerset_t **setp, int only_if_some_cc_set) int routerset_contains_extendinfo(const routerset_t *set, const extend_info_t *ei) { - return routerset_contains(set, - &ei->addr, - ei->port, - ei->nickname, - ei->identity_digest, - -1 /*country*/); + const tor_addr_port_t *ap1 = NULL, *ap2 = NULL; + if (! tor_addr_is_null(&ei->orports[0].addr)) + ap1 = &ei->orports[0]; + if (! tor_addr_is_null(&ei->orports[1].addr)) + ap2 = &ei->orports[1]; + return routerset_contains2(set, + ap1 ? &ap1->addr : NULL, + ap1 ? ap1->port : 0, + ap2 ? &ap2->addr : NULL, + ap2 ? ap2->port : 0, + ei->nickname, + ei->identity_digest, + -1 /*country*/); } /** Return true iff <b>ri</b> is in <b>set</b>. If country is <b>-1</b>, we @@ -302,14 +328,9 @@ int routerset_contains_router(const routerset_t *set, const routerinfo_t *ri, country_t country) { - tor_addr_t addr; - tor_addr_from_ipv4h(&addr, ri->addr); - return routerset_contains(set, - &addr, - ri->or_port, - ri->nickname, - ri->cache_info.identity_digest, - country); + return routerset_contains2(set, &ri->ipv4_addr, ri->ipv4_orport, + &ri->ipv6_addr, ri->ipv6_orport, ri->nickname, + ri->cache_info.identity_digest, country); } /** Return true iff <b>rs</b> is in <b>set</b>. If country is <b>-1</b>, we @@ -319,11 +340,9 @@ routerset_contains_routerstatus(const routerset_t *set, const routerstatus_t *rs, country_t country) { - tor_addr_t addr; - tor_addr_from_ipv4h(&addr, rs->addr); return routerset_contains(set, - &addr, - rs->or_port, + &rs->ipv4_addr, + rs->ipv4_orport, rs->nickname, rs->identity_digest, country); @@ -378,7 +397,7 @@ routerset_get_all_nodes(smartlist_t *out, const routerset_t *routerset, } else { /* We need to iterate over the routerlist to get all the ones of the * right kind. */ - smartlist_t *nodes = nodelist_get_list(); + const smartlist_t *nodes = nodelist_get_list(); SMARTLIST_FOREACH(nodes, const node_t *, node, { if (running_only && !node->is_running) continue; @@ -461,3 +480,133 @@ routerset_free_(routerset_t *routerset) bitarray_free(routerset->countries); tor_free(routerset); } + +/** + * config helper: parse a routerset-typed variable. + * + * Takes as input as a single line in <b>line</b>; writes its results into a + * routerset_t** passed as <b>target</b>. On success return 0; on failure + * return -1 and store an error message into *<b>errmsg</b>. + **/ +/* + * Warning: For this type, the default value (NULL) and "" are sometimes + * considered different values. That is generally risky, and best avoided for + * other types in the future. For cases where we want the default to be "all + * routers" (like EntryNodes) we should add a new routerset value indicating + * "all routers" (see #31908) + */ +static int +routerset_kv_parse(void *target, const config_line_t *line, char **errmsg, + const void *params) +{ + (void)params; + routerset_t **lines = target; + + if (*lines && (*lines)->fragile) { + if (line->command == CONFIG_LINE_APPEND) { + (*lines)->fragile = 0; + } else { + routerset_free(*lines); // Represent empty sets as NULL + } + } + + int ret; + routerset_t *rs = routerset_new(); + if (routerset_parse(rs, line->value, line->key) < 0) { + *errmsg = tor_strdup("Invalid router list."); + ret = -1; + } else { + if (!routerset_is_empty(rs)) { + if (!*lines) { + *lines = routerset_new(); + } + routerset_union(*lines, rs); + } + ret = 0; + } + routerset_free(rs); + return ret; +} + +/** + * config helper: encode a routerset-typed variable. + * + * Return a newly allocated string containing the value of the + * routerset_t** passed as <b>value</b>. + */ +static char * +routerset_encode(const void *value, const void *params) +{ + (void)params; + const routerset_t **p = (const routerset_t**)value; + return routerset_to_string(*p); +} + +/** + * config helper: free and clear a routerset-typed variable. + * + * Clear the routerset_t** passed as <b>value</b>. + */ +static void +routerset_clear(void *value, const void *params) +{ + (void)params; + routerset_t **p = (routerset_t**)value; + routerset_free(*p); // sets *p to NULL. +} + +/** + * config helper: copy a routerset-typed variable. + * + * Takes it input from a routerset_t** in <b>src</b>; writes its output to a + * routerset_t** in <b>dest</b>. Returns 0 on success, -1 on (impossible) + * failure. + **/ +static int +routerset_copy(void *dest, const void *src, const void *params) +{ + (void)params; + routerset_t **output = (routerset_t**)dest; + const routerset_t *input = *(routerset_t**)src; + routerset_free(*output); // sets *output to NULL + if (! routerset_is_empty(input)) { + *output = routerset_new(); + routerset_union(*output, input); + } + return 0; +} + +static void +routerset_mark_fragile(void *target, const void *params) +{ + (void)params; + routerset_t **ptr = (routerset_t **)target; + if (*ptr) + (*ptr)->fragile = 1; +} + +/** + * Function table to implement a routerset_t-based configuration type. + **/ +static const var_type_fns_t routerset_type_fns = { + .kv_parse = routerset_kv_parse, + .encode = routerset_encode, + .clear = routerset_clear, + .copy = routerset_copy, + .mark_fragile = routerset_mark_fragile, +}; + +/** + * Definition of a routerset_t-based configuration type. + * + * Values are mapped to and from strings using the format defined in + * routerset_parse(): nicknames, IP address patterns, and fingerprints--with + * optional space, separated by commas. + * + * Empty sets are represented as NULL. + **/ +const var_type_def_t ROUTERSET_type_defn = { + .name = "RouterList", + .fns = &routerset_type_fns, + .flags = CFLG_NOREPLACE +}; diff --git a/src/feature/nodelist/routerset.h b/src/feature/nodelist/routerset.h index ca8b6fed93..18a0e31ba7 100644 --- a/src/feature/nodelist/routerset.h +++ b/src/feature/nodelist/routerset.h @@ -1,10 +1,10 @@ /* Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2019, The Tor Project, Inc. */ + * Copyright (c) 2007-2020, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** - * \file routerlist.h + * \file routerset.h * \brief Header file for routerset.c **/ @@ -44,6 +44,10 @@ void routerset_free_(routerset_t *routerset); #define routerset_free(rs) FREE_AND_NULL(routerset_t, routerset_free_, (rs)) int routerset_len(const routerset_t *set); +struct var_type_def_t; +extern const struct var_type_def_t ROUTERSET_type_defn; +typedef routerset_t *config_decl_ROUTERSET; + #ifdef ROUTERSET_PRIVATE #include "lib/container/bitarray.h" @@ -84,6 +88,10 @@ struct routerset_t { * routerset_refresh_countries() whenever the geoip country list is * reloaded. */ bitarray_t *countries; + /** If true, subsequent assignments to this routerset should replace + * it, not extend it. Set only on the first item in a routerset in an + * or_options_t. */ + unsigned int fragile:1; }; #endif /* defined(ROUTERSET_PRIVATE) */ #endif /* !defined(TOR_ROUTERSET_H) */ diff --git a/src/feature/nodelist/routerstatus_st.h b/src/feature/nodelist/routerstatus_st.h index 288edf5982..254ba73f7f 100644 --- a/src/feature/nodelist/routerstatus_st.h +++ b/src/feature/nodelist/routerstatus_st.h @@ -1,9 +1,14 @@ /* Copyright (c) 2001 Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2019, The Tor Project, Inc. */ + * Copyright (c) 2007-2020, The Tor Project, Inc. */ /* See LICENSE for licensing information */ +/** + * @file routerstatus_st.h + * @brief Routerstatus (consensus entry) structure + **/ + #ifndef ROUTERSTATUS_ST_H #define ROUTERSTATUS_ST_H @@ -12,6 +17,10 @@ /** Contents of a single router entry in a network status object. */ struct routerstatus_t { + /* This should be kept in sync with the function + * routerstatus_has_visibly_changed and the printing function + * routerstatus_format_entry in NS_CONTROL_PORT mode. + */ time_t published_on; /**< When was this router published? */ char nickname[MAX_NICKNAME_LEN+1]; /**< The nickname this router says it * has. */ @@ -20,9 +29,9 @@ struct routerstatus_t { /** Digest of the router's most recent descriptor or microdescriptor. * If it's a descriptor, we only use the first DIGEST_LEN bytes. */ char descriptor_digest[DIGEST256_LEN]; - uint32_t addr; /**< IPv4 address for this router, in host order. */ - uint16_t or_port; /**< IPv4 OR port for this router. */ - uint16_t dir_port; /**< Directory port for this router. */ + tor_addr_t ipv4_addr; + uint16_t ipv4_orport; /**< IPv4 OR port for this router. */ + uint16_t ipv4_dirport; /**< Directory port for this router. */ tor_addr_t ipv6_addr; /**< IPv6 address for this router. */ uint16_t ipv6_orport; /**< IPv6 OR port for this router. */ unsigned int is_authority:1; /**< True iff this router is an authority. */ @@ -47,6 +56,8 @@ struct routerstatus_t { unsigned int is_v2_dir:1; /** True iff this router publishes an open DirPort * or it claims to accept tunnelled dir requests. */ + unsigned int is_staledesc:1; /** True iff the authorities think this router + * should upload a new descriptor soon. */ unsigned int has_bandwidth:1; /**< The vote/consensus had bw info */ unsigned int has_exitsummary:1; /**< The vote/consensus had exit summaries */ @@ -76,5 +87,4 @@ struct routerstatus_t { }; -#endif - +#endif /* !defined(ROUTERSTATUS_ST_H) */ diff --git a/src/feature/nodelist/signed_descriptor_st.h b/src/feature/nodelist/signed_descriptor_st.h index bdcebf184a..068f2a733c 100644 --- a/src/feature/nodelist/signed_descriptor_st.h +++ b/src/feature/nodelist/signed_descriptor_st.h @@ -1,9 +1,14 @@ /* Copyright (c) 2001 Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2019, The Tor Project, Inc. */ + * Copyright (c) 2007-2020, The Tor Project, Inc. */ /* See LICENSE for licensing information */ +/** + * @file signed_descriptor_st.h + * @brief Descriptor/extrainfo signature structure + **/ + #ifndef SIGNED_DESCRIPTOR_ST_H #define SIGNED_DESCRIPTOR_ST_H @@ -57,5 +62,4 @@ struct signed_descriptor_t { unsigned int send_unencrypted : 1; }; -#endif - +#endif /* !defined(SIGNED_DESCRIPTOR_ST_H) */ diff --git a/src/feature/nodelist/torcert.c b/src/feature/nodelist/torcert.c index b0197e9f13..dc36626122 100644 --- a/src/feature/nodelist/torcert.c +++ b/src/feature/nodelist/torcert.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2019, The Tor Project, Inc. */ +/* Copyright (c) 2014-2020, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -37,11 +37,11 @@ #include "core/or/or_handshake_certs_st.h" -/** Helper for tor_cert_create(): signs any 32 bytes, not just an ed25519 - * key. +/** As tor_cert_create(), but accept an arbitrary signed_key_type as the + * subject key -- not just an ed25519 key. */ -static tor_cert_t * -tor_cert_sign_impl(const ed25519_keypair_t *signing_key, +tor_cert_t * +tor_cert_create_raw(const ed25519_keypair_t *signing_key, uint8_t cert_type, uint8_t signed_key_type, const uint8_t signed_key_info[32], @@ -74,7 +74,7 @@ tor_cert_sign_impl(const ed25519_keypair_t *signing_key, tor_assert(real_len == alloc_len); tor_assert(real_len > ED25519_SIG_LEN); uint8_t *sig = encoded + (real_len - ED25519_SIG_LEN); - tor_assert(tor_mem_is_zero((char*)sig, ED25519_SIG_LEN)); + tor_assert(fast_mem_is_zero((char*)sig, ED25519_SIG_LEN)); ed25519_signature_t signature; if (ed25519_sign(&signature, encoded, @@ -128,13 +128,13 @@ tor_cert_sign_impl(const ed25519_keypair_t *signing_key, * the public part of <b>signing_key</b> in the certificate. */ tor_cert_t * -tor_cert_create(const ed25519_keypair_t *signing_key, +tor_cert_create_ed25519(const ed25519_keypair_t *signing_key, uint8_t cert_type, const ed25519_public_key_t *signed_key, time_t now, time_t lifetime, uint32_t flags) { - return tor_cert_sign_impl(signing_key, cert_type, + return tor_cert_create_raw(signing_key, cert_type, SIGNED_KEY_TYPE_ED25519, signed_key->pubkey, now, lifetime, flags); } @@ -290,8 +290,8 @@ tor_cert_describe_signature_status(const tor_cert_t *cert) } /** Return a new copy of <b>cert</b> */ -tor_cert_t * -tor_cert_dup(const tor_cert_t *cert) +MOCK_IMPL(tor_cert_t *, +tor_cert_dup,(const tor_cert_t *cert)) { tor_cert_t *newcert = tor_memdup(cert, sizeof(tor_cert_t)); if (cert->encoded) diff --git a/src/feature/nodelist/torcert.h b/src/feature/nodelist/torcert.h index 492275b514..3314ee2550 100644 --- a/src/feature/nodelist/torcert.h +++ b/src/feature/nodelist/torcert.h @@ -1,12 +1,19 @@ -/* Copyright (c) 2014-2019, The Tor Project, Inc. */ +/* Copyright (c) 2014-2020, The Tor Project, Inc. */ /* See LICENSE for licensing information */ +/** + * @file torcert.h + * @brief Header for torcert.c + **/ + #ifndef TORCERT_H_INCLUDED #define TORCERT_H_INCLUDED #include "lib/crypt_ops/crypto_ed25519.h" -#define SIGNED_KEY_TYPE_ED25519 0x01 +#define SIGNED_KEY_TYPE_ED25519 0x01 +#define SIGNED_KEY_TYPE_SHA256_OF_RSA 0x02 +#define SIGNED_KEY_TYPE_SHA256_OF_X509 0x03 #define CERT_TYPE_ID_SIGNING 0x04 #define CERT_TYPE_SIGNING_LINK 0x05 @@ -51,11 +58,17 @@ typedef struct tor_cert_st { struct tor_tls_t; -tor_cert_t *tor_cert_create(const ed25519_keypair_t *signing_key, +tor_cert_t *tor_cert_create_ed25519(const ed25519_keypair_t *signing_key, uint8_t cert_type, const ed25519_public_key_t *signed_key, time_t now, time_t lifetime, uint32_t flags); +tor_cert_t * tor_cert_create_raw(const ed25519_keypair_t *signing_key, + uint8_t cert_type, + uint8_t signed_key_type, + const uint8_t signed_key_info[32], + time_t now, time_t lifetime, + uint32_t flags); tor_cert_t *tor_cert_parse(const uint8_t *cert, size_t certlen); @@ -71,7 +84,7 @@ int tor_cert_checksig(tor_cert_t *cert, const ed25519_public_key_t *pubkey, time_t now); const char *tor_cert_describe_signature_status(const tor_cert_t *cert); -tor_cert_t *tor_cert_dup(const tor_cert_t *cert); +MOCK_DECL(tor_cert_t *,tor_cert_dup,(const tor_cert_t *cert)); int tor_cert_eq(const tor_cert_t *cert1, const tor_cert_t *cert2); int tor_cert_opt_eq(const tor_cert_t *cert1, const tor_cert_t *cert2); diff --git a/src/feature/nodelist/vote_routerstatus_st.h b/src/feature/nodelist/vote_routerstatus_st.h index 366754c166..ad0ee3f23b 100644 --- a/src/feature/nodelist/vote_routerstatus_st.h +++ b/src/feature/nodelist/vote_routerstatus_st.h @@ -1,9 +1,13 @@ /* Copyright (c) 2001 Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2019, The Tor Project, Inc. */ + * Copyright (c) 2007-2020, The Tor Project, Inc. */ /* See LICENSE for licensing information */ +/** + * @file vote_routerstatus_st.h + * @brief Routerstatus (vote entry) structure + **/ #ifndef VOTE_ROUTERSTATUS_ST_H #define VOTE_ROUTERSTATUS_ST_H @@ -38,4 +42,4 @@ struct vote_routerstatus_t { uint8_t ed25519_id[ED25519_PUBKEY_LEN]; }; -#endif +#endif /* !defined(VOTE_ROUTERSTATUS_ST_H) */ |