]>
Commit | Line | Data |
---|---|---|
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 |
56 | static const char *arg_service_list (const char *, int, const char *, |
57 | unsigned int); | |
58 | static const char *arg_trimdomain_list (const char *, int, const char *, | |
59 | unsigned int); | |
60 | static const char *arg_spoof (const char *, int, const char *, unsigned int); | |
61 | static const char *arg_bool (const char *, int, const char *, unsigned int); | |
62 | ||
63 | static 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. */ |
81 | struct hconf _res_hconf; | |
fa0bc87c | 82 | |
fa0bc87c RM |
83 | /* Skip white space. */ |
84 | static const char * | |
5edb9387 | 85 | skip_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. */ | |
93 | static const char * | |
5edb9387 | 94 | skip_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 | ||
102 | static const char * | |
5edb9387 UD |
103 | arg_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 | ||
174 | static const char * | |
5edb9387 UD |
175 | arg_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 | ||
217 | static const char * | |
5edb9387 | 218 | arg_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 | ||
239 | static const char * | |
5edb9387 | 240 | arg_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 | ||
262 | static void | |
5edb9387 | 263 | parse_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. */ | |
317 | void | |
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. */ |
384 | static 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 | |
406 | void | |
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. */ | |
514 | void | |
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! */ | |
539 | void | |
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. */ | |
554 | static void __attribute__ ((unused)) | |
555 | free_mem (void) | |
556 | { | |
557 | if (ifaddrs != NULL) | |
558 | free (ifaddrs); | |
559 | } | |
560 | ||
561 | text_set_element (__libc_subfreeres, free_mem); |