1 /* Copyright (C) 1993, 95, 96, 97, 98, 99 Free Software Foundation, Inc.
2 Contributed by David Mosberger (davidm@azstarnet.com).
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.
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.
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. */
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
38 #include <sys/ioctl.h>
40 #include <netinet/in.h>
41 #include <bits/libc-lock.h>
43 #include "res_hconf.h"
45 #define _PATH_HOSTCONF "/etc/host.conf"
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"
56 static const char *arg_service_list (const char *, int, const char *,
58 static const char *arg_trimdomain_list (const char *, int, const char *,
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);
66 const char *(*parse_args
) (const char * filename
, int line_num
,
67 const char * args
, unsigned int arg
);
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
}
80 /* Structure containing the state. */
81 struct hconf _res_hconf
;
83 /* Skip white space. */
85 skip_ws (const char *str
)
87 while (isspace (*str
)) ++str
;
92 /* Skip until whitespace, comma, end of line, or comment character. */
94 skip_string (const char *str
)
96 while (*str
&& !isspace (*str
) && *str
!= '#' && *str
!= ',')
103 arg_service_list (const char *fname
, int line_num
, const char *args
,
106 enum Name_Service service
;
113 enum Name_Service service
;
116 {"bind", SERVICE_BIND
},
117 {"hosts", SERVICE_HOSTS
},
118 {"nis", SERVICE_NIS
},
124 args
= skip_string (args
);
127 service
= SERVICE_NONE
;
128 for (i
= 0; i
< sizeof (svcs
) / sizeof (svcs
[0]); ++i
)
130 if (__strncasecmp (start
, svcs
[i
].name
, len
) == 0
131 && len
== strlen (svcs
[i
].name
))
133 service
= svcs
[i
].service
;
137 if (service
== SERVICE_NONE
)
139 fprintf (stderr
, "%s: line %d: expected service, found `%s'\n",
140 fname
, line_num
, start
);
143 if (_res_hconf
.num_services
>= SERVICE_MAX
)
145 fprintf (stderr
, "%s: line %d: cannot specify more than %d services",
146 fname
, line_num
, SERVICE_MAX
);
149 _res_hconf
.service
[_res_hconf
.num_services
++] = service
;
151 args
= skip_ws (args
);
157 args
= skip_ws (++args
);
158 if (!*args
|| *args
== '#')
161 "%s: line %d: list delimiter not followed by keyword",
169 while (*args
&& *args
!= '#');
175 arg_trimdomain_list (const char *fname
, int line_num
, const char *args
,
184 args
= skip_string (args
);
187 if (_res_hconf
.num_trimdomains
>= TRIMDOMAINS_MAX
)
190 "%s: line %d: cannot specify more than %d trim domains",
191 fname
, line_num
, TRIMDOMAINS_MAX
);
194 _res_hconf
.trimdomain
[_res_hconf
.num_trimdomains
++] =
195 __strndup (start
, len
);
196 args
= skip_ws (args
);
199 case ',': case ';': case ':':
200 args
= skip_ws (++args
);
201 if (!*args
|| *args
== '#')
204 "%s: line %d: list delimiter not followed by domain",
212 while (*args
&& *args
!= '#');
218 arg_spoof (const char *fname
, int line_num
, const char *args
, unsigned flag
)
220 const char *start
= args
;
223 args
= skip_string (args
);
226 if (len
== 3 && __strncasecmp (start
, "off", len
) == 0)
227 _res_hconf
.flags
&= ~(HCONF_FLAG_SPOOF
| HCONF_FLAG_SPOOFALERT
);
230 _res_hconf
.flags
|= (HCONF_FLAG_SPOOF
| HCONF_FLAG_SPOOFALERT
);
231 if ((len
== 6 && __strncasecmp (start
, "nowarn", len
) == 0)
232 || !(len
== 4 && __strncasecmp (start
, "warn", len
) == 0))
233 _res_hconf
.flags
&= ~HCONF_FLAG_SPOOFALERT
;
240 arg_bool (const char *fname
, int line_num
, const char *args
, unsigned flag
)
242 if (__strncasecmp (args
, "on", 2) == 0)
245 _res_hconf
.flags
|= flag
;
247 else if (__strncasecmp (args
, "off", 3) == 0)
250 _res_hconf
.flags
&= ~flag
;
254 fprintf (stderr
, "%s: line %d: expected `on' or `off', found `%s'\n",
255 fname
, line_num
, args
);
263 parse_line (const char *fname
, int line_num
, const char *str
)
272 /* skip line comment and empty lines: */
273 if (*str
== '\0' || *str
== '#') return;
276 str
= skip_string (str
);
279 for (i
= 0; i
< sizeof (cmd
) / sizeof (cmd
[0]); ++i
)
281 if (strncasecmp (start
, cmd
[i
].name
, len
) == 0
282 && strlen (cmd
[i
].name
) == len
)
290 fprintf (stderr
, "%s: line %d: bad command `%s'\n",
291 fname
, line_num
, start
);
297 str
= (*c
->parse_args
) (fname
, line_num
, str
, c
->arg
);
301 /* rest of line must contain white space or comment only: */
304 if (!isspace (*str
)) {
306 fprintf (stderr
, "%s: line %d: ignoring trailing garbage `%s'\n",
307 fname
, line_num
, str
);
315 /* Initialize hconf datastructure by reading host.conf file and
316 environment variables. */
318 _res_hconf_init (void)
320 const char *hconf_name
;
322 char buf
[256], *envval
;
325 if (_res_hconf
.initialized
)
328 memset (&_res_hconf
, '\0', sizeof (_res_hconf
));
330 hconf_name
= __secure_getenv (ENV_HOSTCONF
);
331 if (hconf_name
== NULL
)
332 hconf_name
= _PATH_HOSTCONF
;
334 fp
= fopen (hconf_name
, "r");
336 /* make up something reasonable: */
337 _res_hconf
.service
[_res_hconf
.num_services
++] = SERVICE_BIND
;
340 while (fgets_unlocked (buf
, sizeof (buf
), fp
))
343 *__strchrnul (buf
, '\n') = '\0';
344 parse_line (hconf_name
, line_num
, buf
);
349 envval
= getenv (ENV_SERVORDER
);
352 _res_hconf
.num_services
= 0;
353 arg_service_list (ENV_SERVORDER
, 1, envval
, 0);
356 envval
= getenv (ENV_SPOOF
);
358 arg_spoof (ENV_SPOOF
, 1, envval
, 0);
360 envval
= getenv (ENV_MULTI
);
362 arg_bool (ENV_MULTI
, 1, envval
, HCONF_FLAG_MULTI
);
364 envval
= getenv (ENV_REORDER
);
366 arg_bool (ENV_REORDER
, 1, envval
, HCONF_FLAG_REORDER
);
368 envval
= getenv (ENV_TRIM_ADD
);
370 arg_trimdomain_list (ENV_TRIM_ADD
, 1, envval
, 0);
372 envval
= getenv (ENV_TRIM_OVERR
);
375 _res_hconf
.num_trimdomains
= 0;
376 arg_trimdomain_list (ENV_TRIM_OVERR
, 1, envval
, 0);
379 _res_hconf
.initialized
= 1;
383 /* List of known interfaces. */
384 static struct netaddr
397 /* We need to protect the dynamic buffer handling. */
398 __libc_lock_define_initialized (static, lock
);
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.
402 Otherwise, nothing is changed.
404 Note that this function currently only handles IPv4 addresses. */
407 _res_hconf_reorder_addrs (struct hostent
*hp
)
409 #if defined SIOCGIFCONF && defined SIOCGIFNETMASK
411 /* Number of interfaces. */
412 static int num_ifs
= -1;
414 /* Only reorder if we're supposed to. */
415 if ((_res_hconf
.flags
& HCONF_FLAG_REORDER
) == 0)
418 /* Can't deal with anything but IPv4 for now... */
419 if (hp
->h_addrtype
!= AF_INET
)
424 struct ifreq
*ifr
, *cur_ifr
;
429 /* Initialize interface table. */
438 __libc_lock_lock (lock
);
440 /* Get a list of interfaces. */
441 __ifreq (&ifr
, &num
);
445 ifaddrs
= malloc (num
* sizeof (ifaddrs
[0]));
449 /* Copy usable interfaces in ifaddrs structure. */
450 for (cur_ifr
= ifr
, i
= 0; i
< num
; ++cur_ifr
, ++i
)
452 if (cur_ifr
->ifr_addr
.sa_family
!= AF_INET
)
455 ifaddrs
[num_ifs
].addrtype
= AF_INET
;
456 ifaddrs
[num_ifs
].u
.ipv4
.addr
=
457 ((struct sockaddr_in
*) &cur_ifr
->ifr_addr
)->sin_addr
.s_addr
;
459 if (__ioctl (sd
, SIOCGIFNETMASK
, cur_ifr
) < 0)
462 ifaddrs
[num_ifs
].u
.ipv4
.mask
=
463 ((struct sockaddr_in
*) &cur_ifr
->ifr_netmask
)->sin_addr
.s_addr
;
465 /* Now we're committed to this entry. */
468 /* Just keep enough memory to hold all the interfaces we want. */
469 ifaddrs
= realloc (ifaddrs
, num_ifs
* sizeof (ifaddrs
[0]));
475 /* Release lock, preserve error value, and close socket. */
477 __libc_lock_unlock (lock
);
484 /* Find an address for which we have a direct connection. */
485 for (i
= 0; hp
->h_addr_list
[i
]; ++i
)
487 struct in_addr
*haddr
= (struct in_addr
*) hp
->h_addr_list
[i
];
489 for (j
= 0; j
< num_ifs
; ++j
)
491 u_int32_t if_addr
= ifaddrs
[j
].u
.ipv4
.addr
;
492 u_int32_t if_netmask
= ifaddrs
[j
].u
.ipv4
.mask
;
494 if (((haddr
->s_addr
^ if_addr
) & if_netmask
) == 0)
498 tmp
= hp
->h_addr_list
[i
];
499 hp
->h_addr_list
[i
] = hp
->h_addr_list
[0];
500 hp
->h_addr_list
[0] = tmp
;
505 #endif /* defined(SIOCGIFCONF) && ... */
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. */
515 _res_hconf_trim_domain (char *hostname
)
517 size_t hostname_len
, trim_len
;
520 hostname_len
= strlen (hostname
);
522 for (i
= 0; i
< _res_hconf
.num_trimdomains
; ++i
)
524 const char *trim
= _res_hconf
.trimdomain
[i
];
526 trim_len
= strlen (trim
);
527 if (hostname_len
> trim_len
528 && __strcasecmp (&hostname
[hostname_len
- trim_len
], trim
) == 0)
530 hostname
[hostname_len
- trim_len
] = '\0';
537 /* Trim all hostnames/aliases in HP according to the trimdomain list.
538 Notice that HP is modified inplace! */
540 _res_hconf_trim_domains (struct hostent
*hp
)
544 if (_res_hconf
.num_trimdomains
== 0)
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
]);
553 /* Free all resources if necessary. */
554 static void __attribute__ ((unused
))
561 text_set_element (__libc_subfreeres
, free_mem
);