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.
This commit is contained in:
Florian Weimer 2017-06-27 10:21:34 +02:00
parent e6b4e2de6d
commit 39bd76df3d
2 changed files with 85 additions and 0 deletions

View file

@ -1,3 +1,10 @@
2017-06-27 Florian Weimer <fweimer@redhat.com>
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 <fweimer@redhat.com>
Call _res_hconf_init from __res_vinit.

View file

@ -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);