1 /* Copyright (C) 1993-2013 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"
49 #define _PATH_HOSTCONF "/etc/host.conf"
51 /* Environment vars that all user to override default behavior: */
52 #define ENV_HOSTCONF "RESOLV_HOST_CONF"
53 #define ENV_SPOOF "RESOLV_SPOOF_CHECK"
54 #define ENV_TRIM_OVERR "RESOLV_OVERRIDE_TRIM_DOMAINS"
55 #define ENV_TRIM_ADD "RESOLV_ADD_TRIM_DOMAINS"
56 #define ENV_MULTI "RESOLV_MULTI"
57 #define ENV_REORDER "RESOLV_REORDER"
62 CB_arg_trimdomain_list
,
67 static const struct cmd
74 {"order", CB_none
, 0},
75 {"trim", CB_arg_trimdomain_list
, 0},
76 {"spoof", CB_arg_spoof
, 0},
77 {"multi", CB_arg_bool
, HCONF_FLAG_MULTI
},
78 {"nospoof", CB_arg_bool
, HCONF_FLAG_SPOOF
},
79 {"spoofalert", CB_arg_bool
, HCONF_FLAG_SPOOFALERT
},
80 {"reorder", CB_arg_bool
, HCONF_FLAG_REORDER
}
83 /* Structure containing the state. */
84 struct hconf _res_hconf
;
86 /* Skip white space. */
88 skip_ws (const char *str
)
90 while (isspace (*str
)) ++str
;
95 /* Skip until whitespace, comma, end of line, or comment character. */
97 skip_string (const char *str
)
99 while (*str
&& !isspace (*str
) && *str
!= '#' && *str
!= ',')
106 arg_trimdomain_list (const char *fname
, int line_num
, const char *args
)
114 args
= skip_string (args
);
117 if (_res_hconf
.num_trimdomains
>= TRIMDOMAINS_MAX
)
121 if (__asprintf (&buf
, _("\
122 %s: line %d: cannot specify more than %d trim domains"),
123 fname
, line_num
, TRIMDOMAINS_MAX
) < 0)
126 __fxprintf (NULL
, "%s", buf
);
131 _res_hconf
.trimdomain
[_res_hconf
.num_trimdomains
++] =
132 __strndup (start
, len
);
133 args
= skip_ws (args
);
136 case ',': case ';': case ':':
137 args
= skip_ws (++args
);
138 if (!*args
|| *args
== '#')
142 if (__asprintf (&buf
, _("\
143 %s: line %d: list delimiter not followed by domain"),
144 fname
, line_num
) < 0)
147 __fxprintf (NULL
, "%s", buf
);
156 while (*args
&& *args
!= '#');
162 arg_spoof (const char *fname
, int line_num
, const char *args
)
164 const char *start
= args
;
167 args
= skip_string (args
);
170 if (len
== 3 && __strncasecmp (start
, "off", len
) == 0)
171 _res_hconf
.flags
&= ~(HCONF_FLAG_SPOOF
| HCONF_FLAG_SPOOFALERT
);
174 _res_hconf
.flags
|= (HCONF_FLAG_SPOOF
| HCONF_FLAG_SPOOFALERT
);
175 if ((len
== 6 && __strncasecmp (start
, "nowarn", len
) == 0)
176 || !(len
== 4 && __strncasecmp (start
, "warn", len
) == 0))
177 _res_hconf
.flags
&= ~HCONF_FLAG_SPOOFALERT
;
184 arg_bool (const char *fname
, int line_num
, const char *args
, unsigned flag
)
186 if (__strncasecmp (args
, "on", 2) == 0)
189 _res_hconf
.flags
|= flag
;
191 else if (__strncasecmp (args
, "off", 3) == 0)
194 _res_hconf
.flags
&= ~flag
;
200 if (__asprintf (&buf
,
201 _("%s: line %d: expected `on' or `off', found `%s'\n"),
202 fname
, line_num
, args
) < 0)
205 __fxprintf (NULL
, "%s", buf
);
215 parse_line (const char *fname
, int line_num
, const char *str
)
218 const struct cmd
*c
= 0;
224 /* skip line comment and empty lines: */
225 if (*str
== '\0' || *str
== '#') return;
228 str
= skip_string (str
);
231 for (i
= 0; i
< sizeof (cmd
) / sizeof (cmd
[0]); ++i
)
233 if (__strncasecmp (start
, cmd
[i
].name
, len
) == 0
234 && strlen (cmd
[i
].name
) == len
)
244 if (__asprintf (&buf
, _("%s: line %d: bad command `%s'\n"),
245 fname
, line_num
, start
) < 0)
248 __fxprintf (NULL
, "%s", buf
);
257 if (c
->cb
== CB_arg_trimdomain_list
)
258 str
= arg_trimdomain_list (fname
, line_num
, str
);
259 else if (c
->cb
== CB_arg_spoof
)
260 str
= arg_spoof (fname
, line_num
, str
);
261 else if (c
->cb
== CB_arg_bool
)
262 str
= arg_bool (fname
, line_num
, str
, c
->arg
);
264 /* Ignore the line. */
270 /* rest of line must contain white space or comment only: */
273 if (!isspace (*str
)) {
278 if (__asprintf (&buf
,
279 _("%s: line %d: ignoring trailing garbage `%s'\n"),
280 fname
, line_num
, str
) < 0)
283 __fxprintf (NULL
, "%s", buf
);
297 const char *hconf_name
;
299 char buf
[256], *envval
;
302 memset (&_res_hconf
, '\0', sizeof (_res_hconf
));
304 hconf_name
= getenv (ENV_HOSTCONF
);
305 if (hconf_name
== NULL
)
306 hconf_name
= _PATH_HOSTCONF
;
308 fp
= fopen (hconf_name
, "rce");
311 /* No threads using this stream. */
312 __fsetlocking (fp
, FSETLOCKING_BYCALLER
);
314 while (fgets_unlocked (buf
, sizeof (buf
), fp
))
317 *__strchrnul (buf
, '\n') = '\0';
318 parse_line (hconf_name
, line_num
, buf
);
323 envval
= getenv (ENV_SPOOF
);
325 arg_spoof (ENV_SPOOF
, 1, envval
);
327 envval
= getenv (ENV_MULTI
);
329 arg_bool (ENV_MULTI
, 1, envval
, HCONF_FLAG_MULTI
);
331 envval
= getenv (ENV_REORDER
);
333 arg_bool (ENV_REORDER
, 1, envval
, HCONF_FLAG_REORDER
);
335 envval
= getenv (ENV_TRIM_ADD
);
337 arg_trimdomain_list (ENV_TRIM_ADD
, 1, envval
);
339 envval
= getenv (ENV_TRIM_OVERR
);
342 _res_hconf
.num_trimdomains
= 0;
343 arg_trimdomain_list (ENV_TRIM_OVERR
, 1, envval
);
346 _res_hconf
.initialized
= 1;
350 /* Initialize hconf datastructure by reading host.conf file and
351 environment variables. */
353 _res_hconf_init (void)
355 __libc_once_define (static, once
);
357 __libc_once (once
, do_init
);
362 /* List of known interfaces. */
364 static struct netaddr
377 /* Reorder addresses returned in a hostent such that the first address
378 is an address on the local subnet, if there is such an address.
379 Otherwise, nothing is changed.
381 Note that this function currently only handles IPv4 addresses. */
384 _res_hconf_reorder_addrs (struct hostent
*hp
)
386 #if defined SIOCGIFCONF && defined SIOCGIFNETMASK
388 /* Number of interfaces. */
389 static int num_ifs
= -1;
390 /* We need to protect the dynamic buffer handling. */
391 __libc_lock_define_initialized (static, lock
);
393 /* Only reorder if we're supposed to. */
394 if ((_res_hconf
.flags
& HCONF_FLAG_REORDER
) == 0)
397 /* Can't deal with anything but IPv4 for now... */
398 if (hp
->h_addrtype
!= AF_INET
)
403 struct ifreq
*ifr
, *cur_ifr
;
408 /* Initialize interface table. */
410 /* The SIOCGIFNETMASK ioctl will only work on an AF_INET socket. */
411 sd
= __socket (AF_INET
, SOCK_DGRAM
, 0);
416 __libc_lock_lock (lock
);
418 /* Recheck, somebody else might have done the work by done. */
423 /* Get a list of interfaces. */
424 __ifreq (&ifr
, &num
, sd
);
428 ifaddrs
= malloc (num
* sizeof (ifaddrs
[0]));
432 /* Copy usable interfaces in ifaddrs structure. */
433 for (cur_ifr
= ifr
, i
= 0; i
< num
;
434 cur_ifr
= __if_nextreq (cur_ifr
), ++i
)
436 if (cur_ifr
->ifr_addr
.sa_family
!= AF_INET
)
439 ifaddrs
[new_num_ifs
].addrtype
= AF_INET
;
440 ifaddrs
[new_num_ifs
].u
.ipv4
.addr
=
441 ((struct sockaddr_in
*) &cur_ifr
->ifr_addr
)->sin_addr
.s_addr
;
443 if (__ioctl (sd
, SIOCGIFNETMASK
, cur_ifr
) < 0)
446 ifaddrs
[new_num_ifs
].u
.ipv4
.mask
=
447 ((struct sockaddr_in
*) &cur_ifr
->ifr_netmask
)->sin_addr
.s_addr
;
449 /* Now we're committed to this entry. */
452 /* Just keep enough memory to hold all the interfaces we want. */
453 ifaddrs
= realloc (ifaddrs
, new_num_ifs
* sizeof (ifaddrs
[0]));
454 assert (ifaddrs
!= NULL
);
457 __if_freereq (ifr
, num
);
460 /* Release lock, preserve error value, and close socket. */
463 num_ifs
= new_num_ifs
;
465 __libc_lock_unlock (lock
);
474 /* Find an address for which we have a direct connection. */
475 for (i
= 0; hp
->h_addr_list
[i
]; ++i
)
477 struct in_addr
*haddr
= (struct in_addr
*) hp
->h_addr_list
[i
];
479 for (j
= 0; j
< num_ifs
; ++j
)
481 u_int32_t if_addr
= ifaddrs
[j
].u
.ipv4
.addr
;
482 u_int32_t if_netmask
= ifaddrs
[j
].u
.ipv4
.mask
;
484 if (((haddr
->s_addr
^ if_addr
) & if_netmask
) == 0)
488 tmp
= hp
->h_addr_list
[i
];
489 hp
->h_addr_list
[i
] = hp
->h_addr_list
[0];
490 hp
->h_addr_list
[0] = tmp
;
495 #endif /* defined(SIOCGIFCONF) && ... */
499 /* If HOSTNAME has a postfix matching any of the trimdomains, trim away
500 that postfix. Notice that HOSTNAME is modified inplace. Also, the
501 original code applied all trimdomains in order, meaning that the
502 same domainname could be trimmed multiple times. I believe this
503 was unintentional. */
505 _res_hconf_trim_domain (char *hostname
)
507 size_t hostname_len
, trim_len
;
510 hostname_len
= strlen (hostname
);
512 for (i
= 0; i
< _res_hconf
.num_trimdomains
; ++i
)
514 const char *trim
= _res_hconf
.trimdomain
[i
];
516 trim_len
= strlen (trim
);
517 if (hostname_len
> trim_len
518 && __strcasecmp (&hostname
[hostname_len
- trim_len
], trim
) == 0)
520 hostname
[hostname_len
- trim_len
] = '\0';
527 /* Trim all hostnames/aliases in HP according to the trimdomain list.
528 Notice that HP is modified inplace! */
530 _res_hconf_trim_domains (struct hostent
*hp
)
534 if (_res_hconf
.num_trimdomains
== 0)
537 _res_hconf_trim_domain (hp
->h_name
);
538 for (i
= 0; hp
->h_aliases
[i
]; ++i
)
539 _res_hconf_trim_domain (hp
->h_aliases
[i
]);