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 */ /* we're a general conn */
origin_circuit_t *circ=NULL; 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) { if (conn->chosen_exit_name) {
const node_t *node = node_get_by_nickname(conn->chosen_exit_name, 1); const node_t *node = node_get_by_nickname(conn->chosen_exit_name, 1);
int opt = conn->chosen_exit_optional; 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() /** 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 * 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 * 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)); 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 /** 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 * 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, * 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(int type, int state);
connection_t *connection_get_by_type_state_rendquery(int type, int state, connection_t *connection_get_by_type_state_rendquery(int type, int state,
const char *rendquery); 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( smartlist_t *connection_dir_list_by_purpose_and_resource(
int purpose, int purpose,
const char *resource); const char *resource);

View File

@ -96,6 +96,9 @@ static void directory_initiate_command_rend(
time_t if_modified_since, time_t if_modified_since,
const rend_data_t *rend_query); const rend_data_t *rend_query);
static void connection_dir_close_consensus_fetches(
dir_connection_t *except_this_one, const char *resource);
/********* START VARIABLES **********/ /********* START VARIABLES **********/
/** How far in the future do we allow a directory server to tell us it is /** 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; 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)); conn = dir_connection_new(tor_addr_family(&addr));
/* set up conn so it's got all the data we need to remember */ /* 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; conn->base_.state = DIR_CONN_STATE_CLIENT_SENDING;
/* fall through */ /* fall through */
case 0: 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 */ /* queue the command on the outbuf */
directory_send_command(conn, dir_purpose, 1, resource, directory_send_command(conn, dir_purpose, 1, resource,
payload, payload_len, 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)); connection_mark_for_close(TO_CONN(conn));
return; 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; conn->base_.state = DIR_CONN_STATE_CLIENT_SENDING;
/* queue the command on the outbuf */ /* queue the command on the outbuf */
directory_send_command(conn, dir_purpose, 0, resource, 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); networkstatus_consensus_download_failed(0, flavname);
return -1; return -1;
} }
/* If we launched other fetches for this consensus, cancel them. */
connection_dir_close_consensus_fetches(conn, flavname);
/* launches router downloads as needed */ /* launches router downloads as needed */
routers_update_all_from_networkstatus(now, 3); routers_update_all_from_networkstatus(now, 3);
update_microdescs_from_networkstatus(now); update_microdescs_from_networkstatus(now);
@ -3679,226 +3670,37 @@ connection_dir_finished_flushing(dir_connection_t *conn)
return 0; return 0;
} }
/* A helper function for connection_dir_close_consensus_conn_if_extra() /* We just got a new consensus! If there are other in-progress requests
* and connection_dir_close_extra_consensus_conns() that returns 0 if * for this consensus flavor (for example because we launched several in
* we can't have, or don't want to close, excess consensus connections. */ * parallel), cancel them.
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.
* *
* Post-bootstrap consensus connection attempts are initiated one at a time. * We do this check here (not just in
* So this function won't close any consensus connection attempts that * connection_ap_handshake_attach_circuit()) to handle the edge case where
* are initiated after bootstrap. * 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.
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.
* *
* Post-bootstrap consensus connection attempts are initiated one at a time. * Don't mark the conn that just gave us the consensus -- otherwise we
* So this function won't close any consensus connection attempts that * would end up double-marking it when it cleans itself up.
* are initiated after bootstrap.
*/ */
void static void
connection_dir_close_extra_consensus_conns(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, smartlist_t *conns_to_close =
* and at least one of those connections is already downloading connection_dir_list_by_purpose_and_resource(DIR_PURPOSE_FETCH_CONSENSUS,
* (during bootstrap), or connecting (just after the bootstrap consensus is resource);
* downloaded). SMARTLIST_FOREACH_BEGIN(conns_to_close, dir_connection_t *, d) {
* Post-bootstrap consensus connection attempts won't be cleaned up, because if (d == except_this_one)
* 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)
continue; continue;
/* mark all other connections for close */ log_info(LD_DIR, "Closing consensus fetch (to %s) since one "
if (!d->base_.marked_for_close) { "has just arrived.", TO_CONN(d)->address);
connection_close_immediate(&d->base_); connection_mark_for_close(TO_CONN(d));
connection_mark_for_close(&d->base_);
}
} SMARTLIST_FOREACH_END(d); } SMARTLIST_FOREACH_END(d);
smartlist_free(conns_to_close);
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);
}
} }
/** Connected handler for directory connections: begin sending data to the /** Connected handler for directory connections: begin sending data to the
* server, and return 0, or, if the connection is an excess bootstrap * server, and return 0.
* connection, close all excess bootstrap connections.
* Only used when connections don't immediately connect. */ * Only used when connections don't immediately connect. */
int int
connection_dir_finished_connecting(dir_connection_t *conn) 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.", log_debug(LD_HTTP,"Dir connection to router %s:%u established.",
conn->base_.address,conn->base_.port); 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 */ /* start flushing conn */
conn->base_.state = DIR_CONN_STATE_CLIENT_SENDING; conn->base_.state = DIR_CONN_STATE_CLIENT_SENDING;
return 0; 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 dir_server = dir_server_mode(options);
const int multi_d = networkstatus_consensus_can_use_multiple_directories( const int multi_d = networkstatus_consensus_can_use_multiple_directories(
options); options);
const int we_are_bootstrapping = networkstatus_consensus_is_boostrapping( const int we_are_bootstrapping = networkstatus_consensus_is_bootstrapping(
time(NULL)); time(NULL));
const int use_fallbacks = networkstatus_consensus_can_use_extra_fallbacks( const int use_fallbacks = networkstatus_consensus_can_use_extra_fallbacks(
options); 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 *resource,
const char *payload, size_t payload_len, const char *payload, size_t payload_len,
time_t if_modified_since); 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_HEX (1<<0)
#define DSR_BASE64 (1<<1) #define DSR_BASE64 (1<<1)
@ -147,7 +144,6 @@ STATIC int directory_handle_command_get(dir_connection_t *conn,
const char *headers, const char *headers,
const char *req_body, const char *req_body,
size_t req_body_len); 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, STATIC int download_status_schedule_get_delay(download_status_t *dls,
const smartlist_t *schedule, const smartlist_t *schedule,
time_t now); 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 static was_router_added_t
dirserv_add_extrainfo(extrainfo_t *ei, const char **msg) dirserv_add_extrainfo(extrainfo_t *ei, const char **msg)
{ {
const routerinfo_t *ri; routerinfo_t *ri;
int r; int r;
tor_assert(msg); tor_assert(msg);
*msg = NULL; *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) { if (!ri) {
*msg = "No corresponding router descriptor for extra-info descriptor"; *msg = "No corresponding router descriptor for extra-info descriptor";
extrainfo_free(ei); extrainfo_free(ei);
@ -716,7 +718,8 @@ dirserv_add_extrainfo(extrainfo_t *ei, const char **msg)
return ROUTER_BAD_EI; 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); extrainfo_free(ei);
return r < 0 ? ROUTER_IS_ALREADY_KNOWN : ROUTER_BAD_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); 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 /* 3a. Every second, we examine pending circuits and prune the
* ones which have been pending for more than a few seconds. * ones which have been pending for more than a few seconds.
* We do this before step 4, so it can try building more if * 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 /* How often do we check whether we should download network status
* documents? */ * documents? */
const int we_are_bootstrapping = networkstatus_consensus_is_boostrapping( const int we_are_bootstrapping = networkstatus_consensus_is_bootstrapping(
now); now);
const int prefer_mirrors = !directory_fetches_from_authorities( const int prefer_mirrors = !directory_fetches_from_authorities(
get_options()); 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. */ /** Return the consensus flavor we actually want to use to build circuits. */
int MOCK_IMPL(int,
usable_consensus_flavor(void) usable_consensus_flavor,(void))
{ {
if (we_use_microdescriptors_for_circuits(get_options())) { if (we_use_microdescriptors_for_circuits(get_options())) {
return FLAV_MICRODESC; return FLAV_MICRODESC;

View File

@ -47,7 +47,7 @@ void microdesc_free_all(void);
void update_microdesc_downloads(time_t now); void update_microdesc_downloads(time_t now);
void update_microdescs_from_networkstatus(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_microdescriptors(const or_options_t *options);
int we_fetch_router_descriptors(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); 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 routerstatus_list_update_named_server_map(void);
static void update_consensus_bootstrap_multiple_downloads( static void update_consensus_bootstrap_multiple_downloads(
time_t now, time_t now,
const or_options_t *options, const or_options_t *options);
int we_are_bootstrapping);
/** Forget that we've warned about anything networkstatus-related, so we will /** Forget that we've warned about anything networkstatus-related, so we will
* give fresh warnings if the same behavior happens again. */ * 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 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 /** If we want to download a fresh consensus, launch a new download as
* appropriate. */ * appropriate. */
static void static void
@ -819,7 +798,7 @@ update_consensus_networkstatus_downloads(time_t now)
{ {
int i; int i;
const or_options_t *options = get_options(); 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); now);
const int use_multi_conn = const int use_multi_conn =
networkstatus_consensus_can_use_multiple_directories(options); networkstatus_consensus_can_use_multiple_directories(options);
@ -865,28 +844,14 @@ update_consensus_networkstatus_downloads(time_t now)
&& i == usable_consensus_flavor()) { && i == usable_consensus_flavor()) {
/* Check if we're already downloading a usable consensus */ /* Check if we're already downloading a usable consensus */
int consens_conn_count = if (networkstatus_consensus_is_already_downloading(resource))
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) {
continue; continue;
}
/* Make multiple connections for a bootstrap consensus download */ /* Make multiple connections for a bootstrap consensus download. */
update_consensus_bootstrap_multiple_downloads(now, options, update_consensus_bootstrap_multiple_downloads(now, options);
we_are_bootstrapping);
} else { } else {
/* Check if we failed downloading a consensus too recently */ /* Check if we failed downloading a consensus too recently */
int max_dl_tries = consensus_max_download_tries(options, int max_dl_tries = options->TestingConsensusMaxDownloadTries;
we_are_bootstrapping);
/* Let's make sure we remembered to update consensus_dl_status */ /* Let's make sure we remembered to update consensus_dl_status */
tor_assert(consensus_dl_status[i].schedule == DL_SCHED_CONSENSUS); tor_assert(consensus_dl_status[i].schedule == DL_SCHED_CONSENSUS);
@ -921,12 +886,16 @@ static void
update_consensus_bootstrap_attempt_downloads( update_consensus_bootstrap_attempt_downloads(
time_t now, time_t now,
const or_options_t *options, const or_options_t *options,
int we_are_bootstrapping,
download_status_t *dls, download_status_t *dls,
download_want_authority_t want_authority) download_want_authority_t want_authority)
{ {
int max_dl_tries = consensus_max_download_tries(options, int use_fallbacks = networkstatus_consensus_can_use_extra_fallbacks(options);
we_are_bootstrapping); int max_dl_tries = options->ClientBootstrapConsensusMaxDownloadTries;
if (!use_fallbacks) {
max_dl_tries =
options->ClientBootstrapConsensusAuthorityOnlyMaxDownloadTries;
}
const char *resource = networkstatus_get_flavor_name( const char *resource = networkstatus_get_flavor_name(
usable_consensus_flavor()); usable_consensus_flavor());
@ -954,13 +923,12 @@ update_consensus_bootstrap_attempt_downloads(
* connections. * connections.
* Only call when bootstrapping, and when we want to make additional * Only call when bootstrapping, and when we want to make additional
* connections. Only nodes that satisfy * connections. Only nodes that satisfy
* networkstatus_consensus_can_use_multiple_directories make additonal * networkstatus_consensus_can_use_multiple_directories make additional
* connections. * connections.
*/ */
static void static void
update_consensus_bootstrap_multiple_downloads(time_t now, update_consensus_bootstrap_multiple_downloads(time_t now,
const or_options_t *options, const or_options_t *options)
int we_are_bootstrapping)
{ {
const int usable_flavor = usable_consensus_flavor(); const int usable_flavor = usable_consensus_flavor();
@ -969,12 +937,6 @@ update_consensus_bootstrap_multiple_downloads(time_t now,
return; 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 /* Launch concurrent consensus download attempt(s) based on the mirror and
* authority schedules. Try the mirror first - this makes it slightly more * authority schedules. Try the mirror first - this makes it slightly more
* likely that we'll connect to the fallback first, and then end the * 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)) { if (!check_consensus_waiting_for_certs(usable_flavor, now, dls_f)) {
/* During bootstrap, DL_WANT_ANY_DIRSERVER means "use fallbacks". */ /* During bootstrap, DL_WANT_ANY_DIRSERVER means "use fallbacks". */
update_consensus_bootstrap_attempt_downloads(now, options, update_consensus_bootstrap_attempt_downloads(now, options, dls_f,
we_are_bootstrapping, dls_f,
DL_WANT_ANY_DIRSERVER); DL_WANT_ANY_DIRSERVER);
} }
} }
@ -1004,8 +965,7 @@ update_consensus_bootstrap_multiple_downloads(time_t now,
&consensus_bootstrap_dl_status[CONSENSUS_BOOTSTRAP_SOURCE_AUTHORITY]; &consensus_bootstrap_dl_status[CONSENSUS_BOOTSTRAP_SOURCE_AUTHORITY];
if (!check_consensus_waiting_for_certs(usable_flavor, now, dls_a)) { if (!check_consensus_waiting_for_certs(usable_flavor, now, dls_a)) {
update_consensus_bootstrap_attempt_downloads(now, options, update_consensus_bootstrap_attempt_downloads(now, options, dls_a,
we_are_bootstrapping, dls_a,
DL_WANT_AUTHORITY); DL_WANT_AUTHORITY);
} }
} }
@ -1273,16 +1233,30 @@ networkstatus_get_reasonably_live_consensus(time_t now, int flavor)
return NULL; return NULL;
} }
/** Check if we're bootstrapping a consensus download. This means that we are /** Check if we need to download a consensus during tor's bootstrap phase.
* only using the authorities and fallback directory mirrors to download the * If we have no consensus, or our consensus is unusably old, return 1.
* consensus flavour we'll use. */ * As soon as we have received a consensus, return 0, even if we don't have
int * enough certificates to validate it. */
networkstatus_consensus_is_boostrapping(time_t now) MOCK_IMPL(int,
networkstatus_consensus_is_bootstrapping,(time_t now))
{ {
/* If we don't have a consensus, we must still be bootstrapping */ /* If we have a validated, reasonably live consensus, we're not
return !networkstatus_get_reasonably_live_consensus( * bootstrapping a consensus at all. */
now, if (networkstatus_get_reasonably_live_consensus(
usable_consensus_flavor()); 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. /** 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. /** 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, * If we have fallbacks and don't want to fetch from the authorities,
* we can use them. */ * we can use them. */
int MOCK_IMPL(int,
networkstatus_consensus_can_use_extra_fallbacks(const or_options_t *options) 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 /* 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 * 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()))); > smartlist_len(router_get_trusted_dir_servers())));
} }
/* Check if there is more than 1 consensus connection retrieving the usable /* Is there a consensus fetch for flavor <b>resource</b> that's far
* consensus flavor. If so, return 1, if not, return 0. * enough along to be attached to a circuit? */
*
* 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(). */
int int
networkstatus_consensus_has_excess_connections(void) networkstatus_consensus_is_already_downloading(const char *resource)
{ {
const char *usable_resource = networkstatus_get_flavor_name( int answer = 0;
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;
if (consens_conn_usable_count > max_expected_consens_conn_usable_count) { /* First, get a list of all the dir conns that are fetching a consensus,
return 1; * 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? */ return answer;
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;
} }
/** Given two router status entries for the same router identity, return 1 if /** 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_live_consensus(time_t now);
networkstatus_t *networkstatus_get_reasonably_live_consensus(time_t now, networkstatus_t *networkstatus_get_reasonably_live_consensus(time_t now,
int flavor); 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( int networkstatus_consensus_can_use_multiple_directories(
const or_options_t *options); const or_options_t *options);
int networkstatus_consensus_can_use_extra_fallbacks( MOCK_DECL(int, networkstatus_consensus_can_use_extra_fallbacks,(
const or_options_t *options); const or_options_t *options));
int networkstatus_consensus_has_excess_connections(void); int networkstatus_consensus_is_already_downloading(const char *resource);
int networkstatus_consensus_is_downloading_usable_flavor(void);
#define NSSET_FROM_CACHE 1 #define NSSET_FROM_CACHE 1
#define NSSET_WAS_WAITING_FOR_CERTS 2 #define NSSET_WAS_WAITING_FOR_CERTS 2

View File

@ -2065,6 +2065,10 @@ typedef struct signed_descriptor_t {
time_t published_on; time_t published_on;
/** For routerdescs only: digest of the corresponding extrainfo. */ /** For routerdescs only: digest of the corresponding extrainfo. */
char extra_info_digest[DIGEST_LEN]; 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 /** For routerdescs only: Status of downloading the corresponding
* extrainfo. */ * extrainfo. */
download_status_t ei_dl_status; download_status_t ei_dl_status;
@ -2096,8 +2100,6 @@ typedef int16_t country_t;
/** Information about another onion router in the network. */ /** Information about another onion router in the network. */
typedef struct { typedef struct {
signed_descriptor_t cache_info; 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. */ char *nickname; /**< Human-readable OR name. */
uint32_t addr; /**< IPv4 address of OR, in host order. */ 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. */ crypto_pk_t *identity_pkey; /**< Public RSA key for signing. */
/** Public curve25519 key for onions */ /** Public curve25519 key for onions */
curve25519_public_key_t *onion_curve25519_pkey; 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; struct tor_cert_st *signing_key_cert;
/** What's the earliest expiration time on all the certs in this /** What's the earliest expiration time on all the certs in this
* routerinfo? */ * routerinfo? */
@ -2192,7 +2195,8 @@ typedef struct extrainfo_t {
uint8_t digest256[DIGEST256_LEN]; uint8_t digest256[DIGEST256_LEN];
/** The router's nickname. */ /** The router's nickname. */
char nickname[MAX_NICKNAME_LEN+1]; 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; struct tor_cert_st *signing_key_cert;
/** True iff we found the right key for this extra-info, verified the /** True iff we found the right key for this extra-info, verified the
* signature, and found it to be bad. */ * 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; return -1;
} }
ri->signing_key_cert = tor_cert_dup(get_master_signing_key_cert()); 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)); get_platform_str(platform, sizeof(platform));
ri->platform = tor_strdup(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)); strlcpy(ei->nickname, get_options()->Nickname, sizeof(ei->nickname));
ei->cache_info.published_on = ri->cache_info.published_on; ei->cache_info.published_on = ri->cache_info.published_on;
ei->signing_key_cert = tor_cert_dup(get_master_signing_key_cert()); 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, memcpy(ei->cache_info.identity_digest, ri->cache_info.identity_digest,
DIGEST_LEN); DIGEST_LEN);
if (extrainfo_dump_to_string(&ei->cache_info.signed_descriptor_body, 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, memcpy(ri->cache_info.extra_info_digest,
ei->cache_info.signed_descriptor_digest, ei->cache_info.signed_descriptor_digest,
DIGEST_LEN); DIGEST_LEN);
memcpy(ri->extra_info_digest256, memcpy(ri->cache_info.extra_info_digest256,
ei->digest256, ei->digest256,
DIGEST256_LEN); DIGEST256_LEN);
} else { } else {
@ -2195,7 +2198,8 @@ router_build_fresh_descriptor(routerinfo_t **r, extrainfo_t **e)
ri->cache_info.signed_descriptor_digest); ri->cache_info.signed_descriptor_digest);
if (ei) { 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; *r = ri;
@ -2669,9 +2673,9 @@ router_dump_router_to_string(routerinfo_t *router,
char extra_info_digest[HEX_DIGEST_LEN+1]; char extra_info_digest[HEX_DIGEST_LEN+1];
base16_encode(extra_info_digest, sizeof(extra_info_digest), base16_encode(extra_info_digest, sizeof(extra_info_digest),
router->cache_info.extra_info_digest, DIGEST_LEN); 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]; 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", tor_asprintf(&extra_info_line, "extra-info-digest %s %s\n",
extra_info_digest, d256_64); extra_info_digest, d256_64);
} else { } else {

View File

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

View File

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

View File

@ -11,6 +11,7 @@
#include "connection.h" #include "connection.h"
#include "main.h" #include "main.h"
#include "microdesc.h"
#include "networkstatus.h" #include "networkstatus.h"
#include "rendcache.h" #include "rendcache.h"
#include "directory.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_STATE_SUCCESSFUL (DIR_CONN_STATE_CLIENT_FINISHED)
#define TEST_CONN_RSRC_2 (networkstatus_get_flavor_name(FLAV_NS)) #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 #define TEST_CONN_FD_INIT 50
static int mock_connection_connect_sockaddr_called = 0; 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); tor_addr_make_null(addr, TEST_CONN_FAMILY);
} }
static void * static connection_t *
test_conn_get_basic_setup(const struct testcase_t *tc) test_conn_get_connection(uint8_t state, uint8_t type, uint8_t purpose)
{ {
connection_t *conn = NULL; connection_t *conn = NULL;
tor_addr_t addr; tor_addr_t addr;
int socket_err = 0; int socket_err = 0;
int in_progress = 0; int in_progress = 0;
(void)tc;
MOCK(connection_connect_sockaddr, MOCK(connection_connect_sockaddr,
mock_connection_connect_sockaddr); mock_connection_connect_sockaddr);
init_connection_lists(); init_connection_lists();
conn = connection_new(TEST_CONN_TYPE, TEST_CONN_FAMILY); conn = connection_new(type, TEST_CONN_FAMILY);
tt_assert(conn); tt_assert(conn);
test_conn_lookup_addr_helper(TEST_CONN_ADDRESS, TEST_CONN_FAMILY, &addr); test_conn_lookup_addr_helper(TEST_CONN_ADDRESS, TEST_CONN_FAMILY, &addr);
tt_assert(!tor_addr_is_null(&addr)); tt_assert(!tor_addr_is_null(&addr));
/* XXXX - connection_connect doesn't set these, should it? */
tor_addr_copy_tight(&conn->addr, &addr); tor_addr_copy_tight(&conn->addr, &addr);
conn->port = TEST_CONN_PORT; conn->port = TEST_CONN_PORT;
mock_connection_connect_sockaddr_called = 0; 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); tt_assert(in_progress == 0 || in_progress == 1);
/* fake some of the attributes so the connection looks OK */ /* fake some of the attributes so the connection looks OK */
conn->state = TEST_CONN_STATE; conn->state = state;
conn->purpose = TEST_CONN_BASIC_PURPOSE; conn->purpose = purpose;
assert_connection_ok(conn, time(NULL)); assert_connection_ok(conn, time(NULL));
UNMOCK(connection_connect_sockaddr); UNMOCK(connection_connect_sockaddr);
@ -151,12 +154,17 @@ test_conn_get_basic_setup(const struct testcase_t *tc)
/* On failure */ /* On failure */
done: done:
UNMOCK(connection_connect_sockaddr); UNMOCK(connection_connect_sockaddr);
test_conn_get_basic_teardown(tc, conn);
/* Returning NULL causes the unit test to fail */
return NULL; 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 static int
test_conn_get_basic_teardown(const struct testcase_t *tc, void *arg) 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_close_immediate(conn->linked_conn);
connection_mark_for_close(conn->linked_conn); connection_mark_for_close(conn->linked_conn);
} }
conn->linked_conn->linked_conn = NULL;
connection_free(conn->linked_conn); close_closeable_connections();
conn->linked_conn = NULL;
} }
/* We didn't set the events up properly, so we can't use event_del() in /* 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) test_conn_get_rend_setup(const struct testcase_t *tc)
{ {
dir_connection_t *conn = DOWNCAST(dir_connection_t, 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); tt_assert(conn);
assert_connection_ok(&conn->base_, time(NULL)); 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, TEST_CONN_REND_ADDR,
REND_SERVICE_ID_LEN_BASE32+1); REND_SERVICE_ID_LEN_BASE32+1);
conn->rend_data->hsdirs_fp = smartlist_new(); conn->rend_data->hsdirs_fp = smartlist_new();
conn->base_.purpose = TEST_CONN_REND_PURPOSE;
assert_connection_ok(&conn->base_, time(NULL)); assert_connection_ok(&conn->base_, time(NULL));
return conn; return conn;
@ -266,42 +275,64 @@ test_conn_get_rend_teardown(const struct testcase_t *tc, void *arg)
return rv; return rv;
} }
static void * static dir_connection_t *
test_conn_get_rsrc_setup(const struct testcase_t *tc) test_conn_download_status_add_a_connection(const char *resource)
{ {
dir_connection_t *conn = DOWNCAST(dir_connection_t, 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); tt_assert(conn);
assert_connection_ok(&conn->base_, time(NULL)); assert_connection_ok(&conn->base_, time(NULL));
/* TODO: use the canonical function to do this - maybe? */ /* Replace the existing resource with the one we want */
conn->requested_resource = tor_strdup(TEST_CONN_RSRC); if (resource) {
conn->base_.purpose = TEST_CONN_RSRC_PURPOSE; 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; return conn;
/* On failure */
done: done:
test_conn_get_rend_teardown(tc, conn); test_conn_get_rsrc_teardown(NULL, conn);
/* Returning NULL causes the unit test to fail */
return NULL; 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 static int
test_conn_get_rsrc_teardown(const struct testcase_t *tc, void *arg) test_conn_get_rsrc_teardown(const struct testcase_t *tc, void *arg)
{ {
dir_connection_t *conn = DOWNCAST(dir_connection_t, arg);
int rv = 0; int rv = 0;
connection_t *conn = (connection_t *)arg;
tt_assert(conn); 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 */ if (conn->type == CONN_TYPE_DIR) {
conn->base_.state = TEST_CONN_RSRC_STATE_SUCCESSFUL; 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 */ /* connection_free_() cleans up requested_resource */
rv = test_conn_get_basic_teardown(tc, arg); rv = test_conn_get_basic_teardown(tc, conn);
done: done:
return rv; return rv;
} }
@ -336,14 +367,30 @@ test_conn_download_status_teardown(const struct testcase_t *tc, void *arg)
return rv; return rv;
} }
static dir_connection_t * /* Like connection_ap_make_link(), but does much less */
test_conn_download_status_add_a_connection(void) static connection_t *
test_conn_get_linked_connection(connection_t *l_conn, uint8_t state)
{ {
dir_connection_t *conn = DOWNCAST(dir_connection_t, tt_assert(l_conn);
test_conn_get_rsrc_setup(NULL)); 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); 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; return conn;
@ -524,44 +571,6 @@ test_conn_get_rsrc(void *arg)
tt_assert(conn); tt_assert(conn);
assert_connection_ok(&conn->base_, time(NULL)); 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( sl_is_conn_assert(connection_dir_list_by_purpose_and_resource(
conn->base_.purpose, conn->base_.purpose,
conn->requested_resource), conn->requested_resource),
@ -641,107 +650,190 @@ test_conn_get_rsrc(void *arg)
static void static void
test_conn_download_status(void *arg) test_conn_download_status(void *arg)
{ {
(void)arg;
dir_connection_t *conn = NULL; dir_connection_t *conn = NULL;
dir_connection_t *conn2 = NULL; dir_connection_t *conn2 = NULL;
dir_connection_t *conn3 = 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 */ consensus_flavor_t usable_flavor = (consensus_flavor_t)arg;
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);
/* one connection, no excess, not downloading */ /* The "other flavor" trick only works if there are two flavors */
conn = test_conn_download_status_add_a_connection(); tor_assert(N_CONSENSUS_FLAVORS == 2);
tt_assert(networkstatus_consensus_has_excess_connections() == 0); consensus_flavor_t other_flavor = ((usable_flavor == FLAV_NS)
tt_assert(networkstatus_consensus_is_downloading_usable_flavor() == 0); ? FLAV_MICRODESC
tt_assert(connection_dir_avoid_extra_connection_for_purpose( : FLAV_NS);
TEST_CONN_RSRC_PURPOSE) == 0); 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; conn->base_.state = TEST_CONN_DL_STATE;
tt_assert(networkstatus_consensus_has_excess_connections() == 0); tt_assert(networkstatus_consensus_is_already_downloading(res) == 0);
tt_assert(networkstatus_consensus_is_downloading_usable_flavor() == 1); tt_assert(networkstatus_consensus_is_already_downloading(other_res) == 0);
tt_assert(connection_dir_avoid_extra_connection_for_purpose( tt_assert(connection_dir_count_by_purpose_and_resource(
TEST_CONN_RSRC_PURPOSE) == 1); 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; conn->base_.state = TEST_CONN_STATE;
/* two connections, excess, but not downloading */ /* more connections, all not downloading */
conn2 = test_conn_download_status_add_a_connection(); conn3 = test_conn_download_status_add_a_connection(res);
tt_assert(networkstatus_consensus_has_excess_connections() == 1); tt_assert(networkstatus_consensus_is_already_downloading(res) == 0);
tt_assert(networkstatus_consensus_is_downloading_usable_flavor() == 0); tt_assert(networkstatus_consensus_is_already_downloading(other_res) == 0);
tt_assert(connection_dir_avoid_extra_connection_for_purpose( tt_assert(connection_dir_count_by_purpose_and_resource(
TEST_CONN_RSRC_PURPOSE) == 0); 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; conn2->base_.state = TEST_CONN_DL_STATE;
tt_assert(networkstatus_consensus_has_excess_connections() == 1); ap_conn2 = test_conn_get_linked_connection(TO_CONN(conn2),
tt_assert(networkstatus_consensus_is_downloading_usable_flavor() == 1); TEST_CONN_ATTACHED_STATE);
tt_assert(connection_dir_avoid_extra_connection_for_purpose( tt_assert(networkstatus_consensus_is_already_downloading(res) == 1);
TEST_CONN_RSRC_PURPOSE) == 1); tt_assert(networkstatus_consensus_is_already_downloading(other_res) == 0);
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)));
tt_assert(connection_dir_count_by_purpose_and_resource( tt_assert(connection_dir_count_by_purpose_and_resource(
TEST_CONN_RSRC_PURPOSE, TEST_CONN_RSRC_PURPOSE,
TEST_CONN_RSRC) == 3); res) == 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);
tt_assert(connection_dir_count_by_purpose_and_resource( tt_assert(connection_dir_count_by_purpose_and_resource(
TEST_CONN_RSRC_PURPOSE, TEST_CONN_RSRC_PURPOSE,
TEST_CONN_RSRC) == 2); other_res) == 0);
tt_assert(connection_dir_avoid_extra_connection_for_purpose( conn->base_.state = TEST_CONN_STATE;
TEST_CONN_RSRC_PURPOSE) == 1);
/* now try closing one that is already closed - nothing happens */ /* more connections, a different one downloading */
tt_assert(connection_dir_close_consensus_conn_if_extra(conn) == 0); 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( tt_assert(connection_dir_count_by_purpose_and_resource(
TEST_CONN_RSRC_PURPOSE, TEST_CONN_RSRC_PURPOSE,
TEST_CONN_RSRC) == 2); res) == 3);
tt_assert(connection_dir_avoid_extra_connection_for_purpose( tt_assert(connection_dir_count_by_purpose_and_resource(
TEST_CONN_RSRC_PURPOSE) == 1); TEST_CONN_RSRC_PURPOSE,
other_res) == 0);
/* now try closing one that is downloading - it stays open */ /* a connection for the other flavor (could happen if a client is set to
tt_assert(connection_dir_close_consensus_conn_if_extra(conn2) == 0); * 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( tt_assert(connection_dir_count_by_purpose_and_resource(
TEST_CONN_RSRC_PURPOSE, TEST_CONN_RSRC_PURPOSE,
TEST_CONN_RSRC) == 2); res) == 3);
tt_assert(connection_dir_avoid_extra_connection_for_purpose( tt_assert(connection_dir_count_by_purpose_and_resource(
TEST_CONN_RSRC_PURPOSE) == 1); TEST_CONN_RSRC_PURPOSE,
other_res) == 1);
/* now try closing all excess connections */ /* a connection for the other flavor (could happen if a client is set to
connection_dir_close_extra_consensus_conns(); * 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( tt_assert(connection_dir_count_by_purpose_and_resource(
TEST_CONN_RSRC_PURPOSE, TEST_CONN_RSRC_PURPOSE,
TEST_CONN_RSRC) == 1); res) == 3);
tt_assert(connection_dir_avoid_extra_connection_for_purpose( tt_assert(connection_dir_count_by_purpose_and_resource(
TEST_CONN_RSRC_PURPOSE) == 1); TEST_CONN_RSRC_PURPOSE,
other_res) == 1);
conn4->base_.state = TEST_CONN_STATE;
done: done:
/* the teardown function removes all the connections */; /* the teardown function removes all the connections */;
@ -750,11 +842,18 @@ test_conn_download_status(void *arg)
#define CONNECTION_TESTCASE(name, fork, setup) \ #define CONNECTION_TESTCASE(name, fork, setup) \
{ #name, test_conn_##name, fork, &setup, NULL } { #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[] = { struct testcase_t connection_tests[] = {
CONNECTION_TESTCASE(get_basic, TT_FORK, test_conn_get_basic_st), 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_rend, TT_FORK, test_conn_get_rend_st),
CONNECTION_TESTCASE(get_rsrc, TT_FORK, test_conn_get_rsrc_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), //CONNECTION_TESTCASE(func_suffix, TT_FORK, setup_func_pair),
END_OF_TESTCASES END_OF_TESTCASES
}; };

View File

@ -4041,12 +4041,56 @@ test_dir_choose_compression_level(void* data)
done: ; 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 static void
test_dir_find_dl_schedule(void* data) 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; download_status_t dls;
smartlist_t server, client, server_cons, client_cons, bridge; smartlist_t server, client, server_cons, client_cons;
(void)data; 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)); mock_options = malloc(sizeof(or_options_t));
reset_options(mock_options, &mock_get_options_calls); 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->TestingClientDownloadSchedule = &client;
mock_options->TestingServerConsensusDownloadSchedule = &server_cons; mock_options->TestingServerConsensusDownloadSchedule = &server_cons;
mock_options->TestingClientConsensusDownloadSchedule = &client_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; mock_options->TestingBridgeDownloadSchedule = &bridge;
dls.schedule = DL_SCHED_GENERIC; dls.schedule = DL_SCHED_GENERIC;
/* client */
mock_options->ClientOnly = 1; mock_options->ClientOnly = 1;
tt_ptr_op(find_dl_schedule(&dls, mock_options), OP_EQ, &client); tt_ptr_op(find_dl_schedule(&dls, mock_options), OP_EQ, &client);
mock_options->ClientOnly = 0; mock_options->ClientOnly = 0;
/* dir mode */
mock_options->DirPort_set = 1; mock_options->DirPort_set = 1;
mock_options->ORPort_set = 1;
mock_options->DirCache = 1; mock_options->DirCache = 1;
tt_ptr_op(find_dl_schedule(&dls, mock_options), OP_EQ, &server); tt_ptr_op(find_dl_schedule(&dls, mock_options), OP_EQ, &server);
mock_options->DirPort_set = 0;
#if 0
dls.schedule = DL_SCHED_CONSENSUS;
mock_options->ClientOnly = 1;
mock_options->DirCache = 0; mock_options->DirCache = 0;
tt_ptr_op(find_dl_schedule(&dls, mock_options), OP_EQ, &client_cons);
mock_options->ClientOnly = 0; dls.schedule = DL_SCHED_CONSENSUS;
mock_options->DirCache = 1; /* public server mode */
mock_options->ORPort_set = 1;
tt_ptr_op(find_dl_schedule(&dls, mock_options), OP_EQ, &server_cons); 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; dls.schedule = DL_SCHED_BRIDGE;
/* client */
mock_options->ClientOnly = 1; mock_options->ClientOnly = 1;
tt_ptr_op(find_dl_schedule(&dls, mock_options), OP_EQ, &bridge); 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: done:
UNMOCK(networkstatus_consensus_is_bootstrapping);
UNMOCK(networkstatus_consensus_can_use_extra_fallbacks);
UNMOCK(get_options); 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 } { #name, test_dir_ ## name , TT_FORK, NULL, NULL }
#define DIR(name,flags) \ #define DIR(name,flags) \
{ #name, test_dir_##name, (flags), NULL, NULL } { #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[] = { struct testcase_t dir_tests[] = {
DIR_LEGACY(nicknames), DIR_LEGACY(nicknames),
DIR_LEGACY(formats), 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_not_init_request_to_dir_auths_without_v3_info, 0),
DIR(should_init_request_to_dir_auths, 0), DIR(should_init_request_to_dir_auths, 0),
DIR(choose_compression_level, 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 END_OF_TESTCASES
}; };

