Update.
[glibc.git] / resolv / res_hconf.c
blob642cada6789931dd47c9d3f472497cece4611c15
1 /* Copyright (C) 1993, 1995, 1996, 1997 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>
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}
72 /* Skip white space. */
73 static const char *
74 skip_ws (const char * str)
76 while (isspace (*str)) ++str;
77 return str;
81 /* Skip until whitespace, comma, end of line, or comment character. */
82 static const char *
83 skip_string (const char * str)
85 while (*str && !isspace (*str) && *str != '#' && *str != ',') ++str;
86 return str;
90 static const char *
91 arg_service_list (const char * fname, int line_num, const char * args,
92 unsigned arg)
94 enum Name_Service service;
95 const char * start;
96 size_t len;
97 int i;
98 static struct {
99 const char * name;
100 enum Name_Service service;
101 } svcs[] = {
102 {"bind", SERVICE_BIND},
103 {"hosts", SERVICE_HOSTS},
104 {"nis", SERVICE_NIS},
109 start = args;
110 args = skip_string (args);
111 len = args - start;
113 service = SERVICE_NONE;
114 for (i = 0; i < sizeof (svcs) / sizeof (svcs[0]); ++i)
116 if (strncasecmp (start, svcs[i].name, len) == 0
117 && len == strlen (svcs[i].name))
119 service = svcs[i].service;
120 break;
123 if (service == SERVICE_NONE)
125 fprintf (stderr, "%s: line %d: expected service, found `%s'\n",
126 fname, line_num, start);
127 return 0;
129 if (_res_hconf.num_services >= SERVICE_MAX)
131 fprintf (stderr, "%s: line %d: cannot specify more than %d services",
132 fname, line_num, SERVICE_MAX);
133 return 0;
135 _res_hconf.service[_res_hconf.num_services++] = service;
137 args = skip_ws (args);
138 switch (*args)
140 case ',': case ';': case ':':
141 args = skip_ws (++args);
142 if (!*args || *args == '#')
144 fprintf (stderr,
145 "%s: line %d: list delimiter not followed by keyword",
146 fname, line_num);
147 return 0;
149 default:
150 break;
153 while (*args && *args != '#');
154 return args;
158 static const char *
159 arg_trimdomain_list (const char * fname, int line_num, const char * args,
160 unsigned flag)
162 const char * start;
163 size_t len;
167 start = args;
168 args = skip_string (args);
169 len = args - start;
171 if (_res_hconf.num_trimdomains >= TRIMDOMAINS_MAX)
173 fprintf (stderr,
174 "%s: line %d: cannot specify more than %d trim domains",
175 fname, line_num, TRIMDOMAINS_MAX);
176 return 0;
178 _res_hconf.trimdomain[_res_hconf.num_trimdomains++] =
179 strndup (start, len);
180 args = skip_ws (args);
181 switch (*args)
183 case ',': case ';': case ':':
184 args = skip_ws (++args);
185 if (!*args || *args == '#')
187 fprintf (stderr,
188 "%s: line %d: list delimiter not followed by domain",
189 fname, line_num);
190 return 0;
192 default:
193 break;
196 while (*args && *args != '#');
197 return args;
201 static const char *
202 arg_spoof (const char * fname, int line_num, const char * args, unsigned flag)
204 const char * start = args;
205 size_t len;
207 args = skip_string (args);
208 len = args - start;
210 if (len == 3 && strncasecmp (start, "off", len) == 0)
211 _res_hconf.flags &= ~(HCONF_FLAG_SPOOF | HCONF_FLAG_SPOOFALERT);
212 else
214 _res_hconf.flags |= (HCONF_FLAG_SPOOF | HCONF_FLAG_SPOOFALERT);
215 if ((len == 6 && strncasecmp (start, "nowarn", len) == 0)
216 || !(len == 4 && strncasecmp (start, "warn", len) == 0))
217 _res_hconf.flags &= ~HCONF_FLAG_SPOOFALERT;
219 return args;
223 static const char *
224 arg_bool (const char * fname, int line_num, const char * args, unsigned flag)
226 if (strncasecmp (args, "on", 2) == 0)
228 args += 2;
229 _res_hconf.flags |= flag;
231 else if (strncasecmp (args, "off", 3) == 0)
233 args += 3;
234 _res_hconf.flags &= ~flag;
236 else
238 fprintf (stderr, "%s: line %d: expected `on' or `off', found `%s'\n",
239 fname, line_num, args);
240 return 0;
242 return args;
246 static void
247 parse_line (const char * fname, int line_num, const char * str)
249 const char * start;
250 struct cmd * c = 0;
251 size_t len;
252 int i;
254 str = skip_ws (str);
256 if (*str == '#') return; /* skip line comment */
258 start = str;
259 str = skip_string (str);
260 len = str - start;
262 for (i = 0; i < sizeof (cmd) / sizeof (cmd[0]); ++i)
264 if (strncasecmp (start, cmd[i].name, len) == 0
265 && strlen (cmd[i].name) == len)
267 c = &cmd[i];
268 break;
271 if (!c)
273 fprintf (stderr, "%s: line %d: bad command `%s'\n",
274 fname, line_num, start);
275 return;
278 /* process args: */
279 str = skip_ws (str);
280 str = (*c->parse_args) (fname, line_num, str, c->arg);
281 if (!str)
282 return;
284 /* rest of line must contain white space or comment only: */
285 while (*str)
287 if (!isspace (*str)) {
288 if (*str != '#')
289 fprintf (stderr, "%s: line %d: ignoring trailing garbage `%s'\n",
290 fname, line_num, str);
291 break;
293 ++str;
298 /* Initialize hconf datastructure by reading host.conf file and
299 environment variables. */
300 void
301 _res_hconf_init (void)
303 const char * hconf_name;
304 int line_num = 0;
305 char buf[256], * end, * envval;
306 FILE * fp;
308 memset (&_res_hconf, 0, sizeof (_res_hconf));
310 hconf_name = getenv (ENV_HOSTCONF);
311 if (!hconf_name)
312 hconf_name = _PATH_HOSTCONF;
314 fp = fopen (hconf_name, "r");
315 if (!fp)
316 /* make up something reasonable: */
317 _res_hconf.service[_res_hconf.num_services++] = SERVICE_BIND;
318 else
320 while (fgets (buf, sizeof (buf), fp))
322 ++line_num;
323 end = strchr (buf, '\n');
324 if (end)
325 *end = '\0';
326 parse_line (hconf_name, line_num, buf);
328 fclose (fp);
331 envval = getenv (ENV_SERVORDER);
332 if (envval)
334 _res_hconf.num_services = 0;
335 arg_service_list (ENV_SERVORDER, 1, envval, 0);
338 envval = getenv (ENV_SPOOF);
339 if (envval)
340 arg_spoof (ENV_SPOOF, 1, envval, 0);
342 envval = getenv (ENV_MULTI);
343 if (envval)
344 arg_bool (ENV_MULTI, 1, envval, HCONF_FLAG_MULTI);
346 envval = getenv (ENV_REORDER);
347 if (envval)
348 arg_bool (ENV_REORDER, 1, envval, HCONF_FLAG_REORDER);
350 envval = getenv (ENV_TRIM_ADD);
351 if (envval)
352 arg_trimdomain_list (ENV_TRIM_ADD, 1, envval, 0);
354 envval = getenv (ENV_TRIM_OVERR);
355 if (envval)
357 _res_hconf.num_trimdomains = 0;
358 arg_trimdomain_list (ENV_TRIM_OVERR, 1, envval, 0);
363 /* Reorder addresses returned in a hostent such that the first address
364 is an address on the local subnet, if there is such an address.
365 Otherwise, nothing is changed. */
367 void
368 _res_hconf_reorder_addrs (struct hostent * hp)
370 #if defined (SIOCGIFCONF) && defined (SIOCGIFNETMASK)
371 static int num_ifs = -1; /* number of interfaces */
372 static struct netaddr {
373 int addrtype;
374 union {
375 struct {
376 u_int32_t addr;
377 u_int32_t mask;
378 } ipv4
379 } u;
380 } * ifaddrs;
382 if (hp->h_addrtype != AF_INET)
383 return; /* can't deal with anything but IPv4 for now... */
385 if (num_ifs <= 0)
387 struct ifconf ifs;
388 struct ifreq * ifr;
389 size_t size, num;
390 int sd;
392 /* initialize interface table: */
394 num_ifs = 0;
396 sd = socket (AF_INET, SOCK_DGRAM, 0);
397 if (sd < 0)
398 return;
400 /* Now get list of interfaces. Since we don't know how many
401 interfaces there are, we keep increasing the buffer size
402 until we have at least sizeof(struct ifreq) too many bytes.
403 That implies that the ioctl() return because it ran out of
404 interfaces, not memory */
405 size = 0;
406 ifs.ifc_buf = 0;
407 do {
408 size += 4 * sizeof (struct ifreq);
409 ifs.ifc_buf = realloc (ifs.ifs_buf, size);
410 if (!ifs.ifc_buf)
412 close (sd);
413 return;
415 ifs.ifc_len = size;
416 if (ioctl (sd, SIOCGIFCONF, &ifs) < 0)
417 goto cleanup;
418 } while (size - ifs.ifc_len < sizeof (struct ifreq));
420 num = ifs.ifc_len / sizeof (struct ifreq);
422 ifaddrs = malloc (num * sizeof (ifaddrs[0]));
423 if (!ifaddrs)
424 goto cleanup;
426 ifr = ifs.ifc_req;
427 for (i = 0; i < num; ++i) {
428 if (ifr->ifr_addr.sa_family != AF_INET)
429 continue;
430 ifaddrs[num_ifs].addrtype = AF_INET;
432 memcpy (&ifaddrs[num_ifs].u.ipv4.addr,
433 &((struct sockaddr_in *)ifr->ifr_addr)->sin_addr, 4);
435 if (ioctl (sd, SIOCGIFNETMASK, if) < 0)
436 continue;
437 memcpy (&ifaddrs[num_ifs].u.ipv4.mask,
438 ((struct sockaddr_in *)ifr->ifr_mask)->sin_addr, 4);
440 ++num_ifs; /* now we're committed to this entry */
442 /* just keep enough memory to hold all the interfaces we want: */
443 ifaddrs = realloc (ifaddrs, num_ifs * sizeof (ifaddrs[0]));
445 cleanup:
446 close (sd);
447 free (ifs.ifc_buf);
450 if (num_ifs == 0)
451 return;
453 /* find an address for which we have a direct connection: */
454 for (i = 0; hp->h_addr_list[i]; ++i)
456 h_addr = (struct in_addr *) hp->h_addr_list[i];
458 for (j = 0; j < num_ifs; ++j)
460 if_addr = ifaddrs[j].u.ipv4.addr;
461 if_netmask = ifaddrs[j].u.ipv4.mask;
463 if (((h_addr->s_addr ^ if_addr) & if_netmask) == 0)
465 void * tmp;
467 tmp = hp->h_addr_list[i];
468 hp->h_addr_list[i] = hp->h_addr_list[0];
469 hp->h_addr_list[0] = tmp;
470 return;
474 #endif /* defined(SIOCGIFCONF) && ... */
478 /* If HOSTNAME has a postfix matching any of the trimdomains, trim away
479 that postfix. Notice that HOSTNAME is modified inplace. Also, the
480 original code applied all trimdomains in order, meaning that the
481 same domainname could be trimmed multiple times. I believe this
482 was unintentional. */
483 void
484 _res_hconf_trim_domain (char * hostname)
486 size_t hostname_len, trim_len;
487 int i;
489 hostname_len = strlen(hostname);
491 for (i = 0; i < _res_hconf.num_trimdomains; ++i)
493 const char * trim = _res_hconf.trimdomain[i];
495 trim_len = strlen(trim);
496 if (hostname_len > trim_len
497 && strcasecmp(&hostname[hostname_len - trim_len], trim) == 0)
499 hostname[hostname_len - trim_len] = '\0';
500 break;
506 /* Trim all hostnames/aliases in HP according to the trimdomain list.
507 Notice that HP is modified inplace! */
508 void
509 _res_hconf_trim_domains (struct hostent * hp)
511 int i;
513 if (_res_hconf.num_trimdomains == 0)
514 return;
516 _res_hconf_trim_domain (hp->h_name);
517 for (i = 0; hp->h_aliases[i]; ++i)
518 _res_hconf_trim_domain (hp->h_aliases[i]);
522 #if 0
524 struct hostent *
525 _hconf_gethostent (void)
530 struct hostent *
531 _hconf_gethostbyname (const char * name)
537 struct hostent *
538 _hconf_gethostbyaddr (const char * addr, int len, int type)
543 struct hostent *
544 _hconf_gethtbyname (const char * name)
548 #endif