summaryrefslogtreecommitdiff
path: root/src/or/dirserv.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/or/dirserv.c')
-rw-r--r--src/or/dirserv.c497
1 files changed, 413 insertions, 84 deletions
diff --git a/src/or/dirserv.c b/src/or/dirserv.c
index e85575e6f9..b159afed97 100644
--- a/src/or/dirserv.c
+++ b/src/or/dirserv.c
@@ -36,7 +36,7 @@ static int runningrouters_is_dirty = 1;
static int the_v2_networkstatus_is_dirty = 1;
static void directory_remove_invalid(void);
-static int dirserv_regenerate_directory(void);
+static cached_dir_t *dirserv_regenerate_directory(void);
static char *format_versions_list(config_line_t *ln);
/* Should be static; exposed for testing */
int add_fingerprint_to_dir(const char *nickname, const char *fp,
@@ -51,6 +51,7 @@ dirserv_get_status_impl(const char *fp, const char *nickname,
const char **msg, int should_log);
static int dirserv_thinks_router_is_reachable(routerinfo_t *router,
time_t now);
+static void clear_cached_dir(cached_dir_t *d);
/************** Fingerprint handling code ************/
@@ -121,24 +122,31 @@ dirserv_add_own_fingerprint(const char *nickname, crypto_pk_env_t *pk)
return 0;
}
-/** Parse the nickname-\>fingerprint mappings stored in the file named
- * <b>fname</b>. The file format is line-based, with each non-blank
- * holding one nickname, some space, and a fingerprint for that
- * nickname. On success, replace the current fingerprint list with
- * the contents of <b>fname</b> and return 0. On failure, leave the
- * current fingerprint list untouched, and return -1. */
+/** Load the nickname-\>fingerprint mappings stored in the approved-routers
+ * file. The file format is line-based, with each non-blank holding one
+ * nickname, some space, and a fingerprint for that nickname. On success,
+ * replace the current fingerprint list with the new list and return 0. On
+ * failure, leave the current fingerprint list untouched, and
+ * return -1. */
int
-dirserv_parse_fingerprint_file(const char *fname)
+dirserv_load_fingerprint_file(void)
{
+ char fname[512];
char *cf;
char *nickname, *fingerprint;
smartlist_t *fingerprint_list_new;
int result;
config_line_t *front=NULL, *list;
+ or_options_t *options = get_options();
+
+ tor_snprintf(fname, sizeof(fname),
+ "%s/approved-routers", options->DataDirectory);
+ log_info(LD_GENERAL,
+ "Reloading approved fingerprints from \"%s\"...", fname);
cf = read_file_to_str(fname, 0);
if (!cf) {
- if (get_options()->NamingAuthoritativeDir) {
+ if (options->NamingAuthoritativeDir) {
log_warn(LD_FS, "Cannot open fingerprint file '%s'. Failing.", fname);
return -1;
} else {
@@ -454,6 +462,7 @@ authdir_wants_to_reject_router(routerinfo_t *ri, const char **msg,
case FP_INVALID:
ri->is_named = ri->is_valid = 0;
break;
+ case FP_REJECT:
default:
tor_assert(0);
}
@@ -480,7 +489,7 @@ dirserv_add_descriptor(const char *desc, const char **msg)
*msg = NULL;
/* Check: is the descriptor syntactically valid? */
- ri = router_parse_entry_from_string(desc, NULL);
+ ri = router_parse_entry_from_string(desc, NULL, 1);
if (!ri) {
log_warn(LD_DIRSERV, "Couldn't parse uploaded server descriptor");
*msg = "Rejected: Couldn't parse server descriptor.";
@@ -656,7 +665,7 @@ list_single_server_status(routerinfo_t *desc, int is_live)
/** Each server needs to have passed a reachability test no more
* than this number of seconds ago, or he is listed as down in
* the directory. */
-#define REACHABLE_TIMEOUT (30*60)
+#define REACHABLE_TIMEOUT (45*60)
/** Treat a router as alive if
* - It's me, and I'm not hibernating.
@@ -760,11 +769,12 @@ live_enough_for_v1_dir(routerinfo_t *ri, time_t now)
/** Generate a new v1 directory and write it into a newly allocated string.
* Point *<b>dir_out</b> to the allocated string. Sign the
* directory with <b>private_key</b>. Return 0 on success, -1 on
- * failure.
+ * failure. If <b>complete</b> is set, give us all the descriptors;
+ * otherwise leave out non-running and non-valid ones.
*/
int
dirserv_dump_directory_to_string(char **dir_out,
- crypto_pk_env_t *private_key)
+ crypto_pk_env_t *private_key, int complete)
{
char *cp;
char *router_status;
@@ -798,7 +808,7 @@ dirserv_dump_directory_to_string(char **dir_out,
buf_len = 2048+strlen(recommended_versions)+
strlen(router_status);
SMARTLIST_FOREACH(rl->routers, routerinfo_t *, ri,
- if (live_enough_for_v1_dir(ri, now))
+ if (complete || live_enough_for_v1_dir(ri, now))
buf_len += ri->cache_info.signed_descriptor_len+1);
buf = tor_malloc(buf_len);
/* We'll be comparing against buf_len throughout the rest of the
@@ -824,7 +834,7 @@ dirserv_dump_directory_to_string(char **dir_out,
{
size_t len = ri->cache_info.signed_descriptor_len;
const char *body;
- if (!live_enough_for_v1_dir(ri, now))
+ if (!complete && !live_enough_for_v1_dir(ri, now))
continue;
if (cp+len+1 >= buf+buf_len)
goto truncated;
@@ -864,12 +874,12 @@ dirserv_dump_directory_to_string(char **dir_out,
}
/** Most recently generated encoded signed directory. (auth dirservers only.)*/
-static cached_dir_t the_directory = { NULL, NULL, 0, 0, 0 };
+static cached_dir_t *the_directory = NULL;
/* Used only by non-auth dirservers: The directory and runningrouters we'll
* serve when requested. */
-static cached_dir_t cached_directory = { NULL, NULL, 0, 0, 0 };
-static cached_dir_t cached_runningrouters = { NULL, NULL, 0, 0, 0 };
+static cached_dir_t *cached_directory = NULL;
+static cached_dir_t cached_runningrouters = { NULL, NULL, 0, 0, 0, -1 };
/* Used for other dirservers' v2 network statuses. Map from hexdigest to
* cached_dir_t. */
@@ -879,7 +889,7 @@ static digestmap_t *cached_v2_networkstatus = NULL;
* <b>directory</b> published on <b>when</b>, unless <b>when</b> is older than
* the last value, or too far in the future.
*
- * Does not copy <b>directory</b>; free it if it isn't used.
+ * Does not copy <b>directory</b>; frees it if it isn't used.
*/
static void
set_cached_dir(cached_dir_t *d, char *directory, time_t when)
@@ -906,6 +916,32 @@ set_cached_dir(cached_dir_t *d, char *directory, time_t when)
}
}
+/** DOCDOC */
+void
+cached_dir_decref(cached_dir_t *d)
+{
+ if (!d || --d->refcnt > 0)
+ return;
+ clear_cached_dir(d);
+ tor_free(d);
+}
+
+/** DOCDOC */
+static cached_dir_t *
+new_cached_dir(char *s, time_t published)
+{
+ cached_dir_t *d = tor_malloc_zero(sizeof(cached_dir_t));
+ d->refcnt = 1;
+ d->dir = s;
+ d->dir_len = strlen(s);
+ d->published = published;
+ if (tor_gzip_compress(&(d->dir_z), &(d->dir_z_len), d->dir, d->dir_len,
+ ZLIB_METHOD)) {
+ log_warn(LD_BUG, "Error compressing directory");
+ }
+ return d;
+}
+
/** Remove all storage held in <b>d</b>, but do not free <b>d</b> itself. */
static void
clear_cached_dir(cached_dir_t *d)
@@ -917,11 +953,10 @@ clear_cached_dir(cached_dir_t *d)
/** Free all storage held by the cached_dir_t in <b>d</b>. */
static void
-free_cached_dir(void *_d)
+_free_cached_dir(void *_d)
{
cached_dir_t *d = (cached_dir_t *)_d;
- clear_cached_dir(d);
- tor_free(d);
+ cached_dir_decref(d);
}
/** If we have no cached directory, or it is older than <b>when</b>, then
@@ -931,9 +966,12 @@ void
dirserv_set_cached_directory(const char *directory, time_t published,
int is_running_routers)
{
- cached_dir_t *d;
- d = is_running_routers ? &cached_runningrouters : &cached_directory;
- set_cached_dir(d, tor_strdup(directory), published);
+ if (is_running_routers) {
+ set_cached_dir(&cached_runningrouters, tor_strdup(directory), published);
+ } else {
+ cached_dir_decref(cached_directory);
+ cached_directory = new_cached_dir(tor_strdup(directory), published);
+ }
}
/** We've just received a v2 network-status for an authoritative directory
@@ -947,28 +985,30 @@ dirserv_set_cached_networkstatus_v2(const char *networkstatus,
const char *identity,
time_t published)
{
- cached_dir_t *d;
+ cached_dir_t *d, *old_d;
smartlist_t *trusted_dirs;
if (!cached_v2_networkstatus)
cached_v2_networkstatus = digestmap_new();
- if (!(d = digestmap_get(cached_v2_networkstatus, identity))) {
- if (!networkstatus)
- return;
- d = tor_malloc_zero(sizeof(cached_dir_t));
- digestmap_set(cached_v2_networkstatus, identity, d);
- }
+ old_d = digestmap_get(cached_v2_networkstatus, identity);
+ if (!old_d && !networkstatus)
+ return;
- tor_assert(d);
if (networkstatus) {
- if (published > d->published) {
- set_cached_dir(d, tor_strdup(networkstatus), published);
+ if (!old_d || published > old_d->published) {
+ d = new_cached_dir(tor_strdup(networkstatus), published);
+ digestmap_set(cached_v2_networkstatus, identity, d);
+ if (old_d)
+ cached_dir_decref(old_d);
}
} else {
- free_cached_dir(d);
- digestmap_remove(cached_v2_networkstatus, identity);
+ if (old_d) {
+ digestmap_remove(cached_v2_networkstatus, identity);
+ cached_dir_decref(old_d);
+ }
}
+ /* Now purge old entries. */
trusted_dirs = router_get_trusted_dir_servers();
if (digestmap_size(cached_v2_networkstatus) >
smartlist_len(trusted_dirs) + MAX_UNTRUSTED_NETWORKSTATUSES) {
@@ -993,7 +1033,7 @@ dirserv_set_cached_networkstatus_v2(const char *networkstatus,
tor_assert(oldest);
d = digestmap_remove(cached_v2_networkstatus, oldest);
if (d)
- free_cached_dir(d);
+ cached_dir_decref(d);
}
}
@@ -1005,7 +1045,7 @@ dirserv_set_cached_networkstatus_v2(const char *networkstatus,
static cached_dir_t *
dirserv_pick_cached_dir_obj(cached_dir_t *cache_src,
cached_dir_t *auth_src,
- time_t dirty, int (*regenerate)(void),
+ time_t dirty, cached_dir_t *(*regenerate)(void),
const char *name,
int is_v1_object)
{
@@ -1018,7 +1058,7 @@ dirserv_pick_cached_dir_obj(cached_dir_t *cache_src,
/* We're authoritative. */
if (regenerate != NULL) {
if (dirty && dirty + DIR_REGEN_SLACK_TIME < time(NULL)) {
- if (regenerate()) {
+ if (!(auth_src = regenerate())) {
log_err(LD_BUG, "Couldn't generate %s?", name);
exit(1);
}
@@ -1042,10 +1082,11 @@ dirserv_pick_cached_dir_obj(cached_dir_t *cache_src,
* this kind of object.
**/
static size_t
-dirserv_get_obj(const char **out, int compress,
+dirserv_get_obj(const char **out,
+ int compress,
cached_dir_t *cache_src,
cached_dir_t *auth_src,
- time_t dirty, int (*regenerate)(void),
+ time_t dirty, cached_dir_t *(*regenerate)(void),
const char *name,
int is_v1_object)
{
@@ -1064,53 +1105,54 @@ dirserv_get_obj(const char **out, int compress,
}
}
-/** Set *<b>directory</b> to the most recently generated encoded signed
- * directory, generating a new one as necessary. If not an authoritative
- * directory may return 0 if no directory is yet cached.*/
-size_t
-dirserv_get_directory(const char **directory, int compress)
+/** Return the most recently generated encoded signed directory, generating a
+ * new one as necessary. If not an authoritative directory may return NULL if
+ * no directory is yet cached.*/
+cached_dir_t *
+dirserv_get_directory(void)
{
- return dirserv_get_obj(directory, compress,
- &cached_directory, &the_directory,
- the_directory_is_dirty,
- dirserv_regenerate_directory,
- "server directory", 1);
+ return dirserv_pick_cached_dir_obj(cached_directory, the_directory,
+ the_directory_is_dirty,
+ dirserv_regenerate_directory,
+ "server directory", 1);
}
/**
- * Generate a fresh v1 directory (authdirservers only.)
+ * Generate a fresh v1 directory (authdirservers only); set the_directory
+ * and return a pointer to the new value.
*/
-static int
+static cached_dir_t *
dirserv_regenerate_directory(void)
{
char *new_directory=NULL;
if (dirserv_dump_directory_to_string(&new_directory,
- get_identity_key())) {
+ get_identity_key(), 0)) {
log_warn(LD_BUG, "Error creating directory.");
tor_free(new_directory);
- return -1;
+ return NULL;
}
- set_cached_dir(&the_directory, new_directory, time(NULL));
+ cached_dir_decref(the_directory);
+ the_directory = new_cached_dir(new_directory, time(NULL));
log_info(LD_DIRSERV,"New directory (size %d) has been built.",
- (int)the_directory.dir_len);
+ (int)the_directory->dir_len);
log_debug(LD_DIRSERV,"New directory (size %d):\n%s",
- (int)the_directory.dir_len, the_directory.dir);
+ (int)the_directory->dir_len, the_directory->dir);
the_directory_is_dirty = 0;
/* Save the directory to disk so we re-load it quickly on startup.
*/
- dirserv_set_cached_directory(the_directory.dir, time(NULL), 0);
+ dirserv_set_cached_directory(the_directory->dir, time(NULL), 0);
- return 0;
+ return the_directory;
}
/** For authoritative directories: the current (v1) network status */
-static cached_dir_t the_runningrouters = { NULL, NULL, 0, 0, 0 };
+static cached_dir_t the_runningrouters = { NULL, NULL, 0, 0, 0, -1 };
/** Replace the current running-routers list with a newly generated one. */
-static int
+static cached_dir_t *
generate_runningrouters(void)
{
char *s=NULL;
@@ -1155,11 +1197,11 @@ generate_runningrouters(void)
set_cached_dir(&the_runningrouters, s, time(NULL));
runningrouters_is_dirty = 0;
- return 0;
+ return &the_runningrouters;
err:
tor_free(s);
tor_free(router_status);
- return -1;
+ return NULL;
}
/** Set *<b>rr</b> to the most recently generated encoded signed
@@ -1176,7 +1218,7 @@ dirserv_get_runningrouters(const char **rr, int compress)
}
/** For authoritative directories: the current (v2) network status */
-static cached_dir_t the_v2_networkstatus = { NULL, NULL, 0, 0, 0 };
+static cached_dir_t *the_v2_networkstatus = NULL;
static int
should_generate_v2_networkstatus(void)
@@ -1260,6 +1302,9 @@ dirserv_compute_performance_thresholds(routerlist_t *rl)
if (smartlist_len(bandwidths)) {
fast_bandwidth = *(uint32_t*)smartlist_get(bandwidths,
smartlist_len(bandwidths)/8);
+ if (fast_bandwidth < ROUTER_REQUIRED_MIN_BANDWIDTH)
+ fast_bandwidth = *(uint32_t*)smartlist_get(bandwidths,
+ smartlist_len(bandwidths)/4);
guard_bandwidth = *(uint32_t*)smartlist_get(bandwidths,
smartlist_len(bandwidths)/2);
}
@@ -1278,7 +1323,7 @@ dirserv_compute_performance_thresholds(routerlist_t *rl)
/** For authoritative directories only: replace the contents of
* <b>the_v2_networkstatus</b> with a newly generated network status
* object. */
-static int
+static cached_dir_t *
generate_v2_networkstatus(void)
{
#define LONGEST_STATUS_FLAG_NAME_LEN 7
@@ -1290,7 +1335,7 @@ generate_v2_networkstatus(void)
/* second line */ \
(LONGEST_STATUS_FLAG_NAME_LEN+1)*N_STATUS_FLAGS + 2)
- int r = -1;
+ cached_dir_t *r = NULL;
size_t len, identity_pkey_len;
char *status = NULL, *client_versions = NULL, *server_versions = NULL,
*identity_pkey = NULL, *hostname = NULL;
@@ -1310,7 +1355,7 @@ generate_v2_networkstatus(void)
int versioning = options->VersioningAuthoritativeDir;
const char *contact;
- if (resolve_my_address(options, &addr, &hostname)<0) {
+ if (resolve_my_address(LOG_WARN, options, &addr, &hostname)<0) {
log_warn(LD_NET, "Couldn't resolve my hostname");
goto done;
}
@@ -1359,9 +1404,9 @@ generate_v2_networkstatus(void)
naming ? " Names" : "",
versioning ? " Versions" : "",
versioning ? "client-versions " : "",
- client_versions,
+ versioning ? client_versions : "",
versioning ? "\nserver-versions " : "",
- server_versions,
+ versioning ? server_versions : "",
versioning ? "\n" : "",
identity_pkey);
outp = status + strlen(status);
@@ -1452,13 +1497,14 @@ generate_v2_networkstatus(void)
goto done;
}
- set_cached_dir(&the_v2_networkstatus, status, time(NULL));
+ if (the_v2_networkstatus)
+ cached_dir_decref(the_v2_networkstatus);
+ the_v2_networkstatus = new_cached_dir(status, time(NULL));
status = NULL; /* So it doesn't get double-freed. */
the_v2_networkstatus_is_dirty = 0;
- router_set_networkstatus(the_v2_networkstatus.dir, time(NULL), NS_GENERATED,
- NULL);
-
- r = 0;
+ router_set_networkstatus(the_v2_networkstatus->dir,
+ time(NULL), NS_GENERATED, NULL);
+ r = the_v2_networkstatus;
done:
tor_free(client_versions);
tor_free(server_versions);
@@ -1468,6 +1514,45 @@ generate_v2_networkstatus(void)
return r;
}
+/* DOCDOC */
+void
+dirserv_get_networkstatus_v2_fingerprints(smartlist_t *result,
+ const char *key)
+{
+ tor_assert(result);
+
+ if (!cached_v2_networkstatus)
+ cached_v2_networkstatus = digestmap_new();
+
+ if (should_generate_v2_networkstatus())
+ generate_v2_networkstatus();
+
+ if (!strcmp(key,"authority")) {
+ if (get_options()->AuthoritativeDir) {
+ routerinfo_t *me = router_get_my_routerinfo();
+ if (me)
+ smartlist_add(result,
+ tor_memdup(me->cache_info.identity_digest, DIGEST_LEN));
+ }
+ } else if (!strcmp(key, "all")) {
+ digestmap_iter_t *iter;
+ iter = digestmap_iter_init(cached_v2_networkstatus);
+ while (!digestmap_iter_done(iter)) {
+ const char *ident;
+ void *val;
+ digestmap_iter_get(iter, &ident, &val);
+ smartlist_add(result, tor_memdup(ident, DIGEST_LEN));
+ iter = digestmap_iter_next(cached_v2_networkstatus, iter);
+ }
+ smartlist_sort_digests(result);
+ if (smartlist_len(result) == 0)
+ log_warn(LD_DIRSERV,
+ "Client requested 'all' network status objects; we have none.");
+ } else if (!strcmpstart(key, "fp/")) {
+ dir_split_resource_into_fingerprints(key+3, result, NULL, 1, 1);
+ }
+}
+
/** Look for a network status object as specified by <b>key</b>, which should
* be either "authority" (to find a network status generated by us), a hex
* identity digest (to find a network status generated by given directory), or
@@ -1486,7 +1571,7 @@ dirserv_get_networkstatus_v2(smartlist_t *result,
if (get_options()->AuthoritativeDir) {
cached_dir_t *d =
dirserv_pick_cached_dir_obj(NULL,
- &the_v2_networkstatus,
+ the_v2_networkstatus,
the_v2_networkstatus_is_dirty,
generate_v2_networkstatus,
"network status list", 0);
@@ -1513,7 +1598,7 @@ dirserv_get_networkstatus_v2(smartlist_t *result,
"Client requested 'all' network status objects; we have none.");
} else if (!strcmpstart(key, "fp/")) {
smartlist_t *digests = smartlist_create();
- dir_split_resource_into_fingerprints(key+3, digests, NULL, 1);
+ dir_split_resource_into_fingerprints(key+3, digests, NULL, 1, 1);
SMARTLIST_FOREACH(digests, char *, cp,
{
cached_dir_t *cached;
@@ -1534,6 +1619,44 @@ dirserv_get_networkstatus_v2(smartlist_t *result,
}
}
+/** As dirserv_get_routerdescs(), but instead of getting signed_descriptor_t
+ * pointers, adds copies of digests to fps_out. For a /tor/server/d/ request,
+ * adds descriptor digests; for other requests, adds identity digests.
+ */
+int
+dirserv_get_routerdesc_fingerprints(smartlist_t *fps_out, const char *key,
+ const char **msg)
+{
+ *msg = NULL;
+
+ if (!strcmp(key, "/tor/server/all")) {
+ routerlist_t *rl = router_get_routerlist();
+ SMARTLIST_FOREACH(rl->routers, routerinfo_t *, r,
+ smartlist_add(fps_out,
+ tor_memdup(r->cache_info.identity_digest, DIGEST_LEN)));
+ } else if (!strcmp(key, "/tor/server/authority")) {
+ routerinfo_t *ri = router_get_my_routerinfo();
+ if (ri)
+ smartlist_add(fps_out,
+ tor_memdup(ri->cache_info.identity_digest, DIGEST_LEN));
+ } else if (!strcmpstart(key, "/tor/server/d/")) {
+ key += strlen("/tor/server/d/");
+ dir_split_resource_into_fingerprints(key, fps_out, NULL, 1, 1);
+ } else if (!strcmpstart(key, "/tor/server/fp/")) {
+ key += strlen("/tor/server/fp/");
+ dir_split_resource_into_fingerprints(key, fps_out, NULL, 1, 1);
+ } else {
+ *msg = "Key not recognized";
+ return -1;
+ }
+
+ if (!smartlist_len(fps_out)) {
+ *msg = "Servers unavailable";
+ return -1;
+ }
+ return 0;
+}
+
/** Add a signed_descriptor_t to <b>descs_out</b> for each router matching
* <b>key</b>. The key should be either
* - "/tor/server/authority" for our own routerinfo;
@@ -1569,7 +1692,7 @@ dirserv_get_routerdescs(smartlist_t *descs_out, const char *key,
} else if (!strcmpstart(key, "/tor/server/d/")) {
smartlist_t *digests = smartlist_create();
key += strlen("/tor/server/d/");
- dir_split_resource_into_fingerprints(key, digests, NULL, 1);
+ dir_split_resource_into_fingerprints(key, digests, NULL, 1, 1);
SMARTLIST_FOREACH(digests, const char *, d,
{
signed_descriptor_t *sd = router_get_by_descriptor_digest(d);
@@ -1582,7 +1705,7 @@ dirserv_get_routerdescs(smartlist_t *descs_out, const char *key,
smartlist_t *digests = smartlist_create();
time_t cutoff = time(NULL) - ROUTER_MAX_AGE_TO_PUBLISH;
key += strlen("/tor/server/fp/");
- dir_split_resource_into_fingerprints(key, digests, NULL, 1);
+ dir_split_resource_into_fingerprints(key, digests, NULL, 1, 1);
SMARTLIST_FOREACH(digests, const char *, d,
{
if (router_digest_is_me(d)) {
@@ -1633,6 +1756,7 @@ dirserv_orconn_tls_done(const char *address,
tor_assert(address);
tor_assert(digest_rcvd);
tor_assert(nickname_rcvd);
+ (void) as_advertised; // XXXX This should really be implemented. -NM
// XXXXNM We should really have a better solution here than dropping
// XXXXNM whole routers; otherwise, they come back way too easily.
@@ -1671,6 +1795,211 @@ dirserv_orconn_tls_done(const char *address,
}
}
+/** Auth dir server only: if <b>try_all</b> is 1, launch connections to
+ * all known routers; else we want to load balance such that we only
+ * try a few connections per call.
+ *
+ * The load balancing is such that if we get called once every ten
+ * seconds, we will cycle through all the tests in 1280 seconds (a
+ * bit over 20 minutes).
+ */
+void
+dirserv_test_reachability(int try_all)
+{
+ time_t now = time(NULL);
+ routerlist_t *rl = router_get_routerlist();
+ static char ctr = 0;
+
+ SMARTLIST_FOREACH(rl->routers, routerinfo_t *, router, {
+ const char *id_digest = router->cache_info.identity_digest;
+ if (router_is_me(router))
+ continue;
+ if (try_all || (((uint8_t)id_digest[0]) % 128) == ctr) {
+ log_debug(LD_OR,"Testing reachability of %s at %s:%u.",
+ router->nickname, router->address, router->or_port);
+ /* Remember when we started trying to determine reachability */
+ if (!router->testing_since)
+ router->testing_since = now;
+ connection_or_connect(router->addr, router->or_port,
+ id_digest);
+ }
+ });
+ if (!try_all) /* increment ctr */
+ ctr = (ctr + 1) % 128;
+}
+
+/** When we're spooling data onto our outbuf, add more whenever we dip
+ * below this threshold. */
+#define DIRSERV_BUFFER_MIN 16384
+
+static int
+connection_dirserv_finish_spooling(dir_connection_t *conn)
+{
+ if (conn->zlib_state) {
+ connection_write_to_buf_zlib(conn, "", 0, 1);
+ tor_zlib_free(conn->zlib_state);
+ conn->zlib_state = NULL;
+ }
+ conn->dir_spool_src = DIR_SPOOL_NONE;
+ return 0;
+}
+
+/** DOCDOC */
+static int
+connection_dirserv_add_servers_to_outbuf(dir_connection_t *conn)
+{
+ int by_fp = conn->dir_spool_src == DIR_SPOOL_SERVER_BY_FP;
+
+ while (smartlist_len(conn->fingerprint_stack) &&
+ buf_datalen(conn->_base.outbuf) < DIRSERV_BUFFER_MIN) {
+ const char *body;
+ char *fp = smartlist_pop_last(conn->fingerprint_stack);
+ signed_descriptor_t *sd = NULL;
+ if (by_fp) {
+ if (router_digest_is_me(fp)) {
+ sd = &(router_get_my_routerinfo()->cache_info);
+ } else {
+ routerinfo_t *ri = router_get_by_digest(fp);
+ if (ri &&
+ ri->cache_info.published_on > time(NULL)-ROUTER_MAX_AGE_TO_PUBLISH)
+ sd = &ri->cache_info;
+ }
+ } else
+ sd = router_get_by_descriptor_digest(fp);
+ tor_free(fp);
+ if (!sd)
+ continue;
+ body = signed_descriptor_get_body(sd);
+ if (conn->zlib_state) {
+ int last = ! smartlist_len(conn->fingerprint_stack);
+ connection_write_to_buf_zlib(conn, body,
+ sd->signed_descriptor_len, last);
+ if (last) {
+ tor_zlib_free(conn->zlib_state);
+ conn->zlib_state = NULL;
+ }
+ } else {
+ connection_write_to_buf(body,
+ sd->signed_descriptor_len,
+ TO_CONN(conn));
+ }
+ }
+
+ if (!smartlist_len(conn->fingerprint_stack)) {
+ /* We just wrote the last one; finish up. */
+ conn->dir_spool_src = DIR_SPOOL_NONE;
+ smartlist_free(conn->fingerprint_stack);
+ conn->fingerprint_stack = NULL;
+ }
+ return 0;
+}
+
+/** DOCDOC */
+static int
+connection_dirserv_add_dir_bytes_to_outbuf(dir_connection_t *conn)
+{
+ int bytes, remaining;
+
+ bytes = DIRSERV_BUFFER_MIN - buf_datalen(conn->_base.outbuf);
+ tor_assert(bytes > 0);
+ tor_assert(conn->cached_dir);
+ if (bytes < 8192)
+ bytes = 8192;
+ remaining = conn->cached_dir->dir_z_len - conn->cached_dir_offset;
+ if (bytes > remaining)
+ bytes = remaining;
+
+ if (conn->zlib_state) {
+ connection_write_to_buf_zlib(conn,
+ conn->cached_dir->dir_z + conn->cached_dir_offset,
+ bytes, bytes == remaining);
+ } else {
+ connection_write_to_buf(conn->cached_dir->dir_z + conn->cached_dir_offset,
+ bytes, TO_CONN(conn));
+ }
+ conn->cached_dir_offset += bytes;
+ if (conn->cached_dir_offset == (int)conn->cached_dir->dir_z_len) {
+ /* We just wrote the last one; finish up. */
+ connection_dirserv_finish_spooling(conn);
+ cached_dir_decref(conn->cached_dir);
+ conn->cached_dir = NULL;
+ }
+ return 0;
+}
+
+/* DOCDOC */
+static int
+connection_dirserv_add_networkstatus_bytes_to_outbuf(dir_connection_t *conn)
+{
+
+ while (buf_datalen(conn->_base.outbuf) < DIRSERV_BUFFER_MIN) {
+ if (conn->cached_dir) {
+ int uncompressing = (conn->zlib_state != NULL);
+ int r = connection_dirserv_add_dir_bytes_to_outbuf(conn);
+ if (conn->dir_spool_src == DIR_SPOOL_NONE) {
+ /* add_dir_bytes thinks we're done with the cached_dir. But we
+ * may have more cached_dirs! */
+ conn->dir_spool_src = DIR_SPOOL_NETWORKSTATUS;
+ /* This bit is tricky. If we were uncompressing the last
+ * networkstatus, we may need to make a new zlib object to
+ * uncompress the next one. */
+ if (uncompressing && ! conn->zlib_state &&
+ conn->fingerprint_stack &&
+ smartlist_len(conn->fingerprint_stack)) {
+ conn->zlib_state = tor_zlib_new(0, ZLIB_METHOD);
+ }
+ }
+ if (r) return r;
+ } else if (conn->fingerprint_stack &&
+ smartlist_len(conn->fingerprint_stack)) {
+ /* Add another networkstatus; start serving it. */
+ char *fp = smartlist_pop_last(conn->fingerprint_stack);
+ cached_dir_t *d;
+ if (router_digest_is_me(fp))
+ d = the_v2_networkstatus;
+ else
+ d = digestmap_get(cached_v2_networkstatus, fp);
+ tor_free(fp);
+ if (d) {
+ ++d->refcnt;
+ conn->cached_dir = d;
+ conn->cached_dir_offset = 0;
+ }
+ } else {
+ connection_dirserv_finish_spooling(conn);
+ if (conn->fingerprint_stack)
+ smartlist_free(conn->fingerprint_stack);
+ conn->fingerprint_stack = NULL;
+ return 0;
+ }
+ }
+ return 0;
+}
+
+/** Called whenever we have flushed some directory data in state
+ * SERVER_WRITING. */
+int
+connection_dirserv_flushed_some(dir_connection_t *conn)
+{
+ tor_assert(conn->_base.state == DIR_CONN_STATE_SERVER_WRITING);
+
+ if (buf_datalen(conn->_base.outbuf) >= DIRSERV_BUFFER_MIN)
+ return 0;
+
+ switch (conn->dir_spool_src) {
+ case DIR_SPOOL_SERVER_BY_DIGEST:
+ case DIR_SPOOL_SERVER_BY_FP:
+ return connection_dirserv_add_servers_to_outbuf(conn);
+ case DIR_SPOOL_CACHED_DIR:
+ return connection_dirserv_add_dir_bytes_to_outbuf(conn);
+ case DIR_SPOOL_NETWORKSTATUS:
+ return connection_dirserv_add_networkstatus_bytes_to_outbuf(conn);
+ case DIR_SPOOL_NONE:
+ default:
+ return 0;
+ }
+}
+
/** Release all storage used by the directory server. */
void
dirserv_free_all(void)
@@ -1683,13 +2012,13 @@ dirserv_free_all(void)
smartlist_free(fingerprint_list);
fingerprint_list = NULL;
}
- clear_cached_dir(&the_directory);
+ cached_dir_decref(the_directory);
clear_cached_dir(&the_runningrouters);
- clear_cached_dir(&the_v2_networkstatus);
- clear_cached_dir(&cached_directory);
+ cached_dir_decref(the_v2_networkstatus);
+ cached_dir_decref(cached_directory);
clear_cached_dir(&cached_runningrouters);
if (cached_v2_networkstatus) {
- digestmap_free(cached_v2_networkstatus, free_cached_dir);
+ digestmap_free(cached_v2_networkstatus, _free_cached_dir);
cached_v2_networkstatus = NULL;
}
}