1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2010, Daniel Stenberg, <daniel@haxx.se>, et al.
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 http://curl.haxx.se/docs/copyright.html.
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.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
21 ***************************************************************************/
27 #ifdef HAVE_SYS_SOCKET_H
28 #include <sys/socket.h>
30 #ifdef HAVE_NETINET_IN_H
31 #include <netinet/in.h>
36 #ifdef HAVE_ARPA_INET_H
37 #include <arpa/inet.h>
40 #include <stdlib.h> /* required for free() prototypes */
43 #include <unistd.h> /* for the close() proto */
69 #include "inet_ntop.h"
71 #define _MPRINTF_REPLACE /* use our functions only */
72 #include <curl/mprintf.h>
74 #include "curl_memory.h"
75 /* The last #include file should be: */
78 #if defined(CURLRES_SYNCH) && \
79 defined(HAVE_ALARM) && defined(SIGALRM) && defined(HAVE_SIGSETJMP)
80 /* alarm-based timeouts can only be used with all the dependencies satisfied */
81 #define USE_ALARM_TIMEOUT
88 * The main COMPILE-TIME DEFINES to keep in mind when reading the host*.c
89 * source file are these:
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
96 * CURLRES_ARES - is defined if libcurl is built to use c-ares for
97 * asynchronous name resolves. This can be Windows or *nix.
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.
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
107 * The host*.c sources files are split up like this:
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 * hostares.c - functions for ares-using name resolves
113 * hostthre.c - functions for threaded name resolves
114 * hostip4.c - ipv4-specific functions
115 * hostip6.c - ipv6-specific functions
117 * The hostip.h is the united header file for all this. It defines the
118 * CURLRES_* defines based on the config*.h and setup.h defines.
121 /* These two symbols are for the global DNS cache */
122 static struct curl_hash hostname_cache;
123 static int host_cache_initialized;
125 static void freednsentry(void *freethis);
128 * Curl_global_host_cache_init() initializes and sets up a global DNS cache.
129 * Global DNS cache is general badness. Do not use. This will be removed in
130 * a future version. Use the share interface instead!
132 * Returns a struct curl_hash pointer on success, NULL on failure.
134 struct curl_hash *Curl_global_host_cache_init(void)
137 if(!host_cache_initialized) {
138 rc = Curl_hash_init(&hostname_cache, 7, Curl_hash_str,
139 Curl_str_key_compare, freednsentry);
141 host_cache_initialized = 1;
143 return rc?NULL:&hostname_cache;
147 * Destroy and cleanup the global DNS cache
149 void Curl_global_host_cache_dtor(void)
151 if(host_cache_initialized) {
152 Curl_hash_clean(&hostname_cache);
153 host_cache_initialized = 0;
158 * Return # of adresses in a Curl_addrinfo struct
160 int Curl_num_addresses(const Curl_addrinfo *addr)
164 addr = addr->ai_next;
171 * Curl_printable_address() returns a printable version of the 1st address
172 * given in the 'ai' argument. The result will be stored in the buf that is
175 * If the conversion fails, it returns NULL.
178 Curl_printable_address(const Curl_addrinfo *ai, char *buf, size_t bufsize)
180 const struct sockaddr_in *sa4;
181 const struct in_addr *ipaddr4;
183 const struct sockaddr_in6 *sa6;
184 const struct in6_addr *ipaddr6;
187 switch (ai->ai_family) {
189 sa4 = (const void *)ai->ai_addr;
190 ipaddr4 = &sa4->sin_addr;
191 return Curl_inet_ntop(ai->ai_family, (const void *)ipaddr4, buf,
195 sa6 = (const void *)ai->ai_addr;
196 ipaddr6 = &sa6->sin6_addr;
197 return Curl_inet_ntop(ai->ai_family, (const void *)ipaddr6, buf,
207 * Return a hostcache id string for the providing host + port, to be used by
211 create_hostcache_id(const char *server, int port)
213 /* create and return the new allocated entry */
214 return aprintf("%s:%d", server, port);
217 struct hostcache_prune_data {
223 * This function is set as a callback to be called for every entry in the DNS
224 * cache when we want to prune old unused entries.
226 * Returning non-zero means remove the entry, return 0 to keep it in the
230 hostcache_timestamp_remove(void *datap, void *hc)
232 struct hostcache_prune_data *data =
233 (struct hostcache_prune_data *) datap;
234 struct Curl_dns_entry *c = (struct Curl_dns_entry *) hc;
236 return (data->now - c->timestamp >= data->cache_timeout);
240 * Prune the DNS cache. This assumes that a lock has already been taken.
243 hostcache_prune(struct curl_hash *hostcache, long cache_timeout, time_t now)
245 struct hostcache_prune_data user;
247 user.cache_timeout = cache_timeout;
250 Curl_hash_clean_with_criterium(hostcache,
252 hostcache_timestamp_remove);
256 * Library-wide function for pruning the DNS cache. This function takes and
257 * returns the appropriate locks.
259 void Curl_hostcache_prune(struct SessionHandle *data)
263 if((data->set.dns_cache_timeout == -1) || !data->dns.hostcache)
264 /* cache forever means never prune, and NULL hostcache means
269 Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
273 /* Remove outdated and unused entries from the hostcache */
274 hostcache_prune(data->dns.hostcache,
275 data->set.dns_cache_timeout,
279 Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
283 * Check if the entry should be pruned. Assumes a locked cache.
286 remove_entry_if_stale(struct SessionHandle *data, struct Curl_dns_entry *dns)
288 struct hostcache_prune_data user;
290 if( !dns || (data->set.dns_cache_timeout == -1) || !data->dns.hostcache)
291 /* cache forever means never prune, and NULL hostcache means
296 user.cache_timeout = data->set.dns_cache_timeout;
298 if( !hostcache_timestamp_remove(&user,dns) )
301 Curl_hash_clean_with_criterium(data->dns.hostcache,
303 hostcache_timestamp_remove);
309 #ifdef HAVE_SIGSETJMP
310 /* Beware this is a global and unique instance. This is used to store the
311 return address that we can jump back to from inside a signal handler. This
312 is not thread-safe stuff. */
313 sigjmp_buf curl_jmpenv;
318 * Curl_cache_addr() stores a 'Curl_addrinfo' struct in the DNS cache.
320 * When calling Curl_resolv() has resulted in a response with a returned
321 * address, we call this function to store the information in the dns
324 * Returns the Curl_dns_entry entry pointer or NULL if the storage failed.
326 struct Curl_dns_entry *
327 Curl_cache_addr(struct SessionHandle *data,
329 const char *hostname,
334 struct Curl_dns_entry *dns;
335 struct Curl_dns_entry *dns2;
337 /* Create an entry id, based upon the hostname and port */
338 entry_id = create_hostcache_id(hostname, port);
339 /* If we can't create the entry id, fail */
342 entry_len = strlen(entry_id);
344 /* Create a new cache entry */
345 dns = calloc(1, sizeof(struct Curl_dns_entry));
351 dns->inuse = 0; /* init to not used */
352 dns->addr = addr; /* this is the address(es) */
353 time(&dns->timestamp);
354 if(dns->timestamp == 0)
355 dns->timestamp = 1; /* zero indicates that entry isn't in hash table */
357 /* Store the resolved data in our DNS cache. */
358 dns2 = Curl_hash_add(data->dns.hostcache, entry_id, entry_len+1,
367 dns->inuse++; /* mark entry as in-use */
369 /* free the allocated entry_id again */
376 * Curl_resolv() is the main name resolve function within libcurl. It resolves
377 * a name and returns a pointer to the entry in the 'entry' argument (if one
378 * is provided). This function might return immediately if we're using asynch
379 * resolves. See the return codes.
381 * The cache entry we return will get its 'inuse' counter increased when this
382 * function is used. You MUST call Curl_resolv_unlock() later (when you're
383 * done using this struct) to decrease the counter again.
385 * In debug mode, we specifically test for an interface name "LocalHost"
386 * and resolve "localhost" instead as a means to permit test cases
387 * to connect to a local test server with any host name.
391 * CURLRESOLV_ERROR (-1) = error, no pointer
392 * CURLRESOLV_RESOLVED (0) = OK, pointer provided
393 * CURLRESOLV_PENDING (1) = waiting for response, no pointer
396 int Curl_resolv(struct connectdata *conn,
397 const char *hostname,
399 struct Curl_dns_entry **entry)
401 char *entry_id = NULL;
402 struct Curl_dns_entry *dns = NULL;
404 struct SessionHandle *data = conn->data;
406 int rc = CURLRESOLV_ERROR; /* default to failure */
410 /* Create an entry id, based upon the hostname and port */
411 entry_id = create_hostcache_id(hostname, port);
412 /* If we can't create the entry id, fail */
416 entry_len = strlen(entry_id);
419 Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
421 /* See if its already in our dns cache */
422 dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len+1);
424 /* See whether the returned entry is stale. Done before we release lock */
425 if( remove_entry_if_stale(data, dns) )
426 dns = NULL; /* the memory deallocation is being handled by the hash */
429 dns->inuse++; /* we use it! */
430 rc = CURLRESOLV_RESOLVED;
434 Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
436 /* free the allocated entry_id again */
440 /* The entry was not in the cache. Resolve it to IP address */
445 /* Check what IP specifics the app has requested and if we can provide it.
446 * If not, bail out. */
447 if(!Curl_ipvalid(data))
448 return CURLRESOLV_ERROR;
450 /* If Curl_getaddrinfo() returns NULL, 'respwait' might be set to a
451 non-zero value indicating that we need to wait for the response to the
453 addr = Curl_getaddrinfo(conn,
455 (data->set.str[STRING_DEVICE]
456 && !strcmp(data->set.str[STRING_DEVICE],
457 "LocalHost"))?"localhost":
459 hostname, port, &respwait);
463 /* the response to our resolve call will come asynchronously at
464 a later time, good or bad */
465 /* First, check that we haven't received the info by now */
466 result = Curl_is_resolved(conn, &dns);
467 if(result) /* error detected */
468 return CURLRESOLV_ERROR;
470 rc = CURLRESOLV_RESOLVED; /* pointer provided */
472 rc = CURLRESOLV_PENDING; /* no info yet */
477 Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
479 /* we got a response, store it in the cache */
480 dns = Curl_cache_addr(data, addr, hostname, port);
483 Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
486 /* returned failure, bail out nicely */
487 Curl_freeaddrinfo(addr);
489 rc = CURLRESOLV_RESOLVED;
498 #ifdef USE_ALARM_TIMEOUT
500 * This signal handler jumps back into the main libcurl code and continues
501 * execution. This effectively causes the remainder of the application to run
502 * within a signal handler which is nonportable and could lead to problems.
505 RETSIGTYPE alarmfunc(int sig)
507 /* this is for "-ansi -Wall -pedantic" to stop complaining! (rabe) */
509 siglongjmp(curl_jmpenv, 1);
512 #endif /* USE_ALARM_TIMEOUT */
515 * Curl_resolv_timeout() is the same as Curl_resolv() but specifies a
516 * timeout. This function might return immediately if we're using asynch
517 * resolves. See the return codes.
519 * The cache entry we return will get its 'inuse' counter increased when this
520 * function is used. You MUST call Curl_resolv_unlock() later (when you're
521 * done using this struct) to decrease the counter again.
523 * If built with a synchronous resolver and use of signals is not
524 * disabled by the application, then a nonzero timeout will cause a
525 * timeout after the specified number of milliseconds. Otherwise, timeout
530 * CURLRESOLV_TIMEDOUT(-2) = warning, time too short or previous alarm expired
531 * CURLRESOLV_ERROR (-1) = error, no pointer
532 * CURLRESOLV_RESOLVED (0) = OK, pointer provided
533 * CURLRESOLV_PENDING (1) = waiting for response, no pointer
536 int Curl_resolv_timeout(struct connectdata *conn,
537 const char *hostname,
539 struct Curl_dns_entry **entry,
542 #ifdef USE_ALARM_TIMEOUT
543 #ifdef HAVE_SIGACTION
544 struct sigaction keep_sigact; /* store the old struct here */
545 bool keep_copysig=FALSE; /* did copy it? */
546 struct sigaction sigact;
549 void (*keep_sigact)(int); /* store the old handler here */
550 #endif /* HAVE_SIGNAL */
551 #endif /* HAVE_SIGACTION */
552 volatile long timeout;
553 unsigned int prev_alarm=0;
554 struct SessionHandle *data = conn->data;
555 #endif /* USE_ALARM_TIMEOUT */
560 #ifdef USE_ALARM_TIMEOUT
561 if (data->set.no_signal)
562 /* Ignore the timeout when signals are disabled */
568 /* USE_ALARM_TIMEOUT defined, but no timeout actually requested */
569 return Curl_resolv(conn, hostname, port, entry);
572 /* The alarm() function only provides integer second resolution, so if
573 we want to wait less than one second we must bail out already now. */
574 return CURLRESOLV_TIMEDOUT;
576 /*************************************************************
577 * Set signal handler to catch SIGALRM
578 * Store the old value to be able to set it back later!
579 *************************************************************/
580 #ifdef HAVE_SIGACTION
581 sigaction(SIGALRM, NULL, &sigact);
582 keep_sigact = sigact;
583 keep_copysig = TRUE; /* yes, we have a copy */
584 sigact.sa_handler = alarmfunc;
586 /* HPUX doesn't have SA_RESTART but defaults to that behaviour! */
587 sigact.sa_flags &= ~SA_RESTART;
589 /* now set the new struct */
590 sigaction(SIGALRM, &sigact, NULL);
591 #else /* HAVE_SIGACTION */
592 /* no sigaction(), revert to the much lamer signal() */
594 keep_sigact = signal(SIGALRM, alarmfunc);
596 #endif /* HAVE_SIGACTION */
598 /* alarm() makes a signal get sent when the timeout fires off, and that
599 will abort system calls */
600 prev_alarm = alarm((unsigned int) (timeout/1000L));
602 /* This allows us to time-out from the name resolver, as the timeout
603 will generate a signal and we will siglongjmp() from that here.
604 This technique has problems (see alarmfunc).
605 This should be the last thing we do before calling Curl_resolv(),
606 as otherwise we'd have to worry about variables that get modified
607 before we invoke Curl_resolv() (and thus use "volatile"). */
608 if(sigsetjmp(curl_jmpenv, 1)) {
609 /* this is coming from a siglongjmp() after an alarm signal */
610 failf(data, "name lookup timed out");
611 rc = CURLRESOLV_ERROR;
616 #ifndef CURLRES_ASYNCH
618 infof(conn->data, "timeout on name lookup is not supported\n");
620 (void)timeoutms; /* timeoutms not used with an async resolver */
622 #endif /* USE_ALARM_TIMEOUT */
624 /* Perform the actual name resolution. This might be interrupted by an
625 * alarm if it takes too long.
627 rc = Curl_resolv(conn, hostname, port, entry);
629 #ifdef USE_ALARM_TIMEOUT
633 /* deactivate a possibly active alarm before uninstalling the handler */
636 #ifdef HAVE_SIGACTION
638 /* we got a struct as it looked before, now put that one back nice
640 sigaction(SIGALRM, &keep_sigact, NULL); /* put it back */
644 /* restore the previous SIGALRM handler */
645 signal(SIGALRM, keep_sigact);
647 #endif /* HAVE_SIGACTION */
649 /* switch back the alarm() to either zero or to what it was before minus
650 the time we spent until now! */
652 /* there was an alarm() set before us, now put it back */
653 unsigned long elapsed_ms = Curl_tvdiff(Curl_tvnow(), conn->created);
655 /* the alarm period is counted in even number of seconds */
656 unsigned long alarm_set = prev_alarm - elapsed_ms/1000;
659 ((alarm_set >= 0x80000000) && (prev_alarm < 0x80000000)) ) {
660 /* if the alarm time-left reached zero or turned "negative" (counted
661 with unsigned values), we should fire off a SIGALRM here, but we
662 won't, and zero would be to switch it off so we never set it to
665 rc = CURLRESOLV_TIMEDOUT;
666 failf(data, "Previous alarm fired off!");
669 alarm((unsigned int)alarm_set);
671 #endif /* USE_ALARM_TIMEOUT */
677 * Curl_resolv_unlock() unlocks the given cached DNS entry. When this has been
678 * made, the struct may be destroyed due to pruning. It is important that only
679 * one unlock is made for each Curl_resolv() call.
681 void Curl_resolv_unlock(struct SessionHandle *data, struct Curl_dns_entry *dns)
683 DEBUGASSERT(dns && (dns->inuse>0));
686 Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
689 /* only free if nobody is using AND it is not in hostcache (timestamp ==
691 if (dns->inuse == 0 && dns->timestamp == 0) {
692 Curl_freeaddrinfo(dns->addr);
697 Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
701 * File-internal: free a cache dns entry.
703 static void freednsentry(void *freethis)
705 struct Curl_dns_entry *p = (struct Curl_dns_entry *) freethis;
707 /* mark the entry as not in hostcache */
710 Curl_freeaddrinfo(p->addr);
716 * Curl_mk_dnscache() creates a new DNS cache and returns the handle for it.
718 struct curl_hash *Curl_mk_dnscache(void)
720 return Curl_hash_alloc(7, Curl_hash_str, Curl_str_key_compare, freednsentry);