📄 networkstatus.c
字号:
/* Copyright (c) 2001 Matej Pfajfar.
* Copyright (c) 2001-2004, Roger Dingledine.
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
* Copyright (c) 2007-2008, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/* $Id$ */
const char networkstatus_c_id[] =
"$Id$";
/**
* \file networkstatus.c
* \brief Functions and structures for handling network status documents as a
* client or cache.
*/
#include "or.h"
/* For tracking v2 networkstatus documents. Only caches do this now. */
/** Map from descriptor digest of routers listed in the v2 networkstatus
* documents to download_status_t* */
static digestmap_t *v2_download_status_map = NULL;
/** Global list of all of the current v2 network_status documents that we know
* about. This list is kept sorted by published_on. */
static smartlist_t *networkstatus_v2_list = NULL;
/** True iff any member of networkstatus_v2_list has changed since the last
* time we called download_status_map_update_from_v2_networkstatus() */
static int networkstatus_v2_list_has_changed = 0;
/** Map from lowercase nickname to identity digest of named server, if any. */
static strmap_t *named_server_map = NULL;
/** Map from lowercase nickname to (void*)1 for all names that are listed
* as unnamed for some server in the consensus. */
static strmap_t *unnamed_server_map = NULL;
/** Most recently received and validated v3 consensus network status. */
static networkstatus_t *current_consensus = NULL;
/** A v3 consensus networkstatus that we've received, but which we don't
* have enough certificates to be happy about. */
static networkstatus_t *consensus_waiting_for_certs = NULL;
static char *consensus_waiting_for_certs_body = NULL;
static time_t consensus_waiting_for_certs_set_at = 0;
static int consensus_waiting_for_certs_dl_failed = 0;
/** The last time we tried to download a networkstatus, or 0 for "never". We
* use this to rate-limit download attempts for directory caches (including
* mirrors). Clients don't use this now. */
static time_t last_networkstatus_download_attempted = 0;
/** A time before which we shouldn't try to replace the current consensus:
* this will be at some point after the next consensus becomes valid, but
* before the current consensus becomes invalid. */
static time_t time_to_download_next_consensus = 0;
/** Download status for the current consensus networkstatus. */
static download_status_t consensus_dl_status = { 0, 0, DL_SCHED_CONSENSUS };
/** True iff we have logged a warning about this OR not being valid or
* not being named. */
static int have_warned_about_invalid_status = 0;
/** True iff we have logged a warning about this OR's version being older than
* listed by the authorities. */
static int have_warned_about_old_version = 0;
/** True iff we have logged a warning about this OR's version being newer than
* listed by the authorities. */
static int have_warned_about_new_version = 0;
static void download_status_map_update_from_v2_networkstatus(void);
static void routerstatus_list_update_named_server_map(void);
/** Forget that we've warned about anything networkstatus-related, so we will
* give fresh warnings if the same behavior happens again. */
void
networkstatus_reset_warnings(void)
{
if (current_consensus) {
SMARTLIST_FOREACH(current_consensus->routerstatus_list,
routerstatus_t *, rs,
rs->name_lookup_warned = 0);
}
have_warned_about_invalid_status = 0;
have_warned_about_old_version = 0;
have_warned_about_new_version = 0;
}
/** Reset the descriptor download failure count on all networkstatus docs, so
* that we can retry any long-failed documents immediately.
*/
void
networkstatus_reset_download_failures(void)
{
const smartlist_t *networkstatus_v2_list = networkstatus_get_v2_list();
SMARTLIST_FOREACH(networkstatus_v2_list, networkstatus_v2_t *, ns,
SMARTLIST_FOREACH(ns->entries, routerstatus_t *, rs,
{
if (!router_get_by_descriptor_digest(rs->descriptor_digest))
rs->need_to_mirror = 1;
}));;
download_status_reset(&consensus_dl_status);
if (v2_download_status_map) {
digestmap_iter_t *iter;
digestmap_t *map = v2_download_status_map;
const char *key;
void *val;
download_status_t *dls;
for (iter = digestmap_iter_init(map); !digestmap_iter_done(iter);
iter = digestmap_iter_next(map, iter) ) {
digestmap_iter_get(iter, &key, &val);
dls = val;
download_status_reset(dls);
}
}
}
/** Repopulate our list of network_status_t objects from the list cached on
* disk. Return 0 on success, -1 on failure. */
int
router_reload_v2_networkstatus(void)
{
smartlist_t *entries;
struct stat st;
char *s;
char *filename = get_datadir_fname("cached-status");
int maybe_delete = !directory_caches_v2_dir_info(get_options());
time_t now = time(NULL);
if (!networkstatus_v2_list)
networkstatus_v2_list = smartlist_create();
entries = tor_listdir(filename);
if (!entries) { /* dir doesn't exist */
tor_free(filename);
return 0;
} else if (!smartlist_len(entries) && maybe_delete) {
rmdir(filename);
tor_free(filename);
return 0;
}
tor_free(filename);
SMARTLIST_FOREACH(entries, const char *, fn, {
char buf[DIGEST_LEN];
if (maybe_delete) {
filename = get_datadir_fname2("cached-status", fn);
remove_file_if_very_old(filename, now);
tor_free(filename);
continue;
}
if (strlen(fn) != HEX_DIGEST_LEN ||
base16_decode(buf, sizeof(buf), fn, strlen(fn))) {
log_info(LD_DIR,
"Skipping cached-status file with unexpected name \"%s\"",fn);
continue;
}
filename = get_datadir_fname2("cached-status", fn);
s = read_file_to_str(filename, 0, &st);
if (s) {
if (router_set_networkstatus_v2(s, st.st_mtime, NS_FROM_CACHE,
NULL)<0) {
log_warn(LD_FS, "Couldn't load networkstatus from \"%s\"",filename);
}
tor_free(s);
}
tor_free(filename);
});
SMARTLIST_FOREACH(entries, char *, fn, tor_free(fn));
smartlist_free(entries);
networkstatus_v2_list_clean(time(NULL));
routers_update_all_from_networkstatus(time(NULL), 2);
return 0;
}
/** Read the cached v3 consensus networkstatus from the disk. */
int
router_reload_consensus_networkstatus(void)
{
char *filename;
char *s;
struct stat st;
or_options_t *options = get_options();
const unsigned int flags = NSSET_FROM_CACHE | NSSET_DONT_DOWNLOAD_CERTS;
/* XXXX020 Suppress warnings if cached consensus is bad. */
filename = get_datadir_fname("cached-consensus");
s = read_file_to_str(filename, RFTS_IGNORE_MISSING, NULL);
if (s) {
if (networkstatus_set_current_consensus(s, flags)) {
log_warn(LD_FS, "Couldn't load consensus networkstatus from \"%s\"",
filename);
}
tor_free(s);
}
tor_free(filename);
filename = get_datadir_fname("unverified-consensus");
s = read_file_to_str(filename, RFTS_IGNORE_MISSING, NULL);
if (s) {
if (networkstatus_set_current_consensus(s,
flags|NSSET_WAS_WAITING_FOR_CERTS)) {
log_info(LD_FS, "Couldn't load consensus networkstatus from \"%s\"",
filename);
}
tor_free(s);
}
tor_free(filename);
if (!current_consensus ||
(stat(options->FallbackNetworkstatusFile, &st)==0 &&
st.st_mtime > current_consensus->valid_after)) {
s = read_file_to_str(options->FallbackNetworkstatusFile,
RFTS_IGNORE_MISSING, NULL);
if (s) {
if (networkstatus_set_current_consensus(s, flags)) {
log_info(LD_FS, "Couldn't load consensus networkstatus from \"%s\"",
options->FallbackNetworkstatusFile);
} else {
log_notice(LD_FS,
"Loaded fallback consensus networkstatus from \"%s\"",
options->FallbackNetworkstatusFile);
}
tor_free(s);
}
}
if (!current_consensus) {
if (!named_server_map)
named_server_map = strmap_new();
if (!unnamed_server_map)
unnamed_server_map = strmap_new();
}
update_certificate_downloads(time(NULL));
routers_update_all_from_networkstatus(time(NULL), 3);
return 0;
}
/** Free all storage held by the routerstatus object <b>rs</b>. */
void
routerstatus_free(routerstatus_t *rs)
{
tor_free(rs);
}
/** Free all storage held by the networkstatus object <b>ns</b>. */
void
networkstatus_v2_free(networkstatus_v2_t *ns)
{
tor_free(ns->source_address);
tor_free(ns->contact);
if (ns->signing_key)
crypto_free_pk_env(ns->signing_key);
tor_free(ns->client_versions);
tor_free(ns->server_versions);
if (ns->entries) {
SMARTLIST_FOREACH(ns->entries, routerstatus_t *, rs,
routerstatus_free(rs));
smartlist_free(ns->entries);
}
tor_free(ns);
}
/** Clear all storage held in <b>ns</b>. */
void
networkstatus_vote_free(networkstatus_t *ns)
{
if (!ns)
return;
tor_free(ns->client_versions);
tor_free(ns->server_versions);
if (ns->known_flags) {
SMARTLIST_FOREACH(ns->known_flags, char *, c, tor_free(c));
smartlist_free(ns->known_flags);
}
if (ns->supported_methods) {
SMARTLIST_FOREACH(ns->supported_methods, char *, c, tor_free(c));
smartlist_free(ns->supported_methods);
}
if (ns->voters) {
SMARTLIST_FOREACH(ns->voters, networkstatus_voter_info_t *, voter,
{
tor_free(voter->nickname);
tor_free(voter->address);
tor_free(voter->contact);
tor_free(voter->signature);
tor_free(voter);
});
smartlist_free(ns->voters);
}
if (ns->cert)
authority_cert_free(ns->cert);
if (ns->routerstatus_list) {
if (ns->is_vote) {
SMARTLIST_FOREACH(ns->routerstatus_list, vote_routerstatus_t *, rs,
{
tor_free(rs->version);
tor_free(rs);
});
} else {
SMARTLIST_FOREACH(ns->routerstatus_list, routerstatus_t *, rs,
tor_free(rs));
}
smartlist_free(ns->routerstatus_list);
}
if (ns->desc_digest_map)
digestmap_free(ns->desc_digest_map, NULL);
memset(ns, 11, sizeof(*ns));
tor_free(ns);
}
/** Return the voter info from <b>vote</b> for the voter whose identity digest
* is <b>identity</b>, or NULL if no such voter is associated with
* <b>vote</b>. */
networkstatus_voter_info_t *
networkstatus_get_voter_by_id(networkstatus_t *vote,
const char *identity)
{
if (!vote || !vote->voters)
return NULL;
SMARTLIST_FOREACH(vote->voters, networkstatus_voter_info_t *, voter,
if (!memcmp(voter->identity_digest, identity, DIGEST_LEN))
return voter);
return NULL;
}
/** Check whether the signature on <b>voter</b> is correctly signed by
* the signing key of <b>cert</b>. Return -1 if <b>cert</b> doesn't match the
* signing key; otherwise set the good_signature or bad_signature flag on
* <b>voter</b>, and return 0. */
/* (private; exposed for testing.) */
int
networkstatus_check_voter_signature(networkstatus_t *consensus,
networkstatus_voter_info_t *voter,
authority_cert_t *cert)
{
char d[DIGEST_LEN];
char *signed_digest;
size_t signed_digest_len;
if (crypto_pk_get_digest(cert->signing_key, d)<0)
return -1;
if (memcmp(voter->signing_key_digest, d, DIGEST_LEN))
return -1;
signed_digest_len = crypto_pk_keysize(cert->signing_key);
signed_digest = tor_malloc(signed_digest_len);
if (crypto_pk_public_checksig(cert->signing_key,
signed_digest,
voter->signature,
voter->signature_len) != DIGEST_LEN ||
memcmp(signed_digest, consensus->networkstatus_digest, DIGEST_LEN)) {
log_warn(LD_DIR, "Got a bad signature on a networkstatus vote");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -