|  | /*	$NetBSD: res_send.c,v 1.9 2006/01/24 17:41:25 christos Exp $	*/ | 
|  |  | 
|  | /* | 
|  | * Copyright (c) 1985, 1989, 1993 | 
|  | *    The Regents of the University of California.  All rights reserved. | 
|  | * | 
|  | * Redistribution and use in source and binary forms, with or without | 
|  | * modification, are permitted provided that the following conditions | 
|  | * are met: | 
|  | * 1. Redistributions of source code must retain the above copyright | 
|  | *    notice, this list of conditions and the following disclaimer. | 
|  | * 2. Redistributions in binary form must reproduce the above copyright | 
|  | *    notice, this list of conditions and the following disclaimer in the | 
|  | *    documentation and/or other materials provided with the distribution. | 
|  | * 3. All advertising materials mentioning features or use of this software | 
|  | *    must display the following acknowledgement: | 
|  | * 	This product includes software developed by the University of | 
|  | * 	California, Berkeley and its contributors. | 
|  | * 4. Neither the name of the University nor the names of its contributors | 
|  | *    may be used to endorse or promote products derived from this software | 
|  | *    without specific prior written permission. | 
|  | * | 
|  | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | 
|  | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | 
|  | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | 
|  | * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | 
|  | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | 
|  | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | 
|  | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | 
|  | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | 
|  | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | 
|  | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | 
|  | * SUCH DAMAGE. | 
|  | */ | 
|  |  | 
|  | /* | 
|  | * Portions Copyright (c) 1993 by Digital Equipment Corporation. | 
|  | * | 
|  | * Permission to use, copy, modify, and distribute this software for any | 
|  | * purpose with or without fee is hereby granted, provided that the above | 
|  | * copyright notice and this permission notice appear in all copies, and that | 
|  | * the name of Digital Equipment Corporation not be used in advertising or | 
|  | * publicity pertaining to distribution of the document or software without | 
|  | * specific, written prior permission. | 
|  | * | 
|  | * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL | 
|  | * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES | 
|  | * OF MERCHANTABILITY AND FITNESS.   IN NO EVENT SHALL DIGITAL EQUIPMENT | 
|  | * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL | 
|  | * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR | 
|  | * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS | 
|  | * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS | 
|  | * SOFTWARE. | 
|  | */ | 
|  |  | 
|  | /* | 
|  | * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") | 
|  | * Portions Copyright (c) 1996-1999 by Internet Software Consortium. | 
|  | * | 
|  | * Permission to use, copy, modify, and distribute this software for any | 
|  | * purpose with or without fee is hereby granted, provided that the above | 
|  | * copyright notice and this permission notice appear in all copies. | 
|  | * | 
|  | * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES | 
|  | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | 
|  | * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR | 
|  | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | 
|  | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | 
|  | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT | 
|  | * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | 
|  | */ | 
|  |  | 
|  | #include <sys/cdefs.h> | 
|  | #if defined(LIBC_SCCS) && !defined(lint) | 
|  | #ifdef notdef | 
|  | static const char sccsid[] = "@(#)res_send.c	8.1 (Berkeley) 6/4/93"; | 
|  | static const char rcsid[] = "Id: res_send.c,v 1.5.2.2.4.5 2004/08/10 02:19:56 marka Exp"; | 
|  | #else | 
|  | __RCSID("$NetBSD: res_send.c,v 1.9 2006/01/24 17:41:25 christos Exp $"); | 
|  | #endif | 
|  | #endif /* LIBC_SCCS and not lint */ | 
|  |  | 
|  | /* | 
|  | * Send query to name server and wait for reply. | 
|  | */ | 
|  |  | 
|  | #include <sys/types.h> | 
|  | #include <sys/param.h> | 
|  | #include <sys/time.h> | 
|  | #include <sys/socket.h> | 
|  | #include <sys/uio.h> | 
|  |  | 
|  | #include <netinet/in.h> | 
|  | #include <arpa/nameser.h> | 
|  | #include <arpa/inet.h> | 
|  |  | 
|  | #include <errno.h> | 
|  | #include <fcntl.h> | 
|  | #include <netdb.h> | 
|  | #ifdef ANDROID_CHANGES | 
|  | #include "resolv_netid.h" | 
|  | #include "resolv_private.h" | 
|  | #else | 
|  | #include <resolv.h> | 
|  | #endif | 
|  | #include <signal.h> | 
|  | #include <stdio.h> | 
|  | #include <stdlib.h> | 
|  | #include <string.h> | 
|  | #include <time.h> | 
|  | #include <unistd.h> | 
|  |  | 
|  | #include <isc/eventlib.h> | 
|  |  | 
|  | #include <resolv_cache.h> | 
|  |  | 
|  | #include "private/libc_logging.h" | 
|  |  | 
|  | #ifndef DE_CONST | 
|  | #define DE_CONST(c,v)   v = ((c) ? \ | 
|  | strchr((const void *)(c), *(const char *)(const void *)(c)) : NULL) | 
|  | #endif | 
|  |  | 
|  | /* Options.  Leave them on. */ | 
|  | #ifndef DEBUG | 
|  | #define DEBUG | 
|  | #endif | 
|  | #include "res_debug.h" | 
|  | #include "res_private.h" | 
|  | #include "resolv_stats.h" | 
|  |  | 
|  | #define EXT(res) ((res)->_u._ext) | 
|  | #define DBG 0 | 
|  |  | 
|  | static const int highestFD = FD_SETSIZE - 1; | 
|  |  | 
|  | /* Forward. */ | 
|  |  | 
|  | static int		get_salen __P((const struct sockaddr *)); | 
|  | static struct sockaddr * get_nsaddr __P((res_state, size_t)); | 
|  | static int		send_vc(res_state, const u_char *, int, | 
|  | u_char *, int, int *, int, | 
|  | time_t *, int *, int *); | 
|  | static int		send_dg(res_state, const u_char *, int, | 
|  | u_char *, int, int *, int, | 
|  | int *, int *, | 
|  | time_t *, int *, int *); | 
|  | static void		Aerror(const res_state, FILE *, const char *, int, | 
|  | const struct sockaddr *, int); | 
|  | static void		Perror(const res_state, FILE *, const char *, int); | 
|  | static int		sock_eq(struct sockaddr *, struct sockaddr *); | 
|  | #ifdef NEED_PSELECT | 
|  | static int		pselect(int, void *, void *, void *, | 
|  | struct timespec *, | 
|  | const sigset_t *); | 
|  | #endif | 
|  | void res_pquery(const res_state, const u_char *, int, FILE *); | 
|  | static int connect_with_timeout(int sock, const struct sockaddr *nsap, | 
|  | socklen_t salen, int sec); | 
|  | static int retrying_select(const int sock, fd_set *readset, fd_set *writeset, | 
|  | const struct timespec *finish); | 
|  |  | 
|  | /* BIONIC-BEGIN: implement source port randomization */ | 
|  | typedef union { | 
|  | struct sockaddr      sa; | 
|  | struct sockaddr_in   sin; | 
|  | struct sockaddr_in6  sin6; | 
|  | } _sockaddr_union; | 
|  |  | 
|  | static int | 
|  | random_bind( int  s, int  family ) | 
|  | { | 
|  | _sockaddr_union  u; | 
|  | int              j; | 
|  | socklen_t        slen; | 
|  |  | 
|  | /* clear all, this also sets the IP4/6 address to 'any' */ | 
|  | memset( &u, 0, sizeof u ); | 
|  |  | 
|  | switch (family) { | 
|  | case AF_INET: | 
|  | u.sin.sin_family = family; | 
|  | slen             = sizeof u.sin; | 
|  | break; | 
|  | case AF_INET6: | 
|  | u.sin6.sin6_family = family; | 
|  | slen               = sizeof u.sin6; | 
|  | break; | 
|  | default: | 
|  | errno = EPROTO; | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | /* first try to bind to a random source port a few times */ | 
|  | for (j = 0; j < 10; j++) { | 
|  | /* find a random port between 1025 .. 65534 */ | 
|  | int  port = 1025 + (res_randomid() % (65535-1025)); | 
|  | if (family == AF_INET) | 
|  | u.sin.sin_port = htons(port); | 
|  | else | 
|  | u.sin6.sin6_port = htons(port); | 
|  |  | 
|  | if ( !bind( s, &u.sa, slen ) ) | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* nothing after 10 tries, our network table is probably busy */ | 
|  | /* let the system decide which port is best */ | 
|  | if (family == AF_INET) | 
|  | u.sin.sin_port = 0; | 
|  | else | 
|  | u.sin6.sin6_port = 0; | 
|  |  | 
|  | return bind( s, &u.sa, slen ); | 
|  | } | 
|  | /* BIONIC-END */ | 
|  |  | 
|  | static const int niflags = NI_NUMERICHOST | NI_NUMERICSERV; | 
|  |  | 
|  | /* Public. */ | 
|  |  | 
|  | /* int | 
|  | * res_isourserver(ina) | 
|  | *	looks up "ina" in _res.ns_addr_list[] | 
|  | * returns: | 
|  | *	0  : not found | 
|  | *	>0 : found | 
|  | * author: | 
|  | *	paul vixie, 29may94 | 
|  | */ | 
|  | __LIBC_HIDDEN__ int | 
|  | res_ourserver_p(const res_state statp, const struct sockaddr *sa) { | 
|  | const struct sockaddr_in *inp, *srv; | 
|  | const struct sockaddr_in6 *in6p, *srv6; | 
|  | int ns; | 
|  |  | 
|  | switch (sa->sa_family) { | 
|  | case AF_INET: | 
|  | inp = (const struct sockaddr_in *)(const void *)sa; | 
|  | for (ns = 0;  ns < statp->nscount;  ns++) { | 
|  | srv = (struct sockaddr_in *)(void *)get_nsaddr(statp, (size_t)ns); | 
|  | if (srv->sin_family == inp->sin_family && | 
|  | srv->sin_port == inp->sin_port && | 
|  | (srv->sin_addr.s_addr == INADDR_ANY || | 
|  | srv->sin_addr.s_addr == inp->sin_addr.s_addr)) | 
|  | return (1); | 
|  | } | 
|  | break; | 
|  | case AF_INET6: | 
|  | if (EXT(statp).ext == NULL) | 
|  | break; | 
|  | in6p = (const struct sockaddr_in6 *)(const void *)sa; | 
|  | for (ns = 0;  ns < statp->nscount;  ns++) { | 
|  | srv6 = (struct sockaddr_in6 *)(void *)get_nsaddr(statp, (size_t)ns); | 
|  | if (srv6->sin6_family == in6p->sin6_family && | 
|  | srv6->sin6_port == in6p->sin6_port && | 
|  | #ifdef HAVE_SIN6_SCOPE_ID | 
|  | (srv6->sin6_scope_id == 0 || | 
|  | srv6->sin6_scope_id == in6p->sin6_scope_id) && | 
|  | #endif | 
|  | (IN6_IS_ADDR_UNSPECIFIED(&srv6->sin6_addr) || | 
|  | IN6_ARE_ADDR_EQUAL(&srv6->sin6_addr, &in6p->sin6_addr))) | 
|  | return (1); | 
|  | } | 
|  | break; | 
|  | default: | 
|  | break; | 
|  | } | 
|  | return (0); | 
|  | } | 
|  |  | 
|  | /* int | 
|  | * res_nameinquery(name, type, class, buf, eom) | 
|  | *	look for (name,type,class) in the query section of packet (buf,eom) | 
|  | * requires: | 
|  | *	buf + HFIXEDSZ <= eom | 
|  | * returns: | 
|  | *	-1 : format error | 
|  | *	0  : not found | 
|  | *	>0 : found | 
|  | * author: | 
|  | *	paul vixie, 29may94 | 
|  | */ | 
|  | int | 
|  | res_nameinquery(const char *name, int type, int class, | 
|  | const u_char *buf, const u_char *eom) | 
|  | { | 
|  | const u_char *cp = buf + HFIXEDSZ; | 
|  | int qdcount = ntohs(((const HEADER*)(const void *)buf)->qdcount); | 
|  |  | 
|  | while (qdcount-- > 0) { | 
|  | char tname[MAXDNAME+1]; | 
|  | int n, ttype, tclass; | 
|  |  | 
|  | n = dn_expand(buf, eom, cp, tname, sizeof tname); | 
|  | if (n < 0) | 
|  | return (-1); | 
|  | cp += n; | 
|  | if (cp + 2 * INT16SZ > eom) | 
|  | return (-1); | 
|  | ttype = ns_get16(cp); cp += INT16SZ; | 
|  | tclass = ns_get16(cp); cp += INT16SZ; | 
|  | if (ttype == type && tclass == class && | 
|  | ns_samename(tname, name) == 1) | 
|  | return (1); | 
|  | } | 
|  | return (0); | 
|  | } | 
|  |  | 
|  | /* int | 
|  | * res_queriesmatch(buf1, eom1, buf2, eom2) | 
|  | *	is there a 1:1 mapping of (name,type,class) | 
|  | *	in (buf1,eom1) and (buf2,eom2)? | 
|  | * returns: | 
|  | *	-1 : format error | 
|  | *	0  : not a 1:1 mapping | 
|  | *	>0 : is a 1:1 mapping | 
|  | * author: | 
|  | *	paul vixie, 29may94 | 
|  | */ | 
|  | int | 
|  | res_queriesmatch(const u_char *buf1, const u_char *eom1, | 
|  | const u_char *buf2, const u_char *eom2) | 
|  | { | 
|  | const u_char *cp = buf1 + HFIXEDSZ; | 
|  | int qdcount = ntohs(((const HEADER*)(const void *)buf1)->qdcount); | 
|  |  | 
|  | if (buf1 + HFIXEDSZ > eom1 || buf2 + HFIXEDSZ > eom2) | 
|  | return (-1); | 
|  |  | 
|  | /* | 
|  | * Only header section present in replies to | 
|  | * dynamic update packets. | 
|  | */ | 
|  | if ((((const HEADER *)(const void *)buf1)->opcode == ns_o_update) && | 
|  | (((const HEADER *)(const void *)buf2)->opcode == ns_o_update)) | 
|  | return (1); | 
|  |  | 
|  | if (qdcount != ntohs(((const HEADER*)(const void *)buf2)->qdcount)) | 
|  | return (0); | 
|  | while (qdcount-- > 0) { | 
|  | char tname[MAXDNAME+1]; | 
|  | int n, ttype, tclass; | 
|  |  | 
|  | n = dn_expand(buf1, eom1, cp, tname, sizeof tname); | 
|  | if (n < 0) | 
|  | return (-1); | 
|  | cp += n; | 
|  | if (cp + 2 * INT16SZ > eom1) | 
|  | return (-1); | 
|  | ttype = ns_get16(cp);	cp += INT16SZ; | 
|  | tclass = ns_get16(cp); cp += INT16SZ; | 
|  | if (!res_nameinquery(tname, ttype, tclass, buf2, eom2)) | 
|  | return (0); | 
|  | } | 
|  | return (1); | 
|  | } | 
|  |  | 
|  | int | 
|  | res_nsend(res_state statp, | 
|  | const u_char *buf, int buflen, u_char *ans, int anssiz) | 
|  | { | 
|  | int gotsomewhere, terrno, try, v_circuit, resplen, ns, n; | 
|  | char abuf[NI_MAXHOST]; | 
|  | ResolvCacheStatus     cache_status = RESOLV_CACHE_UNSUPPORTED; | 
|  |  | 
|  | if (anssiz < HFIXEDSZ) { | 
|  | errno = EINVAL; | 
|  | return (-1); | 
|  | } | 
|  | DprintQ((statp->options & RES_DEBUG) || (statp->pfcode & RES_PRF_QUERY), | 
|  | (stdout, ";; res_send()\n"), buf, buflen); | 
|  | v_circuit = (statp->options & RES_USEVC) || buflen > PACKETSZ; | 
|  | gotsomewhere = 0; | 
|  | terrno = ETIMEDOUT; | 
|  |  | 
|  | int  anslen = 0; | 
|  | cache_status = _resolv_cache_lookup( | 
|  | statp->netid, buf, buflen, | 
|  | ans, anssiz, &anslen); | 
|  |  | 
|  | if (cache_status == RESOLV_CACHE_FOUND) { | 
|  | return anslen; | 
|  | } else if (cache_status != RESOLV_CACHE_UNSUPPORTED) { | 
|  | // had a cache miss for a known network, so populate the thread private | 
|  | // data so the normal resolve path can do its thing | 
|  | _resolv_populate_res_for_net(statp); | 
|  | } | 
|  | if (statp->nscount == 0) { | 
|  | // We have no nameservers configured, so there's no point trying. | 
|  | // Tell the cache the query failed, or any retries and anyone else asking the same | 
|  | // question will block for PENDING_REQUEST_TIMEOUT seconds instead of failing fast. | 
|  | _resolv_cache_query_failed(statp->netid, buf, buflen); | 
|  | errno = ESRCH; | 
|  | return (-1); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * If the ns_addr_list in the resolver context has changed, then | 
|  | * invalidate our cached copy and the associated timing data. | 
|  | */ | 
|  | if (EXT(statp).nscount != 0) { | 
|  | int needclose = 0; | 
|  | struct sockaddr_storage peer; | 
|  | socklen_t peerlen; | 
|  |  | 
|  | if (EXT(statp).nscount != statp->nscount) { | 
|  | needclose++; | 
|  | } else { | 
|  | for (ns = 0; ns < statp->nscount; ns++) { | 
|  | if (statp->nsaddr_list[ns].sin_family && | 
|  | !sock_eq((struct sockaddr *)(void *)&statp->nsaddr_list[ns], | 
|  | (struct sockaddr *)(void *)&EXT(statp).ext->nsaddrs[ns])) { | 
|  | needclose++; | 
|  | break; | 
|  | } | 
|  |  | 
|  | if (EXT(statp).nssocks[ns] == -1) | 
|  | continue; | 
|  | peerlen = sizeof(peer); | 
|  | if (getpeername(EXT(statp).nssocks[ns], | 
|  | (struct sockaddr *)(void *)&peer, &peerlen) < 0) { | 
|  | needclose++; | 
|  | break; | 
|  | } | 
|  | if (!sock_eq((struct sockaddr *)(void *)&peer, | 
|  | get_nsaddr(statp, (size_t)ns))) { | 
|  | needclose++; | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  | if (needclose) { | 
|  | res_nclose(statp); | 
|  | EXT(statp).nscount = 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Maybe initialize our private copy of the ns_addr_list. | 
|  | */ | 
|  | if (EXT(statp).nscount == 0) { | 
|  | for (ns = 0; ns < statp->nscount; ns++) { | 
|  | EXT(statp).nstimes[ns] = RES_MAXTIME; | 
|  | EXT(statp).nssocks[ns] = -1; | 
|  | if (!statp->nsaddr_list[ns].sin_family) | 
|  | continue; | 
|  | EXT(statp).ext->nsaddrs[ns].sin = | 
|  | statp->nsaddr_list[ns]; | 
|  | } | 
|  | EXT(statp).nscount = statp->nscount; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Some resolvers want to even out the load on their nameservers. | 
|  | * Note that RES_BLAST overrides RES_ROTATE. | 
|  | */ | 
|  | if ((statp->options & RES_ROTATE) != 0U && | 
|  | (statp->options & RES_BLAST) == 0U) { | 
|  | union res_sockaddr_union inu; | 
|  | struct sockaddr_in ina; | 
|  | int lastns = statp->nscount - 1; | 
|  | int fd; | 
|  | u_int16_t nstime; | 
|  |  | 
|  | if (EXT(statp).ext != NULL) | 
|  | inu = EXT(statp).ext->nsaddrs[0]; | 
|  | ina = statp->nsaddr_list[0]; | 
|  | fd = EXT(statp).nssocks[0]; | 
|  | nstime = EXT(statp).nstimes[0]; | 
|  | for (ns = 0; ns < lastns; ns++) { | 
|  | if (EXT(statp).ext != NULL) | 
|  | EXT(statp).ext->nsaddrs[ns] = | 
|  | EXT(statp).ext->nsaddrs[ns + 1]; | 
|  | statp->nsaddr_list[ns] = statp->nsaddr_list[ns + 1]; | 
|  | EXT(statp).nssocks[ns] = EXT(statp).nssocks[ns + 1]; | 
|  | EXT(statp).nstimes[ns] = EXT(statp).nstimes[ns + 1]; | 
|  | } | 
|  | if (EXT(statp).ext != NULL) | 
|  | EXT(statp).ext->nsaddrs[lastns] = inu; | 
|  | statp->nsaddr_list[lastns] = ina; | 
|  | EXT(statp).nssocks[lastns] = fd; | 
|  | EXT(statp).nstimes[lastns] = nstime; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Send request, RETRY times, or until successful. | 
|  | */ | 
|  | for (try = 0; try < statp->retry; try++) { | 
|  | struct __res_stats stats[MAXNS]; | 
|  | struct __res_params params; | 
|  | int revision_id = _resolv_cache_get_resolver_stats(statp->netid, ¶ms, stats); | 
|  | bool usable_servers[MAXNS]; | 
|  | android_net_res_stats_get_usable_servers(¶ms, stats, statp->nscount, | 
|  | usable_servers); | 
|  |  | 
|  | for (ns = 0; ns < statp->nscount; ns++) { | 
|  | if (!usable_servers[ns]) continue; | 
|  | struct sockaddr *nsap; | 
|  | int nsaplen; | 
|  | time_t now = 0; | 
|  | int rcode = RCODE_INTERNAL_ERROR; | 
|  | int delay = 0; | 
|  | nsap = get_nsaddr(statp, (size_t)ns); | 
|  | nsaplen = get_salen(nsap); | 
|  | statp->_flags &= ~RES_F_LASTMASK; | 
|  | statp->_flags |= (ns << RES_F_LASTSHIFT); | 
|  |  | 
|  | same_ns: | 
|  | if (statp->qhook) { | 
|  | int done = 0, loops = 0; | 
|  |  | 
|  | do { | 
|  | res_sendhookact act; | 
|  |  | 
|  | act = (*statp->qhook)(&nsap, &buf, &buflen, | 
|  | ans, anssiz, &resplen); | 
|  | switch (act) { | 
|  | case res_goahead: | 
|  | done = 1; | 
|  | break; | 
|  | case res_nextns: | 
|  | res_nclose(statp); | 
|  | goto next_ns; | 
|  | case res_done: | 
|  | return (resplen); | 
|  | case res_modified: | 
|  | /* give the hook another try */ | 
|  | if (++loops < 42) /*doug adams*/ | 
|  | break; | 
|  | /*FALLTHROUGH*/ | 
|  | case res_error: | 
|  | /*FALLTHROUGH*/ | 
|  | default: | 
|  | goto fail; | 
|  | } | 
|  | } while (!done); | 
|  | } | 
|  |  | 
|  | Dprint(((statp->options & RES_DEBUG) && | 
|  | getnameinfo(nsap, (socklen_t)nsaplen, abuf, sizeof(abuf), | 
|  | NULL, 0, niflags) == 0), | 
|  | (stdout, ";; Querying server (# %d) address = %s\n", | 
|  | ns + 1, abuf)); | 
|  |  | 
|  |  | 
|  | if (v_circuit) { | 
|  | /* Use VC; at most one attempt per server. */ | 
|  | try = statp->retry; | 
|  |  | 
|  | n = send_vc(statp, buf, buflen, ans, anssiz, &terrno, | 
|  | ns, &now, &rcode, &delay); | 
|  |  | 
|  | /* | 
|  | * Only record stats the first time we try a query. This ensures that | 
|  | * queries that deterministically fail (e.g., a name that always returns | 
|  | * SERVFAIL or times out) do not unduly affect the stats. | 
|  | */ | 
|  | if (try == 0) { | 
|  | struct __res_sample sample; | 
|  | _res_stats_set_sample(&sample, now, rcode, delay); | 
|  | _resolv_cache_add_resolver_stats_sample(statp->netid, revision_id, | 
|  | ns, &sample, params.max_samples); | 
|  | } | 
|  |  | 
|  | if (DBG) { | 
|  | __libc_format_log(ANDROID_LOG_DEBUG, "libc", | 
|  | "used send_vc %d\n", n); | 
|  | } | 
|  |  | 
|  | if (n < 0) | 
|  | goto fail; | 
|  | if (n == 0) | 
|  | goto next_ns; | 
|  | resplen = n; | 
|  | } else { | 
|  | /* Use datagrams. */ | 
|  | if (DBG) { | 
|  | __libc_format_log(ANDROID_LOG_DEBUG, "libc", "using send_dg\n"); | 
|  | } | 
|  |  | 
|  | n = send_dg(statp, buf, buflen, ans, anssiz, &terrno, | 
|  | ns, &v_circuit, &gotsomewhere, &now, &rcode, &delay); | 
|  |  | 
|  | /* Only record stats the first time we try a query. See above. */ | 
|  | if (try == 0) { | 
|  | struct __res_sample sample; | 
|  | _res_stats_set_sample(&sample, now, rcode, delay); | 
|  | _resolv_cache_add_resolver_stats_sample(statp->netid, revision_id, | 
|  | ns, &sample, params.max_samples); | 
|  | } | 
|  |  | 
|  | if (DBG) { | 
|  | __libc_format_log(ANDROID_LOG_DEBUG, "libc", "used send_dg %d\n",n); | 
|  | } | 
|  |  | 
|  | if (n < 0) | 
|  | goto fail; | 
|  | if (n == 0) | 
|  | goto next_ns; | 
|  | if (DBG) { | 
|  | __libc_format_log(ANDROID_LOG_DEBUG, "libc", "time=%ld\n", | 
|  | time(NULL)); | 
|  | } | 
|  | if (v_circuit) | 
|  | goto same_ns; | 
|  | resplen = n; | 
|  | } | 
|  |  | 
|  | Dprint((statp->options & RES_DEBUG) || | 
|  | ((statp->pfcode & RES_PRF_REPLY) && | 
|  | (statp->pfcode & RES_PRF_HEAD1)), | 
|  | (stdout, ";; got answer:\n")); | 
|  |  | 
|  | DprintQ((statp->options & RES_DEBUG) || | 
|  | (statp->pfcode & RES_PRF_REPLY), | 
|  | (stdout, "%s", ""), | 
|  | ans, (resplen > anssiz) ? anssiz : resplen); | 
|  |  | 
|  | if (cache_status == RESOLV_CACHE_NOTFOUND) { | 
|  | _resolv_cache_add(statp->netid, buf, buflen, | 
|  | ans, resplen); | 
|  | } | 
|  | /* | 
|  | * If we have temporarily opened a virtual circuit, | 
|  | * or if we haven't been asked to keep a socket open, | 
|  | * close the socket. | 
|  | */ | 
|  | if ((v_circuit && (statp->options & RES_USEVC) == 0U) || | 
|  | (statp->options & RES_STAYOPEN) == 0U) { | 
|  | res_nclose(statp); | 
|  | } | 
|  | if (statp->rhook) { | 
|  | int done = 0, loops = 0; | 
|  |  | 
|  | do { | 
|  | res_sendhookact act; | 
|  |  | 
|  | act = (*statp->rhook)(nsap, buf, buflen, | 
|  | ans, anssiz, &resplen); | 
|  | switch (act) { | 
|  | case res_goahead: | 
|  | case res_done: | 
|  | done = 1; | 
|  | break; | 
|  | case res_nextns: | 
|  | res_nclose(statp); | 
|  | goto next_ns; | 
|  | case res_modified: | 
|  | /* give the hook another try */ | 
|  | if (++loops < 42) /*doug adams*/ | 
|  | break; | 
|  | /*FALLTHROUGH*/ | 
|  | case res_error: | 
|  | /*FALLTHROUGH*/ | 
|  | default: | 
|  | goto fail; | 
|  | } | 
|  | } while (!done); | 
|  |  | 
|  | } | 
|  | return (resplen); | 
|  | next_ns: ; | 
|  | } /*foreach ns*/ | 
|  | } /*foreach retry*/ | 
|  | res_nclose(statp); | 
|  | if (!v_circuit) { | 
|  | if (!gotsomewhere) | 
|  | errno = ECONNREFUSED;	/* no nameservers found */ | 
|  | else | 
|  | errno = ETIMEDOUT;	/* no answer obtained */ | 
|  | } else | 
|  | errno = terrno; | 
|  |  | 
|  | _resolv_cache_query_failed(statp->netid, buf, buflen); | 
|  |  | 
|  | return (-1); | 
|  | fail: | 
|  |  | 
|  | _resolv_cache_query_failed(statp->netid, buf, buflen); | 
|  | res_nclose(statp); | 
|  | return (-1); | 
|  | } | 
|  |  | 
|  | /* Private */ | 
|  |  | 
|  | static int | 
|  | get_salen(sa) | 
|  | const struct sockaddr *sa; | 
|  | { | 
|  |  | 
|  | #ifdef HAVE_SA_LEN | 
|  | /* There are people do not set sa_len.  Be forgiving to them. */ | 
|  | if (sa->sa_len) | 
|  | return (sa->sa_len); | 
|  | #endif | 
|  |  | 
|  | if (sa->sa_family == AF_INET) | 
|  | return (sizeof(struct sockaddr_in)); | 
|  | else if (sa->sa_family == AF_INET6) | 
|  | return (sizeof(struct sockaddr_in6)); | 
|  | else | 
|  | return (0);	/* unknown, die on connect */ | 
|  | } | 
|  |  | 
|  | /* | 
|  | * pick appropriate nsaddr_list for use.  see res_init() for initialization. | 
|  | */ | 
|  | static struct sockaddr * | 
|  | get_nsaddr(statp, n) | 
|  | res_state statp; | 
|  | size_t n; | 
|  | { | 
|  |  | 
|  | if (!statp->nsaddr_list[n].sin_family && EXT(statp).ext) { | 
|  | /* | 
|  | * - EXT(statp).ext->nsaddrs[n] holds an address that is larger | 
|  | *   than struct sockaddr, and | 
|  | * - user code did not update statp->nsaddr_list[n]. | 
|  | */ | 
|  | return (struct sockaddr *)(void *)&EXT(statp).ext->nsaddrs[n]; | 
|  | } else { | 
|  | /* | 
|  | * - user code updated statp->nsaddr_list[n], or | 
|  | * - statp->nsaddr_list[n] has the same content as | 
|  | *   EXT(statp).ext->nsaddrs[n]. | 
|  | */ | 
|  | return (struct sockaddr *)(void *)&statp->nsaddr_list[n]; | 
|  | } | 
|  | } | 
|  |  | 
|  | static int get_timeout(const res_state statp, const int ns) | 
|  | { | 
|  | int timeout = (statp->retrans << ns); | 
|  | if (ns > 0) { | 
|  | timeout /= statp->nscount; | 
|  | } | 
|  | if (timeout <= 0) { | 
|  | timeout = 1; | 
|  | } | 
|  | if (DBG) { | 
|  | __libc_format_log(ANDROID_LOG_DEBUG, "libc", "using timeout of %d sec\n", timeout); | 
|  | } | 
|  |  | 
|  | return timeout; | 
|  | } | 
|  |  | 
|  | static int | 
|  | send_vc(res_state statp, | 
|  | const u_char *buf, int buflen, u_char *ans, int anssiz, | 
|  | int *terrno, int ns, time_t* at, int* rcode, int* delay) | 
|  | { | 
|  | *at = time(NULL); | 
|  | *rcode = RCODE_INTERNAL_ERROR; | 
|  | *delay = 0; | 
|  | const HEADER *hp = (const HEADER *)(const void *)buf; | 
|  | HEADER *anhp = (HEADER *)(void *)ans; | 
|  | struct sockaddr *nsap; | 
|  | int nsaplen; | 
|  | int truncating, connreset, resplen, n; | 
|  | struct iovec iov[2]; | 
|  | u_short len; | 
|  | u_char *cp; | 
|  | void *tmp; | 
|  |  | 
|  | if (DBG) { | 
|  | __libc_format_log(ANDROID_LOG_DEBUG, "libc", "using send_vc\n"); | 
|  | } | 
|  |  | 
|  | nsap = get_nsaddr(statp, (size_t)ns); | 
|  | nsaplen = get_salen(nsap); | 
|  |  | 
|  | connreset = 0; | 
|  | same_ns: | 
|  | truncating = 0; | 
|  |  | 
|  | struct timespec now = evNowTime(); | 
|  |  | 
|  | /* Are we still talking to whom we want to talk to? */ | 
|  | if (statp->_vcsock >= 0 && (statp->_flags & RES_F_VC) != 0) { | 
|  | struct sockaddr_storage peer; | 
|  | socklen_t size = sizeof peer; | 
|  | unsigned old_mark; | 
|  | socklen_t mark_size = sizeof(old_mark); | 
|  | if (getpeername(statp->_vcsock, | 
|  | (struct sockaddr *)(void *)&peer, &size) < 0 || | 
|  | !sock_eq((struct sockaddr *)(void *)&peer, nsap) || | 
|  | getsockopt(statp->_vcsock, SOL_SOCKET, SO_MARK, &old_mark, &mark_size) < 0 || | 
|  | old_mark != statp->_mark) { | 
|  | res_nclose(statp); | 
|  | statp->_flags &= ~RES_F_VC; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (statp->_vcsock < 0 || (statp->_flags & RES_F_VC) == 0) { | 
|  | if (statp->_vcsock >= 0) | 
|  | res_nclose(statp); | 
|  |  | 
|  | statp->_vcsock = socket(nsap->sa_family, SOCK_STREAM | SOCK_CLOEXEC, 0); | 
|  | if (statp->_vcsock > highestFD) { | 
|  | res_nclose(statp); | 
|  | errno = ENOTSOCK; | 
|  | } | 
|  | if (statp->_vcsock < 0) { | 
|  | switch (errno) { | 
|  | case EPROTONOSUPPORT: | 
|  | #ifdef EPFNOSUPPORT | 
|  | case EPFNOSUPPORT: | 
|  | #endif | 
|  | case EAFNOSUPPORT: | 
|  | Perror(statp, stderr, "socket(vc)", errno); | 
|  | return (0); | 
|  | default: | 
|  | *terrno = errno; | 
|  | Perror(statp, stderr, "socket(vc)", errno); | 
|  | return (-1); | 
|  | } | 
|  | } | 
|  | if (statp->_mark != MARK_UNSET) { | 
|  | if (setsockopt(statp->_vcsock, SOL_SOCKET, | 
|  | SO_MARK, &statp->_mark, sizeof(statp->_mark)) < 0) { | 
|  | *terrno = errno; | 
|  | Perror(statp, stderr, "setsockopt", errno); | 
|  | return -1; | 
|  | } | 
|  | } | 
|  | errno = 0; | 
|  | if (random_bind(statp->_vcsock,nsap->sa_family) < 0) { | 
|  | *terrno = errno; | 
|  | Aerror(statp, stderr, "bind/vc", errno, nsap, | 
|  | nsaplen); | 
|  | res_nclose(statp); | 
|  | return (0); | 
|  | } | 
|  | if (connect_with_timeout(statp->_vcsock, nsap, (socklen_t)nsaplen, | 
|  | get_timeout(statp, ns)) < 0) { | 
|  | *terrno = errno; | 
|  | Aerror(statp, stderr, "connect/vc", errno, nsap, | 
|  | nsaplen); | 
|  | res_nclose(statp); | 
|  | /* | 
|  | * The way connect_with_timeout() is implemented prevents us from reliably | 
|  | * determining whether this was really a timeout or e.g. ECONNREFUSED. Since | 
|  | * currently both cases are handled in the same way, there is no need to | 
|  | * change this (yet). If we ever need to reliably distinguish between these | 
|  | * cases, both connect_with_timeout() and retrying_select() need to be | 
|  | * modified, though. | 
|  | */ | 
|  | *rcode = RCODE_TIMEOUT; | 
|  | return (0); | 
|  | } | 
|  | statp->_flags |= RES_F_VC; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Send length & message | 
|  | */ | 
|  | ns_put16((u_short)buflen, (u_char*)(void *)&len); | 
|  | iov[0] = evConsIovec(&len, INT16SZ); | 
|  | DE_CONST(buf, tmp); | 
|  | iov[1] = evConsIovec(tmp, (size_t)buflen); | 
|  | if (writev(statp->_vcsock, iov, 2) != (INT16SZ + buflen)) { | 
|  | *terrno = errno; | 
|  | Perror(statp, stderr, "write failed", errno); | 
|  | res_nclose(statp); | 
|  | return (0); | 
|  | } | 
|  | /* | 
|  | * Receive length & response | 
|  | */ | 
|  | read_len: | 
|  | cp = ans; | 
|  | len = INT16SZ; | 
|  | while ((n = read(statp->_vcsock, (char *)cp, (size_t)len)) > 0) { | 
|  | cp += n; | 
|  | if ((len -= n) == 0) | 
|  | break; | 
|  | } | 
|  | if (n <= 0) { | 
|  | *terrno = errno; | 
|  | Perror(statp, stderr, "read failed", errno); | 
|  | res_nclose(statp); | 
|  | /* | 
|  | * A long running process might get its TCP | 
|  | * connection reset if the remote server was | 
|  | * restarted.  Requery the server instead of | 
|  | * trying a new one.  When there is only one | 
|  | * server, this means that a query might work | 
|  | * instead of failing.  We only allow one reset | 
|  | * per query to prevent looping. | 
|  | */ | 
|  | if (*terrno == ECONNRESET && !connreset) { | 
|  | connreset = 1; | 
|  | res_nclose(statp); | 
|  | goto same_ns; | 
|  | } | 
|  | res_nclose(statp); | 
|  | return (0); | 
|  | } | 
|  | resplen = ns_get16(ans); | 
|  | if (resplen > anssiz) { | 
|  | Dprint(statp->options & RES_DEBUG, | 
|  | (stdout, ";; response truncated\n") | 
|  | ); | 
|  | truncating = 1; | 
|  | len = anssiz; | 
|  | } else | 
|  | len = resplen; | 
|  | if (len < HFIXEDSZ) { | 
|  | /* | 
|  | * Undersized message. | 
|  | */ | 
|  | Dprint(statp->options & RES_DEBUG, | 
|  | (stdout, ";; undersized: %d\n", len)); | 
|  | *terrno = EMSGSIZE; | 
|  | res_nclose(statp); | 
|  | return (0); | 
|  | } | 
|  | cp = ans; | 
|  | while (len != 0 && (n = read(statp->_vcsock, (char *)cp, (size_t)len)) > 0){ | 
|  | cp += n; | 
|  | len -= n; | 
|  | } | 
|  | if (n <= 0) { | 
|  | *terrno = errno; | 
|  | Perror(statp, stderr, "read(vc)", errno); | 
|  | res_nclose(statp); | 
|  | return (0); | 
|  | } | 
|  |  | 
|  | if (truncating) { | 
|  | /* | 
|  | * Flush rest of answer so connection stays in synch. | 
|  | */ | 
|  | anhp->tc = 1; | 
|  | len = resplen - anssiz; | 
|  | while (len != 0) { | 
|  | char junk[PACKETSZ]; | 
|  |  | 
|  | n = read(statp->_vcsock, junk, | 
|  | (len > sizeof junk) ? sizeof junk : len); | 
|  | if (n > 0) | 
|  | len -= n; | 
|  | else | 
|  | break; | 
|  | } | 
|  | } | 
|  | /* | 
|  | * If the calling applicating has bailed out of | 
|  | * a previous call and failed to arrange to have | 
|  | * the circuit closed or the server has got | 
|  | * itself confused, then drop the packet and | 
|  | * wait for the correct one. | 
|  | */ | 
|  | if (hp->id != anhp->id) { | 
|  | DprintQ((statp->options & RES_DEBUG) || | 
|  | (statp->pfcode & RES_PRF_REPLY), | 
|  | (stdout, ";; old answer (unexpected):\n"), | 
|  | ans, (resplen > anssiz) ? anssiz: resplen); | 
|  | goto read_len; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * All is well, or the error is fatal.  Signal that the | 
|  | * next nameserver ought not be tried. | 
|  | */ | 
|  | if (resplen > 0) { | 
|  | struct timespec done = evNowTime(); | 
|  | *delay = _res_stats_calculate_rtt(&done, &now); | 
|  | *rcode = anhp->rcode; | 
|  | } | 
|  | return (resplen); | 
|  | } | 
|  |  | 
|  | /* return -1 on error (errno set), 0 on success */ | 
|  | static int | 
|  | connect_with_timeout(int sock, const struct sockaddr *nsap, socklen_t salen, int sec) | 
|  | { | 
|  | int res, origflags; | 
|  | fd_set rset, wset; | 
|  | struct timespec now, timeout, finish; | 
|  |  | 
|  | origflags = fcntl(sock, F_GETFL, 0); | 
|  | fcntl(sock, F_SETFL, origflags | O_NONBLOCK); | 
|  |  | 
|  | res = __connect(sock, nsap, salen); | 
|  | if (res < 0 && errno != EINPROGRESS) { | 
|  | res = -1; | 
|  | goto done; | 
|  | } | 
|  | if (res != 0) { | 
|  | now = evNowTime(); | 
|  | timeout = evConsTime((long)sec, 0L); | 
|  | finish = evAddTime(now, timeout); | 
|  | if (DBG) { | 
|  | __libc_format_log(ANDROID_LOG_DEBUG, "libc", "  %d send_vc\n", sock); | 
|  | } | 
|  |  | 
|  | res = retrying_select(sock, &rset, &wset, &finish); | 
|  | if (res <= 0) { | 
|  | res = -1; | 
|  | } | 
|  | } | 
|  | done: | 
|  | fcntl(sock, F_SETFL, origflags); | 
|  | if (DBG) { | 
|  | __libc_format_log(ANDROID_LOG_DEBUG, "libc", | 
|  | "  %d connect_with_timeout returning %d\n", sock, res); | 
|  | } | 
|  | return res; | 
|  | } | 
|  |  | 
|  | static int | 
|  | retrying_select(const int sock, fd_set *readset, fd_set *writeset, const struct timespec *finish) | 
|  | { | 
|  | struct timespec now, timeout; | 
|  | int n, error; | 
|  | socklen_t len; | 
|  |  | 
|  |  | 
|  | retry: | 
|  | if (DBG) { | 
|  | __libc_format_log(ANDROID_LOG_DEBUG, "libc", "  %d retrying_select\n", sock); | 
|  | } | 
|  |  | 
|  | now = evNowTime(); | 
|  | if (readset) { | 
|  | FD_ZERO(readset); | 
|  | FD_SET(sock, readset); | 
|  | } | 
|  | if (writeset) { | 
|  | FD_ZERO(writeset); | 
|  | FD_SET(sock, writeset); | 
|  | } | 
|  | if (evCmpTime(*finish, now) > 0) | 
|  | timeout = evSubTime(*finish, now); | 
|  | else | 
|  | timeout = evConsTime(0L, 0L); | 
|  |  | 
|  | n = pselect(sock + 1, readset, writeset, NULL, &timeout, NULL); | 
|  | if (n == 0) { | 
|  | if (DBG) { | 
|  | __libc_format_log(ANDROID_LOG_DEBUG, " libc", | 
|  | "  %d retrying_select timeout\n", sock); | 
|  | } | 
|  | errno = ETIMEDOUT; | 
|  | return 0; | 
|  | } | 
|  | if (n < 0) { | 
|  | if (errno == EINTR) | 
|  | goto retry; | 
|  | if (DBG) { | 
|  | __libc_format_log(ANDROID_LOG_DEBUG, "libc", | 
|  | "  %d retrying_select got error %d\n",sock, n); | 
|  | } | 
|  | return n; | 
|  | } | 
|  | if ((readset && FD_ISSET(sock, readset)) || (writeset && FD_ISSET(sock, writeset))) { | 
|  | len = sizeof(error); | 
|  | if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &error, &len) < 0 || error) { | 
|  | errno = error; | 
|  | if (DBG) { | 
|  | __libc_format_log(ANDROID_LOG_DEBUG, "libc", | 
|  | "  %d retrying_select dot error2 %d\n", sock, errno); | 
|  | } | 
|  |  | 
|  | return -1; | 
|  | } | 
|  | } | 
|  | if (DBG) { | 
|  | __libc_format_log(ANDROID_LOG_DEBUG, "libc", | 
|  | "  %d retrying_select returning %d\n",sock, n); | 
|  | } | 
|  |  | 
|  | return n; | 
|  | } | 
|  |  | 
|  | static int | 
|  | send_dg(res_state statp, | 
|  | const u_char *buf, int buflen, u_char *ans, int anssiz, | 
|  | int *terrno, int ns, int *v_circuit, int *gotsomewhere, | 
|  | time_t *at, int *rcode, int* delay) | 
|  | { | 
|  | *at = time(NULL); | 
|  | *rcode = RCODE_INTERNAL_ERROR; | 
|  | *delay = 0; | 
|  | const HEADER *hp = (const HEADER *)(const void *)buf; | 
|  | HEADER *anhp = (HEADER *)(void *)ans; | 
|  | const struct sockaddr *nsap; | 
|  | int nsaplen; | 
|  | struct timespec now, timeout, finish, done; | 
|  | fd_set dsmask; | 
|  | struct sockaddr_storage from; | 
|  | socklen_t fromlen; | 
|  | int resplen, seconds, n, s; | 
|  |  | 
|  | nsap = get_nsaddr(statp, (size_t)ns); | 
|  | nsaplen = get_salen(nsap); | 
|  | if (EXT(statp).nssocks[ns] == -1) { | 
|  | EXT(statp).nssocks[ns] = socket(nsap->sa_family, SOCK_DGRAM | SOCK_CLOEXEC, 0); | 
|  | if (EXT(statp).nssocks[ns] > highestFD) { | 
|  | res_nclose(statp); | 
|  | errno = ENOTSOCK; | 
|  | } | 
|  | if (EXT(statp).nssocks[ns] < 0) { | 
|  | switch (errno) { | 
|  | case EPROTONOSUPPORT: | 
|  | #ifdef EPFNOSUPPORT | 
|  | case EPFNOSUPPORT: | 
|  | #endif | 
|  | case EAFNOSUPPORT: | 
|  | Perror(statp, stderr, "socket(dg)", errno); | 
|  | return (0); | 
|  | default: | 
|  | *terrno = errno; | 
|  | Perror(statp, stderr, "socket(dg)", errno); | 
|  | return (-1); | 
|  | } | 
|  | } | 
|  |  | 
|  | if (statp->_mark != MARK_UNSET) { | 
|  | if (setsockopt(EXT(statp).nssocks[ns], SOL_SOCKET, | 
|  | SO_MARK, &(statp->_mark), sizeof(statp->_mark)) < 0) { | 
|  | res_nclose(statp); | 
|  | return -1; | 
|  | } | 
|  | } | 
|  | #ifndef CANNOT_CONNECT_DGRAM | 
|  | /* | 
|  | * On a 4.3BSD+ machine (client and server, | 
|  | * actually), sending to a nameserver datagram | 
|  | * port with no nameserver will cause an | 
|  | * ICMP port unreachable message to be returned. | 
|  | * If our datagram socket is "connected" to the | 
|  | * server, we get an ECONNREFUSED error on the next | 
|  | * socket operation, and select returns if the | 
|  | * error message is received.  We can thus detect | 
|  | * the absence of a nameserver without timing out. | 
|  | */ | 
|  | if (random_bind(EXT(statp).nssocks[ns], nsap->sa_family) < 0) { | 
|  | Aerror(statp, stderr, "bind(dg)", errno, nsap, | 
|  | nsaplen); | 
|  | res_nclose(statp); | 
|  | return (0); | 
|  | } | 
|  | if (__connect(EXT(statp).nssocks[ns], nsap, (socklen_t)nsaplen) < 0) { | 
|  | Aerror(statp, stderr, "connect(dg)", errno, nsap, | 
|  | nsaplen); | 
|  | res_nclose(statp); | 
|  | return (0); | 
|  | } | 
|  | #endif /* !CANNOT_CONNECT_DGRAM */ | 
|  | Dprint(statp->options & RES_DEBUG, | 
|  | (stdout, ";; new DG socket\n")) | 
|  |  | 
|  | } | 
|  | s = EXT(statp).nssocks[ns]; | 
|  | #ifndef CANNOT_CONNECT_DGRAM | 
|  | if (send(s, (const char*)buf, (size_t)buflen, 0) != buflen) { | 
|  | Perror(statp, stderr, "send", errno); | 
|  | res_nclose(statp); | 
|  | return (0); | 
|  | } | 
|  | #else /* !CANNOT_CONNECT_DGRAM */ | 
|  | if (sendto(s, (const char*)buf, buflen, 0, nsap, nsaplen) != buflen) | 
|  | { | 
|  | Aerror(statp, stderr, "sendto", errno, nsap, nsaplen); | 
|  | res_nclose(statp); | 
|  | return (0); | 
|  | } | 
|  | #endif /* !CANNOT_CONNECT_DGRAM */ | 
|  |  | 
|  | /* | 
|  | * Wait for reply. | 
|  | */ | 
|  | seconds = get_timeout(statp, ns); | 
|  | now = evNowTime(); | 
|  | timeout = evConsTime((long)seconds, 0L); | 
|  | finish = evAddTime(now, timeout); | 
|  | retry: | 
|  | n = retrying_select(s, &dsmask, NULL, &finish); | 
|  |  | 
|  | if (n == 0) { | 
|  | *rcode = RCODE_TIMEOUT; | 
|  | Dprint(statp->options & RES_DEBUG, (stdout, ";; timeout\n")); | 
|  | *gotsomewhere = 1; | 
|  | return (0); | 
|  | } | 
|  | if (n < 0) { | 
|  | Perror(statp, stderr, "select", errno); | 
|  | res_nclose(statp); | 
|  | return (0); | 
|  | } | 
|  | errno = 0; | 
|  | fromlen = sizeof(from); | 
|  | resplen = recvfrom(s, (char*)ans, (size_t)anssiz,0, | 
|  | (struct sockaddr *)(void *)&from, &fromlen); | 
|  | if (resplen <= 0) { | 
|  | Perror(statp, stderr, "recvfrom", errno); | 
|  | res_nclose(statp); | 
|  | return (0); | 
|  | } | 
|  | *gotsomewhere = 1; | 
|  | if (resplen < HFIXEDSZ) { | 
|  | /* | 
|  | * Undersized message. | 
|  | */ | 
|  | Dprint(statp->options & RES_DEBUG, | 
|  | (stdout, ";; undersized: %d\n", | 
|  | resplen)); | 
|  | *terrno = EMSGSIZE; | 
|  | res_nclose(statp); | 
|  | return (0); | 
|  | } | 
|  | if (hp->id != anhp->id) { | 
|  | /* | 
|  | * response from old query, ignore it. | 
|  | * XXX - potential security hazard could | 
|  | *	 be detected here. | 
|  | */ | 
|  | DprintQ((statp->options & RES_DEBUG) || | 
|  | (statp->pfcode & RES_PRF_REPLY), | 
|  | (stdout, ";; old answer:\n"), | 
|  | ans, (resplen > anssiz) ? anssiz : resplen); | 
|  | goto retry; | 
|  | } | 
|  | if (!(statp->options & RES_INSECURE1) && | 
|  | !res_ourserver_p(statp, (struct sockaddr *)(void *)&from)) { | 
|  | /* | 
|  | * response from wrong server? ignore it. | 
|  | * XXX - potential security hazard could | 
|  | *	 be detected here. | 
|  | */ | 
|  | DprintQ((statp->options & RES_DEBUG) || | 
|  | (statp->pfcode & RES_PRF_REPLY), | 
|  | (stdout, ";; not our server:\n"), | 
|  | ans, (resplen > anssiz) ? anssiz : resplen); | 
|  | goto retry; | 
|  | } | 
|  | #ifdef RES_USE_EDNS0 | 
|  | if (anhp->rcode == FORMERR && (statp->options & RES_USE_EDNS0) != 0U) { | 
|  | /* | 
|  | * Do not retry if the server do not understand EDNS0. | 
|  | * The case has to be captured here, as FORMERR packet do not | 
|  | * carry query section, hence res_queriesmatch() returns 0. | 
|  | */ | 
|  | DprintQ(statp->options & RES_DEBUG, | 
|  | (stdout, "server rejected query with EDNS0:\n"), | 
|  | ans, (resplen > anssiz) ? anssiz : resplen); | 
|  | /* record the error */ | 
|  | statp->_flags |= RES_F_EDNS0ERR; | 
|  | res_nclose(statp); | 
|  | return (0); | 
|  | } | 
|  | #endif | 
|  | if (!(statp->options & RES_INSECURE2) && | 
|  | !res_queriesmatch(buf, buf + buflen, | 
|  | ans, ans + anssiz)) { | 
|  | /* | 
|  | * response contains wrong query? ignore it. | 
|  | * XXX - potential security hazard could | 
|  | *	 be detected here. | 
|  | */ | 
|  | DprintQ((statp->options & RES_DEBUG) || | 
|  | (statp->pfcode & RES_PRF_REPLY), | 
|  | (stdout, ";; wrong query name:\n"), | 
|  | ans, (resplen > anssiz) ? anssiz : resplen); | 
|  | goto retry;; | 
|  | } | 
|  | done = evNowTime(); | 
|  | *delay = _res_stats_calculate_rtt(&done, &now); | 
|  | if (anhp->rcode == SERVFAIL || | 
|  | anhp->rcode == NOTIMP || | 
|  | anhp->rcode == REFUSED) { | 
|  | DprintQ(statp->options & RES_DEBUG, | 
|  | (stdout, "server rejected query:\n"), | 
|  | ans, (resplen > anssiz) ? anssiz : resplen); | 
|  | res_nclose(statp); | 
|  | /* don't retry if called from dig */ | 
|  | if (!statp->pfcode) { | 
|  | *rcode = anhp->rcode; | 
|  | return (0); | 
|  | } | 
|  | } | 
|  | if (!(statp->options & RES_IGNTC) && anhp->tc) { | 
|  | /* | 
|  | * To get the rest of answer, | 
|  | * use TCP with same server. | 
|  | */ | 
|  | Dprint(statp->options & RES_DEBUG, | 
|  | (stdout, ";; truncated answer\n")); | 
|  | *v_circuit = 1; | 
|  | res_nclose(statp); | 
|  | return (1); | 
|  | } | 
|  | /* | 
|  | * All is well, or the error is fatal.  Signal that the | 
|  | * next nameserver ought not be tried. | 
|  | */ | 
|  | if (resplen > 0) { | 
|  | *rcode = anhp->rcode; | 
|  | } | 
|  | return (resplen); | 
|  | } | 
|  |  | 
|  | static void | 
|  | Aerror(const res_state statp, FILE *file, const char *string, int error, | 
|  | const struct sockaddr *address, int alen) | 
|  | { | 
|  | int save = errno; | 
|  | char hbuf[NI_MAXHOST]; | 
|  | char sbuf[NI_MAXSERV]; | 
|  |  | 
|  | if ((statp->options & RES_DEBUG) != 0U) { | 
|  | if (getnameinfo(address, (socklen_t)alen, hbuf, sizeof(hbuf), | 
|  | sbuf, sizeof(sbuf), niflags)) { | 
|  | strncpy(hbuf, "?", sizeof(hbuf) - 1); | 
|  | hbuf[sizeof(hbuf) - 1] = '\0'; | 
|  | strncpy(sbuf, "?", sizeof(sbuf) - 1); | 
|  | sbuf[sizeof(sbuf) - 1] = '\0'; | 
|  | } | 
|  | fprintf(file, "res_send: %s ([%s].%s): %s\n", | 
|  | string, hbuf, sbuf, strerror(error)); | 
|  | } | 
|  | errno = save; | 
|  | } | 
|  |  | 
|  | static void | 
|  | Perror(const res_state statp, FILE *file, const char *string, int error) { | 
|  | int save = errno; | 
|  |  | 
|  | if ((statp->options & RES_DEBUG) != 0U) | 
|  | fprintf(file, "res_send: %s: %s\n", | 
|  | string, strerror(error)); | 
|  | errno = save; | 
|  | } | 
|  |  | 
|  | static int | 
|  | sock_eq(struct sockaddr *a, struct sockaddr *b) { | 
|  | struct sockaddr_in *a4, *b4; | 
|  | struct sockaddr_in6 *a6, *b6; | 
|  |  | 
|  | if (a->sa_family != b->sa_family) | 
|  | return 0; | 
|  | switch (a->sa_family) { | 
|  | case AF_INET: | 
|  | a4 = (struct sockaddr_in *)(void *)a; | 
|  | b4 = (struct sockaddr_in *)(void *)b; | 
|  | return a4->sin_port == b4->sin_port && | 
|  | a4->sin_addr.s_addr == b4->sin_addr.s_addr; | 
|  | case AF_INET6: | 
|  | a6 = (struct sockaddr_in6 *)(void *)a; | 
|  | b6 = (struct sockaddr_in6 *)(void *)b; | 
|  | return a6->sin6_port == b6->sin6_port && | 
|  | #ifdef HAVE_SIN6_SCOPE_ID | 
|  | a6->sin6_scope_id == b6->sin6_scope_id && | 
|  | #endif | 
|  | IN6_ARE_ADDR_EQUAL(&a6->sin6_addr, &b6->sin6_addr); | 
|  | default: | 
|  | return 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | #ifdef NEED_PSELECT | 
|  | /* XXX needs to move to the porting library. */ | 
|  | static int | 
|  | pselect(int nfds, void *rfds, void *wfds, void *efds, | 
|  | struct timespec *tsp, const sigset_t *sigmask) | 
|  | { | 
|  | struct timeval tv, *tvp; | 
|  | sigset_t sigs; | 
|  | int n; | 
|  |  | 
|  | if (tsp) { | 
|  | tvp = &tv; | 
|  | tv = evTimeVal(*tsp); | 
|  | } else | 
|  | tvp = NULL; | 
|  | if (sigmask) | 
|  | sigprocmask(SIG_SETMASK, sigmask, &sigs); | 
|  | n = select(nfds, rfds, wfds, efds, tvp); | 
|  | if (sigmask) | 
|  | sigprocmask(SIG_SETMASK, &sigs, NULL); | 
|  | if (tsp) | 
|  | *tsp = evTimeSpec(tv); | 
|  | return (n); | 
|  | } | 
|  | #endif |