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