1 /* Copyright (C) 1993, 1995-2001, 2002 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
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
37 #include <stdio_ext.h>
41 #include <sys/ioctl.h>
43 #include <netinet/in.h>
44 #include <bits/libc-lock.h>
46 #include "res_hconf.h"
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 *,
64 static const char *arg_trimdomain_list (const char *, int, const char *,
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);
72 const char *(*parse_args
) (const char * filename
, int line_num
,
73 const char * args
, unsigned int arg
);
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. */
91 skip_ws (const char *str
)
93 while (isspace (*str
)) ++str
;
98 /* Skip until whitespace, comma, end of line, or comment character. */
100 skip_string (const char *str
)
102 while (*str
&& !isspace (*str
) && *str
!= '#' && *str
!= ',')
109 arg_service_list (const char *fname
, int line_num
, const char *args
,
112 enum Name_Service service
;
119 enum Name_Service service
;
122 {"bind", SERVICE_BIND
},
123 {"hosts", SERVICE_HOSTS
},
124 {"nis", SERVICE_NIS
},
130 args
= skip_string (args
);
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
;
143 if (service
== SERVICE_NONE
)
147 __asprintf (&buf
, _("%s: line %d: expected service, found `%s'\n"),
148 fname
, line_num
, start
);
151 if (_IO_fwide (stderr
, 0) > 0)
152 __fwprintf (stderr
, L
"%s", buf
);
160 if (_res_hconf
.num_services
>= SERVICE_MAX
)
165 _("%s: line %d: cannot specify more than %d services"),
166 fname
, line_num
, SERVICE_MAX
);
169 if (_IO_fwide (stderr
, 0) > 0)
170 __fwprintf (stderr
, L
"%s", buf
);
178 _res_hconf
.service
[_res_hconf
.num_services
++] = service
;
180 args
= skip_ws (args
);
186 args
= skip_ws (++args
);
187 if (!*args
|| *args
== '#')
191 __asprintf (&buf
, _("\
192 %s: line %d: list delimiter not followed by keyword"),
196 if (_IO_fwide (stderr
, 0) > 0)
197 __fwprintf (stderr
, L
"%s", buf
);
209 while (*args
&& *args
!= '#');
215 arg_trimdomain_list (const char *fname
, int line_num
, const char *args
,
224 args
= skip_string (args
);
227 if (_res_hconf
.num_trimdomains
>= TRIMDOMAINS_MAX
)
231 __asprintf (&buf
, _("\
232 %s: line %d: cannot specify more than %d trim domains"),
233 fname
, line_num
, TRIMDOMAINS_MAX
);
236 if (_IO_fwide (stderr
, 0) > 0)
237 __fwprintf (stderr
, L
"%s", buf
);
245 _res_hconf
.trimdomain
[_res_hconf
.num_trimdomains
++] =
246 __strndup (start
, len
);
247 args
= skip_ws (args
);
250 case ',': case ';': case ':':
251 args
= skip_ws (++args
);
252 if (!*args
|| *args
== '#')
256 __asprintf (&buf
, _("\
257 %s: line %d: list delimiter not followed by domain"),
261 if (_IO_fwide (stderr
, 0) > 0)
262 __fwprintf (stderr
, L
"%s", buf
);
274 while (*args
&& *args
!= '#');
280 arg_spoof (const char *fname
, int line_num
, const char *args
, unsigned flag
)
282 const char *start
= args
;
285 args
= skip_string (args
);
288 if (len
== 3 && __strncasecmp (start
, "off", len
) == 0)
289 _res_hconf
.flags
&= ~(HCONF_FLAG_SPOOF
| HCONF_FLAG_SPOOFALERT
);
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
;
302 arg_bool (const char *fname
, int line_num
, const char *args
, unsigned flag
)
304 if (__strncasecmp (args
, "on", 2) == 0)
307 _res_hconf
.flags
|= flag
;
309 else if (__strncasecmp (args
, "off", 3) == 0)
312 _res_hconf
.flags
&= ~flag
;
319 _("%s: line %d: expected `on' or `off', found `%s'\n"),
320 fname
, line_num
, args
);
323 if (_IO_fwide (stderr
, 0) > 0)
324 __fwprintf (stderr
, L
"%s", buf
);
337 parse_line (const char *fname
, int line_num
, const char *str
)
346 /* skip line comment and empty lines: */
347 if (*str
== '\0' || *str
== '#') return;
350 str
= skip_string (str
);
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
)
366 __asprintf (&buf
, _("%s: line %d: bad command `%s'\n"),
367 fname
, line_num
, start
);
370 if (_IO_fwide (stderr
, 0) > 0)
371 __fwprintf (stderr
, L
"%s", buf
);
382 str
= (*c
->parse_args
) (fname
, line_num
, str
, c
->arg
);
386 /* rest of line must contain white space or comment only: */
389 if (!isspace (*str
)) {
395 _("%s: line %d: ignoring trailing garbage `%s'\n"),
396 fname
, line_num
, str
);
399 if (_IO_fwide (stderr
, 0) > 0)
400 __fwprintf (stderr
, L
"%s", buf
);
417 const char *hconf_name
;
419 char buf
[256], *envval
;
422 memset (&_res_hconf
, '\0', sizeof (_res_hconf
));
424 hconf_name
= getenv (ENV_HOSTCONF
);
425 if (hconf_name
== NULL
)
426 hconf_name
= _PATH_HOSTCONF
;
428 fp
= fopen (hconf_name
, "r");
430 /* make up something reasonable: */
431 _res_hconf
.service
[_res_hconf
.num_services
++] = SERVICE_BIND
;
434 /* No threads using this stream. */
435 __fsetlocking (fp
, FSETLOCKING_BYCALLER
);
437 while (fgets_unlocked (buf
, sizeof (buf
), fp
))
440 *__strchrnul (buf
, '\n') = '\0';
441 parse_line (hconf_name
, line_num
, buf
);
446 envval
= getenv (ENV_SERVORDER
);
449 _res_hconf
.num_services
= 0;
450 arg_service_list (ENV_SERVORDER
, 1, envval
, 0);
453 envval
= getenv (ENV_SPOOF
);
455 arg_spoof (ENV_SPOOF
, 1, envval
, 0);
457 envval
= getenv (ENV_MULTI
);
459 arg_bool (ENV_MULTI
, 1, envval
, HCONF_FLAG_MULTI
);
461 envval
= getenv (ENV_REORDER
);
463 arg_bool (ENV_REORDER
, 1, envval
, HCONF_FLAG_REORDER
);
465 envval
= getenv (ENV_TRIM_ADD
);
467 arg_trimdomain_list (ENV_TRIM_ADD
, 1, envval
, 0);
469 envval
= getenv (ENV_TRIM_OVERR
);
472 _res_hconf
.num_trimdomains
= 0;
473 arg_trimdomain_list (ENV_TRIM_OVERR
, 1, envval
, 0);
476 _res_hconf
.initialized
= 1;
480 /* Initialize hconf datastructure by reading host.conf file and
481 environment variables. */
483 _res_hconf_init (void)
485 __libc_once_define (static, once
);
487 __libc_once (once
, do_init
);
491 /* List of known interfaces. */
492 static struct netaddr
505 /* We need to protect the dynamic buffer handling. */
506 __libc_lock_define_initialized (static, lock
);
508 /* Reorder addresses returned in a hostent such that the first address
509 is an address on the local subnet, if there is such an address.
510 Otherwise, nothing is changed.
512 Note that this function currently only handles IPv4 addresses. */
515 _res_hconf_reorder_addrs (struct hostent
*hp
)
517 #if defined SIOCGIFCONF && defined SIOCGIFNETMASK
519 /* Number of interfaces. */
520 static int num_ifs
= -1;
522 /* Only reorder if we're supposed to. */
523 if ((_res_hconf
.flags
& HCONF_FLAG_REORDER
) == 0)
526 /* Can't deal with anything but IPv4 for now... */
527 if (hp
->h_addrtype
!= AF_INET
)
532 struct ifreq
*ifr
, *cur_ifr
;
537 /* Initialize interface table. */
541 /* The SIOCGIFNETMASK ioctl will only work on an AF_INET socket. */
542 sd
= __socket (AF_INET
, SOCK_DGRAM
, 0);
547 __libc_lock_lock (lock
);
549 /* Get a list of interfaces. */
550 __ifreq (&ifr
, &num
, sd
);
554 ifaddrs
= malloc (num
* sizeof (ifaddrs
[0]));
558 /* Copy usable interfaces in ifaddrs structure. */
559 for (cur_ifr
= ifr
, i
= 0; i
< num
; ++cur_ifr
, ++i
)
561 if (cur_ifr
->ifr_addr
.sa_family
!= AF_INET
)
564 ifaddrs
[num_ifs
].addrtype
= AF_INET
;
565 ifaddrs
[num_ifs
].u
.ipv4
.addr
=
566 ((struct sockaddr_in
*) &cur_ifr
->ifr_addr
)->sin_addr
.s_addr
;
568 if (__ioctl (sd
, SIOCGIFNETMASK
, cur_ifr
) < 0)
571 ifaddrs
[num_ifs
].u
.ipv4
.mask
=
572 ((struct sockaddr_in
*) &cur_ifr
->ifr_netmask
)->sin_addr
.s_addr
;
574 /* Now we're committed to this entry. */
577 /* Just keep enough memory to hold all the interfaces we want. */
578 ifaddrs
= realloc (ifaddrs
, num_ifs
* sizeof (ifaddrs
[0]));
581 __if_freereq (ifr
, num
);
584 /* Release lock, preserve error value, and close socket. */
586 __libc_lock_unlock (lock
);
593 /* Find an address for which we have a direct connection. */
594 for (i
= 0; hp
->h_addr_list
[i
]; ++i
)
596 struct in_addr
*haddr
= (struct in_addr
*) hp
->h_addr_list
[i
];
598 for (j
= 0; j
< num_ifs
; ++j
)
600 u_int32_t if_addr
= ifaddrs
[j
].u
.ipv4
.addr
;
601 u_int32_t if_netmask
= ifaddrs
[j
].u
.ipv4
.mask
;
603 if (((haddr
->s_addr
^ if_addr
) & if_netmask
) == 0)
607 tmp
= hp
->h_addr_list
[i
];
608 hp
->h_addr_list
[i
] = hp
->h_addr_list
[0];
609 hp
->h_addr_list
[0] = tmp
;
614 #endif /* defined(SIOCGIFCONF) && ... */
618 /* If HOSTNAME has a postfix matching any of the trimdomains, trim away
619 that postfix. Notice that HOSTNAME is modified inplace. Also, the
620 original code applied all trimdomains in order, meaning that the
621 same domainname could be trimmed multiple times. I believe this
622 was unintentional. */
624 _res_hconf_trim_domain (char *hostname
)
626 size_t hostname_len
, trim_len
;
629 hostname_len
= strlen (hostname
);
631 for (i
= 0; i
< _res_hconf
.num_trimdomains
; ++i
)
633 const char *trim
= _res_hconf
.trimdomain
[i
];
635 trim_len
= strlen (trim
);
636 if (hostname_len
> trim_len
637 && __strcasecmp (&hostname
[hostname_len
- trim_len
], trim
) == 0)
639 hostname
[hostname_len
- trim_len
] = '\0';
646 /* Trim all hostnames/aliases in HP according to the trimdomain list.
647 Notice that HP is modified inplace! */
649 _res_hconf_trim_domains (struct hostent
*hp
)
653 if (_res_hconf
.num_trimdomains
== 0)
656 _res_hconf_trim_domain (hp
->h_name
);
657 for (i
= 0; hp
->h_aliases
[i
]; ++i
)
658 _res_hconf_trim_domain (hp
->h_aliases
[i
]);
662 /* Free all resources if necessary. */
663 static void __attribute__ ((unused
))
669 text_set_element (__libc_subfreeres
, free_mem
);