tor

The Tor anonymity network
git clone https://git.dasho.dev/tor.git
Log | Files | Refs | README | LICENSE

test_periodic_event.c (12156B)


      1 /* Copyright (c) 2018-2021, The Tor Project, Inc. */
      2 /* See LICENSE for licensing information */
      3 
      4 /**
      5 * \file test_periodic_event.c
      6 * \brief Test the periodic events that Tor uses for different roles. They are
      7 *        part of the libevent mainloop
      8 */
      9 
     10 #define CONFIG_PRIVATE
     11 #define HS_SERVICE_PRIVATE
     12 #define MAINLOOP_PRIVATE
     13 
     14 #include "test/test.h"
     15 #include "test/test_helpers.h"
     16 
     17 #include "core/or/or.h"
     18 #include "app/config/config.h"
     19 #include "feature/hibernate/hibernate.h"
     20 #include "feature/hs/hs_metrics.h"
     21 #include "feature/hs/hs_service.h"
     22 #include "core/mainloop/mainloop.h"
     23 #include "core/mainloop/netstatus.h"
     24 #include "core/mainloop/periodic.h"
     25 
     26 /** Helper function: This is replaced in some tests for the event callbacks so
     27 * we don't actually go into the code path of those callbacks. */
     28 static int
     29 dumb_event_fn(time_t now, const or_options_t *options)
     30 {
     31  (void) now;
     32  (void) options;
     33 
     34  /* Will get rescheduled in 300 seconds. It just can't be 0. */
     35  return 300;
     36 }
     37 
     38 static void
     39 register_dummy_hidden_service(hs_service_t *service)
     40 {
     41  memset(service, 0, sizeof(hs_service_t));
     42  memset(&service->keys.identity_pk, 'A', sizeof(service->keys.identity_pk));
     43  (void) register_service(get_hs_service_map(), service);
     44 }
     45 
     46 static void
     47 test_pe_initialize(void *arg)
     48 {
     49  (void) arg;
     50 
     51  /* Initialize the events but the callback won't get called since we would
     52   * need to run the main loop and then wait for a second delaying the unit
     53   * tests. Instead, we'll test the callback work independently elsewhere. */
     54  initialize_periodic_events();
     55  periodic_events_connect_all();
     56  set_network_participation(false);
     57  rescan_periodic_events(get_options());
     58 
     59  /* Validate that all events have been set up. */
     60  for (int i = 0; mainloop_periodic_events[i].name; ++i) {
     61    periodic_event_item_t *item = &mainloop_periodic_events[i];
     62    tt_assert(item->ev);
     63    tt_assert(item->fn);
     64    tt_u64_op(item->last_action_time, OP_EQ, 0);
     65    /* Every event must have role(s) assign to it. This is done statically. */
     66    tt_u64_op(item->roles, OP_NE, 0);
     67    int should_be_enabled = (item->roles & PERIODIC_EVENT_ROLE_ALL) &&
     68      !(item->flags & PERIODIC_EVENT_FLAG_NEED_NET);
     69    tt_uint_op(periodic_event_is_enabled(item), OP_EQ, should_be_enabled);
     70  }
     71 
     72 done:
     73  teardown_periodic_events();
     74 }
     75 
     76 static void
     77 test_pe_launch(void *arg)
     78 {
     79  hs_service_t service, *to_remove = NULL;
     80  or_options_t *options;
     81 
     82  (void) arg;
     83 
     84  hs_init();
     85  /* We need to put tor in hibernation live state so the events requiring
     86   * network gets enabled. */
     87  consider_hibernation(time(NULL));
     88 
     89  set_network_participation(true);
     90 
     91  /* Hack: We'll set a dumb fn() of each events so they don't get called when
     92   * dispatching them. We just want to test the state of the callbacks, not
     93   * the whole code path. */
     94  for (int i = 0; mainloop_periodic_events[i].name; ++i) {
     95    periodic_event_item_t *item = &mainloop_periodic_events[i];
     96    item->fn = dumb_event_fn;
     97  }
     98 
     99  options = get_options_mutable();
    100  options->SocksPort_set = 1;
    101  periodic_events_on_new_options(options);
    102 
    103 #if 0
    104  /* Lets make sure that before initialization, we can't scan the periodic
    105   * events list and launch them. Lets try by being a Client. */
    106  /* XXXX We make sure these events are initialized now way earlier than we
    107   * did before. */
    108  for (int i = 0; periodic_events[i].name; ++i) {
    109    periodic_event_item_t *item = &periodic_events[i];
    110    tt_int_op(periodic_event_is_enabled(item), OP_EQ, 0);
    111  }
    112 #endif /* 0 */
    113 
    114  initialize_periodic_events();
    115  periodic_events_connect_all();
    116 
    117  /* Now that we've initialized, rescan the list to launch. */
    118  periodic_events_on_new_options(options);
    119 
    120  int mask = PERIODIC_EVENT_ROLE_CLIENT|PERIODIC_EVENT_ROLE_ALL|
    121    PERIODIC_EVENT_ROLE_NET_PARTICIPANT;
    122  for (int i = 0; mainloop_periodic_events[i].name; ++i) {
    123    periodic_event_item_t *item = &mainloop_periodic_events[i];
    124    int should_be_enabled = !!(item->roles & mask);
    125    tt_int_op(periodic_event_is_enabled(item), OP_EQ, should_be_enabled);
    126    // enabled or not, the event has not yet been run.
    127    tt_u64_op(item->last_action_time, OP_EQ, 0);
    128  }
    129 
    130  /* Remove Client but become a Relay. */
    131  options->SocksPort_set = 0;
    132  options->ORPort_set = 1;
    133  periodic_events_on_new_options(options);
    134 
    135  unsigned roles = get_my_roles(options);
    136  tt_uint_op(roles, OP_EQ,
    137             PERIODIC_EVENT_ROLE_RELAY|PERIODIC_EVENT_ROLE_DIRSERVER|
    138             PERIODIC_EVENT_ROLE_ALL|PERIODIC_EVENT_ROLE_NET_PARTICIPANT);
    139 
    140  for (int i = 0; mainloop_periodic_events[i].name; ++i) {
    141    periodic_event_item_t *item = &mainloop_periodic_events[i];
    142    /* Only Client role should be disabled. */
    143    if (item->roles == PERIODIC_EVENT_ROLE_CLIENT) {
    144      tt_int_op(periodic_event_is_enabled(item), OP_EQ, 0);
    145    }
    146    if (item->roles & PERIODIC_EVENT_ROLE_RELAY) {
    147      tt_int_op(periodic_event_is_enabled(item), OP_EQ, 1);
    148    }
    149    /* Non Relay role should be disabled, except for Dirserver. */
    150    if (!(item->roles & roles)) {
    151      tt_int_op(periodic_event_is_enabled(item), OP_EQ, 0);
    152    }
    153  }
    154 
    155  /* Disable everything and we'll enable them ALL. */
    156  options->SocksPort_set = 0;
    157  options->ORPort_set = 0;
    158  options->DisableNetwork = 1;
    159  set_network_participation(false);
    160  periodic_events_on_new_options(options);
    161 
    162  for (int i = 0; mainloop_periodic_events[i].name; ++i) {
    163    periodic_event_item_t *item = &mainloop_periodic_events[i];
    164    int should_be_enabled = (item->roles & PERIODIC_EVENT_ROLE_ALL) &&
    165      !(item->flags & PERIODIC_EVENT_FLAG_NEED_NET);
    166    tt_int_op(periodic_event_is_enabled(item), OP_EQ, should_be_enabled);
    167  }
    168 
    169  /* Enable everything. */
    170  options->SocksPort_set = 1; options->ORPort_set = 1;
    171  options->BridgeRelay = 1; options->AuthoritativeDir = 1;
    172  options->V3AuthoritativeDir = 1; options->BridgeAuthoritativeDir = 1;
    173  options->DisableNetwork = 0;
    174  set_network_participation(true);
    175  register_dummy_hidden_service(&service);
    176  periodic_events_on_new_options(options);
    177  /* Note down the reference because we need to remove this service from the
    178   * global list before the hs_free_all() call so it doesn't try to free
    179   * memory on the stack. Furthermore, we can't remove it now else it will
    180   * trigger a rescan of the event disabling the HS service event. */
    181  to_remove = &service;
    182 
    183  for (int i = 0; mainloop_periodic_events[i].name; ++i) {
    184    periodic_event_item_t *item = &mainloop_periodic_events[i];
    185    tt_int_op(periodic_event_is_enabled(item), OP_EQ,
    186              (item->roles != PERIODIC_EVENT_ROLE_CONTROLEV));
    187  }
    188 
    189 done:
    190  if (to_remove) {
    191    hs_metrics_service_free(&service);
    192    remove_service(get_hs_service_map(), to_remove);
    193  }
    194  hs_free_all();
    195 }
    196 
    197 static void
    198 test_pe_get_roles(void *arg)
    199 {
    200  int roles;
    201 
    202  (void) arg;
    203 
    204  /* Just so the HS global map exists. */
    205  hs_init();
    206 
    207  or_options_t *options = get_options_mutable();
    208  tt_assert(options);
    209  set_network_participation(true);
    210 
    211  const int ALL = PERIODIC_EVENT_ROLE_ALL |
    212    PERIODIC_EVENT_ROLE_NET_PARTICIPANT;
    213 
    214  /* Nothing configured, should be no roles. */
    215  tt_assert(net_is_disabled());
    216  roles = get_my_roles(options);
    217  tt_int_op(roles, OP_EQ, ALL);
    218 
    219  /* Indicate we have a SocksPort, roles should be come Client. */
    220  options->SocksPort_set = 1;
    221  roles = get_my_roles(options);
    222  tt_int_op(roles, OP_EQ, PERIODIC_EVENT_ROLE_CLIENT|ALL);
    223 
    224  /* Now, we'll add a ORPort so should now be a Relay + Client. */
    225  options->ORPort_set = 1;
    226  roles = get_my_roles(options);
    227  tt_int_op(roles, OP_EQ,
    228            (PERIODIC_EVENT_ROLE_CLIENT | PERIODIC_EVENT_ROLE_RELAY |
    229             PERIODIC_EVENT_ROLE_DIRSERVER | ALL));
    230 
    231  /* Now add a Bridge. */
    232  options->BridgeRelay = 1;
    233  roles = get_my_roles(options);
    234  tt_int_op(roles, OP_EQ,
    235            (PERIODIC_EVENT_ROLE_CLIENT | PERIODIC_EVENT_ROLE_RELAY |
    236             PERIODIC_EVENT_ROLE_BRIDGE | PERIODIC_EVENT_ROLE_DIRSERVER |
    237             ALL));
    238  tt_assert(roles & PERIODIC_EVENT_ROLE_ROUTER);
    239  /* Unset client so we can solely test Router role. */
    240  options->SocksPort_set = 0;
    241  roles = get_my_roles(options);
    242  tt_int_op(roles, OP_EQ,
    243            PERIODIC_EVENT_ROLE_ROUTER | PERIODIC_EVENT_ROLE_DIRSERVER |
    244            ALL);
    245 
    246  /* Reset options so we can test authorities. */
    247  options->SocksPort_set = 0;
    248  options->ORPort_set = 0;
    249  options->BridgeRelay = 0;
    250  roles = get_my_roles(options);
    251  tt_int_op(roles, OP_EQ, ALL);
    252 
    253  /* Now upgrade to Dirauth. */
    254  options->DirPort_set = 1;
    255  options->AuthoritativeDir = 1;
    256  options->V3AuthoritativeDir = 1;
    257  roles = get_my_roles(options);
    258  tt_int_op(roles, OP_EQ,
    259            PERIODIC_EVENT_ROLE_DIRAUTH|PERIODIC_EVENT_ROLE_DIRSERVER|ALL);
    260  tt_assert(roles & PERIODIC_EVENT_ROLE_AUTHORITIES);
    261 
    262  /* Now Bridge Authority. */
    263  options->V3AuthoritativeDir = 0;
    264  options->BridgeAuthoritativeDir = 1;
    265  roles = get_my_roles(options);
    266  tt_int_op(roles, OP_EQ,
    267            PERIODIC_EVENT_ROLE_BRIDGEAUTH|PERIODIC_EVENT_ROLE_DIRSERVER|ALL);
    268  tt_assert(roles & PERIODIC_EVENT_ROLE_AUTHORITIES);
    269 
    270  /* Move that bridge auth to become a relay. */
    271  options->ORPort_set = 1;
    272  roles = get_my_roles(options);
    273  tt_int_op(roles, OP_EQ,
    274            (PERIODIC_EVENT_ROLE_BRIDGEAUTH | PERIODIC_EVENT_ROLE_RELAY
    275             | PERIODIC_EVENT_ROLE_DIRSERVER|ALL));
    276  tt_assert(roles & PERIODIC_EVENT_ROLE_AUTHORITIES);
    277 
    278  /* And now an Hidden service. */
    279  hs_service_t service;
    280  register_dummy_hidden_service(&service);
    281  roles = get_my_roles(options);
    282  /* Remove it now so the hs_free_all() doesn't try to free stack memory. */
    283  remove_service(get_hs_service_map(), &service);
    284  hs_metrics_service_free(&service);
    285  tt_int_op(roles, OP_EQ,
    286            (PERIODIC_EVENT_ROLE_BRIDGEAUTH | PERIODIC_EVENT_ROLE_RELAY |
    287             PERIODIC_EVENT_ROLE_HS_SERVICE | PERIODIC_EVENT_ROLE_DIRSERVER |
    288             ALL));
    289  tt_assert(roles & PERIODIC_EVENT_ROLE_AUTHORITIES);
    290 
    291 done:
    292  hs_free_all();
    293 }
    294 
    295 static void
    296 test_pe_hs_service(void *arg)
    297 {
    298  hs_service_t service, *to_remove = NULL;
    299 
    300  (void) arg;
    301 
    302  hs_init();
    303  /* We need to put tor in hibernation live state so the events requiring
    304   * network gets enabled. */
    305  consider_hibernation(time(NULL));
    306  /* Initialize the events so we can enable them */
    307  initialize_periodic_events();
    308  periodic_events_connect_all();
    309 
    310  /* Hack: We'll set a dumb fn() of each events so they don't get called when
    311   * dispatching them. We just want to test the state of the callbacks, not
    312   * the whole code path. */
    313  for (int i = 0; mainloop_periodic_events[i].name; ++i) {
    314    periodic_event_item_t *item = &mainloop_periodic_events[i];
    315    item->fn = dumb_event_fn;
    316  }
    317 
    318  /* This should trigger a rescan of the list and enable the HS service
    319   * events. */
    320  register_dummy_hidden_service(&service);
    321  /* Note down the reference because we need to remove this service from the
    322   * global list before the hs_free_all() call so it doesn't try to free
    323   * memory on the stack. Furthermore, we can't remove it now else it will
    324   * trigger a rescan of the event disabling the HS service event. */
    325  to_remove = &service;
    326 
    327  for (int i = 0; mainloop_periodic_events[i].name; ++i) {
    328    periodic_event_item_t *item = &mainloop_periodic_events[i];
    329    if (item->roles & PERIODIC_EVENT_ROLE_HS_SERVICE) {
    330      tt_int_op(periodic_event_is_enabled(item), OP_EQ, 1);
    331    }
    332  }
    333  to_remove = NULL;
    334 
    335  /* Remove the service from the global map, it should trigger a rescan and
    336   * disable the HS service events. */
    337  remove_service(get_hs_service_map(), &service);
    338  hs_metrics_service_free(&service);
    339  for (int i = 0; mainloop_periodic_events[i].name; ++i) {
    340    periodic_event_item_t *item = &mainloop_periodic_events[i];
    341    if (item->roles & PERIODIC_EVENT_ROLE_HS_SERVICE) {
    342      tt_int_op(periodic_event_is_enabled(item), OP_EQ, 0);
    343    }
    344  }
    345 
    346 done:
    347  if (to_remove) {
    348    hs_metrics_service_free(&service);
    349    remove_service(get_hs_service_map(), to_remove);
    350  }
    351  hs_free_all();
    352 }
    353 
    354 #define PE_TEST(name) \
    355  { #name, test_pe_## name , TT_FORK, NULL, NULL }
    356 
    357 struct testcase_t periodic_event_tests[] = {
    358  PE_TEST(initialize),
    359  PE_TEST(launch),
    360  PE_TEST(get_roles),
    361  PE_TEST(hs_service),
    362 
    363  END_OF_TESTCASES
    364 };