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