📄 networkstatus.c
字号:
} else {
rs->need_to_mirror = 1;
}
});
}
log_info(LD_DIR, "Setting networkstatus %s %s (published %s)",
source == NS_FROM_CACHE?"cached from":
((source == NS_FROM_DIR_BY_FP || source == NS_FROM_DIR_ALL) ?
"downloaded from":"generated for"),
trusted_dir->description, published);
networkstatus_v2_list_has_changed = 1;
router_dir_info_changed();
smartlist_sort(networkstatus_v2_list,
_compare_networkstatus_v2_published_on);
if (!skewed)
add_networkstatus_to_cache(s, source, ns);
return 0;
}
/** Remove all very-old network_status_t objects from memory and from the
* disk cache. */
void
networkstatus_v2_list_clean(time_t now)
{
int i;
if (!networkstatus_v2_list)
return;
for (i = 0; i < smartlist_len(networkstatus_v2_list); ++i) {
networkstatus_v2_t *ns = smartlist_get(networkstatus_v2_list, i);
char *fname = NULL;
if (ns->published_on + MAX_NETWORKSTATUS_AGE > now)
continue;
/* Okay, this one is too old. Remove it from the list, and delete it
* from the cache. */
smartlist_del(networkstatus_v2_list, i--);
fname = networkstatus_get_cache_filename(ns->identity_digest);
if (file_status(fname) == FN_FILE) {
log_info(LD_DIR, "Removing too-old networkstatus in %s", fname);
unlink(fname);
}
tor_free(fname);
if (directory_caches_v2_dir_info(get_options())) {
dirserv_set_cached_networkstatus_v2(NULL, ns->identity_digest, 0);
}
networkstatus_v2_free(ns);
router_dir_info_changed();
}
/* And now go through the directory cache for any cached untrusted
* networkstatuses and other network info. */
dirserv_clear_old_networkstatuses(now - MAX_NETWORKSTATUS_AGE);
dirserv_clear_old_v1_info(now);
}
/** Helper for bsearching a list of routerstatus_t pointers: compare a
* digest in the key to the identity digest of a routerstatus_t. */
static int
_compare_digest_to_routerstatus_entry(const void *_key, const void **_member)
{
const char *key = _key;
const routerstatus_t *rs = *_member;
return memcmp(key, rs->identity_digest, DIGEST_LEN);
}
/** Return the entry in <b>ns</b> for the identity digest <b>digest</b>, or
* NULL if none was found. */
routerstatus_t *
networkstatus_v2_find_entry(networkstatus_v2_t *ns, const char *digest)
{
return smartlist_bsearch(ns->entries, digest,
_compare_digest_to_routerstatus_entry);
}
/** Return the entry in <b>ns</b> for the identity digest <b>digest</b>, or
* NULL if none was found. */
routerstatus_t *
networkstatus_vote_find_entry(networkstatus_t *ns, const char *digest)
{
return smartlist_bsearch(ns->routerstatus_list, digest,
_compare_digest_to_routerstatus_entry);
}
/*XXXX make this static once functions are moved into this file. */
/** Search the routerstatuses in <b>ns</b> for one whose identity digest is
* <b>digest</b>. Return value and set *<b>found_out</b> as for
* smartlist_bsearch_idx(). */
int
networkstatus_vote_find_entry_idx(networkstatus_t *ns,
const char *digest, int *found_out)
{
return smartlist_bsearch_idx(ns->routerstatus_list, digest,
_compare_digest_to_routerstatus_entry,
found_out);
}
/** Return a list of the v2 networkstatus documents. */
const smartlist_t *
networkstatus_get_v2_list(void)
{
if (!networkstatus_v2_list)
networkstatus_v2_list = smartlist_create();
return networkstatus_v2_list;
}
/** Return the consensus view of the status of the router whose current
* <i>descriptor</i> digest is <b>digest</b>, or NULL if no such router is
* known. */
routerstatus_t *
router_get_consensus_status_by_descriptor_digest(const char *digest)
{
if (!current_consensus) return NULL;
if (!current_consensus->desc_digest_map) {
digestmap_t * m = current_consensus->desc_digest_map = digestmap_new();
SMARTLIST_FOREACH(current_consensus->routerstatus_list,
routerstatus_t *, rs,
{
digestmap_set(m, rs->descriptor_digest, rs);
});
}
return digestmap_get(current_consensus->desc_digest_map, digest);
}
/** Given the digest of a router descriptor, return its current download
* status, or NULL if the digest is unrecognized. */
download_status_t *
router_get_dl_status_by_descriptor_digest(const char *d)
{
routerstatus_t *rs;
if ((rs = router_get_consensus_status_by_descriptor_digest(d)))
return &rs->dl_status;
if (v2_download_status_map)
return digestmap_get(v2_download_status_map, d);
return NULL;
}
/** Return the consensus view of the status of the router whose identity
* digest is <b>digest</b>, or NULL if we don't know about any such router. */
routerstatus_t *
router_get_consensus_status_by_id(const char *digest)
{
if (!current_consensus)
return NULL;
return smartlist_bsearch(current_consensus->routerstatus_list, digest,
_compare_digest_to_routerstatus_entry);
}
/** Given a nickname (possibly verbose, possibly a hexadecimal digest), return
* the corresponding routerstatus_t, or NULL if none exists. Warn the
* user if <b>warn_if_unnamed</b> is set, and they have specified a router by
* nickname, but the Named flag isn't set for that router. */
routerstatus_t *
router_get_consensus_status_by_nickname(const char *nickname,
int warn_if_unnamed)
{
char digest[DIGEST_LEN];
routerstatus_t *best=NULL;
smartlist_t *matches=NULL;
const char *named_id=NULL;
if (!current_consensus || !nickname)
return NULL;
/* Is this name really a hexadecimal identity digest? */
if (nickname[0] == '$') {
if (base16_decode(digest, DIGEST_LEN, nickname+1, strlen(nickname+1))<0)
return NULL;
return networkstatus_vote_find_entry(current_consensus, digest);
} else if (strlen(nickname) == HEX_DIGEST_LEN &&
(base16_decode(digest, DIGEST_LEN, nickname, strlen(nickname))==0)) {
return networkstatus_vote_find_entry(current_consensus, digest);
}
/* Is there a server that is Named with this name? */
if (named_server_map)
named_id = strmap_get_lc(named_server_map, nickname);
if (named_id)
return networkstatus_vote_find_entry(current_consensus, named_id);
/* Okay; is this name listed as Unnamed? */
if (unnamed_server_map &&
strmap_get_lc(unnamed_server_map, nickname)) {
log_info(LD_GENERAL, "The name %s is listed as Unnamed; it is not the "
"canonical name of any server we know.", escaped(nickname));
return NULL;
}
/* This name is not canonical for any server; go through the list and
* see who it matches. */
/*XXXX021 This is inefficient. */
matches = smartlist_create();
SMARTLIST_FOREACH(current_consensus->routerstatus_list,
routerstatus_t *, lrs,
{
if (!strcasecmp(lrs->nickname, nickname)) {
if (lrs->is_named) {
tor_fragile_assert() /* This should never happen. */
smartlist_free(matches);
return lrs;
} else {
if (lrs->is_unnamed) {
tor_fragile_assert(); /* nor should this. */
smartlist_clear(matches);
best=NULL;
break;
}
smartlist_add(matches, lrs);
best = lrs;
}
}
});
if (smartlist_len(matches)>1 && warn_if_unnamed) {
int any_unwarned=0;
SMARTLIST_FOREACH(matches, routerstatus_t *, lrs,
{
if (! lrs->name_lookup_warned) {
lrs->name_lookup_warned=1;
any_unwarned=1;
}
});
if (any_unwarned) {
log_warn(LD_CONFIG,"There are multiple matches for the nickname \"%s\","
" but none is listed as named by the directory authorites. "
"Choosing one arbitrarily.", nickname);
}
} else if (warn_if_unnamed && best && !best->name_lookup_warned) {
char fp[HEX_DIGEST_LEN+1];
base16_encode(fp, sizeof(fp),
best->identity_digest, DIGEST_LEN);
log_warn(LD_CONFIG,
"When looking up a status, you specified a server \"%s\" by name, "
"but the directory authorities do not have any key registered for "
"this nickname -- so it could be used by any server, "
"not just the one you meant. "
"To make sure you get the same server in the future, refer to "
"it by key, as \"$%s\".", nickname, fp);
best->name_lookup_warned = 1;
}
smartlist_free(matches);
return best;
}
/** Return the identity digest that's mapped to officially by
* <b>nickname</b>. */
const char *
networkstatus_get_router_digest_by_nickname(const char *nickname)
{
if (!named_server_map)
return NULL;
return strmap_get_lc(named_server_map, nickname);
}
/** Return true iff <b>nickname</b> is disallowed from being the nickname
* of any server. */
int
networkstatus_nickname_is_unnamed(const char *nickname)
{
if (!unnamed_server_map)
return 0;
return strmap_get_lc(unnamed_server_map, nickname) != NULL;
}
/** How frequently do directory authorities re-download fresh networkstatus
* documents? */
#define AUTHORITY_NS_CACHE_INTERVAL (10*60)
/** How frequently do non-authority directory caches re-download fresh
* networkstatus documents? */
#define NONAUTHORITY_NS_CACHE_INTERVAL (60*60)
/** We are a directory server, and so cache network_status documents.
* Initiate downloads as needed to update them. For v2 authorities,
* this means asking each trusted directory for its network-status.
* For caches, this means asking a random v2 authority for all
* network-statuses.
*/
static void
update_v2_networkstatus_cache_downloads(time_t now)
{
int authority = authdir_mode_v2(get_options());
int interval =
authority ? AUTHORITY_NS_CACHE_INTERVAL : NONAUTHORITY_NS_CACHE_INTERVAL;
const smartlist_t *trusted_dir_servers = router_get_trusted_dir_servers();
if (last_networkstatus_download_attempted + interval >= now)
return;
last_networkstatus_download_attempted = now;
if (authority) {
/* An authority launches a separate connection for everybody. */
SMARTLIST_FOREACH(trusted_dir_servers, trusted_dir_server_t *, ds,
{
char resource[HEX_DIGEST_LEN+6]; /* fp/hexdigit.z\0 */
if (!(ds->type & V2_AUTHORITY))
continue;
if (router_digest_is_me(ds->digest))
continue;
if (connection_get_by_type_addr_port_purpose(
CONN_TYPE_DIR, ds->addr, ds->dir_port,
DIR_PURPOSE_FETCH_NETWORKSTATUS)) {
/* XXX020 the above dir_port won't be accurate if we're
* doing a tunneled conn. In that case it should be or_port.
* How to guess from here? Maybe make the function less general
* and have it know that it's looking for dir conns. -RD */
/* Only directory caches download v2 networkstatuses, and they
* don't use tunneled connections. I think it's okay to ignore
* this. */
continue;
}
strlcpy(resource, "fp/", sizeof(resource));
base16_encode(resource+3, sizeof(resource)-3, ds->digest, DIGEST_LEN);
strlcat(resource, ".z", sizeof(resource));
directory_initiate_command_routerstatus(
&ds->fake_status, DIR_PURPOSE_FETCH_NETWORKSTATUS,
ROUTER_PURPOSE_GENERAL,
0, /* Not private */
resource,
NULL, 0 /* No payload. */,
0 /* No I-M-S. */);
});
} else {
/* A non-authority cache launches one connection to a random authority. */
/* (Check whether we're currently fetching network-status objects.) */
if (!connection_get_by_type_purpose(CONN_TYPE_DIR,
DIR_PURPOSE_FETCH_NETWORKSTATUS))
directory_get_from_dirserver(DIR_PURPOSE_FETCH_NETWORKSTATUS,
ROUTER_PURPOSE_GENERAL, "all.z",1);
}
}
/** How many times will we try to fetch a consensus before we give up? */
#define CONSENSUS_NETWORKSTATUS_MAX_DL_TRIES 8
/** How long will we hang onto a possibly live consensus for which we're
* fetching certs before we check whether there is a better one? */
#define DELAY_WHILE_FETCHING_CERTS (20*60)
/** If we want to download a fresh consensus, launch a new download as
* appropriate. */
static void
update_consensus_networkstatus_downloads(time_t now)
{
or_options_t *options = get_options();
if (!networkstatus_get_live_consensus(now))
time_to_download_next_consensus = now; /* No live consensus? Get one now!*/
if (time_to_download_next_consensus > now)
return; /* Wait until the current consensus is older. */
if (authdir_mode_v3(options))
return; /* Authorities never fetch a consensus */
if (!download_status_is_ready(&consensus_dl_status, now,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -