diff options
Diffstat (limited to 'src/app')
-rw-r--r-- | src/app/config/config.c | 4 | ||||
-rw-r--r-- | src/app/config/or_options_st.h | 5 | ||||
-rw-r--r-- | src/app/config/resolve_addr.c | 778 | ||||
-rw-r--r-- | src/app/config/resolve_addr.h | 12 | ||||
-rw-r--r-- | src/app/config/testnet.inc | 1 | ||||
-rw-r--r-- | src/app/main/main.c | 3 |
6 files changed, 555 insertions, 248 deletions
diff --git a/src/app/config/config.c b/src/app/config/config.c index 71f8c18ca2..286cd9304a 100644 --- a/src/app/config/config.c +++ b/src/app/config/config.c @@ -313,7 +313,7 @@ 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), OBSOLETE("AllowDotExit"), OBSOLETE("AllowInvalidNodes"), V(AllowNonRFC953Hostnames, BOOL, "0"), @@ -4028,7 +4028,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); diff --git a/src/app/config/or_options_st.h b/src/app/config/or_options_st.h index bf58205f89..2f375f5d9b 100644 --- a/src/app/config/or_options_st.h +++ b/src/app/config/or_options_st.h @@ -71,7 +71,10 @@ 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; + char *PidFile; /**< Where to store PID of Tor process. */ struct routerset_t *ExitNodes; /**< Structure containing nicknames, digests, diff --git a/src/app/config/resolve_addr.c b/src/app/config/resolve_addr.c index b551615c02..15a94cb82e 100644 --- a/src/app/config/resolve_addr.c +++ b/src/app/config/resolve_addr.c @@ -15,294 +15,600 @@ #include "feature/control/control_events.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[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 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) { - return last_resolved_addr; + tor_addr_copy(addr_out, &last_resolved_addrs[af_to_idx(family)]); } -/** Reset last_resolved_addr from outside this file. */ +/** Reset the last resolved address of family. + * + * This makes it null address. */ void -reset_last_resolved_addr(void) +resolved_addr_reset_last(int family) { - last_resolved_addr = 0; + tor_addr_make_null(&last_resolved_addrs[af_to_idx(family)], 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 +/** 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; + 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)); - } else { /* then we need to guess our address */ - explicit_ip = 0; /* it's implicit */ - explicit_hostname = 0; /* it's implicit */ + log_debug(LD_CONFIG, "Attempting to get address from configuration"); - 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); + 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; + } - if (tor_inet_aton(hostname, &in) == 0) { - /* then we have to resolve it */ - 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"; + *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. */ + log_fn(warn_severity, LD_CONFIG, + "Could not resolve local Address '%s'. Failing.", cfg->value); + return FN_RET_BAIL; } - } else { - 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) { + log_fn(warn_severity, LD_CONFIG, + "No Address option found for family %s in configuration.", + fmt_af_family(family)); + /* No Address statement for family, inform caller 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)); + return FN_RET_BAIL; + } - addr_string = tor_dup_ip(addr); - if (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. */ + 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_fn(warn_severity, 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_fn(warn_severity, 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_fn(warn_severity, LD_CONFIG, "Address found from interface: %s", + fmt_addr(addr_out)); + return FN_RET_OK; +} + +/** @brief Update 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). + */ +static void +update_resolved_cache(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[IDX_SIZE] = { false, false, false }; + 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; + } - if (last_resolved_addr && last_resolved_addr != *addr_out) { + /* 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; +} + +/** Address discovery function table. The order matters as in the first one is + * executed first and so on. */ +static fn_address_ret_t + (*fn_address_table[])( + const or_options_t *options, int warn_severity, int family, + const char **method_out, char **hostname_out, tor_addr_t *addr_out) = +{ + /* These functions are in order for our find address algorithm. */ + get_address_from_config, + get_address_from_hostname, + get_address_from_interface, +}; +/** Length of address table as in how many functions. */ +static const size_t fn_address_table_len = ARRAY_LENGTH(fn_address_table); + +/** @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 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, fallback to the network + * interface (see section 3). + * + * 3. 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, an error is returned. This error indicates that all + * attempts have failed and thus the address for the given family can not + * be found. + * + * On success, addr_out is set with it, method_out is set to "INTERFACE" + * and hostname_out is set to NULL. + * + * @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; + + tor_assert(options); + tor_assert(addr_out); + + /* Set them to NULL for safety reasons. */ + if (method_out) *method_out = NULL; + if (hostname_out) *hostname_out = NULL; + + /* + * Step 1: Discover address by attempting 3 different methods consecutively. + */ + + /* Go over the function table. They are in order. */ + for (size_t idx = 0; idx < fn_address_table_len; idx++) { + fn_address_ret_t ret = fn_address_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); } - last_resolved_addr = *addr_out; + /* We've exhausted our attempts. Failure. */ + log_fn(warn_severity, LD_CONFIG, "Unable to find our IP address."); + return false; + + found: /* - * And finally, clean up and return success. + * Step 2: Update last resolved address cache and inform the control port. */ + update_resolved_cache(&my_addr, method_used, hostname_used); - tor_free(addr_string); - return 0; + 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. */ -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: + /* XXX: Why is this /24 and not /16 which the rest of tor does? Unknown + * reasons at the moment highlighted in ticket #40009. Because of that, we + * can't use addrs_in_same_network_family(). */ /* 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 the /32 like addrs_in_same_network_family() does. */ + return tor_addr_compare_masked(addr, last_resolved_addr, 32, + CMP_SEMANTIC) == 0; + break; + default: + /* Unknown address type so not local. */ + return false; } - return 0; } diff --git a/src/app/config/resolve_addr.h b/src/app/config/resolve_addr.h index 3747546402..54f55ba36c 100644 --- a/src/app/config/resolve_addr.h +++ b/src/app/config/resolve_addr.h @@ -11,14 +11,14 @@ #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); +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); -uint32_t get_last_resolved_addr(void); -void reset_last_resolved_addr(void); +void resolved_addr_get_last(int family, tor_addr_t *addr_out); +void resolved_addr_reset_last(int family); -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 diff --git a/src/app/config/testnet.inc b/src/app/config/testnet.inc index 907c35f97c..605943d80b 100644 --- a/src/app/config/testnet.inc +++ b/src/app/config/testnet.inc @@ -2,7 +2,6 @@ // for 'TestingTorNetwork' in 'doc/tor.1.txt' { "DirAllowPrivateAddresses", "1" }, { "EnforceDistinctSubnets", "0" }, -{ "AssumeReachable", "1" }, { "AuthDirMaxServersPerAddr", "0" }, { "ClientBootstrapConsensusAuthorityDownloadInitialDelay", "0" }, { "ClientBootstrapConsensusFallbackDownloadInitialDelay", "0" }, diff --git a/src/app/main/main.c b/src/app/main/main.c index 93291674cc..e6fa789a7d 100644 --- a/src/app/main/main.c +++ b/src/app/main/main.c @@ -795,8 +795,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. */ |