Line data Source code
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 0 : dumb_event_fn(time_t now, const or_options_t *options)
30 : {
31 0 : (void) now;
32 0 : (void) options;
33 :
34 : /* Will get rescheduled in 300 seconds. It just can't be 0. */
35 0 : return 300;
36 : }
37 :
38 : static void
39 3 : register_dummy_hidden_service(hs_service_t *service)
40 : {
41 3 : memset(service, 0, sizeof(hs_service_t));
42 3 : memset(&service->keys.identity_pk, 'A', sizeof(service->keys.identity_pk));
43 3 : (void) register_service(get_hs_service_map(), service);
44 3 : }
45 :
46 : static void
47 1 : test_pe_initialize(void *arg)
48 : {
49 1 : (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 indepedently elsewhere. */
54 1 : initialize_periodic_events();
55 1 : periodic_events_connect_all();
56 1 : set_network_participation(false);
57 1 : rescan_periodic_events(get_options());
58 :
59 : /* Validate that all events have been set up. */
60 20 : for (int i = 0; mainloop_periodic_events[i].name; ++i) {
61 19 : periodic_event_item_t *item = &mainloop_periodic_events[i];
62 19 : tt_assert(item->ev);
63 19 : tt_assert(item->fn);
64 19 : 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 19 : tt_u64_op(item->roles, OP_NE, 0);
67 19 : int should_be_enabled = (item->roles & PERIODIC_EVENT_ROLE_ALL) &&
68 : !(item->flags & PERIODIC_EVENT_FLAG_NEED_NET);
69 19 : tt_uint_op(periodic_event_is_enabled(item), OP_EQ, should_be_enabled);
70 : }
71 :
72 1 : done:
73 1 : teardown_periodic_events();
74 1 : }
75 :
76 : static void
77 1 : test_pe_launch(void *arg)
78 : {
79 1 : hs_service_t service, *to_remove = NULL;
80 1 : or_options_t *options;
81 :
82 1 : (void) arg;
83 :
84 1 : hs_init();
85 : /* We need to put tor in hibernation live state so the events requiring
86 : * network gets enabled. */
87 1 : consider_hibernation(time(NULL));
88 :
89 1 : 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 20 : for (int i = 0; mainloop_periodic_events[i].name; ++i) {
95 19 : periodic_event_item_t *item = &mainloop_periodic_events[i];
96 19 : item->fn = dumb_event_fn;
97 : }
98 :
99 1 : options = get_options_mutable();
100 1 : options->SocksPort_set = 1;
101 1 : 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 1 : initialize_periodic_events();
115 1 : periodic_events_connect_all();
116 :
117 : /* Now that we've initialized, rescan the list to launch. */
118 1 : periodic_events_on_new_options(options);
119 :
120 1 : int mask = PERIODIC_EVENT_ROLE_CLIENT|PERIODIC_EVENT_ROLE_ALL|
121 : PERIODIC_EVENT_ROLE_NET_PARTICIPANT;
122 20 : for (int i = 0; mainloop_periodic_events[i].name; ++i) {
123 19 : periodic_event_item_t *item = &mainloop_periodic_events[i];
124 19 : int should_be_enabled = !!(item->roles & mask);
125 19 : 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 19 : tt_u64_op(item->last_action_time, OP_EQ, 0);
128 : }
129 :
130 : /* Remove Client but become a Relay. */
131 1 : options->SocksPort_set = 0;
132 1 : options->ORPort_set = 1;
133 1 : periodic_events_on_new_options(options);
134 :
135 1 : unsigned roles = get_my_roles(options);
136 1 : 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 20 : for (int i = 0; mainloop_periodic_events[i].name; ++i) {
141 19 : periodic_event_item_t *item = &mainloop_periodic_events[i];
142 : /* Only Client role should be disabled. */
143 19 : if (item->roles == PERIODIC_EVENT_ROLE_CLIENT) {
144 0 : tt_int_op(periodic_event_is_enabled(item), OP_EQ, 0);
145 : }
146 19 : if (item->roles & PERIODIC_EVENT_ROLE_RELAY) {
147 0 : tt_int_op(periodic_event_is_enabled(item), OP_EQ, 1);
148 : }
149 : /* Non Relay role should be disabled, except for Dirserver. */
150 19 : if (!(item->roles & roles)) {
151 19 : tt_int_op(periodic_event_is_enabled(item), OP_EQ, 0);
152 : }
153 : }
154 :
155 : /* Disable everything and we'll enable them ALL. */
156 1 : options->SocksPort_set = 0;
157 1 : options->ORPort_set = 0;
158 1 : options->DisableNetwork = 1;
159 1 : set_network_participation(false);
160 1 : periodic_events_on_new_options(options);
161 :
162 20 : for (int i = 0; mainloop_periodic_events[i].name; ++i) {
163 19 : periodic_event_item_t *item = &mainloop_periodic_events[i];
164 19 : int should_be_enabled = (item->roles & PERIODIC_EVENT_ROLE_ALL) &&
165 : !(item->flags & PERIODIC_EVENT_FLAG_NEED_NET);
166 19 : tt_int_op(periodic_event_is_enabled(item), OP_EQ, should_be_enabled);
167 : }
168 :
169 : /* Enable everything. */
170 1 : options->SocksPort_set = 1; options->ORPort_set = 1;
171 1 : options->BridgeRelay = 1; options->AuthoritativeDir = 1;
172 1 : options->V3AuthoritativeDir = 1; options->BridgeAuthoritativeDir = 1;
173 1 : options->DisableNetwork = 0;
174 1 : set_network_participation(true);
175 1 : register_dummy_hidden_service(&service);
176 1 : 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 1 : to_remove = &service;
182 :
183 20 : for (int i = 0; mainloop_periodic_events[i].name; ++i) {
184 19 : periodic_event_item_t *item = &mainloop_periodic_events[i];
185 19 : tt_int_op(periodic_event_is_enabled(item), OP_EQ,
186 : (item->roles != PERIODIC_EVENT_ROLE_CONTROLEV));
187 : }
188 :
189 1 : done:
190 0 : if (to_remove) {
191 1 : hs_metrics_service_free(&service);
192 1 : remove_service(get_hs_service_map(), to_remove);
193 : }
194 1 : hs_free_all();
195 1 : }
196 :
197 : static void
198 1 : test_pe_get_roles(void *arg)
199 : {
200 1 : int roles;
201 :
202 1 : (void) arg;
203 :
204 : /* Just so the HS global map exists. */
205 1 : hs_init();
206 :
207 1 : or_options_t *options = get_options_mutable();
208 1 : tt_assert(options);
209 1 : set_network_participation(true);
210 :
211 1 : const int ALL = PERIODIC_EVENT_ROLE_ALL |
212 : PERIODIC_EVENT_ROLE_NET_PARTICIPANT;
213 :
214 : /* Nothing configured, should be no roles. */
215 1 : tt_assert(net_is_disabled());
216 1 : roles = get_my_roles(options);
217 1 : tt_int_op(roles, OP_EQ, ALL);
218 :
219 : /* Indicate we have a SocksPort, roles should be come Client. */
220 1 : options->SocksPort_set = 1;
221 1 : roles = get_my_roles(options);
222 1 : 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 1 : options->ORPort_set = 1;
226 1 : roles = get_my_roles(options);
227 1 : 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 1 : options->BridgeRelay = 1;
233 1 : roles = get_my_roles(options);
234 1 : 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 1 : tt_assert(roles & PERIODIC_EVENT_ROLE_ROUTER);
239 : /* Unset client so we can solely test Router role. */
240 1 : options->SocksPort_set = 0;
241 1 : roles = get_my_roles(options);
242 1 : 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 1 : options->SocksPort_set = 0;
248 1 : options->ORPort_set = 0;
249 1 : options->BridgeRelay = 0;
250 1 : roles = get_my_roles(options);
251 1 : tt_int_op(roles, OP_EQ, ALL);
252 :
253 : /* Now upgrade to Dirauth. */
254 1 : options->DirPort_set = 1;
255 1 : options->AuthoritativeDir = 1;
256 1 : options->V3AuthoritativeDir = 1;
257 1 : roles = get_my_roles(options);
258 1 : tt_int_op(roles, OP_EQ,
259 : PERIODIC_EVENT_ROLE_DIRAUTH|PERIODIC_EVENT_ROLE_DIRSERVER|ALL);
260 1 : tt_assert(roles & PERIODIC_EVENT_ROLE_AUTHORITIES);
261 :
262 : /* Now Bridge Authority. */
263 1 : options->V3AuthoritativeDir = 0;
264 1 : options->BridgeAuthoritativeDir = 1;
265 1 : roles = get_my_roles(options);
266 1 : tt_int_op(roles, OP_EQ,
267 : PERIODIC_EVENT_ROLE_BRIDGEAUTH|PERIODIC_EVENT_ROLE_DIRSERVER|ALL);
268 1 : tt_assert(roles & PERIODIC_EVENT_ROLE_AUTHORITIES);
269 :
270 : /* Move that bridge auth to become a relay. */
271 1 : options->ORPort_set = 1;
272 1 : roles = get_my_roles(options);
273 1 : tt_int_op(roles, OP_EQ,
274 : (PERIODIC_EVENT_ROLE_BRIDGEAUTH | PERIODIC_EVENT_ROLE_RELAY
275 : | PERIODIC_EVENT_ROLE_DIRSERVER|ALL));
276 1 : tt_assert(roles & PERIODIC_EVENT_ROLE_AUTHORITIES);
277 :
278 : /* And now an Hidden service. */
279 1 : hs_service_t service;
280 1 : register_dummy_hidden_service(&service);
281 1 : roles = get_my_roles(options);
282 : /* Remove it now so the hs_free_all() doesn't try to free stack memory. */
283 1 : remove_service(get_hs_service_map(), &service);
284 1 : hs_metrics_service_free(&service);
285 1 : 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 1 : tt_assert(roles & PERIODIC_EVENT_ROLE_AUTHORITIES);
290 :
291 1 : done:
292 1 : hs_free_all();
293 1 : }
294 :
295 : static void
296 1 : test_pe_hs_service(void *arg)
297 : {
298 1 : hs_service_t service, *to_remove = NULL;
299 :
300 1 : (void) arg;
301 :
302 1 : hs_init();
303 : /* We need to put tor in hibernation live state so the events requiring
304 : * network gets enabled. */
305 1 : consider_hibernation(time(NULL));
306 : /* Initialize the events so we can enable them */
307 1 : initialize_periodic_events();
308 1 : 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 20 : for (int i = 0; mainloop_periodic_events[i].name; ++i) {
314 19 : periodic_event_item_t *item = &mainloop_periodic_events[i];
315 19 : item->fn = dumb_event_fn;
316 : }
317 :
318 : /* This should trigger a rescan of the list and enable the HS service
319 : * events. */
320 1 : 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 1 : to_remove = &service;
326 :
327 20 : for (int i = 0; mainloop_periodic_events[i].name; ++i) {
328 19 : periodic_event_item_t *item = &mainloop_periodic_events[i];
329 19 : if (item->roles & PERIODIC_EVENT_ROLE_HS_SERVICE) {
330 19 : tt_int_op(periodic_event_is_enabled(item), OP_EQ, 1);
331 : }
332 : }
333 1 : 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 1 : remove_service(get_hs_service_map(), &service);
338 1 : hs_metrics_service_free(&service);
339 20 : for (int i = 0; mainloop_periodic_events[i].name; ++i) {
340 19 : periodic_event_item_t *item = &mainloop_periodic_events[i];
341 19 : if (item->roles & PERIODIC_EVENT_ROLE_HS_SERVICE) {
342 19 : tt_int_op(periodic_event_is_enabled(item), OP_EQ, 0);
343 : }
344 : }
345 :
346 1 : done:
347 1 : if (to_remove) {
348 0 : hs_metrics_service_free(&service);
349 0 : remove_service(get_hs_service_map(), to_remove);
350 : }
351 1 : hs_free_all();
352 1 : }
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 : };
|