getaddrinfo.c revision 229766
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: head/lib/libc/net/getaddrinfo.c 229766 2012-01-07 09:01:19Z ume $"); 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> 65121747Sume#include <sys/queue.h> 66121747Sume#ifdef INET6 67121747Sume#include <net/if_var.h> 68121747Sume#include <sys/sysctl.h> 69129901Sume#include <sys/ioctl.h> 70121747Sume#include <netinet6/in6_var.h> /* XXX */ 71121747Sume#endif 7255163Sshin#include <arpa/inet.h> 7355163Sshin#include <arpa/nameser.h> 74121474Sume#include <rpc/rpc.h> 75121474Sume#include <rpcsvc/yp_prot.h> 76121474Sume#include <rpcsvc/ypclnt.h> 7755163Sshin#include <netdb.h> 7855163Sshin#include <resolv.h> 7955163Sshin#include <string.h> 8055163Sshin#include <stdlib.h> 8155163Sshin#include <stddef.h> 8255163Sshin#include <ctype.h> 8355163Sshin#include <unistd.h> 8455163Sshin#include <stdio.h> 8561877Sume#include <errno.h> 86102237Spirzyk 87102237Spirzyk#include "res_config.h" 88102237Spirzyk 8978012Sume#ifdef DEBUG 9078012Sume#include <syslog.h> 9178012Sume#endif 9255163Sshin 9365532Snectar#include <stdarg.h> 9465532Snectar#include <nsswitch.h> 9571579Sdeischen#include "un-namespace.h" 96111618Snectar#include "libc_private.h" 97158115Sume#ifdef NS_CACHING 98158115Sume#include "nscache.h" 99158115Sume#endif 10065532Snectar 10155163Sshin#if defined(__KAME__) && defined(INET6) 10255163Sshin# define FAITH 10355163Sshin#endif 10455163Sshin 105105940Sume#define ANY 0 106105940Sume#define YES 1 107105940Sume#define NO 0 10855163Sshin 10955163Sshinstatic const char in_addrany[] = { 0, 0, 0, 0 }; 110105940Sumestatic const char in_loopback[] = { 127, 0, 0, 1 }; 111105940Sume#ifdef INET6 11255163Sshinstatic const char in6_addrany[] = { 11355163Sshin 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 11455163Sshin}; 11555163Sshinstatic const char in6_loopback[] = { 11655163Sshin 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 11755163Sshin}; 118105940Sume#endif 11955163Sshin 120121747Sumestruct policyqueue { 121121747Sume TAILQ_ENTRY(policyqueue) pc_entry; 122121747Sume#ifdef INET6 123121747Sume struct in6_addrpolicy pc_policy; 124121747Sume#endif 125121747Sume}; 126121747SumeTAILQ_HEAD(policyhead, policyqueue); 127121747Sume 12855163Sshinstatic const struct afd { 12955163Sshin int a_af; 13055163Sshin int a_addrlen; 131146244Sume socklen_t a_socklen; 13255163Sshin int a_off; 13355163Sshin const char *a_addrany; 13473665Sobrien const char *a_loopback; 13555163Sshin int a_scoped; 13655163Sshin} afdl [] = { 13755163Sshin#ifdef INET6 13855163Sshin#define N_INET6 0 13955163Sshin {PF_INET6, sizeof(struct in6_addr), 14055163Sshin sizeof(struct sockaddr_in6), 14155163Sshin offsetof(struct sockaddr_in6, sin6_addr), 14255163Sshin in6_addrany, in6_loopback, 1}, 14355163Sshin#define N_INET 1 14455163Sshin#else 14555163Sshin#define N_INET 0 14655163Sshin#endif 14755163Sshin {PF_INET, sizeof(struct in_addr), 14855163Sshin sizeof(struct sockaddr_in), 14955163Sshin offsetof(struct sockaddr_in, sin_addr), 15055163Sshin in_addrany, in_loopback, 0}, 15155163Sshin {0, 0, 0, 0, NULL, NULL, 0}, 15255163Sshin}; 15355163Sshin 15455163Sshinstruct explore { 15561877Sume int e_af; 15655163Sshin int e_socktype; 15755163Sshin int e_protocol; 15855163Sshin const char *e_protostr; 15955163Sshin int e_wild; 160105940Sume#define WILD_AF(ex) ((ex)->e_wild & 0x01) 161105940Sume#define WILD_SOCKTYPE(ex) ((ex)->e_wild & 0x02) 162105940Sume#define WILD_PROTOCOL(ex) ((ex)->e_wild & 0x04) 16355163Sshin}; 16455163Sshin 16555163Sshinstatic const struct explore explore[] = { 16661877Sume#if 0 167190416Sume { PF_LOCAL, ANY, ANY, NULL, 0x01 }, 16861877Sume#endif 16961877Sume#ifdef INET6 170121474Sume { PF_INET6, SOCK_DGRAM, IPPROTO_UDP, "udp", 0x07 }, 171121474Sume { PF_INET6, SOCK_STREAM, IPPROTO_TCP, "tcp", 0x07 }, 172190416Sume { PF_INET6, SOCK_STREAM, IPPROTO_SCTP, "sctp", 0x03 }, 173190416Sume { PF_INET6, SOCK_SEQPACKET, IPPROTO_SCTP, "sctp", 0x07 }, 174121474Sume { PF_INET6, SOCK_RAW, ANY, NULL, 0x05 }, 17561877Sume#endif 176121474Sume { PF_INET, SOCK_DGRAM, IPPROTO_UDP, "udp", 0x07 }, 177121474Sume { PF_INET, SOCK_STREAM, IPPROTO_TCP, "tcp", 0x07 }, 178190416Sume { PF_INET, SOCK_STREAM, IPPROTO_SCTP, "sctp", 0x03 }, 179190416Sume { PF_INET, SOCK_SEQPACKET, IPPROTO_SCTP, "sctp", 0x07 }, 180121474Sume { PF_INET, SOCK_RAW, ANY, NULL, 0x05 }, 18161877Sume { -1, 0, 0, NULL, 0 }, 18255163Sshin}; 18355163Sshin 18455163Sshin#ifdef INET6 185105940Sume#define PTON_MAX 16 18655163Sshin#else 187105940Sume#define PTON_MAX 4 18855163Sshin#endif 18955163Sshin 190121747Sume#define AIO_SRCFLAG_DEPRECATED 0x1 191121747Sume 192121747Sumestruct ai_order { 193121747Sume union { 194121747Sume struct sockaddr_storage aiou_ss; 195121747Sume struct sockaddr aiou_sa; 196121747Sume } aio_src_un; 197121747Sume#define aio_srcsa aio_src_un.aiou_sa 198121747Sume u_int32_t aio_srcflag; 199121747Sume int aio_srcscope; 200121747Sume int aio_dstscope; 201121747Sume struct policyqueue *aio_srcpolicy; 202121747Sume struct policyqueue *aio_dstpolicy; 203121747Sume struct addrinfo *aio_ai; 204121747Sume int aio_matchlen; 205121747Sume}; 206121747Sume 20765532Snectarstatic const ns_src default_dns_files[] = { 20865532Snectar { NSSRC_FILES, NS_SUCCESS }, 20965532Snectar { NSSRC_DNS, NS_SUCCESS }, 21065532Snectar { 0 } 21165532Snectar}; 21265532Snectar 21361877Sumestruct res_target { 21461877Sume struct res_target *next; 21561877Sume const char *name; /* domain name */ 21662614Sitojun int qclass, qtype; /* class and type of query */ 21761877Sume u_char *answer; /* buffer to put answer */ 21861877Sume int anslen; /* size of answer buffer */ 21961877Sume int n; /* result length */ 22061877Sume}; 22161877Sume 222121426Sume#define MAXPACKET (64*1024) 223121426Sume 224121426Sumetypedef union { 225121426Sume HEADER hdr; 226121426Sume u_char buf[MAXPACKET]; 227121426Sume} querybuf; 228121426Sume 229160593Sumestatic int str2number(const char *, int *); 230190525Sumestatic int explore_copy(const struct addrinfo *, const struct addrinfo *, 231190525Sume struct addrinfo **); 23292941Sobrienstatic int explore_null(const struct addrinfo *, 23392941Sobrien const char *, struct addrinfo **); 23492941Sobrienstatic int explore_numeric(const struct addrinfo *, const char *, 235140906Sume const char *, struct addrinfo **, const char *); 23692941Sobrienstatic int explore_numeric_scope(const struct addrinfo *, const char *, 23792941Sobrien const char *, struct addrinfo **); 23892941Sobrienstatic int get_canonname(const struct addrinfo *, 23992941Sobrien struct addrinfo *, const char *); 24092941Sobrienstatic struct addrinfo *get_ai(const struct addrinfo *, 24192941Sobrien const struct afd *, const char *); 242190525Sumestatic struct addrinfo *copy_ai(const struct addrinfo *); 24392905Sobrienstatic int get_portmatch(const struct addrinfo *, const char *); 24492905Sobrienstatic int get_port(struct addrinfo *, const char *, int); 24592905Sobrienstatic const struct afd *find_afd(int); 246121474Sumestatic int addrconfig(struct addrinfo *); 247129901Sumestatic void set_source(struct ai_order *, struct policyhead *); 248121747Sumestatic int comp_dst(const void *, const void *); 24961877Sume#ifdef INET6 250105943Sumestatic int ip6_str2scopeid(char *, struct sockaddr_in6 *, u_int32_t *); 25161877Sume#endif 252121747Sumestatic int gai_addr2scopetype(struct sockaddr *); 25355163Sshin 254121426Sumestatic int explore_fqdn(const struct addrinfo *, const char *, 255121426Sume const char *, struct addrinfo **); 256121426Sume 257121747Sumestatic int reorder(struct addrinfo *); 258121747Sumestatic int get_addrselectpolicy(struct policyhead *); 259121747Sumestatic void free_addrselectpolicy(struct policyhead *); 260121747Sumestatic struct policyqueue *match_addrselectpolicy(struct sockaddr *, 261121747Sume struct policyhead *); 262129901Sumestatic int matchlen(struct sockaddr *, struct sockaddr *); 263121747Sume 26492941Sobrienstatic struct addrinfo *getanswer(const querybuf *, int, const char *, int, 265156960Sume const struct addrinfo *, res_state); 266121426Sume#if defined(RESOLVSORT) 267156960Sumestatic int addr4sort(struct addrinfo *, res_state); 268121426Sume#endif 269105943Sumestatic int _dns_getaddrinfo(void *, void *, va_list); 270144634Sumestatic void _sethtent(FILE **); 271144634Sumestatic void _endhtent(FILE **); 272144634Sumestatic struct addrinfo *_gethtent(FILE **, const char *, 273144634Sume const struct addrinfo *); 27492905Sobrienstatic int _files_getaddrinfo(void *, void *, va_list); 27561877Sume#ifdef YP 27692905Sobrienstatic struct addrinfo *_yphostent(char *, const struct addrinfo *); 27792905Sobrienstatic int _yp_getaddrinfo(void *, void *, va_list); 27861877Sume#endif 279158115Sume#ifdef NS_CACHING 280158115Sumestatic int addrinfo_id_func(char *, size_t *, va_list, void *); 281158115Sumestatic int addrinfo_marshal_func(char *, size_t *, void *, va_list, void *); 282158115Sumestatic int addrinfo_unmarshal_func(char *, size_t, void *, va_list, void *); 283158115Sume#endif 28461877Sume 285156960Sumestatic int res_queryN(const char *, struct res_target *, res_state); 286156960Sumestatic int res_searchN(const char *, struct res_target *, res_state); 28792941Sobrienstatic int res_querydomainN(const char *, const char *, 288156960Sume struct res_target *, res_state); 28961877Sume 29055163Sshin/* XXX macros that make external reference is BAD. */ 29155163Sshin 292105940Sume#define GET_AI(ai, afd, addr) \ 29355163Sshindo { \ 29455163Sshin /* external reference: pai, error, and label free */ \ 29555163Sshin (ai) = get_ai(pai, (afd), (addr)); \ 29655163Sshin if ((ai) == NULL) { \ 29755163Sshin error = EAI_MEMORY; \ 29855163Sshin goto free; \ 29955163Sshin } \ 30061877Sume} while (/*CONSTCOND*/0) 30155163Sshin 302105940Sume#define GET_PORT(ai, serv) \ 30355163Sshindo { \ 30455163Sshin /* external reference: error and label free */ \ 30555163Sshin error = get_port((ai), (serv), 0); \ 30655163Sshin if (error != 0) \ 30755163Sshin goto free; \ 30861877Sume} while (/*CONSTCOND*/0) 30955163Sshin 310105940Sume#define GET_CANONNAME(ai, str) \ 31155163Sshindo { \ 31255163Sshin /* external reference: pai, error and label free */ \ 31355163Sshin error = get_canonname(pai, (ai), (str)); \ 31455163Sshin if (error != 0) \ 31555163Sshin goto free; \ 31661877Sume} while (/*CONSTCOND*/0) 31755163Sshin 318105940Sume#define ERR(err) \ 31955163Sshindo { \ 32055163Sshin /* external reference: error, and label bad */ \ 32155163Sshin error = (err); \ 32255163Sshin goto bad; \ 32361877Sume /*NOTREACHED*/ \ 32461877Sume} while (/*CONSTCOND*/0) 32555163Sshin 326105940Sume#define MATCH_FAMILY(x, y, w) \ 32761877Sume ((x) == (y) || (/*CONSTCOND*/(w) && ((x) == PF_UNSPEC || (y) == PF_UNSPEC))) 328105940Sume#define MATCH(x, y, w) \ 32961877Sume ((x) == (y) || (/*CONSTCOND*/(w) && ((x) == ANY || (y) == ANY))) 33055163Sshin 33155163Sshinvoid 332157119Sumefreeaddrinfo(struct addrinfo *ai) 33355163Sshin{ 33455163Sshin struct addrinfo *next; 33555163Sshin 33655163Sshin do { 33755163Sshin next = ai->ai_next; 33855163Sshin if (ai->ai_canonname) 33955163Sshin free(ai->ai_canonname); 34055163Sshin /* no need to free(ai->ai_addr) */ 34155163Sshin free(ai); 34261877Sume ai = next; 34361877Sume } while (ai); 34455163Sshin} 34555163Sshin 34655163Sshinstatic int 347160593Sumestr2number(const char *p, int *portp) 34855163Sshin{ 34962836Sitojun char *ep; 350140908Sume unsigned long v; 35162836Sitojun 35262836Sitojun if (*p == '\0') 353140908Sume return -1; 35462836Sitojun ep = NULL; 355105943Sume errno = 0; 356140908Sume v = strtoul(p, &ep, 10); 357160593Sume if (errno == 0 && ep && *ep == '\0' && v <= UINT_MAX) { 358160593Sume *portp = v; 359160593Sume return 0; 360160593Sume } else 361140908Sume return -1; 36255163Sshin} 36355163Sshin 36455163Sshinint 365157119Sumegetaddrinfo(const char *hostname, const char *servname, 366157119Sume const struct addrinfo *hints, struct addrinfo **res) 36755163Sshin{ 36855163Sshin struct addrinfo sentinel; 36955163Sshin struct addrinfo *cur; 37055163Sshin int error = 0; 371190525Sume struct addrinfo ai, ai0, *afai; 37255163Sshin struct addrinfo *pai; 373190525Sume const struct afd *afd; 37455163Sshin const struct explore *ex; 375190525Sume struct addrinfo *afailist[sizeof(afdl)/sizeof(afdl[0])]; 376190525Sume struct addrinfo *afai_unspec; 377190525Sume int found; 378121747Sume int numeric = 0; 37955163Sshin 380190525Sume /* ensure we return NULL on errors */ 381190525Sume *res = NULL; 382190525Sume 383190525Sume memset(&ai, 0, sizeof(ai)); 384190525Sume 385190525Sume memset(afailist, 0, sizeof(afailist)); 386190525Sume afai_unspec = NULL; 387190525Sume 38861877Sume memset(&sentinel, 0, sizeof(sentinel)); 38955163Sshin cur = &sentinel; 39055163Sshin pai = &ai; 39155163Sshin pai->ai_flags = 0; 39255163Sshin pai->ai_family = PF_UNSPEC; 39355163Sshin pai->ai_socktype = ANY; 39455163Sshin pai->ai_protocol = ANY; 39555163Sshin pai->ai_addrlen = 0; 39655163Sshin pai->ai_canonname = NULL; 39755163Sshin pai->ai_addr = NULL; 39855163Sshin pai->ai_next = NULL; 39955163Sshin 40055163Sshin if (hostname == NULL && servname == NULL) 40155163Sshin return EAI_NONAME; 40255163Sshin if (hints) { 40355163Sshin /* error check for hints */ 40455163Sshin if (hints->ai_addrlen || hints->ai_canonname || 40555163Sshin hints->ai_addr || hints->ai_next) 40655163Sshin ERR(EAI_BADHINTS); /* xxx */ 40755163Sshin if (hints->ai_flags & ~AI_MASK) 40855163Sshin ERR(EAI_BADFLAGS); 40955163Sshin switch (hints->ai_family) { 41055163Sshin case PF_UNSPEC: 41155163Sshin case PF_INET: 41255163Sshin#ifdef INET6 41355163Sshin case PF_INET6: 41455163Sshin#endif 41555163Sshin break; 41655163Sshin default: 41755163Sshin ERR(EAI_FAMILY); 41855163Sshin } 41955163Sshin memcpy(pai, hints, sizeof(*pai)); 42055163Sshin 42155163Sshin /* 42255163Sshin * if both socktype/protocol are specified, check if they 42355163Sshin * are meaningful combination. 42455163Sshin */ 42555163Sshin if (pai->ai_socktype != ANY && pai->ai_protocol != ANY) { 42661877Sume for (ex = explore; ex->e_af >= 0; ex++) { 427190525Sume if (!MATCH_FAMILY(pai->ai_family, ex->e_af, 428190525Sume WILD_AF(ex))) 42961877Sume continue; 430190525Sume if (!MATCH(pai->ai_socktype, ex->e_socktype, 431190525Sume WILD_SOCKTYPE(ex))) 43255163Sshin continue; 433190525Sume if (!MATCH(pai->ai_protocol, ex->e_protocol, 434190525Sume WILD_PROTOCOL(ex))) 43555163Sshin continue; 436190525Sume 437190525Sume /* matched */ 438190525Sume break; 43955163Sshin } 440190416Sume 441190416Sume if (ex->e_af < 0) 442190416Sume ERR(EAI_BADHINTS); 44355163Sshin } 44455163Sshin } 44555163Sshin 44661877Sume /* 44761877Sume * check for special cases. (1) numeric servname is disallowed if 44861877Sume * socktype/protocol are left unspecified. (2) servname is disallowed 44961877Sume * for raw and other inet{,6} sockets. 45055163Sshin */ 45155163Sshin if (MATCH_FAMILY(pai->ai_family, PF_INET, 1) 45261877Sume#ifdef PF_INET6 453121474Sume || MATCH_FAMILY(pai->ai_family, PF_INET6, 1) 45455163Sshin#endif 45555163Sshin ) { 45661877Sume ai0 = *pai; /* backup *pai */ 45755163Sshin 45861877Sume if (pai->ai_family == PF_UNSPEC) { 45961877Sume#ifdef PF_INET6 46055163Sshin pai->ai_family = PF_INET6; 46155163Sshin#else 46255163Sshin pai->ai_family = PF_INET; 46355163Sshin#endif 46461877Sume } 46555163Sshin error = get_portmatch(pai, servname); 46655163Sshin if (error) 46755163Sshin ERR(error); 46861877Sume 46961877Sume *pai = ai0; 47055163Sshin } 47155163Sshin 47261877Sume ai0 = *pai; 47361877Sume 474190525Sume /* 475190525Sume * NULL hostname, or numeric hostname. 476190525Sume * If numeric representation of AF1 can be interpreted as FQDN 477190525Sume * representation of AF2, we need to think again about the code below. 478190525Sume */ 479190525Sume found = 0; 480190525Sume for (afd = afdl; afd->a_af; afd++) { 48155163Sshin *pai = ai0; 48255163Sshin 483190525Sume if (!MATCH_FAMILY(pai->ai_family, afd->a_af, 1)) 48455163Sshin continue; 48561877Sume 48655163Sshin if (pai->ai_family == PF_UNSPEC) 487190525Sume pai->ai_family = afd->a_af; 48855163Sshin 489190525Sume if (hostname == NULL) { 490190525Sume error = explore_null(pai, servname, 491190525Sume &afailist[afd - afdl]); 492190525Sume 493190525Sume /* 494190525Sume * Errors from explore_null should be unexpected and 495190525Sume * be caught to avoid returning an incomplete result. 496190525Sume */ 497190525Sume if (error != 0) 498190525Sume goto bad; 499190525Sume } else { 500140906Sume error = explore_numeric_scope(pai, hostname, servname, 501190525Sume &afailist[afd - afdl]); 50255163Sshin 503190525Sume /* 504190525Sume * explore_numeric_scope returns an error for address 505190525Sume * families that do not match that of hostname. 506190525Sume * Thus we should not catch the error at this moment. 507190525Sume */ 508190525Sume } 509121474Sume 510190525Sume if (!error && afailist[afd - afdl]) 511190525Sume found++; 51255163Sshin } 513190525Sume if (found) { 514121747Sume numeric = 1; 515190525Sume goto globcopy; 516121747Sume } 517121474Sume 518121425Sume if (hostname == NULL) 519121425Sume ERR(EAI_NONAME); /* used to be EAI_NODATA */ 52055163Sshin if (pai->ai_flags & AI_NUMERICHOST) 52190053Sroam ERR(EAI_NONAME); 52255163Sshin 523121474Sume if ((pai->ai_flags & AI_ADDRCONFIG) != 0 && !addrconfig(&ai0)) 524121474Sume ERR(EAI_FAIL); 525121474Sume 52661877Sume /* 52761877Sume * hostname as alphabetical name. 52861877Sume */ 529190525Sume *pai = ai0; 530190525Sume error = explore_fqdn(pai, hostname, servname, &afai_unspec); 531190525Sume 532190525Sumeglobcopy: 53361877Sume for (ex = explore; ex->e_af >= 0; ex++) { 53461877Sume *pai = ai0; 53555163Sshin 536190525Sume if (!MATCH_FAMILY(pai->ai_family, ex->e_af, WILD_AF(ex))) 53761877Sume continue; 538121474Sume if (!MATCH(pai->ai_socktype, ex->e_socktype, 539190525Sume WILD_SOCKTYPE(ex))) 54061877Sume continue; 541121474Sume if (!MATCH(pai->ai_protocol, ex->e_protocol, 542190525Sume WILD_PROTOCOL(ex))) 54361877Sume continue; 54455163Sshin 545190525Sume if (pai->ai_family == PF_UNSPEC) 546190525Sume pai->ai_family = ex->e_af; 54761877Sume if (pai->ai_socktype == ANY && ex->e_socktype != ANY) 54861877Sume pai->ai_socktype = ex->e_socktype; 54961877Sume if (pai->ai_protocol == ANY && ex->e_protocol != ANY) 55061877Sume pai->ai_protocol = ex->e_protocol; 55161877Sume 552190525Sume /* 553190525Sume * if the servname does not match socktype/protocol, ignore it. 554190525Sume */ 555190525Sume if (get_portmatch(pai, servname) != 0) 556190525Sume continue; 55761877Sume 558190525Sume if (afai_unspec) 559190525Sume afai = afai_unspec; 560190525Sume else { 561190525Sume if ((afd = find_afd(pai->ai_family)) == NULL) 562190525Sume continue; 563190525Sume /* XXX assumes that afd points inside afdl[] */ 564190525Sume afai = afailist[afd - afdl]; 565190525Sume } 566190525Sume if (!afai) 567190525Sume continue; 568190525Sume 569190525Sume error = explore_copy(pai, afai, &cur->ai_next); 570190525Sume if (error != 0) 571190525Sume goto bad; 572190525Sume 57361877Sume while (cur && cur->ai_next) 57461877Sume cur = cur->ai_next; 57555163Sshin } 57655163Sshin 577121747Sume /* 578121747Sume * ensure we return either: 579121747Sume * - error == 0, non-NULL *res 580121747Sume * - error != 0, NULL *res 581121747Sume */ 58261877Sume if (error == 0) { 58361877Sume if (sentinel.ai_next) { 584121747Sume /* 585121747Sume * If the returned entry is for an active connection, 586121747Sume * and the given name is not numeric, reorder the 587121747Sume * list, so that the application would try the list 588172052Sjinmei * in the most efficient order. Since the head entry 589172052Sjinmei * of the original list may contain ai_canonname and 590172052Sjinmei * that entry may be moved elsewhere in the new list, 591172052Sjinmei * we keep the pointer and will restore it in the new 592172052Sjinmei * head entry. (Note that RFC3493 requires the head 593172052Sjinmei * entry store it when requested by the caller). 594121747Sume */ 595121747Sume if (hints == NULL || !(hints->ai_flags & AI_PASSIVE)) { 596172052Sjinmei if (!numeric) { 597172052Sjinmei char *canonname; 598172052Sjinmei 599172052Sjinmei canonname = 600172052Sjinmei sentinel.ai_next->ai_canonname; 601172052Sjinmei sentinel.ai_next->ai_canonname = NULL; 602121747Sume (void)reorder(&sentinel); 603172052Sjinmei if (sentinel.ai_next->ai_canonname == 604172052Sjinmei NULL) { 605172052Sjinmei sentinel.ai_next->ai_canonname 606172052Sjinmei = canonname; 607172052Sjinmei } else if (canonname != NULL) 608172052Sjinmei free(canonname); 609172052Sjinmei } 610121747Sume } 61161877Sume *res = sentinel.ai_next; 61261877Sume } else 61361877Sume error = EAI_FAIL; 61455163Sshin } 615190525Sume 616121747Sumebad: 617190525Sume if (afai_unspec) 618190525Sume freeaddrinfo(afai_unspec); 619190525Sume for (afd = afdl; afd->a_af; afd++) { 620190525Sume if (afailist[afd - afdl]) 621190525Sume freeaddrinfo(afailist[afd - afdl]); 622190525Sume } 623190525Sume if (!*res) 624190525Sume if (sentinel.ai_next) 625190525Sume freeaddrinfo(sentinel.ai_next); 626190525Sume 627190525Sume return (error); 62855163Sshin} 62955163Sshin 630121747Sumestatic int 631157119Sumereorder(struct addrinfo *sentinel) 632121747Sume{ 633121747Sume struct addrinfo *ai, **aip; 634121747Sume struct ai_order *aio; 635121747Sume int i, n; 636121747Sume struct policyhead policyhead; 637121747Sume 638121747Sume /* count the number of addrinfo elements for sorting. */ 639121747Sume for (n = 0, ai = sentinel->ai_next; ai != NULL; ai = ai->ai_next, n++) 640121747Sume ; 641121747Sume 642121747Sume /* 643121747Sume * If the number is small enough, we can skip the reordering process. 644121747Sume */ 645121747Sume if (n <= 1) 646121747Sume return(n); 647121747Sume 648121747Sume /* allocate a temporary array for sort and initialization of it. */ 649121747Sume if ((aio = malloc(sizeof(*aio) * n)) == NULL) 650121747Sume return(n); /* give up reordering */ 651121747Sume memset(aio, 0, sizeof(*aio) * n); 652121747Sume 653121747Sume /* retrieve address selection policy from the kernel */ 654121747Sume TAILQ_INIT(&policyhead); 655121747Sume if (!get_addrselectpolicy(&policyhead)) { 656121747Sume /* no policy is installed into kernel, we don't sort. */ 657121747Sume free(aio); 658121747Sume return (n); 659121747Sume } 660121747Sume 661121747Sume for (i = 0, ai = sentinel->ai_next; i < n; ai = ai->ai_next, i++) { 662121747Sume aio[i].aio_ai = ai; 663121747Sume aio[i].aio_dstscope = gai_addr2scopetype(ai->ai_addr); 664121747Sume aio[i].aio_dstpolicy = match_addrselectpolicy(ai->ai_addr, 665121747Sume &policyhead); 666129901Sume set_source(&aio[i], &policyhead); 667121747Sume } 668121747Sume 669121747Sume /* perform sorting. */ 670121747Sume qsort(aio, n, sizeof(*aio), comp_dst); 671121747Sume 672121747Sume /* reorder the addrinfo chain. */ 673121747Sume for (i = 0, aip = &sentinel->ai_next; i < n; i++) { 674121747Sume *aip = aio[i].aio_ai; 675121747Sume aip = &aio[i].aio_ai->ai_next; 676121747Sume } 677121747Sume *aip = NULL; 678121747Sume 679121747Sume /* cleanup and return */ 680121747Sume free(aio); 681121747Sume free_addrselectpolicy(&policyhead); 682121747Sume return(n); 683121747Sume} 684121747Sume 685121747Sumestatic int 686157119Sumeget_addrselectpolicy(struct policyhead *head) 687121747Sume{ 688121747Sume#ifdef INET6 689121747Sume int mib[] = { CTL_NET, PF_INET6, IPPROTO_IPV6, IPV6CTL_ADDRCTLPOLICY }; 690121747Sume size_t l; 691121747Sume char *buf; 692121747Sume struct in6_addrpolicy *pol, *ep; 693121747Sume 694121747Sume if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), NULL, &l, NULL, 0) < 0) 695121747Sume return (0); 696121747Sume if ((buf = malloc(l)) == NULL) 697121747Sume return (0); 698121747Sume if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), buf, &l, NULL, 0) < 0) { 699121747Sume free(buf); 700121747Sume return (0); 701121747Sume } 702121747Sume 703121747Sume ep = (struct in6_addrpolicy *)(buf + l); 704121747Sume for (pol = (struct in6_addrpolicy *)buf; pol + 1 <= ep; pol++) { 705121747Sume struct policyqueue *new; 706121747Sume 707121747Sume if ((new = malloc(sizeof(*new))) == NULL) { 708121747Sume free_addrselectpolicy(head); /* make the list empty */ 709121747Sume break; 710121747Sume } 711121747Sume new->pc_policy = *pol; 712121747Sume TAILQ_INSERT_TAIL(head, new, pc_entry); 713121747Sume } 714121747Sume 715121747Sume free(buf); 716121747Sume return (1); 717121747Sume#else 718121747Sume return (0); 719121747Sume#endif 720121747Sume} 721121747Sume 722121747Sumestatic void 723157119Sumefree_addrselectpolicy(struct policyhead *head) 724121747Sume{ 725121747Sume struct policyqueue *ent, *nent; 726121747Sume 727121747Sume for (ent = TAILQ_FIRST(head); ent; ent = nent) { 728121747Sume nent = TAILQ_NEXT(ent, pc_entry); 729121747Sume TAILQ_REMOVE(head, ent, pc_entry); 730121747Sume free(ent); 731121747Sume } 732121747Sume} 733121747Sume 734121747Sumestatic struct policyqueue * 735157119Sumematch_addrselectpolicy(struct sockaddr *addr, struct policyhead *head) 736121747Sume{ 737121747Sume#ifdef INET6 738121747Sume struct policyqueue *ent, *bestent = NULL; 739121747Sume struct in6_addrpolicy *pol; 740121747Sume int matchlen, bestmatchlen = -1; 741121747Sume u_char *mp, *ep, *k, *p, m; 742121747Sume struct sockaddr_in6 key; 743121747Sume 744121747Sume switch(addr->sa_family) { 745121747Sume case AF_INET6: 746121747Sume key = *(struct sockaddr_in6 *)addr; 747121747Sume break; 748121747Sume case AF_INET: 749121747Sume /* convert the address into IPv4-mapped IPv6 address. */ 750121747Sume memset(&key, 0, sizeof(key)); 751121747Sume key.sin6_family = AF_INET6; 752121747Sume key.sin6_len = sizeof(key); 753121747Sume key.sin6_addr.s6_addr[10] = 0xff; 754121747Sume key.sin6_addr.s6_addr[11] = 0xff; 755121747Sume memcpy(&key.sin6_addr.s6_addr[12], 756121747Sume &((struct sockaddr_in *)addr)->sin_addr, 4); 757121747Sume break; 758121747Sume default: 759121747Sume return(NULL); 760121747Sume } 761121747Sume 762121747Sume for (ent = TAILQ_FIRST(head); ent; ent = TAILQ_NEXT(ent, pc_entry)) { 763121747Sume pol = &ent->pc_policy; 764121747Sume matchlen = 0; 765121747Sume 766121747Sume mp = (u_char *)&pol->addrmask.sin6_addr; 767121747Sume ep = mp + 16; /* XXX: scope field? */ 768121747Sume k = (u_char *)&key.sin6_addr; 769121747Sume p = (u_char *)&pol->addr.sin6_addr; 770121747Sume for (; mp < ep && *mp; mp++, k++, p++) { 771121747Sume m = *mp; 772121747Sume if ((*k & m) != *p) 773121747Sume goto next; /* not match */ 774121747Sume if (m == 0xff) /* short cut for a typical case */ 775121747Sume matchlen += 8; 776121747Sume else { 777121747Sume while (m >= 0x80) { 778121747Sume matchlen++; 779121747Sume m <<= 1; 780121747Sume } 781121747Sume } 782121747Sume } 783121747Sume 784121747Sume /* matched. check if this is better than the current best. */ 785121747Sume if (matchlen > bestmatchlen) { 786121747Sume bestent = ent; 787121747Sume bestmatchlen = matchlen; 788121747Sume } 789121747Sume 790121747Sume next: 791121747Sume continue; 792121747Sume } 793121747Sume 794121747Sume return(bestent); 795121747Sume#else 796121747Sume return(NULL); 797121747Sume#endif 798121747Sume 799121747Sume} 800121747Sume 801129901Sumestatic void 802157119Sumeset_source(struct ai_order *aio, struct policyhead *ph) 803129901Sume{ 804129901Sume struct addrinfo ai = *aio->aio_ai; 805129901Sume struct sockaddr_storage ss; 806145786Sume socklen_t srclen; 807145786Sume int s; 808129901Sume 809129901Sume /* set unspec ("no source is available"), just in case */ 810129901Sume aio->aio_srcsa.sa_family = AF_UNSPEC; 811129901Sume aio->aio_srcscope = -1; 812129901Sume 813129901Sume switch(ai.ai_family) { 814129901Sume case AF_INET: 815129901Sume#ifdef INET6 816129901Sume case AF_INET6: 817129901Sume#endif 818129901Sume break; 819129901Sume default: /* ignore unsupported AFs explicitly */ 820129901Sume return; 821129901Sume } 822129901Sume 823129901Sume /* XXX: make a dummy addrinfo to call connect() */ 824129901Sume ai.ai_socktype = SOCK_DGRAM; 825129901Sume ai.ai_protocol = IPPROTO_UDP; /* is UDP too specific? */ 826129901Sume ai.ai_next = NULL; 827129901Sume memset(&ss, 0, sizeof(ss)); 828129901Sume memcpy(&ss, ai.ai_addr, ai.ai_addrlen); 829129901Sume ai.ai_addr = (struct sockaddr *)&ss; 830129901Sume get_port(&ai, "1", 0); 831129901Sume 832129901Sume /* open a socket to get the source address for the given dst */ 833129901Sume if ((s = _socket(ai.ai_family, ai.ai_socktype, ai.ai_protocol)) < 0) 834129901Sume return; /* give up */ 835129901Sume if (_connect(s, ai.ai_addr, ai.ai_addrlen) < 0) 836129901Sume goto cleanup; 837129901Sume srclen = ai.ai_addrlen; 838129901Sume if (_getsockname(s, &aio->aio_srcsa, &srclen) < 0) { 839129901Sume aio->aio_srcsa.sa_family = AF_UNSPEC; 840129901Sume goto cleanup; 841129901Sume } 842129901Sume aio->aio_srcscope = gai_addr2scopetype(&aio->aio_srcsa); 843129901Sume aio->aio_srcpolicy = match_addrselectpolicy(&aio->aio_srcsa, ph); 844129901Sume aio->aio_matchlen = matchlen(&aio->aio_srcsa, aio->aio_ai->ai_addr); 845129901Sume#ifdef INET6 846129901Sume if (ai.ai_family == AF_INET6) { 847129901Sume struct in6_ifreq ifr6; 848129901Sume u_int32_t flags6; 849129901Sume 850129901Sume /* XXX: interface name should not be hardcoded */ 851129901Sume strncpy(ifr6.ifr_name, "lo0", sizeof(ifr6.ifr_name)); 852129901Sume memset(&ifr6, 0, sizeof(ifr6)); 853129901Sume memcpy(&ifr6.ifr_addr, ai.ai_addr, ai.ai_addrlen); 854129901Sume if (_ioctl(s, SIOCGIFAFLAG_IN6, &ifr6) == 0) { 855129901Sume flags6 = ifr6.ifr_ifru.ifru_flags6; 856129901Sume if ((flags6 & IN6_IFF_DEPRECATED)) 857129901Sume aio->aio_srcflag |= AIO_SRCFLAG_DEPRECATED; 858129901Sume } 859129901Sume } 860129901Sume#endif 861129901Sume 862129901Sume cleanup: 863129901Sume _close(s); 864129901Sume return; 865129901Sume} 866129901Sume 867121747Sumestatic int 868157119Sumematchlen(struct sockaddr *src, struct sockaddr *dst) 869129901Sume{ 870129901Sume int match = 0; 871129901Sume u_char *s, *d; 872129901Sume u_char *lim, r; 873129901Sume int addrlen; 874129901Sume 875129901Sume switch (src->sa_family) { 876129901Sume#ifdef INET6 877129901Sume case AF_INET6: 878129901Sume s = (u_char *)&((struct sockaddr_in6 *)src)->sin6_addr; 879129901Sume d = (u_char *)&((struct sockaddr_in6 *)dst)->sin6_addr; 880129901Sume addrlen = sizeof(struct in6_addr); 881129901Sume lim = s + addrlen; 882129901Sume break; 883129901Sume#endif 884129901Sume case AF_INET: 885146222Sgnn s = (u_char *)&((struct sockaddr_in *)src)->sin_addr; 886146222Sgnn d = (u_char *)&((struct sockaddr_in *)dst)->sin_addr; 887129901Sume addrlen = sizeof(struct in_addr); 888129901Sume lim = s + addrlen; 889129901Sume break; 890129901Sume default: 891129901Sume return(0); 892129901Sume } 893129901Sume 894129901Sume while (s < lim) 895129901Sume if ((r = (*d++ ^ *s++)) != 0) { 896129901Sume while (r < addrlen * 8) { 897129901Sume match++; 898129901Sume r <<= 1; 899129901Sume } 900129901Sume break; 901129901Sume } else 902129901Sume match += 8; 903129901Sume return(match); 904129901Sume} 905129901Sume 906129901Sumestatic int 907157119Sumecomp_dst(const void *arg1, const void *arg2) 908121747Sume{ 909121747Sume const struct ai_order *dst1 = arg1, *dst2 = arg2; 910121747Sume 911121747Sume /* 912121747Sume * Rule 1: Avoid unusable destinations. 913121747Sume * XXX: we currently do not consider if an appropriate route exists. 914121747Sume */ 915121747Sume if (dst1->aio_srcsa.sa_family != AF_UNSPEC && 916121747Sume dst2->aio_srcsa.sa_family == AF_UNSPEC) { 917121747Sume return(-1); 918121747Sume } 919121747Sume if (dst1->aio_srcsa.sa_family == AF_UNSPEC && 920121747Sume dst2->aio_srcsa.sa_family != AF_UNSPEC) { 921121747Sume return(1); 922121747Sume } 923121747Sume 924121747Sume /* Rule 2: Prefer matching scope. */ 925121747Sume if (dst1->aio_dstscope == dst1->aio_srcscope && 926121747Sume dst2->aio_dstscope != dst2->aio_srcscope) { 927121747Sume return(-1); 928121747Sume } 929121747Sume if (dst1->aio_dstscope != dst1->aio_srcscope && 930121747Sume dst2->aio_dstscope == dst2->aio_srcscope) { 931121747Sume return(1); 932121747Sume } 933121747Sume 934121747Sume /* Rule 3: Avoid deprecated addresses. */ 935121747Sume if (dst1->aio_srcsa.sa_family != AF_UNSPEC && 936121747Sume dst2->aio_srcsa.sa_family != AF_UNSPEC) { 937121747Sume if (!(dst1->aio_srcflag & AIO_SRCFLAG_DEPRECATED) && 938121747Sume (dst2->aio_srcflag & AIO_SRCFLAG_DEPRECATED)) { 939121747Sume return(-1); 940121747Sume } 941121747Sume if ((dst1->aio_srcflag & AIO_SRCFLAG_DEPRECATED) && 942121747Sume !(dst2->aio_srcflag & AIO_SRCFLAG_DEPRECATED)) { 943121747Sume return(1); 944121747Sume } 945121747Sume } 946121747Sume 947121747Sume /* Rule 4: Prefer home addresses. */ 948121747Sume /* XXX: not implemented yet */ 949121747Sume 950121747Sume /* Rule 5: Prefer matching label. */ 951121747Sume#ifdef INET6 952121747Sume if (dst1->aio_srcpolicy && dst1->aio_dstpolicy && 953121747Sume dst1->aio_srcpolicy->pc_policy.label == 954121747Sume dst1->aio_dstpolicy->pc_policy.label && 955121747Sume (dst2->aio_srcpolicy == NULL || dst2->aio_dstpolicy == NULL || 956121747Sume dst2->aio_srcpolicy->pc_policy.label != 957121747Sume dst2->aio_dstpolicy->pc_policy.label)) { 958121747Sume return(-1); 959121747Sume } 960121747Sume if (dst2->aio_srcpolicy && dst2->aio_dstpolicy && 961121747Sume dst2->aio_srcpolicy->pc_policy.label == 962121747Sume dst2->aio_dstpolicy->pc_policy.label && 963121747Sume (dst1->aio_srcpolicy == NULL || dst1->aio_dstpolicy == NULL || 964121747Sume dst1->aio_srcpolicy->pc_policy.label != 965121747Sume dst1->aio_dstpolicy->pc_policy.label)) { 966121747Sume return(1); 967121747Sume } 968121747Sume#endif 969121747Sume 970121747Sume /* Rule 6: Prefer higher precedence. */ 971121747Sume#ifdef INET6 972121747Sume if (dst1->aio_dstpolicy && 973121747Sume (dst2->aio_dstpolicy == NULL || 974121747Sume dst1->aio_dstpolicy->pc_policy.preced > 975121747Sume dst2->aio_dstpolicy->pc_policy.preced)) { 976121747Sume return(-1); 977121747Sume } 978121747Sume if (dst2->aio_dstpolicy && 979121747Sume (dst1->aio_dstpolicy == NULL || 980121747Sume dst2->aio_dstpolicy->pc_policy.preced > 981121747Sume dst1->aio_dstpolicy->pc_policy.preced)) { 982121747Sume return(1); 983121747Sume } 984121747Sume#endif 985121747Sume 986121747Sume /* Rule 7: Prefer native transport. */ 987121747Sume /* XXX: not implemented yet */ 988121747Sume 989121747Sume /* Rule 8: Prefer smaller scope. */ 990121747Sume if (dst1->aio_dstscope >= 0 && 991121747Sume dst1->aio_dstscope < dst2->aio_dstscope) { 992121747Sume return(-1); 993121747Sume } 994121747Sume if (dst2->aio_dstscope >= 0 && 995121747Sume dst2->aio_dstscope < dst1->aio_dstscope) { 996121747Sume return(1); 997121747Sume } 998121747Sume 999121747Sume /* 1000121747Sume * Rule 9: Use longest matching prefix. 1001121747Sume * We compare the match length in a same AF only. 1002121747Sume */ 1003121747Sume if (dst1->aio_ai->ai_addr->sa_family == 1004121747Sume dst2->aio_ai->ai_addr->sa_family) { 1005121747Sume if (dst1->aio_matchlen > dst2->aio_matchlen) { 1006121747Sume return(-1); 1007121747Sume } 1008121747Sume if (dst1->aio_matchlen < dst2->aio_matchlen) { 1009121747Sume return(1); 1010121747Sume } 1011121747Sume } 1012121747Sume 1013121747Sume /* Rule 10: Otherwise, leave the order unchanged. */ 1014121747Sume return(-1); 1015121747Sume} 1016121747Sume 101755163Sshin/* 1018121747Sume * Copy from scope.c. 1019121747Sume * XXX: we should standardize the functions and link them as standard 1020121747Sume * library. 1021121747Sume */ 1022121747Sumestatic int 1023157119Sumegai_addr2scopetype(struct sockaddr *sa) 1024121747Sume{ 1025121747Sume#ifdef INET6 1026121747Sume struct sockaddr_in6 *sa6; 1027121747Sume#endif 1028121747Sume struct sockaddr_in *sa4; 1029121747Sume 1030121747Sume switch(sa->sa_family) { 1031121747Sume#ifdef INET6 1032121747Sume case AF_INET6: 1033121747Sume sa6 = (struct sockaddr_in6 *)sa; 1034121747Sume if (IN6_IS_ADDR_MULTICAST(&sa6->sin6_addr)) { 1035121747Sume /* just use the scope field of the multicast address */ 1036121747Sume return(sa6->sin6_addr.s6_addr[2] & 0x0f); 1037121747Sume } 1038121747Sume /* 1039121747Sume * Unicast addresses: map scope type to corresponding scope 1040121747Sume * value defined for multcast addresses. 1041121747Sume * XXX: hardcoded scope type values are bad... 1042121747Sume */ 1043121747Sume if (IN6_IS_ADDR_LOOPBACK(&sa6->sin6_addr)) 1044121747Sume return(1); /* node local scope */ 1045121747Sume if (IN6_IS_ADDR_LINKLOCAL(&sa6->sin6_addr)) 1046121747Sume return(2); /* link-local scope */ 1047121747Sume if (IN6_IS_ADDR_SITELOCAL(&sa6->sin6_addr)) 1048121747Sume return(5); /* site-local scope */ 1049121747Sume return(14); /* global scope */ 1050121747Sume break; 1051121747Sume#endif 1052121747Sume case AF_INET: 1053121747Sume /* 1054121747Sume * IPv4 pseudo scoping according to RFC 3484. 1055121747Sume */ 1056121747Sume sa4 = (struct sockaddr_in *)sa; 1057121747Sume /* IPv4 autoconfiguration addresses have link-local scope. */ 1058121747Sume if (((u_char *)&sa4->sin_addr)[0] == 169 && 1059121747Sume ((u_char *)&sa4->sin_addr)[1] == 254) 1060121747Sume return(2); 1061121747Sume /* Private addresses have site-local scope. */ 1062121747Sume if (((u_char *)&sa4->sin_addr)[0] == 10 || 1063121747Sume (((u_char *)&sa4->sin_addr)[0] == 172 && 1064121747Sume (((u_char *)&sa4->sin_addr)[1] & 0xf0) == 16) || 1065121747Sume (((u_char *)&sa4->sin_addr)[0] == 192 && 1066121747Sume ((u_char *)&sa4->sin_addr)[1] == 168)) 1067129905Sume return(14); /* XXX: It should be 5 unless NAT */ 1068121747Sume /* Loopback addresses have link-local scope. */ 1069121747Sume if (((u_char *)&sa4->sin_addr)[0] == 127) 1070121747Sume return(2); 1071121747Sume return(14); 1072121747Sume break; 1073121747Sume default: 1074121747Sume errno = EAFNOSUPPORT; /* is this a good error? */ 1075121747Sume return(-1); 1076121747Sume } 1077121747Sume} 1078121747Sume 1079190525Sumestatic int 1080190525Sumeexplore_copy(const struct addrinfo *pai, const struct addrinfo *src0, 1081190525Sume struct addrinfo **res) 1082190525Sume{ 1083190525Sume int error; 1084190525Sume struct addrinfo sentinel, *cur; 1085190525Sume const struct addrinfo *src; 1086190525Sume 1087190525Sume error = 0; 1088190525Sume sentinel.ai_next = NULL; 1089190525Sume cur = &sentinel; 1090190525Sume 1091190525Sume for (src = src0; src != NULL; src = src->ai_next) { 1092190525Sume if (src->ai_family != pai->ai_family) 1093190525Sume continue; 1094190525Sume 1095190525Sume cur->ai_next = copy_ai(src); 1096190525Sume if (!cur->ai_next) { 1097190525Sume error = EAI_MEMORY; 1098190525Sume goto fail; 1099190525Sume } 1100190525Sume 1101190525Sume cur->ai_next->ai_socktype = pai->ai_socktype; 1102190525Sume cur->ai_next->ai_protocol = pai->ai_protocol; 1103190525Sume cur = cur->ai_next; 1104190525Sume } 1105190525Sume 1106190525Sume *res = sentinel.ai_next; 1107190525Sume return 0; 1108190525Sume 1109190525Sumefail: 1110190525Sume freeaddrinfo(sentinel.ai_next); 1111190525Sume return error; 1112190525Sume} 1113190525Sume 1114121747Sume/* 111555163Sshin * hostname == NULL. 111655163Sshin * passive socket -> anyaddr (0.0.0.0 or ::) 111755163Sshin * non-passive socket -> localhost (127.0.0.1 or ::1) 111855163Sshin */ 111955163Sshinstatic int 1120157119Sumeexplore_null(const struct addrinfo *pai, const char *servname, 1121157119Sume struct addrinfo **res) 112255163Sshin{ 1123121474Sume int s; 112455163Sshin const struct afd *afd; 1125160551Sume struct addrinfo *ai; 112655163Sshin int error; 112755163Sshin 112855163Sshin *res = NULL; 1129160551Sume ai = NULL; 113055163Sshin 113155163Sshin /* 1132121474Sume * filter out AFs that are not supported by the kernel 1133121474Sume * XXX errno? 1134121474Sume */ 1135121474Sume s = _socket(pai->ai_family, SOCK_DGRAM, 0); 1136121474Sume if (s < 0) { 1137121474Sume if (errno != EMFILE) 1138121474Sume return 0; 1139121474Sume } else 1140121474Sume _close(s); 1141121474Sume 114255163Sshin afd = find_afd(pai->ai_family); 114355163Sshin if (afd == NULL) 114455163Sshin return 0; 114555163Sshin 114661877Sume if (pai->ai_flags & AI_PASSIVE) { 1147160551Sume GET_AI(ai, afd, afd->a_addrany); 1148160551Sume GET_PORT(ai, servname); 114961877Sume } else { 1150160551Sume GET_AI(ai, afd, afd->a_loopback); 1151160551Sume GET_PORT(ai, servname); 115261877Sume } 115355163Sshin 1154160551Sume *res = ai; 115555163Sshin return 0; 115655163Sshin 115755163Sshinfree: 1158160551Sume if (ai != NULL) 1159160551Sume freeaddrinfo(ai); 116055163Sshin return error; 116155163Sshin} 116255163Sshin 116355163Sshin/* 116455163Sshin * numeric hostname 116555163Sshin */ 116655163Sshinstatic int 1167157119Sumeexplore_numeric(const struct addrinfo *pai, const char *hostname, 1168157119Sume const char *servname, struct addrinfo **res, const char *canonname) 116955163Sshin{ 117055163Sshin const struct afd *afd; 1171160551Sume struct addrinfo *ai; 117255163Sshin int error; 117355163Sshin char pton[PTON_MAX]; 117455163Sshin 117555163Sshin *res = NULL; 1176160551Sume ai = NULL; 117755163Sshin 117855163Sshin afd = find_afd(pai->ai_family); 117955163Sshin if (afd == NULL) 118055163Sshin return 0; 118155163Sshin 118262614Sitojun switch (afd->a_af) { 118362614Sitojun case AF_INET: 1184160552Sume /* 1185160552Sume * RFC3493 requires getaddrinfo() to accept AF_INET formats 1186160552Sume * that are accepted by inet_addr() and its family. The 1187160552Sume * accepted forms includes the "classful" one, which inet_pton 1188160552Sume * does not accept. So we need to separate the case for 1189160552Sume * AF_INET. 1190160552Sume */ 1191160553Sume if (inet_aton(hostname, (struct in_addr *)pton) != 1) 1192160553Sume return 0; 119362614Sitojun break; 119462614Sitojun default: 1195160553Sume if (inet_pton(afd->a_af, hostname, pton) != 1) 1196160553Sume return 0; 119762614Sitojun break; 119855163Sshin } 119955163Sshin 1200160553Sume if (pai->ai_family == afd->a_af) { 1201160553Sume GET_AI(ai, afd, pton); 1202160553Sume GET_PORT(ai, servname); 1203160553Sume if ((pai->ai_flags & AI_CANONNAME)) { 1204160553Sume /* 1205160553Sume * Set the numeric address itself as the canonical 1206160553Sume * name, based on a clarification in RFC3493. 1207160553Sume */ 1208160553Sume GET_CANONNAME(ai, canonname); 1209160553Sume } 1210160553Sume } else { 1211160553Sume /* 1212160553Sume * XXX: This should not happen since we already matched the AF 1213160553Sume * by find_afd. 1214160553Sume */ 1215160553Sume ERR(EAI_FAMILY); 1216160553Sume } 1217160553Sume 1218160551Sume *res = ai; 121955163Sshin return 0; 122055163Sshin 122155163Sshinfree: 122255163Sshinbad: 1223160551Sume if (ai != NULL) 1224160551Sume freeaddrinfo(ai); 122555163Sshin return error; 122655163Sshin} 122755163Sshin 122855163Sshin/* 122955163Sshin * numeric hostname with scope 123055163Sshin */ 123155163Sshinstatic int 1232157119Sumeexplore_numeric_scope(const struct addrinfo *pai, const char *hostname, 1233157119Sume const char *servname, struct addrinfo **res) 123455163Sshin{ 123561877Sume#if !defined(SCOPE_DELIMITER) || !defined(INET6) 1236140906Sume return explore_numeric(pai, hostname, servname, res, hostname); 123755163Sshin#else 123855163Sshin const struct afd *afd; 123955163Sshin struct addrinfo *cur; 124055163Sshin int error; 124161877Sume char *cp, *hostname2 = NULL, *scope, *addr; 124255163Sshin struct sockaddr_in6 *sin6; 124355163Sshin 124455163Sshin afd = find_afd(pai->ai_family); 124555163Sshin if (afd == NULL) 124655163Sshin return 0; 124765532Snectar 124855163Sshin if (!afd->a_scoped) 1249140906Sume return explore_numeric(pai, hostname, servname, res, hostname); 125055163Sshin 125155163Sshin cp = strchr(hostname, SCOPE_DELIMITER); 125255163Sshin if (cp == NULL) 1253140906Sume return explore_numeric(pai, hostname, servname, res, hostname); 125455163Sshin 125555163Sshin /* 125655163Sshin * Handle special case of <scoped_address><delimiter><scope id> 125755163Sshin */ 125855163Sshin hostname2 = strdup(hostname); 125955163Sshin if (hostname2 == NULL) 126055163Sshin return EAI_MEMORY; 126155163Sshin /* terminate at the delimiter */ 126255163Sshin hostname2[cp - hostname] = '\0'; 126361877Sume addr = hostname2; 126461877Sume scope = cp + 1; 126555163Sshin 1266140906Sume error = explore_numeric(pai, addr, servname, res, hostname); 126761877Sume if (error == 0) { 1268105943Sume u_int32_t scopeid; 126955163Sshin 127055163Sshin for (cur = *res; cur; cur = cur->ai_next) { 127155163Sshin if (cur->ai_family != AF_INET6) 127255163Sshin continue; 127361877Sume sin6 = (struct sockaddr_in6 *)(void *)cur->ai_addr; 1274105943Sume if (ip6_str2scopeid(scope, sin6, &scopeid) == -1) { 127561877Sume free(hostname2); 1276190525Sume freeaddrinfo(*res); 1277190525Sume *res = NULL; 1278121425Sume return(EAI_NONAME); /* XXX: is return OK? */ 127961877Sume } 128061877Sume sin6->sin6_scope_id = scopeid; 128155163Sshin } 128255163Sshin } 128355163Sshin 128455163Sshin free(hostname2); 128555163Sshin 1286190525Sume if (error && *res) { 1287190525Sume freeaddrinfo(*res); 1288190525Sume *res = NULL; 1289190525Sume } 129055163Sshin return error; 129155163Sshin#endif 129255163Sshin} 129355163Sshin 129455163Sshinstatic int 1295157119Sumeget_canonname(const struct addrinfo *pai, struct addrinfo *ai, const char *str) 129655163Sshin{ 129755163Sshin if ((pai->ai_flags & AI_CANONNAME) != 0) { 1298140947Sume ai->ai_canonname = strdup(str); 129955163Sshin if (ai->ai_canonname == NULL) 130055163Sshin return EAI_MEMORY; 130155163Sshin } 130255163Sshin return 0; 130355163Sshin} 130455163Sshin 130555163Sshinstatic struct addrinfo * 1306157119Sumeget_ai(const struct addrinfo *pai, const struct afd *afd, const char *addr) 130755163Sshin{ 130855163Sshin char *p; 130955163Sshin struct addrinfo *ai; 131055163Sshin#ifdef FAITH 131155163Sshin struct in6_addr faith_prefix; 131255163Sshin char *fp_str; 131355163Sshin int translate = 0; 131455163Sshin#endif 131555163Sshin 131655163Sshin#ifdef FAITH 131755163Sshin /* 131855163Sshin * Transfrom an IPv4 addr into a special IPv6 addr format for 131955163Sshin * IPv6->IPv4 translation gateway. (only TCP is supported now) 132055163Sshin * 132155163Sshin * +-----------------------------------+------------+ 132255163Sshin * | faith prefix part (12 bytes) | embedded | 132355163Sshin * | | IPv4 addr part (4 bytes) 132455163Sshin * +-----------------------------------+------------+ 132555163Sshin * 132655163Sshin * faith prefix part is specified as ascii IPv6 addr format 132755163Sshin * in environmental variable GAI. 132855163Sshin * For FAITH to work correctly, routing to faith prefix must be 132955163Sshin * setup toward a machine where a FAITH daemon operates. 133055163Sshin * Also, the machine must enable some mechanizm 133155163Sshin * (e.g. faith interface hack) to divert those packet with 133255163Sshin * faith prefixed destination addr to user-land FAITH daemon. 133355163Sshin */ 133455163Sshin fp_str = getenv("GAI"); 133555163Sshin if (fp_str && inet_pton(AF_INET6, fp_str, &faith_prefix) == 1 && 133655163Sshin afd->a_af == AF_INET && pai->ai_socktype == SOCK_STREAM) { 133755163Sshin u_int32_t v4a; 133855163Sshin u_int8_t v4a_top; 133955163Sshin 134055163Sshin memcpy(&v4a, addr, sizeof v4a); 134155163Sshin v4a_top = v4a >> IN_CLASSA_NSHIFT; 134255163Sshin if (!IN_MULTICAST(v4a) && !IN_EXPERIMENTAL(v4a) && 134355163Sshin v4a_top != 0 && v4a != IN_LOOPBACKNET) { 134455163Sshin afd = &afdl[N_INET6]; 134555163Sshin memcpy(&faith_prefix.s6_addr[12], addr, 134655163Sshin sizeof(struct in_addr)); 134755163Sshin translate = 1; 134855163Sshin } 134955163Sshin } 135055163Sshin#endif 135155163Sshin 135255163Sshin ai = (struct addrinfo *)malloc(sizeof(struct addrinfo) 135355163Sshin + (afd->a_socklen)); 135455163Sshin if (ai == NULL) 135555163Sshin return NULL; 135655163Sshin 135755163Sshin memcpy(ai, pai, sizeof(struct addrinfo)); 135861877Sume ai->ai_addr = (struct sockaddr *)(void *)(ai + 1); 135961877Sume memset(ai->ai_addr, 0, (size_t)afd->a_socklen); 136055163Sshin ai->ai_addr->sa_len = afd->a_socklen; 136155163Sshin ai->ai_addrlen = afd->a_socklen; 136255163Sshin ai->ai_addr->sa_family = ai->ai_family = afd->a_af; 136365532Snectar p = (char *)(void *)(ai->ai_addr); 136455163Sshin#ifdef FAITH 136555163Sshin if (translate == 1) 136665532Snectar memcpy(p + afd->a_off, &faith_prefix, (size_t)afd->a_addrlen); 136755163Sshin else 136855163Sshin#endif 136965532Snectar memcpy(p + afd->a_off, addr, (size_t)afd->a_addrlen); 137055163Sshin return ai; 137155163Sshin} 137255163Sshin 1373190525Sume/* XXX need to malloc() the same way we do from other functions! */ 1374190525Sumestatic struct addrinfo * 1375190525Sumecopy_ai(const struct addrinfo *pai) 1376190525Sume{ 1377190525Sume struct addrinfo *ai; 1378190525Sume size_t l; 1379190525Sume 1380190525Sume l = sizeof(*ai) + pai->ai_addrlen; 1381190525Sume if ((ai = (struct addrinfo *)malloc(l)) == NULL) 1382190525Sume return NULL; 1383190525Sume memset(ai, 0, l); 1384190525Sume memcpy(ai, pai, sizeof(*ai)); 1385190525Sume ai->ai_addr = (struct sockaddr *)(void *)(ai + 1); 1386190525Sume memcpy(ai->ai_addr, pai->ai_addr, pai->ai_addrlen); 1387190525Sume 1388190525Sume if (pai->ai_canonname) { 1389190525Sume l = strlen(pai->ai_canonname) + 1; 1390190525Sume if ((ai->ai_canonname = malloc(l)) == NULL) { 1391190525Sume free(ai); 1392190525Sume return NULL; 1393190525Sume } 1394190525Sume strlcpy(ai->ai_canonname, pai->ai_canonname, l); 1395190525Sume } else { 1396190525Sume /* just to make sure */ 1397190525Sume ai->ai_canonname = NULL; 1398190525Sume } 1399190525Sume 1400190525Sume ai->ai_next = NULL; 1401190525Sume 1402190525Sume return ai; 1403190525Sume} 1404190525Sume 140555163Sshinstatic int 1406157119Sumeget_portmatch(const struct addrinfo *ai, const char *servname) 140755163Sshin{ 140861877Sume 1409140908Sume /* get_port does not touch first argument when matchonly == 1. */ 141061877Sume /* LINTED const cast */ 141155163Sshin return get_port((struct addrinfo *)ai, servname, 1); 141255163Sshin} 141355163Sshin 141455163Sshinstatic int 1415157119Sumeget_port(struct addrinfo *ai, const char *servname, int matchonly) 141655163Sshin{ 141755163Sshin const char *proto; 141855163Sshin struct servent *sp; 1419160593Sume int port, error; 142055163Sshin int allownumeric; 142155163Sshin 142255163Sshin if (servname == NULL) 142355163Sshin return 0; 142461877Sume switch (ai->ai_family) { 142561877Sume case AF_INET: 142661877Sume#ifdef AF_INET6 142761877Sume case AF_INET6: 142855163Sshin#endif 142961877Sume break; 143061877Sume default: 143155163Sshin return 0; 143261877Sume } 143355163Sshin 143455163Sshin switch (ai->ai_socktype) { 143555163Sshin case SOCK_RAW: 143655163Sshin return EAI_SERVICE; 143755163Sshin case SOCK_DGRAM: 143855163Sshin case SOCK_STREAM: 1439190416Sume case SOCK_SEQPACKET: 144055163Sshin allownumeric = 1; 144155163Sshin break; 144255163Sshin case ANY: 1443190382Sume switch (ai->ai_family) { 1444190382Sume case AF_INET: 1445190382Sume#ifdef AF_INET6 1446190382Sume case AF_INET6: 1447190382Sume#endif 1448190382Sume allownumeric = 1; 1449190382Sume break; 1450190382Sume default: 1451190382Sume allownumeric = 0; 1452190382Sume break; 1453190382Sume } 145455163Sshin break; 145555163Sshin default: 145655163Sshin return EAI_SOCKTYPE; 145755163Sshin } 145855163Sshin 1459160593Sume error = str2number(servname, &port); 1460160593Sume if (error == 0) { 146155163Sshin if (!allownumeric) 146255163Sshin return EAI_SERVICE; 146355163Sshin if (port < 0 || port > 65535) 146455163Sshin return EAI_SERVICE; 1465105940Sume port = htons(port); 146655163Sshin } else { 1467140908Sume if (ai->ai_flags & AI_NUMERICSERV) 1468140908Sume return EAI_NONAME; 1469190416Sume 1470190416Sume switch (ai->ai_protocol) { 1471190416Sume case IPPROTO_UDP: 147255163Sshin proto = "udp"; 147355163Sshin break; 1474190416Sume case IPPROTO_TCP: 147555163Sshin proto = "tcp"; 147655163Sshin break; 1477190416Sume case IPPROTO_SCTP: 1478190416Sume proto = "sctp"; 1479190416Sume break; 148055163Sshin default: 148155163Sshin proto = NULL; 148255163Sshin break; 148355163Sshin } 148455163Sshin 1485145118Sume if ((sp = getservbyname(servname, proto)) == NULL) 148655163Sshin return EAI_SERVICE; 148755163Sshin port = sp->s_port; 148855163Sshin } 148955163Sshin 149055163Sshin if (!matchonly) { 149155163Sshin switch (ai->ai_family) { 149255163Sshin case AF_INET: 149361877Sume ((struct sockaddr_in *)(void *) 149461877Sume ai->ai_addr)->sin_port = port; 149555163Sshin break; 149655163Sshin#ifdef INET6 149755163Sshin case AF_INET6: 149861877Sume ((struct sockaddr_in6 *)(void *) 149961877Sume ai->ai_addr)->sin6_port = port; 150055163Sshin break; 150155163Sshin#endif 150255163Sshin } 150355163Sshin } 150455163Sshin 150555163Sshin return 0; 150655163Sshin} 150755163Sshin 150855163Sshinstatic const struct afd * 1509157119Sumefind_afd(int af) 151055163Sshin{ 151155163Sshin const struct afd *afd; 151255163Sshin 151355163Sshin if (af == PF_UNSPEC) 151455163Sshin return NULL; 151555163Sshin for (afd = afdl; afd->a_af; afd++) { 151655163Sshin if (afd->a_af == af) 151755163Sshin return afd; 151855163Sshin } 151955163Sshin return NULL; 152055163Sshin} 152161877Sume 152261877Sume/* 152361877Sume * post-2553: AI_ADDRCONFIG check. if we use getipnodeby* as backend, backend 152461877Sume * will take care of it. 152561877Sume * the semantics of AI_ADDRCONFIG is not defined well. we are not sure 152661877Sume * if the code is right or not. 1527121474Sume * 1528121474Sume * XXX PF_UNSPEC -> PF_INET6 + PF_INET mapping needs to be in sync with 1529121474Sume * _dns_getaddrinfo. 153061877Sume */ 153161877Sumestatic int 1532157119Sumeaddrconfig(struct addrinfo *pai) 153361877Sume{ 1534121474Sume int s, af; 153561877Sume 1536121474Sume /* 1537121474Sume * TODO: 1538121474Sume * Note that implementation dependent test for address 1539121474Sume * configuration should be done everytime called 1540121474Sume * (or apropriate interval), 1541121474Sume * because addresses will be dynamically assigned or deleted. 1542121474Sume */ 1543121474Sume af = pai->ai_family; 1544121474Sume if (af == AF_UNSPEC) { 1545121474Sume if ((s = _socket(AF_INET6, SOCK_DGRAM, 0)) < 0) 1546121474Sume af = AF_INET; 1547121474Sume else { 1548121474Sume _close(s); 1549121474Sume if ((s = _socket(AF_INET, SOCK_DGRAM, 0)) < 0) 1550121474Sume af = AF_INET6; 1551121474Sume else 1552121474Sume _close(s); 1553121474Sume } 1554121474Sume } 1555121474Sume if (af != AF_UNSPEC) { 1556121474Sume if ((s = _socket(af, SOCK_DGRAM, 0)) < 0) 155761877Sume return 0; 1558121474Sume _close(s); 1559121474Sume } 1560121474Sume pai->ai_family = af; 156161877Sume return 1; 156261877Sume} 156361877Sume 156461877Sume#ifdef INET6 156561877Sume/* convert a string to a scope identifier. XXX: IPv6 specific */ 156661877Sumestatic int 1567157119Sumeip6_str2scopeid(char *scope, struct sockaddr_in6 *sin6, u_int32_t *scopeid) 156861877Sume{ 1569105943Sume u_long lscopeid; 1570121474Sume struct in6_addr *a6; 157161877Sume char *ep; 157261877Sume 1573121474Sume a6 = &sin6->sin6_addr; 1574121474Sume 157562836Sitojun /* empty scopeid portion is invalid */ 157662836Sitojun if (*scope == '\0') 157762836Sitojun return -1; 157862836Sitojun 1579229766Sume if (IN6_IS_ADDR_LINKLOCAL(a6) || IN6_IS_ADDR_MC_LINKLOCAL(a6) || 1580229766Sume IN6_IS_ADDR_MC_NODELOCAL(a6)) { 158161877Sume /* 158261877Sume * We currently assume a one-to-one mapping between links 158361877Sume * and interfaces, so we simply use interface indices for 158461877Sume * like-local scopes. 158561877Sume */ 1586105943Sume *scopeid = if_nametoindex(scope); 1587105943Sume if (*scopeid == 0) 158861877Sume goto trynumeric; 1589105943Sume return 0; 159061877Sume } 159161877Sume 159261877Sume /* still unclear about literal, allow numeric only - placeholder */ 159361877Sume if (IN6_IS_ADDR_SITELOCAL(a6) || IN6_IS_ADDR_MC_SITELOCAL(a6)) 159461877Sume goto trynumeric; 159561877Sume if (IN6_IS_ADDR_MC_ORGLOCAL(a6)) 159661877Sume goto trynumeric; 159761877Sume else 159861877Sume goto trynumeric; /* global */ 159961877Sume 160061877Sume /* try to convert to a numeric id as a last resort */ 1601121474Sume trynumeric: 1602105943Sume errno = 0; 1603105943Sume lscopeid = strtoul(scope, &ep, 10); 1604105943Sume *scopeid = (u_int32_t)(lscopeid & 0xffffffffUL); 1605105943Sume if (errno == 0 && ep && *ep == '\0' && *scopeid == lscopeid) 1606105943Sume return 0; 160761877Sume else 160861877Sume return -1; 160961877Sume} 161061877Sume#endif 161161877Sume 1612158115Sume 1613158115Sume#ifdef NS_CACHING 1614158115Sumestatic int 1615158115Sumeaddrinfo_id_func(char *buffer, size_t *buffer_size, va_list ap, 1616158115Sume void *cache_mdata) 1617158115Sume{ 1618158115Sume res_state statp; 1619158115Sume u_long res_options; 1620158115Sume 1621158115Sume const int op_id = 0; /* identifies the getaddrinfo for the cache */ 1622158115Sume char *hostname; 1623158115Sume struct addrinfo *hints; 1624158115Sume 1625158115Sume char *p; 1626158115Sume int ai_flags, ai_family, ai_socktype, ai_protocol; 1627158115Sume size_t desired_size, size; 1628158115Sume 1629158115Sume statp = __res_state(); 1630158115Sume res_options = statp->options & (RES_RECURSE | RES_DEFNAMES | 1631158115Sume RES_DNSRCH | RES_NOALIASES | RES_USE_INET6); 1632158115Sume 1633158115Sume hostname = va_arg(ap, char *); 1634158115Sume hints = va_arg(ap, struct addrinfo *); 1635158115Sume 1636158115Sume desired_size = sizeof(res_options) + sizeof(int) + sizeof(int) * 4; 1637158115Sume if (hostname != NULL) { 1638158115Sume size = strlen(hostname); 1639158115Sume desired_size += size + 1; 1640158115Sume } else 1641158115Sume size = 0; 1642158115Sume 1643158115Sume if (desired_size > *buffer_size) { 1644158115Sume *buffer_size = desired_size; 1645158115Sume return (NS_RETURN); 1646158115Sume } 1647158115Sume 1648158115Sume if (hints == NULL) 1649158115Sume ai_flags = ai_family = ai_socktype = ai_protocol = 0; 1650158115Sume else { 1651158115Sume ai_flags = hints->ai_flags; 1652158115Sume ai_family = hints->ai_family; 1653158115Sume ai_socktype = hints->ai_socktype; 1654158115Sume ai_protocol = hints->ai_protocol; 1655158115Sume } 1656158115Sume 1657158115Sume p = buffer; 1658158115Sume memcpy(p, &res_options, sizeof(res_options)); 1659158115Sume p += sizeof(res_options); 1660158115Sume 1661158115Sume memcpy(p, &op_id, sizeof(int)); 1662158115Sume p += sizeof(int); 1663158115Sume 1664158115Sume memcpy(p, &ai_flags, sizeof(int)); 1665158115Sume p += sizeof(int); 1666158115Sume 1667158115Sume memcpy(p, &ai_family, sizeof(int)); 1668158115Sume p += sizeof(int); 1669158115Sume 1670158115Sume memcpy(p, &ai_socktype, sizeof(int)); 1671158115Sume p += sizeof(int); 1672158115Sume 1673158115Sume memcpy(p, &ai_protocol, sizeof(int)); 1674158115Sume p += sizeof(int); 1675158115Sume 1676158115Sume if (hostname != NULL) 1677158115Sume memcpy(p, hostname, size); 1678158115Sume 1679158115Sume *buffer_size = desired_size; 1680158115Sume return (NS_SUCCESS); 1681158115Sume} 1682158115Sume 1683158115Sumestatic int 1684158115Sumeaddrinfo_marshal_func(char *buffer, size_t *buffer_size, void *retval, 1685158115Sume va_list ap, void *cache_mdata) 1686158115Sume{ 1687158115Sume struct addrinfo *ai, *cai; 1688158115Sume char *p; 1689158115Sume size_t desired_size, size, ai_size; 1690158115Sume 1691158115Sume ai = *((struct addrinfo **)retval); 1692158115Sume 1693158115Sume desired_size = sizeof(size_t); 1694158115Sume ai_size = 0; 1695158115Sume for (cai = ai; cai != NULL; cai = cai->ai_next) { 1696158115Sume desired_size += sizeof(struct addrinfo) + cai->ai_addrlen; 1697158115Sume if (cai->ai_canonname != NULL) 1698158115Sume desired_size += sizeof(size_t) + 1699158115Sume strlen(cai->ai_canonname); 1700158115Sume ++ai_size; 1701158115Sume } 1702158115Sume 1703158115Sume if (desired_size > *buffer_size) { 1704158115Sume /* this assignment is here for future use */ 1705158115Sume errno = ERANGE; 1706158115Sume *buffer_size = desired_size; 1707158115Sume return (NS_RETURN); 1708158115Sume } 1709158115Sume 1710158115Sume memset(buffer, 0, desired_size); 1711158115Sume p = buffer; 1712158115Sume 1713158115Sume memcpy(p, &ai_size, sizeof(size_t)); 1714158115Sume p += sizeof(size_t); 1715158115Sume for (cai = ai; cai != NULL; cai = cai->ai_next) { 1716158115Sume memcpy(p, cai, sizeof(struct addrinfo)); 1717158115Sume p += sizeof(struct addrinfo); 1718158115Sume 1719158115Sume memcpy(p, cai->ai_addr, cai->ai_addrlen); 1720158115Sume p += cai->ai_addrlen; 1721158115Sume 1722158115Sume if (cai->ai_canonname != NULL) { 1723158115Sume size = strlen(cai->ai_canonname); 1724158115Sume memcpy(p, &size, sizeof(size_t)); 1725158115Sume p += sizeof(size_t); 1726158115Sume 1727158115Sume memcpy(p, cai->ai_canonname, size); 1728158115Sume p += size; 1729158115Sume } 1730158115Sume } 1731158115Sume 1732158115Sume return (NS_SUCCESS); 1733158115Sume} 1734158115Sume 1735158115Sumestatic int 1736158115Sumeaddrinfo_unmarshal_func(char *buffer, size_t buffer_size, void *retval, 1737158115Sume va_list ap, void *cache_mdata) 1738158115Sume{ 1739158115Sume struct addrinfo new_ai, *result, *sentinel, *lasts; 1740158115Sume 1741158115Sume char *p; 1742158115Sume size_t ai_size, ai_i, size; 1743158115Sume 1744158115Sume p = buffer; 1745158115Sume memcpy(&ai_size, p, sizeof(size_t)); 1746158115Sume p += sizeof(size_t); 1747158115Sume 1748158115Sume result = NULL; 1749158115Sume lasts = NULL; 1750158115Sume for (ai_i = 0; ai_i < ai_size; ++ai_i) { 1751158115Sume memcpy(&new_ai, p, sizeof(struct addrinfo)); 1752158115Sume p += sizeof(struct addrinfo); 1753158115Sume size = new_ai.ai_addrlen + sizeof(struct addrinfo) + 1754158115Sume _ALIGNBYTES; 1755158115Sume 1756158115Sume sentinel = (struct addrinfo *)malloc(size); 1757158115Sume memset(sentinel, 0, size); 1758158115Sume 1759158115Sume memcpy(sentinel, &new_ai, sizeof(struct addrinfo)); 1760158115Sume sentinel->ai_addr = (struct sockaddr *)_ALIGN((char *)sentinel + 1761158115Sume sizeof(struct addrinfo)); 1762158115Sume 1763158115Sume memcpy(sentinel->ai_addr, p, new_ai.ai_addrlen); 1764158115Sume p += new_ai.ai_addrlen; 1765158115Sume 1766158115Sume if (new_ai.ai_canonname != NULL) { 1767158115Sume memcpy(&size, p, sizeof(size_t)); 1768158115Sume p += sizeof(size_t); 1769158115Sume 1770158115Sume sentinel->ai_canonname = (char *)malloc(size + 1); 1771158115Sume memset(sentinel->ai_canonname, 0, size + 1); 1772158115Sume 1773158115Sume memcpy(sentinel->ai_canonname, p, size); 1774158115Sume p += size; 1775158115Sume } 1776158115Sume 1777158115Sume if (result == NULL) { 1778158115Sume result = sentinel; 1779158115Sume lasts = sentinel; 1780158115Sume } else { 1781158115Sume lasts->ai_next = sentinel; 1782158115Sume lasts = sentinel; 1783158115Sume } 1784158115Sume } 1785158115Sume 1786158115Sume *((struct addrinfo **)retval) = result; 1787158115Sume return (NS_SUCCESS); 1788158115Sume} 1789158115Sume#endif /* NS_CACHING */ 1790158115Sume 1791121426Sume/* 1792121426Sume * FQDN hostname, DNS lookup 1793121426Sume */ 1794102237Spirzykstatic int 1795157119Sumeexplore_fqdn(const struct addrinfo *pai, const char *hostname, 1796157119Sume const char *servname, struct addrinfo **res) 1797102237Spirzyk{ 1798121426Sume struct addrinfo *result; 1799121426Sume struct addrinfo *cur; 1800121426Sume int error = 0; 1801158115Sume 1802158115Sume#ifdef NS_CACHING 1803158115Sume static const nss_cache_info cache_info = 1804158115Sume NS_COMMON_CACHE_INFO_INITIALIZER( 1805158115Sume hosts, NULL, addrinfo_id_func, addrinfo_marshal_func, 1806158115Sume addrinfo_unmarshal_func); 1807158115Sume#endif 1808121426Sume static const ns_dtab dtab[] = { 1809121426Sume NS_FILES_CB(_files_getaddrinfo, NULL) 1810121426Sume { NSSRC_DNS, _dns_getaddrinfo, NULL }, /* force -DHESIOD */ 1811121426Sume NS_NIS_CB(_yp_getaddrinfo, NULL) 1812158115Sume#ifdef NS_CACHING 1813158115Sume NS_CACHE_CB(&cache_info) 1814158115Sume#endif 1815121426Sume { 0 } 1816121426Sume }; 1817102237Spirzyk 1818121426Sume result = NULL; 1819121426Sume 1820121426Sume /* 1821121426Sume * if the servname does not match socktype/protocol, ignore it. 1822121426Sume */ 1823126243Sgreen if (get_portmatch(pai, servname) != 0) 1824102237Spirzyk return 0; 1825102237Spirzyk 1826121426Sume switch (_nsdispatch(&result, dtab, NSDB_HOSTS, "getaddrinfo", 1827121426Sume default_dns_files, hostname, pai)) { 1828121426Sume case NS_TRYAGAIN: 1829121426Sume error = EAI_AGAIN; 1830121426Sume goto free; 1831121426Sume case NS_UNAVAIL: 1832121426Sume error = EAI_FAIL; 1833121426Sume goto free; 1834121426Sume case NS_NOTFOUND: 1835121426Sume error = EAI_NONAME; 1836121426Sume goto free; 1837121426Sume case NS_SUCCESS: 1838121426Sume error = 0; 1839121426Sume for (cur = result; cur; cur = cur->ai_next) { 1840121426Sume GET_PORT(cur, servname); 1841121426Sume /* canonname should be filled already */ 1842121426Sume } 1843121426Sume break; 1844102237Spirzyk } 1845102237Spirzyk 1846121426Sume *res = result; 1847121426Sume 1848102237Spirzyk return 0; 1849121426Sume 1850121426Sumefree: 1851121426Sume if (result) 1852121426Sume freeaddrinfo(result); 1853121426Sume return error; 1854102237Spirzyk} 1855102237Spirzyk 185661877Sume#ifdef DEBUG 185761877Sumestatic const char AskedForGot[] = 185861877Sume "gethostby*.getanswer: asked for \"%s\", got \"%s\""; 185961877Sume#endif 186061877Sume 186161877Sumestatic struct addrinfo * 1862157119Sumegetanswer(const querybuf *answer, int anslen, const char *qname, int qtype, 1863157119Sume const struct addrinfo *pai, res_state res) 186461877Sume{ 186561877Sume struct addrinfo sentinel, *cur; 186661877Sume struct addrinfo ai; 186761877Sume const struct afd *afd; 186861877Sume char *canonname; 186961877Sume const HEADER *hp; 187061877Sume const u_char *cp; 187161877Sume int n; 187261877Sume const u_char *eom; 1873105940Sume char *bp, *ep; 1874105940Sume int type, class, ancount, qdcount; 187561877Sume int haveanswer, had_error; 187661877Sume char tbuf[MAXDNAME]; 187792905Sobrien int (*name_ok)(const char *); 187861877Sume char hostbuf[8*1024]; 187961877Sume 188061877Sume memset(&sentinel, 0, sizeof(sentinel)); 188161877Sume cur = &sentinel; 188261877Sume 188361877Sume canonname = NULL; 188461877Sume eom = answer->buf + anslen; 188561877Sume switch (qtype) { 188661877Sume case T_A: 188761877Sume case T_AAAA: 188861877Sume case T_ANY: /*use T_ANY only for T_A/T_AAAA lookup*/ 188961877Sume name_ok = res_hnok; 189061877Sume break; 189161877Sume default: 189261877Sume return (NULL); /* XXX should be abort(); */ 189361877Sume } 189461877Sume /* 189561877Sume * find first satisfactory answer 189661877Sume */ 189761877Sume hp = &answer->hdr; 189861877Sume ancount = ntohs(hp->ancount); 189961877Sume qdcount = ntohs(hp->qdcount); 190061877Sume bp = hostbuf; 1901105940Sume ep = hostbuf + sizeof hostbuf; 190261877Sume cp = answer->buf + HFIXEDSZ; 190361877Sume if (qdcount != 1) { 1904156960Sume RES_SET_H_ERRNO(res, NO_RECOVERY); 190561877Sume return (NULL); 190661877Sume } 1907105940Sume n = dn_expand(answer->buf, eom, cp, bp, ep - bp); 190861877Sume if ((n < 0) || !(*name_ok)(bp)) { 1909156960Sume RES_SET_H_ERRNO(res, NO_RECOVERY); 191061877Sume return (NULL); 191161877Sume } 191261877Sume cp += n + QFIXEDSZ; 191361877Sume if (qtype == T_A || qtype == T_AAAA || qtype == T_ANY) { 191461877Sume /* res_send() has already verified that the query name is the 191561877Sume * same as the one we sent; this just gets the expanded name 191661877Sume * (i.e., with the succeeding search-domain tacked on). 191761877Sume */ 191861877Sume n = strlen(bp) + 1; /* for the \0 */ 191961877Sume if (n >= MAXHOSTNAMELEN) { 1920156960Sume RES_SET_H_ERRNO(res, NO_RECOVERY); 192161877Sume return (NULL); 192261877Sume } 192361877Sume canonname = bp; 192461877Sume bp += n; 192561877Sume /* The qname can be abbreviated, but h_name is now absolute. */ 192661877Sume qname = canonname; 192761877Sume } 192861877Sume haveanswer = 0; 192961877Sume had_error = 0; 193061877Sume while (ancount-- > 0 && cp < eom && !had_error) { 1931105940Sume n = dn_expand(answer->buf, eom, cp, bp, ep - bp); 193261877Sume if ((n < 0) || !(*name_ok)(bp)) { 193361877Sume had_error++; 193461877Sume continue; 193561877Sume } 193661877Sume cp += n; /* name */ 193761877Sume type = _getshort(cp); 193861877Sume cp += INT16SZ; /* type */ 193961877Sume class = _getshort(cp); 194061877Sume cp += INT16SZ + INT32SZ; /* class, TTL */ 194161877Sume n = _getshort(cp); 194261877Sume cp += INT16SZ; /* len */ 194361877Sume if (class != C_IN) { 194461877Sume /* XXX - debug? syslog? */ 194561877Sume cp += n; 194661877Sume continue; /* XXX - had_error++ ? */ 194761877Sume } 194861877Sume if ((qtype == T_A || qtype == T_AAAA || qtype == T_ANY) && 194961877Sume type == T_CNAME) { 195061877Sume n = dn_expand(answer->buf, eom, cp, tbuf, sizeof tbuf); 195161877Sume if ((n < 0) || !(*name_ok)(tbuf)) { 195261877Sume had_error++; 195361877Sume continue; 195461877Sume } 195561877Sume cp += n; 195661877Sume /* Get canonical name. */ 195761877Sume n = strlen(tbuf) + 1; /* for the \0 */ 1958105940Sume if (n > ep - bp || n >= MAXHOSTNAMELEN) { 195961877Sume had_error++; 196061877Sume continue; 196161877Sume } 1962114443Snectar strlcpy(bp, tbuf, ep - bp); 196361877Sume canonname = bp; 196461877Sume bp += n; 196561877Sume continue; 196661877Sume } 196761877Sume if (qtype == T_ANY) { 196861877Sume if (!(type == T_A || type == T_AAAA)) { 196961877Sume cp += n; 197061877Sume continue; 197161877Sume } 197261877Sume } else if (type != qtype) { 197361877Sume#ifdef DEBUG 1974188316Sume if (type != T_KEY && type != T_SIG && 1975188316Sume type != ns_t_dname) 197661877Sume syslog(LOG_NOTICE|LOG_AUTH, 197761877Sume "gethostby*.getanswer: asked for \"%s %s %s\", got type \"%s\"", 197861877Sume qname, p_class(C_IN), p_type(qtype), 197961877Sume p_type(type)); 198061877Sume#endif 198161877Sume cp += n; 198261877Sume continue; /* XXX - had_error++ ? */ 198361877Sume } 198461877Sume switch (type) { 198561877Sume case T_A: 198661877Sume case T_AAAA: 198761877Sume if (strcasecmp(canonname, bp) != 0) { 198861877Sume#ifdef DEBUG 198961877Sume syslog(LOG_NOTICE|LOG_AUTH, 199061877Sume AskedForGot, canonname, bp); 199161877Sume#endif 199261877Sume cp += n; 199361877Sume continue; /* XXX - had_error++ ? */ 199461877Sume } 199561877Sume if (type == T_A && n != INADDRSZ) { 199661877Sume cp += n; 199761877Sume continue; 199861877Sume } 199961877Sume if (type == T_AAAA && n != IN6ADDRSZ) { 200061877Sume cp += n; 200161877Sume continue; 200261877Sume } 200361877Sume#ifdef FILTER_V4MAPPED 200461877Sume if (type == T_AAAA) { 200561877Sume struct in6_addr in6; 200661877Sume memcpy(&in6, cp, sizeof(in6)); 200761877Sume if (IN6_IS_ADDR_V4MAPPED(&in6)) { 200861877Sume cp += n; 200961877Sume continue; 201061877Sume } 201161877Sume } 201261877Sume#endif 201361877Sume if (!haveanswer) { 201461877Sume int nn; 201561877Sume 201661877Sume canonname = bp; 201761877Sume nn = strlen(bp) + 1; /* for the \0 */ 201861877Sume bp += nn; 201961877Sume } 202061877Sume 202161877Sume /* don't overwrite pai */ 202261877Sume ai = *pai; 202361877Sume ai.ai_family = (type == T_A) ? AF_INET : AF_INET6; 202461877Sume afd = find_afd(ai.ai_family); 202561877Sume if (afd == NULL) { 202661877Sume cp += n; 202761877Sume continue; 202861877Sume } 202961877Sume cur->ai_next = get_ai(&ai, afd, (const char *)cp); 203061877Sume if (cur->ai_next == NULL) 203161877Sume had_error++; 203261877Sume while (cur && cur->ai_next) 203361877Sume cur = cur->ai_next; 203461877Sume cp += n; 203561877Sume break; 203661877Sume default: 203761877Sume abort(); 203861877Sume } 203961877Sume if (!had_error) 204061877Sume haveanswer++; 204161877Sume } 204261877Sume if (haveanswer) { 2043102237Spirzyk#if defined(RESOLVSORT) 2044102237Spirzyk /* 2045102237Spirzyk * We support only IPv4 address for backward 2046102237Spirzyk * compatibility against gethostbyname(3). 2047102237Spirzyk */ 2048156960Sume if (res->nsort && qtype == T_A) { 2049156960Sume if (addr4sort(&sentinel, res) < 0) { 2050102237Spirzyk freeaddrinfo(sentinel.ai_next); 2051156960Sume RES_SET_H_ERRNO(res, NO_RECOVERY); 2052102237Spirzyk return NULL; 2053102237Spirzyk } 2054102237Spirzyk } 2055102237Spirzyk#endif /*RESOLVSORT*/ 205661877Sume if (!canonname) 205761877Sume (void)get_canonname(pai, sentinel.ai_next, qname); 205861877Sume else 205961877Sume (void)get_canonname(pai, sentinel.ai_next, canonname); 2060156960Sume RES_SET_H_ERRNO(res, NETDB_SUCCESS); 206161877Sume return sentinel.ai_next; 206261877Sume } 206361877Sume 2064156960Sume RES_SET_H_ERRNO(res, NO_RECOVERY); 206561877Sume return NULL; 206661877Sume} 206761877Sume 2068121426Sume#ifdef RESOLVSORT 2069121426Sumestruct addr_ptr { 2070121426Sume struct addrinfo *ai; 2071121426Sume int aval; 2072121426Sume}; 2073121426Sume 2074121426Sumestatic int 2075156960Sumeaddr4sort(struct addrinfo *sentinel, res_state res) 2076121426Sume{ 2077121426Sume struct addrinfo *ai; 2078121426Sume struct addr_ptr *addrs, addr; 2079121426Sume struct sockaddr_in *sin; 2080121426Sume int naddrs, i, j; 2081121426Sume int needsort = 0; 2082121426Sume 2083121426Sume if (!sentinel) 2084121426Sume return -1; 2085121426Sume naddrs = 0; 2086121426Sume for (ai = sentinel->ai_next; ai; ai = ai->ai_next) 2087121426Sume naddrs++; 2088121426Sume if (naddrs < 2) 2089121426Sume return 0; /* We don't need sorting. */ 2090121426Sume if ((addrs = malloc(sizeof(struct addr_ptr) * naddrs)) == NULL) 2091121426Sume return -1; 2092121426Sume i = 0; 2093121426Sume for (ai = sentinel->ai_next; ai; ai = ai->ai_next) { 2094121426Sume sin = (struct sockaddr_in *)ai->ai_addr; 2095156960Sume for (j = 0; (unsigned)j < res->nsort; j++) { 2096157119Sume if (res->sort_list[j].addr.s_addr == 2097156960Sume (sin->sin_addr.s_addr & res->sort_list[j].mask)) 2098121426Sume break; 2099121426Sume } 2100121426Sume addrs[i].ai = ai; 2101121426Sume addrs[i].aval = j; 2102121426Sume if (needsort == 0 && i > 0 && j < addrs[i - 1].aval) 2103121426Sume needsort = i; 2104121426Sume i++; 2105121426Sume } 2106121426Sume if (!needsort) { 2107121426Sume free(addrs); 2108121426Sume return 0; 2109121426Sume } 2110121426Sume 2111121426Sume while (needsort < naddrs) { 2112157371Sume for (j = needsort - 1; j >= 0; j--) { 2113157371Sume if (addrs[j].aval > addrs[j+1].aval) { 2114157371Sume addr = addrs[j]; 2115157371Sume addrs[j] = addrs[j + 1]; 2116157371Sume addrs[j + 1] = addr; 2117157371Sume } else 2118157371Sume break; 2119157371Sume } 2120157371Sume needsort++; 2121121426Sume } 2122121426Sume 2123121426Sume ai = sentinel; 2124121426Sume for (i = 0; i < naddrs; ++i) { 2125121426Sume ai->ai_next = addrs[i].ai; 2126121426Sume ai = ai->ai_next; 2127121426Sume } 2128121426Sume ai->ai_next = NULL; 2129121426Sume free(addrs); 2130121426Sume return 0; 2131121426Sume} 2132121426Sume#endif /*RESOLVSORT*/ 2133121426Sume 213461877Sume/*ARGSUSED*/ 213561877Sumestatic int 2136157119Sume_dns_getaddrinfo(void *rv, void *cb_data, va_list ap) 213761877Sume{ 213861877Sume struct addrinfo *ai; 2139103357Sume querybuf *buf, *buf2; 2140130600Sume const char *hostname; 214165532Snectar const struct addrinfo *pai; 214261877Sume struct addrinfo sentinel, *cur; 214361877Sume struct res_target q, q2; 2144156960Sume res_state res; 214561877Sume 2146130600Sume hostname = va_arg(ap, char *); 214765532Snectar pai = va_arg(ap, const struct addrinfo *); 214865532Snectar 2149156946Sdelphij memset(&q, 0, sizeof(q)); 215061877Sume memset(&q2, 0, sizeof(q2)); 215161877Sume memset(&sentinel, 0, sizeof(sentinel)); 215261877Sume cur = &sentinel; 215361877Sume 2154103357Sume buf = malloc(sizeof(*buf)); 2155103357Sume if (!buf) { 2156156960Sume RES_SET_H_ERRNO(res, NETDB_INTERNAL); 2157103357Sume return NS_NOTFOUND; 2158103357Sume } 2159103357Sume buf2 = malloc(sizeof(*buf2)); 2160103357Sume if (!buf2) { 2161103357Sume free(buf); 2162156960Sume RES_SET_H_ERRNO(res, NETDB_INTERNAL); 2163103357Sume return NS_NOTFOUND; 2164103357Sume } 2165103357Sume 216661877Sume switch (pai->ai_family) { 216761877Sume case AF_UNSPEC: 2168130600Sume q.name = hostname; 216962614Sitojun q.qclass = C_IN; 2170140896Sume q.qtype = T_A; 2171103357Sume q.answer = buf->buf; 2172103357Sume q.anslen = sizeof(buf->buf); 217361877Sume q.next = &q2; 2174130600Sume q2.name = hostname; 217562614Sitojun q2.qclass = C_IN; 2176140896Sume q2.qtype = T_AAAA; 2177103357Sume q2.answer = buf2->buf; 2178103357Sume q2.anslen = sizeof(buf2->buf); 217961877Sume break; 218061877Sume case AF_INET: 2181130600Sume q.name = hostname; 218262614Sitojun q.qclass = C_IN; 218362614Sitojun q.qtype = T_A; 2184103357Sume q.answer = buf->buf; 2185103357Sume q.anslen = sizeof(buf->buf); 218661877Sume break; 218761877Sume case AF_INET6: 2188130600Sume q.name = hostname; 218962614Sitojun q.qclass = C_IN; 219062614Sitojun q.qtype = T_AAAA; 2191103357Sume q.answer = buf->buf; 2192103357Sume q.anslen = sizeof(buf->buf); 219361877Sume break; 219461877Sume default: 2195103357Sume free(buf); 2196103357Sume free(buf2); 219765532Snectar return NS_UNAVAIL; 219861877Sume } 2199156960Sume 2200156960Sume res = __res_state(); 2201156960Sume if ((res->options & RES_INIT) == 0 && res_ninit(res) == -1) { 2202156960Sume RES_SET_H_ERRNO(res, NETDB_INTERNAL); 2203103357Sume free(buf); 2204103357Sume free(buf2); 220565532Snectar return NS_NOTFOUND; 2206103357Sume } 2207156960Sume 2208156960Sume if (res_searchN(hostname, &q, res) < 0) { 2209156960Sume free(buf); 2210156960Sume free(buf2); 2211156960Sume return NS_NOTFOUND; 2212156960Sume } 2213140896Sume /* prefer IPv6 */ 221461877Sume if (q.next) { 2215156960Sume ai = getanswer(buf2, q2.n, q2.name, q2.qtype, pai, res); 2216140896Sume if (ai) { 221761877Sume cur->ai_next = ai; 2218140896Sume while (cur && cur->ai_next) 2219140896Sume cur = cur->ai_next; 2220140896Sume } 222161877Sume } 2222156960Sume ai = getanswer(buf, q.n, q.name, q.qtype, pai, res); 2223140896Sume if (ai) 2224140896Sume cur->ai_next = ai; 2225103357Sume free(buf); 2226103357Sume free(buf2); 222761877Sume if (sentinel.ai_next == NULL) 2228156960Sume switch (res->res_h_errno) { 222961877Sume case HOST_NOT_FOUND: 223065532Snectar return NS_NOTFOUND; 223161877Sume case TRY_AGAIN: 223265532Snectar return NS_TRYAGAIN; 223361877Sume default: 223465532Snectar return NS_UNAVAIL; 223561877Sume } 223665532Snectar *((struct addrinfo **)rv) = sentinel.ai_next; 223765532Snectar return NS_SUCCESS; 223861877Sume} 223961877Sume 224065532Snectarstatic void 2241144634Sume_sethtent(FILE **hostf) 224265532Snectar{ 2243144634Sume if (!*hostf) 2244144634Sume *hostf = fopen(_PATH_HOSTS, "r"); 224565532Snectar else 2246144634Sume rewind(*hostf); 224765532Snectar} 224865532Snectar 224965532Snectarstatic void 2250144634Sume_endhtent(FILE **hostf) 225165532Snectar{ 2252144634Sume if (*hostf) { 2253144634Sume (void) fclose(*hostf); 2254144634Sume *hostf = NULL; 225565532Snectar } 225665532Snectar} 225765532Snectar 225861877Sumestatic struct addrinfo * 2259144634Sume_gethtent(FILE **hostf, const char *name, const struct addrinfo *pai) 226061877Sume{ 226161877Sume char *p; 226261877Sume char *cp, *tname, *cname; 226361877Sume struct addrinfo hints, *res0, *res; 226461877Sume int error; 226561877Sume const char *addr; 226661877Sume char hostbuf[8*1024]; 226761877Sume 2268144634Sume if (!*hostf && !(*hostf = fopen(_PATH_HOSTS, "r"))) 226965532Snectar return (NULL); 2270105940Sumeagain: 2271144634Sume if (!(p = fgets(hostbuf, sizeof hostbuf, *hostf))) 227261877Sume return (NULL); 227361877Sume if (*p == '#') 227461877Sume goto again; 2275139612Ssobomax cp = strpbrk(p, "#\n"); 2276139612Ssobomax if (cp != NULL) 2277139612Ssobomax *cp = '\0'; 227861877Sume if (!(cp = strpbrk(p, " \t"))) 227961877Sume goto again; 228061877Sume *cp++ = '\0'; 228161877Sume addr = p; 228261877Sume cname = NULL; 228361877Sume /* if this is not something we're looking for, skip it. */ 228461877Sume while (cp && *cp) { 228561877Sume if (*cp == ' ' || *cp == '\t') { 228661877Sume cp++; 228761877Sume continue; 228861877Sume } 228961877Sume tname = cp; 229061877Sume if (cname == NULL) 229161877Sume cname = cp; 229261877Sume if ((cp = strpbrk(cp, " \t")) != NULL) 229361877Sume *cp++ = '\0'; 229461877Sume if (strcasecmp(name, tname) == 0) 229561877Sume goto found; 229661877Sume } 229761877Sume goto again; 229861877Sume 229961877Sumefound: 2300105940Sume /* we should not glob socktype/protocol here */ 2301105940Sume memset(&hints, 0, sizeof(hints)); 2302105940Sume hints.ai_family = pai->ai_family; 2303105940Sume hints.ai_socktype = SOCK_DGRAM; 2304105940Sume hints.ai_protocol = 0; 230561877Sume hints.ai_flags = AI_NUMERICHOST; 2306105940Sume error = getaddrinfo(addr, "0", &hints, &res0); 230761877Sume if (error) 230861877Sume goto again; 230961877Sume#ifdef FILTER_V4MAPPED 231061877Sume /* XXX should check all items in the chain */ 231161877Sume if (res0->ai_family == AF_INET6 && 231261877Sume IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)res0->ai_addr)->sin6_addr)) { 231361877Sume freeaddrinfo(res0); 231461877Sume goto again; 231561877Sume } 231661877Sume#endif 231761877Sume for (res = res0; res; res = res->ai_next) { 231861877Sume /* cover it up */ 231961877Sume res->ai_flags = pai->ai_flags; 2320105940Sume res->ai_socktype = pai->ai_socktype; 2321105940Sume res->ai_protocol = pai->ai_protocol; 232261877Sume 232361877Sume if (pai->ai_flags & AI_CANONNAME) { 232461877Sume if (get_canonname(pai, res, cname) != 0) { 232561877Sume freeaddrinfo(res0); 232661877Sume goto again; 232761877Sume } 232861877Sume } 232961877Sume } 233061877Sume return res0; 233161877Sume} 233261877Sume 233361877Sume/*ARGSUSED*/ 233461877Sumestatic int 2335157119Sume_files_getaddrinfo(void *rv, void *cb_data, va_list ap) 233665532Snectar{ 233765532Snectar const char *name; 233861877Sume const struct addrinfo *pai; 233961877Sume struct addrinfo sentinel, *cur; 234061877Sume struct addrinfo *p; 2341144634Sume FILE *hostf = NULL; 234261877Sume 234365532Snectar name = va_arg(ap, char *); 234465532Snectar pai = va_arg(ap, struct addrinfo *); 234565532Snectar 234665532Snectar memset(&sentinel, 0, sizeof(sentinel)); 234761877Sume cur = &sentinel; 234861877Sume 2349144634Sume _sethtent(&hostf); 2350144634Sume while ((p = _gethtent(&hostf, name, pai)) != NULL) { 235161877Sume cur->ai_next = p; 235261877Sume while (cur && cur->ai_next) 235361877Sume cur = cur->ai_next; 235461877Sume } 2355144634Sume _endhtent(&hostf); 235661877Sume 235765532Snectar *((struct addrinfo **)rv) = sentinel.ai_next; 235865532Snectar if (sentinel.ai_next == NULL) 235965532Snectar return NS_NOTFOUND; 236065532Snectar return NS_SUCCESS; 236161877Sume} 236261877Sume 236361877Sume#ifdef YP 236461877Sume/*ARGSUSED*/ 236565532Snectarstatic struct addrinfo * 2366157119Sume_yphostent(char *line, const struct addrinfo *pai) 236761877Sume{ 236861877Sume struct addrinfo sentinel, *cur; 236965532Snectar struct addrinfo hints, *res, *res0; 237061877Sume int error; 237165532Snectar char *p = line; 237265532Snectar const char *addr, *canonname; 237365532Snectar char *nextline; 237465532Snectar char *cp; 237561877Sume 237665532Snectar addr = canonname = NULL; 237765532Snectar 237865532Snectar memset(&sentinel, 0, sizeof(sentinel)); 237961877Sume cur = &sentinel; 238061877Sume 238165532Snectarnextline: 238265532Snectar /* terminate line */ 238365532Snectar cp = strchr(p, '\n'); 238465532Snectar if (cp) { 238565532Snectar *cp++ = '\0'; 238665532Snectar nextline = cp; 238765532Snectar } else 238865532Snectar nextline = NULL; 238961877Sume 239065532Snectar cp = strpbrk(p, " \t"); 239165532Snectar if (cp == NULL) { 239265532Snectar if (canonname == NULL) 239365532Snectar return (NULL); 239465532Snectar else 239565532Snectar goto done; 239661877Sume } 239765532Snectar *cp++ = '\0'; 239861877Sume 239965532Snectar addr = p; 240061877Sume 240165532Snectar while (cp && *cp) { 240265532Snectar if (*cp == ' ' || *cp == '\t') { 240365532Snectar cp++; 240461877Sume continue; 240565532Snectar } 240665532Snectar if (!canonname) 240765532Snectar canonname = cp; 240865532Snectar if ((cp = strpbrk(cp, " \t")) != NULL) 240965532Snectar *cp++ = '\0'; 241065532Snectar } 241161877Sume 2412121474Sume hints = *pai; 241365532Snectar hints.ai_flags = AI_NUMERICHOST; 2414121474Sume error = getaddrinfo(addr, NULL, &hints, &res0); 241565532Snectar if (error == 0) { 241665532Snectar for (res = res0; res; res = res->ai_next) { 241765532Snectar /* cover it up */ 241865532Snectar res->ai_flags = pai->ai_flags; 241961877Sume 242065532Snectar if (pai->ai_flags & AI_CANONNAME) 242165532Snectar (void)get_canonname(pai, res, canonname); 242261877Sume } 242365532Snectar } else 242465532Snectar res0 = NULL; 242565532Snectar if (res0) { 242665532Snectar cur->ai_next = res0; 242761877Sume while (cur && cur->ai_next) 242861877Sume cur = cur->ai_next; 242961877Sume } 243061877Sume 243165532Snectar if (nextline) { 243265532Snectar p = nextline; 243365532Snectar goto nextline; 243465532Snectar } 243561877Sume 243665532Snectardone: 243765532Snectar return sentinel.ai_next; 243861877Sume} 243965532Snectar 244065532Snectar/*ARGSUSED*/ 244165532Snectarstatic int 2442157119Sume_yp_getaddrinfo(void *rv, void *cb_data, va_list ap) 244365532Snectar{ 244465532Snectar struct addrinfo sentinel, *cur; 244565532Snectar struct addrinfo *ai = NULL; 2446144679Sume char *ypbuf; 2447144679Sume int ypbuflen, r; 244865532Snectar const char *name; 244965532Snectar const struct addrinfo *pai; 2450144679Sume char *ypdomain; 245165532Snectar 2452144679Sume if (_yp_check(&ypdomain) == 0) 2453144679Sume return NS_UNAVAIL; 2454144679Sume 245565532Snectar name = va_arg(ap, char *); 245665532Snectar pai = va_arg(ap, const struct addrinfo *); 245765532Snectar 245865532Snectar memset(&sentinel, 0, sizeof(sentinel)); 245965532Snectar cur = &sentinel; 246065532Snectar 246165532Snectar /* hosts.byname is only for IPv4 (Solaris8) */ 246265532Snectar if (pai->ai_family == PF_UNSPEC || pai->ai_family == PF_INET) { 2463144679Sume r = yp_match(ypdomain, "hosts.byname", name, 2464144679Sume (int)strlen(name), &ypbuf, &ypbuflen); 246565532Snectar if (r == 0) { 246665532Snectar struct addrinfo ai4; 246765532Snectar 246865532Snectar ai4 = *pai; 246965532Snectar ai4.ai_family = AF_INET; 2470144679Sume ai = _yphostent(ypbuf, &ai4); 247165532Snectar if (ai) { 247265532Snectar cur->ai_next = ai; 247365532Snectar while (cur && cur->ai_next) 247465532Snectar cur = cur->ai_next; 247565532Snectar } 2476146190Sume free(ypbuf); 247765532Snectar } 247865532Snectar } 247965532Snectar 248065532Snectar /* ipnodes.byname can hold both IPv4/v6 */ 2481144679Sume r = yp_match(ypdomain, "ipnodes.byname", name, 2482144679Sume (int)strlen(name), &ypbuf, &ypbuflen); 248365532Snectar if (r == 0) { 2484144679Sume ai = _yphostent(ypbuf, pai); 2485144679Sume if (ai) 248665532Snectar cur->ai_next = ai; 2487144679Sume free(ypbuf); 248865532Snectar } 248965532Snectar 249065532Snectar if (sentinel.ai_next == NULL) { 2491156960Sume RES_SET_H_ERRNO(__res_state(), HOST_NOT_FOUND); 249265532Snectar return NS_NOTFOUND; 249365532Snectar } 249465532Snectar *((struct addrinfo **)rv) = sentinel.ai_next; 249565532Snectar return NS_SUCCESS; 249665532Snectar} 249761877Sume#endif 249861877Sume 249961877Sume/* resolver logic */ 250061877Sume 250161877Sume/* 250261877Sume * Formulate a normal query, send, and await answer. 250361877Sume * Returned answer is placed in supplied buffer "answer". 250461877Sume * Perform preliminary check of answer, returning success only 250561877Sume * if no error is indicated and the answer count is nonzero. 250661877Sume * Return the size of the response on success, -1 on error. 250761877Sume * Error number is left in h_errno. 250861877Sume * 250961877Sume * Caller must parse answer and determine whether it answers the question. 251061877Sume */ 251161877Sumestatic int 2512157119Sumeres_queryN(const char *name, struct res_target *target, res_state res) 251361877Sume{ 2514103357Sume u_char *buf; 251561877Sume HEADER *hp; 251661877Sume int n; 2517157203Sume u_int oflags; 251861877Sume struct res_target *t; 251961877Sume int rcode; 252061877Sume int ancount; 252161877Sume 252261877Sume rcode = NOERROR; 252361877Sume ancount = 0; 252461877Sume 2525103357Sume buf = malloc(MAXPACKET); 2526103357Sume if (!buf) { 2527156960Sume RES_SET_H_ERRNO(res, NETDB_INTERNAL); 2528103357Sume return -1; 2529103357Sume } 2530103357Sume 253161877Sume for (t = target; t; t = t->next) { 253261877Sume int class, type; 253361877Sume u_char *answer; 253461877Sume int anslen; 253561877Sume 253661877Sume hp = (HEADER *)(void *)t->answer; 253761877Sume 253861877Sume /* make it easier... */ 253962614Sitojun class = t->qclass; 254062614Sitojun type = t->qtype; 254161877Sume answer = t->answer; 254261877Sume anslen = t->anslen; 2543157203Sume 2544157203Sume oflags = res->_flags; 2545157203Sume 2546157203Sumeagain: 2547157203Sume hp->rcode = NOERROR; /* default */ 2548157203Sume 254961877Sume#ifdef DEBUG 2550156960Sume if (res->options & RES_DEBUG) 255161877Sume printf(";; res_query(%s, %d, %d)\n", name, class, type); 255261877Sume#endif 255361877Sume 2554156960Sume n = res_nmkquery(res, QUERY, name, class, type, NULL, 0, NULL, 2555103357Sume buf, MAXPACKET); 2556157203Sume if (n > 0 && (res->_flags & RES_F_EDNS0ERR) == 0 && 2557157203Sume (res->options & (RES_USE_EDNS0|RES_USE_DNSSEC)) != 0U) 2558156960Sume n = res_nopt(res, n, buf, MAXPACKET, anslen); 255961877Sume if (n <= 0) { 256061877Sume#ifdef DEBUG 2561156960Sume if (res->options & RES_DEBUG) 256261877Sume printf(";; res_query: mkquery failed\n"); 256361877Sume#endif 2564103357Sume free(buf); 2565156960Sume RES_SET_H_ERRNO(res, NO_RECOVERY); 256661877Sume return (n); 256761877Sume } 2568156960Sume n = res_nsend(res, buf, n, answer, anslen); 256961877Sume if (n < 0) { 2570157203Sume /* 2571157203Sume * if the query choked with EDNS0, retry 2572157203Sume * without EDNS0 2573157203Sume */ 2574157203Sume if ((res->options & (RES_USE_EDNS0|RES_USE_DNSSEC)) 2575157203Sume != 0U && 2576157203Sume ((oflags ^ res->_flags) & RES_F_EDNS0ERR) != 0) { 2577157203Sume res->_flags |= RES_F_EDNS0ERR; 2578157203Sume if (res->options & RES_DEBUG) 2579157203Sume printf(";; res_nquery: retry without EDNS0\n"); 2580157203Sume goto again; 2581157203Sume } 2582157203Sume rcode = hp->rcode; /* record most recent error */ 258361877Sume#ifdef DEBUG 2584156960Sume if (res->options & RES_DEBUG) 258561877Sume printf(";; res_query: send error\n"); 258661877Sume#endif 2587157203Sume continue; 258861877Sume } 258961877Sume 2590157081Sume if (n > anslen) 2591103350Snectar hp->rcode = FORMERR; /* XXX not very informative */ 2592157203Sume if (hp->rcode != NOERROR || ntohs(hp->ancount) == 0) { 259361877Sume rcode = hp->rcode; /* record most recent error */ 259461877Sume#ifdef DEBUG 2595156960Sume if (res->options & RES_DEBUG) 2596105940Sume printf(";; rcode = %u, ancount=%u\n", hp->rcode, 259761877Sume ntohs(hp->ancount)); 259861877Sume#endif 259961877Sume continue; 260061877Sume } 260161877Sume 260261877Sume ancount += ntohs(hp->ancount); 260361877Sume 260461877Sume t->n = n; 260561877Sume } 260661877Sume 2607103357Sume free(buf); 2608103357Sume 260961877Sume if (ancount == 0) { 261061877Sume switch (rcode) { 261161877Sume case NXDOMAIN: 2612156960Sume RES_SET_H_ERRNO(res, HOST_NOT_FOUND); 261361877Sume break; 261461877Sume case SERVFAIL: 2615156960Sume RES_SET_H_ERRNO(res, TRY_AGAIN); 261661877Sume break; 261761877Sume case NOERROR: 2618156960Sume RES_SET_H_ERRNO(res, NO_DATA); 261961877Sume break; 262061877Sume case FORMERR: 262161877Sume case NOTIMP: 262261877Sume case REFUSED: 262361877Sume default: 2624156960Sume RES_SET_H_ERRNO(res, NO_RECOVERY); 262561877Sume break; 262661877Sume } 262761877Sume return (-1); 262861877Sume } 262961877Sume return (ancount); 263061877Sume} 263161877Sume 263261877Sume/* 263361877Sume * Formulate a normal query, send, and retrieve answer in supplied buffer. 263461877Sume * Return the size of the response on success, -1 on error. 263561877Sume * If enabled, implement search rules until answer or unrecoverable failure 263661877Sume * is detected. Error code, if any, is left in h_errno. 263761877Sume */ 263861877Sumestatic int 2639157119Sumeres_searchN(const char *name, struct res_target *target, res_state res) 264061877Sume{ 264161877Sume const char *cp, * const *domain; 264261877Sume HEADER *hp = (HEADER *)(void *)target->answer; /*XXX*/ 264361877Sume u_int dots; 264461877Sume int trailing_dot, ret, saved_herrno; 2645155983Sume int got_nodata = 0, got_servfail = 0, root_on_list = 0; 2646155983Sume int tried_as_is = 0; 2647155983Sume int searched = 0; 2648145113Sume char abuf[MAXDNAME]; 264961877Sume 265061877Sume errno = 0; 2651156960Sume RES_SET_H_ERRNO(res, HOST_NOT_FOUND); /* default, if we never query */ 265261877Sume dots = 0; 265361877Sume for (cp = name; *cp; cp++) 265461877Sume dots += (*cp == '.'); 265561877Sume trailing_dot = 0; 265661877Sume if (cp > name && *--cp == '.') 265761877Sume trailing_dot++; 265861877Sume 265961877Sume /* 266061877Sume * if there aren't any dots, it could be a user-level alias 266161877Sume */ 2662156960Sume if (!dots && 2663156960Sume (cp = res_hostalias(res, name, abuf, sizeof(abuf))) != NULL) 2664156960Sume return (res_queryN(cp, target, res)); 266561877Sume 266661877Sume /* 2667155983Sume * If there are enough dots in the name, let's just give it a 2668155983Sume * try 'as is'. The threshold can be set with the "ndots" option. 2669155983Sume * Also, query 'as is', if there is a trailing dot in the name. 267061877Sume */ 267161877Sume saved_herrno = -1; 2672156960Sume if (dots >= res->ndots || trailing_dot) { 2673156960Sume ret = res_querydomainN(name, NULL, target, res); 2674155983Sume if (ret > 0 || trailing_dot) 267561877Sume return (ret); 2676156155Sume if (errno == ECONNREFUSED) { 2677156960Sume RES_SET_H_ERRNO(res, TRY_AGAIN); 2678156155Sume return (-1); 2679156155Sume } 2680156960Sume switch (res->res_h_errno) { 2681156155Sume case NO_DATA: 2682156155Sume case HOST_NOT_FOUND: 2683156155Sume break; 2684157093Sume case TRY_AGAIN: 2685157093Sume if (hp->rcode == SERVFAIL) 2686157093Sume break; 2687157093Sume /* FALLTHROUGH */ 2688156155Sume default: 2689156155Sume return (-1); 2690156155Sume } 2691156960Sume saved_herrno = res->res_h_errno; 269261877Sume tried_as_is++; 269361877Sume } 269461877Sume 269561877Sume /* 269661877Sume * We do at least one level of search if 269761877Sume * - there is no dot and RES_DEFNAME is set, or 269861877Sume * - there is at least one dot, there is no trailing dot, 269961877Sume * and RES_DNSRCH is set. 270061877Sume */ 2701156960Sume if ((!dots && (res->options & RES_DEFNAMES)) || 2702156960Sume (dots && !trailing_dot && (res->options & RES_DNSRCH))) { 270361877Sume int done = 0; 270461877Sume 2705156960Sume for (domain = (const char * const *)res->dnsrch; 270661877Sume *domain && !done; 270761877Sume domain++) { 2708155983Sume searched = 1; 270961877Sume 2710155983Sume if (domain[0][0] == '\0' || 2711155983Sume (domain[0][0] == '.' && domain[0][1] == '\0')) 2712155983Sume root_on_list++; 2713155983Sume 2714155983Sume if (root_on_list && tried_as_is) 2715155983Sume continue; 2716155983Sume 2717156960Sume ret = res_querydomainN(name, *domain, target, res); 271861877Sume if (ret > 0) 271961877Sume return (ret); 272061877Sume 272161877Sume /* 272261877Sume * If no server present, give up. 272361877Sume * If name isn't found in this domain, 272461877Sume * keep trying higher domains in the search list 272561877Sume * (if that's enabled). 272661877Sume * On a NO_DATA error, keep trying, otherwise 272761877Sume * a wildcard entry of another type could keep us 272861877Sume * from finding this entry higher in the domain. 272961877Sume * If we get some other error (negative answer or 273061877Sume * server failure), then stop searching up, 273161877Sume * but try the input name below in case it's 273261877Sume * fully-qualified. 273361877Sume */ 273461877Sume if (errno == ECONNREFUSED) { 2735156960Sume RES_SET_H_ERRNO(res, TRY_AGAIN); 273661877Sume return (-1); 273761877Sume } 273861877Sume 2739156960Sume switch (res->res_h_errno) { 274061877Sume case NO_DATA: 274161877Sume got_nodata++; 274261877Sume /* FALLTHROUGH */ 274361877Sume case HOST_NOT_FOUND: 274461877Sume /* keep trying */ 274561877Sume break; 274661877Sume case TRY_AGAIN: 2747157093Sume got_servfail++; 274861877Sume if (hp->rcode == SERVFAIL) { 274961877Sume /* try next search element, if any */ 275061877Sume break; 275161877Sume } 275261877Sume /* FALLTHROUGH */ 275361877Sume default: 275461877Sume /* anything else implies that we're done */ 275561877Sume done++; 275661877Sume } 275761877Sume /* 275861877Sume * if we got here for some reason other than DNSRCH, 275961877Sume * we only wanted one iteration of the loop, so stop. 276061877Sume */ 2761156960Sume if (!(res->options & RES_DNSRCH)) 276261877Sume done++; 276361877Sume } 276461877Sume } 276561877Sume 2766156960Sume switch (res->res_h_errno) { 2767156155Sume case NO_DATA: 2768156155Sume case HOST_NOT_FOUND: 2769156155Sume break; 2770157093Sume case TRY_AGAIN: 2771157093Sume if (hp->rcode == SERVFAIL) 2772157093Sume break; 2773157093Sume /* FALLTHROUGH */ 2774156155Sume default: 2775156155Sume goto giveup; 2776156155Sume } 2777156155Sume 277861877Sume /* 2779155983Sume * If the query has not already been tried as is then try it 2780155983Sume * unless RES_NOTLDQUERY is set and there were no dots. 278161877Sume */ 2782156960Sume if ((dots || !searched || !(res->options & RES_NOTLDQUERY)) && 2783155983Sume !(tried_as_is || root_on_list)) { 2784156960Sume ret = res_querydomainN(name, NULL, target, res); 278561877Sume if (ret > 0) 278661877Sume return (ret); 278761877Sume } 278861877Sume 278961877Sume /* 279061877Sume * if we got here, we didn't satisfy the search. 279161877Sume * if we did an initial full query, return that query's h_errno 279261877Sume * (note that we wouldn't be here if that query had succeeded). 279361877Sume * else if we ever got a nodata, send that back as the reason. 279461877Sume * else send back meaningless h_errno, that being the one from 279561877Sume * the last DNSRCH we did. 279661877Sume */ 2797156155Sumegiveup: 279861877Sume if (saved_herrno != -1) 2799156960Sume RES_SET_H_ERRNO(res, saved_herrno); 280061877Sume else if (got_nodata) 2801156960Sume RES_SET_H_ERRNO(res, NO_DATA); 280261877Sume else if (got_servfail) 2803156960Sume RES_SET_H_ERRNO(res, TRY_AGAIN); 280461877Sume return (-1); 280561877Sume} 280661877Sume 280761877Sume/* 280861877Sume * Perform a call on res_query on the concatenation of name and domain, 280961877Sume * removing a trailing dot from name if domain is NULL. 281061877Sume */ 281161877Sumestatic int 2812157119Sumeres_querydomainN(const char *name, const char *domain, 2813157119Sume struct res_target *target, res_state res) 281461877Sume{ 281561877Sume char nbuf[MAXDNAME]; 281661877Sume const char *longname = nbuf; 281761877Sume size_t n, d; 281861877Sume 281961877Sume#ifdef DEBUG 2820156960Sume if (res->options & RES_DEBUG) 282161877Sume printf(";; res_querydomain(%s, %s)\n", 282261877Sume name, domain?domain:"<Nil>"); 282361877Sume#endif 282461877Sume if (domain == NULL) { 282561877Sume /* 282661877Sume * Check for trailing '.'; 282761877Sume * copy without '.' if present. 282861877Sume */ 282961877Sume n = strlen(name); 283061877Sume if (n >= MAXDNAME) { 2831156960Sume RES_SET_H_ERRNO(res, NO_RECOVERY); 283261877Sume return (-1); 283361877Sume } 283461877Sume if (n > 0 && name[--n] == '.') { 283561877Sume strncpy(nbuf, name, n); 283661877Sume nbuf[n] = '\0'; 283761877Sume } else 283861877Sume longname = name; 283961877Sume } else { 284061877Sume n = strlen(name); 284161877Sume d = strlen(domain); 284261877Sume if (n + d + 1 >= MAXDNAME) { 2843156960Sume RES_SET_H_ERRNO(res, NO_RECOVERY); 284461877Sume return (-1); 284561877Sume } 2846105940Sume snprintf(nbuf, sizeof(nbuf), "%s.%s", name, domain); 284761877Sume } 2848156960Sume return (res_queryN(longname, target, res)); 284961877Sume} 2850