Update.
[glibc.git] / resolv / res_hconf.c
blobb8137990ff1e2a93c98528e5ebab500a11f0dd5a
1 /* Copyright (C) 1993,95,96,97,98,99,2000,2001 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 <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 <bits/libc-lock.h>
45 #include "ifreq.h"
46 #include "res_hconf.h"
47 #ifdef USE_IN_LIBIO
48 # include <wchar.h>
49 #endif
51 #define _PATH_HOSTCONF "/etc/host.conf"
53 /* Environment vars that all user to override default behavior: */
54 #define ENV_HOSTCONF "RESOLV_HOST_CONF"
55 #define ENV_SERVORDER "RESOLV_SERV_ORDER"
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 static const char *arg_service_list (const char *, int, const char *,
63 unsigned int);
64 static const char *arg_trimdomain_list (const char *, int, const char *,
65 unsigned int);
66 static const char *arg_spoof (const char *, int, const char *, unsigned int);
67 static const char *arg_bool (const char *, int, const char *, unsigned int);
69 static struct cmd
71 const char *name;
72 const char *(*parse_args) (const char * filename, int line_num,
73 const char * args, unsigned int arg);
74 unsigned int arg;
75 } cmd[] =
77 {"order", arg_service_list, 0},
78 {"trim", arg_trimdomain_list, 0},
79 {"spoof", arg_spoof, 0},
80 {"multi", arg_bool, HCONF_FLAG_MULTI},
81 {"nospoof", arg_bool, HCONF_FLAG_SPOOF},
82 {"spoofalert", arg_bool, HCONF_FLAG_SPOOFALERT},
83 {"reorder", 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_service_list (const char *fname, int line_num, const char *args,
110 unsigned int arg)
112 enum Name_Service service;
113 const char *start;
114 size_t len;
115 int i;
116 static struct
118 const char * name;
119 enum Name_Service service;
120 } svcs[] =
122 {"bind", SERVICE_BIND},
123 {"hosts", SERVICE_HOSTS},
124 {"nis", SERVICE_NIS},
129 start = args;
130 args = skip_string (args);
131 len = args - start;
133 service = SERVICE_NONE;
134 for (i = 0; i < sizeof (svcs) / sizeof (svcs[0]); ++i)
136 if (__strncasecmp (start, svcs[i].name, len) == 0
137 && len == strlen (svcs[i].name))
139 service = svcs[i].service;
140 break;
143 if (service == SERVICE_NONE)
145 char *buf;
147 __asprintf (&buf, _("%s: line %d: expected service, found `%s'\n"),
148 fname, line_num, start);
150 #ifdef USE_IN_LIBIO
151 if (_IO_fwide (stderr, 0) > 0)
152 __fwprintf (stderr, L"%s", buf);
153 else
154 #endif
155 fputs (buf, stderr);
157 free (buf);
158 return 0;
160 if (_res_hconf.num_services >= SERVICE_MAX)
162 char *buf;
164 __asprintf (&buf,
165 _("%s: line %d: cannot specify more than %d services"),
166 fname, line_num, SERVICE_MAX);
168 #ifdef USE_IN_LIBIO
169 if (_IO_fwide (stderr, 0) > 0)
170 __fwprintf (stderr, L"%s", buf);
171 else
172 #endif
173 fputs (buf, stderr);
175 free (buf);
176 return 0;
178 _res_hconf.service[_res_hconf.num_services++] = service;
180 args = skip_ws (args);
181 switch (*args)
183 case ',':
184 case ';':
185 case ':':
186 args = skip_ws (++args);
187 if (!*args || *args == '#')
189 char *buf;
191 __asprintf (&buf, _("\
192 %s: line %d: list delimiter not followed by keyword"),
193 fname, line_num);
195 #ifdef USE_IN_LIBIO
196 if (_IO_fwide (stderr, 0) > 0)
197 __fwprintf (stderr, L"%s", buf);
198 else
199 #endif
200 fputs (buf, stderr);
202 free (buf);
203 return 0;
205 default:
206 break;
209 while (*args && *args != '#');
210 return args;
214 static const char *
215 arg_trimdomain_list (const char *fname, int line_num, const char *args,
216 unsigned int flag)
218 const char * start;
219 size_t len;
223 start = args;
224 args = skip_string (args);
225 len = args - start;
227 if (_res_hconf.num_trimdomains >= TRIMDOMAINS_MAX)
229 char *buf;
231 __asprintf (&buf, _("\
232 %s: line %d: cannot specify more than %d trim domains"),
233 fname, line_num, TRIMDOMAINS_MAX);
235 #ifdef USE_IN_LIBIO
236 if (_IO_fwide (stderr, 0) > 0)
237 __fwprintf (stderr, L"%s", buf);
238 else
239 #endif
240 fputs (buf, stderr);
242 free (buf);
243 return 0;
245 _res_hconf.trimdomain[_res_hconf.num_trimdomains++] =
246 __strndup (start, len);
247 args = skip_ws (args);
248 switch (*args)
250 case ',': case ';': case ':':
251 args = skip_ws (++args);
252 if (!*args || *args == '#')
254 char *buf;
256 __asprintf (&buf, _("\
257 %s: line %d: list delimiter not followed by domain"),
258 fname, line_num);
260 #ifdef USE_IN_LIBIO
261 if (_IO_fwide (stderr, 0) > 0)
262 __fwprintf (stderr, L"%s", buf);
263 else
264 #endif
265 fputs (buf, stderr);
267 free (buf);
268 return 0;
270 default:
271 break;
274 while (*args && *args != '#');
275 return args;
279 static const char *
280 arg_spoof (const char *fname, int line_num, const char *args, unsigned flag)
282 const char *start = args;
283 size_t len;
285 args = skip_string (args);
286 len = args - start;
288 if (len == 3 && __strncasecmp (start, "off", len) == 0)
289 _res_hconf.flags &= ~(HCONF_FLAG_SPOOF | HCONF_FLAG_SPOOFALERT);
290 else
292 _res_hconf.flags |= (HCONF_FLAG_SPOOF | HCONF_FLAG_SPOOFALERT);
293 if ((len == 6 && __strncasecmp (start, "nowarn", len) == 0)
294 || !(len == 4 && __strncasecmp (start, "warn", len) == 0))
295 _res_hconf.flags &= ~HCONF_FLAG_SPOOFALERT;
297 return args;
301 static const char *
302 arg_bool (const char *fname, int line_num, const char *args, unsigned flag)
304 if (__strncasecmp (args, "on", 2) == 0)
306 args += 2;
307 _res_hconf.flags |= flag;
309 else if (__strncasecmp (args, "off", 3) == 0)
311 args += 3;
312 _res_hconf.flags &= ~flag;
314 else
316 char *buf;
318 __asprintf (&buf,
319 _("%s: line %d: expected `on' or `off', found `%s'\n"),
320 fname, line_num, args);
322 #ifdef USE_IN_LIBIO
323 if (_IO_fwide (stderr, 0) > 0)
324 __fwprintf (stderr, L"%s", buf);
325 else
326 #endif
327 fputs (buf, stderr);
329 free (buf);
330 return 0;
332 return args;
336 static void
337 parse_line (const char *fname, int line_num, const char *str)
339 const char *start;
340 struct cmd *c = 0;
341 size_t len;
342 int i;
344 str = skip_ws (str);
346 /* skip line comment and empty lines: */
347 if (*str == '\0' || *str == '#') return;
349 start = str;
350 str = skip_string (str);
351 len = str - start;
353 for (i = 0; i < sizeof (cmd) / sizeof (cmd[0]); ++i)
355 if (__strncasecmp (start, cmd[i].name, len) == 0
356 && strlen (cmd[i].name) == len)
358 c = &cmd[i];
359 break;
362 if (c == NULL)
364 char *buf;
366 __asprintf (&buf, _("%s: line %d: bad command `%s'\n"),
367 fname, line_num, start);
369 #ifdef USE_IN_LIBIO
370 if (_IO_fwide (stderr, 0) > 0)
371 __fwprintf (stderr, L"%s", buf);
372 else
373 #endif
374 fputs (buf, stderr);
376 free (buf);
377 return;
380 /* process args: */
381 str = skip_ws (str);
382 str = (*c->parse_args) (fname, line_num, str, c->arg);
383 if (!str)
384 return;
386 /* rest of line must contain white space or comment only: */
387 while (*str)
389 if (!isspace (*str)) {
390 if (*str != '#')
392 char *buf;
394 __asprintf (&buf,
395 _("%s: line %d: ignoring trailing garbage `%s'\n"),
396 fname, line_num, str);
398 #ifdef USE_IN_LIBIO
399 if (_IO_fwide (stderr, 0) > 0)
400 __fwprintf (stderr, L"%s", buf);
401 else
402 #endif
403 fputs (buf, stderr);
405 free (buf);
407 break;
409 ++str;
414 /* Initialize hconf datastructure by reading host.conf file and
415 environment variables. */
416 void
417 _res_hconf_init (void)
419 const char *hconf_name;
420 int line_num = 0;
421 char buf[256], *envval;
422 FILE *fp;
424 if (_res_hconf.initialized)
425 return;
427 memset (&_res_hconf, '\0', sizeof (_res_hconf));
429 hconf_name = getenv (ENV_HOSTCONF);
430 if (hconf_name == NULL)
431 hconf_name = _PATH_HOSTCONF;
433 fp = fopen (hconf_name, "r");
434 if (!fp)
435 /* make up something reasonable: */
436 _res_hconf.service[_res_hconf.num_services++] = SERVICE_BIND;
437 else
439 /* No threads using this stream. */
440 __fsetlocking (fp, FSETLOCKING_BYCALLER);
442 while (fgets_unlocked (buf, sizeof (buf), fp))
444 ++line_num;
445 *__strchrnul (buf, '\n') = '\0';
446 parse_line (hconf_name, line_num, buf);
448 fclose (fp);
451 envval = getenv (ENV_SERVORDER);
452 if (envval)
454 _res_hconf.num_services = 0;
455 arg_service_list (ENV_SERVORDER, 1, envval, 0);
458 envval = getenv (ENV_SPOOF);
459 if (envval)
460 arg_spoof (ENV_SPOOF, 1, envval, 0);
462 envval = getenv (ENV_MULTI);
463 if (envval)
464 arg_bool (ENV_MULTI, 1, envval, HCONF_FLAG_MULTI);
466 envval = getenv (ENV_REORDER);
467 if (envval)
468 arg_bool (ENV_REORDER, 1, envval, HCONF_FLAG_REORDER);
470 envval = getenv (ENV_TRIM_ADD);
471 if (envval)
472 arg_trimdomain_list (ENV_TRIM_ADD, 1, envval, 0);
474 envval = getenv (ENV_TRIM_OVERR);
475 if (envval)
477 _res_hconf.num_trimdomains = 0;
478 arg_trimdomain_list (ENV_TRIM_OVERR, 1, envval, 0);
481 _res_hconf.initialized = 1;
485 /* List of known interfaces. */
486 static struct netaddr
488 int addrtype;
489 union
491 struct
493 u_int32_t addr;
494 u_int32_t mask;
495 } ipv4;
496 } u;
497 } *ifaddrs;
499 /* We need to protect the dynamic buffer handling. */
500 __libc_lock_define_initialized (static, lock);
502 /* Reorder addresses returned in a hostent such that the first address
503 is an address on the local subnet, if there is such an address.
504 Otherwise, nothing is changed.
506 Note that this function currently only handles IPv4 addresses. */
508 void
509 _res_hconf_reorder_addrs (struct hostent *hp)
511 #if defined SIOCGIFCONF && defined SIOCGIFNETMASK
512 int i, j;
513 /* Number of interfaces. */
514 static int num_ifs = -1;
516 /* Only reorder if we're supposed to. */
517 if ((_res_hconf.flags & HCONF_FLAG_REORDER) == 0)
518 return;
520 /* Can't deal with anything but IPv4 for now... */
521 if (hp->h_addrtype != AF_INET)
522 return;
524 if (num_ifs <= 0)
526 struct ifreq *ifr, *cur_ifr;
527 int sd, num, i;
528 /* Save errno. */
529 int save = errno;
531 /* Initialize interface table. */
533 num_ifs = 0;
535 sd = __opensock ();
536 if (sd < 0)
537 return;
539 /* Get lock. */
540 __libc_lock_lock (lock);
542 /* Get a list of interfaces. */
543 __ifreq (&ifr, &num);
544 if (!ifr)
545 goto cleanup;
547 ifaddrs = malloc (num * sizeof (ifaddrs[0]));
548 if (!ifaddrs)
549 goto cleanup1;
551 /* Copy usable interfaces in ifaddrs structure. */
552 for (cur_ifr = ifr, i = 0; i < num; ++cur_ifr, ++i)
554 if (cur_ifr->ifr_addr.sa_family != AF_INET)
555 continue;
557 ifaddrs[num_ifs].addrtype = AF_INET;
558 ifaddrs[num_ifs].u.ipv4.addr =
559 ((struct sockaddr_in *) &cur_ifr->ifr_addr)->sin_addr.s_addr;
561 if (__ioctl (sd, SIOCGIFNETMASK, cur_ifr) < 0)
562 continue;
564 ifaddrs[num_ifs].u.ipv4.mask =
565 ((struct sockaddr_in *) &cur_ifr->ifr_netmask)->sin_addr.s_addr;
567 /* Now we're committed to this entry. */
568 ++num_ifs;
570 /* Just keep enough memory to hold all the interfaces we want. */
571 ifaddrs = realloc (ifaddrs, num_ifs * sizeof (ifaddrs[0]));
573 cleanup1:
574 __if_freereq (ifr);
576 cleanup:
577 /* Release lock, preserve error value, and close socket. */
578 save = errno;
579 __libc_lock_unlock (lock);
580 __close (sd);
583 if (num_ifs == 0)
584 return;
586 /* Find an address for which we have a direct connection. */
587 for (i = 0; hp->h_addr_list[i]; ++i)
589 struct in_addr *haddr = (struct in_addr *) hp->h_addr_list[i];
591 for (j = 0; j < num_ifs; ++j)
593 u_int32_t if_addr = ifaddrs[j].u.ipv4.addr;
594 u_int32_t if_netmask = ifaddrs[j].u.ipv4.mask;
596 if (((haddr->s_addr ^ if_addr) & if_netmask) == 0)
598 void *tmp;
600 tmp = hp->h_addr_list[i];
601 hp->h_addr_list[i] = hp->h_addr_list[0];
602 hp->h_addr_list[0] = tmp;
603 return;
607 #endif /* defined(SIOCGIFCONF) && ... */
611 /* If HOSTNAME has a postfix matching any of the trimdomains, trim away
612 that postfix. Notice that HOSTNAME is modified inplace. Also, the
613 original code applied all trimdomains in order, meaning that the
614 same domainname could be trimmed multiple times. I believe this
615 was unintentional. */
616 void
617 _res_hconf_trim_domain (char *hostname)
619 size_t hostname_len, trim_len;
620 int i;
622 hostname_len = strlen (hostname);
624 for (i = 0; i < _res_hconf.num_trimdomains; ++i)
626 const char *trim = _res_hconf.trimdomain[i];
628 trim_len = strlen (trim);
629 if (hostname_len > trim_len
630 && __strcasecmp (&hostname[hostname_len - trim_len], trim) == 0)
632 hostname[hostname_len - trim_len] = '\0';
633 break;
639 /* Trim all hostnames/aliases in HP according to the trimdomain list.
640 Notice that HP is modified inplace! */
641 void
642 _res_hconf_trim_domains (struct hostent *hp)
644 int i;
646 if (_res_hconf.num_trimdomains == 0)
647 return;
649 _res_hconf_trim_domain (hp->h_name);
650 for (i = 0; hp->h_aliases[i]; ++i)
651 _res_hconf_trim_domain (hp->h_aliases[i]);
655 /* Free all resources if necessary. */
656 static void __attribute__ ((unused))
657 free_mem (void)
659 free (ifaddrs);
662 text_set_element (__libc_subfreeres, free_mem);