Unit tests for 11243: loading ri, ei, mds from lists

These tests make sure that entries are actually marked
undownloadable as appropriate.
This commit is contained in:
Nick Mathewson 2014-10-07 09:20:44 -04:00
parent 24dfbfda1d
commit 3efeb711f1
7 changed files with 361 additions and 28 deletions

View File

@ -595,10 +595,10 @@ networkstatus_vote_find_entry_idx(networkstatus_t *ns,
/** As router_get_consensus_status_by_descriptor_digest, but does not return
* a const pointer. */
routerstatus_t *
router_get_mutable_consensus_status_by_descriptor_digest(
MOCK_IMPL(routerstatus_t *,
router_get_mutable_consensus_status_by_descriptor_digest,(
networkstatus_t *consensus,
const char *digest)
const char *digest))
{
if (!consensus)
consensus = current_consensus;
@ -628,8 +628,8 @@ router_get_consensus_status_by_descriptor_digest(networkstatus_t *consensus,
/** Given the digest of a router descriptor, return its current download
* status, or NULL if the digest is unrecognized. */
download_status_t *
router_get_dl_status_by_descriptor_digest(const char *d)
MOCK_IMPL(download_status_t *,
router_get_dl_status_by_descriptor_digest,(const char *d))
{
routerstatus_t *rs;
if (!current_ns_consensus)
@ -995,8 +995,8 @@ networkstatus_get_latest_consensus(void)
/** Return the latest consensus we have whose flavor matches <b>f</b>, or NULL
* if we don't have one. */
networkstatus_t *
networkstatus_get_latest_consensus_by_flavor(consensus_flavor_t f)
MOCK_IMPL(networkstatus_t *,
networkstatus_get_latest_consensus_by_flavor,(consensus_flavor_t f))
{
if (f == FLAV_NS)
return current_ns_consensus;

View File

@ -12,6 +12,8 @@
#ifndef TOR_NETWORKSTATUS_H
#define TOR_NETWORKSTATUS_H
#include "testsupport.h"
void networkstatus_reset_warnings(void);
void networkstatus_reset_download_failures(void);
int router_reload_consensus_networkstatus(void);
@ -35,16 +37,19 @@ routerstatus_t *networkstatus_vote_find_mutable_entry(networkstatus_t *ns,
const char *digest);
int networkstatus_vote_find_entry_idx(networkstatus_t *ns,
const char *digest, int *found_out);
download_status_t *router_get_dl_status_by_descriptor_digest(const char *d);
MOCK_DECL(download_status_t *,router_get_dl_status_by_descriptor_digest,
(const char *d));
const routerstatus_t *router_get_consensus_status_by_id(const char *digest);
routerstatus_t *router_get_mutable_consensus_status_by_id(
const char *digest);
const routerstatus_t *router_get_consensus_status_by_descriptor_digest(
networkstatus_t *consensus,
const char *digest);
routerstatus_t *router_get_mutable_consensus_status_by_descriptor_digest(
networkstatus_t *consensus,
const char *digest);
MOCK_DECL(routerstatus_t *,
router_get_mutable_consensus_status_by_descriptor_digest,
(networkstatus_t *consensus, const char *digest));
const routerstatus_t *router_get_consensus_status_by_nickname(
const char *nickname,
int warn_if_unnamed);
@ -60,8 +65,8 @@ int consensus_is_waiting_for_certs(void);
int client_would_use_router(const routerstatus_t *rs, time_t now,
const or_options_t *options);
networkstatus_t *networkstatus_get_latest_consensus(void);
networkstatus_t *networkstatus_get_latest_consensus_by_flavor(
consensus_flavor_t f);
MOCK_DECL(networkstatus_t *,networkstatus_get_latest_consensus_by_flavor,
(consensus_flavor_t f));
networkstatus_t *networkstatus_get_live_consensus(time_t now);
networkstatus_t *networkstatus_get_reasonably_live_consensus(time_t now,
int flavor);

View File

@ -2615,8 +2615,8 @@ router_get_by_descriptor_digest(const char *digest)
/** Return the signed descriptor for the router in our routerlist whose
* 20-byte extra-info digest is <b>digest</b>. Return NULL if no such router
* is known. */
signed_descriptor_t *
router_get_by_extrainfo_digest(const char *digest)
MOCK_IMPL(signed_descriptor_t *,
router_get_by_extrainfo_digest,(const char *digest))
{
tor_assert(digest);
@ -2939,8 +2939,8 @@ routerlist_insert(routerlist_t *rl, routerinfo_t *ri)
/** Adds the extrainfo_t <b>ei</b> to the routerlist <b>rl</b>, if there is a
* corresponding router in rl-\>routers or rl-\>old_routers. Return true iff
* we actually inserted <b>ei</b>. Free <b>ei</b> if it isn't inserted. */
static int
extrainfo_insert(routerlist_t *rl, extrainfo_t *ei)
MOCK_IMPL(STATIC int,
extrainfo_insert,(routerlist_t *rl, extrainfo_t *ei))
{
int r = 0;
routerinfo_t *ri = rimap_get(rl->identity_map,

View File

@ -82,7 +82,8 @@ int hexdigest_to_digest(const char *hexdigest, char *digest);
const routerinfo_t *router_get_by_id_digest(const char *digest);
routerinfo_t *router_get_mutable_by_digest(const char *digest);
signed_descriptor_t *router_get_by_descriptor_digest(const char *digest);
signed_descriptor_t *router_get_by_extrainfo_digest(const char *digest);
MOCK_DECL(signed_descriptor_t *,router_get_by_extrainfo_digest,
(const char *digest));
signed_descriptor_t *extrainfo_get_by_descriptor_digest(const char *digest);
const char *signed_descriptor_get_body(const signed_descriptor_t *desc);
const char *signed_descriptor_get_annotations(const signed_descriptor_t *desc);
@ -215,6 +216,8 @@ STATIC void scale_array_elements_to_u64(u64_dbl_t *entries, int n_entries,
MOCK_DECL(int, router_descriptor_is_older_than, (const routerinfo_t *router,
int seconds));
MOCK_DECL(STATIC int, extrainfo_insert,(routerlist_t *rl, extrainfo_t *ei));
#endif
#endif

View File

@ -133,23 +133,24 @@ static const char EX_EI_BAD_NICKNAME_KEY[] =
"/UBWNSyXCFDMqnddb/LZ8+VgttmxfYkpeRzSSmDijN3RbOvYJhhBAgMBAAE=\n"
"-----END RSA PUBLIC KEY-----\n";
static const char EX_EI_BAD_TOKENS[] =
"extra-info\n"
const char EX_EI_BAD_TOKENS[] =
"extra-info bob 6F314FB01A31162BD5E473D4977AC570DC5B86BB\n"
"published 2014-10-05 20:07:00\n"
"published 2014-10-05 20:07:00\n"
"router-signature\n"
"-----BEGIN SIGNATURE-----\n"
"e0b8fW9SkB81WwpkqR+/oLvVnNMpW4VwBKlNAnsPdInnNYySFCeILyOmb8A5j/D3\n"
"ZRbfKFgXrApya4ViGeh+1QHz+FM8nWaK0+5JE6cNw+DNiQWsUjrvq2bHoZi/Lv+k\n"
"RFkpbb0+IPdub9Jj3qn6cZBVSChD1aeCO9RkchV0744=\n"
"lhRIafrkKoQmnUoBLiq4XC8XKXrleGJZ5vefkLcgjOJ5IffsvVdIA7Vqq/ISbPrG\n"
"b/Zs0sJNL6naHPxJBglgHJqksSyiYHaeOetXg2Rb+vZ1v2S5BrVgk1nPMDhyIzqc\n"
"zU7eCxFf/1sXKtWlEKxGdX4LmVfnIln5aI31Bc4xRrE=\n"
"-----END SIGNATURE-----\n"
;
static const char EX_EI_BAD_TOKENS_FP[] = "1CE7F867C744F0CE635146D82C0A0CC4B3408E0A";
static const char EX_EI_BAD_TOKENS_KEY[] =
const char EX_EI_BAD_TOKENS_FP[] = "6F314FB01A31162BD5E473D4977AC570DC5B86BB";
const char EX_EI_BAD_TOKENS_KEY[] =
"-----BEGIN RSA PUBLIC KEY-----\n"
"MIGJAoGBALfrYfnIA7gTGwEC5tPqqTuSnMiINq/hPyMWXOFUEKEBRmctxba9fjiy\n"
"JDstxC4ZBpdHc3+uuFn6krGgBVzFCKvBkBHUQXgFmjlANzdcLrG7J78K6gXuPeeq\n"
"MbptD7thkuIdkPdVWMko7BrmBEr30EphgrKQqTWjrtBQj+CZ8Sr1AgMBAAE=\n"
"MIGJAoGBAL7Z8tz45Tb4tnEFS2sAyjubBV/giSfZdmXRkDV8Jo4xqWqhWFJn7+zN\n"
"AXBWBThGeVH2WXrpz5seNJXgZJPxMTMsrnSCGcRXZw0Npti2MkLuQ6+prZa+OPwE\n"
"OyC6jivtAaY/o9iYQjDC2avLXD3N4LvoygyF418KnNcjbzuFygffAgMBAAE=\n"
"-----END RSA PUBLIC KEY-----\n";
static const char EX_EI_BAD_START[] =

View File

@ -567,6 +567,231 @@ test_dir_parse_router_list(void *arg)
#undef ADD
}
static download_status_t dls_minimal;
static download_status_t dls_maximal;
static download_status_t dls_bad_fingerprint;
static download_status_t dls_bad_sig2;
static download_status_t dls_bad_ports;
static download_status_t dls_bad_tokens;
static int mock_router_get_dl_status_unrecognized = 0;
static int mock_router_get_dl_status_calls = 0;
static download_status_t *
mock_router_get_dl_status(const char *d)
{
++mock_router_get_dl_status_calls;
char hex[HEX_DIGEST_LEN+1];
base16_encode(hex, sizeof(hex), d, DIGEST_LEN);
if (!strcmp(hex, "3E31D19A69EB719C00B02EC60D13356E3F7A3452")) {
return &dls_minimal;
} else if (!strcmp(hex, "581D8A368A0FA854ECDBFAB841D88B3F1B004038")) {
return &dls_maximal;
} else if (!strcmp(hex, "2578AE227C6116CDE29B3F0E95709B9872DEE5F1")) {
return &dls_bad_fingerprint;
} else if (!strcmp(hex, "16D387D3A58F7DB3CF46638F8D0B90C45C7D769C")) {
return &dls_bad_sig2;
} else if (!strcmp(hex, "AB9EEAA95E7D45740185B4E519C76EAD756277A9")) {
return &dls_bad_ports;
} else if (!strcmp(hex, "A0CC2CEFAD59DBF19F468BFEE60E0868C804B422")) {
return &dls_bad_tokens;
} else {
++mock_router_get_dl_status_unrecognized;
return NULL;
}
}
static void
test_dir_load_routers(void *arg)
{
(void) arg;
smartlist_t *chunks = smartlist_new();
smartlist_t *wanted = smartlist_new();
char buf[DIGEST_LEN];
char *mem_op_hex_tmp = NULL;
#define ADD(str) \
do { \
tt_int_op(0,==,router_get_router_hash(str, strlen(str), buf)); \
smartlist_add(wanted, tor_strdup(hex_str(buf, DIGEST_LEN))); \
} while (0)
MOCK(router_get_dl_status_by_descriptor_digest, mock_router_get_dl_status);
smartlist_add(chunks, tor_strdup(EX_RI_MINIMAL));
smartlist_add(chunks, tor_strdup(EX_RI_BAD_FINGERPRINT));
smartlist_add(chunks, tor_strdup(EX_RI_BAD_SIG2));
smartlist_add(chunks, tor_strdup(EX_RI_MAXIMAL));
smartlist_add(chunks, tor_strdup(EX_RI_BAD_PORTS));
smartlist_add(chunks, tor_strdup(EX_RI_BAD_TOKENS));
/* not ADDing MINIMIAL */
ADD(EX_RI_MAXIMAL);
ADD(EX_RI_BAD_FINGERPRINT);
ADD(EX_RI_BAD_SIG2);
/* Not ADDing BAD_PORTS */
ADD(EX_RI_BAD_TOKENS);
char *list = smartlist_join_strings(chunks, "", 0, NULL);
tt_int_op(1, ==,
router_load_routers_from_string(list, NULL, SAVED_IN_JOURNAL,
wanted, 1, NULL));
/* The "maximal" router was added. */
/* "minimal" was not. */
tt_int_op(smartlist_len(router_get_routerlist()->routers),==,1);
routerinfo_t *r = smartlist_get(router_get_routerlist()->routers, 0);
test_memeq_hex(r->cache_info.signed_descriptor_digest,
"581D8A368A0FA854ECDBFAB841D88B3F1B004038");
tt_int_op(dls_minimal.n_download_failures, ==, 0);
tt_int_op(dls_maximal.n_download_failures, ==, 0);
/* "Bad fingerprint" and "Bad tokens" should have gotten marked
* non-retriable. */
tt_want_int_op(mock_router_get_dl_status_calls, ==, 2);
tt_want_int_op(mock_router_get_dl_status_unrecognized, ==, 0);
tt_int_op(dls_bad_fingerprint.n_download_failures, ==, 255);
tt_int_op(dls_bad_tokens.n_download_failures, ==, 255);
/* bad_sig2 and bad ports" are retriable -- one since only the signature
* was bad, and one because we didn't ask for it. */
tt_int_op(dls_bad_sig2.n_download_failures, ==, 0);
tt_int_op(dls_bad_ports.n_download_failures, ==, 0);
/* Wanted still contains "BAD_SIG2" */
tt_int_op(smartlist_len(wanted), ==, 1);
tt_str_op(smartlist_get(wanted, 0), ==,
"E0A3753CEFD54128EAB239F294954121DB23D2EF");
#undef ADD
done:
tor_free(mem_op_hex_tmp);
UNMOCK(router_get_dl_status_by_descriptor_digest);
SMARTLIST_FOREACH(chunks, char *, cp, tor_free(cp));
smartlist_free(chunks);
SMARTLIST_FOREACH(wanted, char *, cp, tor_free(cp));
smartlist_free(wanted);
}
static int mock_get_by_ei_dd_calls = 0;
static int mock_get_by_ei_dd_unrecognized = 0;
static signed_descriptor_t sd_ei_minimal;
static signed_descriptor_t sd_ei_bad_nickname;
static signed_descriptor_t sd_ei_maximal;
static signed_descriptor_t sd_ei_bad_tokens;
static signed_descriptor_t sd_ei_bad_sig2;
static signed_descriptor_t *
mock_get_by_ei_desc_digest(const char *d)
{
++mock_get_by_ei_dd_calls;
char hex[HEX_DIGEST_LEN+1];
base16_encode(hex, sizeof(hex), d, DIGEST_LEN);
if (!strcmp(hex, "11E0EDF526950739F7769810FCACAB8C882FAEEE")) {
return &sd_ei_minimal;
} else if (!strcmp(hex, "47803B02A0E70E9E8BDA226CB1D74DE354D67DFF")) {
return &sd_ei_maximal;
} else if (!strcmp(hex, "D5DF4AA62EE9FFC9543D41150C9864908E0390AF")) {
return &sd_ei_bad_nickname;
} else if (!strcmp(hex, "16D387D3A58F7DB3CF46638F8D0B90C45C7D769C")) {
return &sd_ei_bad_sig2;
} else if (!strcmp(hex, "9D90F8C42955BBC57D54FB05E54A3F083AF42E8B")) {
return &sd_ei_bad_tokens;
} else {
++mock_get_by_ei_dd_unrecognized;
return NULL;
}
}
static smartlist_t *mock_ei_insert_list = NULL;
static int
mock_ei_insert(routerlist_t *rl, extrainfo_t *ei)
{
(void) rl;
smartlist_add(mock_ei_insert_list, ei);
return 1;
}
static void
test_dir_load_extrainfo(void *arg)
{
(void) arg;
smartlist_t *chunks = smartlist_new();
smartlist_t *wanted = smartlist_new();
char buf[DIGEST_LEN];
char *mem_op_hex_tmp = NULL;
#define ADD(str) \
do { \
tt_int_op(0,==,router_get_extrainfo_hash(str, strlen(str), buf)); \
smartlist_add(wanted, tor_strdup(hex_str(buf, DIGEST_LEN))); \
} while (0)
mock_ei_insert_list = smartlist_new();
MOCK(router_get_by_extrainfo_digest, mock_get_by_ei_desc_digest);
MOCK(extrainfo_insert, mock_ei_insert);
smartlist_add(chunks, tor_strdup(EX_EI_MINIMAL));
smartlist_add(chunks, tor_strdup(EX_EI_BAD_NICKNAME));
smartlist_add(chunks, tor_strdup(EX_EI_MAXIMAL));
smartlist_add(chunks, tor_strdup(EX_EI_BAD_PUBLISHED));
smartlist_add(chunks, tor_strdup(EX_EI_BAD_TOKENS));
/* not ADDing MINIMIAL */
ADD(EX_EI_MAXIMAL);
ADD(EX_EI_BAD_NICKNAME);
/* Not ADDing BAD_PUBLISHED */
ADD(EX_EI_BAD_TOKENS);
ADD(EX_EI_BAD_SIG2);
char *list = smartlist_join_strings(chunks, "", 0, NULL);
router_load_extrainfo_from_string(list, NULL, SAVED_IN_JOURNAL, wanted, 1);
/* The "maximal" router was added. */
/* "minimal" was also added, even though we didn't ask for it, since
* that's what we do with extrainfos. */
tt_int_op(smartlist_len(mock_ei_insert_list),==,2);
extrainfo_t *e = smartlist_get(mock_ei_insert_list, 0);
test_memeq_hex(e->cache_info.signed_descriptor_digest,
"11E0EDF526950739F7769810FCACAB8C882FAEEE");
e = smartlist_get(mock_ei_insert_list, 1);
test_memeq_hex(e->cache_info.signed_descriptor_digest,
"47803B02A0E70E9E8BDA226CB1D74DE354D67DFF");
tt_int_op(dls_minimal.n_download_failures, ==, 0);
tt_int_op(dls_maximal.n_download_failures, ==, 0);
/* "Bad nickname" and "Bad tokens" should have gotten marked
* non-retriable. */
tt_want_int_op(mock_get_by_ei_dd_calls, ==, 2);
tt_want_int_op(mock_get_by_ei_dd_unrecognized, ==, 0);
tt_int_op(sd_ei_bad_nickname.ei_dl_status.n_download_failures, ==, 255);
tt_int_op(sd_ei_bad_tokens.ei_dl_status.n_download_failures, ==, 255);
/* bad_ports is retriable -- because we didn't ask for it. */
tt_int_op(dls_bad_ports.n_download_failures, ==, 0);
/* Wanted still contains "BAD_SIG2" */
tt_int_op(smartlist_len(wanted), ==, 1);
tt_str_op(smartlist_get(wanted, 0), ==,
"16D387D3A58F7DB3CF46638F8D0B90C45C7D769C");
#undef ADD
done:
tor_free(mem_op_hex_tmp);
UNMOCK(router_get_by_extrainfo_digest);
SMARTLIST_FOREACH(chunks, char *, cp, tor_free(cp));
smartlist_free(chunks);
SMARTLIST_FOREACH(wanted, char *, cp, tor_free(cp));
smartlist_free(wanted);
}
static void
test_dir_versions(void *arg)
{
@ -2669,6 +2894,8 @@ struct testcase_t dir_tests[] = {
DIR(routerparse_bad, 0),
DIR(extrainfo_parsing, 0),
DIR(parse_router_list, TT_FORK),
DIR(load_routers, TT_FORK),
DIR(load_extrainfo, TT_FORK),
DIR_LEGACY(versions),
DIR_LEGACY(fp_pairs),
DIR(split_fps, 0),

View File

@ -7,6 +7,7 @@
#include "config.h"
#include "dirvote.h"
#include "microdesc.h"
#include "networkstatus.h"
#include "routerlist.h"
#include "routerparse.h"
@ -618,11 +619,107 @@ test_md_parse(void *arg)
tor_free(mem_op_hex_tmp);
}
static int mock_rgsbd_called = 0;
static routerstatus_t *mock_rgsbd_val_a = NULL;
static routerstatus_t *mock_rgsbd_val_b = NULL;
static routerstatus_t *
mock_router_get_status_by_digest(networkstatus_t *c, const char *d)
{
(void) c;
++mock_rgsbd_called;
if (fast_memeq(d, "\x5d\x76", 2)) {
memcpy(mock_rgsbd_val_a->descriptor_digest, d, 32);
return mock_rgsbd_val_a;
} else if (fast_memeq(d, "\x20\xd1", 2)) {
memcpy(mock_rgsbd_val_b->descriptor_digest, d, 32);
return mock_rgsbd_val_b;
} else {
return NULL;
}
}
static networkstatus_t *mock_ns_val = NULL;
static networkstatus_t *
mock_ns_get_by_flavor(consensus_flavor_t f)
{
(void)f;
return mock_ns_val;
}
static void
test_md_reject_cache(void *arg)
{
(void) arg;
microdesc_cache_t *mc = NULL ;
smartlist_t *added = NULL, *wanted = smartlist_new();
or_options_t *options = get_options_mutable();
char buf[DIGEST256_LEN];
tor_free(options->DataDirectory);
options->DataDirectory = tor_strdup(get_fname("md_datadir_test_rej"));
mock_rgsbd_val_a = tor_malloc_zero(sizeof(routerstatus_t));
mock_rgsbd_val_b = tor_malloc_zero(sizeof(routerstatus_t));
mock_ns_val = tor_malloc_zero(sizeof(networkstatus_t));
mock_ns_val->valid_after = time(NULL) - 86400;
mock_ns_val->valid_until = time(NULL) + 86400;
mock_ns_val->flavor = FLAV_MICRODESC;
#ifdef _WIN32
tt_int_op(0, ==, mkdir(options->DataDirectory));
#else
tt_int_op(0, ==, mkdir(options->DataDirectory, 0700));
#endif
MOCK(router_get_mutable_consensus_status_by_descriptor_digest,
mock_router_get_status_by_digest);
MOCK(networkstatus_get_latest_consensus_by_flavor, mock_ns_get_by_flavor);
mc = get_microdesc_cache();
#define ADD(hex) \
do { \
tt_int_op(0,==,base16_decode(buf,sizeof(buf),hex,strlen(hex))); \
smartlist_add(wanted, tor_memdup(buf, DIGEST256_LEN)); \
} while (0)
/* invalid,0 */
ADD("5d76bf1c6614e885614a1e0ad074e1ab4ea14655ebeefb1736a71b5ed8a15a51");
/* invalid,2 */
ADD("20d1576c5ab11bbcff0dedb1db4a3cfcc8bc8dd839d8cbfef92d00a1a7d7b294");
/* valid, 6 */
ADD("53f740bd222ab37f19f604b1d3759aa65eff1fbce9ac254bd0fa50d4af9b1bae");
/* valid, 8 */
ADD("a0a155562d8093d8fd0feb7b93b7226e17f056c2142aab7a4ea8c5867a0376d5");
added = microdescs_add_to_cache(mc, MD_PARSE_TEST_DATA, NULL,
SAVED_NOWHERE, 0, time(NULL), wanted);
tt_int_op(smartlist_len(added), ==, 2);
tt_int_op(mock_rgsbd_called, ==, 2);
tt_int_op(mock_rgsbd_val_a->dl_status.n_download_failures, ==, 255);
tt_int_op(mock_rgsbd_val_b->dl_status.n_download_failures, ==, 255);
done:
UNMOCK(networkstatus_get_latest_consensus_by_flavor);
UNMOCK(router_get_mutable_consensus_status_by_descriptor_digest);
if (options)
tor_free(options->DataDirectory);
microdesc_free_all();
smartlist_free(added);
SMARTLIST_FOREACH(wanted, char *, cp, tor_free(cp));
smartlist_free(wanted);
tor_free(mock_rgsbd_val_a);
tor_free(mock_rgsbd_val_b);
tor_free(mock_ns_val);
}
struct testcase_t microdesc_tests[] = {
{ "cache", test_md_cache, TT_FORK, NULL, NULL },
{ "broken_cache", test_md_cache_broken, TT_FORK, NULL, NULL },
{ "generate", test_md_generate, 0, NULL, NULL },
{ "parse", test_md_parse, 0, NULL, NULL },
{ "reject_cache", test_md_reject_cache, TT_FORK, NULL, NULL },
END_OF_TESTCASES
};