diff --git a/src/test/test_dir.c b/src/test/test_dir.c index 35bd8ea166..855746e749 100644 --- a/src/test/test_dir.c +++ b/src/test/test_dir.c @@ -24,6 +24,7 @@ #include "routerkeys.h" #include "routerlist.h" #include "routerparse.h" +#include "routerset.h" #include "test.h" #include "torcert.h" @@ -2979,6 +2980,281 @@ test_dir_fmt_control_ns(void *arg) tor_free(s); } +static int mock_get_options_calls = 0; +static or_options_t *mock_options = NULL; + +static void +reset_options(or_options_t *options, int *get_options_calls) +{ + memset(options, 0, sizeof(or_options_t)); + options->TestingTorNetwork = 1; + + *get_options_calls = 0; +} + +static const or_options_t * +mock_get_options(void) +{ + ++mock_get_options_calls; + tor_assert(mock_options); + return mock_options; +} + +static void +reset_routerstatus(routerstatus_t *rs, + const char *hex_identity_digest, + int32_t ipv4_addr) +{ + memset(rs, 0, sizeof(routerstatus_t)); + base16_decode(rs->identity_digest, sizeof(rs->identity_digest), + hex_identity_digest, HEX_DIGEST_LEN); + /* A zero address matches everything, so the address needs to be set. + * But the specific value is irrelevant. */ + rs->addr = ipv4_addr; +} + +#define ROUTER_A_ID_STR "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" +#define ROUTER_A_IPV4 0xAA008801 +#define ROUTER_B_ID_STR "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB" +#define ROUTER_B_IPV4 0xBB008801 + +#define ROUTERSET_ALL_STR "*" +#define ROUTERSET_A_STR ROUTER_A_ID_STR +#define ROUTERSET_NONE_STR "" + +/* + * Test that dirserv_set_routerstatus_testing sets router flags correctly + * Using "*" sets flags on A and B + * Using "A" sets flags on A + * Using "" sets flags on Neither + * If the router is not included: + * - if *Strict is set, the flag is set to 0, + * - otherwise, the flag is not modified. */ +static void +test_dir_dirserv_set_routerstatus_testing(void *arg) +{ + (void)arg; + + /* Init options */ + mock_options = malloc(sizeof(or_options_t)); + reset_options(mock_options, &mock_get_options_calls); + + MOCK(get_options, mock_get_options); + + /* Init routersets */ + routerset_t *routerset_all = routerset_new(); + routerset_parse(routerset_all, ROUTERSET_ALL_STR, "All routers"); + + routerset_t *routerset_a = routerset_new(); + routerset_parse(routerset_a, ROUTERSET_A_STR, "Router A only"); + + routerset_t *routerset_none = routerset_new(); + /* Routersets are empty when provided by routerset_new(), + * so this is not strictly necessary */ + routerset_parse(routerset_none, ROUTERSET_NONE_STR, "No routers"); + + /* Init routerstatuses */ + routerstatus_t *rs_a = malloc(sizeof(routerstatus_t)); + reset_routerstatus(rs_a, ROUTER_A_ID_STR, ROUTER_A_IPV4); + + routerstatus_t *rs_b = malloc(sizeof(routerstatus_t)); + reset_routerstatus(rs_b, ROUTER_B_ID_STR, ROUTER_B_IPV4); + + /* Sanity check that routersets correspond to routerstatuses. + * Return values are {2, 3, 4} */ + + /* We want 3 ("*" means match all addresses) */ + tt_assert(routerset_contains_routerstatus(routerset_all, rs_a, 0) == 3); + tt_assert(routerset_contains_routerstatus(routerset_all, rs_b, 0) == 3); + + /* We want 4 (match id_digest [or nickname]) */ + tt_assert(routerset_contains_routerstatus(routerset_a, rs_a, 0) == 4); + tt_assert(routerset_contains_routerstatus(routerset_a, rs_b, 0) == 0); + + tt_assert(routerset_contains_routerstatus(routerset_none, rs_a, 0) == 0); + tt_assert(routerset_contains_routerstatus(routerset_none, rs_b, 0) == 0); + + /* Check that "*" sets flags on all routers: Exit + * Check the flags aren't being confused with each other */ + reset_options(mock_options, &mock_get_options_calls); + reset_routerstatus(rs_a, ROUTER_A_ID_STR, ROUTER_A_IPV4); + reset_routerstatus(rs_b, ROUTER_B_ID_STR, ROUTER_B_IPV4); + + mock_options->TestingDirAuthVoteExit = routerset_all; + mock_options->TestingDirAuthVoteExitIsStrict = 0; + + dirserv_set_routerstatus_testing(rs_a); + tt_assert(mock_get_options_calls == 1); + dirserv_set_routerstatus_testing(rs_b); + tt_assert(mock_get_options_calls == 2); + + tt_assert(rs_a->is_exit == 1); + tt_assert(rs_b->is_exit == 1); + /* Be paranoid - check no other flags are set */ + tt_assert(rs_a->is_possible_guard == 0); + tt_assert(rs_b->is_possible_guard == 0); + tt_assert(rs_a->is_hs_dir == 0); + tt_assert(rs_b->is_hs_dir == 0); + + /* Check that "*" sets flags on all routers: Guard & HSDir + * Cover the remaining flags in one test */ + reset_options(mock_options, &mock_get_options_calls); + reset_routerstatus(rs_a, ROUTER_A_ID_STR, ROUTER_A_IPV4); + reset_routerstatus(rs_b, ROUTER_B_ID_STR, ROUTER_B_IPV4); + + mock_options->TestingDirAuthVoteGuard = routerset_all; + mock_options->TestingDirAuthVoteGuardIsStrict = 0; + mock_options->TestingDirAuthVoteHSDir = routerset_all; + mock_options->TestingDirAuthVoteHSDirIsStrict = 0; + + dirserv_set_routerstatus_testing(rs_a); + tt_assert(mock_get_options_calls == 1); + dirserv_set_routerstatus_testing(rs_b); + tt_assert(mock_get_options_calls == 2); + + tt_assert(rs_a->is_possible_guard == 1); + tt_assert(rs_b->is_possible_guard == 1); + tt_assert(rs_a->is_hs_dir == 1); + tt_assert(rs_b->is_hs_dir == 1); + /* Be paranoid - check exit isn't set */ + tt_assert(rs_a->is_exit == 0); + tt_assert(rs_b->is_exit == 0); + + /* Check routerset A sets all flags on router A, + * but leaves router B unmodified */ + reset_options(mock_options, &mock_get_options_calls); + reset_routerstatus(rs_a, ROUTER_A_ID_STR, ROUTER_A_IPV4); + reset_routerstatus(rs_b, ROUTER_B_ID_STR, ROUTER_B_IPV4); + + mock_options->TestingDirAuthVoteExit = routerset_a; + mock_options->TestingDirAuthVoteExitIsStrict = 0; + mock_options->TestingDirAuthVoteGuard = routerset_a; + mock_options->TestingDirAuthVoteGuardIsStrict = 0; + mock_options->TestingDirAuthVoteHSDir = routerset_a; + mock_options->TestingDirAuthVoteHSDirIsStrict = 0; + + dirserv_set_routerstatus_testing(rs_a); + tt_assert(mock_get_options_calls == 1); + dirserv_set_routerstatus_testing(rs_b); + tt_assert(mock_get_options_calls == 2); + + tt_assert(rs_a->is_exit == 1); + tt_assert(rs_b->is_exit == 0); + tt_assert(rs_a->is_possible_guard == 1); + tt_assert(rs_b->is_possible_guard == 0); + tt_assert(rs_a->is_hs_dir == 1); + tt_assert(rs_b->is_hs_dir == 0); + + /* Check routerset A unsets all flags on router B when Strict is set */ + reset_options(mock_options, &mock_get_options_calls); + reset_routerstatus(rs_b, ROUTER_B_ID_STR, ROUTER_B_IPV4); + + mock_options->TestingDirAuthVoteExit = routerset_a; + mock_options->TestingDirAuthVoteExitIsStrict = 1; + mock_options->TestingDirAuthVoteGuard = routerset_a; + mock_options->TestingDirAuthVoteGuardIsStrict = 1; + mock_options->TestingDirAuthVoteHSDir = routerset_a; + mock_options->TestingDirAuthVoteHSDirIsStrict = 1; + + rs_b->is_exit = 1; + rs_b->is_possible_guard = 1; + rs_b->is_hs_dir = 1; + + dirserv_set_routerstatus_testing(rs_b); + tt_assert(mock_get_options_calls == 1); + + tt_assert(rs_b->is_exit == 0); + tt_assert(rs_b->is_possible_guard == 0); + tt_assert(rs_b->is_hs_dir == 0); + + /* Check routerset A doesn't modify flags on router B without Strict set */ + reset_options(mock_options, &mock_get_options_calls); + reset_routerstatus(rs_b, ROUTER_B_ID_STR, ROUTER_B_IPV4); + + mock_options->TestingDirAuthVoteExit = routerset_a; + mock_options->TestingDirAuthVoteExitIsStrict = 0; + mock_options->TestingDirAuthVoteGuard = routerset_a; + mock_options->TestingDirAuthVoteGuardIsStrict = 0; + mock_options->TestingDirAuthVoteHSDir = routerset_a; + mock_options->TestingDirAuthVoteHSDirIsStrict = 0; + + rs_b->is_exit = 1; + rs_b->is_possible_guard = 1; + rs_b->is_hs_dir = 1; + + dirserv_set_routerstatus_testing(rs_b); + tt_assert(mock_get_options_calls == 1); + + tt_assert(rs_b->is_exit == 1); + tt_assert(rs_b->is_possible_guard == 1); + tt_assert(rs_b->is_hs_dir == 1); + + /* Check the empty routerset zeroes all flags + * on routers A & B with Strict set */ + reset_options(mock_options, &mock_get_options_calls); + reset_routerstatus(rs_b, ROUTER_B_ID_STR, ROUTER_B_IPV4); + + mock_options->TestingDirAuthVoteExit = routerset_none; + mock_options->TestingDirAuthVoteExitIsStrict = 1; + mock_options->TestingDirAuthVoteGuard = routerset_none; + mock_options->TestingDirAuthVoteGuardIsStrict = 1; + mock_options->TestingDirAuthVoteHSDir = routerset_none; + mock_options->TestingDirAuthVoteHSDirIsStrict = 1; + + rs_b->is_exit = 1; + rs_b->is_possible_guard = 1; + rs_b->is_hs_dir = 1; + + dirserv_set_routerstatus_testing(rs_b); + tt_assert(mock_get_options_calls == 1); + + tt_assert(rs_b->is_exit == 0); + tt_assert(rs_b->is_possible_guard == 0); + tt_assert(rs_b->is_hs_dir == 0); + + /* Check the empty routerset doesn't modify any flags + * on A or B without Strict set */ + reset_options(mock_options, &mock_get_options_calls); + reset_routerstatus(rs_a, ROUTER_A_ID_STR, ROUTER_A_IPV4); + reset_routerstatus(rs_b, ROUTER_B_ID_STR, ROUTER_B_IPV4); + + mock_options->TestingDirAuthVoteExit = routerset_none; + mock_options->TestingDirAuthVoteExitIsStrict = 0; + mock_options->TestingDirAuthVoteGuard = routerset_none; + mock_options->TestingDirAuthVoteGuardIsStrict = 0; + mock_options->TestingDirAuthVoteHSDir = routerset_none; + mock_options->TestingDirAuthVoteHSDirIsStrict = 0; + + rs_b->is_exit = 1; + rs_b->is_possible_guard = 1; + rs_b->is_hs_dir = 1; + + dirserv_set_routerstatus_testing(rs_a); + tt_assert(mock_get_options_calls == 1); + dirserv_set_routerstatus_testing(rs_b); + tt_assert(mock_get_options_calls == 2); + + tt_assert(rs_a->is_exit == 0); + tt_assert(rs_a->is_possible_guard == 0); + tt_assert(rs_a->is_hs_dir == 0); + tt_assert(rs_b->is_exit == 1); + tt_assert(rs_b->is_possible_guard == 1); + tt_assert(rs_b->is_hs_dir == 1); + + done: + free(mock_options); + mock_options = NULL; + + UNMOCK(get_options); + + routerset_free(routerset_all); + routerset_free(routerset_a); + routerset_free(routerset_none); + + free(rs_a); + free(rs_b); +} + static void test_dir_http_handling(void *args) { @@ -3244,6 +3520,7 @@ struct testcase_t dir_tests[] = { DIR_LEGACY(clip_unmeasured_bw_kb), DIR_LEGACY(clip_unmeasured_bw_kb_alt), DIR(fmt_control_ns, 0), + DIR(dirserv_set_routerstatus_testing, 0), DIR(http_handling, 0), DIR(purpose_needs_anonymity, 0), DIR(fetch_type, 0),