ares_getaddrinfo.c (22294B)
1 /* MIT License 2 * 3 * Copyright (c) 1998, 2011, 2013 Massachusetts Institute of Technology 4 * Copyright (c) 2017 Christian Ammer 5 * Copyright (c) 2019 Andrew Selivanov 6 * 7 * Permission is hereby granted, free of charge, to any person obtaining a copy 8 * of this software and associated documentation files (the "Software"), to deal 9 * in the Software without restriction, including without limitation the rights 10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 * copies of the Software, and to permit persons to whom the Software is 12 * furnished to do so, subject to the following conditions: 13 * 14 * The above copyright notice and this permission notice (including the next 15 * paragraph) shall be included in all copies or substantial portions of the 16 * Software. 17 * 18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 * SOFTWARE. 25 * 26 * SPDX-License-Identifier: MIT 27 */ 28 29 #include "ares_private.h" 30 31 #ifdef HAVE_GETSERVBYNAME_R 32 # if !defined(GETSERVBYNAME_R_ARGS) || (GETSERVBYNAME_R_ARGS < 4) || \ 33 (GETSERVBYNAME_R_ARGS > 6) 34 # error "you MUST specify a valid number of arguments for getservbyname_r" 35 # endif 36 #endif 37 38 #ifdef HAVE_NETINET_IN_H 39 # include <netinet/in.h> 40 #endif 41 #ifdef HAVE_NETDB_H 42 # include <netdb.h> 43 #endif 44 #ifdef HAVE_ARPA_INET_H 45 # include <arpa/inet.h> 46 #endif 47 48 #include "ares_nameser.h" 49 50 #ifdef HAVE_STRINGS_H 51 # include <strings.h> 52 #endif 53 #include <assert.h> 54 55 #ifdef HAVE_LIMITS_H 56 # include <limits.h> 57 #endif 58 59 #include "ares_dns.h" 60 61 struct host_query { 62 ares_channel_t *channel; 63 char *name; 64 unsigned short port; /* in host order */ 65 ares_addrinfo_callback callback; 66 void *arg; 67 struct ares_addrinfo_hints hints; 68 int sent_family; /* this family is what was is being used */ 69 size_t timeouts; /* number of timeouts we saw for this request */ 70 char *lookups; /* Duplicate memory from channel because of ares_reinit() */ 71 const char *remaining_lookups; /* types of lookup we need to perform ("fb" by 72 default, file and dns respectively) */ 73 74 /* Search order for names */ 75 char **names; 76 size_t names_cnt; 77 size_t next_name_idx; /* next name index being attempted */ 78 79 struct ares_addrinfo *ai; /* store results between lookups */ 80 unsigned short qid_a; /* qid for A request */ 81 unsigned short qid_aaaa; /* qid for AAAA request */ 82 83 size_t remaining; /* number of DNS answers waiting for */ 84 85 /* Track nodata responses to possibly override final result */ 86 size_t nodata_cnt; 87 }; 88 89 static const struct ares_addrinfo_hints default_hints = { 90 0, /* ai_flags */ 91 AF_UNSPEC, /* ai_family */ 92 0, /* ai_socktype */ 93 0, /* ai_protocol */ 94 }; 95 96 /* forward declarations */ 97 static ares_bool_t next_dns_lookup(struct host_query *hquery); 98 99 struct ares_addrinfo_cname * 100 ares_append_addrinfo_cname(struct ares_addrinfo_cname **head) 101 { 102 struct ares_addrinfo_cname *tail = ares_malloc_zero(sizeof(*tail)); 103 struct ares_addrinfo_cname *last = *head; 104 105 if (tail == NULL) { 106 return NULL; /* LCOV_EXCL_LINE: OutOfMemory */ 107 } 108 109 if (!last) { 110 *head = tail; 111 return tail; 112 } 113 114 while (last->next) { 115 last = last->next; 116 } 117 118 last->next = tail; 119 return tail; 120 } 121 122 void ares_addrinfo_cat_cnames(struct ares_addrinfo_cname **head, 123 struct ares_addrinfo_cname *tail) 124 { 125 struct ares_addrinfo_cname *last = *head; 126 if (!last) { 127 *head = tail; 128 return; 129 } 130 131 while (last->next) { 132 last = last->next; 133 } 134 135 last->next = tail; 136 } 137 138 /* Allocate new addrinfo and append to the tail. */ 139 struct ares_addrinfo_node * 140 ares_append_addrinfo_node(struct ares_addrinfo_node **head) 141 { 142 struct ares_addrinfo_node *tail = ares_malloc_zero(sizeof(*tail)); 143 struct ares_addrinfo_node *last = *head; 144 145 if (tail == NULL) { 146 return NULL; /* LCOV_EXCL_LINE: OutOfMemory */ 147 } 148 149 if (!last) { 150 *head = tail; 151 return tail; 152 } 153 154 while (last->ai_next) { 155 last = last->ai_next; 156 } 157 158 last->ai_next = tail; 159 return tail; 160 } 161 162 void ares_addrinfo_cat_nodes(struct ares_addrinfo_node **head, 163 struct ares_addrinfo_node *tail) 164 { 165 struct ares_addrinfo_node *last = *head; 166 if (!last) { 167 *head = tail; 168 return; 169 } 170 171 while (last->ai_next) { 172 last = last->ai_next; 173 } 174 175 last->ai_next = tail; 176 } 177 178 /* Resolve service name into port number given in host byte order. 179 * If not resolved, return 0. 180 */ 181 static unsigned short lookup_service(const char *service, int flags) 182 { 183 const char *proto; 184 struct servent *sep; 185 #ifdef HAVE_GETSERVBYNAME_R 186 struct servent se; 187 char tmpbuf[4096]; 188 #endif 189 190 if (service) { 191 if (flags & ARES_NI_UDP) { 192 proto = "udp"; 193 } else if (flags & ARES_NI_SCTP) { 194 proto = "sctp"; 195 } else if (flags & ARES_NI_DCCP) { 196 proto = "dccp"; 197 } else { 198 proto = "tcp"; 199 } 200 #ifdef HAVE_GETSERVBYNAME_R 201 memset(&se, 0, sizeof(se)); 202 sep = &se; 203 memset(tmpbuf, 0, sizeof(tmpbuf)); 204 # if GETSERVBYNAME_R_ARGS == 6 205 if (getservbyname_r(service, proto, &se, (void *)tmpbuf, sizeof(tmpbuf), 206 &sep) != 0) { 207 sep = NULL; /* LCOV_EXCL_LINE: buffer large so this never fails */ 208 } 209 # elif GETSERVBYNAME_R_ARGS == 5 210 sep = getservbyname_r(service, proto, &se, (void *)tmpbuf, sizeof(tmpbuf)); 211 # elif GETSERVBYNAME_R_ARGS == 4 212 if (getservbyname_r(service, proto, &se, (void *)tmpbuf) != 0) { 213 sep = NULL; 214 } 215 # else 216 /* Lets just hope the OS uses TLS! */ 217 sep = getservbyname(service, proto); 218 # endif 219 #else 220 /* Lets just hope the OS uses TLS! */ 221 # if (defined(NETWARE) && !defined(__NOVELL_LIBC__)) 222 sep = getservbyname(service, (char *)proto); 223 # else 224 sep = getservbyname(service, proto); 225 # endif 226 #endif 227 return (sep ? ntohs((unsigned short)sep->s_port) : 0); 228 } 229 return 0; 230 } 231 232 /* If the name looks like an IP address or an error occurred, 233 * fake up a host entry, end the query immediately, and return true. 234 * Otherwise return false. 235 */ 236 static ares_bool_t fake_addrinfo(const char *name, unsigned short port, 237 const struct ares_addrinfo_hints *hints, 238 struct ares_addrinfo *ai, 239 ares_addrinfo_callback callback, void *arg) 240 { 241 struct ares_addrinfo_cname *cname; 242 ares_status_t status = ARES_SUCCESS; 243 ares_bool_t result = ARES_FALSE; 244 int family = hints->ai_family; 245 if (family == AF_INET || family == AF_INET6 || family == AF_UNSPEC) { 246 /* It only looks like an IP address if it's all numbers and dots. */ 247 size_t numdots = 0; 248 ares_bool_t valid = ARES_TRUE; 249 const char *p; 250 for (p = name; *p; p++) { 251 if (!ares_isdigit(*p) && *p != '.') { 252 valid = ARES_FALSE; 253 break; 254 } else if (*p == '.') { 255 numdots++; 256 } 257 } 258 259 /* if we don't have 3 dots, it is illegal 260 * (although inet_pton doesn't think so). 261 */ 262 if (numdots != 3 || !valid) { 263 result = ARES_FALSE; 264 } else { 265 struct in_addr addr4; 266 result = 267 ares_inet_pton(AF_INET, name, &addr4) < 1 ? ARES_FALSE : ARES_TRUE; 268 if (result) { 269 status = ares_append_ai_node(AF_INET, port, 0, &addr4, &ai->nodes); 270 if (status != ARES_SUCCESS) { 271 callback(arg, (int)status, 0, NULL); /* LCOV_EXCL_LINE: OutOfMemory */ 272 return ARES_TRUE; /* LCOV_EXCL_LINE: OutOfMemory */ 273 } 274 } 275 } 276 } 277 278 if (!result && (family == AF_INET6 || family == AF_UNSPEC)) { 279 struct ares_in6_addr addr6; 280 result = 281 ares_inet_pton(AF_INET6, name, &addr6) < 1 ? ARES_FALSE : ARES_TRUE; 282 if (result) { 283 status = ares_append_ai_node(AF_INET6, port, 0, &addr6, &ai->nodes); 284 if (status != ARES_SUCCESS) { 285 callback(arg, (int)status, 0, NULL); /* LCOV_EXCL_LINE: OutOfMemory */ 286 return ARES_TRUE; /* LCOV_EXCL_LINE: OutOfMemory */ 287 } 288 } 289 } 290 291 if (!result) { 292 return ARES_FALSE; 293 } 294 295 if (hints->ai_flags & ARES_AI_CANONNAME) { 296 cname = ares_append_addrinfo_cname(&ai->cnames); 297 if (!cname) { 298 /* LCOV_EXCL_START: OutOfMemory */ 299 ares_freeaddrinfo(ai); 300 callback(arg, ARES_ENOMEM, 0, NULL); 301 return ARES_TRUE; 302 /* LCOV_EXCL_STOP */ 303 } 304 305 /* Duplicate the name, to avoid a constness violation. */ 306 cname->name = ares_strdup(name); 307 if (!cname->name) { 308 ares_freeaddrinfo(ai); 309 callback(arg, ARES_ENOMEM, 0, NULL); 310 return ARES_TRUE; 311 } 312 } 313 314 ai->nodes->ai_socktype = hints->ai_socktype; 315 ai->nodes->ai_protocol = hints->ai_protocol; 316 317 callback(arg, ARES_SUCCESS, 0, ai); 318 return ARES_TRUE; 319 } 320 321 static void hquery_free(struct host_query *hquery, ares_bool_t cleanup_ai) 322 { 323 if (cleanup_ai) { 324 ares_freeaddrinfo(hquery->ai); 325 } 326 ares_strsplit_free(hquery->names, hquery->names_cnt); 327 ares_free(hquery->name); 328 ares_free(hquery->lookups); 329 ares_free(hquery); 330 } 331 332 static void end_hquery(struct host_query *hquery, ares_status_t status) 333 { 334 struct ares_addrinfo_node sentinel; 335 struct ares_addrinfo_node *next; 336 337 if (status == ARES_SUCCESS) { 338 if (!(hquery->hints.ai_flags & ARES_AI_NOSORT) && hquery->ai->nodes) { 339 sentinel.ai_next = hquery->ai->nodes; 340 ares_sortaddrinfo(hquery->channel, &sentinel); 341 hquery->ai->nodes = sentinel.ai_next; 342 } 343 next = hquery->ai->nodes; 344 345 while (next) { 346 next->ai_socktype = hquery->hints.ai_socktype; 347 next->ai_protocol = hquery->hints.ai_protocol; 348 next = next->ai_next; 349 } 350 } else { 351 /* Clean up what we have collected by so far. */ 352 ares_freeaddrinfo(hquery->ai); 353 hquery->ai = NULL; 354 } 355 356 hquery->callback(hquery->arg, (int)status, (int)hquery->timeouts, hquery->ai); 357 hquery_free(hquery, ARES_FALSE); 358 } 359 360 ares_bool_t ares_is_localhost(const char *name) 361 { 362 /* RFC6761 6.3 says : The domain "localhost." and any names falling within 363 * ".localhost." */ 364 size_t len; 365 366 if (name == NULL) { 367 return ARES_FALSE; /* LCOV_EXCL_LINE: DefensiveCoding */ 368 } 369 370 if (ares_strcaseeq(name, "localhost")) { 371 return ARES_TRUE; 372 } 373 374 len = ares_strlen(name); 375 if (len < 10 /* strlen(".localhost") */) { 376 return ARES_FALSE; 377 } 378 379 if (ares_strcaseeq(name + (len - 10 /* strlen(".localhost") */), 380 ".localhost")) { 381 return ARES_TRUE; 382 } 383 384 return ARES_FALSE; 385 } 386 387 static ares_status_t file_lookup(struct host_query *hquery) 388 { 389 const ares_hosts_entry_t *entry; 390 ares_status_t status; 391 392 /* Per RFC 7686, reject queries for ".onion" domain names with NXDOMAIN. */ 393 if (ares_is_onion_domain(hquery->name)) { 394 return ARES_ENOTFOUND; 395 } 396 397 status = ares_hosts_search_host( 398 hquery->channel, 399 (hquery->hints.ai_flags & ARES_AI_ENVHOSTS) ? ARES_TRUE : ARES_FALSE, 400 hquery->name, &entry); 401 402 if (status != ARES_SUCCESS) { 403 goto done; 404 } 405 406 status = ares_hosts_entry_to_addrinfo( 407 entry, hquery->name, hquery->hints.ai_family, hquery->port, 408 (hquery->hints.ai_flags & ARES_AI_CANONNAME) ? ARES_TRUE : ARES_FALSE, 409 hquery->ai); 410 411 if (status != ARES_SUCCESS) { 412 goto done; /* LCOV_EXCL_LINE: OutOfMemory */ 413 } 414 415 416 done: 417 /* RFC6761 section 6.3 #3 states that "Name resolution APIs and libraries 418 * SHOULD recognize localhost names as special and SHOULD always return the 419 * IP loopback address for address queries". 420 * We will also ignore ALL errors when trying to resolve localhost, such 421 * as permissions errors reading /etc/hosts or a malformed /etc/hosts. 422 * 423 * Also, just because the query itself returned success from /etc/hosts 424 * lookup doesn't mean it returned everything it needed to for all requested 425 * address families. As long as we're not on a critical out of memory 426 * condition pass it through to fill in any other address classes. */ 427 if (status != ARES_ENOMEM && ares_is_localhost(hquery->name)) { 428 return ares_addrinfo_localhost(hquery->name, hquery->port, &hquery->hints, 429 hquery->ai); 430 } 431 432 return status; 433 } 434 435 static void next_lookup(struct host_query *hquery, ares_status_t status) 436 { 437 switch (*hquery->remaining_lookups) { 438 case 'b': 439 /* RFC6761 section 6.3 #3 says "Name resolution APIs SHOULD NOT send 440 * queries for localhost names to their configured caching DNS 441 * server(s)." 442 * Otherwise, DNS lookup. */ 443 if (!ares_is_localhost(hquery->name) && next_dns_lookup(hquery)) { 444 break; 445 } 446 447 hquery->remaining_lookups++; 448 next_lookup(hquery, status); 449 break; 450 451 case 'f': 452 /* Host file lookup */ 453 if (file_lookup(hquery) == ARES_SUCCESS) { 454 end_hquery(hquery, ARES_SUCCESS); 455 break; 456 } 457 hquery->remaining_lookups++; 458 next_lookup(hquery, status); 459 break; 460 default: 461 /* No lookup left */ 462 end_hquery(hquery, status); 463 break; 464 } 465 } 466 467 static void terminate_retries(const struct host_query *hquery, 468 unsigned short qid) 469 { 470 unsigned short term_qid = 471 (qid == hquery->qid_a) ? hquery->qid_aaaa : hquery->qid_a; 472 const ares_channel_t *channel = hquery->channel; 473 ares_query_t *query = NULL; 474 475 /* No other outstanding queries, nothing to do */ 476 if (!hquery->remaining) { 477 return; 478 } 479 480 query = ares_htable_szvp_get_direct(channel->queries_by_qid, term_qid); 481 if (query == NULL) { 482 return; 483 } 484 485 query->no_retries = ARES_TRUE; 486 } 487 488 static ares_bool_t ai_has_ipv4(struct ares_addrinfo *ai) 489 { 490 struct ares_addrinfo_node *node; 491 492 for (node = ai->nodes; node != NULL; node = node->ai_next) { 493 if (node->ai_family == AF_INET) { 494 return ARES_TRUE; 495 } 496 } 497 return ARES_FALSE; 498 } 499 500 static void host_callback(void *arg, ares_status_t status, size_t timeouts, 501 const ares_dns_record_t *dnsrec) 502 { 503 struct host_query *hquery = (struct host_query *)arg; 504 ares_status_t addinfostatus = ARES_SUCCESS; 505 hquery->timeouts += timeouts; 506 hquery->remaining--; 507 508 if (status == ARES_SUCCESS) { 509 if (dnsrec == NULL) { 510 addinfostatus = ARES_EBADRESP; /* LCOV_EXCL_LINE: DefensiveCoding */ 511 } else { 512 addinfostatus = 513 ares_parse_into_addrinfo(dnsrec, ARES_TRUE, hquery->port, hquery->ai); 514 } 515 516 /* We sent out ipv4 and ipv6 requests simultaneously. If we got a 517 * successful ipv4 response, we want to go ahead and tell the ipv6 request 518 * that if it fails or times out to not try again since we have the data 519 * we need. 520 * 521 * Our initial implementation of this would terminate retries if we got any 522 * successful response (ipv4 _or_ ipv6). But we did get some user-reported 523 * issues with this that had bad system configs and odd behavior: 524 * https://github.com/alpinelinux/docker-alpine/issues/366 525 * 526 * Essentially the ipv6 query succeeded but the ipv4 query failed or timed 527 * out, and so we only returned the ipv6 address, but the host couldn't 528 * use ipv6. If we continued to allow ipv4 retries it would have found a 529 * server that worked and returned both address classes (this is clearly 530 * unexpected behavior). 531 * 532 * At some point down the road if ipv6 actually becomes required and 533 * reliable we can drop this ipv4 check. 534 */ 535 if (addinfostatus == ARES_SUCCESS && ai_has_ipv4(hquery->ai)) { 536 terminate_retries(hquery, ares_dns_record_get_id(dnsrec)); 537 } 538 } 539 540 if (!hquery->remaining) { 541 if (status == ARES_EDESTRUCTION || status == ARES_ECANCELLED) { 542 /* must make sure we don't do next_lookup() on destroy or cancel, 543 * and return the appropriate status. We won't return a partial 544 * result in this case. */ 545 end_hquery(hquery, status); 546 } else if (addinfostatus != ARES_SUCCESS && addinfostatus != ARES_ENODATA) { 547 /* error in parsing result e.g. no memory */ 548 if (addinfostatus == ARES_EBADRESP && hquery->ai->nodes) { 549 /* We got a bad response from server, but at least one query 550 * ended with ARES_SUCCESS */ 551 end_hquery(hquery, ARES_SUCCESS); 552 } else { 553 end_hquery(hquery, addinfostatus); 554 } 555 } else if (hquery->ai->nodes) { 556 /* at least one query ended with ARES_SUCCESS */ 557 end_hquery(hquery, ARES_SUCCESS); 558 } else if (status == ARES_ENOTFOUND || status == ARES_ENODATA || 559 addinfostatus == ARES_ENODATA) { 560 if (status == ARES_ENODATA || addinfostatus == ARES_ENODATA) { 561 hquery->nodata_cnt++; 562 } 563 next_lookup(hquery, hquery->nodata_cnt ? ARES_ENODATA : status); 564 } else if ((status == ARES_ESERVFAIL || status == ARES_EREFUSED) && 565 ares_name_label_cnt(hquery->names[hquery->next_name_idx - 1]) == 566 1) { 567 /* Issue #852, systemd-resolved may return SERVFAIL or REFUSED on a 568 * single label domain name. */ 569 next_lookup(hquery, hquery->nodata_cnt ? ARES_ENODATA : status); 570 } else { 571 end_hquery(hquery, status); 572 } 573 } 574 575 /* at this point we keep on waiting for the next query to finish */ 576 } 577 578 static void ares_getaddrinfo_int(ares_channel_t *channel, const char *name, 579 const char *service, 580 const struct ares_addrinfo_hints *hints, 581 ares_addrinfo_callback callback, void *arg) 582 { 583 struct host_query *hquery; 584 unsigned short port = 0; 585 int family; 586 struct ares_addrinfo *ai; 587 ares_status_t status; 588 589 if (!hints) { 590 hints = &default_hints; 591 } 592 593 family = hints->ai_family; 594 595 /* Right now we only know how to look up Internet addresses 596 and unspec means try both basically. */ 597 if (family != AF_INET && family != AF_INET6 && family != AF_UNSPEC) { 598 callback(arg, ARES_ENOTIMP, 0, NULL); 599 return; 600 } 601 602 if (ares_is_onion_domain(name)) { 603 callback(arg, ARES_ENOTFOUND, 0, NULL); 604 return; 605 } 606 607 if (service) { 608 if (hints->ai_flags & ARES_AI_NUMERICSERV) { 609 unsigned long val; 610 errno = 0; 611 val = strtoul(service, NULL, 0); 612 if ((val == 0 && errno != 0) || val > 65535) { 613 callback(arg, ARES_ESERVICE, 0, NULL); 614 return; 615 } 616 port = (unsigned short)val; 617 } else { 618 port = lookup_service(service, 0); 619 if (!port) { 620 unsigned long val; 621 errno = 0; 622 val = strtoul(service, NULL, 0); 623 if ((val == 0 && errno != 0) || val > 65535) { 624 callback(arg, ARES_ESERVICE, 0, NULL); 625 return; 626 } 627 port = (unsigned short)val; 628 } 629 } 630 } 631 632 ai = ares_malloc_zero(sizeof(*ai)); 633 if (!ai) { 634 callback(arg, ARES_ENOMEM, 0, NULL); 635 return; 636 } 637 638 if (fake_addrinfo(name, port, hints, ai, callback, arg)) { 639 return; 640 } 641 642 /* Allocate and fill in the host query structure. */ 643 hquery = ares_malloc_zero(sizeof(*hquery)); 644 if (!hquery) { 645 ares_freeaddrinfo(ai); 646 callback(arg, ARES_ENOMEM, 0, NULL); 647 return; 648 } 649 650 hquery->port = port; 651 hquery->channel = channel; 652 hquery->hints = *hints; 653 hquery->sent_family = -1; /* nothing is sent yet */ 654 hquery->callback = callback; 655 hquery->arg = arg; 656 hquery->ai = ai; 657 hquery->name = ares_strdup(name); 658 if (hquery->name == NULL) { 659 hquery_free(hquery, ARES_TRUE); 660 callback(arg, ARES_ENOMEM, 0, NULL); 661 return; 662 } 663 664 status = 665 ares_search_name_list(channel, name, &hquery->names, &hquery->names_cnt); 666 if (status != ARES_SUCCESS) { 667 hquery_free(hquery, ARES_TRUE); 668 callback(arg, (int)status, 0, NULL); 669 return; 670 } 671 hquery->next_name_idx = 0; 672 673 674 hquery->lookups = ares_strdup(channel->lookups); 675 if (hquery->lookups == NULL) { 676 hquery_free(hquery, ARES_TRUE); 677 callback(arg, ARES_ENOMEM, 0, NULL); 678 return; 679 } 680 hquery->remaining_lookups = hquery->lookups; 681 682 /* Start performing lookups according to channel->lookups. */ 683 next_lookup(hquery, ARES_ECONNREFUSED /* initial error code */); 684 } 685 686 void ares_getaddrinfo(ares_channel_t *channel, const char *name, 687 const char *service, 688 const struct ares_addrinfo_hints *hints, 689 ares_addrinfo_callback callback, void *arg) 690 { 691 if (channel == NULL) { 692 return; 693 } 694 ares_channel_lock(channel); 695 ares_getaddrinfo_int(channel, name, service, hints, callback, arg); 696 ares_channel_unlock(channel); 697 } 698 699 static ares_bool_t next_dns_lookup(struct host_query *hquery) 700 { 701 const char *name = NULL; 702 703 if (hquery->next_name_idx >= hquery->names_cnt) { 704 return ARES_FALSE; 705 } 706 707 name = hquery->names[hquery->next_name_idx++]; 708 709 /* NOTE: hquery may be invalidated during the call to ares_query_qid(), 710 * so should not be referenced after this point */ 711 switch (hquery->hints.ai_family) { 712 case AF_INET: 713 hquery->remaining += 1; 714 ares_query_nolock(hquery->channel, name, ARES_CLASS_IN, ARES_REC_TYPE_A, 715 host_callback, hquery, &hquery->qid_a); 716 break; 717 case AF_INET6: 718 hquery->remaining += 1; 719 ares_query_nolock(hquery->channel, name, ARES_CLASS_IN, 720 ARES_REC_TYPE_AAAA, host_callback, hquery, 721 &hquery->qid_aaaa); 722 break; 723 case AF_UNSPEC: 724 hquery->remaining += 2; 725 ares_query_nolock(hquery->channel, name, ARES_CLASS_IN, ARES_REC_TYPE_A, 726 host_callback, hquery, &hquery->qid_a); 727 ares_query_nolock(hquery->channel, name, ARES_CLASS_IN, 728 ARES_REC_TYPE_AAAA, host_callback, hquery, 729 &hquery->qid_aaaa); 730 break; 731 default: 732 break; 733 } 734 735 return ARES_TRUE; 736 }