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