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