diff options
Diffstat (limited to 'src/or/dirserv.c')
-rw-r--r-- | src/or/dirserv.c | 497 |
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; } } |