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

source code of glibc/resolv/tst-resolv-res_init-skeleton.c