From faba6909f80da729a1238d12588bb746aae474c9 Mon Sep 17 00:00:00 2001 From: David Goulet Date: Tue, 30 Jun 2020 09:56:14 -0400 Subject: [PATCH 1/6] test: Rename function to be IPv4 specific Signed-off-by: David Goulet --- src/test/test_config.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/test_config.c b/src/test/test_config.c index 7b7c9c6216..fe102eea9c 100644 --- a/src/test/test_config.c +++ b/src/test/test_config.c @@ -1170,7 +1170,7 @@ get_interface_address6_failure(int severity, sa_family_t family, } static void -test_config_find_my_address(void *arg) +test_config_find_my_address_v4(void *arg) { or_options_t *options; tor_addr_t resolved_addr, test_addr; @@ -6245,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(find_my_address, TT_FORK), + CONFIG_TEST(find_my_address_v4, TT_FORK), CONFIG_TEST(addressmap, 0), CONFIG_TEST(parse_bridge_line, 0), CONFIG_TEST(parse_transport_options_line, 0), From fe6b049651c3b3e968e339756e621815f4ae702c Mon Sep 17 00:00:00 2001 From: David Goulet Date: Tue, 30 Jun 2020 10:22:01 -0400 Subject: [PATCH 2/6] test: Unit tests for find_my_address() IPv6 Signed-off-by: David Goulet --- src/test/test_config.c | 415 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 415 insertions(+) diff --git a/src/test/test_config.c b/src/test/test_config.c index fe102eea9c..6161456670 100644 --- a/src/test/test_config.c +++ b/src/test/test_config.c @@ -1004,7 +1004,13 @@ tor_addr_lookup_01010101(const char *name, uint16_t family, tor_addr_t *addr) if (name && addr) { tor_addr_from_ipv4h(addr, 0x01010101); } + } else if (family == AF_INET6) { + if (name && addr) { + int ret = tor_addr_parse(addr, "0101::0101"); + tt_int_op(ret, OP_EQ, AF_INET6); + } } + done: return 0; } @@ -1024,7 +1030,13 @@ tor_addr_lookup_localhost(const char *name, uint16_t family, tor_addr_t *addr) if (name && addr) { tor_addr_from_ipv4h(addr, 0x7f000001); } + } else if (family == AF_INET6) { + if (name && addr) { + int ret = tor_addr_parse(addr, "::1"); + tt_int_op(ret, OP_EQ, AF_INET6); + } } + done: return 0; } @@ -1119,7 +1131,13 @@ get_interface_address6_08080808(int severity, sa_family_t family, if (addr) { tor_addr_from_ipv4h(addr, 0x08080808); } + } else if (family == AF_INET6) { + if (addr) { + int ret = tor_addr_parse(addr, "0808::0808"); + tt_int_op(ret, OP_EQ, AF_INET6); + } } + done: return 0; } @@ -1169,6 +1187,402 @@ get_interface_address6_failure(int severity, sa_family_t family, return -1; } +static void +test_config_find_my_address_v6(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 IPv6 address string, we want + * the corresponding address to be parsed and returned. + */ + 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"); + + retval = find_my_address(options, AF_INET6, 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, "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); + + 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, + &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); + 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_INET6, 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, "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, + &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); + + prev_n_get_interface_address6_failure = n_get_interface_address6_failure; + prev_n_gethostname_replacement = n_gethostname_replacement; + + retval = find_my_address(options, AF_INET6, 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_INET6,.) 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_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; + + retval = find_my_address(options, AF_INET6, 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); + + 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_INET6, 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 + * + * We want resolve_my_addr() to succeed with method "INTERFACE" + * and address from step 6. + */ + + config_free_lines(options->Address); + options->Address = NULL; + tor_addr_parse(&test_addr, "0808::0808"); + + 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, + &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. + */ + + 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_INET6, 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_INET6, 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 test_config_find_my_address_v4(void *arg) { @@ -6246,6 +6660,7 @@ struct testcase_t config_tests[] = { 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(addressmap, 0), CONFIG_TEST(parse_bridge_line, 0), CONFIG_TEST(parse_transport_options_line, 0), From 89c26f4cfd03fa9d26caa99ecf24258e25fc56ea Mon Sep 17 00:00:00 2001 From: David Goulet Date: Tue, 30 Jun 2020 12:04:18 -0400 Subject: [PATCH 3/6] test: Mixed unit tests for find_my_address() This unit tests validates the use of IPv4 _and_ IPv6 together as in multiple option Address lines both addresses and hostnames. Closes #33235 Signed-off-by: David Goulet --- src/test/test_config.c | 203 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 203 insertions(+) diff --git a/src/test/test_config.c b/src/test/test_config.c index 6161456670..84b18c90ed 100644 --- a/src/test/test_config.c +++ b/src/test/test_config.c @@ -1058,6 +1058,46 @@ tor_addr_lookup_failure(const char *name, uint16_t family, tor_addr_t *addr) return -1; } +/** Mock function for tor_addr_lookup(). + * + * Depending on the given hostname and family, resolve either to IPv4 or IPv6. + * + * If the requested hostname family is not the same as the family provided, an + * error is returned. + * + * Possible hostnames: + * - www.torproject.org.v4 for IPv4 -> 1.1.1.1 + * - www.torproject.org.v6 for IPv6 -> [0101::0101] + */ +static int +tor_addr_lookup_mixed(const char *name, uint16_t family, tor_addr_t *addr) +{ + tt_assert(addr); + tt_assert(name); + + if (!strcmp(name, "www.torproject.org.v4")) { + if (family == AF_INET) { + tor_addr_from_ipv4h(addr, 0x01010101); + return 0; + } + /* Resolving AF_INET but the asked family is different. Failure. */ + return -1; + } + + if (!strcmp(name, "www.torproject.org.v6")) { + if (family == AF_INET6) { + int ret = tor_addr_parse(addr, "0101::0101"); + tt_int_op(ret, OP_EQ, AF_INET6); + return 0; + } + /* Resolving AF_INET6 but the asked family is not. Failure. */ + return -1; + } + + done: + return 0; +} + static int n_gethostname_replacement = 0; /** This mock function is meant to replace tor_gethostname(). It @@ -1187,6 +1227,168 @@ get_interface_address6_failure(int severity, sa_family_t family, return -1; } +/** Helper macro: to validate the returned value from find_my_address() so we + * don't copy those all the time. */ +#undef VALIDATE_FOUND_ADDRESS +#define VALIDATE_FOUND_ADDRESS(ret, method, hostname) \ + do { \ + tt_int_op(retval, OP_EQ, ret); \ + if (method == NULL) tt_assert(!method_used); \ + else tt_str_op(method_used, OP_EQ, method); \ + if (hostname == NULL) tt_assert(!hostname_out); \ + else tt_str_op(hostname_out, OP_EQ, hostname); \ + if (ret == true) { \ + tt_assert(tor_addr_eq(&resolved_addr, &test_addr)); \ + } \ + } while (0) + +/** Helper macro: Cleanup the address and variables used after a + * find_my_address() call. */ +#undef CLEANUP_FOUND_ADDRESS +#define CLEANUP_FOUND_ADDRESS \ + do { \ + config_free_lines(options->Address); \ + tor_free(options->DirAuthorities); \ + tor_free(hostname_out); \ + tor_addr_make_unspec(&resolved_addr); \ + tor_addr_make_unspec(&test_addr); \ + } while (0) + +/** Test both IPv4 and IPv6 coexisting together in the configuration. */ +static void +test_config_find_my_address_mixed(void *arg) +{ + or_options_t *options; + tor_addr_t resolved_addr, test_addr; + const char *method_used; + char *hostname_out = NULL; + bool retval; + + (void)arg; + + options = options_new(); + + options_init(options); + + /* + * CASE 1: Only IPv6 address. Accepted. + */ + 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"); + + /* IPv4 address not guessed since one Address statement exists. */ + retval = find_my_address(options, AF_INET, LOG_NOTICE, &resolved_addr, + &method_used, &hostname_out); + VALIDATE_FOUND_ADDRESS(false, NULL, NULL); + /* IPv6 address should be found and considered configured. */ + retval = find_my_address(options, AF_INET6, LOG_NOTICE, &resolved_addr, + &method_used, &hostname_out); + VALIDATE_FOUND_ADDRESS(true, "CONFIGURED", NULL); + + CLEANUP_FOUND_ADDRESS; + + /* + * Case 2: IPv4 _and_ IPv6 given. Accepted. + */ + config_line_append(&options->Address, "Address", + "2a01:4f8:fff0:4f:266:37ff:fe2c:5d19"); + config_line_append(&options->Address, "Address", "1.1.1.1"); + tor_addr_parse(&test_addr, "1.1.1.1"); + + /* IPv4 address should be found and considered configured. */ + retval = find_my_address(options, AF_INET, LOG_NOTICE, &resolved_addr, + &method_used, &hostname_out); + VALIDATE_FOUND_ADDRESS(true, "CONFIGURED", NULL); + + /* IPv6 address should be found and considered configured. */ + tor_addr_parse(&test_addr, "2a01:4f8:fff0:4f:266:37ff:fe2c:5d19"); + retval = find_my_address(options, AF_INET6, LOG_NOTICE, &resolved_addr, + &method_used, &hostname_out); + VALIDATE_FOUND_ADDRESS(true, "CONFIGURED", NULL); + + CLEANUP_FOUND_ADDRESS; + + /* + * Case 3: Two hostnames, IPv4 and IPv6. + */ + config_line_append(&options->Address, "Address", "www.torproject.org.v4"); + config_line_append(&options->Address, "Address", "www.torproject.org.v6"); + + /* Looks at specific hostname to learn which address family to use. */ + MOCK(tor_addr_lookup, tor_addr_lookup_mixed); + + /* IPv4 address should be found and considered resolved. */ + tor_addr_parse(&test_addr, "1.1.1.1"); + retval = find_my_address(options, AF_INET, LOG_NOTICE, &resolved_addr, + &method_used, &hostname_out); + VALIDATE_FOUND_ADDRESS(true, "RESOLVED", "www.torproject.org.v4"); + + /* IPv6 address should be found and considered resolved. */ + tor_addr_parse(&test_addr, "0101::0101"); + retval = find_my_address(options, AF_INET6, LOG_NOTICE, &resolved_addr, + &method_used, &hostname_out); + VALIDATE_FOUND_ADDRESS(true, "RESOLVED", "www.torproject.org.v6"); + + CLEANUP_FOUND_ADDRESS; + UNMOCK(tor_addr_lookup); + + /* + * Case 4: IPv4 address and a hostname resolving to IPV6. + */ + config_line_append(&options->Address, "Address", "1.1.1.1"); + config_line_append(&options->Address, "Address", "www.torproject.org.v6"); + + /* Looks at specific hostname to learn which address family to use. */ + MOCK(tor_addr_lookup, tor_addr_lookup_mixed); + + /* IPv4 address should be found and configured. */ + tor_addr_parse(&test_addr, "1.1.1.1"); + retval = find_my_address(options, AF_INET, LOG_NOTICE, &resolved_addr, + &method_used, &hostname_out); + VALIDATE_FOUND_ADDRESS(true, "CONFIGURED", NULL); + + /* IPv6 address should be found and considered resolved. */ + tor_addr_parse(&test_addr, "0101::0101"); + retval = find_my_address(options, AF_INET6, LOG_NOTICE, &resolved_addr, + &method_used, &hostname_out); + VALIDATE_FOUND_ADDRESS(true, "RESOLVED", "www.torproject.org.v6"); + + CLEANUP_FOUND_ADDRESS; + UNMOCK(tor_addr_lookup); + + /* + * Case 5: Hostname resolving to IPv4 and an IPv6 address. + */ + config_line_append(&options->Address, "Address", "0101::0101"); + config_line_append(&options->Address, "Address", "www.torproject.org.v4"); + + /* Looks at specific hostname to learn which address family to use. */ + MOCK(tor_addr_lookup, tor_addr_lookup_mixed); + + /* IPv4 address should be found and resolved. */ + tor_addr_parse(&test_addr, "1.1.1.1"); + retval = find_my_address(options, AF_INET, LOG_NOTICE, &resolved_addr, + &method_used, &hostname_out); + VALIDATE_FOUND_ADDRESS(true, "RESOLVED", "www.torproject.org.v4"); + + /* IPv6 address should be found and considered resolved. */ + tor_addr_parse(&test_addr, "0101::0101"); + retval = find_my_address(options, AF_INET6, LOG_NOTICE, &resolved_addr, + &method_used, &hostname_out); + VALIDATE_FOUND_ADDRESS(true, "CONFIGURED", NULL); + + CLEANUP_FOUND_ADDRESS; + UNMOCK(tor_addr_lookup); + + done: + config_free_lines(options->Address); + or_options_free(options); + tor_free(hostname_out); + + UNMOCK(tor_addr_lookup); +} + static void test_config_find_my_address_v6(void *arg) { @@ -6661,6 +6863,7 @@ struct testcase_t config_tests[] = { CONFIG_TEST(default_fallback_dirs, 0), CONFIG_TEST(find_my_address_v4, TT_FORK), CONFIG_TEST(find_my_address_v6, TT_FORK), + CONFIG_TEST(find_my_address_mixed, TT_FORK), CONFIG_TEST(addressmap, 0), CONFIG_TEST(parse_bridge_line, 0), CONFIG_TEST(parse_transport_options_line, 0), From a8b8a1ee99fa843a5937b4d2a5973c444a4311e0 Mon Sep 17 00:00:00 2001 From: David Goulet Date: Tue, 30 Jun 2020 12:05:56 -0400 Subject: [PATCH 4/6] addr: Only attempt Address resolution on non parsable lines In get_address_from_config(), we would attempt to resolve an Address line that is not from the requested family but that line could be a valid address from another family (v4 vs v6). This makes it that we don't attempt to resolve a valid address from another family. Found with unit test config/find_my_address_mixed. Fixed in #33235 Related to #33233 Signed-off-by: David Goulet --- src/app/config/resolve_addr.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/app/config/resolve_addr.c b/src/app/config/resolve_addr.c index c8b44de845..61662c340f 100644 --- a/src/app/config/resolve_addr.c +++ b/src/app/config/resolve_addr.c @@ -193,6 +193,10 @@ get_address_from_config(const or_options_t *options, int warn_severity, explicit_ip = true; num_valid_addr++; continue; + } else if (af != -1) { + /* Parsable address but just not the one from the family we want. Skip + * it so we don't attempt a resolve. */ + continue; } /* Not an IP address. Considering this value a hostname and attempting to From 6f1423cb57b35525babb08fe317df126f7363b46 Mon Sep 17 00:00:00 2001 From: David Goulet Date: Tue, 30 Jun 2020 12:12:04 -0400 Subject: [PATCH 5/6] addr: Continue trying all Address options on resolve failure When going over all Address lines from the configuration, continue to attempt resolving other lines if one fails. Before that, we would bail right away and never noticed the other Address lines. Fixed in #33235 Related to #33233 Signed-off-by: David Goulet --- src/app/config/resolve_addr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/config/resolve_addr.c b/src/app/config/resolve_addr.c index 61662c340f..9c208efea3 100644 --- a/src/app/config/resolve_addr.c +++ b/src/app/config/resolve_addr.c @@ -212,7 +212,7 @@ get_address_from_config(const or_options_t *options, int warn_severity, /* 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; + continue; } } From b14b1f2b1d92b7f0e605c312fc59c42a48dea7dd Mon Sep 17 00:00:00 2001 From: David Goulet Date: Tue, 30 Jun 2020 12:13:23 -0400 Subject: [PATCH 6/6] addr: Don't guess our address if no Address lines are valid If at least one Address line is given but invalid, we should not attempt to guess our address. This commit sends back the "bail" signal so find_my_address() can return an error if the requested family doesn't exists but still an Address line is found which is likely another family. Fixed in #33235 Related to #33233 Signed-off-by: David Goulet --- src/app/config/resolve_addr.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/app/config/resolve_addr.c b/src/app/config/resolve_addr.c index 9c208efea3..0070cf7ecf 100644 --- a/src/app/config/resolve_addr.c +++ b/src/app/config/resolve_addr.c @@ -220,8 +220,10 @@ get_address_from_config(const or_options_t *options, int warn_severity, 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; + /* No Address statement for family but one exists since Address is not + * NULL thus we have to stop now and not attempt to send back a guessed + * address. */ + return FN_RET_BAIL; } if (num_valid_addr >= MAX_CONFIG_ADDRESS) {