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