diff options
Diffstat (limited to 'src/feature/nodelist')
46 files changed, 1687 insertions, 487 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..97e44d53e3 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; @@ -463,11 +464,13 @@ trusted_dirs_load_certs_from_string(const char *contents, int source, (ds->addr != cert->addr || ds->dir_port != cert->dir_port)) { char *a = tor_dup_ip(cert->addr); - 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); + if (a) { + 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; } diff --git a/src/feature/nodelist/authcert.h b/src/feature/nodelist/authcert.h index 2effdb06e6..33065589ba 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 */ /** @@ -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..9145b12bbf 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 @@ -28,5 +33,4 @@ struct authority_cert_t { uint16_t dir_port; }; -#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..00896d5a44 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,108 @@ * \brief Format short descriptions of relays. */ +#define DESCRIBE_PRIVATE + #include "core/or/or.h" #include "feature/nodelist/describe.h" -#include "feature/nodelist/routerinfo.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. + * NULL or the null address. The <b>addr32h</b> field is optional and may be + * set to 0. * * 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 *nickname, const tor_addr_t *addr, uint32_t addr32h) { - char *cp; + size_t rv = 0; + bool has_addr = addr && !tor_addr_is_null(addr); 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 (!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), + 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 (addr32h || has_addr) { + rv = strlcat(buf, " at ", NODE_DESC_BUF_LEN); + tor_assert_nonfatal(rv < NODE_DESC_BUF_LEN); } + if (addr32h) { + int ntoa_rv = 0; + char ipv4_addr_str[INET_NTOA_BUF_LEN]; + memset(ipv4_addr_str, 0, sizeof(ipv4_addr_str)); + struct in_addr in; + memset(&in, 0, sizeof(in)); + + in.s_addr = htonl(addr32h); + ntoa_rv = tor_inet_ntoa(&in, ipv4_addr_str, sizeof(ipv4_addr_str)); + tor_assert_nonfatal(ntoa_rv >= 0); + + rv = strlcat(buf, ipv4_addr_str, NODE_DESC_BUF_LEN); + tor_assert_nonfatal(rv < NODE_DESC_BUF_LEN); + } + /* Both addresses are valid */ + if (addr32h && has_addr) { + rv = strlcat(buf, " and ", NODE_DESC_BUF_LEN); + tor_assert_nonfatal(rv < NODE_DESC_BUF_LEN); + } + if (has_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, addr, sizeof(addr_str), 1); + tor_assert_nonfatal(str_rv == addr_str); + + rv = strlcat(buf, addr_str, NODE_DESC_BUF_LEN); + tor_assert_nonfatal(rv < NODE_DESC_BUF_LEN); + } + return buf; } @@ -84,11 +126,11 @@ router_describe(const routerinfo_t *ri) if (!ri) return "<null>"; + return format_node_description(buf, ri->cache_info.identity_digest, - 0, ri->nickname, - NULL, + &ri->ipv6_addr, ri->addr); } @@ -103,25 +145,33 @@ 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; if (!node) return "<null>"; if (node->rs) { nickname = node->rs->nickname; - is_named = node->rs->is_named; addr32h = node->rs->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; + ipv6_addr = &node->ri->ipv6_addr; + } else { + return "<null rs and ri>"; } return format_node_description(buf, node->identity, - is_named, nickname, - NULL, + ipv6_addr, addr32h); } @@ -137,11 +187,11 @@ 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->ipv6_addr, rs->addr); } @@ -157,9 +207,9 @@ extend_info_describe(const extend_info_t *ei) if (!ei) return "<null>"; + return format_node_description(buf, ei->identity_digest, - 0, ei->nickname, &ei->addr, 0); @@ -175,9 +225,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..d0fa1af263 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,36 @@ 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+IPV4_BUF_LEN_NO_NUL+5+TOR_ADDR_BUF_LEN) + +#endif /* defined(DESCRIBE_PRIVATE) || defined(TOR_UNIT_TESTS) */ + +#ifdef TOR_UNIT_TESTS + +STATIC const char *format_node_description(char *buf, + const char *id_digest, + const char *nickname, + const tor_addr_t *addr, + uint32_t addr32h); + +#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..4317491043 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" @@ -70,7 +71,7 @@ add_trusted_dir_to_nodelist_addr_set(const dir_server_t *dir) } /** Go over the trusted directory server list and add their address(es) to the - * nodelist address set. This is called every time a new consensus is set. */ + * nodelist address set. This is called everytime a new consensus is set. */ MOCK_IMPL(void, dirlist_add_trusted_dir_addresses, (void)) { @@ -362,6 +363,9 @@ trusted_dir_server_new(const char *nickname, const char *address, } if (!hostname) hostname = tor_dup_ip(a); + + if (!hostname) + return NULL; } else { if (tor_lookup_hostname(address, &a)) { log_warn(LD_CONFIG, diff --git a/src/feature/nodelist/dirlist.h b/src/feature/nodelist/dirlist.h index 527af35427..9201e76a9c 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 */ /** @@ -46,4 +46,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..ca4a312639 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,6 +53,10 @@ 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_addr32(rs->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); @@ -110,7 +68,7 @@ routerstatus_format_entry(const routerstatus_t *rs, const char *version, (format==NS_V3_CONSENSUS_MICRODESC)?"":digest64, (format==NS_V3_CONSENSUS_MICRODESC)?"":" ", published, - fmt_addr32(rs->addr), + ip_str, (int)rs->or_port, (int)rs->dir_port); @@ -119,12 +77,6 @@ routerstatus_format_entry(const routerstatus_t *rs, const char *version, * 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..cf7732b8dc 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; @@ -221,6 +226,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 +487,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 +525,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 +537,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 +575,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 +915,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 +971,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 +998,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..e07d58c91c 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)); @@ -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,36 +1579,16 @@ 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)); @@ -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 @@ -1725,6 +1733,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 +1887,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 +1916,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 +2006,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 +2023,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 +2038,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 +2102,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)); } } @@ -2077,12 +2120,11 @@ networkstatus_set_current_consensus(const char *consensus, * 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); + dirauth_sched_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 */ @@ -2095,6 +2137,7 @@ networkstatus_set_current_consensus(const char *consensus, circuit_build_times_new_consensus_params( get_circuit_build_times_mutable(), c); channelpadding_new_consensus_params(c); + circpad_new_consensus_params(c); } /* Reset the failure count only if this consensus is actually valid. */ @@ -2108,22 +2151,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 +2198,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 +2351,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); + rs->addr = ri->addr; + strlcpy(rs->nickname, ri->nickname, sizeof(rs->nickname)); + rs->or_port = ri->or_port; + rs->dir_port = ri->dir_port; + 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 +2413,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 +2429,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,41 +2439,6 @@ 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 */ static int32_t get_net_param_from_list(smartlist_t *net_params, const char *param_name, @@ -2668,6 +2710,9 @@ 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(); + tor_assert(warning_out); if (client_mode) { @@ -2685,7 +2730,7 @@ networkstatus_check_required_protocols(const networkstatus_t *ns, "%s on the Tor network. The missing protocols are: %s", func, missing); tor_free(missing); - return 1; + return consensus_postdates_this_release ? 1 : 0; } if (! protover_all_supported(recommended, &missing)) { @@ -2718,6 +2763,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..b4d0b1dd17 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 @@ -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..e831248413 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" @@ -146,7 +148,7 @@ router_pick_dirserver_generic(smartlist_t *sourcelist, 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 @@ -321,7 +323,7 @@ router_pick_directory_server_impl(dirinfo_type_t type, int flags, 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 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) { @@ -630,6 +632,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,6 +874,58 @@ routerlist_add_node_and_family(smartlist_t *sl, const routerinfo_t *router) nodelist_add_node_and_family(sl, node); } +/** + * 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); + + /* 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); + + /* 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); +} + /** Return a random running node from the nodelist. Never * pick a node that is in * <b>excludedsmartlist</b>, or which matches <b>excludedset</b>, @@ -905,6 +960,7 @@ router_choose_random_node(smartlist_t *excludedsmartlist, const int direct_conn = (flags & CRN_DIRECT_CONN) != 0; const int rendezvous_v3 = (flags & CRN_RENDEZVOUS_V3) != 0; + const smartlist_t *node_list = nodelist_get_list(); smartlist_t *sl=smartlist_new(), *excludednodes=smartlist_new(); const node_t *choice = NULL; @@ -915,17 +971,17 @@ router_choose_random_node(smartlist_t *excludedsmartlist, rule = weight_for_exit ? WEIGHT_FOR_EXIT : (need_guard ? WEIGHT_FOR_GUARD : WEIGHT_FOR_MID); - SMARTLIST_FOREACH_BEGIN(nodelist_get_list(), node_t *, node) { + SMARTLIST_FOREACH_BEGIN(node_list, const 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); + smartlist_add(excludednodes, (node_t*)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); + smartlist_add(excludednodes, (node_t*)node); } } SMARTLIST_FOREACH_END(node); @@ -942,19 +998,11 @@ router_choose_random_node(smartlist_t *excludedsmartlist, "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)); - if (excludedsmartlist) { - smartlist_subtract(sl,excludedsmartlist); - log_debug(LD_CIRC, - "We removed %d excludedsmartlist, leaving %d nodes.", - smartlist_len(excludedsmartlist), - smartlist_len(sl)); + smartlist_add_all(excludednodes, excludedsmartlist); } + nodelist_subtract(sl, excludednodes); + if (excludedset) { routerset_subtract_nodes(sl,excludedset); log_debug(LD_CIRC, @@ -1074,7 +1122,7 @@ router_pick_trusteddirserver_impl(const smartlist_t *sourcelist, 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 must_have_or = dirclient_must_use_begindir(options); SMARTLIST_FOREACH_BEGIN(sourcelist, const dir_server_t *, d) { diff --git a/src/feature/nodelist/node_select.h b/src/feature/nodelist/node_select.h index ed7450b92c..2e67f990f6 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 */ /** @@ -97,6 +97,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..b1ec4db202 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. * @@ -99,4 +105,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..7ebc4f5fda 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" @@ -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; @@ -702,7 +703,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) * @@ -1045,7 +1047,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 +1150,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 +1209,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 }; /** Return the protover_summary_flags for a given node. */ @@ -1232,9 +1234,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(int, +node_supports_ed25519_link_authentication,(const node_t *node, + int compatible_with_us)) { if (! node_get_ed25519_id(node)) return 0; @@ -1267,6 +1269,17 @@ 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 the DoS ESTABLISH_INTRO cell + * extenstion. */ +int +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> supports to be a rendezvous point for hidden * service version 3 (HSRend=2). */ int @@ -1290,6 +1303,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 +1538,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; } @@ -1605,19 +1713,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. */ @@ -1877,7 +1972,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; } @@ -1957,7 +2052,7 @@ node_set_country(node_t *node) 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)); } @@ -1968,6 +2063,9 @@ int addrs_in_same_network_family(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 +2081,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 +2093,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 +2104,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 +2171,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 (addrs_in_same_network_family(&a1, &a2) || + addrs_in_same_network_family(&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 +2212,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 +2226,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 (addrs_in_same_network_family(&a, &node_addr) || + addrs_in_same_network_family(&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. */ @@ -2408,7 +2559,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 +2761,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 +2848,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..0e06326a9c 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 */ /** @@ -71,16 +71,23 @@ 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); +MOCK_DECL(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); +int node_supports_establish_intro_dos_extension(const node_t *node); 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,7 +107,7 @@ 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); @@ -158,10 +165,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..0bf2a977f5 100644 --- a/src/feature/nodelist/routerinfo.c +++ b/src/feature/nodelist/routerinfo.c @@ -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.c + * @brief Manipulate full router descriptors. + **/ + #include "core/or/or.h" #include "feature/nodelist/nodelist.h" diff --git a/src/feature/nodelist/routerinfo.h b/src/feature/nodelist/routerinfo.h index bfa28c7754..604e478999 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 */ /** @@ -17,11 +17,9 @@ void router_get_prim_orport(const routerinfo_t *router, 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); - 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..36ead50e33 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 @@ -21,9 +26,6 @@ struct routerinfo_t { uint16_t dir_port; /**< Port for HTTP directory connections. */ /** 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 +114,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..96bea5a670 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 */ /** @@ -67,12 +67,13 @@ #include "core/mainloop/mainloop.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" @@ -116,9 +117,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) @@ -160,7 +161,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. */ @@ -1463,12 +1464,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 +1932,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) { @@ -2405,7 +2409,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 +2452,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 +2543,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 +2563,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 +2596,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. */ } @@ -2915,7 +2926,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 +2986,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 +3070,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 +3238,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..81a2343540 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? */ 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..2e06ecbf04 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 * @@ -378,7 +382,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 +465,111 @@ 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 **p = (routerset_t**)target; + routerset_free(*p); // clear the old value, if any. + routerset_t *rs = routerset_new(); + if (routerset_parse(rs, line->value, line->key) < 0) { + routerset_free(rs); + *errmsg = tor_strdup("Invalid router list."); + return -1; + } else { + if (routerset_is_empty(rs)) { + /* Represent empty sets as NULL. */ + routerset_free(rs); + } + *p = rs; + return 0; + } +} + +/** + * 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; +} + +/** + * 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 +}; + +/** + * 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 +}; diff --git a/src/feature/nodelist/routerset.h b/src/feature/nodelist/routerset.h index ca8b6fed93..0e4fedf64e 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" diff --git a/src/feature/nodelist/routerstatus_st.h b/src/feature/nodelist/routerstatus_st.h index 288edf5982..735c754b31 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. */ @@ -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..89cc9c88fb 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 */ /** @@ -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, @@ -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..f8fba2b794 100644 --- a/src/feature/nodelist/torcert.h +++ b/src/feature/nodelist/torcert.h @@ -1,6 +1,11 @@ -/* 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 @@ -71,7 +76,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) */ |