1 /* Copyright (C) 1993, 1995-2005, 2006 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_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"
62 static const char *arg_trimdomain_list (const char *, int, const char *,
64 static const char *arg_spoof (const char *, int, const char *, unsigned int);
65 static const char *arg_bool (const char *, int, const char *, unsigned int);
67 static const struct cmd
70 const char *(*parse_args
) (const char * filename
, int line_num
,
71 const char * args
, unsigned int arg
);
76 {"trim", arg_trimdomain_list
, 0},
77 {"spoof", arg_spoof
, 0},
78 {"multi", arg_bool
, HCONF_FLAG_MULTI
},
79 {"nospoof", arg_bool
, HCONF_FLAG_SPOOF
},
80 {"spoofalert", arg_bool
, HCONF_FLAG_SPOOFALERT
},
81 {"reorder", arg_bool
, HCONF_FLAG_REORDER
}
84 /* Structure containing the state. */
85 struct hconf _res_hconf
;
87 /* Skip white space. */
89 skip_ws (const char *str
)
91 while (isspace (*str
)) ++str
;
96 /* Skip until whitespace, comma, end of line, or comment character. */
98 skip_string (const char *str
)
100 while (*str
&& !isspace (*str
) && *str
!= '#' && *str
!= ',')
107 arg_trimdomain_list (const char *fname
, int line_num
, const char *args
,
116 args
= skip_string (args
);
119 if (_res_hconf
.num_trimdomains
>= TRIMDOMAINS_MAX
)
123 if (__asprintf (&buf
, _("\
124 %s: line %d: cannot specify more than %d trim domains"),
125 fname
, line_num
, TRIMDOMAINS_MAX
) < 0)
128 __fxprintf (NULL
, "%s", buf
);
133 _res_hconf
.trimdomain
[_res_hconf
.num_trimdomains
++] =
134 __strndup (start
, len
);
135 args
= skip_ws (args
);
138 case ',': case ';': case ':':
139 args
= skip_ws (++args
);
140 if (!*args
|| *args
== '#')
144 if (__asprintf (&buf
, _("\
145 %s: line %d: list delimiter not followed by domain"),
146 fname
, line_num
) < 0)
149 __fxprintf (NULL
, "%s", buf
);
158 while (*args
&& *args
!= '#');
164 arg_spoof (const char *fname
, int line_num
, const char *args
, unsigned flag
)
166 const char *start
= args
;
169 args
= skip_string (args
);
172 if (len
== 3 && __strncasecmp (start
, "off", len
) == 0)
173 _res_hconf
.flags
&= ~(HCONF_FLAG_SPOOF
| HCONF_FLAG_SPOOFALERT
);
176 _res_hconf
.flags
|= (HCONF_FLAG_SPOOF
| HCONF_FLAG_SPOOFALERT
);
177 if ((len
== 6 && __strncasecmp (start
, "nowarn", len
) == 0)
178 || !(len
== 4 && __strncasecmp (start
, "warn", len
) == 0))
179 _res_hconf
.flags
&= ~HCONF_FLAG_SPOOFALERT
;
186 arg_bool (const char *fname
, int line_num
, const char *args
, unsigned flag
)
188 if (__strncasecmp (args
, "on", 2) == 0)
191 _res_hconf
.flags
|= flag
;
193 else if (__strncasecmp (args
, "off", 3) == 0)
196 _res_hconf
.flags
&= ~flag
;
202 if (__asprintf (&buf
,
203 _("%s: line %d: expected `on' or `off', found `%s'\n"),
204 fname
, line_num
, args
) < 0)
207 __fxprintf (NULL
, "%s", buf
);
217 parse_line (const char *fname
, int line_num
, const char *str
)
220 const struct cmd
*c
= 0;
226 /* skip line comment and empty lines: */
227 if (*str
== '\0' || *str
== '#') return;
230 str
= skip_string (str
);
233 for (i
= 0; i
< sizeof (cmd
) / sizeof (cmd
[0]); ++i
)
235 if (__strncasecmp (start
, cmd
[i
].name
, len
) == 0
236 && strlen (cmd
[i
].name
) == len
)
246 if (__asprintf (&buf
, _("%s: line %d: bad command `%s'\n"),
247 fname
, line_num
, start
) < 0)
250 __fxprintf (NULL
, "%s", buf
);
256 /* Ignore lines for which no parser is set. This is used for
257 obsolete commands. */
258 if (c
->parse_args
== NULL
)
263 str
= (*c
->parse_args
) (fname
, line_num
, str
, c
->arg
);
267 /* rest of line must contain white space or comment only: */
270 if (!isspace (*str
)) {
275 if (__asprintf (&buf
,
276 _("%s: line %d: ignoring trailing garbage `%s'\n"),
277 fname
, line_num
, str
) < 0)
280 __fxprintf (NULL
, "%s", buf
);
294 const char *hconf_name
;
296 char buf
[256], *envval
;
299 memset (&_res_hconf
, '\0', sizeof (_res_hconf
));
301 hconf_name
= getenv (ENV_HOSTCONF
);
302 if (hconf_name
== NULL
)
303 hconf_name
= _PATH_HOSTCONF
;
305 fp
= fopen (hconf_name
, "rc");
308 /* No threads using this stream. */
309 __fsetlocking (fp
, FSETLOCKING_BYCALLER
);
311 while (fgets_unlocked (buf
, sizeof (buf
), fp
))
314 *__strchrnul (buf
, '\n') = '\0';
315 parse_line (hconf_name
, line_num
, buf
);
320 envval
= getenv (ENV_SPOOF
);
322 arg_spoof (ENV_SPOOF
, 1, envval
, 0);
324 envval
= getenv (ENV_MULTI
);
326 arg_bool (ENV_MULTI
, 1, envval
, HCONF_FLAG_MULTI
);
328 envval
= getenv (ENV_REORDER
);
330 arg_bool (ENV_REORDER
, 1, envval
, HCONF_FLAG_REORDER
);
332 envval
= getenv (ENV_TRIM_ADD
);
334 arg_trimdomain_list (ENV_TRIM_ADD
, 1, envval
, 0);
336 envval
= getenv (ENV_TRIM_OVERR
);
339 _res_hconf
.num_trimdomains
= 0;
340 arg_trimdomain_list (ENV_TRIM_OVERR
, 1, envval
, 0);
343 _res_hconf
.initialized
= 1;
347 /* Initialize hconf datastructure by reading host.conf file and
348 environment variables. */
350 _res_hconf_init (void)
352 __libc_once_define (static, once
);
354 __libc_once (once
, do_init
);
358 /* List of known interfaces. */
360 static struct netaddr
373 /* We need to protect the dynamic buffer handling. */
374 __libc_lock_define_initialized (static, lock
);
376 /* Reorder addresses returned in a hostent such that the first address
377 is an address on the local subnet, if there is such an address.
378 Otherwise, nothing is changed.
380 Note that this function currently only handles IPv4 addresses. */
383 _res_hconf_reorder_addrs (struct hostent
*hp
)
385 #if defined SIOCGIFCONF && defined SIOCGIFNETMASK
387 /* Number of interfaces. */
388 static int num_ifs
= -1;
390 /* Only reorder if we're supposed to. */
391 if ((_res_hconf
.flags
& HCONF_FLAG_REORDER
) == 0)
394 /* Can't deal with anything but IPv4 for now... */
395 if (hp
->h_addrtype
!= AF_INET
)
400 struct ifreq
*ifr
, *cur_ifr
;
405 /* Initialize interface table. */
409 /* The SIOCGIFNETMASK ioctl will only work on an AF_INET socket. */
410 sd
= __socket (AF_INET
, SOCK_DGRAM
, 0);
415 __libc_lock_lock (lock
);
417 /* Get a list of interfaces. */
418 __ifreq (&ifr
, &num
, sd
);
422 ifaddrs
= malloc (num
* sizeof (ifaddrs
[0]));
426 /* Copy usable interfaces in ifaddrs structure. */
427 for (cur_ifr
= ifr
, i
= 0; i
< num
; cur_ifr
= __if_nextreq (cur_ifr
), ++i
)
429 if (cur_ifr
->ifr_addr
.sa_family
!= AF_INET
)
432 ifaddrs
[num_ifs
].addrtype
= AF_INET
;
433 ifaddrs
[num_ifs
].u
.ipv4
.addr
=
434 ((struct sockaddr_in
*) &cur_ifr
->ifr_addr
)->sin_addr
.s_addr
;
436 if (__ioctl (sd
, SIOCGIFNETMASK
, cur_ifr
) < 0)
439 ifaddrs
[num_ifs
].u
.ipv4
.mask
=
440 ((struct sockaddr_in
*) &cur_ifr
->ifr_netmask
)->sin_addr
.s_addr
;
442 /* Now we're committed to this entry. */
445 /* Just keep enough memory to hold all the interfaces we want. */
446 ifaddrs
= realloc (ifaddrs
, num_ifs
* sizeof (ifaddrs
[0]));
447 assert (ifaddrs
!= NULL
);
450 __if_freereq (ifr
, num
);
453 /* Release lock, preserve error value, and close socket. */
455 __libc_lock_unlock (lock
);
462 /* Find an address for which we have a direct connection. */
463 for (i
= 0; hp
->h_addr_list
[i
]; ++i
)
465 struct in_addr
*haddr
= (struct in_addr
*) hp
->h_addr_list
[i
];
467 for (j
= 0; j
< num_ifs
; ++j
)
469 u_int32_t if_addr
= ifaddrs
[j
].u
.ipv4
.addr
;
470 u_int32_t if_netmask
= ifaddrs
[j
].u
.ipv4
.mask
;
472 if (((haddr
->s_addr
^ if_addr
) & if_netmask
) == 0)
476 tmp
= hp
->h_addr_list
[i
];
477 hp
->h_addr_list
[i
] = hp
->h_addr_list
[0];
478 hp
->h_addr_list
[0] = tmp
;
483 #endif /* defined(SIOCGIFCONF) && ... */
487 /* If HOSTNAME has a postfix matching any of the trimdomains, trim away
488 that postfix. Notice that HOSTNAME is modified inplace. Also, the
489 original code applied all trimdomains in order, meaning that the
490 same domainname could be trimmed multiple times. I believe this
491 was unintentional. */
493 _res_hconf_trim_domain (char *hostname
)
495 size_t hostname_len
, trim_len
;
498 hostname_len
= strlen (hostname
);
500 for (i
= 0; i
< _res_hconf
.num_trimdomains
; ++i
)
502 const char *trim
= _res_hconf
.trimdomain
[i
];
504 trim_len
= strlen (trim
);
505 if (hostname_len
> trim_len
506 && __strcasecmp (&hostname
[hostname_len
- trim_len
], trim
) == 0)
508 hostname
[hostname_len
- trim_len
] = '\0';
515 /* Trim all hostnames/aliases in HP according to the trimdomain list.
516 Notice that HP is modified inplace! */
518 _res_hconf_trim_domains (struct hostent
*hp
)
522 if (_res_hconf
.num_trimdomains
== 0)
525 _res_hconf_trim_domain (hp
->h_name
);
526 for (i
= 0; hp
->h_aliases
[i
]; ++i
)
527 _res_hconf_trim_domain (hp
->h_aliases
[i
]);