📄 dirserv.c
字号:
/* Return 1 if we should fetch new networkstatuses, descriptors, etc
* on the "mirror" schedule rather than the "client" schedule.
*/
int
directory_fetches_dir_info_early(or_options_t *options)
{
return directory_fetches_from_authorities(options);
}
/* Return 1 if we should fetch new networkstatuses, descriptors, etc
* on a very passive schedule -- waiting long enough for ordinary clients
* to probably have the info we want. These would include bridge users,
* and maybe others in the future e.g. if a Tor client uses another Tor
* client as a directory guard.
*/
int
directory_fetches_dir_info_later(or_options_t *options)
{
return options->UseBridges != 0;
}
/** Return 1 if we want to cache v2 dir info (each status file).
*/
int
directory_caches_v2_dir_info(or_options_t *options)
{
return options->DirPort != 0;
}
/** Return 1 if we want to keep descriptors, networkstatuses, etc around
* and we're willing to serve them to others. Else return 0.
*/
int
directory_caches_dir_info(or_options_t *options)
{
return options->BridgeRelay != 0 || options->DirPort != 0;
}
/** Return 1 if we want to allow remote people to ask us directory
* requests via the "begin_dir" interface, which doesn't require
* having any separate port open. */
int
directory_permits_begindir_requests(or_options_t *options)
{
return options->BridgeRelay != 0 || options->DirPort != 0;
}
/** Return 1 if we want to allow controllers to ask us directory
* requests via the controller interface, which doesn't require
* having any separate port open. */
int
directory_permits_controller_requests(or_options_t *options)
{
return options->DirPort != 0;
}
/** Return 1 if we have no need to fetch new descriptors. This generally
* happens when we're not a dir cache and we haven't built any circuits
* lately.
*/
int
directory_too_idle_to_fetch_descriptors(or_options_t *options, time_t now)
{
return !options->DirPort && !options->FetchUselessDescriptors &&
rep_hist_circbuilding_dormant(now);
}
/********************************************************************/
/* Used only by non-v1-auth dirservers: The v1 directory and
* runningrouters we'll serve when requested. */
static cached_dir_t *cached_directory = NULL;
static cached_dir_t cached_runningrouters = { NULL, NULL, 0, 0, 0, -1 };
/** Used for other dirservers' v2 network statuses. Map from hexdigest to
* cached_dir_t. */
static digestmap_t *cached_v2_networkstatus = NULL;
/** The v3 consensus network status that we're currently serving. */
static cached_dir_t *cached_v3_networkstatus = NULL;
/** Possibly replace the contents of <b>d</b> with the value of
* <b>directory</b> published on <b>when</b>, unless <b>when</b> is older than
* the last value, or too far in the future.
*
* Does not copy <b>directory</b>; frees it if it isn't used.
*/
static void
set_cached_dir(cached_dir_t *d, char *directory, time_t when)
{
time_t now = time(NULL);
if (when<=d->published) {
log_info(LD_DIRSERV, "Ignoring old directory; not caching.");
tor_free(directory);
} else if (when>=now+ROUTER_MAX_AGE_TO_PUBLISH) {
log_info(LD_DIRSERV, "Ignoring future directory; not caching.");
tor_free(directory);
} else {
/* if (when>d->published && when<now+ROUTER_MAX_AGE) */
log_debug(LD_DIRSERV, "Caching directory.");
tor_free(d->dir);
d->dir = directory;
d->dir_len = strlen(directory);
tor_free(d->dir_z);
if (tor_gzip_compress(&(d->dir_z), &(d->dir_z_len), d->dir, d->dir_len,
ZLIB_METHOD)) {
log_warn(LD_BUG,"Error compressing cached directory");
}
d->published = when;
}
}
/** Decrement the reference count on <b>d</b>, and free it if it no longer has
* any references. */
void
cached_dir_decref(cached_dir_t *d)
{
if (!d || --d->refcnt > 0)
return;
clear_cached_dir(d);
tor_free(d);
}
/** Allocate and return a new cached_dir_t containing the string <b>s</b>,
* published at <b>published</b>. */
cached_dir_t *
new_cached_dir(char *s, time_t published)
{
cached_dir_t *d = tor_malloc_zero(sizeof(cached_dir_t));
d->refcnt = 1;
d->dir = s;
d->dir_len = strlen(s);
d->published = published;
if (tor_gzip_compress(&(d->dir_z), &(d->dir_z_len), d->dir, d->dir_len,
ZLIB_METHOD)) {
log_warn(LD_BUG, "Error compressing directory");
}
return d;
}
/** Remove all storage held in <b>d</b>, but do not free <b>d</b> itself. */
static void
clear_cached_dir(cached_dir_t *d)
{
tor_free(d->dir);
tor_free(d->dir_z);
memset(d, 0, sizeof(cached_dir_t));
}
/** Free all storage held by the cached_dir_t in <b>d</b>. */
static void
_free_cached_dir(void *_d)
{
cached_dir_t *d = (cached_dir_t *)_d;
cached_dir_decref(d);
}
/** If we have no cached directory, or it is older than <b>published</b>,
* then replace it with <b>directory</b>, published at <b>published</b>.
*
* If <b>published</b> is too old, do nothing.
*
* If <b>is_running_routers</b>, this is really a v1 running_routers
* document rather than a v1 directory.
*/
void
dirserv_set_cached_directory(const char *directory, time_t published,
int is_running_routers)
{
time_t now = time(NULL);
if (is_running_routers) {
if (published >= now - MAX_V1_RR_AGE)
set_cached_dir(&cached_runningrouters, tor_strdup(directory), published);
} else {
if (published >= now - MAX_V1_DIRECTORY_AGE) {
cached_dir_decref(cached_directory);
cached_directory = new_cached_dir(tor_strdup(directory), published);
}
}
}
/** If <b>networkstatus</b> is non-NULL, we've just received a v2
* network-status for an authoritative directory with identity digest
* <b>identity</b> published at <b>published</b> -- store it so we can
* serve it to others.
*
* If <b>networkstatus</b> is NULL, remove the entry with the given
* identity fingerprint from the v2 cache.
*/
void
dirserv_set_cached_networkstatus_v2(const char *networkstatus,
const char *identity,
time_t published)
{
cached_dir_t *d, *old_d;
smartlist_t *trusted_dirs;
if (!cached_v2_networkstatus)
cached_v2_networkstatus = digestmap_new();
old_d = digestmap_get(cached_v2_networkstatus, identity);
if (!old_d && !networkstatus)
return;
if (networkstatus) {
if (!old_d || published > old_d->published) {
d = new_cached_dir(tor_strdup(networkstatus), published);
digestmap_set(cached_v2_networkstatus, identity, d);
if (old_d)
cached_dir_decref(old_d);
}
} else {
if (old_d) {
digestmap_remove(cached_v2_networkstatus, identity);
cached_dir_decref(old_d);
}
}
/* Now purge old entries. */
trusted_dirs = router_get_trusted_dir_servers();
if (digestmap_size(cached_v2_networkstatus) >
smartlist_len(trusted_dirs) + MAX_UNTRUSTED_NETWORKSTATUSES) {
/* We need to remove the oldest untrusted networkstatus. */
const char *oldest = NULL;
time_t oldest_published = TIME_MAX;
digestmap_iter_t *iter;
for (iter = digestmap_iter_init(cached_v2_networkstatus);
!digestmap_iter_done(iter);
iter = digestmap_iter_next(cached_v2_networkstatus, iter)) {
const char *ident;
void *val;
digestmap_iter_get(iter, &ident, &val);
d = val;
if (d->published < oldest_published &&
!router_digest_is_trusted_dir(ident)) {
oldest = ident;
oldest_published = d->published;
}
}
tor_assert(oldest);
d = digestmap_remove(cached_v2_networkstatus, oldest);
if (d)
cached_dir_decref(d);
}
}
/** Replace the v3 consensus networkstatus that we're serving with
* <b>networkstatus</b>, published at <b>published</b>. No validation is
* performed. */
void
dirserv_set_cached_networkstatus_v3(const char *networkstatus,
time_t published)
{
if (cached_v3_networkstatus)
cached_dir_decref(cached_v3_networkstatus);
cached_v3_networkstatus = new_cached_dir(
tor_strdup(networkstatus), published);
}
/** Remove any v2 networkstatus from the directory cache that was published
* before <b>cutoff</b>. */
void
dirserv_clear_old_networkstatuses(time_t cutoff)
{
if (!cached_v2_networkstatus)
return;
DIGESTMAP_FOREACH_MODIFY(cached_v2_networkstatus, id, cached_dir_t *, dir) {
if (dir->published < cutoff) {
char *fname;
fname = networkstatus_get_cache_filename(id);
if (file_status(fname) == FN_FILE) {
log_info(LD_DIR, "Removing too-old untrusted networkstatus in %s",
fname);
unlink(fname);
}
tor_free(fname);
cached_dir_decref(dir);
MAP_DEL_CURRENT(id);
}
} DIGESTMAP_FOREACH_END
}
/** Remove any v1 info from the directory cache that was published
* too long ago. */
void
dirserv_clear_old_v1_info(time_t now)
{
if (cached_directory &&
cached_directory->published < (now - MAX_V1_DIRECTORY_AGE)) {
cached_dir_decref(cached_directory);
cached_directory = NULL;
}
if (cached_runningrouters.published < (now - MAX_V1_RR_AGE)) {
clear_cached_dir(&cached_runningrouters);
}
}
/** Helper: If we're an authority for the right directory version (v1 or v2)
* (based on <b>auth_type</b>), try to regenerate
* auth_src as appropriate and return it, falling back to cache_src on
* failure. If we're a cache, simply return cache_src.
*/
static cached_dir_t *
dirserv_pick_cached_dir_obj(cached_dir_t *cache_src,
cached_dir_t *auth_src,
time_t dirty, cached_dir_t *(*regenerate)(void),
const char *name,
authority_type_t auth_type)
{
or_options_t *options = get_options();
int authority = (auth_type == V1_AUTHORITY && authdir_mode_v1(options)) ||
(auth_type == V2_AUTHORITY && authdir_mode_v2(options));
if (!authority || authdir_mode_bridge(options)) {
return cache_src;
} else {
/* We're authoritative. */
if (regenerate != NULL) {
if (dirty && dirty + DIR_REGEN_SLACK_TIME < time(NULL)) {
if (!(auth_src = regenerate())) {
log_err(LD_BUG, "Couldn't generate %s?", name);
exit(1);
}
} else {
log_info(LD_DIRSERV, "The %s is still clean; reusing.", name);
}
}
return auth_src ? auth_src : cache_src;
}
}
/** Return the most recently generated encoded signed v1 directory,
* generating a new one as necessary. If not a v1 authoritative directory
* may return NULL if no directory is yet cached. */
cached_dir_t *
dirserv_get_directory(void)
{
return dirserv_pick_cached_dir_obj(cached_directory, the_directory,
the_directory_is_dirty,
dirserv_regenerate_directory,
"server directory", V1_AUTHORITY);
}
/** Only called by v1 auth dirservers.
* Generate a fresh v1 directory; set the_directory and return a pointer
* to the new value.
*/
static cached_dir_t *
dirserv_regenerate_directory(void)
{
char *new_directory=NULL;
if (dirserv_dump_directory_to_string(&new_directory, get_identity_key())) {
log_warn(LD_BUG, "Error creating directory.");
tor_free(new_directory);
return NULL;
}
cached_dir_decref(the_directory);
the_directory = new_cached_dir(new_directory, time(NULL));
log_info(LD_DIRSERV,"New directory (size %d) has been built.",
(int)the_directory->dir_len);
log_debug(LD_DIRSERV,"New directory (size %d):\n%s",
(int)the_directory->dir_len, the_directory->dir);
the_directory_is_dirty = 0;
/* Save the directory to disk so we re-load it quickly on startup.
*/
dirserv_set_cached_directory(the_directory->dir, time(NULL), 0);
return the_directory;
}
/** Only called by v1 auth dirservers.
* Replace the current running-routers list with a newly generated one. */
static cached_dir_t *
generate_runningrouters(void)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -