1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2008, 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.
22 ***************************************************************************/
31 #ifdef HAVE_SYS_SOCKET_H
32 #include <sys/socket.h>
34 #ifdef HAVE_NETINET_IN_H
35 #include <netinet/in.h>
40 #ifdef HAVE_ARPA_INET_H
41 #include <arpa/inet.h>
44 #include <stdlib.h> /* required for free() prototypes */
47 #include <unistd.h> /* for the close() proto */
73 #include "inet_ntop.h"
75 #define _MPRINTF_REPLACE /* use our functions only */
76 #include <curl/mprintf.h>
79 /* The last #include file should be: */
82 #if defined(HAVE_ALARM) && defined(SIGALRM) && defined(HAVE_SIGSETJMP) \
84 /* alarm-based timeouts can only be used with all the dependencies satisfied */
85 #define USE_ALARM_TIMEOUT
92 * The main COMPILE-TIME DEFINES to keep in mind when reading the host*.c
93 * source file are these:
95 * CURLRES_IPV6 - this host has getaddrinfo() and family, and thus we use
96 * that. The host may not be able to resolve IPv6, but we don't really have to
97 * take that into account. Hosts that aren't IPv6-enabled have CURLRES_IPV4
100 * CURLRES_ARES - is defined if libcurl is built to use c-ares for
101 * asynchronous name resolves. This can be Windows or *nix.
103 * CURLRES_THREADED - is defined if libcurl is built to run under (native)
104 * Windows, and then the name resolve will be done in a new thread, and the
105 * supported API will be the same as for ares-builds.
107 * If any of the two previous are defined, CURLRES_ASYNCH is defined too. If
108 * libcurl is not built to use an asynchronous resolver, CURLRES_SYNCH is
111 * The host*.c sources files are split up like this:
113 * hostip.c - method-independent resolver functions and utility functions
114 * hostasyn.c - functions for asynchronous name resolves
115 * hostsyn.c - functions for synchronous name resolves
116 * hostares.c - functions for ares-using name resolves
117 * hostthre.c - functions for threaded name resolves
118 * hostip4.c - ipv4-specific functions
119 * hostip6.c - ipv6-specific functions
121 * The hostip.h is the united header file for all this. It defines the
122 * CURLRES_* defines based on the config*.h and setup.h defines.
125 /* These two symbols are for the global DNS cache */
126 static struct curl_hash hostname_cache;
127 static int host_cache_initialized;
129 static void freednsentry(void *freethis);
132 * Curl_global_host_cache_init() initializes and sets up a global DNS cache.
133 * Global DNS cache is general badness. Do not use. This will be removed in
134 * a future version. Use the share interface instead!
136 * Returns a struct curl_hash pointer on success, NULL on failure.
138 struct curl_hash *Curl_global_host_cache_init(void)
141 if(!host_cache_initialized) {
142 rc = Curl_hash_init(&hostname_cache, 7, Curl_hash_str,
143 Curl_str_key_compare, freednsentry);
145 host_cache_initialized = 1;
147 return rc?NULL:&hostname_cache;
151 * Destroy and cleanup the global DNS cache
153 void Curl_global_host_cache_dtor(void)
155 if(host_cache_initialized) {
156 Curl_hash_clean(&hostname_cache);
157 host_cache_initialized = 0;
162 * Return # of adresses in a Curl_addrinfo struct
164 int Curl_num_addresses(const Curl_addrinfo *addr)
168 addr = addr->ai_next;
175 * Curl_printable_address() returns a printable version of the 1st address
176 * given in the 'ai' argument. The result will be stored in the buf that is
179 * If the conversion fails, it returns NULL.
182 Curl_printable_address(const Curl_addrinfo *ai, char *buf, size_t bufsize)
184 struct sockaddr_in *sa4;
185 struct in_addr *ipaddr4;
187 struct sockaddr_in6 *sa6;
188 struct in6_addr *ipaddr6;
191 switch (ai->ai_family) {
193 sa4 = (struct sockaddr_in *)ai->ai_addr;
194 ipaddr4 = &sa4->sin_addr;
195 return Curl_inet_ntop(ai->ai_family, (const void *)ipaddr4, buf, bufsize);
198 sa6 = (struct sockaddr_in6 *)ai->ai_addr;
199 ipaddr6 = &sa6->sin6_addr;
200 return Curl_inet_ntop(ai->ai_family, (const void *)ipaddr6, buf, bufsize);
209 * Return a hostcache id string for the providing host + port, to be used by
213 create_hostcache_id(const char *server, int port)
215 /* create and return the new allocated entry */
216 return aprintf("%s:%d", server, port);
219 struct hostcache_prune_data {
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.
228 * Returning non-zero means remove the entry, return 0 to keep it in the
232 hostcache_timestamp_remove(void *datap, void *hc)
234 struct hostcache_prune_data *data =
235 (struct hostcache_prune_data *) datap;
236 struct Curl_dns_entry *c = (struct Curl_dns_entry *) hc;
238 if((data->now - c->timestamp < data->cache_timeout) ||
240 /* please don't remove */
249 * Prune the DNS cache. This assumes that a lock has already been taken.
252 hostcache_prune(struct curl_hash *hostcache, long cache_timeout, time_t now)
254 struct hostcache_prune_data user;
256 user.cache_timeout = cache_timeout;
259 Curl_hash_clean_with_criterium(hostcache,
261 hostcache_timestamp_remove);
265 * Library-wide function for pruning the DNS cache. This function takes and
266 * returns the appropriate locks.
268 void Curl_hostcache_prune(struct SessionHandle *data)
272 if((data->set.dns_cache_timeout == -1) || !data->dns.hostcache)
273 /* cache forever means never prune, and NULL hostcache means
278 Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
282 /* Remove outdated and unused entries from the hostcache */
283 hostcache_prune(data->dns.hostcache,
284 data->set.dns_cache_timeout,
288 Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
292 * Check if the entry should be pruned. Assumes a locked cache.
295 remove_entry_if_stale(struct SessionHandle *data, struct Curl_dns_entry *dns)
297 struct hostcache_prune_data user;
299 if( !dns || (data->set.dns_cache_timeout == -1) || !data->dns.hostcache)
300 /* cache forever means never prune, and NULL hostcache means
305 user.cache_timeout = data->set.dns_cache_timeout;
307 if( !hostcache_timestamp_remove(&user,dns) )
310 Curl_hash_clean_with_criterium(data->dns.hostcache,
312 hostcache_timestamp_remove);
318 #ifdef HAVE_SIGSETJMP
319 /* Beware this is a global and unique instance. This is used to store the
320 return address that we can jump back to from inside a signal handler. This
321 is not thread-safe stuff. */
322 sigjmp_buf curl_jmpenv;
327 * Curl_cache_addr() stores a 'Curl_addrinfo' struct in the DNS cache.
329 * When calling Curl_resolv() has resulted in a response with a returned
330 * address, we call this function to store the information in the dns
333 * Returns the Curl_dns_entry entry pointer or NULL if the storage failed.
335 struct Curl_dns_entry *
336 Curl_cache_addr(struct SessionHandle *data,
338 const char *hostname,
343 struct Curl_dns_entry *dns;
344 struct Curl_dns_entry *dns2;
347 /* Create an entry id, based upon the hostname and port */
348 entry_id = create_hostcache_id(hostname, port);
349 /* If we can't create the entry id, fail */
352 entry_len = strlen(entry_id);
354 /* Create a new cache entry */
355 dns = calloc(sizeof(struct Curl_dns_entry), 1);
361 dns->inuse = 0; /* init to not used */
362 dns->addr = addr; /* this is the address(es) */
364 /* Store the resolved data in our DNS cache. This function may return a
365 pointer to an existing struct already present in the hash, and it may
366 return the same argument we pass in. Make no assumptions. */
367 dns2 = Curl_hash_add(data->dns.hostcache, entry_id, entry_len+1,
370 /* Major badness, run away. */
378 dns->timestamp = now; /* used now */
379 dns->inuse++; /* mark entry as in-use */
381 /* free the allocated entry_id again */
388 * Curl_resolv() is the main name resolve function within libcurl. It resolves
389 * a name and returns a pointer to the entry in the 'entry' argument (if one
390 * is provided). This function might return immediately if we're using asynch
391 * resolves. See the return codes.
393 * The cache entry we return will get its 'inuse' counter increased when this
394 * function is used. You MUST call Curl_resolv_unlock() later (when you're
395 * done using this struct) to decrease the counter again.
399 * CURLRESOLV_ERROR (-1) = error, no pointer
400 * CURLRESOLV_RESOLVED (0) = OK, pointer provided
401 * CURLRESOLV_PENDING (1) = waiting for response, no pointer
404 int Curl_resolv(struct connectdata *conn,
405 const char *hostname,
407 struct Curl_dns_entry **entry)
409 char *entry_id = NULL;
410 struct Curl_dns_entry *dns = NULL;
412 struct SessionHandle *data = conn->data;
414 int rc = CURLRESOLV_ERROR; /* default to failure */
418 /* Create an entry id, based upon the hostname and port */
419 entry_id = create_hostcache_id(hostname, port);
420 /* If we can't create the entry id, fail */
424 entry_len = strlen(entry_id);
427 Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
429 /* See if its already in our dns cache */
430 dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len+1);
432 /* See whether the returned entry is stale. Done before we release lock */
433 if( remove_entry_if_stale(data, dns) )
434 dns = NULL; /* the memory deallocation is being handled by the hash */
437 dns->inuse++; /* we use it! */
438 rc = CURLRESOLV_RESOLVED;
442 Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
444 /* free the allocated entry_id again */
448 /* The entry was not in the cache. Resolve it to IP address */
453 /* Check what IP specifics the app has requested and if we can provide it.
454 * If not, bail out. */
455 if(!Curl_ipvalid(data))
456 return CURLRESOLV_ERROR;
458 /* If Curl_getaddrinfo() returns NULL, 'respwait' might be set to a
459 non-zero value indicating that we need to wait for the response to the
461 addr = Curl_getaddrinfo(conn, hostname, port, &respwait);
465 /* the response to our resolve call will come asynchronously at
466 a later time, good or bad */
467 /* First, check that we haven't received the info by now */
468 result = Curl_is_resolved(conn, &dns);
469 if(result) /* error detected */
470 return CURLRESOLV_ERROR;
472 rc = CURLRESOLV_RESOLVED; /* pointer provided */
474 rc = CURLRESOLV_PENDING; /* no info yet */
479 Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
481 /* we got a response, store it in the cache */
482 dns = Curl_cache_addr(data, addr, hostname, port);
485 Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
488 /* returned failure, bail out nicely */
489 Curl_freeaddrinfo(addr);
491 rc = CURLRESOLV_RESOLVED;
500 #ifdef USE_ALARM_TIMEOUT
502 * This signal handler jumps back into the main libcurl code and continues
503 * execution. This effectively causes the remainder of the application to run
504 * within a signal handler which is nonportable and could lead to problems.
507 RETSIGTYPE alarmfunc(int sig)
509 /* this is for "-ansi -Wall -pedantic" to stop complaining! (rabe) */
511 siglongjmp(curl_jmpenv, 1);
514 #endif /* USE_ALARM_TIMEOUT */
517 * Curl_resolv_timeout() is the same as Curl_resolv() but specifies a
518 * timeout. This function might return immediately if we're using asynch
519 * resolves. See the return codes.
521 * The cache entry we return will get its 'inuse' counter increased when this
522 * function is used. You MUST call Curl_resolv_unlock() later (when you're
523 * done using this struct) to decrease the counter again.
525 * If built with a synchronous resolver and use of signals is not
526 * disabled by the application, then a nonzero timeout will cause a
527 * timeout after the specified number of milliseconds. Otherwise, timeout
532 * CURLRESOLV_TIMEDOUT(-2) = warning, time too short or previous alarm expired
533 * CURLRESOLV_ERROR (-1) = error, no pointer
534 * CURLRESOLV_RESOLVED (0) = OK, pointer provided
535 * CURLRESOLV_PENDING (1) = waiting for response, no pointer
538 int Curl_resolv_timeout(struct connectdata *conn,
539 const char *hostname,
541 struct Curl_dns_entry **entry,
544 #ifdef USE_ALARM_TIMEOUT
545 #ifdef HAVE_SIGACTION
546 struct sigaction keep_sigact; /* store the old struct here */
547 bool keep_copysig=FALSE; /* did copy it? */
548 struct sigaction sigact;
551 void (*keep_sigact)(int); /* store the old handler here */
552 #endif /* HAVE_SIGNAL */
553 #endif /* HAVE_SIGACTION */
554 volatile long timeout;
555 unsigned int prev_alarm=0;
556 struct SessionHandle *data = conn->data;
557 #endif /* USE_ALARM_TIMEOUT */
562 #ifdef USE_ALARM_TIMEOUT
563 if (data->set.no_signal)
564 /* Ignore the timeout when signals are disabled */
569 if(timeout && timeout < 1000)
570 /* The alarm() function only provides integer second resolution, so if
571 we want to wait less than one second we must bail out already now. */
572 return CURLRESOLV_TIMEDOUT;
575 /* This allows us to time-out from the name resolver, as the timeout
576 will generate a signal and we will siglongjmp() from that here.
577 This technique has problems (see alarmfunc). */
578 if(sigsetjmp(curl_jmpenv, 1)) {
579 /* this is coming from a siglongjmp() after an alarm signal */
580 failf(data, "name lookup timed out");
581 return CURLRESOLV_ERROR;
584 /*************************************************************
585 * Set signal handler to catch SIGALRM
586 * Store the old value to be able to set it back later!
587 *************************************************************/
588 #ifdef HAVE_SIGACTION
589 sigaction(SIGALRM, NULL, &sigact);
590 keep_sigact = sigact;
591 keep_copysig = TRUE; /* yes, we have a copy */
592 sigact.sa_handler = alarmfunc;
594 /* HPUX doesn't have SA_RESTART but defaults to that behaviour! */
595 sigact.sa_flags &= ~SA_RESTART;
597 /* now set the new struct */
598 sigaction(SIGALRM, &sigact, NULL);
599 #else /* HAVE_SIGACTION */
600 /* no sigaction(), revert to the much lamer signal() */
602 keep_sigact = signal(SIGALRM, alarmfunc);
604 #endif /* HAVE_SIGACTION */
606 /* alarm() makes a signal get sent when the timeout fires off, and that
607 will abort system calls */
608 prev_alarm = alarm((unsigned int) (timeout/1000L));
612 #ifndef CURLRES_ASYNCH
614 infof(conn->data, "timeout on name lookup is not supported\n");
616 (void)timeoutms; /* timeoutms not used with an async resolver */
618 #endif /* USE_ALARM_TIMEOUT */
620 /* Perform the actual name resolution. This might be interrupted by an
621 * alarm if it takes too long.
623 rc = Curl_resolv(conn, hostname, port, entry);
625 #ifdef USE_ALARM_TIMEOUT
628 #ifdef HAVE_SIGACTION
630 /* we got a struct as it looked before, now put that one back nice
632 sigaction(SIGALRM, &keep_sigact, NULL); /* put it back */
636 /* restore the previous SIGALRM handler */
637 signal(SIGALRM, keep_sigact);
639 #endif /* HAVE_SIGACTION */
641 /* switch back the alarm() to either zero or to what it was before minus
642 the time we spent until now! */
644 /* there was an alarm() set before us, now put it back */
645 unsigned long elapsed_ms = Curl_tvdiff(Curl_tvnow(), conn->created);
647 /* the alarm period is counted in even number of seconds */
648 unsigned long alarm_set = prev_alarm - elapsed_ms/1000;
651 ((alarm_set >= 0x80000000) && (prev_alarm < 0x80000000)) ) {
652 /* if the alarm time-left reached zero or turned "negative" (counted
653 with unsigned values), we should fire off a SIGALRM here, but we
654 won't, and zero would be to switch it off so we never set it to
657 rc = CURLRESOLV_TIMEDOUT;
658 failf(data, "Previous alarm fired off!");
661 alarm((unsigned int)alarm_set);
664 alarm(0); /* just shut it off */
666 #endif /* USE_ALARM_TIMEOUT */
672 * Curl_resolv_unlock() unlocks the given cached DNS entry. When this has been
673 * made, the struct may be destroyed due to pruning. It is important that only
674 * one unlock is made for each Curl_resolv() call.
676 void Curl_resolv_unlock(struct SessionHandle *data, struct Curl_dns_entry *dns)
678 DEBUGASSERT(dns && (dns->inuse>0));
681 Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
686 Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
690 * File-internal: free a cache dns entry.
692 static void freednsentry(void *freethis)
694 struct Curl_dns_entry *p = (struct Curl_dns_entry *) freethis;
696 Curl_freeaddrinfo(p->addr);
702 * Curl_mk_dnscache() creates a new DNS cache and returns the handle for it.
704 struct curl_hash *Curl_mk_dnscache(void)
706 return Curl_hash_alloc(7, Curl_hash_str, Curl_str_key_compare, freednsentry);
709 #ifdef CURLRES_ADDRINFO_COPY
711 /* align on even 64bit boundaries */
712 #define MEMALIGN(x) ((x)+(8-(((unsigned long)(x))&0x7)))
715 * Curl_addrinfo_copy() performs a "deep" copy of a hostent into a buffer and
716 * returns a pointer to the malloc()ed copy. You need to call free() on the
717 * returned buffer when you're done with it.
719 Curl_addrinfo *Curl_addrinfo_copy(const void *org, int port)
721 const struct hostent *orig = org;
723 return Curl_he2ai(orig, port);
725 #endif /* CURLRES_ADDRINFO_COPY */
727 /***********************************************************************
728 * Only for plain-ipv4 and c-ares builds (NOTE: c-ares builds can be IPv6
730 **********************************************************************/
732 #if defined(CURLRES_IPV4) || defined(CURLRES_ARES)
734 * This is a function for freeing name information in a protocol independent
737 void Curl_freeaddrinfo(Curl_addrinfo *ai)
741 /* walk over the list and free all entries */
747 free(ai->ai_canonname);
748 memset(ai, 0, sizeof(Curl_addrinfo));
755 struct hostent hostentry;
756 struct in_addr addrentry;
757 char *h_addr_list[2];
761 * Curl_ip2addr() takes a 32bit ipv4 internet address as input parameter
762 * together with a pointer to the string version of the address, and it
763 * returns a Curl_addrinfo chain filled in correctly with information for this
766 * The input parameters ARE NOT checked for validity but they are expected
767 * to have been checked already when this is called.
769 Curl_addrinfo *Curl_ip2addr(in_addr_t num, const char *hostname, int port)
773 #if defined(VMS) && \
774 defined(__INITIAL_POINTER_SIZE) && (__INITIAL_POINTER_SIZE == 64)
775 #pragma pointer_size save
776 #pragma pointer_size short
777 #pragma message disable PTRMISMATCH
781 struct in_addr *addrentry;
782 struct namebuf4 *buf;
785 DEBUGASSERT(hostname);
787 buf = malloc(sizeof(struct namebuf4));
791 hoststr = strdup(hostname);
797 addrentry = &buf->addrentry;
799 /* On UNICOS, s_addr is a bit field and for some reason assigning to it
800 * doesn't work. There must be a better fix than this ugly hack.
802 memcpy(addrentry, &num, SIZEOF_in_addr);
804 addrentry->s_addr = num;
810 h->h_addrtype = AF_INET;
811 h->h_length = sizeof(struct in_addr);
812 h->h_addr_list = &buf->h_addr_list[0];
813 h->h_addr_list[0] = (char*)addrentry;
814 h->h_addr_list[1] = NULL; /* terminate list of entries */
816 #if defined(VMS) && \
817 defined(__INITIAL_POINTER_SIZE) && (__INITIAL_POINTER_SIZE == 64)
818 #pragma pointer_size restore
819 #pragma message enable PTRMISMATCH
822 ai = Curl_he2ai(h, port);
831 * Curl_he2ai() translates from a hostent struct to a Curl_addrinfo struct.
832 * The Curl_addrinfo is meant to work like the addrinfo struct does for IPv6
833 * stacks, but for all hosts and environments.
835 * Curl_addrinfo defined in "lib/hostip.h"
837 * struct Curl_addrinfo {
842 * socklen_t ai_addrlen; * Follow rfc3493 struct addrinfo *
843 * char *ai_canonname;
844 * struct sockaddr *ai_addr;
845 * struct Curl_addrinfo *ai_next;
848 * hostent defined in <netdb.h>
855 * char **h_addr_list;
858 * for backward compatibility:
860 * #define h_addr h_addr_list[0]
863 Curl_addrinfo *Curl_he2ai(const struct hostent *he, int port)
866 Curl_addrinfo *prevai = NULL;
867 Curl_addrinfo *firstai = NULL;
868 struct sockaddr_in *addr;
870 struct sockaddr_in6 *addr6;
871 #endif /* CURLRES_IPV6 */
872 CURLcode result = CURLE_OK;
877 /* no input == no output! */
880 for(i=0; (curr = he->h_addr_list[i]) != NULL; i++) {
884 if (he->h_addrtype == AF_INET6)
885 ss_size = sizeof (struct sockaddr_in6);
887 #endif /* CURLRES_IPV6 */
888 ss_size = sizeof (struct sockaddr_in);
890 if((ai = calloc(1, sizeof(Curl_addrinfo))) == NULL) {
891 result = CURLE_OUT_OF_MEMORY;
894 if((ai->ai_canonname = strdup(he->h_name)) == NULL) {
895 result = CURLE_OUT_OF_MEMORY;
899 if((ai->ai_addr = calloc(1, ss_size)) == NULL) {
900 result = CURLE_OUT_OF_MEMORY;
901 free(ai->ai_canonname);
907 /* store the pointer we want to return from this function */
911 /* make the previous entry point to this */
912 prevai->ai_next = ai;
914 ai->ai_family = he->h_addrtype;
916 /* we return all names as STREAM, so when using this address for TFTP
917 the type must be ignored and conn->socktype be used instead! */
918 ai->ai_socktype = SOCK_STREAM;
920 ai->ai_addrlen = ss_size;
922 /* leave the rest of the struct filled with zero */
924 switch (ai->ai_family) {
926 addr = (struct sockaddr_in *)ai->ai_addr; /* storage area for this info */
928 memcpy(&addr->sin_addr, curr, sizeof(struct in_addr));
929 addr->sin_family = (unsigned short)(he->h_addrtype);
930 addr->sin_port = htons((unsigned short)port);
935 addr6 = (struct sockaddr_in6 *)ai->ai_addr; /* storage area for this info */
937 memcpy(&addr6->sin6_addr, curr, sizeof(struct in6_addr));
938 addr6->sin6_family = (unsigned short)(he->h_addrtype);
939 addr6->sin6_port = htons((unsigned short)port);
941 #endif /* CURLRES_IPV6 */
947 if(result != CURLE_OK) {
948 Curl_freeaddrinfo(firstai);
955 #endif /* CURLRES_IPV4 || CURLRES_ARES */