* time/tzfile.h, time/private.h, time/zdump.c, time/zic.c,
[glibc.git] / resolv / res_hconf.c
blobb08dd3ced7911257b219ca731c36acdf3164d4c9
1 /* Copyright (C) 1993, 1995, 1996 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
16 not, write to the Free Software Foundation, Inc., 675 Mass Ave,
17 Cambridge, MA 02139, 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>
36 #include "res_hconf.h"
38 #define _PATH_HOSTCONF "/etc/host.conf"
40 /* Environment vars that all user to override default behavior: */
41 #define ENV_HOSTCONF "RESOLV_HOST_CONF"
42 #define ENV_SERVORDER "RESOLV_SERV_ORDER"
43 #define ENV_SPOOF "RESOLV_SPOOF_CHECK"
44 #define ENV_TRIM_OVERR "RESOLV_OVERRIDE_TRIM_DOMAINS"
45 #define ENV_TRIM_ADD "RESOLV_ADD_TRIM_DOMAINS"
46 #define ENV_MULTI "RESOLV_MULTI"
47 #define ENV_REORDER "RESOLV_REORDER"
49 static const char * arg_service_list (const char *, int, const char *,
50 unsigned);
51 static const char * arg_trimdomain_list (const char *, int, const char *,
52 unsigned);
53 static const char * arg_spoof (const char *, int, const char *, unsigned);
54 static const char * arg_bool (const char *, int, const char *, unsigned);
56 static struct cmd {
57 const char * name;
58 const char * (*parse_args)(const char * filename, int line_num,
59 const char * args, unsigned arg);
60 unsigned arg;;
61 } cmd[] = {
62 {"order", arg_service_list, 0},
63 {"trim", arg_trimdomain_list, 0},
64 {"spoof", arg_spoof, 0},
65 {"multi", arg_bool, HCONF_FLAG_MULTI},
66 {"nospoof", arg_bool, HCONF_FLAG_SPOOF},
67 {"spoofalert", arg_bool, HCONF_FLAG_SPOOFALERT},
68 {"reorder", arg_bool, HCONF_FLAG_REORDER}
73 * Why isn't this in stdlib?
75 char *
76 strndup (const char * s, size_t n)
78 char * retval;
80 retval = malloc (n + 1);
81 if (!retval)
82 return retval;
84 memcpy (retval, s, n);
85 retval[n] = '\0'; /* ensure return value is terminated */
86 return retval;
90 /* Skip white space. */
91 static const char *
92 skip_ws (const char * str)
94 while (isspace (*str)) ++str;
95 return str;
99 /* Skip until whitespace, comma, end of line, or comment character. */
100 static const char *
101 skip_string (const char * str)
103 while (*str && !isspace (*str) && *str != '#' && *str != ',') ++str;
104 return str;
108 static const char *
109 arg_service_list (const char * fname, int line_num, const char * args,
110 unsigned arg)
112 enum Name_Service service;
113 const char * start;
114 size_t len;
115 int i;
116 static struct {
117 const char * name;
118 enum Name_Service service;
119 } svcs[] = {
120 {"bind", SERVICE_BIND},
121 {"hosts", SERVICE_HOSTS},
122 {"nis", SERVICE_NIS},
127 start = args;
128 args = skip_string (args);
129 len = args - start;
131 service = SERVICE_NONE;
132 for (i = 0; i < sizeof (svcs) / sizeof (svcs[0]); ++i)
134 if (strncasecmp (start, svcs[i].name, len) == 0
135 && len == strlen (svcs[i].name))
137 service = svcs[i].service;
138 break;
141 if (service == SERVICE_NONE)
143 fprintf (stderr, "%s: line %d: expected service, found `%s'\n",
144 fname, line_num, start);
145 return 0;
147 if (_res_hconf.num_services >= SERVICE_MAX)
149 fprintf (stderr, "%s: line %d: cannot specify more than %d services",
150 fname, line_num, SERVICE_MAX);
151 return 0;
153 _res_hconf.service[_res_hconf.num_services++] = service;
155 args = skip_ws (args);
156 switch (*args)
158 case ',': case ';': case ':':
159 args = skip_ws (++args);
160 if (!*args || *args == '#')
162 fprintf (stderr,
163 "%s: line %d: list delimiter not followed by keyword",
164 fname, line_num);
165 return 0;
167 default:
168 break;
171 while (*args && *args != '#');
172 return args;
176 static const char *
177 arg_trimdomain_list (const char * fname, int line_num, const char * args,
178 unsigned flag)
180 const char * start;
181 size_t len;
185 start = args;
186 args = skip_string (args);
187 len = args - start;
189 if (_res_hconf.num_trimdomains >= TRIMDOMAINS_MAX)
191 fprintf (stderr,
192 "%s: line %d: cannot specify more than %d trim domains",
193 fname, line_num, TRIMDOMAINS_MAX);
194 return 0;
196 _res_hconf.trimdomain[_res_hconf.num_trimdomains++] =
197 strndup (start, len);
198 args = skip_ws (args);
199 switch (*args)
201 case ',': case ';': case ':':
202 args = skip_ws (++args);
203 if (!*args || *args == '#')
205 fprintf (stderr,
206 "%s: line %d: list delimiter not followed by domain",
207 fname, line_num);
208 return 0;
210 default:
211 break;
214 while (*args && *args != '#');
215 return args;
219 static const char *
220 arg_spoof (const char * fname, int line_num, const char * args, unsigned flag)
222 const char * start = args;
223 size_t len;
225 args = skip_string (args);
226 len = args - start;
228 if (len == 3 && strncasecmp (start, "off", len) == 0)
229 _res_hconf.flags &= ~(HCONF_FLAG_SPOOF | HCONF_FLAG_SPOOFALERT);
230 else
232 _res_hconf.flags |= (HCONF_FLAG_SPOOF | HCONF_FLAG_SPOOFALERT);
233 if ((len == 6 && strncasecmp (start, "nowarn", len) == 0)
234 || !(len == 4 && strncasecmp (start, "warn", len) == 0))
235 _res_hconf.flags &= ~HCONF_FLAG_SPOOFALERT;
237 return args;
241 static const char *
242 arg_bool (const char * fname, int line_num, const char * args, unsigned flag)
244 if (strncasecmp (args, "on", 2) == 0)
246 args += 2;
247 _res_hconf.flags |= flag;
249 else if (strncasecmp (args, "off", 3) == 0)
251 args += 3;
252 _res_hconf.flags &= ~flag;
254 else
256 fprintf (stderr, "%s: line %d: expected `on' or `off', found `%s'\n",
257 fname, line_num, args);
258 return 0;
260 return args;
264 static void
265 parse_line (const char * fname, int line_num, const char * str)
267 const char * start;
268 struct cmd * c = 0;
269 size_t len;
270 int i;
272 str = skip_ws (str);
274 if (*str == '#') return; /* skip line comment */
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)
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], * end, * envval;
324 FILE * fp;
326 memset (&_res_hconf, 0, sizeof (_res_hconf));
328 hconf_name = getenv (ENV_HOSTCONF);
329 if (!hconf_name)
330 hconf_name = _PATH_HOSTCONF;
332 fp = fopen (hconf_name, "r");
333 if (!fp)
334 /* make up something reasonable: */
335 _res_hconf.service[_res_hconf.num_services++] = SERVICE_BIND;
336 else
338 while (fgets (buf, sizeof (buf), fp))
340 ++line_num;
341 end = strchr (buf, '\n');
342 if (end)
343 *end = '\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);
381 /* Reorder addresses returned in a hostent such that the first address
382 is an address on the local subnet, if there is such an address.
383 Otherwise, nothing is changed. */
385 void
386 _res_hconf_reorder_addrs (struct hostent * hp)
388 #if defined (SIOCGIFCONF) && defined (SIOCGIFNETMASK)
389 static int num_ifs = -1; /* number of interfaces */
390 static struct netaddr {
391 int addrtype;
392 union {
393 struct {
394 u_int32_t addr;
395 u_int32_t mask;
396 } ipv4
397 } u;
398 } * ifaddrs;
400 if (hp->h_addrtype != AF_INET)
401 return; /* can't deal with anything but IPv4 for now... */
403 if (num_ifs <= 0)
405 struct ifconf ifs;
406 struct ifreq * ifr;
407 size_t size, num;
408 int sd;
410 /* initialize interface table: */
412 num_ifs = 0;
414 sd = socket (AF_INET, SOCK_DGRAM, 0);
415 if (sd < 0)
416 return;
418 /* Now get list of interfaces. Since we don't know how many
419 interfaces there are, we keep increasing the buffer size
420 until we have at least sizeof(struct ifreq) too many bytes.
421 That implies that the ioctl() return because it ran out of
422 interfaces, not memory */
423 size = 0;
424 ifs.ifc_buf = 0;
425 do {
426 size += 4 * sizeof (struct ifreq);
427 ifs.ifc_buf = realloc (ifs.ifs_buf, size);
428 if (!ifs.ifc_buf)
430 close (sd);
431 return;
433 ifs.ifc_len = size;
434 if (ioctl (sd, SIOCGIFCONF, &ifs) < 0)
435 goto cleanup;
436 } while (size - ifs.ifc_len < sizeof (struct ifreq));
438 num = ifs.ifc_len / sizeof (struct ifreq);
440 ifaddrs = malloc (num * sizeof (ifaddrs[0]));
441 if (!ifaddrs)
442 goto cleanup;
444 ifr = ifs.ifc_req;
445 for (i = 0; i < num; ++i) {
446 if (ifr->ifr_addr.sa_family != AF_INET)
447 continue;
448 ifaddrs[num_ifs].addrtype = AF_INET;
450 memcpy (&ifaddrs[num_ifs].u.ipv4.addr,
451 &((struct sockaddr_in *)ifr->ifr_addr)->sin_addr, 4);
453 if (ioctl (sd, SIOCGIFNETMASK, if) < 0)
454 continue;
455 memcpy (&ifaddrs[num_ifs].u.ipv4.mask,
456 ((struct sockaddr_in *)ifr->ifr_mask)->sin_addr, 4);
458 ++num_ifs; /* now we're committed to this entry */
460 /* just keep enough memory to hold all the interfaces we want: */
461 ifaddrs = realloc (ifaddrs, num_ifs * sizeof (ifaddrs[0]));
463 cleanup:
464 close (sd);
465 free (ifs.ifc_buf);
468 if (num_ifs == 0)
469 return;
471 /* find an address for which we have a direct connection: */
472 for (i = 0; hp->h_addr_list[i]; ++i)
474 h_addr = (struct in_addr *) hp->h_addr_list[i];
476 for (j = 0; j < num_ifs; ++j)
478 if_addr = ifaddrs[j].u.ipv4.addr;
479 if_netmask = ifaddrs[j].u.ipv4.mask;
481 if (((h_addr->s_addr ^ if_addr) & if_netmask) == 0)
483 void * tmp;
485 tmp = hp->h_addr_list[i];
486 hp->h_addr_list[i] = hp->h_addr_list[0];
487 hp->h_addr_list[0] = tmp;
488 return;
492 #endif /* defined(SIOCGIFCONF) && ... */
496 /* If HOSTNAME has a postfix matching any of the trimdomains, trim away
497 that postfix. Notice that HOSTNAME is modified inplace. Also, the
498 original code applied all trimdomains in order, meaning that the
499 same domainname could be trimmed multiple times. I believe this
500 was unintentional. */
501 void
502 _res_hconf_trim_domain (char * hostname)
504 size_t hostname_len, trim_len;
505 int i;
507 hostname_len = strlen(hostname);
509 for (i = 0; i < _res_hconf.num_trimdomains; ++i)
511 const char * trim = _res_hconf.trimdomain[i];
513 trim_len = strlen(trim);
514 if (hostname_len > trim_len
515 && strcasecmp(&hostname[hostname_len - trim_len], trim) == 0)
517 hostname[hostname_len - trim_len] = '\0';
518 break;
524 /* Trim all hostnames/aliases in HP according to the trimdomain list.
525 Notice that HP is modified inplace! */
526 void
527 _res_hconf_trim_domains (struct hostent * hp)
529 int i;
531 if (_res_hconf.num_trimdomains == 0)
532 return;
534 _res_hconf_trim_domain (hp->h_name);
535 for (i = 0; hp->h_aliases[i]; ++i)
536 _res_hconf_trim_domain (hp->h_aliases[i]);
540 #if 0
542 struct hostent *
543 _hconf_gethostent (void)
548 struct hostent *
549 _hconf_gethostbyname (const char * name)
555 struct hostent *
556 _hconf_gethostbyaddr (const char * addr, int len, int type)
561 struct hostent *
562 _hconf_gethtbyname (const char * name)
566 #endif