/* Enumerate /etc/hosts with a long line (bug 18991). Copyright (C) 2018-2022 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 . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include struct support_chroot *chroot_env; /* Number of alias names in the long line. This is varied to catch different cases where the ERANGE handling can go wrong (line buffer length, alias buffer). */ static int name_count; /* Write /etc/hosts, from outside of the chroot. */ static void write_hosts (void) { FILE *fp = xfopen (chroot_env->path_hosts, "w"); fputs ("127.0.0.1 localhost localhost.localdomain\n", fp); fputs ("192.0.2.2 host2.example.com\n", fp); fputs ("192.0.2.1", fp); for (int i = 0; i < name_count; ++i) fprintf (fp, " host%d.example.com", i); fputs ("\n192.0.2.80 www.example.com\n" "192.0.2.5 host5.example.com\n" "192.0.2.81 www1.example.com\n", fp); xfclose (fp); } const char *host1_expected = "name: localhost\n" "alias: localhost.localdomain\n" "address: 127.0.0.1\n"; const char *host2_expected = "name: host2.example.com\n" "address: 192.0.2.2\n"; const char *host4_expected = "name: www.example.com\n" "address: 192.0.2.80\n"; const char *host5_expected = "name: host5.example.com\n" "address: 192.0.2.5\n"; const char *host6_expected = "name: www1.example.com\n" "address: 192.0.2.81\n"; static void prepare (int argc, char **argv) { chroot_env = support_chroot_create ((struct support_chroot_configuration) { .resolv_conf = "", .hosts = "", /* Filled in by write_hosts. */ .host_conf = "multi on\n", }); } /* If -1, no sethostent call. Otherwise, pass do_stayopen as the sethostent argument. */ static int do_stayopen; /* If non-zero, perform an endostent call. */ static int do_endent; static void subprocess_getent (void *closure) { xchroot (chroot_env->path_chroot); errno = 0; if (do_stayopen >= 0) sethostent (do_stayopen); TEST_VERIFY (errno == 0); int i = 0; while (true) { struct xmemstream expected; xopen_memstream (&expected); switch (++i) { case 1: fputs (host1_expected, expected.out); break; case 2: fputs (host2_expected, expected.out); break; case 3: fputs ("name: host0.example.com\n", expected.out); for (int j = 1; j < name_count; ++j) fprintf (expected.out, "alias: host%d.example.com\n", j); fputs ("address: 192.0.2.1\n", expected.out); break; case 4: fputs (host4_expected, expected.out); break; case 5: fputs (host5_expected, expected.out); break; case 6: fputs (host6_expected, expected.out); break; default: fprintf (expected.out, "*** unexpected host %d ***\n", i); break; } xfclose_memstream (&expected); char *context = xasprintf ("do_stayopen=%d host=%d", do_stayopen, i); errno = 0; struct hostent *e = gethostent (); if (e == NULL) { TEST_VERIFY (errno == 0); break; } check_hostent (context, e, expected.buffer); free (context); free (expected.buffer); } errno = 0; if (do_endent) endhostent (); TEST_VERIFY (errno == 0); /* Exercise process termination. */ exit (0); } /* getaddrinfo test. To be run from a subprocess. */ static void test_gai (int family) { struct addrinfo hints = { .ai_family = family, .ai_protocol = IPPROTO_TCP, .ai_socktype = SOCK_STREAM, }; struct addrinfo *ai; int ret = getaddrinfo ("host2.example.com", "80", &hints, &ai); check_addrinfo ("host2.example.com", ai, ret, "address: STREAM/TCP 192.0.2.2 80\n" "address: STREAM/TCP 192.0.2.1 80\n"); ret = getaddrinfo ("host5.example.com", "80", &hints, &ai); check_addrinfo ("host5.example.com", ai, ret, "address: STREAM/TCP 192.0.2.1 80\n" "address: STREAM/TCP 192.0.2.5 80\n"); ret = getaddrinfo ("www.example.com", "80", &hints, &ai); check_addrinfo ("www.example.com", ai, ret, "address: STREAM/TCP 192.0.2.80 80\n"); ret = getaddrinfo ("www1.example.com", "80", &hints, &ai); check_addrinfo ("www1.example.com", ai, ret, "address: STREAM/TCP 192.0.2.81 80\n"); } /* Subprocess routine for gethostbyname/getaddrinfo testing. */ static void subprocess_gethost (void *closure) { xchroot (chroot_env->path_chroot); /* This tests enlarging the read buffer in the multi case. */ struct xmemstream expected; xopen_memstream (&expected); fputs ("name: host2.example.com\n", expected.out); for (int j = 1; j < name_count; ++j) /* NB: host2 is duplicated in the alias list. */ fprintf (expected.out, "alias: host%d.example.com\n", j); fputs ("alias: host0.example.com\n" "address: 192.0.2.2\n" "address: 192.0.2.1\n", expected.out); xfclose_memstream (&expected); check_hostent ("host2.example.com", gethostbyname ("host2.example.com"), expected.buffer); free (expected.buffer); /* Similarly, but with a different order in the /etc/hosts file. */ xopen_memstream (&expected); fputs ("name: host0.example.com\n", expected.out); for (int j = 1; j < name_count; ++j) fprintf (expected.out, "alias: host%d.example.com\n", j); /* NB: host5 is duplicated in the alias list. */ fputs ("alias: host5.example.com\n" "address: 192.0.2.1\n" "address: 192.0.2.5\n", expected.out); xfclose_memstream (&expected); check_hostent ("host5.example.com", gethostbyname ("host5.example.com"), expected.buffer); free (expected.buffer); check_hostent ("www.example.com", gethostbyname ("www.example.com"), host4_expected); check_hostent ("www1.example.com", gethostbyname ("www1.example.com"), host6_expected); test_gai (AF_INET); test_gai (AF_UNSPEC); } static int do_test (void) { support_become_root (); if (!support_can_chroot ()) return EXIT_UNSUPPORTED; __nss_configure_lookup ("hosts", "files"); if (dlopen (LIBNSS_FILES_SO, RTLD_LAZY) == NULL) FAIL_EXIT1 ("could not load " LIBNSS_DNS_SO ": %s", dlerror ()); /* Each name takes about 20 bytes, so this covers a wide range of buffer sizes, from less than 1000 bytes to about 18000 bytes. */ for (name_count = 40; name_count <= 850; ++name_count) { write_hosts (); for (do_stayopen = -1; do_stayopen < 2; ++do_stayopen) for (do_endent = 0; do_endent < 2; ++do_endent) { if (test_verbose > 0) printf ("info: name_count=%d do_stayopen=%d do_endent=%d\n", name_count, do_stayopen, do_endent); support_isolate_in_subprocess (subprocess_getent, NULL); } support_isolate_in_subprocess (subprocess_gethost, NULL); } support_chroot_free (chroot_env); return 0; } #define PREPARE prepare #include