michael@0: /* $NetBSD: getaddrinfo.c,v 1.82 2006/03/25 12:09:40 rpaulo Exp $ */ michael@0: /* $KAME: getaddrinfo.c,v 1.29 2000/08/31 17:26:57 itojun Exp $ */ michael@0: michael@0: /* michael@0: * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. michael@0: * All rights reserved. michael@0: * michael@0: * Redistribution and use in source and binary forms, with or without michael@0: * modification, are permitted provided that the following conditions michael@0: * are met: michael@0: * 1. Redistributions of source code must retain the above copyright michael@0: * notice, this list of conditions and the following disclaimer. michael@0: * 2. Redistributions in binary form must reproduce the above copyright michael@0: * notice, this list of conditions and the following disclaimer in the michael@0: * documentation and/or other materials provided with the distribution. michael@0: * 3. Neither the name of the project nor the names of its contributors michael@0: * may be used to endorse or promote products derived from this software michael@0: * without specific prior written permission. michael@0: * michael@0: * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND michael@0: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE michael@0: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE michael@0: * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE michael@0: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL michael@0: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS michael@0: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) michael@0: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT michael@0: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY michael@0: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF michael@0: * SUCH DAMAGE. michael@0: */ michael@0: michael@0: /* michael@0: * This version of getaddrinfo.c is derived from Android 2.3 "Gingerbread", michael@0: * which contains uncredited changes by Android/Google developers. It has michael@0: * been modified in 2011 for use in the Android build of Mozilla Firefox by michael@0: * Mozilla contributors (including Michael Edwards , michael@0: * and Steve Workman ). michael@0: * These changes are offered under the same license as the original NetBSD michael@0: * file, whose copyright and license are unchanged above. michael@0: */ michael@0: #define ANDROID_CHANGES 1 michael@0: #define INET6 1 michael@0: michael@0: /* michael@0: * Issues to be discussed: michael@0: * - Return values. There are nonstandard return values defined and used michael@0: * in the source code. This is because RFC2553 is silent about which error michael@0: * code must be returned for which situation. michael@0: * - IPv4 classful (shortened) form. RFC2553 is silent about it. XNET 5.2 michael@0: * says to use inet_aton() to convert IPv4 numeric to binary (alows michael@0: * classful form as a result). michael@0: * current code - disallow classful form for IPv4 (due to use of inet_pton). michael@0: * - freeaddrinfo(NULL). RFC2553 is silent about it. XNET 5.2 says it is michael@0: * invalid. michael@0: * current code - SEGV on freeaddrinfo(NULL) michael@0: * Note: michael@0: * - We use getipnodebyname() just for thread-safeness. There's no intent michael@0: * to let it do PF_UNSPEC (actually we never pass PF_UNSPEC to michael@0: * getipnodebyname(). michael@0: * - MOZILLA: Thread safeness for pre-Honeycomb Android versions implemented by michael@0: * way of open/gets/close and mmap rather than fopen/fgets/fclose. Affects michael@0: * _files_getaddrinfo for hosts file. Note: Honeycomb and later versions use michael@0: * a thread-safe stdio, so for those versions normal Bionic libc getaddrinfo michael@0: * is used. michael@0: * - The code filters out AFs that are not supported by the kernel, michael@0: * when globbing NULL hostname (to loopback, or wildcard). Is it the right michael@0: * thing to do? What is the relationship with post-RFC2553 AI_ADDRCONFIG michael@0: * in ai_flags? michael@0: * - (post-2553) semantics of AI_ADDRCONFIG itself is too vague. michael@0: * (1) what should we do against numeric hostname (2) what should we do michael@0: * against NULL hostname (3) what is AI_ADDRCONFIG itself. AF not ready? michael@0: * non-loopback address configured? global address configured? michael@0: * - To avoid search order issue, we have a big amount of code duplicate michael@0: * from gethnamaddr.c and some other places. The issues that there's no michael@0: * lower layer function to lookup "IPv4 or IPv6" record. Calling michael@0: * gethostbyname2 from getaddrinfo will end up in wrong search order, as michael@0: * follows: michael@0: * - The code makes use of following calls when asked to resolver with michael@0: * ai_family = PF_UNSPEC: michael@0: * getipnodebyname(host, AF_INET6); michael@0: * getipnodebyname(host, AF_INET); michael@0: * This will result in the following queries if the node is configure to michael@0: * prefer /etc/hosts than DNS: michael@0: * lookup /etc/hosts for IPv6 address michael@0: * lookup DNS for IPv6 address michael@0: * lookup /etc/hosts for IPv4 address michael@0: * lookup DNS for IPv4 address michael@0: * which may not meet people's requirement. michael@0: * The right thing to happen is to have underlying layer which does michael@0: * PF_UNSPEC lookup (lookup both) and return chain of addrinfos. michael@0: * This would result in a bit of code duplicate with _dns_ghbyname() and michael@0: * friends. michael@0: */ michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include "arpa_nameser.h" michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include "resolv_private.h" michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: #include michael@0: #include michael@0: #include "nsswitch.h" michael@0: michael@0: #ifdef MOZ_GETADDRINFO_LOG_VERBOSE michael@0: #include michael@0: #endif michael@0: michael@0: #ifdef ANDROID_CHANGES michael@0: #include michael@0: #endif /* ANDROID_CHANGES */ michael@0: michael@0: typedef struct _pseudo_FILE { michael@0: int fd; michael@0: off_t maplen; michael@0: void* mapping; michael@0: off_t offset; michael@0: } _pseudo_FILE; michael@0: michael@0: #define _PSEUDO_FILE_INITIALIZER { -1, 0, MAP_FAILED, 0 } michael@0: michael@0: static void michael@0: _pseudo_fclose(_pseudo_FILE * __restrict__ fp) michael@0: { michael@0: assert(fp); michael@0: fp->offset = 0; michael@0: if (fp->mapping != MAP_FAILED) { michael@0: (void) munmap(fp->mapping, fp->maplen); michael@0: fp->mapping = MAP_FAILED; michael@0: } michael@0: fp->maplen = 0; michael@0: if (fp->fd != -1) { michael@0: (void) close(fp->fd); michael@0: fp->fd = -1; michael@0: } michael@0: } michael@0: michael@0: static _pseudo_FILE * michael@0: _pseudo_fopen_r(_pseudo_FILE * __restrict__ fp, const char* fname) michael@0: { michael@0: struct stat statbuf; michael@0: assert(fp); michael@0: fp->fd = open(fname, O_RDONLY); michael@0: if (fp->fd < 0) { michael@0: fp->fd = -1; michael@0: return NULL; michael@0: } michael@0: if ((0 != fstat(fp->fd, &statbuf)) || (statbuf.st_size <= 0)) { michael@0: close(fp->fd); michael@0: fp->fd = -1; michael@0: return NULL; michael@0: } michael@0: fp->maplen = statbuf.st_size; michael@0: fp->mapping = mmap(NULL, fp->maplen, PROT_READ, MAP_PRIVATE, fp->fd, 0); michael@0: if (fp->mapping == MAP_FAILED) { michael@0: close(fp->fd); michael@0: fp->fd = -1; michael@0: return NULL; michael@0: } michael@0: fp->offset = 0; michael@0: return fp; michael@0: } michael@0: michael@0: static void michael@0: _pseudo_rewind(_pseudo_FILE * __restrict__ fp) michael@0: { michael@0: assert(fp); michael@0: fp->offset = 0; michael@0: } michael@0: michael@0: static char* michael@0: _pseudo_fgets(char* buf, int bufsize, _pseudo_FILE * __restrict__ fp) michael@0: { michael@0: char* current; michael@0: char* endp; michael@0: int maxcopy; michael@0: assert(fp); michael@0: maxcopy = fp->maplen - fp->offset; michael@0: if (fp->mapping == MAP_FAILED) michael@0: return NULL; michael@0: if (maxcopy > bufsize - 1) michael@0: maxcopy = bufsize - 1; michael@0: if (maxcopy <= 0) michael@0: return NULL; michael@0: current = ((char*) fp->mapping) + fp->offset; michael@0: endp = memccpy(buf, current, '\n', maxcopy); michael@0: if (endp) michael@0: maxcopy = endp - buf; michael@0: buf[maxcopy] = '\0'; michael@0: fp->offset += maxcopy; michael@0: return buf; michael@0: } michael@0: michael@0: typedef union sockaddr_union { michael@0: struct sockaddr generic; michael@0: struct sockaddr_in in; michael@0: struct sockaddr_in6 in6; michael@0: } sockaddr_union; michael@0: michael@0: #define SUCCESS 0 michael@0: #define ANY 0 michael@0: #define YES 1 michael@0: #define NO 0 michael@0: michael@0: static const char in_addrany[] = { 0, 0, 0, 0 }; michael@0: static const char in_loopback[] = { 127, 0, 0, 1 }; michael@0: #ifdef INET6 michael@0: static const char in6_addrany[] = { michael@0: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 michael@0: }; michael@0: static const char in6_loopback[] = { michael@0: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 michael@0: }; michael@0: #endif michael@0: michael@0: static const struct afd { michael@0: int a_af; michael@0: int a_addrlen; michael@0: int a_socklen; michael@0: int a_off; michael@0: const char *a_addrany; michael@0: const char *a_loopback; michael@0: int a_scoped; michael@0: } afdl [] = { michael@0: #ifdef INET6 michael@0: {PF_INET6, sizeof(struct in6_addr), michael@0: sizeof(struct sockaddr_in6), michael@0: offsetof(struct sockaddr_in6, sin6_addr), michael@0: in6_addrany, in6_loopback, 1}, michael@0: #endif michael@0: {PF_INET, sizeof(struct in_addr), michael@0: sizeof(struct sockaddr_in), michael@0: offsetof(struct sockaddr_in, sin_addr), michael@0: in_addrany, in_loopback, 0}, michael@0: {0, 0, 0, 0, NULL, NULL, 0}, michael@0: }; michael@0: michael@0: struct explore { michael@0: int e_af; michael@0: int e_socktype; michael@0: int e_protocol; michael@0: const char *e_protostr; michael@0: int e_wild; michael@0: #define WILD_AF(ex) ((ex)->e_wild & 0x01) michael@0: #define WILD_SOCKTYPE(ex) ((ex)->e_wild & 0x02) michael@0: #define WILD_PROTOCOL(ex) ((ex)->e_wild & 0x04) michael@0: }; michael@0: michael@0: static const struct explore explore[] = { michael@0: #if 0 michael@0: { PF_LOCAL, 0, ANY, ANY, NULL, 0x01 }, michael@0: #endif michael@0: #ifdef INET6 michael@0: { PF_INET6, SOCK_DGRAM, IPPROTO_UDP, "udp", 0x07 }, michael@0: { PF_INET6, SOCK_STREAM, IPPROTO_TCP, "tcp", 0x07 }, michael@0: { PF_INET6, SOCK_RAW, ANY, NULL, 0x05 }, michael@0: #endif michael@0: { PF_INET, SOCK_DGRAM, IPPROTO_UDP, "udp", 0x07 }, michael@0: { PF_INET, SOCK_STREAM, IPPROTO_TCP, "tcp", 0x07 }, michael@0: { PF_INET, SOCK_RAW, ANY, NULL, 0x05 }, michael@0: { PF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, "udp", 0x07 }, michael@0: { PF_UNSPEC, SOCK_STREAM, IPPROTO_TCP, "tcp", 0x07 }, michael@0: { PF_UNSPEC, SOCK_RAW, ANY, NULL, 0x05 }, michael@0: { -1, 0, 0, NULL, 0 }, michael@0: }; michael@0: michael@0: #ifdef INET6 michael@0: #define PTON_MAX 16 michael@0: #else michael@0: #define PTON_MAX 4 michael@0: #endif michael@0: michael@0: static const ns_src default_dns_files[] = { michael@0: { NSSRC_FILES, NS_SUCCESS }, michael@0: { NSSRC_DNS, NS_SUCCESS }, michael@0: { 0, 0 } michael@0: }; michael@0: michael@0: #define MAXPACKET (64*1024) michael@0: michael@0: typedef union { michael@0: HEADER hdr; michael@0: u_char buf[MAXPACKET]; michael@0: } querybuf; michael@0: michael@0: struct res_target { michael@0: struct res_target *next; michael@0: const char *name; /* domain name */ michael@0: int qclass, qtype; /* class and type of query */ michael@0: u_char *answer; /* buffer to put answer */ michael@0: int anslen; /* size of answer buffer */ michael@0: int n; /* result length */ michael@0: }; michael@0: michael@0: static int str2number(const char *); michael@0: static int explore_fqdn(const struct addrinfo *, const char *, michael@0: const char *, struct addrinfo **); michael@0: static int explore_null(const struct addrinfo *, michael@0: const char *, struct addrinfo **); michael@0: static int explore_numeric(const struct addrinfo *, const char *, michael@0: const char *, struct addrinfo **, const char *); michael@0: static int explore_numeric_scope(const struct addrinfo *, const char *, michael@0: const char *, struct addrinfo **); michael@0: static int get_canonname(const struct addrinfo *, michael@0: struct addrinfo *, const char *); michael@0: static struct addrinfo *get_ai(const struct addrinfo *, michael@0: const struct afd *, const char *); michael@0: static int get_portmatch(const struct addrinfo *, const char *); michael@0: static int get_port(const struct addrinfo *, const char *, int); michael@0: static const struct afd *find_afd(int); michael@0: #ifdef INET6 michael@0: static int ip6_str2scopeid(char *, struct sockaddr_in6 *, u_int32_t *); michael@0: #endif michael@0: michael@0: static struct addrinfo *getanswer(const querybuf *, int, const char *, int, michael@0: const struct addrinfo *); michael@0: static int _dns_getaddrinfo(void *, void *, va_list); michael@0: static void _sethtent(_pseudo_FILE * __restrict__); michael@0: static void _endhtent(_pseudo_FILE * __restrict__); michael@0: static struct addrinfo *_gethtent(_pseudo_FILE * __restrict__, const char *, michael@0: const struct addrinfo *); michael@0: static int _files_getaddrinfo(void *, void *, va_list); michael@0: michael@0: static int res_queryN(const char *, struct res_target *, res_state); michael@0: static int res_searchN(const char *, struct res_target *, res_state); michael@0: static int res_querydomainN(const char *, const char *, michael@0: struct res_target *, res_state); michael@0: michael@0: static const char * const ai_errlist[] = { michael@0: "Success", michael@0: "Address family for hostname not supported", /* EAI_ADDRFAMILY */ michael@0: "Temporary failure in name resolution", /* EAI_AGAIN */ michael@0: "Invalid value for ai_flags", /* EAI_BADFLAGS */ michael@0: "Non-recoverable failure in name resolution", /* EAI_FAIL */ michael@0: "ai_family not supported", /* EAI_FAMILY */ michael@0: "Memory allocation failure", /* EAI_MEMORY */ michael@0: "No address associated with hostname", /* EAI_NODATA */ michael@0: "hostname nor servname provided, or not known", /* EAI_NONAME */ michael@0: "servname not supported for ai_socktype", /* EAI_SERVICE */ michael@0: "ai_socktype not supported", /* EAI_SOCKTYPE */ michael@0: "System error returned in errno", /* EAI_SYSTEM */ michael@0: "Invalid value for hints", /* EAI_BADHINTS */ michael@0: "Resolved protocol is unknown", /* EAI_PROTOCOL */ michael@0: "Argument buffer overflow", /* EAI_OVERFLOW */ michael@0: "Unknown error", /* EAI_MAX */ michael@0: }; michael@0: michael@0: /* XXX macros that make external reference is BAD. */ michael@0: michael@0: #define GET_AI(ai, afd, addr) \ michael@0: do { \ michael@0: /* external reference: pai, error, and label free */ \ michael@0: (ai) = get_ai(pai, (afd), (addr)); \ michael@0: if ((ai) == NULL) { \ michael@0: error = EAI_MEMORY; \ michael@0: goto free; \ michael@0: } \ michael@0: } while (/*CONSTCOND*/0) michael@0: michael@0: #define GET_PORT(ai, serv) \ michael@0: do { \ michael@0: /* external reference: error and label free */ \ michael@0: error = get_port((ai), (serv), 0); \ michael@0: if (error != 0) \ michael@0: goto free; \ michael@0: } while (/*CONSTCOND*/0) michael@0: michael@0: #define GET_CANONNAME(ai, str) \ michael@0: do { \ michael@0: /* external reference: pai, error and label free */ \ michael@0: error = get_canonname(pai, (ai), (str)); \ michael@0: if (error != 0) \ michael@0: goto free; \ michael@0: } while (/*CONSTCOND*/0) michael@0: michael@0: #define ERR(err) \ michael@0: do { \ michael@0: /* external reference: error, and label bad */ \ michael@0: error = (err); \ michael@0: goto bad; \ michael@0: /*NOTREACHED*/ \ michael@0: } while (/*CONSTCOND*/0) michael@0: michael@0: #define MATCH_FAMILY(x, y, w) \ michael@0: ((x) == (y) || (/*CONSTCOND*/(w) && ((x) == PF_UNSPEC || \ michael@0: (y) == PF_UNSPEC))) michael@0: #define MATCH(x, y, w) \ michael@0: ((x) == (y) || (/*CONSTCOND*/(w) && ((x) == ANY || (y) == ANY))) michael@0: michael@0: #pragma GCC visibility push(default) michael@0: michael@0: extern const char * michael@0: __wrap_gai_strerror(int ecode); michael@0: extern void michael@0: __wrap_freeaddrinfo(struct addrinfo *ai); michael@0: extern int michael@0: __wrap_getaddrinfo(const char *hostname, const char *servname, michael@0: const struct addrinfo *hints, struct addrinfo **res); michael@0: michael@0: int android_sdk_version; michael@0: michael@0: #pragma GCC visibility pop michael@0: michael@0: int android_sdk_version = -1; michael@0: michael@0: static int honeycomb_or_later() michael@0: { michael@0: #ifdef MOZ_GETADDRINFO_LOG_VERBOSE michael@0: __android_log_print(ANDROID_LOG_INFO, "getaddrinfo", michael@0: "I am%s Honeycomb\n", michael@0: (android_sdk_version >= 11) ? "" : " not"); michael@0: #endif michael@0: return android_sdk_version >= 11; michael@0: } michael@0: michael@0: const char * michael@0: __wrap_gai_strerror(int ecode) michael@0: { michael@0: if (honeycomb_or_later()) michael@0: return gai_strerror(ecode); michael@0: if (ecode < 0 || ecode > EAI_MAX) michael@0: ecode = EAI_MAX; michael@0: return ai_errlist[ecode]; michael@0: } michael@0: michael@0: void michael@0: __wrap_freeaddrinfo(struct addrinfo *ai) michael@0: { michael@0: struct addrinfo *next; michael@0: michael@0: if (honeycomb_or_later()) { michael@0: freeaddrinfo(ai); michael@0: return; michael@0: } michael@0: michael@0: assert(ai != NULL); michael@0: michael@0: do { michael@0: next = ai->ai_next; michael@0: if (ai->ai_canonname) michael@0: free(ai->ai_canonname); michael@0: /* no need to free(ai->ai_addr) */ michael@0: free(ai); michael@0: ai = next; michael@0: } while (ai); michael@0: } michael@0: michael@0: static int michael@0: str2number(const char *p) michael@0: { michael@0: char *ep; michael@0: unsigned long v; michael@0: michael@0: assert(p != NULL); michael@0: michael@0: if (*p == '\0') michael@0: return -1; michael@0: ep = NULL; michael@0: errno = 0; michael@0: v = strtoul(p, &ep, 10); michael@0: if (errno == 0 && ep && *ep == '\0' && v <= UINT_MAX) michael@0: return v; michael@0: else michael@0: return -1; michael@0: } michael@0: michael@0: /* michael@0: * Connect a UDP socket to a given unicast address. This will cause no network michael@0: * traffic, but will fail fast if the system has no or limited reachability to michael@0: * the destination (e.g., no IPv4 address, no IPv6 default route, ...). michael@0: */ michael@0: static int michael@0: _test_connect(int pf, struct sockaddr *addr, size_t addrlen) { michael@0: int s = socket(pf, SOCK_DGRAM, IPPROTO_UDP); michael@0: if (s < 0) michael@0: return 0; michael@0: int ret; michael@0: do { michael@0: ret = connect(s, addr, addrlen); michael@0: } while (ret < 0 && errno == EINTR); michael@0: int success = (ret == 0); michael@0: do { michael@0: ret = close(s); michael@0: } while (ret < 0 && errno == EINTR); michael@0: return success; michael@0: } michael@0: michael@0: /* michael@0: * The following functions determine whether IPv4 or IPv6 connectivity is michael@0: * available in order to implement AI_ADDRCONFIG. michael@0: * michael@0: * Strictly speaking, AI_ADDRCONFIG should not look at whether connectivity is michael@0: * available, but whether addresses of the specified family are "configured michael@0: * on the local system". However, bionic doesn't currently support getifaddrs, michael@0: * so checking for connectivity is the next best thing. michael@0: */ michael@0: static int michael@0: _have_ipv6() { michael@0: static const struct sockaddr_in6 sin6_test = { michael@0: .sin6_family = AF_INET6, michael@0: .sin6_addr.s6_addr = { // 2000:: michael@0: 0x20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} michael@0: }; michael@0: sockaddr_union addr = { .in6 = sin6_test }; michael@0: return _test_connect(PF_INET6, &addr.generic, sizeof(addr.in6)); michael@0: } michael@0: michael@0: static int michael@0: _have_ipv4() { michael@0: static const struct sockaddr_in sin_test = { michael@0: .sin_family = AF_INET, michael@0: .sin_addr.s_addr = __constant_htonl(0x08080808L) // 8.8.8.8 michael@0: }; michael@0: sockaddr_union addr = { .in = sin_test }; michael@0: return _test_connect(PF_INET, &addr.generic, sizeof(addr.in)); michael@0: } michael@0: michael@0: int michael@0: __wrap_getaddrinfo(const char *hostname, const char *servname, michael@0: const struct addrinfo *hints, struct addrinfo **res) michael@0: { michael@0: struct addrinfo sentinel; michael@0: struct addrinfo *cur; michael@0: int error = 0; michael@0: struct addrinfo ai; michael@0: struct addrinfo ai0; michael@0: struct addrinfo *pai; michael@0: const struct explore *ex; michael@0: michael@0: if (honeycomb_or_later()) michael@0: return getaddrinfo(hostname, servname, hints, res); michael@0: michael@0: /* hostname is allowed to be NULL */ michael@0: /* servname is allowed to be NULL */ michael@0: /* hints is allowed to be NULL */ michael@0: assert(res != NULL); michael@0: michael@0: memset(&sentinel, 0, sizeof(sentinel)); michael@0: cur = &sentinel; michael@0: pai = &ai; michael@0: pai->ai_flags = 0; michael@0: pai->ai_family = PF_UNSPEC; michael@0: pai->ai_socktype = ANY; michael@0: pai->ai_protocol = ANY; michael@0: pai->ai_addrlen = 0; michael@0: pai->ai_canonname = NULL; michael@0: pai->ai_addr = NULL; michael@0: pai->ai_next = NULL; michael@0: michael@0: if (hostname == NULL && servname == NULL) michael@0: return EAI_NONAME; michael@0: if (hints) { michael@0: /* error check for hints */ michael@0: if (hints->ai_addrlen || hints->ai_canonname || michael@0: hints->ai_addr || hints->ai_next) michael@0: ERR(EAI_BADHINTS); /* xxx */ michael@0: if (hints->ai_flags & ~AI_MASK) michael@0: ERR(EAI_BADFLAGS); michael@0: switch (hints->ai_family) { michael@0: case PF_UNSPEC: michael@0: case PF_INET: michael@0: #ifdef INET6 michael@0: case PF_INET6: michael@0: #endif michael@0: break; michael@0: default: michael@0: ERR(EAI_FAMILY); michael@0: } michael@0: memcpy(pai, hints, sizeof(*pai)); michael@0: michael@0: /* michael@0: * if both socktype/protocol are specified, check if they michael@0: * are meaningful combination. michael@0: */ michael@0: if (pai->ai_socktype != ANY && pai->ai_protocol != ANY) { michael@0: for (ex = explore; ex->e_af >= 0; ex++) { michael@0: if (pai->ai_family != ex->e_af) michael@0: continue; michael@0: if (ex->e_socktype == ANY) michael@0: continue; michael@0: if (ex->e_protocol == ANY) michael@0: continue; michael@0: if (pai->ai_socktype == ex->e_socktype michael@0: && pai->ai_protocol != ex->e_protocol) { michael@0: ERR(EAI_BADHINTS); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: /* michael@0: * check for special cases. (1) numeric servname is disallowed if michael@0: * socktype/protocol are left unspecified. (2) servname is disallowed michael@0: * for raw and other inet{,6} sockets. michael@0: */ michael@0: if (MATCH_FAMILY(pai->ai_family, PF_INET, 1) michael@0: #ifdef PF_INET6 michael@0: || MATCH_FAMILY(pai->ai_family, PF_INET6, 1) michael@0: #endif michael@0: ) { michael@0: ai0 = *pai; /* backup *pai */ michael@0: michael@0: if (pai->ai_family == PF_UNSPEC) { michael@0: #ifdef PF_INET6 michael@0: pai->ai_family = PF_INET6; michael@0: #else michael@0: pai->ai_family = PF_INET; michael@0: #endif michael@0: } michael@0: error = get_portmatch(pai, servname); michael@0: if (error) michael@0: ERR(error); michael@0: michael@0: *pai = ai0; michael@0: } michael@0: michael@0: ai0 = *pai; michael@0: michael@0: /* NULL hostname, or numeric hostname */ michael@0: for (ex = explore; ex->e_af >= 0; ex++) { michael@0: *pai = ai0; michael@0: michael@0: /* PF_UNSPEC entries are prepared for DNS queries only */ michael@0: if (ex->e_af == PF_UNSPEC) michael@0: continue; michael@0: michael@0: if (!MATCH_FAMILY(pai->ai_family, ex->e_af, WILD_AF(ex))) michael@0: continue; michael@0: if (!MATCH(pai->ai_socktype, ex->e_socktype, WILD_SOCKTYPE(ex))) michael@0: continue; michael@0: if (!MATCH(pai->ai_protocol, ex->e_protocol, WILD_PROTOCOL(ex))) michael@0: continue; michael@0: michael@0: if (pai->ai_family == PF_UNSPEC) michael@0: pai->ai_family = ex->e_af; michael@0: if (pai->ai_socktype == ANY && ex->e_socktype != ANY) michael@0: pai->ai_socktype = ex->e_socktype; michael@0: if (pai->ai_protocol == ANY && ex->e_protocol != ANY) michael@0: pai->ai_protocol = ex->e_protocol; michael@0: michael@0: if (hostname == NULL) michael@0: error = explore_null(pai, servname, &cur->ai_next); michael@0: else michael@0: error = explore_numeric_scope(pai, hostname, servname, michael@0: &cur->ai_next); michael@0: michael@0: if (error) michael@0: goto free; michael@0: michael@0: while (cur->ai_next) michael@0: cur = cur->ai_next; michael@0: } michael@0: michael@0: /* michael@0: * XXX michael@0: * If numeric representation of AF1 can be interpreted as FQDN michael@0: * representation of AF2, we need to think again about the code below. michael@0: */ michael@0: if (sentinel.ai_next) michael@0: goto good; michael@0: michael@0: if (hostname == NULL) michael@0: ERR(EAI_NODATA); michael@0: if (pai->ai_flags & AI_NUMERICHOST) michael@0: ERR(EAI_NONAME); michael@0: michael@0: /* michael@0: * hostname as alphabetical name. michael@0: * we would like to prefer AF_INET6 than AF_INET, so we'll make a michael@0: * outer loop by AFs. michael@0: */ michael@0: for (ex = explore; ex->e_af >= 0; ex++) { michael@0: *pai = ai0; michael@0: michael@0: /* require exact match for family field */ michael@0: if (pai->ai_family != ex->e_af) michael@0: continue; michael@0: michael@0: if (!MATCH(pai->ai_socktype, ex->e_socktype, michael@0: WILD_SOCKTYPE(ex))) { michael@0: continue; michael@0: } michael@0: if (!MATCH(pai->ai_protocol, ex->e_protocol, michael@0: WILD_PROTOCOL(ex))) { michael@0: continue; michael@0: } michael@0: michael@0: if (pai->ai_socktype == ANY && ex->e_socktype != ANY) michael@0: pai->ai_socktype = ex->e_socktype; michael@0: if (pai->ai_protocol == ANY && ex->e_protocol != ANY) michael@0: pai->ai_protocol = ex->e_protocol; michael@0: michael@0: error = explore_fqdn(pai, hostname, servname, michael@0: &cur->ai_next); michael@0: michael@0: while (cur && cur->ai_next) michael@0: cur = cur->ai_next; michael@0: } michael@0: michael@0: /* XXX */ michael@0: if (sentinel.ai_next) michael@0: error = 0; michael@0: michael@0: if (error) michael@0: goto free; michael@0: if (error == 0) { michael@0: if (sentinel.ai_next) { michael@0: good: michael@0: *res = sentinel.ai_next; michael@0: return SUCCESS; michael@0: } else michael@0: error = EAI_FAIL; michael@0: } michael@0: free: michael@0: bad: michael@0: if (sentinel.ai_next) michael@0: __wrap_freeaddrinfo(sentinel.ai_next); michael@0: *res = NULL; michael@0: return error; michael@0: } michael@0: michael@0: /* michael@0: * FQDN hostname, DNS lookup michael@0: */ michael@0: static int michael@0: explore_fqdn(const struct addrinfo *pai, const char *hostname, michael@0: const char *servname, struct addrinfo **res) michael@0: { michael@0: struct addrinfo *result; michael@0: struct addrinfo *cur; michael@0: int error = 0; michael@0: static const ns_dtab dtab[] = { michael@0: NS_FILES_CB(_files_getaddrinfo, NULL) michael@0: { NSSRC_DNS, _dns_getaddrinfo, NULL }, /* force -DHESIOD */ michael@0: NS_NIS_CB(_yp_getaddrinfo, NULL) michael@0: { 0, 0, 0 } michael@0: }; michael@0: michael@0: assert(pai != NULL); michael@0: /* hostname may be NULL */ michael@0: /* servname may be NULL */ michael@0: assert(res != NULL); michael@0: michael@0: result = NULL; michael@0: michael@0: /* michael@0: * if the servname does not match socktype/protocol, ignore it. michael@0: */ michael@0: if (get_portmatch(pai, servname) != 0) michael@0: return 0; michael@0: michael@0: switch (nsdispatch(&result, dtab, NSDB_HOSTS, "getaddrinfo", michael@0: default_dns_files, hostname, pai)) { michael@0: case NS_TRYAGAIN: michael@0: error = EAI_AGAIN; michael@0: goto free; michael@0: case NS_UNAVAIL: michael@0: error = EAI_FAIL; michael@0: goto free; michael@0: case NS_NOTFOUND: michael@0: error = EAI_NODATA; michael@0: goto free; michael@0: case NS_SUCCESS: michael@0: error = 0; michael@0: for (cur = result; cur; cur = cur->ai_next) { michael@0: GET_PORT(cur, servname); michael@0: /* canonname should be filled already */ michael@0: } michael@0: break; michael@0: } michael@0: michael@0: *res = result; michael@0: michael@0: return 0; michael@0: michael@0: free: michael@0: if (result) michael@0: __wrap_freeaddrinfo(result); michael@0: return error; michael@0: } michael@0: michael@0: /* michael@0: * hostname == NULL. michael@0: * passive socket -> anyaddr (0.0.0.0 or ::) michael@0: * non-passive socket -> localhost (127.0.0.1 or ::1) michael@0: */ michael@0: static int michael@0: explore_null(const struct addrinfo *pai, const char *servname, michael@0: struct addrinfo **res) michael@0: { michael@0: int s; michael@0: const struct afd *afd; michael@0: struct addrinfo *cur; michael@0: struct addrinfo sentinel; michael@0: int error; michael@0: michael@0: assert(pai != NULL); michael@0: /* servname may be NULL */ michael@0: assert(res != NULL); michael@0: michael@0: *res = NULL; michael@0: sentinel.ai_next = NULL; michael@0: cur = &sentinel; michael@0: michael@0: /* michael@0: * filter out AFs that are not supported by the kernel michael@0: * XXX errno? michael@0: */ michael@0: s = socket(pai->ai_family, SOCK_DGRAM, 0); michael@0: if (s < 0) { michael@0: if (errno != EMFILE) michael@0: return 0; michael@0: } else michael@0: close(s); michael@0: michael@0: /* michael@0: * if the servname does not match socktype/protocol, ignore it. michael@0: */ michael@0: if (get_portmatch(pai, servname) != 0) michael@0: return 0; michael@0: michael@0: afd = find_afd(pai->ai_family); michael@0: if (afd == NULL) michael@0: return 0; michael@0: michael@0: if (pai->ai_flags & AI_PASSIVE) { michael@0: GET_AI(cur->ai_next, afd, afd->a_addrany); michael@0: /* xxx meaningless? michael@0: * GET_CANONNAME(cur->ai_next, "anyaddr"); michael@0: */ michael@0: GET_PORT(cur->ai_next, servname); michael@0: } else { michael@0: GET_AI(cur->ai_next, afd, afd->a_loopback); michael@0: /* xxx meaningless? michael@0: * GET_CANONNAME(cur->ai_next, "localhost"); michael@0: */ michael@0: GET_PORT(cur->ai_next, servname); michael@0: } michael@0: cur = cur->ai_next; michael@0: michael@0: *res = sentinel.ai_next; michael@0: return 0; michael@0: michael@0: free: michael@0: if (sentinel.ai_next) michael@0: __wrap_freeaddrinfo(sentinel.ai_next); michael@0: return error; michael@0: } michael@0: michael@0: /* michael@0: * numeric hostname michael@0: */ michael@0: static int michael@0: explore_numeric(const struct addrinfo *pai, const char *hostname, michael@0: const char *servname, struct addrinfo **res, const char *canonname) michael@0: { michael@0: const struct afd *afd; michael@0: struct addrinfo *cur; michael@0: struct addrinfo sentinel; michael@0: int error; michael@0: char pton[PTON_MAX]; michael@0: michael@0: assert(pai != NULL); michael@0: /* hostname may be NULL */ michael@0: /* servname may be NULL */ michael@0: assert(res != NULL); michael@0: michael@0: *res = NULL; michael@0: sentinel.ai_next = NULL; michael@0: cur = &sentinel; michael@0: michael@0: /* michael@0: * if the servname does not match socktype/protocol, ignore it. michael@0: */ michael@0: if (get_portmatch(pai, servname) != 0) michael@0: return 0; michael@0: michael@0: afd = find_afd(pai->ai_family); michael@0: if (afd == NULL) michael@0: return 0; michael@0: michael@0: switch (afd->a_af) { michael@0: #if 0 /*X/Open spec*/ michael@0: case AF_INET: michael@0: if (inet_aton(hostname, (struct in_addr *)pton) == 1) { michael@0: if (pai->ai_family == afd->a_af || michael@0: pai->ai_family == PF_UNSPEC /*?*/) { michael@0: GET_AI(cur->ai_next, afd, pton); michael@0: GET_PORT(cur->ai_next, servname); michael@0: if ((pai->ai_flags & AI_CANONNAME)) { michael@0: /* michael@0: * Set the numeric address itself as michael@0: * the canonical name, based on a michael@0: * clarification in rfc2553bis-03. michael@0: */ michael@0: GET_CANONNAME(cur->ai_next, canonname); michael@0: } michael@0: while (cur && cur->ai_next) michael@0: cur = cur->ai_next; michael@0: } else michael@0: ERR(EAI_FAMILY); /*xxx*/ michael@0: } michael@0: break; michael@0: #endif michael@0: default: michael@0: if (inet_pton(afd->a_af, hostname, pton) == 1) { michael@0: if (pai->ai_family == afd->a_af || michael@0: pai->ai_family == PF_UNSPEC /*?*/) { michael@0: GET_AI(cur->ai_next, afd, pton); michael@0: GET_PORT(cur->ai_next, servname); michael@0: if ((pai->ai_flags & AI_CANONNAME)) { michael@0: /* michael@0: * Set the numeric address itself as michael@0: * the canonical name, based on a michael@0: * clarification in rfc2553bis-03. michael@0: */ michael@0: GET_CANONNAME(cur->ai_next, canonname); michael@0: } michael@0: while (cur->ai_next) michael@0: cur = cur->ai_next; michael@0: } else michael@0: ERR(EAI_FAMILY); /*xxx*/ michael@0: } michael@0: break; michael@0: } michael@0: michael@0: *res = sentinel.ai_next; michael@0: return 0; michael@0: michael@0: free: michael@0: bad: michael@0: if (sentinel.ai_next) michael@0: __wrap_freeaddrinfo(sentinel.ai_next); michael@0: return error; michael@0: } michael@0: michael@0: /* michael@0: * numeric hostname with scope michael@0: */ michael@0: static int michael@0: explore_numeric_scope(const struct addrinfo *pai, const char *hostname, michael@0: const char *servname, struct addrinfo **res) michael@0: { michael@0: #if !defined(SCOPE_DELIMITER) || !defined(INET6) michael@0: return explore_numeric(pai, hostname, servname, res, hostname); michael@0: #else michael@0: const struct afd *afd; michael@0: struct addrinfo *cur; michael@0: int error; michael@0: char *cp, *hostname2 = NULL, *scope, *addr; michael@0: struct sockaddr_in6 *sin6; michael@0: michael@0: assert(pai != NULL); michael@0: /* hostname may be NULL */ michael@0: /* servname may be NULL */ michael@0: assert(res != NULL); michael@0: michael@0: /* michael@0: * if the servname does not match socktype/protocol, ignore it. michael@0: */ michael@0: if (get_portmatch(pai, servname) != 0) michael@0: return 0; michael@0: michael@0: afd = find_afd(pai->ai_family); michael@0: if (afd == NULL) michael@0: return 0; michael@0: michael@0: if (!afd->a_scoped) michael@0: return explore_numeric(pai, hostname, servname, res, hostname); michael@0: michael@0: cp = strchr(hostname, SCOPE_DELIMITER); michael@0: if (cp == NULL) michael@0: return explore_numeric(pai, hostname, servname, res, hostname); michael@0: michael@0: /* michael@0: * Handle special case of michael@0: */ michael@0: hostname2 = strdup(hostname); michael@0: if (hostname2 == NULL) michael@0: return EAI_MEMORY; michael@0: /* terminate at the delimiter */ michael@0: hostname2[cp - hostname] = '\0'; michael@0: addr = hostname2; michael@0: scope = cp + 1; michael@0: michael@0: error = explore_numeric(pai, addr, servname, res, hostname); michael@0: if (error == 0) { michael@0: u_int32_t scopeid; michael@0: michael@0: for (cur = *res; cur; cur = cur->ai_next) { michael@0: if (cur->ai_family != AF_INET6) michael@0: continue; michael@0: sin6 = (struct sockaddr_in6 *)(void *)cur->ai_addr; michael@0: if (ip6_str2scopeid(scope, sin6, &scopeid) == -1) { michael@0: free(hostname2); michael@0: return(EAI_NODATA); /* XXX: is return OK? */ michael@0: } michael@0: sin6->sin6_scope_id = scopeid; michael@0: } michael@0: } michael@0: michael@0: free(hostname2); michael@0: michael@0: return error; michael@0: #endif michael@0: } michael@0: michael@0: static int michael@0: get_canonname(const struct addrinfo *pai, struct addrinfo *ai, const char *str) michael@0: { michael@0: michael@0: assert(pai != NULL); michael@0: assert(ai != NULL); michael@0: assert(str != NULL); michael@0: michael@0: if ((pai->ai_flags & AI_CANONNAME) != 0) { michael@0: ai->ai_canonname = strdup(str); michael@0: if (ai->ai_canonname == NULL) michael@0: return EAI_MEMORY; michael@0: } michael@0: return 0; michael@0: } michael@0: michael@0: static struct addrinfo * michael@0: get_ai(const struct addrinfo *pai, const struct afd *afd, const char *addr) michael@0: { michael@0: char *p; michael@0: struct addrinfo *ai; michael@0: michael@0: assert(pai != NULL); michael@0: assert(afd != NULL); michael@0: assert(addr != NULL); michael@0: michael@0: ai = (struct addrinfo *)malloc(sizeof(struct addrinfo) michael@0: + (afd->a_socklen)); michael@0: if (ai == NULL) michael@0: return NULL; michael@0: michael@0: memcpy(ai, pai, sizeof(struct addrinfo)); michael@0: ai->ai_addr = (struct sockaddr *)(void *)(ai + 1); michael@0: memset(ai->ai_addr, 0, (size_t)afd->a_socklen); michael@0: michael@0: #ifdef HAVE_SA_LEN michael@0: ai->ai_addr->sa_len = afd->a_socklen; michael@0: #endif michael@0: michael@0: ai->ai_addrlen = afd->a_socklen; michael@0: #if defined (__alpha__) || (defined(__i386__) && defined(_LP64)) || defined(__sparc64__) michael@0: ai->__ai_pad0 = 0; michael@0: #endif michael@0: ai->ai_addr->sa_family = ai->ai_family = afd->a_af; michael@0: p = (char *)(void *)(ai->ai_addr); michael@0: memcpy(p + afd->a_off, addr, (size_t)afd->a_addrlen); michael@0: return ai; michael@0: } michael@0: michael@0: static int michael@0: get_portmatch(const struct addrinfo *ai, const char *servname) michael@0: { michael@0: michael@0: assert(ai != NULL); michael@0: /* servname may be NULL */ michael@0: michael@0: return get_port(ai, servname, 1); michael@0: } michael@0: michael@0: static int michael@0: get_port(const struct addrinfo *ai, const char *servname, int matchonly) michael@0: { michael@0: const char *proto; michael@0: struct servent *sp; michael@0: int port; michael@0: int allownumeric; michael@0: michael@0: assert(ai != NULL); michael@0: /* servname may be NULL */ michael@0: michael@0: if (servname == NULL) michael@0: return 0; michael@0: switch (ai->ai_family) { michael@0: case AF_INET: michael@0: #ifdef AF_INET6 michael@0: case AF_INET6: michael@0: #endif michael@0: break; michael@0: default: michael@0: return 0; michael@0: } michael@0: michael@0: switch (ai->ai_socktype) { michael@0: case SOCK_RAW: michael@0: return EAI_SERVICE; michael@0: case SOCK_DGRAM: michael@0: case SOCK_STREAM: michael@0: allownumeric = 1; michael@0: break; michael@0: case ANY: michael@0: #if 1 /* ANDROID-SPECIFIC CHANGE TO MATCH GLIBC */ michael@0: allownumeric = 1; michael@0: #else michael@0: allownumeric = 0; michael@0: #endif michael@0: break; michael@0: default: michael@0: return EAI_SOCKTYPE; michael@0: } michael@0: michael@0: port = str2number(servname); michael@0: if (port >= 0) { michael@0: if (!allownumeric) michael@0: return EAI_SERVICE; michael@0: if (port < 0 || port > 65535) michael@0: return EAI_SERVICE; michael@0: port = htons(port); michael@0: } else { michael@0: if (ai->ai_flags & AI_NUMERICSERV) michael@0: return EAI_NONAME; michael@0: michael@0: switch (ai->ai_socktype) { michael@0: case SOCK_DGRAM: michael@0: proto = "udp"; michael@0: break; michael@0: case SOCK_STREAM: michael@0: proto = "tcp"; michael@0: break; michael@0: default: michael@0: proto = NULL; michael@0: break; michael@0: } michael@0: michael@0: if ((sp = getservbyname(servname, proto)) == NULL) michael@0: return EAI_SERVICE; michael@0: port = sp->s_port; michael@0: } michael@0: michael@0: if (!matchonly) { michael@0: switch (ai->ai_family) { michael@0: case AF_INET: michael@0: ((struct sockaddr_in *)(void *) michael@0: ai->ai_addr)->sin_port = port; michael@0: break; michael@0: #ifdef INET6 michael@0: case AF_INET6: michael@0: ((struct sockaddr_in6 *)(void *) michael@0: ai->ai_addr)->sin6_port = port; michael@0: break; michael@0: #endif michael@0: } michael@0: } michael@0: michael@0: return 0; michael@0: } michael@0: michael@0: static const struct afd * michael@0: find_afd(int af) michael@0: { michael@0: const struct afd *afd; michael@0: michael@0: if (af == PF_UNSPEC) michael@0: return NULL; michael@0: for (afd = afdl; afd->a_af; afd++) { michael@0: if (afd->a_af == af) michael@0: return afd; michael@0: } michael@0: return NULL; michael@0: } michael@0: michael@0: #ifdef INET6 michael@0: /* convert a string to a scope identifier. XXX: IPv6 specific */ michael@0: static int michael@0: ip6_str2scopeid(char *scope, struct sockaddr_in6 *sin6, u_int32_t *scopeid) michael@0: { michael@0: u_long lscopeid; michael@0: struct in6_addr *a6; michael@0: char *ep; michael@0: michael@0: assert(scope != NULL); michael@0: assert(sin6 != NULL); michael@0: assert(scopeid != NULL); michael@0: michael@0: a6 = &sin6->sin6_addr; michael@0: michael@0: /* empty scopeid portion is invalid */ michael@0: if (*scope == '\0') michael@0: return -1; michael@0: michael@0: if (IN6_IS_ADDR_LINKLOCAL(a6) || IN6_IS_ADDR_MC_LINKLOCAL(a6)) { michael@0: /* michael@0: * We currently assume a one-to-one mapping between links michael@0: * and interfaces, so we simply use interface indices for michael@0: * like-local scopes. michael@0: */ michael@0: *scopeid = if_nametoindex(scope); michael@0: if (*scopeid == 0) michael@0: goto trynumeric; michael@0: return 0; michael@0: } michael@0: michael@0: /* still unclear about literal, allow numeric only - placeholder */ michael@0: if (IN6_IS_ADDR_SITELOCAL(a6) || IN6_IS_ADDR_MC_SITELOCAL(a6)) michael@0: goto trynumeric; michael@0: if (IN6_IS_ADDR_MC_ORGLOCAL(a6)) michael@0: goto trynumeric; michael@0: else michael@0: goto trynumeric; /* global */ michael@0: michael@0: /* try to convert to a numeric id as a last resort */ michael@0: trynumeric: michael@0: errno = 0; michael@0: lscopeid = strtoul(scope, &ep, 10); michael@0: *scopeid = (u_int32_t)(lscopeid & 0xffffffffUL); michael@0: if (errno == 0 && ep && *ep == '\0' && *scopeid == lscopeid) michael@0: return 0; michael@0: else michael@0: return -1; michael@0: } michael@0: #endif michael@0: michael@0: /* code duplicate with gethnamaddr.c */ michael@0: michael@0: static const char AskedForGot[] = michael@0: "gethostby*.getanswer: asked for \"%s\", got \"%s\""; michael@0: michael@0: static struct addrinfo * michael@0: getanswer(const querybuf *answer, int anslen, const char *qname, int qtype, michael@0: const struct addrinfo *pai) michael@0: { michael@0: struct addrinfo sentinel, *cur; michael@0: struct addrinfo ai; michael@0: const struct afd *afd; michael@0: char *canonname; michael@0: const HEADER *hp; michael@0: const u_char *cp; michael@0: int n; michael@0: const u_char *eom; michael@0: char *bp, *ep; michael@0: int type, class, ancount, qdcount; michael@0: int haveanswer, had_error; michael@0: char tbuf[MAXDNAME]; michael@0: int (*name_ok) (const char *); michael@0: char hostbuf[8*1024]; michael@0: michael@0: assert(answer != NULL); michael@0: assert(qname != NULL); michael@0: assert(pai != NULL); michael@0: michael@0: memset(&sentinel, 0, sizeof(sentinel)); michael@0: cur = &sentinel; michael@0: michael@0: canonname = NULL; michael@0: eom = answer->buf + anslen; michael@0: switch (qtype) { michael@0: case T_A: michael@0: case T_AAAA: michael@0: case T_ANY: /*use T_ANY only for T_A/T_AAAA lookup*/ michael@0: name_ok = res_hnok; michael@0: break; michael@0: default: michael@0: return NULL; /* XXX should be abort(); */ michael@0: } michael@0: /* michael@0: * find first satisfactory answer michael@0: */ michael@0: hp = &answer->hdr; michael@0: ancount = ntohs(hp->ancount); michael@0: qdcount = ntohs(hp->qdcount); michael@0: bp = hostbuf; michael@0: ep = hostbuf + sizeof hostbuf; michael@0: cp = answer->buf + HFIXEDSZ; michael@0: if (qdcount != 1) { michael@0: h_errno = NO_RECOVERY; michael@0: return (NULL); michael@0: } michael@0: n = dn_expand(answer->buf, eom, cp, bp, ep - bp); michael@0: if ((n < 0) || !(*name_ok)(bp)) { michael@0: h_errno = NO_RECOVERY; michael@0: return (NULL); michael@0: } michael@0: cp += n + QFIXEDSZ; michael@0: if (qtype == T_A || qtype == T_AAAA || qtype == T_ANY) { michael@0: /* res_send() has already verified that the query name is the michael@0: * same as the one we sent; this just gets the expanded name michael@0: * (i.e., with the succeeding search-domain tacked on). michael@0: */ michael@0: n = strlen(bp) + 1; /* for the \0 */ michael@0: if (n >= MAXHOSTNAMELEN) { michael@0: h_errno = NO_RECOVERY; michael@0: return (NULL); michael@0: } michael@0: canonname = bp; michael@0: bp += n; michael@0: /* The qname can be abbreviated, but h_name is now absolute. */ michael@0: qname = canonname; michael@0: } michael@0: haveanswer = 0; michael@0: had_error = 0; michael@0: while (ancount-- > 0 && cp < eom && !had_error) { michael@0: n = dn_expand(answer->buf, eom, cp, bp, ep - bp); michael@0: if ((n < 0) || !(*name_ok)(bp)) { michael@0: had_error++; michael@0: continue; michael@0: } michael@0: cp += n; /* name */ michael@0: type = _getshort(cp); michael@0: cp += INT16SZ; /* type */ michael@0: class = _getshort(cp); michael@0: cp += INT16SZ + INT32SZ; /* class, TTL */ michael@0: n = _getshort(cp); michael@0: cp += INT16SZ; /* len */ michael@0: if (class != C_IN) { michael@0: /* XXX - debug? syslog? */ michael@0: cp += n; michael@0: continue; /* XXX - had_error++ ? */ michael@0: } michael@0: if ((qtype == T_A || qtype == T_AAAA || qtype == T_ANY) && michael@0: type == T_CNAME) { michael@0: n = dn_expand(answer->buf, eom, cp, tbuf, sizeof tbuf); michael@0: if ((n < 0) || !(*name_ok)(tbuf)) { michael@0: had_error++; michael@0: continue; michael@0: } michael@0: cp += n; michael@0: /* Get canonical name. */ michael@0: n = strlen(tbuf) + 1; /* for the \0 */ michael@0: if (n > ep - bp || n >= MAXHOSTNAMELEN) { michael@0: had_error++; michael@0: continue; michael@0: } michael@0: strlcpy(bp, tbuf, (size_t)(ep - bp)); michael@0: canonname = bp; michael@0: bp += n; michael@0: continue; michael@0: } michael@0: if (qtype == T_ANY) { michael@0: if (!(type == T_A || type == T_AAAA)) { michael@0: cp += n; michael@0: continue; michael@0: } michael@0: } else if (type != qtype) { michael@0: if (type != T_KEY && type != T_SIG) michael@0: syslog(LOG_NOTICE|LOG_AUTH, michael@0: "gethostby*.getanswer: asked for \"%s %s %s\", got type \"%s\"", michael@0: qname, p_class(C_IN), p_type(qtype), michael@0: p_type(type)); michael@0: cp += n; michael@0: continue; /* XXX - had_error++ ? */ michael@0: } michael@0: switch (type) { michael@0: case T_A: michael@0: case T_AAAA: michael@0: if (strcasecmp(canonname, bp) != 0) { michael@0: syslog(LOG_NOTICE|LOG_AUTH, michael@0: AskedForGot, canonname, bp); michael@0: cp += n; michael@0: continue; /* XXX - had_error++ ? */ michael@0: } michael@0: if (type == T_A && n != INADDRSZ) { michael@0: cp += n; michael@0: continue; michael@0: } michael@0: if (type == T_AAAA && n != IN6ADDRSZ) { michael@0: cp += n; michael@0: continue; michael@0: } michael@0: if (type == T_AAAA) { michael@0: struct in6_addr in6; michael@0: memcpy(&in6, cp, IN6ADDRSZ); michael@0: if (IN6_IS_ADDR_V4MAPPED(&in6)) { michael@0: cp += n; michael@0: continue; michael@0: } michael@0: } michael@0: if (!haveanswer) { michael@0: int nn; michael@0: michael@0: canonname = bp; michael@0: nn = strlen(bp) + 1; /* for the \0 */ michael@0: bp += nn; michael@0: } michael@0: michael@0: /* don't overwrite pai */ michael@0: ai = *pai; michael@0: ai.ai_family = (type == T_A) ? AF_INET : AF_INET6; michael@0: afd = find_afd(ai.ai_family); michael@0: if (afd == NULL) { michael@0: cp += n; michael@0: continue; michael@0: } michael@0: cur->ai_next = get_ai(&ai, afd, (const char *)cp); michael@0: if (cur->ai_next == NULL) michael@0: had_error++; michael@0: while (cur && cur->ai_next) michael@0: cur = cur->ai_next; michael@0: cp += n; michael@0: break; michael@0: default: michael@0: abort(); michael@0: } michael@0: if (!had_error) michael@0: haveanswer++; michael@0: } michael@0: if (haveanswer) { michael@0: if (!canonname) michael@0: (void)get_canonname(pai, sentinel.ai_next, qname); michael@0: else michael@0: (void)get_canonname(pai, sentinel.ai_next, canonname); michael@0: h_errno = NETDB_SUCCESS; michael@0: return sentinel.ai_next; michael@0: } michael@0: michael@0: h_errno = NO_RECOVERY; michael@0: return NULL; michael@0: } michael@0: michael@0: struct addrinfo_sort_elem { michael@0: struct addrinfo *ai; michael@0: int has_src_addr; michael@0: sockaddr_union src_addr; michael@0: int original_order; michael@0: }; michael@0: michael@0: /*ARGSUSED*/ michael@0: static int michael@0: _get_scope(const struct sockaddr *addr) michael@0: { michael@0: if (addr->sa_family == AF_INET6) { michael@0: const struct sockaddr_in6 *addr6 = (const struct sockaddr_in6 *)addr; michael@0: if (IN6_IS_ADDR_MULTICAST(&addr6->sin6_addr)) { michael@0: return IPV6_ADDR_MC_SCOPE(&addr6->sin6_addr); michael@0: } else if (IN6_IS_ADDR_LOOPBACK(&addr6->sin6_addr) || michael@0: IN6_IS_ADDR_LINKLOCAL(&addr6->sin6_addr)) { michael@0: /* michael@0: * RFC 4291 section 2.5.3 says loopback is to be treated as having michael@0: * link-local scope. michael@0: */ michael@0: return IPV6_ADDR_SCOPE_LINKLOCAL; michael@0: } else if (IN6_IS_ADDR_SITELOCAL(&addr6->sin6_addr)) { michael@0: return IPV6_ADDR_SCOPE_SITELOCAL; michael@0: } else { michael@0: return IPV6_ADDR_SCOPE_GLOBAL; michael@0: } michael@0: } else if (addr->sa_family == AF_INET) { michael@0: const struct sockaddr_in *addr4 = (const struct sockaddr_in *)addr; michael@0: unsigned long int na = ntohl(addr4->sin_addr.s_addr); michael@0: michael@0: if (IN_LOOPBACK(na) || /* 127.0.0.0/8 */ michael@0: (na & 0xffff0000) == 0xa9fe0000) { /* 169.254.0.0/16 */ michael@0: return IPV6_ADDR_SCOPE_LINKLOCAL; michael@0: } else { michael@0: /* michael@0: * According to draft-ietf-6man-rfc3484-revise-01 section 2.3, michael@0: * it is best not to treat the private IPv4 ranges michael@0: * (10.0.0.0/8, 172.16.0.0/12 and 192.168.0.0/16) as being michael@0: * in a special scope, so we don't. michael@0: */ michael@0: return IPV6_ADDR_SCOPE_GLOBAL; michael@0: } michael@0: } else { michael@0: /* michael@0: * This should never happen. michael@0: * Return a scope with low priority as a last resort. michael@0: */ michael@0: return IPV6_ADDR_SCOPE_NODELOCAL; michael@0: } michael@0: } michael@0: michael@0: /* These macros are modelled after the ones in . */ michael@0: michael@0: /* RFC 4380, section 2.6 */ michael@0: #define IN6_IS_ADDR_TEREDO(a) \ michael@0: ((*(const uint32_t *)(const void *)(&(a)->s6_addr[0]) == ntohl(0x20010000))) michael@0: michael@0: /* RFC 3056, section 2. */ michael@0: #define IN6_IS_ADDR_6TO4(a) \ michael@0: (((a)->s6_addr[0] == 0x20) && ((a)->s6_addr[1] == 0x02)) michael@0: michael@0: /* 6bone testing address area (3ffe::/16), deprecated in RFC 3701. */ michael@0: #define IN6_IS_ADDR_6BONE(a) \ michael@0: (((a)->s6_addr[0] == 0x3f) && ((a)->s6_addr[1] == 0xfe)) michael@0: michael@0: /* michael@0: * Get the label for a given IPv4/IPv6 address. michael@0: * RFC 3484, section 2.1, plus changes from draft-ietf-6man-rfc3484-revise-01. michael@0: */ michael@0: michael@0: /*ARGSUSED*/ michael@0: static int michael@0: _get_label(const struct sockaddr *addr) michael@0: { michael@0: if (addr->sa_family == AF_INET) { michael@0: return 3; michael@0: } else if (addr->sa_family == AF_INET6) { michael@0: const struct sockaddr_in6 *addr6 = (const struct sockaddr_in6 *)addr; michael@0: if (IN6_IS_ADDR_LOOPBACK(&addr6->sin6_addr)) { michael@0: return 0; michael@0: } else if (IN6_IS_ADDR_V4MAPPED(&addr6->sin6_addr)) { michael@0: return 3; michael@0: } else if (IN6_IS_ADDR_6TO4(&addr6->sin6_addr)) { michael@0: return 4; michael@0: } else if (IN6_IS_ADDR_TEREDO(&addr6->sin6_addr)) { michael@0: return 5; michael@0: } else if (IN6_IS_ADDR_V4COMPAT(&addr6->sin6_addr)) { michael@0: return 10; michael@0: } else if (IN6_IS_ADDR_SITELOCAL(&addr6->sin6_addr)) { michael@0: return 11; michael@0: } else if (IN6_IS_ADDR_6BONE(&addr6->sin6_addr)) { michael@0: return 12; michael@0: } else { michael@0: return 2; michael@0: } michael@0: } else { michael@0: /* michael@0: * This should never happen. michael@0: * Return a semi-random label as a last resort. michael@0: */ michael@0: return 1; michael@0: } michael@0: } michael@0: michael@0: /* michael@0: * Get the precedence for a given IPv4/IPv6 address. michael@0: * RFC 3484, section 2.1, plus changes from draft-ietf-6man-rfc3484-revise-01. michael@0: */ michael@0: michael@0: /*ARGSUSED*/ michael@0: static int michael@0: _get_precedence(const struct sockaddr *addr) michael@0: { michael@0: if (addr->sa_family == AF_INET) { michael@0: return 30; michael@0: } else if (addr->sa_family == AF_INET6) { michael@0: const struct sockaddr_in6 *addr6 = (const struct sockaddr_in6 *)addr; michael@0: if (IN6_IS_ADDR_LOOPBACK(&addr6->sin6_addr)) { michael@0: return 60; michael@0: } else if (IN6_IS_ADDR_V4MAPPED(&addr6->sin6_addr)) { michael@0: return 30; michael@0: } else if (IN6_IS_ADDR_6TO4(&addr6->sin6_addr)) { michael@0: return 20; michael@0: } else if (IN6_IS_ADDR_TEREDO(&addr6->sin6_addr)) { michael@0: return 10; michael@0: } else if (IN6_IS_ADDR_V4COMPAT(&addr6->sin6_addr) || michael@0: IN6_IS_ADDR_SITELOCAL(&addr6->sin6_addr) || michael@0: IN6_IS_ADDR_6BONE(&addr6->sin6_addr)) { michael@0: return 1; michael@0: } else { michael@0: return 40; michael@0: } michael@0: } else { michael@0: return 1; michael@0: } michael@0: } michael@0: michael@0: /* michael@0: * Find number of matching initial bits between the two addresses a1 and a2. michael@0: */ michael@0: michael@0: /*ARGSUSED*/ michael@0: static int michael@0: _common_prefix_len(const struct in6_addr *a1, const struct in6_addr *a2) michael@0: { michael@0: const char *p1 = (const char *)a1; michael@0: const char *p2 = (const char *)a2; michael@0: unsigned i; michael@0: michael@0: for (i = 0; i < sizeof(*a1); ++i) { michael@0: int x, j; michael@0: michael@0: if (p1[i] == p2[i]) { michael@0: continue; michael@0: } michael@0: x = p1[i] ^ p2[i]; michael@0: for (j = 0; j < CHAR_BIT; ++j) { michael@0: if (x & (1 << (CHAR_BIT - 1))) { michael@0: return i * CHAR_BIT + j; michael@0: } michael@0: x <<= 1; michael@0: } michael@0: } michael@0: return sizeof(*a1) * CHAR_BIT; michael@0: } michael@0: michael@0: /* michael@0: * Compare two source/destination address pairs. michael@0: * RFC 3484, section 6. michael@0: */ michael@0: michael@0: /*ARGSUSED*/ michael@0: static int michael@0: _rfc3484_compare(const void *ptr1, const void* ptr2) michael@0: { michael@0: const struct addrinfo_sort_elem *a1 = (const struct addrinfo_sort_elem *)ptr1; michael@0: const struct addrinfo_sort_elem *a2 = (const struct addrinfo_sort_elem *)ptr2; michael@0: int scope_src1, scope_dst1, scope_match1; michael@0: int scope_src2, scope_dst2, scope_match2; michael@0: int label_src1, label_dst1, label_match1; michael@0: int label_src2, label_dst2, label_match2; michael@0: int precedence1, precedence2; michael@0: int prefixlen1, prefixlen2; michael@0: michael@0: /* Rule 1: Avoid unusable destinations. */ michael@0: if (a1->has_src_addr != a2->has_src_addr) { michael@0: return a2->has_src_addr - a1->has_src_addr; michael@0: } michael@0: michael@0: /* Rule 2: Prefer matching scope. */ michael@0: scope_src1 = _get_scope(&a1->src_addr.generic); michael@0: scope_dst1 = _get_scope(a1->ai->ai_addr); michael@0: scope_match1 = (scope_src1 == scope_dst1); michael@0: michael@0: scope_src2 = _get_scope(&a2->src_addr.generic); michael@0: scope_dst2 = _get_scope(a2->ai->ai_addr); michael@0: scope_match2 = (scope_src2 == scope_dst2); michael@0: michael@0: if (scope_match1 != scope_match2) { michael@0: return scope_match2 - scope_match1; michael@0: } michael@0: michael@0: /* michael@0: * Rule 3: Avoid deprecated addresses. michael@0: * TODO(sesse): We don't currently have a good way of finding this. michael@0: */ michael@0: michael@0: /* michael@0: * Rule 4: Prefer home addresses. michael@0: * TODO(sesse): We don't currently have a good way of finding this. michael@0: */ michael@0: michael@0: /* Rule 5: Prefer matching label. */ michael@0: label_src1 = _get_label(&a1->src_addr.generic); michael@0: label_dst1 = _get_label(a1->ai->ai_addr); michael@0: label_match1 = (label_src1 == label_dst1); michael@0: michael@0: label_src2 = _get_label(&a2->src_addr.generic); michael@0: label_dst2 = _get_label(a2->ai->ai_addr); michael@0: label_match2 = (label_src2 == label_dst2); michael@0: michael@0: if (label_match1 != label_match2) { michael@0: return label_match2 - label_match1; michael@0: } michael@0: michael@0: /* Rule 6: Prefer higher precedence. */ michael@0: precedence1 = _get_precedence(a1->ai->ai_addr); michael@0: precedence2 = _get_precedence(a2->ai->ai_addr); michael@0: if (precedence1 != precedence2) { michael@0: return precedence2 - precedence1; michael@0: } michael@0: michael@0: /* michael@0: * Rule 7: Prefer native transport. michael@0: * TODO(sesse): We don't currently have a good way of finding this. michael@0: */ michael@0: michael@0: /* Rule 8: Prefer smaller scope. */ michael@0: if (scope_dst1 != scope_dst2) { michael@0: return scope_dst1 - scope_dst2; michael@0: } michael@0: michael@0: /* michael@0: * Rule 9: Use longest matching prefix. michael@0: * We implement this for IPv6 only, as the rules in RFC 3484 don't seem michael@0: * to work very well directly applied to IPv4. (glibc uses information from michael@0: * the routing table for a custom IPv4 implementation here.) michael@0: */ michael@0: if (a1->has_src_addr && a1->ai->ai_addr->sa_family == AF_INET6 && michael@0: a2->has_src_addr && a2->ai->ai_addr->sa_family == AF_INET6) { michael@0: const struct sockaddr_in6 *a1_src = &a1->src_addr.in6; michael@0: const struct sockaddr_in6 *a1_dst = (const struct sockaddr_in6 *)a1->ai->ai_addr; michael@0: const struct sockaddr_in6 *a2_src = &a2->src_addr.in6; michael@0: const struct sockaddr_in6 *a2_dst = (const struct sockaddr_in6 *)a2->ai->ai_addr; michael@0: prefixlen1 = _common_prefix_len(&a1_src->sin6_addr, &a1_dst->sin6_addr); michael@0: prefixlen2 = _common_prefix_len(&a2_src->sin6_addr, &a2_dst->sin6_addr); michael@0: if (prefixlen1 != prefixlen2) { michael@0: return prefixlen2 - prefixlen1; michael@0: } michael@0: } michael@0: michael@0: /* michael@0: * Rule 10: Leave the order unchanged. michael@0: * We need this since qsort() is not necessarily stable. michael@0: */ michael@0: return a1->original_order - a2->original_order; michael@0: } michael@0: michael@0: /* michael@0: * Find the source address that will be used if trying to connect to the given michael@0: * address. src_addr must be large enough to hold a struct sockaddr_in6. michael@0: * michael@0: * Returns 1 if a source address was found, 0 if the address is unreachable, michael@0: * and -1 if a fatal error occurred. If 0 or 1, the contents of src_addr are michael@0: * undefined. michael@0: */ michael@0: michael@0: /*ARGSUSED*/ michael@0: static int michael@0: _find_src_addr(const struct sockaddr *addr, struct sockaddr *src_addr) michael@0: { michael@0: int sock; michael@0: int ret; michael@0: socklen_t len; michael@0: michael@0: switch (addr->sa_family) { michael@0: case AF_INET: michael@0: len = sizeof(struct sockaddr_in); michael@0: break; michael@0: case AF_INET6: michael@0: len = sizeof(struct sockaddr_in6); michael@0: break; michael@0: default: michael@0: /* No known usable source address for non-INET families. */ michael@0: return 0; michael@0: } michael@0: michael@0: sock = socket(addr->sa_family, SOCK_DGRAM, IPPROTO_UDP); michael@0: if (sock == -1) { michael@0: if (errno == EAFNOSUPPORT) { michael@0: return 0; michael@0: } else { michael@0: return -1; michael@0: } michael@0: } michael@0: michael@0: do { michael@0: ret = connect(sock, addr, len); michael@0: } while (ret == -1 && errno == EINTR); michael@0: michael@0: if (ret == -1) { michael@0: close(sock); michael@0: return 0; michael@0: } michael@0: michael@0: if (getsockname(sock, src_addr, &len) == -1) { michael@0: close(sock); michael@0: return -1; michael@0: } michael@0: close(sock); michael@0: return 1; michael@0: } michael@0: michael@0: /* michael@0: * Sort the linked list starting at sentinel->ai_next in RFC3484 order. michael@0: * Will leave the list unchanged if an error occurs. michael@0: */ michael@0: michael@0: /*ARGSUSED*/ michael@0: static void michael@0: _rfc3484_sort(struct addrinfo *list_sentinel) michael@0: { michael@0: struct addrinfo *cur; michael@0: int nelem = 0, i; michael@0: struct addrinfo_sort_elem *elems; michael@0: michael@0: cur = list_sentinel->ai_next; michael@0: while (cur) { michael@0: ++nelem; michael@0: cur = cur->ai_next; michael@0: } michael@0: michael@0: elems = (struct addrinfo_sort_elem *)malloc(nelem * sizeof(struct addrinfo_sort_elem)); michael@0: if (elems == NULL) { michael@0: goto error; michael@0: } michael@0: michael@0: /* michael@0: * Convert the linked list to an array that also contains the candidate michael@0: * source address for each destination address. michael@0: */ michael@0: for (i = 0, cur = list_sentinel->ai_next; i < nelem; ++i, cur = cur->ai_next) { michael@0: int has_src_addr; michael@0: assert(cur != NULL); michael@0: elems[i].ai = cur; michael@0: elems[i].original_order = i; michael@0: michael@0: has_src_addr = _find_src_addr(cur->ai_addr, &elems[i].src_addr.generic); michael@0: if (has_src_addr == -1) { michael@0: goto error; michael@0: } michael@0: elems[i].has_src_addr = has_src_addr; michael@0: } michael@0: michael@0: /* Sort the addresses, and rearrange the linked list so it matches the sorted order. */ michael@0: qsort((void *)elems, nelem, sizeof(struct addrinfo_sort_elem), _rfc3484_compare); michael@0: michael@0: list_sentinel->ai_next = elems[0].ai; michael@0: for (i = 0; i < nelem - 1; ++i) { michael@0: elems[i].ai->ai_next = elems[i + 1].ai; michael@0: } michael@0: elems[nelem - 1].ai->ai_next = NULL; michael@0: michael@0: error: michael@0: free(elems); michael@0: } michael@0: michael@0: /*ARGSUSED*/ michael@0: static int michael@0: _dns_getaddrinfo(void *rv, void *cb_data, va_list ap) michael@0: { michael@0: struct addrinfo *ai; michael@0: querybuf *buf, *buf2; michael@0: const char *name; michael@0: const struct addrinfo *pai; michael@0: struct addrinfo sentinel, *cur; michael@0: struct res_target q, q2; michael@0: res_state res; michael@0: michael@0: name = va_arg(ap, char *); michael@0: pai = va_arg(ap, const struct addrinfo *); michael@0: //fprintf(stderr, "_dns_getaddrinfo() name = '%s'\n", name); michael@0: michael@0: memset(&q, 0, sizeof(q)); michael@0: memset(&q2, 0, sizeof(q2)); michael@0: memset(&sentinel, 0, sizeof(sentinel)); michael@0: cur = &sentinel; michael@0: michael@0: buf = malloc(sizeof(*buf)); michael@0: if (buf == NULL) { michael@0: h_errno = NETDB_INTERNAL; michael@0: return NS_NOTFOUND; michael@0: } michael@0: buf2 = malloc(sizeof(*buf2)); michael@0: if (buf2 == NULL) { michael@0: free(buf); michael@0: h_errno = NETDB_INTERNAL; michael@0: return NS_NOTFOUND; michael@0: } michael@0: michael@0: switch (pai->ai_family) { michael@0: case AF_UNSPEC: michael@0: /* prefer IPv6 */ michael@0: q.name = name; michael@0: q.qclass = C_IN; michael@0: q.answer = buf->buf; michael@0: q.anslen = sizeof(buf->buf); michael@0: int query_ipv6 = 1, query_ipv4 = 1; michael@0: if (pai->ai_flags & AI_ADDRCONFIG) { michael@0: query_ipv6 = _have_ipv6(); michael@0: query_ipv4 = _have_ipv4(); michael@0: } michael@0: if (query_ipv6) { michael@0: q.qtype = T_AAAA; michael@0: if (query_ipv4) { michael@0: q.next = &q2; michael@0: q2.name = name; michael@0: q2.qclass = C_IN; michael@0: q2.qtype = T_A; michael@0: q2.answer = buf2->buf; michael@0: q2.anslen = sizeof(buf2->buf); michael@0: } michael@0: } else if (query_ipv4) { michael@0: q.qtype = T_A; michael@0: } else { michael@0: free(buf); michael@0: free(buf2); michael@0: return NS_NOTFOUND; michael@0: } michael@0: break; michael@0: case AF_INET: michael@0: q.name = name; michael@0: q.qclass = C_IN; michael@0: q.qtype = T_A; michael@0: q.answer = buf->buf; michael@0: q.anslen = sizeof(buf->buf); michael@0: break; michael@0: case AF_INET6: michael@0: q.name = name; michael@0: q.qclass = C_IN; michael@0: q.qtype = T_AAAA; michael@0: q.answer = buf->buf; michael@0: q.anslen = sizeof(buf->buf); michael@0: break; michael@0: default: michael@0: free(buf); michael@0: free(buf2); michael@0: return NS_UNAVAIL; michael@0: } michael@0: michael@0: res = __res_get_state(); michael@0: if (res == NULL) { michael@0: free(buf); michael@0: free(buf2); michael@0: return NS_NOTFOUND; michael@0: } michael@0: michael@0: if (res_searchN(name, &q, res) < 0) { michael@0: __res_put_state(res); michael@0: free(buf); michael@0: free(buf2); michael@0: return NS_NOTFOUND; michael@0: } michael@0: ai = getanswer(buf, q.n, q.name, q.qtype, pai); michael@0: if (ai) { michael@0: cur->ai_next = ai; michael@0: while (cur && cur->ai_next) michael@0: cur = cur->ai_next; michael@0: } michael@0: if (q.next) { michael@0: ai = getanswer(buf2, q2.n, q2.name, q2.qtype, pai); michael@0: if (ai) michael@0: cur->ai_next = ai; michael@0: } michael@0: free(buf); michael@0: free(buf2); michael@0: if (sentinel.ai_next == NULL) { michael@0: __res_put_state(res); michael@0: switch (h_errno) { michael@0: case HOST_NOT_FOUND: michael@0: return NS_NOTFOUND; michael@0: case TRY_AGAIN: michael@0: return NS_TRYAGAIN; michael@0: default: michael@0: return NS_UNAVAIL; michael@0: } michael@0: } michael@0: michael@0: _rfc3484_sort(&sentinel); michael@0: michael@0: __res_put_state(res); michael@0: michael@0: *((struct addrinfo **)rv) = sentinel.ai_next; michael@0: return NS_SUCCESS; michael@0: } michael@0: michael@0: static void michael@0: _sethtent(_pseudo_FILE * __restrict__ hostf) michael@0: { michael@0: assert(hostf); michael@0: if (hostf->mapping == MAP_FAILED) michael@0: (void) _pseudo_fopen_r(hostf, _PATH_HOSTS); michael@0: else michael@0: _pseudo_rewind(hostf); michael@0: } michael@0: michael@0: static void michael@0: _endhtent(_pseudo_FILE * __restrict__ hostf) michael@0: { michael@0: assert(hostf); michael@0: (void) _pseudo_fclose(hostf); michael@0: } michael@0: michael@0: static struct addrinfo * michael@0: _gethtent(_pseudo_FILE * __restrict__ hostf, const char *name, const struct addrinfo *pai) michael@0: { michael@0: char *p; michael@0: char *cp, *tname, *cname; michael@0: struct addrinfo hints, *res0, *res; michael@0: int error; michael@0: const char *addr; michael@0: char hostbuf[8*1024]; michael@0: michael@0: assert(hostf); michael@0: // fprintf(stderr, "_gethtent() name = '%s'\n", name); michael@0: assert(name != NULL); michael@0: assert(pai != NULL); michael@0: michael@0: if (hostf->mapping == MAP_FAILED) michael@0: (void) _pseudo_fopen_r(hostf, _PATH_HOSTS); michael@0: if (hostf->mapping == MAP_FAILED) michael@0: return (NULL); michael@0: again: michael@0: if (!(p = _pseudo_fgets(hostbuf, sizeof hostbuf, hostf))) michael@0: return (NULL); michael@0: if (*p == '#') michael@0: goto again; michael@0: if (!(cp = strpbrk(p, "#\n"))) michael@0: goto again; michael@0: *cp = '\0'; michael@0: if (!(cp = strpbrk(p, " \t"))) michael@0: goto again; michael@0: *cp++ = '\0'; michael@0: addr = p; michael@0: /* if this is not something we're looking for, skip it. */ michael@0: cname = NULL; michael@0: while (cp && *cp) { michael@0: if (*cp == ' ' || *cp == '\t') { michael@0: cp++; michael@0: continue; michael@0: } michael@0: if (!cname) michael@0: cname = cp; michael@0: tname = cp; michael@0: if ((cp = strpbrk(cp, " \t")) != NULL) michael@0: *cp++ = '\0'; michael@0: // fprintf(stderr, "\ttname = '%s'", tname); michael@0: if (strcasecmp(name, tname) == 0) michael@0: goto found; michael@0: } michael@0: goto again; michael@0: michael@0: found: michael@0: hints = *pai; michael@0: hints.ai_flags = AI_NUMERICHOST; michael@0: error = __wrap_getaddrinfo(addr, NULL, &hints, &res0); michael@0: if (error) michael@0: goto again; michael@0: for (res = res0; res; res = res->ai_next) { michael@0: /* cover it up */ michael@0: res->ai_flags = pai->ai_flags; michael@0: michael@0: if (pai->ai_flags & AI_CANONNAME) { michael@0: if (get_canonname(pai, res, cname) != 0) { michael@0: __wrap_freeaddrinfo(res0); michael@0: goto again; michael@0: } michael@0: } michael@0: } michael@0: return res0; michael@0: } michael@0: michael@0: /*ARGSUSED*/ michael@0: static int michael@0: _files_getaddrinfo(void *rv, void *cb_data, va_list ap) michael@0: { michael@0: const char *name; michael@0: const struct addrinfo *pai; michael@0: struct addrinfo sentinel, *cur; michael@0: struct addrinfo *p; michael@0: _pseudo_FILE hostf = _PSEUDO_FILE_INITIALIZER; michael@0: michael@0: name = va_arg(ap, char *); michael@0: pai = va_arg(ap, struct addrinfo *); michael@0: michael@0: // fprintf(stderr, "_files_getaddrinfo() name = '%s'\n", name); michael@0: memset(&sentinel, 0, sizeof(sentinel)); michael@0: cur = &sentinel; michael@0: michael@0: _sethtent(&hostf); michael@0: while ((p = _gethtent(&hostf, name, pai)) != NULL) { michael@0: cur->ai_next = p; michael@0: while (cur && cur->ai_next) michael@0: cur = cur->ai_next; michael@0: } michael@0: _endhtent(&hostf); michael@0: michael@0: *((struct addrinfo **)rv) = sentinel.ai_next; michael@0: if (sentinel.ai_next == NULL) michael@0: return NS_NOTFOUND; michael@0: return NS_SUCCESS; michael@0: } michael@0: michael@0: /* resolver logic */ michael@0: michael@0: /* michael@0: * Formulate a normal query, send, and await answer. michael@0: * Returned answer is placed in supplied buffer "answer". michael@0: * Perform preliminary check of answer, returning success only michael@0: * if no error is indicated and the answer count is nonzero. michael@0: * Return the size of the response on success, -1 on error. michael@0: * Error number is left in h_errno. michael@0: * michael@0: * Caller must parse answer and determine whether it answers the question. michael@0: */ michael@0: static int michael@0: res_queryN(const char *name, /* domain name */ struct res_target *target, michael@0: res_state res) michael@0: { michael@0: u_char buf[MAXPACKET]; michael@0: HEADER *hp; michael@0: int n; michael@0: struct res_target *t; michael@0: int rcode; michael@0: int ancount; michael@0: michael@0: assert(name != NULL); michael@0: /* XXX: target may be NULL??? */ michael@0: michael@0: rcode = NOERROR; michael@0: ancount = 0; michael@0: michael@0: for (t = target; t; t = t->next) { michael@0: int class, type; michael@0: u_char *answer; michael@0: int anslen; michael@0: michael@0: hp = (HEADER *)(void *)t->answer; michael@0: hp->rcode = NOERROR; /* default */ michael@0: michael@0: /* make it easier... */ michael@0: class = t->qclass; michael@0: type = t->qtype; michael@0: answer = t->answer; michael@0: anslen = t->anslen; michael@0: #ifdef DEBUG michael@0: if (res->options & RES_DEBUG) michael@0: printf(";; res_nquery(%s, %d, %d)\n", name, class, type); michael@0: #endif michael@0: michael@0: n = res_nmkquery(res, QUERY, name, class, type, NULL, 0, NULL, michael@0: buf, sizeof(buf)); michael@0: #ifdef RES_USE_EDNS0 michael@0: if (n > 0 && (res->options & RES_USE_EDNS0) != 0) michael@0: n = res_nopt(res, n, buf, sizeof(buf), anslen); michael@0: #endif michael@0: if (n <= 0) { michael@0: #ifdef DEBUG michael@0: if (res->options & RES_DEBUG) michael@0: printf(";; res_nquery: mkquery failed\n"); michael@0: #endif michael@0: h_errno = NO_RECOVERY; michael@0: return n; michael@0: } michael@0: n = res_nsend(res, buf, n, answer, anslen); michael@0: #if 0 michael@0: if (n < 0) { michael@0: #ifdef DEBUG michael@0: if (res->options & RES_DEBUG) michael@0: printf(";; res_query: send error\n"); michael@0: #endif michael@0: h_errno = TRY_AGAIN; michael@0: return n; michael@0: } michael@0: #endif michael@0: michael@0: if (n < 0 || hp->rcode != NOERROR || ntohs(hp->ancount) == 0) { michael@0: rcode = hp->rcode; /* record most recent error */ michael@0: #ifdef DEBUG michael@0: if (res->options & RES_DEBUG) michael@0: printf(";; rcode = %u, ancount=%u\n", hp->rcode, michael@0: ntohs(hp->ancount)); michael@0: #endif michael@0: continue; michael@0: } michael@0: michael@0: ancount += ntohs(hp->ancount); michael@0: michael@0: t->n = n; michael@0: } michael@0: michael@0: if (ancount == 0) { michael@0: switch (rcode) { michael@0: case NXDOMAIN: michael@0: h_errno = HOST_NOT_FOUND; michael@0: break; michael@0: case SERVFAIL: michael@0: h_errno = TRY_AGAIN; michael@0: break; michael@0: case NOERROR: michael@0: h_errno = NO_DATA; michael@0: break; michael@0: case FORMERR: michael@0: case NOTIMP: michael@0: case REFUSED: michael@0: default: michael@0: h_errno = NO_RECOVERY; michael@0: break; michael@0: } michael@0: return -1; michael@0: } michael@0: return ancount; michael@0: } michael@0: michael@0: /* michael@0: * Formulate a normal query, send, and retrieve answer in supplied buffer. michael@0: * Return the size of the response on success, -1 on error. michael@0: * If enabled, implement search rules until answer or unrecoverable failure michael@0: * is detected. Error code, if any, is left in h_errno. michael@0: */ michael@0: static int michael@0: res_searchN(const char *name, struct res_target *target, res_state res) michael@0: { michael@0: const char *cp, * const *domain; michael@0: HEADER *hp; michael@0: u_int dots; michael@0: int trailing_dot, ret, saved_herrno; michael@0: int got_nodata = 0, got_servfail = 0, tried_as_is = 0; michael@0: michael@0: assert(name != NULL); michael@0: assert(target != NULL); michael@0: michael@0: hp = (HEADER *)(void *)target->answer; /*XXX*/ michael@0: michael@0: errno = 0; michael@0: h_errno = HOST_NOT_FOUND; /* default, if we never query */ michael@0: dots = 0; michael@0: for (cp = name; *cp; cp++) michael@0: dots += (*cp == '.'); michael@0: trailing_dot = 0; michael@0: if (cp > name && *--cp == '.') michael@0: trailing_dot++; michael@0: michael@0: michael@0: //fprintf(stderr, "res_searchN() name = '%s'\n", name); michael@0: michael@0: /* michael@0: * if there aren't any dots, it could be a user-level alias michael@0: */ michael@0: if (!dots && (cp = __hostalias(name)) != NULL) { michael@0: ret = res_queryN(cp, target, res); michael@0: return ret; michael@0: } michael@0: michael@0: /* michael@0: * If there are dots in the name already, let's just give it a try michael@0: * 'as is'. The threshold can be set with the "ndots" option. michael@0: */ michael@0: saved_herrno = -1; michael@0: if (dots >= res->ndots) { michael@0: ret = res_querydomainN(name, NULL, target, res); michael@0: if (ret > 0) michael@0: return (ret); michael@0: saved_herrno = h_errno; michael@0: tried_as_is++; michael@0: } michael@0: michael@0: /* michael@0: * We do at least one level of search if michael@0: * - there is no dot and RES_DEFNAME is set, or michael@0: * - there is at least one dot, there is no trailing dot, michael@0: * and RES_DNSRCH is set. michael@0: */ michael@0: if ((!dots && (res->options & RES_DEFNAMES)) || michael@0: (dots && !trailing_dot && (res->options & RES_DNSRCH))) { michael@0: int done = 0; michael@0: michael@0: for (domain = (const char * const *)res->dnsrch; michael@0: *domain && !done; michael@0: domain++) { michael@0: michael@0: ret = res_querydomainN(name, *domain, target, res); michael@0: if (ret > 0) michael@0: return ret; michael@0: michael@0: /* michael@0: * If no server present, give up. michael@0: * If name isn't found in this domain, michael@0: * keep trying higher domains in the search list michael@0: * (if that's enabled). michael@0: * On a NO_DATA error, keep trying, otherwise michael@0: * a wildcard entry of another type could keep us michael@0: * from finding this entry higher in the domain. michael@0: * If we get some other error (negative answer or michael@0: * server failure), then stop searching up, michael@0: * but try the input name below in case it's michael@0: * fully-qualified. michael@0: */ michael@0: if (errno == ECONNREFUSED) { michael@0: h_errno = TRY_AGAIN; michael@0: return -1; michael@0: } michael@0: michael@0: switch (h_errno) { michael@0: case NO_DATA: michael@0: got_nodata++; michael@0: /* FALLTHROUGH */ michael@0: case HOST_NOT_FOUND: michael@0: /* keep trying */ michael@0: break; michael@0: case TRY_AGAIN: michael@0: if (hp->rcode == SERVFAIL) { michael@0: /* try next search element, if any */ michael@0: got_servfail++; michael@0: break; michael@0: } michael@0: /* FALLTHROUGH */ michael@0: default: michael@0: /* anything else implies that we're done */ michael@0: done++; michael@0: } michael@0: /* michael@0: * if we got here for some reason other than DNSRCH, michael@0: * we only wanted one iteration of the loop, so stop. michael@0: */ michael@0: if (!(res->options & RES_DNSRCH)) michael@0: done++; michael@0: } michael@0: } michael@0: michael@0: /* michael@0: * if we have not already tried the name "as is", do that now. michael@0: * note that we do this regardless of how many dots were in the michael@0: * name or whether it ends with a dot. michael@0: */ michael@0: if (!tried_as_is) { michael@0: ret = res_querydomainN(name, NULL, target, res); michael@0: if (ret > 0) michael@0: return ret; michael@0: } michael@0: michael@0: /* michael@0: * if we got here, we didn't satisfy the search. michael@0: * if we did an initial full query, return that query's h_errno michael@0: * (note that we wouldn't be here if that query had succeeded). michael@0: * else if we ever got a nodata, send that back as the reason. michael@0: * else send back meaningless h_errno, that being the one from michael@0: * the last DNSRCH we did. michael@0: */ michael@0: if (saved_herrno != -1) michael@0: h_errno = saved_herrno; michael@0: else if (got_nodata) michael@0: h_errno = NO_DATA; michael@0: else if (got_servfail) michael@0: h_errno = TRY_AGAIN; michael@0: return -1; michael@0: } michael@0: michael@0: /* michael@0: * Perform a call on res_query on the concatenation of name and domain, michael@0: * removing a trailing dot from name if domain is NULL. michael@0: */ michael@0: static int michael@0: res_querydomainN(const char *name, const char *domain, michael@0: struct res_target *target, res_state res) michael@0: { michael@0: char nbuf[MAXDNAME]; michael@0: const char *longname = nbuf; michael@0: size_t n, d; michael@0: michael@0: assert(name != NULL); michael@0: /* XXX: target may be NULL??? */ michael@0: michael@0: #ifdef DEBUG michael@0: if (res->options & RES_DEBUG) michael@0: printf(";; res_querydomain(%s, %s)\n", michael@0: name, domain?domain:""); michael@0: #endif michael@0: if (domain == NULL) { michael@0: /* michael@0: * Check for trailing '.'; michael@0: * copy without '.' if present. michael@0: */ michael@0: n = strlen(name); michael@0: if (n + 1 > sizeof(nbuf)) { michael@0: h_errno = NO_RECOVERY; michael@0: return -1; michael@0: } michael@0: if (n > 0 && name[--n] == '.') { michael@0: strncpy(nbuf, name, n); michael@0: nbuf[n] = '\0'; michael@0: } else michael@0: longname = name; michael@0: } else { michael@0: n = strlen(name); michael@0: d = strlen(domain); michael@0: if (n + 1 + d + 1 > sizeof(nbuf)) { michael@0: h_errno = NO_RECOVERY; michael@0: return -1; michael@0: } michael@0: snprintf(nbuf, sizeof(nbuf), "%s.%s", name, domain); michael@0: } michael@0: return res_queryN(longname, target, res); michael@0: }