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