1 /* Copyright (C) 1993, 1995-2003, 2004, 2005 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);
70 static const struct cmd
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
;
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)
153 __fxprintf (NULL
, "%s", buf
);
158 if (_res_hconf
.num_services
>= SERVICE_MAX
)
162 if (__asprintf (&buf
, _("\
163 %s: line %d: cannot specify more than %d services"),
164 fname
, line_num
, SERVICE_MAX
) < 0)
167 __fxprintf (NULL
, "%s", buf
);
172 _res_hconf
.service
[_res_hconf
.num_services
++] = service
;
174 args
= skip_ws (args
);
180 args
= skip_ws (++args
);
181 if (!*args
|| *args
== '#')
185 if (__asprintf (&buf
, _("\
186 %s: line %d: list delimiter not followed by keyword"),
187 fname
, line_num
) < 0)
190 __fxprintf (NULL
, "%s", buf
);
199 while (*args
&& *args
!= '#');
205 arg_trimdomain_list (const char *fname
, int line_num
, const char *args
,
214 args
= skip_string (args
);
217 if (_res_hconf
.num_trimdomains
>= TRIMDOMAINS_MAX
)
221 if (__asprintf (&buf
, _("\
222 %s: line %d: cannot specify more than %d trim domains"),
223 fname
, line_num
, TRIMDOMAINS_MAX
) < 0)
226 __fxprintf (NULL
, "%s", buf
);
231 _res_hconf
.trimdomain
[_res_hconf
.num_trimdomains
++] =
232 __strndup (start
, len
);
233 args
= skip_ws (args
);
236 case ',': case ';': case ':':
237 args
= skip_ws (++args
);
238 if (!*args
|| *args
== '#')
242 if (__asprintf (&buf
, _("\
243 %s: line %d: list delimiter not followed by domain"),
244 fname
, line_num
) < 0)
247 __fxprintf (NULL
, "%s", buf
);
256 while (*args
&& *args
!= '#');
262 arg_spoof (const char *fname
, int line_num
, const char *args
, unsigned flag
)
264 const char *start
= args
;
267 args
= skip_string (args
);
270 if (len
== 3 && __strncasecmp (start
, "off", len
) == 0)
271 _res_hconf
.flags
&= ~(HCONF_FLAG_SPOOF
| HCONF_FLAG_SPOOFALERT
);
274 _res_hconf
.flags
|= (HCONF_FLAG_SPOOF
| HCONF_FLAG_SPOOFALERT
);
275 if ((len
== 6 && __strncasecmp (start
, "nowarn", len
) == 0)
276 || !(len
== 4 && __strncasecmp (start
, "warn", len
) == 0))
277 _res_hconf
.flags
&= ~HCONF_FLAG_SPOOFALERT
;
284 arg_bool (const char *fname
, int line_num
, const char *args
, unsigned flag
)
286 if (__strncasecmp (args
, "on", 2) == 0)
289 _res_hconf
.flags
|= flag
;
291 else if (__strncasecmp (args
, "off", 3) == 0)
294 _res_hconf
.flags
&= ~flag
;
300 if (__asprintf (&buf
,
301 _("%s: line %d: expected `on' or `off', found `%s'\n"),
302 fname
, line_num
, args
) < 0)
305 __fxprintf (NULL
, "%s", buf
);
315 parse_line (const char *fname
, int line_num
, const char *str
)
318 const struct cmd
*c
= 0;
324 /* skip line comment and empty lines: */
325 if (*str
== '\0' || *str
== '#') return;
328 str
= skip_string (str
);
331 for (i
= 0; i
< sizeof (cmd
) / sizeof (cmd
[0]); ++i
)
333 if (__strncasecmp (start
, cmd
[i
].name
, len
) == 0
334 && strlen (cmd
[i
].name
) == len
)
344 if (__asprintf (&buf
, _("%s: line %d: bad command `%s'\n"),
345 fname
, line_num
, start
) < 0)
348 __fxprintf (NULL
, "%s", buf
);
356 str
= (*c
->parse_args
) (fname
, line_num
, str
, c
->arg
);
360 /* rest of line must contain white space or comment only: */
363 if (!isspace (*str
)) {
368 if (__asprintf (&buf
,
369 _("%s: line %d: ignoring trailing garbage `%s'\n"),
370 fname
, line_num
, str
) < 0)
373 __fxprintf (NULL
, "%s", buf
);
387 const char *hconf_name
;
389 char buf
[256], *envval
;
392 memset (&_res_hconf
, '\0', sizeof (_res_hconf
));
394 hconf_name
= getenv (ENV_HOSTCONF
);
395 if (hconf_name
== NULL
)
396 hconf_name
= _PATH_HOSTCONF
;
398 fp
= fopen (hconf_name
, "rc");
400 /* make up something reasonable: */
401 _res_hconf
.service
[_res_hconf
.num_services
++] = SERVICE_BIND
;
404 /* No threads using this stream. */
405 __fsetlocking (fp
, FSETLOCKING_BYCALLER
);
407 while (fgets_unlocked (buf
, sizeof (buf
), fp
))
410 *__strchrnul (buf
, '\n') = '\0';
411 parse_line (hconf_name
, line_num
, buf
);
416 envval
= getenv (ENV_SERVORDER
);
419 _res_hconf
.num_services
= 0;
420 arg_service_list (ENV_SERVORDER
, 1, envval
, 0);
423 envval
= getenv (ENV_SPOOF
);
425 arg_spoof (ENV_SPOOF
, 1, envval
, 0);
427 envval
= getenv (ENV_MULTI
);
429 arg_bool (ENV_MULTI
, 1, envval
, HCONF_FLAG_MULTI
);
431 envval
= getenv (ENV_REORDER
);
433 arg_bool (ENV_REORDER
, 1, envval
, HCONF_FLAG_REORDER
);
435 envval
= getenv (ENV_TRIM_ADD
);
437 arg_trimdomain_list (ENV_TRIM_ADD
, 1, envval
, 0);
439 envval
= getenv (ENV_TRIM_OVERR
);
442 _res_hconf
.num_trimdomains
= 0;
443 arg_trimdomain_list (ENV_TRIM_OVERR
, 1, envval
, 0);
446 _res_hconf
.initialized
= 1;
450 /* Initialize hconf datastructure by reading host.conf file and
451 environment variables. */
453 _res_hconf_init (void)
455 __libc_once_define (static, once
);
457 __libc_once (once
, do_init
);
461 /* List of known interfaces. */
463 static struct netaddr
476 /* We need to protect the dynamic buffer handling. */
477 __libc_lock_define_initialized (static, lock
);
479 /* Reorder addresses returned in a hostent such that the first address
480 is an address on the local subnet, if there is such an address.
481 Otherwise, nothing is changed.
483 Note that this function currently only handles IPv4 addresses. */
486 _res_hconf_reorder_addrs (struct hostent
*hp
)
488 #if defined SIOCGIFCONF && defined SIOCGIFNETMASK
490 /* Number of interfaces. */
491 static int num_ifs
= -1;
493 /* Only reorder if we're supposed to. */
494 if ((_res_hconf
.flags
& HCONF_FLAG_REORDER
) == 0)
497 /* Can't deal with anything but IPv4 for now... */
498 if (hp
->h_addrtype
!= AF_INET
)
503 struct ifreq
*ifr
, *cur_ifr
;
508 /* Initialize interface table. */
512 /* The SIOCGIFNETMASK ioctl will only work on an AF_INET socket. */
513 sd
= __socket (AF_INET
, SOCK_DGRAM
, 0);
518 __libc_lock_lock (lock
);
520 /* Get a list of interfaces. */
521 __ifreq (&ifr
, &num
, sd
);
525 ifaddrs
= malloc (num
* sizeof (ifaddrs
[0]));
529 /* Copy usable interfaces in ifaddrs structure. */
530 for (cur_ifr
= ifr
, i
= 0; i
< num
; cur_ifr
= __if_nextreq (cur_ifr
), ++i
)
532 if (cur_ifr
->ifr_addr
.sa_family
!= AF_INET
)
535 ifaddrs
[num_ifs
].addrtype
= AF_INET
;
536 ifaddrs
[num_ifs
].u
.ipv4
.addr
=
537 ((struct sockaddr_in
*) &cur_ifr
->ifr_addr
)->sin_addr
.s_addr
;
539 if (__ioctl (sd
, SIOCGIFNETMASK
, cur_ifr
) < 0)
542 ifaddrs
[num_ifs
].u
.ipv4
.mask
=
543 ((struct sockaddr_in
*) &cur_ifr
->ifr_netmask
)->sin_addr
.s_addr
;
545 /* Now we're committed to this entry. */
548 /* Just keep enough memory to hold all the interfaces we want. */
549 ifaddrs
= realloc (ifaddrs
, num_ifs
* sizeof (ifaddrs
[0]));
550 assert (ifaddrs
!= NULL
);
553 __if_freereq (ifr
, num
);
556 /* Release lock, preserve error value, and close socket. */
558 __libc_lock_unlock (lock
);
565 /* Find an address for which we have a direct connection. */
566 for (i
= 0; hp
->h_addr_list
[i
]; ++i
)
568 struct in_addr
*haddr
= (struct in_addr
*) hp
->h_addr_list
[i
];
570 for (j
= 0; j
< num_ifs
; ++j
)
572 u_int32_t if_addr
= ifaddrs
[j
].u
.ipv4
.addr
;
573 u_int32_t if_netmask
= ifaddrs
[j
].u
.ipv4
.mask
;
575 if (((haddr
->s_addr
^ if_addr
) & if_netmask
) == 0)
579 tmp
= hp
->h_addr_list
[i
];
580 hp
->h_addr_list
[i
] = hp
->h_addr_list
[0];
581 hp
->h_addr_list
[0] = tmp
;
586 #endif /* defined(SIOCGIFCONF) && ... */
590 /* If HOSTNAME has a postfix matching any of the trimdomains, trim away
591 that postfix. Notice that HOSTNAME is modified inplace. Also, the
592 original code applied all trimdomains in order, meaning that the
593 same domainname could be trimmed multiple times. I believe this
594 was unintentional. */
596 _res_hconf_trim_domain (char *hostname
)
598 size_t hostname_len
, trim_len
;
601 hostname_len
= strlen (hostname
);
603 for (i
= 0; i
< _res_hconf
.num_trimdomains
; ++i
)
605 const char *trim
= _res_hconf
.trimdomain
[i
];
607 trim_len
= strlen (trim
);
608 if (hostname_len
> trim_len
609 && __strcasecmp (&hostname
[hostname_len
- trim_len
], trim
) == 0)
611 hostname
[hostname_len
- trim_len
] = '\0';
618 /* Trim all hostnames/aliases in HP according to the trimdomain list.
619 Notice that HP is modified inplace! */
621 _res_hconf_trim_domains (struct hostent
*hp
)
625 if (_res_hconf
.num_trimdomains
== 0)
628 _res_hconf_trim_domain (hp
->h_name
);
629 for (i
= 0; hp
->h_aliases
[i
]; ++i
)
630 _res_hconf_trim_domain (hp
->h_aliases
[i
]);