]> git.ipfire.org Git - thirdparty/glibc.git/blob - resolv/res_hconf.c
Added <ifaddrs.h> interface with functions `getifaddrs', `freeifaddrs'.
[thirdparty/glibc.git] / resolv / res_hconf.c
1 /* Copyright (C) 1993, 1995-2001, 2002 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
3 Contributed by David Mosberger (davidm@azstarnet.com).
4
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.
9
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.
14
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. */
19
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:
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 */
31
32 #include <errno.h>
33 #include <ctype.h>
34 #include <libintl.h>
35 #include <memory.h>
36 #include <stdio.h>
37 #include <stdio_ext.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <net/if.h>
41 #include <sys/ioctl.h>
42 #include <unistd.h>
43 #include <netinet/in.h>
44 #include <bits/libc-lock.h>
45 #include "ifreq.h"
46 #include "res_hconf.h"
47 #ifdef USE_IN_LIBIO
48 # include <wchar.h>
49 #endif
50
51 #define _PATH_HOSTCONF "/etc/host.conf"
52
53 /* Environment vars that all user to override default behavior: */
54 #define ENV_HOSTCONF "RESOLV_HOST_CONF"
55 #define ENV_SERVORDER "RESOLV_SERV_ORDER"
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
62 static const char *arg_service_list (const char *, int, const char *,
63 unsigned int);
64 static const char *arg_trimdomain_list (const char *, int, const char *,
65 unsigned int);
66 static const char *arg_spoof (const char *, int, const char *, unsigned int);
67 static const char *arg_bool (const char *, int, const char *, unsigned int);
68
69 static struct cmd
70 {
71 const char *name;
72 const char *(*parse_args) (const char * filename, int line_num,
73 const char * args, unsigned int arg);
74 unsigned int arg;
75 } cmd[] =
76 {
77 {"order", arg_service_list, 0},
78 {"trim", arg_trimdomain_list, 0},
79 {"spoof", arg_spoof, 0},
80 {"multi", arg_bool, HCONF_FLAG_MULTI},
81 {"nospoof", arg_bool, HCONF_FLAG_SPOOF},
82 {"spoofalert", arg_bool, HCONF_FLAG_SPOOFALERT},
83 {"reorder", arg_bool, HCONF_FLAG_REORDER}
84 };
85
86 /* Structure containing the state. */
87 struct hconf _res_hconf;
88
89 /* Skip white space. */
90 static const char *
91 skip_ws (const char *str)
92 {
93 while (isspace (*str)) ++str;
94 return str;
95 }
96
97
98 /* Skip until whitespace, comma, end of line, or comment character. */
99 static const char *
100 skip_string (const char *str)
101 {
102 while (*str && !isspace (*str) && *str != '#' && *str != ',')
103 ++str;
104 return str;
105 }
106
107
108 static const char *
109 arg_service_list (const char *fname, int line_num, const char *args,
110 unsigned int arg)
111 {
112 enum Name_Service service;
113 const char *start;
114 size_t len;
115 int i;
116 static struct
117 {
118 const char * name;
119 enum Name_Service service;
120 } svcs[] =
121 {
122 {"bind", SERVICE_BIND},
123 {"hosts", SERVICE_HOSTS},
124 {"nis", SERVICE_NIS},
125 };
126
127 do
128 {
129 start = args;
130 args = skip_string (args);
131 len = args - start;
132
133 service = SERVICE_NONE;
134 for (i = 0; i < sizeof (svcs) / sizeof (svcs[0]); ++i)
135 {
136 if (__strncasecmp (start, svcs[i].name, len) == 0
137 && len == strlen (svcs[i].name))
138 {
139 service = svcs[i].service;
140 break;
141 }
142 }
143 if (service == SERVICE_NONE)
144 {
145 char *buf;
146
147 __asprintf (&buf, _("%s: line %d: expected service, found `%s'\n"),
148 fname, line_num, start);
149
150 #ifdef USE_IN_LIBIO
151 if (_IO_fwide (stderr, 0) > 0)
152 __fwprintf (stderr, L"%s", buf);
153 else
154 #endif
155 fputs (buf, stderr);
156
157 free (buf);
158 return 0;
159 }
160 if (_res_hconf.num_services >= SERVICE_MAX)
161 {
162 char *buf;
163
164 __asprintf (&buf,
165 _("%s: line %d: cannot specify more than %d services"),
166 fname, line_num, SERVICE_MAX);
167
168 #ifdef USE_IN_LIBIO
169 if (_IO_fwide (stderr, 0) > 0)
170 __fwprintf (stderr, L"%s", buf);
171 else
172 #endif
173 fputs (buf, stderr);
174
175 free (buf);
176 return 0;
177 }
178 _res_hconf.service[_res_hconf.num_services++] = service;
179
180 args = skip_ws (args);
181 switch (*args)
182 {
183 case ',':
184 case ';':
185 case ':':
186 args = skip_ws (++args);
187 if (!*args || *args == '#')
188 {
189 char *buf;
190
191 __asprintf (&buf, _("\
192 %s: line %d: list delimiter not followed by keyword"),
193 fname, line_num);
194
195 #ifdef USE_IN_LIBIO
196 if (_IO_fwide (stderr, 0) > 0)
197 __fwprintf (stderr, L"%s", buf);
198 else
199 #endif
200 fputs (buf, stderr);
201
202 free (buf);
203 return 0;
204 }
205 default:
206 break;
207 }
208 }
209 while (*args && *args != '#');
210 return args;
211 }
212
213
214 static const char *
215 arg_trimdomain_list (const char *fname, int line_num, const char *args,
216 unsigned int flag)
217 {
218 const char * start;
219 size_t len;
220
221 do
222 {
223 start = args;
224 args = skip_string (args);
225 len = args - start;
226
227 if (_res_hconf.num_trimdomains >= TRIMDOMAINS_MAX)
228 {
229 char *buf;
230
231 __asprintf (&buf, _("\
232 %s: line %d: cannot specify more than %d trim domains"),
233 fname, line_num, TRIMDOMAINS_MAX);
234
235 #ifdef USE_IN_LIBIO
236 if (_IO_fwide (stderr, 0) > 0)
237 __fwprintf (stderr, L"%s", buf);
238 else
239 #endif
240 fputs (buf, stderr);
241
242 free (buf);
243 return 0;
244 }
245 _res_hconf.trimdomain[_res_hconf.num_trimdomains++] =
246 __strndup (start, len);
247 args = skip_ws (args);
248 switch (*args)
249 {
250 case ',': case ';': case ':':
251 args = skip_ws (++args);
252 if (!*args || *args == '#')
253 {
254 char *buf;
255
256 __asprintf (&buf, _("\
257 %s: line %d: list delimiter not followed by domain"),
258 fname, line_num);
259
260 #ifdef USE_IN_LIBIO
261 if (_IO_fwide (stderr, 0) > 0)
262 __fwprintf (stderr, L"%s", buf);
263 else
264 #endif
265 fputs (buf, stderr);
266
267 free (buf);
268 return 0;
269 }
270 default:
271 break;
272 }
273 }
274 while (*args && *args != '#');
275 return args;
276 }
277
278
279 static const char *
280 arg_spoof (const char *fname, int line_num, const char *args, unsigned flag)
281 {
282 const char *start = args;
283 size_t len;
284
285 args = skip_string (args);
286 len = args - start;
287
288 if (len == 3 && __strncasecmp (start, "off", len) == 0)
289 _res_hconf.flags &= ~(HCONF_FLAG_SPOOF | HCONF_FLAG_SPOOFALERT);
290 else
291 {
292 _res_hconf.flags |= (HCONF_FLAG_SPOOF | HCONF_FLAG_SPOOFALERT);
293 if ((len == 6 && __strncasecmp (start, "nowarn", len) == 0)
294 || !(len == 4 && __strncasecmp (start, "warn", len) == 0))
295 _res_hconf.flags &= ~HCONF_FLAG_SPOOFALERT;
296 }
297 return args;
298 }
299
300
301 static const char *
302 arg_bool (const char *fname, int line_num, const char *args, unsigned flag)
303 {
304 if (__strncasecmp (args, "on", 2) == 0)
305 {
306 args += 2;
307 _res_hconf.flags |= flag;
308 }
309 else if (__strncasecmp (args, "off", 3) == 0)
310 {
311 args += 3;
312 _res_hconf.flags &= ~flag;
313 }
314 else
315 {
316 char *buf;
317
318 __asprintf (&buf,
319 _("%s: line %d: expected `on' or `off', found `%s'\n"),
320 fname, line_num, args);
321
322 #ifdef USE_IN_LIBIO
323 if (_IO_fwide (stderr, 0) > 0)
324 __fwprintf (stderr, L"%s", buf);
325 else
326 #endif
327 fputs (buf, stderr);
328
329 free (buf);
330 return 0;
331 }
332 return args;
333 }
334
335
336 static void
337 parse_line (const char *fname, int line_num, const char *str)
338 {
339 const char *start;
340 struct cmd *c = 0;
341 size_t len;
342 int i;
343
344 str = skip_ws (str);
345
346 /* skip line comment and empty lines: */
347 if (*str == '\0' || *str == '#') return;
348
349 start = str;
350 str = skip_string (str);
351 len = str - start;
352
353 for (i = 0; i < sizeof (cmd) / sizeof (cmd[0]); ++i)
354 {
355 if (__strncasecmp (start, cmd[i].name, len) == 0
356 && strlen (cmd[i].name) == len)
357 {
358 c = &cmd[i];
359 break;
360 }
361 }
362 if (c == NULL)
363 {
364 char *buf;
365
366 __asprintf (&buf, _("%s: line %d: bad command `%s'\n"),
367 fname, line_num, start);
368
369 #ifdef USE_IN_LIBIO
370 if (_IO_fwide (stderr, 0) > 0)
371 __fwprintf (stderr, L"%s", buf);
372 else
373 #endif
374 fputs (buf, stderr);
375
376 free (buf);
377 return;
378 }
379
380 /* process args: */
381 str = skip_ws (str);
382 str = (*c->parse_args) (fname, line_num, str, c->arg);
383 if (!str)
384 return;
385
386 /* rest of line must contain white space or comment only: */
387 while (*str)
388 {
389 if (!isspace (*str)) {
390 if (*str != '#')
391 {
392 char *buf;
393
394 __asprintf (&buf,
395 _("%s: line %d: ignoring trailing garbage `%s'\n"),
396 fname, line_num, str);
397
398 #ifdef USE_IN_LIBIO
399 if (_IO_fwide (stderr, 0) > 0)
400 __fwprintf (stderr, L"%s", buf);
401 else
402 #endif
403 fputs (buf, stderr);
404
405 free (buf);
406 }
407 break;
408 }
409 ++str;
410 }
411 }
412
413
414 static void
415 do_init (void)
416 {
417 const char *hconf_name;
418 int line_num = 0;
419 char buf[256], *envval;
420 FILE *fp;
421
422 memset (&_res_hconf, '\0', sizeof (_res_hconf));
423
424 hconf_name = getenv (ENV_HOSTCONF);
425 if (hconf_name == NULL)
426 hconf_name = _PATH_HOSTCONF;
427
428 fp = fopen (hconf_name, "r");
429 if (!fp)
430 /* make up something reasonable: */
431 _res_hconf.service[_res_hconf.num_services++] = SERVICE_BIND;
432 else
433 {
434 /* No threads using this stream. */
435 __fsetlocking (fp, FSETLOCKING_BYCALLER);
436
437 while (fgets_unlocked (buf, sizeof (buf), fp))
438 {
439 ++line_num;
440 *__strchrnul (buf, '\n') = '\0';
441 parse_line (hconf_name, line_num, buf);
442 }
443 fclose (fp);
444 }
445
446 envval = getenv (ENV_SERVORDER);
447 if (envval)
448 {
449 _res_hconf.num_services = 0;
450 arg_service_list (ENV_SERVORDER, 1, envval, 0);
451 }
452
453 envval = getenv (ENV_SPOOF);
454 if (envval)
455 arg_spoof (ENV_SPOOF, 1, envval, 0);
456
457 envval = getenv (ENV_MULTI);
458 if (envval)
459 arg_bool (ENV_MULTI, 1, envval, HCONF_FLAG_MULTI);
460
461 envval = getenv (ENV_REORDER);
462 if (envval)
463 arg_bool (ENV_REORDER, 1, envval, HCONF_FLAG_REORDER);
464
465 envval = getenv (ENV_TRIM_ADD);
466 if (envval)
467 arg_trimdomain_list (ENV_TRIM_ADD, 1, envval, 0);
468
469 envval = getenv (ENV_TRIM_OVERR);
470 if (envval)
471 {
472 _res_hconf.num_trimdomains = 0;
473 arg_trimdomain_list (ENV_TRIM_OVERR, 1, envval, 0);
474 }
475
476 _res_hconf.initialized = 1;
477 }
478
479
480 /* Initialize hconf datastructure by reading host.conf file and
481 environment variables. */
482 void
483 _res_hconf_init (void)
484 {
485 __libc_once_define (static, once);
486
487 __libc_once (once, do_init);
488 }
489
490
491 /* List of known interfaces. */
492 static struct netaddr
493 {
494 int addrtype;
495 union
496 {
497 struct
498 {
499 u_int32_t addr;
500 u_int32_t mask;
501 } ipv4;
502 } u;
503 } *ifaddrs;
504
505 /* We need to protect the dynamic buffer handling. */
506 __libc_lock_define_initialized (static, lock);
507
508 /* Reorder addresses returned in a hostent such that the first address
509 is an address on the local subnet, if there is such an address.
510 Otherwise, nothing is changed.
511
512 Note that this function currently only handles IPv4 addresses. */
513
514 void
515 _res_hconf_reorder_addrs (struct hostent *hp)
516 {
517 #if defined SIOCGIFCONF && defined SIOCGIFNETMASK
518 int i, j;
519 /* Number of interfaces. */
520 static int num_ifs = -1;
521
522 /* Only reorder if we're supposed to. */
523 if ((_res_hconf.flags & HCONF_FLAG_REORDER) == 0)
524 return;
525
526 /* Can't deal with anything but IPv4 for now... */
527 if (hp->h_addrtype != AF_INET)
528 return;
529
530 if (num_ifs <= 0)
531 {
532 struct ifreq *ifr, *cur_ifr;
533 int sd, num, i;
534 /* Save errno. */
535 int save = errno;
536
537 /* Initialize interface table. */
538
539 num_ifs = 0;
540
541 /* The SIOCGIFNETMASK ioctl will only work on an AF_INET socket. */
542 sd = __socket (AF_INET, SOCK_DGRAM, 0);
543 if (sd < 0)
544 return;
545
546 /* Get lock. */
547 __libc_lock_lock (lock);
548
549 /* Get a list of interfaces. */
550 __ifreq (&ifr, &num, sd);
551 if (!ifr)
552 goto cleanup;
553
554 ifaddrs = malloc (num * sizeof (ifaddrs[0]));
555 if (!ifaddrs)
556 goto cleanup1;
557
558 /* Copy usable interfaces in ifaddrs structure. */
559 for (cur_ifr = ifr, i = 0; i < num; ++cur_ifr, ++i)
560 {
561 if (cur_ifr->ifr_addr.sa_family != AF_INET)
562 continue;
563
564 ifaddrs[num_ifs].addrtype = AF_INET;
565 ifaddrs[num_ifs].u.ipv4.addr =
566 ((struct sockaddr_in *) &cur_ifr->ifr_addr)->sin_addr.s_addr;
567
568 if (__ioctl (sd, SIOCGIFNETMASK, cur_ifr) < 0)
569 continue;
570
571 ifaddrs[num_ifs].u.ipv4.mask =
572 ((struct sockaddr_in *) &cur_ifr->ifr_netmask)->sin_addr.s_addr;
573
574 /* Now we're committed to this entry. */
575 ++num_ifs;
576 }
577 /* Just keep enough memory to hold all the interfaces we want. */
578 ifaddrs = realloc (ifaddrs, num_ifs * sizeof (ifaddrs[0]));
579
580 cleanup1:
581 __if_freereq (ifr, num);
582
583 cleanup:
584 /* Release lock, preserve error value, and close socket. */
585 save = errno;
586 __libc_lock_unlock (lock);
587 __close (sd);
588 }
589
590 if (num_ifs == 0)
591 return;
592
593 /* Find an address for which we have a direct connection. */
594 for (i = 0; hp->h_addr_list[i]; ++i)
595 {
596 struct in_addr *haddr = (struct in_addr *) hp->h_addr_list[i];
597
598 for (j = 0; j < num_ifs; ++j)
599 {
600 u_int32_t if_addr = ifaddrs[j].u.ipv4.addr;
601 u_int32_t if_netmask = ifaddrs[j].u.ipv4.mask;
602
603 if (((haddr->s_addr ^ if_addr) & if_netmask) == 0)
604 {
605 void *tmp;
606
607 tmp = hp->h_addr_list[i];
608 hp->h_addr_list[i] = hp->h_addr_list[0];
609 hp->h_addr_list[0] = tmp;
610 return;
611 }
612 }
613 }
614 #endif /* defined(SIOCGIFCONF) && ... */
615 }
616
617
618 /* If HOSTNAME has a postfix matching any of the trimdomains, trim away
619 that postfix. Notice that HOSTNAME is modified inplace. Also, the
620 original code applied all trimdomains in order, meaning that the
621 same domainname could be trimmed multiple times. I believe this
622 was unintentional. */
623 void
624 _res_hconf_trim_domain (char *hostname)
625 {
626 size_t hostname_len, trim_len;
627 int i;
628
629 hostname_len = strlen (hostname);
630
631 for (i = 0; i < _res_hconf.num_trimdomains; ++i)
632 {
633 const char *trim = _res_hconf.trimdomain[i];
634
635 trim_len = strlen (trim);
636 if (hostname_len > trim_len
637 && __strcasecmp (&hostname[hostname_len - trim_len], trim) == 0)
638 {
639 hostname[hostname_len - trim_len] = '\0';
640 break;
641 }
642 }
643 }
644
645
646 /* Trim all hostnames/aliases in HP according to the trimdomain list.
647 Notice that HP is modified inplace! */
648 void
649 _res_hconf_trim_domains (struct hostent *hp)
650 {
651 int i;
652
653 if (_res_hconf.num_trimdomains == 0)
654 return;
655
656 _res_hconf_trim_domain (hp->h_name);
657 for (i = 0; hp->h_aliases[i]; ++i)
658 _res_hconf_trim_domain (hp->h_aliases[i]);
659 }
660
661
662 /* Free all resources if necessary. */
663 static void __attribute__ ((unused))
664 free_mem (void)
665 {
666 free (ifaddrs);
667 }
668
669 text_set_element (__libc_subfreeres, free_mem);