commit 713eb08bc9582b49e8073122fb68c3fac5bae188 Author: David Goulet dgoulet@torproject.org Date: Tue May 30 16:11:59 2017 -0400
prop224: Add service rendezvous circuit relaunch
This introduces a callback to relaunch a service rendezvous circuit when a previous one failed to build or expired.
It unifies the legacy function rend_service_relaunch_rendezvous() with one for specific to prop224. There is now only one entry point for that which is hs_circ_retry_service_rendezvous_point() supporting both legacy and prop224 circuits.
Signed-off-by: David Goulet dgoulet@torproject.org --- src/or/circuituse.c | 5 ++- src/or/hs_circuit.c | 121 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/or/hs_circuit.h | 1 + src/or/hs_ident.c | 10 +++++ src/or/hs_ident.h | 1 + src/or/rendservice.c | 23 ---------- 6 files changed, 136 insertions(+), 25 deletions(-)
diff --git a/src/or/circuituse.c b/src/or/circuituse.c index 247c5bbbb..b19f9ed46 100644 --- a/src/or/circuituse.c +++ b/src/or/circuituse.c @@ -43,6 +43,7 @@ #include "entrynodes.h" #include "hs_common.h" #include "hs_client.h" +#include "hs_circuit.h" #include "hs_ident.h" #include "nodelist.h" #include "networkstatus.h" @@ -782,7 +783,7 @@ circuit_expire_building(void) victim->state, circuit_state_to_string(victim->state), victim->purpose); TO_ORIGIN_CIRCUIT(victim)->hs_circ_has_timed_out = 1; - rend_service_relaunch_rendezvous(TO_ORIGIN_CIRCUIT(victim)); + hs_circ_retry_service_rendezvous_point(TO_ORIGIN_CIRCUIT(victim)); continue; }
@@ -1790,7 +1791,7 @@ circuit_build_failed(origin_circuit_t *circ) "(%s hop failed).", escaped(build_state_get_exit_nickname(circ->build_state)), failed_at_last_hop?"last":"non-last"); - rend_service_relaunch_rendezvous(circ); + hs_circ_retry_service_rendezvous_point(circ); break; /* default: * This won't happen in normal operation, but might happen if the diff --git a/src/or/hs_circuit.c b/src/or/hs_circuit.c index d9e96c633..ce8522947 100644 --- a/src/or/hs_circuit.c +++ b/src/or/hs_circuit.c @@ -566,10 +566,131 @@ launch_rendezvous_point_circuit(const hs_service_t *service, extend_info_free(info); }
+/* Return true iff the given service rendezvous circuit circ is allowed for a + * relaunch to the rendezvous point. */ +static int +can_relaunch_service_rendezvous_point(const origin_circuit_t *circ) +{ + tor_assert(circ); + /* This is initialized when allocating an origin circuit. */ + tor_assert(circ->build_state); + tor_assert(TO_CIRCUIT(circ)->purpose == CIRCUIT_PURPOSE_S_CONNECT_REND); + + /* XXX: Retrying under certain condition. This is related to #22455. */ + + /* Avoid to relaunch twice a circuit to the same rendezvous point at the + * same time. */ + if (circ->hs_service_side_rend_circ_has_been_relaunched) { + log_info(LD_REND, "Rendezvous circuit to %s has already been retried. " + "Skipping retry.", + safe_str_client( + extend_info_describe(circ->build_state->chosen_exit))); + goto disallow; + } + + /* A failure count that has reached maximum allowed or circuit that expired, + * we skip relaunching. */ + if (circ->build_state->failure_count > MAX_REND_FAILURES || + circ->build_state->expiry_time <= time(NULL)) { + log_info(LD_REND, "Attempt to build a rendezvous circuit to %s has " + "failed with %d attempts and expiry time %ld. " + "Giving up building.", + safe_str_client( + extend_info_describe(circ->build_state->chosen_exit)), + circ->build_state->failure_count, + circ->build_state->expiry_time); + goto disallow; + } + + /* Allowed to relaunch. */ + return 1; + disallow: + return 0; +} + +/* Retry the rendezvous point of circ by launching a new circuit to it. */ +static void +retry_service_rendezvous_point(const origin_circuit_t *circ) +{ + int flags = 0; + origin_circuit_t *new_circ; + cpath_build_state_t *bstate; + + tor_assert(circ); + /* This is initialized when allocating an origin circuit. */ + tor_assert(circ->build_state); + tor_assert(TO_CIRCUIT(circ)->purpose == CIRCUIT_PURPOSE_S_CONNECT_REND); + + /* Ease our life. */ + bstate = circ->build_state; + + log_info(LD_REND, "Retrying rendezvous point circuit to %s", + safe_str_client(extend_info_describe(bstate->chosen_exit))); + + /* Get the current build state flags for the next circuit. */ + flags |= (bstate->need_uptime) ? CIRCLAUNCH_NEED_UPTIME : 0; + flags |= (bstate->need_capacity) ? CIRCLAUNCH_NEED_CAPACITY : 0; + flags |= (bstate->is_internal) ? CIRCLAUNCH_IS_INTERNAL : 0; + + /* We do NOT add the onehop tunnel flag even though it might be a single + * onion service. The reason is that if we failed once to connect to the RP + * with a direct connection, we consider that chances are that we will fail + * again so try a 3-hop circuit and hope for the best. Because the service + * has no anonymity (single onion), this change of behavior won't affect + * security directly. */ + + /* Help predict this next time */ + rep_hist_note_used_internal(time(NULL), bstate->need_uptime, + bstate->need_capacity); + + new_circ = circuit_launch_by_extend_info(CIRCUIT_PURPOSE_S_CONNECT_REND, + bstate->chosen_exit, flags); + if (new_circ == NULL) { + log_warn(LD_REND, "Failed to launch rendezvous circuit to %s", + safe_str_client(extend_info_describe(bstate->chosen_exit))); + goto done; + } + + /* Transfer build state information to the new circuit state in part to + * catch any other failures. */ + new_circ->build_state->failure_count = bstate->failure_count++; + new_circ->build_state->expiry_time = bstate->expiry_time; + new_circ->hs_ident = hs_ident_circuit_dup(circ->hs_ident); + + done: + return; +} + /* ========== */ /* Public API */ /* ========== */
+/* Called when we fail building a rendezvous circuit at some point other than + * the last hop: launches a new circuit to the same rendezvous point. This + * supports legacy service. */ +void +hs_circ_retry_service_rendezvous_point(origin_circuit_t *circ) +{ + tor_assert(circ); + tor_assert(TO_CIRCUIT(circ)->purpose == CIRCUIT_PURPOSE_S_CONNECT_REND); + + /* Check if we are allowed to relaunch to the rendezvous point of circ. */ + if (!can_relaunch_service_rendezvous_point(circ)) { + goto done; + } + + /* Flag the circuit that we are relaunching so to avoid to relaunch twice a + * circuit to the same rendezvous point at the same time. */ + circ->hs_service_side_rend_circ_has_been_relaunched = 1; + + /* Legacy service don't have an hidden service ident. */ + (circ->hs_ident) ? retry_service_rendezvous_point(circ) : + rend_service_relaunch_rendezvous(circ); + + done: + return; +} + /* For a given service and a service intro point, launch a circuit to the * extend info ei. If the service is a single onion, a one-hop circuit will be * requested. Return 0 if the circuit was successfully launched and tagged diff --git a/src/or/hs_circuit.h b/src/or/hs_circuit.h index ca8f1b2f6..8301dea22 100644 --- a/src/or/hs_circuit.h +++ b/src/or/hs_circuit.h @@ -28,6 +28,7 @@ int hs_circ_launch_intro_point(hs_service_t *service, int hs_circ_launch_rendezvous_point(const hs_service_t *service, const curve25519_public_key_t *onion_key, const uint8_t *rendezvous_cookie); +void hs_circ_retry_service_rendezvous_point(origin_circuit_t *circ);
/* Cell API. */ int hs_circ_handle_intro_established(const hs_service_t *service, diff --git a/src/or/hs_ident.c b/src/or/hs_ident.c index c3f789db5..e69350d82 100644 --- a/src/or/hs_ident.c +++ b/src/or/hs_ident.c @@ -34,6 +34,16 @@ hs_ident_circuit_free(hs_ident_circuit_t *ident) tor_free(ident); }
+/* For a given circuit identifier src, return a newly allocated copy of it. + * This can't fail. */ +hs_ident_circuit_t * +hs_ident_circuit_dup(const hs_ident_circuit_t *src) +{ + hs_ident_circuit_t *ident = tor_malloc_zero(sizeof(*ident)); + memcpy(ident, src, sizeof(*ident)); + return ident; +} + /* For a given directory connection identifier src, return a newly allocated * copy of it. This can't fail. */ hs_ident_dir_conn_t * diff --git a/src/or/hs_ident.h b/src/or/hs_ident.h index ca1fa3d70..a3ebd07da 100644 --- a/src/or/hs_ident.h +++ b/src/or/hs_ident.h @@ -113,6 +113,7 @@ hs_ident_circuit_t *hs_ident_circuit_new( const ed25519_public_key_t *identity_pk, hs_ident_circuit_type_t circuit_type); void hs_ident_circuit_free(hs_ident_circuit_t *ident); +hs_ident_circuit_t *hs_ident_circuit_dup(const hs_ident_circuit_t *src);
/* Directory connection identifier API. */ hs_ident_dir_conn_t *hs_ident_dir_conn_dup(const hs_ident_dir_conn_t *src); diff --git a/src/or/rendservice.c b/src/or/rendservice.c index 7c4c6edc4..a205b00c6 100644 --- a/src/or/rendservice.c +++ b/src/or/rendservice.c @@ -2892,29 +2892,6 @@ rend_service_relaunch_rendezvous(origin_circuit_t *oldcirc)
tor_assert(oldcirc->base_.purpose == CIRCUIT_PURPOSE_S_CONNECT_REND);
- /* Don't relaunch the same rend circ twice. */ - if (oldcirc->hs_service_side_rend_circ_has_been_relaunched) { - log_info(LD_REND, "Rendezvous circuit to %s has already been relaunched; " - "not relaunching it again.", - oldcirc->build_state ? - safe_str(extend_info_describe(oldcirc->build_state->chosen_exit)) - : "*unknown*"); - return; - } - oldcirc->hs_service_side_rend_circ_has_been_relaunched = 1; - - if (!oldcirc->build_state || - oldcirc->build_state->failure_count > MAX_REND_FAILURES || - oldcirc->build_state->expiry_time < time(NULL)) { - log_info(LD_REND, - "Attempt to build circuit to %s for rendezvous has failed " - "too many times or expired; giving up.", - oldcirc->build_state ? - safe_str(extend_info_describe(oldcirc->build_state->chosen_exit)) - : "*unknown*"); - return; - } - oldstate = oldcirc->build_state; tor_assert(oldstate);