]> git.ipfire.org Git - thirdparty/glibc.git/blame - resolv/tst-resolv-res_init-skeleton.c
hurd: Fix build
[thirdparty/glibc.git] / resolv / tst-resolv-res_init-skeleton.c
CommitLineData
2714c5f3 1/* Test parsing of /etc/resolv.conf. Genric version.
04277e02 2 Copyright (C) 2017-2019 Free Software Foundation, Inc.
2714c5f3
FW
3 This file is part of the GNU C Library.
4
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; if not, see
17 <http://www.gnu.org/licenses/>. */
18
19/* Before including this file, TEST_THREAD has to be defined to 0 or
20 1, depending on whether the threading tests should be compiled
21 in. */
22
23#include <arpa/inet.h>
39bd76df 24#include <errno.h>
2714c5f3
FW
25#include <gnu/lib-names.h>
26#include <netdb.h>
3f853f22 27#include <resolv/resolv_context.h>
2714c5f3
FW
28#include <stdio.h>
29#include <stdlib.h>
30#include <support/capture_subprocess.h>
31#include <support/check.h>
32#include <support/namespace.h>
33#include <support/run_diff.h>
34#include <support/support.h>
35#include <support/temp_file.h>
36#include <support/test-driver.h>
39bd76df 37#include <support/xsocket.h>
2714c5f3
FW
38#include <support/xstdio.h>
39#include <support/xunistd.h>
40
41#if TEST_THREAD
42# include <support/xthread.h>
43#endif
44
45/* This is the host name used to ensure predictable behavior of
46 res_init. */
47static const char *const test_hostname = "www.example.com";
48
d4165eed 49struct support_chroot *chroot_env;
2714c5f3
FW
50
51static void
52prepare (int argc, char **argv)
53{
d4165eed
FW
54 chroot_env = support_chroot_create
55 ((struct support_chroot_configuration)
56 {
57 .resolv_conf = "",
58 });
2714c5f3
FW
59}
60
61/* Verify that the chroot environment has been set up. */
62static void
63check_chroot_working (void *closure)
64{
d4165eed 65 xchroot (chroot_env->path_chroot);
2714c5f3
FW
66 FILE *fp = xfopen (_PATH_RESCONF, "r");
67 xfclose (fp);
68
69 TEST_VERIFY_EXIT (res_init () == 0);
70 TEST_VERIFY (_res.options & RES_INIT);
71
72 char buf[100];
73 if (gethostname (buf, sizeof (buf)) < 0)
74 FAIL_EXIT1 ("gethostname: %m");
75 if (strcmp (buf, test_hostname) != 0)
76 FAIL_EXIT1 ("unexpected host name: %s", buf);
77}
78
79/* If FLAG is set in *OPTIONS, write NAME to FP, and clear it in
80 *OPTIONS. */
81static void
82print_option_flag (FILE *fp, int *options, int flag, const char *name)
83{
84 if (*options & flag)
85 {
86 fprintf (fp, " %s", name);
87 *options &= ~flag;
88 }
89}
90
91/* Write a decoded version of the resolver configuration *RESP to the
92 stream FP. */
93static void
94print_resp (FILE *fp, res_state resp)
95{
3f853f22
FW
96 struct resolv_context *ctx = __resolv_context_get_override (resp);
97 TEST_VERIFY_EXIT (ctx != NULL);
98 if (ctx->conf == NULL)
99 fprintf (fp, "; extended resolver state missing\n");
100
2714c5f3
FW
101 /* The options directive. */
102 {
103 /* RES_INIT is used internally for tracking initialization. */
104 TEST_VERIFY (resp->options & RES_INIT);
105 /* Also mask out other default flags which cannot be set through
106 the options directive. */
107 int options
108 = resp->options & ~(RES_INIT | RES_RECURSE | RES_DEFNAMES | RES_DNSRCH);
109 if (options != 0
110 || resp->ndots != 1
111 || resp->retrans != RES_TIMEOUT
112 || resp->retry != RES_DFLRETRY)
113 {
114 fputs ("options", fp);
115 if (resp->ndots != 1)
116 fprintf (fp, " ndots:%d", resp->ndots);
117 if (resp->retrans != RES_TIMEOUT)
118 fprintf (fp, " timeout:%d", resp->retrans);
119 if (resp->retry != RES_DFLRETRY)
120 fprintf (fp, " attempts:%d", resp->retry);
121 print_option_flag (fp, &options, RES_USEVC, "use-vc");
2714c5f3
FW
122 print_option_flag (fp, &options, RES_ROTATE, "rotate");
123 print_option_flag (fp, &options, RES_USE_EDNS0, "edns0");
124 print_option_flag (fp, &options, RES_SNGLKUP,
125 "single-request");
126 print_option_flag (fp, &options, RES_SNGLKUPREOP,
127 "single-request-reopen");
128 print_option_flag (fp, &options, RES_NOTLDQUERY, "no-tld-query");
aef16cc8 129 print_option_flag (fp, &options, RES_NORELOAD, "no-reload");
2714c5f3
FW
130 fputc ('\n', fp);
131 if (options != 0)
132 fprintf (fp, "; error: unresolved option bits: 0x%x\n", options);
133 }
134 }
135
136 /* The search and domain directives. */
137 if (resp->dnsrch[0] != NULL)
138 {
139 fputs ("search", fp);
140 for (int i = 0; i < MAXDNSRCH && resp->dnsrch[i] != NULL; ++i)
141 {
142 fputc (' ', fp);
143 fputs (resp->dnsrch[i], fp);
144 }
145 fputc ('\n', fp);
146 }
147 else if (resp->defdname[0] != '\0')
148 fprintf (fp, "domain %s\n", resp->defdname);
149
3f853f22
FW
150 /* The extended search path. */
151 {
152 size_t i = 0;
153 while (true)
154 {
155 const char *name = __resolv_context_search_list (ctx, i);
156 if (name == NULL)
157 break;
158 fprintf (fp, "; search[%zu]: %s\n", i, name);
159 ++i;
160 }
161 }
162
2714c5f3
FW
163 /* The sortlist directive. */
164 if (resp->nsort > 0)
165 {
166 fputs ("sortlist", fp);
167 for (int i = 0; i < resp->nsort && i < MAXRESOLVSORT; ++i)
168 {
169 char net[20];
170 if (inet_ntop (AF_INET, &resp->sort_list[i].addr,
171 net, sizeof (net)) == NULL)
172 FAIL_EXIT1 ("inet_ntop: %m\n");
173 char mask[20];
174 if (inet_ntop (AF_INET, &resp->sort_list[i].mask,
175 mask, sizeof (mask)) == NULL)
176 FAIL_EXIT1 ("inet_ntop: %m\n");
177 fprintf (fp, " %s/%s", net, mask);
178 }
179 fputc ('\n', fp);
180 }
181
182 /* The nameserver directives. */
183 for (size_t i = 0; i < resp->nscount; ++i)
184 {
185 char host[NI_MAXHOST];
186 char service[NI_MAXSERV];
187
188 /* See get_nsaddr in res_send.c. */
189 void *addr;
190 size_t addrlen;
191 if (resp->nsaddr_list[i].sin_family == 0
192 && resp->_u._ext.nsaddrs[i] != NULL)
193 {
194 addr = resp->_u._ext.nsaddrs[i];
195 addrlen = sizeof (*resp->_u._ext.nsaddrs[i]);
196 }
197 else
198 {
199 addr = &resp->nsaddr_list[i];
200 addrlen = sizeof (resp->nsaddr_list[i]);
201 }
202
203 int ret = getnameinfo (addr, addrlen,
204 host, sizeof (host), service, sizeof (service),
205 NI_NUMERICHOST | NI_NUMERICSERV);
206 if (ret != 0)
207 {
208 if (ret == EAI_SYSTEM)
209 fprintf (fp, "; error: getnameinfo: %m\n");
210 else
211 fprintf (fp, "; error: getnameinfo: %s\n", gai_strerror (ret));
212 }
213 else
214 {
215 fprintf (fp, "nameserver %s\n", host);
216 if (strcmp (service, "53") != 0)
217 fprintf (fp, "; unrepresentable port number %s\n\n", service);
218 }
219 }
220
a1c4eb87
FW
221 /* The extended name server list. */
222 {
223 size_t i = 0;
224 while (true)
225 {
226 const struct sockaddr *addr = __resolv_context_nameserver (ctx, i);
227 if (addr == NULL)
228 break;
229 size_t addrlen;
230 switch (addr->sa_family)
231 {
232 case AF_INET:
233 addrlen = sizeof (struct sockaddr_in);
234 break;
235 case AF_INET6:
236 addrlen = sizeof (struct sockaddr_in6);
237 break;
238 default:
239 FAIL_EXIT1 ("invalid address family %d", addr->sa_family);
240 }
241
242 char host[NI_MAXHOST];
243 char service[NI_MAXSERV];
244 int ret = getnameinfo (addr, addrlen,
245 host, sizeof (host), service, sizeof (service),
246 NI_NUMERICHOST | NI_NUMERICSERV);
247
248 if (ret != 0)
249 {
250 if (ret == EAI_SYSTEM)
251 fprintf (fp, "; error: getnameinfo: %m\n");
252 else
253 fprintf (fp, "; error: getnameinfo: %s\n", gai_strerror (ret));
254 }
255 else
256 fprintf (fp, "; nameserver[%zu]: [%s]:%s\n", i, host, service);
257 ++i;
258 }
259 }
260
2714c5f3 261 TEST_VERIFY (!ferror (fp));
3f853f22
FW
262
263 __resolv_context_put (ctx);
2714c5f3
FW
264}
265
266/* Parameters of one test case. */
267struct test_case
268{
269 /* A short, descriptive name of the test. */
270 const char *name;
271
272 /* The contents of the /etc/resolv.conf file. */
273 const char *conf;
274
275 /* The expected output from print_resp. */
276 const char *expected;
277
278 /* Setting for the LOCALDOMAIN environment variable. NULL if the
279 variable is not to be set. */
280 const char *localdomain;
281
282 /* Setting for the RES_OPTIONS environment variable. NULL if the
283 variable is not to be set. */
284 const char *res_options;
4446a885
FW
285
286 /* Override the system host name. NULL means that no change is made
287 and the default is used (test_hostname). */
288 const char *hostname;
2714c5f3
FW
289};
290
291enum test_init
292{
293 test_init,
294 test_ninit,
295 test_mkquery,
296 test_gethostbyname,
297 test_getaddrinfo,
298 test_init_method_last = test_getaddrinfo
299};
300
a9270e67
FW
301static const char *const test_init_names[] =
302 {
303 [test_init] = "res_init",
4e45d83c 304 [test_ninit] = "res_ninit",
a9270e67
FW
305 [test_mkquery] = "res_mkquery",
306 [test_gethostbyname] = "gethostbyname",
307 [test_getaddrinfo] = "getaddrinfo",
308 };
309
2714c5f3
FW
310/* Closure argument for run_res_init. */
311struct test_context
312{
313 enum test_init init;
314 const struct test_case *t;
315};
316
317static void
318setup_nss_dns_and_chroot (void)
319{
320 /* Load nss_dns outside of the chroot. */
321 if (dlopen (LIBNSS_DNS_SO, RTLD_LAZY) == NULL)
322 FAIL_EXIT1 ("could not load " LIBNSS_DNS_SO ": %s", dlerror ());
d4165eed 323 xchroot (chroot_env->path_chroot);
2714c5f3
FW
324 /* Force the use of nss_dns. */
325 __nss_configure_lookup ("hosts", "dns");
326}
327
328/* Run res_ninit or res_init in a subprocess and dump the parsed
329 resolver state to standard output. */
330static void
331run_res_init (void *closure)
332{
333 struct test_context *ctx = closure;
334 TEST_VERIFY (getenv ("LOCALDOMAIN") == NULL);
335 TEST_VERIFY (getenv ("RES_OPTIONS") == NULL);
336 if (ctx->t->localdomain != NULL)
337 setenv ("LOCALDOMAIN", ctx->t->localdomain, 1);
338 if (ctx->t->res_options != NULL)
339 setenv ("RES_OPTIONS", ctx->t->res_options, 1);
4446a885
FW
340 if (ctx->t->hostname != NULL)
341 {
6547ec0a 342#ifdef CLONE_NEWUTS
4446a885
FW
343 /* This test needs its own namespace, to avoid changing the host
344 name for the parent, too. */
345 TEST_VERIFY_EXIT (unshare (CLONE_NEWUTS) == 0);
346 if (sethostname (ctx->t->hostname, strlen (ctx->t->hostname)) != 0)
347 FAIL_EXIT1 ("sethostname (\"%s\"): %m", ctx->t->hostname);
6547ec0a
ST
348#else
349 FAIL_UNSUPPORTED ("clone (CLONE_NEWUTS) not supported");
350#endif
4446a885 351 }
2714c5f3
FW
352
353 switch (ctx->init)
354 {
355 case test_init:
d4165eed 356 xchroot (chroot_env->path_chroot);
2714c5f3
FW
357 TEST_VERIFY (res_init () == 0);
358 print_resp (stdout, &_res);
359 return;
360
361 case test_ninit:
d4165eed 362 xchroot (chroot_env->path_chroot);
2714c5f3
FW
363 res_state resp = xmalloc (sizeof (*resp));
364 memset (resp, 0, sizeof (*resp));
365 TEST_VERIFY (res_ninit (resp) == 0);
366 print_resp (stdout, resp);
367 res_nclose (resp);
368 free (resp);
369 return;
370
371 case test_mkquery:
d4165eed 372 xchroot (chroot_env->path_chroot);
2714c5f3
FW
373 unsigned char buf[512];
374 TEST_VERIFY (res_mkquery (QUERY, "www.example",
375 C_IN, ns_t_a, NULL, 0,
376 NULL, buf, sizeof (buf)) > 0);
377 print_resp (stdout, &_res);
378 return;
379
380 case test_gethostbyname:
381 setup_nss_dns_and_chroot ();
382 /* Trigger implicit initialization of the _res structure. The
383 actual lookup result is immaterial. */
384 (void )gethostbyname ("www.example");
385 print_resp (stdout, &_res);
386 return;
387
388 case test_getaddrinfo:
389 setup_nss_dns_and_chroot ();
390 /* Trigger implicit initialization of the _res structure. The
391 actual lookup result is immaterial. */
392 struct addrinfo *ai;
393 (void) getaddrinfo ("www.example", NULL, NULL, &ai);
394 print_resp (stdout, &_res);
395 return;
396 }
397
398 FAIL_EXIT1 ("invalid init method %d", ctx->init);
399}
400
401#if TEST_THREAD
402/* Helper function which calls run_res_init from a thread. */
403static void *
404run_res_init_thread_func (void *closure)
405{
406 run_res_init (closure);
407 return NULL;
408}
409
410/* Variant of res_run_init which runs the function on a non-main
411 thread. */
412static void
413run_res_init_on_thread (void *closure)
414{
415 xpthread_join (xpthread_create (NULL, run_res_init_thread_func, closure));
416}
417#endif /* TEST_THREAD */
418
419struct test_case test_cases[] =
420 {
421 {.name = "empty file",
422 .conf = "",
423 .expected = "search example.com\n"
3f853f22 424 "; search[0]: example.com\n"
2714c5f3 425 "nameserver 127.0.0.1\n"
a1c4eb87 426 "; nameserver[0]: [127.0.0.1]:53\n"
2714c5f3 427 },
4446a885
FW
428 {.name = "empty file, no-dot hostname",
429 .conf = "",
430 .expected = "nameserver 127.0.0.1\n"
431 "; nameserver[0]: [127.0.0.1]:53\n",
432 .hostname = "example",
433 },
2714c5f3
FW
434 {.name = "empty file with LOCALDOMAIN",
435 .conf = "",
436 .expected = "search example.net\n"
3f853f22 437 "; search[0]: example.net\n"
a1c4eb87
FW
438 "nameserver 127.0.0.1\n"
439 "; nameserver[0]: [127.0.0.1]:53\n",
2714c5f3
FW
440 .localdomain = "example.net",
441 },
442 {.name = "empty file with RES_OPTIONS",
443 .conf = "",
444 .expected = "options attempts:5 edns0\n"
445 "search example.com\n"
3f853f22 446 "; search[0]: example.com\n"
a1c4eb87
FW
447 "nameserver 127.0.0.1\n"
448 "; nameserver[0]: [127.0.0.1]:53\n",
2714c5f3
FW
449 .res_options = "edns0 attempts:5",
450 },
451 {.name = "empty file with RES_OPTIONS and LOCALDOMAIN",
452 .conf = "",
453 .expected = "options attempts:5 edns0\n"
454 "search example.org\n"
3f853f22 455 "; search[0]: example.org\n"
a1c4eb87
FW
456 "nameserver 127.0.0.1\n"
457 "; nameserver[0]: [127.0.0.1]:53\n",
2714c5f3
FW
458 .localdomain = "example.org",
459 .res_options = "edns0 attempts:5",
460 },
461 {.name = "basic",
4446a885 462 .conf = "search corp.example.com example.com\n"
2714c5f3
FW
463 "nameserver 192.0.2.1\n",
464 .expected = "search corp.example.com example.com\n"
3f853f22
FW
465 "; search[0]: corp.example.com\n"
466 "; search[1]: example.com\n"
2714c5f3 467 "nameserver 192.0.2.1\n"
a1c4eb87 468 "; nameserver[0]: [192.0.2.1]:53\n"
2714c5f3 469 },
4446a885
FW
470 {.name = "basic with no-dot hostname",
471 .conf = "search corp.example.com example.com\n"
472 "nameserver 192.0.2.1\n",
473 .expected = "search corp.example.com example.com\n"
474 "; search[0]: corp.example.com\n"
475 "; search[1]: example.com\n"
476 "nameserver 192.0.2.1\n"
477 "; nameserver[0]: [192.0.2.1]:53\n",
478 .hostname = "example",
479 },
aef16cc8
FW
480 {.name = "basic no-reload",
481 .conf = "options no-reload\n"
482 "search corp.example.com example.com\n"
483 "nameserver 192.0.2.1\n",
484 .expected = "options no-reload\n"
485 "search corp.example.com example.com\n"
486 "; search[0]: corp.example.com\n"
487 "; search[1]: example.com\n"
488 "nameserver 192.0.2.1\n"
489 "; nameserver[0]: [192.0.2.1]:53\n"
490 },
491 {.name = "basic no-reload via RES_OPTIONS",
492 .conf = "search corp.example.com example.com\n"
493 "nameserver 192.0.2.1\n",
494 .expected = "options no-reload\n"
495 "search corp.example.com example.com\n"
496 "; search[0]: corp.example.com\n"
497 "; search[1]: example.com\n"
498 "nameserver 192.0.2.1\n"
499 "; nameserver[0]: [192.0.2.1]:53\n",
500 .res_options = "no-reload"
501 },
2714c5f3
FW
502 {.name = "whitespace",
503 .conf = "# This test covers comment and whitespace processing "
504 " (trailing whitespace,\n"
505 "# missing newline at end of file).\n"
506 "\n"
2714c5f3 507 ";search commented out\n"
3f853f22 508 "search corp.example.com\texample.com \n"
2714c5f3
FW
509 "#nameserver 192.0.2.3\n"
510 "nameserver 192.0.2.1 \n"
511 "nameserver 192.0.2.2", /* No \n at end of file. */
512 .expected = "search corp.example.com example.com\n"
3f853f22
FW
513 "; search[0]: corp.example.com\n"
514 "; search[1]: example.com\n"
2714c5f3
FW
515 "nameserver 192.0.2.1\n"
516 "nameserver 192.0.2.2\n"
a1c4eb87
FW
517 "; nameserver[0]: [192.0.2.1]:53\n"
518 "; nameserver[1]: [192.0.2.2]:53\n"
2714c5f3 519 },
3f853f22
FW
520 {.name = "domain",
521 .conf = "domain example.net\n"
522 "nameserver 192.0.2.1\n",
523 .expected = "search example.net\n"
524 "; search[0]: example.net\n"
525 "nameserver 192.0.2.1\n"
a1c4eb87 526 "; nameserver[0]: [192.0.2.1]:53\n"
3f853f22
FW
527 },
528 {.name = "domain space",
529 .conf = "domain example.net \n"
530 "nameserver 192.0.2.1\n",
531 .expected = "search example.net\n"
532 "; search[0]: example.net\n"
533 "nameserver 192.0.2.1\n"
a1c4eb87 534 "; nameserver[0]: [192.0.2.1]:53\n"
3f853f22
FW
535 },
536 {.name = "domain tab",
537 .conf = "domain example.net\t\n"
538 "nameserver 192.0.2.1\n",
539 .expected = "search example.net\n"
540 "; search[0]: example.net\n"
541 "nameserver 192.0.2.1\n"
a1c4eb87 542 "; nameserver[0]: [192.0.2.1]:53\n"
3f853f22
FW
543 },
544 {.name = "domain override",
545 .conf = "search example.com example.org\n"
546 "nameserver 192.0.2.1\n"
547 "domain example.net", /* No \n at end of file. */
548 .expected = "search example.net\n"
549 "; search[0]: example.net\n"
550 "nameserver 192.0.2.1\n"
a1c4eb87 551 "; nameserver[0]: [192.0.2.1]:53\n"
3f853f22 552 },
2714c5f3
FW
553 {.name = "option values, multiple servers",
554 .conf = "options\tinet6\tndots:3 edns0\tattempts:5\ttimeout:19\n"
555 "domain example.net\n"
556 ";domain comment\n"
557 "search corp.example.com\texample.com\n"
558 "nameserver 192.0.2.1\n"
559 "nameserver ::1\n"
560 "nameserver 192.0.2.2\n",
3f8b44be 561 .expected = "options ndots:3 timeout:19 attempts:5 edns0\n"
2714c5f3 562 "search corp.example.com example.com\n"
3f853f22
FW
563 "; search[0]: corp.example.com\n"
564 "; search[1]: example.com\n"
2714c5f3
FW
565 "nameserver 192.0.2.1\n"
566 "nameserver ::1\n"
567 "nameserver 192.0.2.2\n"
a1c4eb87
FW
568 "; nameserver[0]: [192.0.2.1]:53\n"
569 "; nameserver[1]: [::1]:53\n"
570 "; nameserver[2]: [192.0.2.2]:53\n"
2714c5f3
FW
571 },
572 {.name = "out-of-range option vales",
573 .conf = "options use-vc timeout:999 attempts:999 ndots:99\n"
574 "search example.com\n",
575 .expected = "options ndots:15 timeout:30 attempts:5 use-vc\n"
576 "search example.com\n"
3f853f22 577 "; search[0]: example.com\n"
2714c5f3 578 "nameserver 127.0.0.1\n"
a1c4eb87 579 "; nameserver[0]: [127.0.0.1]:53\n"
2714c5f3
FW
580 },
581 {.name = "repeated directives",
582 .conf = "options ndots:3 use-vc\n"
583 "options edns0 ndots:2\n"
584 "domain corp.example\n"
585 "search example.net corp.example.com example.com\n"
586 "search example.org\n"
587 "search\n",
588 .expected = "options ndots:2 use-vc edns0\n"
589 "search example.org\n"
3f853f22 590 "; search[0]: example.org\n"
2714c5f3 591 "nameserver 127.0.0.1\n"
a1c4eb87 592 "; nameserver[0]: [127.0.0.1]:53\n"
2714c5f3
FW
593 },
594 {.name = "many name servers, sortlist",
595 .conf = "options single-request\n"
596 "search example.org example.com example.net corp.example.com\n"
597 "sortlist 192.0.2.0/255.255.255.0\n"
598 "nameserver 192.0.2.1\n"
599 "nameserver 192.0.2.2\n"
600 "nameserver 192.0.2.3\n"
601 "nameserver 192.0.2.4\n"
602 "nameserver 192.0.2.5\n"
603 "nameserver 192.0.2.6\n"
604 "nameserver 192.0.2.7\n"
605 "nameserver 192.0.2.8\n",
606 .expected = "options single-request\n"
607 "search example.org example.com example.net corp.example.com\n"
3f853f22
FW
608 "; search[0]: example.org\n"
609 "; search[1]: example.com\n"
610 "; search[2]: example.net\n"
611 "; search[3]: corp.example.com\n"
2714c5f3
FW
612 "sortlist 192.0.2.0/255.255.255.0\n"
613 "nameserver 192.0.2.1\n"
614 "nameserver 192.0.2.2\n"
615 "nameserver 192.0.2.3\n"
a1c4eb87
FW
616 "; nameserver[0]: [192.0.2.1]:53\n"
617 "; nameserver[1]: [192.0.2.2]:53\n"
618 "; nameserver[2]: [192.0.2.3]:53\n"
619 "; nameserver[3]: [192.0.2.4]:53\n"
620 "; nameserver[4]: [192.0.2.5]:53\n"
621 "; nameserver[5]: [192.0.2.6]:53\n"
622 "; nameserver[6]: [192.0.2.7]:53\n"
623 "; nameserver[7]: [192.0.2.8]:53\n"
2714c5f3
FW
624 },
625 {.name = "IPv4 and IPv6 nameservers",
626 .conf = "options single-request\n"
627 "search example.org example.com example.net corp.example.com"
628 " legacy.example.com\n"
629 "sortlist 192.0.2.0\n"
630 "nameserver 192.0.2.1\n"
631 "nameserver 2001:db8::2\n"
632 "nameserver 192.0.2.3\n"
633 "nameserver 2001:db8::4\n"
634 "nameserver 192.0.2.5\n"
635 "nameserver 2001:db8::6\n"
636 "nameserver 192.0.2.7\n"
637 "nameserver 2001:db8::8\n",
638 .expected = "options single-request\n"
639 "search example.org example.com example.net corp.example.com"
640 " legacy.example.com\n"
3f853f22
FW
641 "; search[0]: example.org\n"
642 "; search[1]: example.com\n"
643 "; search[2]: example.net\n"
644 "; search[3]: corp.example.com\n"
645 "; search[4]: legacy.example.com\n"
2714c5f3
FW
646 "sortlist 192.0.2.0/255.255.255.0\n"
647 "nameserver 192.0.2.1\n"
648 "nameserver 2001:db8::2\n"
649 "nameserver 192.0.2.3\n"
a1c4eb87
FW
650 "; nameserver[0]: [192.0.2.1]:53\n"
651 "; nameserver[1]: [2001:db8::2]:53\n"
652 "; nameserver[2]: [192.0.2.3]:53\n"
653 "; nameserver[3]: [2001:db8::4]:53\n"
654 "; nameserver[4]: [192.0.2.5]:53\n"
655 "; nameserver[5]: [2001:db8::6]:53\n"
656 "; nameserver[6]: [192.0.2.7]:53\n"
657 "; nameserver[7]: [2001:db8::8]:53\n",
2714c5f3
FW
658 },
659 {.name = "garbage after nameserver",
660 .conf = "nameserver 192.0.2.1 garbage\n"
661 "nameserver 192.0.2.2:5353\n"
662 "nameserver 192.0.2.3 5353\n",
663 .expected = "search example.com\n"
3f853f22 664 "; search[0]: example.com\n"
2714c5f3
FW
665 "nameserver 192.0.2.1\n"
666 "nameserver 192.0.2.3\n"
a1c4eb87
FW
667 "; nameserver[0]: [192.0.2.1]:53\n"
668 "; nameserver[1]: [192.0.2.3]:53\n"
2714c5f3
FW
669 },
670 {.name = "RES_OPTIONS is cummulative",
671 .conf = "options timeout:7 ndots:2 use-vc\n"
672 "nameserver 192.0.2.1\n",
673 .expected = "options ndots:3 timeout:7 attempts:5 use-vc edns0\n"
674 "search example.com\n"
3f853f22 675 "; search[0]: example.com\n"
a1c4eb87
FW
676 "nameserver 192.0.2.1\n"
677 "; nameserver[0]: [192.0.2.1]:53\n",
2714c5f3
FW
678 .res_options = "attempts:5 ndots:3 edns0 ",
679 },
3f853f22
FW
680 {.name = "many search list entries (bug 19569)",
681 .conf = "nameserver 192.0.2.1\n"
682 "search corp.example.com support.example.com"
683 " community.example.org wan.example.net vpn.example.net"
684 " example.com example.org example.net\n",
685 .expected = "search corp.example.com support.example.com"
686 " community.example.org wan.example.net vpn.example.net example.com\n"
687 "; search[0]: corp.example.com\n"
688 "; search[1]: support.example.com\n"
689 "; search[2]: community.example.org\n"
690 "; search[3]: wan.example.net\n"
691 "; search[4]: vpn.example.net\n"
692 "; search[5]: example.com\n"
693 "; search[6]: example.org\n"
694 "; search[7]: example.net\n"
a1c4eb87
FW
695 "nameserver 192.0.2.1\n"
696 "; nameserver[0]: [192.0.2.1]:53\n"
3f853f22
FW
697 },
698 {.name = "very long search list entries (bug 21475)",
699 .conf = "nameserver 192.0.2.1\n"
700 "search example.com "
701#define H63 "this-host-name-is-longer-than-yours-yes-I-really-really-mean-it"
702#define D63 "this-domain-name-is-as-long-as-the-previous-name--63-characters"
703 " " H63 "." D63 ".example.org"
704 " " H63 "." D63 ".example.net\n",
705 .expected = "search example.com " H63 "." D63 ".example.org\n"
706 "; search[0]: example.com\n"
707 "; search[1]: " H63 "." D63 ".example.org\n"
708 "; search[2]: " H63 "." D63 ".example.net\n"
709#undef H63
710#undef D63
a1c4eb87
FW
711 "nameserver 192.0.2.1\n"
712 "; nameserver[0]: [192.0.2.1]:53\n"
3f853f22 713 },
2714c5f3
FW
714 { NULL }
715 };
716
717/* Run the indicated test case. This function assumes that the chroot
718 contents has already been set up. */
719static void
720test_file_contents (const struct test_case *t)
721{
722#if TEST_THREAD
723 for (int do_thread = 0; do_thread < 2; ++do_thread)
724#endif
725 for (int init_method = 0; init_method <= test_init_method_last;
726 ++init_method)
727 {
728 if (test_verbose > 0)
a9270e67
FW
729 printf ("info: testing init method %s\n",
730 test_init_names[init_method]);
2714c5f3
FW
731 struct test_context ctx = { .init = init_method, .t = t };
732 void (*func) (void *) = run_res_init;
733#if TEST_THREAD
734 if (do_thread)
735 func = run_res_init_on_thread;
736#endif
737 struct support_capture_subprocess proc
738 = support_capture_subprocess (func, &ctx);
739 if (strcmp (proc.out.buffer, t->expected) != 0)
740 {
741 support_record_failure ();
a9270e67
FW
742 printf ("error: output mismatch for %s (init method %s)\n",
743 t->name, test_init_names[init_method]);
2714c5f3
FW
744 support_run_diff ("expected", t->expected,
745 "actual", proc.out.buffer);
746 }
747 support_capture_subprocess_check (&proc, t->name, 0,
748 sc_allow_stdout);
749 support_capture_subprocess_free (&proc);
750 }
751}
752
4e45d83c 753/* Special tests which do not follow the general pattern. */
aef16cc8 754enum { special_tests_count = 11 };
4e45d83c
FW
755
756/* Implementation of special tests. */
757static void
758special_test_callback (void *closure)
759{
760 unsigned int *test_indexp = closure;
761 unsigned test_index = *test_indexp;
762 TEST_VERIFY (test_index < special_tests_count);
763 if (test_verbose > 0)
764 printf ("info: special test %u\n", test_index);
d4165eed 765 xchroot (chroot_env->path_chroot);
4e45d83c
FW
766
767 switch (test_index)
768 {
769 case 0:
770 case 1:
771 /* Second res_init with missing or empty file preserves
772 flags. */
773 if (test_index == 1)
774 TEST_VERIFY (unlink (_PATH_RESCONF) == 0);
775 _res.options = RES_USE_EDNS0;
776 TEST_VERIFY (res_init () == 0);
777 /* First res_init clears flag. */
778 TEST_VERIFY (!(_res.options & RES_USE_EDNS0));
779 _res.options |= RES_USE_EDNS0;
780 TEST_VERIFY (res_init () == 0);
781 /* Second res_init preserves flag. */
782 TEST_VERIFY (_res.options & RES_USE_EDNS0);
783 if (test_index == 1)
784 /* Restore empty file. */
785 support_write_file_string (_PATH_RESCONF, "");
786 break;
787
788 case 2:
789 /* Second res_init is cumulative. */
790 support_write_file_string (_PATH_RESCONF,
791 "options rotate\n"
792 "nameserver 192.0.2.1\n");
793 _res.options = RES_USE_EDNS0;
794 TEST_VERIFY (res_init () == 0);
795 /* First res_init clears flag. */
796 TEST_VERIFY (!(_res.options & RES_USE_EDNS0));
797 /* And sets RES_ROTATE. */
798 TEST_VERIFY (_res.options & RES_ROTATE);
799 _res.options |= RES_USE_EDNS0;
800 TEST_VERIFY (res_init () == 0);
801 /* Second res_init preserves flag. */
802 TEST_VERIFY (_res.options & RES_USE_EDNS0);
803 TEST_VERIFY (_res.options & RES_ROTATE);
804 /* Reloading the configuration does not clear the explicitly set
805 flag. */
806 support_write_file_string (_PATH_RESCONF,
807 "nameserver 192.0.2.1\n"
808 "nameserver 192.0.2.2\n");
809 TEST_VERIFY (res_init () == 0);
810 TEST_VERIFY (_res.nscount == 2);
811 TEST_VERIFY (_res.options & RES_USE_EDNS0);
812 /* Whether RES_ROTATE (originally in resolv.conf, now removed)
813 should be preserved is subject to debate. See bug 21701. */
814 /* TEST_VERIFY (!(_res.options & RES_ROTATE)); */
815 break;
816
817 case 3:
818 case 4:
819 case 5:
820 case 6:
4e45d83c
FW
821 support_write_file_string (_PATH_RESCONF,
822 "options edns0\n"
823 "nameserver 192.0.2.1\n");
aef16cc8
FW
824 goto reload_tests;
825 case 7: /* 7 and the following tests are with no-reload. */
826 case 8:
827 case 9:
828 case 10:
829 support_write_file_string (_PATH_RESCONF,
830 "options edns0 no-reload\n"
831 "nameserver 192.0.2.1\n");
832 /* Fall through. */
833 reload_tests:
4e45d83c
FW
834 for (int iteration = 0; iteration < 2; ++iteration)
835 {
836 switch (test_index)
837 {
838 case 3:
aef16cc8 839 case 7:
4e45d83c
FW
840 TEST_VERIFY (res_init () == 0);
841 break;
842 case 4:
aef16cc8 843 case 8:
4e45d83c
FW
844 {
845 unsigned char buf[512];
846 TEST_VERIFY
847 (res_mkquery (QUERY, test_hostname, C_IN, T_A,
848 NULL, 0, NULL, buf, sizeof (buf)) > 0);
849 }
850 break;
851 case 5:
aef16cc8 852 case 9:
4e45d83c
FW
853 gethostbyname (test_hostname);
854 break;
855 case 6:
aef16cc8 856 case 10:
4e45d83c
FW
857 {
858 struct addrinfo *ai;
859 (void) getaddrinfo (test_hostname, NULL, NULL, &ai);
860 }
861 break;
862 }
aef16cc8
FW
863 /* test_index == 7 is res_init and performs a reload even
864 with no-reload. */
865 if (iteration == 0 || test_index > 7)
4e45d83c
FW
866 {
867 TEST_VERIFY (_res.options & RES_USE_EDNS0);
868 TEST_VERIFY (!(_res.options & RES_ROTATE));
aef16cc8
FW
869 if (test_index < 7)
870 TEST_VERIFY (!(_res.options & RES_NORELOAD));
871 else
872 TEST_VERIFY (_res.options & RES_NORELOAD);
4e45d83c 873 TEST_VERIFY (_res.nscount == 1);
aef16cc8 874 /* File change triggers automatic reloading. */
4e45d83c
FW
875 support_write_file_string (_PATH_RESCONF,
876 "options rotate\n"
877 "nameserver 192.0.2.1\n"
878 "nameserver 192.0.2.2\n");
4e45d83c
FW
879 }
880 else
881 {
aef16cc8
FW
882 if (test_index != 3 && test_index != 7)
883 /* test_index 3, 7 are res_init; this function does
884 not reset flags. See bug 21701. */
885 TEST_VERIFY (!(_res.options & RES_USE_EDNS0));
4e45d83c
FW
886 TEST_VERIFY (_res.options & RES_ROTATE);
887 TEST_VERIFY (_res.nscount == 2);
888 }
889 }
4e45d83c
FW
890 break;
891 }
892}
893
894#if TEST_THREAD
895/* Helper function which calls special_test_callback from a
896 thread. */
897static void *
898special_test_thread_func (void *closure)
899{
900 special_test_callback (closure);
901 return NULL;
902}
903
904/* Variant of special_test_callback which runs the function on a
905 non-main thread. */
906static void
907run_special_test_on_thread (void *closure)
908{
909 xpthread_join (xpthread_create (NULL, special_test_thread_func, closure));
910}
911#endif /* TEST_THREAD */
912
913/* Perform the requested special test in a subprocess using
914 special_test_callback. */
915static void
916special_test (unsigned int test_index)
917{
918#if TEST_THREAD
919 for (int do_thread = 0; do_thread < 2; ++do_thread)
920#endif
921 {
922 void (*func) (void *) = special_test_callback;
923#if TEST_THREAD
924 if (do_thread)
925 func = run_special_test_on_thread;
926#endif
927 struct support_capture_subprocess proc
928 = support_capture_subprocess (func, &test_index);
929 char *test_name = xasprintf ("special test %u", test_index);
930 if (strcmp (proc.out.buffer, "") != 0)
931 {
932 support_record_failure ();
933 printf ("error: output mismatch for %s\n", test_name);
934 support_run_diff ("expected", "",
935 "actual", proc.out.buffer);
936 }
937 support_capture_subprocess_check (&proc, test_name, 0, sc_allow_stdout);
938 free (test_name);
939 support_capture_subprocess_free (&proc);
940 }
941}
942
943
39bd76df
FW
944/* Dummy DNS server. It ensures that the probe queries sent by
945 gethostbyname and getaddrinfo receive a reply even if the system
946 applies a very strict rate limit to localhost. */
947static pid_t
948start_dummy_server (void)
949{
950 int server_socket = xsocket (AF_INET, SOCK_DGRAM, 0);
951 {
952 struct sockaddr_in sin =
953 {
954 .sin_family = AF_INET,
955 .sin_addr = { .s_addr = htonl (INADDR_LOOPBACK) },
956 .sin_port = htons (53),
957 };
958 int ret = bind (server_socket, (struct sockaddr *) &sin, sizeof (sin));
959 if (ret < 0)
960 {
961 if (errno == EACCES)
962 /* The port is reserved, which means we cannot start the
963 server. */
964 return -1;
965 FAIL_EXIT1 ("cannot bind socket to port 53: %m");
966 }
967 }
968
969 pid_t pid = xfork ();
970 if (pid == 0)
971 {
972 /* Child process. Echo back queries as SERVFAIL responses. */
973 while (true)
974 {
975 union
976 {
977 HEADER header;
978 unsigned char bytes[512];
979 } packet;
980 struct sockaddr_in sin;
981 socklen_t sinlen = sizeof (sin);
982
983 ssize_t ret = recvfrom
984 (server_socket, &packet, sizeof (packet),
985 MSG_NOSIGNAL, (struct sockaddr *) &sin, &sinlen);
986 if (ret < 0)
987 FAIL_EXIT1 ("recvfrom on fake server socket: %m");
988 if (ret > sizeof (HEADER))
989 {
990 /* Turn the query into a SERVFAIL response. */
991 packet.header.qr = 1;
992 packet.header.rcode = ns_r_servfail;
993
994 /* Send the response. */
995 ret = sendto (server_socket, &packet, ret,
996 MSG_NOSIGNAL, (struct sockaddr *) &sin, sinlen);
997 if (ret < 0)
998 /* The peer may have closed socket prematurely, so
999 this is not an error. */
1000 printf ("warning: sending DNS server reply: %m\n");
1001 }
1002 }
1003 }
1004
1005 /* In the parent, close the socket. */
1006 xclose (server_socket);
1007
1008 return pid;
1009}
1010
2714c5f3
FW
1011static int
1012do_test (void)
1013{
1014 support_become_root ();
1015 support_enter_network_namespace ();
1016 if (!support_in_uts_namespace () || !support_can_chroot ())
1017 return EXIT_UNSUPPORTED;
1018
1019 /* We are in an UTS namespace, so we can set the host name without
1020 altering the state of the entire system. */
1021 if (sethostname (test_hostname, strlen (test_hostname)) != 0)
1022 FAIL_EXIT1 ("sethostname: %m");
1023
1024 /* These environment variables affect resolv.conf parsing. */
1025 unsetenv ("LOCALDOMAIN");
1026 unsetenv ("RES_OPTIONS");
1027
1028 /* Ensure that the chroot setup worked. */
1029 {
1030 struct support_capture_subprocess proc
1031 = support_capture_subprocess (check_chroot_working, NULL);
1032 support_capture_subprocess_check (&proc, "chroot", 0, sc_allow_none);
1033 support_capture_subprocess_free (&proc);
1034 }
1035
39bd76df
FW
1036 pid_t server = start_dummy_server ();
1037
2714c5f3
FW
1038 for (size_t i = 0; test_cases[i].name != NULL; ++i)
1039 {
1040 if (test_verbose > 0)
1041 printf ("info: running test: %s\n", test_cases[i].name);
1042 TEST_VERIFY (test_cases[i].conf != NULL);
1043 TEST_VERIFY (test_cases[i].expected != NULL);
1044
d4165eed
FW
1045 support_write_file_string (chroot_env->path_resolv_conf,
1046 test_cases[i].conf);
2714c5f3
FW
1047
1048 test_file_contents (&test_cases[i]);
1049
1050 /* The expected output from the empty file test is used for
1051 further tests. */
1052 if (test_cases[i].conf[0] == '\0')
1053 {
1054 if (test_verbose > 0)
1055 printf ("info: special test: missing file\n");
d4165eed 1056 TEST_VERIFY (unlink (chroot_env->path_resolv_conf) == 0);
2714c5f3
FW
1057 test_file_contents (&test_cases[i]);
1058
1059 if (test_verbose > 0)
1060 printf ("info: special test: dangling symbolic link\n");
d4165eed 1061 TEST_VERIFY (symlink ("does-not-exist", chroot_env->path_resolv_conf) == 0);
2714c5f3 1062 test_file_contents (&test_cases[i]);
d4165eed 1063 TEST_VERIFY (unlink (chroot_env->path_resolv_conf) == 0);
2714c5f3
FW
1064
1065 if (test_verbose > 0)
1066 printf ("info: special test: unreadable file\n");
d4165eed
FW
1067 support_write_file_string (chroot_env->path_resolv_conf, "");
1068 TEST_VERIFY (chmod (chroot_env->path_resolv_conf, 0) == 0);
2714c5f3
FW
1069 test_file_contents (&test_cases[i]);
1070
1071 /* Restore the empty file. */
d4165eed
FW
1072 TEST_VERIFY (unlink (chroot_env->path_resolv_conf) == 0);
1073 support_write_file_string (chroot_env->path_resolv_conf, "");
2714c5f3
FW
1074 }
1075 }
1076
4e45d83c
FW
1077 /* The tests which do not follow a regular pattern. */
1078 for (unsigned int test_index = 0;
1079 test_index < special_tests_count; ++test_index)
1080 special_test (test_index);
1081
39bd76df
FW
1082 if (server > 0)
1083 {
1084 if (kill (server, SIGTERM) < 0)
1085 FAIL_EXIT1 ("could not terminate server process: %m");
1086 xwaitpid (server, NULL, 0);
1087 }
1088
d4165eed 1089 support_chroot_free (chroot_env);
2714c5f3
FW
1090 return 0;
1091}
1092
1093#define PREPARE prepare
1094#include <support/test-driver.c>