[BZ #12724] libio: revert partial POSIX 2008 fclose support
[glibc.git] / resolv / res_hconf.c
blobd23c9b903574323814f25f3cc7d30beb1dfab4c6
1 /* Copyright (C) 1993,1995-2006,2007,2009,2011
2 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4 Contributed by David Mosberger (davidm@azstarnet.com).
6 The GNU C Library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
11 The GNU C Library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public
17 License along with the GNU C Library; if not, see
18 <http://www.gnu.org/licenses/>. */
20 /* This file provides a Linux /etc/host.conf compatible front end to
21 the various name resolvers (/etc/hosts, named, NIS server, etc.).
22 Though mostly compatibly, the following differences exist compared
23 to the original implementation:
25 - new command "spoof" takes an arguments like RESOLV_SPOOF_CHECK
26 environment variable (i.e., `off', `nowarn', or `warn').
28 - line comments can appear anywhere (not just at the beginning of
29 a line)
32 #include <assert.h>
33 #include <errno.h>
34 #include <ctype.h>
35 #include <libintl.h>
36 #include <memory.h>
37 #include <stdio.h>
38 #include <stdio_ext.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <net/if.h>
42 #include <sys/ioctl.h>
43 #include <unistd.h>
44 #include <netinet/in.h>
45 #include <bits/libc-lock.h>
46 #include "ifreq.h"
47 #include "res_hconf.h"
48 #include <wchar.h>
50 #define _PATH_HOSTCONF "/etc/host.conf"
52 /* Environment vars that all user to override default behavior: */
53 #define ENV_HOSTCONF "RESOLV_HOST_CONF"
54 #define ENV_SPOOF "RESOLV_SPOOF_CHECK"
55 #define ENV_TRIM_OVERR "RESOLV_OVERRIDE_TRIM_DOMAINS"
56 #define ENV_TRIM_ADD "RESOLV_ADD_TRIM_DOMAINS"
57 #define ENV_MULTI "RESOLV_MULTI"
58 #define ENV_REORDER "RESOLV_REORDER"
60 enum parse_cbs
62 CB_none,
63 CB_arg_trimdomain_list,
64 CB_arg_spoof,
65 CB_arg_bool
68 static const struct cmd
70 const char name[11];
71 uint8_t cb;
72 unsigned int arg;
73 } cmd[] =
75 {"order", CB_none, 0},
76 {"trim", CB_arg_trimdomain_list, 0},
77 {"spoof", CB_arg_spoof, 0},
78 {"multi", CB_arg_bool, HCONF_FLAG_MULTI},
79 {"nospoof", CB_arg_bool, HCONF_FLAG_SPOOF},
80 {"spoofalert", CB_arg_bool, HCONF_FLAG_SPOOFALERT},
81 {"reorder", CB_arg_bool, HCONF_FLAG_REORDER}
84 /* Structure containing the state. */
85 struct hconf _res_hconf;
87 /* Skip white space. */
88 static const char *
89 skip_ws (const char *str)
91 while (isspace (*str)) ++str;
92 return str;
96 /* Skip until whitespace, comma, end of line, or comment character. */
97 static const char *
98 skip_string (const char *str)
100 while (*str && !isspace (*str) && *str != '#' && *str != ',')
101 ++str;
102 return str;
106 static const char *
107 arg_trimdomain_list (const char *fname, int line_num, const char *args)
109 const char * start;
110 size_t len;
114 start = args;
115 args = skip_string (args);
116 len = args - start;
118 if (_res_hconf.num_trimdomains >= TRIMDOMAINS_MAX)
120 char *buf;
122 if (__asprintf (&buf, _("\
123 %s: line %d: cannot specify more than %d trim domains"),
124 fname, line_num, TRIMDOMAINS_MAX) < 0)
125 return 0;
127 __fxprintf (NULL, "%s", buf);
129 free (buf);
130 return 0;
132 _res_hconf.trimdomain[_res_hconf.num_trimdomains++] =
133 __strndup (start, len);
134 args = skip_ws (args);
135 switch (*args)
137 case ',': case ';': case ':':
138 args = skip_ws (++args);
139 if (!*args || *args == '#')
141 char *buf;
143 if (__asprintf (&buf, _("\
144 %s: line %d: list delimiter not followed by domain"),
145 fname, line_num) < 0)
146 return 0;
148 __fxprintf (NULL, "%s", buf);
150 free (buf);
151 return 0;
153 default:
154 break;
157 while (*args && *args != '#');
158 return args;
162 static const char *
163 arg_spoof (const char *fname, int line_num, const char *args)
165 const char *start = args;
166 size_t len;
168 args = skip_string (args);
169 len = args - start;
171 if (len == 3 && __strncasecmp (start, "off", len) == 0)
172 _res_hconf.flags &= ~(HCONF_FLAG_SPOOF | HCONF_FLAG_SPOOFALERT);
173 else
175 _res_hconf.flags |= (HCONF_FLAG_SPOOF | HCONF_FLAG_SPOOFALERT);
176 if ((len == 6 && __strncasecmp (start, "nowarn", len) == 0)
177 || !(len == 4 && __strncasecmp (start, "warn", len) == 0))
178 _res_hconf.flags &= ~HCONF_FLAG_SPOOFALERT;
180 return args;
184 static const char *
185 arg_bool (const char *fname, int line_num, const char *args, unsigned flag)
187 if (__strncasecmp (args, "on", 2) == 0)
189 args += 2;
190 _res_hconf.flags |= flag;
192 else if (__strncasecmp (args, "off", 3) == 0)
194 args += 3;
195 _res_hconf.flags &= ~flag;
197 else
199 char *buf;
201 if (__asprintf (&buf,
202 _("%s: line %d: expected `on' or `off', found `%s'\n"),
203 fname, line_num, args) < 0)
204 return 0;
206 __fxprintf (NULL, "%s", buf);
208 free (buf);
209 return 0;
211 return args;
215 static void
216 parse_line (const char *fname, int line_num, const char *str)
218 const char *start;
219 const struct cmd *c = 0;
220 size_t len;
221 size_t i;
223 str = skip_ws (str);
225 /* skip line comment and empty lines: */
226 if (*str == '\0' || *str == '#') return;
228 start = str;
229 str = skip_string (str);
230 len = str - start;
232 for (i = 0; i < sizeof (cmd) / sizeof (cmd[0]); ++i)
234 if (__strncasecmp (start, cmd[i].name, len) == 0
235 && strlen (cmd[i].name) == len)
237 c = &cmd[i];
238 break;
241 if (c == NULL)
243 char *buf;
245 if (__asprintf (&buf, _("%s: line %d: bad command `%s'\n"),
246 fname, line_num, start) < 0)
247 return;
249 __fxprintf (NULL, "%s", buf);
251 free (buf);
252 return;
255 /* process args: */
256 str = skip_ws (str);
258 if (c->cb == CB_arg_trimdomain_list)
259 str = arg_trimdomain_list (fname, line_num, str);
260 else if (c->cb == CB_arg_spoof)
261 str = arg_spoof (fname, line_num, str);
262 else if (c->cb == CB_arg_bool)
263 str = arg_bool (fname, line_num, str, c->arg);
264 else
265 /* Ignore the line. */
266 return;
268 if (!str)
269 return;
271 /* rest of line must contain white space or comment only: */
272 while (*str)
274 if (!isspace (*str)) {
275 if (*str != '#')
277 char *buf;
279 if (__asprintf (&buf,
280 _("%s: line %d: ignoring trailing garbage `%s'\n"),
281 fname, line_num, str) < 0)
282 break;
284 __fxprintf (NULL, "%s", buf);
286 free (buf);
288 break;
290 ++str;
295 static void
296 do_init (void)
298 const char *hconf_name;
299 int line_num = 0;
300 char buf[256], *envval;
301 FILE *fp;
303 memset (&_res_hconf, '\0', sizeof (_res_hconf));
305 hconf_name = getenv (ENV_HOSTCONF);
306 if (hconf_name == NULL)
307 hconf_name = _PATH_HOSTCONF;
309 fp = fopen (hconf_name, "rce");
310 if (fp)
312 /* No threads using this stream. */
313 __fsetlocking (fp, FSETLOCKING_BYCALLER);
315 while (fgets_unlocked (buf, sizeof (buf), fp))
317 ++line_num;
318 *__strchrnul (buf, '\n') = '\0';
319 parse_line (hconf_name, line_num, buf);
321 fclose (fp);
324 envval = getenv (ENV_SPOOF);
325 if (envval)
326 arg_spoof (ENV_SPOOF, 1, envval);
328 envval = getenv (ENV_MULTI);
329 if (envval)
330 arg_bool (ENV_MULTI, 1, envval, HCONF_FLAG_MULTI);
332 envval = getenv (ENV_REORDER);
333 if (envval)
334 arg_bool (ENV_REORDER, 1, envval, HCONF_FLAG_REORDER);
336 envval = getenv (ENV_TRIM_ADD);
337 if (envval)
338 arg_trimdomain_list (ENV_TRIM_ADD, 1, envval);
340 envval = getenv (ENV_TRIM_OVERR);
341 if (envval)
343 _res_hconf.num_trimdomains = 0;
344 arg_trimdomain_list (ENV_TRIM_OVERR, 1, envval);
347 _res_hconf.initialized = 1;
351 /* Initialize hconf datastructure by reading host.conf file and
352 environment variables. */
353 void
354 _res_hconf_init (void)
356 __libc_once_define (static, once);
358 __libc_once (once, do_init);
362 #ifndef NOT_IN_libc
363 /* List of known interfaces. */
364 libc_freeres_ptr (
365 static struct netaddr
367 int addrtype;
368 union
370 struct
372 u_int32_t addr;
373 u_int32_t mask;
374 } ipv4;
375 } u;
376 } *ifaddrs);
378 /* Reorder addresses returned in a hostent such that the first address
379 is an address on the local subnet, if there is such an address.
380 Otherwise, nothing is changed.
382 Note that this function currently only handles IPv4 addresses. */
384 void
385 _res_hconf_reorder_addrs (struct hostent *hp)
387 #if defined SIOCGIFCONF && defined SIOCGIFNETMASK
388 int i, j;
389 /* Number of interfaces. */
390 static int num_ifs = -1;
391 /* We need to protect the dynamic buffer handling. */
392 __libc_lock_define_initialized (static, lock);
394 /* Only reorder if we're supposed to. */
395 if ((_res_hconf.flags & HCONF_FLAG_REORDER) == 0)
396 return;
398 /* Can't deal with anything but IPv4 for now... */
399 if (hp->h_addrtype != AF_INET)
400 return;
402 if (num_ifs <= 0)
404 struct ifreq *ifr, *cur_ifr;
405 int sd, num, i;
406 /* Save errno. */
407 int save = errno;
409 /* Initialize interface table. */
411 /* The SIOCGIFNETMASK ioctl will only work on an AF_INET socket. */
412 sd = __socket (AF_INET, SOCK_DGRAM, 0);
413 if (sd < 0)
414 return;
416 /* Get lock. */
417 __libc_lock_lock (lock);
419 /* Recheck, somebody else might have done the work by done. */
420 if (num_ifs <= 0)
422 int new_num_ifs = 0;
424 /* Get a list of interfaces. */
425 __ifreq (&ifr, &num, sd);
426 if (!ifr)
427 goto cleanup;
429 ifaddrs = malloc (num * sizeof (ifaddrs[0]));
430 if (!ifaddrs)
431 goto cleanup1;
433 /* Copy usable interfaces in ifaddrs structure. */
434 for (cur_ifr = ifr, i = 0; i < num;
435 cur_ifr = __if_nextreq (cur_ifr), ++i)
437 if (cur_ifr->ifr_addr.sa_family != AF_INET)
438 continue;
440 ifaddrs[new_num_ifs].addrtype = AF_INET;
441 ifaddrs[new_num_ifs].u.ipv4.addr =
442 ((struct sockaddr_in *) &cur_ifr->ifr_addr)->sin_addr.s_addr;
444 if (__ioctl (sd, SIOCGIFNETMASK, cur_ifr) < 0)
445 continue;
447 ifaddrs[new_num_ifs].u.ipv4.mask =
448 ((struct sockaddr_in *) &cur_ifr->ifr_netmask)->sin_addr.s_addr;
450 /* Now we're committed to this entry. */
451 ++new_num_ifs;
453 /* Just keep enough memory to hold all the interfaces we want. */
454 ifaddrs = realloc (ifaddrs, new_num_ifs * sizeof (ifaddrs[0]));
455 assert (ifaddrs != NULL);
457 cleanup1:
458 __if_freereq (ifr, num);
460 cleanup:
461 /* Release lock, preserve error value, and close socket. */
462 errno = save;
464 num_ifs = new_num_ifs;
466 __libc_lock_unlock (lock);
469 __close (sd);
472 if (num_ifs == 0)
473 return;
475 /* Find an address for which we have a direct connection. */
476 for (i = 0; hp->h_addr_list[i]; ++i)
478 struct in_addr *haddr = (struct in_addr *) hp->h_addr_list[i];
480 for (j = 0; j < num_ifs; ++j)
482 u_int32_t if_addr = ifaddrs[j].u.ipv4.addr;
483 u_int32_t if_netmask = ifaddrs[j].u.ipv4.mask;
485 if (((haddr->s_addr ^ if_addr) & if_netmask) == 0)
487 void *tmp;
489 tmp = hp->h_addr_list[i];
490 hp->h_addr_list[i] = hp->h_addr_list[0];
491 hp->h_addr_list[0] = tmp;
492 return;
496 #endif /* defined(SIOCGIFCONF) && ... */
500 /* If HOSTNAME has a postfix matching any of the trimdomains, trim away
501 that postfix. Notice that HOSTNAME is modified inplace. Also, the
502 original code applied all trimdomains in order, meaning that the
503 same domainname could be trimmed multiple times. I believe this
504 was unintentional. */
505 void
506 _res_hconf_trim_domain (char *hostname)
508 size_t hostname_len, trim_len;
509 int i;
511 hostname_len = strlen (hostname);
513 for (i = 0; i < _res_hconf.num_trimdomains; ++i)
515 const char *trim = _res_hconf.trimdomain[i];
517 trim_len = strlen (trim);
518 if (hostname_len > trim_len
519 && __strcasecmp (&hostname[hostname_len - trim_len], trim) == 0)
521 hostname[hostname_len - trim_len] = '\0';
522 break;
528 /* Trim all hostnames/aliases in HP according to the trimdomain list.
529 Notice that HP is modified inplace! */
530 void
531 _res_hconf_trim_domains (struct hostent *hp)
533 int i;
535 if (_res_hconf.num_trimdomains == 0)
536 return;
538 _res_hconf_trim_domain (hp->h_name);
539 for (i = 0; hp->h_aliases[i]; ++i)
540 _res_hconf_trim_domain (hp->h_aliases[i]);
542 #endif