⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 networkstatus.c

📁 关于tor匿名通信的源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
                                CONSENSUS_NETWORKSTATUS_MAX_DL_TRIES))
    return; /* We failed downloading a consensus too recently. */
  if (connection_get_by_type_purpose(CONN_TYPE_DIR,
                                     DIR_PURPOSE_FETCH_CONSENSUS))
    return; /* There's an in-progress download.*/

  if (consensus_waiting_for_certs) {
    /* XXXX020 make sure this doesn't delay sane downloads. */
    if (consensus_waiting_for_certs_set_at + DELAY_WHILE_FETCHING_CERTS > now)
      return; /* We're still getting certs for this one. */
    else {
      if (!consensus_waiting_for_certs_dl_failed) {
        download_status_failed(&consensus_dl_status, 0);
        consensus_waiting_for_certs_dl_failed=1;
      }
    }
  }

  log_info(LD_DIR, "Launching networkstatus consensus download.");
  directory_get_from_dirserver(DIR_PURPOSE_FETCH_CONSENSUS,
                               ROUTER_PURPOSE_GENERAL, NULL, 1);
}

/** Called when an attempt to download a consensus fails: note that the
 * failure occurred, and possibly retry. */
void
networkstatus_consensus_download_failed(int status_code)
{
  download_status_failed(&consensus_dl_status, status_code);
  /* Retry immediately, if appropriate. */
  update_consensus_networkstatus_downloads(time(NULL));
}

/** How long do we (as a cache) wait after a consensus becomes non-fresh
 * before trying to fetch another? */
#define CONSENSUS_MIN_SECONDS_BEFORE_CACHING 120

/** Update the time at which we'll consider replacing the current
 * consensus. */
void
update_consensus_networkstatus_fetch_time(time_t now)
{
  or_options_t *options = get_options();
  networkstatus_t *c = networkstatus_get_live_consensus(now);
  if (c) {
    long dl_interval;
    long interval = c->fresh_until - c->valid_after;
    time_t start;
    if (directory_fetches_dir_info_early(options)) {
      /* We want to cache the next one at some point after this one
       * is no longer fresh... */
      start = c->fresh_until + CONSENSUS_MIN_SECONDS_BEFORE_CACHING;
      /* But only in the first half-interval after that. */
      dl_interval = interval/2;
    } else {
      /* We're an ordinary client or a bridge. Give all the caches enough
       * time to download the consensus. */
      start = c->fresh_until + (interval*3)/4;
      /* But download the next one well before this one is expired. */
      dl_interval = ((c->valid_until - start) * 7 )/ 8;

      /* If we're a bridge user, make use of the numbers we just computed
       * to choose the rest of the interval *after* them. */
      if (directory_fetches_dir_info_later(options)) {
        /* Give all the *clients* enough time to download the consensus. */
        start = start + dl_interval + CONSENSUS_MIN_SECONDS_BEFORE_CACHING;
        /* But try to get it before ours actually expires. */
        dl_interval = (c->valid_until - start) -
                      CONSENSUS_MIN_SECONDS_BEFORE_CACHING;
      }
    }
    if (dl_interval < 1)
      dl_interval = 1;
    /* We must not try to replace c while it's still the most valid: */
    tor_assert(c->fresh_until < start);
    /* We must download the next one before c is invalid: */
    tor_assert(start+dl_interval < c->valid_until);
    time_to_download_next_consensus = start +crypto_rand_int((int)dl_interval);
    {
      char tbuf1[ISO_TIME_LEN+1];
      char tbuf2[ISO_TIME_LEN+1];
      char tbuf3[ISO_TIME_LEN+1];
      format_local_iso_time(tbuf1, c->fresh_until);
      format_local_iso_time(tbuf2, c->valid_until);
      format_local_iso_time(tbuf3, time_to_download_next_consensus);
      log_info(LD_DIR, "Live consensus %s the most recent until %s and will "
               "expire at %s; fetching the next one at %s.",
               (c->fresh_until > now) ? "will be" : "was",
               tbuf1, tbuf2, tbuf3);
    }
  } else {
    time_to_download_next_consensus = now;
    log_info(LD_DIR, "No live consensus; we should fetch one immediately.");
  }

}

/** Return 1 if there's a reason we shouldn't try any directory
 * fetches yet (e.g. we demand bridges and none are yet known).
 * Else return 0. */
int
should_delay_dir_fetches(or_options_t *options)
{
  if (options->UseBridges && !any_bridge_descriptors_known()) {
    log_info(LD_DIR, "delaying dir fetches (no running bridges known)");
    return 1;
  }
  return 0;
}

/** Launch requests for networkstatus documents and authority certificates as
 * appropriate. */
