(root)/
glibc-2.38/
nss/
tst-nss-files-hosts-multi.c
       1  /* Parse /etc/hosts in multi mode with many addresses/aliases.
       2     Copyright (C) 2017-2023 Free Software Foundation, Inc.
       3     This file is part of the GNU C Library.
       4  
       5     The GNU C Library is free software; you can redistribute it and/or
       6     modify it under the terms of the GNU Lesser General Public
       7     License as published by the Free Software Foundation; either
       8     version 2.1 of the License, or (at your option) any later version.
       9  
      10     The GNU C Library is distributed in the hope that it will be useful,
      11     but WITHOUT ANY WARRANTY; without even the implied warranty of
      12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      13     Lesser General Public License for more details.
      14  
      15     You should have received a copy of the GNU Lesser General Public
      16     License along with the GNU C Library; if not, see
      17     <https://www.gnu.org/licenses/>.  */
      18  
      19  #include <dlfcn.h>
      20  #include <errno.h>
      21  #include <gnu/lib-names.h>
      22  #include <netdb.h>
      23  #include <nss.h>
      24  #include <stdbool.h>
      25  #include <stdlib.h>
      26  #include <string.h>
      27  #include <support/check.h>
      28  #include <support/check_nss.h>
      29  #include <support/namespace.h>
      30  #include <support/support.h>
      31  #include <support/test-driver.h>
      32  #include <support/test-driver.h>
      33  #include <support/xmemstream.h>
      34  #include <support/xstdio.h>
      35  #include <support/xunistd.h>
      36  #include <sys/resource.h>
      37  
      38  struct support_chroot *chroot_env;
      39  
      40  static void
      41  prepare (int argc, char **argv)
      42  {
      43    chroot_env = support_chroot_create
      44      ((struct support_chroot_configuration)
      45       {
      46         .resolv_conf = "",
      47         .hosts = "",             /* See write_hosts below.  */
      48         .host_conf = "multi on\n",
      49       });
      50  }
      51  
      52  /* Create the /etc/hosts file from outside the chroot.  */
      53  static void
      54  write_hosts (int count)
      55  {
      56    TEST_VERIFY (count > 0 && count <= 65535);
      57    FILE *fp = xfopen (chroot_env->path_hosts, "w");
      58    fputs ("127.0.0.1   localhost localhost.localdomain\n"
      59           "::1         localhost localhost.localdomain\n",
      60           fp);
      61    for (int i = 0; i < count; ++i)
      62      {
      63        fprintf (fp, "10.4.%d.%d www4.example.com\n",
      64                 (i / 256) & 0xff, i & 0xff);
      65        fprintf (fp, "10.46.%d.%d www.example.com\n",
      66                 (i / 256) & 0xff, i & 0xff);
      67        fprintf (fp, "192.0.2.1 alias.example.com v4-%d.example.com\n", i);
      68        fprintf (fp, "2001:db8::6:%x www6.example.com\n", i);
      69        fprintf (fp, "2001:db8::46:%x www.example.com\n", i);
      70        fprintf (fp, "2001:db8::1 alias.example.com v6-%d.example.com\n", i);
      71      }
      72    xfclose (fp);
      73  }
      74  
      75  /* Parameters of a single test.  */
      76  struct test_params
      77  {
      78    const char *name;             /* Name to query.  */
      79    const char *marker;           /* Address marker for the name.  */
      80    int count;                    /* Number of addresses/aliases.  */
      81    int family;                   /* AF_INET, AF_INET_6 or AF_UNSPEC.  */
      82    bool canonname;               /* True if AI_CANONNAME should be enabled.  */
      83  };
      84  
      85  /* Expected result of gethostbyname/gethostbyname2.  */
      86  static char *
      87  expected_ghbn (const struct test_params *params)
      88  {
      89    TEST_VERIFY (params->family == AF_INET || params->family == AF_INET6);
      90  
      91    struct xmemstream expected;
      92    xopen_memstream (&expected);
      93    if (strcmp (params->name, "alias.example.com") == 0)
      94      {
      95        fprintf (expected.out, "name: %s\n", params->name);
      96        char af;
      97        if (params->family == AF_INET)
      98          af = '4';
      99        else
     100          af = '6';
     101        for (int i = 0; i < params->count; ++i)
     102          fprintf (expected.out, "alias: v%c-%d.example.com\n", af, i);
     103  
     104        for (int i = 0; i < params->count; ++i)
     105          if (params->family == AF_INET)
     106            fputs ("address: 192.0.2.1\n", expected.out);
     107          else
     108            fputs ("address: 2001:db8::1\n", expected.out);
     109      }
     110    else /* www/www4/www6 name.  */
     111      {
     112        bool do_ipv4 = params->family == AF_INET
     113          && strncmp (params->name, "www6", 4) != 0;
     114        bool do_ipv6 = params->family == AF_INET6
     115          && strncmp (params->name, "www4", 4) != 0;
     116        if (do_ipv4 || do_ipv6)
     117          {
     118            fprintf (expected.out, "name: %s\n", params->name);
     119            if (do_ipv4)
     120              for (int i = 0; i < params->count; ++i)
     121                fprintf (expected.out, "address: 10.%s.%d.%d\n",
     122                         params->marker, i / 256, i % 256);
     123            if (do_ipv6)
     124              for (int i = 0; i < params->count; ++i)
     125                fprintf (expected.out, "address: 2001:db8::%s:%x\n",
     126                         params->marker, i);
     127          }
     128        else
     129          fputs ("error: HOST_NOT_FOUND\n", expected.out);
     130      }
     131    xfclose_memstream (&expected);
     132    return expected.buffer;
     133  }
     134  
     135  /* Expected result of getaddrinfo.  */
     136  static char *
     137  expected_gai (const struct test_params *params)
     138  {
     139    bool do_ipv4 = false;
     140    bool do_ipv6 = false;
     141    if (params->family == AF_UNSPEC)
     142      do_ipv4 = do_ipv6 = true;
     143    else if (params->family == AF_INET)
     144      do_ipv4 = true;
     145    else if (params->family == AF_INET6)
     146      do_ipv6 = true;
     147  
     148    struct xmemstream expected;
     149    xopen_memstream (&expected);
     150    if (strcmp (params->name, "alias.example.com") == 0)
     151      {
     152        if (params->canonname)
     153          fprintf (expected.out,
     154                   "flags: AI_CANONNAME\n"
     155                   "canonname: %s\n",
     156                   params->name);
     157  
     158        if (do_ipv4)
     159          for (int i = 0; i < params->count; ++i)
     160            fputs ("address: STREAM/TCP 192.0.2.1 80\n", expected.out);
     161        if (do_ipv6)
     162          for (int i = 0; i < params->count; ++i)
     163            fputs ("address: STREAM/TCP 2001:db8::1 80\n", expected.out);
     164      }
     165    else /* www/www4/www6 name.  */
     166      {
     167        if (strncmp (params->name, "www4", 4) == 0)
     168          do_ipv6 = false;
     169        else if (strncmp (params->name, "www6", 4) == 0)
     170          do_ipv4 = false;
     171        /* Otherwise, we have www as the name, so we do both.  */
     172  
     173        if (do_ipv4 || do_ipv6)
     174          {
     175            if (params->canonname)
     176              fprintf (expected.out,
     177                       "flags: AI_CANONNAME\n"
     178                       "canonname: %s\n",
     179                       params->name);
     180  
     181            if (do_ipv4)
     182              for (int i = 0; i < params->count; ++i)
     183                fprintf (expected.out, "address: STREAM/TCP 10.%s.%d.%d 80\n",
     184                         params->marker, i / 256, i % 256);
     185            if (do_ipv6)
     186              for (int i = 0; i < params->count; ++i)
     187                fprintf (expected.out,
     188                         "address: STREAM/TCP 2001:db8::%s:%x 80\n",
     189                         params->marker, i);
     190          }
     191        else
     192          fputs ("error: Name or service not known\n", expected.out);
     193      }
     194    xfclose_memstream (&expected);
     195    return expected.buffer;
     196  }
     197  
     198  static void
     199  run_gbhn_gai (struct test_params *params)
     200  {
     201    char *ctx = xasprintf ("name=%s marker=%s count=%d family=%d",
     202                           params->name, params->marker, params->count,
     203                           params->family);
     204    if (test_verbose > 0)
     205      printf ("info: %s\n", ctx);
     206  
     207    /* Check gethostbyname, gethostbyname2.  */
     208    if (params->family == AF_INET)
     209      {
     210        char *expected = expected_ghbn (params);
     211        check_hostent (ctx, gethostbyname (params->name), expected);
     212        free (expected);
     213      }
     214    if (params->family != AF_UNSPEC)
     215      {
     216        char *expected = expected_ghbn (params);
     217        check_hostent (ctx, gethostbyname2 (params->name, params->family),
     218                       expected);
     219        free (expected);
     220      }
     221  
     222    /* Check getaddrinfo.  */
     223    for (int do_canonical = 0; do_canonical < 2; ++do_canonical)
     224      {
     225        params->canonname = do_canonical;
     226        char *expected = expected_gai (params);
     227        struct addrinfo hints =
     228          {
     229            .ai_family = params->family,
     230            .ai_socktype = SOCK_STREAM,
     231            .ai_protocol = IPPROTO_TCP,
     232          };
     233        if (do_canonical)
     234          hints.ai_flags |= AI_CANONNAME;
     235        struct addrinfo *ai;
     236        int ret = getaddrinfo (params->name, "80", &hints, &ai);
     237        check_addrinfo (ctx, ai, ret, expected);
     238        if (ret == 0)
     239          freeaddrinfo (ai);
     240        free (expected);
     241      }
     242  
     243    free (ctx);
     244  }
     245  
     246  /* Callback for the subprocess which runs the test in a chroot.  */
     247  static void
     248  subprocess (void *closure)
     249  {
     250    struct test_params *params = closure;
     251  
     252    xchroot (chroot_env->path_chroot);
     253  
     254    static const int families[] = { AF_INET, AF_INET6, AF_UNSPEC, -1 };
     255    static const char *const names[] =
     256      {
     257        "www.example.com", "www4.example.com", "www6.example.com",
     258        "alias.example.com",
     259        NULL
     260      };
     261    static const char *const names_marker[] = { "46", "4", "6", "" };
     262  
     263    for (int family_idx = 0; families[family_idx] >= 0; ++family_idx)
     264      {
     265        params->family = families[family_idx];
     266        for (int names_idx = 0; names[names_idx] != NULL; ++names_idx)
     267          {
     268            params->name = names[names_idx];
     269            params->marker = names_marker[names_idx];
     270            run_gbhn_gai (params);
     271          }
     272      }
     273  }
     274  
     275  /* Run the test for a specific number of addresses/aliases.  */
     276  static void
     277  run_test (int count)
     278  {
     279    write_hosts (count);
     280  
     281    struct test_params params =
     282      {
     283        .count = count,
     284      };
     285  
     286    support_isolate_in_subprocess (subprocess, &params);
     287  }
     288  
     289  static int
     290  do_test (void)
     291  {
     292    support_become_root ();
     293    if (!support_can_chroot ())
     294      return EXIT_UNSUPPORTED;
     295  
     296    /* This test should not use gigabytes of memory.   */
     297    {
     298      struct rlimit limit;
     299      if (getrlimit (RLIMIT_AS, &limit) != 0)
     300        {
     301          printf ("getrlimit (RLIMIT_AS) failed: %m\n");
     302          return 1;
     303        }
     304      long target = 200 * 1024 * 1024;
     305      if (limit.rlim_cur == RLIM_INFINITY || limit.rlim_cur > target)
     306        {
     307          limit.rlim_cur = target;
     308          if (setrlimit (RLIMIT_AS, &limit) != 0)
     309            {
     310              printf ("setrlimit (RLIMIT_AS) failed: %m\n");
     311              return 1;
     312            }
     313        }
     314    }
     315  
     316    __nss_configure_lookup ("hosts", "files");
     317    if (dlopen (LIBNSS_FILES_SO, RTLD_LAZY) == NULL)
     318      FAIL_EXIT1 ("could not load " LIBNSS_DNS_SO ": %s", dlerror ());
     319  
     320    /* Run the tests with a few different address/alias counts.  */
     321    for (int count = 1; count <= 111; ++count)
     322      run_test (count);
     323    run_test (1111);
     324    run_test (22222);
     325  
     326    support_chroot_free (chroot_env);
     327    return 0;
     328  }
     329  
     330  #define TIMEOUT 40
     331  #define PREPARE prepare
     332  #include <support/test-driver.c>