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