summaryrefslogtreecommitdiff
path: root/resolv/tst-resolv-res_init-skeleton.c
diff options
context:
space:
mode:
authorFlorian Weimer <fweimer@redhat.com>2017-06-27 10:21:34 +0200
committerFlorian Weimer <fweimer@redhat.com>2017-06-27 10:21:34 +0200
commit39bd76df3d61c6d83c5aa8bab06c7c1dbe7159ac (patch)
treeb47f1c3fcefa1b1879caed62862d81ad78e64f0e /resolv/tst-resolv-res_init-skeleton.c
parente6b4e2de6dd91efdcac80b79149c596de8a26b70 (diff)
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.
Diffstat (limited to 'resolv/tst-resolv-res_init-skeleton.c')
-rw-r--r--resolv/tst-resolv-res_init-skeleton.c78
1 files changed, 78 insertions, 0 deletions
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 <arpa/inet.h>
+#include <errno.h>
#include <gnu/lib-names.h>
#include <netdb.h>
#include <resolv/resolv-internal.h> /* For DEPRECATED_RES_USE_INET6. */
@@ -33,6 +34,7 @@
#include <support/support.h>
#include <support/temp_file.h>
#include <support/test-driver.h>
+#include <support/xsocket.h>
#include <support/xstdio.h>
#include <support/xunistd.h>
@@ -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);