michael@0: /* $NetBSD: res_send.c,v 1.9 2006/01/24 17:41:25 christos Exp $ */ michael@0: michael@0: /* michael@0: * Copyright 2008 Android Open Source Project (source port randomization) michael@0: * Copyright (c) 1985, 1989, 1993 michael@0: * The Regents of the University of California. 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. All advertising materials mentioning features or use of this software michael@0: * must display the following acknowledgement: michael@0: * This product includes software developed by the University of michael@0: * California, Berkeley and its contributors. michael@0: * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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: * Portions Copyright (c) 1993 by Digital Equipment Corporation. michael@0: * michael@0: * Permission to use, copy, modify, and distribute this software for any michael@0: * purpose with or without fee is hereby granted, provided that the above michael@0: * copyright notice and this permission notice appear in all copies, and that michael@0: * the name of Digital Equipment Corporation not be used in advertising or michael@0: * publicity pertaining to distribution of the document or software without michael@0: * specific, written prior permission. michael@0: * michael@0: * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL michael@0: * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES michael@0: * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT michael@0: * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL michael@0: * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR michael@0: * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS michael@0: * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS michael@0: * SOFTWARE. michael@0: */ michael@0: michael@0: /* michael@0: * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") michael@0: * Portions Copyright (c) 1996-1999 by Internet Software Consortium. michael@0: * michael@0: * Permission to use, copy, modify, and distribute this software for any michael@0: * purpose with or without fee is hereby granted, provided that the above michael@0: * copyright notice and this permission notice appear in all copies. michael@0: * michael@0: * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES michael@0: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF michael@0: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR michael@0: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES michael@0: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN michael@0: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT michael@0: * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. michael@0: */ michael@0: michael@0: /* michael@0: * This version of this file 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: michael@0: #define ANDROID_CHANGES 1 michael@0: #define MOZILLA_NECKO_EXCLUDE_CODE 1 michael@0: michael@0: #include michael@0: #if defined(LIBC_SCCS) && !defined(lint) michael@0: #ifdef notdef michael@0: static const char sccsid[] = "@(#)res_send.c 8.1 (Berkeley) 6/4/93"; michael@0: static const char rcsid[] = "Id: res_send.c,v 1.5.2.2.4.5 2004/08/10 02:19:56 marka Exp"; michael@0: #else michael@0: __RCSID("$NetBSD: res_send.c,v 1.9 2006/01/24 17:41:25 christos Exp $"); michael@0: #endif michael@0: #endif /* LIBC_SCCS and not lint */ michael@0: michael@0: /* Set to 0 to not use the small/simple/limited DNS cache michael@0: * Cache implemented higher up in Gecko */ michael@0: #define USE_RESOLV_CACHE 0 michael@0: michael@0: /* michael@0: * Send query to name server and wait for reply. michael@0: */ michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: #include michael@0: #include "arpa_nameser.h" michael@0: #include michael@0: michael@0: #include michael@0: #include michael@0: #ifdef ANDROID_CHANGES michael@0: #include "resolv_private.h" michael@0: #else michael@0: #include michael@0: #endif michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: #include "eventlib.h" michael@0: michael@0: #if USE_RESOLV_CACHE michael@0: # include michael@0: #endif michael@0: michael@0: #ifndef DE_CONST michael@0: #define DE_CONST(c,v) v = ((c) ? \ michael@0: strchr((const void *)(c), *(const char *)(const void *)(c)) : NULL) michael@0: #endif michael@0: michael@0: /* Options. Leave them on. */ michael@0: #ifndef DEBUG michael@0: #define DEBUG michael@0: #endif michael@0: #include "res_debug.h" michael@0: #include "res_private.h" michael@0: michael@0: #define EXT(res) ((res)->_u._ext) michael@0: michael@0: static const int highestFD = FD_SETSIZE - 1; michael@0: michael@0: /* Forward. */ michael@0: michael@0: static int get_salen __P((const struct sockaddr *)); michael@0: static struct sockaddr * get_nsaddr __P((res_state, size_t)); michael@0: static int send_vc(res_state, const u_char *, int, michael@0: u_char *, int, int *, int); michael@0: static int send_dg(res_state, const u_char *, int, michael@0: u_char *, int, int *, int, michael@0: int *, int *); michael@0: static void Aerror(const res_state, FILE *, const char *, int, michael@0: const struct sockaddr *, int); michael@0: static void Perror(const res_state, FILE *, const char *, int); michael@0: static int sock_eq(struct sockaddr *, struct sockaddr *); michael@0: #ifdef NEED_PSELECT michael@0: static int pselect(int, void *, void *, void *, michael@0: struct timespec *, michael@0: const sigset_t *); michael@0: #endif michael@0: void res_pquery(const res_state, const u_char *, int, FILE *); michael@0: michael@0: michael@0: /* BIONIC-BEGIN: implement source port randomization */ michael@0: typedef union { michael@0: struct sockaddr sa; michael@0: struct sockaddr_in sin; michael@0: struct sockaddr_in6 sin6; michael@0: } _sockaddr_union; michael@0: michael@0: static int michael@0: random_bind( int s, int family ) michael@0: { michael@0: _sockaddr_union u; michael@0: int j; michael@0: socklen_t slen; michael@0: michael@0: /* clear all, this also sets the IP4/6 address to 'any' */ michael@0: memset( &u, 0, sizeof u ); michael@0: michael@0: switch (family) { michael@0: case AF_INET: michael@0: u.sin.sin_family = family; michael@0: slen = sizeof u.sin; michael@0: break; michael@0: case AF_INET6: michael@0: u.sin6.sin6_family = family; michael@0: slen = sizeof u.sin6; michael@0: break; michael@0: default: michael@0: errno = EPROTO; michael@0: return -1; michael@0: } michael@0: michael@0: /* first try to bind to a random source port a few times */ michael@0: for (j = 0; j < 10; j++) { michael@0: /* find a random port between 1025 .. 65534 */ michael@0: int port = 1025 + (res_randomid() % (65535-1025)); michael@0: if (family == AF_INET) michael@0: u.sin.sin_port = htons(port); michael@0: else michael@0: u.sin6.sin6_port = htons(port); michael@0: michael@0: if ( !bind( s, &u.sa, slen ) ) michael@0: return 0; michael@0: } michael@0: michael@0: /* nothing after 10 tries, our network table is probably busy */ michael@0: /* let the system decide which port is best */ michael@0: if (family == AF_INET) michael@0: u.sin.sin_port = 0; michael@0: else michael@0: u.sin6.sin6_port = 0; michael@0: michael@0: return bind( s, &u.sa, slen ); michael@0: } michael@0: /* BIONIC-END */ michael@0: michael@0: static const int niflags = NI_NUMERICHOST | NI_NUMERICSERV; michael@0: michael@0: /* Public. */ michael@0: michael@0: /* int michael@0: * res_isourserver(ina) michael@0: * looks up "ina" in _res.ns_addr_list[] michael@0: * returns: michael@0: * 0 : not found michael@0: * >0 : found michael@0: * author: michael@0: * paul vixie, 29may94 michael@0: */ michael@0: int michael@0: res_ourserver_p(const res_state statp, const struct sockaddr *sa) { michael@0: const struct sockaddr_in *inp, *srv; michael@0: const struct sockaddr_in6 *in6p, *srv6; michael@0: int ns; michael@0: michael@0: switch (sa->sa_family) { michael@0: case AF_INET: michael@0: inp = (const struct sockaddr_in *)(const void *)sa; michael@0: for (ns = 0; ns < statp->nscount; ns++) { michael@0: srv = (struct sockaddr_in *)(void *)get_nsaddr(statp, (size_t)ns); michael@0: if (srv->sin_family == inp->sin_family && michael@0: srv->sin_port == inp->sin_port && michael@0: (srv->sin_addr.s_addr == INADDR_ANY || michael@0: srv->sin_addr.s_addr == inp->sin_addr.s_addr)) michael@0: return (1); michael@0: } michael@0: break; michael@0: case AF_INET6: michael@0: if (EXT(statp).ext == NULL) michael@0: break; michael@0: in6p = (const struct sockaddr_in6 *)(const void *)sa; michael@0: for (ns = 0; ns < statp->nscount; ns++) { michael@0: srv6 = (struct sockaddr_in6 *)(void *)get_nsaddr(statp, (size_t)ns); michael@0: if (srv6->sin6_family == in6p->sin6_family && michael@0: srv6->sin6_port == in6p->sin6_port && michael@0: #ifdef HAVE_SIN6_SCOPE_ID michael@0: (srv6->sin6_scope_id == 0 || michael@0: srv6->sin6_scope_id == in6p->sin6_scope_id) && michael@0: #endif michael@0: (IN6_IS_ADDR_UNSPECIFIED(&srv6->sin6_addr) || michael@0: IN6_ARE_ADDR_EQUAL(&srv6->sin6_addr, &in6p->sin6_addr))) michael@0: return (1); michael@0: } michael@0: break; michael@0: default: michael@0: break; michael@0: } michael@0: return (0); michael@0: } michael@0: michael@0: /* int michael@0: * res_nameinquery(name, type, class, buf, eom) michael@0: * look for (name,type,class) in the query section of packet (buf,eom) michael@0: * requires: michael@0: * buf + HFIXEDSZ <= eom michael@0: * returns: michael@0: * -1 : format error michael@0: * 0 : not found michael@0: * >0 : found michael@0: * author: michael@0: * paul vixie, 29may94 michael@0: */ michael@0: int michael@0: res_nameinquery(const char *name, int type, int class, michael@0: const u_char *buf, const u_char *eom) michael@0: { michael@0: const u_char *cp = buf + HFIXEDSZ; michael@0: int qdcount = ntohs(((const HEADER*)(const void *)buf)->qdcount); michael@0: michael@0: while (qdcount-- > 0) { michael@0: char tname[MAXDNAME+1]; michael@0: int n, ttype, tclass; michael@0: michael@0: n = dn_expand(buf, eom, cp, tname, sizeof tname); michael@0: if (n < 0) michael@0: return (-1); michael@0: cp += n; michael@0: if (cp + 2 * INT16SZ > eom) michael@0: return (-1); michael@0: ttype = ns_get16(cp); cp += INT16SZ; michael@0: tclass = ns_get16(cp); cp += INT16SZ; michael@0: if (ttype == type && tclass == class && michael@0: ns_samename(tname, name) == 1) michael@0: return (1); michael@0: } michael@0: return (0); michael@0: } michael@0: michael@0: /* int michael@0: * res_queriesmatch(buf1, eom1, buf2, eom2) michael@0: * is there a 1:1 mapping of (name,type,class) michael@0: * in (buf1,eom1) and (buf2,eom2)? michael@0: * returns: michael@0: * -1 : format error michael@0: * 0 : not a 1:1 mapping michael@0: * >0 : is a 1:1 mapping michael@0: * author: michael@0: * paul vixie, 29may94 michael@0: */ michael@0: int michael@0: res_queriesmatch(const u_char *buf1, const u_char *eom1, michael@0: const u_char *buf2, const u_char *eom2) michael@0: { michael@0: const u_char *cp = buf1 + HFIXEDSZ; michael@0: int qdcount = ntohs(((const HEADER*)(const void *)buf1)->qdcount); michael@0: michael@0: if (buf1 + HFIXEDSZ > eom1 || buf2 + HFIXEDSZ > eom2) michael@0: return (-1); michael@0: michael@0: /* michael@0: * Only header section present in replies to michael@0: * dynamic update packets. michael@0: */ michael@0: if ((((const HEADER *)(const void *)buf1)->opcode == ns_o_update) && michael@0: (((const HEADER *)(const void *)buf2)->opcode == ns_o_update)) michael@0: return (1); michael@0: michael@0: if (qdcount != ntohs(((const HEADER*)(const void *)buf2)->qdcount)) michael@0: return (0); michael@0: while (qdcount-- > 0) { michael@0: char tname[MAXDNAME+1]; michael@0: int n, ttype, tclass; michael@0: michael@0: n = dn_expand(buf1, eom1, cp, tname, sizeof tname); michael@0: if (n < 0) michael@0: return (-1); michael@0: cp += n; michael@0: if (cp + 2 * INT16SZ > eom1) michael@0: return (-1); michael@0: ttype = ns_get16(cp); cp += INT16SZ; michael@0: tclass = ns_get16(cp); cp += INT16SZ; michael@0: if (!res_nameinquery(tname, ttype, tclass, buf2, eom2)) michael@0: return (0); michael@0: } michael@0: return (1); michael@0: } michael@0: michael@0: michael@0: int michael@0: res_nsend(res_state statp, michael@0: const u_char *buf, int buflen, u_char *ans, int anssiz) michael@0: { michael@0: int gotsomewhere, terrno, try, v_circuit, resplen, ns, n; michael@0: char abuf[NI_MAXHOST]; michael@0: #if USE_RESOLV_CACHE michael@0: struct resolv_cache* cache; michael@0: ResolvCacheStatus cache_status = RESOLV_CACHE_UNSUPPORTED; michael@0: #endif michael@0: michael@0: if (statp->nscount == 0) { michael@0: errno = ESRCH; michael@0: return (-1); michael@0: } michael@0: if (anssiz < HFIXEDSZ) { michael@0: errno = EINVAL; michael@0: return (-1); michael@0: } michael@0: DprintQ((statp->options & RES_DEBUG) || (statp->pfcode & RES_PRF_QUERY), michael@0: (stdout, ";; res_send()\n"), buf, buflen); michael@0: v_circuit = (statp->options & RES_USEVC) || buflen > PACKETSZ; michael@0: gotsomewhere = 0; michael@0: terrno = ETIMEDOUT; michael@0: michael@0: #if USE_RESOLV_CACHE michael@0: cache = __get_res_cache(); michael@0: if (cache != NULL) { michael@0: int anslen = 0; michael@0: cache_status = _resolv_cache_lookup( michael@0: cache, buf, buflen, michael@0: ans, anssiz, &anslen); michael@0: michael@0: if (cache_status == RESOLV_CACHE_FOUND) { michael@0: return anslen; michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: /* michael@0: * If the ns_addr_list in the resolver context has changed, then michael@0: * invalidate our cached copy and the associated timing data. michael@0: */ michael@0: if (EXT(statp).nscount != 0) { michael@0: int needclose = 0; michael@0: struct sockaddr_storage peer; michael@0: socklen_t peerlen; michael@0: michael@0: if (EXT(statp).nscount != statp->nscount) michael@0: needclose++; michael@0: else michael@0: for (ns = 0; ns < statp->nscount; ns++) { michael@0: if (statp->nsaddr_list[ns].sin_family && michael@0: !sock_eq((struct sockaddr *)(void *)&statp->nsaddr_list[ns], michael@0: (struct sockaddr *)(void *)&EXT(statp).ext->nsaddrs[ns])) { michael@0: needclose++; michael@0: break; michael@0: } michael@0: michael@0: if (EXT(statp).nssocks[ns] == -1) michael@0: continue; michael@0: peerlen = sizeof(peer); michael@0: if (getsockname(EXT(statp).nssocks[ns], michael@0: (struct sockaddr *)(void *)&peer, &peerlen) < 0) { michael@0: needclose++; michael@0: break; michael@0: } michael@0: if (!sock_eq((struct sockaddr *)(void *)&peer, michael@0: get_nsaddr(statp, (size_t)ns))) { michael@0: needclose++; michael@0: break; michael@0: } michael@0: } michael@0: if (needclose) { michael@0: res_nclose(statp); michael@0: EXT(statp).nscount = 0; michael@0: } michael@0: } michael@0: michael@0: /* michael@0: * Maybe initialize our private copy of the ns_addr_list. michael@0: */ michael@0: if (EXT(statp).nscount == 0) { michael@0: for (ns = 0; ns < statp->nscount; ns++) { michael@0: EXT(statp).nstimes[ns] = RES_MAXTIME; michael@0: EXT(statp).nssocks[ns] = -1; michael@0: if (!statp->nsaddr_list[ns].sin_family) michael@0: continue; michael@0: EXT(statp).ext->nsaddrs[ns].sin = michael@0: statp->nsaddr_list[ns]; michael@0: } michael@0: EXT(statp).nscount = statp->nscount; michael@0: } michael@0: michael@0: /* michael@0: * Some resolvers want to even out the load on their nameservers. michael@0: * Note that RES_BLAST overrides RES_ROTATE. michael@0: */ michael@0: if ((statp->options & RES_ROTATE) != 0U && michael@0: (statp->options & RES_BLAST) == 0U) { michael@0: union res_sockaddr_union inu; michael@0: struct sockaddr_in ina; michael@0: int lastns = statp->nscount - 1; michael@0: int fd; michael@0: u_int16_t nstime; michael@0: michael@0: if (EXT(statp).ext != NULL) michael@0: inu = EXT(statp).ext->nsaddrs[0]; michael@0: ina = statp->nsaddr_list[0]; michael@0: fd = EXT(statp).nssocks[0]; michael@0: nstime = EXT(statp).nstimes[0]; michael@0: for (ns = 0; ns < lastns; ns++) { michael@0: if (EXT(statp).ext != NULL) michael@0: EXT(statp).ext->nsaddrs[ns] = michael@0: EXT(statp).ext->nsaddrs[ns + 1]; michael@0: statp->nsaddr_list[ns] = statp->nsaddr_list[ns + 1]; michael@0: EXT(statp).nssocks[ns] = EXT(statp).nssocks[ns + 1]; michael@0: EXT(statp).nstimes[ns] = EXT(statp).nstimes[ns + 1]; michael@0: } michael@0: if (EXT(statp).ext != NULL) michael@0: EXT(statp).ext->nsaddrs[lastns] = inu; michael@0: statp->nsaddr_list[lastns] = ina; michael@0: EXT(statp).nssocks[lastns] = fd; michael@0: EXT(statp).nstimes[lastns] = nstime; michael@0: } michael@0: michael@0: /* michael@0: * Send request, RETRY times, or until successful. michael@0: */ michael@0: for (try = 0; try < statp->retry; try++) { michael@0: for (ns = 0; ns < statp->nscount; ns++) { michael@0: struct sockaddr *nsap; michael@0: int nsaplen; michael@0: nsap = get_nsaddr(statp, (size_t)ns); michael@0: nsaplen = get_salen(nsap); michael@0: statp->_flags &= ~RES_F_LASTMASK; michael@0: statp->_flags |= (ns << RES_F_LASTSHIFT); michael@0: same_ns: michael@0: if (statp->qhook) { michael@0: int done = 0, loops = 0; michael@0: michael@0: do { michael@0: res_sendhookact act; michael@0: michael@0: act = (*statp->qhook)(&nsap, &buf, &buflen, michael@0: ans, anssiz, &resplen); michael@0: switch (act) { michael@0: case res_goahead: michael@0: done = 1; michael@0: break; michael@0: case res_nextns: michael@0: res_nclose(statp); michael@0: goto next_ns; michael@0: case res_done: michael@0: return (resplen); michael@0: case res_modified: michael@0: /* give the hook another try */ michael@0: if (++loops < 42) /*doug adams*/ michael@0: break; michael@0: /*FALLTHROUGH*/ michael@0: case res_error: michael@0: /*FALLTHROUGH*/ michael@0: default: michael@0: goto fail; michael@0: } michael@0: } while (!done); michael@0: } michael@0: michael@0: Dprint(((statp->options & RES_DEBUG) && michael@0: getnameinfo(nsap, (socklen_t)nsaplen, abuf, sizeof(abuf), michael@0: NULL, 0, niflags) == 0), michael@0: (stdout, ";; Querying server (# %d) address = %s\n", michael@0: ns + 1, abuf)); michael@0: michael@0: michael@0: if (v_circuit) { michael@0: /* Use VC; at most one attempt per server. */ michael@0: try = statp->retry; michael@0: n = send_vc(statp, buf, buflen, ans, anssiz, &terrno, michael@0: ns); michael@0: if (n < 0) michael@0: goto fail; michael@0: if (n == 0) michael@0: goto next_ns; michael@0: resplen = n; michael@0: } else { michael@0: /* Use datagrams. */ michael@0: n = send_dg(statp, buf, buflen, ans, anssiz, &terrno, michael@0: ns, &v_circuit, &gotsomewhere); michael@0: if (n < 0) michael@0: goto fail; michael@0: if (n == 0) michael@0: goto next_ns; michael@0: if (v_circuit) michael@0: goto same_ns; michael@0: resplen = n; michael@0: } michael@0: michael@0: Dprint((statp->options & RES_DEBUG) || michael@0: ((statp->pfcode & RES_PRF_REPLY) && michael@0: (statp->pfcode & RES_PRF_HEAD1)), michael@0: (stdout, ";; got answer:\n")); michael@0: michael@0: DprintQ((statp->options & RES_DEBUG) || michael@0: (statp->pfcode & RES_PRF_REPLY), michael@0: (stdout, "%s", ""), michael@0: ans, (resplen > anssiz) ? anssiz : resplen); michael@0: michael@0: #if USE_RESOLV_CACHE michael@0: if (cache_status == RESOLV_CACHE_NOTFOUND) { michael@0: _resolv_cache_add(cache, buf, buflen, michael@0: ans, resplen); michael@0: } michael@0: #endif michael@0: /* michael@0: * If we have temporarily opened a virtual circuit, michael@0: * or if we haven't been asked to keep a socket open, michael@0: * close the socket. michael@0: */ michael@0: if ((v_circuit && (statp->options & RES_USEVC) == 0U) || michael@0: (statp->options & RES_STAYOPEN) == 0U) { michael@0: res_nclose(statp); michael@0: } michael@0: if (statp->rhook) { michael@0: int done = 0, loops = 0; michael@0: michael@0: do { michael@0: res_sendhookact act; michael@0: michael@0: act = (*statp->rhook)(nsap, buf, buflen, michael@0: ans, anssiz, &resplen); michael@0: switch (act) { michael@0: case res_goahead: michael@0: case res_done: michael@0: done = 1; michael@0: break; michael@0: case res_nextns: michael@0: res_nclose(statp); michael@0: goto next_ns; michael@0: case res_modified: michael@0: /* give the hook another try */ michael@0: if (++loops < 42) /*doug adams*/ michael@0: break; michael@0: /*FALLTHROUGH*/ michael@0: case res_error: michael@0: /*FALLTHROUGH*/ michael@0: default: michael@0: goto fail; michael@0: } michael@0: } while (!done); michael@0: michael@0: } michael@0: return (resplen); michael@0: next_ns: ; michael@0: } /*foreach ns*/ michael@0: } /*foreach retry*/ michael@0: res_nclose(statp); michael@0: if (!v_circuit) { michael@0: if (!gotsomewhere) michael@0: errno = ECONNREFUSED; /* no nameservers found */ michael@0: else michael@0: errno = ETIMEDOUT; /* no answer obtained */ michael@0: } else michael@0: errno = terrno; michael@0: return (-1); michael@0: fail: michael@0: res_nclose(statp); michael@0: return (-1); michael@0: } michael@0: michael@0: /* Private */ michael@0: michael@0: static int michael@0: get_salen(sa) michael@0: const struct sockaddr *sa; michael@0: { michael@0: michael@0: #ifdef HAVE_SA_LEN michael@0: /* There are people do not set sa_len. Be forgiving to them. */ michael@0: if (sa->sa_len) michael@0: return (sa->sa_len); michael@0: #endif michael@0: michael@0: if (sa->sa_family == AF_INET) michael@0: return (sizeof(struct sockaddr_in)); michael@0: else if (sa->sa_family == AF_INET6) michael@0: return (sizeof(struct sockaddr_in6)); michael@0: else michael@0: return (0); /* unknown, die on connect */ michael@0: } michael@0: michael@0: /* michael@0: * pick appropriate nsaddr_list for use. see res_init() for initialization. michael@0: */ michael@0: static struct sockaddr * michael@0: get_nsaddr(statp, n) michael@0: res_state statp; michael@0: size_t n; michael@0: { michael@0: michael@0: if (!statp->nsaddr_list[n].sin_family && EXT(statp).ext) { michael@0: /* michael@0: * - EXT(statp).ext->nsaddrs[n] holds an address that is larger michael@0: * than struct sockaddr, and michael@0: * - user code did not update statp->nsaddr_list[n]. michael@0: */ michael@0: return (struct sockaddr *)(void *)&EXT(statp).ext->nsaddrs[n]; michael@0: } else { michael@0: /* michael@0: * - user code updated statp->nsaddr_list[n], or michael@0: * - statp->nsaddr_list[n] has the same content as michael@0: * EXT(statp).ext->nsaddrs[n]. michael@0: */ michael@0: return (struct sockaddr *)(void *)&statp->nsaddr_list[n]; michael@0: } michael@0: } michael@0: michael@0: static int michael@0: send_vc(res_state statp, michael@0: const u_char *buf, int buflen, u_char *ans, int anssiz, michael@0: int *terrno, int ns) michael@0: { michael@0: const HEADER *hp = (const HEADER *)(const void *)buf; michael@0: HEADER *anhp = (HEADER *)(void *)ans; michael@0: struct sockaddr *nsap; michael@0: int nsaplen; michael@0: int truncating, connreset, resplen, n; michael@0: struct iovec iov[2]; michael@0: u_short len; michael@0: u_char *cp; michael@0: void *tmp; michael@0: michael@0: nsap = get_nsaddr(statp, (size_t)ns); michael@0: nsaplen = get_salen(nsap); michael@0: michael@0: connreset = 0; michael@0: same_ns: michael@0: truncating = 0; michael@0: michael@0: /* Are we still talking to whom we want to talk to? */ michael@0: if (statp->_vcsock >= 0 && (statp->_flags & RES_F_VC) != 0) { michael@0: struct sockaddr_storage peer; michael@0: socklen_t size = sizeof peer; michael@0: michael@0: if (getpeername(statp->_vcsock, michael@0: (struct sockaddr *)(void *)&peer, &size) < 0 || michael@0: !sock_eq((struct sockaddr *)(void *)&peer, nsap)) { michael@0: res_nclose(statp); michael@0: statp->_flags &= ~RES_F_VC; michael@0: } michael@0: } michael@0: michael@0: if (statp->_vcsock < 0 || (statp->_flags & RES_F_VC) == 0) { michael@0: if (statp->_vcsock >= 0) michael@0: res_nclose(statp); michael@0: michael@0: statp->_vcsock = socket(nsap->sa_family, SOCK_STREAM, 0); michael@0: if (statp->_vcsock > highestFD) { michael@0: res_nclose(statp); michael@0: errno = ENOTSOCK; michael@0: } michael@0: if (statp->_vcsock < 0) { michael@0: switch (errno) { michael@0: case EPROTONOSUPPORT: michael@0: #ifdef EPFNOSUPPORT michael@0: case EPFNOSUPPORT: michael@0: #endif michael@0: case EAFNOSUPPORT: michael@0: Perror(statp, stderr, "socket(vc)", errno); michael@0: return (0); michael@0: default: michael@0: *terrno = errno; michael@0: Perror(statp, stderr, "socket(vc)", errno); michael@0: return (-1); michael@0: } michael@0: } michael@0: errno = 0; michael@0: if (random_bind(statp->_vcsock,nsap->sa_family) < 0) { michael@0: *terrno = errno; michael@0: Aerror(statp, stderr, "bind/vc", errno, nsap, michael@0: nsaplen); michael@0: res_nclose(statp); michael@0: return (0); michael@0: } michael@0: if (connect(statp->_vcsock, nsap, (socklen_t)nsaplen) < 0) { michael@0: *terrno = errno; michael@0: Aerror(statp, stderr, "connect/vc", errno, nsap, michael@0: nsaplen); michael@0: res_nclose(statp); michael@0: return (0); michael@0: } michael@0: statp->_flags |= RES_F_VC; michael@0: } michael@0: michael@0: /* michael@0: * Send length & message michael@0: */ michael@0: ns_put16((u_short)buflen, (u_char*)(void *)&len); michael@0: iov[0] = evConsIovec(&len, INT16SZ); michael@0: DE_CONST(buf, tmp); michael@0: iov[1] = evConsIovec(tmp, (size_t)buflen); michael@0: if (writev(statp->_vcsock, iov, 2) != (INT16SZ + buflen)) { michael@0: *terrno = errno; michael@0: Perror(statp, stderr, "write failed", errno); michael@0: res_nclose(statp); michael@0: return (0); michael@0: } michael@0: /* michael@0: * Receive length & response michael@0: */ michael@0: read_len: michael@0: cp = ans; michael@0: len = INT16SZ; michael@0: while ((n = read(statp->_vcsock, (char *)cp, (size_t)len)) > 0) { michael@0: cp += n; michael@0: if ((len -= n) == 0) michael@0: break; michael@0: } michael@0: if (n <= 0) { michael@0: *terrno = errno; michael@0: Perror(statp, stderr, "read failed", errno); michael@0: res_nclose(statp); michael@0: /* michael@0: * A long running process might get its TCP michael@0: * connection reset if the remote server was michael@0: * restarted. Requery the server instead of michael@0: * trying a new one. When there is only one michael@0: * server, this means that a query might work michael@0: * instead of failing. We only allow one reset michael@0: * per query to prevent looping. michael@0: */ michael@0: if (*terrno == ECONNRESET && !connreset) { michael@0: connreset = 1; michael@0: res_nclose(statp); michael@0: goto same_ns; michael@0: } michael@0: res_nclose(statp); michael@0: return (0); michael@0: } michael@0: resplen = ns_get16(ans); michael@0: if (resplen > anssiz) { michael@0: Dprint(statp->options & RES_DEBUG, michael@0: (stdout, ";; response truncated\n") michael@0: ); michael@0: truncating = 1; michael@0: len = anssiz; michael@0: } else michael@0: len = resplen; michael@0: if (len < HFIXEDSZ) { michael@0: /* michael@0: * Undersized message. michael@0: */ michael@0: Dprint(statp->options & RES_DEBUG, michael@0: (stdout, ";; undersized: %d\n", len)); michael@0: *terrno = EMSGSIZE; michael@0: res_nclose(statp); michael@0: return (0); michael@0: } michael@0: cp = ans; michael@0: while (len != 0 && (n = read(statp->_vcsock, (char *)cp, (size_t)len)) > 0){ michael@0: cp += n; michael@0: len -= n; michael@0: } michael@0: if (n <= 0) { michael@0: *terrno = errno; michael@0: Perror(statp, stderr, "read(vc)", errno); michael@0: res_nclose(statp); michael@0: return (0); michael@0: } michael@0: if (truncating) { michael@0: /* michael@0: * Flush rest of answer so connection stays in synch. michael@0: */ michael@0: anhp->tc = 1; michael@0: len = resplen - anssiz; michael@0: while (len != 0) { michael@0: char junk[PACKETSZ]; michael@0: michael@0: n = read(statp->_vcsock, junk, michael@0: (len > sizeof junk) ? sizeof junk : len); michael@0: if (n > 0) michael@0: len -= n; michael@0: else michael@0: break; michael@0: } michael@0: } michael@0: /* michael@0: * If the calling applicating has bailed out of michael@0: * a previous call and failed to arrange to have michael@0: * the circuit closed or the server has got michael@0: * itself confused, then drop the packet and michael@0: * wait for the correct one. michael@0: */ michael@0: if (hp->id != anhp->id) { michael@0: DprintQ((statp->options & RES_DEBUG) || michael@0: (statp->pfcode & RES_PRF_REPLY), michael@0: (stdout, ";; old answer (unexpected):\n"), michael@0: ans, (resplen > anssiz) ? anssiz: resplen); michael@0: goto read_len; michael@0: } michael@0: michael@0: /* michael@0: * All is well, or the error is fatal. Signal that the michael@0: * next nameserver ought not be tried. michael@0: */ michael@0: return (resplen); michael@0: } michael@0: michael@0: static int michael@0: send_dg(res_state statp, michael@0: const u_char *buf, int buflen, u_char *ans, int anssiz, michael@0: int *terrno, int ns, int *v_circuit, int *gotsomewhere) michael@0: { michael@0: const HEADER *hp = (const HEADER *)(const void *)buf; michael@0: HEADER *anhp = (HEADER *)(void *)ans; michael@0: const struct sockaddr *nsap; michael@0: int nsaplen; michael@0: struct timespec now, timeout, finish; michael@0: fd_set dsmask; michael@0: struct sockaddr_storage from; michael@0: socklen_t fromlen; michael@0: int resplen, seconds, n, s; michael@0: michael@0: nsap = get_nsaddr(statp, (size_t)ns); michael@0: nsaplen = get_salen(nsap); michael@0: if (EXT(statp).nssocks[ns] == -1) { michael@0: EXT(statp).nssocks[ns] = socket(nsap->sa_family, SOCK_DGRAM, 0); michael@0: if (EXT(statp).nssocks[ns] > highestFD) { michael@0: res_nclose(statp); michael@0: errno = ENOTSOCK; michael@0: } michael@0: if (EXT(statp).nssocks[ns] < 0) { michael@0: switch (errno) { michael@0: case EPROTONOSUPPORT: michael@0: #ifdef EPFNOSUPPORT michael@0: case EPFNOSUPPORT: michael@0: #endif michael@0: case EAFNOSUPPORT: michael@0: Perror(statp, stderr, "socket(dg)", errno); michael@0: return (0); michael@0: default: michael@0: *terrno = errno; michael@0: Perror(statp, stderr, "socket(dg)", errno); michael@0: return (-1); michael@0: } michael@0: } michael@0: #ifndef CANNOT_CONNECT_DGRAM michael@0: /* michael@0: * On a 4.3BSD+ machine (client and server, michael@0: * actually), sending to a nameserver datagram michael@0: * port with no nameserver will cause an michael@0: * ICMP port unreachable message to be returned. michael@0: * If our datagram socket is "connected" to the michael@0: * server, we get an ECONNREFUSED error on the next michael@0: * socket operation, and select returns if the michael@0: * error message is received. We can thus detect michael@0: * the absence of a nameserver without timing out. michael@0: */ michael@0: if (random_bind(EXT(statp).nssocks[ns], nsap->sa_family) < 0) { michael@0: Aerror(statp, stderr, "bind(dg)", errno, nsap, michael@0: nsaplen); michael@0: res_nclose(statp); michael@0: return (0); michael@0: } michael@0: if (connect(EXT(statp).nssocks[ns], nsap, (socklen_t)nsaplen) < 0) { michael@0: Aerror(statp, stderr, "connect(dg)", errno, nsap, michael@0: nsaplen); michael@0: res_nclose(statp); michael@0: return (0); michael@0: } michael@0: #endif /* !CANNOT_CONNECT_DGRAM */ michael@0: Dprint(statp->options & RES_DEBUG, michael@0: (stdout, ";; new DG socket\n")) michael@0: } michael@0: s = EXT(statp).nssocks[ns]; michael@0: #ifndef CANNOT_CONNECT_DGRAM michael@0: if (send(s, (const char*)buf, (size_t)buflen, 0) != buflen) { michael@0: Perror(statp, stderr, "send", errno); michael@0: res_nclose(statp); michael@0: return (0); michael@0: } michael@0: #else /* !CANNOT_CONNECT_DGRAM */ michael@0: if (sendto(s, (const char*)buf, buflen, 0, nsap, nsaplen) != buflen) michael@0: { michael@0: Aerror(statp, stderr, "sendto", errno, nsap, nsaplen); michael@0: res_nclose(statp); michael@0: return (0); michael@0: } michael@0: #endif /* !CANNOT_CONNECT_DGRAM */ michael@0: michael@0: /* michael@0: * Wait for reply. michael@0: */ michael@0: seconds = (statp->retrans << ns); michael@0: if (ns > 0) michael@0: seconds /= statp->nscount; michael@0: if (seconds <= 0) michael@0: seconds = 1; michael@0: now = evNowTime(); michael@0: timeout = evConsTime((long)seconds, 0L); michael@0: finish = evAddTime(now, timeout); michael@0: goto nonow; michael@0: wait: michael@0: now = evNowTime(); michael@0: nonow: michael@0: FD_ZERO(&dsmask); michael@0: FD_SET(s, &dsmask); michael@0: if (evCmpTime(finish, now) > 0) michael@0: timeout = evSubTime(finish, now); michael@0: else michael@0: timeout = evConsTime(0L, 0L); michael@0: n = pselect(s + 1, &dsmask, NULL, NULL, &timeout, NULL); michael@0: if (n == 0) { michael@0: Dprint(statp->options & RES_DEBUG, (stdout, ";; timeout\n")); michael@0: *gotsomewhere = 1; michael@0: return (0); michael@0: } michael@0: if (n < 0) { michael@0: if (errno == EINTR) michael@0: goto wait; michael@0: Perror(statp, stderr, "select", errno); michael@0: res_nclose(statp); michael@0: return (0); michael@0: } michael@0: errno = 0; michael@0: fromlen = sizeof(from); michael@0: resplen = recvfrom(s, (char*)ans, (size_t)anssiz,0, michael@0: (struct sockaddr *)(void *)&from, &fromlen); michael@0: if (resplen <= 0) { michael@0: Perror(statp, stderr, "recvfrom", errno); michael@0: res_nclose(statp); michael@0: return (0); michael@0: } michael@0: *gotsomewhere = 1; michael@0: if (resplen < HFIXEDSZ) { michael@0: /* michael@0: * Undersized message. michael@0: */ michael@0: Dprint(statp->options & RES_DEBUG, michael@0: (stdout, ";; undersized: %d\n", michael@0: resplen)); michael@0: *terrno = EMSGSIZE; michael@0: res_nclose(statp); michael@0: return (0); michael@0: } michael@0: if (hp->id != anhp->id) { michael@0: /* michael@0: * response from old query, ignore it. michael@0: * XXX - potential security hazard could michael@0: * be detected here. michael@0: */ michael@0: DprintQ((statp->options & RES_DEBUG) || michael@0: (statp->pfcode & RES_PRF_REPLY), michael@0: (stdout, ";; old answer:\n"), michael@0: ans, (resplen > anssiz) ? anssiz : resplen); michael@0: goto wait; michael@0: } michael@0: if (!(statp->options & RES_INSECURE1) && michael@0: !res_ourserver_p(statp, (struct sockaddr *)(void *)&from)) { michael@0: /* michael@0: * response from wrong server? ignore it. michael@0: * XXX - potential security hazard could michael@0: * be detected here. michael@0: */ michael@0: DprintQ((statp->options & RES_DEBUG) || michael@0: (statp->pfcode & RES_PRF_REPLY), michael@0: (stdout, ";; not our server:\n"), michael@0: ans, (resplen > anssiz) ? anssiz : resplen); michael@0: goto wait; michael@0: } michael@0: #ifdef RES_USE_EDNS0 michael@0: if (anhp->rcode == FORMERR && (statp->options & RES_USE_EDNS0) != 0U) { michael@0: /* michael@0: * Do not retry if the server do not understand EDNS0. michael@0: * The case has to be captured here, as FORMERR packet do not michael@0: * carry query section, hence res_queriesmatch() returns 0. michael@0: */ michael@0: DprintQ(statp->options & RES_DEBUG, michael@0: (stdout, "server rejected query with EDNS0:\n"), michael@0: ans, (resplen > anssiz) ? anssiz : resplen); michael@0: /* record the error */ michael@0: statp->_flags |= RES_F_EDNS0ERR; michael@0: res_nclose(statp); michael@0: return (0); michael@0: } michael@0: #endif michael@0: if (!(statp->options & RES_INSECURE2) && michael@0: !res_queriesmatch(buf, buf + buflen, michael@0: ans, ans + anssiz)) { michael@0: /* michael@0: * response contains wrong query? ignore it. michael@0: * XXX - potential security hazard could michael@0: * be detected here. michael@0: */ michael@0: DprintQ((statp->options & RES_DEBUG) || michael@0: (statp->pfcode & RES_PRF_REPLY), michael@0: (stdout, ";; wrong query name:\n"), michael@0: ans, (resplen > anssiz) ? anssiz : resplen); michael@0: goto wait; michael@0: } michael@0: if (anhp->rcode == SERVFAIL || michael@0: anhp->rcode == NOTIMP || michael@0: anhp->rcode == REFUSED) { michael@0: DprintQ(statp->options & RES_DEBUG, michael@0: (stdout, "server rejected query:\n"), michael@0: ans, (resplen > anssiz) ? anssiz : resplen); michael@0: res_nclose(statp); michael@0: /* don't retry if called from dig */ michael@0: if (!statp->pfcode) michael@0: return (0); michael@0: } michael@0: if (!(statp->options & RES_IGNTC) && anhp->tc) { michael@0: /* michael@0: * To get the rest of answer, michael@0: * use TCP with same server. michael@0: */ michael@0: Dprint(statp->options & RES_DEBUG, michael@0: (stdout, ";; truncated answer\n")); michael@0: *v_circuit = 1; michael@0: res_nclose(statp); michael@0: return (1); michael@0: } michael@0: /* michael@0: * All is well, or the error is fatal. Signal that the michael@0: * next nameserver ought not be tried. michael@0: */ michael@0: return (resplen); michael@0: } michael@0: michael@0: static void michael@0: Aerror(const res_state statp, FILE *file, const char *string, int error, michael@0: const struct sockaddr *address, int alen) michael@0: { michael@0: int save = errno; michael@0: char hbuf[NI_MAXHOST]; michael@0: char sbuf[NI_MAXSERV]; michael@0: michael@0: alen = alen; michael@0: michael@0: if ((statp->options & RES_DEBUG) != 0U) { michael@0: if (getnameinfo(address, (socklen_t)alen, hbuf, sizeof(hbuf), michael@0: sbuf, sizeof(sbuf), niflags)) { michael@0: strncpy(hbuf, "?", sizeof(hbuf) - 1); michael@0: hbuf[sizeof(hbuf) - 1] = '\0'; michael@0: strncpy(sbuf, "?", sizeof(sbuf) - 1); michael@0: sbuf[sizeof(sbuf) - 1] = '\0'; michael@0: } michael@0: fprintf(file, "res_send: %s ([%s].%s): %s\n", michael@0: string, hbuf, sbuf, strerror(error)); michael@0: } michael@0: errno = save; michael@0: } michael@0: michael@0: static void michael@0: Perror(const res_state statp, FILE *file, const char *string, int error) { michael@0: int save = errno; michael@0: michael@0: if ((statp->options & RES_DEBUG) != 0U) michael@0: fprintf(file, "res_send: %s: %s\n", michael@0: string, strerror(error)); michael@0: errno = save; michael@0: } michael@0: michael@0: static int michael@0: sock_eq(struct sockaddr *a, struct sockaddr *b) { michael@0: struct sockaddr_in *a4, *b4; michael@0: struct sockaddr_in6 *a6, *b6; michael@0: michael@0: if (a->sa_family != b->sa_family) michael@0: return 0; michael@0: switch (a->sa_family) { michael@0: case AF_INET: michael@0: a4 = (struct sockaddr_in *)(void *)a; michael@0: b4 = (struct sockaddr_in *)(void *)b; michael@0: return a4->sin_port == b4->sin_port && michael@0: a4->sin_addr.s_addr == b4->sin_addr.s_addr; michael@0: case AF_INET6: michael@0: a6 = (struct sockaddr_in6 *)(void *)a; michael@0: b6 = (struct sockaddr_in6 *)(void *)b; michael@0: return a6->sin6_port == b6->sin6_port && michael@0: #ifdef HAVE_SIN6_SCOPE_ID michael@0: a6->sin6_scope_id == b6->sin6_scope_id && michael@0: #endif michael@0: IN6_ARE_ADDR_EQUAL(&a6->sin6_addr, &b6->sin6_addr); michael@0: default: michael@0: return 0; michael@0: } michael@0: } michael@0: michael@0: #ifdef NEED_PSELECT michael@0: /* XXX needs to move to the porting library. */ michael@0: static int michael@0: pselect(int nfds, void *rfds, void *wfds, void *efds, michael@0: struct timespec *tsp, const sigset_t *sigmask) michael@0: { michael@0: struct timeval tv, *tvp; michael@0: sigset_t sigs; michael@0: int n; michael@0: michael@0: if (tsp) { michael@0: tvp = &tv; michael@0: tv = evTimeVal(*tsp); michael@0: } else michael@0: tvp = NULL; michael@0: if (sigmask) michael@0: sigprocmask(SIG_SETMASK, sigmask, &sigs); michael@0: n = select(nfds, rfds, wfds, efds, tvp); michael@0: if (sigmask) michael@0: sigprocmask(SIG_SETMASK, &sigs, NULL); michael@0: if (tsp) michael@0: *tsp = evTimeSpec(tv); michael@0: return (n); michael@0: } michael@0: #endif