diff options
Diffstat (limited to 'src/or')
-rw-r--r-- | src/or/connection.c | 40 | ||||
-rw-r--r-- | src/or/directory.c | 70 | ||||
-rw-r--r-- | src/or/dirserv.c | 232 | ||||
-rw-r--r-- | src/or/or.h | 14 |
4 files changed, 277 insertions, 79 deletions
diff --git a/src/or/connection.c b/src/or/connection.c index 6e2ab884aa..479fc00b1c 100644 --- a/src/or/connection.c +++ b/src/or/connection.c @@ -20,6 +20,7 @@ static int connection_init_accepted_conn(connection_t *conn); static int connection_handle_listener_read(connection_t *conn, int new_type); static int connection_receiver_bucket_should_increase(connection_t *conn); static int connection_finished_flushing(connection_t *conn); +static int connection_flushed_some(connection_t *conn); static int connection_finished_connecting(connection_t *conn); static int connection_reached_eof(connection_t *conn); static int connection_read_to_buf(connection_t *conn, int *max_to_read); @@ -241,6 +242,14 @@ _connection_free(connection_t *conn) log_warn(LD_BUG, "called on OR conn with non-zeroed identity_digest"); connection_or_remove_from_identity_map(conn); } + if (conn->zlib_state) + tor_zlib_free(conn->zlib_state); + if (conn->fingerprint_stack) { + SMARTLIST_FOREACH(conn->fingerprint_stack, char *, cp, tor_free(cp)); + smartlist_free(conn->fingerprint_stack); + } + if (conn->cached_dir) + cached_dir_decref(conn->cached_dir); memset(conn, 0xAA, sizeof(connection_t)); /* poison memory */ tor_free(conn); @@ -1491,9 +1500,13 @@ connection_handle_write(connection_t *conn) } } - if (result > 0 && !is_local_IP(conn->addr)) { /* remember it */ - rep_hist_note_bytes_written(result, now); - global_write_bucket -= result; + if (result > 0) { + if (!is_local_IP(conn->addr)) { /* remember it */ + rep_hist_note_bytes_written(result, time(NULL)); + global_write_bucket -= result; + } + if (connection_flushed_some(conn) < 0) + connection_mark_for_close(conn); } if (!connection_wants_to_flush(conn)) { /* it's done flushing */ @@ -1531,9 +1544,13 @@ _connection_controller_force_write(connection_t *conn) return; } - if (result > 0 && !is_local_IP(conn->addr)) { /* remember it */ - rep_hist_note_bytes_written(result, time(NULL)); - global_write_bucket -= result; + if (result > 0) { + if (!is_local_IP(conn->addr)) { /* remember it */ + rep_hist_note_bytes_written(result, time(NULL)); + global_write_bucket -= result; + } + if (connection_flushed_some(conn) < 0) + connection_mark_for_close(conn); } if (!connection_wants_to_flush(conn)) { /* it's done flushing */ @@ -1913,6 +1930,17 @@ connection_process_inbuf(connection_t *conn, int package_partial) } } +/** Called whenever we've written data on a connection. */ +static int +connection_flushed_some(connection_t *conn) +{ + if (conn->type == CONN_TYPE_DIR && + conn->state == DIR_CONN_STATE_SERVER_WRITING) + return connection_dirserv_flushed_some(conn); + else + return 0; +} + /** We just finished flushing bytes from conn-\>outbuf, and there * are no more bytes remaining. * diff --git a/src/or/directory.c b/src/or/directory.c index 6575426d21..9e0adabacb 100644 --- a/src/or/directory.c +++ b/src/or/directory.c @@ -1374,9 +1374,9 @@ directory_handle_command_get(connection_t *conn, char *headers, if (!strcmp(url,"/tor/") || !strcmp(url,"/tor/dir.z")) { /* dir fetch */ int deflated = !strcmp(url,"/tor/dir.z"); - dlen = dirserv_get_directory(&cp, deflated); + cached_dir_t *d = dirserv_get_directory(); - if (dlen == 0) { + if (!d) { log_notice(LD_DIRSERV,"Client asked for the mirrored directory, but we " "don't have a good one yet. Sending 503 Dir not available."); write_http_status_line(conn, 503, "Directory unavailable"); @@ -1386,6 +1386,7 @@ directory_handle_command_get(connection_t *conn, char *headers, tor_free(url); return 0; } + dlen = deflated ? d->dir_z_len : d->dir_len; if (global_write_bucket_empty()) { log_info(LD_DIRSERV, @@ -1410,7 +1411,14 @@ directory_handle_command_get(connection_t *conn, char *headers, deflated?"application/octet-stream":"text/plain", deflated?"deflate":"identity"); connection_write_to_buf(tmp, strlen(tmp), conn); - connection_write_to_buf(cp, dlen, conn); + conn->cached_dir = d; + conn->cached_dir_offset = 0; + if (deflated) + conn->zlib_state = tor_zlib_new(0, ZLIB_METHOD); + ++d->refcnt; + + /* Prime the connection with some data. */ + connection_dirserv_flushed_some(conn); return 0; } @@ -1495,11 +1503,11 @@ directory_handle_command_get(connection_t *conn, char *headers, int deflated = !strcmp(url+url_len-2, ".z"); int res; const char *msg; - smartlist_t *descs = smartlist_create(); const char *request_type = NULL; if (deflated) url[url_len-2] = '\0'; - res = dirserv_get_routerdescs(descs, url, &msg); + conn->fingerprint_stack = smartlist_create(); + res = dirserv_get_routerdescs(conn->fingerprint_stack, url, &msg); if (!strcmpstart(url, "/tor/server/fp/")) request_type = deflated?"/tor/server/fp.z":"/tor/server/fp"; @@ -1516,58 +1524,27 @@ directory_handle_command_get(connection_t *conn, char *headers, if (res < 0) write_http_status_line(conn, 404, msg); else { - size_t len = 0; format_rfc1123_time(date, time(NULL)); - SMARTLIST_FOREACH(descs, signed_descriptor_t *, ri, - len += ri->signed_descriptor_len); if (deflated) { - size_t compressed_len; - char *compressed; - char *inp = tor_malloc(len+smartlist_len(descs)+1); - char *cp = inp; - SMARTLIST_FOREACH(descs, signed_descriptor_t *, ri, - { - const char *body = signed_descriptor_get_body(ri); - memcpy(cp, body, ri->signed_descriptor_len); - cp += ri->signed_descriptor_len; - *cp++ = '\n'; - }); - *cp = '\0'; - /* XXXX This could be way more efficiently handled; let's see if it - * shows up under oprofile. */ - if (tor_gzip_compress(&compressed, &compressed_len, - inp, cp-inp, ZLIB_METHOD)<0) { - tor_free(inp); - smartlist_free(descs); - return -1; - } - tor_free(inp); + conn->zlib_state = tor_zlib_new(1, ZLIB_METHOD); + /* // note_request(request_type, compressed_len); XXXX */ tor_snprintf(tmp, sizeof(tmp), - "HTTP/1.0 200 OK\r\nDate: %s\r\nContent-Length: %d\r\n" + "HTTP/1.0 200 OK\r\nDate: %s\r\n" "Content-Type: application/octet-stream\r\n" "Content-Encoding: deflate\r\n\r\n", - date, - (int)compressed_len); - note_request(request_type, compressed_len); + date); connection_write_to_buf(tmp, strlen(tmp), conn); - connection_write_to_buf(compressed, compressed_len, conn); - tor_free(compressed); } else { tor_snprintf(tmp, sizeof(tmp), - "HTTP/1.0 200 OK\r\nDate: %s\r\nContent-Length: %d\r\n" + "HTTP/1.0 200 OK\r\nDate: %s\r\n" "Content-Type: text/plain\r\n\r\n", - date, - (int)len); - note_request(request_type, len); + date); + /* note_request(request_type, len); XXXX */ connection_write_to_buf(tmp, strlen(tmp), conn); - SMARTLIST_FOREACH(descs, signed_descriptor_t *, ri, - { - const char *body = signed_descriptor_get_body(ri); - connection_write_to_buf(body, ri->signed_descriptor_len, conn); - }); } + /* Prime the connection with some data. */ + connection_dirserv_flushed_some(conn); } - smartlist_free(descs); return 0; } @@ -1690,7 +1667,6 @@ static int directory_handle_command_post(connection_t *conn, char *headers, char *body, size_t body_len) { - const char *cp; char *origin = NULL; char *url = NULL; @@ -1718,7 +1694,7 @@ directory_handle_command_post(connection_t *conn, char *headers, int r = dirserv_add_descriptor(body, &msg); tor_assert(msg); if (r > 0) - dirserv_get_directory(&cp, 0); /* rebuild and write to disk */ + dirserv_get_directory(); /* rebuild and write to disk */ switch (r) { case -2: case -1: 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); diff --git a/src/or/or.h b/src/or/or.h index 7100290f70..128e4172a2 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -697,6 +697,11 @@ struct connection_t { /* Used only by Dir connections */ char *requested_resource; /**< Which 'resource' did we ask the directory * for?*/ +/* Used only for server sides of some dir connections. */ + smartlist_t *fingerprint_stack; + struct cached_dir_t *cached_dir; + off_t cached_dir_offset; + tor_zlib_state_t *zlib_state; /* Used only by AP connections */ socks_request_t *socks_request; /**< SOCKS structure describing request (AP @@ -750,6 +755,7 @@ typedef struct cached_dir_t { size_t dir_len; /**< Length of <b>dir</b> */ size_t dir_z_len; /**< Length of <b>dir_z</b> */ time_t published; /**< When was this object published */ + int refcnt; /**< Reference count for this cached_dir_t. */ } cached_dir_t; /** Information need to cache an onion router's descriptor. */ @@ -1468,6 +1474,8 @@ int flush_buf(int s, buf_t *buf, size_t sz, size_t *buf_flushlen); int flush_buf_tls(tor_tls_t *tls, buf_t *buf, size_t sz, size_t *buf_flushlen); int write_to_buf(const char *string, size_t string_len, buf_t *buf); +int write_to_buf_zlib(buf_t *buf, tor_zlib_state_t *state, + const char *data, size_t data_len, int done); int fetch_from_buf(char *string, size_t string_len, buf_t *buf); int fetch_from_buf_http(buf_t *buf, char **headers_out, size_t max_headerlen, @@ -1899,6 +1907,7 @@ char *directory_dump_request_log(void); /********************************* dirserv.c ***************************/ +int connection_dirserv_flushed_some(connection_t *conn); int dirserv_add_own_fingerprint(const char *nickname, crypto_pk_env_t *pk); int dirserv_parse_fingerprint_file(const char *fname); void dirserv_free_fingerprint_list(void); @@ -1913,7 +1922,7 @@ int dirserv_dump_directory_to_string(char **dir_out, crypto_pk_env_t *private_key, int complete); void directory_set_dirty(void); -size_t dirserv_get_directory(const char **cp, int compress); +cached_dir_t *dirserv_get_directory(void); size_t dirserv_get_runningrouters(const char **rr, int compress); void dirserv_set_cached_directory(const char *directory, time_t when, int is_running_routers); @@ -1921,6 +1930,8 @@ void dirserv_set_cached_networkstatus_v2(const char *directory, const char *identity, time_t published); void dirserv_get_networkstatus_v2(smartlist_t *result, const char *key); +int dirserv_get_routerdesc_fingerprints(smartlist_t *fps_out, const char *key, + const char **msg); int dirserv_get_routerdescs(smartlist_t *descs_out, const char *key, const char **msg); void dirserv_orconn_tls_done(const char *address, @@ -1932,6 +1943,7 @@ int authdir_wants_to_reject_router(routerinfo_t *ri, const char **msg, int complain); int dirserv_would_reject_router(routerstatus_t *rs); void dirserv_free_all(void); +void cached_dir_decref(cached_dir_t *d); /********************************* dns.c ***************************/ |