Commit fcdec006 authored by Nick Mathewson's avatar Nick Mathewson 🦀
Browse files

Merge branch 'maint-0.3.2'

parents 94dce246 9c17a6d2
Loading
Loading
Loading
Loading

changes/bug23817

0 → 100644
+3 −0
Original line number Diff line number Diff line
  o Minor bugfixes (descriptors):
    - Don't try fetching microdescriptors from relays that have failed to
      deliver them in the past. Fixes bug 23817; bugfix on 0.3.0.1-alpha.
+25 −57
Original line number Diff line number Diff line
@@ -116,7 +116,8 @@ static void dir_routerdesc_download_failed(smartlist_t *failed,
                                           int was_extrainfo,
                                           int was_descriptor_digests);
static void dir_microdesc_download_failed(smartlist_t *failed,
                                          int status_code);
                                          int status_code,
                                          const char *dir_id);
static int client_likes_consensus(const struct consensus_cache_entry_t *ent,
                                  const char *want_url);

@@ -469,7 +470,7 @@ directory_pick_generic_dirserver(dirinfo_type_t type, int pds_flags,
    log_warn(LD_BUG, "Called when we have UseBridges set.");

  if (should_use_directory_guards(options)) {
    const node_t *node = guards_choose_dirguard(guard_state_out);
    const node_t *node = guards_choose_dirguard(dir_purpose, guard_state_out);
    if (node)
      rs = node->rs;
  } else {
@@ -603,7 +604,7 @@ directory_get_from_dirserver,(
       * sort of dir fetch we'll be doing, so it won't return a bridge
       * that can't answer our question.
       */
      const node_t *node = guards_choose_dirguard(&guard_state);
      const node_t *node = guards_choose_dirguard(dir_purpose, &guard_state);
      if (node && node->ri) {
        /* every bridge has a routerinfo. */
        routerinfo_t *ri = node->ri;
@@ -1011,48 +1012,6 @@ directory_must_use_begindir(const or_options_t *options)
  return !public_server_mode(options);
}

struct directory_request_t {
  /**
   * These fields specify which directory we're contacting.  Routerstatus,
   * if present, overrides the other fields.
   *
   * @{ */
  tor_addr_port_t or_addr_port;
  tor_addr_port_t dir_addr_port;
  char digest[DIGEST_LEN];

  const routerstatus_t *routerstatus;
  /** @} */
  /** One of DIR_PURPOSE_* other than DIR_PURPOSE_SERVER. Describes what
   * kind of operation we'll be doing (upload/download), and of what kind
   * of document. */
  uint8_t dir_purpose;
  /** One of ROUTER_PURPOSE_*; used for uploads and downloads of routerinfo
   * and extrainfo docs.  */
  uint8_t router_purpose;
  /** Enum: determines whether to anonymize, and whether to use dirport or
   * orport. */
  dir_indirection_t indirection;
  /** Alias to the variable part of the URL for this request */
  const char *resource;
  /** Alias to the payload to upload (if any) */
  const char *payload;
  /** Number of bytes to upload from payload</b> */
  size_t payload_len;
  /** Value to send in an if-modified-since header, or 0 for none. */
  time_t if_modified_since;
  /** Hidden-service-specific information v2. */
  const rend_data_t *rend_query;
  /** Extra headers to append to the request */
  config_line_t *additional_headers;
  /** Hidden-service-specific information for v3+. */
  const hs_ident_dir_conn_t *hs_ident;
  /** Used internally to directory.c: gets informed when the attempt to
   * connect to the directory succeeds or fails, if that attempt bears on the
   * directory's usability as a directory guard. */
  circuit_guard_state_t *guard_state;
};

/** Evaluate the situation and decide if we should use an encrypted
 * "begindir-style" connection for this directory request.
 * 0) If there is no DirPort, yes.
@@ -2245,8 +2204,6 @@ static int handle_response_fetch_detached_signatures(dir_connection_t *,
                                             const response_handler_args_t *);
static int handle_response_fetch_desc(dir_connection_t *,
                                             const response_handler_args_t *);
static int handle_response_fetch_microdesc(dir_connection_t *,
                                           const response_handler_args_t *);
static int handle_response_upload_dir(dir_connection_t *,
                                      const response_handler_args_t *);
static int handle_response_upload_vote(dir_connection_t *,
@@ -2914,7 +2871,7 @@ handle_response_fetch_desc(dir_connection_t *conn,
 * Handler function: processes a response to a request for a group of
 * microdescriptors
 **/
static int
STATIC int
handle_response_fetch_microdesc(dir_connection_t *conn,
                                const response_handler_args_t *args)
{
@@ -2931,6 +2888,7 @@ handle_response_fetch_microdesc(dir_connection_t *conn,
           conn->base_.port);
  tor_assert(conn->requested_resource &&
             !strcmpstart(conn->requested_resource, "d/"));
  tor_assert_nonfatal(!tor_mem_is_zero(conn->identity_digest, DIGEST_LEN));
  which = smartlist_new();
  dir_split_resource_into_fingerprints(conn->requested_resource+2,
                                       which, NULL,
@@ -2941,7 +2899,7 @@ handle_response_fetch_microdesc(dir_connection_t *conn,
             "soon.",
             status_code, escaped(reason), conn->base_.address,
             (int)conn->base_.port, conn->requested_resource);
    dir_microdesc_download_failed(which, status_code);
    dir_microdesc_download_failed(which, status_code, conn->identity_digest);
    SMARTLIST_FOREACH(which, char *, cp, tor_free(cp));
    smartlist_free(which);
    return 0;
@@ -2953,7 +2911,7 @@ handle_response_fetch_microdesc(dir_connection_t *conn,
                                  now, which);
    if (smartlist_len(which)) {
      /* Mark remaining ones as failed. */
      dir_microdesc_download_failed(which, status_code);
      dir_microdesc_download_failed(which, status_code, conn->identity_digest);
    }
    if (mds && smartlist_len(mds)) {
      control_event_bootstrap(BOOTSTRAP_STATUS_LOADING_DESCRIPTORS,
@@ -5794,13 +5752,14 @@ dir_routerdesc_download_failed(smartlist_t *failed, int status_code,
   * every 10 or 60 seconds (FOO_DESCRIPTOR_RETRY_INTERVAL) in main.c. */
}

/** Called when a connection to download microdescriptors has failed in whole
 * or in part. <b>failed</b> is a list of every microdesc digest we didn't
 * get. <b>status_code</b> is the http status code we received. Reschedule the
 * microdesc downloads as appropriate. */
/** Called when a connection to download microdescriptors from relay with
 * <b>dir_id</b> has failed in whole or in part. <b>failed</b> is a list
 * of every microdesc digest we didn't get. <b>status_code</b> is the http
 * status code we received. Reschedule the microdesc downloads as
 * appropriate. */
static void
dir_microdesc_download_failed(smartlist_t *failed,
                              int status_code)
                              int status_code, const char *dir_id)
{
  networkstatus_t *consensus
    = networkstatus_get_latest_consensus_by_flavor(FLAV_MICRODESC);
@@ -5811,17 +5770,26 @@ dir_microdesc_download_failed(smartlist_t *failed,

  if (! consensus)
    return;

  /* We failed to fetch a microdescriptor from 'dir_id', note it down
   * so that we don't try the same relay next time... */
  microdesc_note_outdated_dirserver(dir_id);

  SMARTLIST_FOREACH_BEGIN(failed, const char *, d) {
    rs = router_get_mutable_consensus_status_by_descriptor_digest(consensus,d);
    if (!rs)
      continue;
    dls = &rs->dl_status;
    if (dls->n_download_failures >=
        get_options()->TestingMicrodescMaxDownloadTries)
        get_options()->TestingMicrodescMaxDownloadTries) {
      continue;
    {
    }

    { /* Increment the failure count for this md fetch */
      char buf[BASE64_DIGEST256_LEN+1];
      digest256_to_base64(buf, d);
      log_info(LD_DIR, "Failed to download md %s from %s",
               buf, hex_str(dir_id, DIGEST_LEN));
      download_status_increment_failure(dls, status_code, buf,
                                        server, now);
    }
+44 −0
Original line number Diff line number Diff line
@@ -183,6 +183,48 @@ typedef struct response_handler_args_t {
  const char *headers;
} response_handler_args_t;

struct directory_request_t {
  /**
   * These fields specify which directory we're contacting.  Routerstatus,
   * if present, overrides the other fields.
   *
   * @{ */
  tor_addr_port_t or_addr_port;
  tor_addr_port_t dir_addr_port;
  char digest[DIGEST_LEN];

  const routerstatus_t *routerstatus;
  /** @} */
  /** One of DIR_PURPOSE_* other than DIR_PURPOSE_SERVER. Describes what
   * kind of operation we'll be doing (upload/download), and of what kind
   * of document. */
  uint8_t dir_purpose;
  /** One of ROUTER_PURPOSE_*; used for uploads and downloads of routerinfo
   * and extrainfo docs.  */
  uint8_t router_purpose;
  /** Enum: determines whether to anonymize, and whether to use dirport or
   * orport. */
  dir_indirection_t indirection;
  /** Alias to the variable part of the URL for this request */
  const char *resource;
  /** Alias to the payload to upload (if any) */
  const char *payload;
  /** Number of bytes to upload from payload</b> */
  size_t payload_len;
  /** Value to send in an if-modified-since header, or 0 for none. */
  time_t if_modified_since;
  /** Hidden-service-specific information v2. */
  const rend_data_t *rend_query;
  /** Extra headers to append to the request */
  config_line_t *additional_headers;
  /** Hidden-service-specific information for v3+. */
  const hs_ident_dir_conn_t *hs_ident;
  /** Used internally to directory.c: gets informed when the attempt to
   * connect to the directory succeeds or fails, if that attempt bears on the
   * directory's usability as a directory guard. */
  struct circuit_guard_state_t *guard_state;
};

struct get_handler_args_t;
STATIC int handle_get_hs_descriptor_v3(dir_connection_t *conn,
                                       const struct get_handler_args_t *args);
@@ -193,6 +235,8 @@ STATIC void warn_disallowed_anonymous_compression_method(compress_method_t);

STATIC int handle_response_fetch_hsdesc_v3(dir_connection_t *conn,
                                          const response_handler_args_t *args);
STATIC int handle_response_fetch_microdesc(dir_connection_t *conn,
                                 const response_handler_args_t *args);

STATIC int handle_response_fetch_consensus(dir_connection_t *conn,
                                         const response_handler_args_t *args);
+110 −11
Original line number Diff line number Diff line
@@ -1460,6 +1460,96 @@ guard_in_node_family(const entry_guard_t *guard, const node_t *node)
  }
}

/* Allocate and return a new exit guard restriction (where <b>exit_id</b> is of
 * size DIGEST_LEN) */
STATIC entry_guard_restriction_t *
guard_create_exit_restriction(const uint8_t *exit_id)
{
  entry_guard_restriction_t *rst = NULL;
  rst = tor_malloc_zero(sizeof(entry_guard_restriction_t));
  rst->type = RST_EXIT_NODE;
  memcpy(rst->exclude_id, exit_id, DIGEST_LEN);
  return rst;
}

/** If we have fewer than this many possible guards, don't set
 * MD-availability-based restrictions: we might blacklist all of
 * them. */
#define MIN_GUARDS_FOR_MD_RESTRICTION 10

/** Return true if we should set md dirserver restrictions. We might not want
 *  to set those if our network is too restricted, since we don't want to
 *  blacklist all our nodes. */
static int
should_set_md_dirserver_restriction(void)
{
  const guard_selection_t *gs = get_guard_selection_info();

  /* Compute the number of filtered guards */
  int n_filtered_guards = 0;
  SMARTLIST_FOREACH_BEGIN(gs->sampled_entry_guards, entry_guard_t *, guard) {
    if (guard->is_filtered_guard) {
      ++n_filtered_guards;
    }
  } SMARTLIST_FOREACH_END(guard);

  /* Do we have enough filtered guards that we feel okay about blacklisting
   * some for MD restriction? */
  return (n_filtered_guards >= MIN_GUARDS_FOR_MD_RESTRICTION);
}

/** Allocate and return an outdated md guard restriction. Return NULL if no
 *  such restriction is needed. */
STATIC entry_guard_restriction_t *
guard_create_dirserver_md_restriction(void)
{
  entry_guard_restriction_t *rst = NULL;

  if (!should_set_md_dirserver_restriction()) {
    log_debug(LD_GUARD, "Not setting md restriction: too few "
              "filtered guards.");
    return NULL;
  }

  rst = tor_malloc_zero(sizeof(entry_guard_restriction_t));
  rst->type = RST_OUTDATED_MD_DIRSERVER;

  return rst;
}

/* Return True if <b>guard</b> obeys the exit restriction <b>rst</b>. */
static int
guard_obeys_exit_restriction(const entry_guard_t *guard,
                             const entry_guard_restriction_t *rst)
{
  tor_assert(rst->type == RST_EXIT_NODE);

  // Exclude the exit ID and all of its family.
  const node_t *node = node_get_by_id((const char*)rst->exclude_id);
  if (node && guard_in_node_family(guard, node))
    return 0;

  return tor_memneq(guard->identity, rst->exclude_id, DIGEST_LEN);
}

/** Return True if <b>guard</b> should be used as a dirserver for fetching
 *  microdescriptors. */
static int
guard_obeys_md_dirserver_restriction(const entry_guard_t *guard)
{
  /* If this guard is an outdated dirserver, don't use it. */
  if (microdesc_relay_is_outdated_dirserver(guard->identity)) {
    log_info(LD_GENERAL, "Skipping %s dirserver: outdated",
             hex_str(guard->identity, DIGEST_LEN));
    return 0;
  }

  log_debug(LD_GENERAL, "%s dirserver obeys md restrictions",
            hex_str(guard->identity, DIGEST_LEN));

  return 1;
}

/**
 * Return true iff <b>guard</b> obeys the restrictions defined in <b>rst</b>.
 * (If <b>rst</b> is NULL, there are no restrictions.)
@@ -1472,13 +1562,14 @@ entry_guard_obeys_restriction(const entry_guard_t *guard,
  if (! rst)
    return 1; // No restriction?  No problem.

  // Only one kind of restriction exists right now: excluding an exit
  // ID and all of its family.
  const node_t *node = node_get_by_id((const char*)rst->exclude_id);
  if (node && guard_in_node_family(guard, node))
    return 0;
  if (rst->type == RST_EXIT_NODE) {
    return guard_obeys_exit_restriction(guard, rst);
  } else if (rst->type == RST_OUTDATED_MD_DIRSERVER) {
    return guard_obeys_md_dirserver_restriction(guard);
  }

  return tor_memneq(guard->identity, rst->exclude_id, DIGEST_LEN);
  tor_assert_nonfatal_unreached();
  return 0;
}

/**
@@ -2105,7 +2196,7 @@ entry_guard_has_higher_priority(entry_guard_t *a, entry_guard_t *b)
}

/** Release all storage held in <b>restriction</b> */
static void
STATIC void
entry_guard_restriction_free(entry_guard_restriction_t *rst)
{
  tor_free(rst);
@@ -3358,8 +3449,8 @@ guards_choose_guard(cpath_build_state_t *state,
    /* We're building to a targeted exit node, so that node can't be
     * chosen as our guard for this circuit.  Remember that fact in a
     * restriction. */
    rst = tor_malloc_zero(sizeof(entry_guard_restriction_t));
    memcpy(rst->exclude_id, exit_id, DIGEST_LEN);
    rst = guard_create_exit_restriction(exit_id);
    tor_assert(rst);
  }
  if (entry_guard_pick_for_circuit(get_guard_selection_info(),
                                   GUARD_USAGE_TRAFFIC,
@@ -3411,12 +3502,20 @@ remove_all_entry_guards(void)

/** Helper: pick a directory guard, with whatever algorithm is used. */
const node_t *
guards_choose_dirguard(circuit_guard_state_t **guard_state_out)
guards_choose_dirguard(uint8_t dir_purpose,
                       circuit_guard_state_t **guard_state_out)
{
  const node_t *r = NULL;
  entry_guard_restriction_t *rst = NULL;

  /* If we are fetching microdescs, don't query outdated dirservers. */
  if (dir_purpose == DIR_PURPOSE_FETCH_MICRODESC) {
    rst = guard_create_dirserver_md_restriction();
  }

  if (entry_guard_pick_for_circuit(get_guard_selection_info(),
                                   GUARD_USAGE_DIRGUARD,
                                   NULL,
                                   rst,
                                   &r,
                                   guard_state_out) < 0) {
    tor_assert(r == NULL);
+24 −9
Original line number Diff line number Diff line
@@ -272,22 +272,28 @@ struct guard_selection_s {

struct entry_guard_handle_t;

/** Types of restrictions we impose when picking guard nodes */
typedef enum guard_restriction_type_t {
  /* Don't pick the same guard node as our exit node (or its family) */
  RST_EXIT_NODE = 0,
  /* Don't pick dirguards that have previously shown to be outdated */
  RST_OUTDATED_MD_DIRSERVER = 1
} guard_restriction_type_t;

/**
 * A restriction to remember which entry guards are off-limits for a given
 * circuit.
 *
 * Right now, we only use restrictions to block a single guard and its family
 * from being selected; this mechanism is designed to be more extensible in
 * the future, however.
 *
 * Note: This mechanism is NOT for recording which guards are never to be
 * used: only which guards cannot be used on <em>one particular circuit</em>.
 */
struct entry_guard_restriction_t {
  /**
   * The guard's RSA identity digest must not equal this; and it must not
   * be in the same family as any node with this digest.
   */
  /* What type of restriction are we imposing? */
  guard_restriction_type_t type;

  /* In case of restriction type RST_EXIT_NODE, the guard's RSA identity
   * digest must not equal this; and it must not be in the same family as any
   * node with this digest. */
  uint8_t exclude_id[DIGEST_LEN];
};

@@ -316,7 +322,8 @@ struct circuit_guard_state_t {
int guards_update_all(void);
const node_t *guards_choose_guard(cpath_build_state_t *state,
                                  circuit_guard_state_t **guard_state_out);
const node_t *guards_choose_dirguard(circuit_guard_state_t **guard_state_out);
const node_t *guards_choose_dirguard(uint8_t dir_purpose,
                                     circuit_guard_state_t **guard_state_out);

#if 1
/* XXXX NM I would prefer that all of this stuff be private to
@@ -554,6 +561,14 @@ STATIC unsigned entry_guards_note_guard_success(guard_selection_t *gs,
                                                unsigned old_state);
STATIC int entry_guard_has_higher_priority(entry_guard_t *a, entry_guard_t *b);
STATIC char *getinfo_helper_format_single_entry_guard(const entry_guard_t *e);

STATIC entry_guard_restriction_t *guard_create_exit_restriction(
                                                      const uint8_t *exit_id);

STATIC entry_guard_restriction_t *guard_create_dirserver_md_restriction(void);

STATIC void entry_guard_restriction_free(entry_guard_restriction_t *rst);

#endif /* defined(ENTRYNODES_PRIVATE) */

void remove_all_entry_guards_for_guard_selection(guard_selection_t *gs);
Loading