1 /* $OpenBSD: res_send_async.c,v 1.12 2013/04/02 16:38:37 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 if (errno == EINPROGRESS) 342 return (sock); 343 goto fail; 344 } 345 346 return (sock); 347 348 fail: 349 350 if (sock != -1) { 351 errno_save = errno; 352 close(sock); 353 errno = errno_save; 354 } 355 356 return (-1); 357 } 358 359 /* 360 * Prepare the DNS packet for the query type "type", class "class" and domain 361 * name created by the concatenation on "name" and "dom". 362 * Return 0 on success, set errno and return -1 on error. 363 */ 364 static int 365 setup_query(struct async *as, const char *name, const char *dom, 366 int class, int type) 367 { 368 struct pack p; 369 struct header h; 370 char fqdn[MAXDNAME]; 371 char dname[MAXDNAME]; 372 373 if (as->as.dns.flags & ASYNC_EXTOBUF) { 374 errno = EINVAL; 375 DPRINT("attempting to write in user packet"); 376 return (-1); 377 } 378 379 if (asr_make_fqdn(name, dom, fqdn, sizeof(fqdn)) > sizeof(fqdn)) { 380 errno = EINVAL; 381 DPRINT("asr_make_fqdn: name too long\n"); 382 return (-1); 383 } 384 385 if (dname_from_fqdn(fqdn, dname, sizeof(dname)) == -1) { 386 errno = EINVAL; 387 DPRINT("dname_from_fqdn: invalid\n"); 388 return (-1); 389 } 390 391 if (as->as.dns.obuf == NULL) { 392 as->as.dns.obufsize = PACKETSZ; 393 as->as.dns.obuf = malloc(as->as.dns.obufsize); 394 if (as->as.dns.obuf == NULL) 395 return (-1); /* errno set */ 396 } 397 as->as.dns.obuflen = 0; 398 399 memset(&h, 0, sizeof h); 400 h.id = res_randomid(); 401 if (as->as_ctx->ac_options & RES_RECURSE) 402 h.flags |= RD_MASK; 403 h.qdcount = 1; 404 405 pack_init(&p, as->as.dns.obuf, as->as.dns.obufsize); 406 pack_header(&p, &h); 407 pack_query(&p, type, class, dname); 408 if (p.err) { 409 DPRINT("error packing query"); 410 errno = EINVAL; 411 return (-1); 412 } 413 414 /* Remember the parameters. */ 415 as->as.dns.reqid = h.id; 416 as->as.dns.type = type; 417 as->as.dns.class = class; 418 if (as->as.dns.dname) 419 free(as->as.dns.dname); 420 as->as.dns.dname = strdup(dname); 421 if (as->as.dns.dname == NULL) { 422 DPRINT("strdup"); 423 return (-1); /* errno set */ 424 } 425 as->as.dns.obuflen = p.offset; 426 427 DPRINT_PACKET("asr_setup_query", as->as.dns.obuf, as->as.dns.obuflen); 428 429 return (0); 430 } 431 432 /* 433 * Create a connect UDP socket and send the output packet. 434 * 435 * Return 0 on success, or -1 on error (errno set). 436 */ 437 static int 438 udp_send(struct async *as) 439 { 440 ssize_t n; 441 int save_errno; 442 #ifdef DEBUG 443 char buf[256]; 444 #endif 445 446 DPRINT("asr: [%p] connecting to %s UDP\n", as, 447 print_sockaddr(AS_NS_SA(as), buf, sizeof buf)); 448 449 as->as_fd = sockaddr_connect(AS_NS_SA(as), SOCK_DGRAM); 450 if (as->as_fd == -1) 451 return (-1); /* errno set */ 452 453 n = send(as->as_fd, as->as.dns.obuf, as->as.dns.obuflen, 0); 454 if (n == -1) { 455 save_errno = errno; 456 close(as->as_fd); 457 errno = save_errno; 458 as->as_fd = -1; 459 return (-1); 460 } 461 462 return (0); 463 } 464 465 /* 466 * Try to receive a valid packet from the current UDP socket. 467 * 468 * Return 0 if a full packet could be read, or -1 on error (errno set). 469 */ 470 static int 471 udp_recv(struct async *as) 472 { 473 ssize_t n; 474 int save_errno; 475 476 /* Allocate input buf if needed */ 477 if (as->as.dns.ibuf == NULL) { 478 if (ensure_ibuf(as, PACKETSZ) == -1) { 479 save_errno = errno; 480 close(as->as_fd); 481 errno = save_errno; 482 as->as_fd = -1; 483 return (-1); 484 } 485 } 486 487 n = recv(as->as_fd, as->as.dns.ibuf, as->as.dns.ibufsize, 0); 488 save_errno = errno; 489 close(as->as_fd); 490 errno = save_errno; 491 as->as_fd = -1; 492 if (n == -1) 493 return (-1); 494 495 as->as.dns.ibuflen = n; 496 497 DPRINT_PACKET("asr_udp_recv()", as->as.dns.ibuf, as->as.dns.ibuflen); 498 499 if (validate_packet(as) == -1) 500 return (-1); /* errno set */ 501 502 return (0); 503 } 504 505 /* 506 * Write the output packet to the TCP socket. 507 * 508 * Return 0 when all bytes have been sent, 1 there is no buffer space on the 509 * socket or it is not connected yet, or -1 on error (errno set). 510 */ 511 static int 512 tcp_write(struct async *as) 513 { 514 struct iovec iov[2]; 515 uint16_t len; 516 ssize_t n; 517 size_t offset; 518 int i; 519 #ifdef DEBUG 520 char buf[256]; 521 #endif 522 523 /* First try to connect if not already */ 524 if (as->as_fd == -1) { 525 DPRINT("asr: [%p] connecting to %s TCP\n", as, 526 print_sockaddr(AS_NS_SA(as), buf, sizeof buf)); 527 as->as_fd = sockaddr_connect(AS_NS_SA(as), SOCK_STREAM); 528 if (as->as_fd == -1) 529 return (-1); /* errno set */ 530 as->as.dns.datalen = 0; /* bytes sent */ 531 return (1); 532 } 533 534 i = 0; 535 536 /* Prepend de packet length if not sent already. */ 537 if (as->as.dns.datalen < sizeof(len)) { 538 offset = 0; 539 len = htons(as->as.dns.obuflen); 540 iov[i].iov_base = (char *)(&len) + as->as.dns.datalen; 541 iov[i].iov_len = sizeof(len) - as->as.dns.datalen; 542 i++; 543 } else 544 offset = as->as.dns.datalen - sizeof(len); 545 546 iov[i].iov_base = as->as.dns.obuf + offset; 547 iov[i].iov_len = as->as.dns.obuflen - offset; 548 i++; 549 550 n = writev(as->as_fd, iov, i); 551 if (n == -1) 552 goto close; /* errno set */ 553 554 as->as.dns.datalen += n; 555 556 if (as->as.dns.datalen == as->as.dns.obuflen + sizeof(len)) { 557 /* All sent. Prepare for TCP read */ 558 as->as.dns.datalen = 0; 559 return (0); 560 } 561 562 /* More data to write */ 563 return (1); 564 565 close: 566 close(as->as_fd); 567 as->as_fd = -1; 568 return (-1); 569 } 570 571 /* 572 * Try to read a valid packet from the current TCP socket. 573 * 574 * Return 0 if a full packet could be read, 1 if more data is needed and the 575 * socket must be read again, or -1 on error (errno set). 576 */ 577 static int 578 tcp_read(struct async *as) 579 { 580 ssize_t n; 581 size_t offset, len; 582 char *pos; 583 int save_errno, nfds; 584 struct pollfd pfd; 585 586 /* We must read the packet len first */ 587 if (as->as.dns.datalen < sizeof(as->as.dns.pktlen)) { 588 589 pos = (char*)(&as->as.dns.pktlen) + as->as.dns.datalen; 590 len = sizeof(as->as.dns.pktlen) - as->as.dns.datalen; 591 592 n = read(as->as_fd, pos, len); 593 if (n == -1) 594 goto close; /* errno set */ 595 596 as->as.dns.datalen += n; 597 if (as->as.dns.datalen < sizeof(as->as.dns.pktlen)) 598 return (1); /* need more data */ 599 600 as->as.dns.ibuflen = ntohs(as->as.dns.pktlen); 601 if (ensure_ibuf(as, as->as.dns.ibuflen) == -1) 602 goto close; /* errno set */ 603 604 pfd.fd = as->as_fd; 605 pfd.events = POLLIN; 606 nfds = poll(&pfd, 1, 0); 607 if (nfds == -1) 608 goto close; /* errno set */ 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 n = read(as->as_fd, pos, len); 618 if (n == -1) 619 goto close; /* errno set */ 620 if (n == 0) { 621 errno = ECONNRESET; 622 goto close; 623 } 624 as->as.dns.datalen += n; 625 626 /* See if we got all the advertised bytes. */ 627 if (as->as.dns.datalen != as->as.dns.ibuflen + sizeof(as->as.dns.pktlen)) 628 return (1); 629 630 DPRINT_PACKET("asr_tcp_read()", as->as.dns.ibuf, as->as.dns.ibuflen); 631 632 if (validate_packet(as) == -1) 633 goto close; /* errno set */ 634 635 errno = 0; 636 close: 637 save_errno = errno; 638 close(as->as_fd); 639 errno = save_errno; 640 as->as_fd = -1; 641 return (errno ? -1 : 0); 642 } 643 644 /* 645 * Make sure the input buffer is at least "n" bytes long. 646 * If not (or not allocated) allocated enough space, unless the 647 * buffer is external (owned by the caller), in which case it fails. 648 */ 649 static int 650 ensure_ibuf(struct async *as, size_t n) 651 { 652 char *t; 653 654 if (as->as.dns.flags & ASYNC_EXTIBUF) { 655 if (n <= as->as.dns.ibufsize) 656 return (0); 657 errno = EINVAL; 658 return (-1); 659 } 660 661 if (as->as.dns.ibuf == NULL) { 662 as->as.dns.ibuf = malloc(n); 663 if (as->as.dns.ibuf == NULL) 664 return (-1); /* errno set */ 665 as->as.dns.ibufsize = n; 666 return (0); 667 } 668 669 if (as->as.dns.ibufsize >= n) 670 return (0); 671 672 t = realloc(as->as.dns.ibuf, n); 673 if (t == NULL) 674 return (-1); /* errno set */ 675 as->as.dns.ibuf = t; 676 as->as.dns.ibufsize = n; 677 678 return (0); 679 } 680 681 /* 682 * Check if the received packet is valid. 683 * Return 0 on success, or set errno and return -1. 684 */ 685 static int 686 validate_packet(struct async *as) 687 { 688 struct unpack p; 689 struct header h; 690 struct query q; 691 struct rr rr; 692 int r; 693 694 unpack_init(&p, as->as.dns.ibuf, as->as.dns.ibuflen); 695 696 unpack_header(&p, &h); 697 if (p.err) 698 goto inval; 699 700 if (h.id != as->as.dns.reqid) { 701 DPRINT("incorrect reqid\n"); 702 goto inval; 703 } 704 if (h.qdcount != 1) 705 goto inval; 706 /* Should be zero, we could allow this */ 707 if ((h.flags & Z_MASK) != 0) 708 goto inval; 709 /* Actually, it depends on the request but we only use OP_QUERY */ 710 if (OPCODE(h.flags) != OP_QUERY) 711 goto inval; 712 /* Must be a response */ 713 if ((h.flags & QR_MASK) == 0) 714 goto inval; 715 716 as->as.dns.rcode = RCODE(h.flags); 717 as->as.dns.ancount = h.ancount; 718 719 unpack_query(&p, &q); 720 if (p.err) 721 goto inval; 722 723 if (q.q_type != as->as.dns.type || 724 q.q_class != as->as.dns.class || 725 strcasecmp(q.q_dname, as->as.dns.dname)) { 726 DPRINT("incorrect type/class/dname '%s' != '%s'\n", 727 q.q_dname, as->as.dns.dname); 728 goto inval; 729 } 730 731 /* Check for truncation */ 732 if (h.flags & TC_MASK) { 733 errno = EOVERFLOW; 734 return (-1); 735 } 736 737 /* Validate the rest of the packet */ 738 for (r = h.ancount + h.nscount + h.arcount; r; r--) 739 unpack_rr(&p, &rr); 740 741 if (p.err || (p.offset != as->as.dns.ibuflen)) 742 goto inval; 743 744 return (0); 745 746 inval: 747 errno = EINVAL; 748 return (-1); 749 } 750