From 20b0e9e07d4a1726029c8214e431237a2959f21f Mon Sep 17 00:00:00 2001 From: teor Date: Thu, 26 Oct 2017 14:38:06 +1100 Subject: [PATCH 1/8] Remove buggy IPv6 support from hs_get_extend_info_from_lspecs() The previous version of this function has the following issues: * it doesn't choose between IPv4 and IPv6 addresses correctly, and * it doesn't fall back to a 3-hop path when the address for a direct connection is unreachable. But we can't fix these things in a bugfix release. Instead, treat IPv6 addresses like any other unrecognised link specifier and ignore them. If there is no IPv4 address, return NULL. This supports v3 hidden services on IPv4, dual-stack, and IPv6, and v3 single onion services on IPv4 only. Part of 23820, bugfix on 0.3.2.1-alpha. --- src/or/hs_common.c | 78 +++++++++++++++++----------------------------- 1 file changed, 29 insertions(+), 49 deletions(-) diff --git a/src/or/hs_common.c b/src/or/hs_common.c index 42c15c80d..f79aeb598 100644 --- a/src/or/hs_common.c +++ b/src/or/hs_common.c @@ -1638,24 +1638,22 @@ hs_pick_hsdir(smartlist_t *responsible_dirs, const char *req_key_str) /* 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 checks the firewall policies and if we - * are allowed to extend to the chosen address. + * extend_info_t object. This function always returns an extend info with + * an IPv4 address, or NULL. * - * if either IPv4 or legacy ID is missing, error. - * if not direct_conn, IPv4 is prefered. - * if direct_conn, IPv6 is prefered if we have one available. - * if firewall does not allow the chosen address, error. - * - * Return NULL if we can fulfill the conditions. */ + * 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. + */ extend_info_t * hs_get_extend_info_from_lspecs(const smartlist_t *lspecs, const curve25519_public_key_t *onion_key, int direct_conn) { - int have_v4 = 0, have_v6 = 0, have_legacy_id = 0, have_ed25519_id = 0; + int have_v4 = 0, have_legacy_id = 0, have_ed25519_id = 0; char legacy_id[DIGEST_LEN] = {0}; - uint16_t port_v4 = 0, port_v6 = 0, port = 0; - tor_addr_t addr_v4, addr_v6, *addr = NULL; + uint16_t port_v4 = 0; + tor_addr_t addr_v4; ed25519_public_key_t ed25519_pk; extend_info_t *info = NULL; @@ -1671,14 +1669,6 @@ hs_get_extend_info_from_lspecs(const smartlist_t *lspecs, port_v4 = link_specifier_get_un_ipv4_port(ls); have_v4 = 1; break; - case LS_IPV6: - /* Skip if we already seen a v6. */ - 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; case LS_LEGACY_ID: /* Make sure we do have enough bytes for the legacy ID. */ if (link_specifier_getlen_un_legacy_id(ls) < sizeof(legacy_id)) { @@ -1700,55 +1690,45 @@ hs_get_extend_info_from_lspecs(const smartlist_t *lspecs, } } SMARTLIST_FOREACH_END(ls); - /* IPv4 and legacy ID are mandatory. */ + /* Legacy ID is mandatory, and we require IPv4. */ if (!have_v4 || !have_legacy_id) { goto done; } - /* By default, we pick IPv4 but this might change to v6 if certain - * conditions are met. */ - addr = &addr_v4; port = port_v4; - /* If we are NOT in a direct connection, we'll use our Guard and a 3-hop - * circuit so we can't extend in IPv6. And at this point, we do have an IPv4 - * address available so go to validation. */ + /* 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; - } - - /* From this point on, we have a request for a direct connection to the - * rendezvous point so make sure we can actually connect through our - * firewall. We'll prefer IPv6. */ - - /* IPv6 test. */ - if (have_v6 && - fascist_firewall_allows_address_addr(&addr_v6, port_v6, - FIREWALL_OR_CONNECTION, 1, 1)) { - /* Direct connection and we can reach it in IPv6 so go for it. */ - addr = &addr_v6; port = port_v6; - goto validate; - } - /* IPv4 test and we are sure we have a v4 because of the check above. */ - if (fascist_firewall_allows_address_addr(&addr_v4, port_v4, - FIREWALL_OR_CONNECTION, 0, 0)) { + } 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. */ - addr = &addr_v4; port = port_v4; 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. */ + goto done; } + /* We will add support for IPv6 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 allowing to extend to private address? */ - if (!extend_info_addr_is_allowed(addr)) { - log_warn(LD_REND, "Requested address is private and it is not " - "allowed to extend to it: %s:%u", - fmt_addr(&addr_v4), port_v4); + if (!extend_info_addr_is_allowed(&addr_v4)) { + 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); 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, port); + onion_key, &addr_v4, port_v4); done: return info; } From b4aa8fc3d918cc3aea375985c44abd086f91ae7a Mon Sep 17 00:00:00 2001 From: teor Date: Thu, 26 Oct 2017 14:47:54 +1100 Subject: [PATCH 2/8] Remove buggy IPv6 support from pick_intro_point() and service_intro_point_new() The previous version of these functions had the following issues: * they can't supply both the IPv4 and IPv6 addresses in link specifiers, * they try to fall back to a 3-hop path when the address for a direct connection is unreachable, but this isn't supported by launch_rendezvous_point_circuit(), so it fails. But we can't fix these things in a bugfix release. Instead, always put IPv4 addresses in link specifiers. And if a v3 single onion service can't reach any intro points, fail. This supports v3 hidden services on IPv4, dual-stack, and IPv6, and v3 single onion services on IPv4 only. Part of 23820, bugfix on 0.3.2.1-alpha. --- src/or/hs_service.c | 50 +++++++++++++++++++++++++++------------------ 1 file changed, 30 insertions(+), 20 deletions(-) diff --git a/src/or/hs_service.c b/src/or/hs_service.c index 3d1945aa9..f334de643 100644 --- a/src/or/hs_service.c +++ b/src/or/hs_service.c @@ -375,7 +375,14 @@ service_intro_point_free_(void *obj) /* Return a newly allocated service intro point and fully initialized from the * given extend_info_t ei if non NULL. If is_legacy is true, we also generate - * the legacy key. On error, NULL is returned. */ + * the legacy key. On error, NULL is returned. + * + * If ei is NULL, returns a hs_service_intro_point_t with an empty link + * specifier list and no onion key. (This is used for testing.) + * + * ei must be an extend_info_t containing an IPv4 address. (We will add supoort + * for IPv6 in a later release.) When calling extend_info_from_node(), pass + * 0 in for_direct_connection to make sure ei always has an IPv4 address. */ STATIC hs_service_intro_point_t * service_intro_point_new(const extend_info_t *ei, unsigned int is_legacy) { @@ -427,8 +434,8 @@ service_intro_point_new(const extend_info_t *ei, unsigned int is_legacy) goto done; } - /* We'll try to add all link specifier. Legacy, IPv4 and ed25519 are - * mandatory. */ + /* We'll try to add all link specifiers. Legacy is mandatory. + * IPv4 or IPv6 is required, and we always send IPv4. */ ls = hs_desc_link_specifier_new(ei, LS_IPV4); /* It is impossible to have an extend info object without a v4. */ if (BUG(!ls)) { @@ -450,11 +457,7 @@ service_intro_point_new(const extend_info_t *ei, unsigned int is_legacy) smartlist_add(ip->base.link_specifiers, ls); } - /* IPv6 is optional. */ - ls = hs_desc_link_specifier_new(ei, LS_IPV6); - if (ls) { - smartlist_add(ip->base.link_specifiers, ls); - } + /* IPv6 is not supported in this release. */ /* Finally, copy onion key from the extend_info_t object. */ memcpy(&ip->onion_key, &ei->curve25519_onion_key, sizeof(ip->onion_key)); @@ -1520,10 +1523,17 @@ build_all_descriptors(time_t now) /* Randomly pick a node to become an introduction point but not present in the * given exclude_nodes list. The chosen node is put in the exclude list * regardless of success or not because in case of failure, the node is simply - * unsusable from that point on. If direct_conn is set, try to pick a node - * that our local firewall/policy allows to directly connect to and if not, - * fallback to a normal 3-hop node. Return a newly allocated service intro - * point ready to be used for encoding. NULL on error. */ + * unsusable from that point on. + * + * If direct_conn is set, try to pick a node that our local firewall/policy + * allows us to connect to directly. If we can't find any, return NULL. + * This function supports selecting dual-stack nodes for direct single onion + * service IPv6 connections. But it does not send IPv6 addresses in link + * specifiers. (Current clients don't use IPv6 addresses to extend, and + * direct client connections to intro points are not supported.) + * + * Return a newly allocated service intro point ready to be used for encoding. + * Return NULL on error. */ static hs_service_intro_point_t * pick_intro_point(unsigned int direct_conn, smartlist_t *exclude_nodes) { @@ -1537,12 +1547,9 @@ pick_intro_point(unsigned int direct_conn, smartlist_t *exclude_nodes) node = router_choose_random_node(exclude_nodes, get_options()->ExcludeNodes, direct_conn ? direct_flags : flags); - if (node == NULL && direct_conn) { - /* Unable to find a node for direct connection, let's fall back to a - * normal 3-hop node. */ - node = router_choose_random_node(exclude_nodes, - get_options()->ExcludeNodes, flags); - } + /* Unable to find a node. When looking for a node for a direct connection, + * we could try a 3-hop path instead. We'll add support for this in a later + * release. */ if (!node) { goto err; } @@ -1553,8 +1560,11 @@ pick_intro_point(unsigned int direct_conn, smartlist_t *exclude_nodes) smartlist_add(exclude_nodes, (void *) node); /* We do this to ease our life but also this call makes appropriate checks - * of the node object such as validating ntor support for instance. */ - info = extend_info_from_node(node, direct_conn); + * of the node object such as validating ntor support for instance. + * + * We must provide an extend_info for clients to connect over a 3-hop path, + * so we don't pass direct_conn here. */ + info = extend_info_from_node(node, 0); if (BUG(info == NULL)) { goto err; } From 77ed99b1a79e61146d9e3b9f51aac4fd1c2f5b03 Mon Sep 17 00:00:00 2001 From: teor Date: Thu, 26 Oct 2017 16:30:24 +1100 Subject: [PATCH 3/8] Remove buggy IPv6 and ed25519 handling from get_lspecs_from_extend_info() The previous version of this function had the following issues: * it didn't check if the extend_info contained an IPv6 address, * it didn't check if the ed25519 identity key was valid. But we can't add IPv6 support in a bugfix release. Instead, BUG() if the address is an IPv6 address, so we always put IPv4 addresses in link specifiers. And ignore missing ed25519 identifiers, rather than generating an all-zero link specifier. This supports v3 hidden services on IPv4, dual-stack, and IPv6, and v3 single onion services on IPv4 only. Part of 23820, bugfix on 0.3.2.1-alpha. --- src/or/hs_circuit.c | 37 ++++++++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/src/or/hs_circuit.c b/src/or/hs_circuit.c index e1e513c5f..c8c9b4e56 100644 --- a/src/or/hs_circuit.c +++ b/src/or/hs_circuit.c @@ -533,7 +533,10 @@ retry_service_rendezvous_point(const origin_circuit_t *circ) } /* Using an extend info object ei, set all possible link specifiers in lspecs. - * IPv4, legacy ID and ed25519 ID are mandatory thus MUST be present in ei. */ + * legacy ID is mandatory thus MUST be present in ei. If IPv4 is not present, + * logs a BUG() warning, and returns an empty smartlist. Clients never make + * direct connections to rendezvous points, so they should always have an + * IPv4 address in ei. */ static void get_lspecs_from_extend_info(const extend_info_t *ei, smartlist_t *lspecs) { @@ -542,7 +545,11 @@ get_lspecs_from_extend_info(const extend_info_t *ei, smartlist_t *lspecs) tor_assert(ei); tor_assert(lspecs); - /* IPv4 is mandatory. */ + /* We require IPv4, we will add IPv6 support in a later tor version */ + if (BUG(!tor_addr_is_v4(&ei->addr))) { + return; + } + ls = link_specifier_new(); link_specifier_set_ls_type(ls, LS_IPV4); link_specifier_set_un_ipv4_addr(ls, tor_addr_to_ipv4h(&ei->addr)); @@ -560,15 +567,15 @@ get_lspecs_from_extend_info(const extend_info_t *ei, smartlist_t *lspecs) link_specifier_set_ls_len(ls, link_specifier_getlen_un_legacy_id(ls)); smartlist_add(lspecs, ls); - /* ed25519 ID is mandatory. */ - ls = link_specifier_new(); - link_specifier_set_ls_type(ls, LS_ED25519_ID); - memcpy(link_specifier_getarray_un_ed25519_id(ls), &ei->ed_identity, - link_specifier_getlen_un_ed25519_id(ls)); - link_specifier_set_ls_len(ls, link_specifier_getlen_un_ed25519_id(ls)); - smartlist_add(lspecs, ls); - - /* XXX: IPv6 is not clearly a thing in extend_info_t? */ + /* ed25519 ID is only included if the node has it. */ + if (!ed25519_public_key_is_zero(&ei->ed_identity)) { + ls = link_specifier_new(); + link_specifier_set_ls_type(ls, LS_ED25519_ID); + memcpy(link_specifier_getarray_un_ed25519_id(ls), &ei->ed_identity, + link_specifier_getlen_un_ed25519_id(ls)); + link_specifier_set_ls_len(ls, link_specifier_getlen_un_ed25519_id(ls)); + smartlist_add(lspecs, ls); + } } /* Using the given descriptor intro point ip, the extend information of the @@ -1053,6 +1060,14 @@ hs_circ_send_introduce1(origin_circuit_t *intro_circ, * object which is used to build the content of the cell. */ setup_introduce1_data(ip, rend_circ->build_state->chosen_exit, subcredential, &intro1_data); + /* If we didn't get any link specifiers, it's because our extend info was + * bad. */ + if (BUG(!intro1_data.link_specifiers) || + !smartlist_len(intro1_data.link_specifiers)) { + log_warn(LD_REND, "Unable to get link specifiers for INTRODUCE1 cell on " + "circuit %u.", TO_CIRCUIT(intro_circ)->n_circ_id); + goto done; + } /* Final step before we encode a cell, we setup the circuit identifier which * will generate both the rendezvous cookie and client keypair for this From a4f34c1106c5cd14ce3eda3877d41ff2f76ff0dd Mon Sep 17 00:00:00 2001 From: teor Date: Thu, 26 Oct 2017 16:48:02 +1100 Subject: [PATCH 4/8] Improve v3 onion service logging for intro and rend points Diagnostics for #23820. --- src/or/hs_circuit.c | 29 +++++++++++++++++++++++++---- src/or/hs_client.c | 27 +++++++++++++++++++++++---- 2 files changed, 48 insertions(+), 8 deletions(-) diff --git a/src/or/hs_circuit.c b/src/or/hs_circuit.c index c8c9b4e56..a23edc36a 100644 --- a/src/or/hs_circuit.c +++ b/src/or/hs_circuit.c @@ -343,6 +343,17 @@ send_establish_intro(const hs_service_t *service, memwipe(payload, 0, sizeof(payload)); } +/* Return a string constant describing the anonymity of service. */ +static const char * +get_service_anonymity_string(const hs_service_t *service) +{ + if (service->config.is_single_onion) { + return "single onion"; + } else { + return "hidden"; + } +} + /* For a given service, the ntor onion key and a rendezvous cookie, launch a * circuit to the rendezvous point specified by the link specifiers. On * success, a circuit identifier is attached to the circuit with the needed @@ -370,7 +381,15 @@ launch_rendezvous_point_circuit(const hs_service_t *service, &data->onion_pk, service->config.is_single_onion); if (info == NULL) { - /* We are done here, we can't extend to the rendezvous point. */ + /* We are done here, we can't extend to the rendezvous point. + * If you're running an IPv6-only v3 single onion service on 0.3.2 or with + * 0.3.2 clients, and somehow disable the option check, it will fail here. + */ + log_fn(LOG_PROTOCOL_WARN, LD_REND, + "Not enough info to open a circuit to a rendezvous point for " + "%s service %s.", + get_service_anonymity_string(service), + safe_str_client(service->onion_address)); goto end; } @@ -392,17 +411,19 @@ launch_rendezvous_point_circuit(const hs_service_t *service, } } if (circ == NULL) { - log_warn(LD_REND, "Giving up on launching rendezvous circuit to %s " - "for service %s", + log_warn(LD_REND, "Giving up on launching a rendezvous circuit to %s " + "for %s service %s", safe_str_client(extend_info_describe(info)), + get_service_anonymity_string(service), safe_str_client(service->onion_address)); goto end; } log_info(LD_REND, "Rendezvous circuit launched to %s with cookie %s " - "for service %s", + "for %s service %s", safe_str_client(extend_info_describe(info)), safe_str_client(hex_str((const char *) data->rendezvous_cookie, REND_COOKIE_LEN)), + get_service_anonymity_string(service), safe_str_client(service->onion_address)); tor_assert(circ->build_state); /* Rendezvous circuit have a specific timeout for the time spent on trying diff --git a/src/or/hs_client.c b/src/or/hs_client.c index 93a913b34..581aa478e 100644 --- a/src/or/hs_client.c +++ b/src/or/hs_client.c @@ -727,15 +727,24 @@ client_get_random_intro(const ed25519_public_key_t *service_pk) const hs_descriptor_t *desc; const hs_desc_encrypted_data_t *enc_data; const or_options_t *options = get_options(); + /* Calculate the onion address for logging purposes */ + char onion_address[HS_SERVICE_ADDR_LEN_BASE32 + 1]; tor_assert(service_pk); desc = hs_cache_lookup_as_client(service_pk); + /* Assume the service is v3 if the descriptor is missing. This is ok, + * because we only use the address in log messages */ + hs_build_address(service_pk, + desc ? desc->plaintext_data.version : HS_VERSION_THREE, + onion_address); if (desc == NULL || !hs_client_any_intro_points_usable(service_pk, desc)) { log_info(LD_REND, "Unable to randomly select an introduction point " - "because descriptor %s.", - (desc) ? "doesn't have usable intro point" : "is missing"); + "for service %s because descriptor %s. We can't connect.", + safe_str_client(onion_address), + (desc) ? "doesn't have any usable intro points" + : "is missing (assuming v3 onion address)"); goto end; } @@ -763,6 +772,10 @@ client_get_random_intro(const ed25519_public_key_t *service_pk) if (ei == NULL) { /* We can get here for instance if the intro point is a private address * and we aren't allowed to extend to those. */ + log_info(LD_REND, "Unable to select introduction point with auth key %s " + "for service %s, because we could not extend to it.", + safe_str_client(ed25519_fmt(&ip->auth_key_cert->signed_key)), + safe_str_client(onion_address)); continue; } @@ -791,14 +804,20 @@ client_get_random_intro(const ed25519_public_key_t *service_pk) * set, we are forced to not use anything. */ ei = ei_excluded; if (options->StrictNodes) { - log_warn(LD_REND, "Every introduction points are in the ExcludeNodes set " - "and StrictNodes is set. We can't connect."); + log_warn(LD_REND, "Every introduction point for service %s is in the " + "ExcludeNodes set and StrictNodes is set. We can't connect.", + safe_str_client(onion_address)); extend_info_free(ei); ei = NULL; + } else { + log_fn(LOG_PROTOCOL_WARN, LD_REND, "Every introduction point for service " + "%s is unusable or we can't extend to it. We can't connect.", + safe_str_client(onion_address)); } end: smartlist_free(usable_ips); + memwipe(onion_address, 0, sizeof(onion_address)); return ei; } From cc072b6fbfee7208f41309b76badec65adbca8ee Mon Sep 17 00:00:00 2001 From: teor Date: Thu, 26 Oct 2017 16:49:00 +1100 Subject: [PATCH 5/8] Stop users configuring IPv6-only v3 single onion services They are not yet implemented: they will upload descriptors, but won't be able to rendezvous, because IPv6 addresses in link specifiers are ignored. Part of #23820. --- src/or/hs_config.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/or/hs_config.c b/src/or/hs_config.c index 5f9282ea7..fa5c1ab17 100644 --- a/src/or/hs_config.c +++ b/src/or/hs_config.c @@ -424,11 +424,19 @@ config_generic_service(const config_line_t *line_, } } - /* Check if we are configured in non anonymous mode and single hop mode - * meaning every service become single onion. */ - if (rend_service_allow_non_anonymous_connection(options) && - rend_service_non_anonymous_mode_enabled(options)) { + /* Check if we are configured in non anonymous mode meaning every service + * 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 */ From 176bfe1bede5a4669edba8003d66b913fd3a7de7 Mon Sep 17 00:00:00 2001 From: teor Date: Thu, 26 Oct 2017 16:51:04 +1100 Subject: [PATCH 6/8] Don't run IPv6-only v3 single onion service tests using chutney Part of #23820. --- src/test/include.am | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/test/include.am b/src/test/include.am index 50cee693a..7300b8541 100644 --- a/src/test/include.am +++ b/src/test/include.am @@ -42,8 +42,10 @@ TESTS += src/test/test src/test/test-slow src/test/test-memwipe \ TEST_CHUTNEY_FLAVORS = basic-min bridges-min hs-v2-min hs-v3-min \ single-onion-v23 # only run if we can ping6 ::1 (localhost) +# IPv6-only v3 single onion services don't work yet, so we don't test the +# single-onion-v23-ipv6 flavor TEST_CHUTNEY_FLAVORS_IPV6 = bridges+ipv6-min ipv6-exit-min hs-v23-ipv6 \ - single-onion-v23-ipv6 + single-onion-ipv6 # only run if we can find a stable (or simply another) version of tor TEST_CHUTNEY_FLAVORS_MIXED = mixed+hs-v23 From a2bc979d9d498954e1a2955ce917781caed80634 Mon Sep 17 00:00:00 2001 From: teor Date: Thu, 26 Oct 2017 17:25:16 +1100 Subject: [PATCH 7/8] Changes file for 23820 --- changes/bug23820 | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 changes/bug23820 diff --git a/changes/bug23820 b/changes/bug23820 new file mode 100644 index 000000000..4e920d049 --- /dev/null +++ b/changes/bug23820 @@ -0,0 +1,5 @@ + o Minor bugfixes (IPv6, v3 single onion services): + - Remove buggy code for IPv6-only v3 single onion services, and reject + attempts to configure them. This release supports IPv4, dual-stack, and + IPv6-only v3 hidden services; and IPv4 and dual-stack v3 single onion + services. Fixes bug 23820; bugfix on 0.3.2.1-alpha. From 6a9a118f9077c02ce60052275dcede88d9fd2aa3 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Thu, 2 Nov 2017 10:22:32 -0400 Subject: [PATCH 8/8] Tweak a comment for a minor but important distinction --- src/or/hs_circuit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/or/hs_circuit.c b/src/or/hs_circuit.c index a23edc36a..ee952f4d6 100644 --- a/src/or/hs_circuit.c +++ b/src/or/hs_circuit.c @@ -588,7 +588,7 @@ get_lspecs_from_extend_info(const extend_info_t *ei, smartlist_t *lspecs) link_specifier_set_ls_len(ls, link_specifier_getlen_un_legacy_id(ls)); smartlist_add(lspecs, ls); - /* ed25519 ID is only included if the node has it. */ + /* ed25519 ID is only included if the extend_info has it. */ if (!ed25519_public_key_is_zero(&ei->ed_identity)) { ls = link_specifier_new(); link_specifier_set_ls_type(ls, LS_ED25519_ID);