posix: Correctly enable/disable cancellation on Linux posix_spawn
[glibc.git] / resolv / res_hconf.c
blob093c26837f718fdc1b1f1e473242f97036f87981
1 /* Copyright (C) 1993-2016 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
3 Contributed by David Mosberger (davidm@azstarnet.com).
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.
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.
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/>. */
19 /* This file provides a Linux /etc/host.conf compatible front end to
20 the various name resolvers (/etc/hosts, named, NIS server, etc.).
21 Though mostly compatibly, the following differences exist compared
22 to the original implementation:
24 - new command "spoof" takes an arguments like RESOLV_SPOOF_CHECK
25 environment variable (i.e., `off', `nowarn', or `warn').
27 - line comments can appear anywhere (not just at the beginning of
28 a line)
31 #include <assert.h>
32 #include <errno.h>
33 #include <ctype.h>
34 #include <libintl.h>
35 #include <memory.h>
36 #include <stdio.h>
37 #include <stdio_ext.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <net/if.h>
41 #include <sys/ioctl.h>
42 #include <unistd.h>
43 #include <netinet/in.h>
44 #include <libc-lock.h>
45 #include "ifreq.h"
46 #include "res_hconf.h"
47 #include <wchar.h>
48 #include <atomic.h>
50 #if IS_IN (libc)
51 # define fgets_unlocked __fgets_unlocked
52 #endif
54 #define _PATH_HOSTCONF "/etc/host.conf"
56 /* Environment vars that all user to override default behavior: */
57 #define ENV_HOSTCONF "RESOLV_HOST_CONF"
58 #define ENV_SPOOF "RESOLV_SPOOF_CHECK"
59 #define ENV_TRIM_OVERR "RESOLV_OVERRIDE_TRIM_DOMAINS"
60 #define ENV_TRIM_ADD "RESOLV_ADD_TRIM_DOMAINS"
61 #define ENV_MULTI "RESOLV_MULTI"
62 #define ENV_REORDER "RESOLV_REORDER"
64 enum parse_cbs
66 CB_none,
67 CB_arg_trimdomain_list,
68 CB_arg_spoof,
69 CB_arg_bool
72 static const struct cmd
74 const char name[11];
75 uint8_t cb;
76 unsigned int arg;
77 } cmd[] =
79 {"order", CB_none, 0},
80 {"trim", CB_arg_trimdomain_list, 0},
81 {"spoof", CB_arg_spoof, 0},
82 {"multi", CB_arg_bool, HCONF_FLAG_MULTI},
83 {"nospoof", CB_arg_bool, HCONF_FLAG_SPOOF},
84 {"spoofalert", CB_arg_bool, HCONF_FLAG_SPOOFALERT},
85 {"reorder", CB_arg_bool, HCONF_FLAG_REORDER}
88 /* Structure containing the state. */
89 struct hconf _res_hconf;
91 /* Skip white space. */
92 static const char *
93 skip_ws (const char *str)
95 while (isspace (*str)) ++str;
96 return str;
100 /* Skip until whitespace, comma, end of line, or comment character. */
101 static const char *
102 skip_string (const char *str)
104 while (*str && !isspace (*str) && *str != '#' && *str != ',')
105 ++str;
106 return str;
110 static const char *
111 arg_trimdomain_list (const char *fname, int line_num, const char *args)
113 const char * start;
114 size_t len;
118 start = args;
119 args = skip_string (args);
120 len = args - start;
122 if (_res_hconf.num_trimdomains >= TRIMDOMAINS_MAX)
124 char *buf;
126 if (__asprintf (&buf, _("\
127 %s: line %d: cannot specify more than %d trim domains"),
128 fname, line_num, TRIMDOMAINS_MAX) < 0)
129 return 0;
131 __fxprintf (NULL, "%s", buf);
133 free (buf);
134 return 0;
136 _res_hconf.trimdomain[_res_hconf.num_trimdomains++] =
137 __strndup (start, len);
138 args = skip_ws (args);
139 switch (*args)
141 case ',': case ';': case ':':
142 args = skip_ws (++args);
143 if (!*args || *args == '#')
145 char *buf;
147 if (__asprintf (&buf, _("\
148 %s: line %d: list delimiter not followed by domain"),
149 fname, line_num) < 0)
150 return 0;
152 __fxprintf (NULL, "%s", buf);
154 free (buf);
155 return 0;
157 default:
158 break;
161 while (*args && *args != '#');
162 return args;
166 static const char *
167 arg_spoof (const char *fname, int line_num, const char *args)
169 const char *start = args;
170 size_t len;
172 args = skip_string (args);
173 len = args - start;
175 if (len == 3 && __strncasecmp (start, "off", len) == 0)
176 _res_hconf.flags &= ~(HCONF_FLAG_SPOOF | HCONF_FLAG_SPOOFALERT);
177 else
179 _res_hconf.flags |= (HCONF_FLAG_SPOOF | HCONF_FLAG_SPOOFALERT);
180 if ((len == 6 && __strncasecmp (start, "nowarn", len) == 0)
181 || !(len == 4 && __strncasecmp (start, "warn", len) == 0))
182 _res_hconf.flags &= ~HCONF_FLAG_SPOOFALERT;
184 return args;
188 static const char *
189 arg_bool (const char *fname, int line_num, const char *args, unsigned flag)
191 if (__strncasecmp (args, "on", 2) == 0)
193 args += 2;
194 _res_hconf.flags |= flag;
196 else if (__strncasecmp (args, "off", 3) == 0)
198 args += 3;
199 _res_hconf.flags &= ~flag;
201 else
203 char *buf;
205 if (__asprintf (&buf,
206 _("%s: line %d: expected `on' or `off', found `%s'\n"),
207 fname, line_num, args) < 0)
208 return 0;
210 __fxprintf (NULL, "%s", buf);
212 free (buf);
213 return 0;
215 return args;
219 static void
220 parse_line (const char *fname, int line_num, const char *str)
222 const char *start;
223 const struct cmd *c = 0;
224 size_t len;
225 size_t i;
227 str = skip_ws (str);
229 /* skip line comment and empty lines: */
230 if (*str == '\0' || *str == '#') return;
232 start = str;
233 str = skip_string (str);
234 len = str - start;
236 for (i = 0; i < sizeof (cmd) / sizeof (cmd[0]); ++i)
238 if (__strncasecmp (start, cmd[i].name, len) == 0
239 && strlen (cmd[i].name) == len)
241 c = &cmd[i];
242 break;
245 if (c == NULL)
247 char *buf;
249 if (__asprintf (&buf, _("%s: line %d: bad command `%s'\n"),
250 fname, line_num, start) < 0)
251 return;
253 __fxprintf (NULL, "%s", buf);
255 free (buf);
256 return;
259 /* process args: */
260 str = skip_ws (str);
262 if (c->cb == CB_arg_trimdomain_list)
263 str = arg_trimdomain_list (fname, line_num, str);
264 else if (c->cb == CB_arg_spoof)
265 str = arg_spoof (fname, line_num, str);
266 else if (c->cb == CB_arg_bool)
267 str = arg_bool (fname, line_num, str, c->arg);
268 else
269 /* Ignore the line. */
270 return;
272 if (!str)
273 return;
275 /* rest of line must contain white space or comment only: */
276 while (*str)
278 if (!isspace (*str)) {
279 if (*str != '#')
281 char *buf;
283 if (__asprintf (&buf,
284 _("%s: line %d: ignoring trailing garbage `%s'\n"),
285 fname, line_num, str) < 0)
286 break;
288 __fxprintf (NULL, "%s", buf);
290 free (buf);
292 break;
294 ++str;
299 static void
300 do_init (void)
302 const char *hconf_name;
303 int line_num = 0;
304 char buf[256], *envval;
305 FILE *fp;
307 memset (&_res_hconf, '\0', sizeof (_res_hconf));
309 hconf_name = getenv (ENV_HOSTCONF);
310 if (hconf_name == NULL)
311 hconf_name = _PATH_HOSTCONF;
313 fp = fopen (hconf_name, "rce");
314 if (fp)
316 /* No threads using this stream. */
317 __fsetlocking (fp, FSETLOCKING_BYCALLER);
319 while (fgets_unlocked (buf, sizeof (buf), fp))
321 ++line_num;
322 *__strchrnul (buf, '\n') = '\0';
323 parse_line (hconf_name, line_num, buf);
325 fclose (fp);
328 envval = getenv (ENV_SPOOF);
329 if (envval)
330 arg_spoof (ENV_SPOOF, 1, envval);
332 envval = getenv (ENV_MULTI);
333 if (envval)
334 arg_bool (ENV_MULTI, 1, envval, HCONF_FLAG_MULTI);
336 envval = getenv (ENV_REORDER);
337 if (envval)
338 arg_bool (ENV_REORDER, 1, envval, HCONF_FLAG_REORDER);
340 envval = getenv (ENV_TRIM_ADD);
341 if (envval)
342 arg_trimdomain_list (ENV_TRIM_ADD, 1, envval);
344 envval = getenv (ENV_TRIM_OVERR);
345 if (envval)
347 _res_hconf.num_trimdomains = 0;
348 arg_trimdomain_list (ENV_TRIM_OVERR, 1, envval);
351 /* See comments on the declaration of _res_hconf. */
352 atomic_store_release (&_res_hconf.initialized, 1);
356 /* Initialize hconf datastructure by reading host.conf file and
357 environment variables. */
358 void
359 _res_hconf_init (void)
361 __libc_once_define (static, once);
363 __libc_once (once, do_init);
367 #if IS_IN (libc)
368 # if defined SIOCGIFCONF && defined SIOCGIFNETMASK
369 /* List of known interfaces. */
370 libc_freeres_ptr (
371 static struct netaddr
373 int addrtype;
374 union
376 struct
378 u_int32_t addr;
379 u_int32_t mask;
380 } ipv4;
381 } u;
382 } *ifaddrs);
383 # endif
385 /* Reorder addresses returned in a hostent such that the first address
386 is an address on the local subnet, if there is such an address.
387 Otherwise, nothing is changed.
389 Note that this function currently only handles IPv4 addresses. */
391 void
392 _res_hconf_reorder_addrs (struct hostent *hp)
394 #if defined SIOCGIFCONF && defined SIOCGIFNETMASK
395 int i, j;
396 /* Number of interfaces. Also serves as a flag for the
397 double-checked locking idiom. */
398 static int num_ifs = -1;
399 /* Local copy of num_ifs, for non-atomic access. */
400 int num_ifs_local;
401 /* We need to protect the dynamic buffer handling. The lock is only
402 acquired during initialization. Afterwards, a positive num_ifs
403 value indicates completed initialization. */
404 __libc_lock_define_initialized (static, lock);
406 /* Only reorder if we're supposed to. */
407 if ((_res_hconf.flags & HCONF_FLAG_REORDER) == 0)
408 return;
410 /* Can't deal with anything but IPv4 for now... */
411 if (hp->h_addrtype != AF_INET)
412 return;
414 /* This load synchronizes with the release MO store in the
415 initialization block below. */
416 num_ifs_local = atomic_load_acquire (&num_ifs);
417 if (num_ifs_local <= 0)
419 struct ifreq *ifr, *cur_ifr;
420 int sd, num, i;
421 /* Save errno. */
422 int save = errno;
424 /* Initialize interface table. */
426 /* The SIOCGIFNETMASK ioctl will only work on an AF_INET socket. */
427 sd = __socket (AF_INET, SOCK_DGRAM, 0);
428 if (sd < 0)
429 return;
431 /* Get lock. */
432 __libc_lock_lock (lock);
434 /* Recheck, somebody else might have done the work by now. No
435 ordering is required for the load because we have the lock,
436 and num_ifs is only updated under the lock. Also see (3) in
437 the analysis below. */
438 num_ifs_local = atomic_load_relaxed (&num_ifs);
439 if (num_ifs_local <= 0)
441 /* This is the only block which writes to num_ifs. It can
442 be executed several times (sequentially) if
443 initialization does not yield any interfaces, and num_ifs
444 remains zero. However, once we stored a positive value
445 in num_ifs below, this block cannot be entered again due
446 to the condition above. */
447 int new_num_ifs = 0;
449 /* Get a list of interfaces. */
450 __ifreq (&ifr, &num, sd);
451 if (!ifr)
452 goto cleanup;
454 ifaddrs = malloc (num * sizeof (ifaddrs[0]));
455 if (!ifaddrs)
456 goto cleanup1;
458 /* Copy usable interfaces in ifaddrs structure. */
459 for (cur_ifr = ifr, i = 0; i < num;
460 cur_ifr = __if_nextreq (cur_ifr), ++i)
462 union
464 struct sockaddr sa;
465 struct sockaddr_in sin;
466 } ss;
468 if (cur_ifr->ifr_addr.sa_family != AF_INET)
469 continue;
471 ifaddrs[new_num_ifs].addrtype = AF_INET;
472 ss.sa = cur_ifr->ifr_addr;
473 ifaddrs[new_num_ifs].u.ipv4.addr = ss.sin.sin_addr.s_addr;
475 if (__ioctl (sd, SIOCGIFNETMASK, cur_ifr) < 0)
476 continue;
478 ss.sa = cur_ifr->ifr_netmask;
479 ifaddrs[new_num_ifs].u.ipv4.mask = ss.sin.sin_addr.s_addr;
481 /* Now we're committed to this entry. */
482 ++new_num_ifs;
484 /* Just keep enough memory to hold all the interfaces we want. */
485 ifaddrs = realloc (ifaddrs, new_num_ifs * sizeof (ifaddrs[0]));
486 assert (ifaddrs != NULL);
488 cleanup1:
489 __if_freereq (ifr, num);
491 cleanup:
492 /* Release lock, preserve error value, and close socket. */
493 errno = save;
495 /* Advertise successful initialization if new_num_ifs is
496 positive (and no updates to ifaddrs are permitted after
497 that). Otherwise, num_ifs remains unchanged, at zero.
498 This store synchronizes with the initial acquire MO
499 load. */
500 atomic_store_release (&num_ifs, new_num_ifs);
501 /* Keep the local copy current, to save another load. */
502 num_ifs_local = new_num_ifs;
505 __libc_lock_unlock (lock);
507 __close (sd);
510 /* num_ifs_local cannot be negative because the if statement above
511 covered this case. It can still be zero if we just performed
512 initialization, but could not find any interfaces. */
513 if (num_ifs_local == 0)
514 return;
516 /* The code below accesses ifaddrs, so we need to ensure that the
517 initialization happens-before this point.
519 The actual initialization is sequenced-before the release store
520 to num_ifs, and sequenced-before the end of the critical section.
522 This means there are three possible executions:
524 (1) The thread that initialized the data also uses it, so
525 sequenced-before is sufficient to ensure happens-before.
527 (2) The release MO store of num_ifs synchronizes-with the acquire
528 MO load, and the acquire MO load is sequenced before the use
529 of the initialized data below.
531 (3) We enter the critical section, and the relaxed MO load of
532 num_ifs yields a positive value. The write to ifaddrs is
533 sequenced-before leaving the critical section. Leaving the
534 critical section happens-before we entered the critical
535 section ourselves, which means that the write to ifaddrs
536 happens-before this point.
538 Consequently, all potential writes to ifaddrs (and the data it
539 points to) happens-before this point. */
541 /* Find an address for which we have a direct connection. */
542 for (i = 0; hp->h_addr_list[i]; ++i)
544 struct in_addr *haddr = (struct in_addr *) hp->h_addr_list[i];
546 for (j = 0; j < num_ifs_local; ++j)
548 u_int32_t if_addr = ifaddrs[j].u.ipv4.addr;
549 u_int32_t if_netmask = ifaddrs[j].u.ipv4.mask;
551 if (((haddr->s_addr ^ if_addr) & if_netmask) == 0)
553 void *tmp;
555 tmp = hp->h_addr_list[i];
556 hp->h_addr_list[i] = hp->h_addr_list[0];
557 hp->h_addr_list[0] = tmp;
558 return;
562 #endif /* defined(SIOCGIFCONF) && ... */
566 /* If HOSTNAME has a postfix matching any of the trimdomains, trim away
567 that postfix. Notice that HOSTNAME is modified inplace. Also, the
568 original code applied all trimdomains in order, meaning that the
569 same domainname could be trimmed multiple times. I believe this
570 was unintentional. */
571 void
572 _res_hconf_trim_domain (char *hostname)
574 size_t hostname_len, trim_len;
575 int i;
577 hostname_len = strlen (hostname);
579 for (i = 0; i < _res_hconf.num_trimdomains; ++i)
581 const char *trim = _res_hconf.trimdomain[i];
583 trim_len = strlen (trim);
584 if (hostname_len > trim_len
585 && __strcasecmp (&hostname[hostname_len - trim_len], trim) == 0)
587 hostname[hostname_len - trim_len] = '\0';
588 break;
594 /* Trim all hostnames/aliases in HP according to the trimdomain list.
595 Notice that HP is modified inplace! */
596 void
597 _res_hconf_trim_domains (struct hostent *hp)
599 int i;
601 if (_res_hconf.num_trimdomains == 0)
602 return;
604 _res_hconf_trim_domain (hp->h_name);
605 for (i = 0; hp->h_aliases[i]; ++i)
606 _res_hconf_trim_domain (hp->h_aliases[i]);
608 #endif