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