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