(INLINE_SYSCALL): Don't mark asm input operand as clobbered.
[glibc.git] / resolv / res_hconf.c
blobab97e39554cdb8cf36975ae8c9ddb1233b4f29e0
1 /* Copyright (C) 1993,95,96,97,98,99,2000,2001 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
18 02111-1307 USA. */
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
29 a line)
32 #include <errno.h>
33 #include <ctype.h>
34 #include <memory.h>
35 #include <stdio.h>
36 #include <stdio_ext.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <net/if.h>
40 #include <sys/ioctl.h>
41 #include <unistd.h>
42 #include <netinet/in.h>
43 #include <bits/libc-lock.h>
44 #include "ifreq.h"
45 #include "res_hconf.h"
47 #define _PATH_HOSTCONF "/etc/host.conf"
49 /* Environment vars that all user to override default behavior: */
50 #define ENV_HOSTCONF "RESOLV_HOST_CONF"
51 #define ENV_SERVORDER "RESOLV_SERV_ORDER"
52 #define ENV_SPOOF "RESOLV_SPOOF_CHECK"
53 #define ENV_TRIM_OVERR "RESOLV_OVERRIDE_TRIM_DOMAINS"
54 #define ENV_TRIM_ADD "RESOLV_ADD_TRIM_DOMAINS"
55 #define ENV_MULTI "RESOLV_MULTI"
56 #define ENV_REORDER "RESOLV_REORDER"
58 static const char *arg_service_list (const char *, int, const char *,
59 unsigned int);
60 static const char *arg_trimdomain_list (const char *, int, const char *,
61 unsigned int);
62 static const char *arg_spoof (const char *, int, const char *, unsigned int);
63 static const char *arg_bool (const char *, int, const char *, unsigned int);
65 static struct cmd
67 const char *name;
68 const char *(*parse_args) (const char * filename, int line_num,
69 const char * args, unsigned int arg);
70 unsigned int arg;
71 } cmd[] =
73 {"order", arg_service_list, 0},
74 {"trim", arg_trimdomain_list, 0},
75 {"spoof", arg_spoof, 0},
76 {"multi", arg_bool, HCONF_FLAG_MULTI},
77 {"nospoof", arg_bool, HCONF_FLAG_SPOOF},
78 {"spoofalert", arg_bool, HCONF_FLAG_SPOOFALERT},
79 {"reorder", arg_bool, HCONF_FLAG_REORDER}
82 /* Structure containing the state. */
83 struct hconf _res_hconf;
85 /* Skip white space. */
86 static const char *
87 skip_ws (const char *str)
89 while (isspace (*str)) ++str;
90 return str;
94 /* Skip until whitespace, comma, end of line, or comment character. */
95 static const char *
96 skip_string (const char *str)
98 while (*str && !isspace (*str) && *str != '#' && *str != ',')
99 ++str;
100 return str;
104 static const char *
105 arg_service_list (const char *fname, int line_num, const char *args,
106 unsigned int arg)
108 enum Name_Service service;
109 const char *start;
110 size_t len;
111 int i;
112 static struct
114 const char * name;
115 enum Name_Service service;
116 } svcs[] =
118 {"bind", SERVICE_BIND},
119 {"hosts", SERVICE_HOSTS},
120 {"nis", SERVICE_NIS},
125 start = args;
126 args = skip_string (args);
127 len = args - start;
129 service = SERVICE_NONE;
130 for (i = 0; i < sizeof (svcs) / sizeof (svcs[0]); ++i)
132 if (__strncasecmp (start, svcs[i].name, len) == 0
133 && len == strlen (svcs[i].name))
135 service = svcs[i].service;
136 break;
139 if (service == SERVICE_NONE)
141 fprintf (stderr, "%s: line %d: expected service, found `%s'\n",
142 fname, line_num, start);
143 return 0;
145 if (_res_hconf.num_services >= SERVICE_MAX)
147 fprintf (stderr, "%s: line %d: cannot specify more than %d services",
148 fname, line_num, SERVICE_MAX);
149 return 0;
151 _res_hconf.service[_res_hconf.num_services++] = service;
153 args = skip_ws (args);
154 switch (*args)
156 case ',':
157 case ';':
158 case ':':
159 args = skip_ws (++args);
160 if (!*args || *args == '#')
162 fprintf (stderr,
163 "%s: line %d: list delimiter not followed by keyword",
164 fname, line_num);
165 return 0;
167 default:
168 break;
171 while (*args && *args != '#');
172 return args;
176 static const char *
177 arg_trimdomain_list (const char *fname, int line_num, const char *args,
178 unsigned int flag)
180 const char * start;
181 size_t len;
185 start = args;
186 args = skip_string (args);
187 len = args - start;
189 if (_res_hconf.num_trimdomains >= TRIMDOMAINS_MAX)
191 fprintf (stderr,
192 "%s: line %d: cannot specify more than %d trim domains",
193 fname, line_num, TRIMDOMAINS_MAX);
194 return 0;
196 _res_hconf.trimdomain[_res_hconf.num_trimdomains++] =
197 __strndup (start, len);
198 args = skip_ws (args);
199 switch (*args)
201 case ',': case ';': case ':':
202 args = skip_ws (++args);
203 if (!*args || *args == '#')
205 fprintf (stderr,
206 "%s: line %d: list delimiter not followed by domain",
207 fname, line_num);
208 return 0;
210 default:
211 break;
214 while (*args && *args != '#');
215 return args;
219 static const char *
220 arg_spoof (const char *fname, int line_num, const char *args, unsigned flag)
222 const char *start = args;
223 size_t len;
225 args = skip_string (args);
226 len = args - start;
228 if (len == 3 && __strncasecmp (start, "off", len) == 0)
229 _res_hconf.flags &= ~(HCONF_FLAG_SPOOF | HCONF_FLAG_SPOOFALERT);
230 else
232 _res_hconf.flags |= (HCONF_FLAG_SPOOF | HCONF_FLAG_SPOOFALERT);
233 if ((len == 6 && __strncasecmp (start, "nowarn", len) == 0)
234 || !(len == 4 && __strncasecmp (start, "warn", len) == 0))
235 _res_hconf.flags &= ~HCONF_FLAG_SPOOFALERT;
237 return args;
241 static const char *
242 arg_bool (const char *fname, int line_num, const char *args, unsigned flag)
244 if (__strncasecmp (args, "on", 2) == 0)
246 args += 2;
247 _res_hconf.flags |= flag;
249 else if (__strncasecmp (args, "off", 3) == 0)
251 args += 3;
252 _res_hconf.flags &= ~flag;
254 else
256 fprintf (stderr, "%s: line %d: expected `on' or `off', found `%s'\n",
257 fname, line_num, args);
258 return 0;
260 return args;
264 static void
265 parse_line (const char *fname, int line_num, const char *str)
267 const char *start;
268 struct cmd *c = 0;
269 size_t len;
270 int i;
272 str = skip_ws (str);
274 /* skip line comment and empty lines: */
275 if (*str == '\0' || *str == '#') return;
277 start = str;
278 str = skip_string (str);
279 len = str - start;
281 for (i = 0; i < sizeof (cmd) / sizeof (cmd[0]); ++i)
283 if (__strncasecmp (start, cmd[i].name, len) == 0
284 && strlen (cmd[i].name) == len)
286 c = &cmd[i];
287 break;
290 if (c == NULL)
292 fprintf (stderr, "%s: line %d: bad command `%s'\n",
293 fname, line_num, start);
294 return;
297 /* process args: */
298 str = skip_ws (str);
299 str = (*c->parse_args) (fname, line_num, str, c->arg);
300 if (!str)
301 return;
303 /* rest of line must contain white space or comment only: */
304 while (*str)
306 if (!isspace (*str)) {
307 if (*str != '#')
308 fprintf (stderr, "%s: line %d: ignoring trailing garbage `%s'\n",
309 fname, line_num, str);
310 break;
312 ++str;
317 /* Initialize hconf datastructure by reading host.conf file and
318 environment variables. */
319 void
320 _res_hconf_init (void)
322 const char *hconf_name;
323 int line_num = 0;
324 char buf[256], *envval;
325 FILE *fp;
327 if (_res_hconf.initialized)
328 return;
330 memset (&_res_hconf, '\0', sizeof (_res_hconf));
332 hconf_name = getenv (ENV_HOSTCONF);
333 if (hconf_name == NULL)
334 hconf_name = _PATH_HOSTCONF;
336 fp = fopen (hconf_name, "r");
337 if (!fp)
338 /* make up something reasonable: */
339 _res_hconf.service[_res_hconf.num_services++] = SERVICE_BIND;
340 else
342 /* No threads using this stream. */
343 __fsetlocking (fp, FSETLOCKING_BYCALLER);
345 while (fgets_unlocked (buf, sizeof (buf), fp))
347 ++line_num;
348 *__strchrnul (buf, '\n') = '\0';
349 parse_line (hconf_name, line_num, buf);
351 fclose (fp);
354 envval = getenv (ENV_SERVORDER);
355 if (envval)
357 _res_hconf.num_services = 0;
358 arg_service_list (ENV_SERVORDER, 1, envval, 0);
361 envval = getenv (ENV_SPOOF);
362 if (envval)
363 arg_spoof (ENV_SPOOF, 1, envval, 0);
365 envval = getenv (ENV_MULTI);
366 if (envval)
367 arg_bool (ENV_MULTI, 1, envval, HCONF_FLAG_MULTI);
369 envval = getenv (ENV_REORDER);
370 if (envval)
371 arg_bool (ENV_REORDER, 1, envval, HCONF_FLAG_REORDER);
373 envval = getenv (ENV_TRIM_ADD);
374 if (envval)
375 arg_trimdomain_list (ENV_TRIM_ADD, 1, envval, 0);
377 envval = getenv (ENV_TRIM_OVERR);
378 if (envval)
380 _res_hconf.num_trimdomains = 0;
381 arg_trimdomain_list (ENV_TRIM_OVERR, 1, envval, 0);
384 _res_hconf.initialized = 1;
388 /* List of known interfaces. */
389 static struct netaddr
391 int addrtype;
392 union
394 struct
396 u_int32_t addr;
397 u_int32_t mask;
398 } ipv4;
399 } u;
400 } *ifaddrs;
402 /* We need to protect the dynamic buffer handling. */
403 __libc_lock_define_initialized (static, lock);
405 /* Reorder addresses returned in a hostent such that the first address
406 is an address on the local subnet, if there is such an address.
407 Otherwise, nothing is changed.
409 Note that this function currently only handles IPv4 addresses. */
411 void
412 _res_hconf_reorder_addrs (struct hostent *hp)
414 #if defined SIOCGIFCONF && defined SIOCGIFNETMASK
415 int i, j;
416 /* Number of interfaces. */
417 static int num_ifs = -1;
419 /* Only reorder if we're supposed to. */
420 if ((_res_hconf.flags & HCONF_FLAG_REORDER) == 0)
421 return;
423 /* Can't deal with anything but IPv4 for now... */
424 if (hp->h_addrtype != AF_INET)
425 return;
427 if (num_ifs <= 0)
429 struct ifreq *ifr, *cur_ifr;
430 int sd, num, i;
431 /* Save errno. */
432 int save = errno;
434 /* Initialize interface table. */
436 num_ifs = 0;
438 sd = __opensock ();
439 if (sd < 0)
440 return;
442 /* Get lock. */
443 __libc_lock_lock (lock);
445 /* Get a list of interfaces. */
446 __ifreq (&ifr, &num);
447 if (!ifr)
448 goto cleanup;
450 ifaddrs = malloc (num * sizeof (ifaddrs[0]));
451 if (!ifaddrs)
452 goto cleanup1;
454 /* Copy usable interfaces in ifaddrs structure. */
455 for (cur_ifr = ifr, i = 0; i < num; ++cur_ifr, ++i)
457 if (cur_ifr->ifr_addr.sa_family != AF_INET)
458 continue;
460 ifaddrs[num_ifs].addrtype = AF_INET;
461 ifaddrs[num_ifs].u.ipv4.addr =
462 ((struct sockaddr_in *) &cur_ifr->ifr_addr)->sin_addr.s_addr;
464 if (__ioctl (sd, SIOCGIFNETMASK, cur_ifr) < 0)
465 continue;
467 ifaddrs[num_ifs].u.ipv4.mask =
468 ((struct sockaddr_in *) &cur_ifr->ifr_netmask)->sin_addr.s_addr;
470 /* Now we're committed to this entry. */
471 ++num_ifs;
473 /* Just keep enough memory to hold all the interfaces we want. */
474 ifaddrs = realloc (ifaddrs, num_ifs * sizeof (ifaddrs[0]));
476 cleanup1:
477 __if_freereq (ifr);
479 cleanup:
480 /* Release lock, preserve error value, and close socket. */
481 save = errno;
482 __libc_lock_unlock (lock);
483 __close (sd);
486 if (num_ifs == 0)
487 return;
489 /* Find an address for which we have a direct connection. */
490 for (i = 0; hp->h_addr_list[i]; ++i)
492 struct in_addr *haddr = (struct in_addr *) hp->h_addr_list[i];
494 for (j = 0; j < num_ifs; ++j)
496 u_int32_t if_addr = ifaddrs[j].u.ipv4.addr;
497 u_int32_t if_netmask = ifaddrs[j].u.ipv4.mask;
499 if (((haddr->s_addr ^ if_addr) & if_netmask) == 0)
501 void *tmp;
503 tmp = hp->h_addr_list[i];
504 hp->h_addr_list[i] = hp->h_addr_list[0];
505 hp->h_addr_list[0] = tmp;
506 return;
510 #endif /* defined(SIOCGIFCONF) && ... */
514 /* If HOSTNAME has a postfix matching any of the trimdomains, trim away
515 that postfix. Notice that HOSTNAME is modified inplace. Also, the
516 original code applied all trimdomains in order, meaning that the
517 same domainname could be trimmed multiple times. I believe this
518 was unintentional. */
519 void
520 _res_hconf_trim_domain (char *hostname)
522 size_t hostname_len, trim_len;
523 int i;
525 hostname_len = strlen (hostname);
527 for (i = 0; i < _res_hconf.num_trimdomains; ++i)
529 const char *trim = _res_hconf.trimdomain[i];
531 trim_len = strlen (trim);
532 if (hostname_len > trim_len
533 && __strcasecmp (&hostname[hostname_len - trim_len], trim) == 0)
535 hostname[hostname_len - trim_len] = '\0';
536 break;
542 /* Trim all hostnames/aliases in HP according to the trimdomain list.
543 Notice that HP is modified inplace! */
544 void
545 _res_hconf_trim_domains (struct hostent *hp)
547 int i;
549 if (_res_hconf.num_trimdomains == 0)
550 return;
552 _res_hconf_trim_domain (hp->h_name);
553 for (i = 0; hp->h_aliases[i]; ++i)
554 _res_hconf_trim_domain (hp->h_aliases[i]);
558 /* Free all resources if necessary. */
559 static void __attribute__ ((unused))
560 free_mem (void)
562 free (ifaddrs);
565 text_set_element (__libc_subfreeres, free_mem);