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