1 /* Copyright (C) 1993, 95, 96, 97, 98, 99 Free Software Foundation, Inc.
2 Contributed by David Mosberger (davidm@azstarnet.com).
4 The GNU C Library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public License as
6 published by the Free Software Foundation; either version 2 of the
7 License, or (at your option) any later version.
9 The GNU C Library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
14 You should have received a copy of the GNU Library General Public
15 License along with the GNU C Library; see the file COPYING.LIB. If not,
16 write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 Boston, MA 02111-1307, USA. */
19 /* This file provides a Linux /etc/host.conf compatible front end to
20 the various name resolvers (/etc/hosts, named, NIS server, etc.).
21 Though mostly compatibly, the following differences exist compared
22 to the original implementation:
24 - new command "spoof" takes an arguments like RESOLV_SPOOF_CHECK
25 environment variable (i.e., `off', `nowarn', or `warn').
27 - line comments can appear anywhere (not just at the beginning of
37 #include "res_hconf.h"
39 #define _PATH_HOSTCONF "/etc/host.conf"
41 /* Environment vars that all user to override default behavior: */
42 #define ENV_HOSTCONF "RESOLV_HOST_CONF"
43 #define ENV_SERVORDER "RESOLV_SERV_ORDER"
44 #define ENV_SPOOF "RESOLV_SPOOF_CHECK"
45 #define ENV_TRIM_OVERR "RESOLV_OVERRIDE_TRIM_DOMAINS"
46 #define ENV_TRIM_ADD "RESOLV_ADD_TRIM_DOMAINS"
47 #define ENV_MULTI "RESOLV_MULTI"
48 #define ENV_REORDER "RESOLV_REORDER"
50 static const char *arg_service_list (const char *, int, const char *,
52 static const char *arg_trimdomain_list (const char *, int, const char *,
54 static const char *arg_spoof (const char *, int, const char *, unsigned int);
55 static const char *arg_bool (const char *, int, const char *, unsigned int);
60 const char *(*parse_args) (const char * filename, int line_num,
61 const char * args, unsigned int arg);
65 {"order", arg_service_list, 0},
66 {"trim", arg_trimdomain_list, 0},
67 {"spoof", arg_spoof, 0},
68 {"multi", arg_bool, HCONF_FLAG_MULTI},
69 {"nospoof", arg_bool, HCONF_FLAG_SPOOF},
70 {"spoofalert", arg_bool, HCONF_FLAG_SPOOFALERT},
71 {"reorder", arg_bool, HCONF_FLAG_REORDER}
74 /* Structure containing the state. */
75 struct hconf _res_hconf;
77 /* Skip white space. */
79 skip_ws (const char *str)
81 while (isspace (*str)) ++str;
86 /* Skip until whitespace, comma, end of line, or comment character. */
88 skip_string (const char *str)
90 while (*str && !isspace (*str) && *str != '#' && *str != ',')
97 arg_service_list (const char *fname, int line_num, const char *args,
100 enum Name_Service service;
107 enum Name_Service service;
110 {"bind", SERVICE_BIND},
111 {"hosts", SERVICE_HOSTS},
112 {"nis", SERVICE_NIS},
118 args = skip_string (args);
121 service = SERVICE_NONE;
122 for (i = 0; i < sizeof (svcs) / sizeof (svcs[0]); ++i)
124 if (__strncasecmp (start, svcs[i].name, len) == 0
125 && len == strlen (svcs[i].name))
127 service = svcs[i].service;
131 if (service == SERVICE_NONE)
133 fprintf (stderr, "%s: line %d: expected service, found `%s'\n",
134 fname, line_num, start);
137 if (_res_hconf.num_services >= SERVICE_MAX)
139 fprintf (stderr, "%s: line %d: cannot specify more than %d services",
140 fname, line_num, SERVICE_MAX);
143 _res_hconf.service[_res_hconf.num_services++] = service;
145 args = skip_ws (args);
151 args = skip_ws (++args);
152 if (!*args || *args == '#')
155 "%s: line %d: list delimiter not followed by keyword",
163 while (*args && *args != '#');
169 arg_trimdomain_list (const char *fname, int line_num, const char *args,
178 args = skip_string (args);
181 if (_res_hconf.num_trimdomains >= TRIMDOMAINS_MAX)
184 "%s: line %d: cannot specify more than %d trim domains",
185 fname, line_num, TRIMDOMAINS_MAX);
188 _res_hconf.trimdomain[_res_hconf.num_trimdomains++] =
189 __strndup (start, len);
190 args = skip_ws (args);
193 case ',': case ';': case ':':
194 args = skip_ws (++args);
195 if (!*args || *args == '#')
198 "%s: line %d: list delimiter not followed by domain",
206 while (*args && *args != '#');
212 arg_spoof (const char *fname, int line_num, const char *args, unsigned flag)
214 const char *start = args;
217 args = skip_string (args);
220 if (len == 3 && __strncasecmp (start, "off", len) == 0)
221 _res_hconf.flags &= ~(HCONF_FLAG_SPOOF | HCONF_FLAG_SPOOFALERT);
224 _res_hconf.flags |= (HCONF_FLAG_SPOOF | HCONF_FLAG_SPOOFALERT);
225 if ((len == 6 && __strncasecmp (start, "nowarn", len) == 0)
226 || !(len == 4 && __strncasecmp (start, "warn", len) == 0))
227 _res_hconf.flags &= ~HCONF_FLAG_SPOOFALERT;
234 arg_bool (const char *fname, int line_num, const char *args, unsigned flag)
236 if (__strncasecmp (args, "on", 2) == 0)
239 _res_hconf.flags |= flag;
241 else if (__strncasecmp (args, "off", 3) == 0)
244 _res_hconf.flags &= ~flag;
248 fprintf (stderr, "%s: line %d: expected `on' or `off', found `%s'\n",
249 fname, line_num, args);
257 parse_line (const char *fname, int line_num, const char *str)
266 /* skip line comment and empty lines: */
267 if (*str == '\0' || *str == '#') return;
270 str = skip_string (str);
273 for (i = 0; i < sizeof (cmd) / sizeof (cmd[0]); ++i)
275 if (strncasecmp (start, cmd[i].name, len) == 0
276 && strlen (cmd[i].name) == len)
284 fprintf (stderr, "%s: line %d: bad command `%s'\n",
285 fname, line_num, start);
291 str = (*c->parse_args) (fname, line_num, str, c->arg);
295 /* rest of line must contain white space or comment only: */
298 if (!isspace (*str)) {
300 fprintf (stderr, "%s: line %d: ignoring trailing garbage `%s'\n",
301 fname, line_num, str);
309 /* Initialize hconf datastructure by reading host.conf file and
310 environment variables. */
312 _res_hconf_init (void)
314 const char *hconf_name;
316 char buf[256], *end, *envval;
319 if (_res_hconf.initialized)
322 memset (&_res_hconf, '\0', sizeof (_res_hconf));
324 hconf_name = __secure_getenv (ENV_HOSTCONF);
325 if (hconf_name == NULL)
326 hconf_name = _PATH_HOSTCONF;
328 fp = fopen (hconf_name, "r");
330 /* make up something reasonable: */
331 _res_hconf.service[_res_hconf.num_services++] = SERVICE_BIND;
334 while (fgets_unlocked (buf, sizeof (buf), fp))
337 end = strchr (buf, '\n');
340 parse_line (hconf_name, line_num, buf);
345 envval = getenv (ENV_SERVORDER);
348 _res_hconf.num_services = 0;
349 arg_service_list (ENV_SERVORDER, 1, envval, 0);
352 envval = getenv (ENV_SPOOF);
354 arg_spoof (ENV_SPOOF, 1, envval, 0);
356 envval = getenv (ENV_MULTI);
358 arg_bool (ENV_MULTI, 1, envval, HCONF_FLAG_MULTI);
360 envval = getenv (ENV_REORDER);
362 arg_bool (ENV_REORDER, 1, envval, HCONF_FLAG_REORDER);
364 envval = getenv (ENV_TRIM_ADD);
366 arg_trimdomain_list (ENV_TRIM_ADD, 1, envval, 0);
368 envval = getenv (ENV_TRIM_OVERR);
371 _res_hconf.num_trimdomains = 0;
372 arg_trimdomain_list (ENV_TRIM_OVERR, 1, envval, 0);
375 _res_hconf.initialized = 1;
379 /* Reorder addresses returned in a hostent such that the first address
380 is an address on the local subnet, if there is such an address.
381 Otherwise, nothing is changed. */
384 _res_hconf_reorder_addrs (struct hostent *hp)
386 #if defined SIOCGIFCONF && defined SIOCGIFNETMASK
387 static int num_ifs = -1; /* number of interfaces */
388 static struct netaddr
401 if (hp->h_addrtype != AF_INET)
402 return; /* can't deal with anything but IPv4 for now... */
411 /* initialize interface table: */
415 sd = __socket (AF_INET, SOCK_DGRAM, 0);
419 /* Now get list of interfaces. Since we don't know how many
420 interfaces there are, we keep increasing the buffer size
421 until we have at least sizeof(struct ifreq) too many bytes.
422 That implies that the ioctl() return because it ran out of
423 interfaces, not memory */
428 size += 4 * sizeof (struct ifreq);
429 ifs.ifc_buf = realloc (ifs.ifs_buf, size);
430 if (ifs.ifc_buf == NULL)
436 if (__ioctl (sd, SIOCGIFCONF, &ifs) < 0)
439 while (size - ifs.ifc_len < sizeof (struct ifreq));
441 num = ifs.ifc_len / sizeof (struct ifreq);
443 ifaddrs = malloc (num * sizeof (ifaddrs[0]));
448 for (i = 0; i < num; ++i)
450 if (ifr->ifr_addr.sa_family != AF_INET)
452 ifaddrs[num_ifs].addrtype = AF_INET;
454 memcpy (&ifaddrs[num_ifs].u.ipv4.addr,
455 &((struct sockaddr_in *)ifr->ifr_addr)->sin_addr, 4);
457 if (__ioctl (sd, SIOCGIFNETMASK, if) < 0)
459 memcpy (&ifaddrs[num_ifs].u.ipv4.mask,
460 ((struct sockaddr_in *)ifr->ifr_mask)->sin_addr, 4);
462 ++num_ifs; /* now we're committed to this entry */
464 /* just keep enough memory to hold all the interfaces we want: */
465 ifaddrs = realloc (ifaddrs, num_ifs * sizeof (ifaddrs[0]));
475 /* find an address for which we have a direct connection: */
476 for (i = 0; hp->h_addr_list[i]; ++i)
478 h_addr = (struct in_addr *) hp->h_addr_list[i];
480 for (j = 0; j < num_ifs; ++j)
482 if_addr = ifaddrs[j].u.ipv4.addr;
483 if_netmask = ifaddrs[j].u.ipv4.mask;
485 if (((h_addr->s_addr ^ if_addr) & if_netmask) == 0)
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;
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. */
506 _res_hconf_trim_domain (char *hostname)
508 size_t hostname_len, trim_len;
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';
528 /* Trim all hostnames/aliases in HP according to the trimdomain list.
529 Notice that HP is modified inplace! */
531 _res_hconf_trim_domains (struct hostent *hp)
535 if (_res_hconf.num_trimdomains == 0)
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]);