Fix warnings in fwscanf / rewind tests.
[glibc.git] / resolv / res_hconf.c
blobcde02293307435358cb76069f683e38ff8a0a8d7
1 /* Copyright (C) 1993-2014 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, see
17 <http://www.gnu.org/licenses/>. */
19 /* This file provides a Linux /etc/host.conf compatible front end to
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:
24 - new command "spoof" takes an arguments like RESOLV_SPOOF_CHECK
25 environment variable (i.e., `off', `nowarn', or `warn').
27 - line comments can appear anywhere (not just at the beginning of
28 a line)
31 #include <assert.h>
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 #include <wchar.h>
49 #define _PATH_HOSTCONF "/etc/host.conf"
51 /* Environment vars that all user to override default behavior: */
52 #define ENV_HOSTCONF "RESOLV_HOST_CONF"
53 #define ENV_SPOOF "RESOLV_SPOOF_CHECK"
54 #define ENV_TRIM_OVERR "RESOLV_OVERRIDE_TRIM_DOMAINS"
55 #define ENV_TRIM_ADD "RESOLV_ADD_TRIM_DOMAINS"
56 #define ENV_MULTI "RESOLV_MULTI"
57 #define ENV_REORDER "RESOLV_REORDER"
59 enum parse_cbs
61 CB_none,
62 CB_arg_trimdomain_list,
63 CB_arg_spoof,
64 CB_arg_bool
67 static const struct cmd
69 const char name[11];
70 uint8_t cb;
71 unsigned int arg;
72 } cmd[] =
74 {"order", CB_none, 0},
75 {"trim", CB_arg_trimdomain_list, 0},
76 {"spoof", CB_arg_spoof, 0},
77 {"multi", CB_arg_bool, HCONF_FLAG_MULTI},
78 {"nospoof", CB_arg_bool, HCONF_FLAG_SPOOF},
79 {"spoofalert", CB_arg_bool, HCONF_FLAG_SPOOFALERT},
80 {"reorder", CB_arg_bool, HCONF_FLAG_REORDER}
83 /* Structure containing the state. */
84 struct hconf _res_hconf;
86 /* Skip white space. */
87 static const char *
88 skip_ws (const char *str)
90 while (isspace (*str)) ++str;
91 return str;
95 /* Skip until whitespace, comma, end of line, or comment character. */
96 static const char *
97 skip_string (const char *str)
99 while (*str && !isspace (*str) && *str != '#' && *str != ',')
100 ++str;
101 return str;
105 static const char *
106 arg_trimdomain_list (const char *fname, int line_num, const char *args)
108 const char * start;
109 size_t len;
113 start = args;
114 args = skip_string (args);
115 len = args - start;
117 if (_res_hconf.num_trimdomains >= TRIMDOMAINS_MAX)
119 char *buf;
121 if (__asprintf (&buf, _("\
122 %s: line %d: cannot specify more than %d trim domains"),
123 fname, line_num, TRIMDOMAINS_MAX) < 0)
124 return 0;
126 __fxprintf (NULL, "%s", buf);
128 free (buf);
129 return 0;
131 _res_hconf.trimdomain[_res_hconf.num_trimdomains++] =
132 __strndup (start, len);
133 args = skip_ws (args);
134 switch (*args)
136 case ',': case ';': case ':':
137 args = skip_ws (++args);
138 if (!*args || *args == '#')
140 char *buf;
142 if (__asprintf (&buf, _("\
143 %s: line %d: list delimiter not followed by domain"),
144 fname, line_num) < 0)
145 return 0;
147 __fxprintf (NULL, "%s", buf);
149 free (buf);
150 return 0;
152 default:
153 break;
156 while (*args && *args != '#');
157 return args;
161 static const char *
162 arg_spoof (const char *fname, int line_num, const char *args)
164 const char *start = args;
165 size_t len;
167 args = skip_string (args);
168 len = args - start;
170 if (len == 3 && __strncasecmp (start, "off", len) == 0)
171 _res_hconf.flags &= ~(HCONF_FLAG_SPOOF | HCONF_FLAG_SPOOFALERT);
172 else
174 _res_hconf.flags |= (HCONF_FLAG_SPOOF | HCONF_FLAG_SPOOFALERT);
175 if ((len == 6 && __strncasecmp (start, "nowarn", len) == 0)
176 || !(len == 4 && __strncasecmp (start, "warn", len) == 0))
177 _res_hconf.flags &= ~HCONF_FLAG_SPOOFALERT;
179 return args;
183 static const char *
184 arg_bool (const char *fname, int line_num, const char *args, unsigned flag)
186 if (__strncasecmp (args, "on", 2) == 0)
188 args += 2;
189 _res_hconf.flags |= flag;
191 else if (__strncasecmp (args, "off", 3) == 0)
193 args += 3;
194 _res_hconf.flags &= ~flag;
196 else
198 char *buf;
200 if (__asprintf (&buf,
201 _("%s: line %d: expected `on' or `off', found `%s'\n"),
202 fname, line_num, args) < 0)
203 return 0;
205 __fxprintf (NULL, "%s", buf);
207 free (buf);
208 return 0;
210 return args;
214 static void
215 parse_line (const char *fname, int line_num, const char *str)
217 const char *start;
218 const struct cmd *c = 0;
219 size_t len;
220 size_t i;
222 str = skip_ws (str);
224 /* skip line comment and empty lines: */
225 if (*str == '\0' || *str == '#') return;
227 start = str;
228 str = skip_string (str);
229 len = str - start;
231 for (i = 0; i < sizeof (cmd) / sizeof (cmd[0]); ++i)
233 if (__strncasecmp (start, cmd[i].name, len) == 0
234 && strlen (cmd[i].name) == len)
236 c = &cmd[i];
237 break;
240 if (c == NULL)
242 char *buf;
244 if (__asprintf (&buf, _("%s: line %d: bad command `%s'\n"),
245 fname, line_num, start) < 0)
246 return;
248 __fxprintf (NULL, "%s", buf);
250 free (buf);
251 return;
254 /* process args: */
255 str = skip_ws (str);
257 if (c->cb == CB_arg_trimdomain_list)
258 str = arg_trimdomain_list (fname, line_num, str);
259 else if (c->cb == CB_arg_spoof)
260 str = arg_spoof (fname, line_num, str);
261 else if (c->cb == CB_arg_bool)
262 str = arg_bool (fname, line_num, str, c->arg);
263 else
264 /* Ignore the line. */
265 return;
267 if (!str)
268 return;
270 /* rest of line must contain white space or comment only: */
271 while (*str)
273 if (!isspace (*str)) {
274 if (*str != '#')
276 char *buf;
278 if (__asprintf (&buf,
279 _("%s: line %d: ignoring trailing garbage `%s'\n"),
280 fname, line_num, str) < 0)
281 break;
283 __fxprintf (NULL, "%s", buf);
285 free (buf);
287 break;
289 ++str;
294 static void
295 do_init (void)
297 const char *hconf_name;
298 int line_num = 0;
299 char buf[256], *envval;
300 FILE *fp;
302 memset (&_res_hconf, '\0', sizeof (_res_hconf));
304 hconf_name = getenv (ENV_HOSTCONF);
305 if (hconf_name == NULL)
306 hconf_name = _PATH_HOSTCONF;
308 fp = fopen (hconf_name, "rce");
309 if (fp)
311 /* No threads using this stream. */
312 __fsetlocking (fp, FSETLOCKING_BYCALLER);
314 while (fgets_unlocked (buf, sizeof (buf), fp))
316 ++line_num;
317 *__strchrnul (buf, '\n') = '\0';
318 parse_line (hconf_name, line_num, buf);
320 fclose (fp);
323 envval = getenv (ENV_SPOOF);
324 if (envval)
325 arg_spoof (ENV_SPOOF, 1, envval);
327 envval = getenv (ENV_MULTI);
328 if (envval)
329 arg_bool (ENV_MULTI, 1, envval, HCONF_FLAG_MULTI);
331 envval = getenv (ENV_REORDER);
332 if (envval)
333 arg_bool (ENV_REORDER, 1, envval, HCONF_FLAG_REORDER);
335 envval = getenv (ENV_TRIM_ADD);
336 if (envval)
337 arg_trimdomain_list (ENV_TRIM_ADD, 1, envval);
339 envval = getenv (ENV_TRIM_OVERR);
340 if (envval)
342 _res_hconf.num_trimdomains = 0;
343 arg_trimdomain_list (ENV_TRIM_OVERR, 1, envval);
346 _res_hconf.initialized = 1;
350 /* Initialize hconf datastructure by reading host.conf file and
351 environment variables. */
352 void
353 _res_hconf_init (void)
355 __libc_once_define (static, once);
357 __libc_once (once, do_init);
361 #if IS_IN (libc)
362 # if defined SIOCGIFCONF && defined SIOCGIFNETMASK
363 /* List of known interfaces. */
364 libc_freeres_ptr (
365 static struct netaddr
367 int addrtype;
368 union
370 struct
372 u_int32_t addr;
373 u_int32_t mask;
374 } ipv4;
375 } u;
376 } *ifaddrs);
377 # endif
379 /* Reorder addresses returned in a hostent such that the first address
380 is an address on the local subnet, if there is such an address.
381 Otherwise, nothing is changed.
383 Note that this function currently only handles IPv4 addresses. */
385 void
386 _res_hconf_reorder_addrs (struct hostent *hp)
388 #if defined SIOCGIFCONF && defined SIOCGIFNETMASK
389 int i, j;
390 /* Number of interfaces. */
391 static int num_ifs = -1;
392 /* We need to protect the dynamic buffer handling. */
393 __libc_lock_define_initialized (static, lock);
395 /* Only reorder if we're supposed to. */
396 if ((_res_hconf.flags & HCONF_FLAG_REORDER) == 0)
397 return;
399 /* Can't deal with anything but IPv4 for now... */
400 if (hp->h_addrtype != AF_INET)
401 return;
403 if (num_ifs <= 0)
405 struct ifreq *ifr, *cur_ifr;
406 int sd, num, i;
407 /* Save errno. */
408 int save = errno;
410 /* Initialize interface table. */
412 /* The SIOCGIFNETMASK ioctl will only work on an AF_INET socket. */
413 sd = __socket (AF_INET, SOCK_DGRAM, 0);
414 if (sd < 0)
415 return;
417 /* Get lock. */
418 __libc_lock_lock (lock);
420 /* Recheck, somebody else might have done the work by done. */
421 if (num_ifs <= 0)
423 int new_num_ifs = 0;
425 /* Get a list of interfaces. */
426 __ifreq (&ifr, &num, sd);
427 if (!ifr)
428 goto cleanup;
430 ifaddrs = malloc (num * sizeof (ifaddrs[0]));
431 if (!ifaddrs)
432 goto cleanup1;
434 /* Copy usable interfaces in ifaddrs structure. */
435 for (cur_ifr = ifr, i = 0; i < num;
436 cur_ifr = __if_nextreq (cur_ifr), ++i)
438 if (cur_ifr->ifr_addr.sa_family != AF_INET)
439 continue;
441 ifaddrs[new_num_ifs].addrtype = AF_INET;
442 ifaddrs[new_num_ifs].u.ipv4.addr =
443 ((struct sockaddr_in *) &cur_ifr->ifr_addr)->sin_addr.s_addr;
445 if (__ioctl (sd, SIOCGIFNETMASK, cur_ifr) < 0)
446 continue;
448 ifaddrs[new_num_ifs].u.ipv4.mask =
449 ((struct sockaddr_in *) &cur_ifr->ifr_netmask)->sin_addr.s_addr;
451 /* Now we're committed to this entry. */
452 ++new_num_ifs;
454 /* Just keep enough memory to hold all the interfaces we want. */
455 ifaddrs = realloc (ifaddrs, new_num_ifs * sizeof (ifaddrs[0]));
456 assert (ifaddrs != NULL);
458 cleanup1:
459 __if_freereq (ifr, num);
461 cleanup:
462 /* Release lock, preserve error value, and close socket. */
463 errno = save;
465 num_ifs = new_num_ifs;
467 __libc_lock_unlock (lock);
470 __close (sd);
473 if (num_ifs == 0)
474 return;
476 /* Find an address for which we have a direct connection. */
477 for (i = 0; hp->h_addr_list[i]; ++i)
479 struct in_addr *haddr = (struct in_addr *) hp->h_addr_list[i];
481 for (j = 0; j < num_ifs; ++j)
483 u_int32_t if_addr = ifaddrs[j].u.ipv4.addr;
484 u_int32_t if_netmask = ifaddrs[j].u.ipv4.mask;
486 if (((haddr->s_addr ^ if_addr) & if_netmask) == 0)
488 void *tmp;
490 tmp = hp->h_addr_list[i];
491 hp->h_addr_list[i] = hp->h_addr_list[0];
492 hp->h_addr_list[0] = tmp;
493 return;
497 #endif /* defined(SIOCGIFCONF) && ... */
501 /* If HOSTNAME has a postfix matching any of the trimdomains, trim away
502 that postfix. Notice that HOSTNAME is modified inplace. Also, the
503 original code applied all trimdomains in order, meaning that the
504 same domainname could be trimmed multiple times. I believe this
505 was unintentional. */
506 void
507 _res_hconf_trim_domain (char *hostname)
509 size_t hostname_len, trim_len;
510 int i;
512 hostname_len = strlen (hostname);
514 for (i = 0; i < _res_hconf.num_trimdomains; ++i)
516 const char *trim = _res_hconf.trimdomain[i];
518 trim_len = strlen (trim);
519 if (hostname_len > trim_len
520 && __strcasecmp (&hostname[hostname_len - trim_len], trim) == 0)
522 hostname[hostname_len - trim_len] = '\0';
523 break;
529 /* Trim all hostnames/aliases in HP according to the trimdomain list.
530 Notice that HP is modified inplace! */
531 void
532 _res_hconf_trim_domains (struct hostent *hp)
534 int i;
536 if (_res_hconf.num_trimdomains == 0)
537 return;
539 _res_hconf_trim_domain (hp->h_name);
540 for (i = 0; hp->h_aliases[i]; ++i)
541 _res_hconf_trim_domain (hp->h_aliases[i]);
543 #endif