Update.
[glibc.git] / resolv / res_hconf.c
blob50e49830f524ae91bf268caa60d2e6456b597ba8
1 /* Copyright (C) 1993, 95, 96, 97, 98, 99, 2000 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 <memory.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <net/if.h>
39 #include <sys/ioctl.h>
40 #include <unistd.h>
41 #include <netinet/in.h>
42 #include <bits/libc-lock.h>
43 #include "ifreq.h"
44 #include "res_hconf.h"
46 #define _PATH_HOSTCONF "/etc/host.conf"
48 /* Environment vars that all user to override default behavior: */
49 #define ENV_HOSTCONF "RESOLV_HOST_CONF"
50 #define ENV_SERVORDER "RESOLV_SERV_ORDER"
51 #define ENV_SPOOF "RESOLV_SPOOF_CHECK"
52 #define ENV_TRIM_OVERR "RESOLV_OVERRIDE_TRIM_DOMAINS"
53 #define ENV_TRIM_ADD "RESOLV_ADD_TRIM_DOMAINS"
54 #define ENV_MULTI "RESOLV_MULTI"
55 #define ENV_REORDER "RESOLV_REORDER"
57 static const char *arg_service_list (const char *, int, const char *,
58 unsigned int);
59 static const char *arg_trimdomain_list (const char *, int, const char *,
60 unsigned int);
61 static const char *arg_spoof (const char *, int, const char *, unsigned int);
62 static const char *arg_bool (const char *, int, const char *, unsigned int);
64 static struct cmd
66 const char *name;
67 const char *(*parse_args) (const char * filename, int line_num,
68 const char * args, unsigned int arg);
69 unsigned int arg;
70 } cmd[] =
72 {"order", arg_service_list, 0},
73 {"trim", arg_trimdomain_list, 0},
74 {"spoof", arg_spoof, 0},
75 {"multi", arg_bool, HCONF_FLAG_MULTI},
76 {"nospoof", arg_bool, HCONF_FLAG_SPOOF},
77 {"spoofalert", arg_bool, HCONF_FLAG_SPOOFALERT},
78 {"reorder", arg_bool, HCONF_FLAG_REORDER}
81 /* Structure containing the state. */
82 struct hconf _res_hconf;
84 /* Skip white space. */
85 static const char *
86 skip_ws (const char *str)
88 while (isspace (*str)) ++str;
89 return str;
93 /* Skip until whitespace, comma, end of line, or comment character. */
94 static const char *
95 skip_string (const char *str)
97 while (*str && !isspace (*str) && *str != '#' && *str != ',')
98 ++str;
99 return str;
103 static const char *
104 arg_service_list (const char *fname, int line_num, const char *args,
105 unsigned int arg)
107 enum Name_Service service;
108 const char *start;
109 size_t len;
110 int i;
111 static struct
113 const char * name;
114 enum Name_Service service;
115 } svcs[] =
117 {"bind", SERVICE_BIND},
118 {"hosts", SERVICE_HOSTS},
119 {"nis", SERVICE_NIS},
124 start = args;
125 args = skip_string (args);
126 len = args - start;
128 service = SERVICE_NONE;
129 for (i = 0; i < sizeof (svcs) / sizeof (svcs[0]); ++i)
131 if (__strncasecmp (start, svcs[i].name, len) == 0
132 && len == strlen (svcs[i].name))
134 service = svcs[i].service;
135 break;
138 if (service == SERVICE_NONE)
140 fprintf (stderr, "%s: line %d: expected service, found `%s'\n",
141 fname, line_num, start);
142 return 0;
144 if (_res_hconf.num_services >= SERVICE_MAX)
146 fprintf (stderr, "%s: line %d: cannot specify more than %d services",
147 fname, line_num, SERVICE_MAX);
148 return 0;
150 _res_hconf.service[_res_hconf.num_services++] = service;
152 args = skip_ws (args);
153 switch (*args)
155 case ',':
156 case ';':
157 case ':':
158 args = skip_ws (++args);
159 if (!*args || *args == '#')
161 fprintf (stderr,
162 "%s: line %d: list delimiter not followed by keyword",
163 fname, line_num);
164 return 0;
166 default:
167 break;
170 while (*args && *args != '#');
171 return args;
175 static const char *
176 arg_trimdomain_list (const char *fname, int line_num, const char *args,
177 unsigned int flag)
179 const char * start;
180 size_t len;
184 start = args;
185 args = skip_string (args);
186 len = args - start;
188 if (_res_hconf.num_trimdomains >= TRIMDOMAINS_MAX)
190 fprintf (stderr,
191 "%s: line %d: cannot specify more than %d trim domains",
192 fname, line_num, TRIMDOMAINS_MAX);
193 return 0;
195 _res_hconf.trimdomain[_res_hconf.num_trimdomains++] =
196 __strndup (start, len);
197 args = skip_ws (args);
198 switch (*args)
200 case ',': case ';': case ':':
201 args = skip_ws (++args);
202 if (!*args || *args == '#')
204 fprintf (stderr,
205 "%s: line %d: list delimiter not followed by domain",
206 fname, line_num);
207 return 0;
209 default:
210 break;
213 while (*args && *args != '#');
214 return args;
218 static const char *
219 arg_spoof (const char *fname, int line_num, const char *args, unsigned flag)
221 const char *start = args;
222 size_t len;
224 args = skip_string (args);
225 len = args - start;
227 if (len == 3 && __strncasecmp (start, "off", len) == 0)
228 _res_hconf.flags &= ~(HCONF_FLAG_SPOOF | HCONF_FLAG_SPOOFALERT);
229 else
231 _res_hconf.flags |= (HCONF_FLAG_SPOOF | HCONF_FLAG_SPOOFALERT);
232 if ((len == 6 && __strncasecmp (start, "nowarn", len) == 0)
233 || !(len == 4 && __strncasecmp (start, "warn", len) == 0))
234 _res_hconf.flags &= ~HCONF_FLAG_SPOOFALERT;
236 return args;
240 static const char *
241 arg_bool (const char *fname, int line_num, const char *args, unsigned flag)
243 if (__strncasecmp (args, "on", 2) == 0)
245 args += 2;
246 _res_hconf.flags |= flag;
248 else if (__strncasecmp (args, "off", 3) == 0)
250 args += 3;
251 _res_hconf.flags &= ~flag;
253 else
255 fprintf (stderr, "%s: line %d: expected `on' or `off', found `%s'\n",
256 fname, line_num, args);
257 return 0;
259 return args;
263 static void
264 parse_line (const char *fname, int line_num, const char *str)
266 const char *start;
267 struct cmd *c = 0;
268 size_t len;
269 int i;
271 str = skip_ws (str);
273 /* skip line comment and empty lines: */
274 if (*str == '\0' || *str == '#') return;
276 start = str;
277 str = skip_string (str);
278 len = str - start;
280 for (i = 0; i < sizeof (cmd) / sizeof (cmd[0]); ++i)
282 if (__strncasecmp (start, cmd[i].name, len) == 0
283 && strlen (cmd[i].name) == len)
285 c = &cmd[i];
286 break;
289 if (c == NULL)
291 fprintf (stderr, "%s: line %d: bad command `%s'\n",
292 fname, line_num, start);
293 return;
296 /* process args: */
297 str = skip_ws (str);
298 str = (*c->parse_args) (fname, line_num, str, c->arg);
299 if (!str)
300 return;
302 /* rest of line must contain white space or comment only: */
303 while (*str)
305 if (!isspace (*str)) {
306 if (*str != '#')
307 fprintf (stderr, "%s: line %d: ignoring trailing garbage `%s'\n",
308 fname, line_num, str);
309 break;
311 ++str;
316 /* Initialize hconf datastructure by reading host.conf file and
317 environment variables. */
318 void
319 _res_hconf_init (void)
321 const char *hconf_name;
322 int line_num = 0;
323 char buf[256], *envval;
324 FILE *fp;
326 if (_res_hconf.initialized)
327 return;
329 memset (&_res_hconf, '\0', sizeof (_res_hconf));
331 hconf_name = getenv (ENV_HOSTCONF);
332 if (hconf_name == NULL)
333 hconf_name = _PATH_HOSTCONF;
335 fp = fopen (hconf_name, "r");
336 if (!fp)
337 /* make up something reasonable: */
338 _res_hconf.service[_res_hconf.num_services++] = SERVICE_BIND;
339 else
341 while (fgets_unlocked (buf, sizeof (buf), fp))
343 ++line_num;
344 *__strchrnul (buf, '\n') = '\0';
345 parse_line (hconf_name, line_num, buf);
347 fclose (fp);
350 envval = getenv (ENV_SERVORDER);
351 if (envval)
353 _res_hconf.num_services = 0;
354 arg_service_list (ENV_SERVORDER, 1, envval, 0);
357 envval = getenv (ENV_SPOOF);
358 if (envval)
359 arg_spoof (ENV_SPOOF, 1, envval, 0);
361 envval = getenv (ENV_MULTI);
362 if (envval)
363 arg_bool (ENV_MULTI, 1, envval, HCONF_FLAG_MULTI);
365 envval = getenv (ENV_REORDER);
366 if (envval)
367 arg_bool (ENV_REORDER, 1, envval, HCONF_FLAG_REORDER);
369 envval = getenv (ENV_TRIM_ADD);
370 if (envval)
371 arg_trimdomain_list (ENV_TRIM_ADD, 1, envval, 0);
373 envval = getenv (ENV_TRIM_OVERR);
374 if (envval)
376 _res_hconf.num_trimdomains = 0;
377 arg_trimdomain_list (ENV_TRIM_OVERR, 1, envval, 0);
380 _res_hconf.initialized = 1;
384 /* List of known interfaces. */
385 static struct netaddr
387 int addrtype;
388 union
390 struct
392 u_int32_t addr;
393 u_int32_t mask;
394 } ipv4;
395 } u;
396 } *ifaddrs;
398 /* We need to protect the dynamic buffer handling. */
399 __libc_lock_define_initialized (static, lock);
401 /* Reorder addresses returned in a hostent such that the first address
402 is an address on the local subnet, if there is such an address.
403 Otherwise, nothing is changed.
405 Note that this function currently only handles IPv4 addresses. */
407 void
408 _res_hconf_reorder_addrs (struct hostent *hp)
410 #if defined SIOCGIFCONF && defined SIOCGIFNETMASK
411 int i, j;
412 /* Number of interfaces. */
413 static int num_ifs = -1;
415 /* Only reorder if we're supposed to. */
416 if ((_res_hconf.flags & HCONF_FLAG_REORDER) == 0)
417 return;
419 /* Can't deal with anything but IPv4 for now... */
420 if (hp->h_addrtype != AF_INET)
421 return;
423 if (num_ifs <= 0)
425 struct ifreq *ifr, *cur_ifr;
426 int sd, num, i;
427 /* Save errno. */
428 int save = errno;
430 /* Initialize interface table. */
432 num_ifs = 0;
434 sd = __opensock ();
435 if (sd < 0)
436 return;
438 /* Get lock. */
439 __libc_lock_lock (lock);
441 /* Get a list of interfaces. */
442 __ifreq (&ifr, &num);
443 if (!ifr)
444 goto cleanup;
446 ifaddrs = malloc (num * sizeof (ifaddrs[0]));
447 if (!ifaddrs)
448 goto cleanup1;
450 /* Copy usable interfaces in ifaddrs structure. */
451 for (cur_ifr = ifr, i = 0; i < num; ++cur_ifr, ++i)
453 if (cur_ifr->ifr_addr.sa_family != AF_INET)
454 continue;
456 ifaddrs[num_ifs].addrtype = AF_INET;
457 ifaddrs[num_ifs].u.ipv4.addr =
458 ((struct sockaddr_in *) &cur_ifr->ifr_addr)->sin_addr.s_addr;
460 if (__ioctl (sd, SIOCGIFNETMASK, cur_ifr) < 0)
461 continue;
463 ifaddrs[num_ifs].u.ipv4.mask =
464 ((struct sockaddr_in *) &cur_ifr->ifr_netmask)->sin_addr.s_addr;
466 /* Now we're committed to this entry. */
467 ++num_ifs;
469 /* Just keep enough memory to hold all the interfaces we want. */
470 ifaddrs = realloc (ifaddrs, num_ifs * sizeof (ifaddrs[0]));
472 cleanup1:
473 __if_freereq (ifr);
475 cleanup:
476 /* Release lock, preserve error value, and close socket. */
477 save = errno;
478 __libc_lock_unlock (lock);
479 __close (sd);
482 if (num_ifs == 0)
483 return;
485 /* Find an address for which we have a direct connection. */
486 for (i = 0; hp->h_addr_list[i]; ++i)
488 struct in_addr *haddr = (struct in_addr *) hp->h_addr_list[i];
490 for (j = 0; j < num_ifs; ++j)
492 u_int32_t if_addr = ifaddrs[j].u.ipv4.addr;
493 u_int32_t if_netmask = ifaddrs[j].u.ipv4.mask;
495 if (((haddr->s_addr ^ if_addr) & if_netmask) == 0)
497 void *tmp;
499 tmp = hp->h_addr_list[i];
500 hp->h_addr_list[i] = hp->h_addr_list[0];
501 hp->h_addr_list[0] = tmp;
502 return;
506 #endif /* defined(SIOCGIFCONF) && ... */
510 /* If HOSTNAME has a postfix matching any of the trimdomains, trim away
511 that postfix. Notice that HOSTNAME is modified inplace. Also, the
512 original code applied all trimdomains in order, meaning that the
513 same domainname could be trimmed multiple times. I believe this
514 was unintentional. */
515 void
516 _res_hconf_trim_domain (char *hostname)
518 size_t hostname_len, trim_len;
519 int i;
521 hostname_len = strlen (hostname);
523 for (i = 0; i < _res_hconf.num_trimdomains; ++i)
525 const char *trim = _res_hconf.trimdomain[i];
527 trim_len = strlen (trim);
528 if (hostname_len > trim_len
529 && __strcasecmp (&hostname[hostname_len - trim_len], trim) == 0)
531 hostname[hostname_len - trim_len] = '\0';
532 break;
538 /* Trim all hostnames/aliases in HP according to the trimdomain list.
539 Notice that HP is modified inplace! */
540 void
541 _res_hconf_trim_domains (struct hostent *hp)
543 int i;
545 if (_res_hconf.num_trimdomains == 0)
546 return;
548 _res_hconf_trim_domain (hp->h_name);
549 for (i = 0; hp->h_aliases[i]; ++i)
550 _res_hconf_trim_domain (hp->h_aliases[i]);
554 /* Free all resources if necessary. */
555 static void __attribute__ ((unused))
556 free_mem (void)
558 free (ifaddrs);
561 text_set_element (__libc_subfreeres, free_mem);