Blame resolv/tst-resolv-res_init-skeleton.c

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