1 /* Copyright (C) 1993,1995-2006,2007,2009,2011
2 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4 Contributed by David Mosberger (davidm@azstarnet.com).
6 The GNU C Library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
11 The GNU C Library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public
17 License along with the GNU C Library; if not, write to the Free
18 Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
21 /* This file provides a Linux /etc/host.conf compatible front end to
22 the various name resolvers (/etc/hosts, named, NIS server, etc.).
23 Though mostly compatibly, the following differences exist compared
24 to the original implementation:
26 - new command "spoof" takes an arguments like RESOLV_SPOOF_CHECK
27 environment variable (i.e., `off', `nowarn', or `warn').
29 - line comments can appear anywhere (not just at the beginning of
39 #include <stdio_ext.h>
43 #include <sys/ioctl.h>
45 #include <netinet/in.h>
46 #include <bits/libc-lock.h>
48 #include "res_hconf.h"
51 #define _PATH_HOSTCONF "/etc/host.conf"
53 /* Environment vars that all user to override default behavior: */
54 #define ENV_HOSTCONF "RESOLV_HOST_CONF"
55 #define ENV_SPOOF "RESOLV_SPOOF_CHECK"
56 #define ENV_TRIM_OVERR "RESOLV_OVERRIDE_TRIM_DOMAINS"
57 #define ENV_TRIM_ADD "RESOLV_ADD_TRIM_DOMAINS"
58 #define ENV_MULTI "RESOLV_MULTI"
59 #define ENV_REORDER "RESOLV_REORDER"
64 CB_arg_trimdomain_list
,
69 static const struct cmd
76 {"order", CB_none
, 0},
77 {"trim", CB_arg_trimdomain_list
, 0},
78 {"spoof", CB_arg_spoof
, 0},
79 {"multi", CB_arg_bool
, HCONF_FLAG_MULTI
},
80 {"nospoof", CB_arg_bool
, HCONF_FLAG_SPOOF
},
81 {"spoofalert", CB_arg_bool
, HCONF_FLAG_SPOOFALERT
},
82 {"reorder", CB_arg_bool
, HCONF_FLAG_REORDER
}
85 /* Structure containing the state. */
86 struct hconf _res_hconf
;
88 /* Skip white space. */
90 skip_ws (const char *str
)
92 while (isspace (*str
)) ++str
;
97 /* Skip until whitespace, comma, end of line, or comment character. */
99 skip_string (const char *str
)
101 while (*str
&& !isspace (*str
) && *str
!= '#' && *str
!= ',')
108 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
)
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
);
259 if (c
->cb
== CB_arg_trimdomain_list
)
260 str
= arg_trimdomain_list (fname
, line_num
, str
);
261 else if (c
->cb
== CB_arg_spoof
)
262 str
= arg_spoof (fname
, line_num
, str
);
263 else if (c
->cb
== CB_arg_bool
)
264 str
= arg_bool (fname
, line_num
, str
, c
->arg
);
266 /* Ignore the line. */
272 /* rest of line must contain white space or comment only: */
275 if (!isspace (*str
)) {
280 if (__asprintf (&buf
,
281 _("%s: line %d: ignoring trailing garbage `%s'\n"),
282 fname
, line_num
, str
) < 0)
285 __fxprintf (NULL
, "%s", buf
);
299 const char *hconf_name
;
301 char buf
[256], *envval
;
304 memset (&_res_hconf
, '\0', sizeof (_res_hconf
));
306 hconf_name
= getenv (ENV_HOSTCONF
);
307 if (hconf_name
== NULL
)
308 hconf_name
= _PATH_HOSTCONF
;
310 fp
= fopen (hconf_name
, "rc");
313 /* No threads using this stream. */
314 __fsetlocking (fp
, FSETLOCKING_BYCALLER
);
316 while (fgets_unlocked (buf
, sizeof (buf
), fp
))
319 *__strchrnul (buf
, '\n') = '\0';
320 parse_line (hconf_name
, line_num
, buf
);
325 envval
= getenv (ENV_SPOOF
);
327 arg_spoof (ENV_SPOOF
, 1, envval
);
329 envval
= getenv (ENV_MULTI
);
331 arg_bool (ENV_MULTI
, 1, envval
, HCONF_FLAG_MULTI
);
333 envval
= getenv (ENV_REORDER
);
335 arg_bool (ENV_REORDER
, 1, envval
, HCONF_FLAG_REORDER
);
337 envval
= getenv (ENV_TRIM_ADD
);
339 arg_trimdomain_list (ENV_TRIM_ADD
, 1, envval
);
341 envval
= getenv (ENV_TRIM_OVERR
);
344 _res_hconf
.num_trimdomains
= 0;
345 arg_trimdomain_list (ENV_TRIM_OVERR
, 1, envval
);
348 _res_hconf
.initialized
= 1;
352 /* Initialize hconf datastructure by reading host.conf file and
353 environment variables. */
355 _res_hconf_init (void)
357 __libc_once_define (static, once
);
359 __libc_once (once
, do_init
);
364 /* List of known interfaces. */
366 static struct netaddr
379 /* Reorder addresses returned in a hostent such that the first address
380 is an address on the local subnet, if there is such an address.
381 Otherwise, nothing is changed.
383 Note that this function currently only handles IPv4 addresses. */
386 _res_hconf_reorder_addrs (struct hostent
*hp
)
388 #if defined SIOCGIFCONF && defined SIOCGIFNETMASK
390 /* Number of interfaces. */
391 static int num_ifs
= -1;
392 /* We need to protect the dynamic buffer handling. */
393 __libc_lock_define_initialized (static, lock
);
395 /* Only reorder if we're supposed to. */
396 if ((_res_hconf
.flags
& HCONF_FLAG_REORDER
) == 0)
399 /* Can't deal with anything but IPv4 for now... */
400 if (hp
->h_addrtype
!= AF_INET
)
405 struct ifreq
*ifr
, *cur_ifr
;
410 /* Initialize interface table. */
412 /* The SIOCGIFNETMASK ioctl will only work on an AF_INET socket. */
413 sd
= __socket (AF_INET
, SOCK_DGRAM
, 0);
418 __libc_lock_lock (lock
);
420 /* Recheck, somebody else might have done the work by done. */
425 /* Get a list of interfaces. */
426 __ifreq (&ifr
, &num
, sd
);
430 ifaddrs
= malloc (num
* sizeof (ifaddrs
[0]));
434 /* Copy usable interfaces in ifaddrs structure. */
435 for (cur_ifr
= ifr
, i
= 0; i
< num
;
436 cur_ifr
= __if_nextreq (cur_ifr
), ++i
)
438 if (cur_ifr
->ifr_addr
.sa_family
!= AF_INET
)
441 ifaddrs
[new_num_ifs
].addrtype
= AF_INET
;
442 ifaddrs
[new_num_ifs
].u
.ipv4
.addr
=
443 ((struct sockaddr_in
*) &cur_ifr
->ifr_addr
)->sin_addr
.s_addr
;
445 if (__ioctl (sd
, SIOCGIFNETMASK
, cur_ifr
) < 0)
448 ifaddrs
[new_num_ifs
].u
.ipv4
.mask
=
449 ((struct sockaddr_in
*) &cur_ifr
->ifr_netmask
)->sin_addr
.s_addr
;
451 /* Now we're committed to this entry. */
454 /* Just keep enough memory to hold all the interfaces we want. */
455 ifaddrs
= realloc (ifaddrs
, new_num_ifs
* sizeof (ifaddrs
[0]));
456 assert (ifaddrs
!= NULL
);
459 __if_freereq (ifr
, num
);
462 /* Release lock, preserve error value, and close socket. */
465 num_ifs
= new_num_ifs
;
467 __libc_lock_unlock (lock
);
476 /* Find an address for which we have a direct connection. */
477 for (i
= 0; hp
->h_addr_list
[i
]; ++i
)
479 struct in_addr
*haddr
= (struct in_addr
*) hp
->h_addr_list
[i
];
481 for (j
= 0; j
< num_ifs
; ++j
)
483 u_int32_t if_addr
= ifaddrs
[j
].u
.ipv4
.addr
;
484 u_int32_t if_netmask
= ifaddrs
[j
].u
.ipv4
.mask
;
486 if (((haddr
->s_addr
^ if_addr
) & if_netmask
) == 0)
490 tmp
= hp
->h_addr_list
[i
];
491 hp
->h_addr_list
[i
] = hp
->h_addr_list
[0];
492 hp
->h_addr_list
[0] = tmp
;
497 #endif /* defined(SIOCGIFCONF) && ... */
501 /* If HOSTNAME has a postfix matching any of the trimdomains, trim away
502 that postfix. Notice that HOSTNAME is modified inplace. Also, the
503 original code applied all trimdomains in order, meaning that the
504 same domainname could be trimmed multiple times. I believe this
505 was unintentional. */
507 _res_hconf_trim_domain (char *hostname
)
509 size_t hostname_len
, trim_len
;
512 hostname_len
= strlen (hostname
);
514 for (i
= 0; i
< _res_hconf
.num_trimdomains
; ++i
)
516 const char *trim
= _res_hconf
.trimdomain
[i
];
518 trim_len
= strlen (trim
);
519 if (hostname_len
> trim_len
520 && __strcasecmp (&hostname
[hostname_len
- trim_len
], trim
) == 0)
522 hostname
[hostname_len
- trim_len
] = '\0';
529 /* Trim all hostnames/aliases in HP according to the trimdomain list.
530 Notice that HP is modified inplace! */
532 _res_hconf_trim_domains (struct hostent
*hp
)
536 if (_res_hconf
.num_trimdomains
== 0)
539 _res_hconf_trim_domain (hp
->h_name
);
540 for (i
= 0; hp
->h_aliases
[i
]; ++i
)
541 _res_hconf_trim_domain (hp
->h_aliases
[i
]);