mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-12-06 19:41:15 +01:00
Implement new directory logic: download by descriptor digest, not by key digest. Caches try to download all listed digests from authorities; clients try to download "best" digests from caches.
svn:r5659
This commit is contained in:
@@ -46,39 +46,42 @@ N - Destroy and truncated cells should have reasons.
|
||||
- Specify
|
||||
- Implement
|
||||
|
||||
N - Only use a routerdesc if you recognize its hash.
|
||||
N . Only use a routerdesc if you recognize its hash.
|
||||
o (Must defer till dirservers are upgraded to latest code, which
|
||||
actually generates these hashes.)
|
||||
- Of course, authdirservers must not do this.
|
||||
. Of course, authdirservers must not do this.
|
||||
o If we have a routerdesc for Bob, and he says, "I'm 0.1.0.x", don't
|
||||
fetch a new one if it was published in the last 2 hours.
|
||||
- How does this interact with the 'recognized hash' rule?
|
||||
- Do not ask for any routers until we have 2 networkstatuses.
|
||||
- Client side:
|
||||
- Keep a record of which hash is most desirable for each router inside
|
||||
X Don't, actually. This is the authorities' job to straighten out.
|
||||
o Do not ask for any routers until we have 2 networkstatuses.
|
||||
. Client side:
|
||||
o Keep a record of which hash is most desirable for each router inside
|
||||
local_routerstatus_t.
|
||||
- If any hash is listed by two or more networkstatuses, the most
|
||||
o If any hash is listed by two or more networkstatuses, the most
|
||||
recent such hash is most desirable.
|
||||
- Otherwise, the most recent is desirable.
|
||||
- Once we've accepted a router, it's okay.
|
||||
- Do not accept a router that no networkstatus lists. (This should maybe
|
||||
o Otherwise, the most recent is desirable.
|
||||
o Once we've accepted a router, it's okay.
|
||||
o Do not accept a router that no networkstatus lists. (This should maybe
|
||||
get stricter.)
|
||||
- Download by fingerprint.
|
||||
- Reset failure count to zero when hash changes.
|
||||
- Mirrors and authorities:
|
||||
- Every time we hear a new networkstatus, we want every hash it lists.
|
||||
- Make sure that we are always willing to keep at least N routerinfos
|
||||
o Download by descriptor digest.
|
||||
o Reset failure count to zero when hash changes.
|
||||
. Test
|
||||
- Do we want to rate-limit downloads of each identity?
|
||||
. Mirrors and authorities:
|
||||
o Every time we hear a new networkstatus, we want every hash it lists.
|
||||
o Make sure that we are always willing to keep at least N routerinfos
|
||||
per router, where N = number of authorities.
|
||||
- Do whatever else is needed to be sure that we don't request
|
||||
o Do whatever else is needed to be sure that we don't request
|
||||
hashes that would be immediately discarded, or discard hashes
|
||||
that would be immediately re-requested.
|
||||
- Only fetch routerinfo from an authority that mentions is.
|
||||
- Only ask each authority once.
|
||||
- Retry soon after failure.
|
||||
- We need one bit per routerstatus for "should we download from
|
||||
o Only fetch routerinfo from an authority that mentions is.
|
||||
o Only ask each authority once.
|
||||
o Retry soon after failure.
|
||||
o We need one bit per routerstatus for "should we download from
|
||||
this guy."
|
||||
- Verify that we are actually storing retained old descriptors to our
|
||||
cache.
|
||||
- Test.
|
||||
- Non-directories don't need to keep descriptors in memory.
|
||||
|
||||
N - Should router info have a pointer to routerstatus?
|
||||
|
||||
+2
-2
@@ -1034,9 +1034,9 @@ connection_dir_client_reached_eof(connection_t *conn)
|
||||
info(LD_DIR,"Received server info (size %d) from server '%s:%d'",
|
||||
(int)body_len, conn->address, conn->port);
|
||||
if (conn->requested_resource &&
|
||||
!strcmpstart(conn->requested_resource,"fp/")) {
|
||||
!strcmpstart(conn->requested_resource,"d/")) {
|
||||
which = smartlist_create();
|
||||
dir_split_resource_into_fingerprints(conn->requested_resource+3,
|
||||
dir_split_resource_into_fingerprints(conn->requested_resource+2,
|
||||
which, NULL, 0);
|
||||
n_asked_for = smartlist_len(which);
|
||||
}
|
||||
|
||||
+13
-11
@@ -759,7 +759,6 @@ typedef struct signed_descriptor_t {
|
||||
/** Information about another onion router in the network. */
|
||||
typedef struct {
|
||||
signed_descriptor_t cache_info;
|
||||
|
||||
char *address; /**< Location of OR: either a hostname or an IP address. */
|
||||
char *nickname; /**< Human-readable OR name. */
|
||||
|
||||
@@ -797,12 +796,6 @@ typedef struct {
|
||||
* us? */
|
||||
unsigned int is_fast:1; /** Do we think this is a fast OR? */
|
||||
unsigned int is_stable:1; /** Do we think this is a stable OR? */
|
||||
unsigned int xx_is_recognized:1; /**< Temporary: do we think that this
|
||||
* descriptor's digest is recognized?
|
||||
*/
|
||||
unsigned int xx_is_extra_new:1; /**< Temporary: do we think that this
|
||||
* descriptor's digest is recognized?
|
||||
*/
|
||||
|
||||
/* The below items are used only by authdirservers for
|
||||
* reachability testing. */
|
||||
@@ -838,15 +831,25 @@ typedef struct routerstatus_t {
|
||||
* information with v2 of the directory
|
||||
* protocol. (All directory caches cache v1
|
||||
* directories.) */
|
||||
|
||||
/** True if we, as a directory mirror, want to download the corresponding
|
||||
* routerinfo from the authority who gave us this routerstatus. (That is,
|
||||
* if we don't have the routerinfo, and if we haven't already tried to get it
|
||||
* from this authority.)
|
||||
*/
|
||||
unsigned int need_to_mirror:1;
|
||||
} routerstatus_t;
|
||||
|
||||
/** DOCDOC */
|
||||
/** Our "local" or combined view of the info from all networkstatus objects
|
||||
* about a single router. */
|
||||
typedef struct local_routerstatus_t {
|
||||
/** What do we believe to be the case about this router? In this field,
|
||||
* descriptor_digest represnets the descriptor we would most like to use for
|
||||
* this router. */
|
||||
routerstatus_t status;
|
||||
time_t next_attempt_at; /**< When should we try this descriptor again? */
|
||||
uint8_t n_download_failures; /**< Number of failures trying to download the
|
||||
* most recent descriptor. */
|
||||
unsigned int should_download:1; /**< DOCDOC */
|
||||
unsigned int name_lookup_warned:1; /**< Have we warned the user for referring
|
||||
* to this (unnamed) router by nickname?
|
||||
*/
|
||||
@@ -2302,8 +2305,7 @@ void update_networkstatus_downloads(time_t now);
|
||||
void update_router_descriptor_downloads(time_t now);
|
||||
void routers_update_all_from_networkstatus(void);
|
||||
void routers_update_status_from_networkstatus(smartlist_t *routers,
|
||||
int reset_failures,
|
||||
int assume_recognized);
|
||||
int reset_failures);
|
||||
smartlist_t *router_list_superseded(void);
|
||||
int router_have_minimum_dir_info(void);
|
||||
void networkstatus_list_update_recent(time_t now);
|
||||
|
||||
+417
-244
@@ -31,8 +31,7 @@ static void local_routerstatus_free(local_routerstatus_t *rs);
|
||||
static void trusted_dir_server_free(trusted_dir_server_t *ds);
|
||||
static void update_networkstatus_cache_downloads(time_t now);
|
||||
static void update_networkstatus_client_downloads(time_t now);
|
||||
static int routerdesc_digest_is_recognized(const char *identity,
|
||||
const char *digest);
|
||||
static int signed_desc_digest_is_recognized(signed_descriptor_t *desc);
|
||||
static void routerlist_assert_ok(routerlist_t *rl);
|
||||
|
||||
#define MAX_DESCRIPTORS_PER_ROUTER 5
|
||||
@@ -1089,6 +1088,7 @@ routerinfo_free(routerinfo_t *router)
|
||||
tor_free(router);
|
||||
}
|
||||
|
||||
/** Release storage held by <b>sd</b>. */
|
||||
static void
|
||||
signed_descriptor_free(signed_descriptor_t *sd)
|
||||
{
|
||||
@@ -1096,7 +1096,8 @@ signed_descriptor_free(signed_descriptor_t *sd)
|
||||
tor_free(sd);
|
||||
}
|
||||
|
||||
/** frees ri. DOCDOC */
|
||||
/** Extract a signed_descriptor_t from a routerinfo, and free the routerinfo.
|
||||
*/
|
||||
static signed_descriptor_t *
|
||||
signed_descriptor_from_routerinfo(routerinfo_t *ri)
|
||||
{
|
||||
@@ -1506,15 +1507,15 @@ router_add_to_routerlist(routerinfo_t *router, const char **msg,
|
||||
return -2;
|
||||
}
|
||||
authdir_verified = router->is_verified;
|
||||
/*
|
||||
} else {
|
||||
if (! router->xx_is_recognized && !from_cache) {
|
||||
log_fn(LOG_WARN, "Dropping unrecognized descriptor for router '%s'",
|
||||
} else if (!from_cache) {
|
||||
/* Only check the descriptor digest against the network statuses when
|
||||
* we are recieving from somewhere other than the cache.*/
|
||||
if (!signed_desc_digest_is_recognized(&router->cache_info)) {
|
||||
warn(LD_DIR, "Dropping unrecognized descriptor for router '%s'",
|
||||
router->nickname);
|
||||
routerinfo_free(router);
|
||||
return -1;
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
/* If we have a router with this name, and the identity key is the same,
|
||||
@@ -1647,12 +1648,13 @@ _compare_duration_idx(const void *_d1, const void *_d2)
|
||||
* indices <b>lo</b> or higher may be changed.
|
||||
*/
|
||||
static void
|
||||
routerlist_remove_old_cached_routers_with_id(time_t cutoff, int lo, int hi)
|
||||
routerlist_remove_old_cached_routers_with_id(time_t cutoff, int lo, int hi,
|
||||
digestmap_t *retain)
|
||||
{
|
||||
int i, n = hi-lo+1, n_extra;
|
||||
int n_rmv = 0;
|
||||
struct duration_idx_t *lifespans;
|
||||
uint8_t *rmv;
|
||||
uint8_t *rmv, *must_keep;
|
||||
smartlist_t *lst = routerlist->old_routers;
|
||||
#if 1
|
||||
const char *ident;
|
||||
@@ -1672,12 +1674,16 @@ routerlist_remove_old_cached_routers_with_id(time_t cutoff, int lo, int hi)
|
||||
|
||||
lifespans = tor_malloc_zero(sizeof(struct duration_idx_t)*n);
|
||||
rmv = tor_malloc_zero(sizeof(uint8_t)*n);
|
||||
must_keep = tor_malloc_zero(sizeof(uint8_t)*n);
|
||||
/* Set lifespans to contain the lifespan and index of each server. */
|
||||
/* Set rmv[i-lo]=1 if we're going to remove a server for being too old. */
|
||||
for (i = lo; i <= hi; ++i) {
|
||||
signed_descriptor_t *r = smartlist_get(lst, i);
|
||||
signed_descriptor_t *r_next;
|
||||
lifespans[i-lo].idx = i;
|
||||
if (retain && digestmap_get(retain, r->signed_descriptor_digest)) {
|
||||
must_keep[i-lo] = 1;
|
||||
}
|
||||
if (i < hi) {
|
||||
r_next = smartlist_get(lst, i+1);
|
||||
tor_assert(r->published_on <= r_next->published_on);
|
||||
@@ -1686,7 +1692,7 @@ routerlist_remove_old_cached_routers_with_id(time_t cutoff, int lo, int hi)
|
||||
r_next = NULL;
|
||||
lifespans[i-lo].duration = INT_MAX;
|
||||
}
|
||||
if (r->published_on < cutoff && n_rmv < n_extra) {
|
||||
if (!must_keep[i-lo] && r->published_on < cutoff && n_rmv < n_extra) {
|
||||
++n_rmv;
|
||||
lifespans[i-lo].old = 1;
|
||||
rmv[i-lo] = 1;
|
||||
@@ -1701,7 +1707,7 @@ routerlist_remove_old_cached_routers_with_id(time_t cutoff, int lo, int hi)
|
||||
**/
|
||||
qsort(lifespans, n, sizeof(struct duration_idx_t), _compare_duration_idx);
|
||||
for (i = 0; i < n && n_rmv < n_extra; ++i) {
|
||||
if (!lifespans[i].old) {
|
||||
if (!must_keep[lifespans[i].idx-lo] && !lifespans[i].old) {
|
||||
rmv[lifespans[i].idx-lo] = 1;
|
||||
++n_rmv;
|
||||
}
|
||||
@@ -1712,6 +1718,7 @@ routerlist_remove_old_cached_routers_with_id(time_t cutoff, int lo, int hi)
|
||||
if (rmv[i-lo])
|
||||
routerlist_remove_old(routerlist, smartlist_get(lst, i), i);
|
||||
}
|
||||
tor_free(must_keep);
|
||||
tor_free(rmv);
|
||||
tor_free(lifespans);
|
||||
}
|
||||
@@ -1727,14 +1734,26 @@ routerlist_remove_old_routers(void)
|
||||
const char *cur_id = NULL;
|
||||
time_t cutoff;
|
||||
routerinfo_t *router;
|
||||
if (!routerlist)
|
||||
digestmap_t *retain;
|
||||
or_options_t *options = get_options();
|
||||
if (!routerlist || !networkstatus_list)
|
||||
return;
|
||||
|
||||
retain = digestmap_new();
|
||||
if (server_mode(options) && options->DirPort) {
|
||||
SMARTLIST_FOREACH(networkstatus_list, networkstatus_t *, ns,
|
||||
{
|
||||
SMARTLIST_FOREACH(ns->entries, routerstatus_t *, rs,
|
||||
digestmap_set(retain, rs->descriptor_digest, (void*)1));
|
||||
});
|
||||
}
|
||||
|
||||
cutoff = time(NULL) - ROUTER_MAX_AGE;
|
||||
/* Remove old members of routerlist->routers. */
|
||||
for (i = 0; i < smartlist_len(routerlist->routers); ++i) {
|
||||
router = smartlist_get(routerlist->routers, i);
|
||||
if (router->cache_info.published_on <= cutoff) {
|
||||
if (router->cache_info.published_on <= cutoff &&
|
||||
!digestmap_get(retain, router->cache_info.signed_descriptor_digest)) {
|
||||
/* Too old. Remove it. */
|
||||
info(LD_DIR, "Forgetting obsolete (too old) routerinfo for router '%s'",
|
||||
router->nickname);
|
||||
@@ -1762,14 +1781,15 @@ routerlist_remove_old_routers(void)
|
||||
hi = i;
|
||||
}
|
||||
if (memcmp(cur_id, r->identity_digest, DIGEST_LEN)) {
|
||||
routerlist_remove_old_cached_routers_with_id(cutoff, i+1, hi);
|
||||
routerlist_remove_old_cached_routers_with_id(cutoff, i+1, hi, retain);
|
||||
cur_id = r->identity_digest;
|
||||
hi = i;
|
||||
}
|
||||
}
|
||||
if (hi>=0)
|
||||
routerlist_remove_old_cached_routers_with_id(cutoff, 0, hi);
|
||||
routerlist_remove_old_cached_routers_with_id(cutoff, 0, hi, retain);
|
||||
routerlist_assert_ok(routerlist);
|
||||
digestmap_free(retain, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1806,7 +1826,7 @@ router_load_single_router(const char *s, const char **msg)
|
||||
|
||||
lst = smartlist_create();
|
||||
smartlist_add(lst, ri);
|
||||
routers_update_status_from_networkstatus(lst, 0, 1);
|
||||
routers_update_status_from_networkstatus(lst, 0);
|
||||
|
||||
if (router_add_to_routerlist(ri, msg, 0)<0) {
|
||||
warn(LD_DIR, "Couldn't add router to list: %s Dropping.",
|
||||
@@ -1841,11 +1861,14 @@ router_load_routers_from_string(const char *s, int from_cache,
|
||||
|
||||
router_parse_list_from_string(&s, routers);
|
||||
|
||||
routers_update_status_from_networkstatus(routers, !from_cache, from_cache);
|
||||
routers_update_status_from_networkstatus(routers, !from_cache);
|
||||
|
||||
info(LD_DIR, "%d elements to add", smartlist_len(routers));
|
||||
|
||||
SMARTLIST_FOREACH(routers, routerinfo_t *, ri,
|
||||
{
|
||||
base16_encode(fp, sizeof(fp), ri->cache_info.identity_digest, DIGEST_LEN);
|
||||
base16_encode(fp, sizeof(fp), ri->cache_info.signed_descriptor_digest,
|
||||
DIGEST_LEN);
|
||||
if (requested_fingerprints) {
|
||||
if (smartlist_string_isin(requested_fingerprints, fp)) {
|
||||
smartlist_string_remove(requested_fingerprints, fp);
|
||||
@@ -2039,6 +2062,12 @@ router_set_networkstatus(const char *s, time_t arrived_at,
|
||||
if (!found)
|
||||
smartlist_add(networkstatus_list, ns);
|
||||
|
||||
SMARTLIST_FOREACH(ns->entries, routerstatus_t *, rs,
|
||||
{
|
||||
if (!router_get_by_descriptor_digest(rs->descriptor_digest))
|
||||
rs->need_to_mirror = 1;
|
||||
});
|
||||
|
||||
info(LD_DIR, "Setting networkstatus %s %s (published %s)",
|
||||
source == NS_FROM_CACHE?"cached from":
|
||||
(source==NS_FROM_DIR?"downloaded from":"generated for"),
|
||||
@@ -2127,9 +2156,10 @@ router_get_combined_status_by_digest(const char *digest)
|
||||
_compare_digest_to_routerstatus_entry);
|
||||
}
|
||||
|
||||
/** DOCDOC */
|
||||
/** Return true iff any networkstatus includes a descriptor whose digest
|
||||
* is that of <b>desc</b>. */
|
||||
static int
|
||||
routerdesc_digest_is_recognized(const char *identity, const char *digest)
|
||||
signed_desc_digest_is_recognized(signed_descriptor_t *desc)
|
||||
{
|
||||
routerstatus_t *rs;
|
||||
if (!networkstatus_list)
|
||||
@@ -2137,16 +2167,17 @@ routerdesc_digest_is_recognized(const char *identity, const char *digest)
|
||||
|
||||
SMARTLIST_FOREACH(networkstatus_list, networkstatus_t *, ns,
|
||||
{
|
||||
if (!(rs = networkstatus_find_entry(ns, identity)))
|
||||
if (!(rs = networkstatus_find_entry(ns, desc->identity_digest)))
|
||||
continue;
|
||||
if (!memcmp(rs->descriptor_digest, digest, DIGEST_LEN))
|
||||
if (!memcmp(rs->descriptor_digest,
|
||||
desc->signed_descriptor_digest, DIGEST_LEN))
|
||||
return 1;
|
||||
});
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* XXXX These should be configurable, perhaps? NM */
|
||||
#define AUTHORITY_NS_CACHE_INTERVAL 10*60
|
||||
#define AUTHORITY_NS_CACHE_INTERVAL 5*60
|
||||
#define NONAUTHORITY_NS_CACHE_INTERVAL 15*60
|
||||
/** We are a directory server, and so cache network_status documents.
|
||||
* Initiate downloads as needed to update them. For authorities, this means
|
||||
@@ -2304,7 +2335,7 @@ update_networkstatus_client_downloads(time_t now)
|
||||
tor_free(resource);
|
||||
}
|
||||
|
||||
/*DOCDOC*/
|
||||
/** Launch requests for networkstatus documents as appropriate. */
|
||||
void
|
||||
update_networkstatus_downloads(time_t now)
|
||||
{
|
||||
@@ -2604,7 +2635,8 @@ networkstatus_get_by_digest(const char *digest)
|
||||
}
|
||||
|
||||
/** If the network-status list has changed since the last time we called this
|
||||
* function, update the status of every router from the network-status list.
|
||||
* function, update the status of every routerinfo from the network-status
|
||||
* list.
|
||||
*/
|
||||
void
|
||||
routers_update_all_from_networkstatus(void)
|
||||
@@ -2620,7 +2652,7 @@ routers_update_all_from_networkstatus(void)
|
||||
if (networkstatus_list_has_changed)
|
||||
routerstatus_list_update_from_networkstatus(now);
|
||||
|
||||
routers_update_status_from_networkstatus(routerlist->routers, 0, 0);
|
||||
routers_update_status_from_networkstatus(routerlist->routers, 0);
|
||||
|
||||
me = router_get_my_routerinfo();
|
||||
if (me && !have_warned_about_unverified_status) {
|
||||
@@ -2766,6 +2798,13 @@ networkstatus_list_update_recent(time_t now)
|
||||
networkstatus_list_has_changed = 1;
|
||||
}
|
||||
|
||||
/** Helper for routerstatus_list_update_from_networkstatus: remember how many
|
||||
* authorities recommend a given descriptor digest. */
|
||||
typedef struct {
|
||||
routerstatus_t *rs;
|
||||
int count;
|
||||
} desc_digest_count_t;
|
||||
|
||||
/** Update our view of router status (as stored in routerstatus_list) from the
|
||||
* current set of network status documents (as stored in networkstatus_list).
|
||||
* Do nothing unless the network status list has changed since the last time
|
||||
@@ -2775,14 +2814,14 @@ static void
|
||||
routerstatus_list_update_from_networkstatus(time_t now)
|
||||
{
|
||||
or_options_t *options = get_options();
|
||||
int n_trusted, n_statuses, n_recent=0, n_naming=0;
|
||||
int n_distinct = 0;
|
||||
int i, warned;
|
||||
int n_trusted, n_statuses, n_recent = 0, n_naming = 0;
|
||||
int i, j, warned;
|
||||
int *index, *size;
|
||||
networkstatus_t **networkstatus;
|
||||
smartlist_t *result;
|
||||
strmap_t *name_map;
|
||||
char conflict[DIGEST_LEN];
|
||||
char conflict[DIGEST_LEN]; /* Sentinel value */
|
||||
desc_digest_count_t *digest_counts = NULL;
|
||||
|
||||
networkstatus_list_update_recent(now);
|
||||
|
||||
@@ -2822,6 +2861,10 @@ routerstatus_list_update_from_networkstatus(time_t now)
|
||||
++n_recent;
|
||||
}
|
||||
|
||||
/** Iterate over all entries in all networkstatuses, and build
|
||||
* name_map as a map from lc nickname to identity digest. If there
|
||||
* is a conflict on that nickname, map the lc nickname to conflict.
|
||||
*/
|
||||
name_map = strmap_new();
|
||||
memset(conflict, 0xff, sizeof(conflict));
|
||||
for (i = 0; i < n_statuses; ++i) {
|
||||
@@ -2861,8 +2904,9 @@ routerstatus_list_update_from_networkstatus(time_t now)
|
||||
}
|
||||
|
||||
result = smartlist_create();
|
||||
digest_counts = tor_malloc_zero(sizeof(desc_digest_count_t)*n_statuses);
|
||||
|
||||
/* Iterate through all of the sorted routerstatus lists in step.
|
||||
/* Iterate through all of the sorted routerstatus lists in lockstep.
|
||||
* Invariants:
|
||||
* - For 0 <= i < n_statuses: index[i] is an index into
|
||||
* networkstatus[i]->entries, which has size[i] elements.
|
||||
@@ -2876,11 +2920,13 @@ routerstatus_list_update_from_networkstatus(time_t now)
|
||||
while (1) {
|
||||
int n_running=0, n_named=0, n_valid=0, n_listing=0;
|
||||
int n_v2_dir=0, n_fast=0, n_stable=0, n_exit=0;
|
||||
int n_desc_digests=0, highest_count=0;
|
||||
const char *the_name = NULL;
|
||||
local_routerstatus_t *rs_out, *rs_old;
|
||||
routerstatus_t *rs, *most_recent;
|
||||
networkstatus_t *ns;
|
||||
const char *lowest = NULL;
|
||||
|
||||
/* Find out which of the digests appears first. */
|
||||
for (i = 0; i < n_statuses; ++i) {
|
||||
if (index[i] < size[i]) {
|
||||
@@ -2896,8 +2942,6 @@ routerstatus_list_update_from_networkstatus(time_t now)
|
||||
/* Okay. The routers at networkstatus[i]->entries[index[i]] whose digests
|
||||
* match "lowest" are next in order. Iterate over them, incrementing those
|
||||
* index[i] as we go. */
|
||||
++n_distinct;
|
||||
most_recent = NULL;
|
||||
for (i = 0; i < n_statuses; ++i) {
|
||||
if (index[i] >= size[i])
|
||||
continue;
|
||||
@@ -2905,10 +2949,13 @@ routerstatus_list_update_from_networkstatus(time_t now)
|
||||
rs = smartlist_get(ns->entries, index[i]);
|
||||
if (memcmp(rs->identity_digest, lowest, DIGEST_LEN))
|
||||
continue;
|
||||
/* At this point, we know that we're looking at a routersatus with
|
||||
* identity "lowest".
|
||||
*/
|
||||
++index[i];
|
||||
++n_listing;
|
||||
if (!most_recent || rs->published_on > most_recent->published_on)
|
||||
most_recent = rs;
|
||||
/* Should we name this router? Only if all the names from naming
|
||||
* authorities match. */
|
||||
if (rs->is_named && ns->binds_names) {
|
||||
if (!the_name)
|
||||
the_name = rs->nickname;
|
||||
@@ -2926,6 +2973,23 @@ routerstatus_list_update_from_networkstatus(time_t now)
|
||||
the_name = "**mismatch**";
|
||||
}
|
||||
}
|
||||
/* Keep a running count of how often which descriptor digests
|
||||
* appear. */
|
||||
for (j = 0; j < n_desc_digests; ++j) {
|
||||
if (!memcmp(rs->descriptor_digest,
|
||||
digest_counts[j].rs->descriptor_digest, DIGEST_LEN)) {
|
||||
if (++digest_counts[j].count > highest_count)
|
||||
highest_count = digest_counts[j].count;
|
||||
goto found;
|
||||
}
|
||||
}
|
||||
digest_counts[n_desc_digests].rs = rs;
|
||||
digest_counts[n_desc_digests].count = 1;
|
||||
if (!highest_count)
|
||||
highest_count = 1;
|
||||
++n_desc_digests;
|
||||
found:
|
||||
/* Now tally up the easily-tallied flags. */
|
||||
if (rs->is_valid)
|
||||
++n_valid;
|
||||
if (rs->is_running && ns->is_recent)
|
||||
@@ -2939,11 +3003,26 @@ routerstatus_list_update_from_networkstatus(time_t now)
|
||||
if (rs->is_v2_dir)
|
||||
++n_v2_dir;
|
||||
}
|
||||
/* Go over the descriptor digests and figure out which descriptor we
|
||||
* want. */
|
||||
most_recent = NULL;
|
||||
for (i = 0; i < n_desc_digests; ++i) {
|
||||
/* If any digest appears twice or more, ignore those that don't.*/
|
||||
if (highest_count >= 2 && digest_counts[i].count < 2)
|
||||
continue;
|
||||
if (!most_recent ||
|
||||
digest_counts[i].rs->published_on > most_recent->published_on)
|
||||
most_recent = digest_counts[i].rs;
|
||||
}
|
||||
rs_out = tor_malloc_zero(sizeof(local_routerstatus_t));
|
||||
memcpy(&rs_out->status, most_recent, sizeof(routerstatus_t));
|
||||
/* Copy status info about this router, if we had any before. */
|
||||
if ((rs_old = router_get_combined_status_by_digest(lowest))) {
|
||||
rs_out->n_download_failures = rs_old->n_download_failures;
|
||||
rs_out->next_attempt_at = rs_old->next_attempt_at;
|
||||
if (!memcmp(rs_out->status.descriptor_digest,
|
||||
most_recent->descriptor_digest, DIGEST_LEN)) {
|
||||
rs_out->n_download_failures = rs_old->n_download_failures;
|
||||
rs_out->next_attempt_at = rs_old->next_attempt_at;
|
||||
}
|
||||
rs_out->name_lookup_warned = rs_old->name_lookup_warned;
|
||||
}
|
||||
smartlist_add(result, rs_out);
|
||||
@@ -2979,6 +3058,7 @@ routerstatus_list_update_from_networkstatus(time_t now)
|
||||
tor_free(networkstatus);
|
||||
tor_free(index);
|
||||
tor_free(size);
|
||||
tor_free(digest_counts);
|
||||
strmap_free(name_map, NULL);
|
||||
|
||||
networkstatus_list_has_changed = 0;
|
||||
@@ -2990,11 +3070,11 @@ routerstatus_list_update_from_networkstatus(time_t now)
|
||||
* networkstatus_t documents. */
|
||||
void
|
||||
routers_update_status_from_networkstatus(smartlist_t *routers,
|
||||
int reset_failures,
|
||||
int assume_recognized)
|
||||
int reset_failures)
|
||||
{
|
||||
trusted_dir_server_t *ds;
|
||||
local_routerstatus_t *rs;
|
||||
routerstatus_t *rs2;
|
||||
or_options_t *options = get_options();
|
||||
int authdir = options->AuthoritativeDir;
|
||||
int namingdir = options->AuthoritativeDir &&
|
||||
@@ -3025,195 +3105,171 @@ routers_update_status_from_networkstatus(smartlist_t *routers,
|
||||
if (router->is_running && ds) {
|
||||
ds->n_networkstatus_failures = 0;
|
||||
}
|
||||
if (assume_recognized) {
|
||||
router->xx_is_recognized = 1;
|
||||
} else {
|
||||
if (!router->xx_is_recognized) {
|
||||
router->xx_is_recognized = routerdesc_digest_is_recognized(
|
||||
digest, router->cache_info.signed_descriptor_digest);
|
||||
}
|
||||
router->xx_is_extra_new =
|
||||
router->cache_info.published_on > rs->status.published_on;
|
||||
}
|
||||
if (reset_failures && router->xx_is_recognized) {
|
||||
if (reset_failures) {
|
||||
rs->n_download_failures = 0;
|
||||
rs->next_attempt_at = 0;
|
||||
}
|
||||
|
||||
/* Note that we have this descriptor. This may be redundant? */
|
||||
SMARTLIST_FOREACH(networkstatus_list, networkstatus_t *, ns,
|
||||
{
|
||||
rs2 = networkstatus_find_entry(ns, router->cache_info.identity_digest);
|
||||
if (rs2 && !memcmp(rs2->descriptor_digest,
|
||||
router->cache_info.signed_descriptor_digest,
|
||||
DIGEST_LEN))
|
||||
rs2->need_to_mirror = 0;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/** Return new list of ID fingerprints for superseded routers. A router is
|
||||
* superseded if any network-status has a router with a different digest
|
||||
* published more recently, or if it is listed in the network-status but not
|
||||
* in the router list.
|
||||
*/
|
||||
static smartlist_t *
|
||||
router_list_downloadable(void)
|
||||
/** For every router descriptor we are currently downloading by descriptor
|
||||
* digest, set result[d] to 1. */
|
||||
static void
|
||||
list_pending_descriptor_downloads(digestmap_t *result)
|
||||
{
|
||||
#define MAX_OLD_SERVER_DOWNLOAD_RATE 2*60*60
|
||||
or_options_t *options = get_options();
|
||||
int n_conns, i, n_downloadable = 0;
|
||||
const char *prefix = "d/";
|
||||
size_t p_len = strlen(prefix);
|
||||
int i, n_conns;
|
||||
connection_t **carray;
|
||||
smartlist_t *superseded = smartlist_create();
|
||||
smartlist_t *downloading;
|
||||
time_t now = time(NULL);
|
||||
int mirror = server_mode(options) && options->DirPort;
|
||||
/* these are just used for logging */
|
||||
int n_not_ready = 0, n_in_progress = 0, n_uptodate = 0, n_skip_old = 0,
|
||||
n_obsolete = 0, xx_n_unrecognized = 0, xx_n_extra_new = 0, xx_n_both = 0,
|
||||
xx_n_unrec_old = 0;
|
||||
|
||||
if (!routerstatus_list)
|
||||
return superseded;
|
||||
smartlist_t *tmp = smartlist_create();
|
||||
|
||||
tor_assert(result);
|
||||
get_connection_array(&carray, &n_conns);
|
||||
|
||||
routerstatus_list_update_from_networkstatus(now);
|
||||
|
||||
SMARTLIST_FOREACH(routerstatus_list, local_routerstatus_t *, rs,
|
||||
{
|
||||
if (rs->status.published_on + ROUTER_MAX_AGE < now) {
|
||||
rs->should_download = 0;
|
||||
++n_obsolete;
|
||||
} if (rs->next_attempt_at < now) {
|
||||
if (options->AuthoritativeDir &&
|
||||
dirserv_would_reject_router(&rs->status)) {
|
||||
rs->should_download = 0;
|
||||
} else {
|
||||
rs->should_download = 1;
|
||||
++n_downloadable;
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
char fp[HEX_DIGEST_LEN+1];
|
||||
base16_encode(fp, HEX_DIGEST_LEN+1, rs->status.identity_digest,
|
||||
DIGEST_LEN);
|
||||
log_fn(LOG_NOTICE, "Not yet ready to download %s (%d more seconds)", fp,
|
||||
(int)(rs->next_attempt_at-now));
|
||||
*/
|
||||
rs->should_download = 0;
|
||||
++n_not_ready;
|
||||
}
|
||||
});
|
||||
|
||||
downloading = smartlist_create();
|
||||
for (i = 0; i < n_conns; ++i) {
|
||||
connection_t *conn = carray[i];
|
||||
if (conn->type == CONN_TYPE_DIR &&
|
||||
conn->purpose == DIR_PURPOSE_FETCH_SERVERDESC &&
|
||||
!conn->marked_for_close) {
|
||||
if (!strcmpstart(conn->requested_resource, "all"))
|
||||
n_downloadable = 0;
|
||||
if (!strcmpstart(conn->requested_resource, "fp/"))
|
||||
dir_split_resource_into_fingerprints(conn->requested_resource+3,
|
||||
downloading, NULL, 1);
|
||||
if (!strcmpstart(conn->requested_resource, prefix))
|
||||
dir_split_resource_into_fingerprints(conn->requested_resource+p_len,
|
||||
tmp, NULL, 1);
|
||||
}
|
||||
}
|
||||
SMARTLIST_FOREACH(tmp, char *, d,
|
||||
{
|
||||
digestmap_set(result, d, (void*)1);
|
||||
tor_free(d);
|
||||
});
|
||||
smartlist_free(tmp);
|
||||
}
|
||||
|
||||
if (n_downloadable) {
|
||||
SMARTLIST_FOREACH(downloading, const char *, d,
|
||||
{
|
||||
local_routerstatus_t *rs;
|
||||
if ((rs = router_get_combined_status_by_digest(d)) &&
|
||||
rs->should_download) {
|
||||
rs->should_download = 0;
|
||||
--n_downloadable;
|
||||
++n_in_progress;
|
||||
}
|
||||
});
|
||||
/** Launch downloads for the all the descriptors whose digests are listed
|
||||
* as digests[i] for lo <= i < hi. (Lo and hi may be out of range.)
|
||||
* If <b>source</b> is given, download from <b>source</b>; otherwise,
|
||||
* download from an appropriate random directory server.
|
||||
*/
|
||||
static void
|
||||
initiate_descriptor_downloads(routerstatus_t *source,
|
||||
smartlist_t *digests,
|
||||
int lo, int hi)
|
||||
{
|
||||
int i, n = hi-lo;
|
||||
char *resource, *cp;
|
||||
size_t r_len;
|
||||
if (n <= 0)
|
||||
return;
|
||||
if (lo < 0)
|
||||
lo = 0;
|
||||
if (hi > smartlist_len(digests))
|
||||
hi = smartlist_len(digests);
|
||||
|
||||
r_len = 8 + (HEX_DIGEST_LEN+1)*n;
|
||||
cp = resource = tor_malloc(r_len);
|
||||
memcpy(cp, "d/", 2);
|
||||
cp += 2;
|
||||
for (i = lo; i < hi; ++i) {
|
||||
base16_encode(cp, r_len-(cp-resource),
|
||||
smartlist_get(digests,i), DIGEST_LEN);
|
||||
cp += HEX_DIGEST_LEN;
|
||||
*cp++ = '+';
|
||||
}
|
||||
SMARTLIST_FOREACH(downloading, char *, cp, tor_free(cp));
|
||||
smartlist_free(downloading);
|
||||
if (!n_downloadable)
|
||||
return superseded;
|
||||
memcpy(cp-1, ".z", 3);
|
||||
|
||||
if (routerlist && n_downloadable) {
|
||||
SMARTLIST_FOREACH(routerlist->routers, routerinfo_t *, ri,
|
||||
{
|
||||
local_routerstatus_t *rs;
|
||||
if (!(rs = router_get_combined_status_by_digest(
|
||||
ri->cache_info.identity_digest)) ||
|
||||
!rs->should_download) {
|
||||
// log_fn(LOG_NOTICE, "No status for %s", fp);
|
||||
continue;
|
||||
}
|
||||
if (!ri->xx_is_recognized) {
|
||||
++xx_n_unrecognized;
|
||||
if (ri->xx_is_extra_new)
|
||||
++xx_n_both;
|
||||
}
|
||||
if (ri->xx_is_extra_new)
|
||||
++xx_n_extra_new;
|
||||
|
||||
/* Change this "or" to be an "and" once dirs generate hashes right.
|
||||
* Remove the version check once older versions are uncommon.
|
||||
* XXXXX. NM */
|
||||
if (!memcmp(ri->cache_info.signed_descriptor_digest,
|
||||
rs->status.descriptor_digest,
|
||||
DIGEST_LEN) ||
|
||||
rs->status.published_on <= ri->cache_info.published_on) {
|
||||
++n_uptodate;
|
||||
rs->should_download = 0;
|
||||
--n_downloadable;
|
||||
} else if (!mirror &&
|
||||
ri->platform &&
|
||||
!tor_version_as_new_as(ri->platform, "0.1.1.6-alpha") &&
|
||||
(ri->cache_info.published_on +
|
||||
MAX_OLD_SERVER_DOWNLOAD_RATE) > now) {
|
||||
/* Same digest, or date is up-to-date, or we have a comparatively
|
||||
* recent server with an old version.
|
||||
* No need to download it. */
|
||||
// log_fn(LOG_NOTICE, "Up-to-date status for %s", fp);
|
||||
++n_skip_old;
|
||||
if (!ri->xx_is_recognized)
|
||||
++xx_n_unrec_old;
|
||||
rs->should_download = 0;
|
||||
--n_downloadable;
|
||||
} /* else {
|
||||
char t1[ISO_TIME_LEN+1];
|
||||
char t2[ISO_TIME_LEN+1];
|
||||
format_iso_time(t1, rs->satus.published_on);
|
||||
format_iso_time(t2, ri->published_on);
|
||||
log_fn(LOG_NOTICE, "Out-of-date status for %s %s (%d %d) [%s %s]", fp,
|
||||
ri->nickname,
|
||||
!memcmp(ri->cache_info.signed_descriptor_digest,
|
||||
rs->status.descriptor_digest,
|
||||
DIGEST_LEN),
|
||||
rs->published_on < ri->published_on,
|
||||
t1, t2);
|
||||
} */
|
||||
});
|
||||
if (source) {
|
||||
/* We know which authority we want. */
|
||||
directory_initiate_command_routerstatus(source,
|
||||
DIR_PURPOSE_FETCH_SERVERDESC,
|
||||
0, /* not private */
|
||||
resource, NULL, 0);
|
||||
} else {
|
||||
directory_get_from_dirserver(DIR_PURPOSE_FETCH_SERVERDESC,
|
||||
resource,
|
||||
1);
|
||||
}
|
||||
tor_free(resource);
|
||||
}
|
||||
|
||||
#if 0
|
||||
info(LD_DIR, "%d router descriptors are downloadable; "
|
||||
"%d are up to date; %d are in progress; "
|
||||
"%d are not ready to retry; "
|
||||
"%d are not published recently enough to be worthwhile; "
|
||||
"%d are running pre-0.1.1.6 Tors and aren't stale enough to replace. "
|
||||
"%d have unrecognized descriptor hashes; %d are newer than the dirs "
|
||||
"have told us about; %d are both unrecognized and newer than any "
|
||||
"publication date in the networkstatus; %d are both "
|
||||
"unrecognized and running a pre-0.1.1.6 version.",
|
||||
n_downloadable, n_uptodate, n_in_progress, n_not_ready,
|
||||
n_obsolete, n_skip_old, xx_n_unrecognized, xx_n_extra_new, xx_n_both,
|
||||
xx_n_unrec_old);
|
||||
#endif
|
||||
/** Return new list of ID fingerprints for routers that we (as a client) would
|
||||
* like to download.
|
||||
*/
|
||||
static smartlist_t *
|
||||
router_list_client_downloadable(void)
|
||||
{
|
||||
#define MAX_OLD_SERVER_DOWNLOAD_RATE 2*60*60
|
||||
#define ESTIMATED_PROPAGATION_TIME 10*60
|
||||
int n_downloadable = 0;
|
||||
smartlist_t *downloadable = smartlist_create();
|
||||
digestmap_t *downloading;
|
||||
time_t now = time(NULL);
|
||||
/* these are just used for logging */
|
||||
int n_not_ready = 0, n_in_progress = 0, n_uptodate = 0,
|
||||
n_obsolete = 0, n_too_young = 0;
|
||||
|
||||
if (!n_downloadable)
|
||||
return superseded;
|
||||
if (!routerstatus_list)
|
||||
return downloadable;
|
||||
|
||||
downloading = digestmap_new();
|
||||
list_pending_descriptor_downloads(downloading);
|
||||
|
||||
routerstatus_list_update_from_networkstatus(now);
|
||||
SMARTLIST_FOREACH(routerstatus_list, local_routerstatus_t *, rs,
|
||||
{
|
||||
if (rs->should_download) {
|
||||
char *fp = tor_malloc(HEX_DIGEST_LEN+1);
|
||||
base16_encode(fp, HEX_DIGEST_LEN+1, rs->status.identity_digest,
|
||||
DIGEST_LEN);
|
||||
smartlist_add(superseded, fp);
|
||||
routerinfo_t *ri;
|
||||
if (rs->status.published_on + ROUTER_MAX_AGE < now) {
|
||||
/* This one is too old to consider. */
|
||||
++n_obsolete;
|
||||
} else if (digestmap_get(downloading, rs->status.descriptor_digest)) {
|
||||
/* We're downloading this one now. */
|
||||
++n_in_progress;
|
||||
#if 0
|
||||
/* Authorities don't call this path any longer. */
|
||||
} else if (options->AuthoritativeDir &&
|
||||
dirserv_would_reject_router(&rs->status)) {
|
||||
++n_would_reject;
|
||||
#endif
|
||||
} else if (router_get_by_descriptor_digest(rs->status.descriptor_digest)) {
|
||||
/* We have the 'best' descriptor for this router. */
|
||||
++n_uptodate;
|
||||
} else if ((ri = router_get_by_digest(rs->status.identity_digest)) &&
|
||||
ri->cache_info.published_on > rs->status.published_on) {
|
||||
/* Oddly, we have a descriptor more resent than the 'best' one, but it
|
||||
was once best. So that's okay. */
|
||||
++n_uptodate;
|
||||
} else if (rs->status.published_on + ESTIMATED_PROPAGATION_TIME > now) {
|
||||
/* Most caches probably don't have this descriptor yet. */
|
||||
++n_too_young;
|
||||
} else if (rs->next_attempt_at > now) {
|
||||
/* We failed too recently to try again. */
|
||||
++n_not_ready;
|
||||
} else {
|
||||
/* Okay, time to try it. */
|
||||
smartlist_add(downloadable, rs->status.descriptor_digest);
|
||||
++n_downloadable;
|
||||
}
|
||||
});
|
||||
|
||||
return superseded;
|
||||
#if 0
|
||||
info(LD_DIR,
|
||||
"%d routers are downloadable. %d are too old to consider. "
|
||||
"%d are in progress. %d are up-to-date. %d are too young to consider. "
|
||||
"%d failed too recently to retry.",
|
||||
n_downloadable, n_obsolete, n_in_progress, n_uptodate, n_too_young,
|
||||
n_not_ready);
|
||||
#endif
|
||||
|
||||
digestmap_free(downloading, NULL);
|
||||
return downloadable;
|
||||
}
|
||||
|
||||
/** Initiate new router downloads as needed.
|
||||
@@ -3224,8 +3280,8 @@ router_list_downloadable(void)
|
||||
* Otherwise, we ask for all descriptors that we think are different
|
||||
* from what we have.
|
||||
*/
|
||||
void
|
||||
update_router_descriptor_downloads(time_t now)
|
||||
static void
|
||||
update_router_descriptor_client_downloads(time_t now)
|
||||
{
|
||||
#define MAX_DL_PER_REQUEST 128
|
||||
#define MIN_DL_PER_REQUEST 4
|
||||
@@ -3234,20 +3290,19 @@ update_router_descriptor_downloads(time_t now)
|
||||
#define MAX_CLIENT_INTERVAL_WITHOUT_REQUEST 10*60
|
||||
#define MAX_SERVER_INTERVAL_WITHOUT_REQUEST 1*60
|
||||
smartlist_t *downloadable = NULL;
|
||||
int get_all = 0;
|
||||
int dirserv = server_mode(get_options()) && get_options()->DirPort;
|
||||
int should_delay, n_downloadable;
|
||||
if (!networkstatus_list || smartlist_len(networkstatus_list)<2)
|
||||
get_all = 1;
|
||||
or_options_t *options = get_options();
|
||||
|
||||
if (get_all) {
|
||||
notice(LD_DIR, "Launching request for all routers");
|
||||
last_routerdesc_download_attempted = now;
|
||||
directory_get_from_dirserver(DIR_PURPOSE_FETCH_SERVERDESC,"all.z",1);
|
||||
return;
|
||||
if (server_mode(options) && options->DirPort) {
|
||||
warn(LD_BUG, "Called router_descriptor_client_downloads() on a mirror?");
|
||||
}
|
||||
|
||||
downloadable = router_list_downloadable();
|
||||
if (networkstatus_list && smartlist_len(networkstatus_list) < 2) {
|
||||
/* XXXX Is this redundant? -NM */
|
||||
info(LD_DIR, "Not enough networkstatus documents to launch requests.");
|
||||
}
|
||||
|
||||
downloadable = router_list_client_downloadable();
|
||||
n_downloadable = smartlist_len(downloadable);
|
||||
if (n_downloadable >= MAX_DL_TO_DELAY) {
|
||||
debug(LD_DIR,
|
||||
@@ -3257,57 +3312,169 @@ update_router_descriptor_downloads(time_t now)
|
||||
// debug(LD_DIR, "No routerdescs need to be downloaded.");
|
||||
should_delay = 1;
|
||||
} else {
|
||||
if (dirserv) {
|
||||
should_delay = (last_routerdesc_download_attempted +
|
||||
MAX_SERVER_INTERVAL_WITHOUT_REQUEST) > now;
|
||||
} else {
|
||||
should_delay = (last_routerdesc_download_attempted +
|
||||
should_delay = (last_routerdesc_download_attempted +
|
||||
MAX_CLIENT_INTERVAL_WITHOUT_REQUEST) > now;
|
||||
}
|
||||
if (should_delay) {
|
||||
}
|
||||
if (should_delay) {
|
||||
// debug(LD_DIR, "There are not many downloadable routerdescs; "
|
||||
// "waiting till we have some more.");
|
||||
} else
|
||||
info(LD_DIR, "There are not many downloadable routerdescs, but we've "
|
||||
"been waiting long enough (%d seconds). Downloading.",
|
||||
(int)(now-last_routerdesc_download_attempted));
|
||||
} else {
|
||||
info(LD_DIR, "There are not many downloadable routerdescs, but we've "
|
||||
"been waiting long enough (%d seconds). Downloading.",
|
||||
(int)(now-last_routerdesc_download_attempted));
|
||||
}
|
||||
|
||||
if (! should_delay) {
|
||||
int i, j, n_per_request=MAX_DL_PER_REQUEST;
|
||||
size_t r_len = MAX_DL_PER_REQUEST*(HEX_DIGEST_LEN+1)+16;
|
||||
char *resource = tor_malloc(r_len);
|
||||
int i, n_per_request;
|
||||
n_per_request = (n_downloadable+MIN_REQUESTS-1) / MIN_REQUESTS;
|
||||
if (n_per_request > MAX_DL_PER_REQUEST)
|
||||
n_per_request = MAX_DL_PER_REQUEST;
|
||||
if (n_per_request < MIN_DL_PER_REQUEST)
|
||||
n_per_request = MIN_DL_PER_REQUEST;
|
||||
|
||||
if (! dirserv) {
|
||||
n_per_request = (n_downloadable+MIN_REQUESTS-1) / MIN_REQUESTS;
|
||||
if (n_per_request > MAX_DL_PER_REQUEST)
|
||||
n_per_request = MAX_DL_PER_REQUEST;
|
||||
if (n_per_request < MIN_DL_PER_REQUEST)
|
||||
n_per_request = MIN_DL_PER_REQUEST;
|
||||
}
|
||||
info(LD_DIR, "Launching %d request%s for %d router%s, %d at a time",
|
||||
(n_downloadable+n_per_request-1)/n_per_request,
|
||||
n_downloadable>n_per_request?"s":"",
|
||||
n_downloadable, n_downloadable>1?"s":"", n_per_request);
|
||||
for (i=0; i < n_downloadable; i += n_per_request) {
|
||||
char *cp = resource;
|
||||
memcpy(resource, "fp/", 3);
|
||||
cp = resource + 3;
|
||||
for (j=i; j < i+n_per_request && j < n_downloadable; ++j) {
|
||||
memcpy(cp, smartlist_get(downloadable, j), HEX_DIGEST_LEN);
|
||||
cp += HEX_DIGEST_LEN;
|
||||
*cp++ = '+';
|
||||
}
|
||||
memcpy(cp-1, ".z", 3);
|
||||
directory_get_from_dirserver(DIR_PURPOSE_FETCH_SERVERDESC,resource,1);
|
||||
initiate_descriptor_downloads(NULL, downloadable, i, i+n_per_request);
|
||||
}
|
||||
last_routerdesc_download_attempted = now;
|
||||
tor_free(resource);
|
||||
}
|
||||
SMARTLIST_FOREACH(downloadable, char *, c, tor_free(c));
|
||||
smartlist_free(downloadable);
|
||||
}
|
||||
|
||||
/* DOCDOC */
|
||||
static void
|
||||
update_router_descriptor_cache_downloads(time_t now)
|
||||
{
|
||||
smartlist_t **downloadable; /* For each authority, what can we dl from it? */
|
||||
smartlist_t **download_from; /* ... and, what will we dl from it? */
|
||||
digestmap_t *map; /* Which descs are in progress, or assigned? */
|
||||
int i, j, n;
|
||||
int n_download;
|
||||
or_options_t *options = get_options();
|
||||
|
||||
if (!(server_mode(options) && options->DirPort)) {
|
||||
warn(LD_BUG, "Called update_router_descriptor_cache_downloads() "
|
||||
"on a non-mirror?");
|
||||
}
|
||||
|
||||
if (!networkstatus_list || !smartlist_len(networkstatus_list))
|
||||
return;
|
||||
|
||||
map = digestmap_new();
|
||||
n = smartlist_len(networkstatus_list);
|
||||
|
||||
downloadable = tor_malloc_zero(sizeof(smartlist_t*) * n);
|
||||
download_from = tor_malloc_zero(sizeof(smartlist_t*) * n);
|
||||
|
||||
/* Set map[d]=1 for the digest of every descriptor that we are currently
|
||||
* downloading. */
|
||||
list_pending_descriptor_downloads(map);
|
||||
|
||||
/* For the digest of every descriptor that we don't have, and that we aren't
|
||||
* downloading, add d to downloadable[i] if the i'th networkstatus knows
|
||||
* about that descriptor, and we haven't already failed to get that
|
||||
* descriptor from the corresponding authority.
|
||||
*/
|
||||
n_download = 0;
|
||||
SMARTLIST_FOREACH(networkstatus_list, networkstatus_t *, ns,
|
||||
{
|
||||
smartlist_t *dl = smartlist_create();
|
||||
downloadable[ns_sl_idx] = dl;
|
||||
download_from[ns_sl_idx] = smartlist_create();
|
||||
SMARTLIST_FOREACH(ns->entries, routerstatus_t * , rs,
|
||||
{
|
||||
if (!rs->need_to_mirror)
|
||||
continue;
|
||||
if (router_get_by_descriptor_digest(rs->descriptor_digest)) {
|
||||
warn(LD_BUG, "We have a router descriptor, but need_to_mirror=1.");
|
||||
rs->need_to_mirror = 0;
|
||||
continue;
|
||||
}
|
||||
if (options->AuthoritativeDir && dirserv_would_reject_router(rs)) {
|
||||
rs->need_to_mirror = 0;
|
||||
continue;
|
||||
}
|
||||
if (digestmap_get(map, rs->descriptor_digest)) {
|
||||
/* We're downloading it already. */
|
||||
continue;
|
||||
} else {
|
||||
/* We could download it from this guy. */
|
||||
smartlist_add(dl, rs->descriptor_digest);
|
||||
++n_download;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
/* At random, assign descriptors to authorities such that:
|
||||
* - if d is a member of some downloadable[x], d is a member of some
|
||||
* download_from[y]. (Everything we want to download, we try to download
|
||||
* from somebody.)
|
||||
* - If d is a mamber of download_from[y], d is a member of downloadable[y].
|
||||
* (We only try to download descriptors from authorities who claim to have
|
||||
* them.)
|
||||
* - No d is a member of download_from[x] and download_from[y] s.t. x != y.
|
||||
* (We don't try to download anything from two authorities concurrently.)
|
||||
*/
|
||||
while (n_download) {
|
||||
int which_ns = crypto_rand_int(n);
|
||||
smartlist_t *dl = downloadable[which_ns];
|
||||
int idx;
|
||||
char *d;
|
||||
tor_assert(dl);
|
||||
if (!smartlist_len(dl))
|
||||
continue;
|
||||
idx = crypto_rand_int(smartlist_len(dl));
|
||||
d = smartlist_get(dl, idx);
|
||||
if (! digestmap_get(map, d)) {
|
||||
smartlist_add(download_from[which_ns], d);
|
||||
digestmap_set(map, d, (void*) 1);
|
||||
}
|
||||
smartlist_del(dl, idx);
|
||||
--n_download;
|
||||
}
|
||||
|
||||
/* Now, we can actually launch our requests. */
|
||||
for (i=0; i<n; ++i) {
|
||||
networkstatus_t *ns = smartlist_get(networkstatus_list, i);
|
||||
trusted_dir_server_t *ds =
|
||||
router_get_trusteddirserver_by_digest(ns->identity_digest);
|
||||
if (!ds) {
|
||||
warn(LD_BUG, "Networkstatus with no corresponding authority!");
|
||||
continue;
|
||||
}
|
||||
smartlist_t *dl = download_from[i];
|
||||
info(LD_DIR, "Requesting %d descriptors from authority \"%s\"",
|
||||
smartlist_len(dl), ds->nickname);
|
||||
for (j=0; j < smartlist_len(dl); j += MAX_DL_PER_REQUEST) {
|
||||
initiate_descriptor_downloads(&(ds->fake_status), dl, j,
|
||||
j+MAX_DL_PER_REQUEST);
|
||||
}
|
||||
}
|
||||
|
||||
for (i=0; i<n; ++i) {
|
||||
smartlist_free(download_from[i]);
|
||||
smartlist_free(downloadable[i]);
|
||||
}
|
||||
tor_free(download_from);
|
||||
tor_free(downloadable);
|
||||
digestmap_free(map,NULL);
|
||||
}
|
||||
|
||||
/* DOCDOC */
|
||||
void
|
||||
update_router_descriptor_downloads(time_t now)
|
||||
{
|
||||
or_options_t *options = get_options();
|
||||
if (server_mode(options) && options->DirPort) {
|
||||
update_router_descriptor_cache_downloads(now);
|
||||
} else {
|
||||
update_router_descriptor_client_downloads(now);
|
||||
}
|
||||
}
|
||||
|
||||
/** Return true iff we have enough networkstatus and router information to
|
||||
* start building circuits. Right now, this means "at least 2 networkstatus
|
||||
* documents, and at least 1/4 of expected routers." */
|
||||
@@ -3366,6 +3533,12 @@ router_reset_descriptor_download_failures(void)
|
||||
rs->n_download_failures = 0;
|
||||
rs->next_attempt_at = 0;
|
||||
});
|
||||
SMARTLIST_FOREACH(networkstatus_list, networkstatus_t *, ns,
|
||||
SMARTLIST_FOREACH(ns->entries, routerstatus_t *, rs,
|
||||
{
|
||||
if (!router_get_by_descriptor_digest(rs->descriptor_digest))
|
||||
rs->need_to_mirror = 1;
|
||||
}));
|
||||
last_routerdesc_download_attempted = 0;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user