From 39bd76df3d61c6d83c5aa8bab06c7c1dbe7159ac Mon Sep 17 00:00:00 2001 From: Florian Weimer Date: Tue, 27 Jun 2017 10:21:34 +0200 Subject: [PATCH] resolv: Avoid timeouts in test-resolv-res-init, test-resolv-res_init-thread Some Linux kernels have very aggressive ICMP rate limiting on the loopback interface. This commit introduces a minimal echoing DNS server inside the network namespace, so that there is no need for ICMP error messages anymore. --- ChangeLog | 7 +++ resolv/tst-resolv-res_init-skeleton.c | 78 +++++++++++++++++++++++++++ 2 files changed, 85 insertions(+) diff --git a/ChangeLog b/ChangeLog index 279059b299..f885eb4994 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +2017-06-27 Florian Weimer + + Work around test timeouts with ICMP rate limiting on localhost. + * resolv/tst-resolv-res_init-skeleton.c (start_dummy_server): New + function. + (do_test): Call it. + 2017-06-27 Florian Weimer Call _res_hconf_init from __res_vinit. diff --git a/resolv/tst-resolv-res_init-skeleton.c b/resolv/tst-resolv-res_init-skeleton.c index 1d2c475c4b..2b68c5ff9a 100644 --- a/resolv/tst-resolv-res_init-skeleton.c +++ b/resolv/tst-resolv-res_init-skeleton.c @@ -21,6 +21,7 @@ in. */ #include +#include #include #include #include /* For DEPRECATED_RES_USE_INET6. */ @@ -33,6 +34,7 @@ #include #include #include +#include #include #include @@ -527,6 +529,73 @@ test_file_contents (const struct test_case *t) } } +/* Dummy DNS server. It ensures that the probe queries sent by + gethostbyname and getaddrinfo receive a reply even if the system + applies a very strict rate limit to localhost. */ +static pid_t +start_dummy_server (void) +{ + int server_socket = xsocket (AF_INET, SOCK_DGRAM, 0); + { + struct sockaddr_in sin = + { + .sin_family = AF_INET, + .sin_addr = { .s_addr = htonl (INADDR_LOOPBACK) }, + .sin_port = htons (53), + }; + int ret = bind (server_socket, (struct sockaddr *) &sin, sizeof (sin)); + if (ret < 0) + { + if (errno == EACCES) + /* The port is reserved, which means we cannot start the + server. */ + return -1; + FAIL_EXIT1 ("cannot bind socket to port 53: %m"); + } + } + + pid_t pid = xfork (); + if (pid == 0) + { + /* Child process. Echo back queries as SERVFAIL responses. */ + while (true) + { + union + { + HEADER header; + unsigned char bytes[512]; + } packet; + struct sockaddr_in sin; + socklen_t sinlen = sizeof (sin); + + ssize_t ret = recvfrom + (server_socket, &packet, sizeof (packet), + MSG_NOSIGNAL, (struct sockaddr *) &sin, &sinlen); + if (ret < 0) + FAIL_EXIT1 ("recvfrom on fake server socket: %m"); + if (ret > sizeof (HEADER)) + { + /* Turn the query into a SERVFAIL response. */ + packet.header.qr = 1; + packet.header.rcode = ns_r_servfail; + + /* Send the response. */ + ret = sendto (server_socket, &packet, ret, + MSG_NOSIGNAL, (struct sockaddr *) &sin, sinlen); + if (ret < 0) + /* The peer may have closed socket prematurely, so + this is not an error. */ + printf ("warning: sending DNS server reply: %m\n"); + } + } + } + + /* In the parent, close the socket. */ + xclose (server_socket); + + return pid; +} + static int do_test (void) { @@ -552,6 +621,8 @@ do_test (void) support_capture_subprocess_free (&proc); } + pid_t server = start_dummy_server (); + for (size_t i = 0; test_cases[i].name != NULL; ++i) { if (test_verbose > 0) @@ -590,6 +661,13 @@ do_test (void) } } + if (server > 0) + { + if (kill (server, SIGTERM) < 0) + FAIL_EXIT1 ("could not terminate server process: %m"); + xwaitpid (server, NULL, 0); + } + free (path_chroot); path_chroot = NULL; free (path_resolv_conf);