1 /* Copyright (C) 1993, 1995, 1996 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
16 not, write to the Free Software Foundation, Inc., 675 Mass Ave,
17 Cambridge, MA 02139, 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
36 #include "res_hconf.h"
38 #define _PATH_HOSTCONF "/etc/host.conf"
40 /* Environment vars that all user to override default behavior: */
41 #define ENV_HOSTCONF "RESOLV_HOST_CONF"
42 #define ENV_SERVORDER "RESOLV_SERV_ORDER"
43 #define ENV_SPOOF "RESOLV_SPOOF_CHECK"
44 #define ENV_TRIM_OVERR "RESOLV_OVERRIDE_TRIM_DOMAINS"
45 #define ENV_TRIM_ADD "RESOLV_ADD_TRIM_DOMAINS"
46 #define ENV_MULTI "RESOLV_MULTI"
47 #define ENV_REORDER "RESOLV_REORDER"
49 static const char * arg_service_list (const char *, int, const char *,
51 static const char * arg_trimdomain_list (const char *, int, const char *,
53 static const char * arg_spoof (const char *, int, const char *, unsigned);
54 static const char * arg_bool (const char *, int, const char *, unsigned);
58 const char * (*parse_args
)(const char * filename
, int line_num
,
59 const char * args
, unsigned arg
);
62 {"order", arg_service_list
, 0},
63 {"trim", arg_trimdomain_list
, 0},
64 {"spoof", arg_spoof
, 0},
65 {"multi", arg_bool
, HCONF_FLAG_MULTI
},
66 {"nospoof", arg_bool
, HCONF_FLAG_SPOOF
},
67 {"spoofalert", arg_bool
, HCONF_FLAG_SPOOFALERT
},
68 {"reorder", arg_bool
, HCONF_FLAG_REORDER
}
72 /* Skip white space. */
74 skip_ws (const char * str
)
76 while (isspace (*str
)) ++str
;
81 /* Skip until whitespace, comma, end of line, or comment character. */
83 skip_string (const char * str
)
85 while (*str
&& !isspace (*str
) && *str
!= '#' && *str
!= ',') ++str
;
91 arg_service_list (const char * fname
, int line_num
, const char * args
,
94 enum Name_Service service
;
100 enum Name_Service service
;
102 {"bind", SERVICE_BIND
},
103 {"hosts", SERVICE_HOSTS
},
104 {"nis", SERVICE_NIS
},
110 args
= skip_string (args
);
113 service
= SERVICE_NONE
;
114 for (i
= 0; i
< sizeof (svcs
) / sizeof (svcs
[0]); ++i
)
116 if (strncasecmp (start
, svcs
[i
].name
, len
) == 0
117 && len
== strlen (svcs
[i
].name
))
119 service
= svcs
[i
].service
;
123 if (service
== SERVICE_NONE
)
125 fprintf (stderr
, "%s: line %d: expected service, found `%s'\n",
126 fname
, line_num
, start
);
129 if (_res_hconf
.num_services
>= SERVICE_MAX
)
131 fprintf (stderr
, "%s: line %d: cannot specify more than %d services",
132 fname
, line_num
, SERVICE_MAX
);
135 _res_hconf
.service
[_res_hconf
.num_services
++] = service
;
137 args
= skip_ws (args
);
140 case ',': case ';': case ':':
141 args
= skip_ws (++args
);
142 if (!*args
|| *args
== '#')
145 "%s: line %d: list delimiter not followed by keyword",
153 while (*args
&& *args
!= '#');
159 arg_trimdomain_list (const char * fname
, int line_num
, const char * args
,
168 args
= skip_string (args
);
171 if (_res_hconf
.num_trimdomains
>= TRIMDOMAINS_MAX
)
174 "%s: line %d: cannot specify more than %d trim domains",
175 fname
, line_num
, TRIMDOMAINS_MAX
);
178 _res_hconf
.trimdomain
[_res_hconf
.num_trimdomains
++] =
179 strndup (start
, len
);
180 args
= skip_ws (args
);
183 case ',': case ';': case ':':
184 args
= skip_ws (++args
);
185 if (!*args
|| *args
== '#')
188 "%s: line %d: list delimiter not followed by domain",
196 while (*args
&& *args
!= '#');
202 arg_spoof (const char * fname
, int line_num
, const char * args
, unsigned flag
)
204 const char * start
= args
;
207 args
= skip_string (args
);
210 if (len
== 3 && strncasecmp (start
, "off", len
) == 0)
211 _res_hconf
.flags
&= ~(HCONF_FLAG_SPOOF
| HCONF_FLAG_SPOOFALERT
);
214 _res_hconf
.flags
|= (HCONF_FLAG_SPOOF
| HCONF_FLAG_SPOOFALERT
);
215 if ((len
== 6 && strncasecmp (start
, "nowarn", len
) == 0)
216 || !(len
== 4 && strncasecmp (start
, "warn", len
) == 0))
217 _res_hconf
.flags
&= ~HCONF_FLAG_SPOOFALERT
;
224 arg_bool (const char * fname
, int line_num
, const char * args
, unsigned flag
)
226 if (strncasecmp (args
, "on", 2) == 0)
229 _res_hconf
.flags
|= flag
;
231 else if (strncasecmp (args
, "off", 3) == 0)
234 _res_hconf
.flags
&= ~flag
;
238 fprintf (stderr
, "%s: line %d: expected `on' or `off', found `%s'\n",
239 fname
, line_num
, args
);
247 parse_line (const char * fname
, int line_num
, const char * str
)
256 if (*str
== '#') return; /* skip line comment */
259 str
= skip_string (str
);
262 for (i
= 0; i
< sizeof (cmd
) / sizeof (cmd
[0]); ++i
)
264 if (strncasecmp (start
, cmd
[i
].name
, len
) == 0
265 && strlen (cmd
[i
].name
) == len
)
273 fprintf (stderr
, "%s: line %d: bad command `%s'\n",
274 fname
, line_num
, start
);
280 str
= (*c
->parse_args
) (fname
, line_num
, str
, c
->arg
);
284 /* rest of line must contain white space or comment only: */
287 if (!isspace (*str
)) {
289 fprintf (stderr
, "%s: line %d: ignoring trailing garbage `%s'\n",
290 fname
, line_num
, str
);
298 /* Initialize hconf datastructure by reading host.conf file and
299 environment variables. */
301 _res_hconf_init (void)
303 const char * hconf_name
;
305 char buf
[256], * end
, * envval
;
308 memset (&_res_hconf
, 0, sizeof (_res_hconf
));
310 hconf_name
= getenv (ENV_HOSTCONF
);
312 hconf_name
= _PATH_HOSTCONF
;
314 fp
= fopen (hconf_name
, "r");
316 /* make up something reasonable: */
317 _res_hconf
.service
[_res_hconf
.num_services
++] = SERVICE_BIND
;
320 while (fgets (buf
, sizeof (buf
), fp
))
323 end
= strchr (buf
, '\n');
326 parse_line (hconf_name
, line_num
, buf
);
331 envval
= getenv (ENV_SERVORDER
);
334 _res_hconf
.num_services
= 0;
335 arg_service_list (ENV_SERVORDER
, 1, envval
, 0);
338 envval
= getenv (ENV_SPOOF
);
340 arg_spoof (ENV_SPOOF
, 1, envval
, 0);
342 envval
= getenv (ENV_MULTI
);
344 arg_bool (ENV_MULTI
, 1, envval
, HCONF_FLAG_MULTI
);
346 envval
= getenv (ENV_REORDER
);
348 arg_bool (ENV_REORDER
, 1, envval
, HCONF_FLAG_REORDER
);
350 envval
= getenv (ENV_TRIM_ADD
);
352 arg_trimdomain_list (ENV_TRIM_ADD
, 1, envval
, 0);
354 envval
= getenv (ENV_TRIM_OVERR
);
357 _res_hconf
.num_trimdomains
= 0;
358 arg_trimdomain_list (ENV_TRIM_OVERR
, 1, envval
, 0);
363 /* Reorder addresses returned in a hostent such that the first address
364 is an address on the local subnet, if there is such an address.
365 Otherwise, nothing is changed. */
368 _res_hconf_reorder_addrs (struct hostent
* hp
)
370 #if defined (SIOCGIFCONF) && defined (SIOCGIFNETMASK)
371 static int num_ifs
= -1; /* number of interfaces */
372 static struct netaddr
{
382 if (hp
->h_addrtype
!= AF_INET
)
383 return; /* can't deal with anything but IPv4 for now... */
392 /* initialize interface table: */
396 sd
= socket (AF_INET
, SOCK_DGRAM
, 0);
400 /* Now get list of interfaces. Since we don't know how many
401 interfaces there are, we keep increasing the buffer size
402 until we have at least sizeof(struct ifreq) too many bytes.
403 That implies that the ioctl() return because it ran out of
404 interfaces, not memory */
408 size
+= 4 * sizeof (struct ifreq
);
409 ifs
.ifc_buf
= realloc (ifs
.ifs_buf
, size
);
416 if (ioctl (sd
, SIOCGIFCONF
, &ifs
) < 0)
418 } while (size
- ifs
.ifc_len
< sizeof (struct ifreq
));
420 num
= ifs
.ifc_len
/ sizeof (struct ifreq
);
422 ifaddrs
= malloc (num
* sizeof (ifaddrs
[0]));
427 for (i
= 0; i
< num
; ++i
) {
428 if (ifr
->ifr_addr
.sa_family
!= AF_INET
)
430 ifaddrs
[num_ifs
].addrtype
= AF_INET
;
432 memcpy (&ifaddrs
[num_ifs
].u
.ipv4
.addr
,
433 &((struct sockaddr_in
*)ifr
->ifr_addr
)->sin_addr
, 4);
435 if (ioctl (sd
, SIOCGIFNETMASK
, if) < 0)
437 memcpy (&ifaddrs
[num_ifs
].u
.ipv4
.mask
,
438 ((struct sockaddr_in
*)ifr
->ifr_mask
)->sin_addr
, 4);
440 ++num_ifs
; /* now we're committed to this entry */
442 /* just keep enough memory to hold all the interfaces we want: */
443 ifaddrs
= realloc (ifaddrs
, num_ifs
* sizeof (ifaddrs
[0]));
453 /* find an address for which we have a direct connection: */
454 for (i
= 0; hp
->h_addr_list
[i
]; ++i
)
456 h_addr
= (struct in_addr
*) hp
->h_addr_list
[i
];
458 for (j
= 0; j
< num_ifs
; ++j
)
460 if_addr
= ifaddrs
[j
].u
.ipv4
.addr
;
461 if_netmask
= ifaddrs
[j
].u
.ipv4
.mask
;
463 if (((h_addr
->s_addr
^ if_addr
) & if_netmask
) == 0)
467 tmp
= hp
->h_addr_list
[i
];
468 hp
->h_addr_list
[i
] = hp
->h_addr_list
[0];
469 hp
->h_addr_list
[0] = tmp
;
474 #endif /* defined(SIOCGIFCONF) && ... */
478 /* If HOSTNAME has a postfix matching any of the trimdomains, trim away
479 that postfix. Notice that HOSTNAME is modified inplace. Also, the
480 original code applied all trimdomains in order, meaning that the
481 same domainname could be trimmed multiple times. I believe this
482 was unintentional. */
484 _res_hconf_trim_domain (char * hostname
)
486 size_t hostname_len
, trim_len
;
489 hostname_len
= strlen(hostname
);
491 for (i
= 0; i
< _res_hconf
.num_trimdomains
; ++i
)
493 const char * trim
= _res_hconf
.trimdomain
[i
];
495 trim_len
= strlen(trim
);
496 if (hostname_len
> trim_len
497 && strcasecmp(&hostname
[hostname_len
- trim_len
], trim
) == 0)
499 hostname
[hostname_len
- trim_len
] = '\0';
506 /* Trim all hostnames/aliases in HP according to the trimdomain list.
507 Notice that HP is modified inplace! */
509 _res_hconf_trim_domains (struct hostent
* hp
)
513 if (_res_hconf
.num_trimdomains
== 0)
516 _res_hconf_trim_domain (hp
->h_name
);
517 for (i
= 0; hp
->h_aliases
[i
]; ++i
)
518 _res_hconf_trim_domain (hp
->h_aliases
[i
]);
525 _hconf_gethostent (void)
531 _hconf_gethostbyname (const char * name
)
538 _hconf_gethostbyaddr (const char * addr
, int len
, int type
)
544 _hconf_gethtbyname (const char * name
)