mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-12-06 19:41:15 +01:00
Do not serve a consensus if it is too old
Closes ticket 20511.
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
o Minor feature:
|
||||
- Relays and bridges will now refuse to serve the consensus they have if
|
||||
they know it is too old for a client to use. Closes ticket 20511.
|
||||
@@ -2939,6 +2939,28 @@ handle_get_frontpage(dir_connection_t *conn, const get_handler_args_t *args)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Warn that the consensus <b>v</b> of type <b>flavor</b> is too old and will
|
||||
* not be served to clients. Rate-limit the warning to avoid logging an entry
|
||||
* on every request.
|
||||
*/
|
||||
static void
|
||||
warn_consensus_is_too_old(networkstatus_t *v, const char *flavor, time_t now)
|
||||
{
|
||||
#define TOO_OLD_WARNING_INTERVAL (60*60)
|
||||
static ratelim_t warned = RATELIM_INIT(TOO_OLD_WARNING_INTERVAL);
|
||||
char timestamp[ISO_TIME_LEN+1];
|
||||
char *dupes;
|
||||
|
||||
if ((dupes = rate_limit_log(&warned, now))) {
|
||||
format_local_iso_time(timestamp, v->valid_until);
|
||||
log_warn(LD_DIRSERV, "Our %s%sconsensus is too old, so we will not "
|
||||
"serve it to clients. It was valid until %s local time and we "
|
||||
"continued to serve it for up to 24 hours after it expired.%s",
|
||||
flavor ? flavor : "", flavor ? " " : "", timestamp, dupes);
|
||||
tor_free(dupes);
|
||||
}
|
||||
}
|
||||
|
||||
/** Helper function for GET /tor/status-vote/current/consensus
|
||||
*/
|
||||
static int
|
||||
@@ -2983,6 +3005,15 @@ handle_get_current_consensus(dir_connection_t *conn,
|
||||
|
||||
v = networkstatus_get_latest_consensus_by_flavor(flav);
|
||||
|
||||
if (v && !networkstatus_consensus_reasonably_live(v, now)) {
|
||||
write_http_status_line(conn, 404, "Consensus is too old");
|
||||
warn_consensus_is_too_old(v, flavor, now);
|
||||
smartlist_free(dir_fps);
|
||||
geoip_note_ns_response(GEOIP_REJECT_NOT_FOUND);
|
||||
tor_free(flavor);
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (v && want_fps &&
|
||||
!client_likes_consensus(v, want_fps)) {
|
||||
write_http_status_line(conn, 404, "Consensus not signed by sufficient "
|
||||
|
||||
+19
-2
@@ -1342,6 +1342,24 @@ networkstatus_get_live_consensus,(time_t now))
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/** Determine if <b>consensus</b> is valid or expired recently enough that
|
||||
* we can still use it.
|
||||
*
|
||||
* Return 1 if the consensus is reasonably live, or 0 if it is too old.
|
||||
*/
|
||||
int
|
||||
networkstatus_consensus_reasonably_live(networkstatus_t *consensus, time_t now)
|
||||
{
|
||||
#define REASONABLY_LIVE_TIME (24*60*60)
|
||||
if (BUG(!consensus))
|
||||
return 0;
|
||||
|
||||
if (now <= consensus->valid_until + REASONABLY_LIVE_TIME)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* XXXX remove this in favor of get_live_consensus. But actually,
|
||||
* leave something like it for bridge users, who need to not totally
|
||||
* lose if they spend a while fetching a new consensus. */
|
||||
@@ -1350,12 +1368,11 @@ networkstatus_get_live_consensus,(time_t now))
|
||||
networkstatus_t *
|
||||
networkstatus_get_reasonably_live_consensus(time_t now, int flavor)
|
||||
{
|
||||
#define REASONABLY_LIVE_TIME (24*60*60)
|
||||
networkstatus_t *consensus =
|
||||
networkstatus_get_latest_consensus_by_flavor(flavor);
|
||||
if (consensus &&
|
||||
consensus->valid_after <= now &&
|
||||
now <= consensus->valid_until+REASONABLY_LIVE_TIME)
|
||||
networkstatus_consensus_reasonably_live(consensus, now))
|
||||
return consensus;
|
||||
else
|
||||
return NULL;
|
||||
|
||||
@@ -79,6 +79,8 @@ MOCK_DECL(networkstatus_t *,networkstatus_get_latest_consensus,(void));
|
||||
MOCK_DECL(networkstatus_t *,networkstatus_get_latest_consensus_by_flavor,
|
||||
(consensus_flavor_t f));
|
||||
MOCK_DECL(networkstatus_t *, networkstatus_get_live_consensus,(time_t now));
|
||||
int networkstatus_consensus_reasonably_live(networkstatus_t *consensus,
|
||||
time_t now);
|
||||
networkstatus_t *networkstatus_get_reasonably_live_consensus(time_t now,
|
||||
int flavor);
|
||||
MOCK_DECL(int, networkstatus_consensus_is_bootstrapping,(time_t now));
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "dirserv.h"
|
||||
#include "torgzip.h"
|
||||
#include "dirvote.h"
|
||||
#include "log_test_helpers.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
/* For mkdir() */
|
||||
@@ -53,6 +54,7 @@ ENABLE_GCC_WARNING(overlength-strings)
|
||||
#define NOT_FOUND "HTTP/1.0 404 Not found\r\n\r\n"
|
||||
#define BAD_REQUEST "HTTP/1.0 400 Bad request\r\n\r\n"
|
||||
#define SERVER_BUSY "HTTP/1.0 503 Directory busy, try again later\r\n\r\n"
|
||||
#define TOO_OLD "HTTP/1.0 404 Consensus is too old\r\n\r\n"
|
||||
#define NOT_ENOUGH_CONSENSUS_SIGNATURES "HTTP/1.0 404 " \
|
||||
"Consensus not signed by sufficient number of requested authorities\r\n\r\n"
|
||||
|
||||
@@ -1617,6 +1619,7 @@ test_dir_handle_get_status_vote_current_consensus_ns_not_enough_sigs(void* d)
|
||||
mock_ns_val = tor_malloc_zero(sizeof(networkstatus_t));
|
||||
mock_ns_val->flavor = FLAV_NS;
|
||||
mock_ns_val->voters = smartlist_new();
|
||||
mock_ns_val->valid_until = time(NULL);
|
||||
|
||||
/* init mock */
|
||||
init_mock_options();
|
||||
@@ -1696,6 +1699,62 @@ test_dir_handle_get_status_vote_current_consensus_ns_not_found(void* data)
|
||||
or_options_free(mock_options); mock_options = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
test_dir_handle_get_status_vote_current_consensus_too_old(void *data)
|
||||
{
|
||||
dir_connection_t *conn = NULL;
|
||||
char *header = NULL;
|
||||
(void)data;
|
||||
|
||||
mock_ns_val = tor_malloc_zero(sizeof(networkstatus_t));
|
||||
mock_ns_val->flavor = FLAV_MICRODESC;
|
||||
mock_ns_val->valid_until = time(NULL) - (60 * 60 * 24) - 1;
|
||||
|
||||
init_mock_options();
|
||||
MOCK(get_options, mock_get_options);
|
||||
MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock);
|
||||
MOCK(networkstatus_get_latest_consensus_by_flavor, mock_ns_get_by_flavor);
|
||||
|
||||
conn = new_dir_conn();
|
||||
|
||||
setup_capture_of_logs(LOG_WARN);
|
||||
|
||||
tt_int_op(0, OP_EQ, directory_handle_command_get(conn,
|
||||
GET("/tor/status-vote/current/consensus-microdesc"), NULL, 0));
|
||||
|
||||
fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
|
||||
NULL, NULL, 1, 0);
|
||||
tt_assert(header);
|
||||
tt_str_op(TOO_OLD, OP_EQ, header);
|
||||
|
||||
expect_log_msg_containing("too old");
|
||||
|
||||
tor_free(header);
|
||||
teardown_capture_of_logs();
|
||||
|
||||
setup_capture_of_logs(LOG_WARN);
|
||||
|
||||
tt_int_op(0, OP_EQ, directory_handle_command_get(conn,
|
||||
GET("/tor/status-vote/current/consensus"), NULL, 0));
|
||||
|
||||
fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE,
|
||||
NULL, NULL, 1, 0);
|
||||
tt_assert(header);
|
||||
tt_str_op(TOO_OLD, OP_EQ, header);
|
||||
|
||||
expect_no_log_entry();
|
||||
|
||||
done:
|
||||
teardown_capture_of_logs();
|
||||
UNMOCK(networkstatus_get_latest_consensus_by_flavor);
|
||||
UNMOCK(connection_write_to_buf_impl_);
|
||||
UNMOCK(get_options);
|
||||
connection_free_(TO_CONN(conn));
|
||||
tor_free(header);
|
||||
tor_free(mock_ns_val);
|
||||
or_options_free(mock_options); mock_options = NULL;
|
||||
}
|
||||
|
||||
NS_DECL(int, geoip_get_country_by_addr, (const tor_addr_t *addr));
|
||||
|
||||
int
|
||||
@@ -2481,6 +2540,7 @@ struct testcase_t dir_handle_get_tests[] = {
|
||||
DIR_HANDLE_CMD(status_vote_next_authority, 0),
|
||||
DIR_HANDLE_CMD(status_vote_current_consensus_ns_not_enough_sigs, 0),
|
||||
DIR_HANDLE_CMD(status_vote_current_consensus_ns_not_found, 0),
|
||||
DIR_HANDLE_CMD(status_vote_current_consensus_too_old, 0),
|
||||
DIR_HANDLE_CMD(status_vote_current_consensus_ns_busy, 0),
|
||||
DIR_HANDLE_CMD(status_vote_current_consensus_ns, 0),
|
||||
DIR_HANDLE_CMD(status_vote_current_d_not_found, 0),
|
||||
|
||||
Reference in New Issue
Block a user