]> git.ipfire.org Git - thirdparty/glibc.git/blame - resolv/res_hconf.c
* sysdeps/i386/bits/byteswap.h [__GNUC__ < 2] (__bswap_32):
[thirdparty/glibc.git] / resolv / res_hconf.c
CommitLineData
9d957ce2 1/* Copyright (C) 1993, 1995-2001, 2002 Free Software Foundation, Inc.
41bdb6e2 2 This file is part of the GNU C Library.
fa0bc87c
RM
3 Contributed by David Mosberger (davidm@azstarnet.com).
4
c84142e8 5 The GNU C Library is free software; you can redistribute it and/or
41bdb6e2
AJ
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.
fa0bc87c 9
c84142e8
UD
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
41bdb6e2 13 Lesser General Public License for more details.
fa0bc87c 14
41bdb6e2
AJ
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. */
fa0bc87c
RM
19
20/* This file provides a Linux /etc/host.conf compatible front end to
f720d3d2
UD
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:
fa0bc87c
RM
24
25 - new command "spoof" takes an arguments like RESOLV_SPOOF_CHECK
26 environment variable (i.e., `off', `nowarn', or `warn').
27
28 - line comments can appear anywhere (not just at the beginning of
29 a line)
30*/
f720d3d2
UD
31
32#include <errno.h>
fa0bc87c 33#include <ctype.h>
51028f34 34#include <libintl.h>
fa0bc87c
RM
35#include <memory.h>
36#include <stdio.h>
2706ee38 37#include <stdio_ext.h>
fa0bc87c
RM
38#include <stdlib.h>
39#include <string.h>
5edb9387 40#include <net/if.h>
f720d3d2
UD
41#include <sys/ioctl.h>
42#include <unistd.h>
43#include <netinet/in.h>
44#include <bits/libc-lock.h>
45#include "ifreq.h"
fa0bc87c 46#include "res_hconf.h"
51028f34
UD
47#ifdef USE_IN_LIBIO
48# include <wchar.h>
49#endif
fa0bc87c
RM
50
51#define _PATH_HOSTCONF "/etc/host.conf"
52
53/* Environment vars that all user to override default behavior: */
54#define ENV_HOSTCONF "RESOLV_HOST_CONF"
55#define ENV_SERVORDER "RESOLV_SERV_ORDER"
56#define ENV_SPOOF "RESOLV_SPOOF_CHECK"
57#define ENV_TRIM_OVERR "RESOLV_OVERRIDE_TRIM_DOMAINS"
58#define ENV_TRIM_ADD "RESOLV_ADD_TRIM_DOMAINS"
59#define ENV_MULTI "RESOLV_MULTI"
60#define ENV_REORDER "RESOLV_REORDER"
61
5edb9387
UD
62static const char *arg_service_list (const char *, int, const char *,
63 unsigned int);
64static const char *arg_trimdomain_list (const char *, int, const char *,
65 unsigned int);
66static const char *arg_spoof (const char *, int, const char *, unsigned int);
67static const char *arg_bool (const char *, int, const char *, unsigned int);
68
69static struct cmd
70{
71 const char *name;
72 const char *(*parse_args) (const char * filename, int line_num,
73 const char * args, unsigned int arg);
74 unsigned int arg;
75} cmd[] =
76{
fa0bc87c
RM
77 {"order", arg_service_list, 0},
78 {"trim", arg_trimdomain_list, 0},
79 {"spoof", arg_spoof, 0},
80 {"multi", arg_bool, HCONF_FLAG_MULTI},
81 {"nospoof", arg_bool, HCONF_FLAG_SPOOF},
82 {"spoofalert", arg_bool, HCONF_FLAG_SPOOFALERT},
83 {"reorder", arg_bool, HCONF_FLAG_REORDER}
84};
85
5edb9387
UD
86/* Structure containing the state. */
87struct hconf _res_hconf;
fa0bc87c 88
fa0bc87c
RM
89/* Skip white space. */
90static const char *
5edb9387 91skip_ws (const char *str)
fa0bc87c
RM
92{
93 while (isspace (*str)) ++str;
94 return str;
95}
96
97
98/* Skip until whitespace, comma, end of line, or comment character. */
99static const char *
5edb9387 100skip_string (const char *str)
fa0bc87c 101{
5edb9387
UD
102 while (*str && !isspace (*str) && *str != '#' && *str != ',')
103 ++str;
fa0bc87c
RM
104 return str;
105}
106
107
108static const char *
5edb9387
UD
109arg_service_list (const char *fname, int line_num, const char *args,
110 unsigned int arg)
fa0bc87c
RM
111{
112 enum Name_Service service;
5edb9387 113 const char *start;
fa0bc87c 114 size_t len;
57b36a0a 115 size_t i;
5edb9387
UD
116 static struct
117 {
118 const char * name;
119 enum Name_Service service;
120 } svcs[] =
121 {
122 {"bind", SERVICE_BIND},
123 {"hosts", SERVICE_HOSTS},
124 {"nis", SERVICE_NIS},
125 };
fa0bc87c
RM
126
127 do
128 {
129 start = args;
130 args = skip_string (args);
131 len = args - start;
132
133 service = SERVICE_NONE;
134 for (i = 0; i < sizeof (svcs) / sizeof (svcs[0]); ++i)
135 {
5edb9387 136 if (__strncasecmp (start, svcs[i].name, len) == 0
fa0bc87c
RM
137 && len == strlen (svcs[i].name))
138 {
139 service = svcs[i].service;
140 break;
141 }
142 }
143 if (service == SERVICE_NONE)
144 {
51028f34
UD
145 char *buf;
146
147 __asprintf (&buf, _("%s: line %d: expected service, found `%s'\n"),
148 fname, line_num, start);
149
150#ifdef USE_IN_LIBIO
151 if (_IO_fwide (stderr, 0) > 0)
6293b803 152 __fwprintf (stderr, L"%s", buf);
51028f34
UD
153 else
154#endif
155 fputs (buf, stderr);
156
157 free (buf);
fa0bc87c
RM
158 return 0;
159 }
160 if (_res_hconf.num_services >= SERVICE_MAX)
161 {
51028f34
UD
162 char *buf;
163
164 __asprintf (&buf,
165 _("%s: line %d: cannot specify more than %d services"),
166 fname, line_num, SERVICE_MAX);
167
168#ifdef USE_IN_LIBIO
169 if (_IO_fwide (stderr, 0) > 0)
6293b803 170 __fwprintf (stderr, L"%s", buf);
51028f34
UD
171 else
172#endif
173 fputs (buf, stderr);
174
175 free (buf);
fa0bc87c
RM
176 return 0;
177 }
178 _res_hconf.service[_res_hconf.num_services++] = service;
179
180 args = skip_ws (args);
181 switch (*args)
182 {
5edb9387
UD
183 case ',':
184 case ';':
185 case ':':
fa0bc87c
RM
186 args = skip_ws (++args);
187 if (!*args || *args == '#')
188 {
51028f34
UD
189 char *buf;
190
191 __asprintf (&buf, _("\
192%s: line %d: list delimiter not followed by keyword"),
193 fname, line_num);
194
195#ifdef USE_IN_LIBIO
196 if (_IO_fwide (stderr, 0) > 0)
6293b803 197 __fwprintf (stderr, L"%s", buf);
51028f34
UD
198 else
199#endif
200 fputs (buf, stderr);
201
202 free (buf);
fa0bc87c
RM
203 return 0;
204 }
205 default:
206 break;
207 }
208 }
209 while (*args && *args != '#');
210 return args;
211}
212
213
214static const char *
5edb9387
UD
215arg_trimdomain_list (const char *fname, int line_num, const char *args,
216 unsigned int flag)
fa0bc87c
RM
217{
218 const char * start;
219 size_t len;
220
221 do
222 {
223 start = args;
224 args = skip_string (args);
225 len = args - start;
226
227 if (_res_hconf.num_trimdomains >= TRIMDOMAINS_MAX)
228 {
51028f34
UD
229 char *buf;
230
231 __asprintf (&buf, _("\
232%s: line %d: cannot specify more than %d trim domains"),
233 fname, line_num, TRIMDOMAINS_MAX);
234
235#ifdef USE_IN_LIBIO
236 if (_IO_fwide (stderr, 0) > 0)
6293b803 237 __fwprintf (stderr, L"%s", buf);
51028f34
UD
238 else
239#endif
240 fputs (buf, stderr);
241
242 free (buf);
fa0bc87c
RM
243 return 0;
244 }
245 _res_hconf.trimdomain[_res_hconf.num_trimdomains++] =
5edb9387 246 __strndup (start, len);
fa0bc87c
RM
247 args = skip_ws (args);
248 switch (*args)
249 {
250 case ',': case ';': case ':':
251 args = skip_ws (++args);
252 if (!*args || *args == '#')
253 {
51028f34
UD
254 char *buf;
255
256 __asprintf (&buf, _("\
257%s: line %d: list delimiter not followed by domain"),
258 fname, line_num);
259
260#ifdef USE_IN_LIBIO
261 if (_IO_fwide (stderr, 0) > 0)
6293b803 262 __fwprintf (stderr, L"%s", buf);
51028f34
UD
263 else
264#endif
265 fputs (buf, stderr);
266
267 free (buf);
fa0bc87c
RM
268 return 0;
269 }
270 default:
271 break;
272 }
273 }
274 while (*args && *args != '#');
275 return args;
276}
277
278
279static const char *
5edb9387 280arg_spoof (const char *fname, int line_num, const char *args, unsigned flag)
fa0bc87c 281{
5edb9387 282 const char *start = args;
fa0bc87c
RM
283 size_t len;
284
285 args = skip_string (args);
286 len = args - start;
287
5edb9387 288 if (len == 3 && __strncasecmp (start, "off", len) == 0)
fa0bc87c
RM
289 _res_hconf.flags &= ~(HCONF_FLAG_SPOOF | HCONF_FLAG_SPOOFALERT);
290 else
291 {
292 _res_hconf.flags |= (HCONF_FLAG_SPOOF | HCONF_FLAG_SPOOFALERT);
5edb9387
UD
293 if ((len == 6 && __strncasecmp (start, "nowarn", len) == 0)
294 || !(len == 4 && __strncasecmp (start, "warn", len) == 0))
fa0bc87c
RM
295 _res_hconf.flags &= ~HCONF_FLAG_SPOOFALERT;
296 }
297 return args;
298}
299
300
301static const char *
5edb9387 302arg_bool (const char *fname, int line_num, const char *args, unsigned flag)
fa0bc87c 303{
5edb9387 304 if (__strncasecmp (args, "on", 2) == 0)
fa0bc87c
RM
305 {
306 args += 2;
307 _res_hconf.flags |= flag;
308 }
5edb9387 309 else if (__strncasecmp (args, "off", 3) == 0)
fa0bc87c
RM
310 {
311 args += 3;
312 _res_hconf.flags &= ~flag;
313 }
314 else
315 {
51028f34
UD
316 char *buf;
317
318 __asprintf (&buf,
319 _("%s: line %d: expected `on' or `off', found `%s'\n"),
320 fname, line_num, args);
321
322#ifdef USE_IN_LIBIO
323 if (_IO_fwide (stderr, 0) > 0)
6293b803 324 __fwprintf (stderr, L"%s", buf);
51028f34
UD
325 else
326#endif
327 fputs (buf, stderr);
328
329 free (buf);
fa0bc87c
RM
330 return 0;
331 }
332 return args;
333}
334
335
336static void
5edb9387 337parse_line (const char *fname, int line_num, const char *str)
fa0bc87c 338{
5edb9387
UD
339 const char *start;
340 struct cmd *c = 0;
fa0bc87c 341 size_t len;
57b36a0a 342 size_t i;
fa0bc87c
RM
343
344 str = skip_ws (str);
345
6dc25b55
UD
346 /* skip line comment and empty lines: */
347 if (*str == '\0' || *str == '#') return;
fa0bc87c
RM
348
349 start = str;
350 str = skip_string (str);
351 len = str - start;
352
353 for (i = 0; i < sizeof (cmd) / sizeof (cmd[0]); ++i)
354 {
4aebaa6b 355 if (__strncasecmp (start, cmd[i].name, len) == 0
fa0bc87c
RM
356 && strlen (cmd[i].name) == len)
357 {
358 c = &cmd[i];
359 break;
360 }
361 }
5edb9387 362 if (c == NULL)
fa0bc87c 363 {
51028f34
UD
364 char *buf;
365
366 __asprintf (&buf, _("%s: line %d: bad command `%s'\n"),
367 fname, line_num, start);
368
369#ifdef USE_IN_LIBIO
370 if (_IO_fwide (stderr, 0) > 0)
6293b803 371 __fwprintf (stderr, L"%s", buf);
51028f34
UD
372 else
373#endif
374 fputs (buf, stderr);
375
376 free (buf);
fa0bc87c
RM
377 return;
378 }
379
380 /* process args: */
381 str = skip_ws (str);
382 str = (*c->parse_args) (fname, line_num, str, c->arg);
383 if (!str)
384 return;
385
386 /* rest of line must contain white space or comment only: */
387 while (*str)
388 {
389 if (!isspace (*str)) {
390 if (*str != '#')
51028f34
UD
391 {
392 char *buf;
393
394 __asprintf (&buf,
395 _("%s: line %d: ignoring trailing garbage `%s'\n"),
6293b803 396 fname, line_num, str);
51028f34
UD
397
398#ifdef USE_IN_LIBIO
399 if (_IO_fwide (stderr, 0) > 0)
400 __fwprintf (stderr, L"%s", buf);
401 else
402#endif
403 fputs (buf, stderr);
404
405 free (buf);
406 }
fa0bc87c
RM
407 break;
408 }
409 ++str;
410 }
411}
412
413
9d957ce2
UD
414static void
415do_init (void)
fa0bc87c 416{
5edb9387 417 const char *hconf_name;
fa0bc87c 418 int line_num = 0;
2c68584c 419 char buf[256], *envval;
5edb9387 420 FILE *fp;
fa0bc87c 421
5edb9387 422 memset (&_res_hconf, '\0', sizeof (_res_hconf));
fa0bc87c 423
74955460 424 hconf_name = getenv (ENV_HOSTCONF);
5edb9387 425 if (hconf_name == NULL)
fa0bc87c
RM
426 hconf_name = _PATH_HOSTCONF;
427
428 fp = fopen (hconf_name, "r");
429 if (!fp)
430 /* make up something reasonable: */
431 _res_hconf.service[_res_hconf.num_services++] = SERVICE_BIND;
432 else
433 {
2706ee38
UD
434 /* No threads using this stream. */
435 __fsetlocking (fp, FSETLOCKING_BYCALLER);
436
5edb9387 437 while (fgets_unlocked (buf, sizeof (buf), fp))
fa0bc87c
RM
438 {
439 ++line_num;
c4563d2d 440 *__strchrnul (buf, '\n') = '\0';
fa0bc87c
RM
441 parse_line (hconf_name, line_num, buf);
442 }
443 fclose (fp);
444 }
445
446 envval = getenv (ENV_SERVORDER);
447 if (envval)
448 {
449 _res_hconf.num_services = 0;
450 arg_service_list (ENV_SERVORDER, 1, envval, 0);
451 }
452
453 envval = getenv (ENV_SPOOF);
454 if (envval)
455 arg_spoof (ENV_SPOOF, 1, envval, 0);
456
457 envval = getenv (ENV_MULTI);
458 if (envval)
459 arg_bool (ENV_MULTI, 1, envval, HCONF_FLAG_MULTI);
460
461 envval = getenv (ENV_REORDER);
462 if (envval)
463 arg_bool (ENV_REORDER, 1, envval, HCONF_FLAG_REORDER);
464
465 envval = getenv (ENV_TRIM_ADD);
466 if (envval)
467 arg_trimdomain_list (ENV_TRIM_ADD, 1, envval, 0);
468
469 envval = getenv (ENV_TRIM_OVERR);
470 if (envval)
471 {
472 _res_hconf.num_trimdomains = 0;
473 arg_trimdomain_list (ENV_TRIM_OVERR, 1, envval, 0);
474 }
5edb9387
UD
475
476 _res_hconf.initialized = 1;
fa0bc87c
RM
477}
478
479
9d957ce2
UD
480/* Initialize hconf datastructure by reading host.conf file and
481 environment variables. */
482void
483_res_hconf_init (void)
484{
485 __libc_once_define (static, once);
486
487 __libc_once (once, do_init);
488}
489
490
f720d3d2
UD
491/* List of known interfaces. */
492static struct netaddr
493{
494 int addrtype;
495 union
496 {
497 struct
498 {
499 u_int32_t addr;
500 u_int32_t mask;
501 } ipv4;
502 } u;
503} *ifaddrs;
504
505/* We need to protect the dynamic buffer handling. */
506__libc_lock_define_initialized (static, lock);
507
fa0bc87c
RM
508/* Reorder addresses returned in a hostent such that the first address
509 is an address on the local subnet, if there is such an address.
f720d3d2
UD
510 Otherwise, nothing is changed.
511
512 Note that this function currently only handles IPv4 addresses. */
fa0bc87c
RM
513
514void
5edb9387 515_res_hconf_reorder_addrs (struct hostent *hp)
fa0bc87c 516{
5edb9387 517#if defined SIOCGIFCONF && defined SIOCGIFNETMASK
f720d3d2
UD
518 int i, j;
519 /* Number of interfaces. */
520 static int num_ifs = -1;
fa0bc87c 521
f720d3d2
UD
522 /* Only reorder if we're supposed to. */
523 if ((_res_hconf.flags & HCONF_FLAG_REORDER) == 0)
524 return;
4aebaa6b 525
f720d3d2 526 /* Can't deal with anything but IPv4 for now... */
fa0bc87c 527 if (hp->h_addrtype != AF_INET)
f720d3d2 528 return;
fa0bc87c
RM
529
530 if (num_ifs <= 0)
531 {
f720d3d2
UD
532 struct ifreq *ifr, *cur_ifr;
533 int sd, num, i;
534 /* Save errno. */
535 int save = errno;
4aebaa6b 536
f720d3d2 537 /* Initialize interface table. */
fa0bc87c
RM
538
539 num_ifs = 0;
540
7f1deee6
RM
541 /* The SIOCGIFNETMASK ioctl will only work on an AF_INET socket. */
542 sd = __socket (AF_INET, SOCK_DGRAM, 0);
fa0bc87c
RM
543 if (sd < 0)
544 return;
545
f720d3d2
UD
546 /* Get lock. */
547 __libc_lock_lock (lock);
fa0bc87c 548
f720d3d2 549 /* Get a list of interfaces. */
7f1deee6 550 __ifreq (&ifr, &num, sd);
f720d3d2
UD
551 if (!ifr)
552 goto cleanup;
fa0bc87c
RM
553
554 ifaddrs = malloc (num * sizeof (ifaddrs[0]));
555 if (!ifaddrs)
f720d3d2 556 goto cleanup1;
4aebaa6b 557
f720d3d2 558 /* Copy usable interfaces in ifaddrs structure. */
9db6ee8d 559 for (cur_ifr = ifr, i = 0; i < num; cur_ifr = __if_nextreq (cur_ifr), ++i)
5edb9387 560 {
f720d3d2 561 if (cur_ifr->ifr_addr.sa_family != AF_INET)
5edb9387 562 continue;
4aebaa6b 563
5edb9387 564 ifaddrs[num_ifs].addrtype = AF_INET;
f720d3d2
UD
565 ifaddrs[num_ifs].u.ipv4.addr =
566 ((struct sockaddr_in *) &cur_ifr->ifr_addr)->sin_addr.s_addr;
fa0bc87c 567
f720d3d2 568 if (__ioctl (sd, SIOCGIFNETMASK, cur_ifr) < 0)
5edb9387 569 continue;
fa0bc87c 570
f720d3d2
UD
571 ifaddrs[num_ifs].u.ipv4.mask =
572 ((struct sockaddr_in *) &cur_ifr->ifr_netmask)->sin_addr.s_addr;
573
574 /* Now we're committed to this entry. */
575 ++num_ifs;
5edb9387 576 }
f720d3d2 577 /* Just keep enough memory to hold all the interfaces we want. */
fa0bc87c
RM
578 ifaddrs = realloc (ifaddrs, num_ifs * sizeof (ifaddrs[0]));
579
f720d3d2 580 cleanup1:
7f1deee6 581 __if_freereq (ifr, num);
f720d3d2 582
fa0bc87c 583 cleanup:
f720d3d2
UD
584 /* Release lock, preserve error value, and close socket. */
585 save = errno;
586 __libc_lock_unlock (lock);
4aebaa6b 587 __close (sd);
fa0bc87c
RM
588 }
589
590 if (num_ifs == 0)
591 return;
592
f720d3d2 593 /* Find an address for which we have a direct connection. */
fa0bc87c
RM
594 for (i = 0; hp->h_addr_list[i]; ++i)
595 {
f720d3d2 596 struct in_addr *haddr = (struct in_addr *) hp->h_addr_list[i];
fa0bc87c
RM
597
598 for (j = 0; j < num_ifs; ++j)
599 {
f720d3d2
UD
600 u_int32_t if_addr = ifaddrs[j].u.ipv4.addr;
601 u_int32_t if_netmask = ifaddrs[j].u.ipv4.mask;
fa0bc87c 602
f720d3d2 603 if (((haddr->s_addr ^ if_addr) & if_netmask) == 0)
fa0bc87c 604 {
5edb9387 605 void *tmp;
fa0bc87c 606
5edb9387 607 tmp = hp->h_addr_list[i];
fa0bc87c
RM
608 hp->h_addr_list[i] = hp->h_addr_list[0];
609 hp->h_addr_list[0] = tmp;
610 return;
611 }
612 }
613 }
614#endif /* defined(SIOCGIFCONF) && ... */
615}
616
617
618/* If HOSTNAME has a postfix matching any of the trimdomains, trim away
619 that postfix. Notice that HOSTNAME is modified inplace. Also, the
620 original code applied all trimdomains in order, meaning that the
621 same domainname could be trimmed multiple times. I believe this
622 was unintentional. */
623void
5edb9387 624_res_hconf_trim_domain (char *hostname)
fa0bc87c
RM
625{
626 size_t hostname_len, trim_len;
627 int i;
628
5edb9387 629 hostname_len = strlen (hostname);
fa0bc87c
RM
630
631 for (i = 0; i < _res_hconf.num_trimdomains; ++i)
632 {
5edb9387 633 const char *trim = _res_hconf.trimdomain[i];
fa0bc87c 634
5edb9387 635 trim_len = strlen (trim);
fa0bc87c 636 if (hostname_len > trim_len
5edb9387 637 && __strcasecmp (&hostname[hostname_len - trim_len], trim) == 0)
fa0bc87c
RM
638 {
639 hostname[hostname_len - trim_len] = '\0';
640 break;
641 }
642 }
643}
644
645
646/* Trim all hostnames/aliases in HP according to the trimdomain list.
647 Notice that HP is modified inplace! */
648void
5edb9387 649_res_hconf_trim_domains (struct hostent *hp)
fa0bc87c
RM
650{
651 int i;
652
653 if (_res_hconf.num_trimdomains == 0)
654 return;
655
656 _res_hconf_trim_domain (hp->h_name);
657 for (i = 0; hp->h_aliases[i]; ++i)
658 _res_hconf_trim_domain (hp->h_aliases[i]);
659}
f720d3d2
UD
660
661
662/* Free all resources if necessary. */
663static void __attribute__ ((unused))
664free_mem (void)
665{
0e255b9c 666 free (ifaddrs);
f720d3d2
UD
667}
668
669text_set_element (__libc_subfreeres, free_mem);