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