]> git.ipfire.org Git - thirdparty/glibc.git/blob - resolv/tst-resolv-res_init-skeleton.c
resolv: Add preinit tests to resolv/tst-resolv-res_init-skeleton.c
[thirdparty/glibc.git] / resolv / tst-resolv-res_init-skeleton.c
1 /* Test parsing of /etc/resolv.conf. Genric version.
2 Copyright (C) 2017 Free Software Foundation, Inc.
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>
24 #include <errno.h>
25 #include <gnu/lib-names.h>
26 #include <netdb.h>
27 #include <resolv/resolv-internal.h> /* For DEPRECATED_RES_USE_INET6. */
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>
37 #include <support/xsocket.h>
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. */
47 static const char *const test_hostname = "www.example.com";
48
49 /* Path to the test root directory. */
50 static char *path_chroot;
51
52 /* Path to resolv.conf under path_chroot (outside the chroot). */
53 static char *path_resolv_conf;
54
55 static void
56 prepare (int argc, char **argv)
57 {
58 path_chroot = xasprintf ("%s/tst-resolv-res_init-XXXXXX", test_dir);
59 if (mkdtemp (path_chroot) == NULL)
60 FAIL_EXIT1 ("mkdtemp (\"%s\"): %m", path_chroot);
61 add_temp_file (path_chroot);
62
63 /* Create the /etc directory in the chroot environment. */
64 char *path_etc = xasprintf ("%s/etc", path_chroot);
65 xmkdir (path_etc, 0777);
66 add_temp_file (path_etc);
67
68 /* Create an empty resolv.conf file. */
69 path_resolv_conf = xasprintf ("%s/resolv.conf", path_etc);
70 add_temp_file (path_resolv_conf);
71 support_write_file_string (path_resolv_conf, "");
72
73 free (path_etc);
74
75 /* valgrind needs a temporary directory in the chroot. */
76 {
77 char *path_tmp = xasprintf ("%s/tmp", path_chroot);
78 xmkdir (path_tmp, 0777);
79 add_temp_file (path_tmp);
80 free (path_tmp);
81 }
82 }
83
84 /* Verify that the chroot environment has been set up. */
85 static void
86 check_chroot_working (void *closure)
87 {
88 xchroot (path_chroot);
89 FILE *fp = xfopen (_PATH_RESCONF, "r");
90 xfclose (fp);
91
92 TEST_VERIFY_EXIT (res_init () == 0);
93 TEST_VERIFY (_res.options & RES_INIT);
94
95 char buf[100];
96 if (gethostname (buf, sizeof (buf)) < 0)
97 FAIL_EXIT1 ("gethostname: %m");
98 if (strcmp (buf, test_hostname) != 0)
99 FAIL_EXIT1 ("unexpected host name: %s", buf);
100 }
101
102 /* If FLAG is set in *OPTIONS, write NAME to FP, and clear it in
103 *OPTIONS. */
104 static void
105 print_option_flag (FILE *fp, int *options, int flag, const char *name)
106 {
107 if (*options & flag)
108 {
109 fprintf (fp, " %s", name);
110 *options &= ~flag;
111 }
112 }
113
114 /* Write a decoded version of the resolver configuration *RESP to the
115 stream FP. */
116 static void
117 print_resp (FILE *fp, res_state resp)
118 {
119 /* The options directive. */
120 {
121 /* RES_INIT is used internally for tracking initialization. */
122 TEST_VERIFY (resp->options & RES_INIT);
123 /* Also mask out other default flags which cannot be set through
124 the options directive. */
125 int options
126 = resp->options & ~(RES_INIT | RES_RECURSE | RES_DEFNAMES | RES_DNSRCH);
127 if (options != 0
128 || resp->ndots != 1
129 || resp->retrans != RES_TIMEOUT
130 || resp->retry != RES_DFLRETRY)
131 {
132 fputs ("options", fp);
133 if (resp->ndots != 1)
134 fprintf (fp, " ndots:%d", resp->ndots);
135 if (resp->retrans != RES_TIMEOUT)
136 fprintf (fp, " timeout:%d", resp->retrans);
137 if (resp->retry != RES_DFLRETRY)
138 fprintf (fp, " attempts:%d", resp->retry);
139 print_option_flag (fp, &options, RES_USEVC, "use-vc");
140 print_option_flag (fp, &options, DEPRECATED_RES_USE_INET6, "inet6");
141 print_option_flag (fp, &options, RES_ROTATE, "rotate");
142 print_option_flag (fp, &options, RES_USE_EDNS0, "edns0");
143 print_option_flag (fp, &options, RES_SNGLKUP,
144 "single-request");
145 print_option_flag (fp, &options, RES_SNGLKUPREOP,
146 "single-request-reopen");
147 print_option_flag (fp, &options, RES_NOTLDQUERY, "no-tld-query");
148 fputc ('\n', fp);
149 if (options != 0)
150 fprintf (fp, "; error: unresolved option bits: 0x%x\n", options);
151 }
152 }
153
154 /* The search and domain directives. */
155 if (resp->dnsrch[0] != NULL)
156 {
157 fputs ("search", fp);
158 for (int i = 0; i < MAXDNSRCH && resp->dnsrch[i] != NULL; ++i)
159 {
160 fputc (' ', fp);
161 fputs (resp->dnsrch[i], fp);
162 }
163 fputc ('\n', fp);
164 }
165 else if (resp->defdname[0] != '\0')
166 fprintf (fp, "domain %s\n", resp->defdname);
167
168 /* The sortlist directive. */
169 if (resp->nsort > 0)
170 {
171 fputs ("sortlist", fp);
172 for (int i = 0; i < resp->nsort && i < MAXRESOLVSORT; ++i)
173 {
174 char net[20];
175 if (inet_ntop (AF_INET, &resp->sort_list[i].addr,
176 net, sizeof (net)) == NULL)
177 FAIL_EXIT1 ("inet_ntop: %m\n");
178 char mask[20];
179 if (inet_ntop (AF_INET, &resp->sort_list[i].mask,
180 mask, sizeof (mask)) == NULL)
181 FAIL_EXIT1 ("inet_ntop: %m\n");
182 fprintf (fp, " %s/%s", net, mask);
183 }
184 fputc ('\n', fp);
185 }
186
187 /* The nameserver directives. */
188 for (size_t i = 0; i < resp->nscount; ++i)
189 {
190 char host[NI_MAXHOST];
191 char service[NI_MAXSERV];
192
193 /* See get_nsaddr in res_send.c. */
194 void *addr;
195 size_t addrlen;
196 if (resp->nsaddr_list[i].sin_family == 0
197 && resp->_u._ext.nsaddrs[i] != NULL)
198 {
199 addr = resp->_u._ext.nsaddrs[i];
200 addrlen = sizeof (*resp->_u._ext.nsaddrs[i]);
201 }
202 else
203 {
204 addr = &resp->nsaddr_list[i];
205 addrlen = sizeof (resp->nsaddr_list[i]);
206 }
207
208 int ret = getnameinfo (addr, addrlen,
209 host, sizeof (host), service, sizeof (service),
210 NI_NUMERICHOST | NI_NUMERICSERV);
211 if (ret != 0)
212 {
213 if (ret == EAI_SYSTEM)
214 fprintf (fp, "; error: getnameinfo: %m\n");
215 else
216 fprintf (fp, "; error: getnameinfo: %s\n", gai_strerror (ret));
217 }
218 else
219 {
220 fprintf (fp, "nameserver %s\n", host);
221 if (strcmp (service, "53") != 0)
222 fprintf (fp, "; unrepresentable port number %s\n\n", service);
223 }
224 }
225
226 TEST_VERIFY (!ferror (fp));
227 }
228
229 /* Parameters of one test case. */
230 struct test_case
231 {
232 /* A short, descriptive name of the test. */
233 const char *name;
234
235 /* The contents of the /etc/resolv.conf file. */
236 const char *conf;
237
238 /* The expected output from print_resp. */
239 const char *expected;
240
241 /* Setting for the LOCALDOMAIN environment variable. NULL if the
242 variable is not to be set. */
243 const char *localdomain;
244
245 /* Setting for the RES_OPTIONS environment variable. NULL if the
246 variable is not to be set. */
247 const char *res_options;
248 };
249
250 enum test_init
251 {
252 test_init,
253 test_ninit,
254 test_mkquery,
255 test_gethostbyname,
256 test_getaddrinfo,
257 test_init_method_last = test_getaddrinfo
258 };
259
260 static const char *const test_init_names[] =
261 {
262 [test_init] = "res_init",
263 [test_ninit] = "res_ninit",
264 [test_mkquery] = "res_mkquery",
265 [test_gethostbyname] = "gethostbyname",
266 [test_getaddrinfo] = "getaddrinfo",
267 };
268
269 /* Closure argument for run_res_init. */
270 struct test_context
271 {
272 enum test_init init;
273 const struct test_case *t;
274 };
275
276 static void
277 setup_nss_dns_and_chroot (void)
278 {
279 /* Load nss_dns outside of the chroot. */
280 if (dlopen (LIBNSS_DNS_SO, RTLD_LAZY) == NULL)
281 FAIL_EXIT1 ("could not load " LIBNSS_DNS_SO ": %s", dlerror ());
282 xchroot (path_chroot);
283 /* Force the use of nss_dns. */
284 __nss_configure_lookup ("hosts", "dns");
285 }
286
287 /* Run res_ninit or res_init in a subprocess and dump the parsed
288 resolver state to standard output. */
289 static void
290 run_res_init (void *closure)
291 {
292 struct test_context *ctx = closure;
293 TEST_VERIFY (getenv ("LOCALDOMAIN") == NULL);
294 TEST_VERIFY (getenv ("RES_OPTIONS") == NULL);
295 if (ctx->t->localdomain != NULL)
296 setenv ("LOCALDOMAIN", ctx->t->localdomain, 1);
297 if (ctx->t->res_options != NULL)
298 setenv ("RES_OPTIONS", ctx->t->res_options, 1);
299
300 switch (ctx->init)
301 {
302 case test_init:
303 xchroot (path_chroot);
304 TEST_VERIFY (res_init () == 0);
305 print_resp (stdout, &_res);
306 return;
307
308 case test_ninit:
309 xchroot (path_chroot);
310 res_state resp = xmalloc (sizeof (*resp));
311 memset (resp, 0, sizeof (*resp));
312 TEST_VERIFY (res_ninit (resp) == 0);
313 print_resp (stdout, resp);
314 res_nclose (resp);
315 free (resp);
316 return;
317
318 case test_mkquery:
319 xchroot (path_chroot);
320 unsigned char buf[512];
321 TEST_VERIFY (res_mkquery (QUERY, "www.example",
322 C_IN, ns_t_a, NULL, 0,
323 NULL, buf, sizeof (buf)) > 0);
324 print_resp (stdout, &_res);
325 return;
326
327 case test_gethostbyname:
328 setup_nss_dns_and_chroot ();
329 /* Trigger implicit initialization of the _res structure. The
330 actual lookup result is immaterial. */
331 (void )gethostbyname ("www.example");
332 print_resp (stdout, &_res);
333 return;
334
335 case test_getaddrinfo:
336 setup_nss_dns_and_chroot ();
337 /* Trigger implicit initialization of the _res structure. The
338 actual lookup result is immaterial. */
339 struct addrinfo *ai;
340 (void) getaddrinfo ("www.example", NULL, NULL, &ai);
341 print_resp (stdout, &_res);
342 return;
343 }
344
345 FAIL_EXIT1 ("invalid init method %d", ctx->init);
346 }
347
348 #if TEST_THREAD
349 /* Helper function which calls run_res_init from a thread. */
350 static void *
351 run_res_init_thread_func (void *closure)
352 {
353 run_res_init (closure);
354 return NULL;
355 }
356
357 /* Variant of res_run_init which runs the function on a non-main
358 thread. */
359 static void
360 run_res_init_on_thread (void *closure)
361 {
362 xpthread_join (xpthread_create (NULL, run_res_init_thread_func, closure));
363 }
364 #endif /* TEST_THREAD */
365
366 struct test_case test_cases[] =
367 {
368 {.name = "empty file",
369 .conf = "",
370 .expected = "search example.com\n"
371 "nameserver 127.0.0.1\n"
372 },
373 {.name = "empty file with LOCALDOMAIN",
374 .conf = "",
375 .expected = "search example.net\n"
376 "nameserver 127.0.0.1\n",
377 .localdomain = "example.net",
378 },
379 {.name = "empty file with RES_OPTIONS",
380 .conf = "",
381 .expected = "options attempts:5 edns0\n"
382 "search example.com\n"
383 "nameserver 127.0.0.1\n",
384 .res_options = "edns0 attempts:5",
385 },
386 {.name = "empty file with RES_OPTIONS and LOCALDOMAIN",
387 .conf = "",
388 .expected = "options attempts:5 edns0\n"
389 "search example.org\n"
390 "nameserver 127.0.0.1\n",
391 .localdomain = "example.org",
392 .res_options = "edns0 attempts:5",
393 },
394 {.name = "basic",
395 .conf = "domain example.net\n"
396 "search corp.example.com example.com\n"
397 "nameserver 192.0.2.1\n",
398 .expected = "search corp.example.com example.com\n"
399 "nameserver 192.0.2.1\n"
400 },
401 {.name = "whitespace",
402 .conf = "# This test covers comment and whitespace processing "
403 " (trailing whitespace,\n"
404 "# missing newline at end of file).\n"
405 "\n"
406 "domain example.net\n"
407 ";search commented out\n"
408 "search corp.example.com\texample.com\n"
409 "#nameserver 192.0.2.3\n"
410 "nameserver 192.0.2.1 \n"
411 "nameserver 192.0.2.2", /* No \n at end of file. */
412 .expected = "search corp.example.com example.com\n"
413 "nameserver 192.0.2.1\n"
414 "nameserver 192.0.2.2\n"
415 },
416 {.name = "option values, multiple servers",
417 .conf = "options\tinet6\tndots:3 edns0\tattempts:5\ttimeout:19\n"
418 "domain example.net\n"
419 ";domain comment\n"
420 "search corp.example.com\texample.com\n"
421 "nameserver 192.0.2.1\n"
422 "nameserver ::1\n"
423 "nameserver 192.0.2.2\n",
424 .expected = "options ndots:3 timeout:19 attempts:5 inet6 edns0\n"
425 "search corp.example.com example.com\n"
426 "nameserver 192.0.2.1\n"
427 "nameserver ::1\n"
428 "nameserver 192.0.2.2\n"
429 },
430 {.name = "out-of-range option vales",
431 .conf = "options use-vc timeout:999 attempts:999 ndots:99\n"
432 "search example.com\n",
433 .expected = "options ndots:15 timeout:30 attempts:5 use-vc\n"
434 "search example.com\n"
435 "nameserver 127.0.0.1\n"
436 },
437 {.name = "repeated directives",
438 .conf = "options ndots:3 use-vc\n"
439 "options edns0 ndots:2\n"
440 "domain corp.example\n"
441 "search example.net corp.example.com example.com\n"
442 "search example.org\n"
443 "search\n",
444 .expected = "options ndots:2 use-vc edns0\n"
445 "search example.org\n"
446 "nameserver 127.0.0.1\n"
447 },
448 {.name = "many name servers, sortlist",
449 .conf = "options single-request\n"
450 "search example.org example.com example.net corp.example.com\n"
451 "sortlist 192.0.2.0/255.255.255.0\n"
452 "nameserver 192.0.2.1\n"
453 "nameserver 192.0.2.2\n"
454 "nameserver 192.0.2.3\n"
455 "nameserver 192.0.2.4\n"
456 "nameserver 192.0.2.5\n"
457 "nameserver 192.0.2.6\n"
458 "nameserver 192.0.2.7\n"
459 "nameserver 192.0.2.8\n",
460 .expected = "options single-request\n"
461 "search example.org example.com example.net corp.example.com\n"
462 "sortlist 192.0.2.0/255.255.255.0\n"
463 "nameserver 192.0.2.1\n"
464 "nameserver 192.0.2.2\n"
465 "nameserver 192.0.2.3\n"
466 },
467 {.name = "IPv4 and IPv6 nameservers",
468 .conf = "options single-request\n"
469 "search example.org example.com example.net corp.example.com"
470 " legacy.example.com\n"
471 "sortlist 192.0.2.0\n"
472 "nameserver 192.0.2.1\n"
473 "nameserver 2001:db8::2\n"
474 "nameserver 192.0.2.3\n"
475 "nameserver 2001:db8::4\n"
476 "nameserver 192.0.2.5\n"
477 "nameserver 2001:db8::6\n"
478 "nameserver 192.0.2.7\n"
479 "nameserver 2001:db8::8\n",
480 .expected = "options single-request\n"
481 "search example.org example.com example.net corp.example.com"
482 " legacy.example.com\n"
483 "sortlist 192.0.2.0/255.255.255.0\n"
484 "nameserver 192.0.2.1\n"
485 "nameserver 2001:db8::2\n"
486 "nameserver 192.0.2.3\n"
487 },
488 {.name = "garbage after nameserver",
489 .conf = "nameserver 192.0.2.1 garbage\n"
490 "nameserver 192.0.2.2:5353\n"
491 "nameserver 192.0.2.3 5353\n",
492 .expected = "search example.com\n"
493 "nameserver 192.0.2.1\n"
494 "nameserver 192.0.2.3\n"
495 },
496 {.name = "RES_OPTIONS is cummulative",
497 .conf = "options timeout:7 ndots:2 use-vc\n"
498 "nameserver 192.0.2.1\n",
499 .expected = "options ndots:3 timeout:7 attempts:5 use-vc edns0\n"
500 "search example.com\n"
501 "nameserver 192.0.2.1\n",
502 .res_options = "attempts:5 ndots:3 edns0 ",
503 },
504 { NULL }
505 };
506
507 /* Run the indicated test case. This function assumes that the chroot
508 contents has already been set up. */
509 static void
510 test_file_contents (const struct test_case *t)
511 {
512 #if TEST_THREAD
513 for (int do_thread = 0; do_thread < 2; ++do_thread)
514 #endif
515 for (int init_method = 0; init_method <= test_init_method_last;
516 ++init_method)
517 {
518 if (test_verbose > 0)
519 printf ("info: testing init method %s\n",
520 test_init_names[init_method]);
521 struct test_context ctx = { .init = init_method, .t = t };
522 void (*func) (void *) = run_res_init;
523 #if TEST_THREAD
524 if (do_thread)
525 func = run_res_init_on_thread;
526 #endif
527 struct support_capture_subprocess proc
528 = support_capture_subprocess (func, &ctx);
529 if (strcmp (proc.out.buffer, t->expected) != 0)
530 {
531 support_record_failure ();
532 printf ("error: output mismatch for %s (init method %s)\n",
533 t->name, test_init_names[init_method]);
534 support_run_diff ("expected", t->expected,
535 "actual", proc.out.buffer);
536 }
537 support_capture_subprocess_check (&proc, t->name, 0,
538 sc_allow_stdout);
539 support_capture_subprocess_free (&proc);
540 }
541 }
542
543 /* Special tests which do not follow the general pattern. */
544 enum { special_tests_count = 7 };
545
546 #if TEST_THREAD
547 /* Called from test number 3-6 to trigger reloading of the
548 configuration. */
549 static void *
550 special_test_call_res_init (void *closure)
551 {
552 TEST_VERIFY (res_init () == 0);
553 return NULL;
554 }
555 #endif
556
557 /* Implementation of special tests. */
558 static void
559 special_test_callback (void *closure)
560 {
561 unsigned int *test_indexp = closure;
562 unsigned test_index = *test_indexp;
563 TEST_VERIFY (test_index < special_tests_count);
564 if (test_verbose > 0)
565 printf ("info: special test %u\n", test_index);
566 xchroot (path_chroot);
567
568 switch (test_index)
569 {
570 case 0:
571 case 1:
572 /* Second res_init with missing or empty file preserves
573 flags. */
574 if (test_index == 1)
575 TEST_VERIFY (unlink (_PATH_RESCONF) == 0);
576 _res.options = RES_USE_EDNS0;
577 TEST_VERIFY (res_init () == 0);
578 /* First res_init clears flag. */
579 TEST_VERIFY (!(_res.options & RES_USE_EDNS0));
580 _res.options |= RES_USE_EDNS0;
581 TEST_VERIFY (res_init () == 0);
582 /* Second res_init preserves flag. */
583 TEST_VERIFY (_res.options & RES_USE_EDNS0);
584 if (test_index == 1)
585 /* Restore empty file. */
586 support_write_file_string (_PATH_RESCONF, "");
587 break;
588
589 case 2:
590 /* Second res_init is cumulative. */
591 support_write_file_string (_PATH_RESCONF,
592 "options rotate\n"
593 "nameserver 192.0.2.1\n");
594 _res.options = RES_USE_EDNS0;
595 TEST_VERIFY (res_init () == 0);
596 /* First res_init clears flag. */
597 TEST_VERIFY (!(_res.options & RES_USE_EDNS0));
598 /* And sets RES_ROTATE. */
599 TEST_VERIFY (_res.options & RES_ROTATE);
600 _res.options |= RES_USE_EDNS0;
601 TEST_VERIFY (res_init () == 0);
602 /* Second res_init preserves flag. */
603 TEST_VERIFY (_res.options & RES_USE_EDNS0);
604 TEST_VERIFY (_res.options & RES_ROTATE);
605 /* Reloading the configuration does not clear the explicitly set
606 flag. */
607 support_write_file_string (_PATH_RESCONF,
608 "nameserver 192.0.2.1\n"
609 "nameserver 192.0.2.2\n");
610 TEST_VERIFY (res_init () == 0);
611 TEST_VERIFY (_res.nscount == 2);
612 TEST_VERIFY (_res.options & RES_USE_EDNS0);
613 /* Whether RES_ROTATE (originally in resolv.conf, now removed)
614 should be preserved is subject to debate. See bug 21701. */
615 /* TEST_VERIFY (!(_res.options & RES_ROTATE)); */
616 break;
617
618 case 3:
619 case 4:
620 case 5:
621 case 6:
622 /* Test res_init change broadcast. This requires a second
623 thread to trigger the reload. */
624 #if TEST_THREAD
625 support_write_file_string (_PATH_RESCONF,
626 "options edns0\n"
627 "nameserver 192.0.2.1\n");
628 for (int iteration = 0; iteration < 2; ++iteration)
629 {
630 switch (test_index)
631 {
632 case 3:
633 TEST_VERIFY (res_init () == 0);
634 break;
635 case 4:
636 {
637 unsigned char buf[512];
638 TEST_VERIFY
639 (res_mkquery (QUERY, test_hostname, C_IN, T_A,
640 NULL, 0, NULL, buf, sizeof (buf)) > 0);
641 }
642 break;
643 case 5:
644 gethostbyname (test_hostname);
645 break;
646 case 6:
647 {
648 struct addrinfo *ai;
649 (void) getaddrinfo (test_hostname, NULL, NULL, &ai);
650 }
651 break;
652 }
653 if (iteration == 0)
654 {
655 TEST_VERIFY (_res.options & RES_USE_EDNS0);
656 TEST_VERIFY (!(_res.options & RES_ROTATE));
657 TEST_VERIFY (_res.nscount == 1);
658 support_write_file_string (_PATH_RESCONF,
659 "options rotate\n"
660 "nameserver 192.0.2.1\n"
661 "nameserver 192.0.2.2\n");
662 xpthread_join (xpthread_create
663 (NULL, special_test_call_res_init, NULL));
664 }
665 else
666 {
667 /* edns0 was dropped, but the flag is not cleared. See
668 bug 21701. */
669 /* TEST_VERIFY (!(_res.options & RES_USE_EDNS0)); */
670 TEST_VERIFY (_res.options & RES_ROTATE);
671 TEST_VERIFY (_res.nscount == 2);
672 }
673 }
674 #endif
675 break;
676 }
677 }
678
679 #if TEST_THREAD
680 /* Helper function which calls special_test_callback from a
681 thread. */
682 static void *
683 special_test_thread_func (void *closure)
684 {
685 special_test_callback (closure);
686 return NULL;
687 }
688
689 /* Variant of special_test_callback which runs the function on a
690 non-main thread. */
691 static void
692 run_special_test_on_thread (void *closure)
693 {
694 xpthread_join (xpthread_create (NULL, special_test_thread_func, closure));
695 }
696 #endif /* TEST_THREAD */
697
698 /* Perform the requested special test in a subprocess using
699 special_test_callback. */
700 static void
701 special_test (unsigned int test_index)
702 {
703 #if TEST_THREAD
704 for (int do_thread = 0; do_thread < 2; ++do_thread)
705 #endif
706 {
707 void (*func) (void *) = special_test_callback;
708 #if TEST_THREAD
709 if (do_thread)
710 func = run_special_test_on_thread;
711 #endif
712 struct support_capture_subprocess proc
713 = support_capture_subprocess (func, &test_index);
714 char *test_name = xasprintf ("special test %u", test_index);
715 if (strcmp (proc.out.buffer, "") != 0)
716 {
717 support_record_failure ();
718 printf ("error: output mismatch for %s\n", test_name);
719 support_run_diff ("expected", "",
720 "actual", proc.out.buffer);
721 }
722 support_capture_subprocess_check (&proc, test_name, 0, sc_allow_stdout);
723 free (test_name);
724 support_capture_subprocess_free (&proc);
725 }
726 }
727
728
729 /* Dummy DNS server. It ensures that the probe queries sent by
730 gethostbyname and getaddrinfo receive a reply even if the system
731 applies a very strict rate limit to localhost. */
732 static pid_t
733 start_dummy_server (void)
734 {
735 int server_socket = xsocket (AF_INET, SOCK_DGRAM, 0);
736 {
737 struct sockaddr_in sin =
738 {
739 .sin_family = AF_INET,
740 .sin_addr = { .s_addr = htonl (INADDR_LOOPBACK) },
741 .sin_port = htons (53),
742 };
743 int ret = bind (server_socket, (struct sockaddr *) &sin, sizeof (sin));
744 if (ret < 0)
745 {
746 if (errno == EACCES)
747 /* The port is reserved, which means we cannot start the
748 server. */
749 return -1;
750 FAIL_EXIT1 ("cannot bind socket to port 53: %m");
751 }
752 }
753
754 pid_t pid = xfork ();
755 if (pid == 0)
756 {
757 /* Child process. Echo back queries as SERVFAIL responses. */
758 while (true)
759 {
760 union
761 {
762 HEADER header;
763 unsigned char bytes[512];
764 } packet;
765 struct sockaddr_in sin;
766 socklen_t sinlen = sizeof (sin);
767
768 ssize_t ret = recvfrom
769 (server_socket, &packet, sizeof (packet),
770 MSG_NOSIGNAL, (struct sockaddr *) &sin, &sinlen);
771 if (ret < 0)
772 FAIL_EXIT1 ("recvfrom on fake server socket: %m");
773 if (ret > sizeof (HEADER))
774 {
775 /* Turn the query into a SERVFAIL response. */
776 packet.header.qr = 1;
777 packet.header.rcode = ns_r_servfail;
778
779 /* Send the response. */
780 ret = sendto (server_socket, &packet, ret,
781 MSG_NOSIGNAL, (struct sockaddr *) &sin, sinlen);
782 if (ret < 0)
783 /* The peer may have closed socket prematurely, so
784 this is not an error. */
785 printf ("warning: sending DNS server reply: %m\n");
786 }
787 }
788 }
789
790 /* In the parent, close the socket. */
791 xclose (server_socket);
792
793 return pid;
794 }
795
796 static int
797 do_test (void)
798 {
799 support_become_root ();
800 support_enter_network_namespace ();
801 if (!support_in_uts_namespace () || !support_can_chroot ())
802 return EXIT_UNSUPPORTED;
803
804 /* We are in an UTS namespace, so we can set the host name without
805 altering the state of the entire system. */
806 if (sethostname (test_hostname, strlen (test_hostname)) != 0)
807 FAIL_EXIT1 ("sethostname: %m");
808
809 /* These environment variables affect resolv.conf parsing. */
810 unsetenv ("LOCALDOMAIN");
811 unsetenv ("RES_OPTIONS");
812
813 /* Ensure that the chroot setup worked. */
814 {
815 struct support_capture_subprocess proc
816 = support_capture_subprocess (check_chroot_working, NULL);
817 support_capture_subprocess_check (&proc, "chroot", 0, sc_allow_none);
818 support_capture_subprocess_free (&proc);
819 }
820
821 pid_t server = start_dummy_server ();
822
823 for (size_t i = 0; test_cases[i].name != NULL; ++i)
824 {
825 if (test_verbose > 0)
826 printf ("info: running test: %s\n", test_cases[i].name);
827 TEST_VERIFY (test_cases[i].conf != NULL);
828 TEST_VERIFY (test_cases[i].expected != NULL);
829
830 support_write_file_string (path_resolv_conf, test_cases[i].conf);
831
832 test_file_contents (&test_cases[i]);
833
834 /* The expected output from the empty file test is used for
835 further tests. */
836 if (test_cases[i].conf[0] == '\0')
837 {
838 if (test_verbose > 0)
839 printf ("info: special test: missing file\n");
840 TEST_VERIFY (unlink (path_resolv_conf) == 0);
841 test_file_contents (&test_cases[i]);
842
843 if (test_verbose > 0)
844 printf ("info: special test: dangling symbolic link\n");
845 TEST_VERIFY (symlink ("does-not-exist", path_resolv_conf) == 0);
846 test_file_contents (&test_cases[i]);
847 TEST_VERIFY (unlink (path_resolv_conf) == 0);
848
849 if (test_verbose > 0)
850 printf ("info: special test: unreadable file\n");
851 support_write_file_string (path_resolv_conf, "");
852 TEST_VERIFY (chmod (path_resolv_conf, 0) == 0);
853 test_file_contents (&test_cases[i]);
854
855 /* Restore the empty file. */
856 TEST_VERIFY (unlink (path_resolv_conf) == 0);
857 support_write_file_string (path_resolv_conf, "");
858 }
859 }
860
861 /* The tests which do not follow a regular pattern. */
862 for (unsigned int test_index = 0;
863 test_index < special_tests_count; ++test_index)
864 special_test (test_index);
865
866 if (server > 0)
867 {
868 if (kill (server, SIGTERM) < 0)
869 FAIL_EXIT1 ("could not terminate server process: %m");
870 xwaitpid (server, NULL, 0);
871 }
872
873 free (path_chroot);
874 path_chroot = NULL;
875 free (path_resolv_conf);
876 path_resolv_conf = NULL;
877 return 0;
878 }
879
880 #define PREPARE prepare
881 #include <support/test-driver.c>