Commit 713eb08b authored by David Goulet's avatar David Goulet 🐼 Committed by Nick Mathewson
Browse files

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's avatarDavid Goulet <dgoulet@torproject.org>
parent 1b403a83
Loading
Loading
Loading
Loading
+3 −2
Original line number Diff line number Diff line
@@ -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
+121 −0
Original line number Diff line number Diff line
@@ -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
+1 −0
Original line number Diff line number Diff line
@@ -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,
+10 −0
Original line number Diff line number Diff line
@@ -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 *
+1 −0
Original line number Diff line number Diff line
@@ -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);
Loading