1121474Sume/* $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 * - Return values. There are nonstandard return values defined and used 3755163Sshin * in the source code. This is because RFC2553 is silent about which error 3855163Sshin * code must be returned for which situation. 3961877Sume * - freeaddrinfo(NULL). RFC2553 is silent about it. XNET 5.2 says it is 40105940Sume * invalid. current code - SEGV on freeaddrinfo(NULL) 41105940Sume * 4256627Sshin * Note: 4356627Sshin * - The code filters out AFs that are not supported by the kernel, 4456627Sshin * when globbing NULL hostname (to loopback, or wildcard). Is it the right 4556627Sshin * thing to do? What is the relationship with post-RFC2553 AI_ADDRCONFIG 4656627Sshin * in ai_flags? 4761877Sume * - (post-2553) semantics of AI_ADDRCONFIG itself is too vague. 4861877Sume * (1) what should we do against numeric hostname (2) what should we do 4961877Sume * against NULL hostname (3) what is AI_ADDRCONFIG itself. AF not ready? 5061877Sume * non-loopback address configured? global address configured? 51105940Sume * 52105940Sume * OS specific notes for freebsd4: 53105940Sume * - FreeBSD supported $GAI. The code does not. 5455163Sshin */ 5555163Sshin 5692986Sobrien#include <sys/cdefs.h> 5792986Sobrien__FBSDID("$FreeBSD$"); 5892986Sobrien 5971579Sdeischen#include "namespace.h" 6055163Sshin#include <sys/types.h> 6155163Sshin#include <sys/param.h> 6255163Sshin#include <sys/socket.h> 6355163Sshin#include <net/if.h> 6455163Sshin#include <netinet/in.h> 65267874Sume#include <net/if_types.h> 66267874Sume#include <ifaddrs.h> 67121747Sume#include <sys/queue.h> 68121747Sume#ifdef INET6 69121747Sume#include <net/if_var.h> 70121747Sume#include <sys/sysctl.h> 71129901Sume#include <sys/ioctl.h> 72267874Sume#include <netinet6/in6_var.h> 73267874Sume#include <netinet6/nd6.h> 74121747Sume#endif 7555163Sshin#include <arpa/inet.h> 7655163Sshin#include <arpa/nameser.h> 77121474Sume#include <rpc/rpc.h> 78121474Sume#include <rpcsvc/yp_prot.h> 79121474Sume#include <rpcsvc/ypclnt.h> 8055163Sshin#include <netdb.h> 8155163Sshin#include <resolv.h> 8255163Sshin#include <string.h> 8355163Sshin#include <stdlib.h> 8455163Sshin#include <stddef.h> 8555163Sshin#include <ctype.h> 8655163Sshin#include <unistd.h> 8755163Sshin#include <stdio.h> 8861877Sume#include <errno.h> 89102237Spirzyk 90102237Spirzyk#include "res_config.h" 91102237Spirzyk 9278012Sume#ifdef DEBUG 9378012Sume#include <syslog.h> 9478012Sume#endif 9555163Sshin 9665532Snectar#include <stdarg.h> 9765532Snectar#include <nsswitch.h> 9871579Sdeischen#include "un-namespace.h" 99111618Snectar#include "libc_private.h" 100158115Sume#ifdef NS_CACHING 101158115Sume#include "nscache.h" 102158115Sume#endif 10365532Snectar 10455163Sshin#if defined(__KAME__) && defined(INET6) 10555163Sshin# define FAITH 10655163Sshin#endif 10755163Sshin 108105940Sume#define ANY 0 109105940Sume#define YES 1 110105940Sume#define NO 0 11155163Sshin 11255163Sshinstatic const char in_addrany[] = { 0, 0, 0, 0 }; 113105940Sumestatic const char in_loopback[] = { 127, 0, 0, 1 }; 114105940Sume#ifdef INET6 11555163Sshinstatic const char in6_addrany[] = { 11655163Sshin 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 11755163Sshin}; 11855163Sshinstatic const char in6_loopback[] = { 11955163Sshin 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 12055163Sshin}; 121105940Sume#endif 12255163Sshin 123121747Sumestruct policyqueue { 124121747Sume TAILQ_ENTRY(policyqueue) pc_entry; 125121747Sume#ifdef INET6 126121747Sume struct in6_addrpolicy pc_policy; 127121747Sume#endif 128121747Sume}; 129121747SumeTAILQ_HEAD(policyhead, policyqueue); 130121747Sume 13155163Sshinstatic const struct afd { 13255163Sshin int a_af; 13355163Sshin int a_addrlen; 134146244Sume socklen_t a_socklen; 13555163Sshin int a_off; 13655163Sshin const char *a_addrany; 13773665Sobrien const char *a_loopback; 13855163Sshin int a_scoped; 13955163Sshin} afdl [] = { 14055163Sshin#ifdef INET6 14155163Sshin#define N_INET6 0 14255163Sshin {PF_INET6, sizeof(struct in6_addr), 14355163Sshin sizeof(struct sockaddr_in6), 14455163Sshin offsetof(struct sockaddr_in6, sin6_addr), 14555163Sshin in6_addrany, in6_loopback, 1}, 14655163Sshin#define N_INET 1 14755163Sshin#else 14855163Sshin#define N_INET 0 14955163Sshin#endif 15055163Sshin {PF_INET, sizeof(struct in_addr), 15155163Sshin sizeof(struct sockaddr_in), 15255163Sshin offsetof(struct sockaddr_in, sin_addr), 15355163Sshin in_addrany, in_loopback, 0}, 15455163Sshin {0, 0, 0, 0, NULL, NULL, 0}, 15555163Sshin}; 15655163Sshin 15755163Sshinstruct explore { 15861877Sume int e_af; 15955163Sshin int e_socktype; 16055163Sshin int e_protocol; 16155163Sshin int e_wild; 162105940Sume#define WILD_AF(ex) ((ex)->e_wild & 0x01) 163105940Sume#define WILD_SOCKTYPE(ex) ((ex)->e_wild & 0x02) 164105940Sume#define WILD_PROTOCOL(ex) ((ex)->e_wild & 0x04) 16555163Sshin}; 16655163Sshin 16755163Sshinstatic const struct explore explore[] = { 16861877Sume#if 0 169238504Sjilles { PF_LOCAL, ANY, ANY, 0x01 }, 17061877Sume#endif 17161877Sume#ifdef INET6 172238504Sjilles { PF_INET6, SOCK_DGRAM, IPPROTO_UDP, 0x07 }, 173238504Sjilles { PF_INET6, SOCK_STREAM, IPPROTO_TCP, 0x07 }, 174238504Sjilles { PF_INET6, SOCK_STREAM, IPPROTO_SCTP, 0x03 }, 175238504Sjilles { PF_INET6, SOCK_SEQPACKET, IPPROTO_SCTP, 0x07 }, 176265946Skevlo { PF_INET6, SOCK_DGRAM, IPPROTO_UDPLITE, 0x03 }, 177238504Sjilles { PF_INET6, SOCK_RAW, ANY, 0x05 }, 17861877Sume#endif 179238504Sjilles { PF_INET, SOCK_DGRAM, IPPROTO_UDP, 0x07 }, 180238504Sjilles { PF_INET, SOCK_STREAM, IPPROTO_TCP, 0x07 }, 181238504Sjilles { PF_INET, SOCK_STREAM, IPPROTO_SCTP, 0x03 }, 182238504Sjilles { PF_INET, SOCK_SEQPACKET, IPPROTO_SCTP, 0x07 }, 183265946Skevlo { PF_INET, SOCK_DGRAM, IPPROTO_UDPLITE, 0x03 }, 184238504Sjilles { PF_INET, SOCK_RAW, ANY, 0x05 }, 185238504Sjilles { -1, 0, 0, 0 }, 18655163Sshin}; 18755163Sshin 18855163Sshin#ifdef INET6 189105940Sume#define PTON_MAX 16 19055163Sshin#else 191105940Sume#define PTON_MAX 4 19255163Sshin#endif 19355163Sshin 194121747Sume#define AIO_SRCFLAG_DEPRECATED 0x1 195121747Sume 196121747Sumestruct ai_order { 197121747Sume union { 198121747Sume struct sockaddr_storage aiou_ss; 199121747Sume struct sockaddr aiou_sa; 200121747Sume } aio_src_un; 201121747Sume#define aio_srcsa aio_src_un.aiou_sa 202121747Sume u_int32_t aio_srcflag; 203121747Sume int aio_srcscope; 204121747Sume int aio_dstscope; 205121747Sume struct policyqueue *aio_srcpolicy; 206121747Sume struct policyqueue *aio_dstpolicy; 207121747Sume struct addrinfo *aio_ai; 208121747Sume int aio_matchlen; 209121747Sume}; 210121747Sume 21165532Snectarstatic const ns_src default_dns_files[] = { 21265532Snectar { NSSRC_FILES, NS_SUCCESS }, 21365532Snectar { NSSRC_DNS, NS_SUCCESS }, 21465532Snectar { 0 } 21565532Snectar}; 21665532Snectar 21761877Sumestruct res_target { 21861877Sume struct res_target *next; 21961877Sume const char *name; /* domain name */ 22062614Sitojun int qclass, qtype; /* class and type of query */ 22161877Sume u_char *answer; /* buffer to put answer */ 22261877Sume int anslen; /* size of answer buffer */ 22361877Sume int n; /* result length */ 22461877Sume}; 22561877Sume 226121426Sume#define MAXPACKET (64*1024) 227121426Sume 228121426Sumetypedef union { 229121426Sume HEADER hdr; 230121426Sume u_char buf[MAXPACKET]; 231121426Sume} querybuf; 232121426Sume 233160593Sumestatic int str2number(const char *, int *); 234190525Sumestatic int explore_copy(const struct addrinfo *, const struct addrinfo *, 235190525Sume struct addrinfo **); 23692941Sobrienstatic int explore_null(const struct addrinfo *, 23792941Sobrien const char *, struct addrinfo **); 23892941Sobrienstatic int explore_numeric(const struct addrinfo *, const char *, 239140906Sume const char *, struct addrinfo **, const char *); 24092941Sobrienstatic int explore_numeric_scope(const struct addrinfo *, const char *, 24192941Sobrien const char *, struct addrinfo **); 24292941Sobrienstatic int get_canonname(const struct addrinfo *, 24392941Sobrien struct addrinfo *, const char *); 24492941Sobrienstatic struct addrinfo *get_ai(const struct addrinfo *, 24592941Sobrien const struct afd *, const char *); 246190525Sumestatic struct addrinfo *copy_ai(const struct addrinfo *); 24792905Sobrienstatic int get_portmatch(const struct addrinfo *, const char *); 24892905Sobrienstatic int get_port(struct addrinfo *, const char *, int); 24992905Sobrienstatic const struct afd *find_afd(int); 250121474Sumestatic int addrconfig(struct addrinfo *); 251267874Sume#ifdef INET6 252267874Sumestatic int is_ifdisabled(char *); 253267874Sume#endif 254129901Sumestatic void set_source(struct ai_order *, struct policyhead *); 255121747Sumestatic int comp_dst(const void *, const void *); 25661877Sume#ifdef INET6 257105943Sumestatic int ip6_str2scopeid(char *, struct sockaddr_in6 *, u_int32_t *); 25861877Sume#endif 259121747Sumestatic int gai_addr2scopetype(struct sockaddr *); 26055163Sshin 261121426Sumestatic int explore_fqdn(const struct addrinfo *, const char *, 262121426Sume const char *, struct addrinfo **); 263121426Sume 264121747Sumestatic int reorder(struct addrinfo *); 265121747Sumestatic int get_addrselectpolicy(struct policyhead *); 266121747Sumestatic void free_addrselectpolicy(struct policyhead *); 267121747Sumestatic struct policyqueue *match_addrselectpolicy(struct sockaddr *, 268121747Sume struct policyhead *); 269129901Sumestatic int matchlen(struct sockaddr *, struct sockaddr *); 270121747Sume 27192941Sobrienstatic struct addrinfo *getanswer(const querybuf *, int, const char *, int, 272156960Sume const struct addrinfo *, res_state); 273121426Sume#if defined(RESOLVSORT) 274156960Sumestatic int addr4sort(struct addrinfo *, res_state); 275121426Sume#endif 276105943Sumestatic int _dns_getaddrinfo(void *, void *, va_list); 277144634Sumestatic void _sethtent(FILE **); 278144634Sumestatic void _endhtent(FILE **); 279144634Sumestatic struct addrinfo *_gethtent(FILE **, const char *, 280144634Sume const struct addrinfo *); 28192905Sobrienstatic int _files_getaddrinfo(void *, void *, va_list); 28261877Sume#ifdef YP 28392905Sobrienstatic struct addrinfo *_yphostent(char *, const struct addrinfo *); 28492905Sobrienstatic int _yp_getaddrinfo(void *, void *, va_list); 28561877Sume#endif 286158115Sume#ifdef NS_CACHING 287158115Sumestatic int addrinfo_id_func(char *, size_t *, va_list, void *); 288158115Sumestatic int addrinfo_marshal_func(char *, size_t *, void *, va_list, void *); 289158115Sumestatic int addrinfo_unmarshal_func(char *, size_t, void *, va_list, void *); 290158115Sume#endif 29161877Sume 292156960Sumestatic int res_queryN(const char *, struct res_target *, res_state); 293156960Sumestatic int res_searchN(const char *, struct res_target *, res_state); 29492941Sobrienstatic int res_querydomainN(const char *, const char *, 295156960Sume struct res_target *, res_state); 29661877Sume 29755163Sshin/* XXX macros that make external reference is BAD. */ 29855163Sshin 299105940Sume#define GET_AI(ai, afd, addr) \ 30055163Sshindo { \ 30155163Sshin /* external reference: pai, error, and label free */ \ 30255163Sshin (ai) = get_ai(pai, (afd), (addr)); \ 30355163Sshin if ((ai) == NULL) { \ 30455163Sshin error = EAI_MEMORY; \ 30555163Sshin goto free; \ 30655163Sshin } \ 30761877Sume} while (/*CONSTCOND*/0) 30855163Sshin 309105940Sume#define GET_PORT(ai, serv) \ 31055163Sshindo { \ 31155163Sshin /* external reference: error and label free */ \ 31255163Sshin error = get_port((ai), (serv), 0); \ 31355163Sshin if (error != 0) \ 31455163Sshin goto free; \ 31561877Sume} while (/*CONSTCOND*/0) 31655163Sshin 317105940Sume#define GET_CANONNAME(ai, str) \ 31855163Sshindo { \ 31955163Sshin /* external reference: pai, error and label free */ \ 32055163Sshin error = get_canonname(pai, (ai), (str)); \ 32155163Sshin if (error != 0) \ 32255163Sshin goto free; \ 32361877Sume} while (/*CONSTCOND*/0) 32455163Sshin 325105940Sume#define ERR(err) \ 32655163Sshindo { \ 32755163Sshin /* external reference: error, and label bad */ \ 32855163Sshin error = (err); \ 32955163Sshin goto bad; \ 33061877Sume /*NOTREACHED*/ \ 33161877Sume} while (/*CONSTCOND*/0) 33255163Sshin 333105940Sume#define MATCH_FAMILY(x, y, w) \ 33461877Sume ((x) == (y) || (/*CONSTCOND*/(w) && ((x) == PF_UNSPEC || (y) == PF_UNSPEC))) 335105940Sume#define MATCH(x, y, w) \ 33661877Sume ((x) == (y) || (/*CONSTCOND*/(w) && ((x) == ANY || (y) == ANY))) 33755163Sshin 33855163Sshinvoid 339157119Sumefreeaddrinfo(struct addrinfo *ai) 34055163Sshin{ 34155163Sshin struct addrinfo *next; 34255163Sshin 34355163Sshin do { 34455163Sshin next = ai->ai_next; 34555163Sshin if (ai->ai_canonname) 34655163Sshin free(ai->ai_canonname); 34755163Sshin /* no need to free(ai->ai_addr) */ 34855163Sshin free(ai); 34961877Sume ai = next; 35061877Sume } while (ai); 35155163Sshin} 35255163Sshin 35355163Sshinstatic int 354160593Sumestr2number(const char *p, int *portp) 35555163Sshin{ 35662836Sitojun char *ep; 357140908Sume unsigned long v; 35862836Sitojun 35962836Sitojun if (*p == '\0') 360140908Sume return -1; 36162836Sitojun ep = NULL; 362105943Sume errno = 0; 363140908Sume v = strtoul(p, &ep, 10); 364160593Sume if (errno == 0 && ep && *ep == '\0' && v <= UINT_MAX) { 365160593Sume *portp = v; 366160593Sume return 0; 367160593Sume } else 368140908Sume return -1; 36955163Sshin} 37055163Sshin 37155163Sshinint 372157119Sumegetaddrinfo(const char *hostname, const char *servname, 373157119Sume const struct addrinfo *hints, struct addrinfo **res) 37455163Sshin{ 37555163Sshin struct addrinfo sentinel; 37655163Sshin struct addrinfo *cur; 37755163Sshin int error = 0; 378190525Sume struct addrinfo ai, ai0, *afai; 37955163Sshin struct addrinfo *pai; 380190525Sume const struct afd *afd; 38155163Sshin const struct explore *ex; 382190525Sume struct addrinfo *afailist[sizeof(afdl)/sizeof(afdl[0])]; 383190525Sume struct addrinfo *afai_unspec; 384190525Sume int found; 385121747Sume int numeric = 0; 38655163Sshin 387190525Sume /* ensure we return NULL on errors */ 388190525Sume *res = NULL; 389190525Sume 390190525Sume memset(&ai, 0, sizeof(ai)); 391190525Sume 392190525Sume memset(afailist, 0, sizeof(afailist)); 393190525Sume afai_unspec = NULL; 394190525Sume 39561877Sume memset(&sentinel, 0, sizeof(sentinel)); 39655163Sshin cur = &sentinel; 39755163Sshin pai = &ai; 39855163Sshin pai->ai_flags = 0; 39955163Sshin pai->ai_family = PF_UNSPEC; 40055163Sshin pai->ai_socktype = ANY; 40155163Sshin pai->ai_protocol = ANY; 40255163Sshin pai->ai_addrlen = 0; 40355163Sshin pai->ai_canonname = NULL; 40455163Sshin pai->ai_addr = NULL; 40555163Sshin pai->ai_next = NULL; 40655163Sshin 40755163Sshin if (hostname == NULL && servname == NULL) 40855163Sshin return EAI_NONAME; 40955163Sshin if (hints) { 41055163Sshin /* error check for hints */ 41155163Sshin if (hints->ai_addrlen || hints->ai_canonname || 41255163Sshin hints->ai_addr || hints->ai_next) 41355163Sshin ERR(EAI_BADHINTS); /* xxx */ 41455163Sshin if (hints->ai_flags & ~AI_MASK) 41555163Sshin ERR(EAI_BADFLAGS); 41655163Sshin switch (hints->ai_family) { 41755163Sshin case PF_UNSPEC: 41855163Sshin case PF_INET: 41955163Sshin#ifdef INET6 42055163Sshin case PF_INET6: 42155163Sshin#endif 42255163Sshin break; 42355163Sshin default: 42455163Sshin ERR(EAI_FAMILY); 42555163Sshin } 42655163Sshin memcpy(pai, hints, sizeof(*pai)); 42755163Sshin 42855163Sshin /* 42955163Sshin * if both socktype/protocol are specified, check if they 43055163Sshin * are meaningful combination. 43155163Sshin */ 43255163Sshin if (pai->ai_socktype != ANY && pai->ai_protocol != ANY) { 43361877Sume for (ex = explore; ex->e_af >= 0; ex++) { 434190525Sume if (!MATCH_FAMILY(pai->ai_family, ex->e_af, 435190525Sume WILD_AF(ex))) 43661877Sume continue; 437190525Sume if (!MATCH(pai->ai_socktype, ex->e_socktype, 438190525Sume WILD_SOCKTYPE(ex))) 43955163Sshin continue; 440190525Sume if (!MATCH(pai->ai_protocol, ex->e_protocol, 441190525Sume WILD_PROTOCOL(ex))) 44255163Sshin continue; 443190525Sume 444190525Sume /* matched */ 445190525Sume break; 44655163Sshin } 447190416Sume 448190416Sume if (ex->e_af < 0) 449190416Sume ERR(EAI_BADHINTS); 45055163Sshin } 45155163Sshin } 45255163Sshin 45361877Sume /* 45461877Sume * check for special cases. (1) numeric servname is disallowed if 45561877Sume * socktype/protocol are left unspecified. (2) servname is disallowed 45661877Sume * for raw and other inet{,6} sockets. 45755163Sshin */ 45855163Sshin if (MATCH_FAMILY(pai->ai_family, PF_INET, 1) 45961877Sume#ifdef PF_INET6 460121474Sume || MATCH_FAMILY(pai->ai_family, PF_INET6, 1) 46155163Sshin#endif 46255163Sshin ) { 46361877Sume ai0 = *pai; /* backup *pai */ 46455163Sshin 46561877Sume if (pai->ai_family == PF_UNSPEC) { 46661877Sume#ifdef PF_INET6 46755163Sshin pai->ai_family = PF_INET6; 46855163Sshin#else 46955163Sshin pai->ai_family = PF_INET; 47055163Sshin#endif 47161877Sume } 47255163Sshin error = get_portmatch(pai, servname); 47355163Sshin if (error) 474236695Sdim goto bad; 47561877Sume 47661877Sume *pai = ai0; 47755163Sshin } 47855163Sshin 47961877Sume ai0 = *pai; 48061877Sume 481190525Sume /* 482190525Sume * NULL hostname, or numeric hostname. 483190525Sume * If numeric representation of AF1 can be interpreted as FQDN 484190525Sume * representation of AF2, we need to think again about the code below. 485190525Sume */ 486190525Sume found = 0; 487190525Sume for (afd = afdl; afd->a_af; afd++) { 48855163Sshin *pai = ai0; 48955163Sshin 490190525Sume if (!MATCH_FAMILY(pai->ai_family, afd->a_af, 1)) 49155163Sshin continue; 49261877Sume 49355163Sshin if (pai->ai_family == PF_UNSPEC) 494190525Sume pai->ai_family = afd->a_af; 49555163Sshin 496190525Sume if (hostname == NULL) { 497190525Sume error = explore_null(pai, servname, 498190525Sume &afailist[afd - afdl]); 499190525Sume 500190525Sume /* 501190525Sume * Errors from explore_null should be unexpected and 502190525Sume * be caught to avoid returning an incomplete result. 503190525Sume */ 504190525Sume if (error != 0) 505190525Sume goto bad; 506190525Sume } else { 507140906Sume error = explore_numeric_scope(pai, hostname, servname, 508190525Sume &afailist[afd - afdl]); 50955163Sshin 510190525Sume /* 511190525Sume * explore_numeric_scope returns an error for address 512190525Sume * families that do not match that of hostname. 513190525Sume * Thus we should not catch the error at this moment. 514190525Sume */ 515190525Sume } 516121474Sume 517190525Sume if (!error && afailist[afd - afdl]) 518190525Sume found++; 51955163Sshin } 520190525Sume if (found) { 521121747Sume numeric = 1; 522190525Sume goto globcopy; 523121747Sume } 524121474Sume 525121425Sume if (hostname == NULL) 526121425Sume ERR(EAI_NONAME); /* used to be EAI_NODATA */ 52755163Sshin if (pai->ai_flags & AI_NUMERICHOST) 52890053Sroam ERR(EAI_NONAME); 52955163Sshin 530121474Sume if ((pai->ai_flags & AI_ADDRCONFIG) != 0 && !addrconfig(&ai0)) 531121474Sume ERR(EAI_FAIL); 532121474Sume 53361877Sume /* 53461877Sume * hostname as alphabetical name. 53561877Sume */ 536190525Sume *pai = ai0; 537190525Sume error = explore_fqdn(pai, hostname, servname, &afai_unspec); 538190525Sume 539190525Sumeglobcopy: 54061877Sume for (ex = explore; ex->e_af >= 0; ex++) { 54161877Sume *pai = ai0; 54255163Sshin 543190525Sume if (!MATCH_FAMILY(pai->ai_family, ex->e_af, WILD_AF(ex))) 54461877Sume continue; 545121474Sume if (!MATCH(pai->ai_socktype, ex->e_socktype, 546190525Sume WILD_SOCKTYPE(ex))) 54761877Sume continue; 548121474Sume if (!MATCH(pai->ai_protocol, ex->e_protocol, 549190525Sume WILD_PROTOCOL(ex))) 55061877Sume continue; 55155163Sshin 552190525Sume if (pai->ai_family == PF_UNSPEC) 553190525Sume pai->ai_family = ex->e_af; 55461877Sume if (pai->ai_socktype == ANY && ex->e_socktype != ANY) 55561877Sume pai->ai_socktype = ex->e_socktype; 55661877Sume if (pai->ai_protocol == ANY && ex->e_protocol != ANY) 55761877Sume pai->ai_protocol = ex->e_protocol; 55861877Sume 559190525Sume /* 560190525Sume * if the servname does not match socktype/protocol, ignore it. 561190525Sume */ 562190525Sume if (get_portmatch(pai, servname) != 0) 563190525Sume continue; 56461877Sume 565190525Sume if (afai_unspec) 566190525Sume afai = afai_unspec; 567190525Sume else { 568190525Sume if ((afd = find_afd(pai->ai_family)) == NULL) 569190525Sume continue; 570190525Sume /* XXX assumes that afd points inside afdl[] */ 571190525Sume afai = afailist[afd - afdl]; 572190525Sume } 573190525Sume if (!afai) 574190525Sume continue; 575190525Sume 576190525Sume error = explore_copy(pai, afai, &cur->ai_next); 577190525Sume if (error != 0) 578190525Sume goto bad; 579190525Sume 58061877Sume while (cur && cur->ai_next) 58161877Sume cur = cur->ai_next; 58255163Sshin } 58355163Sshin 584121747Sume /* 585121747Sume * ensure we return either: 586121747Sume * - error == 0, non-NULL *res 587121747Sume * - error != 0, NULL *res 588121747Sume */ 58961877Sume if (error == 0) { 59061877Sume if (sentinel.ai_next) { 591121747Sume /* 592121747Sume * If the returned entry is for an active connection, 593121747Sume * and the given name is not numeric, reorder the 594121747Sume * list, so that the application would try the list 595172052Sjinmei * in the most efficient order. Since the head entry 596172052Sjinmei * of the original list may contain ai_canonname and 597172052Sjinmei * that entry may be moved elsewhere in the new list, 598172052Sjinmei * we keep the pointer and will restore it in the new 599172052Sjinmei * head entry. (Note that RFC3493 requires the head 600172052Sjinmei * entry store it when requested by the caller). 601121747Sume */ 602121747Sume if (hints == NULL || !(hints->ai_flags & AI_PASSIVE)) { 603172052Sjinmei if (!numeric) { 604172052Sjinmei char *canonname; 605172052Sjinmei 606172052Sjinmei canonname = 607172052Sjinmei sentinel.ai_next->ai_canonname; 608172052Sjinmei sentinel.ai_next->ai_canonname = NULL; 609121747Sume (void)reorder(&sentinel); 610172052Sjinmei if (sentinel.ai_next->ai_canonname == 611172052Sjinmei NULL) { 612172052Sjinmei sentinel.ai_next->ai_canonname 613172052Sjinmei = canonname; 614172052Sjinmei } else if (canonname != NULL) 615172052Sjinmei free(canonname); 616172052Sjinmei } 617121747Sume } 61861877Sume *res = sentinel.ai_next; 61961877Sume } else 62061877Sume error = EAI_FAIL; 62155163Sshin } 622190525Sume 623121747Sumebad: 624190525Sume if (afai_unspec) 625190525Sume freeaddrinfo(afai_unspec); 626190525Sume for (afd = afdl; afd->a_af; afd++) { 627190525Sume if (afailist[afd - afdl]) 628190525Sume freeaddrinfo(afailist[afd - afdl]); 629190525Sume } 630190525Sume if (!*res) 631190525Sume if (sentinel.ai_next) 632190525Sume freeaddrinfo(sentinel.ai_next); 633190525Sume 634190525Sume return (error); 63555163Sshin} 63655163Sshin 637121747Sumestatic int 638157119Sumereorder(struct addrinfo *sentinel) 639121747Sume{ 640121747Sume struct addrinfo *ai, **aip; 641121747Sume struct ai_order *aio; 642121747Sume int i, n; 643121747Sume struct policyhead policyhead; 644121747Sume 645121747Sume /* count the number of addrinfo elements for sorting. */ 646121747Sume for (n = 0, ai = sentinel->ai_next; ai != NULL; ai = ai->ai_next, n++) 647121747Sume ; 648121747Sume 649121747Sume /* 650121747Sume * If the number is small enough, we can skip the reordering process. 651121747Sume */ 652121747Sume if (n <= 1) 653121747Sume return(n); 654121747Sume 655121747Sume /* allocate a temporary array for sort and initialization of it. */ 656121747Sume if ((aio = malloc(sizeof(*aio) * n)) == NULL) 657121747Sume return(n); /* give up reordering */ 658121747Sume memset(aio, 0, sizeof(*aio) * n); 659121747Sume 660121747Sume /* retrieve address selection policy from the kernel */ 661121747Sume TAILQ_INIT(&policyhead); 662121747Sume if (!get_addrselectpolicy(&policyhead)) { 663121747Sume /* no policy is installed into kernel, we don't sort. */ 664121747Sume free(aio); 665121747Sume return (n); 666121747Sume } 667121747Sume 668121747Sume for (i = 0, ai = sentinel->ai_next; i < n; ai = ai->ai_next, i++) { 669121747Sume aio[i].aio_ai = ai; 670121747Sume aio[i].aio_dstscope = gai_addr2scopetype(ai->ai_addr); 671121747Sume aio[i].aio_dstpolicy = match_addrselectpolicy(ai->ai_addr, 672121747Sume &policyhead); 673129901Sume set_source(&aio[i], &policyhead); 674121747Sume } 675121747Sume 676121747Sume /* perform sorting. */ 677121747Sume qsort(aio, n, sizeof(*aio), comp_dst); 678121747Sume 679121747Sume /* reorder the addrinfo chain. */ 680121747Sume for (i = 0, aip = &sentinel->ai_next; i < n; i++) { 681121747Sume *aip = aio[i].aio_ai; 682121747Sume aip = &aio[i].aio_ai->ai_next; 683121747Sume } 684121747Sume *aip = NULL; 685121747Sume 686121747Sume /* cleanup and return */ 687121747Sume free(aio); 688121747Sume free_addrselectpolicy(&policyhead); 689121747Sume return(n); 690121747Sume} 691121747Sume 692121747Sumestatic int 693157119Sumeget_addrselectpolicy(struct policyhead *head) 694121747Sume{ 695121747Sume#ifdef INET6 696121747Sume int mib[] = { CTL_NET, PF_INET6, IPPROTO_IPV6, IPV6CTL_ADDRCTLPOLICY }; 697121747Sume size_t l; 698121747Sume char *buf; 699121747Sume struct in6_addrpolicy *pol, *ep; 700121747Sume 701121747Sume if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), NULL, &l, NULL, 0) < 0) 702121747Sume return (0); 703238599Semax if (l == 0) 704238599Semax return (0); 705121747Sume if ((buf = malloc(l)) == NULL) 706121747Sume return (0); 707121747Sume if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), buf, &l, NULL, 0) < 0) { 708121747Sume free(buf); 709121747Sume return (0); 710121747Sume } 711121747Sume 712121747Sume ep = (struct in6_addrpolicy *)(buf + l); 713121747Sume for (pol = (struct in6_addrpolicy *)buf; pol + 1 <= ep; pol++) { 714121747Sume struct policyqueue *new; 715121747Sume 716121747Sume if ((new = malloc(sizeof(*new))) == NULL) { 717121747Sume free_addrselectpolicy(head); /* make the list empty */ 718121747Sume break; 719121747Sume } 720121747Sume new->pc_policy = *pol; 721121747Sume TAILQ_INSERT_TAIL(head, new, pc_entry); 722121747Sume } 723121747Sume 724121747Sume free(buf); 725121747Sume return (1); 726121747Sume#else 727121747Sume return (0); 728121747Sume#endif 729121747Sume} 730121747Sume 731121747Sumestatic void 732157119Sumefree_addrselectpolicy(struct policyhead *head) 733121747Sume{ 734121747Sume struct policyqueue *ent, *nent; 735121747Sume 736121747Sume for (ent = TAILQ_FIRST(head); ent; ent = nent) { 737121747Sume nent = TAILQ_NEXT(ent, pc_entry); 738121747Sume TAILQ_REMOVE(head, ent, pc_entry); 739121747Sume free(ent); 740121747Sume } 741121747Sume} 742121747Sume 743121747Sumestatic struct policyqueue * 744157119Sumematch_addrselectpolicy(struct sockaddr *addr, struct policyhead *head) 745121747Sume{ 746121747Sume#ifdef INET6 747121747Sume struct policyqueue *ent, *bestent = NULL; 748121747Sume struct in6_addrpolicy *pol; 749121747Sume int matchlen, bestmatchlen = -1; 750121747Sume u_char *mp, *ep, *k, *p, m; 751121747Sume struct sockaddr_in6 key; 752121747Sume 753121747Sume switch(addr->sa_family) { 754121747Sume case AF_INET6: 755121747Sume key = *(struct sockaddr_in6 *)addr; 756121747Sume break; 757121747Sume case AF_INET: 758121747Sume /* convert the address into IPv4-mapped IPv6 address. */ 759121747Sume memset(&key, 0, sizeof(key)); 760121747Sume key.sin6_family = AF_INET6; 761121747Sume key.sin6_len = sizeof(key); 762121747Sume key.sin6_addr.s6_addr[10] = 0xff; 763121747Sume key.sin6_addr.s6_addr[11] = 0xff; 764121747Sume memcpy(&key.sin6_addr.s6_addr[12], 765121747Sume &((struct sockaddr_in *)addr)->sin_addr, 4); 766121747Sume break; 767121747Sume default: 768121747Sume return(NULL); 769121747Sume } 770121747Sume 771121747Sume for (ent = TAILQ_FIRST(head); ent; ent = TAILQ_NEXT(ent, pc_entry)) { 772121747Sume pol = &ent->pc_policy; 773121747Sume matchlen = 0; 774121747Sume 775121747Sume mp = (u_char *)&pol->addrmask.sin6_addr; 776121747Sume ep = mp + 16; /* XXX: scope field? */ 777121747Sume k = (u_char *)&key.sin6_addr; 778121747Sume p = (u_char *)&pol->addr.sin6_addr; 779121747Sume for (; mp < ep && *mp; mp++, k++, p++) { 780121747Sume m = *mp; 781121747Sume if ((*k & m) != *p) 782121747Sume goto next; /* not match */ 783121747Sume if (m == 0xff) /* short cut for a typical case */ 784121747Sume matchlen += 8; 785121747Sume else { 786121747Sume while (m >= 0x80) { 787121747Sume matchlen++; 788121747Sume m <<= 1; 789121747Sume } 790121747Sume } 791121747Sume } 792121747Sume 793121747Sume /* matched. check if this is better than the current best. */ 794121747Sume if (matchlen > bestmatchlen) { 795121747Sume bestent = ent; 796121747Sume bestmatchlen = matchlen; 797121747Sume } 798121747Sume 799121747Sume next: 800121747Sume continue; 801121747Sume } 802121747Sume 803121747Sume return(bestent); 804121747Sume#else 805121747Sume return(NULL); 806121747Sume#endif 807121747Sume 808121747Sume} 809121747Sume 810129901Sumestatic void 811157119Sumeset_source(struct ai_order *aio, struct policyhead *ph) 812129901Sume{ 813129901Sume struct addrinfo ai = *aio->aio_ai; 814129901Sume struct sockaddr_storage ss; 815145786Sume socklen_t srclen; 816145786Sume int s; 817129901Sume 818129901Sume /* set unspec ("no source is available"), just in case */ 819129901Sume aio->aio_srcsa.sa_family = AF_UNSPEC; 820129901Sume aio->aio_srcscope = -1; 821129901Sume 822129901Sume switch(ai.ai_family) { 823129901Sume case AF_INET: 824129901Sume#ifdef INET6 825129901Sume case AF_INET6: 826129901Sume#endif 827129901Sume break; 828129901Sume default: /* ignore unsupported AFs explicitly */ 829129901Sume return; 830129901Sume } 831129901Sume 832129901Sume /* XXX: make a dummy addrinfo to call connect() */ 833129901Sume ai.ai_socktype = SOCK_DGRAM; 834129901Sume ai.ai_protocol = IPPROTO_UDP; /* is UDP too specific? */ 835129901Sume ai.ai_next = NULL; 836129901Sume memset(&ss, 0, sizeof(ss)); 837129901Sume memcpy(&ss, ai.ai_addr, ai.ai_addrlen); 838129901Sume ai.ai_addr = (struct sockaddr *)&ss; 839129901Sume get_port(&ai, "1", 0); 840129901Sume 841129901Sume /* open a socket to get the source address for the given dst */ 842255328Sjilles if ((s = _socket(ai.ai_family, ai.ai_socktype | SOCK_CLOEXEC, 843255328Sjilles ai.ai_protocol)) < 0) 844129901Sume return; /* give up */ 845129901Sume if (_connect(s, ai.ai_addr, ai.ai_addrlen) < 0) 846129901Sume goto cleanup; 847129901Sume srclen = ai.ai_addrlen; 848129901Sume if (_getsockname(s, &aio->aio_srcsa, &srclen) < 0) { 849129901Sume aio->aio_srcsa.sa_family = AF_UNSPEC; 850129901Sume goto cleanup; 851129901Sume } 852129901Sume aio->aio_srcscope = gai_addr2scopetype(&aio->aio_srcsa); 853129901Sume aio->aio_srcpolicy = match_addrselectpolicy(&aio->aio_srcsa, ph); 854129901Sume aio->aio_matchlen = matchlen(&aio->aio_srcsa, aio->aio_ai->ai_addr); 855129901Sume#ifdef INET6 856129901Sume if (ai.ai_family == AF_INET6) { 857129901Sume struct in6_ifreq ifr6; 858129901Sume u_int32_t flags6; 859129901Sume 860129901Sume memset(&ifr6, 0, sizeof(ifr6)); 861129901Sume memcpy(&ifr6.ifr_addr, ai.ai_addr, ai.ai_addrlen); 862129901Sume if (_ioctl(s, SIOCGIFAFLAG_IN6, &ifr6) == 0) { 863129901Sume flags6 = ifr6.ifr_ifru.ifru_flags6; 864129901Sume if ((flags6 & IN6_IFF_DEPRECATED)) 865129901Sume aio->aio_srcflag |= AIO_SRCFLAG_DEPRECATED; 866129901Sume } 867129901Sume } 868129901Sume#endif 869129901Sume 870129901Sume cleanup: 871129901Sume _close(s); 872129901Sume return; 873129901Sume} 874129901Sume 875121747Sumestatic int 876157119Sumematchlen(struct sockaddr *src, struct sockaddr *dst) 877129901Sume{ 878129901Sume int match = 0; 879129901Sume u_char *s, *d; 880129901Sume u_char *lim, r; 881129901Sume int addrlen; 882129901Sume 883129901Sume switch (src->sa_family) { 884129901Sume#ifdef INET6 885129901Sume case AF_INET6: 886129901Sume s = (u_char *)&((struct sockaddr_in6 *)src)->sin6_addr; 887129901Sume d = (u_char *)&((struct sockaddr_in6 *)dst)->sin6_addr; 888129901Sume addrlen = sizeof(struct in6_addr); 889129901Sume lim = s + addrlen; 890129901Sume break; 891129901Sume#endif 892129901Sume case AF_INET: 893146222Sgnn s = (u_char *)&((struct sockaddr_in *)src)->sin_addr; 894146222Sgnn d = (u_char *)&((struct sockaddr_in *)dst)->sin_addr; 895129901Sume addrlen = sizeof(struct in_addr); 896129901Sume lim = s + addrlen; 897129901Sume break; 898129901Sume default: 899129901Sume return(0); 900129901Sume } 901129901Sume 902129901Sume while (s < lim) 903129901Sume if ((r = (*d++ ^ *s++)) != 0) { 904129901Sume while (r < addrlen * 8) { 905129901Sume match++; 906129901Sume r <<= 1; 907129901Sume } 908129901Sume break; 909129901Sume } else 910129901Sume match += 8; 911129901Sume return(match); 912129901Sume} 913129901Sume 914129901Sumestatic int 915157119Sumecomp_dst(const void *arg1, const void *arg2) 916121747Sume{ 917121747Sume const struct ai_order *dst1 = arg1, *dst2 = arg2; 918121747Sume 919121747Sume /* 920121747Sume * Rule 1: Avoid unusable destinations. 921121747Sume * XXX: we currently do not consider if an appropriate route exists. 922121747Sume */ 923121747Sume if (dst1->aio_srcsa.sa_family != AF_UNSPEC && 924121747Sume dst2->aio_srcsa.sa_family == AF_UNSPEC) { 925121747Sume return(-1); 926121747Sume } 927121747Sume if (dst1->aio_srcsa.sa_family == AF_UNSPEC && 928121747Sume dst2->aio_srcsa.sa_family != AF_UNSPEC) { 929121747Sume return(1); 930121747Sume } 931121747Sume 932121747Sume /* Rule 2: Prefer matching scope. */ 933121747Sume if (dst1->aio_dstscope == dst1->aio_srcscope && 934121747Sume dst2->aio_dstscope != dst2->aio_srcscope) { 935121747Sume return(-1); 936121747Sume } 937121747Sume if (dst1->aio_dstscope != dst1->aio_srcscope && 938121747Sume dst2->aio_dstscope == dst2->aio_srcscope) { 939121747Sume return(1); 940121747Sume } 941121747Sume 942121747Sume /* Rule 3: Avoid deprecated addresses. */ 943121747Sume if (dst1->aio_srcsa.sa_family != AF_UNSPEC && 944121747Sume dst2->aio_srcsa.sa_family != AF_UNSPEC) { 945121747Sume if (!(dst1->aio_srcflag & AIO_SRCFLAG_DEPRECATED) && 946121747Sume (dst2->aio_srcflag & AIO_SRCFLAG_DEPRECATED)) { 947121747Sume return(-1); 948121747Sume } 949121747Sume if ((dst1->aio_srcflag & AIO_SRCFLAG_DEPRECATED) && 950121747Sume !(dst2->aio_srcflag & AIO_SRCFLAG_DEPRECATED)) { 951121747Sume return(1); 952121747Sume } 953121747Sume } 954121747Sume 955121747Sume /* Rule 4: Prefer home addresses. */ 956121747Sume /* XXX: not implemented yet */ 957121747Sume 958121747Sume /* Rule 5: Prefer matching label. */ 959121747Sume#ifdef INET6 960121747Sume if (dst1->aio_srcpolicy && dst1->aio_dstpolicy && 961121747Sume dst1->aio_srcpolicy->pc_policy.label == 962121747Sume dst1->aio_dstpolicy->pc_policy.label && 963121747Sume (dst2->aio_srcpolicy == NULL || dst2->aio_dstpolicy == NULL || 964121747Sume dst2->aio_srcpolicy->pc_policy.label != 965121747Sume dst2->aio_dstpolicy->pc_policy.label)) { 966121747Sume return(-1); 967121747Sume } 968121747Sume if (dst2->aio_srcpolicy && dst2->aio_dstpolicy && 969121747Sume dst2->aio_srcpolicy->pc_policy.label == 970121747Sume dst2->aio_dstpolicy->pc_policy.label && 971121747Sume (dst1->aio_srcpolicy == NULL || dst1->aio_dstpolicy == NULL || 972121747Sume dst1->aio_srcpolicy->pc_policy.label != 973121747Sume dst1->aio_dstpolicy->pc_policy.label)) { 974121747Sume return(1); 975121747Sume } 976121747Sume#endif 977121747Sume 978121747Sume /* Rule 6: Prefer higher precedence. */ 979121747Sume#ifdef INET6 980121747Sume if (dst1->aio_dstpolicy && 981121747Sume (dst2->aio_dstpolicy == NULL || 982121747Sume dst1->aio_dstpolicy->pc_policy.preced > 983121747Sume dst2->aio_dstpolicy->pc_policy.preced)) { 984121747Sume return(-1); 985121747Sume } 986121747Sume if (dst2->aio_dstpolicy && 987121747Sume (dst1->aio_dstpolicy == NULL || 988121747Sume dst2->aio_dstpolicy->pc_policy.preced > 989121747Sume dst1->aio_dstpolicy->pc_policy.preced)) { 990121747Sume return(1); 991121747Sume } 992121747Sume#endif 993121747Sume 994121747Sume /* Rule 7: Prefer native transport. */ 995121747Sume /* XXX: not implemented yet */ 996121747Sume 997121747Sume /* Rule 8: Prefer smaller scope. */ 998121747Sume if (dst1->aio_dstscope >= 0 && 999121747Sume dst1->aio_dstscope < dst2->aio_dstscope) { 1000121747Sume return(-1); 1001121747Sume } 1002121747Sume if (dst2->aio_dstscope >= 0 && 1003121747Sume dst2->aio_dstscope < dst1->aio_dstscope) { 1004121747Sume return(1); 1005121747Sume } 1006121747Sume 1007121747Sume /* 1008121747Sume * Rule 9: Use longest matching prefix. 1009121747Sume * We compare the match length in a same AF only. 1010121747Sume */ 1011121747Sume if (dst1->aio_ai->ai_addr->sa_family == 1012268051Sume dst2->aio_ai->ai_addr->sa_family && 1013268051Sume dst1->aio_ai->ai_addr->sa_family != AF_INET) { 1014121747Sume if (dst1->aio_matchlen > dst2->aio_matchlen) { 1015121747Sume return(-1); 1016121747Sume } 1017121747Sume if (dst1->aio_matchlen < dst2->aio_matchlen) { 1018121747Sume return(1); 1019121747Sume } 1020121747Sume } 1021121747Sume 1022121747Sume /* Rule 10: Otherwise, leave the order unchanged. */ 1023121747Sume return(-1); 1024121747Sume} 1025121747Sume 102655163Sshin/* 1027121747Sume * Copy from scope.c. 1028121747Sume * XXX: we should standardize the functions and link them as standard 1029121747Sume * library. 1030121747Sume */ 1031121747Sumestatic int 1032157119Sumegai_addr2scopetype(struct sockaddr *sa) 1033121747Sume{ 1034121747Sume#ifdef INET6 1035121747Sume struct sockaddr_in6 *sa6; 1036121747Sume#endif 1037121747Sume struct sockaddr_in *sa4; 1038121747Sume 1039121747Sume switch(sa->sa_family) { 1040121747Sume#ifdef INET6 1041121747Sume case AF_INET6: 1042121747Sume sa6 = (struct sockaddr_in6 *)sa; 1043121747Sume if (IN6_IS_ADDR_MULTICAST(&sa6->sin6_addr)) { 1044121747Sume /* just use the scope field of the multicast address */ 1045121747Sume return(sa6->sin6_addr.s6_addr[2] & 0x0f); 1046121747Sume } 1047121747Sume /* 1048121747Sume * Unicast addresses: map scope type to corresponding scope 1049121747Sume * value defined for multcast addresses. 1050121747Sume * XXX: hardcoded scope type values are bad... 1051121747Sume */ 1052121747Sume if (IN6_IS_ADDR_LOOPBACK(&sa6->sin6_addr)) 1053121747Sume return(1); /* node local scope */ 1054121747Sume if (IN6_IS_ADDR_LINKLOCAL(&sa6->sin6_addr)) 1055121747Sume return(2); /* link-local scope */ 1056121747Sume if (IN6_IS_ADDR_SITELOCAL(&sa6->sin6_addr)) 1057121747Sume return(5); /* site-local scope */ 1058121747Sume return(14); /* global scope */ 1059121747Sume break; 1060121747Sume#endif 1061121747Sume case AF_INET: 1062121747Sume /* 1063121747Sume * IPv4 pseudo scoping according to RFC 3484. 1064121747Sume */ 1065121747Sume sa4 = (struct sockaddr_in *)sa; 1066121747Sume /* IPv4 autoconfiguration addresses have link-local scope. */ 1067121747Sume if (((u_char *)&sa4->sin_addr)[0] == 169 && 1068121747Sume ((u_char *)&sa4->sin_addr)[1] == 254) 1069121747Sume return(2); 1070121747Sume /* Private addresses have site-local scope. */ 1071121747Sume if (((u_char *)&sa4->sin_addr)[0] == 10 || 1072121747Sume (((u_char *)&sa4->sin_addr)[0] == 172 && 1073121747Sume (((u_char *)&sa4->sin_addr)[1] & 0xf0) == 16) || 1074121747Sume (((u_char *)&sa4->sin_addr)[0] == 192 && 1075121747Sume ((u_char *)&sa4->sin_addr)[1] == 168)) 1076129905Sume return(14); /* XXX: It should be 5 unless NAT */ 1077121747Sume /* Loopback addresses have link-local scope. */ 1078121747Sume if (((u_char *)&sa4->sin_addr)[0] == 127) 1079121747Sume return(2); 1080121747Sume return(14); 1081121747Sume break; 1082121747Sume default: 1083121747Sume errno = EAFNOSUPPORT; /* is this a good error? */ 1084121747Sume return(-1); 1085121747Sume } 1086121747Sume} 1087121747Sume 1088190525Sumestatic int 1089190525Sumeexplore_copy(const struct addrinfo *pai, const struct addrinfo *src0, 1090190525Sume struct addrinfo **res) 1091190525Sume{ 1092190525Sume int error; 1093190525Sume struct addrinfo sentinel, *cur; 1094190525Sume const struct addrinfo *src; 1095190525Sume 1096190525Sume error = 0; 1097190525Sume sentinel.ai_next = NULL; 1098190525Sume cur = &sentinel; 1099190525Sume 1100190525Sume for (src = src0; src != NULL; src = src->ai_next) { 1101190525Sume if (src->ai_family != pai->ai_family) 1102190525Sume continue; 1103190525Sume 1104190525Sume cur->ai_next = copy_ai(src); 1105190525Sume if (!cur->ai_next) { 1106190525Sume error = EAI_MEMORY; 1107190525Sume goto fail; 1108190525Sume } 1109190525Sume 1110190525Sume cur->ai_next->ai_socktype = pai->ai_socktype; 1111190525Sume cur->ai_next->ai_protocol = pai->ai_protocol; 1112190525Sume cur = cur->ai_next; 1113190525Sume } 1114190525Sume 1115190525Sume *res = sentinel.ai_next; 1116190525Sume return 0; 1117190525Sume 1118190525Sumefail: 1119190525Sume freeaddrinfo(sentinel.ai_next); 1120190525Sume return error; 1121190525Sume} 1122190525Sume 1123121747Sume/* 112455163Sshin * hostname == NULL. 112555163Sshin * passive socket -> anyaddr (0.0.0.0 or ::) 112655163Sshin * non-passive socket -> localhost (127.0.0.1 or ::1) 112755163Sshin */ 112855163Sshinstatic int 1129157119Sumeexplore_null(const struct addrinfo *pai, const char *servname, 1130157119Sume struct addrinfo **res) 113155163Sshin{ 1132121474Sume int s; 113355163Sshin const struct afd *afd; 1134160551Sume struct addrinfo *ai; 113555163Sshin int error; 113655163Sshin 113755163Sshin *res = NULL; 1138160551Sume ai = NULL; 113955163Sshin 114055163Sshin /* 1141121474Sume * filter out AFs that are not supported by the kernel 1142121474Sume * XXX errno? 1143121474Sume */ 1144255328Sjilles s = _socket(pai->ai_family, SOCK_DGRAM | SOCK_CLOEXEC, 0); 1145121474Sume if (s < 0) { 1146121474Sume if (errno != EMFILE) 1147121474Sume return 0; 1148121474Sume } else 1149121474Sume _close(s); 1150121474Sume 115155163Sshin afd = find_afd(pai->ai_family); 115255163Sshin if (afd == NULL) 115355163Sshin return 0; 115455163Sshin 115561877Sume if (pai->ai_flags & AI_PASSIVE) { 1156160551Sume GET_AI(ai, afd, afd->a_addrany); 1157160551Sume GET_PORT(ai, servname); 115861877Sume } else { 1159160551Sume GET_AI(ai, afd, afd->a_loopback); 1160160551Sume GET_PORT(ai, servname); 116161877Sume } 116255163Sshin 1163160551Sume *res = ai; 116455163Sshin return 0; 116555163Sshin 116655163Sshinfree: 1167160551Sume if (ai != NULL) 1168160551Sume freeaddrinfo(ai); 116955163Sshin return error; 117055163Sshin} 117155163Sshin 117255163Sshin/* 117355163Sshin * numeric hostname 117455163Sshin */ 117555163Sshinstatic int 1176157119Sumeexplore_numeric(const struct addrinfo *pai, const char *hostname, 1177157119Sume const char *servname, struct addrinfo **res, const char *canonname) 117855163Sshin{ 117955163Sshin const struct afd *afd; 1180160551Sume struct addrinfo *ai; 118155163Sshin int error; 118255163Sshin char pton[PTON_MAX]; 118355163Sshin 118455163Sshin *res = NULL; 1185160551Sume ai = NULL; 118655163Sshin 118755163Sshin afd = find_afd(pai->ai_family); 118855163Sshin if (afd == NULL) 118955163Sshin return 0; 119055163Sshin 119162614Sitojun switch (afd->a_af) { 119262614Sitojun case AF_INET: 1193160552Sume /* 1194160552Sume * RFC3493 requires getaddrinfo() to accept AF_INET formats 1195160552Sume * that are accepted by inet_addr() and its family. The 1196160552Sume * accepted forms includes the "classful" one, which inet_pton 1197160552Sume * does not accept. So we need to separate the case for 1198160552Sume * AF_INET. 1199160552Sume */ 1200160553Sume if (inet_aton(hostname, (struct in_addr *)pton) != 1) 1201160553Sume return 0; 120262614Sitojun break; 120362614Sitojun default: 1204160553Sume if (inet_pton(afd->a_af, hostname, pton) != 1) 1205160553Sume return 0; 120662614Sitojun break; 120755163Sshin } 120855163Sshin 1209160553Sume if (pai->ai_family == afd->a_af) { 1210160553Sume GET_AI(ai, afd, pton); 1211160553Sume GET_PORT(ai, servname); 1212160553Sume if ((pai->ai_flags & AI_CANONNAME)) { 1213160553Sume /* 1214160553Sume * Set the numeric address itself as the canonical 1215160553Sume * name, based on a clarification in RFC3493. 1216160553Sume */ 1217160553Sume GET_CANONNAME(ai, canonname); 1218160553Sume } 1219160553Sume } else { 1220160553Sume /* 1221160553Sume * XXX: This should not happen since we already matched the AF 1222160553Sume * by find_afd. 1223160553Sume */ 1224160553Sume ERR(EAI_FAMILY); 1225160553Sume } 1226160553Sume 1227160551Sume *res = ai; 122855163Sshin return 0; 122955163Sshin 123055163Sshinfree: 123155163Sshinbad: 1232160551Sume if (ai != NULL) 1233160551Sume freeaddrinfo(ai); 123455163Sshin return error; 123555163Sshin} 123655163Sshin 123755163Sshin/* 123855163Sshin * numeric hostname with scope 123955163Sshin */ 124055163Sshinstatic int 1241157119Sumeexplore_numeric_scope(const struct addrinfo *pai, const char *hostname, 1242157119Sume const char *servname, struct addrinfo **res) 124355163Sshin{ 124461877Sume#if !defined(SCOPE_DELIMITER) || !defined(INET6) 1245140906Sume return explore_numeric(pai, hostname, servname, res, hostname); 124655163Sshin#else 124755163Sshin const struct afd *afd; 124855163Sshin struct addrinfo *cur; 124955163Sshin int error; 125061877Sume char *cp, *hostname2 = NULL, *scope, *addr; 125155163Sshin struct sockaddr_in6 *sin6; 125255163Sshin 125355163Sshin afd = find_afd(pai->ai_family); 125455163Sshin if (afd == NULL) 125555163Sshin return 0; 125665532Snectar 125755163Sshin if (!afd->a_scoped) 1258140906Sume return explore_numeric(pai, hostname, servname, res, hostname); 125955163Sshin 126055163Sshin cp = strchr(hostname, SCOPE_DELIMITER); 126155163Sshin if (cp == NULL) 1262140906Sume return explore_numeric(pai, hostname, servname, res, hostname); 126355163Sshin 126455163Sshin /* 126555163Sshin * Handle special case of <scoped_address><delimiter><scope id> 126655163Sshin */ 126755163Sshin hostname2 = strdup(hostname); 126855163Sshin if (hostname2 == NULL) 126955163Sshin return EAI_MEMORY; 127055163Sshin /* terminate at the delimiter */ 127155163Sshin hostname2[cp - hostname] = '\0'; 127261877Sume addr = hostname2; 127361877Sume scope = cp + 1; 127455163Sshin 1275140906Sume error = explore_numeric(pai, addr, servname, res, hostname); 127661877Sume if (error == 0) { 1277105943Sume u_int32_t scopeid; 127855163Sshin 127955163Sshin for (cur = *res; cur; cur = cur->ai_next) { 128055163Sshin if (cur->ai_family != AF_INET6) 128155163Sshin continue; 128261877Sume sin6 = (struct sockaddr_in6 *)(void *)cur->ai_addr; 1283105943Sume if (ip6_str2scopeid(scope, sin6, &scopeid) == -1) { 128461877Sume free(hostname2); 1285190525Sume freeaddrinfo(*res); 1286190525Sume *res = NULL; 1287121425Sume return(EAI_NONAME); /* XXX: is return OK? */ 128861877Sume } 128961877Sume sin6->sin6_scope_id = scopeid; 129055163Sshin } 129155163Sshin } 129255163Sshin 129355163Sshin free(hostname2); 129455163Sshin 1295190525Sume if (error && *res) { 1296190525Sume freeaddrinfo(*res); 1297190525Sume *res = NULL; 1298190525Sume } 129955163Sshin return error; 130055163Sshin#endif 130155163Sshin} 130255163Sshin 130355163Sshinstatic int 1304157119Sumeget_canonname(const struct addrinfo *pai, struct addrinfo *ai, const char *str) 130555163Sshin{ 130655163Sshin if ((pai->ai_flags & AI_CANONNAME) != 0) { 1307140947Sume ai->ai_canonname = strdup(str); 130855163Sshin if (ai->ai_canonname == NULL) 130955163Sshin return EAI_MEMORY; 131055163Sshin } 131155163Sshin return 0; 131255163Sshin} 131355163Sshin 131455163Sshinstatic struct addrinfo * 1315157119Sumeget_ai(const struct addrinfo *pai, const struct afd *afd, const char *addr) 131655163Sshin{ 131755163Sshin char *p; 131855163Sshin struct addrinfo *ai; 131955163Sshin#ifdef FAITH 132055163Sshin struct in6_addr faith_prefix; 132155163Sshin char *fp_str; 132255163Sshin int translate = 0; 132355163Sshin#endif 132455163Sshin 132555163Sshin#ifdef FAITH 132655163Sshin /* 132755163Sshin * Transfrom an IPv4 addr into a special IPv6 addr format for 132855163Sshin * IPv6->IPv4 translation gateway. (only TCP is supported now) 132955163Sshin * 133055163Sshin * +-----------------------------------+------------+ 133155163Sshin * | faith prefix part (12 bytes) | embedded | 133255163Sshin * | | IPv4 addr part (4 bytes) 133355163Sshin * +-----------------------------------+------------+ 133455163Sshin * 133555163Sshin * faith prefix part is specified as ascii IPv6 addr format 133655163Sshin * in environmental variable GAI. 133755163Sshin * For FAITH to work correctly, routing to faith prefix must be 133855163Sshin * setup toward a machine where a FAITH daemon operates. 133955163Sshin * Also, the machine must enable some mechanizm 134055163Sshin * (e.g. faith interface hack) to divert those packet with 134155163Sshin * faith prefixed destination addr to user-land FAITH daemon. 134255163Sshin */ 134355163Sshin fp_str = getenv("GAI"); 134455163Sshin if (fp_str && inet_pton(AF_INET6, fp_str, &faith_prefix) == 1 && 134555163Sshin afd->a_af == AF_INET && pai->ai_socktype == SOCK_STREAM) { 134655163Sshin u_int32_t v4a; 134755163Sshin u_int8_t v4a_top; 134855163Sshin 134955163Sshin memcpy(&v4a, addr, sizeof v4a); 135055163Sshin v4a_top = v4a >> IN_CLASSA_NSHIFT; 135155163Sshin if (!IN_MULTICAST(v4a) && !IN_EXPERIMENTAL(v4a) && 135255163Sshin v4a_top != 0 && v4a != IN_LOOPBACKNET) { 135355163Sshin afd = &afdl[N_INET6]; 135455163Sshin memcpy(&faith_prefix.s6_addr[12], addr, 135555163Sshin sizeof(struct in_addr)); 135655163Sshin translate = 1; 135755163Sshin } 135855163Sshin } 135955163Sshin#endif 136055163Sshin 136155163Sshin ai = (struct addrinfo *)malloc(sizeof(struct addrinfo) 136255163Sshin + (afd->a_socklen)); 136355163Sshin if (ai == NULL) 136455163Sshin return NULL; 136555163Sshin 136655163Sshin memcpy(ai, pai, sizeof(struct addrinfo)); 136761877Sume ai->ai_addr = (struct sockaddr *)(void *)(ai + 1); 136861877Sume memset(ai->ai_addr, 0, (size_t)afd->a_socklen); 136955163Sshin ai->ai_addr->sa_len = afd->a_socklen; 137055163Sshin ai->ai_addrlen = afd->a_socklen; 137155163Sshin ai->ai_addr->sa_family = ai->ai_family = afd->a_af; 137265532Snectar p = (char *)(void *)(ai->ai_addr); 137355163Sshin#ifdef FAITH 137455163Sshin if (translate == 1) 137565532Snectar memcpy(p + afd->a_off, &faith_prefix, (size_t)afd->a_addrlen); 137655163Sshin else 137755163Sshin#endif 137865532Snectar memcpy(p + afd->a_off, addr, (size_t)afd->a_addrlen); 137955163Sshin return ai; 138055163Sshin} 138155163Sshin 1382190525Sume/* XXX need to malloc() the same way we do from other functions! */ 1383190525Sumestatic struct addrinfo * 1384190525Sumecopy_ai(const struct addrinfo *pai) 1385190525Sume{ 1386190525Sume struct addrinfo *ai; 1387190525Sume size_t l; 1388190525Sume 1389190525Sume l = sizeof(*ai) + pai->ai_addrlen; 1390190525Sume if ((ai = (struct addrinfo *)malloc(l)) == NULL) 1391190525Sume return NULL; 1392190525Sume memset(ai, 0, l); 1393190525Sume memcpy(ai, pai, sizeof(*ai)); 1394190525Sume ai->ai_addr = (struct sockaddr *)(void *)(ai + 1); 1395190525Sume memcpy(ai->ai_addr, pai->ai_addr, pai->ai_addrlen); 1396190525Sume 1397190525Sume if (pai->ai_canonname) { 1398190525Sume l = strlen(pai->ai_canonname) + 1; 1399190525Sume if ((ai->ai_canonname = malloc(l)) == NULL) { 1400190525Sume free(ai); 1401190525Sume return NULL; 1402190525Sume } 1403190525Sume strlcpy(ai->ai_canonname, pai->ai_canonname, l); 1404190525Sume } else { 1405190525Sume /* just to make sure */ 1406190525Sume ai->ai_canonname = NULL; 1407190525Sume } 1408190525Sume 1409190525Sume ai->ai_next = NULL; 1410190525Sume 1411190525Sume return ai; 1412190525Sume} 1413190525Sume 141455163Sshinstatic int 1415157119Sumeget_portmatch(const struct addrinfo *ai, const char *servname) 141655163Sshin{ 141761877Sume 1418140908Sume /* get_port does not touch first argument when matchonly == 1. */ 141961877Sume /* LINTED const cast */ 142055163Sshin return get_port((struct addrinfo *)ai, servname, 1); 142155163Sshin} 142255163Sshin 142355163Sshinstatic int 1424157119Sumeget_port(struct addrinfo *ai, const char *servname, int matchonly) 142555163Sshin{ 142655163Sshin const char *proto; 142755163Sshin struct servent *sp; 1428160593Sume int port, error; 142955163Sshin int allownumeric; 143055163Sshin 143155163Sshin if (servname == NULL) 143255163Sshin return 0; 143361877Sume switch (ai->ai_family) { 143461877Sume case AF_INET: 143561877Sume#ifdef AF_INET6 143661877Sume case AF_INET6: 143755163Sshin#endif 143861877Sume break; 143961877Sume default: 144055163Sshin return 0; 144161877Sume } 144255163Sshin 144355163Sshin switch (ai->ai_socktype) { 144455163Sshin case SOCK_RAW: 144555163Sshin return EAI_SERVICE; 144655163Sshin case SOCK_DGRAM: 144755163Sshin case SOCK_STREAM: 1448190416Sume case SOCK_SEQPACKET: 144955163Sshin allownumeric = 1; 145055163Sshin break; 145155163Sshin case ANY: 1452190382Sume switch (ai->ai_family) { 1453190382Sume case AF_INET: 1454190382Sume#ifdef AF_INET6 1455190382Sume case AF_INET6: 1456190382Sume#endif 1457190382Sume allownumeric = 1; 1458190382Sume break; 1459190382Sume default: 1460190382Sume allownumeric = 0; 1461190382Sume break; 1462190382Sume } 146355163Sshin break; 146455163Sshin default: 146555163Sshin return EAI_SOCKTYPE; 146655163Sshin } 146755163Sshin 1468160593Sume error = str2number(servname, &port); 1469160593Sume if (error == 0) { 147055163Sshin if (!allownumeric) 147155163Sshin return EAI_SERVICE; 147255163Sshin if (port < 0 || port > 65535) 147355163Sshin return EAI_SERVICE; 1474105940Sume port = htons(port); 147555163Sshin } else { 1476140908Sume if (ai->ai_flags & AI_NUMERICSERV) 1477140908Sume return EAI_NONAME; 1478190416Sume 1479190416Sume switch (ai->ai_protocol) { 1480190416Sume case IPPROTO_UDP: 148155163Sshin proto = "udp"; 148255163Sshin break; 1483190416Sume case IPPROTO_TCP: 148455163Sshin proto = "tcp"; 148555163Sshin break; 1486190416Sume case IPPROTO_SCTP: 1487190416Sume proto = "sctp"; 1488190416Sume break; 1489265946Skevlo case IPPROTO_UDPLITE: 1490265946Skevlo proto = "udplite"; 1491265946Skevlo break; 149255163Sshin default: 149355163Sshin proto = NULL; 149455163Sshin break; 149555163Sshin } 149655163Sshin 1497145118Sume if ((sp = getservbyname(servname, proto)) == NULL) 149855163Sshin return EAI_SERVICE; 149955163Sshin port = sp->s_port; 150055163Sshin } 150155163Sshin 150255163Sshin if (!matchonly) { 150355163Sshin switch (ai->ai_family) { 150455163Sshin case AF_INET: 150561877Sume ((struct sockaddr_in *)(void *) 150661877Sume ai->ai_addr)->sin_port = port; 150755163Sshin break; 150855163Sshin#ifdef INET6 150955163Sshin case AF_INET6: 151061877Sume ((struct sockaddr_in6 *)(void *) 151161877Sume ai->ai_addr)->sin6_port = port; 151255163Sshin break; 151355163Sshin#endif 151455163Sshin } 151555163Sshin } 151655163Sshin 151755163Sshin return 0; 151855163Sshin} 151955163Sshin 152055163Sshinstatic const struct afd * 1521157119Sumefind_afd(int af) 152255163Sshin{ 152355163Sshin const struct afd *afd; 152455163Sshin 152555163Sshin if (af == PF_UNSPEC) 152655163Sshin return NULL; 152755163Sshin for (afd = afdl; afd->a_af; afd++) { 152855163Sshin if (afd->a_af == af) 152955163Sshin return afd; 153055163Sshin } 153155163Sshin return NULL; 153255163Sshin} 153361877Sume 153461877Sume/* 1535268216Sume * RFC 3493: AI_ADDRCONFIG check. Determines which address families are 1536267874Sume * configured on the local system and correlates with pai->ai_family value. 1537267874Sume * If an address family is not configured on the system, it will not be 1538267874Sume * queried for. For this purpose, loopback addresses are not considered 1539267874Sume * configured addresses. 1540121474Sume * 1541121474Sume * XXX PF_UNSPEC -> PF_INET6 + PF_INET mapping needs to be in sync with 1542121474Sume * _dns_getaddrinfo. 154361877Sume */ 154461877Sumestatic int 1545157119Sumeaddrconfig(struct addrinfo *pai) 154661877Sume{ 1547267874Sume struct ifaddrs *ifaddrs, *ifa; 1548268216Sume struct sockaddr_in *sin; 1549268216Sume#ifdef INET6 1550268216Sume struct sockaddr_in6 *sin6; 1551268216Sume#endif 1552267874Sume int seen_inet = 0, seen_inet6 = 0; 155361877Sume 1554267874Sume if (getifaddrs(&ifaddrs) != 0) 1555268216Sume return (0); 1556267874Sume 1557267874Sume for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) { 1558267874Sume if (ifa->ifa_addr == NULL || (ifa->ifa_flags & IFF_UP) == 0) 1559267874Sume continue; 1560267874Sume switch (ifa->ifa_addr->sa_family) { 1561267874Sume case AF_INET: 1562268216Sume if (seen_inet) 1563268216Sume continue; 1564268216Sume sin = (struct sockaddr_in *)(ifa->ifa_addr); 1565268216Sume if (IN_LOOPBACK(htonl(sin->sin_addr.s_addr))) 1566268216Sume continue; 1567267874Sume seen_inet = 1; 1568267874Sume break; 1569267874Sume#ifdef INET6 1570267874Sume case AF_INET6: 1571268216Sume if (seen_inet6) 1572268216Sume continue; 1573268216Sume sin6 = (struct sockaddr_in6 *)(ifa->ifa_addr); 1574268216Sume if (IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr)) 1575268216Sume continue; 1576268216Sume if ((ifa->ifa_flags & IFT_LOOP) != 0 && 1577268216Sume IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) 1578268216Sume continue; 1579268216Sume if (is_ifdisabled(ifa->ifa_name)) 1580268216Sume continue; 1581268216Sume seen_inet6 = 1; 1582267874Sume break; 1583267874Sume#endif 1584121474Sume } 1585121474Sume } 1586267874Sume freeifaddrs(ifaddrs); 1587267874Sume 1588267874Sume switch(pai->ai_family) { 1589267874Sume case AF_INET6: 1590268216Sume return (seen_inet6); 1591267874Sume case AF_INET: 1592268216Sume return (seen_inet); 1593267874Sume case AF_UNSPEC: 1594267874Sume if (seen_inet == seen_inet6) 1595268216Sume return (seen_inet); 1596267874Sume pai->ai_family = seen_inet ? AF_INET : AF_INET6; 1597268216Sume return (1); 1598121474Sume } 1599268216Sume return (1); 160061877Sume} 160161877Sume 160261877Sume#ifdef INET6 1603267874Sumestatic int 1604267874Sumeis_ifdisabled(char *name) 1605267874Sume{ 1606267874Sume struct in6_ndireq nd; 1607267874Sume int fd; 1608267874Sume 1609267874Sume if ((fd = _socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0)) < 0) 1610268216Sume return (-1); 1611267874Sume memset(&nd, 0, sizeof(nd)); 1612267874Sume strlcpy(nd.ifname, name, sizeof(nd.ifname)); 1613267874Sume if (_ioctl(fd, SIOCGIFINFO_IN6, &nd) < 0) { 1614267874Sume _close(fd); 1615268216Sume return (-1); 1616267874Sume } 1617267874Sume _close(fd); 1618267874Sume return ((nd.ndi.flags & ND6_IFF_IFDISABLED) != 0); 1619267874Sume} 1620267874Sume 162161877Sume/* convert a string to a scope identifier. XXX: IPv6 specific */ 162261877Sumestatic int 1623157119Sumeip6_str2scopeid(char *scope, struct sockaddr_in6 *sin6, u_int32_t *scopeid) 162461877Sume{ 1625105943Sume u_long lscopeid; 1626121474Sume struct in6_addr *a6; 162761877Sume char *ep; 162861877Sume 1629121474Sume a6 = &sin6->sin6_addr; 1630121474Sume 163162836Sitojun /* empty scopeid portion is invalid */ 163262836Sitojun if (*scope == '\0') 163362836Sitojun return -1; 163462836Sitojun 1635229766Sume if (IN6_IS_ADDR_LINKLOCAL(a6) || IN6_IS_ADDR_MC_LINKLOCAL(a6) || 1636229766Sume IN6_IS_ADDR_MC_NODELOCAL(a6)) { 163761877Sume /* 163861877Sume * We currently assume a one-to-one mapping between links 163961877Sume * and interfaces, so we simply use interface indices for 164061877Sume * like-local scopes. 164161877Sume */ 1642105943Sume *scopeid = if_nametoindex(scope); 1643105943Sume if (*scopeid == 0) 164461877Sume goto trynumeric; 1645105943Sume return 0; 164661877Sume } 164761877Sume 164861877Sume /* still unclear about literal, allow numeric only - placeholder */ 164961877Sume if (IN6_IS_ADDR_SITELOCAL(a6) || IN6_IS_ADDR_MC_SITELOCAL(a6)) 165061877Sume goto trynumeric; 165161877Sume if (IN6_IS_ADDR_MC_ORGLOCAL(a6)) 165261877Sume goto trynumeric; 165361877Sume else 165461877Sume goto trynumeric; /* global */ 165561877Sume 165661877Sume /* try to convert to a numeric id as a last resort */ 1657121474Sume trynumeric: 1658105943Sume errno = 0; 1659105943Sume lscopeid = strtoul(scope, &ep, 10); 1660105943Sume *scopeid = (u_int32_t)(lscopeid & 0xffffffffUL); 1661105943Sume if (errno == 0 && ep && *ep == '\0' && *scopeid == lscopeid) 1662105943Sume return 0; 166361877Sume else 166461877Sume return -1; 166561877Sume} 166661877Sume#endif 166761877Sume 1668158115Sume 1669158115Sume#ifdef NS_CACHING 1670158115Sumestatic int 1671158115Sumeaddrinfo_id_func(char *buffer, size_t *buffer_size, va_list ap, 1672158115Sume void *cache_mdata) 1673158115Sume{ 1674158115Sume res_state statp; 1675158115Sume u_long res_options; 1676158115Sume 1677158115Sume const int op_id = 0; /* identifies the getaddrinfo for the cache */ 1678158115Sume char *hostname; 1679158115Sume struct addrinfo *hints; 1680158115Sume 1681158115Sume char *p; 1682158115Sume int ai_flags, ai_family, ai_socktype, ai_protocol; 1683158115Sume size_t desired_size, size; 1684158115Sume 1685158115Sume statp = __res_state(); 1686158115Sume res_options = statp->options & (RES_RECURSE | RES_DEFNAMES | 1687158115Sume RES_DNSRCH | RES_NOALIASES | RES_USE_INET6); 1688158115Sume 1689158115Sume hostname = va_arg(ap, char *); 1690158115Sume hints = va_arg(ap, struct addrinfo *); 1691158115Sume 1692158115Sume desired_size = sizeof(res_options) + sizeof(int) + sizeof(int) * 4; 1693158115Sume if (hostname != NULL) { 1694158115Sume size = strlen(hostname); 1695158115Sume desired_size += size + 1; 1696158115Sume } else 1697158115Sume size = 0; 1698158115Sume 1699158115Sume if (desired_size > *buffer_size) { 1700158115Sume *buffer_size = desired_size; 1701158115Sume return (NS_RETURN); 1702158115Sume } 1703158115Sume 1704158115Sume if (hints == NULL) 1705158115Sume ai_flags = ai_family = ai_socktype = ai_protocol = 0; 1706158115Sume else { 1707158115Sume ai_flags = hints->ai_flags; 1708158115Sume ai_family = hints->ai_family; 1709158115Sume ai_socktype = hints->ai_socktype; 1710158115Sume ai_protocol = hints->ai_protocol; 1711158115Sume } 1712158115Sume 1713158115Sume p = buffer; 1714158115Sume memcpy(p, &res_options, sizeof(res_options)); 1715158115Sume p += sizeof(res_options); 1716158115Sume 1717158115Sume memcpy(p, &op_id, sizeof(int)); 1718158115Sume p += sizeof(int); 1719158115Sume 1720158115Sume memcpy(p, &ai_flags, sizeof(int)); 1721158115Sume p += sizeof(int); 1722158115Sume 1723158115Sume memcpy(p, &ai_family, sizeof(int)); 1724158115Sume p += sizeof(int); 1725158115Sume 1726158115Sume memcpy(p, &ai_socktype, sizeof(int)); 1727158115Sume p += sizeof(int); 1728158115Sume 1729158115Sume memcpy(p, &ai_protocol, sizeof(int)); 1730158115Sume p += sizeof(int); 1731158115Sume 1732158115Sume if (hostname != NULL) 1733158115Sume memcpy(p, hostname, size); 1734158115Sume 1735158115Sume *buffer_size = desired_size; 1736158115Sume return (NS_SUCCESS); 1737158115Sume} 1738158115Sume 1739158115Sumestatic int 1740158115Sumeaddrinfo_marshal_func(char *buffer, size_t *buffer_size, void *retval, 1741158115Sume va_list ap, void *cache_mdata) 1742158115Sume{ 1743158115Sume struct addrinfo *ai, *cai; 1744158115Sume char *p; 1745158115Sume size_t desired_size, size, ai_size; 1746158115Sume 1747158115Sume ai = *((struct addrinfo **)retval); 1748158115Sume 1749158115Sume desired_size = sizeof(size_t); 1750158115Sume ai_size = 0; 1751158115Sume for (cai = ai; cai != NULL; cai = cai->ai_next) { 1752158115Sume desired_size += sizeof(struct addrinfo) + cai->ai_addrlen; 1753158115Sume if (cai->ai_canonname != NULL) 1754158115Sume desired_size += sizeof(size_t) + 1755158115Sume strlen(cai->ai_canonname); 1756158115Sume ++ai_size; 1757158115Sume } 1758158115Sume 1759158115Sume if (desired_size > *buffer_size) { 1760158115Sume /* this assignment is here for future use */ 1761158115Sume errno = ERANGE; 1762158115Sume *buffer_size = desired_size; 1763158115Sume return (NS_RETURN); 1764158115Sume } 1765158115Sume 1766158115Sume memset(buffer, 0, desired_size); 1767158115Sume p = buffer; 1768158115Sume 1769158115Sume memcpy(p, &ai_size, sizeof(size_t)); 1770158115Sume p += sizeof(size_t); 1771158115Sume for (cai = ai; cai != NULL; cai = cai->ai_next) { 1772158115Sume memcpy(p, cai, sizeof(struct addrinfo)); 1773158115Sume p += sizeof(struct addrinfo); 1774158115Sume 1775158115Sume memcpy(p, cai->ai_addr, cai->ai_addrlen); 1776158115Sume p += cai->ai_addrlen; 1777158115Sume 1778158115Sume if (cai->ai_canonname != NULL) { 1779158115Sume size = strlen(cai->ai_canonname); 1780158115Sume memcpy(p, &size, sizeof(size_t)); 1781158115Sume p += sizeof(size_t); 1782158115Sume 1783158115Sume memcpy(p, cai->ai_canonname, size); 1784158115Sume p += size; 1785158115Sume } 1786158115Sume } 1787158115Sume 1788158115Sume return (NS_SUCCESS); 1789158115Sume} 1790158115Sume 1791158115Sumestatic int 1792158115Sumeaddrinfo_unmarshal_func(char *buffer, size_t buffer_size, void *retval, 1793158115Sume va_list ap, void *cache_mdata) 1794158115Sume{ 1795158115Sume struct addrinfo new_ai, *result, *sentinel, *lasts; 1796158115Sume 1797158115Sume char *p; 1798158115Sume size_t ai_size, ai_i, size; 1799158115Sume 1800158115Sume p = buffer; 1801158115Sume memcpy(&ai_size, p, sizeof(size_t)); 1802158115Sume p += sizeof(size_t); 1803158115Sume 1804158115Sume result = NULL; 1805158115Sume lasts = NULL; 1806158115Sume for (ai_i = 0; ai_i < ai_size; ++ai_i) { 1807158115Sume memcpy(&new_ai, p, sizeof(struct addrinfo)); 1808158115Sume p += sizeof(struct addrinfo); 1809158115Sume size = new_ai.ai_addrlen + sizeof(struct addrinfo) + 1810158115Sume _ALIGNBYTES; 1811158115Sume 1812158115Sume sentinel = (struct addrinfo *)malloc(size); 1813158115Sume memset(sentinel, 0, size); 1814158115Sume 1815158115Sume memcpy(sentinel, &new_ai, sizeof(struct addrinfo)); 1816158115Sume sentinel->ai_addr = (struct sockaddr *)_ALIGN((char *)sentinel + 1817158115Sume sizeof(struct addrinfo)); 1818158115Sume 1819158115Sume memcpy(sentinel->ai_addr, p, new_ai.ai_addrlen); 1820158115Sume p += new_ai.ai_addrlen; 1821158115Sume 1822158115Sume if (new_ai.ai_canonname != NULL) { 1823158115Sume memcpy(&size, p, sizeof(size_t)); 1824158115Sume p += sizeof(size_t); 1825158115Sume 1826158115Sume sentinel->ai_canonname = (char *)malloc(size + 1); 1827158115Sume memset(sentinel->ai_canonname, 0, size + 1); 1828158115Sume 1829158115Sume memcpy(sentinel->ai_canonname, p, size); 1830158115Sume p += size; 1831158115Sume } 1832158115Sume 1833158115Sume if (result == NULL) { 1834158115Sume result = sentinel; 1835158115Sume lasts = sentinel; 1836158115Sume } else { 1837158115Sume lasts->ai_next = sentinel; 1838158115Sume lasts = sentinel; 1839158115Sume } 1840158115Sume } 1841158115Sume 1842158115Sume *((struct addrinfo **)retval) = result; 1843158115Sume return (NS_SUCCESS); 1844158115Sume} 1845158115Sume#endif /* NS_CACHING */ 1846158115Sume 1847121426Sume/* 1848121426Sume * FQDN hostname, DNS lookup 1849121426Sume */ 1850102237Spirzykstatic int 1851157119Sumeexplore_fqdn(const struct addrinfo *pai, const char *hostname, 1852157119Sume const char *servname, struct addrinfo **res) 1853102237Spirzyk{ 1854121426Sume struct addrinfo *result; 1855121426Sume struct addrinfo *cur; 1856121426Sume int error = 0; 1857158115Sume 1858158115Sume#ifdef NS_CACHING 1859158115Sume static const nss_cache_info cache_info = 1860158115Sume NS_COMMON_CACHE_INFO_INITIALIZER( 1861158115Sume hosts, NULL, addrinfo_id_func, addrinfo_marshal_func, 1862158115Sume addrinfo_unmarshal_func); 1863158115Sume#endif 1864121426Sume static const ns_dtab dtab[] = { 1865121426Sume NS_FILES_CB(_files_getaddrinfo, NULL) 1866121426Sume { NSSRC_DNS, _dns_getaddrinfo, NULL }, /* force -DHESIOD */ 1867121426Sume NS_NIS_CB(_yp_getaddrinfo, NULL) 1868158115Sume#ifdef NS_CACHING 1869158115Sume NS_CACHE_CB(&cache_info) 1870158115Sume#endif 1871121426Sume { 0 } 1872121426Sume }; 1873102237Spirzyk 1874121426Sume result = NULL; 1875121426Sume 1876121426Sume /* 1877121426Sume * if the servname does not match socktype/protocol, ignore it. 1878121426Sume */ 1879126243Sgreen if (get_portmatch(pai, servname) != 0) 1880102237Spirzyk return 0; 1881102237Spirzyk 1882121426Sume switch (_nsdispatch(&result, dtab, NSDB_HOSTS, "getaddrinfo", 1883121426Sume default_dns_files, hostname, pai)) { 1884121426Sume case NS_TRYAGAIN: 1885121426Sume error = EAI_AGAIN; 1886121426Sume goto free; 1887121426Sume case NS_UNAVAIL: 1888121426Sume error = EAI_FAIL; 1889121426Sume goto free; 1890121426Sume case NS_NOTFOUND: 1891121426Sume error = EAI_NONAME; 1892121426Sume goto free; 1893121426Sume case NS_SUCCESS: 1894121426Sume error = 0; 1895121426Sume for (cur = result; cur; cur = cur->ai_next) { 1896121426Sume GET_PORT(cur, servname); 1897121426Sume /* canonname should be filled already */ 1898121426Sume } 1899121426Sume break; 1900102237Spirzyk } 1901102237Spirzyk 1902121426Sume *res = result; 1903121426Sume 1904102237Spirzyk return 0; 1905121426Sume 1906121426Sumefree: 1907121426Sume if (result) 1908121426Sume freeaddrinfo(result); 1909121426Sume return error; 1910102237Spirzyk} 1911102237Spirzyk 191261877Sume#ifdef DEBUG 191361877Sumestatic const char AskedForGot[] = 191461877Sume "gethostby*.getanswer: asked for \"%s\", got \"%s\""; 191561877Sume#endif 191661877Sume 191761877Sumestatic struct addrinfo * 1918157119Sumegetanswer(const querybuf *answer, int anslen, const char *qname, int qtype, 1919157119Sume const struct addrinfo *pai, res_state res) 192061877Sume{ 192161877Sume struct addrinfo sentinel, *cur; 192261877Sume struct addrinfo ai; 192361877Sume const struct afd *afd; 192461877Sume char *canonname; 192561877Sume const HEADER *hp; 192661877Sume const u_char *cp; 192761877Sume int n; 192861877Sume const u_char *eom; 1929105940Sume char *bp, *ep; 1930105940Sume int type, class, ancount, qdcount; 193161877Sume int haveanswer, had_error; 193261877Sume char tbuf[MAXDNAME]; 193392905Sobrien int (*name_ok)(const char *); 193461877Sume char hostbuf[8*1024]; 193561877Sume 193661877Sume memset(&sentinel, 0, sizeof(sentinel)); 193761877Sume cur = &sentinel; 193861877Sume 193961877Sume canonname = NULL; 194061877Sume eom = answer->buf + anslen; 194161877Sume switch (qtype) { 194261877Sume case T_A: 194361877Sume case T_AAAA: 194461877Sume case T_ANY: /*use T_ANY only for T_A/T_AAAA lookup*/ 194561877Sume name_ok = res_hnok; 194661877Sume break; 194761877Sume default: 194861877Sume return (NULL); /* XXX should be abort(); */ 194961877Sume } 195061877Sume /* 195161877Sume * find first satisfactory answer 195261877Sume */ 195361877Sume hp = &answer->hdr; 195461877Sume ancount = ntohs(hp->ancount); 195561877Sume qdcount = ntohs(hp->qdcount); 195661877Sume bp = hostbuf; 1957105940Sume ep = hostbuf + sizeof hostbuf; 195861877Sume cp = answer->buf + HFIXEDSZ; 195961877Sume if (qdcount != 1) { 1960156960Sume RES_SET_H_ERRNO(res, NO_RECOVERY); 196161877Sume return (NULL); 196261877Sume } 1963105940Sume n = dn_expand(answer->buf, eom, cp, bp, ep - bp); 196461877Sume if ((n < 0) || !(*name_ok)(bp)) { 1965156960Sume RES_SET_H_ERRNO(res, NO_RECOVERY); 196661877Sume return (NULL); 196761877Sume } 196861877Sume cp += n + QFIXEDSZ; 196961877Sume if (qtype == T_A || qtype == T_AAAA || qtype == T_ANY) { 197061877Sume /* res_send() has already verified that the query name is the 197161877Sume * same as the one we sent; this just gets the expanded name 197261877Sume * (i.e., with the succeeding search-domain tacked on). 197361877Sume */ 197461877Sume n = strlen(bp) + 1; /* for the \0 */ 197561877Sume if (n >= MAXHOSTNAMELEN) { 1976156960Sume RES_SET_H_ERRNO(res, NO_RECOVERY); 197761877Sume return (NULL); 197861877Sume } 197961877Sume canonname = bp; 198061877Sume bp += n; 198161877Sume /* The qname can be abbreviated, but h_name is now absolute. */ 198261877Sume qname = canonname; 198361877Sume } 198461877Sume haveanswer = 0; 198561877Sume had_error = 0; 198661877Sume while (ancount-- > 0 && cp < eom && !had_error) { 1987105940Sume n = dn_expand(answer->buf, eom, cp, bp, ep - bp); 198861877Sume if ((n < 0) || !(*name_ok)(bp)) { 198961877Sume had_error++; 199061877Sume continue; 199161877Sume } 199261877Sume cp += n; /* name */ 199361877Sume type = _getshort(cp); 199461877Sume cp += INT16SZ; /* type */ 199561877Sume class = _getshort(cp); 199661877Sume cp += INT16SZ + INT32SZ; /* class, TTL */ 199761877Sume n = _getshort(cp); 199861877Sume cp += INT16SZ; /* len */ 199961877Sume if (class != C_IN) { 200061877Sume /* XXX - debug? syslog? */ 200161877Sume cp += n; 200261877Sume continue; /* XXX - had_error++ ? */ 200361877Sume } 200461877Sume if ((qtype == T_A || qtype == T_AAAA || qtype == T_ANY) && 200561877Sume type == T_CNAME) { 200661877Sume n = dn_expand(answer->buf, eom, cp, tbuf, sizeof tbuf); 200761877Sume if ((n < 0) || !(*name_ok)(tbuf)) { 200861877Sume had_error++; 200961877Sume continue; 201061877Sume } 201161877Sume cp += n; 201261877Sume /* Get canonical name. */ 201361877Sume n = strlen(tbuf) + 1; /* for the \0 */ 2014105940Sume if (n > ep - bp || n >= MAXHOSTNAMELEN) { 201561877Sume had_error++; 201661877Sume continue; 201761877Sume } 2018114443Snectar strlcpy(bp, tbuf, ep - bp); 201961877Sume canonname = bp; 202061877Sume bp += n; 202161877Sume continue; 202261877Sume } 202361877Sume if (qtype == T_ANY) { 202461877Sume if (!(type == T_A || type == T_AAAA)) { 202561877Sume cp += n; 202661877Sume continue; 202761877Sume } 202861877Sume } else if (type != qtype) { 202961877Sume#ifdef DEBUG 2030188316Sume if (type != T_KEY && type != T_SIG && 2031188316Sume type != ns_t_dname) 203261877Sume syslog(LOG_NOTICE|LOG_AUTH, 203361877Sume "gethostby*.getanswer: asked for \"%s %s %s\", got type \"%s\"", 203461877Sume qname, p_class(C_IN), p_type(qtype), 203561877Sume p_type(type)); 203661877Sume#endif 203761877Sume cp += n; 203861877Sume continue; /* XXX - had_error++ ? */ 203961877Sume } 204061877Sume switch (type) { 204161877Sume case T_A: 204261877Sume case T_AAAA: 204361877Sume if (strcasecmp(canonname, bp) != 0) { 204461877Sume#ifdef DEBUG 204561877Sume syslog(LOG_NOTICE|LOG_AUTH, 204661877Sume AskedForGot, canonname, bp); 204761877Sume#endif 204861877Sume cp += n; 204961877Sume continue; /* XXX - had_error++ ? */ 205061877Sume } 205161877Sume if (type == T_A && n != INADDRSZ) { 205261877Sume cp += n; 205361877Sume continue; 205461877Sume } 205561877Sume if (type == T_AAAA && n != IN6ADDRSZ) { 205661877Sume cp += n; 205761877Sume continue; 205861877Sume } 205961877Sume#ifdef FILTER_V4MAPPED 206061877Sume if (type == T_AAAA) { 206161877Sume struct in6_addr in6; 206261877Sume memcpy(&in6, cp, sizeof(in6)); 206361877Sume if (IN6_IS_ADDR_V4MAPPED(&in6)) { 206461877Sume cp += n; 206561877Sume continue; 206661877Sume } 206761877Sume } 206861877Sume#endif 206961877Sume if (!haveanswer) { 207061877Sume int nn; 207161877Sume 207261877Sume canonname = bp; 207361877Sume nn = strlen(bp) + 1; /* for the \0 */ 207461877Sume bp += nn; 207561877Sume } 207661877Sume 207761877Sume /* don't overwrite pai */ 207861877Sume ai = *pai; 207961877Sume ai.ai_family = (type == T_A) ? AF_INET : AF_INET6; 208061877Sume afd = find_afd(ai.ai_family); 208161877Sume if (afd == NULL) { 208261877Sume cp += n; 208361877Sume continue; 208461877Sume } 208561877Sume cur->ai_next = get_ai(&ai, afd, (const char *)cp); 208661877Sume if (cur->ai_next == NULL) 208761877Sume had_error++; 208861877Sume while (cur && cur->ai_next) 208961877Sume cur = cur->ai_next; 209061877Sume cp += n; 209161877Sume break; 209261877Sume default: 209361877Sume abort(); 209461877Sume } 209561877Sume if (!had_error) 209661877Sume haveanswer++; 209761877Sume } 209861877Sume if (haveanswer) { 2099102237Spirzyk#if defined(RESOLVSORT) 2100102237Spirzyk /* 2101102237Spirzyk * We support only IPv4 address for backward 2102102237Spirzyk * compatibility against gethostbyname(3). 2103102237Spirzyk */ 2104156960Sume if (res->nsort && qtype == T_A) { 2105156960Sume if (addr4sort(&sentinel, res) < 0) { 2106102237Spirzyk freeaddrinfo(sentinel.ai_next); 2107156960Sume RES_SET_H_ERRNO(res, NO_RECOVERY); 2108102237Spirzyk return NULL; 2109102237Spirzyk } 2110102237Spirzyk } 2111102237Spirzyk#endif /*RESOLVSORT*/ 211261877Sume if (!canonname) 211361877Sume (void)get_canonname(pai, sentinel.ai_next, qname); 211461877Sume else 211561877Sume (void)get_canonname(pai, sentinel.ai_next, canonname); 2116156960Sume RES_SET_H_ERRNO(res, NETDB_SUCCESS); 211761877Sume return sentinel.ai_next; 211861877Sume } 211961877Sume 2120156960Sume RES_SET_H_ERRNO(res, NO_RECOVERY); 212161877Sume return NULL; 212261877Sume} 212361877Sume 2124121426Sume#ifdef RESOLVSORT 2125121426Sumestruct addr_ptr { 2126121426Sume struct addrinfo *ai; 2127121426Sume int aval; 2128121426Sume}; 2129121426Sume 2130121426Sumestatic int 2131156960Sumeaddr4sort(struct addrinfo *sentinel, res_state res) 2132121426Sume{ 2133121426Sume struct addrinfo *ai; 2134121426Sume struct addr_ptr *addrs, addr; 2135121426Sume struct sockaddr_in *sin; 2136121426Sume int naddrs, i, j; 2137121426Sume int needsort = 0; 2138121426Sume 2139121426Sume if (!sentinel) 2140121426Sume return -1; 2141121426Sume naddrs = 0; 2142121426Sume for (ai = sentinel->ai_next; ai; ai = ai->ai_next) 2143121426Sume naddrs++; 2144121426Sume if (naddrs < 2) 2145121426Sume return 0; /* We don't need sorting. */ 2146121426Sume if ((addrs = malloc(sizeof(struct addr_ptr) * naddrs)) == NULL) 2147121426Sume return -1; 2148121426Sume i = 0; 2149121426Sume for (ai = sentinel->ai_next; ai; ai = ai->ai_next) { 2150121426Sume sin = (struct sockaddr_in *)ai->ai_addr; 2151156960Sume for (j = 0; (unsigned)j < res->nsort; j++) { 2152157119Sume if (res->sort_list[j].addr.s_addr == 2153156960Sume (sin->sin_addr.s_addr & res->sort_list[j].mask)) 2154121426Sume break; 2155121426Sume } 2156121426Sume addrs[i].ai = ai; 2157121426Sume addrs[i].aval = j; 2158121426Sume if (needsort == 0 && i > 0 && j < addrs[i - 1].aval) 2159121426Sume needsort = i; 2160121426Sume i++; 2161121426Sume } 2162121426Sume if (!needsort) { 2163121426Sume free(addrs); 2164121426Sume return 0; 2165121426Sume } 2166121426Sume 2167121426Sume while (needsort < naddrs) { 2168157371Sume for (j = needsort - 1; j >= 0; j--) { 2169157371Sume if (addrs[j].aval > addrs[j+1].aval) { 2170157371Sume addr = addrs[j]; 2171157371Sume addrs[j] = addrs[j + 1]; 2172157371Sume addrs[j + 1] = addr; 2173157371Sume } else 2174157371Sume break; 2175157371Sume } 2176157371Sume needsort++; 2177121426Sume } 2178121426Sume 2179121426Sume ai = sentinel; 2180121426Sume for (i = 0; i < naddrs; ++i) { 2181121426Sume ai->ai_next = addrs[i].ai; 2182121426Sume ai = ai->ai_next; 2183121426Sume } 2184121426Sume ai->ai_next = NULL; 2185121426Sume free(addrs); 2186121426Sume return 0; 2187121426Sume} 2188121426Sume#endif /*RESOLVSORT*/ 2189121426Sume 219061877Sume/*ARGSUSED*/ 219161877Sumestatic int 2192157119Sume_dns_getaddrinfo(void *rv, void *cb_data, va_list ap) 219361877Sume{ 219461877Sume struct addrinfo *ai; 2195103357Sume querybuf *buf, *buf2; 2196130600Sume const char *hostname; 219765532Snectar const struct addrinfo *pai; 219861877Sume struct addrinfo sentinel, *cur; 219961877Sume struct res_target q, q2; 2200156960Sume res_state res; 220161877Sume 2202130600Sume hostname = va_arg(ap, char *); 220365532Snectar pai = va_arg(ap, const struct addrinfo *); 220465532Snectar 2205156946Sdelphij memset(&q, 0, sizeof(q)); 220661877Sume memset(&q2, 0, sizeof(q2)); 220761877Sume memset(&sentinel, 0, sizeof(sentinel)); 220861877Sume cur = &sentinel; 220961877Sume 2210103357Sume buf = malloc(sizeof(*buf)); 2211103357Sume if (!buf) { 2212156960Sume RES_SET_H_ERRNO(res, NETDB_INTERNAL); 2213103357Sume return NS_NOTFOUND; 2214103357Sume } 2215103357Sume buf2 = malloc(sizeof(*buf2)); 2216103357Sume if (!buf2) { 2217103357Sume free(buf); 2218156960Sume RES_SET_H_ERRNO(res, NETDB_INTERNAL); 2219103357Sume return NS_NOTFOUND; 2220103357Sume } 2221103357Sume 222261877Sume switch (pai->ai_family) { 222361877Sume case AF_UNSPEC: 2224130600Sume q.name = hostname; 222562614Sitojun q.qclass = C_IN; 2226140896Sume q.qtype = T_A; 2227103357Sume q.answer = buf->buf; 2228103357Sume q.anslen = sizeof(buf->buf); 222961877Sume q.next = &q2; 2230130600Sume q2.name = hostname; 223162614Sitojun q2.qclass = C_IN; 2232140896Sume q2.qtype = T_AAAA; 2233103357Sume q2.answer = buf2->buf; 2234103357Sume q2.anslen = sizeof(buf2->buf); 223561877Sume break; 223661877Sume case AF_INET: 2237130600Sume q.name = hostname; 223862614Sitojun q.qclass = C_IN; 223962614Sitojun q.qtype = T_A; 2240103357Sume q.answer = buf->buf; 2241103357Sume q.anslen = sizeof(buf->buf); 224261877Sume break; 224361877Sume case AF_INET6: 2244130600Sume q.name = hostname; 224562614Sitojun q.qclass = C_IN; 224662614Sitojun q.qtype = T_AAAA; 2247103357Sume q.answer = buf->buf; 2248103357Sume q.anslen = sizeof(buf->buf); 224961877Sume break; 225061877Sume default: 2251103357Sume free(buf); 2252103357Sume free(buf2); 225365532Snectar return NS_UNAVAIL; 225461877Sume } 2255156960Sume 2256156960Sume res = __res_state(); 2257156960Sume if ((res->options & RES_INIT) == 0 && res_ninit(res) == -1) { 2258156960Sume RES_SET_H_ERRNO(res, NETDB_INTERNAL); 2259103357Sume free(buf); 2260103357Sume free(buf2); 226165532Snectar return NS_NOTFOUND; 2262103357Sume } 2263156960Sume 2264156960Sume if (res_searchN(hostname, &q, res) < 0) { 2265156960Sume free(buf); 2266156960Sume free(buf2); 2267156960Sume return NS_NOTFOUND; 2268156960Sume } 2269140896Sume /* prefer IPv6 */ 227061877Sume if (q.next) { 2271156960Sume ai = getanswer(buf2, q2.n, q2.name, q2.qtype, pai, res); 2272140896Sume if (ai) { 227361877Sume cur->ai_next = ai; 2274140896Sume while (cur && cur->ai_next) 2275140896Sume cur = cur->ai_next; 2276140896Sume } 227761877Sume } 2278156960Sume ai = getanswer(buf, q.n, q.name, q.qtype, pai, res); 2279140896Sume if (ai) 2280140896Sume cur->ai_next = ai; 2281103357Sume free(buf); 2282103357Sume free(buf2); 228361877Sume if (sentinel.ai_next == NULL) 2284156960Sume switch (res->res_h_errno) { 228561877Sume case HOST_NOT_FOUND: 228665532Snectar return NS_NOTFOUND; 228761877Sume case TRY_AGAIN: 228865532Snectar return NS_TRYAGAIN; 228961877Sume default: 229065532Snectar return NS_UNAVAIL; 229161877Sume } 229265532Snectar *((struct addrinfo **)rv) = sentinel.ai_next; 229365532Snectar return NS_SUCCESS; 229461877Sume} 229561877Sume 229665532Snectarstatic void 2297144634Sume_sethtent(FILE **hostf) 229865532Snectar{ 2299144634Sume if (!*hostf) 2300254700Sjilles *hostf = fopen(_PATH_HOSTS, "re"); 230165532Snectar else 2302144634Sume rewind(*hostf); 230365532Snectar} 230465532Snectar 230565532Snectarstatic void 2306144634Sume_endhtent(FILE **hostf) 230765532Snectar{ 2308144634Sume if (*hostf) { 2309144634Sume (void) fclose(*hostf); 2310144634Sume *hostf = NULL; 231165532Snectar } 231265532Snectar} 231365532Snectar 231461877Sumestatic struct addrinfo * 2315144634Sume_gethtent(FILE **hostf, const char *name, const struct addrinfo *pai) 231661877Sume{ 231761877Sume char *p; 231861877Sume char *cp, *tname, *cname; 231961877Sume struct addrinfo hints, *res0, *res; 232061877Sume int error; 232161877Sume const char *addr; 232261877Sume char hostbuf[8*1024]; 232361877Sume 2324254700Sjilles if (!*hostf && !(*hostf = fopen(_PATH_HOSTS, "re"))) 232565532Snectar return (NULL); 2326105940Sumeagain: 2327144634Sume if (!(p = fgets(hostbuf, sizeof hostbuf, *hostf))) 232861877Sume return (NULL); 232961877Sume if (*p == '#') 233061877Sume goto again; 2331139612Ssobomax cp = strpbrk(p, "#\n"); 2332139612Ssobomax if (cp != NULL) 2333139612Ssobomax *cp = '\0'; 233461877Sume if (!(cp = strpbrk(p, " \t"))) 233561877Sume goto again; 233661877Sume *cp++ = '\0'; 233761877Sume addr = p; 233861877Sume cname = NULL; 233961877Sume /* if this is not something we're looking for, skip it. */ 234061877Sume while (cp && *cp) { 234161877Sume if (*cp == ' ' || *cp == '\t') { 234261877Sume cp++; 234361877Sume continue; 234461877Sume } 234561877Sume tname = cp; 234661877Sume if (cname == NULL) 234761877Sume cname = cp; 234861877Sume if ((cp = strpbrk(cp, " \t")) != NULL) 234961877Sume *cp++ = '\0'; 235061877Sume if (strcasecmp(name, tname) == 0) 235161877Sume goto found; 235261877Sume } 235361877Sume goto again; 235461877Sume 235561877Sumefound: 2356105940Sume /* we should not glob socktype/protocol here */ 2357105940Sume memset(&hints, 0, sizeof(hints)); 2358105940Sume hints.ai_family = pai->ai_family; 2359105940Sume hints.ai_socktype = SOCK_DGRAM; 2360105940Sume hints.ai_protocol = 0; 236161877Sume hints.ai_flags = AI_NUMERICHOST; 2362105940Sume error = getaddrinfo(addr, "0", &hints, &res0); 236361877Sume if (error) 236461877Sume goto again; 236561877Sume#ifdef FILTER_V4MAPPED 236661877Sume /* XXX should check all items in the chain */ 236761877Sume if (res0->ai_family == AF_INET6 && 236861877Sume IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)res0->ai_addr)->sin6_addr)) { 236961877Sume freeaddrinfo(res0); 237061877Sume goto again; 237161877Sume } 237261877Sume#endif 237361877Sume for (res = res0; res; res = res->ai_next) { 237461877Sume /* cover it up */ 237561877Sume res->ai_flags = pai->ai_flags; 2376105940Sume res->ai_socktype = pai->ai_socktype; 2377105940Sume res->ai_protocol = pai->ai_protocol; 237861877Sume 237961877Sume if (pai->ai_flags & AI_CANONNAME) { 238061877Sume if (get_canonname(pai, res, cname) != 0) { 238161877Sume freeaddrinfo(res0); 238261877Sume goto again; 238361877Sume } 238461877Sume } 238561877Sume } 238661877Sume return res0; 238761877Sume} 238861877Sume 238961877Sume/*ARGSUSED*/ 239061877Sumestatic int 2391157119Sume_files_getaddrinfo(void *rv, void *cb_data, va_list ap) 239265532Snectar{ 239365532Snectar const char *name; 239461877Sume const struct addrinfo *pai; 239561877Sume struct addrinfo sentinel, *cur; 239661877Sume struct addrinfo *p; 2397144634Sume FILE *hostf = NULL; 239861877Sume 239965532Snectar name = va_arg(ap, char *); 240065532Snectar pai = va_arg(ap, struct addrinfo *); 240165532Snectar 240265532Snectar memset(&sentinel, 0, sizeof(sentinel)); 240361877Sume cur = &sentinel; 240461877Sume 2405144634Sume _sethtent(&hostf); 2406144634Sume while ((p = _gethtent(&hostf, name, pai)) != NULL) { 240761877Sume cur->ai_next = p; 240861877Sume while (cur && cur->ai_next) 240961877Sume cur = cur->ai_next; 241061877Sume } 2411144634Sume _endhtent(&hostf); 241261877Sume 241365532Snectar *((struct addrinfo **)rv) = sentinel.ai_next; 241465532Snectar if (sentinel.ai_next == NULL) 241565532Snectar return NS_NOTFOUND; 241665532Snectar return NS_SUCCESS; 241761877Sume} 241861877Sume 241961877Sume#ifdef YP 242061877Sume/*ARGSUSED*/ 242165532Snectarstatic struct addrinfo * 2422157119Sume_yphostent(char *line, const struct addrinfo *pai) 242361877Sume{ 242461877Sume struct addrinfo sentinel, *cur; 242565532Snectar struct addrinfo hints, *res, *res0; 242661877Sume int error; 242765532Snectar char *p = line; 242865532Snectar const char *addr, *canonname; 242965532Snectar char *nextline; 243065532Snectar char *cp; 243161877Sume 243265532Snectar addr = canonname = NULL; 243365532Snectar 243465532Snectar memset(&sentinel, 0, sizeof(sentinel)); 243561877Sume cur = &sentinel; 243661877Sume 243765532Snectarnextline: 243865532Snectar /* terminate line */ 243965532Snectar cp = strchr(p, '\n'); 244065532Snectar if (cp) { 244165532Snectar *cp++ = '\0'; 244265532Snectar nextline = cp; 244365532Snectar } else 244465532Snectar nextline = NULL; 244561877Sume 244665532Snectar cp = strpbrk(p, " \t"); 244765532Snectar if (cp == NULL) { 244865532Snectar if (canonname == NULL) 244965532Snectar return (NULL); 245065532Snectar else 245165532Snectar goto done; 245261877Sume } 245365532Snectar *cp++ = '\0'; 245461877Sume 245565532Snectar addr = p; 245661877Sume 245765532Snectar while (cp && *cp) { 245865532Snectar if (*cp == ' ' || *cp == '\t') { 245965532Snectar cp++; 246061877Sume continue; 246165532Snectar } 246265532Snectar if (!canonname) 246365532Snectar canonname = cp; 246465532Snectar if ((cp = strpbrk(cp, " \t")) != NULL) 246565532Snectar *cp++ = '\0'; 246665532Snectar } 246761877Sume 2468121474Sume hints = *pai; 246965532Snectar hints.ai_flags = AI_NUMERICHOST; 2470121474Sume error = getaddrinfo(addr, NULL, &hints, &res0); 247165532Snectar if (error == 0) { 247265532Snectar for (res = res0; res; res = res->ai_next) { 247365532Snectar /* cover it up */ 247465532Snectar res->ai_flags = pai->ai_flags; 247561877Sume 247665532Snectar if (pai->ai_flags & AI_CANONNAME) 247765532Snectar (void)get_canonname(pai, res, canonname); 247861877Sume } 247965532Snectar } else 248065532Snectar res0 = NULL; 248165532Snectar if (res0) { 248265532Snectar cur->ai_next = res0; 248361877Sume while (cur && cur->ai_next) 248461877Sume cur = cur->ai_next; 248561877Sume } 248661877Sume 248765532Snectar if (nextline) { 248865532Snectar p = nextline; 248965532Snectar goto nextline; 249065532Snectar } 249161877Sume 249265532Snectardone: 249365532Snectar return sentinel.ai_next; 249461877Sume} 249565532Snectar 249665532Snectar/*ARGSUSED*/ 249765532Snectarstatic int 2498157119Sume_yp_getaddrinfo(void *rv, void *cb_data, va_list ap) 249965532Snectar{ 250065532Snectar struct addrinfo sentinel, *cur; 250165532Snectar struct addrinfo *ai = NULL; 2502144679Sume char *ypbuf; 2503144679Sume int ypbuflen, r; 250465532Snectar const char *name; 250565532Snectar const struct addrinfo *pai; 2506144679Sume char *ypdomain; 250765532Snectar 2508144679Sume if (_yp_check(&ypdomain) == 0) 2509144679Sume return NS_UNAVAIL; 2510144679Sume 251165532Snectar name = va_arg(ap, char *); 251265532Snectar pai = va_arg(ap, const struct addrinfo *); 251365532Snectar 251465532Snectar memset(&sentinel, 0, sizeof(sentinel)); 251565532Snectar cur = &sentinel; 251665532Snectar 251765532Snectar /* hosts.byname is only for IPv4 (Solaris8) */ 251865532Snectar if (pai->ai_family == PF_UNSPEC || pai->ai_family == PF_INET) { 2519144679Sume r = yp_match(ypdomain, "hosts.byname", name, 2520144679Sume (int)strlen(name), &ypbuf, &ypbuflen); 252165532Snectar if (r == 0) { 252265532Snectar struct addrinfo ai4; 252365532Snectar 252465532Snectar ai4 = *pai; 252565532Snectar ai4.ai_family = AF_INET; 2526144679Sume ai = _yphostent(ypbuf, &ai4); 252765532Snectar if (ai) { 252865532Snectar cur->ai_next = ai; 252965532Snectar while (cur && cur->ai_next) 253065532Snectar cur = cur->ai_next; 253165532Snectar } 2532146190Sume free(ypbuf); 253365532Snectar } 253465532Snectar } 253565532Snectar 253665532Snectar /* ipnodes.byname can hold both IPv4/v6 */ 2537144679Sume r = yp_match(ypdomain, "ipnodes.byname", name, 2538144679Sume (int)strlen(name), &ypbuf, &ypbuflen); 253965532Snectar if (r == 0) { 2540144679Sume ai = _yphostent(ypbuf, pai); 2541144679Sume if (ai) 254265532Snectar cur->ai_next = ai; 2543144679Sume free(ypbuf); 254465532Snectar } 254565532Snectar 254665532Snectar if (sentinel.ai_next == NULL) { 2547156960Sume RES_SET_H_ERRNO(__res_state(), HOST_NOT_FOUND); 254865532Snectar return NS_NOTFOUND; 254965532Snectar } 255065532Snectar *((struct addrinfo **)rv) = sentinel.ai_next; 255165532Snectar return NS_SUCCESS; 255265532Snectar} 255361877Sume#endif 255461877Sume 255561877Sume/* resolver logic */ 255661877Sume 255761877Sume/* 255861877Sume * Formulate a normal query, send, and await answer. 255961877Sume * Returned answer is placed in supplied buffer "answer". 256061877Sume * Perform preliminary check of answer, returning success only 256161877Sume * if no error is indicated and the answer count is nonzero. 256261877Sume * Return the size of the response on success, -1 on error. 256361877Sume * Error number is left in h_errno. 256461877Sume * 256561877Sume * Caller must parse answer and determine whether it answers the question. 256661877Sume */ 256761877Sumestatic int 2568157119Sumeres_queryN(const char *name, struct res_target *target, res_state res) 256961877Sume{ 2570103357Sume u_char *buf; 257161877Sume HEADER *hp; 257261877Sume int n; 2573157203Sume u_int oflags; 257461877Sume struct res_target *t; 257561877Sume int rcode; 257661877Sume int ancount; 257761877Sume 257861877Sume rcode = NOERROR; 257961877Sume ancount = 0; 258061877Sume 2581103357Sume buf = malloc(MAXPACKET); 2582103357Sume if (!buf) { 2583156960Sume RES_SET_H_ERRNO(res, NETDB_INTERNAL); 2584103357Sume return -1; 2585103357Sume } 2586103357Sume 258761877Sume for (t = target; t; t = t->next) { 258861877Sume int class, type; 258961877Sume u_char *answer; 259061877Sume int anslen; 259161877Sume 259261877Sume hp = (HEADER *)(void *)t->answer; 259361877Sume 259461877Sume /* make it easier... */ 259562614Sitojun class = t->qclass; 259662614Sitojun type = t->qtype; 259761877Sume answer = t->answer; 259861877Sume anslen = t->anslen; 2599157203Sume 2600157203Sume oflags = res->_flags; 2601157203Sume 2602157203Sumeagain: 2603157203Sume hp->rcode = NOERROR; /* default */ 2604157203Sume 260561877Sume#ifdef DEBUG 2606156960Sume if (res->options & RES_DEBUG) 260761877Sume printf(";; res_query(%s, %d, %d)\n", name, class, type); 260861877Sume#endif 260961877Sume 2610156960Sume n = res_nmkquery(res, QUERY, name, class, type, NULL, 0, NULL, 2611103357Sume buf, MAXPACKET); 2612157203Sume if (n > 0 && (res->_flags & RES_F_EDNS0ERR) == 0 && 2613157203Sume (res->options & (RES_USE_EDNS0|RES_USE_DNSSEC)) != 0U) 2614156960Sume n = res_nopt(res, n, buf, MAXPACKET, anslen); 261561877Sume if (n <= 0) { 261661877Sume#ifdef DEBUG 2617156960Sume if (res->options & RES_DEBUG) 261861877Sume printf(";; res_query: mkquery failed\n"); 261961877Sume#endif 2620103357Sume free(buf); 2621156960Sume RES_SET_H_ERRNO(res, NO_RECOVERY); 262261877Sume return (n); 262361877Sume } 2624156960Sume n = res_nsend(res, buf, n, answer, anslen); 262561877Sume if (n < 0) { 2626157203Sume /* 2627157203Sume * if the query choked with EDNS0, retry 2628157203Sume * without EDNS0 2629157203Sume */ 2630157203Sume if ((res->options & (RES_USE_EDNS0|RES_USE_DNSSEC)) 2631157203Sume != 0U && 2632157203Sume ((oflags ^ res->_flags) & RES_F_EDNS0ERR) != 0) { 2633157203Sume res->_flags |= RES_F_EDNS0ERR; 2634157203Sume if (res->options & RES_DEBUG) 2635157203Sume printf(";; res_nquery: retry without EDNS0\n"); 2636157203Sume goto again; 2637157203Sume } 2638157203Sume rcode = hp->rcode; /* record most recent error */ 263961877Sume#ifdef DEBUG 2640156960Sume if (res->options & RES_DEBUG) 264161877Sume printf(";; res_query: send error\n"); 264261877Sume#endif 2643157203Sume continue; 264461877Sume } 264561877Sume 2646157081Sume if (n > anslen) 2647103350Snectar hp->rcode = FORMERR; /* XXX not very informative */ 2648157203Sume if (hp->rcode != NOERROR || ntohs(hp->ancount) == 0) { 264961877Sume rcode = hp->rcode; /* record most recent error */ 265061877Sume#ifdef DEBUG 2651156960Sume if (res->options & RES_DEBUG) 2652105940Sume printf(";; rcode = %u, ancount=%u\n", hp->rcode, 265361877Sume ntohs(hp->ancount)); 265461877Sume#endif 265561877Sume continue; 265661877Sume } 265761877Sume 265861877Sume ancount += ntohs(hp->ancount); 265961877Sume 266061877Sume t->n = n; 266161877Sume } 266261877Sume 2663103357Sume free(buf); 2664103357Sume 266561877Sume if (ancount == 0) { 266661877Sume switch (rcode) { 266761877Sume case NXDOMAIN: 2668156960Sume RES_SET_H_ERRNO(res, HOST_NOT_FOUND); 266961877Sume break; 267061877Sume case SERVFAIL: 2671156960Sume RES_SET_H_ERRNO(res, TRY_AGAIN); 267261877Sume break; 267361877Sume case NOERROR: 2674156960Sume RES_SET_H_ERRNO(res, NO_DATA); 267561877Sume break; 267661877Sume case FORMERR: 267761877Sume case NOTIMP: 267861877Sume case REFUSED: 267961877Sume default: 2680156960Sume RES_SET_H_ERRNO(res, NO_RECOVERY); 268161877Sume break; 268261877Sume } 268361877Sume return (-1); 268461877Sume } 268561877Sume return (ancount); 268661877Sume} 268761877Sume 268861877Sume/* 268961877Sume * Formulate a normal query, send, and retrieve answer in supplied buffer. 269061877Sume * Return the size of the response on success, -1 on error. 269161877Sume * If enabled, implement search rules until answer or unrecoverable failure 269261877Sume * is detected. Error code, if any, is left in h_errno. 269361877Sume */ 269461877Sumestatic int 2695157119Sumeres_searchN(const char *name, struct res_target *target, res_state res) 269661877Sume{ 269761877Sume const char *cp, * const *domain; 269861877Sume HEADER *hp = (HEADER *)(void *)target->answer; /*XXX*/ 269961877Sume u_int dots; 270061877Sume int trailing_dot, ret, saved_herrno; 2701155983Sume int got_nodata = 0, got_servfail = 0, root_on_list = 0; 2702155983Sume int tried_as_is = 0; 2703155983Sume int searched = 0; 2704145113Sume char abuf[MAXDNAME]; 270561877Sume 270661877Sume errno = 0; 2707156960Sume RES_SET_H_ERRNO(res, HOST_NOT_FOUND); /* default, if we never query */ 270861877Sume dots = 0; 270961877Sume for (cp = name; *cp; cp++) 271061877Sume dots += (*cp == '.'); 271161877Sume trailing_dot = 0; 271261877Sume if (cp > name && *--cp == '.') 271361877Sume trailing_dot++; 271461877Sume 271561877Sume /* 271661877Sume * if there aren't any dots, it could be a user-level alias 271761877Sume */ 2718156960Sume if (!dots && 2719156960Sume (cp = res_hostalias(res, name, abuf, sizeof(abuf))) != NULL) 2720156960Sume return (res_queryN(cp, target, res)); 272161877Sume 272261877Sume /* 2723155983Sume * If there are enough dots in the name, let's just give it a 2724155983Sume * try 'as is'. The threshold can be set with the "ndots" option. 2725155983Sume * Also, query 'as is', if there is a trailing dot in the name. 272661877Sume */ 272761877Sume saved_herrno = -1; 2728156960Sume if (dots >= res->ndots || trailing_dot) { 2729156960Sume ret = res_querydomainN(name, NULL, target, res); 2730155983Sume if (ret > 0 || trailing_dot) 273161877Sume return (ret); 2732156155Sume if (errno == ECONNREFUSED) { 2733156960Sume RES_SET_H_ERRNO(res, TRY_AGAIN); 2734156155Sume return (-1); 2735156155Sume } 2736156960Sume switch (res->res_h_errno) { 2737156155Sume case NO_DATA: 2738156155Sume case HOST_NOT_FOUND: 2739156155Sume break; 2740157093Sume case TRY_AGAIN: 2741157093Sume if (hp->rcode == SERVFAIL) 2742157093Sume break; 2743157093Sume /* FALLTHROUGH */ 2744156155Sume default: 2745156155Sume return (-1); 2746156155Sume } 2747156960Sume saved_herrno = res->res_h_errno; 274861877Sume tried_as_is++; 274961877Sume } 275061877Sume 275161877Sume /* 275261877Sume * We do at least one level of search if 275361877Sume * - there is no dot and RES_DEFNAME is set, or 275461877Sume * - there is at least one dot, there is no trailing dot, 275561877Sume * and RES_DNSRCH is set. 275661877Sume */ 2757156960Sume if ((!dots && (res->options & RES_DEFNAMES)) || 2758156960Sume (dots && !trailing_dot && (res->options & RES_DNSRCH))) { 275961877Sume int done = 0; 276061877Sume 2761156960Sume for (domain = (const char * const *)res->dnsrch; 276261877Sume *domain && !done; 276361877Sume domain++) { 2764155983Sume searched = 1; 276561877Sume 2766155983Sume if (domain[0][0] == '\0' || 2767155983Sume (domain[0][0] == '.' && domain[0][1] == '\0')) 2768155983Sume root_on_list++; 2769155983Sume 2770155983Sume if (root_on_list && tried_as_is) 2771155983Sume continue; 2772155983Sume 2773156960Sume ret = res_querydomainN(name, *domain, target, res); 277461877Sume if (ret > 0) 277561877Sume return (ret); 277661877Sume 277761877Sume /* 277861877Sume * If no server present, give up. 277961877Sume * If name isn't found in this domain, 278061877Sume * keep trying higher domains in the search list 278161877Sume * (if that's enabled). 278261877Sume * On a NO_DATA error, keep trying, otherwise 278361877Sume * a wildcard entry of another type could keep us 278461877Sume * from finding this entry higher in the domain. 278561877Sume * If we get some other error (negative answer or 278661877Sume * server failure), then stop searching up, 278761877Sume * but try the input name below in case it's 278861877Sume * fully-qualified. 278961877Sume */ 279061877Sume if (errno == ECONNREFUSED) { 2791156960Sume RES_SET_H_ERRNO(res, TRY_AGAIN); 279261877Sume return (-1); 279361877Sume } 279461877Sume 2795156960Sume switch (res->res_h_errno) { 279661877Sume case NO_DATA: 279761877Sume got_nodata++; 279861877Sume /* FALLTHROUGH */ 279961877Sume case HOST_NOT_FOUND: 280061877Sume /* keep trying */ 280161877Sume break; 280261877Sume case TRY_AGAIN: 2803157093Sume got_servfail++; 280461877Sume if (hp->rcode == SERVFAIL) { 280561877Sume /* try next search element, if any */ 280661877Sume break; 280761877Sume } 280861877Sume /* FALLTHROUGH */ 280961877Sume default: 281061877Sume /* anything else implies that we're done */ 281161877Sume done++; 281261877Sume } 281361877Sume /* 281461877Sume * if we got here for some reason other than DNSRCH, 281561877Sume * we only wanted one iteration of the loop, so stop. 281661877Sume */ 2817156960Sume if (!(res->options & RES_DNSRCH)) 281861877Sume done++; 281961877Sume } 282061877Sume } 282161877Sume 2822156960Sume switch (res->res_h_errno) { 2823156155Sume case NO_DATA: 2824156155Sume case HOST_NOT_FOUND: 2825156155Sume break; 2826157093Sume case TRY_AGAIN: 2827157093Sume if (hp->rcode == SERVFAIL) 2828157093Sume break; 2829157093Sume /* FALLTHROUGH */ 2830156155Sume default: 2831156155Sume goto giveup; 2832156155Sume } 2833156155Sume 283461877Sume /* 2835155983Sume * If the query has not already been tried as is then try it 2836155983Sume * unless RES_NOTLDQUERY is set and there were no dots. 283761877Sume */ 2838156960Sume if ((dots || !searched || !(res->options & RES_NOTLDQUERY)) && 2839155983Sume !(tried_as_is || root_on_list)) { 2840156960Sume ret = res_querydomainN(name, NULL, target, res); 284161877Sume if (ret > 0) 284261877Sume return (ret); 284361877Sume } 284461877Sume 284561877Sume /* 284661877Sume * if we got here, we didn't satisfy the search. 284761877Sume * if we did an initial full query, return that query's h_errno 284861877Sume * (note that we wouldn't be here if that query had succeeded). 284961877Sume * else if we ever got a nodata, send that back as the reason. 285061877Sume * else send back meaningless h_errno, that being the one from 285161877Sume * the last DNSRCH we did. 285261877Sume */ 2853156155Sumegiveup: 285461877Sume if (saved_herrno != -1) 2855156960Sume RES_SET_H_ERRNO(res, saved_herrno); 285661877Sume else if (got_nodata) 2857156960Sume RES_SET_H_ERRNO(res, NO_DATA); 285861877Sume else if (got_servfail) 2859156960Sume RES_SET_H_ERRNO(res, TRY_AGAIN); 286061877Sume return (-1); 286161877Sume} 286261877Sume 286361877Sume/* 286461877Sume * Perform a call on res_query on the concatenation of name and domain, 286561877Sume * removing a trailing dot from name if domain is NULL. 286661877Sume */ 286761877Sumestatic int 2868157119Sumeres_querydomainN(const char *name, const char *domain, 2869157119Sume struct res_target *target, res_state res) 287061877Sume{ 287161877Sume char nbuf[MAXDNAME]; 287261877Sume const char *longname = nbuf; 287361877Sume size_t n, d; 287461877Sume 287561877Sume#ifdef DEBUG 2876156960Sume if (res->options & RES_DEBUG) 287761877Sume printf(";; res_querydomain(%s, %s)\n", 287861877Sume name, domain?domain:"<Nil>"); 287961877Sume#endif 288061877Sume if (domain == NULL) { 288161877Sume /* 288261877Sume * Check for trailing '.'; 288361877Sume * copy without '.' if present. 288461877Sume */ 288561877Sume n = strlen(name); 288661877Sume if (n >= MAXDNAME) { 2887156960Sume RES_SET_H_ERRNO(res, NO_RECOVERY); 288861877Sume return (-1); 288961877Sume } 289061877Sume if (n > 0 && name[--n] == '.') { 289161877Sume strncpy(nbuf, name, n); 289261877Sume nbuf[n] = '\0'; 289361877Sume } else 289461877Sume longname = name; 289561877Sume } else { 289661877Sume n = strlen(name); 289761877Sume d = strlen(domain); 289861877Sume if (n + d + 1 >= MAXDNAME) { 2899156960Sume RES_SET_H_ERRNO(res, NO_RECOVERY); 290061877Sume return (-1); 290161877Sume } 2902105940Sume snprintf(nbuf, sizeof(nbuf), "%s.%s", name, domain); 290361877Sume } 2904156960Sume return (res_queryN(longname, target, res)); 290561877Sume} 2906