aboutsummaryrefslogtreecommitdiff
path: root/src/or/microdesc.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/or/microdesc.c')
-rw-r--r--src/or/microdesc.c235
1 files changed, 222 insertions, 13 deletions
diff --git a/src/or/microdesc.c b/src/or/microdesc.c
index 7c67d51448..7ebb1e14fd 100644
--- a/src/or/microdesc.c
+++ b/src/or/microdesc.c
@@ -3,7 +3,14 @@
#include "or.h"
#include "config.h"
+#include "directory.h"
+#include "dirserv.h"
#include "microdesc.h"
+#include "networkstatus.h"
+#include "nodelist.h"
+#include "policies.h"
+#include "router.h"
+#include "routerlist.h"
#include "routerparse.h"
/** A data structure to hold a bunch of cached microdescriptors. There are
@@ -121,15 +128,19 @@ get_microdesc_cache(void)
* ending at <b>eos</b>, and store them in <b>cache</b>. If <b>no-save</b>,
* 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
- * <b>SAVED_NOWHERE</b>, do not allow annotations. Return a list of the added
- * microdescriptors. */
+ * <b>SAVED_NOWHERE</b>, do not allow annotations. If listed_at is positive,
+ * 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
+ * digest, and alter the list to contain only the digests of those microdescs
+ * we didn't find.
+ * Return a newly allocated list of the added microdescriptors, or NULL */
smartlist_t *
microdescs_add_to_cache(microdesc_cache_t *cache,
const char *s, const char *eos, saved_location_t where,
- int no_save)
+ int no_save, time_t listed_at,
+ smartlist_t *requested_digests256)
{
- /*XXXX need an argument that sets last_listed as appropriate. */
-
smartlist_t *descriptors, *added;
const int allow_annotations = (where != SAVED_NOWHERE);
const int copy_body = (where != SAVED_IN_CACHE);
@@ -137,6 +148,33 @@ microdescs_add_to_cache(microdesc_cache_t *cache,
descriptors = microdescs_parse_from_string(s, eos,
allow_annotations,
copy_body);
+ if (listed_at > 0) {
+ SMARTLIST_FOREACH(descriptors, microdesc_t *, md,
+ md->last_listed = listed_at);
+ }
+ if (requested_digests256) {
+ digestmap_t *requested; /* XXXX actuqlly we should just use a
+ 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 {
+ log_fn(LOG_PROTOCOL_WARN, LD_DIR, "Received non-requested microcdesc");
+ 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);
+ }
added = microdescs_add_list_to_cache(cache, descriptors, where, no_save);
smartlist_free(descriptors);
@@ -144,7 +182,7 @@ microdescs_add_to_cache(microdesc_cache_t *cache,
}
/* As microdescs_add_to_cache, but takes a list of micrdescriptors instead of
- * a string to encode. Frees any members of <b>descriptors</b> that it does
+ * a string to decode. Frees any members of <b>descriptors</b> that it does
* not add. */
smartlist_t *
microdescs_add_list_to_cache(microdesc_cache_t *cache,
@@ -208,6 +246,15 @@ microdescs_add_list_to_cache(microdesc_cache_t *cache,
if (f)
finish_writing_to_file(open_file); /*XXX Check me.*/
+ {
+ networkstatus_t *ns = networkstatus_get_latest_consensus();
+ if (ns && ns->flavor == FLAV_MICRODESC)
+ SMARTLIST_FOREACH(added, microdesc_t *, md, nodelist_add_microdesc(md));
+ }
+
+ if (smartlist_len(added))
+ router_dir_info_changed();
+
return added;
}
@@ -247,7 +294,7 @@ microdesc_cache_reload(microdesc_cache_t *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,
- SAVED_IN_CACHE, 0);
+ SAVED_IN_CACHE, 0, -1, NULL);
if (added) {
total += smartlist_len(added);
smartlist_free(added);
@@ -260,7 +307,7 @@ microdesc_cache_reload(microdesc_cache_t *cache)
cache->journal_len = (size_t) st.st_size;
added = microdescs_add_to_cache(cache, journal_content,
journal_content+st.st_size,
- SAVED_IN_JOURNAL, 0);
+ SAVED_IN_JOURNAL, 0, -1, NULL);
if (added) {
total += smartlist_len(added);
smartlist_free(added);
@@ -293,9 +340,11 @@ microdesc_cache_clean(microdesc_cache_t *cache, time_t cutoff, int force)
size_t bytes_dropped = 0;
time_t now = time(NULL);
- (void) force;
- /* In 0.2.2, we let this proceed unconditionally: only authorities have
- * microdesc caches. */
+ /* 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. */
+ if (! force &&
+ ! networkstatus_get_reasonably_live_consensus(now, FLAV_MICRODESC))
+ return;
if (cutoff <= 0)
cutoff = now - TOLERATE_MICRODESC_AGE;
@@ -390,6 +439,7 @@ microdesc_cache_rebuild(microdesc_cache_t *cache, int force)
/* log? return -1? die? coredump the universe? */
continue;
}
+ tor_assert(((size_t)size) == annotation_len + md->bodylen);
md->off = off + annotation_len;
off += size;
if (md->saved_location != SAVED_IN_CACHE) {
@@ -415,7 +465,21 @@ microdesc_cache_rebuild(microdesc_cache_t *cache, int force)
SMARTLIST_FOREACH_BEGIN(wrote, microdesc_t *, md) {
tor_assert(md->saved_location == SAVED_IN_CACHE);
md->body = (char*)cache->cache_content->data + md->off;
- tor_assert(fast_memeq(md->body, "onion-key", 9));
+ if (PREDICT_UNLIKELY(
+ md->bodylen < 9 || fast_memneq(md->body, "onion-key", 9) != 0)) {
+ /* XXXX023 once bug 2022 is solved, we can kill this block and turn it
+ * into just the tor_assert(!memcmp) */
+ 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);
+ tor_assert(fast_memeq(md->body, "onion-key", 9));
+ }
} SMARTLIST_FOREACH_END(md);
smartlist_free(wrote);
@@ -449,7 +513,7 @@ microdesc_free(microdesc_t *md)
SMARTLIST_FOREACH(md->family, char *, cp, tor_free(cp));
smartlist_free(md->family);
}
- tor_free(md->exitsummary);
+ short_policy_free(md->exit_policy);
tor_free(md);
}
@@ -491,3 +555,148 @@ microdesc_average_size(microdesc_cache_t *cache)
return (size_t)(cache->total_len_seen / cache->n_seen);
}
+/** 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)
+{
+ smartlist_t *result = smartlist_create();
+ 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,
+ MAX_MICRODESC_DOWNLOAD_FAILURES))
+ continue;
+ if (skip && digestmap_get(skip, rs->descriptor_digest))
+ continue;
+ if (tor_mem_is_zero(rs->descriptor_digest, DIGEST256_LEN))
+ continue; /* This indicates a bug somewhere XXXX023*/
+ /* 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;
+}
+
+/** Launch download requests for mircodescriptors as appropriate.
+ *
+ * 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.
+ */
+void
+update_microdesc_downloads(time_t now)
+{
+ or_options_t *options = get_options();
+ networkstatus_t *consensus;
+ smartlist_t *missing;
+ digestmap_t *pending;
+
+ if (should_delay_dir_fetches(options))
+ return;
+ if (directory_too_idle_to_fetch_descriptors(options, now))
+ return;
+
+ consensus = networkstatus_get_reasonably_live_consensus(now, FLAV_MICRODESC);
+ if (!consensus)
+ return;
+
+ if (!we_fetch_microdescriptors(options))
+ 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);
+}
+
+/** 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.
+ */
+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);
+}
+
+/** Return true iff we should prefer to use microdescriptors rather than
+ * routerdescs for building circuits. */
+int
+we_use_microdescriptors_for_circuits(or_options_t *options)
+{
+ int ret = options->UseMicrodescriptors;
+ if (ret == -1) {
+ /* UseMicrodescriptors is "auto"; we need to decide: */
+#if 0
+ /* So we decide that we'll use microdescriptors iff we are not a server */
+ ret = ! server_mode(options);
+#else
+ /* We don't use microdescs for now: not enough caches are running
+ * 0.2.3.1-alpha */
+ ret = 0;
+#endif
+ }
+ return ret;
+}
+
+/** Return true iff we should try to download microdescriptors at all. */
+int
+we_fetch_microdescriptors(or_options_t *options)
+{
+ if (directory_caches_dir_info(options))
+ return 1;
+ return we_use_microdescriptors_for_circuits(options);
+}
+
+/** Return true iff we should try to download router descriptors at all. */
+int
+we_fetch_router_descriptors(or_options_t *options)
+{
+ if (directory_caches_dir_info(options))
+ return 1;
+ 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;
+ }
+}
+