[tor-commits] [tor/master] addr: Prioritize interface lookup over local hostname

nickm at torproject.org nickm at torproject.org
Thu Jul 2 15:00:59 UTC 2020


commit 45afb31e1ce7c8572342c4e444a178b590d9645d
Author: David Goulet <dgoulet at torproject.org>
Date:   Thu Jul 2 09:44:49 2020 -0400

    addr: Prioritize interface lookup over local hostname
    
    The find_my_address() function now prioritize the local interface over the
    local hostname when guessing the IP address.
    
    See proposal 312, section 3.2.1, general case:
    https://gitweb.torproject.org/torspec.git/tree/proposals/312-relay-auto-ipv6-addr.txt#n359
    
    The entire unit tests had to be refactored to make this possible. Instead of
    hot patching it, it has been rewritten to cover all possible cases and the
    test interface has been changed to accomodate both IPv4 and IPv6 in order for
    them to be tested identically.
    
    Closes #33238
    
    Signed-off-by: David Goulet <dgoulet at torproject.org>
---
 changes/ticket33238           |   5 +
 src/app/config/resolve_addr.c |  29 +-
 src/test/test_config.c        | 852 +++++++++++-------------------------------
 3 files changed, 228 insertions(+), 658 deletions(-)

diff --git a/changes/ticket33238 b/changes/ticket33238
new file mode 100644
index 000000000..2c4c3968c
--- /dev/null
+++ b/changes/ticket33238
@@ -0,0 +1,5 @@
+  o Minor feature (address discovery):
+    - If no Address statements are found, relays now prioritize guessing their
+      address by looking at the local interface instead of the local hostname.
+      If the interface address can't be found, the local hostname is used.
+      Closes ticket 33238.
diff --git a/src/app/config/resolve_addr.c b/src/app/config/resolve_addr.c
index 167a4aa4d..8c08c6ec0 100644
--- a/src/app/config/resolve_addr.c
+++ b/src/app/config/resolve_addr.c
@@ -445,8 +445,8 @@ static fn_address_ret_t
 {
   /* These functions are in order for our find address algorithm. */
   get_address_from_config,
-  get_address_from_hostname,
   get_address_from_interface,
+  get_address_from_hostname,
 };
 /** Length of address table as in how many functions. */
 static const size_t fn_address_table_len = ARRAY_LENGTH(fn_address_table);
@@ -478,7 +478,17 @@ static const size_t fn_address_table_len = ARRAY_LENGTH(fn_address_table);
  *
  *     If no given Address, fallback to the local hostname (see section 2).
  *
- *  2. Look at the local hostname.
+ *  2. Look at the network interface.
+ *
+ *     Attempt to find the first public usable address from the list of
+ *     network interface returned by the OS.
+ *
+ *     On failure, we attempt to look at the local hostname (3).
+ *
+ *     On success, addr_out is set with it, method_out is set to "INTERFACE"
+ *     and hostname_out is set to NULL.
+ *
+ *  3. Look at the local hostname.
  *
  *     If the local hostname resolves to a non internal address, addr_out is
  *     set with it, method_out is set to "GETHOSTNAME" and hostname_out is set
@@ -489,20 +499,7 @@ static const size_t fn_address_table_len = ARRAY_LENGTH(fn_address_table);
  *     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.
+ *     If the local hostname can NOT be resolved, an error is returned.
  *
  * @param options Global configuration options.
  * @param family IP address family. Only AF_INET and AF_INET6 are supported.
diff --git a/src/test/test_config.c b/src/test/test_config.c
index 84b18c90e..c4d6a6efc 100644
--- a/src/test/test_config.c
+++ b/src/test/test_config.c
@@ -53,6 +53,7 @@
 
 #include "test/test_helpers.h"
 #include "test/resolve_test_helpers.h"
+#include "test/log_test_helpers.h"
 
 #include "feature/dirclient/dir_server_st.h"
 #include "core/or/port_cfg_st.h"
@@ -989,6 +990,9 @@ test_config_fix_my_family(void *arg)
 }
 
 static int n_hostname_01010101 = 0;
