Update.
[glibc.git] / resolv / res_hconf.c
blobab49ccf9d64ec28129237da33e02d3b29361ec06
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)
30 #include <ctype.h>
31 #include <memory.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <net/if.h>
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 *,
51 unsigned int);
52 static const char *arg_trimdomain_list (const char *, int, const char *,
53 unsigned int);
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);
57 static struct cmd
59 const char *name;
60 const char *(*parse_args) (const char * filename, int line_num,
61 const char * args, unsigned int arg);
62 unsigned int arg;
63 } cmd[] =
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. */
78 static const char *
79 skip_ws (const char *str)
81 while (isspace (*str)) ++str;
82 return str;
86 /* Skip until whitespace, comma, end of line, or comment character. */
87 static const char *
88 skip_string (const char *str)
90 while (*str && !isspace (*str) && *str != '#' && *str != ',')
91 ++str;
92 return str;
96 static const char *
97 arg_service_list (const char *fname, int line_num, const char *args,
98 unsigned int arg)
100 enum Name_Service service;
101 const char *start;
102 size_t len;
103 int i;
104 static struct
106 const char * name;
107 enum Name_Service service;
108 } svcs[] =
110 {"bind", SERVICE_BIND},
111 {"hosts", SERVICE_HOSTS},
112 {"nis", SERVICE_NIS},
117 start = args;
118 args = skip_string (args);
119 len = args - start;
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;
128 break;
131 if (service == SERVICE_NONE)
133 fprintf (stderr, "%s: line %d: expected service, found `%s'\n",
134 fname, line_num, start);
135 return 0;
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);
141 return 0;
143 _res_hconf.service[_res_hconf.num_services++] = service;
145 args = skip_ws (args);
146 switch (*args)
148 case ',':
149 case ';':
150 case ':':
151 args = skip_ws (++args);
152 if (!*args || *args == '#')
154 fprintf (stderr,
155 "%s: line %d: list delimiter not followed by keyword",
156 fname, line_num);
157 return 0;
159 default:
160 break;
163 while (*args && *args != '#');
164 return args;
168 static const char *
169 arg_trimdomain_list (const char *fname, int line_num, const char *args,
170 unsigned int flag)
172 const char * start;
173 size_t len;
177 start = args;
178 args = skip_string (args);
179 len = args - start;
181 if (_res_hconf.num_trimdomains >= TRIMDOMAINS_MAX)
183 fprintf (stderr,
184 "%s: line %d: cannot specify more than %d trim domains",
185 fname, line_num, TRIMDOMAINS_MAX);
186 return 0;
188 _res_hconf.trimdomain[_res_hconf.num_trimdomains++] =
189 __strndup (start, len);
190 args = skip_ws (args);
191 switch (*args)
193 case ',': case ';': case ':':
194 args = skip_ws (++args);
195 if (!*args || *args == '#')
197 fprintf (stderr,
198 "%s: line %d: list delimiter not followed by domain",
199 fname, line_num);
200 return 0;
202 default:
203 break;
206 while (*args && *args != '#');
207 return args;
211 static const char *
212 arg_spoof (const char *fname, int line_num, const char *args, unsigned flag)
214 const char *start = args;
215 size_t len;
217 args = skip_string (args);
218 len = args - start;
220 if (len == 3 && __strncasecmp (start, "off", len) == 0)
221 _res_hconf.flags &= ~(HCONF_FLAG_SPOOF | HCONF_FLAG_SPOOFALERT);
222 else
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;
229 return args;
233 static const char *
234 arg_bool (const char *fname, int line_num, const char *args, unsigned flag)
236 if (__strncasecmp (args, "on", 2) == 0)
238 args += 2;
239 _res_hconf.flags |= flag;
241 else if (__strncasecmp (args, "off", 3) == 0)
243 args += 3;
244 _res_hconf.flags &= ~flag;
246 else
248 fprintf (stderr, "%s: line %d: expected `on' or `off', found `%s'\n",
249 fname, line_num, args);
250 return 0;
252 return args;
256 static void
257 parse_line (const char *fname, int line_num, const char *str)
259 const char *start;
260 struct cmd *c = 0;
261 size_t len;
262 int i;
264 str = skip_ws (str);
266 /* skip line comment and empty lines: */
267 if (*str == '\0' || *str == '#') return;
269 start = str;
270 str = skip_string (str);
271 len = str - start;
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)
278 c = &cmd[i];
279 break;
282 if (c == NULL)
284 fprintf (stderr, "%s: line %d: bad command `%s'\n",
285 fname, line_num, start);
286 return;
289 /* process args: */
290 str = skip_ws (str);
291 str = (*c->parse_args) (fname, line_num, str, c->arg);
292 if (!str)
293 return;
295 /* rest of line must contain white space or comment only: */
296 while (*str)
298 if (!isspace (*str)) {
299 if (*str != '#')
300 fprintf (stderr, "%s: line %d: ignoring trailing garbage `%s'\n",
301 fname, line_num, str);
302 break;
304 ++str;
309 /* Initialize hconf datastructure by reading host.conf file and
310 environment variables. */
311 void
312 _res_hconf_init (void)
314 const char *hconf_name;
315 int line_num = 0;
316 char buf[256], *end, *envval;
317 FILE *fp;
319 if (_res_hconf.initialized)
320 return;
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");
329 if (!fp)
330 /* make up something reasonable: */
331 _res_hconf.service[_res_hconf.num_services++] = SERVICE_BIND;
332 else
334 while (fgets_unlocked (buf, sizeof (buf), fp))
336 ++line_num;
337 *__strchrnul (buf, '\n') = '\0';
338 parse_line (hconf_name, line_num, buf);
340 fclose (fp);
343 envval = getenv (ENV_SERVORDER);
344 if (envval)
346 _res_hconf.num_services = 0;
347 arg_service_list (ENV_SERVORDER, 1, envval, 0);
350 envval = getenv (ENV_SPOOF);
351 if (envval)
352 arg_spoof (ENV_SPOOF, 1, envval, 0);
354 envval = getenv (ENV_MULTI);
355 if (envval)
356 arg_bool (ENV_MULTI, 1, envval, HCONF_FLAG_MULTI);
358 envval = getenv (ENV_REORDER);
359 if (envval)
360 arg_bool (ENV_REORDER, 1, envval, HCONF_FLAG_REORDER);
362 envval = getenv (ENV_TRIM_ADD);
363 if (envval)
364 arg_trimdomain_list (ENV_TRIM_ADD, 1, envval, 0);
366 envval = getenv (ENV_TRIM_OVERR);
367 if (envval)
369 _res_hconf.num_trimdomains = 0;
370 arg_trimdomain_list (ENV_TRIM_OVERR, 1, envval, 0);
373 _res_hconf.initialized = 1;
377 /* Reorder addresses returned in a hostent such that the first address
378 is an address on the local subnet, if there is such an address.
379 Otherwise, nothing is changed. */
381 void
382 _res_hconf_reorder_addrs (struct hostent *hp)
384 #if defined SIOCGIFCONF && defined SIOCGIFNETMASK
385 static int num_ifs = -1; /* number of interfaces */
386 static struct netaddr
388 int addrtype;
389 union
391 struct
393 u_int32_t addr;
394 u_int32_t mask;
395 } ipv4
396 } u;
397 } *ifaddrs;
399 if (hp->h_addrtype != AF_INET)
400 return; /* can't deal with anything but IPv4 for now... */
402 if (num_ifs <= 0)
404 struct ifconf ifs;
405 struct ifreq *ifr;
406 size_t size, num;
407 int sd;
409 /* initialize interface table: */
411 num_ifs = 0;
413 sd = __socket (AF_INET, SOCK_DGRAM, 0);
414 if (sd < 0)
415 return;
417 /* Now get list of interfaces. Since we don't know how many
418 interfaces there are, we keep increasing the buffer size
419 until we have at least sizeof(struct ifreq) too many bytes.
420 That implies that the ioctl() return because it ran out of
421 interfaces, not memory */
422 size = 0;
423 ifs.ifc_buf = 0;
426 size += 4 * sizeof (struct ifreq);
427 ifs.ifc_buf = realloc (ifs.ifs_buf, size);
428 if (ifs.ifc_buf == NULL)
430 close (sd);
431 return;
433 ifs.ifc_len = size;
434 if (__ioctl (sd, SIOCGIFCONF, &ifs) < 0)
435 goto cleanup;
437 while (size - ifs.ifc_len < sizeof (struct ifreq));
439 num = ifs.ifc_len / sizeof (struct ifreq);
441 ifaddrs = malloc (num * sizeof (ifaddrs[0]));
442 if (!ifaddrs)
443 goto cleanup;
445 ifr = ifs.ifc_req;
446 for (i = 0; i < num; ++i)
448 if (ifr->ifr_addr.sa_family != AF_INET)
449 continue;
450 ifaddrs[num_ifs].addrtype = AF_INET;
452 memcpy (&ifaddrs[num_ifs].u.ipv4.addr,
453 &((struct sockaddr_in *)ifr->ifr_addr)->sin_addr, 4);
455 if (__ioctl (sd, SIOCGIFNETMASK, if) < 0)
456 continue;
457 memcpy (&ifaddrs[num_ifs].u.ipv4.mask,
458 ((struct sockaddr_in *)ifr->ifr_mask)->sin_addr, 4);
460 ++num_ifs; /* now we're committed to this entry */
462 /* just keep enough memory to hold all the interfaces we want: */
463 ifaddrs = realloc (ifaddrs, num_ifs * sizeof (ifaddrs[0]));
465 cleanup:
466 close (sd);
467 free (ifs.ifc_buf);
470 if (num_ifs == 0)
471 return;
473 /* find an address for which we have a direct connection: */
474 for (i = 0; hp->h_addr_list[i]; ++i)
476 h_addr = (struct in_addr *) hp->h_addr_list[i];
478 for (j = 0; j < num_ifs; ++j)
480 if_addr = ifaddrs[j].u.ipv4.addr;
481 if_netmask = ifaddrs[j].u.ipv4.mask;
483 if (((h_addr->s_addr ^ if_addr) & if_netmask) == 0)
485 void *tmp;
487 tmp = hp->h_addr_list[i];
488 hp->h_addr_list[i] = hp->h_addr_list[0];
489 hp->h_addr_list[0] = tmp;
490 return;
494 #endif /* defined(SIOCGIFCONF) && ... */
498 /* If HOSTNAME has a postfix matching any of the trimdomains, trim away
499 that postfix. Notice that HOSTNAME is modified inplace. Also, the
500 original code applied all trimdomains in order, meaning that the
501 same domainname could be trimmed multiple times. I believe this
502 was unintentional. */
503 void
504 _res_hconf_trim_domain (char *hostname)
506 size_t hostname_len, trim_len;
507 int i;
509 hostname_len = strlen (hostname);
511 for (i = 0; i < _res_hconf.num_trimdomains; ++i)
513 const char *trim = _res_hconf.trimdomain[i];
515 trim_len = strlen (trim);
516 if (hostname_len > trim_len
517 && __strcasecmp (&hostname[hostname_len - trim_len], trim) == 0)
519 hostname[hostname_len - trim_len] = '\0';
520 break;
526 /* Trim all hostnames/aliases in HP according to the trimdomain list.
527 Notice that HP is modified inplace! */
528 void
529 _res_hconf_trim_domains (struct hostent *hp)
531 int i;
533 if (_res_hconf.num_trimdomains == 0)
534 return;
536 _res_hconf_trim_domain (hp->h_name);
537 for (i = 0; hp->h_aliases[i]; ++i)
538 _res_hconf_trim_domain (hp->h_aliases[i]);