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_setup.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.h"
60 #include "ares_private.h"
61 #include "ares_dns.h"
62
63 #ifdef WATT32
64 # undef WIN32
65 #endif
66 #ifdef WIN32
67 # include "ares_platform.h"
68 #endif
69
70 struct host_query {
71 ares_channel_t *channel;
72 char *name;
73 unsigned short port; /* in host order */
74 ares_addrinfo_callback callback;
75 void *arg;
76 struct ares_addrinfo_hints hints;
77 int sent_family; /* this family is what was is being used */
78 size_t timeouts; /* number of timeouts we saw for this request */
79 char *lookups; /* Duplicate memory from channel because of ares_reinit() */
80 const char *remaining_lookups; /* types of lookup we need to perform ("fb" by
81 default, file and dns respectively) */
82 char **domains; /* duplicate from channel for ares_reinit() safety */
83 size_t ndomains;
84 struct ares_addrinfo *ai; /* store results between lookups */
85 unsigned short qid_a; /* qid for A request */
86 unsigned short qid_aaaa; /* qid for AAAA request */
87 size_t remaining; /* number of DNS answers waiting for */
88 ares_ssize_t next_domain; /* next search domain to try */
89 size_t
90 nodata_cnt; /* Track nodata responses to possibly override final result */
91 };
92
93 static const struct ares_addrinfo_hints default_hints = {
94 0, /* ai_flags */
95 AF_UNSPEC, /* ai_family */
96 0, /* ai_socktype */
97 0, /* ai_protocol */
98 };
99
100 /* forward declarations */
101 static void host_callback(void *arg, int status, int timeouts,
102 unsigned char *abuf, int alen);
103 static ares_bool_t as_is_first(const struct host_query *hquery);
104 static ares_bool_t as_is_only(const struct host_query *hquery);
105 static ares_bool_t next_dns_lookup(struct host_query *hquery);
106
107 struct ares_addrinfo_cname *
ares__append_addrinfo_cname(struct ares_addrinfo_cname ** head)108 ares__append_addrinfo_cname(struct ares_addrinfo_cname **head)
109 {
110 struct ares_addrinfo_cname *tail = ares_malloc_zero(sizeof(*tail));
111 struct ares_addrinfo_cname *last = *head;
112
113 if (tail == NULL) {
114 return NULL;
115 }
116
117 if (!last) {
118 *head = tail;
119 return tail;
120 }
121
122 while (last->next) {
123 last = last->next;
124 }
125
126 last->next = tail;
127 return tail;
128 }
129
ares__addrinfo_cat_cnames(struct ares_addrinfo_cname ** head,struct ares_addrinfo_cname * tail)130 void ares__addrinfo_cat_cnames(struct ares_addrinfo_cname **head,
131 struct ares_addrinfo_cname *tail)
132 {
133 struct ares_addrinfo_cname *last = *head;
134 if (!last) {
135 *head = tail;
136 return;
137 }
138
139 while (last->next) {
140 last = last->next;
141 }
142
143 last->next = tail;
144 }
145
146 /* Allocate new addrinfo and append to the tail. */
147 struct ares_addrinfo_node *
ares__append_addrinfo_node(struct ares_addrinfo_node ** head)148 ares__append_addrinfo_node(struct ares_addrinfo_node **head)
149 {
150 struct ares_addrinfo_node *tail = ares_malloc_zero(sizeof(*tail));
151 struct ares_addrinfo_node *last = *head;
152
153 if (tail == NULL) {
154 return NULL;
155 }
156
157 if (!last) {
158 *head = tail;
159 return tail;
160 }
161
162 while (last->ai_next) {
163 last = last->ai_next;
164 }
165
166 last->ai_next = tail;
167 return tail;
168 }
169
ares__addrinfo_cat_nodes(struct ares_addrinfo_node ** head,struct ares_addrinfo_node * tail)170 void ares__addrinfo_cat_nodes(struct ares_addrinfo_node **head,
171 struct ares_addrinfo_node *tail)
172 {
173 struct ares_addrinfo_node *last = *head;
174 if (!last) {
175 *head = tail;
176 return;
177 }
178
179 while (last->ai_next) {
180 last = last->ai_next;
181 }
182
183 last->ai_next = tail;
184 }
185
186 /* Resolve service name into port number given in host byte order.
187 * If not resolved, return 0.
188 */
lookup_service(const char * service,int flags)189 static unsigned short lookup_service(const char *service, int flags)
190 {
191 const char *proto;
192 struct servent *sep;
193 #ifdef HAVE_GETSERVBYNAME_R
194 struct servent se;
195 char tmpbuf[4096];
196 #endif
197
198 if (service) {
199 if (flags & ARES_NI_UDP) {
200 proto = "udp";
201 } else if (flags & ARES_NI_SCTP) {
202 proto = "sctp";
203 } else if (flags & ARES_NI_DCCP) {
204 proto = "dccp";
205 } else {
206 proto = "tcp";
207 }
208 #ifdef HAVE_GETSERVBYNAME_R
209 memset(&se, 0, sizeof(se));
210 sep = &se;
211 memset(tmpbuf, 0, sizeof(tmpbuf));
212 # if GETSERVBYNAME_R_ARGS == 6
213 if (getservbyname_r(service, proto, &se, (void *)tmpbuf, sizeof(tmpbuf),
214 &sep) != 0) {
215 sep = NULL; /* LCOV_EXCL_LINE: buffer large so this never fails */
216 }
217 # elif GETSERVBYNAME_R_ARGS == 5
218 sep = getservbyname_r(service, proto, &se, (void *)tmpbuf, sizeof(tmpbuf));
219 # elif GETSERVBYNAME_R_ARGS == 4
220 if (getservbyname_r(service, proto, &se, (void *)tmpbuf) != 0) {
221 sep = NULL;
222 }
223 # else
224 /* Lets just hope the OS uses TLS! */
225 sep = getservbyname(service, proto);
226 # endif
227 #else
228 /* Lets just hope the OS uses TLS! */
229 # if (defined(NETWARE) && !defined(__NOVELL_LIBC__))
230 sep = getservbyname(service, (char *)proto);
231 # else
232 sep = getservbyname(service, proto);
233 # endif
234 #endif
235 return (sep ? ntohs((unsigned short)sep->s_port) : 0);
236 }
237 return 0;
238 }
239
240 /* If the name looks like an IP address or an error occurred,
241 * fake up a host entry, end the query immediately, and return true.
242 * Otherwise return false.
243 */
fake_addrinfo(const char * name,unsigned short port,const struct ares_addrinfo_hints * hints,struct ares_addrinfo * ai,ares_addrinfo_callback callback,void * arg)244 static ares_bool_t fake_addrinfo(const char *name, unsigned short port,
245 const struct ares_addrinfo_hints *hints,
246 struct ares_addrinfo *ai,
247 ares_addrinfo_callback callback, void *arg)
248 {
249 struct ares_addrinfo_cname *cname;
250 ares_status_t status = ARES_SUCCESS;
251 ares_bool_t result = ARES_FALSE;
252 int family = hints->ai_family;
253 if (family == AF_INET || family == AF_INET6 || family == AF_UNSPEC) {
254 /* It only looks like an IP address if it's all numbers and dots. */
255 size_t numdots = 0;
256 ares_bool_t valid = ARES_TRUE;
257 const char *p;
258 for (p = name; *p; p++) {
259 if (!ISDIGIT(*p) && *p != '.') {
260 valid = ARES_FALSE;
261 break;
262 } else if (*p == '.') {
263 numdots++;
264 }
265 }
266
267 /* if we don't have 3 dots, it is illegal
268 * (although inet_pton doesn't think so).
269 */
270 if (numdots != 3 || !valid) {
271 result = ARES_FALSE;
272 } else {
273 struct in_addr addr4;
274 result =
275 ares_inet_pton(AF_INET, name, &addr4) < 1 ? ARES_FALSE : ARES_TRUE;
276 if (result) {
277 status = ares_append_ai_node(AF_INET, port, 0, &addr4, &ai->nodes);
278 if (status != ARES_SUCCESS) {
279 callback(arg, (int)status, 0, NULL);
280 return ARES_TRUE;
281 }
282 }
283 }
284 }
285
286 if (!result && (family == AF_INET6 || family == AF_UNSPEC)) {
287 struct ares_in6_addr addr6;
288 result =
289 ares_inet_pton(AF_INET6, name, &addr6) < 1 ? ARES_FALSE : ARES_TRUE;
290 if (result) {
291 status = ares_append_ai_node(AF_INET6, port, 0, &addr6, &ai->nodes);
292 if (status != ARES_SUCCESS) {
293 callback(arg, (int)status, 0, NULL);
294 return ARES_TRUE;
295 }
296 }
297 }
298
299 if (!result) {
300 return ARES_FALSE;
301 }
302
303 if (hints->ai_flags & ARES_AI_CANONNAME) {
304 cname = ares__append_addrinfo_cname(&ai->cnames);
305 if (!cname) {
306 ares_freeaddrinfo(ai);
307 callback(arg, ARES_ENOMEM, 0, NULL);
308 return ARES_TRUE;
309 }
310
311 /* Duplicate the name, to avoid a constness violation. */
312 cname->name = ares_strdup(name);
313 if (!cname->name) {
314 ares_freeaddrinfo(ai);
315 callback(arg, ARES_ENOMEM, 0, NULL);
316 return ARES_TRUE;
317 }
318 }
319
320 ai->nodes->ai_socktype = hints->ai_socktype;
321 ai->nodes->ai_protocol = hints->ai_protocol;
322
323 callback(arg, ARES_SUCCESS, 0, ai);
324 return ARES_TRUE;
325 }
326
end_hquery(struct host_query * hquery,ares_status_t status)327 static void end_hquery(struct host_query *hquery, ares_status_t status)
328 {
329 struct ares_addrinfo_node sentinel;
330 struct ares_addrinfo_node *next;
331
332 if (status == ARES_SUCCESS) {
333 if (!(hquery->hints.ai_flags & ARES_AI_NOSORT) && hquery->ai->nodes) {
334 sentinel.ai_next = hquery->ai->nodes;
335 ares__sortaddrinfo(hquery->channel, &sentinel);
336 hquery->ai->nodes = sentinel.ai_next;
337 }
338 next = hquery->ai->nodes;
339
340 while (next) {
341 next->ai_socktype = hquery->hints.ai_socktype;
342 next->ai_protocol = hquery->hints.ai_protocol;
343 next = next->ai_next;
344 }
345 } else {
346 /* Clean up what we have collected by so far. */
347 ares_freeaddrinfo(hquery->ai);
348 hquery->ai = NULL;
349 }
350
351 hquery->callback(hquery->arg, (int)status, (int)hquery->timeouts, hquery->ai);
352 ares__strsplit_free(hquery->domains, hquery->ndomains);
353 ares_free(hquery->lookups);
354 ares_free(hquery->name);
355 ares_free(hquery);
356 }
357
ares__is_localhost(const char * name)358 ares_bool_t ares__is_localhost(const char *name)
359 {
360 /* RFC6761 6.3 says : The domain "localhost." and any names falling within
361 * ".localhost." */
362 size_t len;
363
364 if (name == NULL) {
365 return ARES_FALSE;
366 }
367
368 if (strcmp(name, "localhost") == 0) {
369 return ARES_TRUE;
370 }
371
372 len = ares_strlen(name);
373 if (len < 10 /* strlen(".localhost") */) {
374 return ARES_FALSE;
375 }
376
377 if (strcmp(name + (len - 10 /* strlen(".localhost") */), ".localhost") == 0) {
378 return ARES_TRUE;
379 }
380
381 return ARES_FALSE;
382 }
383
file_lookup(struct host_query * hquery)384 static ares_status_t file_lookup(struct host_query *hquery)
385 {
386 const ares_hosts_entry_t *entry;
387 ares_status_t status;
388
389 /* Per RFC 7686, reject queries for ".onion" domain names with NXDOMAIN. */
390 if (ares__is_onion_domain(hquery->name)) {
391 return ARES_ENOTFOUND;
392 }
393
394 status = ares__hosts_search_host(
395 hquery->channel,
396 (hquery->hints.ai_flags & ARES_AI_ENVHOSTS) ? ARES_TRUE : ARES_FALSE,
397 hquery->name, &entry);
398
399 if (status != ARES_SUCCESS) {
400 goto done;
401 }
402
403 status = ares__hosts_entry_to_addrinfo(
404 entry, hquery->name, hquery->hints.ai_family, hquery->port,
405 (hquery->hints.ai_flags & ARES_AI_CANONNAME) ? ARES_TRUE : ARES_FALSE,
406 hquery->ai);
407
408 if (status != ARES_SUCCESS) {
409 goto done;
410 }
411
412
413 done:
414 /* RFC6761 section 6.3 #3 states that "Name resolution APIs and libraries
415 * SHOULD recognize localhost names as special and SHOULD always return the
416 * IP loopback address for address queries".
417 * We will also ignore ALL errors when trying to resolve localhost, such
418 * as permissions errors reading /etc/hosts or a malformed /etc/hosts */
419 if (status != ARES_SUCCESS && status != ARES_ENOMEM &&
420 ares__is_localhost(hquery->name)) {
421 return ares__addrinfo_localhost(hquery->name, hquery->port, &hquery->hints,
422 hquery->ai);
423 }
424
425 return status;
426 }
427
next_lookup(struct host_query * hquery,ares_status_t status)428 static void next_lookup(struct host_query *hquery, ares_status_t status)
429 {
430 switch (*hquery->remaining_lookups) {
431 case 'b':
432 /* RFC6761 section 6.3 #3 says "Name resolution APIs SHOULD NOT send
433 * queries for localhost names to their configured caching DNS
434 * server(s)."
435 * Otherwise, DNS lookup. */
436 if (!ares__is_localhost(hquery->name) && next_dns_lookup(hquery)) {
437 break;
438 }
439
440 hquery->remaining_lookups++;
441 next_lookup(hquery, status);
442 break;
443
444 case 'f':
445 /* Host file lookup */
446 if (file_lookup(hquery) == ARES_SUCCESS) {
447 end_hquery(hquery, ARES_SUCCESS);
448 break;
449 }
450 hquery->remaining_lookups++;
451 next_lookup(hquery, status);
452 break;
453 default:
454 /* No lookup left */
455 end_hquery(hquery, status);
456 break;
457 }
458 }
459
terminate_retries(const struct host_query * hquery,unsigned short qid)460 static void terminate_retries(const struct host_query *hquery,
461 unsigned short qid)
462 {
463 unsigned short term_qid =
464 (qid == hquery->qid_a) ? hquery->qid_aaaa : hquery->qid_a;
465 const ares_channel_t *channel = hquery->channel;
466 struct query *query = NULL;
467
468 /* No other outstanding queries, nothing to do */
469 if (!hquery->remaining) {
470 return;
471 }
472
473 query = ares__htable_szvp_get_direct(channel->queries_by_qid, term_qid);
474 if (query == NULL) {
475 return;
476 }
477
478 query->no_retries = ARES_TRUE;
479 }
480
host_callback(void * arg,int status,int timeouts,unsigned char * abuf,int alen)481 static void host_callback(void *arg, int status, int timeouts,
482 unsigned char *abuf, int alen)
483 {
484 struct host_query *hquery = (struct host_query *)arg;
485 ares_status_t addinfostatus = ARES_SUCCESS;
486 unsigned short qid = 0;
487 hquery->timeouts += (size_t)timeouts;
488 hquery->remaining--;
489
490 if (status == ARES_SUCCESS) {
491 if (alen < 0) {
492 addinfostatus = ARES_EBADRESP;
493 } else {
494 addinfostatus = ares__parse_into_addrinfo(abuf, (size_t)alen, ARES_TRUE,
495 hquery->port, hquery->ai);
496 }
497 if (addinfostatus == ARES_SUCCESS && alen >= HFIXEDSZ) {
498 qid = DNS_HEADER_QID(abuf); /* Converts to host byte order */
499 terminate_retries(hquery, qid);
500 }
501 }
502
503 if (!hquery->remaining) {
504 if (status == ARES_EDESTRUCTION || status == ARES_ECANCELLED) {
505 /* must make sure we don't do next_lookup() on destroy or cancel,
506 * and return the appropriate status. We won't return a partial
507 * result in this case. */
508 end_hquery(hquery, (ares_status_t)status);
509 } else if (addinfostatus != ARES_SUCCESS && addinfostatus != ARES_ENODATA) {
510 /* error in parsing result e.g. no memory */
511 if (addinfostatus == ARES_EBADRESP && hquery->ai->nodes) {
512 /* We got a bad response from server, but at least one query
513 * ended with ARES_SUCCESS */
514 end_hquery(hquery, ARES_SUCCESS);
515 } else {
516 end_hquery(hquery, addinfostatus);
517 }
518 } else if (hquery->ai->nodes) {
519 /* at least one query ended with ARES_SUCCESS */
520 end_hquery(hquery, ARES_SUCCESS);
521 } else if (status == ARES_ENOTFOUND || status == ARES_ENODATA ||
522 addinfostatus == ARES_ENODATA) {
523 if (status == ARES_ENODATA || addinfostatus == ARES_ENODATA) {
524 hquery->nodata_cnt++;
525 }
526 next_lookup(hquery,
527 hquery->nodata_cnt ? ARES_ENODATA : (ares_status_t)status);
528 } else {
529 end_hquery(hquery, (ares_status_t)status);
530 }
531 }
532
533 /* at this point we keep on waiting for the next query to finish */
534 }
535
ares_getaddrinfo_int(ares_channel_t * channel,const char * name,const char * service,const struct ares_addrinfo_hints * hints,ares_addrinfo_callback callback,void * arg)536 static void ares_getaddrinfo_int(ares_channel_t *channel, const char *name,
537 const char *service,
538 const struct ares_addrinfo_hints *hints,
539 ares_addrinfo_callback callback, void *arg)
540 {
541 struct host_query *hquery;
542 unsigned short port = 0;
543 int family;
544 struct ares_addrinfo *ai;
545 char *alias_name = NULL;
546 ares_status_t status;
547
548 if (!hints) {
549 hints = &default_hints;
550 }
551
552 family = hints->ai_family;
553
554 /* Right now we only know how to look up Internet addresses
555 and unspec means try both basically. */
556 if (family != AF_INET && family != AF_INET6 && family != AF_UNSPEC) {
557 callback(arg, ARES_ENOTIMP, 0, NULL);
558 return;
559 }
560
561 if (ares__is_onion_domain(name)) {
562 callback(arg, ARES_ENOTFOUND, 0, NULL);
563 return;
564 }
565
566 /* perform HOSTALIAS resolution (technically this function does some other
567 * things we are going to ignore) */
568 status = ares__single_domain(channel, name, &alias_name);
569 if (status != ARES_SUCCESS) {
570 callback(arg, (int)status, 0, NULL);
571 return;
572 }
573
574 if (alias_name) {
575 name = alias_name;
576 }
577
578 if (service) {
579 if (hints->ai_flags & ARES_AI_NUMERICSERV) {
580 unsigned long val;
581 errno = 0;
582 val = strtoul(service, NULL, 0);
583 if ((val == 0 && errno != 0) || val > 65535) {
584 ares_free(alias_name);
585 callback(arg, ARES_ESERVICE, 0, NULL);
586 return;
587 }
588 port = (unsigned short)val;
589 } else {
590 port = lookup_service(service, 0);
591 if (!port) {
592 unsigned long val;
593 errno = 0;
594 val = strtoul(service, NULL, 0);
595 if ((val == 0 && errno != 0) || val > 65535) {
596 ares_free(alias_name);
597 callback(arg, ARES_ESERVICE, 0, NULL);
598 return;
599 }
600 port = (unsigned short)val;
601 }
602 }
603 }
604
605 ai = ares_malloc_zero(sizeof(*ai));
606 if (!ai) {
607 ares_free(alias_name);
608 callback(arg, ARES_ENOMEM, 0, NULL);
609 return;
610 }
611
612 if (fake_addrinfo(name, port, hints, ai, callback, arg)) {
613 ares_free(alias_name);
614 return;
615 }
616
617 /* Allocate and fill in the host query structure. */
618 hquery = ares_malloc_zero(sizeof(*hquery));
619 if (!hquery) {
620 ares_free(alias_name);
621 ares_freeaddrinfo(ai);
622 callback(arg, ARES_ENOMEM, 0, NULL);
623 return;
624 }
625 memset(hquery, 0, sizeof(*hquery));
626 hquery->name = ares_strdup(name);
627 ares_free(alias_name);
628 if (!hquery->name) {
629 ares_free(hquery);
630 ares_freeaddrinfo(ai);
631 callback(arg, ARES_ENOMEM, 0, NULL);
632 return;
633 }
634 hquery->lookups = ares_strdup(channel->lookups);
635 if (!hquery->lookups) {
636 ares_free(hquery->name);
637 ares_free(hquery);
638 ares_freeaddrinfo(ai);
639 callback(arg, ARES_ENOMEM, 0, NULL);
640 return;
641 }
642
643 if (channel->ndomains) {
644 /* Duplicate for ares_reinit() safety */
645 hquery->domains =
646 ares__strsplit_duplicate(channel->domains, channel->ndomains);
647 if (hquery->domains == NULL) {
648 ares_free(hquery->lookups);
649 ares_free(hquery->name);
650 ares_free(hquery);
651 ares_freeaddrinfo(ai);
652 callback(arg, ARES_ENOMEM, 0, NULL);
653 return;
654 }
655 hquery->ndomains = channel->ndomains;
656 }
657
658 hquery->port = port;
659 hquery->channel = channel;
660 hquery->hints = *hints;
661 hquery->sent_family = -1; /* nothing is sent yet */
662 hquery->callback = callback;
663 hquery->arg = arg;
664 hquery->remaining_lookups = hquery->lookups;
665 hquery->ai = ai;
666 hquery->next_domain = -1;
667
668 /* Start performing lookups according to channel->lookups. */
669 next_lookup(hquery, ARES_ECONNREFUSED /* initial error code */);
670 }
671
ares_getaddrinfo(ares_channel_t * channel,const char * name,const char * service,const struct ares_addrinfo_hints * hints,ares_addrinfo_callback callback,void * arg)672 void ares_getaddrinfo(ares_channel_t *channel, const char *name,
673 const char *service,
674 const struct ares_addrinfo_hints *hints,
675 ares_addrinfo_callback callback, void *arg)
676 {
677 if (channel == NULL) {
678 return;
679 }
680 ares__channel_lock(channel);
681 ares_getaddrinfo_int(channel, name, service, hints, callback, arg);
682 ares__channel_unlock(channel);
683 }
684
next_dns_lookup(struct host_query * hquery)685 static ares_bool_t next_dns_lookup(struct host_query *hquery)
686 {
687 char *s = NULL;
688 ares_bool_t is_s_allocated = ARES_FALSE;
689 ares_status_t status;
690
691 /* if next_domain == -1 and as_is_first is true, try hquery->name */
692 if (hquery->next_domain == -1) {
693 if (as_is_first(hquery)) {
694 s = hquery->name;
695 }
696 hquery->next_domain = 0;
697 }
698
699 /* if as_is_first is false, try hquery->name at last */
700 if (!s && (size_t)hquery->next_domain == hquery->ndomains) {
701 if (!as_is_first(hquery)) {
702 s = hquery->name;
703 }
704 hquery->next_domain++;
705 }
706
707 if (!s && (size_t)hquery->next_domain < hquery->ndomains &&
708 !as_is_only(hquery)) {
709 status = ares__cat_domain(hquery->name,
710 hquery->domains[hquery->next_domain++], &s);
711 if (status == ARES_SUCCESS) {
712 is_s_allocated = ARES_TRUE;
713 }
714 }
715
716 if (s) {
717 /* NOTE: hquery may be invalidated during the call to ares_query_qid(),
718 * so should not be referenced after this point */
719 switch (hquery->hints.ai_family) {
720 case AF_INET:
721 hquery->remaining += 1;
722 ares_query_qid(hquery->channel, s, C_IN, T_A, host_callback, hquery,
723 &hquery->qid_a);
724 break;
725 case AF_INET6:
726 hquery->remaining += 1;
727 ares_query_qid(hquery->channel, s, C_IN, T_AAAA, host_callback, hquery,
728 &hquery->qid_aaaa);
729 break;
730 case AF_UNSPEC:
731 hquery->remaining += 2;
732 ares_query_qid(hquery->channel, s, C_IN, T_A, host_callback, hquery,
733 &hquery->qid_a);
734 ares_query_qid(hquery->channel, s, C_IN, T_AAAA, host_callback, hquery,
735 &hquery->qid_aaaa);
736 break;
737 default:
738 break;
739 }
740 if (is_s_allocated) {
741 ares_free(s);
742 }
743 return ARES_TRUE;
744 } else {
745 assert(!hquery->ai->nodes);
746 return ARES_FALSE;
747 }
748 }
749
as_is_first(const struct host_query * hquery)750 static ares_bool_t as_is_first(const struct host_query *hquery)
751 {
752 const char *p;
753 size_t ndots = 0;
754 for (p = hquery->name; p && *p; p++) {
755 if (*p == '.') {
756 ndots++;
757 }
758 }
759 if (as_is_only(hquery)) {
760 /* prevent ARES_EBADNAME for valid FQDN, where ndots < channel->ndots */
761 return ARES_TRUE;
762 }
763 return ndots >= hquery->channel->ndots ? ARES_TRUE : ARES_FALSE;
764 }
765
as_is_only(const struct host_query * hquery)766 static ares_bool_t as_is_only(const struct host_query *hquery)
767 {
768 size_t nname = ares_strlen(hquery->name);
769 if (hquery->channel->flags & ARES_FLAG_NOSEARCH) {
770 return ARES_TRUE;
771 }
772 if (hquery->name != NULL && nname && hquery->name[nname - 1] == '.') {
773 return ARES_TRUE;
774 }
775 return ARES_FALSE;
776 }
777