]>
Commit | Line | Data |
---|---|---|
2714c5f3 | 1 | /* Test parsing of /etc/resolv.conf. Genric version. |
d614a753 | 2 | Copyright (C) 2017-2020 Free Software Foundation, Inc. |
2714c5f3 FW |
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 | |
5a82c748 | 17 | <https://www.gnu.org/licenses/>. */ |
2714c5f3 FW |
18 | |
19 | /* Before including this file, TEST_THREAD has to be defined to 0 or | |
20 | 1, depending on whether the threading tests should be compiled | |
21 | in. */ | |
22 | ||
23 | #include <arpa/inet.h> | |
39bd76df | 24 | #include <errno.h> |
2714c5f3 FW |
25 | #include <gnu/lib-names.h> |
26 | #include <netdb.h> | |
3f853f22 | 27 | #include <resolv/resolv_context.h> |
2714c5f3 FW |
28 | #include <stdio.h> |
29 | #include <stdlib.h> | |
30 | #include <support/capture_subprocess.h> | |
31 | #include <support/check.h> | |
32 | #include <support/namespace.h> | |
33 | #include <support/run_diff.h> | |
34 | #include <support/support.h> | |
35 | #include <support/temp_file.h> | |
36 | #include <support/test-driver.h> | |
39bd76df | 37 | #include <support/xsocket.h> |
2714c5f3 FW |
38 | #include <support/xstdio.h> |
39 | #include <support/xunistd.h> | |
40 | ||
41 | #if TEST_THREAD | |
42 | # include <support/xthread.h> | |
43 | #endif | |
44 | ||
45 | /* This is the host name used to ensure predictable behavior of | |
46 | res_init. */ | |
47 | static const char *const test_hostname = "www.example.com"; | |
48 | ||
d4165eed | 49 | struct support_chroot *chroot_env; |
2714c5f3 FW |
50 | |
51 | static void | |
52 | prepare (int argc, char **argv) | |
53 | { | |
d4165eed FW |
54 | chroot_env = support_chroot_create |
55 | ((struct support_chroot_configuration) | |
56 | { | |
57 | .resolv_conf = "", | |
58 | }); | |
2714c5f3 FW |
59 | } |
60 | ||
61 | /* Verify that the chroot environment has been set up. */ | |
62 | static void | |
63 | check_chroot_working (void *closure) | |
64 | { | |
d4165eed | 65 | xchroot (chroot_env->path_chroot); |
2714c5f3 FW |
66 | FILE *fp = xfopen (_PATH_RESCONF, "r"); |
67 | xfclose (fp); | |
68 | ||
69 | TEST_VERIFY_EXIT (res_init () == 0); | |
70 | TEST_VERIFY (_res.options & RES_INIT); | |
71 | ||
72 | char buf[100]; | |
73 | if (gethostname (buf, sizeof (buf)) < 0) | |
74 | FAIL_EXIT1 ("gethostname: %m"); | |
75 | if (strcmp (buf, test_hostname) != 0) | |
76 | FAIL_EXIT1 ("unexpected host name: %s", buf); | |
77 | } | |
78 | ||
79 | /* If FLAG is set in *OPTIONS, write NAME to FP, and clear it in | |
80 | *OPTIONS. */ | |
81 | static void | |
82 | print_option_flag (FILE *fp, int *options, int flag, const char *name) | |
83 | { | |
84 | if (*options & flag) | |
85 | { | |
86 | fprintf (fp, " %s", name); | |
87 | *options &= ~flag; | |
88 | } | |
89 | } | |
90 | ||
91 | /* Write a decoded version of the resolver configuration *RESP to the | |
92 | stream FP. */ | |
93 | static void | |
94 | print_resp (FILE *fp, res_state resp) | |
95 | { | |
3f853f22 FW |
96 | struct resolv_context *ctx = __resolv_context_get_override (resp); |
97 | TEST_VERIFY_EXIT (ctx != NULL); | |
98 | if (ctx->conf == NULL) | |
99 | fprintf (fp, "; extended resolver state missing\n"); | |
100 | ||
2714c5f3 FW |
101 | /* The options directive. */ |
102 | { | |
103 | /* RES_INIT is used internally for tracking initialization. */ | |
104 | TEST_VERIFY (resp->options & RES_INIT); | |
105 | /* Also mask out other default flags which cannot be set through | |
106 | the options directive. */ | |
107 | int options | |
108 | = resp->options & ~(RES_INIT | RES_RECURSE | RES_DEFNAMES | RES_DNSRCH); | |
109 | if (options != 0 | |
110 | || resp->ndots != 1 | |
111 | || resp->retrans != RES_TIMEOUT | |
112 | || resp->retry != RES_DFLRETRY) | |
113 | { | |
114 | fputs ("options", fp); | |
115 | if (resp->ndots != 1) | |
116 | fprintf (fp, " ndots:%d", resp->ndots); | |
117 | if (resp->retrans != RES_TIMEOUT) | |
118 | fprintf (fp, " timeout:%d", resp->retrans); | |
119 | if (resp->retry != RES_DFLRETRY) | |
120 | fprintf (fp, " attempts:%d", resp->retry); | |
121 | print_option_flag (fp, &options, RES_USEVC, "use-vc"); | |
2714c5f3 FW |
122 | print_option_flag (fp, &options, RES_ROTATE, "rotate"); |
123 | print_option_flag (fp, &options, RES_USE_EDNS0, "edns0"); | |
124 | print_option_flag (fp, &options, RES_SNGLKUP, | |
125 | "single-request"); | |
126 | print_option_flag (fp, &options, RES_SNGLKUPREOP, | |
127 | "single-request-reopen"); | |
128 | print_option_flag (fp, &options, RES_NOTLDQUERY, "no-tld-query"); | |
aef16cc8 | 129 | print_option_flag (fp, &options, RES_NORELOAD, "no-reload"); |
446997ff | 130 | print_option_flag (fp, &options, RES_TRUSTAD, "trust-ad"); |
2714c5f3 FW |
131 | fputc ('\n', fp); |
132 | if (options != 0) | |
133 | fprintf (fp, "; error: unresolved option bits: 0x%x\n", options); | |
134 | } | |
135 | } | |
136 | ||
137 | /* The search and domain directives. */ | |
138 | if (resp->dnsrch[0] != NULL) | |
139 | { | |
140 | fputs ("search", fp); | |
141 | for (int i = 0; i < MAXDNSRCH && resp->dnsrch[i] != NULL; ++i) | |
142 | { | |
143 | fputc (' ', fp); | |
144 | fputs (resp->dnsrch[i], fp); | |
145 | } | |
146 | fputc ('\n', fp); | |
147 | } | |
148 | else if (resp->defdname[0] != '\0') | |
149 | fprintf (fp, "domain %s\n", resp->defdname); | |
150 | ||
3f853f22 FW |
151 | /* The extended search path. */ |
152 | { | |
153 | size_t i = 0; | |
154 | while (true) | |
155 | { | |
156 | const char *name = __resolv_context_search_list (ctx, i); | |
157 | if (name == NULL) | |
158 | break; | |
159 | fprintf (fp, "; search[%zu]: %s\n", i, name); | |
160 | ++i; | |
161 | } | |
162 | } | |
163 | ||
2714c5f3 FW |
164 | /* The sortlist directive. */ |
165 | if (resp->nsort > 0) | |
166 | { | |
167 | fputs ("sortlist", fp); | |
168 | for (int i = 0; i < resp->nsort && i < MAXRESOLVSORT; ++i) | |
169 | { | |
170 | char net[20]; | |
171 | if (inet_ntop (AF_INET, &resp->sort_list[i].addr, | |
172 | net, sizeof (net)) == NULL) | |
173 | FAIL_EXIT1 ("inet_ntop: %m\n"); | |
174 | char mask[20]; | |
175 | if (inet_ntop (AF_INET, &resp->sort_list[i].mask, | |
176 | mask, sizeof (mask)) == NULL) | |
177 | FAIL_EXIT1 ("inet_ntop: %m\n"); | |
178 | fprintf (fp, " %s/%s", net, mask); | |
179 | } | |
180 | fputc ('\n', fp); | |
181 | } | |
182 | ||
183 | /* The nameserver directives. */ | |
184 | for (size_t i = 0; i < resp->nscount; ++i) | |
185 | { | |
186 | char host[NI_MAXHOST]; | |
187 | char service[NI_MAXSERV]; | |
188 | ||
189 | /* See get_nsaddr in res_send.c. */ | |
190 | void *addr; | |
191 | size_t addrlen; | |
192 | if (resp->nsaddr_list[i].sin_family == 0 | |
193 | && resp->_u._ext.nsaddrs[i] != NULL) | |
194 | { | |
195 | addr = resp->_u._ext.nsaddrs[i]; | |
196 | addrlen = sizeof (*resp->_u._ext.nsaddrs[i]); | |
197 | } | |
198 | else | |
199 | { | |
200 | addr = &resp->nsaddr_list[i]; | |
201 | addrlen = sizeof (resp->nsaddr_list[i]); | |
202 | } | |
203 | ||
204 | int ret = getnameinfo (addr, addrlen, | |
205 | host, sizeof (host), service, sizeof (service), | |
206 | NI_NUMERICHOST | NI_NUMERICSERV); | |
207 | if (ret != 0) | |
208 | { | |
209 | if (ret == EAI_SYSTEM) | |
210 | fprintf (fp, "; error: getnameinfo: %m\n"); | |
211 | else | |
212 | fprintf (fp, "; error: getnameinfo: %s\n", gai_strerror (ret)); | |
213 | } | |
214 | else | |
215 | { | |
216 | fprintf (fp, "nameserver %s\n", host); | |
217 | if (strcmp (service, "53") != 0) | |
218 | fprintf (fp, "; unrepresentable port number %s\n\n", service); | |
219 | } | |
220 | } | |
221 | ||
a1c4eb87 FW |
222 | /* The extended name server list. */ |
223 | { | |
224 | size_t i = 0; | |
225 | while (true) | |
226 | { | |
227 | const struct sockaddr *addr = __resolv_context_nameserver (ctx, i); | |
228 | if (addr == NULL) | |
229 | break; | |
230 | size_t addrlen; | |
231 | switch (addr->sa_family) | |
232 | { | |
233 | case AF_INET: | |
234 | addrlen = sizeof (struct sockaddr_in); | |
235 | break; | |
236 | case AF_INET6: | |
237 | addrlen = sizeof (struct sockaddr_in6); | |
238 | break; | |
239 | default: | |
240 | FAIL_EXIT1 ("invalid address family %d", addr->sa_family); | |
241 | } | |
242 | ||
243 | char host[NI_MAXHOST]; | |
244 | char service[NI_MAXSERV]; | |
245 | int ret = getnameinfo (addr, addrlen, | |
246 | host, sizeof (host), service, sizeof (service), | |
247 | NI_NUMERICHOST | NI_NUMERICSERV); | |
248 | ||
249 | if (ret != 0) | |
250 | { | |
251 | if (ret == EAI_SYSTEM) | |
252 | fprintf (fp, "; error: getnameinfo: %m\n"); | |
253 | else | |
254 | fprintf (fp, "; error: getnameinfo: %s\n", gai_strerror (ret)); | |
255 | } | |
256 | else | |
257 | fprintf (fp, "; nameserver[%zu]: [%s]:%s\n", i, host, service); | |
258 | ++i; | |
259 | } | |
260 | } | |
261 | ||
2714c5f3 | 262 | TEST_VERIFY (!ferror (fp)); |
3f853f22 FW |
263 | |
264 | __resolv_context_put (ctx); | |
2714c5f3 FW |
265 | } |
266 | ||
267 | /* Parameters of one test case. */ | |
268 | struct test_case | |
269 | { | |
270 | /* A short, descriptive name of the test. */ | |
271 | const char *name; | |
272 | ||
273 | /* The contents of the /etc/resolv.conf file. */ | |
274 | const char *conf; | |
275 | ||
276 | /* The expected output from print_resp. */ | |
277 | const char *expected; | |
278 | ||
279 | /* Setting for the LOCALDOMAIN environment variable. NULL if the | |
280 | variable is not to be set. */ | |
281 | const char *localdomain; | |
282 | ||
283 | /* Setting for the RES_OPTIONS environment variable. NULL if the | |
284 | variable is not to be set. */ | |
285 | const char *res_options; | |
4446a885 FW |
286 | |
287 | /* Override the system host name. NULL means that no change is made | |
288 | and the default is used (test_hostname). */ | |
289 | const char *hostname; | |
2714c5f3 FW |
290 | }; |
291 | ||
292 | enum test_init | |
293 | { | |
294 | test_init, | |
295 | test_ninit, | |
296 | test_mkquery, | |
297 | test_gethostbyname, | |
298 | test_getaddrinfo, | |
299 | test_init_method_last = test_getaddrinfo | |
300 | }; | |
301 | ||
a9270e67 FW |
302 | static const char *const test_init_names[] = |
303 | { | |
304 | [test_init] = "res_init", | |
4e45d83c | 305 | [test_ninit] = "res_ninit", |
a9270e67 FW |
306 | [test_mkquery] = "res_mkquery", |
307 | [test_gethostbyname] = "gethostbyname", | |
308 | [test_getaddrinfo] = "getaddrinfo", | |
309 | }; | |
310 | ||
2714c5f3 FW |
311 | /* Closure argument for run_res_init. */ |
312 | struct test_context | |
313 | { | |
314 | enum test_init init; | |
315 | const struct test_case *t; | |
316 | }; | |
317 | ||
318 | static void | |
319 | setup_nss_dns_and_chroot (void) | |
320 | { | |
321 | /* Load nss_dns outside of the chroot. */ | |
322 | if (dlopen (LIBNSS_DNS_SO, RTLD_LAZY) == NULL) | |
323 | FAIL_EXIT1 ("could not load " LIBNSS_DNS_SO ": %s", dlerror ()); | |
d4165eed | 324 | xchroot (chroot_env->path_chroot); |
2714c5f3 FW |
325 | /* Force the use of nss_dns. */ |
326 | __nss_configure_lookup ("hosts", "dns"); | |
327 | } | |
328 | ||
329 | /* Run res_ninit or res_init in a subprocess and dump the parsed | |
330 | resolver state to standard output. */ | |
331 | static void | |
332 | run_res_init (void *closure) | |
333 | { | |
334 | struct test_context *ctx = closure; | |
335 | TEST_VERIFY (getenv ("LOCALDOMAIN") == NULL); | |
336 | TEST_VERIFY (getenv ("RES_OPTIONS") == NULL); | |
337 | if (ctx->t->localdomain != NULL) | |
338 | setenv ("LOCALDOMAIN", ctx->t->localdomain, 1); | |
339 | if (ctx->t->res_options != NULL) | |
340 | setenv ("RES_OPTIONS", ctx->t->res_options, 1); | |
4446a885 FW |
341 | if (ctx->t->hostname != NULL) |
342 | { | |
6547ec0a | 343 | #ifdef CLONE_NEWUTS |
4446a885 FW |
344 | /* This test needs its own namespace, to avoid changing the host |
345 | name for the parent, too. */ | |
346 | TEST_VERIFY_EXIT (unshare (CLONE_NEWUTS) == 0); | |
347 | if (sethostname (ctx->t->hostname, strlen (ctx->t->hostname)) != 0) | |
348 | FAIL_EXIT1 ("sethostname (\"%s\"): %m", ctx->t->hostname); | |
6547ec0a ST |
349 | #else |
350 | FAIL_UNSUPPORTED ("clone (CLONE_NEWUTS) not supported"); | |
351 | #endif | |
4446a885 | 352 | } |
2714c5f3 FW |
353 | |
354 | switch (ctx->init) | |
355 | { | |
356 | case test_init: | |
d4165eed | 357 | xchroot (chroot_env->path_chroot); |
2714c5f3 FW |
358 | TEST_VERIFY (res_init () == 0); |
359 | print_resp (stdout, &_res); | |
360 | return; | |
361 | ||
362 | case test_ninit: | |
d4165eed | 363 | xchroot (chroot_env->path_chroot); |
2714c5f3 FW |
364 | res_state resp = xmalloc (sizeof (*resp)); |
365 | memset (resp, 0, sizeof (*resp)); | |
366 | TEST_VERIFY (res_ninit (resp) == 0); | |
367 | print_resp (stdout, resp); | |
368 | res_nclose (resp); | |
369 | free (resp); | |
370 | return; | |
371 | ||
372 | case test_mkquery: | |
d4165eed | 373 | xchroot (chroot_env->path_chroot); |
2714c5f3 FW |
374 | unsigned char buf[512]; |
375 | TEST_VERIFY (res_mkquery (QUERY, "www.example", | |
376 | C_IN, ns_t_a, NULL, 0, | |
377 | NULL, buf, sizeof (buf)) > 0); | |
378 | print_resp (stdout, &_res); | |
379 | return; | |
380 | ||
381 | case test_gethostbyname: | |
382 | setup_nss_dns_and_chroot (); | |
383 | /* Trigger implicit initialization of the _res structure. The | |
384 | actual lookup result is immaterial. */ | |
385 | (void )gethostbyname ("www.example"); | |
386 | print_resp (stdout, &_res); | |
387 | return; | |
388 | ||
389 | case test_getaddrinfo: | |
390 | setup_nss_dns_and_chroot (); | |
391 | /* Trigger implicit initialization of the _res structure. The | |
392 | actual lookup result is immaterial. */ | |
393 | struct addrinfo *ai; | |
394 | (void) getaddrinfo ("www.example", NULL, NULL, &ai); | |
395 | print_resp (stdout, &_res); | |
396 | return; | |
397 | } | |
398 | ||
399 | FAIL_EXIT1 ("invalid init method %d", ctx->init); | |
400 | } | |
401 | ||
402 | #if TEST_THREAD | |
403 | /* Helper function which calls run_res_init from a thread. */ | |
404 | static void * | |
405 | run_res_init_thread_func (void *closure) | |
406 | { | |
407 | run_res_init (closure); | |
408 | return NULL; | |
409 | } | |
410 | ||
411 | /* Variant of res_run_init which runs the function on a non-main | |
412 | thread. */ | |
413 | static void | |
414 | run_res_init_on_thread (void *closure) | |
415 | { | |
416 | xpthread_join (xpthread_create (NULL, run_res_init_thread_func, closure)); | |
417 | } | |
418 | #endif /* TEST_THREAD */ | |
419 | ||
420 | struct test_case test_cases[] = | |
421 | { | |
422 | {.name = "empty file", | |
423 | .conf = "", | |
424 | .expected = "search example.com\n" | |
3f853f22 | 425 | "; search[0]: example.com\n" |
2714c5f3 | 426 | "nameserver 127.0.0.1\n" |
a1c4eb87 | 427 | "; nameserver[0]: [127.0.0.1]:53\n" |
2714c5f3 | 428 | }, |
4446a885 FW |
429 | {.name = "empty file, no-dot hostname", |
430 | .conf = "", | |
431 | .expected = "nameserver 127.0.0.1\n" | |
432 | "; nameserver[0]: [127.0.0.1]:53\n", | |
433 | .hostname = "example", | |
434 | }, | |
2714c5f3 FW |
435 | {.name = "empty file with LOCALDOMAIN", |
436 | .conf = "", | |
437 | .expected = "search example.net\n" | |
3f853f22 | 438 | "; search[0]: example.net\n" |
a1c4eb87 FW |
439 | "nameserver 127.0.0.1\n" |
440 | "; nameserver[0]: [127.0.0.1]:53\n", | |
2714c5f3 FW |
441 | .localdomain = "example.net", |
442 | }, | |
443 | {.name = "empty file with RES_OPTIONS", | |
444 | .conf = "", | |
445 | .expected = "options attempts:5 edns0\n" | |
446 | "search example.com\n" | |
3f853f22 | 447 | "; search[0]: example.com\n" |
a1c4eb87 FW |
448 | "nameserver 127.0.0.1\n" |
449 | "; nameserver[0]: [127.0.0.1]:53\n", | |
2714c5f3 FW |
450 | .res_options = "edns0 attempts:5", |
451 | }, | |
452 | {.name = "empty file with RES_OPTIONS and LOCALDOMAIN", | |
453 | .conf = "", | |
454 | .expected = "options attempts:5 edns0\n" | |
455 | "search example.org\n" | |
3f853f22 | 456 | "; search[0]: example.org\n" |
a1c4eb87 FW |
457 | "nameserver 127.0.0.1\n" |
458 | "; nameserver[0]: [127.0.0.1]:53\n", | |
2714c5f3 FW |
459 | .localdomain = "example.org", |
460 | .res_options = "edns0 attempts:5", | |
461 | }, | |
462 | {.name = "basic", | |
4446a885 | 463 | .conf = "search corp.example.com example.com\n" |
2714c5f3 FW |
464 | "nameserver 192.0.2.1\n", |
465 | .expected = "search corp.example.com example.com\n" | |
3f853f22 FW |
466 | "; search[0]: corp.example.com\n" |
467 | "; search[1]: example.com\n" | |
2714c5f3 | 468 | "nameserver 192.0.2.1\n" |
a1c4eb87 | 469 | "; nameserver[0]: [192.0.2.1]:53\n" |
2714c5f3 | 470 | }, |
4446a885 FW |
471 | {.name = "basic with no-dot hostname", |
472 | .conf = "search corp.example.com example.com\n" | |
473 | "nameserver 192.0.2.1\n", | |
474 | .expected = "search corp.example.com example.com\n" | |
475 | "; search[0]: corp.example.com\n" | |
476 | "; search[1]: example.com\n" | |
477 | "nameserver 192.0.2.1\n" | |
478 | "; nameserver[0]: [192.0.2.1]:53\n", | |
479 | .hostname = "example", | |
480 | }, | |
aef16cc8 FW |
481 | {.name = "basic no-reload", |
482 | .conf = "options no-reload\n" | |
483 | "search corp.example.com example.com\n" | |
484 | "nameserver 192.0.2.1\n", | |
485 | .expected = "options no-reload\n" | |
486 | "search corp.example.com example.com\n" | |
487 | "; search[0]: corp.example.com\n" | |
488 | "; search[1]: example.com\n" | |
489 | "nameserver 192.0.2.1\n" | |
490 | "; nameserver[0]: [192.0.2.1]:53\n" | |
491 | }, | |
492 | {.name = "basic no-reload via RES_OPTIONS", | |
493 | .conf = "search corp.example.com example.com\n" | |
494 | "nameserver 192.0.2.1\n", | |
495 | .expected = "options no-reload\n" | |
496 | "search corp.example.com example.com\n" | |
497 | "; search[0]: corp.example.com\n" | |
498 | "; search[1]: example.com\n" | |
499 | "nameserver 192.0.2.1\n" | |
500 | "; nameserver[0]: [192.0.2.1]:53\n", | |
501 | .res_options = "no-reload" | |
502 | }, | |
2714c5f3 FW |
503 | {.name = "whitespace", |
504 | .conf = "# This test covers comment and whitespace processing " | |
505 | " (trailing whitespace,\n" | |
506 | "# missing newline at end of file).\n" | |
507 | "\n" | |
2714c5f3 | 508 | ";search commented out\n" |
3f853f22 | 509 | "search corp.example.com\texample.com \n" |
2714c5f3 FW |
510 | "#nameserver 192.0.2.3\n" |
511 | "nameserver 192.0.2.1 \n" | |
512 | "nameserver 192.0.2.2", /* No \n at end of file. */ | |
513 | .expected = "search corp.example.com example.com\n" | |
3f853f22 FW |
514 | "; search[0]: corp.example.com\n" |
515 | "; search[1]: example.com\n" | |
2714c5f3 FW |
516 | "nameserver 192.0.2.1\n" |
517 | "nameserver 192.0.2.2\n" | |
a1c4eb87 FW |
518 | "; nameserver[0]: [192.0.2.1]:53\n" |
519 | "; nameserver[1]: [192.0.2.2]:53\n" | |
2714c5f3 | 520 | }, |
3f853f22 FW |
521 | {.name = "domain", |
522 | .conf = "domain example.net\n" | |
523 | "nameserver 192.0.2.1\n", | |
524 | .expected = "search example.net\n" | |
525 | "; search[0]: example.net\n" | |
526 | "nameserver 192.0.2.1\n" | |
a1c4eb87 | 527 | "; nameserver[0]: [192.0.2.1]:53\n" |
3f853f22 FW |
528 | }, |
529 | {.name = "domain space", | |
530 | .conf = "domain example.net \n" | |
531 | "nameserver 192.0.2.1\n", | |
532 | .expected = "search example.net\n" | |
533 | "; search[0]: example.net\n" | |
534 | "nameserver 192.0.2.1\n" | |
a1c4eb87 | 535 | "; nameserver[0]: [192.0.2.1]:53\n" |
3f853f22 FW |
536 | }, |
537 | {.name = "domain tab", | |
538 | .conf = "domain example.net\t\n" | |
539 | "nameserver 192.0.2.1\n", | |
540 | .expected = "search example.net\n" | |
541 | "; search[0]: example.net\n" | |
542 | "nameserver 192.0.2.1\n" | |
a1c4eb87 | 543 | "; nameserver[0]: [192.0.2.1]:53\n" |
3f853f22 FW |
544 | }, |
545 | {.name = "domain override", | |
546 | .conf = "search example.com example.org\n" | |
547 | "nameserver 192.0.2.1\n" | |
548 | "domain example.net", /* No \n at end of file. */ | |
549 | .expected = "search example.net\n" | |
550 | "; search[0]: example.net\n" | |
551 | "nameserver 192.0.2.1\n" | |
a1c4eb87 | 552 | "; nameserver[0]: [192.0.2.1]:53\n" |
3f853f22 | 553 | }, |
2714c5f3 FW |
554 | {.name = "option values, multiple servers", |
555 | .conf = "options\tinet6\tndots:3 edns0\tattempts:5\ttimeout:19\n" | |
556 | "domain example.net\n" | |
557 | ";domain comment\n" | |
558 | "search corp.example.com\texample.com\n" | |
559 | "nameserver 192.0.2.1\n" | |
560 | "nameserver ::1\n" | |
561 | "nameserver 192.0.2.2\n", | |
3f8b44be | 562 | .expected = "options ndots:3 timeout:19 attempts:5 edns0\n" |
2714c5f3 | 563 | "search corp.example.com example.com\n" |
3f853f22 FW |
564 | "; search[0]: corp.example.com\n" |
565 | "; search[1]: example.com\n" | |
2714c5f3 FW |
566 | "nameserver 192.0.2.1\n" |
567 | "nameserver ::1\n" | |
568 | "nameserver 192.0.2.2\n" | |
a1c4eb87 FW |
569 | "; nameserver[0]: [192.0.2.1]:53\n" |
570 | "; nameserver[1]: [::1]:53\n" | |
571 | "; nameserver[2]: [192.0.2.2]:53\n" | |
2714c5f3 FW |
572 | }, |
573 | {.name = "out-of-range option vales", | |
574 | .conf = "options use-vc timeout:999 attempts:999 ndots:99\n" | |
575 | "search example.com\n", | |
576 | .expected = "options ndots:15 timeout:30 attempts:5 use-vc\n" | |
577 | "search example.com\n" | |
3f853f22 | 578 | "; search[0]: example.com\n" |
2714c5f3 | 579 | "nameserver 127.0.0.1\n" |
a1c4eb87 | 580 | "; nameserver[0]: [127.0.0.1]:53\n" |
2714c5f3 FW |
581 | }, |
582 | {.name = "repeated directives", | |
583 | .conf = "options ndots:3 use-vc\n" | |
584 | "options edns0 ndots:2\n" | |
585 | "domain corp.example\n" | |
586 | "search example.net corp.example.com example.com\n" | |
587 | "search example.org\n" | |
588 | "search\n", | |
589 | .expected = "options ndots:2 use-vc edns0\n" | |
590 | "search example.org\n" | |
3f853f22 | 591 | "; search[0]: example.org\n" |
2714c5f3 | 592 | "nameserver 127.0.0.1\n" |
a1c4eb87 | 593 | "; nameserver[0]: [127.0.0.1]:53\n" |
2714c5f3 FW |
594 | }, |
595 | {.name = "many name servers, sortlist", | |
596 | .conf = "options single-request\n" | |
597 | "search example.org example.com example.net corp.example.com\n" | |
598 | "sortlist 192.0.2.0/255.255.255.0\n" | |
599 | "nameserver 192.0.2.1\n" | |
600 | "nameserver 192.0.2.2\n" | |
601 | "nameserver 192.0.2.3\n" | |
602 | "nameserver 192.0.2.4\n" | |
603 | "nameserver 192.0.2.5\n" | |
604 | "nameserver 192.0.2.6\n" | |
605 | "nameserver 192.0.2.7\n" | |
606 | "nameserver 192.0.2.8\n", | |
607 | .expected = "options single-request\n" | |
608 | "search example.org example.com example.net corp.example.com\n" | |
3f853f22 FW |
609 | "; search[0]: example.org\n" |
610 | "; search[1]: example.com\n" | |
611 | "; search[2]: example.net\n" | |
612 | "; search[3]: corp.example.com\n" | |
2714c5f3 FW |
613 | "sortlist 192.0.2.0/255.255.255.0\n" |
614 | "nameserver 192.0.2.1\n" | |
615 | "nameserver 192.0.2.2\n" | |
616 | "nameserver 192.0.2.3\n" | |
a1c4eb87 FW |
617 | "; nameserver[0]: [192.0.2.1]:53\n" |
618 | "; nameserver[1]: [192.0.2.2]:53\n" | |
619 | "; nameserver[2]: [192.0.2.3]:53\n" | |
620 | "; nameserver[3]: [192.0.2.4]:53\n" | |
621 | "; nameserver[4]: [192.0.2.5]:53\n" | |
622 | "; nameserver[5]: [192.0.2.6]:53\n" | |
623 | "; nameserver[6]: [192.0.2.7]:53\n" | |
624 | "; nameserver[7]: [192.0.2.8]:53\n" | |
2714c5f3 FW |
625 | }, |
626 | {.name = "IPv4 and IPv6 nameservers", | |
627 | .conf = "options single-request\n" | |
628 | "search example.org example.com example.net corp.example.com" | |
629 | " legacy.example.com\n" | |
630 | "sortlist 192.0.2.0\n" | |
631 | "nameserver 192.0.2.1\n" | |
632 | "nameserver 2001:db8::2\n" | |
633 | "nameserver 192.0.2.3\n" | |
634 | "nameserver 2001:db8::4\n" | |
635 | "nameserver 192.0.2.5\n" | |
636 | "nameserver 2001:db8::6\n" | |
637 | "nameserver 192.0.2.7\n" | |
638 | "nameserver 2001:db8::8\n", | |
639 | .expected = "options single-request\n" | |
640 | "search example.org example.com example.net corp.example.com" | |
641 | " legacy.example.com\n" | |
3f853f22 FW |
642 | "; search[0]: example.org\n" |
643 | "; search[1]: example.com\n" | |
644 | "; search[2]: example.net\n" | |
645 | "; search[3]: corp.example.com\n" | |
646 | "; search[4]: legacy.example.com\n" | |
2714c5f3 FW |
647 | "sortlist 192.0.2.0/255.255.255.0\n" |
648 | "nameserver 192.0.2.1\n" | |
649 | "nameserver 2001:db8::2\n" | |
650 | "nameserver 192.0.2.3\n" | |
a1c4eb87 FW |
651 | "; nameserver[0]: [192.0.2.1]:53\n" |
652 | "; nameserver[1]: [2001:db8::2]:53\n" | |
653 | "; nameserver[2]: [192.0.2.3]:53\n" | |
654 | "; nameserver[3]: [2001:db8::4]:53\n" | |
655 | "; nameserver[4]: [192.0.2.5]:53\n" | |
656 | "; nameserver[5]: [2001:db8::6]:53\n" | |
657 | "; nameserver[6]: [192.0.2.7]:53\n" | |
658 | "; nameserver[7]: [2001:db8::8]:53\n", | |
2714c5f3 FW |
659 | }, |
660 | {.name = "garbage after nameserver", | |
661 | .conf = "nameserver 192.0.2.1 garbage\n" | |
662 | "nameserver 192.0.2.2:5353\n" | |
663 | "nameserver 192.0.2.3 5353\n", | |
664 | .expected = "search example.com\n" | |
3f853f22 | 665 | "; search[0]: example.com\n" |
2714c5f3 FW |
666 | "nameserver 192.0.2.1\n" |
667 | "nameserver 192.0.2.3\n" | |
a1c4eb87 FW |
668 | "; nameserver[0]: [192.0.2.1]:53\n" |
669 | "; nameserver[1]: [192.0.2.3]:53\n" | |
2714c5f3 FW |
670 | }, |
671 | {.name = "RES_OPTIONS is cummulative", | |
672 | .conf = "options timeout:7 ndots:2 use-vc\n" | |
673 | "nameserver 192.0.2.1\n", | |
674 | .expected = "options ndots:3 timeout:7 attempts:5 use-vc edns0\n" | |
675 | "search example.com\n" | |
3f853f22 | 676 | "; search[0]: example.com\n" |
a1c4eb87 FW |
677 | "nameserver 192.0.2.1\n" |
678 | "; nameserver[0]: [192.0.2.1]:53\n", | |
2714c5f3 FW |
679 | .res_options = "attempts:5 ndots:3 edns0 ", |
680 | }, | |
3f853f22 FW |
681 | {.name = "many search list entries (bug 19569)", |
682 | .conf = "nameserver 192.0.2.1\n" | |
683 | "search corp.example.com support.example.com" | |
684 | " community.example.org wan.example.net vpn.example.net" | |
685 | " example.com example.org example.net\n", | |
686 | .expected = "search corp.example.com support.example.com" | |
687 | " community.example.org wan.example.net vpn.example.net example.com\n" | |
688 | "; search[0]: corp.example.com\n" | |
689 | "; search[1]: support.example.com\n" | |
690 | "; search[2]: community.example.org\n" | |
691 | "; search[3]: wan.example.net\n" | |
692 | "; search[4]: vpn.example.net\n" | |
693 | "; search[5]: example.com\n" | |
694 | "; search[6]: example.org\n" | |
695 | "; search[7]: example.net\n" | |
a1c4eb87 FW |
696 | "nameserver 192.0.2.1\n" |
697 | "; nameserver[0]: [192.0.2.1]:53\n" | |
3f853f22 FW |
698 | }, |
699 | {.name = "very long search list entries (bug 21475)", | |
700 | .conf = "nameserver 192.0.2.1\n" | |
701 | "search example.com " | |
702 | #define H63 "this-host-name-is-longer-than-yours-yes-I-really-really-mean-it" | |
703 | #define D63 "this-domain-name-is-as-long-as-the-previous-name--63-characters" | |
704 | " " H63 "." D63 ".example.org" | |
705 | " " H63 "." D63 ".example.net\n", | |
706 | .expected = "search example.com " H63 "." D63 ".example.org\n" | |
707 | "; search[0]: example.com\n" | |
708 | "; search[1]: " H63 "." D63 ".example.org\n" | |
709 | "; search[2]: " H63 "." D63 ".example.net\n" | |
710 | #undef H63 | |
711 | #undef D63 | |
a1c4eb87 FW |
712 | "nameserver 192.0.2.1\n" |
713 | "; nameserver[0]: [192.0.2.1]:53\n" | |
3f853f22 | 714 | }, |
446997ff FW |
715 | {.name = "trust-ad flag", |
716 | .conf = "options trust-ad\n" | |
717 | "nameserver 192.0.2.1\n", | |
718 | .expected = "options trust-ad\n" | |
719 | "search example.com\n" | |
720 | "; search[0]: example.com\n" | |
721 | "nameserver 192.0.2.1\n" | |
722 | "; nameserver[0]: [192.0.2.1]:53\n" | |
723 | }, | |
2714c5f3 FW |
724 | { NULL } |
725 | }; | |
726 | ||
727 | /* Run the indicated test case. This function assumes that the chroot | |
728 | contents has already been set up. */ | |
729 | static void | |
730 | test_file_contents (const struct test_case *t) | |
731 | { | |
732 | #if TEST_THREAD | |
733 | for (int do_thread = 0; do_thread < 2; ++do_thread) | |
734 | #endif | |
735 | for (int init_method = 0; init_method <= test_init_method_last; | |
736 | ++init_method) | |
737 | { | |
738 | if (test_verbose > 0) | |
a9270e67 FW |
739 | printf ("info: testing init method %s\n", |
740 | test_init_names[init_method]); | |
2714c5f3 FW |
741 | struct test_context ctx = { .init = init_method, .t = t }; |
742 | void (*func) (void *) = run_res_init; | |
743 | #if TEST_THREAD | |
744 | if (do_thread) | |
745 | func = run_res_init_on_thread; | |
746 | #endif | |
747 | struct support_capture_subprocess proc | |
748 | = support_capture_subprocess (func, &ctx); | |
749 | if (strcmp (proc.out.buffer, t->expected) != 0) | |
750 | { | |
751 | support_record_failure (); | |
a9270e67 FW |
752 | printf ("error: output mismatch for %s (init method %s)\n", |
753 | t->name, test_init_names[init_method]); | |
2714c5f3 FW |
754 | support_run_diff ("expected", t->expected, |
755 | "actual", proc.out.buffer); | |
756 | } | |
757 | support_capture_subprocess_check (&proc, t->name, 0, | |
758 | sc_allow_stdout); | |
759 | support_capture_subprocess_free (&proc); | |
760 | } | |
761 | } | |
762 | ||
4e45d83c | 763 | /* Special tests which do not follow the general pattern. */ |
aef16cc8 | 764 | enum { special_tests_count = 11 }; |
4e45d83c FW |
765 | |
766 | /* Implementation of special tests. */ | |
767 | static void | |
768 | special_test_callback (void *closure) | |
769 | { | |
770 | unsigned int *test_indexp = closure; | |
771 | unsigned test_index = *test_indexp; | |
772 | TEST_VERIFY (test_index < special_tests_count); | |
773 | if (test_verbose > 0) | |
774 | printf ("info: special test %u\n", test_index); | |
d4165eed | 775 | xchroot (chroot_env->path_chroot); |
4e45d83c FW |
776 | |
777 | switch (test_index) | |
778 | { | |
779 | case 0: | |
780 | case 1: | |
781 | /* Second res_init with missing or empty file preserves | |
782 | flags. */ | |
783 | if (test_index == 1) | |
784 | TEST_VERIFY (unlink (_PATH_RESCONF) == 0); | |
785 | _res.options = RES_USE_EDNS0; | |
786 | TEST_VERIFY (res_init () == 0); | |
787 | /* First res_init clears flag. */ | |
788 | TEST_VERIFY (!(_res.options & RES_USE_EDNS0)); | |
789 | _res.options |= RES_USE_EDNS0; | |
790 | TEST_VERIFY (res_init () == 0); | |
791 | /* Second res_init preserves flag. */ | |
792 | TEST_VERIFY (_res.options & RES_USE_EDNS0); | |
793 | if (test_index == 1) | |
794 | /* Restore empty file. */ | |
795 | support_write_file_string (_PATH_RESCONF, ""); | |
796 | break; | |
797 | ||
798 | case 2: | |
799 | /* Second res_init is cumulative. */ | |
800 | support_write_file_string (_PATH_RESCONF, | |
801 | "options rotate\n" | |
802 | "nameserver 192.0.2.1\n"); | |
803 | _res.options = RES_USE_EDNS0; | |
804 | TEST_VERIFY (res_init () == 0); | |
805 | /* First res_init clears flag. */ | |
806 | TEST_VERIFY (!(_res.options & RES_USE_EDNS0)); | |
807 | /* And sets RES_ROTATE. */ | |
808 | TEST_VERIFY (_res.options & RES_ROTATE); | |
809 | _res.options |= RES_USE_EDNS0; | |
810 | TEST_VERIFY (res_init () == 0); | |
811 | /* Second res_init preserves flag. */ | |
812 | TEST_VERIFY (_res.options & RES_USE_EDNS0); | |
813 | TEST_VERIFY (_res.options & RES_ROTATE); | |
814 | /* Reloading the configuration does not clear the explicitly set | |
815 | flag. */ | |
816 | support_write_file_string (_PATH_RESCONF, | |
817 | "nameserver 192.0.2.1\n" | |
818 | "nameserver 192.0.2.2\n"); | |
819 | TEST_VERIFY (res_init () == 0); | |
820 | TEST_VERIFY (_res.nscount == 2); | |
821 | TEST_VERIFY (_res.options & RES_USE_EDNS0); | |
822 | /* Whether RES_ROTATE (originally in resolv.conf, now removed) | |
823 | should be preserved is subject to debate. See bug 21701. */ | |
824 | /* TEST_VERIFY (!(_res.options & RES_ROTATE)); */ | |
825 | break; | |
826 | ||
827 | case 3: | |
828 | case 4: | |
829 | case 5: | |
830 | case 6: | |
4e45d83c FW |
831 | support_write_file_string (_PATH_RESCONF, |
832 | "options edns0\n" | |
833 | "nameserver 192.0.2.1\n"); | |
aef16cc8 FW |
834 | goto reload_tests; |
835 | case 7: /* 7 and the following tests are with no-reload. */ | |
836 | case 8: | |
837 | case 9: | |
838 | case 10: | |
839 | support_write_file_string (_PATH_RESCONF, | |
840 | "options edns0 no-reload\n" | |
841 | "nameserver 192.0.2.1\n"); | |
842 | /* Fall through. */ | |
843 | reload_tests: | |
4e45d83c FW |
844 | for (int iteration = 0; iteration < 2; ++iteration) |
845 | { | |
846 | switch (test_index) | |
847 | { | |
848 | case 3: | |
aef16cc8 | 849 | case 7: |
4e45d83c FW |
850 | TEST_VERIFY (res_init () == 0); |
851 | break; | |
852 | case 4: | |
aef16cc8 | 853 | case 8: |
4e45d83c FW |
854 | { |
855 | unsigned char buf[512]; | |
856 | TEST_VERIFY | |
857 | (res_mkquery (QUERY, test_hostname, C_IN, T_A, | |
858 | NULL, 0, NULL, buf, sizeof (buf)) > 0); | |
859 | } | |
860 | break; | |
861 | case 5: | |
aef16cc8 | 862 | case 9: |
4e45d83c FW |
863 | gethostbyname (test_hostname); |
864 | break; | |
865 | case 6: | |
aef16cc8 | 866 | case 10: |
4e45d83c FW |
867 | { |
868 | struct addrinfo *ai; | |
869 | (void) getaddrinfo (test_hostname, NULL, NULL, &ai); | |
870 | } | |
871 | break; | |
872 | } | |
aef16cc8 FW |
873 | /* test_index == 7 is res_init and performs a reload even |
874 | with no-reload. */ | |
875 | if (iteration == 0 || test_index > 7) | |
4e45d83c FW |
876 | { |
877 | TEST_VERIFY (_res.options & RES_USE_EDNS0); | |
878 | TEST_VERIFY (!(_res.options & RES_ROTATE)); | |
aef16cc8 FW |
879 | if (test_index < 7) |
880 | TEST_VERIFY (!(_res.options & RES_NORELOAD)); | |
881 | else | |
882 | TEST_VERIFY (_res.options & RES_NORELOAD); | |
4e45d83c | 883 | TEST_VERIFY (_res.nscount == 1); |
aef16cc8 | 884 | /* File change triggers automatic reloading. */ |
4e45d83c FW |
885 | support_write_file_string (_PATH_RESCONF, |
886 | "options rotate\n" | |
887 | "nameserver 192.0.2.1\n" | |
888 | "nameserver 192.0.2.2\n"); | |
4e45d83c FW |
889 | } |
890 | else | |
891 | { | |
aef16cc8 FW |
892 | if (test_index != 3 && test_index != 7) |
893 | /* test_index 3, 7 are res_init; this function does | |
894 | not reset flags. See bug 21701. */ | |
895 | TEST_VERIFY (!(_res.options & RES_USE_EDNS0)); | |
4e45d83c FW |
896 | TEST_VERIFY (_res.options & RES_ROTATE); |
897 | TEST_VERIFY (_res.nscount == 2); | |
898 | } | |
899 | } | |
4e45d83c FW |
900 | break; |
901 | } | |
902 | } | |
903 | ||
904 | #if TEST_THREAD | |
905 | /* Helper function which calls special_test_callback from a | |
906 | thread. */ | |
907 | static void * | |
908 | special_test_thread_func (void *closure) | |
909 | { | |
910 | special_test_callback (closure); | |
911 | return NULL; | |
912 | } | |
913 | ||
914 | /* Variant of special_test_callback which runs the function on a | |
915 | non-main thread. */ | |
916 | static void | |
917 | run_special_test_on_thread (void *closure) | |
918 | { | |
919 | xpthread_join (xpthread_create (NULL, special_test_thread_func, closure)); | |
920 | } | |
921 | #endif /* TEST_THREAD */ | |
922 | ||
923 | /* Perform the requested special test in a subprocess using | |
924 | special_test_callback. */ | |
925 | static void | |
926 | special_test (unsigned int test_index) | |
927 | { | |
928 | #if TEST_THREAD | |
929 | for (int do_thread = 0; do_thread < 2; ++do_thread) | |
930 | #endif | |
931 | { | |
932 | void (*func) (void *) = special_test_callback; | |
933 | #if TEST_THREAD | |
934 | if (do_thread) | |
935 | func = run_special_test_on_thread; | |
936 | #endif | |
937 | struct support_capture_subprocess proc | |
938 | = support_capture_subprocess (func, &test_index); | |
939 | char *test_name = xasprintf ("special test %u", test_index); | |
940 | if (strcmp (proc.out.buffer, "") != 0) | |
941 | { | |
942 | support_record_failure (); | |
943 | printf ("error: output mismatch for %s\n", test_name); | |
944 | support_run_diff ("expected", "", | |
945 | "actual", proc.out.buffer); | |
946 | } | |
947 | support_capture_subprocess_check (&proc, test_name, 0, sc_allow_stdout); | |
948 | free (test_name); | |
949 | support_capture_subprocess_free (&proc); | |
950 | } | |
951 | } | |
952 | ||
953 | ||
39bd76df FW |
954 | /* Dummy DNS server. It ensures that the probe queries sent by |
955 | gethostbyname and getaddrinfo receive a reply even if the system | |
956 | applies a very strict rate limit to localhost. */ | |
957 | static pid_t | |
958 | start_dummy_server (void) | |
959 | { | |
960 | int server_socket = xsocket (AF_INET, SOCK_DGRAM, 0); | |
961 | { | |
962 | struct sockaddr_in sin = | |
963 | { | |
964 | .sin_family = AF_INET, | |
965 | .sin_addr = { .s_addr = htonl (INADDR_LOOPBACK) }, | |
966 | .sin_port = htons (53), | |
967 | }; | |
968 | int ret = bind (server_socket, (struct sockaddr *) &sin, sizeof (sin)); | |
969 | if (ret < 0) | |
970 | { | |
971 | if (errno == EACCES) | |
972 | /* The port is reserved, which means we cannot start the | |
973 | server. */ | |
974 | return -1; | |
975 | FAIL_EXIT1 ("cannot bind socket to port 53: %m"); | |
976 | } | |
977 | } | |
978 | ||
979 | pid_t pid = xfork (); | |
980 | if (pid == 0) | |
981 | { | |
982 | /* Child process. Echo back queries as SERVFAIL responses. */ | |
983 | while (true) | |
984 | { | |
985 | union | |
986 | { | |
987 | HEADER header; | |
988 | unsigned char bytes[512]; | |
989 | } packet; | |
990 | struct sockaddr_in sin; | |
991 | socklen_t sinlen = sizeof (sin); | |
992 | ||
993 | ssize_t ret = recvfrom | |
994 | (server_socket, &packet, sizeof (packet), | |
995 | MSG_NOSIGNAL, (struct sockaddr *) &sin, &sinlen); | |
996 | if (ret < 0) | |
997 | FAIL_EXIT1 ("recvfrom on fake server socket: %m"); | |
998 | if (ret > sizeof (HEADER)) | |
999 | { | |
1000 | /* Turn the query into a SERVFAIL response. */ | |
1001 | packet.header.qr = 1; | |
1002 | packet.header.rcode = ns_r_servfail; | |
1003 | ||
1004 | /* Send the response. */ | |
1005 | ret = sendto (server_socket, &packet, ret, | |
1006 | MSG_NOSIGNAL, (struct sockaddr *) &sin, sinlen); | |
1007 | if (ret < 0) | |
1008 | /* The peer may have closed socket prematurely, so | |
1009 | this is not an error. */ | |
1010 | printf ("warning: sending DNS server reply: %m\n"); | |
1011 | } | |
1012 | } | |
1013 | } | |
1014 | ||
1015 | /* In the parent, close the socket. */ | |
1016 | xclose (server_socket); | |
1017 | ||
1018 | return pid; | |
1019 | } | |
1020 | ||
2714c5f3 FW |
1021 | static int |
1022 | do_test (void) | |
1023 | { | |
1024 | support_become_root (); | |
1025 | support_enter_network_namespace (); | |
1026 | if (!support_in_uts_namespace () || !support_can_chroot ()) | |
1027 | return EXIT_UNSUPPORTED; | |
1028 | ||
1029 | /* We are in an UTS namespace, so we can set the host name without | |
1030 | altering the state of the entire system. */ | |
1031 | if (sethostname (test_hostname, strlen (test_hostname)) != 0) | |
1032 | FAIL_EXIT1 ("sethostname: %m"); | |
1033 | ||
1034 | /* These environment variables affect resolv.conf parsing. */ | |
1035 | unsetenv ("LOCALDOMAIN"); | |
1036 | unsetenv ("RES_OPTIONS"); | |
1037 | ||
1038 | /* Ensure that the chroot setup worked. */ | |
1039 | { | |
1040 | struct support_capture_subprocess proc | |
1041 | = support_capture_subprocess (check_chroot_working, NULL); | |
1042 | support_capture_subprocess_check (&proc, "chroot", 0, sc_allow_none); | |
1043 | support_capture_subprocess_free (&proc); | |
1044 | } | |
1045 | ||
39bd76df FW |
1046 | pid_t server = start_dummy_server (); |
1047 | ||
2714c5f3 FW |
1048 | for (size_t i = 0; test_cases[i].name != NULL; ++i) |
1049 | { | |
1050 | if (test_verbose > 0) | |
1051 | printf ("info: running test: %s\n", test_cases[i].name); | |
1052 | TEST_VERIFY (test_cases[i].conf != NULL); | |
1053 | TEST_VERIFY (test_cases[i].expected != NULL); | |
1054 | ||
d4165eed FW |
1055 | support_write_file_string (chroot_env->path_resolv_conf, |
1056 | test_cases[i].conf); | |
2714c5f3 FW |
1057 | |
1058 | test_file_contents (&test_cases[i]); | |
1059 | ||
1060 | /* The expected output from the empty file test is used for | |
1061 | further tests. */ | |
1062 | if (test_cases[i].conf[0] == '\0') | |
1063 | { | |
1064 | if (test_verbose > 0) | |
1065 | printf ("info: special test: missing file\n"); | |
d4165eed | 1066 | TEST_VERIFY (unlink (chroot_env->path_resolv_conf) == 0); |
2714c5f3 FW |
1067 | test_file_contents (&test_cases[i]); |
1068 | ||
1069 | if (test_verbose > 0) | |
1070 | printf ("info: special test: dangling symbolic link\n"); | |
d4165eed | 1071 | TEST_VERIFY (symlink ("does-not-exist", chroot_env->path_resolv_conf) == 0); |
2714c5f3 | 1072 | test_file_contents (&test_cases[i]); |
d4165eed | 1073 | TEST_VERIFY (unlink (chroot_env->path_resolv_conf) == 0); |
2714c5f3 FW |
1074 | |
1075 | if (test_verbose > 0) | |
1076 | printf ("info: special test: unreadable file\n"); | |
d4165eed FW |
1077 | support_write_file_string (chroot_env->path_resolv_conf, ""); |
1078 | TEST_VERIFY (chmod (chroot_env->path_resolv_conf, 0) == 0); | |
2714c5f3 FW |
1079 | test_file_contents (&test_cases[i]); |
1080 | ||
1081 | /* Restore the empty file. */ | |
d4165eed FW |
1082 | TEST_VERIFY (unlink (chroot_env->path_resolv_conf) == 0); |
1083 | support_write_file_string (chroot_env->path_resolv_conf, ""); | |
2714c5f3 FW |
1084 | } |
1085 | } | |
1086 | ||
4e45d83c FW |
1087 | /* The tests which do not follow a regular pattern. */ |
1088 | for (unsigned int test_index = 0; | |
1089 | test_index < special_tests_count; ++test_index) | |
1090 | special_test (test_index); | |
1091 | ||
39bd76df FW |
1092 | if (server > 0) |
1093 | { | |
1094 | if (kill (server, SIGTERM) < 0) | |
1095 | FAIL_EXIT1 ("could not terminate server process: %m"); | |
1096 | xwaitpid (server, NULL, 0); | |
1097 | } | |
1098 | ||
d4165eed | 1099 | support_chroot_free (chroot_env); |
2714c5f3 FW |
1100 | return 0; |
1101 | } | |
1102 | ||
1103 | #define PREPARE prepare | |
1104 | #include <support/test-driver.c> |