2004-09-08 H.J. Lu <hongjiu.lu@intel.com>
[glibc.git] / resolv / res_hconf.c
blob91cd300482117e5500ea8515172e45bfd43ae443
1 /* Copyright (C) 1993, 1995-2003, 2004 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_SERVORDER "RESOLV_SERV_ORDER"
57 #define ENV_SPOOF "RESOLV_SPOOF_CHECK"
58 #define ENV_TRIM_OVERR "RESOLV_OVERRIDE_TRIM_DOMAINS"
59 #define ENV_TRIM_ADD "RESOLV_ADD_TRIM_DOMAINS"
60 #define ENV_MULTI "RESOLV_MULTI"
61 #define ENV_REORDER "RESOLV_REORDER"
63 static const char *arg_service_list (const char *, int, const char *,
64 unsigned int);
65 static const char *arg_trimdomain_list (const char *, int, const char *,
66 unsigned int);
67 static const char *arg_spoof (const char *, int, const char *, unsigned int);
68 static const char *arg_bool (const char *, int, const char *, unsigned int);
70 static struct cmd
72 const char *name;
73 const char *(*parse_args) (const char * filename, int line_num,
74 const char * args, unsigned int arg);
75 unsigned int arg;
76 } cmd[] =
78 {"order", arg_service_list, 0},
79 {"trim", arg_trimdomain_list, 0},
80 {"spoof", arg_spoof, 0},
81 {"multi", arg_bool, HCONF_FLAG_MULTI},
82 {"nospoof", arg_bool, HCONF_FLAG_SPOOF},
83 {"spoofalert", arg_bool, HCONF_FLAG_SPOOFALERT},
84 {"reorder", arg_bool, HCONF_FLAG_REORDER}
87 /* Structure containing the state. */
88 struct hconf _res_hconf;
90 /* Skip white space. */
91 static const char *
92 skip_ws (const char *str)
94 while (isspace (*str)) ++str;
95 return str;
99 /* Skip until whitespace, comma, end of line, or comment character. */
100 static const char *
101 skip_string (const char *str)
103 while (*str && !isspace (*str) && *str != '#' && *str != ',')
104 ++str;
105 return str;
109 static const char *
110 arg_service_list (const char *fname, int line_num, const char *args,
111 unsigned int arg)
113 enum Name_Service service;
114 const char *start;
115 size_t len;
116 size_t i;
117 static struct
119 const char * name;
120 enum Name_Service service;
121 } svcs[] =
123 {"bind", SERVICE_BIND},
124 {"hosts", SERVICE_HOSTS},
125 {"nis", SERVICE_NIS},
130 start = args;
131 args = skip_string (args);
132 len = args - start;
134 service = SERVICE_NONE;
135 for (i = 0; i < sizeof (svcs) / sizeof (svcs[0]); ++i)
137 if (__strncasecmp (start, svcs[i].name, len) == 0
138 && len == strlen (svcs[i].name))
140 service = svcs[i].service;
141 break;
144 if (service == SERVICE_NONE)
146 char *buf;
148 if (__asprintf (&buf,
149 _("%s: line %d: expected service, found `%s'\n"),
150 fname, line_num, start) < 0)
151 return 0;
153 #ifdef USE_IN_LIBIO
154 if (_IO_fwide (stderr, 0) > 0)
155 __fwprintf (stderr, L"%s", buf);
156 else
157 #endif
158 fputs (buf, stderr);
160 free (buf);
161 return 0;
163 if (_res_hconf.num_services >= SERVICE_MAX)
165 char *buf;
167 if (__asprintf (&buf, _("\
168 %s: line %d: cannot specify more than %d services"),
169 fname, line_num, SERVICE_MAX) < 0)
170 return 0;
172 #ifdef USE_IN_LIBIO
173 if (_IO_fwide (stderr, 0) > 0)
174 __fwprintf (stderr, L"%s", buf);
175 else
176 #endif
177 fputs (buf, stderr);
179 free (buf);
180 return 0;
182 _res_hconf.service[_res_hconf.num_services++] = service;
184 args = skip_ws (args);
185 switch (*args)
187 case ',':
188 case ';':
189 case ':':
190 args = skip_ws (++args);
191 if (!*args || *args == '#')
193 char *buf;
195 if (__asprintf (&buf, _("\
196 %s: line %d: list delimiter not followed by keyword"),
197 fname, line_num) < 0)
198 return 0;
200 #ifdef USE_IN_LIBIO
201 if (_IO_fwide (stderr, 0) > 0)
202 __fwprintf (stderr, L"%s", buf);
203 else
204 #endif
205 fputs (buf, stderr);
207 free (buf);
208 return 0;
210 default:
211 break;
214 while (*args && *args != '#');
215 return args;
219 static const char *
220 arg_trimdomain_list (const char *fname, int line_num, const char *args,
221 unsigned int flag)
223 const char * start;
224 size_t len;
228 start = args;
229 args = skip_string (args);
230 len = args - start;
232 if (_res_hconf.num_trimdomains >= TRIMDOMAINS_MAX)
234 char *buf;
236 if (__asprintf (&buf, _("\
237 %s: line %d: cannot specify more than %d trim domains"),
238 fname, line_num, TRIMDOMAINS_MAX) < 0)
239 return 0;
241 #ifdef USE_IN_LIBIO
242 if (_IO_fwide (stderr, 0) > 0)
243 __fwprintf (stderr, L"%s", buf);
244 else
245 #endif
246 fputs (buf, stderr);
248 free (buf);
249 return 0;
251 _res_hconf.trimdomain[_res_hconf.num_trimdomains++] =
252 __strndup (start, len);
253 args = skip_ws (args);
254 switch (*args)
256 case ',': case ';': case ':':
257 args = skip_ws (++args);
258 if (!*args || *args == '#')
260 char *buf;
262 if (__asprintf (&buf, _("\
263 %s: line %d: list delimiter not followed by domain"),
264 fname, line_num) < 0)
265 return 0;
267 #ifdef USE_IN_LIBIO
268 if (_IO_fwide (stderr, 0) > 0)
269 __fwprintf (stderr, L"%s", buf);
270 else
271 #endif
272 fputs (buf, stderr);
274 free (buf);
275 return 0;
277 default:
278 break;
281 while (*args && *args != '#');
282 return args;
286 static const char *
287 arg_spoof (const char *fname, int line_num, const char *args, unsigned flag)
289 const char *start = args;
290 size_t len;
292 args = skip_string (args);
293 len = args - start;
295 if (len == 3 && __strncasecmp (start, "off", len) == 0)
296 _res_hconf.flags &= ~(HCONF_FLAG_SPOOF | HCONF_FLAG_SPOOFALERT);
297 else
299 _res_hconf.flags |= (HCONF_FLAG_SPOOF | HCONF_FLAG_SPOOFALERT);
300 if ((len == 6 && __strncasecmp (start, "nowarn", len) == 0)
301 || !(len == 4 && __strncasecmp (start, "warn", len) == 0))
302 _res_hconf.flags &= ~HCONF_FLAG_SPOOFALERT;
304 return args;
308 static const char *
309 arg_bool (const char *fname, int line_num, const char *args, unsigned flag)
311 if (__strncasecmp (args, "on", 2) == 0)
313 args += 2;
314 _res_hconf.flags |= flag;
316 else if (__strncasecmp (args, "off", 3) == 0)
318 args += 3;
319 _res_hconf.flags &= ~flag;
321 else
323 char *buf;
325 if (__asprintf (&buf,
326 _("%s: line %d: expected `on' or `off', found `%s'\n"),
327 fname, line_num, args) < 0)
328 return 0;
330 #ifdef USE_IN_LIBIO
331 if (_IO_fwide (stderr, 0) > 0)
332 __fwprintf (stderr, L"%s", buf);
333 else
334 #endif
335 fputs (buf, stderr);
337 free (buf);
338 return 0;
340 return args;
344 static void
345 parse_line (const char *fname, int line_num, const char *str)
347 const char *start;
348 struct cmd *c = 0;
349 size_t len;
350 size_t i;
352 str = skip_ws (str);
354 /* skip line comment and empty lines: */
355 if (*str == '\0' || *str == '#') return;
357 start = str;
358 str = skip_string (str);
359 len = str - start;
361 for (i = 0; i < sizeof (cmd) / sizeof (cmd[0]); ++i)
363 if (__strncasecmp (start, cmd[i].name, len) == 0
364 && strlen (cmd[i].name) == len)
366 c = &cmd[i];
367 break;
370 if (c == NULL)
372 char *buf;
374 if (__asprintf (&buf, _("%s: line %d: bad command `%s'\n"),
375 fname, line_num, start) < 0)
376 return;
378 #ifdef USE_IN_LIBIO
379 if (_IO_fwide (stderr, 0) > 0)
380 __fwprintf (stderr, L"%s", buf);
381 else
382 #endif
383 fputs (buf, stderr);
385 free (buf);
386 return;
389 /* process args: */
390 str = skip_ws (str);
391 str = (*c->parse_args) (fname, line_num, str, c->arg);
392 if (!str)
393 return;
395 /* rest of line must contain white space or comment only: */
396 while (*str)
398 if (!isspace (*str)) {
399 if (*str != '#')
401 char *buf;
403 if (__asprintf (&buf,
404 _("%s: line %d: ignoring trailing garbage `%s'\n"),
405 fname, line_num, str) < 0)
406 break;
408 #ifdef USE_IN_LIBIO
409 if (_IO_fwide (stderr, 0) > 0)
410 __fwprintf (stderr, L"%s", buf);
411 else
412 #endif
413 fputs (buf, stderr);
415 free (buf);
417 break;
419 ++str;
424 static void
425 do_init (void)
427 const char *hconf_name;
428 int line_num = 0;
429 char buf[256], *envval;
430 FILE *fp;
432 memset (&_res_hconf, '\0', sizeof (_res_hconf));
434 hconf_name = getenv (ENV_HOSTCONF);
435 if (hconf_name == NULL)
436 hconf_name = _PATH_HOSTCONF;
438 fp = fopen (hconf_name, "rc");
439 if (!fp)
440 /* make up something reasonable: */
441 _res_hconf.service[_res_hconf.num_services++] = SERVICE_BIND;
442 else
444 /* No threads using this stream. */
445 __fsetlocking (fp, FSETLOCKING_BYCALLER);
447 while (fgets_unlocked (buf, sizeof (buf), fp))
449 ++line_num;
450 *__strchrnul (buf, '\n') = '\0';
451 parse_line (hconf_name, line_num, buf);
453 fclose (fp);
456 envval = getenv (ENV_SERVORDER);
457 if (envval)
459 _res_hconf.num_services = 0;
460 arg_service_list (ENV_SERVORDER, 1, envval, 0);
463 envval = getenv (ENV_SPOOF);
464 if (envval)
465 arg_spoof (ENV_SPOOF, 1, envval, 0);
467 envval = getenv (ENV_MULTI);
468 if (envval)
469 arg_bool (ENV_MULTI, 1, envval, HCONF_FLAG_MULTI);
471 envval = getenv (ENV_REORDER);
472 if (envval)
473 arg_bool (ENV_REORDER, 1, envval, HCONF_FLAG_REORDER);
475 envval = getenv (ENV_TRIM_ADD);
476 if (envval)
477 arg_trimdomain_list (ENV_TRIM_ADD, 1, envval, 0);
479 envval = getenv (ENV_TRIM_OVERR);
480 if (envval)
482 _res_hconf.num_trimdomains = 0;
483 arg_trimdomain_list (ENV_TRIM_OVERR, 1, envval, 0);
486 _res_hconf.initialized = 1;
490 /* Initialize hconf datastructure by reading host.conf file and
491 environment variables. */
492 void
493 _res_hconf_init (void)
495 __libc_once_define (static, once);
497 __libc_once (once, do_init);
501 /* List of known interfaces. */
502 libc_freeres_ptr (
503 static struct netaddr
505 int addrtype;
506 union
508 struct
510 u_int32_t addr;
511 u_int32_t mask;
512 } ipv4;
513 } u;
514 } *ifaddrs);
516 /* We need to protect the dynamic buffer handling. */
517 __libc_lock_define_initialized (static, lock);
519 /* Reorder addresses returned in a hostent such that the first address
520 is an address on the local subnet, if there is such an address.
521 Otherwise, nothing is changed.
523 Note that this function currently only handles IPv4 addresses. */
525 void
526 _res_hconf_reorder_addrs (struct hostent *hp)
528 #if defined SIOCGIFCONF && defined SIOCGIFNETMASK
529 int i, j;
530 /* Number of interfaces. */
531 static int num_ifs = -1;
533 /* Only reorder if we're supposed to. */
534 if ((_res_hconf.flags & HCONF_FLAG_REORDER) == 0)
535 return;
537 /* Can't deal with anything but IPv4 for now... */
538 if (hp->h_addrtype != AF_INET)
539 return;
541 if (num_ifs <= 0)
543 struct ifreq *ifr, *cur_ifr;
544 int sd, num, i;
545 /* Save errno. */
546 int save = errno;
548 /* Initialize interface table. */
550 num_ifs = 0;
552 /* The SIOCGIFNETMASK ioctl will only work on an AF_INET socket. */
553 sd = __socket (AF_INET, SOCK_DGRAM, 0);
554 if (sd < 0)
555 return;
557 /* Get lock. */
558 __libc_lock_lock (lock);
560 /* Get a list of interfaces. */
561 __ifreq (&ifr, &num, sd);
562 if (!ifr)
563 goto cleanup;
565 ifaddrs = malloc (num * sizeof (ifaddrs[0]));
566 if (!ifaddrs)
567 goto cleanup1;
569 /* Copy usable interfaces in ifaddrs structure. */
570 for (cur_ifr = ifr, i = 0; i < num; cur_ifr = __if_nextreq (cur_ifr), ++i)
572 if (cur_ifr->ifr_addr.sa_family != AF_INET)
573 continue;
575 ifaddrs[num_ifs].addrtype = AF_INET;
576 ifaddrs[num_ifs].u.ipv4.addr =
577 ((struct sockaddr_in *) &cur_ifr->ifr_addr)->sin_addr.s_addr;
579 if (__ioctl (sd, SIOCGIFNETMASK, cur_ifr) < 0)
580 continue;
582 ifaddrs[num_ifs].u.ipv4.mask =
583 ((struct sockaddr_in *) &cur_ifr->ifr_netmask)->sin_addr.s_addr;
585 /* Now we're committed to this entry. */
586 ++num_ifs;
588 /* Just keep enough memory to hold all the interfaces we want. */
589 ifaddrs = realloc (ifaddrs, num_ifs * sizeof (ifaddrs[0]));
590 assert (ifaddrs != NULL);
592 cleanup1:
593 __if_freereq (ifr, num);
595 cleanup:
596 /* Release lock, preserve error value, and close socket. */
597 save = errno;
598 __libc_lock_unlock (lock);
599 __close (sd);
602 if (num_ifs == 0)
603 return;
605 /* Find an address for which we have a direct connection. */
606 for (i = 0; hp->h_addr_list[i]; ++i)
608 struct in_addr *haddr = (struct in_addr *) hp->h_addr_list[i];
610 for (j = 0; j < num_ifs; ++j)
612 u_int32_t if_addr = ifaddrs[j].u.ipv4.addr;
613 u_int32_t if_netmask = ifaddrs[j].u.ipv4.mask;
615 if (((haddr->s_addr ^ if_addr) & if_netmask) == 0)
617 void *tmp;
619 tmp = hp->h_addr_list[i];
620 hp->h_addr_list[i] = hp->h_addr_list[0];
621 hp->h_addr_list[0] = tmp;
622 return;
626 #endif /* defined(SIOCGIFCONF) && ... */
630 /* If HOSTNAME has a postfix matching any of the trimdomains, trim away
631 that postfix. Notice that HOSTNAME is modified inplace. Also, the
632 original code applied all trimdomains in order, meaning that the
633 same domainname could be trimmed multiple times. I believe this
634 was unintentional. */
635 void
636 _res_hconf_trim_domain (char *hostname)
638 size_t hostname_len, trim_len;
639 int i;
641 hostname_len = strlen (hostname);
643 for (i = 0; i < _res_hconf.num_trimdomains; ++i)
645 const char *trim = _res_hconf.trimdomain[i];
647 trim_len = strlen (trim);
648 if (hostname_len > trim_len
649 && __strcasecmp (&hostname[hostname_len - trim_len], trim) == 0)
651 hostname[hostname_len - trim_len] = '\0';
652 break;
658 /* Trim all hostnames/aliases in HP according to the trimdomain list.
659 Notice that HP is modified inplace! */
660 void
661 _res_hconf_trim_domains (struct hostent *hp)
663 int i;
665 if (_res_hconf.num_trimdomains == 0)
666 return;
668 _res_hconf_trim_domain (hp->h_name);
669 for (i = 0; hp->h_aliases[i]; ++i)
670 _res_hconf_trim_domain (hp->h_aliases[i]);