diff options
-rw-r--r-- | src/or/circuitbuild.c | 28 | ||||
-rw-r--r-- | src/or/circuitlist.c | 32 | ||||
-rw-r--r-- | src/or/circuituse.c | 10 | ||||
-rw-r--r-- | src/or/circuituse.h | 3 | ||||
-rw-r--r-- | src/or/hs_client.c | 17 | ||||
-rw-r--r-- | src/or/nodelist.c | 22 | ||||
-rw-r--r-- | src/or/nodelist.h | 1 | ||||
-rw-r--r-- | src/or/or.h | 10 | ||||
-rw-r--r-- | src/or/protover.h | 2 | ||||
-rw-r--r-- | src/or/routerlist.c | 16 | ||||
-rw-r--r-- | src/or/routerparse.c | 3 |
11 files changed, 131 insertions, 13 deletions
diff --git a/src/or/circuitbuild.c b/src/or/circuitbuild.c index 65cd7bd5dc..e5c6767d4a 100644 --- a/src/or/circuitbuild.c +++ b/src/or/circuitbuild.c @@ -71,7 +71,8 @@ static channel_t * channel_connect_for_circuit(const tor_addr_t *addr, static int circuit_deliver_create_cell(circuit_t *circ, const create_cell_t *create_cell, int relayed); -static int onion_pick_cpath_exit(origin_circuit_t *circ, extend_info_t *exit); +static int onion_pick_cpath_exit(origin_circuit_t *circ, extend_info_t *exit, + int is_hs_v3_rp_circuit); static crypt_path_t *onion_next_hop_in_cpath(crypt_path_t *cpath); static int onion_extend_cpath(origin_circuit_t *circ); static int onion_append_hop(crypt_path_t **head_ptr, extend_info_t *choice); @@ -505,10 +506,15 @@ circuit_establish_circuit(uint8_t purpose, extend_info_t *exit_ei, int flags) { origin_circuit_t *circ; int err_reason = 0; + int is_hs_v3_rp_circuit = 0; + + if (flags & CIRCLAUNCH_IS_V3_RP) { + is_hs_v3_rp_circuit = 1; + } circ = origin_circuit_init(purpose, flags); - if (onion_pick_cpath_exit(circ, exit_ei) < 0 || + if (onion_pick_cpath_exit(circ, exit_ei, is_hs_v3_rp_circuit) < 0 || onion_populate_cpath(circ) < 0) { circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_NOPATH); return NULL; @@ -2156,7 +2162,8 @@ pick_rendezvous_node(router_crn_flags_t flags) */ static const node_t * choose_good_exit_server(uint8_t purpose, - int need_uptime, int need_capacity, int is_internal) + int need_uptime, int need_capacity, int is_internal, + int need_hs_v3) { const or_options_t *options = get_options(); router_crn_flags_t flags = CRN_NEED_DESC; @@ -2164,6 +2171,8 @@ choose_good_exit_server(uint8_t purpose, flags |= CRN_NEED_UPTIME; if (need_capacity) flags |= CRN_NEED_CAPACITY; + if (need_hs_v3) + flags |= CRN_RENDEZVOUS_V3; switch (purpose) { case CIRCUIT_PURPOSE_C_GENERAL: @@ -2263,9 +2272,15 @@ warn_if_last_router_excluded(origin_circuit_t *circ, /** Decide a suitable length for circ's cpath, and pick an exit * router (or use <b>exit</b> if provided). Store these in the - * cpath. Return 0 if ok, -1 if circuit should be closed. */ + * cpath. + * + * If <b>is_hs_v3_rp_circuit</b> is set, then this exit should be suitable to + * be used as an HS v3 rendezvous point. + * + * Return 0 if ok, -1 if circuit should be closed. */ static int -onion_pick_cpath_exit(origin_circuit_t *circ, extend_info_t *exit_ei) +onion_pick_cpath_exit(origin_circuit_t *circ, extend_info_t *exit_ei, + int is_hs_v3_rp_circuit) { cpath_build_state_t *state = circ->build_state; @@ -2289,7 +2304,8 @@ onion_pick_cpath_exit(origin_circuit_t *circ, extend_info_t *exit_ei) } else { /* we have to decide one */ const node_t *node = choose_good_exit_server(circ->base_.purpose, state->need_uptime, - state->need_capacity, state->is_internal); + state->need_capacity, state->is_internal, + is_hs_v3_rp_circuit); if (!node) { log_warn(LD_CIRC,"Failed to choose an exit server"); return -1; diff --git a/src/or/circuitlist.c b/src/or/circuitlist.c index 2f3fe327e6..774edc90b4 100644 --- a/src/or/circuitlist.c +++ b/src/or/circuitlist.c @@ -1609,6 +1609,30 @@ circuit_get_next_by_pk_and_purpose(origin_circuit_t *start, return NULL; } +/** We might cannibalize this circuit: Return true if its last hop can be used + * as a v3 rendezvous point. */ +static int +circuit_can_be_cannibalized_for_v3_rp(const origin_circuit_t *circ) +{ + if (!circ->build_state) { + return 0; + } + + extend_info_t *chosen_exit = circ->build_state->chosen_exit; + if (BUG(!chosen_exit)) { + return 0; + } + + const node_t *rp_node = node_get_by_id(chosen_exit->identity_digest); + if (rp_node) { + if (node_supports_v3_rendezvous_point(rp_node)) { + return 1; + } + } + + return 0; +} + /** Return a circuit that is open, is CIRCUIT_PURPOSE_C_GENERAL, * has a timestamp_dirty value of 0, has flags matching the CIRCLAUNCH_* * flags in <b>flags</b>, and if info is defined, does not already use info @@ -1691,6 +1715,14 @@ circuit_find_to_cannibalize(uint8_t purpose, extend_info_t *info, hop = hop->next; } while (hop != circ->cpath); } + + if ((flags & CIRCLAUNCH_IS_V3_RP) && + !circuit_can_be_cannibalized_for_v3_rp(circ)) { + log_debug(LD_GENERAL, "Skipping uncannibalizable circuit for v3 " + "rendezvous point."); + goto next; + } + if (!best || (best->build_state->need_uptime && !need_uptime)) best = circ; next: ; diff --git a/src/or/circuituse.c b/src/or/circuituse.c index 570b05e572..c229c84d96 100644 --- a/src/or/circuituse.c +++ b/src/or/circuituse.c @@ -2290,6 +2290,16 @@ circuit_get_open_circ_or_launch(entry_connection_t *conn, if (want_onehop) flags |= CIRCLAUNCH_ONEHOP_TUNNEL; if (need_uptime) flags |= CIRCLAUNCH_NEED_UPTIME; if (need_internal) flags |= CIRCLAUNCH_IS_INTERNAL; + + /* If we are about to pick a v3 RP right now, make sure we pick a + * rendezvous point that supports the v3 protocol! */ + if (desired_circuit_purpose == CIRCUIT_PURPOSE_C_REND_JOINED && + new_circ_purpose == CIRCUIT_PURPOSE_C_ESTABLISH_REND && + ENTRY_TO_EDGE_CONN(conn)->hs_ident) { + flags |= CIRCLAUNCH_IS_V3_RP; + log_info(LD_GENERAL, "Getting rendezvous circuit to v3 service!"); + } + circ = circuit_launch_by_extend_info(new_circ_purpose, extend_info, flags); } diff --git a/src/or/circuituse.h b/src/or/circuituse.h index e66679586d..6c0bcbb357 100644 --- a/src/or/circuituse.h +++ b/src/or/circuituse.h @@ -44,6 +44,9 @@ void circuit_build_failed(origin_circuit_t *circ); /** Flag to set when the last hop of a circuit doesn't need to be an * exit node. */ #define CIRCLAUNCH_IS_INTERNAL (1<<3) +/** Flag to set when we are trying to launch a v3 rendezvous circuit. We need + * to apply some additional filters on the node picked. */ +#define CIRCLAUNCH_IS_V3_RP (1<<4) origin_circuit_t *circuit_launch_by_extend_info(uint8_t purpose, extend_info_t *info, int flags); diff --git a/src/or/hs_client.c b/src/or/hs_client.c index 19e20c0e8d..0e8fc6c24b 100644 --- a/src/or/hs_client.c +++ b/src/or/hs_client.c @@ -24,6 +24,7 @@ #include "circuitlist.h" #include "circuituse.h" #include "connection.h" +#include "nodelist.h" #include "circpathbias.h" #include "connection.h" #include "hs_ntor.h" @@ -461,9 +462,21 @@ client_rendezvous_circ_has_opened(origin_circuit_t *circ) tor_assert(circ); tor_assert(TO_CIRCUIT(circ)->purpose == CIRCUIT_PURPOSE_C_ESTABLISH_REND); + const extend_info_t *rp_ei = circ->build_state->chosen_exit; + + /* Check that we didn't accidentally choose a node that does not understand + * the v3 rendezvous protocol */ + if (rp_ei) { + const node_t *rp_node = node_get_by_id(rp_ei->identity_digest); + if (rp_node) { + if (BUG(!node_supports_v3_rendezvous_point(rp_node))) { + return; + } + } + } + log_info(LD_REND, "Rendezvous circuit has opened to %s.", - safe_str_client( - extend_info_describe(circ->build_state->chosen_exit))); + safe_str_client(extend_info_describe(rp_ei))); /* Ignore returned value, nothing we can really do. On failure, the circuit * will be marked for close. */ diff --git a/src/or/nodelist.c b/src/or/nodelist.c index 155a511ca1..03b315e682 100644 --- a/src/or/nodelist.c +++ b/src/or/nodelist.c @@ -863,6 +863,28 @@ node_supports_ed25519_hs_intro(const node_t *node) return 0; } +/** Return true iff <b>node</b> supports to be a rendezvous point for hidden + * service version 3 (HSRend=2). */ +int +node_supports_v3_rendezvous_point(const node_t *node) +{ + tor_assert(node); + + if (node->rs) { + return node->rs->supports_v3_rendezvous_point; + } + if (node->ri) { + if (node->ri->protocol_list == NULL) { + return 0; + } + return protocol_list_supports_protocol(node->ri->protocol_list, + PRT_HSREND, + PROTOVER_HS_RENDEZVOUS_POINT_V3); + } + tor_assert_nonfatal_unreached_once(); + return 0; +} + /** Return the RSA ID key's SHA1 digest for the provided node. */ const uint8_t * node_get_rsa_id_digest(const node_t *node) diff --git a/src/or/nodelist.h b/src/or/nodelist.h index d16cf0ecf7..9676263f75 100644 --- a/src/or/nodelist.h +++ b/src/or/nodelist.h @@ -62,6 +62,7 @@ int node_ed25519_id_matches(const node_t *node, int node_supports_ed25519_link_authentication(const node_t *node); int node_supports_v3_hsdir(const node_t *node); int node_supports_ed25519_hs_intro(const node_t *node); +int node_supports_v3_rendezvous_point(const node_t *node); const uint8_t *node_get_rsa_id_digest(const node_t *node); int node_has_ipv6_addr(const node_t *node); diff --git a/src/or/or.h b/src/or/or.h index b3cd83f245..5a543b498c 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -2329,6 +2329,11 @@ typedef struct routerstatus_t { * requires HSDir=2. */ unsigned int supports_v3_hsdir : 1; + /** True iff this router has a protocol list that allows it to be an hidden + * service rendezvous point supporting version 3 as seen in proposal 224. + * This requires HSRend=2. */ + unsigned int supports_v3_rendezvous_point: 1; + unsigned int has_bandwidth:1; /**< The vote/consensus had bw info */ unsigned int has_exitsummary:1; /**< The vote/consensus had exit summaries */ unsigned int bw_is_unmeasured:1; /**< This is a consensus entry, with @@ -5393,7 +5398,10 @@ typedef enum { CRN_PREF_ADDR = 1<<7, /* On clients, only provide nodes that we can connect to directly, based on * our firewall rules */ - CRN_DIRECT_CONN = 1<<8 + CRN_DIRECT_CONN = 1<<8, + /* On clients, only provide nodes with HSRend >= 2 protocol version which + * is required for hidden service version >= 3. */ + CRN_RENDEZVOUS_V3 = 1<<9, } router_crn_flags_t; /** Return value for router_add_to_routerlist() and dirserv_add_descriptor() */ diff --git a/src/or/protover.h b/src/or/protover.h index 2066aeec72..ec8da1a0d0 100644 --- a/src/or/protover.h +++ b/src/or/protover.h @@ -21,6 +21,8 @@ #define PROTOVER_HSDIR_V3 2 /** The protover version number that signifies HSv3 intro point support */ #define PROTOVER_HS_INTRO_V3 4 +/** The protover version number that signifies HSv3 rendezvous point support */ +#define PROTOVER_HS_RENDEZVOUS_POINT_V3 2 /** List of recognized subprotocols. */ typedef enum protocol_type_t { diff --git a/src/or/routerlist.c b/src/or/routerlist.c index 9894019476..da2dff600e 100644 --- a/src/or/routerlist.c +++ b/src/or/routerlist.c @@ -2799,6 +2799,7 @@ router_choose_random_node(smartlist_t *excludedsmartlist, const int need_desc = (flags & CRN_NEED_DESC) != 0; const int pref_addr = (flags & CRN_PREF_ADDR) != 0; const int direct_conn = (flags & CRN_DIRECT_CONN) != 0; + const int rendezvous_v3 = (flags & CRN_RENDEZVOUS_V3) != 0; smartlist_t *sl=smartlist_new(), *excludednodes=smartlist_new(); @@ -2810,12 +2811,19 @@ router_choose_random_node(smartlist_t *excludedsmartlist, rule = weight_for_exit ? WEIGHT_FOR_EXIT : (need_guard ? WEIGHT_FOR_GUARD : WEIGHT_FOR_MID); - /* Exclude relays that allow single hop exit circuits. This is an obsolete - * option since 0.2.9.2-alpha and done by default in 0.3.1.0-alpha. */ - SMARTLIST_FOREACH(nodelist_get_list(), node_t *, node, + SMARTLIST_FOREACH_BEGIN(nodelist_get_list(), node_t *, node) { if (node_allows_single_hop_exits(node)) { + /* Exclude relays that allow single hop exit circuits. This is an + * obsolete option since 0.2.9.2-alpha and done by default in + * 0.3.1.0-alpha. */ smartlist_add(excludednodes, node); - }); + } else if (rendezvous_v3 && + !node_supports_v3_rendezvous_point(node)) { + /* Exclude relays that do not support to rendezvous for a hidden service + * version 3. */ + smartlist_add(excludednodes, node); + } + } SMARTLIST_FOREACH_END(node); if ((r = routerlist_find_my_routerinfo())) routerlist_add_node_and_family(excludednodes, r); diff --git a/src/or/routerparse.c b/src/or/routerparse.c index 9c11f4d07f..08c3fc0a02 100644 --- a/src/or/routerparse.c +++ b/src/or/routerparse.c @@ -2708,6 +2708,9 @@ routerstatus_parse_entry_from_string(memarea_t *area, rs->supports_v3_hsdir = protocol_list_supports_protocol(tok->args[0], PRT_HSDIR, PROTOVER_HSDIR_V3); + rs->supports_v3_rendezvous_point = + protocol_list_supports_protocol(tok->args[0], PRT_HSDIR, + PROTOVER_HS_RENDEZVOUS_POINT_V3); } if ((tok = find_opt_by_keyword(tokens, K_V))) { tor_assert(tok->n_args == 1); |