1 /* $OpenBSD: res_send_async.c,v 1.22 2014/03/26 18:13:15 eric Exp $ */ 2 /* 3 * Copyright (c) 2012 Eric Faurot <eric@openbsd.org> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include <sys/types.h> 19 #include <sys/socket.h> 20 #include <sys/uio.h> 21 #include <netinet/in.h> 22 #include <arpa/nameser.h> 23 #include <netdb.h> 24 25 #include <asr.h> 26 #include <err.h> 27 #include <errno.h> 28 #include <fcntl.h> 29 #include <poll.h> 30 #include <resolv.h> /* for res_random */ 31 #include <stdlib.h> 32 #include <string.h> 33 #include <unistd.h> 34 35 #include "asr_private.h" 36 37 #define OP_QUERY (0) 38 39 static int res_send_async_run(struct asr_query *, struct asr_result *); 40 static int sockaddr_connect(const struct sockaddr *, int); 41 static int udp_send(struct asr_query *); 42 static int udp_recv(struct asr_query *); 43 static int tcp_write(struct asr_query *); 44 static int tcp_read(struct asr_query *); 45 static int validate_packet(struct asr_query *); 46 static int setup_query(struct asr_query *, const char *, const char *, int, int); 47 static int ensure_ibuf(struct asr_query *, size_t); 48 static int iter_ns(struct asr_query *); 49 50 #define AS_NS_SA(p) ((p)->as_ctx->ac_ns[(p)->as.dns.nsidx - 1]) 51 52 53 struct asr_query * 54 res_send_async(const unsigned char *buf, int buflen, void *asr) 55 { 56 struct asr_ctx *ac; 57 struct asr_query *as; 58 struct asr_unpack p; 59 struct asr_dns_header h; 60 struct asr_dns_query q; 61 62 DPRINT_PACKET("asr: res_send_async()", buf, buflen); 63 64 ac = asr_use_resolver(asr); 65 if ((as = asr_async_new(ac, ASR_SEND)) == NULL) { 66 asr_ctx_unref(ac); 67 return (NULL); /* errno set */ 68 } 69 as->as_run = res_send_async_run; 70 71 as->as.dns.flags |= ASYNC_EXTOBUF; 72 as->as.dns.obuf = (unsigned char *)buf; 73 as->as.dns.obuflen = buflen; 74 as->as.dns.obufsize = buflen; 75 76 asr_unpack_init(&p, buf, buflen); 77 asr_unpack_header(&p, &h); 78 asr_unpack_query(&p, &q); 79 if (p.err) { 80 errno = EINVAL; 81 goto err; 82 } 83 as->as.dns.reqid = h.id; 84 as->as.dns.type = q.q_type; 85 as->as.dns.class = q.q_class; 86 as->as.dns.dname = strdup(q.q_dname); 87 if (as->as.dns.dname == NULL) 88 goto err; /* errno set */ 89 90 asr_ctx_unref(ac); 91 return (as); 92 err: 93 if (as) 94 asr_async_free(as); 95 asr_ctx_unref(ac); 96 return (NULL); 97 } 98 99 /* 100 * Unlike res_query(), this version will actually return the packet 101 * if it has received a valid one (errno == 0) even if h_errno is 102 * not NETDB_SUCCESS. So the packet *must* be freed if necessary. 103 */ 104 struct asr_query * 105 res_query_async(const char *name, int class, int type, void *asr) 106 { 107 struct asr_ctx *ac; 108 struct asr_query *as; 109 110 DPRINT("asr: res_query_async(\"%s\", %i, %i)\n", name, class, type); 111 112 ac = asr_use_resolver(asr); 113 as = res_query_async_ctx(name, class, type, ac); 114 asr_ctx_unref(ac); 115 116 return (as); 117 } 118 119 struct asr_query * 120 res_query_async_ctx(const char *name, int class, int type, struct asr_ctx *a_ctx) 121 { 122 struct asr_query *as; 123 124 DPRINT("asr: res_query_async_ctx(\"%s\", %i, %i)\n", name, class, type); 125 126 if ((as = asr_async_new(a_ctx, ASR_SEND)) == NULL) 127 return (NULL); /* errno set */ 128 as->as_run = res_send_async_run; 129 130 /* This adds a "." to name if it doesn't already has one. 131 * That's how res_query() behaves (through res_mkquery"). 132 */ 133 if (setup_query(as, name, NULL, class, type) == -1) 134 goto err; /* errno set */ 135 136 return (as); 137 138 err: 139 if (as) 140 asr_async_free(as); 141 142 return (NULL); 143 } 144 145 static int 146 res_send_async_run(struct asr_query *as, struct asr_result *ar) 147 { 148 next: 149 switch (as->as_state) { 150 151 case ASR_STATE_INIT: 152 153 if (as->as_ctx->ac_nscount == 0) { 154 ar->ar_errno = ECONNREFUSED; 155 async_set_state(as, ASR_STATE_HALT); 156 break; 157 } 158 159 async_set_state(as, ASR_STATE_NEXT_NS); 160 break; 161 162 case ASR_STATE_NEXT_NS: 163 164 if (iter_ns(as) == -1) { 165 ar->ar_errno = ETIMEDOUT; 166 async_set_state(as, ASR_STATE_HALT); 167 break; 168 } 169 170 if (as->as_ctx->ac_options & RES_USEVC || 171 as->as.dns.obuflen > PACKETSZ) 172 async_set_state(as, ASR_STATE_TCP_WRITE); 173 else 174 async_set_state(as, ASR_STATE_UDP_SEND); 175 break; 176 177 case ASR_STATE_UDP_SEND: 178 179 if (udp_send(as) == -1) { 180 async_set_state(as, ASR_STATE_NEXT_NS); 181 break; 182 } 183 async_set_state(as, ASR_STATE_UDP_RECV); 184 ar->ar_cond = ASR_WANT_READ; 185 ar->ar_fd = as->as_fd; 186 ar->ar_timeout = as->as_timeout; 187 return (ASYNC_COND); 188 break; 189 190 case ASR_STATE_UDP_RECV: 191 192 if (udp_recv(as) == -1) { 193 if (errno == ENOMEM) { 194 ar->ar_errno = errno; 195 async_set_state(as, ASR_STATE_HALT); 196 break; 197 } 198 if (errno != EOVERFLOW) { 199 /* Fail or timeout */ 200 async_set_state(as, ASR_STATE_NEXT_NS); 201 break; 202 } 203 if (as->as_ctx->ac_options & RES_IGNTC) 204 async_set_state(as, ASR_STATE_PACKET); 205 else 206 async_set_state(as, ASR_STATE_TCP_WRITE); 207 } else 208 async_set_state(as, ASR_STATE_PACKET); 209 break; 210 211 case ASR_STATE_TCP_WRITE: 212 213 switch (tcp_write(as)) { 214 case -1: /* fail or timeout */ 215 async_set_state(as, ASR_STATE_NEXT_NS); 216 break; 217 case 0: 218 async_set_state(as, ASR_STATE_TCP_READ); 219 ar->ar_cond = ASR_WANT_READ; 220 ar->ar_fd = as->as_fd; 221 ar->ar_timeout = as->as_timeout; 222 return (ASYNC_COND); 223 case 1: 224 ar->ar_cond = ASR_WANT_WRITE; 225 ar->ar_fd = as->as_fd; 226 ar->ar_timeout = as->as_timeout; 227 return (ASYNC_COND); 228 } 229 break; 230 231 case ASR_STATE_TCP_READ: 232 233 switch (tcp_read(as)) { 234 case -1: /* Fail or timeout */ 235 if (errno == ENOMEM) { 236 ar->ar_errno = errno; 237 async_set_state(as, ASR_STATE_HALT); 238 } else 239 async_set_state(as, ASR_STATE_NEXT_NS); 240 break; 241 case 0: 242 async_set_state(as, ASR_STATE_PACKET); 243 break; 244 case 1: 245 ar->ar_cond = ASR_WANT_READ; 246 ar->ar_fd = as->as_fd; 247 ar->ar_timeout = as->as_timeout; 248 return (ASYNC_COND); 249 } 250 break; 251 252 case ASR_STATE_PACKET: 253 254 memmove(&ar->ar_ns, AS_NS_SA(as), AS_NS_SA(as)->sa_len); 255 ar->ar_datalen = as->as.dns.ibuflen; 256 ar->ar_data = as->as.dns.ibuf; 257 as->as.dns.ibuf = NULL; 258 ar->ar_errno = 0; 259 ar->ar_rcode = as->as.dns.rcode; 260 async_set_state(as, ASR_STATE_HALT); 261 break; 262 263 case ASR_STATE_HALT: 264 265 if (ar->ar_errno) { 266 ar->ar_h_errno = TRY_AGAIN; 267 ar->ar_count = 0; 268 ar->ar_datalen = -1; 269 ar->ar_data = NULL; 270 } else if (as->as.dns.ancount) { 271 ar->ar_h_errno = NETDB_SUCCESS; 272 ar->ar_count = as->as.dns.ancount; 273 } else { 274 ar->ar_count = 0; 275 switch (as->as.dns.rcode) { 276 case NXDOMAIN: 277 ar->ar_h_errno = HOST_NOT_FOUND; 278 break; 279 case SERVFAIL: 280 ar->ar_h_errno = TRY_AGAIN; 281 break; 282 case NOERROR: 283 ar->ar_h_errno = NO_DATA; 284 break; 285 default: 286 ar->ar_h_errno = NO_RECOVERY; 287 } 288 } 289 return (ASYNC_DONE); 290 291 default: 292 293 ar->ar_errno = EOPNOTSUPP; 294 ar->ar_h_errno = NETDB_INTERNAL; 295 async_set_state(as, ASR_STATE_HALT); 296 break; 297 } 298 goto next; 299 } 300 301 static int 302 sockaddr_connect(const struct sockaddr *sa, int socktype) 303 { 304 int errno_save, flags, sock; 305 306 if ((sock = socket(sa->sa_family, socktype, 0)) == -1) 307 goto fail; 308 309 if ((flags = fcntl(sock, F_GETFL, 0)) == -1) 310 goto fail; 311 312 flags |= O_NONBLOCK; 313 314 if ((flags = fcntl(sock, F_SETFL, flags)) == -1) 315 goto fail; 316 317 if (connect(sock, sa, sa->sa_len) == -1) { 318 /* 319 * In the TCP case, the caller will be asked to poll for 320 * POLLOUT so that we start writing the packet in tcp_write() 321 * when the connection is established, or fail there on error. 322 */ 323 if (errno == EINPROGRESS) 324 return (sock); 325 goto fail; 326 } 327 328 return (sock); 329 330 fail: 331 332 if (sock != -1) { 333 errno_save = errno; 334 close(sock); 335 errno = errno_save; 336 } 337 338 return (-1); 339 } 340 341 /* 342 * Prepare the DNS packet for the query type "type", class "class" and domain 343 * name created by the concatenation on "name" and "dom". 344 * Return 0 on success, set errno and return -1 on error. 345 */ 346 static int 347 setup_query(struct asr_query *as, const char *name, const char *dom, 348 int class, int type) 349 { 350 struct asr_pack p; 351 struct asr_dns_header h; 352 char fqdn[MAXDNAME]; 353 char dname[MAXDNAME]; 354 355 if (as->as.dns.flags & ASYNC_EXTOBUF) { 356 errno = EINVAL; 357 DPRINT("attempting to write in user packet"); 358 return (-1); 359 } 360 361 if (asr_make_fqdn(name, dom, fqdn, sizeof(fqdn)) > sizeof(fqdn)) { 362 errno = EINVAL; 363 DPRINT("asr_make_fqdn: name too long\n"); 364 return (-1); 365 } 366 367 if (asr_dname_from_fqdn(fqdn, dname, sizeof(dname)) == -1) { 368 errno = EINVAL; 369 DPRINT("asr_dname_from_fqdn: invalid\n"); 370 return (-1); 371 } 372 373 if (as->as.dns.obuf == NULL) { 374 as->as.dns.obufsize = PACKETSZ; 375 as->as.dns.obuf = malloc(as->as.dns.obufsize); 376 if (as->as.dns.obuf == NULL) 377 return (-1); /* errno set */ 378 } 379 as->as.dns.obuflen = 0; 380 381 memset(&h, 0, sizeof h); 382 h.id = res_randomid(); 383 if (as->as_ctx->ac_options & RES_RECURSE) 384 h.flags |= RD_MASK; 385 h.qdcount = 1; 386 387 asr_pack_init(&p, as->as.dns.obuf, as->as.dns.obufsize); 388 asr_pack_header(&p, &h); 389 asr_pack_query(&p, type, class, dname); 390 if (p.err) { 391 DPRINT("error packing query"); 392 errno = EINVAL; 393 return (-1); 394 } 395 396 /* Remember the parameters. */ 397 as->as.dns.reqid = h.id; 398 as->as.dns.type = type; 399 as->as.dns.class = class; 400 if (as->as.dns.dname) 401 free(as->as.dns.dname); 402 as->as.dns.dname = strdup(dname); 403 if (as->as.dns.dname == NULL) { 404 DPRINT("strdup"); 405 return (-1); /* errno set */ 406 } 407 as->as.dns.obuflen = p.offset; 408 409 DPRINT_PACKET("asr_setup_query", as->as.dns.obuf, as->as.dns.obuflen); 410 411 return (0); 412 } 413 414 /* 415 * Create a connect UDP socket and send the output packet. 416 * 417 * Return 0 on success, or -1 on error (errno set). 418 */ 419 static int 420 udp_send(struct asr_query *as) 421 { 422 ssize_t n; 423 int save_errno; 424 #ifdef DEBUG 425 char buf[256]; 426 #endif 427 428 DPRINT("asr: [%p] connecting to %s UDP\n", as, 429 print_sockaddr(AS_NS_SA(as), buf, sizeof buf)); 430 431 as->as_fd = sockaddr_connect(AS_NS_SA(as), SOCK_DGRAM); 432 if (as->as_fd == -1) 433 return (-1); /* errno set */ 434 435 n = send(as->as_fd, as->as.dns.obuf, as->as.dns.obuflen, 0); 436 if (n == -1) { 437 save_errno = errno; 438 close(as->as_fd); 439 errno = save_errno; 440 as->as_fd = -1; 441 return (-1); 442 } 443 444 return (0); 445 } 446 447 /* 448 * Try to receive a valid packet from the current UDP socket. 449 * 450 * Return 0 if a full packet could be read, or -1 on error (errno set). 451 */ 452 static int 453 udp_recv(struct asr_query *as) 454 { 455 ssize_t n; 456 int save_errno; 457 458 if (ensure_ibuf(as, PACKETSZ) == -1) { 459 save_errno = errno; 460 close(as->as_fd); 461 errno = save_errno; 462 as->as_fd = -1; 463 return (-1); 464 } 465 466 n = recv(as->as_fd, as->as.dns.ibuf, as->as.dns.ibufsize, 0); 467 save_errno = errno; 468 close(as->as_fd); 469 errno = save_errno; 470 as->as_fd = -1; 471 if (n == -1) 472 return (-1); 473 474 as->as.dns.ibuflen = n; 475 476 DPRINT_PACKET("asr_udp_recv()", as->as.dns.ibuf, as->as.dns.ibuflen); 477 478 if (validate_packet(as) == -1) 479 return (-1); /* errno set */ 480 481 return (0); 482 } 483 484 /* 485 * Write the output packet to the TCP socket. 486 * 487 * Return 0 when all bytes have been sent, 1 there is no buffer space on the 488 * socket or it is not connected yet, or -1 on error (errno set). 489 */ 490 static int 491 tcp_write(struct asr_query *as) 492 { 493 struct msghdr msg; 494 struct iovec iov[2]; 495 uint16_t len; 496 ssize_t n; 497 size_t offset; 498 int i; 499 #ifdef DEBUG 500 char buf[256]; 501 #endif 502 503 /* First try to connect if not already */ 504 if (as->as_fd == -1) { 505 DPRINT("asr: [%p] connecting to %s TCP\n", as, 506 print_sockaddr(AS_NS_SA(as), buf, sizeof buf)); 507 as->as_fd = sockaddr_connect(AS_NS_SA(as), SOCK_STREAM); 508 if (as->as_fd == -1) 509 return (-1); /* errno set */ 510 as->as.dns.datalen = 0; /* bytes sent */ 511 return (1); 512 } 513 514 i = 0; 515 516 /* Prepend de packet length if not sent already. */ 517 if (as->as.dns.datalen < sizeof(len)) { 518 offset = 0; 519 len = htons(as->as.dns.obuflen); 520 iov[i].iov_base = (char *)(&len) + as->as.dns.datalen; 521 iov[i].iov_len = sizeof(len) - as->as.dns.datalen; 522 i++; 523 } else 524 offset = as->as.dns.datalen - sizeof(len); 525 526 iov[i].iov_base = as->as.dns.obuf + offset; 527 iov[i].iov_len = as->as.dns.obuflen - offset; 528 i++; 529 530 memset(&msg, 0, sizeof msg); 531 msg.msg_iov = iov; 532 msg.msg_iovlen = i; 533 534 send_again: 535 n = sendmsg(as->as_fd, &msg, MSG_NOSIGNAL); 536 if (n == -1) { 537 if (errno == EINTR) 538 goto send_again; 539 goto close; /* errno set */ 540 } 541 542 as->as.dns.datalen += n; 543 544 if (as->as.dns.datalen == as->as.dns.obuflen + sizeof(len)) { 545 /* All sent. Prepare for TCP read */ 546 as->as.dns.datalen = 0; 547 return (0); 548 } 549 550 /* More data to write */ 551 return (1); 552 553 close: 554 close(as->as_fd); 555 as->as_fd = -1; 556 return (-1); 557 } 558 559 /* 560 * Try to read a valid packet from the current TCP socket. 561 * 562 * Return 0 if a full packet could be read, 1 if more data is needed and the 563 * socket must be read again, or -1 on error (errno set). 564 */ 565 static int 566 tcp_read(struct asr_query *as) 567 { 568 ssize_t n; 569 size_t offset, len; 570 char *pos; 571 int save_errno, nfds; 572 struct pollfd pfd; 573 574 /* We must read the packet len first */ 575 if (as->as.dns.datalen < sizeof(as->as.dns.pktlen)) { 576 577 pos = (char *)(&as->as.dns.pktlen) + as->as.dns.datalen; 578 len = sizeof(as->as.dns.pktlen) - as->as.dns.datalen; 579 580 n = read(as->as_fd, pos, len); 581 if (n == -1) 582 goto close; /* errno set */ 583 584 as->as.dns.datalen += n; 585 if (as->as.dns.datalen < sizeof(as->as.dns.pktlen)) 586 return (1); /* need more data */ 587 588 as->as.dns.ibuflen = ntohs(as->as.dns.pktlen); 589 if (ensure_ibuf(as, as->as.dns.ibuflen) == -1) 590 goto close; /* errno set */ 591 592 pfd.fd = as->as_fd; 593 pfd.events = POLLIN; 594 poll_again: 595 nfds = poll(&pfd, 1, 0); 596 if (nfds == -1) { 597 if (errno == EINTR) 598 goto poll_again; 599 goto close; /* errno set */ 600 } 601 if (nfds == 0) 602 return (1); /* no more data available */ 603 } 604 605 offset = as->as.dns.datalen - sizeof(as->as.dns.pktlen); 606 pos = as->as.dns.ibuf + offset; 607 len = as->as.dns.ibuflen - offset; 608 609 read_again: 610 n = read(as->as_fd, pos, len); 611 if (n == -1) { 612 if (errno == EINTR) 613 goto read_again; 614 goto close; /* errno set */ 615 } 616 if (n == 0) { 617 errno = ECONNRESET; 618 goto close; 619 } 620 as->as.dns.datalen += n; 621 622 /* See if we got all the advertised bytes. */ 623 if (as->as.dns.datalen != as->as.dns.ibuflen + sizeof(as->as.dns.pktlen)) 624 return (1); 625 626 DPRINT_PACKET("asr_tcp_read()", as->as.dns.ibuf, as->as.dns.ibuflen); 627 628 if (validate_packet(as) == -1) 629 goto close; /* errno set */ 630 631 errno = 0; 632 close: 633 save_errno = errno; 634 close(as->as_fd); 635 errno = save_errno; 636 as->as_fd = -1; 637 return (errno ? -1 : 0); 638 } 639 640 /* 641 * Make sure the input buffer is at least "n" bytes long, and allocate or 642 * extend it if necessary. Return 0 on success, or set errno and return -1. 643 */ 644 static int 645 ensure_ibuf(struct asr_query *as, size_t n) 646 { 647 char *t; 648 649 if (as->as.dns.ibuf == NULL) { 650 as->as.dns.ibuf = malloc(n); 651 if (as->as.dns.ibuf == NULL) 652 return (-1); /* errno set */ 653 as->as.dns.ibufsize = n; 654 return (0); 655 } 656 657 if (as->as.dns.ibufsize >= n) 658 return (0); 659 660 t = realloc(as->as.dns.ibuf, n); 661 if (t == NULL) 662 return (-1); /* errno set */ 663 as->as.dns.ibuf = t; 664 as->as.dns.ibufsize = n; 665 666 return (0); 667 } 668 669 /* 670 * Check if the received packet is valid. 671 * Return 0 on success, or set errno and return -1. 672 */ 673 static int 674 validate_packet(struct asr_query *as) 675 { 676 struct asr_unpack p; 677 struct asr_dns_header h; 678 struct asr_dns_query q; 679 struct asr_dns_rr rr; 680 int r; 681 682 asr_unpack_init(&p, as->as.dns.ibuf, as->as.dns.ibuflen); 683 684 asr_unpack_header(&p, &h); 685 if (p.err) 686 goto inval; 687 688 if (h.id != as->as.dns.reqid) { 689 DPRINT("incorrect reqid\n"); 690 goto inval; 691 } 692 if (h.qdcount != 1) 693 goto inval; 694 /* Should be zero, we could allow this */ 695 if ((h.flags & Z_MASK) != 0) 696 goto inval; 697 /* Actually, it depends on the request but we only use OP_QUERY */ 698 if (OPCODE(h.flags) != OP_QUERY) 699 goto inval; 700 /* Must be a response */ 701 if ((h.flags & QR_MASK) == 0) 702 goto inval; 703 704 as->as.dns.rcode = RCODE(h.flags); 705 as->as.dns.ancount = h.ancount; 706 707 asr_unpack_query(&p, &q); 708 if (p.err) 709 goto inval; 710 711 if (q.q_type != as->as.dns.type || 712 q.q_class != as->as.dns.class || 713 strcasecmp(q.q_dname, as->as.dns.dname)) { 714 DPRINT("incorrect type/class/dname '%s' != '%s'\n", 715 q.q_dname, as->as.dns.dname); 716 goto inval; 717 } 718 719 /* Check for truncation */ 720 if (h.flags & TC_MASK) { 721 errno = EOVERFLOW; 722 return (-1); 723 } 724 725 /* Validate the rest of the packet */ 726 for (r = h.ancount + h.nscount + h.arcount; r; r--) 727 asr_unpack_rr(&p, &rr); 728 729 if (p.err || (p.offset != as->as.dns.ibuflen)) 730 goto inval; 731 732 return (0); 733 734 inval: 735 errno = EINVAL; 736 return (-1); 737 } 738 739 /* 740 * Set the async context nameserver index to the next nameserver, cycling 741 * over the list until the maximum retry counter is reached. Return 0 on 742 * success, or -1 if all nameservers were used. 743 */ 744 static int 745 iter_ns(struct asr_query *as) 746 { 747 for (;;) { 748 if (as->as.dns.nsloop >= as->as_ctx->ac_nsretries) 749 return (-1); 750 751 as->as.dns.nsidx += 1; 752 if (as->as.dns.nsidx <= as->as_ctx->ac_nscount) 753 break; 754 as->as.dns.nsidx = 0; 755 as->as.dns.nsloop++; 756 DPRINT("asr: iter_ns(): cycle %i\n", as->as.dns.nsloop); 757 } 758 759 as->as_timeout = 1000 * (as->as_ctx->ac_nstimeout << as->as.dns.nsloop); 760 if (as->as.dns.nsloop > 0) 761 as->as_timeout /= as->as_ctx->ac_nscount; 762 if (as->as_timeout < 1000) 763 as->as_timeout = 1000; 764 765 return (0); 766 } 767