void
update_networkstatus_downloads(time_t now)
{
  or_options_t *options = get_options();
  if (should_delay_dir_fetches(options))
    return;
  if (directory_fetches_dir_info_early(options))
    update_v2_networkstatus_cache_downloads(now);
  update_consensus_networkstatus_downloads(now);
  update_certificate_downloads(now);
}

/** Launch requests as appropriate for missing directory authority
 * certificates. */
void
update_certificate_downloads(time_t now)
{
  if (consensus_waiting_for_certs)
    authority_certs_fetch_missing(consensus_waiting_for_certs, now);
  else
    authority_certs_fetch_missing(current_consensus, now);
}

/** Return the network status with a given identity digest. */
networkstatus_v2_t *
networkstatus_v2_get_by_digest(const char *digest)
{
  SMARTLIST_FOREACH(networkstatus_v2_list, networkstatus_v2_t *, ns,
    {
      if (!memcmp(ns->identity_digest, digest, DIGEST_LEN))
        return ns;
    });
  return NULL;
}

/** Return the most recent consensus that we have downloaded, or NULL if we
 * don't have one. */
networkstatus_t *
networkstatus_get_latest_consensus(void)
{
  return current_consensus;
}

/** Return the most recent consensus that we have downloaded, or NULL if it is
 * no longer live. */
networkstatus_t *
networkstatus_get_live_consensus(time_t now)
{
  if (current_consensus &&
      current_consensus->valid_after <= now &&
      now <= current_consensus->valid_until)
    return current_consensus;
  else
    return NULL;
}

/* XXXX020 remove this in favor of get_live_consensus. But actually,
 * leave something like it for bridge users, who need to not totally
 * lose if they spend a while fetching a new consensus. */
/** As networkstatus_get_live_consensus(), but is way more tolerant of expired
 * consensuses. */
networkstatus_t *
networkstatus_get_reasonably_live_consensus(time_t now)
{
#define REASONABLY_LIVE_TIME (24*60*60)
  if (current_consensus &&
      current_consensus->valid_after <= now &&
      now <= current_consensus->valid_until+REASONABLY_LIVE_TIME)
    return current_consensus;
  else
    return NULL;
}

/** Given two router status entries for the same router identity, return 1 if
 * if the contents have changed between them. Otherwise, return 0. */
static int
routerstatus_has_changed(const routerstatus_t *a, const routerstatus_t *b)
{
  tor_assert(!memcmp(a->identity_digest, b->identity_digest, DIGEST_LEN));

  return strcmp(a->nickname, b->nickname) ||
         memcmp(a->descriptor_digest, b->descriptor_digest, DIGEST_LEN) ||
         a->addr != b->addr ||
         a->or_port != b->or_port ||
         a->dir_port != b->dir_port ||
         a->is_authority != b->is_authority ||
         a->is_exit != b->is_exit ||
         a->is_stable != b->is_stable ||
         a->is_fast != b->is_fast ||
         a->is_running != b->is_running ||
         a->is_named != b->is_named ||
         a->is_unnamed != b->is_unnamed ||
         a->is_valid != b->is_valid ||
         a->is_v2_dir != b->is_v2_dir ||
         a->is_possible_guard != b->is_possible_guard ||
         a->is_bad_exit != b->is_bad_exit ||
         a->is_bad_directory != b->is_bad_directory ||
         a->is_hs_dir != b->is_hs_dir ||
         a->version_known != b->version_known ||
         a->version_supports_begindir != b->version_supports_begindir ||
         a->version_supports_extrainfo_upload !=
           b->version_supports_extrainfo_upload ||
         a->version_supports_conditional_consensus !=
           b->version_supports_conditional_consensus ||
         a->version_supports_v3_dir != b->version_supports_v3_dir;
}

/** Notify controllers of any router status entries that changed between
 * <b>old_c</b> and <b>new_c</b>. */
static void
notify_control_networkstatus_changed(const networkstatus_t *old_c,
                                     const networkstatus_t *new_c)
{
  smartlist_t *changed;
  if (old_c == new_c)
    return;
  if (!old_c) {
    control_event_networkstatus_changed(new_c->routerstatus_list);
    return;
  }
  changed = smartlist_create();

  SMARTLIST_FOREACH_JOIN(old_c->routerstatus_list, routerstatus_t *, rs_old,
                         new_c->routerstatus_list, routerstatus_t *, rs_new,
                         memcmp(rs_old->identity_digest,
                                rs_new->identity_digest, DIGEST_LEN),
                         smartlist_add(changed, rs_new)) {
    if (routerstatus_has_changed(rs_old, rs_new))
      smartlist_add(changed, rs_new);
  } SMARTLIST_FOREACH_JOIN_END(rs_old, rs_new);

  control_event_networkstatus_changed(changed);
  smartlist_free(changed);
}

