diff options
Diffstat (limited to 'src')
250 files changed, 9861 insertions, 4692 deletions
diff --git a/src/app/app.md b/src/app/app.md index 138e75b127..298bde75f5 100644 --- a/src/app/app.md +++ b/src/app/app.md @@ -4,3 +4,8 @@ The "app" directory has Tor's main entry point and configuration logic, and is responsible for initializing and managing the other modules in Tor. + +The modules in "app" are: + + - \refdir{app/config} -- configuration and state for Tor + - \refdir{app/main} -- Top-level functions to invoke the rest or Tor. diff --git a/src/app/config/config.c b/src/app/config/config.c index 71f8c18ca2..a70c1d651e 100644 --- a/src/app/config/config.c +++ b/src/app/config/config.c @@ -27,7 +27,7 @@ * <li>The option_vars_ array below in this module, which configures * the names of the torrc options, their types, their multiplicities, * and their mappings to fields in or_options_t. - * <li>The manual in doc/tor.1.txt, to document what the new option + * <li>The manual in doc/man/tor.1.txt, to document what the new option * is, and how it works. * </ul> * @@ -140,6 +140,7 @@ #include "lib/meminfo/meminfo.h" #include "lib/osinfo/uname.h" +#include "lib/osinfo/libc.h" #include "lib/process/daemon.h" #include "lib/process/pidfile.h" #include "lib/process/restrict.h" @@ -313,7 +314,8 @@ static const config_var_t option_vars_[] = { V(AccountingMax, MEMUNIT, "0 bytes"), VAR("AccountingRule", STRING, AccountingRule_option, "max"), V(AccountingStart, STRING, NULL), - V(Address, STRING, NULL), + V(Address, LINELIST, NULL), + V(AddressDisableIPv6, BOOL, "0"), OBSOLETE("AllowDotExit"), OBSOLETE("AllowInvalidNodes"), V(AllowNonRFC953Hostnames, BOOL, "0"), @@ -323,6 +325,7 @@ static const config_var_t option_vars_[] = { V(AlternateDirAuthority, LINELIST, NULL), OBSOLETE("AlternateHSAuthority"), V(AssumeReachable, BOOL, "0"), + V(AssumeReachableIPv6, AUTOBOOL, "auto"), OBSOLETE("AuthDirBadDir"), OBSOLETE("AuthDirBadDirCCs"), V(AuthDirBadExit, LINELIST, NULL), @@ -1735,8 +1738,8 @@ options_rollback_listener_transaction(listener_transaction_t *xn) SMARTLIST_FOREACH(xn->new_listeners, connection_t *, conn, { - log_notice(LD_NET, "Closing partially-constructed %s on %s:%d", - conn_type_to_string(conn->type), conn->address, conn->port); + log_notice(LD_NET, "Closing partially-constructed %s", + connection_describe(conn)); connection_close_immediate(conn); connection_mark_for_close(conn); }); @@ -2771,10 +2774,6 @@ options_dump(const or_options_t *options, int how_to_dump) use_defaults = global_default_options; minimal = 1; break; - case OPTIONS_DUMP_DEFAULTS: - use_defaults = NULL; - minimal = 1; - break; case OPTIONS_DUMP_ALL: use_defaults = NULL; minimal = 0; @@ -3229,6 +3228,10 @@ options_validate_cb(const void *old_options_, void *options_, char **msg) REJECT("TokenBucketRefillInterval must be between 1 and 1000 inclusive."); } + if (options->AssumeReachable && options->AssumeReachableIPv6 == 0) { + REJECT("Cannot set AssumeReachable 1 and AssumeReachableIPv6 0."); + } + if (options->ExcludeExitNodes || options->ExcludeNodes) { options->ExcludeExitNodesUnion_ = routerset_new(); routerset_union(options->ExcludeExitNodesUnion_,options->ExcludeExitNodes); @@ -3436,7 +3439,7 @@ options_validate_cb(const void *old_options_, void *options_, char **msg) "UseEntryGuards is disabled, but you have configured one or more " "hidden services on this Tor instance. Your hidden services " "will be very easy to locate using a well-known attack -- see " - "http://freehaven.net/anonbib/#hs-attack06 for details."); + "https://freehaven.net/anonbib/#hs-attack06 for details."); } if (options->NumPrimaryGuards && options->NumEntryGuards && @@ -4028,7 +4031,7 @@ options_check_transition_cb(const void *old_, if (! CFG_EQ_INT(old, new_val, opt)) \ BAD_CHANGE_TO(opt," with Sandbox active") - SB_NOCHANGE_STR(Address); + SB_NOCHANGE_LINELIST(Address); SB_NOCHANGE_STR(ServerDNSResolvConfFile); SB_NOCHANGE_STR(DirPortFrontPage); SB_NOCHANGE_STR(CookieAuthFile); @@ -4364,6 +4367,12 @@ options_init_from_torrc(int argc, char **argv) tor_compress_version_str(ZSTD_METHOD), tor_compress_header_version_str(ZSTD_METHOD)); } + if (tor_libc_get_name()) { + printf("%-7s \t\t%-15s\t\t%s\n", + tor_libc_get_name(), + tor_libc_get_header_version_str(), + tor_libc_get_version_str()); + } //TODO: Hex versions? return 1; } @@ -5610,7 +5619,7 @@ port_cfg_new(size_t namelen) /* entry_cfg flags */ cfg->entry_cfg.ipv4_traffic = 1; cfg->entry_cfg.ipv6_traffic = 1; - cfg->entry_cfg.prefer_ipv6 = 1; + cfg->entry_cfg.prefer_ipv6 = 0; cfg->entry_cfg.dns_request = 1; cfg->entry_cfg.onion_traffic = 1; cfg->entry_cfg.prefer_ipv6_virtaddr = 1; @@ -5822,6 +5831,15 @@ port_parse_config(smartlist_t *out, int got_zero_port=0, got_nonzero_port=0; char *unix_socket_path = NULL; port_cfg_t *cfg = NULL; + bool addr_is_explicit = false; + int family = -1; + + /* Parse default address. This can fail for Unix socket for instance so + * family can be -1 and the default_addr will be made UNSPEC. */ + tor_addr_t default_addr = TOR_ADDR_NULL; + if (defaultaddr) { + family = tor_addr_parse(&default_addr, defaultaddr); + } /* If there's no FooPort, then maybe make a default one. */ if (! ports) { @@ -5898,8 +5916,8 @@ port_parse_config(smartlist_t *out, port = 1; } else if (!strcasecmp(addrport, "auto")) { port = CFG_AUTO_PORT; - int af = tor_addr_parse(&addr, defaultaddr); - tor_assert(af >= 0); + tor_assert(family >= 0); + tor_addr_copy(&addr, &default_addr); } else if (!strcasecmpend(addrport, ":auto")) { char *addrtmp = tor_strndup(addrport, strlen(addrport)-5); port = CFG_AUTO_PORT; @@ -5915,14 +5933,20 @@ port_parse_config(smartlist_t *out, "9050" might be a valid address. */ port = (int) tor_parse_long(addrport, 10, 0, 65535, &ok, NULL); if (ok) { - int af = tor_addr_parse(&addr, defaultaddr); - tor_assert(af >= 0); + tor_assert(family >= 0); + tor_addr_copy(&addr, &default_addr); } else if (tor_addr_port_lookup(addrport, &addr, &ptmp) == 0) { if (ptmp == 0) { log_warn(LD_CONFIG, "%sPort line has address but no port", portname); goto err; } + if (family != -1 && tor_addr_family(&addr) != family) { + /* This means we are parsing another ORPort family but we are + * attempting to find the default address' family ORPort. */ + goto ignore; + } port = ptmp; + addr_is_explicit = true; } else { log_warn(LD_CONFIG, "Couldn't parse address %s for %sPort", escaped(addrport), portname); @@ -5933,6 +5957,7 @@ port_parse_config(smartlist_t *out, /* Default port_cfg_t object initialization */ cfg = port_cfg_new(unix_socket_path ? strlen(unix_socket_path) : 0); + cfg->explicit_addr = addr_is_explicit; if (unix_socket_path && default_to_group_writable) cfg->is_group_writable = 1; @@ -5975,15 +6000,25 @@ port_parse_config(smartlist_t *out, } if (cfg->server_cfg.bind_ipv4_only && tor_addr_family(&addr) != AF_INET) { - log_warn(LD_CONFIG, "Could not interpret %sPort address as IPv4", - portname); - goto err; + if (cfg->explicit_addr) { + log_warn(LD_CONFIG, "Could not interpret %sPort address as IPv4", + portname); + goto err; + } + /* This ORPort is IPv4Only but the default address is IPv6, ignore it + * since this will be configured with an IPv4 default address. */ + goto ignore; } if (cfg->server_cfg.bind_ipv6_only && tor_addr_family(&addr) != AF_INET6) { - log_warn(LD_CONFIG, "Could not interpret %sPort address as IPv6", - portname); - goto err; + if (cfg->explicit_addr) { + log_warn(LD_CONFIG, "Could not interpret %sPort address as IPv6", + portname); + goto err; + } + /* This ORPort is IPv6Only but the default address is IPv4, ignore it + * since this will be configured with an IPv6 default address. */ + goto ignore; } } else { /* This is a client port; parse isolation options */ @@ -6196,9 +6231,10 @@ port_parse_config(smartlist_t *out, smartlist_add(out, cfg); /* out owns cfg now, don't re-use or free it */ cfg = NULL; - } else { - tor_free(cfg); } + + ignore: + tor_free(cfg); SMARTLIST_FOREACH(elts, char *, cp, tor_free(cp)); smartlist_clear(elts); tor_free(addrport); @@ -6495,14 +6531,13 @@ get_first_listener_addrport_string(int listener_type) return NULL; } -/** Return the first advertised port of type <b>listener_type</b> in - * <b>address_family</b>. Returns 0 when no port is found, and when passed - * AF_UNSPEC. */ -int -get_first_advertised_port_by_type_af(int listener_type, int address_family) +/** Find and return the first configured advertised `port_cfg_t` of type @a + * listener_type in @a address_family. */ +static const port_cfg_t * +portconf_get_first_advertised(int listener_type, int address_family) { if (address_family == AF_UNSPEC) - return 0; + return NULL; const smartlist_t *conf_ports = get_configured_ports(); SMARTLIST_FOREACH_BEGIN(conf_ports, const port_cfg_t *, cfg) { @@ -6510,33 +6545,35 @@ get_first_advertised_port_by_type_af(int listener_type, int address_family) !cfg->server_cfg.no_advertise) { if ((address_family == AF_INET && port_binds_ipv4(cfg)) || (address_family == AF_INET6 && port_binds_ipv6(cfg))) { - return cfg->port; + return cfg; } } } SMARTLIST_FOREACH_END(cfg); - return 0; + return NULL; +} + +/** Return the first advertised port of type <b>listener_type</b> in + * <b>address_family</b>. Returns 0 when no port is found, and when passed + * AF_UNSPEC. */ +int +portconf_get_first_advertised_port(int listener_type, int address_family) +{ + const port_cfg_t *cfg; + cfg = portconf_get_first_advertised(listener_type, address_family); + + return cfg ? cfg->port : 0; } /** Return the first advertised address of type <b>listener_type</b> in * <b>address_family</b>. Returns NULL if there is no advertised address, * and when passed AF_UNSPEC. */ const tor_addr_t * -get_first_advertised_addr_by_type_af(int listener_type, int address_family) +portconf_get_first_advertised_addr(int listener_type, int address_family) { - if (address_family == AF_UNSPEC) - return NULL; - if (!configured_ports) - return NULL; - SMARTLIST_FOREACH_BEGIN(configured_ports, const port_cfg_t *, cfg) { - if (cfg->type == listener_type && - !cfg->server_cfg.no_advertise) { - if ((address_family == AF_INET && port_binds_ipv4(cfg)) || - (address_family == AF_INET6 && port_binds_ipv6(cfg))) { - return &cfg->addr; - } - } - } SMARTLIST_FOREACH_END(cfg); - return NULL; + const port_cfg_t *cfg; + cfg = portconf_get_first_advertised(listener_type, address_family); + + return cfg ? &cfg->addr : NULL; } /** Return 1 if a port exists of type <b>listener_type</b> on <b>addr</b> and diff --git a/src/app/config/config.h b/src/app/config/config.h index 17caa0e3ff..ee39490072 100644 --- a/src/app/config/config.h +++ b/src/app/config/config.h @@ -58,8 +58,7 @@ setopt_err_t options_trial_assign(struct config_line_t *list, unsigned flags, void options_init(or_options_t *options); #define OPTIONS_DUMP_MINIMAL 1 -#define OPTIONS_DUMP_DEFAULTS 2 -#define OPTIONS_DUMP_ALL 3 +#define OPTIONS_DUMP_ALL 2 char *options_dump(const or_options_t *options, int how_to_dump); int options_init_from_torrc(int argc, char **argv); setopt_err_t options_init_from_string(const char *cf_defaults, const char *cf, @@ -160,13 +159,11 @@ int get_num_cpus(const or_options_t *options); MOCK_DECL(const smartlist_t *,get_configured_ports,(void)); int port_binds_ipv4(const port_cfg_t *port); int port_binds_ipv6(const port_cfg_t *port); -int get_first_advertised_port_by_type_af(int listener_type, - int address_family); -#define get_primary_or_port() \ - (get_first_advertised_port_by_type_af(CONN_TYPE_OR_LISTENER, AF_INET)) -#define get_primary_dir_port() \ - (get_first_advertised_port_by_type_af(CONN_TYPE_DIR_LISTENER, AF_INET)) -const tor_addr_t *get_first_advertised_addr_by_type_af(int listener_type, +int portconf_get_first_advertised_port(int listener_type, + int address_family); +#define portconf_get_primary_dir_port() \ + (portconf_get_first_advertised_port(CONN_TYPE_DIR_LISTENER, AF_INET)) +const tor_addr_t *portconf_get_first_advertised_addr(int listener_type, int address_family); int port_exists_by_type_addr_port(int listener_type, const tor_addr_t *addr, int port, int check_wildcard); diff --git a/src/app/config/fallback_dirs.inc b/src/app/config/fallback_dirs.inc index 793f65ce88..ba7e848715 100644 --- a/src/app/config/fallback_dirs.inc +++ b/src/app/config/fallback_dirs.inc @@ -3,10 +3,10 @@ /* timestamp=20190625114911 */ /* timestamp0=20190625114911 */ /* timestamp1=20190628085927 */ -/* source=whitelist */ +/* source=allowlist */ /* ===== */ -/* 0: Whitelist excluded 1550 of 1711 candidates. */ -/* 1: Whitelist excluded 1601 of 1765 candidates. */ +/* 0: Allowlist excluded 1550 of 1711 candidates. */ +/* 1: Allowlist excluded 1601 of 1765 candidates. */ /* Checked IPv4 DirPorts served a consensus within 15.0s. */ /* 0: diff --git a/src/app/config/or_options_st.h b/src/app/config/or_options_st.h index bf58205f89..68be5711ce 100644 --- a/src/app/config/or_options_st.h +++ b/src/app/config/or_options_st.h @@ -71,7 +71,14 @@ struct or_options_t { int CacheDirectoryGroupReadable; /**< Boolean: Is the CacheDirectory g+r? */ char *Nickname; /**< OR only: nickname of this onion router. */ - char *Address; /**< OR only: configured address for this onion router. */ + /** OR only: configured address for this onion router. Up to two times this + * options is accepted as in IPv4 and IPv6. */ + struct config_line_t *Address; + + /** Boolean: If set, disable IPv6 address resolution, IPv6 ORPorts, IPv6 + * reachability checks, and publishing an IPv6 ORPort in its descriptor. */ + int AddressDisableIPv6; + char *PidFile; /**< Where to store PID of Tor process. */ struct routerset_t *ExitNodes; /**< Structure containing nicknames, digests, @@ -192,7 +199,14 @@ struct or_options_t { unsigned int HTTPTunnelPort_set : 1; /**@}*/ - int AssumeReachable; /**< Whether to publish our descriptor regardless. */ + /** Whether to publish our descriptor regardless of all our self-tests + */ + int AssumeReachable; + /** Whether to publish our descriptor regardless of IPv6 self-tests. + * + * This is an autobool; when set to AUTO, it uses AssumeReachable. + **/ + int AssumeReachableIPv6; int AuthoritativeDir; /**< Boolean: is this an authoritative directory? */ int V3AuthoritativeDir; /**< Boolean: is this an authoritative directory * for version 3 directories? */ diff --git a/src/app/config/or_state_st.h b/src/app/config/or_state_st.h index 8c4e9d5e61..31b7f8a983 100644 --- a/src/app/config/or_state_st.h +++ b/src/app/config/or_state_st.h @@ -65,6 +65,14 @@ struct or_state_t { int BWHistoryWriteInterval; struct smartlist_t *BWHistoryWriteValues; struct smartlist_t *BWHistoryWriteMaxima; + time_t BWHistoryIPv6ReadEnds; + int BWHistoryIPv6ReadInterval; + struct smartlist_t *BWHistoryIPv6ReadValues; + struct smartlist_t *BWHistoryIPv6ReadMaxima; + time_t BWHistoryIPv6WriteEnds; + int BWHistoryIPv6WriteInterval; + struct smartlist_t *BWHistoryIPv6WriteValues; + struct smartlist_t *BWHistoryIPv6WriteMaxima; time_t BWHistoryDirReadEnds; int BWHistoryDirReadInterval; struct smartlist_t *BWHistoryDirReadValues; diff --git a/src/app/config/resolve_addr.c b/src/app/config/resolve_addr.c index 9d1a8e0260..798c703f0a 100644 --- a/src/app/config/resolve_addr.c +++ b/src/app/config/resolve_addr.c @@ -14,301 +14,766 @@ #include "core/mainloop/mainloop.h" #include "feature/control/control_events.h" +#include "feature/dirauth/authmode.h" +#include "lib/encoding/confline.h" #include "lib/net/gethostname.h" #include "lib/net/resolve.h" -/** Last value actually set by resolve_my_address. */ -static uint32_t last_resolved_addr = 0; +/** Maximum "Address" statement allowed in our configuration. */ +#define MAX_CONFIG_ADDRESS 2 + +/** Ease our life. Arrays containing state per address family. These are to + * add semantic to the code so we know what is accessed. */ +#define IDX_NULL 0 /* Index to zeroed address object. */ +#define IDX_IPV4 1 /* Index to AF_INET. */ +#define IDX_IPV6 2 /* Index to AF_INET6. */ +#define IDX_SIZE 3 /* How many indexes do we have. */ + +/** Function in our address function table return one of these code. */ +typedef enum { + /* The address has been found. */ + FN_RET_OK = 0, + /* The failure requirements were not met and thus it is recommended that the + * caller stops the search. */ + FN_RET_BAIL = 1, + /* The address was not found or failure is transient so the caller should go + * to the next method. */ + FN_RET_NEXT = 2, +} fn_address_ret_t; + +/** Last resolved addresses. */ +static tor_addr_t last_resolved_addrs[] = + { TOR_ADDR_NULL, TOR_ADDR_NULL, TOR_ADDR_NULL }; +CTASSERT(ARRAY_LENGTH(last_resolved_addrs) == IDX_SIZE); + +/** Last suggested addresses. + * + * These addresses come from a NETINFO cell from a trusted relay (currently + * only authorities). We only use those in last resort. */ +static tor_addr_t last_suggested_addrs[] = + { TOR_ADDR_NULL, TOR_ADDR_NULL, TOR_ADDR_NULL }; +CTASSERT(ARRAY_LENGTH(last_suggested_addrs) == IDX_SIZE); + +static inline int +af_to_idx(const int family) +{ + switch (family) { + case AF_INET: + return IDX_IPV4; + case AF_INET6: + return IDX_IPV6; + default: + /* It wouldn't be safe to just die here with an assert but we can heavily + * scream with a bug. Return the index of the NULL address. */ + tor_assert_nonfatal_unreached(); + return IDX_NULL; + } +} -/** Accessor for last_resolved_addr from outside this file. */ -uint32_t -get_last_resolved_addr(void) +/** Copy the last suggested address of family into addr_out. + * + * If no last suggested address exists, the addr_out is a null address (use + * tor_addr_is_null() to confirm). */ +void +resolved_addr_get_suggested(int family, tor_addr_t *addr_out) { - return last_resolved_addr; + tor_addr_copy(addr_out, &last_suggested_addrs[af_to_idx(family)]); } -/** Reset last_resolved_addr from outside this file. */ +/** Set the last suggested address into our cache. This is called when we get + * a new NETINFO cell from a trusted source. */ void -reset_last_resolved_addr(void) +resolved_addr_set_suggested(const tor_addr_t *addr) { - last_resolved_addr = 0; + if (BUG(tor_addr_family(addr) != AF_INET && + tor_addr_family(addr) != AF_INET6)) { + return; + } + tor_addr_copy(&last_suggested_addrs[af_to_idx(tor_addr_family(addr))], + addr); } -/** - * Attempt getting our non-local (as judged by tor_addr_is_internal() - * function) IP address using following techniques, listed in - * order from best (most desirable, try first) to worst (least - * desirable, try if everything else fails). - * - * First, attempt using <b>options-\>Address</b> to get our - * non-local IP address. - * - * If <b>options-\>Address</b> represents a non-local IP address, - * consider it ours. - * - * If <b>options-\>Address</b> is a DNS name that resolves to - * a non-local IP address, consider this IP address ours. - * - * If <b>options-\>Address</b> is NULL, fall back to getting local - * hostname and using it in above-described ways to try and - * get our IP address. - * - * In case local hostname cannot be resolved to a non-local IP - * address, try getting an IP address of network interface - * in hopes it will be non-local one. - * - * Fail if one or more of the following is true: - * - DNS name in <b>options-\>Address</b> cannot be resolved. - * - <b>options-\>Address</b> is a local host address. - * - Attempt at getting local hostname fails. - * - Attempt at getting network interface address fails. - * - * Return 0 if all is well, or -1 if we can't find a suitable - * public IP address. - * - * If we are returning 0: - * - Put our public IP address (in host order) into *<b>addr_out</b>. - * - If <b>method_out</b> is non-NULL, set *<b>method_out</b> to a static - * string describing how we arrived at our answer. - * - "CONFIGURED" - parsed from IP address string in - * <b>options-\>Address</b> - * - "RESOLVED" - resolved from DNS name in <b>options-\>Address</b> - * - "GETHOSTNAME" - resolved from a local hostname. - * - "INTERFACE" - retrieved from a network interface. - * - If <b>hostname_out</b> is non-NULL, and we resolved a hostname to - * get our address, set *<b>hostname_out</b> to a newly allocated string - * holding that hostname. (If we didn't get our address by resolving a - * hostname, set *<b>hostname_out</b> to NULL.) - * - * XXXX ipv6 +/** Copy the last resolved address of family into addr_out. + * + * If not last resolved address existed, the addr_out is a null address (use + * tor_addr_is_null()). */ +void +resolved_addr_get_last(int family, tor_addr_t *addr_out) +{ + tor_addr_copy(addr_out, &last_resolved_addrs[af_to_idx(family)]); +} + +/** Reset the last resolved address of family. + * + * This makes it null address. */ +void +resolved_addr_reset_last(int family) +{ + tor_addr_make_null(&last_resolved_addrs[af_to_idx(family)], family); +} + +/** Errors returned by address_can_be_used() in order for the caller to know + * why the address is denied or not. */ +#define ERR_DEFAULT_DIRAUTH -1 /* Using default authorities. */ +#define ERR_ADDRESS_IS_INTERNAL -2 /* IP is internal. */ + +/** @brief Return true iff the given IP address can be used as a valid + * external resolved address. + * + * Two tests are done in this function: + * 1) If the address if NOT internal, it can be used. + * 2) If the address is internal and we have custom directory authorities + * configured then it can they be used. Important for testing networks. + * + * @param addr The IP address to validate. + * @param options Global configuration options. + * @param warn_severity Log level that should be used on error. + * @param explicit_ip Was the IP address explicitly given. + * + * @return Return 0 if it can be used. Return error code ERR_* found at the + * top of the file. */ -int -resolve_my_address(int warn_severity, const or_options_t *options, - uint32_t *addr_out, - const char **method_out, char **hostname_out) +static int +address_can_be_used(const tor_addr_t *addr, const or_options_t *options, + int warn_severity, const bool explicit_ip) { - struct in_addr in; - uint32_t addr; /* host order */ - char hostname[256]; - const char *method_used; - const char *hostname_used; - int explicit_ip=1; - int explicit_hostname=1; - int from_interface=0; - char *addr_string = NULL; - const char *address = options->Address; - int notice_severity = warn_severity <= LOG_NOTICE ? - LOG_NOTICE : warn_severity; - - tor_addr_t myaddr; + tor_assert(addr); + + /* Public address, this is fine. */ + if (!tor_addr_is_internal(addr, 0)) { + goto allow; + } + + /* We have a private IP address. It is allowed only if we set custom + * directory authorities. */ + if (using_default_dir_authorities(options)) { + log_fn(warn_severity, LD_CONFIG, + "Address '%s' is a private IP address. Tor relays that use " + "the default DirAuthorities must have public IP addresses.", + fmt_addr(addr)); + return ERR_DEFAULT_DIRAUTH; + } + + if (!explicit_ip) { + /* Even with custom directory authorities, only an explicit internal + * address is accepted. */ + log_fn(warn_severity, LD_CONFIG, + "Address %s was resolved and thus not explicitly " + "set. Even if DirAuthorities are custom, this is " + "not allowed.", fmt_addr(addr)); + return ERR_ADDRESS_IS_INTERNAL; + } + + allow: + return 0; +} + +/** @brief Get IP address from the given config line and for a specific address + * family. + * + * This can fail is more than two Address statement are found for the same + * address family. It also fails if no statement is found. + * + * @param options Global configuration options. + * @param warn_severity Log level that should be used on error. + * @param family IP address family. Only AF_INET and AF_INET6 are supported. + * @param method_out OUT: String denoting by which method the address was + * found. This is described in the control-spec.txt as + * actions for "STATUS_SERVER". + * @param hostname_out OUT: String containing the hostname gotten from the + * Address value if any. + * @param addr_out OUT: Tor address of the address found in the cline or + * resolved from the cline. + * + * @return Return 0 on success that is an address has been found or resolved + * successfully. Return error code ERR_* found at the top of the file. + */ +static fn_address_ret_t +get_address_from_config(const or_options_t *options, int warn_severity, + int family, const char **method_out, + char **hostname_out, tor_addr_t *addr_out) +{ + int ret; + bool explicit_ip = false, resolve_failure = false; + int num_valid_addr = 0; + + tor_assert(options); tor_assert(addr_out); + tor_assert(method_out); + tor_assert(hostname_out); - /* - * Step one: Fill in 'hostname' to be our best guess. - */ + /* Set them to NULL for safety reasons. */ + *hostname_out = NULL; + *method_out = NULL; - if (address && *address) { - strlcpy(hostname, address, sizeof(hostname)); - log_debug(LD_CONFIG, "Trying configured Address '%s' as local hostname", - hostname); - } else { /* then we need to guess our address */ - explicit_ip = 0; /* it's implicit */ - explicit_hostname = 0; /* it's implicit */ - - if (tor_gethostname(hostname, sizeof(hostname)) < 0) { - log_fn(warn_severity, LD_NET,"Error obtaining local hostname"); - return -1; - } - log_debug(LD_CONFIG, "Guessed local host name as '%s'", hostname); + log_debug(LD_CONFIG, "Attempting to get address from configuration"); + + if (!options->Address) { + log_info(LD_CONFIG, "No Address option found in configuration."); + /* No Address statement, inform caller to try next method. */ + return FN_RET_NEXT; } - /* - * Step two: Now that we know 'hostname', parse it or resolve it. If - * it doesn't parse or resolve, look at the interface address. Set 'addr' - * to be our (host-order) 32-bit answer. - */ + for (const config_line_t *cfg = options->Address; cfg != NULL; + cfg = cfg->next) { + int af; + tor_addr_t addr; + + af = tor_addr_parse(&addr, cfg->value); + if (af == family) { + tor_addr_copy(addr_out, &addr); + *method_out = "CONFIGURED"; + explicit_ip = true; + num_valid_addr++; + continue; + } else if (af != -1) { + /* Parsable address but just not the one from the family we want. Skip + * it so we don't attempt a resolve. */ + continue; + } - if (tor_inet_aton(hostname, &in) == 0) { - /* then we have to resolve it */ - log_debug(LD_CONFIG, "Local hostname '%s' is DNS address. " - "Trying to resolve to IP address.", hostname); - explicit_ip = 0; - if (tor_lookup_hostname(hostname, &addr)) { /* failed to resolve */ - uint32_t interface_ip; /* host order */ - - if (explicit_hostname) { - log_fn(warn_severity, LD_CONFIG, - "Could not resolve local Address '%s'. Failing.", hostname); - return -1; - } - log_fn(notice_severity, LD_CONFIG, - "Could not resolve guessed local hostname '%s'. " - "Trying something else.", hostname); - if (get_interface_address(warn_severity, &interface_ip)) { - log_fn(warn_severity, LD_CONFIG, - "Could not get local interface IP address. Failing."); - return -1; - } - from_interface = 1; - addr = interface_ip; - log_fn(notice_severity, LD_CONFIG, "Learned IP address '%s' for " - "local interface. Using that.", fmt_addr32(addr)); - strlcpy(hostname, "<guessed from interfaces>", sizeof(hostname)); - } else { /* resolved hostname into addr */ - tor_addr_from_ipv4h(&myaddr, addr); - - if (!explicit_hostname && - tor_addr_is_internal(&myaddr, 0)) { - tor_addr_t interface_ip; - - log_fn(notice_severity, LD_CONFIG, "Guessed local hostname '%s' " - "resolves to a private IP address (%s). Trying something " - "else.", hostname, fmt_addr32(addr)); - - if (get_interface_address6(warn_severity, AF_INET, &interface_ip)<0) { - log_fn(warn_severity, LD_CONFIG, - "Could not get local interface IP address. Too bad."); - } else if (tor_addr_is_internal(&interface_ip, 0)) { - log_fn(notice_severity, LD_CONFIG, - "Interface IP address '%s' is a private address too. " - "Ignoring.", fmt_addr(&interface_ip)); - } else { - from_interface = 1; - addr = tor_addr_to_ipv4h(&interface_ip); - log_fn(notice_severity, LD_CONFIG, - "Learned IP address '%s' for local interface." - " Using that.", fmt_addr32(addr)); - strlcpy(hostname, "<guessed from interfaces>", sizeof(hostname)); - } + /* Not an IP address. Considering this value a hostname and attempting to + * do a DNS lookup. */ + if (!tor_addr_lookup(cfg->value, family, &addr)) { + tor_addr_copy(addr_out, &addr); + *method_out = "RESOLVED"; + if (*hostname_out) { + tor_free(*hostname_out); } + *hostname_out = tor_strdup(cfg->value); + explicit_ip = false; + num_valid_addr++; + continue; + } else { + /* Hostname that can't be resolved, this is a fatal error. */ + resolve_failure = true; + log_fn(warn_severity, LD_CONFIG, + "Could not resolve local Address '%s'. Failing.", cfg->value); + continue; } - } else { - log_debug(LD_CONFIG, "Local hostname '%s' is already IP address, " - "skipping DNS resolution", hostname); - addr = ntohl(in.s_addr); /* set addr so that addr_string is not - * illformed */ } - /* - * Step three: Check whether 'addr' is an internal IP address, and error - * out if it is and we don't want that. - */ + if (!num_valid_addr) { + if (resolve_failure) { + /* We found no address but we got a resolution failure. This means we + * can know if the hostname given was v4 or v6 so we can't continue. */ + return FN_RET_BAIL; + } + log_info(LD_CONFIG, + "No Address option found for family %s in configuration.", + fmt_af_family(family)); + /* No Address statement for family so move on to try next method. */ + return FN_RET_NEXT; + } - tor_addr_from_ipv4h(&myaddr,addr); + if (num_valid_addr >= MAX_CONFIG_ADDRESS) { + /* Too many Address for same family. This is a fatal error. */ + log_fn(warn_severity, LD_CONFIG, + "Found %d Address statement of address family %s. " + "Only one is allowed.", num_valid_addr, fmt_af_family(family)); + tor_free(*hostname_out); + return FN_RET_BAIL; + } - addr_string = tor_dup_ip(addr); - if (addr_string && tor_addr_is_internal(&myaddr, 0)) { - /* make sure we're ok with publishing an internal IP */ - if (using_default_dir_authorities(options)) { - /* if they are using the default authorities, disallow internal IPs - * always. For IPv6 ORPorts, this check is done in - * router_get_advertised_ipv6_or_ap(). See #33681. */ - log_fn(warn_severity, LD_CONFIG, - "Address '%s' resolves to private IP address '%s'. " - "Tor servers that use the default DirAuthorities must have " - "public IP addresses.", hostname, addr_string); - tor_free(addr_string); - return -1; - } - if (!explicit_ip) { - /* even if they've set their own authorities, require an explicit IP if - * they're using an internal address. */ - log_fn(warn_severity, LD_CONFIG, "Address '%s' resolves to private " - "IP address '%s'. Please set the Address config option to be " - "the IP address you want to use.", hostname, addr_string); - tor_free(addr_string); - return -1; - } + /* Great, we found an address. */ + ret = address_can_be_used(addr_out, options, warn_severity, explicit_ip); + if (ret != 0) { + /* One of the requirement of this interface is if an internal Address is + * used, custom authorities must be defined else it is a fatal error. + * Furthermore, if the Address was resolved to an internal interface, we + * stop immediately. */ + tor_free(*hostname_out); + return FN_RET_BAIL; } - /* - * Step four: We have a winner! 'addr' is our answer for sure, and - * 'addr_string' is its string form. Fill out the various fields to - * say how we decided it. - */ + /* Address can be used. We are done. */ + log_info(LD_CONFIG, "Address found in configuration: %s", + fmt_addr(addr_out)); + return FN_RET_OK; +} - log_debug(LD_CONFIG, "Resolved Address to '%s'.", addr_string); - - if (explicit_ip) { - method_used = "CONFIGURED"; - hostname_used = NULL; - } else if (explicit_hostname) { - method_used = "RESOLVED"; - hostname_used = hostname; - } else if (from_interface) { - method_used = "INTERFACE"; - hostname_used = NULL; - } else { - method_used = "GETHOSTNAME"; - hostname_used = hostname; +/** @brief Get IP address from the local hostname by calling gethostbyname() + * and doing a DNS resolution on the hostname. + * + * @param options Global configuration options. + * @param warn_severity Log level that should be used on error. + * @param family IP address family. Only AF_INET and AF_INET6 are supported. + * @param method_out OUT: String denoting by which method the address was + * found. This is described in the control-spec.txt as + * actions for "STATUS_SERVER". + * @param hostname_out OUT: String containing the local hostname. + * @param addr_out OUT: Tor address resolved from the local hostname. + * + * @return Return 0 on success that is an address has been found and resolved + * successfully. Return error code ERR_* found at the top of the file. + */ +static fn_address_ret_t +get_address_from_hostname(const or_options_t *options, int warn_severity, + int family, const char **method_out, + char **hostname_out, tor_addr_t *addr_out) +{ + int ret; + char hostname[256]; + + tor_assert(addr_out); + tor_assert(method_out); + + /* Set them to NULL for safety reasons. */ + *hostname_out = NULL; + *method_out = NULL; + + log_debug(LD_CONFIG, "Attempting to get address from local hostname"); + + if (tor_gethostname(hostname, sizeof(hostname)) < 0) { + log_fn(warn_severity, LD_NET, "Error obtaining local hostname"); + /* Unable to obtain the local hostname is a fatal error. */ + return FN_RET_BAIL; + } + if (tor_addr_lookup(hostname, family, addr_out)) { + log_fn(warn_severity, LD_NET, + "Could not resolve local hostname '%s'. Failing.", hostname); + /* Unable to resolve, inform caller to try next method. */ + return FN_RET_NEXT; } - *addr_out = addr; - if (method_out) - *method_out = method_used; - if (hostname_out) - *hostname_out = hostname_used ? tor_strdup(hostname_used) : NULL; + ret = address_can_be_used(addr_out, options, warn_severity, false); + if (ret == ERR_DEFAULT_DIRAUTH) { + /* Non custom authorities, inform caller to try next method. */ + return FN_RET_NEXT; + } else if (ret == ERR_ADDRESS_IS_INTERNAL) { + /* Internal address is a fatal error. */ + return FN_RET_BAIL; + } - /* - * Step five: Check if the answer has changed since last time (or if - * there was no last time), and if so call various functions to keep - * us up-to-date. - */ + /* addr_out contains the address of the local hostname. */ + *method_out = "GETHOSTNAME"; + *hostname_out = tor_strdup(hostname); + + /* Found it! */ + log_info(LD_CONFIG, "Address found from local hostname: %s", + fmt_addr(addr_out)); + return FN_RET_OK; +} + +/** @brief Get IP address from a network interface. + * + * @param options Global configuration options. + * @param warn_severity Log level that should be used on error. + * @param family IP address family. Only AF_INET and AF_INET6 are supported. + * @param method_out OUT: Always "INTERFACE" on success which is detailed in + * the control-spec.txt as actions for "STATUS_SERVER". + * @param hostname_out OUT: String containing the local hostname. For this + * function, it is always set to NULL. + * @param addr_out OUT: Tor address found attached to the interface. + * + * @return Return 0 on success that is an address has been found. Return + * error code ERR_* found at the top of the file. + */ +static fn_address_ret_t +get_address_from_interface(const or_options_t *options, int warn_severity, + int family, const char **method_out, + char **hostname_out, tor_addr_t *addr_out) +{ + int ret; + + tor_assert(method_out); + tor_assert(hostname_out); + tor_assert(addr_out); + + /* Set them to NULL for safety reasons. */ + *method_out = NULL; + *hostname_out = NULL; + + log_debug(LD_CONFIG, "Attempting to get address from network interface"); + + if (get_interface_address6(warn_severity, family, addr_out) < 0) { + log_fn(warn_severity, LD_CONFIG, + "Could not get local interface IP address."); + /* Unable to get IP from interface. Inform caller to try next method. */ + return FN_RET_NEXT; + } + + ret = address_can_be_used(addr_out, options, warn_severity, false); + if (ret < 0) { + /* Unable to use address. Inform caller to try next method. */ + return FN_RET_NEXT; + } + + *method_out = "INTERFACE"; + + /* Found it! */ + log_info(LD_CONFIG, "Address found from interface: %s", fmt_addr(addr_out)); + return FN_RET_OK; +} - if (last_resolved_addr && last_resolved_addr != *addr_out) { +/** @brief Get IP address from the ORPort (if any). + * + * @param options Global configuration options. + * @param warn_severity Log level that should be used on error. + * @param family IP address family. Only AF_INET and AF_INET6 are supported. + * @param method_out OUT: Always "CONFIGURED_ORPORT" on success which is + * detailed in the control-spec.txt as actions + * for "STATUS_SERVER". + * @param hostname_out OUT: String containing the ORPort hostname if any. + * @param addr_out OUT: Tor address found if any. + * + * @return Return 0 on success that is an address has been found. Return + * error code ERR_* found at the top of the file. + */ +static fn_address_ret_t +get_address_from_orport(const or_options_t *options, int warn_severity, + int family, const char **method_out, + char **hostname_out, tor_addr_t *addr_out) +{ + int ret; + const tor_addr_t *addr; + + tor_assert(method_out); + tor_assert(hostname_out); + tor_assert(addr_out); + + log_debug(LD_CONFIG, "Attempting to get address from ORPort"); + + if (!options->ORPort_set) { + log_info(LD_CONFIG, "No ORPort found in configuration."); + /* No ORPort statement, inform caller to try next method. */ + return FN_RET_NEXT; + } + + /* Get ORPort for requested family. */ + addr = get_orport_addr(family); + if (!addr) { + /* No address configured for the ORPort. Ignore. */ + return FN_RET_NEXT; + } + + /* We found the ORPort address. Just make sure it can be used. */ + ret = address_can_be_used(addr, options, warn_severity, true); + if (ret < 0) { + /* Unable to use address. Inform caller to try next method. */ + return FN_RET_NEXT; + } + + /* Found it! */ + *method_out = "CONFIGURED_ORPORT"; + tor_addr_copy(addr_out, addr); + + log_fn(warn_severity, LD_CONFIG, "Address found from ORPort: %s", + fmt_addr(addr_out)); + return FN_RET_OK; +} + +/** @brief Set the last resolved address cache using the given address. + * + * A log notice is emitted if the given address has changed from before. Not + * emitted on first resolve. + * + * Control port event "STATUS_SERVER" is emitted with the new information if + * it has changed. + * + * Finally, tor is notified that the IP address has changed. + * + * @param addr IP address to update the cache with. + * @param method_used By which method did we resolved it (for logging and + * control port). + * @param hostname_used Which hostname was used. If none were used, it is + * NULL. (for logging and control port). + */ +void +resolved_addr_set_last(const tor_addr_t *addr, const char *method_used, + const char *hostname_used) +{ + /** Have we done a first resolve. This is used to control logging. */ + static bool have_resolved_once[] = { false, false, false }; + CTASSERT(ARRAY_LENGTH(have_resolved_once) == IDX_SIZE); + + bool *done_one_resolve; + bool have_hostname = false; + tor_addr_t *last_resolved; + + tor_assert(addr); + tor_assert(method_used); + + /* Do we have an hostname. */ + have_hostname = (hostname_used != NULL); + + int idx = af_to_idx(tor_addr_family(addr)); + if (idx == IDX_NULL) { + /* Not suppose to happen and if it does, af_to_idx() screams loudly. */ + return; + } + + /* Get values from cache. */ + done_one_resolve = &have_resolved_once[idx]; + last_resolved = &last_resolved_addrs[idx]; + + /* Same address last resolved. Ignore. */ + if (tor_addr_eq(last_resolved, addr)) { + return; + } + + /* Don't log notice if this is the first resolve we do. */ + if (*done_one_resolve) { /* Leave this as a notice, regardless of the requested severity, * at least until dynamic IP address support becomes bulletproof. */ log_notice(LD_NET, "Your IP address seems to have changed to %s " "(METHOD=%s%s%s). Updating.", - addr_string, method_used, - hostname_used ? " HOSTNAME=" : "", - hostname_used ? hostname_used : ""); + fmt_addr(addr), method_used, + have_hostname ? " HOSTNAME=" : "", + have_hostname ? hostname_used : ""); ip_address_changed(0); } - if (last_resolved_addr != *addr_out) { - control_event_server_status(LOG_NOTICE, - "EXTERNAL_ADDRESS ADDRESS=%s METHOD=%s%s%s", - addr_string, method_used, - hostname_used ? " HOSTNAME=" : "", - hostname_used ? hostname_used : ""); + /* Notify control port. */ + control_event_server_status(LOG_NOTICE, + "EXTERNAL_ADDRESS ADDRESS=%s METHOD=%s%s%s", + fmt_addr(addr), method_used, + have_hostname ? " HOSTNAME=" : "", + have_hostname ? hostname_used : ""); + /* Copy address to cache. */ + tor_addr_copy(last_resolved, addr); + *done_one_resolve = true; +} + +/** Ease our lives. Typedef to the address discovery function signature. */ +typedef fn_address_ret_t + (*fn_address_t)( + const or_options_t *options, int warn_severity, int family, + const char **method_out, char **hostname_out, tor_addr_t *addr_out); + +/** Address discovery function table. The order matters as in the first one is + * executed first and so on. */ +static const fn_address_t fn_address_table[] = +{ + /* These functions are in order for our find address algorithm. */ + get_address_from_config, + get_address_from_orport, + get_address_from_interface, + get_address_from_hostname, +}; +/** Length of address table as in how many functions. */ +static const size_t fn_address_table_len = + ARRAY_LENGTH(fn_address_table); + +/* Address discover function table for authorities (bridge or directory). + * + * They only discover their address from either the configuration file or the + * ORPort. They do not query the interface nor do any DNS resolution for + * security reasons. */ +static const fn_address_t fn_address_table_auth[] = +{ + /* These functions are in order for our find address algorithm. */ + get_address_from_config, + get_address_from_orport, +}; +/** Length of address table as in how many functions. */ +static const size_t fn_address_table_auth_len = + ARRAY_LENGTH(fn_address_table_auth); + +/** @brief Attempt to find our IP address that can be used as our external + * reachable address. + * + * The following describe the algorithm to find an address. Each have + * specific conditions so read carefully. + * + * On success, true is returned and depending on how the address was found, + * the out parameters can have different values. + * + * On error, false is returned and out parameters are set to NULL. + * + * 1. Look at the configuration Address option. + + * If Address is a public address, True is returned and addr_out is set + * with it, the method_out is set to "CONFIGURED" and hostname_out is set + * to NULL. + * + * If Address is an internal address but NO custom authorities are used, + * an error is returned. + * + * If Address is a hostname, that is it can't be converted to an address, + * it is resolved. On success, addr_out is set with the address, + * method_out is set to "RESOLVED" and hostname_out is set to the resolved + * hostname. On failure to resolve, an error is returned. + * + * If no given Address, fallback to the local hostname (see section 2). + * + * 2. Look at the network interface. + * + * Attempt to find the first public usable address from the list of + * network interface returned by the OS. + * + * On failure, we attempt to look at the local hostname (3). + * + * On success, addr_out is set with it, method_out is set to "INTERFACE" + * and hostname_out is set to NULL. + * + * 3. Look at the local hostname. + * + * If the local hostname resolves to a non internal address, addr_out is + * set with it, method_out is set to "GETHOSTNAME" and hostname_out is set + * to the resolved hostname. + * + * If a local hostname can NOT be found, an error is returned. + * + * If the local hostname resolves to an internal address, an error is + * returned. + * + * If the local hostname can NOT be resolved, an error is returned. + * + * @param options Global configuration options. + * @param family IP address family. Only AF_INET and AF_INET6 are supported. + * @param warn_severity Logging level. + * @param addr_out OUT: Set with the IP address found if any. + * @param method_out OUT: (optional) String denoting by which method the + * address was found. This is described in the + * control-spec.txt as actions for "STATUS_SERVER". + * @param hostname_out OUT: String containing the hostname if any was used. + * Only be set for "RESOLVED" and "GETHOSTNAME" methods. + * Else it is set to NULL. + * + * @return True if the address was found for the given family. False if not or + * on errors. + */ +bool +find_my_address(const or_options_t *options, int family, int warn_severity, + tor_addr_t *addr_out, const char **method_out, + char **hostname_out) +{ + const char *method_used = NULL; + char *hostname_used = NULL; + tor_addr_t my_addr; + const fn_address_t *table = fn_address_table; + size_t table_len = fn_address_table_len; + + tor_assert(options); + tor_assert(addr_out); + + /* Set them to NULL for safety reasons. */ + tor_addr_make_unspec(addr_out); + if (method_out) *method_out = NULL; + if (hostname_out) *hostname_out = NULL; + + /* If an IPv6 is requested, check if IPv6 address discovery is disabled and + * if so we always return a failure. It is done here so we don't populate + * the resolve cache or do any DNS resolution. */ + if (family == AF_INET6 && options->AddressDisableIPv6) { + return false; + } + + /* For authorities (bridge and directory), we use a different table. */ + if (authdir_mode(options)) { + table = fn_address_table_auth; + table_len = fn_address_table_auth_len; } - last_resolved_addr = *addr_out; /* - * And finally, clean up and return success. + * Step 1: Discover address by calling methods from the function table. */ - tor_free(addr_string); - return 0; + /* Go over the function table. They are in order. */ + for (size_t idx = 0; idx < table_len; idx++) { + fn_address_ret_t ret = table[idx](options, warn_severity, family, + &method_used, &hostname_used, &my_addr); + if (ret == FN_RET_BAIL) { + return false; + } else if (ret == FN_RET_OK) { + goto found; + } + tor_assert(ret == FN_RET_NEXT); + } + + /* We've exhausted our attempts. Failure. */ + log_fn(warn_severity, LD_CONFIG, "Unable to find our IP address."); + return false; + + found: + /* + * Step 2: Update last resolved address cache and inform the control port. + */ + resolved_addr_set_last(&my_addr, method_used, hostname_used); + + if (method_out) { + *method_out = method_used; + } + if (hostname_out) { + *hostname_out = hostname_used; + } else { + tor_free(hostname_used); + } + + tor_addr_copy(addr_out, &my_addr); + return true; } -/** Return true iff <b>addr</b> is judged to be on the same network as us, or - * on a private network. +/** @brief: Return true iff the given addr is judged to be local to our + * resolved address. + * + * This function is used to tell whether another address is 'remote' enough + * that we can trust it when it tells us that we are reachable, or that we + * have a certain address. + * + * The criterion to learn if the address is local are the following: + * + * 1. Internal address. + * 2. If EnforceDistinctSubnets is set then it is never local. + * 3. Network mask is compared. IPv4: /24 and IPv6 /48. This is different + * from the path selection that looks at /16 and /32 because we only + * want to learn here if the address is considered to come from the + * Internet basically. + * + * @param addr The address to test if local and also test against our resovled + * address. + * + * @return True iff address is considered local or else False. */ -MOCK_IMPL(int, -is_local_addr, (const tor_addr_t *addr)) +MOCK_IMPL(bool, +is_local_to_resolve_addr, (const tor_addr_t *addr)) { - if (tor_addr_is_internal(addr, 0)) - return 1; - /* Check whether ip is on the same /24 as we are. */ - if (get_options()->EnforceDistinctSubnets == 0) - return 0; - if (tor_addr_family(addr) == AF_INET) { - uint32_t ip = tor_addr_to_ipv4h(addr); + const int family = tor_addr_family(addr); + const tor_addr_t *last_resolved_addr = &last_resolved_addrs[family]; + /* Internal address is always local. */ + if (tor_addr_is_internal(addr, 0)) { + return true; + } + + /* Address is not local if we don't enforce subnet distinction. */ + if (get_options()->EnforceDistinctSubnets == 0) { + return false; + } + + switch (family) { + case AF_INET: /* It's possible that this next check will hit before the first time - * resolve_my_address actually succeeds. (For clients, it is likely that - * resolve_my_address will never be called at all). In those cases, - * last_resolved_addr will be 0, and so checking to see whether ip is on - * the same /24 as last_resolved_addr will be the same as checking whether - * it was on net 0, which is already done by tor_addr_is_internal. - */ - if ((last_resolved_addr & (uint32_t)0xffffff00ul) - == (ip & (uint32_t)0xffffff00ul)) - return 1; + * find_my_address actually succeeds. For clients, it is likely that + * find_my_address will never be called at all. In those cases, + * last_resolved_addr_v4 will be 0, and so checking to see whether ip is + * on the same /24 as last_resolved_addrs[AF_INET] will be the same as + * checking whether it was on net 0, which is already done by + * tor_addr_is_internal. */ + return tor_addr_compare_masked(addr, last_resolved_addr, 24, + CMP_SEMANTIC) == 0; + case AF_INET6: + /* Look at /48 because it is typically the smallest network in the global + * IPv6 routing tables, and it was previously the recommended per-customer + * network block. (See [RFC 6177: IPv6 End Site Address Assignment].) */ + return tor_addr_compare_masked(addr, last_resolved_addr, 48, + CMP_SEMANTIC) == 0; + break; + default: + /* Unknown address type so not local. */ + return false; } - return 0; } + +#ifdef TOR_UNIT_TESTS + +void +resolve_addr_reset_suggested(int family) +{ + tor_addr_make_unspec(&last_suggested_addrs[af_to_idx(family)]); +} + +#endif /* TOR_UNIT_TESTS */ diff --git a/src/app/config/resolve_addr.h b/src/app/config/resolve_addr.h index 3747546402..c279d19f6e 100644 --- a/src/app/config/resolve_addr.h +++ b/src/app/config/resolve_addr.h @@ -9,19 +9,36 @@ #ifndef TOR_CONFIG_RESOLVE_ADDR_H #define TOR_CONFIG_RESOLVE_ADDR_H +#include "app/config/config.h" +#include "core/mainloop/connection.h" + #include "app/config/or_options_st.h" -int resolve_my_address(int warn_severity, const or_options_t *options, - uint32_t *addr_out, - const char **method_out, char **hostname_out); +#define get_orport_addr(family) \ + (portconf_get_first_advertised_addr(CONN_TYPE_OR_LISTENER, family)) + +bool find_my_address(const or_options_t *options, int family, + int warn_severity, tor_addr_t *addr_out, + const char **method_out, char **hostname_out); + +void resolved_addr_get_last(int family, tor_addr_t *addr_out); +void resolved_addr_reset_last(int family); +void resolved_addr_set_last(const tor_addr_t *addr, const char *method_used, + const char *hostname_used); -uint32_t get_last_resolved_addr(void); -void reset_last_resolved_addr(void); +void resolved_addr_get_suggested(int family, tor_addr_t *addr_out); +void resolved_addr_set_suggested(const tor_addr_t *addr); -MOCK_DECL(int, is_local_addr, (const tor_addr_t *addr)); +MOCK_DECL(bool, is_local_to_resolve_addr, (const tor_addr_t *addr)); #ifdef RESOLVE_ADDR_PRIVATE +#ifdef TOR_UNIT_TESTS + +void resolve_addr_reset_suggested(int family); + +#endif /* TOR_UNIT_TESTS */ + #endif /* RESOLVE_ADDR_PRIVATE */ #endif /* TOR_CONFIG_RESOLVE_ADDR_H */ diff --git a/src/app/config/statefile.c b/src/app/config/statefile.c index dcc55f1898..b25167d2ec 100644 --- a/src/app/config/statefile.c +++ b/src/app/config/statefile.c @@ -40,7 +40,7 @@ #include "feature/control/control_events.h" #include "feature/client/entrynodes.h" #include "feature/hibernate/hibernate.h" -#include "feature/stats/rephist.h" +#include "feature/stats/bwhist.h" #include "feature/relay/router.h" #include "feature/relay/routermode.h" #include "lib/sandbox/sandbox.h" @@ -112,6 +112,14 @@ static const config_var_t state_vars_[] = { V(BWHistoryWriteInterval, POSINT, "900"), V(BWHistoryWriteValues, CSV, ""), V(BWHistoryWriteMaxima, CSV, ""), + V(BWHistoryIPv6ReadEnds, ISOTIME, NULL), + V(BWHistoryIPv6ReadInterval, POSINT, "900"), + V(BWHistoryIPv6ReadValues, CSV, ""), + V(BWHistoryIPv6ReadMaxima, CSV, ""), + V(BWHistoryIPv6WriteEnds, ISOTIME, NULL), + V(BWHistoryIPv6WriteInterval, POSINT, "900"), + V(BWHistoryIPv6WriteValues, CSV, ""), + V(BWHistoryIPv6WriteMaxima, CSV, ""), V(BWHistoryDirReadEnds, ISOTIME, NULL), V(BWHistoryDirReadInterval, POSINT, "900"), V(BWHistoryDirReadValues, CSV, ""), @@ -324,7 +332,7 @@ or_state_set(or_state_t *new_state) tor_free(err); ret = -1; } - if (rep_hist_load_state(global_state, &err)<0) { + if (bwhist_load_state(global_state, &err)<0) { log_warn(LD_GENERAL,"Unparseable bandwidth history state: %s",err); tor_free(err); ret = -1; @@ -523,7 +531,7 @@ or_state_save(time_t now) * to avoid redundant writes. */ (void) subsystems_flush_state(get_state_mgr(), global_state); entry_guards_update_state(global_state); - rep_hist_update_state(global_state); + bwhist_update_state(global_state); circuit_build_times_update_state(get_circuit_build_times(), global_state); if (accounting_is_enabled(get_options())) diff --git a/src/app/config/testnet.inc b/src/app/config/testnet.inc index 907c35f97c..00b307782b 100644 --- a/src/app/config/testnet.inc +++ b/src/app/config/testnet.inc @@ -1,8 +1,7 @@ // When modifying, don't forget to update the defaults -// for 'TestingTorNetwork' in 'doc/tor.1.txt' +// for 'TestingTorNetwork' in 'doc/man/tor.1.txt' { "DirAllowPrivateAddresses", "1" }, { "EnforceDistinctSubnets", "0" }, -{ "AssumeReachable", "1" }, { "AuthDirMaxServersPerAddr", "0" }, { "ClientBootstrapConsensusAuthorityDownloadInitialDelay", "0" }, { "ClientBootstrapConsensusFallbackDownloadInitialDelay", "0" }, diff --git a/src/app/include.am b/src/app/include.am index 97d53ec0fd..3caa0bab1c 100644 --- a/src/app/include.am +++ b/src/app/include.am @@ -20,7 +20,7 @@ src_app_tor_LDADD = $(TOR_INTERNAL_LIBS) \ @TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ $(TOR_LIBS_CRYPTLIB) \ @TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_GDI@ @TOR_LIB_USERENV@ \ @CURVE25519_LIBS@ @TOR_SYSTEMD_LIBS@ \ - @TOR_LZMA_LIBS@ @TOR_ZSTD_LIBS@ + @TOR_LZMA_LIBS@ @TOR_ZSTD_LIBS@ @TOR_TRACE_LIBS@ if COVERAGE_ENABLED src_app_tor_cov_SOURCES = $(src_app_tor_SOURCES) @@ -31,5 +31,5 @@ src_app_tor_cov_LDADD = $(TOR_INTERNAL_TESTING_LIBS) \ @TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ $(TOR_LIBS_CRYPTLIB) \ @TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_GDI@ \ @CURVE25519_LIBS@ @TOR_SYSTEMD_LIBS@ \ - @TOR_LZMA_LIBS@ @TOR_ZSTD_LIBS@ + @TOR_LZMA_LIBS@ @TOR_ZSTD_LIBS@ @TOR_TRACE_LIBS@ endif diff --git a/src/app/main/main.c b/src/app/main/main.c index dc39611f98..e09b003b42 100644 --- a/src/app/main/main.c +++ b/src/app/main/main.c @@ -53,18 +53,21 @@ #include "feature/rend/rendcache.h" #include "feature/rend/rendservice.h" #include "feature/stats/predict_ports.h" +#include "feature/stats/bwhist.h" #include "feature/stats/rephist.h" #include "lib/compress/compress.h" #include "lib/buf/buffers.h" #include "lib/crypt_ops/crypto_rand.h" #include "lib/crypt_ops/crypto_s2k.h" #include "lib/net/resolve.h" +#include "lib/trace/trace.h" #include "lib/process/waitpid.h" #include "lib/pubsub/pubsub_build.h" #include "lib/meminfo/meminfo.h" #include "lib/osinfo/uname.h" +#include "lib/osinfo/libc.h" #include "lib/sandbox/sandbox.h" #include "lib/fs/lockfile.h" #include "lib/tls/tortls.h" @@ -336,16 +339,12 @@ dumpstats(int severity) SMARTLIST_FOREACH_BEGIN(get_connection_array(), connection_t *, conn) { int i = conn_sl_idx; tor_log(severity, LD_GENERAL, - "Conn %d (socket %d) type %d (%s), state %d (%s), created %d secs ago", - i, (int)conn->s, conn->type, conn_type_to_string(conn->type), - conn->state, conn_state_to_string(conn->type, conn->state), + "Conn %d (socket %d) is a %s, created %d secs ago", + i, (int)conn->s, + connection_describe(conn), (int)(now - conn->timestamp_created)); if (!connection_is_listener(conn)) { tor_log(severity,LD_GENERAL, - "Conn %d is to %s:%d.", i, - safe_str_client(conn->address), - conn->port); - tor_log(severity,LD_GENERAL, "Conn %d: %d bytes waiting on inbuf (len %d, last read %d secs ago)", i, (int)connection_get_inbuf_len(conn), @@ -549,6 +548,7 @@ tor_init(int argc, char *argv[]) /* Initialize the history structures. */ rep_hist_init(); + bwhist_init(); /* Initialize the service cache. */ rend_cache_init(); addressmap_init(); /* Init the client dns cache. Do it always, since it's @@ -575,7 +575,8 @@ tor_init(int argc, char *argv[]) const char *version = get_version(); log_notice(LD_GENERAL, "Tor %s running on %s with Libevent %s, " - "%s %s, Zlib %s, Liblzma %s, and Libzstd %s.", version, + "%s %s, Zlib %s, Liblzma %s, Libzstd %s and %s %s as libc.", + version, get_uname(), tor_libevent_get_version_str(), crypto_get_library_name(), @@ -585,7 +586,10 @@ tor_init(int argc, char *argv[]) tor_compress_supports_method(LZMA_METHOD) ? tor_compress_version_str(LZMA_METHOD) : "N/A", tor_compress_supports_method(ZSTD_METHOD) ? - tor_compress_version_str(ZSTD_METHOD) : "N/A"); + tor_compress_version_str(ZSTD_METHOD) : "N/A", + tor_libc_get_name() ? + tor_libc_get_name() : "Unknown", + tor_libc_get_version_str()); log_notice(LD_GENERAL, "Tor can't help you if you use it wrong! " "Learn how to be safe at " @@ -602,6 +606,9 @@ tor_init(int argc, char *argv[]) rust_log_welcome_string(); #endif /* defined(HAVE_RUST) */ + /* Warn _if_ the tracing subsystem is built in. */ + tracing_log_warning(); + int init_rv = options_init_from_torrc(argc,argv); if (init_rv < 0) { log_err(LD_CONFIG,"Reading config failed--see warnings above."); @@ -775,12 +782,14 @@ do_dump_config(void) if (!strcmp(arg, "short")) { how = OPTIONS_DUMP_MINIMAL; } else if (!strcmp(arg, "non-builtin")) { - how = OPTIONS_DUMP_DEFAULTS; + // Deprecated since 0.4.5.1-alpha. + fprintf(stderr, "'non-builtin' is deprecated; use 'short' instead.\n"); + how = OPTIONS_DUMP_MINIMAL; } else if (!strcmp(arg, "full")) { how = OPTIONS_DUMP_ALL; } else { fprintf(stderr, "No valid argument to --dump-config found!\n"); - fprintf(stderr, "Please select 'short', 'non-builtin', or 'full'.\n"); + fprintf(stderr, "Please select 'short' or 'full'.\n"); return -1; } @@ -795,8 +804,7 @@ do_dump_config(void) static void init_addrinfo(void) { - if (! server_mode(get_options()) || - (get_options()->Address && strlen(get_options()->Address) > 0)) { + if (! server_mode(get_options()) || get_options()->Address) { /* We don't need to seed our own hostname, because we won't be calling * resolve_my_address on it. */ @@ -822,6 +830,9 @@ sandbox_init_filter(void) #define OPEN(name) \ sandbox_cfg_allow_open_filename(&cfg, tor_strdup(name)) +#define OPENDIR(dir) \ + sandbox_cfg_allow_opendir_dirname(&cfg, tor_strdup(dir)) + #define OPEN_DATADIR(name) \ sandbox_cfg_allow_open_filename(&cfg, get_datadir_fname(name)) @@ -839,7 +850,7 @@ sandbox_init_filter(void) } while (0) #define OPEN_KEY_DIRECTORY() \ - sandbox_cfg_allow_open_filename(&cfg, tor_strdup(options->KeyDirectory)) + OPENDIR(options->KeyDirectory) #define OPEN_CACHEDIR(name) \ sandbox_cfg_allow_open_filename(&cfg, get_cachedir_fname(name)) #define OPEN_CACHEDIR_SUFFIX(name, suffix) do { \ @@ -853,7 +864,7 @@ sandbox_init_filter(void) OPEN_KEYDIR(name suffix); \ } while (0) - OPEN(options->DataDirectory); + OPENDIR(options->DataDirectory); OPEN_KEY_DIRECTORY(); OPEN_CACHEDIR_SUFFIX("cached-certs", ".tmp"); @@ -900,7 +911,11 @@ sandbox_init_filter(void) } SMARTLIST_FOREACH(options->FilesOpenedByIncludes, char *, f, { - OPEN(f); + if (file_status(f) == FN_DIR) { + OPENDIR(f); + } else { + OPEN(f); + } }); #define RENAME_SUFFIX(name, suffix) \ @@ -1013,7 +1028,7 @@ sandbox_init_filter(void) * directory that holds it. */ char *dirname = tor_strdup(port->unix_addr); if (get_parent_directory(dirname) == 0) { - OPEN(dirname); + OPENDIR(dirname); } tor_free(dirname); sandbox_cfg_allow_chmod_filename(&cfg, tor_strdup(port->unix_addr)); @@ -1054,12 +1069,14 @@ sandbox_init_filter(void) OPEN_DATADIR("approved-routers"); OPEN_DATADIR_SUFFIX("fingerprint", ".tmp"); + OPEN_DATADIR_SUFFIX("fingerprint-ed25519", ".tmp"); OPEN_DATADIR_SUFFIX("hashed-fingerprint", ".tmp"); OPEN_DATADIR_SUFFIX("router-stability", ".tmp"); OPEN("/etc/resolv.conf"); RENAME_SUFFIX("fingerprint", ".tmp"); + RENAME_SUFFIX("fingerprint-ed25519", ".tmp"); RENAME_KEYDIR_SUFFIX("secret_onion_key_ntor", ".tmp"); RENAME_KEYDIR_SUFFIX("secret_id_key", ".tmp"); diff --git a/src/app/main/ntmain.c b/src/app/main/ntmain.c index 4941199759..5dc0edd591 100644 --- a/src/app/main/ntmain.c +++ b/src/app/main/ntmain.c @@ -603,7 +603,7 @@ nt_service_install(int argc, char **argv) /* Genericity is apparently _so_ last year in Redmond, where some * accounts are accounts that you can look up, and some accounts * are magic and undetectable via the security subsystem. See - * http://msdn2.microsoft.com/en-us/library/ms684188.aspx + * https://msdn2.microsoft.com/en-us/library/ms684188.aspx */ printf("Running on a Post-Win2K OS, so we'll assume that the " "LocalService account exists.\n"); diff --git a/src/app/main/shutdown.c b/src/app/main/shutdown.c index aac15246b9..4a556333db 100644 --- a/src/app/main/shutdown.c +++ b/src/app/main/shutdown.c @@ -47,6 +47,7 @@ #include "feature/relay/relay_config.h" #include "feature/rend/rendcache.h" #include "feature/rend/rendclient.h" +#include "feature/stats/bwhist.h" #include "feature/stats/geoip_stats.h" #include "feature/stats/rephist.h" #include "lib/evloop/compat_libevent.h" @@ -121,6 +122,7 @@ tor_free_all(int postfork) rend_cache_free_all(); rend_service_authorization_free_all(); rep_hist_free_all(); + bwhist_free_all(); circuit_free_all(); circpad_machines_free(); entry_guards_free_all(); diff --git a/src/app/main/subsystem_list.c b/src/app/main/subsystem_list.c index e32083537f..c6da6f4893 100644 --- a/src/app/main/subsystem_list.c +++ b/src/app/main/subsystem_list.c @@ -26,6 +26,7 @@ #include "lib/thread/thread_sys.h" #include "lib/time/time_sys.h" #include "lib/tls/tortls_sys.h" +#include "lib/trace/trace_sys.h" #include "lib/wallclock/wallclock_sys.h" #include "lib/evloop/evloop_sys.h" @@ -47,6 +48,8 @@ const subsys_fns_t *tor_subsystems[] = { &sys_logging, &sys_threads, + &sys_tracing, + &sys_time, &sys_crypto, diff --git a/src/config/mmdb-convert.py b/src/config/mmdb-convert.py index a58f5d43e1..4acfea6c0e 100644 --- a/src/config/mmdb-convert.py +++ b/src/config/mmdb-convert.py @@ -9,7 +9,7 @@ # # You should have received a copy of the CC0 legalcode along with this # work in doc/cc0.txt. If not, see -# <http://creativecommons.org/publicdomain/zero/1.0/>. +# <https://creativecommons.org/publicdomain/zero/1.0/>. # Nick Mathewson is responsible for this kludge, but takes no # responsibility for it. diff --git a/src/core/crypto/onion_crypto.c b/src/core/crypto/onion_crypto.c index 69b4dc40aa..1f34be1cc1 100644 --- a/src/core/crypto/onion_crypto.c +++ b/src/core/crypto/onion_crypto.c @@ -31,7 +31,7 @@ **/ #include "core/or/or.h" -#include "core/or/circuitbuild.h" +#include "core/or/extendinfo.h" #include "core/crypto/onion_crypto.h" #include "core/crypto/onion_fast.h" #include "core/crypto/onion_ntor.h" diff --git a/src/core/mainloop/connection.c b/src/core/mainloop/connection.c index a8417e46d9..efde9df7ed 100644 --- a/src/core/mainloop/connection.c +++ b/src/core/mainloop/connection.c @@ -105,7 +105,9 @@ #include "feature/relay/routermode.h" #include "feature/rend/rendclient.h" #include "feature/rend/rendcommon.h" +#include "feature/stats/connstats.h" #include "feature/stats/rephist.h" +#include "feature/stats/bwhist.h" #include "lib/crypt_ops/crypto_util.h" #include "lib/geoip/geoip.h" @@ -218,8 +220,12 @@ static smartlist_t *outgoing_addrs = NULL; /**************************************************************/ -/** Convert a connection_t* to an listener_connection_t*; assert if the cast - * is invalid. */ +/** + * Cast a `connection_t *` to a `listener_connection_t *`. + * + * Exit with an assertion failure if the input is not a + * `listener_connection_t`. + **/ listener_connection_t * TO_LISTENER_CONN(connection_t *c) { @@ -227,6 +233,18 @@ TO_LISTENER_CONN(connection_t *c) return DOWNCAST(listener_connection_t, c); } +/** + * Cast a `const connection_t *` to a `const listener_connection_t *`. + * + * Exit with an assertion failure if the input is not a + * `listener_connection_t`. + **/ +const listener_connection_t * +CONST_TO_LISTENER_CONN(const connection_t *c) +{ + return TO_LISTENER_CONN((connection_t *)c); +} + size_t connection_get_inbuf_len(connection_t *conn) { @@ -350,13 +368,179 @@ conn_state_to_string(int type, int state) break; } + if (state == 0) { + return "uninitialized"; + } + log_warn(LD_BUG, "unknown connection state %d (type %d)", state, type); tor_snprintf(buf, sizeof(buf), "unknown state [%d] on unknown [%s] connection", state, conn_type_to_string(type)); + tor_assert_nonfatal_unreached_once(); return buf; } +/** + * Helper: describe the peer or address of connection @a conn in a + * human-readable manner. + * + * Returns a pointer to a static buffer; future calls to + * connection_describe_peer_internal() will invalidate this buffer. + * + * If <b>include_preposition</b> is true, include a preposition before the + * peer address. + * + * Nobody should parse the output of this function; it can and will change in + * future versions of tor. + **/ +static const char * +connection_describe_peer_internal(const connection_t *conn, + bool include_preposition) +{ + IF_BUG_ONCE(!conn) { + return "null peer"; + } + + static char peer_buf[256]; + const tor_addr_t *addr = &conn->addr; + const char *address = NULL; + const char *prep; + bool scrub = false; + char extra_buf[128]; + extra_buf[0] = 0; + + /* First, figure out the preposition to use */ + switch (conn->type) { + CASE_ANY_LISTENER_TYPE: + prep = "on"; + break; + case CONN_TYPE_EXIT: + prep = "to"; + break; + case CONN_TYPE_CONTROL: + case CONN_TYPE_AP: + case CONN_TYPE_EXT_OR: + prep = "from"; + break; + default: + prep = "with"; + break; + } + + /* Now figure out the address. */ + if (conn->socket_family == AF_UNIX) { + /* For unix sockets, we always use the `address` string. */ + address = conn->address ? conn->address : "unix socket"; + } else if (conn->type == CONN_TYPE_OR) { + /* For OR connections, we have a lot to do. */ + const or_connection_t *or_conn = CONST_TO_OR_CONN(conn); + /* We report the IDs we're talking to... */ + if (fast_digest_is_zero(or_conn->identity_digest)) { + // This could be a client, so scrub it. No identity to report. + scrub = true; + } else { + char id_buf[HEX_DIGEST_LEN+1]; + base16_encode(id_buf, sizeof(id_buf), + or_conn->identity_digest, DIGEST_LEN); + tor_snprintf(extra_buf, sizeof(extra_buf), + " ID=%s", id_buf); + } + if (! scrub && (! tor_addr_eq(addr, &or_conn->canonical_orport.addr) || + conn->port != or_conn->canonical_orport.port)) { + /* We report canonical address, if it's different */ + char canonical_addr_buf[TOR_ADDR_BUF_LEN]; + if (tor_addr_to_str(canonical_addr_buf, &or_conn->canonical_orport.addr, + sizeof(canonical_addr_buf), 1)) { + tor_snprintf(extra_buf+strlen(extra_buf), + sizeof(extra_buf)-strlen(extra_buf), + " canonical_addr=%s:%"PRIu16, + canonical_addr_buf, + or_conn->canonical_orport.port); + } + } + } else if (conn->type == CONN_TYPE_EXIT) { + scrub = true; /* This is a client's request; scrub it with SafeLogging. */ + if (tor_addr_is_null(addr)) { + address = conn->address; + strlcpy(extra_buf, " (DNS lookup pending)", sizeof(extra_buf)); + } + } + + char addr_buf[TOR_ADDR_BUF_LEN]; + if (address == NULL) { + if (tor_addr_family(addr) == 0) { + address = "<unset>"; + } else { + address = tor_addr_to_str(addr_buf, addr, sizeof(addr_buf), 1); + if (!address) { + address = "<can't format!>"; + tor_assert_nonfatal_unreached_once(); + } + } + } + + char portbuf[7]; + portbuf[0]=0; + if (scrub && get_options()->SafeLogging_ != SAFELOG_SCRUB_NONE) { + address = "[scrubbed]"; + } else { + /* Only set the port if we're not scrubbing the address. */ + if (conn->port != 0) { + tor_snprintf(portbuf, sizeof(portbuf), ":%d", conn->port); + } + } + + const char *sp = include_preposition ? " " : ""; + if (! include_preposition) + prep = ""; + + tor_snprintf(peer_buf, sizeof(peer_buf), + "%s%s%s%s%s", prep, sp, address, portbuf, extra_buf); + return peer_buf; +} + +/** + * Describe the peer or address of connection @a conn in a + * human-readable manner. + * + * Returns a pointer to a static buffer; future calls to + * connection_describe_peer() or connection_describe() will invalidate this + * buffer. + * + * Nobody should parse the output of this function; it can and will change in + * future versions of tor. + **/ +const char * +connection_describe_peer(const connection_t *conn) +{ + return connection_describe_peer_internal(conn, false); +} + +/** + * Describe a connection for logging purposes. + * + * Returns a pointer to a static buffer; future calls to connection_describe() + * will invalidate this buffer. + * + * Nobody should parse the output of this function; it can and will change in + * future versions of tor. + **/ +const char * +connection_describe(const connection_t *conn) +{ + IF_BUG_ONCE(!conn) { + return "null connection"; + } + static char desc_buf[256]; + const char *peer = connection_describe_peer_internal(conn, true); + tor_snprintf(desc_buf, sizeof(desc_buf), + "%s connection (%s) %s", + conn_type_to_string(conn->type), + conn_state_to_string(conn->type, conn->state), + peer); + return desc_buf; +} + /** Allocate and return a new dir_connection_t, initialized as by * connection_init(). */ dir_connection_t * @@ -382,6 +566,7 @@ or_connection_new(int type, int socket_family) tor_assert(type == CONN_TYPE_OR || type == CONN_TYPE_EXT_OR); connection_init(now, TO_CONN(or_conn), type, socket_family); + tor_addr_make_unspec(&or_conn->canonical_orport.addr); connection_or_set_canonical(or_conn, 0); if (type == CONN_TYPE_EXT_OR) @@ -1545,13 +1730,8 @@ connection_listener_new(const struct sockaddr *listensockaddr, */ connection_check_oos(get_n_open_sockets(), 0); - if (conn->socket_family == AF_UNIX) { - log_notice(LD_NET, "Opened %s on %s", - conn_type_to_string(type), conn->address); - } else { - log_notice(LD_NET, "Opened %s on %s", - conn_type_to_string(type), fmt_addrport(&addr, gotPort)); - } + log_notice(LD_NET, "Opened %s", connection_describe(conn)); + return conn; err: @@ -2069,16 +2249,7 @@ connection_connect_log_client_use_ip_version(const connection_t *conn) ? fascist_firewall_prefer_ipv6_orport(options) : fascist_firewall_prefer_ipv6_dirport(options)); tor_addr_t real_addr; - tor_addr_make_null(&real_addr, AF_UNSPEC); - - /* OR conns keep the original address in real_addr, as addr gets overwritten - * with the descriptor address */ - if (conn->type == CONN_TYPE_OR) { - const or_connection_t *or_conn = TO_OR_CONN((connection_t *)conn); - tor_addr_copy(&real_addr, &or_conn->real_addr); - } else if (conn->type == CONN_TYPE_DIR) { - tor_addr_copy(&real_addr, &conn->addr); - } + tor_addr_copy(&real_addr, &conn->addr); /* Check if we broke a mandatory address family restriction */ if ((must_ipv4 && tor_addr_family(&real_addr) == AF_INET6) @@ -2604,8 +2775,8 @@ connection_read_https_proxy_response(connection_t *conn) if (parse_http_response(headers, &status_code, &date_header, NULL, &reason) < 0) { log_warn(LD_NET, - "Unparseable headers from proxy (connecting to '%s'). Closing.", - conn->address); + "Unparseable headers from proxy (%s). Closing.", + connection_describe(conn)); tor_free(headers); return -1; } @@ -2614,8 +2785,8 @@ connection_read_https_proxy_response(connection_t *conn) if (status_code == 200) { log_info(LD_NET, - "HTTPS connect to '%s' successful! (200 %s) Starting TLS.", - conn->address, escaped(reason)); + "HTTPS connect for %s successful! (200 %s) Starting TLS.", + connection_describe(conn), escaped(reason)); tor_free(reason); return 1; } @@ -2831,16 +3002,16 @@ connection_read_proxy_handshake(connection_t *conn) if (ret < 0) { if (reason) { - log_warn(LD_NET, "Proxy Client: unable to connect to %s:%d (%s)", - conn->address, conn->port, escaped(reason)); + log_warn(LD_NET, "Proxy Client: unable to connect %s (%s)", + connection_describe(conn), escaped(reason)); tor_free(reason); } else { - log_warn(LD_NET, "Proxy Client: unable to connect to %s:%d", - conn->address, conn->port); + log_warn(LD_NET, "Proxy Client: unable to connect %s", + connection_describe(conn)); } } else if (ret == 1) { - log_info(LD_NET, "Proxy Client: connection to %s:%d successful", - conn->address, conn->port); + log_info(LD_NET, "Proxy Client: %s successful", + connection_describe(conn)); } return ret; @@ -2997,10 +3168,10 @@ retry_all_listeners(smartlist_t *new_conns, int close_all_noncontrol) smartlist_t *replacements = smartlist_new(); const or_options_t *options = get_options(); int retval = 0; - const uint16_t old_or_port = router_get_advertised_or_port(options); + const uint16_t old_or_port = routerconf_find_or_port(options, AF_INET); const uint16_t old_or_port_ipv6 = - router_get_advertised_or_port_by_af(options,AF_INET6); - const uint16_t old_dir_port = router_get_advertised_dir_port(options, 0); + routerconf_find_or_port(options,AF_INET6); + const uint16_t old_dir_port = routerconf_find_dir_port(options, 0); SMARTLIST_FOREACH_BEGIN(get_connection_array(), connection_t *, conn) { if (connection_is_listener(conn) && !conn->marked_for_close) @@ -3031,8 +3202,8 @@ retry_all_listeners(smartlist_t *new_conns, int close_all_noncontrol) connection_t *old_conn = r->old_conn; if (skip) { - log_debug(LD_NET, "Skipping creating new listener for %s:%d", - old_conn->address, old_conn->port); + log_debug(LD_NET, "Skipping creating new listener for %s", + connection_describe(old_conn)); continue; } @@ -3048,10 +3219,11 @@ retry_all_listeners(smartlist_t *new_conns, int close_all_noncontrol) smartlist_add(new_conns, new_conn); - log_notice(LD_NET, "Closed no-longer-configured %s on %s:%d " - "(replaced by %s:%d)", - conn_type_to_string(old_conn->type), old_conn->address, - old_conn->port, new_conn->address, new_conn->port); + char *old_desc = tor_strdup(connection_describe(old_conn)); + log_notice(LD_NET, "Closed no-longer-configured %s " + "(replaced by %s)", + old_desc, connection_describe(new_conn)); + tor_free(old_desc); } SMARTLIST_FOREACH_END(r); #endif /* defined(ENABLE_LISTENER_REBIND) */ @@ -3069,10 +3241,9 @@ retry_all_listeners(smartlist_t *new_conns, int close_all_noncontrol) SMARTLIST_FOREACH(replacements, listener_replacement_t *, r, tor_free(r)); smartlist_free(replacements); - if (old_or_port != router_get_advertised_or_port(options) || - old_or_port_ipv6 != router_get_advertised_or_port_by_af(options, - AF_INET6) || - old_dir_port != router_get_advertised_dir_port(options, 0)) { + if (old_or_port != routerconf_find_or_port(options, AF_INET) || + old_or_port_ipv6 != routerconf_find_or_port(options, AF_INET6) || + old_dir_port != routerconf_find_dir_port(options, 0)) { /* Our chosen ORPort or DirPort is not what it used to be: the * descriptor we had (if any) should be regenerated. (We won't * automatically notice this because of changes in the option, @@ -3344,9 +3515,9 @@ record_num_bytes_transferred_impl(connection_t *conn, /* Count bytes of answering direct and tunneled directory requests */ if (conn->type == CONN_TYPE_DIR && conn->purpose == DIR_PURPOSE_SERVER) { if (num_read > 0) - rep_hist_note_dir_bytes_read(num_read, now); + bwhist_note_dir_bytes_read(num_read, now); if (num_written > 0) - rep_hist_note_dir_bytes_written(num_written, now); + bwhist_note_dir_bytes_written(num_written, now); } /* Linked connections and internal IPs aren't counted for statistics or @@ -3361,15 +3532,16 @@ record_num_bytes_transferred_impl(connection_t *conn, if (!connection_is_rate_limited(conn)) return; + const bool is_ipv6 = (conn->socket_family == AF_INET6); if (conn->type == CONN_TYPE_OR) - rep_hist_note_or_conn_bytes(conn->global_identifier, num_read, - num_written, now); + conn_stats_note_or_conn_bytes(conn->global_identifier, num_read, + num_written, now, is_ipv6); if (num_read > 0) { - rep_hist_note_bytes_read(num_read, now); + bwhist_note_bytes_read(num_read, now, is_ipv6); } if (num_written > 0) { - rep_hist_note_bytes_written(num_written, now); + bwhist_note_bytes_written(num_written, now, is_ipv6); } if (conn->type == CONN_TYPE_EXIT) rep_hist_note_exit_bytes(conn->port, num_written, num_read); @@ -3858,17 +4030,14 @@ connection_buf_read_from_socket(connection_t *conn, ssize_t *max_to_read, switch (result) { case TOR_TLS_CLOSE: case TOR_TLS_ERROR_IO: - log_debug(LD_NET,"TLS connection closed %son read. Closing. " - "(Nickname %s, address %s)", - result == TOR_TLS_CLOSE ? "cleanly " : "", - or_conn->nickname ? or_conn->nickname : "not set", - conn->address); + log_debug(LD_NET,"TLS %s closed %son read. Closing.", + connection_describe(conn), + result == TOR_TLS_CLOSE ? "cleanly " : ""); return result; CASE_TOR_TLS_ERROR_ANY_NONIO: - log_debug(LD_NET,"tls error [%s]. breaking (nickname %s, address %s).", + log_debug(LD_NET,"tls error [%s] from %s. Breaking.", tor_tls_err_to_string(result), - or_conn->nickname ? or_conn->nickname : "not set", - conn->address); + connection_describe(conn)); return result; case TOR_TLS_WANTWRITE: connection_start_writing(conn); @@ -4205,6 +4374,7 @@ connection_handle_write_impl(connection_t *conn, int force) switch (result) { CASE_TOR_TLS_ERROR_ANY: case TOR_TLS_CLOSE: + or_conn->tls_error = result; log_info(LD_NET, result != TOR_TLS_CLOSE ? "tls error. breaking.":"TLS connection closed on flush"); /* Don't flush; connection is dead. */ @@ -4720,7 +4890,7 @@ any_other_active_or_conns(const or_connection_t *this_conn) connection_t *conn = connection_get_another_active_or_conn(this_conn); if (conn != NULL) { log_debug(LD_DIR, "%s: Found an OR connection: %s", - __func__, conn->address); + __func__, connection_describe(conn)); return 1; } @@ -4870,7 +5040,7 @@ client_check_address_changed(tor_socket_t sock) smartlist_clear(outgoing_addrs); smartlist_add(outgoing_addrs, tor_memdup(&out_addr, sizeof(tor_addr_t))); /* We'll need to resolve ourselves again. */ - reset_last_resolved_addr(); + resolved_addr_reset_last(AF_INET); /* Okay, now change our keys. */ ip_address_changed(1); } diff --git a/src/core/mainloop/connection.h b/src/core/mainloop/connection.h index bcd3d590a5..ee3dce49f4 100644 --- a/src/core/mainloop/connection.h +++ b/src/core/mainloop/connection.h @@ -31,6 +31,8 @@ struct tor_addr_t; struct or_options_t; struct listener_connection_t *TO_LISTENER_CONN(struct connection_t *); +const struct listener_connection_t *CONST_TO_LISTENER_CONN( + const struct connection_t *); struct buf_t; @@ -116,6 +118,9 @@ const char *conn_type_to_string(int type); const char *conn_state_to_string(int type, int state); int conn_listener_type_supports_af_unix(int type); +const char *connection_describe(const connection_t *conn); +const char *connection_describe_peer(const connection_t *conn); + struct dir_connection_t *dir_connection_new(int socket_family); struct or_connection_t *or_connection_new(int type, int socket_family); struct edge_connection_t *edge_connection_new(int type, int socket_family); diff --git a/src/core/mainloop/mainloop.c b/src/core/mainloop/mainloop.c index e4e17f6b76..3bf9be566b 100644 --- a/src/core/mainloop/mainloop.c +++ b/src/core/mainloop/mainloop.c @@ -95,6 +95,7 @@ #include "feature/rend/rendservice.h" #include "feature/stats/geoip_stats.h" #include "feature/stats/predict_ports.h" +#include "feature/stats/connstats.h" #include "feature/stats/rephist.h" #include "lib/buf/buffers.h" #include "lib/crypt_ops/crypto_rand.h" @@ -1950,7 +1951,7 @@ write_stats_file_callback(time_t now, const or_options_t *options) next_time_to_write_stats_files = next_write; } if (options->ConnDirectionStatistics) { - time_t next_write = rep_hist_conn_stats_write(now); + time_t next_write = conn_stats_save(now); if (next_write && next_write < next_time_to_write_stats_files) next_time_to_write_stats_files = next_write; } diff --git a/src/core/or/channel.c b/src/core/or/channel.c index a05554472f..5a42d452f2 100644 --- a/src/core/or/channel.c +++ b/src/core/or/channel.c @@ -71,6 +71,7 @@ #include "core/or/relay.h" #include "core/or/scheduler.h" #include "feature/client/entrynodes.h" +#include "feature/nodelist/dirlist.h" #include "feature/nodelist/networkstatus.h" #include "feature/nodelist/nodelist.h" #include "feature/nodelist/routerlist.h" @@ -83,13 +84,6 @@ #include "core/or/cell_queue_st.h" -/* Static function prototypes */ - -static bool channel_matches_target_addr_for_extend( - channel_t *chan, - const tor_addr_t *target_ipv4_addr, - const tor_addr_t *target_ipv6_addr); - /* Global lists of channels */ /* All channel_t instances */ @@ -755,6 +749,7 @@ channel_check_for_duplicates(void) { channel_idmap_entry_t **iter; channel_t *chan; + int total_dirauth_connections = 0, total_dirauths = 0; int total_relay_connections = 0, total_relays = 0, total_canonical = 0; int total_half_canonical = 0; int total_gt_one_connection = 0, total_gt_two_connections = 0; @@ -762,13 +757,18 @@ channel_check_for_duplicates(void) HT_FOREACH(iter, channel_idmap, &channel_identity_map) { int connections_to_relay = 0; + const char *id_digest = (char *) (*iter)->digest; /* Only consider relay connections */ - if (!connection_or_digest_is_known_relay((char*)(*iter)->digest)) + if (!connection_or_digest_is_known_relay(id_digest)) continue; total_relays++; + const bool is_dirauth = router_digest_is_trusted_dir(id_digest); + if (is_dirauth) + total_dirauths++; + for (chan = TOR_LIST_FIRST(&(*iter)->channel_list); chan; chan = channel_next_with_rsa_identity(chan)) { @@ -777,6 +777,8 @@ channel_check_for_duplicates(void) connections_to_relay++; total_relay_connections++; + if (is_dirauth) + total_dirauth_connections++; if (chan->is_canonical(chan, 0)) total_canonical++; @@ -791,11 +793,28 @@ channel_check_for_duplicates(void) if (connections_to_relay > 4) total_gt_four_connections++; } -#define MIN_RELAY_CONNECTIONS_TO_WARN 5 + /* Don't bother warning about excessive connections unless we have + * at least this many connections, total. + */ +#define MIN_RELAY_CONNECTIONS_TO_WARN 25 + /* If the average number of connections for a regular relay is more than + * this, that's too high. + */ +#define MAX_AVG_RELAY_CONNECTIONS 1.5 + /* If the average number of connections for a dirauth is more than + * this, that's too high. + */ +#define MAX_AVG_DIRAUTH_CONNECTIONS 4 + + /* How many connections total would be okay, given the number of + * relays and dirauths that we have connections to? */ + const int max_tolerable_connections = (int)( + (total_relays-total_dirauths) * MAX_AVG_RELAY_CONNECTIONS + + total_dirauths * MAX_AVG_DIRAUTH_CONNECTIONS); /* If we average 1.5 or more connections per relay, something is wrong */ if (total_relays > MIN_RELAY_CONNECTIONS_TO_WARN && - total_relay_connections >= 1.5*total_relays) { + total_relay_connections > max_tolerable_connections) { log_notice(LD_OR, "Your relay has a very large number of connections to other relays. " "Is your outbound address the same as your relay address? " @@ -2560,7 +2579,7 @@ channel_dump_statistics, (channel_t *chan, int severity)) /* Handle remote address and descriptions */ have_remote_addr = channel_get_addr_if_possible(chan, &remote_addr); if (have_remote_addr) { - char *actual = tor_strdup(channel_get_actual_remote_descr(chan)); + char *actual = tor_strdup(channel_describe_peer(chan)); remote_addr_str = tor_addr_to_str_dup(&remote_addr); tor_log(severity, LD_GENERAL, " * Channel %"PRIu64 " says its remote address" @@ -2568,18 +2587,18 @@ channel_dump_statistics, (channel_t *chan, int severity)) "actual description of \"%s\"", (chan->global_identifier), safe_str(remote_addr_str), - safe_str(channel_get_canonical_remote_descr(chan)), + safe_str(channel_describe_peer(chan)), safe_str(actual)); tor_free(remote_addr_str); tor_free(actual); } else { - char *actual = tor_strdup(channel_get_actual_remote_descr(chan)); + char *actual = tor_strdup(channel_describe_peer(chan)); tor_log(severity, LD_GENERAL, " * Channel %"PRIu64 " does not know its remote " "address, but gives a canonical description of \"%s\" and an " "actual description of \"%s\"", (chan->global_identifier), - channel_get_canonical_remote_descr(chan), + channel_describe_peer(chan), actual); tor_free(actual); } @@ -2789,75 +2808,41 @@ channel_listener_dump_transport_statistics(channel_listener_t *chan_l, } /** - * Return text description of the remote endpoint. - * - * This function return a test provided by the lower layer of the remote - * endpoint for this channel; it should specify the actual address connected - * to/from. - * - * Subsequent calls to channel_get_{actual,canonical}_remote_{address,descr} - * may invalidate the return value from this function. - */ -const char * -channel_get_actual_remote_descr(channel_t *chan) -{ - tor_assert(chan); - tor_assert(chan->get_remote_descr); - - /* Param 1 indicates the actual description */ - return chan->get_remote_descr(chan, GRD_FLAG_ORIGINAL); -} - -/** - * Return the text address of the remote endpoint. - * - * Subsequent calls to channel_get_{actual,canonical}_remote_{address,descr} - * may invalidate the return value from this function. - */ -const char * -channel_get_actual_remote_address(channel_t *chan) -{ - /* Param 1 indicates the actual description */ - return chan->get_remote_descr(chan, GRD_FLAG_ORIGINAL|GRD_FLAG_ADDR_ONLY); -} - -/** * Return text description of the remote endpoint canonical address. * - * This function return a test provided by the lower layer of the remote - * endpoint for this channel; it should use the known canonical address for - * this OR's identity digest if possible. + * This function returns a human-readable string for logging; nothing + * should parse it or rely on a particular format. * - * Subsequent calls to channel_get_{actual,canonical}_remote_{address,descr} - * may invalidate the return value from this function. + * Subsequent calls to this function may invalidate its return value. */ MOCK_IMPL(const char *, -channel_get_canonical_remote_descr,(channel_t *chan)) +channel_describe_peer,(channel_t *chan)) { tor_assert(chan); - tor_assert(chan->get_remote_descr); + tor_assert(chan->describe_peer); - /* Param 0 indicates the canonicalized description */ - return chan->get_remote_descr(chan, 0); + return chan->describe_peer(chan); } /** - * Get remote address if possible. + * Get the remote address for this channel, if possible. * * Write the remote address out to a tor_addr_t if the underlying transport * supports this operation, and return 1. Return 0 if the underlying transport * doesn't let us do this. + * + * Always returns the "real" address of the peer -- the one we're connected to + * on the internet. */ MOCK_IMPL(int, -channel_get_addr_if_possible,(channel_t *chan, tor_addr_t *addr_out)) +channel_get_addr_if_possible,(const channel_t *chan, + tor_addr_t *addr_out)) { tor_assert(chan); tor_assert(addr_out); + tor_assert(chan->get_remote_addr); - if (chan->get_remote_addr) - return chan->get_remote_addr(chan, addr_out); - /* Else no support, method not implemented */ - else return 0; + return chan->get_remote_addr(chan, addr_out); } /** @@ -3296,6 +3281,9 @@ channel_when_last_xmit(channel_t *chan) * * This function calls the lower layer and asks if this channel matches a * given extend_info_t. + * + * NOTE that this function only checks for an address/port match, and should + * be used only when no identity is available. */ int channel_matches_extend_info(channel_t *chan, extend_info_t *extend_info) @@ -3317,7 +3305,7 @@ channel_matches_extend_info(channel_t *chan, extend_info_t *extend_info) * This function calls into the lower layer and asks if this channel thinks * it matches the target addresses for circuit extension purposes. */ -static bool +STATIC bool channel_matches_target_addr_for_extend(channel_t *chan, const tor_addr_t *target_ipv4_addr, const tor_addr_t *target_ipv6_addr) diff --git a/src/core/or/channel.h b/src/core/or/channel.h index 4968c8714a..d52ebdf619 100644 --- a/src/core/or/channel.h +++ b/src/core/or/channel.h @@ -329,24 +329,18 @@ struct channel_t { */ double (*get_overhead_estimate)(channel_t *); /* - * Ask the underlying transport what the remote endpoint address is, in - * a tor_addr_t. This is optional and subclasses may leave this NULL. - * If they implement it, they should write the address out to the - * provided tor_addr_t *, and return 1 if successful or 0 if no address - * available. + * Ask the underlying transport what the remote endpoint address is, in a + * tor_addr_t. Write the address out to the provided tor_addr_t *, and + * return 1 if successful or 0 if no address available. */ - int (*get_remote_addr)(channel_t *, tor_addr_t *); + int (*get_remote_addr)(const channel_t *, tor_addr_t *); int (*get_transport_name)(channel_t *chan, char **transport_out); -#define GRD_FLAG_ORIGINAL 1 -#define GRD_FLAG_ADDR_ONLY 2 /** - * Get a text description of the remote endpoint; canonicalized if the flag - * GRD_FLAG_ORIGINAL is not set, or the one we originally connected - * to/received from if it is. If GRD_FLAG_ADDR_ONLY is set, we return only - * the original address. + * Get a human-readable text description of the remote endpoint, for + * logging. */ - const char * (*get_remote_descr)(channel_t *, int); + const char * (*describe_peer)(const channel_t *); /** Check if the lower layer has queued writes */ int (*has_queued_writes)(channel_t *); /** @@ -562,7 +556,10 @@ void channel_listener_dumpstats(int severity); #ifdef CHANNEL_FILE_PRIVATE STATIC void channel_add_to_digest_map(channel_t *chan); - +STATIC bool channel_matches_target_addr_for_extend( + channel_t *chan, + const tor_addr_t *target_ipv4_addr, + const tor_addr_t *target_ipv6_addr); #endif /* defined(CHANNEL_FILE_PRIVATE) */ /* Channel operations for subclasses and internal use only */ @@ -721,11 +718,9 @@ channel_is_in_state(channel_t *chan, channel_state_t state) const char * channel_describe_transport(channel_t *chan); MOCK_DECL(void, channel_dump_statistics, (channel_t *chan, int severity)); void channel_dump_transport_statistics(channel_t *chan, int severity); -const char * channel_get_actual_remote_descr(channel_t *chan); -const char * channel_get_actual_remote_address(channel_t *chan); -MOCK_DECL(int, channel_get_addr_if_possible, (channel_t *chan, +MOCK_DECL(int, channel_get_addr_if_possible, (const channel_t *chan, tor_addr_t *addr_out)); -MOCK_DECL(const char *, channel_get_canonical_remote_descr,(channel_t *chan)); +MOCK_DECL(const char *, channel_describe_peer,(channel_t *chan)); int channel_has_queued_writes(channel_t *chan); int channel_is_bad_for_new_circs(channel_t *chan); void channel_mark_bad_for_new_circs(channel_t *chan); diff --git a/src/core/or/channelpadding.c b/src/core/or/channelpadding.c index be2ce78a17..d0c43e8bdc 100644 --- a/src/core/or/channelpadding.c +++ b/src/core/or/channelpadding.c @@ -90,7 +90,7 @@ static int consensus_nf_pad_single_onion; * for every single connection, every second. */ void -channelpadding_new_consensus_params(networkstatus_t *ns) +channelpadding_new_consensus_params(const networkstatus_t *ns) { #define DFLT_NETFLOW_INACTIVE_KEEPALIVE_LOW 1500 #define DFLT_NETFLOW_INACTIVE_KEEPALIVE_HIGH 9500 @@ -265,7 +265,7 @@ channelpadding_update_padding_for_channel(channel_t *chan, log_fn_ratelim(&relay_limit,LOG_PROTOCOL_WARN,LD_PROTOCOL, "Got a PADDING_NEGOTIATE from relay at %s (%s). " "This should not happen.", - chan->get_remote_descr(chan, 0), + channel_describe_peer(chan), hex_str(chan->identity_digest, DIGEST_LEN)); return -1; } @@ -399,7 +399,7 @@ channelpadding_send_padding_cell_for_callback(channel_t *chan) "Sending netflow keepalive on %"PRIu64" to %s (%s) after " "%"PRId64" ms. Delta %"PRId64"ms", (chan->global_identifier), - safe_str_client(chan->get_remote_descr(chan, 0)), + safe_str_client(channel_describe_peer(chan)), safe_str_client(hex_str(chan->identity_digest, DIGEST_LEN)), (monotime_coarse_diff_msec(&chan->timestamp_xfer,&now)), ( diff --git a/src/core/or/channelpadding.h b/src/core/or/channelpadding.h index d1c7192ffd..9246988cdc 100644 --- a/src/core/or/channelpadding.h +++ b/src/core/or/channelpadding.h @@ -37,7 +37,6 @@ int channelpadding_send_enable_command(channel_t *chan, uint16_t low_timeout, int channelpadding_get_circuits_available_timeout(void); unsigned int channelpadding_get_channel_idle_timeout(const channel_t *, int); -void channelpadding_new_consensus_params(networkstatus_t *ns); +void channelpadding_new_consensus_params(const networkstatus_t *ns); #endif /* !defined(TOR_CHANNELPADDING_H) */ - diff --git a/src/core/or/channeltls.c b/src/core/or/channeltls.c index 395fbf3455..ae60038c34 100644 --- a/src/core/or/channeltls.c +++ b/src/core/or/channeltls.c @@ -63,15 +63,16 @@ #include "trunnel/channelpadding_negotiation.h" #include "trunnel/netinfo.h" #include "core/or/channelpadding.h" +#include "core/or/extendinfo.h" #include "core/or/cell_st.h" #include "core/or/cell_queue_st.h" -#include "core/or/extend_info_st.h" #include "core/or/or_connection_st.h" #include "core/or/or_handshake_certs_st.h" #include "core/or/or_handshake_state_st.h" #include "feature/nodelist/routerinfo_st.h" #include "core/or/var_cell_st.h" +#include "src/feature/relay/relay_find_addr.h" #include "lib/tls/tortls.h" #include "lib/tls/x509.h" @@ -102,12 +103,11 @@ static void channel_tls_close_method(channel_t *chan); static const char * channel_tls_describe_transport_method(channel_t *chan); static void channel_tls_free_method(channel_t *chan); static double channel_tls_get_overhead_estimate_method(channel_t *chan); -static int -channel_tls_get_remote_addr_method(channel_t *chan, tor_addr_t *addr_out); +static int channel_tls_get_remote_addr_method(const channel_t *chan, + tor_addr_t *addr_out); static int channel_tls_get_transport_name_method(channel_t *chan, char **transport_out); -static const char * -channel_tls_get_remote_descr_method(channel_t *chan, int flags); +static const char *channel_tls_describe_peer_method(const channel_t *chan); static int channel_tls_has_queued_writes_method(channel_t *chan); static int channel_tls_is_canonical_method(channel_t *chan, int req); static int @@ -163,7 +163,7 @@ channel_tls_common_init(channel_tls_t *tlschan) chan->free_fn = channel_tls_free_method; chan->get_overhead_estimate = channel_tls_get_overhead_estimate_method; chan->get_remote_addr = channel_tls_get_remote_addr_method; - chan->get_remote_descr = channel_tls_get_remote_descr_method; + chan->describe_peer = channel_tls_describe_peer_method; chan->get_transport_name = channel_tls_get_transport_name_method; chan->has_queued_writes = channel_tls_has_queued_writes_method; chan->is_canonical = channel_tls_is_canonical_method; @@ -203,7 +203,7 @@ channel_tls_connect(const tor_addr_t *addr, uint16_t port, tlschan, (chan->global_identifier)); - if (is_local_addr(addr)) { + if (is_local_to_resolve_addr(addr)) { log_debug(LD_CHANNEL, "Marking new outgoing channel %"PRIu64 " at %p as local", (chan->global_identifier), chan); @@ -340,7 +340,7 @@ channel_tls_handle_incoming(or_connection_t *orconn) tlschan->conn = orconn; orconn->chan = tlschan; - if (is_local_addr(&(TO_CONN(orconn)->addr))) { + if (is_local_to_resolve_addr(&(TO_CONN(orconn)->addr))) { log_debug(LD_CHANNEL, "Marking new incoming channel %"PRIu64 " at %p as local", (chan->global_identifier), chan); @@ -389,6 +389,25 @@ channel_tls_from_base(channel_t *chan) return (channel_tls_t *)(chan); } +/** + * Cast a const channel_tls_t to a const channel_t. + */ +const channel_t * +channel_tls_to_base_const(const channel_tls_t *tlschan) +{ + return channel_tls_to_base((channel_tls_t*) tlschan); +} + +/** + * Cast a const channel_t to a const channel_tls_t, with appropriate + * type-checking asserts. + */ +const channel_tls_t * +channel_tls_from_base_const(const channel_t *chan) +{ + return channel_tls_from_base((channel_t *)chan); +} + /******************************************** * Method implementations for channel_tls_t * *******************************************/ @@ -510,24 +529,29 @@ channel_tls_get_overhead_estimate_method(channel_t *chan) * Get the remote address of a channel_tls_t. * * This implements the get_remote_addr method for channel_tls_t; copy the - * remote endpoint of the channel to addr_out and return 1 (always - * succeeds for this transport). + * remote endpoint of the channel to addr_out and return 1. (Always + * succeeds if this channel is attached to an OR connection.) + * + * Always returns the real address of the peer, not the canonical address. */ static int -channel_tls_get_remote_addr_method(channel_t *chan, tor_addr_t *addr_out) +channel_tls_get_remote_addr_method(const channel_t *chan, + tor_addr_t *addr_out) { - int rv = 0; - channel_tls_t *tlschan = BASE_CHAN_TO_TLS(chan); + const channel_tls_t *tlschan = CONST_BASE_CHAN_TO_TLS(chan); tor_assert(tlschan); tor_assert(addr_out); - if (tlschan->conn) { - tor_addr_copy(addr_out, &(tlschan->conn->real_addr)); - rv = 1; - } else tor_addr_make_unspec(addr_out); + if (tlschan->conn == NULL) { + tor_addr_make_unspec(addr_out); + return 0; + } - return rv; + /* They want the real address, so give it to them. */ + tor_addr_copy(addr_out, &TO_CONN(tlschan->conn)->addr); + + return 1; } /** @@ -555,62 +579,22 @@ channel_tls_get_transport_name_method(channel_t *chan, char **transport_out) } /** - * Get endpoint description of a channel_tls_t. + * Get a human-readable endpoint description of a channel_tls_t. * - * This implements the get_remote_descr method for channel_tls_t; it returns - * a text description of the remote endpoint of the channel suitable for use - * in log messages. The req parameter is 0 for the canonical address or 1 for - * the actual address seen. + * This format is intended for logging, and may change in the future; + * nothing should parse or rely on its particular details. */ static const char * -channel_tls_get_remote_descr_method(channel_t *chan, int flags) +channel_tls_describe_peer_method(const channel_t *chan) { - static char buf[TOR_ADDRPORT_BUF_LEN]; - channel_tls_t *tlschan = BASE_CHAN_TO_TLS(chan); - connection_t *conn; - const char *answer = NULL; - char *addr_str; - + const channel_tls_t *tlschan = CONST_BASE_CHAN_TO_TLS(chan); tor_assert(tlschan); if (tlschan->conn) { - conn = TO_CONN(tlschan->conn); - switch (flags) { - case 0: - /* Canonical address with port*/ - tor_snprintf(buf, TOR_ADDRPORT_BUF_LEN, - "%s:%u", conn->address, conn->port); - answer = buf; - break; - case GRD_FLAG_ORIGINAL: - /* Actual address with port */ - addr_str = tor_addr_to_str_dup(&(tlschan->conn->real_addr)); - tor_snprintf(buf, TOR_ADDRPORT_BUF_LEN, "%s:%u", addr_str, conn->port); - tor_free(addr_str); - answer = buf; - break; - case GRD_FLAG_ADDR_ONLY: - /* Canonical address, no port */ - strlcpy(buf, conn->address, sizeof(buf)); - answer = buf; - break; - case GRD_FLAG_ORIGINAL|GRD_FLAG_ADDR_ONLY: - /* Actual address, no port */ - addr_str = tor_addr_to_str_dup(&(tlschan->conn->real_addr)); - strlcpy(buf, addr_str, sizeof(buf)); - tor_free(addr_str); - answer = buf; - break; - default: - /* Something's broken in channel.c */ - tor_assert_nonfatal_unreached_once(); - } + return connection_describe_peer(TO_CONN(tlschan->conn)); } else { - strlcpy(buf, "(No connection)", sizeof(buf)); - answer = buf; + return "(No connection)"; } - - return answer; } /** @@ -683,6 +667,9 @@ channel_tls_is_canonical_method(channel_t *chan, int req) * * This implements the matches_extend_info method for channel_tls_t; the upper * layer wants to know if this channel matches an extend_info_t. + * + * NOTE that this function only checks for an address/port match, and should + * be used only when no identify is available. */ static int channel_tls_matches_extend_info_method(channel_t *chan, @@ -702,9 +689,19 @@ channel_tls_matches_extend_info_method(channel_t *chan, return 0; } - return (tor_addr_eq(&(extend_info->addr), - &(TO_CONN(tlschan->conn)->addr)) && - (extend_info->port == TO_CONN(tlschan->conn)->port)); + const tor_addr_port_t *orport = &tlschan->conn->canonical_orport; + // If the canonical address is set, then we'll allow matches based on that. + if (! tor_addr_is_unspec(&orport->addr)) { + if (extend_info_has_orport(extend_info, &orport->addr, orport->port)) { + return 1; + } + } + + // We also want to match if the true address and port are listed in the + // extend info. + return extend_info_has_orport(extend_info, + &TO_CONN(tlschan->conn)->addr, + TO_CONN(tlschan->conn)->port); } /** @@ -732,8 +729,8 @@ channel_tls_matches_target_method(channel_t *chan, return 0; } - /* real_addr is the address this connection came from. - * base_.addr is updated by connection_or_init_conn_from_address() + /* addr is the address this connection came from. + * canonical_orport is updated by connection_or_init_conn_from_address() * to be the address in the descriptor. It may be tempting to * allow either address to be allowed, but if we did so, it would * enable someone who steals a relay's keys to covertly impersonate/MITM it @@ -744,7 +741,7 @@ channel_tls_matches_target_method(channel_t *chan, * An adversary who has stolen a relay's keys could also post a fake relay * descriptor, but that attack is easier to detect. */ - return tor_addr_eq(&(tlschan->conn->real_addr), target); + return tor_addr_eq(&TO_CONN(tlschan->conn)->addr, target); } /** @@ -1353,7 +1350,7 @@ channel_tls_update_marks(or_connection_t *conn) chan = TLS_CHAN_TO_BASE(conn->chan); - if (is_local_addr(&(TO_CONN(conn)->addr))) { + if (is_local_to_resolve_addr(&(TO_CONN(conn)->addr))) { if (!channel_is_local(chan)) { log_debug(LD_CHANNEL, "Marking channel %"PRIu64 " at %p as local", @@ -1517,7 +1514,7 @@ channel_tls_process_versions_cell(var_cell_t *cell, channel_tls_t *chan) log_fn(LOG_WARN, LD_OR, "Negotiated link with non-2 protocol after doing a v2 TLS " "handshake with %s. Closing connection.", - fmt_addr(&chan->conn->base_.addr)); + connection_describe_peer(TO_CONN(chan->conn))); connection_or_close_for_error(chan->conn, 0); return; } @@ -1529,10 +1526,9 @@ channel_tls_process_versions_cell(var_cell_t *cell, channel_tls_t *chan) if (chan->conn->link_proto == 2) { log_info(LD_OR, - "Negotiated version %d with %s:%d; sending NETINFO.", + "Negotiated version %d on %s; sending NETINFO.", highest_supported_version, - safe_str_client(chan->conn->base_.address), - chan->conn->base_.port); + connection_describe(TO_CONN(chan->conn))); if (connection_or_send_netinfo(chan->conn) < 0) { connection_or_close_for_error(chan->conn, 0); @@ -1552,10 +1548,9 @@ channel_tls_process_versions_cell(var_cell_t *cell, channel_tls_t *chan) tor_assert(chan->conn->link_proto >= 3); log_info(LD_OR, - "Negotiated version %d with %s:%d; %s%s%s%s%s", + "Negotiated version %d with on %s; %s%s%s%s%s", highest_supported_version, - safe_str_client(chan->conn->base_.address), - chan->conn->base_.port, + connection_describe(TO_CONN(chan->conn)), send_any ? "Sending cells:" : "Waiting for CERTS cell", send_versions ? " VERSIONS" : "", send_certs ? " CERTS" : "", @@ -1688,6 +1683,85 @@ time_abs(time_t val) return (val < 0) ? -val : val; } +/** Return true iff the channel can process a NETINFO cell. For this to return + * true, these channel conditions apply: + * + * 1. Link protocol is version 2 or higher (tor-spec.txt, NETINFO cells + * section). + * + * 2. Underlying OR connection of the channel is either in v2 or v3 + * handshaking state. + */ +static bool +can_process_netinfo_cell(const channel_tls_t *chan) +{ + /* NETINFO cells can only be negotiated on link protocol 2 or higher. */ + if (chan->conn->link_proto < 2) { + log_fn(LOG_PROTOCOL_WARN, LD_OR, + "Received a NETINFO cell on %s connection; dropping.", + chan->conn->link_proto == 0 ? "non-versioned" : "a v1"); + return false; + } + + /* Can't process a NETINFO cell if the connection is not handshaking. */ + if (chan->conn->base_.state != OR_CONN_STATE_OR_HANDSHAKING_V2 && + chan->conn->base_.state != OR_CONN_STATE_OR_HANDSHAKING_V3) { + log_fn(LOG_PROTOCOL_WARN, LD_OR, + "Received a NETINFO cell on non-handshaking connection; dropping."); + return false; + } + + /* Make sure we do have handshake state. */ + tor_assert(chan->conn->handshake_state); + tor_assert(chan->conn->handshake_state->received_versions); + + return true; +} + +/** Mark the given channel endpoint as a client (which means either a tor + * client or a tor bridge). + * + * This MUST be done on an _unauthenticated_ channel. It is a mistake to mark + * an authenticated channel as a client. + * + * The following is done on the channel: + * + * 1. Marked as a client. + * 2. Type of circuit ID type is set. + * 3. The underlying OR connection is initialized with the address of the + * endpoint. + */ +static void +mark_channel_tls_endpoint_as_client(channel_tls_t *chan) +{ + /* Ending up here for an authenticated link is a mistake. */ + if (BUG(chan->conn->handshake_state->authenticated)) { + return; + } + + tor_assert(tor_digest_is_zero( + (const char*)(chan->conn->handshake_state-> + authenticated_rsa_peer_id))); + tor_assert(fast_mem_is_zero( + (const char*)(chan->conn->handshake_state-> + authenticated_ed25519_peer_id.pubkey), 32)); + /* If the client never authenticated, it's a tor client or bridge + * relay, and we must not use it for EXTEND requests (nor could we, as + * there are no authenticated peer IDs) */ + channel_mark_client(TLS_CHAN_TO_BASE(chan)); + channel_set_circid_type(TLS_CHAN_TO_BASE(chan), NULL, + chan->conn->link_proto < MIN_LINK_PROTO_FOR_WIDE_CIRC_IDS); + + connection_or_init_conn_from_address(chan->conn, + &(chan->conn->base_.addr), + chan->conn->base_.port, + /* zero, checked above */ + (const char*)(chan->conn->handshake_state-> + authenticated_rsa_peer_id), + NULL, /* Ed25519 ID: Also checked as zero */ + 0); +} + /** * Process a 'netinfo' cell * @@ -1713,20 +1787,12 @@ channel_tls_process_netinfo_cell(cell_t *cell, channel_tls_t *chan) tor_assert(chan); tor_assert(chan->conn); - if (chan->conn->link_proto < 2) { - log_fn(LOG_PROTOCOL_WARN, LD_OR, - "Received a NETINFO cell on %s connection; dropping.", - chan->conn->link_proto == 0 ? "non-versioned" : "a v1"); + /* Make sure we can process a NETINFO cell. Link protocol and state + * validation is done to make sure of it. */ + if (!can_process_netinfo_cell(chan)) { return; } - if (chan->conn->base_.state != OR_CONN_STATE_OR_HANDSHAKING_V2 && - chan->conn->base_.state != OR_CONN_STATE_OR_HANDSHAKING_V3) { - log_fn(LOG_PROTOCOL_WARN, LD_OR, - "Received a NETINFO cell on non-handshaking connection; dropping."); - return; - } - tor_assert(chan->conn->handshake_state && - chan->conn->handshake_state->received_versions); + started_here = connection_or_nonopen_was_started_here(chan->conn); identity_digest = chan->conn->identity_digest; @@ -1741,30 +1807,13 @@ channel_tls_process_netinfo_cell(cell_t *cell, channel_tls_t *chan) return; } } else { - /* we're the server. If the client never authenticated, we have - some housekeeping to do.*/ + /* We're the server. If the client never authenticated, we have some + * housekeeping to do. + * + * It's a tor client or bridge relay, and we must not use it for EXTEND + * requests (nor could we, as there are no authenticated peer IDs) */ if (!(chan->conn->handshake_state->authenticated)) { - tor_assert(tor_digest_is_zero( - (const char*)(chan->conn->handshake_state-> - authenticated_rsa_peer_id))); - tor_assert(fast_mem_is_zero( - (const char*)(chan->conn->handshake_state-> - authenticated_ed25519_peer_id.pubkey), 32)); - /* If the client never authenticated, it's a tor client or bridge - * relay, and we must not use it for EXTEND requests (nor could we, as - * there are no authenticated peer IDs) */ - channel_mark_client(TLS_CHAN_TO_BASE(chan)); - channel_set_circid_type(TLS_CHAN_TO_BASE(chan), NULL, - chan->conn->link_proto < MIN_LINK_PROTO_FOR_WIDE_CIRC_IDS); - - connection_or_init_conn_from_address(chan->conn, - &(chan->conn->base_.addr), - chan->conn->base_.port, - /* zero, checked above */ - (const char*)(chan->conn->handshake_state-> - authenticated_rsa_peer_id), - NULL, /* Ed25519 ID: Also checked as zero */ - 0); + mark_channel_tls_endpoint_as_client(chan); } } } @@ -1807,7 +1856,7 @@ channel_tls_process_netinfo_cell(cell_t *cell, channel_tls_t *chan) if (my_addr_type == NETINFO_ADDR_TYPE_IPV4 && my_addr_len == 4) { if (!get_options()->BridgeRelay && me && - tor_addr_eq_ipv4h(&my_apparent_addr, me->addr)) { + tor_addr_eq(&my_apparent_addr, &me->ipv4_addr)) { TLS_CHAN_TO_BASE(chan)->is_canonical_to_peer = 1; } } else if (my_addr_type == NETINFO_ADDR_TYPE_IPV6 && @@ -1841,7 +1890,7 @@ channel_tls_process_netinfo_cell(cell_t *cell, channel_tls_t *chan) * might be doing something funny, but nobody else is doing a MITM * on the relay's TCP. */ - if (tor_addr_eq(&addr, &(chan->conn->real_addr))) { + if (tor_addr_eq(&addr, &TO_CONN(chan->conn)->addr)) { connection_or_set_canonical(chan->conn, 1); break; } @@ -1851,8 +1900,8 @@ channel_tls_process_netinfo_cell(cell_t *cell, channel_tls_t *chan) if (me && !TLS_CHAN_TO_BASE(chan)->is_canonical_to_peer && channel_is_canonical(TLS_CHAN_TO_BASE(chan))) { - const char *descr = - TLS_CHAN_TO_BASE(chan)->get_remote_descr(TLS_CHAN_TO_BASE(chan), 0); + const char *descr = channel_describe_peer( + TLS_CHAN_TO_BASE(chan)); log_info(LD_OR, "We made a connection to a relay at %s (fp=%s) but we think " "they will not consider this connection canonical. They " @@ -1861,7 +1910,7 @@ channel_tls_process_netinfo_cell(cell_t *cell, channel_tls_t *chan) safe_str(hex_str(identity_digest, DIGEST_LEN)), safe_str(tor_addr_is_null(&my_apparent_addr) ? "<none>" : fmt_and_decorate_addr(&my_apparent_addr)), - safe_str(fmt_addr32(me->addr))); + safe_str(fmt_addr(&me->ipv4_addr))); } /* Act on apparent skew. */ @@ -1875,8 +1924,12 @@ channel_tls_process_netinfo_cell(cell_t *cell, channel_tls_t *chan) "NETINFO cell", "OR"); } - /* XXX maybe act on my_apparent_addr, if the source is sufficiently - * trustworthy. */ + /* Consider our apparent address as a possible suggestion for our address if + * we were unable to resolve it previously. The endpoint address is passed + * in order to make sure to never consider an address that is the same as + * our endpoint. */ + relay_address_new_suggestion(&my_apparent_addr, &TO_CONN(chan->conn)->addr, + identity_digest); if (! chan->conn->handshake_state->sent_netinfo) { /* If we were prepared to authenticate, but we never got an AUTH_CHALLENGE @@ -1890,18 +1943,16 @@ channel_tls_process_netinfo_cell(cell_t *cell, channel_tls_t *chan) if (connection_or_set_state_open(chan->conn) < 0) { log_fn(LOG_PROTOCOL_WARN, LD_OR, - "Got good NETINFO cell from %s:%d; but " + "Got good NETINFO cell on %s; but " "was unable to make the OR connection become open.", - safe_str_client(chan->conn->base_.address), - chan->conn->base_.port); + connection_describe(TO_CONN(chan->conn))); connection_or_close_for_error(chan->conn, 0); } else { log_info(LD_OR, - "Got good NETINFO cell from %s:%d; OR connection is now " + "Got good NETINFO cell on %s; OR connection is now " "open, using protocol version %d. Its ID digest is %s. " "Our address is apparently %s.", - safe_str_client(chan->conn->base_.address), - chan->conn->base_.port, + connection_describe(TO_CONN(chan->conn)), (int)(chan->conn->link_proto), hex_str(identity_digest, DIGEST_LEN), tor_addr_is_null(&my_apparent_addr) ? @@ -1986,9 +2037,9 @@ channel_tls_process_certs_cell(var_cell_t *cell, channel_tls_t *chan) #define ERR(s) \ do { \ log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, \ - "Received a bad CERTS cell from %s:%d: %s", \ - safe_str(chan->conn->base_.address), \ - chan->conn->base_.port, (s)); \ + "Received a bad CERTS cell on %s: %s", \ + connection_describe(TO_CONN(chan->conn)), \ + (s)); \ connection_or_close_for_error(chan->conn, 0); \ goto err; \ } while (0) @@ -2036,9 +2087,8 @@ channel_tls_process_certs_cell(var_cell_t *cell, channel_tls_t *chan) tor_x509_cert_t *x509_cert = tor_x509_cert_decode(cert_body, cert_len); if (!x509_cert) { log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, - "Received undecodable certificate in CERTS cell from %s:%d", - safe_str(chan->conn->base_.address), - chan->conn->base_.port); + "Received undecodable certificate in CERTS cell on %s", + connection_describe(TO_CONN(chan->conn))); } else { if (x509_certs[cert_type]) { tor_x509_cert_free(x509_cert); @@ -2054,9 +2104,8 @@ channel_tls_process_certs_cell(var_cell_t *cell, channel_tls_t *chan) if (!ed_cert) { log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, "Received undecodable Ed certificate " - "in CERTS cell from %s:%d", - safe_str(chan->conn->base_.address), - chan->conn->base_.port); + "in CERTS cell on %s", + connection_describe(TO_CONN(chan->conn))); } else { if (ed_certs[cert_type]) { tor_cert_free(ed_cert); @@ -2166,9 +2215,9 @@ channel_tls_process_certs_cell(var_cell_t *cell, channel_tls_t *chan) ERR("Problem setting or checking peer id"); log_info(LD_HANDSHAKE, - "Got some good certificates from %s:%d: Authenticated it with " + "Got some good certificates on %s: Authenticated it with " "RSA%s", - safe_str(chan->conn->base_.address), chan->conn->base_.port, + connection_describe(TO_CONN(chan->conn)), checked_ed_id ? " and Ed25519" : ""); if (!public_server_mode(get_options())) { @@ -2180,11 +2229,10 @@ channel_tls_process_certs_cell(var_cell_t *cell, channel_tls_t *chan) } else { /* We can't call it authenticated till we see an AUTHENTICATE cell. */ log_info(LD_OR, - "Got some good RSA%s certificates from %s:%d. " + "Got some good RSA%s certificates on %s. " "Waiting for AUTHENTICATE.", checked_ed_id ? " and Ed25519" : "", - safe_str(chan->conn->base_.address), - chan->conn->base_.port); + connection_describe(TO_CONN(chan->conn))); /* XXXX check more stuff? */ } @@ -2233,9 +2281,9 @@ channel_tls_process_auth_challenge_cell(var_cell_t *cell, channel_tls_t *chan) #define ERR(s) \ do { \ log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, \ - "Received a bad AUTH_CHALLENGE cell from %s:%d: %s", \ - safe_str(chan->conn->base_.address), \ - chan->conn->base_.port, (s)); \ + "Received a bad AUTH_CHALLENGE cell on %s: %s", \ + connection_describe(TO_CONN(chan->conn)), \ + (s)); \ connection_or_close_for_error(chan->conn, 0); \ goto done; \ } while (0) @@ -2280,10 +2328,9 @@ channel_tls_process_auth_challenge_cell(var_cell_t *cell, channel_tls_t *chan) if (use_type >= 0) { log_info(LD_OR, - "Got an AUTH_CHALLENGE cell from %s:%d: Sending " + "Got an AUTH_CHALLENGE cell on %s: Sending " "authentication type %d", - safe_str(chan->conn->base_.address), - chan->conn->base_.port, + connection_describe(TO_CONN(chan->conn)), use_type); if (connection_or_send_authenticate_cell(chan->conn, use_type) < 0) { @@ -2294,10 +2341,9 @@ channel_tls_process_auth_challenge_cell(var_cell_t *cell, channel_tls_t *chan) } } else { log_info(LD_OR, - "Got an AUTH_CHALLENGE cell from %s:%d, but we don't " + "Got an AUTH_CHALLENGE cell on %s, but we don't " "know any of its authentication types. Not authenticating.", - safe_str(chan->conn->base_.address), - chan->conn->base_.port); + connection_describe(TO_CONN(chan->conn))); } if (connection_or_send_netinfo(chan->conn) < 0) { @@ -2337,9 +2383,9 @@ channel_tls_process_authenticate_cell(var_cell_t *cell, channel_tls_t *chan) #define ERR(s) \ do { \ log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, \ - "Received a bad AUTHENTICATE cell from %s:%d: %s", \ - safe_str(chan->conn->base_.address), \ - chan->conn->base_.port, (s)); \ + "Received a bad AUTHENTICATE cell on %s: %s", \ + connection_describe(TO_CONN(chan->conn)), \ + (s)); \ connection_or_close_for_error(chan->conn, 0); \ var_cell_free(expected_cell); \ return; \ @@ -2500,9 +2546,9 @@ channel_tls_process_authenticate_cell(var_cell_t *cell, channel_tls_t *chan) crypto_pk_free(identity_rcvd); log_debug(LD_HANDSHAKE, - "Calling connection_or_init_conn_from_address for %s " + "Calling connection_or_init_conn_from_address on %s " " from %s, with%s ed25519 id.", - safe_str(chan->conn->base_.address), + connection_describe(TO_CONN(chan->conn)), __func__, ed_identity_received ? "" : "out"); @@ -2515,10 +2561,9 @@ channel_tls_process_authenticate_cell(var_cell_t *cell, channel_tls_t *chan) 0); log_debug(LD_HANDSHAKE, - "Got an AUTHENTICATE cell from %s:%d, type %d: Looks good.", - safe_str(chan->conn->base_.address), - chan->conn->base_.port, - authtype); + "Got an AUTHENTICATE cell on %s, type %d: Looks good.", + connection_describe(TO_CONN(chan->conn)), + authtype); } var_cell_free(expected_cell); diff --git a/src/core/or/channeltls.h b/src/core/or/channeltls.h index f04ce0fa9c..e7010a51fc 100644 --- a/src/core/or/channeltls.h +++ b/src/core/or/channeltls.h @@ -19,6 +19,8 @@ struct curve25519_public_key_t; #define BASE_CHAN_TO_TLS(c) (channel_tls_from_base((c))) #define TLS_CHAN_TO_BASE(c) (channel_tls_to_base((c))) +#define CONST_BASE_CHAN_TO_TLS(c) (channel_tls_from_base_const((c))) +#define CONST_TLS_CHAN_TO_BASE(c) (channel_tls_to_base_const((c))) #define TLS_CHAN_MAGIC 0x8a192427U @@ -44,6 +46,8 @@ channel_t * channel_tls_handle_incoming(or_connection_t *orconn); channel_t * channel_tls_to_base(channel_tls_t *tlschan); channel_tls_t * channel_tls_from_base(channel_t *chan); +const channel_t * channel_tls_to_base_const(const channel_tls_t *tlschan); +const channel_tls_t * channel_tls_from_base_const(const channel_t *chan); /* Things for connection_or.c to call back into */ void channel_tls_handle_cell(cell_t *cell, or_connection_t *conn); diff --git a/src/core/or/circuit_st.h b/src/core/or/circuit_st.h index 4baafb1848..35d214ce08 100644 --- a/src/core/or/circuit_st.h +++ b/src/core/or/circuit_st.h @@ -238,6 +238,12 @@ struct circuit_t { * Each element of this array corresponds to a different padding machine, * and we can have up to CIRCPAD_MAX_MACHINES such machines. */ struct circpad_machine_runtime_t *padding_info[CIRCPAD_MAX_MACHINES]; + + /** padding_machine_ctr increments each time a new padding machine + * is negotiated. It is used for shutdown conditions, to ensure + * that STOP commands actually correspond to the current machine, + * and not a previous one. */ + uint32_t padding_machine_ctr; }; #endif /* !defined(CIRCUIT_ST_H) */ diff --git a/src/core/or/circuitbuild.c b/src/core/or/circuitbuild.c index 83ce9f882b..6ad19eebad 100644 --- a/src/core/or/circuitbuild.c +++ b/src/core/or/circuitbuild.c @@ -45,10 +45,12 @@ #include "core/or/command.h" #include "core/or/connection_edge.h" #include "core/or/connection_or.h" +#include "core/or/extendinfo.h" #include "core/or/onion.h" #include "core/or/ocirc_event.h" #include "core/or/policies.h" #include "core/or/relay.h" +#include "core/or/trace_probes_circuit.h" #include "core/or/crypt_path.h" #include "feature/client/bridges.h" #include "feature/client/circpathbias.h" @@ -70,6 +72,7 @@ #include "feature/rend/rendcommon.h" #include "feature/stats/predict_ports.h" #include "lib/crypt_ops/crypto_rand.h" +#include "lib/trace/events.h" #include "core/or/cell_st.h" #include "core/or/cpath_build_state_st.h" @@ -78,9 +81,6 @@ #include "feature/nodelist/node_st.h" #include "core/or/or_circuit_st.h" #include "core/or/origin_circuit_st.h" -#include "feature/nodelist/microdesc_st.h" -#include "feature/nodelist/routerinfo_st.h" -#include "feature/nodelist/routerstatus_st.h" static int circuit_send_first_onion_skin(origin_circuit_t *circ); static int circuit_build_no_more_hops(origin_circuit_t *circ); @@ -96,13 +96,17 @@ static const node_t *choose_good_middle_server(uint8_t purpose, * callbacks. */ MOCK_IMPL(channel_t *, -channel_connect_for_circuit,(const tor_addr_t *addr, uint16_t port, - const char *id_digest, - const struct ed25519_public_key_t *ed_id)) +channel_connect_for_circuit,(const extend_info_t *ei)) { channel_t *chan; - chan = channel_connect(addr, port, id_digest, ed_id); + const tor_addr_port_t *orport = extend_info_pick_orport(ei); + if (!orport) + return NULL; + const char *id_digest = ei->identity_digest; + const ed25519_public_key_t *ed_id = &ei->ed_identity; + + chan = channel_connect(&orport->addr, orport->port, id_digest, ed_id); if (chan) command_setup_channel(chan); return chan; @@ -439,7 +443,8 @@ onion_populate_cpath(origin_circuit_t *circ) /** Create and return a new origin circuit. Initialize its purpose and * build-state based on our arguments. The <b>flags</b> argument is a - * bitfield of CIRCLAUNCH_* flags. */ + * bitfield of CIRCLAUNCH_* flags, see circuit_launch_by_extend_info() for + * more details. */ origin_circuit_t * origin_circuit_init(uint8_t purpose, int flags) { @@ -455,13 +460,16 @@ origin_circuit_init(uint8_t purpose, int flags) ((flags & CIRCLAUNCH_NEED_CAPACITY) ? 1 : 0); circ->build_state->is_internal = ((flags & CIRCLAUNCH_IS_INTERNAL) ? 1 : 0); + circ->build_state->is_ipv6_selftest = + ((flags & CIRCLAUNCH_IS_IPV6_SELFTEST) ? 1 : 0); circ->base_.purpose = purpose; return circ; } -/** Build a new circuit for <b>purpose</b>. If <b>exit</b> - * is defined, then use that as your exit router, else choose a suitable - * exit node. +/** Build a new circuit for <b>purpose</b>. If <b>exit</b> is defined, then use + * that as your exit router, else choose a suitable exit node. The <b>flags</b> + * argument is a bitfield of CIRCLAUNCH_* flags, see + * circuit_launch_by_extend_info() for more details. * * Also launch a connection to the first OR in the chosen path, if * it's not open already. @@ -491,6 +499,8 @@ circuit_establish_circuit(uint8_t purpose, extend_info_t *exit_ei, int flags) circuit_mark_for_close(TO_CIRCUIT(circ), -err_reason); return NULL; } + + tor_trace(TR_SUBSYS(circuit), TR_EV(establish), circ); return circ; } @@ -546,7 +556,7 @@ circuit_handle_first_hop(origin_circuit_t *circ) * - the address is internal, and * - we're not connecting to a configured bridge, and * - we're not configured to allow extends to private addresses. */ - if (tor_addr_is_internal(&firsthop->extend_info->addr, 0) && + if (extend_info_any_orport_addr_is_internal(firsthop->extend_info) && !extend_info_is_a_configured_bridge(firsthop->extend_info) && !options->ExtendAllowPrivateAddresses) { log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, @@ -555,19 +565,15 @@ circuit_handle_first_hop(origin_circuit_t *circ) } /* now see if we're already connected to the first OR in 'route' */ - log_debug(LD_CIRC,"Looking for firsthop '%s'", - fmt_addrport(&firsthop->extend_info->addr, - firsthop->extend_info->port)); - - /* We'll cleanup this code in #33220, when we add an IPv6 address to - * extend_info_t. */ - const bool addr_is_ipv4 = - (tor_addr_family(&firsthop->extend_info->addr) == AF_INET); + const tor_addr_port_t *orport4 = + extend_info_get_orport(firsthop->extend_info, AF_INET); + const tor_addr_port_t *orport6 = + extend_info_get_orport(firsthop->extend_info, AF_INET6); n_chan = channel_get_for_extend( firsthop->extend_info->identity_digest, &firsthop->extend_info->ed_identity, - addr_is_ipv4 ? &firsthop->extend_info->addr : NULL, - addr_is_ipv4 ? NULL : &firsthop->extend_info->addr, + orport4 ? &orport4->addr : NULL, + orport6 ? &orport6->addr : NULL, &msg, &should_launch); @@ -579,11 +585,7 @@ circuit_handle_first_hop(origin_circuit_t *circ) circ->base_.n_hop = extend_info_dup(firsthop->extend_info); if (should_launch) { - n_chan = channel_connect_for_circuit( - &firsthop->extend_info->addr, - firsthop->extend_info->port, - firsthop->extend_info->identity_digest, - &firsthop->extend_info->ed_identity); + n_chan = channel_connect_for_circuit(firsthop->extend_info); if (!n_chan) { /* connect failed, forget the whole thing */ log_info(LD_CIRC,"connect to firsthop failed. Closing."); return -END_CIRC_REASON_CONNECTFAILED; @@ -601,7 +603,8 @@ circuit_handle_first_hop(origin_circuit_t *circ) tor_assert(!circ->base_.n_hop); circ->base_.n_chan = n_chan; circuit_chan_publish(circ, n_chan); - log_debug(LD_CIRC,"Conn open. Delivering first onion skin."); + log_debug(LD_CIRC,"Conn open for %s. Delivering first onion skin.", + safe_str_client(extend_info_describe(firsthop->extend_info))); if ((err_reason = circuit_send_next_onion_skin(circ)) < 0) { log_info(LD_CIRC,"circuit_send_next_onion_skin failed."); circ->base_.n_chan = NULL; @@ -629,7 +632,7 @@ circuit_n_chan_done(channel_t *chan, int status, int close_origin_circuits) tor_assert(chan); log_debug(LD_CIRC,"chan to %s, status=%d", - channel_get_canonical_remote_descr(chan), status); + channel_describe_peer(chan), status); pending_circs = smartlist_new(); circuit_get_all_pending_on_channel(pending_circs, chan); @@ -980,6 +983,7 @@ circuit_send_first_onion_skin(origin_circuit_t *circ) if (circuit_deliver_create_cell(TO_CIRCUIT(circ), &cc, 0) < 0) return - END_CIRC_REASON_RESOURCELIMIT; + tor_trace(TR_SUBSYS(circuit), TR_EV(first_onion_skin), circ, circ->cpath); circ->cpath->state = CPATH_STATE_AWAITING_KEYS; circuit_set_state(TO_CIRCUIT(circ), CIRCUIT_STATE_BUILDING); @@ -1050,8 +1054,8 @@ circuit_build_no_more_hops(origin_circuit_t *circ) control_event_bootstrap(BOOTSTRAP_STATUS_DONE, 0); control_event_client_status(LOG_NOTICE, "CIRCUIT_ESTABLISHED"); clear_broken_connection_map(1); - if (server_mode(options) && !check_whether_orport_reachable(options)) { - inform_testing_reachability(); + if (server_mode(options) && + !router_all_orports_seem_reachable(options)) { router_do_reachability_checks(1, 1); } } @@ -1075,23 +1079,40 @@ circuit_send_intermediate_onion_skin(origin_circuit_t *circ, { int len; extend_cell_t ec; + /* Relays and bridges can send IPv6 extends. But for clients, it's an + * obvious version distinguisher. */ + const bool include_ipv6 = server_mode(get_options()); memset(&ec, 0, sizeof(ec)); + tor_addr_make_unspec(&ec.orport_ipv4.addr); + tor_addr_make_unspec(&ec.orport_ipv6.addr); log_debug(LD_CIRC,"starting to send subsequent skin."); - if (tor_addr_family(&hop->extend_info->addr) != AF_INET) { - log_warn(LD_BUG, "Trying to extend to a non-IPv4 address."); - return - END_CIRC_REASON_INTERNAL; - } - circuit_pick_extend_handshake(&ec.cell_type, &ec.create_cell.cell_type, &ec.create_cell.handshake_type, hop->extend_info); - tor_addr_copy(&ec.orport_ipv4.addr, &hop->extend_info->addr); - ec.orport_ipv4.port = hop->extend_info->port; - tor_addr_make_unspec(&ec.orport_ipv6.addr); + const tor_addr_port_t *orport4 = + extend_info_get_orport(hop->extend_info, AF_INET); + const tor_addr_port_t *orport6 = + extend_info_get_orport(hop->extend_info, AF_INET6); + int n_addrs_set = 0; + if (orport4) { + tor_addr_copy(&ec.orport_ipv4.addr, &orport4->addr); + ec.orport_ipv4.port = orport4->port; + ++n_addrs_set; + } + if (orport6 && include_ipv6) { + tor_addr_copy(&ec.orport_ipv6.addr, &orport6->addr); + ec.orport_ipv6.port = orport6->port; + ++n_addrs_set; + } + + if (n_addrs_set == 0) { + log_warn(LD_BUG, "No supported address family found in extend_info."); + return - END_CIRC_REASON_INTERNAL; + } memcpy(ec.node_id, hop->extend_info->identity_digest, DIGEST_LEN); /* Set the ED25519 identity too -- it will only get included * in the extend2 cell if we're configured to use it, though. */ @@ -1126,6 +1147,7 @@ circuit_send_intermediate_onion_skin(origin_circuit_t *circ, return 0; /* circuit is closed */ } hop->state = CPATH_STATE_AWAITING_KEYS; + tor_trace(TR_SUBSYS(circuit), TR_EV(intermediate_onion_skin), circ, hop); return 0; } @@ -1539,7 +1561,23 @@ choose_good_exit_server_general(router_crn_flags_t flags) const node_t *selected_node=NULL; const int need_uptime = (flags & CRN_NEED_UPTIME) != 0; const int need_capacity = (flags & CRN_NEED_CAPACITY) != 0; - const int direct_conn = (flags & CRN_DIRECT_CONN) != 0; + + /* We should not require guard flags on exits. */ + IF_BUG_ONCE(flags & CRN_NEED_GUARD) + return NULL; + + /* We reject single-hop exits for all node positions. */ + IF_BUG_ONCE(flags & CRN_DIRECT_CONN) + return NULL; + + /* This isn't the function for picking rendezvous nodes. */ + IF_BUG_ONCE(flags & CRN_RENDEZVOUS_V3) + return NULL; + + /* We only want exits to extend if we cannibalize the circuit. + * But we don't require IPv6 extends yet. */ + IF_BUG_ONCE(flags & CRN_INITIATE_IPV6_EXTEND) + return NULL; connections = get_connection_array(); @@ -1572,19 +1610,14 @@ choose_good_exit_server_general(router_crn_flags_t flags) */ continue; } - if (!node_has_preferred_descriptor(node, direct_conn)) { + if (!router_can_choose_node(node, flags)) { n_supported[i] = -1; continue; } - if (!node->is_running || node->is_bad_exit) { + if (node->is_bad_exit) { n_supported[i] = -1; continue; /* skip routers that are known to be down or bad exits */ } - if (node_get_purpose(node) != ROUTER_PURPOSE_GENERAL) { - /* never pick a non-general node as a random exit. */ - n_supported[i] = -1; - continue; - } if (routerset_contains_node(options->ExcludeExitNodesUnion_, node)) { n_supported[i] = -1; continue; /* user asked us not to use it, no matter what */ @@ -1594,27 +1627,6 @@ choose_good_exit_server_general(router_crn_flags_t flags) n_supported[i] = -1; continue; /* not one of our chosen exit nodes */ } - - if (node_is_unreliable(node, need_uptime, need_capacity, 0)) { - n_supported[i] = -1; - continue; /* skip routers that are not suitable. Don't worry if - * this makes us reject all the possible routers: if so, - * we'll retry later in this function with need_update and - * need_capacity set to 0. */ - } - if (!(node->is_valid)) { - /* if it's invalid and we don't want it */ - n_supported[i] = -1; -// log_fn(LOG_DEBUG,"Skipping node %s (index %d) -- invalid router.", -// router->nickname, i); - continue; /* skip invalid routers */ - } - /* We do not allow relays that allow single hop exits by default. Option - * was deprecated in 0.2.9.2-alpha and removed in 0.3.1.0-alpha. */ - if (node_allows_single_hop_exits(node)) { - n_supported[i] = -1; - continue; - } if (node_exit_policy_rejects_all(node)) { n_supported[i] = -1; // log_fn(LOG_DEBUG,"Skipping node %s (index %d) -- it rejects all.", @@ -1765,35 +1777,29 @@ pick_restricted_middle_node(router_crn_flags_t flags, { const node_t *middle_node = NULL; - smartlist_t *whitelisted_live_middles = smartlist_new(); + smartlist_t *allowlisted_live_middles = smartlist_new(); smartlist_t *all_live_nodes = smartlist_new(); tor_assert(pick_from); /* Add all running nodes to all_live_nodes */ - router_add_running_nodes_to_smartlist(all_live_nodes, - (flags & CRN_NEED_UPTIME) != 0, - (flags & CRN_NEED_CAPACITY) != 0, - (flags & CRN_NEED_GUARD) != 0, - (flags & CRN_NEED_DESC) != 0, - (flags & CRN_PREF_ADDR) != 0, - (flags & CRN_DIRECT_CONN) != 0); - - /* Filter all_live_nodes to only add live *and* whitelisted middles - * to the list whitelisted_live_middles. */ + router_add_running_nodes_to_smartlist(all_live_nodes, flags); + + /* Filter all_live_nodes to only add live *and* allowlisted middles + * to the list allowlisted_live_middles. */ SMARTLIST_FOREACH_BEGIN(all_live_nodes, node_t *, live_node) { if (routerset_contains_node(pick_from, live_node)) { - smartlist_add(whitelisted_live_middles, live_node); + smartlist_add(allowlisted_live_middles, live_node); } } SMARTLIST_FOREACH_END(live_node); /* Honor ExcludeNodes */ if (exclude_set) { - routerset_subtract_nodes(whitelisted_live_middles, exclude_set); + routerset_subtract_nodes(allowlisted_live_middles, exclude_set); } if (exclude_list) { - smartlist_subtract(whitelisted_live_middles, exclude_list); + smartlist_subtract(allowlisted_live_middles, exclude_list); } /** @@ -1809,9 +1815,9 @@ pick_restricted_middle_node(router_crn_flags_t flags, * If there are a lot of nodes in here, assume they did not load balance * and do it for them, but also warn them that they may be Doing It Wrong. */ - if (smartlist_len(whitelisted_live_middles) <= + if (smartlist_len(allowlisted_live_middles) <= MAX_SANE_RESTRICTED_NODES) { - middle_node = smartlist_choose(whitelisted_live_middles); + middle_node = smartlist_choose(allowlisted_live_middles); } else { static ratelim_t pinned_notice_limit = RATELIM_INIT(24*3600); log_fn_ratelim(&pinned_notice_limit, LOG_NOTICE, LD_CIRC, @@ -1819,17 +1825,17 @@ pick_restricted_middle_node(router_crn_flags_t flags, "in %d total nodes. This is a lot of nodes. " "You may want to consider using a Tor controller " "to select and update a smaller set of nodes instead.", - position_hint, smartlist_len(whitelisted_live_middles)); + position_hint, smartlist_len(allowlisted_live_middles)); /* NO_WEIGHTING here just means don't take node flags into account * (ie: use consensus measurement only). This is done so that * we don't further surprise the user by not using Exits that they * specified at all */ - middle_node = node_sl_choose_by_bandwidth(whitelisted_live_middles, + middle_node = node_sl_choose_by_bandwidth(allowlisted_live_middles, NO_WEIGHTING); } - smartlist_free(whitelisted_live_middles); + smartlist_free(allowlisted_live_middles); smartlist_free(all_live_nodes); return middle_node; @@ -1957,6 +1963,43 @@ warn_if_last_router_excluded(origin_circuit_t *circ, return; } +/* Return a set of generic CRN_* flags based on <b>state</b>. + * + * Called for every position in the circuit. */ +STATIC int +cpath_build_state_to_crn_flags(const cpath_build_state_t *state) +{ + router_crn_flags_t flags = 0; + /* These flags apply to entry, middle, and exit nodes. + * If a flag only applies to a specific position, it should be checked in + * that function. */ + if (state->need_uptime) + flags |= CRN_NEED_UPTIME; + if (state->need_capacity) + flags |= CRN_NEED_CAPACITY; + return flags; +} + +/* Return the CRN_INITIATE_IPV6_EXTEND flag, based on <b>state</b> and + * <b>cur_len</b>. + * + * Only called for middle nodes (for now). Must not be called on single-hop + * circuits. */ +STATIC int +cpath_build_state_to_crn_ipv6_extend_flag(const cpath_build_state_t *state, + int cur_len) +{ + IF_BUG_ONCE(state->desired_path_len < 2) + return 0; + + /* The last node is the relay doing the self-test. So we want to extend over + * IPv6 from the second-last node. */ + if (state->is_ipv6_selftest && cur_len == state->desired_path_len - 2) + return CRN_INITIATE_IPV6_EXTEND; + else + return 0; +} + /** Decide a suitable length for circ's cpath, and pick an exit * router (or use <b>exit</b> if provided). Store these in the * cpath. @@ -1990,14 +2033,13 @@ onion_pick_cpath_exit(origin_circuit_t *circ, extend_info_t *exit_ei, exit_ei = extend_info_dup(exit_ei); } else { /* we have to decide one */ router_crn_flags_t flags = CRN_NEED_DESC; - if (state->need_uptime) - flags |= CRN_NEED_UPTIME; - if (state->need_capacity) - flags |= CRN_NEED_CAPACITY; - if (is_hs_v3_rp_circuit) - flags |= CRN_RENDEZVOUS_V3; + flags |= cpath_build_state_to_crn_flags(state); + /* Some internal exits are one hop, for example directory connections. + * (Guards are always direct, middles are never direct.) */ if (state->onehop_tunnel) flags |= CRN_DIRECT_CONN; + if (is_hs_v3_rp_circuit) + flags |= CRN_RENDEZVOUS_V3; const node_t *node = choose_good_exit_server(circ, flags, state->is_internal); if (!node) { @@ -2059,32 +2101,27 @@ circuit_extend_to_new_exit(origin_circuit_t *circ, extend_info_t *exit_ei) return 0; } -/** Return the number of routers in <b>routers</b> that are currently up - * and available for building circuits through. +/** Return the number of routers in <b>nodes</b> that are currently up and + * available for building circuits through. * - * (Note that this function may overcount or undercount, if we have - * descriptors that are not the type we would prefer to use for some - * particular router. See bug #25885.) + * If <b>direct</b> is true, only count nodes that are suitable for direct + * connections. Counts nodes regardless of whether their addresses are + * preferred. */ MOCK_IMPL(STATIC int, count_acceptable_nodes, (const smartlist_t *nodes, int direct)) { int num=0; + int flags = CRN_NEED_DESC; + + if (direct) + flags |= CRN_DIRECT_CONN; SMARTLIST_FOREACH_BEGIN(nodes, const node_t *, node) { // log_debug(LD_CIRC, -// "Contemplating whether router %d (%s) is a new option.", -// i, r->nickname); - if (! node->is_running) -// log_debug(LD_CIRC,"Nope, the directory says %d is not running.",i); - continue; - if (! node->is_valid) -// log_debug(LD_CIRC,"Nope, the directory says %d is not valid.",i); - continue; - if (! node_has_preferred_descriptor(node, direct)) - continue; - /* The node has a descriptor, so we can just check the ntor key directly */ - if (!node_has_curve25519_onion_key(node)) + // "Contemplating whether router %d (%s) is a new option.", + // i, r->nickname); + if (!router_can_choose_node(node, flags)) continue; ++num; } SMARTLIST_FOREACH_END(node); @@ -2278,10 +2315,8 @@ choose_good_middle_server(uint8_t purpose, excluded = build_middle_exclude_list(purpose, state, head, cur_len); - if (state->need_uptime) - flags |= CRN_NEED_UPTIME; - if (state->need_capacity) - flags |= CRN_NEED_CAPACITY; + flags |= cpath_build_state_to_crn_flags(state); + flags |= cpath_build_state_to_crn_ipv6_extend_flag(state, cur_len); /** If a hidden service circuit wants a specific middle node, pin it. */ if (middle_node_must_be_vanguard(options, purpose, cur_len)) { @@ -2357,10 +2392,7 @@ choose_good_entry_server(uint8_t purpose, cpath_build_state_t *state, } if (state) { - if (state->need_uptime) - flags |= CRN_NEED_UPTIME; - if (state->need_capacity) - flags |= CRN_NEED_CAPACITY; + flags |= cpath_build_state_to_crn_flags(state); } choice = router_choose_random_node(excluded, options->ExcludeNodes, flags); @@ -2429,143 +2461,6 @@ onion_extend_cpath(origin_circuit_t *circ) return 0; } -/** Allocate a new extend_info object based on the various arguments. */ -extend_info_t * -extend_info_new(const char *nickname, - const char *rsa_id_digest, - const ed25519_public_key_t *ed_id, - crypto_pk_t *onion_key, - const curve25519_public_key_t *ntor_key, - const tor_addr_t *addr, uint16_t port) -{ - extend_info_t *info = tor_malloc_zero(sizeof(extend_info_t)); - memcpy(info->identity_digest, rsa_id_digest, DIGEST_LEN); - if (ed_id && !ed25519_public_key_is_zero(ed_id)) - memcpy(&info->ed_identity, ed_id, sizeof(ed25519_public_key_t)); - if (nickname) - strlcpy(info->nickname, nickname, sizeof(info->nickname)); - if (onion_key) - info->onion_key = crypto_pk_dup_key(onion_key); - if (ntor_key) - memcpy(&info->curve25519_onion_key, ntor_key, - sizeof(curve25519_public_key_t)); - tor_addr_copy(&info->addr, addr); - info->port = port; - return info; -} - -/** Allocate and return a new extend_info that can be used to build a - * circuit to or through the node <b>node</b>. Use the primary address - * of the node (i.e. its IPv4 address) unless - * <b>for_direct_connect</b> is true, in which case the preferred - * address is used instead. May return NULL if there is not enough - * info about <b>node</b> to extend to it--for example, if the preferred - * routerinfo_t or microdesc_t is missing, or if for_direct_connect is - * true and none of the node's addresses is allowed by tor's firewall - * and IP version config. - **/ -extend_info_t * -extend_info_from_node(const node_t *node, int for_direct_connect) -{ - crypto_pk_t *rsa_pubkey = NULL; - extend_info_t *info = NULL; - tor_addr_port_t ap; - int valid_addr = 0; - - if (!node_has_preferred_descriptor(node, for_direct_connect)) { - return NULL; - } - - /* Choose a preferred address first, but fall back to an allowed address. */ - if (for_direct_connect) - fascist_firewall_choose_address_node(node, FIREWALL_OR_CONNECTION, 0, &ap); - else { - node_get_prim_orport(node, &ap); - } - valid_addr = tor_addr_port_is_valid_ap(&ap, 0); - - if (valid_addr) - log_debug(LD_CIRC, "using %s for %s", - fmt_addrport(&ap.addr, ap.port), - node->ri ? node->ri->nickname : node->rs->nickname); - else - log_warn(LD_CIRC, "Could not choose valid address for %s", - node->ri ? node->ri->nickname : node->rs->nickname); - - /* Every node we connect or extend to must support ntor */ - if (!node_has_curve25519_onion_key(node)) { - log_fn(LOG_PROTOCOL_WARN, LD_CIRC, - "Attempted to create extend_info for a node that does not support " - "ntor: %s", node_describe(node)); - return NULL; - } - - const ed25519_public_key_t *ed_pubkey = NULL; - - /* Don't send the ed25519 pubkey unless the target node actually supports - * authenticating with it. */ - if (node_supports_ed25519_link_authentication(node, 0)) { - log_info(LD_CIRC, "Including Ed25519 ID for %s", node_describe(node)); - ed_pubkey = node_get_ed25519_id(node); - } else if (node_get_ed25519_id(node)) { - log_info(LD_CIRC, "Not including the ed25519 ID for %s, since it won't " - "be able to authenticate it.", - node_describe(node)); - } - - /* Retrieve the curve25519 pubkey. */ - const curve25519_public_key_t *curve_pubkey = - node_get_curve25519_onion_key(node); - rsa_pubkey = node_get_rsa_onion_key(node); - - if (valid_addr && node->ri) { - info = extend_info_new(node->ri->nickname, - node->identity, - ed_pubkey, - rsa_pubkey, - curve_pubkey, - &ap.addr, - ap.port); - } else if (valid_addr && node->rs && node->md) { - info = extend_info_new(node->rs->nickname, - node->identity, - ed_pubkey, - rsa_pubkey, - curve_pubkey, - &ap.addr, - ap.port); - } - - crypto_pk_free(rsa_pubkey); - return info; -} - -/** Release storage held by an extend_info_t struct. */ -void -extend_info_free_(extend_info_t *info) -{ - if (!info) - return; - crypto_pk_free(info->onion_key); - tor_free(info); -} - -/** Allocate and return a new extend_info_t with the same contents as - * <b>info</b>. */ -extend_info_t * -extend_info_dup(extend_info_t *info) -{ - extend_info_t *newinfo; - tor_assert(info); - newinfo = tor_malloc(sizeof(extend_info_t)); - memcpy(newinfo, info, sizeof(extend_info_t)); - if (info->onion_key) - newinfo->onion_key = crypto_pk_dup_key(info->onion_key); - else - newinfo->onion_key = NULL; - return newinfo; -} - /** Return the node_t for the chosen exit router in <b>state</b>. * If there is no chosen exit, or if we don't know the node_t for * the chosen exit, return NULL. @@ -2601,43 +2496,6 @@ build_state_get_exit_nickname(cpath_build_state_t *state) return state->chosen_exit->nickname; } -/** Return true iff the given address can be used to extend to. */ -int -extend_info_addr_is_allowed(const tor_addr_t *addr) -{ - tor_assert(addr); - - /* Check if we have a private address and if we can extend to it. */ - if ((tor_addr_is_internal(addr, 0) || tor_addr_is_multicast(addr)) && - !get_options()->ExtendAllowPrivateAddresses) { - goto disallow; - } - /* Allowed! */ - return 1; - disallow: - return 0; -} - -/* Does ei have a valid TAP key? */ -int -extend_info_supports_tap(const extend_info_t* ei) -{ - tor_assert(ei); - /* Valid TAP keys are not NULL */ - return ei->onion_key != NULL; -} - -/* Does ei have a valid ntor key? */ -int -extend_info_supports_ntor(const extend_info_t* ei) -{ - tor_assert(ei); - /* Valid ntor keys have at least one non-zero byte */ - return !fast_mem_is_zero( - (const char*)ei->curve25519_onion_key.public_key, - CURVE25519_PUBKEY_LEN); -} - /* Is circuit purpose allowed to use the deprecated TAP encryption protocol? * The hidden service protocol still uses TAP for some connections, because * ntor onion keys aren't included in HS descriptors or INTRODUCE cells. */ @@ -2672,15 +2530,6 @@ circuit_has_usable_onion_key(const origin_circuit_t *circ) circuit_can_use_tap(circ)); } -/* Does ei have an onion key which it would prefer to use? - * Currently, we prefer ntor keys*/ -int -extend_info_has_preferred_onion_key(const extend_info_t* ei) -{ - tor_assert(ei); - return extend_info_supports_ntor(ei); -} - /** Find the circuits that are waiting to find out whether their guards are * usable, and if any are ready to become usable, mark them open and try * attaching streams as appropriate. */ diff --git a/src/core/or/circuitbuild.h b/src/core/or/circuitbuild.h index e62bb41de9..0cd1eb4f45 100644 --- a/src/core/or/circuitbuild.h +++ b/src/core/or/circuitbuild.h @@ -42,23 +42,8 @@ MOCK_DECL(int, circuit_all_predicted_ports_handled, (time_t now, int circuit_append_new_exit(origin_circuit_t *circ, extend_info_t *info); int circuit_extend_to_new_exit(origin_circuit_t *circ, extend_info_t *info); -extend_info_t *extend_info_new(const char *nickname, - const char *rsa_id_digest, - const struct ed25519_public_key_t *ed_id, - crypto_pk_t *onion_key, - const struct curve25519_public_key_t *ntor_key, - const tor_addr_t *addr, uint16_t port); -extend_info_t *extend_info_from_node(const node_t *r, int for_direct_connect); -extend_info_t *extend_info_dup(extend_info_t *info); -void extend_info_free_(extend_info_t *info); -#define extend_info_free(info) \ - FREE_AND_NULL(extend_info_t, extend_info_free_, (info)) -int extend_info_addr_is_allowed(const tor_addr_t *addr); -int extend_info_supports_tap(const extend_info_t* ei); -int extend_info_supports_ntor(const extend_info_t* ei); int circuit_can_use_tap(const origin_circuit_t *circ); int circuit_has_usable_onion_key(const origin_circuit_t *circ); -int extend_info_has_preferred_onion_key(const extend_info_t* ei); const uint8_t *build_state_get_exit_rsa_id(cpath_build_state_t *state); MOCK_DECL(const node_t *, build_state_get_exit_node,(cpath_build_state_t *state)); @@ -71,13 +56,7 @@ const node_t *choose_good_entry_server(uint8_t purpose, struct circuit_guard_state_t **guard_state_out); void circuit_upgrade_circuits_from_guard_wait(void); -struct ed25519_public_key_t; - -MOCK_DECL(channel_t *, -channel_connect_for_circuit,(const tor_addr_t *addr, - uint16_t port, - const char *id_digest, - const struct ed25519_public_key_t *ed_id)); +MOCK_DECL(channel_t *, channel_connect_for_circuit,(const extend_info_t *ei)); struct create_cell_t; MOCK_DECL(int, @@ -97,6 +76,10 @@ STATIC int onion_extend_cpath(origin_circuit_t *circ); STATIC int onion_pick_cpath_exit(origin_circuit_t *circ, extend_info_t *exit_ei, int is_hs_v3_rp_circuit); +STATIC int cpath_build_state_to_crn_flags(const cpath_build_state_t *state); +STATIC int cpath_build_state_to_crn_ipv6_extend_flag( + const cpath_build_state_t *state, + int cur_len); #endif /* defined(CIRCUITBUILD_PRIVATE) */ diff --git a/src/core/or/circuitlist.c b/src/core/or/circuitlist.c index 90cce47490..af98af362a 100644 --- a/src/core/or/circuitlist.c +++ b/src/core/or/circuitlist.c @@ -64,6 +64,8 @@ #include "core/or/circuitstats.h" #include "core/or/circuitpadding.h" #include "core/or/crypt_path.h" +#include "core/or/extendinfo.h" +#include "core/or/trace_probes_circuit.h" #include "core/mainloop/connection.h" #include "app/config/config.h" #include "core/or/connection_edge.h" @@ -89,6 +91,7 @@ #include "feature/rend/rendclient.h" #include "feature/rend/rendcommon.h" #include "feature/stats/predict_ports.h" +#include "feature/stats/bwhist.h" #include "feature/stats/rephist.h" #include "feature/nodelist/routerlist.h" #include "feature/nodelist/routerset.h" @@ -564,6 +567,8 @@ circuit_set_state(circuit_t *circ, uint8_t state) } if (state == CIRCUIT_STATE_GUARD_WAIT || state == CIRCUIT_STATE_OPEN) tor_assert(!circ->n_chan_create_cell); + + tor_trace(TR_SUBSYS(circuit), TR_EV(change_state), circ, circ->state, state); circ->state = state; if (CIRCUIT_IS_ORIGIN(circ)) circuit_state_publish(circ); @@ -614,7 +619,7 @@ circuit_count_pending_on_channel(channel_t *chan) cnt = smartlist_len(sl); smartlist_free(sl); log_debug(LD_CIRC,"or_conn to %s, %d pending circs", - channel_get_canonical_remote_descr(chan), + channel_describe_peer(chan), cnt); return cnt; } @@ -1078,6 +1083,7 @@ origin_circuit_new(void) prediction_time_remaining); } + tor_trace(TR_SUBSYS(circuit), TR_EV(new_origin), circ); return circ; } @@ -1100,6 +1106,7 @@ or_circuit_new(circid_t p_circ_id, channel_t *p_chan) init_circuit_base(TO_CIRCUIT(circ)); + tor_trace(TR_SUBSYS(circuit), TR_EV(new_or), circ); return circ; } @@ -1252,6 +1259,10 @@ circuit_free_(circuit_t *circ) /* Clear all dangling handle references. */ circuit_handles_clear(circ); + /* Tracepoint. Data within the circuit object is recorded so do this before + * the actual memory free. */ + tor_trace(TR_SUBSYS(circuit), TR_EV(free), circ); + if (should_free) { memwipe(mem, 0xAA, memlen); /* poison memory */ tor_free(mem); @@ -1944,7 +1955,7 @@ circuit_find_to_cannibalize(uint8_t purpose_to_produce, extend_info_t *info, /* Ignore any circuits for which we can't use the Guard. It is possible * that the Guard was removed from the sampled set after the circuit - * was created so avoid using it. */ + * was created, so avoid using it. */ if (!entry_guard_could_succeed(circ->guard_state)) { goto next; } @@ -2165,6 +2176,12 @@ circuit_synchronize_written_or_bandwidth(const circuit_t *c, else cell_size = CELL_MAX_NETWORK_SIZE; + /* If we know the channel, find out if it's IPv6. */ + tor_addr_t remote_addr; + bool is_ipv6 = chan && + channel_get_addr_if_possible(chan, &remote_addr) && + tor_addr_family(&remote_addr) == AF_INET6; + /* The missing written bytes are the cell counts times their cell * size plus TLS per cell overhead */ written_sync = cells*(cell_size+TLS_PER_CELL_OVERHEAD); @@ -2172,7 +2189,7 @@ circuit_synchronize_written_or_bandwidth(const circuit_t *c, /* Report the missing bytes as written, to avoid asymmetry. * We must use time() for consistency with rephist, even though on * some very old rare platforms, approx_time() may be faster. */ - rep_hist_note_bytes_written(written_sync, time(NULL)); + bwhist_note_bytes_written(written_sync, time(NULL), is_ipv6); } /** Mark <b>circ</b> to be closed next time we call @@ -2274,6 +2291,7 @@ circuit_mark_for_close_, (circuit_t *circ, int reason, int line, CIRCUIT_IS_ORIGIN(circ) ? TO_ORIGIN_CIRCUIT(circ)->global_identifier : 0, file, line, orig_reason, reason); + tor_trace(TR_SUBSYS(circuit), TR_EV(mark_for_close), circ); } /** Called immediately before freeing a marked circuit <b>circ</b> from diff --git a/src/core/or/circuitpadding.c b/src/core/or/circuitpadding.c index 43f4a31624..889ffb03f1 100644 --- a/src/core/or/circuitpadding.c +++ b/src/core/or/circuitpadding.c @@ -266,18 +266,31 @@ circpad_marked_circuit_for_padding(circuit_t *circ, int reason) /** * Free all the machineinfos in <b>circ</b> that match <b>machine_num</b>. * + * If machine_ctr is non-zero, also make sure it matches the padding_info's + * machine counter before freeing. + * * Returns true if any machineinfos with that number were freed. * False otherwise. */ static int -free_circ_machineinfos_with_machine_num(circuit_t *circ, int machine_num) +free_circ_machineinfos_with_machine_num(circuit_t *circ, int machine_num, + uint32_t machine_ctr) { int found = 0; FOR_EACH_CIRCUIT_MACHINE_BEGIN(i) { if (circ->padding_machine[i] && circ->padding_machine[i]->machine_num == machine_num) { - circpad_circuit_machineinfo_free_idx(circ, i); - circ->padding_machine[i] = NULL; - found = 1; + /* If machine_ctr is non-zero, make sure it matches too. This + * is to ensure that old STOP messages don't shutdown newer machines. */ + if (machine_ctr && circ->padding_info[i] && + circ->padding_info[i]->machine_ctr != machine_ctr) { + log_info(LD_CIRC, + "Padding shutdown for wrong (old?) machine ctr: %u vs %u", + machine_ctr, circ->padding_info[i]->machine_ctr); + } else { + circpad_circuit_machineinfo_free_idx(circ, i); + circ->padding_machine[i] = NULL; + found = 1; + } } } FOR_EACH_CIRCUIT_MACHINE_END; @@ -306,6 +319,7 @@ circpad_circuit_machineinfo_new(circuit_t *on_circ, int machine_index) mi->machine_index = machine_index; mi->on_circ = on_circ; mi->last_cell_time_sec = approx_time(); + mi->machine_ctr = on_circ->padding_machine_ctr; return mi; } @@ -1556,19 +1570,23 @@ circpad_machine_spec_transitioned_to_end(circpad_machine_runtime_t *mi) /* We free the machine info here so that we can be replaced * by a different machine. But we must leave the padding_machine * in place to wait for the negotiated response */ + uint32_t machine_ctr = mi->machine_ctr; circpad_circuit_machineinfo_free_idx(on_circ, machine->machine_index); circpad_negotiate_padding(TO_ORIGIN_CIRCUIT(on_circ), machine->machine_num, machine->target_hopnum, - CIRCPAD_COMMAND_STOP); + CIRCPAD_COMMAND_STOP, + machine_ctr); } else { + uint32_t machine_ctr = mi->machine_ctr; circpad_circuit_machineinfo_free_idx(on_circ, machine->machine_index); circpad_padding_negotiated(on_circ, machine->machine_num, CIRCPAD_COMMAND_STOP, - CIRCPAD_RESPONSE_OK); + CIRCPAD_RESPONSE_OK, + machine_ctr); on_circ->padding_machine[machine->machine_index] = NULL; } } @@ -1990,7 +2008,7 @@ circpad_internal_event_state_length_up(circpad_machine_runtime_t *mi) * Returns true if the circuit matches the conditions. */ static inline bool -circpad_machine_conditions_met(origin_circuit_t *circ, +circpad_machine_conditions_apply(origin_circuit_t *circ, const circpad_machine_spec_t *machine) { /* If padding is disabled, no machines should match/apply. This has @@ -2007,7 +2025,7 @@ circpad_machine_conditions_met(origin_circuit_t *circ, } if (!(circpad_circ_purpose_to_mask(TO_CIRCUIT(circ)->purpose) - & machine->conditions.purpose_mask)) + & machine->conditions.apply_purpose_mask)) return 0; if (machine->conditions.requires_vanguards) { @@ -2023,7 +2041,7 @@ circpad_machine_conditions_met(origin_circuit_t *circ, * "I want to apply to circuits with either streams or no streams"; OR * "I only want to apply to circuits with streams"; OR * "I only want to apply to circuits without streams". */ - if (!(circpad_circuit_state(circ) & machine->conditions.state_mask)) + if (!(circpad_circuit_state(circ) & machine->conditions.apply_state_mask)) return 0; if (circuit_get_cpath_opened_len(circ) < machine->conditions.min_hops) @@ -2033,6 +2051,26 @@ circpad_machine_conditions_met(origin_circuit_t *circ, } /** + * Check to see if any of the keep conditions still apply to this circuit. + * + * These conditions keep the machines active if they match, but do not + * cause new machines to start up. + */ +static inline bool +circpad_machine_conditions_keep(origin_circuit_t *circ, + const circpad_machine_spec_t *machine) +{ + if ((circpad_circ_purpose_to_mask(TO_CIRCUIT(circ)->purpose) + & machine->conditions.keep_purpose_mask)) + return 1; + + if ((circpad_circuit_state(circ) & machine->conditions.keep_state_mask)) + return 1; + + return 0; +} + +/** * Returns a minimized representation of the circuit state. * * The padding code only cares if the circuit is building, @@ -2097,15 +2135,22 @@ circpad_shutdown_old_machines(origin_circuit_t *on_circ) circuit_t *circ = TO_CIRCUIT(on_circ); FOR_EACH_ACTIVE_CIRCUIT_MACHINE_BEGIN(i, circ) { - if (!circpad_machine_conditions_met(on_circ, + /* We shut down a machine if neither the apply conditions + * nor the keep conditions match. If either set of conditions match, + * keep it around. */ + if (!circpad_machine_conditions_apply(on_circ, + circ->padding_machine[i]) && + !circpad_machine_conditions_keep(on_circ, circ->padding_machine[i])) { + uint32_t machine_ctr = circ->padding_info[i]->machine_ctr; // Clear machineinfo (frees timers) circpad_circuit_machineinfo_free_idx(circ, i); // Send padding negotiate stop circpad_negotiate_padding(on_circ, circ->padding_machine[i]->machine_num, circ->padding_machine[i]->target_hopnum, - CIRCPAD_COMMAND_STOP); + CIRCPAD_COMMAND_STOP, + machine_ctr); } } FOR_EACH_ACTIVE_CIRCUIT_MACHINE_END; } @@ -2154,7 +2199,7 @@ circpad_add_matching_machines(origin_circuit_t *on_circ, * machines installed on a circuit. Make sure we only * add this machine if its target machine index is free. */ if (machine->machine_index == i && - circpad_machine_conditions_met(on_circ, machine)) { + circpad_machine_conditions_apply(on_circ, machine)) { // We can only replace this machine if the target hopnum // is the same, otherwise we'll get invalid data @@ -2172,7 +2217,8 @@ circpad_add_matching_machines(origin_circuit_t *on_circ, circpad_setup_machine_on_circ(circ, machine); if (circpad_negotiate_padding(on_circ, machine->machine_num, machine->target_hopnum, - CIRCPAD_COMMAND_START) < 0) { + CIRCPAD_COMMAND_START, + circ->padding_machine_ctr) < 0) { log_info(LD_CIRC, "Padding not negotiated. Cleaning machine from circuit %u", CIRCUIT_IS_ORIGIN(circ) ? @@ -2463,6 +2509,17 @@ circpad_setup_machine_on_circ(circuit_t *on_circ, machine->name, on_circ->purpose); } + /* Padding machine ctr starts at 1, so we increment this ctr first. + * (machine ctr of 0 means "any machine"). + * + * See https://bugs.tororject.org/30992. */ + on_circ->padding_machine_ctr++; + + /* uint32 wraparound check: 0 is special, just wrap to 1 */ + if (on_circ->padding_machine_ctr == 0) { + on_circ->padding_machine_ctr = 1; + } + on_circ->padding_info[machine->machine_index] = circpad_circuit_machineinfo_new(on_circ, machine->machine_index); on_circ->padding_machine[machine->machine_index] = machine; @@ -2555,9 +2612,9 @@ circpad_circ_client_machine_init(void) = tor_malloc_zero(sizeof(circpad_machine_spec_t)); circ_client_machine->conditions.min_hops = 2; - circ_client_machine->conditions.state_mask = + circ_client_machine->conditions.apply_state_mask = CIRCPAD_CIRC_BUILDING|CIRCPAD_CIRC_OPENED|CIRCPAD_CIRC_HAS_RELAY_EARLY; - circ_client_machine->conditions.purpose_mask = CIRCPAD_PURPOSE_ALL; + circ_client_machine->conditions.apply_purpose_mask = CIRCPAD_PURPOSE_ALL; circ_client_machine->conditions.reduced_padding_ok = 1; circ_client_machine->target_hopnum = 2; @@ -2816,7 +2873,8 @@ signed_error_t circpad_negotiate_padding(origin_circuit_t *circ, circpad_machine_num_t machine, uint8_t target_hopnum, - uint8_t command) + uint8_t command, + uint32_t machine_ctr) { circpad_negotiate_t type; cell_t cell; @@ -2838,14 +2896,16 @@ circpad_negotiate_padding(origin_circuit_t *circ, circpad_negotiate_set_command(&type, command); circpad_negotiate_set_version(&type, 0); circpad_negotiate_set_machine_type(&type, machine); + circpad_negotiate_set_machine_ctr(&type, machine_ctr); if ((len = circpad_negotiate_encode(cell.payload, CELL_PAYLOAD_SIZE, &type)) < 0) return -1; log_fn(LOG_INFO,LD_CIRC, - "Negotiating padding on circuit %u (%d), command %d", - circ->global_identifier, TO_CIRCUIT(circ)->purpose, command); + "Negotiating padding on circuit %u (%d), command %d, for ctr %u", + circ->global_identifier, TO_CIRCUIT(circ)->purpose, command, + machine_ctr); return circpad_send_command_to_hop(circ, target_hopnum, RELAY_COMMAND_PADDING_NEGOTIATE, @@ -2861,7 +2921,8 @@ bool circpad_padding_negotiated(circuit_t *circ, circpad_machine_num_t machine, uint8_t command, - uint8_t response) + uint8_t response, + uint32_t machine_ctr) { circpad_negotiated_t type; cell_t cell; @@ -2878,6 +2939,7 @@ circpad_padding_negotiated(circuit_t *circ, circpad_negotiated_set_response(&type, response); circpad_negotiated_set_version(&type, 0); circpad_negotiated_set_machine_type(&type, machine); + circpad_negotiated_set_machine_ctr(&type, machine_ctr); if ((len = circpad_negotiated_encode(cell.payload, CELL_PAYLOAD_SIZE, &type)) < 0) @@ -2923,19 +2985,33 @@ circpad_handle_padding_negotiate(circuit_t *circ, cell_t *cell) if (negotiate->command == CIRCPAD_COMMAND_STOP) { /* Free the machine corresponding to this machine type */ if (free_circ_machineinfos_with_machine_num(circ, - negotiate->machine_type)) { - log_info(LD_CIRC, "Received STOP command for machine %u", - negotiate->machine_type); + negotiate->machine_type, + negotiate->machine_ctr)) { + log_info(LD_CIRC, "Received STOP command for machine %u, ctr %u", + negotiate->machine_type, negotiate->machine_ctr); goto done; } - log_fn(LOG_PROTOCOL_WARN, LD_CIRC, - "Received circuit padding stop command for unknown machine."); - goto err; - } else if (negotiate->command == CIRCPAD_COMMAND_START) { + if (negotiate->machine_ctr <= circ->padding_machine_ctr) { + log_info(LD_CIRC, "Received STOP command for old machine %u, ctr %u", + negotiate->machine_type, negotiate->machine_ctr); + goto done; + + } else { + log_fn(LOG_PROTOCOL_WARN, LD_CIRC, + "Received circuit padding stop command for unknown machine."); + goto err; + } + } else if (negotiate->command == CIRCPAD_COMMAND_START) { SMARTLIST_FOREACH_BEGIN(relay_padding_machines, const circpad_machine_spec_t *, m) { if (m->machine_num == negotiate->machine_type) { circpad_setup_machine_on_circ(circ, m); + if (negotiate->machine_ctr && + circ->padding_machine_ctr != negotiate->machine_ctr) { + log_fn(LOG_PROTOCOL_WARN, LD_CIRC, + "Client and relay have different counts for padding machines: " + "%u vs %u", circ->padding_machine_ctr, negotiate->machine_ctr); + } circpad_cell_event_nonpadding_received(circ); goto done; } @@ -2948,7 +3024,8 @@ circpad_handle_padding_negotiate(circuit_t *circ, cell_t *cell) done: circpad_padding_negotiated(circ, negotiate->machine_type, negotiate->command, - (retval == 0) ? CIRCPAD_RESPONSE_OK : CIRCPAD_RESPONSE_ERR); + (retval == 0) ? CIRCPAD_RESPONSE_OK : CIRCPAD_RESPONSE_ERR, + negotiate->machine_ctr); circpad_negotiate_free(negotiate); return retval; @@ -2999,17 +3076,22 @@ circpad_handle_padding_negotiated(circuit_t *circ, cell_t *cell, * circpad_add_matching_matchines() added a new machine, * there may be a padding_machine for a different machine num * than this response. */ - free_circ_machineinfos_with_machine_num(circ, negotiated->machine_type); + free_circ_machineinfos_with_machine_num(circ, negotiated->machine_type, + negotiated->machine_ctr); } else if (negotiated->command == CIRCPAD_COMMAND_START && negotiated->response == CIRCPAD_RESPONSE_ERR) { - // This can happen due to consensus drift.. free the machines + // This can still happen due to consensus drift.. free the machines // and be sad - free_circ_machineinfos_with_machine_num(circ, negotiated->machine_type); - TO_ORIGIN_CIRCUIT(circ)->padding_negotiation_failed = 1; - log_fn(LOG_PROTOCOL_WARN, LD_CIRC, - "Middle node did not accept our padding request on circuit %u (%d)", - TO_ORIGIN_CIRCUIT(circ)->global_identifier, - circ->purpose); + if (free_circ_machineinfos_with_machine_num(circ, negotiated->machine_type, + negotiated->machine_ctr)) { + // Only fail if a machine was there and matched the error cell + TO_ORIGIN_CIRCUIT(circ)->padding_negotiation_failed = 1; + log_fn(LOG_PROTOCOL_WARN, LD_CIRC, + "Middle node did not accept our padding request on circuit " + "%u (%d)", + TO_ORIGIN_CIRCUIT(circ)->global_identifier, + circ->purpose); + } } circpad_negotiated_free(negotiated); diff --git a/src/core/or/circuitpadding.h b/src/core/or/circuitpadding.h index 74b69a1c7a..3d2929cf74 100644 --- a/src/core/or/circuitpadding.h +++ b/src/core/or/circuitpadding.h @@ -173,11 +173,21 @@ typedef struct circpad_machine_conditions_t { /** Only apply the machine *if* the circuit's state matches any of * the bits set in this bitmask. */ - circpad_circuit_state_t state_mask; + circpad_circuit_state_t apply_state_mask; /** Only apply a machine *if* the circuit's purpose matches one * of the bits set in this bitmask */ - circpad_purpose_mask_t purpose_mask; + circpad_purpose_mask_t apply_purpose_mask; + + /** Keep a machine if any of the circuits's state machine's match + * the bits set in this bitmask, but don't apply new machines if + * they match this mask. */ + circpad_circuit_state_t keep_state_mask; + + /** Keep a machine if any of the circuits's state machine's match + * the bits set in this bitmask, but don't apply new machines if + * they match this mask. */ + circpad_purpose_mask_t keep_purpose_mask; } circpad_machine_conditions_t; @@ -565,6 +575,13 @@ typedef struct circpad_machine_runtime_t { /** What state is this machine in? */ circpad_statenum_t current_state; + /** Machine counter, for shutdown sync. + * + * Set from circuit_t.padding_machine_ctr, which is incremented each + * padding machine instantiation. + */ + uint32_t machine_ctr; + /** * True if we have scheduled a timer for padding. * @@ -726,11 +743,13 @@ signed_error_t circpad_handle_padding_negotiated(struct circuit_t *circ, signed_error_t circpad_negotiate_padding(struct origin_circuit_t *circ, circpad_machine_num_t machine, uint8_t target_hopnum, - uint8_t command); + uint8_t command, + uint32_t machine_ctr); bool circpad_padding_negotiated(struct circuit_t *circ, circpad_machine_num_t machine, uint8_t command, - uint8_t response); + uint8_t response, + uint32_t machine_ctr); circpad_purpose_mask_t circpad_circ_purpose_to_mask(uint8_t circ_purpose); diff --git a/src/core/or/circuitpadding_machines.c b/src/core/or/circuitpadding_machines.c index 98767f9e8f..1e6b580f5b 100644 --- a/src/core/or/circuitpadding_machines.c +++ b/src/core/or/circuitpadding_machines.c @@ -67,7 +67,7 @@ circpad_machine_client_hide_intro_circuits(smartlist_t *machines_sl) client_machine->name = "client_ip_circ"; - client_machine->conditions.state_mask = CIRCPAD_CIRC_OPENED; + client_machine->conditions.apply_state_mask = CIRCPAD_CIRC_OPENED; client_machine->target_hopnum = 2; /* This is a client machine */ @@ -102,9 +102,18 @@ circpad_machine_client_hide_intro_circuits(smartlist_t *machines_sl) * INTRO_MACHINE_MAXIMUM_PADDING cells, to match the "...(inbound data cells * continue)" portion of the trace (aka the rest of an HTTPS response body). */ - client_machine->conditions.purpose_mask = - circpad_circ_purpose_to_mask(CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT)| - circpad_circ_purpose_to_mask(CIRCUIT_PURPOSE_C_INTRODUCE_ACKED)| + + /* Start the machine on fresh intro circs. */ + client_machine->conditions.apply_purpose_mask = + circpad_circ_purpose_to_mask(CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT); + + /* If the client purpose changes back to CIRCUIT_PURPOSE_C_INTRODUCING, + * or transitions to CIRCUIT_PURPOSE_C_INTRODUCE_ACKED, keep the machine + * alive, but do not launch new machines for these purposes. Also + * keep the machine around if it is in the CIRCUIT_PADDING purpose + * (but do not try to take over other machines in that purpose). */ + client_machine->conditions.keep_purpose_mask = + circpad_circ_purpose_to_mask(CIRCUIT_PURPOSE_C_INTRODUCE_ACKED) | circpad_circ_purpose_to_mask(CIRCUIT_PURPOSE_C_CIRCUIT_PADDING); /* Keep the circuit alive even after the introduction has been finished, @@ -152,7 +161,7 @@ circpad_machine_relay_hide_intro_circuits(smartlist_t *machines_sl) relay_machine->name = "relay_ip_circ"; - relay_machine->conditions.state_mask = CIRCPAD_CIRC_OPENED; + relay_machine->conditions.apply_state_mask = CIRCPAD_CIRC_OPENED; /* This is a relay-side machine */ relay_machine->is_origin_side = 0; @@ -263,7 +272,7 @@ circpad_machine_client_hide_rend_circuits(smartlist_t *machines_sl) client_machine->name = "client_rp_circ"; /* Only pad after the circuit has been built and pad to the middle */ - client_machine->conditions.state_mask = CIRCPAD_CIRC_OPENED; + client_machine->conditions.apply_state_mask = CIRCPAD_CIRC_OPENED; client_machine->target_hopnum = 2; /* This is a client machine */ @@ -299,7 +308,7 @@ circpad_machine_client_hide_rend_circuits(smartlist_t *machines_sl) * * Hence this way we make rendezvous circuits look like general circuits up * till the end of the circuit setup. */ - client_machine->conditions.purpose_mask = + client_machine->conditions.apply_purpose_mask = circpad_circ_purpose_to_mask(CIRCUIT_PURPOSE_C_REND_JOINED)| circpad_circ_purpose_to_mask(CIRCUIT_PURPOSE_C_REND_READY)| circpad_circ_purpose_to_mask(CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED); @@ -383,7 +392,7 @@ circpad_machine_relay_hide_rend_circuits(smartlist_t *machines_sl) /* Only pad after the circuit has been built and pad to the middle */ relay_machine->conditions.min_hops = 2; - relay_machine->conditions.state_mask = CIRCPAD_CIRC_OPENED; + relay_machine->conditions.apply_state_mask = CIRCPAD_CIRC_OPENED; /* This is a relay-side machine */ relay_machine->is_origin_side = 0; diff --git a/src/core/or/circuitstats.c b/src/core/or/circuitstats.c index 822e5bd308..51bd9e1208 100644 --- a/src/core/or/circuitstats.c +++ b/src/core/or/circuitstats.c @@ -53,9 +53,6 @@ #undef log #include <math.h> -static void cbt_control_event_buildtimeout_set( - const circuit_build_times_t *cbt, - buildtimeout_set_event_t type); static void circuit_build_times_scale_circ_counts(circuit_build_times_t *cbt); #define CBT_BIN_TO_MS(bin) ((bin)*CBT_BIN_WIDTH + (CBT_BIN_WIDTH/2)) @@ -402,7 +399,7 @@ circuit_build_times_initial_timeout(void) * and learn a new timeout. */ static int32_t -circuit_build_times_recent_circuit_count(networkstatus_t *ns) +circuit_build_times_recent_circuit_count(const networkstatus_t *ns) { int32_t num; num = networkstatus_get_param(ns, "cbtrecentcount", @@ -428,7 +425,7 @@ circuit_build_times_recent_circuit_count(networkstatus_t *ns) */ void circuit_build_times_new_consensus_params(circuit_build_times_t *cbt, - networkstatus_t *ns) + const networkstatus_t *ns) { int32_t num; @@ -545,7 +542,7 @@ circuit_build_times_get_initial_timeout(void) * Leave estimated parameters, timeout and network liveness intact * for future use. */ -STATIC void +void circuit_build_times_reset(circuit_build_times_t *cbt) { memset(cbt->circuit_build_times, 0, sizeof(cbt->circuit_build_times)); @@ -972,7 +969,7 @@ circuit_build_times_update_state(const circuit_build_times_t *cbt, /** * Shuffle the build times array. * - * Adapted from http://en.wikipedia.org/wiki/Fisher-Yates_shuffle + * Adapted from https://en.wikipedia.org/wiki/Fisher-Yates_shuffle */ static void circuit_build_times_shuffle_and_store_array(circuit_build_times_t *cbt, @@ -1183,7 +1180,7 @@ circuit_build_times_parse_state(circuit_build_times_t *cbt, /** * Estimates the Xm and Alpha parameters using - * http://en.wikipedia.org/wiki/Pareto_distribution#Parameter_estimation + * https://en.wikipedia.org/wiki/Pareto_distribution#Parameter_estimation * * The notable difference is that we use mode instead of min to estimate Xm. * This is because our distribution is frechet-like. We claim this is @@ -1198,7 +1195,7 @@ circuit_build_times_update_alpha(circuit_build_times_t *cbt) int n=0,i=0,abandoned_count=0; build_time_t max_time=0; - /* http://en.wikipedia.org/wiki/Pareto_distribution#Parameter_estimation */ + /* https://en.wikipedia.org/wiki/Pareto_distribution#Parameter_estimation */ /* We sort of cheat here and make our samples slightly more pareto-like * and less frechet-like. */ cbt->Xm = circuit_build_times_get_xm(cbt); @@ -1270,9 +1267,9 @@ circuit_build_times_update_alpha(circuit_build_times_t *cbt) * We use it to calculate the timeout and also to generate synthetic * values of time for circuits that timeout before completion. * - * See http://en.wikipedia.org/wiki/Quantile_function, - * http://en.wikipedia.org/wiki/Inverse_transform_sampling and - * http://en.wikipedia.org/wiki/Pareto_distribution#Generating_a_ + * See https://en.wikipedia.org/wiki/Quantile_function, + * https://en.wikipedia.org/wiki/Inverse_transform_sampling and + * https://en.wikipedia.org/wiki/Pareto_distribution#Generating_a_ * random_sample_from_Pareto_distribution * That's right. I'll cite wikipedia all day long. * @@ -1893,61 +1890,3 @@ circuit_build_times_update_last_circ(circuit_build_times_t *cbt) { cbt->last_circ_at = approx_time(); } - -static void -cbt_control_event_buildtimeout_set(const circuit_build_times_t *cbt, - buildtimeout_set_event_t type) -{ - char *args = NULL; - double qnt; - double timeout_rate = 0.0; - double close_rate = 0.0; - - switch (type) { - case BUILDTIMEOUT_SET_EVENT_RESET: - case BUILDTIMEOUT_SET_EVENT_SUSPENDED: - case BUILDTIMEOUT_SET_EVENT_DISCARD: - qnt = 1.0; - break; - case BUILDTIMEOUT_SET_EVENT_COMPUTED: - case BUILDTIMEOUT_SET_EVENT_RESUME: - default: - qnt = circuit_build_times_quantile_cutoff(); - break; - } - - /* The timeout rate is the ratio of the timeout count over - * the total number of circuits attempted. The total number of - * circuits is (timeouts+succeeded), since every circuit - * either succeeds, or times out. "Closed" circuits are - * MEASURE_TIMEOUT circuits whose measurement period expired. - * All MEASURE_TIMEOUT circuits are counted in the timeouts stat - * before transitioning to MEASURE_TIMEOUT (in - * circuit_build_times_mark_circ_as_measurement_only()). - * MEASURE_TIMEOUT circuits that succeed are *not* counted as - * "succeeded". See circuit_build_times_handle_completed_hop(). - * - * We cast the denominator - * to promote it to double before the addition, to avoid int32 - * overflow. */ - const double total_circuits = - ((double)cbt->num_circ_timeouts) + cbt->num_circ_succeeded; - if (total_circuits >= 1.0) { - timeout_rate = cbt->num_circ_timeouts / total_circuits; - close_rate = cbt->num_circ_closed / total_circuits; - } - - tor_asprintf(&args, "TOTAL_TIMES=%lu " - "TIMEOUT_MS=%lu XM=%lu ALPHA=%f CUTOFF_QUANTILE=%f " - "TIMEOUT_RATE=%f CLOSE_MS=%lu CLOSE_RATE=%f", - (unsigned long)cbt->total_build_times, - (unsigned long)cbt->timeout_ms, - (unsigned long)cbt->Xm, cbt->alpha, qnt, - timeout_rate, - (unsigned long)cbt->close_ms, - close_rate); - - control_event_buildtimeout_set(type, args); - - tor_free(args); -} diff --git a/src/core/or/circuitstats.h b/src/core/or/circuitstats.h index 52c9100f53..930e0a9ba3 100644 --- a/src/core/or/circuitstats.h +++ b/src/core/or/circuitstats.h @@ -43,12 +43,13 @@ int circuit_build_times_needs_circuits_now(const circuit_build_times_t *cbt); void circuit_build_times_init(circuit_build_times_t *cbt); void circuit_build_times_free_timeouts(circuit_build_times_t *cbt); void circuit_build_times_new_consensus_params(circuit_build_times_t *cbt, - networkstatus_t *ns); + const networkstatus_t *ns); double circuit_build_times_timeout_rate(const circuit_build_times_t *cbt); double circuit_build_times_close_rate(const circuit_build_times_t *cbt); void circuit_build_times_update_last_circ(circuit_build_times_t *cbt); void circuit_build_times_mark_circ_as_measurement_only(origin_circuit_t *circ); +void circuit_build_times_reset(circuit_build_times_t *cbt); /** Total size of the circuit timeout history to accumulate. * 1000 is approx 2.5 days worth of continual-use circuits. */ @@ -137,7 +138,6 @@ int32_t circuit_build_times_initial_timeout(void); STATIC double circuit_build_times_calculate_timeout(circuit_build_times_t *cbt, double quantile); STATIC int circuit_build_times_update_alpha(circuit_build_times_t *cbt); -STATIC void circuit_build_times_reset(circuit_build_times_t *cbt); /* Network liveness functions */ STATIC int circuit_build_times_network_check_changed( @@ -158,7 +158,6 @@ void circuit_build_times_network_is_live(circuit_build_times_t *cbt); int circuit_build_times_network_check_live(const circuit_build_times_t *cbt); void circuit_build_times_network_circ_success(circuit_build_times_t *cbt); -#ifdef CIRCUITSTATS_PRIVATE /** Information about the state of our local network connection */ typedef struct { /** The timestamp we last completed a TLS handshake or received a cell */ @@ -208,6 +207,5 @@ struct circuit_build_times_t { uint32_t num_circ_closed; }; -#endif /* defined(CIRCUITSTATS_PRIVATE) */ #endif /* !defined(TOR_CIRCUITSTATS_H) */ diff --git a/src/core/or/circuituse.c b/src/core/or/circuituse.c index e2c4df25d0..3c8a7c4a89 100644 --- a/src/core/or/circuituse.c +++ b/src/core/or/circuituse.c @@ -37,7 +37,9 @@ #include "core/or/circuituse.h" #include "core/or/circuitpadding.h" #include "core/or/connection_edge.h" +#include "core/or/extendinfo.h" #include "core/or/policies.h" +#include "core/or/trace_probes_circuit.h" #include "feature/client/addressmap.h" #include "feature/client/bridges.h" #include "feature/client/circpathbias.h" @@ -62,6 +64,7 @@ #include "feature/stats/predict_ports.h" #include "lib/math/fp.h" #include "lib/time/tvdiff.h" +#include "lib/trace/events.h" #include "core/or/cpath_build_state_st.h" #include "feature/dircommon/dir_connection_st.h" @@ -202,8 +205,8 @@ circuit_is_acceptable(const origin_circuit_t *origin_circ, const int family = tor_addr_parse(&addr, conn->socks_request->address); if (family < 0 || - !tor_addr_eq(&build_state->chosen_exit->addr, &addr) || - build_state->chosen_exit->port != conn->socks_request->port) + !extend_info_has_orport(build_state->chosen_exit, &addr, + conn->socks_request->port)) return 0; } } @@ -816,7 +819,7 @@ circuit_expire_building(void) log_info(LD_CIRC, "Abandoning circ %u %s:%u (state %d,%d:%s, purpose %d, " "len %d)", TO_ORIGIN_CIRCUIT(victim)->global_identifier, - channel_get_canonical_remote_descr(victim->n_chan), + channel_describe_peer(victim->n_chan), (unsigned)victim->n_circ_id, TO_ORIGIN_CIRCUIT(victim)->has_opened, victim->state, circuit_state_to_string(victim->state), @@ -837,6 +840,7 @@ circuit_expire_building(void) -1); circuit_log_path(LOG_INFO,LD_CIRC,TO_ORIGIN_CIRCUIT(victim)); + tor_trace(TR_SUBSYS(circuit), TR_EV(timeout), TO_ORIGIN_CIRCUIT(victim)); if (victim->purpose == CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT) circuit_mark_for_close(victim, END_CIRC_REASON_MEASUREMENT_EXPIRED); else @@ -1500,8 +1504,11 @@ circuit_expire_old_circuits_clientside(void) circ->purpose); /* Don't do this magic for testing circuits. Their death is governed * by circuit_expire_building */ - if (circ->purpose != CIRCUIT_PURPOSE_PATH_BIAS_TESTING) + if (circ->purpose != CIRCUIT_PURPOSE_PATH_BIAS_TESTING) { + tor_trace(TR_SUBSYS(circuit), TR_EV(idle_timeout), + TO_ORIGIN_CIRCUIT(circ)); circuit_mark_for_close(circ, END_CIRC_REASON_FINISHED); + } } else if (!circ->timestamp_dirty && circ->state == CIRCUIT_STATE_OPEN) { if (timercmp(&circ->timestamp_began, &cutoff, OP_LT)) { if (circ->purpose == CIRCUIT_PURPOSE_C_GENERAL || @@ -1520,6 +1527,8 @@ circuit_expire_old_circuits_clientside(void) " that has been unused for %ld msec.", TO_ORIGIN_CIRCUIT(circ)->global_identifier, tv_mdiff(&circ->timestamp_began, &now)); + tor_trace(TR_SUBSYS(circuit), TR_EV(idle_timeout), + TO_ORIGIN_CIRCUIT(circ)); circuit_mark_for_close(circ, END_CIRC_REASON_FINISHED); } else if (!TO_ORIGIN_CIRCUIT(circ)->is_ancient) { /* Server-side rend joined circuits can end up really old, because @@ -1642,7 +1651,7 @@ static void circuit_testing_opened(origin_circuit_t *circ) { if (have_performed_bandwidth_test || - !check_whether_orport_reachable(get_options())) { + !router_all_orports_seem_reachable(get_options())) { /* either we've already done everything we want with testing circuits, * or this testing circuit became open due to a fluke, e.g. we picked * a last hop where we already had the connection open due to an @@ -1660,7 +1669,8 @@ static void circuit_testing_failed(origin_circuit_t *circ, int at_last_hop) { const or_options_t *options = get_options(); - if (server_mode(options) && check_whether_orport_reachable(options)) + if (server_mode(options) && + router_all_orports_seem_reachable(options)) return; log_info(LD_GENERAL, @@ -1681,6 +1691,7 @@ circuit_testing_failed(origin_circuit_t *circ, int at_last_hop) void circuit_has_opened(origin_circuit_t *circ) { + tor_trace(TR_SUBSYS(circuit), TR_EV(opened), circ); circuit_event_status(circ, CIRC_EVENT_BUILT, 0); /* Remember that this circuit has finished building. Now if we start @@ -1846,7 +1857,7 @@ circuit_build_failed(origin_circuit_t *circ) "from the first hop (%s). I'm going to try to rotate to a " "better connection.", TO_CIRCUIT(circ)->n_circ_id, circ->global_identifier, - channel_get_canonical_remote_descr(n_chan)); + channel_describe_peer(n_chan)); n_chan->is_bad_for_new_circs = 1; } else { log_info(LD_OR, @@ -2092,11 +2103,18 @@ circuit_should_cannibalize_to_build(uint8_t purpose_to_build, } /** Launch a new circuit with purpose <b>purpose</b> and exit node - * <b>extend_info</b> (or NULL to select a random exit node). If flags - * contains CIRCLAUNCH_NEED_UPTIME, choose among routers with high uptime. If - * CIRCLAUNCH_NEED_CAPACITY is set, choose among routers with high bandwidth. - * If CIRCLAUNCH_IS_INTERNAL is true, the last hop need not be an exit node. - * If CIRCLAUNCH_ONEHOP_TUNNEL is set, the circuit will have only one hop. + * <b>extend_info</b> (or NULL to select a random exit node). + * + * If flags contains: + * - CIRCLAUNCH_ONEHOP_TUNNEL: the circuit will have only one hop; + * - CIRCLAUNCH_NEED_UPTIME: choose routers with high uptime; + * - CIRCLAUNCH_NEED_CAPACITY: choose routers with high bandwidth; + * - CIRCLAUNCH_IS_IPV6_SELFTEST: the second-last hop must support IPv6 + * extends; + * - CIRCLAUNCH_IS_INTERNAL: the last hop need not be an exit node; + * - CIRCLAUNCH_IS_V3_RP: the last hop must support v3 onion service + * rendezvous. + * * Return the newly allocated circuit on success, or NULL on failure. */ origin_circuit_t * circuit_launch_by_extend_info(uint8_t purpose, @@ -2195,6 +2213,8 @@ circuit_launch_by_extend_info(uint8_t purpose, tor_fragile_assert(); return NULL; } + + tor_trace(TR_SUBSYS(circuit), TR_EV(cannibalized), circ); return circ; } } @@ -3126,6 +3146,8 @@ circuit_change_purpose(circuit_t *circ, uint8_t new_purpose) old_purpose = circ->purpose; circ->purpose = new_purpose; + tor_trace(TR_SUBSYS(circuit), TR_EV(change_purpose), circ, old_purpose, + new_purpose); if (CIRCUIT_IS_ORIGIN(circ)) { control_event_circuit_purpose_changed(TO_ORIGIN_CIRCUIT(circ), diff --git a/src/core/or/circuituse.h b/src/core/or/circuituse.h index 95d36d6474..028fe4aa48 100644 --- a/src/core/or/circuituse.h +++ b/src/core/or/circuituse.h @@ -36,17 +36,23 @@ void circuit_try_attaching_streams(origin_circuit_t *circ); void circuit_build_failed(origin_circuit_t *circ); /** Flag to set when a circuit should have only a single hop. */ -#define CIRCLAUNCH_ONEHOP_TUNNEL (1<<0) +#define CIRCLAUNCH_ONEHOP_TUNNEL (1<<0) /** Flag to set when a circuit needs to be built of high-uptime nodes */ -#define CIRCLAUNCH_NEED_UPTIME (1<<1) +#define CIRCLAUNCH_NEED_UPTIME (1<<1) /** Flag to set when a circuit needs to be built of high-capacity nodes */ -#define CIRCLAUNCH_NEED_CAPACITY (1<<2) +#define CIRCLAUNCH_NEED_CAPACITY (1<<2) /** Flag to set when the last hop of a circuit doesn't need to be an * exit node. */ -#define CIRCLAUNCH_IS_INTERNAL (1<<3) +#define CIRCLAUNCH_IS_INTERNAL (1<<3) /** Flag to set when we are trying to launch a v3 rendezvous circuit. We need * to apply some additional filters on the node picked. */ -#define CIRCLAUNCH_IS_V3_RP (1<<4) +#define CIRCLAUNCH_IS_V3_RP (1<<4) +/** Flag to set when we are trying to launch a self-testing circuit to our + * IPv6 ORPort. We need to apply some additional filters on the second-last + * node in the circuit. (We are both the client and the last node in the + * circuit.) */ +#define CIRCLAUNCH_IS_IPV6_SELFTEST (1<<5) + origin_circuit_t *circuit_launch_by_extend_info(uint8_t purpose, extend_info_t *info, int flags); diff --git a/src/core/or/command.c b/src/core/or/command.c index 8a1d2066cc..9226309ff7 100644 --- a/src/core/or/command.c +++ b/src/core/or/command.c @@ -252,7 +252,7 @@ command_process_create_cell(cell_t *cell, channel_t *chan) log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, "Received a create cell (type %d) from %s with zero circID; " " ignoring.", (int)cell->command, - channel_get_actual_remote_descr(chan)); + channel_describe_peer(chan)); return; } @@ -295,7 +295,7 @@ command_process_create_cell(cell_t *cell, channel_t *chan) "Received create cell (type %d) from %s, but we're connected " "to it as a client. " "Sending back a destroy.", - (int)cell->command, channel_get_canonical_remote_descr(chan)); + (int)cell->command, channel_describe_peer(chan)); channel_send_destroy(cell->circ_id, chan, END_CIRC_REASON_TORPROTOCOL); return; @@ -475,7 +475,7 @@ command_process_relay_cell(cell_t *cell, channel_t *chan) log_debug(LD_OR, "unknown circuit %u on connection from %s. Dropping.", (unsigned)cell->circ_id, - channel_get_canonical_remote_descr(chan)); + channel_describe_peer(chan)); return; } @@ -536,7 +536,7 @@ command_process_relay_cell(cell_t *cell, channel_t *chan) control_event_circ_bandwidth_used_for_circ(TO_ORIGIN_CIRCUIT(circ)); } else if (circ->n_chan) { log_warn(LD_OR, " upstream=%s", - channel_get_actual_remote_descr(circ->n_chan)); + channel_describe_peer(circ->n_chan)); } circuit_mark_for_close(circ, END_CIRC_REASON_TORPROTOCOL); return; @@ -547,7 +547,7 @@ command_process_relay_cell(cell_t *cell, channel_t *chan) "Received too many RELAY_EARLY cells on circ %u from %s." " Closing circuit.", (unsigned)cell->circ_id, - safe_str(channel_get_canonical_remote_descr(chan))); + safe_str(channel_describe_peer(chan))); circuit_mark_for_close(circ, END_CIRC_REASON_TORPROTOCOL); return; } @@ -618,7 +618,7 @@ command_process_destroy_cell(cell_t *cell, channel_t *chan) if (!circ) { log_info(LD_OR,"unknown circuit %u on connection from %s. Dropping.", (unsigned)cell->circ_id, - channel_get_canonical_remote_descr(chan)); + channel_describe_peer(chan)); return; } log_debug(LD_OR,"Received for circID %u.",(unsigned)cell->circ_id); diff --git a/src/core/or/connection_edge.c b/src/core/or/connection_edge.c index fc77db8334..ed27fb1b57 100644 --- a/src/core/or/connection_edge.c +++ b/src/core/or/connection_edge.c @@ -70,6 +70,7 @@ #include "core/or/circuitpadding.h" #include "core/or/connection_edge.h" #include "core/or/connection_or.h" +#include "core/or/extendinfo.h" #include "core/or/policies.h" #include "core/or/reasons.h" #include "core/or/relay.h" @@ -165,8 +166,12 @@ static int connection_exit_connect_dir(edge_connection_t *exitconn); static int consider_plaintext_ports(entry_connection_t *conn, uint16_t port); static int connection_ap_supports_optimistic_data(const entry_connection_t *); -/** Convert a connection_t* to an edge_connection_t*; assert if the cast is - * invalid. */ +/** + * Cast a `connection_t *` to an `edge_connection_t *`. + * + * Exit with an assertion failure if the input is not an + * `edge_connection_t`. + **/ edge_connection_t * TO_EDGE_CONN(connection_t *c) { @@ -175,6 +180,24 @@ TO_EDGE_CONN(connection_t *c) return DOWNCAST(edge_connection_t, c); } +/** + * Cast a `const connection_t *` to a `const edge_connection_t *`. + * + * Exit with an assertion failure if the input is not an + * `edge_connection_t`. + **/ +const edge_connection_t * +CONST_TO_EDGE_CONN(const connection_t *c) +{ + return TO_EDGE_CONN((connection_t *)c); +} + +/** + * Cast a `connection_t *` to an `entry_connection_t *`. + * + * Exit with an assertion failure if the input is not an + * `entry_connection_t`. + **/ entry_connection_t * TO_ENTRY_CONN(connection_t *c) { @@ -182,6 +205,24 @@ TO_ENTRY_CONN(connection_t *c) return (entry_connection_t*) SUBTYPE_P(c, entry_connection_t, edge_.base_); } +/** + * Cast a `const connection_t *` to a `const entry_connection_t *`. + * + * Exit with an assertion failure if the input is not an + * `entry_connection_t`. + **/ +const entry_connection_t * +CONST_TO_ENTRY_CONN(const connection_t *c) +{ + return TO_ENTRY_CONN((connection_t*) c); +} + +/** + * Cast an `edge_connection_t *` to an `entry_connection_t *`. + * + * Exit with an assertion failure if the input is not an + * `entry_connection_t`. + **/ entry_connection_t * EDGE_TO_ENTRY_CONN(edge_connection_t *c) { @@ -189,6 +230,18 @@ EDGE_TO_ENTRY_CONN(edge_connection_t *c) return (entry_connection_t*) SUBTYPE_P(c, entry_connection_t, edge_); } +/** + * Cast a `const edge_connection_t *` to a `const entry_connection_t *`. + * + * Exit with an assertion failure if the input is not an + * `entry_connection_t`. + **/ +const entry_connection_t * +CONST_EDGE_TO_ENTRY_CONN(const edge_connection_t *c) +{ + return EDGE_TO_ENTRY_CONN((edge_connection_t*)c); +} + /** An AP stream has failed/finished. If it hasn't already sent back * a socks reply, send one now (based on endreason). Also set * has_sent_end to 1, and mark the conn. @@ -423,9 +476,7 @@ warn_if_hs_unreachable(const edge_connection_t *conn, uint8_t reason) char *m; if ((m = rate_limit_log(&warn_limit, approx_time()))) { log_warn(LD_EDGE, "Onion service connection to %s failed (%s)", - (conn->base_.socket_family == AF_UNIX) ? - safe_str(conn->base_.address) : - safe_str(fmt_addrport(&conn->base_.addr, conn->base_.port)), + connection_describe_peer(TO_CONN(conn)), stream_end_reason_to_string(reason)); tor_free(m); } @@ -921,9 +972,8 @@ connection_edge_finished_connecting(edge_connection_t *edge_conn) conn = TO_CONN(edge_conn); tor_assert(conn->state == EXIT_CONN_STATE_CONNECTING); - log_info(LD_EXIT,"Exit connection to %s:%u (%s) established.", - escaped_safe_str(conn->address), conn->port, - safe_str(fmt_and_decorate_addr(&conn->addr))); + log_info(LD_EXIT,"%s established.", + connection_describe(conn)); rep_hist_note_exit_stream_opened(conn->port); @@ -1444,8 +1494,8 @@ connection_ap_fail_onehop(const char *failed_digest, continue; } if (tor_addr_parse(&addr, entry_conn->socks_request->address)<0 || - !tor_addr_eq(&build_state->chosen_exit->addr, &addr) || - build_state->chosen_exit->port != entry_conn->socks_request->port) + !extend_info_has_orport(build_state->chosen_exit, &addr, + entry_conn->socks_request->port)) continue; } log_info(LD_APP, "Closing one-hop stream to '%s/%s' because the OR conn " @@ -1663,6 +1713,9 @@ parse_extended_hostname(char *address, hostname_type_t *type_out) log_warn(LD_APP, "Invalid %shostname %s; rejecting", is_onion ? "onion " : "", safe_str_client(address)); + if (*type_out == ONION_V3_HOSTNAME) { + *type_out = BAD_HOSTNAME; + } return false; } @@ -2139,7 +2192,7 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn, if (!parse_extended_hostname(socks->address, &addresstype)) { control_event_client_status(LOG_WARN, "SOCKS_BAD_HOSTNAME HOSTNAME=%s", escaped(socks->address)); - if (addresstype == ONION_V3_HOSTNAME) { + if (addresstype == BAD_HOSTNAME) { conn->socks_request->socks_extended_error_code = SOCKS5_HS_BAD_ADDRESS; } connection_mark_unattached_ap(conn, END_STREAM_REASON_TORPROTOCOL); @@ -3836,8 +3889,8 @@ handle_hs_exit_conn(circuit_t *circ, edge_connection_t *conn) return -1; } if (ret < 0) { - log_info(LD_REND, "Didn't find rendezvous service (addr%s, port %d)", - fmt_addr(&TO_CONN(conn)->addr), TO_CONN(conn)->port); + log_info(LD_REND, "Didn't find rendezvous service at %s", + connection_describe_peer(TO_CONN(conn))); /* Send back reason DONE because we want to make hidden service port * scanning harder thus instead of returning that the exit policy * didn't match, which makes it obvious that the port is closed, @@ -3972,7 +4025,7 @@ connection_exit_begin_conn(cell_t *cell, circuit_t *circ) * proxies. */ log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, "Attempt by %s to open a stream %s. Closing.", - safe_str(channel_get_canonical_remote_descr(or_circ->p_chan)), + safe_str(channel_describe_peer(or_circ->p_chan)), client_chan ? "on first hop of circuit" : "from unknown relay"); relay_send_end_cell_from_edge(rh.stream_id, circ, @@ -3995,10 +4048,13 @@ connection_exit_begin_conn(cell_t *cell, circuit_t *circ) * caller might want to know whether the remote IP address has changed, * and we might already have corrected base_.addr[ess] for the relay's * canonical IP address. */ - if (or_circ && or_circ->p_chan) - address = tor_strdup(channel_get_actual_remote_address(or_circ->p_chan)); - else + tor_addr_t chan_addr; + if (or_circ && or_circ->p_chan && + channel_get_addr_if_possible(or_circ->p_chan, &chan_addr)) { + address = tor_addr_to_str_dup(&chan_addr); + } else { address = tor_strdup("127.0.0.1"); + } port = 1; /* XXXX This value is never actually used anywhere, and there * isn't "really" a connection here. But we * need to set it to something nonzero. */ @@ -4188,8 +4244,8 @@ connection_exit_connect(edge_connection_t *edge_conn) &why_failed_exit_policy)) { if (BUG(!why_failed_exit_policy)) why_failed_exit_policy = ""; - log_info(LD_EXIT,"%s:%d failed exit policy%s. Closing.", - escaped_safe_str_client(conn->address), conn->port, + log_info(LD_EXIT,"%s failed exit policy%s. Closing.", + connection_describe(conn), why_failed_exit_policy); connection_edge_end(edge_conn, END_STREAM_REASON_EXITPOLICY); circuit_detach_stream(circuit_get_by_edge_conn(edge_conn), edge_conn); diff --git a/src/core/or/connection_edge.h b/src/core/or/connection_edge.h index 8c06af5664..9b2cbb8532 100644 --- a/src/core/or/connection_edge.h +++ b/src/core/or/connection_edge.h @@ -20,6 +20,10 @@ edge_connection_t *TO_EDGE_CONN(connection_t *); entry_connection_t *TO_ENTRY_CONN(connection_t *); entry_connection_t *EDGE_TO_ENTRY_CONN(edge_connection_t *); +const edge_connection_t *CONST_TO_EDGE_CONN(const connection_t *); +const entry_connection_t *CONST_TO_ENTRY_CONN(const connection_t *); +const entry_connection_t *CONST_EDGE_TO_ENTRY_CONN(const edge_connection_t *); + #define EXIT_CONN_STATE_MIN_ 1 /** State for an exit connection: waiting for response from DNS farm. */ #define EXIT_CONN_STATE_RESOLVING 1 diff --git a/src/core/or/connection_or.c b/src/core/or/connection_or.c index 5d71b363f8..0795521be0 100644 --- a/src/core/or/connection_or.c +++ b/src/core/or/connection_or.c @@ -99,8 +99,11 @@ static void connection_or_check_canonicity(or_connection_t *conn, /**************************************************************/ -/** Convert a connection_t* to an or_connection_t*; assert if the cast is - * invalid. */ +/** + * Cast a `connection_t *` to an `or_connection_t *`. + * + * Exit with an assertion failure if the input is not an `or_connnection_t`. + **/ or_connection_t * TO_OR_CONN(connection_t *c) { @@ -108,6 +111,17 @@ TO_OR_CONN(connection_t *c) return DOWNCAST(or_connection_t, c); } +/** + * Cast a `const connection_t *` to a `const or_connection_t *`. + * + * Exit with an assertion failure if the input is not an `or_connnection_t`. + **/ +const or_connection_t * +CONST_TO_OR_CONN(const connection_t *c) +{ + return TO_OR_CONN((connection_t *)c); +} + /** Clear clear conn->identity_digest and update other data * structures as appropriate.*/ void @@ -151,9 +165,9 @@ connection_or_set_identity_digest(or_connection_t *conn, if (conn->chan) chan = TLS_CHAN_TO_BASE(conn->chan); - log_info(LD_HANDSHAKE, "Set identity digest for %p (%s): %s %s.", + log_info(LD_HANDSHAKE, "Set identity digest for %s at %p: %s %s.", + connection_describe(TO_CONN(conn)), conn, - escaped_safe_str(conn->base_.address), hex_str(rsa_digest, DIGEST_LEN), ed25519_fmt(ed_id)); log_info(LD_HANDSHAKE, " (Previously: %s %s)", @@ -575,11 +589,9 @@ connection_or_process_inbuf(or_connection_t *conn) * 100% true. */ if (buf_datalen(conn->base_.inbuf) > MAX_OR_INBUF_WHEN_NONOPEN) { log_fn(LOG_PROTOCOL_WARN, LD_NET, "Accumulated too much data (%d bytes) " - "on nonopen OR connection %s %s:%u in state %s; closing.", + "on non-open %s; closing.", (int)buf_datalen(conn->base_.inbuf), - connection_or_nonopen_was_started_here(conn) ? "to" : "from", - conn->base_.address, conn->base_.port, - conn_state_to_string(conn->base_.type, conn->base_.state)); + connection_describe(TO_CONN(conn))); connection_or_close_for_error(conn, 0); ret = -1; } @@ -691,8 +703,8 @@ connection_or_finished_connecting(or_connection_t *or_conn) conn = TO_CONN(or_conn); tor_assert(conn->state == OR_CONN_STATE_CONNECTING); - log_debug(LD_HANDSHAKE,"OR connect() to router at %s:%u finished.", - conn->address,conn->port); + log_debug(LD_HANDSHAKE,"connect finished for %s", + connection_describe(conn)); if (proxy_type != PROXY_NONE) { /* start proxy handshake */ @@ -745,10 +757,16 @@ connection_or_about_to_close(or_connection_t *or_conn) int reason = tls_error_to_orconn_end_reason(or_conn->tls_error); connection_or_event_status(or_conn, OR_CONN_EVENT_FAILED, reason); - if (!authdir_mode_tests_reachability(options)) - control_event_bootstrap_prob_or( - orconn_end_reason_to_control_string(reason), - reason, or_conn); + if (!authdir_mode_tests_reachability(options)) { + const char *warning = NULL; + if (reason == END_OR_CONN_REASON_TLS_ERROR && or_conn->tls) { + warning = tor_tls_get_last_error_msg(or_conn->tls); + } + if (warning == NULL) { + warning = orconn_end_reason_to_control_string(reason); + } + control_event_bootstrap_prob_or(warning, reason, or_conn); + } } } } else if (conn->hold_open_until_flushed) { @@ -875,7 +893,9 @@ connection_or_init_conn_from_address(or_connection_t *conn, conn->base_.port = port; tor_addr_copy(&conn->base_.addr, addr); - tor_addr_copy(&conn->real_addr, addr); + if (! conn->base_.address) { + conn->base_.address = tor_strdup(fmt_addr(addr)); + } connection_or_check_canonicity(conn, started_here); } @@ -887,9 +907,10 @@ connection_or_init_conn_from_address(or_connection_t *conn, static void connection_or_check_canonicity(or_connection_t *conn, int started_here) { + (void) started_here; + const char *id_digest = conn->identity_digest; const ed25519_public_key_t *ed_id = NULL; - const tor_addr_t *addr = &conn->real_addr; if (conn->chan) ed_id = & TLS_CHAN_TO_BASE(conn->chan)->ed25519_identity; @@ -918,34 +939,17 @@ connection_or_check_canonicity(or_connection_t *conn, int started_here) } else { node_ap = &node_ipv6_ap; } - if (!started_here) { - /* Override the addr/port, so our log messages will make sense. - * This is dangerous, since if we ever try looking up a conn by - * its actual addr/port, we won't remember. Careful! */ - /* XXXX arma: this is stupid, and it's the reason we need real_addr - * to track is_canonical properly. What requires it? */ - /* XXXX <arma> i believe the reason we did this, originally, is because - * we wanted to log what OR a connection was to, and if we logged the - * right IP address and port 56244, that wouldn't be as helpful. now we - * log the "right" port too, so we know if it's moria1 or moria2. - */ - /* See #33898 for a ticket that resolves this technical debt. */ - tor_addr_copy(&conn->base_.addr, &node_ap->addr); - conn->base_.port = node_ap->port; - } + /* Remember the canonical addr/port so our log messages will make + sense. */ + tor_addr_port_copy(&conn->canonical_orport, node_ap); tor_free(conn->nickname); conn->nickname = tor_strdup(node_get_nickname(r)); - tor_free(conn->base_.address); - conn->base_.address = tor_addr_to_str_dup(&node_ap->addr); } else { tor_free(conn->nickname); conn->nickname = tor_malloc(HEX_DIGEST_LEN+2); conn->nickname[0] = '$'; base16_encode(conn->nickname+1, HEX_DIGEST_LEN+1, conn->identity_digest, DIGEST_LEN); - - tor_free(conn->base_.address); - conn->base_.address = tor_addr_to_str_dup(addr); } /* @@ -1004,9 +1008,10 @@ connection_or_single_set_badness_(time_t now, or_conn->base_.timestamp_created + TIME_BEFORE_OR_CONN_IS_TOO_OLD < now) { log_info(LD_OR, - "Marking OR conn to %s:%d as too old for new circuits " + "Marking %s as too old for new circuits " "(fd "TOR_SOCKET_T_FORMAT", %d secs old).", - or_conn->base_.address, or_conn->base_.port, or_conn->base_.s, + connection_describe(TO_CONN(or_conn)), + or_conn->base_.s, (int)(now - or_conn->base_.timestamp_created)); connection_or_mark_bad_for_new_circs(or_conn); } @@ -1071,10 +1076,11 @@ connection_or_group_set_badness_(smartlist_t *group, int force) /* We have at least one open canonical connection to this router, * and this one is open but not canonical. Mark it bad. */ log_info(LD_OR, - "Marking OR conn to %s:%d as unsuitable for new circuits: " + "Marking %s unsuitable for new circuits: " "(fd "TOR_SOCKET_T_FORMAT", %d secs old). It is not " "canonical, and we have another connection to that OR that is.", - or_conn->base_.address, or_conn->base_.port, or_conn->base_.s, + connection_describe(TO_CONN(or_conn)), + or_conn->base_.s, (int)(now - or_conn->base_.timestamp_created)); connection_or_mark_bad_for_new_circs(or_conn); continue; @@ -1115,22 +1121,24 @@ connection_or_group_set_badness_(smartlist_t *group, int force) /* This isn't the best conn, _and_ the best conn is better than it */ if (best->is_canonical) { log_info(LD_OR, - "Marking OR conn to %s:%d as unsuitable for new circuits: " + "Marking %s as unsuitable for new circuits: " "(fd "TOR_SOCKET_T_FORMAT", %d secs old). " "We have a better canonical one " "(fd "TOR_SOCKET_T_FORMAT"; %d secs old).", - or_conn->base_.address, or_conn->base_.port, or_conn->base_.s, + connection_describe(TO_CONN(or_conn)), + or_conn->base_.s, (int)(now - or_conn->base_.timestamp_created), best->base_.s, (int)(now - best->base_.timestamp_created)); connection_or_mark_bad_for_new_circs(or_conn); - } else if (!tor_addr_compare(&or_conn->real_addr, - &best->real_addr, CMP_EXACT)) { + } else if (tor_addr_eq(&TO_CONN(or_conn)->addr, + &TO_CONN(best)->addr)) { log_info(LD_OR, - "Marking OR conn to %s:%d as unsuitable for new circuits: " + "Marking %s unsuitable for new circuits: " "(fd "TOR_SOCKET_T_FORMAT", %d secs old). We have a better " "one with the " "same address (fd "TOR_SOCKET_T_FORMAT"; %d secs old).", - or_conn->base_.address, or_conn->base_.port, or_conn->base_.s, + connection_describe(TO_CONN(or_conn)), + or_conn->base_.s, (int)(now - or_conn->base_.timestamp_created), best->base_.s, (int)(now - best->base_.timestamp_created)); connection_or_mark_bad_for_new_circs(or_conn); @@ -1154,7 +1162,7 @@ static time_t or_connect_failure_map_next_cleanup_ts = 0; * port. * * We need to identify a connection failure with these three values because we - * want to avoid to wrongfully blacklist a relay if someone is trying to + * want to avoid to wrongfully block a relay if someone is trying to * extend to a known identity digest but with the wrong IP/port. For instance, * it can happen if a relay changed its port but the client still has an old * descriptor with the old port. We want to stop connecting to that @@ -1253,7 +1261,7 @@ static or_connect_failure_entry_t * or_connect_failure_new(const or_connection_t *or_conn) { or_connect_failure_entry_t *ocf = tor_malloc_zero(sizeof(*ocf)); - or_connect_failure_init(or_conn->identity_digest, &or_conn->real_addr, + or_connect_failure_init(or_conn->identity_digest, &TO_CONN(or_conn)->addr, TO_CONN(or_conn)->port, ocf); return ocf; } @@ -1458,10 +1466,9 @@ connection_or_connect, (const tor_addr_t *_addr, uint16_t port, * that is we haven't had a failure earlier. This is to avoid to try to * constantly connect to relays that we think are not reachable. */ if (!should_connect_to_relay(conn)) { - log_info(LD_GENERAL, "Can't connect to identity %s at %s:%u because we " + log_info(LD_GENERAL, "Can't connect to %s because we " "failed earlier. Refusing.", - hex_str(id_digest, DIGEST_LEN), fmt_addr(&TO_CONN(conn)->addr), - TO_CONN(conn)->port); + connection_describe_peer(TO_CONN(conn))); connection_free_(TO_CONN(conn)); return NULL; } @@ -1501,7 +1508,7 @@ connection_or_connect, (const tor_addr_t *_addr, uint16_t port, "transport proxy supporting '%s'. This can happen if you " "haven't provided a ClientTransportPlugin line, or if " "your pluggable transport proxy stopped running.", - fmt_addrport(&TO_CONN(conn)->addr, TO_CONN(conn)->port), + connection_describe_peer(TO_CONN(conn)), transport_name, transport_name); control_event_bootstrap_prob_or( @@ -1510,9 +1517,9 @@ connection_or_connect, (const tor_addr_t *_addr, uint16_t port, conn); } else { - log_warn(LD_GENERAL, "Tried to connect to '%s' through a proxy, but " + log_warn(LD_GENERAL, "Tried to connect to %s through a proxy, but " "the proxy address could not be found.", - fmt_addrport(&TO_CONN(conn)->addr, TO_CONN(conn)->port)); + connection_describe_peer(TO_CONN(conn))); } connection_free_(TO_CONN(conn)); @@ -1632,8 +1639,8 @@ connection_tls_start_handshake,(or_connection_t *conn, int receiving)) log_warn(LD_BUG,"tor_tls_new failed. Closing."); return -1; } - tor_tls_set_logged_address(conn->tls, // XXX client and relay? - escaped_safe_str(conn->base_.address)); + tor_tls_set_logged_address(conn->tls, + connection_describe_peer(TO_CONN(conn))); connection_start_reading(TO_CONN(conn)); log_debug(LD_HANDSHAKE,"starting TLS handshake on fd "TOR_SOCKET_T_FORMAT, @@ -1692,7 +1699,8 @@ connection_tls_continue_handshake(or_connection_t *conn) switch (result) { CASE_TOR_TLS_ERROR_ANY: - log_info(LD_OR,"tls error [%s]. breaking connection.", + conn->tls_error = result; + log_info(LD_OR,"tls error [%s]. breaking connection.", tor_tls_err_to_string(result)); return -1; case TOR_TLS_DONE: @@ -1724,6 +1732,7 @@ connection_tls_continue_handshake(or_connection_t *conn) log_debug(LD_OR,"wanted read"); return 0; case TOR_TLS_CLOSE: + conn->tls_error = result; log_info(LD_OR,"tls closed. breaking connection."); return -1; } @@ -1778,18 +1787,15 @@ connection_or_check_valid_tls_handshake(or_connection_t *conn, crypto_pk_t *identity_rcvd=NULL; const or_options_t *options = get_options(); int severity = server_mode(options) ? LOG_PROTOCOL_WARN : LOG_WARN; - const char *safe_address = - started_here ? conn->base_.address : - safe_str_client(conn->base_.address); const char *conn_type = started_here ? "outgoing" : "incoming"; int has_cert = 0; check_no_tls_errors(); has_cert = tor_tls_peer_has_cert(conn->tls); if (started_here && !has_cert) { - log_info(LD_HANDSHAKE,"Tried connecting to router at %s:%d, but it didn't " + log_info(LD_HANDSHAKE,"Tried connecting to router at %s, but it didn't " "send a cert! Closing.", - safe_address, conn->base_.port); + connection_describe_peer(TO_CONN(conn))); return -1; } else if (!has_cert) { log_debug(LD_HANDSHAKE,"Got incoming connection with no certificate. " @@ -1801,9 +1807,9 @@ connection_or_check_valid_tls_handshake(or_connection_t *conn, int v = tor_tls_verify(started_here?severity:LOG_INFO, conn->tls, &identity_rcvd); if (started_here && v<0) { - log_fn(severity,LD_HANDSHAKE,"Tried connecting to router at %s:%d: It" + log_fn(severity,LD_HANDSHAKE,"Tried connecting to router at %s: It" " has a cert but it's invalid. Closing.", - safe_address, conn->base_.port); + connection_describe_peer(TO_CONN(conn))); return -1; } else if (v<0) { log_info(LD_HANDSHAKE,"Incoming connection gave us an invalid cert " @@ -1811,7 +1817,8 @@ connection_or_check_valid_tls_handshake(or_connection_t *conn, } else { log_debug(LD_HANDSHAKE, "The certificate seems to be valid on %s connection " - "with %s:%d", conn_type, safe_address, conn->base_.port); + "with %s", conn_type, + connection_describe_peer(TO_CONN(conn))); } check_no_tls_errors(); } @@ -1883,9 +1890,9 @@ connection_or_client_learned_peer_id(or_connection_t *conn, const int expected_ed_key = ! ed25519_public_key_is_zero(&chan->ed25519_identity); - log_info(LD_HANDSHAKE, "learned peer id for %p (%s): %s, %s", + log_info(LD_HANDSHAKE, "learned peer id for %s at %p: %s, %s", + connection_describe(TO_CONN(conn)), conn, - safe_str_client(conn->base_.address), hex_str((const char*)rsa_peer_id, DIGEST_LEN), ed25519_fmt(ed_peer_id)); @@ -1899,9 +1906,9 @@ connection_or_client_learned_peer_id(or_connection_t *conn, conn->nickname[0] = '$'; base16_encode(conn->nickname+1, HEX_DIGEST_LEN+1, conn->identity_digest, DIGEST_LEN); - log_info(LD_HANDSHAKE, "Connected to router %s at %s:%d without knowing " - "its key. Hoping for the best.", - conn->nickname, conn->base_.address, conn->base_.port); + log_info(LD_HANDSHAKE, "Connected to router at %s without knowing " + "its key. Hoping for the best.", + connection_describe_peer(TO_CONN(conn))); /* if it's a bridge and we didn't know its identity fingerprint, now * we do -- remember it for future attempts. */ learned_router_identity(&conn->base_.addr, conn->base_.port, @@ -1975,9 +1982,9 @@ connection_or_client_learned_peer_id(or_connection_t *conn, } log_fn(severity, LD_HANDSHAKE, - "Tried connecting to router at %s:%d, but RSA + ed25519 identity " + "Tried connecting to router at %s, but RSA + ed25519 identity " "keys were not as expected: wanted %s + %s but got %s + %s.%s", - conn->base_.address, conn->base_.port, + connection_describe_peer(TO_CONN(conn)), expected_rsa, expected_ed, seen_rsa, seen_ed, extra_log); /* Tell the new guard API about the channel failure */ @@ -2004,9 +2011,14 @@ connection_or_client_learned_peer_id(or_connection_t *conn, /* If we learned an identity for this connection, then we might have * just discovered it to be canonical. */ connection_or_check_canonicity(conn, conn->handshake_state->started_here); + if (conn->tls) + tor_tls_set_logged_address(conn->tls, + connection_describe_peer(TO_CONN(conn))); } if (authdir_mode_tests_reachability(options)) { + // We don't want to use canonical_orport here -- we want the address + // that we really used. dirserv_orconn_tls_done(&conn->base_.addr, conn->base_.port, (const char*)rsa_peer_id, ed_peer_id); } @@ -2049,11 +2061,10 @@ connection_tls_finish_handshake(or_connection_t *conn) tor_assert(!started_here); - log_debug(LD_HANDSHAKE,"%s tls handshake on %p with %s done, using " + log_debug(LD_HANDSHAKE,"%s tls handshake on %s done, using " "ciphersuite %s. verifying.", started_here?"outgoing":"incoming", - conn, - safe_str_client(conn->base_.address), + connection_describe_peer(TO_CONN(conn)), tor_tls_get_ciphersuite_name(conn->tls)); if (connection_or_check_valid_tls_handshake(conn, started_here, @@ -2485,11 +2496,9 @@ connection_or_send_netinfo,(or_connection_t *conn)) netinfo_cell_set_timestamp(netinfo_cell, (uint32_t)now); /* Their address. */ - const tor_addr_t *remote_tor_addr = - !tor_addr_is_null(&conn->real_addr) ? &conn->real_addr : &conn->base_.addr; - /* We use &conn->real_addr below, unless it hasn't yet been set. If it - * hasn't yet been set, we know that base_.addr hasn't been tampered with - * yet either. */ + const tor_addr_t *remote_tor_addr = &TO_CONN(conn)->addr; + /* We can safely use TO_CONN(conn)->addr here, since we no longer replace + * it with a canonical address. */ netinfo_addr_t *their_addr = netinfo_addr_from_tor_addr(remote_tor_addr); netinfo_cell_set_other_addr(netinfo_cell, their_addr); @@ -2499,14 +2508,11 @@ connection_or_send_netinfo,(or_connection_t *conn)) * is an outgoing connection, act like a normal client and omit it. */ if ((public_server_mode(get_options()) || !conn->is_outgoing) && (me = router_get_my_routerinfo())) { - tor_addr_t my_addr; - tor_addr_from_ipv4h(&my_addr, me->addr); - uint8_t n_my_addrs = 1 + !tor_addr_is_null(&me->ipv6_addr); netinfo_cell_set_n_my_addrs(netinfo_cell, n_my_addrs); netinfo_cell_add_my_addrs(netinfo_cell, - netinfo_addr_from_tor_addr(&my_addr)); + netinfo_addr_from_tor_addr(&me->ipv4_addr)); if (!tor_addr_is_null(&me->ipv6_addr)) { netinfo_cell_add_my_addrs(netinfo_cell, diff --git a/src/core/or/connection_or.h b/src/core/or/connection_or.h index e9ace56ab4..fe81b5c5e1 100644 --- a/src/core/or/connection_or.h +++ b/src/core/or/connection_or.h @@ -16,6 +16,7 @@ struct ed25519_public_key_t; struct ed25519_keypair_t; or_connection_t *TO_OR_CONN(connection_t *); +const or_connection_t *CONST_TO_OR_CONN(const connection_t *); #include "core/or/orconn_event.h" diff --git a/src/core/or/connection_st.h b/src/core/or/connection_st.h index 55d94d9451..9cc06d1ef3 100644 --- a/src/core/or/connection_st.h +++ b/src/core/or/connection_st.h @@ -109,10 +109,39 @@ struct connection_t { int socket_family; /**< Address family of this connection's socket. Usually * AF_INET, but it can also be AF_UNIX, or AF_INET6 */ - tor_addr_t addr; /**< IP that socket "s" is directly connected to; - * may be the IP address for a proxy or pluggable transport, - * see "address" for the address of the final destination. - */ + /** + * IP address on the internet of this connection's peer, usually. + * + * This address may come from several sources. If this is an outbound + * connection, it is the address we are trying to connect to--either + * directly through `s`, or via a proxy. (If we used a proxy, then + * `getpeername(s)` will not give this address.) + * + * For incoming connections, this field is the address we got from + * getpeername() or accept(), as updated by any proxy that we + * are using (for example, an ExtORPort proxy). + * + * For listeners, this is the address we are trying to bind to. + * + * If this connection is using a unix socket, then this address is a null + * address, and the real address is in the `address` field. + * + * If this connection represents a request made somewhere other than via + * TCP (for example, a UDP dns request, or a controller resolve request), + * then this address is the address that originated the request. + * + * TECHNICAL DEBT: + * + * There are a few places in the code that modify this address, + * or use it in other ways that we don't currently like. Please don't add + * any more! + * + * The misuses of this field include: + * * Setting it on linked connections, possibly. + * * Updating it based on the Forwarded-For header-- Forwarded-For is + * set by a proxy, but not a local trusted proxy. + **/ + tor_addr_t addr; uint16_t port; /**< If non-zero, port that socket "s" is directly connected * to; may be the port for a proxy or pluggable transport, * see "address" for the port at the final destination. */ @@ -122,12 +151,18 @@ struct connection_t { * marked.) */ const char *marked_for_close_file; /**< For debugging: in which file were * we marked for close? */ - char *address; /**< FQDN (or IP) and port of the final destination for this - * connection; this is always the remote address, it is - * passed to a proxy or pluggable transport if one in use. - * See "addr" and "port" for the address that socket "s" is - * directly connected to. - * strdup into this, because free_connection() frees it. */ + /** + * String address of the peer of this connection. + * + * TECHNICAL DEBT: + * + * This field serves many purposes, and they're not all pretty. In addition + * to describing the peer we're connected to, it can also hold: + * + * * An address we're trying to resolve (as an exit). + * * A unix address we're trying to bind to (as a listener). + **/ + char *address; /** Another connection that's connected to this one in lieu of a socket. */ struct connection_t *linked_conn; diff --git a/src/core/or/cpath_build_state_st.h b/src/core/or/cpath_build_state_st.h index ee9a0d972c..eb8e97edc5 100644 --- a/src/core/or/cpath_build_state_st.h +++ b/src/core/or/cpath_build_state_st.h @@ -24,6 +24,8 @@ struct cpath_build_state_t { unsigned int need_capacity : 1; /** Whether the last hop was picked with exiting in mind. */ unsigned int is_internal : 1; + /** Is this an IPv6 ORPort self-testing circuit? */ + unsigned int is_ipv6_selftest : 1; /** Did we pick this as a one-hop tunnel (not safe for other streams)? * These are for encrypted dir conns that exit to this router, not * for arbitrary exits from the circuit. */ diff --git a/src/core/or/crypt_path.c b/src/core/or/crypt_path.c index 8f41540848..e1bbd81251 100644 --- a/src/core/or/crypt_path.c +++ b/src/core/or/crypt_path.c @@ -30,6 +30,7 @@ #include "core/crypto/onion_crypto.h" #include "core/or/circuitbuild.h" #include "core/or/circuitlist.h" +#include "core/or/extendinfo.h" #include "lib/crypt_ops/crypto_dh.h" #include "lib/crypt_ops/crypto_util.h" @@ -259,4 +260,3 @@ cpath_get_n_hops(crypt_path_t **head_ptr) } #endif /* defined(TOR_UNIT_TESTS) */ - diff --git a/src/core/or/dos.c b/src/core/or/dos.c index 5f99280030..41bf303ffe 100644 --- a/src/core/or/dos.c +++ b/src/core/or/dos.c @@ -584,7 +584,7 @@ dos_geoip_entry_about_to_free(const clientmap_entry_t *geoip_ent) SMARTLIST_FOREACH_BEGIN(get_connection_array(), connection_t *, conn) { if (conn->type == CONN_TYPE_OR) { or_connection_t *or_conn = TO_OR_CONN(conn); - if (!tor_addr_compare(&geoip_ent->addr, &or_conn->real_addr, + if (!tor_addr_compare(&geoip_ent->addr, &TO_CONN(or_conn)->addr, CMP_EXACT)) { or_conn->tracked_for_dos_mitigation = 0; } @@ -696,12 +696,12 @@ dos_new_client_conn(or_connection_t *or_conn, const char *transport_name) * reason to do so is because network reentry is possible where a client * connection comes from an Exit node. Even when we'll fix reentry, this is * a robust defense to keep in place. */ - if (nodelist_probably_contains_address(&or_conn->real_addr)) { + if (nodelist_probably_contains_address(&TO_CONN(or_conn)->addr)) { goto end; } /* We are only interested in client connection from the geoip cache. */ - entry = geoip_lookup_client(&or_conn->real_addr, transport_name, + entry = geoip_lookup_client(&TO_CONN(or_conn)->addr, transport_name, GEOIP_CLIENT_CONNECT); if (BUG(entry == NULL)) { /* Should never happen because we note down the address in the geoip @@ -712,7 +712,7 @@ dos_new_client_conn(or_connection_t *or_conn, const char *transport_name) entry->dos_stats.concurrent_count++; or_conn->tracked_for_dos_mitigation = 1; log_debug(LD_DOS, "Client address %s has now %u concurrent connections.", - fmt_addr(&or_conn->real_addr), + fmt_addr(&TO_CONN(or_conn)->addr), entry->dos_stats.concurrent_count); end: @@ -735,7 +735,7 @@ dos_close_client_conn(const or_connection_t *or_conn) } /* We are only interested in client connection from the geoip cache. */ - entry = geoip_lookup_client(&or_conn->real_addr, NULL, + entry = geoip_lookup_client(&TO_CONN(or_conn)->addr, NULL, GEOIP_CLIENT_CONNECT); if (entry == NULL) { /* This can happen because we can close a connection before the channel @@ -753,7 +753,7 @@ dos_close_client_conn(const or_connection_t *or_conn) entry->dos_stats.concurrent_count--; log_debug(LD_DOS, "Client address %s has lost a connection. Concurrent " "connections are now at %u", - fmt_addr(&or_conn->real_addr), + fmt_addr(&TO_CONN(or_conn)->addr), entry->dos_stats.concurrent_count); end: diff --git a/src/core/or/extend_info_st.h b/src/core/or/extend_info_st.h index a66ce24cfa..757c6a1771 100644 --- a/src/core/or/extend_info_st.h +++ b/src/core/or/extend_info_st.h @@ -15,9 +15,14 @@ #include "lib/crypt_ops/crypto_curve25519.h" #include "lib/crypt_ops/crypto_ed25519.h" +/** Largest number of addresses we handle in an extend_info. + * + * More are permitted in an EXTEND cell, but we won't handle them. */ +#define EXTEND_INFO_MAX_ADDRS 2 + /** Information on router used when extending a circuit. We don't need a * full routerinfo_t to extend: we only need addr:port:keyid to build an OR - * connection, and onion_key to create the onionskin. Note that for onehop + * connection, and onion_key to create the onionskin. Note that for one-hop * general-purpose tunnels, the onion_key is NULL. */ struct extend_info_t { char nickname[MAX_HEX_NICKNAME_LEN+1]; /**< This router's nickname for @@ -26,9 +31,12 @@ struct extend_info_t { char identity_digest[DIGEST_LEN]; /** Ed25519 identity for this router, if any. */ ed25519_public_key_t ed_identity; - uint16_t port; /**< OR port. */ - tor_addr_t addr; /**< IP address. */ - crypto_pk_t *onion_key; /**< Current onionskin key. */ + /** IP/Port values for this hop's ORPort(s). Any unused values are set + * to a null address. */ + tor_addr_port_t orports[EXTEND_INFO_MAX_ADDRS]; + /** TAP onion key for this hop. */ + crypto_pk_t *onion_key; + /** Ntor onion key for this hop. */ curve25519_public_key_t curve25519_onion_key; }; diff --git a/src/core/or/extendinfo.c b/src/core/or/extendinfo.c new file mode 100644 index 0000000000..ffc88295cf --- /dev/null +++ b/src/core/or/extendinfo.c @@ -0,0 +1,330 @@ +/* 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 extendinfo.c + * @brief Functions for creating and using extend_info_t objects. + * + * An extend_info_t is the information we hold about a relay in order to + * extend a circuit to it. + **/ + +#include "core/or/or.h" +#include "core/or/extendinfo.h" + +#include "app/config/config.h" +#include "core/or/policies.h" +#include "feature/nodelist/describe.h" +#include "feature/nodelist/nodelist.h" +#include "feature/relay/router.h" +#include "feature/relay/routermode.h" +#include "lib/crypt_ops/crypto_rand.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" + +/** Allocate a new extend_info object based on the various arguments. */ +extend_info_t * +extend_info_new(const char *nickname, + const char *rsa_id_digest, + const ed25519_public_key_t *ed_id, + crypto_pk_t *onion_key, + const curve25519_public_key_t *ntor_key, + const tor_addr_t *addr, uint16_t port) +{ + extend_info_t *info = tor_malloc_zero(sizeof(extend_info_t)); + if (rsa_id_digest) + memcpy(info->identity_digest, rsa_id_digest, DIGEST_LEN); + if (ed_id && !ed25519_public_key_is_zero(ed_id)) + memcpy(&info->ed_identity, ed_id, sizeof(ed25519_public_key_t)); + if (nickname) + strlcpy(info->nickname, nickname, sizeof(info->nickname)); + if (onion_key) + info->onion_key = crypto_pk_dup_key(onion_key); + if (ntor_key) + memcpy(&info->curve25519_onion_key, ntor_key, + sizeof(curve25519_public_key_t)); + for (int i = 0; i < EXTEND_INFO_MAX_ADDRS; ++i) { + tor_addr_make_unspec(&info->orports[i].addr); + } + + if (addr) { + extend_info_add_orport(info, addr, port); + } + return info; +} + +/** + * Add another address:port pair to a given extend_info_t, if there is + * room. Return 0 on success, -1 on failure. + **/ +int +extend_info_add_orport(extend_info_t *ei, + const tor_addr_t *addr, + uint16_t port) +{ + for (int i = 0; i < EXTEND_INFO_MAX_ADDRS; ++i) { + if (tor_addr_is_unspec(&ei->orports[i].addr)) { + tor_addr_copy(&ei->orports[i].addr, addr); + ei->orports[i].port = port; + return 0; + } + } + return -1; +} + +/** Allocate and return a new extend_info that can be used to build a + * circuit to or through the node <b>node</b>. Use the primary address + * of the node (i.e. its IPv4 address) unless + * <b>for_direct_connect</b> is true, in which case the preferred + * address is used instead. May return NULL if there is not enough + * info about <b>node</b> to extend to it--for example, if the preferred + * routerinfo_t or microdesc_t is missing, or if for_direct_connect is + * true and none of the node's addresses is allowed by tor's firewall + * and IP version config. + **/ +extend_info_t * +extend_info_from_node(const node_t *node, int for_direct_connect) +{ + crypto_pk_t *rsa_pubkey = NULL; + extend_info_t *info = NULL; + tor_addr_port_t ap; + int valid_addr = 0; + + if (!node_has_preferred_descriptor(node, for_direct_connect)) { + return NULL; + } + + /* Choose a preferred address first, but fall back to an allowed address. */ + if (for_direct_connect) + fascist_firewall_choose_address_node(node, FIREWALL_OR_CONNECTION, 0, &ap); + else { + node_get_prim_orport(node, &ap); + } + valid_addr = tor_addr_port_is_valid_ap(&ap, 0); + + if (valid_addr) + log_debug(LD_CIRC, "using %s for %s", + fmt_addrport(&ap.addr, ap.port), + node->ri ? node->ri->nickname : node->rs->nickname); + else + log_warn(LD_CIRC, "Could not choose valid address for %s", + node->ri ? node->ri->nickname : node->rs->nickname); + + /* Every node we connect or extend to must support ntor */ + if (!node_has_curve25519_onion_key(node)) { + log_fn(LOG_PROTOCOL_WARN, LD_CIRC, + "Attempted to create extend_info for a node that does not support " + "ntor: %s", node_describe(node)); + return NULL; + } + + const ed25519_public_key_t *ed_pubkey = NULL; + + /* Don't send the ed25519 pubkey unless the target node actually supports + * authenticating with it. */ + if (node_supports_ed25519_link_authentication(node, 0)) { + log_info(LD_CIRC, "Including Ed25519 ID for %s", node_describe(node)); + ed_pubkey = node_get_ed25519_id(node); + } else if (node_get_ed25519_id(node)) { + log_info(LD_CIRC, "Not including the ed25519 ID for %s, since it won't " + "be able to authenticate it.", + node_describe(node)); + } + + /* Retrieve the curve25519 pubkey. */ + const curve25519_public_key_t *curve_pubkey = + node_get_curve25519_onion_key(node); + rsa_pubkey = node_get_rsa_onion_key(node); + + if (valid_addr && node->ri) { + info = extend_info_new(node->ri->nickname, + node->identity, + ed_pubkey, + rsa_pubkey, + curve_pubkey, + &ap.addr, + ap.port); + } else if (valid_addr && node->rs && node->md) { + info = extend_info_new(node->rs->nickname, + node->identity, + ed_pubkey, + rsa_pubkey, + curve_pubkey, + &ap.addr, + ap.port); + } + + crypto_pk_free(rsa_pubkey); + return info; +} + +/** Release storage held by an extend_info_t struct. */ +void +extend_info_free_(extend_info_t *info) +{ + if (!info) + return; + crypto_pk_free(info->onion_key); + tor_free(info); +} + +/** Allocate and return a new extend_info_t with the same contents as + * <b>info</b>. */ +extend_info_t * +extend_info_dup(extend_info_t *info) +{ + extend_info_t *newinfo; + tor_assert(info); + newinfo = tor_malloc(sizeof(extend_info_t)); + memcpy(newinfo, info, sizeof(extend_info_t)); + if (info->onion_key) + newinfo->onion_key = crypto_pk_dup_key(info->onion_key); + else + newinfo->onion_key = NULL; + return newinfo; +} + +/* Does ei have a valid TAP key? */ +int +extend_info_supports_tap(const extend_info_t* ei) +{ + tor_assert(ei); + /* Valid TAP keys are not NULL */ + return ei->onion_key != NULL; +} + +/* Does ei have a valid ntor key? */ +int +extend_info_supports_ntor(const extend_info_t* ei) +{ + tor_assert(ei); + /* Valid ntor keys have at least one non-zero byte */ + return !fast_mem_is_zero( + (const char*)ei->curve25519_onion_key.public_key, + CURVE25519_PUBKEY_LEN); +} + +/* Does ei have an onion key which it would prefer to use? + * Currently, we prefer ntor keys*/ +int +extend_info_has_preferred_onion_key(const extend_info_t* ei) +{ + tor_assert(ei); + return extend_info_supports_ntor(ei); +} + +/** Return true iff the given address can be used to extend to. */ +int +extend_info_addr_is_allowed(const tor_addr_t *addr) +{ + tor_assert(addr); + + /* Check if we have a private address and if we can extend to it. */ + if ((tor_addr_is_internal(addr, 0) || tor_addr_is_multicast(addr)) && + !get_options()->ExtendAllowPrivateAddresses) { + goto disallow; + } + /* Allowed! */ + return 1; + disallow: + return 0; +} + +/** + * Return true if @a addr : @a port is a listed ORPort in @a ei. + **/ +bool +extend_info_has_orport(const extend_info_t *ei, + const tor_addr_t *addr, uint16_t port) +{ + IF_BUG_ONCE(ei == NULL) { + return false; + } + + for (int i = 0; i < EXTEND_INFO_MAX_ADDRS; ++i) { + const tor_addr_port_t *ei_ap = &ei->orports[i]; + if (tor_addr_eq(&ei_ap->addr, addr) && ei_ap->port == port) + return true; + } + return false; +} + +/** + * If the extend_info @a ei has an orport of the chosen family, then return + * that orport. Otherwise, return NULL. + **/ +const tor_addr_port_t * +extend_info_get_orport(const extend_info_t *ei, int family) +{ + for (int i = 0; i < EXTEND_INFO_MAX_ADDRS; ++i) { + if (tor_addr_is_unspec(&ei->orports[i].addr)) + continue; + if (tor_addr_family(&ei->orports[i].addr) == family) + return &ei->orports[i]; + } + return NULL; +} + +/** + * Chose an addr_port_t within @a ei to connect to. + **/ +const tor_addr_port_t * +extend_info_pick_orport(const extend_info_t *ei) +{ + IF_BUG_ONCE(!ei) { + return NULL; + } + const or_options_t *options = get_options(); + if (!server_mode(options)) { + // If we aren't a server, just pick the first address we built into + // this extendinfo. + return &ei->orports[0]; + } + + const bool ipv6_ok = router_can_extend_over_ipv6(options); + + // Use 'usable' to collect the usable orports, then pick one. + const tor_addr_port_t *usable[EXTEND_INFO_MAX_ADDRS]; + int n_usable = 0; + for (int i = 0; i < EXTEND_INFO_MAX_ADDRS; ++i) { + const tor_addr_port_t *a = &ei->orports[i]; + const int family = tor_addr_family(&a->addr); + if (family == AF_INET || (ipv6_ok && family == AF_INET6)) { + usable[n_usable++] = a; + } + } + + if (n_usable == 0) { + // Need to bail out early, since nothing will work. + return NULL; + } + + crypto_fast_rng_t *rng = get_thread_fast_rng(); + const int idx = crypto_fast_rng_get_uint(rng, n_usable); + + return usable[idx]; +} + +/** + * Return true if any orport address in @a ei is an internal address. + **/ +bool +extend_info_any_orport_addr_is_internal(const extend_info_t *ei) +{ + IF_BUG_ONCE(ei == NULL) { + return false; + } + + for (int i = 0; i < EXTEND_INFO_MAX_ADDRS; ++i) { + if (! tor_addr_is_unspec(&ei->orports[i].addr) && + tor_addr_is_internal(&ei->orports[i].addr, 0)) + return true; + } + return false; +} diff --git a/src/core/or/extendinfo.h b/src/core/or/extendinfo.h new file mode 100644 index 0000000000..0049dd0189 --- /dev/null +++ b/src/core/or/extendinfo.h @@ -0,0 +1,40 @@ +/* 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 extendinfo.h + * @brief Header for core/or/extendinfo.c + **/ + +#ifndef TOR_CORE_OR_EXTENDINFO_H +#define TOR_CORE_OR_EXTENDINFO_H + +extend_info_t *extend_info_new(const char *nickname, + const char *rsa_id_digest, + const struct ed25519_public_key_t *ed_id, + crypto_pk_t *onion_key, + const struct curve25519_public_key_t *ntor_key, + const tor_addr_t *addr, uint16_t port); +extend_info_t *extend_info_from_node(const node_t *r, int for_direct_connect); +extend_info_t *extend_info_dup(extend_info_t *info); +void extend_info_free_(extend_info_t *info); +#define extend_info_free(info) \ + FREE_AND_NULL(extend_info_t, extend_info_free_, (info)) +int extend_info_addr_is_allowed(const tor_addr_t *addr); +int extend_info_supports_tap(const extend_info_t* ei); +int extend_info_supports_ntor(const extend_info_t* ei); +int extend_info_has_preferred_onion_key(const extend_info_t* ei); +bool extend_info_has_orport(const extend_info_t *ei, + const tor_addr_t *addr, uint16_t port); +int extend_info_add_orport(extend_info_t *ei, + const tor_addr_t *addr, + uint16_t port); +const tor_addr_port_t *extend_info_get_orport(const extend_info_t *ei, + int family); +const tor_addr_port_t *extend_info_pick_orport(const extend_info_t *ei); +bool extend_info_any_orport_addr_is_internal(const extend_info_t *ei); + +#endif /* !defined(TOR_CORE_OR_EXTENDINFO_H) */ diff --git a/src/core/or/include.am b/src/core/or/include.am index 3626e76bed..9ff92adbde 100644 --- a/src/core/or/include.am +++ b/src/core/or/include.am @@ -18,6 +18,7 @@ LIBTOR_APP_A_SOURCES += \ src/core/or/connection_edge.c \ src/core/or/connection_or.c \ src/core/or/dos.c \ + src/core/or/extendinfo.c \ src/core/or/onion.c \ src/core/or/ocirc_event.c \ src/core/or/or_periodic.c \ @@ -64,6 +65,7 @@ noinst_HEADERS += \ src/core/or/destroy_cell_queue_st.h \ src/core/or/dos.h \ src/core/or/edge_connection_st.h \ + src/core/or/extendinfo.h \ src/core/or/half_edge_st.h \ src/core/or/entry_connection_st.h \ src/core/or/entry_port_cfg_st.h \ @@ -94,3 +96,10 @@ noinst_HEADERS += \ src/core/or/tor_version_st.h \ src/core/or/var_cell_st.h \ src/core/or/versions.h + +if USE_TRACING_INSTRUMENTATION_LTTNG +LIBTOR_APP_A_SOURCES += \ + src/core/or/trace_probes_circuit.c +noinst_HEADERS += \ + src/core/or/trace_probes_circuit.h +endif diff --git a/src/core/or/lttng_circuit.inc b/src/core/or/lttng_circuit.inc new file mode 100644 index 0000000000..fc3e175c8a --- /dev/null +++ b/src/core/or/lttng_circuit.inc @@ -0,0 +1,322 @@ +/* Copyright (c) 2020, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file lttng_circuit.inc + * \brief LTTng tracing probe declaration for the circuit subsystem. It is in +* this .inc file due to the non C standard syntax and the way we guard +* the header with the LTTng specific TRACEPOINT_HEADER_MULTI_READ. + **/ + +#include "orconfig.h" + +/* We only build the following if LTTng instrumentation has been enabled. */ +#ifdef USE_TRACING_INSTRUMENTATION_LTTNG + +/* The following defines are LTTng-UST specific. */ +#undef TRACEPOINT_PROVIDER +#define TRACEPOINT_PROVIDER tor_circuit + +#undef TRACEPOINT_INCLUDE +#define TRACEPOINT_INCLUDE "./src/core/or/lttng_circuit.inc" + +#if !defined(LTTNG_CIRCUIT_INC) || defined(TRACEPOINT_HEADER_MULTI_READ) +#define LTTNG_CIRCUIT_INC + +#include <lttng/tracepoint.h> + +/* + * Circuit Purposes + * + * The following defines an enumeration of all possible circuit purpose so + * they appear in the trace with the define name (first parameter of + * ctf_enum_value) instead of the numerical value. + */ +TRACEPOINT_ENUM(tor_circuit, purpose, + TP_ENUM_VALUES( + /* Initializing. */ + ctf_enum_value("<UNSET>", 0) + + /* OR Side. */ + ctf_enum_value("OR", CIRCUIT_PURPOSE_OR) + ctf_enum_value("OR_INTRO_POINT", CIRCUIT_PURPOSE_INTRO_POINT) + ctf_enum_value("OR_REND_POINT_WAITING", + CIRCUIT_PURPOSE_REND_POINT_WAITING) + ctf_enum_value("OR_REND_ESTABLISHED", CIRCUIT_PURPOSE_REND_ESTABLISHED) + + /* Client Side. */ + ctf_enum_value("C_GENERAL", CIRCUIT_PURPOSE_C_GENERAL) + ctf_enum_value("C_INTRODUCING", CIRCUIT_PURPOSE_C_INTRODUCING) + ctf_enum_value("C_INTRODUCE_ACK_WAIT", + CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT) + ctf_enum_value("C_INTRODUCE_ACKED", CIRCUIT_PURPOSE_C_INTRODUCE_ACKED) + ctf_enum_value("C_ESTABLISH_REND", CIRCUIT_PURPOSE_C_ESTABLISH_REND) + ctf_enum_value("C_REND_READY", CIRCUIT_PURPOSE_C_REND_READY) + ctf_enum_value("C_REND_READY_INTRO_ACKED", + CIRCUIT_PURPOSE_C_REND_READY_INTRO_ACKED) + ctf_enum_value("C_REND_JOINED", CIRCUIT_PURPOSE_C_REND_JOINED) + ctf_enum_value("C_HSDIR_GET", CIRCUIT_PURPOSE_C_HSDIR_GET) + + /* CBT and Padding. */ + ctf_enum_value("C_MEASURE_TIMEOUT", CIRCUIT_PURPOSE_C_MEASURE_TIMEOUT) + ctf_enum_value("C_CIRCUIT_PADDING", CIRCUIT_PURPOSE_C_CIRCUIT_PADDING) + + /* Service Side. */ + ctf_enum_value("S_ESTABLISH_INTRO", CIRCUIT_PURPOSE_S_ESTABLISH_INTRO) + ctf_enum_value("S_INTRO", CIRCUIT_PURPOSE_S_INTRO) + ctf_enum_value("S_CONNECT_REND", CIRCUIT_PURPOSE_S_CONNECT_REND) + ctf_enum_value("S_REND_JOINED", CIRCUIT_PURPOSE_S_REND_JOINED) + ctf_enum_value("S_HSDIR_POST", CIRCUIT_PURPOSE_S_HSDIR_POST) + + /* Misc. */ + ctf_enum_value("TESTING", CIRCUIT_PURPOSE_TESTING) + ctf_enum_value("CONTROLER", CIRCUIT_PURPOSE_CONTROLLER) + ctf_enum_value("PATH_BIAS_TESTING", CIRCUIT_PURPOSE_PATH_BIAS_TESTING) + + /* VanGuard */ + ctf_enum_value("HS_VANGUARDS", CIRCUIT_PURPOSE_HS_VANGUARDS) + ) +) + +/* + * Circuit End Reasons + * + * The following defines an enumeration of all possible circuit end reasons so + * they appear in the trace with the define name (first parameter of + * ctf_enum_value) instead of the numerical value. + */ +TRACEPOINT_ENUM(tor_circuit, end_reason, + TP_ENUM_VALUES( + /* Local reasons. */ + ctf_enum_value("IP_NOW_REDUNDANT", END_CIRC_REASON_IP_NOW_REDUNDANT) + ctf_enum_value("MEASUREMENT_EXPIRED", END_CIRC_REASON_MEASUREMENT_EXPIRED) + ctf_enum_value("REASON_NOPATH", END_CIRC_REASON_NOPATH) + ctf_enum_value("AT_ORIGIN", END_CIRC_AT_ORIGIN) + ctf_enum_value("NONE", END_CIRC_REASON_NONE) + ctf_enum_value("TORPROTOCOL", END_CIRC_REASON_TORPROTOCOL) + ctf_enum_value("INTERNAL", END_CIRC_REASON_INTERNAL) + ctf_enum_value("REQUESTED", END_CIRC_REASON_REQUESTED) + ctf_enum_value("HIBERNATING", END_CIRC_REASON_HIBERNATING) + ctf_enum_value("RESOURCELIMIT", END_CIRC_REASON_RESOURCELIMIT) + ctf_enum_value("CONNECTFAILED", END_CIRC_REASON_CONNECTFAILED) + ctf_enum_value("OR_IDENTITY", END_CIRC_REASON_OR_IDENTITY) + ctf_enum_value("CHANNEL_CLOSED", END_CIRC_REASON_CHANNEL_CLOSED) + ctf_enum_value("FINISHED", END_CIRC_REASON_FINISHED) + ctf_enum_value("TIMEOUT", END_CIRC_REASON_TIMEOUT) + ctf_enum_value("DESTROYED", END_CIRC_REASON_DESTROYED) + ctf_enum_value("NOSUCHSERVICE", END_CIRC_REASON_NOSUCHSERVICE) + + /* Remote reasons. */ + ctf_enum_value("FLAG_REMOTE", END_CIRC_REASON_FLAG_REMOTE) + ctf_enum_value("REMOTE_TORPROTOCOL", + END_CIRC_REASON_FLAG_REMOTE | END_CIRC_REASON_TORPROTOCOL) + ctf_enum_value("REMOTE_INTERNAL", + END_CIRC_REASON_FLAG_REMOTE | END_CIRC_REASON_INTERNAL) + ctf_enum_value("REMOTE_REQUESTED", + END_CIRC_REASON_FLAG_REMOTE | END_CIRC_REASON_REQUESTED) + ctf_enum_value("REMOTE_HIBERNATING", + END_CIRC_REASON_FLAG_REMOTE | END_CIRC_REASON_HIBERNATING) + ctf_enum_value("REMOTE_RESOURCELIMIT", + END_CIRC_REASON_FLAG_REMOTE | END_CIRC_REASON_RESOURCELIMIT) + ctf_enum_value("REMOTE_CONNECTFAILED", + END_CIRC_REASON_FLAG_REMOTE | END_CIRC_REASON_CONNECTFAILED) + ctf_enum_value("REMOTE_OR_IDENTITY", + END_CIRC_REASON_FLAG_REMOTE | END_CIRC_REASON_OR_IDENTITY) + ctf_enum_value("REMOTE_CHANNEL_CLOSED", + END_CIRC_REASON_FLAG_REMOTE | END_CIRC_REASON_CHANNEL_CLOSED) + ctf_enum_value("REMOTE_FINISHED", + END_CIRC_REASON_FLAG_REMOTE | END_CIRC_REASON_FINISHED) + ctf_enum_value("REMOTE_TIMEOUT", + END_CIRC_REASON_FLAG_REMOTE | END_CIRC_REASON_TIMEOUT) + ctf_enum_value("REMOTE_DESTROYED", + END_CIRC_REASON_FLAG_REMOTE | END_CIRC_REASON_DESTROYED) + ctf_enum_value("REMOTE_NOSUCHSERVICE", + END_CIRC_REASON_FLAG_REMOTE | END_CIRC_REASON_NOSUCHSERVICE) + ) +) + +/* + * Circuit State + * + * The following defines an enumeration of all possible circuit state so they + * appear in the trace with the define name (first parameter of + * ctf_enum_value) instead of the numerical value. + */ +TRACEPOINT_ENUM(tor_circuit, state, + TP_ENUM_VALUES( + ctf_enum_value("BUILDING", CIRCUIT_STATE_BUILDING) + ctf_enum_value("ONIONSKIN_PENDING", CIRCUIT_STATE_ONIONSKIN_PENDING) + ctf_enum_value("CHAN_WAIT", CIRCUIT_STATE_CHAN_WAIT) + ctf_enum_value("GUARD_WAIT", CIRCUIT_STATE_GUARD_WAIT) + ctf_enum_value("OPEN", CIRCUIT_STATE_OPEN) + ) +) + +/* + * Event Class + * + * A tracepoint class is a class of tracepoints which share the same output + * event field definitions. They are then used by the + * TRACEPOINT_EVENT_INSTANCE() macro as a base field definition. + */ + +/* Class for origin circuit. */ +TRACEPOINT_EVENT_CLASS(tor_circuit, origin_circuit_t_class, + TP_ARGS(const origin_circuit_t *, circ), + TP_FIELDS( + ctf_integer(uint32_t, circ_id, circ->global_identifier) + ctf_enum(tor_circuit, purpose, int, purpose, TO_CIRCUIT(circ)->purpose) + ctf_enum(tor_circuit, state, int, state, TO_CIRCUIT(circ)->state) + ) +) + +/* Class for or circuit. */ +TRACEPOINT_EVENT_CLASS(tor_circuit, or_circuit_t_class, + TP_ARGS(const or_circuit_t *, circ), + TP_FIELDS( + ctf_enum(tor_circuit, purpose, int, purpose, TO_CIRCUIT(circ)->purpose) + ctf_enum(tor_circuit, state, int, state, TO_CIRCUIT(circ)->state) + ) +) + +/* + * Origin circuit events. + * + * Tracepoint use the origin_circuit_t object. + */ + +/* Tracepoint emitted when a new origin circuit has been created. */ +TRACEPOINT_EVENT_INSTANCE(tor_circuit, origin_circuit_t_class, new_origin, + TP_ARGS(const origin_circuit_t *, circ) +) + +/* Tracepoint emitted when an origin circuit has opened. */ +TRACEPOINT_EVENT_INSTANCE(tor_circuit, origin_circuit_t_class, opened, + TP_ARGS(const origin_circuit_t *, circ) +) + +/* Tracepoint emitted when an origin circuit has established. */ +TRACEPOINT_EVENT_INSTANCE(tor_circuit, origin_circuit_t_class, establish, + TP_ARGS(const origin_circuit_t *, circ) +) + +/* Tracepoint emitted when an origin circuit has been cannibalized. */ +TRACEPOINT_EVENT_INSTANCE(tor_circuit, origin_circuit_t_class, cannibalized, + TP_ARGS(const origin_circuit_t *, circ) +) + +/* Tracepoint emitted when an origin circuit has timed out. This is called + * when circuit_expire_building() as selected the circuit and is about to + * close it for timeout. */ +TRACEPOINT_EVENT_INSTANCE(tor_circuit, origin_circuit_t_class, timeout, + TP_ARGS(const origin_circuit_t *, circ) +) + +/* Tracepoint emitted when an origin circuit has timed out due to idleness. + * This is when the circuit is closed after MaxCircuitDirtiness. */ +TRACEPOINT_EVENT_INSTANCE(tor_circuit, origin_circuit_t_class, idle_timeout, + TP_ARGS(const origin_circuit_t *, circ) +) + +/* Tracepoint emitted when an origin circuit sends out its first onion skin. */ +TRACEPOINT_EVENT(tor_circuit, first_onion_skin, + TP_ARGS(const origin_circuit_t *, circ, const crypt_path_t *, hop), + TP_FIELDS( + ctf_integer(uint32_t, circ_id, circ->global_identifier) + ctf_enum(tor_circuit, purpose, int, purpose, TO_CIRCUIT(circ)->purpose) + ctf_enum(tor_circuit, state, int, state, TO_CIRCUIT(circ)->state) + ctf_array_hex(char, fingerprint, hop->extend_info->identity_digest, + DIGEST_LEN) + ) +) + +/* Tracepoint emitted when an origin circuit sends out an intermediate onion + * skin. */ +TRACEPOINT_EVENT(tor_circuit, intermediate_onion_skin, + TP_ARGS(const origin_circuit_t *, circ, const crypt_path_t *, hop), + TP_FIELDS( + ctf_integer(uint32_t, circ_id, circ->global_identifier) + ctf_enum(tor_circuit, purpose, int, purpose, TO_CIRCUIT(circ)->purpose) + ctf_enum(tor_circuit, state, int, state, TO_CIRCUIT(circ)->state) + ctf_array_hex(char, fingerprint, hop->extend_info->identity_digest, + DIGEST_LEN) + ) +) + +/* + * OR circuit events. + * + * Tracepoint use the or_circuit_t object. + */ + +/* Tracepoint emitted when a new or circuit has been created. */ +TRACEPOINT_EVENT_INSTANCE(tor_circuit, or_circuit_t_class, new_or, + TP_ARGS(const or_circuit_t *, circ) +) + +/* + * General circuit events. + * + * Tracepoint use the circuit_t object. + */ + +/* Tracepoint emitted when a circuit is freed. */ +TRACEPOINT_EVENT(tor_circuit, free, + TP_ARGS(const circuit_t *, circ), + TP_FIELDS( + ctf_integer(uint32_t, circ_id, + (CIRCUIT_IS_ORIGIN(circ) ? + TO_ORIGIN_CIRCUIT(circ)->global_identifier : 0)) + ctf_enum(tor_circuit, purpose, int, purpose, circ->purpose) + ctf_enum(tor_circuit, state, int, state, circ->state) + ) +) + +/* Tracepoint emitted when a circuit is marked for close. */ +TRACEPOINT_EVENT(tor_circuit, mark_for_close, + TP_ARGS(const circuit_t *, circ), + TP_FIELDS( + ctf_integer(uint32_t, circ_id, + (CIRCUIT_IS_ORIGIN(circ) ? + TO_ORIGIN_CIRCUIT(circ)->global_identifier : 0)) + ctf_enum(tor_circuit, purpose, int, purpose, circ->purpose) + ctf_enum(tor_circuit, state, int, state, circ->state) + ctf_enum(tor_circuit, end_reason, int, close_reason, + circ->marked_for_close_reason) + ctf_enum(tor_circuit, end_reason, int, orig_close_reason, + circ->marked_for_close_orig_reason) + ) +) + +/* Tracepoint emitted when a circuit changes purpose. */ +TRACEPOINT_EVENT(tor_circuit, change_purpose, + TP_ARGS(const circuit_t *, circ, int, old_purpose, int, new_purpose), + TP_FIELDS( + ctf_integer(uint32_t, circ_id, + (CIRCUIT_IS_ORIGIN(circ) ? + TO_ORIGIN_CIRCUIT(circ)->global_identifier : 0)) + ctf_enum(tor_circuit, state, int, state, circ->state) + ctf_enum(tor_circuit, purpose, int, purpose, old_purpose) + ctf_enum(tor_circuit, purpose, int, new, new_purpose) + ) +) + +/* Tracepoint emitted when a circuit changes state. */ +TRACEPOINT_EVENT(tor_circuit, change_state, + TP_ARGS(const circuit_t *, circ, int, old_state, int, new_state), + TP_FIELDS( + ctf_integer(uint32_t, circ_id, + (CIRCUIT_IS_ORIGIN(circ) ? + TO_ORIGIN_CIRCUIT(circ)->global_identifier : 0)) + ctf_enum(tor_circuit, purpose, int, purpose, circ->purpose) + ctf_enum(tor_circuit, state, int, old, old_state) + ctf_enum(tor_circuit, state, int, new, new_state) + ) +) + +#endif /* LTTNG_CIRCUIT_INC || TRACEPOINT_HEADER_MULTI_READ */ + +/* Must be included after the probes declaration. */ +#include <lttng/tracepoint-event.h> + +#endif /* USE_TRACING_INSTRUMENTATION_LTTNG */ diff --git a/src/core/or/onion.h b/src/core/or/onion.h index 256f0a3f31..600e67c3cd 100644 --- a/src/core/or/onion.h +++ b/src/core/or/onion.h @@ -48,8 +48,7 @@ typedef struct extend_cell_t { uint8_t cell_type; /** An IPv4 address and port for the node we're connecting to. */ tor_addr_port_t orport_ipv4; - /** An IPv6 address and port for the node we're connecting to. Not currently - * used. */ + /** An IPv6 address and port for the node we're connecting to. */ tor_addr_port_t orport_ipv6; /** Identity fingerprint of the node we're conecting to.*/ uint8_t node_id[DIGEST_LEN]; diff --git a/src/core/or/or.h b/src/core/or/or.h index 5b35cbe7f1..d80c41371e 100644 --- a/src/core/or/or.h +++ b/src/core/or/or.h @@ -220,7 +220,8 @@ struct curve25519_public_key_t; #define END_OR_CONN_REASON_IO_ERROR 7 /* read/write error */ #define END_OR_CONN_REASON_RESOURCE_LIMIT 8 /* sockets, buffers, etc */ #define END_OR_CONN_REASON_PT_MISSING 9 /* PT failed or not available */ -#define END_OR_CONN_REASON_MISC 10 +#define END_OR_CONN_REASON_TLS_ERROR 10 /* Problem in TLS protocol */ +#define END_OR_CONN_REASON_MISC 11 /* Reasons why we (or a remote OR) might close a stream. See tor-spec.txt for * documentation of these. The values must match. */ @@ -815,6 +816,18 @@ typedef struct protover_summary_flags_t { * accept EXTEND2 cells. This requires Relay=2. */ unsigned int supports_extend2_cells:1; + /** True iff this router has a version or protocol list that allows it to + * accept IPv6 connections. This requires Relay=2 or Relay=3. */ + unsigned int supports_accepting_ipv6_extends:1; + + /** True iff this router has a version or protocol list that allows it to + * initiate IPv6 connections. This requires Relay=3. */ + unsigned int supports_initiating_ipv6_extends:1; + + /** True iff this router has a version or protocol list that allows it to + * consider IPv6 connections canonical. This requires Relay=3. */ + unsigned int supports_canonical_ipv6_conns:1; + /** True iff this router has a protocol list that allows it to negotiate * ed25519 identity keys on a link handshake with us. This * requires LinkAuth=3. */ @@ -830,6 +843,10 @@ typedef struct protover_summary_flags_t { * the v3 protocol detailed in proposal 224. This requires HSIntro=4. */ unsigned int supports_ed25519_hs_intro : 1; + /** True iff this router has a protocol list that allows it to support the + * ESTABLISH_INTRO DoS cell extension. Requires HSIntro=5. */ + unsigned int supports_establish_intro_dos_extension : 1; + /** True iff this router has a protocol list that allows it to be an hidden * service directory supporting version 3 as seen in proposal 224. This * requires HSDir=2. */ @@ -841,12 +858,9 @@ typedef struct protover_summary_flags_t { unsigned int supports_v3_rendezvous_point: 1; /** True iff this router has a protocol list that allows clients to - * negotiate hs circuit setup padding. Requires Padding>=2. */ + * negotiate hs circuit setup padding. Requires Padding=2. */ unsigned int supports_hs_setup_padding : 1; - /** True iff this router has a protocol list that allows it to support the - * ESTABLISH_INTRO DoS cell extension. Requires HSIntro>=5. */ - unsigned int supports_establish_intro_dos_extension : 1; } protover_summary_flags_t; typedef struct routerinfo_t routerinfo_t; diff --git a/src/core/or/or_connection_st.h b/src/core/or/or_connection_st.h index 92956c2847..8e012a6b85 100644 --- a/src/core/or/or_connection_st.h +++ b/src/core/or/or_connection_st.h @@ -49,10 +49,19 @@ struct or_connection_t { /* Channel using this connection */ channel_tls_t *chan; - tor_addr_t real_addr; /**< The actual address that this connection came from - * or went to. The <b>addr</b> field is prone to - * getting overridden by the address from the router - * descriptor matching <b>identity_digest</b>. */ + /** + * The "canonical" address and port for this relay's ORPort, if this is + * a known relay. + * + * An ORPort is "canonical" in this sense only if it is the same ORPort + * that is listed for this identity in the consensus we have. + * + * This field may be set on outbound connections for _any_ relay, and on + * inbound connections after authentication. If we don't know the relay's + * identity, or if we don't have the relay's identity in our consensus, we + * leave this address as UNSPEC. + **/ + tor_addr_port_t canonical_orport; /** Should this connection be used for extending circuits to the server * matching the <b>identity_digest</b> field? Set to true if we're pretty diff --git a/src/core/or/policies.c b/src/core/or/policies.c index 2bf2dc7005..020eb0a152 100644 --- a/src/core/or/policies.c +++ b/src/core/or/policies.c @@ -389,19 +389,6 @@ addr_policy_permits_tor_addr(const tor_addr_t *addr, uint16_t port, } } -/** Return true iff <b> policy</b> (possibly NULL) will allow a connection to - * <b>addr</b>:<b>port</b>. <b>addr</b> is an IPv4 address given in host - * order. */ -/* XXXX deprecate when possible. */ -static int -addr_policy_permits_address(uint32_t addr, uint16_t port, - smartlist_t *policy) -{ - tor_addr_t a; - tor_addr_from_ipv4h(&a, addr); - return addr_policy_permits_tor_addr(&a, port, policy); -} - /** Return true iff we think our firewall will let us make a connection to * addr:port. * @@ -576,25 +563,6 @@ fascist_firewall_allows_address_ap(const tor_addr_port_t *ap, pref_ipv6); } -/* Return true iff we think our firewall will let us make a connection to - * ipv4h_or_addr:ipv4_or_port. ipv4h_or_addr is interpreted in host order. - * Uses ReachableORAddresses or ReachableDirAddresses based on - * fw_connection. - * pref_only and pref_ipv6 work as in fascist_firewall_allows_address_addr(). - */ -static int -fascist_firewall_allows_address_ipv4h(uint32_t ipv4h_or_addr, - uint16_t ipv4_or_port, - firewall_connection_t fw_connection, - int pref_only, int pref_ipv6) -{ - tor_addr_t ipv4_or_addr; - tor_addr_from_ipv4h(&ipv4_or_addr, ipv4h_or_addr); - return fascist_firewall_allows_address_addr(&ipv4_or_addr, ipv4_or_port, - fw_connection, pref_only, - pref_ipv6); -} - /** Return true iff we think our firewall will let us make a connection to * ipv4h_addr/ipv6_addr. Uses ipv4_orport/ipv6_orport/ReachableORAddresses or * ipv4_dirport/ipv6_dirport/ReachableDirAddresses based on IPv4/IPv6 and @@ -602,14 +570,14 @@ fascist_firewall_allows_address_ipv4h(uint32_t ipv4h_or_addr, * pref_only and pref_ipv6 work as in fascist_firewall_allows_address_addr(). */ static int -fascist_firewall_allows_base(uint32_t ipv4h_addr, uint16_t ipv4_orport, +fascist_firewall_allows_base(const tor_addr_t *ipv4_addr, uint16_t ipv4_orport, uint16_t ipv4_dirport, const tor_addr_t *ipv6_addr, uint16_t ipv6_orport, uint16_t ipv6_dirport, firewall_connection_t fw_connection, int pref_only, int pref_ipv6) { - if (fascist_firewall_allows_address_ipv4h(ipv4h_addr, + if (fascist_firewall_allows_address_addr(ipv4_addr, (fw_connection == FIREWALL_OR_CONNECTION ? ipv4_orport : ipv4_dirport), @@ -641,10 +609,10 @@ fascist_firewall_allows_ri_impl(const routerinfo_t *ri, } /* Assume IPv4 and IPv6 DirPorts are the same */ - return fascist_firewall_allows_base(ri->addr, ri->or_port, ri->dir_port, - &ri->ipv6_addr, ri->ipv6_orport, - ri->dir_port, fw_connection, pref_only, - pref_ipv6); + return fascist_firewall_allows_base(&ri->ipv4_addr, ri->ipv4_orport, + ri->ipv4_dirport, &ri->ipv6_addr, + ri->ipv6_orport, ri->ipv4_dirport, + fw_connection, pref_only, pref_ipv6); } /** Like fascist_firewall_allows_rs, but takes pref_ipv6. */ @@ -658,10 +626,10 @@ fascist_firewall_allows_rs_impl(const routerstatus_t *rs, } /* Assume IPv4 and IPv6 DirPorts are the same */ - return fascist_firewall_allows_base(rs->addr, rs->or_port, rs->dir_port, - &rs->ipv6_addr, rs->ipv6_orport, - rs->dir_port, fw_connection, pref_only, - pref_ipv6); + return fascist_firewall_allows_base(&rs->ipv4_addr, rs->ipv4_orport, + rs->ipv4_dirport, &rs->ipv6_addr, + rs->ipv6_orport, rs->ipv4_dirport, + fw_connection, pref_only, pref_ipv6); } /** Like fascist_firewall_allows_base(), but takes rs. @@ -892,34 +860,6 @@ fascist_firewall_choose_address_base(const tor_addr_t *ipv4_addr, } } -/** Like fascist_firewall_choose_address_base(), but takes a host-order IPv4 - * address as the first parameter. */ -static void -fascist_firewall_choose_address_ipv4h(uint32_t ipv4h_addr, - uint16_t ipv4_orport, - uint16_t ipv4_dirport, - const tor_addr_t *ipv6_addr, - uint16_t ipv6_orport, - uint16_t ipv6_dirport, - firewall_connection_t fw_connection, - int pref_only, - int pref_ipv6, - tor_addr_port_t* ap) -{ - tor_addr_t ipv4_addr; - tor_addr_from_ipv4h(&ipv4_addr, ipv4h_addr); - tor_assert(ap); - - tor_addr_make_null(&ap->addr, AF_UNSPEC); - ap->port = 0; - - fascist_firewall_choose_address_base(&ipv4_addr, ipv4_orport, - ipv4_dirport, ipv6_addr, - ipv6_orport, ipv6_dirport, - fw_connection, pref_only, - pref_ipv6, ap); -} - /** Like fascist_firewall_choose_address_base(), but takes <b>rs</b>. * Consults the corresponding node, then falls back to rs if node is NULL. * This should only happen when there's no valid consensus, and rs doesn't @@ -951,12 +891,11 @@ fascist_firewall_choose_address_rs(const routerstatus_t *rs, ? fascist_firewall_prefer_ipv6_orport(options) : fascist_firewall_prefer_ipv6_dirport(options)); - /* Assume IPv4 and IPv6 DirPorts are the same. - * Assume the IPv6 OR and Dir addresses are the same. */ - fascist_firewall_choose_address_ipv4h(rs->addr, rs->or_port, rs->dir_port, - &rs->ipv6_addr, rs->ipv6_orport, - rs->dir_port, fw_connection, - pref_only, pref_ipv6, ap); + fascist_firewall_choose_address_base(&rs->ipv4_addr, rs->ipv4_orport, + rs->ipv4_dirport, &rs->ipv6_addr, + rs->ipv6_orport, rs->ipv4_dirport, + fw_connection, pref_only, pref_ipv6, + ap); } } @@ -1124,17 +1063,14 @@ socks_policy_permits_address(const tor_addr_t *addr) /** Return true iff the address <b>addr</b> is in a country listed in the * case-insensitive list of country codes <b>cc_list</b>. */ static int -addr_is_in_cc_list(uint32_t addr, const smartlist_t *cc_list) +addr_is_in_cc_list(const tor_addr_t *addr, const smartlist_t *cc_list) { country_t country; const char *name; - tor_addr_t tar; if (!cc_list) return 0; - /* XXXXipv6 */ - tor_addr_from_ipv4h(&tar, addr); - country = geoip_get_country_by_addr(&tar); + country = geoip_get_country_by_addr(addr); name = geoip_get_country_name(country); return smartlist_contains_string_case(cc_list, name); } @@ -1143,9 +1079,9 @@ addr_is_in_cc_list(uint32_t addr, const smartlist_t *cc_list) * directory, based on <b>authdir_reject_policy</b>. Else return 0. */ int -authdir_policy_permits_address(uint32_t addr, uint16_t port) +authdir_policy_permits_address(const tor_addr_t *addr, uint16_t port) { - if (! addr_policy_permits_address(addr, port, authdir_reject_policy)) + if (!addr_policy_permits_tor_addr(addr, port, authdir_reject_policy)) return 0; return !addr_is_in_cc_list(addr, get_options()->AuthDirRejectCCs); } @@ -1154,9 +1090,9 @@ authdir_policy_permits_address(uint32_t addr, uint16_t port) * directory, based on <b>authdir_invalid_policy</b>. Else return 0. */ int -authdir_policy_valid_address(uint32_t addr, uint16_t port) +authdir_policy_valid_address(const tor_addr_t *addr, uint16_t port) { - if (! addr_policy_permits_address(addr, port, authdir_invalid_policy)) + if (!addr_policy_permits_tor_addr(addr, port, authdir_invalid_policy)) return 0; return !addr_is_in_cc_list(addr, get_options()->AuthDirInvalidCCs); } @@ -1165,9 +1101,9 @@ authdir_policy_valid_address(uint32_t addr, uint16_t port) * based on <b>authdir_badexit_policy</b>. Else return 0. */ int -authdir_policy_badexit_address(uint32_t addr, uint16_t port) +authdir_policy_badexit_address(const tor_addr_t *addr, uint16_t port) { - if (! addr_policy_permits_address(addr, port, authdir_badexit_policy)) + if (!addr_policy_permits_tor_addr(addr, port, authdir_badexit_policy)) return 1; return addr_is_in_cc_list(addr, get_options()->AuthDirBadExitCCs); } @@ -2086,22 +2022,6 @@ policies_copy_addr_to_smartlist(smartlist_t *addr_list, const tor_addr_t *addr) } } -/** Helper function that adds ipv4h_addr to a smartlist as a tor_addr_t *, - * as long as it is not tor_addr_is_null(), by converting it to a tor_addr_t - * and passing it to policies_add_addr_to_smartlist. - * - * The caller is responsible for freeing all the tor_addr_t* in the smartlist. - */ -static void -policies_copy_ipv4h_to_smartlist(smartlist_t *addr_list, uint32_t ipv4h_addr) -{ - if (ipv4h_addr) { - tor_addr_t ipv4_tor_addr; - tor_addr_from_ipv4h(&ipv4_tor_addr, ipv4h_addr); - policies_copy_addr_to_smartlist(addr_list, &ipv4_tor_addr); - } -} - /** Helper function that adds copies of or_options->OutboundBindAddresses * to a smartlist as tor_addr_t *, as long as or_options is non-NULL, and * the addresses are not tor_addr_is_null(), by passing them to @@ -2133,8 +2053,8 @@ policies_copy_outbound_addresses_to_smartlist(smartlist_t *addr_list, * If <b>or_options->ExitPolicyRejectPrivate</b> is true: * - prepend an entry that rejects all destinations in all netblocks reserved * for private use. - * - if local_address is non-zero, treat it as a host-order IPv4 address, and - * add it to the list of configured addresses. + * - if ipv4_local_address is non-zero, treat it as a host-order IPv4 address, + * and add it to the list of configured addresses. * - if ipv6_local_address is non-NULL, and not the null tor_addr_t, add it * to the list of configured addresses. * If <b>or_options->ExitPolicyRejectLocalInterfaces</b> is true: @@ -2151,7 +2071,7 @@ policies_copy_outbound_addresses_to_smartlist(smartlist_t *addr_list, */ int policies_parse_exit_policy_from_options(const or_options_t *or_options, - uint32_t local_address, + const tor_addr_t *ipv4_local_address, const tor_addr_t *ipv6_local_address, smartlist_t **result) { @@ -2192,7 +2112,7 @@ policies_parse_exit_policy_from_options(const or_options_t *or_options, /* Copy the configured addresses into the tor_addr_t* list */ if (or_options->ExitPolicyRejectPrivate) { - policies_copy_ipv4h_to_smartlist(configured_addresses, local_address); + policies_copy_addr_to_smartlist(configured_addresses, ipv4_local_address); policies_copy_addr_to_smartlist(configured_addresses, ipv6_local_address); } @@ -3062,7 +2982,7 @@ getinfo_helper_policies(control_connection_t *conn, /* Copy the configured addresses into the tor_addr_t* list */ if (options->ExitPolicyRejectPrivate) { - policies_copy_ipv4h_to_smartlist(configured_addresses, me->addr); + policies_copy_addr_to_smartlist(configured_addresses, &me->ipv4_addr); policies_copy_addr_to_smartlist(configured_addresses, &me->ipv6_addr); } diff --git a/src/core/or/policies.h b/src/core/or/policies.h index 72a37d62b0..1ac6f87dcf 100644 --- a/src/core/or/policies.h +++ b/src/core/or/policies.h @@ -102,9 +102,9 @@ void fascist_firewall_choose_address_dir_server(const dir_server_t *ds, int dir_policy_permits_address(const tor_addr_t *addr); int socks_policy_permits_address(const tor_addr_t *addr); -int authdir_policy_permits_address(uint32_t addr, uint16_t port); -int authdir_policy_valid_address(uint32_t addr, uint16_t port); -int authdir_policy_badexit_address(uint32_t addr, uint16_t port); +int authdir_policy_permits_address(const tor_addr_t *addr, uint16_t port); +int authdir_policy_valid_address(const tor_addr_t *addr, uint16_t port); +int authdir_policy_badexit_address(const tor_addr_t *addr, uint16_t port); int validate_addr_policies(const or_options_t *options, char **msg); void policy_expand_private(smartlist_t **policy); @@ -120,7 +120,7 @@ addr_policy_result_t compare_tor_addr_to_node_policy(const tor_addr_t *addr, int policies_parse_exit_policy_from_options( const or_options_t *or_options, - uint32_t local_address, + const tor_addr_t *ipv4_local_address, const tor_addr_t *ipv6_local_address, smartlist_t **result); struct config_line_t; diff --git a/src/core/or/port_cfg_st.h b/src/core/or/port_cfg_st.h index 064e679d78..f8ff6f8cc8 100644 --- a/src/core/or/port_cfg_st.h +++ b/src/core/or/port_cfg_st.h @@ -26,6 +26,8 @@ struct port_cfg_t { unsigned is_group_writable : 1; unsigned is_world_writable : 1; unsigned relax_dirmode_check : 1; + unsigned explicit_addr : 1; /** Indicate if address was explicitly set or + * we are using the default address. */ entry_port_cfg_t entry_cfg; diff --git a/src/core/or/protover.c b/src/core/or/protover.c index c3f443631b..1ac264925c 100644 --- a/src/core/or/protover.c +++ b/src/core/or/protover.c @@ -326,6 +326,9 @@ protover_is_supported_here(protocol_type_t pr, uint32_t ver) /** * Return true iff "list" encodes a protocol list that includes support for * the indicated protocol and version. + * + * If the protocol list is unparseable, treat it as if it defines no + * protocols, and return 0. */ int protocol_list_supports_protocol(const char *list, protocol_type_t tp, @@ -348,6 +351,9 @@ protocol_list_supports_protocol(const char *list, protocol_type_t tp, /** * Return true iff "list" encodes a protocol list that includes support for * the indicated protocol and version, or some later version. + * + * If the protocol list is unparseable, treat it as if it defines no + * protocols, and return 0. */ int protocol_list_supports_protocol_or_later(const char *list, @@ -387,6 +393,11 @@ protocol_list_supports_protocol_or_later(const char *list, const char * protover_get_supported_protocols(void) { + /* WARNING! + * + * Remember to edit the SUPPORTED_PROTOCOLS list in protover.rs if you + * are editing this list. + */ return "Cons=1-2 " "Desc=1-2 " @@ -403,7 +414,7 @@ protover_get_supported_protocols(void) #endif "Microdesc=1-2 " "Padding=2 " - "Relay=1-2"; + "Relay=1-3"; } /** The protocols from protover_get_supported_protocols(), as parsed into a @@ -740,6 +751,9 @@ protover_compute_vote(const smartlist_t *list_of_proto_strings, * one that we support, and false otherwise. If <b>missing_out</b> is * provided, set it to the list of protocols we do not support. * + * If the protocol version string is unparseable, treat it as if it defines no + * protocols, and return 1. + * * NOTE: This is quadratic, but we don't do it much: only a few times per * consensus. Checking signatures should be way more expensive than this * ever would be. diff --git a/src/core/or/protover.h b/src/core/or/protover.h index 9509f3e8a3..2950147d1b 100644 --- a/src/core/or/protover.h +++ b/src/core/or/protover.h @@ -22,12 +22,32 @@ struct smartlist_t; /// `FIRST_TOR_VERSION_TO_ADVERTISE_PROTOCOLS` #define FIRST_TOR_VERSION_TO_ADVERTISE_PROTOCOLS "0.2.9.3-alpha" -/** The protover version number that signifies HSDir support for HSv3 */ -#define PROTOVER_HSDIR_V3 2 +/** The protover version number that signifies ed25519 link handshake support + */ +#define PROTOVER_LINKAUTH_ED25519_HANDSHAKE 3 + +/** The protover version number that signifies extend2 cell support */ +#define PROTOVER_RELAY_EXTEND2 2 +/** The protover version number where relays can accept IPv6 connections */ +#define PROTOVER_RELAY_ACCEPT_IPV6 2 +/** The protover version number where relays can initiate IPv6 extends */ +#define PROTOVER_RELAY_EXTEND_IPV6 3 +/** The protover version number where relays can consider IPv6 connections + * canonical */ +#define PROTOVER_RELAY_CANONICAL_IPV6 3 + /** The protover version number that signifies HSv3 intro point support */ #define PROTOVER_HS_INTRO_V3 4 +/** The protover version number where intro points support denial of service + * resistance */ +#define PROTOVER_HS_INTRO_DOS 5 + /** The protover version number that signifies HSv3 rendezvous point support */ #define PROTOVER_HS_RENDEZVOUS_POINT_V3 2 + +/** The protover version number that signifies HSDir support for HSv3 */ +#define PROTOVER_HSDIR_V3 2 + /** The protover that signals support for HS circuit setup padding machines */ #define PROTOVER_HS_SETUP_PADDING 2 diff --git a/src/core/or/reasons.c b/src/core/or/reasons.c index 7da7843cab..708f43a689 100644 --- a/src/core/or/reasons.c +++ b/src/core/or/reasons.c @@ -244,6 +244,8 @@ orconn_end_reason_to_control_string(int r) return "IOERROR"; case END_OR_CONN_REASON_RESOURCE_LIMIT: return "RESOURCELIMIT"; + case END_OR_CONN_REASON_TLS_ERROR: + return "TLS_ERROR"; case END_OR_CONN_REASON_MISC: return "MISC"; case END_OR_CONN_REASON_PT_MISSING: @@ -276,6 +278,8 @@ tls_error_to_orconn_end_reason(int e) case TOR_TLS_CLOSE: case TOR_TLS_DONE: return END_OR_CONN_REASON_DONE; + case TOR_TLS_ERROR_MISC: + return END_OR_CONN_REASON_TLS_ERROR; default: return END_OR_CONN_REASON_MISC; } diff --git a/src/core/or/relay.c b/src/core/or/relay.c index 75d2d479e7..6895591064 100644 --- a/src/core/or/relay.c +++ b/src/core/or/relay.c @@ -56,6 +56,7 @@ #include "core/or/circuitlist.h" #include "core/or/circuituse.h" #include "core/or/circuitpadding.h" +#include "core/or/extendinfo.h" #include "lib/compress/compress.h" #include "app/config/config.h" #include "core/mainloop/connection.h" diff --git a/src/core/or/scheduler.c b/src/core/or/scheduler.c index ff58f9ca5b..18f11487d9 100644 --- a/src/core/or/scheduler.c +++ b/src/core/or/scheduler.c @@ -42,7 +42,7 @@ * circuit scheduler. It was supposed to prioritize circuits across many * channels, but wasn't effective. It is preserved in scheduler_vanilla.c. * - * [0]: http://www.robgjansen.com/publications/kist-sec2014.pdf + * [0]: https://www.robgjansen.com/publications/kist-sec2014.pdf * * Then we actually got around to implementing KIST for real. We decided to * modularize the scheduler so new ones can be implemented. You can find KIST @@ -713,7 +713,7 @@ scheduler_bug_occurred(const channel_t *chan) if (chan != NULL) { const size_t outbuf_len = - buf_datalen(TO_CONN(BASE_CHAN_TO_TLS((channel_t *) chan)->conn)->outbuf); + buf_datalen(TO_CONN(CONST_BASE_CHAN_TO_TLS(chan)->conn)->outbuf); tor_snprintf(buf, sizeof(buf), "Channel %" PRIu64 " in state %s and scheduler state %s." " Num cells on cmux: %d. Connection outbuf len: %lu.", diff --git a/src/core/or/scheduler_kist.c b/src/core/or/scheduler_kist.c index c73d768f88..8c6a7bd1d1 100644 --- a/src/core/or/scheduler_kist.c +++ b/src/core/or/scheduler_kist.c @@ -203,7 +203,7 @@ update_socket_info_impl, (socket_table_ent_t *ent)) tor_assert(ent); tor_assert(ent->chan); const tor_socket_t sock = - TO_CONN(BASE_CHAN_TO_TLS((channel_t *) ent->chan)->conn)->s; + TO_CONN(CONST_BASE_CHAN_TO_TLS(ent->chan)->conn)->s; struct tcp_info tcp; socklen_t tcp_info_len = sizeof(tcp); @@ -445,6 +445,11 @@ update_socket_written(socket_table_t *table, channel_t *chan, size_t bytes) * one cell for each and bouncing back and forth. This KIST impl avoids that * by only writing a channel's outbuf to the kernel if it has 8 cells or more * in it. + * + * Note: The number 8 has been picked for no particular reasons except that it + * is 4096 bytes which is a common number for buffering. A TLS record can hold + * up to 16KiB thus using 8 cells means that a relay will at most send a TLS + * record of 4KiB or 1/4 of the maximum capacity of a TLS record. */ MOCK_IMPL(int, channel_should_write_to_kernel, (outbuf_table_t *table, channel_t *chan)) diff --git a/src/core/or/trace_probes_circuit.c b/src/core/or/trace_probes_circuit.c new file mode 100644 index 0000000000..b186ffda7f --- /dev/null +++ b/src/core/or/trace_probes_circuit.c @@ -0,0 +1,30 @@ +/* Copyright (c) 2020, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file trace_probes_circuit.c + * \brief Tracepoint provider source file for the circuit subsystem. Probes + * are generated within this C file for LTTng-UST + **/ + +#include "orconfig.h" + +/* + * Following section is specific to LTTng-UST. + */ +#ifdef USE_TRACING_INSTRUMENTATION_LTTNG + +/* Header files that the probes need. */ +#include "core/or/circuitlist.h" +#include "core/or/crypt_path_st.h" +#include "core/or/extend_info_st.h" +#include "core/or/or.h" +#include "core/or/or_circuit_st.h" +#include "core/or/origin_circuit_st.h" + +#define TRACEPOINT_DEFINE +#define TRACEPOINT_CREATE_PROBES + +#include "trace_probes_circuit.h" + +#endif /* USE_TRACING_INSTRUMENTATION_LTTNG */ diff --git a/src/core/or/trace_probes_circuit.h b/src/core/or/trace_probes_circuit.h new file mode 100644 index 0000000000..59f53c324a --- /dev/null +++ b/src/core/or/trace_probes_circuit.h @@ -0,0 +1,22 @@ +/* Copyright (c) 2020, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file trace_probes_circuit.c + * \brief The tracing probes for the circuit subsystem. Currently, only + * LTTng-UST probes are available. + **/ + +#ifndef TOR_TRACE_PROBES_CIRCUIT_H +#define TOR_TRACE_PROBES_CIRCUIT_H + +#include "lib/trace/events.h" + +/* We only build the following if LTTng instrumentation has been enabled. */ +#ifdef USE_TRACING_INSTRUMENTATION_LTTNG + +#include "core/or/lttng_circuit.inc" + +#endif /* USE_TRACING_INSTRUMENTATION_LTTNG */ + +#endif /* TOR_TRACE_PROBES_CIRCUIT_H */ diff --git a/src/core/or/versions.c b/src/core/or/versions.c index 31f1f5b997..5dfe0c2cc9 100644 --- a/src/core/or/versions.c +++ b/src/core/or/versions.c @@ -408,6 +408,10 @@ static strmap_t *protover_summary_map = NULL; /** * Helper. Given a non-NULL protover string <b>protocols</b>, set <b>out</b> * to its summary, and memoize the result in <b>protover_summary_map</b>. + * + * If the protover string does not contain any recognised protocols, sets + * protocols_known, but does not set any other flags. (Empty strings are also + * treated this way.) */ static void memoize_protover_summary(protover_summary_flags_t *out, @@ -434,25 +438,49 @@ memoize_protover_summary(protover_summary_flags_t *out, memset(out, 0, sizeof(*out)); out->protocols_known = 1; - out->supports_extend2_cells = - protocol_list_supports_protocol(protocols, PRT_RELAY, 2); + out->supports_ed25519_link_handshake_compat = - protocol_list_supports_protocol(protocols, PRT_LINKAUTH, 3); + protocol_list_supports_protocol(protocols, PRT_LINKAUTH, + PROTOVER_LINKAUTH_ED25519_HANDSHAKE); out->supports_ed25519_link_handshake_any = - protocol_list_supports_protocol_or_later(protocols, PRT_LINKAUTH, 3); + protocol_list_supports_protocol_or_later( + protocols, + PRT_LINKAUTH, + PROTOVER_LINKAUTH_ED25519_HANDSHAKE); + + out->supports_extend2_cells = + protocol_list_supports_protocol(protocols, PRT_RELAY, + PROTOVER_RELAY_EXTEND2); + out->supports_accepting_ipv6_extends = ( + protocol_list_supports_protocol(protocols, PRT_RELAY, + PROTOVER_RELAY_ACCEPT_IPV6) || + protocol_list_supports_protocol(protocols, PRT_RELAY, + PROTOVER_RELAY_EXTEND_IPV6)); + out->supports_initiating_ipv6_extends = + protocol_list_supports_protocol(protocols, PRT_RELAY, + PROTOVER_RELAY_EXTEND_IPV6); + out->supports_canonical_ipv6_conns = + protocol_list_supports_protocol(protocols, PRT_RELAY, + PROTOVER_RELAY_CANONICAL_IPV6); + out->supports_ed25519_hs_intro = - protocol_list_supports_protocol(protocols, PRT_HSINTRO, 4); - out->supports_v3_hsdir = - protocol_list_supports_protocol(protocols, PRT_HSDIR, - PROTOVER_HSDIR_V3); + protocol_list_supports_protocol(protocols, PRT_HSINTRO, + PROTOVER_HS_INTRO_V3); + out->supports_establish_intro_dos_extension = + protocol_list_supports_protocol(protocols, PRT_HSINTRO, + PROTOVER_HS_INTRO_DOS); + out->supports_v3_rendezvous_point = protocol_list_supports_protocol(protocols, PRT_HSREND, PROTOVER_HS_RENDEZVOUS_POINT_V3); + + out->supports_v3_hsdir = + protocol_list_supports_protocol(protocols, PRT_HSDIR, + PROTOVER_HSDIR_V3); + out->supports_hs_setup_padding = protocol_list_supports_protocol(protocols, PRT_PADDING, PROTOVER_HS_SETUP_PADDING); - out->supports_establish_intro_dos_extension = - protocol_list_supports_protocol(protocols, PRT_HSINTRO, 5); protover_summary_flags_t *new_cached = tor_memdup(out, sizeof(*out)); cached = strmap_set(protover_summary_map, protocols, new_cached); @@ -461,6 +489,13 @@ memoize_protover_summary(protover_summary_flags_t *out, /** Summarize the protocols listed in <b>protocols</b> into <b>out</b>, * falling back or correcting them based on <b>version</b> as appropriate. + * + * If protocols and version are both NULL or "", returns a summary with no + * flags set. + * + * If the protover string does not contain any recognised protocols, and the + * version is not recognised, sets protocols_known, but does not set any other + * flags. (Empty strings are also treated this way.) */ void summarize_protover_flags(protover_summary_flags_t *out, @@ -469,10 +504,10 @@ summarize_protover_flags(protover_summary_flags_t *out, { tor_assert(out); memset(out, 0, sizeof(*out)); - if (protocols) { + if (protocols && strcmp(protocols, "")) { memoize_protover_summary(out, protocols); } - if (version && !strcmpstart(version, "Tor ")) { + if (version && strcmp(version, "") && !strcmpstart(version, "Tor ")) { if (!out->protocols_known) { /* The version is a "Tor" version, and where there is no * list of protocol versions that we should be looking at instead. */ diff --git a/src/core/stA1RajU b/src/core/stA1RajU deleted file mode 100644 index e69de29bb2..0000000000 --- a/src/core/stA1RajU +++ /dev/null diff --git a/src/core/stiysZND b/src/core/stiysZND Binary files differdeleted file mode 100644 index faa365b769..0000000000 --- a/src/core/stiysZND +++ /dev/null diff --git a/src/ext/README b/src/ext/README deleted file mode 100644 index d7e5439c71..0000000000 --- a/src/ext/README +++ /dev/null @@ -1,79 +0,0 @@ - -OpenBSD_malloc_Linux.c: - - The OpenBSD malloc implementation, ported to Linux. Used only when - --enable-openbsd-malloc is passed to the configure script. - -strlcat.c -strlcpy.c - - Implementations of strlcat and strlcpy, the more sane replacements - for strcat and strcpy. These are nonstandard, and some libc - implementations refuse to add them for religious reasons. - -ht.h - - An implementation of a hash table in the style of Niels Provos's - tree.h. Shared with Libevent. - -tinytest.[ch] -tinytest_demos.c -tinytest_macros.h - - A unit testing framework. https://github.com/nmathewson/tinytest - -tor_queue.h - - A copy of sys/queue.h from OpenBSD. We keep our own copy rather - than using sys/queue.h, since some platforms don't have a - sys/queue.h, and the ones that do have diverged in incompatible - ways. (CIRCLEQ or no CIRCLEQ? SIMPLQ or STAILQ?) We also rename - the identifiers with a TOR_ prefix to avoid conflicts with - the system headers. - -curve25519_donna/*.c - - A copy of Adam Langley's curve25519-donna mostly-portable - implementations of curve25519. - -csiphash.c -siphash.h - - Marek Majkowski's implementation of siphash 2-4, a secure keyed - hash algorithm to avoid collision-based DoS attacks against hash - tables. - -trunnel/*.[ch] - - Headers and runtime code for Trunnel, a system for generating - code to encode and decode binary formats. - -ed25519/ref10/* - - Daniel Bernsten's portable ref10 implementation of ed25519. - Public domain. - -ed25519/donna/* - - Andrew Moon's semi-portable ed25519-donna implementation of - ed25519. Public domain. - -keccak-tiny/ - - David Leon Gil's portable Keccak implementation. CC0. - -readpassphrase.[ch] - - Portable readpassphrase implementation from OpenSSH portable, version - 6.8p1. - -timeouts/ - - William Ahern's hierarchical timer-wheel implementation. MIT license. - -mulodi/ - - Contains an overflow-checking 64-bit signed integer multiply - from LLVM's compiler_rt. For some reason, this is missing from - 32-bit libclang in many places. Dual licensed MIT-license and - BSD-like license; see mulodi/LICENSE.TXT. diff --git a/src/ext/ext.md b/src/ext/ext.md new file mode 100644 index 0000000000..1eaaab605b --- /dev/null +++ b/src/ext/ext.md @@ -0,0 +1,88 @@ +@dir /ext +@brief Externally maintained code + +The "ext" directory holds code that was written elsewhere, and is not +reliably packaged as a library where we want to build, so we ship +it along with Tor. + +In general, you should not edit this code: we are not the maintainers. +Instead, you should submit patches upstream. + +OpenBSD_malloc_Linux.c: + +> The OpenBSD malloc implementation, ported to Linux. Used only when +> --enable-openbsd-malloc is passed to the configure script. + +strlcat.c +strlcpy.c + +> Implementations of strlcat and strlcpy, the more sane replacements +> for strcat and strcpy. These are nonstandard, and some libc +> implementations refuse to add them for religious reasons. + +ht.h + +> An implementation of a hash table in the style of Niels Provos's +> tree.h. Shared with Libevent. + +tinytest.c tinytest.h +tinytest_demos.c +tinytest_macros.h + +> A unit testing framework. https://github.com/nmathewson/tinytest + +tor_queue.h + +> A copy of sys/queue.h from OpenBSD. We keep our own copy rather +> than using sys/queue.h, since some platforms don't have a +> sys/queue.h, and the ones that do have diverged in incompatible +> ways. (CIRCLEQ or no CIRCLEQ? SIMPLQ or STAILQ?) We also rename +> the identifiers with a TOR_ prefix to avoid conflicts with +> the system headers. + +curve25519_donna/*.c + +> A copy of Adam Langley's curve25519-donna mostly-portable +> implementations of curve25519. + +csiphash.c +siphash.h + +> Marek Majkowski's implementation of siphash 2-4, a secure keyed +> hash algorithm to avoid collision-based DoS attacks against hash +> tables. + +trunnel/*.[ch] + +> Headers and runtime code for Trunnel, a system for generating +> code to encode and decode binary formats. + +ed25519/ref10/* + +> Daniel Bernsten's portable ref10 implementation of ed25519. +> Public domain. + +ed25519/donna/* + +> Andrew Moon's semi-portable ed25519-donna implementation of +> ed25519. Public domain. + +keccak-tiny/ + +> David Leon Gil's portable Keccak implementation. CC0. + +readpassphrase.[ch] + +> Portable readpassphrase implementation from OpenSSH portable, version +> 6.8p1. + +timeouts/ + +> William Ahern's hierarchical timer-wheel implementation. MIT license. + +mulodi/ + +> Contains an overflow-checking 64-bit signed integer multiply +> from LLVM's compiler_rt. For some reason, this is missing from +> 32-bit libclang in many places. Dual licensed MIT-license and +> BSD-like license; see mulodi/LICENSE.TXT. diff --git a/src/ext/include.am b/src/ext/include.am index 317e25d78e..8b646b1b4e 100644 --- a/src/ext/include.am +++ b/src/ext/include.am @@ -1,7 +1,7 @@ AM_CPPFLAGS += -I$(srcdir)/src/ext -Isrc/ext -EXTRA_DIST += src/ext/README +EXTRA_DIST += src/ext/ext.md EXTHEADERS = \ src/ext/ht.h \ diff --git a/src/feature/client/bridges.c b/src/feature/client/bridges.c index 66b04f3bc2..6892c4a25f 100644 --- a/src/feature/client/bridges.c +++ b/src/feature/client/bridges.c @@ -259,12 +259,26 @@ addr_is_a_configured_bridge(const tor_addr_t *addr, /** If we have a bridge configured whose digest matches * <b>ei->identity_digest</b>, or a bridge with no known digest whose address * matches <b>ei->addr</b>:<b>ei->port</b>, return 1. Else return 0. - * If <b>ei->onion_key</b> is NULL, check for address/port matches only. */ + * If <b>ei->onion_key</b> is NULL, check for address/port matches only. + * + * Note that if the extend_info_t contains multiple addresses, we return true + * only if _every_ address is a bridge. + */ int extend_info_is_a_configured_bridge(const extend_info_t *ei) { const char *digest = ei->onion_key ? ei->identity_digest : NULL; - return addr_is_a_configured_bridge(&ei->addr, ei->port, digest); + const tor_addr_port_t *ap1 = NULL, *ap2 = NULL; + if (! tor_addr_is_null(&ei->orports[0].addr)) + ap1 = &ei->orports[0]; + if (! tor_addr_is_null(&ei->orports[1].addr)) + ap2 = &ei->orports[1]; + IF_BUG_ONCE(ap1 == NULL) { + return 0; + } + return addr_is_a_configured_bridge(&ap1->addr, ap1->port, digest) && + (ap2 == NULL || + addr_is_a_configured_bridge(&ap2->addr, ap2->port, digest)); } /** Wrapper around get_configured_bridge_by_addr_port_digest() to look @@ -289,51 +303,21 @@ routerinfo_is_a_configured_bridge(const routerinfo_t *ri) } /** - * Return 1 iff <b>bridge_list</b> contains entry matching - * given; IPv4 address in host byte order (<b>ipv4_addr</b> - * and <b>port</b> (and no identity digest) OR it contains an - * entry whose identity matches <b>digest</b>. Otherwise, - * return 0. - */ -static int -bridge_exists_with_ipv4h_addr_and_port(const uint32_t ipv4_addr, - const uint16_t port, - const char *digest) -{ - tor_addr_t node_ipv4; - - if (tor_addr_port_is_valid_ipv4h(ipv4_addr, port, 0)) { - tor_addr_from_ipv4h(&node_ipv4, ipv4_addr); - - bridge_info_t *bridge = - get_configured_bridge_by_addr_port_digest(&node_ipv4, - port, - digest); - - return (bridge != NULL); - } - - return 0; -} - -/** * Return 1 iff <b>bridge_list</b> contains entry matching given - * <b>ipv6_addr</b> and <b>port</b> (and no identity digest) OR + * <b>addr</b> and <b>port</b> (and no identity digest) OR * it contains an entry whose identity matches <b>digest</b>. * Otherwise, return 0. */ static int -bridge_exists_with_ipv6_addr_and_port(const tor_addr_t *ipv6_addr, - const uint16_t port, - const char *digest) +bridge_exists_with_addr_and_port(const tor_addr_t *addr, + const uint16_t port, + const char *digest) { - if (!tor_addr_port_is_valid(ipv6_addr, port, 0)) + if (!tor_addr_port_is_valid(addr, port, 0)) return 0; bridge_info_t *bridge = - get_configured_bridge_by_addr_port_digest(ipv6_addr, - port, - digest); + get_configured_bridge_by_addr_port_digest(addr, port, digest); return (bridge != NULL); } @@ -360,29 +344,29 @@ node_is_a_configured_bridge(const node_t *node) * check for absence of identity digest in a bridge. */ if (node->ri) { - if (bridge_exists_with_ipv4h_addr_and_port(node->ri->addr, - node->ri->or_port, - node->identity)) + if (bridge_exists_with_addr_and_port(&node->ri->ipv4_addr, + node->ri->ipv4_orport, + node->identity)) return 1; - if (bridge_exists_with_ipv6_addr_and_port(&node->ri->ipv6_addr, - node->ri->ipv6_orport, - node->identity)) + if (bridge_exists_with_addr_and_port(&node->ri->ipv6_addr, + node->ri->ipv6_orport, + node->identity)) return 1; } else if (node->rs) { - if (bridge_exists_with_ipv4h_addr_and_port(node->rs->addr, - node->rs->or_port, - node->identity)) + if (bridge_exists_with_addr_and_port(&node->rs->ipv4_addr, + node->rs->ipv4_orport, + node->identity)) return 1; - if (bridge_exists_with_ipv6_addr_and_port(&node->rs->ipv6_addr, - node->rs->ipv6_orport, - node->identity)) + if (bridge_exists_with_addr_and_port(&node->rs->ipv6_addr, + node->rs->ipv6_orport, + node->identity)) return 1; } else if (node->md) { - if (bridge_exists_with_ipv6_addr_and_port(&node->md->ipv6_addr, - node->md->ipv6_orport, - node->identity)) + if (bridge_exists_with_addr_and_port(&node->md->ipv6_addr, + node->md->ipv6_orport, + node->identity)) return 1; } @@ -811,25 +795,23 @@ rewrite_node_address_for_bridge(const bridge_info_t *bridge, node_t *node) * do that safely if we know that no function that connects to an OR * does so through an address from any source other than node_get_addr(). */ - tor_addr_t addr; const or_options_t *options = get_options(); if (node->ri) { routerinfo_t *ri = node->ri; - tor_addr_from_ipv4h(&addr, ri->addr); - if ((!tor_addr_compare(&bridge->addr, &addr, CMP_EXACT) && - bridge->port == ri->or_port) || + if ((!tor_addr_compare(&bridge->addr, &ri->ipv4_addr, CMP_EXACT) && + bridge->port == ri->ipv4_orport) || (!tor_addr_compare(&bridge->addr, &ri->ipv6_addr, CMP_EXACT) && bridge->port == ri->ipv6_orport)) { /* they match, so no need to do anything */ } else { if (tor_addr_family(&bridge->addr) == AF_INET) { - ri->addr = tor_addr_to_ipv4h(&bridge->addr); - ri->or_port = bridge->port; + tor_addr_copy(&ri->ipv4_addr, &bridge->addr); + ri->ipv4_orport = bridge->port; log_info(LD_DIR, "Adjusted bridge routerinfo for '%s' to match configured " "address %s:%d.", - ri->nickname, fmt_addr32(ri->addr), ri->or_port); + ri->nickname, fmt_addr(&ri->ipv4_addr), ri->ipv4_orport); } else if (tor_addr_family(&bridge->addr) == AF_INET6) { tor_addr_copy(&ri->ipv6_addr, &bridge->addr); ri->ipv6_orport = bridge->port; @@ -872,21 +854,20 @@ rewrite_node_address_for_bridge(const bridge_info_t *bridge, node_t *node) } if (node->rs) { routerstatus_t *rs = node->rs; - tor_addr_from_ipv4h(&addr, rs->addr); - if ((!tor_addr_compare(&bridge->addr, &addr, CMP_EXACT) && - bridge->port == rs->or_port) || + if ((!tor_addr_compare(&bridge->addr, &rs->ipv4_addr, CMP_EXACT) && + bridge->port == rs->ipv4_orport) || (!tor_addr_compare(&bridge->addr, &rs->ipv6_addr, CMP_EXACT) && bridge->port == rs->ipv6_orport)) { /* they match, so no need to do anything */ } else { if (tor_addr_family(&bridge->addr) == AF_INET) { - rs->addr = tor_addr_to_ipv4h(&bridge->addr); - rs->or_port = bridge->port; + tor_addr_copy(&rs->ipv4_addr, &bridge->addr); + rs->ipv4_orport = bridge->port; log_info(LD_DIR, "Adjusted bridge routerstatus for '%s' to match " "configured address %s.", - rs->nickname, fmt_addrport(&bridge->addr, rs->or_port)); + rs->nickname, fmt_addrport(&bridge->addr, rs->ipv4_orport)); /* set IPv6 preferences even if there is no ri */ } else if (tor_addr_family(&bridge->addr) == AF_INET6) { tor_addr_copy(&rs->ipv6_addr, &bridge->addr); diff --git a/src/feature/client/entrynodes.c b/src/feature/client/entrynodes.c index ded7db969a..9b20684bf7 100644 --- a/src/feature/client/entrynodes.c +++ b/src/feature/client/entrynodes.c @@ -47,8 +47,7 @@ * As a persistent ordered list whose elements are taken from the * sampled set, we track a CONFIRMED GUARDS LIST. A guard becomes * confirmed when we successfully build a circuit through it, and decide - * to use that circuit. We order the guards on this list by the order - * in which they became confirmed. + * to use that circuit. * * And as a final group, we have an ordered list of PRIMARY GUARDS, * whose elements are taken from the filtered set. We prefer @@ -59,7 +58,7 @@ * * To build circuits, we take a primary guard if possible -- or a * reachable filtered confirmed guard if no primary guard is possible -- - * or a random reachable filtered guard otherwise. If the guard is + * or the first (by sampled order) filtered guard otherwise. If the guard is * primary, we can use the circuit immediately on success. Otherwise, * the guard is now "pending" -- we won't use its circuit unless all * of the circuits we're trying to build through better guards have @@ -92,14 +91,18 @@ * [x] Whenever we remove a guard from the sample, remove it from the primary * and confirmed lists. * - * [x] When we make a guard confirmed, update the primary list. + * [x] When we make a guard confirmed, update the primary list, and sort them + * by sampled order. * * [x] When we make a guard filtered or unfiltered, update the primary list. * * [x] When we are about to pick a guard, make sure that the primary list is * full. * - * [x] Before calling sample_reachable_filtered_entry_guards(), make sure + * [x] When we update the confirmed list, or when we re-build the primary list + * and detect a change, we sort those lists by sampled_idx + * + * [x] Before calling first_reachable_filtered_entry_guard(), make sure * that the filtered, primary, and confirmed flags are up-to-date. * * [x] Call entry_guard_consider_retry every time we are about to check @@ -172,6 +175,7 @@ static entry_guard_t *get_sampled_guard_by_bridge_addr(guard_selection_t *gs, const tor_addr_port_t *addrport); static int entry_guard_obeys_restriction(const entry_guard_t *guard, const entry_guard_restriction_t *rst); +static int compare_guards_by_sampled_idx(const void **a_, const void **b_); /** Return 0 if we should apply guardfraction information found in the * consensus. A specific consensus can be specified with the @@ -890,6 +894,7 @@ entry_guard_add_to_sample_impl(guard_selection_t *gs, tor_free(guard->sampled_by_version); guard->sampled_by_version = tor_strdup(VERSION); guard->currently_listed = 1; + guard->sampled_idx = gs->next_sampled_idx++; guard->confirmed_idx = -1; /* non-persistent fields */ @@ -901,6 +906,11 @@ entry_guard_add_to_sample_impl(guard_selection_t *gs, guard->in_selection = gs; entry_guard_set_filtered_flags(get_options(), gs, guard); entry_guards_changed_for_guard_selection(gs); + + /* Just added this guard to the sampled set and hence it might be used as a + * guard in the future: send GUARD NEW control event. */ + control_event_guard(guard->nickname, guard->identity, "NEW"); + return guard; } @@ -1383,7 +1393,7 @@ sampled_guards_prune_obsolete_entries(guard_selection_t *gs, if (rmv) { ++n_changes; - SMARTLIST_DEL_CURRENT(gs->sampled_entry_guards, guard); + SMARTLIST_DEL_CURRENT_KEEPORDER(gs->sampled_entry_guards, guard); remove_guard_from_confirmed_and_primary_lists(gs, guard); entry_guard_free(guard); } @@ -1544,7 +1554,7 @@ guard_in_node_family(const entry_guard_t *guard, const node_t *node) if (get_options()->EnforceDistinctSubnets && guard->bridge_addr) { tor_addr_t node_addr; node_get_addr(node, &node_addr); - if (addrs_in_same_network_family(&node_addr, + if (router_addrs_in_same_network(&node_addr, &guard->bridge_addr->addr)) { return 1; } @@ -1566,12 +1576,12 @@ guard_create_exit_restriction(const uint8_t *exit_id) } /** If we have fewer than this many possible usable guards, don't set - * MD-availability-based restrictions: we might blacklist all of them. */ + * MD-availability-based restrictions: we might denylist all of them. */ #define MIN_GUARDS_FOR_MD_RESTRICTION 10 /** Return true if we should set md dirserver restrictions. We might not want * to set those if our guard options are too restricted, since we don't want - * to blacklist all of them. */ + * to denylist all of them. */ static int should_set_md_dirserver_restriction(void) { @@ -1707,7 +1717,7 @@ entry_guards_update_filtered_sets(guard_selection_t *gs) } /** - * Return a random guard from the reachable filtered sample guards + * Return the first sampled guard from the reachable filtered sample guards * in <b>gs</b>, subject to the exclusion rules listed in <b>flags</b>. * Return NULL if no such guard can be found. * @@ -1718,7 +1728,7 @@ entry_guards_update_filtered_sets(guard_selection_t *gs) * violate it. **/ STATIC entry_guard_t * -sample_reachable_filtered_entry_guards(guard_selection_t *gs, +first_reachable_filtered_entry_guard(guard_selection_t *gs, const entry_guard_restriction_t *rst, unsigned flags) { @@ -1771,7 +1781,17 @@ sample_reachable_filtered_entry_guards(guard_selection_t *gs, flags, smartlist_len(reachable_filtered_sample)); if (smartlist_len(reachable_filtered_sample)) { - result = smartlist_choose(reachable_filtered_sample); + /** + * Get the first guard of the filtered set builds from + * sampled_entry_guards. Proposal 310 suggests this design to overcome + * performance and security issues linked to the previous selection + * method. The guard selected here should be filtered out if this function + * is called again in the same context. I.e., if we filter guards to add + * them into some list X, then the guards from list X will be filtered out + * when this function is called again. Hence it requires setting exclude + * flags in a appropriate way (depending of the context of the caller). + */ + result = smartlist_get(reachable_filtered_sample, 0); log_info(LD_GUARD, " (Selected %s.)", result ? entry_guard_describe(result) : "<null>"); } @@ -1780,10 +1800,6 @@ sample_reachable_filtered_entry_guards(guard_selection_t *gs, return result; } -/** - * Helper: compare two entry_guard_t by their confirmed_idx values. - * Used to sort the confirmed list. - */ static int compare_guards_by_confirmed_idx(const void **a_, const void **b_) { @@ -1795,6 +1811,21 @@ compare_guards_by_confirmed_idx(const void **a_, const void **b_) else return 0; } +/** + * Helper: compare two entry_guard_t by their sampled_idx values. + * Used to sort the sampled list + */ +static int +compare_guards_by_sampled_idx(const void **a_, const void **b_) +{ + const entry_guard_t *a = *a_, *b = *b_; + if (a->sampled_idx < b->sampled_idx) + return -1; + else if (a->sampled_idx > b->sampled_idx) + return 1; + else + return 0; +} /** * Find the confirmed guards from among the sampled guards in <b>gs</b>, @@ -1811,7 +1842,7 @@ entry_guards_update_confirmed(guard_selection_t *gs) } SMARTLIST_FOREACH_END(guard); smartlist_sort(gs->confirmed_entry_guards, compare_guards_by_confirmed_idx); - + /** Needed to keep a dense array of confirmed_idx */ int any_changed = 0; SMARTLIST_FOREACH_BEGIN(gs->confirmed_entry_guards, entry_guard_t *, guard) { if (guard->confirmed_idx != guard_sl_idx) { @@ -1821,6 +1852,8 @@ entry_guards_update_confirmed(guard_selection_t *gs) } SMARTLIST_FOREACH_END(guard); gs->next_confirmed_idx = smartlist_len(gs->confirmed_entry_guards); + // We need the confirmed list to always be give guards in sampled order + smartlist_sort(gs->confirmed_entry_guards, compare_guards_by_sampled_idx); if (any_changed) { entry_guards_changed_for_guard_selection(gs); @@ -1849,6 +1882,9 @@ make_guard_confirmed(guard_selection_t *gs, entry_guard_t *guard) guard->confirmed_idx = gs->next_confirmed_idx++; smartlist_add(gs->confirmed_entry_guards, guard); + /** The confirmation ordering might not be the sample ording. We need to + * reorder */ + smartlist_sort(gs->confirmed_entry_guards, compare_guards_by_sampled_idx); // This confirmed guard might kick something else out of the primary // guards. @@ -1912,7 +1948,7 @@ entry_guards_update_primary(guard_selection_t *gs) /* Finally, fill out the list with sampled guards. */ while (smartlist_len(new_primary_guards) < N_PRIMARY_GUARDS) { - entry_guard_t *guard = sample_reachable_filtered_entry_guards(gs, NULL, + entry_guard_t *guard = first_reachable_filtered_entry_guard(gs, NULL, SAMPLE_EXCLUDE_CONFIRMED| SAMPLE_EXCLUDE_PRIMARY| SAMPLE_NO_UPDATE_PRIMARY); @@ -1943,6 +1979,7 @@ entry_guards_update_primary(guard_selection_t *gs) g->confirmed_idx >= 0 ? " (confirmed)" : "", g->is_filtered_guard ? "" : " (excluded by filter)"); } SMARTLIST_FOREACH_END(g); + smartlist_sort(new_primary_guards, compare_guards_by_sampled_idx); } smartlist_free(old_primary_guards); @@ -2055,10 +2092,15 @@ select_primary_guard_for_circuit(guard_selection_t *gs, SMARTLIST_FOREACH_BEGIN(gs->primary_entry_guards, entry_guard_t *, guard) { entry_guard_consider_retry(guard); - if (! entry_guard_obeys_restriction(guard, rst)) + if (!entry_guard_obeys_restriction(guard, rst)) { + log_info(LD_GUARD, "Entry guard %s doesn't obey restriction, we test the" + " next one", entry_guard_describe(guard)); continue; + } if (guard->is_reachable != GUARD_REACHABLE_NO) { if (need_descriptor && !guard_has_descriptor(guard)) { + log_info(LD_GUARD, "Guard %s does not have a descriptor", + entry_guard_describe(guard)); continue; } *state_out = GUARD_CIRC_STATE_USABLE_ON_COMPLETION; @@ -2071,9 +2113,11 @@ select_primary_guard_for_circuit(guard_selection_t *gs, if (smartlist_len(usable_primary_guards)) { chosen_guard = smartlist_choose(usable_primary_guards); + log_info(LD_GUARD, + "Selected primary guard %s for circuit from a list size of %d.", + entry_guard_describe(chosen_guard), + smartlist_len(usable_primary_guards)); smartlist_free(usable_primary_guards); - log_info(LD_GUARD, "Selected primary guard %s for circuit.", - entry_guard_describe(chosen_guard)); } smartlist_free(usable_primary_guards); @@ -2118,10 +2162,10 @@ select_confirmed_guard_for_circuit(guard_selection_t *gs, } /** - * For use with a circuit, pick a confirmed usable filtered guard - * at random. Update the <b>last_tried_to_connect</b> time and the - * <b>is_pending</b> fields of the guard as appropriate. Set <b>state_out</b> - * to the new guard-state of the circuit. + * For use with a circuit, pick a usable filtered guard. Update the + * <b>last_tried_to_connect</b> time and the <b>is_pending</b> fields of the + * guard as appropriate. Set <b>state_out</b> to the new guard-state of the + * circuit. */ static entry_guard_t * select_filtered_guard_for_circuit(guard_selection_t *gs, @@ -2134,7 +2178,7 @@ select_filtered_guard_for_circuit(guard_selection_t *gs, unsigned flags = 0; if (need_descriptor) flags |= SAMPLE_EXCLUDE_NO_DESCRIPTOR; - chosen_guard = sample_reachable_filtered_entry_guards(gs, + chosen_guard = first_reachable_filtered_entry_guard(gs, rst, SAMPLE_EXCLUDE_CONFIRMED | SAMPLE_EXCLUDE_PRIMARY | @@ -2148,7 +2192,7 @@ select_filtered_guard_for_circuit(guard_selection_t *gs, chosen_guard->last_tried_to_connect = approx_time(); *state_out = GUARD_CIRC_STATE_USABLE_IF_NO_BETTER_GUARD; log_info(LD_GUARD, "No primary or confirmed guards available. Selected " - "random guard %s for circuit. Will try other guards before " + "guard %s for circuit. Will try other guards before " "using this circuit.", entry_guard_describe(chosen_guard)); return chosen_guard; @@ -2189,8 +2233,8 @@ select_entry_guard_for_circuit(guard_selection_t *gs, if (chosen_guard) return chosen_guard; - /* "Otherwise, if there is no such entry, select a member at - random from {USABLE_FILTERED_GUARDS}." */ + /* "Otherwise, if there is no such entry, select a member + * {USABLE_FILTERED_GUARDS} following the sample ordering" */ chosen_guard = select_filtered_guard_for_circuit(gs, usage, rst, state_out); if (chosen_guard == NULL) { @@ -2220,6 +2264,9 @@ entry_guards_note_guard_failure(guard_selection_t *gs, if (guard->failing_since == 0) guard->failing_since = approx_time(); + /* This guard not reachable: send GUARD DOWN event */ + control_event_guard(guard->nickname, guard->identity, "DOWN"); + log_info(LD_GUARD, "Recorded failure for %s%sguard %s", guard->is_primary?"primary ":"", guard->confirmed_idx>=0?"confirmed ":"", @@ -2245,6 +2292,11 @@ entry_guards_note_guard_success(guard_selection_t *gs, const time_t last_time_on_internet = gs->last_time_on_internet; gs->last_time_on_internet = approx_time(); + /* If guard was not already marked as reachable, send a GUARD UP signal */ + if (guard->is_reachable != GUARD_REACHABLE_YES) { + control_event_guard(guard->nickname, guard->identity, "UP"); + } + guard->is_reachable = GUARD_REACHABLE_YES; guard->failing_since = 0; guard->is_pending = 0; @@ -2773,10 +2825,12 @@ entry_guards_update_all(guard_selection_t *gs) /** * Return a newly allocated string for encoding the persistent parts of - * <b>guard</b> to the state file. + * <b>guard</b> to the state file. <b>dense_sampled_idx</b> refers to the + * sampled_idx made dense for this <b>guard</b>. Encoding all guards should + * lead to a dense array of sampled_idx in the state file. */ STATIC char * -entry_guard_encode_for_state(entry_guard_t *guard) +entry_guard_encode_for_state(entry_guard_t *guard, int dense_sampled_idx) { /* * The meta-format we use is K=V K=V K=V... where K can be any @@ -2805,7 +2859,8 @@ entry_guard_encode_for_state(entry_guard_t *guard) format_iso_time_nospace(tbuf, guard->sampled_on_date); smartlist_add_asprintf(result, "sampled_on=%s", tbuf); - + // Replacing the sampled_idx by dense array + smartlist_add_asprintf(result, "sampled_idx=%d", dense_sampled_idx); if (guard->sampled_by_version) { smartlist_add_asprintf(result, "sampled_by=%s", guard->sampled_by_version); @@ -2861,6 +2916,78 @@ entry_guard_encode_for_state(entry_guard_t *guard) } /** + * Extract key=val from the state string <b>s</b> and duplicate the value to + * some string target declared in entry_guard_parse_from_state + */ +static void +parse_from_state_set_vals(const char *s, smartlist_t *entries, smartlist_t + *extra, strmap_t *vals) +{ + smartlist_split_string(entries, s, " ", + SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); + + SMARTLIST_FOREACH_BEGIN(entries, char *, entry) { + const char *eq = strchr(entry, '='); + if (!eq) { + smartlist_add(extra, entry); + continue; + } + char *key = tor_strndup(entry, eq-entry); + char **target = strmap_get(vals, key); + if (target == NULL || *target != NULL) { + /* unrecognized or already set */ + smartlist_add(extra, entry); + tor_free(key); + continue; + } + + *target = tor_strdup(eq+1); + tor_free(key); + tor_free(entry); + } SMARTLIST_FOREACH_END(entry); +} + +/** + * Handle part of the parsing state file logic, focused on time related things + */ +static void +parse_from_state_handle_time(entry_guard_t *guard, char *sampled_on, char + *unlisted_since, char *confirmed_on) +{ +#define HANDLE_TIME(field) do { \ + if (field) { \ + int r = parse_iso_time_nospace(field, &field ## _time); \ + if (r < 0) { \ + log_warn(LD_CIRC, "Unable to parse %s %s from guard", \ + #field, escaped(field)); \ + field##_time = -1; \ + } \ + } \ + } while (0) + + time_t sampled_on_time = 0; + time_t unlisted_since_time = 0; + time_t confirmed_on_time = 0; + + HANDLE_TIME(sampled_on); + HANDLE_TIME(unlisted_since); + HANDLE_TIME(confirmed_on); + + if (sampled_on_time <= 0) + sampled_on_time = approx_time(); + if (unlisted_since_time < 0) + unlisted_since_time = 0; + if (confirmed_on_time < 0) + confirmed_on_time = 0; + + #undef HANDLE_TIME + + guard->sampled_on_date = sampled_on_time; + guard->unlisted_since_date = unlisted_since_time; + guard->confirmed_on_date = confirmed_on_time; +} + +/** * Given a string generated by entry_guard_encode_for_state(), parse it * (if possible) and return an entry_guard_t object for it. Return NULL * on complete failure. @@ -2876,6 +3003,7 @@ entry_guard_parse_from_state(const char *s) char *rsa_id = NULL; char *nickname = NULL; char *sampled_on = NULL; + char *sampled_idx = NULL; char *sampled_by = NULL; char *unlisted_since = NULL; char *listed = NULL; @@ -2892,6 +3020,7 @@ entry_guard_parse_from_state(const char *s) char *pb_collapsed_circuits = NULL; char *pb_unusable_circuits = NULL; char *pb_timeouts = NULL; + int invalid_sampled_idx = get_max_sample_size_absolute(); /* Split up the entries. Put the ones we know about in strings and the * rest in "extra". */ @@ -2905,6 +3034,7 @@ entry_guard_parse_from_state(const char *s) FIELD(rsa_id); FIELD(nickname); FIELD(sampled_on); + FIELD(sampled_idx); FIELD(sampled_by); FIELD(unlisted_since); FIELD(listed); @@ -2920,29 +3050,8 @@ entry_guard_parse_from_state(const char *s) FIELD(pb_unusable_circuits); FIELD(pb_timeouts); #undef FIELD - - smartlist_split_string(entries, s, " ", - SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0); - - SMARTLIST_FOREACH_BEGIN(entries, char *, entry) { - const char *eq = strchr(entry, '='); - if (!eq) { - smartlist_add(extra, entry); - continue; - } - char *key = tor_strndup(entry, eq-entry); - char **target = strmap_get(vals, key); - if (target == NULL || *target != NULL) { - /* unrecognized or already set */ - smartlist_add(extra, entry); - tor_free(key); - continue; - } - - *target = tor_strdup(eq+1); - tor_free(key); - tor_free(entry); - } SMARTLIST_FOREACH_END(entry); + /* Extract from s the key=val that we recognize, put the others in extra*/ + parse_from_state_set_vals(s, entries, extra, vals); smartlist_free(entries); strmap_free(vals, NULL); @@ -2990,43 +3099,12 @@ entry_guard_parse_from_state(const char *s) } /* Process the various time fields. */ - -#define HANDLE_TIME(field) do { \ - if (field) { \ - int r = parse_iso_time_nospace(field, &field ## _time); \ - if (r < 0) { \ - log_warn(LD_CIRC, "Unable to parse %s %s from guard", \ - #field, escaped(field)); \ - field##_time = -1; \ - } \ - } \ - } while (0) - - time_t sampled_on_time = 0; - time_t unlisted_since_time = 0; - time_t confirmed_on_time = 0; - - HANDLE_TIME(sampled_on); - HANDLE_TIME(unlisted_since); - HANDLE_TIME(confirmed_on); - - if (sampled_on_time <= 0) - sampled_on_time = approx_time(); - if (unlisted_since_time < 0) - unlisted_since_time = 0; - if (confirmed_on_time < 0) - confirmed_on_time = 0; - - #undef HANDLE_TIME - - guard->sampled_on_date = sampled_on_time; - guard->unlisted_since_date = unlisted_since_time; - guard->confirmed_on_date = confirmed_on_time; + parse_from_state_handle_time(guard, sampled_on, unlisted_since, + confirmed_on); /* Take sampled_by_version verbatim. */ guard->sampled_by_version = sampled_by; sampled_by = NULL; /* prevent free */ - /* Listed is a boolean */ if (listed && strcmp(listed, "0")) guard->currently_listed = 1; @@ -3044,6 +3122,29 @@ entry_guard_parse_from_state(const char *s) } } + if (sampled_idx) { + int ok = 1; + long idx = tor_parse_long(sampled_idx, 10, 0, INT_MAX, &ok, NULL); + if (!ok) { + log_warn(LD_GUARD, "Guard has invalid sampled_idx %s", + escaped(sampled_idx)); + /* set it to a idx higher than the max sample size */ + guard->sampled_idx = invalid_sampled_idx++; + } else { + guard->sampled_idx = (int)idx; + } + } else if (confirmed_idx) { + /* This state has been written by an older Tor version which did not have + * sample ordering */ + + guard->sampled_idx = guard->confirmed_idx; + } else { + log_warn(LD_GUARD, "The state file seems to be into a status that could" + " yield to weird entry node selection: we're missing both a" + " sampled_idx and a confirmed_idx."); + guard->sampled_idx = invalid_sampled_idx++; + } + /* Anything we didn't recognize gets crammed together */ if (smartlist_len(extra) > 0) { guard->extra_state_fields = smartlist_join_strings(extra, " ", 0, NULL); @@ -3098,6 +3199,7 @@ entry_guard_parse_from_state(const char *s) tor_free(listed); tor_free(confirmed_on); tor_free(confirmed_idx); + tor_free(sampled_idx); tor_free(bridge_addr); tor_free(pb_use_attempts); tor_free(pb_use_successes); @@ -3127,13 +3229,15 @@ entry_guards_update_guards_in_state(or_state_t *state) config_line_t **nextline = &lines; SMARTLIST_FOREACH_BEGIN(guard_contexts, guard_selection_t *, gs) { + int i = 0; SMARTLIST_FOREACH_BEGIN(gs->sampled_entry_guards, entry_guard_t *, guard) { if (guard->is_persistent == 0) continue; *nextline = tor_malloc_zero(sizeof(config_line_t)); (*nextline)->key = tor_strdup("Guard"); - (*nextline)->value = entry_guard_encode_for_state(guard); + (*nextline)->value = entry_guard_encode_for_state(guard, i); nextline = &(*nextline)->next; + i++; } SMARTLIST_FOREACH_END(guard); } SMARTLIST_FOREACH_END(gs); @@ -3186,6 +3290,14 @@ entry_guards_load_guards_from_state(or_state_t *state, int set) tor_assert(gs); smartlist_add(gs->sampled_entry_guards, guard); guard->in_selection = gs; + /* Recompute the next_sampled_id from the state. We do not assume that + * sampled guards appear in the correct order within the file, and we + * need to know what would be the next sampled idx to give to any + * new sampled guard (i.e., max of guard->sampled_idx + 1)*/ + if (gs->next_sampled_idx <= guard->sampled_idx) { + gs->next_sampled_idx = guard->sampled_idx + 1; + } + } else { entry_guard_free(guard); } @@ -3193,6 +3305,10 @@ entry_guards_load_guards_from_state(or_state_t *state, int set) if (set) { SMARTLIST_FOREACH_BEGIN(guard_contexts, guard_selection_t *, gs) { + /** Guards should be in sample order within the file, but it is maybe + * better NOT to assume that. Let's order them before updating lists + */ + smartlist_sort(gs->sampled_entry_guards, compare_guards_by_sampled_idx); entry_guards_update_all(gs); } SMARTLIST_FOREACH_END(gs); } diff --git a/src/feature/client/entrynodes.h b/src/feature/client/entrynodes.h index 6eede2c8d4..4b236dc80c 100644 --- a/src/feature/client/entrynodes.h +++ b/src/feature/client/entrynodes.h @@ -117,6 +117,13 @@ struct entry_guard_t { * confirmed guard. */ time_t confirmed_on_date; /* 0 if not confirmed */ /** + * In what order was this guard sampled? Guards with + * lower indices appear earlier on the sampled list, the confirmed list and + * the primary list as a result of Prop 310 + */ + int sampled_idx; + + /** * In what order was this guard confirmed? Guards with lower indices * appear earlier on the confirmed list. If the confirmed list is compacted, * this field corresponds to the index of this guard on the confirmed list. @@ -242,8 +249,9 @@ struct guard_selection_t { * Ordered list (from highest to lowest priority) of guards that we * have successfully contacted and decided to use. Every member of * this list is a member of sampled_entry_guards. Every member should - * have confirmed_on_date set, and have confirmed_idx greater than - * any earlier member of the list. + * have confirmed_on_date set. + * The ordering of the list should be by sampled idx. The reasoning behind + * it is linked to Proposal 310. * * This list is persistent. It is a subset of the elements in * sampled_entry_guards, and its pointers point to elements of @@ -271,6 +279,12 @@ struct guard_selection_t { * confirmed_entry_guards receive? */ int next_confirmed_idx; + /** What sampled_idx value should the next-added member of + * sampled_entry_guards receive? This should follow the size of the sampled + * list until sampled relays get pruned for some reason + */ + int next_sampled_idx; + }; struct entry_guard_handle_t; @@ -515,7 +529,8 @@ MOCK_DECL(STATIC circuit_guard_state_t *, STATIC entry_guard_t *entry_guard_add_to_sample(guard_selection_t *gs, const node_t *node); STATIC entry_guard_t *entry_guards_expand_sample(guard_selection_t *gs); -STATIC char *entry_guard_encode_for_state(entry_guard_t *guard); +STATIC char *entry_guard_encode_for_state(entry_guard_t *guard, int + dense_sampled_index); STATIC entry_guard_t *entry_guard_parse_from_state(const char *s); #define entry_guard_free(e) \ FREE_AND_NULL(entry_guard_t, entry_guard_free_, (e)) @@ -523,7 +538,7 @@ STATIC void entry_guard_free_(entry_guard_t *e); STATIC void entry_guards_update_filtered_sets(guard_selection_t *gs); STATIC int entry_guards_all_primary_guards_are_down(guard_selection_t *gs); /** - * @name Flags for sample_reachable_filtered_entry_guards() + * @name Flags for first_reachable_filtered_entry_guard() */ /**@{*/ #define SAMPLE_EXCLUDE_CONFIRMED (1u<<0) @@ -532,7 +547,7 @@ STATIC int entry_guards_all_primary_guards_are_down(guard_selection_t *gs); #define SAMPLE_NO_UPDATE_PRIMARY (1u<<3) #define SAMPLE_EXCLUDE_NO_DESCRIPTOR (1u<<4) /**@}*/ -STATIC entry_guard_t *sample_reachable_filtered_entry_guards( +STATIC entry_guard_t *first_reachable_filtered_entry_guard( guard_selection_t *gs, const entry_guard_restriction_t *rst, unsigned flags); diff --git a/src/feature/client/transports.c b/src/feature/client/transports.c index 2bdc0ae151..2eb05d6a67 100644 --- a/src/feature/client/transports.c +++ b/src/feature/client/transports.c @@ -1643,17 +1643,26 @@ pt_get_extra_info_descriptor_string(void) SMARTLIST_FOREACH_BEGIN(mp->transports, const transport_t *, t) { char *transport_args = NULL; + const char *addrport = NULL; /* If the transport proxy returned "0.0.0.0" as its address, and * we know our external IP address, use it. Otherwise, use the * returned address. */ - const char *addrport = NULL; - uint32_t external_ip_address = 0; - if (tor_addr_is_null(&t->addr) && - router_pick_published_address(get_options(), - &external_ip_address, 0) >= 0) { + if (tor_addr_is_null(&t->addr)) { tor_addr_t addr; - tor_addr_from_ipv4h(&addr, external_ip_address); + /* Attempt to find the IPv4 and then attempt to find the IPv6 if we + * can't find it. */ + bool found = relay_find_addr_to_publish(get_options(), AF_INET, + RELAY_FIND_ADDR_NO_FLAG, + &addr); + if (!found) { + found = relay_find_addr_to_publish(get_options(), AF_INET6, + RELAY_FIND_ADDR_NO_FLAG, &addr); + } + if (!found) { + log_err(LD_PT, "Unable to find address for transport %s", t->name); + continue; + } addrport = fmt_addrport(&addr, t->port); } else { addrport = fmt_addrport(&t->addr, t->port); diff --git a/src/feature/control/control.c b/src/feature/control/control.c index ee1026359d..ef707319a6 100644 --- a/src/feature/control/control.c +++ b/src/feature/control/control.c @@ -61,8 +61,12 @@ #include <sys/stat.h> #endif -/** Convert a connection_t* to an control_connection_t*; assert if the cast is - * invalid. */ +/** + * Cast a `connection_t *` to a `control_connection_t *`. + * + * Exit with an assertion failure if the input is not a + * `control_connection_t`. + **/ control_connection_t * TO_CONTROL_CONN(connection_t *c) { @@ -70,6 +74,18 @@ TO_CONTROL_CONN(connection_t *c) return DOWNCAST(control_connection_t, c); } +/** + * Cast a `const connection_t *` to a `const control_connection_t *`. + * + * Exit with an assertion failure if the input is not a + * `control_connection_t`. + **/ +const control_connection_t * +CONST_TO_CONTROL_CONN(const connection_t *c) +{ + return TO_CONTROL_CONN((connection_t*)c); +} + /** Create and add a new controller connection on <b>sock</b>. If * <b>CC_LOCAL_FD_IS_OWNER</b> is set in <b>flags</b>, this Tor process should * exit when the connection closes. If <b>CC_LOCAL_FD_IS_AUTHENTICATED</b> diff --git a/src/feature/control/control.h b/src/feature/control/control.h index 7e72b2736b..f884286ec7 100644 --- a/src/feature/control/control.h +++ b/src/feature/control/control.h @@ -13,6 +13,7 @@ #define TOR_CONTROL_H control_connection_t *TO_CONTROL_CONN(connection_t *); +const control_connection_t *CONST_TO_CONTROL_CONN(const connection_t *); #define CONTROL_CONN_STATE_MIN_ 1 /** State for a control connection: Authenticated and accepting v1 commands. */ diff --git a/src/feature/control/control_bootstrap.c b/src/feature/control/control_bootstrap.c index fee7612ba2..d4f2adde81 100644 --- a/src/feature/control/control_bootstrap.c +++ b/src/feature/control/control_bootstrap.c @@ -274,7 +274,7 @@ control_event_bootstrap_problem(const char *warn, const char *reason, const char *recommendation = "ignore"; int severity; char *or_id = NULL, *hostaddr = NULL; - or_connection_t *or_conn = NULL; + const or_connection_t *or_conn = NULL; /* bootstrap_percent must not be in "undefined" state here. */ tor_assert(status >= 0); @@ -301,7 +301,7 @@ control_event_bootstrap_problem(const char *warn, const char *reason, if (conn && conn->type == CONN_TYPE_OR) { /* XXX TO_OR_CONN can't deal with const */ - or_conn = TO_OR_CONN((connection_t *)conn); + or_conn = CONST_TO_OR_CONN(conn); or_id = tor_strdup(hex_str(or_conn->identity_digest, DIGEST_LEN)); } else { or_id = tor_strdup("?"); diff --git a/src/feature/control/control_cmd.c b/src/feature/control/control_cmd.c index d9a38011de..a8926c0b79 100644 --- a/src/feature/control/control_cmd.c +++ b/src/feature/control/control_cmd.c @@ -20,9 +20,12 @@ #include "core/or/circuitlist.h" #include "core/or/circuituse.h" #include "core/or/connection_edge.h" +#include "core/or/circuitstats.h" +#include "core/or/extendinfo.h" #include "feature/client/addressmap.h" #include "feature/client/dnsserv.h" #include "feature/client/entrynodes.h" +#include "feature/control/control_events.h" #include "feature/control/control.h" #include "feature/control/control_auth.h" #include "feature/control/control_cmd.h" @@ -55,6 +58,8 @@ #include "feature/rend/rend_encoded_v2_service_descriptor_st.h" #include "feature/rend/rend_service_descriptor_st.h" +#include "src/app/config/statefile.h" + static int control_setconf_helper(control_connection_t *conn, const control_cmd_args_t *args, int use_defaults); @@ -1396,6 +1401,34 @@ handle_control_dropguards(control_connection_t *conn, return 0; } +static const control_cmd_syntax_t droptimeouts_syntax = { + .max_args = 0, +}; + +/** Implementation for the DROPTIMEOUTS command. */ +static int +handle_control_droptimeouts(control_connection_t *conn, + const control_cmd_args_t *args) +{ + (void) args; /* We don't take arguments. */ + + static int have_warned = 0; + if (! have_warned) { + log_warn(LD_CONTROL, "DROPTIMEOUTS is dangerous; make sure you understand " + "the risks before using it. It may be removed in a future " + "version of Tor."); + have_warned = 1; + } + + circuit_build_times_reset(get_circuit_build_times_mutable()); + send_control_done(conn); + or_state_mark_dirty(get_or_state(), 0); + cbt_control_event_buildtimeout_set(get_circuit_build_times(), + BUILDTIMEOUT_SET_EVENT_RESET); + + return 0; +} + static const char *hsfetch_keywords[] = { "SERVER", NULL, }; @@ -1430,7 +1463,7 @@ handle_control_hsfetch(control_connection_t *conn, rend_valid_descriptor_id(arg1 + v2_str_len) && base32_decode(digest, sizeof(digest), arg1 + v2_str_len, REND_DESC_ID_V2_LEN_BASE32) == - REND_DESC_ID_V2_LEN_BASE32) { + sizeof(digest)) { /* We have a well formed version 2 descriptor ID. Keep the decoded value * of the id. */ desc_id = digest; @@ -2331,6 +2364,7 @@ static const control_cmd_def_t CONTROL_COMMANDS[] = ONE_LINE(protocolinfo, 0), ONE_LINE(authchallenge, CMD_FL_WIPE), ONE_LINE(dropguards, 0), + ONE_LINE(droptimeouts, 0), ONE_LINE(hsfetch, 0), MULTLINE(hspost, 0), ONE_LINE(add_onion, CMD_FL_WIPE), diff --git a/src/feature/control/control_events.c b/src/feature/control/control_events.c index 916ccea875..8e69c772f6 100644 --- a/src/feature/control/control_events.c +++ b/src/feature/control/control_events.c @@ -17,6 +17,7 @@ #include "core/mainloop/mainloop.h" #include "core/or/channeltls.h" #include "core/or/circuitlist.h" +#include "core/or/circuitstats.h" #include "core/or/command.h" #include "core/or/connection_edge.h" #include "core/or/connection_or.h" @@ -141,6 +142,64 @@ clear_circ_bw_fields(void) SMARTLIST_FOREACH_END(circ); } +/* Helper to emit the BUILDTIMEOUT_SET circuit build time event */ +void +cbt_control_event_buildtimeout_set(const circuit_build_times_t *cbt, + buildtimeout_set_event_t type) +{ + char *args = NULL; + double qnt; + double timeout_rate = 0.0; + double close_rate = 0.0; + + switch (type) { + case BUILDTIMEOUT_SET_EVENT_RESET: + case BUILDTIMEOUT_SET_EVENT_SUSPENDED: + case BUILDTIMEOUT_SET_EVENT_DISCARD: + qnt = 1.0; + break; + case BUILDTIMEOUT_SET_EVENT_COMPUTED: + case BUILDTIMEOUT_SET_EVENT_RESUME: + default: + qnt = circuit_build_times_quantile_cutoff(); + break; + } + + /* The timeout rate is the ratio of the timeout count over + * the total number of circuits attempted. The total number of + * circuits is (timeouts+succeeded), since every circuit + * either succeeds, or times out. "Closed" circuits are + * MEASURE_TIMEOUT circuits whose measurement period expired. + * All MEASURE_TIMEOUT circuits are counted in the timeouts stat + * before transitioning to MEASURE_TIMEOUT (in + * circuit_build_times_mark_circ_as_measurement_only()). + * MEASURE_TIMEOUT circuits that succeed are *not* counted as + * "succeeded". See circuit_build_times_handle_completed_hop(). + * + * We cast the denominator + * to promote it to double before the addition, to avoid int32 + * overflow. */ + const double total_circuits = + ((double)cbt->num_circ_timeouts) + cbt->num_circ_succeeded; + if (total_circuits >= 1.0) { + timeout_rate = cbt->num_circ_timeouts / total_circuits; + close_rate = cbt->num_circ_closed / total_circuits; + } + + tor_asprintf(&args, "TOTAL_TIMES=%lu " + "TIMEOUT_MS=%lu XM=%lu ALPHA=%f CUTOFF_QUANTILE=%f " + "TIMEOUT_RATE=%f CLOSE_MS=%lu CLOSE_RATE=%f", + (unsigned long)cbt->total_build_times, + (unsigned long)cbt->timeout_ms, + (unsigned long)cbt->Xm, cbt->alpha, qnt, + timeout_rate, + (unsigned long)cbt->close_ms, + close_rate); + + control_event_buildtimeout_set(type, args); + + tor_free(args); +} /** Set <b>global_event_mask*</b> to the bitwise OR of each live control * connection's event_mask field. */ void diff --git a/src/feature/control/control_events.h b/src/feature/control/control_events.h index 4a5492b510..0c8448e0f8 100644 --- a/src/feature/control/control_events.h +++ b/src/feature/control/control_events.h @@ -223,6 +223,8 @@ void control_event_hs_descriptor_content(const char *onion_address, const char *desc_id, const char *hsdir_fp, const char *content); +void cbt_control_event_buildtimeout_set(const circuit_build_times_t *cbt, + buildtimeout_set_event_t type); void control_events_free_all(void); diff --git a/src/feature/control/control_getinfo.c b/src/feature/control/control_getinfo.c index 0823acbe07..3e4feadded 100644 --- a/src/feature/control/control_getinfo.c +++ b/src/feature/control/control_getinfo.c @@ -51,6 +51,7 @@ #include "feature/rend/rendcache.h" #include "feature/stats/geoip_stats.h" #include "feature/stats/predict_ports.h" +#include "feature/stats/rephist.h" #include "lib/version/torversion.h" #include "lib/encoding/kvline.h" @@ -131,12 +132,13 @@ getinfo_helper_misc(control_connection_t *conn, const char *question, } else if (!strcmp(question, "features/names")) { *answer = tor_strdup("VERBOSE_NAMES EXTENDED_EVENTS"); } else if (!strcmp(question, "address")) { - uint32_t addr; - if (router_pick_published_address(get_options(), &addr, 0) < 0) { + tor_addr_t addr; + if (!relay_find_addr_to_publish(get_options(), AF_INET, + RELAY_FIND_ADDR_CACHE_ONLY, &addr)) { *errmsg = "Address unknown"; return -1; } - *answer = tor_dup_ip(addr); + *answer = tor_addr_to_str_dup(&addr); tor_assert_nonfatal(*answer); } else if (!strcmp(question, "traffic/read")) { tor_asprintf(answer, "%"PRIu64, (get_bytes_read())); @@ -1278,15 +1280,18 @@ getinfo_helper_events(control_connection_t *control_conn, *answer = tor_strdup(directories_have_accepted_server_descriptor() ? "1" : "0"); } else if (!strcmp(question, "status/reachability-succeeded/or")) { - *answer = tor_strdup(check_whether_orport_reachable(options) ? - "1" : "0"); + *answer = tor_strdup( + router_all_orports_seem_reachable(options) ? + "1" : "0"); } else if (!strcmp(question, "status/reachability-succeeded/dir")) { - *answer = tor_strdup(check_whether_dirport_reachable(options) ? - "1" : "0"); + *answer = tor_strdup( + router_dirport_seems_reachable(options) ? + "1" : "0"); } else if (!strcmp(question, "status/reachability-succeeded")) { - tor_asprintf(answer, "OR=%d DIR=%d", - check_whether_orport_reachable(options) ? 1 : 0, - check_whether_dirport_reachable(options) ? 1 : 0); + tor_asprintf( + answer, "OR=%d DIR=%d", + router_all_orports_seem_reachable(options) ? 1 : 0, + router_dirport_seems_reachable(options) ? 1 : 0); } else if (!strcmp(question, "status/bootstrap-phase")) { *answer = control_event_boot_last_msg(); } else if (!strcmpstart(question, "status/version/")) { @@ -1437,6 +1442,39 @@ getinfo_helper_liveness(control_connection_t *control_conn, return 0; } +/** Implementation helper for GETINFO: answers queries about circuit onion + * handshake rephist values */ +STATIC int +getinfo_helper_rephist(control_connection_t *control_conn, + const char *question, char **answer, + const char **errmsg) +{ + (void) control_conn; + (void) errmsg; + int result; + + if (!strcmp(question, "stats/ntor/assigned")) { + result = + rep_hist_get_circuit_handshake_assigned(ONION_HANDSHAKE_TYPE_NTOR); + } else if (!strcmp(question, "stats/ntor/requested")) { + result = + rep_hist_get_circuit_handshake_requested(ONION_HANDSHAKE_TYPE_NTOR); + } else if (!strcmp(question, "stats/tap/assigned")) { + result = + rep_hist_get_circuit_handshake_assigned(ONION_HANDSHAKE_TYPE_TAP); + } else if (!strcmp(question, "stats/tap/requested")) { + result = + rep_hist_get_circuit_handshake_requested(ONION_HANDSHAKE_TYPE_TAP); + } else { + *errmsg = "Unrecognized handshake type"; + return -1; + } + + tor_asprintf(answer, "%d", result); + + return 0; +} + /** Implementation helper for GETINFO: answers queries about shared random * value. */ static int @@ -1661,6 +1699,16 @@ static const getinfo_item_t getinfo_items[] = { "Onion services detached from the control connection."), ITEM("sr/current", sr, "Get current shared random value."), ITEM("sr/previous", sr, "Get previous shared random value."), + PREFIX("stats/ntor/", rephist, "NTor circuit handshake stats."), + ITEM("stats/ntor/assigned", rephist, + "Assigned NTor circuit handshake stats."), + ITEM("stats/ntor/requested", rephist, + "Requested NTor circuit handshake stats."), + PREFIX("stats/tap/", rephist, "TAP circuit handshake stats."), + ITEM("stats/tap/assigned", rephist, + "Assigned TAP circuit handshake stats."), + ITEM("stats/tap/requested", rephist, + "Requested TAP circuit handshake stats."), { NULL, NULL, NULL, 0 } }; diff --git a/src/feature/control/control_getinfo.h b/src/feature/control/control_getinfo.h index 0ada49258e..f61d632446 100644 --- a/src/feature/control/control_getinfo.h +++ b/src/feature/control/control_getinfo.h @@ -60,6 +60,10 @@ STATIC int getinfo_helper_current_time( control_connection_t *control_conn, const char *question, char **answer, const char **errmsg); +STATIC int getinfo_helper_rephist( + control_connection_t *control_conn, + const char *question, char **answer, + const char **errmsg); #endif /* defined(CONTROL_GETINFO_PRIVATE) */ #endif /* !defined(TOR_CONTROL_GETINFO_H) */ diff --git a/src/feature/control/control_hs.c b/src/feature/control/control_hs.c index f5b331de9a..c8de03b318 100644 --- a/src/feature/control/control_hs.c +++ b/src/feature/control/control_hs.c @@ -291,7 +291,8 @@ handle_control_onion_client_auth_view(control_connection_t *conn, if (argc >= 1) { hsaddress = smartlist_get(args->args, 0); if (!hs_address_is_valid(hsaddress)) { - control_printf_endreply(conn, 512, "Invalid v3 addr \"%s\"", hsaddress); + control_printf_endreply(conn, 512, "Invalid v3 address \"%s\"", + hsaddress); goto err; } } diff --git a/src/feature/dirauth/dirauth_config.c b/src/feature/dirauth/dirauth_config.c index a0b6de7eca..1ffd33e5f1 100644 --- a/src/feature/dirauth/dirauth_config.c +++ b/src/feature/dirauth/dirauth_config.c @@ -77,8 +77,8 @@ options_validate_dirauth_mode(const or_options_t *old_options, return 0; /* confirm that our address isn't broken, so we can complain now */ - uint32_t tmp; - if (resolve_my_address(LOG_WARN, options, &tmp, NULL, NULL) < 0) + tor_addr_t tmp; + if (!find_my_address(options, AF_INET, LOG_WARN, &tmp, NULL, NULL)) REJECT("Failed to resolve/guess local address. See logs for details."); if (!options->ContactInfo && !options->TestingTorNetwork) diff --git a/src/feature/dirauth/dirauth_options.inc b/src/feature/dirauth/dirauth_options.inc index 21f4996c39..40ef7c3bab 100644 --- a/src/feature/dirauth/dirauth_options.inc +++ b/src/feature/dirauth/dirauth_options.inc @@ -44,6 +44,13 @@ CONF_VAR(AuthDirSharedRandomness, BOOL, 0, "1") /* NOTE: remove this option someday. */ CONF_VAR(AuthDirTestEd25519LinkKeys, BOOL, 0, "1") +/** + * Bool (default 1): As an authority, should we launch tests for + * reachability, and use those results to vote on "Running"? If 0, + * we assume that every relay is Runnning. + **/ +CONF_VAR(AuthDirTestReachability, BOOL, 0, "1") + /** Authority only: key=value pairs that we add to our networkstatus * consensus vote on the 'params' line. */ CONF_VAR(ConsensusParams, STRING, 0, NULL) diff --git a/src/feature/dirauth/dirvote.c b/src/feature/dirauth/dirvote.c index a1a530b7fa..080edd92f1 100644 --- a/src/feature/dirauth/dirvote.c +++ b/src/feature/dirauth/dirvote.c @@ -225,7 +225,6 @@ format_networkstatus_vote(crypto_pk_t *private_signing_key, smartlist_t *chunks = smartlist_new(); char fingerprint[FINGERPRINT_LEN+1]; char digest[DIGEST_LEN]; - uint32_t addr; char *protocols_lines = NULL; char *client_versions_line = NULL, *server_versions_line = NULL; char *shared_random_vote_str = NULL; @@ -237,8 +236,6 @@ format_networkstatus_vote(crypto_pk_t *private_signing_key, voter = smartlist_get(v3_ns->voters, 0); - addr = voter->addr; - base16_encode(fingerprint, sizeof(fingerprint), v3_ns->cert->cache_info.identity_digest, DIGEST_LEN); @@ -322,7 +319,7 @@ format_networkstatus_vote(crypto_pk_t *private_signing_key, tor_free(digest_algo_b64_digest_bw_file); } - const char *ip_str = fmt_addr32(addr); + const char *ip_str = fmt_addr(&voter->ipv4_addr); if (ip_str[0]) { smartlist_add_asprintf(chunks, @@ -358,7 +355,7 @@ format_networkstatus_vote(crypto_pk_t *private_signing_key, bw_headers_line ? bw_headers_line : "", bw_file_digest ? bw_file_digest: "", voter->nickname, fingerprint, voter->address, - ip_str, voter->dir_port, voter->or_port, + ip_str, voter->ipv4_dirport, voter->ipv4_orport, voter->contact, shared_random_vote_str ? shared_random_vote_str : ""); @@ -636,9 +633,12 @@ compare_vote_rs(const vote_routerstatus_t *a, const vote_routerstatus_t *b) if ((r = strcmp(b->status.nickname, a->status.nickname))) return r; - CMP_FIELD(unsigned, int, addr); - CMP_FIELD(unsigned, int, or_port); - CMP_FIELD(unsigned, int, dir_port); + if ((r = tor_addr_compare(&a->status.ipv4_addr, &b->status.ipv4_addr, + CMP_EXACT))) { + return r; + } + CMP_FIELD(unsigned, int, ipv4_orport); + CMP_FIELD(unsigned, int, ipv4_dirport); return 0; } @@ -1740,9 +1740,9 @@ networkstatus_compute_consensus(smartlist_t *votes, smartlist_add_asprintf(chunks, "dir-source %s%s %s %s %s %d %d\n", voter->nickname, e->is_legacy ? "-legacy" : "", - fingerprint, voter->address, fmt_addr32(voter->addr), - voter->dir_port, - voter->or_port); + fingerprint, voter->address, fmt_addr(&voter->ipv4_addr), + voter->ipv4_dirport, + voter->ipv4_orport); if (! e->is_legacy) { smartlist_add_asprintf(chunks, "contact %s\n" @@ -2039,10 +2039,10 @@ networkstatus_compute_consensus(smartlist_t *votes, memcpy(rs_out.identity_digest, current_rsa_id, DIGEST_LEN); memcpy(rs_out.descriptor_digest, rs->status.descriptor_digest, DIGEST_LEN); - rs_out.addr = rs->status.addr; + tor_addr_copy(&rs_out.ipv4_addr, &rs->status.ipv4_addr); rs_out.published_on = rs->status.published_on; - rs_out.dir_port = rs->status.dir_port; - rs_out.or_port = rs->status.or_port; + rs_out.ipv4_dirport = rs->status.ipv4_dirport; + rs_out.ipv4_orport = rs->status.ipv4_orport; tor_addr_copy(&rs_out.ipv6_addr, &alt_orport.addr); rs_out.ipv6_orport = alt_orport.port; rs_out.has_bandwidth = 0; @@ -4220,12 +4220,14 @@ compare_routerinfo_by_ip_and_bw_(const void **a, const void **b) uint32_t bw_kb_first, bw_kb_second; const node_t *node_first, *node_second; int first_is_running, second_is_running; + uint32_t first_ipv4h = tor_addr_to_ipv4h(&first->ipv4_addr); + uint32_t second_ipv4h = tor_addr_to_ipv4h(&second->ipv4_addr); /* we return -1 if first should appear before second... that is, * if first is a better router. */ - if (first->addr < second->addr) + if (first_ipv4h < second_ipv4h) return -1; - else if (first->addr > second->addr) + else if (first_ipv4h > second_ipv4h) return 1; /* Potentially, this next bit could cause k n lg n memeq calls. But in @@ -4276,7 +4278,7 @@ get_possible_sybil_list(const smartlist_t *routers) const dirauth_options_t *options = dirauth_get_options(); digestmap_t *omit_as_sybil; smartlist_t *routers_by_ip = smartlist_new(); - uint32_t last_addr; + tor_addr_t last_addr = TOR_ADDR_NULL; int addr_count; /* Allow at most this number of Tor servers on a single IP address, ... */ int max_with_same_addr = options->AuthDirMaxServersPerAddr; @@ -4287,11 +4289,10 @@ get_possible_sybil_list(const smartlist_t *routers) smartlist_sort(routers_by_ip, compare_routerinfo_by_ip_and_bw_); omit_as_sybil = digestmap_new(); - last_addr = 0; addr_count = 0; SMARTLIST_FOREACH_BEGIN(routers_by_ip, routerinfo_t *, ri) { - if (last_addr != ri->addr) { - last_addr = ri->addr; + if (!tor_addr_eq(&last_addr, &ri->ipv4_addr)) { + tor_addr_copy(&last_addr, &ri->ipv4_addr); addr_count = 1; } else if (++addr_count > max_with_same_addr) { digestmap_set(omit_as_sybil, ri->cache_info.identity_digest, ri); @@ -4464,7 +4465,7 @@ dirserv_generate_networkstatus_vote_obj(crypto_pk_t *private_key, const or_options_t *options = get_options(); const dirauth_options_t *d_options = dirauth_get_options(); networkstatus_t *v3_out = NULL; - uint32_t addr; + tor_addr_t addr; char *hostname = NULL, *client_versions = NULL, *server_versions = NULL; const char *contact; smartlist_t *routers, *routerstatuses; @@ -4493,13 +4494,13 @@ dirserv_generate_networkstatus_vote_obj(crypto_pk_t *private_key, log_err(LD_BUG, "Error computing identity key digest"); return NULL; } - if (resolve_my_address(LOG_WARN, options, &addr, NULL, &hostname)<0) { + if (!find_my_address(options, AF_INET, LOG_WARN, &addr, NULL, &hostname)) { log_warn(LD_NET, "Couldn't resolve my hostname"); return NULL; } if (!hostname || !strchr(hostname, '.')) { tor_free(hostname); - hostname = tor_dup_ip(addr); + hostname = tor_addr_to_str_dup(&addr); } if (!hostname) { @@ -4680,9 +4681,9 @@ dirserv_generate_networkstatus_vote_obj(crypto_pk_t *private_key, /* These are hardwired, to avoid disaster. */ v3_out->recommended_relay_protocols = - tor_strdup(DIRVOTE_RECCOMEND_RELAY_PROTO); + tor_strdup(DIRVOTE_RECOMMEND_RELAY_PROTO); v3_out->recommended_client_protocols = - tor_strdup(DIRVOTE_RECCOMEND_CLIENT_PROTO); + tor_strdup(DIRVOTE_RECOMMEND_CLIENT_PROTO); v3_out->required_relay_protocols = tor_strdup(DIRVOTE_REQUIRE_RELAY_PROTO); @@ -4723,9 +4724,9 @@ dirserv_generate_networkstatus_vote_obj(crypto_pk_t *private_key, memcpy(voter->identity_digest, identity_digest, DIGEST_LEN); voter->sigs = smartlist_new(); voter->address = hostname; - voter->addr = addr; - voter->dir_port = router_get_advertised_dir_port(options, 0); - voter->or_port = router_get_advertised_or_port(options); + tor_addr_copy(&voter->ipv4_addr, &addr); + voter->ipv4_dirport = routerconf_find_dir_port(options, 0); + voter->ipv4_orport = routerconf_find_or_port(options, AF_INET); voter->contact = tor_strdup(contact); if (options->V3AuthUseLegacyKey) { authority_cert_t *c = get_my_v3_legacy_cert(); diff --git a/src/feature/dirauth/dirvote.h b/src/feature/dirauth/dirvote.h index 3ab40367ae..9cc87489b4 100644 --- a/src/feature/dirauth/dirvote.h +++ b/src/feature/dirauth/dirvote.h @@ -242,15 +242,15 @@ STATIC microdesc_t *dirvote_create_microdescriptor(const routerinfo_t *ri, /** The recommended relay protocols for this authority's votes. * Recommending a new protocol causes old tor versions to log a warning. */ -#define DIRVOTE_RECCOMEND_RELAY_PROTO \ - "Cons=1-2 Desc=1-2 DirCache=1 HSDir=1 HSIntro=3 HSRend=1 " \ - "Link=4 Microdesc=1-2 Relay=2" +#define DIRVOTE_RECOMMEND_RELAY_PROTO \ + "Cons=1-2 Desc=1-2 DirCache=2 HSDir=2 HSIntro=4 HSRend=2 " \ + "Link=5 LinkAuth=3 Microdesc=1-2 Relay=2" /** The recommended client protocols for this authority's votes. * Recommending a new protocol causes old tor versions to log a warning. */ -#define DIRVOTE_RECCOMEND_CLIENT_PROTO \ - "Cons=1-2 Desc=1-2 DirCache=1 HSDir=1 HSIntro=3 HSRend=1 " \ - "Link=4 Microdesc=1-2 Relay=2" +#define DIRVOTE_RECOMMEND_CLIENT_PROTO \ + "Cons=1-2 Desc=1-2 DirCache=2 HSDir=2 HSIntro=4 HSRend=2 " \ + "Link=5 LinkAuth=3 Microdesc=1-2 Relay=2" /** The required relay protocols for this authority's votes. * WARNING: Requiring a new protocol causes old tor versions to shut down. diff --git a/src/feature/dirauth/process_descs.c b/src/feature/dirauth/process_descs.c index 5025d0ae39..b08ffeba07 100644 --- a/src/feature/dirauth/process_descs.c +++ b/src/feature/dirauth/process_descs.c @@ -56,8 +56,9 @@ static was_router_added_t dirserv_add_extrainfo(extrainfo_t *ei, static uint32_t dirserv_get_status_impl(const char *id_digest, const ed25519_public_key_t *ed25519_public_key, - const char *nickname, uint32_t addr, uint16_t or_port, - const char *platform, const char **msg, int severity); + const char *nickname, const tor_addr_t *ipv4_addr, + uint16_t ipv4_orport, const char *platform, + const char **msg, int severity); /** Should be static; exposed for testing. */ static authdir_config_t *fingerprint_list = NULL; @@ -307,9 +308,9 @@ dirserv_router_get_status(const routerinfo_t *router, const char **msg, /* This has an ed25519 identity key. */ signing_key = &router->cache_info.signing_key_cert->signing_key; } - r = dirserv_get_status_impl(d, signing_key, router->nickname, router->addr, - router->or_port, router->platform, msg, - severity); + r = dirserv_get_status_impl(d, signing_key, router->nickname, + &router->ipv4_addr, router->ipv4_orport, + router->platform, msg, severity); if (r) return r; @@ -378,7 +379,8 @@ dirserv_would_reject_router(const routerstatus_t *rs, memcpy(&pk.pubkey, vrs->ed25519_id, ED25519_PUBKEY_LEN); res = dirserv_get_status_impl(rs->identity_digest, &pk, rs->nickname, - rs->addr, rs->or_port, NULL, NULL, LOG_DEBUG); + &rs->ipv4_addr, rs->ipv4_orport, NULL, NULL, + LOG_DEBUG); return (res & RTR_REJECT) != 0; } @@ -409,11 +411,11 @@ dirserv_rejects_tor_version(const char *platform, return true; } - /* Series between Tor 0.3.6 and 0.4.1.4-rc inclusive are unsupported. - * Reject them. 0.3.6.0-alpha-dev only existed for a short time, before - * it was renamed to 0.4.0.0-alpha-dev. */ + /* Series between Tor 0.3.6 and 0.4.1 inclusive are unsupported. Reject + * them. 0.3.6.0-alpha-dev only existed for a short time, before it was + * renamed to 0.4.0.0-alpha-dev. */ if (tor_version_as_new_as(platform,"0.3.6.0-alpha-dev") && - !tor_version_as_new_as(platform,"0.4.1.5")) { + !tor_version_as_new_as(platform,"0.4.2.1-alpha")) { if (msg) { *msg = please_upgrade_string; } @@ -433,8 +435,9 @@ dirserv_rejects_tor_version(const char *platform, static uint32_t dirserv_get_status_impl(const char *id_digest, const ed25519_public_key_t *ed25519_public_key, - const char *nickname, uint32_t addr, uint16_t or_port, - const char *platform, const char **msg, int severity) + const char *nickname, const tor_addr_t *ipv4_addr, + uint16_t ipv4_orport, const char *platform, + const char **msg, int severity) { uint32_t result = 0; rtr_flags_t *status_by_digest; @@ -485,16 +488,16 @@ dirserv_get_status_impl(const char *id_digest, *msg = "Fingerprint and/or ed25519 identity is marked invalid"; } - if (authdir_policy_badexit_address(addr, or_port)) { + if (authdir_policy_badexit_address(ipv4_addr, ipv4_orport)) { log_fn(severity, LD_DIRSERV, "Marking '%s' as bad exit because of address '%s'", - nickname, fmt_addr32(addr)); + nickname, fmt_addr(ipv4_addr)); result |= RTR_BADEXIT; } - if (!authdir_policy_permits_address(addr, or_port)) { + if (!authdir_policy_permits_address(ipv4_addr, ipv4_orport)) { log_fn(severity, LD_DIRSERV, "Rejecting '%s' because of address '%s'", - nickname, fmt_addr32(addr)); + nickname, fmt_addr(ipv4_addr)); if (msg) *msg = "Suspicious relay address range -- if you think this is a " "mistake please set a valid email address in ContactInfo and " @@ -502,10 +505,10 @@ dirserv_get_status_impl(const char *id_digest, "your address(es) and fingerprint(s)?"; return RTR_REJECT; } - if (!authdir_policy_valid_address(addr, or_port)) { + if (!authdir_policy_valid_address(ipv4_addr, ipv4_orport)) { log_fn(severity, LD_DIRSERV, "Not marking '%s' valid because of address '%s'", - nickname, fmt_addr32(addr)); + nickname, fmt_addr(ipv4_addr)); result |= RTR_INVALID; } @@ -534,13 +537,11 @@ dirserv_free_fingerprint_list(void) STATIC int dirserv_router_has_valid_address(routerinfo_t *ri) { - tor_addr_t addr; - if (get_options()->DirAllowPrivateAddresses) return 0; /* whatever it is, we're fine with it */ - tor_addr_from_ipv4h(&addr, ri->addr); - if (tor_addr_is_null(&addr) || tor_addr_is_internal(&addr, 0)) { + if (tor_addr_is_null(&ri->ipv4_addr) || + tor_addr_is_internal(&ri->ipv4_addr, 0)) { log_info(LD_DIRSERV, "Router %s published internal IPv4 address. Refusing.", router_describe(ri)); diff --git a/src/feature/dirauth/reachability.c b/src/feature/dirauth/reachability.c index 65fa27ed80..eb88b4aa07 100644 --- a/src/feature/dirauth/reachability.c +++ b/src/feature/dirauth/reachability.c @@ -84,7 +84,7 @@ dirserv_orconn_tls_done(const tor_addr_t *addr, log_info(LD_DIRSERV, "Found router %s to be reachable at %s:%d. Yay.", router_describe(ri), tor_addr_to_str(addrstr, addr, sizeof(addrstr), 1), - ri->or_port); + ri->ipv4_orport); if (tor_addr_family(addr) == AF_INET) { rep_hist_note_router_reachable(digest_rcvd, addr, or_port, now); node->last_reachable = now; @@ -105,6 +105,8 @@ dirserv_should_launch_reachability_test(const routerinfo_t *ri, { if (!authdir_mode_handles_descs(get_options(), ri->purpose)) return 0; + if (! dirauth_get_options()->AuthDirTestReachability) + return 0; if (!ri_old) { /* New router: Launch an immediate reachability test, so we will have an * opinion soon in case we're generating a consensus soon */ @@ -130,7 +132,6 @@ dirserv_single_reachability_test(time_t now, routerinfo_t *router) const dirauth_options_t *dirauth_options = dirauth_get_options(); channel_t *chan = NULL; const node_t *node = NULL; - tor_addr_t router_addr; const ed25519_public_key_t *ed_id_key; (void) now; @@ -148,9 +149,9 @@ dirserv_single_reachability_test(time_t now, routerinfo_t *router) /* IPv4. */ log_debug(LD_OR,"Testing reachability of %s at %s:%u.", - router->nickname, fmt_addr32(router->addr), router->or_port); - tor_addr_from_ipv4h(&router_addr, router->addr); - chan = channel_tls_connect(&router_addr, router->or_port, + router->nickname, fmt_addr(&router->ipv4_addr), + router->ipv4_orport); + chan = channel_tls_connect(&router->ipv4_addr, router->ipv4_orport, router->cache_info.identity_digest, ed_id_key); if (chan) command_setup_channel(chan); @@ -189,6 +190,9 @@ dirserv_test_reachability(time_t now) * the testing, and directory authorities are easy to upgrade. Let's * wait til 0.2.0. -RD */ // time_t cutoff = now - ROUTER_MAX_AGE_TO_PUBLISH; + if (! dirauth_get_options()->AuthDirTestReachability) + return; + routerlist_t *rl = router_get_routerlist(); static char ctr = 0; int bridge_auth = authdir_mode_bridge(get_options()); diff --git a/src/feature/dirauth/voteflags.c b/src/feature/dirauth/voteflags.c index 477eb6f0b7..3938b61adb 100644 --- a/src/feature/dirauth/voteflags.c +++ b/src/feature/dirauth/voteflags.c @@ -487,7 +487,6 @@ dirserv_set_router_is_running(routerinfo_t *router, time_t now) unreachable. */ int answer; - const or_options_t *options = get_options(); const dirauth_options_t *dirauth_options = dirauth_get_options(); node_t *node = node_get_mutable_by_id(router->cache_info.identity_digest); tor_assert(node); @@ -501,8 +500,9 @@ dirserv_set_router_is_running(routerinfo_t *router, time_t now) /* A hibernating router is down unless we (somehow) had contact with it * since it declared itself to be hibernating. */ answer = 0; - } else if (options->AssumeReachable) { - /* If AssumeReachable, everybody is up unless they say they are down! */ + } else if (! dirauth_options->AuthDirTestReachability) { + /* If we aren't testing reachability, then everybody is up unless they say + * they are down. */ answer = 1; } else { /* Otherwise, a router counts as up if we found all announced OR diff --git a/src/feature/dircache/dircache.c b/src/feature/dircache/dircache.c index ca127720f2..efcae41084 100644 --- a/src/feature/dircache/dircache.c +++ b/src/feature/dircache/dircache.c @@ -142,7 +142,7 @@ write_http_response_header_impl(dir_connection_t *conn, ssize_t length, if (type) { buf_add_printf(buf, "Content-Type: %s\r\n", type); } - if (!is_local_addr(&conn->base_.addr)) { + if (!is_local_to_resolve_addr(&conn->base_.addr)) { /* Don't report the source address for a nearby/private connection. * Otherwise we tend to mis-report in cases where incoming ports are * being forwarded to a Tor server running behind the firewall. */ @@ -1614,7 +1614,8 @@ directory_handle_command_post,(dir_connection_t *conn, const char *headers, if (!public_server_mode(options)) { log_info(LD_DIR, "Rejected dir post request from %s " - "since we're not a public relay.", conn->base_.address); + "since we're not a public relay.", + connection_describe_peer(TO_CONN(conn))); write_short_http_response(conn, 503, "Not acting as a public relay"); goto done; } @@ -1630,7 +1631,8 @@ directory_handle_command_post,(dir_connection_t *conn, const char *headers, !strcmpstart(url,"/tor/rendezvous2/publish")) { if (rend_cache_store_v2_desc_as_dir(body) < 0) { log_warn(LD_REND, "Rejected v2 rend descriptor (body size %d) from %s.", - (int)body_len, conn->base_.address); + (int)body_len, + connection_describe_peer(TO_CONN(conn))); write_short_http_response(conn, 400, "Invalid v2 service descriptor rejected"); } else { @@ -1686,7 +1688,8 @@ directory_handle_command_post,(dir_connection_t *conn, const char *headers, log_info(LD_DIRSERV, "Rejected router descriptor or extra-info from %s " "(\"%s\").", - conn->base_.address, msg); + connection_describe_peer(TO_CONN(conn)), + msg); write_short_http_response(conn, 400, msg); } goto done; @@ -1701,7 +1704,8 @@ directory_handle_command_post,(dir_connection_t *conn, const char *headers, } else { tor_assert(msg); log_warn(LD_DIRSERV, "Rejected vote from %s (\"%s\").", - conn->base_.address, msg); + connection_describe_peer(TO_CONN(conn)), + msg); write_short_http_response(conn, status, msg); } goto done; @@ -1714,7 +1718,8 @@ directory_handle_command_post,(dir_connection_t *conn, const char *headers, write_short_http_response(conn, 200, msg?msg:"Signatures stored"); } else { log_warn(LD_DIR, "Unable to store signatures posted by %s: %s", - conn->base_.address, msg?msg:"???"); + connection_describe_peer(TO_CONN(conn)), + msg?msg:"???"); write_short_http_response(conn, 400, msg?msg:"Unable to store signatures"); } @@ -1775,8 +1780,8 @@ directory_handle_command(dir_connection_t *conn) &body, &body_len, MAX_DIR_UL_SIZE, 0)) { case -1: /* overflow */ log_warn(LD_DIRSERV, - "Request too large from address '%s' to DirPort. Closing.", - safe_str(conn->base_.address)); + "Request too large from %s to DirPort. Closing.", + connection_describe_peer(TO_CONN(conn))); return -1; case 0: log_debug(LD_DIRSERV,"command not all here yet."); diff --git a/src/feature/dirclient/dir_server_st.h b/src/feature/dirclient/dir_server_st.h index 37fa3148a7..57530a571b 100644 --- a/src/feature/dirclient/dir_server_st.h +++ b/src/feature/dirclient/dir_server_st.h @@ -24,10 +24,10 @@ struct dir_server_t { char *address; /**< Hostname. */ /* XX/teor - why do we duplicate the address and port fields here and in * fake_status? Surely we could just use fake_status (#17867). */ + tor_addr_t ipv4_addr; + uint16_t ipv4_dirport; /**< Directory port. */ + uint16_t ipv4_orport; /**< OR port: Used for tunneling connections. */ tor_addr_t ipv6_addr; /**< IPv6 address if present; AF_UNSPEC if not */ - uint32_t addr; /**< IPv4 address. */ - uint16_t dir_port; /**< Directory port. */ - uint16_t or_port; /**< OR port: Used for tunneling connections. */ uint16_t ipv6_orport; /**< OR port corresponding to ipv6_addr. */ double weight; /** Weight used when selecting this node at random */ char digest[DIGEST_LEN]; /**< Digest of identity key. */ diff --git a/src/feature/dirclient/dirclient.c b/src/feature/dirclient/dirclient.c index ae1e018df2..337fa4c965 100644 --- a/src/feature/dirclient/dirclient.c +++ b/src/feature/dirclient/dirclient.c @@ -654,11 +654,11 @@ directory_choose_address_routerstatus(const routerstatus_t *status, /* ORPort connections */ if (indirection == DIRIND_ANONYMOUS) { - if (status->addr) { + if (!tor_addr_is_null(&status->ipv4_addr)) { /* Since we're going to build a 3-hop circuit and ask the 2nd relay * to extend to this address, always use the primary (IPv4) OR address */ - tor_addr_from_ipv4h(&use_or_ap->addr, status->addr); - use_or_ap->port = status->or_port; + tor_addr_copy(&use_or_ap->addr, &status->ipv4_addr); + use_or_ap->port = status->ipv4_orport; have_or = 1; } } else if (indirection == DIRIND_ONEHOP) { @@ -686,12 +686,14 @@ directory_choose_address_routerstatus(const routerstatus_t *status, * connect to it. */ if (!have_or && !have_dir) { static int logged_backtrace = 0; + char *ipv6_str = tor_addr_to_str_dup(&status->ipv6_addr); log_info(LD_BUG, "Rejected all OR and Dir addresses from %s when " "launching an outgoing directory connection to: IPv4 %s OR %d " "Dir %d IPv6 %s OR %d Dir %d", routerstatus_describe(status), - fmt_addr32(status->addr), status->or_port, - status->dir_port, fmt_addr(&status->ipv6_addr), - status->ipv6_orport, status->dir_port); + fmt_addr(&status->ipv4_addr), status->ipv4_orport, + status->ipv4_dirport, ipv6_str, status->ipv6_orport, + status->ipv4_dirport); + tor_free(ipv6_str); if (!logged_backtrace) { log_backtrace(LOG_INFO, LD_BUG, "Addresses came from"); logged_backtrace = 1; @@ -713,8 +715,8 @@ directory_conn_is_self_reachability_test(dir_connection_t *conn) const routerinfo_t *me = router_get_my_routerinfo(); if (me && router_digest_is_me(conn->identity_digest) && - tor_addr_eq_ipv4h(&conn->base_.addr, me->addr) && /*XXXX prop 118*/ - me->dir_port == conn->base_.port) + tor_addr_eq(&TO_CONN(conn)->addr, &me->ipv4_addr) && + me->ipv4_dirport == conn->base_.port) return 1; } return 0; @@ -740,8 +742,8 @@ connection_dir_client_request_failed(dir_connection_t *conn) if (conn->base_.purpose == DIR_PURPOSE_FETCH_SERVERDESC || conn->base_.purpose == DIR_PURPOSE_FETCH_EXTRAINFO) { log_info(LD_DIR, "Giving up on serverdesc/extrainfo fetch from " - "directory server at '%s'; retrying", - conn->base_.address); + "directory server at %s; retrying", + connection_describe_peer(TO_CONN(conn))); if (conn->router_purpose == ROUTER_PURPOSE_BRIDGE) connection_dir_bridge_routerdesc_failed(conn); connection_dir_download_routerdesc_failed(conn); @@ -750,18 +752,19 @@ connection_dir_client_request_failed(dir_connection_t *conn) networkstatus_consensus_download_failed(0, conn->requested_resource); } else if (conn->base_.purpose == DIR_PURPOSE_FETCH_CERTIFICATE) { log_info(LD_DIR, "Giving up on certificate fetch from directory server " - "at '%s'; retrying", - conn->base_.address); + "at %s; retrying", + connection_describe_peer(TO_CONN(conn))); connection_dir_download_cert_failed(conn, 0); } else if (conn->base_.purpose == DIR_PURPOSE_FETCH_DETACHED_SIGNATURES) { - log_info(LD_DIR, "Giving up downloading detached signatures from '%s'", - conn->base_.address); + log_info(LD_DIR, "Giving up downloading detached signatures from %s", + connection_describe_peer(TO_CONN(conn))); } else if (conn->base_.purpose == DIR_PURPOSE_FETCH_STATUS_VOTE) { - log_info(LD_DIR, "Giving up downloading votes from '%s'", - conn->base_.address); + log_info(LD_DIR, "Giving up downloading votes from %s", + connection_describe_peer(TO_CONN(conn))); } else if (conn->base_.purpose == DIR_PURPOSE_FETCH_MICRODESC) { log_info(LD_DIR, "Giving up on downloading microdescriptors from " - "directory server at '%s'; will retry", conn->base_.address); + "directory server at %s; will retry", + connection_describe_peer(TO_CONN(conn))); connection_dir_download_routerdesc_failed(conn); } } @@ -1754,10 +1757,10 @@ directory_send_command(dir_connection_t *conn, smartlist_free(headers); log_debug(LD_DIR, - "Sent request to directory server '%s:%d': " + "Sent request to directory server %s " "(purpose: %d, request size: %"TOR_PRIuSZ", " "payload size: %"TOR_PRIuSZ")", - conn->base_.address, conn->base_.port, + connection_describe_peer(TO_CONN(conn)), conn->base_.purpose, (total_request_len), (payload ? payload_len : 0)); @@ -1893,9 +1896,10 @@ dir_client_decompress_response_body(char **bodyp, size_t *bodylenp, } tor_log(severity, LD_HTTP, - "HTTP body from server '%s:%d' was labeled as %s, " + "HTTP body from %s was labeled as %s, " "%s it seems to be %s.%s", - conn->base_.address, conn->base_.port, description1, + connection_describe(TO_CONN(conn)), + description1, guessed != compression?"but":"and", description2, (compression>0 && guessed>0 && want_to_try_both)? @@ -1941,11 +1945,11 @@ dir_client_decompress_response_body(char **bodyp, size_t *bodylenp, * we didn't manage to uncompress it, then warn and bail. */ if (!plausible && !new_body) { log_fn(LOG_PROTOCOL_WARN, LD_HTTP, - "Unable to decompress HTTP body (tried %s%s%s, server '%s:%d').", + "Unable to decompress HTTP body (tried %s%s%s, on %s).", description1, tried_both?" and ":"", tried_both?description2:"", - conn->base_.address, conn->base_.port); + connection_describe(TO_CONN(conn))); rv = -1; goto done; } @@ -2052,8 +2056,8 @@ connection_dir_client_reached_eof(dir_connection_t *conn) allow_partial)) { case -1: /* overflow */ log_warn(LD_PROTOCOL, - "'fetch' response too large (server '%s:%d'). Closing.", - conn->base_.address, conn->base_.port); + "'fetch' response too large (%s). Closing.", + connection_describe(TO_CONN(conn))); return -1; case 0: log_info(LD_HTTP, @@ -2064,22 +2068,22 @@ connection_dir_client_reached_eof(dir_connection_t *conn) if (parse_http_response(headers, &status_code, &date_header, &compression, &reason) < 0) { - log_warn(LD_HTTP,"Unparseable headers (server '%s:%d'). Closing.", - conn->base_.address, conn->base_.port); - + log_warn(LD_HTTP,"Unparseable headers (%s). Closing.", + connection_describe(TO_CONN(conn))); rv = -1; goto done; } if (!reason) reason = tor_strdup("[no reason given]"); tor_log(LOG_DEBUG, LD_DIR, - "Received response from directory server '%s:%d': %d %s " + "Received response on %s: %d %s " "(purpose: %d, response size: %"TOR_PRIuSZ #ifdef MEASUREMENTS_21206 ", data cells received: %d, data cells sent: %d" #endif ", compression: %d)", - conn->base_.address, conn->base_.port, status_code, + connection_describe(TO_CONN(conn)), + status_code, escaped(reason), conn->base_.purpose, (received_bytes), #ifdef MEASUREMENTS_21206 @@ -2104,7 +2108,13 @@ connection_dir_client_reached_eof(dir_connection_t *conn) if (conn->dirconn_direct) { char *guess = http_get_header(headers, X_ADDRESS_HEADER); if (guess) { - router_new_address_suggestion(guess, conn); + tor_addr_t addr; + if (tor_addr_parse(&addr, guess) < 0) { + log_debug(LD_DIR, "Malformed X-Your-Address-Is header %s. Ignoring.", + escaped(guess)); + } else { + relay_address_new_suggestion(&addr, &TO_CONN(conn)->addr, NULL); + } tor_free(guess); } } @@ -2133,9 +2143,9 @@ connection_dir_client_reached_eof(dir_connection_t *conn) dir_server_t *ds; const char *id_digest = conn->identity_digest; log_info(LD_DIR,"Received http status code %d (%s) from server " - "'%s:%d'. I'll try again soon.", - status_code, escaped(reason), conn->base_.address, - conn->base_.port); + "%s. I'll try again soon.", + status_code, escaped(reason), + connection_describe_peer(TO_CONN(conn))); time_t now = approx_time(); if ((rs = router_get_mutable_consensus_status_by_id(id_digest))) rs->last_dir_503_at = now; @@ -2240,9 +2250,9 @@ handle_response_fetch_consensus(dir_connection_t *conn, int severity = (status_code == 304) ? LOG_INFO : LOG_WARN; tor_log(severity, LD_DIR, "Received http status code %d (%s) from server " - "'%s:%d' while fetching consensus directory.", - status_code, escaped(reason), conn->base_.address, - conn->base_.port); + "%s while fetching consensus directory.", + status_code, escaped(reason), + connection_describe_peer(TO_CONN(conn))); networkstatus_consensus_download_failed(status_code, flavname); return -1; } @@ -2277,21 +2287,21 @@ handle_response_fetch_consensus(dir_connection_t *conn, tor_munmap_file(mapped_consensus); if (new_consensus == NULL) { log_warn(LD_DIR, "Could not apply consensus diff received from server " - "'%s:%d'", conn->base_.address, conn->base_.port); + "%s", connection_describe_peer(TO_CONN(conn))); // XXXX If this happens too many times, we should maybe not use // XXXX this directory for diffs any more? networkstatus_consensus_download_failed(0, flavname); return -1; } log_info(LD_DIR, "Applied consensus diff (size %d) from server " - "'%s:%d', resulting in a new consensus document (size %d).", - (int)body_len, conn->base_.address, conn->base_.port, + "%s, resulting in a new consensus document (size %d).", + (int)body_len, connection_describe_peer(TO_CONN(conn)), (int)strlen(new_consensus)); consensus = new_consensus; sourcename = "generated based on a diff"; } else { log_info(LD_DIR,"Received consensus directory (body size %d) from server " - "'%s:%d'", (int)body_len, conn->base_.address, conn->base_.port); + "%s", (int)body_len, connection_describe_peer(TO_CONN(conn))); consensus = body; sourcename = "downloaded"; } @@ -2302,8 +2312,9 @@ handle_response_fetch_consensus(dir_connection_t *conn, conn->identity_digest))<0) { log_fn(r<-1?LOG_WARN:LOG_INFO, LD_DIR, "Unable to load %s consensus directory %s from " - "server '%s:%d'. I'll try again soon.", - flavname, sourcename, conn->base_.address, conn->base_.port); + "server %s. I'll try again soon.", + flavname, sourcename, + connection_describe_peer(TO_CONN(conn))); networkstatus_consensus_download_failed(0, flavname); tor_free(new_consensus); return -1; @@ -2344,15 +2355,16 @@ handle_response_fetch_certificate(dir_connection_t *conn, if (status_code != 200) { log_warn(LD_DIR, "Received http status code %d (%s) from server " - "'%s:%d' while fetching \"/tor/keys/%s\".", - status_code, escaped(reason), conn->base_.address, - conn->base_.port, conn->requested_resource); + "%s while fetching \"/tor/keys/%s\".", + status_code, escaped(reason), + connection_describe_peer(TO_CONN(conn)), + conn->requested_resource); connection_dir_download_cert_failed(conn, status_code); return -1; } log_info(LD_DIR,"Received authority certificates (body size %d) from " - "server '%s:%d'", - (int)body_len, conn->base_.address, conn->base_.port); + "server %s", + (int)body_len, connection_describe_peer(TO_CONN(conn))); /* * Tell trusted_dirs_load_certs_from_string() whether it was by fp @@ -2403,14 +2415,15 @@ handle_response_fetch_status_vote(dir_connection_t *conn, const char *msg; int st; - log_info(LD_DIR,"Got votes (body size %d) from server %s:%d", - (int)body_len, conn->base_.address, conn->base_.port); + log_info(LD_DIR,"Got votes (body size %d) from server %s", + (int)body_len, connection_describe_peer(TO_CONN(conn))); if (status_code != 200) { log_warn(LD_DIR, "Received http status code %d (%s) from server " - "'%s:%d' while fetching \"/tor/status-vote/next/%s.z\".", - status_code, escaped(reason), conn->base_.address, - conn->base_.port, conn->requested_resource); + "%s while fetching \"/tor/status-vote/next/%s.z\".", + status_code, escaped(reason), + connection_describe_peer(TO_CONN(conn)), + conn->requested_resource); return -1; } dirvote_add_vote(body, 0, &msg, &st); @@ -2438,19 +2451,21 @@ handle_response_fetch_detached_signatures(dir_connection_t *conn, const size_t body_len = args->body_len; const char *msg = NULL; - log_info(LD_DIR,"Got detached signatures (body size %d) from server %s:%d", - (int)body_len, conn->base_.address, conn->base_.port); + log_info(LD_DIR,"Got detached signatures (body size %d) from server %s", + (int)body_len, + connection_describe_peer(TO_CONN(conn))); if (status_code != 200) { log_warn(LD_DIR, - "Received http status code %d (%s) from server '%s:%d' while fetching " + "Received http status code %d (%s) from server %s while fetching " "\"/tor/status-vote/next/consensus-signatures.z\".", - status_code, escaped(reason), conn->base_.address, - conn->base_.port); + status_code, escaped(reason), + connection_describe_peer(TO_CONN(conn))); return -1; } if (dirvote_add_signatures(body, conn->base_.address, &msg)<0) { - log_warn(LD_DIR, "Problem adding detached signatures from %s:%d: %s", - conn->base_.address, conn->base_.port, msg?msg:"???"); + log_warn(LD_DIR, "Problem adding detached signatures from %s: %s", + connection_describe_peer(TO_CONN(conn)), + msg?msg:"???"); } return 0; @@ -2476,9 +2491,9 @@ handle_response_fetch_desc(dir_connection_t *conn, int n_asked_for = 0; int descriptor_digests = conn->requested_resource && !strcmpstart(conn->requested_resource,"d/"); - log_info(LD_DIR,"Received %s (body size %d) from server '%s:%d'", + log_info(LD_DIR,"Received %s (body size %d) from server %s", was_ei ? "extra server info" : "server info", - (int)body_len, conn->base_.address, conn->base_.port); + (int)body_len, connection_describe_peer(TO_CONN(conn))); if (conn->requested_resource && (!strcmpstart(conn->requested_resource,"d/") || !strcmpstart(conn->requested_resource,"fp/"))) { @@ -2494,10 +2509,11 @@ handle_response_fetch_desc(dir_connection_t *conn, /* 404 means that it didn't have them; no big deal. * Older (pre-0.1.1.8) servers said 400 Servers unavailable instead. */ log_fn(dir_okay ? LOG_INFO : LOG_WARN, LD_DIR, - "Received http status code %d (%s) from server '%s:%d' " + "Received http status code %d (%s) from server %s " "while fetching \"/tor/server/%s\". I'll try again soon.", - status_code, escaped(reason), conn->base_.address, - conn->base_.port, conn->requested_resource); + status_code, escaped(reason), + connection_describe_peer(TO_CONN(conn)), + conn->requested_resource); if (!which) { connection_dir_download_routerdesc_failed(conn); } else { @@ -2537,10 +2553,10 @@ handle_response_fetch_desc(dir_connection_t *conn, } } if (which) { /* mark remaining ones as failed */ - log_info(LD_DIR, "Received %d/%d %s requested from %s:%d", + log_info(LD_DIR, "Received %d/%d %s requested from %s", n_asked_for-smartlist_len(which), n_asked_for, was_ei ? "extra-info documents" : "router descriptors", - conn->base_.address, (int)conn->base_.port); + connection_describe_peer(TO_CONN(conn))); if (smartlist_len(which)) { dir_routerdesc_download_failed(which, status_code, conn->router_purpose, @@ -2571,9 +2587,9 @@ handle_response_fetch_microdesc(dir_connection_t *conn, smartlist_t *which = NULL; log_info(LD_DIR,"Received answer to microdescriptor request (status %d, " - "body size %d) from server '%s:%d'", - status_code, (int)body_len, conn->base_.address, - conn->base_.port); + "body size %d) from server %s", + status_code, (int)body_len, + connection_describe_peer(TO_CONN(conn))); tor_assert(conn->requested_resource && !strcmpstart(conn->requested_resource, "d/")); tor_assert_nonfatal(!fast_mem_is_zero(conn->identity_digest, DIGEST_LEN)); @@ -2583,10 +2599,11 @@ handle_response_fetch_microdesc(dir_connection_t *conn, DSR_DIGEST256|DSR_BASE64); if (status_code != 200) { log_info(LD_DIR, "Received status code %d (%s) from server " - "'%s:%d' while fetching \"/tor/micro/%s\". I'll try again " + "%s while fetching \"/tor/micro/%s\". I'll try again " "soon.", - status_code, escaped(reason), conn->base_.address, - (int)conn->base_.port, conn->requested_resource); + status_code, escaped(reason), + connection_describe_peer(TO_CONN(conn)), + conn->requested_resource); dir_microdesc_download_failed(which, status_code, conn->identity_digest); SMARTLIST_FOREACH(which, char *, cp, tor_free(cp)); smartlist_free(which); @@ -2661,8 +2678,8 @@ handle_response_upload_dir(dir_connection_t *conn, break; case 400: log_warn(LD_GENERAL,"http status 400 (%s) response from " - "dirserver '%s:%d'. Please correct.", - escaped(reason), conn->base_.address, conn->base_.port); + "dirserver %s. Please correct.", + escaped(reason), connection_describe_peer(TO_CONN(conn))); control_event_server_status(LOG_WARN, "BAD_SERVER_DESCRIPTOR DIRAUTH=%s:%d REASON=\"%s\"", conn->base_.address, conn->base_.port, escaped(reason)); @@ -2670,10 +2687,10 @@ handle_response_upload_dir(dir_connection_t *conn, default: log_warn(LD_GENERAL, "HTTP status %d (%s) was unexpected while uploading " - "descriptor to server '%s:%d'. Possibly the server is " + "descriptor to server %s'. Possibly the server is " "misconfigured?", - status_code, escaped(reason), conn->base_.address, - conn->base_.port); + status_code, escaped(reason), + connection_describe_peer(TO_CONN(conn))); break; } /* return 0 in all cases, since we don't want to mark any @@ -2696,21 +2713,21 @@ handle_response_upload_vote(dir_connection_t *conn, switch (status_code) { case 200: { - log_notice(LD_DIR,"Uploaded a vote to dirserver %s:%d", - conn->base_.address, conn->base_.port); + log_notice(LD_DIR,"Uploaded a vote to dirserver %s", + connection_describe_peer(TO_CONN(conn))); } break; case 400: log_warn(LD_DIR,"http status 400 (%s) response after uploading " - "vote to dirserver '%s:%d'. Please correct.", - escaped(reason), conn->base_.address, conn->base_.port); + "vote to dirserver %s. Please correct.", + escaped(reason), connection_describe_peer(TO_CONN(conn))); break; default: log_warn(LD_GENERAL, "HTTP status %d (%s) was unexpected while uploading " - "vote to server '%s:%d'.", - status_code, escaped(reason), conn->base_.address, - conn->base_.port); + "vote to server %s.", + status_code, escaped(reason), + connection_describe_peer(TO_CONN(conn))); break; } /* return 0 in all cases, since we don't want to mark any @@ -2732,21 +2749,21 @@ handle_response_upload_signatures(dir_connection_t *conn, switch (status_code) { case 200: { - log_notice(LD_DIR,"Uploaded signature(s) to dirserver %s:%d", - conn->base_.address, conn->base_.port); + log_notice(LD_DIR,"Uploaded signature(s) to dirserver %s", + connection_describe_peer(TO_CONN(conn))); } break; case 400: log_warn(LD_DIR,"http status 400 (%s) response after uploading " - "signatures to dirserver '%s:%d'. Please correct.", - escaped(reason), conn->base_.address, conn->base_.port); + "signatures to dirserver %s. Please correct.", + escaped(reason), connection_describe_peer(TO_CONN(conn))); break; default: log_warn(LD_GENERAL, "HTTP status %d (%s) was unexpected while uploading " - "signatures to server '%s:%d'.", - status_code, escaped(reason), conn->base_.address, - conn->base_.port); + "signatures to server %s.", + status_code, escaped(reason), + connection_describe_peer(TO_CONN(conn))); break; } /* return 0 in all cases, since we don't want to mark any @@ -2861,10 +2878,10 @@ handle_response_fetch_renddesc_v2(dir_connection_t *conn, default: log_warn(LD_REND, "Fetching v2 rendezvous descriptor failed: " "http status %d (%s) response unexpected while " - "fetching v2 hidden service descriptor (server '%s:%d'). " + "fetching v2 hidden service descriptor (server %s). " "Retrying at another directory.", - status_code, escaped(reason), conn->base_.address, - conn->base_.port); + status_code, escaped(reason), + connection_describe_peer(TO_CONN(conn))); SEND_HS_DESC_FAILED_EVENT("UNEXPECTED"); SEND_HS_DESC_FAILED_CONTENT(); break; @@ -2908,15 +2925,15 @@ handle_response_upload_renddesc_v2(dir_connection_t *conn, break; case 400: log_warn(LD_REND,"http status 400 (%s) response from dirserver " - "'%s:%d'. Malformed rendezvous descriptor?", - escaped(reason), conn->base_.address, conn->base_.port); + "%s. Malformed rendezvous descriptor?", + escaped(reason), connection_describe_peer(TO_CONN(conn))); SEND_HS_DESC_UPLOAD_FAILED_EVENT("UPLOAD_REJECTED"); break; default: log_warn(LD_REND,"http status %d (%s) response unexpected (server " - "'%s:%d').", - status_code, escaped(reason), conn->base_.address, - conn->base_.port); + "%s).", + status_code, escaped(reason), + connection_describe_peer(TO_CONN(conn))); SEND_HS_DESC_UPLOAD_FAILED_EVENT("UNEXPECTED"); break; } @@ -2954,17 +2971,17 @@ handle_response_upload_hsdesc(dir_connection_t *conn, log_fn(LOG_PROTOCOL_WARN, LD_REND, "Uploading hidden service descriptor: http " "status 400 (%s) response from dirserver " - "'%s:%d'. Malformed hidden service descriptor?", - escaped(reason), conn->base_.address, conn->base_.port); + "%s. Malformed hidden service descriptor?", + escaped(reason), connection_describe_peer(TO_CONN(conn))); hs_control_desc_event_failed(conn->hs_ident, conn->identity_digest, "UPLOAD_REJECTED"); break; default: log_warn(LD_REND, "Uploading hidden service descriptor: http " "status %d (%s) response unexpected (server " - "'%s:%d').", - status_code, escaped(reason), conn->base_.address, - conn->base_.port); + "%s').", + status_code, escaped(reason), + connection_describe_peer(TO_CONN(conn))); hs_control_desc_event_failed(conn->hs_ident, conn->identity_digest, "UNEXPECTED"); break; @@ -3116,7 +3133,7 @@ connection_dir_close_consensus_fetches(dir_connection_t *except_this_one, if (d == except_this_one) continue; log_info(LD_DIR, "Closing consensus fetch (to %s) since one " - "has just arrived.", TO_CONN(d)->address); + "has just arrived.", connection_describe_peer(TO_CONN(d))); connection_mark_for_close(TO_CONN(d)); } SMARTLIST_FOREACH_END(d); smartlist_free(conns_to_close); diff --git a/src/feature/dirclient/dirclient_modes.c b/src/feature/dirclient/dirclient_modes.c index 31a3f8af58..62cdad6c36 100644 --- a/src/feature/dirclient/dirclient_modes.c +++ b/src/feature/dirclient/dirclient_modes.c @@ -40,15 +40,19 @@ int dirclient_fetches_from_authorities(const or_options_t *options) { const routerinfo_t *me; - uint32_t addr; int refuseunknown; if (options->FetchDirInfoEarly) return 1; if (options->BridgeRelay == 1) return 0; - if (server_mode(options) && - router_pick_published_address(options, &addr, 1) < 0) - return 1; /* we don't know our IP address; ask an authority. */ + /* We don't know our IP address; ask an authority. IPv4 is still mandatory + * to have thus if we don't have it, we ought to learn it from an authority + * through the NETINFO cell or the HTTP header it sends us back. + * + * Note that at the moment, relay do a direct connection so no NETINFO cell + * for now. */ + if (server_mode(options) && !relay_has_address_set(AF_INET)) + return 1; refuseunknown = ! router_my_exit_policy_is_reject_star() && should_refuse_unknown_exits(options); if (!dir_server_mode(options) && !refuseunknown) diff --git a/src/feature/dircommon/directory.c b/src/feature/dircommon/directory.c index b177fe5201..b276ac3441 100644 --- a/src/feature/dircommon/directory.c +++ b/src/feature/dircommon/directory.c @@ -79,8 +79,12 @@ * connection_finished_connecting() in connection.c */ -/** Convert a connection_t* to a dir_connection_t*; assert if the cast is - * invalid. */ +/** + * Cast a `connection_t *` to a `dir_connection_t *`. + * + * Exit with an assertion failure if the input is not a + * `dir_connection_t`. + **/ dir_connection_t * TO_DIR_CONN(connection_t *c) { @@ -88,6 +92,18 @@ TO_DIR_CONN(connection_t *c) return DOWNCAST(dir_connection_t, c); } +/** + * Cast a `const connection_t *` to a `const dir_connection_t *`. + * + * Exit with an assertion failure if the input is not a + * `dir_connection_t`. + **/ +const dir_connection_t * +CONST_TO_DIR_CONN(const connection_t *c) +{ + return TO_DIR_CONN((connection_t *)c); +} + /** Return false if the directory purpose <b>dir_purpose</b> * does not require an anonymous (three-hop) connection. * @@ -217,7 +233,7 @@ connection_dir_is_anonymous(const dir_connection_t *dir_conn) return false; } - edge_conn = TO_EDGE_CONN((connection_t *) linked_conn); + edge_conn = CONST_TO_EDGE_CONN(linked_conn); circ = edge_conn->on_circuit; /* Can't be a circuit we initiated and without a circuit, no channel. */ @@ -455,9 +471,9 @@ connection_dir_process_inbuf(dir_connection_t *conn) if (connection_get_inbuf_len(TO_CONN(conn)) > max_size) { log_warn(LD_HTTP, - "Too much data received from directory connection (%s): " + "Too much data received from %s: " "denial of service attempt, or you need to upgrade?", - conn->base_.address); + connection_describe(TO_CONN(conn))); connection_mark_for_close(TO_CONN(conn)); return -1; } @@ -540,8 +556,8 @@ connection_dir_finished_connecting(dir_connection_t *conn) tor_assert(conn->base_.type == CONN_TYPE_DIR); tor_assert(conn->base_.state == DIR_CONN_STATE_CONNECTING); - log_debug(LD_HTTP,"Dir connection to router %s:%u established.", - conn->base_.address,conn->base_.port); + log_debug(LD_HTTP,"Dir connection to %s established.", + connection_describe_peer(TO_CONN(conn))); /* start flushing conn */ conn->base_.state = DIR_CONN_STATE_CLIENT_SENDING; diff --git a/src/feature/dircommon/directory.h b/src/feature/dircommon/directory.h index 0f26cdeff9..0aa2ff53ef 100644 --- a/src/feature/dircommon/directory.h +++ b/src/feature/dircommon/directory.h @@ -13,6 +13,7 @@ #define TOR_DIRECTORY_H dir_connection_t *TO_DIR_CONN(connection_t *c); +const dir_connection_t *CONST_TO_DIR_CONN(const connection_t *c); #define DIR_CONN_STATE_MIN_ 1 /** State for connection to directory server: waiting for connect(). */ diff --git a/src/feature/dirparse/authcert_parse.c b/src/feature/dirparse/authcert_parse.c index deb45c12de..b2460f6ace 100644 --- a/src/feature/dirparse/authcert_parse.c +++ b/src/feature/dirparse/authcert_parse.c @@ -130,13 +130,13 @@ authority_cert_parse_from_string(const char *s, size_t maxlen, tor_assert(tok->n_args); /* XXX++ use some tor_addr parse function below instead. -RD */ if (tor_addr_port_split(LOG_WARN, tok->args[0], &address, - &cert->dir_port) < 0 || + &cert->ipv4_dirport) < 0 || tor_inet_aton(address, &in) == 0) { log_warn(LD_DIR, "Couldn't parse dir-address in certificate"); tor_free(address); goto err; } - cert->addr = ntohl(in.s_addr); + tor_addr_from_in(&cert->ipv4_addr, &in); tor_free(address); } diff --git a/src/feature/dirparse/ns_parse.c b/src/feature/dirparse/ns_parse.c index ac9325a608..d4bcdae973 100644 --- a/src/feature/dirparse/ns_parse.c +++ b/src/feature/dirparse/ns_parse.c @@ -384,12 +384,12 @@ routerstatus_parse_entry_from_string(memarea_t *area, escaped(tok->args[5+offset])); goto err; } - rs->addr = ntohl(in.s_addr); + tor_addr_from_in(&rs->ipv4_addr, &in); - rs->or_port = (uint16_t) tor_parse_long(tok->args[6+offset], - 10,0,65535,NULL,NULL); - rs->dir_port = (uint16_t) tor_parse_long(tok->args[7+offset], - 10,0,65535,NULL,NULL); + rs->ipv4_orport = (uint16_t) tor_parse_long(tok->args[6+offset], + 10,0,65535,NULL,NULL); + rs->ipv4_dirport = (uint16_t) tor_parse_long(tok->args[7+offset], + 10,0,65535,NULL,NULL); { smartlist_t *a_lines = find_all_by_keyword(tokens, K_A); @@ -563,7 +563,7 @@ routerstatus_parse_entry_from_string(memarea_t *area, log_info(LD_BUG, "Found an entry in networkstatus with no " "microdescriptor digest. (Router %s ($%s) at %s:%d.)", rs->nickname, hex_str(rs->identity_digest, DIGEST_LEN), - fmt_addr32(rs->addr), rs->or_port); + fmt_addr(&rs->ipv4_addr), rs->ipv4_orport); } } @@ -1354,8 +1354,8 @@ networkstatus_parse_vote_from_string(const char *s, goto err; } if (ns->type != NS_TYPE_CONSENSUS) { - if (authority_cert_is_blacklisted(ns->cert)) { - log_warn(LD_DIR, "Rejecting vote signature made with blacklisted " + if (authority_cert_is_denylisted(ns->cert)) { + log_warn(LD_DIR, "Rejecting vote signature made with denylisted " "signing key %s", hex_str(ns->cert->signing_key_digest, DIGEST_LEN)); goto err; @@ -1367,13 +1367,13 @@ networkstatus_parse_vote_from_string(const char *s, escaped(tok->args[3])); goto err; } - voter->addr = ntohl(in.s_addr); + tor_addr_from_in(&voter->ipv4_addr, &in); int ok; - voter->dir_port = (uint16_t) + voter->ipv4_dirport = (uint16_t) tor_parse_long(tok->args[4], 10, 0, 65535, &ok, NULL); if (!ok) goto err; - voter->or_port = (uint16_t) + voter->ipv4_orport = (uint16_t) tor_parse_long(tok->args[5], 10, 0, 65535, &ok, NULL); if (!ok) goto err; diff --git a/src/feature/dirparse/routerparse.c b/src/feature/dirparse/routerparse.c index 8828a0f97a..42a53101b0 100644 --- a/src/feature/dirparse/routerparse.c +++ b/src/feature/dirparse/routerparse.c @@ -519,15 +519,15 @@ router_parse_entry_from_string(const char *s, const char *end, log_warn(LD_DIR,"Router address is not an IP address."); goto err; } - router->addr = ntohl(in.s_addr); + tor_addr_from_in(&router->ipv4_addr, &in); - router->or_port = + router->ipv4_orport = (uint16_t) tor_parse_long(tok->args[2],10,0,65535,&ok,NULL); if (!ok) { log_warn(LD_DIR,"Invalid OR port %s", escaped(tok->args[2])); goto err; } - router->dir_port = + router->ipv4_dirport = (uint16_t) tor_parse_long(tok->args[4],10,0,65535,&ok,NULL); if (!ok) { log_warn(LD_DIR,"Invalid dir port %s", escaped(tok->args[4])); @@ -907,13 +907,14 @@ router_parse_entry_from_string(const char *s, const char *end, /* This router accepts tunnelled directory requests via begindir if it has * an open dirport or it included "tunnelled-dir-server". */ - if (find_opt_by_keyword(tokens, K_DIR_TUNNELLED) || router->dir_port > 0) { + if (find_opt_by_keyword(tokens, K_DIR_TUNNELLED) || + router->ipv4_dirport > 0) { router->supports_tunnelled_dir_requests = 1; } tok = find_by_keyword(tokens, K_ROUTER_SIGNATURE); - if (!router->or_port) { + if (!router->ipv4_orport) { log_warn(LD_DIR,"or_port unreadable or 0. Failing."); goto err; } diff --git a/src/feature/feature.md b/src/feature/feature.md index acc3487e55..d9f7bd5c0e 100644 --- a/src/feature/feature.md +++ b/src/feature/feature.md @@ -5,3 +5,26 @@ The "feature" directory has modules that Tor uses only for a particular role or service, such as maintaining/using an onion service, operating as a relay or a client, or being a directory authority. +Current subdirectories are: + + - \refdir{feature/api} -- Support for making Tor embeddable + - \refdir{feature/client} -- Functionality which only Tor clients need + - \refdir{feature/control} -- Controller implementation + - \refdir{feature/dirauth} -- Directory authority + - \refdir{feature/dircache} -- Directory cache + - \refdir{feature/dirclient} -- Directory client + - \refdir{feature/dircommon} -- Shared code between the other directory modules + - \refdir{feature/dirparse} -- Directory parsing code. + - \refdir{feature/hibernate} -- Hibernating when Tor is out of bandwidth + or shutting down + - \refdir{feature/hs} -- v3 onion service implementation + - \refdir{feature/hs_common} -- shared code between both onion service + implementations + - \refdir{feature/keymgt} -- shared code for key management between + relays and onion services. + - \refdir{feature/nodelist} -- storing and accessing the list of relays on + the network. + - \refdir{feature/relay} -- code that only relay servers and exit servers + need. + - \refdir{feature/rend} -- v2 onion service implementation + - \refdir{feature/stats} -- statistics and history diff --git a/src/feature/hs/hs_circuit.c b/src/feature/hs/hs_circuit.c index 447f664f81..5e7a3ac9c8 100644 --- a/src/feature/hs/hs_circuit.c +++ b/src/feature/hs/hs_circuit.c @@ -16,6 +16,7 @@ #include "core/or/policies.h" #include "core/or/relay.h" #include "core/or/crypt_path.h" +#include "core/or/extendinfo.h" #include "feature/client/circpathbias.h" #include "feature/hs/hs_cell.h" #include "feature/hs/hs_circuit.h" diff --git a/src/feature/hs/hs_client.c b/src/feature/hs/hs_client.c index c3697d0c1d..23768213f3 100644 --- a/src/feature/hs/hs_client.c +++ b/src/feature/hs/hs_client.c @@ -16,6 +16,7 @@ #include "core/or/circuitlist.h" #include "core/or/circuituse.h" #include "core/or/connection_edge.h" +#include "core/or/extendinfo.h" #include "core/or/reasons.h" #include "feature/client/circpathbias.h" #include "feature/dirclient/dirclient.h" @@ -704,8 +705,11 @@ send_introduce1(origin_circuit_t *intro_circ, } /** Using the introduction circuit circ, setup the authentication key of the - * intro point this circuit has extended to. */ -static void + * intro point this circuit has extended to. + * + * Return 0 if everything went well, otherwise return -1 in the case of errors. + */ +static int setup_intro_circ_auth_key(origin_circuit_t *circ) { const hs_descriptor_t *desc; @@ -719,27 +723,28 @@ setup_intro_circ_auth_key(origin_circuit_t *circ) * and the client descriptor cache that gets purged (NEWNYM) or the * cleaned up because it expired. Mark the circuit for close so a new * descriptor fetch can occur. */ - circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_INTERNAL); - goto end; + goto err; } /* We will go over every intro point and try to find which one is linked to * that circuit. Those lists are small so it's not that expensive. */ ip = find_desc_intro_point_by_legacy_id( circ->build_state->chosen_exit->identity_digest, desc); - if (ip) { - /* We got it, copy its authentication key to the identifier. */ - ed25519_pubkey_copy(&circ->hs_ident->intro_auth_pk, - &ip->auth_key_cert->signed_key); - goto end; + if (!ip) { + /* Reaching this point means we didn't find any intro point for this + * circuit which is not supposed to happen. */ + log_info(LD_REND,"Could not match opened intro circuit with intro point."); + goto err; } - /* Reaching this point means we didn't find any intro point for this circuit - * which is not supposed to happen. */ - tor_assert_nonfatal_unreached(); + /* We got it, copy its authentication key to the identifier. */ + ed25519_pubkey_copy(&circ->hs_ident->intro_auth_pk, + &ip->auth_key_cert->signed_key); + return 0; - end: - return; + err: + circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_INTERNAL); + return -1; } /** Called when an introduction circuit has opened. */ @@ -754,7 +759,9 @@ client_intro_circ_has_opened(origin_circuit_t *circ) /* This is an introduction circuit so we'll attach the correct * authentication key to the circuit identifier so it can be identified * properly later on. */ - setup_intro_circ_auth_key(circ); + if (setup_intro_circ_auth_key(circ) < 0) { + return; + } connection_ap_attach_pending(1); } @@ -1059,8 +1066,10 @@ close_or_reextend_intro_circ(origin_circuit_t *intro_circ) tor_assert(intro_circ); desc = hs_cache_lookup_as_client(&intro_circ->hs_ident->identity_pk); - if (BUG(desc == NULL)) { - /* We can't continue without a descriptor. */ + if (desc == NULL) { + /* We can't continue without a descriptor. This is possible if the cache + * was cleaned up between the intro point established and the reception of + * the introduce ack. */ goto close; } /* We still have the descriptor, great! Let's try to see if we can @@ -1545,9 +1554,9 @@ client_dir_fetch_unexpected(dir_connection_t *dir_conn, const char *reason, log_warn(LD_REND, "Fetching v3 hidden service descriptor failed: " "http status %d (%s) response unexpected from HSDir " - "server '%s:%d'. Retrying at another directory.", - status_code, escaped(reason), TO_CONN(dir_conn)->address, - TO_CONN(dir_conn)->port); + "server %s'. Retrying at another directory.", + status_code, escaped(reason), + connection_describe_peer(TO_CONN(dir_conn))); /* Fire control port FAILED event. */ hs_control_desc_event_failed(dir_conn->hs_ident, dir_conn->identity_digest, "UNEXPECTED"); diff --git a/src/feature/hs/hs_common.c b/src/feature/hs/hs_common.c index 4639cdb68a..706b42529f 100644 --- a/src/feature/hs/hs_common.c +++ b/src/feature/hs/hs_common.c @@ -16,6 +16,7 @@ #include "app/config/config.h" #include "core/or/circuitbuild.h" #include "core/or/policies.h" +#include "core/or/extendinfo.h" #include "feature/dirauth/shared_random_state.h" #include "feature/hs/hs_cache.h" #include "feature/hs/hs_circuitmap.h" diff --git a/src/feature/hs/hs_service.c b/src/feature/hs/hs_service.c index a42879a48f..b56b7f4368 100644 --- a/src/feature/hs/hs_service.c +++ b/src/feature/hs/hs_service.c @@ -16,6 +16,7 @@ #include "core/or/circuitbuild.h" #include "core/or/circuitlist.h" #include "core/or/circuituse.h" +#include "core/or/extendinfo.h" #include "core/or/relay.h" #include "feature/client/circpathbias.h" #include "feature/dirclient/dirclient.h" @@ -2873,6 +2874,9 @@ upload_descriptor_to_hsdir(const hs_service_t *service, hsdir->hsdir_index.store_first; char *blinded_pubkey_log_str = tor_strdup(hex_str((char*)&desc->blinded_kp.pubkey.pubkey, 32)); + /* This log message is used by Chutney as part of its bootstrap + * detection mechanism. Please don't change without first checking + * Chutney. */ log_info(LD_REND, "Service %s %s descriptor of revision %" PRIu64 " initiated upload request to %s with index %s (%s)", safe_str_client(service->onion_address), @@ -3900,7 +3904,7 @@ hs_service_exports_circuit_id(const ed25519_public_key_t *pk) /** Add to file_list every filename used by a configured hidden service, and to * dir_list every directory path used by a configured hidden service. This is - * used by the sandbox subsystem to whitelist those. */ + * used by the sandbox subsystem to allowlist those. */ void hs_service_lists_fnames_for_sandbox(smartlist_t *file_list, smartlist_t *dir_list) diff --git a/src/feature/nodelist/authcert.c b/src/feature/nodelist/authcert.c index 97e44d53e3..26713d7149 100644 --- a/src/feature/nodelist/authcert.c +++ b/src/feature/nodelist/authcert.c @@ -460,19 +460,15 @@ trusted_dirs_load_certs_from_string(const char *contents, int source, if (ds && cert->cache_info.published_on > ds->addr_current_at) { /* Check to see whether we should update our view of the authority's * address. */ - if (cert->addr && cert->dir_port && - (ds->addr != cert->addr || - ds->dir_port != cert->dir_port)) { - char *a = tor_dup_ip(cert->addr); - if (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; + if (!tor_addr_is_null(&cert->ipv4_addr) && cert->ipv4_dirport && + (!tor_addr_eq(&ds->ipv4_addr, &cert->ipv4_addr) || + ds->ipv4_dirport != cert->ipv4_dirport)) { + log_notice(LD_DIR, "Updating address for directory authority %s " + "from %s:%"PRIu16" to %s:%"PRIu16" based on certificate.", + ds->nickname, ds->address, ds->ipv4_dirport, + fmt_addr(&cert->ipv4_addr), cert->ipv4_dirport); + tor_addr_copy(&ds->ipv4_addr, &cert->ipv4_addr); + ds->ipv4_dirport = cert->ipv4_dirport; } ds->addr_current_at = cert->cache_info.published_on; } @@ -745,7 +741,7 @@ static const char *BAD_SIGNING_KEYS[] = { * which, because of the old openssl heartbleed vulnerability, should * never be trusted. */ int -authority_cert_is_blacklisted(const authority_cert_t *cert) +authority_cert_is_denylisted(const authority_cert_t *cert) { char hex_digest[HEX_DIGEST_LEN+1]; int i; diff --git a/src/feature/nodelist/authcert.h b/src/feature/nodelist/authcert.h index 33065589ba..4c3d79ceed 100644 --- a/src/feature/nodelist/authcert.h +++ b/src/feature/nodelist/authcert.h @@ -41,7 +41,7 @@ void authority_cert_dl_failed(const char *id_digest, void authority_certs_fetch_missing(networkstatus_t *status, time_t now, const char *dir_hint); int authority_cert_dl_looks_uncertain(const char *id_digest); -int authority_cert_is_blacklisted(const authority_cert_t *cert); +int authority_cert_is_denylisted(const authority_cert_t *cert); void authority_cert_free_(authority_cert_t *cert); #define authority_cert_free(cert) \ diff --git a/src/feature/nodelist/authority_cert_st.h b/src/feature/nodelist/authority_cert_st.h index 9145b12bbf..aa9831d12e 100644 --- a/src/feature/nodelist/authority_cert_st.h +++ b/src/feature/nodelist/authority_cert_st.h @@ -27,10 +27,10 @@ struct authority_cert_t { char signing_key_digest[DIGEST_LEN]; /** The listed expiration time of this certificate. */ time_t expires; - /** This authority's IPv4 address, in host order. */ - uint32_t addr; + /** This authority's IPv4 address. */ + tor_addr_t ipv4_addr; /** This authority's directory port. */ - uint16_t dir_port; + uint16_t ipv4_dirport; }; #endif /* !defined(AUTHORITY_CERT_ST_H) */ diff --git a/src/feature/nodelist/describe.c b/src/feature/nodelist/describe.c index 00896d5a44..96604800e9 100644 --- a/src/feature/nodelist/describe.c +++ b/src/feature/nodelist/describe.c @@ -12,6 +12,7 @@ #define DESCRIBE_PRIVATE #include "core/or/or.h" +#include "core/or/extendinfo.h" #include "feature/nodelist/describe.h" #include "core/or/extend_info_st.h" @@ -25,9 +26,8 @@ * <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 or the null address. The <b>addr32h</b> field is optional and may be - * set to 0. + * The <b>nickname</b>, <b>ipv6_addr</b> and <b>ipv4_addr</b> fields are + * optional and may be set to NULL or the null address. * * Return a pointer to the front of <b>buf</b>. * If buf is NULL, return a string constant describing the error. @@ -36,11 +36,12 @@ STATIC const char * format_node_description(char *buf, const char *id_digest, const char *nickname, - const tor_addr_t *addr, - uint32_t addr32h) + const tor_addr_t *ipv4_addr, + const tor_addr_t *ipv6_addr) { size_t rv = 0; - bool has_addr = addr && !tor_addr_is_null(addr); + bool has_ipv6 = ipv6_addr && !tor_addr_is_null(ipv6_addr); + bool valid_ipv4 = false; if (!buf) return "<NULL BUFFER>"; @@ -76,39 +77,37 @@ format_node_description(char *buf, rv = strlcat(buf, nickname, NODE_DESC_BUF_LEN); tor_assert_nonfatal(rv < NODE_DESC_BUF_LEN); } - if (addr32h || has_addr) { + if (ipv4_addr || has_ipv6) { 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); + if (ipv4_addr) { + const char *str_rv = NULL; + char addr_str[TOR_ADDR_BUF_LEN]; + memset(addr_str, 0, sizeof(addr_str)); + + str_rv = tor_addr_to_str(addr_str, ipv4_addr, sizeof(addr_str), 0); + if (str_rv) { + rv = strlcat(buf, addr_str, NODE_DESC_BUF_LEN); + tor_assert_nonfatal(rv < NODE_DESC_BUF_LEN); + valid_ipv4 = true; + } } /* Both addresses are valid */ - if (addr32h && has_addr) { + if (valid_ipv4 && has_ipv6) { rv = strlcat(buf, " and ", NODE_DESC_BUF_LEN); tor_assert_nonfatal(rv < NODE_DESC_BUF_LEN); } - if (has_addr) { + if (has_ipv6) { const char *str_rv = NULL; char addr_str[TOR_ADDR_BUF_LEN]; memset(addr_str, 0, sizeof(addr_str)); - str_rv = tor_addr_to_str(addr_str, 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); + str_rv = tor_addr_to_str(addr_str, ipv6_addr, sizeof(addr_str), 1); + if (str_rv) { + rv = strlcat(buf, addr_str, NODE_DESC_BUF_LEN); + tor_assert_nonfatal(rv < NODE_DESC_BUF_LEN); + } } return buf; @@ -130,8 +129,8 @@ router_describe(const routerinfo_t *ri) return format_node_description(buf, ri->cache_info.identity_digest, ri->nickname, - &ri->ipv6_addr, - ri->addr); + &ri->ipv4_addr, + &ri->ipv6_addr); } /** Return a human-readable description of the node_t <b>node</b>. @@ -144,15 +143,14 @@ node_describe(const node_t *node) { static char buf[NODE_DESC_BUF_LEN]; const char *nickname = NULL; - uint32_t addr32h = 0; - const tor_addr_t *ipv6_addr = NULL; + const tor_addr_t *ipv6_addr = NULL, *ipv4_addr = NULL; if (!node) return "<null>"; if (node->rs) { nickname = node->rs->nickname; - addr32h = node->rs->addr; + ipv4_addr = &node->rs->ipv4_addr; ipv6_addr = &node->rs->ipv6_addr; /* Support consensus versions less than 28, when IPv6 addresses were in * microdescs. This code can be removed when 0.2.9 is no longer supported, @@ -162,7 +160,7 @@ node_describe(const node_t *node) } } else if (node->ri) { nickname = node->ri->nickname; - addr32h = node->ri->addr; + ipv4_addr = &node->ri->ipv4_addr; ipv6_addr = &node->ri->ipv6_addr; } else { return "<null rs and ri>"; @@ -171,8 +169,8 @@ node_describe(const node_t *node) return format_node_description(buf, node->identity, nickname, - ipv6_addr, - addr32h); + ipv4_addr, + ipv6_addr); } /** Return a human-readable description of the routerstatus_t <b>rs</b>. @@ -191,8 +189,8 @@ routerstatus_describe(const routerstatus_t *rs) return format_node_description(buf, rs->identity_digest, rs->nickname, - &rs->ipv6_addr, - rs->addr); + &rs->ipv4_addr, + &rs->ipv6_addr); } /** Return a human-readable description of the extend_info_t <b>ei</b>. @@ -208,11 +206,16 @@ extend_info_describe(const extend_info_t *ei) if (!ei) return "<null>"; + const tor_addr_port_t *ap4 = extend_info_get_orport(ei, AF_INET); + const tor_addr_port_t *ap6 = extend_info_get_orport(ei, AF_INET6); + const tor_addr_t *addr4 = ap4 ? &ap4->addr : NULL; + const tor_addr_t *addr6 = ap6 ? &ap6->addr : NULL; + return format_node_description(buf, ei->identity_digest, ei->nickname, - &ei->addr, - 0); + addr4, + addr6); } /** Set <b>buf</b> (which must have MAX_VERBOSE_NICKNAME_LEN+1 bytes) to the diff --git a/src/feature/nodelist/describe.h b/src/feature/nodelist/describe.h index d0fa1af263..62f6c693e2 100644 --- a/src/feature/nodelist/describe.h +++ b/src/feature/nodelist/describe.h @@ -49,8 +49,8 @@ void router_get_verbose_nickname(char *buf, const routerinfo_t *router); STATIC const char *format_node_description(char *buf, const char *id_digest, const char *nickname, - const tor_addr_t *addr, - uint32_t addr32h); + const tor_addr_t *ipv4_addr, + const tor_addr_t *ipv6_addr); #endif /* defined(TOR_UNIT_TESTS) */ diff --git a/src/feature/nodelist/dirlist.c b/src/feature/nodelist/dirlist.c index 33d1bfc4d0..cd2921e653 100644 --- a/src/feature/nodelist/dirlist.c +++ b/src/feature/nodelist/dirlist.c @@ -59,9 +59,9 @@ add_trusted_dir_to_nodelist_addr_set(const dir_server_t *dir) tor_assert(dir->is_authority); /* Add IPv4 and then IPv6 if applicable. */ - nodelist_add_addr4_to_address_set(dir->addr); + nodelist_add_addr_to_address_set(&dir->ipv4_addr); if (!tor_addr_is_null(&dir->ipv6_addr)) { - nodelist_add_addr6_to_address_set(&dir->ipv6_addr); + nodelist_add_addr_to_address_set(&dir->ipv6_addr); } } @@ -250,6 +250,34 @@ router_digest_is_trusted_dir_type(const char *digest, dirinfo_type_t type) return 0; } +/** Return true iff the given address matches a trusted directory that matches + * at least one bit of type. + * + * If type is NO_DIRINFO or ALL_DIRINFO, any authority is matched. */ +bool +router_addr_is_trusted_dir_type(const tor_addr_t *addr, dirinfo_type_t type) +{ + int family = tor_addr_family(addr); + + if (!trusted_dir_servers) { + return false; + } + + SMARTLIST_FOREACH_BEGIN(trusted_dir_servers, dir_server_t *, ent) { + /* Ignore entries that don't match the given type. */ + if (type != NO_DIRINFO && (type & ent->type) == 0) { + continue; + } + /* Match IPv4 or IPv6 address. */ + if ((family == AF_INET && tor_addr_eq(addr, &ent->ipv4_addr)) || + (family == AF_INET6 && tor_addr_eq(addr, &ent->ipv6_addr))) { + return true; + } + } SMARTLIST_FOREACH_END(ent); + + return false; +} + /** Create a directory server at <b>address</b>:<b>port</b>, with OR identity * key <b>digest</b> which has DIGEST_LEN bytes. If <b>address</b> is NULL, * add ourself. If <b>is_authority</b>, this is a directory authority. Return @@ -257,16 +285,15 @@ router_digest_is_trusted_dir_type(const char *digest, dirinfo_type_t type) static dir_server_t * dir_server_new(int is_authority, const char *nickname, - const tor_addr_t *addr, + const tor_addr_t *ipv4_addr, const char *hostname, - uint16_t dir_port, uint16_t or_port, + uint16_t ipv4_dirport, uint16_t ipv4_orport, const tor_addr_port_t *addrport_ipv6, const char *digest, const char *v3_auth_digest, dirinfo_type_t type, double weight) { dir_server_t *ent; - uint32_t a; char *hostname_ = NULL; tor_assert(digest); @@ -274,27 +301,26 @@ dir_server_new(int is_authority, if (weight < 0) return NULL; - if (tor_addr_family(addr) == AF_INET) - a = tor_addr_to_ipv4h(addr); - else + if (!ipv4_addr) { return NULL; + } if (!hostname) - hostname_ = tor_addr_to_str_dup(addr); + hostname_ = tor_addr_to_str_dup(ipv4_addr); else hostname_ = tor_strdup(hostname); ent = tor_malloc_zero(sizeof(dir_server_t)); ent->nickname = nickname ? tor_strdup(nickname) : NULL; ent->address = hostname_; - ent->addr = a; - ent->dir_port = dir_port; - ent->or_port = or_port; + tor_addr_copy(&ent->ipv4_addr, ipv4_addr); + ent->ipv4_dirport = ipv4_dirport; + ent->ipv4_orport = ipv4_orport; ent->is_running = 1; ent->is_authority = is_authority; ent->type = type; ent->weight = weight; - if (addrport_ipv6) { + if (addrport_ipv6 && tor_addr_port_is_valid_ap(addrport_ipv6, 0)) { if (tor_addr_family(&addrport_ipv6->addr) != AF_INET6) { log_warn(LD_BUG, "Hey, I got a non-ipv6 addr as addrport_ipv6."); tor_addr_make_unspec(&ent->ipv6_addr); @@ -311,13 +337,13 @@ dir_server_new(int is_authority, memcpy(ent->v3_identity_digest, v3_auth_digest, DIGEST_LEN); if (nickname) - tor_asprintf(&ent->description, "directory server \"%s\" at %s:%d", - nickname, hostname_, (int)dir_port); + tor_asprintf(&ent->description, "directory server \"%s\" at %s:%" PRIu16, + nickname, hostname_, ipv4_dirport); else - tor_asprintf(&ent->description, "directory server at %s:%d", - hostname_, (int)dir_port); + tor_asprintf(&ent->description, "directory server at %s:%" PRIu16, + hostname_, ipv4_dirport); - ent->fake_status.addr = ent->addr; + tor_addr_copy(&ent->fake_status.ipv4_addr, &ent->ipv4_addr); tor_addr_copy(&ent->fake_status.ipv6_addr, &ent->ipv6_addr); memcpy(ent->fake_status.identity_digest, digest, DIGEST_LEN); if (nickname) @@ -325,44 +351,43 @@ dir_server_new(int is_authority, sizeof(ent->fake_status.nickname)); else ent->fake_status.nickname[0] = '\0'; - ent->fake_status.dir_port = ent->dir_port; - ent->fake_status.or_port = ent->or_port; + ent->fake_status.ipv4_dirport = ent->ipv4_dirport; + ent->fake_status.ipv4_orport = ent->ipv4_orport; ent->fake_status.ipv6_orport = ent->ipv6_orport; return ent; } -/** Create an authoritative directory server at - * <b>address</b>:<b>port</b>, with identity key <b>digest</b>. If - * <b>address</b> is NULL, add ourself. Return the new trusted directory - * server entry on success or NULL if we couldn't add it. */ +/** Create an authoritative directory server at <b>address</b>:<b>port</b>, + * with identity key <b>digest</b>. If <b>ipv4_addr_str</b> is NULL, add + * ourself. Return the new trusted directory server entry on success or NULL + * if we couldn't add it. */ dir_server_t * trusted_dir_server_new(const char *nickname, const char *address, - uint16_t dir_port, uint16_t or_port, + uint16_t ipv4_dirport, uint16_t ipv4_orport, const tor_addr_port_t *ipv6_addrport, const char *digest, const char *v3_auth_digest, dirinfo_type_t type, double weight) { - uint32_t a; - tor_addr_t addr; + tor_addr_t ipv4_addr; char *hostname=NULL; dir_server_t *result; if (!address) { /* The address is us; we should guess. */ - if (resolve_my_address(LOG_WARN, get_options(), - &a, NULL, &hostname) < 0) { + if (!find_my_address(get_options(), AF_INET, LOG_WARN, &ipv4_addr, + NULL, &hostname)) { log_warn(LD_CONFIG, "Couldn't find a suitable address when adding ourself as a " "trusted directory server."); return NULL; } if (!hostname) - hostname = tor_dup_ip(a); + hostname = tor_addr_to_str_dup(&ipv4_addr); if (!hostname) return NULL; } else { - if (tor_lookup_hostname(address, &a)) { + if (tor_addr_lookup(address, AF_INET, &ipv4_addr)) { log_warn(LD_CONFIG, "Unable to lookup address for directory server at '%s'", address); @@ -370,10 +395,9 @@ trusted_dir_server_new(const char *nickname, const char *address, } hostname = tor_strdup(address); } - tor_addr_from_ipv4h(&addr, a); - result = dir_server_new(1, nickname, &addr, hostname, - dir_port, or_port, + result = dir_server_new(1, nickname, &ipv4_addr, hostname, + ipv4_dirport, ipv4_orport, ipv6_addrport, digest, v3_auth_digest, type, weight); @@ -385,15 +409,13 @@ trusted_dir_server_new(const char *nickname, const char *address, * <b>addr</b>:<b>or_port</b>/<b>dir_port</b>, with identity key digest * <b>id_digest</b> */ dir_server_t * -fallback_dir_server_new(const tor_addr_t *addr, - uint16_t dir_port, uint16_t or_port, +fallback_dir_server_new(const tor_addr_t *ipv4_addr, + uint16_t ipv4_dirport, uint16_t ipv4_orport, const tor_addr_port_t *addrport_ipv6, const char *id_digest, double weight) { - return dir_server_new(0, NULL, addr, NULL, dir_port, or_port, - addrport_ipv6, - id_digest, - NULL, ALL_DIRINFO, weight); + return dir_server_new(0, NULL, ipv4_addr, NULL, ipv4_dirport, ipv4_orport, + addrport_ipv6, id_digest, NULL, ALL_DIRINFO, weight); } /** Add a directory server to the global list(s). */ diff --git a/src/feature/nodelist/dirlist.h b/src/feature/nodelist/dirlist.h index 9201e76a9c..c9310ff357 100644 --- a/src/feature/nodelist/dirlist.h +++ b/src/feature/nodelist/dirlist.h @@ -25,6 +25,11 @@ int router_digest_is_fallback_dir(const char *digest); MOCK_DECL(dir_server_t *, trusteddirserver_get_by_v3_auth_digest, (const char *d)); +bool router_addr_is_trusted_dir_type(const tor_addr_t *addr, + dirinfo_type_t type); +#define router_addr_is_trusted_dir(d) \ + router_addr_is_trusted_dir_type((d), NO_DIRINFO) + int router_digest_is_trusted_dir_type(const char *digest, dirinfo_type_t type); #define router_digest_is_trusted_dir(d) \ diff --git a/src/feature/nodelist/fmt_routerstatus.c b/src/feature/nodelist/fmt_routerstatus.c index ca4a312639..252b2e61fe 100644 --- a/src/feature/nodelist/fmt_routerstatus.c +++ b/src/feature/nodelist/fmt_routerstatus.c @@ -53,7 +53,7 @@ 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); + const char *ip_str = fmt_addr(&rs->ipv4_addr); if (ip_str[0] == '\0') goto err; @@ -62,15 +62,15 @@ routerstatus_format_entry(const routerstatus_t *rs, const char *version, digest_to_base64(digest64, rs->descriptor_digest); smartlist_add_asprintf(chunks, - "r %s %s %s%s%s %s %d %d\n", + "r %s %s %s%s%s %s %" PRIu16 " %" PRIu16 "\n", rs->nickname, identity64, (format==NS_V3_CONSENSUS_MICRODESC)?"":digest64, (format==NS_V3_CONSENSUS_MICRODESC)?"":" ", published, ip_str, - (int)rs->or_port, - (int)rs->dir_port); + rs->ipv4_orport, + rs->ipv4_dirport); /* TODO: Maybe we want to pass in what we need to build the rest of * this here, instead of in the caller. Then we could use the diff --git a/src/feature/nodelist/networkstatus.c b/src/feature/nodelist/networkstatus.c index e07d58c91c..9210518de0 100644 --- a/src/feature/nodelist/networkstatus.c +++ b/src/feature/nodelist/networkstatus.c @@ -471,8 +471,8 @@ networkstatus_check_document_signature(const networkstatus_t *consensus, DIGEST_LEN)) return -1; - if (authority_cert_is_blacklisted(cert)) { - /* We implement blacklisting for authority signing keys by treating + if (authority_cert_is_denylisted(cert)) { + /* We implement denylisting for authority signing keys by treating * all their signatures as always bad. That way we don't get into * crazy loops of dropping and re-fetching signatures. */ log_warn(LD_DIR, "Ignoring a consensus signature made with deprecated" @@ -608,25 +608,25 @@ networkstatus_check_consensus_signature(networkstatus_t *consensus, SMARTLIST_FOREACH(unrecognized, networkstatus_voter_info_t *, voter, { tor_log(severity, LD_DIR, "Consensus includes unrecognized authority " - "'%s' at %s:%d (contact %s; identity %s)", - voter->nickname, voter->address, (int)voter->dir_port, + "'%s' at %s:%" PRIu16 " (contact %s; identity %s)", + voter->nickname, voter->address, voter->ipv4_dirport, voter->contact?voter->contact:"n/a", hex_str(voter->identity_digest, DIGEST_LEN)); }); SMARTLIST_FOREACH(need_certs_from, networkstatus_voter_info_t *, voter, { tor_log(severity, LD_DIR, "Looks like we need to download a new " - "certificate from authority '%s' at %s:%d (contact %s; " - "identity %s)", - voter->nickname, voter->address, (int)voter->dir_port, + "certificate from authority '%s' at %s:%" PRIu16 + " (contact %s; identity %s)", + voter->nickname, voter->address, voter->ipv4_dirport, voter->contact?voter->contact:"n/a", hex_str(voter->identity_digest, DIGEST_LEN)); }); SMARTLIST_FOREACH(missing_authorities, dir_server_t *, ds, { tor_log(severity, LD_DIR, "Consensus does not include configured " - "authority '%s' at %s:%d (identity %s)", - ds->nickname, ds->address, (int)ds->dir_port, + "authority '%s' at %s:%" PRIu16 " (identity %s)", + ds->nickname, ds->address, ds->ipv4_dirport, hex_str(ds->v3_identity_digest, DIGEST_LEN)); }); { @@ -1594,9 +1594,9 @@ routerstatus_has_visibly_changed(const routerstatus_t *a, return strcmp(a->nickname, b->nickname) || fast_memneq(a->descriptor_digest, b->descriptor_digest, DIGEST_LEN) || - a->addr != b->addr || - a->or_port != b->or_port || - a->dir_port != b->dir_port || + !tor_addr_eq(&a->ipv4_addr, &b->ipv4_addr) || + a->ipv4_orport != b->ipv4_orport || + a->ipv4_dirport != b->ipv4_dirport || a->is_authority != b->is_authority || a->is_exit != b->is_exit || a->is_stable != b->is_stable || @@ -1670,7 +1670,35 @@ notify_before_networkstatus_changes(const networkstatus_t *old_c, static void notify_after_networkstatus_changes(void) { + const networkstatus_t *c = networkstatus_get_latest_consensus(); + const or_options_t *options = get_options(); + const time_t now = approx_time(); + scheduler_notify_networkstatus_changed(); + + /* The "current" consensus has just been set and it is a usable flavor so + * the first thing we need to do is recalculate the voting schedule static + * object so we can use the timings in there needed by some subsystems + * such as hidden service and shared random. */ + dirauth_sched_recalculate_timing(options, now); + reschedule_dirvote(options); + + nodelist_set_consensus(c); + + update_consensus_networkstatus_fetch_time(now); + + /* Change the cell EWMA settings */ + cmux_ewma_set_options(options, c); + + /* XXXX this call might be unnecessary here: can changing the + * current consensus really alter our view of any OR's rate limits? */ + connection_or_update_token_buckets(get_connection_array(), options); + + circuit_build_times_new_consensus_params( + get_circuit_build_times_mutable(), c); + channelpadding_new_consensus_params(c); + circpad_new_consensus_params(c); + router_new_consensus_params(c); } /** Copy all the ancillary information (like router download status and so on) @@ -2115,29 +2143,6 @@ networkstatus_set_current_consensus(const char *consensus, /* Notify that we just changed the consensus so the current global value * can be looked at. */ notify_after_networkstatus_changes(); - - /* The "current" consensus has just been set and it is a usable flavor so - * the first thing we need to do is recalculate the voting schedule static - * object so we can use the timings in there needed by some subsystems - * such as hidden service and shared random. */ - dirauth_sched_recalculate_timing(options, now); - reschedule_dirvote(options); - - nodelist_set_consensus(c); - - update_consensus_networkstatus_fetch_time(now); - - /* Change the cell EWMA settings */ - cmux_ewma_set_options(options, c); - - /* XXXX this call might be unnecessary here: can changing the - * current consensus really alter our view of any OR's rate limits? */ - connection_or_update_token_buckets(get_connection_array(), options); - - circuit_build_times_new_consensus_params( - get_circuit_build_times_mutable(), c); - channelpadding_new_consensus_params(c); - circpad_new_consensus_params(c); } /* Reset the failure count only if this consensus is actually valid. */ @@ -2387,10 +2392,10 @@ set_routerstatus_from_routerinfo(routerstatus_t *rs, memcpy(rs->identity_digest, node->identity, DIGEST_LEN); memcpy(rs->descriptor_digest, ri->cache_info.signed_descriptor_digest, DIGEST_LEN); - rs->addr = ri->addr; + tor_addr_copy(&rs->ipv4_addr, &ri->ipv4_addr); strlcpy(rs->nickname, ri->nickname, sizeof(rs->nickname)); - rs->or_port = ri->or_port; - rs->dir_port = ri->dir_port; + rs->ipv4_orport = ri->ipv4_orport; + rs->ipv4_dirport = ri->ipv4_dirport; rs->is_v2_dir = ri->supports_tunnelled_dir_requests; tor_addr_copy(&rs->ipv6_addr, &ri->ipv6_addr); diff --git a/src/feature/nodelist/networkstatus_voter_info_st.h b/src/feature/nodelist/networkstatus_voter_info_st.h index b4d0b1dd17..a0fba2e1b5 100644 --- a/src/feature/nodelist/networkstatus_voter_info_st.h +++ b/src/feature/nodelist/networkstatus_voter_info_st.h @@ -21,9 +21,9 @@ struct networkstatus_voter_info_t { * consensuses, we treat legacy keys as additional signers. */ char legacy_id_digest[DIGEST_LEN]; char *address; /**< Address of this voter, in string format. */ - uint32_t addr; /**< Address of this voter, in IPv4, in host order. */ - uint16_t dir_port; /**< Directory port of this voter */ - uint16_t or_port; /**< OR port of this voter */ + tor_addr_t ipv4_addr; + uint16_t ipv4_dirport; /**< Directory port of this voter */ + uint16_t ipv4_orport; /**< OR port of this voter */ char *contact; /**< Contact information for this voter. */ char vote_digest[DIGEST_LEN]; /**< Digest of this voter's vote, as signed. */ diff --git a/src/feature/nodelist/node_select.c b/src/feature/nodelist/node_select.c index e831248413..a7c02f64d8 100644 --- a/src/feature/nodelist/node_select.c +++ b/src/feature/nodelist/node_select.c @@ -217,13 +217,15 @@ router_picked_poor_directory_log(const routerstatus_t *rs) ) { /* This is rare, and might be interesting to users trying to diagnose * connection issues on dual-stack machines. */ + char *ipv4_str = tor_addr_to_str_dup(&rs->ipv4_addr); log_info(LD_DIR, "Selected a directory %s with non-preferred OR and Dir " "addresses for launching an outgoing connection: " "IPv4 %s OR %d Dir %d IPv6 %s OR %d Dir %d", routerstatus_describe(rs), - fmt_addr32(rs->addr), rs->or_port, - rs->dir_port, fmt_addr(&rs->ipv6_addr), - rs->ipv6_orport, rs->dir_port); + ipv4_str, rs->ipv4_orport, + rs->ipv4_dirport, fmt_addr(&rs->ipv6_addr), + rs->ipv6_orport, rs->ipv4_dirport); + tor_free(ipv4_str); } } @@ -266,7 +268,7 @@ router_is_already_dir_fetching(const tor_addr_port_t *ap, int serverdesc, * If so, return 1, if not, return 0. */ static int -router_is_already_dir_fetching_(uint32_t ipv4_addr, +router_is_already_dir_fetching_(const tor_addr_t *ipv4_addr, const tor_addr_t *ipv6_addr, uint16_t dir_port, int serverdesc, @@ -275,7 +277,7 @@ router_is_already_dir_fetching_(uint32_t ipv4_addr, tor_addr_port_t ipv4_dir_ap, ipv6_dir_ap; /* Assume IPv6 DirPort is the same as IPv4 DirPort */ - tor_addr_from_ipv4h(&ipv4_dir_ap.addr, ipv4_addr); + tor_addr_copy(&ipv4_dir_ap.addr, ipv4_addr); ipv4_dir_ap.port = dir_port; tor_addr_copy(&ipv6_dir_ap.addr, ipv6_addr); ipv6_dir_ap.port = dir_port; @@ -321,8 +323,12 @@ router_pick_directory_server_impl(dirinfo_type_t type, int flags, overloaded_direct = smartlist_new(); overloaded_tunnel = smartlist_new(); - const int skip_or_fw = router_skip_or_reachability(options, try_ip_pref); - const int skip_dir_fw = router_skip_dir_reachability(options, try_ip_pref); + const int skip_or_fw = router_or_conn_should_skip_reachable_address_check( + options, + try_ip_pref); + const int skip_dir_fw = router_dir_conn_should_skip_reachable_address_check( + options, + try_ip_pref); const int must_have_or = dirclient_must_use_begindir(options); /* Find all the running dirservers we know about. */ @@ -348,9 +354,9 @@ router_pick_directory_server_impl(dirinfo_type_t type, int flags, continue; } - if (router_is_already_dir_fetching_(status->addr, + if (router_is_already_dir_fetching_(&status->ipv4_addr, &status->ipv6_addr, - status->dir_port, + status->ipv4_dirport, no_serverdesc_fetching, no_microdesc_fetching)) { ++n_busy; @@ -926,64 +932,67 @@ nodelist_subtract(smartlist_t *sl, const smartlist_t *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>, - * even if they are the only nodes available. - * If <b>CRN_NEED_UPTIME</b> is set in flags and any router has more than - * a minimum uptime, return one of those. - * If <b>CRN_NEED_CAPACITY</b> is set in flags, weight your choice by the - * advertised capacity of each router. - * If <b>CRN_NEED_GUARD</b> is set in flags, consider only Guard routers. - * If <b>CRN_WEIGHT_AS_EXIT</b> is set in flags, we weight bandwidths as if - * picking an exit node, otherwise we weight bandwidths for picking a relay - * node (that is, possibly discounting exit nodes). - * If <b>CRN_NEED_DESC</b> is set in flags, we only consider nodes that - * have a routerinfo or microdescriptor -- that is, enough info to be - * used to build a circuit. - * If <b>CRN_PREF_ADDR</b> is set in flags, we only consider nodes that - * have an address that is preferred by the ClientPreferIPv6ORPort setting - * (regardless of this flag, we exclude nodes that aren't allowed by the - * firewall, including ClientUseIPv4 0 and fascist_firewall_use_ipv6() == 0). +/* Node selection helper for router_choose_random_node(). + * + * Populates a node list based on <b>flags</b>, ignoring nodes in + * <b>excludednodes</b> and <b>excludedset</b>. Chooses the node based on + * <b>rule</b>. */ +static const node_t * +router_choose_random_node_helper(smartlist_t *excludednodes, + routerset_t *excludedset, + router_crn_flags_t flags, + bandwidth_weight_rule_t rule) +{ + smartlist_t *sl=smartlist_new(); + const node_t *choice = NULL; + + router_add_running_nodes_to_smartlist(sl, flags); + log_debug(LD_CIRC, + "We found %d running nodes.", + smartlist_len(sl)); + + nodelist_subtract(sl, excludednodes); + + if (excludedset) { + routerset_subtract_nodes(sl,excludedset); + log_debug(LD_CIRC, + "We removed excludedset, leaving %d nodes.", + smartlist_len(sl)); + } + + // Always weight by bandwidth + choice = node_sl_choose_by_bandwidth(sl, rule); + + smartlist_free(sl); + + return choice; +} + +/** Return a random running node from the nodelist. Never pick a node that is + * in <b>excludedsmartlist</b>, or which matches <b>excludedset</b>, even if + * they are the only nodes available. + * + * <b>flags</b> is a set of CRN_* flags, see + * router_add_running_nodes_to_smartlist() for details. */ const node_t * router_choose_random_node(smartlist_t *excludedsmartlist, routerset_t *excludedset, router_crn_flags_t flags) -{ /* XXXX MOVE */ - const int need_uptime = (flags & CRN_NEED_UPTIME) != 0; - const int need_capacity = (flags & CRN_NEED_CAPACITY) != 0; - const int need_guard = (flags & CRN_NEED_GUARD) != 0; - const int weight_for_exit = (flags & CRN_WEIGHT_AS_EXIT) != 0; - const int need_desc = (flags & CRN_NEED_DESC) != 0; - const int pref_addr = (flags & CRN_PREF_ADDR) != 0; - const int direct_conn = (flags & CRN_DIRECT_CONN) != 0; - const int rendezvous_v3 = (flags & CRN_RENDEZVOUS_V3) != 0; - - const smartlist_t *node_list = nodelist_get_list(); - smartlist_t *sl=smartlist_new(), - *excludednodes=smartlist_new(); +{ + /* A limited set of flags, used for fallback node selection. + */ + const bool need_uptime = (flags & CRN_NEED_UPTIME) != 0; + const bool need_capacity = (flags & CRN_NEED_CAPACITY) != 0; + const bool need_guard = (flags & CRN_NEED_GUARD) != 0; + const bool pref_addr = (flags & CRN_PREF_ADDR) != 0; + + smartlist_t *excludednodes=smartlist_new(); const node_t *choice = NULL; const routerinfo_t *r; bandwidth_weight_rule_t rule; - tor_assert(!(weight_for_exit && need_guard)); - rule = weight_for_exit ? WEIGHT_FOR_EXIT : - (need_guard ? WEIGHT_FOR_GUARD : WEIGHT_FOR_MID); - - SMARTLIST_FOREACH_BEGIN(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_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_t*)node); - } - } SMARTLIST_FOREACH_END(node); + rule = (need_guard ? WEIGHT_FOR_GUARD : WEIGHT_FOR_MID); /* If the node_t is not found we won't be to exclude ourself but we * won't be able to pick ourself in router_choose_random_node() so @@ -991,41 +1000,30 @@ router_choose_random_node(smartlist_t *excludedsmartlist, if ((r = router_get_my_routerinfo())) routerlist_add_node_and_family(excludednodes, r); - router_add_running_nodes_to_smartlist(sl, need_uptime, need_capacity, - need_guard, need_desc, pref_addr, - direct_conn); - log_debug(LD_CIRC, - "We found %d running nodes.", - smartlist_len(sl)); - if (excludedsmartlist) { smartlist_add_all(excludednodes, excludedsmartlist); } - nodelist_subtract(sl, excludednodes); - if (excludedset) { - routerset_subtract_nodes(sl,excludedset); - log_debug(LD_CIRC, - "We removed excludedset, leaving %d nodes.", - smartlist_len(sl)); - } + choice = router_choose_random_node_helper(excludednodes, + excludedset, + flags, + rule); - // Always weight by bandwidth - choice = node_sl_choose_by_bandwidth(sl, rule); - - smartlist_free(sl); if (!choice && (need_uptime || need_capacity || need_guard || pref_addr)) { - /* try once more -- recurse but with fewer restrictions. */ + /* try once more, with fewer restrictions. */ log_info(LD_CIRC, - "We couldn't find any live%s%s%s routers; falling back " + "We couldn't find any live%s%s%s%s routers; falling back " "to list of all routers.", need_capacity?", fast":"", need_uptime?", stable":"", - need_guard?", guard":""); + need_guard?", guard":"", + pref_addr?", preferred address":""); flags &= ~ (CRN_NEED_UPTIME|CRN_NEED_CAPACITY|CRN_NEED_GUARD| CRN_PREF_ADDR); - choice = router_choose_random_node( - excludedsmartlist, excludedset, flags); + choice = router_choose_random_node_helper(excludednodes, + excludedset, + flags, + rule); } smartlist_free(excludednodes); if (!choice) { @@ -1120,8 +1118,12 @@ router_pick_trusteddirserver_impl(const smartlist_t *sourcelist, overloaded_direct = smartlist_new(); overloaded_tunnel = smartlist_new(); - const int skip_or_fw = router_skip_or_reachability(options, try_ip_pref); - const int skip_dir_fw = router_skip_dir_reachability(options, try_ip_pref); + const int skip_or_fw = router_or_conn_should_skip_reachable_address_check( + options, + try_ip_pref); + const int skip_dir_fw = router_dir_conn_should_skip_reachable_address_check( + options, + try_ip_pref); const int must_have_or = dirclient_must_use_begindir(options); SMARTLIST_FOREACH_BEGIN(sourcelist, const dir_server_t *, d) @@ -1143,9 +1145,9 @@ router_pick_trusteddirserver_impl(const smartlist_t *sourcelist, continue; } - if (router_is_already_dir_fetching_(d->addr, + if (router_is_already_dir_fetching_(&d->ipv4_addr, &d->ipv6_addr, - d->dir_port, + d->ipv4_dirport, no_serverdesc_fetching, no_microdesc_fetching)) { ++n_busy; diff --git a/src/feature/nodelist/node_select.h b/src/feature/nodelist/node_select.h index 2e67f990f6..1776d8ea1a 100644 --- a/src/feature/nodelist/node_select.h +++ b/src/feature/nodelist/node_select.h @@ -14,20 +14,26 @@ /** Flags to be passed to control router_choose_random_node() to indicate what * kind of nodes to pick according to what algorithm. */ typedef enum router_crn_flags_t { + /* Try to choose stable nodes. */ CRN_NEED_UPTIME = 1<<0, + /* Try to choose nodes with a reasonable amount of bandwidth. */ CRN_NEED_CAPACITY = 1<<1, - CRN_NEED_GUARD = 1<<2, - /* XXXX not used, apparently. */ - CRN_WEIGHT_AS_EXIT = 1<<5, - CRN_NEED_DESC = 1<<6, - /* On clients, only provide nodes that satisfy ClientPreferIPv6OR */ - CRN_PREF_ADDR = 1<<7, + /* Only choose nodes if we have downloaded their descriptor or + * microdescriptor. */ + CRN_NEED_DESC = 1<<2, + /* Choose nodes that can be used as Guard relays. */ + CRN_NEED_GUARD = 1<<3, /* On clients, only provide nodes that we can connect to directly, based on - * our firewall rules */ - CRN_DIRECT_CONN = 1<<8, - /* On clients, only provide nodes with HSRend >= 2 protocol version which - * is required for hidden service version >= 3. */ - CRN_RENDEZVOUS_V3 = 1<<9, + * our firewall rules. */ + CRN_DIRECT_CONN = 1<<4, + /* On clients, if choosing a node for a direct connection, only provide + * nodes that satisfy ClientPreferIPv6OR. */ + CRN_PREF_ADDR = 1<<5, + /* On clients, only provide nodes with HSRend=2 protocol version which + * is required for hidden service version 3. */ + CRN_RENDEZVOUS_V3 = 1<<6, + /* On clients, only provide nodes that can initiate IPv6 extends. */ + CRN_INITIATE_IPV6_EXTEND = 1<<7, } router_crn_flags_t; /** Possible ways to weight routers when choosing one randomly. See diff --git a/src/feature/nodelist/node_st.h b/src/feature/nodelist/node_st.h index b1ec4db202..3769f9dc84 100644 --- a/src/feature/nodelist/node_st.h +++ b/src/feature/nodelist/node_st.h @@ -84,12 +84,11 @@ struct node_t { /* Local info: derived. */ - /** True if the IPv6 OR port is preferred over the IPv4 OR port. - * XX/teor - can this become out of date if the torrc changes? */ + /** True if the IPv6 OR port is preferred over the IPv4 OR port. */ unsigned int ipv6_preferred:1; /** According to the geoip db what country is this router in? */ - /* XXXprop186 what is this suppose to mean with multiple OR ports? */ + /* IPv6: what is this supposed to mean with multiple OR ports? */ country_t country; /* The below items are used only by authdirservers for diff --git a/src/feature/nodelist/nodelist.c b/src/feature/nodelist/nodelist.c index 7454f342f9..c30684d2d8 100644 --- a/src/feature/nodelist/nodelist.c +++ b/src/feature/nodelist/nodelist.c @@ -454,38 +454,29 @@ node_add_to_address_set(const node_t *node) * to add them all than to compare them all for equality. */ if (node->rs) { - if (node->rs->addr) - nodelist_add_addr4_to_address_set(node->rs->addr); + if (!tor_addr_is_null(&node->rs->ipv4_addr)) + nodelist_add_addr_to_address_set(&node->rs->ipv4_addr); if (!tor_addr_is_null(&node->rs->ipv6_addr)) - nodelist_add_addr6_to_address_set(&node->rs->ipv6_addr); + nodelist_add_addr_to_address_set(&node->rs->ipv6_addr); } if (node->ri) { - if (node->ri->addr) - nodelist_add_addr4_to_address_set(node->ri->addr); + if (!tor_addr_is_null(&node->ri->ipv4_addr)) + nodelist_add_addr_to_address_set(&node->ri->ipv4_addr); if (!tor_addr_is_null(&node->ri->ipv6_addr)) - nodelist_add_addr6_to_address_set(&node->ri->ipv6_addr); + nodelist_add_addr_to_address_set(&node->ri->ipv6_addr); } if (node->md) { if (!tor_addr_is_null(&node->md->ipv6_addr)) - nodelist_add_addr6_to_address_set(&node->md->ipv6_addr); + nodelist_add_addr_to_address_set(&node->md->ipv6_addr); } } -/** Add the given v4 address into the nodelist address set. */ +/** Add the given address into the nodelist address set. */ void -nodelist_add_addr4_to_address_set(const uint32_t addr) +nodelist_add_addr_to_address_set(const tor_addr_t *addr) { - if (!the_nodelist || !the_nodelist->node_addrs || addr == 0) { - return; - } - address_set_add_ipv4h(the_nodelist->node_addrs, addr); -} - -/** Add the given v6 address into the nodelist address set. */ -void -nodelist_add_addr6_to_address_set(const tor_addr_t *addr) -{ - if (BUG(!addr) || tor_addr_is_null(addr) || tor_addr_is_v4(addr) || + if (BUG(!addr) || tor_addr_is_null(addr) || + (!tor_addr_is_v4(addr) && !tor_addr_is_v6(addr)) || !the_nodelist || !the_nodelist->node_addrs) { return; } @@ -621,7 +612,7 @@ get_estimated_address_per_node, (void)) * and grab microdescriptors into nodes as appropriate. */ void -nodelist_set_consensus(networkstatus_t *ns) +nodelist_set_consensus(const networkstatus_t *ns) { const or_options_t *options = get_options(); int authdir = authdir_mode_v3(options); @@ -952,7 +943,7 @@ nodelist_assert_ok(void) /** Ensure that the nodelist has been created with the most recent consensus. * If that's not the case, make it so. */ void -nodelist_ensure_freshness(networkstatus_t *ns) +nodelist_ensure_freshness(const networkstatus_t *ns) { tor_assert(ns); @@ -1133,7 +1124,7 @@ node_ed25519_id_matches(const node_t *node, const ed25519_public_key_t *id) /** Dummy object that should be unreturnable. Used to ensure that * node_get_protover_summary_flags() always returns non-NULL. */ static const protover_summary_flags_t zero_protover_flags = { - 0,0,0,0,0,0,0,0,0 + 0,0,0,0,0,0,0,0,0,0,0,0 }; /** Return the protover_summary_flags for a given node. */ @@ -1158,9 +1149,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. */ -MOCK_IMPL(int, +MOCK_IMPL(bool, node_supports_ed25519_link_authentication,(const node_t *node, - int compatible_with_us)) + bool compatible_with_us)) { if (! node_get_ed25519_id(node)) return 0; @@ -1175,7 +1166,7 @@ node_supports_ed25519_link_authentication,(const node_t *node, /** Return true iff <b>node</b> supports the hidden service directory version * 3 protocol (proposal 224). */ -int +bool node_supports_v3_hsdir(const node_t *node) { tor_assert(node); @@ -1185,7 +1176,7 @@ node_supports_v3_hsdir(const node_t *node) /** Return true iff <b>node</b> supports ed25519 authentication as an hidden * service introduction point.*/ -int +bool node_supports_ed25519_hs_intro(const node_t *node) { tor_assert(node); @@ -1193,9 +1184,24 @@ 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> can be a rendezvous point for hidden + * service version 3 (HSRend=2). */ +bool +node_supports_v3_rendezvous_point(const node_t *node) +{ + tor_assert(node); + + /* We can't use a v3 rendezvous point without the curve25519 onion pk. */ + if (!node_get_curve25519_onion_key(node)) { + return 0; + } + + return node_get_protover_summary_flags(node)->supports_v3_rendezvous_point; +} + /** Return true iff <b>node</b> supports the DoS ESTABLISH_INTRO cell * extenstion. */ -int +bool node_supports_establish_intro_dos_extension(const node_t *node) { tor_assert(node); @@ -1204,19 +1210,54 @@ node_supports_establish_intro_dos_extension(const node_t *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 -node_supports_v3_rendezvous_point(const node_t *node) +/** Return true iff <b>node</b> can initiate IPv6 extends (Relay=3). + * + * This check should only be performed by client path selection code. + * + * Extending relays should check their own IPv6 support using + * router_can_extend_over_ipv6(). Like other extends, they should not verify + * the link specifiers in the extend cell against the consensus, because it + * may be out of date. */ +bool +node_supports_initiating_ipv6_extends(const node_t *node) { tor_assert(node); - /* We can't use a v3 rendezvous point without the curve25519 onion pk. */ - if (!node_get_curve25519_onion_key(node)) { + /* Relays can't initiate an IPv6 extend, unless they have an IPv6 ORPort. */ + if (!node_has_ipv6_orport(node)) { return 0; } - return node_get_protover_summary_flags(node)->supports_v3_rendezvous_point; + /* Initiating relays also need to support the relevant protocol version. */ + return + node_get_protover_summary_flags(node)->supports_initiating_ipv6_extends; +} + +/** Return true iff <b>node</b> can accept IPv6 extends (Relay=2 or Relay=3) + * from other relays. If <b>need_canonical_ipv6_conn</b> is true, also check + * if the relay supports canonical IPv6 connections (Relay=3 only). + * + * This check should only be performed by client path selection code. + */ +bool +node_supports_accepting_ipv6_extends(const node_t *node, + bool need_canonical_ipv6_conn) +{ + tor_assert(node); + + /* Relays can't accept an IPv6 extend, unless they have an IPv6 ORPort. */ + if (!node_has_ipv6_orport(node)) { + return 0; + } + + /* Accepting relays also need to support the relevant protocol version. */ + if (need_canonical_ipv6_conn) { + return + node_get_protover_summary_flags(node)->supports_canonical_ipv6_conns; + } else { + return + node_get_protover_summary_flags(node)->supports_accepting_ipv6_extends; + } } /** Return the RSA ID key's SHA1 digest for the provided node. */ @@ -1491,32 +1532,14 @@ node_exit_policy_is_exact(const node_t *node, sa_family_t family) * "addr" is an IPv4 host-order address and port_field is a uint16_t. * r is typically a routerinfo_t or routerstatus_t. */ -#define SL_ADD_NEW_IPV4_AP(r, port_field, sl, valid) \ - STMT_BEGIN \ - if (tor_addr_port_is_valid_ipv4h((r)->addr, (r)->port_field, 0)) { \ - valid = 1; \ - tor_addr_port_t *ap = tor_malloc(sizeof(tor_addr_port_t)); \ - tor_addr_from_ipv4h(&ap->addr, (r)->addr); \ - ap->port = (r)->port_field; \ - smartlist_add((sl), ap); \ - } \ - STMT_END - -/* Check if the "addr" and port_field fields from r are a valid non-listening - * address/port. If so, set valid to true and add a newly allocated - * tor_addr_port_t containing "addr" and port_field to sl. - * "addr" is a tor_addr_t and port_field is a uint16_t. - * r is typically a routerinfo_t or routerstatus_t. - */ -#define SL_ADD_NEW_IPV6_AP(r, port_field, sl, valid) \ - STMT_BEGIN \ - if (tor_addr_port_is_valid(&(r)->ipv6_addr, (r)->port_field, 0)) { \ - valid = 1; \ - tor_addr_port_t *ap = tor_malloc(sizeof(tor_addr_port_t)); \ - tor_addr_copy(&ap->addr, &(r)->ipv6_addr); \ - ap->port = (r)->port_field; \ - smartlist_add((sl), ap); \ - } \ +#define SL_ADD_NEW_AP(r, addr_field, port_field, sl, valid) \ + STMT_BEGIN \ + if (tor_addr_port_is_valid(&(r)->addr_field, (r)->port_field, 0)) { \ + valid = 1; \ + tor_addr_port_t *ap = tor_addr_port_new(&(r)->addr_field, \ + (r)->port_field); \ + smartlist_add((sl), ap); \ + } \ STMT_END /** Return list of tor_addr_port_t with all OR ports (in the sense IP @@ -1535,33 +1558,32 @@ node_get_all_orports(const node_t *node) /* Find a valid IPv4 address and port */ if (node->ri != NULL) { - SL_ADD_NEW_IPV4_AP(node->ri, or_port, sl, valid); + SL_ADD_NEW_AP(node->ri, ipv4_addr, ipv4_orport, sl, valid); } /* If we didn't find a valid address/port in the ri, try the rs */ if (!valid && node->rs != NULL) { - SL_ADD_NEW_IPV4_AP(node->rs, or_port, sl, valid); + SL_ADD_NEW_AP(node->rs, ipv4_addr, ipv4_orport, sl, valid); } /* Find a valid IPv6 address and port */ valid = 0; if (node->ri != NULL) { - SL_ADD_NEW_IPV6_AP(node->ri, ipv6_orport, sl, valid); + SL_ADD_NEW_AP(node->ri, ipv6_addr, ipv6_orport, sl, valid); } if (!valid && node->rs != NULL) { - SL_ADD_NEW_IPV6_AP(node->rs, ipv6_orport, sl, valid); + SL_ADD_NEW_AP(node->rs, ipv6_addr, ipv6_orport, sl, valid); } if (!valid && node->md != NULL) { - SL_ADD_NEW_IPV6_AP(node->md, ipv6_orport, sl, valid); + SL_ADD_NEW_AP(node->md, ipv6_addr, ipv6_orport, sl, valid); } return sl; } -#undef SL_ADD_NEW_IPV4_AP -#undef SL_ADD_NEW_IPV6_AP +#undef SL_ADD_NEW_AP /** Wrapper around node_get_prim_orport for backward compatibility. */ @@ -1573,21 +1595,20 @@ node_get_addr(const node_t *node, tor_addr_t *addr_out) tor_addr_copy(addr_out, &ap.addr); } -/** Return the host-order IPv4 address for <b>node</b>, or 0 if it doesn't - * seem to have one. */ -uint32_t -node_get_prim_addr_ipv4h(const node_t *node) +/** Return the IPv4 address for <b>node</b>, or NULL if none found. */ +static const tor_addr_t * +node_get_prim_addr_ipv4(const node_t *node) { /* Don't check the ORPort or DirPort, as this function isn't port-specific, * and the node might have a valid IPv4 address, yet have a zero * ORPort or DirPort. */ - if (node->ri && tor_addr_is_valid_ipv4h(node->ri->addr, 0)) { - return node->ri->addr; - } else if (node->rs && tor_addr_is_valid_ipv4h(node->rs->addr, 0)) { - return node->rs->addr; + if (node->ri && tor_addr_is_valid(&node->ri->ipv4_addr, 0)) { + return &node->ri->ipv4_addr; + } else if (node->rs && tor_addr_is_valid(&node->rs->ipv4_addr, 0)) { + return &node->rs->ipv4_addr; } - return 0; + return NULL; } /** Copy a string representation of an IP address for <b>node</b> into @@ -1595,12 +1616,10 @@ node_get_prim_addr_ipv4h(const node_t *node) void node_get_address_string(const node_t *node, char *buf, size_t len) { - uint32_t ipv4_addr = node_get_prim_addr_ipv4h(node); + const tor_addr_t *ipv4_addr = node_get_prim_addr_ipv4(node); - if (tor_addr_is_valid_ipv4h(ipv4_addr, 0)) { - tor_addr_t addr; - tor_addr_from_ipv4h(&addr, ipv4_addr); - tor_addr_to_str(buf, &addr, len, 0); + if (ipv4_addr) { + tor_addr_to_str(buf, ipv4_addr, len, 0); } else if (len > 0) { buf[0] = '\0'; } @@ -1707,12 +1726,12 @@ node_ipv6_or_preferred(const node_t *node) return 0; } -#define RETURN_IPV4_AP(r, port_field, ap_out) \ - STMT_BEGIN \ - if (r && tor_addr_port_is_valid_ipv4h((r)->addr, (r)->port_field, 0)) { \ - tor_addr_from_ipv4h(&(ap_out)->addr, (r)->addr); \ - (ap_out)->port = (r)->port_field; \ - } \ +#define RETURN_IPV4_AP(r, port_field, ap_out) \ + STMT_BEGIN \ + if (r && tor_addr_port_is_valid(&(r)->ipv4_addr, (r)->port_field, 0)) { \ + tor_addr_copy(&(ap_out)->addr, &(r)->ipv4_addr); \ + (ap_out)->port = (r)->port_field; \ + } \ STMT_END /** Copy the primary (IPv4) OR port (IP address and TCP port) for <b>node</b> @@ -1731,8 +1750,8 @@ node_get_prim_orport(const node_t *node, tor_addr_port_t *ap_out) /* Check ri first, because rewrite_node_address_for_bridge() updates * node->ri with the configured bridge address. */ - RETURN_IPV4_AP(node->ri, or_port, ap_out); - RETURN_IPV4_AP(node->rs, or_port, ap_out); + RETURN_IPV4_AP(node->ri, ipv4_orport, ap_out); + RETURN_IPV4_AP(node->rs, ipv4_orport, ap_out); /* Microdescriptors only have an IPv6 address */ } @@ -1832,8 +1851,8 @@ node_get_prim_dirport(const node_t *node, tor_addr_port_t *ap_out) /* Check ri first, because rewrite_node_address_for_bridge() updates * node->ri with the configured bridge address. */ - RETURN_IPV4_AP(node->ri, dir_port, ap_out); - RETURN_IPV4_AP(node->rs, dir_port, ap_out); + RETURN_IPV4_AP(node->ri, ipv4_dirport, ap_out); + RETURN_IPV4_AP(node->rs, ipv4_dirport, ap_out); /* Microdescriptors only have an IPv6 address */ } @@ -1870,13 +1889,13 @@ node_get_pref_ipv6_dirport(const node_t *node, tor_addr_port_t *ap_out) /* Assume IPv4 and IPv6 dirports are the same */ if (node->ri && tor_addr_port_is_valid(&node->ri->ipv6_addr, - node->ri->dir_port, 0)) { + node->ri->ipv4_dirport, 0)) { tor_addr_copy(&ap_out->addr, &node->ri->ipv6_addr); - ap_out->port = node->ri->dir_port; + ap_out->port = node->ri->ipv4_dirport; } else if (node->rs && tor_addr_port_is_valid(&node->rs->ipv6_addr, - node->rs->dir_port, 0)) { + node->rs->ipv4_dirport, 0)) { tor_addr_copy(&ap_out->addr, &node->rs->ipv6_addr); - ap_out->port = node->rs->dir_port; + ap_out->port = node->rs->ipv4_dirport; } else { tor_addr_make_null(&ap_out->addr, AF_INET6); ap_out->port = 0; @@ -1961,15 +1980,21 @@ node_get_rsa_onion_key(const node_t *node) void node_set_country(node_t *node) { - tor_addr_t addr = TOR_ADDR_NULL; + const tor_addr_t *ipv4_addr = NULL; /* XXXXipv6 */ if (node->rs) - tor_addr_from_ipv4h(&addr, node->rs->addr); + ipv4_addr = &node->rs->ipv4_addr; else if (node->ri) - tor_addr_from_ipv4h(&addr, node->ri->addr); + ipv4_addr = &node->ri->ipv4_addr; - node->country = geoip_get_country_by_addr(&addr); + /* IPv4 is mandatory for a relay so this should not happen unless we are + * attempting to set the country code on a node without a descriptor. */ + if (BUG(!ipv4_addr)) { + node->country = -1; + return; + } + node->country = geoip_get_country_by_addr(ipv4_addr); } /** Set the country code of all routers in the routerlist. */ @@ -1984,7 +2009,7 @@ nodelist_refresh_countries(void) /** Return true iff router1 and router2 have similar enough network addresses * that we should treat them as being in the same family */ int -addrs_in_same_network_family(const tor_addr_t *a1, +router_addrs_in_same_network(const tor_addr_t *a1, const tor_addr_t *a2) { if (tor_addr_is_null(a1) || tor_addr_is_null(a2)) @@ -2100,8 +2125,8 @@ nodes_in_same_family(const node_t *node1, const node_t *node2) 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)) + if (router_addrs_in_same_network(&a1, &a2) || + router_addrs_in_same_network(&ap6_1.addr, &ap6_2.addr)) return 1; } @@ -2159,8 +2184,8 @@ nodelist_add_node_and_family(smartlist_t *sl, const node_t *node) tor_addr_port_t ap6; node_get_addr(node2, &a); 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)) + if (router_addrs_in_same_network(&a, &node_addr) || + router_addrs_in_same_network(&ap6.addr, &node_ap6.addr)) smartlist_add(sl, (void*)node2); } SMARTLIST_FOREACH_END(node2); } @@ -2200,21 +2225,18 @@ nodelist_add_node_and_family(smartlist_t *sl, const node_t *node) const node_t * router_find_exact_exit_enclave(const char *address, uint16_t port) {/*XXXX MOVE*/ - uint32_t addr; struct in_addr in; - tor_addr_t a; + tor_addr_t ipv4_addr; const or_options_t *options = get_options(); if (!tor_inet_aton(address, &in)) return NULL; /* it's not an IP already */ - addr = ntohl(in.s_addr); - - tor_addr_from_ipv4h(&a, addr); + tor_addr_from_in(&ipv4_addr, &in); SMARTLIST_FOREACH(nodelist_get_list(), const node_t *, node, { - if (node_get_addr_ipv4h(node) == addr && + if (tor_addr_eq(node_get_prim_addr_ipv4(node), &ipv4_addr) && node->is_running && - compare_tor_addr_to_node_policy(&a, port, node) == + compare_tor_addr_to_node_policy(&ipv4_addr, port, node) == ADDR_POLICY_ACCEPTED && !routerset_contains_node(options->ExcludeExitNodesUnion_, node)) return node; diff --git a/src/feature/nodelist/nodelist.h b/src/feature/nodelist/nodelist.h index 57ab2d5913..b762fb339c 100644 --- a/src/feature/nodelist/nodelist.h +++ b/src/feature/nodelist/nodelist.h @@ -32,11 +32,10 @@ const node_t *node_get_by_hex_id(const char *identity_digest, unsigned flags); node_t *nodelist_set_routerinfo(routerinfo_t *ri, routerinfo_t **ri_old_out); node_t *nodelist_add_microdesc(microdesc_t *md); -void nodelist_set_consensus(networkstatus_t *ns); -void nodelist_ensure_freshness(networkstatus_t *ns); +void nodelist_set_consensus(const networkstatus_t *ns); +void nodelist_ensure_freshness(const networkstatus_t *ns); int nodelist_probably_contains_address(const tor_addr_t *addr); -void nodelist_add_addr4_to_address_set(const uint32_t addr); -void nodelist_add_addr6_to_address_set(const tor_addr_t *addr); +void nodelist_add_addr_to_address_set(const tor_addr_t *addr); void nodelist_remove_microdesc(const char *identity_digest, microdesc_t *md); void nodelist_remove_routerinfo(routerinfo_t *ri); @@ -67,20 +66,23 @@ smartlist_t *node_get_all_orports(const node_t *node); int node_allows_single_hop_exits(const node_t *node); const char *node_get_nickname(const node_t *node); const char *node_get_platform(const node_t *node); -uint32_t node_get_prim_addr_ipv4h(const node_t *node); void node_get_address_string(const node_t *node, char *cp, size_t len); long node_get_declared_uptime(const node_t *node); 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); -MOCK_DECL(int,node_supports_ed25519_link_authentication, +MOCK_DECL(bool,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); + bool compatible_with_us)); +bool node_supports_v3_hsdir(const node_t *node); +bool node_supports_ed25519_hs_intro(const node_t *node); +bool node_supports_v3_rendezvous_point(const node_t *node); +bool node_supports_establish_intro_dos_extension(const node_t *node); +bool node_supports_initiating_ipv6_extends(const node_t *node); +bool node_supports_accepting_ipv6_extends(const node_t *node, + bool need_canonical_ipv6_conn); + const uint8_t *node_get_rsa_id_digest(const node_t *node); MOCK_DECL(smartlist_t *,node_get_link_specifier_smartlist,(const node_t *node, bool direct_conn)); @@ -110,7 +112,6 @@ MOCK_DECL(const smartlist_t *, nodelist_get_list, (void)); /* Temporary during transition to multiple addresses. */ void node_get_addr(const node_t *node, tor_addr_t *addr_out); -#define node_get_addr_ipv4h(n) node_get_prim_addr_ipv4h((n)) void nodelist_refresh_countries(void); void node_set_country(node_t *node); @@ -124,7 +125,7 @@ int node_is_unreliable(const node_t *router, int need_uptime, int router_exit_policy_all_nodes_reject(const tor_addr_t *addr, uint16_t port, int need_uptime); void router_set_status(const char *digest, int up); -int addrs_in_same_network_family(const tor_addr_t *a1, +int router_addrs_in_same_network(const tor_addr_t *a1, const tor_addr_t *a2); /** router_have_minimum_dir_info tests to see if we have enough diff --git a/src/feature/nodelist/routerinfo.c b/src/feature/nodelist/routerinfo.c index 0bf2a977f5..2a094d7fae 100644 --- a/src/feature/nodelist/routerinfo.c +++ b/src/feature/nodelist/routerinfo.c @@ -17,22 +17,45 @@ #include "feature/nodelist/node_st.h" #include "feature/nodelist/routerinfo_st.h" -/** Copy the primary (IPv4) OR port (IP address and TCP port) for - * <b>router</b> into *<b>ap_out</b>. */ -void -router_get_prim_orport(const routerinfo_t *router, tor_addr_port_t *ap_out) +/** Copy the OR port (IP address and TCP port) for <b>router</b> and + * <b>family</b> into *<b>ap_out</b>. + * + * If the requested ORPort does not exist, sets *<b>ap_out</b> to the null + * address and port, and returns -1. Otherwise, returns 0. */ +int +router_get_orport(const routerinfo_t *router, + tor_addr_port_t *ap_out, + int family) { tor_assert(ap_out != NULL); - tor_addr_from_ipv4h(&ap_out->addr, router->addr); - ap_out->port = router->or_port; + if (family == AF_INET) { + tor_addr_copy(&ap_out->addr, &router->ipv4_addr); + ap_out->port = router->ipv4_orport; + return 0; + } else if (family == AF_INET6) { + /* IPv6 addresses are optional, so check if it is valid. */ + if (tor_addr_port_is_valid(&router->ipv6_addr, router->ipv6_orport, 0)) { + tor_addr_copy(&ap_out->addr, &router->ipv6_addr); + ap_out->port = router->ipv6_orport; + return 0; + } else { + tor_addr_port_make_null_ap(ap_out, AF_INET6); + return -1; + } + } else { + /* Unsupported address family */ + tor_assert_nonfatal_unreached(); + tor_addr_port_make_null_ap(ap_out, AF_UNSPEC); + return -1; + } } int router_has_orport(const routerinfo_t *router, const tor_addr_port_t *orport) { return - (tor_addr_eq_ipv4h(&orport->addr, router->addr) && - orport->port == router->or_port) || + (tor_addr_eq(&orport->addr, &router->ipv4_addr) && + orport->port == router->ipv4_orport) || (tor_addr_eq(&orport->addr, &router->ipv6_addr) && orport->port == router->ipv6_orport); } diff --git a/src/feature/nodelist/routerinfo.h b/src/feature/nodelist/routerinfo.h index 604e478999..2e12cbeba3 100644 --- a/src/feature/nodelist/routerinfo.h +++ b/src/feature/nodelist/routerinfo.h @@ -12,8 +12,9 @@ #ifndef TOR_ROUTERINFO_H #define TOR_ROUTERINFO_H -void router_get_prim_orport(const routerinfo_t *router, - tor_addr_port_t *addr_port_out); +int router_get_orport(const routerinfo_t *router, + tor_addr_port_t *addr_port_out, + int family); int router_has_orport(const routerinfo_t *router, const tor_addr_port_t *orport); diff --git a/src/feature/nodelist/routerinfo_st.h b/src/feature/nodelist/routerinfo_st.h index 36ead50e33..7197c88c18 100644 --- a/src/feature/nodelist/routerinfo_st.h +++ b/src/feature/nodelist/routerinfo_st.h @@ -21,9 +21,10 @@ struct routerinfo_t { signed_descriptor_t cache_info; char *nickname; /**< Human-readable OR name. */ - uint32_t addr; /**< IPv4 address of OR, in host order. */ - uint16_t or_port; /**< Port for TLS connections. */ - uint16_t dir_port; /**< Port for HTTP directory connections. */ + /** A router's IPv4 address. */ + tor_addr_t ipv4_addr; + uint16_t ipv4_orport; + uint16_t ipv4_dirport; /** A router's IPv6 address, if it has one. */ tor_addr_t ipv6_addr; diff --git a/src/feature/nodelist/routerlist.c b/src/feature/nodelist/routerlist.c index 80c1aa6893..72824f2dd2 100644 --- a/src/feature/nodelist/routerlist.c +++ b/src/feature/nodelist/routerlist.c @@ -465,11 +465,20 @@ router_reload_router_list(void) return 0; } -/* When iterating through the routerlist, can OR address/port preference - * and reachability checks be skipped? +/* When selecting a router for a direct connection, can OR address/port + * preference and reachability checks be skipped? + * + * Servers never check ReachableAddresses or ClientPreferIPv6. Returns + * true for servers. + * + * Otherwise, if <b>try_ip_pref</b> is true, returns false. Used to make + * clients check ClientPreferIPv6, even if ReachableAddresses is not set. + * Finally, return true if ReachableAddresses is set. */ int -router_skip_or_reachability(const or_options_t *options, int try_ip_pref) +router_or_conn_should_skip_reachable_address_check( + const or_options_t *options, + int try_ip_pref) { /* Servers always have and prefer IPv4. * And if clients are checking against the firewall for reachability only, @@ -477,11 +486,15 @@ router_skip_or_reachability(const or_options_t *options, int try_ip_pref) return server_mode(options) || (!try_ip_pref && !firewall_is_fascist_or()); } -/* When iterating through the routerlist, can Dir address/port preference +/* When selecting a router for a direct connection, can Dir address/port * and reachability checks be skipped? + * + * This function is obsolete, because clients only use ORPorts. */ int -router_skip_dir_reachability(const or_options_t *options, int try_ip_pref) +router_dir_conn_should_skip_reachable_address_check( + const or_options_t *options, + int try_ip_pref) { /* Servers always have and prefer IPv4. * And if clients are checking against the firewall for reachability only, @@ -493,45 +506,115 @@ router_skip_dir_reachability(const or_options_t *options, int try_ip_pref) int routers_have_same_or_addrs(const routerinfo_t *r1, const routerinfo_t *r2) { - return r1->addr == r2->addr && r1->or_port == r2->or_port && + return tor_addr_eq(&r1->ipv4_addr, &r2->ipv4_addr) && + r1->ipv4_orport == r2->ipv4_orport && tor_addr_eq(&r1->ipv6_addr, &r2->ipv6_addr) && r1->ipv6_orport == r2->ipv6_orport; } +/* Returns true if <b>node</b> can be chosen based on <b>flags</b>. + * + * The following conditions are applied to all nodes: + * - is running; + * - is valid; + * - supports EXTEND2 cells; + * - has an ntor circuit crypto key; and + * - does not allow single-hop exits. + * + * If the node has a routerinfo, we're checking for a direct connection, and + * we're using bridges, the following condition is applied: + * - has a bridge-purpose routerinfo; + * and for all other nodes: + * - has a general-purpose routerinfo (or no routerinfo). + * + * Nodes that don't have a routerinfo must be general-purpose nodes, because + * routerstatuses and microdescriptors only come via consensuses. + * + * The <b>flags</b> chech that <b>node</b>: + * - <b>CRN_NEED_UPTIME</b>: has more than a minimum uptime; + * - <b>CRN_NEED_CAPACITY</b>: has more than a minimum capacity; + * - <b>CRN_NEED_GUARD</b>: is a Guard; + * - <b>CRN_NEED_DESC</b>: has a routerinfo or microdescriptor -- that is, + * enough info to be used to build a circuit; + * - <b>CRN_DIRECT_CONN</b>: is suitable for direct connections. Checks + * for the relevant descriptors. Checks the address + * against ReachableAddresses, ClientUseIPv4 0, and + * fascist_firewall_use_ipv6() == 0); + * - <b>CRN_PREF_ADDR</b>: if we are connecting directly to the node, it has + * an address that is preferred by the + * ClientPreferIPv6ORPort setting; + * - <b>CRN_RENDEZVOUS_V3</b>: can become a v3 onion service rendezvous point; + * - <b>CRN_INITIATE_IPV6_EXTEND</b>: can initiate IPv6 extends. + */ +bool +router_can_choose_node(const node_t *node, int flags) +{ + /* The full set of flags used for node selection. */ + const bool need_uptime = (flags & CRN_NEED_UPTIME) != 0; + const bool need_capacity = (flags & CRN_NEED_CAPACITY) != 0; + const bool need_guard = (flags & CRN_NEED_GUARD) != 0; + const bool need_desc = (flags & CRN_NEED_DESC) != 0; + const bool pref_addr = (flags & CRN_PREF_ADDR) != 0; + const bool direct_conn = (flags & CRN_DIRECT_CONN) != 0; + const bool rendezvous_v3 = (flags & CRN_RENDEZVOUS_V3) != 0; + const bool initiate_ipv6_extend = (flags & CRN_INITIATE_IPV6_EXTEND) != 0; + + const or_options_t *options = get_options(); + const bool check_reach = + !router_or_conn_should_skip_reachable_address_check(options, pref_addr); + const bool direct_bridge = direct_conn && options->UseBridges; + + if (!node->is_running || !node->is_valid) + return false; + if (need_desc && !node_has_preferred_descriptor(node, direct_conn)) + return false; + if (node->ri) { + if (direct_bridge && node->ri->purpose != ROUTER_PURPOSE_BRIDGE) + return false; + else if (node->ri->purpose != ROUTER_PURPOSE_GENERAL) + return false; + } + if (node_is_unreliable(node, need_uptime, need_capacity, need_guard)) + return false; + /* Don't choose nodes if we are certain they can't do EXTEND2 cells */ + if (node->rs && !routerstatus_version_supports_extend2_cells(node->rs, 1)) + return false; + /* Don't choose nodes if we are certain they can't do ntor. */ + if ((node->ri || node->md) && !node_has_curve25519_onion_key(node)) + return false; + /* Exclude relays that allow single hop exit circuits. This is an + * obsolete option since 0.2.9.2-alpha and done by default in + * 0.3.1.0-alpha. */ + if (node_allows_single_hop_exits(node)) + return false; + /* Exclude relays that can not become a rendezvous for a hidden service + * version 3. */ + if (rendezvous_v3 && + !node_supports_v3_rendezvous_point(node)) + return false; + /* Choose a node with an OR address that matches the firewall rules */ + if (direct_conn && check_reach && + !fascist_firewall_allows_node(node, + FIREWALL_OR_CONNECTION, + pref_addr)) + return false; + if (initiate_ipv6_extend && !node_supports_initiating_ipv6_extends(node)) + return false; + + return true; +} + /** Add every suitable node from our nodelist to <b>sl</b>, so that - * we can pick a node for a circuit. + * we can pick a node for a circuit based on <b>flags</b>. + * + * See router_can_choose_node() for details of <b>flags</b>. */ void -router_add_running_nodes_to_smartlist(smartlist_t *sl, int need_uptime, - int need_capacity, int need_guard, - int need_desc, int pref_addr, - int direct_conn) -{ - const int check_reach = !router_skip_or_reachability(get_options(), - pref_addr); - /* XXXX MOVE */ +router_add_running_nodes_to_smartlist(smartlist_t *sl, int flags) +{ SMARTLIST_FOREACH_BEGIN(nodelist_get_list(), const node_t *, node) { - if (!node->is_running || !node->is_valid) - continue; - if (need_desc && !node_has_preferred_descriptor(node, direct_conn)) - continue; - if (node->ri && node->ri->purpose != ROUTER_PURPOSE_GENERAL) - continue; - if (node_is_unreliable(node, need_uptime, need_capacity, need_guard)) + if (!router_can_choose_node(node, flags)) continue; - /* Don't choose nodes if we are certain they can't do EXTEND2 cells */ - if (node->rs && !routerstatus_version_supports_extend2_cells(node->rs, 1)) - continue; - /* Don't choose nodes if we are certain they can't do ntor. */ - if ((node->ri || node->md) && !node_has_curve25519_onion_key(node)) - continue; - /* Choose a node with an OR address that matches the firewall rules */ - if (direct_conn && check_reach && - !fascist_firewall_allows_node(node, - FIREWALL_OR_CONNECTION, - pref_addr)) - continue; - smartlist_add(sl, (void *)node); } SMARTLIST_FOREACH_END(node); } @@ -1088,7 +1171,11 @@ extrainfo_insert,(routerlist_t *rl, extrainfo_t *ei, int warn_if_incompatible)) * This just won't work. */; static ratelim_t no_sd_ratelim = RATELIM_INIT(1800); r = ROUTER_BAD_EI; - log_fn_ratelim(&no_sd_ratelim, severity, LD_BUG, + /* This is a DEBUG because it can happen naturally, if we tried + * to add an extrainfo for which we no longer have the + * corresponding routerinfo. + */ + log_fn_ratelim(&no_sd_ratelim, LOG_DEBUG, LD_DIR, "No entry found in extrainfo map."); goto done; } @@ -2878,12 +2965,12 @@ router_differences_are_cosmetic(const routerinfo_t *r1, const routerinfo_t *r2) } /* If any key fields differ, they're different. */ - if (r1->addr != r2->addr || + if (!tor_addr_eq(&r1->ipv4_addr, &r2->ipv4_addr) || strcasecmp(r1->nickname, r2->nickname) || - r1->or_port != r2->or_port || + r1->ipv4_orport != r2->ipv4_orport || !tor_addr_eq(&r1->ipv6_addr, &r2->ipv6_addr) || r1->ipv6_orport != r2->ipv6_orport || - r1->dir_port != r2->dir_port || + r1->ipv4_dirport != r2->ipv4_dirport || r1->purpose != r2->purpose || r1->onion_pkey_len != r2->onion_pkey_len || !tor_memeq(r1->onion_pkey, r2->onion_pkey, r1->onion_pkey_len) || diff --git a/src/feature/nodelist/routerlist.h b/src/feature/nodelist/routerlist.h index 81a2343540..98472b2771 100644 --- a/src/feature/nodelist/routerlist.h +++ b/src/feature/nodelist/routerlist.h @@ -50,14 +50,16 @@ typedef enum was_router_added_t { int router_reload_router_list(void); -int router_skip_or_reachability(const or_options_t *options, int try_ip_pref); -int router_skip_dir_reachability(const or_options_t *options, int try_ip_pref); +int router_or_conn_should_skip_reachable_address_check( + const or_options_t *options, + int try_ip_pref); +int router_dir_conn_should_skip_reachable_address_check( + const or_options_t *options, + int try_ip_pref); void router_reset_status_download_failures(void); int routers_have_same_or_addrs(const routerinfo_t *r1, const routerinfo_t *r2); -void router_add_running_nodes_to_smartlist(smartlist_t *sl, int need_uptime, - int need_capacity, int need_guard, - int need_desc, int pref_addr, - int direct_conn); +bool router_can_choose_node(const node_t *node, int flags); +void router_add_running_nodes_to_smartlist(smartlist_t *sl, int flags); const routerinfo_t *routerlist_find_my_routerinfo(void); uint32_t router_get_advertised_bandwidth(const routerinfo_t *router); diff --git a/src/feature/nodelist/routerset.c b/src/feature/nodelist/routerset.c index 2e06ecbf04..7234dc5441 100644 --- a/src/feature/nodelist/routerset.c +++ b/src/feature/nodelist/routerset.c @@ -223,11 +223,11 @@ routerset_len(const routerset_t *set) * * (If country is -1, then we take the country * from addr.) */ -STATIC int -routerset_contains(const routerset_t *set, const tor_addr_t *addr, - uint16_t orport, - const char *nickname, const char *id_digest, - country_t country) +static int +routerset_contains2(const routerset_t *set, const tor_addr_t *addr, + uint16_t orport, const tor_addr_t *addr2, + uint16_t orport2, const char *nickname, + const char *id_digest, country_t country) { if (!set || !set->list) return 0; @@ -238,6 +238,9 @@ routerset_contains(const routerset_t *set, const tor_addr_t *addr, if (addr && compare_tor_addr_to_addr_policy(addr, orport, set->policies) == ADDR_POLICY_REJECTED) return 3; + if (addr2 && compare_tor_addr_to_addr_policy(addr2, orport2, set->policies) + == ADDR_POLICY_REJECTED) + return 3; if (set->countries) { if (country < 0 && addr) country = geoip_get_country_by_addr(addr); @@ -249,6 +252,17 @@ routerset_contains(const routerset_t *set, const tor_addr_t *addr, return 0; } +/** Helper. Like routerset_contains2() but for a single IP/port combo. + */ +STATIC int +routerset_contains(const routerset_t *set, const tor_addr_t *addr, + uint16_t orport, const char *nickname, + const char *id_digest, country_t country) +{ + return routerset_contains2(set, addr, orport, NULL, 0, + nickname, id_digest, country); +} + /** If *<b>setp</b> includes at least one country code, or if * <b>only_some_cc_set</b> is 0, add the ?? and A1 country codes to * *<b>setp</b>, creating it as needed. Return true iff *<b>setp</b> changed. @@ -292,12 +306,19 @@ routerset_add_unknown_ccs(routerset_t **setp, int only_if_some_cc_set) int routerset_contains_extendinfo(const routerset_t *set, const extend_info_t *ei) { - return routerset_contains(set, - &ei->addr, - ei->port, - ei->nickname, - ei->identity_digest, - -1 /*country*/); + const tor_addr_port_t *ap1 = NULL, *ap2 = NULL; + if (! tor_addr_is_null(&ei->orports[0].addr)) + ap1 = &ei->orports[0]; + if (! tor_addr_is_null(&ei->orports[1].addr)) + ap2 = &ei->orports[1]; + return routerset_contains2(set, + ap1 ? &ap1->addr : NULL, + ap1 ? ap1->port : 0, + ap2 ? &ap2->addr : NULL, + ap2 ? ap2->port : 0, + ei->nickname, + ei->identity_digest, + -1 /*country*/); } /** Return true iff <b>ri</b> is in <b>set</b>. If country is <b>-1</b>, we @@ -306,14 +327,9 @@ int routerset_contains_router(const routerset_t *set, const routerinfo_t *ri, country_t country) { - tor_addr_t addr; - tor_addr_from_ipv4h(&addr, ri->addr); - return routerset_contains(set, - &addr, - ri->or_port, - ri->nickname, - ri->cache_info.identity_digest, - country); + return routerset_contains2(set, &ri->ipv4_addr, ri->ipv4_orport, + &ri->ipv6_addr, ri->ipv6_orport, ri->nickname, + ri->cache_info.identity_digest, country); } /** Return true iff <b>rs</b> is in <b>set</b>. If country is <b>-1</b>, we @@ -323,11 +339,9 @@ routerset_contains_routerstatus(const routerset_t *set, const routerstatus_t *rs, country_t country) { - tor_addr_t addr; - tor_addr_from_ipv4h(&addr, rs->addr); return routerset_contains(set, - &addr, - rs->or_port, + &rs->ipv4_addr, + rs->ipv4_orport, rs->nickname, rs->identity_digest, country); diff --git a/src/feature/nodelist/routerstatus_st.h b/src/feature/nodelist/routerstatus_st.h index 735c754b31..254ba73f7f 100644 --- a/src/feature/nodelist/routerstatus_st.h +++ b/src/feature/nodelist/routerstatus_st.h @@ -29,9 +29,9 @@ struct routerstatus_t { /** Digest of the router's most recent descriptor or microdescriptor. * If it's a descriptor, we only use the first DIGEST_LEN bytes. */ char descriptor_digest[DIGEST256_LEN]; - uint32_t addr; /**< IPv4 address for this router, in host order. */ - uint16_t or_port; /**< IPv4 OR port for this router. */ - uint16_t dir_port; /**< Directory port for this router. */ + tor_addr_t ipv4_addr; + uint16_t ipv4_orport; /**< IPv4 OR port for this router. */ + uint16_t ipv4_dirport; /**< Directory port for this router. */ tor_addr_t ipv6_addr; /**< IPv6 address for this router. */ uint16_t ipv6_orport; /**< IPv6 OR port for this router. */ unsigned int is_authority:1; /**< True iff this router is an authority. */ diff --git a/src/feature/relay/circuitbuild_relay.c b/src/feature/relay/circuitbuild_relay.c index b89866b477..ad20e143be 100644 --- a/src/feature/relay/circuitbuild_relay.c +++ b/src/feature/relay/circuitbuild_relay.c @@ -33,6 +33,7 @@ #include "core/or/channel.h" #include "core/or/circuitbuild.h" #include "core/or/circuitlist.h" +#include "core/or/extendinfo.h" #include "core/or/onion.h" #include "core/or/relay.h" @@ -122,6 +123,52 @@ circuit_extend_add_ed25519_helper(struct extend_cell_t *ec) return 0; } +/* Make sure the extend cell <b>ec</b> has an IPv4 address if the relay + * supports in, and if not, fill it in. */ +STATIC int +circuit_extend_add_ipv4_helper(struct extend_cell_t *ec) +{ + IF_BUG_ONCE(!ec) { + return -1; + } + + const node_t *node = node_get_by_id((const char *) ec->node_id); + if (node) { + tor_addr_port_t node_ipv4; + node_get_prim_orport(node, &node_ipv4); + if (tor_addr_is_null(&ec->orport_ipv4.addr) && + !tor_addr_is_null(&node_ipv4.addr)) { + tor_addr_copy(&ec->orport_ipv4.addr, &node_ipv4.addr); + ec->orport_ipv4.port = node_ipv4.port; + } + } + + return 0; +} + +/* Make sure the extend cell <b>ec</b> has an IPv6 address if the relay + * supports in, and if not, fill it in. */ +STATIC int +circuit_extend_add_ipv6_helper(struct extend_cell_t *ec) +{ + IF_BUG_ONCE(!ec) { + return -1; + } + + const node_t *node = node_get_by_id((const char *) ec->node_id); + if (node) { + tor_addr_port_t node_ipv6; + node_get_pref_ipv6_orport(node, &node_ipv6); + if (tor_addr_is_null(&ec->orport_ipv6.addr) && + !tor_addr_is_null(&node_ipv6.addr)) { + tor_addr_copy(&ec->orport_ipv6.addr, &node_ipv6.addr); + ec->orport_ipv6.port = node_ipv6.port; + } + } + + return 0; +} + /* Check if the address and port in the tor_addr_port_t <b>ap</b> are valid, * and are allowed by the current ExtendAllowPrivateAddresses config. * @@ -354,11 +401,7 @@ circuit_open_connection_for_extend(const struct extend_cell_t *ec, if (should_launch) { /* we should try to open a connection */ - channel_t *n_chan = channel_connect_for_circuit( - &circ->n_hop->addr, - circ->n_hop->port, - circ->n_hop->identity_digest, - &circ->n_hop->ed_identity); + channel_t *n_chan = channel_connect_for_circuit(circ->n_hop); if (!n_chan) { log_info(LD_CIRC,"Launching n_chan failed. Closing circuit."); circuit_mark_for_close(circ, END_CIRC_REASON_CONNECTFAILED); @@ -412,6 +455,12 @@ circuit_extend(struct cell_t *cell, struct circuit_t *circ) if (circuit_extend_lspec_valid_helper(&ec, circ) < 0) return -1; + if (circuit_extend_add_ipv4_helper(&ec) < 0) + return -1; + + if (circuit_extend_add_ipv6_helper(&ec) < 0) + return -1; + /* Check the addresses, without logging */ const int ipv4_valid = circuit_extend_addr_port_is_valid(&ec.orport_ipv4, false, false, 0); @@ -452,7 +501,7 @@ circuit_extend(struct cell_t *cell, struct circuit_t *circ) circ->n_chan = n_chan; log_debug(LD_CIRC, "n_chan is %s.", - channel_get_canonical_remote_descr(n_chan)); + channel_describe_peer(n_chan)); if (circuit_deliver_create_cell(circ, &ec.create_cell, 1) < 0) return -1; @@ -542,7 +591,11 @@ onionskin_answer(struct or_circuit_t *circ, /* record that we could process create cells from a non-local conn * that we didn't initiate; presumably this means that create cells * can reach us too. */ - router_orport_found_reachable(); + tor_addr_t remote_addr; + if (channel_get_addr_if_possible(circ->p_chan, &remote_addr)) { + int family = tor_addr_family(&remote_addr); + router_orport_found_reachable(family); + } } return 0; diff --git a/src/feature/relay/circuitbuild_relay.h b/src/feature/relay/circuitbuild_relay.h index 0783161538..dc0b886a34 100644 --- a/src/feature/relay/circuitbuild_relay.h +++ b/src/feature/relay/circuitbuild_relay.h @@ -73,6 +73,8 @@ onionskin_answer(struct or_circuit_t *circ, STATIC int circuit_extend_state_valid_helper(const struct circuit_t *circ); STATIC int circuit_extend_add_ed25519_helper(struct extend_cell_t *ec); +STATIC int circuit_extend_add_ipv4_helper(struct extend_cell_t *ec); +STATIC int circuit_extend_add_ipv6_helper(struct extend_cell_t *ec); STATIC int circuit_extend_lspec_valid_helper(const struct extend_cell_t *ec, const struct circuit_t *circ); STATIC const tor_addr_port_t * circuit_choose_ip_ap_for_extend( diff --git a/src/feature/relay/relay_config.c b/src/feature/relay/relay_config.c index fac6a2f577..9273a7cef0 100644 --- a/src/feature/relay/relay_config.c +++ b/src/feature/relay/relay_config.c @@ -36,6 +36,7 @@ #include "feature/nodelist/nickname.h" #include "feature/stats/geoip_stats.h" #include "feature/stats/predict_ports.h" +#include "feature/stats/connstats.h" #include "feature/stats/rephist.h" #include "feature/dirauth/authmode.h" @@ -132,12 +133,96 @@ port_warn_nonlocal_ext_orports(const smartlist_t *ports, const char *portname) } SMARTLIST_FOREACH_END(port); } +/** Return a static buffer containing the human readable logging string that + * describes the given port object. */ +static const char * +describe_relay_port(const port_cfg_t *port) +{ + IF_BUG_ONCE(!port) { + return "<null port>"; + } + + static char buf[256]; + const char *type, *addr; + + switch (port->type) { + case CONN_TYPE_OR_LISTENER: + type = "OR"; + break; + case CONN_TYPE_DIR_LISTENER: + type = "Dir"; + break; + case CONN_TYPE_EXT_OR_LISTENER: + type = "ExtOR"; + break; + default: + type = ""; + break; + } + + if (port->explicit_addr) { + addr = fmt_and_decorate_addr(&port->addr); + } else { + addr = ""; + } + + tor_snprintf(buf, sizeof(buf), "%sPort %s%s%d", + type, addr, (strlen(addr) > 0) ? ":" : "", port->port); + return buf; +} + +/** Attempt to find duplicate ORPort that would be superseded by another and + * remove them from the given ports list. This is possible if we have for + * instance: + * + * ORPort 9050 + * ORPort [4242::1]:9050 + * + * First one binds to both v4 and v6 address but second one is specific to an + * address superseding the global bind one. + * + * The following is O(n^2) but it is done at bootstrap or config reload and + * the list is not very long usually. */ +static void +remove_duplicate_orports(smartlist_t *ports) +{ + for (int i = 0; i < smartlist_len(ports); ++i) { + port_cfg_t *current = smartlist_get(ports, i); + + /* Skip non ORPorts. */ + if (current->type != CONN_TYPE_OR_LISTENER) { + continue; + } + + for (int j = 0; j < smartlist_len(ports); ++j) { + port_cfg_t *next = smartlist_get(ports, j); + + /* Avoid comparing the same object. */ + if (current == next) { + continue; + } + /* Same address family and same port number, we have a match. */ + if (!current->explicit_addr && next->explicit_addr && + tor_addr_family(¤t->addr) == tor_addr_family(&next->addr) && + current->port == next->port) { + /* Remove current because next is explicitly set. */ + smartlist_del_keeporder(ports, i--); + char *next_str = tor_strdup(describe_relay_port(next)); + log_warn(LD_CONFIG, "Configuration port %s superseded by %s", + describe_relay_port(current), next_str); + tor_free(next_str); + port_cfg_free(current); + } + } + } +} + /** Given a list of <b>port_cfg_t</b> in <b>ports</b>, check them for internal * consistency and warn as appropriate. On Unix-based OSes, set * *<b>n_low_ports_out</b> to the number of sub-1024 ports we will be * binding, and warn if we may be unable to re-bind after hibernation. */ static int -check_server_ports(const smartlist_t *ports, +check_and_prune_server_ports(smartlist_t *ports, const or_options_t *options, int *n_low_ports_out) { @@ -158,6 +243,9 @@ check_server_ports(const smartlist_t *ports, int n_low_port = 0; int r = 0; + /* Remove possible duplicate ORPorts before inspecting the list. */ + remove_duplicate_orports(ports); + SMARTLIST_FOREACH_BEGIN(ports, const port_cfg_t *, port) { if (port->type == CONN_TYPE_DIR_LISTENER) { if (! port->server_cfg.no_advertise) @@ -270,6 +358,14 @@ port_parse_ports_relay(or_options_t *options, goto err; } if (port_parse_config(ports, + options->ORPort_lines, + "OR", CONN_TYPE_OR_LISTENER, + "[::]", 0, + CL_PORT_SERVER_OPTIONS) < 0) { + *msg = tor_strdup("Invalid ORPort configuration"); + goto err; + } + if (port_parse_config(ports, options->ExtORPort_lines, "ExtOR", CONN_TYPE_EXT_OR_LISTENER, "127.0.0.1", 0, @@ -286,7 +382,7 @@ port_parse_ports_relay(or_options_t *options, goto err; } - if (check_server_ports(ports, options, &n_low_ports) < 0) { + if (check_and_prune_server_ports(ports, options, &n_low_ports) < 0) { *msg = tor_strdup("Misconfigured server ports"); goto err; } @@ -1029,7 +1125,7 @@ options_transition_affects_descriptor(const or_options_t *old_options, YES_IF_CHANGED_STRING(DataDirectory); YES_IF_CHANGED_STRING(Nickname); - YES_IF_CHANGED_STRING(Address); + YES_IF_CHANGED_LINELIST(Address); YES_IF_CHANGED_LINELIST(ExitPolicy); YES_IF_CHANGED_BOOL(ExitRelay); YES_IF_CHANGED_BOOL(ExitPolicyRejectPrivate); @@ -1114,8 +1210,6 @@ options_act_relay(const or_options_t *old_options) if (server_mode_turned_on) { ip_address_changed(0); - if (have_completed_a_circuit() || !any_predicted_circuits(time(NULL))) - inform_testing_reachability(); } cpuworkers_rotate_keyinfo(); } @@ -1309,7 +1403,7 @@ options_act_relay_stats(const or_options_t *old_options, } if ((!old_options || !old_options->ConnDirectionStatistics) && options->ConnDirectionStatistics) { - rep_hist_conn_stats_init(now); + conn_stats_init(now); } if ((!old_options || !old_options->HiddenServiceStatistics) && options->HiddenServiceStatistics) { @@ -1339,7 +1433,7 @@ options_act_relay_stats(const or_options_t *old_options, rep_hist_exit_stats_term(); if (old_options && old_options->ConnDirectionStatistics && !options->ConnDirectionStatistics) - rep_hist_conn_stats_term(); + conn_stats_terminate(); return 0; } diff --git a/src/feature/relay/relay_find_addr.c b/src/feature/relay/relay_find_addr.c index 86cd799d42..43b958d563 100644 --- a/src/feature/relay/relay_find_addr.c +++ b/src/feature/relay/relay_find_addr.c @@ -15,119 +15,139 @@ #include "feature/control/control_events.h" #include "feature/dircommon/dir_connection_st.h" +#include "feature/nodelist/dirlist.h" #include "feature/relay/relay_find_addr.h" #include "feature/relay/router.h" #include "feature/relay/routermode.h" -/** The most recently guessed value of our IP address, based on directory - * headers. */ -static tor_addr_t last_guessed_ip = TOR_ADDR_NULL; - -/** We failed to resolve our address locally, but we'd like to build - * a descriptor and publish / test reachability. If we have a guess - * about our address based on directory headers, answer it and return - * 0; else return -1. */ -static int -router_guess_address_from_dir_headers(uint32_t *guess) -{ - if (!tor_addr_is_null(&last_guessed_ip)) { - *guess = tor_addr_to_ipv4h(&last_guessed_ip); - return 0; - } - return -1; -} - -/** A directory server <b>d_conn</b> told us our IP address is - * <b>suggestion</b>. - * If this address is different from the one we think we are now, and - * if our computer doesn't actually know its IP address, then switch. */ +/** Consider the address suggestion suggested_addr as a possible one to use as + * our address. + * + * This is called when a valid NETINFO cell is received containing a candidate + * for our address or when a directory sends us back the X-Your-Address-Is + * header. + * + * The suggested address is ignored if it does NOT come from a trusted source. + * At the moment, we only look a trusted directory authorities. + * + * The suggested address is ignored if it is internal or it is the same as the + * given peer_addr which is the address from the endpoint that sent the + * NETINFO cell. + * + * The identity_digest is NULL if this is an address suggested by a directory + * since this is a plaintext connection. + * + * The suggested address is set in our suggested address cache if everything + * passes. */ void -router_new_address_suggestion(const char *suggestion, - const dir_connection_t *d_conn) +relay_address_new_suggestion(const tor_addr_t *suggested_addr, + const tor_addr_t *peer_addr, + const char *identity_digest) { - tor_addr_t addr; - uint32_t cur = 0; /* Current IPv4 address. */ const or_options_t *options = get_options(); - /* first, learn what the IP address actually is */ - if (tor_addr_parse(&addr, suggestion) == -1) { - log_debug(LD_DIR, "Malformed X-Your-Address-Is header %s. Ignoring.", - escaped(suggestion)); - return; - } - - log_debug(LD_DIR, "Got X-Your-Address-Is: %s.", suggestion); + tor_assert(suggested_addr); + tor_assert(peer_addr); + /* Non server should just ignore this suggestion. Clients don't need to + * learn their address let alone cache it. */ if (!server_mode(options)) { - tor_addr_copy(&last_guessed_ip, &addr); return; } - /* XXXX ipv6 */ - cur = get_last_resolved_addr(); - if (cur || - resolve_my_address(LOG_INFO, options, &cur, NULL, NULL) >= 0) { - /* We're all set -- we already know our address. Great. */ - tor_addr_from_ipv4h(&last_guessed_ip, cur); /* store it in case we - need it later */ + /* Is the peer a trusted source? Ignore anything coming from non trusted + * source. In this case, we only look at trusted directory authorities. */ + if (!router_addr_is_trusted_dir(peer_addr) || + (identity_digest && !router_digest_is_trusted_dir(identity_digest))) { return; } - if (tor_addr_is_internal(&addr, 0)) { - /* Don't believe anybody who says our IP is, say, 127.0.0.1. */ + + /* Ignore a suggestion that is an internal address or the same as the one + * the peer address. */ + if (tor_addr_is_internal(suggested_addr, 0)) { + /* Do not believe anyone who says our address is internal. */ return; } - if (tor_addr_eq(&d_conn->base_.addr, &addr)) { - /* Don't believe anybody who says our IP is their IP. */ - log_debug(LD_DIR, "A directory server told us our IP address is %s, " - "but they are just reporting their own IP address. Ignoring.", - suggestion); + if (tor_addr_eq(suggested_addr, peer_addr)) { + /* Do not believe anyone who says our address is their address. */ + log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, + "A relay endpoint %s is telling us that their address is ours.", + fmt_addr(peer_addr)); return; } - /* Okay. We can't resolve our own address, and X-Your-Address-Is is giving - * us an answer different from what we had the last time we managed to - * resolve it. */ - if (!tor_addr_eq(&last_guessed_ip, &addr)) { - control_event_server_status(LOG_NOTICE, - "EXTERNAL_ADDRESS ADDRESS=%s METHOD=DIRSERV", - suggestion); - log_addr_has_changed(LOG_NOTICE, &last_guessed_ip, &addr, - d_conn->base_.address); - ip_address_changed(0); - tor_addr_copy(&last_guessed_ip, &addr); /* router_rebuild_descriptor() - will fetch it */ - } + /* Save the suggestion in our cache. */ + resolved_addr_set_suggested(suggested_addr); } -/** Make a current best guess at our address, either because - * it's configured in torrc, or because we've learned it from - * dirserver headers. Place the answer in *<b>addr</b> and return - * 0 on success, else return -1 if we have no guess. +/** Find our address to be published in our descriptor. Three places are + * looked at: * - * If <b>cache_only</b> is true, just return any cached answers, and - * don't try to get any new answers. - */ -MOCK_IMPL(int, -router_pick_published_address, (const or_options_t *options, uint32_t *addr, - int cache_only)) + * 1. Resolved cache. Populated by find_my_address() during the relay + * periodic event that attempts to learn if our address has changed. + * + * 2. If flags is set with RELAY_FIND_ADDR_CACHE_ONLY, only the resolved + * and suggested cache are looked at. No address discovery will be done. + * + * 3. Finally, if all fails, use the suggested address cache which is + * populated by the NETINFO cell content or HTTP header from a + * directory. + * + * Return true on success and addr_out contains the address to use for the + * given family. On failure to find the address, false is returned and + * addr_out is set to an AF_UNSPEC address. */ +MOCK_IMPL(bool, +relay_find_addr_to_publish, (const or_options_t *options, int family, + int flags, tor_addr_t *addr_out)) { - /* First, check the cached output from resolve_my_address(). */ - *addr = get_last_resolved_addr(); - if (*addr) - return 0; - - /* Second, consider doing a resolve attempt right here. */ - if (!cache_only) { - if (resolve_my_address(LOG_INFO, options, addr, NULL, NULL) >= 0) { - log_info(LD_CONFIG,"Success: chose address '%s'.", fmt_addr32(*addr)); - return 0; + tor_assert(options); + tor_assert(addr_out); + + tor_addr_make_unspec(addr_out); + + /* If an IPv6 is requested, check if IPv6 address discovery is disabled on + * this instance. If so, we return a failure. It is done here so we don't + * query the suggested cache that might be populated with an IPv6. */ + if (family == AF_INET6 && options->AddressDisableIPv6) { + return false; + } + + /* First, check our resolved address cache. It should contain the address + * we've discovered from the periodic relay event. */ + resolved_addr_get_last(family, addr_out); + if (!tor_addr_is_null(addr_out)) { + goto found; + } + + /* Second, attempt to find our address. The following can do a DNS resolve + * thus only do it when the no cache only flag is flipped. */ + if (!(flags & RELAY_FIND_ADDR_CACHE_ONLY)) { + if (find_my_address(options, family, LOG_INFO, addr_out, NULL, NULL)) { + goto found; } } - /* Third, check the cached output from router_new_address_suggestion(). */ - if (router_guess_address_from_dir_headers(addr) >= 0) - return 0; + /* Third, consider address from our suggestion cache. */ + resolved_addr_get_suggested(family, addr_out); + if (!tor_addr_is_null(addr_out)) { + goto found; + } + + /* No publishable address was found. */ + return false; + + found: + return true; +} - /* We have no useful cached answers. Return failure. */ - return -1; +/** Return true iff this relay has an address set for the given family. + * + * This only checks the caches so it will not trigger a full discovery of the + * address. */ +bool +relay_has_address_set(int family) +{ + tor_addr_t addr; + return relay_find_addr_to_publish(get_options(), family, + RELAY_FIND_ADDR_CACHE_ONLY, &addr); } diff --git a/src/feature/relay/relay_find_addr.h b/src/feature/relay/relay_find_addr.h index ac51a977e6..3d30946b05 100644 --- a/src/feature/relay/relay_find_addr.h +++ b/src/feature/relay/relay_find_addr.h @@ -9,11 +9,20 @@ #ifndef TOR_RELAY_FIND_ADDR_H #define TOR_RELAY_FIND_ADDR_H -MOCK_DECL(int, router_pick_published_address, - (const or_options_t *options, uint32_t *addr, int cache_only)); +typedef enum { + RELAY_FIND_ADDR_NO_FLAG = (1U << 0), + RELAY_FIND_ADDR_CACHE_ONLY = (1U << 1), +} relay_find_addr_flags_t; -void router_new_address_suggestion(const char *suggestion, - const dir_connection_t *d_conn); +void relay_address_new_suggestion(const tor_addr_t *suggested_addr, + const tor_addr_t *peer_addr, + const char *identity_digest); + +MOCK_DECL(bool, relay_find_addr_to_publish, + (const or_options_t *options, int family, int flags, + tor_addr_t *addr_out)); + +bool relay_has_address_set(int family); #ifdef RELAY_FIND_ADDR_PRIVATE diff --git a/src/feature/relay/relay_periodic.c b/src/feature/relay/relay_periodic.c index 08ad110cf6..cc346bc3fc 100644 --- a/src/feature/relay/relay_periodic.c +++ b/src/feature/relay/relay_periodic.c @@ -201,36 +201,60 @@ reachability_warnings_callback(time_t now, const or_options_t *options) have_completed_a_circuit()) { /* every 20 minutes, check and complain if necessary */ const routerinfo_t *me = router_get_my_routerinfo(); - if (me && !check_whether_orport_reachable(options)) { - char *address = tor_dup_ip(me->addr); - if (address) { + bool v4_ok = + router_orport_seems_reachable(options,AF_INET); + bool v6_ok = + router_orport_seems_reachable(options,AF_INET6); + if (me && !(v4_ok && v6_ok)) { + /* We need to warn that one or more of our ORPorts isn't reachable. + * Determine which, and give a reasonable warning. */ + char *address4 = tor_addr_to_str_dup(&me->ipv4_addr); + char *address6 = tor_addr_to_str_dup(&me->ipv6_addr); + if (address4 || address6) { + char *where4=NULL, *where6=NULL; + if (!v4_ok) + tor_asprintf(&where4, "%s:%d", address4, me->ipv4_orport); + if (!v6_ok) + tor_asprintf(&where6, "[%s]:%d", address6, me->ipv6_orport); + const char *opt_and = (!v4_ok && !v6_ok) ? "and" : ""; + log_warn(LD_CONFIG, - "Your server (%s:%d) has not managed to confirm that " - "its ORPort is reachable. Relays do not publish descriptors " + "Your server has not managed to confirm reachability for " + "its ORPort(s) at %s%s%s. Relays do not publish descriptors " "until their ORPort and DirPort are reachable. Please check " "your firewalls, ports, address, /etc/hosts file, etc.", - address, me->or_port); - control_event_server_status(LOG_WARN, - "REACHABILITY_FAILED ORADDRESS=%s:%d", - address, me->or_port); - tor_free(address); + where4?where4:"", + opt_and, + where6?where6:""); + tor_free(where4); + tor_free(where6); + if (!v4_ok) { + control_event_server_status(LOG_WARN, + "REACHABILITY_FAILED ORADDRESS=%s:%d", + address4, me->ipv4_orport); + } + if (!v6_ok) { + control_event_server_status(LOG_WARN, + "REACHABILITY_FAILED ORADDRESS=[%s]:%d", + address6, me->ipv6_orport); + } } + tor_free(address4); + tor_free(address6); } - if (me && !check_whether_dirport_reachable(options)) { - char *address = tor_dup_ip(me->addr); - if (address) { - log_warn(LD_CONFIG, - "Your server (%s:%d) has not managed to confirm that its " - "DirPort is reachable. Relays do not publish descriptors " - "until their ORPort and DirPort are reachable. Please check " - "your firewalls, ports, address, /etc/hosts file, etc.", - address, me->dir_port); - control_event_server_status(LOG_WARN, - "REACHABILITY_FAILED DIRADDRESS=%s:%d", - address, me->dir_port); - tor_free(address); - } + if (me && !router_dirport_seems_reachable(options)) { + char *address4 = tor_addr_to_str_dup(&me->ipv4_addr); + log_warn(LD_CONFIG, + "Your server (%s:%d) has not managed to confirm that its " + "DirPort is reachable. Relays do not publish descriptors " + "until their ORPort and DirPort are reachable. Please check " + "your firewalls, ports, address, /etc/hosts file, etc.", + address4, me->ipv4_dirport); + control_event_server_status(LOG_WARN, + "REACHABILITY_FAILED DIRADDRESS=%s:%d", + address4, me->ipv4_dirport); + tor_free(address4); } } diff --git a/src/feature/relay/router.c b/src/feature/relay/router.c index ffaf7c3cc5..206545bdec 100644 --- a/src/feature/relay/router.c +++ b/src/feature/relay/router.c @@ -38,12 +38,14 @@ #include "feature/relay/dns.h" #include "feature/relay/relay_config.h" #include "feature/relay/relay_find_addr.h" +#include "feature/relay/relay_periodic.h" #include "feature/relay/router.h" #include "feature/relay/routerkeys.h" #include "feature/relay/routermode.h" #include "feature/relay/selftest.h" #include "lib/geoip/geoip.h" #include "feature/stats/geoip_stats.h" +#include "feature/stats/bwhist.h" #include "feature/stats/rephist.h" #include "lib/crypt_ops/crypto_ed25519.h" #include "lib/crypt_ops/crypto_format.h" @@ -831,30 +833,37 @@ router_initialize_tls_context(void) * -1 if Tor should die, */ STATIC int -router_write_fingerprint(int hashed) +router_write_fingerprint(int hashed, int ed25519_identity) { char *keydir = NULL, *cp = NULL; const char *fname = hashed ? "hashed-fingerprint" : - "fingerprint"; + (ed25519_identity ? "fingerprint-ed25519" : + "fingerprint"); char fingerprint[FINGERPRINT_LEN+1]; const or_options_t *options = get_options(); char *fingerprint_line = NULL; int result = -1; keydir = get_datadir_fname(fname); - log_info(LD_GENERAL,"Dumping %sfingerprint to \"%s\"...", - hashed ? "hashed " : "", keydir); - if (!hashed) { - if (crypto_pk_get_fingerprint(get_server_identity_key(), - fingerprint, 0) < 0) { - log_err(LD_GENERAL,"Error computing fingerprint"); - goto done; - } - } else { - if (crypto_pk_get_hashed_fingerprint(get_server_identity_key(), - fingerprint) < 0) { - log_err(LD_GENERAL,"Error computing hashed fingerprint"); - goto done; + log_info(LD_GENERAL,"Dumping %s%s to \"%s\"...", hashed ? "hashed " : "", + ed25519_identity ? "ed25519 identity" : "fingerprint", keydir); + + if (ed25519_identity) { /* ed25519 identity */ + digest256_to_base64(fingerprint, (const char *) + get_master_identity_key()->pubkey); + } else { /* RSA identity */ + if (!hashed) { + if (crypto_pk_get_fingerprint(get_server_identity_key(), + fingerprint, 0) < 0) { + log_err(LD_GENERAL,"Error computing fingerprint"); + goto done; + } + } else { + if (crypto_pk_get_hashed_fingerprint(get_server_identity_key(), + fingerprint) < 0) { + log_err(LD_GENERAL,"Error computing hashed fingerprint"); + goto done; + } } } @@ -865,15 +874,17 @@ router_write_fingerprint(int hashed) cp = read_file_to_str(keydir, RFTS_IGNORE_MISSING, NULL); if (!cp || strcmp(cp, fingerprint_line)) { if (write_str_to_file(keydir, fingerprint_line, 0)) { - log_err(LD_FS, "Error writing %sfingerprint line to file", - hashed ? "hashed " : ""); + log_err(LD_FS, "Error writing %s%s line to file", + hashed ? "hashed " : "", + ed25519_identity ? "ed25519 identity" : "fingerprint"); goto done; } } - log_notice(LD_GENERAL, "Your Tor %s identity key fingerprint is '%s %s'", - hashed ? "bridge's hashed" : "server's", options->Nickname, - fingerprint); + log_notice(LD_GENERAL, "Your Tor %s identity key %s fingerprint is '%s %s'", + hashed ? "bridge's hashed" : "server's", + ed25519_identity ? "ed25519" : "", + options->Nickname, fingerprint); result = 0; done: @@ -1109,15 +1120,20 @@ init_keys(void) } } - /* 5. Dump fingerprint and possibly hashed fingerprint to files. */ - if (router_write_fingerprint(0)) { + /* 5. Dump fingerprint, ed25519 identity and possibly hashed fingerprint + * to files. */ + if (router_write_fingerprint(0, 0)) { log_err(LD_FS, "Error writing fingerprint to file"); return -1; } - if (!public_server_mode(options) && router_write_fingerprint(1)) { + if (!public_server_mode(options) && router_write_fingerprint(1, 0)) { log_err(LD_FS, "Error writing hashed fingerprint to file"); return -1; } + if (router_write_fingerprint(0, 1)) { + log_err(LD_FS, "Error writing ed25519 identity to file"); + return -1; + } if (!authdir_mode(options)) return 0; @@ -1134,10 +1150,12 @@ init_keys(void) ds = router_get_trusteddirserver_by_digest(digest); if (!ds) { + tor_addr_port_t ipv6_orport; + routerconf_find_ipv6_or_ap(options, &ipv6_orport); ds = trusted_dir_server_new(options->Nickname, NULL, - router_get_advertised_dir_port(options, 0), - router_get_advertised_or_port(options), - NULL, + routerconf_find_dir_port(options, 0), + routerconf_find_or_port(options,AF_INET), + &ipv6_orport, digest, v3_digest, type, 0.0); @@ -1288,10 +1306,10 @@ decide_to_advertise_dir_impl(const or_options_t *options, return 1; if (net_is_disabled()) return 0; - if (dir_port && !router_get_advertised_dir_port(options, dir_port)) + if (dir_port && !routerconf_find_dir_port(options, dir_port)) return 0; if (supports_tunnelled_dir_requests && - !router_get_advertised_or_port(options)) + !routerconf_find_or_port(options, AF_INET)) return 0; /* Part two: consider config options that could make us choose to @@ -1335,6 +1353,17 @@ should_refuse_unknown_exits(const or_options_t *options) } } +/** + * If true, then we will publish our descriptor even if our own IPv4 ORPort + * seems to be unreachable. + **/ +static bool publish_even_when_ipv4_orport_unreachable = false; +/** + * If true, then we will publish our descriptor even if our own IPv6 ORPort + * seems to be unreachable. + **/ +static bool publish_even_when_ipv6_orport_unreachable = false; + /** Decide if we're a publishable server. We are a publishable server if: * - We don't have the ClientOnly option set * and @@ -1361,16 +1390,26 @@ decide_if_publishable_server(void) return 0; if (authdir_mode(options)) return 1; - if (!router_get_advertised_or_port(options)) - return 0; - if (!check_whether_orport_reachable(options)) + if (!routerconf_find_or_port(options, AF_INET)) return 0; + if (!router_orport_seems_reachable(options, AF_INET)) { + // We have an ipv4 orport, and it doesn't seem reachable. + if (!publish_even_when_ipv4_orport_unreachable) { + return 0; + } + } + if (!router_orport_seems_reachable(options, AF_INET6)) { + // We have an ipv6 orport, and it doesn't seem reachable. + if (!publish_even_when_ipv6_orport_unreachable) { + return 0; + } + } if (router_have_consensus_path() == CONSENSUS_PATH_INTERNAL) { /* All set: there are no exits in the consensus (maybe this is a tiny * test network), so we can't check our DirPort reachability. */ return 1; } else { - return check_whether_dirport_reachable(options); + return router_dirport_seems_reachable(options); } } @@ -1420,22 +1459,14 @@ router_get_active_listener_port_by_type_af(int listener_type, return 0; } -/** Return the port that we should advertise as our ORPort; this is either - * the one configured in the ORPort option, or the one we actually bound to - * if ORPort is "auto". Returns 0 if no port is found. */ -uint16_t -router_get_advertised_or_port(const or_options_t *options) -{ - return router_get_advertised_or_port_by_af(options, AF_INET); -} - -/** As router_get_advertised_or_port(), but allows an address family argument. - */ +/** Return the port that we should advertise as our ORPort in a given address + * family; this is either the one configured in the ORPort option, or the one + * we actually bound to if ORPort is "auto". Returns 0 if no port is found. */ uint16_t -router_get_advertised_or_port_by_af(const or_options_t *options, - sa_family_t family) +routerconf_find_or_port(const or_options_t *options, + sa_family_t family) { - int port = get_first_advertised_port_by_type_af(CONN_TYPE_OR_LISTENER, + int port = portconf_get_first_advertised_port(CONN_TYPE_OR_LISTENER, family); (void)options; @@ -1448,11 +1479,11 @@ router_get_advertised_or_port_by_af(const or_options_t *options, return port; } -/** As router_get_advertised_or_port(), but returns the IPv6 address and +/** As routerconf_find_or_port(), but returns the IPv6 address and * port in ipv6_ap_out, which must not be NULL. Returns a null address and * zero port, if no ORPort is found. */ void -router_get_advertised_ipv6_or_ap(const or_options_t *options, +routerconf_find_ipv6_or_ap(const or_options_t *options, tor_addr_port_t *ipv6_ap_out) { /* Bug in calling function, we can't return a sensible result, and it @@ -1463,11 +1494,10 @@ router_get_advertised_ipv6_or_ap(const or_options_t *options, tor_addr_make_null(&ipv6_ap_out->addr, AF_INET6); ipv6_ap_out->port = 0; - const tor_addr_t *addr = get_first_advertised_addr_by_type_af( + const tor_addr_t *addr = portconf_get_first_advertised_addr( CONN_TYPE_OR_LISTENER, AF_INET6); - const uint16_t port = router_get_advertised_or_port_by_af( - options, + const uint16_t port = routerconf_find_or_port(options, AF_INET6); if (!addr || port == 0) { @@ -1494,20 +1524,42 @@ router_get_advertised_ipv6_or_ap(const or_options_t *options, /** Returns true if this router has an advertised IPv6 ORPort. */ bool -router_has_advertised_ipv6_orport(const or_options_t *options) +routerconf_has_ipv6_orport(const or_options_t *options) { - tor_addr_port_t ipv6_ap; - router_get_advertised_ipv6_or_ap(options, &ipv6_ap); - return tor_addr_port_is_valid_ap(&ipv6_ap, 0); + /* What we want here is to learn if we have configured an IPv6 ORPort. + * Remember, ORPort can listen on [::] and thus consider internal by + * router_get_advertised_ipv6_or_ap() since we do _not_ want to advertise + * such address. */ + const tor_addr_t *addr = + portconf_get_first_advertised_addr(CONN_TYPE_OR_LISTENER, AF_INET6); + const uint16_t port = + routerconf_find_or_port(options, AF_INET6); + + return tor_addr_port_is_valid(addr, port, 1); } -/** Returns true if this router has an advertised IPv6 ORPort. */ +/** Returns true if this router can extend over IPv6. + * + * This check should only be performed by relay extend code. + * + * Clients should check if relays can initiate and accept IPv6 extends using + * node_supports_initiating_ipv6_extends() and + * node_supports_accepting_ipv6_extends(). + * + * As with other extends, relays should assume the client has already + * performed the relevant checks for the next hop. (Otherwise, relays that + * have just added IPv6 ORPorts won't be able to self-test those ORPorts.) + * + * Accepting relays don't need to perform any IPv6-specific checks before + * accepting a connection, because having an IPv6 ORPort implies support for + * the relevant protocol version. + */ MOCK_IMPL(bool, router_can_extend_over_ipv6,(const or_options_t *options)) { /* We might add some extra checks here, such as ExtendAllowIPv6Addresses * from ticket 33818. */ - return router_has_advertised_ipv6_orport(options); + return routerconf_has_ipv6_orport(options); } /** Return the port that we should advertise as our DirPort; @@ -1516,9 +1568,9 @@ router_can_extend_over_ipv6,(const or_options_t *options)) * the one configured in the DirPort option, * or the one we actually bound to if DirPort is "auto". */ uint16_t -router_get_advertised_dir_port(const or_options_t *options, uint16_t dirport) +routerconf_find_dir_port(const or_options_t *options, uint16_t dirport) { - int dirport_configured = get_primary_dir_port(); + int dirport_configured = portconf_get_primary_dir_port(); (void)options; if (!dirport_configured) @@ -1704,16 +1756,6 @@ router_get_my_routerinfo_with_err,(int *err)) return NULL; } - if (!desc_clean_since) { - int rebuild_err = router_rebuild_descriptor(0); - if (rebuild_err < 0) { - if (err) - *err = rebuild_err; - - return NULL; - } - } - if (!desc_routerinfo) { if (err) *err = TOR_ROUTERINFO_ERROR_DESC_REBUILDING; @@ -1769,54 +1811,55 @@ router_get_descriptor_gen_reason(void) * ORPort or DirPort. * listener_type is either CONN_TYPE_OR_LISTENER or CONN_TYPE_DIR_LISTENER. */ static void -router_check_descriptor_address_port_consistency(uint32_t ipv4h_desc_addr, +router_check_descriptor_address_port_consistency(const tor_addr_t *addr, int listener_type) { + int family, port_cfg; + + tor_assert(addr); tor_assert(listener_type == CONN_TYPE_OR_LISTENER || listener_type == CONN_TYPE_DIR_LISTENER); - /* The first advertised Port may be the magic constant CFG_AUTO_PORT. - */ - int port_v4_cfg = get_first_advertised_port_by_type_af(listener_type, - AF_INET); - if (port_v4_cfg != 0 && - !port_exists_by_type_addr32h_port(listener_type, - ipv4h_desc_addr, port_v4_cfg, 1)) { - const tor_addr_t *port_addr = get_first_advertised_addr_by_type_af( - listener_type, - AF_INET); - /* If we're building a descriptor with no advertised address, - * something is terribly wrong. */ - tor_assert(port_addr); - - tor_addr_t desc_addr; - char port_addr_str[TOR_ADDR_BUF_LEN]; - char desc_addr_str[TOR_ADDR_BUF_LEN]; - - tor_addr_to_str(port_addr_str, port_addr, TOR_ADDR_BUF_LEN, 0); - - tor_addr_from_ipv4h(&desc_addr, ipv4h_desc_addr); - tor_addr_to_str(desc_addr_str, &desc_addr, TOR_ADDR_BUF_LEN, 0); - - const char *listener_str = (listener_type == CONN_TYPE_OR_LISTENER ? - "OR" : "Dir"); - log_warn(LD_CONFIG, "The IPv4 %sPort address %s does not match the " - "descriptor address %s. If you have a static public IPv4 " - "address, use 'Address <IPv4>' and 'OutboundBindAddress " - "<IPv4>'. If you are behind a NAT, use two %sPort lines: " - "'%sPort <PublicPort> NoListen' and '%sPort <InternalPort> " - "NoAdvertise'.", - listener_str, port_addr_str, desc_addr_str, listener_str, - listener_str, listener_str); - } -} - -/* Tor relays only have one IPv4 address in the descriptor, which is derived - * from the Address torrc option, or guessed using various methods in - * router_pick_published_address(). - * Warn the operator if there is no ORPort on the descriptor address - * ipv4h_desc_addr. + family = tor_addr_family(addr); + /* The first advertised Port may be the magic constant CFG_AUTO_PORT. */ + port_cfg = portconf_get_first_advertised_port(listener_type, family); + if (port_cfg != 0 && + !port_exists_by_type_addr_port(listener_type, addr, port_cfg, 1)) { + const tor_addr_t *port_addr = + portconf_get_first_advertised_addr(listener_type, family); + /* If we're building a descriptor with no advertised address, + * something is terribly wrong. */ + tor_assert(port_addr); + + char port_addr_str[TOR_ADDR_BUF_LEN]; + char desc_addr_str[TOR_ADDR_BUF_LEN]; + + tor_addr_to_str(port_addr_str, port_addr, TOR_ADDR_BUF_LEN, 0); + tor_addr_to_str(desc_addr_str, addr, TOR_ADDR_BUF_LEN, 0); + + const char *listener_str = (listener_type == CONN_TYPE_OR_LISTENER ? + "OR" : "Dir"); + const char *af_str = fmt_af_family(family); + log_warn(LD_CONFIG, "The %s %sPort address %s does not match the " + "descriptor address %s. If you have a static public IPv4 " + "address, use 'Address <%s>' and 'OutboundBindAddress " + "<%s>'. If you are behind a NAT, use two %sPort lines: " + "'%sPort <PublicPort> NoListen' and '%sPort <InternalPort> " + "NoAdvertise'.", + af_str, listener_str, port_addr_str, desc_addr_str, af_str, + af_str, listener_str, listener_str, listener_str); + } +} + +/** Tor relays only have one IPv4 or/and one IPv6 address in the descriptor, + * which is derived from the Address torrc option, or guessed using various + * methods in relay_find_addr_to_publish(). + * + * Warn the operator if there is no ORPort associated with the given address + * in addr. + * * Warn the operator if there is no DirPort on the descriptor address. + * * This catches a few common config errors: * - operators who expect ORPorts and DirPorts to be advertised on the * ports' listen addresses, rather than the torrc Address (or guessed @@ -1825,20 +1868,22 @@ router_check_descriptor_address_port_consistency(uint32_t ipv4h_desc_addr, * addresses; * - discrepancies between guessed addresses and configured listen * addresses (when the Address option isn't set). + * * If a listener is listening on all IPv4 addresses, it is assumed that it * is listening on the configured Address, and no messages are logged. + * * If an operators has specified NoAdvertise ORPorts in a NAT setting, * no messages are logged, unless they have specified other advertised * addresses. + * * The message tells operators to configure an ORPort and DirPort that match - * the Address (using NoListen if needed). - */ + * the Address (using NoListen if needed). */ static void -router_check_descriptor_address_consistency(uint32_t ipv4h_desc_addr) +router_check_descriptor_address_consistency(const tor_addr_t *addr) { - router_check_descriptor_address_port_consistency(ipv4h_desc_addr, + router_check_descriptor_address_port_consistency(addr, CONN_TYPE_OR_LISTENER); - router_check_descriptor_address_port_consistency(ipv4h_desc_addr, + router_check_descriptor_address_port_consistency(addr, CONN_TYPE_DIR_LISTENER); } @@ -1980,33 +2025,56 @@ MOCK_IMPL(STATIC int, router_build_fresh_unsigned_routerinfo,(routerinfo_t **ri_out)) { routerinfo_t *ri = NULL; - uint32_t addr; + tor_addr_t ipv4_addr, ipv6_addr; char platform[256]; int hibernating = we_are_hibernating(); const or_options_t *options = get_options(); int result = TOR_ROUTERINFO_ERROR_INTERNAL_BUG; + uint16_t ipv6_orport = 0; if (BUG(!ri_out)) { result = TOR_ROUTERINFO_ERROR_INTERNAL_BUG; goto err; } - if (router_pick_published_address(options, &addr, 0) < 0) { + /* Find our resolved address both IPv4 and IPv6. In case the address is not + * found, the object is set to an UNSPEC address. */ + bool have_v4 = relay_find_addr_to_publish(options, AF_INET, + RELAY_FIND_ADDR_NO_FLAG, + &ipv4_addr); + bool have_v6 = relay_find_addr_to_publish(options, AF_INET6, + RELAY_FIND_ADDR_NO_FLAG, + &ipv6_addr); + + /* Tor requires a relay to have an IPv4 so bail if we can't find it. */ + if (!have_v4) { log_warn(LD_CONFIG, "Don't know my address while generating descriptor"); result = TOR_ROUTERINFO_ERROR_NO_EXT_ADDR; goto err; } - /* Log a message if the address in the descriptor doesn't match the ORPort * and DirPort addresses configured by the operator. */ - router_check_descriptor_address_consistency(addr); + router_check_descriptor_address_consistency(&ipv4_addr); + router_check_descriptor_address_consistency(&ipv6_addr); ri = tor_malloc_zero(sizeof(routerinfo_t)); ri->cache_info.routerlist_index = -1; ri->nickname = tor_strdup(options->Nickname); - ri->addr = addr; - ri->or_port = router_get_advertised_or_port(options); - ri->dir_port = router_get_advertised_dir_port(options, 0); + + /* IPv4. */ + tor_addr_copy(&ri->ipv4_addr, &ipv4_addr); + ri->ipv4_orport = routerconf_find_or_port(options, AF_INET); + ri->ipv4_dirport = routerconf_find_dir_port(options, 0); + + /* IPv6. Do not publish an IPv6 if we don't have an ORPort that can be used + * with the address. This is possible for instance if the ORPort is + * IPv4Only. */ + ipv6_orport = routerconf_find_or_port(options, AF_INET6); + if (have_v6 && ipv6_orport != 0) { + tor_addr_copy(&ri->ipv6_addr, &ipv6_addr); + ri->ipv6_orport = ipv6_orport; + } + ri->supports_tunnelled_dir_requests = directory_permits_begindir_requests(options); ri->cache_info.published_on = time(NULL); @@ -2018,13 +2086,6 @@ router_build_fresh_unsigned_routerinfo,(routerinfo_t **ri_out)) tor_memdup(&get_current_curve25519_keypair()->pubkey, sizeof(curve25519_public_key_t)); - /* For now, at most one IPv6 or-address is being advertised. */ - tor_addr_port_t ipv6_orport; - router_get_advertised_ipv6_or_ap(options, &ipv6_orport); - /* If there is no valid IPv6 ORPort, the address and port are null. */ - tor_addr_copy(&ri->ipv6_addr, &ipv6_orport.addr); - ri->ipv6_orport = ipv6_orport.port; - ri->identity_pkey = crypto_pk_dup_key(get_server_identity_key()); if (BUG(crypto_pk_get_digest(ri->identity_pkey, ri->cache_info.identity_digest) < 0)) { @@ -2046,13 +2107,14 @@ router_build_fresh_unsigned_routerinfo,(routerinfo_t **ri_out)) ri->bandwidthburst = relay_get_effective_bwburst(options); /* Report bandwidth, unless we're hibernating or shutting down */ - ri->bandwidthcapacity = hibernating ? 0 : rep_hist_bandwidth_assess(); + ri->bandwidthcapacity = hibernating ? 0 : bwhist_bandwidth_assess(); if (dns_seems_to_be_broken() || has_dns_init_failed()) { /* DNS is screwed up; don't claim to be an exit. */ policies_exit_policy_append_reject_star(&ri->exit_policy); } else { - policies_parse_exit_policy_from_options(options,ri->addr,&ri->ipv6_addr, + policies_parse_exit_policy_from_options(options, &ri->ipv4_addr, + &ri->ipv6_addr, &ri->exit_policy); } ri->policy_is_reject_star = @@ -2335,21 +2397,10 @@ router_rebuild_descriptor(int force) int err = 0; routerinfo_t *ri; extrainfo_t *ei; - uint32_t addr; - const or_options_t *options = get_options(); if (desc_clean_since && !force) return 0; - if (router_pick_published_address(options, &addr, 0) < 0 || - router_get_advertised_or_port(options) == 0) { - /* Stop trying to rebuild our descriptor every second. We'll - * learn that it's time to try again when ip_address_changed() - * marks it dirty. */ - desc_clean_since = time(NULL); - return TOR_ROUTERINFO_ERROR_DESC_REBUILDING; - } - log_info(LD_OR, "Rebuilding relay descriptor%s", force ? " (forced)" : ""); err = router_build_fresh_descriptor(&ri, &ei); @@ -2373,6 +2424,24 @@ router_rebuild_descriptor(int force) return 0; } +/** Called when we have a new set of consensus parameters. */ +void +router_new_consensus_params(const networkstatus_t *ns) +{ + const int32_t DEFAULT_ASSUME_REACHABLE = 0; + const int32_t DEFAULT_ASSUME_REACHABLE_IPV6 = 0; + int ar, ar6; + ar = networkstatus_get_param(ns, + "assume-reachable", + DEFAULT_ASSUME_REACHABLE, 0, 1); + ar6 = networkstatus_get_param(ns, + "assume-reachable-ipv6", + DEFAULT_ASSUME_REACHABLE_IPV6, 0, 1); + + publish_even_when_ipv4_orport_unreachable = ar; + publish_even_when_ipv6_orport_unreachable = ar || ar6; +} + /** If our router descriptor ever goes this long without being regenerated * because something changed, we force an immediate regenerate-and-upload. */ #define FORCE_REGENERATE_DESCRIPTOR_INTERVAL (18*60*60) @@ -2431,11 +2500,13 @@ mark_my_descriptor_dirty(const char *reason) if (BUG(reason == NULL)) { reason = "marked descriptor dirty for unspecified reason"; } - if (server_mode(options) && options->PublishServerDescriptor_) + if (server_mode(options) && options->PublishServerDescriptor_) { log_info(LD_OR, "Decided to publish new relay descriptor: %s", reason); + } desc_clean_since = 0; if (!desc_dirty_reason) desc_dirty_reason = reason; + reschedule_descriptor_update_check(); } /** How frequently will we republish our descriptor because of large (factor @@ -2474,7 +2545,7 @@ check_descriptor_bandwidth_changed(time_t now) /* Consider ourselves to have zero bandwidth if we're hibernating or * shutting down. */ - cur = hibernating ? 0 : rep_hist_bandwidth_assess(); + cur = hibernating ? 0 : bwhist_bandwidth_assess(); if ((prev != cur && (!prev || !cur)) || cur > (prev * BANDWIDTH_CHANGE_FACTOR) || @@ -2518,48 +2589,59 @@ log_addr_has_changed(int severity, addrbuf_cur, source); } -/** Check whether our own address as defined by the Address configuration - * has changed. This is for routers that get their address from a service - * like dyndns. If our address has changed, mark our descriptor dirty. */ +/** Check whether our own address has changed versus the one we have in our + * current descriptor. + * + * If our address has changed, call ip_address_changed() which takes + * appropriate actions. */ void check_descriptor_ipaddress_changed(time_t now) { - uint32_t prev, cur; - const or_options_t *options = get_options(); + const routerinfo_t *my_ri = router_get_my_routerinfo(); const char *method = NULL; char *hostname = NULL; - const routerinfo_t *my_ri = router_get_my_routerinfo(); + int families[2] = { AF_INET, AF_INET6 }; + bool has_changed = false; (void) now; - if (my_ri == NULL) /* make sure routerinfo exists */ - return; - - /* XXXX ipv6 */ - prev = my_ri->addr; - if (resolve_my_address(LOG_INFO, options, &cur, &method, &hostname) < 0) { - log_info(LD_CONFIG,"options->Address didn't resolve into an IP."); + /* We can't learn our descriptor address without one. */ + if (my_ri == NULL) { return; } - if (prev != cur) { - char *source; - tor_addr_t tmp_prev, tmp_cur; - - tor_addr_from_ipv4h(&tmp_prev, prev); - tor_addr_from_ipv4h(&tmp_cur, cur); + for (size_t i = 0; i < ARRAY_LENGTH(families); i++) { + tor_addr_t current; + const tor_addr_t *previous; + int family = families[i]; - tor_asprintf(&source, "METHOD=%s%s%s", method, - hostname ? " HOSTNAME=" : "", - hostname ? hostname : ""); + /* Get the descriptor address from the family we are looking up. */ + previous = &my_ri->ipv4_addr; + if (family == AF_INET6) { + previous = &my_ri->ipv6_addr; + } - log_addr_has_changed(LOG_NOTICE, &tmp_prev, &tmp_cur, source); - tor_free(source); + /* Ignore returned value because we want to notice not only an address + * change but also if an address is lost (current == UNSPEC). */ + find_my_address(get_options(), family, LOG_INFO, ¤t, &method, + &hostname); + + if (!tor_addr_eq(previous, ¤t)) { + char *source; + tor_asprintf(&source, "METHOD=%s%s%s", + method ? method : "UNKNOWN", + hostname ? " HOSTNAME=" : "", + hostname ? hostname : ""); + log_addr_has_changed(LOG_NOTICE, previous, ¤t, source); + tor_free(source); + has_changed = true; + } + tor_free(hostname); + } + if (has_changed) { ip_address_changed(0); } - - tor_free(hostname); } /** Set <b>platform</b> (max length <b>len</b>) to a NUL-terminated short @@ -2783,7 +2865,7 @@ router_dump_router_to_string(routerinfo_t *router, proto_line = tor_strdup(""); } - address = tor_dup_ip(router->addr); + address = tor_addr_to_str_dup(&router->ipv4_addr); if (!address) goto err; @@ -2807,8 +2889,8 @@ router_dump_router_to_string(routerinfo_t *router, "%s%s%s", router->nickname, address, - router->or_port, - router_should_advertise_dirport(options, router->dir_port), + router->ipv4_orport, + router_should_advertise_dirport(options, router->ipv4_dirport), ed_cert_line ? ed_cert_line : "", extra_or_address ? extra_or_address : "", router->platform, @@ -3133,7 +3215,7 @@ extrainfo_dump_to_string_stats_helper(smartlist_t *chunks, log_info(LD_GENERAL, "Adding stats to extra-info descriptor."); /* Bandwidth usage stats don't have their own option */ { - contents = rep_hist_get_bandwidth_lines(); + contents = bwhist_get_bandwidth_lines(); smartlist_add(chunks, contents); } /* geoip hashes aren't useful unless we are publishing other stats */ diff --git a/src/feature/relay/router.h b/src/feature/relay/router.h index 50790a73dd..c4e9af039f 100644 --- a/src/feature/relay/router.h +++ b/src/feature/relay/router.h @@ -65,14 +65,13 @@ int init_keys_client(void); uint16_t router_get_active_listener_port_by_type_af(int listener_type, sa_family_t family); -uint16_t router_get_advertised_or_port(const or_options_t *options); -void router_get_advertised_ipv6_or_ap(const or_options_t *options, +void routerconf_find_ipv6_or_ap(const or_options_t *options, tor_addr_port_t *ipv6_ap_out); -bool router_has_advertised_ipv6_orport(const or_options_t *options); +bool routerconf_has_ipv6_orport(const or_options_t *options); MOCK_DECL(bool, router_can_extend_over_ipv6,(const or_options_t *options)); -uint16_t router_get_advertised_or_port_by_af(const or_options_t *options, - sa_family_t family); -uint16_t router_get_advertised_dir_port(const or_options_t *options, +uint16_t routerconf_find_or_port(const or_options_t *options, + sa_family_t family); +uint16_t routerconf_find_dir_port(const or_options_t *options, uint16_t dirport); int router_should_advertise_dirport(const or_options_t *options, @@ -81,6 +80,7 @@ int router_should_advertise_dirport(const or_options_t *options, void consider_publishable_server(int force); int should_refuse_unknown_exits(const or_options_t *options); +void router_new_consensus_params(const networkstatus_t *); void router_upload_dir_desc_to_dirservers(int force); void mark_my_descriptor_dirty_if_too_old(time_t now); void mark_my_descriptor_dirty(const char *reason); @@ -124,7 +124,7 @@ void router_free_all(void); #ifdef ROUTER_PRIVATE /* Used only by router.c and the unit tests */ STATIC void get_platform_str(char *platform, size_t len); -STATIC int router_write_fingerprint(int hashed); +STATIC int router_write_fingerprint(int hashed, int ed25519_identity); STATIC smartlist_t *get_my_declared_family(const or_options_t *options); #ifdef TOR_UNIT_TESTS diff --git a/src/feature/relay/selftest.c b/src/feature/relay/selftest.c index 18fe25b989..d24748b297 100644 --- a/src/feature/relay/selftest.c +++ b/src/feature/relay/selftest.c @@ -15,38 +15,53 @@ #include "core/or/or.h" #include "app/config/config.h" + #include "core/mainloop/connection.h" #include "core/mainloop/mainloop.h" #include "core/mainloop/netstatus.h" + #include "core/or/circuitbuild.h" #include "core/or/circuitlist.h" #include "core/or/circuituse.h" #include "core/or/crypt_path_st.h" +#include "core/or/extendinfo.h" +#include "core/or/extend_info_st.h" #include "core/or/origin_circuit_st.h" #include "core/or/relay.h" + #include "feature/control/control_events.h" + #include "feature/dirclient/dirclient.h" #include "feature/dircommon/directory.h" + #include "feature/nodelist/authority_cert_st.h" #include "feature/nodelist/routerinfo.h" #include "feature/nodelist/routerinfo_st.h" #include "feature/nodelist/routerlist.h" // but... #include "feature/nodelist/routerset.h" #include "feature/nodelist/torcert.h" + #include "feature/relay/relay_periodic.h" #include "feature/relay/router.h" #include "feature/relay/selftest.h" -/** Whether we can reach our ORPort from the outside. */ -static int can_reach_or_port = 0; +static bool have_orport_for_family(int family); +static void inform_testing_reachability(const tor_addr_t *addr, + uint16_t port, + bool is_dirport); + +/** Whether we can reach our IPv4 ORPort from the outside. */ +static bool can_reach_or_port_ipv4 = false; +/** Whether we can reach our IPv6 ORPort from the outside. */ +static bool can_reach_or_port_ipv6 = false; /** Whether we can reach our DirPort from the outside. */ -static int can_reach_dir_port = 0; +static bool can_reach_dir_port = false; /** Forget what we have learned about our reachability status. */ void router_reset_reachability(void) { - can_reach_or_port = can_reach_dir_port = 0; + can_reach_or_port_ipv4 = can_reach_or_port_ipv6 = can_reach_dir_port = false; } /** Return 1 if we won't do reachability checks, because: @@ -68,13 +83,43 @@ router_reachability_checks_disabled(const or_options_t *options) * - we've seen a successful reachability check, or * - AssumeReachable is set, or * - the network is disabled. + + * If `family'`is AF_INET or AF_INET6, return true only when we should skip + * the given family's orport check (Because it's been checked, or because we + * aren't checking it.) If `family` is 0, return true if we can skip _all_ + * orport checks. */ int -check_whether_orport_reachable(const or_options_t *options) +router_orport_seems_reachable(const or_options_t *options, + int family) { + tor_assert_nonfatal(family == AF_INET || family == AF_INET6 || family == 0); int reach_checks_disabled = router_reachability_checks_disabled(options); - return reach_checks_disabled || - can_reach_or_port; + if (reach_checks_disabled) { + return true; + } + + // Note that we do a == 1 here, not just a boolean check. This value + // is also an autobool, so CFG_AUTO does not mean that we should + // assume IPv6 ports are reachable. + const bool ipv6_assume_reachable = (options->AssumeReachableIPv6 == 1); + + // Which reachability flags should we look at? + const bool checking_ipv4 = (family == AF_INET || family == 0); + const bool checking_ipv6 = (family == AF_INET6 || family == 0); + + if (checking_ipv4) { + if (have_orport_for_family(AF_INET) && !can_reach_or_port_ipv4) { + return false; + } + } + if (checking_ipv6 && !ipv6_assume_reachable) { + if (have_orport_for_family(AF_INET6) && !can_reach_or_port_ipv6) { + return false; + } + } + + return true; } /** Return 0 if we need to do a DirPort reachability check, because: @@ -87,7 +132,7 @@ check_whether_orport_reachable(const or_options_t *options) * - the network is disabled. */ int -check_whether_dirport_reachable(const or_options_t *options) +router_dirport_seems_reachable(const or_options_t *options) { int reach_checks_disabled = router_reachability_checks_disabled(options) || !options->DirPort_set; @@ -107,6 +152,7 @@ router_should_check_reachability(int test_or, int test_dir) if (!me) return 0; + /* Doesn't check our IPv6 address, see #34065. */ if (routerset_contains_router(options->ExcludeNodes, me, -1) && options->StrictNodes) { /* If we've excluded ourself, and StrictNodes is set, we can't test @@ -125,19 +171,51 @@ router_should_check_reachability(int test_or, int test_dir) return 1; } +/** + * Return true if we have configured an ORPort for the given family that + * we would like to advertise. + * + * Like other self-testing functions, this function looks at our most + * recently built descriptor. + **/ +static bool +have_orport_for_family(int family) +{ + const routerinfo_t *me = router_get_my_routerinfo(); + + if (!me) + return false; + + tor_addr_port_t ap; + if (router_get_orport(me, &ap, family) < 0) { + return false; + } + return true; +} + /** Allocate and return a new extend_info_t that can be used to build - * a circuit to or through the router <b>r</b>. Uses the primary - * address of the router, so should only be called on a server. */ + * a circuit to or through the router <b>r</b>, using an address from + * <b>family</b> (if available). + * + * Clients don't have routerinfos, so this function should only be called on a + * server. + * + * If the requested address is not available, returns NULL. */ static extend_info_t * -extend_info_from_router(const routerinfo_t *r) +extend_info_from_router(const routerinfo_t *r, int family) { crypto_pk_t *rsa_pubkey; extend_info_t *info; tor_addr_port_t ap; - tor_assert(r); - /* Make sure we don't need to check address reachability */ - tor_assert_nonfatal(router_skip_or_reachability(get_options(), 0)); + if (BUG(!r)) { + return NULL; + } + + /* Relays always assume that the first hop is reachable. They ignore + * ReachableAddresses. */ + tor_assert_nonfatal(router_or_conn_should_skip_reachable_address_check( + get_options(), 0)); const ed25519_public_key_t *ed_id_key; if (r->cache_info.signing_key_cert) @@ -145,7 +223,10 @@ extend_info_from_router(const routerinfo_t *r) else ed_id_key = NULL; - router_get_prim_orport(r, &ap); + if (router_get_orport(r, &ap, family) < 0) { + /* We don't have an ORPort for the requested family. */ + return NULL; + } rsa_pubkey = router_get_rsa_onion_pkey(r->onion_pkey, r->onion_pkey_len); info = extend_info_new(r->nickname, r->cache_info.identity_digest, ed_id_key, @@ -155,6 +236,78 @@ extend_info_from_router(const routerinfo_t *r) return info; } +/** Launch a self-testing circuit to one of our ORPorts, using an address from + * <b>family</b> (if available). The circuit can be used to test reachability + * or bandwidth. <b>me</b> is our own routerinfo. + * + * Logs an info-level status message. If <b>orport_reachable</b> is false, + * call it a reachability circuit. Otherwise, call it a bandwidth circuit. + * + * See router_do_reachability_checks() for details. */ +static void +router_do_orport_reachability_checks(const routerinfo_t *me, + int family, + int orport_reachable) +{ + extend_info_t *ei = extend_info_from_router(me, family); + int ipv6_flags = (family == AF_INET6 ? CIRCLAUNCH_IS_IPV6_SELFTEST : 0); + + /* If we're trying to test IPv6, but we don't have an IPv6 ORPort, ei will + * be NULL. */ + if (ei) { + const char *family_name = fmt_af_family(family); + const tor_addr_port_t *ap = extend_info_get_orport(ei, family); + log_info(LD_CIRC, "Testing %s of my %s ORPort: %s.", + !orport_reachable ? "reachability" : "bandwidth", + family_name, fmt_addrport_ap(ap)); + if (!orport_reachable) { + /* This is only a 'reachability test' if we don't already think that + * the port is reachable. If we _do_ think it's reachable, then + * it counts as a 'bandwidth test'. */ + inform_testing_reachability(&ap->addr, ap->port, false); + } + circuit_launch_by_extend_info(CIRCUIT_PURPOSE_TESTING, ei, + CIRCLAUNCH_NEED_CAPACITY| + CIRCLAUNCH_IS_INTERNAL| + ipv6_flags); + extend_info_free(ei); + } +} + +/** Launch a self-testing circuit, and ask an exit to connect to our DirPort. + * <b>me</b> is our own routerinfo. + * + * Relays don't advertise IPv6 DirPorts, so this function only supports IPv4. + * + * See router_do_reachability_checks() for details. */ +static void +router_do_dirport_reachability_checks(const routerinfo_t *me) +{ + tor_addr_port_t my_dirport; + tor_addr_copy(&my_dirport.addr, &me->ipv4_addr); + my_dirport.port = me->ipv4_dirport; + + /* If there is already a pending connection, don't open another one. */ + if (!connection_get_by_type_addr_port_purpose( + CONN_TYPE_DIR, + &my_dirport.addr, my_dirport.port, + DIR_PURPOSE_FETCH_SERVERDESC)) { + /* ask myself, via tor, for my server descriptor. */ + directory_request_t *req = + directory_request_new(DIR_PURPOSE_FETCH_SERVERDESC); + directory_request_set_dir_addr_port(req, &my_dirport); + directory_request_set_directory_id_digest(req, + me->cache_info.identity_digest); + /* ask via an anon circuit, connecting to our dirport. */ + directory_request_set_indirection(req, DIRIND_ANON_DIRPORT); + directory_request_set_resource(req, "authority.z"); + directory_initiate_request(req); + directory_request_free(req); + + inform_testing_reachability(&my_dirport.addr, my_dirport.port, true); + } +} + /** Some time has passed, or we just got new directory information. * See if we currently believe our ORPort or DirPort to be * unreachable. If so, launch a new test for it. @@ -171,104 +324,104 @@ router_do_reachability_checks(int test_or, int test_dir) { const routerinfo_t *me = router_get_my_routerinfo(); const or_options_t *options = get_options(); - int orport_reachable = check_whether_orport_reachable(options); - tor_addr_t addr; + int orport_reachable_v4 = + router_orport_seems_reachable(options, AF_INET); + int orport_reachable_v6 = + router_orport_seems_reachable(options, AF_INET6); if (router_should_check_reachability(test_or, test_dir)) { - if (test_or && (!orport_reachable || !circuit_enough_testing_circs())) { - extend_info_t *ei = extend_info_from_router(me); - /* XXX IPv6 self testing */ - log_info(LD_CIRC, "Testing %s of my ORPort: %s:%d.", - !orport_reachable ? "reachability" : "bandwidth", - fmt_addr32(me->addr), me->or_port); - circuit_launch_by_extend_info(CIRCUIT_PURPOSE_TESTING, ei, - CIRCLAUNCH_NEED_CAPACITY|CIRCLAUNCH_IS_INTERNAL); - extend_info_free(ei); + bool need_testing = !circuit_enough_testing_circs(); + /* At the moment, tor relays believe that they are reachable when they + * receive any create cell on an inbound connection, if the address + * family is correct. + */ + if (test_or && (!orport_reachable_v4 || need_testing)) { + router_do_orport_reachability_checks(me, AF_INET, orport_reachable_v4); + } + if (test_or && (!orport_reachable_v6 || need_testing)) { + router_do_orport_reachability_checks(me, AF_INET6, orport_reachable_v6); } - /* XXX IPv6 self testing */ - tor_addr_from_ipv4h(&addr, me->addr); - if (test_dir && !check_whether_dirport_reachable(options) && - !connection_get_by_type_addr_port_purpose( - CONN_TYPE_DIR, &addr, me->dir_port, - DIR_PURPOSE_FETCH_SERVERDESC)) { - tor_addr_port_t my_orport, my_dirport; - memcpy(&my_orport.addr, &addr, sizeof(addr)); - memcpy(&my_dirport.addr, &addr, sizeof(addr)); - my_orport.port = me->or_port; - my_dirport.port = me->dir_port; - /* ask myself, via tor, for my server descriptor. */ - directory_request_t *req = - directory_request_new(DIR_PURPOSE_FETCH_SERVERDESC); - directory_request_set_or_addr_port(req, &my_orport); - directory_request_set_dir_addr_port(req, &my_dirport); - directory_request_set_directory_id_digest(req, - me->cache_info.identity_digest); - // ask via an anon circuit, connecting to our dirport. - directory_request_set_indirection(req, DIRIND_ANON_DIRPORT); - directory_request_set_resource(req, "authority.z"); - directory_initiate_request(req); - directory_request_free(req); + if (test_dir && !router_dirport_seems_reachable(options)) { + router_do_dirport_reachability_checks(me); } } } -/** We've decided to start our reachability testing. If all - * is set, log this to the user. Return 1 if we did, or 0 if - * we chose not to log anything. */ -int -inform_testing_reachability(void) +/** Log a message informing the user that we are testing a port for + * reachability. + * + * If @a is_dirport is true, then the port is a DirPort; otherwise it is an + * ORPort. */ +static void +inform_testing_reachability(const tor_addr_t *addr, + uint16_t port, + bool is_dirport) { - char dirbuf[128]; - char *address; - const routerinfo_t *me = router_get_my_routerinfo(); - if (!me) - return 0; + if (!router_get_my_routerinfo()) + return; - address = tor_dup_ip(me->addr); - if (!address) - return 0; + char addr_buf[TOR_ADDRPORT_BUF_LEN]; + strlcpy(addr_buf, fmt_addrport(addr, port), sizeof(addr_buf)); + + const char *control_addr_type = is_dirport ? "DIRADDRESS" : "ORADDRESS"; + const char *port_type = is_dirport ? "DirPort" : "ORPort"; + const char *afname = fmt_af_family(tor_addr_family(addr)); control_event_server_status(LOG_NOTICE, - "CHECKING_REACHABILITY ORADDRESS=%s:%d", - address, me->or_port); - if (me->dir_port) { - tor_snprintf(dirbuf, sizeof(dirbuf), " and DirPort %s:%d", - address, me->dir_port); - control_event_server_status(LOG_NOTICE, - "CHECKING_REACHABILITY DIRADDRESS=%s:%d", - address, me->dir_port); - } - log_notice(LD_OR, "Now checking whether ORPort %s:%d%s %s reachable... " - "(this may take up to %d minutes -- look for log " - "messages indicating success)", - address, me->or_port, - me->dir_port ? dirbuf : "", - me->dir_port ? "are" : "is", - TIMEOUT_UNTIL_UNREACHABILITY_COMPLAINT/60); - - tor_free(address); - return 1; + "CHECKING_REACHABILITY %s=%s", + control_addr_type, addr_buf); + + log_notice(LD_OR, "Now checking whether %s %s %s is reachable... " + "(this may take up to %d minutes -- look for log " + "messages indicating success)", + afname, port_type, addr_buf, + TIMEOUT_UNTIL_UNREACHABILITY_COMPLAINT/60); +} + +/** + * Return true if this module knows of no reason why we shouldn't publish + * a server descriptor. + **/ +static bool +ready_to_publish(const or_options_t *options) +{ + return options->PublishServerDescriptor_ != NO_DIRINFO && + router_dirport_seems_reachable(options) && + router_all_orports_seem_reachable(options); } -/** Annotate that we found our ORPort reachable. */ +/** Annotate that we found our ORPort reachable with a given address + * family. */ void -router_orport_found_reachable(void) +router_orport_found_reachable(int family) { const routerinfo_t *me = router_get_my_routerinfo(); const or_options_t *options = get_options(); - if (!can_reach_or_port && me) { - char *address = tor_dup_ip(me->addr); - - if (!address) + bool *can_reach_ptr; + if (family == AF_INET) { + can_reach_ptr = &can_reach_or_port_ipv4; + } else if (family == AF_INET6) { + can_reach_ptr = &can_reach_or_port_ipv6; + } else { + tor_assert_nonfatal_unreached(); + return; + } + if (!*can_reach_ptr && me) { + tor_addr_port_t ap; + if (router_get_orport(me, &ap, family) < 0) { return; + } + char *address = tor_strdup(fmt_addrport_ap(&ap)); + + *can_reach_ptr = true; - log_notice(LD_OR,"Self-testing indicates your ORPort is reachable from " + log_notice(LD_OR,"Self-testing indicates your ORPort %s is reachable from " "the outside. Excellent.%s", - options->PublishServerDescriptor_ != NO_DIRINFO - && check_whether_dirport_reachable(options) ? - " Publishing server descriptor." : ""); - can_reach_or_port = 1; + address, + ready_to_publish(options) ? + " Publishing server descriptor." : ""); + mark_my_descriptor_dirty("ORPort found reachable"); /* This is a significant enough change to upload immediately, * at least in a test network */ @@ -276,8 +429,8 @@ router_orport_found_reachable(void) reschedule_descriptor_update_check(); } control_event_server_status(LOG_NOTICE, - "REACHABILITY_SUCCEEDED ORADDRESS=%s:%d", - address, me->or_port); + "REACHABILITY_SUCCEEDED ORADDRESS=%s", + address); tor_free(address); } } @@ -288,19 +441,20 @@ router_dirport_found_reachable(void) { const routerinfo_t *me = router_get_my_routerinfo(); const or_options_t *options = get_options(); + if (!can_reach_dir_port && me) { - char *address = tor_dup_ip(me->addr); + char *address = tor_addr_to_str_dup(&me->ipv4_addr); if (!address) return; + can_reach_dir_port = true; log_notice(LD_DIRSERV,"Self-testing indicates your DirPort is reachable " "from the outside. Excellent.%s", - options->PublishServerDescriptor_ != NO_DIRINFO - && check_whether_orport_reachable(options) ? + ready_to_publish(options) ? " Publishing server descriptor." : ""); - can_reach_dir_port = 1; - if (router_should_advertise_dirport(options, me->dir_port)) { + + if (router_should_advertise_dirport(options, me->ipv4_dirport)) { mark_my_descriptor_dirty("DirPort found reachable"); /* This is a significant enough change to upload immediately, * at least in a test network */ @@ -310,13 +464,15 @@ router_dirport_found_reachable(void) } control_event_server_status(LOG_NOTICE, "REACHABILITY_SUCCEEDED DIRADDRESS=%s:%d", - address, me->dir_port); + address, me->ipv4_dirport); tor_free(address); } } /** We have enough testing circuits open. Send a bunch of "drop" - * cells down each of them, to exercise our bandwidth. */ + * cells down each of them, to exercise our bandwidth. + * + * May use IPv4 and IPv6 testing circuits (if available). */ void router_perform_bandwidth_test(int num_circs, time_t now) { diff --git a/src/feature/relay/selftest.h b/src/feature/relay/selftest.h index f5babc95da..e09c0e7898 100644 --- a/src/feature/relay/selftest.h +++ b/src/feature/relay/selftest.h @@ -15,23 +15,29 @@ #ifdef HAVE_MODULE_RELAY struct or_options_t; -int check_whether_orport_reachable(const struct or_options_t *options); -int check_whether_dirport_reachable(const struct or_options_t *options); +#define router_all_orports_seem_reachable(opts) \ + router_orport_seems_reachable((opts),0) +int router_orport_seems_reachable( + const struct or_options_t *options, + int family); +int router_dirport_seems_reachable( + const struct or_options_t *options); void router_do_reachability_checks(int test_or, int test_dir); void router_perform_bandwidth_test(int num_circs, time_t now); -int inform_testing_reachability(void); -void router_orport_found_reachable(void); +void router_orport_found_reachable(int family); void router_dirport_found_reachable(void); void router_reset_reachability(void); #else /* !defined(HAVE_MODULE_RELAY) */ -#define check_whether_orport_reachable(opts) \ +#define router_all_orports_seem_reachable(opts) \ ((void)(opts), 0) -#define check_whether_dirport_reachable(opts) \ +#define router_orport_seems_reachable(opts, fam) \ + ((void)(opts), (void)(fam), 0) +#define router_dirport_seems_reachable(opts) \ ((void)(opts), 0) static inline void diff --git a/src/feature/rend/rendclient.c b/src/feature/rend/rendclient.c index 09db7045fa..e171562d17 100644 --- a/src/feature/rend/rendclient.c +++ b/src/feature/rend/rendclient.c @@ -15,6 +15,7 @@ #include "core/or/circuitlist.h" #include "core/or/circuituse.h" #include "core/or/connection_edge.h" +#include "core/or/extendinfo.h" #include "core/or/relay.h" #include "feature/client/circpathbias.h" #include "feature/control/control_events.h" @@ -234,9 +235,15 @@ rend_client_send_introduction(origin_circuit_t *introcirc, /* version 2 format */ extend_info_t *extend_info = rendcirc->build_state->chosen_exit; int klen; + const tor_addr_port_t *orport = + extend_info_get_orport(extend_info, AF_INET); + IF_BUG_ONCE(! orport) { + /* we should never put an IPv6 address here. */ + goto perm_err; + } /* nul pads */ - set_uint32(tmp+v3_shift+1, tor_addr_to_ipv4n(&extend_info->addr)); - set_uint16(tmp+v3_shift+5, htons(extend_info->port)); + set_uint32(tmp+v3_shift+1, tor_addr_to_ipv4n(&orport->addr)); + set_uint16(tmp+v3_shift+5, htons(orport->port)); memcpy(tmp+v3_shift+7, extend_info->identity_digest, DIGEST_LEN); klen = crypto_pk_asn1_encode(extend_info->onion_key, tmp+v3_shift+7+DIGEST_LEN+2, diff --git a/src/feature/rend/rendcommon.c b/src/feature/rend/rendcommon.c index 5d04755819..775d487805 100644 --- a/src/feature/rend/rendcommon.c +++ b/src/feature/rend/rendcommon.c @@ -14,6 +14,7 @@ #include "core/or/circuitbuild.h" #include "core/or/circuitlist.h" #include "core/or/circuituse.h" +#include "core/or/extendinfo.h" #include "app/config/config.h" #include "feature/control/control_events.h" #include "lib/crypt_ops/crypto_rand.h" @@ -233,7 +234,12 @@ rend_encode_v2_intro_points(char **encoded, rend_service_descriptor_t *desc) goto done; } /* Assemble everything for this introduction point. */ - address = tor_addr_to_str_dup(&info->addr); + const tor_addr_port_t *orport = extend_info_get_orport(info, AF_INET); + IF_BUG_ONCE(!orport) { + /* There must be an IPv4 address for v2 hs. */ + goto done; + } + address = tor_addr_to_str_dup(&orport->addr); res = tor_snprintf(unenc + unenc_written, unenc_len - unenc_written, "introduction-point %s\n" "ip-address %s\n" @@ -242,7 +248,7 @@ rend_encode_v2_intro_points(char **encoded, rend_service_descriptor_t *desc) "service-key\n%s", id_base32, address, - info->port, + orport->port, onion_key, service_key); tor_free(address); diff --git a/src/feature/rend/rendparse.c b/src/feature/rend/rendparse.c index 0979d767a7..c28add5ca9 100644 --- a/src/feature/rend/rendparse.c +++ b/src/feature/rend/rendparse.c @@ -10,6 +10,7 @@ **/ #include "core/or/or.h" +#include "core/or/extendinfo.h" #include "feature/dirparse/parsecommon.h" #include "feature/dirparse/sigcommon.h" #include "feature/rend/rendcommon.h" @@ -428,7 +429,8 @@ rend_parse_introduction_points(rend_service_descriptor_t *parsed, } /* Allocate new intro point and extend info. */ intro = tor_malloc_zero(sizeof(rend_intro_point_t)); - info = intro->extend_info = tor_malloc_zero(sizeof(extend_info_t)); + info = intro->extend_info = + extend_info_new(NULL, NULL, NULL, NULL, NULL, NULL, 0); /* Parse identifier. */ tok = find_by_keyword(tokens, R_IPO_IDENTIFIER); if (base32_decode(info->identity_digest, DIGEST_LEN, @@ -446,12 +448,13 @@ rend_parse_introduction_points(rend_service_descriptor_t *parsed, info->identity_digest, DIGEST_LEN); /* Parse IP address. */ tok = find_by_keyword(tokens, R_IPO_IP_ADDRESS); - if (tor_addr_parse(&info->addr, tok->args[0])<0) { + tor_addr_t addr; + if (tor_addr_parse(&addr, tok->args[0])<0) { log_warn(LD_REND, "Could not parse introduction point address."); rend_intro_point_free(intro); goto err; } - if (tor_addr_family(&info->addr) != AF_INET) { + if (tor_addr_family(&addr) != AF_INET) { log_warn(LD_REND, "Introduction point address was not ipv4."); rend_intro_point_free(intro); goto err; @@ -459,14 +462,18 @@ rend_parse_introduction_points(rend_service_descriptor_t *parsed, /* Parse onion port. */ tok = find_by_keyword(tokens, R_IPO_ONION_PORT); - info->port = (uint16_t) tor_parse_long(tok->args[0],10,1,65535, + uint16_t port = (uint16_t) tor_parse_long(tok->args[0],10,1,65535, &num_ok,NULL); - if (!info->port || !num_ok) { + if (!port || !num_ok) { log_warn(LD_REND, "Introduction point onion port %s is invalid", escaped(tok->args[0])); rend_intro_point_free(intro); goto err; } + + /* Add the address and port. */ + extend_info_add_orport(info, &addr, port); + /* Parse onion key. */ tok = find_by_keyword(tokens, R_IPO_ONION_KEY); if (!crypto_pk_public_exponent_ok(tok->key)) { diff --git a/src/feature/rend/rendservice.c b/src/feature/rend/rendservice.c index a88c2080fd..1ac88d0eb7 100644 --- a/src/feature/rend/rendservice.c +++ b/src/feature/rend/rendservice.c @@ -16,6 +16,7 @@ #include "core/or/circuitbuild.h" #include "core/or/circuitlist.h" #include "core/or/circuituse.h" +#include "core/or/extendinfo.h" #include "core/or/policies.h" #include "core/or/relay.h" #include "core/or/crypt_path.h" @@ -132,6 +133,22 @@ static smartlist_t *rend_service_list = NULL; * service on config reload. */ static smartlist_t *rend_service_staging_list = NULL; +/** Helper: log the deprecation warning for version 2 only once. */ +static void +log_once_deprecation_warning(void) +{ + static bool logged_once = false; + if (!logged_once) { + log_warn(LD_REND, "DEPRECATED: Onion service version 2 are deprecated. " + "Please use version 3 which is the default now. " + "Currently, version 2 is planned to be obsolete in " + "the Tor version 0.4.6 stable series."); + logged_once = true; + } +} +/** Macro to make it very explicit that we are warning about deprecation. */ +#define WARN_ONCE_DEPRECATION() log_once_deprecation_warning() + /* Like rend_get_service_list_mutable, but returns a read-only list. */ static const smartlist_t* rend_get_service_list(const smartlist_t* substitute_service_list) @@ -731,6 +748,9 @@ rend_config_service(const hs_opts_t *hs_opts, tor_assert(hs_opts); tor_assert(config); + /* We are about to configure a version 2 service. Warn of deprecation. */ + WARN_ONCE_DEPRECATION(); + /* Use the staging service list so that we can check then do the pruning * process using the main list at the end. */ if (rend_service_staging_list == NULL) { @@ -1831,8 +1851,11 @@ rend_service_use_direct_connection(const or_options_t* options, /* We'll connect directly all reachable addresses, whether preferred or not. * The prefer_ipv6 argument to fascist_firewall_allows_address_addr is * ignored, because pref_only is 0. */ + const tor_addr_port_t *ap = extend_info_get_orport(ei, AF_INET); + if (!ap) + return 0; return (rend_service_allow_non_anonymous_connection(options) && - fascist_firewall_allows_address_addr(&ei->addr, ei->port, + fascist_firewall_allows_address_addr(&ap->addr, ap->port, FIREWALL_OR_CONNECTION, 0, 0)); } @@ -2261,7 +2284,8 @@ find_rp_for_intro(const rend_intro_cell_t *intro, /* Make sure the RP we are being asked to connect to is _not_ a private * address unless it's allowed. Let's avoid to build a circuit to our * second middle node and fail right after when extending to the RP. */ - if (!extend_info_addr_is_allowed(&rp->addr)) { + const tor_addr_port_t *orport = extend_info_get_orport(rp, AF_INET); + if (! orport || !extend_info_addr_is_allowed(&orport->addr)) { if (err_msg_out) { tor_asprintf(&err_msg, "Relay IP in INTRODUCE2 cell is private address."); @@ -2530,9 +2554,11 @@ rend_service_parse_intro_for_v2( goto err; } - extend_info = tor_malloc_zero(sizeof(extend_info_t)); - tor_addr_from_ipv4n(&extend_info->addr, get_uint32(buf + 1)); - extend_info->port = ntohs(get_uint16(buf + 5)); + extend_info = extend_info_new(NULL, NULL, NULL, NULL, NULL, NULL, 0); + tor_addr_t addr; + tor_addr_from_ipv4n(&addr, get_uint32(buf + 1)); + uint16_t port = ntohs(get_uint16(buf + 5)); + extend_info_add_orport(extend_info, &addr, port); memcpy(extend_info->identity_digest, buf + 7, DIGEST_LEN); extend_info->nickname[0] = '$'; base16_encode(extend_info->nickname + 1, sizeof(extend_info->nickname) - 1, @@ -3714,7 +3740,7 @@ directory_post_to_hs_dir(rend_service_descriptor_t *renddesc, rend_data_free(rend_data); base32_encode(desc_id_base32, sizeof(desc_id_base32), desc->desc_id, DIGEST_LEN); - hs_dir_ip = tor_dup_ip(hs_dir->addr); + hs_dir_ip = tor_addr_to_str_dup(&hs_dir->ipv4_addr); if (hs_dir_ip) { log_info(LD_REND, "Launching upload for v2 descriptor for " "service '%s' with descriptor ID '%s' with validity " @@ -3725,7 +3751,7 @@ directory_post_to_hs_dir(rend_service_descriptor_t *renddesc, seconds_valid, hs_dir->nickname, hs_dir_ip, - hs_dir->or_port); + hs_dir->ipv4_orport); tor_free(hs_dir_ip); } @@ -3820,6 +3846,9 @@ upload_service_descriptor(rend_service_t *service) rend_get_service_id(service->desc->pk, serviceid); if (get_options()->PublishHidServDescriptors) { /* Post the current descriptors to the hidden service directories. */ + /* This log message is used by Chutney as part of its bootstrap + * detection mechanism. Please don't change without first checking + * Chutney. */ log_info(LD_REND, "Launching upload for hidden service %s", serviceid); directory_post_to_hs_dir(service->desc, descs, NULL, serviceid, diff --git a/src/feature/stats/bwhist.c b/src/feature/stats/bwhist.c new file mode 100644 index 0000000000..e74a2881f5 --- /dev/null +++ b/src/feature/stats/bwhist.c @@ -0,0 +1,592 @@ +/* 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 bwhist.c + * @brief Tracking for relay bandwidth history + * + * This module handles bandwidth usage history, used by relays to + * self-report how much bandwidth they've used for different + * purposes over last day or so, in order to generate the + * {dirreq-,}{read,write}-history lines in that they publish. + **/ + +#define BWHIST_PRIVATE +#include "orconfig.h" +#include "core/or/or.h" +#include "feature/stats/bwhist.h" + +#include "app/config/config.h" +#include "app/config/statefile.h" +#include "feature/relay/routermode.h" + +#include "app/config/or_state_st.h" +#include "app/config/or_options_st.h" + +/** For how many seconds do we keep track of individual per-second bandwidth + * totals? */ +#define NUM_SECS_ROLLING_MEASURE 10 +/** How large are the intervals for which we track and report bandwidth use? */ +#define NUM_SECS_BW_SUM_INTERVAL (24*60*60) +/** How far in the past do we remember and publish bandwidth use? */ +#define NUM_SECS_BW_SUM_IS_VALID (5*24*60*60) +/** How many bandwidth usage intervals do we remember? (derived) */ +#define NUM_TOTALS (NUM_SECS_BW_SUM_IS_VALID/NUM_SECS_BW_SUM_INTERVAL) + +/** Structure to track bandwidth use, and remember the maxima for a given + * time period. + */ +struct bw_array_t { + /** Observation array: Total number of bytes transferred in each of the last + * NUM_SECS_ROLLING_MEASURE seconds. This is used as a circular array. */ + uint64_t obs[NUM_SECS_ROLLING_MEASURE]; + int cur_obs_idx; /**< Current position in obs. */ + time_t cur_obs_time; /**< Time represented in obs[cur_obs_idx] */ + uint64_t total_obs; /**< Total for all members of obs except + * obs[cur_obs_idx] */ + uint64_t max_total; /**< Largest value that total_obs has taken on in the + * current period. */ + uint64_t total_in_period; /**< Total bytes transferred in the current + * period. */ + + /** When does the next period begin? */ + time_t next_period; + /** Where in 'maxima' should the maximum bandwidth usage for the current + * period be stored? */ + int next_max_idx; + /** How many values in maxima/totals have been set ever? */ + int num_maxes_set; + /** Circular array of the maximum + * bandwidth-per-NUM_SECS_ROLLING_MEASURE usage for the last + * NUM_TOTALS periods */ + uint64_t maxima[NUM_TOTALS]; + /** Circular array of the total bandwidth usage for the last NUM_TOTALS + * periods */ + uint64_t totals[NUM_TOTALS]; +}; + +/** Shift the current period of b forward by one. */ +STATIC void +commit_max(bw_array_t *b) +{ + /* Store total from current period. */ + b->totals[b->next_max_idx] = b->total_in_period; + /* Store maximum from current period. */ + b->maxima[b->next_max_idx++] = b->max_total; + /* Advance next_period and next_max_idx */ + b->next_period += NUM_SECS_BW_SUM_INTERVAL; + if (b->next_max_idx == NUM_TOTALS) + b->next_max_idx = 0; + if (b->num_maxes_set < NUM_TOTALS) + ++b->num_maxes_set; + /* Reset max_total. */ + b->max_total = 0; + /* Reset total_in_period. */ + b->total_in_period = 0; +} + +/** Shift the current observation time of <b>b</b> forward by one second. */ +STATIC void +advance_obs(bw_array_t *b) +{ + int nextidx; + uint64_t total; + + /* Calculate the total bandwidth for the last NUM_SECS_ROLLING_MEASURE + * seconds; adjust max_total as needed.*/ + total = b->total_obs + b->obs[b->cur_obs_idx]; + if (total > b->max_total) + b->max_total = total; + + nextidx = b->cur_obs_idx+1; + if (nextidx == NUM_SECS_ROLLING_MEASURE) + nextidx = 0; + + b->total_obs = total - b->obs[nextidx]; + b->obs[nextidx]=0; + b->cur_obs_idx = nextidx; + + if (++b->cur_obs_time >= b->next_period) + commit_max(b); +} + +/** Add <b>n</b> bytes to the number of bytes in <b>b</b> for second + * <b>when</b>. */ +static inline void +add_obs(bw_array_t *b, time_t when, uint64_t n) +{ + if (when < b->cur_obs_time) + return; /* Don't record data in the past. */ + + /* If we're currently adding observations for an earlier second than + * 'when', advance b->cur_obs_time and b->cur_obs_idx by an + * appropriate number of seconds, and do all the other housekeeping. */ + while (when > b->cur_obs_time) { + /* Doing this one second at a time is potentially inefficient, if we start + with a state file that is very old. Fortunately, it doesn't seem to + show up in profiles, so we can just ignore it for now. */ + advance_obs(b); + } + + b->obs[b->cur_obs_idx] += n; + b->total_in_period += n; +} + +/** Allocate, initialize, and return a new bw_array. */ +static bw_array_t * +bw_array_new(void) +{ + bw_array_t *b; + time_t start; + b = tor_malloc_zero(sizeof(bw_array_t)); + start = time(NULL); + b->cur_obs_time = start; + b->next_period = start + NUM_SECS_BW_SUM_INTERVAL; + return b; +} + +#define bw_array_free(val) \ + FREE_AND_NULL(bw_array_t, bw_array_free_, (val)) + +/** Free storage held by bandwidth array <b>b</b>. */ +static void +bw_array_free_(bw_array_t *b) +{ + if (!b) { + return; + } + + tor_free(b); +} + +/** Recent history of bandwidth observations for (all) read operations. */ +static bw_array_t *read_array = NULL; +/** Recent history of bandwidth observations for IPv6 read operations. */ +static bw_array_t *read_array_ipv6 = NULL; +/** Recent history of bandwidth observations for (all) write operations. */ +STATIC bw_array_t *write_array = NULL; +/** Recent history of bandwidth observations for IPv6 write operations. */ +static bw_array_t *write_array_ipv6 = NULL; +/** Recent history of bandwidth observations for read operations for the + directory protocol. */ +static bw_array_t *dir_read_array = NULL; +/** Recent history of bandwidth observations for write operations for the + directory protocol. */ +static bw_array_t *dir_write_array = NULL; + +/** Set up structures for bandwidth history, clearing them if they already + * exist. */ +void +bwhist_init(void) +{ + bw_array_free(read_array); + bw_array_free(read_array_ipv6); + bw_array_free(write_array); + bw_array_free(write_array_ipv6); + bw_array_free(dir_read_array); + bw_array_free(dir_write_array); + + read_array = bw_array_new(); + read_array_ipv6 = bw_array_new(); + write_array = bw_array_new(); + write_array_ipv6 = bw_array_new(); + dir_read_array = bw_array_new(); + dir_write_array = bw_array_new(); +} + +/** Remember that we read <b>num_bytes</b> bytes in second <b>when</b>. + * + * Add num_bytes to the current running total for <b>when</b>. + * + * <b>when</b> can go back to time, but it's safe to ignore calls + * earlier than the latest <b>when</b> you've heard of. + */ +void +bwhist_note_bytes_written(uint64_t num_bytes, time_t when, bool ipv6) +{ +/* Maybe a circular array for recent seconds, and step to a new point + * every time a new second shows up. Or simpler is to just to have + * a normal array and push down each item every second; it's short. + */ +/* When a new second has rolled over, compute the sum of the bytes we've + * seen over when-1 to when-1-NUM_SECS_ROLLING_MEASURE, and stick it + * somewhere. See bwhist_bandwidth_assess() below. + */ + add_obs(write_array, when, num_bytes); + if (ipv6) + add_obs(write_array_ipv6, when, num_bytes); +} + +/** Remember that we wrote <b>num_bytes</b> bytes in second <b>when</b>. + * (like bwhist_note_bytes_written() above) + */ +void +bwhist_note_bytes_read(uint64_t num_bytes, time_t when, bool ipv6) +{ +/* if we're smart, we can make this func and the one above share code */ + add_obs(read_array, when, num_bytes); + if (ipv6) + add_obs(read_array_ipv6, when, num_bytes); +} + +/** Remember that we wrote <b>num_bytes</b> directory bytes in second + * <b>when</b>. (like bwhist_note_bytes_written() above) + */ +void +bwhist_note_dir_bytes_written(uint64_t num_bytes, time_t when) +{ + add_obs(dir_write_array, when, num_bytes); +} + +/** Remember that we read <b>num_bytes</b> directory bytes in second + * <b>when</b>. (like bwhist_note_bytes_written() above) + */ +void +bwhist_note_dir_bytes_read(uint64_t num_bytes, time_t when) +{ + add_obs(dir_read_array, when, num_bytes); +} + +/** Helper: Return the largest value in b->maxima. (This is equal to the + * most bandwidth used in any NUM_SECS_ROLLING_MEASURE period for the last + * NUM_SECS_BW_SUM_IS_VALID seconds.) + */ +STATIC uint64_t +find_largest_max(bw_array_t *b) +{ + int i; + uint64_t max; + max=0; + for (i=0; i<NUM_TOTALS; ++i) { + if (b->maxima[i]>max) + max = b->maxima[i]; + } + return max; +} + +/** Find the largest sums in the past NUM_SECS_BW_SUM_IS_VALID (roughly) + * seconds. Find one sum for reading and one for writing. They don't have + * to be at the same time. + * + * Return the smaller of these sums, divided by NUM_SECS_ROLLING_MEASURE. + */ +MOCK_IMPL(int, +bwhist_bandwidth_assess,(void)) +{ + uint64_t w,r; + r = find_largest_max(read_array); + w = find_largest_max(write_array); + if (r>w) + return (int)(((double)w)/NUM_SECS_ROLLING_MEASURE); + else + return (int)(((double)r)/NUM_SECS_ROLLING_MEASURE); +} + +/** Print the bandwidth history of b (either [dir-]read_array or + * [dir-]write_array) into the buffer pointed to by buf. The format is + * simply comma separated numbers, from oldest to newest. + * + * It returns the number of bytes written. + */ +static size_t +bwhist_fill_bandwidth_history(char *buf, size_t len, const bw_array_t *b) +{ + char *cp = buf; + int i, n; + const or_options_t *options = get_options(); + uint64_t cutoff; + + if (b->num_maxes_set <= b->next_max_idx) { + /* We haven't been through the circular array yet; time starts at i=0.*/ + i = 0; + } else { + /* We've been around the array at least once. The next i to be + overwritten is the oldest. */ + i = b->next_max_idx; + } + + if (options->RelayBandwidthRate) { + /* We don't want to report that we used more bandwidth than the max we're + * willing to relay; otherwise everybody will know how much traffic + * we used ourself. */ + cutoff = options->RelayBandwidthRate * NUM_SECS_BW_SUM_INTERVAL; + } else { + cutoff = UINT64_MAX; + } + + for (n=0; n<b->num_maxes_set; ++n,++i) { + uint64_t total; + if (i >= NUM_TOTALS) + i -= NUM_TOTALS; + tor_assert(i < NUM_TOTALS); + /* Round the bandwidth used down to the nearest 1k. */ + total = b->totals[i] & ~0x3ff; + if (total > cutoff) + total = cutoff; + + if (n==(b->num_maxes_set-1)) + tor_snprintf(cp, len-(cp-buf), "%"PRIu64, (total)); + else + tor_snprintf(cp, len-(cp-buf), "%"PRIu64",", (total)); + cp += strlen(cp); + } + return cp-buf; +} + +/** Encode a single bandwidth history line into <b>buf</b>. */ +static void +bwhist_get_one_bandwidth_line(buf_t *buf, const char *desc, + const bw_array_t *b) +{ + /* [dirreq-](read|write)-history yyyy-mm-dd HH:MM:SS (n s) n,n,n... */ + /* The n,n,n part above. Largest representation of a uint64_t is 20 chars + * long, plus the comma. */ +#define MAX_HIST_VALUE_LEN (21*NUM_TOTALS) + + char tmp[MAX_HIST_VALUE_LEN]; + char end[ISO_TIME_LEN+1]; + + size_t slen = bwhist_fill_bandwidth_history(tmp, MAX_HIST_VALUE_LEN, b); + /* If we don't have anything to write, skip to the next entry. */ + if (slen == 0) + return; + + format_iso_time(end, b->next_period-NUM_SECS_BW_SUM_INTERVAL); + buf_add_printf(buf, "%s %s (%d s) %s\n", + desc, end, NUM_SECS_BW_SUM_INTERVAL, tmp); +} + +/** Allocate and return lines for representing this server's bandwidth + * history in its descriptor. We publish these lines in our extra-info + * descriptor. + */ +char * +bwhist_get_bandwidth_lines(void) +{ + buf_t *buf = buf_new(); + + bwhist_get_one_bandwidth_line(buf, "write-history", write_array); + bwhist_get_one_bandwidth_line(buf, "read-history", read_array); + bwhist_get_one_bandwidth_line(buf, "ipv6-write-history", write_array_ipv6); + bwhist_get_one_bandwidth_line(buf, "ipv6-read-history", read_array_ipv6); + bwhist_get_one_bandwidth_line(buf, "dirreq-write-history", dir_write_array); + bwhist_get_one_bandwidth_line(buf, "dirreq-read-history", dir_read_array); + + char *result = buf_extract(buf, NULL); + buf_free(buf); + return result; +} + +/** Write a single bw_array_t into the Values, Ends, Interval, and Maximum + * entries of an or_state_t. Done before writing out a new state file. */ +static void +bwhist_update_bwhist_state_section(or_state_t *state, + const bw_array_t *b, + smartlist_t **s_values, + smartlist_t **s_maxima, + time_t *s_begins, + int *s_interval) +{ + int i,j; + uint64_t maxval; + + if (*s_values) { + SMARTLIST_FOREACH(*s_values, char *, val, tor_free(val)); + smartlist_free(*s_values); + } + if (*s_maxima) { + SMARTLIST_FOREACH(*s_maxima, char *, val, tor_free(val)); + smartlist_free(*s_maxima); + } + if (! server_mode(get_options())) { + /* Clients don't need to store bandwidth history persistently; + * force these values to the defaults. */ + /* FFFF we should pull the default out of config.c's state table, + * so we don't have two defaults. */ + if (*s_begins != 0 || *s_interval != 900) { + time_t now = time(NULL); + time_t save_at = get_options()->AvoidDiskWrites ? now+3600 : now+600; + or_state_mark_dirty(state, save_at); + } + *s_begins = 0; + *s_interval = 900; + *s_values = smartlist_new(); + *s_maxima = smartlist_new(); + return; + } + *s_begins = b->next_period; + *s_interval = NUM_SECS_BW_SUM_INTERVAL; + + *s_values = smartlist_new(); + *s_maxima = smartlist_new(); + /* Set i to first position in circular array */ + i = (b->num_maxes_set <= b->next_max_idx) ? 0 : b->next_max_idx; + for (j=0; j < b->num_maxes_set; ++j,++i) { + if (i >= NUM_TOTALS) + i = 0; + smartlist_add_asprintf(*s_values, "%"PRIu64, + (b->totals[i] & ~0x3ff)); + maxval = b->maxima[i] / NUM_SECS_ROLLING_MEASURE; + smartlist_add_asprintf(*s_maxima, "%"PRIu64, + (maxval & ~0x3ff)); + } + smartlist_add_asprintf(*s_values, "%"PRIu64, + (b->total_in_period & ~0x3ff)); + maxval = b->max_total / NUM_SECS_ROLLING_MEASURE; + smartlist_add_asprintf(*s_maxima, "%"PRIu64, + (maxval & ~0x3ff)); +} + +/** Update <b>state</b> with the newest bandwidth history. Done before + * writing out a new state file. */ +void +bwhist_update_state(or_state_t *state) +{ +#define UPDATE(arrname,st) \ + bwhist_update_bwhist_state_section(state,\ + (arrname),\ + &state->BWHistory ## st ## Values, \ + &state->BWHistory ## st ## Maxima, \ + &state->BWHistory ## st ## Ends, \ + &state->BWHistory ## st ## Interval) + + UPDATE(write_array, Write); + UPDATE(read_array, Read); + UPDATE(write_array_ipv6, IPv6Write); + UPDATE(read_array_ipv6, IPv6Read); + UPDATE(dir_write_array, DirWrite); + UPDATE(dir_read_array, DirRead); + + if (server_mode(get_options())) { + or_state_mark_dirty(state, time(NULL)+(2*3600)); + } +#undef UPDATE +} + +/** Load a single bw_array_t from its Values, Ends, Maxima, and Interval + * entries in an or_state_t. Done while reading the state file. */ +static int +bwhist_load_bwhist_state_section(bw_array_t *b, + const smartlist_t *s_values, + const smartlist_t *s_maxima, + const time_t s_begins, + const int s_interval) +{ + time_t now = time(NULL); + int retval = 0; + time_t start; + + uint64_t v, mv; + int i,ok,ok_m = 0; + int have_maxima = s_maxima && s_values && + (smartlist_len(s_values) == smartlist_len(s_maxima)); + + if (s_values && s_begins >= now - NUM_SECS_BW_SUM_INTERVAL*NUM_TOTALS) { + start = s_begins - s_interval*(smartlist_len(s_values)); + if (start > now) + return 0; + b->cur_obs_time = start; + b->next_period = start + NUM_SECS_BW_SUM_INTERVAL; + SMARTLIST_FOREACH_BEGIN(s_values, const char *, cp) { + const char *maxstr = NULL; + v = tor_parse_uint64(cp, 10, 0, UINT64_MAX, &ok, NULL); + if (have_maxima) { + maxstr = smartlist_get(s_maxima, cp_sl_idx); + mv = tor_parse_uint64(maxstr, 10, 0, UINT64_MAX, &ok_m, NULL); + mv *= NUM_SECS_ROLLING_MEASURE; + } else { + /* No maxima known; guess average rate to be conservative. */ + mv = (v / s_interval) * NUM_SECS_ROLLING_MEASURE; + } + if (!ok) { + retval = -1; + log_notice(LD_HIST, "Could not parse value '%s' into a number.'",cp); + } + if (maxstr && !ok_m) { + retval = -1; + log_notice(LD_HIST, "Could not parse maximum '%s' into a number.'", + maxstr); + } + + if (start < now) { + time_t cur_start = start; + time_t actual_interval_len = s_interval; + uint64_t cur_val = 0; + /* Calculate the average per second. This is the best we can do + * because our state file doesn't have per-second resolution. */ + if (start + s_interval > now) + actual_interval_len = now - start; + cur_val = v / actual_interval_len; + /* This is potentially inefficient, but since we don't do it very + * often it should be ok. */ + while (cur_start < start + actual_interval_len) { + add_obs(b, cur_start, cur_val); + ++cur_start; + } + b->max_total = mv; + /* This will result in some fairly choppy history if s_interval + * is not the same as NUM_SECS_BW_SUM_INTERVAL. XXXX */ + start += actual_interval_len; + } + } SMARTLIST_FOREACH_END(cp); + } + + /* Clean up maxima and observed */ + for (i=0; i<NUM_SECS_ROLLING_MEASURE; ++i) { + b->obs[i] = 0; + } + b->total_obs = 0; + + return retval; +} + +/** Set bandwidth history from the state file we just loaded. */ +int +bwhist_load_state(or_state_t *state, char **err) +{ + int all_ok = 1; + + /* Assert they already have been malloced */ + tor_assert(read_array && write_array); + tor_assert(read_array_ipv6 && write_array_ipv6); + tor_assert(dir_read_array && dir_write_array); + +#define LOAD(arrname,st) \ + if (bwhist_load_bwhist_state_section( \ + (arrname), \ + state->BWHistory ## st ## Values, \ + state->BWHistory ## st ## Maxima, \ + state->BWHistory ## st ## Ends, \ + state->BWHistory ## st ## Interval)<0) \ + all_ok = 0 + + LOAD(write_array, Write); + LOAD(read_array, Read); + LOAD(write_array_ipv6, IPv6Write); + LOAD(read_array_ipv6, IPv6Read); + LOAD(dir_write_array, DirWrite); + LOAD(dir_read_array, DirRead); + +#undef LOAD + if (!all_ok) { + *err = tor_strdup("Parsing of bandwidth history values failed"); + /* and create fresh arrays */ + bwhist_init(); + return -1; + } + return 0; +} + +void +bwhist_free_all(void) +{ + bw_array_free(read_array); + bw_array_free(read_array_ipv6); + bw_array_free(write_array); + bw_array_free(write_array_ipv6); + bw_array_free(dir_read_array); + bw_array_free(dir_write_array); +} diff --git a/src/feature/stats/bwhist.h b/src/feature/stats/bwhist.h new file mode 100644 index 0000000000..d556f5a026 --- /dev/null +++ b/src/feature/stats/bwhist.h @@ -0,0 +1,40 @@ +/* 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 bwhist.h + * @brief Header for feature/stats/bwhist.c + **/ + +#ifndef TOR_FEATURE_STATS_BWHIST_H +#define TOR_FEATURE_STATS_BWHIST_H + +void bwhist_init(void); +void bwhist_free_all(void); + +void bwhist_note_bytes_read(uint64_t num_bytes, time_t when, bool ipv6); +void bwhist_note_bytes_written(uint64_t num_bytes, time_t when, bool ipv6); +void bwhist_note_dir_bytes_read(uint64_t num_bytes, time_t when); +void bwhist_note_dir_bytes_written(uint64_t num_bytes, time_t when); + +MOCK_DECL(int, bwhist_bandwidth_assess, (void)); +char *bwhist_get_bandwidth_lines(void); +struct or_state_t; +void bwhist_update_state(struct or_state_t *state); +int bwhist_load_state(struct or_state_t *state, char **err); + +#ifdef BWHIST_PRIVATE +typedef struct bw_array_t bw_array_t; +STATIC uint64_t find_largest_max(bw_array_t *b); +STATIC void commit_max(bw_array_t *b); +STATIC void advance_obs(bw_array_t *b); +#endif /* defined(REPHIST_PRIVATE) */ + +#ifdef TOR_UNIT_TESTS +extern struct bw_array_t *write_array; +#endif + +#endif /* !defined(TOR_FEATURE_STATS_BWHIST_H) */ diff --git a/src/feature/stats/connstats.c b/src/feature/stats/connstats.c new file mode 100644 index 0000000000..827a332be1 --- /dev/null +++ b/src/feature/stats/connstats.c @@ -0,0 +1,283 @@ +/* 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 connstats.c + * @brief Count bidirectional vs one-way connections. + * + * Connection statistics, use to track one-way and bidirectional connections. + * + * Note that this code counts concurrent connections in each + * BIDI_INTERVAL-second interval, not total connections. It can tell you what + * fraction of connections are bidirectional at each time, not necessarily + * what number are bidirectional. + **/ + +#include "orconfig.h" +#include "core/or/or.h" +#include "feature/stats/connstats.h" +#include "app/config/config.h" + +/** Start of the current connection stats interval or 0 if we're not + * collecting connection statistics. */ +static time_t start_of_conn_stats_interval; + +/** Initialize connection stats. */ +void +conn_stats_init(time_t now) +{ + start_of_conn_stats_interval = now; +} + +/** Count connections on which we read and wrote less than this many bytes + * as "below threshold." */ +#define BIDI_THRESHOLD 20480 + +/** Count connections that we read or wrote at least this factor as many + * bytes from/to than we wrote or read to/from as mostly reading or + * writing. */ +#define BIDI_FACTOR 10 + +/** Interval length in seconds for considering read and written bytes for + * connection stats. */ +#define BIDI_INTERVAL 10 + +/** Start of next BIDI_INTERVAL second interval. */ +static time_t bidi_next_interval = 0; + +/** A single grouped set of connection type counts. */ +typedef struct conn_counts_t { + /** Number of connections that we read and wrote less than BIDI_THRESHOLD + * bytes from/to in BIDI_INTERVAL seconds. */ + uint32_t below_threshold; + + /** Number of connections that we read at least BIDI_FACTOR times more + * bytes from than we wrote to in BIDI_INTERVAL seconds. */ + uint32_t mostly_read; + + /** Number of connections that we wrote at least BIDI_FACTOR times more + * bytes to than we read from in BIDI_INTERVAL seconds. */ + uint32_t mostly_written; + + /** Number of connections that we read and wrote at least BIDI_THRESHOLD + * bytes from/to, but not BIDI_FACTOR times more in either direction in + * BIDI_INTERVAL seconds. */ + uint32_t both_read_and_written; +} conn_counts_t ; + +/** A collection of connection counts, over all OR connections. */ +static conn_counts_t counts; +/** A collection of connection counts, over IPv6 OR connections only. */ +static conn_counts_t counts_ipv6; + +/** Entry in a map from connection ID to the number of read and written + * bytes on this connection in a BIDI_INTERVAL second interval. */ +typedef struct bidi_map_entry_t { + HT_ENTRY(bidi_map_entry_t) node; + uint64_t conn_id; /**< Connection ID */ + size_t read; /**< Number of read bytes */ + size_t written; /**< Number of written bytes */ + bool is_ipv6; /**< True if this is an IPv6 connection */ +} bidi_map_entry_t; + +/** Map of OR connections together with the number of read and written + * bytes in the current BIDI_INTERVAL second interval. */ +static HT_HEAD(bidimap, bidi_map_entry_t) bidi_map = + HT_INITIALIZER(); + +/** Hashtable helper: return true if @a a and @a b have the same key. */ +static int +bidi_map_ent_eq(const bidi_map_entry_t *a, const bidi_map_entry_t *b) +{ + return a->conn_id == b->conn_id; +} + +/** Hashtable helper: compute a digest for the key of @a entry. */ +static unsigned +bidi_map_ent_hash(const bidi_map_entry_t *entry) +{ + return (unsigned) entry->conn_id; +} + +HT_PROTOTYPE(bidimap, bidi_map_entry_t, node, bidi_map_ent_hash, + bidi_map_ent_eq); +HT_GENERATE2(bidimap, bidi_map_entry_t, node, bidi_map_ent_hash, + bidi_map_ent_eq, 0.6, tor_reallocarray_, tor_free_); + +/** Release all storage held in connstats.c */ +void +conn_stats_free_all(void) +{ + bidi_map_entry_t **ptr, **next, *ent; + for (ptr = HT_START(bidimap, &bidi_map); ptr; ptr = next) { + ent = *ptr; + next = HT_NEXT_RMV(bidimap, &bidi_map, ptr); + tor_free(ent); + } + HT_CLEAR(bidimap, &bidi_map); +} + +/** Reset counters for conn statistics. */ +void +conn_stats_reset(time_t now) +{ + start_of_conn_stats_interval = now; + memset(&counts, 0, sizeof(counts)); + memset(&counts_ipv6, 0, sizeof(counts_ipv6)); + conn_stats_free_all(); +} + +/** Stop collecting connection stats in a way that we can re-start doing + * so in conn_stats_init(). */ +void +conn_stats_terminate(void) +{ + conn_stats_reset(0); +} + +/** + * Record a single entry @a ent in the counts structure @a cnt. + */ +static void +add_entry_to_count(conn_counts_t *cnt, const bidi_map_entry_t *ent) +{ + if (ent->read + ent->written < BIDI_THRESHOLD) + cnt->below_threshold++; + else if (ent->read >= ent->written * BIDI_FACTOR) + cnt->mostly_read++; + else if (ent->written >= ent->read * BIDI_FACTOR) + cnt->mostly_written++; + else + cnt->both_read_and_written++; +} + +/** + * Count all the connection information we've received during the current + * period in 'bidimap', and store that information in the appropriate count + * structures. + **/ +static void +collect_period_statistics(void) +{ + bidi_map_entry_t **ptr, **next, *ent; + for (ptr = HT_START(bidimap, &bidi_map); ptr; ptr = next) { + ent = *ptr; + add_entry_to_count(&counts, ent); + if (ent->is_ipv6) + add_entry_to_count(&counts_ipv6, ent); + next = HT_NEXT_RMV(bidimap, &bidi_map, ptr); + tor_free(ent); + } + log_info(LD_GENERAL, "%d below threshold, %d mostly read, " + "%d mostly written, %d both read and written.", + counts.below_threshold, counts.mostly_read, counts.mostly_written, + counts.both_read_and_written); +} + +/** We read <b>num_read</b> bytes and wrote <b>num_written</b> from/to OR + * connection <b>conn_id</b> in second <b>when</b>. If this is the first + * observation in a new interval, sum up the last observations. Add bytes + * for this connection. */ +void +conn_stats_note_or_conn_bytes(uint64_t conn_id, size_t num_read, + size_t num_written, time_t when, + bool is_ipv6) +{ + if (!start_of_conn_stats_interval) + return; + /* Initialize */ + if (bidi_next_interval == 0) + bidi_next_interval = when + BIDI_INTERVAL; + /* Sum up last period's statistics */ + if (when >= bidi_next_interval) { + collect_period_statistics(); + while (when >= bidi_next_interval) + bidi_next_interval += BIDI_INTERVAL; + } + /* Add this connection's bytes. */ + if (num_read > 0 || num_written > 0) { + bidi_map_entry_t *entry, lookup; + lookup.conn_id = conn_id; + entry = HT_FIND(bidimap, &bidi_map, &lookup); + if (entry) { + entry->written += num_written; + entry->read += num_read; + entry->is_ipv6 |= is_ipv6; + } else { + entry = tor_malloc_zero(sizeof(bidi_map_entry_t)); + entry->conn_id = conn_id; + entry->written = num_written; + entry->read = num_read; + entry->is_ipv6 = is_ipv6; + HT_INSERT(bidimap, &bidi_map, entry); + } + } +} + +/** Return a newly allocated string containing the connection statistics + * until <b>now</b>, or NULL if we're not collecting conn stats. Caller must + * ensure start_of_conn_stats_interval is in the past. */ +char * +conn_stats_format(time_t now) +{ + char *result, written_at[ISO_TIME_LEN+1]; + + if (!start_of_conn_stats_interval) + return NULL; /* Not initialized. */ + + tor_assert(now >= start_of_conn_stats_interval); + + format_iso_time(written_at, now); + tor_asprintf(&result, + "conn-bi-direct %s (%d s) " + "%"PRIu32",%"PRIu32",%"PRIu32",%"PRIu32"\n" + "ipv6-conn-bi-direct %s (%d s) " + "%"PRIu32",%"PRIu32",%"PRIu32",%"PRIu32"\n", + written_at, + (unsigned) (now - start_of_conn_stats_interval), + counts.below_threshold, + counts.mostly_read, + counts.mostly_written, + counts.both_read_and_written, + written_at, + (unsigned) (now - start_of_conn_stats_interval), + counts_ipv6.below_threshold, + counts_ipv6.mostly_read, + counts_ipv6.mostly_written, + counts_ipv6.both_read_and_written); + + return result; +} + +/** If 24 hours have passed since the beginning of the current conn stats + * period, write conn stats to $DATADIR/stats/conn-stats (possibly + * overwriting an existing file) and reset counters. Return when we would + * next want to write conn stats or 0 if we never want to write. */ +time_t +conn_stats_save(time_t now) +{ + char *str = NULL; + + if (!start_of_conn_stats_interval) + return 0; /* Not initialized. */ + if (start_of_conn_stats_interval + WRITE_STATS_INTERVAL > now) + goto done; /* Not ready to write */ + + /* Generate history string. */ + str = conn_stats_format(now); + + /* Reset counters. */ + conn_stats_reset(now); + + /* Try to write to disk. */ + if (!check_or_create_data_subdir("stats")) { + write_to_data_subdir("stats", "conn-stats", str, "connection statistics"); + } + + done: + tor_free(str); + return start_of_conn_stats_interval + WRITE_STATS_INTERVAL; +} diff --git a/src/feature/stats/connstats.h b/src/feature/stats/connstats.h new file mode 100644 index 0000000000..1a03d0748b --- /dev/null +++ b/src/feature/stats/connstats.h @@ -0,0 +1,25 @@ +/* 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 connstats.h + * @brief Header for feature/stats/connstats.c + **/ + +#ifndef TOR_FEATURE_STATS_CONNSTATS_H +#define TOR_FEATURE_STATS_CONNSTATS_H + +void conn_stats_init(time_t now); +void conn_stats_note_or_conn_bytes(uint64_t conn_id, size_t num_read, + size_t num_written, time_t when, + bool is_ipv6); +void conn_stats_reset(time_t now); +char *conn_stats_format(time_t now); +time_t conn_stats_save(time_t now); +void conn_stats_terminate(void); +void conn_stats_free_all(void); + +#endif /* !defined(TOR_FEATURE_STATS_CONNSTATS_H) */ diff --git a/src/feature/stats/include.am b/src/feature/stats/include.am index 8789bc3d96..bc13882f4b 100644 --- a/src/feature/stats/include.am +++ b/src/feature/stats/include.am @@ -1,12 +1,16 @@ # ADD_C_FILE: INSERT SOURCES HERE. LIBTOR_APP_A_SOURCES += \ + src/feature/stats/bwhist.c \ + src/feature/stats/connstats.c \ src/feature/stats/geoip_stats.c \ src/feature/stats/rephist.c \ src/feature/stats/predict_ports.c # ADD_C_FILE: INSERT HEADERS HERE. noinst_HEADERS += \ + src/feature/stats/bwhist.h \ + src/feature/stats/connstats.h \ src/feature/stats/geoip_stats.h \ src/feature/stats/rephist.h \ src/feature/stats/predict_ports.h diff --git a/src/feature/stats/predict_ports.c b/src/feature/stats/predict_ports.c index d728f106a2..57463952e7 100644 --- a/src/feature/stats/predict_ports.c +++ b/src/feature/stats/predict_ports.c @@ -270,10 +270,10 @@ rep_hist_circbuilding_dormant(time_t now) /* see if we'll still need to build testing circuits */ if (server_mode(options) && - (!check_whether_orport_reachable(options) || + (!router_all_orports_seem_reachable(options) || !circuit_enough_testing_circs())) return 0; - if (!check_whether_dirport_reachable(options)) + if (!router_dirport_seems_reachable(options)) return 0; return 1; diff --git a/src/feature/stats/rephist.c b/src/feature/stats/rephist.c index 71e2e00086..b6730e1226 100644 --- a/src/feature/stats/rephist.c +++ b/src/feature/stats/rephist.c @@ -18,11 +18,6 @@ * stability information about various relays, including "uptime", * "weighted fractional uptime" and "mean time between failures". * - * <li>Bandwidth usage history, used by relays to self-report how much - * bandwidth they've used for different purposes over last day or so, - * in order to generate the {dirreq-,}{read,write}-history lines in - * that they publish. - * * <li>Predicted ports, used by clients to remember how long it's been * since they opened an exit connection to each given target * port. Clients use this information in order to try to keep circuits @@ -48,9 +43,6 @@ * <li>Descriptor serving statistics, used by directory caches to track * how many descriptors they've served. * - * <li>Connection statistics, used by relays to track one-way and - * bidirectional connections. - * * <li>Onion handshake statistics, used by relays to count how many * TAP and ntor handshakes they've handled. * @@ -77,14 +69,13 @@ #define REPHIST_PRIVATE #include "core/or/or.h" #include "app/config/config.h" -#include "app/config/statefile.h" #include "core/or/circuitlist.h" #include "core/or/connection_or.h" #include "feature/dirauth/authmode.h" #include "feature/nodelist/networkstatus.h" #include "feature/nodelist/nodelist.h" -#include "feature/relay/routermode.h" #include "feature/stats/predict_ports.h" +#include "feature/stats/connstats.h" #include "feature/stats/rephist.h" #include "lib/container/order.h" #include "lib/crypt_ops/crypto_rand.h" @@ -92,14 +83,11 @@ #include "feature/nodelist/networkstatus_st.h" #include "core/or/or_circuit_st.h" -#include "app/config/or_state_st.h" #ifdef HAVE_FCNTL_H #include <fcntl.h> #endif -static void bw_arrays_init(void); - /** Total number of bytes currently allocated in fields used by rephist.c. */ uint64_t rephist_total_alloc=0; /** Number of or_history_t objects currently allocated. */ @@ -232,7 +220,6 @@ void rep_hist_init(void) { history_map = digestmap_new(); - bw_arrays_init(); } /** We have just decided that this router with identity digest <b>id</b> is @@ -973,560 +960,6 @@ rep_hist_load_mtbf_data(time_t now) return r; } -/** For how many seconds do we keep track of individual per-second bandwidth - * totals? */ -#define NUM_SECS_ROLLING_MEASURE 10 -/** How large are the intervals for which we track and report bandwidth use? */ -#define NUM_SECS_BW_SUM_INTERVAL (24*60*60) -/** How far in the past do we remember and publish bandwidth use? */ -#define NUM_SECS_BW_SUM_IS_VALID (5*24*60*60) -/** How many bandwidth usage intervals do we remember? (derived) */ -#define NUM_TOTALS (NUM_SECS_BW_SUM_IS_VALID/NUM_SECS_BW_SUM_INTERVAL) - -/** Structure to track bandwidth use, and remember the maxima for a given - * time period. - */ -struct bw_array_t { - /** Observation array: Total number of bytes transferred in each of the last - * NUM_SECS_ROLLING_MEASURE seconds. This is used as a circular array. */ - uint64_t obs[NUM_SECS_ROLLING_MEASURE]; - int cur_obs_idx; /**< Current position in obs. */ - time_t cur_obs_time; /**< Time represented in obs[cur_obs_idx] */ - uint64_t total_obs; /**< Total for all members of obs except - * obs[cur_obs_idx] */ - uint64_t max_total; /**< Largest value that total_obs has taken on in the - * current period. */ - uint64_t total_in_period; /**< Total bytes transferred in the current - * period. */ - - /** When does the next period begin? */ - time_t next_period; - /** Where in 'maxima' should the maximum bandwidth usage for the current - * period be stored? */ - int next_max_idx; - /** How many values in maxima/totals have been set ever? */ - int num_maxes_set; - /** Circular array of the maximum - * bandwidth-per-NUM_SECS_ROLLING_MEASURE usage for the last - * NUM_TOTALS periods */ - uint64_t maxima[NUM_TOTALS]; - /** Circular array of the total bandwidth usage for the last NUM_TOTALS - * periods */ - uint64_t totals[NUM_TOTALS]; -}; - -/** Shift the current period of b forward by one. */ -STATIC void -commit_max(bw_array_t *b) -{ - /* Store total from current period. */ - b->totals[b->next_max_idx] = b->total_in_period; - /* Store maximum from current period. */ - b->maxima[b->next_max_idx++] = b->max_total; - /* Advance next_period and next_max_idx */ - b->next_period += NUM_SECS_BW_SUM_INTERVAL; - if (b->next_max_idx == NUM_TOTALS) - b->next_max_idx = 0; - if (b->num_maxes_set < NUM_TOTALS) - ++b->num_maxes_set; - /* Reset max_total. */ - b->max_total = 0; - /* Reset total_in_period. */ - b->total_in_period = 0; -} - -/** Shift the current observation time of <b>b</b> forward by one second. */ -STATIC void -advance_obs(bw_array_t *b) -{ - int nextidx; - uint64_t total; - - /* Calculate the total bandwidth for the last NUM_SECS_ROLLING_MEASURE - * seconds; adjust max_total as needed.*/ - total = b->total_obs + b->obs[b->cur_obs_idx]; - if (total > b->max_total) - b->max_total = total; - - nextidx = b->cur_obs_idx+1; - if (nextidx == NUM_SECS_ROLLING_MEASURE) - nextidx = 0; - - b->total_obs = total - b->obs[nextidx]; - b->obs[nextidx]=0; - b->cur_obs_idx = nextidx; - - if (++b->cur_obs_time >= b->next_period) - commit_max(b); -} - -/** Add <b>n</b> bytes to the number of bytes in <b>b</b> for second - * <b>when</b>. */ -static inline void -add_obs(bw_array_t *b, time_t when, uint64_t n) -{ - if (when < b->cur_obs_time) - return; /* Don't record data in the past. */ - - /* If we're currently adding observations for an earlier second than - * 'when', advance b->cur_obs_time and b->cur_obs_idx by an - * appropriate number of seconds, and do all the other housekeeping. */ - while (when > b->cur_obs_time) { - /* Doing this one second at a time is potentially inefficient, if we start - with a state file that is very old. Fortunately, it doesn't seem to - show up in profiles, so we can just ignore it for now. */ - advance_obs(b); - } - - b->obs[b->cur_obs_idx] += n; - b->total_in_period += n; -} - -/** Allocate, initialize, and return a new bw_array. */ -static bw_array_t * -bw_array_new(void) -{ - bw_array_t *b; - time_t start; - b = tor_malloc_zero(sizeof(bw_array_t)); - rephist_total_alloc += sizeof(bw_array_t); - start = time(NULL); - b->cur_obs_time = start; - b->next_period = start + NUM_SECS_BW_SUM_INTERVAL; - return b; -} - -#define bw_array_free(val) \ - FREE_AND_NULL(bw_array_t, bw_array_free_, (val)) - -/** Free storage held by bandwidth array <b>b</b>. */ -static void -bw_array_free_(bw_array_t *b) -{ - if (!b) { - return; - } - - rephist_total_alloc -= sizeof(bw_array_t); - tor_free(b); -} - -/** Recent history of bandwidth observations for read operations. */ -static bw_array_t *read_array = NULL; -/** Recent history of bandwidth observations for write operations. */ -STATIC bw_array_t *write_array = NULL; -/** Recent history of bandwidth observations for read operations for the - directory protocol. */ -static bw_array_t *dir_read_array = NULL; -/** Recent history of bandwidth observations for write operations for the - directory protocol. */ -static bw_array_t *dir_write_array = NULL; - -/** Set up [dir_]read_array and [dir_]write_array, freeing them if they - * already exist. */ -static void -bw_arrays_init(void) -{ - bw_array_free(read_array); - bw_array_free(write_array); - bw_array_free(dir_read_array); - bw_array_free(dir_write_array); - - read_array = bw_array_new(); - write_array = bw_array_new(); - dir_read_array = bw_array_new(); - dir_write_array = bw_array_new(); -} - -/** Remember that we read <b>num_bytes</b> bytes in second <b>when</b>. - * - * Add num_bytes to the current running total for <b>when</b>. - * - * <b>when</b> can go back to time, but it's safe to ignore calls - * earlier than the latest <b>when</b> you've heard of. - */ -void -rep_hist_note_bytes_written(uint64_t num_bytes, time_t when) -{ -/* Maybe a circular array for recent seconds, and step to a new point - * every time a new second shows up. Or simpler is to just to have - * a normal array and push down each item every second; it's short. - */ -/* When a new second has rolled over, compute the sum of the bytes we've - * seen over when-1 to when-1-NUM_SECS_ROLLING_MEASURE, and stick it - * somewhere. See rep_hist_bandwidth_assess() below. - */ - add_obs(write_array, when, num_bytes); -} - -/** Remember that we wrote <b>num_bytes</b> bytes in second <b>when</b>. - * (like rep_hist_note_bytes_written() above) - */ -void -rep_hist_note_bytes_read(uint64_t num_bytes, time_t when) -{ -/* if we're smart, we can make this func and the one above share code */ - add_obs(read_array, when, num_bytes); -} - -/** Remember that we wrote <b>num_bytes</b> directory bytes in second - * <b>when</b>. (like rep_hist_note_bytes_written() above) - */ -void -rep_hist_note_dir_bytes_written(uint64_t num_bytes, time_t when) -{ - add_obs(dir_write_array, when, num_bytes); -} - -/** Remember that we read <b>num_bytes</b> directory bytes in second - * <b>when</b>. (like rep_hist_note_bytes_written() above) - */ -void -rep_hist_note_dir_bytes_read(uint64_t num_bytes, time_t when) -{ - add_obs(dir_read_array, when, num_bytes); -} - -/** Helper: Return the largest value in b->maxima. (This is equal to the - * most bandwidth used in any NUM_SECS_ROLLING_MEASURE period for the last - * NUM_SECS_BW_SUM_IS_VALID seconds.) - */ -STATIC uint64_t -find_largest_max(bw_array_t *b) -{ - int i; - uint64_t max; - max=0; - for (i=0; i<NUM_TOTALS; ++i) { - if (b->maxima[i]>max) - max = b->maxima[i]; - } - return max; -} - -/** Find the largest sums in the past NUM_SECS_BW_SUM_IS_VALID (roughly) - * seconds. Find one sum for reading and one for writing. They don't have - * to be at the same time. - * - * Return the smaller of these sums, divided by NUM_SECS_ROLLING_MEASURE. - */ -MOCK_IMPL(int, -rep_hist_bandwidth_assess,(void)) -{ - uint64_t w,r; - r = find_largest_max(read_array); - w = find_largest_max(write_array); - if (r>w) - return (int)(((double)w)/NUM_SECS_ROLLING_MEASURE); - else - return (int)(((double)r)/NUM_SECS_ROLLING_MEASURE); -} - -/** Print the bandwidth history of b (either [dir-]read_array or - * [dir-]write_array) into the buffer pointed to by buf. The format is - * simply comma separated numbers, from oldest to newest. - * - * It returns the number of bytes written. - */ -static size_t -rep_hist_fill_bandwidth_history(char *buf, size_t len, const bw_array_t *b) -{ - char *cp = buf; - int i, n; - const or_options_t *options = get_options(); - uint64_t cutoff; - - if (b->num_maxes_set <= b->next_max_idx) { - /* We haven't been through the circular array yet; time starts at i=0.*/ - i = 0; - } else { - /* We've been around the array at least once. The next i to be - overwritten is the oldest. */ - i = b->next_max_idx; - } - - if (options->RelayBandwidthRate) { - /* We don't want to report that we used more bandwidth than the max we're - * willing to relay; otherwise everybody will know how much traffic - * we used ourself. */ - cutoff = options->RelayBandwidthRate * NUM_SECS_BW_SUM_INTERVAL; - } else { - cutoff = UINT64_MAX; - } - - for (n=0; n<b->num_maxes_set; ++n,++i) { - uint64_t total; - if (i >= NUM_TOTALS) - i -= NUM_TOTALS; - tor_assert(i < NUM_TOTALS); - /* Round the bandwidth used down to the nearest 1k. */ - total = b->totals[i] & ~0x3ff; - if (total > cutoff) - total = cutoff; - - if (n==(b->num_maxes_set-1)) - tor_snprintf(cp, len-(cp-buf), "%"PRIu64, (total)); - else - tor_snprintf(cp, len-(cp-buf), "%"PRIu64",", (total)); - cp += strlen(cp); - } - return cp-buf; -} - -/** Allocate and return lines for representing this server's bandwidth - * history in its descriptor. We publish these lines in our extra-info - * descriptor. - */ -char * -rep_hist_get_bandwidth_lines(void) -{ - char *buf, *cp; - char t[ISO_TIME_LEN+1]; - int r; - bw_array_t *b = NULL; - const char *desc = NULL; - size_t len; - - /* [dirreq-](read|write)-history yyyy-mm-dd HH:MM:SS (n s) n,n,n... */ -/* The n,n,n part above. Largest representation of a uint64_t is 20 chars - * long, plus the comma. */ -#define MAX_HIST_VALUE_LEN (21*NUM_TOTALS) - len = (67+MAX_HIST_VALUE_LEN)*4; - buf = tor_malloc_zero(len); - cp = buf; - for (r=0;r<4;++r) { - char tmp[MAX_HIST_VALUE_LEN]; - size_t slen; - switch (r) { - case 0: - b = write_array; - desc = "write-history"; - break; - case 1: - b = read_array; - desc = "read-history"; - break; - case 2: - b = dir_write_array; - desc = "dirreq-write-history"; - break; - case 3: - b = dir_read_array; - desc = "dirreq-read-history"; - break; - } - tor_assert(b); - slen = rep_hist_fill_bandwidth_history(tmp, MAX_HIST_VALUE_LEN, b); - /* If we don't have anything to write, skip to the next entry. */ - if (slen == 0) - continue; - format_iso_time(t, b->next_period-NUM_SECS_BW_SUM_INTERVAL); - tor_snprintf(cp, len-(cp-buf), "%s %s (%d s) ", - desc, t, NUM_SECS_BW_SUM_INTERVAL); - cp += strlen(cp); - strlcat(cp, tmp, len-(cp-buf)); - cp += slen; - strlcat(cp, "\n", len-(cp-buf)); - ++cp; - } - return buf; -} - -/** Write a single bw_array_t into the Values, Ends, Interval, and Maximum - * entries of an or_state_t. Done before writing out a new state file. */ -static void -rep_hist_update_bwhist_state_section(or_state_t *state, - const bw_array_t *b, - smartlist_t **s_values, - smartlist_t **s_maxima, - time_t *s_begins, - int *s_interval) -{ - int i,j; - uint64_t maxval; - - if (*s_values) { - SMARTLIST_FOREACH(*s_values, char *, val, tor_free(val)); - smartlist_free(*s_values); - } - if (*s_maxima) { - SMARTLIST_FOREACH(*s_maxima, char *, val, tor_free(val)); - smartlist_free(*s_maxima); - } - if (! server_mode(get_options())) { - /* Clients don't need to store bandwidth history persistently; - * force these values to the defaults. */ - /* FFFF we should pull the default out of config.c's state table, - * so we don't have two defaults. */ - if (*s_begins != 0 || *s_interval != 900) { - time_t now = time(NULL); - time_t save_at = get_options()->AvoidDiskWrites ? now+3600 : now+600; - or_state_mark_dirty(state, save_at); - } - *s_begins = 0; - *s_interval = 900; - *s_values = smartlist_new(); - *s_maxima = smartlist_new(); - return; - } - *s_begins = b->next_period; - *s_interval = NUM_SECS_BW_SUM_INTERVAL; - - *s_values = smartlist_new(); - *s_maxima = smartlist_new(); - /* Set i to first position in circular array */ - i = (b->num_maxes_set <= b->next_max_idx) ? 0 : b->next_max_idx; - for (j=0; j < b->num_maxes_set; ++j,++i) { - if (i >= NUM_TOTALS) - i = 0; - smartlist_add_asprintf(*s_values, "%"PRIu64, - (b->totals[i] & ~0x3ff)); - maxval = b->maxima[i] / NUM_SECS_ROLLING_MEASURE; - smartlist_add_asprintf(*s_maxima, "%"PRIu64, - (maxval & ~0x3ff)); - } - smartlist_add_asprintf(*s_values, "%"PRIu64, - (b->total_in_period & ~0x3ff)); - maxval = b->max_total / NUM_SECS_ROLLING_MEASURE; - smartlist_add_asprintf(*s_maxima, "%"PRIu64, - (maxval & ~0x3ff)); -} - -/** Update <b>state</b> with the newest bandwidth history. Done before - * writing out a new state file. */ -void -rep_hist_update_state(or_state_t *state) -{ -#define UPDATE(arrname,st) \ - rep_hist_update_bwhist_state_section(state,\ - (arrname),\ - &state->BWHistory ## st ## Values, \ - &state->BWHistory ## st ## Maxima, \ - &state->BWHistory ## st ## Ends, \ - &state->BWHistory ## st ## Interval) - - UPDATE(write_array, Write); - UPDATE(read_array, Read); - UPDATE(dir_write_array, DirWrite); - UPDATE(dir_read_array, DirRead); - - if (server_mode(get_options())) { - or_state_mark_dirty(state, time(NULL)+(2*3600)); - } -#undef UPDATE -} - -/** Load a single bw_array_t from its Values, Ends, Maxima, and Interval - * entries in an or_state_t. Done while reading the state file. */ -static int -rep_hist_load_bwhist_state_section(bw_array_t *b, - const smartlist_t *s_values, - const smartlist_t *s_maxima, - const time_t s_begins, - const int s_interval) -{ - time_t now = time(NULL); - int retval = 0; - time_t start; - - uint64_t v, mv; - int i,ok,ok_m = 0; - int have_maxima = s_maxima && s_values && - (smartlist_len(s_values) == smartlist_len(s_maxima)); - - if (s_values && s_begins >= now - NUM_SECS_BW_SUM_INTERVAL*NUM_TOTALS) { - start = s_begins - s_interval*(smartlist_len(s_values)); - if (start > now) - return 0; - b->cur_obs_time = start; - b->next_period = start + NUM_SECS_BW_SUM_INTERVAL; - SMARTLIST_FOREACH_BEGIN(s_values, const char *, cp) { - const char *maxstr = NULL; - v = tor_parse_uint64(cp, 10, 0, UINT64_MAX, &ok, NULL); - if (have_maxima) { - maxstr = smartlist_get(s_maxima, cp_sl_idx); - mv = tor_parse_uint64(maxstr, 10, 0, UINT64_MAX, &ok_m, NULL); - mv *= NUM_SECS_ROLLING_MEASURE; - } else { - /* No maxima known; guess average rate to be conservative. */ - mv = (v / s_interval) * NUM_SECS_ROLLING_MEASURE; - } - if (!ok) { - retval = -1; - log_notice(LD_HIST, "Could not parse value '%s' into a number.'",cp); - } - if (maxstr && !ok_m) { - retval = -1; - log_notice(LD_HIST, "Could not parse maximum '%s' into a number.'", - maxstr); - } - - if (start < now) { - time_t cur_start = start; - time_t actual_interval_len = s_interval; - uint64_t cur_val = 0; - /* Calculate the average per second. This is the best we can do - * because our state file doesn't have per-second resolution. */ - if (start + s_interval > now) - actual_interval_len = now - start; - cur_val = v / actual_interval_len; - /* This is potentially inefficient, but since we don't do it very - * often it should be ok. */ - while (cur_start < start + actual_interval_len) { - add_obs(b, cur_start, cur_val); - ++cur_start; - } - b->max_total = mv; - /* This will result in some fairly choppy history if s_interval - * is not the same as NUM_SECS_BW_SUM_INTERVAL. XXXX */ - start += actual_interval_len; - } - } SMARTLIST_FOREACH_END(cp); - } - - /* Clean up maxima and observed */ - for (i=0; i<NUM_SECS_ROLLING_MEASURE; ++i) { - b->obs[i] = 0; - } - b->total_obs = 0; - - return retval; -} - -/** Set bandwidth history from the state file we just loaded. */ -int -rep_hist_load_state(or_state_t *state, char **err) -{ - int all_ok = 1; - - /* Assert they already have been malloced */ - tor_assert(read_array && write_array); - tor_assert(dir_read_array && dir_write_array); - -#define LOAD(arrname,st) \ - if (rep_hist_load_bwhist_state_section( \ - (arrname), \ - state->BWHistory ## st ## Values, \ - state->BWHistory ## st ## Maxima, \ - state->BWHistory ## st ## Ends, \ - state->BWHistory ## st ## Interval)<0) \ - all_ok = 0 - - LOAD(write_array, Write); - LOAD(read_array, Read); - LOAD(dir_write_array, DirWrite); - LOAD(dir_read_array, DirRead); - -#undef LOAD - if (!all_ok) { - *err = tor_strdup("Parsing of bandwidth history values failed"); - /* and create fresh arrays */ - bw_arrays_init(); - return -1; - } - return 0; -} - /*** Exit port statistics ***/ /* Some constants */ @@ -2213,223 +1646,6 @@ rep_hist_note_desc_served(const char * desc) /*** Connection statistics ***/ -/** Start of the current connection stats interval or 0 if we're not - * collecting connection statistics. */ -static time_t start_of_conn_stats_interval; - -/** Initialize connection stats. */ -void -rep_hist_conn_stats_init(time_t now) -{ - start_of_conn_stats_interval = now; -} - -/* Count connections that we read and wrote less than these many bytes - * from/to as below threshold. */ -#define BIDI_THRESHOLD 20480 - -/* Count connections that we read or wrote at least this factor as many - * bytes from/to than we wrote or read to/from as mostly reading or - * writing. */ -#define BIDI_FACTOR 10 - -/* Interval length in seconds for considering read and written bytes for - * connection stats. */ -#define BIDI_INTERVAL 10 - -/** Start of next BIDI_INTERVAL second interval. */ -static time_t bidi_next_interval = 0; - -/** Number of connections that we read and wrote less than BIDI_THRESHOLD - * bytes from/to in BIDI_INTERVAL seconds. */ -static uint32_t below_threshold = 0; - -/** Number of connections that we read at least BIDI_FACTOR times more - * bytes from than we wrote to in BIDI_INTERVAL seconds. */ -static uint32_t mostly_read = 0; - -/** Number of connections that we wrote at least BIDI_FACTOR times more - * bytes to than we read from in BIDI_INTERVAL seconds. */ -static uint32_t mostly_written = 0; - -/** Number of connections that we read and wrote at least BIDI_THRESHOLD - * bytes from/to, but not BIDI_FACTOR times more in either direction in - * BIDI_INTERVAL seconds. */ -static uint32_t both_read_and_written = 0; - -/** Entry in a map from connection ID to the number of read and written - * bytes on this connection in a BIDI_INTERVAL second interval. */ -typedef struct bidi_map_entry_t { - HT_ENTRY(bidi_map_entry_t) node; - uint64_t conn_id; /**< Connection ID */ - size_t read; /**< Number of read bytes */ - size_t written; /**< Number of written bytes */ -} bidi_map_entry_t; - -/** Map of OR connections together with the number of read and written - * bytes in the current BIDI_INTERVAL second interval. */ -static HT_HEAD(bidimap, bidi_map_entry_t) bidi_map = - HT_INITIALIZER(); - -static int -bidi_map_ent_eq(const bidi_map_entry_t *a, const bidi_map_entry_t *b) -{ - return a->conn_id == b->conn_id; -} - -/* DOCDOC bidi_map_ent_hash */ -static unsigned -bidi_map_ent_hash(const bidi_map_entry_t *entry) -{ - return (unsigned) entry->conn_id; -} - -HT_PROTOTYPE(bidimap, bidi_map_entry_t, node, bidi_map_ent_hash, - bidi_map_ent_eq); -HT_GENERATE2(bidimap, bidi_map_entry_t, node, bidi_map_ent_hash, - bidi_map_ent_eq, 0.6, tor_reallocarray_, tor_free_); - -/* DOCDOC bidi_map_free */ -static void -bidi_map_free_all(void) -{ - bidi_map_entry_t **ptr, **next, *ent; - for (ptr = HT_START(bidimap, &bidi_map); ptr; ptr = next) { - ent = *ptr; - next = HT_NEXT_RMV(bidimap, &bidi_map, ptr); - tor_free(ent); - } - HT_CLEAR(bidimap, &bidi_map); -} - -/** Reset counters for conn statistics. */ -void -rep_hist_reset_conn_stats(time_t now) -{ - start_of_conn_stats_interval = now; - below_threshold = 0; - mostly_read = 0; - mostly_written = 0; - both_read_and_written = 0; - bidi_map_free_all(); -} - -/** Stop collecting connection stats in a way that we can re-start doing - * so in rep_hist_conn_stats_init(). */ -void -rep_hist_conn_stats_term(void) -{ - rep_hist_reset_conn_stats(0); -} - -/** We read <b>num_read</b> bytes and wrote <b>num_written</b> from/to OR - * connection <b>conn_id</b> in second <b>when</b>. If this is the first - * observation in a new interval, sum up the last observations. Add bytes - * for this connection. */ -void -rep_hist_note_or_conn_bytes(uint64_t conn_id, size_t num_read, - size_t num_written, time_t when) -{ - if (!start_of_conn_stats_interval) - return; - /* Initialize */ - if (bidi_next_interval == 0) - bidi_next_interval = when + BIDI_INTERVAL; - /* Sum up last period's statistics */ - if (when >= bidi_next_interval) { - bidi_map_entry_t **ptr, **next, *ent; - for (ptr = HT_START(bidimap, &bidi_map); ptr; ptr = next) { - ent = *ptr; - if (ent->read + ent->written < BIDI_THRESHOLD) - below_threshold++; - else if (ent->read >= ent->written * BIDI_FACTOR) - mostly_read++; - else if (ent->written >= ent->read * BIDI_FACTOR) - mostly_written++; - else - both_read_and_written++; - next = HT_NEXT_RMV(bidimap, &bidi_map, ptr); - tor_free(ent); - } - while (when >= bidi_next_interval) - bidi_next_interval += BIDI_INTERVAL; - log_info(LD_GENERAL, "%d below threshold, %d mostly read, " - "%d mostly written, %d both read and written.", - below_threshold, mostly_read, mostly_written, - both_read_and_written); - } - /* Add this connection's bytes. */ - if (num_read > 0 || num_written > 0) { - bidi_map_entry_t *entry, lookup; - lookup.conn_id = conn_id; - entry = HT_FIND(bidimap, &bidi_map, &lookup); - if (entry) { - entry->written += num_written; - entry->read += num_read; - } else { - entry = tor_malloc_zero(sizeof(bidi_map_entry_t)); - entry->conn_id = conn_id; - entry->written = num_written; - entry->read = num_read; - HT_INSERT(bidimap, &bidi_map, entry); - } - } -} - -/** Return a newly allocated string containing the connection statistics - * until <b>now</b>, or NULL if we're not collecting conn stats. Caller must - * ensure start_of_conn_stats_interval is in the past. */ -char * -rep_hist_format_conn_stats(time_t now) -{ - char *result, written[ISO_TIME_LEN+1]; - - if (!start_of_conn_stats_interval) - return NULL; /* Not initialized. */ - - tor_assert(now >= start_of_conn_stats_interval); - - format_iso_time(written, now); - tor_asprintf(&result, "conn-bi-direct %s (%d s) %d,%d,%d,%d\n", - written, - (unsigned) (now - start_of_conn_stats_interval), - below_threshold, - mostly_read, - mostly_written, - both_read_and_written); - return result; -} - -/** If 24 hours have passed since the beginning of the current conn stats - * period, write conn stats to $DATADIR/stats/conn-stats (possibly - * overwriting an existing file) and reset counters. Return when we would - * next want to write conn stats or 0 if we never want to write. */ -time_t -rep_hist_conn_stats_write(time_t now) -{ - char *str = NULL; - - if (!start_of_conn_stats_interval) - return 0; /* Not initialized. */ - if (start_of_conn_stats_interval + WRITE_STATS_INTERVAL > now) - goto done; /* Not ready to write */ - - /* Generate history string. */ - str = rep_hist_format_conn_stats(now); - - /* Reset counters. */ - rep_hist_reset_conn_stats(now); - - /* Try to write to disk. */ - if (!check_or_create_data_subdir("stats")) { - write_to_data_subdir("stats", "conn-stats", str, "connection statistics"); - } - - done: - tor_free(str); - return start_of_conn_stats_interval + WRITE_STATS_INTERVAL; -} - /** Internal statistics to track how many requests of each type of * handshake we've received, and how many we've assigned to cpuworkers. * Useful for seeing trends in cpu load. @@ -2455,6 +1671,26 @@ rep_hist_note_circuit_handshake_assigned(uint16_t type) onion_handshakes_assigned[type]++; } +/** Get the circuit handshake value that is requested. */ +MOCK_IMPL(int, +rep_hist_get_circuit_handshake_requested, (uint16_t type)) +{ + if (BUG(type > MAX_ONION_HANDSHAKE_TYPE)) { + return 0; + } + return onion_handshakes_requested[type]; +} + +/** Get the circuit handshake value that is assigned. */ +MOCK_IMPL(int, +rep_hist_get_circuit_handshake_assigned, (uint16_t type)) +{ + if (BUG(type > MAX_ONION_HANDSHAKE_TYPE)) { + return 0; + } + return onion_handshakes_assigned[type]; +} + /** Log our onionskin statistics since the last time we were called. */ void rep_hist_log_circuit_handshake_stats(time_t now) @@ -2901,23 +2137,11 @@ rep_hist_free_all(void) hs_stats_free(hs_stats); digestmap_free(history_map, free_or_history); - bw_array_free(read_array); - read_array = NULL; - - bw_array_free(write_array); - write_array = NULL; - - bw_array_free(dir_read_array); - dir_read_array = NULL; - - bw_array_free(dir_write_array); - dir_write_array = NULL; - tor_free(exit_bytes_read); tor_free(exit_bytes_written); tor_free(exit_streams); predicted_ports_free_all(); - bidi_map_free_all(); + conn_stats_free_all(); if (circuits_for_buffer_stats) { SMARTLIST_FOREACH(circuits_for_buffer_stats, circ_buffer_stats_t *, s, diff --git a/src/feature/stats/rephist.h b/src/feature/stats/rephist.h index 92c3d2a5a5..c9ebc5c328 100644 --- a/src/feature/stats/rephist.h +++ b/src/feature/stats/rephist.h @@ -14,18 +14,9 @@ void rep_hist_init(void); void rep_hist_dump_stats(time_t now, int severity); -void rep_hist_note_bytes_read(uint64_t num_bytes, time_t when); -void rep_hist_note_bytes_written(uint64_t num_bytes, time_t when); void rep_hist_make_router_pessimal(const char *id, time_t when); -void rep_hist_note_dir_bytes_read(uint64_t num_bytes, time_t when); -void rep_hist_note_dir_bytes_written(uint64_t num_bytes, time_t when); - -MOCK_DECL(int, rep_hist_bandwidth_assess, (void)); -char *rep_hist_get_bandwidth_lines(void); -void rep_hist_update_state(or_state_t *state); -int rep_hist_load_state(or_state_t *state, char **err); void rep_history_clean(time_t before); void rep_hist_note_router_reachable(const char *id, const tor_addr_t *at_addr, @@ -65,18 +56,13 @@ void rep_hist_note_desc_served(const char * desc); void rep_hist_desc_stats_term(void); time_t rep_hist_desc_stats_write(time_t now); -void rep_hist_conn_stats_init(time_t now); -void rep_hist_note_or_conn_bytes(uint64_t conn_id, size_t num_read, - size_t num_written, time_t when); -void rep_hist_reset_conn_stats(time_t now); -char *rep_hist_format_conn_stats(time_t now); -time_t rep_hist_conn_stats_write(time_t now); -void rep_hist_conn_stats_term(void); - void rep_hist_note_circuit_handshake_requested(uint16_t type); void rep_hist_note_circuit_handshake_assigned(uint16_t type); void rep_hist_log_circuit_handshake_stats(time_t now); +MOCK_DECL(int, rep_hist_get_circuit_handshake_requested, (uint16_t type)); +MOCK_DECL(int, rep_hist_get_circuit_handshake_assigned, (uint16_t type)); + void rep_hist_hs_stats_init(time_t now); void rep_hist_hs_stats_term(void); time_t rep_hist_hs_stats_write(time_t now); @@ -95,16 +81,8 @@ extern uint32_t rephist_total_num; #ifdef TOR_UNIT_TESTS extern int onion_handshakes_requested[MAX_ONION_HANDSHAKE_TYPE+1]; extern int onion_handshakes_assigned[MAX_ONION_HANDSHAKE_TYPE+1]; -extern struct bw_array_t *write_array; #endif -#ifdef REPHIST_PRIVATE -typedef struct bw_array_t bw_array_t; -STATIC uint64_t find_largest_max(bw_array_t *b); -STATIC void commit_max(bw_array_t *b); -STATIC void advance_obs(bw_array_t *b); -#endif /* defined(REPHIST_PRIVATE) */ - /** * Represents the type of a cell for padding accounting */ diff --git a/src/lib/evloop/timers.c b/src/lib/evloop/timers.c index 7be9bae08e..11418e93fd 100644 --- a/src/lib/evloop/timers.c +++ b/src/lib/evloop/timers.c @@ -11,7 +11,7 @@ * The main advantage of tor_timer_t over using libevent's timers is that * they're way more efficient if we need to have thousands or millions of * them. For more information, see - * http://www.25thandclement.com/~william/projects/timeout.c.html + * https://www.25thandclement.com/~william/projects/timeout.c.html * * Periodic timers are available in the backend, but I've turned them off. * We can turn them back on if needed. diff --git a/src/lib/fs/files.c b/src/lib/fs/files.c index aeaeb5daea..a0b5a40aac 100644 --- a/src/lib/fs/files.c +++ b/src/lib/fs/files.c @@ -607,6 +607,9 @@ read_file_to_str_until_eof(int fd, size_t max_bytes_to_read, size_t *sz_out) * If <b>flags</b> & RFTS_BIN, open the file in binary mode. * If <b>flags</b> & RFTS_IGNORE_MISSING, don't warn if the file * doesn't exist. + * + * Unless the RFTS_BIN flag is set in <b>flags</b>, this function will strip + * any CR characters in the return value on all platforms. */ /* * This function <em>may</em> return an erroneous result if the file @@ -685,7 +688,6 @@ read_file_to_str, (const char *filename, int flags, struct stat *stat_out)) } string[r] = '\0'; /* NUL-terminate the result. */ -#if defined(_WIN32) || defined(__CYGWIN__) if (!bin && strchr(string, '\r')) { log_debug(LD_FS, "We didn't convert CRLF to LF as well as we hoped " "when reading %s. Coping.", @@ -695,8 +697,7 @@ read_file_to_str, (const char *filename, int flags, struct stat *stat_out)) } if (!bin) { statbuf.st_size = (size_t) r; - } else -#endif /* defined(_WIN32) || defined(__CYGWIN__) */ + } else { if (r != statbuf.st_size) { /* Unless we're using text mode on win32, we'd better have an exact * match for size. */ @@ -708,6 +709,7 @@ read_file_to_str, (const char *filename, int flags, struct stat *stat_out)) errno = save_errno; return NULL; } + } close(fd); if (stat_out) { memcpy(stat_out, &statbuf, sizeof(struct stat)); diff --git a/src/lib/log/log.c b/src/lib/log/log.c index 9ee87c0668..cd848fdd73 100644 --- a/src/lib/log/log.c +++ b/src/lib/log/log.c @@ -1308,7 +1308,7 @@ log_level_to_string(int level) /** NULL-terminated array of names for log domains such that domain_list[dom] * is a description of <b>dom</b>. * - * Remember to update doc/tor.1.txt if you modify this list. + * Remember to update doc/man/tor.1.txt if you modify this list. * */ static const char *domain_list[] = { "GENERAL", "CRYPTO", "NET", "CONFIG", "FS", "PROTOCOL", "MM", diff --git a/src/lib/math/laplace.c b/src/lib/math/laplace.c index 5c1d686a9c..a0e67384e6 100644 --- a/src/lib/math/laplace.c +++ b/src/lib/math/laplace.c @@ -29,7 +29,7 @@ sample_laplace_distribution(double mu, double b, double p) tor_assert(p >= 0.0 && p < 1.0); /* This is the "inverse cumulative distribution function" from: - * http://en.wikipedia.org/wiki/Laplace_distribution */ + * https://en.wikipedia.org/wiki/Laplace_distribution */ if (p <= 0.0) { /* Avoid taking log(0.0) == -INFINITY, as some processors or compiler * options can cause the program to trap. */ diff --git a/src/lib/net/address.c b/src/lib/net/address.c index 6d46f9b955..5a32533610 100644 --- a/src/lib/net/address.c +++ b/src/lib/net/address.c @@ -764,6 +764,15 @@ tor_addr_is_v4(const tor_addr_t *addr) return 0; /* Not IPv4 - unknown family or a full-blood IPv6 address */ } +/** Determine whether an address <b>addr</b> is an IPv6 (AF_INET6). Return + * true if so else false. */ +int +tor_addr_is_v6(const tor_addr_t *addr) +{ + tor_assert(addr); + return (tor_addr_family(addr) == AF_INET6); +} + /** Determine whether an address <b>addr</b> is null, either all zeroes or * belonging to family AF_UNSPEC. */ @@ -1217,20 +1226,28 @@ fmt_addr32(uint32_t addr) return buf; } -/** Return a string representing the family of <b>addr</b>. +/** Like fmt_addrport(), but takes <b>addr</b> as a host-order IPv4 + * addresses. Also not thread-safe, also clobbers its return buffer on + * repeated calls. */ +const char * +fmt_addr32_port(uint32_t addr, uint16_t port) +{ + static char buf[INET_NTOA_BUF_LEN + 6]; + snprintf(buf, sizeof(buf), "%s:%u", fmt_addr32(addr), port); + return buf; +} + +/** Return a string representing <b>family</b>. * * This string is a string constant, and must not be freed. * This function is thread-safe. */ const char * -fmt_addr_family(const tor_addr_t *addr) +fmt_af_family(sa_family_t family) { static int default_bug_once = 0; - IF_BUG_ONCE(!addr) - return "NULL pointer"; - - switch (tor_addr_family(addr)) { + switch (family) { case AF_INET6: return "IPv6"; case AF_INET: @@ -1242,7 +1259,7 @@ fmt_addr_family(const tor_addr_t *addr) default: if (!default_bug_once) { log_warn(LD_BUG, "Called with unknown address family %d", - (int)tor_addr_family(addr)); + (int)family); default_bug_once = 1; } return "unknown"; @@ -1250,6 +1267,20 @@ fmt_addr_family(const tor_addr_t *addr) //return "(unreachable code)"; } +/** Return a string representing the family of <b>addr</b>. + * + * This string is a string constant, and must not be freed. + * This function is thread-safe. + */ +const char * +fmt_addr_family(const tor_addr_t *addr) +{ + IF_BUG_ONCE(!addr) + return "NULL pointer"; + + return fmt_af_family(tor_addr_family(addr)); +} + /** Convert the string in <b>src</b> to a tor_addr_t <b>addr</b>. The string * may be an IPv4 address, or an IPv6 address surrounded by square brackets. * @@ -2083,6 +2114,18 @@ tor_addr_port_eq(const tor_addr_port_t *a, return tor_addr_eq(&a->addr, &b->addr) && a->port == b->port; } +/** + * Copy a tor_addr_port_t from @a source to @a dest. + **/ +void +tor_addr_port_copy(tor_addr_port_t *dest, + const tor_addr_port_t *source) +{ + tor_assert(dest); + tor_assert(source); + memcpy(dest, source, sizeof(tor_addr_port_t)); +} + /** Return true if <b>string</b> represents a valid IPv4 adddress in * 'a.b.c.d' form. */ diff --git a/src/lib/net/address.h b/src/lib/net/address.h index e5016ee4fe..bc8ec7744f 100644 --- a/src/lib/net/address.h +++ b/src/lib/net/address.h @@ -95,6 +95,7 @@ static inline uint32_t tor_addr_to_ipv4n(const tor_addr_t *a); static inline uint32_t tor_addr_to_ipv4h(const tor_addr_t *a); static inline uint32_t tor_addr_to_mapped_ipv4h(const tor_addr_t *a); static inline sa_family_t tor_addr_family(const tor_addr_t *a); +static inline bool tor_addr_is_unspec(const tor_addr_t *a); static inline const struct in_addr *tor_addr_to_in(const tor_addr_t *a); static inline int tor_addr_eq_ipv4h(const tor_addr_t *a, uint32_t u); @@ -188,6 +189,15 @@ tor_addr_family(const tor_addr_t *a) return a->family; } +/** + * Return true if the address @a is in the UNSPEC family. + **/ +static inline bool +tor_addr_is_unspec(const tor_addr_t *a) +{ + return a->family == AF_UNSPEC; +} + /** Return an in_addr* equivalent to <b>a</b>, or NULL if <b>a</b> is not * an IPv4 address. */ static inline const struct in_addr * @@ -236,6 +246,8 @@ const char *fmt_addr_impl(const tor_addr_t *addr, int decorate); const char *fmt_addrport(const tor_addr_t *addr, uint16_t port); #define fmt_addrport_ap(ap) fmt_addrport(&(ap)->addr, (ap)->port) const char *fmt_addr32(uint32_t addr); +const char *fmt_addr32_port(uint32_t addr, uint16_t port); +const char *fmt_af_family(sa_family_t family); const char *fmt_addr_family(const tor_addr_t *addr); MOCK_DECL(int,get_interface_address6,(int severity, sa_family_t family, @@ -272,6 +284,7 @@ struct sipkey; uint64_t tor_addr_keyed_hash(const struct sipkey *key, const tor_addr_t *addr); int tor_addr_is_v4(const tor_addr_t *addr); +int tor_addr_is_v6(const tor_addr_t *addr); int tor_addr_is_internal_(const tor_addr_t *ip, int for_listening, const char *filename, int lineno); #define tor_addr_is_internal(addr, for_listening) \ @@ -381,6 +394,7 @@ get_interface_address_list(int severity, int include_internal) tor_addr_port_t *tor_addr_port_new(const tor_addr_t *addr, uint16_t port); int tor_addr_port_eq(const tor_addr_port_t *a, const tor_addr_port_t *b); +void tor_addr_port_copy(tor_addr_port_t *dest, const tor_addr_port_t *source); int string_is_valid_dest(const char *string); int string_is_valid_nonrfc_hostname(const char *string); diff --git a/src/lib/osinfo/include.am b/src/lib/osinfo/include.am index 84bd7feb00..df8c98500c 100644 --- a/src/lib/osinfo/include.am +++ b/src/lib/osinfo/include.am @@ -7,7 +7,8 @@ endif # ADD_C_FILE: INSERT SOURCES HERE. src_lib_libtor_osinfo_a_SOURCES = \ - src/lib/osinfo/uname.c + src/lib/osinfo/uname.c \ + src/lib/osinfo/libc.c src_lib_libtor_osinfo_testing_a_SOURCES = \ $(src_lib_libtor_osinfo_a_SOURCES) @@ -16,4 +17,5 @@ src_lib_libtor_osinfo_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS) # ADD_C_FILE: INSERT HEADERS HERE. noinst_HEADERS += \ - src/lib/osinfo/uname.h + src/lib/osinfo/uname.h \ + src/lib/osinfo/libc.h diff --git a/src/lib/osinfo/libc.c b/src/lib/osinfo/libc.c new file mode 100644 index 0000000000..32cbad0fa2 --- /dev/null +++ b/src/lib/osinfo/libc.c @@ -0,0 +1,66 @@ +/* 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 libc.c + * @brief Functions to get the name and version of the system libc. + **/ + +#include "orconfig.h" +#include "lib/osinfo/libc.h" +#include <stdlib.h> + +#ifdef HAVE_GNU_LIBC_VERSION_H +#include <gnu/libc-version.h> +#endif + +#ifdef HAVE_GNU_LIBC_VERSION_H +#ifdef HAVE_GNU_GET_LIBC_VERSION +#define CHECK_LIBC_VERSION +#endif +#endif + +#define STR_IMPL(x) #x +#define STR(x) STR_IMPL(x) + +/** Return the name of the compile time libc. Returns NULL if we + * cannot identify the libc. */ +const char * +tor_libc_get_name(void) +{ +#ifdef __GLIBC__ + return "Glibc"; +#else /* !defined(__GLIBC__) */ + return NULL; +#endif /* defined(__GLIBC__) */ +} + +/** Return a string representation of the version of the currently running + * version of Glibc. */ +const char * +tor_libc_get_version_str(void) +{ +#ifdef CHECK_LIBC_VERSION + const char *version = gnu_get_libc_version(); + if (version == NULL) + return "N/A"; + return version; +#else /* !defined(CHECK_LIBC_VERSION) */ + return "N/A"; +#endif /* defined(CHECK_LIBC_VERSION) */ +} + +/** Return a string representation of the version of Glibc that was used at + * compilation time. */ +const char * +tor_libc_get_header_version_str(void) +{ +#ifdef __GLIBC__ + return STR(__GLIBC__) "." STR(__GLIBC_MINOR__); +#else + return "N/A"; +#endif /* defined(__GLIBC__) */ +} diff --git a/src/lib/osinfo/libc.h b/src/lib/osinfo/libc.h new file mode 100644 index 0000000000..f4303f8c9c --- /dev/null +++ b/src/lib/osinfo/libc.h @@ -0,0 +1,19 @@ +/* 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 libc.h + * @brief Header for lib/osinfo/libc.c + **/ + +#ifndef TOR_LIB_OSINFO_LIBC_H +#define TOR_LIB_OSINFO_LIBC_H + +const char *tor_libc_get_name(void); +const char *tor_libc_get_version_str(void); +const char *tor_libc_get_header_version_str(void); + +#endif /* !defined(TOR_LIB_OSINFO_LIBC_H) */ diff --git a/src/lib/process/restrict.c b/src/lib/process/restrict.c index a3ce52deaa..cd2a1c57b5 100644 --- a/src/lib/process/restrict.c +++ b/src/lib/process/restrict.c @@ -85,7 +85,7 @@ tor_set_max_memlock(void) { /* Future consideration for Windows is probably SetProcessWorkingSetSize * This is similar to setting the memory rlimit of RLIMIT_MEMLOCK - * http://msdn.microsoft.com/en-us/library/ms686234(VS.85).aspx + * https://msdn.microsoft.com/en-us/library/ms686234(VS.85).aspx */ struct rlimit limit; @@ -128,7 +128,7 @@ tor_mlockall(void) * Future consideration for Windows may be VirtualLock * VirtualLock appears to implement mlock() but not mlockall() * - * http://msdn.microsoft.com/en-us/library/aa366895(VS.85).aspx + * https://msdn.microsoft.com/en-us/library/aa366895(VS.85).aspx */ #ifdef HAVE_UNIX_MLOCKALL @@ -190,7 +190,7 @@ set_max_file_descriptors(rlim_t limit, int *max_out) /* Define some maximum connections values for systems where we cannot * automatically determine a limit. Re Cygwin, see - * http://archives.seul.org/or/talk/Aug-2006/msg00210.html + * https://archives.seul.org/or/talk/Aug-2006/msg00210.html * For an iPhone, 9999 should work. For Windows and all other unknown * systems we use 15000 as the default. */ #ifndef HAVE_GETRLIMIT diff --git a/src/lib/sandbox/sandbox.c b/src/lib/sandbox/sandbox.c index b917912f4d..a6eea9daca 100644 --- a/src/lib/sandbox/sandbox.c +++ b/src/lib/sandbox/sandbox.c @@ -117,6 +117,10 @@ #endif /* defined(__i386__) || ... */ +#ifdef M_SYSCALL +#define SYSCALL_NAME_DEBUGGING +#endif + /**Determines if at least one sandbox is active.*/ static int sandbox_active = 0; /** Holds the parameter list configuration for the sandbox.*/ @@ -133,6 +137,10 @@ static sandbox_cfg_t *filter_dynamic = NULL; * the high bits of the value might get masked out improperly. */ #define SCMP_CMP_MASKED(a,b,c) \ SCMP_CMP4((a), SCMP_CMP_MASKED_EQ, ~(scmp_datum_t)(b), (c)) +/* For negative constants, the rule to add depends on the glibc version. */ +#define SCMP_CMP_NEG(a,op,b) (libc_negative_constant_needs_cast() ? \ + (SCMP_CMP((a), (op), (unsigned int)(b))) : \ + (SCMP_CMP_STR((a), (op), (b)))) /** Variable used for storing all syscall numbers that will be allowed with the * stage 1 general Tor sandbox. @@ -275,9 +283,18 @@ static int filter_nopar_gen[] = { SCMP_SYS(recvfrom), SCMP_SYS(sendto), SCMP_SYS(unlink), +#ifdef __NR_unlinkat + SCMP_SYS(unlinkat), +#endif SCMP_SYS(poll) }; +/* opendir is not a syscall but it will use either open or openat. We do not + * want the decision to allow open/openat to be the callers reponsability, so + * we create a phony syscall number for opendir and sb_opendir will choose the + * correct syscall. */ +#define PHONY_OPENDIR_SYSCALL -2 + /* These macros help avoid the error where the number of filters we add on a * single rule don't match the arg_cnt param. */ #define seccomp_rule_add_0(ctx,act,call) \ @@ -431,31 +448,59 @@ sb_mmap2(scmp_filter_ctx ctx, sandbox_cfg_t *filter) #endif #endif -/* Return true if we think we're running with a libc that always uses - * openat on linux. */ +/* Return true the libc version is greater or equal than + * <b>major</b>.<b>minor</b>. Returns false otherwise. */ static int -libc_uses_openat_for_everything(void) +is_libc_at_least(int major, int minor) { #ifdef CHECK_LIBC_VERSION const char *version = gnu_get_libc_version(); if (version == NULL) return 0; - int major = -1; - int minor = -1; + int libc_major = -1; + int libc_minor = -1; - tor_sscanf(version, "%d.%d", &major, &minor); - if (major >= 3) + tor_sscanf(version, "%d.%d", &libc_major, &libc_minor); + if (libc_major > major) return 1; - else if (major == 2 && minor >= 26) + else if (libc_major == major && libc_minor >= minor) return 1; else return 0; #else /* !defined(CHECK_LIBC_VERSION) */ + (void)major; + (void)minor; return 0; #endif /* defined(CHECK_LIBC_VERSION) */ } +/* Return true if we think we're running with a libc that uses openat for the + * open function on linux. */ +static int +libc_uses_openat_for_open(void) +{ + return is_libc_at_least(2, 26); +} + +/* Return true if we think we're running with a libc that uses openat for the + * opendir function on linux. */ +static int +libc_uses_openat_for_opendir(void) +{ + // libc 2.27 and above or between 2.15 (inclusive) and 2.22 (exclusive) + return is_libc_at_least(2, 27) || + (is_libc_at_least(2, 15) && !is_libc_at_least(2, 22)); +} + +/* Return true if we think we're running with a libc that needs to cast + * negative arguments like AT_FDCWD for seccomp rules. */ +static int +libc_negative_constant_needs_cast(void) +{ + return is_libc_at_least(2, 27); +} + /** Allow a single file to be opened. If <b>use_openat</b> is true, * we're using a libc that remaps all the opens into openats. */ static int @@ -463,7 +508,7 @@ allow_file_open(scmp_filter_ctx ctx, int use_openat, const char *file) { if (use_openat) { return seccomp_rule_add_2(ctx, SCMP_ACT_ALLOW, SCMP_SYS(openat), - SCMP_CMP(0, SCMP_CMP_EQ, (unsigned int)AT_FDCWD), + SCMP_CMP_NEG(0, SCMP_CMP_EQ, AT_FDCWD), SCMP_CMP_STR(1, SCMP_CMP_EQ, file)); } else { return seccomp_rule_add_1(ctx, SCMP_ACT_ALLOW, SCMP_SYS(open), @@ -481,7 +526,7 @@ sb_open(scmp_filter_ctx ctx, sandbox_cfg_t *filter) int rc; sandbox_cfg_t *elem = NULL; - int use_openat = libc_uses_openat_for_everything(); + int use_openat = libc_uses_openat_for_open(); // for each dynamic parameter filters for (elem = filter; elem != NULL; elem = elem->next) { @@ -599,7 +644,7 @@ sb_openat(scmp_filter_ctx ctx, sandbox_cfg_t *filter) if (param != NULL && param->prot == 1 && param->syscall == SCMP_SYS(openat)) { rc = seccomp_rule_add_3(ctx, SCMP_ACT_ALLOW, SCMP_SYS(openat), - SCMP_CMP(0, SCMP_CMP_EQ, AT_FDCWD), + SCMP_CMP_NEG(0, SCMP_CMP_EQ, AT_FDCWD), SCMP_CMP_STR(1, SCMP_CMP_EQ, param->value), SCMP_CMP(2, SCMP_CMP_EQ, O_RDONLY|O_NONBLOCK|O_LARGEFILE|O_DIRECTORY| O_CLOEXEC)); @@ -614,6 +659,38 @@ sb_openat(scmp_filter_ctx ctx, sandbox_cfg_t *filter) return 0; } +static int +sb_opendir(scmp_filter_ctx ctx, sandbox_cfg_t *filter) +{ + int rc; + sandbox_cfg_t *elem = NULL; + + // for each dynamic parameter filters + for (elem = filter; elem != NULL; elem = elem->next) { + smp_param_t *param = elem->param; + + if (param != NULL && param->prot == 1 && param->syscall + == PHONY_OPENDIR_SYSCALL) { + if (libc_uses_openat_for_opendir()) { + rc = seccomp_rule_add_3(ctx, SCMP_ACT_ALLOW, SCMP_SYS(openat), + SCMP_CMP_NEG(0, SCMP_CMP_EQ, AT_FDCWD), + SCMP_CMP_STR(1, SCMP_CMP_EQ, param->value), + SCMP_CMP(2, SCMP_CMP_EQ, O_RDONLY|O_NONBLOCK|O_LARGEFILE| + O_DIRECTORY|O_CLOEXEC)); + } else { + rc = allow_file_open(ctx, 0, param->value); + } + if (rc != 0) { + log_err(LD_BUG,"(Sandbox) failed to add openat syscall, received " + "libseccomp error %d", rc); + return rc; + } + } + } + + return 0; +} + /** * Function responsible for setting up the socket syscall for * the seccomp filter sandbox. @@ -928,7 +1005,7 @@ sb_epoll_ctl(scmp_filter_ctx ctx, sandbox_cfg_t *filter) * the seccomp filter sandbox. * * NOTE: if multiple filters need to be added, the PR_SECCOMP parameter needs - * to be whitelisted in this function. + * to be allowlisted in this function. */ static int sb_prctl(scmp_filter_ctx ctx, sandbox_cfg_t *filter) @@ -1128,6 +1205,7 @@ static sandbox_filter_func_t filter_func[] = { sb_chmod, sb_open, sb_openat, + sb_opendir, sb_rename, #ifdef __NR_fcntl64 sb_fcntl64, @@ -1447,6 +1525,19 @@ sandbox_cfg_allow_openat_filename(sandbox_cfg_t **cfg, char *file) return 0; } +int +sandbox_cfg_allow_opendir_dirname(sandbox_cfg_t **cfg, char *dir) +{ + sandbox_cfg_t *elem = NULL; + + elem = new_element(PHONY_OPENDIR_SYSCALL, dir); + + elem->next = *cfg; + *cfg = elem; + + return 0; +} + /** * Function responsible for going through the parameter syscall filters and * call each function pointer in the list. @@ -1545,8 +1636,10 @@ install_syscall_filter(sandbox_cfg_t* cfg) return (rc < 0 ? -rc : rc); } +#ifdef SYSCALL_NAME_DEBUGGING #include "lib/sandbox/linux_syscalls.inc" +/** Return a string containing the name of a given syscall (if we know it) */ static const char * get_syscall_name(int syscall_num) { @@ -1564,6 +1657,28 @@ get_syscall_name(int syscall_num) } } +/** Return the syscall number from a ucontext_t that we got in a signal + * handler (if we know how to do that). */ +static int +get_syscall_from_ucontext(const ucontext_t *ctx) +{ + return (int) ctx->uc_mcontext.M_SYSCALL; +} +#else +static const char * +get_syscall_name(int syscall_num) +{ + (void) syscall_num; + return "unknown"; +} +static int +get_syscall_from_ucontext(const ucontext_t *ctx) +{ + (void) ctx; + return -1; +} +#endif + #ifdef USE_BACKTRACE #define MAX_DEPTH 256 static void *syscall_cb_buf[MAX_DEPTH]; @@ -1579,7 +1694,6 @@ sigsys_debugging(int nr, siginfo_t *info, void *void_context) { ucontext_t *ctx = (ucontext_t *) (void_context); const char *syscall_name; - int syscall; #ifdef USE_BACKTRACE size_t depth; int n_fds, i; @@ -1594,7 +1708,7 @@ sigsys_debugging(int nr, siginfo_t *info, void *void_context) if (!ctx) return; - syscall = (int) ctx->uc_mcontext.M_SYSCALL; + int syscall = get_syscall_from_ucontext(ctx); #ifdef USE_BACKTRACE depth = backtrace(syscall_cb_buf, MAX_DEPTH); @@ -1752,6 +1866,13 @@ sandbox_cfg_allow_openat_filename(sandbox_cfg_t **cfg, char *file) } int +sandbox_cfg_allow_opendir_dirname(sandbox_cfg_t **cfg, char *dir) +{ + (void)cfg; (void)dir; + return 0; +} + +int sandbox_cfg_allow_stat_filename(sandbox_cfg_t **cfg, char *file) { (void)cfg; (void)file; diff --git a/src/lib/sandbox/sandbox.h b/src/lib/sandbox/sandbox.h index b50df48255..a2b3227b90 100644 --- a/src/lib/sandbox/sandbox.h +++ b/src/lib/sandbox/sandbox.h @@ -136,6 +136,13 @@ int sandbox_cfg_allow_rename(sandbox_cfg_t **cfg, char *file1, char *file2); int sandbox_cfg_allow_openat_filename(sandbox_cfg_t **cfg, char *file); /** + * Function used to add a opendir allowed filename to a supplied configuration. + * The (char*) specifies the path to the allowed dir; we steal the pointer to + * that dir. + */ +int sandbox_cfg_allow_opendir_dirname(sandbox_cfg_t **cfg, char *dir); + +/** * Function used to add a stat/stat64 allowed filename to a configuration. * The (char*) specifies the path to the allowed file; that pointer is stolen. */ diff --git a/src/lib/tls/buffers_tls.c b/src/lib/tls/buffers_tls.c index b92a14d6a1..1b99467d2b 100644 --- a/src/lib/tls/buffers_tls.c +++ b/src/lib/tls/buffers_tls.c @@ -59,6 +59,9 @@ read_to_chunk_tls(buf_t *buf, chunk_t *chunk, tor_tls_t *tls, * Second, the TLS stream's events do not correspond directly to network * events: sometimes, before a TLS stream can read, the network must be * ready to write -- or vice versa. + * + * On success, return the number of bytes read. On error, a TOR_TLS_* negative + * code is returned (expect any of them except TOR_TLS_DONE). */ int buf_read_from_tls(buf_t *buf, tor_tls_t *tls, size_t at_most) @@ -92,8 +95,6 @@ buf_read_from_tls(buf_t *buf, tor_tls_t *tls, size_t at_most) return r; /* Error */ tor_assert(total_read+r <= BUF_MAX_LEN); total_read += r; - if ((size_t)r < readlen) /* eof, block, or no more to read. */ - break; } return (int)total_read; } diff --git a/src/lib/tls/tortls.h b/src/lib/tls/tortls.h index e8dbbf5279..517cdc17dd 100644 --- a/src/lib/tls/tortls.h +++ b/src/lib/tls/tortls.h @@ -81,6 +81,7 @@ void tor_tls_free_all(void); void tor_tls_init(void); void tls_log_errors(tor_tls_t *tls, int severity, int domain, const char *doing); +const char *tor_tls_get_last_error_msg(const tor_tls_t *tls); int tor_tls_context_init(unsigned flags, crypto_pk_t *client_identity, crypto_pk_t *server_identity, diff --git a/src/lib/tls/tortls_nss.c b/src/lib/tls/tortls_nss.c index 62e8262115..c873367f6f 100644 --- a/src/lib/tls/tortls_nss.c +++ b/src/lib/tls/tortls_nss.c @@ -369,6 +369,8 @@ tls_log_errors(tor_tls_t *tls, int severity, int domain, (void)tls; PRErrorCode code = PORT_GetError(); + if (tls) + tls->last_error = code; const char *addr = tls ? tls->address : NULL; const char *string = PORT_ErrorToString(code); @@ -391,6 +393,17 @@ tls_log_errors(tor_tls_t *tls, int severity, int domain, with, addr); } } +const char * +tor_tls_get_last_error_msg(const tor_tls_t *tls) +{ + IF_BUG_ONCE(!tls) { + return NULL; + } + if (tls->last_error == 0) { + return NULL; + } + return PORT_ErrorToString((PRErrorCode)tls->last_error); +} tor_tls_t * tor_tls_new(tor_socket_t sock, int is_server) @@ -418,6 +431,16 @@ tor_tls_new(tor_socket_t sock, int is_server) return NULL; } + /* even if though the socket is already nonblocking, we need to tell NSS + * about the fact, so that it knows what to do when it says EAGAIN. */ + PRSocketOptionData data; + data.option = PR_SockOpt_Nonblocking; + data.value.non_blocking = 1; + if (PR_SetSocketOption(ssl, &data) != PR_SUCCESS) { + PR_Close(ssl); + return NULL; + } + tor_tls_t *tls = tor_malloc_zero(sizeof(tor_tls_t)); tls->magic = TOR_TLS_MAGIC; tls->context = ctx; @@ -713,23 +736,58 @@ MOCK_IMPL(int, tor_tls_cert_matches_key,(const tor_tls_t *tls, const struct tor_x509_cert_t *cert)) { - tor_assert(tls); tor_assert(cert); + tor_assert(cert->cert); + int rv = 0; - CERTCertificate *peercert = SSL_PeerCertificate(tls->ssl); - if (!peercert) + tor_x509_cert_t *peercert = tor_tls_get_peer_cert((tor_tls_t *)tls); + + if (!peercert || !peercert->cert) goto done; - CERTSubjectPublicKeyInfo *peer_info = &peercert->subjectPublicKeyInfo; + + CERTSubjectPublicKeyInfo *peer_info = &peercert->cert->subjectPublicKeyInfo; CERTSubjectPublicKeyInfo *cert_info = &cert->cert->subjectPublicKeyInfo; + + /* NSS stores the `len` field in bits, instead of bytes, for the + * `subjectPublicKey` field in CERTSubjectPublicKeyInfo, but + * `SECITEM_ItemsAreEqual()` compares the two bitstrings using a length field + * defined in bytes. + * + * We convert the `len` field from bits to bytes, do our comparison with + * `SECITEM_ItemsAreEqual()`, and reset the length field from bytes to bits + * again. + * + * See also NSS's own implementation of `SECKEY_CopySubjectPublicKeyInfo()` + * in seckey.c in the NSS source tree. This function also does the conversion + * between bits and bytes. + */ + const unsigned int peer_info_orig_len = peer_info->subjectPublicKey.len; + const unsigned int cert_info_orig_len = cert_info->subjectPublicKey.len; + + /* We convert the length from bits to bytes, but instead of using NSS's + * `DER_ConvertBitString()` macro on both of peer_info->subjectPublicKey and + * cert_info->subjectPublicKey, we have to do the conversion explicitly since + * both of the two subjectPublicKey fields are allowed to point to the same + * memory address. Otherwise, the bits to bytes conversion would potentially + * be applied twice, which would lead to us comparing too few of the bytes + * when we call SECITEM_ItemsAreEqual(), which would be catastrophic. + */ + peer_info->subjectPublicKey.len = ((peer_info_orig_len + 7) >> 3); + cert_info->subjectPublicKey.len = ((cert_info_orig_len + 7) >> 3); + rv = SECOID_CompareAlgorithmID(&peer_info->algorithm, &cert_info->algorithm) == 0 && SECITEM_ItemsAreEqual(&peer_info->subjectPublicKey, &cert_info->subjectPublicKey); + /* Convert from bytes back to bits. */ + peer_info->subjectPublicKey.len = peer_info_orig_len; + cert_info->subjectPublicKey.len = cert_info_orig_len; + done: - if (peercert) - CERT_DestroyCertificate(peercert); + tor_x509_cert_free(peercert); + return rv; } diff --git a/src/lib/tls/tortls_openssl.c b/src/lib/tls/tortls_openssl.c index 68d6e2aa50..2269714141 100644 --- a/src/lib/tls/tortls_openssl.c +++ b/src/lib/tls/tortls_openssl.c @@ -245,10 +245,30 @@ tls_log_errors(tor_tls_t *tls, int severity, int domain, const char *doing) unsigned long err; while ((err = ERR_get_error()) != 0) { + if (tls) + tls->last_error = err; tor_tls_log_one_error(tls, err, severity, domain, doing); } } +/** + * Return a string representing more detail about the last error received + * on TLS. + * + * May return null if no error was found. + **/ +const char * +tor_tls_get_last_error_msg(const tor_tls_t *tls) +{ + IF_BUG_ONCE(!tls) { + return NULL; + } + if (tls->last_error == 0) { + return NULL; + } + return (const char*)ERR_reason_error_string(tls->last_error); +} + #define CATCH_SYSCALL 1 #define CATCH_ZERO 2 diff --git a/src/lib/tls/tortls_st.h b/src/lib/tls/tortls_st.h index 925896d493..34abe52ee3 100644 --- a/src/lib/tls/tortls_st.h +++ b/src/lib/tls/tortls_st.h @@ -67,6 +67,8 @@ struct tor_tls_t { */ unsigned long last_write_count; unsigned long last_read_count; + /** Most recent error value from ERR_get_error(). */ + unsigned long last_error; /** If set, a callback to invoke whenever the client tries to renegotiate * the handshake. */ void (*negotiated_callback)(tor_tls_t *tls, void *arg); @@ -77,6 +79,7 @@ struct tor_tls_t { /** Last values retried from tor_get_prfiledesc_byte_counts(). */ uint64_t last_write_count; uint64_t last_read_count; + long last_error; #endif }; diff --git a/src/lib/tls/x509.c b/src/lib/tls/x509.c index 793fa8b9c3..2515499298 100644 --- a/src/lib/tls/x509.c +++ b/src/lib/tls/x509.c @@ -23,6 +23,7 @@ tor_tls_pick_certificate_lifetime(time_t now, time_t *start_time_out, time_t *end_time_out) { + tor_assert(cert_lifetime < INT_MAX); time_t start_time, end_time; /* Make sure we're part-way through the certificate lifetime, rather * than having it start right now. Don't choose quite uniformly, since @@ -36,7 +37,7 @@ tor_tls_pick_certificate_lifetime(time_t now, const time_t start_granularity = 24*3600; time_t earliest_start_time; /* Don't actually start in the future! */ - if (cert_lifetime <= min_real_lifetime + start_granularity) { + if ((int)cert_lifetime <= min_real_lifetime + start_granularity) { earliest_start_time = now - 1; } else { earliest_start_time = now + min_real_lifetime + start_granularity diff --git a/src/lib/trace/.may_include b/src/lib/trace/.may_include index 45cd13676b..1ed533cc7a 100644 --- a/src/lib/trace/.may_include +++ b/src/lib/trace/.may_include @@ -1,3 +1,4 @@ orconfig.h lib/log/*.h lib/trace/*.h +lib/subsys/*.h diff --git a/src/lib/trace/debug.h b/src/lib/trace/debug.h index 87b3074e0b..84a2867a6d 100644 --- a/src/lib/trace/debug.h +++ b/src/lib/trace/debug.h @@ -6,8 +6,10 @@ * \brief Macros for debugging our event-trace support. **/ -#ifndef TOR_TRACE_LOG_DEBUG_H -#define TOR_TRACE_LOG_DEBUG_H +#ifndef TOR_TRACE_DEBUG_H +#define TOR_TRACE_DEBUG_H + +#ifdef USE_TRACING_INSTRUMENTATION_LOG_DEBUG #include "lib/log/log.h" @@ -17,14 +19,20 @@ /* Send every event to a debug log level. This is useful to debug new trace * events without implementing them for a specific event tracing framework. - * Note that the arguments are ignored since at this step we do not know the - * types and amount there is. */ + * + * NOTE: arguments can't be used becaue there is no easy generic ways to learn + * their type and amount. It is probably doable with massive C pre-processor + * trickery but this is meant to be simple. */ + +#define TOR_TRACE_LOG_DEBUG(subsystem, event_name, ...) \ + log_debug(LD_GENERAL, "Tracepoint \"" XSTR(event_name) "\" from " \ + "subsystem \"" XSTR(subsystem) "\" hit.") + +#else /* defined(USE_TRACING_INSTRUMENTATION_LOG_DEBUG) */ + +/* NOP the debug event. */ +#define TOR_TRACE_LOG_DEBUG(subsystem, name, ...) -/* Example on how to map a tracepoint to log_debug(). */ -#undef tor_trace -#define tor_trace(subsystem, name, args...) \ - log_debug(LD_GENERAL, "Trace event \"" XSTR(name) "\" from " \ - "\"" XSTR(subsystem) "\" hit. " \ - "(line "XSTR(__LINE__) ")") +#endif /* defined(USE_TRACING_INSTRUMENTATION_LOG_DEBUG) */ -#endif /* !defined(TOR_TRACE_LOG_DEBUG_H) */ +#endif /* !defined(TOR_TRACE_DEBUG_H) */ diff --git a/src/lib/trace/events.h b/src/lib/trace/events.h index 368f85dd02..ce1604de22 100644 --- a/src/lib/trace/events.h +++ b/src/lib/trace/events.h @@ -3,43 +3,75 @@ /** * \file events.h - * \brief Header file for Tor event tracing. + * \brief Header file for Tor tracing instrumentation definition. **/ -#ifndef TOR_TRACE_EVENTS_H -#define TOR_TRACE_EVENTS_H +#ifndef TOR_LIB_TRACE_EVENTS_H +#define TOR_LIB_TRACE_EVENTS_H + +#include "orconfig.h" /* - * The following defines a generic event tracing function name that has to be - * used to trace events in the code base. + * A tracepoint signature is defined as follow: + * + * tor_trace(<subsystem>, <event_name>, <args>...) + * + * If tracing is enabled, the tor_trace() macro is mapped to all possible + * instrumentations (defined below). Each instrumentation type MUST define a + * top level macro (TOR_TRACE_<type>) so it can be inserted into each + * tracepoint. + * + * In case no tracing is enabled (HAVE_TRACING), tracepoints are NOP and thus + * have no execution cost. * - * That generic function is then defined by a event tracing framework. For - * instance, the "log debug" framework sends all trace events to log_debug() - * which is defined in src/trace/debug.h which can only be enabled at compile - * time (--enable-event-tracing-debug). + * Currently, three types of instrumentation are supported: * - * By default, every trace events in the code base are replaced by a NOP. See - * doc/HACKING/Tracing.md for more information on how to use event tracing or - * add events. + * log-debug: Every tracepoints is mapped to a log_debug() statement. + * + * User Statically-Defined Tracing (USDT): Probes that can be used with perf, + * dtrace, SystemTap, DTrace and BPF Compiler Collection (BCC). + * + * LTTng-UST: Probes for the LTTng Userspace Tracer. If USDT interface + * (sdt.h) is available, the USDT probes are also generated by LTTng thus + * enabling this instrumentation provides both probes. */ -#ifdef TOR_EVENT_TRACING_ENABLED -/* Map every trace event to a per subsystem macro. */ -#define tor_trace(subsystem, name, ...) \ - tor_trace_##subsystem(name, __VA_ARGS__) +/** Helper to disambiguate these identifiers in the code base. They should + * only be used with tor_trace() like so: + * + * tor_trace(TR_SUBSYS(circuit), TR_EV(opened), ...); + */ + +#define TR_SUBSYS(name) tor_ ## name +#define TR_EV(name) name + +#ifdef HAVE_TRACING -/* Enable event tracing for the debug framework where all trace events are - * mapped to a log_debug(). */ -#ifdef USE_EVENT_TRACING_DEBUG +#define tor_trace(subsystem, event_name, ...) \ + do { \ + TOR_TRACE_LOG_DEBUG(subsystem, event_name); \ + TOR_TRACE_USDT(subsystem, event_name, ## __VA_ARGS__); \ + TOR_TRACE_LTTNG(subsystem, event_name, ## __VA_ARGS__); \ + } while (0) + +/* This corresponds to the --enable-tracing-instrumentation-log-debug + * configure option which maps all tracepoints to a log_debug() statement. */ #include "lib/trace/debug.h" -#endif -#else /* !defined(TOR_EVENT_TRACING_ENABLED) */ +/* This corresponds to the --enable-tracing-instrumentation-usdt configure + * option which will generate USDT probes for each tracepoints. */ +#include "lib/trace/usdt/usdt.h" + +/* This corresponds to the --enable-tracing-instrumentation-lttng configure + * option which will generate LTTng probes for each tracepoints. */ +#include "lib/trace/lttng/lttng.h" + +#else /* !defined(HAVE_TRACING) */ -/* Reaching this point, we NOP every event declaration because event tracing - * is not been enabled at compile time. */ -#define tor_trace(subsystem, name, args...) +/* Reaching this point, tracing is disabled thus we NOP every tracepoints + * declaration so we have no execution cost at runtime. */ +#define tor_trace(subsystem, name, ...) -#endif /* defined(TOR_EVENT_TRACING_ENABLED) */ +#endif /* defined(HAVE_TRACING) */ -#endif /* !defined(TOR_TRACE_EVENTS_H) */ +#endif /* !defined(TOR_LIB_TRACE_EVENTS_H) */ diff --git a/src/lib/trace/include.am b/src/lib/trace/include.am index 98098c87f4..6fe1365652 100644 --- a/src/lib/trace/include.am +++ b/src/lib/trace/include.am @@ -2,18 +2,34 @@ noinst_LIBRARIES += \ src/lib/libtor-trace.a +# ADD_C_FILE: INSERT SOURCES HERE. +LIBTOR_TRACE_A_SOURCES = \ + src/lib/trace/trace.c \ + src/lib/trace/trace_sys.c + # ADD_C_FILE: INSERT HEADERS HERE. TRACEHEADERS = \ - src/lib/trace/trace.h \ + src/lib/trace/trace.h \ + src/lib/trace/trace_sys.h \ src/lib/trace/events.h -if USE_EVENT_TRACING_DEBUG +if USE_TRACING_INSTRUMENTATION_LOG_DEBUG TRACEHEADERS += \ src/lib/trace/debug.h endif -# ADD_C_FILE: INSERT SOURCES HERE. -src_lib_libtor_trace_a_SOURCES = \ - src/lib/trace/trace.c +if USE_TRACING_INSTRUMENTATION_USDT +include src/lib/trace/usdt/include.am +endif + +if USE_TRACING_INSTRUMENTATION_LTTNG +include src/lib/trace/lttng/include.am +endif + +if USE_TRACING +src_lib_libtor_trace_a_SOURCES = $(LIBTOR_TRACE_A_SOURCES) +else +src_lib_libtor_trace_a_SOURCES = src/lib/trace/trace_stub.c +endif noinst_HEADERS+= $(TRACEHEADERS) diff --git a/src/lib/trace/lttng/include.am b/src/lib/trace/lttng/include.am new file mode 100644 index 0000000000..4495ce0900 --- /dev/null +++ b/src/lib/trace/lttng/include.am @@ -0,0 +1,3 @@ +# ADD_C_FILE: INSERT HEADERS HERE. +TRACEHEADERS += \ + src/lib/trace/lttng/lttng.h diff --git a/src/lib/trace/lttng/lttng.h b/src/lib/trace/lttng/lttng.h new file mode 100644 index 0000000000..8ede98bb02 --- /dev/null +++ b/src/lib/trace/lttng/lttng.h @@ -0,0 +1,28 @@ +/* Copyright (c) 2020, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file lttng.h + * \brief Header file for lttng.c. + **/ + +#ifndef TOR_TRACE_LTTNG_LTTNG_H +#define TOR_TRACE_LTTNG_LTTNG_H + +#ifdef USE_TRACING_INSTRUMENTATION_LTTNG + +#include <lttng/tracepoint.h> + +/* Map event to an LTTng tracepoint. */ +#define TOR_TRACE_LTTNG(subsystem, event_name, ...) \ + tracepoint(subsystem, event_name, ## __VA_ARGS__) + +#else /* !defined(USE_TRACING_INSTRUMENTATION_LTTNG) */ + +/* NOP event. */ +#define TOR_TRACE_LTTNG(subsystem, event_name, ...) + +#endif /* !defined(USE_TRACING_INSTRUMENTATION_LTTNG) */ + +#endif /* TOR_TRACE_LTTNG_LTTNG_H */ + diff --git a/src/lib/trace/trace.c b/src/lib/trace/trace.c index 4e5c66b4c6..10d11c17c5 100644 --- a/src/lib/trace/trace.c +++ b/src/lib/trace/trace.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2017-2020, The Tor Project, Inc. */ +/* Copyright (c) 2020, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -15,3 +15,9 @@ void tor_trace_init(void) { } + +/** Free all the tracing library. */ +void +tor_trace_free_all(void) +{ +} diff --git a/src/lib/trace/trace.h b/src/lib/trace/trace.h index 5e24678c3c..22589dbe94 100644 --- a/src/lib/trace/trace.h +++ b/src/lib/trace/trace.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2017-2020, The Tor Project, Inc. */ +/* Copyright (c) 2020, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -6,9 +6,31 @@ * \brief Header for trace.c **/ -#ifndef TOR_TRACE_TRACE_H -#define TOR_TRACE_TRACE_H +#ifndef TOR_LIB_TRACE_TRACE_H +#define TOR_LIB_TRACE_TRACE_H + +#include "orconfig.h" void tor_trace_init(void); +void tor_trace_free_all(void); + +#ifdef HAVE_TRACING + +#include "lib/log/log.h" + +static inline void +tracing_log_warning(void) +{ + log_warn(LD_GENERAL, + "Tracing capabilities have been built in. If this is NOT on " + "purpose, your tor is NOT safe to run."); +} + +#else + +/* NOP it. */ +#define tracing_log_warning() + +#endif /* defined(HAVE_TRACING) */ -#endif /* !defined(TOR_TRACE_TRACE_H) */ +#endif /* !defined(TOR_LIB_TRACE_TRACE_H) */ diff --git a/src/lib/trace/trace_stub.c b/src/lib/trace/trace_stub.c new file mode 100644 index 0000000000..9043efe360 --- /dev/null +++ b/src/lib/trace/trace_stub.c @@ -0,0 +1,19 @@ +/* Copyright (c) 2020, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file trace_stub.c + * \brief Stub declaratinos for use when trace library is disabled. + **/ + +#include "lib/subsys/subsys.h" + +#include "lib/trace/trace_sys.h" + +const subsys_fns_t sys_tracing = { + SUBSYS_DECLARE_LOCATION(), + + .name = "tracing", + .supported = false, + .level = TRACE_SUBSYS_LEVEL, +}; diff --git a/src/lib/trace/trace_sys.c b/src/lib/trace/trace_sys.c new file mode 100644 index 0000000000..2ba0258407 --- /dev/null +++ b/src/lib/trace/trace_sys.c @@ -0,0 +1,36 @@ +/* Copyright (c) 2018-2019, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file log_sys.c + * \brief Setup and tear down the tracing module. + **/ + +#include "lib/subsys/subsys.h" + +#include "lib/trace/trace.h" +#include "lib/trace/trace_sys.h" + +static int +subsys_tracing_initialize(void) +{ + tor_trace_init(); + return 0; +} + +static void +subsys_tracing_shutdown(void) +{ + tor_trace_free_all(); +} + +const subsys_fns_t sys_tracing = { + SUBSYS_DECLARE_LOCATION(), + + .name = "tracing", + .supported = true, + .level = TRACE_SUBSYS_LEVEL, + + .initialize = subsys_tracing_initialize, + .shutdown = subsys_tracing_shutdown, +}; diff --git a/src/lib/trace/trace_sys.h b/src/lib/trace/trace_sys.h new file mode 100644 index 0000000000..d4da5a9701 --- /dev/null +++ b/src/lib/trace/trace_sys.h @@ -0,0 +1,22 @@ +/* Copyright (c) 2018-2019, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file log_sys.h + * \brief Declare subsystem object for the logging module. + **/ + +#ifndef TOR_TRACE_SYS_H +#define TOR_TRACE_SYS_H + +extern const struct subsys_fns_t sys_tracing; + +/** + * Subsystem level for the tracing system. + * + * Defined here so that it can be shared between the real and stub + * definitions. + **/ +#define TRACE_SUBSYS_LEVEL (-85) + +#endif /* !defined(TOR_TRACE_SYS_H) */ diff --git a/src/lib/trace/usdt/include.am b/src/lib/trace/usdt/include.am new file mode 100644 index 0000000000..4e7e04c326 --- /dev/null +++ b/src/lib/trace/usdt/include.am @@ -0,0 +1,3 @@ +# ADD_C_FILE: INSERT HEADERS HERE. +TRACEHEADERS += \ + src/lib/trace/usdt/usdt.h diff --git a/src/lib/trace/usdt/usdt.h b/src/lib/trace/usdt/usdt.h new file mode 100644 index 0000000000..0b5fd6c444 --- /dev/null +++ b/src/lib/trace/usdt/usdt.h @@ -0,0 +1,33 @@ +/* Copyright (c) 2020, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file trace.h + * \brief Header for usdt.h + **/ + +#ifndef TOR_TRACE_USDT_USDT_H +#define TOR_TRACE_USDT_USDT_H + +#ifdef USE_TRACING_INSTRUMENTATION_USDT + +#ifdef HAVE_SYS_SDT_H +#define SDT_USE_VARIADIC +#include <sys/sdt.h> +#define TOR_STAP_PROBEV STAP_PROBEV +#else /* defined(HAVE_SYS_SDT_H) */ +#define TOR_STAP_PROBEV(...) +#endif + +/* Map events to an USDT probe. */ +#define TOR_TRACE_USDT(subsystem, event_name, ...) \ + TOR_STAP_PROBEV(subsystem, event_name, ## __VA_ARGS__); + +#else /* !defined(USE_TRACING_INSTRUMENTATION_USDT) */ + +/* NOP event. */ +#define TOR_TRACE_USDT(subsystem, event_name, ...) + +#endif /* !defined(USE_TRACING_INSTRUMENTATION_USDT) */ + +#endif /* !defined(TOR_TRACE_USDT_USDT_H) */ diff --git a/src/mainpage.md b/src/mainpage.md index 2c4c494354..91ceb7dbf7 100644 --- a/src/mainpage.md +++ b/src/mainpage.md @@ -83,8 +83,9 @@ will be scheduled. The codebase is divided into a few top-level subdirectories, each of which contains several sub-modules. - - `ext` -- Code maintained elsewhere that we include in the Tor - source distribution. + - \refdir{ext} -- Code maintained elsewhere that we include in the Tor + source distribution. You should not edit this code if you can + avoid it: we try to keep it identical to the upstream versions. - \refdir{lib} -- Lower-level utility code, not necessarily tor-specific. diff --git a/src/rust/protover/ffi.rs b/src/rust/protover/ffi.rs index 14170d0353..2bf8d3a987 100644 --- a/src/rust/protover/ffi.rs +++ b/src/rust/protover/ffi.rs @@ -84,7 +84,7 @@ pub extern "C" fn protocol_list_supports_protocol( version: uint32_t, ) -> c_int { if c_protocol_list.is_null() { - return 1; + return 0; } // Require an unsafe block to read the version from a C string. The pointer @@ -93,7 +93,7 @@ pub extern "C" fn protocol_list_supports_protocol( let protocol_list = match c_str.to_str() { Ok(n) => n, - Err(_) => return 1, + Err(_) => return 0, }; let proto_entry: UnvalidatedProtoEntry = match protocol_list.parse() { Ok(n) => n, @@ -140,7 +140,7 @@ pub extern "C" fn protocol_list_supports_protocol_or_later( version: uint32_t, ) -> c_int { if c_protocol_list.is_null() { - return 1; + return 0; } // Require an unsafe block to read the version from a C string. The pointer @@ -149,7 +149,7 @@ pub extern "C" fn protocol_list_supports_protocol_or_later( let protocol_list = match c_str.to_str() { Ok(n) => n, - Err(_) => return 1, + Err(_) => return 0, }; let protocol = match translate_to_rust(c_protocol) { @@ -159,7 +159,7 @@ pub extern "C" fn protocol_list_supports_protocol_or_later( let proto_entry: UnvalidatedProtoEntry = match protocol_list.parse() { Ok(n) => n, - Err(_) => return 1, + Err(_) => return 0, }; if proto_entry.supports_protocol_or_later(&protocol.into(), &version) { diff --git a/src/rust/protover/protover.rs b/src/rust/protover/protover.rs index 6d2ef33eec..076cd5301e 100644 --- a/src/rust/protover/protover.rs +++ b/src/rust/protover/protover.rs @@ -163,13 +163,13 @@ pub(crate) fn get_supported_protocols_cstr() -> &'static CStr { DirCache=1-2 \ FlowCtrl=1 \ HSDir=1-2 \ - HSIntro=3-4 \ + HSIntro=3-5 \ HSRend=1-2 \ Link=1-5 \ LinkAuth=3 \ Microdesc=1-2 \ Padding=2 \ - Relay=1-2" + Relay=1-3" ) } else { cstr!( @@ -178,13 +178,13 @@ pub(crate) fn get_supported_protocols_cstr() -> &'static CStr { DirCache=1-2 \ FlowCtrl=1 \ HSDir=1-2 \ - HSIntro=3-4 \ + HSIntro=3-5 \ HSRend=1-2 \ Link=1-5 \ LinkAuth=1,3 \ Microdesc=1-2 \ Padding=2 \ - Relay=1-2" + Relay=1-3" ) } } diff --git a/src/test/conf_examples/crypto_accel/expected_log_nss b/src/test/conf_examples/crypto_accel/expected_log_nss index c0fe7b003c..bcbfa2cf6b 100644 --- a/src/test/conf_examples/crypto_accel/expected_log_nss +++ b/src/test/conf_examples/crypto_accel/expected_log_nss @@ -1 +1 @@ -Tor 0.* running on .* with Libevent .*, NSS .*, Zlib .*, Liblzma .*, and Libzstd .* +Tor 0.* running on .* with Libevent .*, NSS .*, Zlib .*, Liblzma .*, Libzstd .* and .* .* as libc diff --git a/src/test/conf_examples/crypto_accel_req/expected_log_nss b/src/test/conf_examples/crypto_accel_req/expected_log_nss index c0fe7b003c..bcbfa2cf6b 100644 --- a/src/test/conf_examples/crypto_accel_req/expected_log_nss +++ b/src/test/conf_examples/crypto_accel_req/expected_log_nss @@ -1 +1 @@ -Tor 0.* running on .* with Libevent .*, NSS .*, Zlib .*, Liblzma .*, and Libzstd .* +Tor 0.* running on .* with Libevent .*, NSS .*, Zlib .*, Liblzma .*, Libzstd .* and .* .* as libc diff --git a/src/test/conf_examples/dirauth_3/error_no_dirauth b/src/test/conf_examples/dirauth_3/error_no_dirauth new file mode 100644 index 0000000000..e6bd5db69c --- /dev/null +++ b/src/test/conf_examples/dirauth_3/error_no_dirauth @@ -0,0 +1 @@ +This tor was built with dirauth mode disabled. diff --git a/src/test/conf_examples/dirauth_3/error_no_dirauth_relay b/src/test/conf_examples/dirauth_3/error_no_dirauth_relay new file mode 100644 index 0000000000..e6bd5db69c --- /dev/null +++ b/src/test/conf_examples/dirauth_3/error_no_dirauth_relay @@ -0,0 +1 @@ +This tor was built with dirauth mode disabled. diff --git a/src/test/conf_examples/dirauth_3/expected b/src/test/conf_examples/dirauth_3/expected new file mode 100644 index 0000000000..23eac3a5f8 --- /dev/null +++ b/src/test/conf_examples/dirauth_3/expected @@ -0,0 +1,9 @@ +Address 192.0.2.1 +AuthoritativeDirectory 1 +ContactInfo tor_parse_test@example.net +DirPort 192.0.2.1:2 +DownloadExtraInfo 1 +Nickname Unnamed +ORPort 192.0.2.1:1 +ORPort [2001:DB8::1]:3 +V3AuthoritativeDirectory 1 diff --git a/src/test/conf_examples/dirauth_3/expected_log b/src/test/conf_examples/dirauth_3/expected_log new file mode 100644 index 0000000000..3127c9b125 --- /dev/null +++ b/src/test/conf_examples/dirauth_3/expected_log @@ -0,0 +1 @@ +Read configuration file .*dirauth_3[./]*torrc diff --git a/src/test/conf_examples/dirauth_3/torrc b/src/test/conf_examples/dirauth_3/torrc new file mode 100644 index 0000000000..9663a9bc0c --- /dev/null +++ b/src/test/conf_examples/dirauth_3/torrc @@ -0,0 +1,13 @@ +# Authority with IPv6 address + +AuthoritativeDirectory 1 +V3AuthoritativeDirectory 1 + +ContactInfo tor_parse_test@example.net + +Address 192.0.2.1 + +ORPort 192.0.2.1:1 +DirPort 192.0.2.1:2 + +ORPort [2001:DB8::1]:3 diff --git a/src/test/conf_examples/lzma_zstd_1/expected_log b/src/test/conf_examples/lzma_zstd_1/expected_log index a5531ca21e..f143b23102 100644 --- a/src/test/conf_examples/lzma_zstd_1/expected_log +++ b/src/test/conf_examples/lzma_zstd_1/expected_log @@ -1 +1 @@ -Tor 0.* running on .* with Libevent .*, .*, Zlib .*, Liblzma N/A, and Libzstd N/A +Tor 0.* running on .* with Libevent .*, .*, Zlib .*, Liblzma N/A, Libzstd N/A and .* .* as libc diff --git a/src/test/conf_examples/lzma_zstd_1/expected_log_lzma b/src/test/conf_examples/lzma_zstd_1/expected_log_lzma index 2947e5991b..abb4731abc 100644 --- a/src/test/conf_examples/lzma_zstd_1/expected_log_lzma +++ b/src/test/conf_examples/lzma_zstd_1/expected_log_lzma @@ -1 +1 @@ -Tor 0.* running on .* with Libevent .*, .*, Zlib .*, Liblzma .*, and Libzstd N/A +Tor 0.* running on .* with Libevent .*, .*, Zlib .*, Liblzma .*, Libzstd N/A and .* .* as libc diff --git a/src/test/conf_examples/lzma_zstd_1/expected_log_lzma_zstd b/src/test/conf_examples/lzma_zstd_1/expected_log_lzma_zstd index e76e4357f8..b4e45772dd 100644 --- a/src/test/conf_examples/lzma_zstd_1/expected_log_lzma_zstd +++ b/src/test/conf_examples/lzma_zstd_1/expected_log_lzma_zstd @@ -1 +1 @@ -Tor 0.* running on .* with Libevent .*, .*, Zlib .*, Liblzma .*, and Libzstd .*
\ No newline at end of file +Tor 0.* running on .* with Libevent .*, .*, Zlib .*, Liblzma .*, Libzstd .* and .* .* as libc
\ No newline at end of file diff --git a/src/test/conf_examples/lzma_zstd_1/expected_log_zstd b/src/test/conf_examples/lzma_zstd_1/expected_log_zstd index c8b174423b..994b46974b 100644 --- a/src/test/conf_examples/lzma_zstd_1/expected_log_zstd +++ b/src/test/conf_examples/lzma_zstd_1/expected_log_zstd @@ -1 +1 @@ -Tor 0.* running on .* with Libevent .*, .*, Zlib .*, Liblzma N/A, and Libzstd .*
\ No newline at end of file +Tor 0.* running on .* with Libevent .*, .*, Zlib .*, Liblzma N/A, Libzstd .* and .* .* as libc
\ No newline at end of file diff --git a/src/test/conf_examples/nss_1/expected_log b/src/test/conf_examples/nss_1/expected_log index 32e8cfc2f8..38f1febda5 100644 --- a/src/test/conf_examples/nss_1/expected_log +++ b/src/test/conf_examples/nss_1/expected_log @@ -1 +1 @@ -Tor 0.* running on .* with Libevent .*, OpenSSL .*, Zlib .*, Liblzma .*, and Libzstd .* +Tor 0.* running on .* with Libevent .*, OpenSSL .*, Zlib .*, Liblzma .*, Libzstd .* and .* .* as libc diff --git a/src/test/conf_examples/nss_1/expected_log_nss b/src/test/conf_examples/nss_1/expected_log_nss index c0fe7b003c..bcbfa2cf6b 100644 --- a/src/test/conf_examples/nss_1/expected_log_nss +++ b/src/test/conf_examples/nss_1/expected_log_nss @@ -1 +1 @@ -Tor 0.* running on .* with Libevent .*, NSS .*, Zlib .*, Liblzma .*, and Libzstd .* +Tor 0.* running on .* with Libevent .*, NSS .*, Zlib .*, Liblzma .*, Libzstd .* and .* .* as libc diff --git a/src/test/fuzz/include.am b/src/test/fuzz/include.am index d0711f05d6..f3f7202ce2 100644 --- a/src/test/fuzz/include.am +++ b/src/test/fuzz/include.am @@ -14,7 +14,7 @@ FUZZING_LIBS = \ @TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_GDI@ @TOR_LIB_USERENV@ @CURVE25519_LIBS@ \ @TOR_SYSTEMD_LIBS@ \ @TOR_LZMA_LIBS@ \ - @TOR_ZSTD_LIBS@ + @TOR_ZSTD_LIBS@ @TOR_TRACE_LIBS@ oss-fuzz-prereqs: \ $(TOR_INTERNAL_TESTING_LIBS) diff --git a/src/test/include.am b/src/test/include.am index e7647260c5..d7be1a5f77 100644 --- a/src/test/include.am +++ b/src/test/include.am @@ -297,7 +297,7 @@ src_test_test_switch_id_LDADD = \ $(rust_ldadd) \ @TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ \ @TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_USERENV@ \ - @TOR_LZMA_LIBS@ @TOR_ZSTD_LIBS@ + @TOR_LZMA_LIBS@ @TOR_ZSTD_LIBS@ @TOR_TRACE_LIBS@ src_test_test_LDFLAGS = @TOR_LDFLAGS_zlib@ $(TOR_LDFLAGS_CRYPTLIB) \ @TOR_LDFLAGS_libevent@ @@ -307,7 +307,7 @@ src_test_test_LDADD = \ @TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ \ $(TOR_LIBS_CRYPTLIB) @TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_GDI@ @TOR_LIB_USERENV@ \ @CURVE25519_LIBS@ \ - @TOR_SYSTEMD_LIBS@ @TOR_LZMA_LIBS@ @TOR_ZSTD_LIBS@ + @TOR_SYSTEMD_LIBS@ @TOR_LZMA_LIBS@ @TOR_ZSTD_LIBS@ @TOR_TRACE_LIBS@ src_test_test_slow_CPPFLAGS = $(src_test_test_CPPFLAGS) src_test_test_slow_CFLAGS = $(src_test_test_CFLAGS) @@ -336,7 +336,7 @@ src_test_bench_LDADD = \ @TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ \ $(TOR_LIBS_CRYPTLIB) @TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_GDI@ @TOR_LIB_USERENV@ \ @CURVE25519_LIBS@ \ - @TOR_SYSTEMD_LIBS@ @TOR_LZMA_LIBS@ @TOR_ZSTD_LIBS@ + @TOR_SYSTEMD_LIBS@ @TOR_LZMA_LIBS@ @TOR_ZSTD_LIBS@ @TOR_TRACE_LIBS@ src_test_test_workqueue_LDFLAGS = @TOR_LDFLAGS_zlib@ $(TOR_LDFLAGS_CRYPTLIB) \ @TOR_LDFLAGS_libevent@ @@ -346,7 +346,7 @@ src_test_test_workqueue_LDADD = \ @TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ \ $(TOR_LIBS_CRYPTLIB) @TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_GDI@ @TOR_LIB_USERENV@ \ @CURVE25519_LIBS@ \ - @TOR_LZMA_LIBS@ @TOR_ZSTD_LIBS@ + @TOR_LZMA_LIBS@ @TOR_ZSTD_LIBS@ @TOR_TRACE_LIBS@ src_test_test_timers_CPPFLAGS = $(src_test_test_CPPFLAGS) src_test_test_timers_CFLAGS = $(src_test_test_CFLAGS) @@ -358,7 +358,7 @@ src_test_test_timers_LDADD = \ @TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ \ $(TOR_LIBS_CRYPTLIB) @TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_GDI@ @TOR_LIB_USERENV@ \ @CURVE25519_LIBS@ \ - @TOR_LZMA_LIBS@ + @TOR_LZMA_LIBS@ @TOR_TRACE_LIBS@ src_test_test_timers_LDFLAGS = $(src_test_test_LDFLAGS) # ADD_C_FILE: INSERT HEADERS HERE. @@ -394,7 +394,7 @@ src_test_test_ntor_cl_LDADD = \ $(rust_ldadd) \ @TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ \ $(TOR_LIBS_CRYPTLIB) @TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_GDI@ @TOR_LIB_USERENV@ \ - @CURVE25519_LIBS@ @TOR_LZMA_LIBS@ + @CURVE25519_LIBS@ @TOR_LZMA_LIBS@ @TOR_TRACE_LIBS@ src_test_test_ntor_cl_AM_CPPFLAGS = \ $(AM_CPPFLAGS) @@ -403,7 +403,8 @@ src_test_test_hs_ntor_cl_LDFLAGS = @TOR_LDFLAGS_zlib@ $(TOR_LDFLAGS_CRYPTLIB) src_test_test_hs_ntor_cl_LDADD = \ $(TOR_INTERNAL_LIBS) \ @TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ \ - $(TOR_LIBS_CRYPTLIB) @TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_GDI@ @CURVE25519_LIBS@ + $(TOR_LIBS_CRYPTLIB) @TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_GDI@ \ + @CURVE25519_LIBS@ @TOR_TRACE_LIBS@ src_test_test_hs_ntor_cl_AM_CPPFLAGS = \ $(AM_CPPFLAGS) @@ -415,7 +416,8 @@ src_test_test_bt_cl_LDADD = \ $(TOR_UTIL_TESTING_LIBS) \ $(rust_ldadd) \ @TOR_LIB_MATH@ \ - @TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_GDI@ @TOR_LIB_USERENV@ + @TOR_LIB_WS32@ @TOR_LIB_IPHLPAPI@ @TOR_LIB_GDI@ @TOR_LIB_USERENV@ \ + @TOR_TRACE_LIBS@ src_test_test_bt_cl_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS) src_test_test_bt_cl_CPPFLAGS= $(src_test_AM_CPPFLAGS) $(TEST_CPPFLAGS) endif diff --git a/src/test/rend_test_helpers.c b/src/test/rend_test_helpers.c index 61bacb4d2e..8e40167aeb 100644 --- a/src/test/rend_test_helpers.c +++ b/src/test/rend_test_helpers.c @@ -2,6 +2,7 @@ /* See LICENSE for licensing information */ #include "core/or/or.h" +#include "core/or/extendinfo.h" #include "lib/crypt_ops/crypto_rand.h" #include "test/test.h" #include "feature/rend/rendcommon.h" @@ -58,7 +59,8 @@ create_descriptor(rend_service_descriptor_t **generated, char **service_id, for (i = 0; i < intro_points; i++) { rend_intro_point_t *intro = tor_malloc_zero(sizeof(rend_intro_point_t)); crypto_pk_t *okey = pk_generate(2 + i); - intro->extend_info = tor_malloc_zero(sizeof(extend_info_t)); + intro->extend_info = + extend_info_new(NULL, NULL, NULL, NULL, NULL, NULL, 0); intro->extend_info->onion_key = okey; crypto_pk_get_digest(intro->extend_info->onion_key, intro->extend_info->identity_digest); @@ -66,8 +68,12 @@ create_descriptor(rend_service_descriptor_t **generated, char **service_id, base16_encode(intro->extend_info->nickname + 1, sizeof(intro->extend_info->nickname) - 1, intro->extend_info->identity_digest, DIGEST_LEN); - tor_addr_from_ipv4h(&intro->extend_info->addr, crypto_rand_int(65536)); - intro->extend_info->port = 1 + crypto_rand_int(65535); + tor_addr_t addr; + uint16_t port; + /* Does not cover all IP addresses. */ + tor_addr_from_ipv4h(&addr, crypto_rand_int(65536) + 1); + port = 1 + crypto_rand_int(65535); + extend_info_add_orport(intro->extend_info, &addr, port); intro->intro_key = crypto_pk_dup_key(pk2); smartlist_add((*generated)->intro_nodes, intro); } @@ -91,4 +97,3 @@ mock_rend_data(const char *onion_address) DIGEST_LEN)); return rend_query; } - diff --git a/src/test/slow_ed25519.py b/src/test/slow_ed25519.py index afad678000..be4eeab857 100644 --- a/src/test/slow_ed25519.py +++ b/src/test/slow_ed25519.py @@ -1,5 +1,5 @@ # This is the ed25519 implementation from -# http://ed25519.cr.yp.to/python/ed25519.py . +# https://ed25519.cr.yp.to/python/ed25519.py . # It is in the public domain. # # It isn't constant-time. Don't use it except for testing. Also, see diff --git a/src/test/test-memwipe.c b/src/test/test-memwipe.c index 4faf7bc5a1..5e4cc7678e 100644 --- a/src/test/test-memwipe.c +++ b/src/test/test-memwipe.c @@ -30,8 +30,8 @@ const char *s = NULL; #define BUF_LEN 2048 #define FILL_BUFFER_IMPL() \ + do { \ unsigned int i; \ - unsigned sum = 0; \ \ /* Fill up a 1k buffer with a recognizable pattern. */ \ for (i = 0; i < BUF_LEN; i += strlen(s)) { \ @@ -42,7 +42,8 @@ const char *s = NULL; /* optimized away. */ \ for (i = 0; i < BUF_LEN; ++i) { \ sum += (unsigned char)buf[i]; \ - } + } \ + } while (0) #ifdef OpenBSD /* Disable some of OpenBSD's malloc protections for this test. This helps @@ -55,7 +56,8 @@ static unsigned fill_a_buffer_memset(void) { char buf[BUF_LEN]; - FILL_BUFFER_IMPL() + unsigned sum = 0; + FILL_BUFFER_IMPL(); memset(buf, 0, sizeof(buf)); return sum; } @@ -64,7 +66,8 @@ static unsigned fill_a_buffer_memwipe(void) { char buf[BUF_LEN]; - FILL_BUFFER_IMPL() + unsigned sum = 0; + FILL_BUFFER_IMPL(); memwipe(buf, 0, sizeof(buf)); return sum; } @@ -73,7 +76,8 @@ static unsigned fill_a_buffer_nothing(void) { char buf[BUF_LEN]; - FILL_BUFFER_IMPL() + unsigned sum = 0; + FILL_BUFFER_IMPL(); return sum; } @@ -116,7 +120,8 @@ static unsigned fill_heap_buffer_memset(void) { char *buf = heap_buf = raw_malloc(BUF_LEN); - FILL_BUFFER_IMPL() + unsigned sum = 0; + FILL_BUFFER_IMPL(); memset(buf, 0, BUF_LEN); raw_free(buf); return sum; @@ -126,7 +131,8 @@ static unsigned fill_heap_buffer_memwipe(void) { char *buf = heap_buf = raw_malloc(BUF_LEN); - FILL_BUFFER_IMPL() + unsigned sum = 0; + FILL_BUFFER_IMPL(); memwipe(buf, 0, BUF_LEN); raw_free(buf); return sum; @@ -136,7 +142,8 @@ static unsigned fill_heap_buffer_nothing(void) { char *buf = heap_buf = raw_malloc(BUF_LEN); - FILL_BUFFER_IMPL() + unsigned sum = 0; + FILL_BUFFER_IMPL(); raw_free(buf); return sum; } diff --git a/src/test/test.c b/src/test/test.c index 4b6082ce4f..2961669c46 100644 --- a/src/test/test.c +++ b/src/test/test.c @@ -1,5 +1,5 @@ /* Copyright (c) 2001-2004, Roger Dingledine. - * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. +->a * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. * Copyright (c) 2007-2020, The Tor Project, Inc. */ /* See LICENSE for licensing information */ @@ -44,6 +44,7 @@ #include "lib/compress/compress.h" #include "app/config/config.h" #include "core/or/connection_edge.h" +#include "core/or/extendinfo.h" #include "feature/rend/rendcommon.h" #include "feature/rend/rendcache.h" #include "feature/rend/rendparse.h" @@ -564,7 +565,8 @@ test_rend_fns(void *arg) for (i = 0; i < 3; i++) { rend_intro_point_t *intro = tor_malloc_zero(sizeof(rend_intro_point_t)); crypto_pk_t *okey = pk_generate(2 + i); - intro->extend_info = tor_malloc_zero(sizeof(extend_info_t)); + intro->extend_info = + extend_info_new(NULL, NULL, NULL, NULL, NULL, NULL, 0); intro->extend_info->onion_key = okey; crypto_pk_get_digest(intro->extend_info->onion_key, intro->extend_info->identity_digest); @@ -573,9 +575,12 @@ test_rend_fns(void *arg) base16_encode(intro->extend_info->nickname + 1, sizeof(intro->extend_info->nickname) - 1, intro->extend_info->identity_digest, DIGEST_LEN); + tor_addr_t addr; + uint16_t port; /* Does not cover all IP addresses. */ - tor_addr_from_ipv4h(&intro->extend_info->addr, crypto_rand_int(65536)); - intro->extend_info->port = 1 + crypto_rand_int(65535); + tor_addr_from_ipv4h(&addr, crypto_rand_int(65536) + 1); + port = 1 + crypto_rand_int(65535); + extend_info_add_orport(intro->extend_info, &addr, port); intro->intro_key = crypto_pk_dup_key(pk2); smartlist_add(generated->intro_nodes, intro); } @@ -613,8 +618,12 @@ test_rend_fns(void *arg) tt_mem_op(gen_info->identity_digest,OP_EQ, par_info->identity_digest, DIGEST_LEN); tt_str_op(gen_info->nickname,OP_EQ, par_info->nickname); - tt_assert(tor_addr_eq(&gen_info->addr, &par_info->addr)); - tt_int_op(gen_info->port,OP_EQ, par_info->port); + const tor_addr_port_t *a1, *a2; + a1 = extend_info_get_orport(gen_info, AF_INET); + a2 = extend_info_get_orport(par_info, AF_INET); + tt_assert(a1 && a2); + tt_assert(tor_addr_eq(&a1->addr, &a2->addr)); + tt_int_op(a2->port,OP_EQ, a2->port); } rend_service_descriptor_free(parsed); diff --git a/src/test/test_addr.c b/src/test/test_addr.c index cf5aad7e71..dbc581288d 100644 --- a/src/test/test_addr.c +++ b/src/test/test_addr.c @@ -341,6 +341,7 @@ test_addr_ip6_helpers(void *arg) test_pton6_bad("0XYXXY"); test_pton6_bad("0x"); test_pton6_bad("0X"); + test_pton6_bad("2000::1a00::1000:fc098"); /* test internal checking */ test_external_ip("fbff:ffff::2:7", 0); diff --git a/src/test/test_address.c b/src/test/test_address.c index 4cedbda347..17479d69c2 100644 --- a/src/test/test_address.c +++ b/src/test/test_address.c @@ -1152,23 +1152,23 @@ test_address_tor_addr_in_same_network_family(void *ignored) tor_addr_parse(&a, "8.8.8.8"); tor_addr_parse(&b, "8.8.4.4"); - tt_int_op(addrs_in_same_network_family(&a, &b), OP_EQ, 1); + tt_int_op(router_addrs_in_same_network(&a, &b), OP_EQ, 1); tor_addr_parse(&a, "8.8.8.8"); tor_addr_parse(&b, "1.1.1.1"); - tt_int_op(addrs_in_same_network_family(&a, &b), OP_EQ, 0); + tt_int_op(router_addrs_in_same_network(&a, &b), OP_EQ, 0); tor_addr_parse(&a, "8.8.8.8"); tor_addr_parse(&b, "2001:4860:4860::8844"); - tt_int_op(addrs_in_same_network_family(&a, &b), OP_EQ, 0); + tt_int_op(router_addrs_in_same_network(&a, &b), OP_EQ, 0); tor_addr_parse(&a, "2001:4860:4860::8888"); tor_addr_parse(&b, "2001:4860:4860::8844"); - tt_int_op(addrs_in_same_network_family(&a, &b), OP_EQ, 1); + tt_int_op(router_addrs_in_same_network(&a, &b), OP_EQ, 1); tor_addr_parse(&a, "2001:4860:4860::8888"); tor_addr_parse(&b, "2001:470:20::2"); - tt_int_op(addrs_in_same_network_family(&a, &b), OP_EQ, 0); + tt_int_op(router_addrs_in_same_network(&a, &b), OP_EQ, 0); done: return; @@ -1194,16 +1194,14 @@ helper_free_mock_node(node_t *node) tor_free(node); } -#define NODE_SET_IPV4(node, ipv4_addr, ipv4_port) { \ - tor_addr_t addr; \ - tor_addr_parse(&addr, ipv4_addr); \ - node->ri->addr = tor_addr_to_ipv4h(&addr); \ - node->ri->or_port = ipv4_port; \ +#define NODE_SET_IPV4(node, ipv4_addr_str, ipv4_port) { \ + tor_addr_parse(&(node)->ri->ipv4_addr, ipv4_addr_str); \ + node->ri->ipv4_orport = ipv4_port; \ } #define NODE_CLEAR_IPV4(node) { \ - node->ri->addr = 0; \ - node->ri->or_port = 0; \ + tor_addr_make_unspec(&node->ri->ipv4_addr); \ + node->ri->ipv4_orport = 0; \ } #define NODE_SET_IPV6(node, ipv6_addr_str, ipv6_port) { \ @@ -1260,9 +1258,7 @@ mock_get_options(void) #define TEST_ROUTER_VALID_ADDRESS_HELPER(ipv4_addr_str, ipv6_addr_str, rv) \ STMT_BEGIN \ ri = tor_malloc_zero(sizeof(routerinfo_t)); \ - tor_addr_t addr; \ - tor_addr_parse(&addr, (ipv4_addr_str)); \ - ri->addr = tor_addr_to_ipv4h(&addr); \ + tor_addr_parse(&ri->ipv4_addr, (ipv4_addr_str)); \ tor_addr_parse(&ri->ipv6_addr, (ipv6_addr_str)); \ tt_int_op(dirserv_router_has_valid_address(ri), OP_EQ, (rv)); \ tor_free(ri); \ @@ -1320,7 +1316,7 @@ test_address_dirserv_router_addr_private(void *opt_dir_allow_private) /* IPv6 null succeeds, because IPv4 is not internal */ { ri = tor_malloc_zero(sizeof(routerinfo_t)); - ri->addr = 16777217; /* 1.0.0.1 */ + tor_addr_parse(&ri->ipv4_addr, "1.0.0.1"); tt_int_op(dirserv_router_has_valid_address(ri), OP_EQ, 0); tor_free(ri); } diff --git a/src/test/test_address_set.c b/src/test/test_address_set.c index 829ecd79e8..3fee322c47 100644 --- a/src/test/test_address_set.c +++ b/src/test/test_address_set.c @@ -114,7 +114,6 @@ test_nodelist(void *arg) tor_addr_t addr_v4, addr_v6, dummy_addr; tor_addr_parse(&addr_v4, "42.42.42.42"); - uint32_t ipv4h = tor_addr_to_ipv4h(&addr_v4); tor_addr_parse(&addr_v6, "1:2:3:4::"); memset(&dummy_addr, 'A', sizeof(dummy_addr)); @@ -148,9 +147,9 @@ test_nodelist(void *arg) memcpy(rs->descriptor_digest, md->digest, DIGEST256_LEN); /* Setup the rs, ri and md addresses. */ - rs->addr = ipv4h; + tor_addr_copy(&rs->ipv4_addr, &addr_v4); tor_addr_parse(&rs->ipv6_addr, "1:2:3:4::"); - ri->addr = ipv4h; + tor_addr_copy(&ri->ipv4_addr, &addr_v4); tor_addr_parse(&ri->ipv6_addr, "1:2:3:4::"); tor_addr_parse(&md->ipv6_addr, "1:2:3:4::"); diff --git a/src/test/test_bridges.c b/src/test/test_bridges.c index f1624a529d..1942a8cb89 100644 --- a/src/test/test_bridges.c +++ b/src/test/test_bridges.c @@ -592,8 +592,12 @@ test_bridges_get_transport_by_bridge_addrport(void *arg) static void test_bridges_node_is_a_configured_bridge(void *arg) { - routerinfo_t ri_ipv4 = { .addr = 0x06060606, .or_port = 6666 }; - routerstatus_t rs_ipv4 = { .addr = 0x06060606, .or_port = 6666 }; + + routerinfo_t ri_ipv4 = { .ipv4_orport = 6666 }; + tor_addr_parse(&ri_ipv4.ipv4_addr, "6.6.6.6"); + + routerstatus_t rs_ipv4 = { .ipv4_orport = 6666 }; + tor_addr_parse(&rs_ipv4.ipv4_addr, "6.6.6.6"); routerinfo_t ri_ipv6 = { .ipv6_orport = 6666 }; tor_addr_parse(&(ri_ipv6.ipv6_addr), @@ -632,8 +636,8 @@ test_bridges_node_is_a_configured_bridge(void *arg) /* It won't match bridge1, though, since bridge1 has a digest, and this isn't it! */ - node_ri_ipv4.ri->addr = 0x06060607; - node_ri_ipv4.ri->or_port = 6667; + tor_addr_parse(&node_ri_ipv4.ri->ipv4_addr, "6.6.6.7"); + node_ri_ipv4.ri->ipv4_orport = 6667; tt_assert(! node_is_a_configured_bridge(&node_ri_ipv4)); /* If we set the fingerprint right, though, it will match. */ base16_decode(node_ri_ipv4.identity, DIGEST_LEN, diff --git a/src/test/test_bwmgt.c b/src/test/test_bwmgt.c index 117783cafc..4cf83e45d0 100644 --- a/src/test/test_bwmgt.c +++ b/src/test/test_bwmgt.c @@ -317,8 +317,8 @@ test_bwmgt_dir_conn_global_write_low(void *arg) memcpy(rs->descriptor_digest, md->digest, DIGEST256_LEN); /* Set IP address. */ - rs->addr = tor_addr_to_ipv4h(&relay_addr); - ri->addr = rs->addr; + tor_addr_copy(&rs->ipv4_addr, &relay_addr); + tor_addr_copy(&ri->ipv4_addr, &rs->ipv4_addr); /* Add the rs to the consensus becoming a node_t. */ smartlist_add(dummy_ns->routerstatus_list, rs); diff --git a/src/test/test_channel.c b/src/test/test_channel.c index 849cc497fc..c7d4343ede 100644 --- a/src/test/test_channel.c +++ b/src/test/test_channel.c @@ -16,6 +16,10 @@ /* For packed_cell stuff */ #define RELAY_PRIVATE #include "core/or/relay.h" +/* For channel_tls_t object and private functions. */ +#define CHANNEL_OBJECT_PRIVATE +#define CHANNELTLS_PRIVATE +#include "core/or/channeltls.h" /* For init/free stuff */ #include "core/or/scheduler.h" #include "feature/nodelist/networkstatus.h" @@ -25,6 +29,8 @@ #include "core/or/origin_circuit_st.h" #include "feature/nodelist/routerstatus_st.h" #include "core/or/var_cell_st.h" +#include "core/or/or_connection_st.h" +#include "lib/net/inaddr.h" /* Test suite stuff */ #include "test/log_test_helpers.h" @@ -157,16 +163,23 @@ chan_test_finish_close(channel_t *ch) } static const char * -chan_test_get_remote_descr(channel_t *ch, int flags) +chan_test_describe_peer(const channel_t *ch) { tt_assert(ch); - tt_int_op(flags & ~(GRD_FLAG_ORIGINAL | GRD_FLAG_ADDR_ONLY), OP_EQ, 0); done: return "Fake channel for unit tests; no real endpoint"; } static int +chan_test_get_remote_addr(const channel_t *ch, tor_addr_t *out) +{ + (void)ch; + tor_addr_from_ipv4h(out, 0x7f000001); + return 1; +} + +static int chan_test_num_cells_writeable(channel_t *ch) { tt_assert(ch); @@ -262,7 +275,8 @@ new_fake_channel(void) chan->close = chan_test_close; chan->num_cells_writeable = chan_test_num_cells_writeable; - chan->get_remote_descr = chan_test_get_remote_descr; + chan->describe_peer = chan_test_describe_peer; + chan->get_remote_addr = chan_test_get_remote_addr; chan->write_packed_cell = chan_test_write_packed_cell; chan->write_var_cell = chan_test_write_var_cell; chan->state = CHANNEL_STATE_OPEN; @@ -1537,6 +1551,54 @@ test_channel_listener(void *arg) channel_free_all(); } +#define TEST_SETUP_MATCHES_ADDR(orcon, addr, src, rv) STMT_BEGIN \ + rv = tor_inet_pton(addr.family, src, &addr.addr); \ + tt_int_op(rv, OP_EQ, 1); \ + orcon->base_.addr = addr; \ + STMT_END; + +#define TEST_MATCHES_ADDR(chan, addr4, addr6, rv, exp) STMT_BEGIN \ + rv = channel_matches_target_addr_for_extend(chan, addr4, addr6); \ + tt_int_op(rv, OP_EQ, exp); \ + STMT_END; + +static void +test_channel_matches_target_addr_for_extend(void *arg) +{ + (void) arg; + + channel_tls_t *tlschan = tor_malloc_zero(sizeof(*tlschan)); + or_connection_t *orcon = tor_malloc_zero(sizeof(*orcon)); + channel_t *chan = &(tlschan->base_); + tor_addr_t addr; + int rv; + + tlschan->conn = orcon; + channel_tls_common_init(tlschan); + + /* Test for IPv4 addresses. */ + addr.family = AF_INET; + TEST_SETUP_MATCHES_ADDR(orcon, addr, "1.2.3.4", rv); + TEST_MATCHES_ADDR(chan, &addr, NULL, rv, 1); + + tor_inet_pton(addr.family, "2.5.3.4", &addr.addr); + TEST_MATCHES_ADDR(chan, &addr, NULL, rv, 0); + + /* Test for IPv6 addresses. */ + addr.family = AF_INET6; + TEST_SETUP_MATCHES_ADDR(orcon, addr, "3:4:7:1:9:8:09:10", rv); + TEST_MATCHES_ADDR(chan, NULL, &addr, rv, 1); + + tor_inet_pton(addr.family, "::", &addr.addr); + TEST_MATCHES_ADDR(chan, NULL, &addr, rv, 0); + + done: + circuitmux_clear_policy(chan->cmux); + circuitmux_free(chan->cmux); + tor_free(orcon); + tor_free(tlschan); +} + struct testcase_t channel_tests[] = { { "inbound_cell", test_channel_inbound_cell, TT_FORK, NULL, NULL }, @@ -1558,5 +1620,7 @@ struct testcase_t channel_tests[] = { NULL, NULL }, { "listener", test_channel_listener, TT_FORK, NULL, NULL }, + { "matches_target", test_channel_matches_target_addr_for_extend, TT_FORK, + NULL, NULL }, END_OF_TESTCASES }; diff --git a/src/test/test_channeltls.c b/src/test/test_channeltls.c index f4f5cb447e..0227779e8b 100644 --- a/src/test/test_channeltls.c +++ b/src/test/test_channeltls.c @@ -38,13 +38,13 @@ static or_connection_t * tlschan_connection_or_connect_mock( const char *digest, const ed25519_public_key_t *ed_id, channel_tls_t *tlschan); -static int tlschan_is_local_addr_mock(const tor_addr_t *addr); +static bool tlschan_resolved_addr_is_local_mock(const tor_addr_t *addr); /* Fake close method */ static void tlschan_fake_close_method(channel_t *chan); /* Flags controlling behavior of channeltls unit test mocks */ -static int tlschan_local = 0; +static bool tlschan_local = false; static const buf_t * tlschan_buf_datalen_mock_target = NULL; static size_t tlschan_buf_datalen_mock_size = 0; @@ -67,9 +67,9 @@ test_channeltls_create(void *arg) test_addr.addr.in_addr.s_addr = htonl(0x01020304); /* For this test we always want the address to be treated as non-local */ - tlschan_local = 0; - /* Install is_local_addr() mock */ - MOCK(is_local_addr, tlschan_is_local_addr_mock); + tlschan_local = false; + /* Install is_local_to_resolve_addr() mock */ + MOCK(is_local_to_resolve_addr, tlschan_resolved_addr_is_local_mock); /* Install mock for connection_or_connect() */ MOCK(connection_or_connect, tlschan_connection_or_connect_mock); @@ -92,7 +92,7 @@ test_channeltls_create(void *arg) } UNMOCK(connection_or_connect); - UNMOCK(is_local_addr); + UNMOCK(is_local_to_resolve_addr); return; } @@ -116,9 +116,9 @@ test_channeltls_num_bytes_queued(void *arg) test_addr.addr.in_addr.s_addr = htonl(0x01020304); /* For this test we always want the address to be treated as non-local */ - tlschan_local = 0; - /* Install is_local_addr() mock */ - MOCK(is_local_addr, tlschan_is_local_addr_mock); + tlschan_local = false; + /* Install is_local_to_resolve_addr() mock */ + MOCK(is_local_to_resolve_addr, tlschan_resolved_addr_is_local_mock); /* Install mock for connection_or_connect() */ MOCK(connection_or_connect, tlschan_connection_or_connect_mock); @@ -178,7 +178,7 @@ test_channeltls_num_bytes_queued(void *arg) } UNMOCK(connection_or_connect); - UNMOCK(is_local_addr); + UNMOCK(is_local_to_resolve_addr); return; } @@ -201,9 +201,9 @@ test_channeltls_overhead_estimate(void *arg) test_addr.addr.in_addr.s_addr = htonl(0x01020304); /* For this test we always want the address to be treated as non-local */ - tlschan_local = 0; - /* Install is_local_addr() mock */ - MOCK(is_local_addr, tlschan_is_local_addr_mock); + tlschan_local = false; + /* Install is_local_to_resolve_addr() mock */ + MOCK(is_local_to_resolve_addr, tlschan_resolved_addr_is_local_mock); /* Install mock for connection_or_connect() */ MOCK(connection_or_connect, tlschan_connection_or_connect_mock); @@ -252,7 +252,7 @@ test_channeltls_overhead_estimate(void *arg) } UNMOCK(connection_or_connect); - UNMOCK(is_local_addr); + UNMOCK(is_local_to_resolve_addr); return; } @@ -293,7 +293,7 @@ tlschan_connection_or_connect_mock(const tor_addr_t *addr, result->base_.port = port; memcpy(result->identity_digest, digest, DIGEST_LEN); result->chan = tlschan; - memcpy(&(result->real_addr), addr, sizeof(tor_addr_t)); + memcpy(&result->base_.addr, addr, sizeof(tor_addr_t)); result->tls = (tor_tls_t *)((void *)(&fake_tortls)); done: @@ -321,8 +321,8 @@ tlschan_fake_close_method(channel_t *chan) return; } -static int -tlschan_is_local_addr_mock(const tor_addr_t *addr) +static bool +tlschan_resolved_addr_is_local_mock(const tor_addr_t *addr) { tt_ptr_op(addr, OP_NE, NULL); diff --git a/src/test/test_circuitbuild.c b/src/test/test_circuitbuild.c index 03fd176ead..74824a1bc1 100644 --- a/src/test/test_circuitbuild.c +++ b/src/test/test_circuitbuild.c @@ -19,6 +19,7 @@ #include "core/or/channel.h" #include "core/or/circuitbuild.h" #include "core/or/circuitlist.h" +#include "core/or/circuituse.h" #include "core/or/onion.h" #include "core/or/cell_st.h" @@ -29,11 +30,13 @@ #include "feature/client/entrynodes.h" #include "feature/nodelist/nodelist.h" +#include "feature/nodelist/node_select.h" #include "feature/relay/circuitbuild_relay.h" #include "feature/relay/router.h" #include "feature/relay/routermode.h" #include "feature/nodelist/node_st.h" +#include "feature/nodelist/routerinfo_st.h" /* Dummy nodes smartlist for testing */ static smartlist_t dummy_nodes; @@ -279,10 +282,10 @@ mock_node_get_by_id(const char *identity_digest) return mocked_node; } -static int mocked_supports_ed25519_link_authentication = 0; -static int +static bool mocked_supports_ed25519_link_authentication = 0; +static bool mock_node_supports_ed25519_link_authentication(const node_t *node, - int compatible_with_us) + bool compatible_with_us) { (void)node; (void)compatible_with_us; @@ -821,6 +824,75 @@ test_circuit_extend_lspec_valid(void *arg) tor_free(p_chan); } +#define NODE_SET_IPV4(node, ipv4_addr_str, ipv4_port) { \ + tor_addr_parse(&node->ri->ipv4_addr, ipv4_addr_str); \ + node->ri->ipv4_orport = ipv4_port; \ + } + +#define NODE_CLEAR_IPV4(node) { \ + tor_addr_make_unspec(&node->ri->ipv4_addr); \ + node->ri->ipv4_orport = 0; \ + } + +#define NODE_SET_IPV6(node, ipv6_addr_str, ipv6_port) { \ + tor_addr_parse(&node->ri->ipv6_addr, ipv6_addr_str); \ + node->ri->ipv6_orport = ipv6_port; \ + } + +/* Test the different cases in circuit_extend_add_ed25519_helper(). */ +static void +test_circuit_extend_add_ip(void *arg) +{ + (void) arg; + tor_addr_t ipv4_tmp; + extend_cell_t *ec = tor_malloc_zero(sizeof(extend_cell_t)); + extend_cell_t *old_ec = tor_malloc_zero(sizeof(extend_cell_t)); + + node_t *fake_node = tor_malloc_zero(sizeof(node_t)); + routerinfo_t *ri = tor_malloc_zero(sizeof(routerinfo_t)); + + MOCK(node_get_by_id, mock_node_get_by_id); + + /* Set up the fake variables for the IPv4 test */ + fake_node->ri = ri; + mocked_node = fake_node; + memset(ec->node_id, 0xAA, sizeof(ec->node_id)); + memcpy(old_ec, ec, sizeof(extend_cell_t)); + NODE_SET_IPV4(fake_node, PUBLIC_IPV4, VALID_PORT); + + /* Do the IPv4 test */ + tt_int_op(circuit_extend_add_ipv4_helper(ec), OP_EQ, 0); + tor_addr_copy(&ipv4_tmp, &fake_node->ri->ipv4_addr); + /* The IPv4 should match */ + tt_int_op(tor_addr_compare(&ec->orport_ipv4.addr, &ipv4_tmp, CMP_SEMANTIC), + OP_EQ, 0); + tt_int_op(ec->orport_ipv4.port, OP_EQ, VALID_PORT); + + /* Set up the fake variables for the IPv6 test */ + memcpy(ec, old_ec, sizeof(extend_cell_t)); + NODE_CLEAR_IPV4(fake_node); + NODE_SET_IPV6(fake_node, PUBLIC_IPV6, VALID_PORT); + + /* Do the IPv6 test */ + tt_int_op(circuit_extend_add_ipv6_helper(ec), OP_EQ, 0); + /* The IPv6 should match */ + tt_int_op(tor_addr_compare(&ec->orport_ipv6.addr, &fake_node->ri->ipv6_addr, + CMP_SEMANTIC), OP_EQ, 0); + tt_int_op(ec->orport_ipv6.port, OP_EQ, VALID_PORT); + + /* Cleanup */ + mocked_node = NULL; + + done: + UNMOCK(node_get_by_id); + + tor_free(ec); + tor_free(old_ec); + + tor_free(ri); + tor_free(fake_node); +} + static bool can_extend_over_ipv6_result = false; static int mock_router_can_extend_over_ipv6_calls = 0; static bool @@ -927,15 +999,9 @@ mock_circuit_mark_for_close_(circuit_t *circ, int reason, static int mock_channel_connect_calls = 0; static channel_t *mock_channel_connect_nchan = NULL; static channel_t * -mock_channel_connect_for_circuit(const tor_addr_t *addr, - uint16_t port, - const char *id_digest, - const struct ed25519_public_key_t *ed_id) +mock_channel_connect_for_circuit(const extend_info_t *ei) { - (void)addr; - (void)port; - (void)id_digest; - (void)ed_id; + (void)ei; mock_channel_connect_calls++; return mock_channel_connect_nchan; } @@ -1176,6 +1242,8 @@ mock_channel_get_canonical_remote_descr(channel_t *chan) return "mock_channel_get_canonical_remote_descr()"; } +/* Should mock_circuit_deliver_create_cell() expect a direct connection? */ +static bool mock_circuit_deliver_create_cell_expect_direct = false; static int mock_circuit_deliver_create_cell_calls = 0; static int mock_circuit_deliver_create_cell_result = 0; static int @@ -1188,10 +1256,13 @@ mock_circuit_deliver_create_cell(circuit_t *circ, /* circuit_deliver_create_cell() requires non-NULL arguments, * but we only check circ and circ->n_chan here. */ tt_ptr_op(circ, OP_NE, NULL); - tt_ptr_op(circ->n_chan, OP_NE, NULL); + /* We expect n_chan for relayed cells. But should we also expect it for + * direct connections? */ + if (!mock_circuit_deliver_create_cell_expect_direct) + tt_ptr_op(circ->n_chan, OP_NE, NULL); /* We should only ever get relayed cells from extends */ - tt_int_op(relayed, OP_EQ, 1); + tt_int_op(relayed, OP_EQ, !mock_circuit_deliver_create_cell_expect_direct); mock_circuit_deliver_create_cell_calls++; return mock_circuit_deliver_create_cell_result; @@ -1215,7 +1286,7 @@ test_circuit_extend(void *arg) MOCK(server_mode, mock_server_mode); /* Mock a debug function, but otherwise ignore it */ - MOCK(channel_get_canonical_remote_descr, + MOCK(channel_describe_peer, mock_channel_get_canonical_remote_descr); setup_full_capture_of_logs(LOG_INFO); @@ -1352,6 +1423,7 @@ test_circuit_extend(void *arg) /* Mock circuit_deliver_create_cell(), so it doesn't crash */ mock_circuit_deliver_create_cell_calls = 0; + mock_circuit_deliver_create_cell_expect_direct = false; MOCK(circuit_deliver_create_cell, mock_circuit_deliver_create_cell); /* Test circuit established, re-using channel, successful delivery */ @@ -1407,7 +1479,7 @@ test_circuit_extend(void *arg) UNMOCK(server_mode); server = 0; - UNMOCK(channel_get_canonical_remote_descr); + UNMOCK(channel_describe_peer); UNMOCK(extend_cell_parse); memset(&mock_extend_cell_parse_cell_out, 0, @@ -1516,6 +1588,355 @@ test_onionskin_answer(void *arg) tor_free(or_circ); } +/* Test the different cases in origin_circuit_init(). */ +static void +test_origin_circuit_init(void *arg) +{ + (void)arg; + origin_circuit_t *origin_circ = NULL; + + /* Init with 0 purpose and 0 flags */ + origin_circ = origin_circuit_init(0, 0); + tt_int_op(origin_circ->base_.purpose, OP_EQ, 0); + tt_int_op(origin_circ->base_.state, OP_EQ, CIRCUIT_STATE_CHAN_WAIT); + tt_ptr_op(origin_circ->build_state, OP_NE, NULL); + tt_int_op(origin_circ->build_state->is_internal, OP_EQ, 0); + tt_int_op(origin_circ->build_state->is_ipv6_selftest, OP_EQ, 0); + tt_int_op(origin_circ->build_state->need_capacity, OP_EQ, 0); + tt_int_op(origin_circ->build_state->need_uptime, OP_EQ, 0); + tt_int_op(origin_circ->build_state->onehop_tunnel, OP_EQ, 0); + /* The circuits are automatically freed by the circuitlist. */ + + /* Init with a purpose */ + origin_circ = origin_circuit_init(CIRCUIT_PURPOSE_C_GENERAL, 0); + tt_int_op(origin_circ->base_.purpose, OP_EQ, CIRCUIT_PURPOSE_C_GENERAL); + + /* Init with each flag */ + origin_circ = origin_circuit_init(0, CIRCLAUNCH_IS_INTERNAL); + tt_ptr_op(origin_circ->build_state, OP_NE, NULL); + tt_int_op(origin_circ->build_state->is_internal, OP_EQ, 1); + tt_int_op(origin_circ->build_state->is_ipv6_selftest, OP_EQ, 0); + tt_int_op(origin_circ->build_state->need_capacity, OP_EQ, 0); + tt_int_op(origin_circ->build_state->need_uptime, OP_EQ, 0); + tt_int_op(origin_circ->build_state->onehop_tunnel, OP_EQ, 0); + + origin_circ = origin_circuit_init(0, CIRCLAUNCH_IS_IPV6_SELFTEST); + tt_ptr_op(origin_circ->build_state, OP_NE, NULL); + tt_int_op(origin_circ->build_state->is_internal, OP_EQ, 0); + tt_int_op(origin_circ->build_state->is_ipv6_selftest, OP_EQ, 1); + tt_int_op(origin_circ->build_state->need_capacity, OP_EQ, 0); + tt_int_op(origin_circ->build_state->need_uptime, OP_EQ, 0); + tt_int_op(origin_circ->build_state->onehop_tunnel, OP_EQ, 0); + + origin_circ = origin_circuit_init(0, CIRCLAUNCH_NEED_CAPACITY); + tt_ptr_op(origin_circ->build_state, OP_NE, NULL); + tt_int_op(origin_circ->build_state->is_internal, OP_EQ, 0); + tt_int_op(origin_circ->build_state->is_ipv6_selftest, OP_EQ, 0); + tt_int_op(origin_circ->build_state->need_capacity, OP_EQ, 1); + tt_int_op(origin_circ->build_state->need_uptime, OP_EQ, 0); + tt_int_op(origin_circ->build_state->onehop_tunnel, OP_EQ, 0); + + origin_circ = origin_circuit_init(0, CIRCLAUNCH_NEED_UPTIME); + tt_ptr_op(origin_circ->build_state, OP_NE, NULL); + tt_int_op(origin_circ->build_state->is_internal, OP_EQ, 0); + tt_int_op(origin_circ->build_state->is_ipv6_selftest, OP_EQ, 0); + tt_int_op(origin_circ->build_state->need_capacity, OP_EQ, 0); + tt_int_op(origin_circ->build_state->need_uptime, OP_EQ, 1); + tt_int_op(origin_circ->build_state->onehop_tunnel, OP_EQ, 0); + + origin_circ = origin_circuit_init(0, CIRCLAUNCH_ONEHOP_TUNNEL); + tt_ptr_op(origin_circ->build_state, OP_NE, NULL); + tt_int_op(origin_circ->build_state->is_internal, OP_EQ, 0); + tt_int_op(origin_circ->build_state->is_ipv6_selftest, OP_EQ, 0); + tt_int_op(origin_circ->build_state->need_capacity, OP_EQ, 0); + tt_int_op(origin_circ->build_state->need_uptime, OP_EQ, 0); + tt_int_op(origin_circ->build_state->onehop_tunnel, OP_EQ, 1); + + done: + /* The circuits are automatically freed by the circuitlist. */ + ; +} + +/* Test the different cases in circuit_send_next_onion_skin(). */ +static void +test_circuit_send_next_onion_skin(void *arg) +{ + (void)arg; + origin_circuit_t *origin_circ = NULL; + struct timeval circ_start_time; + memset(&circ_start_time, 0, sizeof(circ_start_time)); + + extend_info_t fakehop; + memset(&fakehop, 0, sizeof(fakehop)); + extend_info_t *single_fakehop = &fakehop; + extend_info_t *multi_fakehop[DEFAULT_ROUTE_LEN] = {&fakehop, + &fakehop, + &fakehop}; + + extend_info_t ipv6_hop; + memset(&ipv6_hop, 0, sizeof(ipv6_hop)); + tor_addr_parse(&ipv6_hop.orports[0].addr, "1::2"); + extend_info_t *multi_ipv6_hop[DEFAULT_ROUTE_LEN] = {&ipv6_hop, + &ipv6_hop, + &ipv6_hop}; + + extend_info_t ipv4_hop; + memset(&ipv4_hop, 0, sizeof(ipv4_hop)); + tor_addr_from_ipv4h(&ipv4_hop.orports[0].addr, 0x20304050); + extend_info_t *multi_ipv4_hop[DEFAULT_ROUTE_LEN] = {&ipv4_hop, + &ipv4_hop, + &ipv4_hop}; + + mock_circuit_deliver_create_cell_expect_direct = false; + MOCK(circuit_deliver_create_cell, mock_circuit_deliver_create_cell); + server = 0; + MOCK(server_mode, mock_server_mode); + + /* Try a direct connection, and succeed on a client */ + server = 0; + origin_circ = new_test_origin_circuit(false, + circ_start_time, + 1, + &single_fakehop); + tt_ptr_op(origin_circ, OP_NE, NULL); + /* Skip some of the multi-hop checks */ + origin_circ->build_state->onehop_tunnel = 1; + /* This is a direct connection */ + mock_circuit_deliver_create_cell_expect_direct = true; + tt_int_op(circuit_send_next_onion_skin(origin_circ), OP_EQ, 0); + /* The circuits are automatically freed by the circuitlist. */ + + /* Try a direct connection, and succeed on a server */ + server = 1; + origin_circ = new_test_origin_circuit(false, + circ_start_time, + 1, + &single_fakehop); + tt_ptr_op(origin_circ, OP_NE, NULL); + origin_circ->build_state->onehop_tunnel = 1; + mock_circuit_deliver_create_cell_expect_direct = true; + tt_int_op(circuit_send_next_onion_skin(origin_circ), OP_EQ, 0); + + /* Start capturing bugs */ + setup_full_capture_of_logs(LOG_WARN); + tor_capture_bugs_(1); + + /* Try an extend, but fail the client valid address family check */ + server = 0; + origin_circ = new_test_origin_circuit(true, + circ_start_time, + ARRAY_LENGTH(multi_fakehop), + multi_fakehop); + tt_ptr_op(origin_circ, OP_NE, NULL); + /* Fix the state */ + origin_circ->base_.state = 0; + /* This is an indirect connection */ + mock_circuit_deliver_create_cell_expect_direct = false; + /* Fail because the address family is invalid */ + tt_int_op(circuit_send_next_onion_skin(origin_circ), OP_EQ, + -END_CIRC_REASON_INTERNAL); + expect_log_msg("No supported address family found in extend_info.\n"); + mock_clean_saved_logs(); + + /* Try an extend, but fail the server valid address check */ + server = 1; + origin_circ = new_test_origin_circuit(true, + circ_start_time, + ARRAY_LENGTH(multi_fakehop), + multi_fakehop); + tt_ptr_op(origin_circ, OP_NE, NULL); + origin_circ->base_.state = 0; + mock_circuit_deliver_create_cell_expect_direct = false; + tt_int_op(circuit_send_next_onion_skin(origin_circ), OP_EQ, + -END_CIRC_REASON_INTERNAL); + expect_log_msg("No supported address family found in extend_info.\n"); + mock_clean_saved_logs(); + + /* Try an extend, but fail in the client code, with an IPv6 address */ + server = 0; + origin_circ = new_test_origin_circuit(true, + circ_start_time, + ARRAY_LENGTH(multi_ipv6_hop), + multi_ipv6_hop); + tt_ptr_op(origin_circ, OP_NE, NULL); + origin_circ->base_.state = 0; + mock_circuit_deliver_create_cell_expect_direct = false; + tt_int_op(circuit_send_next_onion_skin(origin_circ), OP_EQ, + -END_CIRC_REASON_INTERNAL); + expect_log_msg("No supported address family found in extend_info.\n"); + mock_clean_saved_logs(); + + /* Stop capturing bugs, but keep capturing logs */ + tor_end_capture_bugs_(); + + /* Try an extend, pass the client IPv4 check, but fail later */ + server = 0; + origin_circ = new_test_origin_circuit(true, + circ_start_time, + ARRAY_LENGTH(multi_ipv4_hop), + multi_ipv4_hop); + tt_ptr_op(origin_circ, OP_NE, NULL); + origin_circ->base_.state = 0; + mock_circuit_deliver_create_cell_expect_direct = false; + /* Fail because the circuit data is invalid */ + tt_int_op(circuit_send_next_onion_skin(origin_circ), OP_EQ, + -END_CIRC_REASON_INTERNAL); + expect_log_msg("onion_skin_create failed.\n"); + mock_clean_saved_logs(); + + /* Try an extend, pass the server IPv4 check, but fail later */ + server = 1; + origin_circ = new_test_origin_circuit(true, + circ_start_time, + ARRAY_LENGTH(multi_ipv4_hop), + multi_ipv4_hop); + tt_ptr_op(origin_circ, OP_NE, NULL); + origin_circ->base_.state = 0; + mock_circuit_deliver_create_cell_expect_direct = false; + tt_int_op(circuit_send_next_onion_skin(origin_circ), OP_EQ, + -END_CIRC_REASON_INTERNAL); + expect_log_msg("onion_skin_create failed.\n"); + mock_clean_saved_logs(); + + /* Try an extend, pass the server IPv6 check, but fail later */ + server = 1; + origin_circ = new_test_origin_circuit(true, + circ_start_time, + ARRAY_LENGTH(multi_ipv6_hop), + multi_ipv6_hop); + tt_ptr_op(origin_circ, OP_NE, NULL); + origin_circ->base_.state = 0; + mock_circuit_deliver_create_cell_expect_direct = false; + tt_int_op(circuit_send_next_onion_skin(origin_circ), OP_EQ, + -END_CIRC_REASON_INTERNAL); + expect_log_msg("onion_skin_create failed.\n"); + mock_clean_saved_logs(); + + /* Things we're not testing right now: + * - the addresses in the extend cell inside + * circuit_send_intermediate_onion_skin() matches the address in the + * supplied extend_info. + * - valid circuit data. + * - actually extending the circuit to each hop. */ + + done: + tor_end_capture_bugs_(); + mock_clean_saved_logs(); + teardown_capture_of_logs(); + + UNMOCK(circuit_deliver_create_cell); + UNMOCK(server_mode); + server = 0; + + /* The circuits are automatically freed by the circuitlist. */ +} + +/* Test the different cases in cpath_build_state_to_crn_flags(). */ +static void +test_cpath_build_state_to_crn_flags(void *arg) +{ + (void)arg; + + cpath_build_state_t state; + memset(&state, 0, sizeof(state)); + + tt_int_op(cpath_build_state_to_crn_flags(&state), OP_EQ, + 0); + + memset(&state, 0, sizeof(state)); + state.need_uptime = 1; + tt_int_op(cpath_build_state_to_crn_flags(&state), OP_EQ, + CRN_NEED_UPTIME); + + memset(&state, 0, sizeof(state)); + state.need_capacity = 1; + tt_int_op(cpath_build_state_to_crn_flags(&state), OP_EQ, + CRN_NEED_CAPACITY); + + memset(&state, 0, sizeof(state)); + state.need_capacity = 1; + state.need_uptime = 1; + tt_int_op(cpath_build_state_to_crn_flags(&state), OP_EQ, + CRN_NEED_CAPACITY | CRN_NEED_UPTIME); + + /* Check that no other flags are handled */ + memset(&state, 0xff, sizeof(state)); + tt_int_op(cpath_build_state_to_crn_flags(&state), OP_EQ, + CRN_NEED_CAPACITY | CRN_NEED_UPTIME); + + done: + ; +} + +/* Test the different cases in cpath_build_state_to_crn_ipv6_extend_flag(). */ +static void +test_cpath_build_state_to_crn_ipv6_extend_flag(void *arg) +{ + (void)arg; + + cpath_build_state_t state; + + memset(&state, 0, sizeof(state)); + state.desired_path_len = DEFAULT_ROUTE_LEN; + tt_int_op(cpath_build_state_to_crn_ipv6_extend_flag(&state, 0), OP_EQ, + 0); + + /* Pass the state flag check, but not the length check */ + memset(&state, 0, sizeof(state)); + state.desired_path_len = DEFAULT_ROUTE_LEN; + state.is_ipv6_selftest = 1; + tt_int_op(cpath_build_state_to_crn_ipv6_extend_flag(&state, 0), OP_EQ, + 0); + + /* Pass the length check, but not the state flag check */ + memset(&state, 0, sizeof(state)); + state.desired_path_len = DEFAULT_ROUTE_LEN; + tt_int_op( + cpath_build_state_to_crn_ipv6_extend_flag(&state, + DEFAULT_ROUTE_LEN - 2), + OP_EQ, 0); + + /* Pass both checks */ + memset(&state, 0, sizeof(state)); + state.desired_path_len = DEFAULT_ROUTE_LEN; + state.is_ipv6_selftest = 1; + tt_int_op( + cpath_build_state_to_crn_ipv6_extend_flag(&state, + DEFAULT_ROUTE_LEN - 2), + OP_EQ, CRN_INITIATE_IPV6_EXTEND); + + /* Check that no other flags are handled */ + memset(&state, 0xff, sizeof(state)); + state.desired_path_len = INT_MAX; + tt_int_op(cpath_build_state_to_crn_ipv6_extend_flag(&state, INT_MAX), OP_EQ, + 0); + +#ifndef ALL_BUGS_ARE_FATAL + /* Start capturing bugs */ + setup_full_capture_of_logs(LOG_INFO); + tor_capture_bugs_(1); + + /* Now test the single hop circuit case */ +#define SINGLE_HOP_ROUTE_LEN 1 + memset(&state, 0, sizeof(state)); + state.desired_path_len = SINGLE_HOP_ROUTE_LEN; + state.is_ipv6_selftest = 1; + tt_int_op( + cpath_build_state_to_crn_ipv6_extend_flag(&state, + SINGLE_HOP_ROUTE_LEN - 2), + OP_EQ, 0); + tt_int_op(smartlist_len(tor_get_captured_bug_log_()), OP_EQ, 1); + tt_str_op(smartlist_get(tor_get_captured_bug_log_(), 0), OP_EQ, + "!(ASSERT_PREDICT_UNLIKELY_(state->desired_path_len < 2))"); + mock_clean_saved_logs(); +#endif /* !defined(ALL_BUGS_ARE_FATAL) */ + + done: + tor_end_capture_bugs_(); + mock_clean_saved_logs(); + teardown_capture_of_logs(); +} + #define TEST(name, flags, setup, cleanup) \ { #name, test_ ## name, flags, setup, cleanup } @@ -1525,6 +1946,9 @@ test_onionskin_answer(void *arg) #define TEST_CIRCUIT(name, flags) \ { #name, test_circuit_ ## name, flags, NULL, NULL } +#define TEST_CPATH(name, flags) \ + { #name, test_cpath_ ## name, flags, NULL, NULL } + #ifndef COCCI #define TEST_CIRCUIT_PASSTHROUGH(name, flags, arg) \ { #name "/" arg, test_circuit_ ## name, flags, \ @@ -1542,13 +1966,21 @@ struct testcase_t circuitbuild_tests[] = { TEST_CIRCUIT(extend_state_valid, TT_FORK), TEST_CIRCUIT(extend_add_ed25519, TT_FORK), TEST_CIRCUIT(extend_lspec_valid, TT_FORK), + TEST_CIRCUIT(extend_add_ip, TT_FORK), TEST_CIRCUIT(choose_ip_ap_for_extend, 0), + TEST_CIRCUIT_PASSTHROUGH(open_connection_for_extend, TT_FORK, "4"), TEST_CIRCUIT_PASSTHROUGH(open_connection_for_extend, TT_FORK, "6"), TEST_CIRCUIT_PASSTHROUGH(open_connection_for_extend, TT_FORK, "dual-stack"), + TEST_CIRCUIT(extend, TT_FORK), TEST(onionskin_answer, TT_FORK, NULL, NULL), + TEST(origin_circuit_init, TT_FORK, NULL, NULL), + TEST_CIRCUIT(send_next_onion_skin, TT_FORK), + TEST_CPATH(build_state_to_crn_flags, 0), + TEST_CPATH(build_state_to_crn_ipv6_extend_flag, TT_FORK), + END_OF_TESTCASES }; diff --git a/src/test/test_circuitpadding.c b/src/test/test_circuitpadding.c index cfb24c032c..c3346e2e03 100644 --- a/src/test/test_circuitpadding.c +++ b/src/test/test_circuitpadding.c @@ -23,6 +23,7 @@ #include "core/or/circuitbuild.h" #include "core/or/circuitpadding.h" #include "core/or/circuitpadding_machines.h" +#include "core/or/extendinfo.h" #include "core/mainloop/netstatus.h" #include "core/crypto/relay_crypto.h" #include "core/or/protover.h" @@ -1361,7 +1362,7 @@ test_circuitpadding_wronghop(void *arg) /* 5. Test that asking to stop the wrong machine does nothing */ circpad_negotiate_padding(TO_ORIGIN_CIRCUIT(client_side), - 255, 2, CIRCPAD_COMMAND_STOP); + 255, 2, CIRCPAD_COMMAND_STOP, 0); tt_ptr_op(client_side->padding_machine[0], OP_NE, NULL); tt_ptr_op(client_side->padding_info[0], OP_NE, NULL); tt_ptr_op(relay_side->padding_machine[0], OP_NE, NULL); @@ -1409,7 +1410,7 @@ test_circuitpadding_wronghop(void *arg) circpad_padding_negotiated(relay_side, CIRCPAD_MACHINE_CIRC_SETUP, CIRCPAD_COMMAND_START, - CIRCPAD_RESPONSE_OK); + CIRCPAD_RESPONSE_OK, 0); /* verify no padding was negotiated */ tt_ptr_op(relay_side->padding_machine[0], OP_EQ, NULL); @@ -1418,7 +1419,7 @@ test_circuitpadding_wronghop(void *arg) circpad_padding_negotiated(relay_side, CIRCPAD_MACHINE_CIRC_SETUP, CIRCPAD_COMMAND_START, - CIRCPAD_RESPONSE_ERR); + CIRCPAD_RESPONSE_ERR, 0); /* verify no padding was negotiated */ tt_ptr_op(relay_side->padding_machine[0], OP_EQ, NULL); @@ -1521,7 +1522,7 @@ test_circuitpadding_negotiation(void *arg) /* Force negotiate padding. */ circpad_negotiate_padding(TO_ORIGIN_CIRCUIT(client_side), CIRCPAD_MACHINE_CIRC_SETUP, - 2, CIRCPAD_COMMAND_START); + 2, CIRCPAD_COMMAND_START, 0); /* verify no padding was negotiated */ tt_ptr_op(relay_side->padding_machine[0], OP_EQ, NULL); @@ -1732,9 +1733,9 @@ helper_create_conditional_machines(void) add->conditions.requires_vanguards = 0; add->conditions.min_hops = 2; - add->conditions.state_mask = CIRCPAD_CIRC_BUILDING| + add->conditions.apply_state_mask = CIRCPAD_CIRC_BUILDING| CIRCPAD_CIRC_NO_STREAMS|CIRCPAD_CIRC_HAS_RELAY_EARLY; - add->conditions.purpose_mask = CIRCPAD_PURPOSE_ALL; + add->conditions.apply_purpose_mask = CIRCPAD_PURPOSE_ALL; circpad_register_padding_machine(add, origin_padding_machines); add = helper_create_conditional_machine(); @@ -1751,9 +1752,9 @@ helper_create_conditional_machines(void) add->conditions.requires_vanguards = 1; add->conditions.min_hops = 3; - add->conditions.state_mask = CIRCPAD_CIRC_OPENED| + add->conditions.apply_state_mask = CIRCPAD_CIRC_OPENED| CIRCPAD_CIRC_STREAMS|CIRCPAD_CIRC_HAS_NO_RELAY_EARLY; - add->conditions.purpose_mask = CIRCPAD_PURPOSE_ALL; + add->conditions.apply_purpose_mask = CIRCPAD_PURPOSE_ALL; circpad_register_padding_machine(add, origin_padding_machines); add = helper_create_conditional_machine(); @@ -2727,8 +2728,8 @@ helper_create_ender_machine(void) circ_client_machine.states[CIRCPAD_STATE_START]. next_state[CIRCPAD_EVENT_NONPADDING_RECV] = CIRCPAD_STATE_END; - circ_client_machine.conditions.state_mask = CIRCPAD_STATE_ALL; - circ_client_machine.conditions.purpose_mask = CIRCPAD_PURPOSE_ALL; + circ_client_machine.conditions.apply_state_mask = CIRCPAD_STATE_ALL; + circ_client_machine.conditions.apply_purpose_mask = CIRCPAD_PURPOSE_ALL; } static time_t mocked_timeofday; diff --git a/src/test/test_circuitstats.c b/src/test/test_circuitstats.c index e15dec5a01..00ca1b544c 100644 --- a/src/test/test_circuitstats.c +++ b/src/test/test_circuitstats.c @@ -17,18 +17,13 @@ #include "core/or/circuituse.h" #include "core/or/channel.h" -#include "core/or/cpath_build_state_st.h" #include "core/or/crypt_path_st.h" #include "core/or/extend_info_st.h" #include "core/or/origin_circuit_st.h" -void test_circuitstats_timeout(void *arg); -void test_circuitstats_hoplen(void *arg); -origin_circuit_t *subtest_fourhop_circuit(struct timeval, int); -origin_circuit_t *add_opened_threehop(void); -origin_circuit_t *build_unopened_fourhop(struct timeval); - -int cpath_append_hop(crypt_path_t **head_ptr, extend_info_t *choice); +static origin_circuit_t *add_opened_threehop(void); +static origin_circuit_t *build_unopened_fourhop(struct timeval); +static origin_circuit_t *subtest_fourhop_circuit(struct timeval, int); static int marked_for_close; /* Mock function because we are not trying to test the close circuit that does @@ -45,85 +40,71 @@ mock_circuit_mark_for_close(circuit_t *circ, int reason, int line, return; } -origin_circuit_t * +static origin_circuit_t * add_opened_threehop(void) { - origin_circuit_t *or_circ = origin_circuit_new(); + struct timeval circ_start_time; + memset(&circ_start_time, 0, sizeof(circ_start_time)); extend_info_t fakehop; memset(&fakehop, 0, sizeof(fakehop)); - - TO_CIRCUIT(or_circ)->purpose = CIRCUIT_PURPOSE_C_GENERAL; - - or_circ->build_state = tor_malloc_zero(sizeof(cpath_build_state_t)); - or_circ->build_state->desired_path_len = DEFAULT_ROUTE_LEN; - - cpath_append_hop(&or_circ->cpath, &fakehop); - cpath_append_hop(&or_circ->cpath, &fakehop); - cpath_append_hop(&or_circ->cpath, &fakehop); - - or_circ->has_opened = 1; - TO_CIRCUIT(or_circ)->state = CIRCUIT_STATE_OPEN; - TO_CIRCUIT(or_circ)->purpose = CIRCUIT_PURPOSE_C_GENERAL; - - return or_circ; + extend_info_t *fakehop_list[DEFAULT_ROUTE_LEN] = {&fakehop, + &fakehop, + &fakehop}; + + return new_test_origin_circuit(true, + circ_start_time, + DEFAULT_ROUTE_LEN, + fakehop_list); } -origin_circuit_t * +static origin_circuit_t * build_unopened_fourhop(struct timeval circ_start_time) { - origin_circuit_t *or_circ = origin_circuit_new(); - extend_info_t *fakehop = tor_malloc_zero(sizeof(extend_info_t)); - memset(fakehop, 0, sizeof(extend_info_t)); - - TO_CIRCUIT(or_circ)->purpose = CIRCUIT_PURPOSE_C_GENERAL; - TO_CIRCUIT(or_circ)->timestamp_began = circ_start_time; - TO_CIRCUIT(or_circ)->timestamp_created = circ_start_time; - - or_circ->build_state = tor_malloc_zero(sizeof(cpath_build_state_t)); - or_circ->build_state->desired_path_len = 4; - - cpath_append_hop(&or_circ->cpath, fakehop); - cpath_append_hop(&or_circ->cpath, fakehop); - cpath_append_hop(&or_circ->cpath, fakehop); - cpath_append_hop(&or_circ->cpath, fakehop); - - tor_free(fakehop); - - return or_circ; + extend_info_t fakehop; + memset(&fakehop, 0, sizeof(fakehop)); + extend_info_t *fakehop_list[4] = {&fakehop, + &fakehop, + &fakehop, + &fakehop}; + + return new_test_origin_circuit(false, + circ_start_time, + 4, + fakehop_list); } -origin_circuit_t * +static origin_circuit_t * subtest_fourhop_circuit(struct timeval circ_start_time, int should_timeout) { - origin_circuit_t *or_circ = build_unopened_fourhop(circ_start_time); + origin_circuit_t *origin_circ = build_unopened_fourhop(circ_start_time); // Now make them open one at a time and call // circuit_build_times_handle_completed_hop(); - or_circ->cpath->state = CPATH_STATE_OPEN; - circuit_build_times_handle_completed_hop(or_circ); + origin_circ->cpath->state = CPATH_STATE_OPEN; + circuit_build_times_handle_completed_hop(origin_circ); tt_int_op(get_circuit_build_times()->total_build_times, OP_EQ, 0); - or_circ->cpath->next->state = CPATH_STATE_OPEN; - circuit_build_times_handle_completed_hop(or_circ); + origin_circ->cpath->next->state = CPATH_STATE_OPEN; + circuit_build_times_handle_completed_hop(origin_circ); tt_int_op(get_circuit_build_times()->total_build_times, OP_EQ, 0); // Third hop: We should count it now. - or_circ->cpath->next->next->state = CPATH_STATE_OPEN; - circuit_build_times_handle_completed_hop(or_circ); + origin_circ->cpath->next->next->state = CPATH_STATE_OPEN; + circuit_build_times_handle_completed_hop(origin_circ); tt_int_op(get_circuit_build_times()->total_build_times, OP_EQ, !should_timeout); // 1 if counted, 0 otherwise // Fourth hop: Don't double count - or_circ->cpath->next->next->next->state = CPATH_STATE_OPEN; - circuit_build_times_handle_completed_hop(or_circ); + origin_circ->cpath->next->next->next->state = CPATH_STATE_OPEN; + circuit_build_times_handle_completed_hop(origin_circ); tt_int_op(get_circuit_build_times()->total_build_times, OP_EQ, !should_timeout); done: - return or_circ; + return origin_circ; } -void +static void test_circuitstats_hoplen(void *arg) { /* Plan: diff --git a/src/test/test_config.c b/src/test/test_config.c index 095eb24c49..121b51e925 100644 --- a/src/test/test_config.c +++ b/src/test/test_config.c @@ -53,6 +53,7 @@ #include "test/test_helpers.h" #include "test/resolve_test_helpers.h" +#include "test/log_test_helpers.h" #include "feature/dirclient/dir_server_st.h" #include "core/or/port_cfg_st.h" @@ -989,53 +990,72 @@ test_config_fix_my_family(void *arg) } static int n_hostname_01010101 = 0; +static const char *ret_addr_lookup_01010101[2] = { + "1.1.1.1", "0101::0101", +}; -/** This mock function is meant to replace tor_lookup_hostname(). +/** This mock function is meant to replace tor_addr_lookup(). * It answers with 1.1.1.1 as IP adddress that resulted from lookup. * This function increments <b>n_hostname_01010101</b> counter by one * every time it is called. */ static int -tor_lookup_hostname_01010101(const char *name, uint32_t *addr) +tor_addr_lookup_01010101(const char *name, uint16_t family, tor_addr_t *addr) { n_hostname_01010101++; - if (name && addr) { - *addr = ntohl(0x01010101); + if (family == AF_INET) { + if (name && addr) { + int ret = tor_addr_parse(addr, ret_addr_lookup_01010101[0]); + tt_int_op(ret, OP_EQ, family); + } + } else if (family == AF_INET6) { + if (name && addr) { + int ret = tor_addr_parse(addr, ret_addr_lookup_01010101[1]); + tt_int_op(ret, OP_EQ, family); + } } - + done: return 0; } static int n_hostname_localhost = 0; -/** This mock function is meant to replace tor_lookup_hostname(). +/** This mock function is meant to replace tor_addr_lookup(). * It answers with 127.0.0.1 as IP adddress that resulted from lookup. * This function increments <b>n_hostname_localhost</b> counter by one * every time it is called. */ static int -tor_lookup_hostname_localhost(const char *name, uint32_t *addr) +tor_addr_lookup_localhost(const char *name, uint16_t family, tor_addr_t *addr) { n_hostname_localhost++; - if (name && addr) { - *addr = 0x7f000001; + if (family == AF_INET) { + if (name && addr) { + tor_addr_from_ipv4h(addr, 0x7f000001); + } + } else if (family == AF_INET6) { + if (name && addr) { + int ret = tor_addr_parse(addr, "::1"); + tt_int_op(ret, OP_EQ, AF_INET6); + } } - + done: return 0; } static int n_hostname_failure = 0; -/** This mock function is meant to replace tor_lookup_hostname(). +/** This mock function is meant to replace tor_addr_lookup(). * It pretends to fail by returning -1 to caller. Also, this function * increments <b>n_hostname_failure</b> every time it is called. */ static int -tor_lookup_hostname_failure(const char *name, uint32_t *addr) +tor_addr_lookup_failure(const char *name, uint16_t family, tor_addr_t *addr) { (void)name; + (void)family; (void)addr; n_hostname_failure++; @@ -1043,6 +1063,46 @@ tor_lookup_hostname_failure(const char *name, uint32_t *addr) return -1; } +/** Mock function for tor_addr_lookup(). + * + * Depending on the given hostname and family, resolve either to IPv4 or IPv6. + * + * If the requested hostname family is not the same as the family provided, an + * error is returned. + * + * Possible hostnames: + * - www.torproject.org.v4 for IPv4 -> 1.1.1.1 + * - www.torproject.org.v6 for IPv6 -> [0101::0101] + */ +static int +tor_addr_lookup_mixed(const char *name, uint16_t family, tor_addr_t *addr) +{ + tt_assert(addr); + tt_assert(name); + + if (!strcmp(name, "www.torproject.org.v4")) { + if (family == AF_INET) { + tor_addr_from_ipv4h(addr, 0x01010101); + return 0; + } + /* Resolving AF_INET but the asked family is different. Failure. */ + return -1; + } + + if (!strcmp(name, "www.torproject.org.v6")) { + if (family == AF_INET6) { + int ret = tor_addr_parse(addr, "0101::0101"); + tt_int_op(ret, OP_EQ, AF_INET6); + return 0; + } + /* Resolving AF_INET6 but the asked family is not. Failure. */ + return -1; + } + + done: + return 0; +} + static int n_gethostname_replacement = 0; /** This mock function is meant to replace tor_gethostname(). It @@ -1097,29 +1157,39 @@ tor_gethostname_failure(char *name, size_t namelen) return -1; } -static int n_get_interface_address = 0; +static int n_get_interface_address6 = 0; +static sa_family_t last_address6_family; +static const char *ret_get_interface_address6_08080808[2] = { + "8.8.8.8", "0808::0808", +}; /** This mock function is meant to replace get_interface_address(). * It answers with address 8.8.8.8. This function increments * <b>n_get_interface_address</b> by one every time it is called. */ static int -get_interface_address_08080808(int severity, uint32_t *addr) +get_interface_address6_08080808(int severity, sa_family_t family, + tor_addr_t *addr) { (void)severity; - n_get_interface_address++; + n_get_interface_address6++; - if (addr) { - *addr = ntohl(0x08080808); + if (family == AF_INET) { + if (addr) { + int ret = tor_addr_parse(addr, ret_get_interface_address6_08080808[0]); + tt_int_op(ret, OP_EQ, AF_INET); + } + } else if (family == AF_INET6) { + if (addr) { + int ret = tor_addr_parse(addr, ret_get_interface_address6_08080808[1]); + tt_int_op(ret, OP_EQ, AF_INET6); + } } - + done: return 0; } -static int n_get_interface_address6 = 0; -static sa_family_t last_address6_family; - /** This mock function is meant to replace get_interface_address6(). * It answers with IP address 9.9.9.9 iff both of the following are true: * - <b>family</b> is AF_INET @@ -1127,6 +1197,7 @@ static sa_family_t last_address6_family; * This function increments <b>n_get_interface_address6</b> by one every * time it is called. */ +#if 0 static int get_interface_address6_replacement(int severity, sa_family_t family, tor_addr_t *addr) @@ -1144,25 +1215,7 @@ get_interface_address6_replacement(int severity, sa_family_t family, return 0; } - -static int n_get_interface_address_failure = 0; - -/** - * This mock function is meant to replace get_interface_address(). - * It pretends to fail getting interface address by returning -1. - * <b>n_get_interface_address_failure</b> is incremented by one - * every time this function is called. - */ -static int -get_interface_address_failure(int severity, uint32_t *addr) -{ - (void)severity; - (void)addr; - - n_get_interface_address_failure++; - - return -1; -} +#endif static int n_get_interface_address6_failure = 0; @@ -1185,24 +1238,45 @@ get_interface_address6_failure(int severity, sa_family_t family, return -1; } +/** Helper macro: to validate the returned value from find_my_address() so we + * don't copy those all the time. */ +#undef VALIDATE_FOUND_ADDRESS +#define VALIDATE_FOUND_ADDRESS(ret, method, hostname) \ + do { \ + tt_int_op(retval, OP_EQ, ret); \ + if (method == NULL) tt_assert(!method_used); \ + else tt_str_op(method_used, OP_EQ, method); \ + if (hostname == NULL) tt_assert(!hostname_out); \ + else tt_str_op(hostname_out, OP_EQ, hostname); \ + if (ret == true) { \ + tt_assert(tor_addr_eq(&resolved_addr, &test_addr)); \ + } \ + } while (0) + +/** Helper macro: Cleanup the address and variables used after a + * find_my_address() call. */ +#undef CLEANUP_FOUND_ADDRESS +#define CLEANUP_FOUND_ADDRESS \ + do { \ + config_free_lines(options->Address); \ + config_free_lines(options->ORPort_lines); \ + options->AddressDisableIPv6 = 0; \ + options->ORPort_set = 0; \ + tor_free(options->DirAuthorities); \ + tor_free(hostname_out); \ + tor_addr_make_unspec(&resolved_addr); \ + tor_addr_make_unspec(&test_addr); \ + } while (0) + +/** Test both IPv4 and IPv6 coexisting together in the configuration. */ static void -test_config_resolve_my_address(void *arg) +test_config_find_my_address_mixed(void *arg) { or_options_t *options; - uint32_t resolved_addr; + tor_addr_t resolved_addr, test_addr; const char *method_used; char *hostname_out = NULL; - int retval; - int prev_n_hostname_01010101; - int prev_n_hostname_localhost; - int prev_n_hostname_failure; - int prev_n_gethostname_replacement; - int prev_n_gethostname_failure; - int prev_n_gethostname_localhost; - int prev_n_get_interface_address; - int prev_n_get_interface_address_failure; - int prev_n_get_interface_address6; - int prev_n_get_interface_address6_failure; + bool retval; (void)arg; @@ -1210,369 +1284,509 @@ test_config_resolve_my_address(void *arg) options_init(options); - /* - * CASE 1: - * If options->Address is a valid IPv4 address string, we want - * the corresponding address to be parsed and returned. - */ - - options->Address = tor_strdup("128.52.128.105"); + /* + * CASE 1: Only IPv6 address. Accepted. + */ + config_line_append(&options->Address, "Address", + "2a01:4f8:fff0:4f:266:37ff:fe2c:5d19"); + tor_addr_parse(&test_addr, "2a01:4f8:fff0:4f:266:37ff:fe2c:5d19"); - retval = resolve_my_address(LOG_NOTICE,options,&resolved_addr, - &method_used,&hostname_out); + /* IPv6 address should be found and considered configured. */ + retval = find_my_address(options, AF_INET6, LOG_NOTICE, &resolved_addr, + &method_used, &hostname_out); + VALIDATE_FOUND_ADDRESS(true, "CONFIGURED", NULL); - tt_want(retval == 0); - tt_want_str_op(method_used,OP_EQ,"CONFIGURED"); - tt_want(hostname_out == NULL); - tt_assert(resolved_addr == 0x80348069); + CLEANUP_FOUND_ADDRESS; - tor_free(options->Address); + /* + * Case 2: IPv4 _and_ IPv6 given. Accepted. + */ + config_line_append(&options->Address, "Address", + "2a01:4f8:fff0:4f:266:37ff:fe2c:5d19"); + config_line_append(&options->Address, "Address", "1.1.1.1"); + tor_addr_parse(&test_addr, "1.1.1.1"); -/* - * CASE 2: - * If options->Address is a valid DNS address, we want resolve_my_address() - * function to ask tor_lookup_hostname() for help with resolving it - * and return the address that was resolved (in host order). - */ + /* IPv4 address should be found and considered configured. */ + retval = find_my_address(options, AF_INET, LOG_NOTICE, &resolved_addr, + &method_used, &hostname_out); + VALIDATE_FOUND_ADDRESS(true, "CONFIGURED", NULL); - MOCK(tor_lookup_hostname,tor_lookup_hostname_01010101); + /* IPv6 address should be found and considered configured. */ + tor_addr_parse(&test_addr, "2a01:4f8:fff0:4f:266:37ff:fe2c:5d19"); + retval = find_my_address(options, AF_INET6, LOG_NOTICE, &resolved_addr, + &method_used, &hostname_out); + VALIDATE_FOUND_ADDRESS(true, "CONFIGURED", NULL); - tor_free(options->Address); - options->Address = tor_strdup("www.torproject.org"); + CLEANUP_FOUND_ADDRESS; - prev_n_hostname_01010101 = n_hostname_01010101; + /* + * Case 3: Two hostnames, IPv4 and IPv6. + */ + config_line_append(&options->Address, "Address", "www.torproject.org.v4"); + config_line_append(&options->Address, "Address", "www.torproject.org.v6"); - retval = resolve_my_address(LOG_NOTICE,options,&resolved_addr, - &method_used,&hostname_out); + /* Looks at specific hostname to learn which address family to use. */ + MOCK(tor_addr_lookup, tor_addr_lookup_mixed); - tt_want(retval == 0); - tt_want(n_hostname_01010101 == prev_n_hostname_01010101 + 1); - tt_want_str_op(method_used,OP_EQ,"RESOLVED"); - tt_want_str_op(hostname_out,OP_EQ,"www.torproject.org"); - tt_assert(resolved_addr == 0x01010101); + /* IPv4 address should be found and considered resolved. */ + tor_addr_parse(&test_addr, "1.1.1.1"); + retval = find_my_address(options, AF_INET, LOG_NOTICE, &resolved_addr, + &method_used, &hostname_out); + VALIDATE_FOUND_ADDRESS(true, "RESOLVED", "www.torproject.org.v4"); + tor_free(hostname_out); - UNMOCK(tor_lookup_hostname); + /* IPv6 address should be found and considered resolved. */ + tor_addr_parse(&test_addr, "0101::0101"); + retval = find_my_address(options, AF_INET6, LOG_NOTICE, &resolved_addr, + &method_used, &hostname_out); + VALIDATE_FOUND_ADDRESS(true, "RESOLVED", "www.torproject.org.v6"); - tor_free(options->Address); - tor_free(hostname_out); + CLEANUP_FOUND_ADDRESS; + UNMOCK(tor_addr_lookup); -/* - * CASE 3: - * Given that options->Address is NULL, we want resolve_my_address() - * to try and use tor_gethostname() to get hostname AND use - * tor_lookup_hostname() to get IP address. - */ + /* + * Case 4: IPv4 address and a hostname resolving to IPV6. + */ + config_line_append(&options->Address, "Address", "1.1.1.1"); + config_line_append(&options->Address, "Address", "www.torproject.org.v6"); - resolved_addr = 0; - tor_free(options->Address); - options->Address = NULL; + /* Looks at specific hostname to learn which address family to use. */ + MOCK(tor_addr_lookup, tor_addr_lookup_mixed); - MOCK(tor_gethostname,tor_gethostname_replacement); - MOCK(tor_lookup_hostname,tor_lookup_hostname_01010101); + /* IPv4 address should be found and configured. */ + tor_addr_parse(&test_addr, "1.1.1.1"); + retval = find_my_address(options, AF_INET, LOG_NOTICE, &resolved_addr, + &method_used, &hostname_out); + VALIDATE_FOUND_ADDRESS(true, "CONFIGURED", NULL); - prev_n_gethostname_replacement = n_gethostname_replacement; - prev_n_hostname_01010101 = n_hostname_01010101; + /* IPv6 address should be found and considered resolved. */ + tor_addr_parse(&test_addr, "0101::0101"); + retval = find_my_address(options, AF_INET6, LOG_NOTICE, &resolved_addr, + &method_used, &hostname_out); + VALIDATE_FOUND_ADDRESS(true, "RESOLVED", "www.torproject.org.v6"); - retval = resolve_my_address(LOG_NOTICE,options,&resolved_addr, - &method_used,&hostname_out); + CLEANUP_FOUND_ADDRESS; + UNMOCK(tor_addr_lookup); - tt_want(retval == 0); - tt_want(n_gethostname_replacement == prev_n_gethostname_replacement + 1); - tt_want(n_hostname_01010101 == prev_n_hostname_01010101 + 1); - tt_want_str_op(method_used,OP_EQ,"GETHOSTNAME"); - tt_want_str_op(hostname_out,OP_EQ,"onionrouter!"); - tt_assert(resolved_addr == 0x01010101); + /* + * Case 5: Hostname resolving to IPv4 and an IPv6 address. + */ + config_line_append(&options->Address, "Address", "0101::0101"); + config_line_append(&options->Address, "Address", "www.torproject.org.v4"); - UNMOCK(tor_gethostname); - UNMOCK(tor_lookup_hostname); + /* Looks at specific hostname to learn which address family to use. */ + MOCK(tor_addr_lookup, tor_addr_lookup_mixed); + /* IPv4 address should be found and resolved. */ + tor_addr_parse(&test_addr, "1.1.1.1"); + retval = find_my_address(options, AF_INET, LOG_NOTICE, &resolved_addr, + &method_used, &hostname_out); + VALIDATE_FOUND_ADDRESS(true, "RESOLVED", "www.torproject.org.v4"); tor_free(hostname_out); -/* - * CASE 4: - * Given that options->Address is a local host address, we want - * resolve_my_address() function to fail. - */ + /* IPv6 address should be found and considered resolved. */ + tor_addr_parse(&test_addr, "0101::0101"); + retval = find_my_address(options, AF_INET6, LOG_NOTICE, &resolved_addr, + &method_used, &hostname_out); + VALIDATE_FOUND_ADDRESS(true, "CONFIGURED", NULL); + CLEANUP_FOUND_ADDRESS; - resolved_addr = 0; - tor_free(options->Address); - options->Address = tor_strdup("127.0.0.1"); + UNMOCK(tor_addr_lookup); - retval = resolve_my_address(LOG_NOTICE,options,&resolved_addr, - &method_used,&hostname_out); + done: + config_free_lines(options->Address); + or_options_free(options); + tor_free(hostname_out); - tt_want(resolved_addr == 0); - tt_int_op(retval, OP_EQ, -1); + UNMOCK(tor_addr_lookup); +} - tor_free(options->Address); - tor_free(hostname_out); +/** Parameters for the find_my_address() test. We test both AF_INET and + * AF_INET6 but we have one interface to do so thus we run the same exact unit + * tests for both without copying them. */ +typedef struct find_my_address_params_t { + /* Index where the mock function results are located. For intance, + * tor_addr_lookup_01010101() will have its returned value depending on the + * family in ret_addr_lookup_01010101[]. + * + * Values that can be found: + * AF_INET : index 0. + * AF_INET6: index 1. + */ + int idx; + int family; + const char *public_ip; + const char *internal_ip; + const char *orport; +} find_my_address_params_t; + +static find_my_address_params_t addr_param_v4 = { + .idx = 0, + .family = AF_INET, + .public_ip = "128.52.128.105", + .internal_ip = "127.0.0.1", +}; -/* - * CASE 5: - * We want resolve_my_address() to fail if DNS address in options->Address - * cannot be resolved. - */ +static find_my_address_params_t addr_param_v6 = { + .idx = 1, + .family = AF_INET6, + .public_ip = "[4242::4242]", + .internal_ip = "[::1]", +}; - MOCK(tor_lookup_hostname,tor_lookup_hostname_failure); +static void +test_config_find_my_address(void *arg) +{ + or_options_t *options; + tor_addr_t resolved_addr, test_addr; + const char *method_used; + char *hostname_out = NULL; + bool retval; + int prev_n_hostname_01010101; + int prev_n_hostname_failure; + int prev_n_hostname_localhost; + int prev_n_gethostname_replacement; + int prev_n_gethostname_failure; + int prev_n_gethostname_localhost; + int prev_n_get_interface_address6; + int prev_n_get_interface_address6_failure; - prev_n_hostname_failure = n_hostname_failure; + const find_my_address_params_t *p = arg; - tor_free(options->Address); - options->Address = tor_strdup("www.tor-project.org"); + options = options_new(); + options_init(options); - retval = resolve_my_address(LOG_NOTICE,options,&resolved_addr, - &method_used,&hostname_out); + /* + * Case 0: + * AddressDisableIPv6 is set. + * + * Only run this if we are in the IPv6 test. + */ + if (p->family == AF_INET6) { + options->AddressDisableIPv6 = 1; + /* Set a valid IPv6. However, the discovery should still fail. */ + config_line_append(&options->Address, "Address", p->public_ip); + tor_addr_parse(&test_addr, p->public_ip); + + retval = find_my_address(options, p->family, LOG_NOTICE, &resolved_addr, + &method_used, &hostname_out); + VALIDATE_FOUND_ADDRESS(false, NULL, NULL); + CLEANUP_FOUND_ADDRESS; + } - tt_want(n_hostname_failure == prev_n_hostname_failure + 1); - tt_int_op(retval, OP_EQ, -1); + /* + * Case 1: + * 1. Address is a valid address. + * + * Expected to succeed. + */ + config_line_append(&options->Address, "Address", p->public_ip); + tor_addr_parse(&test_addr, p->public_ip); - UNMOCK(tor_lookup_hostname); + retval = find_my_address(options, p->family, LOG_NOTICE, &resolved_addr, + &method_used, &hostname_out); - tor_free(options->Address); - tor_free(hostname_out); + VALIDATE_FOUND_ADDRESS(true, "CONFIGURED", NULL); + CLEANUP_FOUND_ADDRESS; -/* - * CASE 6: - * If options->Address is NULL AND gettting local hostname fails, we want - * resolve_my_address() to fail as well. - */ + /* + * Case 2: Address is a resolvable address. Expected to succeed. + */ + MOCK(tor_addr_lookup, tor_addr_lookup_01010101); - MOCK(tor_gethostname,tor_gethostname_failure); + config_line_append(&options->Address, "Address", "www.torproject.org"); + tor_addr_parse(&test_addr, ret_addr_lookup_01010101[p->idx]); - prev_n_gethostname_failure = n_gethostname_failure; + prev_n_hostname_01010101 = n_hostname_01010101; - retval = resolve_my_address(LOG_NOTICE,options,&resolved_addr, - &method_used,&hostname_out); + retval = find_my_address(options, p->family, LOG_NOTICE, &resolved_addr, + &method_used, &hostname_out); - tt_want(n_gethostname_failure == prev_n_gethostname_failure + 1); - tt_int_op(retval, OP_EQ, -1); + tt_int_op(n_hostname_01010101, OP_EQ, ++prev_n_hostname_01010101); + VALIDATE_FOUND_ADDRESS(true, "RESOLVED", "www.torproject.org"); + CLEANUP_FOUND_ADDRESS; - UNMOCK(tor_gethostname); - tor_free(hostname_out); + UNMOCK(tor_addr_lookup); -/* - * CASE 7: - * We want resolve_my_address() to try and get network interface address via - * get_interface_address() if hostname returned by tor_gethostname() cannot be - * resolved into IP address. - */ + /* + * Case 3: Address is a local addressi (internal). Expected to fail. + */ + config_line_append(&options->Address, "Address", p->internal_ip); - MOCK(tor_gethostname,tor_gethostname_replacement); - MOCK(tor_lookup_hostname,tor_lookup_hostname_failure); - MOCK(get_interface_address,get_interface_address_08080808); + setup_full_capture_of_logs(LOG_NOTICE); - prev_n_gethostname_replacement = n_gethostname_replacement; - prev_n_get_interface_address = n_get_interface_address; + retval = find_my_address(options, p->family, LOG_NOTICE, &resolved_addr, + &method_used, &hostname_out); - retval = resolve_my_address(LOG_NOTICE,options,&resolved_addr, - &method_used,&hostname_out); + expect_log_msg_containing("is a private IP address. Tor relays that " + "use the default DirAuthorities must have " + "public IP addresses."); + teardown_capture_of_logs(); - tt_want(retval == 0); - tt_want_int_op(n_gethostname_replacement, OP_EQ, - prev_n_gethostname_replacement + 1); - tt_want_int_op(n_get_interface_address, OP_EQ, - prev_n_get_interface_address + 1); - tt_want_str_op(method_used,OP_EQ,"INTERFACE"); - tt_want(hostname_out == NULL); - tt_assert(resolved_addr == 0x08080808); + VALIDATE_FOUND_ADDRESS(false, NULL, NULL); + CLEANUP_FOUND_ADDRESS; - UNMOCK(get_interface_address); - tor_free(hostname_out); + /* + * Case 4: Address is a local address but custom authorities. Expected to + * succeed. + */ + config_line_append(&options->Address, "Address", p->internal_ip); + options->DirAuthorities = tor_malloc_zero(sizeof(config_line_t)); + tor_addr_parse(&test_addr, p->internal_ip); -/* - * CASE 8: - * Suppose options->Address is NULL AND hostname returned by tor_gethostname() - * is unresolvable. We want resolve_my_address to fail if - * get_interface_address() fails. - */ + retval = find_my_address(options, p->family, LOG_NOTICE, &resolved_addr, + &method_used, &hostname_out); - MOCK(get_interface_address,get_interface_address_failure); + VALIDATE_FOUND_ADDRESS(true, "CONFIGURED", NULL); + CLEANUP_FOUND_ADDRESS; - prev_n_get_interface_address_failure = n_get_interface_address_failure; - prev_n_gethostname_replacement = n_gethostname_replacement; + /* + * Case 5: Multiple address in Address. Expected to fail. + */ + config_line_append(&options->Address, "Address", p->public_ip); + config_line_append(&options->Address, "Address", p->public_ip); - retval = resolve_my_address(LOG_NOTICE,options,&resolved_addr, - &method_used,&hostname_out); + setup_full_capture_of_logs(LOG_NOTICE); - tt_want(n_get_interface_address_failure == - prev_n_get_interface_address_failure + 1); - tt_want(n_gethostname_replacement == - prev_n_gethostname_replacement + 1); - tt_int_op(retval, OP_EQ, -1); + retval = find_my_address(options, p->family, LOG_NOTICE, &resolved_addr, + &method_used, &hostname_out); - UNMOCK(get_interface_address); - tor_free(hostname_out); + expect_log_msg_containing("Found 2 Address statement of address family"); + teardown_capture_of_logs(); -/* - * CASE 9: - * Given that options->Address is NULL AND tor_lookup_hostname() - * fails AND hostname returned by gethostname() resolves - * to local IP address, we want resolve_my_address() function to - * call get_interface_address6(.,AF_INET,.) and return IP address - * the latter function has found. - */ + VALIDATE_FOUND_ADDRESS(false, NULL, NULL); + CLEANUP_FOUND_ADDRESS; - MOCK(tor_lookup_hostname,tor_lookup_hostname_failure); - MOCK(tor_gethostname,tor_gethostname_replacement); - MOCK(get_interface_address6,get_interface_address6_replacement); + /* + * Case 8: + * 1. Address is NULL + * 2. Interface address is a valid address. + * + * Expected to succeed. + */ + options->Address = NULL; + tor_addr_parse(&test_addr, ret_get_interface_address6_08080808[p->idx]); + + MOCK(get_interface_address6, get_interface_address6_08080808); - prev_n_gethostname_replacement = n_gethostname_replacement; - prev_n_hostname_failure = n_hostname_failure; prev_n_get_interface_address6 = n_get_interface_address6; - retval = resolve_my_address(LOG_NOTICE,options,&resolved_addr, - &method_used,&hostname_out); + retval = find_my_address(options, p->family, LOG_NOTICE, &resolved_addr, + &method_used, &hostname_out); - tt_want(last_address6_family == AF_INET); - tt_want(n_get_interface_address6 == prev_n_get_interface_address6 + 1); - tt_want(n_hostname_failure == prev_n_hostname_failure + 1); - tt_want(n_gethostname_replacement == prev_n_gethostname_replacement + 1); - tt_want(retval == 0); - tt_want_str_op(method_used,OP_EQ,"INTERFACE"); - tt_assert(resolved_addr == 0x09090909); + tt_int_op(n_get_interface_address6, OP_EQ, ++prev_n_get_interface_address6); + VALIDATE_FOUND_ADDRESS(true, "INTERFACE", NULL); + CLEANUP_FOUND_ADDRESS; - UNMOCK(tor_lookup_hostname); - UNMOCK(tor_gethostname); UNMOCK(get_interface_address6); - tor_free(hostname_out); - /* - * CASE 10: We want resolve_my_address() to fail if all of the following - * are true: - * 1. options->Address is not NULL - * 2. ... but it cannot be converted to struct in_addr by - * tor_inet_aton() - * 3. ... and tor_lookup_hostname() fails to resolve the - * options->Address + * Case 9: + * 1. Address is NULL + * 2. Interface address fails to be found. + * 3. Local hostname resolves to a valid address. + * + * Expected to succeed. */ + options->Address = NULL; + tor_addr_parse(&test_addr, ret_addr_lookup_01010101[p->idx]); - MOCK(tor_lookup_hostname,tor_lookup_hostname_failure); - - prev_n_hostname_failure = n_hostname_failure; + MOCK(get_interface_address6, get_interface_address6_failure); + MOCK(tor_gethostname, tor_gethostname_replacement); + MOCK(tor_addr_lookup, tor_addr_lookup_01010101); - tor_free(options->Address); - options->Address = tor_strdup("some_hostname"); + prev_n_get_interface_address6_failure = n_get_interface_address6_failure; + prev_n_hostname_01010101 = n_hostname_01010101; + prev_n_gethostname_replacement = n_gethostname_replacement; - retval = resolve_my_address(LOG_NOTICE, options, &resolved_addr, - &method_used,&hostname_out); + retval = find_my_address(options, p->family, LOG_NOTICE, &resolved_addr, + &method_used, &hostname_out); - tt_want(n_hostname_failure == prev_n_hostname_failure + 1); - tt_int_op(retval, OP_EQ, -1); + tt_int_op(n_get_interface_address6_failure, OP_EQ, + ++prev_n_get_interface_address6_failure); + tt_int_op(n_hostname_01010101, OP_EQ, + ++prev_n_hostname_01010101); + tt_int_op(n_gethostname_replacement, OP_EQ, + ++prev_n_gethostname_replacement); + VALIDATE_FOUND_ADDRESS(true, "GETHOSTNAME", "onionrouter!"); + CLEANUP_FOUND_ADDRESS; + UNMOCK(get_interface_address6); UNMOCK(tor_gethostname); - UNMOCK(tor_lookup_hostname); - - tor_free(hostname_out); + UNMOCK(tor_addr_lookup); /* - * CASE 11: - * Suppose the following sequence of events: - * 1. options->Address is NULL - * 2. tor_gethostname() succeeds to get hostname of machine Tor - * if running on. - * 3. Hostname from previous step cannot be converted to - * address by using tor_inet_aton() function. - * 4. However, tor_lookup_hostname() succeeds in resolving the - * hostname from step 2. - * 5. Unfortunately, tor_addr_is_internal() deems this address - * to be internal. - * 6. get_interface_address6(.,AF_INET,.) returns non-internal - * IPv4 + * Case 10: + * 1. Address is NULL + * 2. Interface address fails to be found. + * 3. Local hostname resolves to an internal address. * - * We want resolve_my_addr() to succeed with method "INTERFACE" - * and address from step 6. + * Expected to fail. */ - - tor_free(options->Address); options->Address = NULL; - MOCK(tor_gethostname,tor_gethostname_replacement); - MOCK(tor_lookup_hostname,tor_lookup_hostname_localhost); - MOCK(get_interface_address6,get_interface_address6_replacement); + MOCK(get_interface_address6, get_interface_address6_failure); + MOCK(tor_gethostname, tor_gethostname_localhost); + MOCK(tor_addr_lookup, tor_addr_lookup_localhost); - prev_n_gethostname_replacement = n_gethostname_replacement; + prev_n_get_interface_address6_failure = n_get_interface_address6_failure; prev_n_hostname_localhost = n_hostname_localhost; - prev_n_get_interface_address6 = n_get_interface_address6; + prev_n_gethostname_localhost = n_gethostname_localhost; - retval = resolve_my_address(LOG_DEBUG, options, &resolved_addr, - &method_used,&hostname_out); + retval = find_my_address(options, p->family, LOG_NOTICE, &resolved_addr, + &method_used, &hostname_out); - tt_want(n_gethostname_replacement == prev_n_gethostname_replacement + 1); - tt_want(n_hostname_localhost == prev_n_hostname_localhost + 1); - tt_want(n_get_interface_address6 == prev_n_get_interface_address6 + 1); + tt_int_op(n_get_interface_address6_failure, OP_EQ, + ++prev_n_get_interface_address6_failure); + tt_int_op(n_hostname_localhost, OP_EQ, + ++prev_n_hostname_localhost); + tt_int_op(n_gethostname_localhost, OP_EQ, + ++prev_n_gethostname_localhost); + VALIDATE_FOUND_ADDRESS(false, NULL, NULL); + CLEANUP_FOUND_ADDRESS; - tt_str_op(method_used,OP_EQ,"INTERFACE"); - tt_ptr_op(hostname_out, OP_EQ, NULL); - tt_int_op(retval, OP_EQ, 0); + UNMOCK(get_interface_address6); + UNMOCK(tor_gethostname); + UNMOCK(tor_addr_lookup); /* - * CASE 11b: - * 1-5 as above. - * 6. get_interface_address6() fails. + * Case 11: + * 1. Address is NULL + * 2. Interface address fails to be found. + * 3. Local hostname fails to be found. * - * In this subcase, we want resolve_my_address() to fail. + * Expected to fail. */ + options->Address = NULL; - UNMOCK(get_interface_address6); - MOCK(get_interface_address6,get_interface_address6_failure); + MOCK(get_interface_address6, get_interface_address6_failure); + MOCK(tor_gethostname, tor_gethostname_failure); - prev_n_gethostname_replacement = n_gethostname_replacement; - prev_n_hostname_localhost = n_hostname_localhost; prev_n_get_interface_address6_failure = n_get_interface_address6_failure; + prev_n_gethostname_failure = n_gethostname_failure; - retval = resolve_my_address(LOG_DEBUG, options, &resolved_addr, - &method_used,&hostname_out); - - tt_want(n_gethostname_replacement == prev_n_gethostname_replacement + 1); - tt_want(n_hostname_localhost == prev_n_hostname_localhost + 1); - tt_want(n_get_interface_address6_failure == - prev_n_get_interface_address6_failure + 1); + retval = find_my_address(options, p->family, LOG_NOTICE, &resolved_addr, + &method_used, &hostname_out); - tt_int_op(retval, OP_EQ, -1); + tt_int_op(n_get_interface_address6_failure, OP_EQ, + ++prev_n_get_interface_address6_failure); + tt_int_op(n_gethostname_failure, OP_EQ, + ++prev_n_gethostname_failure); + VALIDATE_FOUND_ADDRESS(false, NULL, NULL); + CLEANUP_FOUND_ADDRESS; - UNMOCK(tor_gethostname); - UNMOCK(tor_lookup_hostname); UNMOCK(get_interface_address6); + UNMOCK(tor_gethostname); - /* CASE 12: - * Suppose the following happens: - * 1. options->Address is NULL AND options->DirAuthorities is non-NULL - * 2. tor_gethostname() succeeds in getting hostname of a machine ... - * 3. ... which is successfully parsed by tor_inet_aton() ... - * 4. into IPv4 address that tor_addr_is_inernal() considers to be - * internal. + /* + * Case 12: + * 1. Address is NULL + * 2. Interface address fails to be found. + * 3. Local hostname can't be resolved. * - * In this case, we want resolve_my_address() to fail. + * Expected to fail. */ - - tor_free(options->Address); options->Address = NULL; - options->DirAuthorities = tor_malloc_zero(sizeof(config_line_t)); - MOCK(tor_gethostname,tor_gethostname_localhost); + MOCK(get_interface_address6, get_interface_address6_failure); + MOCK(tor_gethostname, tor_gethostname_replacement); + MOCK(tor_addr_lookup, tor_addr_lookup_failure); - prev_n_gethostname_localhost = n_gethostname_localhost; + prev_n_get_interface_address6_failure = n_get_interface_address6_failure; + prev_n_gethostname_replacement = n_gethostname_replacement; + prev_n_hostname_failure = n_hostname_failure; + + retval = find_my_address(options, p->family, LOG_NOTICE, &resolved_addr, + &method_used, &hostname_out); + + tt_int_op(n_get_interface_address6_failure, OP_EQ, + ++prev_n_get_interface_address6_failure); + tt_int_op(n_gethostname_replacement, OP_EQ, + ++prev_n_gethostname_replacement); + tt_int_op(n_hostname_failure, OP_EQ, + ++prev_n_hostname_failure); + VALIDATE_FOUND_ADDRESS(false, NULL, NULL); + CLEANUP_FOUND_ADDRESS; + + /* + * Case 13: + * 1. Address is NULL. + * 2. ORPort has a valid public address. + */ + { + char *msg = NULL; + int n, w, ret; + char *orport_line = NULL; + + options->Address = NULL; + tor_asprintf(&orport_line, "%s:9001", p->public_ip); + config_line_append(&options->ORPort_lines, "ORPort", orport_line); + tor_free(orport_line); + + if (p->family == AF_INET6) { + /* XXX: Tor does _not_ allow an IPv6 only ORPort thus we need to add a + * bogus IPv4 at the moment. */ + config_line_append(&options->ORPort_lines, "ORPort", "1.1.1.1:9001"); + } + + ret = parse_ports(options, 0, &msg, &n, &w); + tt_int_op(ret, OP_EQ, 0); + tor_addr_parse(&test_addr, p->public_ip); + } - retval = resolve_my_address(LOG_DEBUG, options, &resolved_addr, - &method_used,&hostname_out); + retval = find_my_address(options, p->family, LOG_NOTICE, &resolved_addr, + &method_used, &hostname_out); + VALIDATE_FOUND_ADDRESS(true, "CONFIGURED_ORPORT", NULL); + CLEANUP_FOUND_ADDRESS; - tt_want(n_gethostname_localhost == prev_n_gethostname_localhost + 1); - tt_int_op(retval, OP_EQ, -1); + /* + * Case 14: + * 1. Address is NULL. + * 2. ORPort has an internal address thus fails. + * 3. Interface as a valid address. + */ + { + char *msg = NULL; + int n, w, ret; + char *orport_line = NULL; + + options->Address = NULL; + tor_asprintf(&orport_line, "%s:9001", p->internal_ip); + config_line_append(&options->ORPort_lines, "ORPort", orport_line); + tor_free(orport_line); + + if (p->family == AF_INET6) { + /* XXX: Tor does _not_ allow an IPv6 only ORPort thus we need to add a + * bogus IPv4 at the moment. */ + config_line_append(&options->ORPort_lines, "ORPort", "1.1.1.1:9001"); + } + + ret = parse_ports(options, 0, &msg, &n, &w); + tt_int_op(ret, OP_EQ, 0); + } + tor_addr_parse(&test_addr, ret_get_interface_address6_08080808[p->idx]); + + MOCK(get_interface_address6, get_interface_address6_08080808); + prev_n_get_interface_address6 = n_get_interface_address6; + + retval = find_my_address(options, p->family, LOG_NOTICE, &resolved_addr, + &method_used, &hostname_out); + + tt_int_op(n_get_interface_address6, OP_EQ, ++prev_n_get_interface_address6); + VALIDATE_FOUND_ADDRESS(true, "INTERFACE", NULL); + CLEANUP_FOUND_ADDRESS; + + UNMOCK(get_interface_address6); UNMOCK(tor_gethostname); + UNMOCK(tor_addr_lookup); done: - tor_free(options->Address); - tor_free(options->DirAuthorities); or_options_free(options); - tor_free(hostname_out); UNMOCK(tor_gethostname); - UNMOCK(tor_lookup_hostname); - UNMOCK(get_interface_address); + UNMOCK(tor_addr_lookup); UNMOCK(get_interface_address6); - UNMOCK(tor_gethostname); } static void @@ -2071,7 +2285,7 @@ test_config_adding_dir_servers(void *arg) ds, /* increment the found counter if dir_port matches */ found_D0 += - (ds->dir_port == 60090 ? + (ds->ipv4_dirport == 60090 ? 1 : 0) ); tt_int_op(found_D0, OP_EQ, 1); @@ -2083,7 +2297,7 @@ test_config_adding_dir_servers(void *arg) ds, /* increment the found counter if dir_port matches */ found_B1 += - (ds->dir_port == 60091 ? + (ds->ipv4_dirport == 60091 ? 1 : 0) ); tt_int_op(found_B1, OP_EQ, 0); @@ -2095,7 +2309,7 @@ test_config_adding_dir_servers(void *arg) ds, /* increment the found counter if dir_port matches */ found_A2 += - (ds->dir_port == 60092 ? + (ds->ipv4_dirport == 60092 ? 1 : 0) ); tt_int_op(found_A2, OP_EQ, 0); @@ -2114,7 +2328,7 @@ test_config_adding_dir_servers(void *arg) ds, /* increment the found counter if dir_port matches */ found_D0 += - (ds->dir_port == 60090 ? + (ds->ipv4_dirport == 60090 ? 1 : 0) ); tt_int_op(found_D0, OP_EQ, 1); @@ -2126,7 +2340,7 @@ test_config_adding_dir_servers(void *arg) ds, /* increment the found counter if dir_port matches */ found_B1 += - (ds->dir_port == 60091 ? + (ds->ipv4_dirport == 60091 ? 1 : 0) ); tt_int_op(found_B1, OP_EQ, 0); @@ -2138,7 +2352,7 @@ test_config_adding_dir_servers(void *arg) ds, /* increment the found counter if dir_port matches */ found_A2 += - (ds->dir_port == 60092 ? + (ds->ipv4_dirport == 60092 ? 1 : 0) ); tt_int_op(found_A2, OP_EQ, 0); @@ -2150,7 +2364,7 @@ test_config_adding_dir_servers(void *arg) ds, /* increment the found counter if dir_port matches */ found_non_default_fallback += - (ds->dir_port == 60093 ? + (ds->ipv4_dirport == 60093 ? 1 : 0) ); tt_int_op(found_non_default_fallback, OP_EQ, 1); @@ -2162,7 +2376,7 @@ test_config_adding_dir_servers(void *arg) ds, /* increment the found counter if dir_port matches */ found_default_fallback += - (ds->dir_port == 60099 ? + (ds->ipv4_dirport == 60099 ? 1 : 0) ); tt_int_op(found_default_fallback, OP_EQ, 0); @@ -2214,7 +2428,7 @@ test_config_adding_dir_servers(void *arg) ds, /* increment the found counter if dir_port matches */ found_D0 += - (ds->dir_port == 60090 ? + (ds->ipv4_dirport == 60090 ? 1 : 0) ); tt_int_op(found_D0, OP_EQ, 1); @@ -2226,7 +2440,7 @@ test_config_adding_dir_servers(void *arg) ds, /* increment the found counter if dir_port matches */ found_B1 += - (ds->dir_port == 60091 ? + (ds->ipv4_dirport == 60091 ? 1 : 0) ); tt_int_op(found_B1, OP_EQ, 0); @@ -2238,7 +2452,7 @@ test_config_adding_dir_servers(void *arg) ds, /* increment the found counter if dir_port matches */ found_A2 += - (ds->dir_port == 60092 ? + (ds->ipv4_dirport == 60092 ? 1 : 0) ); tt_int_op(found_A2, OP_EQ, 0); @@ -2257,7 +2471,7 @@ test_config_adding_dir_servers(void *arg) ds, /* increment the found counter if dir_port matches */ found_D0 += - (ds->dir_port == 60090 ? + (ds->ipv4_dirport == 60090 ? 1 : 0) ); tt_int_op(found_D0, OP_EQ, 1); @@ -2269,7 +2483,7 @@ test_config_adding_dir_servers(void *arg) ds, /* increment the found counter if dir_port matches */ found_B1 += - (ds->dir_port == 60091 ? + (ds->ipv4_dirport == 60091 ? 1 : 0) ); tt_int_op(found_B1, OP_EQ, 0); @@ -2281,7 +2495,7 @@ test_config_adding_dir_servers(void *arg) ds, /* increment the found counter if dir_port matches */ found_A2 += - (ds->dir_port == 60092 ? + (ds->ipv4_dirport == 60092 ? 1 : 0) ); tt_int_op(found_A2, OP_EQ, 0); @@ -2293,7 +2507,7 @@ test_config_adding_dir_servers(void *arg) ds, /* increment the found counter if dir_port matches */ found_non_default_fallback += - (ds->dir_port == 60093 ? + (ds->ipv4_dirport == 60093 ? 1 : 0) ); tt_int_op(found_non_default_fallback, OP_EQ, 0); @@ -2305,7 +2519,7 @@ test_config_adding_dir_servers(void *arg) ds, /* increment the found counter if dir_port matches */ found_default_fallback += - (ds->dir_port == 60099 ? + (ds->ipv4_dirport == 60099 ? 1 : 0) ); tt_int_op(found_default_fallback, OP_EQ, 0); @@ -2357,7 +2571,7 @@ test_config_adding_dir_servers(void *arg) ds, /* increment the found counter if dir_port matches */ found_D0 += - (ds->dir_port == 60090 ? + (ds->ipv4_dirport == 60090 ? 1 : 0) ); tt_int_op(found_D0, OP_EQ, 0); @@ -2369,7 +2583,7 @@ test_config_adding_dir_servers(void *arg) ds, /* increment the found counter if dir_port matches */ found_B1 += - (ds->dir_port == 60091 ? + (ds->ipv4_dirport == 60091 ? 1 : 0) ); tt_int_op(found_B1, OP_EQ, 1); @@ -2381,7 +2595,7 @@ test_config_adding_dir_servers(void *arg) ds, /* increment the found counter if dir_port matches */ found_A2 += - (ds->dir_port == 60092 ? + (ds->ipv4_dirport == 60092 ? 1 : 0) ); tt_int_op(found_A2, OP_EQ, 1); @@ -2400,7 +2614,7 @@ test_config_adding_dir_servers(void *arg) ds, /* increment the found counter if dir_port matches */ found_D0 += - (ds->dir_port == 60090 ? + (ds->ipv4_dirport == 60090 ? 1 : 0) ); tt_int_op(found_D0, OP_EQ, 0); @@ -2412,7 +2626,7 @@ test_config_adding_dir_servers(void *arg) ds, /* increment the found counter if dir_port matches */ found_B1 += - (ds->dir_port == 60091 ? + (ds->ipv4_dirport == 60091 ? 1 : 0) ); tt_int_op(found_B1, OP_EQ, 1); @@ -2424,7 +2638,7 @@ test_config_adding_dir_servers(void *arg) ds, /* increment the found counter if dir_port matches */ found_A2 += - (ds->dir_port == 60092 ? + (ds->ipv4_dirport == 60092 ? 1 : 0) ); tt_int_op(found_A2, OP_EQ, 1); @@ -2436,7 +2650,7 @@ test_config_adding_dir_servers(void *arg) ds, /* increment the found counter if dir_port matches */ found_non_default_fallback += - (ds->dir_port == 60093 ? + (ds->ipv4_dirport == 60093 ? 1 : 0) ); tt_int_op(found_non_default_fallback, OP_EQ, 1); @@ -2448,7 +2662,7 @@ test_config_adding_dir_servers(void *arg) ds, /* increment the found counter if dir_port matches */ found_default_fallback += - (ds->dir_port == 60099 ? + (ds->ipv4_dirport == 60099 ? 1 : 0) ); tt_int_op(found_default_fallback, OP_EQ, 0); @@ -2501,7 +2715,7 @@ test_config_adding_dir_servers(void *arg) ds, /* increment the found counter if dir_port matches */ found_D0 += - (ds->dir_port == 60090 ? + (ds->ipv4_dirport == 60090 ? 1 : 0) ); tt_int_op(found_D0, OP_EQ, 0); @@ -2513,7 +2727,7 @@ test_config_adding_dir_servers(void *arg) ds, /* increment the found counter if dir_port matches */ found_B1 += - (ds->dir_port == 60091 ? + (ds->ipv4_dirport == 60091 ? 1 : 0) ); tt_int_op(found_B1, OP_EQ, 1); @@ -2525,7 +2739,7 @@ test_config_adding_dir_servers(void *arg) ds, /* increment the found counter if dir_port matches */ found_A2 += - (ds->dir_port == 60092 ? + (ds->ipv4_dirport == 60092 ? 1 : 0) ); tt_int_op(found_A2, OP_EQ, 1); @@ -2544,7 +2758,7 @@ test_config_adding_dir_servers(void *arg) ds, /* increment the found counter if dir_port matches */ found_D0 += - (ds->dir_port == 60090 ? + (ds->ipv4_dirport == 60090 ? 1 : 0) ); tt_int_op(found_D0, OP_EQ, 0); @@ -2556,7 +2770,7 @@ test_config_adding_dir_servers(void *arg) ds, /* increment the found counter if dir_port matches */ found_B1 += - (ds->dir_port == 60091 ? + (ds->ipv4_dirport == 60091 ? 1 : 0) ); tt_int_op(found_B1, OP_EQ, 1); @@ -2568,7 +2782,7 @@ test_config_adding_dir_servers(void *arg) ds, /* increment the found counter if dir_port matches */ found_A2 += - (ds->dir_port == 60092 ? + (ds->ipv4_dirport == 60092 ? 1 : 0) ); tt_int_op(found_A2, OP_EQ, 1); @@ -2580,7 +2794,7 @@ test_config_adding_dir_servers(void *arg) ds, /* increment the found counter if dir_port matches */ found_non_default_fallback += - (ds->dir_port == 60093 ? + (ds->ipv4_dirport == 60093 ? 1 : 0) ); tt_int_op(found_non_default_fallback, OP_EQ, 0); @@ -2592,7 +2806,7 @@ test_config_adding_dir_servers(void *arg) ds, /* increment the found counter if dir_port matches */ found_default_fallback += - (ds->dir_port == 60099 ? + (ds->ipv4_dirport == 60099 ? 1 : 0) ); tt_int_op(found_default_fallback, OP_EQ, 0); @@ -2655,7 +2869,7 @@ test_config_adding_dir_servers(void *arg) ds, /* increment the found counter if dir_port matches */ found_D0 += - (ds->dir_port == 60090 ? + (ds->ipv4_dirport == 60090 ? 1 : 0) ); tt_int_op(found_D0, OP_EQ, 0); @@ -2667,7 +2881,7 @@ test_config_adding_dir_servers(void *arg) ds, /* increment the found counter if dir_port matches */ found_B1 += - (ds->dir_port == 60091 ? + (ds->ipv4_dirport == 60091 ? 1 : 0) ); tt_int_op(found_B1, OP_EQ, 1); @@ -2679,7 +2893,7 @@ test_config_adding_dir_servers(void *arg) ds, /* increment the found counter if dir_port matches */ found_A2 += - (ds->dir_port == 60092 ? + (ds->ipv4_dirport == 60092 ? 1 : 0) ); tt_int_op(found_A2, OP_EQ, 0); @@ -2705,7 +2919,7 @@ test_config_adding_dir_servers(void *arg) ds, /* increment the found counter if dir_port matches */ found_D0 += - (ds->dir_port == 60090 ? + (ds->ipv4_dirport == 60090 ? 1 : 0) ); tt_int_op(found_D0, OP_EQ, 0); @@ -2717,7 +2931,7 @@ test_config_adding_dir_servers(void *arg) ds, /* increment the found counter if dir_port matches */ found_B1 += - (ds->dir_port == 60091 ? + (ds->ipv4_dirport == 60091 ? 1 : 0) ); tt_int_op(found_B1, OP_EQ, 1); @@ -2729,7 +2943,7 @@ test_config_adding_dir_servers(void *arg) ds, /* increment the found counter if dir_port matches */ found_A2 += - (ds->dir_port == 60092 ? + (ds->ipv4_dirport == 60092 ? 1 : 0) ); tt_int_op(found_A2, OP_EQ, 0); @@ -2741,7 +2955,7 @@ test_config_adding_dir_servers(void *arg) ds, /* increment the found counter if dir_port matches */ found_non_default_fallback += - (ds->dir_port == 60093 ? + (ds->ipv4_dirport == 60093 ? 1 : 0) ); tt_int_op(found_non_default_fallback, OP_EQ, 1); @@ -2753,7 +2967,7 @@ test_config_adding_dir_servers(void *arg) ds, /* increment the found counter if dir_port matches */ found_default_fallback += - (ds->dir_port == 60099 ? + (ds->ipv4_dirport == 60099 ? 1 : 0) ); tt_int_op(found_default_fallback, OP_EQ, 0); @@ -2811,7 +3025,7 @@ test_config_adding_dir_servers(void *arg) ds, /* increment the found counter if dir_port matches */ found_D0 += - (ds->dir_port == 60090 ? + (ds->ipv4_dirport == 60090 ? 1 : 0) ); tt_int_op(found_D0, OP_EQ, 0); @@ -2823,7 +3037,7 @@ test_config_adding_dir_servers(void *arg) ds, /* increment the found counter if dir_port matches */ found_B1 += - (ds->dir_port == 60091 ? + (ds->ipv4_dirport == 60091 ? 1 : 0) ); tt_int_op(found_B1, OP_EQ, 1); @@ -2835,7 +3049,7 @@ test_config_adding_dir_servers(void *arg) ds, /* increment the found counter if dir_port matches */ found_A2 += - (ds->dir_port == 60092 ? + (ds->ipv4_dirport == 60092 ? 1 : 0) ); tt_int_op(found_A2, OP_EQ, 0); @@ -2861,7 +3075,7 @@ test_config_adding_dir_servers(void *arg) ds, /* increment the found counter if dir_port matches */ found_D0 += - (ds->dir_port == 60090 ? + (ds->ipv4_dirport == 60090 ? 1 : 0) ); tt_int_op(found_D0, OP_EQ, 0); @@ -2873,7 +3087,7 @@ test_config_adding_dir_servers(void *arg) ds, /* increment the found counter if dir_port matches */ found_B1 += - (ds->dir_port == 60091 ? + (ds->ipv4_dirport == 60091 ? 1 : 0) ); tt_int_op(found_B1, OP_EQ, 1); @@ -2885,7 +3099,7 @@ test_config_adding_dir_servers(void *arg) ds, /* increment the found counter if dir_port matches */ found_A2 += - (ds->dir_port == 60092 ? + (ds->ipv4_dirport == 60092 ? 1 : 0) ); tt_int_op(found_A2, OP_EQ, 0); @@ -2897,7 +3111,7 @@ test_config_adding_dir_servers(void *arg) ds, /* increment the found counter if dir_port matches */ found_non_default_fallback += - (ds->dir_port == 60093 ? + (ds->ipv4_dirport == 60093 ? 1 : 0) ); tt_int_op(found_non_default_fallback, OP_EQ, 0); @@ -2909,7 +3123,7 @@ test_config_adding_dir_servers(void *arg) ds, /* increment the found counter if dir_port matches */ found_default_fallback += - (ds->dir_port == 60099 ? + (ds->ipv4_dirport == 60099 ? 1 : 0) ); tt_int_op(found_default_fallback, OP_EQ, 1); @@ -2977,7 +3191,7 @@ test_config_adding_dir_servers(void *arg) ds, /* increment the found counter if dir_port matches */ found_D0 += - (ds->dir_port == 60090 ? + (ds->ipv4_dirport == 60090 ? 1 : 0) ); tt_int_op(found_D0, OP_EQ, 0); @@ -2989,7 +3203,7 @@ test_config_adding_dir_servers(void *arg) ds, /* increment the found counter if dir_port matches */ found_B1 += - (ds->dir_port == 60091 ? + (ds->ipv4_dirport == 60091 ? 1 : 0) ); tt_int_op(found_B1, OP_EQ, 0); @@ -3001,7 +3215,7 @@ test_config_adding_dir_servers(void *arg) ds, /* increment the found counter if dir_port matches */ found_A2 += - (ds->dir_port == 60092 ? + (ds->ipv4_dirport == 60092 ? 1 : 0) ); tt_int_op(found_A2, OP_EQ, 1); @@ -3028,7 +3242,7 @@ test_config_adding_dir_servers(void *arg) ds, /* increment the found counter if dir_port matches */ found_D0 += - (ds->dir_port == 60090 ? + (ds->ipv4_dirport == 60090 ? 1 : 0) ); tt_int_op(found_D0, OP_EQ, 0); @@ -3040,7 +3254,7 @@ test_config_adding_dir_servers(void *arg) ds, /* increment the found counter if dir_port matches */ found_B1 += - (ds->dir_port == 60091 ? + (ds->ipv4_dirport == 60091 ? 1 : 0) ); tt_int_op(found_B1, OP_EQ, 0); @@ -3052,7 +3266,7 @@ test_config_adding_dir_servers(void *arg) ds, /* increment the found counter if dir_port matches */ found_A2 += - (ds->dir_port == 60092 ? + (ds->ipv4_dirport == 60092 ? 1 : 0) ); tt_int_op(found_A2, OP_EQ, 1); @@ -3064,7 +3278,7 @@ test_config_adding_dir_servers(void *arg) ds, /* increment the found counter if dir_port matches */ found_non_default_fallback += - (ds->dir_port == 60093 ? + (ds->ipv4_dirport == 60093 ? 1 : 0) ); tt_int_op(found_non_default_fallback, OP_EQ, 1); @@ -3076,7 +3290,7 @@ test_config_adding_dir_servers(void *arg) ds, /* increment the found counter if dir_port matches */ found_default_fallback += - (ds->dir_port == 60099 ? + (ds->ipv4_dirport == 60099 ? 1 : 0) ); tt_int_op(found_default_fallback, OP_EQ, 0); @@ -3137,7 +3351,7 @@ test_config_adding_dir_servers(void *arg) ds, /* increment the found counter if dir_port matches */ found_D0 += - (ds->dir_port == 60090 ? + (ds->ipv4_dirport == 60090 ? 1 : 0) ); tt_int_op(found_D0, OP_EQ, 0); @@ -3149,7 +3363,7 @@ test_config_adding_dir_servers(void *arg) ds, /* increment the found counter if dir_port matches */ found_B1 += - (ds->dir_port == 60091 ? + (ds->ipv4_dirport == 60091 ? 1 : 0) ); tt_int_op(found_B1, OP_EQ, 0); @@ -3161,7 +3375,7 @@ test_config_adding_dir_servers(void *arg) ds, /* increment the found counter if dir_port matches */ found_A2 += - (ds->dir_port == 60092 ? + (ds->ipv4_dirport == 60092 ? 1 : 0) ); tt_int_op(found_A2, OP_EQ, 1); @@ -3188,7 +3402,7 @@ test_config_adding_dir_servers(void *arg) ds, /* increment the found counter if dir_port matches */ found_D0 += - (ds->dir_port == 60090 ? + (ds->ipv4_dirport == 60090 ? 1 : 0) ); tt_int_op(found_D0, OP_EQ, 0); @@ -3200,7 +3414,7 @@ test_config_adding_dir_servers(void *arg) ds, /* increment the found counter if dir_port matches */ found_B1 += - (ds->dir_port == 60091 ? + (ds->ipv4_dirport == 60091 ? 1 : 0) ); tt_int_op(found_B1, OP_EQ, 0); @@ -3212,7 +3426,7 @@ test_config_adding_dir_servers(void *arg) ds, /* increment the found counter if dir_port matches */ found_A2 += - (ds->dir_port == 60092 ? + (ds->ipv4_dirport == 60092 ? 1 : 0) ); tt_int_op(found_A2, OP_EQ, 1); @@ -3224,7 +3438,7 @@ test_config_adding_dir_servers(void *arg) ds, /* increment the found counter if dir_port matches */ found_non_default_fallback += - (ds->dir_port == 60093 ? + (ds->ipv4_dirport == 60093 ? 1 : 0) ); tt_int_op(found_non_default_fallback, OP_EQ, 0); @@ -3236,7 +3450,7 @@ test_config_adding_dir_servers(void *arg) ds, /* increment the found counter if dir_port matches */ found_default_fallback += - (ds->dir_port == 60099 ? + (ds->ipv4_dirport == 60099 ? 1 : 0) ); tt_int_op(found_default_fallback, OP_EQ, 0); @@ -3304,7 +3518,7 @@ test_config_adding_dir_servers(void *arg) ds, /* increment the found counter if dir_port matches */ found_D0 += - (ds->dir_port == 60090 ? + (ds->ipv4_dirport == 60090 ? 1 : 0) ); tt_int_op(found_D0, OP_EQ, 0); @@ -3316,7 +3530,7 @@ test_config_adding_dir_servers(void *arg) ds, /* increment the found counter if dir_port matches */ found_B1 += - (ds->dir_port == 60091 ? + (ds->ipv4_dirport == 60091 ? 1 : 0) ); tt_int_op(found_B1, OP_EQ, 0); @@ -3328,7 +3542,7 @@ test_config_adding_dir_servers(void *arg) ds, /* increment the found counter if dir_port matches */ found_A2 += - (ds->dir_port == 60092 ? + (ds->ipv4_dirport == 60092 ? 1 : 0) ); tt_int_op(found_A2, OP_EQ, 0); @@ -3355,7 +3569,7 @@ test_config_adding_dir_servers(void *arg) ds, /* increment the found counter if dir_port matches */ found_D0 += - (ds->dir_port == 60090 ? + (ds->ipv4_dirport == 60090 ? 1 : 0) ); tt_int_op(found_D0, OP_EQ, 0); @@ -3367,7 +3581,7 @@ test_config_adding_dir_servers(void *arg) ds, /* increment the found counter if dir_port matches */ found_B1 += - (ds->dir_port == 60091 ? + (ds->ipv4_dirport == 60091 ? 1 : 0) ); tt_int_op(found_B1, OP_EQ, 0); @@ -3379,7 +3593,7 @@ test_config_adding_dir_servers(void *arg) ds, /* increment the found counter if dir_port matches */ found_A2 += - (ds->dir_port == 60092 ? + (ds->ipv4_dirport == 60092 ? 1 : 0) ); tt_int_op(found_A2, OP_EQ, 0); @@ -3391,7 +3605,7 @@ test_config_adding_dir_servers(void *arg) ds, /* increment the found counter if dir_port matches */ found_non_default_fallback += - (ds->dir_port == 60093 ? + (ds->ipv4_dirport == 60093 ? 1 : 0) ); tt_int_op(found_non_default_fallback, OP_EQ, 1); @@ -3403,7 +3617,7 @@ test_config_adding_dir_servers(void *arg) ds, /* increment the found counter if dir_port matches */ found_default_fallback += - (ds->dir_port == 60099 ? + (ds->ipv4_dirport == 60099 ? 1 : 0) ); tt_int_op(found_default_fallback, OP_EQ, 0); @@ -3469,7 +3683,7 @@ test_config_adding_dir_servers(void *arg) ds, /* increment the found counter if dir_port matches */ found_D0 += - (ds->dir_port == 60090 ? + (ds->ipv4_dirport == 60090 ? 1 : 0) ); tt_int_op(found_D0, OP_EQ, 0); @@ -3481,7 +3695,7 @@ test_config_adding_dir_servers(void *arg) ds, /* increment the found counter if dir_port matches */ found_B1 += - (ds->dir_port == 60091 ? + (ds->ipv4_dirport == 60091 ? 1 : 0) ); tt_int_op(found_B1, OP_EQ, 0); @@ -3493,7 +3707,7 @@ test_config_adding_dir_servers(void *arg) ds, /* increment the found counter if dir_port matches */ found_A2 += - (ds->dir_port == 60092 ? + (ds->ipv4_dirport == 60092 ? 1 : 0) ); tt_int_op(found_A2, OP_EQ, 0); @@ -3520,7 +3734,7 @@ test_config_adding_dir_servers(void *arg) ds, /* increment the found counter if dir_port matches */ found_D0 += - (ds->dir_port == 60090 ? + (ds->ipv4_dirport == 60090 ? 1 : 0) ); tt_int_op(found_D0, OP_EQ, 0); @@ -3532,7 +3746,7 @@ test_config_adding_dir_servers(void *arg) ds, /* increment the found counter if dir_port matches */ found_B1 += - (ds->dir_port == 60091 ? + (ds->ipv4_dirport == 60091 ? 1 : 0) ); tt_int_op(found_B1, OP_EQ, 0); @@ -3544,7 +3758,7 @@ test_config_adding_dir_servers(void *arg) ds, /* increment the found counter if dir_port matches */ found_A2 += - (ds->dir_port == 60092 ? + (ds->ipv4_dirport == 60092 ? 1 : 0) ); tt_int_op(found_A2, OP_EQ, 0); @@ -3556,7 +3770,7 @@ test_config_adding_dir_servers(void *arg) ds, /* increment the found counter if dir_port matches */ found_non_default_fallback += - (ds->dir_port == 60093 ? + (ds->ipv4_dirport == 60093 ? 1 : 0) ); tt_int_op(found_non_default_fallback, OP_EQ, 0); @@ -3568,7 +3782,7 @@ test_config_adding_dir_servers(void *arg) ds, /* increment the found counter if dir_port matches */ found_default_fallback += - (ds->dir_port == 60099 ? + (ds->ipv4_dirport == 60099 ? 1 : 0) ); tt_int_op(found_default_fallback, OP_EQ, 1); @@ -3647,16 +3861,17 @@ test_config_default_dir_servers(void *arg) or_options_free(opts); } -static int mock_router_pick_published_address_result = 0; +static bool mock_relay_find_addr_to_publish_result = true; -static int -mock_router_pick_published_address(const or_options_t *options, - uint32_t *addr, int cache_only) +static bool +mock_relay_find_addr_to_publish(const or_options_t *options, int family, + int flags, tor_addr_t *addr_out) { - (void)options; - (void)addr; - (void)cache_only; - return mock_router_pick_published_address_result; + (void) options; + (void) family; + (void) flags; + (void) addr_out; + return mock_relay_find_addr_to_publish_result; } static int mock_router_my_exit_policy_is_reject_star_result = 0; @@ -3692,11 +3907,11 @@ test_config_directory_fetch(void *arg) or_options_t *options = options_new(); routerinfo_t routerinfo; memset(&routerinfo, 0, sizeof(routerinfo)); - mock_router_pick_published_address_result = -1; + mock_relay_find_addr_to_publish_result = false; mock_router_my_exit_policy_is_reject_star_result = 1; mock_advertised_server_mode_result = 0; mock_router_get_my_routerinfo_result = NULL; - MOCK(router_pick_published_address, mock_router_pick_published_address); + MOCK(relay_find_addr_to_publish, mock_relay_find_addr_to_publish); MOCK(router_my_exit_policy_is_reject_star, mock_router_my_exit_policy_is_reject_star); MOCK(advertised_server_mode, mock_advertised_server_mode); @@ -3752,14 +3967,14 @@ test_config_directory_fetch(void *arg) options = options_new(); options->ORPort_set = 1; - mock_router_pick_published_address_result = -1; + mock_relay_find_addr_to_publish_result = false; tt_assert(server_mode(options) == 1); tt_assert(public_server_mode(options) == 1); tt_int_op(dirclient_fetches_from_authorities(options), OP_EQ, 1); tt_int_op(networkstatus_consensus_can_use_multiple_directories(options), OP_EQ, 0); - mock_router_pick_published_address_result = 0; + mock_relay_find_addr_to_publish_result = true; tt_assert(server_mode(options) == 1); tt_assert(public_server_mode(options) == 1); tt_int_op(dirclient_fetches_from_authorities(options), OP_EQ, 0); @@ -3773,7 +3988,7 @@ test_config_directory_fetch(void *arg) options = options_new(); options->ORPort_set = 1; options->ExitRelay = 1; - mock_router_pick_published_address_result = 0; + mock_relay_find_addr_to_publish_result = true; mock_router_my_exit_policy_is_reject_star_result = 0; mock_advertised_server_mode_result = 1; mock_router_get_my_routerinfo_result = &routerinfo; @@ -3788,7 +4003,7 @@ test_config_directory_fetch(void *arg) OP_EQ, 0); options->RefuseUnknownExits = 0; - mock_router_pick_published_address_result = 0; + mock_relay_find_addr_to_publish_result = true; tt_assert(server_mode(options) == 1); tt_assert(public_server_mode(options) == 1); tt_int_op(dirclient_fetches_from_authorities(options), OP_EQ, 0); @@ -3805,11 +4020,11 @@ test_config_directory_fetch(void *arg) options->DirPort_set = 1; options->ORPort_set = 1; options->DirCache = 1; - mock_router_pick_published_address_result = 0; + mock_relay_find_addr_to_publish_result = true; mock_router_my_exit_policy_is_reject_star_result = 1; mock_advertised_server_mode_result = 1; - routerinfo.dir_port = 1; + routerinfo.ipv4_dirport = 1; mock_router_get_my_routerinfo_result = &routerinfo; tt_assert(server_mode(options) == 1); tt_assert(public_server_mode(options) == 1); @@ -3818,7 +4033,7 @@ test_config_directory_fetch(void *arg) OP_EQ, 0); mock_advertised_server_mode_result = 0; - routerinfo.dir_port = 1; + routerinfo.ipv4_dirport = 1; mock_router_get_my_routerinfo_result = &routerinfo; tt_assert(server_mode(options) == 1); tt_assert(public_server_mode(options) == 1); @@ -3835,7 +4050,7 @@ test_config_directory_fetch(void *arg) OP_EQ, 0); mock_advertised_server_mode_result = 1; - routerinfo.dir_port = 0; + routerinfo.ipv4_dirport = 0; routerinfo.supports_tunnelled_dir_requests = 0; mock_router_get_my_routerinfo_result = &routerinfo; tt_assert(server_mode(options) == 1); @@ -3845,7 +4060,7 @@ test_config_directory_fetch(void *arg) OP_EQ, 0); mock_advertised_server_mode_result = 1; - routerinfo.dir_port = 1; + routerinfo.ipv4_dirport = 1; routerinfo.supports_tunnelled_dir_requests = 1; mock_router_get_my_routerinfo_result = &routerinfo; tt_assert(server_mode(options) == 1); @@ -3856,7 +4071,7 @@ test_config_directory_fetch(void *arg) done: or_options_free(options); - UNMOCK(router_pick_published_address); + UNMOCK(relay_find_addr_to_publish); UNMOCK(router_get_my_routerinfo); UNMOCK(advertised_server_mode); UNMOCK(router_my_exit_policy_is_reject_star); @@ -4160,8 +4375,6 @@ test_config_parse_port_config__ports__ports_given(void *data) /* Test entry port defaults as initialised in port_parse_config */ tt_int_op(port_cfg->entry_cfg.dns_request, OP_EQ, 1); tt_int_op(port_cfg->entry_cfg.ipv4_traffic, OP_EQ, 1); - tt_int_op(port_cfg->entry_cfg.ipv6_traffic, OP_EQ, 1); - tt_int_op(port_cfg->entry_cfg.prefer_ipv6, OP_EQ, 1); tt_int_op(port_cfg->entry_cfg.onion_traffic, OP_EQ, 1); tt_int_op(port_cfg->entry_cfg.cache_ipv4_answers, OP_EQ, 0); tt_int_op(port_cfg->entry_cfg.prefer_ipv6_virtaddr, OP_EQ, 1); @@ -4949,6 +5162,44 @@ test_config_parse_port_config__ports__server_options(void *data) 0, CL_PORT_SERVER_OPTIONS); tt_int_op(ret, OP_EQ, -1); + /* Default address is IPv4 but pass IPv6Only flag. Should be ignored. */ + config_free_lines(config_port_invalid); config_port_invalid = NULL; + SMARTLIST_FOREACH(slout,port_cfg_t *,pf,port_cfg_free(pf)); + smartlist_clear(slout); + config_port_invalid = mock_config_line("ORPort", "9050 IPv6Only"); + ret = port_parse_config(slout, config_port_invalid, "ORPort", 0, + "127.0.0.1", 0, CL_PORT_SERVER_OPTIONS); + tt_int_op(ret, OP_EQ, 0); + + /* Default address is IPv6 but pass IPv4Only flag. Should be ignored. */ + config_free_lines(config_port_invalid); config_port_invalid = NULL; + SMARTLIST_FOREACH(slout,port_cfg_t *,pf,port_cfg_free(pf)); + smartlist_clear(slout); + config_port_invalid = mock_config_line("ORPort", "9050 IPv4Only"); + ret = port_parse_config(slout, config_port_invalid, "ORPort", 0, + "[::]", 0, CL_PORT_SERVER_OPTIONS); + tt_int_op(ret, OP_EQ, 0); + + /* Explicit address is IPv6 but pass IPv4Only flag. Should error. */ + config_free_lines(config_port_invalid); config_port_invalid = NULL; + SMARTLIST_FOREACH(slout,port_cfg_t *,pf,port_cfg_free(pf)); + smartlist_clear(slout); + config_port_invalid = mock_config_line("ORPort", + "[4242::4242]:9050 IPv4Only"); + ret = port_parse_config(slout, config_port_invalid, "ORPort", 0, + "[::]", 0, CL_PORT_SERVER_OPTIONS); + tt_int_op(ret, OP_EQ, -1); + + /* Explicit address is IPv4 but pass IPv6Only flag. Should error. */ + config_free_lines(config_port_invalid); config_port_invalid = NULL; + SMARTLIST_FOREACH(slout,port_cfg_t *,pf,port_cfg_free(pf)); + smartlist_clear(slout); + config_port_invalid = mock_config_line("ORPort", + "1.2.3.4:9050 IPv6Only"); + ret = port_parse_config(slout, config_port_invalid, "ORPort", 0, + "127.0.0.1", 0, CL_PORT_SERVER_OPTIONS); + tt_int_op(ret, OP_EQ, -1); + done: if (slout) SMARTLIST_FOREACH(slout,port_cfg_t *,pf,port_cfg_free(pf)); @@ -4968,17 +5219,17 @@ test_config_get_first_advertised(void *data) const tor_addr_t *addr; // no ports are configured? We get NULL. - port = get_first_advertised_port_by_type_af(CONN_TYPE_OR_LISTENER, + port = portconf_get_first_advertised_port(CONN_TYPE_OR_LISTENER, AF_INET); tt_int_op(port, OP_EQ, 0); - addr = get_first_advertised_addr_by_type_af(CONN_TYPE_OR_LISTENER, + addr = portconf_get_first_advertised_addr(CONN_TYPE_OR_LISTENER, AF_INET); tt_ptr_op(addr, OP_EQ, NULL); - port = get_first_advertised_port_by_type_af(CONN_TYPE_OR_LISTENER, + port = portconf_get_first_advertised_port(CONN_TYPE_OR_LISTENER, AF_INET6); tt_int_op(port, OP_EQ, 0); - addr = get_first_advertised_addr_by_type_af(CONN_TYPE_OR_LISTENER, + addr = portconf_get_first_advertised_addr(CONN_TYPE_OR_LISTENER, AF_INET6); tt_ptr_op(addr, OP_EQ, NULL); @@ -4992,27 +5243,27 @@ test_config_get_first_advertised(void *data) tt_assert(r == 0); // UNSPEC gets us nothing. - port = get_first_advertised_port_by_type_af(CONN_TYPE_OR_LISTENER, + port = portconf_get_first_advertised_port(CONN_TYPE_OR_LISTENER, AF_UNSPEC); tt_int_op(port, OP_EQ, 0); - addr = get_first_advertised_addr_by_type_af(CONN_TYPE_OR_LISTENER, + addr = portconf_get_first_advertised_addr(CONN_TYPE_OR_LISTENER, AF_UNSPEC); tt_ptr_op(addr, OP_EQ, NULL); // Try AF_INET. - port = get_first_advertised_port_by_type_af(CONN_TYPE_OR_LISTENER, + port = portconf_get_first_advertised_port(CONN_TYPE_OR_LISTENER, AF_INET); tt_int_op(port, OP_EQ, 9911); - addr = get_first_advertised_addr_by_type_af(CONN_TYPE_OR_LISTENER, + addr = portconf_get_first_advertised_addr(CONN_TYPE_OR_LISTENER, AF_INET); tt_ptr_op(addr, OP_NE, NULL); tt_str_op(fmt_addrport(addr,port), OP_EQ, "5.6.7.8:9911"); // Try AF_INET6 - port = get_first_advertised_port_by_type_af(CONN_TYPE_OR_LISTENER, + port = portconf_get_first_advertised_port(CONN_TYPE_OR_LISTENER, AF_INET6); tt_int_op(port, OP_EQ, 8080); - addr = get_first_advertised_addr_by_type_af(CONN_TYPE_OR_LISTENER, + addr = portconf_get_first_advertised_addr(CONN_TYPE_OR_LISTENER, AF_INET6); tt_ptr_op(addr, OP_NE, NULL); tt_str_op(fmt_addrport(addr,port), OP_EQ, "[1234::5678]:8080"); @@ -6243,9 +6494,14 @@ test_config_getinfo_config_names(void *arg) tor_free(answer); } +#ifndef COCCI #define CONFIG_TEST(name, flags) \ { #name, test_config_ ## name, flags, NULL, NULL } +#define CONFIG_TEST_SETUP(suffix, name, flags, setup, setup_data) \ + { #name#suffix, test_config_ ## name, flags, setup, setup_data } +#endif + struct testcase_t config_tests[] = { CONFIG_TEST(adding_trusted_dir_server, TT_FORK), CONFIG_TEST(adding_fallback_dir_server, TT_FORK), @@ -6256,7 +6512,11 @@ struct testcase_t config_tests[] = { CONFIG_TEST(adding_dir_servers, TT_FORK), CONFIG_TEST(default_dir_servers, TT_FORK), CONFIG_TEST(default_fallback_dirs, 0), - CONFIG_TEST(resolve_my_address, TT_FORK), + CONFIG_TEST_SETUP(_v4, find_my_address, TT_FORK, + &passthrough_setup, &addr_param_v4), + CONFIG_TEST_SETUP(_v6, find_my_address, TT_FORK, + &passthrough_setup, &addr_param_v6), + CONFIG_TEST(find_my_address_mixed, TT_FORK), CONFIG_TEST(addressmap, 0), CONFIG_TEST(parse_bridge_line, 0), CONFIG_TEST(parse_transport_options_line, 0), diff --git a/src/test/test_connection.c b/src/test/test_connection.c index fca4d884bb..954aeb82e3 100644 --- a/src/test/test_connection.c +++ b/src/test/test_connection.c @@ -10,6 +10,7 @@ #include "core/or/or.h" #include "test/test.h" +#include "app/config/config.h" #include "app/config/or_options_st.h" #include "core/mainloop/connection.h" #include "core/or/connection_edge.h" @@ -882,10 +883,8 @@ mock_node_get_mutable_by_id(const char *digest) test_node.ri = &node_ri; memset(test_node.identity, 'c', sizeof(test_node.identity)); - tor_addr_t ipv4_addr; - tor_addr_parse(&ipv4_addr, "18.0.0.1"); - node_ri.addr = tor_addr_to_ipv4h(&ipv4_addr); - node_ri.or_port = 1; + tor_addr_parse(&node_ri.ipv4_addr, "18.0.0.1"); + node_ri.ipv4_orport = 1; return &test_node; } @@ -912,7 +911,8 @@ test_failed_orconn_tracker(void *arg) /* Prepare the OR connection that will be used in this test */ or_connection_t or_conn; - tt_int_op(AF_INET,OP_EQ, tor_addr_parse(&or_conn.real_addr, "18.0.0.1")); + tt_int_op(AF_INET,OP_EQ, tor_addr_parse(&or_conn.canonical_orport.addr, + "18.0.0.1")); tt_int_op(AF_INET,OP_EQ, tor_addr_parse(&or_conn.base_.addr, "18.0.0.1")); or_conn.base_.port = 1; memset(or_conn.identity_digest, 'c', sizeof(or_conn.identity_digest)); @@ -963,6 +963,114 @@ test_failed_orconn_tracker(void *arg) ; } +static void +test_conn_describe(void *arg) +{ + (void)arg; + or_options_t *options = get_options_mutable(); + options->SafeLogging_ = SAFELOG_SCRUB_ALL; + + // Let's start with a listener connection since they're simple. + connection_t *conn = connection_new(CONN_TYPE_OR_LISTENER, AF_INET); + tor_addr_parse(&conn->addr, "44.22.11.11"); + conn->port = 80; + tt_str_op(connection_describe(conn), OP_EQ, + "OR listener connection (ready) on 44.22.11.11:80"); + // If the address is unspec, we should still work. + tor_addr_make_unspec(&conn->addr); + tt_str_op(connection_describe(conn), OP_EQ, + "OR listener connection (ready) on <unset>:80"); + // Try making the address null. + tor_addr_make_null(&conn->addr, AF_INET); + tt_str_op(connection_describe(conn), OP_EQ, + "OR listener connection (ready) on 0.0.0.0:80"); + // What if the address is uninitialized? (This can happen if we log about the + // connection before we set the address.) + memset(&conn->addr, 0, sizeof(conn->addr)); + tt_str_op(connection_describe(conn), OP_EQ, + "OR listener connection (ready) on <unset>:80"); + connection_free_minimal(conn); + + // Try a unix socket. + conn = connection_new(CONN_TYPE_CONTROL_LISTENER, AF_UNIX); + conn->address = tor_strdup("/a/path/that/could/exist"); + tt_str_op(connection_describe(conn), OP_EQ, + "Control listener connection (ready) on /a/path/that/could/exist"); + connection_free_minimal(conn); + + // Try an IPv6 address. + conn = connection_new(CONN_TYPE_AP_LISTENER, AF_INET6); + tor_addr_parse(&conn->addr, "ff00::3"); + conn->port = 9050; + tt_str_op(connection_describe(conn), OP_EQ, + "Socks listener connection (ready) on [ff00::3]:9050"); + connection_free_minimal(conn); + + // Now let's mess with exit connections. They have some special issues. + options->SafeLogging_ = SAFELOG_SCRUB_NONE; + conn = connection_new(CONN_TYPE_EXIT, AF_INET); + // If address and state are unset, we should say SOMETHING. + tt_str_op(connection_describe(conn), OP_EQ, + "Exit connection (uninitialized) to <unset> (DNS lookup pending)"); + // Now suppose that the address is set but we haven't resolved the hostname. + conn->port = 443; + conn->address = tor_strdup("www.torproject.org"); + conn->state = EXIT_CONN_STATE_RESOLVING; + tt_str_op(connection_describe(conn), OP_EQ, + "Exit connection (waiting for dest info) to " + "www.torproject.org:443 (DNS lookup pending)"); + // Now give it a hostname! + tor_addr_parse(&conn->addr, "192.168.8.8"); + conn->state = EXIT_CONN_STATE_OPEN; + tt_str_op(connection_describe(conn), OP_EQ, + "Exit connection (open) to 192.168.8.8:443"); + // But what if safelogging is on? + options->SafeLogging_ = SAFELOG_SCRUB_RELAY; + tt_str_op(connection_describe(conn), OP_EQ, + "Exit connection (open) to [scrubbed]"); + connection_free_minimal(conn); + + // Now at last we look at OR addresses, which are complicated. + conn = connection_new(CONN_TYPE_OR, AF_INET6); + conn->state = OR_CONN_STATE_OPEN; + conn->port = 8080; + tor_addr_parse(&conn->addr, "[ffff:3333:1111::2]"); + // This should get scrubbed, since the lack of a set ID means we might be + // talking to a client. + tt_str_op(connection_describe(conn), OP_EQ, + "OR connection (open) with [scrubbed]"); + // But now suppose we aren't safelogging? We'll get the address then. + options->SafeLogging_ = SAFELOG_SCRUB_NONE; + tt_str_op(connection_describe(conn), OP_EQ, + "OR connection (open) with [ffff:3333:1111::2]:8080"); + // Suppose we have an ID, so we know it isn't a client. + TO_OR_CONN(conn)->identity_digest[3] = 7; + options->SafeLogging_ = SAFELOG_SCRUB_RELAY; // back to safelogging. + tt_str_op(connection_describe(conn), OP_EQ, + "OR connection (open) with [ffff:3333:1111::2]:8080 " + "ID=0000000700000000000000000000000000000000"); + // Add a 'canonical address' that is the same as the one we have. + tor_addr_parse(&TO_OR_CONN(conn)->canonical_orport.addr, + "[ffff:3333:1111::2]"); + TO_OR_CONN(conn)->canonical_orport.port = 8080; + tt_str_op(connection_describe(conn), OP_EQ, + "OR connection (open) with [ffff:3333:1111::2]:8080 " + "ID=0000000700000000000000000000000000000000"); + // Add a different 'canonical address' + tor_addr_parse(&TO_OR_CONN(conn)->canonical_orport.addr, + "[ffff:3333:1111::8]"); + tt_str_op(connection_describe(conn), OP_EQ, + "OR connection (open) with [ffff:3333:1111::2]:8080 " + "ID=0000000700000000000000000000000000000000 " + "canonical_addr=[ffff:3333:1111::8]:8080"); + + // Clear identity_digest so that free_minimal won't complain. + memset(TO_OR_CONN(conn)->identity_digest, 0, DIGEST_LEN); + + done: + connection_free_minimal(conn); +} + #ifndef COCCI #define CONNECTION_TESTCASE(name, fork, setup) \ { #name, test_conn_##name, fork, &setup, NULL } @@ -997,5 +1105,6 @@ struct testcase_t connection_tests[] = { //CONNECTION_TESTCASE(func_suffix, TT_FORK, setup_func_pair), { "failed_orconn_tracker", test_failed_orconn_tracker, TT_FORK, NULL, NULL }, + { "describe", test_conn_describe, TT_FORK, NULL, NULL }, END_OF_TESTCASES }; diff --git a/src/test/test_controller.c b/src/test/test_controller.c index a69ec17db8..49efeb5f88 100644 --- a/src/test/test_controller.c +++ b/src/test/test_controller.c @@ -19,6 +19,7 @@ #include "feature/rend/rendservice.h" #include "feature/nodelist/authcert.h" #include "feature/nodelist/nodelist.h" +#include "feature/stats/rephist.h" #include "test/test.h" #include "test/test_helpers.h" #include "lib/net/resolve.h" @@ -2112,6 +2113,91 @@ test_control_getconf(void *arg) smartlist_free(reply_strs); } +static int +mock_rep_hist_get_circuit_handshake(uint16_t type) +{ + int ret; + + switch (type) { + case ONION_HANDSHAKE_TYPE_NTOR: + ret = 80; + break; + case ONION_HANDSHAKE_TYPE_TAP: + ret = 86; + break; + default: + ret = 0; + break; + } + + return ret; +} + +static void +test_stats(void *arg) +{ + /* We just need one of these to pass, it doesn't matter what's in it */ + control_connection_t dummy; + /* Get results out */ + char *answer = NULL; + const char *errmsg = NULL; + + (void) arg; + + /* We need these for returning the (mock) rephist. */ + MOCK(rep_hist_get_circuit_handshake_requested, + mock_rep_hist_get_circuit_handshake); + MOCK(rep_hist_get_circuit_handshake_assigned, + mock_rep_hist_get_circuit_handshake); + + /* NTor tests */ + getinfo_helper_rephist(&dummy, "stats/ntor/requested", + &answer, &errmsg); + tt_ptr_op(answer, OP_NE, NULL); + tt_ptr_op(errmsg, OP_EQ, NULL); + tt_str_op(answer, OP_EQ, "80"); + tor_free(answer); + errmsg = NULL; + + getinfo_helper_rephist(&dummy, "stats/ntor/assigned", + &answer, &errmsg); + tt_ptr_op(answer, OP_NE, NULL); + tt_ptr_op(errmsg, OP_EQ, NULL); + tt_str_op(answer, OP_EQ, "80"); + tor_free(answer); + errmsg = NULL; + + /* TAP tests */ + getinfo_helper_rephist(&dummy, "stats/tap/requested", + &answer, &errmsg); + tt_ptr_op(answer, OP_NE, NULL); + tt_ptr_op(errmsg, OP_EQ, NULL); + tt_str_op(answer, OP_EQ, "86"); + tor_free(answer); + errmsg = NULL; + + getinfo_helper_rephist(&dummy, "stats/tap/assigned", + &answer, &errmsg); + tt_ptr_op(answer, OP_NE, NULL); + tt_ptr_op(errmsg, OP_EQ, NULL); + tt_str_op(answer, OP_EQ, "86"); + tor_free(answer); + errmsg = NULL; + + getinfo_helper_rephist(&dummy, "stats/tap/onion_circuits_ddosed", + &answer, &errmsg); + tt_ptr_op(answer, OP_EQ, NULL); + tt_str_op(errmsg, OP_EQ, "Unrecognized handshake type"); + errmsg = NULL; + + done: + UNMOCK(rep_hist_get_circuit_handshake_requested); + UNMOCK(rep_hist_get_circuit_handshake_assigned); + tor_free(answer); + + return; +} + #ifndef COCCI #define PARSER_TEST(type) \ { "parse/" #type, test_controller_parse_cmd, 0, &passthrough_setup, \ @@ -2146,5 +2232,6 @@ struct testcase_t controller_tests[] = { { "getinfo_md_all", test_getinfo_md_all, 0, NULL, NULL }, { "control_reply", test_control_reply, 0, NULL, NULL }, { "control_getconf", test_control_getconf, 0, NULL, NULL }, + { "stats", test_stats, 0, NULL, NULL }, END_OF_TESTCASES }; diff --git a/src/test/test_crypto_slow.c b/src/test/test_crypto_slow.c index 56319f2c72..1702427b08 100644 --- a/src/test/test_crypto_slow.c +++ b/src/test/test_crypto_slow.c @@ -342,7 +342,7 @@ test_crypto_scrypt_vectors(void *arg) #endif /* Test vectors from - http://tools.ietf.org/html/draft-josefsson-scrypt-kdf-00 section 11. + https://tools.ietf.org/html/draft-josefsson-scrypt-kdf-00 section 11. Note that the names of 'r' and 'N' are switched in that section. Or possibly in libscrypt. diff --git a/src/test/test_dir.c b/src/test/test_dir.c index f6a21c804e..ab0315aa2d 100644 --- a/src/test/test_dir.c +++ b/src/test/test_dir.c @@ -203,9 +203,9 @@ basic_routerinfo_new(const char *nickname, uint32_t ipv4_addr, r1->nickname = tor_strdup(nickname); r1->platform = tor_strdup(platform); - r1->addr = ipv4_addr; - r1->or_port = or_port; - r1->dir_port = dir_port; + tor_addr_from_ipv4h(&r1->ipv4_addr, ipv4_addr); + r1->ipv4_orport = or_port; + r1->ipv4_dirport = dir_port; r1->supports_tunnelled_dir_requests = 1; router_set_rsa_onion_pkey(pk1, &r1->onion_pkey, &r1->onion_pkey_len); @@ -236,8 +236,8 @@ get_new_router_line(const routerinfo_t *r1) tor_asprintf(&line, "router %s %s %d 0 %d\n", - r1->nickname, fmt_addr32(r1->addr), - r1->or_port, r1->dir_port); + r1->nickname, fmt_addr(&r1->ipv4_addr), + r1->ipv4_orport, r1->ipv4_dirport); tor_assert(line); return line; @@ -634,9 +634,9 @@ setup_dir_formats_options(const char *arg, or_options_t *options) STMT_BEGIN \ tt_assert(r1); \ tt_assert(rp1); \ - tt_int_op(rp1->addr,OP_EQ, r1->addr); \ - tt_int_op(rp1->or_port,OP_EQ, r1->or_port); \ - tt_int_op(rp1->dir_port,OP_EQ, r1->dir_port); \ + tt_assert(tor_addr_eq(&rp1->ipv4_addr, &r1->ipv4_addr)); \ + tt_int_op(rp1->ipv4_orport,OP_EQ, r1->ipv4_orport); \ + tt_int_op(rp1->ipv4_dirport,OP_EQ, r1->ipv4_dirport); \ tt_int_op(rp1->bandwidthrate,OP_EQ, r1->bandwidthrate); \ tt_int_op(rp1->bandwidthburst,OP_EQ, r1->bandwidthburst); \ tt_int_op(rp1->bandwidthcapacity,OP_EQ, r1->bandwidthcapacity); \ @@ -714,7 +714,7 @@ test_dir_formats_rsa(void *arg) options->ContactInfo = tor_strdup("Magri White " "<magri@elsewhere.example.com>"); - setup_mock_configured_ports(r1->or_port, r1->dir_port); + setup_mock_configured_ports(r1->ipv4_orport, r1->ipv4_dirport); buf = router_dump_router_to_string(r1, r1->identity_pkey, NULL, NULL, NULL); tt_assert(buf); @@ -763,7 +763,7 @@ test_dir_formats_rsa(void *arg) tt_str_op(buf,OP_EQ, buf2); tor_free(buf); - setup_mock_configured_ports(r1->or_port, r1->dir_port); + setup_mock_configured_ports(r1->ipv4_orport, r1->ipv4_dirport); buf = router_dump_router_to_string(r1, r1->identity_pkey, NULL, NULL, NULL); tt_assert(buf); @@ -797,7 +797,7 @@ test_dir_formats_rsa(void *arg) MOCK(tor_cert_dup, mock_tor_cert_dup_null); /* Fake just enough of an ORPort and DirPort to get by */ - setup_mock_configured_ports(r1->or_port, r1->dir_port); + setup_mock_configured_ports(r1->ipv4_orport, r1->ipv4_dirport); /* Test some of the low-level static functions. */ e1 = router_build_fresh_signed_extrainfo(r1); @@ -966,7 +966,7 @@ test_dir_formats_rsa_ed25519(void *arg) smartlist_add(r2->exit_policy, ex2); /* Fake just enough of an ORPort to get by */ - setup_mock_configured_ports(r2->or_port, 0); + setup_mock_configured_ports(r2->ipv4_orport, 0); buf = router_dump_router_to_string(r2, r2->identity_pkey, r2_onion_pkey, @@ -1062,7 +1062,7 @@ test_dir_formats_rsa_ed25519(void *arg) tt_str_op(buf, OP_EQ, buf2); tor_free(buf); - setup_mock_configured_ports(r2->or_port, 0); + setup_mock_configured_ports(r2->ipv4_orport, 0); buf = router_dump_router_to_string(r2, r2->identity_pkey, NULL, NULL, NULL); tt_assert(buf); @@ -1108,7 +1108,7 @@ test_dir_formats_rsa_ed25519(void *arg) MOCK(get_current_curve25519_keypair, mock_get_current_curve25519_keypair); /* Fake just enough of an ORPort to get by */ - setup_mock_configured_ports(r2->or_port, 0); + setup_mock_configured_ports(r2->ipv4_orport, 0); /* Test the high-level interface. */ rv = router_build_fresh_descriptor(&r2_out, &e2); @@ -3057,9 +3057,9 @@ test_same_voter(networkstatus_voter_info_t *v1, tt_str_op(v1->nickname,OP_EQ, v2->nickname); tt_mem_op(v1->identity_digest,OP_EQ, v2->identity_digest, DIGEST_LEN); tt_str_op(v1->address,OP_EQ, v2->address); - tt_int_op(v1->addr,OP_EQ, v2->addr); - tt_int_op(v1->dir_port,OP_EQ, v2->dir_port); - tt_int_op(v1->or_port,OP_EQ, v2->or_port); + tt_assert(tor_addr_eq(&v1->ipv4_addr, &v2->ipv4_addr)); + tt_int_op(v1->ipv4_dirport,OP_EQ, v2->ipv4_dirport); + tt_int_op(v1->ipv4_orport,OP_EQ, v2->ipv4_orport); tt_str_op(v1->contact,OP_EQ, v2->contact); tt_mem_op(v1->vote_digest,OP_EQ, v2->vote_digest, DIGEST_LEN); done: @@ -3149,9 +3149,9 @@ test_vrs_for_v3ns(vote_routerstatus_t *vrs, int voter, time_t now) "\x3\x3\x3\x3", DIGEST_LEN); tt_mem_op(rs->descriptor_digest,OP_EQ, "NNNNNNNNNNNNNNNNNNNN", DIGEST_LEN); - tt_int_op(rs->addr,OP_EQ, 0x99008801); - tt_int_op(rs->or_port,OP_EQ, 443); - tt_int_op(rs->dir_port,OP_EQ, 8000); + tt_assert(tor_addr_eq_ipv4h(&rs->ipv4_addr, 0x99008801)); + tt_int_op(rs->ipv4_orport,OP_EQ, 443); + tt_int_op(rs->ipv4_dirport,OP_EQ, 8000); /* no flags except "running" (16) and "v2dir" (64) and "valid" (128) */ tt_u64_op(vrs->flags, OP_EQ, UINT64_C(0xd0)); } else if (tor_memeq(rs->identity_digest, @@ -3171,9 +3171,9 @@ test_vrs_for_v3ns(vote_routerstatus_t *vrs, int voter, time_t now) tt_str_op(rs->nickname,OP_EQ, "router1"); } tt_mem_op(rs->descriptor_digest,OP_EQ, "MMMMMMMMMMMMMMMMMMMM", DIGEST_LEN); - tt_int_op(rs->addr,OP_EQ, 0x99009901); - tt_int_op(rs->or_port,OP_EQ, 443); - tt_int_op(rs->dir_port,OP_EQ, 0); + tt_assert(tor_addr_eq_ipv4h(&rs->ipv4_addr, 0x99009901)); + tt_int_op(rs->ipv4_orport,OP_EQ, 443); + tt_int_op(rs->ipv4_dirport,OP_EQ, 0); tor_addr_parse(&addr_ipv6, "[1:2:3::4]"); tt_assert(tor_addr_eq(&rs->ipv6_addr, &addr_ipv6)); tt_int_op(rs->ipv6_orport,OP_EQ, 4711); @@ -3265,9 +3265,9 @@ test_routerstatus_for_v3ns(routerstatus_t *rs, time_t now) tt_str_op(rs->nickname,OP_EQ, "router1"); tt_mem_op(rs->descriptor_digest,OP_EQ, "MMMMMMMMMMMMMMMMMMMM", DIGEST_LEN); tt_int_op(rs->published_on,OP_EQ, now-1000); - tt_int_op(rs->addr,OP_EQ, 0x99009901); - tt_int_op(rs->or_port,OP_EQ, 443); - tt_int_op(rs->dir_port,OP_EQ, 0); + tt_assert(tor_addr_eq_ipv4h(&rs->ipv4_addr, 0x99009901)); + tt_int_op(rs->ipv4_orport,OP_EQ, 443); + tt_int_op(rs->ipv4_dirport,OP_EQ, 0); tor_addr_parse(&addr_ipv6, "[1:2:3::4]"); tt_assert(tor_addr_eq(&rs->ipv6_addr, &addr_ipv6)); tt_int_op(rs->ipv6_orport,OP_EQ, 4711); @@ -3639,9 +3639,9 @@ test_a_networkstatus( voter = smartlist_get(v1->voters, 0); tt_str_op(voter->nickname,OP_EQ, "Voter1"); tt_str_op(voter->address,OP_EQ, "1.2.3.4"); - tt_int_op(voter->addr,OP_EQ, 0x01020304); - tt_int_op(voter->dir_port,OP_EQ, 80); - tt_int_op(voter->or_port,OP_EQ, 9000); + tt_assert(tor_addr_eq_ipv4h(&voter->ipv4_addr, 0x01020304)); + tt_int_op(voter->ipv4_dirport,OP_EQ, 80); + tt_int_op(voter->ipv4_orport,OP_EQ, 9000); tt_str_op(voter->contact,OP_EQ, "voter@example.com"); tt_assert(v1->cert); tt_assert(!crypto_pk_cmp_keys(sign_skey_1, v1->cert->signing_key)); @@ -4143,9 +4143,9 @@ gen_routerstatus_for_umbw(int idx, time_t now) strlcpy(rs->nickname, "router2", sizeof(rs->nickname)); memset(rs->identity_digest, 3, DIGEST_LEN); memset(rs->descriptor_digest, 78, DIGEST_LEN); - rs->addr = 0x99008801; - rs->or_port = 443; - rs->dir_port = 8000; + tor_addr_from_ipv4h(&rs->ipv4_addr, 0x99008801); + rs->ipv4_orport = 443; + rs->ipv4_dirport = 8000; /* all flags but running and valid cleared */ rs->is_flagged_running = 1; rs->is_valid = 1; @@ -4167,9 +4167,9 @@ gen_routerstatus_for_umbw(int idx, time_t now) strlcpy(rs->nickname, "router1", sizeof(rs->nickname)); memset(rs->identity_digest, 5, DIGEST_LEN); memset(rs->descriptor_digest, 77, DIGEST_LEN); - rs->addr = 0x99009901; - rs->or_port = 443; - rs->dir_port = 0; + tor_addr_from_ipv4h(&rs->ipv4_addr, 0x99009901); + rs->ipv4_orport = 443; + rs->ipv4_dirport = 0; tor_addr_parse(&addr_ipv6, "[1:2:3::4]"); tor_addr_copy(&rs->ipv6_addr, &addr_ipv6); rs->ipv6_orport = 4711; @@ -4193,9 +4193,9 @@ gen_routerstatus_for_umbw(int idx, time_t now) strlcpy(rs->nickname, "router3", sizeof(rs->nickname)); memset(rs->identity_digest, 0x33, DIGEST_LEN); memset(rs->descriptor_digest, 79, DIGEST_LEN); - rs->addr = 0xAA009901; - rs->or_port = 400; - rs->dir_port = 9999; + tor_addr_from_ipv4h(&rs->ipv4_addr, 0xAA009901); + rs->ipv4_orport = 400; + rs->ipv4_dirport = 9999; rs->is_authority = rs->is_exit = rs->is_stable = rs->is_fast = rs->is_flagged_running = rs->is_valid = rs->is_possible_guard = 1; @@ -4218,9 +4218,9 @@ gen_routerstatus_for_umbw(int idx, time_t now) strlcpy(rs->nickname, "router4", sizeof(rs->nickname)); memset(rs->identity_digest, 0x34, DIGEST_LEN); memset(rs->descriptor_digest, 47, DIGEST_LEN); - rs->addr = 0xC0000203; - rs->or_port = 500; - rs->dir_port = 1999; + tor_addr_from_ipv4h(&rs->ipv4_addr, 0xC0000203); + rs->ipv4_orport = 500; + rs->ipv4_dirport = 1999; /* all flags but running and valid cleared */ rs->is_flagged_running = 1; rs->is_valid = 1; @@ -4320,9 +4320,9 @@ test_vrs_for_umbw(vote_routerstatus_t *vrs, int voter, time_t now) "\x3\x3\x3\x3\x3\x3\x3\x3\x3\x3", DIGEST_LEN); tt_mem_op(rs->descriptor_digest,OP_EQ, "NNNNNNNNNNNNNNNNNNNN", DIGEST_LEN); - tt_int_op(rs->addr,OP_EQ, 0x99008801); - tt_int_op(rs->or_port,OP_EQ, 443); - tt_int_op(rs->dir_port,OP_EQ, 8000); + tt_assert(tor_addr_eq_ipv4h(&rs->ipv4_addr, 0x99008801)); + tt_int_op(rs->ipv4_orport,OP_EQ, 443); + tt_int_op(rs->ipv4_dirport,OP_EQ, 8000); tt_assert(rs->has_bandwidth); tt_assert(vrs->has_measured_bw); tt_int_op(rs->bandwidth_kb,OP_EQ, max_unmeasured_bw_kb / 2); @@ -4344,9 +4344,9 @@ test_vrs_for_umbw(vote_routerstatus_t *vrs, int voter, time_t now) "\x5\x5\x5\x5\x5\x5\x5\x5\x5\x5", DIGEST_LEN); tt_mem_op(rs->descriptor_digest,OP_EQ, "MMMMMMMMMMMMMMMMMMMM", DIGEST_LEN); - tt_int_op(rs->addr,OP_EQ, 0x99009901); - tt_int_op(rs->or_port,OP_EQ, 443); - tt_int_op(rs->dir_port,OP_EQ, 0); + tt_assert(tor_addr_eq_ipv4h(&rs->ipv4_addr, 0x99009901)); + tt_int_op(rs->ipv4_orport,OP_EQ, 443); + tt_int_op(rs->ipv4_dirport,OP_EQ, 0); tor_addr_parse(&addr_ipv6, "[1:2:3::4]"); tt_assert(tor_addr_eq(&rs->ipv6_addr, &addr_ipv6)); tt_int_op(rs->ipv6_orport,OP_EQ, 4711); @@ -4453,9 +4453,9 @@ test_routerstatus_for_umbw(routerstatus_t *rs, time_t now) tt_str_op(rs->nickname,OP_EQ, "router1"); tt_mem_op(rs->descriptor_digest,OP_EQ, "MMMMMMMMMMMMMMMMMMMM", DIGEST_LEN); tt_int_op(rs->published_on,OP_EQ, now-1000); - tt_int_op(rs->addr,OP_EQ, 0x99009901); - tt_int_op(rs->or_port,OP_EQ, 443); - tt_int_op(rs->dir_port,OP_EQ, 0); + tt_assert(tor_addr_eq_ipv4h(&rs->ipv4_addr, 0x99009901)); + tt_int_op(rs->ipv4_orport,OP_EQ, 443); + tt_int_op(rs->ipv4_dirport,OP_EQ, 0); tor_addr_parse(&addr_ipv6, "[1:2:3::4]"); tt_assert(tor_addr_eq(&rs->ipv6_addr, &addr_ipv6)); tt_int_op(rs->ipv6_orport,OP_EQ, 4711); @@ -4556,9 +4556,9 @@ test_dir_fmt_control_ns(void *arg) strlcpy(rs.nickname, "TetsuoMilk", sizeof(rs.nickname)); memcpy(rs.identity_digest, "Stately, plump Buck ", DIGEST_LEN); memcpy(rs.descriptor_digest, "Mulligan came up fro", DIGEST_LEN); - rs.addr = 0x20304050; - rs.or_port = 9001; - rs.dir_port = 9002; + tor_addr_from_ipv4h(&rs.ipv4_addr, 0x20304050); + rs.ipv4_orport = 9001; + rs.ipv4_dirport = 9002; rs.is_exit = 1; rs.is_fast = 1; rs.is_flagged_running = 1; @@ -4665,7 +4665,7 @@ reset_routerstatus(routerstatus_t *rs, hex_identity_digest, HEX_DIGEST_LEN); /* A zero address matches everything, so the address needs to be set. * But the specific value is irrelevant. */ - rs->addr = ipv4_addr; + tor_addr_from_ipv4h(&rs->ipv4_addr, ipv4_addr); } #define ROUTER_A_ID_STR "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" @@ -7273,8 +7273,8 @@ test_dir_dirserv_router_get_status(void *arg) /* Set up the routerinfo */ ri = tor_malloc_zero(sizeof(routerinfo_t)); - ri->addr = 0xc0a80001u; - ri->or_port = 9001; + tor_addr_from_ipv4h(&ri->ipv4_addr, 0xc0a80001u); + ri->ipv4_orport = 9001; ri->platform = tor_strdup("0.4.0.1-alpha"); ri->nickname = tor_strdup("Jessica"); ri->identity_pkey = crypto_pk_dup_key(pk); @@ -7352,8 +7352,8 @@ test_dir_dirserv_would_reject_router(void *arg) /* Set up the routerstatus */ memset(&rs, 0, sizeof(rs)); - rs.addr = 0xc0a80001u; - rs.or_port = 9001; + tor_addr_from_ipv4h(&rs.ipv4_addr, 0xc0a80001u); + rs.ipv4_orport = 9001; strlcpy(rs.nickname, "Nicole", sizeof(rs.nickname)); memcpy(rs.identity_digest, "Cloud nine is great ", DIGEST_LEN); diff --git a/src/test/test_dir_common.c b/src/test/test_dir_common.c index f2b4e8724b..6eb4fb6d43 100644 --- a/src/test/test_dir_common.c +++ b/src/test/test_dir_common.c @@ -97,9 +97,9 @@ dir_common_gen_routerstatus_for_v3ns(int idx, time_t now) strlcpy(rs->nickname, "router2", sizeof(rs->nickname)); memset(rs->identity_digest, TEST_DIR_ROUTER_ID_1, DIGEST_LEN); memset(rs->descriptor_digest, TEST_DIR_ROUTER_DD_1, DIGEST_LEN); - rs->addr = 0x99008801; - rs->or_port = 443; - rs->dir_port = 8000; + tor_addr_from_ipv4h(&rs->ipv4_addr, 0x99008801); + rs->ipv4_orport = 443; + rs->ipv4_dirport = 8000; /* all flags but running and v2dir cleared */ rs->is_flagged_running = 1; rs->is_v2_dir = 1; @@ -114,9 +114,9 @@ dir_common_gen_routerstatus_for_v3ns(int idx, time_t now) strlcpy(rs->nickname, "router1", sizeof(rs->nickname)); memset(rs->identity_digest, TEST_DIR_ROUTER_ID_2, DIGEST_LEN); memset(rs->descriptor_digest, TEST_DIR_ROUTER_DD_2, DIGEST_LEN); - rs->addr = 0x99009901; - rs->or_port = 443; - rs->dir_port = 0; + tor_addr_from_ipv4h(&rs->ipv4_addr, 0x99009901); + rs->ipv4_orport = 443; + rs->ipv4_dirport = 0; tor_addr_parse(&addr_ipv6, "[1:2:3::4]"); tor_addr_copy(&rs->ipv6_addr, &addr_ipv6); rs->ipv6_orport = 4711; @@ -132,9 +132,9 @@ dir_common_gen_routerstatus_for_v3ns(int idx, time_t now) strlcpy(rs->nickname, "router3", sizeof(rs->nickname)); memset(rs->identity_digest, TEST_DIR_ROUTER_ID_3, DIGEST_LEN); memset(rs->descriptor_digest, TEST_DIR_ROUTER_DD_3, DIGEST_LEN); - rs->addr = 0xAA009901; - rs->or_port = 400; - rs->dir_port = 9999; + tor_addr_from_ipv4h(&rs->ipv4_addr, 0xAA009901); + rs->ipv4_orport = 400; + rs->ipv4_dirport = 9999; rs->is_authority = rs->is_exit = rs->is_stable = rs->is_fast = rs->is_flagged_running = rs->is_valid = rs->is_v2_dir = rs->is_possible_guard = 1; @@ -148,9 +148,9 @@ dir_common_gen_routerstatus_for_v3ns(int idx, time_t now) strlcpy(rs->nickname, "router4", sizeof(rs->nickname)); memset(rs->identity_digest, TEST_DIR_ROUTER_ID_4, DIGEST_LEN); memset(rs->descriptor_digest, TEST_DIR_ROUTER_DD_4, DIGEST_LEN); - rs->addr = 0xC0000203; - rs->or_port = 500; - rs->dir_port = 1999; + tor_addr_from_ipv4h(&rs->ipv4_addr, 0xC0000203); + rs->ipv4_orport = 500; + rs->ipv4_dirport = 1999; rs->is_v2_dir = 1; /* Running flag (and others) cleared */ break; @@ -313,9 +313,9 @@ dir_common_construct_vote_1(networkstatus_t **vote, authority_cert_t *cert, voter = tor_malloc_zero(sizeof(networkstatus_voter_info_t)); voter->nickname = tor_strdup("Voter1"); voter->address = tor_strdup("1.2.3.4"); - voter->addr = 0x01020304; - voter->dir_port = 80; - voter->or_port = 9000; + tor_addr_from_ipv4h(&voter->ipv4_addr, 0x01020304); + voter->ipv4_dirport = 80; + voter->ipv4_orport = 9000; voter->contact = tor_strdup("voter@example.com"); crypto_pk_get_digest(cert->identity_key, voter->identity_digest); /* @@ -362,9 +362,9 @@ dir_common_construct_vote_2(networkstatus_t **vote, authority_cert_t *cert, voter = tor_malloc_zero(sizeof(networkstatus_voter_info_t)); voter->nickname = tor_strdup("Voter2"); voter->address = tor_strdup("2.3.4.5"); - voter->addr = 0x02030405; - voter->dir_port = 80; - voter->or_port = 9000; + tor_addr_from_ipv4h(&voter->ipv4_addr, 0x02030405); + voter->ipv4_dirport = 80; + voter->ipv4_orport = 9000; voter->contact = tor_strdup("voter@example.com"); crypto_pk_get_digest(cert->identity_key, voter->identity_digest); /* @@ -412,9 +412,9 @@ dir_common_construct_vote_3(networkstatus_t **vote, authority_cert_t *cert, voter = tor_malloc_zero(sizeof(networkstatus_voter_info_t)); voter->nickname = tor_strdup("Voter2"); voter->address = tor_strdup("3.4.5.6"); - voter->addr = 0x03040506; - voter->dir_port = 80; - voter->or_port = 9000; + tor_addr_from_ipv4h(&voter->ipv4_addr, 0x03040506); + voter->ipv4_dirport = 80; + voter->ipv4_orport = 9000; voter->contact = tor_strdup("voter@example.com"); crypto_pk_get_digest(cert->identity_key, voter->identity_digest); memset(voter->legacy_id_digest, (int)'A', DIGEST_LEN); diff --git a/src/test/test_dos.c b/src/test/test_dos.c index 527e5bbe7f..850bbef59b 100644 --- a/src/test/test_dos.c +++ b/src/test/test_dos.c @@ -66,9 +66,9 @@ test_dos_conn_creation(void *arg) /* Initialize test data */ or_connection_t or_conn; time_t now = 1281533250; /* 2010-08-11 13:27:30 UTC */ - tt_int_op(AF_INET,OP_EQ, tor_addr_parse(&or_conn.real_addr, + tt_int_op(AF_INET,OP_EQ, tor_addr_parse(&TO_CONN(&or_conn)->addr, "18.0.0.1")); - tor_addr_t *addr = &or_conn.real_addr; + tor_addr_t *addr = &TO_CONN(&or_conn)->addr; /* Get DoS subsystem limits */ dos_init(); @@ -108,7 +108,7 @@ test_dos_conn_creation(void *arg) /** Helper mock: Place a fake IP addr for this channel in <b>addr_out</b> */ static int -mock_channel_get_addr_if_possible(channel_t *chan, tor_addr_t *addr_out) +mock_channel_get_addr_if_possible(const channel_t *chan, tor_addr_t *addr_out) { (void)chan; tt_int_op(AF_INET,OP_EQ, tor_addr_parse(addr_out, "18.0.0.1")); @@ -139,9 +139,9 @@ test_dos_circuit_creation(void *arg) /* Initialize test data */ or_connection_t or_conn; time_t now = 1281533250; /* 2010-08-11 13:27:30 UTC */ - tt_int_op(AF_INET,OP_EQ, tor_addr_parse(&or_conn.real_addr, + tt_int_op(AF_INET,OP_EQ, tor_addr_parse(&TO_CONN(&or_conn)->addr, "18.0.0.1")); - tor_addr_t *addr = &or_conn.real_addr; + tor_addr_t *addr = &TO_CONN(&or_conn)->addr; /* Get DoS subsystem limits */ dos_init(); @@ -202,9 +202,9 @@ test_dos_bucket_refill(void *arg) channel_init(chan); chan->is_client = 1; or_connection_t or_conn; - tt_int_op(AF_INET,OP_EQ, tor_addr_parse(&or_conn.real_addr, + tt_int_op(AF_INET,OP_EQ, tor_addr_parse(&TO_CONN(&or_conn)->addr, "18.0.0.1")); - tor_addr_t *addr = &or_conn.real_addr; + tor_addr_t *addr = &TO_CONN(&or_conn)->addr; /* Initialize DoS subsystem and get relevant limits */ dos_init(); @@ -443,10 +443,10 @@ test_known_relay(void *arg) /* Setup an OR conn so we can pass it to the DoS subsystem. */ or_connection_t or_conn; - tor_addr_parse(&or_conn.real_addr, "42.42.42.42"); + tor_addr_parse(&TO_CONN(&or_conn)->addr, "42.42.42.42"); rs = tor_malloc_zero(sizeof(*rs)); - rs->addr = tor_addr_to_ipv4h(&or_conn.real_addr); + tor_addr_copy(&rs->ipv4_addr, &TO_CONN(&or_conn)->addr); crypto_rand(rs->identity_digest, sizeof(rs->identity_digest)); smartlist_add(dummy_ns->routerstatus_list, rs); @@ -457,7 +457,8 @@ test_known_relay(void *arg) /* We have now a node in our list so we'll make sure we don't count it as a * client connection. */ - geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &or_conn.real_addr, NULL, 0); + geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &TO_CONN(&or_conn)->addr, + NULL, 0); /* Suppose we have 5 connections in rapid succession, the counter should * always be 0 because we should ignore this. */ dos_new_client_conn(&or_conn, NULL); @@ -465,18 +466,21 @@ test_known_relay(void *arg) dos_new_client_conn(&or_conn, NULL); dos_new_client_conn(&or_conn, NULL); dos_new_client_conn(&or_conn, NULL); - entry = geoip_lookup_client(&or_conn.real_addr, NULL, GEOIP_CLIENT_CONNECT); + entry = geoip_lookup_client(&TO_CONN(&or_conn)->addr, NULL, + GEOIP_CLIENT_CONNECT); tt_assert(entry); /* We should have a count of 0. */ tt_uint_op(entry->dos_stats.concurrent_count, OP_EQ, 0); /* To make sure that his is working properly, make a unknown client * connection and see if we do get it. */ - tor_addr_parse(&or_conn.real_addr, "42.42.42.43"); - geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &or_conn.real_addr, NULL, 0); + tor_addr_parse(&TO_CONN(&or_conn)->addr, "42.42.42.43"); + geoip_note_client_seen(GEOIP_CLIENT_CONNECT, &TO_CONN(&or_conn)->addr, + NULL, 0); dos_new_client_conn(&or_conn, NULL); dos_new_client_conn(&or_conn, NULL); - entry = geoip_lookup_client(&or_conn.real_addr, NULL, GEOIP_CLIENT_CONNECT); + entry = geoip_lookup_client(&TO_CONN(&or_conn)->addr, NULL, + GEOIP_CLIENT_CONNECT); tt_assert(entry); /* We should have a count of 2. */ tt_uint_op(entry->dos_stats.concurrent_count, OP_EQ, 2); diff --git a/src/test/test_entrynodes.c b/src/test/test_entrynodes.c index 12b4fcde3c..7da7ea66e4 100644 --- a/src/test/test_entrynodes.c +++ b/src/test/test_entrynodes.c @@ -171,8 +171,8 @@ big_fake_network_setup(const struct testcase_t *testcase) /* Note: all these guards have the same address, so you'll need to * disable EnforceDistinctSubnets when a restriction is applied. */ - n->rs->addr = 0x04020202; - n->rs->or_port = 1234; + tor_addr_from_ipv4h(&n->rs->ipv4_addr, 0x04020202); + n->rs->ipv4_orport = 1234; n->rs->is_v2_dir = 1; n->rs->has_bandwidth = 1; n->rs->bandwidth_kb = 30; @@ -272,8 +272,8 @@ test_node_preferred_orport(void *arg) /* Setup node_ri */ memset(&node_ri, 0, sizeof(node_ri)); - node_ri.addr = tor_addr_to_ipv4h(&ipv4_addr); - node_ri.or_port = ipv4_port; + tor_addr_copy(&node_ri.ipv4_addr, &ipv4_addr); + node_ri.ipv4_orport = ipv4_port; tor_addr_copy(&node_ri.ipv6_addr, &ipv6_addr); node_ri.ipv6_orport = ipv6_port; @@ -390,12 +390,13 @@ test_entry_guard_encode_for_state_minimal(void *arg) eg->confirmed_idx = -1; char *s = NULL; - s = entry_guard_encode_for_state(eg); + s = entry_guard_encode_for_state(eg, 0); tt_str_op(s, OP_EQ, "in=wubwub " "rsa_id=706C75727079666C75727079736C75727079646F " "sampled_on=2016-11-14T00:00:00 " + "sampled_idx=0 " "listed=0"); done: @@ -421,10 +422,11 @@ test_entry_guard_encode_for_state_maximal(void *arg) eg->currently_listed = 1; eg->confirmed_on_date = 1479081690; eg->confirmed_idx = 333; + eg->sampled_idx = 42; eg->extra_state_fields = tor_strdup("and the green grass grew all around"); char *s = NULL; - s = entry_guard_encode_for_state(eg); + s = entry_guard_encode_for_state(eg, 0); tt_str_op(s, OP_EQ, "in=default " @@ -432,6 +434,7 @@ test_entry_guard_encode_for_state_maximal(void *arg) "bridge_addr=8.8.4.4:9999 " "nickname=Fred " "sampled_on=2016-11-14T00:00:00 " + "sampled_idx=0 " "sampled_by=1.2.3 " "unlisted_since=2016-11-14T00:00:45 " "listed=1 " @@ -621,39 +624,47 @@ test_entry_guard_parse_from_state_full(void *arg) const char STATE[] = "Guard in=default rsa_id=214F44BD5B638E8C817D47FF7C97397790BF0345 " "nickname=TotallyNinja sampled_on=2016-11-12T19:32:49 " + "sampled_idx=0 " "sampled_by=0.3.0.0-alpha-dev " "listed=1\n" "Guard in=default rsa_id=052900AB0EA3ED54BAB84AE8A99E74E8693CE2B2 " "nickname=5OfNovember sampled_on=2016-11-20T04:32:05 " + "sampled_idx=1 " "sampled_by=0.3.0.0-alpha-dev " "listed=1 confirmed_on=2016-11-22T08:13:28 confirmed_idx=0 " "pb_circ_attempts=4.000000 pb_circ_successes=2.000000 " "pb_successful_circuits_closed=2.000000\n" "Guard in=default rsa_id=7B700C0C207EBD0002E00F499BE265519AC3C25A " "nickname=dc6jgk11 sampled_on=2016-11-28T11:50:13 " + "sampled_idx=2 " "sampled_by=0.3.0.0-alpha-dev " "listed=1 confirmed_on=2016-11-24T08:45:30 confirmed_idx=4 " "pb_circ_attempts=5.000000 pb_circ_successes=5.000000 " "pb_successful_circuits_closed=5.000000\n" "Guard in=wobblesome rsa_id=7B700C0C207EBD0002E00F499BE265519AC3C25A " "nickname=dc6jgk11 sampled_on=2016-11-28T11:50:13 " + "sampled_idx=0 " "sampled_by=0.3.0.0-alpha-dev " "listed=1\n" "Guard in=default rsa_id=E9025AD60D86875D5F11548D536CC6AF60F0EF5E " "nickname=maibrunn sampled_on=2016-11-25T22:36:38 " + "sampled_idx=3 " "sampled_by=0.3.0.0-alpha-dev listed=1\n" "Guard in=default rsa_id=DCD30B90BA3A792DA75DC54A327EF353FB84C38E " "nickname=Unnamed sampled_on=2016-11-25T14:34:00 " + "sampled_idx=10 " "sampled_by=0.3.0.0-alpha-dev listed=1\n" "Guard in=bridges rsa_id=8FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2E " "bridge_addr=24.1.1.1:443 sampled_on=2016-11-25T06:44:14 " + "sampled_idx=0 " "sampled_by=0.3.0.0-alpha-dev listed=1 " "confirmed_on=2016-11-29T10:36:06 confirmed_idx=0 " "pb_circ_attempts=8.000000 pb_circ_successes=8.000000 " "pb_successful_circuits_closed=13.000000\n" "Guard in=bridges rsa_id=5800000000000000000000000000000000000000 " "bridge_addr=37.218.246.143:28366 " - "sampled_on=2016-11-18T15:07:34 sampled_by=0.3.0.0-alpha-dev listed=1\n"; + "sampled_on=2016-11-18T15:07:34 sampled_idx=1 " + "sampled_by=0.3.0.0-alpha-dev listed=1\n"; config_line_t *lines = NULL; or_state_t *state = tor_malloc_zero(sizeof(or_state_t)); @@ -729,35 +740,42 @@ test_entry_guard_parse_from_state_full(void *arg) tt_str_op(joined, OP_EQ, "Guard in=default rsa_id=052900AB0EA3ED54BAB84AE8A99E74E8693CE2B2 " "nickname=5OfNovember sampled_on=2016-11-20T04:32:05 " + "sampled_idx=0 " "sampled_by=0.3.0.0-alpha-dev " "listed=1 confirmed_on=2016-11-22T08:13:28 confirmed_idx=0 " "pb_circ_attempts=4.000000 pb_circ_successes=2.000000 " "pb_successful_circuits_closed=2.000000\n" "Guard in=default rsa_id=7B700C0C207EBD0002E00F499BE265519AC3C25A " "nickname=dc6jgk11 sampled_on=2016-11-28T11:50:13 " + "sampled_idx=1 " "sampled_by=0.3.0.0-alpha-dev " "listed=1 confirmed_on=2016-11-24T08:45:30 confirmed_idx=1 " "pb_circ_attempts=5.000000 pb_circ_successes=5.000000 " "pb_successful_circuits_closed=5.000000\n" "Guard in=default rsa_id=E9025AD60D86875D5F11548D536CC6AF60F0EF5E " "nickname=maibrunn sampled_on=2016-11-25T22:36:38 " + "sampled_idx=2 " "sampled_by=0.3.0.0-alpha-dev listed=1\n" "Guard in=default rsa_id=DCD30B90BA3A792DA75DC54A327EF353FB84C38E " "nickname=Unnamed sampled_on=2016-11-25T14:34:00 " + "sampled_idx=3 " "sampled_by=0.3.0.0-alpha-dev listed=1\n" "Guard in=wobblesome rsa_id=7B700C0C207EBD0002E00F499BE265519AC3C25A " "nickname=dc6jgk11 sampled_on=2016-11-28T11:50:13 " + "sampled_idx=0 " "sampled_by=0.3.0.0-alpha-dev " "listed=1\n" "Guard in=bridges rsa_id=8FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2E " "bridge_addr=24.1.1.1:443 sampled_on=2016-11-25T06:44:14 " + "sampled_idx=0 " "sampled_by=0.3.0.0-alpha-dev listed=1 " "confirmed_on=2016-11-29T10:36:06 confirmed_idx=0 " "pb_circ_attempts=8.000000 pb_circ_successes=8.000000 " "pb_successful_circuits_closed=13.000000\n" "Guard in=bridges rsa_id=5800000000000000000000000000000000000000 " "bridge_addr=37.218.246.143:28366 " - "sampled_on=2016-11-18T15:07:34 sampled_by=0.3.0.0-alpha-dev listed=1\n"); + "sampled_on=2016-11-18T15:07:34 sampled_idx=1 " + "sampled_by=0.3.0.0-alpha-dev listed=1\n"); done: config_free_lines(lines); @@ -984,10 +1002,10 @@ test_entry_guard_node_filter(void *arg) g[1]->pb.path_bias_disabled = 1; /* 2: Unreachable address. */ - n[2]->rs->addr = 0; + tor_addr_make_unspec(&n[2]->rs->ipv4_addr); /* 3: ExcludeNodes */ - n[3]->rs->addr = 0x90902020; + tor_addr_from_ipv4h(&n[3]->rs->ipv4_addr, 0x90902020); routerset_free(get_options_mutable()->ExcludeNodes); get_options_mutable()->ExcludeNodes = routerset_new(); routerset_parse(get_options_mutable()->ExcludeNodes, "144.144.0.0/16", ""); @@ -996,8 +1014,8 @@ test_entry_guard_node_filter(void *arg) get_options_mutable()->UseBridges = 1; sweep_bridge_list(); bl = tor_malloc_zero(sizeof(bridge_line_t)); - tor_addr_from_ipv4h(&bl->addr, n[4]->rs->addr); - bl->port = n[4]->rs->or_port; + tor_addr_copy(&bl->addr, &n[4]->rs->ipv4_addr); + bl->port = n[4]->rs->ipv4_orport; memcpy(bl->digest, n[4]->identity, 20); bridge_add_from_config(bl); bl = NULL; // prevent free. @@ -1106,7 +1124,7 @@ test_entry_guard_expand_sample(void *arg) routerset_parse(get_options_mutable()->ExcludeNodes, "144.144.0.0/16", ""); SMARTLIST_FOREACH(big_fake_net_nodes, node_t *, n, { if (n_sl_idx % 64 != 0) { - n->rs->addr = 0x90903030; + tor_addr_from_ipv4h(&n->rs->ipv4_addr, 0x90903030); } }); entry_guards_update_filtered_sets(gs); @@ -1144,7 +1162,7 @@ test_entry_guard_expand_sample_small_net(void *arg) test_node_free(n); SMARTLIST_DEL_CURRENT(big_fake_net_nodes, n); } else { - n->rs->addr = 0; // make the filter reject this. + tor_addr_make_unspec(&n->rs->ipv4_addr); // make the filter reject this. } }); @@ -1461,8 +1479,8 @@ test_entry_guard_confirming_guards(void *arg) tt_i64_op(g1->confirmed_on_date, OP_EQ, start+10); tt_i64_op(g2->confirmed_on_date, OP_EQ, start); tt_i64_op(g3->confirmed_on_date, OP_EQ, start+10); - tt_ptr_op(smartlist_get(gs->confirmed_entry_guards, 0), OP_EQ, g2); - tt_ptr_op(smartlist_get(gs->confirmed_entry_guards, 1), OP_EQ, g1); + tt_ptr_op(smartlist_get(gs->confirmed_entry_guards, 0), OP_EQ, g1); + tt_ptr_op(smartlist_get(gs->confirmed_entry_guards, 1), OP_EQ, g2); tt_ptr_op(smartlist_get(gs->confirmed_entry_guards, 2), OP_EQ, g3); /* Now make sure we can regenerate the confirmed_entry_guards list. */ @@ -1474,8 +1492,8 @@ test_entry_guard_confirming_guards(void *arg) tt_int_op(g1->confirmed_idx, OP_EQ, 1); tt_int_op(g2->confirmed_idx, OP_EQ, 0); tt_int_op(g3->confirmed_idx, OP_EQ, 2); - tt_ptr_op(smartlist_get(gs->confirmed_entry_guards, 0), OP_EQ, g2); - tt_ptr_op(smartlist_get(gs->confirmed_entry_guards, 1), OP_EQ, g1); + tt_ptr_op(smartlist_get(gs->confirmed_entry_guards, 0), OP_EQ, g1); + tt_ptr_op(smartlist_get(gs->confirmed_entry_guards, 1), OP_EQ, g2); tt_ptr_op(smartlist_get(gs->confirmed_entry_guards, 2), OP_EQ, g3); /* Now make sure we can regenerate the confirmed_entry_guards list if @@ -1492,9 +1510,9 @@ test_entry_guard_confirming_guards(void *arg) g1 = smartlist_get(gs->confirmed_entry_guards, 0); g2 = smartlist_get(gs->confirmed_entry_guards, 1); g3 = smartlist_get(gs->confirmed_entry_guards, 2); - tt_int_op(g1->confirmed_idx, OP_EQ, 0); - tt_int_op(g2->confirmed_idx, OP_EQ, 1); - tt_int_op(g3->confirmed_idx, OP_EQ, 2); + tt_int_op(g1->sampled_idx, OP_EQ, 0); + tt_int_op(g2->sampled_idx, OP_EQ, 1); + tt_int_op(g3->sampled_idx, OP_EQ, 8); tt_assert(g1 != g2); tt_assert(g1 != g3); tt_assert(g2 != g3); @@ -1510,9 +1528,6 @@ test_entry_guard_sample_reachable_filtered(void *arg) (void)arg; guard_selection_t *gs = guard_selection_new("default", GS_TYPE_NORMAL); entry_guards_expand_sample(gs); - const int N = 10000; - bitarray_t *selected = NULL; - int i, j; /* We've got a sampled list now; let's make one non-usable-filtered; some * confirmed, some primary, some pending. @@ -1547,32 +1562,21 @@ test_entry_guard_sample_reachable_filtered(void *arg) { SAMPLE_EXCLUDE_PENDING, 0 }, { -1, -1}, }; - + int j; for (j = 0; tests[j].flag >= 0; ++j) { - selected = bitarray_init_zero(n_guards); const int excluded_flags = tests[j].flag; const int excluded_idx = tests[j].idx; - for (i = 0; i < N; ++i) { - g = sample_reachable_filtered_entry_guards(gs, NULL, excluded_flags); - tor_assert(g); - int pos = smartlist_pos(gs->sampled_entry_guards, g); - tt_int_op(smartlist_len(gs->sampled_entry_guards), OP_EQ, n_guards); - tt_int_op(pos, OP_GE, 0); - tt_int_op(pos, OP_LT, n_guards); - bitarray_set(selected, pos); - } - for (i = 0; i < n_guards; ++i) { - const int should_be_set = (i != excluded_idx && - i != 3); // filtered out. - tt_int_op(!!bitarray_is_set(selected, i), OP_EQ, should_be_set); - } - bitarray_free(selected); - selected = NULL; + g = first_reachable_filtered_entry_guard(gs, NULL, excluded_flags); + tor_assert(g); + int pos = smartlist_pos(gs->sampled_entry_guards, g); + tt_int_op(smartlist_len(gs->sampled_entry_guards), OP_EQ, n_guards); + const int should_be_set = (pos != excluded_idx && + pos != 3); // filtered out. + tt_int_op(1, OP_EQ, should_be_set); } done: guard_selection_free(gs); - bitarray_free(selected); } static void @@ -1584,7 +1588,7 @@ test_entry_guard_sample_reachable_filtered_empty(void *arg) SMARTLIST_FOREACH(big_fake_net_nodes, node_t *, n, n->is_possible_guard = 0); - entry_guard_t *g = sample_reachable_filtered_entry_guards(gs, NULL, 0); + entry_guard_t *g = first_reachable_filtered_entry_guard(gs, NULL, 0); tt_ptr_op(g, OP_EQ, NULL); done: @@ -1675,10 +1679,13 @@ test_entry_guard_manage_primary(void *arg) tt_ptr_op(g, OP_EQ, smartlist_get(prev_guards, g_sl_idx)); }); - /* If we have one confirmed guard, that guards becomes the first primary - * guard, and the other primary guards get kept. */ + /** + * If we have one confirmed guard, that guards becomes the first primary + * only if its sampled_idx is smaller + * */ - /* find a non-primary guard... */ + /* find a non-primary guard... it should have a sampled_idx higher than + * existing primary guards */ entry_guard_t *confirmed = NULL; SMARTLIST_FOREACH(gs->sampled_entry_guards, entry_guard_t *, g, { if (! g->is_primary) { @@ -1694,15 +1701,13 @@ test_entry_guard_manage_primary(void *arg) smartlist_add_all(prev_guards, gs->primary_entry_guards); entry_guards_update_primary(gs); - /* and see what's primary now! */ + /* the confirmed guard should be at the end of the primary list! Hopefully, + * one of the primary guards with a lower sampled_idx will confirm soon :) + * Doing this won't make the client switches between primaries depending on + * the order of confirming events */ tt_int_op(smartlist_len(gs->primary_entry_guards), OP_EQ, n_primary); - tt_ptr_op(smartlist_get(gs->primary_entry_guards, 0), OP_EQ, confirmed); - SMARTLIST_FOREACH(gs->primary_entry_guards, entry_guard_t *, g, { - tt_assert(g->is_primary); - if (g_sl_idx == 0) - continue; - tt_ptr_op(g, OP_EQ, smartlist_get(prev_guards, g_sl_idx - 1)); - }); + tt_ptr_op(smartlist_get(gs->primary_entry_guards, + smartlist_len(gs->primary_entry_guards)-1), OP_EQ, confirmed); { entry_guard_t *prev_last_guard = smartlist_get(prev_guards, n_primary-1); tt_assert(! prev_last_guard->is_primary); @@ -1793,6 +1798,57 @@ test_entry_guard_guard_preferred(void *arg) } static void +test_entry_guard_correct_cascading_order(void *arg) +{ + (void)arg; + smartlist_t *old_primary_guards = smartlist_new(); + guard_selection_t *gs = guard_selection_new("default", GS_TYPE_NORMAL); + entry_guards_expand_sample(gs); + /** First, a test in which the primary guards need be pulled from different + * lists to fill up the primary list -- this may happen, if for example, not + * enough guards have confirmed yet */ + entry_guard_t *g; + /** just one confirmed */ + g = smartlist_get(gs->sampled_entry_guards, 2); + make_guard_confirmed(gs, g); + entry_guards_update_primary(gs); + g = smartlist_get(gs->primary_entry_guards, 0); + tt_int_op(g->sampled_idx, OP_EQ, 0); + g = smartlist_get(gs->primary_entry_guards, 1); + tt_int_op(g->sampled_idx, OP_EQ, 1); + g = smartlist_get(gs->primary_entry_guards, 2); + tt_int_op(g->sampled_idx, OP_EQ, 2); + + /** Now the primaries get all confirmed, and the primary list should not + * change */ + make_guard_confirmed(gs, smartlist_get(gs->primary_entry_guards, 0)); + make_guard_confirmed(gs, smartlist_get(gs->primary_entry_guards, 1)); + smartlist_add_all(old_primary_guards, gs->primary_entry_guards); + entry_guards_update_primary(gs); + smartlist_ptrs_eq(gs->primary_entry_guards, old_primary_guards); + /** the confirmed guards should also have the same set of guards, in the same + * order :-) */ + smartlist_ptrs_eq(gs->confirmed_entry_guards, gs->primary_entry_guards); + /** Now select a guard for a circuit, and make sure it is the first primary + * guard */ + unsigned state = 9999; + g = select_entry_guard_for_circuit(gs, GUARD_USAGE_TRAFFIC, NULL, &state); + tt_ptr_op(g, OP_EQ, smartlist_get(gs->primary_entry_guards, 0)); + /** Now, let's mark this guard as unreachable and let's update the lists */ + g->is_reachable = GUARD_REACHABLE_NO; + g->failing_since = approx_time() - 10; + g->last_tried_to_connect = approx_time() - 10; + state = 9999; + entry_guards_update_primary(gs); + g = select_entry_guard_for_circuit(gs, GUARD_USAGE_TRAFFIC, NULL, &state); + /** we should have switched to the next one is sampled order */ + tt_int_op(g->sampled_idx, OP_EQ, 1); + done: + smartlist_free(old_primary_guards); + guard_selection_free(gs); +} + +static void test_entry_guard_select_for_circuit_no_confirmed(void *arg) { /* Simpler cases: no gaurds are confirmed yet. */ @@ -3094,6 +3150,7 @@ struct testcase_t entrynodes_tests[] = { BFN_TEST(sample_reachable_filtered_empty), BFN_TEST(retry_unreachable), BFN_TEST(manage_primary), + BFN_TEST(correct_cascading_order), EN_TEST_FORK(guard_preferred), diff --git a/src/test/test_guardfraction.c b/src/test/test_guardfraction.c index 00c200e0fd..6019dfc2b1 100644 --- a/src/test/test_guardfraction.c +++ b/src/test/test_guardfraction.c @@ -51,9 +51,9 @@ gen_vote_routerstatus_for_tests(const char *digest_in_hex, int is_guard) vrs->version = tor_strdup("0.1.2.14"); strlcpy(rs->nickname, "router2", sizeof(rs->nickname)); memset(rs->descriptor_digest, 78, DIGEST_LEN); - rs->addr = 0x99008801; - rs->or_port = 443; - rs->dir_port = 8000; + tor_addr_from_ipv4h(&rs->ipv4_addr, 0x99008801); + rs->ipv4_orport = 443; + rs->ipv4_dirport = 8000; /* all flags but running cleared */ rs->is_flagged_running = 1; vrs->has_measured_bw = 1; diff --git a/src/test/test_helpers.c b/src/test/test_helpers.c index f31c28b24d..14913b4b40 100644 --- a/src/test/test_helpers.c +++ b/src/test/test_helpers.c @@ -16,28 +16,35 @@ #include "core/or/or.h" #include "lib/buf/buffers.h" -#include "app/config/config.h" #include "lib/confmgt/confmgt.h" -#include "app/main/subsysmgr.h" -#include "core/mainloop/connection.h" -#include "core/or/connection_or.h" #include "lib/crypt_ops/crypto_rand.h" -#include "core/mainloop/mainloop.h" -#include "feature/nodelist/nodelist.h" -#include "core/or/relay.h" -#include "feature/nodelist/routerlist.h" #include "lib/dispatch/dispatch.h" #include "lib/dispatch/dispatch_naming.h" -#include "lib/pubsub/pubsub_build.h" -#include "lib/pubsub/pubsub_connect.h" #include "lib/encoding/confline.h" #include "lib/net/resolve.h" +#include "lib/pubsub/pubsub_build.h" +#include "lib/pubsub/pubsub_connect.h" + +#include "core/mainloop/connection.h" +#include "core/mainloop/mainloop.h" +#include "core/or/connection_or.h" +#include "core/or/crypt_path.h" +#include "core/or/relay.h" + +#include "feature/nodelist/nodelist.h" +#include "feature/nodelist/routerlist.h" + +#include "app/config/config.h" +#include "app/main/subsysmgr.h" #include "core/or/cell_st.h" #include "core/or/connection_st.h" +#include "core/or/cpath_build_state_st.h" +#include "core/or/crypt_path_st.h" +#include "core/or/origin_circuit_st.h" #include "core/or/or_connection_st.h" + #include "feature/nodelist/node_st.h" -#include "core/or/origin_circuit_st.h" #include "feature/nodelist/routerlist_st.h" #include "test/test.h" @@ -441,3 +448,36 @@ helper_cleanup_pubsub(const struct testcase_t *testcase, void *dispatcher_) const struct testcase_setup_t helper_pubsub_setup = { helper_setup_pubsub, helper_cleanup_pubsub }; + +origin_circuit_t * +new_test_origin_circuit(bool has_opened, + struct timeval circ_start_time, + int path_len, + extend_info_t **ei_list) +{ + origin_circuit_t *origin_circ = origin_circuit_new(); + + TO_CIRCUIT(origin_circ)->purpose = CIRCUIT_PURPOSE_C_GENERAL; + + origin_circ->build_state = tor_malloc_zero(sizeof(cpath_build_state_t)); + origin_circ->build_state->desired_path_len = path_len; + + if (ei_list) { + for (int i = 0; i < path_len; i++) { + extend_info_t *ei = ei_list[i]; + cpath_append_hop(&origin_circ->cpath, ei); + } + } + + if (has_opened) { + origin_circ->has_opened = 1; + TO_CIRCUIT(origin_circ)->state = CIRCUIT_STATE_OPEN; + origin_circ->cpath->state = CPATH_STATE_OPEN; + } else { + TO_CIRCUIT(origin_circ)->timestamp_began = circ_start_time; + TO_CIRCUIT(origin_circ)->timestamp_created = circ_start_time; + origin_circ->cpath->state = CPATH_STATE_CLOSED; + } + + return origin_circ; +} diff --git a/src/test/test_helpers.h b/src/test/test_helpers.h index eaf18e19e2..66007873d1 100644 --- a/src/test/test_helpers.h +++ b/src/test/test_helpers.h @@ -40,5 +40,10 @@ int helper_cleanup_pubsub(const struct testcase_t *, void *); extern const struct testcase_setup_t helper_pubsub_setup; +origin_circuit_t *new_test_origin_circuit(bool has_opened, + struct timeval circ_start_time, + int path_len, + extend_info_t **ei_list); + #endif /* !defined(TOR_TEST_HELPERS_H) */ diff --git a/src/test/test_hs_client.c b/src/test/test_hs_client.c index ae5cc5ed84..f965344ce0 100644 --- a/src/test/test_hs_client.c +++ b/src/test/test_hs_client.c @@ -41,6 +41,7 @@ #include "feature/rend/rendcache.h" #include "core/or/circuitlist.h" #include "core/or/circuitbuild.h" +#include "core/or/extendinfo.h" #include "core/mainloop/connection.h" #include "core/or/connection_edge.h" #include "feature/nodelist/networkstatus.h" @@ -529,7 +530,7 @@ test_client_pick_intro(void *arg) get_options_mutable()->ClientUseIPv6 = 1; intro_ei = hs_get_extend_info_from_lspecs(ip->link_specifiers, &ip->onion_key, 1); - tt_assert(tor_addr_family(&intro_ei->addr) == AF_INET6); + tt_assert(tor_addr_family(&intro_ei->orports[0].addr) == AF_INET6); } tt_assert(intro_ei); if (intro_ei) { @@ -537,7 +538,8 @@ test_client_pick_intro(void *arg) char ip_addr[TOR_ADDR_BUF_LEN]; /* We need to decorate in case it is an IPv6 else routerset_parse() * doesn't like it. */ - ptr = tor_addr_to_str(ip_addr, &intro_ei->addr, sizeof(ip_addr), 1); + ptr = tor_addr_to_str(ip_addr, &intro_ei->orports[0].addr, + sizeof(ip_addr), 1); tt_assert(ptr == ip_addr); ret = routerset_parse(get_options_mutable()->ExcludeNodes, ip_addr, ""); diff --git a/src/test/test_hs_common.c b/src/test/test_hs_common.c index 4a161db334..295d9cdb62 100644 --- a/src/test/test_hs_common.c +++ b/src/test/test_hs_common.c @@ -293,7 +293,6 @@ helper_add_hsdir_to_networkstatus(networkstatus_t *ns, routerstatus_t *rs = tor_malloc_zero(sizeof(routerstatus_t)); routerinfo_t *ri = tor_malloc_zero(sizeof(routerinfo_t)); uint8_t identity[DIGEST_LEN]; - tor_addr_t ipv4_addr; node_t *node = NULL; memset(identity, identity_idx, sizeof(identity)); @@ -302,9 +301,8 @@ helper_add_hsdir_to_networkstatus(networkstatus_t *ns, rs->is_hs_dir = is_hsdir; rs->pv.supports_v3_hsdir = 1; strlcpy(rs->nickname, nickname, sizeof(rs->nickname)); - tor_addr_parse(&ipv4_addr, "1.2.3.4"); - ri->addr = tor_addr_to_ipv4h(&ipv4_addr); - rs->addr = tor_addr_to_ipv4h(&ipv4_addr); + tor_addr_parse(&ri->ipv4_addr, "1.2.3.4"); + tor_addr_parse(&rs->ipv4_addr, "1.2.3.4"); ri->nickname = tor_strdup(nickname); ri->protocol_list = tor_strdup("HSDir=1-2 LinkAuth=3"); memcpy(ri->cache_info.identity_digest, identity, DIGEST_LEN); @@ -792,6 +790,8 @@ test_parse_extended_hostname(void *arg) "www.25njqamcweflpvkl73j4szahhihoc4xt3ktcgjnpaingr5yhkenl5sid.onion"; char address9[] = "www.15njqamcweflpvkl73j4szahhihoc4xt3ktcgjnpaingr5yhkenl5sid.onion"; + char address10[] = + "15njqamcweflpvkl73j4szahhihoc4xt3ktcgjnpaingr5yhkenl5sid7jdl.onion"; tt_assert(!parse_extended_hostname(address1, &type)); tt_int_op(type, OP_EQ, BAD_HOSTNAME); @@ -824,7 +824,11 @@ test_parse_extended_hostname(void *arg) /* Invalid v3 address. */ tt_assert(!parse_extended_hostname(address9, &type)); - tt_int_op(type, OP_EQ, ONION_V3_HOSTNAME); + tt_int_op(type, OP_EQ, BAD_HOSTNAME); + + /* Invalid v3 address: too long */ + tt_assert(!parse_extended_hostname(address10, &type)); + tt_int_op(type, OP_EQ, BAD_HOSTNAME); done: ; } diff --git a/src/test/test_hs_control.c b/src/test/test_hs_control.c index 8ba9f1173c..1f574179e9 100644 --- a/src/test/test_hs_control.c +++ b/src/test/test_hs_control.c @@ -393,7 +393,7 @@ test_hs_control_good_onion_client_auth_add(void *arg) retval = handle_control_command(&conn, (uint32_t) strlen(args), args); tt_int_op(retval, OP_EQ, 0); cp1 = buf_get_contents(TO_CONN(&conn)->outbuf, &sz); - tt_str_op(cp1, OP_EQ, "512 Invalid v3 addr \"house\"\r\n"); + tt_str_op(cp1, OP_EQ, "512 Invalid v3 address \"house\"\r\n"); done: tor_free(args); diff --git a/src/test/test_hs_service.c b/src/test/test_hs_service.c index 80383baff8..608e738d85 100644 --- a/src/test/test_hs_service.c +++ b/src/test/test_hs_service.c @@ -1545,14 +1545,12 @@ test_build_update_descriptors(void *arg) /* Now, we'll setup a node_t. */ { - tor_addr_t ipv4_addr; curve25519_secret_key_t curve25519_secret_key; memset(&ri, 0, sizeof(routerinfo_t)); - tor_addr_parse(&ipv4_addr, "127.0.0.1"); - ri.addr = tor_addr_to_ipv4h(&ipv4_addr); - ri.or_port = 1337; + tor_addr_parse(&ri.ipv4_addr, "127.0.0.1"); + ri.ipv4_orport = 1337; ri.purpose = ROUTER_PURPOSE_GENERAL; /* Ugly yes but we never free the "ri" object so this just makes things * easier. */ @@ -1619,7 +1617,7 @@ test_build_update_descriptors(void *arg) /* We won't test the service IP object because there is a specific test * already for this but we'll make sure that the state is coherent.*/ - /* Three link specifiers are mandatoy so make sure we do have them. */ + /* Three link specifiers are mandatory so make sure we do have them. */ tt_int_op(smartlist_len(ip_cur->base.link_specifiers), OP_EQ, 3); /* Make sure we have a valid encryption keypair generated when we pick an * intro point in the update process. */ diff --git a/src/test/test_nodelist.c b/src/test/test_nodelist.c index fbbbf0a99f..c165eebb63 100644 --- a/src/test/test_nodelist.c +++ b/src/test/test_nodelist.c @@ -104,7 +104,7 @@ test_nodelist_node_is_dir(void *arg) tt_assert(node_is_dir(&node)); rs.is_v2_dir = 0; - rs.dir_port = 1; + rs.ipv4_dirport = 1; tt_assert(! node_is_dir(&node)); node.rs = NULL; @@ -113,7 +113,7 @@ test_nodelist_node_is_dir(void *arg) ri.supports_tunnelled_dir_requests = 1; tt_assert(node_is_dir(&node)); ri.supports_tunnelled_dir_requests = 0; - ri.dir_port = 1; + ri.ipv4_dirport = 1; tt_assert(! node_is_dir(&node)); done: @@ -685,7 +685,7 @@ test_nodelist_format_node_description(void *arg) mock_digest, NULL, NULL, - 0); + NULL); tt_ptr_op(rv, OP_EQ, ndesc); tt_str_op(ndesc, OP_EQ, "$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"); @@ -694,7 +694,7 @@ test_nodelist_format_node_description(void *arg) mock_digest, mock_nickname, NULL, - 0); + NULL); tt_ptr_op(rv, OP_EQ, ndesc); tt_str_op(ndesc, OP_EQ, "$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA~""TestOR7890123456789"); @@ -703,8 +703,8 @@ test_nodelist_format_node_description(void *arg) rv = format_node_description(ndesc, mock_digest, mock_nickname, - &mock_null_ip, - 0); + NULL, + &mock_null_ip); tt_ptr_op(rv, OP_EQ, ndesc); tt_str_op(ndesc, OP_EQ, "$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA~TestOR7890123456789"); @@ -714,7 +714,7 @@ test_nodelist_format_node_description(void *arg) mock_digest, NULL, &mock_ipv4, - 0); + NULL); tt_ptr_op(rv, OP_EQ, ndesc); tt_str_op(ndesc, OP_EQ, "$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA at 111.222.233.244"); @@ -722,8 +722,8 @@ test_nodelist_format_node_description(void *arg) rv = format_node_description(ndesc, mock_digest, mock_nickname, - &mock_ipv6, - 0); + NULL, + &mock_ipv6); tt_ptr_op(rv, OP_EQ, ndesc); tt_str_op(ndesc, OP_EQ, "$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA~TestOR7890123456789 at " @@ -732,18 +732,18 @@ test_nodelist_format_node_description(void *arg) rv = format_node_description(ndesc, mock_digest, mock_nickname, - &mock_ipv6, - tor_addr_to_ipv4h(&mock_ipv4)); + &mock_ipv4, + &mock_ipv6); tt_ptr_op(rv, OP_EQ, ndesc); tt_str_op(ndesc, OP_EQ, "$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA~TestOR7890123456789 at " "111.222.233.244 and [1111:2222:3333:4444:5555:6666:7777:8888]"); /* test NULL handling */ - rv = format_node_description(NULL, NULL, NULL, NULL, 0); + rv = format_node_description(NULL, NULL, NULL, NULL, NULL); tt_str_op(rv, OP_EQ, "<NULL BUFFER>"); - rv = format_node_description(ndesc, NULL, NULL, NULL, 0); + rv = format_node_description(ndesc, NULL, NULL, NULL, NULL); tt_ptr_op(rv, OP_EQ, ndesc); tt_str_op(rv, OP_EQ, "<NULL ID DIGEST>"); @@ -761,7 +761,6 @@ static void test_nodelist_router_describe(void *arg) { char mock_nickname[MAX_NICKNAME_LEN+1]; - tor_addr_t mock_ipv4; routerinfo_t mock_ri_ipv4; routerinfo_t mock_ri_ipv6; routerinfo_t mock_ri_dual; @@ -772,7 +771,6 @@ test_nodelist_router_describe(void *arg) /* Clear variables */ memset(mock_nickname, 0, sizeof(mock_nickname)); - memset(&mock_ipv4, 0, sizeof(mock_ipv4)); memset(&mock_ri_ipv4, 0, sizeof(mock_ri_ipv4)); memset(&mock_ri_ipv6, 0, sizeof(mock_ri_ipv6)); memset(&mock_ri_dual, 0, sizeof(mock_ri_dual)); @@ -784,8 +782,7 @@ test_nodelist_router_describe(void *arg) sizeof(mock_ri_dual.cache_info.identity_digest)); strlcpy(mock_nickname, "TestOR7890123456789", sizeof(mock_nickname)); mock_ri_dual.nickname = mock_nickname; - tor_addr_parse(&mock_ipv4, "111.222.233.244"); - mock_ri_dual.addr = tor_addr_to_ipv4h(&mock_ipv4); + tor_addr_parse(&mock_ri_dual.ipv4_addr, "111.222.233.244"); tor_addr_parse(&mock_ri_dual.ipv6_addr, "[1111:2222:3333:4444:5555:6666:7777:8888]"); @@ -796,7 +793,7 @@ test_nodelist_router_describe(void *arg) memcpy(&mock_ri_ipv6, &mock_ri_dual, sizeof(mock_ri_ipv6)); /* Clear the unnecessary addresses */ memset(&mock_ri_ipv4.ipv6_addr, 0, sizeof(mock_ri_ipv4.ipv6_addr)); - mock_ri_ipv6.addr = 0; + tor_addr_make_unspec(&mock_ri_ipv6.ipv4_addr); /* We don't test the no-nickname and no-IP cases, because they're covered by * format_node_description(), and we don't expect to see them in Tor code. */ @@ -863,7 +860,6 @@ static void test_nodelist_node_describe(void *arg) { char mock_nickname[MAX_NICKNAME_LEN+1]; - tor_addr_t mock_ipv4; const char *rv = NULL; @@ -874,7 +870,6 @@ test_nodelist_node_describe(void *arg) /* Clear variables */ memset(mock_nickname, 0, sizeof(mock_nickname)); - memset(&mock_ipv4, 0, sizeof(mock_ipv4)); memset(&mock_ri_dual, 0, sizeof(mock_ri_dual)); /* Set up the dual-stack routerinfo */ @@ -884,8 +879,7 @@ test_nodelist_node_describe(void *arg) sizeof(mock_ri_dual.cache_info.identity_digest)); strlcpy(mock_nickname, "TestOR7890123456789", sizeof(mock_nickname)); mock_ri_dual.nickname = mock_nickname; - tor_addr_parse(&mock_ipv4, "111.222.233.244"); - mock_ri_dual.addr = tor_addr_to_ipv4h(&mock_ipv4); + tor_addr_parse(&mock_ri_dual.ipv4_addr, "111.222.233.244"); tor_addr_parse(&mock_ri_dual.ipv6_addr, "[1111:2222:3333:4444:5555:6666:7777:8888]"); @@ -894,7 +888,6 @@ test_nodelist_node_describe(void *arg) routerstatus_t mock_rs_dual; /* Clear variables */ - memset(&mock_ipv4, 0, sizeof(mock_ipv4)); memset(&mock_rs_ipv4, 0, sizeof(mock_rs_ipv4)); memset(&mock_rs_dual, 0, sizeof(mock_rs_dual)); @@ -905,8 +898,7 @@ test_nodelist_node_describe(void *arg) sizeof(mock_rs_dual.identity_digest)); strlcpy(mock_rs_dual.nickname, "Bbb", sizeof(mock_rs_dual.nickname)); - tor_addr_parse(&mock_ipv4, "2.2.2.2"); - mock_rs_dual.addr = tor_addr_to_ipv4h(&mock_ipv4); + tor_addr_parse(&mock_rs_dual.ipv4_addr, "2.2.2.2"); tor_addr_parse(&mock_rs_dual.ipv6_addr, "[bbbb::bbbb]"); @@ -1070,7 +1062,6 @@ test_nodelist_node_describe(void *arg) static void test_nodelist_routerstatus_describe(void *arg) { - tor_addr_t mock_ipv4; routerstatus_t mock_rs_ipv4; routerstatus_t mock_rs_ipv6; routerstatus_t mock_rs_dual; @@ -1080,7 +1071,6 @@ test_nodelist_routerstatus_describe(void *arg) (void) arg; /* Clear variables */ - memset(&mock_ipv4, 0, sizeof(mock_ipv4)); memset(&mock_rs_ipv4, 0, sizeof(mock_rs_ipv4)); memset(&mock_rs_ipv6, 0, sizeof(mock_rs_ipv6)); memset(&mock_rs_dual, 0, sizeof(mock_rs_dual)); @@ -1092,8 +1082,7 @@ test_nodelist_routerstatus_describe(void *arg) sizeof(mock_rs_dual.identity_digest)); strlcpy(mock_rs_dual.nickname, "TestOR7890123456789", sizeof(mock_rs_dual.nickname)); - tor_addr_parse(&mock_ipv4, "111.222.233.244"); - mock_rs_dual.addr = tor_addr_to_ipv4h(&mock_ipv4); + tor_addr_parse(&mock_rs_dual.ipv4_addr, "111.222.233.244"); tor_addr_parse(&mock_rs_dual.ipv6_addr, "[1111:2222:3333:4444:5555:6666:7777:8888]"); @@ -1102,7 +1091,7 @@ test_nodelist_routerstatus_describe(void *arg) memcpy(&mock_rs_ipv6, &mock_rs_dual, sizeof(mock_rs_ipv6)); /* Clear the unnecessary addresses */ memset(&mock_rs_ipv4.ipv6_addr, 0, sizeof(mock_rs_ipv4.ipv6_addr)); - mock_rs_ipv6.addr = 0; + tor_addr_make_unspec(&mock_rs_ipv6.ipv4_addr); /* We don't test the no-nickname and no-IP cases, because they're covered by * format_node_description(), and we don't expect to see them in Tor code. */ @@ -1182,11 +1171,11 @@ test_nodelist_extend_info_describe(void *arg) sizeof(mock_ei_ipv4.identity_digest)); strlcpy(mock_ei_ipv4.nickname, "TestOR7890123456789", sizeof(mock_ei_ipv4.nickname)); - tor_addr_parse(&mock_ei_ipv4.addr, "111.222.233.244"); + tor_addr_parse(&mock_ei_ipv4.orports[0].addr, "111.222.233.244"); /* Create and modify the other extend info. */ memcpy(&mock_ei_ipv6, &mock_ei_ipv4, sizeof(mock_ei_ipv6)); - tor_addr_parse(&mock_ei_ipv6.addr, + tor_addr_parse(&mock_ei_ipv6.orports[0].addr, "[1111:2222:3333:4444:5555:6666:7777:8888]"); /* We don't test the no-nickname and no-IP cases, because they're covered by @@ -1259,8 +1248,8 @@ test_nodelist_routerstatus_has_visibly_changed(void *arg) strlcpy(rs_orig.nickname, "friendly", sizeof(rs_orig.nickname)); memcpy(rs_orig.identity_digest, "abcdefghijklmnopqrst", 20); memcpy(rs_orig.descriptor_digest, "abcdefghijklmnopqrst", 20); - rs_orig.addr = 0x7f000001; - rs_orig.or_port = 3; + tor_addr_from_ipv4h(&rs_orig.ipv4_addr, 0x7f000001); + rs_orig.ipv4_orport = 3; rs_orig.published_on = time(NULL); rs_orig.has_bandwidth = 1; rs_orig.bandwidth_kb = 20; @@ -1301,7 +1290,7 @@ test_nodelist_routerstatus_has_visibly_changed(void *arg) COPY(); ASSERT_SAME(); - rs.addr = 0x7f000002; + tor_addr_from_ipv4h(&rs.ipv4_addr, 0x7f000002); ASSERT_CHANGED(); strlcpy(rs.descriptor_digest, "hello world", sizeof(rs.descriptor_digest)); @@ -1313,10 +1302,10 @@ test_nodelist_routerstatus_has_visibly_changed(void *arg) rs.published_on += 3600; ASSERT_CHANGED(); - rs.or_port = 55; + rs.ipv4_orport = 55; ASSERT_CHANGED(); - rs.dir_port = 9999; + rs.ipv4_dirport = 9999; ASSERT_CHANGED(); tor_addr_parse(&rs.ipv6_addr, "1234::56"); diff --git a/src/test/test_options.c b/src/test/test_options.c index 9cd1d11d29..8e0d19f126 100644 --- a/src/test/test_options.c +++ b/src/test/test_options.c @@ -2362,7 +2362,7 @@ test_options_validate__rend(void *ignored) expect_log_msg("UseEntryGuards is disabled, but you" " have configured one or more hidden services on this Tor " "instance. Your hidden services will be very easy to locate using" - " a well-known attack -- see http://freehaven.net/anonbib/#hs-" + " a well-known attack -- see https://freehaven.net/anonbib/#hs-" "attack06 for details.\n"); tor_free(msg); @@ -2378,7 +2378,7 @@ test_options_validate__rend(void *ignored) expect_no_log_msg("UseEntryGuards is disabled, but you" " have configured one or more hidden services on this Tor " "instance. Your hidden services will be very easy to locate using" - " a well-known attack -- see http://freehaven.net/anonbib/#hs-" + " a well-known attack -- see https://freehaven.net/anonbib/#hs-" "attack06 for details.\n"); free_options_test_data(tdata); diff --git a/src/test/test_parseconf.sh b/src/test/test_parseconf.sh index 4fe27d9f5d..c02b8b23c0 100755 --- a/src/test/test_parseconf.sh +++ b/src/test/test_parseconf.sh @@ -202,7 +202,7 @@ STANDARD_LIBS="libevent\\|openssl\\|zlib" # shellcheck disable=SC2018,SC2019 TOR_LIBS_ENABLED="$("$TOR_BINARY" --verify-config \ -f "$EMPTY" --defaults-torrc "$EMPTY" \ - | sed -n 's/.* Tor .* running on .* with\(.*\)\./\1/p' \ + | sed -n 's/.* Tor .* running on .* with\(.*\) and .* .* as libc\./\1/p' \ | tr 'A-Z' 'a-z' | tr ',' '\n' \ | grep -v "$STANDARD_LIBS" | grep -v "n/a" \ | sed 's/\( and\)* \(lib\)*\([a-z0-9]*\) .*/\3/' \ diff --git a/src/test/test_policy.c b/src/test/test_policy.c index 7949e90e9e..3559c0dda8 100644 --- a/src/test/test_policy.c +++ b/src/test/test_policy.c @@ -8,6 +8,7 @@ #include "app/config/config.h" #include "core/or/circuitbuild.h" #include "core/or/policies.h" +#include "core/or/extendinfo.h" #include "feature/dirparse/policy_parse.h" #include "feature/hs/hs_common.h" #include "feature/hs/hs_descriptor.h" @@ -1124,7 +1125,7 @@ test_policy_has_address_helper(const smartlist_t *policy_list, return 0; } -#define TEST_IPV4_ADDR (0x01020304) +#define TEST_IPV4_ADDR ("1.2.3.4") #define TEST_IPV6_ADDR ("2002::abcd") /** Run unit tests for rejecting the configured addresses on this exit relay @@ -1137,7 +1138,7 @@ test_policies_reject_exit_address(void *arg) smartlist_t *ipv4_list, *ipv6_list, *both_list, *dupl_list; (void)arg; - tor_addr_from_ipv4h(&ipv4_addr, TEST_IPV4_ADDR); + tor_addr_parse(&ipv4_addr, TEST_IPV4_ADDR); tor_addr_parse(&ipv6_addr, TEST_IPV6_ADDR); ipv4_list = smartlist_new(); @@ -1255,7 +1256,7 @@ test_policies_reject_port_address(void *arg) test_configured_ports = smartlist_new(); ipv4_port = port_cfg_new(0); - tor_addr_from_ipv4h(&ipv4_port->addr, TEST_IPV4_ADDR); + tor_addr_parse(&ipv4_port->addr, TEST_IPV4_ADDR); smartlist_add(test_configured_ports, ipv4_port); ipv6_port = port_cfg_new(0); @@ -1373,7 +1374,7 @@ test_policies_reject_interface_address(void *arg) } /* Now do it all again, but mocked */ - tor_addr_from_ipv4h(&ipv4_addr, TEST_IPV4_ADDR); + tor_addr_parse(&ipv4_addr, TEST_IPV4_ADDR); mock_ipv4_addrs = smartlist_new(); smartlist_add(mock_ipv4_addrs, (void *)&ipv4_addr); @@ -1528,7 +1529,7 @@ mock_router_get_my_routerinfo_with_err(int *err) } #define DEFAULT_POLICY_STRING "reject *:*" -#define TEST_IPV4_ADDR (0x02040608) +#define TEST_IPV4_ADDR ("2.4.6.8") #define TEST_IPV6_ADDR ("2003::ef01") static or_options_t mock_options; @@ -1607,13 +1608,13 @@ test_policies_getinfo_helper_policies(void *arg) tt_assert(strlen(answer) == 0 || !strcasecmp(answer, DEFAULT_POLICY_STRING)); tor_free(answer); - mock_my_routerinfo.addr = TEST_IPV4_ADDR; + tor_addr_parse(&mock_my_routerinfo.ipv4_addr, TEST_IPV4_ADDR); tor_addr_parse(&mock_my_routerinfo.ipv6_addr, TEST_IPV6_ADDR); append_exit_policy_string(&mock_my_routerinfo.exit_policy, "accept *4:*"); append_exit_policy_string(&mock_my_routerinfo.exit_policy, "reject *6:*"); mock_options.IPv6Exit = 1; - tor_addr_from_ipv4h( + tor_addr_parse( &mock_options.OutboundBindAddresses[OUTBOUND_ADDR_EXIT][0], TEST_IPV4_ADDR); tor_addr_parse( @@ -2084,8 +2085,8 @@ test_policies_fascist_firewall_allows_address(void *arg) expect_single_log_msg("Specified link specifiers is null"); \ } else { \ expect_no_log_entry(); \ - tt_assert(tor_addr_eq(&(expect_ap).addr, &ei->addr)); \ - tt_int_op((expect_ap).port, OP_EQ, ei->port); \ + tt_assert(tor_addr_eq(&(expect_ap).addr, &ei->orports[0].addr)); \ + tt_int_op((expect_ap).port, OP_EQ, ei->orports[0].port); \ extend_info_free(ei); \ } \ teardown_capture_of_logs(); \ @@ -2242,9 +2243,9 @@ test_policies_fascist_firewall_choose_address(void *arg) routerstatus_t fake_rs; memset(&fake_rs, 0, sizeof(routerstatus_t)); /* In a routerstatus, the OR and Dir addresses are the same */ - fake_rs.addr = tor_addr_to_ipv4h(&ipv4_or_ap.addr); - fake_rs.or_port = ipv4_or_ap.port; - fake_rs.dir_port = ipv4_dir_ap.port; + tor_addr_copy(&fake_rs.ipv4_addr, &ipv4_or_ap.addr); + fake_rs.ipv4_orport = ipv4_or_ap.port; + fake_rs.ipv4_dirport = ipv4_dir_ap.port; tor_addr_copy(&fake_rs.ipv6_addr, &ipv6_or_ap.addr); fake_rs.ipv6_orport = ipv6_or_ap.port; diff --git a/src/test/test_prob_distr.c b/src/test/test_prob_distr.c index c5423ce14a..541a81df3a 100644 --- a/src/test/test_prob_distr.c +++ b/src/test/test_prob_distr.c @@ -893,7 +893,7 @@ test_uniform_interval(void *arg) * * NIST/SEMATECH e-Handbook of Statistical Methods, Section * 1.3.6.7.4 `Critical Values of the Chi-Square Distribution', - * <http://www.itl.nist.gov/div898/handbook/eda/section3/eda3674.htm>, + * <https://www.itl.nist.gov/div898/handbook/eda/section3/eda3674.htm>, * retrieved 2018-10-28. */ diff --git a/src/test/test_process_descs.c b/src/test/test_process_descs.c index 14865cff13..5c2301f873 100644 --- a/src/test/test_process_descs.c +++ b/src/test/test_process_descs.c @@ -38,10 +38,10 @@ test_process_descs_versions(void *arg) { "Tor 0.4.0.5", true }, { "Tor 0.4.1.1-alpha", true }, { "Tor 0.4.1.4-rc", true }, + { "Tor 0.4.1.5", true }, // new enough to be supported { "Tor 0.3.5.7", false }, { "Tor 0.3.5.8", false }, - { "Tor 0.4.1.5", false }, { "Tor 0.4.2.1-alpha", false }, { "Tor 0.4.2.4-rc", false }, { "Tor 0.4.3.0-alpha-dev", false }, diff --git a/src/test/test_protover.c b/src/test/test_protover.c index c33fbcae2c..5e74265550 100644 --- a/src/test/test_protover.c +++ b/src/test/test_protover.c @@ -7,14 +7,18 @@ #include "orconfig.h" #include "test/test.h" -#include "core/or/protover.h" +#include "lib/tls/tortls.h" #include "core/or/or.h" + #include "core/or/connection_or.h" -#include "lib/tls/tortls.h" +#include "core/or/protover.h" +#include "core/or/versions.h" #include "feature/dirauth/dirvote.h" +#include "feature/relay/relay_handshake.h" + static void test_protover_parse(void *arg) { @@ -409,23 +413,21 @@ test_protover_supports_version(void *arg) * Hard-coded here, because they are not in the code, or not exposed in the * headers. */ #define PROTOVER_LINKAUTH_V1 1 -#define PROTOVER_LINKAUTH_V3 3 - +#define PROTOVER_LINKAUTH_V2 2 #define PROTOVER_RELAY_V1 1 -#define PROTOVER_RELAY_V2 2 +/* Deprecated HSIntro versions */ +#define PROTOVER_HS_INTRO_DEPRECATED_1 1 +#define PROTOVER_HS_INTRO_DEPRECATED_2 2 /* Highest supported HSv2 introduce protocol version. - * Hard-coded here, because it does not appear anywhere in the code. * It's not clear if we actually support version 2, see #25068. */ -#define PROTOVER_HSINTRO_V2 3 +#define PROTOVER_HS_INTRO_V2 3 -/* HSv2 Rend and HSDir protocol versions. - * Hard-coded here, because they do not appear anywhere in the code. */ +/* HSv2 Rend and HSDir protocol versions. */ #define PROTOVER_HS_RENDEZVOUS_POINT_V2 1 #define PROTOVER_HSDIR_V2 1 -/* DirCache, Desc, Microdesc, and Cons protocol versions. - * Hard-coded here, because they do not appear anywhere in the code. */ +/* DirCache, Desc, Microdesc, and Cons protocol versions. */ #define PROTOVER_DIRCACHE_V1 1 #define PROTOVER_DIRCACHE_V2 2 @@ -438,6 +440,10 @@ test_protover_supports_version(void *arg) #define PROTOVER_CONS_V1 1 #define PROTOVER_CONS_V2 2 +#define PROTOVER_PADDING_V1 1 + +#define PROTOVER_FLOWCTRL_V1 1 + /* Make sure we haven't forgotten any supported protocols */ static void test_protover_supported_protocols(void *arg) @@ -452,24 +458,27 @@ test_protover_supported_protocols(void *arg) PRT_LINK, MAX_LINK_PROTO)); for (uint16_t i = 0; i < MAX_PROTOCOLS_TO_TEST; i++) { - if (is_or_protocol_version_known(i)) { - tt_assert(protocol_list_supports_protocol(supported_protocols, + tt_int_op(protocol_list_supports_protocol(supported_protocols, PRT_LINK, - i)); - } + i), + OP_EQ, + is_or_protocol_version_known(i)); } -#ifdef HAVE_WORKING_TOR_TLS_GET_TLSSECRETS - /* Legacy LinkAuth does not appear anywhere in the code. */ - tt_assert(protocol_list_supports_protocol(supported_protocols, - PRT_LINKAUTH, - PROTOVER_LINKAUTH_V1)); -#endif /* defined(HAVE_WORKING_TOR_TLS_GET_TLSSECRETS) */ - /* Latest LinkAuth is not exposed in the headers. */ - tt_assert(protocol_list_supports_protocol(supported_protocols, + /* Legacy LinkAuth is only supported on OpenSSL and similar. */ + tt_int_op(protocol_list_supports_protocol(supported_protocols, PRT_LINKAUTH, - PROTOVER_LINKAUTH_V3)); - /* Is there any way to test for new LinkAuth? */ + PROTOVER_LINKAUTH_V1), + OP_EQ, + authchallenge_type_is_supported(AUTHTYPE_RSA_SHA256_TLSSECRET)); + /* LinkAuth=2 is unused */ + tt_assert(!protocol_list_supports_protocol(supported_protocols, + PRT_LINKAUTH, + PROTOVER_LINKAUTH_V2)); + tt_assert( + protocol_list_supports_protocol(supported_protocols, + PRT_LINKAUTH, + PROTOVER_LINKAUTH_ED25519_HANDSHAKE)); /* Relay protovers do not appear anywhere in the code. */ tt_assert(protocol_list_supports_protocol(supported_protocols, @@ -477,20 +486,38 @@ test_protover_supported_protocols(void *arg) PROTOVER_RELAY_V1)); tt_assert(protocol_list_supports_protocol(supported_protocols, PRT_RELAY, - PROTOVER_RELAY_V2)); - /* Is there any way to test for new Relay? */ + PROTOVER_RELAY_EXTEND2)); + tt_assert(protocol_list_supports_protocol(supported_protocols, + PRT_RELAY, + PROTOVER_RELAY_ACCEPT_IPV6)); + tt_assert(protocol_list_supports_protocol(supported_protocols, + PRT_RELAY, + PROTOVER_RELAY_EXTEND_IPV6)); + tt_assert(protocol_list_supports_protocol(supported_protocols, + PRT_RELAY, + PROTOVER_RELAY_CANONICAL_IPV6)); + /* These HSIntro versions are deprecated */ + tt_assert(!protocol_list_supports_protocol(supported_protocols, + PRT_HSINTRO, + PROTOVER_HS_INTRO_DEPRECATED_1)); + tt_assert(!protocol_list_supports_protocol(supported_protocols, + PRT_HSINTRO, + PROTOVER_HS_INTRO_DEPRECATED_2)); /* We could test legacy HSIntro by calling rend_service_update_descriptor(), * and checking the protocols field. But that's unlikely to change, so * we just use a hard-coded value. */ tt_assert(protocol_list_supports_protocol(supported_protocols, PRT_HSINTRO, - PROTOVER_HSINTRO_V2)); + PROTOVER_HS_INTRO_V2)); /* Test for HSv3 HSIntro */ tt_assert(protocol_list_supports_protocol(supported_protocols, PRT_HSINTRO, PROTOVER_HS_INTRO_V3)); - /* Is there any way to test for new HSIntro? */ + /* Test for HSIntro DoS */ + tt_assert(protocol_list_supports_protocol(supported_protocols, + PRT_HSINTRO, + PROTOVER_HS_INTRO_DOS)); /* Legacy HSRend does not appear anywhere in the code. */ tt_assert(protocol_list_supports_protocol(supported_protocols, @@ -500,7 +527,6 @@ test_protover_supported_protocols(void *arg) tt_assert(protocol_list_supports_protocol(supported_protocols, PRT_HSREND, PROTOVER_HS_RENDEZVOUS_POINT_V3)); - /* Is there any way to test for new HSRend? */ /* Legacy HSDir does not appear anywhere in the code. */ tt_assert(protocol_list_supports_protocol(supported_protocols, @@ -510,7 +536,6 @@ test_protover_supported_protocols(void *arg) tt_assert(protocol_list_supports_protocol(supported_protocols, PRT_HSDIR, PROTOVER_HSDIR_V3)); - /* Is there any way to test for new HSDir? */ /* No DirCache versions appear anywhere in the code. */ tt_assert(protocol_list_supports_protocol(supported_protocols, @@ -519,7 +544,6 @@ test_protover_supported_protocols(void *arg) tt_assert(protocol_list_supports_protocol(supported_protocols, PRT_DIRCACHE, PROTOVER_DIRCACHE_V2)); - /* Is there any way to test for new DirCache? */ /* No Desc versions appear anywhere in the code. */ tt_assert(protocol_list_supports_protocol(supported_protocols, @@ -537,7 +561,6 @@ test_protover_supported_protocols(void *arg) tt_assert(protocol_list_supports_protocol(supported_protocols, PRT_MICRODESC, PROTOVER_MICRODESC_V2)); - /* Is there any way to test for new Microdesc? */ /* No Cons versions appear anywhere in the code. */ tt_assert(protocol_list_supports_protocol(supported_protocols, @@ -546,7 +569,19 @@ test_protover_supported_protocols(void *arg) tt_assert(protocol_list_supports_protocol(supported_protocols, PRT_CONS, PROTOVER_CONS_V2)); - /* Is there any way to test for new Cons? */ + + /* Padding=1 is deprecated. */ + tt_assert(!protocol_list_supports_protocol(supported_protocols, + PRT_PADDING, + PROTOVER_PADDING_V1)); + tt_assert(protocol_list_supports_protocol(supported_protocols, + PRT_PADDING, + PROTOVER_HS_SETUP_PADDING)); + + /* FlowCtrl */ + tt_assert(protocol_list_supports_protocol(supported_protocols, + PRT_FLOWCTRL, + PROTOVER_FLOWCTRL_V1)); done: ; @@ -645,8 +680,8 @@ test_protover_vote_roundtrip_ours(void *args) (void) args; const char *examples[] = { protover_get_supported_protocols(), - DIRVOTE_RECCOMEND_RELAY_PROTO, - DIRVOTE_RECCOMEND_CLIENT_PROTO, + DIRVOTE_RECOMMEND_RELAY_PROTO, + DIRVOTE_RECOMMEND_CLIENT_PROTO, DIRVOTE_REQUIRE_RELAY_PROTO, DIRVOTE_REQUIRE_CLIENT_PROTO, }; @@ -676,6 +711,228 @@ test_protover_vote_roundtrip_ours(void *args) tor_free(result); } +/* Stringifies its argument. + * 4 -> "4" */ +#define STR(x) #x + +#ifdef COCCI +#define PROTOVER(proto_string, version_macro) +#else +/* Generate a protocol version string using proto_string and version_macro. + * PROTOVER("HSIntro", PROTOVER_HS_INTRO_DOS) -> "HSIntro" "=" "5" + * Uses two levels of macros to turn PROTOVER_HS_INTRO_DOS into "5". + */ +#define PROTOVER(proto_string, version_macro) \ + (proto_string "=" STR(version_macro)) +#endif + +#define DEBUG_PROTOVER(flags) \ + STMT_BEGIN \ + log_debug(LD_GENERAL, \ + "protovers:\n" \ + "protocols_known: %d,\n" \ + "supports_extend2_cells: %d,\n" \ + "supports_accepting_ipv6_extends: %d,\n" \ + "supports_initiating_ipv6_extends: %d,\n" \ + "supports_canonical_ipv6_conns: %d,\n" \ + "supports_ed25519_link_handshake_compat: %d,\n" \ + "supports_ed25519_link_handshake_any: %d,\n" \ + "supports_ed25519_hs_intro: %d,\n" \ + "supports_establish_intro_dos_extension: %d,\n" \ + "supports_v3_hsdir: %d,\n" \ + "supports_v3_rendezvous_point: %d,\n" \ + "supports_hs_setup_padding: %d.", \ + (flags).protocols_known, \ + (flags).supports_extend2_cells, \ + (flags).supports_accepting_ipv6_extends, \ + (flags).supports_initiating_ipv6_extends, \ + (flags).supports_canonical_ipv6_conns, \ + (flags).supports_ed25519_link_handshake_compat, \ + (flags).supports_ed25519_link_handshake_any, \ + (flags).supports_ed25519_hs_intro, \ + (flags).supports_establish_intro_dos_extension, \ + (flags).supports_v3_hsdir, \ + (flags).supports_v3_rendezvous_point, \ + (flags).supports_hs_setup_padding); \ + STMT_END + +/* Test that the proto_string version version_macro sets summary_flag. */ +#define TEST_PROTOVER(proto_string, version_macro, summary_flag) \ + STMT_BEGIN \ + memset(&flags, 0, sizeof(flags)); \ + summarize_protover_flags(&flags, \ + PROTOVER(proto_string, version_macro), \ + NULL); \ + DEBUG_PROTOVER(flags); \ + tt_int_op(flags.protocols_known, OP_EQ, 1); \ + tt_int_op(flags.summary_flag, OP_EQ, 1); \ + flags.protocols_known = 0; \ + flags.summary_flag = 0; \ + tt_mem_op(&flags, OP_EQ, &zero_flags, sizeof(flags)); \ + STMT_END + +static void +test_protover_summarize_flags(void *args) +{ + (void) args; + char pv[30]; + memset(&pv, 0, sizeof(pv)); + + protover_summary_cache_free_all(); + + protover_summary_flags_t zero_flags; + memset(&zero_flags, 0, sizeof(zero_flags)); + protover_summary_flags_t flags; + + memset(&flags, 0, sizeof(flags)); + summarize_protover_flags(&flags, NULL, NULL); + DEBUG_PROTOVER(flags); + tt_mem_op(&flags, OP_EQ, &zero_flags, sizeof(flags)); + + memset(&flags, 0, sizeof(flags)); + summarize_protover_flags(&flags, "", ""); + DEBUG_PROTOVER(flags); + tt_mem_op(&flags, OP_EQ, &zero_flags, sizeof(flags)); + + /* Now check version exceptions */ + + /* EXTEND2 cell support */ + memset(&flags, 0, sizeof(flags)); + summarize_protover_flags(&flags, NULL, "Tor 0.2.4.8-alpha"); + DEBUG_PROTOVER(flags); + tt_int_op(flags.protocols_known, OP_EQ, 1); + tt_int_op(flags.supports_extend2_cells, OP_EQ, 1); + /* Now clear those flags, and check the rest are zero */ + flags.protocols_known = 0; + flags.supports_extend2_cells = 0; + tt_mem_op(&flags, OP_EQ, &zero_flags, sizeof(flags)); + + /* disabling HSDir v3 support for buggy versions */ + memset(&flags, 0, sizeof(flags)); + summarize_protover_flags(&flags, + PROTOVER("HSDir", PROTOVER_HSDIR_V3), + NULL); + DEBUG_PROTOVER(flags); + tt_int_op(flags.protocols_known, OP_EQ, 1); + tt_int_op(flags.supports_v3_hsdir, OP_EQ, 1); + /* Now clear those flags, and check the rest are zero */ + flags.protocols_known = 0; + flags.supports_v3_hsdir = 0; + tt_mem_op(&flags, OP_EQ, &zero_flags, sizeof(flags)); + + memset(&flags, 0, sizeof(flags)); + summarize_protover_flags(&flags, + PROTOVER("HSDir", PROTOVER_HSDIR_V3), + "Tor 0.3.0.7"); + DEBUG_PROTOVER(flags); + tt_int_op(flags.protocols_known, OP_EQ, 1); + /* Now clear that flag, and check the rest are zero */ + flags.protocols_known = 0; + tt_mem_op(&flags, OP_EQ, &zero_flags, sizeof(flags)); + + /* Now check standard summaries */ + + /* LinkAuth */ + memset(&flags, 0, sizeof(flags)); + summarize_protover_flags(&flags, + PROTOVER("LinkAuth", + PROTOVER_LINKAUTH_ED25519_HANDSHAKE), + NULL); + DEBUG_PROTOVER(flags); + tt_int_op(flags.protocols_known, OP_EQ, 1); + tt_int_op(flags.supports_ed25519_link_handshake_compat, OP_EQ, 1); + tt_int_op(flags.supports_ed25519_link_handshake_any, OP_EQ, 1); + /* Now clear those flags, and check the rest are zero */ + flags.protocols_known = 0; + flags.supports_ed25519_link_handshake_compat = 0; + flags.supports_ed25519_link_handshake_any = 0; + tt_mem_op(&flags, OP_EQ, &zero_flags, sizeof(flags)); + + /* Test one greater */ + memset(&flags, 0, sizeof(flags)); + snprintf(pv, sizeof(pv), + "%s=%d", "LinkAuth", PROTOVER_LINKAUTH_ED25519_HANDSHAKE + 1); + summarize_protover_flags(&flags, pv, NULL); + DEBUG_PROTOVER(flags); + tt_int_op(flags.protocols_known, OP_EQ, 1); + tt_int_op(flags.supports_ed25519_link_handshake_compat, OP_EQ, 0); + tt_int_op(flags.supports_ed25519_link_handshake_any, OP_EQ, 1); + /* Now clear those flags, and check the rest are zero */ + flags.protocols_known = 0; + flags.supports_ed25519_link_handshake_compat = 0; + flags.supports_ed25519_link_handshake_any = 0; + tt_mem_op(&flags, OP_EQ, &zero_flags, sizeof(flags)); + + /* Test one less */ + memset(&flags, 0, sizeof(flags)); + snprintf(pv, sizeof(pv), + "%s=%d", "LinkAuth", PROTOVER_LINKAUTH_ED25519_HANDSHAKE - 1); + summarize_protover_flags(&flags, pv, NULL); + DEBUG_PROTOVER(flags); + tt_int_op(flags.protocols_known, OP_EQ, 1); + tt_int_op(flags.supports_ed25519_link_handshake_compat, OP_EQ, 0); + tt_int_op(flags.supports_ed25519_link_handshake_any, OP_EQ, 0); + /* Now clear those flags, and check the rest are zero */ + flags.protocols_known = 0; + flags.supports_ed25519_link_handshake_compat = 0; + flags.supports_ed25519_link_handshake_any = 0; + tt_mem_op(&flags, OP_EQ, &zero_flags, sizeof(flags)); + + /* We don't test "one more" and "one less" for each protocol version. + * But that could be a useful thing to add. */ + + /* Relay */ + memset(&flags, 0, sizeof(flags)); + /* This test relies on these versions being equal */ + tt_int_op(PROTOVER_RELAY_EXTEND2, OP_EQ, PROTOVER_RELAY_ACCEPT_IPV6); + summarize_protover_flags(&flags, + PROTOVER("Relay", PROTOVER_RELAY_EXTEND2), NULL); + DEBUG_PROTOVER(flags); + tt_int_op(flags.protocols_known, OP_EQ, 1); + tt_int_op(flags.supports_extend2_cells, OP_EQ, 1); + tt_int_op(flags.supports_accepting_ipv6_extends, OP_EQ, 1); + /* Now clear those flags, and check the rest are zero */ + flags.protocols_known = 0; + flags.supports_extend2_cells = 0; + flags.supports_accepting_ipv6_extends = 0; + tt_mem_op(&flags, OP_EQ, &zero_flags, sizeof(flags)); + + memset(&flags, 0, sizeof(flags)); + /* This test relies on these versions being equal */ + tt_int_op(PROTOVER_RELAY_EXTEND_IPV6, OP_EQ, PROTOVER_RELAY_CANONICAL_IPV6); + summarize_protover_flags(&flags, + PROTOVER("Relay", PROTOVER_RELAY_EXTEND_IPV6), + NULL); + DEBUG_PROTOVER(flags); + tt_int_op(flags.protocols_known, OP_EQ, 1); + tt_int_op(flags.supports_accepting_ipv6_extends, OP_EQ, 1); + tt_int_op(flags.supports_initiating_ipv6_extends, OP_EQ, 1); + tt_int_op(flags.supports_canonical_ipv6_conns, OP_EQ, 1); + /* Now clear those flags, and check the rest are zero */ + flags.protocols_known = 0; + flags.supports_accepting_ipv6_extends = 0; + flags.supports_initiating_ipv6_extends = 0; + flags.supports_canonical_ipv6_conns = 0; + tt_mem_op(&flags, OP_EQ, &zero_flags, sizeof(flags)); + + TEST_PROTOVER("HSIntro", PROTOVER_HS_INTRO_V3, + supports_ed25519_hs_intro); + TEST_PROTOVER("HSIntro", PROTOVER_HS_INTRO_DOS, + supports_establish_intro_dos_extension); + + TEST_PROTOVER("HSRend", PROTOVER_HS_RENDEZVOUS_POINT_V3, + supports_v3_rendezvous_point); + + TEST_PROTOVER("HSDir", PROTOVER_HSDIR_V3, + supports_v3_hsdir); + + TEST_PROTOVER("Padding", PROTOVER_HS_SETUP_PADDING, + supports_hs_setup_padding); + + done: + ; +} + #define PV_TEST(name, flags) \ { #name, test_protover_ ##name, (flags), NULL, NULL } @@ -690,5 +947,7 @@ struct testcase_t protover_tests[] = { PV_TEST(supported_protocols, 0), PV_TEST(vote_roundtrip, 0), PV_TEST(vote_roundtrip_ours, 0), + /* fork, because we memoize flags internally */ + PV_TEST(summarize_flags, TT_FORK), END_OF_TESTCASES }; diff --git a/src/test/test_rebind.py b/src/test/test_rebind.py index 3fc3deb68e..6b72ece911 100644 --- a/src/test/test_rebind.py +++ b/src/test/test_rebind.py @@ -116,7 +116,7 @@ tor_process = subprocess.Popen([tor_path, if tor_process == None: fail('ERROR: running tor failed') -wait_for_log('Opened Control listener on') +wait_for_log('Opened Control listener') try_connecting_to_socksport() diff --git a/src/test/test_relay.c b/src/test/test_relay.c index 066aeaa7b3..ee704ceb8c 100644 --- a/src/test/test_relay.c +++ b/src/test/test_relay.c @@ -3,12 +3,12 @@ #define CIRCUITBUILD_PRIVATE #define RELAY_PRIVATE -#define REPHIST_PRIVATE +#define BWHIST_PRIVATE #include "core/or/or.h" #include "core/or/circuitbuild.h" #include "core/or/circuitlist.h" #include "core/or/channeltls.h" -#include "feature/stats/rephist.h" +#include "feature/stats/bwhist.h" #include "core/or/relay.h" #include "lib/container/order.h" /* For init/free stuff */ @@ -17,6 +17,14 @@ #include "core/or/cell_st.h" #include "core/or/or_circuit_st.h" +#define RESOLVE_ADDR_PRIVATE +#include "feature/nodelist/dirlist.h" +#include "feature/relay/relay_find_addr.h" +#include "feature/relay/routermode.h" +#include "feature/dirclient/dir_server_st.h" + +#include "app/config/resolve_addr.h" + /* Test suite stuff */ #include "test/test.h" #include "test/fakechans.h" @@ -24,6 +32,13 @@ static void test_relay_append_cell_to_circuit_queue(void *arg); +static int +mock_server_mode_true(const or_options_t *options) +{ + (void) options; + return 1; +} + static void assert_circuit_ok_mock(const circuit_t *c) { @@ -192,10 +207,167 @@ test_relay_append_cell_to_circuit_queue(void *arg) return; } +static void +test_suggested_address(void *arg) +{ + int ret; + const char *untrusted_id = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; + dir_server_t *ds = NULL; + tor_addr_t ipv4_addr, ipv6_addr, cache_addr; + tor_addr_t trusted_addr, untrusted_addr; + tor_addr_port_t trusted_ap_v6 = { .port = 443 }; + + (void) arg; + + MOCK(server_mode, mock_server_mode_true); + + /* Unstrusted relay source. */ + ret = tor_addr_parse(&untrusted_addr, "8.8.8.8"); + tt_int_op(ret, OP_EQ, AF_INET); + + /* Add gabelmoo as a trusted directory authority. */ + ret = tor_addr_parse(&trusted_addr, "[2001:638:a000:4140::ffff:189]"); + tt_int_op(ret, OP_EQ, AF_INET6); + tor_addr_copy(&trusted_ap_v6.addr, &trusted_addr); + + ds = trusted_dir_server_new("gabelmoo", "131.188.40.189", 80, 443, + &trusted_ap_v6, + "F2044413DAC2E02E3D6BCF4735A19BCA1DE97281", + "ED03BB616EB2F60BEC80151114BB25CEF515B226", + V3_DIRINFO, 1.0); + tt_assert(ds); + dir_server_add(ds); + + /* 1. Valid IPv4 from a trusted authority (gabelmoo). */ + ret = tor_addr_parse(&ipv4_addr, "1.2.3.4"); + relay_address_new_suggestion(&ipv4_addr, &ds->ipv4_addr, ds->digest); + resolved_addr_get_suggested(AF_INET, &cache_addr); + tt_assert(tor_addr_eq(&cache_addr, &ipv4_addr)); + resolve_addr_reset_suggested(AF_INET); + + /* 2. Valid IPv6 from a trusted authority (gabelmoo). */ + ret = tor_addr_parse(&ipv6_addr, "[4242::4242]"); + relay_address_new_suggestion(&ipv6_addr, &ds->ipv6_addr, ds->digest); + resolved_addr_get_suggested(AF_INET6, &cache_addr); + tt_assert(tor_addr_eq(&cache_addr, &ipv6_addr)); + resolve_addr_reset_suggested(AF_INET6); + + /* 3. Valid IPv4 but untrusted source. */ + ret = tor_addr_parse(&ipv4_addr, "1.2.3.4"); + relay_address_new_suggestion(&ipv4_addr, &untrusted_addr, untrusted_id); + resolved_addr_get_suggested(AF_INET, &cache_addr); + tt_assert(tor_addr_is_unspec(&cache_addr)); + + /* 4. Valid IPv6 but untrusted source. */ + ret = tor_addr_parse(&ipv6_addr, "[4242::4242]"); + relay_address_new_suggestion(&ipv6_addr, &untrusted_addr, untrusted_id); + resolved_addr_get_suggested(AF_INET6, &cache_addr); + tt_assert(tor_addr_is_unspec(&cache_addr)); + + /* 5. Internal IPv4 from a trusted authority (gabelmoo). */ + ret = tor_addr_parse(&ipv4_addr, "127.0.0.1"); + relay_address_new_suggestion(&ipv4_addr, &ds->ipv4_addr, ds->digest); + resolved_addr_get_suggested(AF_INET, &cache_addr); + tt_assert(tor_addr_is_unspec(&cache_addr)); + + /* 6. Internal IPv6 from a trusted authority (gabelmoo). */ + ret = tor_addr_parse(&ipv6_addr, "[::1]"); + relay_address_new_suggestion(&ipv6_addr, &ds->ipv6_addr, ds->digest); + resolved_addr_get_suggested(AF_INET6, &cache_addr); + tt_assert(tor_addr_is_unspec(&cache_addr)); + + /* 7. IPv4 from a trusted authority (gabelmoo). */ + relay_address_new_suggestion(&ds->ipv4_addr, &ds->ipv4_addr, ds->digest); + resolved_addr_get_suggested(AF_INET, &cache_addr); + tt_assert(tor_addr_is_unspec(&cache_addr)); + + /* 8. IPv6 from a trusted authority (gabelmoo). */ + relay_address_new_suggestion(&ds->ipv6_addr, &ds->ipv6_addr, ds->digest); + resolved_addr_get_suggested(AF_INET6, &cache_addr); + tt_assert(tor_addr_is_unspec(&cache_addr)); + + done: + dirlist_free_all(); + + UNMOCK(server_mode); +} + +static void +test_find_addr_to_publish(void *arg) +{ + int family; + bool ret; + tor_addr_t ipv4_addr, ipv6_addr, cache_addr; + or_options_t options; + + (void) arg; + + memset(&options, 0, sizeof(options)); + + /* Populate our resolved cache with a valid IPv4 and IPv6. */ + family = tor_addr_parse(&ipv4_addr, "1.2.3.4"); + tt_int_op(family, OP_EQ, AF_INET); + resolved_addr_set_last(&ipv4_addr, "NA", NULL); + resolved_addr_get_last(AF_INET, &cache_addr); + tt_assert(tor_addr_eq(&ipv4_addr, &cache_addr)); + + family = tor_addr_parse(&ipv6_addr, "[4242::4242]"); + tt_int_op(family, OP_EQ, AF_INET6); + resolved_addr_set_last(&ipv6_addr, "NA", NULL); + resolved_addr_get_last(AF_INET6, &cache_addr); + tt_assert(tor_addr_eq(&ipv6_addr, &cache_addr)); + + /* 1. Address located in the resolved cache. */ + ret = relay_find_addr_to_publish(&options, AF_INET, + RELAY_FIND_ADDR_CACHE_ONLY, &cache_addr); + tt_assert(ret); + tt_assert(tor_addr_eq(&ipv4_addr, &cache_addr)); + + ret = relay_find_addr_to_publish(&options, AF_INET6, + RELAY_FIND_ADDR_CACHE_ONLY, &cache_addr); + tt_assert(ret); + tt_assert(tor_addr_eq(&ipv6_addr, &cache_addr)); + resolved_addr_reset_last(AF_INET); + resolved_addr_reset_last(AF_INET6); + + /* 2. No IP in the resolve cache, go to the suggested cache. We will ignore + * the find_my_address() code path because that is extensively tested in + * another unit tests. */ + resolved_addr_set_suggested(&ipv4_addr); + ret = relay_find_addr_to_publish(&options, AF_INET, + RELAY_FIND_ADDR_CACHE_ONLY, &cache_addr); + tt_assert(ret); + tt_assert(tor_addr_eq(&ipv4_addr, &cache_addr)); + + resolved_addr_set_suggested(&ipv6_addr); + ret = relay_find_addr_to_publish(&options, AF_INET6, + RELAY_FIND_ADDR_CACHE_ONLY, &cache_addr); + tt_assert(ret); + tt_assert(tor_addr_eq(&ipv6_addr, &cache_addr)); + resolve_addr_reset_suggested(AF_INET); + resolve_addr_reset_suggested(AF_INET6); + + /* 3. No IP anywhere. */ + ret = relay_find_addr_to_publish(&options, AF_INET, + RELAY_FIND_ADDR_CACHE_ONLY, &cache_addr); + tt_assert(!ret); + ret = relay_find_addr_to_publish(&options, AF_INET6, + RELAY_FIND_ADDR_CACHE_ONLY, &cache_addr); + tt_assert(!ret); + + done: + ; +} + struct testcase_t relay_tests[] = { { "append_cell_to_circuit_queue", test_relay_append_cell_to_circuit_queue, TT_FORK, NULL, NULL }, { "close_circ_rephist", test_relay_close_circuit, TT_FORK, NULL, NULL }, + { "suggested_address", test_suggested_address, + TT_FORK, NULL, NULL }, + { "find_addr_to_publish", test_find_addr_to_publish, + TT_FORK, NULL, NULL }, + END_OF_TESTCASES }; diff --git a/src/test/test_router.c b/src/test/test_router.c index cf0c2b3dd1..ddd043b941 100644 --- a/src/test/test_router.c +++ b/src/test/test_router.c @@ -24,7 +24,7 @@ #include "feature/nodelist/routerlist.h" #include "feature/nodelist/routerstatus_st.h" #include "feature/relay/router.h" -#include "feature/stats/rephist.h" +#include "feature/stats/bwhist.h" #include "lib/crypt_ops/crypto_curve25519.h" #include "lib/crypt_ops/crypto_ed25519.h" #include "lib/encoding/confline.h" @@ -61,8 +61,8 @@ rtr_tests_router_get_my_routerinfo(void) mock_routerinfo = tor_malloc_zero(sizeof(routerinfo_t)); mock_routerinfo->nickname = tor_strdup("ConlonNancarrow"); - mock_routerinfo->addr = 123456789; - mock_routerinfo->or_port = 443; + tor_addr_from_ipv4h(&mock_routerinfo->ipv4_addr, 123456789); + mock_routerinfo->ipv4_orport = 443; mock_routerinfo->platform = tor_strdup("unittest"); mock_routerinfo->cache_info.published_on = now; mock_routerinfo->identity_pkey = crypto_pk_dup_key(ident_key); @@ -226,13 +226,13 @@ test_router_check_descriptor_bandwidth_changed(void *arg) /* When uptime is less than 24h and bandwidthcapacity does not change * Uptime: 10800, last_changed: x, Previous bw: 10000, Current bw: 20001 */ - MOCK(rep_hist_bandwidth_assess, mock_rep_hist_bandwidth_assess); + MOCK(bwhist_bandwidth_assess, mock_rep_hist_bandwidth_assess); setup_full_capture_of_logs(LOG_INFO); check_descriptor_bandwidth_changed(time(NULL) + 6*60*60 + 1); expect_log_msg_containing( "Measured bandwidth has changed; rebuilding descriptor."); UNMOCK(get_uptime); - UNMOCK(rep_hist_bandwidth_assess); + UNMOCK(bwhist_bandwidth_assess); teardown_capture_of_logs(); /* When uptime is more than 24h */ @@ -507,13 +507,12 @@ test_router_get_advertised_or_port(void *arg) listener_connection_t *listener = NULL; tor_addr_port_t ipv6; - // Test one failing case of router_get_advertised_ipv6_or_ap(). - router_get_advertised_ipv6_or_ap(opts, &ipv6); + // Test one failing case of routerconf_find_ipv6_or_ap(). + routerconf_find_ipv6_or_ap(opts, &ipv6); tt_str_op(fmt_addrport(&ipv6.addr, ipv6.port), OP_EQ, "[::]:0"); - // And one failing case of router_get_advertised_or_port(). - tt_int_op(0, OP_EQ, router_get_advertised_or_port_by_af(opts, AF_INET)); - tt_int_op(0, OP_EQ, router_get_advertised_or_port(opts)); + // And one failing case of routerconf_find_or_port(). + tt_int_op(0, OP_EQ, routerconf_find_or_port(opts, AF_INET)); // Set up a couple of configured ports. config_line_append(&opts->ORPort_lines, "ORPort", "[1234::5678]:auto"); @@ -522,13 +521,12 @@ test_router_get_advertised_or_port(void *arg) tt_assert(r == 0); // There are no listeners, so the "auto" case will turn up no results. - tt_int_op(0, OP_EQ, router_get_advertised_or_port_by_af(opts, AF_INET6)); - router_get_advertised_ipv6_or_ap(opts, &ipv6); + tt_int_op(0, OP_EQ, routerconf_find_or_port(opts, AF_INET6)); + routerconf_find_ipv6_or_ap(opts, &ipv6); tt_str_op(fmt_addrport(&ipv6.addr, ipv6.port), OP_EQ, "[::]:0"); // This will return the matching value from the configured port. - tt_int_op(9999, OP_EQ, router_get_advertised_or_port_by_af(opts, AF_INET)); - tt_int_op(9999, OP_EQ, router_get_advertised_or_port(opts)); + tt_int_op(9999, OP_EQ, routerconf_find_or_port(opts, AF_INET)); // Now set up a dummy listener. MOCK(get_connection_array, mock_get_connection_array); @@ -538,16 +536,15 @@ test_router_get_advertised_or_port(void *arg) smartlist_add(fake_connection_array, TO_CONN(listener)); // We should get a port this time. - tt_int_op(54321, OP_EQ, router_get_advertised_or_port_by_af(opts, AF_INET6)); + tt_int_op(54321, OP_EQ, routerconf_find_or_port(opts, AF_INET6)); - // Test one succeeding case of router_get_advertised_ipv6_or_ap(). - router_get_advertised_ipv6_or_ap(opts, &ipv6); + // Test one succeeding case of routerconf_find_ipv6_or_ap(). + routerconf_find_ipv6_or_ap(opts, &ipv6); tt_str_op(fmt_addrport(&ipv6.addr, ipv6.port), OP_EQ, "[1234::5678]:54321"); // This will return the matching value from the configured port. - tt_int_op(9999, OP_EQ, router_get_advertised_or_port_by_af(opts, AF_INET)); - tt_int_op(9999, OP_EQ, router_get_advertised_or_port(opts)); + tt_int_op(9999, OP_EQ, routerconf_find_or_port(opts, AF_INET)); done: or_options_free(opts); @@ -573,28 +570,26 @@ test_router_get_advertised_or_port_localhost(void *arg) tt_assert(r == 0); // We should refuse to advertise them, since we have default dirauths. - router_get_advertised_ipv6_or_ap(opts, &ipv6); + routerconf_find_ipv6_or_ap(opts, &ipv6); tt_str_op(fmt_addrport(&ipv6.addr, ipv6.port), OP_EQ, "[::]:0"); // But the lower-level function should still report the correct value - tt_int_op(9999, OP_EQ, router_get_advertised_or_port_by_af(opts, AF_INET6)); + tt_int_op(9999, OP_EQ, routerconf_find_or_port(opts, AF_INET6)); // The IPv4 checks are done in resolve_my_address(), which doesn't use // ORPorts so we can't test them here. (See #33681.) Both these lower-level // functions should still report the correct value. - tt_int_op(8888, OP_EQ, router_get_advertised_or_port_by_af(opts, AF_INET)); - tt_int_op(8888, OP_EQ, router_get_advertised_or_port(opts)); + tt_int_op(8888, OP_EQ, routerconf_find_or_port(opts, AF_INET)); // Now try with a fake authority set up. config_line_append(&opts->DirAuthorities, "DirAuthority", "127.0.0.1:1066 " "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"); - tt_int_op(9999, OP_EQ, router_get_advertised_or_port_by_af(opts, AF_INET6)); - router_get_advertised_ipv6_or_ap(opts, &ipv6); + tt_int_op(9999, OP_EQ, routerconf_find_or_port(opts, AF_INET6)); + routerconf_find_ipv6_or_ap(opts, &ipv6); tt_str_op(fmt_addrport(&ipv6.addr, ipv6.port), OP_EQ, "[::1]:9999"); - tt_int_op(8888, OP_EQ, router_get_advertised_or_port_by_af(opts, AF_INET)); - tt_int_op(8888, OP_EQ, router_get_advertised_or_port(opts)); + tt_int_op(8888, OP_EQ, routerconf_find_or_port(opts, AF_INET)); done: or_options_free(opts); diff --git a/src/test/test_routerkeys.c b/src/test/test_routerkeys.c index fc437dccc0..8d653e44a2 100644 --- a/src/test/test_routerkeys.c +++ b/src/test/test_routerkeys.c @@ -51,7 +51,7 @@ test_routerkeys_write_fingerprint(void *arg) tt_int_op(crypto_pk_cmp_keys(get_server_identity_key(),key),OP_EQ,0); /* Write fingerprint file */ - tt_int_op(0, OP_EQ, router_write_fingerprint(0)); + tt_int_op(0, OP_EQ, router_write_fingerprint(0, 0)); cp = read_file_to_str(get_fname("write_fingerprint/fingerprint"), 0, NULL); crypto_pk_get_fingerprint(key, fp, 0); @@ -61,7 +61,7 @@ test_routerkeys_write_fingerprint(void *arg) tor_free(cp2); /* Write hashed-fingerprint file */ - tt_int_op(0, OP_EQ, router_write_fingerprint(1)); + tt_int_op(0, OP_EQ, router_write_fingerprint(1, 0)); cp = read_file_to_str(get_fname("write_fingerprint/hashed-fingerprint"), 0, NULL); crypto_pk_get_hashed_fingerprint(key, fp); @@ -73,7 +73,7 @@ test_routerkeys_write_fingerprint(void *arg) /* Replace outdated file */ write_str_to_file(get_fname("write_fingerprint/hashed-fingerprint"), "junk goes here", 0); - tt_int_op(0, OP_EQ, router_write_fingerprint(1)); + tt_int_op(0, OP_EQ, router_write_fingerprint(1, 0)); cp = read_file_to_str(get_fname("write_fingerprint/hashed-fingerprint"), 0, NULL); crypto_pk_get_hashed_fingerprint(key, fp); @@ -90,6 +90,51 @@ test_routerkeys_write_fingerprint(void *arg) } static void +test_routerkeys_write_ed25519_identity(void *arg) +{ + crypto_pk_t *key = pk_generate(2); + or_options_t *options = get_options_mutable(); + time_t now = time(NULL); + const char *ddir = get_fname("write_fingerprint"); + char *cp = NULL, *cp2 = NULL; + char ed25519_id[BASE64_DIGEST256_LEN + 1]; + + (void) arg; + + tt_assert(key); + + options->ORPort_set = 1; /* So that we can get the server ID key */ + tor_free(options->DataDirectory); + options->DataDirectory = tor_strdup(ddir); + options->Nickname = tor_strdup("haflinger"); + set_server_identity_key(key); + set_client_identity_key(crypto_pk_dup_key(key)); + + load_ed_keys(options, now); + tt_assert(get_master_identity_key()); + + tt_int_op(0, OP_EQ, check_private_dir(ddir, CPD_CREATE, NULL)); + + /* Write fingerprint file */ + tt_int_op(0, OP_EQ, router_write_fingerprint(0, 1)); + cp = read_file_to_str(get_fname("write_fingerprint/fingerprint-ed25519"), + 0, NULL); + digest256_to_base64(ed25519_id, + (const char *) get_master_identity_key()->pubkey); + tor_asprintf(&cp2, "haflinger %s\n", ed25519_id); + tt_str_op(cp, OP_EQ, cp2); + tor_free(cp); + tor_free(cp2); + + done: + crypto_pk_free(key); + set_client_identity_key(NULL); + tor_free(cp); + tor_free(cp2); + routerkeys_free_all(); +} + +static void test_routerkeys_ed_certs(void *args) { (void)args; @@ -695,6 +740,7 @@ test_routerkeys_rsa_ed_crosscert(void *arg) struct testcase_t routerkeys_tests[] = { TEST(write_fingerprint, TT_FORK), + TEST(write_ed25519_identity, TT_FORK), TEST(ed_certs, TT_FORK), TEST(ed_key_create, TT_FORK), TEST(ed_key_init_basic, TT_FORK), diff --git a/src/test/test_routerlist.c b/src/test/test_routerlist.c index f2a83c18a3..91a75d7080 100644 --- a/src/test/test_routerlist.c +++ b/src/test/test_routerlist.c @@ -341,18 +341,18 @@ test_router_pick_directory_server_impl(void *arg) node_router1->rs->is_v2_dir = 0; node_router3->rs->is_v2_dir = 0; - tmp_dirport1 = node_router1->rs->dir_port; - tmp_dirport3 = node_router3->rs->dir_port; - node_router1->rs->dir_port = 0; - node_router3->rs->dir_port = 0; + tmp_dirport1 = node_router1->rs->ipv4_dirport; + tmp_dirport3 = node_router3->rs->ipv4_dirport; + node_router1->rs->ipv4_dirport = 0; + node_router3->rs->ipv4_dirport = 0; rs = router_pick_directory_server_impl(V3_DIRINFO, flags, NULL); tt_ptr_op(rs, OP_NE, NULL); tt_assert(tor_memeq(rs->identity_digest, router2_id, DIGEST_LEN)); rs = NULL; node_router1->rs->is_v2_dir = 1; node_router3->rs->is_v2_dir = 1; - node_router1->rs->dir_port = tmp_dirport1; - node_router3->rs->dir_port = tmp_dirport3; + node_router1->rs->ipv4_dirport = tmp_dirport1; + node_router3->rs->ipv4_dirport = tmp_dirport3; node_router1->is_valid = 0; node_router3->is_valid = 0; @@ -381,23 +381,23 @@ test_router_pick_directory_server_impl(void *arg) options->ReachableORAddresses = policy_line; policies_parse_from_options(options); - node_router1->rs->or_port = 444; - node_router2->rs->or_port = 443; - node_router3->rs->or_port = 442; + node_router1->rs->ipv4_orport = 444; + node_router2->rs->ipv4_orport = 443; + node_router3->rs->ipv4_orport = 442; rs = router_pick_directory_server_impl(V3_DIRINFO, flags, NULL); tt_ptr_op(rs, OP_NE, NULL); tt_assert(tor_memeq(rs->identity_digest, router3_id, DIGEST_LEN)); - node_router1->rs->or_port = 442; - node_router2->rs->or_port = 443; - node_router3->rs->or_port = 444; + node_router1->rs->ipv4_orport = 442; + node_router2->rs->ipv4_orport = 443; + node_router3->rs->ipv4_orport = 444; rs = router_pick_directory_server_impl(V3_DIRINFO, flags, NULL); tt_ptr_op(rs, OP_NE, NULL); tt_assert(tor_memeq(rs->identity_digest, router1_id, DIGEST_LEN)); /* Fascist firewall and overloaded */ - node_router1->rs->or_port = 442; - node_router2->rs->or_port = 443; - node_router3->rs->or_port = 442; + node_router1->rs->ipv4_orport = 442; + node_router2->rs->ipv4_orport = 443; + node_router3->rs->ipv4_orport = 442; node_router3->rs->last_dir_503_at = now; rs = router_pick_directory_server_impl(V3_DIRINFO, flags, NULL); tt_ptr_op(rs, OP_NE, NULL); @@ -410,12 +410,12 @@ test_router_pick_directory_server_impl(void *arg) policy_line->value = tor_strdup("accept *:80, reject *:*"); options->ReachableDirAddresses = policy_line; policies_parse_from_options(options); - node_router1->rs->or_port = 442; - node_router2->rs->or_port = 441; - node_router3->rs->or_port = 443; - node_router1->rs->dir_port = 80; - node_router2->rs->dir_port = 80; - node_router3->rs->dir_port = 81; + node_router1->rs->ipv4_orport = 442; + node_router2->rs->ipv4_orport = 441; + node_router3->rs->ipv4_orport = 443; + node_router1->rs->ipv4_dirport = 80; + node_router2->rs->ipv4_dirport = 80; + node_router3->rs->ipv4_dirport = 81; node_router1->rs->last_dir_503_at = now; rs = router_pick_directory_server_impl(V3_DIRINFO, flags, NULL); tt_ptr_op(rs, OP_NE, NULL); diff --git a/src/test/test_routerset.c b/src/test/test_routerset.c index 892ac6e210..d00eefa23f 100644 --- a/src/test/test_routerset.c +++ b/src/test/test_routerset.c @@ -1417,12 +1417,62 @@ test_rset_contains_router(void *arg) ri.nickname = (char *)nickname; r = routerset_contains_router(set, &ri, country); - tt_int_op(r, OP_EQ, 4); + done: routerset_free(set); } +static void +test_rset_contains_router_ipv4(void *arg) +{ + routerset_t *set; + routerinfo_t ri; + country_t country = 1; + int r; + const char *s; + (void) arg; + + /* IPv4 address test. */ + memset(&ri, 0, sizeof(ri)); + set = routerset_new(); + s = "10.0.0.1"; + r = routerset_parse(set, s, ""); + tor_addr_from_ipv4h(&ri.ipv4_addr, 0x0a000001); + ri.ipv4_orport = 1234; + + r = routerset_contains_router(set, &ri, country); + tt_int_op(r, OP_EQ, 3); + + done: + routerset_free(set); +} + +static void +test_rset_contains_router_ipv6(void *arg) +{ + routerset_t *set; + routerinfo_t ri; + country_t country = 1; + int r; + const char *s; + (void) arg; + + /* IPv6 address test. */ + memset(&ri, 0, sizeof(ri)); + set = routerset_new(); + s = "2600::1"; + r = routerset_parse(set, s, ""); + tor_addr_parse(&ri.ipv6_addr, "2600::1"); + ri.ipv6_orport = 12345; + + r = routerset_contains_router(set, &ri, country); + tt_int_op(r, OP_EQ, 3); + + done: + routerset_free(set); +} + /* * Functional test for routerset_contains_routerstatus. */ @@ -2144,6 +2194,10 @@ struct testcase_t routerset_tests[] = { { "contains_extendinfo", test_rset_contains_extendinfo, TT_FORK, NULL, NULL }, { "contains_router", test_rset_contains_router, TT_FORK, NULL, NULL }, + { "contains_router_ipv4", test_rset_contains_router_ipv4, + TT_FORK, NULL, NULL }, + { "contains_router_ipv6", test_rset_contains_router_ipv6, + TT_FORK, NULL, NULL }, { "contains_routerstatus", test_rset_contains_routerstatus, TT_FORK, NULL, NULL }, { "contains_none", test_rset_contains_none, TT_FORK, NULL, NULL }, diff --git a/src/test/test_stats.c b/src/test/test_stats.c index 291473ebc9..f0715c2e45 100644 --- a/src/test/test_stats.c +++ b/src/test/test_stats.c @@ -39,6 +39,7 @@ #include "test/test.h" #include "core/mainloop/mainloop.h" #include "lib/memarea/memarea.h" +#include "feature/stats/connstats.h" #include "feature/stats/rephist.h" #include "app/config/statefile.h" @@ -111,37 +112,41 @@ test_stats(void *arg) /* Continue with testing connection statistics; we shouldn't collect * conn stats without initializing them. */ - rep_hist_note_or_conn_bytes(1, 20, 400, now); - s = rep_hist_format_conn_stats(now + 86400); + conn_stats_note_or_conn_bytes(1, 20, 400, now, false); + s = conn_stats_format(now + 86400); tt_ptr_op(s, OP_EQ, NULL); /* Initialize stats, note bytes, and generate history string. */ - rep_hist_conn_stats_init(now); - rep_hist_note_or_conn_bytes(1, 30000, 400000, now); - rep_hist_note_or_conn_bytes(1, 30000, 400000, now + 5); - rep_hist_note_or_conn_bytes(2, 400000, 30000, now + 10); - rep_hist_note_or_conn_bytes(2, 400000, 30000, now + 15); - s = rep_hist_format_conn_stats(now + 86400); - tt_str_op("conn-bi-direct 2010-08-12 13:27:30 (86400 s) 0,0,1,0\n",OP_EQ, s); + conn_stats_init(now); + conn_stats_note_or_conn_bytes(1, 30000, 400000, now, false); + conn_stats_note_or_conn_bytes(1, 30000, 400000, now + 5, false); + conn_stats_note_or_conn_bytes(2, 400000, 30000, now + 10, true); + conn_stats_note_or_conn_bytes(2, 400000, 30000, now + 15, true); + s = conn_stats_format(now + 86400); + tt_str_op("conn-bi-direct 2010-08-12 13:27:30 (86400 s) 0,0,1,0\n" + "ipv6-conn-bi-direct 2010-08-12 13:27:30 (86400 s) 0,0,0,0\n", + OP_EQ, s); tor_free(s); /* Stop collecting stats, add some bytes, and ensure we don't generate * a history string. */ - rep_hist_conn_stats_term(); - rep_hist_note_or_conn_bytes(2, 400000, 30000, now + 15); - s = rep_hist_format_conn_stats(now + 86400); + conn_stats_terminate(); + conn_stats_note_or_conn_bytes(2, 400000, 30000, now + 15, true); + s = conn_stats_format(now + 86400); tt_ptr_op(s, OP_EQ, NULL); /* Re-start stats, add some bytes, reset stats, and see what history we * get when observing no bytes at all. */ - rep_hist_conn_stats_init(now); - rep_hist_note_or_conn_bytes(1, 30000, 400000, now); - rep_hist_note_or_conn_bytes(1, 30000, 400000, now + 5); - rep_hist_note_or_conn_bytes(2, 400000, 30000, now + 10); - rep_hist_note_or_conn_bytes(2, 400000, 30000, now + 15); - rep_hist_reset_conn_stats(now); - s = rep_hist_format_conn_stats(now + 86400); - tt_str_op("conn-bi-direct 2010-08-12 13:27:30 (86400 s) 0,0,0,0\n",OP_EQ, s); + conn_stats_init(now); + conn_stats_note_or_conn_bytes(1, 30000, 400000, now, false); + conn_stats_note_or_conn_bytes(1, 30000, 400000, now + 5, false); + conn_stats_note_or_conn_bytes(2, 400000, 30000, now + 10, true); + conn_stats_note_or_conn_bytes(2, 400000, 30000, now + 15, true); + conn_stats_reset(now); + s = conn_stats_format(now + 86400); + tt_str_op("conn-bi-direct 2010-08-12 13:27:30 (86400 s) 0,0,0,0\n" + "ipv6-conn-bi-direct 2010-08-12 13:27:30 (86400 s) 0,0,0,0\n", + OP_EQ, s); tor_free(s); /* Continue with testing buffer statistics; we shouldn't collect buffer diff --git a/src/test/test_tortls.c b/src/test/test_tortls.c index a822bc5ad8..12ba873650 100644 --- a/src/test/test_tortls.c +++ b/src/test/test_tortls.c @@ -105,6 +105,17 @@ const char* caCertString = "-----BEGIN CERTIFICATE-----\n" "Yy1RT69d0rwYc5u/vnqODz1IjvT90smsrkBumGt791FAFeg=\n" "-----END CERTIFICATE-----\n"; +static tor_x509_cert_t *fixed_x509_cert = NULL; +static tor_x509_cert_t * +get_peer_cert_mock_return_fixed(tor_tls_t *tls) +{ + (void)tls; + if (fixed_x509_cert) + return tor_x509_cert_dup(fixed_x509_cert); + else + return NULL; +} + tor_x509_cert_impl_t * read_cert_from(const char *str) { @@ -513,6 +524,67 @@ test_tortls_verify(void *ignored) crypto_pk_free(k); } +static void +test_tortls_cert_matches_key(void *ignored) +{ + (void)ignored; + + tor_x509_cert_impl_t *cert1 = NULL, + *cert2 = NULL, + *cert3 = NULL, + *cert4 = NULL; + tor_x509_cert_t *c1 = NULL, *c2 = NULL, *c3 = NULL, *c4 = NULL; + crypto_pk_t *k1 = NULL, *k2 = NULL, *k3 = NULL; + + k1 = pk_generate(1); + k2 = pk_generate(2); + k3 = pk_generate(3); + + cert1 = tor_tls_create_certificate(k1, k2, "A", "B", 1000); + cert2 = tor_tls_create_certificate(k1, k3, "C", "D", 1000); + cert3 = tor_tls_create_certificate(k2, k3, "C", "D", 1000); + cert4 = tor_tls_create_certificate(k3, k2, "E", "F", 1000); + + tt_assert(cert1 && cert2 && cert3 && cert4); + + c1 = tor_x509_cert_new(cert1); cert1 = NULL; + c2 = tor_x509_cert_new(cert2); cert2 = NULL; + c3 = tor_x509_cert_new(cert3); cert3 = NULL; + c4 = tor_x509_cert_new(cert4); cert4 = NULL; + + tt_assert(c1 && c2 && c3 && c4); + + MOCK(tor_tls_get_peer_cert, get_peer_cert_mock_return_fixed); + + fixed_x509_cert = NULL; + /* If the peer has no certificate, it shouldn't match anything. */ + tt_assert(! tor_tls_cert_matches_key(NULL, c1)); + tt_assert(! tor_tls_cert_matches_key(NULL, c2)); + tt_assert(! tor_tls_cert_matches_key(NULL, c3)); + tt_assert(! tor_tls_cert_matches_key(NULL, c4)); + fixed_x509_cert = c1; + /* If the peer has a certificate, it should match every cert with the same + * subject key. */ + tt_assert(tor_tls_cert_matches_key(NULL, c1)); + tt_assert(tor_tls_cert_matches_key(NULL, c2)); + tt_assert(! tor_tls_cert_matches_key(NULL, c3)); + tt_assert(! tor_tls_cert_matches_key(NULL, c4)); + + done: + tor_x509_cert_free(c1); + tor_x509_cert_free(c2); + tor_x509_cert_free(c3); + tor_x509_cert_free(c4); + if (cert1) tor_x509_cert_impl_free(cert1); + if (cert2) tor_x509_cert_impl_free(cert2); + if (cert3) tor_x509_cert_impl_free(cert3); + if (cert4) tor_x509_cert_impl_free(cert4); + crypto_pk_free(k1); + crypto_pk_free(k2); + crypto_pk_free(k3); + UNMOCK(tor_tls_get_peer_cert); +} + #define LOCAL_TEST_CASE(name, flags) \ { #name, test_tortls_##name, (flags|TT_FORK), NULL, NULL } @@ -533,5 +605,6 @@ struct testcase_t tortls_tests[] = { LOCAL_TEST_CASE(is_server, 0), LOCAL_TEST_CASE(bridge_init, TT_FORK), LOCAL_TEST_CASE(verify, TT_FORK), + LOCAL_TEST_CASE(cert_matches_key, 0), END_OF_TESTCASES }; diff --git a/src/test/test_tortls_openssl.c b/src/test/test_tortls_openssl.c index 4318f7f1eb..e20b0d1ede 100644 --- a/src/test/test_tortls_openssl.c +++ b/src/test/test_tortls_openssl.c @@ -475,75 +475,6 @@ fake_x509_free(X509 *cert) } #endif /* !defined(OPENSSL_OPAQUE) */ -static tor_x509_cert_t *fixed_x509_cert = NULL; -static tor_x509_cert_t * -get_peer_cert_mock_return_fixed(tor_tls_t *tls) -{ - (void)tls; - if (fixed_x509_cert) - return tor_x509_cert_dup(fixed_x509_cert); - else - return NULL; -} - -static void -test_tortls_cert_matches_key(void *ignored) -{ - (void)ignored; - - X509 *cert1 = NULL, *cert2 = NULL, *cert3 = NULL, *cert4 = NULL; - tor_x509_cert_t *c1 = NULL, *c2 = NULL, *c3 = NULL, *c4 = NULL; - crypto_pk_t *k1 = NULL, *k2 = NULL, *k3 = NULL; - - k1 = pk_generate(1); - k2 = pk_generate(2); - k3 = pk_generate(3); - - cert1 = tor_tls_create_certificate(k1, k2, "A", "B", 1000); - cert2 = tor_tls_create_certificate(k1, k3, "C", "D", 1000); - cert3 = tor_tls_create_certificate(k2, k3, "C", "D", 1000); - cert4 = tor_tls_create_certificate(k3, k2, "E", "F", 1000); - - tt_assert(cert1 && cert2 && cert3 && cert4); - - c1 = tor_x509_cert_new(cert1); cert1 = NULL; - c2 = tor_x509_cert_new(cert2); cert2 = NULL; - c3 = tor_x509_cert_new(cert3); cert3 = NULL; - c4 = tor_x509_cert_new(cert4); cert4 = NULL; - - tt_assert(c1 && c2 && c3 && c4); - - MOCK(tor_tls_get_peer_cert, get_peer_cert_mock_return_fixed); - - fixed_x509_cert = NULL; - /* If the peer has no certificate, it shouldn't match anything. */ - tt_assert(! tor_tls_cert_matches_key(NULL, c1)); - tt_assert(! tor_tls_cert_matches_key(NULL, c2)); - tt_assert(! tor_tls_cert_matches_key(NULL, c3)); - tt_assert(! tor_tls_cert_matches_key(NULL, c4)); - fixed_x509_cert = c1; - /* If the peer has a certificate, it should match every cert with the same - * subject key. */ - tt_assert(tor_tls_cert_matches_key(NULL, c1)); - tt_assert(tor_tls_cert_matches_key(NULL, c2)); - tt_assert(! tor_tls_cert_matches_key(NULL, c3)); - tt_assert(! tor_tls_cert_matches_key(NULL, c4)); - - done: - tor_x509_cert_free(c1); - tor_x509_cert_free(c2); - tor_x509_cert_free(c3); - tor_x509_cert_free(c4); - if (cert1) X509_free(cert1); - if (cert2) X509_free(cert2); - if (cert3) X509_free(cert3); - if (cert4) X509_free(cert4); - crypto_pk_free(k1); - crypto_pk_free(k2); - crypto_pk_free(k3); - UNMOCK(tor_tls_get_peer_cert); -} - #ifndef OPENSSL_OPAQUE static void test_tortls_cert_get_key(void *ignored) @@ -2275,7 +2206,6 @@ struct testcase_t tortls_openssl_tests[] = { INTRUSIVE_TEST_CASE(get_error, TT_FORK), LOCAL_TEST_CASE(always_accept_verify_cb, 0), INTRUSIVE_TEST_CASE(x509_cert_free, 0), - LOCAL_TEST_CASE(cert_matches_key, 0), INTRUSIVE_TEST_CASE(cert_get_key, 0), LOCAL_TEST_CASE(get_my_client_auth_key, TT_FORK), INTRUSIVE_TEST_CASE(get_ciphersuite_name, 0), diff --git a/src/test/test_util.c b/src/test/test_util.c index b2ee7cd35c..2aee07a26a 100644 --- a/src/test/test_util.c +++ b/src/test/test_util.c @@ -206,6 +206,54 @@ test_util_read_file_eof_zero_bytes(void *arg) test_util_read_until_eof_impl("tor_test_fifo_empty", 0, 10000); } +static void +test_util_read_file_endlines(void *arg) +{ + (void)arg; + + char *fname = NULL; + char *read_content = NULL; + int r = -1; + + /* Write a file that contains both \n and \r\n as line ending. */ + const char *file_content = "foo bar\n" + "foo bar baz\r\n" + "foo bar\r\n"; + + const char *expected_file_content = "foo bar\n" + "foo bar baz\n" + "foo bar\n"; + + fname = tor_strdup(get_fname("file_with_crlf_ending")); + + r = write_bytes_to_file(fname, file_content, strlen(file_content), 1); + tt_int_op(r, OP_EQ, 0); + + /* Read the file in text mode: we strip \r's from the files on both Windows + * and UNIX. */ + read_content = read_file_to_str(fname, 0, NULL); + + tt_ptr_op(read_content, OP_NE, NULL); + tt_int_op(strlen(read_content), OP_EQ, strlen(expected_file_content)); + tt_str_op(read_content, OP_EQ, expected_file_content); + + tor_free(read_content); + + /* Read the file in binary mode: we should preserve the \r here. */ + read_content = read_file_to_str(fname, RFTS_BIN, NULL); + + tt_ptr_op(read_content, OP_NE, NULL); + tt_int_op(strlen(read_content), OP_EQ, strlen(file_content)); + tt_str_op(read_content, OP_EQ, file_content); + + tor_free(read_content); + + done: + unlink(fname); + tor_free(fname); + tor_free(read_content); +} + /* Test the basic expected behaviour for write_chunks_to_file. * NOTE: This will need to be updated if we ever change the tempfile location * or extension */ @@ -5651,7 +5699,7 @@ test_util_hostname_validation(void *arg) tt_assert(string_is_valid_nonrfc_hostname("luck.y13.")); // We allow punycode TLDs. For examples, see - // http://data.iana.org/TLD/tlds-alpha-by-domain.txt + // https://data.iana.org/TLD/tlds-alpha-by-domain.txt tt_assert(string_is_valid_nonrfc_hostname("example.xn--l1acc")); done: @@ -6495,6 +6543,7 @@ struct testcase_t util_tests[] = { UTIL_TEST(read_file_eof_two_loops, 0), UTIL_TEST(read_file_eof_two_loops_b, 0), UTIL_TEST(read_file_eof_zero_bytes, 0), + UTIL_TEST(read_file_endlines, 0), UTIL_TEST(write_chunks_to_file, 0), UTIL_TEST(mathlog, 0), UTIL_TEST(fraction, 0), diff --git a/src/test/test_voting_flags.c b/src/test/test_voting_flags.c index ae89e43889..72f70b9865 100644 --- a/src/test/test_voting_flags.c +++ b/src/test/test_voting_flags.c @@ -42,10 +42,10 @@ setup_cfg(flag_vote_test_cfg_t *c) c->ri.cache_info.published_on = c->now - 100; c->expected.published_on = c->now - 100; - c->ri.addr = 0x7f010105; - c->expected.addr = 0x7f010105; - c->ri.or_port = 9090; - c->expected.or_port = 9090; + tor_addr_from_ipv4h(&c->ri.ipv4_addr, 0x7f010105); + tor_addr_from_ipv4h(&c->expected.ipv4_addr, 0x7f010105); + c->ri.ipv4_orport = 9090; + c->expected.ipv4_orport = 9090; tor_addr_make_null(&c->ri.ipv6_addr, AF_INET6); tor_addr_make_null(&c->expected.ipv6_addr, AF_INET6); @@ -69,9 +69,9 @@ check_result(flag_vote_test_cfg_t *c) // identity_digest and descriptor_digest are not set here. - tt_uint_op(rs.addr, OP_EQ, c->expected.addr); - tt_uint_op(rs.or_port, OP_EQ, c->expected.or_port); - tt_uint_op(rs.dir_port, OP_EQ, c->expected.dir_port); + tt_assert(tor_addr_eq(&rs.ipv4_addr, &c->expected.ipv4_addr)); + tt_uint_op(rs.ipv4_orport, OP_EQ, c->expected.ipv4_orport); + tt_uint_op(rs.ipv4_dirport, OP_EQ, c->expected.ipv4_dirport); tt_assert(tor_addr_eq(&rs.ipv6_addr, &c->expected.ipv6_addr)); tt_uint_op(rs.ipv6_orport, OP_EQ, c->expected.ipv6_orport); diff --git a/src/test/testing_common.c b/src/test/testing_common.c index b3337f24b0..cd93055dfb 100644 --- a/src/test/testing_common.c +++ b/src/test/testing_common.c @@ -18,6 +18,7 @@ #include "lib/crypt_ops/crypto_ed25519.h" #include "lib/crypt_ops/crypto_rand.h" #include "feature/stats/predict_ports.h" +#include "feature/stats/bwhist.h" #include "feature/stats/rephist.h" #include "lib/err/backtrace.h" #include "test/test.h" @@ -333,6 +334,7 @@ main(int c, const char **v) return 1; } rep_hist_init(); + bwhist_init(); setup_directory(); initialize_mainloop_events(); options_init(options); diff --git a/src/trunnel/circpad_negotiation.c b/src/trunnel/circpad_negotiation.c index 547818f2ec..4e3ee3d5bd 100644 --- a/src/trunnel/circpad_negotiation.c +++ b/src/trunnel/circpad_negotiation.c @@ -112,6 +112,17 @@ circpad_negotiate_set_echo_request(circpad_negotiate_t *inp, uint8_t val) inp->echo_request = val; return 0; } +uint32_t +circpad_negotiate_get_machine_ctr(const circpad_negotiate_t *inp) +{ + return inp->machine_ctr; +} +int +circpad_negotiate_set_machine_ctr(circpad_negotiate_t *inp, uint32_t val) +{ + inp->machine_ctr = val; + return 0; +} const char * circpad_negotiate_check(const circpad_negotiate_t *obj) { @@ -148,6 +159,9 @@ circpad_negotiate_encoded_len(const circpad_negotiate_t *obj) /* Length of u8 echo_request IN [0, 1] */ result += 1; + + /* Length of u32 machine_ctr */ + result += 4; return result; } int @@ -203,6 +217,13 @@ circpad_negotiate_encode(uint8_t *output, const size_t avail, const circpad_nego trunnel_set_uint8(ptr, (obj->echo_request)); written += 1; ptr += 1; + /* Encode u32 machine_ctr */ + trunnel_assert(written <= avail); + if (avail - written < 4) + goto truncated; + trunnel_set_uint32(ptr, trunnel_htonl(obj->machine_ctr)); + written += 4; ptr += 4; + trunnel_assert(ptr == output + written); #ifdef TRUNNEL_CHECK_ENCODED_LEN @@ -263,6 +284,11 @@ circpad_negotiate_parse_into(circpad_negotiate_t *obj, const uint8_t *input, con remaining -= 1; ptr += 1; if (! (obj->echo_request == 0 || obj->echo_request == 1)) goto fail; + + /* Parse u32 machine_ctr */ + CHECK_REMAINING(4, truncated); + obj->machine_ctr = trunnel_ntohl(trunnel_get_uint32(ptr)); + remaining -= 4; ptr += 4; trunnel_assert(ptr + remaining == input + len_in); return len_in - remaining; @@ -372,6 +398,17 @@ circpad_negotiated_set_machine_type(circpad_negotiated_t *inp, uint8_t val) inp->machine_type = val; return 0; } +uint32_t +circpad_negotiated_get_machine_ctr(const circpad_negotiated_t *inp) +{ + return inp->machine_ctr; +} +int +circpad_negotiated_set_machine_ctr(circpad_negotiated_t *inp, uint32_t val) +{ + inp->machine_ctr = val; + return 0; +} const char * circpad_negotiated_check(const circpad_negotiated_t *obj) { @@ -408,6 +445,9 @@ circpad_negotiated_encoded_len(const circpad_negotiated_t *obj) /* Length of u8 machine_type */ result += 1; + + /* Length of u32 machine_ctr */ + result += 4; return result; } int @@ -463,6 +503,13 @@ circpad_negotiated_encode(uint8_t *output, const size_t avail, const circpad_neg trunnel_set_uint8(ptr, (obj->machine_type)); written += 1; ptr += 1; + /* Encode u32 machine_ctr */ + trunnel_assert(written <= avail); + if (avail - written < 4) + goto truncated; + trunnel_set_uint32(ptr, trunnel_htonl(obj->machine_ctr)); + written += 4; ptr += 4; + trunnel_assert(ptr == output + written); #ifdef TRUNNEL_CHECK_ENCODED_LEN @@ -523,6 +570,11 @@ circpad_negotiated_parse_into(circpad_negotiated_t *obj, const uint8_t *input, c CHECK_REMAINING(1, truncated); obj->machine_type = (trunnel_get_uint8(ptr)); remaining -= 1; ptr += 1; + + /* Parse u32 machine_ctr */ + CHECK_REMAINING(4, truncated); + obj->machine_ctr = trunnel_ntohl(trunnel_get_uint32(ptr)); + remaining -= 4; ptr += 4; trunnel_assert(ptr + remaining == input + len_in); return len_in - remaining; diff --git a/src/trunnel/circpad_negotiation.h b/src/trunnel/circpad_negotiation.h index ba9155019e..9004540d43 100644 --- a/src/trunnel/circpad_negotiation.h +++ b/src/trunnel/circpad_negotiation.h @@ -26,6 +26,7 @@ struct circpad_negotiate_st { uint8_t machine_type; /** If true, send a relay_drop reply.. */ uint8_t echo_request; + uint32_t machine_ctr; uint8_t trunnel_error_code_; }; #endif @@ -42,6 +43,14 @@ struct circpad_negotiated_st { /** Machine type is left unbounded because we can specify * new machines in the consensus */ uint8_t machine_type; + /** + * This field is used for shutdown synchronization. It is OK if + * it wraps, because all we need to do is make sure the STOP + * command is actually for the currently active machine. + * For backward-compatibility, though, 0 has special meaning + * (it means match any machine). + */ + uint32_t machine_ctr; uint8_t trunnel_error_code_; }; #endif @@ -118,6 +127,15 @@ uint8_t circpad_negotiate_get_echo_request(const circpad_negotiate_t *inp); * code on 'inp' on failure. */ int circpad_negotiate_set_echo_request(circpad_negotiate_t *inp, uint8_t val); +/** Return the value of the machine_ctr field of the + * circpad_negotiate_t in 'inp' + */ +uint32_t circpad_negotiate_get_machine_ctr(const circpad_negotiate_t *inp); +/** Set the value of the machine_ctr field of the circpad_negotiate_t + * in 'inp' to 'val'. Return 0 on success; return -1 and set the error + * code on 'inp' on failure. + */ +int circpad_negotiate_set_machine_ctr(circpad_negotiate_t *inp, uint32_t val); /** Return a newly allocated circpad_negotiated with all elements set * to zero. */ @@ -190,6 +208,15 @@ uint8_t circpad_negotiated_get_machine_type(const circpad_negotiated_t *inp); * -1 and set the error code on 'inp' on failure. */ int circpad_negotiated_set_machine_type(circpad_negotiated_t *inp, uint8_t val); +/** Return the value of the machine_ctr field of the + * circpad_negotiated_t in 'inp' + */ +uint32_t circpad_negotiated_get_machine_ctr(const circpad_negotiated_t *inp); +/** Set the value of the machine_ctr field of the circpad_negotiated_t + * in 'inp' to 'val'. Return 0 on success; return -1 and set the error + * code on 'inp' on failure. + */ +int circpad_negotiated_set_machine_ctr(circpad_negotiated_t *inp, uint32_t val); #endif diff --git a/src/trunnel/circpad_negotiation.trunnel b/src/trunnel/circpad_negotiation.trunnel index abbc929cc5..68fed6a013 100644 --- a/src/trunnel/circpad_negotiation.trunnel +++ b/src/trunnel/circpad_negotiation.trunnel @@ -27,6 +27,13 @@ struct circpad_negotiate { // FIXME-MP-AP: Maybe we just say to transition to the first state // here instead.. Also what about delay before responding? u8 echo_request IN [0,1]; + + // This field is used for shutdown synchronization. It is OK if + // it wraps, because all we need to do is make sure the STOP + // command is actually for the currently active machine. + // For backward-compatibility, though, 0 has special meaning + // (it means match any machine). + u32 machine_ctr; }; /** @@ -41,4 +48,14 @@ struct circpad_negotiated { /** Machine type is left unbounded because we can specify * new machines in the consensus */ u8 machine_type; + + /** + * This field is used for shutdown synchronization. It is OK if + * it wraps, because all we need to do is make sure the STOP + * command is actually for the currently active machine. + * For backward-compatibility, though, 0 has special meaning + * (it means match any machine). + */ + u32 machine_ctr; + }; diff --git a/src/win32/orconfig.h b/src/win32/orconfig.h index 3c8f91d53b..3c7b2ab5fc 100644 --- a/src/win32/orconfig.h +++ b/src/win32/orconfig.h @@ -217,7 +217,7 @@ #define USING_TWOS_COMPLEMENT /* Version number of package */ -#define VERSION "0.4.4.0-alpha-dev" +#define VERSION "0.4.5.0-alpha-dev" #define HAVE_STRUCT_SOCKADDR_IN6 #define HAVE_STRUCT_IN6_ADDR |