commit 2962c32b7a60b78f4d470bf063f8b1454b56506b Merge: 42ea03eb7 96ca14d98 Author: teor teor@torproject.org Date: Sat Mar 21 03:50:36 2020 +1000
Merge branch 'bug32588_035' into bug32588_041
Merge tests from maint-0.4.1 with new tests from bug32588_035 in test_router.c.
changes/bug32588 | 4 ++ src/app/config/config.c | 5 +- src/app/config/config.h | 4 ++ src/feature/relay/router.c | 77 ++++++++++++++++++----------- src/feature/relay/router.h | 2 + src/test/test_config.c | 66 +++++++++++++++++++++++++ src/test/test_router.c | 117 +++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 243 insertions(+), 32 deletions(-)
diff --cc src/test/test_router.c index 5477ab51e,e0d3adfdb..e0dd607b8 --- a/src/test/test_router.c +++ b/src/test/test_router.c @@@ -8,19 -8,14 +8,21 @@@ **/
#define CONFIG_PRIVATE + #define CONNECTION_PRIVATE +#define ROUTER_PRIVATE + #include "core/or/or.h" #include "app/config/config.h" #include "core/mainloop/mainloop.h" + #include "core/mainloop/connection.h" #include "feature/hibernate/hibernate.h" +#include "feature/nodelist/networkstatus.h" +#include "feature/nodelist/networkstatus_st.h" +#include "feature/nodelist/node_st.h" +#include "feature/nodelist/nodelist.h" #include "feature/nodelist/routerinfo_st.h" #include "feature/nodelist/routerlist.h" +#include "feature/nodelist/routerstatus_st.h" #include "feature/relay/router.h" #include "feature/stats/rephist.h" #include "lib/crypt_ops/crypto_curve25519.h" @@@ -243,254 -237,124 +247,367 @@@ test_router_check_descriptor_bandwidth_ UNMOCK(we_are_hibernating); }
+static networkstatus_t *mock_ns = NULL; +static networkstatus_t * +mock_networkstatus_get_live_consensus(time_t now) +{ + (void)now; + return mock_ns; +} + +static routerstatus_t *mock_rs = NULL; +static const routerstatus_t * +mock_networkstatus_vote_find_entry(networkstatus_t *ns, const char *digest) +{ + (void)ns; + (void)digest; + return mock_rs; +} + +static void +test_router_mark_if_too_old(void *arg) +{ + (void)arg; + time_t now = approx_time(); + MOCK(networkstatus_get_live_consensus, + mock_networkstatus_get_live_consensus); + MOCK(networkstatus_vote_find_entry, mock_networkstatus_vote_find_entry); + + routerstatus_t rs; + networkstatus_t ns; + memset(&rs, 0, sizeof(rs)); + memset(&ns, 0, sizeof(ns)); + mock_ns = &ns; + mock_ns->valid_after = now-3600; + mock_rs = &rs; + mock_rs->published_on = now - 10; + + // no reason to mark this time. + desc_clean_since = now-10; + desc_dirty_reason = NULL; + mark_my_descriptor_dirty_if_too_old(now); + tt_i64_op(desc_clean_since, OP_EQ, now-10); + + // Doesn't appear in consensus? Still don't mark it. + mock_ns = NULL; + mark_my_descriptor_dirty_if_too_old(now); + tt_i64_op(desc_clean_since, OP_EQ, now-10); + mock_ns = &ns; + + // No new descriptor in a long time? Mark it. + desc_clean_since = now - 3600 * 96; + mark_my_descriptor_dirty_if_too_old(now); + tt_i64_op(desc_clean_since, OP_EQ, 0); + tt_str_op(desc_dirty_reason, OP_EQ, "time for new descriptor"); + + // Version in consensus published a long time ago? We won't mark it + // if it's been clean for only a short time. + desc_clean_since = now - 10; + desc_dirty_reason = NULL; + mock_rs->published_on = now - 3600 * 96; + mark_my_descriptor_dirty_if_too_old(now); + tt_i64_op(desc_clean_since, OP_EQ, now - 10); + + // ... but if it's been clean a while, we mark. + desc_clean_since = now - 2 * 3600; + mark_my_descriptor_dirty_if_too_old(now); + tt_i64_op(desc_clean_since, OP_EQ, 0); + tt_str_op(desc_dirty_reason, OP_EQ, + "version listed in consensus is quite old"); + + // same deal if we're marked stale. + desc_clean_since = now - 2 * 3600; + desc_dirty_reason = NULL; + mock_rs->published_on = now - 10; + mock_rs->is_staledesc = 1; + mark_my_descriptor_dirty_if_too_old(now); + tt_i64_op(desc_clean_since, OP_EQ, 0); + tt_str_op(desc_dirty_reason, OP_EQ, + "listed as stale in consensus"); + + // same deal if we're absent from the consensus. + desc_clean_since = now - 2 * 3600; + desc_dirty_reason = NULL; + mock_rs = NULL; + mark_my_descriptor_dirty_if_too_old(now); + tt_i64_op(desc_clean_since, OP_EQ, 0); + tt_str_op(desc_dirty_reason, OP_EQ, + "not listed in consensus"); + + done: + UNMOCK(networkstatus_get_live_consensus); + UNMOCK(networkstatus_vote_find_entry); +} + +static node_t fake_node; +static const node_t * +mock_node_get_by_nickname(const char *name, unsigned flags) +{ + (void)flags; + if (!strcasecmp(name, "crumpet")) + return &fake_node; + else + return NULL; +} + +static void +test_router_get_my_family(void *arg) +{ + (void)arg; + or_options_t *options = options_new(); + smartlist_t *sl = NULL; + char *join = NULL; + // Overwrite the result of router_get_my_identity_digest(). This + // happens to be okay, but only for testing. + set_server_identity_key_digest_testing( + (const uint8_t*)"holeinthebottomofthe"); + + setup_capture_of_logs(LOG_WARN); + + // No family listed -- so there's no list. + sl = get_my_declared_family(options); + tt_ptr_op(sl, OP_EQ, NULL); + expect_no_log_entry(); + +#define CLEAR() do { \ + if (sl) { \ + SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp)); \ + smartlist_free(sl); \ + } \ + tor_free(join); \ + mock_clean_saved_logs(); \ + } while (0) + + // Add a single nice friendly hex member. This should be enough + // to have our own ID added. + tt_ptr_op(options->MyFamily, OP_EQ, NULL); + config_line_append(&options->MyFamily, "MyFamily", + "$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"); + + sl = get_my_declared_family(options); + tt_ptr_op(sl, OP_NE, NULL); + tt_int_op(smartlist_len(sl), OP_EQ, 2); + join = smartlist_join_strings(sl, " ", 0, NULL); + tt_str_op(join, OP_EQ, + "$686F6C65696E746865626F74746F6D6F66746865 " + "$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"); + expect_no_log_entry(); + CLEAR(); + + // Add a hex member with a ~. The ~ part should get removed. + config_line_append(&options->MyFamily, "MyFamily", + "$0123456789abcdef0123456789abcdef01234567~Muffin"); + sl = get_my_declared_family(options); + tt_ptr_op(sl, OP_NE, NULL); + tt_int_op(smartlist_len(sl), OP_EQ, 3); + join = smartlist_join_strings(sl, " ", 0, NULL); + tt_str_op(join, OP_EQ, + "$0123456789ABCDEF0123456789ABCDEF01234567 " + "$686F6C65696E746865626F74746F6D6F66746865 " + "$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"); + expect_no_log_entry(); + CLEAR(); + + // Nickname lookup will fail, so a nickname will appear verbatim. + config_line_append(&options->MyFamily, "MyFamily", + "BAGEL"); + sl = get_my_declared_family(options); + tt_ptr_op(sl, OP_NE, NULL); + tt_int_op(smartlist_len(sl), OP_EQ, 4); + join = smartlist_join_strings(sl, " ", 0, NULL); + tt_str_op(join, OP_EQ, + "$0123456789ABCDEF0123456789ABCDEF01234567 " + "$686F6C65696E746865626F74746F6D6F66746865 " + "$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA " + "bagel"); + expect_single_log_msg_containing( + "There is a router named "BAGEL" in my declared family, but " + "I have no descriptor for it."); + CLEAR(); + + // A bogus digest should fail entirely. + config_line_append(&options->MyFamily, "MyFamily", + "$painauchocolat"); + sl = get_my_declared_family(options); + tt_ptr_op(sl, OP_NE, NULL); + tt_int_op(smartlist_len(sl), OP_EQ, 4); + join = smartlist_join_strings(sl, " ", 0, NULL); + tt_str_op(join, OP_EQ, + "$0123456789ABCDEF0123456789ABCDEF01234567 " + "$686F6C65696E746865626F74746F6D6F66746865 " + "$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA " + "bagel"); + // "BAGEL" is still there, but it won't make a warning, because we already + // warned about it. + expect_single_log_msg_containing( + "There is a router named "$painauchocolat" in my declared " + "family, but that isn't a legal digest or nickname. Skipping it."); + CLEAR(); + + // Let's introduce a node we can look up by nickname + memset(&fake_node, 0, sizeof(fake_node)); + memcpy(fake_node.identity, "whydoyouasknonononon", DIGEST_LEN); + MOCK(node_get_by_nickname, mock_node_get_by_nickname); + + config_line_append(&options->MyFamily, "MyFamily", + "CRUmpeT"); + sl = get_my_declared_family(options); + tt_ptr_op(sl, OP_NE, NULL); + tt_int_op(smartlist_len(sl), OP_EQ, 5); + join = smartlist_join_strings(sl, " ", 0, NULL); + tt_str_op(join, OP_EQ, + "$0123456789ABCDEF0123456789ABCDEF01234567 " + "$686F6C65696E746865626F74746F6D6F66746865 " + "$776879646F796F7561736B6E6F6E6F6E6F6E6F6E " + "$AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA " + "bagel"); + // "BAGEL" is still there, but it won't make a warning, because we already + // warned about it. Some with "$painauchocolat". + expect_single_log_msg_containing( + "There is a router named "CRUmpeT" in my declared " + "family, but it wasn't listed by digest. Please consider saying " + "$776879646F796F7561736B6E6F6E6F6E6F6E6F6E instead, if that's " + "what you meant."); + CLEAR(); + UNMOCK(node_get_by_nickname); + + // Try a singleton list containing only us: It should give us NULL. + config_free_lines(options->MyFamily); + config_line_append(&options->MyFamily, "MyFamily", + "$686F6C65696E746865626F74746F6D6F66746865"); + sl = get_my_declared_family(options); + tt_ptr_op(sl, OP_EQ, NULL); + expect_no_log_entry(); + + done: + or_options_free(options); + teardown_capture_of_logs(); + CLEAR(); + UNMOCK(node_get_by_nickname); + +#undef CLEAR +} + + static smartlist_t *fake_connection_array = NULL; + static smartlist_t * + mock_get_connection_array(void) + { + return fake_connection_array; + } + + static void + test_router_get_advertised_or_port(void *arg) + { + (void)arg; + int r, w=0, n=0; + char *msg=NULL; + or_options_t *opts = options_new(); + listener_connection_t *listener = NULL; + tor_addr_port_t ipv6; + + // Test one failing case of router_get_advertised_ipv6_or_ap(). + router_get_advertised_ipv6_or_ap(opts, &ipv6); + tt_str_op(fmt_addrport(&ipv6.addr, ipv6.port), OP_EQ, "[::]:0"); + + // And one failing case of router_get_advertised_or_port(). + tt_int_op(0, OP_EQ, router_get_advertised_or_port_by_af(opts, AF_INET)); + tt_int_op(0, OP_EQ, router_get_advertised_or_port(opts)); + + // Set up a couple of configured ports. + config_line_append(&opts->ORPort_lines, "ORPort", "[1234::5678]:auto"); + config_line_append(&opts->ORPort_lines, "ORPort", "5.6.7.8:9999"); + r = parse_ports(opts, 0, &msg, &n, &w); + tt_assert(r == 0); + + // There are no listeners, so the "auto" case will turn up no results. + tt_int_op(0, OP_EQ, router_get_advertised_or_port_by_af(opts, AF_INET6)); + router_get_advertised_ipv6_or_ap(opts, &ipv6); + tt_str_op(fmt_addrport(&ipv6.addr, ipv6.port), OP_EQ, "[::]:0"); + + // This will return the matching value from the configured port. + tt_int_op(9999, OP_EQ, router_get_advertised_or_port_by_af(opts, AF_INET)); + tt_int_op(9999, OP_EQ, router_get_advertised_or_port(opts)); + + // Now set up a dummy listener. + MOCK(get_connection_array, mock_get_connection_array); + fake_connection_array = smartlist_new(); + listener = listener_connection_new(CONN_TYPE_OR_LISTENER, AF_INET6); + TO_CONN(listener)->port = 54321; + smartlist_add(fake_connection_array, TO_CONN(listener)); + + // We should get a port this time. + tt_int_op(54321, OP_EQ, router_get_advertised_or_port_by_af(opts, AF_INET6)); + + // Test one succeeding case of router_get_advertised_ipv6_or_ap(). + router_get_advertised_ipv6_or_ap(opts, &ipv6); + tt_str_op(fmt_addrport(&ipv6.addr, ipv6.port), OP_EQ, + "[1234::5678]:54321"); + + // This will return the matching value from the configured port. + tt_int_op(9999, OP_EQ, router_get_advertised_or_port_by_af(opts, AF_INET)); + tt_int_op(9999, OP_EQ, router_get_advertised_or_port(opts)); + + done: + or_options_free(opts); + config_free_all(); + smartlist_free(fake_connection_array); + connection_free_minimal(TO_CONN(listener)); + UNMOCK(get_connection_array); + } + + static void + test_router_get_advertised_or_port_localhost(void *arg) + { + (void)arg; + int r, w=0, n=0; + char *msg=NULL; + or_options_t *opts = options_new(); + tor_addr_port_t ipv6; + + // Set up a couple of configured ports on localhost. + config_line_append(&opts->ORPort_lines, "ORPort", "[::1]:9999"); + config_line_append(&opts->ORPort_lines, "ORPort", "127.0.0.1:8888"); + r = parse_ports(opts, 0, &msg, &n, &w); + tt_assert(r == 0); + + // We should refuse to advertise them, since we have default dirauths. + router_get_advertised_ipv6_or_ap(opts, &ipv6); + tt_str_op(fmt_addrport(&ipv6.addr, ipv6.port), OP_EQ, "[::]:0"); + // But the lower-level function should still report the correct value + tt_int_op(9999, OP_EQ, router_get_advertised_or_port_by_af(opts, AF_INET6)); + + // The IPv4 checks are done in resolve_my_address(), which doesn't use + // ORPorts so we can't test them here. (See #33681.) Both these lower-level + // functions should still report the correct value. + tt_int_op(8888, OP_EQ, router_get_advertised_or_port_by_af(opts, AF_INET)); + tt_int_op(8888, OP_EQ, router_get_advertised_or_port(opts)); + + // Now try with a fake authority set up. + config_line_append(&opts->DirAuthorities, "DirAuthority", + "127.0.0.1:1066 " + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"); + + tt_int_op(9999, OP_EQ, router_get_advertised_or_port_by_af(opts, AF_INET6)); + router_get_advertised_ipv6_or_ap(opts, &ipv6); + tt_str_op(fmt_addrport(&ipv6.addr, ipv6.port), OP_EQ, "[::1]:9999"); + + tt_int_op(8888, OP_EQ, router_get_advertised_or_port_by_af(opts, AF_INET)); + tt_int_op(8888, OP_EQ, router_get_advertised_or_port(opts)); + + done: + or_options_free(opts); + config_free_all(); + } + #define ROUTER_TEST(name, flags) \ { #name, test_router_ ## name, flags, NULL, NULL }
struct testcase_t router_tests[] = { ROUTER_TEST(check_descriptor_bandwidth_changed, TT_FORK), ROUTER_TEST(dump_router_to_string_no_bridge_distribution_method, TT_FORK), + ROUTER_TEST(mark_if_too_old, TT_FORK), + ROUTER_TEST(get_my_family, TT_FORK), + ROUTER_TEST(get_advertised_or_port, TT_FORK), + ROUTER_TEST(get_advertised_or_port_localhost, TT_FORK), END_OF_TESTCASES };