1 /* Copyright (C) 1993, 1995-2003, 2004 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
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
38 #include <stdio_ext.h>
42 #include <sys/ioctl.h>
44 #include <netinet/in.h>
45 #include <bits/libc-lock.h>
47 #include "res_hconf.h"
52 #define _PATH_HOSTCONF "/etc/host.conf"
54 /* Environment vars that all user to override default behavior: */
55 #define ENV_HOSTCONF "RESOLV_HOST_CONF"
56 #define ENV_SERVORDER "RESOLV_SERV_ORDER"
57 #define ENV_SPOOF "RESOLV_SPOOF_CHECK"
58 #define ENV_TRIM_OVERR "RESOLV_OVERRIDE_TRIM_DOMAINS"
59 #define ENV_TRIM_ADD "RESOLV_ADD_TRIM_DOMAINS"
60 #define ENV_MULTI "RESOLV_MULTI"
61 #define ENV_REORDER "RESOLV_REORDER"
63 static const char *arg_service_list (const char *, int, const char *,
65 static const char *arg_trimdomain_list (const char *, int, const char *,
67 static const char *arg_spoof (const char *, int, const char *, unsigned int);
68 static const char *arg_bool (const char *, int, const char *, unsigned int);
73 const char *(*parse_args
) (const char * filename
, int line_num
,
74 const char * args
, unsigned int arg
);
78 {"order", arg_service_list
, 0},
79 {"trim", arg_trimdomain_list
, 0},
80 {"spoof", arg_spoof
, 0},
81 {"multi", arg_bool
, HCONF_FLAG_MULTI
},
82 {"nospoof", arg_bool
, HCONF_FLAG_SPOOF
},
83 {"spoofalert", arg_bool
, HCONF_FLAG_SPOOFALERT
},
84 {"reorder", arg_bool
, HCONF_FLAG_REORDER
}
87 /* Structure containing the state. */
88 struct hconf _res_hconf
;
90 /* Skip white space. */
92 skip_ws (const char *str
)
94 while (isspace (*str
)) ++str
;
99 /* Skip until whitespace, comma, end of line, or comment character. */
101 skip_string (const char *str
)
103 while (*str
&& !isspace (*str
) && *str
!= '#' && *str
!= ',')
110 arg_service_list (const char *fname
, int line_num
, const char *args
,
113 enum Name_Service service
;
120 enum Name_Service service
;
123 {"bind", SERVICE_BIND
},
124 {"hosts", SERVICE_HOSTS
},
125 {"nis", SERVICE_NIS
},
131 args
= skip_string (args
);
134 service
= SERVICE_NONE
;
135 for (i
= 0; i
< sizeof (svcs
) / sizeof (svcs
[0]); ++i
)
137 if (__strncasecmp (start
, svcs
[i
].name
, len
) == 0
138 && len
== strlen (svcs
[i
].name
))
140 service
= svcs
[i
].service
;
144 if (service
== SERVICE_NONE
)
148 if (__asprintf (&buf
,
149 _("%s: line %d: expected service, found `%s'\n"),
150 fname
, line_num
, start
) < 0)
154 if (_IO_fwide (stderr
, 0) > 0)
155 __fwprintf (stderr
, L
"%s", buf
);
163 if (_res_hconf
.num_services
>= SERVICE_MAX
)
167 if (__asprintf (&buf
, _("\
168 %s: line %d: cannot specify more than %d services"),
169 fname
, line_num
, SERVICE_MAX
) < 0)
173 if (_IO_fwide (stderr
, 0) > 0)
174 __fwprintf (stderr
, L
"%s", buf
);
182 _res_hconf
.service
[_res_hconf
.num_services
++] = service
;
184 args
= skip_ws (args
);
190 args
= skip_ws (++args
);
191 if (!*args
|| *args
== '#')
195 if (__asprintf (&buf
, _("\
196 %s: line %d: list delimiter not followed by keyword"),
197 fname
, line_num
) < 0)
201 if (_IO_fwide (stderr
, 0) > 0)
202 __fwprintf (stderr
, L
"%s", buf
);
214 while (*args
&& *args
!= '#');
220 arg_trimdomain_list (const char *fname
, int line_num
, const char *args
,
229 args
= skip_string (args
);
232 if (_res_hconf
.num_trimdomains
>= TRIMDOMAINS_MAX
)
236 if (__asprintf (&buf
, _("\
237 %s: line %d: cannot specify more than %d trim domains"),
238 fname
, line_num
, TRIMDOMAINS_MAX
) < 0)
242 if (_IO_fwide (stderr
, 0) > 0)
243 __fwprintf (stderr
, L
"%s", buf
);
251 _res_hconf
.trimdomain
[_res_hconf
.num_trimdomains
++] =
252 __strndup (start
, len
);
253 args
= skip_ws (args
);
256 case ',': case ';': case ':':
257 args
= skip_ws (++args
);
258 if (!*args
|| *args
== '#')
262 if (__asprintf (&buf
, _("\
263 %s: line %d: list delimiter not followed by domain"),
264 fname
, line_num
) < 0)
268 if (_IO_fwide (stderr
, 0) > 0)
269 __fwprintf (stderr
, L
"%s", buf
);
281 while (*args
&& *args
!= '#');
287 arg_spoof (const char *fname
, int line_num
, const char *args
, unsigned flag
)
289 const char *start
= args
;
292 args
= skip_string (args
);
295 if (len
== 3 && __strncasecmp (start
, "off", len
) == 0)
296 _res_hconf
.flags
&= ~(HCONF_FLAG_SPOOF
| HCONF_FLAG_SPOOFALERT
);
299 _res_hconf
.flags
|= (HCONF_FLAG_SPOOF
| HCONF_FLAG_SPOOFALERT
);
300 if ((len
== 6 && __strncasecmp (start
, "nowarn", len
) == 0)
301 || !(len
== 4 && __strncasecmp (start
, "warn", len
) == 0))
302 _res_hconf
.flags
&= ~HCONF_FLAG_SPOOFALERT
;
309 arg_bool (const char *fname
, int line_num
, const char *args
, unsigned flag
)
311 if (__strncasecmp (args
, "on", 2) == 0)
314 _res_hconf
.flags
|= flag
;
316 else if (__strncasecmp (args
, "off", 3) == 0)
319 _res_hconf
.flags
&= ~flag
;
325 if (__asprintf (&buf
,
326 _("%s: line %d: expected `on' or `off', found `%s'\n"),
327 fname
, line_num
, args
) < 0)
331 if (_IO_fwide (stderr
, 0) > 0)
332 __fwprintf (stderr
, L
"%s", buf
);
345 parse_line (const char *fname
, int line_num
, const char *str
)
354 /* skip line comment and empty lines: */
355 if (*str
== '\0' || *str
== '#') return;
358 str
= skip_string (str
);
361 for (i
= 0; i
< sizeof (cmd
) / sizeof (cmd
[0]); ++i
)
363 if (__strncasecmp (start
, cmd
[i
].name
, len
) == 0
364 && strlen (cmd
[i
].name
) == len
)
374 if (__asprintf (&buf
, _("%s: line %d: bad command `%s'\n"),
375 fname
, line_num
, start
) < 0)
379 if (_IO_fwide (stderr
, 0) > 0)
380 __fwprintf (stderr
, L
"%s", buf
);
391 str
= (*c
->parse_args
) (fname
, line_num
, str
, c
->arg
);
395 /* rest of line must contain white space or comment only: */
398 if (!isspace (*str
)) {
403 if (__asprintf (&buf
,
404 _("%s: line %d: ignoring trailing garbage `%s'\n"),
405 fname
, line_num
, str
) < 0)
409 if (_IO_fwide (stderr
, 0) > 0)
410 __fwprintf (stderr
, L
"%s", buf
);
427 const char *hconf_name
;
429 char buf
[256], *envval
;
432 memset (&_res_hconf
, '\0', sizeof (_res_hconf
));
434 hconf_name
= getenv (ENV_HOSTCONF
);
435 if (hconf_name
== NULL
)
436 hconf_name
= _PATH_HOSTCONF
;
438 fp
= fopen (hconf_name
, "rc");
440 /* make up something reasonable: */
441 _res_hconf
.service
[_res_hconf
.num_services
++] = SERVICE_BIND
;
444 /* No threads using this stream. */
445 __fsetlocking (fp
, FSETLOCKING_BYCALLER
);
447 while (fgets_unlocked (buf
, sizeof (buf
), fp
))
450 *__strchrnul (buf
, '\n') = '\0';
451 parse_line (hconf_name
, line_num
, buf
);
456 envval
= getenv (ENV_SERVORDER
);
459 _res_hconf
.num_services
= 0;
460 arg_service_list (ENV_SERVORDER
, 1, envval
, 0);
463 envval
= getenv (ENV_SPOOF
);
465 arg_spoof (ENV_SPOOF
, 1, envval
, 0);
467 envval
= getenv (ENV_MULTI
);
469 arg_bool (ENV_MULTI
, 1, envval
, HCONF_FLAG_MULTI
);
471 envval
= getenv (ENV_REORDER
);
473 arg_bool (ENV_REORDER
, 1, envval
, HCONF_FLAG_REORDER
);
475 envval
= getenv (ENV_TRIM_ADD
);
477 arg_trimdomain_list (ENV_TRIM_ADD
, 1, envval
, 0);
479 envval
= getenv (ENV_TRIM_OVERR
);
482 _res_hconf
.num_trimdomains
= 0;
483 arg_trimdomain_list (ENV_TRIM_OVERR
, 1, envval
, 0);
486 _res_hconf
.initialized
= 1;
490 /* Initialize hconf datastructure by reading host.conf file and
491 environment variables. */
493 _res_hconf_init (void)
495 __libc_once_define (static, once
);
497 __libc_once (once
, do_init
);
501 /* List of known interfaces. */
503 static struct netaddr
516 /* We need to protect the dynamic buffer handling. */
517 __libc_lock_define_initialized (static, lock
);
519 /* Reorder addresses returned in a hostent such that the first address
520 is an address on the local subnet, if there is such an address.
521 Otherwise, nothing is changed.
523 Note that this function currently only handles IPv4 addresses. */
526 _res_hconf_reorder_addrs (struct hostent
*hp
)
528 #if defined SIOCGIFCONF && defined SIOCGIFNETMASK
530 /* Number of interfaces. */
531 static int num_ifs
= -1;
533 /* Only reorder if we're supposed to. */
534 if ((_res_hconf
.flags
& HCONF_FLAG_REORDER
) == 0)
537 /* Can't deal with anything but IPv4 for now... */
538 if (hp
->h_addrtype
!= AF_INET
)
543 struct ifreq
*ifr
, *cur_ifr
;
548 /* Initialize interface table. */
552 /* The SIOCGIFNETMASK ioctl will only work on an AF_INET socket. */
553 sd
= __socket (AF_INET
, SOCK_DGRAM
, 0);
558 __libc_lock_lock (lock
);
560 /* Get a list of interfaces. */
561 __ifreq (&ifr
, &num
, sd
);
565 ifaddrs
= malloc (num
* sizeof (ifaddrs
[0]));
569 /* Copy usable interfaces in ifaddrs structure. */
570 for (cur_ifr
= ifr
, i
= 0; i
< num
; cur_ifr
= __if_nextreq (cur_ifr
), ++i
)
572 if (cur_ifr
->ifr_addr
.sa_family
!= AF_INET
)
575 ifaddrs
[num_ifs
].addrtype
= AF_INET
;
576 ifaddrs
[num_ifs
].u
.ipv4
.addr
=
577 ((struct sockaddr_in
*) &cur_ifr
->ifr_addr
)->sin_addr
.s_addr
;
579 if (__ioctl (sd
, SIOCGIFNETMASK
, cur_ifr
) < 0)
582 ifaddrs
[num_ifs
].u
.ipv4
.mask
=
583 ((struct sockaddr_in
*) &cur_ifr
->ifr_netmask
)->sin_addr
.s_addr
;
585 /* Now we're committed to this entry. */
588 /* Just keep enough memory to hold all the interfaces we want. */
589 ifaddrs
= realloc (ifaddrs
, num_ifs
* sizeof (ifaddrs
[0]));
590 assert (ifaddrs
!= NULL
);
593 __if_freereq (ifr
, num
);
596 /* Release lock, preserve error value, and close socket. */
598 __libc_lock_unlock (lock
);
605 /* Find an address for which we have a direct connection. */
606 for (i
= 0; hp
->h_addr_list
[i
]; ++i
)
608 struct in_addr
*haddr
= (struct in_addr
*) hp
->h_addr_list
[i
];
610 for (j
= 0; j
< num_ifs
; ++j
)
612 u_int32_t if_addr
= ifaddrs
[j
].u
.ipv4
.addr
;
613 u_int32_t if_netmask
= ifaddrs
[j
].u
.ipv4
.mask
;
615 if (((haddr
->s_addr
^ if_addr
) & if_netmask
) == 0)
619 tmp
= hp
->h_addr_list
[i
];
620 hp
->h_addr_list
[i
] = hp
->h_addr_list
[0];
621 hp
->h_addr_list
[0] = tmp
;
626 #endif /* defined(SIOCGIFCONF) && ... */
630 /* If HOSTNAME has a postfix matching any of the trimdomains, trim away
631 that postfix. Notice that HOSTNAME is modified inplace. Also, the
632 original code applied all trimdomains in order, meaning that the
633 same domainname could be trimmed multiple times. I believe this
634 was unintentional. */
636 _res_hconf_trim_domain (char *hostname
)
638 size_t hostname_len
, trim_len
;
641 hostname_len
= strlen (hostname
);
643 for (i
= 0; i
< _res_hconf
.num_trimdomains
; ++i
)
645 const char *trim
= _res_hconf
.trimdomain
[i
];
647 trim_len
= strlen (trim
);
648 if (hostname_len
> trim_len
649 && __strcasecmp (&hostname
[hostname_len
- trim_len
], trim
) == 0)
651 hostname
[hostname_len
- trim_len
] = '\0';
658 /* Trim all hostnames/aliases in HP according to the trimdomain list.
659 Notice that HP is modified inplace! */
661 _res_hconf_trim_domains (struct hostent
*hp
)
665 if (_res_hconf
.num_trimdomains
== 0)
668 _res_hconf_trim_domain (hp
->h_name
);
669 for (i
= 0; hp
->h_aliases
[i
]; ++i
)
670 _res_hconf_trim_domain (hp
->h_aliases
[i
]);