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