1 /* $OpenBSD: gethostnamadr_async.c,v 1.29 2014/05/13 11:57:35 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/socket.h> 20 #include <netinet/in.h> 21 #include <arpa/inet.h> 22 #include <arpa/nameser.h> 23 #ifdef YP 24 #include <rpc/rpc.h> 25 #include <rpcsvc/yp.h> 26 #include <rpcsvc/ypclnt.h> 27 #include "ypinternal.h" 28 #endif 29 #include <netdb.h> 30 31 #include <asr.h> 32 #include <ctype.h> 33 #include <err.h> 34 #include <errno.h> 35 #include <resolv.h> /* for res_hnok */ 36 #include <stdlib.h> 37 #include <string.h> 38 #include <unistd.h> 39 40 #include "asr_private.h" 41 42 #define MAXALIASES 16 43 #define MAXADDRS 16 44 45 struct hostent_ext { 46 struct hostent h; 47 char *aliases[MAXALIASES + 1]; 48 char *addrs[MAXADDRS + 1]; 49 char *end; 50 char *pos; 51 }; 52 53 static int gethostnamadr_async_run(struct asr_query *, struct asr_result *); 54 static struct hostent_ext *hostent_alloc(int); 55 static int hostent_set_cname(struct hostent_ext *, const char *, int); 56 static int hostent_add_alias(struct hostent_ext *, const char *, int); 57 static int hostent_add_addr(struct hostent_ext *, const void *, size_t); 58 static struct hostent_ext *hostent_from_addr(int, const char *, const char *); 59 static struct hostent_ext *hostent_file_match(FILE *, int, int, const char *, 60 int); 61 static struct hostent_ext *hostent_from_packet(int, int, char *, size_t); 62 #ifdef YP 63 static struct hostent_ext *_yp_gethostnamadr(int, const void *); 64 static struct hostent_ext *hostent_from_yp(int, char *); 65 #endif 66 67 struct asr_query * 68 gethostbyname_async(const char *name, void *asr) 69 { 70 return gethostbyname2_async(name, AF_INET, asr); 71 } 72 73 struct asr_query * 74 gethostbyname2_async(const char *name, int af, void *asr) 75 { 76 struct asr_ctx *ac; 77 struct asr_query *as; 78 79 /* the original segfaults */ 80 if (name == NULL) { 81 errno = EINVAL; 82 return (NULL); 83 } 84 85 ac = asr_use_resolver(asr); 86 if ((as = asr_async_new(ac, ASR_GETHOSTBYNAME)) == NULL) 87 goto abort; /* errno set */ 88 as->as_run = gethostnamadr_async_run; 89 90 as->as.hostnamadr.family = af; 91 if (af == AF_INET) 92 as->as.hostnamadr.addrlen = INADDRSZ; 93 else if (af == AF_INET6) 94 as->as.hostnamadr.addrlen = IN6ADDRSZ; 95 as->as.hostnamadr.name = strdup(name); 96 if (as->as.hostnamadr.name == NULL) 97 goto abort; /* errno set */ 98 99 asr_ctx_unref(ac); 100 return (as); 101 102 abort: 103 if (as) 104 asr_async_free(as); 105 asr_ctx_unref(ac); 106 return (NULL); 107 } 108 109 struct asr_query * 110 gethostbyaddr_async(const void *addr, socklen_t len, int af, void *asr) 111 { 112 struct asr_ctx *ac; 113 struct asr_query *as; 114 115 ac = asr_use_resolver(asr); 116 as = gethostbyaddr_async_ctx(addr, len, af, ac); 117 asr_ctx_unref(ac); 118 119 return (as); 120 } 121 122 struct asr_query * 123 gethostbyaddr_async_ctx(const void *addr, socklen_t len, int af, 124 struct asr_ctx *ac) 125 { 126 struct asr_query *as; 127 128 if ((as = asr_async_new(ac, ASR_GETHOSTBYADDR)) == NULL) 129 goto abort; /* errno set */ 130 as->as_run = gethostnamadr_async_run; 131 132 as->as.hostnamadr.family = af; 133 as->as.hostnamadr.addrlen = len; 134 if (len > 0) 135 memmove(as->as.hostnamadr.addr, addr, (len > 16) ? 16 : len); 136 137 return (as); 138 139 abort: 140 if (as) 141 asr_async_free(as); 142 return (NULL); 143 } 144 145 static int 146 gethostnamadr_async_run(struct asr_query *as, struct asr_result *ar) 147 { 148 struct hostent_ext *h; 149 int r, type, saved_errno; 150 FILE *f; 151 char name[MAXDNAME], *data, addr[16], *c; 152 153 next: 154 switch (as->as_state) { 155 156 case ASR_STATE_INIT: 157 158 if (as->as.hostnamadr.family != AF_INET && 159 as->as.hostnamadr.family != AF_INET6) { 160 ar->ar_h_errno = NETDB_INTERNAL; 161 ar->ar_errno = EAFNOSUPPORT; 162 async_set_state(as, ASR_STATE_HALT); 163 break; 164 } 165 166 if ((as->as.hostnamadr.family == AF_INET && 167 as->as.hostnamadr.addrlen != INADDRSZ) || 168 (as->as.hostnamadr.family == AF_INET6 && 169 as->as.hostnamadr.addrlen != IN6ADDRSZ)) { 170 ar->ar_h_errno = NETDB_INTERNAL; 171 ar->ar_errno = EINVAL; 172 async_set_state(as, ASR_STATE_HALT); 173 break; 174 } 175 176 /* Name might be an IP address string */ 177 if (as->as_type == ASR_GETHOSTBYNAME) { 178 for (c = as->as.hostnamadr.name; *c; c++) 179 if (!isdigit((unsigned char)*c) && 180 *c != '.' && *c != ':') 181 break; 182 if (*c == 0 && 183 inet_pton(as->as.hostnamadr.family, 184 as->as.hostnamadr.name, addr) == 1) { 185 h = hostent_from_addr(as->as.hostnamadr.family, 186 as->as.hostnamadr.name, addr); 187 if (h == NULL) { 188 ar->ar_errno = errno; 189 ar->ar_h_errno = NETDB_INTERNAL; 190 } 191 else { 192 ar->ar_hostent = &h->h; 193 ar->ar_h_errno = NETDB_SUCCESS; 194 } 195 async_set_state(as, ASR_STATE_HALT); 196 break; 197 } 198 } 199 async_set_state(as, ASR_STATE_NEXT_DB); 200 break; 201 202 case ASR_STATE_NEXT_DB: 203 204 if (asr_iter_db(as) == -1) { 205 async_set_state(as, ASR_STATE_NOT_FOUND); 206 break; 207 } 208 209 switch (AS_DB(as)) { 210 211 case ASR_DB_DNS: 212 213 /* Create a subquery to do the DNS lookup */ 214 215 if (as->as_type == ASR_GETHOSTBYNAME) { 216 type = (as->as.hostnamadr.family == AF_INET) ? 217 T_A : T_AAAA; 218 as->as.hostnamadr.subq = res_search_async_ctx( 219 as->as.hostnamadr.name, 220 C_IN, type, as->as_ctx); 221 } else { 222 asr_addr_as_fqdn(as->as.hostnamadr.addr, 223 as->as.hostnamadr.family, 224 name, sizeof(name)); 225 as->as.hostnamadr.subq = res_query_async_ctx( 226 name, C_IN, T_PTR, as->as_ctx); 227 } 228 229 if (as->as.hostnamadr.subq == NULL) { 230 ar->ar_errno = errno; 231 ar->ar_h_errno = NETDB_INTERNAL; 232 async_set_state(as, ASR_STATE_HALT); 233 break; 234 } 235 236 async_set_state(as, ASR_STATE_SUBQUERY); 237 break; 238 239 case ASR_DB_FILE: 240 241 /* Try to find a match in the host file */ 242 243 if ((f = fopen(as->as_ctx->ac_hostfile, "r")) == NULL) 244 break; 245 246 if (as->as_type == ASR_GETHOSTBYNAME) { 247 data = asr_hostalias(as->as_ctx, 248 as->as.hostnamadr.name, name, sizeof(name)); 249 if (data == NULL) 250 data = as->as.hostnamadr.name; 251 } 252 else 253 data = as->as.hostnamadr.addr; 254 255 h = hostent_file_match(f, as->as_type, 256 as->as.hostnamadr.family, data, 257 as->as.hostnamadr.addrlen); 258 saved_errno = errno; 259 fclose(f); 260 errno = saved_errno; 261 262 if (h == NULL) { 263 if (errno) { 264 ar->ar_errno = errno; 265 ar->ar_h_errno = NETDB_INTERNAL; 266 async_set_state(as, ASR_STATE_HALT); 267 } 268 /* otherwise not found */ 269 break; 270 } 271 ar->ar_hostent = &h->h; 272 ar->ar_h_errno = NETDB_SUCCESS; 273 async_set_state(as, ASR_STATE_HALT); 274 break; 275 #ifdef YP 276 case ASR_DB_YP: 277 /* IPv4 only */ 278 if (as->as.hostnamadr.family != AF_INET) 279 break; 280 if (as->as_type == ASR_GETHOSTBYNAME) { 281 data = asr_hostalias(as->as_ctx, 282 as->as.hostnamadr.name, name, sizeof(name)); 283 if (data == NULL) 284 data = as->as.hostnamadr.name; 285 } 286 else 287 data = as->as.hostnamadr.addr; 288 h = _yp_gethostnamadr(as->as_type, data); 289 if (h == NULL) { 290 if (errno) { 291 ar->ar_errno = errno; 292 ar->ar_h_errno = NETDB_INTERNAL; 293 async_set_state(as, ASR_STATE_HALT); 294 } 295 /* otherwise not found */ 296 break; 297 } 298 ar->ar_hostent = &h->h; 299 ar->ar_h_errno = NETDB_SUCCESS; 300 async_set_state(as, ASR_STATE_HALT); 301 break; 302 #endif 303 } 304 break; 305 306 case ASR_STATE_SUBQUERY: 307 308 /* Run the DNS subquery. */ 309 310 if ((r = asr_run(as->as.hostnamadr.subq, ar)) == ASYNC_COND) 311 return (ASYNC_COND); 312 313 /* Done. */ 314 as->as.hostnamadr.subq = NULL; 315 316 if (ar->ar_datalen == -1) { 317 async_set_state(as, ASR_STATE_NEXT_DB); 318 break; 319 } 320 321 /* If we got a packet but no anwser, use the next DB. */ 322 if (ar->ar_count == 0) { 323 free(ar->ar_data); 324 as->as.hostnamadr.subq_h_errno = ar->ar_h_errno; 325 async_set_state(as, ASR_STATE_NEXT_DB); 326 break; 327 } 328 329 /* Read the hostent from the packet. */ 330 331 h = hostent_from_packet(as->as_type, 332 as->as.hostnamadr.family, ar->ar_data, ar->ar_datalen); 333 free(ar->ar_data); 334 if (h == NULL) { 335 ar->ar_errno = errno; 336 ar->ar_h_errno = NETDB_INTERNAL; 337 async_set_state(as, ASR_STATE_HALT); 338 break; 339 } 340 341 if (as->as_type == ASR_GETHOSTBYADDR) { 342 if (hostent_add_addr(h, as->as.hostnamadr.addr, 343 as->as.hostnamadr.addrlen) == -1) { 344 free(h); 345 ar->ar_errno = errno; 346 ar->ar_h_errno = NETDB_INTERNAL; 347 async_set_state(as, ASR_STATE_HALT); 348 break; 349 } 350 } 351 352 /* 353 * No address found in the dns packet. The blocking version 354 * reports this as an error. 355 */ 356 if ((as->as_type == ASR_GETHOSTBYNAME && 357 h->h.h_addr_list[0] == NULL) || 358 (as->as_type == ASR_GETHOSTBYADDR && 359 h->h.h_name == NULL)) { 360 free(h); 361 async_set_state(as, ASR_STATE_NEXT_DB); 362 break; 363 } 364 365 ar->ar_hostent = &h->h; 366 ar->ar_h_errno = NETDB_SUCCESS; 367 async_set_state(as, ASR_STATE_HALT); 368 break; 369 370 case ASR_STATE_NOT_FOUND: 371 ar->ar_errno = 0; 372 if (as->as.hostnamadr.subq_h_errno) 373 ar->ar_h_errno = as->as.hostnamadr.subq_h_errno; 374 else 375 ar->ar_h_errno = HOST_NOT_FOUND; 376 async_set_state(as, ASR_STATE_HALT); 377 break; 378 379 case ASR_STATE_HALT: 380 if (ar->ar_h_errno) 381 ar->ar_hostent = NULL; 382 else 383 ar->ar_errno = 0; 384 return (ASYNC_DONE); 385 386 default: 387 ar->ar_errno = EOPNOTSUPP; 388 ar->ar_h_errno = NETDB_INTERNAL; 389 ar->ar_gai_errno = EAI_SYSTEM; 390 async_set_state(as, ASR_STATE_HALT); 391 break; 392 } 393 goto next; 394 } 395 396 /* 397 * Create a hostent from a numeric address string. 398 */ 399 static struct hostent_ext * 400 hostent_from_addr(int family, const char *name, const char *addr) 401 { 402 struct hostent_ext *h; 403 404 if ((h = hostent_alloc(family)) == NULL) 405 return (NULL); 406 if (hostent_set_cname(h, name, 0) == -1) 407 goto fail; 408 if (hostent_add_addr(h, addr, h->h.h_length) == -1) 409 goto fail; 410 return (h); 411 fail: 412 free(h); 413 return (NULL); 414 } 415 416 /* 417 * Lookup the first matching entry in the hostfile, either by address or by 418 * name depending on reqtype, and build a hostent from the line. 419 */ 420 static struct hostent_ext * 421 hostent_file_match(FILE *f, int reqtype, int family, const char *data, 422 int datalen) 423 { 424 char *tokens[MAXTOKEN], addr[16]; 425 struct hostent_ext *h; 426 int n, i; 427 428 for (;;) { 429 n = asr_parse_namedb_line(f, tokens, MAXTOKEN); 430 if (n == -1) { 431 errno = 0; /* ignore errors reading the file */ 432 return (NULL); 433 } 434 435 /* there must be an address and at least one name */ 436 if (n < 2) 437 continue; 438 439 if (reqtype == ASR_GETHOSTBYNAME) { 440 for (i = 1; i < n; i++) { 441 if (strcasecmp(data, tokens[i])) 442 continue; 443 if (inet_pton(family, tokens[0], addr) == 1) 444 goto found; 445 } 446 } else { 447 if (inet_pton(family, tokens[0], addr) == 1 && 448 memcmp(addr, data, datalen) == 0) 449 goto found; 450 } 451 } 452 453 found: 454 if ((h = hostent_alloc(family)) == NULL) 455 return (NULL); 456 if (hostent_set_cname(h, tokens[1], 0) == -1) 457 goto fail; 458 for (i = 2; i < n; i ++) 459 if (hostent_add_alias(h, tokens[i], 0) == -1) 460 goto fail; 461 if (hostent_add_addr(h, addr, h->h.h_length) == -1) 462 goto fail; 463 return (h); 464 fail: 465 free(h); 466 return (NULL); 467 } 468 469 /* 470 * Fill the hostent from the given DNS packet. 471 */ 472 static struct hostent_ext * 473 hostent_from_packet(int reqtype, int family, char *pkt, size_t pktlen) 474 { 475 struct hostent_ext *h; 476 struct asr_unpack p; 477 struct asr_dns_header hdr; 478 struct asr_dns_query q; 479 struct asr_dns_rr rr; 480 char dname[MAXDNAME]; 481 482 if ((h = hostent_alloc(family)) == NULL) 483 return (NULL); 484 485 asr_unpack_init(&p, pkt, pktlen); 486 asr_unpack_header(&p, &hdr); 487 for (; hdr.qdcount; hdr.qdcount--) 488 asr_unpack_query(&p, &q); 489 strlcpy(dname, q.q_dname, sizeof(dname)); 490 491 for (; hdr.ancount; hdr.ancount--) { 492 asr_unpack_rr(&p, &rr); 493 if (rr.rr_class != C_IN) 494 continue; 495 switch (rr.rr_type) { 496 497 case T_CNAME: 498 if (reqtype == ASR_GETHOSTBYNAME) { 499 if (hostent_add_alias(h, rr.rr_dname, 1) == -1) 500 goto fail; 501 } else { 502 if (strcasecmp(rr.rr_dname, dname) == 0) 503 strlcpy(dname, rr.rr.cname.cname, 504 sizeof(dname)); 505 } 506 break; 507 508 case T_PTR: 509 if (reqtype != ASR_GETHOSTBYADDR) 510 break; 511 if (strcasecmp(rr.rr_dname, dname) != 0) 512 continue; 513 if (hostent_set_cname(h, rr.rr.ptr.ptrname, 1) == -1) 514 hostent_add_alias(h, rr.rr.ptr.ptrname, 1); 515 break; 516 517 case T_A: 518 if (reqtype != ASR_GETHOSTBYNAME) 519 break; 520 if (family != AF_INET) 521 break; 522 if (hostent_set_cname(h, rr.rr_dname, 1) == -1) 523 ; 524 if (hostent_add_addr(h, &rr.rr.in_a.addr, 4) == -1) 525 goto fail; 526 break; 527 528 case T_AAAA: 529 if (reqtype != ASR_GETHOSTBYNAME) 530 break; 531 if (family != AF_INET6) 532 break; 533 if (hostent_set_cname(h, rr.rr_dname, 1) == -1) 534 ; 535 if (hostent_add_addr(h, &rr.rr.in_aaaa.addr6, 16) == -1) 536 goto fail; 537 break; 538 } 539 } 540 541 return (h); 542 fail: 543 free(h); 544 return (NULL); 545 } 546 547 static struct hostent_ext * 548 hostent_alloc(int family) 549 { 550 struct hostent_ext *h; 551 size_t alloc; 552 553 alloc = sizeof(*h) + 1024; 554 if ((h = calloc(1, alloc)) == NULL) 555 return (NULL); 556 557 h->h.h_addrtype = family; 558 h->h.h_length = (family == AF_INET) ? 4 : 16; 559 h->h.h_aliases = h->aliases; 560 h->h.h_addr_list = h->addrs; 561 h->pos = (char *)(h) + sizeof(*h); 562 h->end = h->pos + 1024; 563 564 return (h); 565 } 566 567 static int 568 hostent_set_cname(struct hostent_ext *h, const char *name, int isdname) 569 { 570 char buf[MAXDNAME]; 571 size_t n; 572 573 if (h->h.h_name) 574 return (-1); 575 576 if (isdname) { 577 asr_strdname(name, buf, sizeof buf); 578 buf[strlen(buf) - 1] = '\0'; 579 if (!res_hnok(buf)) 580 return (-1); 581 name = buf; 582 } 583 584 n = strlen(name) + 1; 585 if (h->pos + n >= h->end) 586 return (-1); 587 588 h->h.h_name = h->pos; 589 memmove(h->pos, name, n); 590 h->pos += n; 591 return (0); 592 } 593 594 static int 595 hostent_add_alias(struct hostent_ext *h, const char *name, int isdname) 596 { 597 char buf[MAXDNAME]; 598 size_t i, n; 599 600 for (i = 0; i < MAXALIASES; i++) 601 if (h->aliases[i] == NULL) 602 break; 603 if (i == MAXALIASES) 604 return (-1); 605 606 if (isdname) { 607 asr_strdname(name, buf, sizeof buf); 608 buf[strlen(buf)-1] = '\0'; 609 if (!res_hnok(buf)) 610 return (-1); 611 name = buf; 612 } 613 614 n = strlen(name) + 1; 615 if (h->pos + n >= h->end) 616 return (-1); 617 618 h->aliases[i] = h->pos; 619 memmove(h->pos, name, n); 620 h->pos += n; 621 return (0); 622 } 623 624 static int 625 hostent_add_addr(struct hostent_ext *h, const void *addr, size_t size) 626 { 627 int i; 628 629 for (i = 0; i < MAXADDRS; i++) 630 if (h->addrs[i] == NULL) 631 break; 632 if (i == MAXADDRS) 633 return (-1); 634 635 if (h->pos + size >= h->end) 636 return (-1); 637 638 h->addrs[i] = h->pos; 639 memmove(h->pos, addr, size); 640 h->pos += size; 641 return (0); 642 } 643 644 #ifdef YP 645 static struct hostent_ext * 646 _yp_gethostnamadr(int type, const void *data) 647 { 648 static char *domain = NULL; 649 struct hostent_ext *h = NULL; 650 const char *name; 651 char buf[MAXHOSTNAMELEN]; 652 char *res = NULL; 653 int r, len; 654 655 if (!domain && _yp_check(&domain) == 0) { 656 errno = 0; /* ignore yp_bind errors */ 657 return (NULL); 658 } 659 660 if (type == ASR_GETHOSTBYNAME) { 661 name = data; 662 len = strlen(name); 663 r = yp_match(domain, "hosts.byname", name, len, &res, &len); 664 } 665 else { 666 if (inet_ntop(AF_INET, data, buf, sizeof buf) == NULL) 667 return (NULL); 668 len = strlen(buf); 669 r = yp_match(domain, "hosts.byaddr", buf, len, &res, &len); 670 } 671 if (r == 0) { 672 h = hostent_from_yp(AF_INET, res); 673 } else { 674 errno = 0; /* ignore error if not found */ 675 } 676 if (res) 677 free(res); 678 return (h); 679 } 680 681 static int 682 strsplit(char *line, char **tokens, int ntokens) 683 { 684 int ntok; 685 char *cp, **tp; 686 687 for (cp = line, tp = tokens, ntok = 0; 688 ntok < ntokens && (*tp = strsep(&cp, " \t")) != NULL; ) 689 if (**tp != '\0') { 690 tp++; 691 ntok++; 692 } 693 694 return (ntok); 695 } 696 697 static struct hostent_ext * 698 hostent_from_yp(int family, char *line) 699 { 700 struct hostent_ext *h; 701 char *next, *tokens[10], addr[IN6ADDRSZ]; 702 int i, ntok; 703 704 if ((h = hostent_alloc(family)) == NULL) 705 return (NULL); 706 707 for (next = line; line; line = next) { 708 if ((next = strchr(line, '\n'))) { 709 *next = '\0'; 710 next += 1; 711 } 712 ntok = strsplit(line, tokens, 10); 713 if (ntok < 2) 714 continue; 715 if (inet_pton(family, tokens[0], addr) == 1) 716 hostent_add_addr(h, addr, family == AF_INET ? 717 INADDRSZ : IN6ADDRSZ); 718 i = 2; 719 if (h->h.h_name == NULL) 720 hostent_set_cname(h, tokens[1], 0); 721 else if (strcmp(h->h.h_name, tokens[1])) 722 i = 1; 723 for (; i < ntok; i++) 724 hostent_add_alias(h, tokens[i], 0); 725 } 726 727 if (h->h.h_name == NULL) { 728 free(h); 729 return (NULL); 730 } 731 732 return (h); 733 } 734 #endif 735