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