aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGeorge Kadianakis <desnacked@riseup.net>2019-05-10 12:49:01 +0300
committerGeorge Kadianakis <desnacked@riseup.net>2019-05-10 12:49:01 +0300
commit501d1ae0bd4638730c1ba050c9ddb47d7c438280 (patch)
tree802034b41b2566c448d02f4593913de5bc1b127e
parent3885e7b44b1df421aa062ba2598f1731e4e3e3a5 (diff)
parentde91b838496a3646721608bdb7740696601f57c4 (diff)
downloadtor-501d1ae0bd4638730c1ba050c9ddb47d7c438280.tar.gz
tor-501d1ae0bd4638730c1ba050c9ddb47d7c438280.zip
Merge branch 'tor-github/pr/973'
-rw-r--r--changes/bug235885
-rw-r--r--scripts/maint/practracker/exceptions.txt2
-rw-r--r--src/core/or/policies.c78
-rw-r--r--src/core/or/policies.h2
-rw-r--r--src/feature/hs/hs_common.c90
-rw-r--r--src/feature/hs/hs_config.c9
-rw-r--r--src/test/hs_test_helpers.c3
-rw-r--r--src/test/test_hs_client.c11
-rw-r--r--src/test/test_policy.c235
9 files changed, 376 insertions, 59 deletions
diff --git a/changes/bug23588 b/changes/bug23588
new file mode 100644
index 0000000000..86064ab313
--- /dev/null
+++ b/changes/bug23588
@@ -0,0 +1,5 @@
+ o Minor bugfixes (v3 onion services):
+ - Stop ignoring IPv6 link specifiers sent to v3 onion services.
+ v3 onion service IPv6 support is still incomplete, see 23493 for
+ details. Fixes bug 23588; bugfix on 0.3.2.1-alpha.
+ Patch by Neel Chauhan.
diff --git a/scripts/maint/practracker/exceptions.txt b/scripts/maint/practracker/exceptions.txt
index 70176ad896..efd903e68c 100644
--- a/scripts/maint/practracker/exceptions.txt
+++ b/scripts/maint/practracker/exceptions.txt
@@ -117,7 +117,7 @@ problem include-count /src/core/or/connection_or.c 51
problem function-size /src/core/or/connection_or.c:connection_or_group_set_badness_() 105
problem function-size /src/core/or/connection_or.c:connection_or_client_learned_peer_id() 144
problem function-size /src/core/or/connection_or.c:connection_or_compute_authenticate_cell_body() 235
-problem file-size /src/core/or/policies.c 3171
+problem file-size /src/core/or/policies.c 3249
problem function-size /src/core/or/policies.c:policy_summarize() 107
problem function-size /src/core/or/protover.c:protover_all_supported() 117
problem file-size /src/core/or/relay.c 3173
diff --git a/src/core/or/policies.c b/src/core/or/policies.c
index f59894ea8f..83d9a53fc0 100644
--- a/src/core/or/policies.c
+++ b/src/core/or/policies.c
@@ -31,6 +31,7 @@
#include "ht.h"
#include "lib/crypt_ops/crypto_rand.h"
#include "lib/encoding/confline.h"
+#include "trunnel/ed25519_cert.h"
#include "core/or/addr_policy_st.h"
#include "feature/dirclient/dir_server_st.h"
@@ -1015,6 +1016,83 @@ fascist_firewall_choose_address_rs(const routerstatus_t *rs,
}
}
+/** Like fascist_firewall_choose_address_base(), but takes in a smartlist
+ * <b>lspecs</b> consisting of one or more link specifiers. We assume
+ * fw_connection is FIREWALL_OR_CONNECTION as link specifiers cannot
+ * contain DirPorts.
+ */
+void
+fascist_firewall_choose_address_ls(const smartlist_t *lspecs,
+ int pref_only, tor_addr_port_t* ap)
+{
+ int have_v4 = 0, have_v6 = 0;
+ uint16_t port_v4 = 0, port_v6 = 0;
+ tor_addr_t addr_v4, addr_v6;
+
+ tor_assert(ap);
+
+ if (lspecs == NULL) {
+ log_warn(LD_BUG, "Unknown or missing link specifiers");
+ return;
+ }
+ if (smartlist_len(lspecs) == 0) {
+ log_warn(LD_PROTOCOL, "Link specifiers are empty");
+ return;
+ }
+
+ tor_addr_make_null(&ap->addr, AF_UNSPEC);
+ ap->port = 0;
+
+ tor_addr_make_null(&addr_v4, AF_INET);
+ tor_addr_make_null(&addr_v6, AF_INET6);
+
+ SMARTLIST_FOREACH_BEGIN(lspecs, const link_specifier_t *, ls) {
+ switch (link_specifier_get_ls_type(ls)) {
+ case LS_IPV4:
+ /* Skip if we already seen a v4. */
+ if (have_v4) continue;
+ tor_addr_from_ipv4h(&addr_v4,
+ link_specifier_get_un_ipv4_addr(ls));
+ port_v4 = link_specifier_get_un_ipv4_port(ls);
+ have_v4 = 1;
+ break;
+ case LS_IPV6:
+ /* Skip if we already seen a v6, or deliberately skip it if we're not a
+ * direct connection. */
+ if (have_v6) continue;
+ tor_addr_from_ipv6_bytes(&addr_v6,
+ (const char *) link_specifier_getconstarray_un_ipv6_addr(ls));
+ port_v6 = link_specifier_get_un_ipv6_port(ls);
+ have_v6 = 1;
+ break;
+ default:
+ /* Ignore unknown. */
+ break;
+ }
+ } SMARTLIST_FOREACH_END(ls);
+
+ /* If we don't have IPv4 or IPv6 in link specifiers, log a bug and return. */
+ if (!have_v4 && !have_v6) {
+ if (!have_v6) {
+ log_warn(LD_PROTOCOL, "None of our link specifiers have IPv4 or IPv6");
+ } else {
+ log_warn(LD_PROTOCOL, "None of our link specifiers have IPv4");
+ }
+ return;
+ }
+
+ /* Here, don't check for DirPorts as link specifiers are only used for
+ * ORPorts. */
+ const or_options_t *options = get_options();
+ int pref_ipv6 = fascist_firewall_prefer_ipv6_orport(options);
+ /* Assume that the DirPorts are zero as link specifiers only use ORPorts. */
+ fascist_firewall_choose_address_base(&addr_v4, port_v4, 0,
+ &addr_v6, port_v6, 0,
+ FIREWALL_OR_CONNECTION,
+ pref_only, pref_ipv6,
+ ap);
+}
+
/** Like fascist_firewall_choose_address_base(), but takes <b>node</b>, and
* looks up the node's IPv6 preference rather than taking an argument
* for pref_ipv6. */
diff --git a/src/core/or/policies.h b/src/core/or/policies.h
index 324c1c2dd1..3c46363c04 100644
--- a/src/core/or/policies.h
+++ b/src/core/or/policies.h
@@ -92,6 +92,8 @@ int fascist_firewall_allows_dir_server(const dir_server_t *ds,
void fascist_firewall_choose_address_rs(const routerstatus_t *rs,
firewall_connection_t fw_connection,
int pref_only, tor_addr_port_t* ap);
+void fascist_firewall_choose_address_ls(const smartlist_t *lspecs,
+ int pref_only, tor_addr_port_t* ap);
void fascist_firewall_choose_address_node(const node_t *node,
firewall_connection_t fw_connection,
int pref_only, tor_addr_port_t* ap);
diff --git a/src/feature/hs/hs_common.c b/src/feature/hs/hs_common.c
index 1e4f6d9166..597409a1e9 100644
--- a/src/feature/hs/hs_common.c
+++ b/src/feature/hs/hs_common.c
@@ -1672,14 +1672,16 @@ hs_pick_hsdir(smartlist_t *responsible_dirs, const char *req_key_str,
return hs_dir;
}
-/* From a list of link specifier, an onion key and if we are requesting a
- * direct connection (ex: single onion service), return a newly allocated
- * extend_info_t object. This function always returns an extend info with
- * an IPv4 address, or NULL.
+/* Given a list of link specifiers lspecs, a curve 25519 onion_key, and
+ * a direct connection boolean direct_conn (true for single onion services),
+ * return a newly allocated extend_info_t object.
+ *
+ * This function always returns an extend info with a valid IP address and
+ * ORPort, or NULL. If direct_conn is false, the IP address is always IPv4.
*
* It performs the following checks:
- * if either IPv4 or legacy ID is missing, return NULL.
- * if direct_conn, and we can't reach the IPv4 address, return NULL.
+ * if there is no usable IP address, or legacy ID is missing, return NULL.
+ * if direct_conn, and we can't reach any IP address, return NULL.
*/
extend_info_t *
hs_get_extend_info_from_lspecs(const smartlist_t *lspecs,
@@ -1688,12 +1690,22 @@ hs_get_extend_info_from_lspecs(const smartlist_t *lspecs,
{
int have_v4 = 0, have_legacy_id = 0, have_ed25519_id = 0;
char legacy_id[DIGEST_LEN] = {0};
- uint16_t port_v4 = 0;
- tor_addr_t addr_v4;
ed25519_public_key_t ed25519_pk;
extend_info_t *info = NULL;
+ tor_addr_port_t ap;
+
+ tor_addr_make_null(&ap.addr, AF_UNSPEC);
+ ap.port = 0;
+
+ if (lspecs == NULL) {
+ log_warn(LD_BUG, "Specified link specifiers is null");
+ goto done;
+ }
- tor_assert(lspecs);
+ if (onion_key == NULL) {
+ log_warn(LD_BUG, "Specified onion key is null");
+ goto done;
+ }
if (smartlist_len(lspecs) == 0) {
log_fn(LOG_PROTOCOL_WARN, LD_REND, "Empty link specifier list.");
@@ -1704,11 +1716,14 @@ hs_get_extend_info_from_lspecs(const smartlist_t *lspecs,
SMARTLIST_FOREACH_BEGIN(lspecs, const link_specifier_t *, ls) {
switch (link_specifier_get_ls_type(ls)) {
case LS_IPV4:
- /* Skip if we already seen a v4. */
- if (have_v4) continue;
- tor_addr_from_ipv4h(&addr_v4,
+ /* Skip if we already seen a v4. If direct_conn is true, we skip this
+ * block because fascist_firewall_choose_address_ls() will set ap. If
+ * direct_conn is false, set ap to the first IPv4 address and port in
+ * the link specifiers.*/
+ if (have_v4 || direct_conn) continue;
+ tor_addr_from_ipv4h(&ap.addr,
link_specifier_get_un_ipv4_addr(ls));
- port_v4 = link_specifier_get_un_ipv4_port(ls);
+ ap.port = link_specifier_get_un_ipv4_port(ls);
have_v4 = 1;
break;
case LS_LEGACY_ID:
@@ -1732,55 +1747,38 @@ hs_get_extend_info_from_lspecs(const smartlist_t *lspecs,
}
} SMARTLIST_FOREACH_END(ls);
- /* Legacy ID is mandatory, and we require IPv4. */
- if (!have_v4 || !have_legacy_id) {
- bool both = !have_v4 && !have_legacy_id;
- log_fn(LOG_PROTOCOL_WARN, LD_REND, "Missing %s%s%s link specifier%s.",
- !have_v4 ? "IPv4" : "",
- both ? " and " : "",
- !have_legacy_id ? "legacy ID" : "",
- both ? "s" : "");
+ /* Choose a preferred address first, but fall back to an allowed address. */
+ if (direct_conn)
+ fascist_firewall_choose_address_ls(lspecs, 0, &ap);
+
+ /* Legacy ID is mandatory, and we require an IP address. */
+ if (!tor_addr_port_is_valid_ap(&ap, 0)) {
+ /* If we're missing the IP address, log a warning and return NULL. */
+ log_info(LD_NET, "Unreachable or invalid IP address in link state");
goto done;
}
-
- /* We know we have IPv4, because we just checked. */
- if (!direct_conn) {
- /* All clients can extend to any IPv4 via a 3-hop path. */
- goto validate;
- } else if (direct_conn &&
- fascist_firewall_allows_address_addr(&addr_v4, port_v4,
- FIREWALL_OR_CONNECTION,
- 0, 0)) {
- /* Direct connection and we can reach it in IPv4 so go for it. */
- goto validate;
-
- /* We will add support for falling back to a 3-hop path in a later
- * release. */
- } else {
- /* If we can't reach IPv4, return NULL. */
- log_fn(LOG_PROTOCOL_WARN, LD_REND,
- "Received an IPv4 link specifier, "
- "but the address is not reachable: %s:%u",
- fmt_addr(&addr_v4), port_v4);
+ if (!have_legacy_id) {
+ /* If we're missing the legacy ID, log a warning and return NULL. */
+ log_warn(LD_PROTOCOL, "Missing Legacy ID in link state");
goto done;
}
- /* We will add support for IPv6 in a later release. */
+ /* We will add support for falling back to a 3-hop path in a later
+ * release. */
- validate:
/* We'll validate now that the address we've picked isn't a private one. If
* it is, are we allowed to extend to private addresses? */
- if (!extend_info_addr_is_allowed(&addr_v4)) {
+ if (!extend_info_addr_is_allowed(&ap.addr)) {
log_fn(LOG_PROTOCOL_WARN, LD_REND,
"Requested address is private and we are not allowed to extend to "
- "it: %s:%u", fmt_addr(&addr_v4), port_v4);
+ "it: %s:%u", fmt_addr(&ap.addr), ap.port);
goto done;
}
/* We do have everything for which we think we can connect successfully. */
info = extend_info_new(NULL, legacy_id,
(have_ed25519_id) ? &ed25519_pk : NULL, NULL,
- onion_key, &addr_v4, port_v4);
+ onion_key, &ap.addr, ap.port);
done:
return info;
}
diff --git a/src/feature/hs/hs_config.c b/src/feature/hs/hs_config.c
index ee4499ef5b..87f6257591 100644
--- a/src/feature/hs/hs_config.c
+++ b/src/feature/hs/hs_config.c
@@ -496,15 +496,6 @@ config_generic_service(const config_line_t *line_,
* becomes a single onion service. */
if (rend_service_non_anonymous_mode_enabled(options)) {
config->is_single_onion = 1;
- /* We will add support for IPv6-only v3 single onion services in a future
- * Tor version. This won't catch "ReachableAddresses reject *4", but that
- * option doesn't work anyway. */
- if (options->ClientUseIPv4 == 0 && config->version == HS_VERSION_THREE) {
- log_warn(LD_CONFIG, "IPv6-only v3 single onion services are not "
- "supported. Set HiddenServiceSingleHopMode 0 and "
- "HiddenServiceNonAnonymousMode 0, or set ClientUseIPv4 1.");
- goto err;
- }
}
/* Success */
diff --git a/src/test/hs_test_helpers.c b/src/test/hs_test_helpers.c
index c57bdc730b..0a21fe576b 100644
--- a/src/test/hs_test_helpers.c
+++ b/src/test/hs_test_helpers.c
@@ -162,11 +162,8 @@ hs_helper_build_hs_desc_impl(unsigned int no_ip,
/* Add four intro points. */
smartlist_add(desc->encrypted_data.intro_points,
hs_helper_build_intro_point(signing_kp, now, "1.2.3.4", 0));
-/* IPv6-only introduction points are not supported yet, see #23588 */
-#if 0
smartlist_add(desc->encrypted_data.intro_points,
hs_helper_build_intro_point(signing_kp, now, "[2600::1]", 0));
-#endif
smartlist_add(desc->encrypted_data.intro_points,
hs_helper_build_intro_point(signing_kp, now, "3.2.1.4", 1));
smartlist_add(desc->encrypted_data.intro_points,
diff --git a/src/test/test_hs_client.c b/src/test/test_hs_client.c
index f8300c6d41..0d25a98bb3 100644
--- a/src/test/test_hs_client.c
+++ b/src/test/test_hs_client.c
@@ -485,6 +485,17 @@ test_client_pick_intro(void *arg)
SMARTLIST_FOREACH_BEGIN(desc->encrypted_data.intro_points,
hs_desc_intro_point_t *, ip) {
extend_info_t *intro_ei = desc_intro_point_to_extend_info(ip);
+ /* desc_intro_point_to_extend_info() doesn't return IPv6 intro points
+ * yet, because we can't extend to them. See #24404, #24451, and #24181.
+ */
+ if (intro_ei == NULL) {
+ /* Pretend we're making a direct connection, and that we can use IPv6
+ */
+ get_options_mutable()->ClientUseIPv6 = 1;
+ intro_ei = hs_get_extend_info_from_lspecs(ip->link_specifiers,
+ &ip->onion_key, 1);
+ tt_assert(tor_addr_family(&intro_ei->addr) == AF_INET6);
+ }
tt_assert(intro_ei);
if (intro_ei) {
const char *ptr;
diff --git a/src/test/test_policy.c b/src/test/test_policy.c
index 46d4a1b94a..e58bb3d174 100644
--- a/src/test/test_policy.c
+++ b/src/test/test_policy.c
@@ -6,13 +6,18 @@
#include "core/or/or.h"
#include "app/config/config.h"
+#include "core/or/circuitbuild.h"
#include "core/or/policies.h"
#include "feature/dirparse/policy_parse.h"
+#include "feature/hs/hs_common.h"
+#include "feature/hs/hs_descriptor.h"
#include "feature/relay/router.h"
#include "lib/encoding/confline.h"
#include "test/test.h"
+#include "test/log_test_helpers.h"
#include "core/or/addr_policy_st.h"
+#include "core/or/extend_info_st.h"
#include "core/or/port_cfg_st.h"
#include "feature/nodelist/node_st.h"
#include "feature/nodelist/routerinfo_st.h"
@@ -2024,6 +2029,101 @@ test_policies_fascist_firewall_allows_address(void *arg)
expect_ap); \
STMT_END
+/* Check that fascist_firewall_choose_address_ls() returns the expected
+ * results. */
+#define CHECK_CHOSEN_ADDR_NULL_LS() \
+ STMT_BEGIN \
+ tor_addr_port_t chosen_ls_ap; \
+ tor_addr_make_null(&chosen_ls_ap.addr, AF_UNSPEC); \
+ chosen_ls_ap.port = 0; \
+ setup_full_capture_of_logs(LOG_WARN); \
+ fascist_firewall_choose_address_ls(NULL, 1, &chosen_ls_ap); \
+ expect_single_log_msg("Unknown or missing link specifiers"); \
+ teardown_capture_of_logs(); \
+ STMT_END
+
+#define CHECK_CHOSEN_ADDR_LS(fake_ls, pref_only, expect_rv, expect_ap) \
+ STMT_BEGIN \
+ tor_addr_port_t chosen_ls_ap; \
+ tor_addr_make_null(&chosen_ls_ap.addr, AF_UNSPEC); \
+ chosen_ls_ap.port = 0; \
+ setup_full_capture_of_logs(LOG_WARN); \
+ fascist_firewall_choose_address_ls(fake_ls, pref_only, &chosen_ls_ap); \
+ if (smartlist_len(fake_ls) == 0) { \
+ expect_single_log_msg("Link specifiers are empty"); \
+ } else { \
+ expect_no_log_entry(); \
+ tt_assert(tor_addr_eq(&(expect_ap).addr, &chosen_ls_ap.addr)); \
+ tt_int_op((expect_ap).port, OP_EQ, chosen_ls_ap.port); \
+ } \
+ teardown_capture_of_logs(); \
+ STMT_END
+
+#define CHECK_LS_LEGACY_ONLY(fake_ls) \
+ STMT_BEGIN \
+ tor_addr_port_t chosen_ls_ap; \
+ tor_addr_make_null(&chosen_ls_ap.addr, AF_UNSPEC); \
+ chosen_ls_ap.port = 0; \
+ setup_full_capture_of_logs(LOG_WARN); \
+ fascist_firewall_choose_address_ls(fake_ls, 0, &chosen_ls_ap); \
+ expect_single_log_msg("None of our link specifiers have IPv4 or IPv6"); \
+ teardown_capture_of_logs(); \
+ STMT_END
+
+#define CHECK_HS_EXTEND_INFO_ADDR_LS(fake_ls, direct_conn, expect_ap) \
+ STMT_BEGIN \
+ curve25519_secret_key_t seckey; \
+ curve25519_secret_key_generate(&seckey, 0); \
+ curve25519_public_key_t pubkey; \
+ curve25519_public_key_generate(&pubkey, &seckey); \
+ setup_full_capture_of_logs(LOG_WARN); \
+ extend_info_t *ei = hs_get_extend_info_from_lspecs(fake_ls, &pubkey, \
+ direct_conn); \
+ if (fake_ls == NULL) { \
+ tt_ptr_op(ei, OP_EQ, NULL); \
+ expect_single_log_msg("Specified link specifiers is null"); \
+ } else { \
+ expect_no_log_entry(); \
+ tt_assert(tor_addr_eq(&(expect_ap).addr, &ei->addr)); \
+ tt_int_op((expect_ap).port, OP_EQ, ei->port); \
+ extend_info_free(ei); \
+ } \
+ teardown_capture_of_logs(); \
+ STMT_END
+
+#define CHECK_HS_EXTEND_INFO_ADDR_LS_NULL_KEY(fake_ls) \
+ STMT_BEGIN \
+ setup_full_capture_of_logs(LOG_WARN); \
+ extend_info_t *ei = hs_get_extend_info_from_lspecs(fake_ls, NULL, 0); \
+ tt_ptr_op(ei, OP_EQ, NULL); \
+ expect_single_log_msg("Specified onion key is null"); \
+ teardown_capture_of_logs(); \
+ STMT_END
+
+#define CHECK_HS_EXTEND_INFO_ADDR_LS_EXPECT_NULL(fake_ls, direct_conn) \
+ STMT_BEGIN \
+ curve25519_secret_key_t seckey; \
+ curve25519_secret_key_generate(&seckey, 0); \
+ curve25519_public_key_t pubkey; \
+ curve25519_public_key_generate(&pubkey, &seckey); \
+ extend_info_t *ei = hs_get_extend_info_from_lspecs(fake_ls, &pubkey, \
+ direct_conn); \
+ tt_ptr_op(ei, OP_EQ, NULL); \
+ STMT_END
+
+#define CHECK_HS_EXTEND_INFO_ADDR_LS_EXPECT_MSG(fake_ls, msg_level, msg) \
+ STMT_BEGIN \
+ curve25519_secret_key_t seckey; \
+ curve25519_secret_key_generate(&seckey, 0); \
+ curve25519_public_key_t pubkey; \
+ curve25519_public_key_generate(&pubkey, &seckey); \
+ setup_full_capture_of_logs(msg_level); \
+ extend_info_t *ei = hs_get_extend_info_from_lspecs(fake_ls, &pubkey, 0); \
+ tt_ptr_op(ei, OP_EQ, NULL); \
+ expect_single_log_msg(msg); \
+ teardown_capture_of_logs(); \
+ STMT_END
+
/** Mock the preferred address function to return zero (prefer IPv4). */
static int
mock_fascist_firewall_rand_prefer_ipv6_addr_use_ipv4(void)
@@ -2472,6 +2572,141 @@ test_policies_fascist_firewall_choose_address(void *arg)
UNMOCK(fascist_firewall_rand_prefer_ipv6_addr);
+ /* Test firewall_choose_address_ls(). To do this, we make a fake link
+ * specifier. */
+ smartlist_t *lspecs = smartlist_new(),
+ *lspecs_blank = smartlist_new(),
+ *lspecs_v4 = smartlist_new(),
+ *lspecs_v6 = smartlist_new(),
+ *lspecs_no_legacy = smartlist_new(),
+ *lspecs_legacy_only = smartlist_new();
+ link_specifier_t *fake_ls;
+
+ /* IPv4 link specifier */
+ fake_ls = link_specifier_new();
+ link_specifier_set_ls_type(fake_ls, LS_IPV4);
+ link_specifier_set_un_ipv4_addr(fake_ls,
+ tor_addr_to_ipv4h(&ipv4_or_ap.addr));
+ link_specifier_set_un_ipv4_port(fake_ls, ipv4_or_ap.port);
+ link_specifier_set_ls_len(fake_ls, sizeof(ipv4_or_ap.addr.addr.in_addr) +
+ sizeof(ipv4_or_ap.port));
+ smartlist_add(lspecs, fake_ls);
+ smartlist_add(lspecs_v4, fake_ls);
+ smartlist_add(lspecs_no_legacy, fake_ls);
+
+ /* IPv6 link specifier */
+ fake_ls = link_specifier_new();
+ link_specifier_set_ls_type(fake_ls, LS_IPV6);
+ size_t addr_len = link_specifier_getlen_un_ipv6_addr(fake_ls);
+ const uint8_t *in6_addr = tor_addr_to_in6_addr8(&ipv6_or_ap.addr);
+ uint8_t *ipv6_array = link_specifier_getarray_un_ipv6_addr(fake_ls);
+ memcpy(ipv6_array, in6_addr, addr_len);
+ link_specifier_set_un_ipv6_port(fake_ls, ipv6_or_ap.port);
+ link_specifier_set_ls_len(fake_ls, addr_len + sizeof(ipv6_or_ap.port));
+ smartlist_add(lspecs, fake_ls);
+ smartlist_add(lspecs_v6, fake_ls);
+
+ /* Legacy ID link specifier */
+ fake_ls = link_specifier_new();
+ link_specifier_set_ls_type(fake_ls, LS_LEGACY_ID);
+ uint8_t *legacy_id = link_specifier_getarray_un_legacy_id(fake_ls);
+ memset(legacy_id, 'A', sizeof(*legacy_id));
+ link_specifier_set_ls_len(fake_ls,
+ link_specifier_getlen_un_legacy_id(fake_ls));
+ smartlist_add(lspecs, fake_ls);
+ smartlist_add(lspecs_legacy_only, fake_ls);
+ smartlist_add(lspecs_v4, fake_ls);
+ smartlist_add(lspecs_v6, fake_ls);
+
+ /* Check with bogus requests. */
+ tor_addr_port_t null_ap; \
+ tor_addr_make_null(&null_ap.addr, AF_UNSPEC); \
+ null_ap.port = 0; \
+
+ /* Check for a null link state. */
+ CHECK_CHOSEN_ADDR_NULL_LS();
+ CHECK_HS_EXTEND_INFO_ADDR_LS(NULL, 1, null_ap);
+
+ /* Check for a blank link state. */
+ CHECK_CHOSEN_ADDR_LS(lspecs_blank, 0, 0, null_ap);
+ CHECK_HS_EXTEND_INFO_ADDR_LS_EXPECT_NULL(lspecs_blank, 0);
+
+ /* Check for a link state with only a Legacy ID. */
+ CHECK_LS_LEGACY_ONLY(lspecs_legacy_only);
+ CHECK_HS_EXTEND_INFO_ADDR_LS_EXPECT_NULL(lspecs_legacy_only, 0);
+ smartlist_free(lspecs_legacy_only);
+
+ /* Check with a null onion_key. */
+ CHECK_HS_EXTEND_INFO_ADDR_LS_NULL_KEY(lspecs_blank);
+ smartlist_free(lspecs_blank);
+
+ /* Check with a null onion_key. */
+ CHECK_HS_EXTEND_INFO_ADDR_LS_EXPECT_MSG(lspecs_no_legacy, LOG_WARN,
+ "Missing Legacy ID in link state");
+ smartlist_free(lspecs_no_legacy);
+
+ /* Enable both IPv4 and IPv6. */
+ memset(&mock_options, 0, sizeof(or_options_t));
+ mock_options.ClientUseIPv4 = 1;
+ mock_options.ClientUseIPv6 = 1;
+
+ /* Prefer IPv4, enable both IPv4 and IPv6. */
+ mock_options.ClientPreferIPv6ORPort = 0;
+
+ CHECK_CHOSEN_ADDR_LS(lspecs, 0, 1, ipv4_or_ap);
+ CHECK_CHOSEN_ADDR_LS(lspecs, 1, 1, ipv4_or_ap);
+
+ CHECK_HS_EXTEND_INFO_ADDR_LS(lspecs, 1, ipv4_or_ap);
+ CHECK_HS_EXTEND_INFO_ADDR_LS(lspecs, 0, ipv4_or_ap);
+
+ /* Prefer IPv6, enable both IPv4 and IPv6. */
+ mock_options.ClientPreferIPv6ORPort = 1;
+
+ CHECK_CHOSEN_ADDR_LS(lspecs, 0, 1, ipv6_or_ap);
+ CHECK_CHOSEN_ADDR_LS(lspecs, 1, 1, ipv6_or_ap);
+
+ CHECK_HS_EXTEND_INFO_ADDR_LS(lspecs, 1, ipv6_or_ap);
+ CHECK_HS_EXTEND_INFO_ADDR_LS(lspecs, 0, ipv4_or_ap);
+
+ /* IPv4-only. */
+ memset(&mock_options, 0, sizeof(or_options_t));
+ mock_options.ClientUseIPv4 = 1;
+ mock_options.ClientUseIPv6 = 0;
+
+ CHECK_CHOSEN_ADDR_LS(lspecs, 0, 1, ipv4_or_ap);
+ CHECK_CHOSEN_ADDR_LS(lspecs, 1, 1, ipv4_or_ap);
+
+ CHECK_CHOSEN_ADDR_LS(lspecs_v6, 0, 0, null_ap);
+
+ CHECK_HS_EXTEND_INFO_ADDR_LS(lspecs, 1, ipv4_or_ap);
+ CHECK_HS_EXTEND_INFO_ADDR_LS(lspecs, 0, ipv4_or_ap);
+
+ CHECK_HS_EXTEND_INFO_ADDR_LS_EXPECT_NULL(lspecs_v6, 0);
+ CHECK_HS_EXTEND_INFO_ADDR_LS_EXPECT_NULL(lspecs_v6, 1);
+
+ /* IPv6-only. */
+ memset(&mock_options, 0, sizeof(or_options_t));
+ mock_options.ClientUseIPv4 = 0;
+ mock_options.ClientUseIPv6 = 1;
+
+ CHECK_CHOSEN_ADDR_LS(lspecs, 0, 1, ipv6_or_ap);
+ CHECK_CHOSEN_ADDR_LS(lspecs, 1, 1, ipv6_or_ap);
+
+ CHECK_CHOSEN_ADDR_LS(lspecs_v4, 0, 0, null_ap);
+
+ CHECK_HS_EXTEND_INFO_ADDR_LS(lspecs, 1, ipv6_or_ap);
+ CHECK_HS_EXTEND_INFO_ADDR_LS(lspecs, 0, ipv4_or_ap);
+
+ CHECK_HS_EXTEND_INFO_ADDR_LS_EXPECT_NULL(lspecs_v4, 1);
+ CHECK_HS_EXTEND_INFO_ADDR_LS_EXPECT_NULL(lspecs_v6, 0);
+
+ smartlist_free(lspecs_v4);
+ smartlist_free(lspecs_v6);
+
+ SMARTLIST_FOREACH(lspecs, link_specifier_t *, lspec, \
+ link_specifier_free(lspec)); \
+ smartlist_free(lspecs);
+
done:
UNMOCK(get_options);
}