Update.
[glibc.git] / resolv / res_hconf.c
blob59da9d6753ada66edc4adf52fd08d2d277ec7853
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
28 a line)
31 #include <errno.h>
32 #include <ctype.h>
33 #include <memory.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <net/if.h>
38 #include <sys/ioctl.h>
39 #include <unistd.h>
40 #include <netinet/in.h>
41 #include <bits/libc-lock.h>
42 #include "ifreq.h"
43 #include "res_hconf.h"
45 #define _PATH_HOSTCONF "/etc/host.conf"
47 /* Environment vars that all user to override default behavior: */
48 #define ENV_HOSTCONF "RESOLV_HOST_CONF"
49 #define ENV_SERVORDER "RESOLV_SERV_ORDER"
50 #define ENV_SPOOF "RESOLV_SPOOF_CHECK"
51 #define ENV_TRIM_OVERR "RESOLV_OVERRIDE_TRIM_DOMAINS"
52 #define ENV_TRIM_ADD "RESOLV_ADD_TRIM_DOMAINS"
53 #define ENV_MULTI "RESOLV_MULTI"
54 #define ENV_REORDER "RESOLV_REORDER"
56 static const char *arg_service_list (const char *, int, const char *,
57 unsigned int);
58 static const char *arg_trimdomain_list (const char *, int, const char *,
59 unsigned int);
60 static const char *arg_spoof (const char *, int, const char *, unsigned int);
61 static const char *arg_bool (const char *, int, const char *, unsigned int);
63 static struct cmd
65 const char *name;
66 const char *(*parse_args) (const char * filename, int line_num,
67 const char * args, unsigned int arg);
68 unsigned int arg;
69 } cmd[] =
71 {"order", arg_service_list, 0},
72 {"trim", arg_trimdomain_list, 0},
73 {"spoof", arg_spoof, 0},
74 {"multi", arg_bool, HCONF_FLAG_MULTI},
75 {"nospoof", arg_bool, HCONF_FLAG_SPOOF},
76 {"spoofalert", arg_bool, HCONF_FLAG_SPOOFALERT},
77 {"reorder", arg_bool, HCONF_FLAG_REORDER}
80 /* Structure containing the state. */
81 struct hconf _res_hconf;
83 /* Skip white space. */
84 static const char *
85 skip_ws (const char *str)
87 while (isspace (*str)) ++str;
88 return str;
92 /* Skip until whitespace, comma, end of line, or comment character. */
93 static const char *
94 skip_string (const char *str)
96 while (*str && !isspace (*str) && *str != '#' && *str != ',')
97 ++str;
98 return str;
102 static const char *
103 arg_service_list (const char *fname, int line_num, const char *args,
104 unsigned int arg)
106 enum Name_Service service;
107 const char *start;
108 size_t len;
109 int i;
110 static struct
112 const char * name;
113 enum Name_Service service;
114 } svcs[] =
116 {"bind", SERVICE_BIND},
117 {"hosts", SERVICE_HOSTS},
118 {"nis", SERVICE_NIS},
123 start = args;
124 args = skip_string (args);
125 len = args - start;
127 service = SERVICE_NONE;
128 for (i = 0; i < sizeof (svcs) / sizeof (svcs[0]); ++i)
130 if (__strncasecmp (start, svcs[i].name, len) == 0
131 && len == strlen (svcs[i].name))
133 service = svcs[i].service;
134 break;
137 if (service == SERVICE_NONE)
139 fprintf (stderr, "%s: line %d: expected service, found `%s'\n",
140 fname, line_num, start);
141 return 0;
143 if (_res_hconf.num_services >= SERVICE_MAX)
145 fprintf (stderr, "%s: line %d: cannot specify more than %d services",
146 fname, line_num, SERVICE_MAX);
147 return 0;
149 _res_hconf.service[_res_hconf.num_services++] = service;
151 args = skip_ws (args);
152 switch (*args)
154 case ',':
155 case ';':
156 case ':':
157 args = skip_ws (++args);
158 if (!*args || *args == '#')
160 fprintf (stderr,
161 "%s: line %d: list delimiter not followed by keyword",
162 fname, line_num);
163 return 0;
165 default:
166 break;
169 while (*args && *args != '#');
170 return args;
174 static const char *
175 arg_trimdomain_list (const char *fname, int line_num, const char *args,
176 unsigned int flag)
178 const char * start;
179 size_t len;
183 start = args;
184 args = skip_string (args);
185 len = args - start;
187 if (_res_hconf.num_trimdomains >= TRIMDOMAINS_MAX)
189 fprintf (stderr,
190 "%s: line %d: cannot specify more than %d trim domains",
191 fname, line_num, TRIMDOMAINS_MAX);
192 return 0;
194 _res_hconf.trimdomain[_res_hconf.num_trimdomains++] =
195 __strndup (start, len);
196 args = skip_ws (args);
197 switch (*args)
199 case ',': case ';': case ':':
200 args = skip_ws (++args);
201 if (!*args || *args == '#')
203 fprintf (stderr,
204 "%s: line %d: list delimiter not followed by domain",
205 fname, line_num);
206 return 0;
208 default:
209 break;
212 while (*args && *args != '#');
213 return args;
217 static const char *
218 arg_spoof (const char *fname, int line_num, const char *args, unsigned flag)
220 const char *start = args;
221 size_t len;
223 args = skip_string (args);
224 len = args - start;
226 if (len == 3 && __strncasecmp (start, "off", len) == 0)
227 _res_hconf.flags &= ~(HCONF_FLAG_SPOOF | HCONF_FLAG_SPOOFALERT);
228 else
230 _res_hconf.flags |= (HCONF_FLAG_SPOOF | HCONF_FLAG_SPOOFALERT);
231 if ((len == 6 && __strncasecmp (start, "nowarn", len) == 0)
232 || !(len == 4 && __strncasecmp (start, "warn", len) == 0))
233 _res_hconf.flags &= ~HCONF_FLAG_SPOOFALERT;
235 return args;
239 static const char *
240 arg_bool (const char *fname, int line_num, const char *args, unsigned flag)
242 if (__strncasecmp (args, "on", 2) == 0)
244 args += 2;
245 _res_hconf.flags |= flag;
247 else if (__strncasecmp (args, "off", 3) == 0)
249 args += 3;
250 _res_hconf.flags &= ~flag;
252 else
254 fprintf (stderr, "%s: line %d: expected `on' or `off', found `%s'\n",
255 fname, line_num, args);
256 return 0;
258 return args;
262 static void
263 parse_line (const char *fname, int line_num, const char *str)
265 const char *start;
266 struct cmd *c = 0;
267 size_t len;
268 int i;
270 str = skip_ws (str);
272 /* skip line comment and empty lines: */
273 if (*str == '\0' || *str == '#') return;
275 start = str;
276 str = skip_string (str);
277 len = str - start;
279 for (i = 0; i < sizeof (cmd) / sizeof (cmd[0]); ++i)
281 if (strncasecmp (start, cmd[i].name, len) == 0
282 && strlen (cmd[i].name) == len)
284 c = &cmd[i];
285 break;
288 if (c == NULL)
290 fprintf (stderr, "%s: line %d: bad command `%s'\n",
291 fname, line_num, start);
292 return;
295 /* process args: */
296 str = skip_ws (str);
297 str = (*c->parse_args) (fname, line_num, str, c->arg);
298 if (!str)
299 return;
301 /* rest of line must contain white space or comment only: */
302 while (*str)
304 if (!isspace (*str)) {
305 if (*str != '#')
306 fprintf (stderr, "%s: line %d: ignoring trailing garbage `%s'\n",
307 fname, line_num, str);
308 break;
310 ++str;
315 /* Initialize hconf datastructure by reading host.conf file and
316 environment variables. */
317 void
318 _res_hconf_init (void)
320 const char *hconf_name;
321 int line_num = 0;
322 char buf[256], *envval;
323 FILE *fp;
325 if (_res_hconf.initialized)
326 return;
328 memset (&_res_hconf, '\0', sizeof (_res_hconf));
330 hconf_name = __secure_getenv (ENV_HOSTCONF);
331 if (hconf_name == NULL)
332 hconf_name = _PATH_HOSTCONF;
334 fp = fopen (hconf_name, "r");
335 if (!fp)
336 /* make up something reasonable: */
337 _res_hconf.service[_res_hconf.num_services++] = SERVICE_BIND;
338 else
340 while (fgets_unlocked (buf, sizeof (buf), fp))
342 ++line_num;
343 *__strchrnul (buf, '\n') = '\0';
344 parse_line (hconf_name, line_num, buf);
346 fclose (fp);
349 envval = getenv (ENV_SERVORDER);
350 if (envval)
352 _res_hconf.num_services = 0;
353 arg_service_list (ENV_SERVORDER, 1, envval, 0);
356 envval = getenv (ENV_SPOOF);
357 if (envval)
358 arg_spoof (ENV_SPOOF, 1, envval, 0);
360 envval = getenv (ENV_MULTI);
361 if (envval)
362 arg_bool (ENV_MULTI, 1, envval, HCONF_FLAG_MULTI);
364 envval = getenv (ENV_REORDER);
365 if (envval)
366 arg_bool (ENV_REORDER, 1, envval, HCONF_FLAG_REORDER);
368 envval = getenv (ENV_TRIM_ADD);
369 if (envval)
370 arg_trimdomain_list (ENV_TRIM_ADD, 1, envval, 0);
372 envval = getenv (ENV_TRIM_OVERR);
373 if (envval)
375 _res_hconf.num_trimdomains = 0;
376 arg_trimdomain_list (ENV_TRIM_OVERR, 1, envval, 0);
379 _res_hconf.initialized = 1;
383 /* List of known interfaces. */
384 static struct netaddr
386 int addrtype;
387 union
389 struct
391 u_int32_t addr;
392 u_int32_t mask;
393 } ipv4;
394 } u;
395 } *ifaddrs;
397 /* We need to protect the dynamic buffer handling. */
398 __libc_lock_define_initialized (static, lock);
400 /* Reorder addresses returned in a hostent such that the first address
401 is an address on the local subnet, if there is such an address.
402 Otherwise, nothing is changed.
404 Note that this function currently only handles IPv4 addresses. */
406 void
407 _res_hconf_reorder_addrs (struct hostent *hp)
409 #if defined SIOCGIFCONF && defined SIOCGIFNETMASK
410 int i, j;
411 /* Number of interfaces. */
412 static int num_ifs = -1;
414 /* Only reorder if we're supposed to. */
415 if ((_res_hconf.flags & HCONF_FLAG_REORDER) == 0)
416 return;
418 /* Can't deal with anything but IPv4 for now... */
419 if (hp->h_addrtype != AF_INET)
420 return;
422 if (num_ifs <= 0)
424 struct ifreq *ifr, *cur_ifr;
425 int sd, num, i;
426 /* Save errno. */
427 int save = errno;
429 /* Initialize interface table. */
431 num_ifs = 0;
433 sd = __opensock ();
434 if (sd < 0)
435 return;
437 /* Get lock. */
438 __libc_lock_lock (lock);
440 /* Get a list of interfaces. */
441 __ifreq (&ifr, &num);
442 if (!ifr)
443 goto cleanup;
445 ifaddrs = malloc (num * sizeof (ifaddrs[0]));
446 if (!ifaddrs)
447 goto cleanup1;
449 /* Copy usable interfaces in ifaddrs structure. */
450 for (cur_ifr = ifr, i = 0; i < num; ++cur_ifr, ++i)
452 if (cur_ifr->ifr_addr.sa_family != AF_INET)
453 continue;
455 ifaddrs[num_ifs].addrtype = AF_INET;
456 ifaddrs[num_ifs].u.ipv4.addr =
457 ((struct sockaddr_in *) &cur_ifr->ifr_addr)->sin_addr.s_addr;
459 if (__ioctl (sd, SIOCGIFNETMASK, cur_ifr) < 0)
460 continue;
462 ifaddrs[num_ifs].u.ipv4.mask =
463 ((struct sockaddr_in *) &cur_ifr->ifr_netmask)->sin_addr.s_addr;
465 /* Now we're committed to this entry. */
466 ++num_ifs;
468 /* Just keep enough memory to hold all the interfaces we want. */
469 ifaddrs = realloc (ifaddrs, num_ifs * sizeof (ifaddrs[0]));
471 cleanup1:
472 __if_freereq (ifr);
474 cleanup:
475 /* Release lock, preserve error value, and close socket. */
476 save = errno;
477 __libc_lock_unlock (lock);
478 close (sd);
481 if (num_ifs == 0)
482 return;
484 /* Find an address for which we have a direct connection. */
485 for (i = 0; hp->h_addr_list[i]; ++i)
487 struct in_addr *haddr = (struct in_addr *) hp->h_addr_list[i];
489 for (j = 0; j < num_ifs; ++j)
491 u_int32_t if_addr = ifaddrs[j].u.ipv4.addr;
492 u_int32_t if_netmask = ifaddrs[j].u.ipv4.mask;
494 if (((haddr->s_addr ^ if_addr) & if_netmask) == 0)
496 void *tmp;
498 tmp = hp->h_addr_list[i];
499 hp->h_addr_list[i] = hp->h_addr_list[0];
500 hp->h_addr_list[0] = tmp;
501 return;
505 #endif /* defined(SIOCGIFCONF) && ... */
509 /* If HOSTNAME has a postfix matching any of the trimdomains, trim away
510 that postfix. Notice that HOSTNAME is modified inplace. Also, the
511 original code applied all trimdomains in order, meaning that the
512 same domainname could be trimmed multiple times. I believe this
513 was unintentional. */
514 void
515 _res_hconf_trim_domain (char *hostname)
517 size_t hostname_len, trim_len;
518 int i;
520 hostname_len = strlen (hostname);
522 for (i = 0; i < _res_hconf.num_trimdomains; ++i)
524 const char *trim = _res_hconf.trimdomain[i];
526 trim_len = strlen (trim);
527 if (hostname_len > trim_len
528 && __strcasecmp (&hostname[hostname_len - trim_len], trim) == 0)
530 hostname[hostname_len - trim_len] = '\0';
531 break;
537 /* Trim all hostnames/aliases in HP according to the trimdomain list.
538 Notice that HP is modified inplace! */
539 void
540 _res_hconf_trim_domains (struct hostent *hp)
542 int i;
544 if (_res_hconf.num_trimdomains == 0)
545 return;
547 _res_hconf_trim_domain (hp->h_name);
548 for (i = 0; hp->h_aliases[i]; ++i)
549 _res_hconf_trim_domain (hp->h_aliases[i]);
553 /* Free all resources if necessary. */
554 static void __attribute__ ((unused))
555 free_mem (void)
557 if (ifaddrs != NULL)
558 free (ifaddrs);
561 text_set_element (__libc_subfreeres, free_mem);