aboutsummaryrefslogtreecommitdiff
path: root/src/or/dirserv.c
diff options
context:
space:
mode:
authorNick Mathewson <nickm@torproject.org>2006-06-18 07:38:55 +0000
committerNick Mathewson <nickm@torproject.org>2006-06-18 07:38:55 +0000
commit1d9923da7c197724d453ff315289c3c70536ee09 (patch)
treee350b26733a666c8052b73415bfd2adaa61c3f4b /src/or/dirserv.c
parent73ada60d64d9a97309cbf01b4b1b2c4b1bb3b8b3 (diff)
downloadtor-1d9923da7c197724d453ff315289c3c70536ee09.tar.gz
tor-1d9923da7c197724d453ff315289c3c70536ee09.zip
Instead of adding servers and v1 directories to buffers en masse, directory servers add them on the fly as their outbufs are depleted. This will save ram on busy dirservers.
svn:r6641
Diffstat (limited to 'src/or/dirserv.c')
-rw-r--r--src/or/dirserv.c232
1 files changed, 207 insertions, 25 deletions
diff --git a/src/or/dirserv.c b/src/or/dirserv.c
index dece9a6a80..be3a3dc6d3 100644
--- a/src/or/dirserv.c
+++ b/src/or/dirserv.c
@@ -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 ************/
@@ -865,12 +866,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. */
@@ -907,6 +908,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)
@@ -932,9 +959,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
@@ -1043,7 +1073,8 @@ 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),
@@ -1065,17 +1096,16 @@ 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);
}
/**
@@ -1092,23 +1122,24 @@ dirserv_regenerate_directory(void)
tor_free(new_directory);
return -1;
}
- 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;
}
/** 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
@@ -1177,7 +1208,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, NULL, 0, 0, 0, -1 };
static int
should_generate_v2_networkstatus(void)
@@ -1535,6 +1566,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);
+ } else if (!strcmpstart(key, "/tor/server/fp/")) {
+ key += strlen("/tor/server/fp/");
+ dir_split_resource_into_fingerprints(key, fps_out, NULL, 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;
@@ -1673,6 +1742,119 @@ dirserv_orconn_tls_done(const char *address,
}
}
+/** When we're spooling data onto our outbuf, add more whenever we dip
+ * below this threshold. */
+#define DIRSERV_BUFFER_MIN 16384
+
+/** DOCDOC */
+static int
+connection_dirserv_add_servers_to_outbuf(connection_t *conn)
+{
+ int fp;
+
+ if (!strcmpstart(conn->requested_resource, "/tor/server/d/"))
+ fp = 0;
+ else
+ fp = 1;
+
+ while (smartlist_len(conn->fingerprint_stack) &&
+ buf_datalen(conn->outbuf) < DIRSERV_BUFFER_MIN) {
+ char *fp = smartlist_pop_last(conn->fingerprint_stack);
+ signed_descriptor_t *sd = NULL;
+ if (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;
+ if (conn->zlib_state) {
+ write_to_buf_zlib(conn->outbuf, conn->zlib_state,
+ sd->signed_descriptor_body, sd->signed_descriptor_len,
+ 0);
+ } else {
+ write_to_buf(sd->signed_descriptor_body, sd->signed_descriptor_len,
+ conn->outbuf);
+ }
+ }
+
+ if (!smartlist_len(conn->fingerprint_stack)) {
+ /* We just wrote the last one; finish up. */
+ if (conn->zlib_state) {
+ write_to_buf_zlib(conn->outbuf, conn->zlib_state, "", 0, 1);
+ tor_zlib_free(conn->zlib_state);
+ conn->zlib_state = NULL;
+ }
+ smartlist_free(conn->fingerprint_stack);
+ conn->fingerprint_stack = NULL;
+ }
+ return 0;
+}
+
+/** DOCDOC */
+static int
+connection_dirserv_add_dir_bytes_to_outbuf(connection_t *conn)
+{
+ int bytes, remaining;
+
+ bytes = DIRSERV_BUFFER_MIN - buf_datalen(conn->outbuf);
+ tor_assert(bytes > 0);
+ 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) {
+ write_to_buf_zlib(conn->outbuf, conn->zlib_state,
+ conn->cached_dir->dir_z + conn->cached_dir_offset,
+ bytes, bytes == remaining);
+ } else {
+ write_to_buf(conn->cached_dir->dir_z + conn->cached_dir_offset,
+ bytes, conn->outbuf);
+ }
+ conn->cached_dir_offset += bytes;
+ if (bytes == (int)conn->cached_dir->dir_z_len) {
+ /* We just wrote the last one; finish up. */
+ if (conn->zlib_state) {
+ write_to_buf_zlib(conn->outbuf, conn->zlib_state, "", 0, 1);
+ tor_zlib_free(conn->zlib_state);
+ conn->zlib_state = NULL;
+ }
+ cached_dir_decref(conn->cached_dir);
+ conn->cached_dir = NULL;
+ }
+ return 0;
+}
+
+/** Called whenever we have flushed some directory data in state
+ * SERVER_WRITING. */
+int
+connection_dirserv_flushed_some(connection_t *conn)
+{
+ tor_assert(conn->type == CONN_TYPE_DIR);
+ tor_assert(conn->state == DIR_CONN_STATE_SERVER_WRITING);
+
+ if (! (conn->fingerprint_stack || conn->cached_dir)
+ || buf_datalen(conn->outbuf) > DIRSERV_BUFFER_MIN)
+ return 0;
+
+ if (!strcmpstart(conn->requested_resource, "/tor/server/")) {
+ return connection_dirserv_add_servers_to_outbuf(conn);
+ } else if (conn->cached_dir) {
+ return connection_dirserv_add_dir_bytes_to_outbuf(conn);
+ } else {
+ return 0;
+ }
+}
+
/** Release all storage used by the directory server. */
void
dirserv_free_all(void)
@@ -1685,10 +1867,10 @@ 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(cached_directory);
clear_cached_dir(&cached_runningrouters);
if (cached_v2_networkstatus) {
digestmap_free(cached_v2_networkstatus, free_cached_dir);