1 /***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 1998 - 2019, 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.haxx.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 "multiif.h"
63 #include "doh.h"
64 #include "warnless.h"
65 /* The last 3 #include files should be in this order */
66 #include "curl_printf.h"
67 #include "curl_memory.h"
68 #include "memdebug.h"
69
70 #if defined(CURLRES_SYNCH) && \
71 defined(HAVE_ALARM) && defined(SIGALRM) && defined(HAVE_SIGSETJMP)
72 /* alarm-based timeouts can only be used with all the dependencies satisfied */
73 #define USE_ALARM_TIMEOUT
74 #endif
75
76 #define MAX_HOSTCACHE_LEN (255 + 7) /* max FQDN + colon + port number + zero */
77
78 /*
79 * hostip.c explained
80 * ==================
81 *
82 * The main COMPILE-TIME DEFINES to keep in mind when reading the host*.c
83 * source file are these:
84 *
85 * CURLRES_IPV6 - this host has getaddrinfo() and family, and thus we use
86 * that. The host may not be able to resolve IPv6, but we don't really have to
87 * take that into account. Hosts that aren't IPv6-enabled have CURLRES_IPV4
88 * defined.
89 *
90 * CURLRES_ARES - is defined if libcurl is built to use c-ares for
91 * asynchronous name resolves. This can be Windows or *nix.
92 *
93 * CURLRES_THREADED - is defined if libcurl is built to run under (native)
94 * Windows, and then the name resolve will be done in a new thread, and the
95 * supported API will be the same as for ares-builds.
96 *
97 * If any of the two previous are defined, CURLRES_ASYNCH is defined too. If
98 * libcurl is not built to use an asynchronous resolver, CURLRES_SYNCH is
99 * defined.
100 *
101 * The host*.c sources files are split up like this:
102 *
103 * hostip.c - method-independent resolver functions and utility functions
104 * hostasyn.c - functions for asynchronous name resolves
105 * hostsyn.c - functions for synchronous name resolves
106 * hostip4.c - IPv4 specific functions
107 * hostip6.c - IPv6 specific functions
108 *
109 * The two asynchronous name resolver backends are implemented in:
110 * asyn-ares.c - functions for ares-using name resolves
111 * asyn-thread.c - functions for threaded name resolves
112
113 * The hostip.h is the united header file for all this. It defines the
114 * CURLRES_* defines based on the config*.h and curl_setup.h defines.
115 */
116
117 static void freednsentry(void *freethis);
118
119 /*
120 * Return # of addresses in a Curl_addrinfo struct
121 */
Curl_num_addresses(const Curl_addrinfo * addr)122 int Curl_num_addresses(const Curl_addrinfo *addr)
123 {
124 int i = 0;
125 while(addr) {
126 addr = addr->ai_next;
127 i++;
128 }
129 return i;
130 }
131
132 /*
133 * Curl_printable_address() returns a printable version of the 1st address
134 * given in the 'ai' argument. The result will be stored in the buf that is
135 * bufsize bytes big.
136 *
137 * If the conversion fails, it returns NULL.
138 */
139 const char *
Curl_printable_address(const Curl_addrinfo * ai,char * buf,size_t bufsize)140 Curl_printable_address(const Curl_addrinfo *ai, char *buf, size_t bufsize)
141 {
142 const struct sockaddr_in *sa4;
143 const struct in_addr *ipaddr4;
144 #ifdef ENABLE_IPV6
145 const struct sockaddr_in6 *sa6;
146 const struct in6_addr *ipaddr6;
147 #endif
148
149 switch(ai->ai_family) {
150 case AF_INET:
151 sa4 = (const void *)ai->ai_addr;
152 ipaddr4 = &sa4->sin_addr;
153 return Curl_inet_ntop(ai->ai_family, (const void *)ipaddr4, buf,
154 bufsize);
155 #ifdef ENABLE_IPV6
156 case AF_INET6:
157 sa6 = (const void *)ai->ai_addr;
158 ipaddr6 = &sa6->sin6_addr;
159 return Curl_inet_ntop(ai->ai_family, (const void *)ipaddr6, buf,
160 bufsize);
161 #endif
162 default:
163 break;
164 }
165 return NULL;
166 }
167
168 /*
169 * Create a hostcache id string for the provided host + port, to be used by
170 * the DNS caching. Without alloc.
171 */
172 static void
create_hostcache_id(const char * name,int port,char * ptr,size_t buflen)173 create_hostcache_id(const char *name, int port, char *ptr, size_t buflen)
174 {
175 size_t len = strlen(name);
176 if(len > (buflen - 7))
177 len = buflen - 7;
178 /* store and lower case the name */
179 while(len--)
180 *ptr++ = (char)TOLOWER(*name++);
181 msnprintf(ptr, 7, ":%u", port);
182 }
183
184 struct hostcache_prune_data {
185 long cache_timeout;
186 time_t now;
187 };
188
189 /*
190 * This function is set as a callback to be called for every entry in the DNS
191 * cache when we want to prune old unused entries.
192 *
193 * Returning non-zero means remove the entry, return 0 to keep it in the
194 * cache.
195 */
196 static int
hostcache_timestamp_remove(void * datap,void * hc)197 hostcache_timestamp_remove(void *datap, void *hc)
198 {
199 struct hostcache_prune_data *data =
200 (struct hostcache_prune_data *) datap;
201 struct Curl_dns_entry *c = (struct Curl_dns_entry *) hc;
202
203 return (0 != c->timestamp)
204 && (data->now - c->timestamp >= data->cache_timeout);
205 }
206
207 /*
208 * Prune the DNS cache. This assumes that a lock has already been taken.
209 */
210 static void
hostcache_prune(struct curl_hash * hostcache,long cache_timeout,time_t now)211 hostcache_prune(struct curl_hash *hostcache, long cache_timeout, time_t now)
212 {
213 struct hostcache_prune_data user;
214
215 user.cache_timeout = cache_timeout;
216 user.now = now;
217
218 Curl_hash_clean_with_criterium(hostcache,
219 (void *) &user,
220 hostcache_timestamp_remove);
221 }
222
223 /*
224 * Library-wide function for pruning the DNS cache. This function takes and
225 * returns the appropriate locks.
226 */
Curl_hostcache_prune(struct Curl_easy * data)227 void Curl_hostcache_prune(struct Curl_easy *data)
228 {
229 time_t now;
230
231 if((data->set.dns_cache_timeout == -1) || !data->dns.hostcache)
232 /* cache forever means never prune, and NULL hostcache means
233 we can't do it */
234 return;
235
236 if(data->share)
237 Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
238
239 time(&now);
240
241 /* Remove outdated and unused entries from the hostcache */
242 hostcache_prune(data->dns.hostcache,
243 data->set.dns_cache_timeout,
244 now);
245
246 if(data->share)
247 Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
248 }
249
250 #ifdef HAVE_SIGSETJMP
251 /* Beware this is a global and unique instance. This is used to store the
252 return address that we can jump back to from inside a signal handler. This
253 is not thread-safe stuff. */
254 sigjmp_buf curl_jmpenv;
255 #endif
256
257 /* lookup address, returns entry if found and not stale */
258 static struct Curl_dns_entry *
fetch_addr(struct connectdata * conn,const char * hostname,int port)259 fetch_addr(struct connectdata *conn,
260 const char *hostname,
261 int port)
262 {
263 struct Curl_dns_entry *dns = NULL;
264 size_t entry_len;
265 struct Curl_easy *data = conn->data;
266 char entry_id[MAX_HOSTCACHE_LEN];
267
268 /* Create an entry id, based upon the hostname and port */
269 create_hostcache_id(hostname, port, entry_id, sizeof(entry_id));
270 entry_len = strlen(entry_id);
271
272 /* See if its already in our dns cache */
273 dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len + 1);
274
275 /* No entry found in cache, check if we might have a wildcard entry */
276 if(!dns && data->change.wildcard_resolve) {
277 create_hostcache_id("*", port, entry_id, sizeof(entry_id));
278 entry_len = strlen(entry_id);
279
280 /* See if it's already in our dns cache */
281 dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len + 1);
282 }
283
284 if(dns && (data->set.dns_cache_timeout != -1)) {
285 /* See whether the returned entry is stale. Done before we release lock */
286 struct hostcache_prune_data user;
287
288 time(&user.now);
289 user.cache_timeout = data->set.dns_cache_timeout;
290
291 if(hostcache_timestamp_remove(&user, dns)) {
292 infof(data, "Hostname in DNS cache was stale, zapped\n");
293 dns = NULL; /* the memory deallocation is being handled by the hash */
294 Curl_hash_delete(data->dns.hostcache, entry_id, entry_len + 1);
295 }
296 }
297
298 return dns;
299 }
300
301 /*
302 * Curl_fetch_addr() fetches a 'Curl_dns_entry' already in the DNS cache.
303 *
304 * Curl_resolv() checks initially and multi_runsingle() checks each time
305 * it discovers the handle in the state WAITRESOLVE whether the hostname
306 * has already been resolved and the address has already been stored in
307 * the DNS cache. This short circuits waiting for a lot of pending
308 * lookups for the same hostname requested by different handles.
309 *
310 * Returns the Curl_dns_entry entry pointer or NULL if not in the cache.
311 *
312 * The returned data *MUST* be "unlocked" with Curl_resolv_unlock() after
313 * use, or we'll leak memory!
314 */
315 struct Curl_dns_entry *
Curl_fetch_addr(struct connectdata * conn,const char * hostname,int port)316 Curl_fetch_addr(struct connectdata *conn,
317 const char *hostname,
318 int port)
319 {
320 struct Curl_easy *data = conn->data;
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(conn, 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 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,Curl_addrinfo ** addr)352 UNITTEST CURLcode Curl_shuffle_addr(struct Curl_easy *data,
353 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 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 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,Curl_addrinfo * addr,const char * hostname,int port)416 Curl_cache_addr(struct Curl_easy *data,
417 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 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 /*
465 * Curl_resolv() is the main name resolve function within libcurl. It resolves
466 * a name and returns a pointer to the entry in the 'entry' argument (if one
467 * is provided). This function might return immediately if we're using asynch
468 * resolves. See the return codes.
469 *
470 * The cache entry we return will get its 'inuse' counter increased when this
471 * function is used. You MUST call Curl_resolv_unlock() later (when you're
472 * done using this struct) to decrease the counter again.
473 *
474 * In debug mode, we specifically test for an interface name "LocalHost"
475 * and resolve "localhost" instead as a means to permit test cases
476 * to connect to a local test server with any host name.
477 *
478 * Return codes:
479 *
480 * CURLRESOLV_ERROR (-1) = error, no pointer
481 * CURLRESOLV_RESOLVED (0) = OK, pointer provided
482 * CURLRESOLV_PENDING (1) = waiting for response, no pointer
483 */
484
Curl_resolv(struct connectdata * conn,const char * hostname,int port,bool allowDOH,struct Curl_dns_entry ** entry)485 int Curl_resolv(struct connectdata *conn,
486 const char *hostname,
487 int port,
488 bool allowDOH,
489 struct Curl_dns_entry **entry)
490 {
491 struct Curl_dns_entry *dns = NULL;
492 struct Curl_easy *data = conn->data;
493 CURLcode result;
494 int rc = CURLRESOLV_ERROR; /* default to failure */
495
496 *entry = NULL;
497
498 if(data->share)
499 Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
500
501 dns = fetch_addr(conn, hostname, port);
502
503 if(dns) {
504 infof(data, "Hostname %s was found in DNS cache\n", hostname);
505 dns->inuse++; /* we use it! */
506 rc = CURLRESOLV_RESOLVED;
507 }
508
509 if(data->share)
510 Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
511
512 if(!dns) {
513 /* The entry was not in the cache. Resolve it to IP address */
514
515 Curl_addrinfo *addr;
516 int respwait = 0;
517
518 /* Check what IP specifics the app has requested and if we can provide it.
519 * If not, bail out. */
520 if(!Curl_ipvalid(conn))
521 return CURLRESOLV_ERROR;
522
523 /* notify the resolver start callback */
524 if(data->set.resolver_start) {
525 int st;
526 Curl_set_in_callback(data, true);
527 st = data->set.resolver_start(data->state.resolver, NULL,
528 data->set.resolver_start_client);
529 Curl_set_in_callback(data, false);
530 if(st)
531 return CURLRESOLV_ERROR;
532 }
533
534 if(allowDOH && data->set.doh) {
535 addr = Curl_doh(conn, hostname, port, &respwait);
536 }
537 else {
538 /* If Curl_getaddrinfo() returns NULL, 'respwait' might be set to a
539 non-zero value indicating that we need to wait for the response to the
540 resolve call */
541 addr = Curl_getaddrinfo(conn,
542 #ifdef DEBUGBUILD
543 (data->set.str[STRING_DEVICE]
544 && !strcmp(data->set.str[STRING_DEVICE],
545 "LocalHost"))?"localhost":
546 #endif
547 hostname, port, &respwait);
548 }
549 if(!addr) {
550 if(respwait) {
551 /* the response to our resolve call will come asynchronously at
552 a later time, good or bad */
553 /* First, check that we haven't received the info by now */
554 result = Curl_resolv_check(conn, &dns);
555 if(result) /* error detected */
556 return CURLRESOLV_ERROR;
557 if(dns)
558 rc = CURLRESOLV_RESOLVED; /* pointer provided */
559 else
560 rc = CURLRESOLV_PENDING; /* no info yet */
561 }
562 }
563 else {
564 if(data->share)
565 Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
566
567 /* we got a response, store it in the cache */
568 dns = Curl_cache_addr(data, addr, hostname, port);
569
570 if(data->share)
571 Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
572
573 if(!dns)
574 /* returned failure, bail out nicely */
575 Curl_freeaddrinfo(addr);
576 else
577 rc = CURLRESOLV_RESOLVED;
578 }
579 }
580
581 *entry = dns;
582
583 return rc;
584 }
585
586 #ifdef USE_ALARM_TIMEOUT
587 /*
588 * This signal handler jumps back into the main libcurl code and continues
589 * execution. This effectively causes the remainder of the application to run
590 * within a signal handler which is nonportable and could lead to problems.
591 */
592 static
alarmfunc(int sig)593 RETSIGTYPE alarmfunc(int sig)
594 {
595 /* this is for "-ansi -Wall -pedantic" to stop complaining! (rabe) */
596 (void)sig;
597 siglongjmp(curl_jmpenv, 1);
598 }
599 #endif /* USE_ALARM_TIMEOUT */
600
601 /*
602 * Curl_resolv_timeout() is the same as Curl_resolv() but specifies a
603 * timeout. This function might return immediately if we're using asynch
604 * resolves. See the return codes.
605 *
606 * The cache entry we return will get its 'inuse' counter increased when this
607 * function is used. You MUST call Curl_resolv_unlock() later (when you're
608 * done using this struct) to decrease the counter again.
609 *
610 * If built with a synchronous resolver and use of signals is not
611 * disabled by the application, then a nonzero timeout will cause a
612 * timeout after the specified number of milliseconds. Otherwise, timeout
613 * is ignored.
614 *
615 * Return codes:
616 *
617 * CURLRESOLV_TIMEDOUT(-2) = warning, time too short or previous alarm expired
618 * CURLRESOLV_ERROR (-1) = error, no pointer
619 * CURLRESOLV_RESOLVED (0) = OK, pointer provided
620 * CURLRESOLV_PENDING (1) = waiting for response, no pointer
621 */
622
Curl_resolv_timeout(struct connectdata * conn,const char * hostname,int port,struct Curl_dns_entry ** entry,timediff_t timeoutms)623 int Curl_resolv_timeout(struct connectdata *conn,
624 const char *hostname,
625 int port,
626 struct Curl_dns_entry **entry,
627 timediff_t timeoutms)
628 {
629 #ifdef USE_ALARM_TIMEOUT
630 #ifdef HAVE_SIGACTION
631 struct sigaction keep_sigact; /* store the old struct here */
632 volatile bool keep_copysig = FALSE; /* whether old sigact has been saved */
633 struct sigaction sigact;
634 #else
635 #ifdef HAVE_SIGNAL
636 void (*keep_sigact)(int); /* store the old handler here */
637 #endif /* HAVE_SIGNAL */
638 #endif /* HAVE_SIGACTION */
639 volatile long timeout;
640 volatile unsigned int prev_alarm = 0;
641 struct Curl_easy *data = conn->data;
642 #endif /* USE_ALARM_TIMEOUT */
643 int rc;
644
645 *entry = NULL;
646
647 if(timeoutms < 0)
648 /* got an already expired timeout */
649 return CURLRESOLV_TIMEDOUT;
650
651 #ifdef USE_ALARM_TIMEOUT
652 if(data->set.no_signal)
653 /* Ignore the timeout when signals are disabled */
654 timeout = 0;
655 else
656 timeout = (timeoutms > LONG_MAX) ? LONG_MAX : (long)timeoutms;
657
658 if(!timeout)
659 /* USE_ALARM_TIMEOUT defined, but no timeout actually requested */
660 return Curl_resolv(conn, hostname, port, TRUE, entry);
661
662 if(timeout < 1000) {
663 /* The alarm() function only provides integer second resolution, so if
664 we want to wait less than one second we must bail out already now. */
665 failf(data,
666 "remaining timeout of %ld too small to resolve via SIGALRM method",
667 timeout);
668 return CURLRESOLV_TIMEDOUT;
669 }
670 /* This allows us to time-out from the name resolver, as the timeout
671 will generate a signal and we will siglongjmp() from that here.
672 This technique has problems (see alarmfunc).
673 This should be the last thing we do before calling Curl_resolv(),
674 as otherwise we'd have to worry about variables that get modified
675 before we invoke Curl_resolv() (and thus use "volatile"). */
676 if(sigsetjmp(curl_jmpenv, 1)) {
677 /* this is coming from a siglongjmp() after an alarm signal */
678 failf(data, "name lookup timed out");
679 rc = CURLRESOLV_ERROR;
680 goto clean_up;
681 }
682 else {
683 /*************************************************************
684 * Set signal handler to catch SIGALRM
685 * Store the old value to be able to set it back later!
686 *************************************************************/
687 #ifdef HAVE_SIGACTION
688 sigaction(SIGALRM, NULL, &sigact);
689 keep_sigact = sigact;
690 keep_copysig = TRUE; /* yes, we have a copy */
691 sigact.sa_handler = alarmfunc;
692 #ifdef SA_RESTART
693 /* HPUX doesn't have SA_RESTART but defaults to that behaviour! */
694 sigact.sa_flags &= ~SA_RESTART;
695 #endif
696 /* now set the new struct */
697 sigaction(SIGALRM, &sigact, NULL);
698 #else /* HAVE_SIGACTION */
699 /* no sigaction(), revert to the much lamer signal() */
700 #ifdef HAVE_SIGNAL
701 keep_sigact = signal(SIGALRM, alarmfunc);
702 #endif
703 #endif /* HAVE_SIGACTION */
704
705 /* alarm() makes a signal get sent when the timeout fires off, and that
706 will abort system calls */
707 prev_alarm = alarm(curlx_sltoui(timeout/1000L));
708 }
709
710 #else
711 #ifndef CURLRES_ASYNCH
712 if(timeoutms)
713 infof(conn->data, "timeout on name lookup is not supported\n");
714 #else
715 (void)timeoutms; /* timeoutms not used with an async resolver */
716 #endif
717 #endif /* USE_ALARM_TIMEOUT */
718
719 /* Perform the actual name resolution. This might be interrupted by an
720 * alarm if it takes too long.
721 */
722 rc = Curl_resolv(conn, hostname, port, TRUE, entry);
723
724 #ifdef USE_ALARM_TIMEOUT
725 clean_up:
726
727 if(!prev_alarm)
728 /* deactivate a possibly active alarm before uninstalling the handler */
729 alarm(0);
730
731 #ifdef HAVE_SIGACTION
732 if(keep_copysig) {
733 /* we got a struct as it looked before, now put that one back nice
734 and clean */
735 sigaction(SIGALRM, &keep_sigact, NULL); /* put it back */
736 }
737 #else
738 #ifdef HAVE_SIGNAL
739 /* restore the previous SIGALRM handler */
740 signal(SIGALRM, keep_sigact);
741 #endif
742 #endif /* HAVE_SIGACTION */
743
744 /* switch back the alarm() to either zero or to what it was before minus
745 the time we spent until now! */
746 if(prev_alarm) {
747 /* there was an alarm() set before us, now put it back */
748 timediff_t elapsed_secs = Curl_timediff(Curl_now(),
749 conn->created) / 1000;
750
751 /* the alarm period is counted in even number of seconds */
752 unsigned long alarm_set = (unsigned long)(prev_alarm - elapsed_secs);
753
754 if(!alarm_set ||
755 ((alarm_set >= 0x80000000) && (prev_alarm < 0x80000000)) ) {
756 /* if the alarm time-left reached zero or turned "negative" (counted
757 with unsigned values), we should fire off a SIGALRM here, but we
758 won't, and zero would be to switch it off so we never set it to
759 less than 1! */
760 alarm(1);
761 rc = CURLRESOLV_TIMEDOUT;
762 failf(data, "Previous alarm fired off!");
763 }
764 else
765 alarm((unsigned int)alarm_set);
766 }
767 #endif /* USE_ALARM_TIMEOUT */
768
769 return rc;
770 }
771
772 /*
773 * Curl_resolv_unlock() unlocks the given cached DNS entry. When this has been
774 * made, the struct may be destroyed due to pruning. It is important that only
775 * one unlock is made for each Curl_resolv() call.
776 *
777 * May be called with 'data' == NULL for global cache.
778 */
Curl_resolv_unlock(struct Curl_easy * data,struct Curl_dns_entry * dns)779 void Curl_resolv_unlock(struct Curl_easy *data, struct Curl_dns_entry *dns)
780 {
781 if(data && data->share)
782 Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
783
784 freednsentry(dns);
785
786 if(data && data->share)
787 Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
788 }
789
790 /*
791 * File-internal: release cache dns entry reference, free if inuse drops to 0
792 */
freednsentry(void * freethis)793 static void freednsentry(void *freethis)
794 {
795 struct Curl_dns_entry *dns = (struct Curl_dns_entry *) freethis;
796 DEBUGASSERT(dns && (dns->inuse>0));
797
798 dns->inuse--;
799 if(dns->inuse == 0) {
800 Curl_freeaddrinfo(dns->addr);
801 free(dns);
802 }
803 }
804
805 /*
806 * Curl_mk_dnscache() inits a new DNS cache and returns success/failure.
807 */
Curl_mk_dnscache(struct curl_hash * hash)808 int Curl_mk_dnscache(struct curl_hash *hash)
809 {
810 return Curl_hash_init(hash, 7, Curl_hash_str, Curl_str_key_compare,
811 freednsentry);
812 }
813
814 /*
815 * Curl_hostcache_clean()
816 *
817 * This _can_ be called with 'data' == NULL but then of course no locking
818 * can be done!
819 */
820
Curl_hostcache_clean(struct Curl_easy * data,struct curl_hash * hash)821 void Curl_hostcache_clean(struct Curl_easy *data,
822 struct curl_hash *hash)
823 {
824 if(data && data->share)
825 Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
826
827 Curl_hash_clean(hash);
828
829 if(data && data->share)
830 Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
831 }
832
833
Curl_loadhostpairs(struct Curl_easy * data)834 CURLcode Curl_loadhostpairs(struct Curl_easy *data)
835 {
836 struct curl_slist *hostp;
837 char hostname[256];
838 int port = 0;
839
840 /* Default is no wildcard found */
841 data->change.wildcard_resolve = false;
842
843 for(hostp = data->change.resolve; hostp; hostp = hostp->next) {
844 char entry_id[MAX_HOSTCACHE_LEN];
845 if(!hostp->data)
846 continue;
847 if(hostp->data[0] == '-') {
848 size_t entry_len;
849
850 if(2 != sscanf(hostp->data + 1, "%255[^:]:%d", hostname, &port)) {
851 infof(data, "Couldn't parse CURLOPT_RESOLVE removal entry '%s'!\n",
852 hostp->data);
853 continue;
854 }
855
856 /* Create an entry id, based upon the hostname and port */
857 create_hostcache_id(hostname, port, entry_id, sizeof(entry_id));
858 entry_len = strlen(entry_id);
859
860 if(data->share)
861 Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
862
863 /* delete entry, ignore if it didn't exist */
864 Curl_hash_delete(data->dns.hostcache, entry_id, entry_len + 1);
865
866 if(data->share)
867 Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
868 }
869 else {
870 struct Curl_dns_entry *dns;
871 Curl_addrinfo *head = NULL, *tail = NULL;
872 size_t entry_len;
873 char address[64];
874 #if !defined(CURL_DISABLE_VERBOSE_STRINGS)
875 char *addresses = NULL;
876 #endif
877 char *addr_begin;
878 char *addr_end;
879 char *port_ptr;
880 char *end_ptr;
881 char *host_end;
882 unsigned long tmp_port;
883 bool error = true;
884
885 host_end = strchr(hostp->data, ':');
886 if(!host_end ||
887 ((host_end - hostp->data) >= (ptrdiff_t)sizeof(hostname)))
888 goto err;
889
890 memcpy(hostname, hostp->data, host_end - hostp->data);
891 hostname[host_end - hostp->data] = '\0';
892
893 port_ptr = host_end + 1;
894 tmp_port = strtoul(port_ptr, &end_ptr, 10);
895 if(tmp_port > USHRT_MAX || end_ptr == port_ptr || *end_ptr != ':')
896 goto err;
897
898 port = (int)tmp_port;
899 #if !defined(CURL_DISABLE_VERBOSE_STRINGS)
900 addresses = end_ptr + 1;
901 #endif
902
903 while(*end_ptr) {
904 size_t alen;
905 Curl_addrinfo *ai;
906
907 addr_begin = end_ptr + 1;
908 addr_end = strchr(addr_begin, ',');
909 if(!addr_end)
910 addr_end = addr_begin + strlen(addr_begin);
911 end_ptr = addr_end;
912
913 /* allow IP(v6) address within [brackets] */
914 if(*addr_begin == '[') {
915 if(addr_end == addr_begin || *(addr_end - 1) != ']')
916 goto err;
917 ++addr_begin;
918 --addr_end;
919 }
920
921 alen = addr_end - addr_begin;
922 if(!alen)
923 continue;
924
925 if(alen >= sizeof(address))
926 goto err;
927
928 memcpy(address, addr_begin, alen);
929 address[alen] = '\0';
930
931 #ifndef ENABLE_IPV6
932 if(strchr(address, ':')) {
933 infof(data, "Ignoring resolve address '%s', missing IPv6 support.\n",
934 address);
935 continue;
936 }
937 #endif
938
939 ai = Curl_str2addr(address, port);
940 if(!ai) {
941 infof(data, "Resolve address '%s' found illegal!\n", address);
942 goto err;
943 }
944
945 if(tail) {
946 tail->ai_next = ai;
947 tail = tail->ai_next;
948 }
949 else {
950 head = tail = ai;
951 }
952 }
953
954 if(!head)
955 goto err;
956
957 error = false;
958 err:
959 if(error) {
960 infof(data, "Couldn't parse CURLOPT_RESOLVE entry '%s'!\n",
961 hostp->data);
962 Curl_freeaddrinfo(head);
963 continue;
964 }
965
966 /* Create an entry id, based upon the hostname and port */
967 create_hostcache_id(hostname, port, entry_id, sizeof(entry_id));
968 entry_len = strlen(entry_id);
969
970 if(data->share)
971 Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
972
973 /* See if its already in our dns cache */
974 dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len + 1);
975
976 if(dns) {
977 infof(data, "RESOLVE %s:%d is - old addresses discarded!\n",
978 hostname, port);
979 /* delete old entry entry, there are two reasons for this
980 1. old entry may have different addresses.
981 2. even if entry with correct addresses is already in the cache,
982 but if it is close to expire, then by the time next http
983 request is made, it can get expired and pruned because old
984 entry is not necessarily marked as added by CURLOPT_RESOLVE. */
985
986 Curl_hash_delete(data->dns.hostcache, entry_id, entry_len + 1);
987 }
988
989 /* put this new host in the cache */
990 dns = Curl_cache_addr(data, head, hostname, port);
991 if(dns) {
992 dns->timestamp = 0; /* mark as added by CURLOPT_RESOLVE */
993 /* release the returned reference; the cache itself will keep the
994 * entry alive: */
995 dns->inuse--;
996 }
997
998 if(data->share)
999 Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
1000
1001 if(!dns) {
1002 Curl_freeaddrinfo(head);
1003 return CURLE_OUT_OF_MEMORY;
1004 }
1005 infof(data, "Added %s:%d:%s to DNS cache\n",
1006 hostname, port, addresses);
1007
1008 /* Wildcard hostname */
1009 if(hostname[0] == '*' && hostname[1] == '\0') {
1010 infof(data, "RESOLVE %s:%d is wildcard, enabling wildcard checks\n",
1011 hostname, port);
1012 data->change.wildcard_resolve = true;
1013 }
1014 }
1015 }
1016 data->change.resolve = NULL; /* dealt with now */
1017
1018 return CURLE_OK;
1019 }
1020
Curl_resolv_check(struct connectdata * conn,struct Curl_dns_entry ** dns)1021 CURLcode Curl_resolv_check(struct connectdata *conn,
1022 struct Curl_dns_entry **dns)
1023 {
1024 if(conn->data->set.doh)
1025 return Curl_doh_is_resolved(conn, dns);
1026 return Curl_resolver_is_resolved(conn, dns);
1027 }
1028
Curl_resolv_getsock(struct connectdata * conn,curl_socket_t * socks)1029 int Curl_resolv_getsock(struct connectdata *conn,
1030 curl_socket_t *socks)
1031 {
1032 #ifdef CURLRES_ASYNCH
1033 if(conn->data->set.doh)
1034 /* nothing to wait for during DOH resolve, those handles have their own
1035 sockets */
1036 return GETSOCK_BLANK;
1037 return Curl_resolver_getsock(conn, socks);
1038 #else
1039 (void)conn;
1040 (void)socks;
1041 return GETSOCK_BLANK;
1042 #endif
1043 }
1044
1045 /* Call this function after Curl_connect() has returned async=TRUE and
1046 then a successful name resolve has been received.
1047
1048 Note: this function disconnects and frees the conn data in case of
1049 resolve failure */
Curl_once_resolved(struct connectdata * conn,bool * protocol_done)1050 CURLcode Curl_once_resolved(struct connectdata *conn,
1051 bool *protocol_done)
1052 {
1053 CURLcode result;
1054
1055 if(conn->async.dns) {
1056 conn->dns_entry = conn->async.dns;
1057 conn->async.dns = NULL;
1058 }
1059
1060 result = Curl_setup_conn(conn, protocol_done);
1061
1062 if(result)
1063 /* We're not allowed to return failure with memory left allocated
1064 in the connectdata struct, free those here */
1065 Curl_disconnect(conn->data, conn, TRUE); /* close the connection */
1066
1067 return result;
1068 }
1069