📄 routerlist.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 routerlist_c_id[] =
"$Id$";
/**
* \file routerlist.c
* \brief Code to
* maintain and access the global list of routerinfos for known
* servers.
**/
#include "or.h"
// #define DEBUG_ROUTERLIST
/****************************************************************************/
/* static function prototypes */
static routerstatus_t *router_pick_directory_server_impl(
authority_type_t auth, int flags);
static routerstatus_t *router_pick_trusteddirserver_impl(
authority_type_t auth, int flags);
static void mark_all_trusteddirservers_up(void);
static int router_nickname_matches(routerinfo_t *router, const char *nickname);
static void trusted_dir_server_free(trusted_dir_server_t *ds);
static void launch_router_descriptor_downloads(smartlist_t *downloadable,
time_t now);
static void update_consensus_router_descriptor_downloads(time_t now);
static int signed_desc_digest_is_recognized(signed_descriptor_t *desc);
static void update_router_have_minimum_dir_info(void);
static const char *signed_descriptor_get_body_impl(signed_descriptor_t *desc,
int with_annotations);
static void list_pending_downloads(digestmap_t *result,
int purpose, const char *prefix);
DECLARE_TYPED_DIGESTMAP_FNS(sdmap_, digest_sd_map_t, signed_descriptor_t)
DECLARE_TYPED_DIGESTMAP_FNS(rimap_, digest_ri_map_t, routerinfo_t)
DECLARE_TYPED_DIGESTMAP_FNS(eimap_, digest_ei_map_t, extrainfo_t)
#define SDMAP_FOREACH(map, keyvar, valvar) \
DIGESTMAP_FOREACH(sdmap_to_digestmap(map), keyvar, signed_descriptor_t *, \
valvar)
#define RIMAP_FOREACH(map, keyvar, valvar) \
DIGESTMAP_FOREACH(rimap_to_digestmap(map), keyvar, routerinfo_t *, valvar)
#define EIMAP_FOREACH(map, keyvar, valvar) \
DIGESTMAP_FOREACH(eimap_to_digestmap(map), keyvar, extrainfo_t *, valvar)
/****************************************************************************/
/** Global list of a trusted_dir_server_t object for each trusted directory
* server. */
static smartlist_t *trusted_dir_servers = NULL;
/** List of for a given authority, and download status for latest certificate.
*/
typedef struct cert_list_t {
download_status_t dl_status;
smartlist_t *certs;
} cert_list_t;
/** Map from v3 identity key digest to cert_list_t. */
static digestmap_t *trusted_dir_certs = NULL;
/** True iff any key certificate in at least one member of
* <b>trusted_dir_certs</b> has changed since we last flushed the
* certificates to disk. */
static int trusted_dir_servers_certs_changed = 0;
/** Global list of all of the routers that we know about. */
static routerlist_t *routerlist = NULL;
/** List of strings for nicknames we've already warned about and that are
* still unknown / unavailable. */
static smartlist_t *warned_nicknames = NULL;
/** The last time we tried to download any routerdesc, or 0 for "never". We
* use this to rate-limit download attempts when the number of routerdescs to
* download is low. */
static time_t last_routerdesc_download_attempted = 0;
/** Return the number of directory authorities whose type matches some bit set
* in <b>type</b> */
int
get_n_authorities(authority_type_t type)
{
int n = 0;
if (!trusted_dir_servers)
return 0;
SMARTLIST_FOREACH(trusted_dir_servers, trusted_dir_server_t *, ds,
if (ds->type & type)
++n);
return n;
}
#define get_n_v2_authorities() get_n_authorities(V2_AUTHORITY)
/** Helper: Return the cert_list_t for an authority whose authority ID is
* <b>id_digest</b>, allocating a new list if necessary. */
static cert_list_t *
get_cert_list(const char *id_digest)
{
cert_list_t *cl;
if (!trusted_dir_certs)
trusted_dir_certs = digestmap_new();
cl = digestmap_get(trusted_dir_certs, id_digest);
if (!cl) {
cl = tor_malloc_zero(sizeof(cert_list_t));
cl->certs = smartlist_create();
digestmap_set(trusted_dir_certs, id_digest, cl);
}
return cl;
}
/** Reload the cached v3 key certificates from the cached-certs file in
* the data directory. Return 0 on success, -1 on failure. */
int
trusted_dirs_reload_certs(void)
{
char *filename;
char *contents;
int r;
filename = get_datadir_fname("cached-certs");
contents = read_file_to_str(filename, RFTS_IGNORE_MISSING, NULL);
tor_free(filename);
if (!contents)
return 0;
r = trusted_dirs_load_certs_from_string(contents, 1, 1);
tor_free(contents);
return r;
}
/** Helper: return true iff we already have loaded the exact cert
* <b>cert</b>. */
static INLINE int
already_have_cert(authority_cert_t *cert)
{
cert_list_t *cl = get_cert_list(cert->cache_info.identity_digest);
SMARTLIST_FOREACH(cl->certs, authority_cert_t *, c,
{
if (!memcmp(c->cache_info.signed_descriptor_digest,
cert->cache_info.signed_descriptor_digest,
DIGEST_LEN))
return 1;
});
return 0;
}
/** Load a bunch of new key certificates from the string <b>contents</b>. If
* <b>from_store</b> is true, the certificates are from the cache, and we
* don't need to flush them to disk. If <b>from_store</b> is false, we need
* to flush any changed certificates to disk. Return 0 on success, -1 on
* failure. */
int
trusted_dirs_load_certs_from_string(const char *contents, int from_store,
int flush)
{
trusted_dir_server_t *ds;
const char *s, *eos;
for (s = contents; *s; s = eos) {
authority_cert_t *cert = authority_cert_parse_from_string(s, &eos);
cert_list_t *cl;
if (!cert)
break;
ds = trusteddirserver_get_by_v3_auth_digest(
cert->cache_info.identity_digest);
log_debug(LD_DIR, "Parsed certificate for %s",
ds ? ds->nickname : "unknown authority");
if (already_have_cert(cert)) {
/* we already have this one. continue. */
log_info(LD_DIR, "Skipping %s certificate for %s that we "
"already have.",
from_store ? "cached" : "downloaded",
ds ? ds->nickname : "??");
authority_cert_free(cert);
continue;
}
if (ds) {
log_info(LD_DIR, "Adding %s certificate for directory authority %s with "
"signing key %s", from_store ? "cached" : "downloaded",
ds->nickname, hex_str(cert->signing_key_digest,DIGEST_LEN));
} else {
log_info(LD_DIR, "Adding %s certificate for unrecognized directory "
"authority with signing key %s",
from_store ? "cached" : "downloaded",
hex_str(cert->signing_key_digest,DIGEST_LEN));
}
cl = get_cert_list(cert->cache_info.identity_digest);
smartlist_add(cl->certs, cert);
if (ds && cert->cache_info.published_on > ds->addr_current_at) {
/* Check to see whether we should update our view of the authority's
* address. */
if (cert->addr && cert->dir_port &&
(ds->addr != cert->addr ||
ds->dir_port != cert->dir_port)) {
char *a = tor_dup_addr(cert->addr);
log_notice(LD_DIR, "Updating address for directory authority %s "
"from %s:%d to %s:%d based on in certificate.",
ds->nickname, ds->address, (int)ds->dir_port,
a, cert->dir_port);
tor_free(a);
ds->addr = cert->addr;
ds->dir_port = cert->dir_port;
}
ds->addr_current_at = cert->cache_info.published_on;
}
if (!from_store)
trusted_dir_servers_certs_changed = 1;
}
if (flush)
trusted_dirs_flush_certs_to_disk();
networkstatus_note_certs_arrived();
return 0;
}
/** Save all v3 key certificates to the cached-certs file. */
void
trusted_dirs_flush_certs_to_disk(void)
{
char *filename;
smartlist_t *chunks;
if (!trusted_dir_servers_certs_changed || !trusted_dir_certs)
return;
chunks = smartlist_create();
DIGESTMAP_FOREACH(trusted_dir_certs, key, cert_list_t *, cl) {
SMARTLIST_FOREACH(cl->certs, authority_cert_t *, cert,
{
sized_chunk_t *c = tor_malloc(sizeof(sized_chunk_t));
c->bytes = cert->cache_info.signed_descriptor_body;
c->len = cert->cache_info.signed_descriptor_len;
smartlist_add(chunks, c);
});
} DIGESTMAP_FOREACH_END;
filename = get_datadir_fname("cached-certs");
if (write_chunks_to_file(filename, chunks, 0)) {
log_warn(LD_FS, "Error writing certificates to disk.");
}
tor_free(filename);
SMARTLIST_FOREACH(chunks, sized_chunk_t *, c, tor_free(c));
smartlist_free(chunks);
trusted_dir_servers_certs_changed = 0;
}
/** Remove all v3 authority certificates that have been superseded for more
* than 48 hours. (If the most recent cert was published more than 48 hours
* ago, then we aren't going to get any consensuses signed with older
* keys.) */
static void
trusted_dirs_remove_old_certs(void)
{
#define OLD_CERT_LIFETIME (48*60*60)
if (!trusted_dir_certs)
return;
DIGESTMAP_FOREACH(trusted_dir_certs, key, cert_list_t *, cl) {
authority_cert_t *newest = NULL;
SMARTLIST_FOREACH(cl->certs, authority_cert_t *, cert,
if (!newest || (cert->cache_info.published_on >
newest->cache_info.published_on))
newest = cert);
SMARTLIST_FOREACH(cl->certs, authority_cert_t *, cert,
if (newest && (newest->cache_info.published_on >
cert->cache_info.published_on + OLD_CERT_LIFETIME)) {
SMARTLIST_DEL_CURRENT(cl->certs, cert);
authority_cert_free(cert);
trusted_dir_servers_certs_changed = 1;
});
} DIGESTMAP_FOREACH_END;
#undef OLD_CERT_LIFETIME
trusted_dirs_flush_certs_to_disk();
}
/** Return the newest v3 authority certificate whose v3 authority identity key
* has digest <b>id_digest</b>. Return NULL if no such authority is known,
* or it has no certificate. */
authority_cert_t *
authority_cert_get_newest_by_id(const char *id_digest)
{
cert_list_t *cl;
authority_cert_t *best = NULL;
if (!trusted_dir_certs ||
!(cl = digestmap_get(trusted_dir_certs, id_digest)))
return NULL;
SMARTLIST_FOREACH(cl->certs, authority_cert_t *, cert,
{
if (!best || cert->cache_info.published_on > best->cache_info.published_on)
best = cert;
});
return best;
}
/** Return the newest v3 authority certificate whose directory signing key has
* giest <sk_digest</b>. Return NULL if no such certificate is known.
*/
authority_cert_t *
authority_cert_get_by_sk_digest(const char *sk_digest)
{
if (!trusted_dir_certs)
return NULL;
DIGESTMAP_FOREACH(trusted_dir_certs, key, cert_list_t *, cl) {
SMARTLIST_FOREACH(cl->certs, authority_cert_t *, cert,
{
if (!memcmp(cert->signing_key_digest, sk_digest, DIGEST_LEN))
return cert;
});
} DIGESTMAP_FOREACH_END;
return NULL;
}
/** Return the v3 authority certificate with signing key matching
* <b>sk_digest</b>, for the authority with identity digest <b>id_digest</b>.
* Return NULL if no such authority is known. */
authority_cert_t *
authority_cert_get_by_digests(const char *id_digest,
const char *sk_digest)
{
cert_list_t *cl;
if (!trusted_dir_certs ||
!(cl = digestmap_get(trusted_dir_certs, id_digest)))
return NULL;
SMARTLIST_FOREACH(cl->certs, authority_cert_t *, cert,
if (!memcmp(cert->signing_key_digest, sk_digest, DIGEST_LEN))
return cert; );
return NULL;
}
/** Add every known authority_cert_t to <b>certs_out</b>. */
void
authority_cert_get_all(smartlist_t *certs_out)
{
tor_assert(certs_out);
if (!trusted_dir_certs)
return;
DIGESTMAP_FOREACH(trusted_dir_certs, key, cert_list_t *, cl) {
SMARTLIST_FOREACH(cl->certs, authority_cert_t *, c,
smartlist_add(certs_out, c));
} DIGESTMAP_FOREACH_END;
}
/** Called when an attempt to download a certificate with the authority with
* ID <b>id_digest</b> fails with HTTP response code <b>status</b>: remember
* the failure, so we don't try again immediately. */
void
authority_cert_dl_failed(const char *id_digest, int status)
{
cert_list_t *cl;
if (!trusted_dir_certs ||
!(cl = digestmap_get(trusted_dir_certs, id_digest)))
return;
download_status_failed(&cl->dl_status, status);
}
/** How many times will we try to fetch a certificate before giving up? */
#define MAX_CERT_DL_FAILURES 8
/** Try to download any v3 authority certificates that we may be missing. If
* <b>status</b> is provided, try to get all the ones that were used to sign
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -