diff options
author | David Goulet <dgoulet@torproject.org> | 2020-06-24 13:51:51 -0400 |
---|---|---|
committer | David Goulet <dgoulet@torproject.org> | 2020-06-24 13:51:51 -0400 |
commit | 86da0cfd36f82d1d8a0948a3587aef0f518c7a66 (patch) | |
tree | 672cc3cc473d55332ef7eaa4b977a489e3874494 | |
parent | 4af6e3b4dccf22d6d34b744f5f90c5218a9de6c4 (diff) | |
parent | 29a35d262c4b60535050748df78325c78060c535 (diff) | |
download | tor-86da0cfd36f82d1d8a0948a3587aef0f518c7a66.tar.gz tor-86da0cfd36f82d1d8a0948a3587aef0f518c7a66.zip |
Merge branch 'tor-github/pr/1945'
-rw-r--r-- | changes/ticket33233 | 4 | ||||
-rw-r--r-- | scripts/maint/practracker/exceptions.txt | 2 | ||||
-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 | 786 | ||||
-rw-r--r-- | src/app/config/resolve_addr.h | 12 | ||||
-rw-r--r-- | src/app/main/main.c | 3 | ||||
-rw-r--r-- | src/core/mainloop/connection.c | 2 | ||||
-rw-r--r-- | src/core/or/channeltls.c | 6 | ||||
-rw-r--r-- | src/feature/dirauth/dirauth_config.c | 4 | ||||
-rw-r--r-- | src/feature/dirauth/dirvote.c | 8 | ||||
-rw-r--r-- | src/feature/dircache/dircache.c | 2 | ||||
-rw-r--r-- | src/feature/nodelist/dirlist.c | 10 | ||||
-rw-r--r-- | src/feature/relay/relay_config.c | 2 | ||||
-rw-r--r-- | src/feature/relay/relay_find_addr.c | 37 | ||||
-rw-r--r-- | src/feature/relay/router.c | 5 | ||||
-rw-r--r-- | src/test/test_channeltls.c | 32 | ||||
-rw-r--r-- | src/test/test_config.c | 285 |
18 files changed, 760 insertions, 449 deletions
diff --git a/changes/ticket33233 b/changes/ticket33233 new file mode 100644 index 0000000000..977286c323 --- /dev/null +++ b/changes/ticket33233 @@ -0,0 +1,4 @@ + o Major feature (IPv6, relay): + - The torrc option Address now supports IPv6. By doing so, we've also + unified the interface to find our address to support IPv4, IPv6 and + hostname. Closes ticket 33233; diff --git a/scripts/maint/practracker/exceptions.txt b/scripts/maint/practracker/exceptions.txt index 154f43ee1f..a5b3ca7242 100644 --- a/scripts/maint/practracker/exceptions.txt +++ b/scripts/maint/practracker/exceptions.txt @@ -46,7 +46,7 @@ problem function-size /src/app/config/config.c:parse_dir_authority_line() 150 problem function-size /src/app/config/config.c:parse_dir_fallback_line() 101 problem function-size /src/app/config/config.c:port_parse_config() 435 problem function-size /src/app/config/config.c:parse_ports() 132 -problem function-size /src/app/config/resolve_addr.c:resolve_my_address() 191 +problem function-size /src/app/config/resolve_addr.c:resolve_my_address_v4() 197 problem file-size /src/app/config/or_options_st.h 1050 problem include-count /src/app/main/main.c 68 problem function-size /src/app/main/main.c:dumpstats() 102 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 9d1a8e0260..15a94cb82e 100644 --- a/src/app/config/resolve_addr.c +++ b/src/app/config/resolve_addr.c @@ -15,300 +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)); - log_debug(LD_CONFIG, "Trying configured Address '%s' as local hostname", - hostname); - } else { /* then we need to guess our address */ - explicit_ip = 0; /* it's implicit */ - explicit_hostname = 0; /* it's implicit */ - - if (tor_gethostname(hostname, sizeof(hostname)) < 0) { - log_fn(warn_severity, LD_NET,"Error obtaining local hostname"); - return -1; - } - log_debug(LD_CONFIG, "Guessed local host name as '%s'", hostname); + log_debug(LD_CONFIG, "Attempting to get address from configuration"); + + if (!options->Address) { + log_info(LD_CONFIG, "No Address option found in configuration."); + /* No Address statement, inform caller to try next method. */ + return FN_RET_NEXT; } - /* - * Step two: Now that we know 'hostname', parse it or resolve it. If - * it doesn't parse or resolve, look at the interface address. Set 'addr' - * to be our (host-order) 32-bit answer. - */ + for (const config_line_t *cfg = options->Address; cfg != NULL; + cfg = cfg->next) { + int af; + tor_addr_t addr; + + af = tor_addr_parse(&addr, cfg->value); + if (af == family) { + tor_addr_copy(addr_out, &addr); + *method_out = "CONFIGURED"; + explicit_ip = true; + num_valid_addr++; + continue; + } - 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"; + *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 { - 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) { + 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 (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. */ + 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; +} - if (last_resolved_addr && last_resolved_addr != *addr_out) { +/** @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; + } + + /* 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/main/main.c b/src/app/main/main.c index dc39611f98..32c34ea821 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. */ diff --git a/src/core/mainloop/connection.c b/src/core/mainloop/connection.c index a8417e46d9..3ebe18cd33 100644 --- a/src/core/mainloop/connection.c +++ b/src/core/mainloop/connection.c @@ -4870,7 +4870,7 @@ client_check_address_changed(tor_socket_t sock) smartlist_clear(outgoing_addrs); smartlist_add(outgoing_addrs, tor_memdup(&out_addr, sizeof(tor_addr_t))); /* We'll need to resolve ourselves again. */ - reset_last_resolved_addr(); + resolved_addr_reset_last(AF_INET); /* Okay, now change our keys. */ ip_address_changed(1); } diff --git a/src/core/or/channeltls.c b/src/core/or/channeltls.c index 395fbf3455..f9937ce880 100644 --- a/src/core/or/channeltls.c +++ b/src/core/or/channeltls.c @@ -203,7 +203,7 @@ channel_tls_connect(const tor_addr_t *addr, uint16_t port, tlschan, (chan->global_identifier)); - if (is_local_addr(addr)) { + if (is_local_to_resolve_addr(addr)) { log_debug(LD_CHANNEL, "Marking new outgoing channel %"PRIu64 " at %p as local", (chan->global_identifier), chan); @@ -340,7 +340,7 @@ channel_tls_handle_incoming(or_connection_t *orconn) tlschan->conn = orconn; orconn->chan = tlschan; - if (is_local_addr(&(TO_CONN(orconn)->addr))) { + if (is_local_to_resolve_addr(&(TO_CONN(orconn)->addr))) { log_debug(LD_CHANNEL, "Marking new incoming channel %"PRIu64 " at %p as local", (chan->global_identifier), chan); @@ -1353,7 +1353,7 @@ channel_tls_update_marks(or_connection_t *conn) chan = TLS_CHAN_TO_BASE(conn->chan); - if (is_local_addr(&(TO_CONN(conn)->addr))) { + if (is_local_to_resolve_addr(&(TO_CONN(conn)->addr))) { if (!channel_is_local(chan)) { log_debug(LD_CHANNEL, "Marking channel %"PRIu64 " at %p as local", diff --git a/src/feature/dirauth/dirauth_config.c b/src/feature/dirauth/dirauth_config.c index a0b6de7eca..1ffd33e5f1 100644 --- a/src/feature/dirauth/dirauth_config.c +++ b/src/feature/dirauth/dirauth_config.c @@ -77,8 +77,8 @@ options_validate_dirauth_mode(const or_options_t *old_options, return 0; /* confirm that our address isn't broken, so we can complain now */ - uint32_t tmp; - if (resolve_my_address(LOG_WARN, options, &tmp, NULL, NULL) < 0) + tor_addr_t tmp; + if (!find_my_address(options, AF_INET, LOG_WARN, &tmp, NULL, NULL)) REJECT("Failed to resolve/guess local address. See logs for details."); if (!options->ContactInfo && !options->TestingTorNetwork) diff --git a/src/feature/dirauth/dirvote.c b/src/feature/dirauth/dirvote.c index 79651563b4..5fda842246 100644 --- a/src/feature/dirauth/dirvote.c +++ b/src/feature/dirauth/dirvote.c @@ -4463,7 +4463,7 @@ dirserv_generate_networkstatus_vote_obj(crypto_pk_t *private_key, const or_options_t *options = get_options(); const dirauth_options_t *d_options = dirauth_get_options(); networkstatus_t *v3_out = NULL; - uint32_t addr; + tor_addr_t addr; char *hostname = NULL, *client_versions = NULL, *server_versions = NULL; const char *contact; smartlist_t *routers, *routerstatuses; @@ -4492,13 +4492,13 @@ dirserv_generate_networkstatus_vote_obj(crypto_pk_t *private_key, log_err(LD_BUG, "Error computing identity key digest"); return NULL; } - if (resolve_my_address(LOG_WARN, options, &addr, NULL, &hostname)<0) { + if (!find_my_address(options, AF_INET, LOG_WARN, &addr, NULL, &hostname)) { log_warn(LD_NET, "Couldn't resolve my hostname"); return NULL; } if (!hostname || !strchr(hostname, '.')) { tor_free(hostname); - hostname = tor_dup_ip(addr); + hostname = tor_addr_to_str_dup(&addr); } if (!hostname) { @@ -4722,7 +4722,7 @@ dirserv_generate_networkstatus_vote_obj(crypto_pk_t *private_key, memcpy(voter->identity_digest, identity_digest, DIGEST_LEN); voter->sigs = smartlist_new(); voter->address = hostname; - voter->addr = addr; + voter->addr = tor_addr_to_ipv4h(&addr); voter->dir_port = router_get_advertised_dir_port(options, 0); voter->or_port = router_get_advertised_or_port(options); voter->contact = tor_strdup(contact); diff --git a/src/feature/dircache/dircache.c b/src/feature/dircache/dircache.c index ca127720f2..691c100974 100644 --- a/src/feature/dircache/dircache.c +++ b/src/feature/dircache/dircache.c @@ -142,7 +142,7 @@ write_http_response_header_impl(dir_connection_t *conn, ssize_t length, if (type) { buf_add_printf(buf, "Content-Type: %s\r\n", type); } - if (!is_local_addr(&conn->base_.addr)) { + if (!is_local_to_resolve_addr(&conn->base_.addr)) { /* Don't report the source address for a nearby/private connection. * Otherwise we tend to mis-report in cases where incoming ports are * being forwarded to a Tor server running behind the firewall. */ diff --git a/src/feature/nodelist/dirlist.c b/src/feature/nodelist/dirlist.c index 33d1bfc4d0..f49d991f9b 100644 --- a/src/feature/nodelist/dirlist.c +++ b/src/feature/nodelist/dirlist.c @@ -343,25 +343,25 @@ trusted_dir_server_new(const char *nickname, const char *address, const char *digest, const char *v3_auth_digest, dirinfo_type_t type, double weight) { - uint32_t a; tor_addr_t addr; char *hostname=NULL; dir_server_t *result; if (!address) { /* The address is us; we should guess. */ - if (resolve_my_address(LOG_WARN, get_options(), - &a, NULL, &hostname) < 0) { + if (!find_my_address(get_options(), AF_INET, LOG_WARN, &addr, + NULL, &hostname)) { log_warn(LD_CONFIG, "Couldn't find a suitable address when adding ourself as a " "trusted directory server."); return NULL; } if (!hostname) - hostname = tor_dup_ip(a); + hostname = tor_addr_to_str_dup(&addr); if (!hostname) return NULL; } else { + uint32_t a; if (tor_lookup_hostname(address, &a)) { log_warn(LD_CONFIG, "Unable to lookup address for directory server at '%s'", @@ -369,8 +369,8 @@ trusted_dir_server_new(const char *nickname, const char *address, return NULL; } hostname = tor_strdup(address); + tor_addr_from_ipv4h(&addr, a); } - tor_addr_from_ipv4h(&addr, a); result = dir_server_new(1, nickname, &addr, hostname, dir_port, or_port, diff --git a/src/feature/relay/relay_config.c b/src/feature/relay/relay_config.c index fac6a2f577..7b43b57423 100644 --- a/src/feature/relay/relay_config.c +++ b/src/feature/relay/relay_config.c @@ -1029,7 +1029,7 @@ options_transition_affects_descriptor(const or_options_t *old_options, YES_IF_CHANGED_STRING(DataDirectory); YES_IF_CHANGED_STRING(Nickname); - YES_IF_CHANGED_STRING(Address); + YES_IF_CHANGED_LINELIST(Address); YES_IF_CHANGED_LINELIST(ExitPolicy); YES_IF_CHANGED_BOOL(ExitRelay); YES_IF_CHANGED_BOOL(ExitPolicyRejectPrivate); diff --git a/src/feature/relay/relay_find_addr.c b/src/feature/relay/relay_find_addr.c index 86cd799d42..a51457ddbb 100644 --- a/src/feature/relay/relay_find_addr.c +++ b/src/feature/relay/relay_find_addr.c @@ -45,8 +45,7 @@ void router_new_address_suggestion(const char *suggestion, const dir_connection_t *d_conn) { - tor_addr_t addr; - uint32_t cur = 0; /* Current IPv4 address. */ + tor_addr_t addr, my_addr, last_resolved_addr; const or_options_t *options = get_options(); /* first, learn what the IP address actually is */ @@ -64,14 +63,22 @@ router_new_address_suggestion(const char *suggestion, } /* XXXX ipv6 */ - cur = get_last_resolved_addr(); - if (cur || - resolve_my_address(LOG_INFO, options, &cur, NULL, NULL) >= 0) { + resolved_addr_get_last(AF_INET, &last_resolved_addr); + if (!tor_addr_is_null(&last_resolved_addr)) { + /* Lets use this one. */ + tor_addr_copy(&last_guessed_ip, &last_resolved_addr); + return; + } + + /* Attempt to find our address. */ + if (find_my_address(options, AF_INET, LOG_INFO, &my_addr, NULL, NULL)) { /* We're all set -- we already know our address. Great. */ - tor_addr_from_ipv4h(&last_guessed_ip, cur); /* store it in case we - need it later */ + tor_addr_copy(&last_guessed_ip, &my_addr); /* store it in case we + need it later */ return; } + + /* Consider the suggestion from the directory. */ if (tor_addr_is_internal(&addr, 0)) { /* Don't believe anybody who says our IP is, say, 127.0.0.1. */ return; @@ -111,15 +118,21 @@ MOCK_IMPL(int, router_pick_published_address, (const or_options_t *options, uint32_t *addr, int cache_only)) { - /* First, check the cached output from resolve_my_address(). */ - *addr = get_last_resolved_addr(); - if (*addr) + tor_addr_t last_resolved_addr; + + /* First, check the cached output from find_my_address(). */ + resolved_addr_get_last(AF_INET, &last_resolved_addr); + if (!tor_addr_is_null(&last_resolved_addr)) { + *addr = tor_addr_to_ipv4h(&last_resolved_addr); return 0; + } /* Second, consider doing a resolve attempt right here. */ if (!cache_only) { - if (resolve_my_address(LOG_INFO, options, addr, NULL, NULL) >= 0) { - log_info(LD_CONFIG,"Success: chose address '%s'.", fmt_addr32(*addr)); + tor_addr_t my_addr; + if (find_my_address(options, AF_INET, LOG_INFO, &my_addr, NULL, NULL)) { + log_info(LD_CONFIG,"Success: chose address '%s'.", fmt_addr(&my_addr)); + *addr = tor_addr_to_ipv4h(&my_addr); return 0; } } diff --git a/src/feature/relay/router.c b/src/feature/relay/router.c index 0fa46103cf..1691ac5f48 100644 --- a/src/feature/relay/router.c +++ b/src/feature/relay/router.c @@ -2540,6 +2540,7 @@ void check_descriptor_ipaddress_changed(time_t now) { uint32_t prev, cur; + tor_addr_t addr; const or_options_t *options = get_options(); const char *method = NULL; char *hostname = NULL; @@ -2552,10 +2553,12 @@ check_descriptor_ipaddress_changed(time_t now) /* XXXX ipv6 */ prev = my_ri->addr; - if (resolve_my_address(LOG_INFO, options, &cur, &method, &hostname) < 0) { + if (!find_my_address(options, AF_INET, LOG_INFO, &addr, &method, + &hostname)) { log_info(LD_CONFIG,"options->Address didn't resolve into an IP."); return; } + cur = tor_addr_to_ipv4h(&addr); if (prev != cur) { char *source; diff --git a/src/test/test_channeltls.c b/src/test/test_channeltls.c index f4f5cb447e..f682c57acf 100644 --- a/src/test/test_channeltls.c +++ b/src/test/test_channeltls.c @@ -38,13 +38,13 @@ static or_connection_t * tlschan_connection_or_connect_mock( const char *digest, const ed25519_public_key_t *ed_id, channel_tls_t *tlschan); -static int tlschan_is_local_addr_mock(const tor_addr_t *addr); +static bool tlschan_resolved_addr_is_local_mock(const tor_addr_t *addr); /* Fake close method */ static void tlschan_fake_close_method(channel_t *chan); /* Flags controlling behavior of channeltls unit test mocks */ -static int tlschan_local = 0; +static bool tlschan_local = false; static const buf_t * tlschan_buf_datalen_mock_target = NULL; static size_t tlschan_buf_datalen_mock_size = 0; @@ -67,9 +67,9 @@ test_channeltls_create(void *arg) test_addr.addr.in_addr.s_addr = htonl(0x01020304); /* For this test we always want the address to be treated as non-local */ - tlschan_local = 0; - /* Install is_local_addr() mock */ - MOCK(is_local_addr, tlschan_is_local_addr_mock); + tlschan_local = false; + /* Install is_local_to_resolve_addr() mock */ + MOCK(is_local_to_resolve_addr, tlschan_resolved_addr_is_local_mock); /* Install mock for connection_or_connect() */ MOCK(connection_or_connect, tlschan_connection_or_connect_mock); @@ -92,7 +92,7 @@ test_channeltls_create(void *arg) } UNMOCK(connection_or_connect); - UNMOCK(is_local_addr); + UNMOCK(is_local_to_resolve_addr); return; } @@ -116,9 +116,9 @@ test_channeltls_num_bytes_queued(void *arg) test_addr.addr.in_addr.s_addr = htonl(0x01020304); /* For this test we always want the address to be treated as non-local */ - tlschan_local = 0; - /* Install is_local_addr() mock */ - MOCK(is_local_addr, tlschan_is_local_addr_mock); + tlschan_local = false; + /* Install is_local_to_resolve_addr() mock */ + MOCK(is_local_to_resolve_addr, tlschan_resolved_addr_is_local_mock); /* Install mock for connection_or_connect() */ MOCK(connection_or_connect, tlschan_connection_or_connect_mock); @@ -178,7 +178,7 @@ test_channeltls_num_bytes_queued(void *arg) } UNMOCK(connection_or_connect); - UNMOCK(is_local_addr); + UNMOCK(is_local_to_resolve_addr); return; } @@ -201,9 +201,9 @@ test_channeltls_overhead_estimate(void *arg) test_addr.addr.in_addr.s_addr = htonl(0x01020304); /* For this test we always want the address to be treated as non-local */ - tlschan_local = 0; - /* Install is_local_addr() mock */ - MOCK(is_local_addr, tlschan_is_local_addr_mock); + tlschan_local = false; + /* Install is_local_to_resolve_addr() mock */ + MOCK(is_local_to_resolve_addr, tlschan_resolved_addr_is_local_mock); /* Install mock for connection_or_connect() */ MOCK(connection_or_connect, tlschan_connection_or_connect_mock); @@ -252,7 +252,7 @@ test_channeltls_overhead_estimate(void *arg) } UNMOCK(connection_or_connect); - UNMOCK(is_local_addr); + UNMOCK(is_local_to_resolve_addr); return; } @@ -321,8 +321,8 @@ tlschan_fake_close_method(channel_t *chan) return; } -static int -tlschan_is_local_addr_mock(const tor_addr_t *addr) +static bool +tlschan_resolved_addr_is_local_mock(const tor_addr_t *addr) { tt_ptr_op(addr, OP_NE, NULL); diff --git a/src/test/test_config.c b/src/test/test_config.c index 095eb24c49..7b7c9c6216 100644 --- a/src/test/test_config.c +++ b/src/test/test_config.c @@ -990,52 +990,55 @@ test_config_fix_my_family(void *arg) static int n_hostname_01010101 = 0; -/** This mock function is meant to replace tor_lookup_hostname(). +/** This mock function is meant to replace tor_addr_lookup(). * It answers with 1.1.1.1 as IP adddress that resulted from lookup. * This function increments <b>n_hostname_01010101</b> counter by one * every time it is called. */ static int -tor_lookup_hostname_01010101(const char *name, uint32_t *addr) +tor_addr_lookup_01010101(const char *name, uint16_t family, tor_addr_t *addr) { n_hostname_01010101++; - if (name && addr) { - *addr = ntohl(0x01010101); + if (family == AF_INET) { + if (name && addr) { + tor_addr_from_ipv4h(addr, 0x01010101); + } } - return 0; } static int n_hostname_localhost = 0; -/** This mock function is meant to replace tor_lookup_hostname(). +/** This mock function is meant to replace tor_addr_lookup(). * It answers with 127.0.0.1 as IP adddress that resulted from lookup. * This function increments <b>n_hostname_localhost</b> counter by one * every time it is called. */ static int -tor_lookup_hostname_localhost(const char *name, uint32_t *addr) +tor_addr_lookup_localhost(const char *name, uint16_t family, tor_addr_t *addr) { n_hostname_localhost++; - if (name && addr) { - *addr = 0x7f000001; + if (family == AF_INET) { + if (name && addr) { + tor_addr_from_ipv4h(addr, 0x7f000001); + } } - return 0; } static int n_hostname_failure = 0; -/** This mock function is meant to replace tor_lookup_hostname(). +/** This mock function is meant to replace tor_addr_lookup(). * It pretends to fail by returning -1 to caller. Also, this function * increments <b>n_hostname_failure</b> every time it is called. */ static int -tor_lookup_hostname_failure(const char *name, uint32_t *addr) +tor_addr_lookup_failure(const char *name, uint16_t family, tor_addr_t *addr) { (void)name; + (void)family; (void)addr; n_hostname_failure++; @@ -1097,29 +1100,29 @@ tor_gethostname_failure(char *name, size_t namelen) return -1; } -static int n_get_interface_address = 0; +static int n_get_interface_address6 = 0; +static sa_family_t last_address6_family; /** This mock function is meant to replace get_interface_address(). * It answers with address 8.8.8.8. This function increments * <b>n_get_interface_address</b> by one every time it is called. */ static int -get_interface_address_08080808(int severity, uint32_t *addr) +get_interface_address6_08080808(int severity, sa_family_t family, + tor_addr_t *addr) { (void)severity; - n_get_interface_address++; + n_get_interface_address6++; - if (addr) { - *addr = ntohl(0x08080808); + if (family == AF_INET) { + if (addr) { + tor_addr_from_ipv4h(addr, 0x08080808); + } } - return 0; } -static int n_get_interface_address6 = 0; -static sa_family_t last_address6_family; - /** This mock function is meant to replace get_interface_address6(). * It answers with IP address 9.9.9.9 iff both of the following are true: * - <b>family</b> is AF_INET @@ -1145,25 +1148,6 @@ get_interface_address6_replacement(int severity, sa_family_t family, return 0; } -static int n_get_interface_address_failure = 0; - -/** - * This mock function is meant to replace get_interface_address(). - * It pretends to fail getting interface address by returning -1. - * <b>n_get_interface_address_failure</b> is incremented by one - * every time this function is called. - */ -static int -get_interface_address_failure(int severity, uint32_t *addr) -{ - (void)severity; - (void)addr; - - n_get_interface_address_failure++; - - return -1; -} - static int n_get_interface_address6_failure = 0; /** @@ -1186,21 +1170,19 @@ get_interface_address6_failure(int severity, sa_family_t family, } static void -test_config_resolve_my_address(void *arg) +test_config_find_my_address(void *arg) { or_options_t *options; - uint32_t resolved_addr; + tor_addr_t resolved_addr, test_addr; const char *method_used; char *hostname_out = NULL; - int retval; + bool retval; int prev_n_hostname_01010101; int prev_n_hostname_localhost; int prev_n_hostname_failure; int prev_n_gethostname_replacement; int prev_n_gethostname_failure; int prev_n_gethostname_localhost; - int prev_n_get_interface_address; - int prev_n_get_interface_address_failure; int prev_n_get_interface_address6; int prev_n_get_interface_address6_failure; @@ -1215,166 +1197,170 @@ test_config_resolve_my_address(void *arg) * If options->Address is a valid IPv4 address string, we want * the corresponding address to be parsed and returned. */ + config_line_append(&options->Address, "Address", "128.52.128.105"); + tor_addr_parse(&test_addr, "128.52.128.105"); - options->Address = tor_strdup("128.52.128.105"); - - retval = resolve_my_address(LOG_NOTICE,options,&resolved_addr, - &method_used,&hostname_out); + retval = find_my_address(options, AF_INET, LOG_NOTICE, &resolved_addr, + &method_used, &hostname_out); - tt_want(retval == 0); + tt_want(retval == true); tt_want_str_op(method_used,OP_EQ,"CONFIGURED"); tt_want(hostname_out == NULL); - tt_assert(resolved_addr == 0x80348069); + tt_assert(tor_addr_eq(&resolved_addr, &test_addr)); - tor_free(options->Address); + config_free_lines(options->Address); /* * CASE 2: - * If options->Address is a valid DNS address, we want resolve_my_address() - * function to ask tor_lookup_hostname() for help with resolving it + * If options->Address is a valid DNS address, we want find_my_address() + * function to ask tor_addr_lookup() for help with resolving it * and return the address that was resolved (in host order). */ - MOCK(tor_lookup_hostname,tor_lookup_hostname_01010101); + MOCK(tor_addr_lookup, tor_addr_lookup_01010101); - tor_free(options->Address); - options->Address = tor_strdup("www.torproject.org"); + config_line_append(&options->Address, "Address", "www.torproject.org"); + tor_addr_parse(&test_addr, "1.1.1.1"); prev_n_hostname_01010101 = n_hostname_01010101; - retval = resolve_my_address(LOG_NOTICE,options,&resolved_addr, - &method_used,&hostname_out); + retval = find_my_address(options, AF_INET, LOG_NOTICE, &resolved_addr, + &method_used, &hostname_out); - tt_want(retval == 0); + tt_want(retval == true); tt_want(n_hostname_01010101 == prev_n_hostname_01010101 + 1); tt_want_str_op(method_used,OP_EQ,"RESOLVED"); tt_want_str_op(hostname_out,OP_EQ,"www.torproject.org"); - tt_assert(resolved_addr == 0x01010101); + tt_assert(tor_addr_eq(&resolved_addr, &test_addr)); - UNMOCK(tor_lookup_hostname); + UNMOCK(tor_addr_lookup); - tor_free(options->Address); + config_free_lines(options->Address); tor_free(hostname_out); /* * CASE 3: - * Given that options->Address is NULL, we want resolve_my_address() + * Given that options->Address is NULL, we want find_my_address() * to try and use tor_gethostname() to get hostname AND use - * tor_lookup_hostname() to get IP address. + * tor_addr_lookup() to get IP address. */ - resolved_addr = 0; - tor_free(options->Address); + tor_addr_make_unspec(&resolved_addr); options->Address = NULL; + tor_addr_parse(&test_addr, "1.1.1.1"); MOCK(tor_gethostname,tor_gethostname_replacement); - MOCK(tor_lookup_hostname,tor_lookup_hostname_01010101); + MOCK(tor_addr_lookup,tor_addr_lookup_01010101); prev_n_gethostname_replacement = n_gethostname_replacement; prev_n_hostname_01010101 = n_hostname_01010101; - retval = resolve_my_address(LOG_NOTICE,options,&resolved_addr, - &method_used,&hostname_out); + retval = find_my_address(options, AF_INET, LOG_NOTICE, &resolved_addr, + &method_used, &hostname_out); - tt_want(retval == 0); + tt_want(retval == true); tt_want(n_gethostname_replacement == prev_n_gethostname_replacement + 1); tt_want(n_hostname_01010101 == prev_n_hostname_01010101 + 1); tt_want_str_op(method_used,OP_EQ,"GETHOSTNAME"); tt_want_str_op(hostname_out,OP_EQ,"onionrouter!"); - tt_assert(resolved_addr == 0x01010101); + tt_assert(tor_addr_eq(&resolved_addr, &test_addr)); UNMOCK(tor_gethostname); - UNMOCK(tor_lookup_hostname); + UNMOCK(tor_addr_lookup); tor_free(hostname_out); /* * CASE 4: * Given that options->Address is a local host address, we want - * resolve_my_address() function to fail. + * find_my_address() function to fail. */ - resolved_addr = 0; - tor_free(options->Address); - options->Address = tor_strdup("127.0.0.1"); + tor_addr_make_unspec(&resolved_addr); + config_line_append(&options->Address, "Address", "127.0.0.1"); + tor_addr_parse(&test_addr, "127.0.0.1"); - retval = resolve_my_address(LOG_NOTICE,options,&resolved_addr, - &method_used,&hostname_out); + retval = find_my_address(options, AF_INET, LOG_NOTICE, &resolved_addr, + &method_used, &hostname_out); - tt_want(resolved_addr == 0); - tt_int_op(retval, OP_EQ, -1); + tt_want(tor_addr_is_null(&resolved_addr) == 1); + tt_want(retval == false); - tor_free(options->Address); + config_free_lines(options->Address); tor_free(hostname_out); /* * CASE 5: - * We want resolve_my_address() to fail if DNS address in options->Address + * We want find_my_address() to fail if DNS address in options->Address * cannot be resolved. */ - MOCK(tor_lookup_hostname,tor_lookup_hostname_failure); + MOCK(tor_addr_lookup,tor_addr_lookup_failure); prev_n_hostname_failure = n_hostname_failure; - tor_free(options->Address); - options->Address = tor_strdup("www.tor-project.org"); + config_line_append(&options->Address, "Address", "www.tor-project.org"); - retval = resolve_my_address(LOG_NOTICE,options,&resolved_addr, - &method_used,&hostname_out); + retval = find_my_address(options, AF_INET, LOG_NOTICE, &resolved_addr, + &method_used, &hostname_out); tt_want(n_hostname_failure == prev_n_hostname_failure + 1); - tt_int_op(retval, OP_EQ, -1); + tt_want(tor_addr_is_null(&resolved_addr) == 1); + tt_want(retval == false); - UNMOCK(tor_lookup_hostname); + UNMOCK(tor_addr_lookup); - tor_free(options->Address); + config_free_lines(options->Address); + options->Address = NULL; tor_free(hostname_out); /* * CASE 6: * If options->Address is NULL AND gettting local hostname fails, we want - * resolve_my_address() to fail as well. + * find_my_address() to fail as well. */ MOCK(tor_gethostname,tor_gethostname_failure); prev_n_gethostname_failure = n_gethostname_failure; - retval = resolve_my_address(LOG_NOTICE,options,&resolved_addr, - &method_used,&hostname_out); + retval = find_my_address(options, AF_INET, LOG_NOTICE, &resolved_addr, + &method_used, &hostname_out); tt_want(n_gethostname_failure == prev_n_gethostname_failure + 1); - tt_int_op(retval, OP_EQ, -1); + tt_want(tor_addr_is_null(&resolved_addr) == 1); + tt_want(retval == false); UNMOCK(tor_gethostname); tor_free(hostname_out); /* * CASE 7: - * We want resolve_my_address() to try and get network interface address via + * We want find_my_address() to try and get network interface address via * get_interface_address() if hostname returned by tor_gethostname() cannot be * resolved into IP address. */ MOCK(tor_gethostname,tor_gethostname_replacement); - MOCK(tor_lookup_hostname,tor_lookup_hostname_failure); - MOCK(get_interface_address,get_interface_address_08080808); + MOCK(tor_addr_lookup,tor_addr_lookup_failure); + MOCK(get_interface_address6, get_interface_address6_08080808); + + tor_addr_parse(&test_addr, "8.8.8.8"); prev_n_gethostname_replacement = n_gethostname_replacement; - prev_n_get_interface_address = n_get_interface_address; + prev_n_get_interface_address6 = n_get_interface_address6; - retval = resolve_my_address(LOG_NOTICE,options,&resolved_addr, - &method_used,&hostname_out); + retval = find_my_address(options, AF_INET, LOG_NOTICE, &resolved_addr, + &method_used, &hostname_out); - tt_want(retval == 0); + tt_want(retval == true); tt_want_int_op(n_gethostname_replacement, OP_EQ, prev_n_gethostname_replacement + 1); - tt_want_int_op(n_get_interface_address, OP_EQ, - prev_n_get_interface_address + 1); + tt_want_int_op(n_get_interface_address6, OP_EQ, + prev_n_get_interface_address6 + 1); tt_want_str_op(method_used,OP_EQ,"INTERFACE"); tt_want(hostname_out == NULL); - tt_assert(resolved_addr == 0x08080808); + tt_assert(tor_addr_eq(&resolved_addr, &test_addr)); UNMOCK(get_interface_address); tor_free(hostname_out); @@ -1382,86 +1368,87 @@ test_config_resolve_my_address(void *arg) /* * CASE 8: * Suppose options->Address is NULL AND hostname returned by tor_gethostname() - * is unresolvable. We want resolve_my_address to fail if + * is unresolvable. We want find_my_address to fail if * get_interface_address() fails. */ - MOCK(get_interface_address,get_interface_address_failure); + MOCK(get_interface_address6, get_interface_address6_failure); - prev_n_get_interface_address_failure = n_get_interface_address_failure; + prev_n_get_interface_address6_failure = n_get_interface_address6_failure; prev_n_gethostname_replacement = n_gethostname_replacement; - retval = resolve_my_address(LOG_NOTICE,options,&resolved_addr, - &method_used,&hostname_out); + retval = find_my_address(options, AF_INET, LOG_NOTICE, &resolved_addr, + &method_used, &hostname_out); - tt_want(n_get_interface_address_failure == - prev_n_get_interface_address_failure + 1); + tt_want(n_get_interface_address6_failure == + prev_n_get_interface_address6_failure + 1); tt_want(n_gethostname_replacement == prev_n_gethostname_replacement + 1); - tt_int_op(retval, OP_EQ, -1); + tt_want(retval == false); UNMOCK(get_interface_address); tor_free(hostname_out); /* * CASE 9: - * Given that options->Address is NULL AND tor_lookup_hostname() + * Given that options->Address is NULL AND tor_addr_lookup() * fails AND hostname returned by gethostname() resolves - * to local IP address, we want resolve_my_address() function to + * to local IP address, we want find_my_address() function to * call get_interface_address6(.,AF_INET,.) and return IP address * the latter function has found. */ - MOCK(tor_lookup_hostname,tor_lookup_hostname_failure); + MOCK(tor_addr_lookup,tor_addr_lookup_failure); MOCK(tor_gethostname,tor_gethostname_replacement); MOCK(get_interface_address6,get_interface_address6_replacement); + tor_addr_parse(&test_addr, "9.9.9.9"); + prev_n_gethostname_replacement = n_gethostname_replacement; prev_n_hostname_failure = n_hostname_failure; prev_n_get_interface_address6 = n_get_interface_address6; - retval = resolve_my_address(LOG_NOTICE,options,&resolved_addr, - &method_used,&hostname_out); + retval = find_my_address(options, AF_INET, LOG_NOTICE, &resolved_addr, + &method_used, &hostname_out); tt_want(last_address6_family == AF_INET); tt_want(n_get_interface_address6 == prev_n_get_interface_address6 + 1); tt_want(n_hostname_failure == prev_n_hostname_failure + 1); tt_want(n_gethostname_replacement == prev_n_gethostname_replacement + 1); - tt_want(retval == 0); + tt_want(retval == true); tt_want_str_op(method_used,OP_EQ,"INTERFACE"); - tt_assert(resolved_addr == 0x09090909); + tt_assert(tor_addr_eq(&resolved_addr, &test_addr)); - UNMOCK(tor_lookup_hostname); + UNMOCK(tor_addr_lookup); UNMOCK(tor_gethostname); UNMOCK(get_interface_address6); tor_free(hostname_out); /* - * CASE 10: We want resolve_my_address() to fail if all of the following + * CASE 10: We want find_my_address() to fail if all of the following * are true: * 1. options->Address is not NULL * 2. ... but it cannot be converted to struct in_addr by * tor_inet_aton() - * 3. ... and tor_lookup_hostname() fails to resolve the + * 3. ... and tor_addr_lookup() fails to resolve the * options->Address */ - MOCK(tor_lookup_hostname,tor_lookup_hostname_failure); + MOCK(tor_addr_lookup, tor_addr_lookup_failure); prev_n_hostname_failure = n_hostname_failure; - tor_free(options->Address); - options->Address = tor_strdup("some_hostname"); + config_line_append(&options->Address, "Address", "some_hostname"); - retval = resolve_my_address(LOG_NOTICE, options, &resolved_addr, - &method_used,&hostname_out); + retval = find_my_address(options, AF_INET, LOG_NOTICE, &resolved_addr, + &method_used, &hostname_out); tt_want(n_hostname_failure == prev_n_hostname_failure + 1); - tt_int_op(retval, OP_EQ, -1); + tt_want(retval == false); UNMOCK(tor_gethostname); - UNMOCK(tor_lookup_hostname); + UNMOCK(tor_addr_lookup); tor_free(hostname_out); @@ -1473,7 +1460,7 @@ test_config_resolve_my_address(void *arg) * if running on. * 3. Hostname from previous step cannot be converted to * address by using tor_inet_aton() function. - * 4. However, tor_lookup_hostname() succeeds in resolving the + * 4. However, tor_addr_lookup() succeeds in resolving the * hostname from step 2. * 5. Unfortunately, tor_addr_is_internal() deems this address * to be internal. @@ -1484,19 +1471,20 @@ test_config_resolve_my_address(void *arg) * and address from step 6. */ - tor_free(options->Address); + config_free_lines(options->Address); options->Address = NULL; + tor_addr_parse(&test_addr, "9.9.9.9"); MOCK(tor_gethostname,tor_gethostname_replacement); - MOCK(tor_lookup_hostname,tor_lookup_hostname_localhost); + MOCK(tor_addr_lookup,tor_addr_lookup_localhost); MOCK(get_interface_address6,get_interface_address6_replacement); prev_n_gethostname_replacement = n_gethostname_replacement; prev_n_hostname_localhost = n_hostname_localhost; prev_n_get_interface_address6 = n_get_interface_address6; - retval = resolve_my_address(LOG_DEBUG, options, &resolved_addr, - &method_used,&hostname_out); + retval = find_my_address(options, AF_INET, LOG_NOTICE, &resolved_addr, + &method_used, &hostname_out); tt_want(n_gethostname_replacement == prev_n_gethostname_replacement + 1); tt_want(n_hostname_localhost == prev_n_hostname_localhost + 1); @@ -1504,14 +1492,15 @@ test_config_resolve_my_address(void *arg) tt_str_op(method_used,OP_EQ,"INTERFACE"); tt_ptr_op(hostname_out, OP_EQ, NULL); - tt_int_op(retval, OP_EQ, 0); + tt_want(retval == true); + tt_assert(tor_addr_eq(&resolved_addr, &test_addr)); /* * CASE 11b: * 1-5 as above. * 6. get_interface_address6() fails. * - * In this subcase, we want resolve_my_address() to fail. + * In this subcase, we want find_my_address() to fail. */ UNMOCK(get_interface_address6); @@ -1521,18 +1510,18 @@ test_config_resolve_my_address(void *arg) prev_n_hostname_localhost = n_hostname_localhost; prev_n_get_interface_address6_failure = n_get_interface_address6_failure; - retval = resolve_my_address(LOG_DEBUG, options, &resolved_addr, - &method_used,&hostname_out); + retval = find_my_address(options, AF_INET, LOG_DEBUG, &resolved_addr, + &method_used, &hostname_out); tt_want(n_gethostname_replacement == prev_n_gethostname_replacement + 1); tt_want(n_hostname_localhost == prev_n_hostname_localhost + 1); tt_want(n_get_interface_address6_failure == prev_n_get_interface_address6_failure + 1); - tt_int_op(retval, OP_EQ, -1); + tt_want(retval == false); UNMOCK(tor_gethostname); - UNMOCK(tor_lookup_hostname); + UNMOCK(tor_addr_lookup); UNMOCK(get_interface_address6); /* CASE 12: @@ -1543,7 +1532,7 @@ test_config_resolve_my_address(void *arg) * 4. into IPv4 address that tor_addr_is_inernal() considers to be * internal. * - * In this case, we want resolve_my_address() to fail. + * In this case, we want find_my_address() to fail. */ tor_free(options->Address); @@ -1554,22 +1543,22 @@ test_config_resolve_my_address(void *arg) prev_n_gethostname_localhost = n_gethostname_localhost; - retval = resolve_my_address(LOG_DEBUG, options, &resolved_addr, - &method_used,&hostname_out); + retval = find_my_address(options, AF_INET, LOG_DEBUG, &resolved_addr, + &method_used, &hostname_out); tt_want(n_gethostname_localhost == prev_n_gethostname_localhost + 1); - tt_int_op(retval, OP_EQ, -1); + tt_want(retval == false); UNMOCK(tor_gethostname); done: - tor_free(options->Address); + config_free_lines(options->Address); tor_free(options->DirAuthorities); or_options_free(options); tor_free(hostname_out); UNMOCK(tor_gethostname); - UNMOCK(tor_lookup_hostname); + UNMOCK(tor_addr_lookup); UNMOCK(get_interface_address); UNMOCK(get_interface_address6); UNMOCK(tor_gethostname); @@ -6256,7 +6245,7 @@ struct testcase_t config_tests[] = { CONFIG_TEST(adding_dir_servers, TT_FORK), CONFIG_TEST(default_dir_servers, TT_FORK), CONFIG_TEST(default_fallback_dirs, 0), - CONFIG_TEST(resolve_my_address, TT_FORK), + CONFIG_TEST(find_my_address, TT_FORK), CONFIG_TEST(addressmap, 0), CONFIG_TEST(parse_bridge_line, 0), CONFIG_TEST(parse_transport_options_line, 0), |