gethostnamadr.c revision 158115
1/*-
2 * Copyright (c) 1994, Garrett Wollman
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND
14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23 * SUCH DAMAGE.
24 */
25
26#include <sys/cdefs.h>
27__FBSDID("$FreeBSD: head/lib/libc/net/gethostnamadr.c 158115 2006-04-28 12:03:38Z ume $");
28
29#include "namespace.h"
30#include "reentrant.h"
31#include <sys/param.h>
32#include <sys/socket.h>
33#include <netinet/in.h>
34#include <arpa/inet.h>
35#include <netdb.h>
36#include <stdio.h>
37#include <ctype.h>
38#include <errno.h>
39#include <stdlib.h>
40#include <string.h>
41#include <stdarg.h>
42#include <nsswitch.h>
43#include <arpa/nameser.h>		/* XXX hack for _res */
44#include <resolv.h>			/* XXX hack for _res */
45#include "un-namespace.h"
46#include "netdb_private.h"
47#ifdef NS_CACHING
48#include "nscache.h"
49#endif
50
51extern int _ht_gethostbyname(void *, void *, va_list);
52extern int _dns_gethostbyname(void *, void *, va_list);
53extern int _nis_gethostbyname(void *, void *, va_list);
54extern int _ht_gethostbyaddr(void *, void *, va_list);
55extern int _dns_gethostbyaddr(void *, void *, va_list);
56extern int _nis_gethostbyaddr(void *, void *, va_list);
57
58static int gethostbyname_internal(const char *, int, struct hostent *, char *,
59    size_t, struct hostent **, int *, res_state);
60
61/* Host lookup order if nsswitch.conf is broken or nonexistant */
62static const ns_src default_src[] = {
63	{ NSSRC_FILES, NS_SUCCESS },
64	{ NSSRC_DNS, NS_SUCCESS },
65	{ 0 }
66};
67#ifdef NS_CACHING
68static int host_id_func(char *, size_t *, va_list, void *);
69static int host_marshal_func(char *, size_t *, void *, va_list, void *);
70static int host_unmarshal_func(char *, size_t, void *, va_list, void *);
71#endif
72
73NETDB_THREAD_ALLOC(hostent)
74NETDB_THREAD_ALLOC(hostent_data)
75NETDB_THREAD_ALLOC(hostdata)
76
77static void
78hostent_free(void *ptr)
79{
80	free(ptr);
81}
82
83static void
84hostent_data_free(void *ptr)
85{
86	struct hostent_data *hed = ptr;
87
88	if (hed == NULL)
89		return;
90	hed->stayopen = 0;
91	_endhosthtent(hed);
92	free(hed);
93}
94
95static void
96hostdata_free(void *ptr)
97{
98	free(ptr);
99}
100
101int
102__copy_hostent(struct hostent *he, struct hostent *hptr, char *buf,
103    size_t buflen)
104{
105	char *cp;
106	char **ptr;
107	int i, n;
108	int nptr, len;
109
110	/* Find out the amount of space required to store the answer. */
111	nptr = 2; /* NULL ptrs */
112	len = (char *)ALIGN(buf) - buf;
113	for (i = 0; he->h_addr_list[i]; i++, nptr++) {
114		len += he->h_length;
115	}
116	for (i = 0; he->h_aliases[i]; i++, nptr++) {
117		len += strlen(he->h_aliases[i]) + 1;
118	}
119	len += strlen(he->h_name) + 1;
120	len += nptr * sizeof(char*);
121
122	if (len > buflen) {
123		errno = ERANGE;
124		return (-1);
125	}
126
127	/* copy address size and type */
128	hptr->h_addrtype = he->h_addrtype;
129	n = hptr->h_length = he->h_length;
130
131	ptr = (char **)ALIGN(buf);
132	cp = (char *)ALIGN(buf) + nptr * sizeof(char *);
133
134	/* copy address list */
135	hptr->h_addr_list = ptr;
136	for (i = 0; he->h_addr_list[i]; i++ , ptr++) {
137		memcpy(cp, he->h_addr_list[i], n);
138		hptr->h_addr_list[i] = cp;
139		cp += n;
140	}
141	hptr->h_addr_list[i] = NULL;
142	ptr++;
143
144	/* copy official name */
145	n = strlen(he->h_name) + 1;
146	strcpy(cp, he->h_name);
147	hptr->h_name = cp;
148	cp += n;
149
150	/* copy aliases */
151	hptr->h_aliases = ptr;
152	for (i = 0 ; he->h_aliases[i]; i++) {
153		n = strlen(he->h_aliases[i]) + 1;
154		strcpy(cp, he->h_aliases[i]);
155		hptr->h_aliases[i] = cp;
156		cp += n;
157	}
158	hptr->h_aliases[i] = NULL;
159
160	return (0);
161}
162
163#ifdef NS_CACHING
164static int
165host_id_func(char *buffer, size_t *buffer_size, va_list ap, void *cache_mdata)
166{
167	res_state statp;
168	u_long res_options;
169
170	const int op_id = 1;
171	char *str;
172	int len, type;
173
174	size_t desired_size, size;
175	enum nss_lookup_type lookup_type;
176	char *p;
177	int res = NS_UNAVAIL;
178
179	statp = __res_state();
180	res_options = statp->options & (RES_RECURSE | RES_DEFNAMES |
181	    RES_DNSRCH | RES_NOALIASES | RES_USE_INET6);
182
183	lookup_type = (enum nss_lookup_type)cache_mdata;
184	switch (lookup_type) {
185	case nss_lt_name:
186		str = va_arg(ap, char *);
187		type = va_arg(ap, int);
188
189		size = strlen(str);
190		desired_size = sizeof(res_options) + sizeof(int) +
191		    sizeof(enum nss_lookup_type) + sizeof(int) + size + 1;
192
193		if (desired_size > *buffer_size) {
194			res = NS_RETURN;
195			goto fin;
196		}
197
198		p = buffer;
199
200		memcpy(p, &res_options, sizeof(res_options));
201		p += sizeof(res_options);
202
203		memcpy(p, &op_id, sizeof(int));
204		p += sizeof(int);
205
206		memcpy(p, &lookup_type, sizeof(enum nss_lookup_type));
207		p += sizeof(int);
208
209		memcpy(p, &type, sizeof(int));
210		p += sizeof(int);
211
212		memcpy(p, str, size + 1);
213
214		res = NS_SUCCESS;
215		break;
216	case nss_lt_id:
217		str = va_arg(ap, char *);
218		len = va_arg(ap, int);
219		type = va_arg(ap, int);
220
221		desired_size = sizeof(res_options) + sizeof(int) +
222		    sizeof(enum nss_lookup_type) + sizeof(int) * 2 + len;
223
224		if (desired_size > *buffer_size) {
225			res = NS_RETURN;
226			goto fin;
227		}
228
229		p = buffer;
230		memcpy(p, &res_options, sizeof(res_options));
231		p += sizeof(res_options);
232
233		memcpy(p, &op_id, sizeof(int));
234		p += sizeof(int);
235
236		memcpy(p, &lookup_type, sizeof(enum nss_lookup_type));
237		p += sizeof(int);
238
239		memcpy(p, &type, sizeof(int));
240		p += sizeof(int);
241
242		memcpy(p, &len, sizeof(int));
243		p += sizeof(int);
244
245		memcpy(p, str, len);
246
247		res = NS_SUCCESS;
248		break;
249	default:
250		/* should be unreachable */
251		return (NS_UNAVAIL);
252	}
253
254fin:
255	*buffer_size = desired_size;
256	return (res);
257}
258
259static int
260host_marshal_func(char *buffer, size_t *buffer_size, void *retval, va_list ap,
261    void *cache_mdata)
262{
263	char *str;
264	int len, type;
265	struct hostent *ht;
266
267	struct hostent new_ht;
268	size_t desired_size, aliases_size, addr_size, size;
269	char *p, **iter;
270
271	switch ((enum nss_lookup_type)cache_mdata) {
272	case nss_lt_name:
273		str = va_arg(ap, char *);
274		type = va_arg(ap, int);
275		break;
276	case nss_lt_id:
277		str = va_arg(ap, char *);
278		len = va_arg(ap, int);
279		type = va_arg(ap, int);
280		break;
281	default:
282		/* should be unreachable */
283		return (NS_UNAVAIL);
284	}
285	ht = va_arg(ap, struct hostent *);
286
287	desired_size = _ALIGNBYTES + sizeof(struct hostent) + sizeof(char *);
288	if (ht->h_name != NULL)
289		desired_size += strlen(ht->h_name) + 1;
290
291	if (ht->h_aliases != NULL) {
292		aliases_size = 0;
293		for (iter = ht->h_aliases; *iter; ++iter) {
294			desired_size += strlen(*iter) + 1;
295			++aliases_size;
296		}
297
298		desired_size += _ALIGNBYTES +
299		    (aliases_size + 1) * sizeof(char *);
300	}
301
302	if (ht->h_addr_list != NULL) {
303		addr_size = 0;
304		for (iter = ht->h_addr_list; *iter; ++iter)
305			++addr_size;
306
307		desired_size += addr_size * _ALIGN(ht->h_length);
308		desired_size += _ALIGNBYTES + (addr_size + 1) * sizeof(char *);
309	}
310
311	if (desired_size > *buffer_size) {
312		/* this assignment is here for future use */
313		*buffer_size = desired_size;
314		return (NS_RETURN);
315	}
316
317	memcpy(&new_ht, ht, sizeof(struct hostent));
318	memset(buffer, 0, desired_size);
319
320	*buffer_size = desired_size;
321	p = buffer + sizeof(struct hostent) + sizeof(char *);
322	memcpy(buffer + sizeof(struct hostent), &p, sizeof(char *));
323	p = (char *)_ALIGN(p);
324
325	if (new_ht.h_name != NULL) {
326		size = strlen(new_ht.h_name);
327		memcpy(p, new_ht.h_name, size);
328		new_ht.h_name = p;
329		p += size + 1;
330	}
331
332	if (new_ht.h_aliases != NULL) {
333		p = (char *)_ALIGN(p);
334		memcpy(p, new_ht.h_aliases, sizeof(char *) * aliases_size);
335		new_ht.h_aliases = (char **)p;
336		p += sizeof(char *) * (aliases_size + 1);
337
338		for (iter = new_ht.h_aliases; *iter; ++iter) {
339			size = strlen(*iter);
340			memcpy(p, *iter, size);
341			*iter = p;
342			p += size + 1;
343		}
344	}
345
346	if (new_ht.h_addr_list != NULL) {
347		p = (char *)_ALIGN(p);
348		memcpy(p, new_ht.h_addr_list, sizeof(char *) * addr_size);
349		new_ht.h_addr_list = (char **)p;
350		p += sizeof(char *) * (addr_size + 1);
351
352		size = _ALIGN(new_ht.h_length);
353		for (iter = new_ht.h_addr_list; *iter; ++iter) {
354			memcpy(p, *iter, size);
355			*iter = p;
356			p += size + 1;
357		}
358	}
359	memcpy(buffer, &new_ht, sizeof(struct hostent));
360	return (NS_SUCCESS);
361}
362
363static int
364host_unmarshal_func(char *buffer, size_t buffer_size, void *retval, va_list ap,
365    void *cache_mdata)
366{
367	char *str;
368	int len, type;
369	struct hostent *ht;
370
371	char *p;
372	char **iter;
373	char *orig_buf;
374	size_t orig_buf_size;
375
376	switch ((enum nss_lookup_type)cache_mdata) {
377	case nss_lt_name:
378		str = va_arg(ap, char *);
379		type = va_arg(ap, int);
380		break;
381	case nss_lt_id:
382		str = va_arg(ap, char *);
383		len = va_arg(ap, int);
384		type = va_arg(ap, int);
385		break;
386	default:
387		/* should be unreachable */
388		return (NS_UNAVAIL);
389	}
390
391	ht = va_arg(ap, struct hostent *);
392	orig_buf = va_arg(ap, char *);
393	orig_buf_size = va_arg(ap, size_t);
394
395	if (orig_buf_size <
396	    buffer_size - sizeof(struct hostent) - sizeof(char *)) {
397		errno = ERANGE;
398		return (NS_RETURN);
399	}
400
401	memcpy(ht, buffer, sizeof(struct hostent));
402	memcpy(&p, buffer + sizeof(struct hostent), sizeof(char *));
403
404	orig_buf = (char *)_ALIGN(orig_buf);
405	memcpy(orig_buf, buffer + sizeof(struct hostent) + sizeof(char *) +
406	    _ALIGN(p) - (size_t)p,
407	    buffer_size - sizeof(struct hostent) - sizeof(char *) -
408	    _ALIGN(p) + (size_t)p);
409	p = (char *)_ALIGN(p);
410
411	NS_APPLY_OFFSET(ht->h_name, orig_buf, p, char *);
412	if (ht->h_aliases != NULL) {
413		NS_APPLY_OFFSET(ht->h_aliases, orig_buf, p, char **);
414
415		for (iter = ht->h_aliases; *iter; ++iter)
416			NS_APPLY_OFFSET(*iter, orig_buf, p, char *);
417	}
418
419	if (ht->h_addr_list != NULL) {
420		NS_APPLY_OFFSET(ht->h_addr_list, orig_buf, p, char **);
421
422		for (iter = ht->h_addr_list; *iter; ++iter)
423			NS_APPLY_OFFSET(*iter, orig_buf, p, char *);
424	}
425
426	*((struct hostent **)retval) = ht;
427	return (NS_SUCCESS);
428}
429#endif /* NS_CACHING */
430
431static int
432fakeaddr(const char *name, int af, struct hostent *hp, char *buf,
433    size_t buflen, res_state statp)
434{
435	struct hostent_data *hed;
436	struct hostent he;
437
438	if ((hed = __hostent_data_init()) == NULL) {
439		errno = ENOMEM;
440		RES_SET_H_ERRNO(statp, NETDB_INTERNAL);
441		return (-1);
442	}
443
444	if ((af != AF_INET ||
445	    inet_aton(name, (struct in_addr *)hed->host_addr) != 1) &&
446	    inet_pton(af, name, hed->host_addr) != 1) {
447		RES_SET_H_ERRNO(statp, HOST_NOT_FOUND);
448		return (-1);
449	}
450	strncpy(hed->hostbuf, name, MAXDNAME);
451	hed->hostbuf[MAXDNAME] = '\0';
452	if (af == AF_INET && (statp->options & RES_USE_INET6) != 0U) {
453		_map_v4v6_address((char *)hed->host_addr,
454		    (char *)hed->host_addr);
455		af = AF_INET6;
456	}
457	he.h_addrtype = af;
458	switch(af) {
459	case AF_INET:
460		he.h_length = NS_INADDRSZ;
461		break;
462	case AF_INET6:
463		he.h_length = NS_IN6ADDRSZ;
464		break;
465	default:
466		errno = EAFNOSUPPORT;
467		RES_SET_H_ERRNO(statp, NETDB_INTERNAL);
468		return (-1);
469	}
470	he.h_name = hed->hostbuf;
471	he.h_aliases = hed->host_aliases;
472	hed->host_aliases[0] = NULL;
473	hed->h_addr_ptrs[0] = (char *)hed->host_addr;
474	hed->h_addr_ptrs[1] = NULL;
475	he.h_addr_list = hed->h_addr_ptrs;
476	RES_SET_H_ERRNO(statp, NETDB_SUCCESS);
477	return (__copy_hostent(&he, hp, buf, buflen));
478}
479
480int
481gethostbyname_r(const char *name, struct hostent *he, char *buffer,
482    size_t buflen, struct hostent **result, int *h_errnop)
483{
484	res_state statp;
485
486	statp = __res_state();
487	if ((statp->options & RES_INIT) == 0 && res_ninit(statp) == -1) {
488		RES_SET_H_ERRNO(statp, NETDB_INTERNAL);
489		return (-1);
490	}
491	if (statp->options & RES_USE_INET6) {
492		if (fakeaddr(name, AF_INET, he, buffer, buflen, statp) == 0) {
493			*result = he;
494			return (0);
495		}
496		if (gethostbyname_internal(name, AF_INET6, he, buffer, buflen,
497		    result, h_errnop, statp) == 0)
498			return (0);
499	}
500	return (gethostbyname_internal(name, AF_INET, he, buffer, buflen,
501	    result, h_errnop, statp));
502}
503
504int
505gethostbyname2_r(const char *name, int af, struct hostent *he, char *buffer,
506    size_t buflen, struct hostent **result, int *h_errnop)
507{
508	res_state statp;
509
510	statp = __res_state();
511	if ((statp->options & RES_INIT) == 0 && res_ninit(statp) == -1) {
512		RES_SET_H_ERRNO(statp, NETDB_INTERNAL);
513		return (-1);
514	}
515	return (gethostbyname_internal(name, af, he, buffer, buflen, result,
516	    h_errnop, statp));
517}
518
519int
520gethostbyname_internal(const char *name, int af, struct hostent *hp, char *buf,
521    size_t buflen, struct hostent **result, int *h_errnop, res_state statp)
522{
523	const char *cp;
524	int rval, ret_errno;
525	char abuf[MAXDNAME];
526
527#ifdef NS_CACHING
528	static const nss_cache_info cache_info =
529		NS_COMMON_CACHE_INFO_INITIALIZER(
530		hosts, (void *)nss_lt_name,
531		host_id_func, host_marshal_func, host_unmarshal_func);
532#endif
533	static const ns_dtab dtab[] = {
534		NS_FILES_CB(_ht_gethostbyname, NULL)
535		{ NSSRC_DNS, _dns_gethostbyname, NULL },
536		NS_NIS_CB(_nis_gethostbyname, NULL) /* force -DHESIOD */
537#ifdef NS_CACHING
538		NS_CACHE_CB(&cache_info)
539#endif
540		{ 0 }
541	};
542
543	switch (af) {
544	case AF_INET:
545	case AF_INET6:
546		break;
547	default:
548		RES_SET_H_ERRNO(statp, NETDB_INTERNAL);
549		*h_errnop = statp->res_h_errno;
550		errno = EAFNOSUPPORT;
551		return (-1);
552	}
553
554	/*
555	 * if there aren't any dots, it could be a user-level alias.
556	 * this is also done in res_query() since we are not the only
557	 * function that looks up host names.
558	 */
559	if (!strchr(name, '.') &&
560	    (cp = res_hostalias(statp, name, abuf, sizeof abuf)))
561		name = cp;
562
563	if (fakeaddr(name, af, hp, buf, buflen, statp) == 0) {
564		*result = hp;
565		return (0);
566	}
567
568	rval = _nsdispatch((void *)result, dtab, NSDB_HOSTS,
569	    "gethostbyname2_r", default_src, name, af, hp, buf, buflen,
570	    &ret_errno, h_errnop);
571
572	return ((rval == NS_SUCCESS) ? 0 : -1);
573}
574
575int
576gethostbyaddr_r(const char *addr, int len, int af, struct hostent *hp,
577    char *buf, size_t buflen, struct hostent **result, int *h_errnop)
578{
579	const u_char *uaddr = (const u_char *)addr;
580	const struct in6_addr *addr6;
581	socklen_t size;
582	int rval, ret_errno;
583	res_state statp;
584
585#ifdef NS_CACHING
586	static const nss_cache_info cache_info =
587		NS_COMMON_CACHE_INFO_INITIALIZER(
588		hosts, (void *)nss_lt_id,
589		host_id_func, host_marshal_func, host_unmarshal_func);
590#endif
591	static const ns_dtab dtab[] = {
592		NS_FILES_CB(_ht_gethostbyaddr, NULL)
593		{ NSSRC_DNS, _dns_gethostbyaddr, NULL },
594		NS_NIS_CB(_nis_gethostbyaddr, NULL) /* force -DHESIOD */
595#ifdef NS_CACHING
596		NS_CACHE_CB(&cache_info)
597#endif
598		{ 0 }
599	};
600
601	statp = __res_state();
602	if ((statp->options & RES_INIT) == 0 && res_ninit(statp) == -1) {
603		RES_SET_H_ERRNO(statp, NETDB_INTERNAL);
604		*h_errnop = statp->res_h_errno;
605		return (-1);
606	}
607
608	if (af == AF_INET6 && len == NS_IN6ADDRSZ) {
609		addr6 = (const struct in6_addr *)(const void *)uaddr;
610		if (IN6_IS_ADDR_LINKLOCAL(addr6)) {
611			RES_SET_H_ERRNO(statp, HOST_NOT_FOUND);
612			*h_errnop = statp->res_h_errno;
613			return (-1);
614		}
615		if (IN6_IS_ADDR_V4MAPPED(addr6) ||
616		    IN6_IS_ADDR_V4COMPAT(addr6)) {
617			/* Unmap. */
618			uaddr += NS_IN6ADDRSZ - NS_INADDRSZ;
619			af = AF_INET;
620			len = NS_INADDRSZ;
621		}
622	}
623	switch (af) {
624	case AF_INET:
625		size = NS_INADDRSZ;
626		break;
627	case AF_INET6:
628		size = NS_IN6ADDRSZ;
629		break;
630	default:
631		errno = EAFNOSUPPORT;
632		RES_SET_H_ERRNO(statp, NETDB_INTERNAL);
633		*h_errnop = statp->res_h_errno;
634		return (-1);
635	}
636	if (size != len) {
637		errno = EINVAL;
638		RES_SET_H_ERRNO(statp, NETDB_INTERNAL);
639		*h_errnop = statp->res_h_errno;
640		return (-1);
641	}
642
643	rval = _nsdispatch((void *)result, dtab, NSDB_HOSTS,
644	    "gethostbyaddr_r", default_src, uaddr, len, af, hp, buf, buflen,
645	    &ret_errno, h_errnop);
646
647	return ((rval == NS_SUCCESS) ? 0 : -1);
648}
649
650struct hostent *
651gethostbyname(const char *name)
652{
653	struct hostdata *hd;
654	struct hostent *rval;
655	int ret_h_errno;
656
657	if ((hd = __hostdata_init()) == NULL)
658		return (NULL);
659	if (gethostbyname_r(name, &hd->host, hd->data, sizeof(hd->data), &rval,
660	    &ret_h_errno) != 0)
661		return (NULL);
662	return (rval);
663}
664
665struct hostent *
666gethostbyname2(const char *name, int af)
667{
668	struct hostdata *hd;
669	struct hostent *rval;
670	int ret_h_errno;
671
672	if ((hd = __hostdata_init()) == NULL)
673		return (NULL);
674	if (gethostbyname2_r(name, af, &hd->host, hd->data, sizeof(hd->data),
675	    &rval, &ret_h_errno) != 0)
676		return (NULL);
677	return (rval);
678}
679
680struct hostent *
681gethostbyaddr(const char *addr, int len, int af)
682{
683	struct hostdata *hd;
684	struct hostent *rval;
685	int ret_h_errno;
686
687	if ((hd = __hostdata_init()) == NULL)
688		return (NULL);
689	if (gethostbyaddr_r(addr, len, af, &hd->host, hd->data,
690	    sizeof(hd->data), &rval, &ret_h_errno) != 0)
691		return (NULL);
692	return (rval);
693}
694
695void
696sethostent(int stayopen)
697{
698	struct hostent_data *hed;
699
700	if ((hed = __hostent_data_init()) == NULL)
701		return;
702	_sethosthtent(stayopen, hed);
703	_sethostdnsent(stayopen);
704}
705
706void
707endhostent(void)
708{
709	struct hostent_data *hed;
710
711	if ((hed = __hostent_data_init()) == NULL)
712		return;
713	_endhosthtent(hed);
714	_endhostdnsent();
715}
716