From: Aki Tuomi Date: Thu, 9 Sep 2021 06:48:53 +0000 (+0300) Subject: lib-dns: Add unit tests for lookup X-Git-Tag: 2.4.0~4741 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=bc6615a0ca2cbef9f145d1bde0979e63eaf8b62d;p=thirdparty%2Fdovecot%2Fcore.git lib-dns: Add unit tests for lookup --- diff --git a/src/lib-dns/Makefile.am b/src/lib-dns/Makefile.am index 8eb687f3cc..b40cf72d82 100644 --- a/src/lib-dns/Makefile.am +++ b/src/lib-dns/Makefile.am @@ -13,6 +13,7 @@ headers = \ dns-util.h test_programs = \ + test-dns-lookup \ test-dns-util noinst_PROGRAMS = $(test_programs) @@ -25,6 +26,9 @@ test_libs = \ test_dns_util_SOURCES = test-dns-util.c test_dns_util_LDADD = $(test_libs) +test_dns_lookup_SOURCES = test-dns-lookup.c +test_dns_lookup_LDADD = $(test_libs) + check-local: for bin in $(test_programs); do \ if ! $(RUN_TEST) ./$$bin; then exit 1; fi; \ diff --git a/src/lib-dns/test-dns-lookup.c b/src/lib-dns/test-dns-lookup.c new file mode 100644 index 0000000000..9db7fdb931 --- /dev/null +++ b/src/lib-dns/test-dns-lookup.c @@ -0,0 +1,265 @@ +/* Copyright (c) 2021 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "test-common.h" +#include "strescape.h" +#include "strnum.h" +#include "strfuncs.h" +#include "unix-socket-create.h" +#include "ioloop.h" +#include "istream.h" +#include "ostream.h" +#include "connection.h" +#include "dns-lookup.h" +#include + +#define TEST_SOCKET_NAME ".test-dns-server" +#define TEST_CACHE_TTL 4 + +static const struct { + const char *name; + const char *reply; +} replies[] = { + { "localhost", "0\t127.0.0.1\t::1\n" }, + { "127.0.0.1", "0\tlocalhost\n" }, +}; + +static struct test_server { + struct ioloop *loop; + struct io *io; + struct timeout *to; + int fd; + unsigned int lookup_counter; +} test_server; + +struct test_expect_result { + int ret; + const char *result; +}; + +static void server_handle_timeout(struct connection *client) +{ + timeout_remove(&test_server.to); + o_stream_nsend_str(client->output, "-1\tUnresolved\n"); + connection_input_resume(client); +} + +static int +test_dns_client_input_args(struct connection *client, const char *const *args) +{ + if (strcmp(args[0], "QUIT") == 0) + return 0; + if (strcmp(args[0], "IP") != 0 && strcmp(args[0], "NAME") != 0) + return -1; + test_server.lookup_counter++; + /* never finish this query */ + if (str_begins(args[1], "waitfor")) { + unsigned int msecs; + i_assert(test_server.to == NULL); + if (str_to_uint(args[1]+7, &msecs) < 0) + i_unreached(); + connection_input_halt(client); + test_server.to = + timeout_add_short(msecs, server_handle_timeout, client); + return 1; + } + for (size_t i = 0; i < N_ELEMENTS(replies); i++) { + if (strcmp(args[1], replies[i].name) == 0) { + o_stream_nsend_str(client->output, replies[i].reply); + return 1; + } + } + o_stream_nsend_str(client->output, "-1\tUnresolved\n"); + return 1; +} + +static void test_dns_client_destroy(struct connection *client) +{ + connection_deinit(client); + i_free(client); +} + +static const struct connection_vfuncs dns_client_vfuncs = { + .input_args = test_dns_client_input_args, + .destroy = test_dns_client_destroy +}; + +static const struct connection_settings dns_client_set = { + .service_name_in = "dns-client", + .service_name_out = "dns", + .major_version = 1, + .minor_version = 0, + .input_max_size = SIZE_MAX, + .output_max_size = SIZE_MAX +}; + +static struct connection_list *test_dns_clients = NULL; + +static void test_dns_client_connected(struct test_server *server) +{ + int fd = accept(server->fd, NULL, NULL); + i_assert(fd > -1); + struct connection *conn = i_new(struct connection, 1); + connection_init_server(test_dns_clients, conn, "test client", fd, fd); +} + +static void create_dns_server(struct test_server *server_r) +{ + i_zero(server_r); + server_r->loop = io_loop_create(); + test_dns_clients = connection_list_init(&dns_client_set, + &dns_client_vfuncs); + /* create unix socket for listening connections */ + server_r->fd = unix_socket_create(TEST_SOCKET_NAME, 0700, geteuid(), + getegid(), 1); + server_r->io = io_add_to(server_r->loop, server_r->fd, IO_READ, + test_dns_client_connected, server_r); +} + +static void destroy_dns_server(struct test_server *server) +{ + io_remove(&server->io); + timeout_remove(&server->to); + connection_list_deinit(&test_dns_clients); + io_loop_destroy(&server->loop); + i_close_fd(&server->fd); + i_unlink_if_exists(TEST_SOCKET_NAME); + i_zero(server); +} + +static void test_callback_name(const struct dns_lookup_result *result, + struct test_expect_result *expected) +{ + io_loop_stop(current_ioloop); + test_assert_cmp(result->ret, ==, expected->ret); + if (result->ret != 0) + return; + test_assert_strcmp(result->name, expected->result); +} + +static void test_callback_ips(const struct dns_lookup_result *result, + struct test_expect_result *expected) +{ + io_loop_stop(current_ioloop); + test_assert_cmp(result->ret, ==, expected->ret); + if (result->ret != 0) + return; + const char *const *addr = t_strsplit_tabescaped(expected->result); + test_assert(result->ips_count == str_array_length(addr)); + if (result->ips_count == str_array_length(addr)) { + for (unsigned int i = 0; i < result->ips_count; i++) { + struct ip_addr ip; + i_assert(net_addr2ip(addr[i], &ip) == 0); + test_assert(net_ip_compare(&result->ips[i], &ip)); + } + } +} + +static void test_dns_expect_result_ips(const char *name, const char *result) +{ + const struct dns_lookup_settings set = { + .dns_client_socket_path = TEST_SOCKET_NAME, + .ioloop = test_server.loop, + .timeout_msecs = 1000, + }; + struct dns_lookup *lookup; + struct test_expect_result ctx = { + .ret = result == NULL ? -1 : 0, + .result = result + }; + test_assert(dns_lookup(name, &set, test_callback_ips, &ctx, &lookup) == 0); + io_loop_run(test_server.loop); +} + +static void test_dns_expect_result_name(const char *name, const char *result) +{ + const struct dns_lookup_settings set = { + .dns_client_socket_path = TEST_SOCKET_NAME, + .ioloop = test_server.loop, + .timeout_msecs = 1000, + }; + struct dns_lookup *lookup; + struct test_expect_result ctx = { + .ret = result == NULL ? -1 : 0, + .result = result + }; + struct ip_addr addr; + i_assert(net_addr2ip(name, &addr) == 0); + test_assert(dns_lookup_ptr(&addr, &set, test_callback_name, &ctx, &lookup) == 0); + io_loop_run(test_server.loop); +} + +static void test_dns_lookup(void) +{ + test_begin("dns lookup"); + create_dns_server(&test_server); + + test_dns_expect_result_ips("localhost", "127.0.0.1\t::1"); + test_dns_expect_result_name("127.0.0.1", "localhost"); + test_dns_expect_result_ips("nullhost", NULL); + test_dns_expect_result_name("127.0.1.0", NULL); + + destroy_dns_server(&test_server); + test_end(); +} + +static void test_dns_lookup_timeout(void) +{ + test_begin("dns lookup (timeout)"); + create_dns_server(&test_server); + + const struct dns_lookup_settings set = { + .dns_client_socket_path = TEST_SOCKET_NAME, + .ioloop = test_server.loop, + .timeout_msecs = 1000, + }; + struct dns_lookup *lookup; + struct test_expect_result ctx = { + .ret = -4, + .result = NULL, + }; + + test_assert(dns_lookup("waitfor1500", &set, test_callback_ips, &ctx, &lookup) == 0); + io_loop_run(current_ioloop); + + destroy_dns_server(&test_server); + test_end(); +} + +static void test_dns_lookup_abort(void) +{ + test_begin("dns lookup (abort)"); + create_dns_server(&test_server); + + const struct dns_lookup_settings set = { + .dns_client_socket_path = TEST_SOCKET_NAME, + .ioloop = test_server.loop, + .timeout_msecs = 1000, + }; + struct dns_lookup *lookup; + struct test_expect_result ctx = { + .ret = -4, + .result = NULL, + }; + + test_assert(dns_lookup("waitfor1500", &set, test_callback_ips, &ctx, &lookup) == 0); + struct timeout *to = timeout_add_short(100, io_loop_stop, current_ioloop); + io_loop_run(current_ioloop); + timeout_remove(&to); + dns_lookup_abort(&lookup); + + destroy_dns_server(&test_server); + test_end(); +} + +int main(void) +{ + static void (*const test_functions[])(void) = { + test_dns_lookup, + test_dns_lookup_timeout, + test_dns_lookup_abort, + NULL + }; + + return test_run(test_functions); +}