Imported Upstream version 1.18.1
[platform/upstream/c-ares.git] / src / lib / ares_getaddrinfo.c
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
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
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
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
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
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. */
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
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  */
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  */
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 (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
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
407 static int file_lookup(struct host_query *hquery)
408 {
409   FILE *fp;
410   int error;
411   int status;
412   const char *path_hosts = NULL;
413
414   if (hquery->hints.ai_flags & ARES_AI_ENVHOSTS)
415     {
416       path_hosts = getenv("CARES_HOSTS");
417     }
418
419   if (!path_hosts)
420     {
421 #ifdef WIN32
422       char PATH_HOSTS[MAX_PATH];
423       win_platform platform;
424
425       PATH_HOSTS[0] = '\0';
426
427       platform = ares__getplatform();
428
429       if (platform == WIN_NT)
430         {
431           char tmp[MAX_PATH];
432           HKEY hkeyHosts;
433
434           if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, WIN_NS_NT_KEY, 0, KEY_READ,
435                            &hkeyHosts) == ERROR_SUCCESS)
436             {
437               DWORD dwLength = MAX_PATH;
438               RegQueryValueExA(hkeyHosts, DATABASEPATH, NULL, NULL, (LPBYTE)tmp,
439                               &dwLength);
440               ExpandEnvironmentStringsA(tmp, PATH_HOSTS, MAX_PATH);
441               RegCloseKey(hkeyHosts);
442             }
443         }
444       else if (platform == WIN_9X)
445         GetWindowsDirectoryA(PATH_HOSTS, MAX_PATH);
446       else
447         return ARES_ENOTFOUND;
448
449       strcat(PATH_HOSTS, WIN_PATH_HOSTS);
450       path_hosts = PATH_HOSTS;
451
452 #elif defined(WATT32)
453       const char *PATH_HOSTS = _w32_GetHostsFile();
454
455       if (!PATH_HOSTS)
456         return ARES_ENOTFOUND;
457 #endif
458       path_hosts = PATH_HOSTS;
459     }
460
461   fp = fopen(path_hosts, "r");
462   if (!fp)
463     {
464       error = ERRNO;
465       switch (error)
466         {
467         case ENOENT:
468         case ESRCH:
469           return ARES_ENOTFOUND;
470         default:
471           DEBUGF(fprintf(stderr, "fopen() failed with error: %d %s\n", error,
472                          strerror(error)));
473           DEBUGF(fprintf(stderr, "Error opening file: %s\n", path_hosts));
474           return ARES_EFILE;
475         }
476     }
477   status = ares__readaddrinfo(fp, hquery->name, hquery->port, &hquery->hints, hquery->ai);
478   fclose(fp);
479
480   /* RFC6761 section 6.3 #3 states that "Name resolution APIs and libraries
481    * SHOULD recognize localhost names as special and SHOULD always return the
482    * IP loopback address for address queries" */
483   if (status == ARES_ENOTFOUND && strcmp(hquery->name, "localhost") == 0)
484     {
485       return ares__addrinfo_localhost(hquery->name, hquery->port,
486                                       &hquery->hints, hquery->ai);
487     }
488
489   return status;
490 }
491
492 static void next_lookup(struct host_query *hquery, int status)
493 {
494   switch (*hquery->remaining_lookups)
495     {
496       case 'b':
497           /* RFC6761 section 6.3 #3 says "Name resolution APIs SHOULD NOT send
498            * queries for localhost names to their configured caching DNS
499            * server(s)." */
500           if (strcmp(hquery->name, "localhost") != 0)
501             {
502               /* DNS lookup */
503               if (next_dns_lookup(hquery))
504                 break;
505             }
506
507           hquery->remaining_lookups++;
508           next_lookup(hquery, status);
509           break;
510
511       case 'f':
512           /* Host file lookup */
513           if (file_lookup(hquery) == ARES_SUCCESS)
514             {
515               end_hquery(hquery, ARES_SUCCESS);
516               break;
517             }
518           hquery->remaining_lookups++;
519           next_lookup(hquery, status);
520           break;
521       default:
522           /* No lookup left */
523          end_hquery(hquery, status);
524          break;
525     }
526 }
527
528 static void host_callback(void *arg, int status, int timeouts,
529                           unsigned char *abuf, int alen)
530 {
531   struct host_query *hquery = (struct host_query*)arg;
532   int addinfostatus = ARES_SUCCESS;
533   hquery->timeouts += timeouts;
534   hquery->remaining--;
535
536   if (status == ARES_SUCCESS)
537     {
538       addinfostatus = ares__parse_into_addrinfo(abuf, alen, 1, hquery->port, hquery->ai);
539     }
540
541   if (!hquery->remaining)
542     {
543       if (addinfostatus != ARES_SUCCESS && addinfostatus != ARES_ENODATA)
544         {
545           /* error in parsing result e.g. no memory */
546           end_hquery(hquery, addinfostatus);
547         }
548       else if (hquery->ai->nodes)
549         {
550           /* at least one query ended with ARES_SUCCESS */
551           end_hquery(hquery, ARES_SUCCESS);
552         }
553       else if (status == ARES_ENOTFOUND || status == ARES_ENODATA ||
554                addinfostatus == ARES_ENODATA)
555         {
556           if (status == ARES_ENODATA || addinfostatus == ARES_ENODATA)
557             hquery->nodata_cnt++;
558           next_lookup(hquery, hquery->nodata_cnt?ARES_ENODATA:status);
559         }
560       else if (status == ARES_EDESTRUCTION)
561         {
562           /* NOTE: Could also be ARES_EDESTRUCTION.  We need to only call this
563            * once all queries (there can be multiple for getaddrinfo) are
564            * terminated.  */
565           end_hquery(hquery, status);
566         }
567       else
568         {
569           end_hquery(hquery, status);
570         }
571     }
572
573   /* at this point we keep on waiting for the next query to finish */
574 }
575
576 void ares_getaddrinfo(ares_channel channel,
577                       const char* name, const char* service,
578                       const struct ares_addrinfo_hints* hints,
579                       ares_addrinfo_callback callback, void* arg)
580 {
581   struct host_query *hquery;
582   unsigned short port = 0;
583   int family;
584   struct ares_addrinfo *ai;
585   char *alias_name = NULL;
586   int status;
587
588   if (!hints)
589     {
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 &&
598       family != AF_INET6 &&
599       family != AF_UNSPEC)
600     {
601       callback(arg, ARES_ENOTIMP, 0, NULL);
602       return;
603     }
604
605   if (ares__is_onion_domain(name))
606     {
607       callback(arg, ARES_ENOTFOUND, 0, NULL);
608       return;
609     }
610
611   /* perform HOSTALIAS resolution (technically this function does some other
612    * things we are going to ignore) */
613   status = ares__single_domain(channel, name, &alias_name);
614   if (status != ARES_SUCCESS) {
615     callback(arg, status, 0, NULL);
616     return;
617   }
618
619   if (alias_name)
620     name = alias_name;
621
622   if (service)
623     {
624       if (hints->ai_flags & ARES_AI_NUMERICSERV)
625         {
626           port = (unsigned short)strtoul(service, NULL, 0);
627           if (!port)
628             {
629               ares_free(alias_name);
630               callback(arg, ARES_ESERVICE, 0, NULL);
631               return;
632             }
633         }
634       else
635         {
636           port = lookup_service(service, 0);
637           if (!port)
638             {
639               port = (unsigned short)strtoul(service, NULL, 0);
640               if (!port)
641                 {
642                   ares_free(alias_name);
643                   callback(arg, ARES_ESERVICE, 0, NULL);
644                   return;
645                 }
646             }
647         }
648     }
649
650   ai = ares__malloc_addrinfo();
651   if (!ai)
652     {
653       ares_free(alias_name);
654       callback(arg, ARES_ENOMEM, 0, NULL);
655       return;
656     }
657
658   if (fake_addrinfo(name, port, hints, ai, callback, arg))
659     {
660       ares_free(alias_name);
661       return;
662     }
663
664   /* Allocate and fill in the host query structure. */
665   hquery = ares_malloc(sizeof(struct host_query));
666   if (!hquery)
667     {
668       ares_free(alias_name);
669       ares_freeaddrinfo(ai);
670       callback(arg, ARES_ENOMEM, 0, NULL);
671       return;
672     }
673
674   hquery->name = ares_strdup(name);
675   ares_free(alias_name);
676   if (!hquery->name)
677     {
678       ares_free(hquery);
679       ares_freeaddrinfo(ai);
680       callback(arg, ARES_ENOMEM, 0, NULL);
681       return;
682     }
683
684   hquery->port = port;
685   hquery->channel = channel;
686   hquery->hints = *hints;
687   hquery->sent_family = -1; /* nothing is sent yet */
688   hquery->callback = callback;
689   hquery->arg = arg;
690   hquery->remaining_lookups = channel->lookups;
691   hquery->timeouts = 0;
692   hquery->ai = ai;
693   hquery->next_domain = -1;
694   hquery->remaining = 0;
695   hquery->nodata_cnt = 0;
696
697   /* Start performing lookups according to channel->lookups. */
698   next_lookup(hquery, ARES_ECONNREFUSED /* initial error code */);
699 }
700
701 static int next_dns_lookup(struct host_query *hquery)
702 {
703   char *s = NULL;
704   int is_s_allocated = 0;
705   int status;
706
707   /* if next_domain == -1 and as_is_first is true, try hquery->name */
708   if (hquery->next_domain == -1)
709     {
710       if (as_is_first(hquery))
711         {
712           s = hquery->name;
713         }
714       hquery->next_domain = 0;
715     }
716
717   /* if as_is_first is false, try hquery->name at last */
718   if (!s && hquery->next_domain == hquery->channel->ndomains) {
719     if (!as_is_first(hquery))
720       {
721         s = hquery->name;
722       }
723     hquery->next_domain++;
724   }
725
726   if (!s && hquery->next_domain < hquery->channel->ndomains && !as_is_only(hquery))
727     {
728       status = ares__cat_domain(
729           hquery->name,
730           hquery->channel->domains[hquery->next_domain++],
731           &s);
732       if (status == ARES_SUCCESS)
733         {
734           is_s_allocated = 1;
735         }
736     }
737
738   if (s)
739     {
740       switch (hquery->hints.ai_family)
741         {
742           case AF_INET:
743             hquery->remaining += 1;
744             ares_query(hquery->channel, s, C_IN, T_A, host_callback, hquery);
745             break;
746           case AF_INET6:
747             hquery->remaining += 1;
748             ares_query(hquery->channel, s, C_IN, T_AAAA, host_callback, hquery);
749             break;
750           case AF_UNSPEC:
751             hquery->remaining += 2;
752             ares_query(hquery->channel, s, C_IN, T_A, host_callback, hquery);
753             ares_query(hquery->channel, s, C_IN, T_AAAA, host_callback, hquery);
754             break;
755           default: break;
756         }
757       if (is_s_allocated)
758         {
759           ares_free(s);
760         }
761       return 1;
762     }
763   else
764     {
765       assert(!hquery->ai->nodes);
766       return 0;
767     }
768 }
769
770 static int as_is_first(const struct host_query* hquery)
771 {
772   char* p;
773   int ndots = 0;
774   size_t nname = hquery->name?strlen(hquery->name):0;
775   for (p = hquery->name; *p; p++)
776     {
777       if (*p == '.')
778         {
779           ndots++;
780         }
781     }
782   if (nname && hquery->name[nname-1] == '.')
783     {
784       /* prevent ARES_EBADNAME for valid FQDN, where ndots < channel->ndots  */
785       return 1;
786     }
787   return ndots >= hquery->channel->ndots;
788 }
789
790 static int as_is_only(const struct host_query* hquery)
791 {
792   size_t nname = hquery->name?strlen(hquery->name):0;
793   if (nname && hquery->name[nname-1] == '.')
794     return 1;
795   return 0;
796 }
797