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