/** Copy all the ancillary information (like router download status and so on)
 * from <b>old_c</b> to <b>new_c</b>. */
static void
networkstatus_copy_old_consensus_info(networkstatus_t *new_c,
                                      const networkstatus_t *old_c)
{
  if (old_c == new_c)
    return;
  if (!old_c || !smartlist_len(old_c->routerstatus_list))
    return;

  SMARTLIST_FOREACH_JOIN(old_c->routerstatus_list, routerstatus_t *, rs_old,
                         new_c->routerstatus_list, routerstatus_t *, rs_new,
                         memcmp(rs_old->identity_digest,
                                rs_new->identity_digest, DIGEST_LEN),
                         STMT_NIL) {
    /* Okay, so we're looking at the same identity. */
    rs_new->name_lookup_warned = rs_old->name_lookup_warned;
    rs_new->last_dir_503_at = rs_old->last_dir_503_at;

    if (!memcmp(rs_old->descriptor_digest, rs_new->descriptor_digest,
                DIGEST_LEN)) {
      /* And the same descriptor too! */
      memcpy(&rs_new->dl_status, &rs_old->dl_status,sizeof(download_status_t));
    }
  } SMARTLIST_FOREACH_JOIN_END(rs_old, rs_new);
}

/** Try to replace the current cached v3 networkstatus with the one in
 * <b>consensus</b>.  If we don't have enough certificates to validate it,
 * store it in consensus_waiting_for_certs and launch a certificate fetch.
 *
 * Return 0 on success, <0 on failure.  On failure, caller should increment
 * the failure count as appropriate.
 *
 * We return -1 for mild failures that don't need to be reported to the
 * user, and -2 for more serious problems.
 */
int
networkstatus_set_current_consensus(const char *consensus, unsigned flags)
{
  networkstatus_t *c;
  int r, result = -1;
  time_t now = time(NULL);
  char *unverified_fname = NULL, *consensus_fname = NULL;
  const unsigned from_cache = flags & NSSET_FROM_CACHE;
  const unsigned was_waiting_for_certs = flags & NSSET_WAS_WAITING_FOR_CERTS;
  const unsigned dl_certs = !(flags & NSSET_DONT_DOWNLOAD_CERTS);

  /* Make sure it's parseable. */
  c = networkstatus_parse_vote_from_string(consensus, NULL, 0);
  if (!c) {
    log_warn(LD_DIR, "Unable to parse networkstatus consensus");
    result = -2;
    goto done;
  }

  if (current_consensus &&
      !memcmp(c->networkstatus_digest, current_consensus->networkstatus_digest,
              DIGEST_LEN)) {
    /* We already have this one. That's a failure. */
    log_info(LD_DIR, "Got a consensus we already have");
    goto done;
  }

  if (current_consensus && c->valid_after <= current_consensus->valid_after) {
    /* We have a newer one.  There's no point in accepting this one,
     * even if it's great. */
    log_info(LD_DIR, "Got a consensus at least as old as the one we have");
    goto done;
  }

  consensus_fname = get_datadir_fname("cached-consensus");
  unverified_fname = get_datadir_fname("unverified-consensus");

  /* Make sure it's signed enough. */
  if ((r=networkstatus_check_consensus_signature(c, 1))<0) {
    if (r == -1) {
      /* Okay, so it _might_ be signed enough if we get more certificates. */
      if (!was_waiting_for_certs) {
        log_info(LD_DIR,
                 "Not enough certificates to check networkstatus consensus");
      }
      if (!current_consensus ||
          c->valid_after > current_consensus->valid_after) {
        if (consensus_waiting_for_certs)
          networkstatus_vote_free(consensus_waiting_for_certs);
        tor_free(consensus_waiting_for_certs_body);
        consensus_waiting_for_certs = c;
        c = NULL; /* Prevent free. */
        consensus_waiting_for_certs_body = tor_strdup(consensus);
        consensus_waiting_for_certs_set_at = now;
        consensus_waiting_for_certs_dl_failed = 0;
        if (!from_cache) {
          write_str_to_file(unverified_fname, consensus, 0);
        }
        if (dl_certs)
          authority_certs_fetch_missing(c, now);
        /* This case is not a success or a failure until we get the certs
         * or fail to get the certs. */
        result = 0;
      } else {
        /* Even if we had enough signatures, we'd never use this as the
         * latest consensus. */
        if (was_waiting_for_certs && from_cache)
          unlink(unverified_fname);
      }
      goto done;
    } else {

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -