|
@@ -20,6 +20,7 @@
|
|
|
#include "dirserv.h"
|
|
|
#include "dirvote.h"
|
|
|
#include "main.h"
|
|
|
+#include "microdesc.h"
|
|
|
#include "networkstatus.h"
|
|
|
#include "relay.h"
|
|
|
#include "router.h"
|
|
@@ -44,8 +45,19 @@ static strmap_t *named_server_map = NULL;
|
|
|
* as unnamed for some server in the consensus. */
|
|
|
static strmap_t *unnamed_server_map = NULL;
|
|
|
|
|
|
-/** Most recently received and validated v3 consensus network status. */
|
|
|
-static networkstatus_t *current_consensus = NULL;
|
|
|
+/** Most recently received and validated v3 consensus network status,
|
|
|
+ * of whichever type we are using for our own circuits. This will be the same
|
|
|
+ * as one of current_ns_consensus or current_md_consensus.
|
|
|
+ */
|
|
|
+#define current_consensus current_ns_consensus
|
|
|
+
|
|
|
+/** Most recently received and validated v3 "ns"-flavored consensus network
|
|
|
+ * status. */
|
|
|
+static networkstatus_t *current_ns_consensus = NULL;
|
|
|
+
|
|
|
+/** Most recently received and validated v3 "microdec"-flavored consensus
|
|
|
+ * network status. */
|
|
|
+static networkstatus_t *current_md_consensus = NULL;
|
|
|
|
|
|
/** A v3 consensus networkstatus that we've received, but which we don't
|
|
|
* have enough certificates to be happy about. */
|
|
@@ -271,6 +283,7 @@ router_reload_consensus_networkstatus(void)
|
|
|
update_certificate_downloads(time(NULL));
|
|
|
|
|
|
routers_update_all_from_networkstatus(time(NULL), 3);
|
|
|
+ update_microdescs_from_networkstatus(time(NULL));
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
@@ -967,21 +980,25 @@ networkstatus_get_v2_list(void)
|
|
|
}
|
|
|
|
|
|
/** Return the consensus view of the status of the router whose current
|
|
|
- * <i>descriptor</i> digest is <b>digest</b>, or NULL if no such router is
|
|
|
- * known. */
|
|
|
+ * <i>descriptor</i> digest in <b>consensus</b> is <b>digest</b>, or NULL if
|
|
|
+ * no such router is known. */
|
|
|
routerstatus_t *
|
|
|
-router_get_consensus_status_by_descriptor_digest(const char *digest)
|
|
|
+router_get_consensus_status_by_descriptor_digest(networkstatus_t *consensus,
|
|
|
+ const char *digest)
|
|
|
{
|
|
|
- if (!current_consensus) return NULL;
|
|
|
- if (!current_consensus->desc_digest_map) {
|
|
|
- digestmap_t * m = current_consensus->desc_digest_map = digestmap_new();
|
|
|
- SMARTLIST_FOREACH(current_consensus->routerstatus_list,
|
|
|
+ if (!consensus)
|
|
|
+ consensus = current_consensus;
|
|
|
+ if (!consensus)
|
|
|
+ return NULL;
|
|
|
+ if (!consensus->desc_digest_map) {
|
|
|
+ digestmap_t *m = consensus->desc_digest_map = digestmap_new();
|
|
|
+ SMARTLIST_FOREACH(consensus->routerstatus_list,
|
|
|
routerstatus_t *, rs,
|
|
|
{
|
|
|
digestmap_set(m, rs->descriptor_digest, rs);
|
|
|
});
|
|
|
}
|
|
|
- return digestmap_get(current_consensus->desc_digest_map, digest);
|
|
|
+ return digestmap_get(consensus->desc_digest_map, digest);
|
|
|
}
|
|
|
|
|
|
/** Given the digest of a router descriptor, return its current download
|
|
@@ -990,7 +1007,10 @@ download_status_t *
|
|
|
router_get_dl_status_by_descriptor_digest(const char *d)
|
|
|
{
|
|
|
routerstatus_t *rs;
|
|
|
- if ((rs = router_get_consensus_status_by_descriptor_digest(d)))
|
|
|
+ if (!current_ns_consensus)
|
|
|
+ return NULL;
|
|
|
+ if ((rs = router_get_consensus_status_by_descriptor_digest(
|
|
|
+ current_ns_consensus, d)))
|
|
|
return &rs->dl_status;
|
|
|
if (v2_download_status_map)
|
|
|
return digestmap_get(v2_download_status_map, d);
|
|
@@ -1199,6 +1219,25 @@ update_v2_networkstatus_cache_downloads(time_t now)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+/** DOCDOC */
|
|
|
+static int
|
|
|
+we_want_to_fetch_flavor(or_options_t *options, int flavor)
|
|
|
+{
|
|
|
+ if (flavor < 0 || flavor > N_CONSENSUS_FLAVORS) {
|
|
|
+ /* This flavor is crazy; we don't want it */
|
|
|
+ /*XXXX handle unrecognized flavors later */
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ if (authdir_mode_v3(options) || directory_caches_dir_info(options)) {
|
|
|
+ /* We want to serve all flavors to others, regardless if we would use
|
|
|
+ * it ourselves. */
|
|
|
+ return 1;
|
|
|
+ }
|
|
|
+ /* Otherwise, we want the flavor only if we want to use it to build
|
|
|
+ * circuits. */
|
|
|
+ return (flavor == USABLE_CONSENSUS_FLAVOR);
|
|
|
+}
|
|
|
+
|
|
|
/** How many times will we try to fetch a consensus before we give up? */
|
|
|
#define CONSENSUS_NETWORKSTATUS_MAX_DL_TRIES 8
|
|
|
/** How long will we hang onto a possibly live consensus for which we're
|
|
@@ -1211,48 +1250,65 @@ static void
|
|
|
update_consensus_networkstatus_downloads(time_t now)
|
|
|
{
|
|
|
int i;
|
|
|
+ or_options_t *options = get_options();
|
|
|
+
|
|
|
if (!networkstatus_get_live_consensus(now))
|
|
|
time_to_download_next_consensus = now; /* No live consensus? Get one now!*/
|
|
|
if (time_to_download_next_consensus > now)
|
|
|
return; /* Wait until the current consensus is older. */
|
|
|
- /* XXXXNM Microdescs: may need to download more types. */
|
|
|
- if (!download_status_is_ready(&consensus_dl_status[FLAV_NS], now,
|
|
|
- CONSENSUS_NETWORKSTATUS_MAX_DL_TRIES))
|
|
|
- return; /* We failed downloading a consensus too recently. */
|
|
|
- if (connection_get_by_type_purpose(CONN_TYPE_DIR,
|
|
|
- DIR_PURPOSE_FETCH_CONSENSUS))
|
|
|
- return; /* There's an in-progress download.*/
|
|
|
|
|
|
for (i=0; i < N_CONSENSUS_FLAVORS; ++i) {
|
|
|
- consensus_waiting_for_certs_t *waiting = &consensus_waiting_for_certs[i];
|
|
|
+ /* XXXX need some way to download unknown flavors if we are caching. */
|
|
|
+ const char *resource;
|
|
|
+ consensus_waiting_for_certs_t *waiting;
|
|
|
+
|
|
|
+ if (! we_want_to_fetch_flavor(options, i))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ resource = i==FLAV_NS ? NULL : networkstatus_get_flavor_name(i);
|
|
|
+
|
|
|
+ if (!download_status_is_ready(&consensus_dl_status[i], now,
|
|
|
+ CONSENSUS_NETWORKSTATUS_MAX_DL_TRIES))
|
|
|
+ continue; /* We failed downloading a consensus too recently. */
|
|
|
+ if (connection_dir_get_by_purpose_and_resource(
|
|
|
+ DIR_PURPOSE_FETCH_CONSENSUS, resource))
|
|
|
+ continue; /* There's an in-progress download.*/
|
|
|
+
|
|
|
+ waiting = &consensus_waiting_for_certs[i];
|
|
|
if (waiting->consensus) {
|
|
|
/* XXXX make sure this doesn't delay sane downloads. */
|
|
|
- if (waiting->set_at + DELAY_WHILE_FETCHING_CERTS > now)
|
|
|
- return; /* We're still getting certs for this one. */
|
|
|
- else {
|
|
|
+ if (waiting->set_at + DELAY_WHILE_FETCHING_CERTS > now) {
|
|
|
+ continue; /* We're still getting certs for this one. */
|
|
|
+ } else {
|
|
|
if (!waiting->dl_failed) {
|
|
|
- download_status_failed(&consensus_dl_status[FLAV_NS], 0);
|
|
|
+ download_status_failed(&consensus_dl_status[i], 0);
|
|
|
waiting->dl_failed=1;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
- }
|
|
|
|
|
|
- log_info(LD_DIR, "Launching networkstatus consensus download.");
|
|
|
- directory_get_from_dirserver(DIR_PURPOSE_FETCH_CONSENSUS,
|
|
|
- ROUTER_PURPOSE_GENERAL, NULL,
|
|
|
- PDS_RETRY_IF_NO_SERVERS);
|
|
|
+ log_info(LD_DIR, "Launching %s networkstatus consensus download.",
|
|
|
+ networkstatus_get_flavor_name(i));
|
|
|
+
|
|
|
+ directory_get_from_dirserver(DIR_PURPOSE_FETCH_CONSENSUS,
|
|
|
+ ROUTER_PURPOSE_GENERAL, resource,
|
|
|
+ PDS_RETRY_IF_NO_SERVERS);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
/** Called when an attempt to download a consensus fails: note that the
|
|
|
* failure occurred, and possibly retry. */
|
|
|
void
|
|
|
-networkstatus_consensus_download_failed(int status_code)
|
|
|
+networkstatus_consensus_download_failed(int status_code, const char *flavname)
|
|
|
{
|
|
|
- /* XXXXNM Microdescs: may need to handle more types. */
|
|
|
- download_status_failed(&consensus_dl_status[FLAV_NS], status_code);
|
|
|
- /* Retry immediately, if appropriate. */
|
|
|
- update_consensus_networkstatus_downloads(time(NULL));
|
|
|
+ int flav = networkstatus_parse_flavor_name(flavname);
|
|
|
+ if (flav >= 0) {
|
|
|
+ tor_assert(flav < N_CONSENSUS_FLAVORS);
|
|
|
+ /* XXXX handle unrecognized flavors */
|
|
|
+ download_status_failed(&consensus_dl_status[flav], status_code);
|
|
|
+ /* Retry immediately, if appropriate. */
|
|
|
+ update_consensus_networkstatus_downloads(time(NULL));
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
/** How long do we (as a cache) wait after a consensus becomes non-fresh
|
|
@@ -1373,7 +1429,10 @@ update_certificate_downloads(time_t now)
|
|
|
now);
|
|
|
}
|
|
|
|
|
|
- authority_certs_fetch_missing(current_consensus, now);
|
|
|
+ if (current_ns_consensus)
|
|
|
+ authority_certs_fetch_missing(current_ns_consensus, now);
|
|
|
+ if (current_ns_consensus)
|
|
|
+ authority_certs_fetch_missing(current_md_consensus, now);
|
|
|
}
|
|
|
|
|
|
/** Return 1 if we have a consensus but we don't have enough certificates
|
|
@@ -1405,6 +1464,18 @@ networkstatus_get_latest_consensus(void)
|
|
|
return current_consensus;
|
|
|
}
|
|
|
|
|
|
+/** DOCDOC */
|
|
|
+networkstatus_t *
|
|
|
+networkstatus_get_latest_consensus_by_flavor(consensus_flavor_t f)
|
|
|
+{
|
|
|
+ if (f == FLAV_NS)
|
|
|
+ return current_ns_consensus;
|
|
|
+ else if (f == FLAV_MICRODESC)
|
|
|
+ return current_md_consensus;
|
|
|
+ else
|
|
|
+ tor_assert(0);
|
|
|
+}
|
|
|
+
|
|
|
/** Return the most recent consensus that we have downloaded, or NULL if it is
|
|
|
* no longer live. */
|
|
|
networkstatus_t *
|
|
@@ -1424,13 +1495,15 @@ networkstatus_get_live_consensus(time_t now)
|
|
|
/** As networkstatus_get_live_consensus(), but is way more tolerant of expired
|
|
|
* consensuses. */
|
|
|
networkstatus_t *
|
|
|
-networkstatus_get_reasonably_live_consensus(time_t now)
|
|
|
+networkstatus_get_reasonably_live_consensus(time_t now, int flavor)
|
|
|
{
|
|
|
#define REASONABLY_LIVE_TIME (24*60*60)
|
|
|
- if (current_consensus &&
|
|
|
- current_consensus->valid_after <= now &&
|
|
|
- now <= current_consensus->valid_until+REASONABLY_LIVE_TIME)
|
|
|
- return current_consensus;
|
|
|
+ networkstatus_t *consensus =
|
|
|
+ networkstatus_get_latest_consensus_by_flavor(flavor);
|
|
|
+ if (consensus &&
|
|
|
+ consensus->valid_after <= now &&
|
|
|
+ now <= consensus->valid_until+REASONABLY_LIVE_TIME)
|
|
|
+ return consensus;
|
|
|
else
|
|
|
return NULL;
|
|
|
}
|
|
@@ -1569,6 +1642,7 @@ networkstatus_set_current_consensus(const char *consensus,
|
|
|
const digests_t *current_digests = NULL;
|
|
|
consensus_waiting_for_certs_t *waiting = NULL;
|
|
|
time_t current_valid_after = 0;
|
|
|
+ int free_consensus = 1;
|
|
|
|
|
|
if (flav < 0) {
|
|
|
/* XXXX we don't handle unrecognized flavors yet. */
|
|
@@ -1614,9 +1688,16 @@ networkstatus_set_current_consensus(const char *consensus,
|
|
|
if (!strcmp(flavor, "ns")) {
|
|
|
consensus_fname = get_datadir_fname("cached-consensus");
|
|
|
unverified_fname = get_datadir_fname("unverified-consensus");
|
|
|
- if (current_consensus) {
|
|
|
- current_digests = ¤t_consensus->digests;
|
|
|
- current_valid_after = current_consensus->valid_after;
|
|
|
+ if (current_ns_consensus) {
|
|
|
+ current_digests = ¤t_ns_consensus->digests;
|
|
|
+ current_valid_after = current_ns_consensus->valid_after;
|
|
|
+ }
|
|
|
+ } else if (!strcmp(flavor, "microdesc")) {
|
|
|
+ consensus_fname = get_datadir_fname("cached-microdesc-consensus");
|
|
|
+ unverified_fname = get_datadir_fname("unverified-microdesc-consensus");
|
|
|
+ if (current_md_consensus) {
|
|
|
+ current_digests = ¤t_md_consensus->digests;
|
|
|
+ current_valid_after = current_md_consensus->valid_after;
|
|
|
}
|
|
|
} else {
|
|
|
cached_dir_t *cur;
|
|
@@ -1702,11 +1783,21 @@ networkstatus_set_current_consensus(const char *consensus,
|
|
|
|
|
|
if (flav == USABLE_CONSENSUS_FLAVOR) {
|
|
|
notify_control_networkstatus_changed(current_consensus, c);
|
|
|
-
|
|
|
- if (current_consensus) {
|
|
|
- networkstatus_copy_old_consensus_info(c, current_consensus);
|
|
|
- networkstatus_vote_free(current_consensus);
|
|
|
+ }
|
|
|
+ if (flav == FLAV_NS) {
|
|
|
+ if (current_ns_consensus) {
|
|
|
+ networkstatus_copy_old_consensus_info(c, current_ns_consensus);
|
|
|
+ networkstatus_vote_free(current_ns_consensus);
|
|
|
+ }
|
|
|
+ current_ns_consensus = c;
|
|
|
+ free_consensus = 0; /* avoid free */
|
|
|
+ } else if (flav == FLAV_MICRODESC) {
|
|
|
+ if (current_md_consensus) {
|
|
|
+ networkstatus_copy_old_consensus_info(c, current_md_consensus);
|
|
|
+ networkstatus_vote_free(current_md_consensus);
|
|
|
}
|
|
|
+ current_md_consensus = c;
|
|
|
+ free_consensus = 0; /* avoid free */
|
|
|
}
|
|
|
|
|
|
waiting = &consensus_waiting_for_certs[flav];
|
|
@@ -1739,11 +1830,9 @@ networkstatus_set_current_consensus(const char *consensus,
|
|
|
}
|
|
|
|
|
|
if (flav == USABLE_CONSENSUS_FLAVOR) {
|
|
|
- current_consensus = c;
|
|
|
- c = NULL; /* Prevent free. */
|
|
|
-
|
|
|
/* XXXXNM Microdescs: needs a non-ns variant. */
|
|
|
update_consensus_networkstatus_fetch_time(now);
|
|
|
+
|
|
|
dirvote_recalculate_timing(options, now);
|
|
|
routerstatus_list_update_named_server_map();
|
|
|
cell_ewma_set_scale_factor(options, current_consensus);
|
|
@@ -1758,11 +1847,11 @@ networkstatus_set_current_consensus(const char *consensus,
|
|
|
write_str_to_file(consensus_fname, consensus, 0);
|
|
|
}
|
|
|
|
|
|
- if (ftime_definitely_before(now, current_consensus->valid_after)) {
|
|
|
+ if (ftime_definitely_before(now, c->valid_after)) {
|
|
|
char tbuf[ISO_TIME_LEN+1];
|
|
|
char dbuf[64];
|
|
|
- long delta = now - current_consensus->valid_after;
|
|
|
- format_iso_time(tbuf, current_consensus->valid_after);
|
|
|
+ long delta = now - c->valid_after;
|
|
|
+ format_iso_time(tbuf, c->valid_after);
|
|
|
format_time_interval(dbuf, sizeof(dbuf), delta);
|
|
|
log_warn(LD_GENERAL, "Our clock is %s behind the time published in the "
|
|
|
"consensus network status document (%s GMT). Tor needs an "
|
|
@@ -1776,7 +1865,8 @@ networkstatus_set_current_consensus(const char *consensus,
|
|
|
|
|
|
result = 0;
|
|
|
done:
|
|
|
- networkstatus_vote_free(c);
|
|
|
+ if (free_consensus)
|
|
|
+ networkstatus_vote_free(c);
|
|
|
tor_free(consensus_fname);
|
|
|
tor_free(unverified_fname);
|
|
|
return result;
|
|
@@ -1812,7 +1902,8 @@ void
|
|
|
routers_update_all_from_networkstatus(time_t now, int dir_version)
|
|
|
{
|
|
|
routerlist_t *rl = router_get_routerlist();
|
|
|
- networkstatus_t *consensus = networkstatus_get_live_consensus(now);
|
|
|
+ networkstatus_t *consensus = networkstatus_get_reasonably_live_consensus(now,
|
|
|
+ FLAV_NS);
|
|
|
|
|
|
if (networkstatus_v2_list_has_changed)
|
|
|
download_status_map_update_from_v2_networkstatus();
|
|
@@ -2028,7 +2119,7 @@ routers_update_status_from_consensus_networkstatus(smartlist_t *routers,
|
|
|
void
|
|
|
signed_descs_update_status_from_consensus_networkstatus(smartlist_t *descs)
|
|
|
{
|
|
|
- networkstatus_t *ns = current_consensus;
|
|
|
+ networkstatus_t *ns = current_ns_consensus;
|
|
|
if (!ns)
|
|
|
return;
|
|
|
|
|
@@ -2036,7 +2127,7 @@ signed_descs_update_status_from_consensus_networkstatus(smartlist_t *descs)
|
|
|
char dummy[DIGEST_LEN];
|
|
|
/* instantiates the digest map. */
|
|
|
memset(dummy, 0, sizeof(dummy));
|
|
|
- router_get_consensus_status_by_descriptor_digest(dummy);
|
|
|
+ router_get_consensus_status_by_descriptor_digest(ns, dummy);
|
|
|
}
|
|
|
SMARTLIST_FOREACH(descs, signed_descriptor_t *, d,
|
|
|
{
|
|
@@ -2264,8 +2355,9 @@ networkstatus_free_all(void)
|
|
|
|
|
|
digestmap_free(v2_download_status_map, _tor_free);
|
|
|
v2_download_status_map = NULL;
|
|
|
- networkstatus_vote_free(current_consensus);
|
|
|
- current_consensus = NULL;
|
|
|
+ networkstatus_vote_free(current_ns_consensus);
|
|
|
+ networkstatus_vote_free(current_md_consensus);
|
|
|
+ current_md_consensus = current_ns_consensus = NULL;
|
|
|
|
|
|
for (i=0; i < N_CONSENSUS_FLAVORS; ++i) {
|
|
|
consensus_waiting_for_certs_t *waiting = &consensus_waiting_for_certs[i];
|