diff options
Diffstat (limited to 'Utilities/cmcurl/hostip.c')
-rw-r--r-- | Utilities/cmcurl/hostip.c | 208 |
1 files changed, 152 insertions, 56 deletions
diff --git a/Utilities/cmcurl/hostip.c b/Utilities/cmcurl/hostip.c index 43ade26..fd555ef 100644 --- a/Utilities/cmcurl/hostip.c +++ b/Utilities/cmcurl/hostip.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2006, 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 @@ -24,13 +24,10 @@ #include "setup.h" #include <string.h> -#include <errno.h> -#define _REENTRANT - -#if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__) +#ifdef NEED_MALLOC_H #include <malloc.h> -#else +#endif #ifdef HAVE_SYS_TYPES_H #include <sys/types.h> #endif @@ -57,21 +54,15 @@ #include <inet.h> #include <stdlib.h> #endif -#endif #ifdef HAVE_SETJMP_H #include <setjmp.h> #endif -#ifdef WIN32 +#ifdef HAVE_PROCESS_H #include <process.h> #endif -#if (defined(NETWARE) && defined(__NOVELL_LIBC__)) -#undef in_addr_t -#define in_addr_t unsigned long -#endif - #include "urldata.h" #include "sendf.h" #include "hostip.h" @@ -88,7 +79,7 @@ #include "inet_ntoa_r.h" #endif -#include "curl_memory.h" +#include "memory.h" /* The last #include file should be: */ #include "memdebug.h" @@ -105,8 +96,7 @@ * 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 @@ -131,7 +121,7 @@ */ /* These two symbols are for the global DNS cache */ -static curl_hash hostname_cache; +static struct curl_hash hostname_cache; static int host_cache_initialized; static void freednsentry(void *freethis); @@ -152,7 +142,7 @@ void Curl_global_host_cache_init(void) /* * Return a pointer to the global cache */ -curl_hash *Curl_global_host_cache_get(void) +struct curl_hash *Curl_global_host_cache_get(void) { return &hostname_cache; } @@ -174,20 +164,11 @@ void Curl_global_host_cache_dtor(void) int Curl_num_addresses(const Curl_addrinfo *addr) { int i; - for (i = 0; addr; addr = addr->ai_next, i++); + for (i = 0; addr; addr = addr->ai_next, i++) + ; /* empty loop */ return i; } -#define GET_SIN_ADDR_FROM_CURL_ADDRINFO(ai_addr, si, sin, sinaddr, ip) \ - { \ - union { \ - struct si* vsi; \ - struct sin* vsin;\ - } vi; \ - vi.vsi = ai_addr; \ - ip = &(vi.vsin->sinaddr); \ - } - /* * Curl_printable_address() returns a printable version of the 1st address * given in the 'ip' argument. The result will be stored in the buf that is @@ -198,17 +179,13 @@ int Curl_num_addresses(const Curl_addrinfo *addr) const char *Curl_printable_address(const Curl_addrinfo *ip, char *buf, size_t bufsize) { + const void *ip4 = &((const struct sockaddr_in*)ip->ai_addr)->sin_addr; int af = ip->ai_family; - const void *ip4; #ifdef CURLRES_IPV6 - const void *ip6; - GET_SIN_ADDR_FROM_CURL_ADDRINFO(ip->ai_addr, sockaddr, sockaddr_in6, - sin6_addr, ip6); + const void *ip6 = &((const struct sockaddr_in6*)ip->ai_addr)->sin6_addr; #else const void *ip6 = NULL; #endif - GET_SIN_ADDR_FROM_CURL_ADDRINFO(ip->ai_addr, sockaddr, sockaddr_in, - sin_addr, ip4); return Curl_inet_ntop(af, af == AF_INET ? ip4 : ip6, buf, bufsize); } @@ -218,7 +195,7 @@ const char *Curl_printable_address(const Curl_addrinfo *ip, * 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); @@ -257,7 +234,7 @@ hostcache_timestamp_remove(void *datap, void *hc) * Prune the DNS cache. This assumes that a lock has already been taken. */ static void -hostcache_prune(curl_hash *hostcache, int cache_timeout, time_t now) +hostcache_prune(struct curl_hash *hostcache, int cache_timeout, time_t now) { struct hostcache_prune_data user; @@ -277,8 +254,9 @@ void Curl_hostcache_prune(struct SessionHandle *data) { time_t now; - if(data->set.dns_cache_timeout == -1) - /* cache forever means never prune! */ + 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; if(data->share) @@ -287,7 +265,7 @@ void Curl_hostcache_prune(struct SessionHandle *data) 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); @@ -295,6 +273,39 @@ void Curl_hostcache_prune(struct SessionHandle *data) Curl_share_unlock(data, CURL_LOCK_DATA_DNS); } +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->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) ) + 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->dns.hostcache, + (void *) &user, + hostcache_timestamp_remove); + + if(data->share) + Curl_share_unlock(data, CURL_LOCK_DATA_DNS); + + return 1; +} + + #ifdef HAVE_SIGSETJMP /* Beware this is a global and unique instance. This is used to store the return address that we can jump back to from inside a signal handler. This @@ -315,7 +326,7 @@ sigjmp_buf curl_jmpenv; struct Curl_dns_entry * Curl_cache_addr(struct SessionHandle *data, Curl_addrinfo *addr, - char *hostname, + const char *hostname, int port) { char *entry_id; @@ -332,7 +343,7 @@ Curl_cache_addr(struct SessionHandle *data, entry_len = strlen(entry_id); /* Create a new cache entry */ - dns = (struct Curl_dns_entry *) malloc(sizeof(struct Curl_dns_entry)); + dns = (struct Curl_dns_entry *) calloc(sizeof(struct Curl_dns_entry), 1); if (!dns) { free(entry_id); return NULL; @@ -344,7 +355,8 @@ Curl_cache_addr(struct SessionHandle *data, /* 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); @@ -381,31 +393,30 @@ Curl_cache_addr(struct SessionHandle *data, */ int Curl_resolv(struct connectdata *conn, - char *hostname, + const char *hostname, int port, struct Curl_dns_entry **entry) { - char *entry_id; + char *entry_id = NULL; struct Curl_dns_entry *dns = NULL; size_t entry_len; int wait; struct SessionHandle *data = conn->data; CURLcode result; - - /* default to failure */ int rc; *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 CURLRESOLV_ERROR; + } } #endif - rc = CURLRESOLV_ERROR; /* Create an entry id, based upon the hostname and port */ entry_id = create_hostcache_id(hostname, port); @@ -419,7 +430,7 @@ int Curl_resolv(struct connectdata *conn, 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); if(data->share) Curl_share_unlock(data, CURL_LOCK_DATA_DNS); @@ -427,6 +438,13 @@ int Curl_resolv(struct connectdata *conn, /* 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) { /* The entry was not in the cache. Resolve it to IP address */ @@ -474,7 +492,11 @@ int Curl_resolv(struct connectdata *conn, } } 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; } @@ -516,7 +538,7 @@ static void freednsentry(void *freethis) /* * Curl_mk_dnscache() creates a new DNS cache and returns the handle for it. */ -curl_hash *Curl_mk_dnscache(void) +struct curl_hash *Curl_mk_dnscache(void) { return Curl_hash_alloc(7, freednsentry); } @@ -531,10 +553,84 @@ curl_hash *Curl_mk_dnscache(void) * 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 + **********************************************************************/ + +#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; + 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; + 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((char *)h->h_name, 16, "%s", hostname); + + ai = Curl_he2ai(h, port); + + return ai; +} +#endif + + |