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
}
73 * Why isn't this in stdlib?
76 strndup (const char * s
, size_t n
)
80 retval
= malloc (n
+ 1);
84 memcpy (retval
, s
, n
);
85 retval
[n
] = '\0'; /* ensure return value is terminated */
90 /* Skip white space. */
92 skip_ws (const char * str
)
94 while (isspace (*str
)) ++str
;
99 /* Skip until whitespace, comma, end of line, or comment character. */
101 skip_string (const char * str
)
103 while (*str
&& !isspace (*str
) && *str
!= '#' && *str
!= ',') ++str
;
109 arg_service_list (const char * fname
, int line_num
, const char * args
,
112 enum Name_Service service
;
118 enum Name_Service service
;
120 {"bind", SERVICE_BIND
},
121 {"hosts", SERVICE_HOSTS
},
122 {"nis", SERVICE_NIS
},
128 args
= skip_string (args
);
131 service
= SERVICE_NONE
;
132 for (i
= 0; i
< sizeof (svcs
) / sizeof (svcs
[0]); ++i
)
134 if (strncasecmp (start
, svcs
[i
].name
, len
) == 0
135 && len
== strlen (svcs
[i
].name
))
137 service
= svcs
[i
].service
;
141 if (service
== SERVICE_NONE
)
143 fprintf (stderr
, "%s: line %d: expected service, found `%s'\n",
144 fname
, line_num
, start
);
147 if (_res_hconf
.num_services
>= SERVICE_MAX
)
149 fprintf (stderr
, "%s: line %d: cannot specify more than %d services",
150 fname
, line_num
, SERVICE_MAX
);
153 _res_hconf
.service
[_res_hconf
.num_services
++] = service
;
155 args
= skip_ws (args
);
158 case ',': case ';': case ':':
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 if (*str
== '#') return; /* skip line comment */
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], * end
, * envval
;
326 memset (&_res_hconf
, 0, sizeof (_res_hconf
));
328 hconf_name
= getenv (ENV_HOSTCONF
);
330 hconf_name
= _PATH_HOSTCONF
;
332 fp
= fopen (hconf_name
, "r");
334 /* make up something reasonable: */
335 _res_hconf
.service
[_res_hconf
.num_services
++] = SERVICE_BIND
;
338 while (fgets (buf
, sizeof (buf
), fp
))
341 end
= strchr (buf
, '\n');
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);
381 /* Reorder addresses returned in a hostent such that the first address
382 is an address on the local subnet, if there is such an address.
383 Otherwise, nothing is changed. */
386 _res_hconf_reorder_addrs (struct hostent
* hp
)
388 #if defined (SIOCGIFCONF) && defined (SIOCGIFNETMASK)
389 static int num_ifs
= -1; /* number of interfaces */
390 static struct netaddr
{
400 if (hp
->h_addrtype
!= AF_INET
)
401 return; /* can't deal with anything but IPv4 for now... */
410 /* initialize interface table: */
414 sd
= socket (AF_INET
, SOCK_DGRAM
, 0);
418 /* Now get list of interfaces. Since we don't know how many
419 interfaces there are, we keep increasing the buffer size
420 until we have at least sizeof(struct ifreq) too many bytes.
421 That implies that the ioctl() return because it ran out of
422 interfaces, not memory */
426 size
+= 4 * sizeof (struct ifreq
);
427 ifs
.ifc_buf
= realloc (ifs
.ifs_buf
, size
);
434 if (ioctl (sd
, SIOCGIFCONF
, &ifs
) < 0)
436 } while (size
- ifs
.ifc_len
< sizeof (struct ifreq
));
438 num
= ifs
.ifc_len
/ sizeof (struct ifreq
);
440 ifaddrs
= malloc (num
* sizeof (ifaddrs
[0]));
445 for (i
= 0; i
< num
; ++i
) {
446 if (ifr
->ifr_addr
.sa_family
!= AF_INET
)
448 ifaddrs
[num_ifs
].addrtype
= AF_INET
;
450 memcpy (&ifaddrs
[num_ifs
].u
.ipv4
.addr
,
451 &((struct sockaddr_in
*)ifr
->ifr_addr
)->sin_addr
, 4);
453 if (ioctl (sd
, SIOCGIFNETMASK
, if) < 0)
455 memcpy (&ifaddrs
[num_ifs
].u
.ipv4
.mask
,
456 ((struct sockaddr_in
*)ifr
->ifr_mask
)->sin_addr
, 4);
458 ++num_ifs
; /* now we're committed to this entry */
460 /* just keep enough memory to hold all the interfaces we want: */
461 ifaddrs
= realloc (ifaddrs
, num_ifs
* sizeof (ifaddrs
[0]));
471 /* find an address for which we have a direct connection: */
472 for (i
= 0; hp
->h_addr_list
[i
]; ++i
)
474 h_addr
= (struct in_addr
*) hp
->h_addr_list
[i
];
476 for (j
= 0; j
< num_ifs
; ++j
)
478 if_addr
= ifaddrs
[j
].u
.ipv4
.addr
;
479 if_netmask
= ifaddrs
[j
].u
.ipv4
.mask
;
481 if (((h_addr
->s_addr
^ if_addr
) & if_netmask
) == 0)
485 tmp
= hp
->h_addr_list
[i
];
486 hp
->h_addr_list
[i
] = hp
->h_addr_list
[0];
487 hp
->h_addr_list
[0] = tmp
;
492 #endif /* defined(SIOCGIFCONF) && ... */
496 /* If HOSTNAME has a postfix matching any of the trimdomains, trim away
497 that postfix. Notice that HOSTNAME is modified inplace. Also, the
498 original code applied all trimdomains in order, meaning that the
499 same domainname could be trimmed multiple times. I believe this
500 was unintentional. */
502 _res_hconf_trim_domain (char * hostname
)
504 size_t hostname_len
, trim_len
;
507 hostname_len
= strlen(hostname
);
509 for (i
= 0; i
< _res_hconf
.num_trimdomains
; ++i
)
511 const char * trim
= _res_hconf
.trimdomain
[i
];
513 trim_len
= strlen(trim
);
514 if (hostname_len
> trim_len
515 && strcasecmp(&hostname
[hostname_len
- trim_len
], trim
) == 0)
517 hostname
[hostname_len
- trim_len
] = '\0';
524 /* Trim all hostnames/aliases in HP according to the trimdomain list.
525 Notice that HP is modified inplace! */
527 _res_hconf_trim_domains (struct hostent
* hp
)
531 if (_res_hconf
.num_trimdomains
== 0)
534 _res_hconf_trim_domain (hp
->h_name
);
535 for (i
= 0; hp
->h_aliases
[i
]; ++i
)
536 _res_hconf_trim_domain (hp
->h_aliases
[i
]);
543 _hconf_gethostent (void)
549 _hconf_gethostbyname (const char * name
)
556 _hconf_gethostbyaddr (const char * addr
, int len
, int type
)
562 _hconf_gethtbyname (const char * name
)