1 /* Enumerate /etc/hosts with a long line (bug 18991).
2    Copyright (C) 2018-2022 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
write_hosts(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
prepare(int argc,char ** argv)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
subprocess_getent(void * closure)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
test_gai(int family)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
subprocess_gethost(void * closure)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
do_test(void)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>
277