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