dispatch_test.c revision 1.2.2.2 1 /* $NetBSD: dispatch_test.c,v 1.2.2.2 2024/02/25 15:47:39 martin Exp $ */
2
3 /*
4 * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
5 *
6 * SPDX-License-Identifier: MPL-2.0
7 *
8 * This Source Code Form is subject to the terms of the Mozilla Public
9 * License, v. 2.0. If a copy of the MPL was not distributed with this
10 * file, you can obtain one at https://mozilla.org/MPL/2.0/.
11 *
12 * See the COPYRIGHT file distributed with this work for additional
13 * information regarding copyright ownership.
14 */
15
16 #include <inttypes.h>
17 #include <sched.h> /* IWYU pragma: keep */
18 #include <setjmp.h>
19 #include <stdarg.h>
20 #include <stdbool.h>
21 #include <stddef.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <unistd.h>
25 #include <uv.h>
26
27 #define UNIT_TESTING
28 #include <cmocka.h>
29
30 #include <isc/buffer.h>
31 #include <isc/managers.h>
32 #include <isc/refcount.h>
33 #include <isc/task.h>
34 #include <isc/util.h>
35
36 #include <dns/dispatch.h>
37 #include <dns/name.h>
38 #include <dns/view.h>
39
40 #include <tests/dns.h>
41
42 uv_sem_t sem;
43
44 /* Timeouts in miliseconds */
45 #define T_SERVER_INIT 5000
46 #define T_SERVER_IDLE 5000
47 #define T_SERVER_KEEPALIVE 5000
48 #define T_SERVER_ADVERTISED 5000
49
50 #define T_CLIENT_INIT 2000
51 #define T_CLIENT_IDLE 2000
52 #define T_CLIENT_KEEPALIVE 2000
53 #define T_CLIENT_ADVERTISED 2000
54
55 #define T_CLIENT_CONNECT 1000
56
57 dns_dispatchmgr_t *dispatchmgr = NULL;
58 dns_dispatchset_t *dset = NULL;
59 isc_nm_t *connect_nm = NULL;
60 static isc_sockaddr_t udp_server_addr;
61 static isc_sockaddr_t udp_connect_addr;
62 static isc_sockaddr_t tcp_server_addr;
63 static isc_sockaddr_t tcp_connect_addr;
64
65 const struct in6_addr in6addr_blackhole = { { { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
66 0, 0, 0, 0, 1 } } };
67
68 static int
69 setup_ephemeral_port(isc_sockaddr_t *addr, sa_family_t family) {
70 socklen_t addrlen = sizeof(*addr);
71 uv_os_sock_t fd;
72 int r;
73
74 isc_sockaddr_fromin6(addr, &in6addr_loopback, 0);
75
76 fd = socket(AF_INET6, family, 0);
77 if (fd < 0) {
78 perror("setup_ephemeral_port: socket()");
79 return (-1);
80 }
81
82 r = bind(fd, (const struct sockaddr *)&addr->type.sa,
83 sizeof(addr->type.sin6));
84 if (r != 0) {
85 perror("setup_ephemeral_port: bind()");
86 close(fd);
87 return (r);
88 }
89
90 r = getsockname(fd, (struct sockaddr *)&addr->type.sa, &addrlen);
91 if (r != 0) {
92 perror("setup_ephemeral_port: getsockname()");
93 close(fd);
94 return (r);
95 }
96
97 #if IPV6_RECVERR
98 #define setsockopt_on(socket, level, name) \
99 setsockopt(socket, level, name, &(int){ 1 }, sizeof(int))
100
101 r = setsockopt_on(fd, IPPROTO_IPV6, IPV6_RECVERR);
102 if (r != 0) {
103 perror("setup_ephemeral_port");
104 close(fd);
105 return (r);
106 }
107 #endif
108
109 return (fd);
110 }
111
112 static void
113 reset_testdata(void);
114
115 static int
116 _setup(void **state) {
117 uv_os_sock_t sock = -1;
118 int r;
119
120 udp_connect_addr = (isc_sockaddr_t){ .length = 0 };
121 isc_sockaddr_fromin6(&udp_connect_addr, &in6addr_loopback, 0);
122
123 tcp_connect_addr = (isc_sockaddr_t){ .length = 0 };
124 isc_sockaddr_fromin6(&tcp_connect_addr, &in6addr_loopback, 0);
125
126 udp_server_addr = (isc_sockaddr_t){ .length = 0 };
127 sock = setup_ephemeral_port(&udp_server_addr, SOCK_DGRAM);
128 if (sock < 0) {
129 return (-1);
130 }
131 close(sock);
132
133 tcp_server_addr = (isc_sockaddr_t){ .length = 0 };
134 sock = setup_ephemeral_port(&tcp_server_addr, SOCK_STREAM);
135 if (sock < 0) {
136 return (-1);
137 }
138 close(sock);
139
140 setup_managers(state);
141
142 /* Create a secondary network manager */
143 isc_managers_create(mctx, workers, 0, &connect_nm, NULL, NULL);
144
145 isc_nm_settimeouts(netmgr, T_SERVER_INIT, T_SERVER_IDLE,
146 T_SERVER_KEEPALIVE, T_SERVER_ADVERTISED);
147
148 /*
149 * Use shorter client-side timeouts, to ensure that clients
150 * time out before the server.
151 */
152 isc_nm_settimeouts(connect_nm, T_CLIENT_INIT, T_CLIENT_IDLE,
153 T_CLIENT_KEEPALIVE, T_CLIENT_ADVERTISED);
154
155 r = uv_sem_init(&sem, 0);
156 assert_int_equal(r, 0);
157
158 reset_testdata();
159
160 return (0);
161 }
162
163 static int
164 _teardown(void **state) {
165 uv_sem_destroy(&sem);
166
167 isc_managers_destroy(&connect_nm, NULL, NULL);
168 assert_null(connect_nm);
169
170 teardown_managers(state);
171
172 return (0);
173 }
174
175 static isc_result_t
176 make_dispatchset(unsigned int ndisps) {
177 isc_result_t result;
178 isc_sockaddr_t any;
179 dns_dispatch_t *disp = NULL;
180
181 result = dns_dispatchmgr_create(mctx, netmgr, &dispatchmgr);
182 if (result != ISC_R_SUCCESS) {
183 return (result);
184 }
185
186 isc_sockaddr_any(&any);
187 result = dns_dispatch_createudp(dispatchmgr, &any, &disp);
188 if (result != ISC_R_SUCCESS) {
189 return (result);
190 }
191
192 result = dns_dispatchset_create(mctx, disp, &dset, ndisps);
193 dns_dispatch_detach(&disp);
194
195 return (result);
196 }
197
198 static void
199 reset(void) {
200 if (dset != NULL) {
201 dns_dispatchset_destroy(&dset);
202 }
203 if (dispatchmgr != NULL) {
204 dns_dispatchmgr_detach(&dispatchmgr);
205 }
206 }
207
208 /* create dispatch set */
209 ISC_RUN_TEST_IMPL(dispatchset_create) {
210 isc_result_t result;
211
212 UNUSED(state);
213
214 result = make_dispatchset(1);
215 assert_int_equal(result, ISC_R_SUCCESS);
216 reset();
217
218 result = make_dispatchset(10);
219 assert_int_equal(result, ISC_R_SUCCESS);
220 reset();
221 }
222
223 /* test dispatch set round-robin */
224 ISC_RUN_TEST_IMPL(dispatchset_get) {
225 isc_result_t result;
226 dns_dispatch_t *d1, *d2, *d3, *d4, *d5;
227
228 UNUSED(state);
229
230 result = make_dispatchset(1);
231 assert_int_equal(result, ISC_R_SUCCESS);
232
233 d1 = dns_dispatchset_get(dset);
234 d2 = dns_dispatchset_get(dset);
235 d3 = dns_dispatchset_get(dset);
236 d4 = dns_dispatchset_get(dset);
237 d5 = dns_dispatchset_get(dset);
238
239 assert_ptr_equal(d1, d2);
240 assert_ptr_equal(d2, d3);
241 assert_ptr_equal(d3, d4);
242 assert_ptr_equal(d4, d5);
243
244 reset();
245
246 result = make_dispatchset(4);
247 assert_int_equal(result, ISC_R_SUCCESS);
248
249 d1 = dns_dispatchset_get(dset);
250 d2 = dns_dispatchset_get(dset);
251 d3 = dns_dispatchset_get(dset);
252 d4 = dns_dispatchset_get(dset);
253 d5 = dns_dispatchset_get(dset);
254
255 assert_ptr_equal(d1, d5);
256 assert_ptr_not_equal(d1, d2);
257 assert_ptr_not_equal(d2, d3);
258 assert_ptr_not_equal(d3, d4);
259 assert_ptr_not_equal(d4, d5);
260
261 reset();
262 }
263
264 struct {
265 atomic_uint_fast32_t responses;
266 atomic_uint_fast32_t result;
267 } testdata;
268
269 static dns_dispatch_t *dispatch = NULL;
270 static dns_dispentry_t *dispentry = NULL;
271 static atomic_bool first = true;
272
273 static void
274 reset_testdata(void) {
275 atomic_init(&testdata.responses, 0);
276 atomic_init(&testdata.result, ISC_R_UNSET);
277 }
278
279 static void
280 server_senddone(isc_nmhandle_t *handle, isc_result_t eresult, void *cbarg) {
281 UNUSED(handle);
282 UNUSED(eresult);
283 UNUSED(cbarg);
284
285 return;
286 }
287
288 static void
289 nameserver(isc_nmhandle_t *handle, isc_result_t eresult, isc_region_t *region,
290 void *cbarg) {
291 isc_region_t response;
292 static unsigned char buf1[16];
293 static unsigned char buf2[16];
294
295 UNUSED(eresult);
296 UNUSED(cbarg);
297
298 memmove(buf1, region->base, 12);
299 memset(buf1 + 12, 0, 4);
300 buf1[2] |= 0x80; /* qr=1 */
301
302 memmove(buf2, region->base, 12);
303 memset(buf2 + 12, 1, 4);
304 buf2[2] |= 0x80; /* qr=1 */
305
306 /*
307 * send message to be discarded.
308 */
309 response.base = buf1;
310 response.length = sizeof(buf1);
311 isc_nm_send(handle, &response, server_senddone, NULL);
312
313 /*
314 * send nextitem message.
315 */
316 response.base = buf2;
317 response.length = sizeof(buf2);
318 isc_nm_send(handle, &response, server_senddone, NULL);
319 }
320
321 static isc_result_t
322 accept_cb(isc_nmhandle_t *handle, isc_result_t eresult, void *cbarg) {
323 UNUSED(handle);
324 UNUSED(cbarg);
325
326 return (eresult);
327 }
328
329 static void
330 noop_nameserver(isc_nmhandle_t *handle, isc_result_t eresult,
331 isc_region_t *region, void *cbarg) {
332 UNUSED(handle);
333 UNUSED(eresult);
334 UNUSED(region);
335 UNUSED(cbarg);
336 }
337
338 static void
339 response_getnext(isc_result_t result, isc_region_t *region, void *arg) {
340 UNUSED(region);
341 UNUSED(arg);
342
343 atomic_fetch_add_relaxed(&testdata.responses, 1);
344
345 if (atomic_compare_exchange_strong(&first, &(bool){ true }, false)) {
346 result = dns_dispatch_getnext(dispentry);
347 assert_int_equal(result, ISC_R_SUCCESS);
348 } else {
349 uv_sem_post(&sem);
350 }
351 }
352
353 static void
354 response(isc_result_t eresult, isc_region_t *region, void *arg) {
355 UNUSED(region);
356 UNUSED(arg);
357
358 switch (eresult) {
359 case ISC_R_EOF:
360 case ISC_R_CANCELED:
361 case ISC_R_SHUTTINGDOWN:
362 break;
363 default:
364 atomic_fetch_add_relaxed(&testdata.responses, 1);
365 atomic_store_relaxed(&testdata.result, eresult);
366 }
367
368 uv_sem_post(&sem);
369 }
370
371 static void
372 response_timeout(isc_result_t eresult, isc_region_t *region, void *arg) {
373 UNUSED(region);
374 UNUSED(arg);
375
376 atomic_store_relaxed(&testdata.result, eresult);
377
378 uv_sem_post(&sem);
379 }
380
381 static void
382 connected(isc_result_t eresult, isc_region_t *region, void *cbarg) {
383 isc_region_t *r = (isc_region_t *)cbarg;
384
385 UNUSED(eresult);
386 UNUSED(region);
387
388 dns_dispatch_send(dispentry, r);
389 }
390
391 static void
392 client_senddone(isc_result_t eresult, isc_region_t *region, void *cbarg) {
393 UNUSED(eresult);
394 UNUSED(region);
395 UNUSED(cbarg);
396
397 return;
398 }
399
400 static void
401 timeout_connected(isc_result_t eresult, isc_region_t *region, void *cbarg) {
402 UNUSED(region);
403 UNUSED(cbarg);
404
405 atomic_store_relaxed(&testdata.result, eresult);
406
407 uv_sem_post(&sem);
408 }
409
410 ISC_RUN_TEST_IMPL(dispatch_timeout_tcp_connect) {
411 isc_result_t result;
412 isc_region_t region;
413 unsigned char rbuf[12] = { 0 };
414 unsigned char message[12] = { 0 };
415 uint16_t id;
416
417 UNUSED(state);
418
419 tcp_connect_addr = (isc_sockaddr_t){ .length = 0 };
420 isc_sockaddr_fromin6(&tcp_connect_addr, &in6addr_blackhole, 0);
421
422 result = dns_dispatchmgr_create(mctx, connect_nm, &dispatchmgr);
423 assert_int_equal(result, ISC_R_SUCCESS);
424
425 result = dns_dispatch_createtcp(dispatchmgr, &tcp_connect_addr,
426 &tcp_server_addr, &dispatch);
427 assert_int_equal(result, ISC_R_SUCCESS);
428
429 region.base = rbuf;
430 region.length = sizeof(rbuf);
431
432 result = dns_dispatch_add(dispatch, 0, T_CLIENT_CONNECT,
433 &tcp_server_addr, timeout_connected,
434 client_senddone, response, ®ion, &id,
435 &dispentry);
436 assert_int_equal(result, ISC_R_SUCCESS);
437
438 memset(message, 0, sizeof(message));
439 message[0] = (id >> 8) & 0xff;
440 message[1] = id & 0xff;
441
442 region.base = message;
443 region.length = sizeof(message);
444
445 dns_dispatch_connect(dispentry);
446
447 uv_sem_wait(&sem);
448
449 dns_dispatch_done(&dispentry);
450
451 dns_dispatch_detach(&dispatch);
452 dns_dispatchmgr_detach(&dispatchmgr);
453
454 /* Skip if the IPv6 is not available or not blackholed */
455
456 result = atomic_load_acquire(&testdata.result);
457 if (result == ISC_R_ADDRNOTAVAIL || result == ISC_R_CONNREFUSED) {
458 skip();
459 return;
460 }
461
462 assert_int_equal(result, ISC_R_TIMEDOUT);
463 }
464
465 ISC_RUN_TEST_IMPL(dispatch_timeout_tcp_response) {
466 isc_result_t result;
467 isc_region_t region;
468 unsigned char rbuf[12] = { 0 };
469 unsigned char message[12] = { 0 };
470 uint16_t id;
471 isc_nmsocket_t *sock = NULL;
472
473 UNUSED(state);
474
475 tcp_connect_addr = (isc_sockaddr_t){ .length = 0 };
476 isc_sockaddr_fromin6(&tcp_connect_addr, &in6addr_loopback, 0);
477
478 result = dns_dispatchmgr_create(mctx, connect_nm, &dispatchmgr);
479 assert_int_equal(result, ISC_R_SUCCESS);
480
481 result = dns_dispatch_createtcp(dispatchmgr, &tcp_connect_addr,
482 &tcp_server_addr, &dispatch);
483 assert_int_equal(result, ISC_R_SUCCESS);
484
485 result = isc_nm_listentcpdns(netmgr, &tcp_server_addr, noop_nameserver,
486 NULL, accept_cb, NULL, 0, 0, NULL, &sock);
487 assert_int_equal(result, ISC_R_SUCCESS);
488
489 region.base = rbuf;
490 region.length = sizeof(rbuf);
491
492 result = dns_dispatch_add(dispatch, 0, T_CLIENT_CONNECT,
493 &tcp_server_addr, connected, client_senddone,
494 response_timeout, ®ion, &id, &dispentry);
495 assert_int_equal(result, ISC_R_SUCCESS);
496
497 memset(message, 0, sizeof(message));
498 message[0] = (id >> 8) & 0xff;
499 message[1] = id & 0xff;
500
501 region.base = message;
502 region.length = sizeof(message);
503
504 dns_dispatch_connect(dispentry);
505
506 uv_sem_wait(&sem);
507
508 assert_int_equal(atomic_load_acquire(&testdata.result), ISC_R_TIMEDOUT);
509
510 isc_nm_stoplistening(sock);
511 isc_nmsocket_close(&sock);
512 assert_null(sock);
513
514 dns_dispatch_done(&dispentry);
515
516 dns_dispatch_detach(&dispatch);
517 dns_dispatchmgr_detach(&dispatchmgr);
518 }
519
520 ISC_RUN_TEST_IMPL(dispatch_tcp_response) {
521 isc_result_t result;
522 isc_region_t region;
523 unsigned char rbuf[12] = { 0 };
524 unsigned char message[12] = { 0 };
525 uint16_t id;
526 isc_nmsocket_t *sock = NULL;
527
528 UNUSED(state);
529
530 tcp_connect_addr = (isc_sockaddr_t){ .length = 0 };
531 isc_sockaddr_fromin6(&tcp_connect_addr, &in6addr_loopback, 0);
532
533 result = dns_dispatchmgr_create(mctx, connect_nm, &dispatchmgr);
534 assert_int_equal(result, ISC_R_SUCCESS);
535
536 result = dns_dispatch_createtcp(dispatchmgr, &tcp_connect_addr,
537 &tcp_server_addr, &dispatch);
538 assert_int_equal(result, ISC_R_SUCCESS);
539
540 result = isc_nm_listentcpdns(netmgr, &tcp_server_addr, nameserver, NULL,
541 accept_cb, NULL, 0, 0, NULL, &sock);
542 assert_int_equal(result, ISC_R_SUCCESS);
543
544 region.base = rbuf;
545 region.length = sizeof(rbuf);
546
547 result = dns_dispatch_add(dispatch, 0, T_CLIENT_CONNECT,
548 &tcp_server_addr, connected, client_senddone,
549 response, ®ion, &id, &dispentry);
550 assert_int_equal(result, ISC_R_SUCCESS);
551
552 memset(message, 0, sizeof(message));
553 message[0] = (id >> 8) & 0xff;
554 message[1] = id & 0xff;
555
556 region.base = message;
557 region.length = sizeof(message);
558
559 dns_dispatch_connect(dispentry);
560
561 uv_sem_wait(&sem);
562
563 assert_in_range(atomic_load_acquire(&testdata.responses), 1, 2);
564 assert_int_equal(atomic_load_acquire(&testdata.result), ISC_R_SUCCESS);
565
566 /* Cleanup */
567
568 isc_nm_stoplistening(sock);
569 isc_nmsocket_close(&sock);
570 assert_null(sock);
571
572 dns_dispatch_done(&dispentry);
573
574 dns_dispatch_detach(&dispatch);
575 dns_dispatchmgr_detach(&dispatchmgr);
576 }
577
578 ISC_RUN_TEST_IMPL(dispatch_timeout_udp_response) {
579 isc_result_t result;
580 isc_region_t region;
581 unsigned char rbuf[12] = { 0 };
582 unsigned char message[12] = { 0 };
583 uint16_t id;
584 isc_nmsocket_t *sock = NULL;
585
586 UNUSED(state);
587
588 udp_connect_addr = (isc_sockaddr_t){ .length = 0 };
589 isc_sockaddr_fromin6(&udp_connect_addr, &in6addr_loopback, 0);
590
591 result = dns_dispatchmgr_create(mctx, connect_nm, &dispatchmgr);
592 assert_int_equal(result, ISC_R_SUCCESS);
593
594 result = dns_dispatch_createudp(dispatchmgr, &tcp_connect_addr,
595 &dispatch);
596 assert_int_equal(result, ISC_R_SUCCESS);
597
598 result = isc_nm_listenudp(netmgr, &udp_server_addr, noop_nameserver,
599 NULL, 0, &sock);
600 assert_int_equal(result, ISC_R_SUCCESS);
601
602 region.base = rbuf;
603 region.length = sizeof(rbuf);
604
605 result = dns_dispatch_add(dispatch, 0, T_CLIENT_CONNECT,
606 &udp_server_addr, connected, client_senddone,
607 response_timeout, ®ion, &id, &dispentry);
608 assert_int_equal(result, ISC_R_SUCCESS);
609
610 memset(message, 0, sizeof(message));
611 message[0] = (id >> 8) & 0xff;
612 message[1] = id & 0xff;
613
614 region.base = message;
615 region.length = sizeof(message);
616
617 dns_dispatch_connect(dispentry);
618
619 uv_sem_wait(&sem);
620
621 assert_int_equal(atomic_load_acquire(&testdata.result), ISC_R_TIMEDOUT);
622
623 isc_nm_stoplistening(sock);
624 isc_nmsocket_close(&sock);
625 assert_null(sock);
626
627 dns_dispatch_done(&dispentry);
628
629 dns_dispatch_detach(&dispatch);
630 dns_dispatchmgr_detach(&dispatchmgr);
631 }
632
633 /* test dispatch getnext */
634 ISC_RUN_TEST_IMPL(dispatch_getnext) {
635 isc_result_t result;
636 isc_region_t region;
637 isc_nmsocket_t *sock = NULL;
638 unsigned char message[12] = { 0 };
639 unsigned char rbuf[12] = { 0 };
640 uint16_t id;
641
642 UNUSED(state);
643
644 result = dns_dispatchmgr_create(mctx, connect_nm, &dispatchmgr);
645 assert_int_equal(result, ISC_R_SUCCESS);
646
647 result = dns_dispatch_createudp(dispatchmgr, &udp_connect_addr,
648 &dispatch);
649 assert_int_equal(result, ISC_R_SUCCESS);
650
651 /*
652 * Create a local udp nameserver on the loopback.
653 */
654 result = isc_nm_listenudp(netmgr, &udp_server_addr, nameserver, NULL, 0,
655 &sock);
656 assert_int_equal(result, ISC_R_SUCCESS);
657
658 region.base = rbuf;
659 region.length = sizeof(rbuf);
660 result = dns_dispatch_add(dispatch, 0, T_CLIENT_CONNECT,
661 &udp_server_addr, connected, client_senddone,
662 response_getnext, ®ion, &id, &dispentry);
663 assert_int_equal(result, ISC_R_SUCCESS);
664
665 memset(message, 0, sizeof(message));
666 message[0] = (id >> 8) & 0xff;
667 message[1] = id & 0xff;
668
669 region.base = message;
670 region.length = sizeof(message);
671
672 dns_dispatch_connect(dispentry);
673
674 uv_sem_wait(&sem);
675
676 assert_int_equal(atomic_load_acquire(&testdata.responses), 2);
677
678 /* Cleanup */
679 isc_nm_stoplistening(sock);
680 isc_nmsocket_close(&sock);
681 assert_null(sock);
682
683 dns_dispatch_done(&dispentry);
684 dns_dispatch_detach(&dispatch);
685 dns_dispatchmgr_detach(&dispatchmgr);
686 }
687
688 ISC_TEST_LIST_START
689
690 ISC_TEST_ENTRY_CUSTOM(dispatch_timeout_tcp_connect, _setup, _teardown)
691 ISC_TEST_ENTRY_CUSTOM(dispatch_timeout_tcp_response, _setup, _teardown)
692 ISC_TEST_ENTRY_CUSTOM(dispatch_tcp_response, _setup, _teardown)
693 ISC_TEST_ENTRY_CUSTOM(dispatch_timeout_udp_response, _setup, _teardown)
694 ISC_TEST_ENTRY_CUSTOM(dispatchset_create, _setup, _teardown)
695 ISC_TEST_ENTRY_CUSTOM(dispatchset_get, _setup, _teardown)
696 ISC_TEST_ENTRY_CUSTOM(dispatch_getnext, _setup, _teardown)
697
698 ISC_TEST_LIST_END
699
700 ISC_TEST_MAIN
701