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