|
@@ -0,0 +1,256 @@
|
|
|
|
+/* 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 MAIN_PRIVATE
|
|
|
|
+
|
|
|
|
+#include "test.h"
|
|
|
|
+#include "test_helpers.h"
|
|
|
|
+
|
|
|
|
+#include "or.h"
|
|
|
|
+#include "config.h"
|
|
|
|
+#include "hs_service.h"
|
|
|
|
+#include "main.h"
|
|
|
|
+#include "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;
|
|
|
|
+ or_options_t *options;
|
|
|
|
+
|
|
|
|
+ (void) arg;
|
|
|
|
+
|
|
|
|
+ hs_init();
|
|
|
|
+
|
|
|
|
+ /* 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;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* Lets make sure that before intialization, we can't scan the periodic
|
|
|
|
+ * events list and launch them. Lets try by being a Client. */
|
|
|
|
+ options = get_options_mutable();
|
|
|
|
+ options->SocksPort_set = 1;
|
|
|
|
+ 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);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ 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);
|
|
|
|
+ tt_u64_op(item->last_action_time, OP_NE, 0);
|
|
|
|
+ } else {
|
|
|
|
+ tt_int_op(periodic_event_is_enabled(item), OP_EQ, 0);
|
|
|
|
+ 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);
|
|
|
|
+
|
|
|
|
+ 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);
|
|
|
|
+ /* Was previously enabled so they should never be to 0. */
|
|
|
|
+ tt_u64_op(item->last_action_time, OP_NE, 0);
|
|
|
|
+ }
|
|
|
|
+ if (item->roles & PERIODIC_EVENT_ROLE_RELAY) {
|
|
|
|
+ tt_int_op(periodic_event_is_enabled(item), OP_EQ, 1);
|
|
|
|
+ tt_u64_op(item->last_action_time, OP_NE, 0);
|
|
|
|
+ }
|
|
|
|
+ /* Non Relay role should be disabled! */
|
|
|
|
+ if (!(item->roles & PERIODIC_EVENT_ROLE_RELAY)) {
|
|
|
|
+ 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);
|
|
|
|
+ /* Remove it now so the hs_free_all() doesn't try to free stack memory. */
|
|
|
|
+ remove_service(get_hs_service_map(), &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:
|
|
|
|
+ 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));
|
|
|
|
+
|
|
|
|
+ /* 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));
|
|
|
|
+ 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);
|
|
|
|
+
|
|
|
|
+ /* 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->AuthoritativeDir = 1;
|
|
|
|
+ options->V3AuthoritativeDir = 1;
|
|
|
|
+ roles = get_my_roles(options);
|
|
|
|
+ tt_int_op(roles, OP_EQ, PERIODIC_EVENT_ROLE_DIRAUTH);
|
|
|
|
+ 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);
|
|
|
|
+ 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));
|
|
|
|
+ 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));
|
|
|
|
+ tt_assert(roles & PERIODIC_EVENT_ROLE_AUTHORITIES);
|
|
|
|
+
|
|
|
|
+ done:
|
|
|
|
+ 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),
|
|
|
|
+
|
|
|
|
+ END_OF_TESTCASES
|
|
|
|
+};
|
|
|
|
+
|