]> git.ipfire.org Git - thirdparty/glibc.git/blame - resolv/res_hconf.c
hurd: Fix build
[thirdparty/glibc.git] / resolv / res_hconf.c
CommitLineData
04277e02 1/* Copyright (C) 1993-2019 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 15 You should have received a copy of the GNU Lesser General Public
59ba27a6
PE
16 License along with the GNU C Library; if not, see
17 <http://www.gnu.org/licenses/>. */
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 23
fa0bc87c
RM
24 - line comments can appear anywhere (not just at the beginning of
25 a line)
26*/
f720d3d2 27
9be31a51 28#include <assert.h>
f720d3d2 29#include <errno.h>
fa0bc87c 30#include <ctype.h>
51028f34 31#include <libintl.h>
fa0bc87c
RM
32#include <memory.h>
33#include <stdio.h>
2706ee38 34#include <stdio_ext.h>
fa0bc87c
RM
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>
ec999b8e 41#include <libc-lock.h>
f720d3d2 42#include "ifreq.h"
fa0bc87c 43#include "res_hconf.h"
3ce1f295 44#include <wchar.h>
f463c7b1 45#include <atomic.h>
fa0bc87c 46
84e5e756
JM
47#if IS_IN (libc)
48# define fgets_unlocked __fgets_unlocked
49#endif
50
fa0bc87c
RM
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"
fa0bc87c
RM
55#define ENV_TRIM_OVERR "RESOLV_OVERRIDE_TRIM_DOMAINS"
56#define ENV_TRIM_ADD "RESOLV_ADD_TRIM_DOMAINS"
57#define ENV_MULTI "RESOLV_MULTI"
58#define ENV_REORDER "RESOLV_REORDER"
59
bc054367
UD
60enum parse_cbs
61 {
62 CB_none,
63 CB_arg_trimdomain_list,
bc054367
UD
64 CB_arg_bool
65 };
5edb9387 66
545f1b11 67static const struct cmd
5edb9387 68{
bc054367
UD
69 const char name[11];
70 uint8_t cb;
5edb9387
UD
71 unsigned int arg;
72} cmd[] =
73{
bc054367
UD
74 {"order", CB_none, 0},
75 {"trim", CB_arg_trimdomain_list, 0},
bc054367 76 {"multi", CB_arg_bool, HCONF_FLAG_MULTI},
bc054367 77 {"reorder", CB_arg_bool, HCONF_FLAG_REORDER}
fa0bc87c
RM
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
a334319f 102static const char *
bc054367 103arg_trimdomain_list (const char *fname, int line_num, const char *args)
fa0bc87c
RM
104{
105 const char * start;
106 size_t len;
107
108 do
109 {
110 start = args;
111 args = skip_string (args);
112 len = args - start;
113
114 if (_res_hconf.num_trimdomains >= TRIMDOMAINS_MAX)
115 {
51028f34
UD
116 char *buf;
117
322861e8 118 if (__asprintf (&buf, _("\
51028f34 119%s: line %d: cannot specify more than %d trim domains"),
322861e8
UD
120 fname, line_num, TRIMDOMAINS_MAX) < 0)
121 return 0;
51028f34 122
8a259a23 123 __fxprintf (NULL, "%s", buf);
51028f34 124
df6f8969 125 free (buf);
fa0bc87c
RM
126 return 0;
127 }
128 _res_hconf.trimdomain[_res_hconf.num_trimdomains++] =
5edb9387 129 __strndup (start, len);
fa0bc87c
RM
130 args = skip_ws (args);
131 switch (*args)
132 {
133 case ',': case ';': case ':':
134 args = skip_ws (++args);
135 if (!*args || *args == '#')
136 {
51028f34
UD
137 char *buf;
138
322861e8 139 if (__asprintf (&buf, _("\
51028f34 140%s: line %d: list delimiter not followed by domain"),
322861e8
UD
141 fname, line_num) < 0)
142 return 0;
51028f34 143
8a259a23 144 __fxprintf (NULL, "%s", buf);
51028f34
UD
145
146 free (buf);
fa0bc87c
RM
147 return 0;
148 }
149 default:
150 break;
151 }
152 }
153 while (*args && *args != '#');
154 return args;
155}
156
157
fa0bc87c 158static const char *
5edb9387 159arg_bool (const char *fname, int line_num, const char *args, unsigned flag)
fa0bc87c 160{
5edb9387 161 if (__strncasecmp (args, "on", 2) == 0)
fa0bc87c
RM
162 {
163 args += 2;
164 _res_hconf.flags |= flag;
165 }
5edb9387 166 else if (__strncasecmp (args, "off", 3) == 0)
fa0bc87c
RM
167 {
168 args += 3;
169 _res_hconf.flags &= ~flag;
170 }
171 else
172 {
51028f34
UD
173 char *buf;
174
322861e8
UD
175 if (__asprintf (&buf,
176 _("%s: line %d: expected `on' or `off', found `%s'\n"),
177 fname, line_num, args) < 0)
178 return 0;
51028f34 179
8a259a23 180 __fxprintf (NULL, "%s", buf);
51028f34
UD
181
182 free (buf);
fa0bc87c
RM
183 return 0;
184 }
185 return args;
186}
187
188
189static void
5edb9387 190parse_line (const char *fname, int line_num, const char *str)
fa0bc87c 191{
5edb9387 192 const char *start;
545f1b11 193 const struct cmd *c = 0;
fa0bc87c 194 size_t len;
57b36a0a 195 size_t i;
fa0bc87c
RM
196
197 str = skip_ws (str);
198
6dc25b55
UD
199 /* skip line comment and empty lines: */
200 if (*str == '\0' || *str == '#') return;
fa0bc87c
RM
201
202 start = str;
203 str = skip_string (str);
204 len = str - start;
205
206 for (i = 0; i < sizeof (cmd) / sizeof (cmd[0]); ++i)
207 {
4aebaa6b 208 if (__strncasecmp (start, cmd[i].name, len) == 0
fa0bc87c
RM
209 && strlen (cmd[i].name) == len)
210 {
211 c = &cmd[i];
212 break;
213 }
214 }
5edb9387 215 if (c == NULL)
fa0bc87c 216 {
51028f34
UD
217 char *buf;
218
322861e8
UD
219 if (__asprintf (&buf, _("%s: line %d: bad command `%s'\n"),
220 fname, line_num, start) < 0)
221 return;
51028f34 222
8a259a23 223 __fxprintf (NULL, "%s", buf);
51028f34
UD
224
225 free (buf);
fa0bc87c
RM
226 return;
227 }
228
229 /* process args: */
230 str = skip_ws (str);
bc054367
UD
231
232 if (c->cb == CB_arg_trimdomain_list)
233 str = arg_trimdomain_list (fname, line_num, str);
bc054367
UD
234 else if (c->cb == CB_arg_bool)
235 str = arg_bool (fname, line_num, str, c->arg);
236 else
237 /* Ignore the line. */
238 return;
239
fa0bc87c
RM
240 if (!str)
241 return;
242
243 /* rest of line must contain white space or comment only: */
244 while (*str)
245 {
246 if (!isspace (*str)) {
247 if (*str != '#')
51028f34
UD
248 {
249 char *buf;
250
322861e8
UD
251 if (__asprintf (&buf,
252 _("%s: line %d: ignoring trailing garbage `%s'\n"),
253 fname, line_num, str) < 0)
254 break;
51028f34 255
8a259a23 256 __fxprintf (NULL, "%s", buf);
51028f34
UD
257
258 free (buf);
259 }
fa0bc87c
RM
260 break;
261 }
262 ++str;
263 }
264}
265
266
9d957ce2
UD
267static void
268do_init (void)
fa0bc87c 269{
5edb9387 270 const char *hconf_name;
fa0bc87c 271 int line_num = 0;
2c68584c 272 char buf[256], *envval;
5edb9387 273 FILE *fp;
fa0bc87c 274
5edb9387 275 memset (&_res_hconf, '\0', sizeof (_res_hconf));
fa0bc87c 276
74955460 277 hconf_name = getenv (ENV_HOSTCONF);
5edb9387 278 if (hconf_name == NULL)
fa0bc87c
RM
279 hconf_name = _PATH_HOSTCONF;
280
312be3f9 281 fp = fopen (hconf_name, "rce");
b9c65d09 282 if (fp)
fa0bc87c 283 {
2706ee38
UD
284 /* No threads using this stream. */
285 __fsetlocking (fp, FSETLOCKING_BYCALLER);
286
5edb9387 287 while (fgets_unlocked (buf, sizeof (buf), fp))
fa0bc87c
RM
288 {
289 ++line_num;
c4563d2d 290 *__strchrnul (buf, '\n') = '\0';
fa0bc87c
RM
291 parse_line (hconf_name, line_num, buf);
292 }
293 fclose (fp);
294 }
295
fa0bc87c
RM
296 envval = getenv (ENV_MULTI);
297 if (envval)
298 arg_bool (ENV_MULTI, 1, envval, HCONF_FLAG_MULTI);
299
300 envval = getenv (ENV_REORDER);
301 if (envval)
302 arg_bool (ENV_REORDER, 1, envval, HCONF_FLAG_REORDER);
303
304 envval = getenv (ENV_TRIM_ADD);
305 if (envval)
bc054367 306 arg_trimdomain_list (ENV_TRIM_ADD, 1, envval);
fa0bc87c
RM
307
308 envval = getenv (ENV_TRIM_OVERR);
309 if (envval)
310 {
311 _res_hconf.num_trimdomains = 0;
bc054367 312 arg_trimdomain_list (ENV_TRIM_OVERR, 1, envval);
fa0bc87c 313 }
5edb9387 314
6f9d4f59
TR
315 /* See comments on the declaration of _res_hconf. */
316 atomic_store_release (&_res_hconf.initialized, 1);
fa0bc87c
RM
317}
318
319
9d957ce2
UD
320/* Initialize hconf datastructure by reading host.conf file and
321 environment variables. */
322void
323_res_hconf_init (void)
324{
325 __libc_once_define (static, once);
326
327 __libc_once (once, do_init);
328}
329
330
4f41c682 331#if IS_IN (libc)
f890a59b 332# if defined SIOCGIFCONF && defined SIOCGIFNETMASK
f720d3d2 333/* List of known interfaces. */
c877418f 334libc_freeres_ptr (
f720d3d2
UD
335static struct netaddr
336{
337 int addrtype;
338 union
339 {
340 struct
341 {
d9fee042
JM
342 uint32_t addr;
343 uint32_t mask;
f720d3d2
UD
344 } ipv4;
345 } u;
c877418f 346} *ifaddrs);
f890a59b 347# endif
f720d3d2 348
fa0bc87c
RM
349/* Reorder addresses returned in a hostent such that the first address
350 is an address on the local subnet, if there is such an address.
f720d3d2
UD
351 Otherwise, nothing is changed.
352
353 Note that this function currently only handles IPv4 addresses. */
fa0bc87c
RM
354
355void
5edb9387 356_res_hconf_reorder_addrs (struct hostent *hp)
fa0bc87c 357{
5edb9387 358#if defined SIOCGIFCONF && defined SIOCGIFNETMASK
f720d3d2 359 int i, j;
f463c7b1
FW
360 /* Number of interfaces. Also serves as a flag for the
361 double-checked locking idiom. */
f720d3d2 362 static int num_ifs = -1;
f463c7b1
FW
363 /* Local copy of num_ifs, for non-atomic access. */
364 int num_ifs_local;
365 /* We need to protect the dynamic buffer handling. The lock is only
366 acquired during initialization. Afterwards, a positive num_ifs
367 value indicates completed initialization. */
5c3a3dba 368 __libc_lock_define_initialized (static, lock);
fa0bc87c 369
f720d3d2
UD
370 /* Only reorder if we're supposed to. */
371 if ((_res_hconf.flags & HCONF_FLAG_REORDER) == 0)
372 return;
4aebaa6b 373
f720d3d2 374 /* Can't deal with anything but IPv4 for now... */
fa0bc87c 375 if (hp->h_addrtype != AF_INET)
f720d3d2 376 return;
fa0bc87c 377
f463c7b1
FW
378 /* This load synchronizes with the release MO store in the
379 initialization block below. */
380 num_ifs_local = atomic_load_acquire (&num_ifs);
381 if (num_ifs_local <= 0)
fa0bc87c 382 {
f720d3d2
UD
383 struct ifreq *ifr, *cur_ifr;
384 int sd, num, i;
385 /* Save errno. */
386 int save = errno;
4aebaa6b 387
f720d3d2 388 /* Initialize interface table. */
fa0bc87c 389
7f1deee6 390 /* The SIOCGIFNETMASK ioctl will only work on an AF_INET socket. */
2f83a729 391 sd = __socket (AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
fa0bc87c
RM
392 if (sd < 0)
393 return;
394
f720d3d2
UD
395 /* Get lock. */
396 __libc_lock_lock (lock);
fa0bc87c 397
f463c7b1
FW
398 /* Recheck, somebody else might have done the work by now. No
399 ordering is required for the load because we have the lock,
400 and num_ifs is only updated under the lock. Also see (3) in
401 the analysis below. */
402 num_ifs_local = atomic_load_relaxed (&num_ifs);
403 if (num_ifs_local <= 0)
5c3a3dba 404 {
f463c7b1
FW
405 /* This is the only block which writes to num_ifs. It can
406 be executed several times (sequentially) if
407 initialization does not yield any interfaces, and num_ifs
408 remains zero. However, once we stored a positive value
409 in num_ifs below, this block cannot be entered again due
410 to the condition above. */
5c3a3dba 411 int new_num_ifs = 0;
fa0bc87c 412
5c3a3dba
UD
413 /* Get a list of interfaces. */
414 __ifreq (&ifr, &num, sd);
415 if (!ifr)
416 goto cleanup;
4aebaa6b 417
5c3a3dba
UD
418 ifaddrs = malloc (num * sizeof (ifaddrs[0]));
419 if (!ifaddrs)
420 goto cleanup1;
4aebaa6b 421
5c3a3dba
UD
422 /* Copy usable interfaces in ifaddrs structure. */
423 for (cur_ifr = ifr, i = 0; i < num;
424 cur_ifr = __if_nextreq (cur_ifr), ++i)
425 {
2483fa85
SE
426 union
427 {
428 struct sockaddr sa;
429 struct sockaddr_in sin;
430 } ss;
431
5c3a3dba
UD
432 if (cur_ifr->ifr_addr.sa_family != AF_INET)
433 continue;
fa0bc87c 434
5c3a3dba 435 ifaddrs[new_num_ifs].addrtype = AF_INET;
2483fa85
SE
436 ss.sa = cur_ifr->ifr_addr;
437 ifaddrs[new_num_ifs].u.ipv4.addr = ss.sin.sin_addr.s_addr;
fa0bc87c 438
5c3a3dba
UD
439 if (__ioctl (sd, SIOCGIFNETMASK, cur_ifr) < 0)
440 continue;
f720d3d2 441
2483fa85
SE
442 ss.sa = cur_ifr->ifr_netmask;
443 ifaddrs[new_num_ifs].u.ipv4.mask = ss.sin.sin_addr.s_addr;
fa0bc87c 444
5c3a3dba
UD
445 /* Now we're committed to this entry. */
446 ++new_num_ifs;
447 }
448 /* Just keep enough memory to hold all the interfaces we want. */
449 ifaddrs = realloc (ifaddrs, new_num_ifs * sizeof (ifaddrs[0]));
450 assert (ifaddrs != NULL);
451
452 cleanup1:
453 __if_freereq (ifr, num);
454
455 cleanup:
456 /* Release lock, preserve error value, and close socket. */
5615eaf2 457 errno = save;
5c3a3dba 458
f463c7b1
FW
459 /* Advertise successful initialization if new_num_ifs is
460 positive (and no updates to ifaddrs are permitted after
461 that). Otherwise, num_ifs remains unchanged, at zero.
462 This store synchronizes with the initial acquire MO
463 load. */
464 atomic_store_release (&num_ifs, new_num_ifs);
465 /* Keep the local copy current, to save another load. */
466 num_ifs_local = new_num_ifs;
5c3a3dba 467 }
f720d3d2 468
b57525f1
DL
469 __libc_lock_unlock (lock);
470
4aebaa6b 471 __close (sd);
fa0bc87c
RM
472 }
473
f463c7b1
FW
474 /* num_ifs_local cannot be negative because the if statement above
475 covered this case. It can still be zero if we just performed
476 initialization, but could not find any interfaces. */
477 if (num_ifs_local == 0)
fa0bc87c
RM
478 return;
479
f463c7b1
FW
480 /* The code below accesses ifaddrs, so we need to ensure that the
481 initialization happens-before this point.
482
483 The actual initialization is sequenced-before the release store
484 to num_ifs, and sequenced-before the end of the critical section.
485
486 This means there are three possible executions:
487
488 (1) The thread that initialized the data also uses it, so
489 sequenced-before is sufficient to ensure happens-before.
490
491 (2) The release MO store of num_ifs synchronizes-with the acquire
492 MO load, and the acquire MO load is sequenced before the use
493 of the initialized data below.
494
495 (3) We enter the critical section, and the relaxed MO load of
496 num_ifs yields a positive value. The write to ifaddrs is
497 sequenced-before leaving the critical section. Leaving the
498 critical section happens-before we entered the critical
499 section ourselves, which means that the write to ifaddrs
500 happens-before this point.
501
502 Consequently, all potential writes to ifaddrs (and the data it
503 points to) happens-before this point. */
504
f720d3d2 505 /* Find an address for which we have a direct connection. */
fa0bc87c
RM
506 for (i = 0; hp->h_addr_list[i]; ++i)
507 {
f720d3d2 508 struct in_addr *haddr = (struct in_addr *) hp->h_addr_list[i];
fa0bc87c 509
f463c7b1 510 for (j = 0; j < num_ifs_local; ++j)
fa0bc87c 511 {
d9fee042
JM
512 uint32_t if_addr = ifaddrs[j].u.ipv4.addr;
513 uint32_t if_netmask = ifaddrs[j].u.ipv4.mask;
fa0bc87c 514
f720d3d2 515 if (((haddr->s_addr ^ if_addr) & if_netmask) == 0)
fa0bc87c 516 {
5edb9387 517 void *tmp;
fa0bc87c 518
5edb9387 519 tmp = hp->h_addr_list[i];
fa0bc87c
RM
520 hp->h_addr_list[i] = hp->h_addr_list[0];
521 hp->h_addr_list[0] = tmp;
522 return;
523 }
524 }
525 }
526#endif /* defined(SIOCGIFCONF) && ... */
527}
528
529
530/* If HOSTNAME has a postfix matching any of the trimdomains, trim away
531 that postfix. Notice that HOSTNAME is modified inplace. Also, the
532 original code applied all trimdomains in order, meaning that the
533 same domainname could be trimmed multiple times. I believe this
534 was unintentional. */
535void
5edb9387 536_res_hconf_trim_domain (char *hostname)
fa0bc87c
RM
537{
538 size_t hostname_len, trim_len;
539 int i;
540
5edb9387 541 hostname_len = strlen (hostname);
fa0bc87c
RM
542
543 for (i = 0; i < _res_hconf.num_trimdomains; ++i)
544 {
5edb9387 545 const char *trim = _res_hconf.trimdomain[i];
fa0bc87c 546
5edb9387 547 trim_len = strlen (trim);
fa0bc87c 548 if (hostname_len > trim_len
4c8b8cc3 549 && __strcasecmp (&hostname[hostname_len - trim_len], trim) == 0)
fa0bc87c
RM
550 {
551 hostname[hostname_len - trim_len] = '\0';
552 break;
553 }
554 }
555}
556
557
558/* Trim all hostnames/aliases in HP according to the trimdomain list.
559 Notice that HP is modified inplace! */
560void
5edb9387 561_res_hconf_trim_domains (struct hostent *hp)
fa0bc87c
RM
562{
563 int i;
564
565 if (_res_hconf.num_trimdomains == 0)
566 return;
567
568 _res_hconf_trim_domain (hp->h_name);
569 for (i = 0; hp->h_aliases[i]; ++i)
570 _res_hconf_trim_domain (hp->h_aliases[i]);
571}
1ce7d80d 572#endif