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