+static const char *ret_addr_lookup_01010101[2] = {
+  "1.1.1.1", "0101::0101",
+};
 
 /** This mock function is meant to replace tor_addr_lookup().
  * It answers with 1.1.1.1 as IP adddress that resulted from lookup.
@@ -1002,12 +1006,13 @@ tor_addr_lookup_01010101(const char *name, uint16_t family, tor_addr_t *addr)
 
   if (family == AF_INET) {
     if (name && addr) {
-      tor_addr_from_ipv4h(addr, 0x01010101);
+      int ret = tor_addr_parse(addr, ret_addr_lookup_01010101[0]);
+      tt_int_op(ret, OP_EQ, family);
     }
   } else if (family == AF_INET6) {
     if (name && addr) {
-      int ret = tor_addr_parse(addr, "0101::0101");
-      tt_int_op(ret, OP_EQ, AF_INET6);
+      int ret = tor_addr_parse(addr, ret_addr_lookup_01010101[1]);
+      tt_int_op(ret, OP_EQ, family);
     }
   }
  done:
@@ -1154,6 +1159,9 @@ tor_gethostname_failure(char *name, size_t namelen)
 
 static int n_get_interface_address6 = 0;
 static sa_family_t last_address6_family;
+static const char *ret_get_interface_address6_08080808[2] = {
+  "8.8.8.8", "0808::0808",
+};
 
 /** This mock function is meant to replace get_interface_address().
  * It answers with address 8.8.8.8. This function increments
@@ -1169,11 +1177,12 @@ get_interface_address6_08080808(int severity, sa_family_t family,
 
   if (family == AF_INET) {
     if (addr) {
-      tor_addr_from_ipv4h(addr, 0x08080808);
+      int ret = tor_addr_parse(addr, ret_get_interface_address6_08080808[0]);
+      tt_int_op(ret, OP_EQ, AF_INET);
     }
   } else if (family == AF_INET6) {
     if (addr) {
-      int ret = tor_addr_parse(addr, "0808::0808");
+      int ret = tor_addr_parse(addr, ret_get_interface_address6_08080808[1]);
       tt_int_op(ret, OP_EQ, AF_INET6);
     }
   }
@@ -1188,6 +1197,7 @@ get_interface_address6_08080808(int severity, sa_family_t family,
  * This function increments <b>n_get_interface_address6</b> by one every
  * time it is called.
  */
+#if 0
 static int
 get_interface_address6_replacement(int severity, sa_family_t family,
                                    tor_addr_t *addr)
@@ -1205,6 +1215,7 @@ get_interface_address6_replacement(int severity, sa_family_t family,
 
   return 0;
 }
+#endif
 
 static int n_get_interface_address6_failure = 0;
 
@@ -1389,8 +1400,40 @@ test_config_find_my_address_mixed(void *arg)
   UNMOCK(tor_addr_lookup);
 }
 
+/** Parameters for the find_my_address() test. We test both AF_INET and
+ * AF_INET6 but we have one interface to do so thus we run the same exact unit
+ * tests for both without copying them. */
+typedef struct find_my_address_params_t {
+  /* Index where the mock function results are located. For intance,
+   * tor_addr_lookup_01010101() will have its returned value depending on the
+   * family in ret_addr_lookup_01010101[].
+   *
+   * Values that can be found:
+   *    AF_INET : index 0.
+   *    AF_INET6: index 1.
+   */
+  int idx;
+  int family;
+  const char *public_ip;
+  const char *internal_ip;
+} find_my_address_params_t;
+
+static find_my_address_params_t addr_param_v4 = {
+  .idx = 0,
+  .family = AF_INET,
+  .public_ip = "128.52.128.105",
+  .internal_ip = "127.0.0.1",
+};
+
+static find_my_address_params_t addr_param_v6 = {
+  .idx = 1,
+  .family = AF_INET6,
+  .public_ip = "4242::4242",
+  .internal_ip = "::1",
+};
+
 static void
