From 3eacae42b2ade91bc5d0e3761dce1e785aad7621 Mon Sep 17 00:00:00 2001 From: juga0 Date: Fri, 2 Nov 2018 18:38:46 +0000 Subject: [PATCH 01/10] Serve bandwidth file used in the next vote When a directory authority is using a bandwidth file to obtain the bandwidth values that will be included in the next vote, serve this bandwidth file at /tor/status-vote/next/bandwidth.z. --- src/feature/dircache/dircache.c | 24 +++++++++++ src/test/test_dir_handle_get.c | 76 +++++++++++++++++++++++++++++++++ 2 files changed, 100 insertions(+) diff --git a/src/feature/dircache/dircache.c b/src/feature/dircache/dircache.c index e8cb284165..f373b74c85 100644 --- a/src/feature/dircache/dircache.c +++ b/src/feature/dircache/dircache.c @@ -357,12 +357,15 @@ static int handle_get_robots(dir_connection_t *conn, const get_handler_args_t *args); static int handle_get_networkstatus_bridges(dir_connection_t *conn, const get_handler_args_t *args); +static int handle_get_next_bandwidth(dir_connection_t *conn, + const get_handler_args_t *args); /** Table for handling GET requests. */ static const url_table_ent_t url_table[] = { { "/tor/", 0, handle_get_frontpage }, { "/tor/status-vote/current/consensus", 1, handle_get_current_consensus }, { "/tor/status-vote/current/", 1, handle_get_status_vote }, + { "/tor/status-vote/next/bandwidth", 0, handle_get_next_bandwidth }, { "/tor/status-vote/next/", 1, handle_get_status_vote }, { "/tor/micro/d/", 1, handle_get_microdesc }, { "/tor/server/", 1, handle_get_descriptor }, @@ -1438,6 +1441,27 @@ handle_get_networkstatus_bridges(dir_connection_t *conn, return 0; } +/** Helper function for GET the bandwidth file used for the next vote */ +static int +handle_get_next_bandwidth(dir_connection_t *conn, + const get_handler_args_t *args) +{ + (void)args; + log_debug(LD_DIR, "Getting next bandwidth."); + const or_options_t *options = get_options(); + if (options->V3BandwidthsFile) { + int lifetime = 60; + char *bandwidth = read_file_to_str(options->V3BandwidthsFile, 0, NULL); + size_t len = strlen(bandwidth); + write_http_response_header(conn, len, NO_METHOD, lifetime); + connection_buf_add(bandwidth, len, TO_CONN(conn)); + tor_free(bandwidth); + } else { + write_short_http_response(conn, 404, "Not found"); + } + return 0; +} + /** Helper function for GET robots.txt or /tor/robots.txt */ static int handle_get_robots(dir_connection_t *conn, const get_handler_args_t *args) diff --git a/src/test/test_dir_handle_get.c b/src/test/test_dir_handle_get.c index 90691fff94..8b2ee3ecac 100644 --- a/src/test/test_dir_handle_get.c +++ b/src/test/test_dir_handle_get.c @@ -2217,6 +2217,31 @@ test_dir_handle_get_status_vote_next_authority_not_found(void* data) tor_free(header); } +static void +test_dir_handle_get_status_vote_next_bandwidth_not_found(void* data) +{ + dir_connection_t *conn = NULL; + char *header = NULL; + (void) data; + + MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock); + + conn = new_dir_conn(); + + tt_int_op(0, OP_EQ, directory_handle_command_get(conn, + GET("/tor/status-vote/next/bandwdith"), NULL, 0)); + + fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE, + NULL, NULL, 1, 0); + tt_assert(header); + tt_str_op(NOT_FOUND, OP_EQ, header); + + done: + UNMOCK(connection_write_to_buf_impl_); + connection_free_minimal(TO_CONN(conn)); + tor_free(header); +} + NS_DECL(const char*, dirvote_get_pending_consensus, (consensus_flavor_t flav)); @@ -2454,6 +2479,55 @@ test_dir_handle_get_status_vote_next_authority(void* data) dirvote_free_all(); } +static void +test_dir_handle_get_status_vote_next_bandwidth(void* data) +{ + dir_connection_t *conn = NULL; + char *header = NULL, *body = NULL; + size_t body_used = 0; + (void) data; + + const char *content = + "1541171221\n" + "node_id=$68A483E05A2ABDCA6DA5A3EF8DB5177638A27F80 " + "master_key_ed25519=YaqV4vbvPYKucElk297eVdNArDz9HtIwUoIeo0+cVIpQ " + "bw=760 nick=Test time=2018-05-08T16:13:26\n"; + + init_mock_options(); + MOCK(get_options, mock_get_options); + mock_options->V3BandwidthsFile = tor_strdup( + get_fname_rnd("V3BandwidthsFile") + ); + + write_str_to_file(mock_options->V3BandwidthsFile, content, 0); + + MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock); + + conn = new_dir_conn(); + tt_int_op(0, OP_EQ, directory_handle_command_get(conn, + GET("/tor/status-vote/next/bandwidth"), NULL, 0)); + + fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE, + &body, &body_used, strlen(content)+1, 0); + + tt_assert(header); + tt_ptr_op(strstr(header, "HTTP/1.0 200 OK\r\n"), OP_EQ, header); + tt_assert(strstr(header, "Content-Type: text/plain\r\n")); + tt_assert(strstr(header, "Content-Encoding: identity\r\n")); + tt_assert(strstr(header, "Content-Length: 167\r\n")); + + tt_int_op(body_used, OP_EQ, strlen(body)); + tt_str_op(content, OP_EQ, body); + + done: + UNMOCK(get_options); + UNMOCK(connection_write_to_buf_impl_); + connection_free_minimal(TO_CONN(conn)); + tor_free(header); + tor_free(body); + or_options_free(mock_options); mock_options = NULL; +} + static void test_dir_handle_get_status_vote_current_authority(void* data) { @@ -2627,6 +2701,8 @@ struct testcase_t dir_handle_get_tests[] = { DIR_HANDLE_CMD(status_vote_current_authority, 0), DIR_HANDLE_CMD(status_vote_next_authority_not_found, 0), DIR_HANDLE_CMD(status_vote_next_authority, 0), + DIR_HANDLE_CMD(status_vote_next_bandwidth_not_found, 0), + DIR_HANDLE_CMD(status_vote_next_bandwidth, 0), DIR_HANDLE_CMD(status_vote_current_consensus_ns_not_enough_sigs, TT_FORK), DIR_HANDLE_CMD(status_vote_current_consensus_ns_not_found, TT_FORK), DIR_HANDLE_CMD(status_vote_current_consensus_too_old, TT_FORK), From ee09e5d7eacdfde0cd5e1bc4a18c72fd7e47c1a7 Mon Sep 17 00:00:00 2001 From: juga0 Date: Wed, 7 Nov 2018 08:49:51 +0000 Subject: [PATCH 02/10] bwauth: use flag to do not warn when file is missing Use flag to do not warn when the bandwidth file is missing trying to serve it by http. Also remove double space in the assignement. --- src/feature/dircache/dircache.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/feature/dircache/dircache.c b/src/feature/dircache/dircache.c index f373b74c85..2aaaf7be11 100644 --- a/src/feature/dircache/dircache.c +++ b/src/feature/dircache/dircache.c @@ -1451,7 +1451,8 @@ handle_get_next_bandwidth(dir_connection_t *conn, const or_options_t *options = get_options(); if (options->V3BandwidthsFile) { int lifetime = 60; - char *bandwidth = read_file_to_str(options->V3BandwidthsFile, 0, NULL); + char *bandwidth = read_file_to_str(options->V3BandwidthsFile, + RFTS_IGNORE_MISSING, NULL); size_t len = strlen(bandwidth); write_http_response_header(conn, len, NO_METHOD, lifetime); connection_buf_add(bandwidth, len, TO_CONN(conn)); From b75e2539f9994dc04ee7662445878e8486eb77b0 Mon Sep 17 00:00:00 2001 From: juga0 Date: Wed, 7 Nov 2018 12:04:09 +0000 Subject: [PATCH 03/10] bwauth: check if a bw file could be read Before serving it by HTTP. --- src/feature/dircache/dircache.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/feature/dircache/dircache.c b/src/feature/dircache/dircache.c index 2aaaf7be11..d9625ee698 100644 --- a/src/feature/dircache/dircache.c +++ b/src/feature/dircache/dircache.c @@ -1453,13 +1453,15 @@ handle_get_next_bandwidth(dir_connection_t *conn, int lifetime = 60; char *bandwidth = read_file_to_str(options->V3BandwidthsFile, RFTS_IGNORE_MISSING, NULL); - size_t len = strlen(bandwidth); - write_http_response_header(conn, len, NO_METHOD, lifetime); - connection_buf_add(bandwidth, len, TO_CONN(conn)); - tor_free(bandwidth); - } else { - write_short_http_response(conn, 404, "Not found"); + if (bandwidth != NULL) { + site_t len = strlen(bandwidth); + write_http_response_header(conn, len, NO_METHOD, lifetime); + connection_buf_add(bandwidth, len, TO_CONN(conn)); + tor_free(bandwidth); + return 0; + } } + write_short_http_response(conn, 404, "Not found"); return 0; } From 4d3502e45bc9b0da91b32c362a7995f33cee0539 Mon Sep 17 00:00:00 2001 From: juga0 Date: Wed, 7 Nov 2018 12:15:31 +0000 Subject: [PATCH 04/10] bwauth: check and use compression serving bw file --- src/feature/dircache/dircache.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/feature/dircache/dircache.c b/src/feature/dircache/dircache.c index d9625ee698..e5ea9f73b3 100644 --- a/src/feature/dircache/dircache.c +++ b/src/feature/dircache/dircache.c @@ -1449,14 +1449,28 @@ handle_get_next_bandwidth(dir_connection_t *conn, (void)args; log_debug(LD_DIR, "Getting next bandwidth."); const or_options_t *options = get_options(); + const compress_method_t compress_method = + find_best_compression_method(args->compression_supported, 1); + if (options->V3BandwidthsFile) { int lifetime = 60; char *bandwidth = read_file_to_str(options->V3BandwidthsFile, RFTS_IGNORE_MISSING, NULL); if (bandwidth != NULL) { - site_t len = strlen(bandwidth); - write_http_response_header(conn, len, NO_METHOD, lifetime); - connection_buf_add(bandwidth, len, TO_CONN(conn)); + ssize_t len = strlen(bandwidth); + write_http_response_header(conn, compress_method != NO_METHOD ? -1 : len, + compress_method, BANDWIDTH_CACHE_LIFETIME); + if (compress_method != NO_METHOD) { + conn->compress_state = tor_compress_new(1, compress_method, + choose_compression_level(len/2)); + log_debug(LD_DIR, "Compressing bandwidth file."); + connection_buf_add_compress(bandwidth, len, conn, 0); + /* Flush the compression state. */ + connection_buf_add_compress("", 0, conn, 1); + } else { + log_debug(LD_DIR, "Not compressing bandwidth file."); + connection_buf_add(bandwidth, len, TO_CONN(conn)); + } tor_free(bandwidth); return 0; } From 76271347438ff03796a9424019860332f50c5134 Mon Sep 17 00:00:00 2001 From: juga0 Date: Wed, 7 Nov 2018 12:19:20 +0000 Subject: [PATCH 05/10] bwauth: increment bw file cache lifetime Increment bw file cache lifetime when serving it by HTTP. And add a constant to define that lifetime. --- src/feature/dircache/dircache.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/feature/dircache/dircache.c b/src/feature/dircache/dircache.c index e5ea9f73b3..0f92dfaea1 100644 --- a/src/feature/dircache/dircache.c +++ b/src/feature/dircache/dircache.c @@ -49,7 +49,8 @@ #define ROUTERDESC_BY_DIGEST_CACHE_LIFETIME (48*60*60) #define ROBOTS_CACHE_LIFETIME (24*60*60) #define MICRODESC_CACHE_LIFETIME (48*60*60) - +/* Bandwidth files change every hour. */ +#define BANDWIDTH_CACHE_LIFETIME (30*60) /** Parse an HTTP request string headers of the form * \verbatim * "\%s [http[s]://]\%s HTTP/1..." @@ -1453,7 +1454,6 @@ handle_get_next_bandwidth(dir_connection_t *conn, find_best_compression_method(args->compression_supported, 1); if (options->V3BandwidthsFile) { - int lifetime = 60; char *bandwidth = read_file_to_str(options->V3BandwidthsFile, RFTS_IGNORE_MISSING, NULL); if (bandwidth != NULL) { From fb4a40c32c4a7e5b7175ea2c635687b6b24a29d0 Mon Sep 17 00:00:00 2001 From: juga0 Date: Wed, 7 Nov 2018 12:38:45 +0000 Subject: [PATCH 06/10] test: Check bw file cache lifetime --- src/test/test_dir_handle_get.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/test/test_dir_handle_get.c b/src/test/test_dir_handle_get.c index 8b2ee3ecac..6d478f6b49 100644 --- a/src/test/test_dir_handle_get.c +++ b/src/test/test_dir_handle_get.c @@ -2516,6 +2516,16 @@ test_dir_handle_get_status_vote_next_bandwidth(void* data) tt_assert(strstr(header, "Content-Encoding: identity\r\n")); tt_assert(strstr(header, "Content-Length: 167\r\n")); + /* Check cache lifetime */ + char expbuf[RFC1123_TIME_LEN+1]; + time_t now = time(NULL); + /* BANDWIDTH_CACHE_LIFETIME is defined in dircache.c. */ + format_rfc1123_time(expbuf, (time_t)(now + 30*60)); + char *expires = NULL; + /* Change to 'Cache-control: max-age=%d' if using http/1.1. */ + tor_asprintf(&expires, "Expires: %s\r\n", expbuf); + tt_assert(strstr(header, expires)); + tt_int_op(body_used, OP_EQ, strlen(body)); tt_str_op(content, OP_EQ, body); @@ -2525,6 +2535,7 @@ test_dir_handle_get_status_vote_next_bandwidth(void* data) connection_free_minimal(TO_CONN(conn)); tor_free(header); tor_free(body); + tor_free(expires); or_options_free(mock_options); mock_options = NULL; } From a4bf3be8bce662c77422fb88072e4685083e90b2 Mon Sep 17 00:00:00 2001 From: juga0 Date: Wed, 7 Nov 2018 12:40:51 +0000 Subject: [PATCH 07/10] test: check that .../bandwidth.z is compressed --- src/test/test_dir_handle_get.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/test/test_dir_handle_get.c b/src/test/test_dir_handle_get.c index 6d478f6b49..0cd7e1050a 100644 --- a/src/test/test_dir_handle_get.c +++ b/src/test/test_dir_handle_get.c @@ -2529,6 +2529,25 @@ test_dir_handle_get_status_vote_next_bandwidth(void* data) tt_int_op(body_used, OP_EQ, strlen(body)); tt_str_op(content, OP_EQ, body); + tor_free(header); + tor_free(body); + + /* Request the file using compression, the result should be the same. */ + tt_int_op(0, OP_EQ, directory_handle_command_get(conn, + GET("/tor/status-vote/next/bandwidth.z"), NULL, 0)); + + fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE, + &body, &body_used, strlen(content)+1, 0); + + tt_assert(header); + tt_ptr_op(strstr(header, "HTTP/1.0 200 OK\r\n"), OP_EQ, header); + tt_assert(strstr(header, "Content-Encoding: deflate\r\n")); + + /* Since using connection_write_to_buf_mock instead of mocking + * connection_buf_add_compress, the content is not actually compressed. + * If it would, the size and content would be different than the original. + */ + done: UNMOCK(get_options); UNMOCK(connection_write_to_buf_impl_); From 892b918b6610d662a9536d29c4edf9f4eac5da88 Mon Sep 17 00:00:00 2001 From: juga0 Date: Thu, 8 Nov 2018 09:13:54 +0000 Subject: [PATCH 08/10] bwauth: remove declaring args, they are now in use --- src/feature/dircache/dircache.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/feature/dircache/dircache.c b/src/feature/dircache/dircache.c index 0f92dfaea1..14c6302c25 100644 --- a/src/feature/dircache/dircache.c +++ b/src/feature/dircache/dircache.c @@ -1447,7 +1447,6 @@ static int handle_get_next_bandwidth(dir_connection_t *conn, const get_handler_args_t *args) { - (void)args; log_debug(LD_DIR, "Getting next bandwidth."); const or_options_t *options = get_options(); const compress_method_t compress_method = From da7a8d7624b76053210bad6f830df7842a4b30f1 Mon Sep 17 00:00:00 2001 From: juga0 Date: Mon, 19 Nov 2018 17:27:41 +0000 Subject: [PATCH 09/10] dircache: fix identation and remove unneded goto --- src/test/test_dir_handle_get.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/test/test_dir_handle_get.c b/src/test/test_dir_handle_get.c index 0cd7e1050a..22f5a9b3da 100644 --- a/src/test/test_dir_handle_get.c +++ b/src/test/test_dir_handle_get.c @@ -2548,14 +2548,14 @@ test_dir_handle_get_status_vote_next_bandwidth(void* data) * If it would, the size and content would be different than the original. */ - done: - UNMOCK(get_options); - UNMOCK(connection_write_to_buf_impl_); - connection_free_minimal(TO_CONN(conn)); - tor_free(header); - tor_free(body); - tor_free(expires); - or_options_free(mock_options); mock_options = NULL; + done: + UNMOCK(get_options); + UNMOCK(connection_write_to_buf_impl_); + connection_free_minimal(TO_CONN(conn)); + tor_free(header); + tor_free(body); + tor_free(expires); + or_options_free(mock_options); } static void From 6ecf9590eae8cac125705d2f99b284de998d5649 Mon Sep 17 00:00:00 2001 From: juga0 Date: Fri, 21 Dec 2018 17:10:19 +0000 Subject: [PATCH 10/10] changes: add file for #21377 --- changes/ticket21377 | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 changes/ticket21377 diff --git a/changes/ticket21377 b/changes/ticket21377 new file mode 100644 index 0000000000..2bf5149a0a --- /dev/null +++ b/changes/ticket21377 @@ -0,0 +1,4 @@ + o Minor features (dircache): + - When a directory authority is using a bandwidth file to obtain the + bandwidth values that will be included in the next vote, serve this + bandwidth file at /tor/status-vote/next/bandwidth. Closes ticket 21377. \ No newline at end of file