View File

@ -15,6 +15,7 @@
#include "container.h" #include "container.h"
#include "directory.h" #include "directory.h"
#include "dirvote.h" #include "dirvote.h"
#include "microdesc.h"
#include "networkstatus.h" #include "networkstatus.h"
#include "nodelist.h" #include "nodelist.h"
#include "policies.h" #include "policies.h"
@ -190,6 +191,14 @@ construct_consensus(char **consensus_text_md)
crypto_pk_free(sign_skey_leg); 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 static void
test_router_pick_directory_server_impl(void *arg) test_router_pick_directory_server_impl(void *arg)
{ {
@ -209,6 +218,22 @@ test_router_pick_directory_server_impl(void *arg)
(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 */ /* No consensus available, fail early */
rs = router_pick_directory_server_impl(V3_DIRINFO, (const int) 0, NULL); rs = router_pick_directory_server_impl(V3_DIRINFO, (const int) 0, NULL);
tt_assert(rs == 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_int_op(smartlist_len(con_md->routerstatus_list), ==, 3);
tt_assert(!networkstatus_set_current_consensus_from_ns(con_md, tt_assert(!networkstatus_set_current_consensus_from_ns(con_md,
"microdesc")); "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_set_consensus(con_md);
nodelist_assert_ok(); nodelist_assert_ok();
@ -362,6 +409,7 @@ test_router_pick_directory_server_impl(void *arg)
node_router1->rs->last_dir_503_at = 0; node_router1->rs->last_dir_503_at = 0;
done: done:
UNMOCK(usable_consensus_flavor);
if (router1_id) if (router1_id)
tor_free(router1_id); tor_free(router1_id);
if (router2_id) if (router2_id)