ares_hosts_file.c (25888B)
1 /* MIT License 2 * 3 * Copyright (c) 2023 Brad House 4 * 5 * Permission is hereby granted, free of charge, to any person obtaining a copy 6 * of this software and associated documentation files (the "Software"), to deal 7 * in the Software without restriction, including without limitation the rights 8 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 * copies of the Software, and to permit persons to whom the Software is 10 * furnished to do so, subject to the following conditions: 11 * 12 * The above copyright notice and this permission notice (including the next 13 * paragraph) shall be included in all copies or substantial portions of the 14 * Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 * SOFTWARE. 23 * 24 * SPDX-License-Identifier: MIT 25 */ 26 #include "ares_private.h" 27 #ifdef HAVE_SYS_TYPES_H 28 # include <sys/types.h> 29 #endif 30 #ifdef HAVE_SYS_STAT_H 31 # include <sys/stat.h> 32 #endif 33 #ifdef HAVE_NETINET_IN_H 34 # include <netinet/in.h> 35 #endif 36 #ifdef HAVE_NETDB_H 37 # include <netdb.h> 38 #endif 39 #ifdef HAVE_ARPA_INET_H 40 # include <arpa/inet.h> 41 #endif 42 #include <time.h> 43 44 /* HOSTS FILE PROCESSING OVERVIEW 45 * ============================== 46 * The hosts file on the system contains static entries to be processed locally 47 * rather than querying the nameserver. Each row is an IP address followed by 48 * a list of space delimited hostnames that match the ip address. This is used 49 * for both forward and reverse lookups. 50 * 51 * We are caching the entire parsed hosts file for performance reasons. Some 52 * files may be quite sizable and as per Issue #458 can approach 1/2MB in size, 53 * and the parse overhead on a rapid succession of queries can be quite large. 54 * The entries are stored in forwards and backwards hashtables so we can get 55 * O(1) performance on lookup. The file is cached until the file modification 56 * timestamp changes. 57 * 58 * The hosts file processing is quite unique. It has to merge all related hosts 59 * and ips into a single entry due to file formatting requirements. For 60 * instance take the below: 61 * 62 * 127.0.0.1 localhost.localdomain localhost 63 * ::1 localhost.localdomain localhost 64 * 192.168.1.1 host.example.com host 65 * 192.168.1.5 host.example.com host 66 * 2620:1234::1 host.example.com host6.example.com host6 host 67 * 68 * This will yield 2 entries. 69 * 1) ips: 127.0.0.1,::1 70 * hosts: localhost.localdomain,localhost 71 * 2) ips: 192.168.1.1,192.168.1.5,2620:1234::1 72 * hosts: host.example.com,host,host6.example.com,host6 73 * 74 * It could be argued that if searching for 192.168.1.1 that the 'host6' 75 * hostnames should not be returned, but this implementation will return them 76 * since they are related. It is unlikely this will matter in the real world. 77 */ 78 79 struct ares_hosts_file { 80 time_t ts; 81 /*! cache the filename so we know if the filename changes it automatically 82 * invalidates the cache */ 83 char *filename; 84 /*! iphash is the owner of the 'entry' object as there is only ever a single 85 * match to the object. */ 86 ares_htable_strvp_t *iphash; 87 /*! hosthash does not own the entry so won't free on destruction */ 88 ares_htable_strvp_t *hosthash; 89 }; 90 91 struct ares_hosts_entry { 92 size_t refcnt; /*! If the entry is stored multiple times in the 93 * ip address hash, we have to reference count it */ 94 ares_llist_t *ips; 95 ares_llist_t *hosts; 96 }; 97 98 const void *ares_dns_pton(const char *ipaddr, struct ares_addr *addr, 99 size_t *out_len) 100 { 101 const void *ptr = NULL; 102 size_t ptr_len = 0; 103 104 if (ipaddr == NULL || addr == NULL || out_len == NULL) { 105 return NULL; /* LCOV_EXCL_LINE: DefensiveCoding */ 106 } 107 108 *out_len = 0; 109 110 if (addr->family == AF_INET && 111 ares_inet_pton(AF_INET, ipaddr, &addr->addr.addr4) > 0) { 112 ptr = &addr->addr.addr4; 113 ptr_len = sizeof(addr->addr.addr4); 114 } else if (addr->family == AF_INET6 && 115 ares_inet_pton(AF_INET6, ipaddr, &addr->addr.addr6) > 0) { 116 ptr = &addr->addr.addr6; 117 ptr_len = sizeof(addr->addr.addr6); 118 } else if (addr->family == AF_UNSPEC) { 119 if (ares_inet_pton(AF_INET, ipaddr, &addr->addr.addr4) > 0) { 120 addr->family = AF_INET; 121 ptr = &addr->addr.addr4; 122 ptr_len = sizeof(addr->addr.addr4); 123 } else if (ares_inet_pton(AF_INET6, ipaddr, &addr->addr.addr6) > 0) { 124 addr->family = AF_INET6; 125 ptr = &addr->addr.addr6; 126 ptr_len = sizeof(addr->addr.addr6); 127 } 128 } 129 130 *out_len = ptr_len; 131 return ptr; 132 } 133 134 static ares_bool_t ares_normalize_ipaddr(const char *ipaddr, char *out, 135 size_t out_len) 136 { 137 struct ares_addr data; 138 const void *addr; 139 size_t addr_len = 0; 140 141 memset(&data, 0, sizeof(data)); 142 data.family = AF_UNSPEC; 143 144 addr = ares_dns_pton(ipaddr, &data, &addr_len); 145 if (addr == NULL) { 146 return ARES_FALSE; 147 } 148 149 if (!ares_inet_ntop(data.family, addr, out, (ares_socklen_t)out_len)) { 150 return ARES_FALSE; /* LCOV_EXCL_LINE: DefensiveCoding */ 151 } 152 153 return ARES_TRUE; 154 } 155 156 static void ares_hosts_entry_destroy(ares_hosts_entry_t *entry) 157 { 158 if (entry == NULL) { 159 return; 160 } 161 162 /* Honor reference counting */ 163 if (entry->refcnt != 0) { 164 entry->refcnt--; 165 } 166 167 if (entry->refcnt > 0) { 168 return; 169 } 170 171 ares_llist_destroy(entry->hosts); 172 ares_llist_destroy(entry->ips); 173 ares_free(entry); 174 } 175 176 static void ares_hosts_entry_destroy_cb(void *entry) 177 { 178 ares_hosts_entry_destroy(entry); 179 } 180 181 void ares_hosts_file_destroy(ares_hosts_file_t *hf) 182 { 183 if (hf == NULL) { 184 return; 185 } 186 187 ares_free(hf->filename); 188 ares_htable_strvp_destroy(hf->hosthash); 189 ares_htable_strvp_destroy(hf->iphash); 190 ares_free(hf); 191 } 192 193 static ares_hosts_file_t *ares_hosts_file_create(const char *filename) 194 { 195 ares_hosts_file_t *hf = ares_malloc_zero(sizeof(*hf)); 196 if (hf == NULL) { 197 goto fail; 198 } 199 200 hf->ts = time(NULL); 201 202 hf->filename = ares_strdup(filename); 203 if (hf->filename == NULL) { 204 goto fail; 205 } 206 207 hf->iphash = ares_htable_strvp_create(ares_hosts_entry_destroy_cb); 208 if (hf->iphash == NULL) { 209 goto fail; 210 } 211 212 hf->hosthash = ares_htable_strvp_create(NULL); 213 if (hf->hosthash == NULL) { 214 goto fail; 215 } 216 217 return hf; 218 219 fail: 220 ares_hosts_file_destroy(hf); 221 return NULL; 222 } 223 224 typedef enum { 225 ARES_MATCH_NONE = 0, 226 ARES_MATCH_IPADDR = 1, 227 ARES_MATCH_HOST = 2 228 } ares_hosts_file_match_t; 229 230 static ares_status_t ares_hosts_file_merge_entry( 231 const ares_hosts_file_t *hf, ares_hosts_entry_t *existing, 232 ares_hosts_entry_t *entry, ares_hosts_file_match_t matchtype) 233 { 234 ares_llist_node_t *node; 235 236 /* If we matched on IP address, we know there can only be 1, so there's no 237 * reason to do anything */ 238 if (matchtype != ARES_MATCH_IPADDR) { 239 while ((node = ares_llist_node_first(entry->ips)) != NULL) { 240 const char *ipaddr = ares_llist_node_val(node); 241 242 if (ares_htable_strvp_get_direct(hf->iphash, ipaddr) != NULL) { 243 ares_llist_node_destroy(node); 244 continue; 245 } 246 247 ares_llist_node_mvparent_last(node, existing->ips); 248 } 249 } 250 251 252 while ((node = ares_llist_node_first(entry->hosts)) != NULL) { 253 const char *hostname = ares_llist_node_val(node); 254 255 if (ares_htable_strvp_get_direct(hf->hosthash, hostname) != NULL) { 256 ares_llist_node_destroy(node); 257 continue; 258 } 259 260 ares_llist_node_mvparent_last(node, existing->hosts); 261 } 262 263 ares_hosts_entry_destroy(entry); 264 return ARES_SUCCESS; 265 } 266 267 static ares_hosts_file_match_t 268 ares_hosts_file_match(const ares_hosts_file_t *hf, ares_hosts_entry_t *entry, 269 ares_hosts_entry_t **match) 270 { 271 ares_llist_node_t *node; 272 *match = NULL; 273 274 for (node = ares_llist_node_first(entry->ips); node != NULL; 275 node = ares_llist_node_next(node)) { 276 const char *ipaddr = ares_llist_node_val(node); 277 *match = ares_htable_strvp_get_direct(hf->iphash, ipaddr); 278 if (*match != NULL) { 279 return ARES_MATCH_IPADDR; 280 } 281 } 282 283 for (node = ares_llist_node_first(entry->hosts); node != NULL; 284 node = ares_llist_node_next(node)) { 285 const char *host = ares_llist_node_val(node); 286 *match = ares_htable_strvp_get_direct(hf->hosthash, host); 287 if (*match != NULL) { 288 return ARES_MATCH_HOST; 289 } 290 } 291 292 return ARES_MATCH_NONE; 293 } 294 295 /*! entry is invalidated upon calling this function, always, even on error */ 296 static ares_status_t ares_hosts_file_add(ares_hosts_file_t *hosts, 297 ares_hosts_entry_t *entry) 298 { 299 ares_hosts_entry_t *match = NULL; 300 ares_status_t status = ARES_SUCCESS; 301 ares_llist_node_t *node; 302 ares_hosts_file_match_t matchtype; 303 size_t num_hostnames; 304 305 /* Record the number of hostnames in this entry file. If we merge into an 306 * existing record, these will be *appended* to the entry, so we'll count 307 * backwards when adding to the hosts hashtable */ 308 num_hostnames = ares_llist_len(entry->hosts); 309 310 matchtype = ares_hosts_file_match(hosts, entry, &match); 311 312 if (matchtype != ARES_MATCH_NONE) { 313 status = ares_hosts_file_merge_entry(hosts, match, entry, matchtype); 314 if (status != ARES_SUCCESS) { 315 ares_hosts_entry_destroy(entry); /* LCOV_EXCL_LINE: DefensiveCoding */ 316 return status; /* LCOV_EXCL_LINE: DefensiveCoding */ 317 } 318 /* entry was invalidated above by merging */ 319 entry = match; 320 } 321 322 if (matchtype != ARES_MATCH_IPADDR) { 323 const char *ipaddr = ares_llist_last_val(entry->ips); 324 325 if (!ares_htable_strvp_get(hosts->iphash, ipaddr, NULL)) { 326 if (!ares_htable_strvp_insert(hosts->iphash, ipaddr, entry)) { 327 ares_hosts_entry_destroy(entry); 328 return ARES_ENOMEM; 329 } 330 entry->refcnt++; 331 } 332 } 333 334 /* Go backwards, on a merge, hostnames are appended. Breakout once we've 335 * consumed all the hosts that we appended */ 336 for (node = ares_llist_node_last(entry->hosts); node != NULL; 337 node = ares_llist_node_prev(node)) { 338 const char *val = ares_llist_node_val(node); 339 340 if (num_hostnames == 0) { 341 break; 342 } 343 344 num_hostnames--; 345 346 /* first hostname match wins. If we detect a duplicate hostname for another 347 * ip it will automatically be added to the same entry */ 348 if (ares_htable_strvp_get(hosts->hosthash, val, NULL)) { 349 continue; 350 } 351 352 if (!ares_htable_strvp_insert(hosts->hosthash, val, entry)) { 353 return ARES_ENOMEM; 354 } 355 } 356 357 return ARES_SUCCESS; 358 } 359 360 static ares_bool_t ares_hosts_entry_isdup(ares_hosts_entry_t *entry, 361 const char *host) 362 { 363 ares_llist_node_t *node; 364 365 for (node = ares_llist_node_first(entry->ips); node != NULL; 366 node = ares_llist_node_next(node)) { 367 const char *myhost = ares_llist_node_val(node); 368 if (ares_strcaseeq(myhost, host)) { 369 return ARES_TRUE; 370 } 371 } 372 373 return ARES_FALSE; 374 } 375 376 static ares_status_t ares_parse_hosts_hostnames(ares_buf_t *buf, 377 ares_hosts_entry_t *entry) 378 { 379 entry->hosts = ares_llist_create(ares_free); 380 if (entry->hosts == NULL) { 381 return ARES_ENOMEM; 382 } 383 384 /* Parse hostnames and aliases */ 385 while (ares_buf_len(buf)) { 386 char hostname[256]; 387 char *temp; 388 ares_status_t status; 389 unsigned char comment = '#'; 390 391 ares_buf_consume_whitespace(buf, ARES_FALSE); 392 393 if (ares_buf_len(buf) == 0) { 394 break; 395 } 396 397 /* See if it is a comment, if so stop processing */ 398 if (ares_buf_begins_with(buf, &comment, 1)) { 399 break; 400 } 401 402 ares_buf_tag(buf); 403 404 /* Must be at end of line */ 405 if (ares_buf_consume_nonwhitespace(buf) == 0) { 406 break; 407 } 408 409 status = ares_buf_tag_fetch_string(buf, hostname, sizeof(hostname)); 410 if (status != ARES_SUCCESS) { 411 /* Bad entry, just ignore as long as its not the first. If its the first, 412 * it must be valid */ 413 if (ares_llist_len(entry->hosts) == 0) { 414 return ARES_EBADSTR; 415 } 416 417 continue; 418 } 419 420 /* Validate it is a valid hostname characterset */ 421 if (!ares_is_hostname(hostname)) { 422 continue; 423 } 424 425 /* Don't add a duplicate to the same entry */ 426 if (ares_hosts_entry_isdup(entry, hostname)) { 427 continue; 428 } 429 430 /* Add to list */ 431 temp = ares_strdup(hostname); 432 if (temp == NULL) { 433 return ARES_ENOMEM; 434 } 435 436 if (ares_llist_insert_last(entry->hosts, temp) == NULL) { 437 ares_free(temp); 438 return ARES_ENOMEM; 439 } 440 } 441 442 /* Must have at least 1 entry */ 443 if (ares_llist_len(entry->hosts) == 0) { 444 return ARES_EBADSTR; 445 } 446 447 return ARES_SUCCESS; 448 } 449 450 static ares_status_t ares_parse_hosts_ipaddr(ares_buf_t *buf, 451 ares_hosts_entry_t **entry_out) 452 { 453 char addr[INET6_ADDRSTRLEN]; 454 char *temp; 455 ares_hosts_entry_t *entry = NULL; 456 ares_status_t status; 457 458 *entry_out = NULL; 459 460 ares_buf_tag(buf); 461 ares_buf_consume_nonwhitespace(buf); 462 status = ares_buf_tag_fetch_string(buf, addr, sizeof(addr)); 463 if (status != ARES_SUCCESS) { 464 return status; 465 } 466 467 /* Validate and normalize the ip address format */ 468 if (!ares_normalize_ipaddr(addr, addr, sizeof(addr))) { 469 return ARES_EBADSTR; 470 } 471 472 entry = ares_malloc_zero(sizeof(*entry)); 473 if (entry == NULL) { 474 return ARES_ENOMEM; 475 } 476 477 entry->ips = ares_llist_create(ares_free); 478 if (entry->ips == NULL) { 479 ares_hosts_entry_destroy(entry); 480 return ARES_ENOMEM; 481 } 482 483 temp = ares_strdup(addr); 484 if (temp == NULL) { 485 ares_hosts_entry_destroy(entry); 486 return ARES_ENOMEM; 487 } 488 489 if (ares_llist_insert_first(entry->ips, temp) == NULL) { 490 ares_free(temp); 491 ares_hosts_entry_destroy(entry); 492 return ARES_ENOMEM; 493 } 494 495 *entry_out = entry; 496 497 return ARES_SUCCESS; 498 } 499 500 static ares_status_t ares_parse_hosts(const char *filename, 501 ares_hosts_file_t **out) 502 { 503 ares_buf_t *buf = NULL; 504 ares_status_t status = ARES_EBADRESP; 505 ares_hosts_file_t *hf = NULL; 506 ares_hosts_entry_t *entry = NULL; 507 508 *out = NULL; 509 510 buf = ares_buf_create(); 511 if (buf == NULL) { 512 status = ARES_ENOMEM; 513 goto done; 514 } 515 516 status = ares_buf_load_file(filename, buf); 517 if (status != ARES_SUCCESS) { 518 goto done; 519 } 520 521 hf = ares_hosts_file_create(filename); 522 if (hf == NULL) { 523 status = ARES_ENOMEM; 524 goto done; 525 } 526 527 while (ares_buf_len(buf)) { 528 unsigned char comment = '#'; 529 530 /* -- Start of new line here -- */ 531 532 /* Consume any leading whitespace */ 533 ares_buf_consume_whitespace(buf, ARES_FALSE); 534 535 if (ares_buf_len(buf) == 0) { 536 break; 537 } 538 539 /* See if it is a comment, if so, consume remaining line */ 540 if (ares_buf_begins_with(buf, &comment, 1)) { 541 ares_buf_consume_line(buf, ARES_TRUE); 542 continue; 543 } 544 545 /* Pull off ip address */ 546 status = ares_parse_hosts_ipaddr(buf, &entry); 547 if (status == ARES_ENOMEM) { 548 goto done; 549 } 550 if (status != ARES_SUCCESS) { 551 /* Bad line, consume and go onto next */ 552 ares_buf_consume_line(buf, ARES_TRUE); 553 continue; 554 } 555 556 /* Parse of the hostnames */ 557 status = ares_parse_hosts_hostnames(buf, entry); 558 if (status == ARES_ENOMEM) { 559 goto done; 560 } else if (status != ARES_SUCCESS) { 561 /* Bad line, consume and go onto next */ 562 ares_hosts_entry_destroy(entry); 563 entry = NULL; 564 ares_buf_consume_line(buf, ARES_TRUE); 565 continue; 566 } 567 568 /* Append the successful entry to the hosts file */ 569 status = ares_hosts_file_add(hf, entry); 570 entry = NULL; /* is always invalidated by this function, even on error */ 571 if (status != ARES_SUCCESS) { 572 goto done; 573 } 574 575 /* Go to next line */ 576 ares_buf_consume_line(buf, ARES_TRUE); 577 } 578 579 status = ARES_SUCCESS; 580 581 done: 582 ares_hosts_entry_destroy(entry); 583 ares_buf_destroy(buf); 584 if (status != ARES_SUCCESS) { 585 ares_hosts_file_destroy(hf); 586 } else { 587 *out = hf; 588 } 589 return status; 590 } 591 592 static ares_bool_t ares_hosts_expired(const char *filename, 593 const ares_hosts_file_t *hf) 594 { 595 time_t mod_ts = 0; 596 597 #ifdef HAVE_STAT 598 struct stat st; 599 if (stat(filename, &st) == 0) { 600 mod_ts = st.st_mtime; 601 } 602 #elif defined(_WIN32) 603 struct _stat st; 604 if (_stat(filename, &st) == 0) { 605 mod_ts = st.st_mtime; 606 } 607 #else 608 (void)filename; 609 #endif 610 611 if (hf == NULL) { 612 return ARES_TRUE; 613 } 614 615 /* Expire every 60s if we can't get a time */ 616 if (mod_ts == 0) { 617 mod_ts = 618 time(NULL) - 60; /* LCOV_EXCL_LINE: only on systems without stat() */ 619 } 620 621 /* If filenames are different, its expired */ 622 if (!ares_strcaseeq(hf->filename, filename)) { 623 return ARES_TRUE; 624 } 625 626 if (hf->ts <= mod_ts) { 627 return ARES_TRUE; 628 } 629 630 return ARES_FALSE; 631 } 632 633 static ares_status_t ares_hosts_path(const ares_channel_t *channel, 634 ares_bool_t use_env, char **path) 635 { 636 char *path_hosts = NULL; 637 638 *path = NULL; 639 640 if (channel->hosts_path) { 641 path_hosts = ares_strdup(channel->hosts_path); 642 if (!path_hosts) { 643 return ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */ 644 } 645 } 646 647 if (use_env) { 648 if (path_hosts) { 649 ares_free(path_hosts); 650 } 651 652 path_hosts = ares_strdup(getenv("CARES_HOSTS")); 653 if (!path_hosts) { 654 return ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */ 655 } 656 } 657 658 if (!path_hosts) { 659 #if defined(USE_WINSOCK) 660 char PATH_HOSTS[MAX_PATH] = ""; 661 char tmp[MAX_PATH]; 662 HKEY hkeyHosts; 663 DWORD dwLength = sizeof(tmp); 664 if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, WIN_NS_NT_KEY, 0, KEY_READ, 665 &hkeyHosts) != ERROR_SUCCESS) { 666 return ARES_ENOTFOUND; 667 } 668 RegQueryValueExA(hkeyHosts, DATABASEPATH, NULL, NULL, (LPBYTE)tmp, 669 &dwLength); 670 ExpandEnvironmentStringsA(tmp, PATH_HOSTS, MAX_PATH); 671 RegCloseKey(hkeyHosts); 672 strcat(PATH_HOSTS, WIN_PATH_HOSTS); 673 #elif defined(WATT32) 674 const char *PATH_HOSTS = _w32_GetHostsFile(); 675 676 if (!PATH_HOSTS) { 677 return ARES_ENOTFOUND; 678 } 679 #endif 680 path_hosts = ares_strdup(PATH_HOSTS); 681 if (!path_hosts) { 682 return ARES_ENOMEM; 683 } 684 } 685 686 *path = path_hosts; 687 return ARES_SUCCESS; 688 } 689 690 static ares_status_t ares_hosts_update(ares_channel_t *channel, 691 ares_bool_t use_env) 692 { 693 ares_status_t status; 694 char *filename = NULL; 695 696 status = ares_hosts_path(channel, use_env, &filename); 697 if (status != ARES_SUCCESS) { 698 return status; 699 } 700 701 if (!ares_hosts_expired(filename, channel->hf)) { 702 ares_free(filename); 703 return ARES_SUCCESS; 704 } 705 706 ares_hosts_file_destroy(channel->hf); 707 channel->hf = NULL; 708 709 status = ares_parse_hosts(filename, &channel->hf); 710 ares_free(filename); 711 return status; 712 } 713 714 ares_status_t ares_hosts_search_ipaddr(ares_channel_t *channel, 715 ares_bool_t use_env, const char *ipaddr, 716 const ares_hosts_entry_t **entry) 717 { 718 ares_status_t status; 719 char addr[INET6_ADDRSTRLEN]; 720 721 *entry = NULL; 722 723 status = ares_hosts_update(channel, use_env); 724 if (status != ARES_SUCCESS) { 725 return status; 726 } 727 728 if (channel->hf == NULL) { 729 return ARES_ENOTFOUND; /* LCOV_EXCL_LINE: DefensiveCoding */ 730 } 731 732 if (!ares_normalize_ipaddr(ipaddr, addr, sizeof(addr))) { 733 return ARES_EBADNAME; 734 } 735 736 *entry = ares_htable_strvp_get_direct(channel->hf->iphash, addr); 737 if (*entry == NULL) { 738 return ARES_ENOTFOUND; 739 } 740 741 return ARES_SUCCESS; 742 } 743 744 ares_status_t ares_hosts_search_host(ares_channel_t *channel, 745 ares_bool_t use_env, const char *host, 746 const ares_hosts_entry_t **entry) 747 { 748 ares_status_t status; 749 750 *entry = NULL; 751 752 status = ares_hosts_update(channel, use_env); 753 if (status != ARES_SUCCESS) { 754 return status; 755 } 756 757 if (channel->hf == NULL) { 758 return ARES_ENOTFOUND; /* LCOV_EXCL_LINE: DefensiveCoding */ 759 } 760 761 *entry = ares_htable_strvp_get_direct(channel->hf->hosthash, host); 762 if (*entry == NULL) { 763 return ARES_ENOTFOUND; 764 } 765 766 return ARES_SUCCESS; 767 } 768 769 static ares_status_t 770 ares_hosts_ai_append_cnames(const ares_hosts_entry_t *entry, 771 struct ares_addrinfo_cname **cnames_out) 772 { 773 struct ares_addrinfo_cname *cname = NULL; 774 struct ares_addrinfo_cname *cnames = NULL; 775 const char *primaryhost; 776 ares_llist_node_t *node; 777 ares_status_t status; 778 size_t cnt = 0; 779 780 node = ares_llist_node_first(entry->hosts); 781 primaryhost = ares_llist_node_val(node); 782 /* Skip to next node to start with aliases */ 783 node = ares_llist_node_next(node); 784 785 while (node != NULL) { 786 const char *host = ares_llist_node_val(node); 787 788 /* Cap at 100 entries. , some people use 789 * https://github.com/StevenBlack/hosts and we don't need 200k+ aliases */ 790 cnt++; 791 if (cnt > 100) { 792 break; /* LCOV_EXCL_LINE: DefensiveCoding */ 793 } 794 795 cname = ares_append_addrinfo_cname(&cnames); 796 if (cname == NULL) { 797 status = ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */ 798 goto done; /* LCOV_EXCL_LINE: OutOfMemory */ 799 } 800 801 cname->alias = ares_strdup(host); 802 if (cname->alias == NULL) { 803 status = ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */ 804 goto done; /* LCOV_EXCL_LINE: OutOfMemory */ 805 } 806 807 cname->name = ares_strdup(primaryhost); 808 if (cname->name == NULL) { 809 status = ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */ 810 goto done; /* LCOV_EXCL_LINE: OutOfMemory */ 811 } 812 813 node = ares_llist_node_next(node); 814 } 815 816 /* No entries, add only primary */ 817 if (cnames == NULL) { 818 cname = ares_append_addrinfo_cname(&cnames); 819 if (cname == NULL) { 820 status = ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */ 821 goto done; /* LCOV_EXCL_LINE: OutOfMemory */ 822 } 823 824 cname->name = ares_strdup(primaryhost); 825 if (cname->name == NULL) { 826 status = ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */ 827 goto done; /* LCOV_EXCL_LINE: OutOfMemory */ 828 } 829 } 830 status = ARES_SUCCESS; 831 832 done: 833 if (status != ARES_SUCCESS) { 834 ares_freeaddrinfo_cnames(cnames); /* LCOV_EXCL_LINE: DefensiveCoding */ 835 return status; /* LCOV_EXCL_LINE: DefensiveCoding */ 836 } 837 838 *cnames_out = cnames; 839 return ARES_SUCCESS; 840 } 841 842 ares_status_t ares_hosts_entry_to_addrinfo(const ares_hosts_entry_t *entry, 843 const char *name, int family, 844 unsigned short port, 845 ares_bool_t want_cnames, 846 struct ares_addrinfo *ai) 847 { 848 ares_status_t status = ARES_ENOTFOUND; 849 struct ares_addrinfo_cname *cnames = NULL; 850 struct ares_addrinfo_node *ainodes = NULL; 851 ares_llist_node_t *node; 852 853 switch (family) { 854 case AF_INET: 855 case AF_INET6: 856 case AF_UNSPEC: 857 break; 858 default: /* LCOV_EXCL_LINE: DefensiveCoding */ 859 return ARES_EBADFAMILY; /* LCOV_EXCL_LINE: DefensiveCoding */ 860 } 861 862 if (name != NULL) { 863 ares_free(ai->name); 864 ai->name = ares_strdup(name); 865 if (ai->name == NULL) { 866 status = ARES_ENOMEM; /* LCOV_EXCL_LINE: OutOfMemory */ 867 goto done; /* LCOV_EXCL_LINE: OutOfMemory */ 868 } 869 } 870 871 for (node = ares_llist_node_first(entry->ips); node != NULL; 872 node = ares_llist_node_next(node)) { 873 struct ares_addr addr; 874 const void *ptr = NULL; 875 size_t ptr_len = 0; 876 const char *ipaddr = ares_llist_node_val(node); 877 878 memset(&addr, 0, sizeof(addr)); 879 addr.family = family; 880 ptr = ares_dns_pton(ipaddr, &addr, &ptr_len); 881 882 if (ptr == NULL) { 883 continue; 884 } 885 886 status = ares_append_ai_node(addr.family, port, 0, ptr, &ainodes); 887 if (status != ARES_SUCCESS) { 888 goto done; /* LCOV_EXCL_LINE: DefensiveCoding */ 889 } 890 } 891 892 /* Might be ARES_ENOTFOUND here if no ips matched requested address family */ 893 if (status != ARES_SUCCESS) { 894 goto done; 895 } 896 897 if (want_cnames) { 898 status = ares_hosts_ai_append_cnames(entry, &cnames); 899 if (status != ARES_SUCCESS) { 900 goto done; /* LCOV_EXCL_LINE: DefensiveCoding */ 901 } 902 } 903 904 status = ARES_SUCCESS; 905 906 done: 907 if (status != ARES_SUCCESS) { 908 /* LCOV_EXCL_START: defensive coding */ 909 ares_freeaddrinfo_cnames(cnames); 910 ares_freeaddrinfo_nodes(ainodes); 911 ares_free(ai->name); 912 ai->name = NULL; 913 return status; 914 /* LCOV_EXCL_STOP */ 915 } 916 ares_addrinfo_cat_cnames(&ai->cnames, cnames); 917 ares_addrinfo_cat_nodes(&ai->nodes, ainodes); 918 919 return status; 920 } 921 922 ares_status_t ares_hosts_entry_to_hostent(const ares_hosts_entry_t *entry, 923 int family, struct hostent **hostent) 924 { 925 ares_status_t status; 926 struct ares_addrinfo *ai = ares_malloc_zero(sizeof(*ai)); 927 928 *hostent = NULL; 929 930 if (ai == NULL) { 931 return ARES_ENOMEM; 932 } 933 934 status = ares_hosts_entry_to_addrinfo(entry, NULL, family, 0, ARES_TRUE, ai); 935 if (status != ARES_SUCCESS) { 936 goto done; 937 } 938 939 status = ares_addrinfo2hostent(ai, family, hostent); 940 if (status != ARES_SUCCESS) { 941 goto done; 942 } 943 944 done: 945 ares_freeaddrinfo(ai); 946 if (status != ARES_SUCCESS) { 947 ares_free_hostent(*hostent); 948 *hostent = NULL; 949 } 950 951 return status; 952 }