]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib-dns: Add unit tests for lookup
authorAki Tuomi <aki.tuomi@open-xchange.com>
Thu, 9 Sep 2021 06:48:53 +0000 (09:48 +0300)
committerAki Tuomi <aki.tuomi@open-xchange.com>
Mon, 17 Jan 2022 11:52:09 +0000 (13:52 +0200)
src/lib-dns/Makefile.am
src/lib-dns/test-dns-lookup.c [new file with mode: 0644]

index 8eb687f3cc882435b47e0325ef4f20a9fbb2a245..b40cf72d82657b49ca9aa5a91e5e2218af89ef6b 100644 (file)
@@ -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 (file)
index 0000000..9db7fdb
--- /dev/null
@@ -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 <unistd.h>
+
+#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);
+}