1 /* $OpenBSD: res_send_async.c,v 1.3 2012/09/09 09:42:06 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 21 #include <netinet/in.h> 22 #include <arpa/nameser.h> 23 24 #include <err.h> 25 #include <errno.h> 26 #include <fcntl.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 packed 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 packed_init(&p, (char*)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 (trough 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 packed 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 packed_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 asr_print_addr(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 as->as_timeout = as->as_ctx->ac_nstimeout; 454 455 n = send(as->as_fd, as->as.dns.obuf, as->as.dns.obuflen, 0); 456 if (n == -1) { 457 save_errno = errno; 458 close(as->as_fd); 459 errno = save_errno; 460 as->as_fd = -1; 461 return (-1); 462 } 463 464 return (0); 465 } 466 467 /* 468 * Try to receive a valid packet from the current UDP socket. 469 * 470 * Return 0 if a full packet could be read, or -1 on error (errno set). 471 */ 472 static int 473 udp_recv(struct async *as) 474 { 475 ssize_t n; 476 int save_errno; 477 478 /* Allocate input buf if needed */ 479 if (as->as.dns.ibuf == NULL) { 480 if (ensure_ibuf(as, PACKETSZ) == -1) { 481 save_errno = errno; 482 close(as->as_fd); 483 errno = save_errno; 484 as->as_fd = -1; 485 return (-1); 486 } 487 } 488 489 n = recv(as->as_fd, as->as.dns.ibuf, as->as.dns.ibufsize, 0); 490 save_errno = errno; 491 close(as->as_fd); 492 errno = save_errno; 493 as->as_fd = -1; 494 if (n == -1) 495 return (-1); 496 497 as->as.dns.ibuflen = n; 498 499 DPRINT_PACKET("asr_udp_recv()", as->as.dns.ibuf, as->as.dns.ibuflen); 500 501 if (validate_packet(as) == -1) 502 return (-1); /* errno set */ 503 504 return (0); 505 } 506 507 /* 508 * Write the output packet to the TCP socket. 509 * 510 * Return 0 when all bytes have been sent, 1 there is no buffer space on the 511 * socket or it is not connected yet, or -1 on error (errno set). 512 */ 513 static int 514 tcp_write(struct async *as) 515 { 516 struct iovec iov[2]; 517 uint16_t len; 518 ssize_t n; 519 int i, se; 520 socklen_t sl; 521 #ifdef DEBUG 522 char buf[256]; 523 #endif 524 525 /* First try to connect if not already */ 526 if (as->as_fd == -1) { 527 DPRINT("asr: [%p] connecting to %s TCP\n", as, 528 asr_print_addr(AS_NS_SA(as), buf, sizeof buf)); 529 as->as_fd = sockaddr_connect(AS_NS_SA(as), SOCK_STREAM); 530 if (as->as_fd == -1) 531 return (-1); /* errno set */ 532 as->as_timeout = as->as_ctx->ac_nstimeout; 533 return (1); 534 } 535 536 i = 0; 537 538 /* Check if the connection succeeded. */ 539 if (as->as.dns.datalen == 0) { 540 sl = sizeof(se); 541 if (getsockopt(as->as_fd, SOL_SOCKET, SO_ERROR, &se, &sl) == -1) 542 goto close; /* errno set */ 543 if (se) { 544 errno = se; 545 goto close; 546 } 547 548 as->as.dns.bufpos = 0; 549 550 /* Send the packet length first */ 551 len = htons(as->as.dns.obuflen); 552 iov[i].iov_base = &len; 553 iov[i].iov_len = sizeof(len); 554 i++; 555 } 556 557 iov[i].iov_base = as->as.dns.obuf + as->as.dns.bufpos; 558 iov[i].iov_len = as->as.dns.obuflen - as->as.dns.bufpos; 559 i++; 560 561 n = writev(as->as_fd, iov, i); 562 if (n == -1) 563 goto close; /* errno set */ 564 565 /* 566 * We want at least the packet length to be written out the first time. 567 * Technically we could recover but that makes little sense to support 568 * that. 569 */ 570 if (as->as.dns.datalen == 0 && n < 2) { 571 errno = EIO; 572 goto close; 573 } 574 575 if (as->as.dns.datalen == 0) { 576 as->as.dns.datalen = len; 577 n -= 2; 578 } 579 580 as->as.dns.bufpos += n; 581 if (as->as.dns.bufpos == as->as.dns.obuflen) { 582 /* All sent. Prepare for TCP read */ 583 as->as.dns.datalen = 0; 584 return (0); 585 } 586 587 /* More data to write */ 588 as->as_timeout = as->as_ctx->ac_nstimeout; 589 return (1); 590 591 close: 592 close(as->as_fd); 593 as->as_fd = -1; 594 return (-1); 595 } 596 597 /* 598 * Try to read a valid packet from the current TCP socket. 599 * 600 * Return 0 if a full packet could be read, 1 if more data is needed and the 601 * socket must be read again, or -1 on error (errno set). 602 */ 603 static int 604 tcp_read(struct async *as) 605 { 606 uint16_t len; 607 ssize_t n; 608 int save_errno; 609 610 /* We must read the packet len first */ 611 if (as->as.dns.datalen == 0) { 612 n = read(as->as_fd, &len, sizeof(len)); 613 if (n == -1) 614 goto close; /* errno set */ 615 /* 616 * If the server has sent us only the first byte, we fail. 617 * Technically, we could recover but it might not be worth 618 * supporting that. 619 */ 620 if (n < 2) { 621 errno = EIO; 622 goto close; 623 } 624 625 as->as.dns.datalen = ntohs(len); 626 as->as.dns.bufpos = 0; 627 as->as.dns.ibuflen = 0; 628 629 if (ensure_ibuf(as, as->as.dns.datalen) == -1) 630 goto close; /* errno set */ 631 } 632 633 n = read(as->as_fd, as->as.dns.ibuf + as->as.dns.ibuflen, 634 as->as.dns.datalen - as->as.dns.ibuflen); 635 if (n == -1) 636 goto close; /* errno set */ 637 if (n == 0) { 638 errno = ECONNRESET; 639 goto close; 640 } 641 as->as.dns.ibuflen += n; 642 643 /* See if we got all the advertised bytes. */ 644 if (as->as.dns.ibuflen != as->as.dns.datalen) 645 return (1); 646 647 DPRINT_PACKET("asr_tcp_read()", as->as.dns.ibuf, as->as.dns.ibuflen); 648 649 if (validate_packet(as) == -1) 650 goto close; /* errno set */ 651 652 errno = 0; 653 close: 654 save_errno = errno; 655 close(as->as_fd); 656 errno = save_errno; 657 as->as_fd = -1; 658 return (errno ? -1 : 0); 659 } 660 661 /* 662 * Make sure the input buffer is at least "n" bytes long. 663 * If not (or not allocated) allocated enough space, unless the 664 * buffer is external (owned by the caller), in which case it fails. 665 */ 666 static int 667 ensure_ibuf(struct async *as, size_t n) 668 { 669 char *t; 670 671 if (as->as.dns.flags & ASYNC_EXTIBUF) { 672 if (n <= as->as.dns.ibufsize) 673 return (0); 674 errno = EINVAL; 675 return (-1); 676 } 677 678 if (as->as.dns.ibuf == NULL) { 679 as->as.dns.ibuf = malloc(n); 680 if (as->as.dns.ibuf == NULL) 681 return (-1); /* errno set */ 682 as->as.dns.ibufsize = n; 683 return (0); 684 } 685 686 if (as->as.dns.ibufsize >= n) 687 return (0); 688 689 t = realloc(as->as.dns.ibuf, n); 690 if (t == NULL) 691 return (-1); /* errno set */ 692 as->as.dns.ibuf = t; 693 as->as.dns.ibufsize = n; 694 695 return (0); 696 } 697 698 /* 699 * Check if the received packet is valid. 700 * Return 0 on success, or set errno and return -1. 701 */ 702 static int 703 validate_packet(struct async *as) 704 { 705 struct packed p; 706 struct header h; 707 struct query q; 708 struct rr rr; 709 int r; 710 711 packed_init(&p, as->as.dns.ibuf, as->as.dns.ibuflen); 712 713 unpack_header(&p, &h); 714 if (p.err) 715 goto inval; 716 717 if (h.id != as->as.dns.reqid) { 718 DPRINT("incorrect reqid\n"); 719 goto inval; 720 } 721 if (h.qdcount != 1) 722 goto inval; 723 /* Should be zero, we could allow this */ 724 if ((h.flags & Z_MASK) != 0) 725 goto inval; 726 /* Actually, it depends on the request but we only use OP_QUERY */ 727 if (OPCODE(h.flags) != OP_QUERY) 728 goto inval; 729 /* Must be a response */ 730 if ((h.flags & QR_MASK) == 0) 731 goto inval; 732 733 as->as.dns.rcode = RCODE(h.flags); 734 as->as.dns.ancount = h.ancount; 735 736 unpack_query(&p, &q); 737 if (p.err) 738 goto inval; 739 740 if (q.q_type != as->as.dns.type || 741 q.q_class != as->as.dns.class || 742 strcasecmp(q.q_dname, as->as.dns.dname)) { 743 DPRINT("incorrect type/class/dname '%s' != '%s'\n", 744 q.q_dname, as->as.dns.dname); 745 goto inval; 746 } 747 748 /* Check for truncation */ 749 if (h.flags & TC_MASK) { 750 errno = EOVERFLOW; 751 return (-1); 752 } 753 754 /* Validate the rest of the packet */ 755 for(r = h.ancount + h.nscount + h.arcount; r; r--) 756 unpack_rr(&p, &rr); 757 758 if (p.err || (p.offset != as->as.dns.ibuflen)) 759 goto inval; 760 761 return (0); 762 763 inval: 764 errno = EINVAL; 765 return (-1); 766 } 767