diff options
author | David Goulet <dgoulet@torproject.org> | 2017-05-30 16:11:59 -0400 |
---|---|---|
committer | Nick Mathewson <nickm@torproject.org> | 2017-08-08 20:29:34 -0400 |
commit | 713eb08bc9582b49e8073122fb68c3fac5bae188 (patch) | |
tree | fe8544abeb5fc6388798b3f15494023522157824 /src/or/hs_circuit.c | |
parent | 1b403a83821d86ac358e49ae24bd1284ed0dcfab (diff) | |
download | tor-713eb08bc9582b49e8073122fb68c3fac5bae188.tar.gz tor-713eb08bc9582b49e8073122fb68c3fac5bae188.zip |
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>
Diffstat (limited to 'src/or/hs_circuit.c')
-rw-r--r-- | src/or/hs_circuit.c | 121 |
1 files changed, 121 insertions, 0 deletions
diff --git a/src/or/hs_circuit.c b/src/or/hs_circuit.c index d9e96c6330..ce85229470 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 |