getaddrinfo.c revision 111618
162836Sitojun/* $KAME: getaddrinfo.c,v 1.15 2000/07/09 04:37:24 itojun Exp $ */ 262614Sitojun 355163Sshin/* 455163Sshin * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. 555163Sshin * All rights reserved. 655163Sshin * 755163Sshin * Redistribution and use in source and binary forms, with or without 855163Sshin * modification, are permitted provided that the following conditions 955163Sshin * are met: 1055163Sshin * 1. Redistributions of source code must retain the above copyright 1155163Sshin * notice, this list of conditions and the following disclaimer. 1255163Sshin * 2. Redistributions in binary form must reproduce the above copyright 1355163Sshin * notice, this list of conditions and the following disclaimer in the 1455163Sshin * documentation and/or other materials provided with the distribution. 1555163Sshin * 3. Neither the name of the project nor the names of its contributors 1655163Sshin * may be used to endorse or promote products derived from this software 1755163Sshin * without specific prior written permission. 1855163Sshin * 1955163Sshin * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 2055163Sshin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2155163Sshin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2255163Sshin * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 2355163Sshin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2455163Sshin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2555163Sshin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2655163Sshin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2755163Sshin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2855163Sshin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2955163Sshin * SUCH DAMAGE. 3055163Sshin */ 3155163Sshin 3255163Sshin/* 3355163Sshin * "#ifdef FAITH" part is local hack for supporting IPv4-v6 translator. 3455163Sshin * 3555163Sshin * Issues to be discussed: 3655163Sshin * - Thread safe-ness must be checked. 3755163Sshin * - Return values. There are nonstandard return values defined and used 3855163Sshin * in the source code. This is because RFC2553 is silent about which error 3955163Sshin * code must be returned for which situation. 4061877Sume * - freeaddrinfo(NULL). RFC2553 is silent about it. XNET 5.2 says it is 41105940Sume * invalid. current code - SEGV on freeaddrinfo(NULL) 42105940Sume * 4356627Sshin * Note: 4456627Sshin * - The code filters out AFs that are not supported by the kernel, 4556627Sshin * when globbing NULL hostname (to loopback, or wildcard). Is it the right 4656627Sshin * thing to do? What is the relationship with post-RFC2553 AI_ADDRCONFIG 4756627Sshin * in ai_flags? 4861877Sume * - (post-2553) semantics of AI_ADDRCONFIG itself is too vague. 4961877Sume * (1) what should we do against numeric hostname (2) what should we do 5061877Sume * against NULL hostname (3) what is AI_ADDRCONFIG itself. AF not ready? 5161877Sume * non-loopback address configured? global address configured? 52105940Sume * 53105940Sume * OS specific notes for netbsd/openbsd/freebsd4/bsdi4: 5461877Sume * - To avoid search order issue, we have a big amount of code duplicate 5561877Sume * from gethnamaddr.c and some other places. The issues that there's no 5661877Sume * lower layer function to lookup "IPv4 or IPv6" record. Calling 5761877Sume * gethostbyname2 from getaddrinfo will end up in wrong search order, as 58105940Sume * presented above. 59105940Sume * 60105940Sume * OS specific notes for freebsd4: 61105940Sume * - FreeBSD supported $GAI. The code does not. 62105940Sume * - FreeBSD allowed classful IPv4 numeric (127.1), the code does not. 6355163Sshin */ 6455163Sshin 6592986Sobrien#include <sys/cdefs.h> 6692986Sobrien__FBSDID("$FreeBSD: head/lib/libc/net/getaddrinfo.c 111618 2003-02-27 13:40:01Z nectar $"); 6792986Sobrien 6871579Sdeischen#include "namespace.h" 6955163Sshin#include <sys/types.h> 7055163Sshin#include <sys/param.h> 7155163Sshin#include <sys/socket.h> 7255163Sshin#include <net/if.h> 7355163Sshin#include <netinet/in.h> 7455163Sshin#include <arpa/inet.h> 7555163Sshin#include <arpa/nameser.h> 7690271Salfred#include <rpc/rpc.h> 7790271Salfred#include <rpcsvc/yp_prot.h> 7890271Salfred#include <rpcsvc/ypclnt.h> 7955163Sshin#include <netdb.h> 8055163Sshin#include <resolv.h> 8155163Sshin#include <string.h> 8255163Sshin#include <stdlib.h> 8355163Sshin#include <stddef.h> 8455163Sshin#include <ctype.h> 8555163Sshin#include <unistd.h> 8655163Sshin#include <stdio.h> 8761877Sume#include <errno.h> 88102237Spirzyk 89102237Spirzyk#include "res_config.h" 90102237Spirzyk 9178012Sume#ifdef DEBUG 9278012Sume#include <syslog.h> 9378012Sume#endif 9455163Sshin 9565532Snectar#include <stdarg.h> 9665532Snectar#include <nsswitch.h> 9771579Sdeischen#include "un-namespace.h" 98111618Snectar#include "libc_private.h" 9965532Snectar 10055163Sshin#if defined(__KAME__) && defined(INET6) 10155163Sshin# define FAITH 10255163Sshin#endif 10355163Sshin 104105940Sume#define SUCCESS 0 105105940Sume#define ANY 0 106105940Sume#define YES 1 107105940Sume#define NO 0 10855163Sshin 10955163Sshinstatic const char in_addrany[] = { 0, 0, 0, 0 }; 110105940Sumestatic const char in_loopback[] = { 127, 0, 0, 1 }; 111105940Sume#ifdef INET6 11255163Sshinstatic const char in6_addrany[] = { 11355163Sshin 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 11455163Sshin}; 11555163Sshinstatic const char in6_loopback[] = { 11655163Sshin 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 11755163Sshin}; 118105940Sume#endif 11955163Sshin 12055163Sshinstatic const struct afd { 12155163Sshin int a_af; 12255163Sshin int a_addrlen; 12355163Sshin int a_socklen; 12455163Sshin int a_off; 12555163Sshin const char *a_addrany; 12673665Sobrien const char *a_loopback; 12755163Sshin int a_scoped; 12855163Sshin} afdl [] = { 12955163Sshin#ifdef INET6 13055163Sshin#define N_INET6 0 13155163Sshin {PF_INET6, sizeof(struct in6_addr), 13255163Sshin sizeof(struct sockaddr_in6), 13355163Sshin offsetof(struct sockaddr_in6, sin6_addr), 13455163Sshin in6_addrany, in6_loopback, 1}, 13555163Sshin#define N_INET 1 13655163Sshin#else 13755163Sshin#define N_INET 0 13855163Sshin#endif 13955163Sshin {PF_INET, sizeof(struct in_addr), 14055163Sshin sizeof(struct sockaddr_in), 14155163Sshin offsetof(struct sockaddr_in, sin_addr), 14255163Sshin in_addrany, in_loopback, 0}, 14355163Sshin {0, 0, 0, 0, NULL, NULL, 0}, 14455163Sshin}; 14555163Sshin 14655163Sshinstruct explore { 14761877Sume int e_af; 14855163Sshin int e_socktype; 14955163Sshin int e_protocol; 15055163Sshin const char *e_protostr; 15155163Sshin int e_wild; 152105940Sume#define WILD_AF(ex) ((ex)->e_wild & 0x01) 153105940Sume#define WILD_SOCKTYPE(ex) ((ex)->e_wild & 0x02) 154105940Sume#define WILD_PROTOCOL(ex) ((ex)->e_wild & 0x04) 15555163Sshin}; 15655163Sshin 15755163Sshinstatic const struct explore explore[] = { 15861877Sume#if 0 15961877Sume { PF_LOCAL, 0, ANY, ANY, NULL, 0x01 }, 16061877Sume#endif 16161877Sume#ifdef INET6 16261877Sume { PF_INET6, SOCK_DGRAM, IPPROTO_UDP, "udp", 0x07 }, 16361877Sume { PF_INET6, SOCK_STREAM, IPPROTO_TCP, "tcp", 0x07 }, 16461877Sume { PF_INET6, SOCK_RAW, ANY, NULL, 0x05 }, 16561877Sume#endif 16661877Sume { PF_INET, SOCK_DGRAM, IPPROTO_UDP, "udp", 0x07 }, 16761877Sume { PF_INET, SOCK_STREAM, IPPROTO_TCP, "tcp", 0x07 }, 16861877Sume { PF_INET, SOCK_RAW, ANY, NULL, 0x05 }, 16961877Sume { PF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, "udp", 0x07 }, 17061877Sume { PF_UNSPEC, SOCK_STREAM, IPPROTO_TCP, "tcp", 0x07 }, 17161877Sume { PF_UNSPEC, SOCK_RAW, ANY, NULL, 0x05 }, 17261877Sume { -1, 0, 0, NULL, 0 }, 17355163Sshin}; 17455163Sshin 17555163Sshin#ifdef INET6 176105940Sume#define PTON_MAX 16 17755163Sshin#else 178105940Sume#define PTON_MAX 4 17955163Sshin#endif 18055163Sshin 18165532Snectarstatic const ns_src default_dns_files[] = { 18265532Snectar { NSSRC_FILES, NS_SUCCESS }, 18365532Snectar { NSSRC_DNS, NS_SUCCESS }, 18465532Snectar { 0 } 18565532Snectar}; 18665532Snectar 187103357Sume#define MAXPACKET (64*1024) 18861877Sume 18961877Sumetypedef union { 19061877Sume HEADER hdr; 19161877Sume u_char buf[MAXPACKET]; 19261877Sume} querybuf; 19361877Sume 19461877Sumestruct res_target { 19561877Sume struct res_target *next; 19661877Sume const char *name; /* domain name */ 19762614Sitojun int qclass, qtype; /* class and type of query */ 19861877Sume u_char *answer; /* buffer to put answer */ 19961877Sume int anslen; /* size of answer buffer */ 20061877Sume int n; /* result length */ 20161877Sume}; 20261877Sume 20392905Sobrienstatic int str_isnumber(const char *); 20492941Sobrienstatic int explore_fqdn(const struct addrinfo *, const char *, 20592941Sobrien const char *, struct addrinfo **); 20692941Sobrienstatic int explore_null(const struct addrinfo *, 20792941Sobrien const char *, struct addrinfo **); 20892941Sobrienstatic int explore_numeric(const struct addrinfo *, const char *, 20992941Sobrien const char *, struct addrinfo **); 21092941Sobrienstatic int explore_numeric_scope(const struct addrinfo *, const char *, 21192941Sobrien const char *, struct addrinfo **); 21292941Sobrienstatic int get_canonname(const struct addrinfo *, 21392941Sobrien struct addrinfo *, const char *); 21492941Sobrienstatic struct addrinfo *get_ai(const struct addrinfo *, 21592941Sobrien const struct afd *, const char *); 21692905Sobrienstatic int get_portmatch(const struct addrinfo *, const char *); 21792905Sobrienstatic int get_port(struct addrinfo *, const char *, int); 21892905Sobrienstatic const struct afd *find_afd(int); 21992905Sobrienstatic int addrconfig(struct addrinfo *); 22061877Sume#ifdef INET6 221105943Sumestatic int ip6_str2scopeid(char *, struct sockaddr_in6 *, u_int32_t *); 22261877Sume#endif 22355163Sshin 22492941Sobrienstatic struct addrinfo *getanswer(const querybuf *, int, const char *, int, 22592941Sobrien const struct addrinfo *); 226105943Sumestatic int _dns_getaddrinfo(void *, void *, va_list); 22792905Sobrienstatic void _sethtent(void); 22892905Sobrienstatic void _endhtent(void); 22992905Sobrienstatic struct addrinfo *_gethtent(const char *, const struct addrinfo *); 23092905Sobrienstatic int _files_getaddrinfo(void *, void *, va_list); 23161877Sume#ifdef YP 23292905Sobrienstatic struct addrinfo *_yphostent(char *, const struct addrinfo *); 23392905Sobrienstatic int _yp_getaddrinfo(void *, void *, va_list); 23461877Sume#endif 23561877Sume 23692905Sobrienstatic int res_queryN(const char *, struct res_target *); 23792905Sobrienstatic int res_searchN(const char *, struct res_target *); 23892941Sobrienstatic int res_querydomainN(const char *, const char *, 23992941Sobrien struct res_target *); 24061877Sume 24155163Sshinstatic char *ai_errlist[] = { 24255163Sshin "Success", 24355163Sshin "Address family for hostname not supported", /* EAI_ADDRFAMILY */ 24455163Sshin "Temporary failure in name resolution", /* EAI_AGAIN */ 24555163Sshin "Invalid value for ai_flags", /* EAI_BADFLAGS */ 24655163Sshin "Non-recoverable failure in name resolution", /* EAI_FAIL */ 24755163Sshin "ai_family not supported", /* EAI_FAMILY */ 24855163Sshin "Memory allocation failure", /* EAI_MEMORY */ 24955163Sshin "No address associated with hostname", /* EAI_NODATA */ 25055163Sshin "hostname nor servname provided, or not known", /* EAI_NONAME */ 25155163Sshin "servname not supported for ai_socktype", /* EAI_SERVICE */ 25255163Sshin "ai_socktype not supported", /* EAI_SOCKTYPE */ 25355163Sshin "System error returned in errno", /* EAI_SYSTEM */ 25455163Sshin "Invalid value for hints", /* EAI_BADHINTS */ 25555163Sshin "Resolved protocol is unknown", /* EAI_PROTOCOL */ 25655163Sshin "Unknown error", /* EAI_MAX */ 25755163Sshin}; 25855163Sshin 259104558Sume/* Make getaddrinfo() thread-safe in libc for use with kernel threads. */ 260104558Sume#include "libc_private.h" 261104558Sume#include "spinlock.h" 262104558Sume/* 263104558Sume * XXX: Our res_*() is not thread-safe. So, we share lock between 264104558Sume * getaddrinfo() and getipnodeby*(). Still, we cannot use 265104558Sume * getaddrinfo() and getipnodeby*() in conjunction with other 266104558Sume * functions which call res_*(). 267104558Sume */ 268104558Sumespinlock_t __getaddrinfo_thread_lock = _SPINLOCK_INITIALIZER; 269104558Sume#define THREAD_LOCK() \ 270104558Sume if (__isthreaded) _SPINLOCK(&__getaddrinfo_thread_lock); 271104558Sume#define THREAD_UNLOCK() \ 272104558Sume if (__isthreaded) _SPINUNLOCK(&__getaddrinfo_thread_lock); 273104558Sume 27455163Sshin/* XXX macros that make external reference is BAD. */ 27555163Sshin 276105940Sume#define GET_AI(ai, afd, addr) \ 27755163Sshindo { \ 27855163Sshin /* external reference: pai, error, and label free */ \ 27955163Sshin (ai) = get_ai(pai, (afd), (addr)); \ 28055163Sshin if ((ai) == NULL) { \ 28155163Sshin error = EAI_MEMORY; \ 28255163Sshin goto free; \ 28355163Sshin } \ 28461877Sume} while (/*CONSTCOND*/0) 28555163Sshin 286105940Sume#define GET_PORT(ai, serv) \ 28755163Sshindo { \ 28855163Sshin /* external reference: error and label free */ \ 28955163Sshin error = get_port((ai), (serv), 0); \ 29055163Sshin if (error != 0) \ 29155163Sshin goto free; \ 29261877Sume} while (/*CONSTCOND*/0) 29355163Sshin 294105940Sume#define GET_CANONNAME(ai, str) \ 29555163Sshindo { \ 29655163Sshin /* external reference: pai, error and label free */ \ 29755163Sshin error = get_canonname(pai, (ai), (str)); \ 29855163Sshin if (error != 0) \ 29955163Sshin goto free; \ 30061877Sume} while (/*CONSTCOND*/0) 30155163Sshin 302105940Sume#define ERR(err) \ 30355163Sshindo { \ 30455163Sshin /* external reference: error, and label bad */ \ 30555163Sshin error = (err); \ 30655163Sshin goto bad; \ 30761877Sume /*NOTREACHED*/ \ 30861877Sume} while (/*CONSTCOND*/0) 30955163Sshin 310105940Sume#define MATCH_FAMILY(x, y, w) \ 31161877Sume ((x) == (y) || (/*CONSTCOND*/(w) && ((x) == PF_UNSPEC || (y) == PF_UNSPEC))) 312105940Sume#define MATCH(x, y, w) \ 31361877Sume ((x) == (y) || (/*CONSTCOND*/(w) && ((x) == ANY || (y) == ANY))) 31455163Sshin 31555163Sshinchar * 31655163Sshingai_strerror(ecode) 31755163Sshin int ecode; 31855163Sshin{ 31955163Sshin if (ecode < 0 || ecode > EAI_MAX) 32055163Sshin ecode = EAI_MAX; 32155163Sshin return ai_errlist[ecode]; 32255163Sshin} 32355163Sshin 32455163Sshinvoid 32555163Sshinfreeaddrinfo(ai) 32655163Sshin struct addrinfo *ai; 32755163Sshin{ 32855163Sshin struct addrinfo *next; 32955163Sshin 33055163Sshin do { 33155163Sshin next = ai->ai_next; 33255163Sshin if (ai->ai_canonname) 33355163Sshin free(ai->ai_canonname); 33455163Sshin /* no need to free(ai->ai_addr) */ 33555163Sshin free(ai); 33661877Sume ai = next; 33761877Sume } while (ai); 33855163Sshin} 33955163Sshin 34055163Sshinstatic int 34155163Sshinstr_isnumber(p) 34255163Sshin const char *p; 34355163Sshin{ 34462836Sitojun char *ep; 34562836Sitojun 34662836Sitojun if (*p == '\0') 34762836Sitojun return NO; 34862836Sitojun ep = NULL; 349105943Sume errno = 0; 35062836Sitojun (void)strtoul(p, &ep, 10); 351105943Sume if (errno == 0 && ep && *ep == '\0') 35262836Sitojun return YES; 35362836Sitojun else 35462836Sitojun return NO; 35555163Sshin} 35655163Sshin 35755163Sshinint 35855163Sshingetaddrinfo(hostname, servname, hints, res) 35955163Sshin const char *hostname, *servname; 36055163Sshin const struct addrinfo *hints; 36155163Sshin struct addrinfo **res; 36255163Sshin{ 36355163Sshin struct addrinfo sentinel; 36455163Sshin struct addrinfo *cur; 36555163Sshin int error = 0; 36655163Sshin struct addrinfo ai; 36755163Sshin struct addrinfo ai0; 36855163Sshin struct addrinfo *pai; 36955163Sshin const struct explore *ex; 37055163Sshin 37161877Sume memset(&sentinel, 0, sizeof(sentinel)); 37255163Sshin cur = &sentinel; 37355163Sshin pai = &ai; 37455163Sshin pai->ai_flags = 0; 37555163Sshin pai->ai_family = PF_UNSPEC; 37655163Sshin pai->ai_socktype = ANY; 37755163Sshin pai->ai_protocol = ANY; 37855163Sshin pai->ai_addrlen = 0; 37955163Sshin pai->ai_canonname = NULL; 38055163Sshin pai->ai_addr = NULL; 38155163Sshin pai->ai_next = NULL; 38255163Sshin 38355163Sshin if (hostname == NULL && servname == NULL) 38455163Sshin return EAI_NONAME; 38555163Sshin if (hints) { 38655163Sshin /* error check for hints */ 38755163Sshin if (hints->ai_addrlen || hints->ai_canonname || 38855163Sshin hints->ai_addr || hints->ai_next) 38955163Sshin ERR(EAI_BADHINTS); /* xxx */ 39055163Sshin if (hints->ai_flags & ~AI_MASK) 39155163Sshin ERR(EAI_BADFLAGS); 39255163Sshin switch (hints->ai_family) { 39355163Sshin case PF_UNSPEC: 39455163Sshin case PF_INET: 39555163Sshin#ifdef INET6 39655163Sshin case PF_INET6: 39755163Sshin#endif 39855163Sshin break; 39955163Sshin default: 40055163Sshin ERR(EAI_FAMILY); 40155163Sshin } 40255163Sshin memcpy(pai, hints, sizeof(*pai)); 40355163Sshin 40455163Sshin /* 40555163Sshin * if both socktype/protocol are specified, check if they 40655163Sshin * are meaningful combination. 40755163Sshin */ 40855163Sshin if (pai->ai_socktype != ANY && pai->ai_protocol != ANY) { 40961877Sume for (ex = explore; ex->e_af >= 0; ex++) { 41061877Sume if (pai->ai_family != ex->e_af) 41161877Sume continue; 41255163Sshin if (ex->e_socktype == ANY) 41355163Sshin continue; 41455163Sshin if (ex->e_protocol == ANY) 41555163Sshin continue; 416105940Sume if (pai->ai_socktype == ex->e_socktype && 417105940Sume pai->ai_protocol != ex->e_protocol) { 41855163Sshin ERR(EAI_BADHINTS); 41961877Sume } 42055163Sshin } 42155163Sshin } 42255163Sshin } 42355163Sshin 42461877Sume /* 42561877Sume * post-2553: AI_ALL and AI_V4MAPPED are effective only against 426105940Sume * AF_INET6 query. They need to be ignored if specified in other 42761877Sume * occassions. 42861877Sume */ 42961877Sume switch (pai->ai_flags & (AI_ALL | AI_V4MAPPED)) { 43061877Sume case AI_V4MAPPED: 43161877Sume case AI_ALL | AI_V4MAPPED: 43261877Sume if (pai->ai_family != AF_INET6) 43361877Sume pai->ai_flags &= ~(AI_ALL | AI_V4MAPPED); 43461877Sume break; 43561877Sume case AI_ALL: 43661877Sume#if 1 43761877Sume /* illegal */ 43861877Sume ERR(EAI_BADFLAGS); 43961877Sume#else 44061877Sume pai->ai_flags &= ~(AI_ALL | AI_V4MAPPED); 44161877Sume#endif 44261877Sume break; 44361877Sume } 44455163Sshin 44555163Sshin /* 44661877Sume * check for special cases. (1) numeric servname is disallowed if 44761877Sume * socktype/protocol are left unspecified. (2) servname is disallowed 44861877Sume * for raw and other inet{,6} sockets. 44955163Sshin */ 45055163Sshin if (MATCH_FAMILY(pai->ai_family, PF_INET, 1) 45161877Sume#ifdef PF_INET6 45273665Sobrien || MATCH_FAMILY(pai->ai_family, PF_INET6, 1) 45355163Sshin#endif 45455163Sshin ) { 45561877Sume ai0 = *pai; /* backup *pai */ 45655163Sshin 45761877Sume if (pai->ai_family == PF_UNSPEC) { 45861877Sume#ifdef PF_INET6 45955163Sshin pai->ai_family = PF_INET6; 46055163Sshin#else 46155163Sshin pai->ai_family = PF_INET; 46255163Sshin#endif 46361877Sume } 46455163Sshin error = get_portmatch(pai, servname); 46555163Sshin if (error) 46655163Sshin ERR(error); 46761877Sume 46861877Sume *pai = ai0; 46955163Sshin } 47055163Sshin 47161877Sume ai0 = *pai; 47261877Sume 47355163Sshin /* NULL hostname, or numeric hostname */ 47461877Sume for (ex = explore; ex->e_af >= 0; ex++) { 47555163Sshin *pai = ai0; 47655163Sshin 47761877Sume /* PF_UNSPEC entries are prepared for DNS queries only */ 47861877Sume if (ex->e_af == PF_UNSPEC) 47955163Sshin continue; 48061877Sume 48161877Sume if (!MATCH_FAMILY(pai->ai_family, ex->e_af, WILD_AF(ex))) 48255163Sshin continue; 48365532Snectar if (!MATCH(pai->ai_socktype, ex->e_socktype, WILD_SOCKTYPE(ex))) 48455163Sshin continue; 48565532Snectar if (!MATCH(pai->ai_protocol, ex->e_protocol, WILD_PROTOCOL(ex))) 48661877Sume continue; 48755163Sshin 48855163Sshin if (pai->ai_family == PF_UNSPEC) 48961877Sume pai->ai_family = ex->e_af; 49055163Sshin if (pai->ai_socktype == ANY && ex->e_socktype != ANY) 49155163Sshin pai->ai_socktype = ex->e_socktype; 49255163Sshin if (pai->ai_protocol == ANY && ex->e_protocol != ANY) 49355163Sshin pai->ai_protocol = ex->e_protocol; 49455163Sshin 49555163Sshin if (hostname == NULL) 49661877Sume error = explore_null(pai, servname, &cur->ai_next); 49755163Sshin else 49865532Snectar error = explore_numeric_scope(pai, hostname, servname, &cur->ai_next); 49955163Sshin 50055163Sshin if (error) 50155163Sshin goto free; 50255163Sshin 50355163Sshin while (cur && cur->ai_next) 50455163Sshin cur = cur->ai_next; 50555163Sshin } 50655163Sshin 50755163Sshin /* 50855163Sshin * XXX 50955163Sshin * If numreic representation of AF1 can be interpreted as FQDN 51055163Sshin * representation of AF2, we need to think again about the code below. 51155163Sshin */ 51255163Sshin if (sentinel.ai_next) 51355163Sshin goto good; 51455163Sshin 51555163Sshin if (pai->ai_flags & AI_NUMERICHOST) 51690053Sroam ERR(EAI_NONAME); 51755163Sshin if (hostname == NULL) 51862614Sitojun ERR(EAI_NODATA); 51955163Sshin 52061877Sume if ((pai->ai_flags & AI_ADDRCONFIG) != 0 && !addrconfig(&ai0)) 52161877Sume ERR(EAI_FAIL); 52255163Sshin 52361877Sume /* 52461877Sume * hostname as alphabetical name. 52561877Sume * we would like to prefer AF_INET6 than AF_INET, so we'll make a 52661877Sume * outer loop by AFs. 52761877Sume */ 52861877Sume for (ex = explore; ex->e_af >= 0; ex++) { 52961877Sume *pai = ai0; 53055163Sshin 53161877Sume /* require exact match for family field */ 53261877Sume if (pai->ai_family != ex->e_af) 53361877Sume continue; 53455163Sshin 53561877Sume if (!MATCH(pai->ai_socktype, ex->e_socktype, 53665532Snectar WILD_SOCKTYPE(ex))) { 53761877Sume continue; 53861877Sume } 53961877Sume if (!MATCH(pai->ai_protocol, ex->e_protocol, 54065532Snectar WILD_PROTOCOL(ex))) { 54161877Sume continue; 54261877Sume } 54355163Sshin 54461877Sume if (pai->ai_socktype == ANY && ex->e_socktype != ANY) 54561877Sume pai->ai_socktype = ex->e_socktype; 54661877Sume if (pai->ai_protocol == ANY && ex->e_protocol != ANY) 54761877Sume pai->ai_protocol = ex->e_protocol; 54861877Sume 54965532Snectar error = explore_fqdn(pai, hostname, servname, 55065532Snectar &cur->ai_next); 55161877Sume 55261877Sume while (cur && cur->ai_next) 55361877Sume cur = cur->ai_next; 55455163Sshin } 55555163Sshin 55661877Sume /* XXX */ 55761877Sume if (sentinel.ai_next) 55861877Sume error = 0; 55961877Sume 56061877Sume if (error) 56161877Sume goto free; 56261877Sume if (error == 0) { 56361877Sume if (sentinel.ai_next) { 56455163Sshin good: 56561877Sume *res = sentinel.ai_next; 56661877Sume return SUCCESS; 56761877Sume } else 56861877Sume error = EAI_FAIL; 56955163Sshin } 57055163Sshin free: 57155163Sshin bad: 57255163Sshin if (sentinel.ai_next) 57355163Sshin freeaddrinfo(sentinel.ai_next); 57455163Sshin *res = NULL; 57555163Sshin return error; 57655163Sshin} 57755163Sshin 57855163Sshin/* 57955163Sshin * FQDN hostname, DNS lookup 58055163Sshin */ 58155163Sshinstatic int 58255163Sshinexplore_fqdn(pai, hostname, servname, res) 58355163Sshin const struct addrinfo *pai; 58455163Sshin const char *hostname; 58555163Sshin const char *servname; 58655163Sshin struct addrinfo **res; 58755163Sshin{ 58861877Sume struct addrinfo *result; 58961877Sume struct addrinfo *cur; 59065532Snectar int error = 0; 59165532Snectar static const ns_dtab dtab[] = { 59265532Snectar NS_FILES_CB(_files_getaddrinfo, NULL) 59365532Snectar { NSSRC_DNS, _dns_getaddrinfo, NULL }, /* force -DHESIOD */ 59465532Snectar NS_NIS_CB(_yp_getaddrinfo, NULL) 59565532Snectar { 0 } 59665532Snectar }; 59755163Sshin 59861877Sume result = NULL; 59955163Sshin 600104558Sume THREAD_LOCK(); 601104558Sume 60255163Sshin /* 60355163Sshin * if the servname does not match socktype/protocol, ignore it. 60455163Sshin */ 605104558Sume if (get_portmatch(pai, servname) != 0) { 606104558Sume THREAD_UNLOCK(); 60755163Sshin return 0; 608104558Sume } 60955163Sshin 61065532Snectar switch (nsdispatch(&result, dtab, NSDB_HOSTS, "getaddrinfo", 61165532Snectar default_dns_files, hostname, pai)) { 61265532Snectar case NS_TRYAGAIN: 61365532Snectar error = EAI_AGAIN; 61465532Snectar goto free; 61565532Snectar case NS_UNAVAIL: 61665532Snectar error = EAI_FAIL; 61765532Snectar goto free; 61865532Snectar case NS_NOTFOUND: 61965532Snectar error = EAI_NODATA; 62065532Snectar goto free; 62165532Snectar case NS_SUCCESS: 62265532Snectar error = 0; 62361877Sume for (cur = result; cur; cur = cur->ai_next) { 62461877Sume GET_PORT(cur, servname); 62561877Sume /* canonname should be filled already */ 62655163Sshin } 62765532Snectar break; 62855163Sshin } 629104558Sume THREAD_UNLOCK(); 63055163Sshin 63165532Snectar *res = result; 63265532Snectar 63365532Snectar return 0; 63465532Snectar 63555163Sshinfree: 636104558Sume THREAD_UNLOCK(); 63761877Sume if (result) 63861877Sume freeaddrinfo(result); 63955163Sshin return error; 64055163Sshin} 64155163Sshin 64255163Sshin/* 64355163Sshin * hostname == NULL. 64455163Sshin * passive socket -> anyaddr (0.0.0.0 or ::) 64555163Sshin * non-passive socket -> localhost (127.0.0.1 or ::1) 64655163Sshin */ 64755163Sshinstatic int 64861877Sumeexplore_null(pai, servname, res) 64955163Sshin const struct addrinfo *pai; 65055163Sshin const char *servname; 65155163Sshin struct addrinfo **res; 65255163Sshin{ 65355163Sshin int s; 65455163Sshin const struct afd *afd; 65555163Sshin struct addrinfo *cur; 65655163Sshin struct addrinfo sentinel; 65755163Sshin int error; 65855163Sshin 65955163Sshin *res = NULL; 66055163Sshin sentinel.ai_next = NULL; 66155163Sshin cur = &sentinel; 66255163Sshin 66355163Sshin /* 66455163Sshin * filter out AFs that are not supported by the kernel 66555163Sshin * XXX errno? 66655163Sshin */ 66771579Sdeischen s = _socket(pai->ai_family, SOCK_DGRAM, 0); 66861877Sume if (s < 0) { 66961877Sume if (errno != EMFILE) 67061877Sume return 0; 67161877Sume } else 67261877Sume _close(s); 67361877Sume 67461877Sume /* 67561877Sume * if the servname does not match socktype/protocol, ignore it. 67661877Sume */ 67761877Sume if (get_portmatch(pai, servname) != 0) 67855163Sshin return 0; 67961877Sume 68055163Sshin afd = find_afd(pai->ai_family); 68155163Sshin if (afd == NULL) 68255163Sshin return 0; 68355163Sshin 68461877Sume if (pai->ai_flags & AI_PASSIVE) { 68561877Sume GET_AI(cur->ai_next, afd, afd->a_addrany); 68661877Sume /* xxx meaningless? 68761877Sume * GET_CANONNAME(cur->ai_next, "anyaddr"); 68861877Sume */ 68961877Sume GET_PORT(cur->ai_next, servname); 69061877Sume } else { 69161877Sume GET_AI(cur->ai_next, afd, afd->a_loopback); 69261877Sume /* xxx meaningless? 69361877Sume * GET_CANONNAME(cur->ai_next, "localhost"); 69461877Sume */ 69561877Sume GET_PORT(cur->ai_next, servname); 69661877Sume } 69761877Sume cur = cur->ai_next; 69855163Sshin 69955163Sshin *res = sentinel.ai_next; 70055163Sshin return 0; 70155163Sshin 70255163Sshinfree: 70355163Sshin if (sentinel.ai_next) 70455163Sshin freeaddrinfo(sentinel.ai_next); 70555163Sshin return error; 70655163Sshin} 70755163Sshin 70855163Sshin/* 70955163Sshin * numeric hostname 71055163Sshin */ 71155163Sshinstatic int 71255163Sshinexplore_numeric(pai, hostname, servname, res) 71355163Sshin const struct addrinfo *pai; 71455163Sshin const char *hostname; 71555163Sshin const char *servname; 71655163Sshin struct addrinfo **res; 71755163Sshin{ 71855163Sshin const struct afd *afd; 71955163Sshin struct addrinfo *cur; 72055163Sshin struct addrinfo sentinel; 72155163Sshin int error; 72255163Sshin char pton[PTON_MAX]; 72355163Sshin 72455163Sshin *res = NULL; 72555163Sshin sentinel.ai_next = NULL; 72655163Sshin cur = &sentinel; 72755163Sshin 72855163Sshin /* 72955163Sshin * if the servname does not match socktype/protocol, ignore it. 73055163Sshin */ 73155163Sshin if (get_portmatch(pai, servname) != 0) 73255163Sshin return 0; 73355163Sshin 73455163Sshin afd = find_afd(pai->ai_family); 73555163Sshin if (afd == NULL) 73655163Sshin return 0; 73755163Sshin 73862614Sitojun switch (afd->a_af) { 73962614Sitojun#if 1 /*X/Open spec*/ 74062614Sitojun case AF_INET: 74162614Sitojun if (inet_aton(hostname, (struct in_addr *)pton) == 1) { 74262614Sitojun if (pai->ai_family == afd->a_af || 74362614Sitojun pai->ai_family == PF_UNSPEC /*?*/) { 74462614Sitojun GET_AI(cur->ai_next, afd, pton); 74562614Sitojun GET_PORT(cur->ai_next, servname); 74662614Sitojun while (cur && cur->ai_next) 74762614Sitojun cur = cur->ai_next; 74862614Sitojun } else 74962614Sitojun ERR(EAI_FAMILY); /*xxx*/ 75062614Sitojun } 75162614Sitojun break; 75262614Sitojun#endif 75362614Sitojun default: 75462614Sitojun if (inet_pton(afd->a_af, hostname, pton) == 1) { 75562614Sitojun if (pai->ai_family == afd->a_af || 75662614Sitojun pai->ai_family == PF_UNSPEC /*?*/) { 75762614Sitojun GET_AI(cur->ai_next, afd, pton); 75862614Sitojun GET_PORT(cur->ai_next, servname); 75962614Sitojun while (cur && cur->ai_next) 76062614Sitojun cur = cur->ai_next; 76162614Sitojun } else 762105940Sume ERR(EAI_FAMILY); /* XXX */ 76362614Sitojun } 76462614Sitojun break; 76555163Sshin } 76655163Sshin 76755163Sshin *res = sentinel.ai_next; 76855163Sshin return 0; 76955163Sshin 77055163Sshinfree: 77155163Sshinbad: 77255163Sshin if (sentinel.ai_next) 77355163Sshin freeaddrinfo(sentinel.ai_next); 77455163Sshin return error; 77555163Sshin} 77655163Sshin 77755163Sshin/* 77855163Sshin * numeric hostname with scope 77955163Sshin */ 78055163Sshinstatic int 78155163Sshinexplore_numeric_scope(pai, hostname, servname, res) 78255163Sshin const struct addrinfo *pai; 78355163Sshin const char *hostname; 78455163Sshin const char *servname; 78555163Sshin struct addrinfo **res; 78655163Sshin{ 78761877Sume#if !defined(SCOPE_DELIMITER) || !defined(INET6) 78855163Sshin return explore_numeric(pai, hostname, servname, res); 78955163Sshin#else 79055163Sshin const struct afd *afd; 79155163Sshin struct addrinfo *cur; 79255163Sshin int error; 79361877Sume char *cp, *hostname2 = NULL, *scope, *addr; 79455163Sshin struct sockaddr_in6 *sin6; 79555163Sshin 79655163Sshin /* 79755163Sshin * if the servname does not match socktype/protocol, ignore it. 79855163Sshin */ 79955163Sshin if (get_portmatch(pai, servname) != 0) 80055163Sshin return 0; 80155163Sshin 80255163Sshin afd = find_afd(pai->ai_family); 80355163Sshin if (afd == NULL) 80455163Sshin return 0; 80565532Snectar 80655163Sshin if (!afd->a_scoped) 80755163Sshin return explore_numeric(pai, hostname, servname, res); 80855163Sshin 80955163Sshin cp = strchr(hostname, SCOPE_DELIMITER); 81055163Sshin if (cp == NULL) 81155163Sshin return explore_numeric(pai, hostname, servname, res); 81255163Sshin 81355163Sshin /* 81455163Sshin * Handle special case of <scoped_address><delimiter><scope id> 81555163Sshin */ 81655163Sshin hostname2 = strdup(hostname); 81755163Sshin if (hostname2 == NULL) 81855163Sshin return EAI_MEMORY; 81955163Sshin /* terminate at the delimiter */ 82055163Sshin hostname2[cp - hostname] = '\0'; 82161877Sume addr = hostname2; 82261877Sume scope = cp + 1; 82355163Sshin 82461877Sume error = explore_numeric(pai, addr, servname, res); 82561877Sume if (error == 0) { 826105943Sume u_int32_t scopeid; 82755163Sshin 82855163Sshin for (cur = *res; cur; cur = cur->ai_next) { 82955163Sshin if (cur->ai_family != AF_INET6) 83055163Sshin continue; 83161877Sume sin6 = (struct sockaddr_in6 *)(void *)cur->ai_addr; 832105943Sume if (ip6_str2scopeid(scope, sin6, &scopeid) == -1) { 83361877Sume free(hostname2); 83462614Sitojun return(EAI_NODATA); /* XXX: is return OK? */ 83561877Sume } 83661877Sume sin6->sin6_scope_id = scopeid; 83755163Sshin } 83855163Sshin } 83955163Sshin 84055163Sshin free(hostname2); 84155163Sshin 84255163Sshin return error; 84355163Sshin#endif 84455163Sshin} 84555163Sshin 84655163Sshinstatic int 84755163Sshinget_canonname(pai, ai, str) 84855163Sshin const struct addrinfo *pai; 84955163Sshin struct addrinfo *ai; 85055163Sshin const char *str; 85155163Sshin{ 85255163Sshin if ((pai->ai_flags & AI_CANONNAME) != 0) { 85355163Sshin ai->ai_canonname = (char *)malloc(strlen(str) + 1); 85455163Sshin if (ai->ai_canonname == NULL) 85555163Sshin return EAI_MEMORY; 856105940Sume strlcpy(ai->ai_canonname, str, strlen(str) + 1); 85755163Sshin } 85855163Sshin return 0; 85955163Sshin} 86055163Sshin 86155163Sshinstatic struct addrinfo * 86255163Sshinget_ai(pai, afd, addr) 86355163Sshin const struct addrinfo *pai; 86455163Sshin const struct afd *afd; 86555163Sshin const char *addr; 86655163Sshin{ 86755163Sshin char *p; 86855163Sshin struct addrinfo *ai; 86955163Sshin#ifdef FAITH 87055163Sshin struct in6_addr faith_prefix; 87155163Sshin char *fp_str; 87255163Sshin int translate = 0; 87355163Sshin#endif 87455163Sshin 87555163Sshin#ifdef FAITH 87655163Sshin /* 87755163Sshin * Transfrom an IPv4 addr into a special IPv6 addr format for 87855163Sshin * IPv6->IPv4 translation gateway. (only TCP is supported now) 87955163Sshin * 88055163Sshin * +-----------------------------------+------------+ 88155163Sshin * | faith prefix part (12 bytes) | embedded | 88255163Sshin * | | IPv4 addr part (4 bytes) 88355163Sshin * +-----------------------------------+------------+ 88455163Sshin * 88555163Sshin * faith prefix part is specified as ascii IPv6 addr format 88655163Sshin * in environmental variable GAI. 88755163Sshin * For FAITH to work correctly, routing to faith prefix must be 88855163Sshin * setup toward a machine where a FAITH daemon operates. 88955163Sshin * Also, the machine must enable some mechanizm 89055163Sshin * (e.g. faith interface hack) to divert those packet with 89155163Sshin * faith prefixed destination addr to user-land FAITH daemon. 89255163Sshin */ 89355163Sshin fp_str = getenv("GAI"); 89455163Sshin if (fp_str && inet_pton(AF_INET6, fp_str, &faith_prefix) == 1 && 89555163Sshin afd->a_af == AF_INET && pai->ai_socktype == SOCK_STREAM) { 89655163Sshin u_int32_t v4a; 89755163Sshin u_int8_t v4a_top; 89855163Sshin 89955163Sshin memcpy(&v4a, addr, sizeof v4a); 90055163Sshin v4a_top = v4a >> IN_CLASSA_NSHIFT; 90155163Sshin if (!IN_MULTICAST(v4a) && !IN_EXPERIMENTAL(v4a) && 90255163Sshin v4a_top != 0 && v4a != IN_LOOPBACKNET) { 90355163Sshin afd = &afdl[N_INET6]; 90455163Sshin memcpy(&faith_prefix.s6_addr[12], addr, 90555163Sshin sizeof(struct in_addr)); 90655163Sshin translate = 1; 90755163Sshin } 90855163Sshin } 90955163Sshin#endif 91055163Sshin 91155163Sshin ai = (struct addrinfo *)malloc(sizeof(struct addrinfo) 91255163Sshin + (afd->a_socklen)); 91355163Sshin if (ai == NULL) 91455163Sshin return NULL; 91555163Sshin 91655163Sshin memcpy(ai, pai, sizeof(struct addrinfo)); 91761877Sume ai->ai_addr = (struct sockaddr *)(void *)(ai + 1); 91861877Sume memset(ai->ai_addr, 0, (size_t)afd->a_socklen); 91955163Sshin ai->ai_addr->sa_len = afd->a_socklen; 92055163Sshin ai->ai_addrlen = afd->a_socklen; 92155163Sshin ai->ai_addr->sa_family = ai->ai_family = afd->a_af; 92265532Snectar p = (char *)(void *)(ai->ai_addr); 92355163Sshin#ifdef FAITH 92455163Sshin if (translate == 1) 92565532Snectar memcpy(p + afd->a_off, &faith_prefix, (size_t)afd->a_addrlen); 92655163Sshin else 92755163Sshin#endif 92865532Snectar memcpy(p + afd->a_off, addr, (size_t)afd->a_addrlen); 92955163Sshin return ai; 93055163Sshin} 93155163Sshin 93255163Sshinstatic int 93355163Sshinget_portmatch(ai, servname) 93455163Sshin const struct addrinfo *ai; 93555163Sshin const char *servname; 93655163Sshin{ 93761877Sume 93855163Sshin /* get_port does not touch first argument. when matchonly == 1. */ 93961877Sume /* LINTED const cast */ 94055163Sshin return get_port((struct addrinfo *)ai, servname, 1); 94155163Sshin} 94255163Sshin 94355163Sshinstatic int 94455163Sshinget_port(ai, servname, matchonly) 94555163Sshin struct addrinfo *ai; 94655163Sshin const char *servname; 94755163Sshin int matchonly; 94855163Sshin{ 94955163Sshin const char *proto; 95055163Sshin struct servent *sp; 95155163Sshin int port; 95255163Sshin int allownumeric; 95355163Sshin 95455163Sshin if (servname == NULL) 95555163Sshin return 0; 95661877Sume switch (ai->ai_family) { 95761877Sume case AF_INET: 95861877Sume#ifdef AF_INET6 95961877Sume case AF_INET6: 96055163Sshin#endif 96161877Sume break; 96261877Sume default: 96355163Sshin return 0; 96461877Sume } 96555163Sshin 96655163Sshin switch (ai->ai_socktype) { 96755163Sshin case SOCK_RAW: 96855163Sshin return EAI_SERVICE; 96955163Sshin case SOCK_DGRAM: 97055163Sshin case SOCK_STREAM: 97155163Sshin allownumeric = 1; 97255163Sshin break; 97355163Sshin case ANY: 97455163Sshin allownumeric = 0; 97555163Sshin break; 97655163Sshin default: 97755163Sshin return EAI_SOCKTYPE; 97855163Sshin } 97955163Sshin 98055163Sshin if (str_isnumber(servname)) { 98155163Sshin if (!allownumeric) 98255163Sshin return EAI_SERVICE; 983105940Sume port = atoi(servname); 98455163Sshin if (port < 0 || port > 65535) 98555163Sshin return EAI_SERVICE; 986105940Sume port = htons(port); 98755163Sshin } else { 98855163Sshin switch (ai->ai_socktype) { 98955163Sshin case SOCK_DGRAM: 99055163Sshin proto = "udp"; 99155163Sshin break; 99255163Sshin case SOCK_STREAM: 99355163Sshin proto = "tcp"; 99455163Sshin break; 99555163Sshin default: 99655163Sshin proto = NULL; 99755163Sshin break; 99855163Sshin } 99955163Sshin 100055163Sshin if ((sp = getservbyname(servname, proto)) == NULL) 100155163Sshin return EAI_SERVICE; 100255163Sshin port = sp->s_port; 100355163Sshin } 100455163Sshin 100555163Sshin if (!matchonly) { 100655163Sshin switch (ai->ai_family) { 100755163Sshin case AF_INET: 100861877Sume ((struct sockaddr_in *)(void *) 100961877Sume ai->ai_addr)->sin_port = port; 101055163Sshin break; 101155163Sshin#ifdef INET6 101255163Sshin case AF_INET6: 101361877Sume ((struct sockaddr_in6 *)(void *) 101461877Sume ai->ai_addr)->sin6_port = port; 101555163Sshin break; 101655163Sshin#endif 101755163Sshin } 101855163Sshin } 101955163Sshin 102055163Sshin return 0; 102155163Sshin} 102255163Sshin 102355163Sshinstatic const struct afd * 102455163Sshinfind_afd(af) 102555163Sshin int af; 102655163Sshin{ 102755163Sshin const struct afd *afd; 102855163Sshin 102955163Sshin if (af == PF_UNSPEC) 103055163Sshin return NULL; 103155163Sshin for (afd = afdl; afd->a_af; afd++) { 103255163Sshin if (afd->a_af == af) 103355163Sshin return afd; 103455163Sshin } 103555163Sshin return NULL; 103655163Sshin} 103761877Sume 103861877Sume/* 103961877Sume * post-2553: AI_ADDRCONFIG check. if we use getipnodeby* as backend, backend 104061877Sume * will take care of it. 104161877Sume * the semantics of AI_ADDRCONFIG is not defined well. we are not sure 104261877Sume * if the code is right or not. 104361877Sume * 104461877Sume * XXX PF_UNSPEC -> PF_INET6 + PF_INET mapping needs to be in sync with 104561877Sume * _dns_getaddrinfo. 104661877Sume */ 104761877Sumestatic int 104861877Sumeaddrconfig(pai) 104961877Sume struct addrinfo *pai; 105061877Sume{ 105161877Sume int s, af; 105261877Sume 105361877Sume /* 105461877Sume * TODO: 105561877Sume * Note that implementation dependent test for address 105661877Sume * configuration should be done everytime called 105761877Sume * (or apropriate interval), 105861877Sume * because addresses will be dynamically assigned or deleted. 105961877Sume */ 106061877Sume af = pai->ai_family; 106161877Sume if (af == AF_UNSPEC) { 106271579Sdeischen if ((s = _socket(AF_INET6, SOCK_DGRAM, 0)) < 0) 106361877Sume af = AF_INET; 106461877Sume else { 106563704Sjasone _close(s); 106671579Sdeischen if ((s = _socket(AF_INET, SOCK_DGRAM, 0)) < 0) 106761877Sume af = AF_INET6; 106861877Sume else 106963704Sjasone _close(s); 107061877Sume } 107161877Sume } 107261877Sume if (af != AF_UNSPEC) { 107371579Sdeischen if ((s = _socket(af, SOCK_DGRAM, 0)) < 0) 107461877Sume return 0; 107563704Sjasone _close(s); 107661877Sume } 107761877Sume pai->ai_family = af; 107861877Sume return 1; 107961877Sume} 108061877Sume 108161877Sume#ifdef INET6 108261877Sume/* convert a string to a scope identifier. XXX: IPv6 specific */ 108361877Sumestatic int 1084105943Sumeip6_str2scopeid(scope, sin6, scopeid) 108561877Sume char *scope; 108661877Sume struct sockaddr_in6 *sin6; 1087105943Sume u_int32_t *scopeid; 108861877Sume{ 1089105943Sume u_long lscopeid; 1090105943Sume struct in6_addr *a6; 109161877Sume char *ep; 109261877Sume 1093105943Sume a6 = &sin6->sin6_addr; 1094105943Sume 109562836Sitojun /* empty scopeid portion is invalid */ 109662836Sitojun if (*scope == '\0') 109762836Sitojun return -1; 109862836Sitojun 109961877Sume if (IN6_IS_ADDR_LINKLOCAL(a6) || IN6_IS_ADDR_MC_LINKLOCAL(a6)) { 110061877Sume /* 110161877Sume * We currently assume a one-to-one mapping between links 110261877Sume * and interfaces, so we simply use interface indices for 110361877Sume * like-local scopes. 110461877Sume */ 1105105943Sume *scopeid = if_nametoindex(scope); 1106105943Sume if (*scopeid == 0) 110761877Sume goto trynumeric; 1108105943Sume return 0; 110961877Sume } 111061877Sume 111161877Sume /* still unclear about literal, allow numeric only - placeholder */ 111261877Sume if (IN6_IS_ADDR_SITELOCAL(a6) || IN6_IS_ADDR_MC_SITELOCAL(a6)) 111361877Sume goto trynumeric; 111461877Sume if (IN6_IS_ADDR_MC_ORGLOCAL(a6)) 111561877Sume goto trynumeric; 111661877Sume else 111761877Sume goto trynumeric; /* global */ 111861877Sume 111961877Sume /* try to convert to a numeric id as a last resort */ 112061877Sume trynumeric: 1121105943Sume errno = 0; 1122105943Sume lscopeid = strtoul(scope, &ep, 10); 1123105943Sume *scopeid = (u_int32_t)(lscopeid & 0xffffffffUL); 1124105943Sume if (errno == 0 && ep && *ep == '\0' && *scopeid == lscopeid) 1125105943Sume return 0; 112661877Sume else 112761877Sume return -1; 112861877Sume} 112961877Sume#endif 113061877Sume 1131102237Spirzyk#ifdef RESOLVSORT 1132102237Spirzykstruct addr_ptr { 1133102237Spirzyk struct addrinfo *ai; 1134102237Spirzyk int aval; 1135102237Spirzyk}; 1136102237Spirzyk 1137102237Spirzykstatic int 1138102237Spirzykaddr4sort(struct addrinfo *sentinel) 1139102237Spirzyk{ 1140102237Spirzyk struct addrinfo *ai; 1141102237Spirzyk struct addr_ptr *addrs, addr; 1142102237Spirzyk struct sockaddr_in *sin; 1143102237Spirzyk int naddrs, i, j; 1144102237Spirzyk int needsort = 0; 1145102237Spirzyk 1146102237Spirzyk if (!sentinel) 1147102237Spirzyk return -1; 1148102237Spirzyk naddrs = 0; 1149102237Spirzyk for (ai = sentinel->ai_next; ai; ai = ai->ai_next) 1150102237Spirzyk naddrs++; 1151102237Spirzyk if (naddrs < 2) 1152102237Spirzyk return 0; /* We don't need sorting. */ 1153102237Spirzyk if ((addrs = malloc(sizeof(struct addr_ptr) * naddrs)) == NULL) 1154102237Spirzyk return -1; 1155102237Spirzyk i = 0; 1156102237Spirzyk for (ai = sentinel->ai_next; ai; ai = ai->ai_next) { 1157102237Spirzyk sin = (struct sockaddr_in *)ai->ai_addr; 1158102237Spirzyk for (j = 0; (unsigned)j < _res.nsort; j++) { 1159102237Spirzyk if (_res.sort_list[j].addr.s_addr == 1160102237Spirzyk (sin->sin_addr.s_addr & _res.sort_list[j].mask)) 1161102237Spirzyk break; 1162102237Spirzyk } 1163102237Spirzyk addrs[i].ai = ai; 1164102237Spirzyk addrs[i].aval = j; 1165102237Spirzyk if (needsort == 0 && i > 0 && j < addrs[i - 1].aval) 1166102237Spirzyk needsort = i; 1167102237Spirzyk i++; 1168102237Spirzyk } 1169102237Spirzyk if (!needsort) { 1170102237Spirzyk free(addrs); 1171102237Spirzyk return 0; 1172102237Spirzyk } 1173102237Spirzyk 1174102237Spirzyk while (needsort < naddrs) { 1175102237Spirzyk for (j = needsort - 1; j >= 0; j--) { 1176102237Spirzyk if (addrs[j].aval > addrs[j+1].aval) { 1177102237Spirzyk addr = addrs[j]; 1178102237Spirzyk addrs[j] = addrs[j + 1]; 1179102237Spirzyk addrs[j + 1] = addr; 1180102237Spirzyk } else 1181102237Spirzyk break; 1182102237Spirzyk } 1183102237Spirzyk needsort++; 1184102237Spirzyk } 1185102237Spirzyk 1186102237Spirzyk ai = sentinel; 1187102237Spirzyk for (i = 0; i < naddrs; ++i) { 1188102237Spirzyk ai->ai_next = addrs[i].ai; 1189102237Spirzyk ai = ai->ai_next; 1190102237Spirzyk } 1191102237Spirzyk ai->ai_next = NULL; 1192102237Spirzyk free(addrs); 1193102237Spirzyk return 0; 1194102237Spirzyk} 1195102237Spirzyk#endif /*RESOLVSORT*/ 1196102237Spirzyk 119761877Sume#ifdef DEBUG 119861877Sumestatic const char AskedForGot[] = 119961877Sume "gethostby*.getanswer: asked for \"%s\", got \"%s\""; 120061877Sume#endif 120165532Snectarstatic FILE *hostf = NULL; 120261877Sume 120361877Sumestatic struct addrinfo * 120461877Sumegetanswer(answer, anslen, qname, qtype, pai) 120561877Sume const querybuf *answer; 120661877Sume int anslen; 120761877Sume const char *qname; 120861877Sume int qtype; 120961877Sume const struct addrinfo *pai; 121061877Sume{ 121161877Sume struct addrinfo sentinel, *cur; 121261877Sume struct addrinfo ai; 121361877Sume const struct afd *afd; 121461877Sume char *canonname; 121561877Sume const HEADER *hp; 121661877Sume const u_char *cp; 121761877Sume int n; 121861877Sume const u_char *eom; 1219105940Sume char *bp, *ep; 1220105940Sume int type, class, ancount, qdcount; 122161877Sume int haveanswer, had_error; 122261877Sume char tbuf[MAXDNAME]; 122392905Sobrien int (*name_ok)(const char *); 122461877Sume char hostbuf[8*1024]; 122561877Sume 122661877Sume memset(&sentinel, 0, sizeof(sentinel)); 122761877Sume cur = &sentinel; 122861877Sume 122961877Sume canonname = NULL; 123061877Sume eom = answer->buf + anslen; 123161877Sume switch (qtype) { 123261877Sume case T_A: 123361877Sume case T_AAAA: 123461877Sume case T_ANY: /*use T_ANY only for T_A/T_AAAA lookup*/ 123561877Sume name_ok = res_hnok; 123661877Sume break; 123761877Sume default: 123861877Sume return (NULL); /* XXX should be abort(); */ 123961877Sume } 124061877Sume /* 124161877Sume * find first satisfactory answer 124261877Sume */ 124361877Sume hp = &answer->hdr; 124461877Sume ancount = ntohs(hp->ancount); 124561877Sume qdcount = ntohs(hp->qdcount); 124661877Sume bp = hostbuf; 1247105940Sume ep = hostbuf + sizeof hostbuf; 124861877Sume cp = answer->buf + HFIXEDSZ; 124961877Sume if (qdcount != 1) { 125061877Sume h_errno = NO_RECOVERY; 125161877Sume return (NULL); 125261877Sume } 1253105940Sume n = dn_expand(answer->buf, eom, cp, bp, ep - bp); 125461877Sume if ((n < 0) || !(*name_ok)(bp)) { 125561877Sume h_errno = NO_RECOVERY; 125661877Sume return (NULL); 125761877Sume } 125861877Sume cp += n + QFIXEDSZ; 125961877Sume if (qtype == T_A || qtype == T_AAAA || qtype == T_ANY) { 126061877Sume /* res_send() has already verified that the query name is the 126161877Sume * same as the one we sent; this just gets the expanded name 126261877Sume * (i.e., with the succeeding search-domain tacked on). 126361877Sume */ 126461877Sume n = strlen(bp) + 1; /* for the \0 */ 126561877Sume if (n >= MAXHOSTNAMELEN) { 126661877Sume h_errno = NO_RECOVERY; 126761877Sume return (NULL); 126861877Sume } 126961877Sume canonname = bp; 127061877Sume bp += n; 127161877Sume /* The qname can be abbreviated, but h_name is now absolute. */ 127261877Sume qname = canonname; 127361877Sume } 127461877Sume haveanswer = 0; 127561877Sume had_error = 0; 127661877Sume while (ancount-- > 0 && cp < eom && !had_error) { 1277105940Sume n = dn_expand(answer->buf, eom, cp, bp, ep - bp); 127861877Sume if ((n < 0) || !(*name_ok)(bp)) { 127961877Sume had_error++; 128061877Sume continue; 128161877Sume } 128261877Sume cp += n; /* name */ 128361877Sume type = _getshort(cp); 128461877Sume cp += INT16SZ; /* type */ 128561877Sume class = _getshort(cp); 128661877Sume cp += INT16SZ + INT32SZ; /* class, TTL */ 128761877Sume n = _getshort(cp); 128861877Sume cp += INT16SZ; /* len */ 128961877Sume if (class != C_IN) { 129061877Sume /* XXX - debug? syslog? */ 129161877Sume cp += n; 129261877Sume continue; /* XXX - had_error++ ? */ 129361877Sume } 129461877Sume if ((qtype == T_A || qtype == T_AAAA || qtype == T_ANY) && 129561877Sume type == T_CNAME) { 129661877Sume n = dn_expand(answer->buf, eom, cp, tbuf, sizeof tbuf); 129761877Sume if ((n < 0) || !(*name_ok)(tbuf)) { 129861877Sume had_error++; 129961877Sume continue; 130061877Sume } 130161877Sume cp += n; 130261877Sume /* Get canonical name. */ 130361877Sume n = strlen(tbuf) + 1; /* for the \0 */ 1304105940Sume if (n > ep - bp || n >= MAXHOSTNAMELEN) { 130561877Sume had_error++; 130661877Sume continue; 130761877Sume } 1308105940Sume strlcpy(bp, tbuf, ep - bp); 130961877Sume canonname = bp; 131061877Sume bp += n; 131161877Sume continue; 131261877Sume } 131361877Sume if (qtype == T_ANY) { 131461877Sume if (!(type == T_A || type == T_AAAA)) { 131561877Sume cp += n; 131661877Sume continue; 131761877Sume } 131861877Sume } else if (type != qtype) { 131961877Sume#ifdef DEBUG 132061877Sume if (type != T_KEY && type != T_SIG) 132161877Sume syslog(LOG_NOTICE|LOG_AUTH, 132261877Sume "gethostby*.getanswer: asked for \"%s %s %s\", got type \"%s\"", 132361877Sume qname, p_class(C_IN), p_type(qtype), 132461877Sume p_type(type)); 132561877Sume#endif 132661877Sume cp += n; 132761877Sume continue; /* XXX - had_error++ ? */ 132861877Sume } 132961877Sume switch (type) { 133061877Sume case T_A: 133161877Sume case T_AAAA: 133261877Sume if (strcasecmp(canonname, bp) != 0) { 133361877Sume#ifdef DEBUG 133461877Sume syslog(LOG_NOTICE|LOG_AUTH, 133561877Sume AskedForGot, canonname, bp); 133661877Sume#endif 133761877Sume cp += n; 133861877Sume continue; /* XXX - had_error++ ? */ 133961877Sume } 134061877Sume if (type == T_A && n != INADDRSZ) { 134161877Sume cp += n; 134261877Sume continue; 134361877Sume } 134461877Sume if (type == T_AAAA && n != IN6ADDRSZ) { 134561877Sume cp += n; 134661877Sume continue; 134761877Sume } 134861877Sume#ifdef FILTER_V4MAPPED 134961877Sume if (type == T_AAAA) { 135061877Sume struct in6_addr in6; 135161877Sume memcpy(&in6, cp, sizeof(in6)); 135261877Sume if (IN6_IS_ADDR_V4MAPPED(&in6)) { 135361877Sume cp += n; 135461877Sume continue; 135561877Sume } 135661877Sume } 135761877Sume#endif 135861877Sume if (!haveanswer) { 135961877Sume int nn; 136061877Sume 136161877Sume canonname = bp; 136261877Sume nn = strlen(bp) + 1; /* for the \0 */ 136361877Sume bp += nn; 136461877Sume } 136561877Sume 136661877Sume /* don't overwrite pai */ 136761877Sume ai = *pai; 136861877Sume ai.ai_family = (type == T_A) ? AF_INET : AF_INET6; 136961877Sume afd = find_afd(ai.ai_family); 137061877Sume if (afd == NULL) { 137161877Sume cp += n; 137261877Sume continue; 137361877Sume } 137461877Sume cur->ai_next = get_ai(&ai, afd, (const char *)cp); 137561877Sume if (cur->ai_next == NULL) 137661877Sume had_error++; 137761877Sume while (cur && cur->ai_next) 137861877Sume cur = cur->ai_next; 137961877Sume cp += n; 138061877Sume break; 138161877Sume default: 138261877Sume abort(); 138361877Sume } 138461877Sume if (!had_error) 138561877Sume haveanswer++; 138661877Sume } 138761877Sume if (haveanswer) { 1388102237Spirzyk#if defined(RESOLVSORT) 1389102237Spirzyk /* 1390102237Spirzyk * We support only IPv4 address for backward 1391102237Spirzyk * compatibility against gethostbyname(3). 1392102237Spirzyk */ 1393102237Spirzyk if (_res.nsort && qtype == T_A) { 1394102237Spirzyk if (addr4sort(&sentinel) < 0) { 1395102237Spirzyk freeaddrinfo(sentinel.ai_next); 1396102237Spirzyk h_errno = NO_RECOVERY; 1397102237Spirzyk return NULL; 1398102237Spirzyk } 1399102237Spirzyk } 1400102237Spirzyk#endif /*RESOLVSORT*/ 140161877Sume if (!canonname) 140261877Sume (void)get_canonname(pai, sentinel.ai_next, qname); 140361877Sume else 140461877Sume (void)get_canonname(pai, sentinel.ai_next, canonname); 140561877Sume h_errno = NETDB_SUCCESS; 140661877Sume return sentinel.ai_next; 140761877Sume } 140861877Sume 140961877Sume h_errno = NO_RECOVERY; 141061877Sume return NULL; 141161877Sume} 141261877Sume 141361877Sume/*ARGSUSED*/ 141461877Sumestatic int 141565532Snectar_dns_getaddrinfo(rv, cb_data, ap) 141665532Snectar void *rv; 141765532Snectar void *cb_data; 141865532Snectar va_list ap; 141961877Sume{ 142061877Sume struct addrinfo *ai; 1421103357Sume querybuf *buf, *buf2; 142261877Sume const char *name; 142365532Snectar const struct addrinfo *pai; 142461877Sume struct addrinfo sentinel, *cur; 142561877Sume struct res_target q, q2; 142661877Sume 142765532Snectar name = va_arg(ap, char *); 142865532Snectar pai = va_arg(ap, const struct addrinfo *); 142965532Snectar 143061877Sume memset(&q, 0, sizeof(q2)); 143161877Sume memset(&q2, 0, sizeof(q2)); 143261877Sume memset(&sentinel, 0, sizeof(sentinel)); 143361877Sume cur = &sentinel; 143461877Sume 1435103357Sume buf = malloc(sizeof(*buf)); 1436103357Sume if (!buf) { 1437103357Sume h_errno = NETDB_INTERNAL; 1438103357Sume return NS_NOTFOUND; 1439103357Sume } 1440103357Sume buf2 = malloc(sizeof(*buf2)); 1441103357Sume if (!buf2) { 1442103357Sume free(buf); 1443103357Sume h_errno = NETDB_INTERNAL; 1444103357Sume return NS_NOTFOUND; 1445103357Sume } 1446103357Sume 144761877Sume switch (pai->ai_family) { 144861877Sume case AF_UNSPEC: 144961877Sume /* prefer IPv6 */ 1450105940Sume q.name = name; 145162614Sitojun q.qclass = C_IN; 145262614Sitojun q.qtype = T_AAAA; 1453103357Sume q.answer = buf->buf; 1454103357Sume q.anslen = sizeof(buf->buf); 145561877Sume q.next = &q2; 1456105943Sume q2.name = name; 145762614Sitojun q2.qclass = C_IN; 145862614Sitojun q2.qtype = T_A; 1459103357Sume q2.answer = buf2->buf; 1460103357Sume q2.anslen = sizeof(buf2->buf); 146161877Sume break; 146261877Sume case AF_INET: 1463105940Sume q.name = name; 146462614Sitojun q.qclass = C_IN; 146562614Sitojun q.qtype = T_A; 1466103357Sume q.answer = buf->buf; 1467103357Sume q.anslen = sizeof(buf->buf); 146861877Sume break; 146961877Sume case AF_INET6: 1470105940Sume q.name = name; 147162614Sitojun q.qclass = C_IN; 147262614Sitojun q.qtype = T_AAAA; 1473103357Sume q.answer = buf->buf; 1474103357Sume q.anslen = sizeof(buf->buf); 147561877Sume break; 147661877Sume default: 1477103357Sume free(buf); 1478103357Sume free(buf2); 147965532Snectar return NS_UNAVAIL; 148061877Sume } 1481103357Sume if (res_searchN(name, &q) < 0) { 1482103357Sume free(buf); 1483103357Sume free(buf2); 148465532Snectar return NS_NOTFOUND; 1485103357Sume } 1486103357Sume ai = getanswer(buf, q.n, q.name, q.qtype, pai); 148761877Sume if (ai) { 148861877Sume cur->ai_next = ai; 148961877Sume while (cur && cur->ai_next) 149061877Sume cur = cur->ai_next; 149161877Sume } 149261877Sume if (q.next) { 1493103357Sume ai = getanswer(buf2, q2.n, q2.name, q2.qtype, pai); 149461877Sume if (ai) 149561877Sume cur->ai_next = ai; 149661877Sume } 1497103357Sume free(buf); 1498103357Sume free(buf2); 149961877Sume if (sentinel.ai_next == NULL) 150061877Sume switch (h_errno) { 150161877Sume case HOST_NOT_FOUND: 150265532Snectar return NS_NOTFOUND; 150361877Sume case TRY_AGAIN: 150465532Snectar return NS_TRYAGAIN; 150561877Sume default: 150665532Snectar return NS_UNAVAIL; 150761877Sume } 150865532Snectar *((struct addrinfo **)rv) = sentinel.ai_next; 150965532Snectar return NS_SUCCESS; 151061877Sume} 151161877Sume 151265532Snectarstatic void 151365532Snectar_sethtent() 151465532Snectar{ 151565532Snectar if (!hostf) 151665532Snectar hostf = fopen(_PATH_HOSTS, "r" ); 151765532Snectar else 151865532Snectar rewind(hostf); 151965532Snectar} 152065532Snectar 152165532Snectarstatic void 152265532Snectar_endhtent() 152365532Snectar{ 152465532Snectar if (hostf) { 152565532Snectar (void) fclose(hostf); 152665532Snectar hostf = NULL; 152765532Snectar } 152865532Snectar} 152965532Snectar 153061877Sumestatic struct addrinfo * 153165532Snectar_gethtent(name, pai) 153261877Sume const char *name; 153361877Sume const struct addrinfo *pai; 153461877Sume{ 153561877Sume char *p; 153661877Sume char *cp, *tname, *cname; 153761877Sume struct addrinfo hints, *res0, *res; 153861877Sume int error; 153961877Sume const char *addr; 154061877Sume char hostbuf[8*1024]; 154161877Sume 154265532Snectar if (!hostf && !(hostf = fopen(_PATH_HOSTS, "r" ))) 154365532Snectar return (NULL); 1544105940Sumeagain: 154561877Sume if (!(p = fgets(hostbuf, sizeof hostbuf, hostf))) 154661877Sume return (NULL); 154761877Sume if (*p == '#') 154861877Sume goto again; 154961877Sume if (!(cp = strpbrk(p, "#\n"))) 155061877Sume goto again; 155161877Sume *cp = '\0'; 155261877Sume if (!(cp = strpbrk(p, " \t"))) 155361877Sume goto again; 155461877Sume *cp++ = '\0'; 155561877Sume addr = p; 155661877Sume cname = NULL; 155761877Sume /* if this is not something we're looking for, skip it. */ 155861877Sume while (cp && *cp) { 155961877Sume if (*cp == ' ' || *cp == '\t') { 156061877Sume cp++; 156161877Sume continue; 156261877Sume } 156361877Sume tname = cp; 156461877Sume if (cname == NULL) 156561877Sume cname = cp; 156661877Sume if ((cp = strpbrk(cp, " \t")) != NULL) 156761877Sume *cp++ = '\0'; 156861877Sume if (strcasecmp(name, tname) == 0) 156961877Sume goto found; 157061877Sume } 157161877Sume goto again; 157261877Sume 157361877Sumefound: 1574105940Sume /* we should not glob socktype/protocol here */ 1575105940Sume memset(&hints, 0, sizeof(hints)); 1576105940Sume hints.ai_family = pai->ai_family; 1577105940Sume hints.ai_socktype = SOCK_DGRAM; 1578105940Sume hints.ai_protocol = 0; 157961877Sume hints.ai_flags = AI_NUMERICHOST; 1580105940Sume error = getaddrinfo(addr, "0", &hints, &res0); 158161877Sume if (error) 158261877Sume goto again; 158361877Sume#ifdef FILTER_V4MAPPED 158461877Sume /* XXX should check all items in the chain */ 158561877Sume if (res0->ai_family == AF_INET6 && 158661877Sume IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)res0->ai_addr)->sin6_addr)) { 158761877Sume freeaddrinfo(res0); 158861877Sume goto again; 158961877Sume } 159061877Sume#endif 159161877Sume for (res = res0; res; res = res->ai_next) { 159261877Sume /* cover it up */ 159361877Sume res->ai_flags = pai->ai_flags; 1594105940Sume res->ai_socktype = pai->ai_socktype; 1595105940Sume res->ai_protocol = pai->ai_protocol; 159661877Sume 159761877Sume if (pai->ai_flags & AI_CANONNAME) { 159861877Sume if (get_canonname(pai, res, cname) != 0) { 159961877Sume freeaddrinfo(res0); 160061877Sume goto again; 160161877Sume } 160261877Sume } 160361877Sume } 160461877Sume return res0; 160561877Sume} 160661877Sume 160761877Sume/*ARGSUSED*/ 160861877Sumestatic int 160965532Snectar_files_getaddrinfo(rv, cb_data, ap) 161065532Snectar void *rv; 161165532Snectar void *cb_data; 161265532Snectar va_list ap; 161365532Snectar{ 161465532Snectar const char *name; 161561877Sume const struct addrinfo *pai; 161661877Sume struct addrinfo sentinel, *cur; 161761877Sume struct addrinfo *p; 161861877Sume 161965532Snectar name = va_arg(ap, char *); 162065532Snectar pai = va_arg(ap, struct addrinfo *); 162165532Snectar 162265532Snectar memset(&sentinel, 0, sizeof(sentinel)); 162361877Sume cur = &sentinel; 162461877Sume 162565532Snectar _sethtent(); 162665532Snectar while ((p = _gethtent(name, pai)) != NULL) { 162761877Sume cur->ai_next = p; 162861877Sume while (cur && cur->ai_next) 162961877Sume cur = cur->ai_next; 163061877Sume } 163165532Snectar _endhtent(); 163261877Sume 163365532Snectar *((struct addrinfo **)rv) = sentinel.ai_next; 163465532Snectar if (sentinel.ai_next == NULL) 163565532Snectar return NS_NOTFOUND; 163665532Snectar return NS_SUCCESS; 163761877Sume} 163861877Sume 163961877Sume#ifdef YP 164065532Snectarstatic char *__ypdomain; 164165532Snectar 164261877Sume/*ARGSUSED*/ 164365532Snectarstatic struct addrinfo * 164465532Snectar_yphostent(line, pai) 164565532Snectar char *line; 164661877Sume const struct addrinfo *pai; 164761877Sume{ 164861877Sume struct addrinfo sentinel, *cur; 164965532Snectar struct addrinfo hints, *res, *res0; 165061877Sume int error; 165165532Snectar char *p = line; 165265532Snectar const char *addr, *canonname; 165365532Snectar char *nextline; 165465532Snectar char *cp; 165561877Sume 165665532Snectar addr = canonname = NULL; 165765532Snectar 165865532Snectar memset(&sentinel, 0, sizeof(sentinel)); 165961877Sume cur = &sentinel; 166061877Sume 166165532Snectarnextline: 166265532Snectar /* terminate line */ 166365532Snectar cp = strchr(p, '\n'); 166465532Snectar if (cp) { 166565532Snectar *cp++ = '\0'; 166665532Snectar nextline = cp; 166765532Snectar } else 166865532Snectar nextline = NULL; 166961877Sume 167065532Snectar cp = strpbrk(p, " \t"); 167165532Snectar if (cp == NULL) { 167265532Snectar if (canonname == NULL) 167365532Snectar return (NULL); 167465532Snectar else 167565532Snectar goto done; 167661877Sume } 167765532Snectar *cp++ = '\0'; 167861877Sume 167965532Snectar addr = p; 168061877Sume 168165532Snectar while (cp && *cp) { 168265532Snectar if (*cp == ' ' || *cp == '\t') { 168365532Snectar cp++; 168461877Sume continue; 168565532Snectar } 168665532Snectar if (!canonname) 168765532Snectar canonname = cp; 168865532Snectar if ((cp = strpbrk(cp, " \t")) != NULL) 168965532Snectar *cp++ = '\0'; 169065532Snectar } 169161877Sume 169265532Snectar hints = *pai; 169365532Snectar hints.ai_flags = AI_NUMERICHOST; 169465532Snectar error = getaddrinfo(addr, NULL, &hints, &res0); 169565532Snectar if (error == 0) { 169665532Snectar for (res = res0; res; res = res->ai_next) { 169765532Snectar /* cover it up */ 169865532Snectar res->ai_flags = pai->ai_flags; 169961877Sume 170065532Snectar if (pai->ai_flags & AI_CANONNAME) 170165532Snectar (void)get_canonname(pai, res, canonname); 170261877Sume } 170365532Snectar } else 170465532Snectar res0 = NULL; 170565532Snectar if (res0) { 170665532Snectar cur->ai_next = res0; 170761877Sume while (cur && cur->ai_next) 170861877Sume cur = cur->ai_next; 170961877Sume } 171061877Sume 171165532Snectar if (nextline) { 171265532Snectar p = nextline; 171365532Snectar goto nextline; 171465532Snectar } 171561877Sume 171665532Snectardone: 171765532Snectar return sentinel.ai_next; 171861877Sume} 171965532Snectar 172065532Snectar/*ARGSUSED*/ 172165532Snectarstatic int 172265532Snectar_yp_getaddrinfo(rv, cb_data, ap) 172365532Snectar void *rv; 172465532Snectar void *cb_data; 172565532Snectar va_list ap; 172665532Snectar{ 172765532Snectar struct addrinfo sentinel, *cur; 172865532Snectar struct addrinfo *ai = NULL; 172965532Snectar static char *__ypcurrent; 173065532Snectar int __ypcurrentlen, r; 173165532Snectar const char *name; 173265532Snectar const struct addrinfo *pai; 173365532Snectar 173465532Snectar name = va_arg(ap, char *); 173565532Snectar pai = va_arg(ap, const struct addrinfo *); 173665532Snectar 173765532Snectar memset(&sentinel, 0, sizeof(sentinel)); 173865532Snectar cur = &sentinel; 173965532Snectar 174065532Snectar if (!__ypdomain) { 174165532Snectar if (_yp_check(&__ypdomain) == 0) 174265532Snectar return NS_UNAVAIL; 174365532Snectar } 174465532Snectar if (__ypcurrent) 174565532Snectar free(__ypcurrent); 174665532Snectar __ypcurrent = NULL; 174765532Snectar 174865532Snectar /* hosts.byname is only for IPv4 (Solaris8) */ 174965532Snectar if (pai->ai_family == PF_UNSPEC || pai->ai_family == PF_INET) { 175065532Snectar r = yp_match(__ypdomain, "hosts.byname", name, 175165532Snectar (int)strlen(name), &__ypcurrent, &__ypcurrentlen); 175265532Snectar if (r == 0) { 175365532Snectar struct addrinfo ai4; 175465532Snectar 175565532Snectar ai4 = *pai; 175665532Snectar ai4.ai_family = AF_INET; 175765532Snectar ai = _yphostent(__ypcurrent, &ai4); 175865532Snectar if (ai) { 175965532Snectar cur->ai_next = ai; 176065532Snectar while (cur && cur->ai_next) 176165532Snectar cur = cur->ai_next; 176265532Snectar } 176365532Snectar } 176465532Snectar } 176565532Snectar 176665532Snectar /* ipnodes.byname can hold both IPv4/v6 */ 176765532Snectar r = yp_match(__ypdomain, "ipnodes.byname", name, 176865532Snectar (int)strlen(name), &__ypcurrent, &__ypcurrentlen); 176965532Snectar if (r == 0) { 177065532Snectar ai = _yphostent(__ypcurrent, pai); 177165532Snectar if (ai) { 177265532Snectar cur->ai_next = ai; 177365532Snectar while (cur && cur->ai_next) 177465532Snectar cur = cur->ai_next; 177565532Snectar } 177665532Snectar } 177765532Snectar 177865532Snectar if (sentinel.ai_next == NULL) { 177965532Snectar h_errno = HOST_NOT_FOUND; 178065532Snectar return NS_NOTFOUND; 178165532Snectar } 178265532Snectar *((struct addrinfo **)rv) = sentinel.ai_next; 178365532Snectar return NS_SUCCESS; 178465532Snectar} 178561877Sume#endif 178661877Sume 178761877Sume/* resolver logic */ 178861877Sume 178992905Sobrienextern const char *__hostalias(const char *); 179061877Sumeextern int h_errno; 179161877Sume 179261877Sume/* 179361877Sume * Formulate a normal query, send, and await answer. 179461877Sume * Returned answer is placed in supplied buffer "answer". 179561877Sume * Perform preliminary check of answer, returning success only 179661877Sume * if no error is indicated and the answer count is nonzero. 179761877Sume * Return the size of the response on success, -1 on error. 179861877Sume * Error number is left in h_errno. 179961877Sume * 180061877Sume * Caller must parse answer and determine whether it answers the question. 180161877Sume */ 180261877Sumestatic int 180361877Sumeres_queryN(name, target) 180461877Sume const char *name; /* domain name */ 180561877Sume struct res_target *target; 180661877Sume{ 1807103357Sume u_char *buf; 180861877Sume HEADER *hp; 180961877Sume int n; 181061877Sume struct res_target *t; 181161877Sume int rcode; 181261877Sume int ancount; 181361877Sume 181461877Sume rcode = NOERROR; 181561877Sume ancount = 0; 181661877Sume 181761877Sume if ((_res.options & RES_INIT) == 0 && res_init() == -1) { 181861877Sume h_errno = NETDB_INTERNAL; 181961877Sume return (-1); 182061877Sume } 182161877Sume 1822103357Sume buf = malloc(MAXPACKET); 1823103357Sume if (!buf) { 1824103357Sume h_errno = NETDB_INTERNAL; 1825103357Sume return -1; 1826103357Sume } 1827103357Sume 182861877Sume for (t = target; t; t = t->next) { 182961877Sume int class, type; 183061877Sume u_char *answer; 183161877Sume int anslen; 183261877Sume 183361877Sume hp = (HEADER *)(void *)t->answer; 183461877Sume hp->rcode = NOERROR; /* default */ 183561877Sume 183661877Sume /* make it easier... */ 183762614Sitojun class = t->qclass; 183862614Sitojun type = t->qtype; 183961877Sume answer = t->answer; 184061877Sume anslen = t->anslen; 184161877Sume#ifdef DEBUG 184261877Sume if (_res.options & RES_DEBUG) 184361877Sume printf(";; res_query(%s, %d, %d)\n", name, class, type); 184461877Sume#endif 184561877Sume 184661877Sume n = res_mkquery(QUERY, name, class, type, NULL, 0, NULL, 1847103357Sume buf, MAXPACKET); 184878012Sume if (n > 0 && (_res.options & RES_USE_EDNS0) != 0) 1849103357Sume n = res_opt(n, buf, MAXPACKET, anslen); 185061877Sume if (n <= 0) { 185161877Sume#ifdef DEBUG 185261877Sume if (_res.options & RES_DEBUG) 185361877Sume printf(";; res_query: mkquery failed\n"); 185461877Sume#endif 1855103357Sume free(buf); 185661877Sume h_errno = NO_RECOVERY; 185761877Sume return (n); 185861877Sume } 185961877Sume n = res_send(buf, n, answer, anslen); 186061877Sume#if 0 186161877Sume if (n < 0) { 186261877Sume#ifdef DEBUG 186361877Sume if (_res.options & RES_DEBUG) 186461877Sume printf(";; res_query: send error\n"); 186561877Sume#endif 1866103357Sume free(buf); 186761877Sume h_errno = TRY_AGAIN; 186861877Sume return (n); 186961877Sume } 187061877Sume#endif 187161877Sume 1872103350Snectar if (n < 0 || n > anslen) 1873103350Snectar hp->rcode = FORMERR; /* XXX not very informative */ 1874103350Snectar if (hp->rcode != NOERROR || ntohs(hp->ancount) == 0) { 187561877Sume rcode = hp->rcode; /* record most recent error */ 187661877Sume#ifdef DEBUG 187761877Sume if (_res.options & RES_DEBUG) 1878105940Sume printf(";; rcode = %u, ancount=%u\n", hp->rcode, 187961877Sume ntohs(hp->ancount)); 188061877Sume#endif 188161877Sume continue; 188261877Sume } 188361877Sume 188461877Sume ancount += ntohs(hp->ancount); 188561877Sume 188661877Sume t->n = n; 188761877Sume } 188861877Sume 1889103357Sume free(buf); 1890103357Sume 189161877Sume if (ancount == 0) { 189261877Sume switch (rcode) { 189361877Sume case NXDOMAIN: 189461877Sume h_errno = HOST_NOT_FOUND; 189561877Sume break; 189661877Sume case SERVFAIL: 189761877Sume h_errno = TRY_AGAIN; 189861877Sume break; 189961877Sume case NOERROR: 190061877Sume h_errno = NO_DATA; 190161877Sume break; 190261877Sume case FORMERR: 190361877Sume case NOTIMP: 190461877Sume case REFUSED: 190561877Sume default: 190661877Sume h_errno = NO_RECOVERY; 190761877Sume break; 190861877Sume } 190961877Sume return (-1); 191061877Sume } 191161877Sume return (ancount); 191261877Sume} 191361877Sume 191461877Sume/* 191561877Sume * Formulate a normal query, send, and retrieve answer in supplied buffer. 191661877Sume * Return the size of the response on success, -1 on error. 191761877Sume * If enabled, implement search rules until answer or unrecoverable failure 191861877Sume * is detected. Error code, if any, is left in h_errno. 191961877Sume */ 192061877Sumestatic int 192161877Sumeres_searchN(name, target) 192261877Sume const char *name; /* domain name */ 192361877Sume struct res_target *target; 192461877Sume{ 192561877Sume const char *cp, * const *domain; 192661877Sume HEADER *hp = (HEADER *)(void *)target->answer; /*XXX*/ 192761877Sume u_int dots; 192861877Sume int trailing_dot, ret, saved_herrno; 192961877Sume int got_nodata = 0, got_servfail = 0, tried_as_is = 0; 193061877Sume 193161877Sume if ((_res.options & RES_INIT) == 0 && res_init() == -1) { 193261877Sume h_errno = NETDB_INTERNAL; 193361877Sume return (-1); 193461877Sume } 193561877Sume 193661877Sume errno = 0; 193761877Sume h_errno = HOST_NOT_FOUND; /* default, if we never query */ 193861877Sume dots = 0; 193961877Sume for (cp = name; *cp; cp++) 194061877Sume dots += (*cp == '.'); 194161877Sume trailing_dot = 0; 194261877Sume if (cp > name && *--cp == '.') 194361877Sume trailing_dot++; 194461877Sume 194561877Sume /* 194661877Sume * if there aren't any dots, it could be a user-level alias 194761877Sume */ 194861877Sume if (!dots && (cp = __hostalias(name)) != NULL) 194961877Sume return (res_queryN(cp, target)); 195061877Sume 195161877Sume /* 195261877Sume * If there are dots in the name already, let's just give it a try 195361877Sume * 'as is'. The threshold can be set with the "ndots" option. 195461877Sume */ 195561877Sume saved_herrno = -1; 195661877Sume if (dots >= _res.ndots) { 195761877Sume ret = res_querydomainN(name, NULL, target); 195861877Sume if (ret > 0) 195961877Sume return (ret); 196061877Sume saved_herrno = h_errno; 196161877Sume tried_as_is++; 196261877Sume } 196361877Sume 196461877Sume /* 196561877Sume * We do at least one level of search if 196661877Sume * - there is no dot and RES_DEFNAME is set, or 196761877Sume * - there is at least one dot, there is no trailing dot, 196861877Sume * and RES_DNSRCH is set. 196961877Sume */ 197061877Sume if ((!dots && (_res.options & RES_DEFNAMES)) || 197161877Sume (dots && !trailing_dot && (_res.options & RES_DNSRCH))) { 197261877Sume int done = 0; 197361877Sume 197461877Sume for (domain = (const char * const *)_res.dnsrch; 197561877Sume *domain && !done; 197661877Sume domain++) { 197761877Sume 197861877Sume ret = res_querydomainN(name, *domain, target); 197961877Sume if (ret > 0) 198061877Sume return (ret); 198161877Sume 198261877Sume /* 198361877Sume * If no server present, give up. 198461877Sume * If name isn't found in this domain, 198561877Sume * keep trying higher domains in the search list 198661877Sume * (if that's enabled). 198761877Sume * On a NO_DATA error, keep trying, otherwise 198861877Sume * a wildcard entry of another type could keep us 198961877Sume * from finding this entry higher in the domain. 199061877Sume * If we get some other error (negative answer or 199161877Sume * server failure), then stop searching up, 199261877Sume * but try the input name below in case it's 199361877Sume * fully-qualified. 199461877Sume */ 199561877Sume if (errno == ECONNREFUSED) { 199661877Sume h_errno = TRY_AGAIN; 199761877Sume return (-1); 199861877Sume } 199961877Sume 200061877Sume switch (h_errno) { 200161877Sume case NO_DATA: 200261877Sume got_nodata++; 200361877Sume /* FALLTHROUGH */ 200461877Sume case HOST_NOT_FOUND: 200561877Sume /* keep trying */ 200661877Sume break; 200761877Sume case TRY_AGAIN: 200861877Sume if (hp->rcode == SERVFAIL) { 200961877Sume /* try next search element, if any */ 201061877Sume got_servfail++; 201161877Sume break; 201261877Sume } 201361877Sume /* FALLTHROUGH */ 201461877Sume default: 201561877Sume /* anything else implies that we're done */ 201661877Sume done++; 201761877Sume } 201861877Sume /* 201961877Sume * if we got here for some reason other than DNSRCH, 202061877Sume * we only wanted one iteration of the loop, so stop. 202161877Sume */ 202261877Sume if (!(_res.options & RES_DNSRCH)) 202361877Sume done++; 202461877Sume } 202561877Sume } 202661877Sume 202761877Sume /* 202861877Sume * if we have not already tried the name "as is", do that now. 202961877Sume * note that we do this regardless of how many dots were in the 203061877Sume * name or whether it ends with a dot. 203161877Sume */ 203261877Sume if (!tried_as_is && (dots || !(_res.options & RES_NOTLDQUERY))) { 203361877Sume ret = res_querydomainN(name, NULL, target); 203461877Sume if (ret > 0) 203561877Sume return (ret); 203661877Sume } 203761877Sume 203861877Sume /* 203961877Sume * if we got here, we didn't satisfy the search. 204061877Sume * if we did an initial full query, return that query's h_errno 204161877Sume * (note that we wouldn't be here if that query had succeeded). 204261877Sume * else if we ever got a nodata, send that back as the reason. 204361877Sume * else send back meaningless h_errno, that being the one from 204461877Sume * the last DNSRCH we did. 204561877Sume */ 204661877Sume if (saved_herrno != -1) 204761877Sume h_errno = saved_herrno; 204861877Sume else if (got_nodata) 204961877Sume h_errno = NO_DATA; 205061877Sume else if (got_servfail) 205161877Sume h_errno = TRY_AGAIN; 205261877Sume return (-1); 205361877Sume} 205461877Sume 205561877Sume/* 205661877Sume * Perform a call on res_query on the concatenation of name and domain, 205761877Sume * removing a trailing dot from name if domain is NULL. 205861877Sume */ 205961877Sumestatic int 206061877Sumeres_querydomainN(name, domain, target) 206161877Sume const char *name, *domain; 206261877Sume struct res_target *target; 206361877Sume{ 206461877Sume char nbuf[MAXDNAME]; 206561877Sume const char *longname = nbuf; 206661877Sume size_t n, d; 206761877Sume 206861877Sume if ((_res.options & RES_INIT) == 0 && res_init() == -1) { 206961877Sume h_errno = NETDB_INTERNAL; 207061877Sume return (-1); 207161877Sume } 207261877Sume#ifdef DEBUG 207361877Sume if (_res.options & RES_DEBUG) 207461877Sume printf(";; res_querydomain(%s, %s)\n", 207561877Sume name, domain?domain:"<Nil>"); 207661877Sume#endif 207761877Sume if (domain == NULL) { 207861877Sume /* 207961877Sume * Check for trailing '.'; 208061877Sume * copy without '.' if present. 208161877Sume */ 208261877Sume n = strlen(name); 208361877Sume if (n >= MAXDNAME) { 208461877Sume h_errno = NO_RECOVERY; 208561877Sume return (-1); 208661877Sume } 208761877Sume if (n > 0 && name[--n] == '.') { 208861877Sume strncpy(nbuf, name, n); 208961877Sume nbuf[n] = '\0'; 209061877Sume } else 209161877Sume longname = name; 209261877Sume } else { 209361877Sume n = strlen(name); 209461877Sume d = strlen(domain); 209561877Sume if (n + d + 1 >= MAXDNAME) { 209661877Sume h_errno = NO_RECOVERY; 209761877Sume return (-1); 209861877Sume } 2099105940Sume snprintf(nbuf, sizeof(nbuf), "%s.%s", name, domain); 210061877Sume } 210161877Sume return (res_queryN(longname, target)); 210261877Sume} 2103