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