]>
Commit | Line | Data |
---|---|---|
d8425e11 FW |
1 | /* Parse /etc/hosts in multi mode with many addresses/aliases. |
2 | Copyright (C) 2017 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 | <http://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 | |
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 | |
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 * | |
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 * | |
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 | |
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 | |
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 | |
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 | |
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 PREPARE prepare | |
331 | #include <support/test-driver.c> |