1 /* Copyright (C) 1993, 95, 96, 97, 98, 99, 2000 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
39 #include <sys/ioctl.h>
41 #include <netinet/in.h>
42 #include <bits/libc-lock.h>
44 #include "res_hconf.h"
46 #define _PATH_HOSTCONF "/etc/host.conf"
48 /* Environment vars that all user to override default behavior: */
49 #define ENV_HOSTCONF "RESOLV_HOST_CONF"
50 #define ENV_SERVORDER "RESOLV_SERV_ORDER"
51 #define ENV_SPOOF "RESOLV_SPOOF_CHECK"
52 #define ENV_TRIM_OVERR "RESOLV_OVERRIDE_TRIM_DOMAINS"
53 #define ENV_TRIM_ADD "RESOLV_ADD_TRIM_DOMAINS"
54 #define ENV_MULTI "RESOLV_MULTI"
55 #define ENV_REORDER "RESOLV_REORDER"
57 static const char *arg_service_list (const char *, int, const char *,
59 static const char *arg_trimdomain_list (const char *, int, const char *,
61 static const char *arg_spoof (const char *, int, const char *, unsigned int);
62 static const char *arg_bool (const char *, int, const char *, unsigned int);
67 const char *(*parse_args
) (const char * filename
, int line_num
,
68 const char * args
, unsigned int arg
);
72 {"order", arg_service_list
, 0},
73 {"trim", arg_trimdomain_list
, 0},
74 {"spoof", arg_spoof
, 0},
75 {"multi", arg_bool
, HCONF_FLAG_MULTI
},
76 {"nospoof", arg_bool
, HCONF_FLAG_SPOOF
},
77 {"spoofalert", arg_bool
, HCONF_FLAG_SPOOFALERT
},
78 {"reorder", arg_bool
, HCONF_FLAG_REORDER
}
81 /* Structure containing the state. */
82 struct hconf _res_hconf
;
84 /* Skip white space. */
86 skip_ws (const char *str
)
88 while (isspace (*str
)) ++str
;
93 /* Skip until whitespace, comma, end of line, or comment character. */
95 skip_string (const char *str
)
97 while (*str
&& !isspace (*str
) && *str
!= '#' && *str
!= ',')
104 arg_service_list (const char *fname
, int line_num
, const char *args
,
107 enum Name_Service service
;
114 enum Name_Service service
;
117 {"bind", SERVICE_BIND
},
118 {"hosts", SERVICE_HOSTS
},
119 {"nis", SERVICE_NIS
},
125 args
= skip_string (args
);
128 service
= SERVICE_NONE
;
129 for (i
= 0; i
< sizeof (svcs
) / sizeof (svcs
[0]); ++i
)
131 if (__strncasecmp (start
, svcs
[i
].name
, len
) == 0
132 && len
== strlen (svcs
[i
].name
))
134 service
= svcs
[i
].service
;
138 if (service
== SERVICE_NONE
)
140 fprintf (stderr
, "%s: line %d: expected service, found `%s'\n",
141 fname
, line_num
, start
);
144 if (_res_hconf
.num_services
>= SERVICE_MAX
)
146 fprintf (stderr
, "%s: line %d: cannot specify more than %d services",
147 fname
, line_num
, SERVICE_MAX
);
150 _res_hconf
.service
[_res_hconf
.num_services
++] = service
;
152 args
= skip_ws (args
);
158 args
= skip_ws (++args
);
159 if (!*args
|| *args
== '#')
162 "%s: line %d: list delimiter not followed by keyword",
170 while (*args
&& *args
!= '#');
176 arg_trimdomain_list (const char *fname
, int line_num
, const char *args
,
185 args
= skip_string (args
);
188 if (_res_hconf
.num_trimdomains
>= TRIMDOMAINS_MAX
)
191 "%s: line %d: cannot specify more than %d trim domains",
192 fname
, line_num
, TRIMDOMAINS_MAX
);
195 _res_hconf
.trimdomain
[_res_hconf
.num_trimdomains
++] =
196 __strndup (start
, len
);
197 args
= skip_ws (args
);
200 case ',': case ';': case ':':
201 args
= skip_ws (++args
);
202 if (!*args
|| *args
== '#')
205 "%s: line %d: list delimiter not followed by domain",
213 while (*args
&& *args
!= '#');
219 arg_spoof (const char *fname
, int line_num
, const char *args
, unsigned flag
)
221 const char *start
= args
;
224 args
= skip_string (args
);
227 if (len
== 3 && __strncasecmp (start
, "off", len
) == 0)
228 _res_hconf
.flags
&= ~(HCONF_FLAG_SPOOF
| HCONF_FLAG_SPOOFALERT
);
231 _res_hconf
.flags
|= (HCONF_FLAG_SPOOF
| HCONF_FLAG_SPOOFALERT
);
232 if ((len
== 6 && __strncasecmp (start
, "nowarn", len
) == 0)
233 || !(len
== 4 && __strncasecmp (start
, "warn", len
) == 0))
234 _res_hconf
.flags
&= ~HCONF_FLAG_SPOOFALERT
;
241 arg_bool (const char *fname
, int line_num
, const char *args
, unsigned flag
)
243 if (__strncasecmp (args
, "on", 2) == 0)
246 _res_hconf
.flags
|= flag
;
248 else if (__strncasecmp (args
, "off", 3) == 0)
251 _res_hconf
.flags
&= ~flag
;
255 fprintf (stderr
, "%s: line %d: expected `on' or `off', found `%s'\n",
256 fname
, line_num
, args
);
264 parse_line (const char *fname
, int line_num
, const char *str
)
273 /* skip line comment and empty lines: */
274 if (*str
== '\0' || *str
== '#') return;
277 str
= skip_string (str
);
280 for (i
= 0; i
< sizeof (cmd
) / sizeof (cmd
[0]); ++i
)
282 if (__strncasecmp (start
, cmd
[i
].name
, len
) == 0
283 && strlen (cmd
[i
].name
) == len
)
291 fprintf (stderr
, "%s: line %d: bad command `%s'\n",
292 fname
, line_num
, start
);
298 str
= (*c
->parse_args
) (fname
, line_num
, str
, c
->arg
);
302 /* rest of line must contain white space or comment only: */
305 if (!isspace (*str
)) {
307 fprintf (stderr
, "%s: line %d: ignoring trailing garbage `%s'\n",
308 fname
, line_num
, str
);
316 /* Initialize hconf datastructure by reading host.conf file and
317 environment variables. */
319 _res_hconf_init (void)
321 const char *hconf_name
;
323 char buf
[256], *envval
;
326 if (_res_hconf
.initialized
)
329 memset (&_res_hconf
, '\0', sizeof (_res_hconf
));
331 hconf_name
= getenv (ENV_HOSTCONF
);
332 if (hconf_name
== NULL
)
333 hconf_name
= _PATH_HOSTCONF
;
335 fp
= fopen (hconf_name
, "r");
337 /* make up something reasonable: */
338 _res_hconf
.service
[_res_hconf
.num_services
++] = SERVICE_BIND
;
341 while (fgets_unlocked (buf
, sizeof (buf
), fp
))
344 *__strchrnul (buf
, '\n') = '\0';
345 parse_line (hconf_name
, line_num
, buf
);
350 envval
= getenv (ENV_SERVORDER
);
353 _res_hconf
.num_services
= 0;
354 arg_service_list (ENV_SERVORDER
, 1, envval
, 0);
357 envval
= getenv (ENV_SPOOF
);
359 arg_spoof (ENV_SPOOF
, 1, envval
, 0);
361 envval
= getenv (ENV_MULTI
);
363 arg_bool (ENV_MULTI
, 1, envval
, HCONF_FLAG_MULTI
);
365 envval
= getenv (ENV_REORDER
);
367 arg_bool (ENV_REORDER
, 1, envval
, HCONF_FLAG_REORDER
);
369 envval
= getenv (ENV_TRIM_ADD
);
371 arg_trimdomain_list (ENV_TRIM_ADD
, 1, envval
, 0);
373 envval
= getenv (ENV_TRIM_OVERR
);
376 _res_hconf
.num_trimdomains
= 0;
377 arg_trimdomain_list (ENV_TRIM_OVERR
, 1, envval
, 0);
380 _res_hconf
.initialized
= 1;
384 /* List of known interfaces. */
385 static struct netaddr
398 /* We need to protect the dynamic buffer handling. */
399 __libc_lock_define_initialized (static, lock
);
401 /* Reorder addresses returned in a hostent such that the first address
402 is an address on the local subnet, if there is such an address.
403 Otherwise, nothing is changed.
405 Note that this function currently only handles IPv4 addresses. */
408 _res_hconf_reorder_addrs (struct hostent
*hp
)
410 #if defined SIOCGIFCONF && defined SIOCGIFNETMASK
412 /* Number of interfaces. */
413 static int num_ifs
= -1;
415 /* Only reorder if we're supposed to. */
416 if ((_res_hconf
.flags
& HCONF_FLAG_REORDER
) == 0)
419 /* Can't deal with anything but IPv4 for now... */
420 if (hp
->h_addrtype
!= AF_INET
)
425 struct ifreq
*ifr
, *cur_ifr
;
430 /* Initialize interface table. */
439 __libc_lock_lock (lock
);
441 /* Get a list of interfaces. */
442 __ifreq (&ifr
, &num
);
446 ifaddrs
= malloc (num
* sizeof (ifaddrs
[0]));
450 /* Copy usable interfaces in ifaddrs structure. */
451 for (cur_ifr
= ifr
, i
= 0; i
< num
; ++cur_ifr
, ++i
)
453 if (cur_ifr
->ifr_addr
.sa_family
!= AF_INET
)
456 ifaddrs
[num_ifs
].addrtype
= AF_INET
;
457 ifaddrs
[num_ifs
].u
.ipv4
.addr
=
458 ((struct sockaddr_in
*) &cur_ifr
->ifr_addr
)->sin_addr
.s_addr
;
460 if (__ioctl (sd
, SIOCGIFNETMASK
, cur_ifr
) < 0)
463 ifaddrs
[num_ifs
].u
.ipv4
.mask
=
464 ((struct sockaddr_in
*) &cur_ifr
->ifr_netmask
)->sin_addr
.s_addr
;
466 /* Now we're committed to this entry. */
469 /* Just keep enough memory to hold all the interfaces we want. */
470 ifaddrs
= realloc (ifaddrs
, num_ifs
* sizeof (ifaddrs
[0]));
476 /* Release lock, preserve error value, and close socket. */
478 __libc_lock_unlock (lock
);
485 /* Find an address for which we have a direct connection. */
486 for (i
= 0; hp
->h_addr_list
[i
]; ++i
)
488 struct in_addr
*haddr
= (struct in_addr
*) hp
->h_addr_list
[i
];
490 for (j
= 0; j
< num_ifs
; ++j
)
492 u_int32_t if_addr
= ifaddrs
[j
].u
.ipv4
.addr
;
493 u_int32_t if_netmask
= ifaddrs
[j
].u
.ipv4
.mask
;
495 if (((haddr
->s_addr
^ if_addr
) & if_netmask
) == 0)
499 tmp
= hp
->h_addr_list
[i
];
500 hp
->h_addr_list
[i
] = hp
->h_addr_list
[0];
501 hp
->h_addr_list
[0] = tmp
;
506 #endif /* defined(SIOCGIFCONF) && ... */
510 /* If HOSTNAME has a postfix matching any of the trimdomains, trim away
511 that postfix. Notice that HOSTNAME is modified inplace. Also, the
512 original code applied all trimdomains in order, meaning that the
513 same domainname could be trimmed multiple times. I believe this
514 was unintentional. */
516 _res_hconf_trim_domain (char *hostname
)
518 size_t hostname_len
, trim_len
;
521 hostname_len
= strlen (hostname
);
523 for (i
= 0; i
< _res_hconf
.num_trimdomains
; ++i
)
525 const char *trim
= _res_hconf
.trimdomain
[i
];
527 trim_len
= strlen (trim
);
528 if (hostname_len
> trim_len
529 && __strcasecmp (&hostname
[hostname_len
- trim_len
], trim
) == 0)
531 hostname
[hostname_len
- trim_len
] = '\0';
538 /* Trim all hostnames/aliases in HP according to the trimdomain list.
539 Notice that HP is modified inplace! */
541 _res_hconf_trim_domains (struct hostent
*hp
)
545 if (_res_hconf
.num_trimdomains
== 0)
548 _res_hconf_trim_domain (hp
->h_name
);
549 for (i
= 0; hp
->h_aliases
[i
]; ++i
)
550 _res_hconf_trim_domain (hp
->h_aliases
[i
]);
554 /* Free all resources if necessary. */
555 static void __attribute__ ((unused
))
561 text_set_element (__libc_subfreeres
, free_mem
);