1 /* Parse /etc/hosts in multi mode with many addresses/aliases.
2 Copyright (C) 2017-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 #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
prepare(int argc,char ** argv)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
write_hosts(int count)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 *
expected_ghbn(const struct test_params * params)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 *
expected_gai(const struct test_params * params)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
run_gbhn_gai(struct test_params * params)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
subprocess(void * closure)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
run_test(int count)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, ¶ms);
287 }
288
289 static int
do_test(void)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>
333