blob: c2f9defd94ff74946b84e63a649b8854470166b3 [file] [log] [blame]
Kristian Monsen5ab50182010-05-14 18:53:44 +01001/***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
Elliott Hughes1ef06ba2018-05-30 15:43:58 -07008 * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
Kristian Monsen5ab50182010-05-14 18:53:44 +01009 *
10 * This software is licensed as described in the file COPYING, which
11 * you should have received as part of this distribution. The terms
Alex Deymod15eaac2016-06-28 14:49:26 -070012 * are also available at https://curl.haxx.se/docs/copyright.html.
Kristian Monsen5ab50182010-05-14 18:53:44 +010013 *
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
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -070023#include "curl_setup.h"
Kristian Monsen5ab50182010-05-14 18:53:44 +010024
Kristian Monsen5ab50182010-05-14 18:53:44 +010025#ifdef HAVE_NETINET_IN_H
26#include <netinet/in.h>
27#endif
Elliott Hughes0128fe42018-02-27 14:57:55 -080028#ifdef HAVE_NETINET_IN6_H
29#include <netinet/in6.h>
30#endif
Kristian Monsen5ab50182010-05-14 18:53:44 +010031#ifdef HAVE_NETDB_H
32#include <netdb.h>
33#endif
34#ifdef HAVE_ARPA_INET_H
35#include <arpa/inet.h>
36#endif
Kristian Monsen5ab50182010-05-14 18:53:44 +010037#ifdef __VMS
38#include <in.h>
39#include <inet.h>
Kristian Monsen5ab50182010-05-14 18:53:44 +010040#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"
Elliott Hughes1ef06ba2018-05-30 15:43:58 -070057#include "rand.h"
Kristian Monsen5ab50182010-05-14 18:53:44 +010058#include "share.h"
59#include "strerror.h"
60#include "url.h"
61#include "inet_ntop.h"
Elliott Hughescac39802018-04-27 16:19:43 -070062#include "multiif.h"
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -070063#include "warnless.h"
Alex Deymod15eaac2016-06-28 14:49:26 -070064/* The last 3 #include files should be in this order */
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -070065#include "curl_printf.h"
Kristian Monsen5ab50182010-05-14 18:53:44 +010066#include "curl_memory.h"
Kristian Monsen5ab50182010-05-14 18:53:44 +010067#include "memdebug.h"
68
69#if defined(CURLRES_SYNCH) && \
70 defined(HAVE_ALARM) && defined(SIGALRM) && defined(HAVE_SIGSETJMP)
71/* alarm-based timeouts can only be used with all the dependencies satisfied */
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -070072#define USE_ALARM_TIMEOUT
Kristian Monsen5ab50182010-05-14 18:53:44 +010073#endif
74
75/*
76 * hostip.c explained
77 * ==================
78 *
79 * The main COMPILE-TIME DEFINES to keep in mind when reading the host*.c
80 * source file are these:
81 *
82 * CURLRES_IPV6 - this host has getaddrinfo() and family, and thus we use
83 * that. The host may not be able to resolve IPv6, but we don't really have to
84 * take that into account. Hosts that aren't IPv6-enabled have CURLRES_IPV4
85 * defined.
86 *
87 * CURLRES_ARES - is defined if libcurl is built to use c-ares for
88 * asynchronous name resolves. This can be Windows or *nix.
89 *
90 * CURLRES_THREADED - is defined if libcurl is built to run under (native)
91 * Windows, and then the name resolve will be done in a new thread, and the
92 * supported API will be the same as for ares-builds.
93 *
94 * If any of the two previous are defined, CURLRES_ASYNCH is defined too. If
95 * libcurl is not built to use an asynchronous resolver, CURLRES_SYNCH is
96 * defined.
97 *
98 * The host*.c sources files are split up like this:
99 *
100 * hostip.c - method-independent resolver functions and utility functions
101 * hostasyn.c - functions for asynchronous name resolves
102 * hostsyn.c - functions for synchronous name resolves
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700103 * hostip4.c - IPv4 specific functions
104 * hostip6.c - IPv6 specific functions
Kristian Monsen5ab50182010-05-14 18:53:44 +0100105 *
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700106 * The two asynchronous name resolver backends are implemented in:
107 * asyn-ares.c - functions for ares-using name resolves
108 * asyn-thread.c - functions for threaded name resolves
109
Kristian Monsen5ab50182010-05-14 18:53:44 +0100110 * The hostip.h is the united header file for all this. It defines the
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700111 * CURLRES_* defines based on the config*.h and curl_setup.h defines.
Kristian Monsen5ab50182010-05-14 18:53:44 +0100112 */
113
114/* These two symbols are for the global DNS cache */
115static struct curl_hash hostname_cache;
116static int host_cache_initialized;
117
118static void freednsentry(void *freethis);
119
120/*
121 * Curl_global_host_cache_init() initializes and sets up a global DNS cache.
122 * Global DNS cache is general badness. Do not use. This will be removed in
123 * a future version. Use the share interface instead!
124 *
125 * Returns a struct curl_hash pointer on success, NULL on failure.
126 */
127struct curl_hash *Curl_global_host_cache_init(void)
128{
129 int rc = 0;
130 if(!host_cache_initialized) {
131 rc = Curl_hash_init(&hostname_cache, 7, Curl_hash_str,
132 Curl_str_key_compare, freednsentry);
133 if(!rc)
134 host_cache_initialized = 1;
135 }
136 return rc?NULL:&hostname_cache;
137}
138
139/*
140 * Destroy and cleanup the global DNS cache
141 */
142void Curl_global_host_cache_dtor(void)
143{
144 if(host_cache_initialized) {
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700145 Curl_hash_destroy(&hostname_cache);
Kristian Monsen5ab50182010-05-14 18:53:44 +0100146 host_cache_initialized = 0;
147 }
148}
149
150/*
Elliott Hughes82be86d2017-09-20 17:00:17 -0700151 * Return # of addresses in a Curl_addrinfo struct
Kristian Monsen5ab50182010-05-14 18:53:44 +0100152 */
153int Curl_num_addresses(const Curl_addrinfo *addr)
154{
155 int i = 0;
156 while(addr) {
157 addr = addr->ai_next;
158 i++;
159 }
160 return i;
161}
162
163/*
164 * Curl_printable_address() returns a printable version of the 1st address
165 * given in the 'ai' argument. The result will be stored in the buf that is
166 * bufsize bytes big.
167 *
168 * If the conversion fails, it returns NULL.
169 */
170const char *
171Curl_printable_address(const Curl_addrinfo *ai, char *buf, size_t bufsize)
172{
173 const struct sockaddr_in *sa4;
174 const struct in_addr *ipaddr4;
175#ifdef ENABLE_IPV6
176 const struct sockaddr_in6 *sa6;
177 const struct in6_addr *ipaddr6;
178#endif
179
Elliott Hughes82be86d2017-09-20 17:00:17 -0700180 switch(ai->ai_family) {
Kristian Monsen5ab50182010-05-14 18:53:44 +0100181 case AF_INET:
182 sa4 = (const void *)ai->ai_addr;
183 ipaddr4 = &sa4->sin_addr;
184 return Curl_inet_ntop(ai->ai_family, (const void *)ipaddr4, buf,
185 bufsize);
186#ifdef ENABLE_IPV6
187 case AF_INET6:
188 sa6 = (const void *)ai->ai_addr;
189 ipaddr6 = &sa6->sin6_addr;
190 return Curl_inet_ntop(ai->ai_family, (const void *)ipaddr6, buf,
191 bufsize);
192#endif
193 default:
194 break;
195 }
196 return NULL;
197}
198
199/*
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700200 * Return a hostcache id string for the provided host + port, to be used by
Kristian Monsen5ab50182010-05-14 18:53:44 +0100201 * the DNS caching.
202 */
203static char *
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700204create_hostcache_id(const char *name, int port)
Kristian Monsen5ab50182010-05-14 18:53:44 +0100205{
206 /* create and return the new allocated entry */
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700207 char *id = aprintf("%s:%d", name, port);
208 char *ptr = id;
209 if(ptr) {
210 /* lower case the name part */
211 while(*ptr && (*ptr != ':')) {
212 *ptr = (char)TOLOWER(*ptr);
213 ptr++;
214 }
215 }
216 return id;
Kristian Monsen5ab50182010-05-14 18:53:44 +0100217}
218
219struct hostcache_prune_data {
220 long cache_timeout;
221 time_t now;
222};
223
224/*
225 * This function is set as a callback to be called for every entry in the DNS
226 * cache when we want to prune old unused entries.
227 *
228 * Returning non-zero means remove the entry, return 0 to keep it in the
229 * cache.
230 */
231static int
232hostcache_timestamp_remove(void *datap, void *hc)
233{
234 struct hostcache_prune_data *data =
235 (struct hostcache_prune_data *) datap;
236 struct Curl_dns_entry *c = (struct Curl_dns_entry *) hc;
237
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700238 return (0 != c->timestamp)
239 && (data->now - c->timestamp >= data->cache_timeout);
Kristian Monsen5ab50182010-05-14 18:53:44 +0100240}
241
242/*
243 * Prune the DNS cache. This assumes that a lock has already been taken.
244 */
245static void
246hostcache_prune(struct curl_hash *hostcache, long cache_timeout, time_t now)
247{
248 struct hostcache_prune_data user;
249
250 user.cache_timeout = cache_timeout;
251 user.now = now;
252
253 Curl_hash_clean_with_criterium(hostcache,
254 (void *) &user,
255 hostcache_timestamp_remove);
256}
257
258/*
259 * Library-wide function for pruning the DNS cache. This function takes and
260 * returns the appropriate locks.
261 */
Alex Deymoe3149cc2016-10-05 11:18:42 -0700262void Curl_hostcache_prune(struct Curl_easy *data)
Kristian Monsen5ab50182010-05-14 18:53:44 +0100263{
264 time_t now;
265
266 if((data->set.dns_cache_timeout == -1) || !data->dns.hostcache)
267 /* cache forever means never prune, and NULL hostcache means
268 we can't do it */
269 return;
270
271 if(data->share)
272 Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
273
274 time(&now);
275
276 /* Remove outdated and unused entries from the hostcache */
277 hostcache_prune(data->dns.hostcache,
278 data->set.dns_cache_timeout,
279 now);
280
281 if(data->share)
282 Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
283}
284
Kristian Monsen5ab50182010-05-14 18:53:44 +0100285#ifdef HAVE_SIGSETJMP
286/* Beware this is a global and unique instance. This is used to store the
287 return address that we can jump back to from inside a signal handler. This
288 is not thread-safe stuff. */
289sigjmp_buf curl_jmpenv;
290#endif
291
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700292/* lookup address, returns entry if found and not stale */
293static struct Curl_dns_entry *
294fetch_addr(struct connectdata *conn,
295 const char *hostname,
296 int port)
297{
298 char *entry_id = NULL;
299 struct Curl_dns_entry *dns = NULL;
300 size_t entry_len;
Alex Deymoe3149cc2016-10-05 11:18:42 -0700301 struct Curl_easy *data = conn->data;
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700302
303 /* Create an entry id, based upon the hostname and port */
304 entry_id = create_hostcache_id(hostname, port);
305 /* If we can't create the entry id, fail */
306 if(!entry_id)
307 return dns;
308
309 entry_len = strlen(entry_id);
310
311 /* See if its already in our dns cache */
Alex Deymo486467e2017-12-19 19:04:07 +0100312 dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len + 1);
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700313
Alex Deymo486467e2017-12-19 19:04:07 +0100314 if(dns && (data->set.dns_cache_timeout != -1)) {
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700315 /* See whether the returned entry is stale. Done before we release lock */
316 struct hostcache_prune_data user;
317
318 time(&user.now);
319 user.cache_timeout = data->set.dns_cache_timeout;
320
321 if(hostcache_timestamp_remove(&user, dns)) {
322 infof(data, "Hostname in DNS cache was stale, zapped\n");
323 dns = NULL; /* the memory deallocation is being handled by the hash */
Alex Deymo486467e2017-12-19 19:04:07 +0100324 Curl_hash_delete(data->dns.hostcache, entry_id, entry_len + 1);
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700325 }
326 }
327
328 /* free the allocated entry_id again */
329 free(entry_id);
330
331 return dns;
332}
333
334/*
335 * Curl_fetch_addr() fetches a 'Curl_dns_entry' already in the DNS cache.
336 *
337 * Curl_resolv() checks initially and multi_runsingle() checks each time
338 * it discovers the handle in the state WAITRESOLVE whether the hostname
339 * has already been resolved and the address has already been stored in
340 * the DNS cache. This short circuits waiting for a lot of pending
341 * lookups for the same hostname requested by different handles.
342 *
343 * Returns the Curl_dns_entry entry pointer or NULL if not in the cache.
344 *
345 * The returned data *MUST* be "unlocked" with Curl_resolv_unlock() after
346 * use, or we'll leak memory!
347 */
348struct Curl_dns_entry *
349Curl_fetch_addr(struct connectdata *conn,
350 const char *hostname,
351 int port)
352{
Alex Deymoe3149cc2016-10-05 11:18:42 -0700353 struct Curl_easy *data = conn->data;
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700354 struct Curl_dns_entry *dns = NULL;
355
356 if(data->share)
357 Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
358
359 dns = fetch_addr(conn, hostname, port);
360
Alex Deymod15eaac2016-06-28 14:49:26 -0700361 if(dns)
362 dns->inuse++; /* we use it! */
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700363
364 if(data->share)
365 Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
366
367 return dns;
368}
Kristian Monsen5ab50182010-05-14 18:53:44 +0100369
370/*
Elliott Hughes1ef06ba2018-05-30 15:43:58 -0700371 * Curl_shuffle_addr() shuffles the order of addresses in a 'Curl_addrinfo'
372 * struct by re-linking its linked list.
373 *
374 * The addr argument should be the address of a pointer to the head node of a
375 * `Curl_addrinfo` list and it will be modified to point to the new head after
376 * shuffling.
377 *
378 * Not declared static only to make it easy to use in a unit test!
379 *
380 * @unittest: 1608
381 */
382CURLcode Curl_shuffle_addr(struct Curl_easy *data, Curl_addrinfo **addr)
383{
384 CURLcode result = CURLE_OK;
385 const int num_addrs = Curl_num_addresses(*addr);
386
387 if(num_addrs > 1) {
388 Curl_addrinfo **nodes;
389 infof(data, "Shuffling %i addresses", num_addrs);
390
391 nodes = malloc(num_addrs*sizeof(*nodes));
392 if(nodes) {
393 int i;
394 unsigned int *rnd;
395 const size_t rnd_size = num_addrs * sizeof(*rnd);
396
397 /* build a plain array of Curl_addrinfo pointers */
398 nodes[0] = *addr;
399 for(i = 1; i < num_addrs; i++) {
400 nodes[i] = nodes[i-1]->ai_next;
401 }
402
403 rnd = malloc(rnd_size);
404 if(rnd) {
405 /* Fisher-Yates shuffle */
406 if(Curl_rand(data, (unsigned char *)rnd, rnd_size) == CURLE_OK) {
407 Curl_addrinfo *swap_tmp;
408 for(i = num_addrs - 1; i > 0; i--) {
409 swap_tmp = nodes[rnd[i] % (i + 1)];
410 nodes[rnd[i] % (i + 1)] = nodes[i];
411 nodes[i] = swap_tmp;
412 }
413
414 /* relink list in the new order */
415 for(i = 1; i < num_addrs; i++) {
416 nodes[i-1]->ai_next = nodes[i];
417 }
418
419 nodes[num_addrs-1]->ai_next = NULL;
420 *addr = nodes[0];
421 }
422 free(rnd);
423 }
424 else
425 result = CURLE_OUT_OF_MEMORY;
426 free(nodes);
427 }
428 else
429 result = CURLE_OUT_OF_MEMORY;
430 }
431 return result;
432}
433
434/*
Kristian Monsen5ab50182010-05-14 18:53:44 +0100435 * Curl_cache_addr() stores a 'Curl_addrinfo' struct in the DNS cache.
436 *
437 * When calling Curl_resolv() has resulted in a response with a returned
438 * address, we call this function to store the information in the dns
439 * cache etc
440 *
441 * Returns the Curl_dns_entry entry pointer or NULL if the storage failed.
442 */
443struct Curl_dns_entry *
Alex Deymoe3149cc2016-10-05 11:18:42 -0700444Curl_cache_addr(struct Curl_easy *data,
Kristian Monsen5ab50182010-05-14 18:53:44 +0100445 Curl_addrinfo *addr,
446 const char *hostname,
447 int port)
448{
449 char *entry_id;
450 size_t entry_len;
451 struct Curl_dns_entry *dns;
452 struct Curl_dns_entry *dns2;
453
Elliott Hughes1ef06ba2018-05-30 15:43:58 -0700454 /* shuffle addresses if requested */
455 if(data->set.dns_shuffle_addresses) {
456 CURLcode result = Curl_shuffle_addr(data, &addr);
457 if(!result)
458 return NULL;
459 }
460
Kristian Monsen5ab50182010-05-14 18:53:44 +0100461 /* Create an entry id, based upon the hostname and port */
462 entry_id = create_hostcache_id(hostname, port);
463 /* If we can't create the entry id, fail */
464 if(!entry_id)
465 return NULL;
466 entry_len = strlen(entry_id);
467
468 /* Create a new cache entry */
469 dns = calloc(1, sizeof(struct Curl_dns_entry));
470 if(!dns) {
471 free(entry_id);
472 return NULL;
473 }
474
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700475 dns->inuse = 1; /* the cache has the first reference */
Kristian Monsen5ab50182010-05-14 18:53:44 +0100476 dns->addr = addr; /* this is the address(es) */
477 time(&dns->timestamp);
478 if(dns->timestamp == 0)
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700479 dns->timestamp = 1; /* zero indicates CURLOPT_RESOLVE entry */
Kristian Monsen5ab50182010-05-14 18:53:44 +0100480
481 /* Store the resolved data in our DNS cache. */
Alex Deymo486467e2017-12-19 19:04:07 +0100482 dns2 = Curl_hash_add(data->dns.hostcache, entry_id, entry_len + 1,
Kristian Monsen5ab50182010-05-14 18:53:44 +0100483 (void *)dns);
484 if(!dns2) {
485 free(dns);
486 free(entry_id);
487 return NULL;
488 }
489
490 dns = dns2;
491 dns->inuse++; /* mark entry as in-use */
492
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700493 /* free the allocated entry_id */
Kristian Monsen5ab50182010-05-14 18:53:44 +0100494 free(entry_id);
495
496 return dns;
497}
498
499/*
500 * Curl_resolv() is the main name resolve function within libcurl. It resolves
501 * a name and returns a pointer to the entry in the 'entry' argument (if one
502 * is provided). This function might return immediately if we're using asynch
503 * resolves. See the return codes.
504 *
505 * The cache entry we return will get its 'inuse' counter increased when this
506 * function is used. You MUST call Curl_resolv_unlock() later (when you're
507 * done using this struct) to decrease the counter again.
508 *
509 * In debug mode, we specifically test for an interface name "LocalHost"
510 * and resolve "localhost" instead as a means to permit test cases
511 * to connect to a local test server with any host name.
512 *
513 * Return codes:
514 *
515 * CURLRESOLV_ERROR (-1) = error, no pointer
516 * CURLRESOLV_RESOLVED (0) = OK, pointer provided
517 * CURLRESOLV_PENDING (1) = waiting for response, no pointer
518 */
519
520int Curl_resolv(struct connectdata *conn,
521 const char *hostname,
522 int port,
523 struct Curl_dns_entry **entry)
524{
Kristian Monsen5ab50182010-05-14 18:53:44 +0100525 struct Curl_dns_entry *dns = NULL;
Alex Deymoe3149cc2016-10-05 11:18:42 -0700526 struct Curl_easy *data = conn->data;
Kristian Monsen5ab50182010-05-14 18:53:44 +0100527 CURLcode result;
528 int rc = CURLRESOLV_ERROR; /* default to failure */
529
530 *entry = NULL;
531
Kristian Monsen5ab50182010-05-14 18:53:44 +0100532 if(data->share)
533 Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
534
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700535 dns = fetch_addr(conn, hostname, port);
Kristian Monsen5ab50182010-05-14 18:53:44 +0100536
537 if(dns) {
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700538 infof(data, "Hostname %s was found in DNS cache\n", hostname);
Kristian Monsen5ab50182010-05-14 18:53:44 +0100539 dns->inuse++; /* we use it! */
540 rc = CURLRESOLV_RESOLVED;
541 }
542
543 if(data->share)
544 Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
545
Kristian Monsen5ab50182010-05-14 18:53:44 +0100546 if(!dns) {
547 /* The entry was not in the cache. Resolve it to IP address */
548
549 Curl_addrinfo *addr;
550 int respwait;
551
552 /* Check what IP specifics the app has requested and if we can provide it.
553 * If not, bail out. */
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700554 if(!Curl_ipvalid(conn))
Kristian Monsen5ab50182010-05-14 18:53:44 +0100555 return CURLRESOLV_ERROR;
556
Elliott Hughescac39802018-04-27 16:19:43 -0700557 /* notify the resolver start callback */
558 if(data->set.resolver_start) {
559 int st;
560 Curl_set_in_callback(data, true);
561 st = data->set.resolver_start(data->state.resolver, NULL,
562 data->set.resolver_start_client);
563 Curl_set_in_callback(data, false);
564 if(st)
565 return CURLRESOLV_ERROR;
566 }
567
Kristian Monsen5ab50182010-05-14 18:53:44 +0100568 /* If Curl_getaddrinfo() returns NULL, 'respwait' might be set to a
569 non-zero value indicating that we need to wait for the response to the
570 resolve call */
571 addr = Curl_getaddrinfo(conn,
572#ifdef DEBUGBUILD
573 (data->set.str[STRING_DEVICE]
574 && !strcmp(data->set.str[STRING_DEVICE],
575 "LocalHost"))?"localhost":
576#endif
577 hostname, port, &respwait);
578
579 if(!addr) {
580 if(respwait) {
581 /* the response to our resolve call will come asynchronously at
582 a later time, good or bad */
583 /* First, check that we haven't received the info by now */
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700584 result = Curl_resolver_is_resolved(conn, &dns);
Kristian Monsen5ab50182010-05-14 18:53:44 +0100585 if(result) /* error detected */
586 return CURLRESOLV_ERROR;
587 if(dns)
588 rc = CURLRESOLV_RESOLVED; /* pointer provided */
589 else
590 rc = CURLRESOLV_PENDING; /* no info yet */
591 }
592 }
593 else {
594 if(data->share)
595 Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
596
597 /* we got a response, store it in the cache */
598 dns = Curl_cache_addr(data, addr, hostname, port);
599
600 if(data->share)
601 Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
602
603 if(!dns)
604 /* returned failure, bail out nicely */
605 Curl_freeaddrinfo(addr);
606 else
607 rc = CURLRESOLV_RESOLVED;
608 }
609 }
610
611 *entry = dns;
612
613 return rc;
614}
615
616#ifdef USE_ALARM_TIMEOUT
617/*
618 * This signal handler jumps back into the main libcurl code and continues
619 * execution. This effectively causes the remainder of the application to run
620 * within a signal handler which is nonportable and could lead to problems.
621 */
622static
623RETSIGTYPE alarmfunc(int sig)
624{
625 /* this is for "-ansi -Wall -pedantic" to stop complaining! (rabe) */
626 (void)sig;
627 siglongjmp(curl_jmpenv, 1);
Kristian Monsen5ab50182010-05-14 18:53:44 +0100628}
629#endif /* USE_ALARM_TIMEOUT */
630
631/*
632 * Curl_resolv_timeout() is the same as Curl_resolv() but specifies a
633 * timeout. This function might return immediately if we're using asynch
634 * resolves. See the return codes.
635 *
636 * The cache entry we return will get its 'inuse' counter increased when this
637 * function is used. You MUST call Curl_resolv_unlock() later (when you're
638 * done using this struct) to decrease the counter again.
639 *
640 * If built with a synchronous resolver and use of signals is not
641 * disabled by the application, then a nonzero timeout will cause a
642 * timeout after the specified number of milliseconds. Otherwise, timeout
643 * is ignored.
644 *
645 * Return codes:
646 *
647 * CURLRESOLV_TIMEDOUT(-2) = warning, time too short or previous alarm expired
648 * CURLRESOLV_ERROR (-1) = error, no pointer
649 * CURLRESOLV_RESOLVED (0) = OK, pointer provided
650 * CURLRESOLV_PENDING (1) = waiting for response, no pointer
651 */
652
653int Curl_resolv_timeout(struct connectdata *conn,
654 const char *hostname,
655 int port,
656 struct Curl_dns_entry **entry,
Elliott Hughescee03382017-06-23 12:17:18 -0700657 time_t timeoutms)
Kristian Monsen5ab50182010-05-14 18:53:44 +0100658{
659#ifdef USE_ALARM_TIMEOUT
660#ifdef HAVE_SIGACTION
661 struct sigaction keep_sigact; /* store the old struct here */
Elliott Hughes82be86d2017-09-20 17:00:17 -0700662 volatile bool keep_copysig = FALSE; /* whether old sigact has been saved */
Kristian Monsen5ab50182010-05-14 18:53:44 +0100663 struct sigaction sigact;
664#else
665#ifdef HAVE_SIGNAL
666 void (*keep_sigact)(int); /* store the old handler here */
667#endif /* HAVE_SIGNAL */
668#endif /* HAVE_SIGACTION */
669 volatile long timeout;
670 volatile unsigned int prev_alarm = 0;
Alex Deymoe3149cc2016-10-05 11:18:42 -0700671 struct Curl_easy *data = conn->data;
Kristian Monsen5ab50182010-05-14 18:53:44 +0100672#endif /* USE_ALARM_TIMEOUT */
673 int rc;
674
675 *entry = NULL;
676
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700677 if(timeoutms < 0)
678 /* got an already expired timeout */
679 return CURLRESOLV_TIMEDOUT;
680
Kristian Monsen5ab50182010-05-14 18:53:44 +0100681#ifdef USE_ALARM_TIMEOUT
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700682 if(data->set.no_signal)
Kristian Monsen5ab50182010-05-14 18:53:44 +0100683 /* Ignore the timeout when signals are disabled */
684 timeout = 0;
685 else
Elliott Hughes82be86d2017-09-20 17:00:17 -0700686 timeout = (timeoutms > LONG_MAX) ? LONG_MAX : (long)timeoutms;
Kristian Monsen5ab50182010-05-14 18:53:44 +0100687
688 if(!timeout)
689 /* USE_ALARM_TIMEOUT defined, but no timeout actually requested */
690 return Curl_resolv(conn, hostname, port, entry);
691
Elliott Hughescee03382017-06-23 12:17:18 -0700692 if(timeout < 1000) {
Kristian Monsen5ab50182010-05-14 18:53:44 +0100693 /* The alarm() function only provides integer second resolution, so if
694 we want to wait less than one second we must bail out already now. */
Elliott Hughescee03382017-06-23 12:17:18 -0700695 failf(data,
696 "remaining timeout of %ld too small to resolve via SIGALRM method",
697 timeout);
Kristian Monsen5ab50182010-05-14 18:53:44 +0100698 return CURLRESOLV_TIMEDOUT;
Elliott Hughescee03382017-06-23 12:17:18 -0700699 }
Kristian Monsen5ab50182010-05-14 18:53:44 +0100700 /* This allows us to time-out from the name resolver, as the timeout
701 will generate a signal and we will siglongjmp() from that here.
702 This technique has problems (see alarmfunc).
703 This should be the last thing we do before calling Curl_resolv(),
704 as otherwise we'd have to worry about variables that get modified
705 before we invoke Curl_resolv() (and thus use "volatile"). */
706 if(sigsetjmp(curl_jmpenv, 1)) {
707 /* this is coming from a siglongjmp() after an alarm signal */
708 failf(data, "name lookup timed out");
709 rc = CURLRESOLV_ERROR;
710 goto clean_up;
711 }
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700712 else {
713 /*************************************************************
714 * Set signal handler to catch SIGALRM
715 * Store the old value to be able to set it back later!
716 *************************************************************/
717#ifdef HAVE_SIGACTION
718 sigaction(SIGALRM, NULL, &sigact);
719 keep_sigact = sigact;
720 keep_copysig = TRUE; /* yes, we have a copy */
721 sigact.sa_handler = alarmfunc;
722#ifdef SA_RESTART
723 /* HPUX doesn't have SA_RESTART but defaults to that behaviour! */
724 sigact.sa_flags &= ~SA_RESTART;
725#endif
726 /* now set the new struct */
727 sigaction(SIGALRM, &sigact, NULL);
728#else /* HAVE_SIGACTION */
729 /* no sigaction(), revert to the much lamer signal() */
730#ifdef HAVE_SIGNAL
731 keep_sigact = signal(SIGALRM, alarmfunc);
732#endif
733#endif /* HAVE_SIGACTION */
734
735 /* alarm() makes a signal get sent when the timeout fires off, and that
736 will abort system calls */
737 prev_alarm = alarm(curlx_sltoui(timeout/1000L));
738 }
Kristian Monsen5ab50182010-05-14 18:53:44 +0100739
740#else
741#ifndef CURLRES_ASYNCH
742 if(timeoutms)
743 infof(conn->data, "timeout on name lookup is not supported\n");
744#else
745 (void)timeoutms; /* timeoutms not used with an async resolver */
746#endif
747#endif /* USE_ALARM_TIMEOUT */
748
749 /* Perform the actual name resolution. This might be interrupted by an
750 * alarm if it takes too long.
751 */
752 rc = Curl_resolv(conn, hostname, port, entry);
753
754#ifdef USE_ALARM_TIMEOUT
755clean_up:
756
757 if(!prev_alarm)
758 /* deactivate a possibly active alarm before uninstalling the handler */
759 alarm(0);
760
761#ifdef HAVE_SIGACTION
762 if(keep_copysig) {
763 /* we got a struct as it looked before, now put that one back nice
764 and clean */
765 sigaction(SIGALRM, &keep_sigact, NULL); /* put it back */
766 }
767#else
768#ifdef HAVE_SIGNAL
769 /* restore the previous SIGALRM handler */
770 signal(SIGALRM, keep_sigact);
771#endif
772#endif /* HAVE_SIGACTION */
773
774 /* switch back the alarm() to either zero or to what it was before minus
775 the time we spent until now! */
776 if(prev_alarm) {
777 /* there was an alarm() set before us, now put it back */
Alex Deymo486467e2017-12-19 19:04:07 +0100778 timediff_t elapsed_secs = Curl_timediff(Curl_now(),
779 conn->created) / 1000;
Kristian Monsen5ab50182010-05-14 18:53:44 +0100780
781 /* the alarm period is counted in even number of seconds */
Elliott Hughes82be86d2017-09-20 17:00:17 -0700782 unsigned long alarm_set = prev_alarm - elapsed_secs;
Kristian Monsen5ab50182010-05-14 18:53:44 +0100783
784 if(!alarm_set ||
785 ((alarm_set >= 0x80000000) && (prev_alarm < 0x80000000)) ) {
786 /* if the alarm time-left reached zero or turned "negative" (counted
787 with unsigned values), we should fire off a SIGALRM here, but we
788 won't, and zero would be to switch it off so we never set it to
789 less than 1! */
790 alarm(1);
791 rc = CURLRESOLV_TIMEDOUT;
792 failf(data, "Previous alarm fired off!");
793 }
794 else
795 alarm((unsigned int)alarm_set);
796 }
797#endif /* USE_ALARM_TIMEOUT */
798
799 return rc;
800}
801
802/*
803 * Curl_resolv_unlock() unlocks the given cached DNS entry. When this has been
804 * made, the struct may be destroyed due to pruning. It is important that only
805 * one unlock is made for each Curl_resolv() call.
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700806 *
807 * May be called with 'data' == NULL for global cache.
Kristian Monsen5ab50182010-05-14 18:53:44 +0100808 */
Alex Deymoe3149cc2016-10-05 11:18:42 -0700809void Curl_resolv_unlock(struct Curl_easy *data, struct Curl_dns_entry *dns)
Kristian Monsen5ab50182010-05-14 18:53:44 +0100810{
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700811 if(data && data->share)
Kristian Monsen5ab50182010-05-14 18:53:44 +0100812 Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
813
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700814 freednsentry(dns);
Kristian Monsen5ab50182010-05-14 18:53:44 +0100815
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700816 if(data && data->share)
Kristian Monsen5ab50182010-05-14 18:53:44 +0100817 Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
818}
819
820/*
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700821 * File-internal: release cache dns entry reference, free if inuse drops to 0
Kristian Monsen5ab50182010-05-14 18:53:44 +0100822 */
823static void freednsentry(void *freethis)
824{
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700825 struct Curl_dns_entry *dns = (struct Curl_dns_entry *) freethis;
826 DEBUGASSERT(dns && (dns->inuse>0));
Kristian Monsen5ab50182010-05-14 18:53:44 +0100827
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700828 dns->inuse--;
829 if(dns->inuse == 0) {
830 Curl_freeaddrinfo(dns->addr);
831 free(dns);
Kristian Monsen5ab50182010-05-14 18:53:44 +0100832 }
833}
834
835/*
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700836 * Curl_mk_dnscache() inits a new DNS cache and returns success/failure.
Kristian Monsen5ab50182010-05-14 18:53:44 +0100837 */
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700838int Curl_mk_dnscache(struct curl_hash *hash)
Kristian Monsen5ab50182010-05-14 18:53:44 +0100839{
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700840 return Curl_hash_init(hash, 7, Curl_hash_str, Curl_str_key_compare,
841 freednsentry);
842}
843
844/*
845 * Curl_hostcache_clean()
846 *
847 * This _can_ be called with 'data' == NULL but then of course no locking
848 * can be done!
849 */
850
Alex Deymoe3149cc2016-10-05 11:18:42 -0700851void Curl_hostcache_clean(struct Curl_easy *data,
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700852 struct curl_hash *hash)
853{
854 if(data && data->share)
855 Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
856
857 Curl_hash_clean(hash);
858
859 if(data && data->share)
860 Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
861}
862
863
Alex Deymoe3149cc2016-10-05 11:18:42 -0700864CURLcode Curl_loadhostpairs(struct Curl_easy *data)
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700865{
866 struct curl_slist *hostp;
867 char hostname[256];
Elliott Hughescac39802018-04-27 16:19:43 -0700868 int port = 0;
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700869
Alex Deymod15eaac2016-06-28 14:49:26 -0700870 for(hostp = data->change.resolve; hostp; hostp = hostp->next) {
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700871 if(!hostp->data)
872 continue;
873 if(hostp->data[0] == '-') {
874 char *entry_id;
875 size_t entry_len;
876
877 if(2 != sscanf(hostp->data + 1, "%255[^:]:%d", hostname, &port)) {
878 infof(data, "Couldn't parse CURLOPT_RESOLVE removal entry '%s'!\n",
879 hostp->data);
880 continue;
881 }
882
883 /* Create an entry id, based upon the hostname and port */
884 entry_id = create_hostcache_id(hostname, port);
885 /* If we can't create the entry id, fail */
886 if(!entry_id) {
887 return CURLE_OUT_OF_MEMORY;
888 }
889
890 entry_len = strlen(entry_id);
891
892 if(data->share)
893 Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
894
895 /* delete entry, ignore if it didn't exist */
Alex Deymo486467e2017-12-19 19:04:07 +0100896 Curl_hash_delete(data->dns.hostcache, entry_id, entry_len + 1);
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700897
898 if(data->share)
899 Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
900
901 /* free the allocated entry_id again */
902 free(entry_id);
903 }
904 else {
905 struct Curl_dns_entry *dns;
Elliott Hughescac39802018-04-27 16:19:43 -0700906 Curl_addrinfo *head = NULL, *tail = NULL;
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700907 char *entry_id;
908 size_t entry_len;
Elliott Hughescac39802018-04-27 16:19:43 -0700909 char address[64];
910 char *addresses = NULL;
911 char *addr_begin;
912 char *addr_end;
913 char *port_ptr;
914 char *end_ptr;
915 char *host_end;
916 unsigned long tmp_port;
917 bool error = true;
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700918
Elliott Hughescac39802018-04-27 16:19:43 -0700919 host_end = strchr(hostp->data, ':');
920 if(!host_end ||
921 ((host_end - hostp->data) >= (ptrdiff_t)sizeof(hostname)))
922 goto err;
923
924 memcpy(hostname, hostp->data, host_end - hostp->data);
925 hostname[host_end - hostp->data] = '\0';
926
927 port_ptr = host_end + 1;
928 tmp_port = strtoul(port_ptr, &end_ptr, 10);
929 if(tmp_port > USHRT_MAX || end_ptr == port_ptr || *end_ptr != ':')
930 goto err;
931
932 port = (int)tmp_port;
933 addresses = end_ptr + 1;
934
935 while(*end_ptr) {
936 size_t alen;
937 Curl_addrinfo *ai;
938
939 addr_begin = end_ptr + 1;
940 addr_end = strchr(addr_begin, ',');
941 if(!addr_end)
942 addr_end = addr_begin + strlen(addr_begin);
943 end_ptr = addr_end;
944
945 /* allow IP(v6) address within [brackets] */
946 if(*addr_begin == '[') {
947 if(addr_end == addr_begin || *(addr_end - 1) != ']')
948 goto err;
949 ++addr_begin;
950 --addr_end;
951 }
952
953 alen = addr_end - addr_begin;
954 if(!alen)
955 continue;
956
957 if(alen >= sizeof(address))
958 goto err;
959
960 memcpy(address, addr_begin, alen);
961 address[alen] = '\0';
962
963#ifndef ENABLE_IPV6
964 if(strchr(address, ':')) {
965 infof(data, "Ignoring resolve address '%s', missing IPv6 support.\n",
966 address);
967 continue;
968 }
969#endif
970
971 ai = Curl_str2addr(address, port);
972 if(!ai) {
973 infof(data, "Resolve address '%s' found illegal!\n", address);
974 goto err;
975 }
976
977 if(tail) {
978 tail->ai_next = ai;
979 tail = tail->ai_next;
980 }
981 else {
982 head = tail = ai;
983 }
984 }
985
986 if(!head)
987 goto err;
988
989 error = false;
990 err:
991 if(error) {
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700992 infof(data, "Couldn't parse CURLOPT_RESOLVE entry '%s'!\n",
993 hostp->data);
Elliott Hughescac39802018-04-27 16:19:43 -0700994 Curl_freeaddrinfo(head);
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -0700995 continue;
996 }
997
998 /* Create an entry id, based upon the hostname and port */
999 entry_id = create_hostcache_id(hostname, port);
1000 /* If we can't create the entry id, fail */
1001 if(!entry_id) {
Elliott Hughescac39802018-04-27 16:19:43 -07001002 Curl_freeaddrinfo(head);
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001003 return CURLE_OUT_OF_MEMORY;
1004 }
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001005 entry_len = strlen(entry_id);
1006
1007 if(data->share)
1008 Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
1009
1010 /* See if its already in our dns cache */
Alex Deymo486467e2017-12-19 19:04:07 +01001011 dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len + 1);
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001012
1013 /* free the allocated entry_id again */
1014 free(entry_id);
1015
1016 if(!dns) {
1017 /* if not in the cache already, put this host in the cache */
Elliott Hughescac39802018-04-27 16:19:43 -07001018 dns = Curl_cache_addr(data, head, hostname, port);
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001019 if(dns) {
1020 dns->timestamp = 0; /* mark as added by CURLOPT_RESOLVE */
1021 /* release the returned reference; the cache itself will keep the
1022 * entry alive: */
1023 dns->inuse--;
1024 }
1025 }
Elliott Hughes0128fe42018-02-27 14:57:55 -08001026 else {
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001027 /* this is a duplicate, free it again */
Elliott Hughes0128fe42018-02-27 14:57:55 -08001028 infof(data, "RESOLVE %s:%d is already cached, %s not stored!\n",
Elliott Hughescac39802018-04-27 16:19:43 -07001029 hostname, port, addresses);
1030 Curl_freeaddrinfo(head);
Elliott Hughes0128fe42018-02-27 14:57:55 -08001031 }
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001032
1033 if(data->share)
1034 Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
1035
1036 if(!dns) {
Elliott Hughescac39802018-04-27 16:19:43 -07001037 Curl_freeaddrinfo(head);
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001038 return CURLE_OUT_OF_MEMORY;
1039 }
1040 infof(data, "Added %s:%d:%s to DNS cache\n",
Elliott Hughescac39802018-04-27 16:19:43 -07001041 hostname, port, addresses);
Bertrand SIMONNETe6cd7382015-07-01 15:39:44 -07001042 }
1043 }
1044 data->change.resolve = NULL; /* dealt with now */
1045
1046 return CURLE_OK;
Kristian Monsen5ab50182010-05-14 18:53:44 +01001047}