Merge branch 'maint-0.2.8' into release-0.2.8

This commit is contained in:
Nick Mathewson 2016-05-19 08:17:11 -04:00
commit 1e97b6afc6
21 changed files with 636 additions and 622 deletions

7
changes/bug17150 Normal file
View File

@ -0,0 +1,7 @@
o Minor bugfixes (directory warnings):
- When fetching extrainfo documents, compare their SHA256 digests
and Ed25519 signing key certificates
with the routerinfo that led us to fetch them, rather than
with the most recent routerinfo. Otherwise we generate many
spurious warnings about mismatches. Fixes bug 17150; bugfix
on 0.2.7.2-alpha.

14
changes/bug18809 Normal file
View File

@ -0,0 +1,14 @@
o Major bugfixes (bootstrap):
- Check if bootstrap consensus downloads are still needed
when the linked connection attaches. This prevents tor
making unnecessary begindir-style connections, which are
the only directory connections tor clients make since
#18483 was merged.
- Fix some edge cases where consensus download connections
may not have been closed, even though they were not needed.
- Make relays retry consensus downloads the correct number of
times, rather than the more aggresive client retry count.
- Stop downloading consensuses when we have a consensus,
even if we don't have all the certificates for it yet.
Closes ticket 18943, bugfix on #4483 in 0.2.8.1-alpha,
patches by arma and teor.

View File

