1 /* Copyright (C) 1993, 1995-2006, 2007 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"
65 CB_arg_trimdomain_list
,
70 static const struct cmd
77 {"order", CB_none
, 0},
78 {"trim", CB_arg_trimdomain_list
, 0},
79 {"spoof", CB_arg_spoof
, 0},
80 {"multi", CB_arg_bool
, HCONF_FLAG_MULTI
},
81 {"nospoof", CB_arg_bool
, HCONF_FLAG_SPOOF
},
82 {"spoofalert", CB_arg_bool
, HCONF_FLAG_SPOOFALERT
},
83 {"reorder", CB_arg_bool
, HCONF_FLAG_REORDER
}
86 /* Structure containing the state. */
87 struct hconf _res_hconf
;
89 /* Skip white space. */
91 skip_ws (const char *str
)
93 while (isspace (*str
)) ++str
;
98 /* Skip until whitespace, comma, end of line, or comment character. */
100 skip_string (const char *str
)
102 while (*str
&& !isspace (*str
) && *str
!= '#' && *str
!= ',')
109 arg_trimdomain_list (const char *fname
, int line_num
, const char *args
)
117 args
= skip_string (args
);
120 if (_res_hconf
.num_trimdomains
>= TRIMDOMAINS_MAX
)
124 if (__asprintf (&buf
, _("\
125 %s: line %d: cannot specify more than %d trim domains"),
126 fname
, line_num
, TRIMDOMAINS_MAX
) < 0)
129 __fxprintf (NULL
, "%s", buf
);
134 _res_hconf
.trimdomain
[_res_hconf
.num_trimdomains
++] =
135 __strndup (start
, len
);
136 args
= skip_ws (args
);
139 case ',': case ';': case ':':
140 args
= skip_ws (++args
);
141 if (!*args
|| *args
== '#')
145 if (__asprintf (&buf
, _("\
146 %s: line %d: list delimiter not followed by domain"),
147 fname
, line_num
) < 0)
150 __fxprintf (NULL
, "%s", buf
);
159 while (*args
&& *args
!= '#');
165 arg_spoof (const char *fname
, int line_num
, const char *args
)
167 const char *start
= args
;
170 args
= skip_string (args
);
173 if (len
== 3 && __strncasecmp (start
, "off", len
) == 0)
174 _res_hconf
.flags
&= ~(HCONF_FLAG_SPOOF
| HCONF_FLAG_SPOOFALERT
);
177 _res_hconf
.flags
|= (HCONF_FLAG_SPOOF
| HCONF_FLAG_SPOOFALERT
);
178 if ((len
== 6 && __strncasecmp (start
, "nowarn", len
) == 0)
179 || !(len
== 4 && __strncasecmp (start
, "warn", len
) == 0))
180 _res_hconf
.flags
&= ~HCONF_FLAG_SPOOFALERT
;
187 arg_bool (const char *fname
, int line_num
, const char *args
, unsigned flag
)
189 if (__strncasecmp (args
, "on", 2) == 0)
192 _res_hconf
.flags
|= flag
;
194 else if (__strncasecmp (args
, "off", 3) == 0)
197 _res_hconf
.flags
&= ~flag
;
203 if (__asprintf (&buf
,
204 _("%s: line %d: expected `on' or `off', found `%s'\n"),
205 fname
, line_num
, args
) < 0)
208 __fxprintf (NULL
, "%s", buf
);
218 parse_line (const char *fname
, int line_num
, const char *str
)
221 const struct cmd
*c
= 0;
227 /* skip line comment and empty lines: */
228 if (*str
== '\0' || *str
== '#') return;
231 str
= skip_string (str
);
234 for (i
= 0; i
< sizeof (cmd
) / sizeof (cmd
[0]); ++i
)
236 if (__strncasecmp (start
, cmd
[i
].name
, len
) == 0
237 && strlen (cmd
[i
].name
) == len
)
247 if (__asprintf (&buf
, _("%s: line %d: bad command `%s'\n"),
248 fname
, line_num
, start
) < 0)
251 __fxprintf (NULL
, "%s", buf
);
260 if (c
->cb
== CB_arg_trimdomain_list
)
261 str
= arg_trimdomain_list (fname
, line_num
, str
);
262 else if (c
->cb
== CB_arg_spoof
)
263 str
= arg_spoof (fname
, line_num
, str
);
264 else if (c
->cb
== CB_arg_bool
)
265 str
= arg_bool (fname
, line_num
, str
, c
->arg
);
267 /* Ignore the line. */
273 /* rest of line must contain white space or comment only: */
276 if (!isspace (*str
)) {
281 if (__asprintf (&buf
,
282 _("%s: line %d: ignoring trailing garbage `%s'\n"),
283 fname
, line_num
, str
) < 0)
286 __fxprintf (NULL
, "%s", buf
);
300 const char *hconf_name
;
302 char buf
[256], *envval
;
305 memset (&_res_hconf
, '\0', sizeof (_res_hconf
));
307 hconf_name
= getenv (ENV_HOSTCONF
);
308 if (hconf_name
== NULL
)
309 hconf_name
= _PATH_HOSTCONF
;
311 fp
= fopen (hconf_name
, "rc");
314 /* No threads using this stream. */
315 __fsetlocking (fp
, FSETLOCKING_BYCALLER
);
317 while (fgets_unlocked (buf
, sizeof (buf
), fp
))
320 *__strchrnul (buf
, '\n') = '\0';
321 parse_line (hconf_name
, line_num
, buf
);
326 envval
= getenv (ENV_SPOOF
);
328 arg_spoof (ENV_SPOOF
, 1, envval
);
330 envval
= getenv (ENV_MULTI
);
332 arg_bool (ENV_MULTI
, 1, envval
, HCONF_FLAG_MULTI
);
334 envval
= getenv (ENV_REORDER
);
336 arg_bool (ENV_REORDER
, 1, envval
, HCONF_FLAG_REORDER
);
338 envval
= getenv (ENV_TRIM_ADD
);
340 arg_trimdomain_list (ENV_TRIM_ADD
, 1, envval
);
342 envval
= getenv (ENV_TRIM_OVERR
);
345 _res_hconf
.num_trimdomains
= 0;
346 arg_trimdomain_list (ENV_TRIM_OVERR
, 1, envval
);
349 _res_hconf
.initialized
= 1;
353 /* Initialize hconf datastructure by reading host.conf file and
354 environment variables. */
356 _res_hconf_init (void)
358 __libc_once_define (static, once
);
360 __libc_once (once
, do_init
);
365 /* List of known interfaces. */
367 static struct netaddr
380 /* We need to protect the dynamic buffer handling. */
381 __libc_lock_define_initialized (static, lock
);
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;
397 /* Only reorder if we're supposed to. */
398 if ((_res_hconf
.flags
& HCONF_FLAG_REORDER
) == 0)
401 /* Can't deal with anything but IPv4 for now... */
402 if (hp
->h_addrtype
!= AF_INET
)
407 struct ifreq
*ifr
, *cur_ifr
;
412 /* 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 /* Get a list of interfaces. */
425 __ifreq (&ifr
, &num
, sd
);
429 ifaddrs
= malloc (num
* sizeof (ifaddrs
[0]));
433 /* Copy usable interfaces in ifaddrs structure. */
434 for (cur_ifr
= ifr
, i
= 0; i
< num
; cur_ifr
= __if_nextreq (cur_ifr
), ++i
)
436 if (cur_ifr
->ifr_addr
.sa_family
!= AF_INET
)
439 ifaddrs
[num_ifs
].addrtype
= AF_INET
;
440 ifaddrs
[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
[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
, num_ifs
* sizeof (ifaddrs
[0]));
454 assert (ifaddrs
!= NULL
);
457 __if_freereq (ifr
, num
);
460 /* Release lock, preserve error value, and close socket. */
462 __libc_lock_unlock (lock
);
469 /* Find an address for which we have a direct connection. */
470 for (i
= 0; hp
->h_addr_list
[i
]; ++i
)
472 struct in_addr
*haddr
= (struct in_addr
*) hp
->h_addr_list
[i
];
474 for (j
= 0; j
< num_ifs
; ++j
)
476 u_int32_t if_addr
= ifaddrs
[j
].u
.ipv4
.addr
;
477 u_int32_t if_netmask
= ifaddrs
[j
].u
.ipv4
.mask
;
479 if (((haddr
->s_addr
^ if_addr
) & if_netmask
) == 0)
483 tmp
= hp
->h_addr_list
[i
];
484 hp
->h_addr_list
[i
] = hp
->h_addr_list
[0];
485 hp
->h_addr_list
[0] = tmp
;
490 #endif /* defined(SIOCGIFCONF) && ... */
494 /* If HOSTNAME has a postfix matching any of the trimdomains, trim away
495 that postfix. Notice that HOSTNAME is modified inplace. Also, the
496 original code applied all trimdomains in order, meaning that the
497 same domainname could be trimmed multiple times. I believe this
498 was unintentional. */
500 _res_hconf_trim_domain (char *hostname
)
502 size_t hostname_len
, trim_len
;
505 hostname_len
= strlen (hostname
);
507 for (i
= 0; i
< _res_hconf
.num_trimdomains
; ++i
)
509 const char *trim
= _res_hconf
.trimdomain
[i
];
511 trim_len
= strlen (trim
);
512 if (hostname_len
> trim_len
513 && __strcasecmp (&hostname
[hostname_len
- trim_len
], trim
) == 0)
515 hostname
[hostname_len
- trim_len
] = '\0';
522 /* Trim all hostnames/aliases in HP according to the trimdomain list.
523 Notice that HP is modified inplace! */
525 _res_hconf_trim_domains (struct hostent
*hp
)
529 if (_res_hconf
.num_trimdomains
== 0)
532 _res_hconf_trim_domain (hp
->h_name
);
533 for (i
= 0; hp
->h_aliases
[i
]; ++i
)
534 _res_hconf_trim_domain (hp
->h_aliases
[i
]);