(root)/
glibc-2.38/
nss/
tst-nss-files-hosts-getent.c
       1  /* Enumerate /etc/hosts with a long line (bug 18991).
       2     Copyright (C) 2018-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  
      20  #include <dlfcn.h>
      21  #include <errno.h>
      22  #include <gnu/lib-names.h>
      23  #include <netdb.h>
      24  #include <nss.h>
      25  #include <stdlib.h>
      26  #include <stdio.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/xmemstream.h>
      33  #include <support/xstdio.h>
      34  #include <support/xunistd.h>
      35  
      36  struct support_chroot *chroot_env;
      37  
      38  /* Number of alias names in the long line.  This is varied to catch
      39     different cases where the ERANGE handling can go wrong (line buffer
      40     length, alias buffer).  */
      41  static int name_count;
      42  
      43  /* Write /etc/hosts, from outside of the chroot.  */
      44  static void
      45  write_hosts (void)
      46  {
      47    FILE *fp = xfopen (chroot_env->path_hosts, "w");
      48    fputs ("127.0.0.1   localhost localhost.localdomain\n", fp);
      49    fputs ("192.0.2.2 host2.example.com\n", fp);
      50    fputs ("192.0.2.1", fp);
      51    for (int i = 0; i < name_count; ++i)
      52      fprintf (fp, " host%d.example.com", i);
      53    fputs ("\n192.0.2.80 www.example.com\n"
      54           "192.0.2.5 host5.example.com\n"
      55           "192.0.2.81 www1.example.com\n", fp);
      56    xfclose (fp);
      57  }
      58  
      59  const char *host1_expected =
      60    "name: localhost\n"
      61    "alias: localhost.localdomain\n"
      62    "address: 127.0.0.1\n";
      63  const char *host2_expected =
      64    "name: host2.example.com\n"
      65    "address: 192.0.2.2\n";
      66  const char *host4_expected =
      67    "name: www.example.com\n"
      68    "address: 192.0.2.80\n";
      69  const char *host5_expected =
      70    "name: host5.example.com\n"
      71    "address: 192.0.2.5\n";
      72  const char *host6_expected =
      73    "name: www1.example.com\n"
      74    "address: 192.0.2.81\n";
      75  
      76  static void
      77  prepare (int argc, char **argv)
      78  {
      79    chroot_env = support_chroot_create
      80      ((struct support_chroot_configuration)
      81       {
      82         .resolv_conf = "",
      83         .hosts = "",             /* Filled in by write_hosts.  */
      84         .host_conf = "multi on\n",
      85       });
      86  }
      87  
      88  /* If -1, no sethostent call.  Otherwise, pass do_stayopen as the
      89     sethostent argument.  */
      90  static int do_stayopen;
      91  
      92  /* If non-zero, perform an endostent call.  */
      93  static int do_endent;
      94  
      95  static void
      96  subprocess_getent (void *closure)
      97  {
      98    xchroot (chroot_env->path_chroot);
      99  
     100    errno = 0;
     101    if (do_stayopen >= 0)
     102      sethostent (do_stayopen);
     103    TEST_VERIFY (errno == 0);
     104  
     105    int i = 0;
     106    while (true)
     107      {
     108        struct xmemstream expected;
     109        xopen_memstream (&expected);
     110        switch (++i)
     111          {
     112          case 1:
     113            fputs (host1_expected, expected.out);
     114            break;
     115          case 2:
     116            fputs (host2_expected, expected.out);
     117            break;
     118          case 3:
     119            fputs ("name: host0.example.com\n", expected.out);
     120            for (int j = 1; j < name_count; ++j)
     121              fprintf (expected.out, "alias: host%d.example.com\n", j);
     122            fputs ("address: 192.0.2.1\n", expected.out);
     123            break;
     124          case 4:
     125            fputs (host4_expected, expected.out);
     126            break;
     127          case 5:
     128            fputs (host5_expected, expected.out);
     129            break;
     130          case 6:
     131            fputs (host6_expected, expected.out);
     132            break;
     133          default:
     134            fprintf (expected.out, "*** unexpected host %d ***\n", i);
     135            break;
     136          }
     137        xfclose_memstream (&expected);
     138        char *context = xasprintf ("do_stayopen=%d host=%d", do_stayopen, i);
     139  
     140        errno = 0;
     141        struct hostent *e = gethostent ();
     142        if (e == NULL)
     143          {
     144            TEST_VERIFY (errno == 0);
     145            break;
     146          }
     147        check_hostent (context, e, expected.buffer);
     148        free (context);
     149        free (expected.buffer);
     150      }
     151  
     152    errno = 0;
     153    if (do_endent)
     154      endhostent ();
     155    TEST_VERIFY (errno == 0);
     156  
     157    /* Exercise process termination.   */
     158    exit (0);
     159  }
     160  
     161  /* getaddrinfo test.  To be run from a subprocess.  */
     162  static void
     163  test_gai (int family)
     164  {
     165    struct addrinfo hints =
     166      {
     167        .ai_family = family,
     168        .ai_protocol = IPPROTO_TCP,
     169        .ai_socktype = SOCK_STREAM,
     170      };
     171  
     172    struct addrinfo *ai;
     173    int ret = getaddrinfo ("host2.example.com", "80", &hints, &ai);
     174    check_addrinfo ("host2.example.com", ai, ret,
     175                    "address: STREAM/TCP 192.0.2.2 80\n"
     176                    "address: STREAM/TCP 192.0.2.1 80\n");
     177  
     178    ret = getaddrinfo ("host5.example.com", "80", &hints, &ai);
     179    check_addrinfo ("host5.example.com", ai, ret,
     180                    "address: STREAM/TCP 192.0.2.1 80\n"
     181                    "address: STREAM/TCP 192.0.2.5 80\n");
     182  
     183    ret = getaddrinfo ("www.example.com", "80", &hints, &ai);
     184    check_addrinfo ("www.example.com", ai, ret,
     185                    "address: STREAM/TCP 192.0.2.80 80\n");
     186  
     187    ret = getaddrinfo ("www1.example.com", "80", &hints, &ai);
     188    check_addrinfo ("www1.example.com", ai, ret,
     189                    "address: STREAM/TCP 192.0.2.81 80\n");
     190  }
     191  
     192  /* Subprocess routine for gethostbyname/getaddrinfo testing.  */
     193  static void
     194  subprocess_gethost (void *closure)
     195  {
     196    xchroot (chroot_env->path_chroot);
     197  
     198    /* This tests enlarging the read buffer in the multi case.  */
     199    struct xmemstream expected;
     200    xopen_memstream (&expected);
     201    fputs ("name: host2.example.com\n", expected.out);
     202    for (int j = 1; j < name_count; ++j)
     203      /* NB: host2 is duplicated in the alias list.  */
     204      fprintf (expected.out, "alias: host%d.example.com\n", j);
     205    fputs ("alias: host0.example.com\n"
     206           "address: 192.0.2.2\n"
     207           "address: 192.0.2.1\n",
     208           expected.out);
     209    xfclose_memstream (&expected);
     210    check_hostent ("host2.example.com",
     211                   gethostbyname ("host2.example.com"),
     212                   expected.buffer);
     213    free (expected.buffer);
     214  
     215    /* Similarly, but with a different order in the /etc/hosts file.  */
     216    xopen_memstream (&expected);
     217    fputs ("name: host0.example.com\n", expected.out);
     218    for (int j = 1; j < name_count; ++j)
     219      fprintf (expected.out, "alias: host%d.example.com\n", j);
     220    /* NB: host5 is duplicated in the alias list.  */
     221    fputs ("alias: host5.example.com\n"
     222           "address: 192.0.2.1\n"
     223           "address: 192.0.2.5\n",
     224           expected.out);
     225    xfclose_memstream (&expected);
     226    check_hostent ("host5.example.com",
     227                   gethostbyname ("host5.example.com"),
     228                   expected.buffer);
     229    free (expected.buffer);
     230  
     231    check_hostent ("www.example.com",
     232                   gethostbyname ("www.example.com"),
     233                   host4_expected);
     234    check_hostent ("www1.example.com",
     235                   gethostbyname ("www1.example.com"),
     236                   host6_expected);
     237  
     238    test_gai (AF_INET);
     239    test_gai (AF_UNSPEC);
     240  }
     241  
     242  static int
     243  do_test (void)
     244  {
     245    support_become_root ();
     246    if (!support_can_chroot ())
     247      return EXIT_UNSUPPORTED;
     248  
     249    __nss_configure_lookup ("hosts", "files");
     250    if (dlopen (LIBNSS_FILES_SO, RTLD_LAZY) == NULL)
     251      FAIL_EXIT1 ("could not load " LIBNSS_DNS_SO ": %s", dlerror ());
     252  
     253    /* Each name takes about 20 bytes, so this covers a wide range of
     254       buffer sizes, from less than 1000 bytes to about 18000 bytes.  */
     255    for (name_count = 40; name_count <= 850; ++name_count)
     256      {
     257        write_hosts ();
     258  
     259        for (do_stayopen = -1; do_stayopen < 2; ++do_stayopen)
     260          for (do_endent = 0; do_endent < 2; ++do_endent)
     261            {
     262              if (test_verbose > 0)
     263                printf ("info: name_count=%d do_stayopen=%d do_endent=%d\n",
     264                        name_count, do_stayopen, do_endent);
     265              support_isolate_in_subprocess (subprocess_getent, NULL);
     266            }
     267  
     268        support_isolate_in_subprocess (subprocess_gethost, NULL);
     269      }
     270  
     271    support_chroot_free (chroot_env);
     272    return 0;
     273  }
     274  
     275  #define PREPARE prepare
     276  #include <support/test-driver.c>