• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 
2 /* Copyright 1998, 2011, 2013 by the Massachusetts Institute of Technology.
3  * Copyright (C) 2017 - 2018 by Christian Ammer
4  * Copyright (C) 2019 by Andrew Selivanov
5  *
6  * Permission to use, copy, modify, and distribute this
7  * software and its documentation for any purpose and without
8  * fee is hereby granted, provided that the above copyright
9  * notice appear in all copies and that both that copyright
10  * notice and this permission notice appear in supporting
11  * documentation, and that the name of M.I.T. not be used in
12  * advertising or publicity pertaining to distribution of the
13  * software without specific, written prior permission.
14  * M.I.T. makes no representations about the suitability of
15  * this software for any purpose.  It is provided "as is"
16  * without express or implied warranty.
17  */
18 
19 #include "ares_setup.h"
20 
21 #ifdef HAVE_GETSERVBYNAME_R
22 #  if !defined(GETSERVBYNAME_R_ARGS) || \
23      (GETSERVBYNAME_R_ARGS < 4) || (GETSERVBYNAME_R_ARGS > 6)
24 #    error "you MUST specifiy a valid number of arguments for getservbyname_r"
25 #  endif
26 #endif
27 
28 #ifdef HAVE_NETINET_IN_H
29 #  include <netinet/in.h>
30 #endif
31 #ifdef HAVE_NETDB_H
32 #  include <netdb.h>
33 #endif
34 #ifdef HAVE_ARPA_INET_H
35 #  include <arpa/inet.h>
36 #endif
37 
38 #include "ares_nameser.h"
39 
40 #ifdef HAVE_STRINGS_H
41 #include <strings.h>
42 #endif
43 #include <assert.h>
44 
45 #ifdef HAVE_LIMITS_H
46 #include <limits.h>
47 #endif
48 
49 #include "ares.h"
50 #include "bitncmp.h"
51 #include "ares_private.h"
52 
53 #ifdef WATT32
54 #undef WIN32
55 #endif
56 #ifdef WIN32
57 #  include "ares_platform.h"
58 #endif
59 
60 struct host_query
61 {
62   ares_channel 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   int timeouts;    /* number of timeouts we saw for this request */
70   const char *remaining_lookups; /* types of lookup we need to perform ("fb" by
71                                     default, file and dns respectively) */
72   struct ares_addrinfo *ai;      /* store results between lookups */
73   int remaining;   /* number of DNS answers waiting for */
74   int next_domain; /* next search domain to try */
75   int nodata_cnt; /* Track nodata responses to possibly override final result */
76 };
77 
78 static const struct ares_addrinfo_hints default_hints = {
79   0,         /* ai_flags */
80   AF_UNSPEC, /* ai_family */
81   0,         /* ai_socktype */
82   0,         /* ai_protocol */
83 };
84 
85 static const struct ares_addrinfo_cname empty_addrinfo_cname = {
86   INT_MAX, /* ttl */
87   NULL,    /* alias */
88   NULL,    /* name */
89   NULL,    /* next */
90 };
91 
92 static const struct ares_addrinfo_node empty_addrinfo_node = {
93   0,    /* ai_ttl */
94   0,    /* ai_flags */
95   0,    /* ai_family */
96   0,    /* ai_socktype */
97   0,    /* ai_protocol */
98   0,    /* ai_addrlen */
99   NULL, /* ai_addr */
100   NULL  /* ai_next */
101 };
102 
103 static const struct ares_addrinfo empty_addrinfo = {
104   NULL, /* cnames */
105   NULL, /* nodes */
106   NULL  /* name */
107 };
108 
109 /* forward declarations */
110 static void host_callback(void *arg, int status, int timeouts,
111                           unsigned char *abuf, int alen);
112 static int as_is_first(const struct host_query *hquery);
113 static int as_is_only(const struct host_query* hquery);
114 static int next_dns_lookup(struct host_query *hquery);
115 
ares__malloc_addrinfo_cname()116 struct ares_addrinfo_cname *ares__malloc_addrinfo_cname()
117 {
118   struct ares_addrinfo_cname *cname = ares_malloc(sizeof(struct ares_addrinfo_cname));
119   if (!cname)
120     return NULL;
121 
122   *cname = empty_addrinfo_cname;
123   return cname;
124 }
125 
ares__append_addrinfo_cname(struct ares_addrinfo_cname ** head)126 struct ares_addrinfo_cname *ares__append_addrinfo_cname(struct ares_addrinfo_cname **head)
127 {
128   struct ares_addrinfo_cname *tail = ares__malloc_addrinfo_cname();
129   struct ares_addrinfo_cname *last = *head;
130   if (!last)
131     {
132       *head = tail;
133       return tail;
134     }
135 
136   while (last->next)
137     {
138       last = last->next;
139     }
140 
141   last->next = tail;
142   return tail;
143 }
144 
ares__addrinfo_cat_cnames(struct ares_addrinfo_cname ** head,struct ares_addrinfo_cname * tail)145 void ares__addrinfo_cat_cnames(struct ares_addrinfo_cname **head,
146                                struct ares_addrinfo_cname *tail)
147 {
148   struct ares_addrinfo_cname *last = *head;
149   if (!last)
150     {
151       *head = tail;
152       return;
153     }
154 
155   while (last->next)
156     {
157       last = last->next;
158     }
159 
160   last->next = tail;
161 }
162 
ares__malloc_addrinfo()163 struct ares_addrinfo *ares__malloc_addrinfo()
164 {
165   struct ares_addrinfo *ai = ares_malloc(sizeof(struct ares_addrinfo));
166   if (!ai)
167     return NULL;
168 
169   *ai = empty_addrinfo;
170   return ai;
171 }
172 
ares__malloc_addrinfo_node()173 struct ares_addrinfo_node *ares__malloc_addrinfo_node()
174 {
175   struct ares_addrinfo_node *node =
176       ares_malloc(sizeof(struct ares_addrinfo_node));
177   if (!node)
178     return NULL;
179 
180   *node = empty_addrinfo_node;
181   return node;
182 }
183 
184 /* Allocate new addrinfo and append to the tail. */
ares__append_addrinfo_node(struct ares_addrinfo_node ** head)185 struct ares_addrinfo_node *ares__append_addrinfo_node(struct ares_addrinfo_node **head)
186 {
187   struct ares_addrinfo_node *tail = ares__malloc_addrinfo_node();
188   struct ares_addrinfo_node *last = *head;
189   if (!last)
190     {
191       *head = tail;
192       return tail;
193     }
194 
195   while (last->ai_next)
196     {
197       last = last->ai_next;
198     }
199 
200   last->ai_next = tail;
201   return tail;
202 }
203 
ares__addrinfo_cat_nodes(struct ares_addrinfo_node ** head,struct ares_addrinfo_node * tail)204 void ares__addrinfo_cat_nodes(struct ares_addrinfo_node **head,
205                               struct ares_addrinfo_node *tail)
206 {
207   struct ares_addrinfo_node *last = *head;
208   if (!last)
209     {
210       *head = tail;
211       return;
212     }
213 
214   while (last->ai_next)
215     {
216       last = last->ai_next;
217     }
218 
219   last->ai_next = tail;
220 }
221 
222 /* Resolve service name into port number given in host byte order.
223  * If not resolved, return 0.
224  */
lookup_service(const char * service,int flags)225 static unsigned short lookup_service(const char *service, int flags)
226 {
227   const char *proto;
228   struct servent *sep;
229 #ifdef HAVE_GETSERVBYNAME_R
230   struct servent se;
231   char tmpbuf[4096];
232 #endif
233 
234   if (service)
235     {
236       if (flags & ARES_NI_UDP)
237         proto = "udp";
238       else if (flags & ARES_NI_SCTP)
239         proto = "sctp";
240       else if (flags & ARES_NI_DCCP)
241         proto = "dccp";
242       else
243         proto = "tcp";
244 #ifdef HAVE_GETSERVBYNAME_R
245       memset(&se, 0, sizeof(se));
246       sep = &se;
247       memset(tmpbuf, 0, sizeof(tmpbuf));
248 #if GETSERVBYNAME_R_ARGS == 6
249       if (getservbyname_r(service, proto, &se, (void *)tmpbuf, sizeof(tmpbuf),
250                           &sep) != 0)
251         sep = NULL; /* LCOV_EXCL_LINE: buffer large so this never fails */
252 #elif GETSERVBYNAME_R_ARGS == 5
253       sep =
254           getservbyname_r(service, proto, &se, (void *)tmpbuf, sizeof(tmpbuf));
255 #elif GETSERVBYNAME_R_ARGS == 4
256       if (getservbyname_r(service, proto, &se, (void *)tmpbuf) != 0)
257         sep = NULL;
258 #else
259       /* Lets just hope the OS uses TLS! */
260       sep = getservbyname(service, proto);
261 #endif
262 #else
263         /* Lets just hope the OS uses TLS! */
264 #if (defined(NETWARE) && !defined(__NOVELL_LIBC__))
265       sep = getservbyname(service, (char *)proto);
266 #else
267       sep = getservbyname(service, proto);
268 #endif
269 #endif
270       return (sep ? ntohs((unsigned short)sep->s_port) : 0);
271     }
272   return 0;
273 }
274 
275 /* If the name looks like an IP address or an error occured,
276  * fake up a host entry, end the query immediately, and return true.
277  * Otherwise return false.
278  */
fake_addrinfo(const char * name,unsigned short port,const struct ares_addrinfo_hints * hints,struct ares_addrinfo * ai,ares_addrinfo_callback callback,void * arg)279 static int fake_addrinfo(const char *name,
280                          unsigned short port,
281                          const struct ares_addrinfo_hints *hints,
282                          struct ares_addrinfo *ai,
283                          ares_addrinfo_callback callback,
284                          void *arg)
285 {
286   struct ares_addrinfo_cname *cname;
287   int status = ARES_SUCCESS;
288   int result = 0;
289   int family = hints->ai_family;
290   if (family == AF_INET || family == AF_INET6 || family == AF_UNSPEC)
291     {
292       /* It only looks like an IP address if it's all numbers and dots. */
293       int numdots = 0, valid = 1;
294       const char *p;
295       for (p = name; *p; p++)
296         {
297           if (!ISDIGIT(*p) && *p != '.')
298             {
299               valid = 0;
300               break;
301             }
302           else if (*p == '.')
303             {
304               numdots++;
305             }
306         }
307 
308       /* if we don't have 3 dots, it is illegal
309        * (although inet_pton doesn't think so).
310        */
311       if (numdots != 3 || !valid)
312         result = 0;
313       else
314         {
315           struct in_addr addr4;
316           result = ares_inet_pton(AF_INET, name, &addr4) < 1 ? 0 : 1;
317           if (result)
318             {
319               status = ares_append_ai_node(AF_INET, port, 0, &addr4, &ai->nodes);
320               if (status != ARES_SUCCESS)
321                 {
322                   callback(arg, status, 0, NULL);
323                   return 1;
324                 }
325             }
326         }
327     }
328 
329   if (!result && (family == AF_INET6 || family == AF_UNSPEC))
330     {
331       struct ares_in6_addr addr6;
332       result = ares_inet_pton(AF_INET6, name, &addr6) < 1 ? 0 : 1;
333       if (result)
334         {
335           status = ares_append_ai_node(AF_INET6, port, 0, &addr6, &ai->nodes);
336           if (status != ARES_SUCCESS)
337             {
338               callback(arg, status, 0, NULL);
339               return 1;
340             }
341         }
342     }
343 
344   if (!result)
345     return 0;
346 
347   if (hints->ai_flags & ARES_AI_CANONNAME)
348     {
349       cname = ares__append_addrinfo_cname(&ai->cnames);
350       if (!cname)
351         {
352           ares_freeaddrinfo(ai);
353           callback(arg, ARES_ENOMEM, 0, NULL);
354           return 1;
355         }
356 
357       /* Duplicate the name, to avoid a constness violation. */
358       cname->name = ares_strdup(name);
359       if (!cname->name)
360         {
361           ares_freeaddrinfo(ai);
362           callback(arg, ARES_ENOMEM, 0, NULL);
363           return 1;
364         }
365     }
366 
367   ai->nodes->ai_socktype = hints->ai_socktype;
368   ai->nodes->ai_protocol = hints->ai_protocol;
369 
370   callback(arg, ARES_SUCCESS, 0, ai);
371   return 1;
372 }
373 
end_hquery(struct host_query * hquery,int status)374 static void end_hquery(struct host_query *hquery, int status)
375 {
376   struct ares_addrinfo_node sentinel;
377   struct ares_addrinfo_node *next;
378   if (status == ARES_SUCCESS)
379     {
380       if (!(hquery->hints.ai_flags & ARES_AI_NOSORT) && hquery->ai->nodes)
381         {
382           sentinel.ai_next = hquery->ai->nodes;
383           ares__sortaddrinfo(hquery->channel, &sentinel);
384           hquery->ai->nodes = sentinel.ai_next;
385         }
386       next = hquery->ai->nodes;
387 
388       while (next)
389         {
390           next->ai_socktype = hquery->hints.ai_socktype;
391           next->ai_protocol = hquery->hints.ai_protocol;
392           next = next->ai_next;
393         }
394     }
395   else
396     {
397       /* Clean up what we have collected by so far. */
398       ares_freeaddrinfo(hquery->ai);
399       hquery->ai = NULL;
400     }
401 
402   hquery->callback(hquery->arg, status, hquery->timeouts, hquery->ai);
403   ares_free(hquery->name);
404   ares_free(hquery);
405 }
406 
is_localhost(const char * name)407 static int is_localhost(const char *name)
408 {
409   /* RFC6761 6.3 says : The domain "localhost." and any names falling within ".localhost." */
410   size_t len;
411 
412   if (name == NULL)
413     return 0;
414 
415   if (strcmp(name, "localhost") == 0)
416     return 1;
417 
418   len = strlen(name);
419   if (len < 10 /* strlen(".localhost") */)
420     return 0;
421 
422   if (strcmp(name + (len - 10 /* strlen(".localhost") */), ".localhost") == 0)
423     return 1;
424 
425   return 0;
426 }
427 
file_lookup(struct host_query * hquery)428 static int file_lookup(struct host_query *hquery)
429 {
430   FILE *fp;
431   int error;
432   int status;
433   char *path_hosts = NULL;
434 
435   if (hquery->hints.ai_flags & ARES_AI_ENVHOSTS)
436     {
437       path_hosts = ares_strdup(getenv("CARES_HOSTS"));
438       if (!path_hosts)
439         return ARES_ENOMEM;
440     }
441 
442   if (hquery->channel->hosts_path)
443     {
444       path_hosts = ares_strdup(hquery->channel->hosts_path);
445       if (!path_hosts)
446         return ARES_ENOMEM;
447     }
448 
449   if (!path_hosts)
450     {
451 #ifdef WIN32
452       char PATH_HOSTS[MAX_PATH];
453       win_platform platform;
454 
455       PATH_HOSTS[0] = '\0';
456 
457       platform = ares__getplatform();
458 
459       if (platform == WIN_NT)
460         {
461           char tmp[MAX_PATH];
462           HKEY hkeyHosts;
463 
464           if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, WIN_NS_NT_KEY, 0, KEY_READ,
465                            &hkeyHosts) == ERROR_SUCCESS)
466             {
467               DWORD dwLength = MAX_PATH;
468               RegQueryValueExA(hkeyHosts, DATABASEPATH, NULL, NULL, (LPBYTE)tmp,
469                               &dwLength);
470               ExpandEnvironmentStringsA(tmp, PATH_HOSTS, MAX_PATH);
471               RegCloseKey(hkeyHosts);
472             }
473         }
474       else if (platform == WIN_9X)
475         GetWindowsDirectoryA(PATH_HOSTS, MAX_PATH);
476       else
477         return ARES_ENOTFOUND;
478 
479       strcat(PATH_HOSTS, WIN_PATH_HOSTS);
480 #elif defined(WATT32)
481       const char *PATH_HOSTS = _w32_GetHostsFile();
482 
483       if (!PATH_HOSTS)
484         return ARES_ENOTFOUND;
485 #endif
486       path_hosts = ares_strdup(PATH_HOSTS);
487       if (!path_hosts)
488         return ARES_ENOMEM;
489     }
490 
491   fp = fopen(path_hosts, "r");
492   if (!fp)
493     {
494       error = ERRNO;
495       switch (error)
496         {
497         case ENOENT:
498         case ESRCH:
499           status = ARES_ENOTFOUND;
500           break;
501         default:
502           DEBUGF(fprintf(stderr, "fopen() failed with error: %d %s\n", error,
503                          strerror(error)));
504           DEBUGF(fprintf(stderr, "Error opening file: %s\n", path_hosts));
505           status = ARES_EFILE;
506           break;
507         }
508     }
509   else
510     {
511       status = ares__readaddrinfo(fp, hquery->name, hquery->port, &hquery->hints, hquery->ai);
512       fclose(fp);
513     }
514   ares_free(path_hosts);
515 
516   /* RFC6761 section 6.3 #3 states that "Name resolution APIs and libraries
517    * SHOULD recognize localhost names as special and SHOULD always return the
518    * IP loopback address for address queries".
519    * We will also ignore ALL errors when trying to resolve localhost, such
520    * as permissions errors reading /etc/hosts or a malformed /etc/hosts */
521   if (status != ARES_SUCCESS && is_localhost(hquery->name))
522     {
523       return ares__addrinfo_localhost(hquery->name, hquery->port,
524                                       &hquery->hints, hquery->ai);
525     }
526 
527   return status;
528 }
529 
next_lookup(struct host_query * hquery,int status)530 static void next_lookup(struct host_query *hquery, int status)
531 {
532   switch (*hquery->remaining_lookups)
533     {
534       case 'b':
535           /* RFC6761 section 6.3 #3 says "Name resolution APIs SHOULD NOT send
536            * queries for localhost names to their configured caching DNS
537            * server(s)." */
538           if (!is_localhost(hquery->name))
539             {
540               /* DNS lookup */
541               if (next_dns_lookup(hquery))
542                 break;
543             }
544 
545           hquery->remaining_lookups++;
546           next_lookup(hquery, status);
547           break;
548 
549       case 'f':
550           /* Host file lookup */
551           if (file_lookup(hquery) == ARES_SUCCESS)
552             {
553               end_hquery(hquery, ARES_SUCCESS);
554               break;
555             }
556           hquery->remaining_lookups++;
557           next_lookup(hquery, status);
558           break;
559       default:
560           /* No lookup left */
561          end_hquery(hquery, status);
562          break;
563     }
564 }
565 
host_callback(void * arg,int status,int timeouts,unsigned char * abuf,int alen)566 static void host_callback(void *arg, int status, int timeouts,
567                           unsigned char *abuf, int alen)
568 {
569   struct host_query *hquery = (struct host_query*)arg;
570   int addinfostatus = ARES_SUCCESS;
571   hquery->timeouts += timeouts;
572   hquery->remaining--;
573 
574   if (status == ARES_SUCCESS)
575     {
576       addinfostatus = ares__parse_into_addrinfo(abuf, alen, 1, hquery->port, hquery->ai);
577     }
578 
579   if (!hquery->remaining)
580     {
581       if (addinfostatus != ARES_SUCCESS && addinfostatus != ARES_ENODATA)
582         {
583           /* error in parsing result e.g. no memory */
584           if (addinfostatus == ARES_EBADRESP && hquery->ai->nodes)
585             {
586               /* We got a bad response from server, but at least one query
587                * ended with ARES_SUCCESS */
588               end_hquery(hquery, ARES_SUCCESS);
589             }
590           else
591             {
592               end_hquery(hquery, addinfostatus);
593             }
594         }
595       else if (hquery->ai->nodes)
596         {
597           /* at least one query ended with ARES_SUCCESS */
598           end_hquery(hquery, ARES_SUCCESS);
599         }
600       else if (status == ARES_ENOTFOUND || status == ARES_ENODATA ||
601                addinfostatus == ARES_ENODATA)
602         {
603           if (status == ARES_ENODATA || addinfostatus == ARES_ENODATA)
604             hquery->nodata_cnt++;
605           next_lookup(hquery, hquery->nodata_cnt?ARES_ENODATA:status);
606         }
607       else if (status == ARES_EDESTRUCTION)
608         {
609           /* NOTE: Could also be ARES_EDESTRUCTION.  We need to only call this
610            * once all queries (there can be multiple for getaddrinfo) are
611            * terminated.  */
612           end_hquery(hquery, status);
613         }
614       else
615         {
616           end_hquery(hquery, status);
617         }
618     }
619 
620   /* at this point we keep on waiting for the next query to finish */
621 }
622 
ares_getaddrinfo(ares_channel channel,const char * name,const char * service,const struct ares_addrinfo_hints * hints,ares_addrinfo_callback callback,void * arg)623 void ares_getaddrinfo(ares_channel channel,
624                       const char* name, const char* service,
625                       const struct ares_addrinfo_hints* hints,
626                       ares_addrinfo_callback callback, void* arg)
627 {
628   struct host_query *hquery;
629   unsigned short port = 0;
630   int family;
631   struct ares_addrinfo *ai;
632   char *alias_name = NULL;
633   int status;
634 
635   if (!hints)
636     {
637       hints = &default_hints;
638     }
639 
640   family = hints->ai_family;
641 
642   /* Right now we only know how to look up Internet addresses
643      and unspec means try both basically. */
644   if (family != AF_INET &&
645       family != AF_INET6 &&
646       family != AF_UNSPEC)
647     {
648       callback(arg, ARES_ENOTIMP, 0, NULL);
649       return;
650     }
651 
652   if (ares__is_onion_domain(name))
653     {
654       callback(arg, ARES_ENOTFOUND, 0, NULL);
655       return;
656     }
657 
658   /* perform HOSTALIAS resolution (technically this function does some other
659    * things we are going to ignore) */
660   status = ares__single_domain(channel, name, &alias_name);
661   if (status != ARES_SUCCESS) {
662     callback(arg, status, 0, NULL);
663     return;
664   }
665 
666   if (alias_name)
667     name = alias_name;
668 
669   if (service)
670     {
671       if (hints->ai_flags & ARES_AI_NUMERICSERV)
672         {
673           unsigned long val;
674           errno = 0;
675           val = strtoul(service, NULL, 0);
676           if ((val == 0 && errno != 0) || val > 65535)
677             {
678               ares_free(alias_name);
679               callback(arg, ARES_ESERVICE, 0, NULL);
680               return;
681             }
682           port = (unsigned short)val;
683         }
684       else
685         {
686           port = lookup_service(service, 0);
687           if (!port)
688             {
689               unsigned long val;
690               errno = 0;
691               val = strtoul(service, NULL, 0);
692               if ((val == 0 && errno != 0) || val > 65535)
693                 {
694                   ares_free(alias_name);
695                   callback(arg, ARES_ESERVICE, 0, NULL);
696                   return;
697                 }
698               port = (unsigned short)val;
699             }
700         }
701     }
702 
703   ai = ares__malloc_addrinfo();
704   if (!ai)
705     {
706       ares_free(alias_name);
707       callback(arg, ARES_ENOMEM, 0, NULL);
708       return;
709     }
710 
711   if (fake_addrinfo(name, port, hints, ai, callback, arg))
712     {
713       ares_free(alias_name);
714       return;
715     }
716 
717   /* Allocate and fill in the host query structure. */
718   hquery = ares_malloc(sizeof(struct host_query));
719   if (!hquery)
720     {
721       ares_free(alias_name);
722       ares_freeaddrinfo(ai);
723       callback(arg, ARES_ENOMEM, 0, NULL);
724       return;
725     }
726 
727   hquery->name = ares_strdup(name);
728   ares_free(alias_name);
729   if (!hquery->name)
730     {
731       ares_free(hquery);
732       ares_freeaddrinfo(ai);
733       callback(arg, ARES_ENOMEM, 0, NULL);
734       return;
735     }
736 
737   hquery->port = port;
738   hquery->channel = channel;
739   hquery->hints = *hints;
740   hquery->sent_family = -1; /* nothing is sent yet */
741   hquery->callback = callback;
742   hquery->arg = arg;
743   hquery->remaining_lookups = channel->lookups;
744   hquery->timeouts = 0;
745   hquery->ai = ai;
746   hquery->next_domain = -1;
747   hquery->remaining = 0;
748   hquery->nodata_cnt = 0;
749 
750   /* Start performing lookups according to channel->lookups. */
751   next_lookup(hquery, ARES_ECONNREFUSED /* initial error code */);
752 }
753 
next_dns_lookup(struct host_query * hquery)754 static int next_dns_lookup(struct host_query *hquery)
755 {
756   char *s = NULL;
757   int is_s_allocated = 0;
758   int status;
759 
760   /* if next_domain == -1 and as_is_first is true, try hquery->name */
761   if (hquery->next_domain == -1)
762     {
763       if (as_is_first(hquery))
764         {
765           s = hquery->name;
766         }
767       hquery->next_domain = 0;
768     }
769 
770   /* if as_is_first is false, try hquery->name at last */
771   if (!s && hquery->next_domain == hquery->channel->ndomains) {
772     if (!as_is_first(hquery))
773       {
774         s = hquery->name;
775       }
776     hquery->next_domain++;
777   }
778 
779   if (!s && hquery->next_domain < hquery->channel->ndomains && !as_is_only(hquery))
780     {
781       status = ares__cat_domain(
782           hquery->name,
783           hquery->channel->domains[hquery->next_domain++],
784           &s);
785       if (status == ARES_SUCCESS)
786         {
787           is_s_allocated = 1;
788         }
789     }
790 
791   if (s)
792     {
793       switch (hquery->hints.ai_family)
794         {
795           case AF_INET:
796             hquery->remaining += 1;
797             ares_query(hquery->channel, s, C_IN, T_A, host_callback, hquery);
798             break;
799           case AF_INET6:
800             hquery->remaining += 1;
801             ares_query(hquery->channel, s, C_IN, T_AAAA, host_callback, hquery);
802             break;
803           case AF_UNSPEC:
804             hquery->remaining += 2;
805             ares_query(hquery->channel, s, C_IN, T_A, host_callback, hquery);
806             ares_query(hquery->channel, s, C_IN, T_AAAA, host_callback, hquery);
807             break;
808           default: break;
809         }
810       if (is_s_allocated)
811         {
812           ares_free(s);
813         }
814       return 1;
815     }
816   else
817     {
818       assert(!hquery->ai->nodes);
819       return 0;
820     }
821 }
822 
as_is_first(const struct host_query * hquery)823 static int as_is_first(const struct host_query* hquery)
824 {
825   char* p;
826   int ndots = 0;
827   size_t nname = hquery->name?strlen(hquery->name):0;
828   for (p = hquery->name; *p; p++)
829     {
830       if (*p == '.')
831         {
832           ndots++;
833         }
834     }
835   if (nname && hquery->name[nname-1] == '.')
836     {
837       /* prevent ARES_EBADNAME for valid FQDN, where ndots < channel->ndots  */
838       return 1;
839     }
840   return ndots >= hquery->channel->ndots;
841 }
842 
as_is_only(const struct host_query * hquery)843 static int as_is_only(const struct host_query* hquery)
844 {
845   size_t nname = hquery->name?strlen(hquery->name):0;
846   if (nname && hquery->name[nname-1] == '.')
847     return 1;
848   return 0;
849 }
850 
851