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
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
36 #include <stdio_ext.h>
40 #include <sys/ioctl.h>
42 #include <netinet/in.h>
43 #include <bits/libc-lock.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 *,
60 static const char *arg_trimdomain_list (const char *, int, const char *,
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);
68 const char *(*parse_args
) (const char * filename
, int line_num
,
69 const char * args
, unsigned int arg
);
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. */
87 skip_ws (const char *str
)
89 while (isspace (*str
)) ++str
;
94 /* Skip until whitespace, comma, end of line, or comment character. */
96 skip_string (const char *str
)
98 while (*str
&& !isspace (*str
) && *str
!= '#' && *str
!= ',')
105 arg_service_list (const char *fname
, int line_num
, const char *args
,
108 enum Name_Service service
;
115 enum Name_Service service
;
118 {"bind", SERVICE_BIND
},
119 {"hosts", SERVICE_HOSTS
},
120 {"nis", SERVICE_NIS
},
126 args
= skip_string (args
);
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
;
139 if (service
== SERVICE_NONE
)
141 fprintf (stderr
, "%s: line %d: expected service, found `%s'\n",
142 fname
, line_num
, start
);
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
);
151 _res_hconf
.service
[_res_hconf
.num_services
++] = service
;
153 args
= skip_ws (args
);
159 args
= skip_ws (++args
);
160 if (!*args
|| *args
== '#')
163 "%s: line %d: list delimiter not followed by keyword",
171 while (*args
&& *args
!= '#');
177 arg_trimdomain_list (const char *fname
, int line_num
, const char *args
,
186 args
= skip_string (args
);
189 if (_res_hconf
.num_trimdomains
>= TRIMDOMAINS_MAX
)
192 "%s: line %d: cannot specify more than %d trim domains",
193 fname
, line_num
, TRIMDOMAINS_MAX
);
196 _res_hconf
.trimdomain
[_res_hconf
.num_trimdomains
++] =
197 __strndup (start
, len
);
198 args
= skip_ws (args
);
201 case ',': case ';': case ':':
202 args
= skip_ws (++args
);
203 if (!*args
|| *args
== '#')
206 "%s: line %d: list delimiter not followed by domain",
214 while (*args
&& *args
!= '#');
220 arg_spoof (const char *fname
, int line_num
, const char *args
, unsigned flag
)
222 const char *start
= args
;
225 args
= skip_string (args
);
228 if (len
== 3 && __strncasecmp (start
, "off", len
) == 0)
229 _res_hconf
.flags
&= ~(HCONF_FLAG_SPOOF
| HCONF_FLAG_SPOOFALERT
);
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
;
242 arg_bool (const char *fname
, int line_num
, const char *args
, unsigned flag
)
244 if (__strncasecmp (args
, "on", 2) == 0)
247 _res_hconf
.flags
|= flag
;
249 else if (__strncasecmp (args
, "off", 3) == 0)
252 _res_hconf
.flags
&= ~flag
;
256 fprintf (stderr
, "%s: line %d: expected `on' or `off', found `%s'\n",
257 fname
, line_num
, args
);
265 parse_line (const char *fname
, int line_num
, const char *str
)
274 /* skip line comment and empty lines: */
275 if (*str
== '\0' || *str
== '#') return;
278 str
= skip_string (str
);
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
)
292 fprintf (stderr
, "%s: line %d: bad command `%s'\n",
293 fname
, line_num
, start
);
299 str
= (*c
->parse_args
) (fname
, line_num
, str
, c
->arg
);
303 /* rest of line must contain white space or comment only: */
306 if (!isspace (*str
)) {
308 fprintf (stderr
, "%s: line %d: ignoring trailing garbage `%s'\n",
309 fname
, line_num
, str
);
317 /* Initialize hconf datastructure by reading host.conf file and
318 environment variables. */
320 _res_hconf_init (void)
322 const char *hconf_name
;
324 char buf
[256], *envval
;
327 if (_res_hconf
.initialized
)
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");
338 /* make up something reasonable: */
339 _res_hconf
.service
[_res_hconf
.num_services
++] = SERVICE_BIND
;
342 /* No threads using this stream. */
343 __fsetlocking (fp
, FSETLOCKING_BYCALLER
);
345 while (fgets_unlocked (buf
, sizeof (buf
), fp
))
348 *__strchrnul (buf
, '\n') = '\0';
349 parse_line (hconf_name
, line_num
, buf
);
354 envval
= getenv (ENV_SERVORDER
);
357 _res_hconf
.num_services
= 0;
358 arg_service_list (ENV_SERVORDER
, 1, envval
, 0);
361 envval
= getenv (ENV_SPOOF
);
363 arg_spoof (ENV_SPOOF
, 1, envval
, 0);
365 envval
= getenv (ENV_MULTI
);
367 arg_bool (ENV_MULTI
, 1, envval
, HCONF_FLAG_MULTI
);
369 envval
= getenv (ENV_REORDER
);
371 arg_bool (ENV_REORDER
, 1, envval
, HCONF_FLAG_REORDER
);
373 envval
= getenv (ENV_TRIM_ADD
);
375 arg_trimdomain_list (ENV_TRIM_ADD
, 1, envval
, 0);
377 envval
= getenv (ENV_TRIM_OVERR
);
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
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. */
412 _res_hconf_reorder_addrs (struct hostent
*hp
)
414 #if defined SIOCGIFCONF && defined SIOCGIFNETMASK
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)
423 /* Can't deal with anything but IPv4 for now... */
424 if (hp
->h_addrtype
!= AF_INET
)
429 struct ifreq
*ifr
, *cur_ifr
;
434 /* Initialize interface table. */
443 __libc_lock_lock (lock
);
445 /* Get a list of interfaces. */
446 __ifreq (&ifr
, &num
);
450 ifaddrs
= malloc (num
* sizeof (ifaddrs
[0]));
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
)
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)
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. */
473 /* Just keep enough memory to hold all the interfaces we want. */
474 ifaddrs
= realloc (ifaddrs
, num_ifs
* sizeof (ifaddrs
[0]));
480 /* Release lock, preserve error value, and close socket. */
482 __libc_lock_unlock (lock
);
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)
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
;
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. */
520 _res_hconf_trim_domain (char *hostname
)
522 size_t hostname_len
, trim_len
;
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';
542 /* Trim all hostnames/aliases in HP according to the trimdomain list.
543 Notice that HP is modified inplace! */
545 _res_hconf_trim_domains (struct hostent
*hp
)
549 if (_res_hconf
.num_trimdomains
== 0)
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
))
565 text_set_element (__libc_subfreeres
, free_mem
);