1 /* $OpenBSD: res_send_async.c,v 1.39 2019/09/28 11:21:07 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 <errno.h> 27 #include <fcntl.h> 28 #include <poll.h> 29 #include <resolv.h> /* for res_random */ 30 #include <stdlib.h> 31 #include <string.h> 32 #include <unistd.h> 33 34 #include "asr_private.h" 35 36 #define OP_QUERY (0) 37 38 static int res_send_async_run(struct asr_query *, struct asr_result *); 39 static int sockaddr_connect(const struct sockaddr *, int); 40 static int udp_send(struct asr_query *); 41 static int udp_recv(struct asr_query *); 42 static int tcp_write(struct asr_query *); 43 static int tcp_read(struct asr_query *); 44 static int validate_packet(struct asr_query *); 45 static int setup_query(struct asr_query *, const char *, const char *, int, int); 46 static int ensure_ibuf(struct asr_query *, size_t); 47 static int iter_ns(struct asr_query *); 48 49 #define AS_NS_SA(p) ((p)->as_ctx->ac_ns[(p)->as.dns.nsidx - 1]) 50 51 52 struct asr_query * 53 res_send_async(const unsigned char *buf, int buflen, void *asr) 54 { 55 struct asr_ctx *ac; 56 struct asr_query *as; 57 struct asr_unpack p; 58 struct asr_dns_header h; 59 struct asr_dns_query q; 60 61 DPRINT_PACKET("asr: res_send_async()", buf, buflen); 62 63 ac = _asr_use_resolver(asr); 64 if ((as = _asr_async_new(ac, ASR_SEND)) == NULL) { 65 _asr_ctx_unref(ac); 66 return (NULL); /* errno set */ 67 } 68 as->as_run = res_send_async_run; 69 70 as->as_flags |= ASYNC_EXTOBUF; 71 as->as.dns.obuf = (unsigned char *)buf; 72 as->as.dns.obuflen = buflen; 73 as->as.dns.obufsize = buflen; 74 75 _asr_unpack_init(&p, buf, buflen); 76 _asr_unpack_header(&p, &h); 77 _asr_unpack_query(&p, &q); 78 if (p.err) { 79 errno = EINVAL; 80 goto err; 81 } 82 as->as.dns.reqid = h.id; 83 as->as.dns.type = q.q_type; 84 as->as.dns.class = q.q_class; 85 as->as.dns.dname = strdup(q.q_dname); 86 if (as->as.dns.dname == NULL) 87 goto err; /* errno set */ 88 89 _asr_ctx_unref(ac); 90 return (as); 91 err: 92 if (as) 93 _asr_async_free(as); 94 _asr_ctx_unref(ac); 95 return (NULL); 96 } 97 DEF_WEAK(res_send_async); 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 DEF_WEAK(res_query_async); 119 120 struct asr_query * 121 _res_query_async_ctx(const char *name, int class, int type, struct asr_ctx *a_ctx) 122 { 123 struct asr_query *as; 124 125 DPRINT("asr: res_query_async_ctx(\"%s\", %i, %i)\n", name, class, type); 126 127 if ((as = _asr_async_new(a_ctx, ASR_SEND)) == NULL) 128 return (NULL); /* errno set */ 129 as->as_run = res_send_async_run; 130 131 /* This adds a "." to name if it doesn't already has one. 132 * That's how res_query() behaves (through res_mkquery"). 133 */ 134 if (setup_query(as, name, NULL, class, type) == -1) 135 goto err; /* errno set */ 136 137 return (as); 138 139 err: 140 if (as) 141 _asr_async_free(as); 142 143 return (NULL); 144 } 145 146 static int 147 res_send_async_run(struct asr_query *as, struct asr_result *ar) 148 { 149 next: 150 switch (as->as_state) { 151 152 case ASR_STATE_INIT: 153 154 if (as->as_ctx->ac_nscount == 0) { 155 ar->ar_errno = ECONNREFUSED; 156 async_set_state(as, ASR_STATE_HALT); 157 break; 158 } 159 160 async_set_state(as, ASR_STATE_NEXT_NS); 161 break; 162 163 case ASR_STATE_NEXT_NS: 164 165 if (iter_ns(as) == -1) { 166 ar->ar_errno = ETIMEDOUT; 167 async_set_state(as, ASR_STATE_HALT); 168 break; 169 } 170 171 if (as->as_ctx->ac_options & RES_USEVC || 172 as->as.dns.obuflen > PACKETSZ) 173 async_set_state(as, ASR_STATE_TCP_WRITE); 174 else 175 async_set_state(as, ASR_STATE_UDP_SEND); 176 break; 177 178 case ASR_STATE_UDP_SEND: 179 180 if (udp_send(as) == -1) { 181 async_set_state(as, ASR_STATE_NEXT_NS); 182 break; 183 } 184 async_set_state(as, ASR_STATE_UDP_RECV); 185 ar->ar_cond = ASR_WANT_READ; 186 ar->ar_fd = as->as_fd; 187 ar->ar_timeout = as->as_timeout; 188 return (ASYNC_COND); 189 break; 190 191 case ASR_STATE_UDP_RECV: 192 193 if (udp_recv(as) == -1) { 194 if (errno == ENOMEM) { 195 ar->ar_errno = errno; 196 async_set_state(as, ASR_STATE_HALT); 197 break; 198 } 199 if (errno != EOVERFLOW) { 200 /* Fail or timeout */ 201 async_set_state(as, ASR_STATE_NEXT_NS); 202 break; 203 } 204 if (as->as_ctx->ac_options & RES_IGNTC) 205 async_set_state(as, ASR_STATE_PACKET); 206 else 207 async_set_state(as, ASR_STATE_TCP_WRITE); 208 } else 209 async_set_state(as, ASR_STATE_PACKET); 210 break; 211 212 case ASR_STATE_TCP_WRITE: 213 214 switch (tcp_write(as)) { 215 case -1: /* fail or timeout */ 216 async_set_state(as, ASR_STATE_NEXT_NS); 217 break; 218 case 0: 219 async_set_state(as, ASR_STATE_TCP_READ); 220 ar->ar_cond = ASR_WANT_READ; 221 ar->ar_fd = as->as_fd; 222 ar->ar_timeout = as->as_timeout; 223 return (ASYNC_COND); 224 case 1: 225 ar->ar_cond = ASR_WANT_WRITE; 226 ar->ar_fd = as->as_fd; 227 ar->ar_timeout = as->as_timeout; 228 return (ASYNC_COND); 229 } 230 break; 231 232 case ASR_STATE_TCP_READ: 233 234 switch (tcp_read(as)) { 235 case -1: /* Fail or timeout */ 236 if (errno == ENOMEM) { 237 ar->ar_errno = errno; 238 async_set_state(as, ASR_STATE_HALT); 239 } else 240 async_set_state(as, ASR_STATE_NEXT_NS); 241 break; 242 case 0: 243 async_set_state(as, ASR_STATE_PACKET); 244 break; 245 case 1: 246 ar->ar_cond = ASR_WANT_READ; 247 ar->ar_fd = as->as_fd; 248 ar->ar_timeout = as->as_timeout; 249 return (ASYNC_COND); 250 } 251 break; 252 253 case ASR_STATE_PACKET: 254 255 memmove(&ar->ar_ns, AS_NS_SA(as), AS_NS_SA(as)->sa_len); 256 ar->ar_datalen = as->as.dns.ibuflen; 257 ar->ar_data = as->as.dns.ibuf; 258 as->as.dns.ibuf = NULL; 259 ar->ar_errno = 0; 260 ar->ar_rcode = as->as.dns.rcode; 261 async_set_state(as, ASR_STATE_HALT); 262 break; 263 264 case ASR_STATE_HALT: 265 266 if (ar->ar_errno) { 267 ar->ar_h_errno = TRY_AGAIN; 268 ar->ar_count = 0; 269 ar->ar_datalen = -1; 270 ar->ar_data = NULL; 271 } else if (as->as.dns.ancount) { 272 ar->ar_h_errno = NETDB_SUCCESS; 273 ar->ar_count = as->as.dns.ancount; 274 } else { 275 ar->ar_count = 0; 276 switch (as->as.dns.rcode) { 277 case NXDOMAIN: 278 ar->ar_h_errno = HOST_NOT_FOUND; 279 break; 280 case SERVFAIL: 281 ar->ar_h_errno = TRY_AGAIN; 282 break; 283 case NOERROR: 284 ar->ar_h_errno = NO_DATA; 285 break; 286 default: 287 ar->ar_h_errno = NO_RECOVERY; 288 } 289 } 290 return (ASYNC_DONE); 291 292 default: 293 294 ar->ar_errno = EOPNOTSUPP; 295 ar->ar_h_errno = NETDB_INTERNAL; 296 async_set_state(as, ASR_STATE_HALT); 297 break; 298 } 299 goto next; 300 } 301 302 static int 303 sockaddr_connect(const struct sockaddr *sa, int socktype) 304 { 305 int errno_save, sock; 306 307 if ((sock = socket(sa->sa_family, 308 socktype | SOCK_NONBLOCK | SOCK_DNS, 0)) == -1) 309 goto fail; 310 311 if (connect(sock, sa, sa->sa_len) == -1) { 312 /* 313 * In the TCP case, the caller will be asked to poll for 314 * POLLOUT so that we start writing the packet in tcp_write() 315 * when the connection is established, or fail there on error. 316 */ 317 if (errno == EINPROGRESS) 318 return (sock); 319 goto fail; 320 } 321 322 return (sock); 323 324 fail: 325 326 if (sock != -1) { 327 errno_save = errno; 328 close(sock); 329 errno = errno_save; 330 } 331 332 return (-1); 333 } 334 335 /* 336 * Prepare the DNS packet for the query type "type", class "class" and domain 337 * name created by the concatenation on "name" and "dom". 338 * Return 0 on success, set errno and return -1 on error. 339 */ 340 static int 341 setup_query(struct asr_query *as, const char *name, const char *dom, 342 int class, int type) 343 { 344 struct asr_pack p; 345 struct asr_dns_header h; 346 char fqdn[MAXDNAME]; 347 char dname[MAXDNAME]; 348 349 if (as->as_flags & ASYNC_EXTOBUF) { 350 errno = EINVAL; 351 DPRINT("attempting to write in user packet"); 352 return (-1); 353 } 354 355 if (_asr_make_fqdn(name, dom, fqdn, sizeof(fqdn)) > sizeof(fqdn)) { 356 errno = EINVAL; 357 DPRINT("asr_make_fqdn: name too long\n"); 358 return (-1); 359 } 360 361 if (_asr_dname_from_fqdn(fqdn, dname, sizeof(dname)) == -1) { 362 errno = EINVAL; 363 DPRINT("asr_dname_from_fqdn: invalid\n"); 364 return (-1); 365 } 366 367 if (as->as.dns.obuf == NULL) { 368 as->as.dns.obufsize = PACKETSZ; 369 as->as.dns.obuf = malloc(as->as.dns.obufsize); 370 if (as->as.dns.obuf == NULL) 371 return (-1); /* errno set */ 372 } 373 as->as.dns.obuflen = 0; 374 375 memset(&h, 0, sizeof h); 376 h.id = res_randomid(); 377 if (as->as_ctx->ac_options & RES_RECURSE) 378 h.flags |= RD_MASK; 379 if (as->as_ctx->ac_options & RES_USE_CD) 380 h.flags |= CD_MASK; 381 h.qdcount = 1; 382 if (as->as_ctx->ac_options & (RES_USE_EDNS0 | RES_USE_DNSSEC)) 383 h.arcount = 1; 384 385 _asr_pack_init(&p, as->as.dns.obuf, as->as.dns.obufsize); 386 _asr_pack_header(&p, &h); 387 _asr_pack_query(&p, type, class, dname); 388 if (as->as_ctx->ac_options & (RES_USE_EDNS0 | RES_USE_DNSSEC)) 389 _asr_pack_edns0(&p, MAXPACKETSZ, 390 as->as_ctx->ac_options & RES_USE_DNSSEC); 391 if (p.err) { 392 DPRINT("error packing query"); 393 errno = EINVAL; 394 return (-1); 395 } 396 397 /* Remember the parameters. */ 398 as->as.dns.reqid = h.id; 399 as->as.dns.type = type; 400 as->as.dns.class = class; 401 if (as->as.dns.dname) 402 free(as->as.dns.dname); 403 as->as.dns.dname = strdup(dname); 404 if (as->as.dns.dname == NULL) { 405 DPRINT("strdup"); 406 return (-1); /* errno set */ 407 } 408 as->as.dns.obuflen = p.offset; 409 410 DPRINT_PACKET("asr_setup_query", as->as.dns.obuf, as->as.dns.obuflen); 411 412 return (0); 413 } 414 415 /* 416 * Create a connect UDP socket and send the output packet. 417 * 418 * Return 0 on success, or -1 on error (errno set). 419 */ 420 static int 421 udp_send(struct asr_query *as) 422 { 423 ssize_t n; 424 int save_errno; 425 #ifdef DEBUG 426 char buf[256]; 427 #endif 428 429 DPRINT("asr: [%p] connecting to %s UDP\n", as, 430 _asr_print_sockaddr(AS_NS_SA(as), buf, sizeof buf)); 431 432 as->as_fd = sockaddr_connect(AS_NS_SA(as), SOCK_DGRAM); 433 if (as->as_fd == -1) 434 return (-1); /* errno set */ 435 436 n = send(as->as_fd, as->as.dns.obuf, as->as.dns.obuflen, 0); 437 if (n == -1) { 438 save_errno = errno; 439 close(as->as_fd); 440 errno = save_errno; 441 as->as_fd = -1; 442 return (-1); 443 } 444 445 return (0); 446 } 447 448 /* 449 * Try to receive a valid packet from the current UDP socket. 450 * 451 * Return 0 if a full packet could be read, or -1 on error (errno set). 452 */ 453 static int 454 udp_recv(struct asr_query *as) 455 { 456 ssize_t n; 457 int save_errno; 458 459 if (ensure_ibuf(as, MAXPACKETSZ) == -1) { 460 save_errno = errno; 461 close(as->as_fd); 462 errno = save_errno; 463 as->as_fd = -1; 464 return (-1); 465 } 466 467 n = recv(as->as_fd, as->as.dns.ibuf, as->as.dns.ibufsize, 0); 468 save_errno = errno; 469 close(as->as_fd); 470 errno = save_errno; 471 as->as_fd = -1; 472 if (n == -1) 473 return (-1); 474 475 as->as.dns.ibuflen = n; 476 477 DPRINT_PACKET("asr_udp_recv()", as->as.dns.ibuf, as->as.dns.ibuflen); 478 479 if (validate_packet(as) == -1) 480 return (-1); /* errno set */ 481 482 return (0); 483 } 484 485 /* 486 * Write the output packet to the TCP socket. 487 * 488 * Return 0 when all bytes have been sent, 1 there is no buffer space on the 489 * socket or it is not connected yet, or -1 on error (errno set). 490 */ 491 static int 492 tcp_write(struct asr_query *as) 493 { 494 struct msghdr msg; 495 struct iovec iov[2]; 496 uint16_t len; 497 ssize_t n; 498 size_t offset; 499 int i; 500 #ifdef DEBUG 501 char buf[256]; 502 #endif 503 504 /* First try to connect if not already */ 505 if (as->as_fd == -1) { 506 DPRINT("asr: [%p] connecting to %s TCP\n", as, 507 _asr_print_sockaddr(AS_NS_SA(as), buf, sizeof buf)); 508 as->as_fd = sockaddr_connect(AS_NS_SA(as), SOCK_STREAM); 509 if (as->as_fd == -1) 510 return (-1); /* errno set */ 511 as->as.dns.datalen = 0; /* bytes sent */ 512 return (1); 513 } 514 515 i = 0; 516 517 /* Prepend de packet length if not sent already. */ 518 if (as->as.dns.datalen < sizeof(len)) { 519 offset = 0; 520 len = htons(as->as.dns.obuflen); 521 iov[i].iov_base = (char *)(&len) + as->as.dns.datalen; 522 iov[i].iov_len = sizeof(len) - as->as.dns.datalen; 523 i++; 524 } else 525 offset = as->as.dns.datalen - sizeof(len); 526 527 iov[i].iov_base = as->as.dns.obuf + offset; 528 iov[i].iov_len = as->as.dns.obuflen - offset; 529 i++; 530 531 memset(&msg, 0, sizeof msg); 532 msg.msg_iov = iov; 533 msg.msg_iovlen = i; 534 535 send_again: 536 n = sendmsg(as->as_fd, &msg, MSG_NOSIGNAL); 537 if (n == -1) { 538 if (errno == EINTR) 539 goto send_again; 540 goto close; /* errno set */ 541 } 542 543 as->as.dns.datalen += n; 544 545 if (as->as.dns.datalen == as->as.dns.obuflen + sizeof(len)) { 546 /* All sent. Prepare for TCP read */ 547 as->as.dns.datalen = 0; 548 return (0); 549 } 550 551 /* More data to write */ 552 return (1); 553 554 close: 555 close(as->as_fd); 556 as->as_fd = -1; 557 return (-1); 558 } 559 560 /* 561 * Try to read a valid packet from the current TCP socket. 562 * 563 * Return 0 if a full packet could be read, 1 if more data is needed and the 564 * socket must be read again, or -1 on error (errno set). 565 */ 566 static int 567 tcp_read(struct asr_query *as) 568 { 569 ssize_t n; 570 size_t offset, len; 571 char *pos; 572 int save_errno, nfds; 573 struct pollfd pfd; 574 575 /* We must read the packet len first */ 576 if (as->as.dns.datalen < sizeof(as->as.dns.pktlen)) { 577 578 pos = (char *)(&as->as.dns.pktlen) + as->as.dns.datalen; 579 len = sizeof(as->as.dns.pktlen) - as->as.dns.datalen; 580 581 read_again0: 582 n = read(as->as_fd, pos, len); 583 if (n == -1) { 584 if (errno == EINTR) 585 goto read_again0; 586 goto close; /* errno set */ 587 } 588 if (n == 0) { 589 errno = ECONNRESET; 590 goto close; 591 } 592 as->as.dns.datalen += n; 593 if (as->as.dns.datalen < sizeof(as->as.dns.pktlen)) 594 return (1); /* need more data */ 595 596 as->as.dns.ibuflen = ntohs(as->as.dns.pktlen); 597 if (ensure_ibuf(as, as->as.dns.ibuflen) == -1) 598 goto close; /* errno set */ 599 600 pfd.fd = as->as_fd; 601 pfd.events = POLLIN; 602 poll_again: 603 nfds = poll(&pfd, 1, 0); 604 if (nfds == -1) { 605 if (errno == EINTR) 606 goto poll_again; 607 goto close; /* errno set */ 608 } 609 if (nfds == 0) 610 return (1); /* no more data available */ 611 } 612 613 offset = as->as.dns.datalen - sizeof(as->as.dns.pktlen); 614 pos = as->as.dns.ibuf + offset; 615 len = as->as.dns.ibuflen - offset; 616 617 read_again: 618 n = read(as->as_fd, pos, len); 619 if (n == -1) { 620 if (errno == EINTR) 621 goto read_again; 622 goto close; /* errno set */ 623 } 624 if (n == 0) { 625 errno = ECONNRESET; 626 goto close; 627 } 628 as->as.dns.datalen += n; 629 630 /* See if we got all the advertised bytes. */ 631 if (as->as.dns.datalen != as->as.dns.ibuflen + sizeof(as->as.dns.pktlen)) 632 return (1); 633 634 DPRINT_PACKET("asr_tcp_read()", as->as.dns.ibuf, as->as.dns.ibuflen); 635 636 if (validate_packet(as) == -1) 637 goto close; /* errno set */ 638 639 errno = 0; 640 close: 641 save_errno = errno; 642 close(as->as_fd); 643 errno = save_errno; 644 as->as_fd = -1; 645 return (errno ? -1 : 0); 646 } 647 648 /* 649 * Make sure the input buffer is at least "n" bytes long, and allocate or 650 * extend it if necessary. Return 0 on success, or set errno and return -1. 651 */ 652 static int 653 ensure_ibuf(struct asr_query *as, size_t n) 654 { 655 char *t; 656 657 if (as->as.dns.ibufsize >= n) 658 return (0); 659 660 t = recallocarray(as->as.dns.ibuf, as->as.dns.ibufsize, n, 1); 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 && !(as->as_ctx->ac_options & RES_IGNTC)) { 721 DPRINT("truncated\n"); 722 errno = EOVERFLOW; 723 return (-1); 724 } 725 726 /* Validate the rest of the packet */ 727 for (r = h.ancount + h.nscount + h.arcount; r; r--) 728 _asr_unpack_rr(&p, &rr); 729 730 /* Report any error found when unpacking the RRs. */ 731 if (p.err) { 732 DPRINT("unpack: %s\n", strerror(p.err)); 733 errno = p.err; 734 return (-1); 735 } 736 737 if (p.offset != as->as.dns.ibuflen) { 738 DPRINT("trailing garbage\n"); 739 errno = EMSGSIZE; 740 return (-1); 741 } 742 743 return (0); 744 745 inval: 746 errno = EINVAL; 747 return (-1); 748 } 749 750 /* 751 * Set the async context nameserver index to the next nameserver, cycling 752 * over the list until the maximum retry counter is reached. Return 0 on 753 * success, or -1 if all nameservers were used. 754 */ 755 static int 756 iter_ns(struct asr_query *as) 757 { 758 for (;;) { 759 if (as->as.dns.nsloop >= as->as_ctx->ac_nsretries) 760 return (-1); 761 762 as->as.dns.nsidx += 1; 763 if (as->as.dns.nsidx <= as->as_ctx->ac_nscount) 764 break; 765 as->as.dns.nsidx = 0; 766 as->as.dns.nsloop++; 767 DPRINT("asr: iter_ns(): cycle %i\n", as->as.dns.nsloop); 768 } 769 770 as->as_timeout = 1000 * (as->as_ctx->ac_nstimeout << as->as.dns.nsloop); 771 if (as->as.dns.nsloop > 0) 772 as->as_timeout /= as->as_ctx->ac_nscount; 773 if (as->as_timeout < 1000) 774 as->as_timeout = 1000; 775 776 return (0); 777 } 778