Tue, 06 Jan 2015 21:39:09 +0100
Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.
michael@0 | 1 | /* $NetBSD: res_send.c,v 1.9 2006/01/24 17:41:25 christos Exp $ */ |
michael@0 | 2 | |
michael@0 | 3 | /* |
michael@0 | 4 | * Copyright 2008 Android Open Source Project (source port randomization) |
michael@0 | 5 | * Copyright (c) 1985, 1989, 1993 |
michael@0 | 6 | * The Regents of the University of California. All rights reserved. |
michael@0 | 7 | * |
michael@0 | 8 | * Redistribution and use in source and binary forms, with or without |
michael@0 | 9 | * modification, are permitted provided that the following conditions |
michael@0 | 10 | * are met: |
michael@0 | 11 | * 1. Redistributions of source code must retain the above copyright |
michael@0 | 12 | * notice, this list of conditions and the following disclaimer. |
michael@0 | 13 | * 2. Redistributions in binary form must reproduce the above copyright |
michael@0 | 14 | * notice, this list of conditions and the following disclaimer in the |
michael@0 | 15 | * documentation and/or other materials provided with the distribution. |
michael@0 | 16 | * 3. All advertising materials mentioning features or use of this software |
michael@0 | 17 | * must display the following acknowledgement: |
michael@0 | 18 | * This product includes software developed by the University of |
michael@0 | 19 | * California, Berkeley and its contributors. |
michael@0 | 20 | * 4. Neither the name of the University nor the names of its contributors |
michael@0 | 21 | * may be used to endorse or promote products derived from this software |
michael@0 | 22 | * without specific prior written permission. |
michael@0 | 23 | * |
michael@0 | 24 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
michael@0 | 25 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
michael@0 | 26 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
michael@0 | 27 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
michael@0 | 28 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
michael@0 | 29 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
michael@0 | 30 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
michael@0 | 31 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
michael@0 | 32 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
michael@0 | 33 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
michael@0 | 34 | * SUCH DAMAGE. |
michael@0 | 35 | */ |
michael@0 | 36 | |
michael@0 | 37 | /* |
michael@0 | 38 | * Portions Copyright (c) 1993 by Digital Equipment Corporation. |
michael@0 | 39 | * |
michael@0 | 40 | * Permission to use, copy, modify, and distribute this software for any |
michael@0 | 41 | * purpose with or without fee is hereby granted, provided that the above |
michael@0 | 42 | * copyright notice and this permission notice appear in all copies, and that |
michael@0 | 43 | * the name of Digital Equipment Corporation not be used in advertising or |
michael@0 | 44 | * publicity pertaining to distribution of the document or software without |
michael@0 | 45 | * specific, written prior permission. |
michael@0 | 46 | * |
michael@0 | 47 | * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL |
michael@0 | 48 | * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES |
michael@0 | 49 | * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT |
michael@0 | 50 | * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL |
michael@0 | 51 | * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR |
michael@0 | 52 | * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS |
michael@0 | 53 | * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS |
michael@0 | 54 | * SOFTWARE. |
michael@0 | 55 | */ |
michael@0 | 56 | |
michael@0 | 57 | /* |
michael@0 | 58 | * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") |
michael@0 | 59 | * Portions Copyright (c) 1996-1999 by Internet Software Consortium. |
michael@0 | 60 | * |
michael@0 | 61 | * Permission to use, copy, modify, and distribute this software for any |
michael@0 | 62 | * purpose with or without fee is hereby granted, provided that the above |
michael@0 | 63 | * copyright notice and this permission notice appear in all copies. |
michael@0 | 64 | * |
michael@0 | 65 | * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES |
michael@0 | 66 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
michael@0 | 67 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR |
michael@0 | 68 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
michael@0 | 69 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
michael@0 | 70 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT |
michael@0 | 71 | * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
michael@0 | 72 | */ |
michael@0 | 73 | |
michael@0 | 74 | /* |
michael@0 | 75 | * This version of this file is derived from Android 2.3 "Gingerbread", |
michael@0 | 76 | * which contains uncredited changes by Android/Google developers. It has |
michael@0 | 77 | * been modified in 2011 for use in the Android build of Mozilla Firefox by |
michael@0 | 78 | * Mozilla contributors (including Michael Edwards <m.k.edwards@gmail.com>, |
michael@0 | 79 | * and Steve Workman <sjhworkman@gmail.com>). |
michael@0 | 80 | * These changes are offered under the same license as the original NetBSD |
michael@0 | 81 | * file, whose copyright and license are unchanged above. |
michael@0 | 82 | */ |
michael@0 | 83 | |
michael@0 | 84 | #define ANDROID_CHANGES 1 |
michael@0 | 85 | #define MOZILLA_NECKO_EXCLUDE_CODE 1 |
michael@0 | 86 | |
michael@0 | 87 | #include <sys/cdefs.h> |
michael@0 | 88 | #if defined(LIBC_SCCS) && !defined(lint) |
michael@0 | 89 | #ifdef notdef |
michael@0 | 90 | static const char sccsid[] = "@(#)res_send.c 8.1 (Berkeley) 6/4/93"; |
michael@0 | 91 | 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 | 92 | #else |
michael@0 | 93 | __RCSID("$NetBSD: res_send.c,v 1.9 2006/01/24 17:41:25 christos Exp $"); |
michael@0 | 94 | #endif |
michael@0 | 95 | #endif /* LIBC_SCCS and not lint */ |
michael@0 | 96 | |
michael@0 | 97 | /* Set to 0 to not use the small/simple/limited DNS cache |
michael@0 | 98 | * Cache implemented higher up in Gecko */ |
michael@0 | 99 | #define USE_RESOLV_CACHE 0 |
michael@0 | 100 | |
michael@0 | 101 | /* |
michael@0 | 102 | * Send query to name server and wait for reply. |
michael@0 | 103 | */ |
michael@0 | 104 | |
michael@0 | 105 | #include <sys/types.h> |
michael@0 | 106 | #include <sys/param.h> |
michael@0 | 107 | #include <sys/time.h> |
michael@0 | 108 | #include <sys/socket.h> |
michael@0 | 109 | #include <sys/uio.h> |
michael@0 | 110 | |
michael@0 | 111 | #include <netinet/in.h> |
michael@0 | 112 | #include "arpa_nameser.h" |
michael@0 | 113 | #include <arpa/inet.h> |
michael@0 | 114 | |
michael@0 | 115 | #include <errno.h> |
michael@0 | 116 | #include <netdb.h> |
michael@0 | 117 | #ifdef ANDROID_CHANGES |
michael@0 | 118 | #include "resolv_private.h" |
michael@0 | 119 | #else |
michael@0 | 120 | #include <resolv.h> |
michael@0 | 121 | #endif |
michael@0 | 122 | #include <signal.h> |
michael@0 | 123 | #include <stdio.h> |
michael@0 | 124 | #include <stdlib.h> |
michael@0 | 125 | #include <string.h> |
michael@0 | 126 | #include <unistd.h> |
michael@0 | 127 | |
michael@0 | 128 | #include "eventlib.h" |
michael@0 | 129 | |
michael@0 | 130 | #if USE_RESOLV_CACHE |
michael@0 | 131 | # include <resolv_cache.h> |
michael@0 | 132 | #endif |
michael@0 | 133 | |
michael@0 | 134 | #ifndef DE_CONST |
michael@0 | 135 | #define DE_CONST(c,v) v = ((c) ? \ |
michael@0 | 136 | strchr((const void *)(c), *(const char *)(const void *)(c)) : NULL) |
michael@0 | 137 | #endif |
michael@0 | 138 | |
michael@0 | 139 | /* Options. Leave them on. */ |
michael@0 | 140 | #ifndef DEBUG |
michael@0 | 141 | #define DEBUG |
michael@0 | 142 | #endif |
michael@0 | 143 | #include "res_debug.h" |
michael@0 | 144 | #include "res_private.h" |
michael@0 | 145 | |
michael@0 | 146 | #define EXT(res) ((res)->_u._ext) |
michael@0 | 147 | |
michael@0 | 148 | static const int highestFD = FD_SETSIZE - 1; |
michael@0 | 149 | |
michael@0 | 150 | /* Forward. */ |
michael@0 | 151 | |
michael@0 | 152 | static int get_salen __P((const struct sockaddr *)); |
michael@0 | 153 | static struct sockaddr * get_nsaddr __P((res_state, size_t)); |
michael@0 | 154 | static int send_vc(res_state, const u_char *, int, |
michael@0 | 155 | u_char *, int, int *, int); |
michael@0 | 156 | static int send_dg(res_state, const u_char *, int, |
michael@0 | 157 | u_char *, int, int *, int, |
michael@0 | 158 | int *, int *); |
michael@0 | 159 | static void Aerror(const res_state, FILE *, const char *, int, |
michael@0 | 160 | const struct sockaddr *, int); |
michael@0 | 161 | static void Perror(const res_state, FILE *, const char *, int); |
michael@0 | 162 | static int sock_eq(struct sockaddr *, struct sockaddr *); |
michael@0 | 163 | #ifdef NEED_PSELECT |
michael@0 | 164 | static int pselect(int, void *, void *, void *, |
michael@0 | 165 | struct timespec *, |
michael@0 | 166 | const sigset_t *); |
michael@0 | 167 | #endif |
michael@0 | 168 | void res_pquery(const res_state, const u_char *, int, FILE *); |
michael@0 | 169 | |
michael@0 | 170 | |
michael@0 | 171 | /* BIONIC-BEGIN: implement source port randomization */ |
michael@0 | 172 | typedef union { |
michael@0 | 173 | struct sockaddr sa; |
michael@0 | 174 | struct sockaddr_in sin; |
michael@0 | 175 | struct sockaddr_in6 sin6; |
michael@0 | 176 | } _sockaddr_union; |
michael@0 | 177 | |
michael@0 | 178 | static int |
michael@0 | 179 | random_bind( int s, int family ) |
michael@0 | 180 | { |
michael@0 | 181 | _sockaddr_union u; |
michael@0 | 182 | int j; |
michael@0 | 183 | socklen_t slen; |
michael@0 | 184 | |
michael@0 | 185 | /* clear all, this also sets the IP4/6 address to 'any' */ |
michael@0 | 186 | memset( &u, 0, sizeof u ); |
michael@0 | 187 | |
michael@0 | 188 | switch (family) { |
michael@0 | 189 | case AF_INET: |
michael@0 | 190 | u.sin.sin_family = family; |
michael@0 | 191 | slen = sizeof u.sin; |
michael@0 | 192 | break; |
michael@0 | 193 | case AF_INET6: |
michael@0 | 194 | u.sin6.sin6_family = family; |
michael@0 | 195 | slen = sizeof u.sin6; |
michael@0 | 196 | break; |
michael@0 | 197 | default: |
michael@0 | 198 | errno = EPROTO; |
michael@0 | 199 | return -1; |
michael@0 | 200 | } |
michael@0 | 201 | |
michael@0 | 202 | /* first try to bind to a random source port a few times */ |
michael@0 | 203 | for (j = 0; j < 10; j++) { |
michael@0 | 204 | /* find a random port between 1025 .. 65534 */ |
michael@0 | 205 | int port = 1025 + (res_randomid() % (65535-1025)); |
michael@0 | 206 | if (family == AF_INET) |
michael@0 | 207 | u.sin.sin_port = htons(port); |
michael@0 | 208 | else |
michael@0 | 209 | u.sin6.sin6_port = htons(port); |
michael@0 | 210 | |
michael@0 | 211 | if ( !bind( s, &u.sa, slen ) ) |
michael@0 | 212 | return 0; |
michael@0 | 213 | } |
michael@0 | 214 | |
michael@0 | 215 | /* nothing after 10 tries, our network table is probably busy */ |
michael@0 | 216 | /* let the system decide which port is best */ |
michael@0 | 217 | if (family == AF_INET) |
michael@0 | 218 | u.sin.sin_port = 0; |
michael@0 | 219 | else |
michael@0 | 220 | u.sin6.sin6_port = 0; |
michael@0 | 221 | |
michael@0 | 222 | return bind( s, &u.sa, slen ); |
michael@0 | 223 | } |
michael@0 | 224 | /* BIONIC-END */ |
michael@0 | 225 | |
michael@0 | 226 | static const int niflags = NI_NUMERICHOST | NI_NUMERICSERV; |
michael@0 | 227 | |
michael@0 | 228 | /* Public. */ |
michael@0 | 229 | |
michael@0 | 230 | /* int |
michael@0 | 231 | * res_isourserver(ina) |
michael@0 | 232 | * looks up "ina" in _res.ns_addr_list[] |
michael@0 | 233 | * returns: |
michael@0 | 234 | * 0 : not found |
michael@0 | 235 | * >0 : found |
michael@0 | 236 | * author: |
michael@0 | 237 | * paul vixie, 29may94 |
michael@0 | 238 | */ |
michael@0 | 239 | int |
michael@0 | 240 | res_ourserver_p(const res_state statp, const struct sockaddr *sa) { |
michael@0 | 241 | const struct sockaddr_in *inp, *srv; |
michael@0 | 242 | const struct sockaddr_in6 *in6p, *srv6; |
michael@0 | 243 | int ns; |
michael@0 | 244 | |
michael@0 | 245 | switch (sa->sa_family) { |
michael@0 | 246 | case AF_INET: |
michael@0 | 247 | inp = (const struct sockaddr_in *)(const void *)sa; |
michael@0 | 248 | for (ns = 0; ns < statp->nscount; ns++) { |
michael@0 | 249 | srv = (struct sockaddr_in *)(void *)get_nsaddr(statp, (size_t)ns); |
michael@0 | 250 | if (srv->sin_family == inp->sin_family && |
michael@0 | 251 | srv->sin_port == inp->sin_port && |
michael@0 | 252 | (srv->sin_addr.s_addr == INADDR_ANY || |
michael@0 | 253 | srv->sin_addr.s_addr == inp->sin_addr.s_addr)) |
michael@0 | 254 | return (1); |
michael@0 | 255 | } |
michael@0 | 256 | break; |
michael@0 | 257 | case AF_INET6: |
michael@0 | 258 | if (EXT(statp).ext == NULL) |
michael@0 | 259 | break; |
michael@0 | 260 | in6p = (const struct sockaddr_in6 *)(const void *)sa; |
michael@0 | 261 | for (ns = 0; ns < statp->nscount; ns++) { |
michael@0 | 262 | srv6 = (struct sockaddr_in6 *)(void *)get_nsaddr(statp, (size_t)ns); |
michael@0 | 263 | if (srv6->sin6_family == in6p->sin6_family && |
michael@0 | 264 | srv6->sin6_port == in6p->sin6_port && |
michael@0 | 265 | #ifdef HAVE_SIN6_SCOPE_ID |
michael@0 | 266 | (srv6->sin6_scope_id == 0 || |
michael@0 | 267 | srv6->sin6_scope_id == in6p->sin6_scope_id) && |
michael@0 | 268 | #endif |
michael@0 | 269 | (IN6_IS_ADDR_UNSPECIFIED(&srv6->sin6_addr) || |
michael@0 | 270 | IN6_ARE_ADDR_EQUAL(&srv6->sin6_addr, &in6p->sin6_addr))) |
michael@0 | 271 | return (1); |
michael@0 | 272 | } |
michael@0 | 273 | break; |
michael@0 | 274 | default: |
michael@0 | 275 | break; |
michael@0 | 276 | } |
michael@0 | 277 | return (0); |
michael@0 | 278 | } |
michael@0 | 279 | |
michael@0 | 280 | /* int |
michael@0 | 281 | * res_nameinquery(name, type, class, buf, eom) |
michael@0 | 282 | * look for (name,type,class) in the query section of packet (buf,eom) |
michael@0 | 283 | * requires: |
michael@0 | 284 | * buf + HFIXEDSZ <= eom |
michael@0 | 285 | * returns: |
michael@0 | 286 | * -1 : format error |
michael@0 | 287 | * 0 : not found |
michael@0 | 288 | * >0 : found |
michael@0 | 289 | * author: |
michael@0 | 290 | * paul vixie, 29may94 |
michael@0 | 291 | */ |
michael@0 | 292 | int |
michael@0 | 293 | res_nameinquery(const char *name, int type, int class, |
michael@0 | 294 | const u_char *buf, const u_char *eom) |
michael@0 | 295 | { |
michael@0 | 296 | const u_char *cp = buf + HFIXEDSZ; |
michael@0 | 297 | int qdcount = ntohs(((const HEADER*)(const void *)buf)->qdcount); |
michael@0 | 298 | |
michael@0 | 299 | while (qdcount-- > 0) { |
michael@0 | 300 | char tname[MAXDNAME+1]; |
michael@0 | 301 | int n, ttype, tclass; |
michael@0 | 302 | |
michael@0 | 303 | n = dn_expand(buf, eom, cp, tname, sizeof tname); |
michael@0 | 304 | if (n < 0) |
michael@0 | 305 | return (-1); |
michael@0 | 306 | cp += n; |
michael@0 | 307 | if (cp + 2 * INT16SZ > eom) |
michael@0 | 308 | return (-1); |
michael@0 | 309 | ttype = ns_get16(cp); cp += INT16SZ; |
michael@0 | 310 | tclass = ns_get16(cp); cp += INT16SZ; |
michael@0 | 311 | if (ttype == type && tclass == class && |
michael@0 | 312 | ns_samename(tname, name) == 1) |
michael@0 | 313 | return (1); |
michael@0 | 314 | } |
michael@0 | 315 | return (0); |
michael@0 | 316 | } |
michael@0 | 317 | |
michael@0 | 318 | /* int |
michael@0 | 319 | * res_queriesmatch(buf1, eom1, buf2, eom2) |
michael@0 | 320 | * is there a 1:1 mapping of (name,type,class) |
michael@0 | 321 | * in (buf1,eom1) and (buf2,eom2)? |
michael@0 | 322 | * returns: |
michael@0 | 323 | * -1 : format error |
michael@0 | 324 | * 0 : not a 1:1 mapping |
michael@0 | 325 | * >0 : is a 1:1 mapping |
michael@0 | 326 | * author: |
michael@0 | 327 | * paul vixie, 29may94 |
michael@0 | 328 | */ |
michael@0 | 329 | int |
michael@0 | 330 | res_queriesmatch(const u_char *buf1, const u_char *eom1, |
michael@0 | 331 | const u_char *buf2, const u_char *eom2) |
michael@0 | 332 | { |
michael@0 | 333 | const u_char *cp = buf1 + HFIXEDSZ; |
michael@0 | 334 | int qdcount = ntohs(((const HEADER*)(const void *)buf1)->qdcount); |
michael@0 | 335 | |
michael@0 | 336 | if (buf1 + HFIXEDSZ > eom1 || buf2 + HFIXEDSZ > eom2) |
michael@0 | 337 | return (-1); |
michael@0 | 338 | |
michael@0 | 339 | /* |
michael@0 | 340 | * Only header section present in replies to |
michael@0 | 341 | * dynamic update packets. |
michael@0 | 342 | */ |
michael@0 | 343 | if ((((const HEADER *)(const void *)buf1)->opcode == ns_o_update) && |
michael@0 | 344 | (((const HEADER *)(const void *)buf2)->opcode == ns_o_update)) |
michael@0 | 345 | return (1); |
michael@0 | 346 | |
michael@0 | 347 | if (qdcount != ntohs(((const HEADER*)(const void *)buf2)->qdcount)) |
michael@0 | 348 | return (0); |
michael@0 | 349 | while (qdcount-- > 0) { |
michael@0 | 350 | char tname[MAXDNAME+1]; |
michael@0 | 351 | int n, ttype, tclass; |
michael@0 | 352 | |
michael@0 | 353 | n = dn_expand(buf1, eom1, cp, tname, sizeof tname); |
michael@0 | 354 | if (n < 0) |
michael@0 | 355 | return (-1); |
michael@0 | 356 | cp += n; |
michael@0 | 357 | if (cp + 2 * INT16SZ > eom1) |
michael@0 | 358 | return (-1); |
michael@0 | 359 | ttype = ns_get16(cp); cp += INT16SZ; |
michael@0 | 360 | tclass = ns_get16(cp); cp += INT16SZ; |
michael@0 | 361 | if (!res_nameinquery(tname, ttype, tclass, buf2, eom2)) |
michael@0 | 362 | return (0); |
michael@0 | 363 | } |
michael@0 | 364 | return (1); |
michael@0 | 365 | } |
michael@0 | 366 | |
michael@0 | 367 | |
michael@0 | 368 | int |
michael@0 | 369 | res_nsend(res_state statp, |
michael@0 | 370 | const u_char *buf, int buflen, u_char *ans, int anssiz) |
michael@0 | 371 | { |
michael@0 | 372 | int gotsomewhere, terrno, try, v_circuit, resplen, ns, n; |
michael@0 | 373 | char abuf[NI_MAXHOST]; |
michael@0 | 374 | #if USE_RESOLV_CACHE |
michael@0 | 375 | struct resolv_cache* cache; |
michael@0 | 376 | ResolvCacheStatus cache_status = RESOLV_CACHE_UNSUPPORTED; |
michael@0 | 377 | #endif |
michael@0 | 378 | |
michael@0 | 379 | if (statp->nscount == 0) { |
michael@0 | 380 | errno = ESRCH; |
michael@0 | 381 | return (-1); |
michael@0 | 382 | } |
michael@0 | 383 | if (anssiz < HFIXEDSZ) { |
michael@0 | 384 | errno = EINVAL; |
michael@0 | 385 | return (-1); |
michael@0 | 386 | } |
michael@0 | 387 | DprintQ((statp->options & RES_DEBUG) || (statp->pfcode & RES_PRF_QUERY), |
michael@0 | 388 | (stdout, ";; res_send()\n"), buf, buflen); |
michael@0 | 389 | v_circuit = (statp->options & RES_USEVC) || buflen > PACKETSZ; |
michael@0 | 390 | gotsomewhere = 0; |
michael@0 | 391 | terrno = ETIMEDOUT; |
michael@0 | 392 | |
michael@0 | 393 | #if USE_RESOLV_CACHE |
michael@0 | 394 | cache = __get_res_cache(); |
michael@0 | 395 | if (cache != NULL) { |
michael@0 | 396 | int anslen = 0; |
michael@0 | 397 | cache_status = _resolv_cache_lookup( |
michael@0 | 398 | cache, buf, buflen, |
michael@0 | 399 | ans, anssiz, &anslen); |
michael@0 | 400 | |
michael@0 | 401 | if (cache_status == RESOLV_CACHE_FOUND) { |
michael@0 | 402 | return anslen; |
michael@0 | 403 | } |
michael@0 | 404 | } |
michael@0 | 405 | #endif |
michael@0 | 406 | |
michael@0 | 407 | /* |
michael@0 | 408 | * If the ns_addr_list in the resolver context has changed, then |
michael@0 | 409 | * invalidate our cached copy and the associated timing data. |
michael@0 | 410 | */ |
michael@0 | 411 | if (EXT(statp).nscount != 0) { |
michael@0 | 412 | int needclose = 0; |
michael@0 | 413 | struct sockaddr_storage peer; |
michael@0 | 414 | socklen_t peerlen; |
michael@0 | 415 | |
michael@0 | 416 | if (EXT(statp).nscount != statp->nscount) |
michael@0 | 417 | needclose++; |
michael@0 | 418 | else |
michael@0 | 419 | for (ns = 0; ns < statp->nscount; ns++) { |
michael@0 | 420 | if (statp->nsaddr_list[ns].sin_family && |
michael@0 | 421 | !sock_eq((struct sockaddr *)(void *)&statp->nsaddr_list[ns], |
michael@0 | 422 | (struct sockaddr *)(void *)&EXT(statp).ext->nsaddrs[ns])) { |
michael@0 | 423 | needclose++; |
michael@0 | 424 | break; |
michael@0 | 425 | } |
michael@0 | 426 | |
michael@0 | 427 | if (EXT(statp).nssocks[ns] == -1) |
michael@0 | 428 | continue; |
michael@0 | 429 | peerlen = sizeof(peer); |
michael@0 | 430 | if (getsockname(EXT(statp).nssocks[ns], |
michael@0 | 431 | (struct sockaddr *)(void *)&peer, &peerlen) < 0) { |
michael@0 | 432 | needclose++; |
michael@0 | 433 | break; |
michael@0 | 434 | } |
michael@0 | 435 | if (!sock_eq((struct sockaddr *)(void *)&peer, |
michael@0 | 436 | get_nsaddr(statp, (size_t)ns))) { |
michael@0 | 437 | needclose++; |
michael@0 | 438 | break; |
michael@0 | 439 | } |
michael@0 | 440 | } |
michael@0 | 441 | if (needclose) { |
michael@0 | 442 | res_nclose(statp); |
michael@0 | 443 | EXT(statp).nscount = 0; |
michael@0 | 444 | } |
michael@0 | 445 | } |
michael@0 | 446 | |
michael@0 | 447 | /* |
michael@0 | 448 | * Maybe initialize our private copy of the ns_addr_list. |
michael@0 | 449 | */ |
michael@0 | 450 | if (EXT(statp).nscount == 0) { |
michael@0 | 451 | for (ns = 0; ns < statp->nscount; ns++) { |
michael@0 | 452 | EXT(statp).nstimes[ns] = RES_MAXTIME; |
michael@0 | 453 | EXT(statp).nssocks[ns] = -1; |
michael@0 | 454 | if (!statp->nsaddr_list[ns].sin_family) |
michael@0 | 455 | continue; |
michael@0 | 456 | EXT(statp).ext->nsaddrs[ns].sin = |
michael@0 | 457 | statp->nsaddr_list[ns]; |
michael@0 | 458 | } |
michael@0 | 459 | EXT(statp).nscount = statp->nscount; |
michael@0 | 460 | } |
michael@0 | 461 | |
michael@0 | 462 | /* |
michael@0 | 463 | * Some resolvers want to even out the load on their nameservers. |
michael@0 | 464 | * Note that RES_BLAST overrides RES_ROTATE. |
michael@0 | 465 | */ |
michael@0 | 466 | if ((statp->options & RES_ROTATE) != 0U && |
michael@0 | 467 | (statp->options & RES_BLAST) == 0U) { |
michael@0 | 468 | union res_sockaddr_union inu; |
michael@0 | 469 | struct sockaddr_in ina; |
michael@0 | 470 | int lastns = statp->nscount - 1; |
michael@0 | 471 | int fd; |
michael@0 | 472 | u_int16_t nstime; |
michael@0 | 473 | |
michael@0 | 474 | if (EXT(statp).ext != NULL) |
michael@0 | 475 | inu = EXT(statp).ext->nsaddrs[0]; |
michael@0 | 476 | ina = statp->nsaddr_list[0]; |
michael@0 | 477 | fd = EXT(statp).nssocks[0]; |
michael@0 | 478 | nstime = EXT(statp).nstimes[0]; |
michael@0 | 479 | for (ns = 0; ns < lastns; ns++) { |
michael@0 | 480 | if (EXT(statp).ext != NULL) |
michael@0 | 481 | EXT(statp).ext->nsaddrs[ns] = |
michael@0 | 482 | EXT(statp).ext->nsaddrs[ns + 1]; |
michael@0 | 483 | statp->nsaddr_list[ns] = statp->nsaddr_list[ns + 1]; |
michael@0 | 484 | EXT(statp).nssocks[ns] = EXT(statp).nssocks[ns + 1]; |
michael@0 | 485 | EXT(statp).nstimes[ns] = EXT(statp).nstimes[ns + 1]; |
michael@0 | 486 | } |
michael@0 | 487 | if (EXT(statp).ext != NULL) |
michael@0 | 488 | EXT(statp).ext->nsaddrs[lastns] = inu; |
michael@0 | 489 | statp->nsaddr_list[lastns] = ina; |
michael@0 | 490 | EXT(statp).nssocks[lastns] = fd; |
michael@0 | 491 | EXT(statp).nstimes[lastns] = nstime; |
michael@0 | 492 | } |
michael@0 | 493 | |
michael@0 | 494 | /* |
michael@0 | 495 | * Send request, RETRY times, or until successful. |
michael@0 | 496 | */ |
michael@0 | 497 | for (try = 0; try < statp->retry; try++) { |
michael@0 | 498 | for (ns = 0; ns < statp->nscount; ns++) { |
michael@0 | 499 | struct sockaddr *nsap; |
michael@0 | 500 | int nsaplen; |
michael@0 | 501 | nsap = get_nsaddr(statp, (size_t)ns); |
michael@0 | 502 | nsaplen = get_salen(nsap); |
michael@0 | 503 | statp->_flags &= ~RES_F_LASTMASK; |
michael@0 | 504 | statp->_flags |= (ns << RES_F_LASTSHIFT); |
michael@0 | 505 | same_ns: |
michael@0 | 506 | if (statp->qhook) { |
michael@0 | 507 | int done = 0, loops = 0; |
michael@0 | 508 | |
michael@0 | 509 | do { |
michael@0 | 510 | res_sendhookact act; |
michael@0 | 511 | |
michael@0 | 512 | act = (*statp->qhook)(&nsap, &buf, &buflen, |
michael@0 | 513 | ans, anssiz, &resplen); |
michael@0 | 514 | switch (act) { |
michael@0 | 515 | case res_goahead: |
michael@0 | 516 | done = 1; |
michael@0 | 517 | break; |
michael@0 | 518 | case res_nextns: |
michael@0 | 519 | res_nclose(statp); |
michael@0 | 520 | goto next_ns; |
michael@0 | 521 | case res_done: |
michael@0 | 522 | return (resplen); |
michael@0 | 523 | case res_modified: |
michael@0 | 524 | /* give the hook another try */ |
michael@0 | 525 | if (++loops < 42) /*doug adams*/ |
michael@0 | 526 | break; |
michael@0 | 527 | /*FALLTHROUGH*/ |
michael@0 | 528 | case res_error: |
michael@0 | 529 | /*FALLTHROUGH*/ |
michael@0 | 530 | default: |
michael@0 | 531 | goto fail; |
michael@0 | 532 | } |
michael@0 | 533 | } while (!done); |
michael@0 | 534 | } |
michael@0 | 535 | |
michael@0 | 536 | Dprint(((statp->options & RES_DEBUG) && |
michael@0 | 537 | getnameinfo(nsap, (socklen_t)nsaplen, abuf, sizeof(abuf), |
michael@0 | 538 | NULL, 0, niflags) == 0), |
michael@0 | 539 | (stdout, ";; Querying server (# %d) address = %s\n", |
michael@0 | 540 | ns + 1, abuf)); |
michael@0 | 541 | |
michael@0 | 542 | |
michael@0 | 543 | if (v_circuit) { |
michael@0 | 544 | /* Use VC; at most one attempt per server. */ |
michael@0 | 545 | try = statp->retry; |
michael@0 | 546 | n = send_vc(statp, buf, buflen, ans, anssiz, &terrno, |
michael@0 | 547 | ns); |
michael@0 | 548 | if (n < 0) |
michael@0 | 549 | goto fail; |
michael@0 | 550 | if (n == 0) |
michael@0 | 551 | goto next_ns; |
michael@0 | 552 | resplen = n; |
michael@0 | 553 | } else { |
michael@0 | 554 | /* Use datagrams. */ |
michael@0 | 555 | n = send_dg(statp, buf, buflen, ans, anssiz, &terrno, |
michael@0 | 556 | ns, &v_circuit, &gotsomewhere); |
michael@0 | 557 | if (n < 0) |
michael@0 | 558 | goto fail; |
michael@0 | 559 | if (n == 0) |
michael@0 | 560 | goto next_ns; |
michael@0 | 561 | if (v_circuit) |
michael@0 | 562 | goto same_ns; |
michael@0 | 563 | resplen = n; |
michael@0 | 564 | } |
michael@0 | 565 | |
michael@0 | 566 | Dprint((statp->options & RES_DEBUG) || |
michael@0 | 567 | ((statp->pfcode & RES_PRF_REPLY) && |
michael@0 | 568 | (statp->pfcode & RES_PRF_HEAD1)), |
michael@0 | 569 | (stdout, ";; got answer:\n")); |
michael@0 | 570 | |
michael@0 | 571 | DprintQ((statp->options & RES_DEBUG) || |
michael@0 | 572 | (statp->pfcode & RES_PRF_REPLY), |
michael@0 | 573 | (stdout, "%s", ""), |
michael@0 | 574 | ans, (resplen > anssiz) ? anssiz : resplen); |
michael@0 | 575 | |
michael@0 | 576 | #if USE_RESOLV_CACHE |
michael@0 | 577 | if (cache_status == RESOLV_CACHE_NOTFOUND) { |
michael@0 | 578 | _resolv_cache_add(cache, buf, buflen, |
michael@0 | 579 | ans, resplen); |
michael@0 | 580 | } |
michael@0 | 581 | #endif |
michael@0 | 582 | /* |
michael@0 | 583 | * If we have temporarily opened a virtual circuit, |
michael@0 | 584 | * or if we haven't been asked to keep a socket open, |
michael@0 | 585 | * close the socket. |
michael@0 | 586 | */ |
michael@0 | 587 | if ((v_circuit && (statp->options & RES_USEVC) == 0U) || |
michael@0 | 588 | (statp->options & RES_STAYOPEN) == 0U) { |
michael@0 | 589 | res_nclose(statp); |
michael@0 | 590 | } |
michael@0 | 591 | if (statp->rhook) { |
michael@0 | 592 | int done = 0, loops = 0; |
michael@0 | 593 | |
michael@0 | 594 | do { |
michael@0 | 595 | res_sendhookact act; |
michael@0 | 596 | |
michael@0 | 597 | act = (*statp->rhook)(nsap, buf, buflen, |
michael@0 | 598 | ans, anssiz, &resplen); |
michael@0 | 599 | switch (act) { |
michael@0 | 600 | case res_goahead: |
michael@0 | 601 | case res_done: |
michael@0 | 602 | done = 1; |
michael@0 | 603 | break; |
michael@0 | 604 | case res_nextns: |
michael@0 | 605 | res_nclose(statp); |
michael@0 | 606 | goto next_ns; |
michael@0 | 607 | case res_modified: |
michael@0 | 608 | /* give the hook another try */ |
michael@0 | 609 | if (++loops < 42) /*doug adams*/ |
michael@0 | 610 | break; |
michael@0 | 611 | /*FALLTHROUGH*/ |
michael@0 | 612 | case res_error: |
michael@0 | 613 | /*FALLTHROUGH*/ |
michael@0 | 614 | default: |
michael@0 | 615 | goto fail; |
michael@0 | 616 | } |
michael@0 | 617 | } while (!done); |
michael@0 | 618 | |
michael@0 | 619 | } |
michael@0 | 620 | return (resplen); |
michael@0 | 621 | next_ns: ; |
michael@0 | 622 | } /*foreach ns*/ |
michael@0 | 623 | } /*foreach retry*/ |
michael@0 | 624 | res_nclose(statp); |
michael@0 | 625 | if (!v_circuit) { |
michael@0 | 626 | if (!gotsomewhere) |
michael@0 | 627 | errno = ECONNREFUSED; /* no nameservers found */ |
michael@0 | 628 | else |
michael@0 | 629 | errno = ETIMEDOUT; /* no answer obtained */ |
michael@0 | 630 | } else |
michael@0 | 631 | errno = terrno; |
michael@0 | 632 | return (-1); |
michael@0 | 633 | fail: |
michael@0 | 634 | res_nclose(statp); |
michael@0 | 635 | return (-1); |
michael@0 | 636 | } |
michael@0 | 637 | |
michael@0 | 638 | /* Private */ |
michael@0 | 639 | |
michael@0 | 640 | static int |
michael@0 | 641 | get_salen(sa) |
michael@0 | 642 | const struct sockaddr *sa; |
michael@0 | 643 | { |
michael@0 | 644 | |
michael@0 | 645 | #ifdef HAVE_SA_LEN |
michael@0 | 646 | /* There are people do not set sa_len. Be forgiving to them. */ |
michael@0 | 647 | if (sa->sa_len) |
michael@0 | 648 | return (sa->sa_len); |
michael@0 | 649 | #endif |
michael@0 | 650 | |
michael@0 | 651 | if (sa->sa_family == AF_INET) |
michael@0 | 652 | return (sizeof(struct sockaddr_in)); |
michael@0 | 653 | else if (sa->sa_family == AF_INET6) |
michael@0 | 654 | return (sizeof(struct sockaddr_in6)); |
michael@0 | 655 | else |
michael@0 | 656 | return (0); /* unknown, die on connect */ |
michael@0 | 657 | } |
michael@0 | 658 | |
michael@0 | 659 | /* |
michael@0 | 660 | * pick appropriate nsaddr_list for use. see res_init() for initialization. |
michael@0 | 661 | */ |
michael@0 | 662 | static struct sockaddr * |
michael@0 | 663 | get_nsaddr(statp, n) |
michael@0 | 664 | res_state statp; |
michael@0 | 665 | size_t n; |
michael@0 | 666 | { |
michael@0 | 667 | |
michael@0 | 668 | if (!statp->nsaddr_list[n].sin_family && EXT(statp).ext) { |
michael@0 | 669 | /* |
michael@0 | 670 | * - EXT(statp).ext->nsaddrs[n] holds an address that is larger |
michael@0 | 671 | * than struct sockaddr, and |
michael@0 | 672 | * - user code did not update statp->nsaddr_list[n]. |
michael@0 | 673 | */ |
michael@0 | 674 | return (struct sockaddr *)(void *)&EXT(statp).ext->nsaddrs[n]; |
michael@0 | 675 | } else { |
michael@0 | 676 | /* |
michael@0 | 677 | * - user code updated statp->nsaddr_list[n], or |
michael@0 | 678 | * - statp->nsaddr_list[n] has the same content as |
michael@0 | 679 | * EXT(statp).ext->nsaddrs[n]. |
michael@0 | 680 | */ |
michael@0 | 681 | return (struct sockaddr *)(void *)&statp->nsaddr_list[n]; |
michael@0 | 682 | } |
michael@0 | 683 | } |
michael@0 | 684 | |
michael@0 | 685 | static int |
michael@0 | 686 | send_vc(res_state statp, |
michael@0 | 687 | const u_char *buf, int buflen, u_char *ans, int anssiz, |
michael@0 | 688 | int *terrno, int ns) |
michael@0 | 689 | { |
michael@0 | 690 | const HEADER *hp = (const HEADER *)(const void *)buf; |
michael@0 | 691 | HEADER *anhp = (HEADER *)(void *)ans; |
michael@0 | 692 | struct sockaddr *nsap; |
michael@0 | 693 | int nsaplen; |
michael@0 | 694 | int truncating, connreset, resplen, n; |
michael@0 | 695 | struct iovec iov[2]; |
michael@0 | 696 | u_short len; |
michael@0 | 697 | u_char *cp; |
michael@0 | 698 | void *tmp; |
michael@0 | 699 | |
michael@0 | 700 | nsap = get_nsaddr(statp, (size_t)ns); |
michael@0 | 701 | nsaplen = get_salen(nsap); |
michael@0 | 702 | |
michael@0 | 703 | connreset = 0; |
michael@0 | 704 | same_ns: |
michael@0 | 705 | truncating = 0; |
michael@0 | 706 | |
michael@0 | 707 | /* Are we still talking to whom we want to talk to? */ |
michael@0 | 708 | if (statp->_vcsock >= 0 && (statp->_flags & RES_F_VC) != 0) { |
michael@0 | 709 | struct sockaddr_storage peer; |
michael@0 | 710 | socklen_t size = sizeof peer; |
michael@0 | 711 | |
michael@0 | 712 | if (getpeername(statp->_vcsock, |
michael@0 | 713 | (struct sockaddr *)(void *)&peer, &size) < 0 || |
michael@0 | 714 | !sock_eq((struct sockaddr *)(void *)&peer, nsap)) { |
michael@0 | 715 | res_nclose(statp); |
michael@0 | 716 | statp->_flags &= ~RES_F_VC; |
michael@0 | 717 | } |
michael@0 | 718 | } |
michael@0 | 719 | |
michael@0 | 720 | if (statp->_vcsock < 0 || (statp->_flags & RES_F_VC) == 0) { |
michael@0 | 721 | if (statp->_vcsock >= 0) |
michael@0 | 722 | res_nclose(statp); |
michael@0 | 723 | |
michael@0 | 724 | statp->_vcsock = socket(nsap->sa_family, SOCK_STREAM, 0); |
michael@0 | 725 | if (statp->_vcsock > highestFD) { |
michael@0 | 726 | res_nclose(statp); |
michael@0 | 727 | errno = ENOTSOCK; |
michael@0 | 728 | } |
michael@0 | 729 | if (statp->_vcsock < 0) { |
michael@0 | 730 | switch (errno) { |
michael@0 | 731 | case EPROTONOSUPPORT: |
michael@0 | 732 | #ifdef EPFNOSUPPORT |
michael@0 | 733 | case EPFNOSUPPORT: |
michael@0 | 734 | #endif |
michael@0 | 735 | case EAFNOSUPPORT: |
michael@0 | 736 | Perror(statp, stderr, "socket(vc)", errno); |
michael@0 | 737 | return (0); |
michael@0 | 738 | default: |
michael@0 | 739 | *terrno = errno; |
michael@0 | 740 | Perror(statp, stderr, "socket(vc)", errno); |
michael@0 | 741 | return (-1); |
michael@0 | 742 | } |
michael@0 | 743 | } |
michael@0 | 744 | errno = 0; |
michael@0 | 745 | if (random_bind(statp->_vcsock,nsap->sa_family) < 0) { |
michael@0 | 746 | *terrno = errno; |
michael@0 | 747 | Aerror(statp, stderr, "bind/vc", errno, nsap, |
michael@0 | 748 | nsaplen); |
michael@0 | 749 | res_nclose(statp); |
michael@0 | 750 | return (0); |
michael@0 | 751 | } |
michael@0 | 752 | if (connect(statp->_vcsock, nsap, (socklen_t)nsaplen) < 0) { |
michael@0 | 753 | *terrno = errno; |
michael@0 | 754 | Aerror(statp, stderr, "connect/vc", errno, nsap, |
michael@0 | 755 | nsaplen); |
michael@0 | 756 | res_nclose(statp); |
michael@0 | 757 | return (0); |
michael@0 | 758 | } |
michael@0 | 759 | statp->_flags |= RES_F_VC; |
michael@0 | 760 | } |
michael@0 | 761 | |
michael@0 | 762 | /* |
michael@0 | 763 | * Send length & message |
michael@0 | 764 | */ |
michael@0 | 765 | ns_put16((u_short)buflen, (u_char*)(void *)&len); |
michael@0 | 766 | iov[0] = evConsIovec(&len, INT16SZ); |
michael@0 | 767 | DE_CONST(buf, tmp); |
michael@0 | 768 | iov[1] = evConsIovec(tmp, (size_t)buflen); |
michael@0 | 769 | if (writev(statp->_vcsock, iov, 2) != (INT16SZ + buflen)) { |
michael@0 | 770 | *terrno = errno; |
michael@0 | 771 | Perror(statp, stderr, "write failed", errno); |
michael@0 | 772 | res_nclose(statp); |
michael@0 | 773 | return (0); |
michael@0 | 774 | } |
michael@0 | 775 | /* |
michael@0 | 776 | * Receive length & response |
michael@0 | 777 | */ |
michael@0 | 778 | read_len: |
michael@0 | 779 | cp = ans; |
michael@0 | 780 | len = INT16SZ; |
michael@0 | 781 | while ((n = read(statp->_vcsock, (char *)cp, (size_t)len)) > 0) { |
michael@0 | 782 | cp += n; |
michael@0 | 783 | if ((len -= n) == 0) |
michael@0 | 784 | break; |
michael@0 | 785 | } |
michael@0 | 786 | if (n <= 0) { |
michael@0 | 787 | *terrno = errno; |
michael@0 | 788 | Perror(statp, stderr, "read failed", errno); |
michael@0 | 789 | res_nclose(statp); |
michael@0 | 790 | /* |
michael@0 | 791 | * A long running process might get its TCP |
michael@0 | 792 | * connection reset if the remote server was |
michael@0 | 793 | * restarted. Requery the server instead of |
michael@0 | 794 | * trying a new one. When there is only one |
michael@0 | 795 | * server, this means that a query might work |
michael@0 | 796 | * instead of failing. We only allow one reset |
michael@0 | 797 | * per query to prevent looping. |
michael@0 | 798 | */ |
michael@0 | 799 | if (*terrno == ECONNRESET && !connreset) { |
michael@0 | 800 | connreset = 1; |
michael@0 | 801 | res_nclose(statp); |
michael@0 | 802 | goto same_ns; |
michael@0 | 803 | } |
michael@0 | 804 | res_nclose(statp); |
michael@0 | 805 | return (0); |
michael@0 | 806 | } |
michael@0 | 807 | resplen = ns_get16(ans); |
michael@0 | 808 | if (resplen > anssiz) { |
michael@0 | 809 | Dprint(statp->options & RES_DEBUG, |
michael@0 | 810 | (stdout, ";; response truncated\n") |
michael@0 | 811 | ); |
michael@0 | 812 | truncating = 1; |
michael@0 | 813 | len = anssiz; |
michael@0 | 814 | } else |
michael@0 | 815 | len = resplen; |
michael@0 | 816 | if (len < HFIXEDSZ) { |
michael@0 | 817 | /* |
michael@0 | 818 | * Undersized message. |
michael@0 | 819 | */ |
michael@0 | 820 | Dprint(statp->options & RES_DEBUG, |
michael@0 | 821 | (stdout, ";; undersized: %d\n", len)); |
michael@0 | 822 | *terrno = EMSGSIZE; |
michael@0 | 823 | res_nclose(statp); |
michael@0 | 824 | return (0); |
michael@0 | 825 | } |
michael@0 | 826 | cp = ans; |
michael@0 | 827 | while (len != 0 && (n = read(statp->_vcsock, (char *)cp, (size_t)len)) > 0){ |
michael@0 | 828 | cp += n; |
michael@0 | 829 | len -= n; |
michael@0 | 830 | } |
michael@0 | 831 | if (n <= 0) { |
michael@0 | 832 | *terrno = errno; |
michael@0 | 833 | Perror(statp, stderr, "read(vc)", errno); |
michael@0 | 834 | res_nclose(statp); |
michael@0 | 835 | return (0); |
michael@0 | 836 | } |
michael@0 | 837 | if (truncating) { |
michael@0 | 838 | /* |
michael@0 | 839 | * Flush rest of answer so connection stays in synch. |
michael@0 | 840 | */ |
michael@0 | 841 | anhp->tc = 1; |
michael@0 | 842 | len = resplen - anssiz; |
michael@0 | 843 | while (len != 0) { |
michael@0 | 844 | char junk[PACKETSZ]; |
michael@0 | 845 | |
michael@0 | 846 | n = read(statp->_vcsock, junk, |
michael@0 | 847 | (len > sizeof junk) ? sizeof junk : len); |
michael@0 | 848 | if (n > 0) |
michael@0 | 849 | len -= n; |
michael@0 | 850 | else |
michael@0 | 851 | break; |
michael@0 | 852 | } |
michael@0 | 853 | } |
michael@0 | 854 | /* |
michael@0 | 855 | * If the calling applicating has bailed out of |
michael@0 | 856 | * a previous call and failed to arrange to have |
michael@0 | 857 | * the circuit closed or the server has got |
michael@0 | 858 | * itself confused, then drop the packet and |
michael@0 | 859 | * wait for the correct one. |
michael@0 | 860 | */ |
michael@0 | 861 | if (hp->id != anhp->id) { |
michael@0 | 862 | DprintQ((statp->options & RES_DEBUG) || |
michael@0 | 863 | (statp->pfcode & RES_PRF_REPLY), |
michael@0 | 864 | (stdout, ";; old answer (unexpected):\n"), |
michael@0 | 865 | ans, (resplen > anssiz) ? anssiz: resplen); |
michael@0 | 866 | goto read_len; |
michael@0 | 867 | } |
michael@0 | 868 | |
michael@0 | 869 | /* |
michael@0 | 870 | * All is well, or the error is fatal. Signal that the |
michael@0 | 871 | * next nameserver ought not be tried. |
michael@0 | 872 | */ |
michael@0 | 873 | return (resplen); |
michael@0 | 874 | } |
michael@0 | 875 | |
michael@0 | 876 | static int |
michael@0 | 877 | send_dg(res_state statp, |
michael@0 | 878 | const u_char *buf, int buflen, u_char *ans, int anssiz, |
michael@0 | 879 | int *terrno, int ns, int *v_circuit, int *gotsomewhere) |
michael@0 | 880 | { |
michael@0 | 881 | const HEADER *hp = (const HEADER *)(const void *)buf; |
michael@0 | 882 | HEADER *anhp = (HEADER *)(void *)ans; |
michael@0 | 883 | const struct sockaddr *nsap; |
michael@0 | 884 | int nsaplen; |
michael@0 | 885 | struct timespec now, timeout, finish; |
michael@0 | 886 | fd_set dsmask; |
michael@0 | 887 | struct sockaddr_storage from; |
michael@0 | 888 | socklen_t fromlen; |
michael@0 | 889 | int resplen, seconds, n, s; |
michael@0 | 890 | |
michael@0 | 891 | nsap = get_nsaddr(statp, (size_t)ns); |
michael@0 | 892 | nsaplen = get_salen(nsap); |
michael@0 | 893 | if (EXT(statp).nssocks[ns] == -1) { |
michael@0 | 894 | EXT(statp).nssocks[ns] = socket(nsap->sa_family, SOCK_DGRAM, 0); |
michael@0 | 895 | if (EXT(statp).nssocks[ns] > highestFD) { |
michael@0 | 896 | res_nclose(statp); |
michael@0 | 897 | errno = ENOTSOCK; |
michael@0 | 898 | } |
michael@0 | 899 | if (EXT(statp).nssocks[ns] < 0) { |
michael@0 | 900 | switch (errno) { |
michael@0 | 901 | case EPROTONOSUPPORT: |
michael@0 | 902 | #ifdef EPFNOSUPPORT |
michael@0 | 903 | case EPFNOSUPPORT: |
michael@0 | 904 | #endif |
michael@0 | 905 | case EAFNOSUPPORT: |
michael@0 | 906 | Perror(statp, stderr, "socket(dg)", errno); |
michael@0 | 907 | return (0); |
michael@0 | 908 | default: |
michael@0 | 909 | *terrno = errno; |
michael@0 | 910 | Perror(statp, stderr, "socket(dg)", errno); |
michael@0 | 911 | return (-1); |
michael@0 | 912 | } |
michael@0 | 913 | } |
michael@0 | 914 | #ifndef CANNOT_CONNECT_DGRAM |
michael@0 | 915 | /* |
michael@0 | 916 | * On a 4.3BSD+ machine (client and server, |
michael@0 | 917 | * actually), sending to a nameserver datagram |
michael@0 | 918 | * port with no nameserver will cause an |
michael@0 | 919 | * ICMP port unreachable message to be returned. |
michael@0 | 920 | * If our datagram socket is "connected" to the |
michael@0 | 921 | * server, we get an ECONNREFUSED error on the next |
michael@0 | 922 | * socket operation, and select returns if the |
michael@0 | 923 | * error message is received. We can thus detect |
michael@0 | 924 | * the absence of a nameserver without timing out. |
michael@0 | 925 | */ |
michael@0 | 926 | if (random_bind(EXT(statp).nssocks[ns], nsap->sa_family) < 0) { |
michael@0 | 927 | Aerror(statp, stderr, "bind(dg)", errno, nsap, |
michael@0 | 928 | nsaplen); |
michael@0 | 929 | res_nclose(statp); |
michael@0 | 930 | return (0); |
michael@0 | 931 | } |
michael@0 | 932 | if (connect(EXT(statp).nssocks[ns], nsap, (socklen_t)nsaplen) < 0) { |
michael@0 | 933 | Aerror(statp, stderr, "connect(dg)", errno, nsap, |
michael@0 | 934 | nsaplen); |
michael@0 | 935 | res_nclose(statp); |
michael@0 | 936 | return (0); |
michael@0 | 937 | } |
michael@0 | 938 | #endif /* !CANNOT_CONNECT_DGRAM */ |
michael@0 | 939 | Dprint(statp->options & RES_DEBUG, |
michael@0 | 940 | (stdout, ";; new DG socket\n")) |
michael@0 | 941 | } |
michael@0 | 942 | s = EXT(statp).nssocks[ns]; |
michael@0 | 943 | #ifndef CANNOT_CONNECT_DGRAM |
michael@0 | 944 | if (send(s, (const char*)buf, (size_t)buflen, 0) != buflen) { |
michael@0 | 945 | Perror(statp, stderr, "send", errno); |
michael@0 | 946 | res_nclose(statp); |
michael@0 | 947 | return (0); |
michael@0 | 948 | } |
michael@0 | 949 | #else /* !CANNOT_CONNECT_DGRAM */ |
michael@0 | 950 | if (sendto(s, (const char*)buf, buflen, 0, nsap, nsaplen) != buflen) |
michael@0 | 951 | { |
michael@0 | 952 | Aerror(statp, stderr, "sendto", errno, nsap, nsaplen); |
michael@0 | 953 | res_nclose(statp); |
michael@0 | 954 | return (0); |
michael@0 | 955 | } |
michael@0 | 956 | #endif /* !CANNOT_CONNECT_DGRAM */ |
michael@0 | 957 | |
michael@0 | 958 | /* |
michael@0 | 959 | * Wait for reply. |
michael@0 | 960 | */ |
michael@0 | 961 | seconds = (statp->retrans << ns); |
michael@0 | 962 | if (ns > 0) |
michael@0 | 963 | seconds /= statp->nscount; |
michael@0 | 964 | if (seconds <= 0) |
michael@0 | 965 | seconds = 1; |
michael@0 | 966 | now = evNowTime(); |
michael@0 | 967 | timeout = evConsTime((long)seconds, 0L); |
michael@0 | 968 | finish = evAddTime(now, timeout); |
michael@0 | 969 | goto nonow; |
michael@0 | 970 | wait: |
michael@0 | 971 | now = evNowTime(); |
michael@0 | 972 | nonow: |
michael@0 | 973 | FD_ZERO(&dsmask); |
michael@0 | 974 | FD_SET(s, &dsmask); |
michael@0 | 975 | if (evCmpTime(finish, now) > 0) |
michael@0 | 976 | timeout = evSubTime(finish, now); |
michael@0 | 977 | else |
michael@0 | 978 | timeout = evConsTime(0L, 0L); |
michael@0 | 979 | n = pselect(s + 1, &dsmask, NULL, NULL, &timeout, NULL); |
michael@0 | 980 | if (n == 0) { |
michael@0 | 981 | Dprint(statp->options & RES_DEBUG, (stdout, ";; timeout\n")); |
michael@0 | 982 | *gotsomewhere = 1; |
michael@0 | 983 | return (0); |
michael@0 | 984 | } |
michael@0 | 985 | if (n < 0) { |
michael@0 | 986 | if (errno == EINTR) |
michael@0 | 987 | goto wait; |
michael@0 | 988 | Perror(statp, stderr, "select", errno); |
michael@0 | 989 | res_nclose(statp); |
michael@0 | 990 | return (0); |
michael@0 | 991 | } |
michael@0 | 992 | errno = 0; |
michael@0 | 993 | fromlen = sizeof(from); |
michael@0 | 994 | resplen = recvfrom(s, (char*)ans, (size_t)anssiz,0, |
michael@0 | 995 | (struct sockaddr *)(void *)&from, &fromlen); |
michael@0 | 996 | if (resplen <= 0) { |
michael@0 | 997 | Perror(statp, stderr, "recvfrom", errno); |
michael@0 | 998 | res_nclose(statp); |
michael@0 | 999 | return (0); |
michael@0 | 1000 | } |
michael@0 | 1001 | *gotsomewhere = 1; |
michael@0 | 1002 | if (resplen < HFIXEDSZ) { |
michael@0 | 1003 | /* |
michael@0 | 1004 | * Undersized message. |
michael@0 | 1005 | */ |
michael@0 | 1006 | Dprint(statp->options & RES_DEBUG, |
michael@0 | 1007 | (stdout, ";; undersized: %d\n", |
michael@0 | 1008 | resplen)); |
michael@0 | 1009 | *terrno = EMSGSIZE; |
michael@0 | 1010 | res_nclose(statp); |
michael@0 | 1011 | return (0); |
michael@0 | 1012 | } |
michael@0 | 1013 | if (hp->id != anhp->id) { |
michael@0 | 1014 | /* |
michael@0 | 1015 | * response from old query, ignore it. |
michael@0 | 1016 | * XXX - potential security hazard could |
michael@0 | 1017 | * be detected here. |
michael@0 | 1018 | */ |
michael@0 | 1019 | DprintQ((statp->options & RES_DEBUG) || |
michael@0 | 1020 | (statp->pfcode & RES_PRF_REPLY), |
michael@0 | 1021 | (stdout, ";; old answer:\n"), |
michael@0 | 1022 | ans, (resplen > anssiz) ? anssiz : resplen); |
michael@0 | 1023 | goto wait; |
michael@0 | 1024 | } |
michael@0 | 1025 | if (!(statp->options & RES_INSECURE1) && |
michael@0 | 1026 | !res_ourserver_p(statp, (struct sockaddr *)(void *)&from)) { |
michael@0 | 1027 | /* |
michael@0 | 1028 | * response from wrong server? ignore it. |
michael@0 | 1029 | * XXX - potential security hazard could |
michael@0 | 1030 | * be detected here. |
michael@0 | 1031 | */ |
michael@0 | 1032 | DprintQ((statp->options & RES_DEBUG) || |
michael@0 | 1033 | (statp->pfcode & RES_PRF_REPLY), |
michael@0 | 1034 | (stdout, ";; not our server:\n"), |
michael@0 | 1035 | ans, (resplen > anssiz) ? anssiz : resplen); |
michael@0 | 1036 | goto wait; |
michael@0 | 1037 | } |
michael@0 | 1038 | #ifdef RES_USE_EDNS0 |
michael@0 | 1039 | if (anhp->rcode == FORMERR && (statp->options & RES_USE_EDNS0) != 0U) { |
michael@0 | 1040 | /* |
michael@0 | 1041 | * Do not retry if the server do not understand EDNS0. |
michael@0 | 1042 | * The case has to be captured here, as FORMERR packet do not |
michael@0 | 1043 | * carry query section, hence res_queriesmatch() returns 0. |
michael@0 | 1044 | */ |
michael@0 | 1045 | DprintQ(statp->options & RES_DEBUG, |
michael@0 | 1046 | (stdout, "server rejected query with EDNS0:\n"), |
michael@0 | 1047 | ans, (resplen > anssiz) ? anssiz : resplen); |
michael@0 | 1048 | /* record the error */ |
michael@0 | 1049 | statp->_flags |= RES_F_EDNS0ERR; |
michael@0 | 1050 | res_nclose(statp); |
michael@0 | 1051 | return (0); |
michael@0 | 1052 | } |
michael@0 | 1053 | #endif |
michael@0 | 1054 | if (!(statp->options & RES_INSECURE2) && |
michael@0 | 1055 | !res_queriesmatch(buf, buf + buflen, |
michael@0 | 1056 | ans, ans + anssiz)) { |
michael@0 | 1057 | /* |
michael@0 | 1058 | * response contains wrong query? ignore it. |
michael@0 | 1059 | * XXX - potential security hazard could |
michael@0 | 1060 | * be detected here. |
michael@0 | 1061 | */ |
michael@0 | 1062 | DprintQ((statp->options & RES_DEBUG) || |
michael@0 | 1063 | (statp->pfcode & RES_PRF_REPLY), |
michael@0 | 1064 | (stdout, ";; wrong query name:\n"), |
michael@0 | 1065 | ans, (resplen > anssiz) ? anssiz : resplen); |
michael@0 | 1066 | goto wait; |
michael@0 | 1067 | } |
michael@0 | 1068 | if (anhp->rcode == SERVFAIL || |
michael@0 | 1069 | anhp->rcode == NOTIMP || |
michael@0 | 1070 | anhp->rcode == REFUSED) { |
michael@0 | 1071 | DprintQ(statp->options & RES_DEBUG, |
michael@0 | 1072 | (stdout, "server rejected query:\n"), |
michael@0 | 1073 | ans, (resplen > anssiz) ? anssiz : resplen); |
michael@0 | 1074 | res_nclose(statp); |
michael@0 | 1075 | /* don't retry if called from dig */ |
michael@0 | 1076 | if (!statp->pfcode) |
michael@0 | 1077 | return (0); |
michael@0 | 1078 | } |
michael@0 | 1079 | if (!(statp->options & RES_IGNTC) && anhp->tc) { |
michael@0 | 1080 | /* |
michael@0 | 1081 | * To get the rest of answer, |
michael@0 | 1082 | * use TCP with same server. |
michael@0 | 1083 | */ |
michael@0 | 1084 | Dprint(statp->options & RES_DEBUG, |
michael@0 | 1085 | (stdout, ";; truncated answer\n")); |
michael@0 | 1086 | *v_circuit = 1; |
michael@0 | 1087 | res_nclose(statp); |
michael@0 | 1088 | return (1); |
michael@0 | 1089 | } |
michael@0 | 1090 | /* |
michael@0 | 1091 | * All is well, or the error is fatal. Signal that the |
michael@0 | 1092 | * next nameserver ought not be tried. |
michael@0 | 1093 | */ |
michael@0 | 1094 | return (resplen); |
michael@0 | 1095 | } |
michael@0 | 1096 | |
michael@0 | 1097 | static void |
michael@0 | 1098 | Aerror(const res_state statp, FILE *file, const char *string, int error, |
michael@0 | 1099 | const struct sockaddr *address, int alen) |
michael@0 | 1100 | { |
michael@0 | 1101 | int save = errno; |
michael@0 | 1102 | char hbuf[NI_MAXHOST]; |
michael@0 | 1103 | char sbuf[NI_MAXSERV]; |
michael@0 | 1104 | |
michael@0 | 1105 | alen = alen; |
michael@0 | 1106 | |
michael@0 | 1107 | if ((statp->options & RES_DEBUG) != 0U) { |
michael@0 | 1108 | if (getnameinfo(address, (socklen_t)alen, hbuf, sizeof(hbuf), |
michael@0 | 1109 | sbuf, sizeof(sbuf), niflags)) { |
michael@0 | 1110 | strncpy(hbuf, "?", sizeof(hbuf) - 1); |
michael@0 | 1111 | hbuf[sizeof(hbuf) - 1] = '\0'; |
michael@0 | 1112 | strncpy(sbuf, "?", sizeof(sbuf) - 1); |
michael@0 | 1113 | sbuf[sizeof(sbuf) - 1] = '\0'; |
michael@0 | 1114 | } |
michael@0 | 1115 | fprintf(file, "res_send: %s ([%s].%s): %s\n", |
michael@0 | 1116 | string, hbuf, sbuf, strerror(error)); |
michael@0 | 1117 | } |
michael@0 | 1118 | errno = save; |
michael@0 | 1119 | } |
michael@0 | 1120 | |
michael@0 | 1121 | static void |
michael@0 | 1122 | Perror(const res_state statp, FILE *file, const char *string, int error) { |
michael@0 | 1123 | int save = errno; |
michael@0 | 1124 | |
michael@0 | 1125 | if ((statp->options & RES_DEBUG) != 0U) |
michael@0 | 1126 | fprintf(file, "res_send: %s: %s\n", |
michael@0 | 1127 | string, strerror(error)); |
michael@0 | 1128 | errno = save; |
michael@0 | 1129 | } |
michael@0 | 1130 | |
michael@0 | 1131 | static int |
michael@0 | 1132 | sock_eq(struct sockaddr *a, struct sockaddr *b) { |
michael@0 | 1133 | struct sockaddr_in *a4, *b4; |
michael@0 | 1134 | struct sockaddr_in6 *a6, *b6; |
michael@0 | 1135 | |
michael@0 | 1136 | if (a->sa_family != b->sa_family) |
michael@0 | 1137 | return 0; |
michael@0 | 1138 | switch (a->sa_family) { |
michael@0 | 1139 | case AF_INET: |
michael@0 | 1140 | a4 = (struct sockaddr_in *)(void *)a; |
michael@0 | 1141 | b4 = (struct sockaddr_in *)(void *)b; |
michael@0 | 1142 | return a4->sin_port == b4->sin_port && |
michael@0 | 1143 | a4->sin_addr.s_addr == b4->sin_addr.s_addr; |
michael@0 | 1144 | case AF_INET6: |
michael@0 | 1145 | a6 = (struct sockaddr_in6 *)(void *)a; |
michael@0 | 1146 | b6 = (struct sockaddr_in6 *)(void *)b; |
michael@0 | 1147 | return a6->sin6_port == b6->sin6_port && |
michael@0 | 1148 | #ifdef HAVE_SIN6_SCOPE_ID |
michael@0 | 1149 | a6->sin6_scope_id == b6->sin6_scope_id && |
michael@0 | 1150 | #endif |
michael@0 | 1151 | IN6_ARE_ADDR_EQUAL(&a6->sin6_addr, &b6->sin6_addr); |
michael@0 | 1152 | default: |
michael@0 | 1153 | return 0; |
michael@0 | 1154 | } |
michael@0 | 1155 | } |
michael@0 | 1156 | |
michael@0 | 1157 | #ifdef NEED_PSELECT |
michael@0 | 1158 | /* XXX needs to move to the porting library. */ |
michael@0 | 1159 | static int |
michael@0 | 1160 | pselect(int nfds, void *rfds, void *wfds, void *efds, |
michael@0 | 1161 | struct timespec *tsp, const sigset_t *sigmask) |
michael@0 | 1162 | { |
michael@0 | 1163 | struct timeval tv, *tvp; |
michael@0 | 1164 | sigset_t sigs; |
michael@0 | 1165 | int n; |
michael@0 | 1166 | |
michael@0 | 1167 | if (tsp) { |
michael@0 | 1168 | tvp = &tv; |
michael@0 | 1169 | tv = evTimeVal(*tsp); |
michael@0 | 1170 | } else |
michael@0 | 1171 | tvp = NULL; |
michael@0 | 1172 | if (sigmask) |
michael@0 | 1173 | sigprocmask(SIG_SETMASK, sigmask, &sigs); |
michael@0 | 1174 | n = select(nfds, rfds, wfds, efds, tvp); |
michael@0 | 1175 | if (sigmask) |
michael@0 | 1176 | sigprocmask(SIG_SETMASK, &sigs, NULL); |
michael@0 | 1177 | if (tsp) |
michael@0 | 1178 | *tsp = evTimeSpec(tv); |
michael@0 | 1179 | return (n); |
michael@0 | 1180 | } |
michael@0 | 1181 | #endif |