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