summaryrefslogtreecommitdiffstats
path: root/tests/dns/dispatch_test.c
diff options
context:
space:
mode:
Diffstat (limited to 'tests/dns/dispatch_test.c')
-rw-r--r--tests/dns/dispatch_test.c698
1 files changed, 698 insertions, 0 deletions
diff --git a/tests/dns/dispatch_test.c b/tests/dns/dispatch_test.c
new file mode 100644
index 0000000..c101744
--- /dev/null
+++ b/tests/dns/dispatch_test.c
@@ -0,0 +1,698 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#include <inttypes.h>
+#include <sched.h> /* IWYU pragma: keep */
+#include <setjmp.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <uv.h>
+
+#define UNIT_TESTING
+#include <cmocka.h>
+
+#include <isc/buffer.h>
+#include <isc/managers.h>
+#include <isc/refcount.h>
+#include <isc/task.h>
+#include <isc/util.h>
+
+#include <dns/dispatch.h>
+#include <dns/name.h>
+#include <dns/view.h>
+
+#include <tests/dns.h>
+
+uv_sem_t sem;
+
+/* Timeouts in miliseconds */
+#define T_SERVER_INIT 5000
+#define T_SERVER_IDLE 5000
+#define T_SERVER_KEEPALIVE 5000
+#define T_SERVER_ADVERTISED 5000
+
+#define T_CLIENT_INIT 2000
+#define T_CLIENT_IDLE 2000
+#define T_CLIENT_KEEPALIVE 2000
+#define T_CLIENT_ADVERTISED 2000
+
+#define T_CLIENT_CONNECT 1000
+
+dns_dispatchmgr_t *dispatchmgr = NULL;
+dns_dispatchset_t *dset = NULL;
+isc_nm_t *connect_nm = NULL;
+static isc_sockaddr_t udp_server_addr;
+static isc_sockaddr_t udp_connect_addr;
+static isc_sockaddr_t tcp_server_addr;
+static isc_sockaddr_t tcp_connect_addr;
+
+const struct in6_addr in6addr_blackhole = { { { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1 } } };
+
+static int
+setup_ephemeral_port(isc_sockaddr_t *addr, sa_family_t family) {
+ socklen_t addrlen = sizeof(*addr);
+ uv_os_sock_t fd;
+ int r;
+
+ isc_sockaddr_fromin6(addr, &in6addr_loopback, 0);
+
+ fd = socket(AF_INET6, family, 0);
+ if (fd < 0) {
+ perror("setup_ephemeral_port: socket()");
+ return (-1);
+ }
+
+ r = bind(fd, (const struct sockaddr *)&addr->type.sa,
+ sizeof(addr->type.sin6));
+ if (r != 0) {
+ perror("setup_ephemeral_port: bind()");
+ close(fd);
+ return (r);
+ }
+
+ r = getsockname(fd, (struct sockaddr *)&addr->type.sa, &addrlen);
+ if (r != 0) {
+ perror("setup_ephemeral_port: getsockname()");
+ close(fd);
+ return (r);
+ }
+
+#if IPV6_RECVERR
+#define setsockopt_on(socket, level, name) \
+ setsockopt(socket, level, name, &(int){ 1 }, sizeof(int))
+
+ r = setsockopt_on(fd, IPPROTO_IPV6, IPV6_RECVERR);
+ if (r != 0) {
+ perror("setup_ephemeral_port");
+ close(fd);
+ return (r);
+ }
+#endif
+
+ return (fd);
+}
+
+static void
+reset_testdata(void);
+
+static int
+_setup(void **state) {
+ uv_os_sock_t sock = -1;
+ int r;
+
+ udp_connect_addr = (isc_sockaddr_t){ .length = 0 };
+ isc_sockaddr_fromin6(&udp_connect_addr, &in6addr_loopback, 0);
+
+ tcp_connect_addr = (isc_sockaddr_t){ .length = 0 };
+ isc_sockaddr_fromin6(&tcp_connect_addr, &in6addr_loopback, 0);
+
+ udp_server_addr = (isc_sockaddr_t){ .length = 0 };
+ sock = setup_ephemeral_port(&udp_server_addr, SOCK_DGRAM);
+ if (sock < 0) {
+ return (-1);
+ }
+ close(sock);
+
+ tcp_server_addr = (isc_sockaddr_t){ .length = 0 };
+ sock = setup_ephemeral_port(&tcp_server_addr, SOCK_STREAM);
+ if (sock < 0) {
+ return (-1);
+ }
+ close(sock);
+
+ setup_managers(state);
+
+ /* Create a secondary network manager */
+ isc_managers_create(mctx, workers, 0, &connect_nm, NULL, NULL);
+
+ isc_nm_settimeouts(netmgr, T_SERVER_INIT, T_SERVER_IDLE,
+ T_SERVER_KEEPALIVE, T_SERVER_ADVERTISED);
+
+ /*
+ * Use shorter client-side timeouts, to ensure that clients
+ * time out before the server.
+ */
+ isc_nm_settimeouts(connect_nm, T_CLIENT_INIT, T_CLIENT_IDLE,
+ T_CLIENT_KEEPALIVE, T_CLIENT_ADVERTISED);
+
+ r = uv_sem_init(&sem, 0);
+ assert_int_equal(r, 0);
+
+ reset_testdata();
+
+ return (0);
+}
+
+static int
+_teardown(void **state) {
+ uv_sem_destroy(&sem);
+
+ isc_managers_destroy(&connect_nm, NULL, NULL);
+ assert_null(connect_nm);
+
+ teardown_managers(state);
+
+ return (0);
+}
+
+static isc_result_t
+make_dispatchset(unsigned int ndisps) {
+ isc_result_t result;
+ isc_sockaddr_t any;
+ dns_dispatch_t *disp = NULL;
+
+ result = dns_dispatchmgr_create(mctx, netmgr, &dispatchmgr);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+
+ isc_sockaddr_any(&any);
+ result = dns_dispatch_createudp(dispatchmgr, &any, &disp);
+ if (result != ISC_R_SUCCESS) {
+ return (result);
+ }
+
+ result = dns_dispatchset_create(mctx, disp, &dset, ndisps);
+ dns_dispatch_detach(&disp);
+
+ return (result);
+}
+
+static void
+reset(void) {
+ if (dset != NULL) {
+ dns_dispatchset_destroy(&dset);
+ }
+ if (dispatchmgr != NULL) {
+ dns_dispatchmgr_detach(&dispatchmgr);
+ }
+}
+
+/* create dispatch set */
+ISC_RUN_TEST_IMPL(dispatchset_create) {
+ isc_result_t result;
+
+ UNUSED(state);
+
+ result = make_dispatchset(1);
+ assert_int_equal(result, ISC_R_SUCCESS);
+ reset();
+
+ result = make_dispatchset(10);
+ assert_int_equal(result, ISC_R_SUCCESS);
+ reset();
+}
+
+/* test dispatch set round-robin */
+ISC_RUN_TEST_IMPL(dispatchset_get) {
+ isc_result_t result;
+ dns_dispatch_t *d1, *d2, *d3, *d4, *d5;
+
+ UNUSED(state);
+
+ result = make_dispatchset(1);
+ assert_int_equal(result, ISC_R_SUCCESS);
+
+ d1 = dns_dispatchset_get(dset);
+ d2 = dns_dispatchset_get(dset);
+ d3 = dns_dispatchset_get(dset);
+ d4 = dns_dispatchset_get(dset);
+ d5 = dns_dispatchset_get(dset);
+
+ assert_ptr_equal(d1, d2);
+ assert_ptr_equal(d2, d3);
+ assert_ptr_equal(d3, d4);
+ assert_ptr_equal(d4, d5);
+
+ reset();
+
+ result = make_dispatchset(4);
+ assert_int_equal(result, ISC_R_SUCCESS);
+
+ d1 = dns_dispatchset_get(dset);
+ d2 = dns_dispatchset_get(dset);
+ d3 = dns_dispatchset_get(dset);
+ d4 = dns_dispatchset_get(dset);
+ d5 = dns_dispatchset_get(dset);
+
+ assert_ptr_equal(d1, d5);
+ assert_ptr_not_equal(d1, d2);
+ assert_ptr_not_equal(d2, d3);
+ assert_ptr_not_equal(d3, d4);
+ assert_ptr_not_equal(d4, d5);
+
+ reset();
+}
+
+struct {
+ atomic_uint_fast32_t responses;
+ atomic_uint_fast32_t result;
+} testdata;
+
+static dns_dispatch_t *dispatch = NULL;
+static dns_dispentry_t *dispentry = NULL;
+static atomic_bool first = true;
+
+static void
+reset_testdata(void) {
+ atomic_init(&testdata.responses, 0);
+ atomic_init(&testdata.result, ISC_R_UNSET);
+}
+
+static void
+server_senddone(isc_nmhandle_t *handle, isc_result_t eresult, void *cbarg) {
+ UNUSED(handle);
+ UNUSED(eresult);
+ UNUSED(cbarg);
+
+ return;
+}
+
+static void
+nameserver(isc_nmhandle_t *handle, isc_result_t eresult, isc_region_t *region,
+ void *cbarg) {
+ isc_region_t response;
+ static unsigned char buf1[16];
+ static unsigned char buf2[16];
+
+ UNUSED(eresult);
+ UNUSED(cbarg);
+
+ memmove(buf1, region->base, 12);
+ memset(buf1 + 12, 0, 4);
+ buf1[2] |= 0x80; /* qr=1 */
+
+ memmove(buf2, region->base, 12);
+ memset(buf2 + 12, 1, 4);
+ buf2[2] |= 0x80; /* qr=1 */
+
+ /*
+ * send message to be discarded.
+ */
+ response.base = buf1;
+ response.length = sizeof(buf1);
+ isc_nm_send(handle, &response, server_senddone, NULL);
+
+ /*
+ * send nextitem message.
+ */
+ response.base = buf2;
+ response.length = sizeof(buf2);
+ isc_nm_send(handle, &response, server_senddone, NULL);
+}
+
+static isc_result_t
+accept_cb(isc_nmhandle_t *handle, isc_result_t eresult, void *cbarg) {
+ UNUSED(handle);
+ UNUSED(cbarg);
+
+ return (eresult);
+}
+
+static void
+noop_nameserver(isc_nmhandle_t *handle, isc_result_t eresult,
+ isc_region_t *region, void *cbarg) {
+ UNUSED(handle);
+ UNUSED(eresult);
+ UNUSED(region);
+ UNUSED(cbarg);
+}
+
+static void
+response_getnext(isc_result_t result, isc_region_t *region, void *arg) {
+ UNUSED(region);
+ UNUSED(arg);
+
+ atomic_fetch_add_relaxed(&testdata.responses, 1);
+
+ if (atomic_compare_exchange_strong(&first, &(bool){ true }, false)) {
+ result = dns_dispatch_getnext(dispentry);
+ assert_int_equal(result, ISC_R_SUCCESS);
+ } else {
+ uv_sem_post(&sem);
+ }
+}
+
+static void
+response(isc_result_t eresult, isc_region_t *region, void *arg) {
+ UNUSED(region);
+ UNUSED(arg);
+
+ switch (eresult) {
+ case ISC_R_EOF:
+ case ISC_R_CANCELED:
+ case ISC_R_SHUTTINGDOWN:
+ break;
+ default:
+ atomic_fetch_add_relaxed(&testdata.responses, 1);
+ atomic_store_relaxed(&testdata.result, eresult);
+ }
+
+ uv_sem_post(&sem);
+}
+
+static void
+response_timeout(isc_result_t eresult, isc_region_t *region, void *arg) {
+ UNUSED(region);
+ UNUSED(arg);
+
+ atomic_store_relaxed(&testdata.result, eresult);
+
+ uv_sem_post(&sem);
+}
+
+static void
+connected(isc_result_t eresult, isc_region_t *region, void *cbarg) {
+ isc_region_t *r = (isc_region_t *)cbarg;
+
+ UNUSED(eresult);
+ UNUSED(region);
+
+ dns_dispatch_send(dispentry, r);
+}
+
+static void
+client_senddone(isc_result_t eresult, isc_region_t *region, void *cbarg) {
+ UNUSED(eresult);
+ UNUSED(region);
+ UNUSED(cbarg);
+
+ return;
+}
+
+static void
+timeout_connected(isc_result_t eresult, isc_region_t *region, void *cbarg) {
+ UNUSED(region);
+ UNUSED(cbarg);
+
+ atomic_store_relaxed(&testdata.result, eresult);
+
+ uv_sem_post(&sem);
+}
+
+ISC_RUN_TEST_IMPL(dispatch_timeout_tcp_connect) {
+ isc_result_t result;
+ isc_region_t region;
+ unsigned char rbuf[12] = { 0 };
+ unsigned char message[12] = { 0 };
+ uint16_t id;
+
+ UNUSED(state);
+
+ tcp_connect_addr = (isc_sockaddr_t){ .length = 0 };
+ isc_sockaddr_fromin6(&tcp_connect_addr, &in6addr_blackhole, 0);
+
+ result = dns_dispatchmgr_create(mctx, connect_nm, &dispatchmgr);
+ assert_int_equal(result, ISC_R_SUCCESS);
+
+ result = dns_dispatch_createtcp(dispatchmgr, &tcp_connect_addr,
+ &tcp_server_addr, &dispatch);
+ assert_int_equal(result, ISC_R_SUCCESS);
+
+ region.base = rbuf;
+ region.length = sizeof(rbuf);
+
+ result = dns_dispatch_add(dispatch, 0, T_CLIENT_CONNECT,
+ &tcp_server_addr, timeout_connected,
+ client_senddone, response, &region, &id,
+ &dispentry);
+ assert_int_equal(result, ISC_R_SUCCESS);
+
+ memset(message, 0, sizeof(message));
+ message[0] = (id >> 8) & 0xff;
+ message[1] = id & 0xff;
+
+ region.base = message;
+ region.length = sizeof(message);
+
+ dns_dispatch_connect(dispentry);
+
+ uv_sem_wait(&sem);
+
+ dns_dispatch_done(&dispentry);
+
+ dns_dispatch_detach(&dispatch);
+ dns_dispatchmgr_detach(&dispatchmgr);
+
+ /* Skip if the IPv6 is not available or not blackholed */
+
+ result = atomic_load_acquire(&testdata.result);
+ if (result == ISC_R_ADDRNOTAVAIL || result == ISC_R_CONNREFUSED) {
+ skip();
+ return;
+ }
+
+ assert_int_equal(result, ISC_R_TIMEDOUT);
+}
+
+ISC_RUN_TEST_IMPL(dispatch_timeout_tcp_response) {
+ isc_result_t result;
+ isc_region_t region;
+ unsigned char rbuf[12] = { 0 };
+ unsigned char message[12] = { 0 };
+ uint16_t id;
+ isc_nmsocket_t *sock = NULL;
+
+ UNUSED(state);
+
+ tcp_connect_addr = (isc_sockaddr_t){ .length = 0 };
+ isc_sockaddr_fromin6(&tcp_connect_addr, &in6addr_loopback, 0);
+
+ result = dns_dispatchmgr_create(mctx, connect_nm, &dispatchmgr);
+ assert_int_equal(result, ISC_R_SUCCESS);
+
+ result = dns_dispatch_createtcp(dispatchmgr, &tcp_connect_addr,
+ &tcp_server_addr, &dispatch);
+ assert_int_equal(result, ISC_R_SUCCESS);
+
+ result = isc_nm_listentcpdns(netmgr, &tcp_server_addr, noop_nameserver,
+ NULL, accept_cb, NULL, 0, 0, NULL, &sock);
+ assert_int_equal(result, ISC_R_SUCCESS);
+
+ region.base = rbuf;
+ region.length = sizeof(rbuf);
+
+ result = dns_dispatch_add(dispatch, 0, T_CLIENT_CONNECT,
+ &tcp_server_addr, connected, client_senddone,
+ response_timeout, &region, &id, &dispentry);
+ assert_int_equal(result, ISC_R_SUCCESS);
+
+ memset(message, 0, sizeof(message));
+ message[0] = (id >> 8) & 0xff;
+ message[1] = id & 0xff;
+
+ region.base = message;
+ region.length = sizeof(message);
+
+ dns_dispatch_connect(dispentry);
+
+ uv_sem_wait(&sem);
+
+ assert_int_equal(atomic_load_acquire(&testdata.result), ISC_R_TIMEDOUT);
+
+ isc_nm_stoplistening(sock);
+ isc_nmsocket_close(&sock);
+ assert_null(sock);
+
+ dns_dispatch_done(&dispentry);
+
+ dns_dispatch_detach(&dispatch);
+ dns_dispatchmgr_detach(&dispatchmgr);
+}
+
+ISC_RUN_TEST_IMPL(dispatch_tcp_response) {
+ isc_result_t result;
+ isc_region_t region;
+ unsigned char rbuf[12] = { 0 };
+ unsigned char message[12] = { 0 };
+ uint16_t id;
+ isc_nmsocket_t *sock = NULL;
+
+ UNUSED(state);
+
+ tcp_connect_addr = (isc_sockaddr_t){ .length = 0 };
+ isc_sockaddr_fromin6(&tcp_connect_addr, &in6addr_loopback, 0);
+
+ result = dns_dispatchmgr_create(mctx, connect_nm, &dispatchmgr);
+ assert_int_equal(result, ISC_R_SUCCESS);
+
+ result = dns_dispatch_createtcp(dispatchmgr, &tcp_connect_addr,
+ &tcp_server_addr, &dispatch);
+ assert_int_equal(result, ISC_R_SUCCESS);
+
+ result = isc_nm_listentcpdns(netmgr, &tcp_server_addr, nameserver, NULL,
+ accept_cb, NULL, 0, 0, NULL, &sock);
+ assert_int_equal(result, ISC_R_SUCCESS);
+
+ region.base = rbuf;
+ region.length = sizeof(rbuf);
+
+ result = dns_dispatch_add(dispatch, 0, T_CLIENT_CONNECT,
+ &tcp_server_addr, connected, client_senddone,
+ response, &region, &id, &dispentry);
+ assert_int_equal(result, ISC_R_SUCCESS);
+
+ memset(message, 0, sizeof(message));
+ message[0] = (id >> 8) & 0xff;
+ message[1] = id & 0xff;
+
+ region.base = message;
+ region.length = sizeof(message);
+
+ dns_dispatch_connect(dispentry);
+
+ uv_sem_wait(&sem);
+
+ assert_in_range(atomic_load_acquire(&testdata.responses), 1, 2);
+ assert_int_equal(atomic_load_acquire(&testdata.result), ISC_R_SUCCESS);
+
+ /* Cleanup */
+
+ isc_nm_stoplistening(sock);
+ isc_nmsocket_close(&sock);
+ assert_null(sock);
+
+ dns_dispatch_done(&dispentry);
+
+ dns_dispatch_detach(&dispatch);
+ dns_dispatchmgr_detach(&dispatchmgr);
+}
+
+ISC_RUN_TEST_IMPL(dispatch_timeout_udp_response) {
+ isc_result_t result;
+ isc_region_t region;
+ unsigned char rbuf[12] = { 0 };
+ unsigned char message[12] = { 0 };
+ uint16_t id;
+ isc_nmsocket_t *sock = NULL;
+
+ UNUSED(state);
+
+ udp_connect_addr = (isc_sockaddr_t){ .length = 0 };
+ isc_sockaddr_fromin6(&udp_connect_addr, &in6addr_loopback, 0);
+
+ result = dns_dispatchmgr_create(mctx, connect_nm, &dispatchmgr);
+ assert_int_equal(result, ISC_R_SUCCESS);
+
+ result = dns_dispatch_createudp(dispatchmgr, &tcp_connect_addr,
+ &dispatch);
+ assert_int_equal(result, ISC_R_SUCCESS);
+
+ result = isc_nm_listenudp(netmgr, &udp_server_addr, noop_nameserver,
+ NULL, 0, &sock);
+ assert_int_equal(result, ISC_R_SUCCESS);
+
+ region.base = rbuf;
+ region.length = sizeof(rbuf);
+
+ result = dns_dispatch_add(dispatch, 0, T_CLIENT_CONNECT,
+ &udp_server_addr, connected, client_senddone,
+ response_timeout, &region, &id, &dispentry);
+ assert_int_equal(result, ISC_R_SUCCESS);
+
+ memset(message, 0, sizeof(message));
+ message[0] = (id >> 8) & 0xff;
+ message[1] = id & 0xff;
+
+ region.base = message;
+ region.length = sizeof(message);
+
+ dns_dispatch_connect(dispentry);
+
+ uv_sem_wait(&sem);
+
+ assert_int_equal(atomic_load_acquire(&testdata.result), ISC_R_TIMEDOUT);
+
+ isc_nm_stoplistening(sock);
+ isc_nmsocket_close(&sock);
+ assert_null(sock);
+
+ dns_dispatch_done(&dispentry);
+
+ dns_dispatch_detach(&dispatch);
+ dns_dispatchmgr_detach(&dispatchmgr);
+}
+
+/* test dispatch getnext */
+ISC_RUN_TEST_IMPL(dispatch_getnext) {
+ isc_result_t result;
+ isc_region_t region;
+ isc_nmsocket_t *sock = NULL;
+ unsigned char message[12] = { 0 };
+ unsigned char rbuf[12] = { 0 };
+ uint16_t id;
+
+ UNUSED(state);
+
+ result = dns_dispatchmgr_create(mctx, connect_nm, &dispatchmgr);
+ assert_int_equal(result, ISC_R_SUCCESS);
+
+ result = dns_dispatch_createudp(dispatchmgr, &udp_connect_addr,
+ &dispatch);
+ assert_int_equal(result, ISC_R_SUCCESS);
+
+ /*
+ * Create a local udp nameserver on the loopback.
+ */
+ result = isc_nm_listenudp(netmgr, &udp_server_addr, nameserver, NULL, 0,
+ &sock);
+ assert_int_equal(result, ISC_R_SUCCESS);
+
+ region.base = rbuf;
+ region.length = sizeof(rbuf);
+ result = dns_dispatch_add(dispatch, 0, T_CLIENT_CONNECT,
+ &udp_server_addr, connected, client_senddone,
+ response_getnext, &region, &id, &dispentry);
+ assert_int_equal(result, ISC_R_SUCCESS);
+
+ memset(message, 0, sizeof(message));
+ message[0] = (id >> 8) & 0xff;
+ message[1] = id & 0xff;
+
+ region.base = message;
+ region.length = sizeof(message);
+
+ dns_dispatch_connect(dispentry);
+
+ uv_sem_wait(&sem);
+
+ assert_int_equal(atomic_load_acquire(&testdata.responses), 2);
+
+ /* Cleanup */
+ isc_nm_stoplistening(sock);
+ isc_nmsocket_close(&sock);
+ assert_null(sock);
+
+ dns_dispatch_done(&dispentry);
+ dns_dispatch_detach(&dispatch);
+ dns_dispatchmgr_detach(&dispatchmgr);
+}
+
+ISC_TEST_LIST_START
+
+ISC_TEST_ENTRY_CUSTOM(dispatch_timeout_tcp_connect, _setup, _teardown)
+ISC_TEST_ENTRY_CUSTOM(dispatch_timeout_tcp_response, _setup, _teardown)
+ISC_TEST_ENTRY_CUSTOM(dispatch_tcp_response, _setup, _teardown)
+ISC_TEST_ENTRY_CUSTOM(dispatch_timeout_udp_response, _setup, _teardown)
+ISC_TEST_ENTRY_CUSTOM(dispatchset_create, _setup, _teardown)
+ISC_TEST_ENTRY_CUSTOM(dispatchset_get, _setup, _teardown)
+ISC_TEST_ENTRY_CUSTOM(dispatch_getnext, _setup, _teardown)
+
+ISC_TEST_LIST_END
+
+ISC_TEST_MAIN