2013-01-16 07:54:56 +01:00
|
|
|
/* Copyright (c) 2009-2013, The Tor Project, Inc. */
|
2009-08-24 18:51:33 +02:00
|
|
|
/* See LICENSE for licensing information */
|
|
|
|
|
|
|
|
#include "or.h"
|
2012-01-26 00:54:59 +01:00
|
|
|
#include "circuitbuild.h"
|
2010-07-22 10:22:51 +02:00
|
|
|
#include "config.h"
|
2010-05-11 23:20:33 +02:00
|
|
|
#include "directory.h"
|
2010-10-02 00:12:30 +02:00
|
|
|
#include "dirserv.h"
|
2012-10-15 20:48:34 +02:00
|
|
|
#include "entrynodes.h"
|
2010-07-23 20:04:12 +02:00
|
|
|
#include "microdesc.h"
|
2010-05-11 23:20:33 +02:00
|
|
|
#include "networkstatus.h"
|
2010-10-02 00:12:30 +02:00
|
|
|
#include "nodelist.h"
|
|
|
|
#include "policies.h"
|
2010-11-08 20:21:32 +01:00
|
|
|
#include "router.h"
|
2010-05-11 23:20:33 +02:00
|
|
|
#include "routerlist.h"
|
2010-10-02 00:12:30 +02:00
|
|
|
#include "routerparse.h"
|
2009-08-24 18:51:33 +02:00
|
|
|
|
2009-10-15 22:06:00 +02:00
|
|
|
/** A data structure to hold a bunch of cached microdescriptors. There are
|
|
|
|
* two active files in the cache: a "cache file" that we mmap, and a "journal
|
|
|
|
* file" that we append to. Periodically, we rebuild the cache file to hold
|
|
|
|
* only the microdescriptors that we want to keep */
|
2009-08-24 18:51:33 +02:00
|
|
|
struct microdesc_cache_t {
|
2009-10-15 22:06:00 +02:00
|
|
|
/** Map from sha256-digest to microdesc_t for every microdesc_t in the
|
|
|
|
* cache. */
|
2009-08-24 18:51:33 +02:00
|
|
|
HT_HEAD(microdesc_map, microdesc_t) map;
|
|
|
|
|
2009-10-15 22:06:00 +02:00
|
|
|
/** Name of the cache file. */
|
2009-08-24 18:51:33 +02:00
|
|
|
char *cache_fname;
|
2009-10-15 22:06:00 +02:00
|
|
|
/** Name of the journal file. */
|
2009-08-24 18:51:33 +02:00
|
|
|
char *journal_fname;
|
2009-10-15 22:06:00 +02:00
|
|
|
/** Mmap'd contents of the cache file, or NULL if there is none. */
|
2009-08-24 18:51:33 +02:00
|
|
|
tor_mmap_t *cache_content;
|
2009-10-15 22:06:00 +02:00
|
|
|
/** Number of bytes used in the journal file. */
|
2009-08-24 18:51:33 +02:00
|
|
|
size_t journal_len;
|
2010-09-21 22:16:56 +02:00
|
|
|
/** Number of bytes in descriptors removed as too old. */
|
|
|
|
size_t bytes_dropped;
|
2009-10-18 21:45:57 +02:00
|
|
|
|
|
|
|
/** Total bytes of microdescriptor bodies we have added to this cache */
|
|
|
|
uint64_t total_len_seen;
|
|
|
|
/** Total number of microdescriptors we have added to this cache */
|
|
|
|
unsigned n_seen;
|
2009-08-24 18:51:33 +02:00
|
|
|
};
|
|
|
|
|
2009-10-15 22:06:00 +02:00
|
|
|
/** Helper: computes a hash of <b>md</b> to place it in a hash table. */
|
2009-08-24 18:51:33 +02:00
|
|
|
static INLINE unsigned int
|
2012-10-12 18:22:13 +02:00
|
|
|
microdesc_hash_(microdesc_t *md)
|
2009-08-24 18:51:33 +02:00
|
|
|
{
|
2014-02-07 23:38:16 +01:00
|
|
|
return (unsigned) siphash24g(md->digest, sizeof(md->digest));
|
2009-08-24 18:51:33 +02:00
|
|
|
}
|
|
|
|
|
2009-10-15 22:06:00 +02:00
|
|
|
/** Helper: compares <b>a</b> and </b> for equality for hash-table purposes. */
|
2009-08-24 18:51:33 +02:00
|
|
|
static INLINE int
|
2012-10-12 18:22:13 +02:00
|
|
|
microdesc_eq_(microdesc_t *a, microdesc_t *b)
|
2009-08-24 18:51:33 +02:00
|
|
|
{
|
2011-05-11 22:27:27 +02:00
|
|
|
return tor_memeq(a->digest, b->digest, DIGEST256_LEN);
|
2009-08-24 18:51:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
HT_PROTOTYPE(microdesc_map, microdesc_t, node,
|
2012-10-12 18:22:13 +02:00
|
|
|
microdesc_hash_, microdesc_eq_);
|
2014-09-02 18:48:34 +02:00
|
|
|
HT_GENERATE2(microdesc_map, microdesc_t, node,
|
2012-10-12 18:22:13 +02:00
|
|
|
microdesc_hash_, microdesc_eq_, 0.6,
|
2014-09-02 18:48:34 +02:00
|
|
|
tor_reallocarray_, tor_free_)
|
2009-08-24 18:51:33 +02:00
|
|
|
|
2009-10-15 22:06:00 +02:00
|
|
|
/** Write the body of <b>md</b> into <b>f</b>, with appropriate annotations.
|
|
|
|
* On success, return the total number of bytes written, and set
|
|
|
|
* *<b>annotation_len_out</b> to the number of bytes written as
|
|
|
|
* annotations. */
|
2010-01-24 22:24:47 +01:00
|
|
|
static ssize_t
|
2013-03-11 19:37:44 +01:00
|
|
|
dump_microdescriptor(int fd, microdesc_t *md, size_t *annotation_len_out)
|
2009-08-24 18:51:33 +02:00
|
|
|
{
|
2010-01-24 22:24:47 +01:00
|
|
|
ssize_t r = 0;
|
2013-06-13 18:29:01 +02:00
|
|
|
ssize_t written;
|
2013-06-12 18:12:11 +02:00
|
|
|
if (md->body == NULL) {
|
|
|
|
*annotation_len_out = 0;
|
|
|
|
return 0;
|
|
|
|
}
|
2012-12-03 19:33:43 +01:00
|
|
|
/* XXXX drops unknown annotations. */
|
2009-08-24 18:51:33 +02:00
|
|
|
if (md->last_listed) {
|
|
|
|
char buf[ISO_TIME_LEN+1];
|
2009-10-14 22:05:08 +02:00
|
|
|
char annotation[ISO_TIME_LEN+32];
|
2009-08-24 18:51:33 +02:00
|
|
|
format_iso_time(buf, md->last_listed);
|
2009-10-14 22:05:08 +02:00
|
|
|
tor_snprintf(annotation, sizeof(annotation), "@last-listed %s\n", buf);
|
2013-03-11 19:37:44 +01:00
|
|
|
if (write_all(fd, annotation, strlen(annotation), 0) < 0) {
|
2012-05-24 17:07:01 +02:00
|
|
|
log_warn(LD_DIR,
|
|
|
|
"Couldn't write microdescriptor annotation: %s",
|
2013-03-11 19:37:44 +01:00
|
|
|
strerror(errno));
|
2012-05-24 17:07:01 +02:00
|
|
|
return -1;
|
|
|
|
}
|
2009-10-14 22:05:08 +02:00
|
|
|
r += strlen(annotation);
|
|
|
|
*annotation_len_out = r;
|
|
|
|
} else {
|
|
|
|
*annotation_len_out = 0;
|
2009-08-24 18:51:33 +02:00
|
|
|
}
|
|
|
|
|
2013-03-11 19:37:44 +01:00
|
|
|
md->off = tor_fd_getpos(fd);
|
|
|
|
written = write_all(fd, md->body, md->bodylen, 0);
|
2013-06-13 18:29:01 +02:00
|
|
|
if (written != (ssize_t)md->bodylen) {
|
2010-01-25 19:44:17 +01:00
|
|
|
log_warn(LD_DIR,
|
2013-06-13 18:29:01 +02:00
|
|
|
"Couldn't dump microdescriptor (wrote %ld out of %lu): %s",
|
|
|
|
(long)written, (unsigned long)md->bodylen,
|
2013-03-11 19:37:44 +01:00
|
|
|
strerror(errno));
|
2010-01-24 22:24:47 +01:00
|
|
|
return -1;
|
2010-01-24 20:58:30 +01:00
|
|
|
}
|
2009-10-14 22:05:08 +02:00
|
|
|
r += md->bodylen;
|
|
|
|
return r;
|
2009-08-24 18:51:33 +02:00
|
|
|
}
|
|
|
|
|
2009-10-15 22:06:00 +02:00
|
|
|
/** Holds a pointer to the current microdesc_cache_t object, or NULL if no
|
|
|
|
* such object has been allocated. */
|
2009-08-24 18:51:33 +02:00
|
|
|
static microdesc_cache_t *the_microdesc_cache = NULL;
|
|
|
|
|
2009-10-15 22:06:00 +02:00
|
|
|
/** Return a pointer to the microdescriptor cache, loading it if necessary. */
|
2009-08-24 18:51:33 +02:00
|
|
|
microdesc_cache_t *
|
|
|
|
get_microdesc_cache(void)
|
|
|
|
{
|
|
|
|
if (PREDICT_UNLIKELY(the_microdesc_cache==NULL)) {
|
|
|
|
microdesc_cache_t *cache = tor_malloc_zero(sizeof(microdesc_cache_t));
|
|
|
|
HT_INIT(microdesc_map, &cache->map);
|
|
|
|
cache->cache_fname = get_datadir_fname("cached-microdescs");
|
|
|
|
cache->journal_fname = get_datadir_fname("cached-microdescs.new");
|
|
|
|
microdesc_cache_reload(cache);
|
|
|
|
the_microdesc_cache = cache;
|
|
|
|
}
|
|
|
|
return the_microdesc_cache;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* There are three sources of microdescriptors:
|
2009-10-14 22:05:08 +02:00
|
|
|
1) Generated by us while acting as a directory authority.
|
2009-08-24 18:51:33 +02:00
|
|
|
2) Loaded from the cache on disk.
|
|
|
|
3) Downloaded.
|
|
|
|
*/
|
|
|
|
|
2009-10-15 22:06:00 +02:00
|
|
|
/** Decode the microdescriptors from the string starting at <b>s</b> and
|
2012-12-03 19:33:43 +01:00
|
|
|
* ending at <b>eos</b>, and store them in <b>cache</b>. If <b>no_save</b>,
|
2009-10-15 22:06:00 +02:00
|
|
|
* mark them as non-writable to disk. If <b>where</b> is SAVED_IN_CACHE,
|
|
|
|
* leave their bodies as pointers to the mmap'd cache. If where is
|
2013-03-11 21:20:20 +01:00
|
|
|
* <b>SAVED_NOWHERE</b>, do not allow annotations. If listed_at is not -1,
|
2010-05-11 23:20:33 +02:00
|
|
|
* set the last_listed field of every microdesc to listed_at. If
|
|
|
|
* requested_digests is non-null, then it contains a list of digests we mean
|
|
|
|
* to allow, so we should reject any non-requested microdesc with a different
|
2010-09-30 07:36:36 +02:00
|
|
|
* digest, and alter the list to contain only the digests of those microdescs
|
|
|
|
* we didn't find.
|
2011-02-24 15:47:33 +01:00
|
|
|
* Return a newly allocated list of the added microdescriptors, or NULL */
|
2009-08-24 18:51:33 +02:00
|
|
|
smartlist_t *
|
|
|
|
microdescs_add_to_cache(microdesc_cache_t *cache,
|
|
|
|
const char *s, const char *eos, saved_location_t where,
|
2010-05-11 23:20:33 +02:00
|
|
|
int no_save, time_t listed_at,
|
|
|
|
smartlist_t *requested_digests256)
|
2009-08-24 18:51:33 +02:00
|
|
|
{
|
|
|
|
smartlist_t *descriptors, *added;
|
|
|
|
const int allow_annotations = (where != SAVED_NOWHERE);
|
|
|
|
|
|
|
|
descriptors = microdescs_parse_from_string(s, eos,
|
|
|
|
allow_annotations,
|
2013-12-16 19:00:15 +01:00
|
|
|
where);
|
2013-03-11 21:20:20 +01:00
|
|
|
if (listed_at != (time_t)-1) {
|
2010-05-11 23:20:33 +02:00
|
|
|
SMARTLIST_FOREACH(descriptors, microdesc_t *, md,
|
|
|
|
md->last_listed = listed_at);
|
|
|
|
}
|
|
|
|
if (requested_digests256) {
|
2012-12-03 19:33:43 +01:00
|
|
|
digestmap_t *requested; /* XXXX actually we should just use a
|
2010-05-11 23:20:33 +02:00
|
|
|
digest256map */
|
|
|
|
requested = digestmap_new();
|
|
|
|
SMARTLIST_FOREACH(requested_digests256, const char *, cp,
|
|
|
|
digestmap_set(requested, cp, (void*)1));
|
|
|
|
SMARTLIST_FOREACH_BEGIN(descriptors, microdesc_t *, md) {
|
|
|
|
if (digestmap_get(requested, md->digest)) {
|
|
|
|
digestmap_set(requested, md->digest, (void*)2);
|
|
|
|
} else {
|
2012-12-03 19:33:43 +01:00
|
|
|
log_fn(LOG_PROTOCOL_WARN, LD_DIR, "Received non-requested microdesc");
|
2010-05-11 23:20:33 +02:00
|
|
|
microdesc_free(md);
|
|
|
|
SMARTLIST_DEL_CURRENT(descriptors, md);
|
|
|
|
}
|
|
|
|
} SMARTLIST_FOREACH_END(md);
|
|
|
|
SMARTLIST_FOREACH_BEGIN(requested_digests256, char *, cp) {
|
|
|
|
if (digestmap_get(requested, cp) == (void*)2) {
|
|
|
|
tor_free(cp);
|
|
|
|
SMARTLIST_DEL_CURRENT(requested_digests256, cp);
|
|
|
|
}
|
|
|
|
} SMARTLIST_FOREACH_END(cp);
|
|
|
|
digestmap_free(requested, NULL);
|
|
|
|
}
|
2009-08-24 18:51:33 +02:00
|
|
|
|
|
|
|
added = microdescs_add_list_to_cache(cache, descriptors, where, no_save);
|
|
|
|
smartlist_free(descriptors);
|
|
|
|
return added;
|
|
|
|
}
|
|
|
|
|
2012-12-03 19:33:43 +01:00
|
|
|
/** As microdescs_add_to_cache, but takes a list of microdescriptors instead of
|
2011-02-24 15:47:33 +01:00
|
|
|
* a string to decode. Frees any members of <b>descriptors</b> that it does
|
2009-10-15 22:06:00 +02:00
|
|
|
* not add. */
|
2009-08-24 18:51:33 +02:00
|
|
|
smartlist_t *
|
|
|
|
microdescs_add_list_to_cache(microdesc_cache_t *cache,
|
|
|
|
smartlist_t *descriptors, saved_location_t where,
|
|
|
|
int no_save)
|
|
|
|
{
|
|
|
|
smartlist_t *added;
|
|
|
|
open_file_t *open_file = NULL;
|
2013-03-11 19:37:44 +01:00
|
|
|
int fd = -1;
|
2009-08-24 18:51:33 +02:00
|
|
|
// int n_added = 0;
|
2010-01-24 22:24:47 +01:00
|
|
|
ssize_t size = 0;
|
2009-08-24 18:51:33 +02:00
|
|
|
|
|
|
|
if (where == SAVED_NOWHERE && !no_save) {
|
2013-03-11 19:37:44 +01:00
|
|
|
fd = start_writing_to_file(cache->journal_fname,
|
|
|
|
OPEN_FLAGS_APPEND|O_BINARY,
|
|
|
|
0600, &open_file);
|
|
|
|
if (fd < 0) {
|
2009-10-14 22:05:08 +02:00
|
|
|
log_warn(LD_DIR, "Couldn't append to journal in %s: %s",
|
|
|
|
cache->journal_fname, strerror(errno));
|
|
|
|
}
|
2009-08-24 18:51:33 +02:00
|
|
|
}
|
|
|
|
|
2012-01-18 21:53:30 +01:00
|
|
|
added = smartlist_new();
|
2009-08-24 18:51:33 +02:00
|
|
|
SMARTLIST_FOREACH_BEGIN(descriptors, microdesc_t *, md) {
|
|
|
|
microdesc_t *md2;
|
|
|
|
md2 = HT_FIND(microdesc_map, &cache->map, md);
|
|
|
|
if (md2) {
|
|
|
|
/* We already had this one. */
|
|
|
|
if (md2->last_listed < md->last_listed)
|
|
|
|
md2->last_listed = md->last_listed;
|
|
|
|
microdesc_free(md);
|
2011-05-03 23:03:49 +02:00
|
|
|
if (where != SAVED_NOWHERE)
|
|
|
|
cache->bytes_dropped += size;
|
2009-08-24 18:51:33 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Okay, it's a new one. */
|
2013-03-11 19:37:44 +01:00
|
|
|
if (fd >= 0) {
|
2009-10-19 01:30:46 +02:00
|
|
|
size_t annotation_len;
|
2013-03-11 19:37:44 +01:00
|
|
|
size = dump_microdescriptor(fd, md, &annotation_len);
|
2010-01-24 20:58:30 +01:00
|
|
|
if (size < 0) {
|
2012-12-03 19:33:43 +01:00
|
|
|
/* we already warned in dump_microdescriptor */
|
2012-05-24 17:07:01 +02:00
|
|
|
abort_writing_to_file(open_file);
|
2013-09-03 20:25:01 +02:00
|
|
|
fd = -1;
|
|
|
|
} else {
|
|
|
|
md->saved_location = SAVED_IN_JOURNAL;
|
|
|
|
cache->journal_len += size;
|
2010-01-24 20:58:30 +01:00
|
|
|
}
|
2009-08-24 18:51:33 +02:00
|
|
|
} else {
|
|
|
|
md->saved_location = where;
|
|
|
|
}
|
|
|
|
|
|
|
|
md->no_save = no_save;
|
|
|
|
|
|
|
|
HT_INSERT(microdesc_map, &cache->map, md);
|
2011-05-12 17:10:35 +02:00
|
|
|
md->held_in_map = 1;
|
2009-08-24 18:51:33 +02:00
|
|
|
smartlist_add(added, md);
|
2009-10-18 21:45:57 +02:00
|
|
|
++cache->n_seen;
|
|
|
|
cache->total_len_seen += md->bodylen;
|
2009-08-24 18:51:33 +02:00
|
|
|
} SMARTLIST_FOREACH_END(md);
|
|
|
|
|
2013-03-11 19:50:41 +01:00
|
|
|
if (fd >= 0) {
|
|
|
|
if (finish_writing_to_file(open_file) < 0) {
|
|
|
|
log_warn(LD_DIR, "Error appending to microdescriptor file: %s",
|
|
|
|
strerror(errno));
|
|
|
|
smartlist_clear(added);
|
|
|
|
return added;
|
|
|
|
}
|
|
|
|
}
|
2009-10-14 22:05:08 +02:00
|
|
|
|
2010-09-28 19:29:31 +02:00
|
|
|
{
|
|
|
|
networkstatus_t *ns = networkstatus_get_latest_consensus();
|
|
|
|
if (ns && ns->flavor == FLAV_MICRODESC)
|
|
|
|
SMARTLIST_FOREACH(added, microdesc_t *, md, nodelist_add_microdesc(md));
|
|
|
|
}
|
|
|
|
|
2010-11-08 20:21:32 +01:00
|
|
|
if (smartlist_len(added))
|
|
|
|
router_dir_info_changed();
|
|
|
|
|
2009-08-24 18:51:33 +02:00
|
|
|
return added;
|
|
|
|
}
|
|
|
|
|
2009-10-15 22:06:00 +02:00
|
|
|
/** Remove every microdescriptor in <b>cache</b>. */
|
2009-08-24 18:51:33 +02:00
|
|
|
void
|
|
|
|
microdesc_cache_clear(microdesc_cache_t *cache)
|
|
|
|
{
|
|
|
|
microdesc_t **entry, **next;
|
2014-03-18 20:39:02 +01:00
|
|
|
|
2009-08-24 18:51:33 +02:00
|
|
|
for (entry = HT_START(microdesc_map, &cache->map); entry; entry = next) {
|
2009-10-14 22:05:08 +02:00
|
|
|
microdesc_t *md = *entry;
|
2009-08-24 18:51:33 +02:00
|
|
|
next = HT_NEXT_RMV(microdesc_map, &cache->map, entry);
|
2011-05-12 17:10:35 +02:00
|
|
|
md->held_in_map = 0;
|
2009-10-14 22:05:08 +02:00
|
|
|
microdesc_free(md);
|
2009-08-24 18:51:33 +02:00
|
|
|
}
|
2009-10-14 22:05:08 +02:00
|
|
|
HT_CLEAR(microdesc_map, &cache->map);
|
2009-08-24 18:51:33 +02:00
|
|
|
if (cache->cache_content) {
|
2014-03-18 20:39:02 +01:00
|
|
|
int res = tor_munmap_file(cache->cache_content);
|
|
|
|
if (res != 0) {
|
|
|
|
log_warn(LD_FS,
|
|
|
|
"tor_munmap_file() failed clearing microdesc cache; "
|
|
|
|
"we are probably about to leak memory.");
|
|
|
|
/* TODO something smarter? */
|
|
|
|
}
|
2009-08-24 18:51:33 +02:00
|
|
|
cache->cache_content = NULL;
|
|
|
|
}
|
2009-10-18 21:45:57 +02:00
|
|
|
cache->total_len_seen = 0;
|
|
|
|
cache->n_seen = 0;
|
2011-05-03 23:28:28 +02:00
|
|
|
cache->bytes_dropped = 0;
|
2009-08-24 18:51:33 +02:00
|
|
|
}
|
|
|
|
|
2009-10-15 22:06:00 +02:00
|
|
|
/** Reload the contents of <b>cache</b> from disk. If it is empty, load it
|
|
|
|
* for the first time. Return 0 on success, -1 on failure. */
|
2009-08-24 18:51:33 +02:00
|
|
|
int
|
|
|
|
microdesc_cache_reload(microdesc_cache_t *cache)
|
|
|
|
{
|
|
|
|
struct stat st;
|
|
|
|
char *journal_content;
|
|
|
|
smartlist_t *added;
|
|
|
|
tor_mmap_t *mm;
|
|
|
|
int total = 0;
|
|
|
|
|
|
|
|
microdesc_cache_clear(cache);
|
|
|
|
|
|
|
|
mm = cache->cache_content = tor_mmap_file(cache->cache_fname);
|
|
|
|
if (mm) {
|
|
|
|
added = microdescs_add_to_cache(cache, mm->data, mm->data+mm->size,
|
2010-05-11 23:20:33 +02:00
|
|
|
SAVED_IN_CACHE, 0, -1, NULL);
|
2009-10-14 22:05:08 +02:00
|
|
|
if (added) {
|
|
|
|
total += smartlist_len(added);
|
|
|
|
smartlist_free(added);
|
|
|
|
}
|
2009-08-24 18:51:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
journal_content = read_file_to_str(cache->journal_fname,
|
|
|
|
RFTS_IGNORE_MISSING, &st);
|
|
|
|
if (journal_content) {
|
2011-05-03 22:22:31 +02:00
|
|
|
cache->journal_len = (size_t) st.st_size;
|
2009-08-24 18:51:33 +02:00
|
|
|
added = microdescs_add_to_cache(cache, journal_content,
|
|
|
|
journal_content+st.st_size,
|
2010-05-11 23:20:33 +02:00
|
|
|
SAVED_IN_JOURNAL, 0, -1, NULL);
|
2009-10-14 22:05:08 +02:00
|
|
|
if (added) {
|
|
|
|
total += smartlist_len(added);
|
|
|
|
smartlist_free(added);
|
|
|
|
}
|
2009-08-24 18:51:33 +02:00
|
|
|
tor_free(journal_content);
|
|
|
|
}
|
2012-09-04 00:50:27 +02:00
|
|
|
log_info(LD_DIR, "Reloaded microdescriptor cache. Found %d descriptors.",
|
|
|
|
total);
|
2011-05-03 23:03:49 +02:00
|
|
|
|
2011-05-03 23:28:28 +02:00
|
|
|
microdesc_cache_rebuild(cache, 0 /* don't force */);
|
2011-05-03 23:03:49 +02:00
|
|
|
|
2009-08-24 18:51:33 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-10-07 22:12:43 +02:00
|
|
|
/** By default, we remove any microdescriptors that have gone at least this
|
|
|
|
* long without appearing in a current consensus. */
|
2010-09-21 22:16:56 +02:00
|
|
|
#define TOLERATE_MICRODESC_AGE (7*24*60*60)
|
|
|
|
|
2010-10-07 22:12:43 +02:00
|
|
|
/** Remove all microdescriptors from <b>cache</b> that haven't been listed for
|
|
|
|
* a long time. Does not rebuild the cache on disk. If <b>cutoff</b> is
|
|
|
|
* positive, specifically remove microdescriptors that have been unlisted
|
|
|
|
* since <b>cutoff</b>. If <b>force</b> is true, remove microdescriptors even
|
|
|
|
* if we have no current live microdescriptor consensus.
|
|
|
|
*/
|
2010-09-21 22:16:56 +02:00
|
|
|
void
|
2010-10-07 21:28:54 +02:00
|
|
|
microdesc_cache_clean(microdesc_cache_t *cache, time_t cutoff, int force)
|
2010-09-21 22:16:56 +02:00
|
|
|
{
|
|
|
|
microdesc_t **mdp, *victim;
|
|
|
|
int dropped=0, kept=0;
|
|
|
|
size_t bytes_dropped = 0;
|
|
|
|
time_t now = time(NULL);
|
|
|
|
|
2010-10-07 22:12:43 +02:00
|
|
|
/* If we don't know a live consensus, don't believe last_listed values: we
|
|
|
|
* might be starting up after being down for a while. */
|
2010-10-07 21:28:54 +02:00
|
|
|
if (! force &&
|
|
|
|
! networkstatus_get_reasonably_live_consensus(now, FLAV_MICRODESC))
|
|
|
|
return;
|
2010-09-21 22:16:56 +02:00
|
|
|
|
2010-10-07 21:28:54 +02:00
|
|
|
if (cutoff <= 0)
|
|
|
|
cutoff = now - TOLERATE_MICRODESC_AGE;
|
2010-09-21 22:16:56 +02:00
|
|
|
|
|
|
|
for (mdp = HT_START(microdesc_map, &cache->map); mdp != NULL; ) {
|
2014-03-17 19:15:12 +01:00
|
|
|
const int is_old = (*mdp)->last_listed < cutoff;
|
|
|
|
const unsigned held_by_nodes = (*mdp)->held_by_nodes;
|
|
|
|
if (is_old && !held_by_nodes) {
|
2010-09-21 22:16:56 +02:00
|
|
|
++dropped;
|
|
|
|
victim = *mdp;
|
|
|
|
mdp = HT_NEXT_RMV(microdesc_map, &cache->map, mdp);
|
2011-05-12 17:10:35 +02:00
|
|
|
victim->held_in_map = 0;
|
2010-09-21 22:16:56 +02:00
|
|
|
bytes_dropped += victim->bodylen;
|
|
|
|
microdesc_free(victim);
|
|
|
|
} else {
|
2014-03-17 19:15:12 +01:00
|
|
|
if (is_old) {
|
|
|
|
/* It's old, but it has held_by_nodes set. That's not okay. */
|
|
|
|
/* Let's try to diagnose and fix #7164 . */
|
|
|
|
smartlist_t *nodes = nodelist_find_nodes_with_microdesc(*mdp);
|
|
|
|
const networkstatus_t *ns = networkstatus_get_latest_consensus();
|
2014-03-31 16:07:42 +02:00
|
|
|
long networkstatus_age = -1;
|
2014-05-06 19:03:24 +02:00
|
|
|
const int ht_badness = HT_REP_IS_BAD_(microdesc_map, &cache->map);
|
2014-03-17 19:15:12 +01:00
|
|
|
if (ns) {
|
|
|
|
networkstatus_age = now - ns->valid_after;
|
|
|
|
}
|
|
|
|
log_warn(LD_BUG, "Microdescriptor seemed very old "
|
|
|
|
"(last listed %d hours ago vs %d hour cutoff), but is still "
|
|
|
|
"marked as being held by %d node(s). I found %d node(s) "
|
2014-05-06 19:03:24 +02:00
|
|
|
"holding it. Current networkstatus is %ld hours old. "
|
|
|
|
"Hashtable badness is %d.",
|
2014-03-17 19:15:12 +01:00
|
|
|
(int)((now - (*mdp)->last_listed) / 3600),
|
|
|
|
(int)((now - cutoff) / 3600),
|
|
|
|
held_by_nodes,
|
|
|
|
smartlist_len(nodes),
|
2014-05-06 19:03:24 +02:00
|
|
|
networkstatus_age / 3600,
|
|
|
|
ht_badness);
|
2014-03-17 19:15:12 +01:00
|
|
|
|
|
|
|
SMARTLIST_FOREACH_BEGIN(nodes, const node_t *, node) {
|
|
|
|
const char *rs_match = "No RS";
|
|
|
|
const char *rs_present = "";
|
|
|
|
if (node->rs) {
|
|
|
|
if (tor_memeq(node->rs->descriptor_digest,
|
|
|
|
(*mdp)->digest, DIGEST256_LEN)) {
|
|
|
|
rs_match = "Microdesc digest in RS matches";
|
|
|
|
} else {
|
|
|
|
rs_match = "Microdesc digest in RS does match";
|
|
|
|
}
|
|
|
|
if (ns) {
|
|
|
|
/* This should be impossible, but let's see! */
|
|
|
|
rs_present = " RS not present in networkstatus.";
|
|
|
|
SMARTLIST_FOREACH(ns->routerstatus_list, routerstatus_t *,rs, {
|
|
|
|
if (rs == node->rs) {
|
|
|
|
rs_present = " RS okay in networkstatus.";
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
log_warn(LD_BUG, " [%d]: ID=%s. md=%p, rs=%p, ri=%p. %s.%s",
|
|
|
|
node_sl_idx,
|
|
|
|
hex_str(node->identity, DIGEST_LEN),
|
|
|
|
node->md, node->rs, node->ri, rs_match, rs_present);
|
|
|
|
} SMARTLIST_FOREACH_END(node);
|
|
|
|
smartlist_free(nodes);
|
|
|
|
(*mdp)->last_listed = now;
|
|
|
|
}
|
|
|
|
|
2010-09-21 22:16:56 +02:00
|
|
|
++kept;
|
|
|
|
mdp = HT_NEXT(microdesc_map, &cache->map, mdp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dropped) {
|
2012-06-26 16:30:11 +02:00
|
|
|
log_info(LD_DIR, "Removed %d/%d microdescriptors as old.",
|
|
|
|
dropped,dropped+kept);
|
2010-09-21 22:16:56 +02:00
|
|
|
cache->bytes_dropped += bytes_dropped;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-05-03 23:03:49 +02:00
|
|
|
static int
|
|
|
|
should_rebuild_md_cache(microdesc_cache_t *cache)
|
|
|
|
{
|
|
|
|
const size_t old_len =
|
|
|
|
cache->cache_content ? cache->cache_content->size : 0;
|
|
|
|
const size_t journal_len = cache->journal_len;
|
|
|
|
const size_t dropped = cache->bytes_dropped;
|
|
|
|
|
|
|
|
if (journal_len < 16384)
|
|
|
|
return 0; /* Don't bother, not enough has happened yet. */
|
|
|
|
if (dropped > (journal_len + old_len) / 3)
|
|
|
|
return 1; /* We could save 1/3 or more of the currently used space. */
|
|
|
|
if (journal_len > old_len / 2)
|
|
|
|
return 1; /* We should append to the regular file */
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-12-17 19:12:52 +01:00
|
|
|
/**
|
|
|
|
* Mark <b>md</b> as having no body, and release any storage previously held
|
|
|
|
* by its body.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
microdesc_wipe_body(microdesc_t *md)
|
|
|
|
{
|
|
|
|
if (!md)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (md->saved_location != SAVED_IN_CACHE)
|
|
|
|
tor_free(md->body);
|
|
|
|
|
|
|
|
md->off = 0;
|
|
|
|
md->saved_location = SAVED_NOWHERE;
|
|
|
|
md->body = NULL;
|
|
|
|
md->bodylen = 0;
|
|
|
|
md->no_save = 1;
|
|
|
|
}
|
|
|
|
|
2009-10-15 22:06:00 +02:00
|
|
|
/** Regenerate the main cache file for <b>cache</b>, clear the journal file,
|
|
|
|
* and update every microdesc_t in the cache with pointers to its new
|
2011-05-03 23:03:49 +02:00
|
|
|
* location. If <b>force</b> is true, do this unconditionally. If
|
|
|
|
* <b>force</b> is false, do it only if we expect to save space on disk. */
|
2009-08-24 18:51:33 +02:00
|
|
|
int
|
2011-05-03 23:03:49 +02:00
|
|
|
microdesc_cache_rebuild(microdesc_cache_t *cache, int force)
|
2009-08-24 18:51:33 +02:00
|
|
|
{
|
|
|
|
open_file_t *open_file;
|
2014-03-18 20:39:02 +01:00
|
|
|
int fd = -1, res;
|
2009-08-24 18:51:33 +02:00
|
|
|
microdesc_t **mdp;
|
|
|
|
smartlist_t *wrote;
|
2010-01-24 22:24:47 +01:00
|
|
|
ssize_t size;
|
2013-03-11 19:52:56 +01:00
|
|
|
off_t off = 0, off_real;
|
2009-10-14 22:05:08 +02:00
|
|
|
int orig_size, new_size;
|
2009-08-24 18:51:33 +02:00
|
|
|
|
2011-05-03 23:28:28 +02:00
|
|
|
if (cache == NULL) {
|
|
|
|
cache = the_microdesc_cache;
|
|
|
|
if (cache == NULL)
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-10-07 22:12:43 +02:00
|
|
|
/* Remove dead descriptors */
|
2010-10-07 21:28:54 +02:00
|
|
|
microdesc_cache_clean(cache, 0/*cutoff*/, 0/*force*/);
|
2010-09-21 22:16:56 +02:00
|
|
|
|
2011-05-03 23:03:49 +02:00
|
|
|
if (!force && !should_rebuild_md_cache(cache))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
log_info(LD_DIR, "Rebuilding the microdescriptor cache...");
|
|
|
|
|
2009-10-14 22:05:08 +02:00
|
|
|
orig_size = (int)(cache->cache_content ? cache->cache_content->size : 0);
|
|
|
|
orig_size += (int)cache->journal_len;
|
|
|
|
|
2013-03-11 19:37:44 +01:00
|
|
|
fd = start_writing_to_file(cache->cache_fname,
|
|
|
|
OPEN_FLAGS_REPLACE|O_BINARY,
|
|
|
|
0600, &open_file);
|
|
|
|
if (fd < 0)
|
2009-08-24 18:51:33 +02:00
|
|
|
return -1;
|
|
|
|
|
2012-01-18 21:53:30 +01:00
|
|
|
wrote = smartlist_new();
|
2009-08-24 18:51:33 +02:00
|
|
|
|
|
|
|
HT_FOREACH(mdp, microdesc_map, &cache->map) {
|
|
|
|
microdesc_t *md = *mdp;
|
2009-10-19 01:30:46 +02:00
|
|
|
size_t annotation_len;
|
2013-06-12 18:12:11 +02:00
|
|
|
if (md->no_save || !md->body)
|
2009-08-24 18:51:33 +02:00
|
|
|
continue;
|
|
|
|
|
2013-03-11 19:37:44 +01:00
|
|
|
size = dump_microdescriptor(fd, md, &annotation_len);
|
2010-01-24 20:58:30 +01:00
|
|
|
if (size < 0) {
|
2013-12-17 19:12:52 +01:00
|
|
|
microdesc_wipe_body(md);
|
2013-06-13 18:29:01 +02:00
|
|
|
|
|
|
|
/* rewind, in case it was a partial write. */
|
|
|
|
tor_fd_setpos(fd, off);
|
2010-01-24 20:58:30 +01:00
|
|
|
continue;
|
|
|
|
}
|
2010-10-07 22:12:43 +02:00
|
|
|
tor_assert(((size_t)size) == annotation_len + md->bodylen);
|
2009-10-14 22:05:08 +02:00
|
|
|
md->off = off + annotation_len;
|
|
|
|
off += size;
|
2013-03-11 19:52:56 +01:00
|
|
|
off_real = tor_fd_getpos(fd);
|
|
|
|
if (off_real != off) {
|
|
|
|
log_warn(LD_BUG, "Discontinuity in position in microdescriptor cache."
|
|
|
|
"By my count, I'm at "I64_FORMAT
|
|
|
|
", but I should be at "I64_FORMAT,
|
|
|
|
I64_PRINTF_ARG(off), I64_PRINTF_ARG(off_real));
|
2013-11-22 18:38:58 +01:00
|
|
|
if (off_real >= 0)
|
|
|
|
off = off_real;
|
2013-03-11 19:52:56 +01:00
|
|
|
}
|
2009-08-24 18:51:33 +02:00
|
|
|
if (md->saved_location != SAVED_IN_CACHE) {
|
|
|
|
tor_free(md->body);
|
|
|
|
md->saved_location = SAVED_IN_CACHE;
|
|
|
|
}
|
|
|
|
smartlist_add(wrote, md);
|
|
|
|
}
|
|
|
|
|
2013-06-12 18:04:33 +02:00
|
|
|
/* We must do this unmap _before_ we call finish_writing_to_file(), or
|
|
|
|
* windows will not actually replace the file. */
|
2014-03-18 20:39:02 +01:00
|
|
|
if (cache->cache_content) {
|
|
|
|
res = tor_munmap_file(cache->cache_content);
|
|
|
|
if (res != 0) {
|
|
|
|
log_warn(LD_FS,
|
|
|
|
"Failed to unmap old microdescriptor cache while rebuilding");
|
|
|
|
}
|
2014-03-31 17:42:49 +02:00
|
|
|
cache->cache_content = NULL;
|
2014-03-18 20:39:02 +01:00
|
|
|
}
|
2013-06-12 18:04:33 +02:00
|
|
|
|
2013-03-11 19:50:41 +01:00
|
|
|
if (finish_writing_to_file(open_file) < 0) {
|
|
|
|
log_warn(LD_DIR, "Error rebuilding microdescriptor cache: %s",
|
|
|
|
strerror(errno));
|
2013-06-12 18:04:33 +02:00
|
|
|
/* Okay. Let's prevent from making things worse elsewhere. */
|
|
|
|
cache->cache_content = NULL;
|
|
|
|
HT_FOREACH(mdp, microdesc_map, &cache->map) {
|
|
|
|
microdesc_t *md = *mdp;
|
|
|
|
if (md->saved_location == SAVED_IN_CACHE) {
|
2013-12-17 19:12:52 +01:00
|
|
|
microdesc_wipe_body(md);
|
2013-06-12 18:04:33 +02:00
|
|
|
}
|
|
|
|
}
|
2013-03-11 19:50:41 +01:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2009-08-24 18:51:33 +02:00
|
|
|
cache->cache_content = tor_mmap_file(cache->cache_fname);
|
2009-10-14 22:05:08 +02:00
|
|
|
|
2009-08-24 18:51:33 +02:00
|
|
|
if (!cache->cache_content && smartlist_len(wrote)) {
|
|
|
|
log_err(LD_DIR, "Couldn't map file that we just wrote to %s!",
|
|
|
|
cache->cache_fname);
|
2009-10-14 22:05:08 +02:00
|
|
|
smartlist_free(wrote);
|
2009-08-24 18:51:33 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
SMARTLIST_FOREACH_BEGIN(wrote, microdesc_t *, md) {
|
|
|
|
tor_assert(md->saved_location == SAVED_IN_CACHE);
|
|
|
|
md->body = (char*)cache->cache_content->data + md->off;
|
2010-10-07 22:04:24 +02:00
|
|
|
if (PREDICT_UNLIKELY(
|
2011-05-11 22:46:38 +02:00
|
|
|
md->bodylen < 9 || fast_memneq(md->body, "onion-key", 9) != 0)) {
|
2012-06-15 15:37:40 +02:00
|
|
|
/* XXXX once bug 2022 is solved, we can kill this block and turn it
|
2012-12-13 23:34:05 +01:00
|
|
|
* into just the tor_assert(fast_memeq) */
|
2010-10-07 22:04:24 +02:00
|
|
|
off_t avail = cache->cache_content->size - md->off;
|
|
|
|
char *bad_str;
|
|
|
|
tor_assert(avail >= 0);
|
|
|
|
bad_str = tor_strndup(md->body, MIN(128, (size_t)avail));
|
|
|
|
log_err(LD_BUG, "After rebuilding microdesc cache, offsets seem wrong. "
|
|
|
|
" At offset %d, I expected to find a microdescriptor starting "
|
|
|
|
" with \"onion-key\". Instead I got %s.",
|
|
|
|
(int)md->off, escaped(bad_str));
|
|
|
|
tor_free(bad_str);
|
2011-05-11 22:39:45 +02:00
|
|
|
tor_assert(fast_memeq(md->body, "onion-key", 9));
|
2010-10-07 22:04:24 +02:00
|
|
|
}
|
2009-10-14 22:05:08 +02:00
|
|
|
} SMARTLIST_FOREACH_END(md);
|
2009-08-24 18:51:33 +02:00
|
|
|
|
|
|
|
smartlist_free(wrote);
|
|
|
|
|
2009-10-14 22:05:08 +02:00
|
|
|
write_str_to_file(cache->journal_fname, "", 1);
|
|
|
|
cache->journal_len = 0;
|
2010-09-21 22:16:56 +02:00
|
|
|
cache->bytes_dropped = 0;
|
2009-10-14 22:05:08 +02:00
|
|
|
|
2011-04-26 04:23:35 +02:00
|
|
|
new_size = cache->cache_content ? (int)cache->cache_content->size : 0;
|
2009-10-14 22:05:08 +02:00
|
|
|
log_info(LD_DIR, "Done rebuilding microdesc cache. "
|
|
|
|
"Saved %d bytes; %d still used.",
|
|
|
|
orig_size-new_size, new_size);
|
|
|
|
|
2009-08-24 18:51:33 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-09-28 20:13:49 +02:00
|
|
|
/** Make sure that the reference count of every microdescriptor in cache is
|
|
|
|
* accurate. */
|
|
|
|
void
|
|
|
|
microdesc_check_counts(void)
|
|
|
|
{
|
|
|
|
microdesc_t **mdp;
|
|
|
|
if (!the_microdesc_cache)
|
|
|
|
return;
|
|
|
|
|
|
|
|
HT_FOREACH(mdp, microdesc_map, &the_microdesc_cache->map) {
|
|
|
|
microdesc_t *md = *mdp;
|
|
|
|
unsigned int found=0;
|
|
|
|
const smartlist_t *nodes = nodelist_get_list();
|
|
|
|
SMARTLIST_FOREACH(nodes, node_t *, node, {
|
|
|
|
if (node->md == md) {
|
|
|
|
++found;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
tor_assert(found == md->held_by_nodes);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-10-15 22:06:00 +02:00
|
|
|
/** Deallocate a single microdescriptor. Note: the microdescriptor MUST have
|
|
|
|
* previously been removed from the cache if it had ever been inserted. */
|
2009-08-24 18:51:33 +02:00
|
|
|
void
|
2013-03-13 15:42:58 +01:00
|
|
|
microdesc_free_(microdesc_t *md, const char *fname, int lineno)
|
2009-08-24 18:51:33 +02:00
|
|
|
{
|
2009-12-12 08:07:59 +01:00
|
|
|
if (!md)
|
|
|
|
return;
|
2011-05-12 17:10:35 +02:00
|
|
|
|
|
|
|
/* Make sure that the microdesc was really removed from the appropriate data
|
|
|
|
structures. */
|
|
|
|
if (md->held_in_map) {
|
|
|
|
microdesc_cache_t *cache = get_microdesc_cache();
|
|
|
|
microdesc_t *md2 = HT_FIND(microdesc_map, &cache->map, md);
|
|
|
|
if (md2 == md) {
|
2013-03-13 15:42:58 +01:00
|
|
|
log_warn(LD_BUG, "microdesc_free() called from %s:%d, but md was still "
|
|
|
|
"in microdesc_map", fname, lineno);
|
2011-05-12 17:10:35 +02:00
|
|
|
HT_REMOVE(microdesc_map, &cache->map, md);
|
|
|
|
} else {
|
2013-03-13 15:42:58 +01:00
|
|
|
log_warn(LD_BUG, "microdesc_free() called from %s:%d with held_in_map "
|
|
|
|
"set, but microdesc was not in the map.", fname, lineno);
|
2011-05-12 17:10:35 +02:00
|
|
|
}
|
|
|
|
tor_fragile_assert();
|
|
|
|
}
|
2011-09-28 19:29:01 +02:00
|
|
|
if (md->held_by_nodes) {
|
2014-05-06 19:03:24 +02:00
|
|
|
microdesc_cache_t *cache = get_microdesc_cache();
|
2011-05-12 17:10:35 +02:00
|
|
|
int found=0;
|
|
|
|
const smartlist_t *nodes = nodelist_get_list();
|
2014-05-06 19:03:24 +02:00
|
|
|
const int ht_badness = HT_REP_IS_BAD_(microdesc_map, &cache->map);
|
2011-05-12 17:10:35 +02:00
|
|
|
SMARTLIST_FOREACH(nodes, node_t *, node, {
|
|
|
|
if (node->md == md) {
|
|
|
|
++found;
|
|
|
|
node->md = NULL;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
if (found) {
|
2013-03-13 15:42:58 +01:00
|
|
|
log_warn(LD_BUG, "microdesc_free() called from %s:%d, but md was still "
|
2014-05-06 19:03:24 +02:00
|
|
|
"referenced %d node(s); held_by_nodes == %u, ht_badness == %d",
|
|
|
|
fname, lineno, found, md->held_by_nodes, ht_badness);
|
2011-05-12 17:10:35 +02:00
|
|
|
} else {
|
2013-03-13 15:42:58 +01:00
|
|
|
log_warn(LD_BUG, "microdesc_free() called from %s:%d with held_by_nodes "
|
2014-05-06 19:03:24 +02:00
|
|
|
"set to %u, but md was not referenced by any nodes. "
|
|
|
|
"ht_badness == %d",
|
|
|
|
fname, lineno, md->held_by_nodes, ht_badness);
|
2011-05-12 17:10:35 +02:00
|
|
|
}
|
|
|
|
tor_fragile_assert();
|
|
|
|
}
|
|
|
|
//tor_assert(md->held_in_map == 0);
|
2011-09-28 19:29:01 +02:00
|
|
|
//tor_assert(md->held_by_nodes == 0);
|
2011-05-12 17:10:35 +02:00
|
|
|
|
2009-08-24 18:51:33 +02:00
|
|
|
if (md->onion_pkey)
|
2012-01-18 21:53:30 +01:00
|
|
|
crypto_pk_free(md->onion_pkey);
|
2012-12-04 21:58:18 +01:00
|
|
|
tor_free(md->onion_curve25519_pkey);
|
2009-08-24 18:51:33 +02:00
|
|
|
if (md->body && md->saved_location != SAVED_IN_CACHE)
|
|
|
|
tor_free(md->body);
|
|
|
|
|
|
|
|
if (md->family) {
|
|
|
|
SMARTLIST_FOREACH(md->family, char *, cp, tor_free(cp));
|
|
|
|
smartlist_free(md->family);
|
|
|
|
}
|
2010-10-02 00:12:30 +02:00
|
|
|
short_policy_free(md->exit_policy);
|
2012-10-25 03:59:55 +02:00
|
|
|
short_policy_free(md->ipv6_exit_policy);
|
2009-08-24 18:51:33 +02:00
|
|
|
|
|
|
|
tor_free(md);
|
|
|
|
}
|
|
|
|
|
2009-10-15 22:06:00 +02:00
|
|
|
/** Free all storage held in the microdesc.c module. */
|
2009-10-14 22:05:08 +02:00
|
|
|
void
|
|
|
|
microdesc_free_all(void)
|
|
|
|
{
|
|
|
|
if (the_microdesc_cache) {
|
|
|
|
microdesc_cache_clear(the_microdesc_cache);
|
|
|
|
tor_free(the_microdesc_cache->cache_fname);
|
|
|
|
tor_free(the_microdesc_cache->journal_fname);
|
|
|
|
tor_free(the_microdesc_cache);
|
|
|
|
}
|
|
|
|
}
|
2009-10-18 21:45:57 +02:00
|
|
|
|
|
|
|
/** If there is a microdescriptor in <b>cache</b> whose sha256 digest is
|
|
|
|
* <b>d</b>, return it. Otherwise return NULL. */
|
|
|
|
microdesc_t *
|
|
|
|
microdesc_cache_lookup_by_digest256(microdesc_cache_t *cache, const char *d)
|
|
|
|
{
|
|
|
|
microdesc_t *md, search;
|
|
|
|
if (!cache)
|
|
|
|
cache = get_microdesc_cache();
|
|
|
|
memcpy(search.digest, d, DIGEST256_LEN);
|
|
|
|
md = HT_FIND(microdesc_map, &cache->map, &search);
|
|
|
|
return md;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Return the mean size of decriptors added to <b>cache</b> since it was last
|
|
|
|
* cleared. Used to estimate the size of large downloads. */
|
|
|
|
size_t
|
|
|
|
microdesc_average_size(microdesc_cache_t *cache)
|
|
|
|
{
|
|
|
|
if (!cache)
|
|
|
|
cache = get_microdesc_cache();
|
|
|
|
if (!cache->n_seen)
|
|
|
|
return 512;
|
|
|
|
return (size_t)(cache->total_len_seen / cache->n_seen);
|
|
|
|
}
|
|
|
|
|
2010-05-11 23:20:33 +02:00
|
|
|
/** Return a smartlist of all the sha256 digest of the microdescriptors that
|
|
|
|
* are listed in <b>ns</b> but not present in <b>cache</b>. Returns pointers
|
|
|
|
* to internals of <b>ns</b>; you should not free the members of the resulting
|
|
|
|
* smartlist. Omit all microdescriptors whose digest appear in <b>skip</b>. */
|
|
|
|
smartlist_t *
|
|
|
|
microdesc_list_missing_digest256(networkstatus_t *ns, microdesc_cache_t *cache,
|
|
|
|
int downloadable_only, digestmap_t *skip)
|
|
|
|
{
|
2012-01-18 21:53:30 +01:00
|
|
|
smartlist_t *result = smartlist_new();
|
2010-05-11 23:20:33 +02:00
|
|
|
time_t now = time(NULL);
|
|
|
|
tor_assert(ns->flavor == FLAV_MICRODESC);
|
|
|
|
SMARTLIST_FOREACH_BEGIN(ns->routerstatus_list, routerstatus_t *, rs) {
|
|
|
|
if (microdesc_cache_lookup_by_digest256(cache, rs->descriptor_digest))
|
|
|
|
continue;
|
|
|
|
if (downloadable_only &&
|
|
|
|
!download_status_is_ready(&rs->dl_status, now,
|
2013-05-16 12:08:48 +02:00
|
|
|
get_options()->TestingMicrodescMaxDownloadTries))
|
2010-05-11 23:20:33 +02:00
|
|
|
continue;
|
|
|
|
if (skip && digestmap_get(skip, rs->descriptor_digest))
|
|
|
|
continue;
|
2012-07-31 17:00:18 +02:00
|
|
|
if (tor_mem_is_zero(rs->descriptor_digest, DIGEST256_LEN))
|
2012-06-15 16:40:37 +02:00
|
|
|
continue;
|
2010-05-11 23:20:33 +02:00
|
|
|
/* XXXX Also skip if we're a noncache and wouldn't use this router.
|
|
|
|
* XXXX NM Microdesc
|
|
|
|
*/
|
|
|
|
smartlist_add(result, rs->descriptor_digest);
|
|
|
|
} SMARTLIST_FOREACH_END(rs);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2012-09-03 08:09:39 +02:00
|
|
|
/** Launch download requests for microdescriptors as appropriate.
|
2010-10-07 22:12:43 +02:00
|
|
|
*
|
|
|
|
* Specifically, we should launch download requests if we are configured to
|
|
|
|
* download mirodescriptors, and there are some microdescriptors listed the
|
|
|
|
* current microdesc consensus that we don't have, and either we never asked
|
|
|
|
* for them, or we failed to download them but we're willing to retry.
|
|
|
|
*/
|
2010-05-11 23:20:33 +02:00
|
|
|
void
|
|
|
|
update_microdesc_downloads(time_t now)
|
|
|
|
{
|
2011-06-14 19:01:38 +02:00
|
|
|
const or_options_t *options = get_options();
|
2010-05-11 23:20:33 +02:00
|
|
|
networkstatus_t *consensus;
|
|
|
|
smartlist_t *missing;
|
|
|
|
digestmap_t *pending;
|
|
|
|
|
2014-03-08 14:13:12 +01:00
|
|
|
if (should_delay_dir_fetches(options, NULL))
|
2010-05-11 23:20:33 +02:00
|
|
|
return;
|
|
|
|
if (directory_too_idle_to_fetch_descriptors(options, now))
|
|
|
|
return;
|
|
|
|
|
|
|
|
consensus = networkstatus_get_reasonably_live_consensus(now, FLAV_MICRODESC);
|
|
|
|
if (!consensus)
|
|
|
|
return;
|
|
|
|
|
2010-11-08 20:21:32 +01:00
|
|
|
if (!we_fetch_microdescriptors(options))
|
2010-05-11 23:20:33 +02:00
|
|
|
return;
|
|
|
|
|
|
|
|
pending = digestmap_new();
|
|
|
|
list_pending_microdesc_downloads(pending);
|
|
|
|
|
|
|
|
missing = microdesc_list_missing_digest256(consensus,
|
|
|
|
get_microdesc_cache(),
|
|
|
|
1,
|
|
|
|
pending);
|
|
|
|
digestmap_free(pending, NULL);
|
|
|
|
|
|
|
|
launch_descriptor_downloads(DIR_PURPOSE_FETCH_MICRODESC,
|
|
|
|
missing, NULL, now);
|
|
|
|
|
|
|
|
smartlist_free(missing);
|
|
|
|
}
|
2010-09-21 22:16:56 +02:00
|
|
|
|
2010-10-07 22:12:43 +02:00
|
|
|
/** For every microdescriptor listed in the current microdecriptor consensus,
|
|
|
|
* update its last_listed field to be at least as recent as the publication
|
|
|
|
* time of the current microdescriptor consensus.
|
|
|
|
*/
|
2010-09-21 22:16:56 +02:00
|
|
|
void
|
|
|
|
update_microdescs_from_networkstatus(time_t now)
|
|
|
|
{
|
|
|
|
microdesc_cache_t *cache = get_microdesc_cache();
|
|
|
|
microdesc_t *md;
|
|
|
|
networkstatus_t *ns =
|
|
|
|
networkstatus_get_reasonably_live_consensus(now, FLAV_MICRODESC);
|
|
|
|
|
|
|
|
if (! ns)
|
|
|
|
return;
|
|
|
|
|
|
|
|
tor_assert(ns->flavor == FLAV_MICRODESC);
|
|
|
|
|
|
|
|
SMARTLIST_FOREACH_BEGIN(ns->routerstatus_list, routerstatus_t *, rs) {
|
|
|
|
md = microdesc_cache_lookup_by_digest256(cache, rs->descriptor_digest);
|
|
|
|
if (md && ns->valid_after > md->last_listed)
|
|
|
|
md->last_listed = ns->valid_after;
|
|
|
|
} SMARTLIST_FOREACH_END(rs);
|
|
|
|
}
|
Initial conversion to use node_t throughout our codebase.
A node_t is an abstraction over routerstatus_t, routerinfo_t, and
microdesc_t. It should try to present a consistent interface to all
of them. There should be a node_t for a server whenever there is
* A routerinfo_t for it in the routerlist
* A routerstatus_t in the current_consensus.
(note that a microdesc_t alone isn't enough to make a node_t exist,
since microdescriptors aren't usable on their own.)
There are three ways to get a node_t right now: looking it up by ID,
looking it up by nickname, and iterating over the whole list of
microdescriptors.
All (or nearly all) functions that are supposed to return "a router"
-- especially those used in building connections and circuits --
should return a node_t, not a routerinfo_t or a routerstatus_t.
A node_t should hold all the *mutable* flags about a node. This
patch moves the is_foo flags from routerinfo_t into node_t. The
flags in routerstatus_t remain, but they get set from the consensus
and should not change.
Some other highlights of this patch are:
* Looking up routerinfo and routerstatus by nickname is now
unified and based on the "look up a node by nickname" function.
This tries to look only at the values from current consensus,
and not get confused by the routerinfo_t->is_named flag, which
could get set for other weird reasons. This changes the
behavior of how authorities (when acting as clients) deal with
nodes that have been listed by nickname.
* I tried not to artificially increase the size of the diff here
by moving functions around. As a result, some functions that
now operate on nodes are now in the wrong file -- they should
get moved to nodelist.c once this refactoring settles down.
This moving should happen as part of a patch that moves
functions AND NOTHING ELSE.
* Some old code is now left around inside #if 0/1 blocks, and
should get removed once I've verified that I don't want it
sitting around to see how we used to do things.
There are still some unimplemented functions: these are flagged
with "UNIMPLEMENTED_NODELIST()." I'll work on filling in the
implementation here, piece by piece.
I wish this patch could have been smaller, but there did not seem to
be any piece of it that was independent from the rest. Moving flags
forces many functions that once returned routerinfo_t * to return
node_t *, which forces their friends to change, and so on.
2010-09-29 21:00:41 +02:00
|
|
|
|
2010-11-08 20:21:32 +01:00
|
|
|
/** Return true iff we should prefer to use microdescriptors rather than
|
|
|
|
* routerdescs for building circuits. */
|
|
|
|
int
|
2011-06-14 19:01:38 +02:00
|
|
|
we_use_microdescriptors_for_circuits(const or_options_t *options)
|
2010-11-08 20:21:32 +01:00
|
|
|
{
|
|
|
|
int ret = options->UseMicrodescriptors;
|
|
|
|
if (ret == -1) {
|
|
|
|
/* UseMicrodescriptors is "auto"; we need to decide: */
|
2012-01-26 02:18:51 +01:00
|
|
|
/* If we are configured to use bridges and none of our bridges
|
2012-01-26 00:54:59 +01:00
|
|
|
* know what a microdescriptor is, the answer is no. */
|
2012-01-26 02:18:51 +01:00
|
|
|
if (options->UseBridges && !any_bridge_supports_microdescriptors())
|
2012-01-26 00:54:59 +01:00
|
|
|
return 0;
|
|
|
|
/* Otherwise, we decide that we'll use microdescriptors iff we are
|
|
|
|
* not a server, and we're not autofetching everything. */
|
|
|
|
/* XXX023 what does not being a server have to do with it? also there's
|
|
|
|
* a partitioning issue here where bridges differ from clients. */
|
2011-08-29 17:18:06 +02:00
|
|
|
ret = !server_mode(options) && !options->FetchUselessDescriptors;
|
2010-11-08 20:21:32 +01:00
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Return true iff we should try to download microdescriptors at all. */
|
|
|
|
int
|
2011-06-14 19:01:38 +02:00
|
|
|
we_fetch_microdescriptors(const or_options_t *options)
|
2010-11-08 20:21:32 +01:00
|
|
|
{
|
|
|
|
if (directory_caches_dir_info(options))
|
|
|
|
return 1;
|
2011-08-29 17:18:06 +02:00
|
|
|
if (options->FetchUselessDescriptors)
|
|
|
|
return 1;
|
2010-11-08 20:21:32 +01:00
|
|
|
return we_use_microdescriptors_for_circuits(options);
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Return true iff we should try to download router descriptors at all. */
|
|
|
|
int
|
2011-06-14 19:01:38 +02:00
|
|
|
we_fetch_router_descriptors(const or_options_t *options)
|
2010-11-08 20:21:32 +01:00
|
|
|
{
|
|
|
|
if (directory_caches_dir_info(options))
|
|
|
|
return 1;
|
2011-08-29 17:18:06 +02:00
|
|
|
if (options->FetchUselessDescriptors)
|
|
|
|
return 1;
|
2010-11-08 20:21:32 +01:00
|
|
|
return ! we_use_microdescriptors_for_circuits(options);
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Return the consensus flavor we actually want to use to build circuits. */
|
|
|
|
int
|
|
|
|
usable_consensus_flavor(void)
|
|
|
|
{
|
|
|
|
if (we_use_microdescriptors_for_circuits(get_options())) {
|
|
|
|
return FLAV_MICRODESC;
|
|
|
|
} else {
|
|
|
|
return FLAV_NS;
|
|
|
|
}
|
|
|
|
}
|
2011-05-06 03:56:52 +02:00
|
|
|
|