-test_config_find_my_address_v6(void *arg)
+test_config_find_my_address(void *arg)
 {
   or_options_t *options;
   tor_addr_t resolved_addr, test_addr;
@@ -1398,786 +1441,306 @@ test_config_find_my_address_v6(void *arg)
   char *hostname_out = NULL;
   bool retval;
   int prev_n_hostname_01010101;
-  int prev_n_hostname_localhost;
   int prev_n_hostname_failure;
+  int prev_n_hostname_localhost;
   int prev_n_gethostname_replacement;
   int prev_n_gethostname_failure;
   int prev_n_gethostname_localhost;
   int prev_n_get_interface_address6;
   int prev_n_get_interface_address6_failure;
 
-  (void)arg;
+  const find_my_address_params_t *p = arg;
 
   options = options_new();
-
   options_init(options);
 
   /*
-   * CASE 1:
-   * If options->Address is a valid IPv6 address string, we want
-   * the corresponding address to be parsed and returned.
+   * Case 1:
+   *    1. Address is a valid address.
+   *
+   * Expected to succeed.
    */
-  config_line_append(&options->Address, "Address",
-                     "2a01:4f8:fff0:4f:266:37ff:fe2c:5d19");
-  tor_addr_parse(&test_addr, "2a01:4f8:fff0:4f:266:37ff:fe2c:5d19");
+  config_line_append(&options->Address, "Address", p->public_ip);
+  tor_addr_parse(&test_addr, p->public_ip);
 
-  retval = find_my_address(options, AF_INET6, LOG_NOTICE, &resolved_addr,
+  retval = find_my_address(options, p->family, LOG_NOTICE, &resolved_addr,
                            &method_used, &hostname_out);
 
-  tt_want(retval == true);
-  tt_want_str_op(method_used, OP_EQ, "CONFIGURED");
-  tt_want(hostname_out == NULL);
-  tt_assert(tor_addr_eq(&resolved_addr, &test_addr));
-
-  config_free_lines(options->Address);
+  VALIDATE_FOUND_ADDRESS(true, "CONFIGURED", NULL);
+  CLEANUP_FOUND_ADDRESS;
 
   /*
-   * CASE 2:
-   * 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).
+   * Case 2: Address is a resolvable address. Expected to succeed.
    */
-
   MOCK(tor_addr_lookup, tor_addr_lookup_01010101);
 
   config_line_append(&options->Address, "Address", "www.torproject.org");
-  tor_addr_parse(&test_addr, "0101::0101");
-
-  prev_n_hostname_01010101 = n_hostname_01010101;
-
-  retval = find_my_address(options, AF_INET6, LOG_NOTICE, &resolved_addr,
-                           &method_used, &hostname_out);
-
-  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(tor_addr_eq(&resolved_addr, &test_addr));
-
-  UNMOCK(tor_addr_lookup);
-
-  config_free_lines(options->Address);
-  tor_free(hostname_out);
-
-  /*
-   * CASE 3:
-   * Given that options->Address is NULL, we want find_my_address()
-   * to try and use tor_gethostname() to get hostname AND use
-   * tor_addr_lookup() to get IP address.
-   */
-
-  tor_addr_make_unspec(&resolved_addr);
-  options->Address = NULL;
-  tor_addr_parse(&test_addr, "0101::0101");
-
-  MOCK(tor_gethostname, tor_gethostname_replacement);
-  MOCK(tor_addr_lookup, tor_addr_lookup_01010101);
+  tor_addr_parse(&test_addr, ret_addr_lookup_01010101[p->idx]);
 
-  prev_n_gethostname_replacement = n_gethostname_replacement;
   prev_n_hostname_01010101 = n_hostname_01010101;
 
-  retval = find_my_address(options, AF_INET6, LOG_NOTICE, &resolved_addr,
-                           &method_used, &hostname_out);
-
-  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(tor_addr_eq(&resolved_addr, &test_addr));
-
-  UNMOCK(tor_gethostname);
-  UNMOCK(tor_addr_lookup);
-
-  tor_free(hostname_out);
-
-  /*
-   * CASE 4:
-   * Given that options->Address is a local host address, we want
-   * find_my_address() function to fail.
-   */
-
-  tor_addr_make_unspec(&resolved_addr);
-  config_line_append(&options->Address, "Address", "::1");
-  tor_addr_parse(&test_addr, "::1");
-
-  retval = find_my_address(options, AF_INET6, LOG_NOTICE, &resolved_addr,
-                           &method_used, &hostname_out);
-
-  tt_want(tor_addr_is_null(&resolved_addr) == 1);
-  tt_want(retval == false);
-
-  config_free_lines(options->Address);
-  tor_free(hostname_out);
-
-  /*
-   * CASE 5:
-   * We want find_my_address() to fail if DNS address in options->Address
-   * cannot be resolved.
-   */
-
-  MOCK(tor_addr_lookup, tor_addr_lookup_failure);
-
-  prev_n_hostname_failure = n_hostname_failure;
-
-  config_line_append(&options->Address, "Address", "www.tor-project.org");
-
-  retval = find_my_address(options, AF_INET6, LOG_NOTICE, &resolved_addr,
+  retval = find_my_address(options, p->family, LOG_NOTICE, &resolved_addr,
                            &method_used, &hostname_out);
 
-  tt_want(n_hostname_failure == prev_n_hostname_failure + 1);
-  tt_want(tor_addr_is_null(&resolved_addr) == 1);
-  tt_want(retval == false);
+  tt_int_op(n_hostname_01010101, OP_EQ, ++prev_n_hostname_01010101);
+  VALIDATE_FOUND_ADDRESS(true, "RESOLVED", "www.torproject.org");
+  CLEANUP_FOUND_ADDRESS;
 
   UNMOCK(tor_addr_lookup);
 
-  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
-   * find_my_address() to fail as well.
+   * Case 3: Address is a local addressi (internal). Expected to fail.
    */
+  config_line_append(&options->Address, "Address", p->internal_ip);
 
-  MOCK(tor_gethostname,tor_gethostname_failure);
-
-  prev_n_gethostname_failure = n_gethostname_failure;
+  setup_full_capture_of_logs(LOG_NOTICE);
 
-  retval = find_my_address(options, AF_INET6, LOG_NOTICE, &resolved_addr,
+  retval = find_my_address(options, p->family, LOG_NOTICE, &resolved_addr,
                            &method_used, &hostname_out);
 
-  tt_want(n_gethostname_failure == prev_n_gethostname_failure + 1);
-  tt_want(tor_addr_is_null(&resolved_addr) == 1);
-  tt_want(retval == false);
+  expect_log_msg_containing("is a private IP address. Tor relays that "
+                            "use the default DirAuthorities must have "
+                            "public IP addresses.");
+  teardown_capture_of_logs();
 
-  UNMOCK(tor_gethostname);
-  tor_free(hostname_out);
+  VALIDATE_FOUND_ADDRESS(false, NULL, NULL);
+  CLEANUP_FOUND_ADDRESS;
 
   /*
-   * CASE 7:
-   * 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.
+   * Case 4: Address is a local address but custom authorities. Expected to
+   * succeed.
    */
+  config_line_append(&options->Address, "Address", p->internal_ip);
+  options->DirAuthorities = tor_malloc_zero(sizeof(config_line_t));
+  tor_addr_parse(&test_addr, p->internal_ip);
 
-  MOCK(tor_gethostname, tor_gethostname_replacement);
-  MOCK(tor_addr_lookup, tor_addr_lookup_failure);
-  MOCK(get_interface_address6, get_interface_address6_08080808);
-
-  tor_addr_parse(&test_addr, "0808::0808");
-
-  prev_n_gethostname_replacement = n_gethostname_replacement;
-  prev_n_get_interface_address6 = n_get_interface_address6;
-
-  retval = find_my_address(options, AF_INET6, LOG_NOTICE, &resolved_addr,
+  retval = find_my_address(options, p->family, LOG_NOTICE, &resolved_addr,
                            &method_used, &hostname_out);
 
-  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_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(tor_addr_eq(&resolved_addr, &test_addr));
-
-  UNMOCK(get_interface_address);
-  tor_free(hostname_out);
+  VALIDATE_FOUND_ADDRESS(true, "CONFIGURED", NULL);
+  CLEANUP_FOUND_ADDRESS;
 
   /*
-   * CASE 8:
-   * Suppose options->Address is NULL AND hostname returned by
-   * tor_gethostname() is unresolvable. We want find_my_address to fail if
-   * get_interface_address() fails.
+   * Case 5: Multiple address in Address. Expected to fail.
    */
+  config_line_append(&options->Address, "Address", p->public_ip);
+  config_line_append(&options->Address, "Address", p->public_ip);
 
-  MOCK(get_interface_address6, get_interface_address6_failure);
-
-  prev_n_get_interface_address6_failure = n_get_interface_address6_failure;
-  prev_n_gethostname_replacement = n_gethostname_replacement;
+  setup_full_capture_of_logs(LOG_NOTICE);
 
-  retval = find_my_address(options, AF_INET6, LOG_NOTICE, &resolved_addr,
+  retval = find_my_address(options, p->family, LOG_NOTICE, &resolved_addr,
                            &method_used, &hostname_out);
 
-  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_want(retval == false);
+  expect_log_msg_containing("Found 2 Address statement of address family");
+  teardown_capture_of_logs();
 
-  UNMOCK(get_interface_address);
-  tor_free(hostname_out);
+  VALIDATE_FOUND_ADDRESS(false, NULL, NULL);
+  CLEANUP_FOUND_ADDRESS;
 
   /*
-   * CASE 9:
-   * Given that options->Address is NULL AND tor_addr_lookup()
-   * fails AND hostname returned by gethostname() resolves
-   * to local IP address, we want find_my_address() function to
-   * call get_interface_address6(.,AF_INET6,.) and return IP address
-   * the latter function has found.
+   * Case 6: Another address family is configured. Expected to fail.
    */
+  if (p->family == AF_INET) {
+    config_line_append(&options->Address, "Address", "4242::4242");
+  } else {
+    config_line_append(&options->Address, "Address", "1.1.1.1");
+  }
 
-  MOCK(tor_addr_lookup, tor_addr_lookup_failure);
-  MOCK(tor_gethostname, tor_gethostname_replacement);
-  MOCK(get_interface_address6, get_interface_address6_08080808);
-
-  tor_addr_parse(&test_addr, "0808::0808");
-
-  prev_n_gethostname_replacement = n_gethostname_replacement;
-  prev_n_hostname_failure = n_hostname_failure;
-  prev_n_get_interface_address6 = n_get_interface_address6;
+  setup_full_capture_of_logs(LOG_NOTICE);
 
-  retval = find_my_address(options, AF_INET6, LOG_NOTICE, &resolved_addr,
+  retval = find_my_address(options, p->family, LOG_NOTICE, &resolved_addr,
                            &method_used, &hostname_out);
 
-  tt_want(last_address6_family == AF_INET6);
-  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 == true);
-  tt_want_str_op(method_used, OP_EQ, "INTERFACE");
-  tt_assert(tor_addr_eq(&resolved_addr, &test_addr));
-
-  UNMOCK(tor_addr_lookup);
-  UNMOCK(tor_gethostname);
-  UNMOCK(get_interface_address6);
+  expect_log_msg_containing("No Address option found for family");
+  teardown_capture_of_logs();
 
-  tor_free(hostname_out);
+  VALIDATE_FOUND_ADDRESS(false, NULL, NULL);
+  CLEANUP_FOUND_ADDRESS;
 
   /*
-   * 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_addr_lookup() fails to resolve the
-   *      options->Address
+   * Case 7: Address is a non resolvable hostname. Expected to fail.
    */
-
   MOCK(tor_addr_lookup, tor_addr_lookup_failure);
 
+  config_line_append(&options->Address, "Address", "www.torproject.org");
   prev_n_hostname_failure = n_hostname_failure;
 
-  config_line_append(&options->Address, "Address", "some_hostname");
+  setup_full_capture_of_logs(LOG_NOTICE);
 
-  retval = find_my_address(options, AF_INET6, LOG_NOTICE, &resolved_addr,
+  retval = find_my_address(options, p->family, LOG_NOTICE, &resolved_addr,
                            &method_used, &hostname_out);
 
-  tt_want(n_hostname_failure == prev_n_hostname_failure + 1);
-  tt_want(retval == false);
+  expect_log_msg_containing("Could not resolve local Address "
+                            "'www.torproject.org'. Failing.");
+  teardown_capture_of_logs();
 
-  UNMOCK(tor_gethostname);
-  UNMOCK(tor_addr_lookup);
+  tt_int_op(n_hostname_failure, OP_EQ, ++prev_n_hostname_failure);
+  VALIDATE_FOUND_ADDRESS(false, NULL, NULL);
+  CLEANUP_FOUND_ADDRESS;
 
-  tor_free(hostname_out);
+  UNMOCK(tor_addr_lookup);
 
   /*
-   * CASE 11:
-   * Suppose the following sequence of events:
-   *   1. options->Address is NULL
-   *   2. tor_gethostname() succeeds to get hostname of machine Tor
-   *      if running on.
-   *   3. Hostname from previous step cannot be converted to
-   *      address by using tor_inet_aton() function.
-   *   4. However, tor_addr_lookup() succeeds in resolving the
-   *      hostname from step 2.
-   *   5. Unfortunately, tor_addr_is_internal() deems this address
-   *      to be internal.
-   *   6. get_interface_address6(.,AF_INET,.) returns non-internal
-   *      IPv4
+   * Case 8:
+   *    1. Address is NULL
+   *    2. Interface address is a valid address.
    *
-   *   We want resolve_my_addr() to succeed with method "INTERFACE"
-   *   and address from step 6.
+   * Expected to succeed.
    */
-
-  config_free_lines(options->Address);
   options->Address = NULL;
-  tor_addr_parse(&test_addr, "0808::0808");
+  tor_addr_parse(&test_addr, ret_get_interface_address6_08080808[p->idx]);
 
-  MOCK(tor_gethostname, tor_gethostname_replacement);
-  MOCK(tor_addr_lookup, tor_addr_lookup_localhost);
   MOCK(get_interface_address6, get_interface_address6_08080808);
 
-  prev_n_gethostname_replacement = n_gethostname_replacement;
-  prev_n_hostname_localhost = n_hostname_localhost;
   prev_n_get_interface_address6 = n_get_interface_address6;
 
-  retval = find_my_address(options, AF_INET6, LOG_NOTICE, &resolved_addr,
+  retval = find_my_address(options, p->family, LOG_NOTICE, &resolved_addr,
                            &method_used, &hostname_out);
 
-  tt_want(n_gethostname_replacement == prev_n_gethostname_replacement + 1);
-  tt_want(n_hostname_localhost == prev_n_hostname_localhost + 1);
-  tt_want(n_get_interface_address6 == prev_n_get_interface_address6 + 1);
+  tt_int_op(n_get_interface_address6, OP_EQ, ++prev_n_get_interface_address6);
+  VALIDATE_FOUND_ADDRESS(true, "INTERFACE", NULL);
+  CLEANUP_FOUND_ADDRESS;
 
-  tt_str_op(method_used, OP_EQ, "INTERFACE");
-  tt_ptr_op(hostname_out, OP_EQ, NULL);
-  tt_want(retval == true);
-  tt_assert(tor_addr_eq(&resolved_addr, &test_addr));
+  UNMOCK(get_interface_address6);
 
   /*
-   * CASE 11b:
-   *   1-5 as above.
-   *   6. get_interface_address6() fails.
+   * Case 9:
+   *    1. Address is NULL
+   *    2. Interface address fails to be found.
+   *    3. Local hostname resolves to a valid address.
    *
-   *   In this subcase, we want find_my_address() to fail.
+   * Expected to succeed.
    */
+  options->Address = NULL;
+  tor_addr_parse(&test_addr, ret_addr_lookup_01010101[p->idx]);
 
-  UNMOCK(get_interface_address6);
   MOCK(get_interface_address6, get_interface_address6_failure);
+  MOCK(tor_gethostname, tor_gethostname_replacement);
+  MOCK(tor_addr_lookup, tor_addr_lookup_01010101);
 
-  prev_n_gethostname_replacement = n_gethostname_replacement;
-  prev_n_hostname_localhost = n_hostname_localhost;
   prev_n_get_interface_address6_failure = n_get_interface_address6_failure;
+  prev_n_hostname_01010101 = n_hostname_01010101;
+  prev_n_gethostname_replacement = n_gethostname_replacement;
 
-  retval = find_my_address(options, AF_INET6, LOG_DEBUG, &resolved_addr,
+  retval = find_my_address(options, p->family, LOG_NOTICE, &resolved_addr,
                            &method_used, &hostname_out);
 
-  tt_want(n_gethostname_replacement == prev_n_gethostname_replacement + 1);
-  tt_want(n_hostname_localhost == prev_n_hostname_localhost + 1);
-  tt_want(n_get_interface_address6_failure ==
-          prev_n_get_interface_address6_failure + 1);
-
-  tt_want(retval == false);
+  tt_int_op(n_get_interface_address6_failure, OP_EQ,
+            ++prev_n_get_interface_address6_failure);
+  tt_int_op(n_hostname_01010101, OP_EQ,
+            ++prev_n_hostname_01010101);
+  tt_int_op(n_gethostname_replacement, OP_EQ,
+            ++prev_n_gethostname_replacement);
+  VALIDATE_FOUND_ADDRESS(true, "GETHOSTNAME", "onionrouter!");
+  CLEANUP_FOUND_ADDRESS;
 
+  UNMOCK(get_interface_address6);
   UNMOCK(tor_gethostname);
   UNMOCK(tor_addr_lookup);
-  UNMOCK(get_interface_address6);
 
-  /* CASE 12:
-   * Suppose the following happens:
-   *   1. options->Address is NULL AND options->DirAuthorities is non-NULL
-   *   2. tor_gethostname() succeeds in getting hostname of a machine ...
-   *   3. ... which is successfully parsed by tor_inet_aton() ...
-   *   4. into IPv4 address that tor_addr_is_inernal() considers to be
-   *      internal.
+  /*
+   * Case 10:
+   *    1. Address is NULL
+   *    2. Interface address fails to be found.
+   *    3. Local hostname resolves to an internal address.
    *
-   *  In this case, we want find_my_address() to fail.
+   * Expected to fail.
    */
-
-  tor_free(options->Address);
   options->Address = NULL;
-  options->DirAuthorities = tor_malloc_zero(sizeof(config_line_t));
 
-  MOCK(tor_gethostname,tor_gethostname_localhost);
+  MOCK(get_interface_address6, get_interface_address6_failure);
+  MOCK(tor_gethostname, tor_gethostname_localhost);
+  MOCK(tor_addr_lookup, tor_addr_lookup_localhost);
 
+  prev_n_get_interface_address6_failure = n_get_interface_address6_failure;
+  prev_n_hostname_localhost = n_hostname_localhost;
   prev_n_gethostname_localhost = n_gethostname_localhost;
 
-  retval = find_my_address(options, AF_INET6, LOG_DEBUG, &resolved_addr,
+  retval = find_my_address(options, p->family, LOG_NOTICE, &resolved_addr,
                            &method_used, &hostname_out);
 
-  tt_want(n_gethostname_localhost == prev_n_gethostname_localhost + 1);
-  tt_want(retval == false);
-
-  UNMOCK(tor_gethostname);
-
- done:
-  config_free_lines(options->Address);
-  tor_free(options->DirAuthorities);
-  or_options_free(options);
-  tor_free(hostname_out);
+  tt_int_op(n_get_interface_address6_failure, OP_EQ,
+            ++prev_n_get_interface_address6_failure);
+  tt_int_op(n_hostname_localhost, OP_EQ,
+            ++prev_n_hostname_localhost);
+  tt_int_op(n_gethostname_localhost, OP_EQ,
+            ++prev_n_gethostname_localhost);
+  VALIDATE_FOUND_ADDRESS(false, NULL, NULL);
+  CLEANUP_FOUND_ADDRESS;
 
-  UNMOCK(tor_gethostname);
-  UNMOCK(tor_addr_lookup);
-  UNMOCK(get_interface_address);
   UNMOCK(get_interface_address6);
   UNMOCK(tor_gethostname);
-}
-
-static void
-test_config_find_my_address_v4(void *arg)
-{
-  or_options_t *options;
-  tor_addr_t resolved_addr, test_addr;
-  const char *method_used;
-  char *hostname_out = NULL;
-  bool retval;
-  int prev_n_hostname_01010101;
-  int prev_n_hostname_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_address6;
-  int prev_n_get_interface_address6_failure;
-
-  (void)arg;
-
-  options = options_new();
-
-  options_init(options);
-
- /*
-  * CASE 1:
-  * 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");
-
-  retval = find_my_address(options, AF_INET, LOG_NOTICE, &resolved_addr,
-                           &method_used, &hostname_out);
-
-  tt_want(retval == true);
-  tt_want_str_op(method_used,OP_EQ,"CONFIGURED");
-  tt_want(hostname_out == NULL);
-  tt_assert(tor_addr_eq(&resolved_addr, &test_addr));
-
-  config_free_lines(options->Address);
-
-/*
- * CASE 2:
- * 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_addr_lookup, tor_addr_lookup_01010101);
-
-  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 = find_my_address(options, AF_INET, LOG_NOTICE, &resolved_addr,
-                           &method_used, &hostname_out);
-
-  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(tor_addr_eq(&resolved_addr, &test_addr));
-
   UNMOCK(tor_addr_lookup);
 
-  config_free_lines(options->Address);
-  tor_free(hostname_out);
-
-/*
- * CASE 3:
- * Given that options->Address is NULL, we want find_my_address()
- * to try and use tor_gethostname() to get hostname AND use
- * tor_addr_lookup() to get IP 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_addr_lookup,tor_addr_lookup_01010101);
-
-  prev_n_gethostname_replacement = n_gethostname_replacement;
-  prev_n_hostname_01010101 = n_hostname_01010101;
-
-  retval = find_my_address(options, AF_INET, LOG_NOTICE, &resolved_addr,
-                           &method_used, &hostname_out);
-
-  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(tor_addr_eq(&resolved_addr, &test_addr));
-
-  UNMOCK(tor_gethostname);
-  UNMOCK(tor_addr_lookup);
-
-  tor_free(hostname_out);
-
-/*
- * CASE 4:
- * Given that options->Address is a local host address, we want
- * find_my_address() function to fail.
- */
-
-  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 = find_my_address(options, AF_INET, LOG_NOTICE, &resolved_addr,
-                           &method_used, &hostname_out);
-
-  tt_want(tor_addr_is_null(&resolved_addr) == 1);
-  tt_want(retval == false);
-
-  config_free_lines(options->Address);
-  tor_free(hostname_out);
-
-/*
- * CASE 5:
- * We want find_my_address() to fail if DNS address in options->Address
- * cannot be resolved.
- */
-
-  MOCK(tor_addr_lookup,tor_addr_lookup_failure);
-
-  prev_n_hostname_failure = n_hostname_failure;
-
-  config_line_append(&options->Address, "Address", "www.tor-project.org");
-
-  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_want(tor_addr_is_null(&resolved_addr) == 1);
-  tt_want(retval == false);
-
-  UNMOCK(tor_addr_lookup);
-
-  config_free_lines(options->Address);
+  /*
+   * Case 11:
+   *    1. Address is NULL
+   *    2. Interface address fails to be found.
+   *    3. Local hostname fails to be found.
+   *
+   * Expected to fail.
+   */
   options->Address = NULL;
-  tor_free(hostname_out);
-
-/*
- * CASE 6:
- * If options->Address is NULL AND gettting local hostname fails, we want
- * find_my_address() to fail as well.
- */
-
-  MOCK(tor_gethostname,tor_gethostname_failure);
-
-  prev_n_gethostname_failure = n_gethostname_failure;
-
-  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_want(tor_addr_is_null(&resolved_addr) == 1);
-  tt_want(retval == false);
-
-  UNMOCK(tor_gethostname);
-  tor_free(hostname_out);
-
-/*
- * CASE 7:
- * 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_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_address6 = n_get_interface_address6;
-
-  retval = find_my_address(options, AF_INET, LOG_NOTICE, &resolved_addr,
-                           &method_used, &hostname_out);
-
-  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_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(tor_addr_eq(&resolved_addr, &test_addr));
-
-  UNMOCK(get_interface_address);
-  tor_free(hostname_out);
-
-/*
- * CASE 8:
- * Suppose options->Address is NULL AND hostname returned by tor_gethostname()
- * is unresolvable. We want find_my_address to fail if
- * get_interface_address() fails.
- */
 
   MOCK(get_interface_address6, get_interface_address6_failure);
+  MOCK(tor_gethostname, tor_gethostname_failure);
 
   prev_n_get_interface_address6_failure = n_get_interface_address6_failure;
-  prev_n_gethostname_replacement = n_gethostname_replacement;
-
-  retval = find_my_address(options, AF_INET, LOG_NOTICE, &resolved_addr,
-                           &method_used, &hostname_out);
-
-  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_want(retval == false);
-
-  UNMOCK(get_interface_address);
-  tor_free(hostname_out);
-
-/*
- * CASE 9:
- * Given that options->Address is NULL AND tor_addr_lookup()
- * fails AND hostname returned by gethostname() resolves
- * 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_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;
+  prev_n_gethostname_failure = n_gethostname_failure;
 
-  retval = find_my_address(options, AF_INET, LOG_NOTICE, &resolved_addr,
+  retval = find_my_address(options, p->family, LOG_NOTICE, &resolved_addr,
                            &method_used, &hostname_out);
 
-  tt_want(last_address6_family == AF_INET);
-  tt_want(n_get_interface_address6 == prev_n_get_interface_address6 + 1);
-  tt_want(n_hostname_failure == prev_n_hostname_failure + 1);
-  tt_want(n_gethostname_replacement == prev_n_gethostname_replacement + 1);
-  tt_want(retval == true);
-  tt_want_str_op(method_used,OP_EQ,"INTERFACE");
-  tt_assert(tor_addr_eq(&resolved_addr, &test_addr));
+  tt_int_op(n_get_interface_address6_failure, OP_EQ,
+            ++prev_n_get_interface_address6_failure);
+  tt_int_op(n_gethostname_failure, OP_EQ,
+            ++prev_n_gethostname_failure);
+  VALIDATE_FOUND_ADDRESS(false, NULL, NULL);
+  CLEANUP_FOUND_ADDRESS;
 
-  UNMOCK(tor_addr_lookup);
-  UNMOCK(tor_gethostname);
   UNMOCK(get_interface_address6);
-
-  tor_free(hostname_out);
-
-  /*
-   * 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_addr_lookup() fails to resolve the
-   *      options->Address
-   */
-
-  MOCK(tor_addr_lookup, tor_addr_lookup_failure);
-
-  prev_n_hostname_failure = n_hostname_failure;
-
-  config_line_append(&options->Address, "Address", "some_hostname");
-
-  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_want(retval == false);
-
   UNMOCK(tor_gethostname);
-  UNMOCK(tor_addr_lookup);
-
-  tor_free(hostname_out);
 
   /*
-   * CASE 11:
-   * Suppose the following sequence of events:
-   *   1. options->Address is NULL
-   *   2. tor_gethostname() succeeds to get hostname of machine Tor
-   *      if running on.
-   *   3. Hostname from previous step cannot be converted to
-   *      address by using tor_inet_aton() function.
-   *   4. However, tor_addr_lookup() succeeds in resolving the
-   *      hostname from step 2.
-   *   5. Unfortunately, tor_addr_is_internal() deems this address
-   *      to be internal.
-   *   6. get_interface_address6(.,AF_INET,.) returns non-internal
-   *      IPv4
+   * Case 12:
+   *    1. Address is NULL
+   *    2. Interface address fails to be found.
+   *    3. Local hostname can't be resolved.
    *
-   *   We want resolve_my_addr() to succeed with method "INTERFACE"
-   *   and address from step 6.
+   * Expected to fail.
    */
-
-  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_addr_lookup,tor_addr_lookup_localhost);
-  MOCK(get_interface_address6,get_interface_address6_replacement);
+  MOCK(get_interface_address6, get_interface_address6_failure);
+  MOCK(tor_gethostname, tor_gethostname_replacement);
+  MOCK(tor_addr_lookup, tor_addr_lookup_failure);
 
+  prev_n_get_interface_address6_failure = n_get_interface_address6_failure;
   prev_n_gethostname_replacement = n_gethostname_replacement;
-  prev_n_hostname_localhost = n_hostname_localhost;
-  prev_n_get_interface_address6 = n_get_interface_address6;
+  prev_n_hostname_failure = n_hostname_failure;
 
-  retval = find_my_address(options, AF_INET, LOG_NOTICE, &resolved_addr,
+  retval = find_my_address(options, p->family, LOG_NOTICE, &resolved_addr,
                            &method_used, &hostname_out);
 
-  tt_want(n_gethostname_replacement == prev_n_gethostname_replacement + 1);
-  tt_want(n_hostname_localhost == prev_n_hostname_localhost + 1);
-  tt_want(n_get_interface_address6 == prev_n_get_interface_address6 + 1);
-
-  tt_str_op(method_used,OP_EQ,"INTERFACE");
-  tt_ptr_op(hostname_out, OP_EQ, NULL);
-  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 find_my_address() to fail.
-   */
+  tt_int_op(n_get_interface_address6_failure, OP_EQ,
+            ++prev_n_get_interface_address6_failure);
+  tt_int_op(n_gethostname_replacement, OP_EQ,
+            ++prev_n_gethostname_replacement);
+  tt_int_op(n_hostname_failure, OP_EQ,
+            ++prev_n_hostname_failure);
+  VALIDATE_FOUND_ADDRESS(false, NULL, NULL);
+  CLEANUP_FOUND_ADDRESS;
 
   UNMOCK(get_interface_address6);
-  MOCK(get_interface_address6,get_interface_address6_failure);
-
-  prev_n_gethostname_replacement = n_gethostname_replacement;
-  prev_n_hostname_localhost = n_hostname_localhost;
-  prev_n_get_interface_address6_failure = n_get_interface_address6_failure;
-
-  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_want(retval == false);
-
   UNMOCK(tor_gethostname);
   UNMOCK(tor_addr_lookup);
-  UNMOCK(get_interface_address6);
-
-  /* CASE 12:
-   * Suppose the following happens:
-   *   1. options->Address is NULL AND options->DirAuthorities is non-NULL
-   *   2. tor_gethostname() succeeds in getting hostname of a machine ...
-   *   3. ... which is successfully parsed by tor_inet_aton() ...
-   *   4. into IPv4 address that tor_addr_is_inernal() considers to be
-   *      internal.
-   *
-   *  In this case, we want find_my_address() to fail.
-   */
-
-  tor_free(options->Address);
-  options->Address = NULL;
-  options->DirAuthorities = tor_malloc_zero(sizeof(config_line_t));
-
-  MOCK(tor_gethostname,tor_gethostname_localhost);
-
-  prev_n_gethostname_localhost = n_gethostname_localhost;
-
-  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_want(retval == false);
-
-  UNMOCK(tor_gethostname);
 
  done:
-  config_free_lines(options->Address);
-  tor_free(options->DirAuthorities);
   or_options_free(options);
-  tor_free(hostname_out);
 
   UNMOCK(tor_gethostname);
   UNMOCK(tor_addr_lookup);
-  UNMOCK(get_interface_address);
   UNMOCK(get_interface_address6);
-  UNMOCK(tor_gethostname);
 }
 
 static void
@@ -6851,6 +6414,9 @@ test_config_getinfo_config_names(void *arg)
 #define CONFIG_TEST(name, flags)                          \
   { #name, test_config_ ## name, flags, NULL, NULL }
 
+#define CONFIG_TEST_SETUP(suffix, name, flags, setup, setup_data) \
+  { #name#suffix, test_config_ ## name, flags, setup, setup_data }
+
 struct testcase_t config_tests[] = {
   CONFIG_TEST(adding_trusted_dir_server, TT_FORK),
   CONFIG_TEST(adding_fallback_dir_server, TT_FORK),
@@ -6861,8 +6427,10 @@ 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(find_my_address_v4, TT_FORK),
-  CONFIG_TEST(find_my_address_v6, TT_FORK),
+  CONFIG_TEST_SETUP(_v4, find_my_address, TT_FORK,
+                    &passthrough_setup, &addr_param_v4),
+  CONFIG_TEST_SETUP(_v6, find_my_address, TT_FORK,
+                    &passthrough_setup, &addr_param_v6),
   CONFIG_TEST(find_my_address_mixed, TT_FORK),
   CONFIG_TEST(addressmap, 0),
   CONFIG_TEST(parse_bridge_line, 0),





More information about the tor-commits mailing list