• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
9  *
10  * This software is licensed as described in the file COPYING, which
11  * you should have received as part of this distribution. The terms
12  * are also available at https://curl.se/docs/copyright.html.
13  *
14  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15  * copies of the Software, and permit persons to whom the Software is
16  * furnished to do so, under the terms of the COPYING file.
17  *
18  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19  * KIND, either express or implied.
20  *
21  * SPDX-License-Identifier: curl
22  *
23  ***************************************************************************/
24 
25 #include "curl_setup.h"
26 
27 #ifdef HAVE_NETINET_IN_H
28 #include <netinet/in.h>
29 #endif
30 #ifdef HAVE_NETINET_IN6_H
31 #include <netinet/in6.h>
32 #endif
33 #ifdef HAVE_NETDB_H
34 #include <netdb.h>
35 #endif
36 #ifdef HAVE_ARPA_INET_H
37 #include <arpa/inet.h>
38 #endif
39 #ifdef __VMS
40 #include <in.h>
41 #include <inet.h>
42 #endif
43 
44 #ifdef HAVE_SETJMP_H
45 #include <setjmp.h>
46 #endif
47 #ifdef HAVE_SIGNAL_H
48 #include <signal.h>
49 #endif
50 
51 #include "urldata.h"
52 #include "sendf.h"
53 #include "hostip.h"
54 #include "hash.h"
55 #include "rand.h"
56 #include "share.h"
57 #include "url.h"
58 #include "inet_ntop.h"
59 #include "inet_pton.h"
60 #include "multiif.h"
61 #include "doh.h"
62 #include "warnless.h"
63 #include "strcase.h"
64 /* The last 3 #include files should be in this order */
65 #include "curl_printf.h"
66 #include "curl_memory.h"
67 #include "memdebug.h"
68 
69 #if defined(ENABLE_IPV6) && defined(CURL_OSX_CALL_COPYPROXIES)
70 #include <SystemConfiguration/SCDynamicStoreCopySpecific.h>
71 #endif
72 
73 #if defined(CURLRES_SYNCH) && \
74     defined(HAVE_ALARM) && defined(SIGALRM) && defined(HAVE_SIGSETJMP)
75 /* alarm-based timeouts can only be used with all the dependencies satisfied */
76 #define USE_ALARM_TIMEOUT
77 #endif
78 
79 #define MAX_HOSTCACHE_LEN (255 + 7) /* max FQDN + colon + port number + zero */
80 
81 /*
82  * hostip.c explained
83  * ==================
84  *
85  * The main COMPILE-TIME DEFINES to keep in mind when reading the host*.c
86  * source file are these:
87  *
88  * CURLRES_IPV6 - this host has getaddrinfo() and family, and thus we use
89  * that. The host may not be able to resolve IPv6, but we don't really have to
90  * take that into account. Hosts that aren't IPv6-enabled have CURLRES_IPV4
91  * defined.
92  *
93  * CURLRES_ARES - is defined if libcurl is built to use c-ares for
94  * asynchronous name resolves. This can be Windows or *nix.
95  *
96  * CURLRES_THREADED - is defined if libcurl is built to run under (native)
97  * Windows, and then the name resolve will be done in a new thread, and the
98  * supported API will be the same as for ares-builds.
99  *
100  * If any of the two previous are defined, CURLRES_ASYNCH is defined too. If
101  * libcurl is not built to use an asynchronous resolver, CURLRES_SYNCH is
102  * defined.
103  *
104  * The host*.c sources files are split up like this:
105  *
106  * hostip.c   - method-independent resolver functions and utility functions
107  * hostasyn.c - functions for asynchronous name resolves
108  * hostsyn.c  - functions for synchronous name resolves
109  * hostip4.c  - IPv4 specific functions
110  * hostip6.c  - IPv6 specific functions
111  *
112  * The two asynchronous name resolver backends are implemented in:
113  * asyn-ares.c   - functions for ares-using name resolves
114  * asyn-thread.c - functions for threaded name resolves
115 
116  * The hostip.h is the united header file for all this. It defines the
117  * CURLRES_* defines based on the config*.h and curl_setup.h defines.
118  */
119 
120 static void freednsentry(void *freethis);
121 
122 /*
123  * Return # of addresses in a Curl_addrinfo struct
124  */
Curl_num_addresses(const struct Curl_addrinfo * addr)125 int Curl_num_addresses(const struct Curl_addrinfo *addr)
126 {
127   int i = 0;
128   while(addr) {
129     addr = addr->ai_next;
130     i++;
131   }
132   return i;
133 }
134 
135 /*
136  * Curl_printable_address() stores a printable version of the 1st address
137  * given in the 'ai' argument. The result will be stored in the buf that is
138  * bufsize bytes big.
139  *
140  * If the conversion fails, the target buffer is empty.
141  */
Curl_printable_address(const struct Curl_addrinfo * ai,char * buf,size_t bufsize)142 void Curl_printable_address(const struct Curl_addrinfo *ai, char *buf,
143                             size_t bufsize)
144 {
145   DEBUGASSERT(bufsize);
146   buf[0] = 0;
147 
148   switch(ai->ai_family) {
149   case AF_INET: {
150     const struct sockaddr_in *sa4 = (const void *)ai->ai_addr;
151     const struct in_addr *ipaddr4 = &sa4->sin_addr;
152     (void)Curl_inet_ntop(ai->ai_family, (const void *)ipaddr4, buf, bufsize);
153     break;
154   }
155 #ifdef ENABLE_IPV6
156   case AF_INET6: {
157     const struct sockaddr_in6 *sa6 = (const void *)ai->ai_addr;
158     const struct in6_addr *ipaddr6 = &sa6->sin6_addr;
159     (void)Curl_inet_ntop(ai->ai_family, (const void *)ipaddr6, buf, bufsize);
160     break;
161   }
162 #endif
163   default:
164     break;
165   }
166 }
167 
168 /*
169  * Create a hostcache id string for the provided host + port, to be used by
170  * the DNS caching. Without alloc. Return length of the id string.
171  */
172 static size_t
create_hostcache_id(const char * name,size_t nlen,int port,char * ptr,size_t buflen)173 create_hostcache_id(const char *name,
174                     size_t nlen, /* 0 or actual name length */
175                     int port, char *ptr, size_t buflen)
176 {
177   size_t len = nlen ? nlen : strlen(name);
178   size_t olen = 0;
179   DEBUGASSERT(buflen >= MAX_HOSTCACHE_LEN);
180   if(len > (buflen - 7))
181     len = buflen - 7;
182   /* store and lower case the name */
183   while(len--) {
184     *ptr++ = Curl_raw_tolower(*name++);
185     olen++;
186   }
187   olen += msnprintf(ptr, 7, ":%u", port);
188   return olen;
189 }
190 
191 struct hostcache_prune_data {
192   long cache_timeout;
193   time_t now;
194 };
195 
196 /*
197  * This function is set as a callback to be called for every entry in the DNS
198  * cache when we want to prune old unused entries.
199  *
200  * Returning non-zero means remove the entry, return 0 to keep it in the
201  * cache.
202  */
203 static int
hostcache_timestamp_remove(void * datap,void * hc)204 hostcache_timestamp_remove(void *datap, void *hc)
205 {
206   struct hostcache_prune_data *data =
207     (struct hostcache_prune_data *) datap;
208   struct Curl_dns_entry *c = (struct Curl_dns_entry *) hc;
209 
210   return (0 != c->timestamp)
211     && (data->now - c->timestamp >= data->cache_timeout);
212 }
213 
214 /*
215  * Prune the DNS cache. This assumes that a lock has already been taken.
216  */
217 static void
hostcache_prune(struct Curl_hash * hostcache,long cache_timeout,time_t now)218 hostcache_prune(struct Curl_hash *hostcache, long cache_timeout, time_t now)
219 {
220   struct hostcache_prune_data user;
221 
222   user.cache_timeout = cache_timeout;
223   user.now = now;
224 
225   Curl_hash_clean_with_criterium(hostcache,
226                                  (void *) &user,
227                                  hostcache_timestamp_remove);
228 }
229 
230 /*
231  * Library-wide function for pruning the DNS cache. This function takes and
232  * returns the appropriate locks.
233  */
Curl_hostcache_prune(struct Curl_easy * data)234 void Curl_hostcache_prune(struct Curl_easy *data)
235 {
236   time_t now;
237 
238   if((data->set.dns_cache_timeout == -1) || !data->dns.hostcache)
239     /* cache forever means never prune, and NULL hostcache means
240        we can't do it */
241     return;
242 
243   if(data->share)
244     Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
245 
246   time(&now);
247 
248   /* Remove outdated and unused entries from the hostcache */
249   hostcache_prune(data->dns.hostcache,
250                   data->set.dns_cache_timeout,
251                   now);
252 
253   if(data->share)
254     Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
255 }
256 
257 #ifdef HAVE_SIGSETJMP
258 /* Beware this is a global and unique instance. This is used to store the
259    return address that we can jump back to from inside a signal handler. This
260    is not thread-safe stuff. */
261 sigjmp_buf curl_jmpenv;
262 #endif
263 
264 /* lookup address, returns entry if found and not stale */
fetch_addr(struct Curl_easy * data,const char * hostname,int port)265 static struct Curl_dns_entry *fetch_addr(struct Curl_easy *data,
266                                          const char *hostname,
267                                          int port)
268 {
269   struct Curl_dns_entry *dns = NULL;
270   char entry_id[MAX_HOSTCACHE_LEN];
271 
272   /* Create an entry id, based upon the hostname and port */
273   size_t entry_len = create_hostcache_id(hostname, 0, port,
274                                          entry_id, sizeof(entry_id));
275 
276   /* See if its already in our dns cache */
277   dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len + 1);
278 
279   /* No entry found in cache, check if we might have a wildcard entry */
280   if(!dns && data->state.wildcard_resolve) {
281     entry_len = create_hostcache_id("*", 1, port, entry_id, sizeof(entry_id));
282 
283     /* See if it's already in our dns cache */
284     dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len + 1);
285   }
286 
287   if(dns && (data->set.dns_cache_timeout != -1)) {
288     /* See whether the returned entry is stale. Done before we release lock */
289     struct hostcache_prune_data user;
290 
291     time(&user.now);
292     user.cache_timeout = data->set.dns_cache_timeout;
293 
294     if(hostcache_timestamp_remove(&user, dns)) {
295       infof(data, "Hostname in DNS cache was stale, zapped");
296       dns = NULL; /* the memory deallocation is being handled by the hash */
297       Curl_hash_delete(data->dns.hostcache, entry_id, entry_len + 1);
298     }
299   }
300 
301   /* See if the returned entry matches the required resolve mode */
302   if(dns && data->conn->ip_version != CURL_IPRESOLVE_WHATEVER) {
303     int pf = PF_INET;
304     bool found = false;
305     struct Curl_addrinfo *addr = dns->addr;
306 
307 #ifdef PF_INET6
308     if(data->conn->ip_version == CURL_IPRESOLVE_V6)
309       pf = PF_INET6;
310 #endif
311 
312     while(addr) {
313       if(addr->ai_family == pf) {
314         found = true;
315         break;
316       }
317       addr = addr->ai_next;
318     }
319 
320     if(!found) {
321       infof(data, "Hostname in DNS cache doesn't have needed family, zapped");
322       dns = NULL; /* the memory deallocation is being handled by the hash */
323       Curl_hash_delete(data->dns.hostcache, entry_id, entry_len + 1);
324     }
325   }
326   return dns;
327 }
328 
329 /*
330  * Curl_fetch_addr() fetches a 'Curl_dns_entry' already in the DNS cache.
331  *
332  * Curl_resolv() checks initially and multi_runsingle() checks each time
333  * it discovers the handle in the state WAITRESOLVE whether the hostname
334  * has already been resolved and the address has already been stored in
335  * the DNS cache. This short circuits waiting for a lot of pending
336  * lookups for the same hostname requested by different handles.
337  *
338  * Returns the Curl_dns_entry entry pointer or NULL if not in the cache.
339  *
340  * The returned data *MUST* be "unlocked" with Curl_resolv_unlock() after
341  * use, or we'll leak memory!
342  */
343 struct Curl_dns_entry *
Curl_fetch_addr(struct Curl_easy * data,const char * hostname,int port)344 Curl_fetch_addr(struct Curl_easy *data,
345                 const char *hostname,
346                 int port)
347 {
348   struct Curl_dns_entry *dns = NULL;
349 
350   if(data->share)
351     Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
352 
353   dns = fetch_addr(data, hostname, port);
354 
355   if(dns)
356     dns->inuse++; /* we use it! */
357 
358   if(data->share)
359     Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
360 
361   return dns;
362 }
363 
364 #ifndef CURL_DISABLE_SHUFFLE_DNS
365 UNITTEST CURLcode Curl_shuffle_addr(struct Curl_easy *data,
366                                     struct Curl_addrinfo **addr);
367 /*
368  * Curl_shuffle_addr() shuffles the order of addresses in a 'Curl_addrinfo'
369  * struct by re-linking its linked list.
370  *
371  * The addr argument should be the address of a pointer to the head node of a
372  * `Curl_addrinfo` list and it will be modified to point to the new head after
373  * shuffling.
374  *
375  * Not declared static only to make it easy to use in a unit test!
376  *
377  * @unittest: 1608
378  */
Curl_shuffle_addr(struct Curl_easy * data,struct Curl_addrinfo ** addr)379 UNITTEST CURLcode Curl_shuffle_addr(struct Curl_easy *data,
380                                     struct Curl_addrinfo **addr)
381 {
382   CURLcode result = CURLE_OK;
383   const int num_addrs = Curl_num_addresses(*addr);
384 
385   if(num_addrs > 1) {
386     struct Curl_addrinfo **nodes;
387     infof(data, "Shuffling %i addresses", num_addrs);
388 
389     nodes = malloc(num_addrs*sizeof(*nodes));
390     if(nodes) {
391       int i;
392       unsigned int *rnd;
393       const size_t rnd_size = num_addrs * sizeof(*rnd);
394 
395       /* build a plain array of Curl_addrinfo pointers */
396       nodes[0] = *addr;
397       for(i = 1; i < num_addrs; i++) {
398         nodes[i] = nodes[i-1]->ai_next;
399       }
400 
401       rnd = malloc(rnd_size);
402       if(rnd) {
403         /* Fisher-Yates shuffle */
404         if(Curl_rand(data, (unsigned char *)rnd, rnd_size) == CURLE_OK) {
405           struct Curl_addrinfo *swap_tmp;
406           for(i = num_addrs - 1; i > 0; i--) {
407             swap_tmp = nodes[rnd[i] % (i + 1)];
408             nodes[rnd[i] % (i + 1)] = nodes[i];
409             nodes[i] = swap_tmp;
410           }
411 
412           /* relink list in the new order */
413           for(i = 1; i < num_addrs; i++) {
414             nodes[i-1]->ai_next = nodes[i];
415           }
416 
417           nodes[num_addrs-1]->ai_next = NULL;
418           *addr = nodes[0];
419         }
420         free(rnd);
421       }
422       else
423         result = CURLE_OUT_OF_MEMORY;
424       free(nodes);
425     }
426     else
427       result = CURLE_OUT_OF_MEMORY;
428   }
429   return result;
430 }
431 #endif
432 
433 /*
434  * Curl_cache_addr() stores a 'Curl_addrinfo' struct in the DNS cache.
435  *
436  * When calling Curl_resolv() has resulted in a response with a returned
437  * address, we call this function to store the information in the dns
438  * cache etc
439  *
440  * Returns the Curl_dns_entry entry pointer or NULL if the storage failed.
441  */
442 struct Curl_dns_entry *
Curl_cache_addr(struct Curl_easy * data,struct Curl_addrinfo * addr,const char * hostname,size_t hostlen,int port)443 Curl_cache_addr(struct Curl_easy *data,
444                 struct Curl_addrinfo *addr,
445                 const char *hostname,
446                 size_t hostlen, /* length or zero */
447                 int port)
448 {
449   char entry_id[MAX_HOSTCACHE_LEN];
450   size_t entry_len;
451   struct Curl_dns_entry *dns;
452   struct Curl_dns_entry *dns2;
453 
454 #ifndef CURL_DISABLE_SHUFFLE_DNS
455   /* shuffle addresses if requested */
456   if(data->set.dns_shuffle_addresses) {
457     CURLcode result = Curl_shuffle_addr(data, &addr);
458     if(result)
459       return NULL;
460   }
461 #endif
462 
463   /* Create a new cache entry */
464   dns = calloc(1, sizeof(struct Curl_dns_entry));
465   if(!dns) {
466     return NULL;
467   }
468 
469   /* Create an entry id, based upon the hostname and port */
470   entry_len = create_hostcache_id(hostname, hostlen, port,
471                                   entry_id, sizeof(entry_id));
472 
473   dns->inuse = 1;   /* the cache has the first reference */
474   dns->addr = addr; /* this is the address(es) */
475   time(&dns->timestamp);
476   if(dns->timestamp == 0)
477     dns->timestamp = 1;   /* zero indicates permanent CURLOPT_RESOLVE entry */
478 
479   /* Store the resolved data in our DNS cache. */
480   dns2 = Curl_hash_add(data->dns.hostcache, entry_id, entry_len + 1,
481                        (void *)dns);
482   if(!dns2) {
483     free(dns);
484     return NULL;
485   }
486 
487   dns = dns2;
488   dns->inuse++;         /* mark entry as in-use */
489   return dns;
490 }
491 
492 #ifdef ENABLE_IPV6
493 /* return a static IPv6 ::1 for the name */
get_localhost6(int port,const char * name)494 static struct Curl_addrinfo *get_localhost6(int port, const char *name)
495 {
496   struct Curl_addrinfo *ca;
497   const size_t ss_size = sizeof(struct sockaddr_in6);
498   const size_t hostlen = strlen(name);
499   struct sockaddr_in6 sa6;
500   unsigned char ipv6[16];
501   unsigned short port16 = (unsigned short)(port & 0xffff);
502   ca = calloc(sizeof(struct Curl_addrinfo) + ss_size + hostlen + 1, 1);
503   if(!ca)
504     return NULL;
505 
506   sa6.sin6_family = AF_INET6;
507   sa6.sin6_port = htons(port16);
508   sa6.sin6_flowinfo = 0;
509   sa6.sin6_scope_id = 0;
510   if(Curl_inet_pton(AF_INET6, "::1", ipv6) < 1)
511     return NULL;
512   memcpy(&sa6.sin6_addr, ipv6, sizeof(ipv6));
513 
514   ca->ai_flags     = 0;
515   ca->ai_family    = AF_INET6;
516   ca->ai_socktype  = SOCK_STREAM;
517   ca->ai_protocol  = IPPROTO_TCP;
518   ca->ai_addrlen   = (curl_socklen_t)ss_size;
519   ca->ai_next      = NULL;
520   ca->ai_addr = (void *)((char *)ca + sizeof(struct Curl_addrinfo));
521   memcpy(ca->ai_addr, &sa6, ss_size);
522   ca->ai_canonname = (char *)ca->ai_addr + ss_size;
523   strcpy(ca->ai_canonname, name);
524   return ca;
525 }
526 #else
527 #define get_localhost6(x,y) NULL
528 #endif
529 
530 /* return a static IPv4 127.0.0.1 for the given name */
get_localhost(int port,const char * name)531 static struct Curl_addrinfo *get_localhost(int port, const char *name)
532 {
533   struct Curl_addrinfo *ca;
534   const size_t ss_size = sizeof(struct sockaddr_in);
535   const size_t hostlen = strlen(name);
536   struct sockaddr_in sa;
537   unsigned int ipv4;
538   unsigned short port16 = (unsigned short)(port & 0xffff);
539 
540   /* memset to clear the sa.sin_zero field */
541   memset(&sa, 0, sizeof(sa));
542   sa.sin_family = AF_INET;
543   sa.sin_port = htons(port16);
544   if(Curl_inet_pton(AF_INET, "127.0.0.1", (char *)&ipv4) < 1)
545     return NULL;
546   memcpy(&sa.sin_addr, &ipv4, sizeof(ipv4));
547 
548   ca = calloc(sizeof(struct Curl_addrinfo) + ss_size + hostlen + 1, 1);
549   if(!ca)
550     return NULL;
551   ca->ai_flags     = 0;
552   ca->ai_family    = AF_INET;
553   ca->ai_socktype  = SOCK_STREAM;
554   ca->ai_protocol  = IPPROTO_TCP;
555   ca->ai_addrlen   = (curl_socklen_t)ss_size;
556   ca->ai_addr = (void *)((char *)ca + sizeof(struct Curl_addrinfo));
557   memcpy(ca->ai_addr, &sa, ss_size);
558   ca->ai_canonname = (char *)ca->ai_addr + ss_size;
559   strcpy(ca->ai_canonname, name);
560   ca->ai_next = get_localhost6(port, name);
561   return ca;
562 }
563 
564 #ifdef ENABLE_IPV6
565 /*
566  * Curl_ipv6works() returns TRUE if IPv6 seems to work.
567  */
Curl_ipv6works(struct Curl_easy * data)568 bool Curl_ipv6works(struct Curl_easy *data)
569 {
570   if(data) {
571     /* the nature of most system is that IPv6 status doesn't come and go
572        during a program's lifetime so we only probe the first time and then we
573        have the info kept for fast re-use */
574     DEBUGASSERT(data);
575     DEBUGASSERT(data->multi);
576     if(data->multi->ipv6_up == IPV6_UNKNOWN) {
577       bool works = Curl_ipv6works(NULL);
578       data->multi->ipv6_up = works ? IPV6_WORKS : IPV6_DEAD;
579     }
580     return data->multi->ipv6_up == IPV6_WORKS;
581   }
582   else {
583     int ipv6_works = -1;
584     /* probe to see if we have a working IPv6 stack */
585     curl_socket_t s = socket(PF_INET6, SOCK_DGRAM, 0);
586     if(s == CURL_SOCKET_BAD)
587       /* an IPv6 address was requested but we can't get/use one */
588       ipv6_works = 0;
589     else {
590       ipv6_works = 1;
591       sclose(s);
592     }
593     return (ipv6_works>0)?TRUE:FALSE;
594   }
595 }
596 #endif /* ENABLE_IPV6 */
597 
598 /*
599  * Curl_host_is_ipnum() returns TRUE if the given string is a numerical IPv4
600  * (or IPv6 if supported) address.
601  */
Curl_host_is_ipnum(const char * hostname)602 bool Curl_host_is_ipnum(const char *hostname)
603 {
604   struct in_addr in;
605 #ifdef ENABLE_IPV6
606   struct in6_addr in6;
607 #endif
608   if(Curl_inet_pton(AF_INET, hostname, &in) > 0
609 #ifdef ENABLE_IPV6
610      || Curl_inet_pton(AF_INET6, hostname, &in6) > 0
611 #endif
612     )
613     return TRUE;
614   return FALSE;
615 }
616 
617 
618 /* return TRUE if 'part' is a case insensitive tail of 'full' */
tailmatch(const char * full,const char * part)619 static bool tailmatch(const char *full, const char *part)
620 {
621   size_t plen = strlen(part);
622   size_t flen = strlen(full);
623   if(plen > flen)
624     return FALSE;
625   return strncasecompare(part, &full[flen - plen], plen);
626 }
627 
628 /*
629  * Curl_resolv() is the main name resolve function within libcurl. It resolves
630  * a name and returns a pointer to the entry in the 'entry' argument (if one
631  * is provided). This function might return immediately if we're using asynch
632  * resolves. See the return codes.
633  *
634  * The cache entry we return will get its 'inuse' counter increased when this
635  * function is used. You MUST call Curl_resolv_unlock() later (when you're
636  * done using this struct) to decrease the counter again.
637  *
638  * Return codes:
639  *
640  * CURLRESOLV_ERROR   (-1) = error, no pointer
641  * CURLRESOLV_RESOLVED (0) = OK, pointer provided
642  * CURLRESOLV_PENDING  (1) = waiting for response, no pointer
643  */
644 
Curl_resolv(struct Curl_easy * data,const char * hostname,int port,bool allowDOH,struct Curl_dns_entry ** entry)645 enum resolve_t Curl_resolv(struct Curl_easy *data,
646                            const char *hostname,
647                            int port,
648                            bool allowDOH,
649                            struct Curl_dns_entry **entry)
650 {
651   struct Curl_dns_entry *dns = NULL;
652   CURLcode result;
653   enum resolve_t rc = CURLRESOLV_ERROR; /* default to failure */
654   struct connectdata *conn = data->conn;
655   *entry = NULL;
656 #ifndef CURL_DISABLE_DOH
657   conn->bits.doh = FALSE; /* default is not */
658 #else
659   (void)allowDOH;
660 #endif
661 
662   if(data->share)
663     Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
664 
665   dns = fetch_addr(data, hostname, port);
666 
667   if(dns) {
668     infof(data, "Hostname %s was found in DNS cache", hostname);
669     dns->inuse++; /* we use it! */
670     rc = CURLRESOLV_RESOLVED;
671   }
672 
673   if(data->share)
674     Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
675 
676   if(!dns) {
677     /* The entry was not in the cache. Resolve it to IP address */
678 
679     struct Curl_addrinfo *addr = NULL;
680     int respwait = 0;
681 #if !defined(CURL_DISABLE_DOH) || !defined(USE_RESOLVE_ON_IPS)
682     struct in_addr in;
683 #endif
684 #ifndef CURL_DISABLE_DOH
685 #ifndef USE_RESOLVE_ON_IPS
686     const
687 #endif
688       bool ipnum = FALSE;
689 #endif
690 
691     /* notify the resolver start callback */
692     if(data->set.resolver_start) {
693       int st;
694       Curl_set_in_callback(data, true);
695       st = data->set.resolver_start(
696 #ifdef USE_CURL_ASYNC
697         data->state.async.resolver,
698 #else
699         NULL,
700 #endif
701         NULL,
702         data->set.resolver_start_client);
703       Curl_set_in_callback(data, false);
704       if(st)
705         return CURLRESOLV_ERROR;
706     }
707 
708 #if defined(ENABLE_IPV6) && defined(CURL_OSX_CALL_COPYPROXIES)
709     {
710       /*
711        * The automagic conversion from IPv4 literals to IPv6 literals only
712        * works if the SCDynamicStoreCopyProxies system function gets called
713        * first. As Curl currently doesn't support system-wide HTTP proxies, we
714        * therefore don't use any value this function might return.
715        *
716        * This function is only available on a macOS and is not needed for
717        * IPv4-only builds, hence the conditions above.
718        */
719       CFDictionaryRef dict = SCDynamicStoreCopyProxies(NULL);
720       if(dict)
721         CFRelease(dict);
722     }
723 #endif
724 
725 #ifndef USE_RESOLVE_ON_IPS
726     /* First check if this is an IPv4 address string */
727     if(Curl_inet_pton(AF_INET, hostname, &in) > 0)
728       /* This is a dotted IP address 123.123.123.123-style */
729       addr = Curl_ip2addr(AF_INET, &in, hostname, port);
730 #ifdef ENABLE_IPV6
731     if(!addr) {
732       struct in6_addr in6;
733       /* check if this is an IPv6 address string */
734       if(Curl_inet_pton(AF_INET6, hostname, &in6) > 0)
735         /* This is an IPv6 address literal */
736         addr = Curl_ip2addr(AF_INET6, &in6, hostname, port);
737     }
738 #endif /* ENABLE_IPV6 */
739 
740 #else /* if USE_RESOLVE_ON_IPS */
741 #ifndef CURL_DISABLE_DOH
742     /* First check if this is an IPv4 address string */
743     if(Curl_inet_pton(AF_INET, hostname, &in) > 0)
744       /* This is a dotted IP address 123.123.123.123-style */
745       ipnum = TRUE;
746 #ifdef ENABLE_IPV6
747     else {
748       struct in6_addr in6;
749       /* check if this is an IPv6 address string */
750       if(Curl_inet_pton(AF_INET6, hostname, &in6) > 0)
751         /* This is an IPv6 address literal */
752         ipnum = TRUE;
753     }
754 #endif /* ENABLE_IPV6 */
755 #endif /* CURL_DISABLE_DOH */
756 
757 #endif /* !USE_RESOLVE_ON_IPS */
758 
759     if(!addr) {
760       if(conn->ip_version == CURL_IPRESOLVE_V6 && !Curl_ipv6works(data))
761         return CURLRESOLV_ERROR;
762 
763       if(strcasecompare(hostname, "localhost") ||
764          tailmatch(hostname, ".localhost"))
765         addr = get_localhost(port, hostname);
766 #ifndef CURL_DISABLE_DOH
767       else if(allowDOH && data->set.doh && !ipnum)
768         addr = Curl_doh(data, hostname, port, &respwait);
769 #endif
770       else {
771         /* Check what IP specifics the app has requested and if we can provide
772          * it. If not, bail out. */
773         if(!Curl_ipvalid(data, conn))
774           return CURLRESOLV_ERROR;
775         /* If Curl_getaddrinfo() returns NULL, 'respwait' might be set to a
776            non-zero value indicating that we need to wait for the response to
777            the resolve call */
778         addr = Curl_getaddrinfo(data, hostname, port, &respwait);
779       }
780     }
781     if(!addr) {
782       if(respwait) {
783         /* the response to our resolve call will come asynchronously at
784            a later time, good or bad */
785         /* First, check that we haven't received the info by now */
786         result = Curl_resolv_check(data, &dns);
787         if(result) /* error detected */
788           return CURLRESOLV_ERROR;
789         if(dns)
790           rc = CURLRESOLV_RESOLVED; /* pointer provided */
791         else
792           rc = CURLRESOLV_PENDING; /* no info yet */
793       }
794     }
795     else {
796       if(data->share)
797         Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
798 
799       /* we got a response, store it in the cache */
800       dns = Curl_cache_addr(data, addr, hostname, 0, port);
801 
802       if(data->share)
803         Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
804 
805       if(!dns)
806         /* returned failure, bail out nicely */
807         Curl_freeaddrinfo(addr);
808       else
809         rc = CURLRESOLV_RESOLVED;
810     }
811   }
812 
813   *entry = dns;
814 
815   return rc;
816 }
817 
818 #ifdef USE_ALARM_TIMEOUT
819 /*
820  * This signal handler jumps back into the main libcurl code and continues
821  * execution.  This effectively causes the remainder of the application to run
822  * within a signal handler which is nonportable and could lead to problems.
823  */
824 static
alarmfunc(int sig)825 void alarmfunc(int sig)
826 {
827   /* this is for "-ansi -Wall -pedantic" to stop complaining!   (rabe) */
828   (void)sig;
829   siglongjmp(curl_jmpenv, 1);
830 }
831 #endif /* USE_ALARM_TIMEOUT */
832 
833 /*
834  * Curl_resolv_timeout() is the same as Curl_resolv() but specifies a
835  * timeout.  This function might return immediately if we're using asynch
836  * resolves. See the return codes.
837  *
838  * The cache entry we return will get its 'inuse' counter increased when this
839  * function is used. You MUST call Curl_resolv_unlock() later (when you're
840  * done using this struct) to decrease the counter again.
841  *
842  * If built with a synchronous resolver and use of signals is not
843  * disabled by the application, then a nonzero timeout will cause a
844  * timeout after the specified number of milliseconds. Otherwise, timeout
845  * is ignored.
846  *
847  * Return codes:
848  *
849  * CURLRESOLV_TIMEDOUT(-2) = warning, time too short or previous alarm expired
850  * CURLRESOLV_ERROR   (-1) = error, no pointer
851  * CURLRESOLV_RESOLVED (0) = OK, pointer provided
852  * CURLRESOLV_PENDING  (1) = waiting for response, no pointer
853  */
854 
Curl_resolv_timeout(struct Curl_easy * data,const char * hostname,int port,struct Curl_dns_entry ** entry,timediff_t timeoutms)855 enum resolve_t Curl_resolv_timeout(struct Curl_easy *data,
856                                    const char *hostname,
857                                    int port,
858                                    struct Curl_dns_entry **entry,
859                                    timediff_t timeoutms)
860 {
861 #ifdef USE_ALARM_TIMEOUT
862 #ifdef HAVE_SIGACTION
863   struct sigaction keep_sigact;   /* store the old struct here */
864   volatile bool keep_copysig = FALSE; /* whether old sigact has been saved */
865   struct sigaction sigact;
866 #else
867 #ifdef HAVE_SIGNAL
868   void (*keep_sigact)(int);       /* store the old handler here */
869 #endif /* HAVE_SIGNAL */
870 #endif /* HAVE_SIGACTION */
871   volatile long timeout;
872   volatile unsigned int prev_alarm = 0;
873 #endif /* USE_ALARM_TIMEOUT */
874   enum resolve_t rc;
875 
876   *entry = NULL;
877 
878   if(timeoutms < 0)
879     /* got an already expired timeout */
880     return CURLRESOLV_TIMEDOUT;
881 
882 #ifdef USE_ALARM_TIMEOUT
883   if(data->set.no_signal)
884     /* Ignore the timeout when signals are disabled */
885     timeout = 0;
886   else
887     timeout = (timeoutms > LONG_MAX) ? LONG_MAX : (long)timeoutms;
888 
889   if(!timeout)
890     /* USE_ALARM_TIMEOUT defined, but no timeout actually requested */
891     return Curl_resolv(data, hostname, port, TRUE, entry);
892 
893   if(timeout < 1000) {
894     /* The alarm() function only provides integer second resolution, so if
895        we want to wait less than one second we must bail out already now. */
896     failf(data,
897         "remaining timeout of %ld too small to resolve via SIGALRM method",
898         timeout);
899     return CURLRESOLV_TIMEDOUT;
900   }
901   /* This allows us to time-out from the name resolver, as the timeout
902      will generate a signal and we will siglongjmp() from that here.
903      This technique has problems (see alarmfunc).
904      This should be the last thing we do before calling Curl_resolv(),
905      as otherwise we'd have to worry about variables that get modified
906      before we invoke Curl_resolv() (and thus use "volatile"). */
907   if(sigsetjmp(curl_jmpenv, 1)) {
908     /* this is coming from a siglongjmp() after an alarm signal */
909     failf(data, "name lookup timed out");
910     rc = CURLRESOLV_ERROR;
911     goto clean_up;
912   }
913   else {
914     /*************************************************************
915      * Set signal handler to catch SIGALRM
916      * Store the old value to be able to set it back later!
917      *************************************************************/
918 #ifdef HAVE_SIGACTION
919     sigaction(SIGALRM, NULL, &sigact);
920     keep_sigact = sigact;
921     keep_copysig = TRUE; /* yes, we have a copy */
922     sigact.sa_handler = alarmfunc;
923 #ifdef SA_RESTART
924     /* HPUX doesn't have SA_RESTART but defaults to that behavior! */
925     sigact.sa_flags &= ~SA_RESTART;
926 #endif
927     /* now set the new struct */
928     sigaction(SIGALRM, &sigact, NULL);
929 #else /* HAVE_SIGACTION */
930     /* no sigaction(), revert to the much lamer signal() */
931 #ifdef HAVE_SIGNAL
932     keep_sigact = signal(SIGALRM, alarmfunc);
933 #endif
934 #endif /* HAVE_SIGACTION */
935 
936     /* alarm() makes a signal get sent when the timeout fires off, and that
937        will abort system calls */
938     prev_alarm = alarm(curlx_sltoui(timeout/1000L));
939   }
940 
941 #else
942 #ifndef CURLRES_ASYNCH
943   if(timeoutms)
944     infof(data, "timeout on name lookup is not supported");
945 #else
946   (void)timeoutms; /* timeoutms not used with an async resolver */
947 #endif
948 #endif /* USE_ALARM_TIMEOUT */
949 
950   /* Perform the actual name resolution. This might be interrupted by an
951    * alarm if it takes too long.
952    */
953   rc = Curl_resolv(data, hostname, port, TRUE, entry);
954 
955 #ifdef USE_ALARM_TIMEOUT
956 clean_up:
957 
958   if(!prev_alarm)
959     /* deactivate a possibly active alarm before uninstalling the handler */
960     alarm(0);
961 
962 #ifdef HAVE_SIGACTION
963   if(keep_copysig) {
964     /* we got a struct as it looked before, now put that one back nice
965        and clean */
966     sigaction(SIGALRM, &keep_sigact, NULL); /* put it back */
967   }
968 #else
969 #ifdef HAVE_SIGNAL
970   /* restore the previous SIGALRM handler */
971   signal(SIGALRM, keep_sigact);
972 #endif
973 #endif /* HAVE_SIGACTION */
974 
975   /* switch back the alarm() to either zero or to what it was before minus
976      the time we spent until now! */
977   if(prev_alarm) {
978     /* there was an alarm() set before us, now put it back */
979     timediff_t elapsed_secs = Curl_timediff(Curl_now(),
980                                             data->conn->created) / 1000;
981 
982     /* the alarm period is counted in even number of seconds */
983     unsigned long alarm_set = (unsigned long)(prev_alarm - elapsed_secs);
984 
985     if(!alarm_set ||
986        ((alarm_set >= 0x80000000) && (prev_alarm < 0x80000000)) ) {
987       /* if the alarm time-left reached zero or turned "negative" (counted
988          with unsigned values), we should fire off a SIGALRM here, but we
989          won't, and zero would be to switch it off so we never set it to
990          less than 1! */
991       alarm(1);
992       rc = CURLRESOLV_TIMEDOUT;
993       failf(data, "Previous alarm fired off");
994     }
995     else
996       alarm((unsigned int)alarm_set);
997   }
998 #endif /* USE_ALARM_TIMEOUT */
999 
1000   return rc;
1001 }
1002 
1003 /*
1004  * Curl_resolv_unlock() unlocks the given cached DNS entry. When this has been
1005  * made, the struct may be destroyed due to pruning. It is important that only
1006  * one unlock is made for each Curl_resolv() call.
1007  *
1008  * May be called with 'data' == NULL for global cache.
1009  */
Curl_resolv_unlock(struct Curl_easy * data,struct Curl_dns_entry * dns)1010 void Curl_resolv_unlock(struct Curl_easy *data, struct Curl_dns_entry *dns)
1011 {
1012   if(data && data->share)
1013     Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
1014 
1015   freednsentry(dns);
1016 
1017   if(data && data->share)
1018     Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
1019 }
1020 
1021 /*
1022  * File-internal: release cache dns entry reference, free if inuse drops to 0
1023  */
freednsentry(void * freethis)1024 static void freednsentry(void *freethis)
1025 {
1026   struct Curl_dns_entry *dns = (struct Curl_dns_entry *) freethis;
1027   DEBUGASSERT(dns && (dns->inuse>0));
1028 
1029   dns->inuse--;
1030   if(dns->inuse == 0) {
1031     Curl_freeaddrinfo(dns->addr);
1032     free(dns);
1033   }
1034 }
1035 
1036 /*
1037  * Curl_init_dnscache() inits a new DNS cache.
1038  */
Curl_init_dnscache(struct Curl_hash * hash,int size)1039 void Curl_init_dnscache(struct Curl_hash *hash, int size)
1040 {
1041   Curl_hash_init(hash, size, Curl_hash_str, Curl_str_key_compare,
1042                  freednsentry);
1043 }
1044 
1045 /*
1046  * Curl_hostcache_clean()
1047  *
1048  * This _can_ be called with 'data' == NULL but then of course no locking
1049  * can be done!
1050  */
1051 
Curl_hostcache_clean(struct Curl_easy * data,struct Curl_hash * hash)1052 void Curl_hostcache_clean(struct Curl_easy *data,
1053                           struct Curl_hash *hash)
1054 {
1055   if(data && data->share)
1056     Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
1057 
1058   Curl_hash_clean(hash);
1059 
1060   if(data && data->share)
1061     Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
1062 }
1063 
1064 
Curl_loadhostpairs(struct Curl_easy * data)1065 CURLcode Curl_loadhostpairs(struct Curl_easy *data)
1066 {
1067   struct curl_slist *hostp;
1068   char *host_end;
1069 
1070   /* Default is no wildcard found */
1071   data->state.wildcard_resolve = false;
1072 
1073   for(hostp = data->state.resolve; hostp; hostp = hostp->next) {
1074     char entry_id[MAX_HOSTCACHE_LEN];
1075     if(!hostp->data)
1076       continue;
1077     if(hostp->data[0] == '-') {
1078       unsigned long num = 0;
1079       size_t entry_len;
1080       size_t hlen = 0;
1081       host_end = strchr(&hostp->data[1], ':');
1082 
1083       if(host_end) {
1084         hlen = host_end - &hostp->data[1];
1085         num = strtoul(++host_end, NULL, 10);
1086         if(!hlen || (num > 0xffff))
1087           host_end = NULL;
1088       }
1089       if(!host_end) {
1090         infof(data, "Bad syntax CURLOPT_RESOLVE removal entry '%s'",
1091               hostp->data);
1092         continue;
1093       }
1094       /* Create an entry id, based upon the hostname and port */
1095       entry_len = create_hostcache_id(&hostp->data[1], hlen, (int)num,
1096                                       entry_id, sizeof(entry_id));
1097       if(data->share)
1098         Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
1099 
1100       /* delete entry, ignore if it didn't exist */
1101       Curl_hash_delete(data->dns.hostcache, entry_id, entry_len + 1);
1102 
1103       if(data->share)
1104         Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
1105     }
1106     else {
1107       struct Curl_dns_entry *dns;
1108       struct Curl_addrinfo *head = NULL, *tail = NULL;
1109       size_t entry_len;
1110       char address[64];
1111 #if !defined(CURL_DISABLE_VERBOSE_STRINGS)
1112       char *addresses = NULL;
1113 #endif
1114       char *addr_begin;
1115       char *addr_end;
1116       char *port_ptr;
1117       int port = 0;
1118       char *end_ptr;
1119       bool permanent = TRUE;
1120       unsigned long tmp_port;
1121       bool error = true;
1122       char *host_begin = hostp->data;
1123       size_t hlen = 0;
1124 
1125       if(host_begin[0] == '+') {
1126         host_begin++;
1127         permanent = FALSE;
1128       }
1129       host_end = strchr(host_begin, ':');
1130       if(!host_end)
1131         goto err;
1132       hlen = host_end - host_begin;
1133 
1134       port_ptr = host_end + 1;
1135       tmp_port = strtoul(port_ptr, &end_ptr, 10);
1136       if(tmp_port > USHRT_MAX || end_ptr == port_ptr || *end_ptr != ':')
1137         goto err;
1138 
1139       port = (int)tmp_port;
1140 #if !defined(CURL_DISABLE_VERBOSE_STRINGS)
1141       addresses = end_ptr + 1;
1142 #endif
1143 
1144       while(*end_ptr) {
1145         size_t alen;
1146         struct Curl_addrinfo *ai;
1147 
1148         addr_begin = end_ptr + 1;
1149         addr_end = strchr(addr_begin, ',');
1150         if(!addr_end)
1151           addr_end = addr_begin + strlen(addr_begin);
1152         end_ptr = addr_end;
1153 
1154         /* allow IP(v6) address within [brackets] */
1155         if(*addr_begin == '[') {
1156           if(addr_end == addr_begin || *(addr_end - 1) != ']')
1157             goto err;
1158           ++addr_begin;
1159           --addr_end;
1160         }
1161 
1162         alen = addr_end - addr_begin;
1163         if(!alen)
1164           continue;
1165 
1166         if(alen >= sizeof(address))
1167           goto err;
1168 
1169         memcpy(address, addr_begin, alen);
1170         address[alen] = '\0';
1171 
1172 #ifndef ENABLE_IPV6
1173         if(strchr(address, ':')) {
1174           infof(data, "Ignoring resolve address '%s', missing IPv6 support.",
1175                 address);
1176           continue;
1177         }
1178 #endif
1179 
1180         ai = Curl_str2addr(address, port);
1181         if(!ai) {
1182           infof(data, "Resolve address '%s' found illegal", address);
1183           goto err;
1184         }
1185 
1186         if(tail) {
1187           tail->ai_next = ai;
1188           tail = tail->ai_next;
1189         }
1190         else {
1191           head = tail = ai;
1192         }
1193       }
1194 
1195       if(!head)
1196         goto err;
1197 
1198       error = false;
1199    err:
1200       if(error) {
1201         failf(data, "Couldn't parse CURLOPT_RESOLVE entry '%s'",
1202               hostp->data);
1203         Curl_freeaddrinfo(head);
1204         return CURLE_SETOPT_OPTION_SYNTAX;
1205       }
1206 
1207       /* Create an entry id, based upon the hostname and port */
1208       entry_len = create_hostcache_id(host_begin, hlen, port,
1209                                       entry_id, sizeof(entry_id));
1210 
1211       if(data->share)
1212         Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
1213 
1214       /* See if it's already in our dns cache */
1215       dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len + 1);
1216 
1217       if(dns) {
1218         infof(data, "RESOLVE %.*s:%d is - old addresses discarded",
1219               (int)hlen, host_begin, port);
1220         /* delete old entry, there are two reasons for this
1221          1. old entry may have different addresses.
1222          2. even if entry with correct addresses is already in the cache,
1223             but if it is close to expire, then by the time next http
1224             request is made, it can get expired and pruned because old
1225             entry is not necessarily marked as permanent.
1226          3. when adding a non-permanent entry, we want it to remove and
1227             replace an existing permanent entry.
1228          4. when adding a non-permanent entry, we want it to get a "fresh"
1229             timeout that starts _now_. */
1230 
1231         Curl_hash_delete(data->dns.hostcache, entry_id, entry_len + 1);
1232       }
1233 
1234       /* put this new host in the cache */
1235       dns = Curl_cache_addr(data, head, host_begin, hlen, port);
1236       if(dns) {
1237         if(permanent)
1238           dns->timestamp = 0; /* mark as permanent */
1239         /* release the returned reference; the cache itself will keep the
1240          * entry alive: */
1241         dns->inuse--;
1242       }
1243 
1244       if(data->share)
1245         Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
1246 
1247       if(!dns) {
1248         Curl_freeaddrinfo(head);
1249         return CURLE_OUT_OF_MEMORY;
1250       }
1251       infof(data, "Added %.*s:%d:%s to DNS cache%s",
1252             (int)hlen, host_begin, port, addresses,
1253             permanent ? "" : " (non-permanent)");
1254 
1255       /* Wildcard hostname */
1256       if((hlen == 1) && (host_begin[0] == '*')) {
1257         infof(data, "RESOLVE *:%d using wildcard", port);
1258         data->state.wildcard_resolve = true;
1259       }
1260     }
1261   }
1262   data->state.resolve = NULL; /* dealt with now */
1263 
1264   return CURLE_OK;
1265 }
1266 
Curl_resolv_check(struct Curl_easy * data,struct Curl_dns_entry ** dns)1267 CURLcode Curl_resolv_check(struct Curl_easy *data,
1268                            struct Curl_dns_entry **dns)
1269 {
1270 #if defined(CURL_DISABLE_DOH) && !defined(CURLRES_ASYNCH)
1271   (void)data;
1272   (void)dns;
1273 #endif
1274 #ifndef CURL_DISABLE_DOH
1275   if(data->conn->bits.doh)
1276     return Curl_doh_is_resolved(data, dns);
1277 #endif
1278   return Curl_resolver_is_resolved(data, dns);
1279 }
1280 
Curl_resolv_getsock(struct Curl_easy * data,curl_socket_t * socks)1281 int Curl_resolv_getsock(struct Curl_easy *data,
1282                         curl_socket_t *socks)
1283 {
1284 #ifdef CURLRES_ASYNCH
1285 #ifndef CURL_DISABLE_DOH
1286   if(data->conn->bits.doh)
1287     /* nothing to wait for during DoH resolve, those handles have their own
1288        sockets */
1289     return GETSOCK_BLANK;
1290 #endif
1291   return Curl_resolver_getsock(data, socks);
1292 #else
1293   (void)data;
1294   (void)socks;
1295   return GETSOCK_BLANK;
1296 #endif
1297 }
1298 
1299 /* Call this function after Curl_connect() has returned async=TRUE and
1300    then a successful name resolve has been received.
1301 
1302    Note: this function disconnects and frees the conn data in case of
1303    resolve failure */
Curl_once_resolved(struct Curl_easy * data,bool * protocol_done)1304 CURLcode Curl_once_resolved(struct Curl_easy *data, bool *protocol_done)
1305 {
1306   CURLcode result;
1307   struct connectdata *conn = data->conn;
1308 
1309 #ifdef USE_CURL_ASYNC
1310   if(data->state.async.dns) {
1311     conn->dns_entry = data->state.async.dns;
1312     data->state.async.dns = NULL;
1313   }
1314 #endif
1315 
1316   result = Curl_setup_conn(data, protocol_done);
1317 
1318   if(result) {
1319     Curl_detach_connection(data);
1320     Curl_conncache_remove_conn(data, conn, TRUE);
1321     Curl_disconnect(data, conn, TRUE);
1322   }
1323   return result;
1324 }
1325 
1326 /*
1327  * Curl_resolver_error() calls failf() with the appropriate message after a
1328  * resolve error
1329  */
1330 
1331 #ifdef USE_CURL_ASYNC
Curl_resolver_error(struct Curl_easy * data)1332 CURLcode Curl_resolver_error(struct Curl_easy *data)
1333 {
1334   const char *host_or_proxy;
1335   CURLcode result;
1336 
1337 #ifndef CURL_DISABLE_PROXY
1338   struct connectdata *conn = data->conn;
1339   if(conn->bits.httpproxy) {
1340     host_or_proxy = "proxy";
1341     result = CURLE_COULDNT_RESOLVE_PROXY;
1342   }
1343   else
1344 #endif
1345   {
1346     host_or_proxy = "host";
1347     result = CURLE_COULDNT_RESOLVE_HOST;
1348   }
1349 
1350   failf(data, "Could not resolve %s: %s", host_or_proxy,
1351         data->state.async.hostname);
1352 
1353   return result;
1354 }
1355 #endif /* USE_CURL_ASYNC */
1356