diff options
Diffstat (limited to 'src/app/config')
-rw-r--r-- | src/app/config/app_config.md | 3 | ||||
-rw-r--r-- | src/app/config/config.c | 437 | ||||
-rw-r--r-- | src/app/config/config.h | 15 | ||||
-rw-r--r-- | src/app/config/or_options_st.h | 72 | ||||
-rw-r--r-- | src/app/config/or_state_st.h | 18 | ||||
-rw-r--r-- | src/app/config/resolve_addr.c | 1017 | ||||
-rw-r--r-- | src/app/config/resolve_addr.h | 51 | ||||
-rw-r--r-- | src/app/config/statefile.c | 91 | ||||
-rw-r--r-- | src/app/config/statefile.h | 1 | ||||
-rw-r--r-- | src/app/config/testnet.inc | 3 |
10 files changed, 1251 insertions, 457 deletions
diff --git a/src/app/config/app_config.md b/src/app/config/app_config.md index b359ce77f6..96a55494ff 100644 --- a/src/app/config/app_config.md +++ b/src/app/config/app_config.md @@ -2,5 +2,4 @@ @brief app/config: Top-level configuration code Refactoring this module is a work in progress, see -[ticket 29211](https://trac.torproject.org/projects/tor/ticket/29211). - +[ticket 29211](https://bugs.torproject.org/tpo/core/tor/29211) diff --git a/src/app/config/config.c b/src/app/config/config.c index a0c188adc4..c7799ec1a2 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> * @@ -91,6 +91,7 @@ #include "feature/dirclient/dirclient_modes.h" #include "feature/hibernate/hibernate.h" #include "feature/hs/hs_config.h" +#include "feature/metrics/metrics.h" #include "feature/nodelist/dirlist.h" #include "feature/nodelist/networkstatus.h" #include "feature/nodelist/nickname.h" @@ -140,6 +141,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 +315,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 +326,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), @@ -545,7 +549,7 @@ static const config_var_t option_vars_[] = { V(LogTimeGranularity, MSEC_INTERVAL, "1 second"), V(TruncateLogFile, BOOL, "0"), V_IMMUTABLE(SyslogIdentityTag, STRING, NULL), - V_IMMUTABLE(AndroidIdentityTag,STRING, NULL), + OBSOLETE("AndroidIdentityTag"), V(LongLivedPorts, CSV, "21,22,706,1863,5050,5190,5222,5223,6523,6667,6697,8300"), VAR("MapAddress", LINELIST, AddressMap, NULL), @@ -557,6 +561,8 @@ static const config_var_t option_vars_[] = { OBSOLETE("MaxOnionsPending"), V(MaxOnionQueueDelay, MSEC_INTERVAL, "1750 msec"), V(MaxUnparseableDescSizeToLog, MEMUNIT, "10 MB"), + VPORT(MetricsPort), + V(MetricsPortPolicy, LINELIST, NULL), VAR("MyFamily", LINELIST, MyFamily_lines, NULL), V(NewCircuitPeriod, INTERVAL, "30 seconds"), OBSOLETE("NamingAuthoritativeDirectory"), @@ -577,6 +583,7 @@ static const config_var_t option_vars_[] = { V(OutboundBindAddress, LINELIST, NULL), V(OutboundBindAddressOR, LINELIST, NULL), V(OutboundBindAddressExit, LINELIST, NULL), + V(OutboundBindAddressPT, LINELIST, NULL), OBSOLETE("PathBiasDisableRate"), V(PathBiasCircThreshold, INT, "-1"), @@ -606,7 +613,7 @@ static const config_var_t option_vars_[] = { V(TestingAuthKeySlop, INTERVAL, "3 hours"), V(TestingSigningKeySlop, INTERVAL, "1 day"), - V(OptimisticData, AUTOBOOL, "auto"), + OBSOLETE("OptimisticData"), OBSOLETE("PortForwarding"), OBSOLETE("PortForwardingHelper"), OBSOLETE("PreferTunneledDirConns"), @@ -1511,7 +1518,7 @@ compute_group_readable_flag(const char *datadir, * the datadirectory */ return datadir_gr; } else { - /* The directores are different, so we default to "not group-readable" */ + /* The directories are different, so we default to "not group-readable" */ return 0; } } @@ -1735,8 +1742,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); }); @@ -2101,6 +2108,16 @@ options_act,(const or_options_t *old_options)) "in a non-anonymous mode. It will provide NO ANONYMITY."); } + /* 31851: OutboundBindAddressExit is relay-only */ + if (parse_outbound_addresses(options, 0, &msg) < 0) { + // LCOV_EXCL_START + log_warn(LD_BUG, "Failed parsing previously validated outbound " + "bind addresses: %s", msg); + tor_free(msg); + return -1; + // LCOV_EXCL_STOP + } + if (options->Bridges) { mark_bridge_list(); for (cl = options->Bridges; cl; cl = cl->next) { @@ -2262,16 +2279,6 @@ options_act,(const or_options_t *old_options)) tor_free(http_authenticator); } - /* 31851: OutboundBindAddressExit is relay-only */ - if (parse_outbound_addresses(options, 0, &msg) < 0) { - // LCOV_EXCL_START - log_warn(LD_BUG, "Failed parsing previously validated outbound " - "bind addresses: %s", msg); - tor_free(msg); - return -1; - // LCOV_EXCL_STOP - } - config_maybe_load_geoip_files_(options, old_options); if (geoip_is_loaded(AF_INET) && options->GeoIPExcludeUnknown) { @@ -2465,6 +2472,8 @@ static const struct { { .name="--key-expiration", .takes_argument=ARGUMENT_OPTIONAL, .command=CMD_KEY_EXPIRATION }, + { .name="--format", + .takes_argument=ARGUMENT_NECESSARY }, { .name="--newpass" }, { .name="--no-passphrase" }, { .name="--passphrase-fd", @@ -2722,6 +2731,140 @@ list_enabled_modules(void) // test variants in test_parseconf.sh to no useful purpose. } +/** Prints compile-time and runtime library versions. */ +static void +print_library_versions(void) +{ + printf("Tor version %s. \n", get_version()); + printf("Library versions\tCompiled\t\tRuntime\n"); + printf("Libevent\t\t%-15s\t\t%s\n", + tor_libevent_get_header_version_str(), + tor_libevent_get_version_str()); +#ifdef ENABLE_OPENSSL + printf("OpenSSL \t\t%-15s\t\t%s\n", + crypto_openssl_get_header_version_str(), + crypto_openssl_get_version_str()); +#endif +#ifdef ENABLE_NSS + printf("NSS \t\t%-15s\t\t%s\n", + crypto_nss_get_header_version_str(), + crypto_nss_get_version_str()); +#endif + if (tor_compress_supports_method(ZLIB_METHOD)) { + printf("Zlib \t\t%-15s\t\t%s\n", + tor_compress_version_str(ZLIB_METHOD), + tor_compress_header_version_str(ZLIB_METHOD)); + } + if (tor_compress_supports_method(LZMA_METHOD)) { + printf("Liblzma \t\t%-15s\t\t%s\n", + tor_compress_version_str(LZMA_METHOD), + tor_compress_header_version_str(LZMA_METHOD)); + } + if (tor_compress_supports_method(ZSTD_METHOD)) { + printf("Libzstd \t\t%-15s\t\t%s\n", + 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? +} + +/** Handles the --no-passphrase command line option. */ +static int +handle_cmdline_no_passphrase(tor_cmdline_mode_t command) +{ + if (command == CMD_KEYGEN) { + get_options_mutable()->keygen_force_passphrase = FORCE_PASSPHRASE_OFF; + return 0; + } else { + log_err(LD_CONFIG, "--no-passphrase specified without --keygen!"); + return -1; + } +} + +/** Handles the --format command line option. */ +static int +handle_cmdline_format(tor_cmdline_mode_t command, const char *value) +{ + if (command == CMD_KEY_EXPIRATION) { + // keep the same order as enum key_expiration_format + const char *formats[] = { "iso8601", "timestamp" }; + int format = -1; + for (unsigned i = 0; i < ARRAY_LENGTH(formats); i++) { + if (!strcmp(value, formats[i])) { + format = i; + break; + } + } + + if (format < 0) { + log_err(LD_CONFIG, "Invalid --format value %s", escaped(value)); + return -1; + } else { + get_options_mutable()->key_expiration_format = format; + } + return 0; + } else { + log_err(LD_CONFIG, "--format specified without --key-expiration!"); + return -1; + } +} + +/** Handles the --newpass command line option. */ +static int +handle_cmdline_newpass(tor_cmdline_mode_t command) +{ + if (command == CMD_KEYGEN) { + get_options_mutable()->change_key_passphrase = 1; + return 0; + } else { + log_err(LD_CONFIG, "--newpass specified without --keygen!"); + return -1; + } +} + +/** Handles the --passphrase-fd command line option. */ +static int +handle_cmdline_passphrase_fd(tor_cmdline_mode_t command, const char *value) +{ + if (get_options()->keygen_force_passphrase == FORCE_PASSPHRASE_OFF) { + log_err(LD_CONFIG, "--no-passphrase specified with --passphrase-fd!"); + return -1; + } else if (command != CMD_KEYGEN) { + log_err(LD_CONFIG, "--passphrase-fd specified without --keygen!"); + return -1; + } else { + int ok = 1; + long fd = tor_parse_long(value, 10, 0, INT_MAX, &ok, NULL); + if (fd < 0 || ok == 0) { + log_err(LD_CONFIG, "Invalid --passphrase-fd value %s", escaped(value)); + return -1; + } + get_options_mutable()->keygen_passphrase_fd = (int)fd; + get_options_mutable()->use_keygen_passphrase_fd = 1; + get_options_mutable()->keygen_force_passphrase = FORCE_PASSPHRASE_ON; + return 0; + } +} + +/** Handles the --master-key command line option. */ +static int +handle_cmdline_master_key(tor_cmdline_mode_t command, const char *value) +{ + if (command != CMD_KEYGEN) { + log_err(LD_CONFIG, "--master-key without --keygen!"); + return -1; + } else { + get_options_mutable()->master_key_fname = tor_strdup(value); + return 0; + } +} + /* Return true if <b>options</b> is using the default authorities, and false * if any authority-related option has been overridden. */ int @@ -2771,10 +2914,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 +3368,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); @@ -3453,7 +3596,7 @@ options_validate_cb(const void *old_options_, void *options_, char **msg) "configured. This is bad because it's very easy to locate your " "entry guard which can then lead to the deanonymization of your " "hidden service -- for more details, see " - "https://trac.torproject.org/projects/tor/ticket/14917. " + "https://bugs.torproject.org/tpo/core/tor/14917. " "For this reason, the use of one EntryNodes with an hidden " "service is prohibited until a better solution is found."); return -1; @@ -3470,7 +3613,7 @@ options_validate_cb(const void *old_options_, void *options_, char **msg) "be harmful to the service anonymity. Because of this, we " "recommend you either don't do that or make sure you know what " "you are doing. For more details, please look at " - "https://trac.torproject.org/projects/tor/ticket/21155."); + "https://bugs.torproject.org/tpo/core/tor/21155."); } /* Single Onion Services: non-anonymous hidden services */ @@ -4045,7 +4188,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); @@ -4351,37 +4494,7 @@ options_init_from_torrc(int argc, char **argv) } if (config_line_find(cmdline_only_options, "--library-versions")) { - printf("Tor version %s. \n", get_version()); - printf("Library versions\tCompiled\t\tRuntime\n"); - printf("Libevent\t\t%-15s\t\t%s\n", - tor_libevent_get_header_version_str(), - tor_libevent_get_version_str()); -#ifdef ENABLE_OPENSSL - printf("OpenSSL \t\t%-15s\t\t%s\n", - crypto_openssl_get_header_version_str(), - crypto_openssl_get_version_str()); -#endif -#ifdef ENABLE_NSS - printf("NSS \t\t%-15s\t\t%s\n", - crypto_nss_get_header_version_str(), - crypto_nss_get_version_str()); -#endif - if (tor_compress_supports_method(ZLIB_METHOD)) { - printf("Zlib \t\t%-15s\t\t%s\n", - tor_compress_version_str(ZLIB_METHOD), - tor_compress_header_version_str(ZLIB_METHOD)); - } - if (tor_compress_supports_method(LZMA_METHOD)) { - printf("Liblzma \t\t%-15s\t\t%s\n", - tor_compress_version_str(LZMA_METHOD), - tor_compress_header_version_str(LZMA_METHOD)); - } - if (tor_compress_supports_method(ZSTD_METHOD)) { - printf("Libzstd \t\t%-15s\t\t%s\n", - tor_compress_version_str(ZSTD_METHOD), - tor_compress_header_version_str(ZSTD_METHOD)); - } - //TODO: Hex versions? + print_library_versions(); return 1; } @@ -4395,10 +4508,7 @@ options_init_from_torrc(int argc, char **argv) cf = tor_strdup(""); } else { cf_defaults = load_torrc_from_disk(cmdline_only_options, 1); - - const config_line_t *f_line = config_line_find(cmdline_only_options, - "-f"); - + const config_line_t *f_line = config_line_find(cmdline_only_options, "-f"); const int read_torrc_from_stdin = (f_line != NULL && strcmp(f_line->value, "-") == 0); @@ -4419,74 +4529,54 @@ options_init_from_torrc(int argc, char **argv) retval = options_init_from_string(cf_defaults, cf, command, command_arg, &errmsg); - if (retval < 0) goto err; if (config_line_find(cmdline_only_options, "--no-passphrase")) { - if (command == CMD_KEYGEN) { - get_options_mutable()->keygen_force_passphrase = FORCE_PASSPHRASE_OFF; - } else { - log_err(LD_CONFIG, "--no-passphrase specified without --keygen!"); + if (handle_cmdline_no_passphrase(command) < 0) { retval = -1; goto err; } } + const config_line_t *format_line = config_line_find(cmdline_only_options, + "--format"); + if (format_line) { + if (handle_cmdline_format(command, format_line->value) < 0) { + retval = -1; + goto err; + } + } else { + get_options_mutable()->key_expiration_format = + KEY_EXPIRATION_FORMAT_ISO8601; + } + if (config_line_find(cmdline_only_options, "--newpass")) { - if (command == CMD_KEYGEN) { - get_options_mutable()->change_key_passphrase = 1; - } else { - log_err(LD_CONFIG, "--newpass specified without --keygen!"); + if (handle_cmdline_newpass(command) < 0) { retval = -1; goto err; } } - { - const config_line_t *fd_line = config_line_find(cmdline_only_options, - "--passphrase-fd"); - if (fd_line) { - if (get_options()->keygen_force_passphrase == FORCE_PASSPHRASE_OFF) { - log_err(LD_CONFIG, "--no-passphrase specified with --passphrase-fd!"); - retval = -1; - goto err; - } else if (command != CMD_KEYGEN) { - log_err(LD_CONFIG, "--passphrase-fd specified without --keygen!"); - retval = -1; - goto err; - } else { - const char *v = fd_line->value; - int ok = 1; - long fd = tor_parse_long(v, 10, 0, INT_MAX, &ok, NULL); - if (fd < 0 || ok == 0) { - log_err(LD_CONFIG, "Invalid --passphrase-fd value %s", escaped(v)); - retval = -1; - goto err; - } - get_options_mutable()->keygen_passphrase_fd = (int)fd; - get_options_mutable()->use_keygen_passphrase_fd = 1; - get_options_mutable()->keygen_force_passphrase = FORCE_PASSPHRASE_ON; - } + const config_line_t *fd_line = config_line_find(cmdline_only_options, + "--passphrase-fd"); + if (fd_line) { + if (handle_cmdline_passphrase_fd(command, fd_line->value) < 0) { + retval = -1; + goto err; } } - { - const config_line_t *key_line = config_line_find(cmdline_only_options, - "--master-key"); - if (key_line) { - if (command != CMD_KEYGEN) { - log_err(LD_CONFIG, "--master-key without --keygen!"); - retval = -1; - goto err; - } else { - get_options_mutable()->master_key_fname = tor_strdup(key_line->value); - } + const config_line_t *key_line = config_line_find(cmdline_only_options, + "--master-key"); + if (key_line) { + if (handle_cmdline_master_key(command, key_line->value) < 0) { + retval = -1; + goto err; } } err: - tor_free(cf); tor_free(cf_defaults); if (errmsg) { @@ -4848,15 +4938,19 @@ options_init_logs(const or_options_t *old_options, const or_options_t *options, goto cleanup; } + /* We added this workaround in 0.4.5.x; we can remove it in 0.4.6 or + * later */ if (!strcasecmp(smartlist_get(elts, 0), "android")) { -#ifdef HAVE_ANDROID_LOG_H +#ifdef HAVE_SYSLOG_H + log_warn(LD_CONFIG, "The android logging API is no longer supported;" + " adding a syslog instead. The 'android' logging " + " type will no longer work in the future."); if (!validate_only) { - add_android_log(severity, options->AndroidIdentityTag); + add_syslog_log(severity, options->SyslogIdentityTag); } #else - log_warn(LD_CONFIG, "Android logging is not supported" - " on this system. Sorry."); -#endif /* defined(HAVE_ANDROID_LOG_H) */ + log_warn(LD_CONFIG, "The android logging API is no longer supported."); +#endif goto cleanup; } } @@ -5778,9 +5872,9 @@ warn_client_dns_cache(const char *option, int disabling) return; warn_deprecated_option(option, - "Client-side DNS cacheing enables a wide variety of route-" + "Client-side DNS caching enables a wide variety of route-" "capture attacks. If a single bad exit node lies to you about " - "an IP address, cacheing that address would make you visit " + "an IP address, caching that address would make you visit " "an address of the attacker's choice every time you connected " "to your destination."); } @@ -5839,6 +5933,14 @@ 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; + tor_addr_t default_addr = TOR_ADDR_NULL; + + /* Parse default address. This can fail for Unix socket so the default_addr + * will simply be made UNSPEC. */ + if (defaultaddr) { + tor_addr_parse(&default_addr, defaultaddr); + } /* If there's no FooPort, then maybe make a default one. */ if (! ports) { @@ -5915,8 +6017,7 @@ 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_addr_copy(&addr, &default_addr); } else if (!strcasecmpend(addrport, ":auto")) { char *addrtmp = tor_strndup(addrport, strlen(addrport)-5); port = CFG_AUTO_PORT; @@ -5932,14 +6033,14 @@ 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_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; } port = ptmp; + addr_is_explicit = true; } else { log_warn(LD_CONFIG, "Couldn't parse address %s for %sPort", escaped(addrport), portname); @@ -5950,6 +6051,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; @@ -5992,15 +6094,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 */ @@ -6213,9 +6325,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); @@ -6343,6 +6456,10 @@ parse_ports(or_options_t *options, int validate_only, *msg = tor_strdup("Invalid HTTPTunnelPort configuration"); goto err; } + if (metrics_parse_ports(options, ports, msg) < 0) { + goto err; + } + { unsigned control_port_flags = CL_PORT_NO_STREAM_OPTIONS | CL_PORT_WARN_NONLOCAL; @@ -6512,48 +6629,57 @@ 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) { + const port_cfg_t *first_port = NULL; + const port_cfg_t *first_port_explicit_addr = NULL; + 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) { - if (cfg->type == listener_type && - !cfg->server_cfg.no_advertise) { + 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->port; + if (cfg->explicit_addr && !first_port_explicit_addr) { + first_port_explicit_addr = cfg; + } else if (!first_port) { + first_port = cfg; + } } } } SMARTLIST_FOREACH_END(cfg); - return 0; + + /* Prefer the port with the explicit address if any. */ + return (first_port_explicit_addr) ? first_port_explicit_addr : first_port; +} + +/** 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 @@ -6883,7 +7009,7 @@ options_get_dir_fname2_suffix,(const or_options_t *options, return fname; } -/** Check wether the data directory has a private subdirectory +/** Check whether the data directory has a private subdirectory * <b>subdir</b>. If not, try to create it. Return 0 on success, * -1 otherwise. */ int @@ -7066,7 +7192,8 @@ parse_outbound_address_lines(const config_line_t *lines, outbound_addr_t type, "configured: %s", family==AF_INET?" IPv4":(family==AF_INET6?" IPv6":""), type==OUTBOUND_ADDR_OR?" OR": - (type==OUTBOUND_ADDR_EXIT?" exit":""), lines->value); + (type==OUTBOUND_ADDR_EXIT?" exit": + (type==OUTBOUND_ADDR_PT?" PT":"")), lines->value); return -1; } lines = lines->next; @@ -7089,7 +7216,7 @@ parse_outbound_addresses(or_options_t *options, int validate_only, char **msg) } if (parse_outbound_address_lines(options->OutboundBindAddress, - OUTBOUND_ADDR_EXIT_AND_OR, options, + OUTBOUND_ADDR_ANY, options, validate_only, msg) < 0) { goto err; } @@ -7106,6 +7233,12 @@ parse_outbound_addresses(or_options_t *options, int validate_only, char **msg) goto err; } + if (parse_outbound_address_lines(options->OutboundBindAddressPT, + OUTBOUND_ADDR_PT, options, validate_only, + msg) < 0) { + goto err; + } + return 0; err: return -1; diff --git a/src/app/config/config.h b/src/app/config/config.h index 1ba10d1d37..e95ef4a728 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/or_options_st.h b/src/app/config/or_options_st.h index bf58205f89..4364f145ed 100644 --- a/src/app/config/or_options_st.h +++ b/src/app/config/or_options_st.h @@ -24,10 +24,29 @@ struct config_suite_t; struct routerset_t; /** Enumeration of outbound address configuration types: - * Exit-only, OR-only, or both */ -typedef enum {OUTBOUND_ADDR_EXIT, OUTBOUND_ADDR_OR, - OUTBOUND_ADDR_EXIT_AND_OR, - OUTBOUND_ADDR_MAX} outbound_addr_t; + * Exit-only, OR-only, PT-only, or any of them */ +typedef enum { + /** Outbound IP address for Exit connections. Controlled by the + * `OutboundBindAddressExit` configuration entry in torrc. */ + OUTBOUND_ADDR_EXIT, + + /** Outbound IP address for OR connections. Controlled by the + * `OutboundBindAddressOR` configuration entry in torrc. */ + OUTBOUND_ADDR_OR, + + /** Outbound IP address for PT connections. Controlled by the + * `OutboundBindAddressPT` configuration entry in torrc. */ + OUTBOUND_ADDR_PT, + + /** Outbound IP address for any outgoing connections. Controlled by the + * OutboundBindAddress configuration entry in torrc. This value is used as + * fallback if the more specific OUTBOUND_ADDR_EXIT, OUTBOUND_ADDR_OR, and + * OUTBOUND_ADDR_PT are unset. */ + OUTBOUND_ADDR_ANY, + + /** Max value for this enum. Must be the last element in this enum. */ + OUTBOUND_ADDR_MAX +} outbound_addr_t; /** Which protocol to use for TCPProxy. */ typedef enum { @@ -35,6 +54,12 @@ typedef enum { TCP_PROXY_PROTOCOL_HAPROXY } tcp_proxy_protocol_t; +/** Enumeration of available time formats for output of --key-expiration */ +typedef enum { + KEY_EXPIRATION_FORMAT_ISO8601 = 0, + KEY_EXPIRATION_FORMAT_TIMESTAMP +} key_expiration_format_t; + /** Configuration options for a Tor process. */ struct or_options_t { uint32_t magic_; @@ -52,7 +77,6 @@ struct or_options_t { int TruncateLogFile; /**< Boolean: Should we truncate the log file before we start writing? */ char *SyslogIdentityTag; /**< Identity tag to add for syslog logging. */ - char *AndroidIdentityTag; /**< Identity tag to add for Android logging. */ char *DebugLogFile; /**< Where to send verbose log messages. */ char *DataDirectory_option; /**< Where to store long-term data, as @@ -71,7 +95,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, @@ -118,6 +149,8 @@ struct or_options_t { struct config_line_t *OutboundBindAddressOR; /** Local address to bind outbound exit sockets */ struct config_line_t *OutboundBindAddressExit; + /** Local address to bind outbound PT sockets */ + struct config_line_t *OutboundBindAddressPT; /** Addresses derived from the various OutboundBindAddress lines. * [][0] is IPv4, [][1] is IPv6 */ @@ -131,6 +164,8 @@ struct or_options_t { struct config_line_t *ORPort_lines; /** Ports to listen on for extended OR connections. */ struct config_line_t *ExtORPort_lines; + /** Ports to listen on for Metrics connections. */ + struct config_line_t *MetricsPort_lines; /** Ports to listen on for SOCKS connections. */ struct config_line_t *SocksPort_lines; /** Ports to listen on for transparent pf/netfilter connections. */ @@ -190,9 +225,17 @@ struct or_options_t { unsigned int DNSPort_set : 1; unsigned int ExtORPort_set : 1; unsigned int HTTPTunnelPort_set : 1; + unsigned int MetricsPort_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? */ @@ -648,7 +691,7 @@ struct or_options_t { int ClientUseIPv4; /** If true, clients may connect over IPv6. If false, they will avoid * connecting over IPv4. We enforce this for OR and Dir connections. - * Use fascist_firewall_use_ipv6() instead of accessing this value + * Use reachable_addr_use_ipv6() instead of accessing this value * directly. */ int ClientUseIPv6; /** If true, prefer an IPv6 OR port over an IPv4 one for entry node @@ -658,7 +701,7 @@ struct or_options_t { int ClientPreferIPv6ORPort; /** If true, prefer an IPv6 directory port over an IPv4 one for direct * directory connections. If auto, bridge clients prefer IPv6, and other - * clients prefer IPv4. Use fascist_firewall_prefer_ipv6_dirport() instead of + * clients prefer IPv4. Use reachable_addr_prefer_ipv6_dirport() instead of * accessing this value directly. */ int ClientPreferIPv6DirPort; @@ -828,10 +871,6 @@ struct or_options_t { * once. */ int MaxClientCircuitsPending; - /** If 1, we always send optimistic data when it's supported. If 0, we - * never use it. If -1, we do what the consensus says. */ - int OptimisticData; - /** If 1, we accept and launch no external network connections, except on * control ports. */ int DisableNetwork; @@ -930,6 +969,8 @@ struct or_options_t { * ed25519 identity key except from tor --keygen */ int OfflineMasterKey; + key_expiration_format_t key_expiration_format; + enum { FORCE_PASSPHRASE_AUTO=0, FORCE_PASSPHRASE_ON, @@ -1000,7 +1041,7 @@ struct or_options_t { /** Maximum allowed burst of circuits. Reaching that value, the address is * detected as malicious and a defense might be used. */ int DoSCircuitCreationBurst; - /** When an address is marked as malicous, what defense should be used + /** When an address is marked as malicious, what defense should be used * against it. See the dos_cc_defense_type_t enum. */ int DoSCircuitCreationDefenseType; /** For how much time (in seconds) the defense is applicable for a malicious @@ -1038,6 +1079,9 @@ struct or_options_t { **/ int DormantCanceledByStartup; + /** List of policy allowed to query the Metrics port. */ + struct config_line_t *MetricsPortPolicy; + /** * Configuration objects for individual modules. * diff --git a/src/app/config/or_state_st.h b/src/app/config/or_state_st.h index 8c4e9d5e61..807f546169 100644 --- a/src/app/config/or_state_st.h +++ b/src/app/config/or_state_st.h @@ -21,7 +21,7 @@ struct config_suite_t; struct or_state_t { uint32_t magic_; /** The time at which we next plan to write the state to the disk. Equal to - * TIME_MAX if there are no savable changes, 0 if there are changes that + * TIME_MAX if there are no saveable changes, 0 if there are changes that * should be saved right away. */ time_t next_write; @@ -38,17 +38,11 @@ struct or_state_t { uint64_t AccountingBytesAtSoftLimit; uint64_t AccountingExpectedUsage; - /** A list of Entry Guard-related configuration lines. (pre-prop271) */ - struct config_line_t *EntryGuards; - - /** A list of guard-related configuration lines. (post-prop271) */ + /** A list of guard-related configuration lines. */ struct config_line_t *Guard; struct config_line_t *TransportProxies; - /** Cached revision counters for active hidden services on this host */ - struct config_line_t *HidServRevCounter; - /** These fields hold information on the history of bandwidth usage for * servers. The "Ends" fields hold the time when we last updated the * bandwidth usage. The "Interval" fields hold the granularity, in seconds, @@ -65,6 +59,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..080cb967bc 100644 --- a/src/app/config/resolve_addr.c +++ b/src/app/config/resolve_addr.c @@ -14,301 +14,830 @@ #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); + +/** True iff the address was found to be configured that is from the + * configuration file either using Address or ORPort. */ +static bool last_addrs_configured[] = { false, false, false }; +CTASSERT(ARRAY_LENGTH(last_addrs_configured) == 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) +/** Return string representation of the given method. */ +const char * +resolved_addr_method_to_str(const resolved_addr_method_t method) { - return last_resolved_addr; + switch (method) { + case RESOLVED_ADDR_NONE: + return "NONE"; + case RESOLVED_ADDR_CONFIGURED: + return "CONFIGURED"; + case RESOLVED_ADDR_CONFIGURED_ORPORT: + return "CONFIGURED_ORPORT"; + case RESOLVED_ADDR_GETHOSTNAME: + return "GETHOSTNAME"; + case RESOLVED_ADDR_INTERFACE: + return "INTERFACE"; + case RESOLVED_ADDR_RESOLVED: + return "RESOLVED"; + default: + tor_assert_nonfatal_unreached(); + return "???"; + } } -/** Reset last_resolved_addr from outside this file. */ +/** Return true if the last address of family was configured or not. An + * address is considered configured if it was found in the Address or ORPort + * statement. + * + * This applies to the address returned by the function + * resolved_addr_get_last() which is the cache of discovered addresses. */ +bool +resolved_addr_is_configured(int family) +{ + return last_addrs_configured[af_to_idx(family)]; +} + +/** 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 -reset_last_resolved_addr(void) +resolved_addr_get_suggested(int family, tor_addr_t *addr_out) { - last_resolved_addr = 0; + tor_addr_copy(addr_out, &last_suggested_addrs[af_to_idx(family)]); } -/** - * 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 +/** Set the last suggested address into our cache. This is called when we get + * a new NETINFO cell from a trusted source. */ +void +resolved_addr_set_suggested(const tor_addr_t *addr) +{ + if (BUG(tor_addr_family(addr) != AF_INET && + tor_addr_family(addr) != AF_INET6)) { + return; + } + + /* In case we don't have a configured address, log that we will be using the + * one discovered from the dirauth. */ + const int idx = af_to_idx(tor_addr_family(addr)); + if (tor_addr_is_null(&last_resolved_addrs[idx]) && + !tor_addr_eq(&last_suggested_addrs[idx], addr)) { + log_notice(LD_CONFIG, "External address seen and suggested by a " + "directory authority: %s", fmt_addr(addr)); + } + tor_addr_copy(&last_suggested_addrs[idx], addr); +} + +/** 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: Method denoting how 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, resolved_addr_method_t *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 = RESOLVED_ADDR_NONE; - 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 = RESOLVED_ADDR_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_ADDR_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: Method denoting how 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, resolved_addr_method_t *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 = RESOLVED_ADDR_NONE; + + 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 = RESOLVED_ADDR_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 RESOLVED_ADDR_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, resolved_addr_method_t *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 = RESOLVED_ADDR_NONE; + *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 = RESOLVED_ADDR_INTERFACE; + + /* Found it! */ + log_info(LD_CONFIG, "Address found from interface: %s", fmt_addr(addr_out)); + return FN_RET_OK; +} + +/** @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 RESOLVED_ADDR_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, resolved_addr_method_t *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); + + /* Set them to NULL for safety reasons. */ + *method_out = RESOLVED_ADDR_NONE; + *hostname_out = NULL; + + log_debug(LD_CONFIG, "Attempting to get address from ORPort"); - if (last_resolved_addr && last_resolved_addr != *addr_out) { + 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 = RESOLVED_ADDR_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 resolved_addr_method_t 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); + + /* 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), + resolved_addr_method_to_str(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), + resolved_addr_method_to_str(method_used), + have_hostname ? " HOSTNAME=" : "", + have_hostname ? hostname_used : ""); + /* Copy address to cache. */ + tor_addr_copy(last_resolved, addr); + *done_one_resolve = true; + + /* Flag true if the address was configured. Else, indicate it was not. */ + last_addrs_configured[idx] = false; + if (method_used == RESOLVED_ADDR_CONFIGURED || + method_used == RESOLVED_ADDR_CONFIGURED_ORPORT) { + last_addrs_configured[idx] = 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, + resolved_addr_method_t *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 RESOLVED_ADDR_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_ADDR_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 network interface (see section 2). + * + * 2. Look at the network interface. + * + * Attempt to find the first public usable address from the list of + * network interfaces 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 + * RESOLVED_ADDR_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 RESOLVED_ADDR_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) Method denoting how the address wa + * 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, resolved_addr_method_t *method_out, + char **hostname_out) +{ + resolved_addr_method_t method_used = RESOLVED_ADDR_NONE; + 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 = RESOLVED_ADDR_NONE; + 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[af_to_idx(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..919d5d42cc 100644 --- a/src/app/config/resolve_addr.h +++ b/src/app/config/resolve_addr.h @@ -9,19 +9,58 @@ #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); +/** Method used to resolved an address. In other words, how was the address + * discovered by tor. */ +typedef enum { + /* Default value. Indicate that no method found the address. */ + RESOLVED_ADDR_NONE = 0, + /* Found from the "Address" configuration option. */ + RESOLVED_ADDR_CONFIGURED = 1, + /* Found from the "ORPort" configuration option. */ + RESOLVED_ADDR_CONFIGURED_ORPORT = 2, + /* Found by resolving the local hostname. */ + RESOLVED_ADDR_GETHOSTNAME = 3, + /* Found by querying the local interface(s). */ + RESOLVED_ADDR_INTERFACE = 4, + /* Found by resolving the hostname from the Address configuration option. */ + RESOLVED_ADDR_RESOLVED = 5, +} resolved_addr_method_t; + +const char *resolved_addr_method_to_str(const resolved_addr_method_t method); + +#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, + resolved_addr_method_t *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 resolved_addr_method_t 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)); +bool resolved_addr_is_configured(int family); + +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..22b15fcf24 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" @@ -58,16 +58,38 @@ /** A list of state-file "abbreviations," for compatibility. */ static config_abbrev_t state_abbrevs_[] = { - { "AccountingBytesReadInterval", "AccountingBytesReadInInterval", 0, 0 }, - { "HelperNode", "EntryGuard", 0, 0 }, - { "HelperNodeDownSince", "EntryGuardDownSince", 0, 0 }, - { "HelperNodeUnlistedSince", "EntryGuardUnlistedSince", 0, 0 }, - { "EntryNode", "EntryGuard", 0, 0 }, - { "EntryNodeDownSince", "EntryGuardDownSince", 0, 0 }, - { "EntryNodeUnlistedSince", "EntryGuardUnlistedSince", 0, 0 }, { NULL, NULL, 0, 0}, }; +/** A list of obsolete keys that we do not and should not preserve. + * + * We could just let these live in ExtraLines indefinitely, but they're + * never going to be used again, and every version that used them + * has been obsolete for a long time. + * */ +static const char *obsolete_state_keys[] = { + /* These were renamed in 0.1.1.11-alpha */ + "AccountingBytesReadInterval", + "HelperNode", + "HelperNodeDownSince", + "HelperNodeUnlistedSince", + "EntryNode", + "HelperNodeDownSince", + "EntryNodeUnlistedSince", + /* These were replaced by "Guard" in 0.3.0.1-alpha. */ + "EntryGuard", + "EntryGuardDownSince", + "EntryGuardUnlistedSince", + "EntryGuardAddedBy", + "EntryGuardPathBias", + "EntryGuardPathUseBias", + /* This was replaced by OPE-based revision numbers in 0.3.5.1-alpha, + * and was never actually used in a released version. */ + "HidServRevCounter", + + NULL, +}; + /** dummy instance of or_state_t, used for type-checking its * members with CONF_CHECK_VAR_TYPE. */ DUMMY_TYPECHECK_INSTANCE(or_state_t); @@ -91,19 +113,9 @@ static const config_var_t state_vars_[] = { V(AccountingSoftLimitHitAt, ISOTIME, NULL), V(AccountingBytesAtSoftLimit, MEMUNIT, NULL), - VAR("EntryGuard", LINELIST_S, EntryGuards, NULL), - VAR("EntryGuardDownSince", LINELIST_S, EntryGuards, NULL), - VAR("EntryGuardUnlistedSince", LINELIST_S, EntryGuards, NULL), - VAR("EntryGuardAddedBy", LINELIST_S, EntryGuards, NULL), - VAR("EntryGuardPathBias", LINELIST_S, EntryGuards, NULL), - VAR("EntryGuardPathUseBias", LINELIST_S, EntryGuards, NULL), - V(EntryGuards, LINELIST_V, NULL), - VAR("TransportProxy", LINELIST_S, TransportProxies, NULL), V(TransportProxies, LINELIST_V, NULL), - V(HidServRevCounter, LINELIST, NULL), - V(BWHistoryReadEnds, ISOTIME, NULL), V(BWHistoryReadInterval, POSINT, "900"), V(BWHistoryReadValues, CSV, ""), @@ -112,6 +124,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 +344,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; @@ -467,6 +487,7 @@ or_state_load(void) } else { log_info(LD_GENERAL, "Initialized state"); } + or_state_remove_obsolete_lines(&new_state->ExtraLines); if (or_state_set(new_state) == -1) { or_state_save_broken(fname); } @@ -486,6 +507,36 @@ or_state_load(void) return r; } +/** Remove from `extra_lines` every element whose key appears in + * `obsolete_state_keys`. */ +STATIC void +or_state_remove_obsolete_lines(config_line_t **extra_lines) +{ + /* make a strmap for the obsolete state names, so we can have O(1) + lookup. */ + strmap_t *bad_keys = strmap_new(); + for (unsigned i = 0; obsolete_state_keys[i] != NULL; ++i) { + strmap_set_lc(bad_keys, obsolete_state_keys[i], (void*)"rmv"); + } + + config_line_t **line = extra_lines; + while (*line) { + if (strmap_get_lc(bad_keys, (*line)->key) != NULL) { + /* This key is obsolete; remove it. */ + config_line_t *victim = *line; + *line = (*line)->next; + + victim->next = NULL; // prevent double-free. + config_free_lines(victim); + } else { + /* This is just an unrecognized key; keep it. */ + line = &(*line)->next; + } + } + + strmap_free(bad_keys, NULL); +} + /** Did the last time we tried to write the state file fail? If so, we * should consider disabling such features as preemptive circuit generation * to compute circuit-build-time. */ @@ -523,7 +574,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/statefile.h b/src/app/config/statefile.h index 98d9d2dda1..89b10560f3 100644 --- a/src/app/config/statefile.h +++ b/src/app/config/statefile.h @@ -33,6 +33,7 @@ STATIC void or_state_free_(or_state_t *state); STATIC or_state_t *or_state_new(void); struct config_mgr_t; STATIC const struct config_mgr_t *get_state_mgr(void); +STATIC void or_state_remove_obsolete_lines(struct config_line_t **extra_lines); #endif /* defined(STATEFILE_PRIVATE) */ #endif /* !defined(TOR_STATEFILE_H) */ 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" }, |