* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2008, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
#include "setup.h"
#include <string.h>
-#include <errno.h>
-#ifdef HAVE_MALLOC_H /* Win32 */
+#ifdef NEED_MALLOC_H
#include <malloc.h>
#endif
-#ifdef HAVE_SYS_TYPES_H
-#include <sys/types.h>
-#endif
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#define _MPRINTF_REPLACE /* use our functions only */
#include <curl/mprintf.h>
-#if defined(HAVE_INET_NTOA_R) && !defined(HAVE_INET_NTOA_R_DECL)
-#include "inet_ntoa_r.h"
-#endif
-
#include "memory.h"
/* The last #include file should be: */
#include "memdebug.h"
* defined.
*
* CURLRES_ARES - is defined if libcurl is built to use c-ares for
- * asynchronous name resolves. It cannot have ENABLE_IPV6 defined at the same
- * time, as c-ares has no ipv6 support. This can be Windows or *nix.
+ * asynchronous name resolves. This can be Windows or *nix.
*
* CURLRES_THREADED - is defined if libcurl is built to run under (native)
* Windows, and then the name resolve will be done in a new thread, and the
* Curl_global_host_cache_init() initializes and sets up a global DNS cache.
* Global DNS cache is general badness. Do not use. This will be removed in
* a future version. Use the share interface instead!
+ *
+ * Returns a struct curl_hash pointer on success, NULL on failure.
*/
-void Curl_global_host_cache_init(void)
+struct curl_hash *Curl_global_host_cache_init(void)
{
- if (!host_cache_initialized) {
- Curl_hash_init(&hostname_cache, 7, freednsentry);
- host_cache_initialized = 1;
+ int rc = 0;
+ if(!host_cache_initialized) {
+ rc = Curl_hash_init(&hostname_cache, 7, Curl_hash_str,
+ Curl_str_key_compare, freednsentry);
+ if(!rc)
+ host_cache_initialized = 1;
}
-}
-
-/*
- * Return a pointer to the global cache
- */
-struct curl_hash *Curl_global_host_cache_get(void)
-{
- return &hostname_cache;
+ return rc?NULL:&hostname_cache;
}
/*
*/
void Curl_global_host_cache_dtor(void)
{
- if (host_cache_initialized) {
+ if(host_cache_initialized) {
Curl_hash_clean(&hostname_cache);
host_cache_initialized = 0;
}
* the DNS caching.
*/
static char *
-create_hostcache_id(char *server, int port)
+create_hostcache_id(const char *server, int port)
{
/* create and return the new allocated entry */
return aprintf("%s:%d", server, port);
}
struct hostcache_prune_data {
- int cache_timeout;
+ long cache_timeout;
time_t now;
};
(struct hostcache_prune_data *) datap;
struct Curl_dns_entry *c = (struct Curl_dns_entry *) hc;
- if ((data->now - c->timestamp < data->cache_timeout) ||
+ if((data->now - c->timestamp < data->cache_timeout) ||
c->inuse) {
/* please don't remove */
return 0;
* Prune the DNS cache. This assumes that a lock has already been taken.
*/
static void
-hostcache_prune(struct curl_hash *hostcache, int cache_timeout, time_t now)
+hostcache_prune(struct curl_hash *hostcache, long cache_timeout, time_t now)
{
struct hostcache_prune_data user;
{
time_t now;
- if((data->set.dns_cache_timeout == -1) || !data->hostcache)
+ if((data->set.dns_cache_timeout == -1) || !data->dns.hostcache)
/* cache forever means never prune, and NULL hostcache means
we can't do it */
return;
time(&now);
/* Remove outdated and unused entries from the hostcache */
- hostcache_prune(data->hostcache,
+ hostcache_prune(data->dns.hostcache,
data->set.dns_cache_timeout,
now);
Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
}
+/*
+ * Check if the entry should be pruned. Assumes a locked cache.
+ */
static int
remove_entry_if_stale(struct SessionHandle *data, struct Curl_dns_entry *dns)
{
struct hostcache_prune_data user;
- if( !dns || (data->set.dns_cache_timeout == -1) || !data->hostcache)
+ if( !dns || (data->set.dns_cache_timeout == -1) || !data->dns.hostcache)
/* cache forever means never prune, and NULL hostcache means
we can't do it */
return 0;
time(&user.now);
user.cache_timeout = data->set.dns_cache_timeout;
- if ( !hostcache_timestamp_remove(&user,dns) )
+ if( !hostcache_timestamp_remove(&user,dns) )
return 0;
- /* ok, we do need to clear the cache. although we need to remove just a
- single entry we clean the entire hash, as no explicit delete function
- is provided */
- if(data->share)
- Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
-
- Curl_hash_clean_with_criterium(data->hostcache,
+ Curl_hash_clean_with_criterium(data->dns.hostcache,
(void *) &user,
hostcache_timestamp_remove);
- if(data->share)
- Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
-
return 1;
}
struct Curl_dns_entry *
Curl_cache_addr(struct SessionHandle *data,
Curl_addrinfo *addr,
- char *hostname,
+ const char *hostname,
int port)
{
char *entry_id;
/* Create an entry id, based upon the hostname and port */
entry_id = create_hostcache_id(hostname, port);
/* If we can't create the entry id, fail */
- if (!entry_id)
+ if(!entry_id)
return NULL;
entry_len = strlen(entry_id);
/* Create a new cache entry */
- dns = (struct Curl_dns_entry *) malloc(sizeof(struct Curl_dns_entry));
- if (!dns) {
+ dns = calloc(sizeof(struct Curl_dns_entry), 1);
+ if(!dns) {
free(entry_id);
return NULL;
}
/* Store the resolved data in our DNS cache. This function may return a
pointer to an existing struct already present in the hash, and it may
return the same argument we pass in. Make no assumptions. */
- dns2 = Curl_hash_add(data->hostcache, entry_id, entry_len+1, (void *)dns);
+ dns2 = Curl_hash_add(data->dns.hostcache, entry_id, entry_len+1,
+ (void *)dns);
if(!dns2) {
/* Major badness, run away. */
free(dns);
*/
int Curl_resolv(struct connectdata *conn,
- char *hostname,
+ const char *hostname,
int port,
struct Curl_dns_entry **entry)
{
char *entry_id = NULL;
struct Curl_dns_entry *dns = NULL;
size_t entry_len;
- int wait;
struct SessionHandle *data = conn->data;
CURLcode result;
- int rc;
+ int rc = CURLRESOLV_ERROR; /* default to failure */
*entry = NULL;
#ifdef HAVE_SIGSETJMP
/* this allows us to time-out from the name resolver, as the timeout
will generate a signal and we will siglongjmp() from that here */
- if(!data->set.no_signal && sigsetjmp(curl_jmpenv, 1)) {
- /* this is coming from a siglongjmp() */
- failf(data, "name lookup timed out");
- return CURLRESOLV_ERROR;
+ if(!data->set.no_signal) {
+ if(sigsetjmp(curl_jmpenv, 1)) {
+ /* this is coming from a siglongjmp() */
+ failf(data, "name lookup timed out");
+ return rc;
+ }
}
#endif
/* Create an entry id, based upon the hostname and port */
entry_id = create_hostcache_id(hostname, port);
/* If we can't create the entry id, fail */
- if (!entry_id)
- return CURLRESOLV_ERROR;
+ if(!entry_id)
+ return rc;
entry_len = strlen(entry_id);
Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
/* See if its already in our dns cache */
- dns = Curl_hash_pick(data->hostcache, entry_id, entry_len+1);
+ dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len+1);
+
+ /* See whether the returned entry is stale. Done before we release lock */
+ if( remove_entry_if_stale(data, dns) )
+ dns = NULL; /* the memory deallocation is being handled by the hash */
+
+ if(dns) {
+ dns->inuse++; /* we use it! */
+ rc = CURLRESOLV_RESOLVED;
+ }
if(data->share)
Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
/* free the allocated entry_id again */
free(entry_id);
- /* See whether the returned entry is stale. Deliberately done after the
- locked block */
- if ( remove_entry_if_stale(data,dns) )
- dns = NULL; /* the memory deallocation is being handled by the hash */
-
- rc = CURLRESOLV_ERROR; /* default to failure */
-
- if (!dns) {
+ if(!dns) {
/* The entry was not in the cache. Resolve it to IP address */
Curl_addrinfo *addr;
+ int respwait;
/* Check what IP specifics the app has requested and if we can provide it.
* If not, bail out. */
if(!Curl_ipvalid(data))
return CURLRESOLV_ERROR;
- /* If Curl_getaddrinfo() returns NULL, 'wait' might be set to a non-zero
- value indicating that we need to wait for the response to the resolve
- call */
- addr = Curl_getaddrinfo(conn, hostname, port, &wait);
+ /* If Curl_getaddrinfo() returns NULL, 'respwait' might be set to a
+ non-zero value indicating that we need to wait for the response to the
+ resolve call */
+ addr = Curl_getaddrinfo(conn, hostname, port, &respwait);
- if (!addr) {
- if(wait) {
+ if(!addr) {
+ if(respwait) {
/* the response to our resolve call will come asynchronously at
a later time, good or bad */
/* First, check that we haven't received the info by now */
rc = CURLRESOLV_RESOLVED;
}
}
- else {
- if(data->share)
- Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
- dns->inuse++; /* we use it! */
- if(data->share)
- Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
- rc = CURLRESOLV_RESOLVED;
- }
*entry = dns;
*/
void Curl_resolv_unlock(struct SessionHandle *data, struct Curl_dns_entry *dns)
{
- curlassert(dns && (dns->inuse>0));
+ DEBUGASSERT(dns && (dns->inuse>0));
if(data->share)
Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
*/
struct curl_hash *Curl_mk_dnscache(void)
{
- return Curl_hash_alloc(7, freednsentry);
+ return Curl_hash_alloc(7, Curl_hash_str, Curl_str_key_compare, freednsentry);
}
#ifdef CURLRES_ADDRINFO_COPY
* returns a pointer to the malloc()ed copy. You need to call free() on the
* returned buffer when you're done with it.
*/
-Curl_addrinfo *Curl_addrinfo_copy(void *org, int port)
+Curl_addrinfo *Curl_addrinfo_copy(const void *org, int port)
{
- struct hostent *orig = org;
+ const struct hostent *orig = org;
return Curl_he2ai(orig, port);
}
#endif /* CURLRES_ADDRINFO_COPY */
+
+/***********************************************************************
+ * Only for plain-ipv4 and c-ares builds (NOTE: c-ares builds can be IPv6
+ * enabled)
+ **********************************************************************/
+
+#if defined(CURLRES_IPV4) || defined(CURLRES_ARES)
+/*
+ * This is a function for freeing name information in a protocol independent
+ * way.
+ */
+void Curl_freeaddrinfo(Curl_addrinfo *ai)
+{
+ Curl_addrinfo *next;
+
+ /* walk over the list and free all entries */
+ while(ai) {
+ next = ai->ai_next;
+ if(ai->ai_canonname)
+ free(ai->ai_canonname);
+ free(ai);
+ ai = next;
+ }
+}
+
+struct namebuf {
+ struct hostent hostentry;
+ char *h_addr_list[2];
+ struct in_addr addrentry;
+ char h_name[16]; /* 123.123.123.123 = 15 letters is maximum */
+};
+
+/*
+ * Curl_ip2addr() takes a 32bit ipv4 internet address as input parameter
+ * together with a pointer to the string version of the address, and it
+ * returns a Curl_addrinfo chain filled in correctly with information for this
+ * address/host.
+ *
+ * The input parameters ARE NOT checked for validity but they are expected
+ * to have been checked already when this is called.
+ */
+Curl_addrinfo *Curl_ip2addr(in_addr_t num, const char *hostname, int port)
+{
+ Curl_addrinfo *ai;
+
+#if defined(VMS) && \
+ defined(__INITIAL_POINTER_SIZE) && (__INITIAL_POINTER_SIZE == 64)
+#pragma pointer_size save
+#pragma pointer_size short
+#pragma message disable PTRMISMATCH
+#endif
+
+ struct hostent *h;
+ struct in_addr *addrentry;
+ struct namebuf buffer;
+ struct namebuf *buf = &buffer;
+
+ h = &buf->hostentry;
+ h->h_addr_list = &buf->h_addr_list[0];
+ addrentry = &buf->addrentry;
+#ifdef _CRAYC
+ /* On UNICOS, s_addr is a bit field and for some reason assigning to it
+ * doesn't work. There must be a better fix than this ugly hack.
+ */
+ memcpy(addrentry, &num, SIZEOF_in_addr);
+#else
+ addrentry->s_addr = num;
+#endif
+ h->h_addr_list[0] = (char*)addrentry;
+ h->h_addr_list[1] = NULL;
+ h->h_addrtype = AF_INET;
+ h->h_length = sizeof(*addrentry);
+ h->h_name = &buf->h_name[0];
+ h->h_aliases = NULL;
+
+ /* Now store the dotted version of the address */
+ snprintf(h->h_name, 16, "%s", hostname);
+
+#if defined(VMS) && \
+ defined(__INITIAL_POINTER_SIZE) && (__INITIAL_POINTER_SIZE == 64)
+#pragma pointer_size restore
+#pragma message enable PTRMISMATCH
+#endif
+
+ ai = Curl_he2ai(h, port);
+
+ return ai;
+}
+
+/*
+ * Curl_he2ai() translates from a hostent struct to a Curl_addrinfo struct.
+ * The Curl_addrinfo is meant to work like the addrinfo struct does for IPv6
+ * stacks, but for all hosts and environments.
+ *
+ * Curl_addrinfo defined in "lib/hostip.h"
+ *
+ * struct Curl_addrinfo {
+ * int ai_flags;
+ * int ai_family;
+ * int ai_socktype;
+ * int ai_protocol;
+ * socklen_t ai_addrlen; * Follow rfc3493 struct addrinfo *
+ * char *ai_canonname;
+ * struct sockaddr *ai_addr;
+ * struct Curl_addrinfo *ai_next;
+ * };
+ *
+ * hostent defined in <netdb.h>
+ *
+ * struct hostent {
+ * char *h_name;
+ * char **h_aliases;
+ * int h_addrtype;
+ * int h_length;
+ * char **h_addr_list;
+ * };
+ *
+ * for backward compatibility:
+ *
+ * #define h_addr h_addr_list[0]
+ */
+
+Curl_addrinfo *Curl_he2ai(const struct hostent *he, int port)
+{
+ Curl_addrinfo *ai;
+ Curl_addrinfo *prevai = NULL;
+ Curl_addrinfo *firstai = NULL;
+ struct sockaddr_in *addr;
+#ifdef CURLRES_IPV6
+ struct sockaddr_in6 *addr6;
+#endif /* CURLRES_IPV6 */
+ int i;
+ struct in_addr *curr;
+
+ if(!he)
+ /* no input == no output! */
+ return NULL;
+
+ for(i=0; (curr = (struct in_addr *)he->h_addr_list[i]) != NULL; i++) {
+
+ int ss_size;
+#ifdef CURLRES_IPV6
+ if (he->h_addrtype == AF_INET6)
+ ss_size = sizeof (struct sockaddr_in6);
+ else
+#endif /* CURLRES_IPV6 */
+ ss_size = sizeof (struct sockaddr_in);
+
+ ai = calloc(1, sizeof(Curl_addrinfo) + ss_size);
+
+ if(!ai)
+ break;
+
+ if(!firstai)
+ /* store the pointer we want to return from this function */
+ firstai = ai;
+
+ if(prevai)
+ /* make the previous entry point to this */
+ prevai->ai_next = ai;
+
+ ai->ai_family = he->h_addrtype;
+
+ /* we return all names as STREAM, so when using this address for TFTP
+ the type must be ignored and conn->socktype be used instead! */
+ ai->ai_socktype = SOCK_STREAM;
+
+ ai->ai_addrlen = ss_size;
+ /* make the ai_addr point to the address immediately following this struct
+ and use that area to store the address */
+ ai->ai_addr = (struct sockaddr *) ((char*)ai + sizeof(Curl_addrinfo));
+
+ /* need to free this eventually */
+ ai->ai_canonname = strdup(he->h_name);
+
+ /* leave the rest of the struct filled with zero */
+
+ switch (ai->ai_family) {
+ case AF_INET:
+ addr = (struct sockaddr_in *)ai->ai_addr; /* storage area for this info */
+
+ memcpy((char *)&(addr->sin_addr), curr, sizeof(struct in_addr));
+ addr->sin_family = (unsigned short)(he->h_addrtype);
+ addr->sin_port = htons((unsigned short)port);
+ break;
+
+#ifdef CURLRES_IPV6
+ case AF_INET6:
+ addr6 = (struct sockaddr_in6 *)ai->ai_addr; /* storage area for this info */
+
+ memcpy((char *)&(addr6->sin6_addr), curr, sizeof(struct in6_addr));
+ addr6->sin6_family = (unsigned short)(he->h_addrtype);
+ addr6->sin6_port = htons((unsigned short)port);
+ break;
+#endif /* CURLRES_IPV6 */
+ }
+
+ prevai = ai;
+ }
+ return firstai;
+}
+
+#endif /* CURLRES_IPV4 || CURLRES_ARES */