1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2006, 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 ***************************************************************************/
28 #if defined(HAVE_MALLOC_H) && defined(NEED_MALLOC_H)
31 #ifdef HAVE_SYS_TYPES_H
32 #include <sys/types.h>
34 #ifdef HAVE_SYS_SOCKET_H
35 #include <sys/socket.h>
37 #ifdef HAVE_NETINET_IN_H
38 #include <netinet/in.h>
43 #ifdef HAVE_ARPA_INET_H
44 #include <arpa/inet.h>
47 #include <stdlib.h> /* required for free() prototypes */
50 #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>
78 #if defined(HAVE_INET_NTOA_R) && !defined(HAVE_INET_NTOA_R_DECL)
79 #include "inet_ntoa_r.h"
83 /* The last #include file should be: */
90 * The main COMPILE-TIME DEFINES to keep in mind when reading the host*.c
91 * source file are these:
93 * CURLRES_IPV6 - this host has getaddrinfo() and family, and thus we use
94 * that. The host may not be able to resolve IPv6, but we don't really have to
95 * take that into account. Hosts that aren't IPv6-enabled have CURLRES_IPV4
98 * CURLRES_ARES - is defined if libcurl is built to use c-ares for
99 * asynchronous name resolves. It cannot have ENABLE_IPV6 defined at the same
100 * time, as c-ares has no ipv6 support. This can be Windows or *nix.
102 * CURLRES_THREADED - is defined if libcurl is built to run under (native)
103 * Windows, and then the name resolve will be done in a new thread, and the
104 * supported API will be the same as for ares-builds.
106 * If any of the two previous are defined, CURLRES_ASYNCH is defined too. If
107 * libcurl is not built to use an asynchronous resolver, CURLRES_SYNCH is
110 * The host*.c sources files are split up like this:
112 * hostip.c - method-independent resolver functions and utility functions
113 * hostasyn.c - functions for asynchronous name resolves
114 * hostsyn.c - functions for synchronous name resolves
115 * hostares.c - functions for ares-using name resolves
116 * hostthre.c - functions for threaded name resolves
117 * hostip4.c - ipv4-specific functions
118 * hostip6.c - ipv6-specific functions
120 * The hostip.h is the united header file for all this. It defines the
121 * CURLRES_* defines based on the config*.h and setup.h defines.
124 /* These two symbols are for the global DNS cache */
125 static struct curl_hash hostname_cache;
126 static int host_cache_initialized;
128 static void freednsentry(void *freethis);
131 * Curl_global_host_cache_init() initializes and sets up a global DNS cache.
132 * Global DNS cache is general badness. Do not use. This will be removed in
133 * a future version. Use the share interface instead!
135 void Curl_global_host_cache_init(void)
137 if (!host_cache_initialized) {
138 Curl_hash_init(&hostname_cache, 7, freednsentry);
139 host_cache_initialized = 1;
144 * Return a pointer to the global cache
146 struct curl_hash *Curl_global_host_cache_get(void)
148 return &hostname_cache;
152 * Destroy and cleanup the global DNS cache
154 void Curl_global_host_cache_dtor(void)
156 if (host_cache_initialized) {
157 Curl_hash_clean(&hostname_cache);
158 host_cache_initialized = 0;
163 * Return # of adresses in a Curl_addrinfo struct
165 int Curl_num_addresses(const Curl_addrinfo *addr)
168 for (i = 0; addr; addr = addr->ai_next, i++)
174 * Curl_printable_address() returns a printable version of the 1st address
175 * given in the 'ip' argument. The result will be stored in the buf that is
178 * If the conversion fails, it returns NULL.
180 const char *Curl_printable_address(const Curl_addrinfo *ip,
181 char *buf, size_t bufsize)
183 const void *ip4 = &((const struct sockaddr_in*)ip->ai_addr)->sin_addr;
184 int af = ip->ai_family;
186 const void *ip6 = &((const struct sockaddr_in6*)ip->ai_addr)->sin6_addr;
188 const void *ip6 = NULL;
191 return Curl_inet_ntop(af, af == AF_INET ? ip4 : ip6, buf, bufsize);
195 * Return a hostcache id string for the providing host + port, to be used by
199 create_hostcache_id(char *server, int port)
201 /* create and return the new allocated entry */
202 return aprintf("%s:%d", server, port);
205 struct hostcache_prune_data {
211 * This function is set as a callback to be called for every entry in the DNS
212 * cache when we want to prune old unused entries.
214 * Returning non-zero means remove the entry, return 0 to keep it in the
218 hostcache_timestamp_remove(void *datap, void *hc)
220 struct hostcache_prune_data *data =
221 (struct hostcache_prune_data *) datap;
222 struct Curl_dns_entry *c = (struct Curl_dns_entry *) hc;
224 if ((data->now - c->timestamp < data->cache_timeout) ||
226 /* please don't remove */
235 * Prune the DNS cache. This assumes that a lock has already been taken.
238 hostcache_prune(struct curl_hash *hostcache, int cache_timeout, time_t now)
240 struct hostcache_prune_data user;
242 user.cache_timeout = cache_timeout;
245 Curl_hash_clean_with_criterium(hostcache,
247 hostcache_timestamp_remove);
251 * Library-wide function for pruning the DNS cache. This function takes and
252 * returns the appropriate locks.
254 void Curl_hostcache_prune(struct SessionHandle *data)
258 if((data->set.dns_cache_timeout == -1) || !data->dns.hostcache)
259 /* cache forever means never prune, and NULL hostcache means
264 Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
268 /* Remove outdated and unused entries from the hostcache */
269 hostcache_prune(data->dns.hostcache,
270 data->set.dns_cache_timeout,
274 Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
278 remove_entry_if_stale(struct SessionHandle *data, struct Curl_dns_entry *dns)
280 struct hostcache_prune_data user;
282 if( !dns || (data->set.dns_cache_timeout == -1) || !data->dns.hostcache)
283 /* cache forever means never prune, and NULL hostcache means
288 user.cache_timeout = data->set.dns_cache_timeout;
290 if ( !hostcache_timestamp_remove(&user,dns) )
293 /* ok, we do need to clear the cache. although we need to remove just a
294 single entry we clean the entire hash, as no explicit delete function
297 Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
299 Curl_hash_clean_with_criterium(data->dns.hostcache,
301 hostcache_timestamp_remove);
304 Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
310 #ifdef HAVE_SIGSETJMP
311 /* Beware this is a global and unique instance. This is used to store the
312 return address that we can jump back to from inside a signal handler. This
313 is not thread-safe stuff. */
314 sigjmp_buf curl_jmpenv;
319 * Curl_cache_addr() stores a 'Curl_addrinfo' struct in the DNS cache.
321 * When calling Curl_resolv() has resulted in a response with a returned
322 * address, we call this function to store the information in the dns
325 * Returns the Curl_dns_entry entry pointer or NULL if the storage failed.
327 struct Curl_dns_entry *
328 Curl_cache_addr(struct SessionHandle *data,
335 struct Curl_dns_entry *dns;
336 struct Curl_dns_entry *dns2;
339 /* Create an entry id, based upon the hostname and port */
340 entry_id = create_hostcache_id(hostname, port);
341 /* If we can't create the entry id, fail */
344 entry_len = strlen(entry_id);
346 /* Create a new cache entry */
347 dns = (struct Curl_dns_entry *) malloc(sizeof(struct Curl_dns_entry));
353 dns->inuse = 0; /* init to not used */
354 dns->addr = addr; /* this is the address(es) */
356 /* Store the resolved data in our DNS cache. This function may return a
357 pointer to an existing struct already present in the hash, and it may
358 return the same argument we pass in. Make no assumptions. */
359 dns2 = Curl_hash_add(data->dns.hostcache, entry_id, entry_len+1,
362 /* Major badness, run away. */
370 dns->timestamp = now; /* used now */
371 dns->inuse++; /* mark entry as in-use */
373 /* free the allocated entry_id again */
380 * Curl_resolv() is the main name resolve function within libcurl. It resolves
381 * a name and returns a pointer to the entry in the 'entry' argument (if one
382 * is provided). This function might return immediately if we're using asynch
383 * resolves. See the return codes.
385 * The cache entry we return will get its 'inuse' counter increased when this
386 * function is used. You MUST call Curl_resolv_unlock() later (when you're
387 * done using this struct) to decrease the counter again.
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,
399 struct Curl_dns_entry **entry)
401 char *entry_id = NULL;
402 struct Curl_dns_entry *dns = NULL;
405 struct SessionHandle *data = conn->data;
410 #ifdef HAVE_SIGSETJMP
411 /* this allows us to time-out from the name resolver, as the timeout
412 will generate a signal and we will siglongjmp() from that here */
413 if(!data->set.no_signal && sigsetjmp(curl_jmpenv, 1)) {
414 /* this is coming from a siglongjmp() */
415 failf(data, "name lookup timed out");
416 return CURLRESOLV_ERROR;
420 /* Create an entry id, based upon the hostname and port */
421 entry_id = create_hostcache_id(hostname, port);
422 /* If we can't create the entry id, fail */
424 return CURLRESOLV_ERROR;
426 entry_len = strlen(entry_id);
429 Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
431 /* See if its already in our dns cache */
432 dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len+1);
435 Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
437 /* free the allocated entry_id again */
440 /* See whether the returned entry is stale. Deliberately done after the
442 if ( remove_entry_if_stale(data,dns) )
443 dns = NULL; /* the memory deallocation is being handled by the hash */
445 rc = CURLRESOLV_ERROR; /* default to failure */
448 /* The entry was not in the cache. Resolve it to IP address */
452 /* Check what IP specifics the app has requested and if we can provide it.
453 * If not, bail out. */
454 if(!Curl_ipvalid(data))
455 return CURLRESOLV_ERROR;
457 /* If Curl_getaddrinfo() returns NULL, 'wait' might be set to a non-zero
458 value indicating that we need to wait for the response to the resolve
460 addr = Curl_getaddrinfo(conn, hostname, port, &wait);
464 /* the response to our resolve call will come asynchronously at
465 a later time, good or bad */
466 /* First, check that we haven't received the info by now */
467 result = Curl_is_resolved(conn, &dns);
468 if(result) /* error detected */
469 return CURLRESOLV_ERROR;
471 rc = CURLRESOLV_RESOLVED; /* pointer provided */
473 rc = CURLRESOLV_PENDING; /* no info yet */
478 Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
480 /* we got a response, store it in the cache */
481 dns = Curl_cache_addr(data, addr, hostname, port);
484 Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
487 /* returned failure, bail out nicely */
488 Curl_freeaddrinfo(addr);
490 rc = CURLRESOLV_RESOLVED;
495 Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
496 dns->inuse++; /* we use it! */
498 Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
499 rc = CURLRESOLV_RESOLVED;
508 * Curl_resolv_unlock() unlocks the given cached DNS entry. When this has been
509 * made, the struct may be destroyed due to pruning. It is important that only
510 * one unlock is made for each Curl_resolv() call.
512 void Curl_resolv_unlock(struct SessionHandle *data, struct Curl_dns_entry *dns)
514 curlassert(dns && (dns->inuse>0));
517 Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
522 Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
526 * File-internal: free a cache dns entry.
528 static void freednsentry(void *freethis)
530 struct Curl_dns_entry *p = (struct Curl_dns_entry *) freethis;
532 Curl_freeaddrinfo(p->addr);
538 * Curl_mk_dnscache() creates a new DNS cache and returns the handle for it.
540 struct curl_hash *Curl_mk_dnscache(void)
542 return Curl_hash_alloc(7, freednsentry);
545 #ifdef CURLRES_ADDRINFO_COPY
547 /* align on even 64bit boundaries */
548 #define MEMALIGN(x) ((x)+(8-(((unsigned long)(x))&0x7)))
551 * Curl_addrinfo_copy() performs a "deep" copy of a hostent into a buffer and
552 * returns a pointer to the malloc()ed copy. You need to call free() on the
553 * returned buffer when you're done with it.
555 Curl_addrinfo *Curl_addrinfo_copy(void *org, int port)
557 struct hostent *orig = org;
559 return Curl_he2ai(orig, port);
561 #endif /* CURLRES_ADDRINFO_COPY */