Commit a7d44731 authored by Nick Mathewson's avatar Nick Mathewson 🤹
Browse files

Merge remote-tracking branch 'teor/feature4483-v10-squashed'

parents 54433993 d72af108
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
  o Major features (consensus downloads):
    - Schedule multiple in-progress consensus downloads during client
      bootstrap. Use the first one that starts downloading, close the
      rest. This reduces failures when authorities are slow or down.
      With #15775, it reduces failures due to fallback churn.
      Implements #4483 (reduce failures when authorities are down).
      Patch by "teor".
      Implements IPv4 portions of proposal #210 by "mikeperry" and
      "teor".
+59 −2
Original line number Diff line number Diff line
@@ -360,7 +360,11 @@ GENERAL OPTIONS

[[FallbackDir]] **FallbackDir** __address__:__port__ orport=__port__ id=__fingerprint__ [weight=__num__]::
    When we're unable to connect to any directory cache for directory info
    (usually because we don't know about any yet) we try a FallbackDir.
    (usually because we don't know about any yet) we try a directory authority.
    Clients also simultaneously try a FallbackDir, to avoid hangs on client
    startup if a directory authority is down. Clients retry FallbackDirs more
    often than directory authorities, to reduce the load on the directory
    authorities.
    By default, the directory authorities are also FallbackDirs. Specifying a
    FallbackDir replaces Tor's default hard-coded FallbackDirs (if any).

@@ -2287,10 +2291,18 @@ The following options are used for running a testing Tor network.
       TestingClientDownloadSchedule 0, 0, 5, 10, 15, 20, 30, 60
       TestingServerConsensusDownloadSchedule 0, 0, 5, 10, 15, 20, 30, 60
       TestingClientConsensusDownloadSchedule 0, 0, 5, 10, 15, 20, 30, 60
       TestingClientBootstrapConsensusAuthorityDownloadSchedule 0, 2,
           4 (for 40 seconds), 8, 16, 32, 60
       TestingClientBootstrapConsensusFallbackDownloadSchedule 0, 1,
           4 (for 40 seconds), 8, 16, 32, 60
       TestingClientBootstrapConsensusAuthorityOnlyDownloadSchedule 0, 1,
           4 (for 40 seconds), 8, 16, 32, 60
       TestingBridgeDownloadSchedule 60, 30, 30, 60
       TestingClientMaxIntervalWithoutRequest 5 seconds
       TestingDirConnectionMaxStall 30 seconds
       TestingConsensusMaxDownloadTries 80
       TestingClientBootstrapConsensusMaxDownloadTries 80
       TestingClientBootstrapConsensusAuthorityOnlyMaxDownloadTries 80
       TestingDescriptorMaxDownloadTries 80
       TestingMicrodescMaxDownloadTries 80
       TestingCertMaxDownloadTries 80
@@ -2351,6 +2363,36 @@ The following options are used for running a testing Tor network.
    requires that **TestingTorNetwork** is set. (Default: 0, 0, 60, 300, 600,
    1800, 3600, 3600, 3600, 10800, 21600, 43200)

[[TestingClientBootstrapConsensusAuthorityDownloadSchedule]] **TestingClientBootstrapConsensusAuthorityDownloadSchedule** __N__,__N__,__...__::
    Schedule for when clients should download consensuses from authorities if
    they are bootstrapping (that is, they don't have a usable, reasonably live
    consensus). Only used by clients fetching from a list of fallback
    directory mirrors. This schedule is advanced by (potentially concurrent)
    connection attempts, unlike other schedules, which are advanced by
    connection failures. Changing this schedule requires that
    **TestingTorNetwork** is set. (Default: 10, 11, 3600, 10800, 25200, 54000,
    111600, 262800)

[[TestingClientBootstrapConsensusFallbackDownloadSchedule]] **TestingClientBootstrapConsensusFallbackDownloadSchedule** __N__,__N__,__...__::
    Schedule for when clients should download consensuses from fallback
    directory mirrors if they are bootstrapping (that is, they don't have a
    usable, reasonably live consensus). Only used by clients fetching from a
    list of fallback directory mirrors.  This schedule is advanced by
    (potentially concurrent) connection attempts, unlike other schedules, which
    are advanced by connection failures. Changing this schedule requires that
    **TestingTorNetwork** is set. (Default: 0, 1, 4, 11, 3600, 10800, 25200,
    54000, 111600, 262800)

[[TestingClientBootstrapConsensusAuthorityOnlyDownloadSchedule]] **TestingClientBootstrapConsensusAuthorityOnlyDownloadSchedule** __N__,__N__,__...__::
    Schedule for when clients should download consensuses from authorities if
    they are bootstrapping (that is, they don't have a usable, reasonably live
    consensus). Only used by clients which don't have or won't fetch from a
    list of fallback directory mirrors. This schedule is advanced by
    (potentially concurrent) connection attempts, unlike other schedules,
    which are advanced by connection failures. Changing this schedule requires
    that **TestingTorNetwork** is set. (Default: 0, 3, 7, 3600, 10800, 25200,
    54000, 111600, 262800)

[[TestingBridgeDownloadSchedule]] **TestingBridgeDownloadSchedule** __N__,__N__,__...__::
    Schedule for when clients should download bridge descriptors. Changing this
    requires that **TestingTorNetwork** is set. (Default: 3600, 900, 900, 3600)
@@ -2367,9 +2409,24 @@ The following options are used for running a testing Tor network.
    5 minutes)

[[TestingConsensusMaxDownloadTries]] **TestingConsensusMaxDownloadTries** __NUM__::
    Try this often to download a consensus before giving up. Changing
    Try this many times to download a consensus before giving up. Changing
    this requires that **TestingTorNetwork** is set. (Default: 8)

[[TestingClientBootstrapConsensusMaxDownloadTries]] **TestingClientBootstrapConsensusMaxDownloadTries** __NUM__::
    Try this many times to download a consensus while bootstrapping using
    fallback directory mirrors before giving up. Changing this requires that
    **TestingTorNetwork** is set. (Default: 7)

[[TestingClientBootstrapConsensusMaxDownloadTries]] **TestingClientBootstrapConsensusMaxDownloadTries** __NUM__::
    Try this many times to download a consensus while bootstrapping using
    only authorities before giving up. Changing this requires that
    **TestingTorNetwork** is set. (Default: 4)

[[TestingClientBootstrapConsensusMaxInProgressTries]] **TestingClientBootstrapConsensusMaxInProgressTries** __NUM__::
    Try this many simultaneous connections to download a consensus before
    waiting for one to complete, timeout, or error out. Changing this
    requires that **TestingTorNetwork** is set. (Default: 4)

[[TestingDescriptorMaxDownloadTries]] **TestingDescriptorMaxDownloadTries** __NUM__::
    Try this often to download a server descriptor before giving up.
    Changing this requires that **TestingTorNetwork** is set. (Default: 8)
+26 −0
Original line number Diff line number Diff line
@@ -336,6 +336,32 @@ typedef uint32_t uintptr_t;
#endif /* time_t_is_signed */
#endif /* ifndef(TIME_MAX) */

#ifndef TIME_MIN

#ifdef TIME_T_IS_SIGNED

#if (SIZEOF_TIME_T == SIZEOF_INT)
#define TIME_MIN ((time_t)INT_MIN)
#elif (SIZEOF_TIME_T == SIZEOF_LONG)
#define TIME_MIN ((time_t)LONG_MIN)
#elif (SIZEOF_TIME_T == 8)
#define TIME_MIN ((time_t)INT64_MIN)
#else
#error "Can't define (signed) TIME_MIN"
#endif

#else
/* Unsigned case */
#if (SIZEOF_TIME_T == 4)
#define TIME_MIN ((time_t)UINT32_MIN)
#elif (SIZEOF_TIME_T == 8)
#define TIME_MIN ((time_t)UINT64_MIN)
#else
#error "Can't define (unsigned) TIME_MIN"
#endif
#endif /* time_t_is_signed */
#endif /* ifndef(TIME_MIN) */

#ifndef SIZE_MAX
#if (SIZEOF_SIZE_T == 4)
#define SIZE_MAX UINT32_MAX
+75 −1
Original line number Diff line number Diff line
@@ -476,10 +476,40 @@ static config_var_t option_vars_[] = {
  V(TestingClientConsensusDownloadSchedule, CSV_INTERVAL, "0, 0, 60, "
                                 "300, 600, 1800, 3600, 3600, 3600, "
                                 "10800, 21600, 43200"),
  /* With the TestingClientBootstrapConsensus*Download* below:
   * Clients with only authorities will try:
   *  - 3 authorities over 10 seconds, then wait 60 minutes.
   * Clients with authorities and fallbacks will try:
   *  - 2 authorities and 4 fallbacks over 21 seconds, then wait 60 minutes.
   * Clients will also retry when an application request arrives.
   * After a number of failed reqests, clients retry every 3 days + 1 hour.
   *
   * Clients used to try 2 authorities over 10 seconds, then wait for
   * 60 minutes or an application request.
   *
   * When clients have authorities and fallbacks available, they use these
   * schedules: (we stagger the times to avoid thundering herds) */
  V(TestingClientBootstrapConsensusAuthorityDownloadSchedule, CSV_INTERVAL,
    "10, 11, 3600, 10800, 25200, 54000, 111600, 262800" /* 3 days + 1 hour */),
  V(TestingClientBootstrapConsensusFallbackDownloadSchedule, CSV_INTERVAL,
    "0, 1, 4, 11, 3600, 10800, 25200, 54000, 111600, 262800"),
  /* When clients only have authorities available, they use this schedule: */
  V(TestingClientBootstrapConsensusAuthorityOnlyDownloadSchedule, CSV_INTERVAL,
    "0, 3, 7, 3600, 10800, 25200, 54000, 111600, 262800"),
  /* We don't want to overwhelm slow networks (or mirrors whose replies are
   * blocked), but we also don't want to fail if only some mirrors are
   * blackholed. Clients will try 3 directories simultaneously.
   * (Relays never use simultaneous connections.) */
  V(TestingClientBootstrapConsensusMaxInProgressTries, UINT, "3"),
  V(TestingBridgeDownloadSchedule, CSV_INTERVAL, "3600, 900, 900, 3600"),
  V(TestingClientMaxIntervalWithoutRequest, INTERVAL, "10 minutes"),
  V(TestingDirConnectionMaxStall, INTERVAL, "5 minutes"),
  V(TestingConsensusMaxDownloadTries, UINT, "8"),
  /* Since we try connections rapidly and simultaneously, we can afford
   * to give up earlier. (This protects against overloading directories.) */
  V(TestingClientBootstrapConsensusMaxDownloadTries, UINT, "7"),
  /* We want to give up much earlier if we're only using authorities. */
  V(TestingClientBootstrapConsensusAuthorityOnlyMaxDownloadTries, UINT, "4"),
  V(TestingDescriptorMaxDownloadTries, UINT, "8"),
  V(TestingMicrodescMaxDownloadTries, UINT, "8"),
  V(TestingCertMaxDownloadTries, UINT, "8"),
@@ -526,10 +556,18 @@ static const config_var_t testing_tor_network_defaults[] = {
                                 "15, 20, 30, 60"),
  V(TestingClientConsensusDownloadSchedule, CSV_INTERVAL, "0, 0, 5, 10, "
                                 "15, 20, 30, 60"),
  V(TestingClientBootstrapConsensusAuthorityDownloadSchedule, CSV_INTERVAL,
    "0, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 8, 16, 32, 60"),
  V(TestingClientBootstrapConsensusFallbackDownloadSchedule, CSV_INTERVAL,
    "0, 1, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 8, 16, 32, 60"),
  V(TestingClientBootstrapConsensusAuthorityOnlyDownloadSchedule, CSV_INTERVAL,
    "0, 1, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 8, 16, 32, 60"),
  V(TestingBridgeDownloadSchedule, CSV_INTERVAL, "60, 30, 30, 60"),
  V(TestingClientMaxIntervalWithoutRequest, INTERVAL, "5 seconds"),
  V(TestingDirConnectionMaxStall, INTERVAL, "30 seconds"),
  V(TestingConsensusMaxDownloadTries, UINT, "80"),
  V(TestingClientBootstrapConsensusMaxDownloadTries, UINT, "80"),
  V(TestingClientBootstrapConsensusAuthorityOnlyMaxDownloadTries, UINT, "80"),
  V(TestingDescriptorMaxDownloadTries, UINT, "80"),
  V(TestingMicrodescMaxDownloadTries, UINT, "80"),
  V(TestingCertMaxDownloadTries, UINT, "80"),
@@ -3758,10 +3796,16 @@ options_validate(or_options_t *old_options, or_options_t *options,
  CHECK_DEFAULT(TestingClientDownloadSchedule);
  CHECK_DEFAULT(TestingServerConsensusDownloadSchedule);
  CHECK_DEFAULT(TestingClientConsensusDownloadSchedule);
  CHECK_DEFAULT(TestingClientBootstrapConsensusAuthorityDownloadSchedule);
  CHECK_DEFAULT(TestingClientBootstrapConsensusFallbackDownloadSchedule);
  CHECK_DEFAULT(TestingClientBootstrapConsensusAuthorityOnlyDownloadSchedule);
  CHECK_DEFAULT(TestingBridgeDownloadSchedule);
  CHECK_DEFAULT(TestingClientMaxIntervalWithoutRequest);
  CHECK_DEFAULT(TestingDirConnectionMaxStall);
  CHECK_DEFAULT(TestingConsensusMaxDownloadTries);
  CHECK_DEFAULT(TestingClientBootstrapConsensusMaxDownloadTries);
  CHECK_DEFAULT(TestingClientBootstrapConsensusAuthorityOnlyMaxDownloadTries);
  CHECK_DEFAULT(TestingClientBootstrapConsensusMaxInProgressTries);
  CHECK_DEFAULT(TestingDescriptorMaxDownloadTries);
  CHECK_DEFAULT(TestingMicrodescMaxDownloadTries);
  CHECK_DEFAULT(TestingCertMaxDownloadTries);
@@ -3836,11 +3880,41 @@ options_validate(or_options_t *old_options, or_options_t *options,
  }

  if (options->TestingConsensusMaxDownloadTries < 2) {
    REJECT("TestingConsensusMaxDownloadTries must be greater than 1.");
    REJECT("TestingConsensusMaxDownloadTries must be greater than 2.");
  } else if (options->TestingConsensusMaxDownloadTries > 800) {
    COMPLAIN("TestingConsensusMaxDownloadTries is insanely high.");
  }

  if (options->TestingClientBootstrapConsensusMaxDownloadTries < 2) {
    REJECT("TestingClientBootstrapConsensusMaxDownloadTries must be greater "
           "than 2."
           );
  } else if (options->TestingClientBootstrapConsensusMaxDownloadTries > 800) {
    COMPLAIN("TestingClientBootstrapConsensusMaxDownloadTries is insanely "
             "high.");
  }

  if (options->TestingClientBootstrapConsensusAuthorityOnlyMaxDownloadTries
      < 2) {
    REJECT("TestingClientBootstrapConsensusAuthorityOnlyMaxDownloadTries must "
           "be greater than 2."
           );
  } else if (
        options->TestingClientBootstrapConsensusAuthorityOnlyMaxDownloadTries
        > 800) {
    COMPLAIN("TestingClientBootstrapConsensusAuthorityOnlyMaxDownloadTries is "
             "insanely high.");
  }

  if (options->TestingClientBootstrapConsensusMaxInProgressTries < 1) {
    REJECT("TestingClientBootstrapConsensusMaxInProgressTries must be greater "
           "than 0.");
  } else if (options->TestingClientBootstrapConsensusMaxInProgressTries
             > 100) {
    COMPLAIN("TestingClientBootstrapConsensusMaxInProgressTries is insanely "
             "high.");
  }

  if (options->TestingDescriptorMaxDownloadTries < 2) {
    REJECT("TestingDescriptorMaxDownloadTries must be greater than 1.");
  } else if (options->TestingDescriptorMaxDownloadTries > 800) {
+153 −77
Original line number Diff line number Diff line
@@ -1618,13 +1618,18 @@ connection_init_accepted_conn(connection_t *conn,
  return 0;
}

static int
connection_connect_sockaddr(connection_t *conn,
/** Take conn, make a nonblocking socket; try to connect to
 * sa, binding to bindaddr if sa is not localhost. If fail, return -1 and if
 * applicable put your best guess about errno into *<b>socket_error</b>.
 * If connected return 1, if EAGAIN return 0.
 */
MOCK_IMPL(STATIC int,
connection_connect_sockaddr,(connection_t *conn,
                            const struct sockaddr *sa,
                            socklen_t sa_len,
                            const struct sockaddr *bindaddr,
                            socklen_t bindaddr_len,
                            int *socket_error)
                            int *socket_error))
{
  tor_socket_t s;
  int inprogress = 0;
@@ -4222,6 +4227,19 @@ connection_write_to_buf_impl_,(const char *string, size_t len,
  }
}

/** Return a connection_t * from get_connection_array() that satisfies test on
 * var, and that is not marked for close. */
#define CONN_GET_TEMPLATE(var, test)               \
  STMT_BEGIN                                       \
    smartlist_t *conns = get_connection_array();   \
    SMARTLIST_FOREACH(conns, connection_t *, var,  \
    {                                              \
      if (var && (test) && !var->marked_for_close) \
        return var;                                \
    });                                            \
    return NULL;                                   \
  STMT_END

/** Return a connection with given type, address, port, and purpose;
 * or NULL if no such connection exists (or if all such connections are marked
 * for close). */
@@ -4230,17 +4248,11 @@ connection_get_by_type_addr_port_purpose(int type,
                                         const tor_addr_t *addr, uint16_t port,
                                         int purpose)
{
  smartlist_t *conns = get_connection_array();
  SMARTLIST_FOREACH(conns, connection_t *, conn,
  {
    if (conn->type == type &&
  CONN_GET_TEMPLATE(conn,
       (conn->type == type &&
        tor_addr_eq(&conn->addr, addr) &&
        conn->port == port &&
        conn->purpose == purpose &&
        !conn->marked_for_close)
      return conn;
  });
  return NULL;
        conn->purpose == purpose));
}

/** Return the stream with id <b>id</b> if it is not already marked for
@@ -4249,13 +4261,7 @@ connection_get_by_type_addr_port_purpose(int type,
connection_t *
connection_get_by_global_id(uint64_t id)
{
  smartlist_t *conns = get_connection_array();
  SMARTLIST_FOREACH(conns, connection_t *, conn,
  {
    if (conn->global_identifier == id)
      return conn;
  });
  return NULL;
  CONN_GET_TEMPLATE(conn, conn->global_identifier == id);
}

/** Return a connection of type <b>type</b> that is not marked for close.
@@ -4263,13 +4269,7 @@ connection_get_by_global_id(uint64_t id)
connection_t *
connection_get_by_type(int type)
{
  smartlist_t *conns = get_connection_array();
  SMARTLIST_FOREACH(conns, connection_t *, conn,
  {
    if (conn->type == type && !conn->marked_for_close)
      return conn;
  });
  return NULL;
  CONN_GET_TEMPLATE(conn, conn->type == type);
}

/** Return a connection of type <b>type</b> that is in state <b>state</b>,
@@ -4278,13 +4278,7 @@ connection_get_by_type(int type)
connection_t *
connection_get_by_type_state(int type, int state)
{
  smartlist_t *conns = get_connection_array();
  SMARTLIST_FOREACH(conns, connection_t *, conn,
  {
    if (conn->type == type && conn->state == state && !conn->marked_for_close)
      return conn;
  });
  return NULL;
  CONN_GET_TEMPLATE(conn, conn->type == type && conn->state == state);
}

/** Return a connection of type <b>type</b> that has rendquery equal
@@ -4295,55 +4289,142 @@ connection_t *
connection_get_by_type_state_rendquery(int type, int state,
                                       const char *rendquery)
{
  smartlist_t *conns = get_connection_array();

  tor_assert(type == CONN_TYPE_DIR ||
             type == CONN_TYPE_AP || type == CONN_TYPE_EXIT);
  tor_assert(rendquery);

  SMARTLIST_FOREACH_BEGIN(conns, connection_t *, conn) {
    if (conn->type == type &&
        !conn->marked_for_close &&
        (!state || state == conn->state)) {
      if (type == CONN_TYPE_DIR &&
  CONN_GET_TEMPLATE(conn,
       (conn->type == type &&
        (!state || state == conn->state)) &&
        (
         (type == CONN_TYPE_DIR &&
          TO_DIR_CONN(conn)->rend_data &&
          !rend_cmp_service_ids(rendquery,
                                TO_DIR_CONN(conn)->rend_data->onion_address))
        return conn;
      else if (CONN_IS_EDGE(conn) &&
         ||
              (CONN_IS_EDGE(conn) &&
               TO_EDGE_CONN(conn)->rend_data &&
               !rend_cmp_service_ids(rendquery,
                            TO_EDGE_CONN(conn)->rend_data->onion_address))
        return conn;
    }
  } SMARTLIST_FOREACH_END(conn);
  return NULL;
         ));
}

#define CONN_FIRST_AND_FREE_TEMPLATE(sl)       \
  STMT_BEGIN                                   \
    if (smartlist_len(sl) > 0) {               \
      void *first_item = smartlist_get(sl, 0); \
      smartlist_free(sl);                      \
      return first_item;                       \
    } else {                                   \
      smartlist_free(sl);                      \
      return NULL;                             \
    }                                          \
  STMT_END


/** Return a directory connection (if any one exists) that is fetching
 * the item described by <b>state</b>/<b>resource</b> */
 * the item described by <b>purpose</b>/<b>resource</b>, otherwise return NULL.
 */
dir_connection_t *
connection_dir_get_by_purpose_and_resource(int purpose,
connection_dir_get_by_purpose_and_resource(
                                           int purpose,
                                           const char *resource)
{
  smartlist_t *conns = get_connection_array();
  smartlist_t *conns = connection_dir_list_by_purpose_and_resource(
                                                          purpose,
                                                          resource);
  CONN_FIRST_AND_FREE_TEMPLATE(conns);
}

/** Return a new smartlist of dir_connection_t * from get_connection_array()
 * that satisfy conn_test on connection_t *conn_var, and dirconn_test on
 * dir_connection_t *dirconn_var. conn_var must be of CONN_TYPE_DIR and not
 * marked for close to be included in the list. */
#define DIR_CONN_LIST_TEMPLATE(conn_var, conn_test,             \
                               dirconn_var, dirconn_test)       \
  STMT_BEGIN                                                    \
    smartlist_t *conns = get_connection_array();                \
    smartlist_t *dir_conns = smartlist_new();                   \
    SMARTLIST_FOREACH_BEGIN(conns, connection_t *, conn_var) {  \
      if (conn_var && (conn_test)                               \
          && conn_var->type == CONN_TYPE_DIR                    \
          && !conn_var->marked_for_close) {                     \
        dir_connection_t *dirconn_var = TO_DIR_CONN(conn_var);  \
        if (dirconn_var && (dirconn_test)) {                    \
          smartlist_add(dir_conns, dirconn_var);                \
        }                                                       \
      }                                                         \
    } SMARTLIST_FOREACH_END(conn_var);                          \
    return dir_conns;                                           \
  STMT_END

/** Return a list of directory connections that are fetching the item
 * described by <b>purpose</b>/<b>resource</b>. If there are none,
 * return an empty list. This list must be freed using smartlist_free,
 * but the pointers in it must not be freed.
 * Note that this list should not be cached, as the pointers in it can be
 * freed if their connections close. */
smartlist_t *
connection_dir_list_by_purpose_and_resource(
                                            int purpose,
                                            const char *resource)
{
  DIR_CONN_LIST_TEMPLATE(conn,
                         conn->purpose == purpose,
                         dirconn,
                         0 == strcmp_opt(resource,
                                         dirconn->requested_resource));
}

  SMARTLIST_FOREACH_BEGIN(conns, connection_t *, conn) {
    dir_connection_t *dirconn;
    if (conn->type != CONN_TYPE_DIR || conn->marked_for_close ||
        conn->purpose != purpose)
      continue;
    dirconn = TO_DIR_CONN(conn);
    if (dirconn->requested_resource == NULL) {
      if (resource == NULL)
        return dirconn;
    } else if (resource) {
      if (0 == strcmp(resource, dirconn->requested_resource))
        return dirconn;
/** Return a directory connection (if any one exists) that is fetching
 * the item described by <b>purpose</b>/<b>resource</b>/<b>state</b>,
 * otherwise return NULL. */
dir_connection_t *
connection_dir_get_by_purpose_resource_and_state(
                                                 int purpose,
                                                 const char *resource,
                                                 int state)
{
  smartlist_t *conns =
    connection_dir_list_by_purpose_resource_and_state(
                                                      purpose,
                                                      resource,
                                                      state);
  CONN_FIRST_AND_FREE_TEMPLATE(conns);
}

#undef CONN_FIRST_AND_FREE_TEMPLATE

/** Return a list of directory connections that are fetching the item
 * described by <b>purpose</b>/<b>resource</b>/<b>state</b>. If there are
 * none, return an empty list. This list must be freed using smartlist_free,
 * but the pointers in it must not be freed.
 * Note that this list should not be cached, as the pointers in it can be
 * freed if their connections close. */
smartlist_t *
connection_dir_list_by_purpose_resource_and_state(
                                                  int purpose,
                                                  const char *resource,
                                                  int state)
{
  DIR_CONN_LIST_TEMPLATE(conn,
                         conn->purpose == purpose && conn->state == state,
                         dirconn,
                         0 == strcmp_opt(resource,
                                         dirconn->requested_resource));
}
  } SMARTLIST_FOREACH_END(conn);

  return NULL;
#undef DIR_CONN_LIST_TEMPLATE

/** Return an arbitrary active OR connection that isn't <b>this_conn</b>.
 *
 * We use this to guess if we should tell the controller that we
 * didn't manage to connect to any of our bridges. */
static connection_t *
connection_get_another_active_or_conn(const or_connection_t *this_conn)
{
  CONN_GET_TEMPLATE(conn,
                    conn != TO_CONN(this_conn) && conn->type == CONN_TYPE_OR);
}

/** Return 1 if there are any active OR connections apart from
@@ -4354,23 +4435,18 @@ connection_dir_get_by_purpose_and_resource(int purpose,
int
any_other_active_or_conns(const or_connection_t *this_conn)
{
  smartlist_t *conns = get_connection_array();
  SMARTLIST_FOREACH_BEGIN(conns, connection_t *, conn) {
    if (conn == TO_CONN(this_conn)) { /* don't consider this conn */
      continue;
    }

    if (conn->type == CONN_TYPE_OR &&
        !conn->marked_for_close) {
  connection_t *conn = connection_get_another_active_or_conn(this_conn);
  if (conn != NULL) {
    log_debug(LD_DIR, "%s: Found an OR connection: %s",
              __func__, conn->address);
    return 1;
  }
  } SMARTLIST_FOREACH_END(conn);

  return 0;
}

#undef CONN_GET_TEMPLATE

/** Return 1 if <b>conn</b> is a listener conn, else return 0. */
int
connection_is_listener(connection_t *conn)
Loading