1 /* Copyright (C) 1993-2015 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, see
17 <http://www.gnu.org/licenses/>. */
19 /* This file provides a Linux /etc/host.conf compatible front end to
20 the various name resolvers (/etc/hosts, named, NIS server, etc.).
21 Though mostly compatibly, the following differences exist compared
22 to the original implementation:
24 - new command "spoof" takes an arguments like RESOLV_SPOOF_CHECK
25 environment variable (i.e., `off', `nowarn', or `warn').
27 - line comments can appear anywhere (not just at the beginning of
37 #include <stdio_ext.h>
41 #include <sys/ioctl.h>
43 #include <netinet/in.h>
44 #include <bits/libc-lock.h>
46 #include "res_hconf.h"
50 # define fgets_unlocked __fgets_unlocked
53 #define _PATH_HOSTCONF "/etc/host.conf"
55 /* Environment vars that all user to override default behavior: */
56 #define ENV_HOSTCONF "RESOLV_HOST_CONF"
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"
66 CB_arg_trimdomain_list
,
71 static const struct cmd
78 {"order", CB_none
, 0},
79 {"trim", CB_arg_trimdomain_list
, 0},
80 {"spoof", CB_arg_spoof
, 0},
81 {"multi", CB_arg_bool
, HCONF_FLAG_MULTI
},
82 {"nospoof", CB_arg_bool
, HCONF_FLAG_SPOOF
},
83 {"spoofalert", CB_arg_bool
, HCONF_FLAG_SPOOFALERT
},
84 {"reorder", CB_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_trimdomain_list (const char *fname
, int line_num
, const char *args
)
118 args
= skip_string (args
);
121 if (_res_hconf
.num_trimdomains
>= TRIMDOMAINS_MAX
)
125 if (__asprintf (&buf
, _("\
126 %s: line %d: cannot specify more than %d trim domains"),
127 fname
, line_num
, TRIMDOMAINS_MAX
) < 0)
130 __fxprintf (NULL
, "%s", buf
);
135 _res_hconf
.trimdomain
[_res_hconf
.num_trimdomains
++] =
136 __strndup (start
, len
);
137 args
= skip_ws (args
);
140 case ',': case ';': case ':':
141 args
= skip_ws (++args
);
142 if (!*args
|| *args
== '#')
146 if (__asprintf (&buf
, _("\
147 %s: line %d: list delimiter not followed by domain"),
148 fname
, line_num
) < 0)
151 __fxprintf (NULL
, "%s", buf
);
160 while (*args
&& *args
!= '#');
166 arg_spoof (const char *fname
, int line_num
, const char *args
)
168 const char *start
= args
;
171 args
= skip_string (args
);
174 if (len
== 3 && __strncasecmp (start
, "off", len
) == 0)
175 _res_hconf
.flags
&= ~(HCONF_FLAG_SPOOF
| HCONF_FLAG_SPOOFALERT
);
178 _res_hconf
.flags
|= (HCONF_FLAG_SPOOF
| HCONF_FLAG_SPOOFALERT
);
179 if ((len
== 6 && __strncasecmp (start
, "nowarn", len
) == 0)
180 || !(len
== 4 && __strncasecmp (start
, "warn", len
) == 0))
181 _res_hconf
.flags
&= ~HCONF_FLAG_SPOOFALERT
;
188 arg_bool (const char *fname
, int line_num
, const char *args
, unsigned flag
)
190 if (__strncasecmp (args
, "on", 2) == 0)
193 _res_hconf
.flags
|= flag
;
195 else if (__strncasecmp (args
, "off", 3) == 0)
198 _res_hconf
.flags
&= ~flag
;
204 if (__asprintf (&buf
,
205 _("%s: line %d: expected `on' or `off', found `%s'\n"),
206 fname
, line_num
, args
) < 0)
209 __fxprintf (NULL
, "%s", buf
);
219 parse_line (const char *fname
, int line_num
, const char *str
)
222 const struct cmd
*c
= 0;
228 /* skip line comment and empty lines: */
229 if (*str
== '\0' || *str
== '#') return;
232 str
= skip_string (str
);
235 for (i
= 0; i
< sizeof (cmd
) / sizeof (cmd
[0]); ++i
)
237 if (__strncasecmp (start
, cmd
[i
].name
, len
) == 0
238 && strlen (cmd
[i
].name
) == len
)
248 if (__asprintf (&buf
, _("%s: line %d: bad command `%s'\n"),
249 fname
, line_num
, start
) < 0)
252 __fxprintf (NULL
, "%s", buf
);
261 if (c
->cb
== CB_arg_trimdomain_list
)
262 str
= arg_trimdomain_list (fname
, line_num
, str
);
263 else if (c
->cb
== CB_arg_spoof
)
264 str
= arg_spoof (fname
, line_num
, str
);
265 else if (c
->cb
== CB_arg_bool
)
266 str
= arg_bool (fname
, line_num
, str
, c
->arg
);
268 /* Ignore the line. */
274 /* rest of line must contain white space or comment only: */
277 if (!isspace (*str
)) {
282 if (__asprintf (&buf
,
283 _("%s: line %d: ignoring trailing garbage `%s'\n"),
284 fname
, line_num
, str
) < 0)
287 __fxprintf (NULL
, "%s", buf
);
301 const char *hconf_name
;
303 char buf
[256], *envval
;
306 memset (&_res_hconf
, '\0', sizeof (_res_hconf
));
308 hconf_name
= getenv (ENV_HOSTCONF
);
309 if (hconf_name
== NULL
)
310 hconf_name
= _PATH_HOSTCONF
;
312 fp
= fopen (hconf_name
, "rce");
315 /* No threads using this stream. */
316 __fsetlocking (fp
, FSETLOCKING_BYCALLER
);
318 while (fgets_unlocked (buf
, sizeof (buf
), fp
))
321 *__strchrnul (buf
, '\n') = '\0';
322 parse_line (hconf_name
, line_num
, buf
);
327 envval
= getenv (ENV_SPOOF
);
329 arg_spoof (ENV_SPOOF
, 1, envval
);
331 envval
= getenv (ENV_MULTI
);
333 arg_bool (ENV_MULTI
, 1, envval
, HCONF_FLAG_MULTI
);
335 envval
= getenv (ENV_REORDER
);
337 arg_bool (ENV_REORDER
, 1, envval
, HCONF_FLAG_REORDER
);
339 envval
= getenv (ENV_TRIM_ADD
);
341 arg_trimdomain_list (ENV_TRIM_ADD
, 1, envval
);
343 envval
= getenv (ENV_TRIM_OVERR
);
346 _res_hconf
.num_trimdomains
= 0;
347 arg_trimdomain_list (ENV_TRIM_OVERR
, 1, envval
);
350 _res_hconf
.initialized
= 1;
354 /* Initialize hconf datastructure by reading host.conf file and
355 environment variables. */
357 _res_hconf_init (void)
359 __libc_once_define (static, once
);
361 __libc_once (once
, do_init
);
366 # if defined SIOCGIFCONF && defined SIOCGIFNETMASK
367 /* List of known interfaces. */
369 static struct netaddr
383 /* Reorder addresses returned in a hostent such that the first address
384 is an address on the local subnet, if there is such an address.
385 Otherwise, nothing is changed.
387 Note that this function currently only handles IPv4 addresses. */
390 _res_hconf_reorder_addrs (struct hostent
*hp
)
392 #if defined SIOCGIFCONF && defined SIOCGIFNETMASK
394 /* Number of interfaces. */
395 static int num_ifs
= -1;
396 /* We need to protect the dynamic buffer handling. */
397 __libc_lock_define_initialized (static, lock
);
399 /* Only reorder if we're supposed to. */
400 if ((_res_hconf
.flags
& HCONF_FLAG_REORDER
) == 0)
403 /* Can't deal with anything but IPv4 for now... */
404 if (hp
->h_addrtype
!= AF_INET
)
409 struct ifreq
*ifr
, *cur_ifr
;
414 /* Initialize interface table. */
416 /* The SIOCGIFNETMASK ioctl will only work on an AF_INET socket. */
417 sd
= __socket (AF_INET
, SOCK_DGRAM
, 0);
422 __libc_lock_lock (lock
);
424 /* Recheck, somebody else might have done the work by done. */
429 /* Get a list of interfaces. */
430 __ifreq (&ifr
, &num
, sd
);
434 ifaddrs
= malloc (num
* sizeof (ifaddrs
[0]));
438 /* Copy usable interfaces in ifaddrs structure. */
439 for (cur_ifr
= ifr
, i
= 0; i
< num
;
440 cur_ifr
= __if_nextreq (cur_ifr
), ++i
)
442 if (cur_ifr
->ifr_addr
.sa_family
!= AF_INET
)
445 ifaddrs
[new_num_ifs
].addrtype
= AF_INET
;
446 ifaddrs
[new_num_ifs
].u
.ipv4
.addr
=
447 ((struct sockaddr_in
*) &cur_ifr
->ifr_addr
)->sin_addr
.s_addr
;
449 if (__ioctl (sd
, SIOCGIFNETMASK
, cur_ifr
) < 0)
452 ifaddrs
[new_num_ifs
].u
.ipv4
.mask
=
453 ((struct sockaddr_in
*) &cur_ifr
->ifr_netmask
)->sin_addr
.s_addr
;
455 /* Now we're committed to this entry. */
458 /* Just keep enough memory to hold all the interfaces we want. */
459 ifaddrs
= realloc (ifaddrs
, new_num_ifs
* sizeof (ifaddrs
[0]));
460 assert (ifaddrs
!= NULL
);
463 __if_freereq (ifr
, num
);
466 /* Release lock, preserve error value, and close socket. */
469 num_ifs
= new_num_ifs
;
471 __libc_lock_unlock (lock
);
480 /* Find an address for which we have a direct connection. */
481 for (i
= 0; hp
->h_addr_list
[i
]; ++i
)
483 struct in_addr
*haddr
= (struct in_addr
*) hp
->h_addr_list
[i
];
485 for (j
= 0; j
< num_ifs
; ++j
)
487 u_int32_t if_addr
= ifaddrs
[j
].u
.ipv4
.addr
;
488 u_int32_t if_netmask
= ifaddrs
[j
].u
.ipv4
.mask
;
490 if (((haddr
->s_addr
^ if_addr
) & if_netmask
) == 0)
494 tmp
= hp
->h_addr_list
[i
];
495 hp
->h_addr_list
[i
] = hp
->h_addr_list
[0];
496 hp
->h_addr_list
[0] = tmp
;
501 #endif /* defined(SIOCGIFCONF) && ... */
505 /* If HOSTNAME has a postfix matching any of the trimdomains, trim away
506 that postfix. Notice that HOSTNAME is modified inplace. Also, the
507 original code applied all trimdomains in order, meaning that the
508 same domainname could be trimmed multiple times. I believe this
509 was unintentional. */
511 _res_hconf_trim_domain (char *hostname
)
513 size_t hostname_len
, trim_len
;
516 hostname_len
= strlen (hostname
);
518 for (i
= 0; i
< _res_hconf
.num_trimdomains
; ++i
)
520 const char *trim
= _res_hconf
.trimdomain
[i
];
522 trim_len
= strlen (trim
);
523 if (hostname_len
> trim_len
524 && __strcasecmp (&hostname
[hostname_len
- trim_len
], trim
) == 0)
526 hostname
[hostname_len
- trim_len
] = '\0';
533 /* Trim all hostnames/aliases in HP according to the trimdomain list.
534 Notice that HP is modified inplace! */
536 _res_hconf_trim_domains (struct hostent
*hp
)
540 if (_res_hconf
.num_trimdomains
== 0)
543 _res_hconf_trim_domain (hp
->h_name
);
544 for (i
= 0; hp
->h_aliases
[i
]; ++i
)
545 _res_hconf_trim_domain (hp
->h_aliases
[i
]);