aboutsummaryrefslogtreecommitdiff
path: root/src/app
diff options
context:
space:
mode:
Diffstat (limited to 'src/app')
-rw-r--r--src/app/config/config.c4
-rw-r--r--src/app/config/or_options_st.h5
-rw-r--r--src/app/config/resolve_addr.c778
-rw-r--r--src/app/config/resolve_addr.h12
-rw-r--r--src/app/config/testnet.inc1
-rw-r--r--src/app/main/main.c3
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.
*/