]> git.ipfire.org Git - thirdparty/glibc.git/blame - resolv/res_hconf.c
2.5-18.1
[thirdparty/glibc.git] / resolv / res_hconf.c
CommitLineData
0ecb606c 1/* Copyright (C) 1993, 1995-2005, 2006 Free Software Foundation, Inc.
41bdb6e2 2 This file is part of the GNU C Library.
fa0bc87c
RM
3 Contributed by David Mosberger (davidm@azstarnet.com).
4
c84142e8 5 The GNU C Library is free software; you can redistribute it and/or
41bdb6e2
AJ
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.
fa0bc87c 9
c84142e8
UD
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
41bdb6e2 13 Lesser General Public License for more details.
fa0bc87c 14
41bdb6e2
AJ
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
18 02111-1307 USA. */
fa0bc87c
RM
19
20/* This file provides a Linux /etc/host.conf compatible front end to
f720d3d2
UD
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:
fa0bc87c
RM
24
25 - new command "spoof" takes an arguments like RESOLV_SPOOF_CHECK
26 environment variable (i.e., `off', `nowarn', or `warn').
27
28 - line comments can appear anywhere (not just at the beginning of
29 a line)
30*/
f720d3d2 31
9be31a51 32#include <assert.h>
f720d3d2 33#include <errno.h>
fa0bc87c 34#include <ctype.h>
51028f34 35#include <libintl.h>
fa0bc87c
RM
36#include <memory.h>
37#include <stdio.h>
2706ee38 38#include <stdio_ext.h>
fa0bc87c
RM
39#include <stdlib.h>
40#include <string.h>
5edb9387 41#include <net/if.h>
f720d3d2
UD
42#include <sys/ioctl.h>
43#include <unistd.h>
44#include <netinet/in.h>
45#include <bits/libc-lock.h>
46#include "ifreq.h"
fa0bc87c 47#include "res_hconf.h"
51028f34
UD
48#ifdef USE_IN_LIBIO
49# include <wchar.h>
50#endif
fa0bc87c
RM
51
52#define _PATH_HOSTCONF "/etc/host.conf"
53
54/* Environment vars that all user to override default behavior: */
55#define ENV_HOSTCONF "RESOLV_HOST_CONF"
fa0bc87c
RM
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"
61
0ecb606c
JJ
62enum parse_cbs
63 {
64 CB_none,
65 CB_arg_trimdomain_list,
66 CB_arg_spoof,
67 CB_arg_bool
68 };
5edb9387 69
0ecb606c 70static const struct cmd
5edb9387 71{
0ecb606c
JJ
72 const char name[11];
73 uint8_t cb;
5edb9387
UD
74 unsigned int arg;
75} cmd[] =
76{
0ecb606c
JJ
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}
fa0bc87c
RM
84};
85
5edb9387
UD
86/* Structure containing the state. */
87struct hconf _res_hconf;
fa0bc87c 88
fa0bc87c
RM
89/* Skip white space. */
90static const char *
5edb9387 91skip_ws (const char *str)
fa0bc87c
RM
92{
93 while (isspace (*str)) ++str;
94 return str;
95}
96
97
98/* Skip until whitespace, comma, end of line, or comment character. */
99static const char *
5edb9387 100skip_string (const char *str)
fa0bc87c 101{
5edb9387
UD
102 while (*str && !isspace (*str) && *str != '#' && *str != ',')
103 ++str;
fa0bc87c
RM
104 return str;
105}
106
107
108static const char *
0ecb606c 109arg_trimdomain_list (const char *fname, int line_num, const char *args)
fa0bc87c
RM
110{
111 const char * start;
112 size_t len;
113
114 do
115 {
116 start = args;
117 args = skip_string (args);
118 len = args - start;
119
120 if (_res_hconf.num_trimdomains >= TRIMDOMAINS_MAX)
121 {
51028f34
UD
122 char *buf;
123
322861e8 124 if (__asprintf (&buf, _("\
51028f34 125%s: line %d: cannot specify more than %d trim domains"),
322861e8
UD
126 fname, line_num, TRIMDOMAINS_MAX) < 0)
127 return 0;
51028f34 128
0ecb606c 129 __fxprintf (NULL, "%s", buf);
51028f34 130
0ecb606c 131 free (buf);
fa0bc87c
RM
132 return 0;
133 }
134 _res_hconf.trimdomain[_res_hconf.num_trimdomains++] =
5edb9387 135 __strndup (start, len);
fa0bc87c
RM
136 args = skip_ws (args);
137 switch (*args)
138 {
139 case ',': case ';': case ':':
140 args = skip_ws (++args);
141 if (!*args || *args == '#')
142 {
51028f34
UD
143 char *buf;
144
322861e8 145 if (__asprintf (&buf, _("\
51028f34 146%s: line %d: list delimiter not followed by domain"),
322861e8
UD
147 fname, line_num) < 0)
148 return 0;
51028f34 149
0ecb606c 150 __fxprintf (NULL, "%s", buf);
51028f34
UD
151
152 free (buf);
fa0bc87c
RM
153 return 0;
154 }
155 default:
156 break;
157 }
158 }
159 while (*args && *args != '#');
160 return args;
161}
162
163
164static const char *
0ecb606c 165arg_spoof (const char *fname, int line_num, const char *args)
fa0bc87c 166{
5edb9387 167 const char *start = args;
fa0bc87c
RM
168 size_t len;
169
170 args = skip_string (args);
171 len = args - start;
172
5edb9387 173 if (len == 3 && __strncasecmp (start, "off", len) == 0)
fa0bc87c
RM
174 _res_hconf.flags &= ~(HCONF_FLAG_SPOOF | HCONF_FLAG_SPOOFALERT);
175 else
176 {
177 _res_hconf.flags |= (HCONF_FLAG_SPOOF | HCONF_FLAG_SPOOFALERT);
5edb9387
UD
178 if ((len == 6 && __strncasecmp (start, "nowarn", len) == 0)
179 || !(len == 4 && __strncasecmp (start, "warn", len) == 0))
fa0bc87c
RM
180 _res_hconf.flags &= ~HCONF_FLAG_SPOOFALERT;
181 }
182 return args;
183}
184
185
186static const char *
5edb9387 187arg_bool (const char *fname, int line_num, const char *args, unsigned flag)
fa0bc87c 188{
5edb9387 189 if (__strncasecmp (args, "on", 2) == 0)
fa0bc87c
RM
190 {
191 args += 2;
192 _res_hconf.flags |= flag;
193 }
5edb9387 194 else if (__strncasecmp (args, "off", 3) == 0)
fa0bc87c
RM
195 {
196 args += 3;
197 _res_hconf.flags &= ~flag;
198 }
199 else
200 {
51028f34
UD
201 char *buf;
202
322861e8
UD
203 if (__asprintf (&buf,
204 _("%s: line %d: expected `on' or `off', found `%s'\n"),
205 fname, line_num, args) < 0)
206 return 0;
51028f34 207
0ecb606c 208 __fxprintf (NULL, "%s", buf);
51028f34
UD
209
210 free (buf);
fa0bc87c
RM
211 return 0;
212 }
213 return args;
214}
215
216
217static void
5edb9387 218parse_line (const char *fname, int line_num, const char *str)
fa0bc87c 219{
5edb9387 220 const char *start;
0ecb606c 221 const struct cmd *c = 0;
fa0bc87c 222 size_t len;
57b36a0a 223 size_t i;
fa0bc87c
RM
224
225 str = skip_ws (str);
226
6dc25b55
UD
227 /* skip line comment and empty lines: */
228 if (*str == '\0' || *str == '#') return;
fa0bc87c
RM
229
230 start = str;
231 str = skip_string (str);
232 len = str - start;
233
234 for (i = 0; i < sizeof (cmd) / sizeof (cmd[0]); ++i)
235 {
4aebaa6b 236 if (__strncasecmp (start, cmd[i].name, len) == 0
fa0bc87c
RM
237 && strlen (cmd[i].name) == len)
238 {
239 c = &cmd[i];
240 break;
241 }
242 }
5edb9387 243 if (c == NULL)
fa0bc87c 244 {
51028f34
UD
245 char *buf;
246
322861e8
UD
247 if (__asprintf (&buf, _("%s: line %d: bad command `%s'\n"),
248 fname, line_num, start) < 0)
249 return;
51028f34 250
0ecb606c 251 __fxprintf (NULL, "%s", buf);
51028f34
UD
252
253 free (buf);
fa0bc87c
RM
254 return;
255 }
256
257 /* process args: */
258 str = skip_ws (str);
0ecb606c
JJ
259
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);
266 else
267 /* Ignore the line. */
268 return;
269
fa0bc87c
RM
270 if (!str)
271 return;
272
273 /* rest of line must contain white space or comment only: */
274 while (*str)
275 {
276 if (!isspace (*str)) {
277 if (*str != '#')
51028f34
UD
278 {
279 char *buf;
280
322861e8
UD
281 if (__asprintf (&buf,
282 _("%s: line %d: ignoring trailing garbage `%s'\n"),
283 fname, line_num, str) < 0)
284 break;
51028f34 285
0ecb606c 286 __fxprintf (NULL, "%s", buf);
51028f34
UD
287
288 free (buf);
289 }
fa0bc87c
RM
290 break;
291 }
292 ++str;
293 }
294}
295
296
9d957ce2
UD
297static void
298do_init (void)
fa0bc87c 299{
5edb9387 300 const char *hconf_name;
fa0bc87c 301 int line_num = 0;
2c68584c 302 char buf[256], *envval;
5edb9387 303 FILE *fp;
fa0bc87c 304
5edb9387 305 memset (&_res_hconf, '\0', sizeof (_res_hconf));
fa0bc87c 306
74955460 307 hconf_name = getenv (ENV_HOSTCONF);
5edb9387 308 if (hconf_name == NULL)
fa0bc87c
RM
309 hconf_name = _PATH_HOSTCONF;
310
ee8449f7 311 fp = fopen (hconf_name, "rc");
0ecb606c 312 if (fp)
fa0bc87c 313 {
2706ee38
UD
314 /* No threads using this stream. */
315 __fsetlocking (fp, FSETLOCKING_BYCALLER);
316
5edb9387 317 while (fgets_unlocked (buf, sizeof (buf), fp))
fa0bc87c
RM
318 {
319 ++line_num;
c4563d2d 320 *__strchrnul (buf, '\n') = '\0';
fa0bc87c
RM
321 parse_line (hconf_name, line_num, buf);
322 }
323 fclose (fp);
324 }
325
fa0bc87c
RM
326 envval = getenv (ENV_SPOOF);
327 if (envval)
0ecb606c 328 arg_spoof (ENV_SPOOF, 1, envval);
fa0bc87c
RM
329
330 envval = getenv (ENV_MULTI);
331 if (envval)
332 arg_bool (ENV_MULTI, 1, envval, HCONF_FLAG_MULTI);
333
334 envval = getenv (ENV_REORDER);
335 if (envval)
336 arg_bool (ENV_REORDER, 1, envval, HCONF_FLAG_REORDER);
337
338 envval = getenv (ENV_TRIM_ADD);
339 if (envval)
0ecb606c 340 arg_trimdomain_list (ENV_TRIM_ADD, 1, envval);
fa0bc87c
RM
341
342 envval = getenv (ENV_TRIM_OVERR);
343 if (envval)
344 {
345 _res_hconf.num_trimdomains = 0;
0ecb606c 346 arg_trimdomain_list (ENV_TRIM_OVERR, 1, envval);
fa0bc87c 347 }
5edb9387
UD
348
349 _res_hconf.initialized = 1;
fa0bc87c
RM
350}
351
352
9d957ce2
UD
353/* Initialize hconf datastructure by reading host.conf file and
354 environment variables. */
355void
356_res_hconf_init (void)
357{
358 __libc_once_define (static, once);
359
360 __libc_once (once, do_init);
361}
362
363
f720d3d2 364/* List of known interfaces. */
c877418f 365libc_freeres_ptr (
f720d3d2
UD
366static struct netaddr
367{
368 int addrtype;
369 union
370 {
371 struct
372 {
373 u_int32_t addr;
374 u_int32_t mask;
375 } ipv4;
376 } u;
c877418f 377} *ifaddrs);
f720d3d2
UD
378
379/* We need to protect the dynamic buffer handling. */
380__libc_lock_define_initialized (static, lock);
381
fa0bc87c
RM
382/* Reorder addresses returned in a hostent such that the first address
383 is an address on the local subnet, if there is such an address.
f720d3d2
UD
384 Otherwise, nothing is changed.
385
386 Note that this function currently only handles IPv4 addresses. */
fa0bc87c
RM
387
388void
5edb9387 389_res_hconf_reorder_addrs (struct hostent *hp)
fa0bc87c 390{
5edb9387 391#if defined SIOCGIFCONF && defined SIOCGIFNETMASK
f720d3d2
UD
392 int i, j;
393 /* Number of interfaces. */
394 static int num_ifs = -1;
fa0bc87c 395
f720d3d2
UD
396 /* Only reorder if we're supposed to. */
397 if ((_res_hconf.flags & HCONF_FLAG_REORDER) == 0)
398 return;
4aebaa6b 399
f720d3d2 400 /* Can't deal with anything but IPv4 for now... */
fa0bc87c 401 if (hp->h_addrtype != AF_INET)
f720d3d2 402 return;
fa0bc87c
RM
403
404 if (num_ifs <= 0)
405 {
f720d3d2
UD
406 struct ifreq *ifr, *cur_ifr;
407 int sd, num, i;
408 /* Save errno. */
409 int save = errno;
4aebaa6b 410
f720d3d2 411 /* Initialize interface table. */
fa0bc87c
RM
412
413 num_ifs = 0;
414
7f1deee6
RM
415 /* The SIOCGIFNETMASK ioctl will only work on an AF_INET socket. */
416 sd = __socket (AF_INET, SOCK_DGRAM, 0);
fa0bc87c
RM
417 if (sd < 0)
418 return;
419
f720d3d2
UD
420 /* Get lock. */
421 __libc_lock_lock (lock);
fa0bc87c 422
f720d3d2 423 /* Get a list of interfaces. */
7f1deee6 424 __ifreq (&ifr, &num, sd);
f720d3d2
UD
425 if (!ifr)
426 goto cleanup;
fa0bc87c
RM
427
428 ifaddrs = malloc (num * sizeof (ifaddrs[0]));
429 if (!ifaddrs)
f720d3d2 430 goto cleanup1;
4aebaa6b 431
f720d3d2 432 /* Copy usable interfaces in ifaddrs structure. */
9db6ee8d 433 for (cur_ifr = ifr, i = 0; i < num; cur_ifr = __if_nextreq (cur_ifr), ++i)
5edb9387 434 {
f720d3d2 435 if (cur_ifr->ifr_addr.sa_family != AF_INET)
5edb9387 436 continue;
4aebaa6b 437
5edb9387 438 ifaddrs[num_ifs].addrtype = AF_INET;
f720d3d2
UD
439 ifaddrs[num_ifs].u.ipv4.addr =
440 ((struct sockaddr_in *) &cur_ifr->ifr_addr)->sin_addr.s_addr;
fa0bc87c 441
f720d3d2 442 if (__ioctl (sd, SIOCGIFNETMASK, cur_ifr) < 0)
5edb9387 443 continue;
fa0bc87c 444
f720d3d2
UD
445 ifaddrs[num_ifs].u.ipv4.mask =
446 ((struct sockaddr_in *) &cur_ifr->ifr_netmask)->sin_addr.s_addr;
447
448 /* Now we're committed to this entry. */
449 ++num_ifs;
5edb9387 450 }
f720d3d2 451 /* Just keep enough memory to hold all the interfaces we want. */
fa0bc87c 452 ifaddrs = realloc (ifaddrs, num_ifs * sizeof (ifaddrs[0]));
9be31a51 453 assert (ifaddrs != NULL);
fa0bc87c 454
f720d3d2 455 cleanup1:
7f1deee6 456 __if_freereq (ifr, num);
f720d3d2 457
fa0bc87c 458 cleanup:
f720d3d2
UD
459 /* Release lock, preserve error value, and close socket. */
460 save = errno;
461 __libc_lock_unlock (lock);
4aebaa6b 462 __close (sd);
fa0bc87c
RM
463 }
464
465 if (num_ifs == 0)
466 return;
467
f720d3d2 468 /* Find an address for which we have a direct connection. */
fa0bc87c
RM
469 for (i = 0; hp->h_addr_list[i]; ++i)
470 {
f720d3d2 471 struct in_addr *haddr = (struct in_addr *) hp->h_addr_list[i];
fa0bc87c
RM
472
473 for (j = 0; j < num_ifs; ++j)
474 {
f720d3d2
UD
475 u_int32_t if_addr = ifaddrs[j].u.ipv4.addr;
476 u_int32_t if_netmask = ifaddrs[j].u.ipv4.mask;
fa0bc87c 477
f720d3d2 478 if (((haddr->s_addr ^ if_addr) & if_netmask) == 0)
fa0bc87c 479 {
5edb9387 480 void *tmp;
fa0bc87c 481
5edb9387 482 tmp = hp->h_addr_list[i];
fa0bc87c
RM
483 hp->h_addr_list[i] = hp->h_addr_list[0];
484 hp->h_addr_list[0] = tmp;
485 return;
486 }
487 }
488 }
489#endif /* defined(SIOCGIFCONF) && ... */
490}
491
492
493/* If HOSTNAME has a postfix matching any of the trimdomains, trim away
494 that postfix. Notice that HOSTNAME is modified inplace. Also, the
495 original code applied all trimdomains in order, meaning that the
496 same domainname could be trimmed multiple times. I believe this
497 was unintentional. */
498void
5edb9387 499_res_hconf_trim_domain (char *hostname)
fa0bc87c
RM
500{
501 size_t hostname_len, trim_len;
502 int i;
503
5edb9387 504 hostname_len = strlen (hostname);
fa0bc87c
RM
505
506 for (i = 0; i < _res_hconf.num_trimdomains; ++i)
507 {
5edb9387 508 const char *trim = _res_hconf.trimdomain[i];
fa0bc87c 509
5edb9387 510 trim_len = strlen (trim);
fa0bc87c 511 if (hostname_len > trim_len
5edb9387 512 && __strcasecmp (&hostname[hostname_len - trim_len], trim) == 0)
fa0bc87c
RM
513 {
514 hostname[hostname_len - trim_len] = '\0';
515 break;
516 }
517 }
518}
519
520
521/* Trim all hostnames/aliases in HP according to the trimdomain list.
522 Notice that HP is modified inplace! */
523void
5edb9387 524_res_hconf_trim_domains (struct hostent *hp)
fa0bc87c
RM
525{
526 int i;
527
528 if (_res_hconf.num_trimdomains == 0)
529 return;
530
531 _res_hconf_trim_domain (hp->h_name);
532 for (i = 0; hp->h_aliases[i]; ++i)
533 _res_hconf_trim_domain (hp->h_aliases[i]);
534}