diff --git a/src/or/dirserv.c b/src/or/dirserv.c
index 3970cdd631..f3ca5fda9e 100644
--- a/src/or/dirserv.c
+++ b/src/or/dirserv.c
@@ -17,6 +17,9 @@ const char dirserv_c_id[] =
#define ROUTER_ALLOW_SKEW (60*60*12) /* 12 hours */
/** How many seconds do we wait before regenerating the directory? */
#define DIR_REGEN_SLACK_TIME 30
+/** If we're a cache, keep this many networkstatuses around from non-trusted
+ * directory authorities. */
+#define MAX_UNTRUSTED_NETWORKSTATUSES 16
extern long stats_n_seconds_working;
@@ -984,28 +987,60 @@ dirserv_set_cached_directory(const char *directory, time_t published,
* the cache.
*/
void
-dirserv_set_cached_networkstatus_v2(const char *directory,
+dirserv_set_cached_networkstatus_v2(const char *networkstatus,
const char *identity,
time_t published)
{
cached_dir_t *d;
+ smartlist_t *trusted_dirs;
if (!cached_v2_networkstatus)
cached_v2_networkstatus = digestmap_new();
if (!(d = digestmap_get(cached_v2_networkstatus, identity))) {
- if (!directory)
+ if (!networkstatus)
return;
d = tor_malloc_zero(sizeof(cached_dir_t));
digestmap_set(cached_v2_networkstatus, identity, d);
}
tor_assert(d);
- if (directory) {
- set_cached_dir(d, tor_strdup(directory), published);
+ if (networkstatus) {
+ if (published > d->published) {
+ set_cached_dir(d, tor_strdup(networkstatus), published);
+ } else {
+ networkstatus_free(networkstatus);
+ }
} else {
free_cached_dir(d);
digestmap_remove(cached_v2_networkstatus, identity);
}
+
+ router_get_trusted_dir_servers(&trusted_dirs);
+ 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_get_trusteddirserver_by_digest(ident)) {
+ oldest = ident;
+ oldest_published = d->published;
+ }
+ }
+ tor_assert(oldest);
+ d = digestmap_remove(cached_v2_networkstatus, oldest);
+ if (d)
+ free_cached_dir(d);
+ }
}
/** Helper: If we're an authority for the right directory version (the
diff --git a/src/or/routerlist.c b/src/or/routerlist.c
index 44139d1369..d79d8931a7 100644
--- a/src/or/routerlist.c
+++ b/src/or/routerlist.c
@@ -1938,6 +1938,30 @@ _compare_networkstatus_published_on(const void **_a, const void **_b)
return 0;
}
+/** Add the parsed neworkstatus in ns (with original document in
+ * s to the disk cache (and the in-memory directory server cache) as
+ * appropriate. */
+static int
+add_networkstatus_to_cache(const char *s,
+ networkstatus_source_t source,
+ networkstatus_t *ns)
+{
+ if (source != NS_FROM_CACHE) {
+ char *fn = networkstatus_get_cache_filename(ns);
+ if (write_str_to_file(fn, s, 0)<0) {
+ notice(LD_FS, "Couldn't write cached network status to \"%s\"", fn);
+ }
+ tor_free(fn);
+ }
+
+ if (get_options()->DirPort)
+ dirserv_set_cached_networkstatus_v2(s,
+ ns->identity_digest,
+ ns->published_on);
+
+ return 0;
+}
+
/** How far in the future do we allow a network-status to get before removing
* it? (seconds) */
#define NETWORKSTATUS_ALLOW_SKEW (48*60*60)
@@ -1969,7 +1993,8 @@ router_set_networkstatus(const char *s, time_t arrived_at,
int i, found;
time_t now;
int skewed = 0;
- trusted_dir_server_t *trusted_dir;
+ trusted_dir_server_t *trusted_dir = NULL;
+ const char *source_desc = NULL;
char fp[HEX_DIGEST_LEN+1];
char published[ISO_TIME_LEN+1];
@@ -1978,12 +2003,18 @@ router_set_networkstatus(const char *s, time_t arrived_at,
warn(LD_DIR, "Couldn't parse network status.");
return -1;
}
+ base16_encode(fp, HEX_DIGEST_LEN+1, ns->identity_digest, DIGEST_LEN);
if (!(trusted_dir =
router_get_trusteddirserver_by_digest(ns->identity_digest))) {
info(LD_DIR, "Network status was signed, but not by an authoritative "
"directory we recognize.");
- networkstatus_free(ns);
- return -1;
+ if (!get_options()->DirPort) {
+ networkstatus_free(ns);
+ return 0;
+ }
+ source_desc = fp;
+ } else {
+ source_desc = trusted_dir->description;
}
now = time(NULL);
if (arrived_at > now)
@@ -1996,7 +2027,7 @@ router_set_networkstatus(const char *s, time_t arrived_at,
if (ns->published_on > now + NETWORKSTATUS_ALLOW_SKEW) {
warn(LD_GENERAL, "Network status from %s was published in the future "
"(%s GMT). Somebody is skewed here: check your clock. Not caching.",
- trusted_dir->description, published);
+ source_desc, published);
skewed = 1;
}
@@ -2009,8 +2040,6 @@ router_set_networkstatus(const char *s, time_t arrived_at,
return 0;
}
- base16_encode(fp, HEX_DIGEST_LEN+1, ns->identity_digest, DIGEST_LEN);
-
if (requested_fingerprints) {
if (smartlist_string_isin(requested_fingerprints, fp)) {
smartlist_string_remove(requested_fingerprints, fp);
@@ -2025,7 +2054,15 @@ router_set_networkstatus(const char *s, time_t arrived_at,
}
}
- if (source != NS_FROM_CACHE)
+ if (!trusted_dir) {
+ if (!skewed && get_options()->DirPort) {
+ add_networkstatus_to_cache(s, source, ns);
+ networkstatus_free(ns);
+ }
+ return 0;
+ }
+
+ if (source != NS_FROM_CACHE && trusted_dir)
trusted_dir->n_networkstatus_failures = 0;
found = 0;
@@ -2075,8 +2112,8 @@ router_set_networkstatus(const char *s, time_t arrived_at,
SMARTLIST_FOREACH(ns->entries, routerstatus_t *, rs,
{
- if (!router_get_by_descriptor_digest(rs->descriptor_digest))
- rs->need_to_mirror = 1;
+ if (!router_get_by_descriptor_digest(rs->descriptor_digest))
+ rs->need_to_mirror = 1;
});
info(LD_DIR, "Setting networkstatus %s %s (published %s)",
@@ -2087,21 +2124,11 @@ router_set_networkstatus(const char *s, time_t arrived_at,
smartlist_sort(networkstatus_list, _compare_networkstatus_published_on);
- if (source != NS_FROM_CACHE && !skewed) {
- char *fn = networkstatus_get_cache_filename(ns);
- if (write_str_to_file(fn, s, 0)<0) {
- notice(LD_FS, "Couldn't write cached network status to \"%s\"", fn);
- }
- tor_free(fn);
- }
+ if (!skewed)
+ add_networkstatus_to_cache(s, source, ns);
networkstatus_list_update_recent(now);
- if (get_options()->DirPort && !skewed)
- dirserv_set_cached_networkstatus_v2(s,
- ns->identity_digest,
- ns->published_on);
-
return 0;
}