• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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