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