/* Copyright (c) 2018, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** * \file test_periodic_event.c * \brief Test the periodic events that Tor uses for different roles. They are * part of the libevent mainloop */ #define CONFIG_PRIVATE #define HS_SERVICE_PRIVATE #define MAINLOOP_PRIVATE #include "test/test.h" #include "test/test_helpers.h" #include "core/or/or.h" #include "app/config/config.h" #include "feature/hibernate/hibernate.h" #include "feature/hs/hs_service.h" #include "core/mainloop/mainloop.h" #include "core/mainloop/periodic.h" /** Helper function: This is replaced in some tests for the event callbacks so * we don't actually go into the code path of those callbacks. */ static int dumb_event_fn(time_t now, const or_options_t *options) { (void) now; (void) options; /* Will get rescheduled in 300 seconds. It just can't be 0. */ return 300; } static void register_dummy_hidden_service(hs_service_t *service) { memset(service, 0, sizeof(hs_service_t)); memset(&service->keys.identity_pk, 'A', sizeof(service->keys.identity_pk)); (void) register_service(get_hs_service_map(), service); } static void test_pe_initialize(void *arg) { (void) arg; /* Initialize the events but the callback won't get called since we would * need to run the main loop and then wait for a second delaying the unit * tests. Instead, we'll test the callback work indepedently elsewhere. */ initialize_periodic_events(); /* Validate that all events have been set up. */ for (int i = 0; periodic_events[i].name; ++i) { periodic_event_item_t *item = &periodic_events[i]; tt_assert(item->ev); tt_assert(item->fn); tt_u64_op(item->last_action_time, OP_EQ, 0); /* Every event must have role(s) assign to it. This is done statically. */ tt_u64_op(item->roles, OP_NE, 0); tt_uint_op(periodic_event_is_enabled(item), OP_EQ, 0); } done: teardown_periodic_events(); } static void test_pe_launch(void *arg) { hs_service_t service, *to_remove = NULL; or_options_t *options; (void) arg; hs_init(); /* We need to put tor in hibernation live state so the events requiring * network gets enabled. */ consider_hibernation(time(NULL)); /* Hack: We'll set a dumb fn() of each events so they don't get called when * dispatching them. We just want to test the state of the callbacks, not * the whole code path. */ for (int i = 0; periodic_events[i].name; ++i) { periodic_event_item_t *item = &periodic_events[i]; item->fn = dumb_event_fn; } options = get_options_mutable(); options->SocksPort_set = 1; periodic_events_on_new_options(options); #if 0 /* Lets make sure that before intialization, we can't scan the periodic * events list and launch them. Lets try by being a Client. */ /* XXXX We make sure these events are initialized now way earlier than we * did before. */ for (int i = 0; periodic_events[i].name; ++i) { periodic_event_item_t *item = &periodic_events[i]; tt_int_op(periodic_event_is_enabled(item), OP_EQ, 0); } #endif initialize_periodic_events(); /* Now that we've initialized, rescan the list to launch. */ periodic_events_on_new_options(options); for (int i = 0; periodic_events[i].name; ++i) { periodic_event_item_t *item = &periodic_events[i]; if (item->roles & PERIODIC_EVENT_ROLE_CLIENT) { tt_int_op(periodic_event_is_enabled(item), OP_EQ, 1); } else { tt_int_op(periodic_event_is_enabled(item), OP_EQ, 0); } // enabled or not, the event has not yet been run. tt_u64_op(item->last_action_time, OP_EQ, 0); } /* Remove Client but become a Relay. */ options->SocksPort_set = 0; options->ORPort_set = 1; periodic_events_on_new_options(options); unsigned roles = get_my_roles(options); tt_uint_op(roles, OP_EQ, PERIODIC_EVENT_ROLE_RELAY|PERIODIC_EVENT_ROLE_DIRSERVER); for (int i = 0; periodic_events[i].name; ++i) { periodic_event_item_t *item = &periodic_events[i]; /* Only Client role should be disabled. */ if (item->roles == PERIODIC_EVENT_ROLE_CLIENT) { tt_int_op(periodic_event_is_enabled(item), OP_EQ, 0); } if (item->roles & PERIODIC_EVENT_ROLE_RELAY) { tt_int_op(periodic_event_is_enabled(item), OP_EQ, 1); } /* Non Relay role should be disabled, except for Dirserver. */ if (!(item->roles & roles)) { tt_int_op(periodic_event_is_enabled(item), OP_EQ, 0); } } /* Disable everything and we'll enable them ALL. */ options->SocksPort_set = 0; options->ORPort_set = 0; periodic_events_on_new_options(options); for (int i = 0; periodic_events[i].name; ++i) { periodic_event_item_t *item = &periodic_events[i]; tt_int_op(periodic_event_is_enabled(item), OP_EQ, 0); } /* Enable everything. */ options->SocksPort_set = 1; options->ORPort_set = 1; options->BridgeRelay = 1; options->AuthoritativeDir = 1; options->V3AuthoritativeDir = 1; options->BridgeAuthoritativeDir = 1; register_dummy_hidden_service(&service); periodic_events_on_new_options(options); /* Note down the reference because we need to remove this service from the * global list before the hs_free_all() call so it doesn't try to free * memory on the stack. Furthermore, we can't remove it now else it will * trigger a rescan of the event disabling the HS service event. */ to_remove = &service; for (int i = 0; periodic_events[i].name; ++i) { periodic_event_item_t *item = &periodic_events[i]; tt_int_op(periodic_event_is_enabled(item), OP_EQ, 1); } done: if (to_remove) { remove_service(get_hs_service_map(), to_remove); } hs_free_all(); } static void test_pe_get_roles(void *arg) { int roles; (void) arg; /* Just so the HS global map exists. */ hs_init(); or_options_t *options = get_options_mutable(); tt_assert(options); /* Nothing configured, should be no roles. */ roles = get_my_roles(options); tt_int_op(roles, OP_EQ, 0); /* Indicate we have a SocksPort, roles should be come Client. */ options->SocksPort_set = 1; roles = get_my_roles(options); tt_int_op(roles, OP_EQ, PERIODIC_EVENT_ROLE_CLIENT); /* Now, we'll add a ORPort so should now be a Relay + Client. */ options->ORPort_set = 1; roles = get_my_roles(options); tt_int_op(roles, OP_EQ, (PERIODIC_EVENT_ROLE_CLIENT | PERIODIC_EVENT_ROLE_RELAY | PERIODIC_EVENT_ROLE_DIRSERVER)); /* Now add a Bridge. */ options->BridgeRelay = 1; roles = get_my_roles(options); tt_int_op(roles, OP_EQ, (PERIODIC_EVENT_ROLE_CLIENT | PERIODIC_EVENT_ROLE_RELAY | PERIODIC_EVENT_ROLE_BRIDGE | PERIODIC_EVENT_ROLE_DIRSERVER)); tt_assert(roles & PERIODIC_EVENT_ROLE_ROUTER); /* Unset client so we can solely test Router role. */ options->SocksPort_set = 0; roles = get_my_roles(options); tt_int_op(roles, OP_EQ, PERIODIC_EVENT_ROLE_ROUTER | PERIODIC_EVENT_ROLE_DIRSERVER); /* Reset options so we can test authorities. */ options->SocksPort_set = 0; options->ORPort_set = 0; options->BridgeRelay = 0; roles = get_my_roles(options); tt_int_op(roles, OP_EQ, 0); /* Now upgrade to Dirauth. */ options->DirPort_set = 1; options->AuthoritativeDir = 1; options->V3AuthoritativeDir = 1; roles = get_my_roles(options); tt_int_op(roles, OP_EQ, PERIODIC_EVENT_ROLE_DIRAUTH|PERIODIC_EVENT_ROLE_DIRSERVER); tt_assert(roles & PERIODIC_EVENT_ROLE_AUTHORITIES); /* Now Bridge Authority. */ options->V3AuthoritativeDir = 0; options->BridgeAuthoritativeDir = 1; roles = get_my_roles(options); tt_int_op(roles, OP_EQ, PERIODIC_EVENT_ROLE_BRIDGEAUTH|PERIODIC_EVENT_ROLE_DIRSERVER); tt_assert(roles & PERIODIC_EVENT_ROLE_AUTHORITIES); /* Move that bridge auth to become a relay. */ options->ORPort_set = 1; roles = get_my_roles(options); tt_int_op(roles, OP_EQ, (PERIODIC_EVENT_ROLE_BRIDGEAUTH | PERIODIC_EVENT_ROLE_RELAY | PERIODIC_EVENT_ROLE_DIRSERVER)); tt_assert(roles & PERIODIC_EVENT_ROLE_AUTHORITIES); /* And now an Hidden service. */ hs_service_t service; register_dummy_hidden_service(&service); roles = get_my_roles(options); /* Remove it now so the hs_free_all() doesn't try to free stack memory. */ remove_service(get_hs_service_map(), &service); tt_int_op(roles, OP_EQ, (PERIODIC_EVENT_ROLE_BRIDGEAUTH | PERIODIC_EVENT_ROLE_RELAY | PERIODIC_EVENT_ROLE_HS_SERVICE | PERIODIC_EVENT_ROLE_DIRSERVER)); tt_assert(roles & PERIODIC_EVENT_ROLE_AUTHORITIES); done: hs_free_all(); } static void test_pe_hs_service(void *arg) { hs_service_t service, *to_remove = NULL; (void) arg; hs_init(); /* We need to put tor in hibernation live state so the events requiring * network gets enabled. */ consider_hibernation(time(NULL)); /* Initialize the events so we can enable them */ initialize_periodic_events(); /* Hack: We'll set a dumb fn() of each events so they don't get called when * dispatching them. We just want to test the state of the callbacks, not * the whole code path. */ for (int i = 0; periodic_events[i].name; ++i) { periodic_event_item_t *item = &periodic_events[i]; item->fn = dumb_event_fn; } /* This should trigger a rescan of the list and enable the HS service * events. */ register_dummy_hidden_service(&service); /* Note down the reference because we need to remove this service from the * global list before the hs_free_all() call so it doesn't try to free * memory on the stack. Furthermore, we can't remove it now else it will * trigger a rescan of the event disabling the HS service event. */ to_remove = &service; for (int i = 0; periodic_events[i].name; ++i) { periodic_event_item_t *item = &periodic_events[i]; if (item->roles & PERIODIC_EVENT_ROLE_HS_SERVICE) { tt_int_op(periodic_event_is_enabled(item), OP_EQ, 1); } } to_remove = NULL; /* Remove the service from the global map, it should trigger a rescan and * disable the HS service events. */ remove_service(get_hs_service_map(), &service); for (int i = 0; periodic_events[i].name; ++i) { periodic_event_item_t *item = &periodic_events[i]; if (item->roles & PERIODIC_EVENT_ROLE_HS_SERVICE) { tt_int_op(periodic_event_is_enabled(item), OP_EQ, 0); } } done: if (to_remove) { remove_service(get_hs_service_map(), to_remove); } hs_free_all(); } #define PE_TEST(name) \ { #name, test_pe_## name , TT_FORK, NULL, NULL } struct testcase_t periodic_event_tests[] = { PE_TEST(initialize), PE_TEST(launch), PE_TEST(get_roles), PE_TEST(hs_service), END_OF_TESTCASES };