getaddrinfo.c revision 92905
173663Sobrien/*	$FreeBSD: head/lib/libc/net/getaddrinfo.c 92905 2002-03-21 22:49:10Z obrien $        */
262836Sitojun/*	$KAME: getaddrinfo.c,v 1.15 2000/07/09 04:37:24 itojun Exp $	*/
362614Sitojun
455163Sshin/*
555163Sshin * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
655163Sshin * All rights reserved.
755163Sshin *
855163Sshin * Redistribution and use in source and binary forms, with or without
955163Sshin * modification, are permitted provided that the following conditions
1055163Sshin * are met:
1155163Sshin * 1. Redistributions of source code must retain the above copyright
1255163Sshin *    notice, this list of conditions and the following disclaimer.
1355163Sshin * 2. Redistributions in binary form must reproduce the above copyright
1455163Sshin *    notice, this list of conditions and the following disclaimer in the
1555163Sshin *    documentation and/or other materials provided with the distribution.
1655163Sshin * 3. Neither the name of the project nor the names of its contributors
1755163Sshin *    may be used to endorse or promote products derived from this software
1855163Sshin *    without specific prior written permission.
1955163Sshin *
2055163Sshin * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
2155163Sshin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2255163Sshin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2355163Sshin * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
2455163Sshin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2555163Sshin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2655163Sshin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2755163Sshin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2855163Sshin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2955163Sshin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3055163Sshin * SUCH DAMAGE.
3155163Sshin */
3255163Sshin
3355163Sshin/*
3455163Sshin * "#ifdef FAITH" part is local hack for supporting IPv4-v6 translator.
3555163Sshin *
3655163Sshin * Issues to be discussed:
3755163Sshin * - Thread safe-ness must be checked.
3855163Sshin * - Return values.  There are nonstandard return values defined and used
3955163Sshin *   in the source code.  This is because RFC2553 is silent about which error
4055163Sshin *   code must be returned for which situation.
4161877Sume * - freeaddrinfo(NULL).  RFC2553 is silent about it.  XNET 5.2 says it is
4261877Sume *   invalid.
4361877Sume *   current code - SEGV on freeaddrinfo(NULL)
4456627Sshin * Note:
4556627Sshin * - We use getipnodebyname() just for thread-safeness.  There's no intent
4656627Sshin *   to let it do PF_UNSPEC (actually we never pass PF_UNSPEC to
4756627Sshin *   getipnodebyname().
4856627Sshin * - The code filters out AFs that are not supported by the kernel,
4956627Sshin *   when globbing NULL hostname (to loopback, or wildcard).  Is it the right
5056627Sshin *   thing to do?  What is the relationship with post-RFC2553 AI_ADDRCONFIG
5156627Sshin *   in ai_flags?
5261877Sume * - (post-2553) semantics of AI_ADDRCONFIG itself is too vague.
5361877Sume *   (1) what should we do against numeric hostname (2) what should we do
5461877Sume *   against NULL hostname (3) what is AI_ADDRCONFIG itself.  AF not ready?
5561877Sume *   non-loopback address configured?  global address configured?
5661877Sume * - To avoid search order issue, we have a big amount of code duplicate
5761877Sume *   from gethnamaddr.c and some other places.  The issues that there's no
5861877Sume *   lower layer function to lookup "IPv4 or IPv6" record.  Calling
5961877Sume *   gethostbyname2 from getaddrinfo will end up in wrong search order, as
6061877Sume *   follows:
6161877Sume *	- The code makes use of following calls when asked to resolver with
6261877Sume *	  ai_family  = PF_UNSPEC:
6361877Sume *		getipnodebyname(host, AF_INET6);
6461877Sume *		getipnodebyname(host, AF_INET);
6561877Sume *	  This will result in the following queries if the node is configure to
6661877Sume *	  prefer /etc/hosts than DNS:
6761877Sume *		lookup /etc/hosts for IPv6 address
6861877Sume *		lookup DNS for IPv6 address
6961877Sume *		lookup /etc/hosts for IPv4 address
7061877Sume *		lookup DNS for IPv4 address
7161877Sume *	  which may not meet people's requirement.
7261877Sume *	  The right thing to happen is to have underlying layer which does
7361877Sume *	  PF_UNSPEC lookup (lookup both) and return chain of addrinfos.
7461877Sume *	  This would result in a bit of code duplicate with _dns_ghbyname() and
7561877Sume *	  friends.
7655163Sshin */
7761877Sume/*
7861877Sume * diffs with other KAME platforms:
7961877Sume * - other KAME platforms already nuked FAITH ($GAI), but as FreeBSD
8061877Sume *   4.0-RELEASE supplies it, we still have the code here.
8161877Sume * - AI_ADDRCONFIG support is supplied
8261877Sume * - some of FreeBSD style (#define tabify and others)
8361877Sume * - classful IPv4 numeric (127.1) is allowed.
8461877Sume */
8555163Sshin
8671579Sdeischen#include "namespace.h"
8755163Sshin#include <sys/types.h>
8855163Sshin#include <sys/param.h>
8955163Sshin#include <sys/socket.h>
9055163Sshin#include <net/if.h>
9155163Sshin#include <netinet/in.h>
9255163Sshin#include <arpa/inet.h>
9355163Sshin#include <arpa/nameser.h>
9490271Salfred#include <rpc/rpc.h>
9590271Salfred#include <rpcsvc/yp_prot.h>
9690271Salfred#include <rpcsvc/ypclnt.h>
9755163Sshin#include <netdb.h>
9855163Sshin#include <resolv.h>
9955163Sshin#include <string.h>
10055163Sshin#include <stdlib.h>
10155163Sshin#include <stddef.h>
10255163Sshin#include <ctype.h>
10355163Sshin#include <unistd.h>
10455163Sshin#include <stdio.h>
10561877Sume#include <errno.h>
10678012Sume#ifdef DEBUG
10778012Sume#include <syslog.h>
10878012Sume#endif
10955163Sshin
11065532Snectar#include <syslog.h>
11165532Snectar#include <stdarg.h>
11265532Snectar#include <nsswitch.h>
11371579Sdeischen#include "un-namespace.h"
11465532Snectar
11555163Sshin#if defined(__KAME__) && defined(INET6)
11655163Sshin# define FAITH
11755163Sshin#endif
11855163Sshin
11973665Sobrien#define	SUCCESS 0
12073665Sobrien#define	ANY 0
12173665Sobrien#define	YES 1
12273665Sobrien#define	NO  0
12355163Sshin
12455163Sshinstatic const char in_addrany[] = { 0, 0, 0, 0 };
12555163Sshinstatic const char in6_addrany[] = {
12655163Sshin	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
12755163Sshin};
12855163Sshinstatic const char in_loopback[] = { 127, 0, 0, 1 };
12955163Sshinstatic const char in6_loopback[] = {
13055163Sshin	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1
13155163Sshin};
13255163Sshin
13355163Sshinstatic const struct afd {
13455163Sshin	int a_af;
13555163Sshin	int a_addrlen;
13655163Sshin	int a_socklen;
13755163Sshin	int a_off;
13855163Sshin	const char *a_addrany;
13973665Sobrien	const char *a_loopback;
14055163Sshin	int a_scoped;
14155163Sshin} afdl [] = {
14255163Sshin#ifdef INET6
14355163Sshin#define	N_INET6 0
14455163Sshin	{PF_INET6, sizeof(struct in6_addr),
14555163Sshin	 sizeof(struct sockaddr_in6),
14655163Sshin	 offsetof(struct sockaddr_in6, sin6_addr),
14755163Sshin	 in6_addrany, in6_loopback, 1},
14855163Sshin#define	N_INET 1
14955163Sshin#else
15055163Sshin#define	N_INET 0
15155163Sshin#endif
15255163Sshin	{PF_INET, sizeof(struct in_addr),
15355163Sshin	 sizeof(struct sockaddr_in),
15455163Sshin	 offsetof(struct sockaddr_in, sin_addr),
15555163Sshin	 in_addrany, in_loopback, 0},
15655163Sshin	{0, 0, 0, 0, NULL, NULL, 0},
15755163Sshin};
15855163Sshin
15955163Sshinstruct explore {
16061877Sume	int e_af;
16155163Sshin	int e_socktype;
16255163Sshin	int e_protocol;
16355163Sshin	const char *e_protostr;
16455163Sshin	int e_wild;
16573665Sobrien#define	WILD_AF(ex)		((ex)->e_wild & 0x01)
16673665Sobrien#define	WILD_SOCKTYPE(ex)	((ex)->e_wild & 0x02)
16773665Sobrien#define	WILD_PROTOCOL(ex)	((ex)->e_wild & 0x04)
16855163Sshin};
16955163Sshin
17055163Sshinstatic const struct explore explore[] = {
17161877Sume#if 0
17261877Sume	{ PF_LOCAL, 0, ANY, ANY, NULL, 0x01 },
17361877Sume#endif
17461877Sume#ifdef INET6
17561877Sume	{ PF_INET6, SOCK_DGRAM, IPPROTO_UDP, "udp", 0x07 },
17661877Sume	{ PF_INET6, SOCK_STREAM, IPPROTO_TCP, "tcp", 0x07 },
17761877Sume	{ PF_INET6, SOCK_RAW, ANY, NULL, 0x05 },
17861877Sume#endif
17961877Sume	{ PF_INET, SOCK_DGRAM, IPPROTO_UDP, "udp", 0x07 },
18061877Sume	{ PF_INET, SOCK_STREAM, IPPROTO_TCP, "tcp", 0x07 },
18161877Sume	{ PF_INET, SOCK_RAW, ANY, NULL, 0x05 },
18261877Sume	{ PF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, "udp", 0x07 },
18361877Sume	{ PF_UNSPEC, SOCK_STREAM, IPPROTO_TCP, "tcp", 0x07 },
18461877Sume	{ PF_UNSPEC, SOCK_RAW, ANY, NULL, 0x05 },
18561877Sume	{ -1, 0, 0, NULL, 0 },
18655163Sshin};
18755163Sshin
18855163Sshin#ifdef INET6
18973665Sobrien#define	PTON_MAX	16
19055163Sshin#else
19173665Sobrien#define	PTON_MAX	4
19255163Sshin#endif
19355163Sshin
19465532Snectarstatic const ns_src default_dns_files[] = {
19565532Snectar	{ NSSRC_FILES, 	NS_SUCCESS },
19665532Snectar	{ NSSRC_DNS, 	NS_SUCCESS },
19765532Snectar	{ 0 }
19865532Snectar};
19965532Snectar
20061877Sume#if PACKETSZ > 1024
20161877Sume#define MAXPACKET	PACKETSZ
20261877Sume#else
20361877Sume#define MAXPACKET	1024
20461877Sume#endif
20561877Sume
20661877Sumetypedef union {
20761877Sume	HEADER hdr;
20861877Sume	u_char buf[MAXPACKET];
20961877Sume} querybuf;
21061877Sume
21161877Sumestruct res_target {
21261877Sume	struct res_target *next;
21361877Sume	const char *name;	/* domain name */
21462614Sitojun	int qclass, qtype;	/* class and type of query */
21561877Sume	u_char *answer;		/* buffer to put answer */
21661877Sume	int anslen;		/* size of answer buffer */
21761877Sume	int n;			/* result length */
21861877Sume};
21961877Sume
22092905Sobrienstatic int str_isnumber(const char *);
22155163Sshinstatic int explore_fqdn __P((const struct addrinfo *, const char *,
22255163Sshin	const char *, struct addrinfo **));
22361877Sumestatic int explore_null __P((const struct addrinfo *,
22455163Sshin	const char *, struct addrinfo **));
22555163Sshinstatic int explore_numeric __P((const struct addrinfo *, const char *,
22655163Sshin	const char *, struct addrinfo **));
22755163Sshinstatic int explore_numeric_scope __P((const struct addrinfo *, const char *,
22855163Sshin	const char *, struct addrinfo **));
22955163Sshinstatic int get_canonname __P((const struct addrinfo *,
23055163Sshin	struct addrinfo *, const char *));
23155163Sshinstatic struct addrinfo *get_ai __P((const struct addrinfo *,
23255163Sshin	const struct afd *, const char *));
23392905Sobrienstatic int get_portmatch(const struct addrinfo *, const char *);
23492905Sobrienstatic int get_port(struct addrinfo *, const char *, int);
23592905Sobrienstatic const struct afd *find_afd(int);
23692905Sobrienstatic int addrconfig(struct addrinfo *);
23761877Sume#ifdef INET6
23892905Sobrienstatic int ip6_str2scopeid(char *, struct sockaddr_in6 *);
23961877Sume#endif
24055163Sshin
24165532Snectarstatic struct addrinfo *getanswer __P((const querybuf *, int, const char *, int,
24265532Snectar	const struct addrinfo *));
24392905Sobrienstatic int _dns_getaddrinfo(void *, void *, va_list);
24492905Sobrienstatic void _sethtent(void);
24592905Sobrienstatic void _endhtent(void);
24692905Sobrienstatic struct addrinfo *_gethtent(const char *, const struct addrinfo *);
24792905Sobrienstatic int _files_getaddrinfo(void *, void *, va_list);
24861877Sume#ifdef YP
24992905Sobrienstatic struct addrinfo *_yphostent(char *, const struct addrinfo *);
25092905Sobrienstatic int _yp_getaddrinfo(void *, void *, va_list);
25192905Sobrienextern int _yp_check(char **);
25261877Sume#endif
25361877Sume
25492905Sobrienstatic int res_queryN(const char *, struct res_target *);
25592905Sobrienstatic int res_searchN(const char *, struct res_target *);
25661877Sumestatic int res_querydomainN __P((const char *, const char *,
25765532Snectar	struct res_target *));
25861877Sume
25955163Sshinstatic char *ai_errlist[] = {
26055163Sshin	"Success",
26155163Sshin	"Address family for hostname not supported",	/* EAI_ADDRFAMILY */
26255163Sshin	"Temporary failure in name resolution",		/* EAI_AGAIN      */
26355163Sshin	"Invalid value for ai_flags",		       	/* EAI_BADFLAGS   */
26455163Sshin	"Non-recoverable failure in name resolution", 	/* EAI_FAIL       */
26555163Sshin	"ai_family not supported",			/* EAI_FAMILY     */
26655163Sshin	"Memory allocation failure", 			/* EAI_MEMORY     */
26755163Sshin	"No address associated with hostname", 		/* EAI_NODATA     */
26855163Sshin	"hostname nor servname provided, or not known",	/* EAI_NONAME     */
26955163Sshin	"servname not supported for ai_socktype",	/* EAI_SERVICE    */
27055163Sshin	"ai_socktype not supported", 			/* EAI_SOCKTYPE   */
27155163Sshin	"System error returned in errno", 		/* EAI_SYSTEM     */
27255163Sshin	"Invalid value for hints",			/* EAI_BADHINTS	  */
27355163Sshin	"Resolved protocol is unknown",			/* EAI_PROTOCOL   */
27455163Sshin	"Unknown error", 				/* EAI_MAX        */
27555163Sshin};
27655163Sshin
27755163Sshin/* XXX macros that make external reference is BAD. */
27855163Sshin
27973665Sobrien#define	GET_AI(ai, afd, addr) \
28055163Sshindo { \
28155163Sshin	/* external reference: pai, error, and label free */ \
28255163Sshin	(ai) = get_ai(pai, (afd), (addr)); \
28355163Sshin	if ((ai) == NULL) { \
28455163Sshin		error = EAI_MEMORY; \
28555163Sshin		goto free; \
28655163Sshin	} \
28761877Sume} while (/*CONSTCOND*/0)
28855163Sshin
28973665Sobrien#define	GET_PORT(ai, serv) \
29055163Sshindo { \
29155163Sshin	/* external reference: error and label free */ \
29255163Sshin	error = get_port((ai), (serv), 0); \
29355163Sshin	if (error != 0) \
29455163Sshin		goto free; \
29561877Sume} while (/*CONSTCOND*/0)
29655163Sshin
29773665Sobrien#define	GET_CANONNAME(ai, str) \
29855163Sshindo { \
29955163Sshin	/* external reference: pai, error and label free */ \
30055163Sshin	error = get_canonname(pai, (ai), (str)); \
30155163Sshin	if (error != 0) \
30255163Sshin		goto free; \
30361877Sume} while (/*CONSTCOND*/0)
30455163Sshin
30573665Sobrien#define	ERR(err) \
30655163Sshindo { \
30755163Sshin	/* external reference: error, and label bad */ \
30855163Sshin	error = (err); \
30955163Sshin	goto bad; \
31061877Sume	/*NOTREACHED*/ \
31161877Sume} while (/*CONSTCOND*/0)
31255163Sshin
31373665Sobrien#define	MATCH_FAMILY(x, y, w) \
31461877Sume	((x) == (y) || (/*CONSTCOND*/(w) && ((x) == PF_UNSPEC || (y) == PF_UNSPEC)))
31573665Sobrien#define	MATCH(x, y, w) \
31661877Sume	((x) == (y) || (/*CONSTCOND*/(w) && ((x) == ANY || (y) == ANY)))
31755163Sshin
31855163Sshinchar *
31955163Sshingai_strerror(ecode)
32055163Sshin	int ecode;
32155163Sshin{
32255163Sshin	if (ecode < 0 || ecode > EAI_MAX)
32355163Sshin		ecode = EAI_MAX;
32455163Sshin	return ai_errlist[ecode];
32555163Sshin}
32655163Sshin
32755163Sshinvoid
32855163Sshinfreeaddrinfo(ai)
32955163Sshin	struct addrinfo *ai;
33055163Sshin{
33155163Sshin	struct addrinfo *next;
33255163Sshin
33355163Sshin	do {
33455163Sshin		next = ai->ai_next;
33555163Sshin		if (ai->ai_canonname)
33655163Sshin			free(ai->ai_canonname);
33755163Sshin		/* no need to free(ai->ai_addr) */
33855163Sshin		free(ai);
33961877Sume		ai = next;
34061877Sume	} while (ai);
34155163Sshin}
34255163Sshin
34355163Sshinstatic int
34455163Sshinstr_isnumber(p)
34555163Sshin	const char *p;
34655163Sshin{
34762836Sitojun	char *ep;
34862836Sitojun
34962836Sitojun	if (*p == '\0')
35062836Sitojun		return NO;
35162836Sitojun	ep = NULL;
35262836Sitojun	(void)strtoul(p, &ep, 10);
35362836Sitojun	if (ep && *ep == '\0')
35462836Sitojun		return YES;
35562836Sitojun	else
35662836Sitojun		return NO;
35755163Sshin}
35855163Sshin
35955163Sshinint
36055163Sshingetaddrinfo(hostname, servname, hints, res)
36155163Sshin	const char *hostname, *servname;
36255163Sshin	const struct addrinfo *hints;
36355163Sshin	struct addrinfo **res;
36455163Sshin{
36555163Sshin	struct addrinfo sentinel;
36655163Sshin	struct addrinfo *cur;
36755163Sshin	int error = 0;
36855163Sshin	struct addrinfo ai;
36955163Sshin	struct addrinfo ai0;
37055163Sshin	struct addrinfo *pai;
37155163Sshin	const struct explore *ex;
37255163Sshin
37361877Sume	memset(&sentinel, 0, sizeof(sentinel));
37455163Sshin	cur = &sentinel;
37555163Sshin	pai = &ai;
37655163Sshin	pai->ai_flags = 0;
37755163Sshin	pai->ai_family = PF_UNSPEC;
37855163Sshin	pai->ai_socktype = ANY;
37955163Sshin	pai->ai_protocol = ANY;
38055163Sshin	pai->ai_addrlen = 0;
38155163Sshin	pai->ai_canonname = NULL;
38255163Sshin	pai->ai_addr = NULL;
38355163Sshin	pai->ai_next = NULL;
38455163Sshin
38555163Sshin	if (hostname == NULL && servname == NULL)
38655163Sshin		return EAI_NONAME;
38755163Sshin	if (hints) {
38855163Sshin		/* error check for hints */
38955163Sshin		if (hints->ai_addrlen || hints->ai_canonname ||
39055163Sshin		    hints->ai_addr || hints->ai_next)
39155163Sshin			ERR(EAI_BADHINTS); /* xxx */
39255163Sshin		if (hints->ai_flags & ~AI_MASK)
39355163Sshin			ERR(EAI_BADFLAGS);
39455163Sshin		switch (hints->ai_family) {
39555163Sshin		case PF_UNSPEC:
39655163Sshin		case PF_INET:
39755163Sshin#ifdef INET6
39855163Sshin		case PF_INET6:
39955163Sshin#endif
40055163Sshin			break;
40155163Sshin		default:
40255163Sshin			ERR(EAI_FAMILY);
40355163Sshin		}
40455163Sshin		memcpy(pai, hints, sizeof(*pai));
40555163Sshin
40655163Sshin		/*
40755163Sshin		 * if both socktype/protocol are specified, check if they
40855163Sshin		 * are meaningful combination.
40955163Sshin		 */
41055163Sshin		if (pai->ai_socktype != ANY && pai->ai_protocol != ANY) {
41161877Sume			for (ex = explore; ex->e_af >= 0; ex++) {
41261877Sume				if (pai->ai_family != ex->e_af)
41361877Sume					continue;
41455163Sshin				if (ex->e_socktype == ANY)
41555163Sshin					continue;
41655163Sshin				if (ex->e_protocol == ANY)
41755163Sshin					continue;
41855163Sshin				if (pai->ai_socktype == ex->e_socktype
41961877Sume				 && pai->ai_protocol != ex->e_protocol) {
42055163Sshin					ERR(EAI_BADHINTS);
42161877Sume				}
42255163Sshin			}
42355163Sshin		}
42455163Sshin	}
42555163Sshin
42661877Sume	/*
42761877Sume	 * post-2553: AI_ALL and AI_V4MAPPED are effective only against
42861877Sume	 * AF_INET6 query.  They needs to be ignored if specified in other
42961877Sume	 * occassions.
43061877Sume	 */
43161877Sume	switch (pai->ai_flags & (AI_ALL | AI_V4MAPPED)) {
43261877Sume	case AI_V4MAPPED:
43361877Sume	case AI_ALL | AI_V4MAPPED:
43461877Sume		if (pai->ai_family != AF_INET6)
43561877Sume			pai->ai_flags &= ~(AI_ALL | AI_V4MAPPED);
43661877Sume		break;
43761877Sume	case AI_ALL:
43861877Sume#if 1
43961877Sume		/* illegal */
44061877Sume		ERR(EAI_BADFLAGS);
44161877Sume#else
44261877Sume		pai->ai_flags &= ~(AI_ALL | AI_V4MAPPED);
44361877Sume#endif
44461877Sume		break;
44561877Sume	}
44655163Sshin
44755163Sshin	/*
44861877Sume	 * check for special cases.  (1) numeric servname is disallowed if
44961877Sume	 * socktype/protocol are left unspecified. (2) servname is disallowed
45061877Sume	 * for raw and other inet{,6} sockets.
45155163Sshin	 */
45255163Sshin	if (MATCH_FAMILY(pai->ai_family, PF_INET, 1)
45361877Sume#ifdef PF_INET6
45473665Sobrien	    || MATCH_FAMILY(pai->ai_family, PF_INET6, 1)
45555163Sshin#endif
45655163Sshin	    ) {
45761877Sume		ai0 = *pai;	/* backup *pai */
45855163Sshin
45961877Sume		if (pai->ai_family == PF_UNSPEC) {
46061877Sume#ifdef PF_INET6
46155163Sshin			pai->ai_family = PF_INET6;
46255163Sshin#else
46355163Sshin			pai->ai_family = PF_INET;
46455163Sshin#endif
46561877Sume		}
46655163Sshin		error = get_portmatch(pai, servname);
46755163Sshin		if (error)
46855163Sshin			ERR(error);
46961877Sume
47061877Sume		*pai = ai0;
47155163Sshin	}
47255163Sshin
47361877Sume	ai0 = *pai;
47461877Sume
47555163Sshin	/* NULL hostname, or numeric hostname */
47661877Sume	for (ex = explore; ex->e_af >= 0; ex++) {
47755163Sshin		*pai = ai0;
47855163Sshin
47961877Sume		/* PF_UNSPEC entries are prepared for DNS queries only */
48061877Sume		if (ex->e_af == PF_UNSPEC)
48155163Sshin			continue;
48261877Sume
48361877Sume		if (!MATCH_FAMILY(pai->ai_family, ex->e_af, WILD_AF(ex)))
48455163Sshin			continue;
48565532Snectar		if (!MATCH(pai->ai_socktype, ex->e_socktype, WILD_SOCKTYPE(ex)))
48655163Sshin			continue;
48765532Snectar		if (!MATCH(pai->ai_protocol, ex->e_protocol, WILD_PROTOCOL(ex)))
48861877Sume			continue;
48955163Sshin
49055163Sshin		if (pai->ai_family == PF_UNSPEC)
49161877Sume			pai->ai_family = ex->e_af;
49255163Sshin		if (pai->ai_socktype == ANY && ex->e_socktype != ANY)
49355163Sshin			pai->ai_socktype = ex->e_socktype;
49455163Sshin		if (pai->ai_protocol == ANY && ex->e_protocol != ANY)
49555163Sshin			pai->ai_protocol = ex->e_protocol;
49655163Sshin
49755163Sshin		if (hostname == NULL)
49861877Sume			error = explore_null(pai, servname, &cur->ai_next);
49955163Sshin		else
50065532Snectar			error = explore_numeric_scope(pai, hostname, servname, &cur->ai_next);
50155163Sshin
50255163Sshin		if (error)
50355163Sshin			goto free;
50455163Sshin
50555163Sshin		while (cur && cur->ai_next)
50655163Sshin			cur = cur->ai_next;
50755163Sshin	}
50855163Sshin
50955163Sshin	/*
51055163Sshin	 * XXX
51155163Sshin	 * If numreic representation of AF1 can be interpreted as FQDN
51255163Sshin	 * representation of AF2, we need to think again about the code below.
51355163Sshin	 */
51455163Sshin	if (sentinel.ai_next)
51555163Sshin		goto good;
51655163Sshin
51755163Sshin	if (pai->ai_flags & AI_NUMERICHOST)
51890053Sroam		ERR(EAI_NONAME);
51955163Sshin	if (hostname == NULL)
52062614Sitojun		ERR(EAI_NODATA);
52155163Sshin
52261877Sume	if ((pai->ai_flags & AI_ADDRCONFIG) != 0 && !addrconfig(&ai0))
52361877Sume		ERR(EAI_FAIL);
52455163Sshin
52561877Sume	/*
52661877Sume	 * hostname as alphabetical name.
52761877Sume	 * we would like to prefer AF_INET6 than AF_INET, so we'll make a
52861877Sume	 * outer loop by AFs.
52961877Sume	 */
53061877Sume	for (ex = explore; ex->e_af >= 0; ex++) {
53161877Sume		*pai = ai0;
53255163Sshin
53361877Sume		/* require exact match for family field */
53461877Sume		if (pai->ai_family != ex->e_af)
53561877Sume			continue;
53655163Sshin
53761877Sume		if (!MATCH(pai->ai_socktype, ex->e_socktype,
53865532Snectar				WILD_SOCKTYPE(ex))) {
53961877Sume			continue;
54061877Sume		}
54161877Sume		if (!MATCH(pai->ai_protocol, ex->e_protocol,
54265532Snectar				WILD_PROTOCOL(ex))) {
54361877Sume			continue;
54461877Sume		}
54555163Sshin
54661877Sume		if (pai->ai_socktype == ANY && ex->e_socktype != ANY)
54761877Sume			pai->ai_socktype = ex->e_socktype;
54861877Sume		if (pai->ai_protocol == ANY && ex->e_protocol != ANY)
54961877Sume			pai->ai_protocol = ex->e_protocol;
55061877Sume
55165532Snectar		error = explore_fqdn(pai, hostname, servname,
55265532Snectar			&cur->ai_next);
55361877Sume
55461877Sume		while (cur && cur->ai_next)
55561877Sume			cur = cur->ai_next;
55655163Sshin	}
55755163Sshin
55861877Sume	/* XXX */
55961877Sume	if (sentinel.ai_next)
56061877Sume		error = 0;
56161877Sume
56261877Sume	if (error)
56361877Sume		goto free;
56461877Sume	if (error == 0) {
56561877Sume		if (sentinel.ai_next) {
56655163Sshin good:
56761877Sume			*res = sentinel.ai_next;
56861877Sume			return SUCCESS;
56961877Sume		} else
57061877Sume			error = EAI_FAIL;
57155163Sshin	}
57255163Sshin free:
57355163Sshin bad:
57455163Sshin	if (sentinel.ai_next)
57555163Sshin		freeaddrinfo(sentinel.ai_next);
57655163Sshin	*res = NULL;
57755163Sshin	return error;
57855163Sshin}
57955163Sshin
58055163Sshin/*
58155163Sshin * FQDN hostname, DNS lookup
58255163Sshin */
58355163Sshinstatic int
58455163Sshinexplore_fqdn(pai, hostname, servname, res)
58555163Sshin	const struct addrinfo *pai;
58655163Sshin	const char *hostname;
58755163Sshin	const char *servname;
58855163Sshin	struct addrinfo **res;
58955163Sshin{
59061877Sume	struct addrinfo *result;
59161877Sume	struct addrinfo *cur;
59265532Snectar	int error = 0;
59365532Snectar	static const ns_dtab dtab[] = {
59465532Snectar		NS_FILES_CB(_files_getaddrinfo, NULL)
59565532Snectar		{ NSSRC_DNS, _dns_getaddrinfo, NULL },	/* force -DHESIOD */
59665532Snectar		NS_NIS_CB(_yp_getaddrinfo, NULL)
59765532Snectar		{ 0 }
59865532Snectar	};
59955163Sshin
60061877Sume	result = NULL;
60155163Sshin
60255163Sshin	/*
60355163Sshin	 * if the servname does not match socktype/protocol, ignore it.
60455163Sshin	 */
60555163Sshin	if (get_portmatch(pai, servname) != 0)
60655163Sshin		return 0;
60755163Sshin
60865532Snectar	switch (nsdispatch(&result, dtab, NSDB_HOSTS, "getaddrinfo",
60965532Snectar			default_dns_files, hostname, pai)) {
61065532Snectar	case NS_TRYAGAIN:
61165532Snectar		error = EAI_AGAIN;
61265532Snectar		goto free;
61365532Snectar	case NS_UNAVAIL:
61465532Snectar		error = EAI_FAIL;
61565532Snectar		goto free;
61665532Snectar	case NS_NOTFOUND:
61765532Snectar		error = EAI_NODATA;
61865532Snectar		goto free;
61965532Snectar	case NS_SUCCESS:
62065532Snectar		error = 0;
62161877Sume		for (cur = result; cur; cur = cur->ai_next) {
62261877Sume			GET_PORT(cur, servname);
62361877Sume			/* canonname should be filled already */
62455163Sshin		}
62565532Snectar		break;
62655163Sshin	}
62755163Sshin
62865532Snectar	*res = result;
62965532Snectar
63065532Snectar	return 0;
63165532Snectar
63255163Sshinfree:
63361877Sume	if (result)
63461877Sume		freeaddrinfo(result);
63555163Sshin	return error;
63655163Sshin}
63755163Sshin
63855163Sshin/*
63955163Sshin * hostname == NULL.
64055163Sshin * passive socket -> anyaddr (0.0.0.0 or ::)
64155163Sshin * non-passive socket -> localhost (127.0.0.1 or ::1)
64255163Sshin */
64355163Sshinstatic int
64461877Sumeexplore_null(pai, servname, res)
64555163Sshin	const struct addrinfo *pai;
64655163Sshin	const char *servname;
64755163Sshin	struct addrinfo **res;
64855163Sshin{
64955163Sshin	int s;
65055163Sshin	const struct afd *afd;
65155163Sshin	struct addrinfo *cur;
65255163Sshin	struct addrinfo sentinel;
65355163Sshin	int error;
65455163Sshin
65555163Sshin	*res = NULL;
65655163Sshin	sentinel.ai_next = NULL;
65755163Sshin	cur = &sentinel;
65855163Sshin
65955163Sshin	/*
66055163Sshin	 * filter out AFs that are not supported by the kernel
66155163Sshin	 * XXX errno?
66255163Sshin	 */
66371579Sdeischen	s = _socket(pai->ai_family, SOCK_DGRAM, 0);
66461877Sume	if (s < 0) {
66561877Sume		if (errno != EMFILE)
66661877Sume			return 0;
66761877Sume	} else
66861877Sume		_close(s);
66961877Sume
67061877Sume	/*
67161877Sume	 * if the servname does not match socktype/protocol, ignore it.
67261877Sume	 */
67361877Sume	if (get_portmatch(pai, servname) != 0)
67455163Sshin		return 0;
67561877Sume
67655163Sshin	afd = find_afd(pai->ai_family);
67755163Sshin	if (afd == NULL)
67855163Sshin		return 0;
67955163Sshin
68061877Sume	if (pai->ai_flags & AI_PASSIVE) {
68161877Sume		GET_AI(cur->ai_next, afd, afd->a_addrany);
68261877Sume		/* xxx meaningless?
68361877Sume		 * GET_CANONNAME(cur->ai_next, "anyaddr");
68461877Sume		 */
68561877Sume		GET_PORT(cur->ai_next, servname);
68661877Sume	} else {
68761877Sume		GET_AI(cur->ai_next, afd, afd->a_loopback);
68861877Sume		/* xxx meaningless?
68961877Sume		 * GET_CANONNAME(cur->ai_next, "localhost");
69061877Sume		 */
69161877Sume		GET_PORT(cur->ai_next, servname);
69261877Sume	}
69361877Sume	cur = cur->ai_next;
69455163Sshin
69555163Sshin	*res = sentinel.ai_next;
69655163Sshin	return 0;
69755163Sshin
69855163Sshinfree:
69955163Sshin	if (sentinel.ai_next)
70055163Sshin		freeaddrinfo(sentinel.ai_next);
70155163Sshin	return error;
70255163Sshin}
70355163Sshin
70455163Sshin/*
70555163Sshin * numeric hostname
70655163Sshin */
70755163Sshinstatic int
70855163Sshinexplore_numeric(pai, hostname, servname, res)
70955163Sshin	const struct addrinfo *pai;
71055163Sshin	const char *hostname;
71155163Sshin	const char *servname;
71255163Sshin	struct addrinfo **res;
71355163Sshin{
71455163Sshin	const struct afd *afd;
71555163Sshin	struct addrinfo *cur;
71655163Sshin	struct addrinfo sentinel;
71755163Sshin	int error;
71855163Sshin	char pton[PTON_MAX];
71955163Sshin
72055163Sshin	*res = NULL;
72155163Sshin	sentinel.ai_next = NULL;
72255163Sshin	cur = &sentinel;
72355163Sshin
72455163Sshin	/*
72555163Sshin	 * if the servname does not match socktype/protocol, ignore it.
72655163Sshin	 */
72755163Sshin	if (get_portmatch(pai, servname) != 0)
72855163Sshin		return 0;
72955163Sshin
73055163Sshin	afd = find_afd(pai->ai_family);
73155163Sshin	if (afd == NULL)
73255163Sshin		return 0;
73355163Sshin
73462614Sitojun	switch (afd->a_af) {
73562614Sitojun#if 1 /*X/Open spec*/
73662614Sitojun	case AF_INET:
73762614Sitojun		if (inet_aton(hostname, (struct in_addr *)pton) == 1) {
73862614Sitojun			if (pai->ai_family == afd->a_af ||
73962614Sitojun			    pai->ai_family == PF_UNSPEC /*?*/) {
74062614Sitojun				GET_AI(cur->ai_next, afd, pton);
74162614Sitojun				GET_PORT(cur->ai_next, servname);
74262614Sitojun				while (cur && cur->ai_next)
74362614Sitojun					cur = cur->ai_next;
74462614Sitojun			} else
74562614Sitojun				ERR(EAI_FAMILY);	/*xxx*/
74662614Sitojun		}
74762614Sitojun		break;
74862614Sitojun#endif
74962614Sitojun	default:
75062614Sitojun		if (inet_pton(afd->a_af, hostname, pton) == 1) {
75162614Sitojun			if (pai->ai_family == afd->a_af ||
75262614Sitojun			    pai->ai_family == PF_UNSPEC /*?*/) {
75362614Sitojun				GET_AI(cur->ai_next, afd, pton);
75462614Sitojun				GET_PORT(cur->ai_next, servname);
75562614Sitojun				while (cur && cur->ai_next)
75662614Sitojun					cur = cur->ai_next;
75762614Sitojun			} else
75862614Sitojun				ERR(EAI_FAMILY);	/*xxx*/
75962614Sitojun		}
76062614Sitojun		break;
76155163Sshin	}
76255163Sshin
76355163Sshin	*res = sentinel.ai_next;
76455163Sshin	return 0;
76555163Sshin
76655163Sshinfree:
76755163Sshinbad:
76855163Sshin	if (sentinel.ai_next)
76955163Sshin		freeaddrinfo(sentinel.ai_next);
77055163Sshin	return error;
77155163Sshin}
77255163Sshin
77355163Sshin/*
77455163Sshin * numeric hostname with scope
77555163Sshin */
77655163Sshinstatic int
77755163Sshinexplore_numeric_scope(pai, hostname, servname, res)
77855163Sshin	const struct addrinfo *pai;
77955163Sshin	const char *hostname;
78055163Sshin	const char *servname;
78155163Sshin	struct addrinfo **res;
78255163Sshin{
78361877Sume#if !defined(SCOPE_DELIMITER) || !defined(INET6)
78455163Sshin	return explore_numeric(pai, hostname, servname, res);
78555163Sshin#else
78655163Sshin	const struct afd *afd;
78755163Sshin	struct addrinfo *cur;
78855163Sshin	int error;
78961877Sume	char *cp, *hostname2 = NULL, *scope, *addr;
79055163Sshin	struct sockaddr_in6 *sin6;
79155163Sshin
79255163Sshin	/*
79355163Sshin	 * if the servname does not match socktype/protocol, ignore it.
79455163Sshin	 */
79555163Sshin	if (get_portmatch(pai, servname) != 0)
79655163Sshin		return 0;
79755163Sshin
79855163Sshin	afd = find_afd(pai->ai_family);
79955163Sshin	if (afd == NULL)
80055163Sshin		return 0;
80165532Snectar
80255163Sshin	if (!afd->a_scoped)
80355163Sshin		return explore_numeric(pai, hostname, servname, res);
80455163Sshin
80555163Sshin	cp = strchr(hostname, SCOPE_DELIMITER);
80655163Sshin	if (cp == NULL)
80755163Sshin		return explore_numeric(pai, hostname, servname, res);
80855163Sshin
80955163Sshin	/*
81055163Sshin	 * Handle special case of <scoped_address><delimiter><scope id>
81155163Sshin	 */
81255163Sshin	hostname2 = strdup(hostname);
81355163Sshin	if (hostname2 == NULL)
81455163Sshin		return EAI_MEMORY;
81555163Sshin	/* terminate at the delimiter */
81655163Sshin	hostname2[cp - hostname] = '\0';
81761877Sume	addr = hostname2;
81861877Sume	scope = cp + 1;
81955163Sshin
82061877Sume	error = explore_numeric(pai, addr, servname, res);
82161877Sume	if (error == 0) {
82261877Sume		int scopeid;
82355163Sshin
82455163Sshin		for (cur = *res; cur; cur = cur->ai_next) {
82555163Sshin			if (cur->ai_family != AF_INET6)
82655163Sshin				continue;
82761877Sume			sin6 = (struct sockaddr_in6 *)(void *)cur->ai_addr;
82861877Sume			if ((scopeid = ip6_str2scopeid(scope, sin6)) == -1) {
82961877Sume				free(hostname2);
83062614Sitojun				return(EAI_NODATA); /* XXX: is return OK? */
83161877Sume			}
83261877Sume			sin6->sin6_scope_id = scopeid;
83355163Sshin		}
83455163Sshin	}
83555163Sshin
83655163Sshin	free(hostname2);
83755163Sshin
83855163Sshin	return error;
83955163Sshin#endif
84055163Sshin}
84155163Sshin
84255163Sshinstatic int
84355163Sshinget_canonname(pai, ai, str)
84455163Sshin	const struct addrinfo *pai;
84555163Sshin	struct addrinfo *ai;
84655163Sshin	const char *str;
84755163Sshin{
84855163Sshin	if ((pai->ai_flags & AI_CANONNAME) != 0) {
84955163Sshin		ai->ai_canonname = (char *)malloc(strlen(str) + 1);
85055163Sshin		if (ai->ai_canonname == NULL)
85155163Sshin			return EAI_MEMORY;
85255163Sshin		strcpy(ai->ai_canonname, str);
85355163Sshin	}
85455163Sshin	return 0;
85555163Sshin}
85655163Sshin
85755163Sshinstatic struct addrinfo *
85855163Sshinget_ai(pai, afd, addr)
85955163Sshin	const struct addrinfo *pai;
86055163Sshin	const struct afd *afd;
86155163Sshin	const char *addr;
86255163Sshin{
86355163Sshin	char *p;
86455163Sshin	struct addrinfo *ai;
86555163Sshin#ifdef FAITH
86655163Sshin	struct in6_addr faith_prefix;
86755163Sshin	char *fp_str;
86855163Sshin	int translate = 0;
86955163Sshin#endif
87055163Sshin
87155163Sshin#ifdef FAITH
87255163Sshin	/*
87355163Sshin	 * Transfrom an IPv4 addr into a special IPv6 addr format for
87455163Sshin	 * IPv6->IPv4 translation gateway. (only TCP is supported now)
87555163Sshin	 *
87655163Sshin	 * +-----------------------------------+------------+
87755163Sshin	 * | faith prefix part (12 bytes)      | embedded   |
87855163Sshin	 * |                                   | IPv4 addr part (4 bytes)
87955163Sshin	 * +-----------------------------------+------------+
88055163Sshin	 *
88155163Sshin	 * faith prefix part is specified as ascii IPv6 addr format
88255163Sshin	 * in environmental variable GAI.
88355163Sshin	 * For FAITH to work correctly, routing to faith prefix must be
88455163Sshin	 * setup toward a machine where a FAITH daemon operates.
88555163Sshin	 * Also, the machine must enable some mechanizm
88655163Sshin	 * (e.g. faith interface hack) to divert those packet with
88755163Sshin	 * faith prefixed destination addr to user-land FAITH daemon.
88855163Sshin	 */
88955163Sshin	fp_str = getenv("GAI");
89055163Sshin	if (fp_str && inet_pton(AF_INET6, fp_str, &faith_prefix) == 1 &&
89155163Sshin	    afd->a_af == AF_INET && pai->ai_socktype == SOCK_STREAM) {
89255163Sshin		u_int32_t v4a;
89355163Sshin		u_int8_t v4a_top;
89455163Sshin
89555163Sshin		memcpy(&v4a, addr, sizeof v4a);
89655163Sshin		v4a_top = v4a >> IN_CLASSA_NSHIFT;
89755163Sshin		if (!IN_MULTICAST(v4a) && !IN_EXPERIMENTAL(v4a) &&
89855163Sshin		    v4a_top != 0 && v4a != IN_LOOPBACKNET) {
89955163Sshin			afd = &afdl[N_INET6];
90055163Sshin			memcpy(&faith_prefix.s6_addr[12], addr,
90155163Sshin			       sizeof(struct in_addr));
90255163Sshin			translate = 1;
90355163Sshin		}
90455163Sshin	}
90555163Sshin#endif
90655163Sshin
90755163Sshin	ai = (struct addrinfo *)malloc(sizeof(struct addrinfo)
90855163Sshin		+ (afd->a_socklen));
90955163Sshin	if (ai == NULL)
91055163Sshin		return NULL;
91155163Sshin
91255163Sshin	memcpy(ai, pai, sizeof(struct addrinfo));
91361877Sume	ai->ai_addr = (struct sockaddr *)(void *)(ai + 1);
91461877Sume	memset(ai->ai_addr, 0, (size_t)afd->a_socklen);
91555163Sshin	ai->ai_addr->sa_len = afd->a_socklen;
91655163Sshin	ai->ai_addrlen = afd->a_socklen;
91755163Sshin	ai->ai_addr->sa_family = ai->ai_family = afd->a_af;
91865532Snectar	p = (char *)(void *)(ai->ai_addr);
91955163Sshin#ifdef FAITH
92055163Sshin	if (translate == 1)
92165532Snectar		memcpy(p + afd->a_off, &faith_prefix, (size_t)afd->a_addrlen);
92255163Sshin	else
92355163Sshin#endif
92465532Snectar	memcpy(p + afd->a_off, addr, (size_t)afd->a_addrlen);
92555163Sshin	return ai;
92655163Sshin}
92755163Sshin
92855163Sshinstatic int
92955163Sshinget_portmatch(ai, servname)
93055163Sshin	const struct addrinfo *ai;
93155163Sshin	const char *servname;
93255163Sshin{
93361877Sume
93455163Sshin	/* get_port does not touch first argument. when matchonly == 1. */
93561877Sume	/* LINTED const cast */
93655163Sshin	return get_port((struct addrinfo *)ai, servname, 1);
93755163Sshin}
93855163Sshin
93955163Sshinstatic int
94055163Sshinget_port(ai, servname, matchonly)
94155163Sshin	struct addrinfo *ai;
94255163Sshin	const char *servname;
94355163Sshin	int matchonly;
94455163Sshin{
94555163Sshin	const char *proto;
94655163Sshin	struct servent *sp;
94755163Sshin	int port;
94855163Sshin	int allownumeric;
94955163Sshin
95055163Sshin	if (servname == NULL)
95155163Sshin		return 0;
95261877Sume	switch (ai->ai_family) {
95361877Sume	case AF_INET:
95461877Sume#ifdef AF_INET6
95561877Sume	case AF_INET6:
95655163Sshin#endif
95761877Sume		break;
95861877Sume	default:
95955163Sshin		return 0;
96061877Sume	}
96155163Sshin
96255163Sshin	switch (ai->ai_socktype) {
96355163Sshin	case SOCK_RAW:
96455163Sshin		return EAI_SERVICE;
96555163Sshin	case SOCK_DGRAM:
96655163Sshin	case SOCK_STREAM:
96755163Sshin		allownumeric = 1;
96855163Sshin		break;
96955163Sshin	case ANY:
97055163Sshin		allownumeric = 0;
97155163Sshin		break;
97255163Sshin	default:
97355163Sshin		return EAI_SOCKTYPE;
97455163Sshin	}
97555163Sshin
97655163Sshin	if (str_isnumber(servname)) {
97755163Sshin		if (!allownumeric)
97855163Sshin			return EAI_SERVICE;
97955163Sshin		port = htons(atoi(servname));
98055163Sshin		if (port < 0 || port > 65535)
98155163Sshin			return EAI_SERVICE;
98255163Sshin	} else {
98355163Sshin		switch (ai->ai_socktype) {
98455163Sshin		case SOCK_DGRAM:
98555163Sshin			proto = "udp";
98655163Sshin			break;
98755163Sshin		case SOCK_STREAM:
98855163Sshin			proto = "tcp";
98955163Sshin			break;
99055163Sshin		default:
99155163Sshin			proto = NULL;
99255163Sshin			break;
99355163Sshin		}
99455163Sshin
99555163Sshin		if ((sp = getservbyname(servname, proto)) == NULL)
99655163Sshin			return EAI_SERVICE;
99755163Sshin		port = sp->s_port;
99855163Sshin	}
99955163Sshin
100055163Sshin	if (!matchonly) {
100155163Sshin		switch (ai->ai_family) {
100255163Sshin		case AF_INET:
100361877Sume			((struct sockaddr_in *)(void *)
100461877Sume			    ai->ai_addr)->sin_port = port;
100555163Sshin			break;
100655163Sshin#ifdef INET6
100755163Sshin		case AF_INET6:
100861877Sume			((struct sockaddr_in6 *)(void *)
100961877Sume			    ai->ai_addr)->sin6_port = port;
101055163Sshin			break;
101155163Sshin#endif
101255163Sshin		}
101355163Sshin	}
101455163Sshin
101555163Sshin	return 0;
101655163Sshin}
101755163Sshin
101855163Sshinstatic const struct afd *
101955163Sshinfind_afd(af)
102055163Sshin	int af;
102155163Sshin{
102255163Sshin	const struct afd *afd;
102355163Sshin
102455163Sshin	if (af == PF_UNSPEC)
102555163Sshin		return NULL;
102655163Sshin	for (afd = afdl; afd->a_af; afd++) {
102755163Sshin		if (afd->a_af == af)
102855163Sshin			return afd;
102955163Sshin	}
103055163Sshin	return NULL;
103155163Sshin}
103261877Sume
103361877Sume/*
103461877Sume * post-2553: AI_ADDRCONFIG check.  if we use getipnodeby* as backend, backend
103561877Sume * will take care of it.
103661877Sume * the semantics of AI_ADDRCONFIG is not defined well.  we are not sure
103761877Sume * if the code is right or not.
103861877Sume *
103961877Sume * XXX PF_UNSPEC -> PF_INET6 + PF_INET mapping needs to be in sync with
104061877Sume * _dns_getaddrinfo.
104161877Sume */
104261877Sumestatic int
104361877Sumeaddrconfig(pai)
104461877Sume	struct addrinfo *pai;
104561877Sume{
104661877Sume	int s, af;
104761877Sume
104861877Sume	/*
104961877Sume	 * TODO:
105061877Sume	 * Note that implementation dependent test for address
105161877Sume	 * configuration should be done everytime called
105261877Sume	 * (or apropriate interval),
105361877Sume	 * because addresses will be dynamically assigned or deleted.
105461877Sume	 */
105561877Sume	af = pai->ai_family;
105661877Sume	if (af == AF_UNSPEC) {
105771579Sdeischen		if ((s = _socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
105861877Sume			af = AF_INET;
105961877Sume		else {
106063704Sjasone			_close(s);
106171579Sdeischen			if ((s = _socket(AF_INET, SOCK_DGRAM, 0)) < 0)
106261877Sume				af = AF_INET6;
106361877Sume			else
106463704Sjasone				_close(s);
106561877Sume		}
106661877Sume	}
106761877Sume	if (af != AF_UNSPEC) {
106871579Sdeischen		if ((s = _socket(af, SOCK_DGRAM, 0)) < 0)
106961877Sume			return 0;
107063704Sjasone		_close(s);
107161877Sume	}
107261877Sume	pai->ai_family = af;
107361877Sume	return 1;
107461877Sume}
107561877Sume
107661877Sume#ifdef INET6
107761877Sume/* convert a string to a scope identifier. XXX: IPv6 specific */
107861877Sumestatic int
107961877Sumeip6_str2scopeid(scope, sin6)
108061877Sume	char *scope;
108161877Sume	struct sockaddr_in6 *sin6;
108261877Sume{
108361877Sume	int scopeid;
108461877Sume	struct in6_addr *a6 = &sin6->sin6_addr;
108561877Sume	char *ep;
108661877Sume
108762836Sitojun	/* empty scopeid portion is invalid */
108862836Sitojun	if (*scope == '\0')
108962836Sitojun		return -1;
109062836Sitojun
109161877Sume	if (IN6_IS_ADDR_LINKLOCAL(a6) || IN6_IS_ADDR_MC_LINKLOCAL(a6)) {
109261877Sume		/*
109361877Sume		 * We currently assume a one-to-one mapping between links
109461877Sume		 * and interfaces, so we simply use interface indices for
109561877Sume		 * like-local scopes.
109661877Sume		 */
109761877Sume		scopeid = if_nametoindex(scope);
109861877Sume		if (scopeid == 0)
109961877Sume			goto trynumeric;
110061877Sume		return(scopeid);
110161877Sume	}
110261877Sume
110361877Sume	/* still unclear about literal, allow numeric only - placeholder */
110461877Sume	if (IN6_IS_ADDR_SITELOCAL(a6) || IN6_IS_ADDR_MC_SITELOCAL(a6))
110561877Sume		goto trynumeric;
110661877Sume	if (IN6_IS_ADDR_MC_ORGLOCAL(a6))
110761877Sume		goto trynumeric;
110861877Sume	else
110961877Sume		goto trynumeric;	/* global */
111061877Sume
111161877Sume	/* try to convert to a numeric id as a last resort */
111261877Sume  trynumeric:
111361877Sume	scopeid = (int)strtoul(scope, &ep, 10);
111461877Sume	if (*ep == '\0')
111561877Sume		return scopeid;
111661877Sume	else
111761877Sume		return -1;
111861877Sume}
111961877Sume#endif
112061877Sume
112161877Sume#ifdef DEBUG
112261877Sumestatic const char AskedForGot[] =
112361877Sume	"gethostby*.getanswer: asked for \"%s\", got \"%s\"";
112461877Sume#endif
112565532Snectarstatic FILE *hostf = NULL;
112661877Sume
112761877Sumestatic struct addrinfo *
112861877Sumegetanswer(answer, anslen, qname, qtype, pai)
112961877Sume	const querybuf *answer;
113061877Sume	int anslen;
113161877Sume	const char *qname;
113261877Sume	int qtype;
113361877Sume	const struct addrinfo *pai;
113461877Sume{
113561877Sume	struct addrinfo sentinel, *cur;
113661877Sume	struct addrinfo ai;
113761877Sume	const struct afd *afd;
113861877Sume	char *canonname;
113961877Sume	const HEADER *hp;
114061877Sume	const u_char *cp;
114161877Sume	int n;
114261877Sume	const u_char *eom;
114361877Sume	char *bp;
114461877Sume	int type, class, buflen, ancount, qdcount;
114561877Sume	int haveanswer, had_error;
114661877Sume	char tbuf[MAXDNAME];
114792905Sobrien	int (*name_ok)(const char *);
114861877Sume	char hostbuf[8*1024];
114961877Sume
115061877Sume	memset(&sentinel, 0, sizeof(sentinel));
115161877Sume	cur = &sentinel;
115261877Sume
115361877Sume	canonname = NULL;
115461877Sume	eom = answer->buf + anslen;
115561877Sume	switch (qtype) {
115661877Sume	case T_A:
115761877Sume	case T_AAAA:
115861877Sume	case T_ANY:	/*use T_ANY only for T_A/T_AAAA lookup*/
115961877Sume		name_ok = res_hnok;
116061877Sume		break;
116161877Sume	default:
116261877Sume		return (NULL);	/* XXX should be abort(); */
116361877Sume	}
116461877Sume	/*
116561877Sume	 * find first satisfactory answer
116661877Sume	 */
116761877Sume	hp = &answer->hdr;
116861877Sume	ancount = ntohs(hp->ancount);
116961877Sume	qdcount = ntohs(hp->qdcount);
117061877Sume	bp = hostbuf;
117161877Sume	buflen = sizeof hostbuf;
117261877Sume	cp = answer->buf + HFIXEDSZ;
117361877Sume	if (qdcount != 1) {
117461877Sume		h_errno = NO_RECOVERY;
117561877Sume		return (NULL);
117661877Sume	}
117761877Sume	n = dn_expand(answer->buf, eom, cp, bp, buflen);
117861877Sume	if ((n < 0) || !(*name_ok)(bp)) {
117961877Sume		h_errno = NO_RECOVERY;
118061877Sume		return (NULL);
118161877Sume	}
118261877Sume	cp += n + QFIXEDSZ;
118361877Sume	if (qtype == T_A || qtype == T_AAAA || qtype == T_ANY) {
118461877Sume		/* res_send() has already verified that the query name is the
118561877Sume		 * same as the one we sent; this just gets the expanded name
118661877Sume		 * (i.e., with the succeeding search-domain tacked on).
118761877Sume		 */
118861877Sume		n = strlen(bp) + 1;		/* for the \0 */
118961877Sume		if (n >= MAXHOSTNAMELEN) {
119061877Sume			h_errno = NO_RECOVERY;
119161877Sume			return (NULL);
119261877Sume		}
119361877Sume		canonname = bp;
119461877Sume		bp += n;
119561877Sume		buflen -= n;
119661877Sume		/* The qname can be abbreviated, but h_name is now absolute. */
119761877Sume		qname = canonname;
119861877Sume	}
119961877Sume	haveanswer = 0;
120061877Sume	had_error = 0;
120161877Sume	while (ancount-- > 0 && cp < eom && !had_error) {
120261877Sume		n = dn_expand(answer->buf, eom, cp, bp, buflen);
120361877Sume		if ((n < 0) || !(*name_ok)(bp)) {
120461877Sume			had_error++;
120561877Sume			continue;
120661877Sume		}
120761877Sume		cp += n;			/* name */
120861877Sume		type = _getshort(cp);
120961877Sume 		cp += INT16SZ;			/* type */
121061877Sume		class = _getshort(cp);
121161877Sume 		cp += INT16SZ + INT32SZ;	/* class, TTL */
121261877Sume		n = _getshort(cp);
121361877Sume		cp += INT16SZ;			/* len */
121461877Sume		if (class != C_IN) {
121561877Sume			/* XXX - debug? syslog? */
121661877Sume			cp += n;
121761877Sume			continue;		/* XXX - had_error++ ? */
121861877Sume		}
121961877Sume		if ((qtype == T_A || qtype == T_AAAA || qtype == T_ANY) &&
122061877Sume		    type == T_CNAME) {
122161877Sume			n = dn_expand(answer->buf, eom, cp, tbuf, sizeof tbuf);
122261877Sume			if ((n < 0) || !(*name_ok)(tbuf)) {
122361877Sume				had_error++;
122461877Sume				continue;
122561877Sume			}
122661877Sume			cp += n;
122761877Sume			/* Get canonical name. */
122861877Sume			n = strlen(tbuf) + 1;	/* for the \0 */
122961877Sume			if (n > buflen || n >= MAXHOSTNAMELEN) {
123061877Sume				had_error++;
123161877Sume				continue;
123261877Sume			}
123361877Sume			strcpy(bp, tbuf);
123461877Sume			canonname = bp;
123561877Sume			bp += n;
123661877Sume			buflen -= n;
123761877Sume			continue;
123861877Sume		}
123961877Sume		if (qtype == T_ANY) {
124061877Sume			if (!(type == T_A || type == T_AAAA)) {
124161877Sume				cp += n;
124261877Sume				continue;
124361877Sume			}
124461877Sume		} else if (type != qtype) {
124561877Sume#ifdef DEBUG
124661877Sume			if (type != T_KEY && type != T_SIG)
124761877Sume				syslog(LOG_NOTICE|LOG_AUTH,
124861877Sume	       "gethostby*.getanswer: asked for \"%s %s %s\", got type \"%s\"",
124961877Sume				       qname, p_class(C_IN), p_type(qtype),
125061877Sume				       p_type(type));
125161877Sume#endif
125261877Sume			cp += n;
125361877Sume			continue;		/* XXX - had_error++ ? */
125461877Sume		}
125561877Sume		switch (type) {
125661877Sume		case T_A:
125761877Sume		case T_AAAA:
125861877Sume			if (strcasecmp(canonname, bp) != 0) {
125961877Sume#ifdef DEBUG
126061877Sume				syslog(LOG_NOTICE|LOG_AUTH,
126161877Sume				       AskedForGot, canonname, bp);
126261877Sume#endif
126361877Sume				cp += n;
126461877Sume				continue;	/* XXX - had_error++ ? */
126561877Sume			}
126661877Sume			if (type == T_A && n != INADDRSZ) {
126761877Sume				cp += n;
126861877Sume				continue;
126961877Sume			}
127061877Sume			if (type == T_AAAA && n != IN6ADDRSZ) {
127161877Sume				cp += n;
127261877Sume				continue;
127361877Sume			}
127461877Sume#ifdef FILTER_V4MAPPED
127561877Sume			if (type == T_AAAA) {
127661877Sume				struct in6_addr in6;
127761877Sume				memcpy(&in6, cp, sizeof(in6));
127861877Sume				if (IN6_IS_ADDR_V4MAPPED(&in6)) {
127961877Sume					cp += n;
128061877Sume					continue;
128161877Sume				}
128261877Sume			}
128361877Sume#endif
128461877Sume			if (!haveanswer) {
128561877Sume				int nn;
128661877Sume
128761877Sume				canonname = bp;
128861877Sume				nn = strlen(bp) + 1;	/* for the \0 */
128961877Sume				bp += nn;
129061877Sume				buflen -= nn;
129161877Sume			}
129261877Sume
129361877Sume			/* don't overwrite pai */
129461877Sume			ai = *pai;
129561877Sume			ai.ai_family = (type == T_A) ? AF_INET : AF_INET6;
129661877Sume			afd = find_afd(ai.ai_family);
129761877Sume			if (afd == NULL) {
129861877Sume				cp += n;
129961877Sume				continue;
130061877Sume			}
130161877Sume			cur->ai_next = get_ai(&ai, afd, (const char *)cp);
130261877Sume			if (cur->ai_next == NULL)
130361877Sume				had_error++;
130461877Sume			while (cur && cur->ai_next)
130561877Sume				cur = cur->ai_next;
130661877Sume			cp += n;
130761877Sume			break;
130861877Sume		default:
130961877Sume			abort();
131061877Sume		}
131161877Sume		if (!had_error)
131261877Sume			haveanswer++;
131361877Sume	}
131461877Sume	if (haveanswer) {
131561877Sume		if (!canonname)
131661877Sume			(void)get_canonname(pai, sentinel.ai_next, qname);
131761877Sume		else
131861877Sume			(void)get_canonname(pai, sentinel.ai_next, canonname);
131961877Sume		h_errno = NETDB_SUCCESS;
132061877Sume		return sentinel.ai_next;
132161877Sume	}
132261877Sume
132361877Sume	h_errno = NO_RECOVERY;
132461877Sume	return NULL;
132561877Sume}
132661877Sume
132761877Sume/*ARGSUSED*/
132861877Sumestatic int
132965532Snectar_dns_getaddrinfo(rv, cb_data, ap)
133065532Snectar	void	*rv;
133165532Snectar	void	*cb_data;
133265532Snectar	va_list	 ap;
133361877Sume{
133461877Sume	struct addrinfo *ai;
133561877Sume	querybuf buf, buf2;
133661877Sume	const char *name;
133765532Snectar	const struct addrinfo *pai;
133861877Sume	struct addrinfo sentinel, *cur;
133961877Sume	struct res_target q, q2;
134061877Sume
134165532Snectar	name = va_arg(ap, char *);
134265532Snectar	pai = va_arg(ap, const struct addrinfo *);
134365532Snectar
134461877Sume	memset(&q, 0, sizeof(q2));
134561877Sume	memset(&q2, 0, sizeof(q2));
134661877Sume	memset(&sentinel, 0, sizeof(sentinel));
134761877Sume	cur = &sentinel;
134861877Sume
134961877Sume	switch (pai->ai_family) {
135061877Sume	case AF_UNSPEC:
135161877Sume		/* prefer IPv6 */
135262614Sitojun		q.qclass = C_IN;
135362614Sitojun		q.qtype = T_AAAA;
135461877Sume		q.answer = buf.buf;
135561877Sume		q.anslen = sizeof(buf);
135661877Sume		q.next = &q2;
135762614Sitojun		q2.qclass = C_IN;
135862614Sitojun		q2.qtype = T_A;
135961877Sume		q2.answer = buf2.buf;
136061877Sume		q2.anslen = sizeof(buf2);
136161877Sume		break;
136261877Sume	case AF_INET:
136362614Sitojun		q.qclass = C_IN;
136462614Sitojun		q.qtype = T_A;
136561877Sume		q.answer = buf.buf;
136661877Sume		q.anslen = sizeof(buf);
136761877Sume		break;
136861877Sume	case AF_INET6:
136962614Sitojun		q.qclass = C_IN;
137062614Sitojun		q.qtype = T_AAAA;
137161877Sume		q.answer = buf.buf;
137261877Sume		q.anslen = sizeof(buf);
137361877Sume		break;
137461877Sume	default:
137565532Snectar		return NS_UNAVAIL;
137661877Sume	}
137765532Snectar	if (res_searchN(name, &q) < 0)
137865532Snectar		return NS_NOTFOUND;
137962614Sitojun	ai = getanswer(&buf, q.n, q.name, q.qtype, pai);
138061877Sume	if (ai) {
138161877Sume		cur->ai_next = ai;
138261877Sume		while (cur && cur->ai_next)
138361877Sume			cur = cur->ai_next;
138461877Sume	}
138561877Sume	if (q.next) {
138662614Sitojun		ai = getanswer(&buf2, q2.n, q2.name, q2.qtype, pai);
138761877Sume		if (ai)
138861877Sume			cur->ai_next = ai;
138961877Sume	}
139061877Sume	if (sentinel.ai_next == NULL)
139161877Sume		switch (h_errno) {
139261877Sume		case HOST_NOT_FOUND:
139365532Snectar			return NS_NOTFOUND;
139461877Sume		case TRY_AGAIN:
139565532Snectar			return NS_TRYAGAIN;
139661877Sume		default:
139765532Snectar			return NS_UNAVAIL;
139861877Sume		}
139965532Snectar	*((struct addrinfo **)rv) = sentinel.ai_next;
140065532Snectar	return NS_SUCCESS;
140161877Sume}
140261877Sume
140365532Snectarstatic void
140465532Snectar_sethtent()
140565532Snectar{
140665532Snectar	if (!hostf)
140765532Snectar		hostf = fopen(_PATH_HOSTS, "r" );
140865532Snectar	else
140965532Snectar		rewind(hostf);
141065532Snectar}
141165532Snectar
141265532Snectarstatic void
141365532Snectar_endhtent()
141465532Snectar{
141565532Snectar	if (hostf) {
141665532Snectar		(void) fclose(hostf);
141765532Snectar		hostf = NULL;
141865532Snectar	}
141965532Snectar}
142065532Snectar
142161877Sumestatic struct addrinfo *
142265532Snectar_gethtent(name, pai)
142361877Sume	const char *name;
142461877Sume	const struct addrinfo *pai;
142561877Sume{
142661877Sume	char *p;
142761877Sume	char *cp, *tname, *cname;
142861877Sume	struct addrinfo hints, *res0, *res;
142961877Sume	int error;
143061877Sume	const char *addr;
143161877Sume	char hostbuf[8*1024];
143261877Sume
143365532Snectar	if (!hostf && !(hostf = fopen(_PATH_HOSTS, "r" )))
143465532Snectar		return (NULL);
143561877Sume again:
143661877Sume	if (!(p = fgets(hostbuf, sizeof hostbuf, hostf)))
143761877Sume		return (NULL);
143861877Sume	if (*p == '#')
143961877Sume		goto again;
144061877Sume	if (!(cp = strpbrk(p, "#\n")))
144161877Sume		goto again;
144261877Sume	*cp = '\0';
144361877Sume	if (!(cp = strpbrk(p, " \t")))
144461877Sume		goto again;
144561877Sume	*cp++ = '\0';
144661877Sume	addr = p;
144761877Sume	cname = NULL;
144861877Sume	/* if this is not something we're looking for, skip it. */
144961877Sume	while (cp && *cp) {
145061877Sume		if (*cp == ' ' || *cp == '\t') {
145161877Sume			cp++;
145261877Sume			continue;
145361877Sume		}
145461877Sume		tname = cp;
145561877Sume		if (cname == NULL)
145661877Sume			cname = cp;
145761877Sume		if ((cp = strpbrk(cp, " \t")) != NULL)
145861877Sume			*cp++ = '\0';
145961877Sume		if (strcasecmp(name, tname) == 0)
146061877Sume			goto found;
146161877Sume	}
146261877Sume	goto again;
146361877Sume
146461877Sumefound:
146561877Sume	hints = *pai;
146661877Sume	hints.ai_flags = AI_NUMERICHOST;
146761877Sume	error = getaddrinfo(addr, NULL, &hints, &res0);
146861877Sume	if (error)
146961877Sume		goto again;
147061877Sume#ifdef FILTER_V4MAPPED
147161877Sume	/* XXX should check all items in the chain */
147261877Sume	if (res0->ai_family == AF_INET6 &&
147361877Sume	    IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)res0->ai_addr)->sin6_addr)) {
147461877Sume		freeaddrinfo(res0);
147561877Sume		goto again;
147661877Sume	}
147761877Sume#endif
147861877Sume	for (res = res0; res; res = res->ai_next) {
147961877Sume		/* cover it up */
148061877Sume		res->ai_flags = pai->ai_flags;
148161877Sume
148261877Sume		if (pai->ai_flags & AI_CANONNAME) {
148361877Sume			if (get_canonname(pai, res, cname) != 0) {
148461877Sume				freeaddrinfo(res0);
148561877Sume				goto again;
148661877Sume			}
148761877Sume		}
148861877Sume	}
148961877Sume	return res0;
149061877Sume}
149161877Sume
149261877Sume/*ARGSUSED*/
149361877Sumestatic int
149465532Snectar_files_getaddrinfo(rv, cb_data, ap)
149565532Snectar	void	*rv;
149665532Snectar	void	*cb_data;
149765532Snectar	va_list	 ap;
149865532Snectar{
149965532Snectar	const char *name;
150061877Sume	const struct addrinfo *pai;
150161877Sume	struct addrinfo sentinel, *cur;
150261877Sume	struct addrinfo *p;
150361877Sume
150465532Snectar	name = va_arg(ap, char *);
150565532Snectar	pai = va_arg(ap, struct addrinfo *);
150665532Snectar
150765532Snectar	memset(&sentinel, 0, sizeof(sentinel));
150861877Sume	cur = &sentinel;
150961877Sume
151065532Snectar	_sethtent();
151165532Snectar	while ((p = _gethtent(name, pai)) != NULL) {
151261877Sume		cur->ai_next = p;
151361877Sume		while (cur && cur->ai_next)
151461877Sume			cur = cur->ai_next;
151561877Sume	}
151665532Snectar	_endhtent();
151761877Sume
151865532Snectar	*((struct addrinfo **)rv) = sentinel.ai_next;
151965532Snectar	if (sentinel.ai_next == NULL)
152065532Snectar		return NS_NOTFOUND;
152165532Snectar	return NS_SUCCESS;
152261877Sume}
152361877Sume
152461877Sume#ifdef YP
152565532Snectarstatic char *__ypdomain;
152665532Snectar
152761877Sume/*ARGSUSED*/
152865532Snectarstatic struct addrinfo *
152965532Snectar_yphostent(line, pai)
153065532Snectar	char *line;
153161877Sume	const struct addrinfo *pai;
153261877Sume{
153361877Sume	struct addrinfo sentinel, *cur;
153465532Snectar	struct addrinfo hints, *res, *res0;
153561877Sume	int error;
153665532Snectar	char *p = line;
153765532Snectar	const char *addr, *canonname;
153865532Snectar	char *nextline;
153965532Snectar	char *cp;
154061877Sume
154165532Snectar	addr = canonname = NULL;
154265532Snectar
154365532Snectar	memset(&sentinel, 0, sizeof(sentinel));
154461877Sume	cur = &sentinel;
154561877Sume
154665532Snectarnextline:
154765532Snectar	/* terminate line */
154865532Snectar	cp = strchr(p, '\n');
154965532Snectar	if (cp) {
155065532Snectar		*cp++ = '\0';
155165532Snectar		nextline = cp;
155265532Snectar	} else
155365532Snectar		nextline = NULL;
155461877Sume
155565532Snectar	cp = strpbrk(p, " \t");
155665532Snectar	if (cp == NULL) {
155765532Snectar		if (canonname == NULL)
155865532Snectar			return (NULL);
155965532Snectar		else
156065532Snectar			goto done;
156161877Sume	}
156265532Snectar	*cp++ = '\0';
156361877Sume
156465532Snectar	addr = p;
156561877Sume
156665532Snectar	while (cp && *cp) {
156765532Snectar		if (*cp == ' ' || *cp == '\t') {
156865532Snectar			cp++;
156961877Sume			continue;
157065532Snectar		}
157165532Snectar		if (!canonname)
157265532Snectar			canonname = cp;
157365532Snectar		if ((cp = strpbrk(cp, " \t")) != NULL)
157465532Snectar			*cp++ = '\0';
157565532Snectar	}
157661877Sume
157765532Snectar	hints = *pai;
157865532Snectar	hints.ai_flags = AI_NUMERICHOST;
157965532Snectar	error = getaddrinfo(addr, NULL, &hints, &res0);
158065532Snectar	if (error == 0) {
158165532Snectar		for (res = res0; res; res = res->ai_next) {
158265532Snectar			/* cover it up */
158365532Snectar			res->ai_flags = pai->ai_flags;
158461877Sume
158565532Snectar			if (pai->ai_flags & AI_CANONNAME)
158665532Snectar				(void)get_canonname(pai, res, canonname);
158761877Sume		}
158865532Snectar	} else
158965532Snectar		res0 = NULL;
159065532Snectar	if (res0) {
159165532Snectar		cur->ai_next = res0;
159261877Sume		while (cur && cur->ai_next)
159361877Sume			cur = cur->ai_next;
159461877Sume	}
159561877Sume
159665532Snectar	if (nextline) {
159765532Snectar		p = nextline;
159865532Snectar		goto nextline;
159965532Snectar	}
160061877Sume
160165532Snectardone:
160265532Snectar	return sentinel.ai_next;
160361877Sume}
160465532Snectar
160565532Snectar/*ARGSUSED*/
160665532Snectarstatic int
160765532Snectar_yp_getaddrinfo(rv, cb_data, ap)
160865532Snectar	void	*rv;
160965532Snectar	void	*cb_data;
161065532Snectar	va_list	 ap;
161165532Snectar{
161265532Snectar	struct addrinfo sentinel, *cur;
161365532Snectar	struct addrinfo *ai = NULL;
161465532Snectar	static char *__ypcurrent;
161565532Snectar	int __ypcurrentlen, r;
161665532Snectar	const char *name;
161765532Snectar	const struct addrinfo *pai;
161865532Snectar
161965532Snectar	name = va_arg(ap, char *);
162065532Snectar	pai = va_arg(ap, const struct addrinfo *);
162165532Snectar
162265532Snectar	memset(&sentinel, 0, sizeof(sentinel));
162365532Snectar	cur = &sentinel;
162465532Snectar
162565532Snectar	if (!__ypdomain) {
162665532Snectar		if (_yp_check(&__ypdomain) == 0)
162765532Snectar			return NS_UNAVAIL;
162865532Snectar	}
162965532Snectar	if (__ypcurrent)
163065532Snectar		free(__ypcurrent);
163165532Snectar	__ypcurrent = NULL;
163265532Snectar
163365532Snectar	/* hosts.byname is only for IPv4 (Solaris8) */
163465532Snectar	if (pai->ai_family == PF_UNSPEC || pai->ai_family == PF_INET) {
163565532Snectar		r = yp_match(__ypdomain, "hosts.byname", name,
163665532Snectar			(int)strlen(name), &__ypcurrent, &__ypcurrentlen);
163765532Snectar		if (r == 0) {
163865532Snectar			struct addrinfo ai4;
163965532Snectar
164065532Snectar			ai4 = *pai;
164165532Snectar			ai4.ai_family = AF_INET;
164265532Snectar			ai = _yphostent(__ypcurrent, &ai4);
164365532Snectar			if (ai) {
164465532Snectar				cur->ai_next = ai;
164565532Snectar				while (cur && cur->ai_next)
164665532Snectar					cur = cur->ai_next;
164765532Snectar			}
164865532Snectar		}
164965532Snectar	}
165065532Snectar
165165532Snectar	/* ipnodes.byname can hold both IPv4/v6 */
165265532Snectar	r = yp_match(__ypdomain, "ipnodes.byname", name,
165365532Snectar		(int)strlen(name), &__ypcurrent, &__ypcurrentlen);
165465532Snectar	if (r == 0) {
165565532Snectar		ai = _yphostent(__ypcurrent, pai);
165665532Snectar		if (ai) {
165765532Snectar			cur->ai_next = ai;
165865532Snectar			while (cur && cur->ai_next)
165965532Snectar				cur = cur->ai_next;
166065532Snectar		}
166165532Snectar	}
166265532Snectar
166365532Snectar	if (sentinel.ai_next == NULL) {
166465532Snectar		h_errno = HOST_NOT_FOUND;
166565532Snectar		return NS_NOTFOUND;
166665532Snectar	}
166765532Snectar	*((struct addrinfo **)rv) = sentinel.ai_next;
166865532Snectar	return NS_SUCCESS;
166965532Snectar}
167061877Sume#endif
167161877Sume
167261877Sume/* resolver logic */
167361877Sume
167492905Sobrienextern const char *__hostalias(const char *);
167561877Sumeextern int h_errno;
167661877Sume
167761877Sume/*
167861877Sume * Formulate a normal query, send, and await answer.
167961877Sume * Returned answer is placed in supplied buffer "answer".
168061877Sume * Perform preliminary check of answer, returning success only
168161877Sume * if no error is indicated and the answer count is nonzero.
168261877Sume * Return the size of the response on success, -1 on error.
168361877Sume * Error number is left in h_errno.
168461877Sume *
168561877Sume * Caller must parse answer and determine whether it answers the question.
168661877Sume */
168761877Sumestatic int
168861877Sumeres_queryN(name, target)
168961877Sume	const char *name;	/* domain name */
169061877Sume	struct res_target *target;
169161877Sume{
169261877Sume	u_char buf[MAXPACKET];
169361877Sume	HEADER *hp;
169461877Sume	int n;
169561877Sume	struct res_target *t;
169661877Sume	int rcode;
169761877Sume	int ancount;
169861877Sume
169961877Sume	rcode = NOERROR;
170061877Sume	ancount = 0;
170161877Sume
170261877Sume	if ((_res.options & RES_INIT) == 0 && res_init() == -1) {
170361877Sume		h_errno = NETDB_INTERNAL;
170461877Sume		return (-1);
170561877Sume	}
170661877Sume
170761877Sume	for (t = target; t; t = t->next) {
170861877Sume		int class, type;
170961877Sume		u_char *answer;
171061877Sume		int anslen;
171161877Sume
171261877Sume		hp = (HEADER *)(void *)t->answer;
171361877Sume		hp->rcode = NOERROR;	/* default */
171461877Sume
171561877Sume		/* make it easier... */
171662614Sitojun		class = t->qclass;
171762614Sitojun		type = t->qtype;
171861877Sume		answer = t->answer;
171961877Sume		anslen = t->anslen;
172061877Sume#ifdef DEBUG
172161877Sume		if (_res.options & RES_DEBUG)
172261877Sume			printf(";; res_query(%s, %d, %d)\n", name, class, type);
172361877Sume#endif
172461877Sume
172561877Sume		n = res_mkquery(QUERY, name, class, type, NULL, 0, NULL,
172661877Sume		    buf, sizeof(buf));
172778012Sume		if (n > 0 && (_res.options & RES_USE_EDNS0) != 0)
172878012Sume			n = res_opt(n, buf, sizeof(buf), anslen);
172961877Sume		if (n <= 0) {
173061877Sume#ifdef DEBUG
173161877Sume			if (_res.options & RES_DEBUG)
173261877Sume				printf(";; res_query: mkquery failed\n");
173361877Sume#endif
173461877Sume			h_errno = NO_RECOVERY;
173561877Sume			return (n);
173661877Sume		}
173761877Sume		n = res_send(buf, n, answer, anslen);
173861877Sume#if 0
173961877Sume		if (n < 0) {
174061877Sume#ifdef DEBUG
174161877Sume			if (_res.options & RES_DEBUG)
174261877Sume				printf(";; res_query: send error\n");
174361877Sume#endif
174461877Sume			h_errno = TRY_AGAIN;
174561877Sume			return (n);
174661877Sume		}
174761877Sume#endif
174861877Sume
174961877Sume		if (n < 0 || hp->rcode != NOERROR || ntohs(hp->ancount) == 0) {
175061877Sume			rcode = hp->rcode;	/* record most recent error */
175161877Sume#ifdef DEBUG
175261877Sume			if (_res.options & RES_DEBUG)
175361877Sume				printf(";; rcode = %d, ancount=%d\n", hp->rcode,
175461877Sume				    ntohs(hp->ancount));
175561877Sume#endif
175661877Sume			continue;
175761877Sume		}
175861877Sume
175961877Sume		ancount += ntohs(hp->ancount);
176061877Sume
176161877Sume		t->n = n;
176261877Sume	}
176361877Sume
176461877Sume	if (ancount == 0) {
176561877Sume		switch (rcode) {
176661877Sume		case NXDOMAIN:
176761877Sume			h_errno = HOST_NOT_FOUND;
176861877Sume			break;
176961877Sume		case SERVFAIL:
177061877Sume			h_errno = TRY_AGAIN;
177161877Sume			break;
177261877Sume		case NOERROR:
177361877Sume			h_errno = NO_DATA;
177461877Sume			break;
177561877Sume		case FORMERR:
177661877Sume		case NOTIMP:
177761877Sume		case REFUSED:
177861877Sume		default:
177961877Sume			h_errno = NO_RECOVERY;
178061877Sume			break;
178161877Sume		}
178261877Sume		return (-1);
178361877Sume	}
178461877Sume	return (ancount);
178561877Sume}
178661877Sume
178761877Sume/*
178861877Sume * Formulate a normal query, send, and retrieve answer in supplied buffer.
178961877Sume * Return the size of the response on success, -1 on error.
179061877Sume * If enabled, implement search rules until answer or unrecoverable failure
179161877Sume * is detected.  Error code, if any, is left in h_errno.
179261877Sume */
179361877Sumestatic int
179461877Sumeres_searchN(name, target)
179561877Sume	const char *name;	/* domain name */
179661877Sume	struct res_target *target;
179761877Sume{
179861877Sume	const char *cp, * const *domain;
179961877Sume	HEADER *hp = (HEADER *)(void *)target->answer;	/*XXX*/
180061877Sume	u_int dots;
180161877Sume	int trailing_dot, ret, saved_herrno;
180261877Sume	int got_nodata = 0, got_servfail = 0, tried_as_is = 0;
180361877Sume
180461877Sume	if ((_res.options & RES_INIT) == 0 && res_init() == -1) {
180561877Sume		h_errno = NETDB_INTERNAL;
180661877Sume		return (-1);
180761877Sume	}
180861877Sume
180961877Sume	errno = 0;
181061877Sume	h_errno = HOST_NOT_FOUND;	/* default, if we never query */
181161877Sume	dots = 0;
181261877Sume	for (cp = name; *cp; cp++)
181361877Sume		dots += (*cp == '.');
181461877Sume	trailing_dot = 0;
181561877Sume	if (cp > name && *--cp == '.')
181661877Sume		trailing_dot++;
181761877Sume
181861877Sume	/*
181961877Sume	 * if there aren't any dots, it could be a user-level alias
182061877Sume	 */
182161877Sume	if (!dots && (cp = __hostalias(name)) != NULL)
182261877Sume		return (res_queryN(cp, target));
182361877Sume
182461877Sume	/*
182561877Sume	 * If there are dots in the name already, let's just give it a try
182661877Sume	 * 'as is'.  The threshold can be set with the "ndots" option.
182761877Sume	 */
182861877Sume	saved_herrno = -1;
182961877Sume	if (dots >= _res.ndots) {
183061877Sume		ret = res_querydomainN(name, NULL, target);
183161877Sume		if (ret > 0)
183261877Sume			return (ret);
183361877Sume		saved_herrno = h_errno;
183461877Sume		tried_as_is++;
183561877Sume	}
183661877Sume
183761877Sume	/*
183861877Sume	 * We do at least one level of search if
183961877Sume	 *	- there is no dot and RES_DEFNAME is set, or
184061877Sume	 *	- there is at least one dot, there is no trailing dot,
184161877Sume	 *	  and RES_DNSRCH is set.
184261877Sume	 */
184361877Sume	if ((!dots && (_res.options & RES_DEFNAMES)) ||
184461877Sume	    (dots && !trailing_dot && (_res.options & RES_DNSRCH))) {
184561877Sume		int done = 0;
184661877Sume
184761877Sume		for (domain = (const char * const *)_res.dnsrch;
184861877Sume		   *domain && !done;
184961877Sume		   domain++) {
185061877Sume
185161877Sume			ret = res_querydomainN(name, *domain, target);
185261877Sume			if (ret > 0)
185361877Sume				return (ret);
185461877Sume
185561877Sume			/*
185661877Sume			 * If no server present, give up.
185761877Sume			 * If name isn't found in this domain,
185861877Sume			 * keep trying higher domains in the search list
185961877Sume			 * (if that's enabled).
186061877Sume			 * On a NO_DATA error, keep trying, otherwise
186161877Sume			 * a wildcard entry of another type could keep us
186261877Sume			 * from finding this entry higher in the domain.
186361877Sume			 * If we get some other error (negative answer or
186461877Sume			 * server failure), then stop searching up,
186561877Sume			 * but try the input name below in case it's
186661877Sume			 * fully-qualified.
186761877Sume			 */
186861877Sume			if (errno == ECONNREFUSED) {
186961877Sume				h_errno = TRY_AGAIN;
187061877Sume				return (-1);
187161877Sume			}
187261877Sume
187361877Sume			switch (h_errno) {
187461877Sume			case NO_DATA:
187561877Sume				got_nodata++;
187661877Sume				/* FALLTHROUGH */
187761877Sume			case HOST_NOT_FOUND:
187861877Sume				/* keep trying */
187961877Sume				break;
188061877Sume			case TRY_AGAIN:
188161877Sume				if (hp->rcode == SERVFAIL) {
188261877Sume					/* try next search element, if any */
188361877Sume					got_servfail++;
188461877Sume					break;
188561877Sume				}
188661877Sume				/* FALLTHROUGH */
188761877Sume			default:
188861877Sume				/* anything else implies that we're done */
188961877Sume				done++;
189061877Sume			}
189161877Sume			/*
189261877Sume			 * if we got here for some reason other than DNSRCH,
189361877Sume			 * we only wanted one iteration of the loop, so stop.
189461877Sume			 */
189561877Sume			if (!(_res.options & RES_DNSRCH))
189661877Sume			        done++;
189761877Sume		}
189861877Sume	}
189961877Sume
190061877Sume	/*
190161877Sume	 * if we have not already tried the name "as is", do that now.
190261877Sume	 * note that we do this regardless of how many dots were in the
190361877Sume	 * name or whether it ends with a dot.
190461877Sume	 */
190561877Sume	if (!tried_as_is && (dots || !(_res.options & RES_NOTLDQUERY))) {
190661877Sume		ret = res_querydomainN(name, NULL, target);
190761877Sume		if (ret > 0)
190861877Sume			return (ret);
190961877Sume	}
191061877Sume
191161877Sume	/*
191261877Sume	 * if we got here, we didn't satisfy the search.
191361877Sume	 * if we did an initial full query, return that query's h_errno
191461877Sume	 * (note that we wouldn't be here if that query had succeeded).
191561877Sume	 * else if we ever got a nodata, send that back as the reason.
191661877Sume	 * else send back meaningless h_errno, that being the one from
191761877Sume	 * the last DNSRCH we did.
191861877Sume	 */
191961877Sume	if (saved_herrno != -1)
192061877Sume		h_errno = saved_herrno;
192161877Sume	else if (got_nodata)
192261877Sume		h_errno = NO_DATA;
192361877Sume	else if (got_servfail)
192461877Sume		h_errno = TRY_AGAIN;
192561877Sume	return (-1);
192661877Sume}
192761877Sume
192861877Sume/*
192961877Sume * Perform a call on res_query on the concatenation of name and domain,
193061877Sume * removing a trailing dot from name if domain is NULL.
193161877Sume */
193261877Sumestatic int
193361877Sumeres_querydomainN(name, domain, target)
193461877Sume	const char *name, *domain;
193561877Sume	struct res_target *target;
193661877Sume{
193761877Sume	char nbuf[MAXDNAME];
193861877Sume	const char *longname = nbuf;
193961877Sume	size_t n, d;
194061877Sume
194161877Sume	if ((_res.options & RES_INIT) == 0 && res_init() == -1) {
194261877Sume		h_errno = NETDB_INTERNAL;
194361877Sume		return (-1);
194461877Sume	}
194561877Sume#ifdef DEBUG
194661877Sume	if (_res.options & RES_DEBUG)
194761877Sume		printf(";; res_querydomain(%s, %s)\n",
194861877Sume			name, domain?domain:"<Nil>");
194961877Sume#endif
195061877Sume	if (domain == NULL) {
195161877Sume		/*
195261877Sume		 * Check for trailing '.';
195361877Sume		 * copy without '.' if present.
195461877Sume		 */
195561877Sume		n = strlen(name);
195661877Sume		if (n >= MAXDNAME) {
195761877Sume			h_errno = NO_RECOVERY;
195861877Sume			return (-1);
195961877Sume		}
196061877Sume		if (n > 0 && name[--n] == '.') {
196161877Sume			strncpy(nbuf, name, n);
196261877Sume			nbuf[n] = '\0';
196361877Sume		} else
196461877Sume			longname = name;
196561877Sume	} else {
196661877Sume		n = strlen(name);
196761877Sume		d = strlen(domain);
196861877Sume		if (n + d + 1 >= MAXDNAME) {
196961877Sume			h_errno = NO_RECOVERY;
197061877Sume			return (-1);
197161877Sume		}
197261877Sume		sprintf(nbuf, "%s.%s", name, domain);
197361877Sume	}
197461877Sume	return (res_queryN(longname, target));
197561877Sume}
1976