Blame nss/tst-nss-files-hosts-multi.c

Packit 6c4009
/* Parse /etc/hosts in multi mode with many addresses/aliases.
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
#include <dlfcn.h>
Packit 6c4009
#include <errno.h>
Packit 6c4009
#include <gnu/lib-names.h>
Packit 6c4009
#include <netdb.h>
Packit 6c4009
#include <nss.h>
Packit 6c4009
#include <stdbool.h>
Packit 6c4009
#include <stdlib.h>
Packit 6c4009
#include <string.h>
Packit 6c4009
#include <support/check.h>
Packit 6c4009
#include <support/check_nss.h>
Packit 6c4009
#include <support/namespace.h>
Packit 6c4009
#include <support/support.h>
Packit 6c4009
#include <support/test-driver.h>
Packit 6c4009
#include <support/test-driver.h>
Packit 6c4009
#include <support/xmemstream.h>
Packit 6c4009
#include <support/xstdio.h>
Packit 6c4009
#include <support/xunistd.h>
Packit 6c4009
#include <sys/resource.h>
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
       .hosts = "",             /* See write_hosts below.  */
Packit 6c4009
       .host_conf = "multi on\n",
Packit 6c4009
     });
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Create the /etc/hosts file from outside the chroot.  */
Packit 6c4009
static void
Packit 6c4009
write_hosts (int count)
Packit 6c4009
{
Packit 6c4009
  TEST_VERIFY (count > 0 && count <= 65535);
Packit 6c4009
  FILE *fp = xfopen (chroot_env->path_hosts, "w");
Packit 6c4009
  fputs ("127.0.0.1   localhost localhost.localdomain\n"
Packit 6c4009
         "::1         localhost localhost.localdomain\n",
Packit 6c4009
         fp);
Packit 6c4009
  for (int i = 0; i < count; ++i)
Packit 6c4009
    {
Packit 6c4009
      fprintf (fp, "10.4.%d.%d www4.example.com\n",
Packit 6c4009
               (i / 256) & 0xff, i & 0xff);
Packit 6c4009
      fprintf (fp, "10.46.%d.%d www.example.com\n",
Packit 6c4009
               (i / 256) & 0xff, i & 0xff);
Packit 6c4009
      fprintf (fp, "192.0.2.1 alias.example.com v4-%d.example.com\n", i);
Packit 6c4009
      fprintf (fp, "2001:db8::6:%x www6.example.com\n", i);
Packit 6c4009
      fprintf (fp, "2001:db8::46:%x www.example.com\n", i);
Packit 6c4009
      fprintf (fp, "2001:db8::1 alias.example.com v6-%d.example.com\n", i);
Packit 6c4009
    }
Packit 6c4009
  xfclose (fp);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Parameters of a single test.  */
Packit 6c4009
struct test_params
Packit 6c4009
{
Packit 6c4009
  const char *name;             /* Name to query.  */
Packit 6c4009
  const char *marker;           /* Address marker for the name.  */
Packit 6c4009
  int count;                    /* Number of addresses/aliases.  */
Packit 6c4009
  int family;                   /* AF_INET, AF_INET_6 or AF_UNSPEC.  */
Packit 6c4009
  bool canonname;               /* True if AI_CANONNAME should be enabled.  */
Packit 6c4009
};
Packit 6c4009
Packit 6c4009
/* Expected result of gethostbyname/gethostbyname2.  */
Packit 6c4009
static char *
Packit 6c4009
expected_ghbn (const struct test_params *params)
Packit 6c4009
{
Packit 6c4009
  TEST_VERIFY (params->family == AF_INET || params->family == AF_INET6);
Packit 6c4009
Packit 6c4009
  struct xmemstream expected;
Packit 6c4009
  xopen_memstream (&expected);
Packit 6c4009
  if (strcmp (params->name, "alias.example.com") == 0)
Packit 6c4009
    {
Packit 6c4009
      fprintf (expected.out, "name: %s\n", params->name);
Packit 6c4009
      char af;
Packit 6c4009
      if (params->family == AF_INET)
Packit 6c4009
        af = '4';
Packit 6c4009
      else
Packit 6c4009
        af = '6';
Packit 6c4009
      for (int i = 0; i < params->count; ++i)
Packit 6c4009
        fprintf (expected.out, "alias: v%c-%d.example.com\n", af, i);
Packit 6c4009
Packit 6c4009
      for (int i = 0; i < params->count; ++i)
Packit 6c4009
        if (params->family == AF_INET)
Packit 6c4009
          fputs ("address: 192.0.2.1\n", expected.out);
Packit 6c4009
        else
Packit 6c4009
          fputs ("address: 2001:db8::1\n", expected.out);
Packit 6c4009
    }
Packit 6c4009
  else /* www/www4/www6 name.  */
Packit 6c4009
    {
Packit 6c4009
      bool do_ipv4 = params->family == AF_INET
Packit 6c4009
        && strncmp (params->name, "www6", 4) != 0;
Packit 6c4009
      bool do_ipv6 = params->family == AF_INET6
Packit 6c4009
        && strncmp (params->name, "www4", 4) != 0;
Packit 6c4009
      if (do_ipv4 || do_ipv6)
Packit 6c4009
        {
Packit 6c4009
          fprintf (expected.out, "name: %s\n", params->name);
Packit 6c4009
          if (do_ipv4)
Packit 6c4009
            for (int i = 0; i < params->count; ++i)
Packit 6c4009
              fprintf (expected.out, "address: 10.%s.%d.%d\n",
Packit 6c4009
                       params->marker, i / 256, i % 256);
Packit 6c4009
          if (do_ipv6)
Packit 6c4009
            for (int i = 0; i < params->count; ++i)
Packit 6c4009
              fprintf (expected.out, "address: 2001:db8::%s:%x\n",
Packit 6c4009
                       params->marker, i);
Packit 6c4009
        }
Packit 6c4009
      else
Packit 6c4009
        fputs ("error: HOST_NOT_FOUND\n", expected.out);
Packit 6c4009
    }
Packit 6c4009
  xfclose_memstream (&expected);
Packit 6c4009
  return expected.buffer;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Expected result of getaddrinfo.  */
Packit 6c4009
static char *
Packit 6c4009
expected_gai (const struct test_params *params)
Packit 6c4009
{
Packit 6c4009
  bool do_ipv4 = false;
Packit 6c4009
  bool do_ipv6 = false;
Packit 6c4009
  if (params->family == AF_UNSPEC)
Packit 6c4009
    do_ipv4 = do_ipv6 = true;
Packit 6c4009
  else if (params->family == AF_INET)
Packit 6c4009
    do_ipv4 = true;
Packit 6c4009
  else if (params->family == AF_INET6)
Packit 6c4009
    do_ipv6 = true;
Packit 6c4009
Packit 6c4009
  struct xmemstream expected;
Packit 6c4009
  xopen_memstream (&expected);
Packit 6c4009
  if (strcmp (params->name, "alias.example.com") == 0)
Packit 6c4009
    {
Packit 6c4009
      if (params->canonname)
Packit 6c4009
        fprintf (expected.out,
Packit 6c4009
                 "flags: AI_CANONNAME\n"
Packit 6c4009
                 "canonname: %s\n",
Packit 6c4009
                 params->name);
Packit 6c4009
Packit 6c4009
      if (do_ipv4)
Packit 6c4009
        for (int i = 0; i < params->count; ++i)
Packit 6c4009
          fputs ("address: STREAM/TCP 192.0.2.1 80\n", expected.out);
Packit 6c4009
      if (do_ipv6)
Packit 6c4009
        for (int i = 0; i < params->count; ++i)
Packit 6c4009
          fputs ("address: STREAM/TCP 2001:db8::1 80\n", expected.out);
Packit 6c4009
    }
Packit 6c4009
  else /* www/www4/www6 name.  */
Packit 6c4009
    {
Packit 6c4009
      if (strncmp (params->name, "www4", 4) == 0)
Packit 6c4009
        do_ipv6 = false;
Packit 6c4009
      else if (strncmp (params->name, "www6", 4) == 0)
Packit 6c4009
        do_ipv4 = false;
Packit 6c4009
      /* Otherwise, we have www as the name, so we do both.  */
Packit 6c4009
Packit 6c4009
      if (do_ipv4 || do_ipv6)
Packit 6c4009
        {
Packit 6c4009
          if (params->canonname)
Packit 6c4009
            fprintf (expected.out,
Packit 6c4009
                     "flags: AI_CANONNAME\n"
Packit 6c4009
                     "canonname: %s\n",
Packit 6c4009
                     params->name);
Packit 6c4009
Packit 6c4009
          if (do_ipv4)
Packit 6c4009
            for (int i = 0; i < params->count; ++i)
Packit 6c4009
              fprintf (expected.out, "address: STREAM/TCP 10.%s.%d.%d 80\n",
Packit 6c4009
                       params->marker, i / 256, i % 256);
Packit 6c4009
          if (do_ipv6)
Packit 6c4009
            for (int i = 0; i < params->count; ++i)
Packit 6c4009
              fprintf (expected.out,
Packit 6c4009
                       "address: STREAM/TCP 2001:db8::%s:%x 80\n",
Packit 6c4009
                       params->marker, i);
Packit 6c4009
        }
Packit 6c4009
      else
Packit 6c4009
        fputs ("error: Name or service not known\n", expected.out);
Packit 6c4009
    }
Packit 6c4009
  xfclose_memstream (&expected);
Packit 6c4009
  return expected.buffer;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
static void
Packit 6c4009
run_gbhn_gai (struct test_params *params)
Packit 6c4009
{
Packit 6c4009
  char *ctx = xasprintf ("name=%s marker=%s count=%d family=%d",
Packit 6c4009
                         params->name, params->marker, params->count,
Packit 6c4009
                         params->family);
Packit 6c4009
  if (test_verbose > 0)
Packit 6c4009
    printf ("info: %s\n", ctx);
Packit 6c4009
Packit 6c4009
  /* Check gethostbyname, gethostbyname2.  */
Packit 6c4009
  if (params->family == AF_INET)
Packit 6c4009
    {
Packit 6c4009
      char *expected = expected_ghbn (params);
Packit 6c4009
      check_hostent (ctx, gethostbyname (params->name), expected);
Packit 6c4009
      free (expected);
Packit 6c4009
    }
Packit 6c4009
  if (params->family != AF_UNSPEC)
Packit 6c4009
    {
Packit 6c4009
      char *expected = expected_ghbn (params);
Packit 6c4009
      check_hostent (ctx, gethostbyname2 (params->name, params->family),
Packit 6c4009
                     expected);
Packit 6c4009
      free (expected);
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* Check getaddrinfo.  */
Packit 6c4009
  for (int do_canonical = 0; do_canonical < 2; ++do_canonical)
Packit 6c4009
    {
Packit 6c4009
      params->canonname = do_canonical;
Packit 6c4009
      char *expected = expected_gai (params);
Packit 6c4009
      struct addrinfo hints =
Packit 6c4009
        {
Packit 6c4009
          .ai_family = params->family,
Packit 6c4009
          .ai_socktype = SOCK_STREAM,
Packit 6c4009
          .ai_protocol = IPPROTO_TCP,
Packit 6c4009
        };
Packit 6c4009
      if (do_canonical)
Packit 6c4009
        hints.ai_flags |= AI_CANONNAME;
Packit 6c4009
      struct addrinfo *ai;
Packit 6c4009
      int ret = getaddrinfo (params->name, "80", &hints, &ai;;
Packit 6c4009
      check_addrinfo (ctx, ai, ret, expected);
Packit 6c4009
      if (ret == 0)
Packit 6c4009
        freeaddrinfo (ai);
Packit 6c4009
      free (expected);
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  free (ctx);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Callback for the subprocess which runs the test in a chroot.  */
Packit 6c4009
static void
Packit 6c4009
subprocess (void *closure)
Packit 6c4009
{
Packit 6c4009
  struct test_params *params = closure;
Packit 6c4009
Packit 6c4009
  xchroot (chroot_env->path_chroot);
Packit 6c4009
Packit 6c4009
  static const int families[] = { AF_INET, AF_INET6, AF_UNSPEC, -1 };
Packit 6c4009
  static const char *const names[] =
Packit 6c4009
    {
Packit 6c4009
      "www.example.com", "www4.example.com", "www6.example.com",
Packit 6c4009
      "alias.example.com",
Packit 6c4009
      NULL
Packit 6c4009
    };
Packit 6c4009
  static const char *const names_marker[] = { "46", "4", "6", "" };
Packit 6c4009
Packit 6c4009
  for (int family_idx = 0; families[family_idx] >= 0; ++family_idx)
Packit 6c4009
    {
Packit 6c4009
      params->family = families[family_idx];
Packit 6c4009
      for (int names_idx = 0; names[names_idx] != NULL; ++names_idx)
Packit 6c4009
        {
Packit 6c4009
          params->name = names[names_idx];
Packit 6c4009
          params->marker = names_marker[names_idx];
Packit 6c4009
          run_gbhn_gai (params);
Packit 6c4009
        }
Packit 6c4009
    }
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Run the test for a specific number of addresses/aliases.  */
Packit 6c4009
static void
Packit 6c4009
run_test (int count)
Packit 6c4009
{
Packit 6c4009
  write_hosts (count);
Packit 6c4009
Packit 6c4009
  struct test_params params =
Packit 6c4009
    {
Packit 6c4009
      .count = count,
Packit 6c4009
    };
Packit 6c4009
Packit 6c4009
  support_isolate_in_subprocess (subprocess, &params);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
static int
Packit 6c4009
do_test (void)
Packit 6c4009
{
Packit 6c4009
  support_become_root ();
Packit 6c4009
  if (!support_can_chroot ())
Packit 6c4009
    return EXIT_UNSUPPORTED;
Packit 6c4009
Packit 6c4009
  /* This test should not use gigabytes of memory.   */
Packit 6c4009
  {
Packit 6c4009
    struct rlimit limit;
Packit 6c4009
    if (getrlimit (RLIMIT_AS, &limit) != 0)
Packit 6c4009
      {
Packit 6c4009
        printf ("getrlimit (RLIMIT_AS) failed: %m\n");
Packit 6c4009
        return 1;
Packit 6c4009
      }
Packit 6c4009
    long target = 200 * 1024 * 1024;
Packit 6c4009
    if (limit.rlim_cur == RLIM_INFINITY || limit.rlim_cur > target)
Packit 6c4009
      {
Packit 6c4009
        limit.rlim_cur = target;
Packit 6c4009
        if (setrlimit (RLIMIT_AS, &limit) != 0)
Packit 6c4009
          {
Packit 6c4009
            printf ("setrlimit (RLIMIT_AS) failed: %m\n");
Packit 6c4009
            return 1;
Packit 6c4009
          }
Packit 6c4009
      }
Packit 6c4009
  }
Packit 6c4009
Packit 6c4009
  __nss_configure_lookup ("hosts", "files");
Packit 6c4009
  if (dlopen (LIBNSS_FILES_SO, RTLD_LAZY) == NULL)
Packit 6c4009
    FAIL_EXIT1 ("could not load " LIBNSS_DNS_SO ": %s", dlerror ());
Packit 6c4009
Packit 6c4009
  /* Run the tests with a few different address/alias counts.  */
Packit 6c4009
  for (int count = 1; count <= 111; ++count)
Packit 6c4009
    run_test (count);
Packit 6c4009
  run_test (1111);
Packit 6c4009
  run_test (22222);
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>