Don't crash when dependencies are missing
[glibc.git] / resolv / res_hconf.c
blobed55bec29655079502f4519d6f940079091a1c6d
1 /* Copyright (C) 1993, 1995-2006, 2007, 2009 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, write to the Free
17 Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
18 02111-1307 USA. */
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 #ifdef USE_IN_LIBIO
49 # include <wchar.h>
50 #endif
52 #define _PATH_HOSTCONF "/etc/host.conf"
54 /* Environment vars that all user to override default behavior: */
55 #define ENV_HOSTCONF "RESOLV_HOST_CONF"
56 #define ENV_SPOOF "RESOLV_SPOOF_CHECK"
57 #define ENV_TRIM_OVERR "RESOLV_OVERRIDE_TRIM_DOMAINS"
58 #define ENV_TRIM_ADD "RESOLV_ADD_TRIM_DOMAINS"
59 #define ENV_MULTI "RESOLV_MULTI"
60 #define ENV_REORDER "RESOLV_REORDER"
62 enum parse_cbs
64 CB_none,
65 CB_arg_trimdomain_list,
66 CB_arg_spoof,
67 CB_arg_bool
70 static const struct cmd
72 const char name[11];
73 uint8_t cb;
74 unsigned int arg;
75 } cmd[] =
77 {"order", CB_none, 0},
78 {"trim", CB_arg_trimdomain_list, 0},
79 {"spoof", CB_arg_spoof, 0},
80 {"multi", CB_arg_bool, HCONF_FLAG_MULTI},
81 {"nospoof", CB_arg_bool, HCONF_FLAG_SPOOF},
82 {"spoofalert", CB_arg_bool, HCONF_FLAG_SPOOFALERT},
83 {"reorder", CB_arg_bool, HCONF_FLAG_REORDER}
86 /* Structure containing the state. */
87 struct hconf _res_hconf;
89 /* Skip white space. */
90 static const char *
91 skip_ws (const char *str)
93 while (isspace (*str)) ++str;
94 return str;
98 /* Skip until whitespace, comma, end of line, or comment character. */
99 static const char *
100 skip_string (const char *str)
102 while (*str && !isspace (*str) && *str != '#' && *str != ',')
103 ++str;
104 return str;
108 static const char *
109 arg_trimdomain_list (const char *fname, int line_num, const char *args)
111 const char * start;
112 size_t len;
116 start = args;
117 args = skip_string (args);
118 len = args - start;
120 if (_res_hconf.num_trimdomains >= TRIMDOMAINS_MAX)
122 char *buf;
124 if (__asprintf (&buf, _("\
125 %s: line %d: cannot specify more than %d trim domains"),
126 fname, line_num, TRIMDOMAINS_MAX) < 0)
127 return 0;
129 __fxprintf (NULL, "%s", buf);
131 free (buf);
132 return 0;
134 _res_hconf.trimdomain[_res_hconf.num_trimdomains++] =
135 __strndup (start, len);
136 args = skip_ws (args);
137 switch (*args)
139 case ',': case ';': case ':':
140 args = skip_ws (++args);
141 if (!*args || *args == '#')
143 char *buf;
145 if (__asprintf (&buf, _("\
146 %s: line %d: list delimiter not followed by domain"),
147 fname, line_num) < 0)
148 return 0;
150 __fxprintf (NULL, "%s", buf);
152 free (buf);
153 return 0;
155 default:
156 break;
159 while (*args && *args != '#');
160 return args;
164 static const char *
165 arg_spoof (const char *fname, int line_num, const char *args)
167 const char *start = args;
168 size_t len;
170 args = skip_string (args);
171 len = args - start;
173 if (len == 3 && __strncasecmp (start, "off", len) == 0)
174 _res_hconf.flags &= ~(HCONF_FLAG_SPOOF | HCONF_FLAG_SPOOFALERT);
175 else
177 _res_hconf.flags |= (HCONF_FLAG_SPOOF | HCONF_FLAG_SPOOFALERT);
178 if ((len == 6 && __strncasecmp (start, "nowarn", len) == 0)
179 || !(len == 4 && __strncasecmp (start, "warn", len) == 0))
180 _res_hconf.flags &= ~HCONF_FLAG_SPOOFALERT;
182 return args;
186 static const char *
187 arg_bool (const char *fname, int line_num, const char *args, unsigned flag)
189 if (__strncasecmp (args, "on", 2) == 0)
191 args += 2;
192 _res_hconf.flags |= flag;
194 else if (__strncasecmp (args, "off", 3) == 0)
196 args += 3;
197 _res_hconf.flags &= ~flag;
199 else
201 char *buf;
203 if (__asprintf (&buf,
204 _("%s: line %d: expected `on' or `off', found `%s'\n"),
205 fname, line_num, args) < 0)
206 return 0;
208 __fxprintf (NULL, "%s", buf);
210 free (buf);
211 return 0;
213 return args;
217 static void
218 parse_line (const char *fname, int line_num, const char *str)
220 const char *start;
221 const struct cmd *c = 0;
222 size_t len;
223 size_t i;
225 str = skip_ws (str);
227 /* skip line comment and empty lines: */
228 if (*str == '\0' || *str == '#') return;
230 start = str;
231 str = skip_string (str);
232 len = str - start;
234 for (i = 0; i < sizeof (cmd) / sizeof (cmd[0]); ++i)
236 if (__strncasecmp (start, cmd[i].name, len) == 0
237 && strlen (cmd[i].name) == len)
239 c = &cmd[i];
240 break;
243 if (c == NULL)
245 char *buf;
247 if (__asprintf (&buf, _("%s: line %d: bad command `%s'\n"),
248 fname, line_num, start) < 0)
249 return;
251 __fxprintf (NULL, "%s", buf);
253 free (buf);
254 return;
257 /* process args: */
258 str = skip_ws (str);
260 if (c->cb == CB_arg_trimdomain_list)
261 str = arg_trimdomain_list (fname, line_num, str);
262 else if (c->cb == CB_arg_spoof)
263 str = arg_spoof (fname, line_num, str);
264 else if (c->cb == CB_arg_bool)
265 str = arg_bool (fname, line_num, str, c->arg);
266 else
267 /* Ignore the line. */
268 return;
270 if (!str)
271 return;
273 /* rest of line must contain white space or comment only: */
274 while (*str)
276 if (!isspace (*str)) {
277 if (*str != '#')
279 char *buf;
281 if (__asprintf (&buf,
282 _("%s: line %d: ignoring trailing garbage `%s'\n"),
283 fname, line_num, str) < 0)
284 break;
286 __fxprintf (NULL, "%s", buf);
288 free (buf);
290 break;
292 ++str;
297 static void
298 do_init (void)
300 const char *hconf_name;
301 int line_num = 0;
302 char buf[256], *envval;
303 FILE *fp;
305 memset (&_res_hconf, '\0', sizeof (_res_hconf));
307 hconf_name = getenv (ENV_HOSTCONF);
308 if (hconf_name == NULL)
309 hconf_name = _PATH_HOSTCONF;
311 fp = fopen (hconf_name, "rc");
312 if (fp)
314 /* No threads using this stream. */
315 __fsetlocking (fp, FSETLOCKING_BYCALLER);
317 while (fgets_unlocked (buf, sizeof (buf), fp))
319 ++line_num;
320 *__strchrnul (buf, '\n') = '\0';
321 parse_line (hconf_name, line_num, buf);
323 fclose (fp);
326 envval = getenv (ENV_SPOOF);
327 if (envval)
328 arg_spoof (ENV_SPOOF, 1, envval);
330 envval = getenv (ENV_MULTI);
331 if (envval)
332 arg_bool (ENV_MULTI, 1, envval, HCONF_FLAG_MULTI);
334 envval = getenv (ENV_REORDER);
335 if (envval)
336 arg_bool (ENV_REORDER, 1, envval, HCONF_FLAG_REORDER);
338 envval = getenv (ENV_TRIM_ADD);
339 if (envval)
340 arg_trimdomain_list (ENV_TRIM_ADD, 1, envval);
342 envval = getenv (ENV_TRIM_OVERR);
343 if (envval)
345 _res_hconf.num_trimdomains = 0;
346 arg_trimdomain_list (ENV_TRIM_OVERR, 1, envval);
349 _res_hconf.initialized = 1;
353 /* Initialize hconf datastructure by reading host.conf file and
354 environment variables. */
355 void
356 _res_hconf_init (void)
358 __libc_once_define (static, once);
360 __libc_once (once, do_init);
364 #ifndef NOT_IN_libc
365 /* List of known interfaces. */
366 libc_freeres_ptr (
367 static struct netaddr
369 int addrtype;
370 union
372 struct
374 u_int32_t addr;
375 u_int32_t mask;
376 } ipv4;
377 } u;
378 } *ifaddrs);
380 /* Reorder addresses returned in a hostent such that the first address
381 is an address on the local subnet, if there is such an address.
382 Otherwise, nothing is changed.
384 Note that this function currently only handles IPv4 addresses. */
386 void
387 _res_hconf_reorder_addrs (struct hostent *hp)
389 #if defined SIOCGIFCONF && defined SIOCGIFNETMASK
390 int i, j;
391 /* Number of interfaces. */
392 static int num_ifs = -1;
393 /* We need to protect the dynamic buffer handling. */
394 __libc_lock_define_initialized (static, lock);
396 /* Only reorder if we're supposed to. */
397 if ((_res_hconf.flags & HCONF_FLAG_REORDER) == 0)
398 return;
400 /* Can't deal with anything but IPv4 for now... */
401 if (hp->h_addrtype != AF_INET)
402 return;
404 if (num_ifs <= 0)
406 struct ifreq *ifr, *cur_ifr;
407 int sd, num, i;
408 /* Save errno. */
409 int save = errno;
411 /* Initialize interface table. */
413 /* The SIOCGIFNETMASK ioctl will only work on an AF_INET socket. */
414 sd = __socket (AF_INET, SOCK_DGRAM, 0);
415 if (sd < 0)
416 return;
418 /* Get lock. */
419 __libc_lock_lock (lock);
421 /* Recheck, somebody else might have done the work by done. */
422 if (num_ifs <= 0)
424 int new_num_ifs = 0;
426 /* Get a list of interfaces. */
427 __ifreq (&ifr, &num, sd);
428 if (!ifr)
429 goto cleanup;
431 ifaddrs = malloc (num * sizeof (ifaddrs[0]));
432 if (!ifaddrs)
433 goto cleanup1;
435 /* Copy usable interfaces in ifaddrs structure. */
436 for (cur_ifr = ifr, i = 0; i < num;
437 cur_ifr = __if_nextreq (cur_ifr), ++i)
439 if (cur_ifr->ifr_addr.sa_family != AF_INET)
440 continue;
442 ifaddrs[new_num_ifs].addrtype = AF_INET;
443 ifaddrs[new_num_ifs].u.ipv4.addr =
444 ((struct sockaddr_in *) &cur_ifr->ifr_addr)->sin_addr.s_addr;
446 if (__ioctl (sd, SIOCGIFNETMASK, cur_ifr) < 0)
447 continue;
449 ifaddrs[new_num_ifs].u.ipv4.mask =
450 ((struct sockaddr_in *) &cur_ifr->ifr_netmask)->sin_addr.s_addr;
452 /* Now we're committed to this entry. */
453 ++new_num_ifs;
455 /* Just keep enough memory to hold all the interfaces we want. */
456 ifaddrs = realloc (ifaddrs, new_num_ifs * sizeof (ifaddrs[0]));
457 assert (ifaddrs != NULL);
459 cleanup1:
460 __if_freereq (ifr, num);
462 cleanup:
463 /* Release lock, preserve error value, and close socket. */
464 save = errno;
466 num_ifs = new_num_ifs;
468 __libc_lock_unlock (lock);
471 __close (sd);
474 if (num_ifs == 0)
475 return;
477 /* Find an address for which we have a direct connection. */
478 for (i = 0; hp->h_addr_list[i]; ++i)
480 struct in_addr *haddr = (struct in_addr *) hp->h_addr_list[i];
482 for (j = 0; j < num_ifs; ++j)
484 u_int32_t if_addr = ifaddrs[j].u.ipv4.addr;
485 u_int32_t if_netmask = ifaddrs[j].u.ipv4.mask;
487 if (((haddr->s_addr ^ if_addr) & if_netmask) == 0)
489 void *tmp;
491 tmp = hp->h_addr_list[i];
492 hp->h_addr_list[i] = hp->h_addr_list[0];
493 hp->h_addr_list[0] = tmp;
494 return;
498 #endif /* defined(SIOCGIFCONF) && ... */
502 /* If HOSTNAME has a postfix matching any of the trimdomains, trim away
503 that postfix. Notice that HOSTNAME is modified inplace. Also, the
504 original code applied all trimdomains in order, meaning that the
505 same domainname could be trimmed multiple times. I believe this
506 was unintentional. */
507 void
508 _res_hconf_trim_domain (char *hostname)
510 size_t hostname_len, trim_len;
511 int i;
513 hostname_len = strlen (hostname);
515 for (i = 0; i < _res_hconf.num_trimdomains; ++i)
517 const char *trim = _res_hconf.trimdomain[i];
519 trim_len = strlen (trim);
520 if (hostname_len > trim_len
521 && __strcasecmp (&hostname[hostname_len - trim_len], trim) == 0)
523 hostname[hostname_len - trim_len] = '\0';
524 break;
530 /* Trim all hostnames/aliases in HP according to the trimdomain list.
531 Notice that HP is modified inplace! */
532 void
533 _res_hconf_trim_domains (struct hostent *hp)
535 int i;
537 if (_res_hconf.num_trimdomains == 0)
538 return;
540 _res_hconf_trim_domain (hp->h_name);
541 for (i = 0; hp->h_aliases[i]; ++i)
542 _res_hconf_trim_domain (hp->h_aliases[i]);
544 #endif