name6.c revision 92889
1255570Strasz/*	$FreeBSD: head/lib/libc/net/name6.c 92889 2002-03-21 18:49:23Z obrien $	*/
2255570Strasz/*	$KAME: name6.c,v 1.25 2000/06/26 16:44:40 itojun Exp $	*/
3255570Strasz
4255570Strasz/*
5255570Strasz * Copyright (C) 1995, 1996, 1997, 1998, and 1999 WIDE Project.
6255570Strasz * All rights reserved.
7255570Strasz *
8255570Strasz * Redistribution and use in source and binary forms, with or without
9255570Strasz * modification, are permitted provided that the following conditions
10255570Strasz * are met:
11255570Strasz * 1. Redistributions of source code must retain the above copyright
12255570Strasz *    notice, this list of conditions and the following disclaimer.
13255570Strasz * 2. Redistributions in binary form must reproduce the above copyright
14255570Strasz *    notice, this list of conditions and the following disclaimer in the
15255570Strasz *    documentation and/or other materials provided with the distribution.
16255570Strasz * 3. Neither the name of the project nor the names of its contributors
17255570Strasz *    may be used to endorse or promote products derived from this software
18255570Strasz *    without specific prior written permission.
19255570Strasz *
20255570Strasz * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
21255570Strasz * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22255570Strasz * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23255570Strasz * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
24255570Strasz * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25255570Strasz * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26255570Strasz * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27255570Strasz * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28255570Strasz * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29255570Strasz * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30255570Strasz * SUCH DAMAGE.
31255570Strasz */
32255570Strasz/*
33255570Strasz * ++Copyright++ 1985, 1988, 1993
34255570Strasz * -
35255570Strasz * Copyright (c) 1985, 1988, 1993
36255570Strasz *    The Regents of the University of California.  All rights reserved.
37255570Strasz *
38255570Strasz * Redistribution and use in source and binary forms, with or without
39255570Strasz * modification, are permitted provided that the following conditions
40255570Strasz * are met:
41255570Strasz * 1. Redistributions of source code must retain the above copyright
42255570Strasz *    notice, this list of conditions and the following disclaimer.
43255570Strasz * 2. Redistributions in binary form must reproduce the above copyright
44255570Strasz *    notice, this list of conditions and the following disclaimer in the
45255570Strasz *    documentation and/or other materials provided with the distribution.
46255570Strasz * 3. All advertising materials mentioning features or use of this software
47255570Strasz *    must display the following acknowledgement:
48255570Strasz * 	This product includes software developed by the University of
49255570Strasz * 	California, Berkeley and its contributors.
50255570Strasz * 4. Neither the name of the University nor the names of its contributors
51255570Strasz *    may be used to endorse or promote products derived from this software
52255570Strasz *    without specific prior written permission.
53255570Strasz *
54255570Strasz * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
55255570Strasz * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
56255570Strasz * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
57255570Strasz * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
58255570Strasz * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
59255570Strasz * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
60255570Strasz * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
61255570Strasz * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
62255570Strasz * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
63255570Strasz * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
64255570Strasz * SUCH DAMAGE.
65255570Strasz * -
66255570Strasz * Portions Copyright (c) 1993 by Digital Equipment Corporation.
67255570Strasz *
68265508Strasz * Permission to use, copy, modify, and distribute this software for any
69265508Strasz * purpose with or without fee is hereby granted, provided that the above
70265508Strasz * copyright notice and this permission notice appear in all copies, and that
71265508Strasz * the name of Digital Equipment Corporation not be used in advertising or
72255570Strasz * publicity pertaining to distribution of the document or software without
73255570Strasz * specific, written prior permission.
74255570Strasz *
75255570Strasz * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
76255570Strasz * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
77255570Strasz * OF MERCHANTABILITY AND FITNESS.   IN NO EVENT SHALL DIGITAL EQUIPMENT
78255570Strasz * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
79255570Strasz * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
80255570Strasz * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
81265501Strasz * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
82265531Strasz * SOFTWARE.
83255570Strasz * -
84255570Strasz * --Copyright--
85265501Strasz */
86265531Strasz
87255570Strasz/*
88255570Strasz *	Atsushi Onoe <onoe@sm.sony.co.jp>
89265501Strasz */
90265531Strasz
91255570Strasz/*
92255570Strasz * TODO for thread safe
93265501Strasz *	use mutex for _hostconf, _hostconf_init.
94265531Strasz *	rewrite resolvers to be thread safe
95255570Strasz */
96255570Strasz
97265501Strasz#include "namespace.h"
98265531Strasz#include <sys/param.h>
99265523Strasz#include <sys/socket.h>
100265523Strasz#include <sys/time.h>
101265523Strasz#include <sys/queue.h>
102265523Strasz#include <netinet/in.h>
103255570Strasz
104255570Strasz#include <arpa/inet.h>
105255570Strasz#include <arpa/nameser.h>
106255570Strasz
107255570Strasz#include <errno.h>
108255570Strasz#include <netdb.h>
109255570Strasz#include <resolv.h>
110265505Strasz#include <stdio.h>
111265505Strasz#include <stdlib.h>
112265505Strasz#include <string.h>
113265505Strasz#include <stdarg.h>
114255570Strasz#include <nsswitch.h>
115255570Strasz#include <unistd.h>
116265505Strasz#include "un-namespace.h"
117265505Strasz
118265505Strasz#ifndef _PATH_HOSTS
119265505Strasz#define	_PATH_HOSTS	"/etc/hosts"
120265505Strasz#endif
121265505Strasz
122255570Strasz#ifndef MAXALIASES
123255570Strasz#define	MAXALIASES	10
124265505Strasz#endif
125265505Strasz#ifndef	MAXADDRS
126265505Strasz#define	MAXADDRS	20
127265505Strasz#endif
128265505Strasz#ifndef MAXDNAME
129265505Strasz#define	MAXDNAME	1025
130265505Strasz#endif
131255570Strasz
132255570Strasz#ifdef INET6
133265505Strasz#define	ADDRLEN(af)	((af) == AF_INET6 ? sizeof(struct in6_addr) : \
134265505Strasz					    sizeof(struct in_addr))
135265505Strasz#else
136265505Strasz#define	ADDRLEN(af)	sizeof(struct in_addr)
137265505Strasz#endif
138265505Strasz
139265505Strasz#define	MAPADDR(ab, ina) \
140255570Straszdo {									\
141255570Strasz	memcpy(&(ab)->map_inaddr, ina, sizeof(struct in_addr));		\
142255570Strasz	memset((ab)->map_zero, 0, sizeof((ab)->map_zero));		\
143255570Strasz	memset((ab)->map_one, 0xff, sizeof((ab)->map_one));		\
144255570Strasz} while (0)
145255570Strasz#define	MAPADDRENABLED(flags) \
146255570Strasz	(((flags) & AI_V4MAPPED) || \
147255570Strasz	 (((flags) & AI_V4MAPPED_CFG) && _mapped_addr_enabled()))
148255570Strasz
149255570Straszunion inx_addr {
150255570Strasz	struct in_addr	in_addr;
151255570Strasz#ifdef INET6
152255570Strasz	struct in6_addr	in6_addr;
153255570Strasz#endif
154255570Strasz	struct {
155255570Strasz		u_char	mau_zero[10];
156255570Strasz		u_char	mau_one[2];
157255570Strasz		struct in_addr mau_inaddr;
158255570Strasz	}		map_addr_un;
159255570Strasz#define	map_zero	map_addr_un.mau_zero
160255570Strasz#define	map_one		map_addr_un.mau_one
161255570Strasz#define	map_inaddr	map_addr_un.mau_inaddr
162255570Strasz};
163255570Strasz
164255570Straszstatic struct	 hostent *_hpcopy(struct hostent *hp, int *errp);
165255570Straszstatic struct	 hostent *_hpaddr(int af, const char *name, void *addr, int *errp);
166255570Straszstatic struct	 hostent *_hpmerge(struct hostent *hp1, struct hostent *hp2, int *errp);
167255570Strasz#ifdef INET6
168255570Straszstatic struct	 hostent *_hpmapv6(struct hostent *hp, int *errp);
169255570Strasz#endif
170255570Straszstatic struct	 hostent *_hpsort(struct hostent *hp);
171255570Straszstatic struct	 hostent *_ghbyname(const char *name, int af, int flags, int *errp);
172255570Straszstatic char	*_hgetword(char **pp);
173255570Straszstatic int	 _mapped_addr_enabled(void);
174255570Strasz
175255570Straszstatic FILE	*_files_open(int *errp);
176255570Straszstatic int	 _files_ghbyname(void *, void *, va_list);
177255570Straszstatic int	 _files_ghbyaddr(void *, void *, va_list);
178255570Straszstatic void	 _files_shent(int stayopen);
179255570Straszstatic void	 _files_ehent(void);
180255570Strasz#ifdef YP
181255570Straszstatic int	 _nis_ghbyname(void *, void *, va_list);
182255570Straszstatic int	 _nis_ghbyaddr(void *, void *, va_list);
183255570Strasz#endif
184255570Straszstatic int	 _dns_ghbyname(void *, void *, va_list);
185255570Straszstatic int	 _dns_ghbyaddr(void *, void *, va_list);
186255570Straszstatic void	 _dns_shent(int stayopen);
187255570Straszstatic void	 _dns_ehent(void);
188255570Strasz#ifdef ICMPNL
189255570Straszstatic int	 _icmp_ghbyaddr(void *, void *, va_list);
190255570Strasz#endif /* ICMPNL */
191255570Strasz
192255570Strasz/* Host lookup order if nsswitch.conf is broken or nonexistant */
193255570Straszstatic const ns_src default_src[] = {
194255570Strasz	{ NSSRC_FILES, NS_SUCCESS },
195255570Strasz	{ NSSRC_DNS, NS_SUCCESS },
196255570Strasz#ifdef ICMPNL
197255570Strasz#define NSSRC_ICMP "icmp"
198255570Strasz	{ NSSRC_ICMP, NS_SUCCESS },
199255570Strasz#endif
200255570Strasz	{ 0 }
201255570Strasz};
202255570Strasz
203255570Strasz/*
204255570Strasz * Check if kernel supports mapped address.
205255570Strasz *	implementation dependent
206255570Strasz */
207255570Strasz#ifdef __KAME__
208255570Strasz#include <sys/sysctl.h>
209255570Strasz#endif /* __KAME__ */
210255570Strasz
211255570Straszstatic int
212255570Strasz_mapped_addr_enabled(void)
213255570Strasz{
214255570Strasz	/* implementation dependent check */
215255570Strasz#if defined(__KAME__) && defined(IPV6CTL_MAPPED_ADDR)
216255570Strasz	int mib[4];
217255570Strasz	size_t len;
218255570Strasz	int val;
219255570Strasz
220255570Strasz	mib[0] = CTL_NET;
221255570Strasz	mib[1] = PF_INET6;
222255570Strasz	mib[2] = IPPROTO_IPV6;
223255570Strasz	mib[3] = IPV6CTL_MAPPED_ADDR;
224255570Strasz	len = sizeof(val);
225255570Strasz	if (sysctl(mib, 4, &val, &len, 0, 0) == 0 && val != 0)
226255570Strasz		return 1;
227265500Strasz#endif /* __KAME__ && IPV6CTL_MAPPED_ADDR */
228265500Strasz	return 0;
229255570Strasz}
230255570Strasz
231255570Strasz/*
232265500Strasz * Functions defined in RFC2553
233255570Strasz *	getipnodebyname, getipnodebyaddr, freehostent
234255570Strasz */
235255570Strasz
236255570Straszstatic struct hostent *
237255570Strasz_ghbyname(const char *name, int af, int flags, int *errp)
238255570Strasz{
239255570Strasz	struct hostent *hp;
240255570Strasz	int rval;
241255570Strasz
242255570Strasz	static const ns_dtab dtab[] = {
243255570Strasz		NS_FILES_CB(_files_ghbyname, NULL)
244255570Strasz		{ NSSRC_DNS, _dns_ghbyname, NULL },
245255570Strasz		NS_NIS_CB(_nis_ghbyname, NULL)
246255570Strasz		{ 0 }
247255570Strasz	};
248265500Strasz
249255570Strasz	if (flags & AI_ADDRCONFIG) {
250255570Strasz		int s;
251255570Strasz
252255570Strasz		/*
253255570Strasz		 * TODO:
254255570Strasz		 * Note that implementation dependent test for address
255255570Strasz		 * configuration should be done everytime called
256255570Strasz		 * (or apropriate interval),
257255570Strasz		 * because addresses will be dynamically assigned or deleted.
258255570Strasz		 */
259255570Strasz		if (af == AF_UNSPEC) {
260255570Strasz			if ((s = _socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
261255570Strasz				af = AF_INET;
262255570Strasz			else {
263255570Strasz				_close(s);
264255570Strasz				if ((s = _socket(AF_INET, SOCK_DGRAM, 0)) < 0)
265255570Strasz					af = AF_INET6;
266255570Strasz				else
267255570Strasz				_close(s);
268255570Strasz			}
269255570Strasz
270255570Strasz		}
271255570Strasz		if (af != AF_UNSPEC) {
272255570Strasz			if ((s = _socket(af, SOCK_DGRAM, 0)) < 0)
273255570Strasz				return NULL;
274255570Strasz			_close(s);
275255570Strasz		}
276255570Strasz	}
277255570Strasz
278255570Strasz	rval = nsdispatch(&hp, dtab, NSDB_HOSTS, "ghbyname", default_src,
279255570Strasz			  name, af, errp);
280255570Strasz	return (rval == NS_SUCCESS) ? hp : NULL;
281255570Strasz}
282255570Strasz
283255570Strasz/* getipnodebyname() internal routine for multiple query(PF_UNSPEC) support. */
284255570Straszstruct hostent *
285255570Strasz_getipnodebyname_multi(const char *name, int af, int flags, int *errp)
286255570Strasz{
287255570Strasz	struct hostent *hp;
288255570Strasz	union inx_addr addrbuf;
289255570Strasz
290255570Strasz	/* XXX: PF_UNSPEC is only supposed to be passed from getaddrinfo() */
291255570Strasz	if (af != AF_INET
292255570Strasz#ifdef INET6
293255570Strasz	    && af != AF_INET6
294255570Strasz#endif
295255570Strasz	    && af != PF_UNSPEC
296255570Strasz		)
297255570Strasz	{
298255570Strasz		*errp = NO_RECOVERY;
299255570Strasz		return NULL;
300255570Strasz	}
301255570Strasz
302255570Strasz#ifdef INET6
303255570Strasz	/* special case for literal address */
304255570Strasz	if (inet_pton(AF_INET6, name, &addrbuf) == 1) {
305255570Strasz		if (af != AF_INET6) {
306255570Strasz			*errp = HOST_NOT_FOUND;
307265523Strasz			return NULL;
308255570Strasz		}
309255570Strasz		return _hpaddr(af, name, &addrbuf, errp);
310255570Strasz	}
311265523Strasz#endif
312255570Strasz	if (inet_aton(name, (struct in_addr *)&addrbuf) == 1) {
313255570Strasz		if (af != AF_INET) {
314255570Strasz			if (MAPADDRENABLED(flags)) {
315255570Strasz				MAPADDR(&addrbuf, &addrbuf.in_addr);
316255570Strasz			} else {
317255570Strasz				*errp = HOST_NOT_FOUND;
318255570Strasz				return NULL;
319255570Strasz			}
320255570Strasz		}
321255570Strasz		return _hpaddr(af, name, &addrbuf, errp);
322255570Strasz	}
323255570Strasz
324255570Strasz	*errp = HOST_NOT_FOUND;
325265500Strasz	hp = _ghbyname(name, af, flags, errp);
326265500Strasz
327265500Strasz#ifdef INET6
328255570Strasz	if (af == AF_INET6
329255570Strasz	&&  ((flags & AI_ALL) || hp == NULL)
330255570Strasz	&&  (MAPADDRENABLED(flags))) {
331265523Strasz		struct hostent *hp2 = _ghbyname(name, AF_INET, flags, errp);
332265523Strasz		if (hp == NULL)
333265523Strasz			hp = _hpmapv6(hp2, errp);
334265523Strasz		else {
335265523Strasz			if (hp2 && strcmp(hp->h_name, hp2->h_name) != 0) {
336265523Strasz				freehostent(hp2);
337265523Strasz				hp2 = NULL;
338255570Strasz			}
339265523Strasz			hp = _hpmerge(hp, hp2, errp);
340265523Strasz		}
341265523Strasz	}
342265523Strasz#endif
343265523Strasz	return _hpsort(hp);
344265523Strasz}
345265523Strasz
346265523Straszstruct hostent *
347265523Straszgetipnodebyname(const char *name, int af, int flags, int *errp)
348265523Strasz{
349265523Strasz	if (af != AF_INET
350265523Strasz#ifdef INET6
351265523Strasz	    && af != AF_INET6
352265523Strasz#endif
353265523Strasz		)
354265523Strasz	{
355265523Strasz		*errp = NO_RECOVERY;
356265523Strasz		return NULL;
357265523Strasz	}
358265523Strasz	return(_getipnodebyname_multi(name, af ,flags, errp));
359265523Strasz}
360265523Strasz
361265523Straszstruct hostent *
362265523Straszgetipnodebyaddr(const void *src, size_t len, int af, int *errp)
363265523Strasz{
364265523Strasz	struct hostent *hp;
365265523Strasz	int rval;
366265523Strasz#ifdef INET6
367265523Strasz	struct in6_addr addrbuf;
368265523Strasz#else
369265523Strasz	struct in_addr addrbuf;
370265523Strasz#endif
371265523Strasz
372265523Strasz	static const ns_dtab dtab[] = {
373265523Strasz		NS_FILES_CB(_files_ghbyaddr, NULL)
374265523Strasz		{ NSSRC_DNS, _dns_ghbyaddr, NULL },
375265523Strasz		NS_NIS_CB(_nis_ghbyaddr, NULL)
376265523Strasz#ifdef ICMPNL
377265523Strasz		{ NSSRC_ICMP, _icmp_ghbyaddr, NULL },
378265523Strasz#endif
379265523Strasz		{ 0 }
380265523Strasz	};
381265523Strasz
382265523Strasz	*errp = HOST_NOT_FOUND;
383265523Strasz
384265523Strasz	switch (af) {
385265523Strasz	case AF_INET:
386265523Strasz		if (len != sizeof(struct in_addr)) {
387265523Strasz			*errp = NO_RECOVERY;
388255570Strasz			return NULL;
389255570Strasz		}
390265500Strasz		if ((long)src & ~(sizeof(struct in_addr) - 1)) {
391255570Strasz			memcpy(&addrbuf, src, len);
392255570Strasz			src = &addrbuf;
393255570Strasz		}
394255570Strasz		if (((struct in_addr *)src)->s_addr == 0)
395255570Strasz			return NULL;
396255570Strasz		break;
397255570Strasz#ifdef INET6
398255570Strasz	case AF_INET6:
399255570Strasz		if (len != sizeof(struct in6_addr)) {
400255570Strasz			*errp = NO_RECOVERY;
401255570Strasz			return NULL;
402255570Strasz		}
403255570Strasz		if ((long)src & ~(sizeof(struct in6_addr) / 2 - 1)) {	/*XXX*/
404255570Strasz			memcpy(&addrbuf, src, len);
405255570Strasz			src = &addrbuf;
406255570Strasz		}
407255570Strasz		if (IN6_IS_ADDR_UNSPECIFIED((struct in6_addr *)src))
408255570Strasz			return NULL;
409255570Strasz		if (IN6_IS_ADDR_V4MAPPED((struct in6_addr *)src)
410255570Strasz		||  IN6_IS_ADDR_V4COMPAT((struct in6_addr *)src)) {
411255570Strasz			src = (char *)src +
412255570Strasz			    (sizeof(struct in6_addr) - sizeof(struct in_addr));
413255570Strasz			af = AF_INET;
414255570Strasz			len = sizeof(struct in_addr);
415255570Strasz		}
416255570Strasz		break;
417255570Strasz#endif
418255570Strasz	default:
419255570Strasz		*errp = NO_RECOVERY;
420255570Strasz		return NULL;
421255570Strasz	}
422255570Strasz
423255570Strasz	rval = nsdispatch(&hp, dtab, NSDB_HOSTS, "ghbyaddr", default_src,
424255570Strasz			  src, len, af, errp);
425255570Strasz	return (rval == NS_SUCCESS) ? hp : NULL;
426255570Strasz}
427255570Strasz
428255570Straszvoid
429255570Straszfreehostent(struct hostent *ptr)
430265523Strasz{
431255570Strasz	free(ptr);
432255570Strasz}
433255570Strasz
434265500Strasz#if 0
435255570Strasz
436255570Strasz/* XXX: should be deprecated */
437255570Straszstruct hostent *
438255570Straszgetnodebyname(const char *name, int af, int flags)
439255570Strasz{
440255570Strasz	return getipnodebyname(name, af, flags, &h_errno);
441255570Strasz}
442255570Strasz
443255570Strasz#ifdef __warn_references
444255570Strasz__warn_references(getnodebyname,
445255570Strasz	"warning: getnodebyname() deprecated, "
446255570Strasz	"should use getaddrinfo() or getipnodebyname()");
447255570Strasz#endif
448255570Strasz
449255570Straszstruct hostent *
450255570Straszgetnodebyaddr(const void *src, size_t len, int af)
451255570Strasz{
452255570Strasz	return getipnodebyaddr(src, len, af, &h_errno);
453255570Strasz}
454255570Strasz
455255570Strasz#ifdef __warn_references
456255570Strasz__warn_references(getnodebyaddr,
457255570Strasz	"warning: getnodebyaddr() deprecated, "
458255570Strasz	"should use getnameinfo() or getipnodebyaddr()");
459255570Strasz#endif
460255570Strasz
461255570Strasz#endif
462255570Strasz
463255570Strasz/*
464255570Strasz * Private utility functions
465265500Strasz */
466255570Strasz
467255570Strasz/*
468255570Strasz * _hpcopy: allocate and copy hostent structure
469255570Strasz */
470255570Straszstatic struct hostent *
471255570Strasz_hpcopy(struct hostent *hp, int *errp)
472255570Strasz{
473255570Strasz	struct hostent *nhp;
474255570Strasz	char *cp, **pp;
475255570Strasz	int size, addrsize;
476255570Strasz	int nalias = 0, naddr = 0;
477255570Strasz	int al_off;
478255570Strasz	int i;
479255570Strasz
480255570Strasz	if (hp == NULL)
481255570Strasz		return hp;
482255570Strasz
483255570Strasz	/* count size to be allocated */
484255570Strasz	size = sizeof(struct hostent);
485255570Strasz	if (hp->h_name != NULL)
486255570Strasz		size += strlen(hp->h_name) + 1;
487255570Strasz	if ((pp = hp->h_aliases) != NULL) {
488255570Strasz		for (i = 0; *pp != NULL; i++, pp++) {
489255570Strasz			if (**pp != '\0') {
490255570Strasz				size += strlen(*pp) + 1;
491255570Strasz				nalias++;
492255570Strasz			}
493255570Strasz		}
494255570Strasz	}
495255570Strasz	/* adjust alignment */
496255570Strasz	size = ALIGN(size);
497255570Strasz	al_off = size;
498255570Strasz	size += sizeof(char *) * (nalias + 1);
499255570Strasz	addrsize = ALIGN(hp->h_length);
500255570Strasz	if ((pp = hp->h_addr_list) != NULL) {
501255570Strasz		while (*pp++ != NULL)
502255570Strasz			naddr++;
503255570Strasz	}
504255570Strasz	size += addrsize * naddr;
505255570Strasz	size += sizeof(char *) * (naddr + 1);
506255570Strasz
507255570Strasz	/* copy */
508255570Strasz	if ((nhp = (struct hostent *)malloc(size)) == NULL) {
509255570Strasz		*errp = TRY_AGAIN;
510255570Strasz		return NULL;
511255570Strasz	}
512255570Strasz	cp = (char *)&nhp[1];
513255570Strasz	if (hp->h_name != NULL) {
514255570Strasz		nhp->h_name = cp;
515255570Strasz		strcpy(cp, hp->h_name);
516255570Strasz		cp += strlen(cp) + 1;
517255570Strasz	} else
518255570Strasz		nhp->h_name = NULL;
519255570Strasz	nhp->h_aliases = (char **)((char *)nhp + al_off);
520255570Strasz	if ((pp = hp->h_aliases) != NULL) {
521255570Strasz		for (i = 0; *pp != NULL; pp++) {
522255570Strasz			if (**pp != '\0') {
523255570Strasz				nhp->h_aliases[i++] = cp;
524255570Strasz				strcpy(cp, *pp);
525255570Strasz				cp += strlen(cp) + 1;
526255570Strasz			}
527255570Strasz		}
528255570Strasz	}
529255570Strasz	nhp->h_aliases[nalias] = NULL;
530255570Strasz	cp = (char *)&nhp->h_aliases[nalias + 1];
531255570Strasz	nhp->h_addrtype = hp->h_addrtype;
532255570Strasz	nhp->h_length = hp->h_length;
533255570Strasz	nhp->h_addr_list = (char **)cp;
534255570Strasz	if ((pp = hp->h_addr_list) != NULL) {
535255570Strasz		cp = (char *)&nhp->h_addr_list[naddr + 1];
536255570Strasz		for (i = 0; *pp != NULL; pp++) {
537255570Strasz			nhp->h_addr_list[i++] = cp;
538255570Strasz			memcpy(cp, *pp, hp->h_length);
539255570Strasz			cp += addrsize;
540255570Strasz		}
541255570Strasz	}
542255570Strasz	nhp->h_addr_list[naddr] = NULL;
543255570Strasz	return nhp;
544255570Strasz}
545255570Strasz
546255570Strasz/*
547255570Strasz * _hpaddr: construct hostent structure with one address
548255570Strasz */
549255570Straszstatic struct hostent *
550255570Strasz_hpaddr(int af, const char *name, void *addr, int *errp)
551255570Strasz{
552255570Strasz	struct hostent *hp, hpbuf;
553255570Strasz	char *addrs[2];
554255570Strasz
555255570Strasz	hp = &hpbuf;
556255570Strasz	hp->h_name = (char *)name;
557255570Strasz	hp->h_aliases = NULL;
558255570Strasz	hp->h_addrtype = af;
559255570Strasz	hp->h_length = ADDRLEN(af);
560255570Strasz	hp->h_addr_list = addrs;
561255570Strasz	addrs[0] = (char *)addr;
562255570Strasz	addrs[1] = NULL;
563255570Strasz	return _hpcopy(hp, errp);
564255570Strasz}
565255570Strasz
566255570Strasz/*
567255570Strasz * _hpmerge: merge 2 hostent structure, arguments will be freed
568255570Strasz */
569255570Straszstatic struct hostent *
570255570Strasz_hpmerge(struct hostent *hp1, struct hostent *hp2, int *errp)
571255570Strasz{
572255570Strasz	int i, j;
573255824Strasz	int naddr, nalias;
574255824Strasz	char **pp;
575255824Strasz	struct hostent *hp, hpbuf;
576255824Strasz	char *aliases[MAXALIASES + 1], *addrs[MAXADDRS + 1];
577255824Strasz	union inx_addr addrbuf[MAXADDRS];
578255570Strasz
579255570Strasz	if (hp1 == NULL)
580255570Strasz		return hp2;
581255570Strasz	if (hp2 == NULL)
582255570Strasz		return hp1;
583255570Strasz
584255570Strasz#define	HP(i)	(i == 1 ? hp1 : hp2)
585255570Strasz	hp = &hpbuf;
586255570Strasz	hp->h_name = (hp1->h_name != NULL ? hp1->h_name : hp2->h_name);
587255570Strasz	hp->h_aliases = aliases;
588255570Strasz	nalias = 0;
589255570Strasz	for (i = 1; i <= 2; i++) {
590255570Strasz		if ((pp = HP(i)->h_aliases) == NULL)
591255570Strasz			continue;
592255570Strasz		for (; nalias < MAXALIASES && *pp != NULL; pp++) {
593255570Strasz			/* check duplicates */
594255570Strasz			for (j = 0; j < nalias; j++)
595255570Strasz				if (strcasecmp(*pp, aliases[j]) == 0)
596255570Strasz					break;
597255570Strasz			if (j == nalias)
598255570Strasz				aliases[nalias++] = *pp;
599255570Strasz		}
600255570Strasz	}
601255570Strasz	aliases[nalias] = NULL;
602255570Strasz#ifdef INET6
603255570Strasz	if (hp1->h_length != hp2->h_length) {
604255570Strasz		hp->h_addrtype = AF_INET6;
605255570Strasz		hp->h_length = sizeof(struct in6_addr);
606255570Strasz	} else {
607255570Strasz#endif
608255570Strasz		hp->h_addrtype = hp1->h_addrtype;
609255570Strasz		hp->h_length = hp1->h_length;
610255570Strasz#ifdef INET6
611255570Strasz	}
612255570Strasz#endif
613255570Strasz	hp->h_addr_list = addrs;
614255570Strasz	naddr = 0;
615255570Strasz	for (i = 1; i <= 2; i++) {
616255570Strasz		if ((pp = HP(i)->h_addr_list) == NULL)
617255570Strasz			continue;
618255570Strasz		if (HP(i)->h_length == hp->h_length) {
619255570Strasz			while (naddr < MAXADDRS && *pp != NULL)
620255570Strasz				addrs[naddr++] = *pp++;
621255570Strasz		} else {
622255570Strasz			/* copy IPv4 addr as mapped IPv6 addr */
623255570Strasz			while (naddr < MAXADDRS && *pp != NULL) {
624255570Strasz				MAPADDR(&addrbuf[naddr], *pp++);
625255570Strasz				addrs[naddr] = (char *)&addrbuf[naddr];
626255570Strasz				naddr++;
627255570Strasz			}
628255570Strasz		}
629255570Strasz	}
630255570Strasz	addrs[naddr] = NULL;
631255570Strasz	hp = _hpcopy(hp, errp);
632255570Strasz	freehostent(hp1);
633255570Strasz	freehostent(hp2);
634255570Strasz	return hp;
635255570Strasz}
636255570Strasz
637255570Strasz/*
638255570Strasz * _hpmapv6: convert IPv4 hostent into IPv4-mapped IPv6 addresses
639255570Strasz */
640255570Strasz#ifdef INET6
641255570Straszstatic struct hostent *
642255570Strasz_hpmapv6(struct hostent *hp, int *errp)
643265500Strasz{
644255570Strasz	struct hostent *hp6;
645255570Strasz
646255570Strasz	if (hp == NULL)
647255570Strasz		return NULL;
648255570Strasz	if (hp->h_addrtype == AF_INET6)
649255570Strasz		return hp;
650255570Strasz
651255570Strasz	/* make dummy hostent to convert IPv6 address */
652255570Strasz	if ((hp6 = (struct hostent *)malloc(sizeof(struct hostent))) == NULL) {
653255570Strasz		*errp = TRY_AGAIN;
654255570Strasz		return NULL;
655255570Strasz	}
656255570Strasz	hp6->h_name = NULL;
657255570Strasz	hp6->h_aliases = NULL;
658255570Strasz	hp6->h_addrtype = AF_INET6;
659255570Strasz	hp6->h_length = sizeof(struct in6_addr);
660255570Strasz	hp6->h_addr_list = NULL;
661255570Strasz	return _hpmerge(hp6, hp, errp);
662255570Strasz}
663255570Strasz#endif
664255570Strasz
665255570Strasz/*
666255570Strasz * _hpsort: sort address by sortlist
667255570Strasz */
668255570Straszstatic struct hostent *
669255570Strasz_hpsort(struct hostent *hp)
670255570Strasz{
671255570Strasz	int i, j, n;
672255570Strasz	u_char *ap, *sp, *mp, **pp;
673255570Strasz	char t;
674255570Strasz	char order[MAXADDRS];
675255570Strasz	int nsort = _res.nsort;
676255570Strasz
677255570Strasz	if (hp == NULL || hp->h_addr_list[1] == NULL || nsort == 0)
678255570Strasz		return hp;
679255570Strasz	for (i = 0; (ap = (u_char *)hp->h_addr_list[i]); i++) {
680255570Strasz		for (j = 0; j < nsort; j++) {
681255570Strasz#ifdef INET6
682255570Strasz			if (_res_ext.sort_list[j].af != hp->h_addrtype)
683255570Strasz				continue;
684255570Strasz			sp = (u_char *)&_res_ext.sort_list[j].addr;
685255570Strasz			mp = (u_char *)&_res_ext.sort_list[j].mask;
686255570Strasz#else
687255570Strasz			sp = (u_char *)&_res.sort_list[j].addr;
688255570Strasz			mp = (u_char *)&_res.sort_list[j].mask;
689255570Strasz#endif
690255570Strasz			for (n = 0; n < hp->h_length; n++) {
691255570Strasz				if ((ap[n] & mp[n]) != sp[n])
692255570Strasz					break;
693255570Strasz			}
694255570Strasz			if (n == hp->h_length)
695255570Strasz				break;
696255570Strasz		}
697255570Strasz		order[i] = j;
698255570Strasz	}
699255570Strasz	n = i;
700255570Strasz	pp = (u_char **)hp->h_addr_list;
701255570Strasz	for (i = 0; i < n - 1; i++) {
702255570Strasz		for (j = i + 1; j < n; j++) {
703255570Strasz			if (order[i] > order[j]) {
704255570Strasz				ap = pp[i];
705255570Strasz				pp[i] = pp[j];
706255570Strasz				pp[j] = ap;
707255570Strasz				t = order[i];
708255570Strasz				order[i] = order[j];
709255570Strasz				order[j] = t;
710255570Strasz			}
711255570Strasz		}
712255570Strasz	}
713255570Strasz	return hp;
714255570Strasz}
715255570Strasz
716255570Straszstatic char *
717255570Strasz_hgetword(char **pp)
718255570Strasz{
719255570Strasz	char c, *p, *ret;
720255570Strasz	const char *sp;
721255570Strasz	static const char sep[] = "# \t\n";
722255570Strasz
723255570Strasz	ret = NULL;
724255570Strasz	for (p = *pp; (c = *p) != '\0'; p++) {
725255570Strasz		for (sp = sep; *sp != '\0'; sp++) {
726255570Strasz			if (c == *sp)
727255570Strasz				break;
728255570Strasz		}
729255570Strasz		if (c == '#')
730255570Strasz			p[1] = '\0';	/* ignore rest of line */
731255570Strasz		if (ret == NULL) {
732255570Strasz			if (*sp == '\0')
733255570Strasz				ret = p;
734255570Strasz		} else {
735255570Strasz			if (*sp != '\0') {
736255570Strasz				*p++ = '\0';
737255570Strasz				break;
738255570Strasz			}
739255570Strasz		}
740255570Strasz	}
741256187Strasz	*pp = p;
742255570Strasz	if (ret == NULL || *ret == '\0')
743255570Strasz		return NULL;
744255570Strasz	return ret;
745256187Strasz}
746256187Strasz
747256187Strasz/*
748255570Strasz * FILES (/etc/hosts)
749256187Strasz */
750255570Strasz
751255570Straszstatic FILE *
752255570Strasz_files_open(int *errp)
753255570Strasz{
754255570Strasz	FILE *fp;
755255570Strasz	fp = fopen(_PATH_HOSTS, "r");
756255570Strasz	if (fp == NULL)
757255570Strasz		*errp = NO_RECOVERY;
758255570Strasz	return fp;
759255570Strasz}
760255570Strasz
761256187Straszstatic int
762256187Strasz_files_ghbyname(void *rval, void *cb_data, va_list ap)
763256187Strasz{
764256187Strasz	const char *name;
765256187Strasz	int af;
766256187Strasz	int *errp;
767256187Strasz	int match, nalias;
768256187Strasz	char *p, *line, *addrstr, *cname;
769256187Strasz	FILE *fp;
770256187Strasz	struct hostent *rethp, *hp, hpbuf;
771256187Strasz	char *aliases[MAXALIASES + 1], *addrs[2];
772256187Strasz	union inx_addr addrbuf;
773256187Strasz	char buf[BUFSIZ];
774255570Strasz	int af0;
775255570Strasz
776256187Strasz	name = va_arg(ap, const char *);
777256187Strasz	af = va_arg(ap, int);
778256187Strasz	errp = va_arg(ap, int *);
779255570Strasz
780256187Strasz	*(struct hostent **)rval = NULL;
781255570Strasz
782255570Strasz	if ((fp = _files_open(errp)) == NULL)
783255570Strasz		return NS_UNAVAIL;
784255570Strasz	rethp = hp = NULL;
785255570Strasz
786255570Strasz	af0 = af;
787256187Strasz	while (fgets(buf, sizeof(buf), fp)) {
788255570Strasz		line = buf;
789256187Strasz		if ((addrstr = _hgetword(&line)) == NULL
790256187Strasz		||  (cname = _hgetword(&line)) == NULL)
791256187Strasz			continue;
792256187Strasz		match = (strcasecmp(cname, name) == 0);
793256187Strasz		nalias = 0;
794256187Strasz		while ((p = _hgetword(&line)) != NULL) {
795256187Strasz			if (!match)
796256187Strasz				match = (strcasecmp(p, name) == 0);
797256187Strasz			if (nalias < MAXALIASES)
798256187Strasz				aliases[nalias++] = p;
799256187Strasz		}
800256187Strasz		if (!match)
801256187Strasz			continue;
802255570Strasz		switch (af0) {
803255570Strasz		case AF_INET:
804255570Strasz			if (inet_aton(addrstr, (struct in_addr *)&addrbuf)
805255570Strasz			    != 1) {
806255570Strasz				*errp = NO_DATA;	/* name found */
807255570Strasz				continue;
808255570Strasz			}
809255570Strasz			af = af0;
810255570Strasz			break;
811255570Strasz#ifdef INET6
812255570Strasz		case AF_INET6:
813255570Strasz			if (inet_pton(af, addrstr, &addrbuf) != 1) {
814255570Strasz				*errp = NO_DATA;	/* name found */
815255570Strasz				continue;
816255570Strasz			}
817255570Strasz			af = af0;
818255570Strasz			break;
819255570Strasz#endif
820255570Strasz		case AF_UNSPEC:
821255570Strasz			if (inet_aton(addrstr, (struct in_addr *)&addrbuf)
822255570Strasz			    == 1) {
823255570Strasz				af = AF_INET;
824255570Strasz				break;
825255570Strasz			}
826255570Strasz#ifdef INET6
827255570Strasz			if (inet_pton(AF_INET6, addrstr, &addrbuf) == 1) {
828255570Strasz				af = AF_INET6;
829255570Strasz				break;
830255570Strasz			}
831255570Strasz#endif
832255570Strasz			*errp = NO_DATA;	/* name found */
833255570Strasz			continue;
834255570Strasz			/* NOTREACHED */
835255570Strasz		}
836255570Strasz		hp = &hpbuf;
837255570Strasz		hp->h_name = cname;
838255570Strasz		hp->h_aliases = aliases;
839255570Strasz		aliases[nalias] = NULL;
840255570Strasz		hp->h_addrtype = af;
841255570Strasz		hp->h_length = ADDRLEN(af);
842255570Strasz		hp->h_addr_list = addrs;
843255570Strasz		addrs[0] = (char *)&addrbuf;
844255570Strasz		addrs[1] = NULL;
845255570Strasz		hp = _hpcopy(hp, errp);
846255570Strasz		rethp = _hpmerge(rethp, hp, errp);
847255570Strasz	}
848255570Strasz	fclose(fp);
849255570Strasz	*(struct hostent **)rval = rethp;
850255570Strasz	return (rethp != NULL) ? NS_SUCCESS : NS_NOTFOUND;
851255570Strasz}
852255570Strasz
853255570Straszstatic int
854255570Strasz_files_ghbyaddr(void *rval, void *cb_data, va_list ap)
855255570Strasz{
856255570Strasz	const void *addr;
857255570Strasz	int addrlen;
858255570Strasz	int af;
859255570Strasz	int *errp;
860255570Strasz	int nalias;
861255570Strasz	char *p, *line;
862255570Strasz	FILE *fp;
863255570Strasz	struct hostent *hp, hpbuf;
864255570Strasz	char *aliases[MAXALIASES + 1], *addrs[2];
865255570Strasz	union inx_addr addrbuf;
866255570Strasz	char buf[BUFSIZ];
867255570Strasz
868255570Strasz	addr = va_arg(ap, const void *);
869255570Strasz	addrlen = va_arg(ap, int);
870255570Strasz	af = va_arg(ap, int);
871255570Strasz	errp = va_arg(ap, int *);
872255570Strasz
873255570Strasz	*(struct hostent**)rval = NULL;
874255570Strasz
875255570Strasz	if ((fp = _files_open(errp)) == NULL)
876255570Strasz		return NS_UNAVAIL;
877255570Strasz	hp = NULL;
878255570Strasz	while (fgets(buf, sizeof(buf), fp)) {
879255570Strasz		line = buf;
880255570Strasz		if ((p = _hgetword(&line)) == NULL
881255570Strasz		||  (af == AF_INET
882255570Strasz		     ? inet_aton(p, (struct in_addr *)&addrbuf)
883255570Strasz		     : inet_pton(af, p, &addrbuf)) != 1
884255570Strasz		||  memcmp(addr, &addrbuf, addrlen) != 0
885255570Strasz		||  (p = _hgetword(&line)) == NULL)
886255570Strasz			continue;
887255570Strasz		hp = &hpbuf;
888255570Strasz		hp->h_name = p;
889255570Strasz		hp->h_aliases = aliases;
890255570Strasz		nalias = 0;
891255570Strasz		while ((p = _hgetword(&line)) != NULL) {
892255570Strasz			if (nalias < MAXALIASES)
893255570Strasz				aliases[nalias++] = p;
894255570Strasz		}
895255570Strasz		aliases[nalias] = NULL;
896255570Strasz		hp->h_addrtype = af;
897255570Strasz		hp->h_length = addrlen;
898255570Strasz		hp->h_addr_list = addrs;
899255570Strasz		addrs[0] = (char *)&addrbuf;
900255570Strasz		addrs[1] = NULL;
901255570Strasz		hp = _hpcopy(hp, errp);
902255570Strasz		break;
903255570Strasz	}
904255570Strasz	fclose(fp);
905255570Strasz	*(struct hostent **)rval = hp;
906255570Strasz	return (hp != NULL) ? NS_SUCCESS : NS_NOTFOUND;
907255570Strasz}
908255570Strasz
909255570Strasz#ifdef YP
910255570Strasz/*
911255570Strasz * NIS
912255570Strasz *
913255570Strasz * XXX actually a hack, these are INET4 specific.
914255570Strasz */
915255570Straszstatic int
916255570Strasz_nis_ghbyname(void *rval, void *cb_data, va_list ap)
917255570Strasz{
918255570Strasz	const char *name;
919255570Strasz	int af;
920255570Strasz	int *errp;
921255570Strasz	struct hostent *hp = NULL;
922255570Strasz
923255570Strasz	name = va_arg(ap, const char *);
924255570Strasz	af = va_arg(ap, int);
925255570Strasz	errp = va_arg(ap, int *);
926255570Strasz
927255570Strasz	if (af == AF_UNSPEC)
928255570Strasz		af = AF_INET;
929255570Strasz	if (af == AF_INET) {
930255570Strasz		hp = _gethostbynisname(name, af);
931255570Strasz		if (hp != NULL)
932255570Strasz			hp = _hpcopy(hp, errp);
933255570Strasz	}
934255570Strasz
935255570Strasz	*(struct hostent **)rval = hp;
936255570Strasz	return (hp != NULL) ? NS_SUCCESS : NS_NOTFOUND;
937255570Strasz
938255570Strasz}
939255570Strasz
940255570Straszstatic int
941255570Strasz_nis_ghbyaddr(void *rval, void *cb_data, va_list ap)
942255570Strasz{
943255570Strasz	const void *addr;
944255570Strasz	int addrlen;
945255570Strasz	int af;
946255570Strasz	int *errp;
947255570Strasz	struct hostent *hp = NULL;
948255570Strasz
949255570Strasz	addr = va_arg(ap, const void *);
950255570Strasz	addrlen = va_arg(ap, int);
951255570Strasz	af = va_arg(ap, int);
952255570Strasz
953255570Strasz	if (af == AF_INET) {
954255570Strasz		hp = _gethostbynisaddr(addr, addrlen, af);
955255570Strasz		if (hp != NULL)
956255570Strasz			hp = _hpcopy(hp, errp);
957255570Strasz	}
958255570Strasz	*(struct hostent **)rval = hp;
959255570Strasz	return (hp != NULL) ? NS_SUCCESS : NS_NOTFOUND;
960255570Strasz}
961255570Strasz#endif
962255570Strasz
963255570Straszstruct __res_type_list {
964255570Strasz        SLIST_ENTRY(__res_type_list) rtl_entry;
965256229Strasz        int     rtl_type;
966255570Strasz};
967256229Strasz
968256229Strasz#if PACKETSZ > 1024
969255570Strasz#define	MAXPACKET	PACKETSZ
970255570Strasz#else
971255570Strasz#define	MAXPACKET	1024
972255570Strasz#endif
973255570Strasz
974256229Strasztypedef union {
975255570Strasz	HEADER hdr;
976255570Strasz	u_char buf[MAXPACKET];
977255570Strasz} querybuf;
978255570Strasz
979255570Straszstatic struct hostent *getanswer __P((const querybuf *, int, const char *,
980255570Strasz	int, struct hostent *, int *));
981256238Strasz
982256238Strasz/*
983256238Strasz * we don't need to take care about sorting, nor IPv4 mapped address here.
984256238Strasz */
985256238Straszstatic struct hostent *
986256238Straszgetanswer(answer, anslen, qname, qtype, template, errp)
987256238Strasz	const querybuf *answer;
988256238Strasz	int anslen;
989256238Strasz	const char *qname;
990256238Strasz	int qtype;
991256238Strasz	struct hostent *template;
992256238Strasz	int *errp;
993256238Strasz{
994256238Strasz	const HEADER *hp;
995256238Strasz	const u_char *cp;
996256238Strasz	int n;
997256238Strasz	const u_char *eom, *erdata;
998256238Strasz	char *bp, **ap, **hap;
999256238Strasz	int type, class, buflen, ancount, qdcount;
1000256238Strasz	int haveanswer, had_error;
1001256238Strasz	char tbuf[MAXDNAME];
1002256238Strasz	const char *tname;
1003256238Strasz	int (*name_ok) __P((const char *));
1004256238Strasz	static char *h_addr_ptrs[MAXADDRS + 1];
1005256238Strasz	static char *host_aliases[MAXALIASES];
1006256238Strasz	static char hostbuf[8*1024];
1007256238Strasz
1008256238Strasz#define BOUNDED_INCR(x) \
1009256238Strasz	do { \
1010256238Strasz		cp += x; \
1011255570Strasz		if (cp > eom) { \
1012256238Strasz			*errp = NO_RECOVERY; \
1013255570Strasz			return (NULL); \
1014255570Strasz		} \
1015255570Strasz	} while (0)
1016256238Strasz
1017256238Strasz#define BOUNDS_CHECK(ptr, count) \
1018255570Strasz	do { \
1019255570Strasz		if ((ptr) + (count) > eom) { \
1020255570Strasz			*errp = NO_RECOVERY; \
1021255570Strasz			return (NULL); \
1022255570Strasz		} \
1023255570Strasz	} while (0)
1024255570Strasz
1025255570Strasz/* XXX do {} while (0) cannot be put here */
1026255570Strasz#define DNS_ASSERT(x) \
1027255570Strasz	{				\
1028255570Strasz		if (!(x)) {		\
1029255570Strasz			cp += n;	\
1030255570Strasz			continue;	\
1031255570Strasz		}			\
1032255570Strasz	}
1033255570Strasz
1034255570Strasz/* XXX do {} while (0) cannot be put here */
1035255570Strasz#define DNS_FATAL(x) \
1036255570Strasz	{				\
1037255570Strasz		if (!(x)) {		\
1038255570Strasz			had_error++;	\
1039255570Strasz			continue;	\
1040255570Strasz		}			\
1041255570Strasz	}
1042255570Strasz
1043255570Strasz	tname = qname;
1044255570Strasz	template->h_name = NULL;
1045255570Strasz	eom = answer->buf + anslen;
1046255570Strasz	switch (qtype) {
1047255570Strasz	case T_A:
1048255570Strasz	case T_AAAA:
1049255570Strasz		name_ok = res_hnok;
1050255570Strasz		break;
1051255570Strasz	case T_PTR:
1052255570Strasz		name_ok = res_dnok;
1053255570Strasz		break;
1054255570Strasz	default:
1055255570Strasz		return (NULL);	/* XXX should be abort(); */
1056255570Strasz	}
1057255570Strasz	/*
1058255570Strasz	 * find first satisfactory answer
1059255570Strasz	 */
1060255570Strasz	hp = &answer->hdr;
1061255570Strasz	ancount = ntohs(hp->ancount);
1062255570Strasz	qdcount = ntohs(hp->qdcount);
1063255570Strasz	bp = hostbuf;
1064255570Strasz	buflen = sizeof hostbuf;
1065255570Strasz	cp = answer->buf;
1066255570Strasz	BOUNDED_INCR(HFIXEDSZ);
1067256229Strasz	if (qdcount != 1) {
1068255570Strasz		*errp = NO_RECOVERY;
1069256229Strasz		return (NULL);
1070256229Strasz	}
1071256229Strasz	n = dn_expand(answer->buf, eom, cp, bp, buflen);
1072256229Strasz	if ((n < 0) || !(*name_ok)(bp)) {
1073256229Strasz		*errp = NO_RECOVERY;
1074256229Strasz		return (NULL);
1075256229Strasz	}
1076256229Strasz	BOUNDED_INCR(n + QFIXEDSZ);
1077255570Strasz	if (qtype == T_A || qtype == T_AAAA) {
1078256229Strasz		/* res_send() has already verified that the query name is the
1079256229Strasz		 * same as the one we sent; this just gets the expanded name
1080256229Strasz		 * (i.e., with the succeeding search-domain tacked on).
1081256229Strasz		 */
1082256229Strasz		n = strlen(bp) + 1;		/* for the \0 */
1083256229Strasz		if (n >= MAXHOSTNAMELEN) {
1084256229Strasz			*errp = NO_RECOVERY;
1085255570Strasz			return (NULL);
1086255570Strasz		}
1087255570Strasz		template->h_name = bp;
1088255570Strasz		bp += n;
1089255570Strasz		buflen -= n;
1090255570Strasz		/* The qname can be abbreviated, but h_name is now absolute. */
1091255570Strasz		qname = template->h_name;
1092255570Strasz	}
1093255570Strasz	ap = host_aliases;
1094255570Strasz	*ap = NULL;
1095256229Strasz	template->h_aliases = host_aliases;
1096256229Strasz	hap = h_addr_ptrs;
1097255570Strasz	*hap = NULL;
1098255570Strasz	template->h_addr_list = h_addr_ptrs;
1099255570Strasz	haveanswer = 0;
1100255570Strasz	had_error = 0;
1101255570Strasz	while (ancount-- > 0 && cp < eom && !had_error) {
1102255570Strasz		n = dn_expand(answer->buf, eom, cp, bp, buflen);
1103255570Strasz		DNS_FATAL(n >= 0);
1104255570Strasz		DNS_FATAL((*name_ok)(bp));
1105255570Strasz		cp += n;			/* name */
1106255570Strasz		BOUNDS_CHECK(cp, 3 * INT16SZ + INT32SZ);
1107255570Strasz		type = _getshort(cp);
1108255570Strasz 		cp += INT16SZ;			/* type */
1109255570Strasz		class = _getshort(cp);
1110255570Strasz 		cp += INT16SZ + INT32SZ;	/* class, TTL */
1111255570Strasz		n = _getshort(cp);
1112255570Strasz		cp += INT16SZ;			/* len */
1113255570Strasz		BOUNDS_CHECK(cp, n);
1114255570Strasz		erdata = cp + n;
1115255570Strasz		DNS_ASSERT(class == C_IN);
1116255570Strasz		if ((qtype == T_A || qtype == T_AAAA) && type == T_CNAME) {
1117255570Strasz			if (ap >= &host_aliases[MAXALIASES-1])
1118255570Strasz				continue;
1119256229Strasz			n = dn_expand(answer->buf, eom, cp, tbuf, sizeof tbuf);
1120256229Strasz			DNS_FATAL(n >= 0);
1121255570Strasz			DNS_FATAL((*name_ok)(tbuf));
1122256229Strasz			cp += n;
1123256229Strasz			if (cp != erdata) {
1124255570Strasz				*errp = NO_RECOVERY;
1125255570Strasz				return (NULL);
1126255570Strasz			}
1127255570Strasz			/* Store alias. */
1128255570Strasz			*ap++ = bp;
1129255570Strasz			n = strlen(bp) + 1;	/* for the \0 */
1130255570Strasz			DNS_FATAL(n < MAXHOSTNAMELEN);
1131255570Strasz			bp += n;
1132255570Strasz			buflen -= n;
1133255570Strasz			/* Get canonical name. */
1134255570Strasz			n = strlen(tbuf) + 1;	/* for the \0 */
1135255570Strasz			DNS_FATAL(n <= buflen);
1136255570Strasz			DNS_FATAL(n < MAXHOSTNAMELEN);
1137255570Strasz			strcpy(bp, tbuf);
1138255570Strasz			template->h_name = bp;
1139255570Strasz			bp += n;
1140255570Strasz			buflen -= n;
1141255570Strasz			continue;
1142255570Strasz		}
1143255570Strasz		if (qtype == T_PTR && type == T_CNAME) {
1144255570Strasz			n = dn_expand(answer->buf, eom, cp, tbuf, sizeof tbuf);
1145255570Strasz			if (n < 0 || !res_dnok(tbuf)) {
1146255570Strasz				had_error++;
1147255570Strasz				continue;
1148255570Strasz			}
1149255570Strasz			cp += n;
1150255570Strasz			if (cp != erdata) {
1151255570Strasz				*errp = NO_RECOVERY;
1152255570Strasz				return (NULL);
1153255570Strasz			}
1154255570Strasz			/* Get canonical name. */
1155255570Strasz			n = strlen(tbuf) + 1;	/* for the \0 */
1156255570Strasz			if (n > buflen || n >= MAXHOSTNAMELEN) {
1157255570Strasz				had_error++;
1158255570Strasz				continue;
1159255570Strasz			}
1160255570Strasz			strcpy(bp, tbuf);
1161255570Strasz			tname = bp;
1162255570Strasz			bp += n;
1163255570Strasz			buflen -= n;
1164255570Strasz			continue;
1165255570Strasz		}
1166255570Strasz		DNS_ASSERT(type == qtype);
1167255570Strasz		switch (type) {
1168255570Strasz		case T_PTR:
1169255570Strasz			DNS_ASSERT(strcasecmp(tname, bp) == 0);
1170255570Strasz			n = dn_expand(answer->buf, eom, cp, bp, buflen);
1171255570Strasz			DNS_FATAL(n >= 0);
1172255570Strasz			DNS_FATAL(res_hnok(bp));
1173255570Strasz#if MULTI_PTRS_ARE_ALIASES
1174255570Strasz			cp += n;
1175255570Strasz			if (cp != erdata) {
1176255570Strasz				*errp = NO_RECOVERY;
1177255570Strasz				return (NULL);
1178255570Strasz			}
1179255570Strasz			if (!haveanswer)
1180255570Strasz				template->h_name = bp;
1181255570Strasz			else if (ap < &host_aliases[MAXALIASES-1])
1182255570Strasz				*ap++ = bp;
1183255570Strasz			else
1184255570Strasz				n = -1;
1185255570Strasz			if (n != -1) {
1186255570Strasz				n = strlen(bp) + 1;	/* for the \0 */
1187255570Strasz				if (n >= MAXHOSTNAMELEN) {
1188255570Strasz					had_error++;
1189255570Strasz					break;
1190255570Strasz				}
1191255570Strasz				bp += n;
1192255570Strasz				buflen -= n;
1193255570Strasz			}
1194255570Strasz			break;
1195255570Strasz#else
1196255570Strasz			template->h_name = bp;
1197255570Strasz			*errp = NETDB_SUCCESS;
1198255570Strasz			return (template);
1199255570Strasz#endif
1200255570Strasz		case T_A:
1201255570Strasz		case T_AAAA:
1202255570Strasz			DNS_ASSERT(strcasecmp(template->h_name, bp) == 0);
1203255570Strasz			DNS_ASSERT(n == template->h_length);
1204255570Strasz			if (!haveanswer) {
1205265521Strasz				int nn;
1206255570Strasz
1207255570Strasz				template->h_name = bp;
1208265521Strasz				nn = strlen(bp) + 1;	/* for the \0 */
1209255570Strasz				bp += nn;
1210255570Strasz				buflen -= nn;
1211255570Strasz			}
1212255570Strasz			bp = (char *)ALIGN(bp);
1213255570Strasz
1214255570Strasz			DNS_FATAL(bp + n < &hostbuf[sizeof hostbuf]);
1215255570Strasz			DNS_ASSERT(hap < &h_addr_ptrs[MAXADDRS-1]);
1216255570Strasz#ifdef FILTER_V4MAPPED
1217255570Strasz			if (type == T_AAAA) {
1218255570Strasz				struct in6_addr in6;
1219255570Strasz				memcpy(&in6, cp, sizeof(in6));
1220255570Strasz				DNS_ASSERT(IN6_IS_ADDR_V4MAPPED(&in6) == 0);
1221255570Strasz			}
1222255570Strasz#endif
1223255570Strasz			bcopy(cp, *hap++ = bp, n);
1224255570Strasz			bp += n;
1225255570Strasz			buflen -= n;
1226255570Strasz			cp += n;
1227255570Strasz			if (cp != erdata) {
1228255570Strasz				*errp = NO_RECOVERY;
1229255570Strasz				return (NULL);
1230255570Strasz			}
1231255570Strasz			break;
1232255570Strasz		default:
1233255570Strasz			abort();
1234255570Strasz		}
1235255570Strasz		if (!had_error)
1236255570Strasz			haveanswer++;
1237255570Strasz	}
1238255570Strasz	if (haveanswer) {
1239255570Strasz		*ap = NULL;
1240255570Strasz		*hap = NULL;
1241255570Strasz		if (!template->h_name) {
1242255570Strasz			n = strlen(qname) + 1;	/* for the \0 */
1243255570Strasz			if (n > buflen || n >= MAXHOSTNAMELEN)
1244255570Strasz				goto no_recovery;
1245255570Strasz			strcpy(bp, qname);
1246255570Strasz			template->h_name = bp;
1247255570Strasz			bp += n;
1248255570Strasz			buflen -= n;
1249255570Strasz		}
1250255570Strasz		*errp = NETDB_SUCCESS;
1251255570Strasz		return (template);
1252255570Strasz	}
1253255570Strasz no_recovery:
1254255570Strasz	*errp = NO_RECOVERY;
1255255570Strasz	return (NULL);
1256255570Strasz
1257255570Strasz#undef BOUNDED_INCR
1258255570Strasz#undef BOUNDS_CHECK
1259255570Strasz#undef DNS_ASSERT
1260255570Strasz#undef DNS_FATAL
1261255570Strasz}
1262255570Strasz
1263259306Strasz/* res_search() variant with multiple query support. */
1264259306Straszstatic struct hostent *
1265259306Strasz_res_search_multi(name, rtl, errp)
1266259306Strasz	const char *name;	/* domain name */
1267259306Strasz	struct	__res_type_list *rtl; /* list of query types */
1268259306Strasz	int *errp;
1269259306Strasz{
1270259306Strasz	const char *cp, * const *domain;
1271259306Strasz	struct hostent *hp0 = NULL, *hp;
1272259306Strasz	struct hostent hpbuf;
1273259306Strasz	u_int dots;
1274259306Strasz	int trailing_dot, ret, saved_herrno;
1275255570Strasz	int got_nodata = 0, got_servfail = 0, tried_as_is = 0;
1276255570Strasz	struct __res_type_list *rtl0 = rtl;
1277255570Strasz	querybuf buf;
1278255570Strasz
1279255570Strasz	if ((_res.options & RES_INIT) == 0 && res_init() == -1) {
1280255570Strasz		*errp = NETDB_INTERNAL;
1281255570Strasz		return (NULL);
1282255570Strasz	}
1283255570Strasz	dots = 0;
1284255570Strasz	for (cp = name; *cp; cp++)
1285255570Strasz		dots += (*cp == '.');
1286255570Strasz	trailing_dot = 0;
1287255570Strasz	if (cp > name && *--cp == '.')
1288255570Strasz		trailing_dot++;
1289255570Strasz
1290255570Strasz	/* If there aren't any dots, it could be a user-level alias */
1291255570Strasz	if (!dots && (cp = hostalias(name)) != NULL) {
1292255570Strasz		for(rtl = rtl0; rtl != NULL;
1293255570Strasz		    rtl = SLIST_NEXT(rtl, rtl_entry)) {
1294255570Strasz			ret = res_query(cp, C_IN, rtl->rtl_type, buf.buf,
1295255570Strasz					     sizeof(buf.buf));
1296265487Strasz			if (ret > 0) {
1297265487Strasz				hpbuf.h_addrtype = (rtl->rtl_type == T_AAAA)
1298255570Strasz				    ? AF_INET6 : AF_INET;
1299255570Strasz				hpbuf.h_length = ADDRLEN(hpbuf.h_addrtype);
1300255570Strasz				hp = getanswer(&buf, ret, name, rtl->rtl_type,
1301255570Strasz						    &hpbuf, errp);
1302255570Strasz				if (!hp)
1303255570Strasz					continue;
1304255570Strasz				hp = _hpcopy(&hpbuf, errp);
1305255570Strasz				hp0 = _hpmerge(hp0, hp, errp);
1306265526Strasz			}
1307265526Strasz		}
1308265526Strasz		return (hp0);
1309265526Strasz	}
1310265526Strasz
1311265526Strasz	/*
1312265526Strasz	 * If there are dots in the name already, let's just give it a try
1313265526Strasz	 * 'as is'.  The threshold can be set with the "ndots" option.
1314265526Strasz	 */
1315265526Strasz	saved_herrno = -1;
1316265526Strasz	if (dots >= _res.ndots) {
1317265526Strasz		for(rtl = rtl0; rtl != NULL;
1318265526Strasz		    rtl = SLIST_NEXT(rtl, rtl_entry)) {
1319255570Strasz			ret = res_querydomain(name, NULL, C_IN, rtl->rtl_type,
1320255570Strasz					      buf.buf, sizeof(buf.buf));
1321255570Strasz			if (ret > 0) {
1322255570Strasz				hpbuf.h_addrtype = (rtl->rtl_type == T_AAAA)
1323255570Strasz				    ? AF_INET6 : AF_INET;
1324255570Strasz				hpbuf.h_length = ADDRLEN(hpbuf.h_addrtype);
1325255570Strasz				hp = getanswer(&buf, ret, name, rtl->rtl_type,
1326255570Strasz						    &hpbuf, errp);
1327255570Strasz				if (!hp)
1328255570Strasz					continue;
1329255570Strasz				hp = _hpcopy(&hpbuf, errp);
1330255570Strasz				hp0 = _hpmerge(hp0, hp, errp);
1331255570Strasz			}
1332255570Strasz		}
1333255570Strasz		if (hp0 != NULL)
1334255570Strasz			return (hp0);
1335255570Strasz		saved_herrno = *errp;
1336255570Strasz		tried_as_is++;
1337255570Strasz	}
1338255570Strasz
1339255570Strasz	/*
1340255570Strasz	 * We do at least one level of search if
1341255570Strasz	 *	- there is no dot and RES_DEFNAME is set, or
1342255570Strasz	 *	- there is at least one dot, there is no trailing dot,
1343255570Strasz	 *	  and RES_DNSRCH is set.
1344255570Strasz	 */
1345255570Strasz	if ((!dots && (_res.options & RES_DEFNAMES)) ||
1346255570Strasz	    (dots && !trailing_dot && (_res.options & RES_DNSRCH))) {
1347255570Strasz		int done = 0;
1348255570Strasz
1349255570Strasz		for (domain = (const char * const *)_res.dnsrch;
1350255570Strasz		     *domain && !done;
1351255570Strasz		     domain++) {
1352255570Strasz
1353255570Strasz			for(rtl = rtl0; rtl != NULL;
1354255570Strasz			    rtl = SLIST_NEXT(rtl, rtl_entry)) {
1355255570Strasz				ret = res_querydomain(name, *domain, C_IN,
1356255570Strasz						      rtl->rtl_type,
1357255570Strasz						      buf.buf, sizeof(buf.buf));
1358255570Strasz				if (ret > 0) {
1359255570Strasz					hpbuf.h_addrtype = (rtl->rtl_type == T_AAAA)
1360255570Strasz					    ? AF_INET6 : AF_INET;
1361255570Strasz					hpbuf.h_length = ADDRLEN(hpbuf.h_addrtype);
1362255570Strasz					hp = getanswer(&buf, ret, name,
1363255570Strasz					    rtl->rtl_type, &hpbuf, errp);
1364255570Strasz					if (!hp)
1365255570Strasz						continue;
1366255570Strasz					hp = _hpcopy(&hpbuf, errp);
1367255570Strasz					hp0 = _hpmerge(hp0, hp, errp);
1368255570Strasz				}
1369255570Strasz			}
1370255570Strasz			if (hp0 != NULL)
1371255570Strasz				return (hp0);
1372255570Strasz
1373255570Strasz			/*
1374255570Strasz			 * If no server present, give up.
1375255570Strasz			 * If name isn't found in this domain,
1376255570Strasz			 * keep trying higher domains in the search list
1377255570Strasz			 * (if that's enabled).
1378255570Strasz			 * On a NO_DATA error, keep trying, otherwise
1379255570Strasz			 * a wildcard entry of another type could keep us
1380255570Strasz			 * from finding this entry higher in the domain.
1381255570Strasz			 * If we get some other error (negative answer or
1382255570Strasz			 * server failure), then stop searching up,
1383255570Strasz			 * but try the input name below in case it's
1384255570Strasz			 * fully-qualified.
1385255570Strasz			 */
1386255570Strasz			if (errno == ECONNREFUSED) {
1387255570Strasz				*errp = TRY_AGAIN;
1388255570Strasz				return (NULL);
1389255570Strasz			}
1390255570Strasz
1391255570Strasz			switch (*errp) {
1392255570Strasz			case NO_DATA:
1393255570Strasz				got_nodata++;
1394255570Strasz				/* FALLTHROUGH */
1395255570Strasz			case HOST_NOT_FOUND:
1396255570Strasz				/* keep trying */
1397255570Strasz				break;
1398255570Strasz			case TRY_AGAIN:
1399255570Strasz				if (buf.hdr.rcode == SERVFAIL) {
1400255570Strasz					/* try next search element, if any */
1401255570Strasz					got_servfail++;
1402255570Strasz					break;
1403255570Strasz				}
1404255570Strasz				/* FALLTHROUGH */
1405255570Strasz			default:
1406255570Strasz				/* anything else implies that we're done */
1407255570Strasz				done++;
1408255570Strasz			}
1409255570Strasz
1410255570Strasz			/* if we got here for some reason other than DNSRCH,
1411255570Strasz			 * we only wanted one iteration of the loop, so stop.
1412255570Strasz			 */
1413255570Strasz			if (!(_res.options & RES_DNSRCH))
1414255570Strasz				done++;
1415255570Strasz		}
1416255570Strasz	}
1417255570Strasz
1418255570Strasz	/*
1419255570Strasz	 * If we have not already tried the name "as is", do that now.
1420255570Strasz	 * note that we do this regardless of how many dots were in the
1421255570Strasz	 * name or whether it ends with a dot unless NOTLDQUERY is set.
1422255570Strasz	 */
1423255570Strasz	if (!tried_as_is && (dots || !(_res.options & RES_NOTLDQUERY))) {
1424255570Strasz		for(rtl = rtl0; rtl != NULL;
1425255570Strasz		    rtl = SLIST_NEXT(rtl, rtl_entry)) {
1426255570Strasz			ret = res_querydomain(name, NULL, C_IN, rtl->rtl_type,
1427255570Strasz					      buf.buf, sizeof(buf.buf));
1428255570Strasz			if (ret > 0) {
1429265526Strasz				hpbuf.h_addrtype = (rtl->rtl_type == T_AAAA)
1430265526Strasz				    ? AF_INET6 : AF_INET;
1431265526Strasz				hpbuf.h_length = ADDRLEN(hpbuf.h_addrtype);
1432255570Strasz				hp = getanswer(&buf, ret, name, rtl->rtl_type,
1433265526Strasz				    &hpbuf, errp);
1434255570Strasz				if (!hp)
1435255570Strasz					continue;
1436255570Strasz				hp = _hpcopy(&hpbuf, errp);
1437255570Strasz				hp0 = _hpmerge(hp0, hp, errp);
1438255570Strasz			}
1439265526Strasz		}
1440265526Strasz		if (hp0 != NULL)
1441255570Strasz			return (hp0);
1442255570Strasz	}
1443255570Strasz
1444255570Strasz	/* if we got here, we didn't satisfy the search.
1445255570Strasz	 * if we did an initial full query, return that query's h_errno
1446255570Strasz	 * (note that we wouldn't be here if that query had succeeded).
1447255570Strasz	 * else if we ever got a nodata, send that back as the reason.
1448255570Strasz	 * else send back meaningless h_errno, that being the one from
1449255570Strasz	 * the last DNSRCH we did.
1450255570Strasz	 */
1451255570Strasz	if (saved_herrno != -1)
1452255570Strasz		*errp = saved_herrno;
1453255570Strasz	else if (got_nodata)
1454255570Strasz		*errp = NO_DATA;
1455255570Strasz	else if (got_servfail)
1456255570Strasz		*errp = TRY_AGAIN;
1457255570Strasz	return (NULL);
1458255570Strasz}
1459255570Strasz
1460255570Straszstatic int
1461255570Strasz_dns_ghbyname(void *rval, void *cb_data, va_list ap)
1462255570Strasz{
1463255570Strasz	const char *name;
1464255570Strasz	int af;
1465255570Strasz	int *errp;
1466255570Strasz	struct __res_type_list *rtl, rtl4;
1467255570Strasz#ifdef INET6
1468255570Strasz	struct __res_type_list rtl6;
1469255570Strasz#endif
1470255570Strasz
1471255570Strasz	name = va_arg(ap, const char *);
1472255570Strasz	af = va_arg(ap, int);
1473255570Strasz	errp = va_arg(ap, int *);
1474255570Strasz
1475255570Strasz#ifdef INET6
1476255570Strasz	switch (af) {
1477255570Strasz	case AF_UNSPEC:
1478255570Strasz		SLIST_NEXT(&rtl4, rtl_entry) = NULL; rtl4.rtl_type = T_A;
1479255570Strasz		SLIST_NEXT(&rtl6, rtl_entry) = &rtl4; rtl6.rtl_type = T_AAAA;
1480255570Strasz		rtl = &rtl6;
1481255570Strasz		break;
1482255570Strasz	case AF_INET6:
1483255570Strasz		SLIST_NEXT(&rtl6, rtl_entry) = NULL; rtl6.rtl_type = T_AAAA;
1484255570Strasz		rtl = &rtl6;
1485255570Strasz		break;
1486255570Strasz	case AF_INET:
1487255570Strasz		SLIST_NEXT(&rtl4, rtl_entry) = NULL; rtl4.rtl_type = T_A;
1488255570Strasz		rtl = &rtl4;
1489255570Strasz		break;
1490255570Strasz	}
1491255570Strasz#else
1492255570Strasz	SLIST_NEXT(&rtl4, rtl_entry) = NULL; rtl4.rtl_type = T_A;
1493255570Strasz	rtl = &rtl4;
1494255570Strasz#endif
1495255570Strasz	*(struct hostent **)rval = _res_search_multi(name, rtl, errp);
1496255570Strasz	return (*(struct hostent **)rval != NULL) ? NS_SUCCESS : NS_NOTFOUND;
1497255570Strasz}
1498255570Strasz
1499255570Straszstatic int
1500255570Strasz_dns_ghbyaddr(void *rval, void *cb_data, va_list ap)
1501255570Strasz{
1502255570Strasz	const void *addr;
1503255570Strasz	int addrlen;
1504255570Strasz	int af;
1505255570Strasz	int *errp;
1506255570Strasz	int n;
1507255570Strasz	struct hostent *hp;
1508255570Strasz	u_char c, *cp;
1509255570Strasz	char *bp;
1510255570Strasz	struct hostent hbuf;
1511255570Strasz	int na;
1512255570Strasz#ifdef INET6
1513255570Strasz	static const char hex[] = "0123456789abcdef";
1514255570Strasz#endif
1515255570Strasz	querybuf buf;
1516255570Strasz	char qbuf[MAXDNAME+1];
1517255570Strasz	char *hlist[2];
1518255570Strasz
1519255570Strasz	addr = va_arg(ap, const void *);
1520255570Strasz	addrlen = va_arg(ap, int);
1521255570Strasz	af = va_arg(ap, int);
1522255570Strasz	errp = va_arg(ap, int *);
1523255570Strasz
1524255570Strasz	*(struct hostent **)rval = NULL;
1525255570Strasz
1526255570Strasz#ifdef INET6
1527255570Strasz	/* XXX */
1528255570Strasz	if (af == AF_INET6 && IN6_IS_ADDR_LINKLOCAL((struct in6_addr *)addr))
1529255570Strasz		return NS_NOTFOUND;
1530255570Strasz#endif
1531255570Strasz
1532255570Strasz	if ((_res.options & RES_INIT) == 0) {
1533255570Strasz		if (res_init() < 0) {
1534255570Strasz			*errp = h_errno;
1535255570Strasz			return NS_UNAVAIL;
1536255570Strasz		}
1537255570Strasz	}
1538255570Strasz	memset(&hbuf, 0, sizeof(hbuf));
1539255570Strasz	hbuf.h_name = NULL;
1540255570Strasz	hbuf.h_addrtype = af;
1541255570Strasz	hbuf.h_length = addrlen;
1542255570Strasz	na = 0;
1543255570Strasz
1544255570Strasz	/* XXX assumes that MAXDNAME is big enough */
1545255570Strasz	n = 0;
1546255570Strasz	bp = qbuf;
1547255570Strasz	cp = (u_char *)addr+addrlen-1;
1548255570Strasz	switch (af) {
1549255570Strasz#ifdef INET6
1550255570Strasz	case AF_INET6:
1551255570Strasz		for (; n < addrlen; n++, cp--) {
1552255570Strasz			c = *cp;
1553255570Strasz			*bp++ = hex[c & 0xf];
1554255570Strasz			*bp++ = '.';
1555255570Strasz			*bp++ = hex[c >> 4];
1556255570Strasz			*bp++ = '.';
1557255570Strasz		}
1558255570Strasz		strcpy(bp, "ip6.int");
1559255570Strasz		break;
1560255570Strasz#endif
1561255570Strasz	default:
1562255570Strasz		for (; n < addrlen; n++, cp--) {
1563255570Strasz			c = *cp;
1564255570Strasz			if (c >= 100)
1565255570Strasz				*bp++ = '0' + c / 100;
1566255570Strasz			if (c >= 10)
1567255570Strasz				*bp++ = '0' + (c % 100) / 10;
1568255570Strasz			*bp++ = '0' + c % 10;
1569255570Strasz			*bp++ = '.';
1570255570Strasz		}
1571255570Strasz		strcpy(bp, "in-addr.arpa");
1572255570Strasz		break;
1573255570Strasz	}
1574255570Strasz
1575255570Strasz	n = res_query(qbuf, C_IN, T_PTR, buf.buf, sizeof buf.buf);
1576255570Strasz	if (n < 0) {
1577255570Strasz		*errp = h_errno;
1578255570Strasz		return NS_UNAVAIL;
1579255570Strasz	}
1580255570Strasz	hp = getanswer(&buf, n, qbuf, T_PTR, &hbuf, errp);
1581255570Strasz	if (!hp)
1582255570Strasz		return NS_NOTFOUND;
1583255570Strasz	hbuf.h_addrtype = af;
1584255570Strasz	hbuf.h_length = addrlen;
1585255570Strasz	hbuf.h_addr_list = hlist;
1586255570Strasz	hlist[0] = (char *)addr;
1587255570Strasz	hlist[1] = NULL;
1588255570Strasz	*(struct hostent **)rval = _hpcopy(&hbuf, errp);
1589255570Strasz	return NS_SUCCESS;
1590255570Strasz}
1591255570Strasz
1592255570Straszstatic void
1593255570Strasz_dns_shent(int stayopen)
1594255570Strasz{
1595255570Strasz	if ((_res.options & RES_INIT) == 0) {
1596255570Strasz		if (res_init() < 0)
1597255570Strasz			return;
1598255570Strasz	}
1599255570Strasz	if (stayopen)
1600255570Strasz		_res.options |= RES_STAYOPEN | RES_USEVC;
1601255570Strasz}
1602255570Strasz
1603255570Straszstatic void
1604255678Strasz_dns_ehent(void)
1605255570Strasz{
1606255570Strasz	_res.options &= ~(RES_STAYOPEN | RES_USEVC);
1607255570Strasz	res_close();
1608255570Strasz}
1609255678Strasz
1610255678Strasz#ifdef ICMPNL
1611255678Strasz
1612255678Strasz/*
1613255678Strasz * experimental:
1614255678Strasz *	draft-ietf-ipngwg-icmp-namelookups-02.txt
1615255570Strasz *	ifindex is assumed to be encoded in addr.
1616255570Strasz */
1617255570Strasz#include <sys/uio.h>
1618255570Strasz#include <netinet/ip6.h>
1619255570Strasz#include <netinet/icmp6.h>
1620255570Strasz
1621255678Straszstruct _icmp_host_cache {
1622255678Strasz	struct _icmp_host_cache *hc_next;
1623255678Strasz	int hc_ifindex;
1624255678Strasz	struct in6_addr hc_addr;
1625255678Strasz	char *hc_name;
1626255678Strasz};
1627255678Strasz
1628255678Straszstatic char *
1629255678Strasz_icmp_fqdn_query(const struct in6_addr *addr, int ifindex)
1630255678Strasz{
1631255678Strasz	int s;
1632255678Strasz	struct icmp6_filter filter;
1633255678Strasz	struct msghdr msg;
1634255678Strasz	struct cmsghdr *cmsg;
1635255678Strasz	struct in6_pktinfo *pkt;
1636255678Strasz	char cbuf[256];
1637255570Strasz	char buf[1024];
1638255570Strasz	int cc;
1639265496Strasz	struct icmp6_fqdn_query *fq;
1640255570Strasz	struct icmp6_fqdn_reply *fr;
1641255570Strasz	struct _icmp_host_cache *hc;
1642255570Strasz	struct sockaddr_in6 sin6;
1643255570Strasz	struct iovec iov;
1644265500Strasz	fd_set s_fds, fds;
1645255570Strasz	struct timeval tout;
1646255570Strasz	int len;
1647255570Strasz	char *name;
1648255570Strasz	static int pid;
1649255570Strasz	static struct _icmp_host_cache *hc_head;
1650255570Strasz
1651255570Strasz	for (hc = hc_head; hc; hc = hc->hc_next) {
1652255570Strasz		if (hc->hc_ifindex == ifindex
1653255570Strasz		&&  IN6_ARE_ADDR_EQUAL(&hc->hc_addr, addr))
1654255570Strasz			return hc->hc_name;
1655255570Strasz	}
1656255570Strasz
1657255570Strasz	if (pid == 0)
1658255570Strasz		pid = getpid();
1659255570Strasz
1660255570Strasz	ICMP6_FILTER_SETBLOCKALL(&filter);
1661255570Strasz	ICMP6_FILTER_SETPASS(ICMP6_FQDN_REPLY, &filter);
1662255570Strasz
1663255570Strasz	FD_ZERO(&s_fds);
1664255570Strasz	tout.tv_sec = 0;
1665255570Strasz	tout.tv_usec = 200000;	/*XXX: 200ms*/
1666255570Strasz
1667265521Strasz	fq = (struct icmp6_fqdn_query *)buf;
1668255570Strasz	fq->icmp6_fqdn_type = ICMP6_FQDN_QUERY;
1669255570Strasz	fq->icmp6_fqdn_code = 0;
1670265521Strasz	fq->icmp6_fqdn_cksum = 0;
1671255570Strasz	fq->icmp6_fqdn_id = (u_short)pid;
1672255570Strasz	fq->icmp6_fqdn_unused = 0;
1673255570Strasz	fq->icmp6_fqdn_cookie[0] = 0;
1674255570Strasz	fq->icmp6_fqdn_cookie[1] = 0;
1675255570Strasz
1676255570Strasz	memset(&sin6, 0, sizeof(sin6));
1677255570Strasz	sin6.sin6_family = AF_INET6;
1678255570Strasz	sin6.sin6_addr = *addr;
1679255570Strasz
1680255570Strasz	memset(&msg, 0, sizeof(msg));
1681255570Strasz	msg.msg_name = (caddr_t)&sin6;
1682255570Strasz	msg.msg_namelen = sizeof(sin6);
1683255570Strasz	msg.msg_iov = &iov;
1684255570Strasz	msg.msg_iovlen = 1;
1685255570Strasz	msg.msg_control = NULL;
1686255570Strasz	msg.msg_controllen = 0;
1687255570Strasz	iov.iov_base = (caddr_t)buf;
1688255570Strasz	iov.iov_len = sizeof(struct icmp6_fqdn_query);
1689255570Strasz
1690255570Strasz	if (ifindex) {
1691255570Strasz		msg.msg_control = cbuf;
1692255570Strasz		msg.msg_controllen = sizeof(cbuf);
1693255570Strasz		cmsg = CMSG_FIRSTHDR(&msg);
1694255570Strasz		cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
1695255570Strasz		cmsg->cmsg_level = IPPROTO_IPV6;
1696255570Strasz		cmsg->cmsg_type = IPV6_PKTINFO;
1697255570Strasz		pkt = (struct in6_pktinfo *)&cmsg[1];
1698255570Strasz		memset(&pkt->ipi6_addr, 0, sizeof(struct in6_addr));
1699255570Strasz		pkt->ipi6_ifindex = ifindex;
1700255570Strasz		cmsg = CMSG_NXTHDR(&msg, cmsg);
1701255570Strasz		msg.msg_controllen = (char *)cmsg - cbuf;
1702255570Strasz	}
1703255570Strasz
1704255570Strasz	if ((s = _socket(PF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) < 0)
1705255570Strasz		return NULL;
1706255570Strasz	(void)_setsockopt(s, IPPROTO_ICMPV6, ICMP6_FILTER,
1707255570Strasz			 (char *)&filter, sizeof(filter));
1708255570Strasz	cc = _sendmsg(s, &msg, 0);
1709255570Strasz	if (cc < 0) {
1710255570Strasz		_close(s);
1711255570Strasz		return NULL;
1712255570Strasz	}
1713255570Strasz	FD_SET(s, &s_fds);
1714255570Strasz	for (;;) {
1715255570Strasz		fds = s_fds;
1716255570Strasz		if (_select(s + 1, &fds, NULL, NULL, &tout) <= 0) {
1717255570Strasz			_close(s);
1718255570Strasz			return NULL;
1719255570Strasz		}
1720255570Strasz		len = sizeof(sin6);
1721255570Strasz		cc = _recvfrom(s, buf, sizeof(buf), 0,
1722255570Strasz			      (struct sockaddr *)&sin6, &len);
1723255570Strasz		if (cc <= 0) {
1724255570Strasz			_close(s);
1725255570Strasz			return NULL;
1726255570Strasz		}
1727255570Strasz		if (cc < sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr))
1728255570Strasz			continue;
1729255570Strasz		if (!IN6_ARE_ADDR_EQUAL(addr, &sin6.sin6_addr))
1730255570Strasz			continue;
1731255570Strasz		fr = (struct icmp6_fqdn_reply *)(buf + sizeof(struct ip6_hdr));
1732255570Strasz		if (fr->icmp6_fqdn_type == ICMP6_FQDN_REPLY)
1733255570Strasz			break;
1734255570Strasz	}
1735255570Strasz	_close(s);
1736255570Strasz	if (fr->icmp6_fqdn_cookie[1] != 0) {
1737255570Strasz		/* rfc1788 type */
1738255570Strasz		name = buf + sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr) + 4;
1739255570Strasz		len = (buf + cc) - name;
1740255570Strasz	} else {
1741255570Strasz		len = fr->icmp6_fqdn_namelen;
1742255570Strasz		name = fr->icmp6_fqdn_name;
1743255570Strasz	}
1744255570Strasz	if (len <= 0)
1745255570Strasz		return NULL;
1746255570Strasz	name[len] = 0;
1747255570Strasz
1748255570Strasz	if ((hc = (struct _icmp_host_cache *)malloc(sizeof(*hc))) == NULL)
1749255570Strasz		return NULL;
1750255570Strasz	/* XXX: limit number of cached entries */
1751255570Strasz	hc->hc_ifindex = ifindex;
1752255570Strasz	hc->hc_addr = *addr;
1753255570Strasz	hc->hc_name = strdup(name);
1754255570Strasz	hc->hc_next = hc_head;
1755255570Strasz	hc_head = hc;
1756255570Strasz	return hc->hc_name;
1757255570Strasz}
1758255570Strasz
1759255570Straszstatic struct hostent *
1760255570Strasz_icmp_ghbyaddr(const void *addr, int addrlen, int af, int *errp)
1761255570Strasz{
1762255570Strasz	char *hname;
1763255570Strasz	int ifindex;
1764255570Strasz	struct in6_addr addr6;
1765255570Strasz
1766255570Strasz	if (af != AF_INET6) {
1767255570Strasz		/*
1768255570Strasz		 * Note: rfc1788 defines Who Are You for IPv4,
1769255570Strasz		 * but no one implements it.
1770255570Strasz		 */
1771255570Strasz		return NULL;
1772255570Strasz	}
1773255570Strasz
1774255570Strasz	memcpy(&addr6, addr, addrlen);
1775255570Strasz	ifindex = (addr6.s6_addr[2] << 8) | addr6.s6_addr[3];
1776255570Strasz	addr6.s6_addr[2] = addr6.s6_addr[3] = 0;
1777255570Strasz
1778255570Strasz	if (!IN6_IS_ADDR_LINKLOCAL(&addr6))
1779255570Strasz		return NULL;	/*XXX*/
1780255570Strasz
1781255570Strasz	if ((hname = _icmp_fqdn_query(&addr6, ifindex)) == NULL)
1782255570Strasz		return NULL;
1783255570Strasz	return _hpaddr(af, hname, &addr6, errp);
1784255570Strasz}
1785255570Strasz#endif /* ICMPNL */
1786255570Strasz