@ -2360,6 +2360,25 @@ connection_ap_handshake_attach_circuit(entry_connection_t *conn)
/* we're a general conn */
origin_circuit_t *circ=NULL;
/* Are we linked to a dir conn that aims to fetch a consensus?
* We check here because this conn might no longer be needed. */
if (base_conn->linked_conn &&
base_conn->linked_conn->type == CONN_TYPE_DIR &&
base_conn->linked_conn->purpose == DIR_PURPOSE_FETCH_CONSENSUS) {
/* Yes we are. Is there a consensus fetch farther along than us? */
if (networkstatus_consensus_is_already_downloading(
TO_DIR_CONN(base_conn->linked_conn)->requested_resource)) {
/* We're doing the "multiple consensus fetch attempts" game from
* proposal 210, and we're late to the party. Just close this conn.
* The circuit and TLS conn that we made will time out after a while
* if nothing else wants to use them. */
log_info(LD_DIR, "Closing extra consensus fetch (to %s) since one "
"is already downloading.", base_conn->linked_conn->address);
return -1;
}
}
if (conn->chosen_exit_name) {
const node_t *node = node_get_by_nickname(conn->chosen_exit_name, 1);
int opt = conn->chosen_exit_optional;

View File

@ -4438,32 +4438,6 @@ connection_get_by_type_state_rendquery(int type, int state,
));
}
#define CONN_FIRST_AND_FREE_TEMPLATE(sl) \
STMT_BEGIN \
if (smartlist_len(sl) > 0) { \
void *first_item = smartlist_get(sl, 0); \
smartlist_free(sl); \
return first_item; \
} else { \
smartlist_free(sl); \
return NULL; \
} \
STMT_END
/** Return a directory connection (if any one exists) that is fetching
* the item described by <b>purpose</b>/<b>resource</b>, otherwise return NULL.
*/
dir_connection_t *
connection_dir_get_by_purpose_and_resource(
int purpose,
const char *resource)
{
smartlist_t *conns = connection_dir_list_by_purpose_and_resource(
purpose,
resource);
CONN_FIRST_AND_FREE_TEMPLATE(conns);
}
/** Return a new smartlist of dir_connection_t * from get_connection_array()
* that satisfy conn_test on connection_t *conn_var, and dirconn_test on
* dir_connection_t *dirconn_var. conn_var must be of CONN_TYPE_DIR and not
@ -4504,25 +4478,6 @@ connection_dir_list_by_purpose_and_resource(
dirconn->requested_resource));
}
/** Return a directory connection (if any one exists) that is fetching
* the item described by <b>purpose</b>/<b>resource</b>/<b>state</b>,
* otherwise return NULL. */
dir_connection_t *
connection_dir_get_by_purpose_resource_and_state(
int purpose,
const char *resource,
int state)
{
smartlist_t *conns =
connection_dir_list_by_purpose_resource_and_state(
purpose,
resource,
state);
CONN_FIRST_AND_FREE_TEMPLATE(conns);
}
#undef CONN_FIRST_AND_FREE_TEMPLATE
/** Return a list of directory connections that are fetching the item
* described by <b>purpose</b>/<b>resource</b>/<b>state</b>. If there are
* none, return an empty list. This list must be freed using smartlist_free,

View File

@ -192,13 +192,6 @@ MOCK_DECL(connection_t *,connection_get_by_type_addr_port_purpose,(int type,
connection_t *connection_get_by_type_state(int type, int state);
connection_t *connection_get_by_type_state_rendquery(int type, int state,
const char *rendquery);
dir_connection_t *connection_dir_get_by_purpose_and_resource(
int purpose,
const char *resource);
dir_connection_t *connection_dir_get_by_purpose_resource_and_state(
int purpose,
const char *resource,
int state);
smartlist_t *connection_dir_list_by_purpose_and_resource(
int purpose,
const char *resource);

View File

@ -96,6 +96,9 @@ static void directory_initiate_command_rend(
time_t if_modified_since,
const rend_data_t *rend_query);
static void connection_dir_close_consensus_fetches(
dir_connection_t *except_this_one, const char *resource);
/********* START VARIABLES **********/
/** How far in the future do we allow a directory server to tell us it is
@ -1170,12 +1173,6 @@ directory_initiate_command_rend(const tor_addr_port_t *or_addr_port,
return;
}
/* ensure we don't make excess connections when we're already downloading
* a consensus during bootstrap */
if (connection_dir_avoid_extra_connection_for_purpose(dir_purpose)) {
return;
}
conn = dir_connection_new(tor_addr_family(&addr));
/* set up conn so it's got all the data we need to remember */
@ -1216,11 +1213,6 @@ directory_initiate_command_rend(const tor_addr_port_t *or_addr_port,
conn->base_.state = DIR_CONN_STATE_CLIENT_SENDING;
/* fall through */
case 0:
/* Close this connection if there's another consensus connection
* downloading (during bootstrap), or connecting (after bootstrap). */
if (connection_dir_close_consensus_conn_if_extra(conn)) {
return;
}
/* queue the command on the outbuf */
directory_send_command(conn, dir_purpose, 1, resource,
payload, payload_len,
@ -1268,11 +1260,6 @@ directory_initiate_command_rend(const tor_addr_port_t *or_addr_port,
connection_mark_for_close(TO_CONN(conn));
return;
}
/* Close this connection if there's another consensus connection
* downloading (during bootstrap), or connecting (after bootstrap). */
if (connection_dir_close_consensus_conn_if_extra(conn)) {
return;
}
conn->base_.state = DIR_CONN_STATE_CLIENT_SENDING;
/* queue the command on the outbuf */
directory_send_command(conn, dir_purpose, 0, resource,
@ -2028,6 +2015,10 @@ connection_dir_client_reached_eof(dir_connection_t *conn)
networkstatus_consensus_download_failed(0, flavname);
return -1;
}
/* If we launched other fetches for this consensus, cancel them. */
connection_dir_close_consensus_fetches(conn, flavname);
/* launches router downloads as needed */
routers_update_all_from_networkstatus(now, 3);
update_microdescs_from_networkstatus(now);
@ -3679,226 +3670,37 @@ connection_dir_finished_flushing(dir_connection_t *conn)
return 0;
}
/* A helper function for connection_dir_close_consensus_conn_if_extra()
* and connection_dir_close_extra_consensus_conns() that returns 0 if
* we can't have, or don't want to close, excess consensus connections. */
STATIC int
connection_dir_would_close_consensus_conn_helper(void)
{
const or_options_t *options = get_options();
/* we're only interested in closing excess connections if we could
* have created any in the first place */
if (!networkstatus_consensus_can_use_multiple_directories(options)) {
return 0;
}
/* We want to close excess connections downloading a consensus.
* If there aren't any excess, we don't have anything to close. */
if (!networkstatus_consensus_has_excess_connections()) {
return 0;
}
/* If we have excess connections, but none of them are downloading a
* consensus, and we are still bootstrapping (that is, we have no usable
* consensus), we don't want to close any until one starts downloading. */
if (!networkstatus_consensus_is_downloading_usable_flavor()
&& networkstatus_consensus_is_boostrapping(time(NULL))) {
return 0;
}
/* If we have just stopped bootstrapping (that is, just parsed a consensus),
* we might still have some excess connections hanging around. So we still
* have to check if we want to close any, even if we've stopped
* bootstrapping. */
return 1;
}
/* Check if we would close excess consensus connections. If we would, any
* new consensus connection would become excess immediately, so return 1.
* Otherwise, return 0. */
int
connection_dir_avoid_extra_connection_for_purpose(unsigned int purpose)
{
const or_options_t *options = get_options();
/* We're not interested in connections that aren't fetching a consensus. */
if (purpose != DIR_PURPOSE_FETCH_CONSENSUS) {
return 0;
}
/* we're only interested in avoiding excess connections if we could
* have created any in the first place */
if (!networkstatus_consensus_can_use_multiple_directories(options)) {
return 0;
}
/* If there are connections downloading a consensus, and we are still
* bootstrapping (that is, we have no usable consensus), we can be sure that
* any further connections would be excess. */
if (networkstatus_consensus_is_downloading_usable_flavor()
&& networkstatus_consensus_is_boostrapping(time(NULL))) {
return 1;
}
return 0;
}
/* Check if we have more than one consensus download connection attempt, and
* close conn:
* - if we don't have a consensus, and we're downloading a consensus, and conn
* is not downloading a consensus yet;
* - if we do have a consensus, and there's more than one consensus connection.
/* We just got a new consensus! If there are other in-progress requests
* for this consensus flavor (for example because we launched several in
* parallel), cancel them.
*
* Post-bootstrap consensus connection attempts are initiated one at a time.
* So this function won't close any consensus connection attempts that
* are initiated after bootstrap.
*/
int
connection_dir_close_consensus_conn_if_extra(dir_connection_t *conn)
{
tor_assert(conn);
tor_assert(conn->base_.type == CONN_TYPE_DIR);
/* We're not interested in connections that aren't fetching a consensus. */
if (conn->base_.purpose != DIR_PURPOSE_FETCH_CONSENSUS) {
return 0;
}
/* The connection has already been closed */
if (conn->base_.marked_for_close) {
return 0;
}
/* Only close this connection if there's another consensus connection
* downloading (during bootstrap), or connecting (after bootstrap).
* Post-bootstrap consensus connection attempts won't be closed, because
* they only occur one at a time. */
if (!connection_dir_would_close_consensus_conn_helper()) {
return 0;
}
const int we_are_bootstrapping = networkstatus_consensus_is_boostrapping(
time(NULL));
/* We don't want to check other connections to see if they are downloading,
* as this is prone to race-conditions. So leave it for
* connection_dir_consider_close_extra_consensus_conns() to clean up.
*
* But if conn has just started connecting, or we have a consensus already,
* we can be sure it's not needed any more. */
if (!we_are_bootstrapping
|| conn->base_.state == DIR_CONN_STATE_CONNECTING) {
connection_close_immediate(&conn->base_);
connection_mark_for_close(&conn->base_);
return -1;
}
return 0;
}
/* Clean up excess consensus download connection attempts.
* During bootstrap, or when the bootstrap consensus has just been downloaded,
* if we have more than one active consensus connection:
* - if we don't have a consensus, and we're downloading a consensus, keep an
* earlier connection, or a connection to a fallback directory, and close
* all other connections;
* - if we have just downloaded the bootstrap consensus, and have other
* consensus connections left over, close all of them.
* We do this check here (not just in
* connection_ap_handshake_attach_circuit()) to handle the edge case where
* a consensus fetch begins and ends before some other one tries to attach to
* a circuit, in which case the other one won't know that we're all happy now.
*
* Post-bootstrap consensus connection attempts are initiated one at a time.
* So this function won't close any consensus connection attempts that
* are initiated after bootstrap.
* Don't mark the conn that just gave us the consensus -- otherwise we
* would end up double-marking it when it cleans itself up.
*/
void
connection_dir_close_extra_consensus_conns(void)
static void
connection_dir_close_consensus_fetches(dir_connection_t *except_this_one,
const char *resource)
{
/* Only cleanup connections if there is more than one consensus connection,
* and at least one of those connections is already downloading
* (during bootstrap), or connecting (just after the bootstrap consensus is
* downloaded).
* Post-bootstrap consensus connection attempts won't be cleaned up, because
* they only occur one at a time. */
if (!connection_dir_would_close_consensus_conn_helper()) {
return;
}
int we_are_bootstrapping = networkstatus_consensus_is_boostrapping(
time(NULL));
const char *usable_resource = networkstatus_get_flavor_name(
usable_consensus_flavor());
smartlist_t *consens_usable_conns =
connection_dir_list_by_purpose_and_resource(
DIR_PURPOSE_FETCH_CONSENSUS,
usable_resource);
/* If we want to keep a connection that's downloading, find a connection to
* keep, favouring:
* - connections opened earlier (they are likely to have progressed further)
* - connections to fallbacks (to reduce the load on authorities) */
dir_connection_t *kept_download_conn = NULL;
int kept_is_authority = 0;
if (we_are_bootstrapping) {
SMARTLIST_FOREACH_BEGIN(consens_usable_conns,
dir_connection_t *, d) {
tor_assert(d);
int d_is_authority = router_digest_is_trusted_dir(d->identity_digest);
/* keep the first connection that is past the connecting state, but
* prefer fallbacks. */
if (d->base_.state != DIR_CONN_STATE_CONNECTING) {
if (!kept_download_conn || (kept_is_authority && !d_is_authority)) {
kept_download_conn = d;
kept_is_authority = d_is_authority;
/* we've found the earliest fallback, and want to keep it regardless
* of any other connections */
if (!kept_is_authority)
break;
}
}
} SMARTLIST_FOREACH_END(d);
}
SMARTLIST_FOREACH_BEGIN(consens_usable_conns,
dir_connection_t *, d) {
tor_assert(d);
/* don't close this connection if it's the one we want to keep */
if (kept_download_conn && d == kept_download_conn)
smartlist_t *conns_to_close =
connection_dir_list_by_purpose_and_resource(DIR_PURPOSE_FETCH_CONSENSUS,
resource);
SMARTLIST_FOREACH_BEGIN(conns_to_close, dir_connection_t *, d) {
if (d == except_this_one)
continue;
/* mark all other connections for close */
if (!d->base_.marked_for_close) {
connection_close_immediate(&d->base_);
connection_mark_for_close(&d->base_);
}
log_info(LD_DIR, "Closing consensus fetch (to %s) since one "
"has just arrived.", TO_CONN(d)->address);
connection_mark_for_close(TO_CONN(d));
} SMARTLIST_FOREACH_END(d);
smartlist_free(consens_usable_conns);
consens_usable_conns = NULL;
/* make sure we've closed all excess connections */
const int final_connecting_conn_count =
connection_dir_count_by_purpose_resource_and_state(
DIR_PURPOSE_FETCH_CONSENSUS,
usable_resource,
DIR_CONN_STATE_CONNECTING);
if (final_connecting_conn_count > 0) {
log_warn(LD_BUG, "Expected 0 consensus connections connecting after "
"cleanup, got %d.", final_connecting_conn_count);
}
const int expected_final_conn_count = (we_are_bootstrapping ? 1 : 0);
const int final_conn_count =
connection_dir_count_by_purpose_and_resource(
DIR_PURPOSE_FETCH_CONSENSUS,
usable_resource);
if (final_conn_count > expected_final_conn_count) {
log_warn(LD_BUG, "Expected %d consensus connections after cleanup, got "
"%d.", expected_final_conn_count, final_connecting_conn_count);
}
smartlist_free(conns_to_close);
}
/** Connected handler for directory connections: begin sending data to the
* server, and return 0, or, if the connection is an excess bootstrap
* connection, close all excess bootstrap connections.
* server, and return 0.
* Only used when connections don't immediately connect. */
int
connection_dir_finished_connecting(dir_connection_t *conn)
@ -3910,12 +3712,6 @@ connection_dir_finished_connecting(dir_connection_t *conn)
log_debug(LD_HTTP,"Dir connection to router %s:%u established.",
conn->base_.address,conn->base_.port);
/* Close this connection if there's another consensus connection
* downloading (during bootstrap), or connecting (after bootstrap). */
if (connection_dir_close_consensus_conn_if_extra(conn)) {
return -1;
}
/* start flushing conn */
conn->base_.state = DIR_CONN_STATE_CLIENT_SENDING;
return 0;
@ -3932,7 +3728,7 @@ find_dl_schedule(download_status_t *dls, const or_options_t *options)
const int dir_server = dir_server_mode(options);
const int multi_d = networkstatus_consensus_can_use_multiple_directories(
options);
const int we_are_bootstrapping = networkstatus_consensus_is_boostrapping(
const int we_are_bootstrapping = networkstatus_consensus_is_bootstrapping(
time(NULL));
const int use_fallbacks = networkstatus_consensus_can_use_extra_fallbacks(
options);

View File

@ -78,9 +78,6 @@ void directory_initiate_command(const tor_addr_t *or_addr, uint16_t or_port,
const char *resource,
const char *payload, size_t payload_len,
time_t if_modified_since);
int connection_dir_avoid_extra_connection_for_purpose(unsigned int purpose);
int connection_dir_close_consensus_conn_if_extra(dir_connection_t *conn);
void connection_dir_close_extra_consensus_conns(void);
#define DSR_HEX (1<<0)
#define DSR_BASE64 (1<<1)
@ -147,7 +144,6 @@ STATIC int directory_handle_command_get(dir_connection_t *conn,
const char *headers,
const char *req_body,
size_t req_body_len);
STATIC int connection_dir_would_close_consensus_conn_helper(void);
STATIC int download_status_schedule_get_delay(download_status_t *dls,
const smartlist_t *schedule,
time_t now);

View File

@ -691,12 +691,14 @@ dirserv_add_descriptor(routerinfo_t *ri, const char **msg, const char *source)
static was_router_added_t
dirserv_add_extrainfo(extrainfo_t *ei, const char **msg)
{
const routerinfo_t *ri;
routerinfo_t *ri;
int r;
tor_assert(msg);
*msg = NULL;
ri = router_get_by_id_digest(ei->cache_info.identity_digest);
/* Needs to be mutable so routerinfo_incompatible_with_extrainfo
* can mess with some of the flags in ri->cache_info. */
ri = router_get_mutable_by_digest(ei->cache_info.identity_digest);
if (!ri) {
*msg = "No corresponding router descriptor for extra-info descriptor";
extrainfo_free(ei);
@ -716,7 +718,8 @@ dirserv_add_extrainfo(extrainfo_t *ei, const char **msg)
return ROUTER_BAD_EI;
}
if ((r = routerinfo_incompatible_with_extrainfo(ri, ei, NULL, msg))) {
if ((r = routerinfo_incompatible_with_extrainfo(ri->identity_pkey, ei,
&ri->cache_info, msg))) {
extrainfo_free(ei);
return r < 0 ? ROUTER_IS_ALREADY_KNOWN : ROUTER_BAD_EI;
}

View File

@ -1484,17 +1484,6 @@ run_scheduled_events(time_t now)
dirvote_act(options, now);
}
/* 2d. Cleanup excess consensus bootstrap connections every second.
* connection_dir_close_consensus_conn_if_extra() closes some connections
* that are clearly excess, but this check is more thorough.
* This only closes connections if there is more than one consensus
* connection, and at least one of those connections is already downloading
* (during bootstrap), or connecting (just after the bootstrap consensus is
* downloaded).
* It won't close any consensus connections initiated after bootstrap,
* because those attempts are made one at a time. */
connection_dir_close_extra_consensus_conns();
/* 3a. Every second, we examine pending circuits and prune the
* ones which have been pending for more than a few seconds.
* We do this before step 4, so it can try building more if
@ -1917,7 +1906,7 @@ fetch_networkstatus_callback(time_t now, const or_options_t *options)
/* How often do we check whether we should download network status
* documents? */
const int we_are_bootstrapping = networkstatus_consensus_is_boostrapping(
const int we_are_bootstrapping = networkstatus_consensus_is_bootstrapping(
now);
const int prefer_mirrors = !directory_fetches_from_authorities(
get_options());

View File

@ -955,8 +955,8 @@ we_fetch_router_descriptors(const or_options_t *options)
}
/** Return the consensus flavor we actually want to use to build circuits. */
int
usable_consensus_flavor(void)
MOCK_IMPL(int,
usable_consensus_flavor,(void))
{
if (we_use_microdescriptors_for_circuits(get_options())) {
return FLAV_MICRODESC;

View File

@ -47,7 +47,7 @@ void microdesc_free_all(void);
void update_microdesc_downloads(time_t now);
void update_microdescs_from_networkstatus(time_t now);
int usable_consensus_flavor(void);
MOCK_DECL(int, usable_consensus_flavor,(void));
int we_fetch_microdescriptors(const or_options_t *options);
int we_fetch_router_descriptors(const or_options_t *options);
int we_use_microdescriptors_for_circuits(const or_options_t *options);

View File

@ -121,8 +121,7 @@ static int have_warned_about_new_version = 0;
static void routerstatus_list_update_named_server_map(void);
static void update_consensus_bootstrap_multiple_downloads(
time_t now,
const or_options_t *options,
int we_are_bootstrapping);
const or_options_t *options);
/** Forget that we've warned about anything networkstatus-related, so we will
* give fresh warnings if the same behavior happens again. */
@ -792,26 +791,6 @@ check_consensus_waiting_for_certs(int flavor, time_t now,
return 0;
}
/* Return the maximum download tries for a consensus, based on options and
* whether we_are_bootstrapping. */
static int
consensus_max_download_tries(const or_options_t *options,
int we_are_bootstrapping)
{
int use_fallbacks = networkstatus_consensus_can_use_extra_fallbacks(options);
if (we_are_bootstrapping) {
if (use_fallbacks) {
return options->ClientBootstrapConsensusMaxDownloadTries;
} else {
return
options->ClientBootstrapConsensusAuthorityOnlyMaxDownloadTries;
}
}
return options->TestingConsensusMaxDownloadTries;
}
/** If we want to download a fresh consensus, launch a new download as
* appropriate. */
static void
@ -819,7 +798,7 @@ update_consensus_networkstatus_downloads(time_t now)
{
int i;
const or_options_t *options = get_options();
const int we_are_bootstrapping = networkstatus_consensus_is_boostrapping(
const int we_are_bootstrapping = networkstatus_consensus_is_bootstrapping(
now);
const int use_multi_conn =
networkstatus_consensus_can_use_multiple_directories(options);
@ -865,28 +844,14 @@ update_consensus_networkstatus_downloads(time_t now)
&& i == usable_consensus_flavor()) {
/* Check if we're already downloading a usable consensus */
int consens_conn_count =
connection_dir_count_by_purpose_and_resource(
DIR_PURPOSE_FETCH_CONSENSUS,
resource);
int connect_consens_conn_count =
connection_dir_count_by_purpose_resource_and_state(
DIR_PURPOSE_FETCH_CONSENSUS,
resource,
DIR_CONN_STATE_CONNECTING);
if (i == usable_consensus_flavor()
&& connect_consens_conn_count < consens_conn_count) {
if (networkstatus_consensus_is_already_downloading(resource))
continue;
}
/* Make multiple connections for a bootstrap consensus download */
update_consensus_bootstrap_multiple_downloads(now, options,
we_are_bootstrapping);
/* Make multiple connections for a bootstrap consensus download. */
update_consensus_bootstrap_multiple_downloads(now, options);
} else {
/* Check if we failed downloading a consensus too recently */
int max_dl_tries = consensus_max_download_tries(options,
we_are_bootstrapping);
int max_dl_tries = options->TestingConsensusMaxDownloadTries;
/* Let's make sure we remembered to update consensus_dl_status */
tor_assert(consensus_dl_status[i].schedule == DL_SCHED_CONSENSUS);
@ -921,12 +886,16 @@ static void
update_consensus_bootstrap_attempt_downloads(
time_t now,
const or_options_t *options,
int we_are_bootstrapping,
download_status_t *dls,
download_want_authority_t want_authority)
{
int max_dl_tries = consensus_max_download_tries(options,
we_are_bootstrapping);
int use_fallbacks = networkstatus_consensus_can_use_extra_fallbacks(options);
int max_dl_tries = options->ClientBootstrapConsensusMaxDownloadTries;
if (!use_fallbacks) {
max_dl_tries =
options->ClientBootstrapConsensusAuthorityOnlyMaxDownloadTries;
}
const char *resource = networkstatus_get_flavor_name(
usable_consensus_flavor());
@ -954,13 +923,12 @@ update_consensus_bootstrap_attempt_downloads(
* connections.
* Only call when bootstrapping, and when we want to make additional
* connections. Only nodes that satisfy
* networkstatus_consensus_can_use_multiple_directories make additonal
* networkstatus_consensus_can_use_multiple_directories make additional
* connections.
*/
static void
update_consensus_bootstrap_multiple_downloads(time_t now,
const or_options_t *options,
int we_are_bootstrapping)
const or_options_t *options)
{
const int usable_flavor = usable_consensus_flavor();
@ -969,12 +937,6 @@ update_consensus_bootstrap_multiple_downloads(time_t now,
return;
}
/* If we've managed to validate a usable consensus, don't make additonal
* connections. */
if (!we_are_bootstrapping) {
return;
}
/* Launch concurrent consensus download attempt(s) based on the mirror and
* authority schedules. Try the mirror first - this makes it slightly more
* likely that we'll connect to the fallback first, and then end the
@ -993,8 +955,7 @@ update_consensus_bootstrap_multiple_downloads(time_t now,
if (!check_consensus_waiting_for_certs(usable_flavor, now, dls_f)) {
/* During bootstrap, DL_WANT_ANY_DIRSERVER means "use fallbacks". */
update_consensus_bootstrap_attempt_downloads(now, options,
we_are_bootstrapping, dls_f,
update_consensus_bootstrap_attempt_downloads(now, options, dls_f,
DL_WANT_ANY_DIRSERVER);
}
}
@ -1004,8 +965,7 @@ update_consensus_bootstrap_multiple_downloads(time_t now,
&consensus_bootstrap_dl_status[CONSENSUS_BOOTSTRAP_SOURCE_AUTHORITY];
if (!check_consensus_waiting_for_certs(usable_flavor, now, dls_a)) {
update_consensus_bootstrap_attempt_downloads(now, options,
we_are_bootstrapping, dls_a,
update_consensus_bootstrap_attempt_downloads(now, options, dls_a,
DL_WANT_AUTHORITY);
}
}
@ -1273,16 +1233,30 @@ networkstatus_get_reasonably_live_consensus(time_t now, int flavor)
return NULL;
}
/** Check if we're bootstrapping a consensus download. This means that we are
* only using the authorities and fallback directory mirrors to download the
* consensus flavour we'll use. */
int
networkstatus_consensus_is_boostrapping(time_t now)
/** Check if we need to download a consensus during tor's bootstrap phase.
* If we have no consensus, or our consensus is unusably old, return 1.
* As soon as we have received a consensus, return 0, even if we don't have
* enough certificates to validate it. */
MOCK_IMPL(int,
networkstatus_consensus_is_bootstrapping,(time_t now))
{
/* If we don't have a consensus, we must still be bootstrapping */
return !networkstatus_get_reasonably_live_consensus(
now,
usable_consensus_flavor());
/* If we have a validated, reasonably live consensus, we're not
* bootstrapping a consensus at all. */
if (networkstatus_get_reasonably_live_consensus(
now,
usable_consensus_flavor())) {
return 0;
}
/* If we have a consensus, but we're waiting for certificates,
* we're not waiting for a consensus download while bootstrapping. */
if (consensus_is_waiting_for_certs()) {
return 0;
}
/* If we have no consensus, or our consensus is very old, we are
* bootstrapping, and we need to download a consensus. */
return 1;
}
/** Check if we can use multiple directories for a consensus download.
@ -1299,8 +1273,8 @@ networkstatus_consensus_can_use_multiple_directories(
/** Check if we can use fallback directory mirrors for a consensus download.
* If we have fallbacks and don't want to fetch from the authorities,
* we can use them. */
int
networkstatus_consensus_can_use_extra_fallbacks(const or_options_t *options)
MOCK_IMPL(int,
networkstatus_consensus_can_use_extra_fallbacks,(const or_options_t *options))
{
/* The list length comparisons are a quick way to check if we have any
* non-authority fallback directories. If we ever have any authorities that
@ -1314,61 +1288,39 @@ networkstatus_consensus_can_use_extra_fallbacks(const or_options_t *options)
> smartlist_len(router_get_trusted_dir_servers())));
}
/* Check if there is more than 1 consensus connection retrieving the usable
* consensus flavor. If so, return 1, if not, return 0.
*
* During normal operation, Tor only makes one consensus download
* connection. But clients can make multiple simultaneous consensus
* connections to improve bootstrap speed and reliability.
*
* If there is more than one connection, we must have connections left
* over from bootstrapping. However, some of the connections may have
* completed and been cleaned up, so it is not sufficient to check the
* return value of this function to see if a client could make multiple
* bootstrap connections. Use
* networkstatus_consensus_can_use_multiple_directories()
* and networkstatus_consensus_is_boostrapping(). */
/* Is there a consensus fetch for flavor <b>resource</b> that's far
* enough along to be attached to a circuit? */
int
networkstatus_consensus_has_excess_connections(void)
networkstatus_consensus_is_already_downloading(const char *resource)
{
const char *usable_resource = networkstatus_get_flavor_name(
usable_consensus_flavor());
const int consens_conn_usable_count =
connection_dir_count_by_purpose_and_resource(
DIR_PURPOSE_FETCH_CONSENSUS,
usable_resource);
/* The maximum number of connections we want downloading a usable consensus
* Always 1, whether bootstrapping or not. */
const int max_expected_consens_conn_usable_count = 1;
int answer = 0;
if (consens_conn_usable_count > max_expected_consens_conn_usable_count) {
return 1;
}
/* First, get a list of all the dir conns that are fetching a consensus,
* fetching *this* consensus, and are in state "reading" (meaning they
* have already flushed their request onto the socks connection). */
smartlist_t *fetching_conns =
connection_dir_list_by_purpose_resource_and_state(
DIR_PURPOSE_FETCH_CONSENSUS, resource, DIR_CONN_STATE_CLIENT_READING);
return 0;
}
/* Then, walk through each conn, to see if its linked socks connection
* is in an attached state. We have to check this separately, since with
* the optimistic data feature, fetches can send their request to the
* socks connection and go into state 'reading', even before they're
* attached to any circuit. */
SMARTLIST_FOREACH_BEGIN(fetching_conns, dir_connection_t *, dirconn) {
/* Do any of these other dir conns have a linked socks conn that is
* attached to a circuit already? */
connection_t *base = TO_CONN(dirconn);
if (base->linked_conn &&
base->linked_conn->type == CONN_TYPE_AP &&
!AP_CONN_STATE_IS_UNATTACHED(base->linked_conn->state)) {
answer = 1;
break; /* stop looping, because we know the answer will be yes */
}
} SMARTLIST_FOREACH_END(dirconn);
smartlist_free(fetching_conns);
/* Is tor currently downloading a consensus of the usable flavor? */
int
networkstatus_consensus_is_downloading_usable_flavor(void)
{
const char *usable_resource = networkstatus_get_flavor_name(
usable_consensus_flavor());
const int consens_conn_usable_count =
connection_dir_count_by_purpose_and_resource(
DIR_PURPOSE_FETCH_CONSENSUS,
usable_resource);
const int connect_consens_conn_usable_count =
connection_dir_count_by_purpose_resource_and_state(
DIR_PURPOSE_FETCH_CONSENSUS,
usable_resource,
DIR_CONN_STATE_CONNECTING);
if (connect_consens_conn_usable_count < consens_conn_usable_count) {
return 1;
}
return 0;
return answer;
}
/** Given two router status entries for the same router identity, return 1 if

View File

@ -70,13 +70,12 @@ MOCK_DECL(networkstatus_t *,networkstatus_get_latest_consensus_by_flavor,
networkstatus_t *networkstatus_get_live_consensus(time_t now);
networkstatus_t *networkstatus_get_reasonably_live_consensus(time_t now,
int flavor);
int networkstatus_consensus_is_boostrapping(time_t now);
MOCK_DECL(int, networkstatus_consensus_is_bootstrapping,(time_t now));
int networkstatus_consensus_can_use_multiple_directories(
const or_options_t *options);
int networkstatus_consensus_can_use_extra_fallbacks(
const or_options_t *options);
int networkstatus_consensus_has_excess_connections(void);
int networkstatus_consensus_is_downloading_usable_flavor(void);
MOCK_DECL(int, networkstatus_consensus_can_use_extra_fallbacks,(
const or_options_t *options));
int networkstatus_consensus_is_already_downloading(const char *resource);
#define NSSET_FROM_CACHE 1
#define NSSET_WAS_WAITING_FOR_CERTS 2

View File

@ -2065,6 +2065,10 @@ typedef struct signed_descriptor_t {
time_t published_on;
/** For routerdescs only: digest of the corresponding extrainfo. */
char extra_info_digest[DIGEST_LEN];
/** For routerdescs only: A SHA256-digest of the extrainfo (if any) */
char extra_info_digest256[DIGEST256_LEN];
/** Certificate for ed25519 signing key. */
struct tor_cert_st *signing_key_cert;
/** For routerdescs only: Status of downloading the corresponding
* extrainfo. */
download_status_t ei_dl_status;
@ -2096,8 +2100,6 @@ typedef int16_t country_t;
/** Information about another onion router in the network. */
typedef struct {
signed_descriptor_t cache_info;
/** A SHA256-digest of the extrainfo (if any) */
char extra_info_digest256[DIGEST256_LEN];
char *nickname; /**< Human-readable OR name. */
uint32_t addr; /**< IPv4 address of OR, in host order. */
@ -2115,7 +2117,8 @@ typedef struct {
crypto_pk_t *identity_pkey; /**< Public RSA key for signing. */
/** Public curve25519 key for onions */
curve25519_public_key_t *onion_curve25519_pkey;
/** Certificate for ed25519 signing key */
/** Certificate for ed25519 signing key
* (XXXX duplicated in cache_info.) */
struct tor_cert_st *signing_key_cert;
/** What's the earliest expiration time on all the certs in this
* routerinfo? */
@ -2192,7 +2195,8 @@ typedef struct extrainfo_t {
uint8_t digest256[DIGEST256_LEN];
/** The router's nickname. */
char nickname[MAX_NICKNAME_LEN+1];
/** Certificate for ed25519 signing key */
/** Certificate for ed25519 signing key
* (XXXX duplicated in cache_info.) */
struct tor_cert_st *signing_key_cert;
/** True iff we found the right key for this extra-info, verified the
* signature, and found it to be bad. */

View File

@ -2037,6 +2037,7 @@ router_build_fresh_descriptor(routerinfo_t **r, extrainfo_t **e)
return -1;
}
ri->signing_key_cert = tor_cert_dup(get_master_signing_key_cert());
ri->cache_info.signing_key_cert = tor_cert_dup(get_master_signing_key_cert());
get_platform_str(platform, sizeof(platform));
ri->platform = tor_strdup(platform);
@ -2129,6 +2130,8 @@ router_build_fresh_descriptor(routerinfo_t **r, extrainfo_t **e)
strlcpy(ei->nickname, get_options()->Nickname, sizeof(ei->nickname));
ei->cache_info.published_on = ri->cache_info.published_on;
ei->signing_key_cert = tor_cert_dup(get_master_signing_key_cert());
ei->cache_info.signing_key_cert = tor_cert_dup(get_master_signing_key_cert());
memcpy(ei->cache_info.identity_digest, ri->cache_info.identity_digest,
DIGEST_LEN);
if (extrainfo_dump_to_string(&ei->cache_info.signed_descriptor_body,
@ -2154,7 +2157,7 @@ router_build_fresh_descriptor(routerinfo_t **r, extrainfo_t **e)
memcpy(ri->cache_info.extra_info_digest,
ei->cache_info.signed_descriptor_digest,
DIGEST_LEN);
memcpy(ri->extra_info_digest256,
memcpy(ri->cache_info.extra_info_digest256,
ei->digest256,
DIGEST256_LEN);
} else {
@ -2195,7 +2198,8 @@ router_build_fresh_descriptor(routerinfo_t **r, extrainfo_t **e)
ri->cache_info.signed_descriptor_digest);
if (ei) {
tor_assert(! routerinfo_incompatible_with_extrainfo(ri, ei, NULL, NULL));
tor_assert(! routerinfo_incompatible_with_extrainfo(ri->identity_pkey, ei,
&ri->cache_info, NULL));
}
*r = ri;
@ -2669,9 +2673,9 @@ router_dump_router_to_string(routerinfo_t *router,
char extra_info_digest[HEX_DIGEST_LEN+1];
base16_encode(extra_info_digest, sizeof(extra_info_digest),
router->cache_info.extra_info_digest, DIGEST_LEN);
if (!tor_digest256_is_zero(router->extra_info_digest256)) {
if (!tor_digest256_is_zero(router->cache_info.extra_info_digest256)) {
char d256_64[BASE64_DIGEST256_LEN+1];
digest256_to_base64(d256_64, router->extra_info_digest256);
digest256_to_base64(d256_64, router->cache_info.extra_info_digest256);
tor_asprintf(&extra_info_line, "extra-info-digest %s %s\n",
extra_info_digest, d256_64);
} else {

View File

@ -2898,6 +2898,7 @@ routerinfo_free(routerinfo_t *router)
if (router->identity_pkey)
crypto_pk_free(router->identity_pkey);
tor_cert_free(router->signing_key_cert);
tor_cert_free(router->cache_info.signing_key_cert);
if (router->declared_family) {
SMARTLIST_FOREACH(router->declared_family, char *, s, tor_free(s));
smartlist_free(router->declared_family);
@ -2917,6 +2918,7 @@ extrainfo_free(extrainfo_t *extrainfo)
if (!extrainfo)
return;
tor_cert_free(extrainfo->signing_key_cert);
tor_cert_free(extrainfo->cache_info.signing_key_cert);
tor_free(extrainfo->cache_info.signed_descriptor_body);
tor_free(extrainfo->pending_sig);
@ -3126,7 +3128,7 @@ extrainfo_insert,(routerlist_t *rl, extrainfo_t *ei, int warn_if_incompatible))
"Mismatch in digest in extrainfo map.");
goto done;
}
if (routerinfo_incompatible_with_extrainfo(ri, ei, sd,
if (routerinfo_incompatible_with_extrainfo(ri->identity_pkey, ei, sd,
&compatibility_error_msg)) {
char d1[HEX_DIGEST_LEN+1], d2[HEX_DIGEST_LEN+1];
r = (ri->cache_info.extrainfo_is_bogus) ?
@ -5165,25 +5167,32 @@ router_differences_are_cosmetic(const routerinfo_t *r1, const routerinfo_t *r2)
return 1;
}
/** Check whether <b>ri</b> (a.k.a. sd) is a router compatible with the
* extrainfo document
* <b>ei</b>. If no router is compatible with <b>ei</b>, <b>ei</b> should be
/** Check whether <b>sd</b> describes a router descriptor compatible with the
* extrainfo document <b>ei</b>.
*
* <b>identity_pkey</b> (which must also be provided) is RSA1024 identity key
* for the router. We use it to check the signature of the extrainfo document,
* if it has not already been checked.
*
* If no router is compatible with <b>ei</b>, <b>ei</b> should be
* dropped. Return 0 for "compatible", return 1 for "reject, and inform
* whoever uploaded <b>ei</b>, and return -1 for "reject silently.". If
* <b>msg</b> is present, set *<b>msg</b> to a description of the
* incompatibility (if any).
*
* Set the extrainfo_is_bogus field in <b>sd</b> if the digests matched
* but the extrainfo was nonetheless incompatible.
**/
int
routerinfo_incompatible_with_extrainfo(const routerinfo_t *ri,
routerinfo_incompatible_with_extrainfo(const crypto_pk_t *identity_pkey,
extrainfo_t *ei,
signed_descriptor_t *sd,
const char **msg)
{
int digest_matches, digest256_matches, r=1;
tor_assert(ri);
tor_assert(identity_pkey);
tor_assert(sd);
tor_assert(ei);
if (!sd)
sd = (signed_descriptor_t*)&ri->cache_info;
if (ei->bad_sig) {
if (msg) *msg = "Extrainfo signature was bad, or signed with wrong key.";
@ -5195,27 +5204,27 @@ routerinfo_incompatible_with_extrainfo(const routerinfo_t *ri,
/* Set digest256_matches to 1 if the digest is correct, or if no
* digest256 was in the ri. */
digest256_matches = tor_memeq(ei->digest256,
ri->extra_info_digest256, DIGEST256_LEN);
sd->extra_info_digest256, DIGEST256_LEN);
digest256_matches |=
tor_mem_is_zero(ri->extra_info_digest256, DIGEST256_LEN);
tor_mem_is_zero(sd->extra_info_digest256, DIGEST256_LEN);
/* The identity must match exactly to have been generated at the same time
* by the same router. */
if (tor_memneq(ri->cache_info.identity_digest,
if (tor_memneq(sd->identity_digest,
ei->cache_info.identity_digest,
DIGEST_LEN)) {
if (msg) *msg = "Extrainfo nickname or identity did not match routerinfo";
goto err; /* different servers */
}
if (! tor_cert_opt_eq(ri->signing_key_cert, ei->signing_key_cert)) {
if (! tor_cert_opt_eq(sd->signing_key_cert, ei->signing_key_cert)) {
if (msg) *msg = "Extrainfo signing key cert didn't match routerinfo";
goto err; /* different servers */
}
if (ei->pending_sig) {
char signed_digest[128];
if (crypto_pk_public_checksig(ri->identity_pkey,
if (crypto_pk_public_checksig(identity_pkey,
signed_digest, sizeof(signed_digest),
ei->pending_sig, ei->pending_sig_len) != DIGEST_LEN ||
tor_memneq(signed_digest, ei->cache_info.signed_descriptor_digest,
@ -5226,7 +5235,7 @@ routerinfo_incompatible_with_extrainfo(const routerinfo_t *ri,
goto err; /* Bad signature, or no match. */
}
ei->cache_info.send_unencrypted = ri->cache_info.send_unencrypted;
ei->cache_info.send_unencrypted = sd->send_unencrypted;
tor_free(ei->pending_sig);
}

View File

@ -191,7 +191,7 @@ void update_extrainfo_downloads(time_t now);
void router_reset_descriptor_download_failures(void);
int router_differences_are_cosmetic(const routerinfo_t *r1,
const routerinfo_t *r2);
int routerinfo_incompatible_with_extrainfo(const routerinfo_t *ri,
int routerinfo_incompatible_with_extrainfo(const crypto_pk_t *ri,
extrainfo_t *ei,
signed_descriptor_t *sd,
const char **msg);

View File

@ -1406,6 +1406,7 @@ router_parse_entry_from_string(const char *s, const char *end,
goto err;
}
router->signing_key_cert = cert; /* makes sure it gets freed. */
router->cache_info.signing_key_cert = tor_cert_dup(cert);
if (cert->cert_type != CERT_TYPE_ID_SIGNING ||
! cert->signing_key_included) {
@ -1600,7 +1601,7 @@ router_parse_entry_from_string(const char *s, const char *end,
}
if (tok->n_args >= 2) {
if (digest256_from_base64(router->extra_info_digest256, tok->args[1])
if (digest256_from_base64(router->cache_info.extra_info_digest256, tok->args[1])
< 0) {
log_warn(LD_DIR, "Invalid extra info digest256 %s",
escaped(tok->args[1]));
@ -1787,6 +1788,7 @@ extrainfo_parse_entry_from_string(const char *s, const char *end,
goto err;
}
extrainfo->signing_key_cert = cert; /* makes sure it gets freed. */
extrainfo->cache_info.signing_key_cert = tor_cert_dup(cert);
if (cert->cert_type != CERT_TYPE_ID_SIGNING ||
! cert->signing_key_included) {
log_warn(LD_DIR, "Invalid form for ed25519 cert");

View File

@ -11,6 +11,7 @@
#include "connection.h"
#include "main.h"
#include "microdesc.h"
#include "networkstatus.h"
#include "rendcache.h"
#include "directory.h"
@ -54,7 +55,11 @@ static int test_conn_get_rsrc_teardown(const struct testcase_t *tc,
#define TEST_CONN_RSRC_STATE_SUCCESSFUL (DIR_CONN_STATE_CLIENT_FINISHED)
#define TEST_CONN_RSRC_2 (networkstatus_get_flavor_name(FLAV_NS))
#define TEST_CONN_DL_STATE (DIR_CONN_STATE_CLIENT_SENDING)
#define TEST_CONN_DL_STATE (DIR_CONN_STATE_CLIENT_READING)
/* see AP_CONN_STATE_IS_UNATTACHED() */
#define TEST_CONN_UNATTACHED_STATE (AP_CONN_STATE_CIRCUIT_WAIT)
#define TEST_CONN_ATTACHED_STATE (AP_CONN_STATE_CONNECT_WAIT)
#define TEST_CONN_FD_INIT 50
static int mock_connection_connect_sockaddr_called = 0;
@ -109,27 +114,25 @@ test_conn_lookup_addr_helper(const char *address, int family, tor_addr_t *addr)
tor_addr_make_null(addr, TEST_CONN_FAMILY);
}
static void *
test_conn_get_basic_setup(const struct testcase_t *tc)
static connection_t *
test_conn_get_connection(uint8_t state, uint8_t type, uint8_t purpose)
{
connection_t *conn = NULL;
tor_addr_t addr;
int socket_err = 0;
int in_progress = 0;
(void)tc;
MOCK(connection_connect_sockaddr,
mock_connection_connect_sockaddr);
init_connection_lists();
conn = connection_new(TEST_CONN_TYPE, TEST_CONN_FAMILY);
conn = connection_new(type, TEST_CONN_FAMILY);
tt_assert(conn);
test_conn_lookup_addr_helper(TEST_CONN_ADDRESS, TEST_CONN_FAMILY, &addr);
tt_assert(!tor_addr_is_null(&addr));
/* XXXX - connection_connect doesn't set these, should it? */
tor_addr_copy_tight(&conn->addr, &addr);
conn->port = TEST_CONN_PORT;
mock_connection_connect_sockaddr_called = 0;
@ -140,8 +143,8 @@ test_conn_get_basic_setup(const struct testcase_t *tc)
tt_assert(in_progress == 0 || in_progress == 1);
/* fake some of the attributes so the connection looks OK */
conn->state = TEST_CONN_STATE;
conn->purpose = TEST_CONN_BASIC_PURPOSE;
conn->state = state;
conn->purpose = purpose;
assert_connection_ok(conn, time(NULL));
UNMOCK(connection_connect_sockaddr);
@ -151,12 +154,17 @@ test_conn_get_basic_setup(const struct testcase_t *tc)
/* On failure */
done:
UNMOCK(connection_connect_sockaddr);
test_conn_get_basic_teardown(tc, conn);
/* Returning NULL causes the unit test to fail */
return NULL;
}
static void *
test_conn_get_basic_setup(const struct testcase_t *tc)
{
(void)tc;
return test_conn_get_connection(TEST_CONN_STATE, TEST_CONN_TYPE,
TEST_CONN_BASIC_PURPOSE);
}
static int
test_conn_get_basic_teardown(const struct testcase_t *tc, void *arg)
{
@ -186,9 +194,8 @@ test_conn_get_basic_teardown(const struct testcase_t *tc, void *arg)
connection_close_immediate(conn->linked_conn);
connection_mark_for_close(conn->linked_conn);
}
conn->linked_conn->linked_conn = NULL;
connection_free(conn->linked_conn);
conn->linked_conn = NULL;
close_closeable_connections();
}
/* We didn't set the events up properly, so we can't use event_del() in
@ -222,7 +229,10 @@ static void *
test_conn_get_rend_setup(const struct testcase_t *tc)
{
dir_connection_t *conn = DOWNCAST(dir_connection_t,
test_conn_get_basic_setup(tc));
test_conn_get_connection(
TEST_CONN_STATE,
TEST_CONN_TYPE,
TEST_CONN_REND_PURPOSE));
tt_assert(conn);
assert_connection_ok(&conn->base_, time(NULL));
@ -235,7 +245,6 @@ test_conn_get_rend_setup(const struct testcase_t *tc)
TEST_CONN_REND_ADDR,
REND_SERVICE_ID_LEN_BASE32+1);
conn->rend_data->hsdirs_fp = smartlist_new();
conn->base_.purpose = TEST_CONN_REND_PURPOSE;
assert_connection_ok(&conn->base_, time(NULL));
return conn;
@ -266,42 +275,64 @@ test_conn_get_rend_teardown(const struct testcase_t *tc, void *arg)
return rv;
}
static void *
test_conn_get_rsrc_setup(const struct testcase_t *tc)
static dir_connection_t *
test_conn_download_status_add_a_connection(const char *resource)
{
dir_connection_t *conn = DOWNCAST(dir_connection_t,
test_conn_get_basic_setup(tc));
test_conn_get_connection(
TEST_CONN_STATE,
TEST_CONN_TYPE,
TEST_CONN_RSRC_PURPOSE));
tt_assert(conn);
assert_connection_ok(&conn->base_, time(NULL));
/* TODO: use the canonical function to do this - maybe? */
conn->requested_resource = tor_strdup(TEST_CONN_RSRC);
conn->base_.purpose = TEST_CONN_RSRC_PURPOSE;
/* Replace the existing resource with the one we want */
if (resource) {
if (conn->requested_resource) {
tor_free(conn->requested_resource);
}
conn->requested_resource = tor_strdup(resource);
assert_connection_ok(&conn->base_, time(NULL));
}
assert_connection_ok(&conn->base_, time(NULL));
return conn;
/* On failure */
done:
test_conn_get_rend_teardown(tc, conn);
/* Returning NULL causes the unit test to fail */
test_conn_get_rsrc_teardown(NULL, conn);
return NULL;
}
static void *
test_conn_get_rsrc_setup(const struct testcase_t *tc)
{
(void)tc;
return test_conn_download_status_add_a_connection(TEST_CONN_RSRC);
}
static int
test_conn_get_rsrc_teardown(const struct testcase_t *tc, void *arg)
{
dir_connection_t *conn = DOWNCAST(dir_connection_t, arg);
int rv = 0;
connection_t *conn = (connection_t *)arg;
tt_assert(conn);
assert_connection_ok(&conn->base_, time(NULL));
assert_connection_ok(conn, time(NULL));
/* avoid a last-ditch attempt to refetch the consensus */
conn->base_.state = TEST_CONN_RSRC_STATE_SUCCESSFUL;
if (conn->type == CONN_TYPE_DIR) {
dir_connection_t *dir_conn = DOWNCAST(dir_connection_t, arg);
tt_assert(dir_conn);
assert_connection_ok(&dir_conn->base_, time(NULL));
/* avoid a last-ditch attempt to refetch the consensus */
dir_conn->base_.state = TEST_CONN_RSRC_STATE_SUCCESSFUL;
assert_connection_ok(&dir_conn->base_, time(NULL));
}
/* connection_free_() cleans up requested_resource */
rv = test_conn_get_basic_teardown(tc, arg);
rv = test_conn_get_basic_teardown(tc, conn);
done:
return rv;
}
@ -336,14 +367,30 @@ test_conn_download_status_teardown(const struct testcase_t *tc, void *arg)
return rv;
}
static dir_connection_t *
test_conn_download_status_add_a_connection(void)
/* Like connection_ap_make_link(), but does much less */
static connection_t *
test_conn_get_linked_connection(connection_t *l_conn, uint8_t state)
{
dir_connection_t *conn = DOWNCAST(dir_connection_t,
test_conn_get_rsrc_setup(NULL));
tt_assert(l_conn);
assert_connection_ok(l_conn, time(NULL));
/* AP connections don't seem to have purposes */
connection_t *conn = test_conn_get_connection(state, CONN_TYPE_AP,
0);
tt_assert(conn);
assert_connection_ok(&conn->base_, time(NULL));
assert_connection_ok(conn, time(NULL));
conn->linked = 1;
l_conn->linked = 1;
conn->linked_conn = l_conn;
l_conn->linked_conn = conn;
/* we never opened a real socket, so we can just overwrite it */
conn->s = TOR_INVALID_SOCKET;
l_conn->s = TOR_INVALID_SOCKET;
assert_connection_ok(conn, time(NULL));
assert_connection_ok(l_conn, time(NULL));
return conn;
@ -524,44 +571,6 @@ test_conn_get_rsrc(void *arg)
tt_assert(conn);
assert_connection_ok(&conn->base_, time(NULL));
tt_assert(connection_dir_get_by_purpose_and_resource(
conn->base_.purpose,
conn->requested_resource)
== conn);
tt_assert(connection_dir_get_by_purpose_and_resource(
TEST_CONN_RSRC_PURPOSE,
TEST_CONN_RSRC)
== conn);
tt_assert(connection_dir_get_by_purpose_and_resource(
!conn->base_.purpose,
"")
== NULL);
tt_assert(connection_dir_get_by_purpose_and_resource(
!TEST_CONN_RSRC_PURPOSE,
TEST_CONN_RSRC_2)
== NULL);
tt_assert(connection_dir_get_by_purpose_resource_and_state(
conn->base_.purpose,
conn->requested_resource,
conn->base_.state)
== conn);
tt_assert(connection_dir_get_by_purpose_resource_and_state(
TEST_CONN_RSRC_PURPOSE,
TEST_CONN_RSRC,
TEST_CONN_STATE)
== conn);
tt_assert(connection_dir_get_by_purpose_resource_and_state(
!conn->base_.purpose,
"",
!conn->base_.state)
== NULL);
tt_assert(connection_dir_get_by_purpose_resource_and_state(
!TEST_CONN_RSRC_PURPOSE,
TEST_CONN_RSRC_2,
!TEST_CONN_STATE)
== NULL);
sl_is_conn_assert(connection_dir_list_by_purpose_and_resource(
conn->base_.purpose,
conn->requested_resource),
@ -641,107 +650,190 @@ test_conn_get_rsrc(void *arg)
static void
test_conn_download_status(void *arg)
{
(void)arg;
dir_connection_t *conn = NULL;
dir_connection_t *conn2 = NULL;
dir_connection_t *conn3 = NULL;
dir_connection_t *conn4 = NULL;
connection_t *ap_conn = NULL;
connection_t *ap_conn2 = NULL;
/* we never create an ap_conn for conn3 */
connection_t *ap_conn4 = NULL;
/* no connections, no excess, not downloading */
tt_assert(networkstatus_consensus_has_excess_connections() == 0);
tt_assert(networkstatus_consensus_is_downloading_usable_flavor() == 0);
tt_assert(connection_dir_avoid_extra_connection_for_purpose(
TEST_CONN_RSRC_PURPOSE) == 0);
consensus_flavor_t usable_flavor = (consensus_flavor_t)arg;
/* one connection, no excess, not downloading */
conn = test_conn_download_status_add_a_connection();
tt_assert(networkstatus_consensus_has_excess_connections() == 0);
tt_assert(networkstatus_consensus_is_downloading_usable_flavor() == 0);
tt_assert(connection_dir_avoid_extra_connection_for_purpose(
TEST_CONN_RSRC_PURPOSE) == 0);
/* The "other flavor" trick only works if there are two flavors */
tor_assert(N_CONSENSUS_FLAVORS == 2);
consensus_flavor_t other_flavor = ((usable_flavor == FLAV_NS)
? FLAV_MICRODESC
: FLAV_NS);
const char *res = networkstatus_get_flavor_name(usable_flavor);
const char *other_res = networkstatus_get_flavor_name(other_flavor);
/* one connection, no excess, but downloading */
/* no connections */
tt_assert(networkstatus_consensus_is_already_downloading(res) == 0);
tt_assert(networkstatus_consensus_is_already_downloading(other_res) == 0);
tt_assert(connection_dir_count_by_purpose_and_resource(
TEST_CONN_RSRC_PURPOSE,
res) == 0);
tt_assert(connection_dir_count_by_purpose_and_resource(
TEST_CONN_RSRC_PURPOSE,
other_res) == 0);
/* one connection, not downloading */
conn = test_conn_download_status_add_a_connection(res);
tt_assert(networkstatus_consensus_is_already_downloading(res) == 0);
tt_assert(networkstatus_consensus_is_already_downloading(other_res) == 0);
tt_assert(connection_dir_count_by_purpose_and_resource(
TEST_CONN_RSRC_PURPOSE,
res) == 1);
tt_assert(connection_dir_count_by_purpose_and_resource(
TEST_CONN_RSRC_PURPOSE,
other_res) == 0);
/* one connection, downloading but not linked (not possible on a client,
* but possible on a relay) */
conn->base_.state = TEST_CONN_DL_STATE;
tt_assert(networkstatus_consensus_has_excess_connections() == 0);
tt_assert(networkstatus_consensus_is_downloading_usable_flavor() == 1);
tt_assert(connection_dir_avoid_extra_connection_for_purpose(
TEST_CONN_RSRC_PURPOSE) == 1);
tt_assert(networkstatus_consensus_is_already_downloading(res) == 0);
tt_assert(networkstatus_consensus_is_already_downloading(other_res) == 0);
tt_assert(connection_dir_count_by_purpose_and_resource(
TEST_CONN_RSRC_PURPOSE,
res) == 1);
tt_assert(connection_dir_count_by_purpose_and_resource(
TEST_CONN_RSRC_PURPOSE,
other_res) == 0);
/* one connection, downloading and linked, but not yet attached */
ap_conn = test_conn_get_linked_connection(TO_CONN(conn),
TEST_CONN_UNATTACHED_STATE);
tt_assert(networkstatus_consensus_is_already_downloading(res) == 0);
tt_assert(networkstatus_consensus_is_already_downloading(other_res) == 0);
tt_assert(connection_dir_count_by_purpose_and_resource(
TEST_CONN_RSRC_PURPOSE,
res) == 1);
tt_assert(connection_dir_count_by_purpose_and_resource(
TEST_CONN_RSRC_PURPOSE,
other_res) == 0);
/* one connection, downloading and linked and attached */
ap_conn->state = TEST_CONN_ATTACHED_STATE;
tt_assert(networkstatus_consensus_is_already_downloading(res) == 1);
tt_assert(networkstatus_consensus_is_already_downloading(other_res) == 0);
tt_assert(connection_dir_count_by_purpose_and_resource(
TEST_CONN_RSRC_PURPOSE,
res) == 1);
tt_assert(connection_dir_count_by_purpose_and_resource(
TEST_CONN_RSRC_PURPOSE,
other_res) == 0);
/* one connection, linked and attached but not downloading */
conn->base_.state = TEST_CONN_STATE;
tt_assert(networkstatus_consensus_is_already_downloading(res) == 0);
tt_assert(networkstatus_consensus_is_already_downloading(other_res) == 0);
tt_assert(connection_dir_count_by_purpose_and_resource(
TEST_CONN_RSRC_PURPOSE,
res) == 1);
tt_assert(connection_dir_count_by_purpose_and_resource(
TEST_CONN_RSRC_PURPOSE,
other_res) == 0);
/* two connections, both not downloading */
conn2 = test_conn_download_status_add_a_connection(res);
tt_assert(networkstatus_consensus_is_already_downloading(res) == 0);
tt_assert(networkstatus_consensus_is_already_downloading(other_res) == 0);
tt_assert(connection_dir_count_by_purpose_and_resource(
TEST_CONN_RSRC_PURPOSE,
res) == 2);
tt_assert(connection_dir_count_by_purpose_and_resource(
TEST_CONN_RSRC_PURPOSE,
other_res) == 0);
/* two connections, one downloading */
conn->base_.state = TEST_CONN_DL_STATE;
tt_assert(networkstatus_consensus_is_already_downloading(res) == 1);
tt_assert(networkstatus_consensus_is_already_downloading(other_res) == 0);
tt_assert(connection_dir_count_by_purpose_and_resource(
TEST_CONN_RSRC_PURPOSE,
res) == 2);
tt_assert(connection_dir_count_by_purpose_and_resource(
TEST_CONN_RSRC_PURPOSE,
other_res) == 0);
conn->base_.state = TEST_CONN_STATE;
/* two connections, excess, but not downloading */
conn2 = test_conn_download_status_add_a_connection();
tt_assert(networkstatus_consensus_has_excess_connections() == 1);
tt_assert(networkstatus_consensus_is_downloading_usable_flavor() == 0);
tt_assert(connection_dir_avoid_extra_connection_for_purpose(
TEST_CONN_RSRC_PURPOSE) == 0);
/* more connections, all not downloading */
conn3 = test_conn_download_status_add_a_connection(res);
tt_assert(networkstatus_consensus_is_already_downloading(res) == 0);
tt_assert(networkstatus_consensus_is_already_downloading(other_res) == 0);
tt_assert(connection_dir_count_by_purpose_and_resource(
TEST_CONN_RSRC_PURPOSE,
res) == 3);
tt_assert(connection_dir_count_by_purpose_and_resource(
TEST_CONN_RSRC_PURPOSE,
other_res) == 0);
/* two connections, excess, downloading */
/* more connections, one downloading */
conn->base_.state = TEST_CONN_DL_STATE;
tt_assert(networkstatus_consensus_is_already_downloading(res) == 1);
tt_assert(networkstatus_consensus_is_already_downloading(other_res) == 0);
tt_assert(connection_dir_count_by_purpose_and_resource(
TEST_CONN_RSRC_PURPOSE,
res) == 3);
tt_assert(connection_dir_count_by_purpose_and_resource(
TEST_CONN_RSRC_PURPOSE,
other_res) == 0);
/* more connections, two downloading (should never happen, but needs
* to be tested for completeness) */
conn2->base_.state = TEST_CONN_DL_STATE;
tt_assert(networkstatus_consensus_has_excess_connections() == 1);
tt_assert(networkstatus_consensus_is_downloading_usable_flavor() == 1);
tt_assert(connection_dir_avoid_extra_connection_for_purpose(
TEST_CONN_RSRC_PURPOSE) == 1);
conn2->base_.state = TEST_CONN_STATE;
/* more connections, excess, but not downloading */
conn3 = test_conn_download_status_add_a_connection();
tt_assert(networkstatus_consensus_has_excess_connections() == 1);
tt_assert(networkstatus_consensus_is_downloading_usable_flavor() == 0);
tt_assert(connection_dir_avoid_extra_connection_for_purpose(
TEST_CONN_RSRC_PURPOSE) == 0);
/* more connections, excess, downloading */
conn3->base_.state = TEST_CONN_DL_STATE;
tt_assert(networkstatus_consensus_has_excess_connections() == 1);
tt_assert(networkstatus_consensus_is_downloading_usable_flavor() == 1);
tt_assert(connection_dir_avoid_extra_connection_for_purpose(
TEST_CONN_RSRC_PURPOSE) == 1);
/* more connections, more downloading */
conn2->base_.state = TEST_CONN_DL_STATE;
tt_assert(networkstatus_consensus_has_excess_connections() == 1);
tt_assert(networkstatus_consensus_is_downloading_usable_flavor() == 1);
tt_assert(connection_dir_avoid_extra_connection_for_purpose(
TEST_CONN_RSRC_PURPOSE) == 1);
/* now try closing the one that isn't downloading:
* these tests won't work unless tor thinks it is bootstrapping */
tt_assert(networkstatus_consensus_is_boostrapping(time(NULL)));
ap_conn2 = test_conn_get_linked_connection(TO_CONN(conn2),
TEST_CONN_ATTACHED_STATE);
tt_assert(networkstatus_consensus_is_already_downloading(res) == 1);
tt_assert(networkstatus_consensus_is_already_downloading(other_res) == 0);
tt_assert(connection_dir_count_by_purpose_and_resource(
TEST_CONN_RSRC_PURPOSE,
TEST_CONN_RSRC) == 3);
tt_assert(connection_dir_avoid_extra_connection_for_purpose(
TEST_CONN_RSRC_PURPOSE) == 1);
tt_assert(connection_dir_close_consensus_conn_if_extra(conn) == -1);
res) == 3);
tt_assert(connection_dir_count_by_purpose_and_resource(
TEST_CONN_RSRC_PURPOSE,
TEST_CONN_RSRC) == 2);
tt_assert(connection_dir_avoid_extra_connection_for_purpose(
TEST_CONN_RSRC_PURPOSE) == 1);
other_res) == 0);
conn->base_.state = TEST_CONN_STATE;
/* now try closing one that is already closed - nothing happens */
tt_assert(connection_dir_close_consensus_conn_if_extra(conn) == 0);
/* more connections, a different one downloading */
tt_assert(networkstatus_consensus_is_already_downloading(res) == 1);
tt_assert(networkstatus_consensus_is_already_downloading(other_res) == 0);
tt_assert(connection_dir_count_by_purpose_and_resource(
TEST_CONN_RSRC_PURPOSE,
TEST_CONN_RSRC) == 2);
tt_assert(connection_dir_avoid_extra_connection_for_purpose(
TEST_CONN_RSRC_PURPOSE) == 1);
res) == 3);
tt_assert(connection_dir_count_by_purpose_and_resource(
TEST_CONN_RSRC_PURPOSE,
other_res) == 0);
/* now try closing one that is downloading - it stays open */
tt_assert(connection_dir_close_consensus_conn_if_extra(conn2) == 0);
/* a connection for the other flavor (could happen if a client is set to
* cache directory documents), one preferred flavor downloading
*/
conn4 = test_conn_download_status_add_a_connection(other_res);
tt_assert(networkstatus_consensus_is_already_downloading(res) == 1);
tt_assert(networkstatus_consensus_is_already_downloading(other_res) == 0);
tt_assert(connection_dir_count_by_purpose_and_resource(
TEST_CONN_RSRC_PURPOSE,
TEST_CONN_RSRC) == 2);
tt_assert(connection_dir_avoid_extra_connection_for_purpose(
TEST_CONN_RSRC_PURPOSE) == 1);
res) == 3);
tt_assert(connection_dir_count_by_purpose_and_resource(
TEST_CONN_RSRC_PURPOSE,
other_res) == 1);
/* now try closing all excess connections */
connection_dir_close_extra_consensus_conns();
/* a connection for the other flavor (could happen if a client is set to
* cache directory documents), both flavors downloading
*/
conn4->base_.state = TEST_CONN_DL_STATE;
ap_conn4 = test_conn_get_linked_connection(TO_CONN(conn4),
TEST_CONN_ATTACHED_STATE);
tt_assert(networkstatus_consensus_is_already_downloading(res) == 1);
tt_assert(networkstatus_consensus_is_already_downloading(other_res) == 1);
tt_assert(connection_dir_count_by_purpose_and_resource(
TEST_CONN_RSRC_PURPOSE,
TEST_CONN_RSRC) == 1);
tt_assert(connection_dir_avoid_extra_connection_for_purpose(
TEST_CONN_RSRC_PURPOSE) == 1);
res) == 3);
tt_assert(connection_dir_count_by_purpose_and_resource(
TEST_CONN_RSRC_PURPOSE,
other_res) == 1);
conn4->base_.state = TEST_CONN_STATE;
done:
/* the teardown function removes all the connections */;
@ -750,11 +842,18 @@ test_conn_download_status(void *arg)
#define CONNECTION_TESTCASE(name, fork, setup) \
{ #name, test_conn_##name, fork, &setup, NULL }
/* where arg is an expression (constant, varaible, compound expression) */
#define CONNECTION_TESTCASE_ARG(name, fork, setup, arg) \
{ #name "_" #arg, test_conn_##name, fork, &setup, (void *)arg }
struct testcase_t connection_tests[] = {
CONNECTION_TESTCASE(get_basic, TT_FORK, test_conn_get_basic_st),
CONNECTION_TESTCASE(get_rend, TT_FORK, test_conn_get_rend_st),
CONNECTION_TESTCASE(get_rsrc, TT_FORK, test_conn_get_rsrc_st),
CONNECTION_TESTCASE(download_status, TT_FORK, test_conn_download_status_st),
CONNECTION_TESTCASE_ARG(download_status, TT_FORK,
test_conn_download_status_st, FLAV_MICRODESC),
CONNECTION_TESTCASE_ARG(download_status, TT_FORK,
test_conn_download_status_st, FLAV_NS),
//CONNECTION_TESTCASE(func_suffix, TT_FORK, setup_func_pair),
END_OF_TESTCASES
};

View File

@ -4041,12 +4041,56 @@ test_dir_choose_compression_level(void* data)
done: ;
}
static int mock_networkstatus_consensus_is_bootstrapping_value = 0;
static int
mock_networkstatus_consensus_is_bootstrapping(time_t now)
{
(void)now;
return mock_networkstatus_consensus_is_bootstrapping_value;
}
static int mock_networkstatus_consensus_can_use_extra_fallbacks_value = 0;
static int
mock_networkstatus_consensus_can_use_extra_fallbacks(
const or_options_t *options)
{
(void)options;
return mock_networkstatus_consensus_can_use_extra_fallbacks_value;
}
/* data is a 2 character nul-terminated string.
* If data[0] is 'b', set bootstrapping, anything else means not bootstrapping
* If data[1] is 'f', set extra fallbacks, anything else means no extra
* fallbacks.
*/
static void
test_dir_find_dl_schedule(void* data)
{
const char *str = (const char *)data;
tt_assert(strlen(data) == 2);
if (str[0] == 'b') {
mock_networkstatus_consensus_is_bootstrapping_value = 1;
} else {
mock_networkstatus_consensus_is_bootstrapping_value = 0;
}
if (str[1] == 'f') {
mock_networkstatus_consensus_can_use_extra_fallbacks_value = 1;
} else {
mock_networkstatus_consensus_can_use_extra_fallbacks_value = 0;
}
MOCK(networkstatus_consensus_is_bootstrapping,
mock_networkstatus_consensus_is_bootstrapping);
MOCK(networkstatus_consensus_can_use_extra_fallbacks,
mock_networkstatus_consensus_can_use_extra_fallbacks);
download_status_t dls;
smartlist_t server, client, server_cons, client_cons, bridge;
(void)data;
smartlist_t server, client, server_cons, client_cons;
smartlist_t client_boot_auth_only_cons, client_boot_auth_cons;
smartlist_t client_boot_fallback_cons, bridge;
mock_options = malloc(sizeof(or_options_t));
reset_options(mock_options, &mock_get_options_calls);
@ -4056,43 +4100,121 @@ test_dir_find_dl_schedule(void* data)
mock_options->TestingClientDownloadSchedule = &client;
mock_options->TestingServerConsensusDownloadSchedule = &server_cons;
mock_options->TestingClientConsensusDownloadSchedule = &client_cons;
mock_options->ClientBootstrapConsensusAuthorityOnlyDownloadSchedule =
&client_boot_auth_only_cons;
mock_options->ClientBootstrapConsensusAuthorityDownloadSchedule =
&client_boot_auth_cons;
mock_options->ClientBootstrapConsensusFallbackDownloadSchedule =
&client_boot_fallback_cons;
mock_options->TestingBridgeDownloadSchedule = &bridge;
dls.schedule = DL_SCHED_GENERIC;
/* client */
mock_options->ClientOnly = 1;
tt_ptr_op(find_dl_schedule(&dls, mock_options), OP_EQ, &client);
mock_options->ClientOnly = 0;
/* dir mode */
mock_options->DirPort_set = 1;
mock_options->ORPort_set = 1;
mock_options->DirCache = 1;
tt_ptr_op(find_dl_schedule(&dls, mock_options), OP_EQ, &server);
#if 0
dls.schedule = DL_SCHED_CONSENSUS;
mock_options->ClientOnly = 1;
mock_options->DirPort_set = 0;
mock_options->DirCache = 0;
tt_ptr_op(find_dl_schedule(&dls, mock_options), OP_EQ, &client_cons);
mock_options->ClientOnly = 0;
mock_options->DirCache = 1;
dls.schedule = DL_SCHED_CONSENSUS;
/* public server mode */
mock_options->ORPort_set = 1;
tt_ptr_op(find_dl_schedule(&dls, mock_options), OP_EQ, &server_cons);
#endif
mock_options->ORPort_set = 0;
/* client and bridge modes */
if (networkstatus_consensus_is_bootstrapping(time(NULL))) {
if (networkstatus_consensus_can_use_extra_fallbacks(mock_options)) {
dls.want_authority = 1;
/* client */
mock_options->ClientOnly = 1;
tt_ptr_op(find_dl_schedule(&dls, mock_options), OP_EQ,
&client_boot_auth_cons);
mock_options->ClientOnly = 0;
/* bridge relay */
mock_options->ORPort_set = 1;
mock_options->BridgeRelay = 1;
tt_ptr_op(find_dl_schedule(&dls, mock_options), OP_EQ,
&client_boot_auth_cons);
mock_options->ORPort_set = 0;
mock_options->BridgeRelay = 0;
dls.want_authority = 0;
/* client */
mock_options->ClientOnly = 1;
tt_ptr_op(find_dl_schedule(&dls, mock_options), OP_EQ,
&client_boot_fallback_cons);
mock_options->ClientOnly = 0;
/* bridge relay */
mock_options->ORPort_set = 1;
mock_options->BridgeRelay = 1;
tt_ptr_op(find_dl_schedule(&dls, mock_options), OP_EQ,
&client_boot_fallback_cons);
mock_options->ORPort_set = 0;
mock_options->BridgeRelay = 0;
} else {
/* dls.want_authority is ignored */
/* client */
mock_options->ClientOnly = 1;
tt_ptr_op(find_dl_schedule(&dls, mock_options), OP_EQ,
&client_boot_auth_only_cons);
mock_options->ClientOnly = 0;
/* bridge relay */
mock_options->ORPort_set = 1;
mock_options->BridgeRelay = 1;
tt_ptr_op(find_dl_schedule(&dls, mock_options), OP_EQ,
&client_boot_auth_only_cons);
mock_options->ORPort_set = 0;
mock_options->BridgeRelay = 0;
}
} else {
/* client */
mock_options->ClientOnly = 1;
tt_ptr_op(find_dl_schedule(&dls, mock_options), OP_EQ,
&client_cons);
mock_options->ClientOnly = 0;
/* bridge relay */
mock_options->ORPort_set = 1;
mock_options->BridgeRelay = 1;
tt_ptr_op(find_dl_schedule(&dls, mock_options), OP_EQ,
&client_cons);
mock_options->ORPort_set = 0;
mock_options->BridgeRelay = 0;
}
dls.schedule = DL_SCHED_BRIDGE;
/* client */
mock_options->ClientOnly = 1;
tt_ptr_op(find_dl_schedule(&dls, mock_options), OP_EQ, &bridge);
mock_options->ClientOnly = 0;
tt_ptr_op(find_dl_schedule(&dls, mock_options), OP_EQ, &bridge);
done:
UNMOCK(networkstatus_consensus_is_bootstrapping);
UNMOCK(networkstatus_consensus_can_use_extra_fallbacks);
UNMOCK(get_options);
free(mock_options);
mock_options = NULL;
}
#define DIR_LEGACY(name) \
#define DIR_LEGACY(name) \
{ #name, test_dir_ ## name , TT_FORK, NULL, NULL }
#define DIR(name,flags) \
{ #name, test_dir_##name, (flags), NULL, NULL }
/* where arg is a string constant */
#define DIR_ARG(name,flags,arg) \
{ #name "_" arg, test_dir_##name, (flags), &passthrough_setup, arg }
struct testcase_t dir_tests[] = {
DIR_LEGACY(nicknames),
DIR_LEGACY(formats),
@ -4127,7 +4249,10 @@ struct testcase_t dir_tests[] = {
DIR(should_not_init_request_to_dir_auths_without_v3_info, 0),
DIR(should_init_request_to_dir_auths, 0),
DIR(choose_compression_level, 0),
DIR(find_dl_schedule, 0),
DIR_ARG(find_dl_schedule, TT_FORK, "bf"),
DIR_ARG(find_dl_schedule, TT_FORK, "ba"),
DIR_ARG(find_dl_schedule, TT_FORK, "cf"),
DIR_ARG(find_dl_schedule, TT_FORK, "ca"),
END_OF_TESTCASES
};

View File

@ -15,6 +15,7 @@
#include "container.h"
#include "directory.h"
#include "dirvote.h"
#include "microdesc.h"
#include "networkstatus.h"
#include "nodelist.h"
#include "policies.h"
@ -190,6 +191,14 @@ construct_consensus(char **consensus_text_md)
crypto_pk_free(sign_skey_leg);
}
static int mock_usable_consensus_flavor_value = FLAV_NS;
static int
mock_usable_consensus_flavor(void)
{
return mock_usable_consensus_flavor_value;
}
static void
test_router_pick_directory_server_impl(void *arg)
{
@ -209,6 +218,22 @@ test_router_pick_directory_server_impl(void *arg)
(void)arg;
MOCK(usable_consensus_flavor, mock_usable_consensus_flavor);
/* With no consensus, we must be bootstrapping, regardless of time or flavor
*/
mock_usable_consensus_flavor_value = FLAV_NS;
tt_assert(networkstatus_consensus_is_bootstrapping(now));
tt_assert(networkstatus_consensus_is_bootstrapping(now + 2000));
tt_assert(networkstatus_consensus_is_bootstrapping(now + 2*24*60*60));
tt_assert(networkstatus_consensus_is_bootstrapping(now - 2*24*60*60));
mock_usable_consensus_flavor_value = FLAV_MICRODESC;
tt_assert(networkstatus_consensus_is_bootstrapping(now));
tt_assert(networkstatus_consensus_is_bootstrapping(now + 2000));
tt_assert(networkstatus_consensus_is_bootstrapping(now + 2*24*60*60));
tt_assert(networkstatus_consensus_is_bootstrapping(now - 2*24*60*60));
/* No consensus available, fail early */
rs = router_pick_directory_server_impl(V3_DIRINFO, (const int) 0, NULL);
tt_assert(rs == NULL);
@ -223,6 +248,28 @@ test_router_pick_directory_server_impl(void *arg)
tt_int_op(smartlist_len(con_md->routerstatus_list), ==, 3);
tt_assert(!networkstatus_set_current_consensus_from_ns(con_md,
"microdesc"));
/* If the consensus time or flavor doesn't match, we are still
* bootstrapping */
mock_usable_consensus_flavor_value = FLAV_NS;
tt_assert(networkstatus_consensus_is_bootstrapping(now));
tt_assert(networkstatus_consensus_is_bootstrapping(now + 2000));
tt_assert(networkstatus_consensus_is_bootstrapping(now + 2*24*60*60));
tt_assert(networkstatus_consensus_is_bootstrapping(now - 2*24*60*60));
/* With a valid consensus for the current time and flavor, we stop
* bootstrapping, even if we have no certificates */
mock_usable_consensus_flavor_value = FLAV_MICRODESC;
tt_assert(!networkstatus_consensus_is_bootstrapping(now + 2000));
tt_assert(!networkstatus_consensus_is_bootstrapping(con_md->valid_after));
tt_assert(!networkstatus_consensus_is_bootstrapping(con_md->valid_until));
tt_assert(!networkstatus_consensus_is_bootstrapping(con_md->valid_until
+ 24*60*60));
/* These times are outside the test validity period */
tt_assert(networkstatus_consensus_is_bootstrapping(now));
tt_assert(networkstatus_consensus_is_bootstrapping(now + 2*24*60*60));
tt_assert(networkstatus_consensus_is_bootstrapping(now - 2*24*60*60));
nodelist_set_consensus(con_md);
nodelist_assert_ok();
@ -362,6 +409,7 @@ test_router_pick_directory_server_impl(void *arg)
node_router1->rs->last_dir_503_at = 0;
done:
UNMOCK(usable_consensus_flavor);
if (router1_id)
tor_free(router1_id);
if (router2_id)