1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096 |
- /* Test parsing of /etc/resolv.conf. Genric version.
- Copyright (C) 2017-2019 Free Software Foundation, Inc.
- This file is part of the GNU C Library.
- The GNU C Library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
- The GNU C Library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
- You should have received a copy of the GNU Lesser General Public
- License along with the GNU C Library; if not, see
- <http://www.gnu.org/licenses/>. */
- /* Before including this file, TEST_THREAD has to be defined to 0 or
- 1, depending on whether the threading tests should be compiled
- in. */
- #include <arpa/inet.h>
- #include <errno.h>
- #include <gnu/lib-names.h>
- #include <netdb.h>
- #include <resolv/resolv-internal.h> /* For DEPRECATED_RES_USE_INET6. */
- #include <resolv/resolv_context.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <support/capture_subprocess.h>
- #include <support/check.h>
- #include <support/namespace.h>
- #include <support/run_diff.h>
- #include <support/support.h>
- #include <support/temp_file.h>
- #include <support/test-driver.h>
- #include <support/xsocket.h>
- #include <support/xstdio.h>
- #include <support/xunistd.h>
- #if TEST_THREAD
- # include <support/xthread.h>
- #endif
- /* This is the host name used to ensure predictable behavior of
- res_init. */
- static const char *const test_hostname = "www.example.com";
- struct support_chroot *chroot_env;
- static void
- prepare (int argc, char **argv)
- {
- chroot_env = support_chroot_create
- ((struct support_chroot_configuration)
- {
- .resolv_conf = "",
- });
- }
- /* Verify that the chroot environment has been set up. */
- static void
- check_chroot_working (void *closure)
- {
- xchroot (chroot_env->path_chroot);
- FILE *fp = xfopen (_PATH_RESCONF, "r");
- xfclose (fp);
- TEST_VERIFY_EXIT (res_init () == 0);
- TEST_VERIFY (_res.options & RES_INIT);
- char buf[100];
- if (gethostname (buf, sizeof (buf)) < 0)
- FAIL_EXIT1 ("gethostname: %m");
- if (strcmp (buf, test_hostname) != 0)
- FAIL_EXIT1 ("unexpected host name: %s", buf);
- }
- /* If FLAG is set in *OPTIONS, write NAME to FP, and clear it in
- *OPTIONS. */
- static void
- print_option_flag (FILE *fp, int *options, int flag, const char *name)
- {
- if (*options & flag)
- {
- fprintf (fp, " %s", name);
- *options &= ~flag;
- }
- }
- /* Write a decoded version of the resolver configuration *RESP to the
- stream FP. */
- static void
- print_resp (FILE *fp, res_state resp)
- {
- struct resolv_context *ctx = __resolv_context_get_override (resp);
- TEST_VERIFY_EXIT (ctx != NULL);
- if (ctx->conf == NULL)
- fprintf (fp, "; extended resolver state missing\n");
- /* The options directive. */
- {
- /* RES_INIT is used internally for tracking initialization. */
- TEST_VERIFY (resp->options & RES_INIT);
- /* Also mask out other default flags which cannot be set through
- the options directive. */
- int options
- = resp->options & ~(RES_INIT | RES_RECURSE | RES_DEFNAMES | RES_DNSRCH);
- if (options != 0
- || resp->ndots != 1
- || resp->retrans != RES_TIMEOUT
- || resp->retry != RES_DFLRETRY)
- {
- fputs ("options", fp);
- if (resp->ndots != 1)
- fprintf (fp, " ndots:%d", resp->ndots);
- if (resp->retrans != RES_TIMEOUT)
- fprintf (fp, " timeout:%d", resp->retrans);
- if (resp->retry != RES_DFLRETRY)
- fprintf (fp, " attempts:%d", resp->retry);
- print_option_flag (fp, &options, RES_USEVC, "use-vc");
- print_option_flag (fp, &options, DEPRECATED_RES_USE_INET6, "inet6");
- print_option_flag (fp, &options, RES_ROTATE, "rotate");
- print_option_flag (fp, &options, RES_USE_EDNS0, "edns0");
- print_option_flag (fp, &options, RES_SNGLKUP,
- "single-request");
- print_option_flag (fp, &options, RES_SNGLKUPREOP,
- "single-request-reopen");
- print_option_flag (fp, &options, RES_NOTLDQUERY, "no-tld-query");
- print_option_flag (fp, &options, RES_NORELOAD, "no-reload");
- fputc ('\n', fp);
- if (options != 0)
- fprintf (fp, "; error: unresolved option bits: 0x%x\n", options);
- }
- }
- /* The search and domain directives. */
- if (resp->dnsrch[0] != NULL)
- {
- fputs ("search", fp);
- for (int i = 0; i < MAXDNSRCH && resp->dnsrch[i] != NULL; ++i)
- {
- fputc (' ', fp);
- fputs (resp->dnsrch[i], fp);
- }
- fputc ('\n', fp);
- }
- else if (resp->defdname[0] != '\0')
- fprintf (fp, "domain %s\n", resp->defdname);
- /* The extended search path. */
- {
- size_t i = 0;
- while (true)
- {
- const char *name = __resolv_context_search_list (ctx, i);
- if (name == NULL)
- break;
- fprintf (fp, "; search[%zu]: %s\n", i, name);
- ++i;
- }
- }
- /* The sortlist directive. */
- if (resp->nsort > 0)
- {
- fputs ("sortlist", fp);
- for (int i = 0; i < resp->nsort && i < MAXRESOLVSORT; ++i)
- {
- char net[20];
- if (inet_ntop (AF_INET, &resp->sort_list[i].addr,
- net, sizeof (net)) == NULL)
- FAIL_EXIT1 ("inet_ntop: %m\n");
- char mask[20];
- if (inet_ntop (AF_INET, &resp->sort_list[i].mask,
- mask, sizeof (mask)) == NULL)
- FAIL_EXIT1 ("inet_ntop: %m\n");
- fprintf (fp, " %s/%s", net, mask);
- }
- fputc ('\n', fp);
- }
- /* The nameserver directives. */
- for (size_t i = 0; i < resp->nscount; ++i)
- {
- char host[NI_MAXHOST];
- char service[NI_MAXSERV];
- /* See get_nsaddr in res_send.c. */
- void *addr;
- size_t addrlen;
- if (resp->nsaddr_list[i].sin_family == 0
- && resp->_u._ext.nsaddrs[i] != NULL)
- {
- addr = resp->_u._ext.nsaddrs[i];
- addrlen = sizeof (*resp->_u._ext.nsaddrs[i]);
- }
- else
- {
- addr = &resp->nsaddr_list[i];
- addrlen = sizeof (resp->nsaddr_list[i]);
- }
- int ret = getnameinfo (addr, addrlen,
- host, sizeof (host), service, sizeof (service),
- NI_NUMERICHOST | NI_NUMERICSERV);
- if (ret != 0)
- {
- if (ret == EAI_SYSTEM)
- fprintf (fp, "; error: getnameinfo: %m\n");
- else
- fprintf (fp, "; error: getnameinfo: %s\n", gai_strerror (ret));
- }
- else
- {
- fprintf (fp, "nameserver %s\n", host);
- if (strcmp (service, "53") != 0)
- fprintf (fp, "; unrepresentable port number %s\n\n", service);
- }
- }
- /* The extended name server list. */
- {
- size_t i = 0;
- while (true)
- {
- const struct sockaddr *addr = __resolv_context_nameserver (ctx, i);
- if (addr == NULL)
- break;
- size_t addrlen;
- switch (addr->sa_family)
- {
- case AF_INET:
- addrlen = sizeof (struct sockaddr_in);
- break;
- case AF_INET6:
- addrlen = sizeof (struct sockaddr_in6);
- break;
- default:
- FAIL_EXIT1 ("invalid address family %d", addr->sa_family);
- }
- char host[NI_MAXHOST];
- char service[NI_MAXSERV];
- int ret = getnameinfo (addr, addrlen,
- host, sizeof (host), service, sizeof (service),
- NI_NUMERICHOST | NI_NUMERICSERV);
- if (ret != 0)
- {
- if (ret == EAI_SYSTEM)
- fprintf (fp, "; error: getnameinfo: %m\n");
- else
- fprintf (fp, "; error: getnameinfo: %s\n", gai_strerror (ret));
- }
- else
- fprintf (fp, "; nameserver[%zu]: [%s]:%s\n", i, host, service);
- ++i;
- }
- }
- TEST_VERIFY (!ferror (fp));
- __resolv_context_put (ctx);
- }
- /* Parameters of one test case. */
- struct test_case
- {
- /* A short, descriptive name of the test. */
- const char *name;
- /* The contents of the /etc/resolv.conf file. */
- const char *conf;
- /* The expected output from print_resp. */
- const char *expected;
- /* Setting for the LOCALDOMAIN environment variable. NULL if the
- variable is not to be set. */
- const char *localdomain;
- /* Setting for the RES_OPTIONS environment variable. NULL if the
- variable is not to be set. */
- const char *res_options;
- /* Override the system host name. NULL means that no change is made
- and the default is used (test_hostname). */
- const char *hostname;
- };
- enum test_init
- {
- test_init,
- test_ninit,
- test_mkquery,
- test_gethostbyname,
- test_getaddrinfo,
- test_init_method_last = test_getaddrinfo
- };
- static const char *const test_init_names[] =
- {
- [test_init] = "res_init",
- [test_ninit] = "res_ninit",
- [test_mkquery] = "res_mkquery",
- [test_gethostbyname] = "gethostbyname",
- [test_getaddrinfo] = "getaddrinfo",
- };
- /* Closure argument for run_res_init. */
- struct test_context
- {
- enum test_init init;
- const struct test_case *t;
- };
- static void
- setup_nss_dns_and_chroot (void)
- {
- /* Load nss_dns outside of the chroot. */
- if (dlopen (LIBNSS_DNS_SO, RTLD_LAZY) == NULL)
- FAIL_EXIT1 ("could not load " LIBNSS_DNS_SO ": %s", dlerror ());
- xchroot (chroot_env->path_chroot);
- /* Force the use of nss_dns. */
- __nss_configure_lookup ("hosts", "dns");
- }
- /* Run res_ninit or res_init in a subprocess and dump the parsed
- resolver state to standard output. */
- static void
- run_res_init (void *closure)
- {
- struct test_context *ctx = closure;
- TEST_VERIFY (getenv ("LOCALDOMAIN") == NULL);
- TEST_VERIFY (getenv ("RES_OPTIONS") == NULL);
- if (ctx->t->localdomain != NULL)
- setenv ("LOCALDOMAIN", ctx->t->localdomain, 1);
- if (ctx->t->res_options != NULL)
- setenv ("RES_OPTIONS", ctx->t->res_options, 1);
- if (ctx->t->hostname != NULL)
- {
- #ifdef CLONE_NEWUTS
- /* This test needs its own namespace, to avoid changing the host
- name for the parent, too. */
- TEST_VERIFY_EXIT (unshare (CLONE_NEWUTS) == 0);
- if (sethostname (ctx->t->hostname, strlen (ctx->t->hostname)) != 0)
- FAIL_EXIT1 ("sethostname (\"%s\"): %m", ctx->t->hostname);
- #else
- FAIL_UNSUPPORTED ("clone (CLONE_NEWUTS) not supported");
- #endif
- }
- switch (ctx->init)
- {
- case test_init:
- xchroot (chroot_env->path_chroot);
- TEST_VERIFY (res_init () == 0);
- print_resp (stdout, &_res);
- return;
- case test_ninit:
- xchroot (chroot_env->path_chroot);
- res_state resp = xmalloc (sizeof (*resp));
- memset (resp, 0, sizeof (*resp));
- TEST_VERIFY (res_ninit (resp) == 0);
- print_resp (stdout, resp);
- res_nclose (resp);
- free (resp);
- return;
- case test_mkquery:
- xchroot (chroot_env->path_chroot);
- unsigned char buf[512];
- TEST_VERIFY (res_mkquery (QUERY, "www.example",
- C_IN, ns_t_a, NULL, 0,
- NULL, buf, sizeof (buf)) > 0);
- print_resp (stdout, &_res);
- return;
- case test_gethostbyname:
- setup_nss_dns_and_chroot ();
- /* Trigger implicit initialization of the _res structure. The
- actual lookup result is immaterial. */
- (void )gethostbyname ("www.example");
- print_resp (stdout, &_res);
- return;
- case test_getaddrinfo:
- setup_nss_dns_and_chroot ();
- /* Trigger implicit initialization of the _res structure. The
- actual lookup result is immaterial. */
- struct addrinfo *ai;
- (void) getaddrinfo ("www.example", NULL, NULL, &ai);
- print_resp (stdout, &_res);
- return;
- }
- FAIL_EXIT1 ("invalid init method %d", ctx->init);
- }
- #if TEST_THREAD
- /* Helper function which calls run_res_init from a thread. */
- static void *
- run_res_init_thread_func (void *closure)
- {
- run_res_init (closure);
- return NULL;
- }
- /* Variant of res_run_init which runs the function on a non-main
- thread. */
- static void
- run_res_init_on_thread (void *closure)
- {
- xpthread_join (xpthread_create (NULL, run_res_init_thread_func, closure));
- }
- #endif /* TEST_THREAD */
- struct test_case test_cases[] =
- {
- {.name = "empty file",
- .conf = "",
- .expected = "search example.com\n"
- "; search[0]: example.com\n"
- "nameserver 127.0.0.1\n"
- "; nameserver[0]: [127.0.0.1]:53\n"
- },
- {.name = "empty file, no-dot hostname",
- .conf = "",
- .expected = "nameserver 127.0.0.1\n"
- "; nameserver[0]: [127.0.0.1]:53\n",
- .hostname = "example",
- },
- {.name = "empty file with LOCALDOMAIN",
- .conf = "",
- .expected = "search example.net\n"
- "; search[0]: example.net\n"
- "nameserver 127.0.0.1\n"
- "; nameserver[0]: [127.0.0.1]:53\n",
- .localdomain = "example.net",
- },
- {.name = "empty file with RES_OPTIONS",
- .conf = "",
- .expected = "options attempts:5 edns0\n"
- "search example.com\n"
- "; search[0]: example.com\n"
- "nameserver 127.0.0.1\n"
- "; nameserver[0]: [127.0.0.1]:53\n",
- .res_options = "edns0 attempts:5",
- },
- {.name = "empty file with RES_OPTIONS and LOCALDOMAIN",
- .conf = "",
- .expected = "options attempts:5 edns0\n"
- "search example.org\n"
- "; search[0]: example.org\n"
- "nameserver 127.0.0.1\n"
- "; nameserver[0]: [127.0.0.1]:53\n",
- .localdomain = "example.org",
- .res_options = "edns0 attempts:5",
- },
- {.name = "basic",
- .conf = "search corp.example.com example.com\n"
- "nameserver 192.0.2.1\n",
- .expected = "search corp.example.com example.com\n"
- "; search[0]: corp.example.com\n"
- "; search[1]: example.com\n"
- "nameserver 192.0.2.1\n"
- "; nameserver[0]: [192.0.2.1]:53\n"
- },
- {.name = "basic with no-dot hostname",
- .conf = "search corp.example.com example.com\n"
- "nameserver 192.0.2.1\n",
- .expected = "search corp.example.com example.com\n"
- "; search[0]: corp.example.com\n"
- "; search[1]: example.com\n"
- "nameserver 192.0.2.1\n"
- "; nameserver[0]: [192.0.2.1]:53\n",
- .hostname = "example",
- },
- {.name = "basic no-reload",
- .conf = "options no-reload\n"
- "search corp.example.com example.com\n"
- "nameserver 192.0.2.1\n",
- .expected = "options no-reload\n"
- "search corp.example.com example.com\n"
- "; search[0]: corp.example.com\n"
- "; search[1]: example.com\n"
- "nameserver 192.0.2.1\n"
- "; nameserver[0]: [192.0.2.1]:53\n"
- },
- {.name = "basic no-reload via RES_OPTIONS",
- .conf = "search corp.example.com example.com\n"
- "nameserver 192.0.2.1\n",
- .expected = "options no-reload\n"
- "search corp.example.com example.com\n"
- "; search[0]: corp.example.com\n"
- "; search[1]: example.com\n"
- "nameserver 192.0.2.1\n"
- "; nameserver[0]: [192.0.2.1]:53\n",
- .res_options = "no-reload"
- },
- {.name = "whitespace",
- .conf = "# This test covers comment and whitespace processing "
- " (trailing whitespace,\n"
- "# missing newline at end of file).\n"
- "\n"
- ";search commented out\n"
- "search corp.example.com\texample.com \n"
- "#nameserver 192.0.2.3\n"
- "nameserver 192.0.2.1 \n"
- "nameserver 192.0.2.2", /* No \n at end of file. */
- .expected = "search corp.example.com example.com\n"
- "; search[0]: corp.example.com\n"
- "; search[1]: example.com\n"
- "nameserver 192.0.2.1\n"
- "nameserver 192.0.2.2\n"
- "; nameserver[0]: [192.0.2.1]:53\n"
- "; nameserver[1]: [192.0.2.2]:53\n"
- },
- {.name = "domain",
- .conf = "domain example.net\n"
- "nameserver 192.0.2.1\n",
- .expected = "search example.net\n"
- "; search[0]: example.net\n"
- "nameserver 192.0.2.1\n"
- "; nameserver[0]: [192.0.2.1]:53\n"
- },
- {.name = "domain space",
- .conf = "domain example.net \n"
- "nameserver 192.0.2.1\n",
- .expected = "search example.net\n"
- "; search[0]: example.net\n"
- "nameserver 192.0.2.1\n"
- "; nameserver[0]: [192.0.2.1]:53\n"
- },
- {.name = "domain tab",
- .conf = "domain example.net\t\n"
- "nameserver 192.0.2.1\n",
- .expected = "search example.net\n"
- "; search[0]: example.net\n"
- "nameserver 192.0.2.1\n"
- "; nameserver[0]: [192.0.2.1]:53\n"
- },
- {.name = "domain override",
- .conf = "search example.com example.org\n"
- "nameserver 192.0.2.1\n"
- "domain example.net", /* No \n at end of file. */
- .expected = "search example.net\n"
- "; search[0]: example.net\n"
- "nameserver 192.0.2.1\n"
- "; nameserver[0]: [192.0.2.1]:53\n"
- },
- {.name = "option values, multiple servers",
- .conf = "options\tinet6\tndots:3 edns0\tattempts:5\ttimeout:19\n"
- "domain example.net\n"
- ";domain comment\n"
- "search corp.example.com\texample.com\n"
- "nameserver 192.0.2.1\n"
- "nameserver ::1\n"
- "nameserver 192.0.2.2\n",
- .expected = "options ndots:3 timeout:19 attempts:5 inet6 edns0\n"
- "search corp.example.com example.com\n"
- "; search[0]: corp.example.com\n"
- "; search[1]: example.com\n"
- "nameserver 192.0.2.1\n"
- "nameserver ::1\n"
- "nameserver 192.0.2.2\n"
- "; nameserver[0]: [192.0.2.1]:53\n"
- "; nameserver[1]: [::1]:53\n"
- "; nameserver[2]: [192.0.2.2]:53\n"
- },
- {.name = "out-of-range option vales",
- .conf = "options use-vc timeout:999 attempts:999 ndots:99\n"
- "search example.com\n",
- .expected = "options ndots:15 timeout:30 attempts:5 use-vc\n"
- "search example.com\n"
- "; search[0]: example.com\n"
- "nameserver 127.0.0.1\n"
- "; nameserver[0]: [127.0.0.1]:53\n"
- },
- {.name = "repeated directives",
- .conf = "options ndots:3 use-vc\n"
- "options edns0 ndots:2\n"
- "domain corp.example\n"
- "search example.net corp.example.com example.com\n"
- "search example.org\n"
- "search\n",
- .expected = "options ndots:2 use-vc edns0\n"
- "search example.org\n"
- "; search[0]: example.org\n"
- "nameserver 127.0.0.1\n"
- "; nameserver[0]: [127.0.0.1]:53\n"
- },
- {.name = "many name servers, sortlist",
- .conf = "options single-request\n"
- "search example.org example.com example.net corp.example.com\n"
- "sortlist 192.0.2.0/255.255.255.0\n"
- "nameserver 192.0.2.1\n"
- "nameserver 192.0.2.2\n"
- "nameserver 192.0.2.3\n"
- "nameserver 192.0.2.4\n"
- "nameserver 192.0.2.5\n"
- "nameserver 192.0.2.6\n"
- "nameserver 192.0.2.7\n"
- "nameserver 192.0.2.8\n",
- .expected = "options single-request\n"
- "search example.org example.com example.net corp.example.com\n"
- "; search[0]: example.org\n"
- "; search[1]: example.com\n"
- "; search[2]: example.net\n"
- "; search[3]: corp.example.com\n"
- "sortlist 192.0.2.0/255.255.255.0\n"
- "nameserver 192.0.2.1\n"
- "nameserver 192.0.2.2\n"
- "nameserver 192.0.2.3\n"
- "; nameserver[0]: [192.0.2.1]:53\n"
- "; nameserver[1]: [192.0.2.2]:53\n"
- "; nameserver[2]: [192.0.2.3]:53\n"
- "; nameserver[3]: [192.0.2.4]:53\n"
- "; nameserver[4]: [192.0.2.5]:53\n"
- "; nameserver[5]: [192.0.2.6]:53\n"
- "; nameserver[6]: [192.0.2.7]:53\n"
- "; nameserver[7]: [192.0.2.8]:53\n"
- },
- {.name = "IPv4 and IPv6 nameservers",
- .conf = "options single-request\n"
- "search example.org example.com example.net corp.example.com"
- " legacy.example.com\n"
- "sortlist 192.0.2.0\n"
- "nameserver 192.0.2.1\n"
- "nameserver 2001:db8::2\n"
- "nameserver 192.0.2.3\n"
- "nameserver 2001:db8::4\n"
- "nameserver 192.0.2.5\n"
- "nameserver 2001:db8::6\n"
- "nameserver 192.0.2.7\n"
- "nameserver 2001:db8::8\n",
- .expected = "options single-request\n"
- "search example.org example.com example.net corp.example.com"
- " legacy.example.com\n"
- "; search[0]: example.org\n"
- "; search[1]: example.com\n"
- "; search[2]: example.net\n"
- "; search[3]: corp.example.com\n"
- "; search[4]: legacy.example.com\n"
- "sortlist 192.0.2.0/255.255.255.0\n"
- "nameserver 192.0.2.1\n"
- "nameserver 2001:db8::2\n"
- "nameserver 192.0.2.3\n"
- "; nameserver[0]: [192.0.2.1]:53\n"
- "; nameserver[1]: [2001:db8::2]:53\n"
- "; nameserver[2]: [192.0.2.3]:53\n"
- "; nameserver[3]: [2001:db8::4]:53\n"
- "; nameserver[4]: [192.0.2.5]:53\n"
- "; nameserver[5]: [2001:db8::6]:53\n"
- "; nameserver[6]: [192.0.2.7]:53\n"
- "; nameserver[7]: [2001:db8::8]:53\n",
- },
- {.name = "garbage after nameserver",
- .conf = "nameserver 192.0.2.1 garbage\n"
- "nameserver 192.0.2.2:5353\n"
- "nameserver 192.0.2.3 5353\n",
- .expected = "search example.com\n"
- "; search[0]: example.com\n"
- "nameserver 192.0.2.1\n"
- "nameserver 192.0.2.3\n"
- "; nameserver[0]: [192.0.2.1]:53\n"
- "; nameserver[1]: [192.0.2.3]:53\n"
- },
- {.name = "RES_OPTIONS is cummulative",
- .conf = "options timeout:7 ndots:2 use-vc\n"
- "nameserver 192.0.2.1\n",
- .expected = "options ndots:3 timeout:7 attempts:5 use-vc edns0\n"
- "search example.com\n"
- "; search[0]: example.com\n"
- "nameserver 192.0.2.1\n"
- "; nameserver[0]: [192.0.2.1]:53\n",
- .res_options = "attempts:5 ndots:3 edns0 ",
- },
- {.name = "many search list entries (bug 19569)",
- .conf = "nameserver 192.0.2.1\n"
- "search corp.example.com support.example.com"
- " community.example.org wan.example.net vpn.example.net"
- " example.com example.org example.net\n",
- .expected = "search corp.example.com support.example.com"
- " community.example.org wan.example.net vpn.example.net example.com\n"
- "; search[0]: corp.example.com\n"
- "; search[1]: support.example.com\n"
- "; search[2]: community.example.org\n"
- "; search[3]: wan.example.net\n"
- "; search[4]: vpn.example.net\n"
- "; search[5]: example.com\n"
- "; search[6]: example.org\n"
- "; search[7]: example.net\n"
- "nameserver 192.0.2.1\n"
- "; nameserver[0]: [192.0.2.1]:53\n"
- },
- {.name = "very long search list entries (bug 21475)",
- .conf = "nameserver 192.0.2.1\n"
- "search example.com "
- #define H63 "this-host-name-is-longer-than-yours-yes-I-really-really-mean-it"
- #define D63 "this-domain-name-is-as-long-as-the-previous-name--63-characters"
- " " H63 "." D63 ".example.org"
- " " H63 "." D63 ".example.net\n",
- .expected = "search example.com " H63 "." D63 ".example.org\n"
- "; search[0]: example.com\n"
- "; search[1]: " H63 "." D63 ".example.org\n"
- "; search[2]: " H63 "." D63 ".example.net\n"
- #undef H63
- #undef D63
- "nameserver 192.0.2.1\n"
- "; nameserver[0]: [192.0.2.1]:53\n"
- },
- { NULL }
- };
- /* Run the indicated test case. This function assumes that the chroot
- contents has already been set up. */
- static void
- test_file_contents (const struct test_case *t)
- {
- #if TEST_THREAD
- for (int do_thread = 0; do_thread < 2; ++do_thread)
- #endif
- for (int init_method = 0; init_method <= test_init_method_last;
- ++init_method)
- {
- if (test_verbose > 0)
- printf ("info: testing init method %s\n",
- test_init_names[init_method]);
- struct test_context ctx = { .init = init_method, .t = t };
- void (*func) (void *) = run_res_init;
- #if TEST_THREAD
- if (do_thread)
- func = run_res_init_on_thread;
- #endif
- struct support_capture_subprocess proc
- = support_capture_subprocess (func, &ctx);
- if (strcmp (proc.out.buffer, t->expected) != 0)
- {
- support_record_failure ();
- printf ("error: output mismatch for %s (init method %s)\n",
- t->name, test_init_names[init_method]);
- support_run_diff ("expected", t->expected,
- "actual", proc.out.buffer);
- }
- support_capture_subprocess_check (&proc, t->name, 0,
- sc_allow_stdout);
- support_capture_subprocess_free (&proc);
- }
- }
- /* Special tests which do not follow the general pattern. */
- enum { special_tests_count = 11 };
- /* Implementation of special tests. */
- static void
- special_test_callback (void *closure)
- {
- unsigned int *test_indexp = closure;
- unsigned test_index = *test_indexp;
- TEST_VERIFY (test_index < special_tests_count);
- if (test_verbose > 0)
- printf ("info: special test %u\n", test_index);
- xchroot (chroot_env->path_chroot);
- switch (test_index)
- {
- case 0:
- case 1:
- /* Second res_init with missing or empty file preserves
- flags. */
- if (test_index == 1)
- TEST_VERIFY (unlink (_PATH_RESCONF) == 0);
- _res.options = RES_USE_EDNS0;
- TEST_VERIFY (res_init () == 0);
- /* First res_init clears flag. */
- TEST_VERIFY (!(_res.options & RES_USE_EDNS0));
- _res.options |= RES_USE_EDNS0;
- TEST_VERIFY (res_init () == 0);
- /* Second res_init preserves flag. */
- TEST_VERIFY (_res.options & RES_USE_EDNS0);
- if (test_index == 1)
- /* Restore empty file. */
- support_write_file_string (_PATH_RESCONF, "");
- break;
- case 2:
- /* Second res_init is cumulative. */
- support_write_file_string (_PATH_RESCONF,
- "options rotate\n"
- "nameserver 192.0.2.1\n");
- _res.options = RES_USE_EDNS0;
- TEST_VERIFY (res_init () == 0);
- /* First res_init clears flag. */
- TEST_VERIFY (!(_res.options & RES_USE_EDNS0));
- /* And sets RES_ROTATE. */
- TEST_VERIFY (_res.options & RES_ROTATE);
- _res.options |= RES_USE_EDNS0;
- TEST_VERIFY (res_init () == 0);
- /* Second res_init preserves flag. */
- TEST_VERIFY (_res.options & RES_USE_EDNS0);
- TEST_VERIFY (_res.options & RES_ROTATE);
- /* Reloading the configuration does not clear the explicitly set
- flag. */
- support_write_file_string (_PATH_RESCONF,
- "nameserver 192.0.2.1\n"
- "nameserver 192.0.2.2\n");
- TEST_VERIFY (res_init () == 0);
- TEST_VERIFY (_res.nscount == 2);
- TEST_VERIFY (_res.options & RES_USE_EDNS0);
- /* Whether RES_ROTATE (originally in resolv.conf, now removed)
- should be preserved is subject to debate. See bug 21701. */
- /* TEST_VERIFY (!(_res.options & RES_ROTATE)); */
- break;
- case 3:
- case 4:
- case 5:
- case 6:
- support_write_file_string (_PATH_RESCONF,
- "options edns0\n"
- "nameserver 192.0.2.1\n");
- goto reload_tests;
- case 7: /* 7 and the following tests are with no-reload. */
- case 8:
- case 9:
- case 10:
- support_write_file_string (_PATH_RESCONF,
- "options edns0 no-reload\n"
- "nameserver 192.0.2.1\n");
- /* Fall through. */
- reload_tests:
- for (int iteration = 0; iteration < 2; ++iteration)
- {
- switch (test_index)
- {
- case 3:
- case 7:
- TEST_VERIFY (res_init () == 0);
- break;
- case 4:
- case 8:
- {
- unsigned char buf[512];
- TEST_VERIFY
- (res_mkquery (QUERY, test_hostname, C_IN, T_A,
- NULL, 0, NULL, buf, sizeof (buf)) > 0);
- }
- break;
- case 5:
- case 9:
- gethostbyname (test_hostname);
- break;
- case 6:
- case 10:
- {
- struct addrinfo *ai;
- (void) getaddrinfo (test_hostname, NULL, NULL, &ai);
- }
- break;
- }
- /* test_index == 7 is res_init and performs a reload even
- with no-reload. */
- if (iteration == 0 || test_index > 7)
- {
- TEST_VERIFY (_res.options & RES_USE_EDNS0);
- TEST_VERIFY (!(_res.options & RES_ROTATE));
- if (test_index < 7)
- TEST_VERIFY (!(_res.options & RES_NORELOAD));
- else
- TEST_VERIFY (_res.options & RES_NORELOAD);
- TEST_VERIFY (_res.nscount == 1);
- /* File change triggers automatic reloading. */
- support_write_file_string (_PATH_RESCONF,
- "options rotate\n"
- "nameserver 192.0.2.1\n"
- "nameserver 192.0.2.2\n");
- }
- else
- {
- if (test_index != 3 && test_index != 7)
- /* test_index 3, 7 are res_init; this function does
- not reset flags. See bug 21701. */
- TEST_VERIFY (!(_res.options & RES_USE_EDNS0));
- TEST_VERIFY (_res.options & RES_ROTATE);
- TEST_VERIFY (_res.nscount == 2);
- }
- }
- break;
- }
- }
- #if TEST_THREAD
- /* Helper function which calls special_test_callback from a
- thread. */
- static void *
- special_test_thread_func (void *closure)
- {
- special_test_callback (closure);
- return NULL;
- }
- /* Variant of special_test_callback which runs the function on a
- non-main thread. */
- static void
- run_special_test_on_thread (void *closure)
- {
- xpthread_join (xpthread_create (NULL, special_test_thread_func, closure));
- }
- #endif /* TEST_THREAD */
- /* Perform the requested special test in a subprocess using
- special_test_callback. */
- static void
- special_test (unsigned int test_index)
- {
- #if TEST_THREAD
- for (int do_thread = 0; do_thread < 2; ++do_thread)
- #endif
- {
- void (*func) (void *) = special_test_callback;
- #if TEST_THREAD
- if (do_thread)
- func = run_special_test_on_thread;
- #endif
- struct support_capture_subprocess proc
- = support_capture_subprocess (func, &test_index);
- char *test_name = xasprintf ("special test %u", test_index);
- if (strcmp (proc.out.buffer, "") != 0)
- {
- support_record_failure ();
- printf ("error: output mismatch for %s\n", test_name);
- support_run_diff ("expected", "",
- "actual", proc.out.buffer);
- }
- support_capture_subprocess_check (&proc, test_name, 0, sc_allow_stdout);
- free (test_name);
- support_capture_subprocess_free (&proc);
- }
- }
- /* Dummy DNS server. It ensures that the probe queries sent by
- gethostbyname and getaddrinfo receive a reply even if the system
- applies a very strict rate limit to localhost. */
- static pid_t
- start_dummy_server (void)
- {
- int server_socket = xsocket (AF_INET, SOCK_DGRAM, 0);
- {
- struct sockaddr_in sin =
- {
- .sin_family = AF_INET,
- .sin_addr = { .s_addr = htonl (INADDR_LOOPBACK) },
- .sin_port = htons (53),
- };
- int ret = bind (server_socket, (struct sockaddr *) &sin, sizeof (sin));
- if (ret < 0)
- {
- if (errno == EACCES)
- /* The port is reserved, which means we cannot start the
- server. */
- return -1;
- FAIL_EXIT1 ("cannot bind socket to port 53: %m");
- }
- }
- pid_t pid = xfork ();
- if (pid == 0)
- {
- /* Child process. Echo back queries as SERVFAIL responses. */
- while (true)
- {
- union
- {
- HEADER header;
- unsigned char bytes[512];
- } packet;
- struct sockaddr_in sin;
- socklen_t sinlen = sizeof (sin);
- ssize_t ret = recvfrom
- (server_socket, &packet, sizeof (packet),
- MSG_NOSIGNAL, (struct sockaddr *) &sin, &sinlen);
- if (ret < 0)
- FAIL_EXIT1 ("recvfrom on fake server socket: %m");
- if (ret > sizeof (HEADER))
- {
- /* Turn the query into a SERVFAIL response. */
- packet.header.qr = 1;
- packet.header.rcode = ns_r_servfail;
- /* Send the response. */
- ret = sendto (server_socket, &packet, ret,
- MSG_NOSIGNAL, (struct sockaddr *) &sin, sinlen);
- if (ret < 0)
- /* The peer may have closed socket prematurely, so
- this is not an error. */
- printf ("warning: sending DNS server reply: %m\n");
- }
- }
- }
- /* In the parent, close the socket. */
- xclose (server_socket);
- return pid;
- }
- static int
- do_test (void)
- {
- support_become_root ();
- support_enter_network_namespace ();
- if (!support_in_uts_namespace () || !support_can_chroot ())
- return EXIT_UNSUPPORTED;
- /* We are in an UTS namespace, so we can set the host name without
- altering the state of the entire system. */
- if (sethostname (test_hostname, strlen (test_hostname)) != 0)
- FAIL_EXIT1 ("sethostname: %m");
- /* These environment variables affect resolv.conf parsing. */
- unsetenv ("LOCALDOMAIN");
- unsetenv ("RES_OPTIONS");
- /* Ensure that the chroot setup worked. */
- {
- struct support_capture_subprocess proc
- = support_capture_subprocess (check_chroot_working, NULL);
- support_capture_subprocess_check (&proc, "chroot", 0, sc_allow_none);
- support_capture_subprocess_free (&proc);
- }
- pid_t server = start_dummy_server ();
- for (size_t i = 0; test_cases[i].name != NULL; ++i)
- {
- if (test_verbose > 0)
- printf ("info: running test: %s\n", test_cases[i].name);
- TEST_VERIFY (test_cases[i].conf != NULL);
- TEST_VERIFY (test_cases[i].expected != NULL);
- support_write_file_string (chroot_env->path_resolv_conf,
- test_cases[i].conf);
- test_file_contents (&test_cases[i]);
- /* The expected output from the empty file test is used for
- further tests. */
- if (test_cases[i].conf[0] == '\0')
- {
- if (test_verbose > 0)
- printf ("info: special test: missing file\n");
- TEST_VERIFY (unlink (chroot_env->path_resolv_conf) == 0);
- test_file_contents (&test_cases[i]);
- if (test_verbose > 0)
- printf ("info: special test: dangling symbolic link\n");
- TEST_VERIFY (symlink ("does-not-exist", chroot_env->path_resolv_conf) == 0);
- test_file_contents (&test_cases[i]);
- TEST_VERIFY (unlink (chroot_env->path_resolv_conf) == 0);
- if (test_verbose > 0)
- printf ("info: special test: unreadable file\n");
- support_write_file_string (chroot_env->path_resolv_conf, "");
- TEST_VERIFY (chmod (chroot_env->path_resolv_conf, 0) == 0);
- test_file_contents (&test_cases[i]);
- /* Restore the empty file. */
- TEST_VERIFY (unlink (chroot_env->path_resolv_conf) == 0);
- support_write_file_string (chroot_env->path_resolv_conf, "");
- }
- }
- /* The tests which do not follow a regular pattern. */
- for (unsigned int test_index = 0;
- test_index < special_tests_count; ++test_index)
- special_test (test_index);
- if (server > 0)
- {
- if (kill (server, SIGTERM) < 0)
- FAIL_EXIT1 ("could not terminate server process: %m");
- xwaitpid (server, NULL, 0);
- }
- support_chroot_free (chroot_env);
- return 0;
- }
- #define PREPARE prepare
- #include <support/test-driver.c>
|