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