diff options
Diffstat (limited to 'Source/CTest/Curl/hostip.c')
-rw-r--r-- | Source/CTest/Curl/hostip.c | 396 |
1 files changed, 288 insertions, 108 deletions
diff --git a/Source/CTest/Curl/hostip.c b/Source/CTest/Curl/hostip.c index 489dfab..07fb1d2 100644 --- a/Source/CTest/Curl/hostip.c +++ b/Source/CTest/Curl/hostip.c @@ -1,25 +1,25 @@ -/***************************************************************************** +/*************************************************************************** * _ _ ____ _ * Project ___| | | | _ \| | * / __| | | | |_) | | * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2001, Daniel Stenberg, <daniel@haxx.se>, et al. - * - * In order to be useful for every potential user, curl and libcurl are - * dual-licensed under the MPL and the MIT/X-derivate licenses. + * Copyright (C) 1998 - 2002, 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 + * are also available at http://curl.haxx.se/docs/copyright.html. + * * You may opt to use, copy, modify, merge, publish, distribute and/or sell * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the MPL or the MIT/X-derivate - * licenses. You may pick one of these licenses. + * furnished to do so, under the terms of the COPYING file. * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY * KIND, either express or implied. * * $Id$ - *****************************************************************************/ + ***************************************************************************/ #include "setup.h" @@ -56,6 +56,10 @@ #endif #endif +#ifdef HAVE_SETJMP_H +#include <setjmp.h> +#endif + #include "urldata.h" #include "sendf.h" #include "hostip.h" @@ -76,10 +80,15 @@ static curl_hash hostname_cache; static int host_cache_initialized; +static Curl_addrinfo *my_getaddrinfo(struct SessionHandle *data, + char *hostname, + int port, + char **bufp); + void Curl_global_host_cache_init(void) { if (!host_cache_initialized) { - curl_hash_init(&hostname_cache, 7, Curl_freeaddrinfo); + Curl_hash_init(&hostname_cache, 7, Curl_freeaddrinfo); host_cache_initialized = 1; } } @@ -92,16 +101,11 @@ curl_hash *Curl_global_host_cache_get(void) void Curl_global_host_cache_dtor(void) { if (host_cache_initialized) { - curl_hash_clean(&hostname_cache); + Curl_hash_clean(&hostname_cache); host_cache_initialized = 0; } } -struct curl_dns_cache_entry { - Curl_addrinfo *addr; - time_t timestamp; -}; - /* count the number of characters that an integer takes up */ static int _num_chars(int i) { @@ -125,7 +129,7 @@ static int _num_chars(int i) /* Create a hostcache id */ static char * -_create_hostcache_id(char *server, int port, ssize_t *entry_len) +create_hostcache_id(char *server, int port, ssize_t *entry_len) { char *id = NULL; @@ -152,70 +156,132 @@ _create_hostcache_id(char *server, int port, ssize_t *entry_len) return id; } +struct hostcache_prune_data { + int cache_timeout; + int now; +}; + +static int +hostcache_timestamp_remove(void *datap, void *hc) +{ + struct hostcache_prune_data *data = + (struct hostcache_prune_data *) datap; + struct Curl_dns_entry *c = (struct Curl_dns_entry *) hc; + + if ((data->now - c->timestamp < data->cache_timeout) || + c->inuse) { + /* please don't remove */ + return 0; + } + + /* fine, remove */ + return 1; +} + +static void +hostcache_prune(curl_hash *hostcache, int cache_timeout, int now) +{ + struct hostcache_prune_data user; + + user.cache_timeout = cache_timeout; + user.now = now; + + Curl_hash_clean_with_criterium(hostcache, + (void *) &user, + hostcache_timestamp_remove); +} + +#if defined(MALLOCDEBUG) && defined(AGGRESIVE_TEST) +/* Called from Curl_done() to check that there's no DNS cache entry with + a non-zero counter left. */ +void Curl_scan_cache_used(void *user, void *ptr) +{ + struct Curl_dns_entry *e = ptr; + (void)user; /* prevent compiler warning */ + if(e->inuse) { + fprintf(stderr, "*** WARNING: locked DNS cache entry detected: %s\n", + e->entry_id); + /* perform a segmentation fault to draw attention */ + *(void **)0 = 0; + } +} +#endif + /* Macro to save redundant free'ing of entry_id */ -#define _hostcache_return(__v) \ +#define HOSTCACHE_RETURN(dns) \ { \ free(entry_id); \ - return (__v); \ + return dns; \ } -Curl_addrinfo *Curl_resolv(struct SessionHandle *data, - char *hostname, - int port, - char **bufp) +#ifdef HAVE_SIGSETJMP +/* Beware this is a global and unique instance */ +sigjmp_buf curl_jmpenv; +#endif + +struct Curl_dns_entry *Curl_resolv(struct SessionHandle *data, + char *hostname, + int port) { char *entry_id = NULL; - struct curl_dns_cache_entry *p = NULL; + struct Curl_dns_entry *dns = NULL; ssize_t entry_len; time_t now; - - /* If the host cache timeout is 0, we don't do DNS cach'ing - so fall through */ - if (data->set.dns_cache_timeout == 0) { - return Curl_getaddrinfo(data, hostname, port, bufp); + char *bufp; + +#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 time-outed"); + return NULL; } +#endif /* Create an entry id, based upon the hostname and port */ entry_len = strlen(hostname); - entry_id = _create_hostcache_id(hostname, port, &entry_len); - /* If we can't create the entry id, don't cache, just fall-through - to the plain Curl_getaddrinfo() */ - if (!entry_id) { - return Curl_getaddrinfo(data, hostname, port, bufp); - } + entry_id = create_hostcache_id(hostname, port, &entry_len); + /* If we can't create the entry id, fail */ + if (!entry_id) + return NULL; - time(&now); /* See if its already in our dns cache */ - if (entry_id && curl_hash_find(data->hostcache, entry_id, entry_len+1, (void **) &p)) { - /* Do we need to check for a cache timeout? */ - if (data->set.dns_cache_timeout != -1) { - /* Return if the entry has not timed out */ - if ((now - p->timestamp) < data->set.dns_cache_timeout) { - _hostcache_return(p->addr); - } + dns = Curl_hash_pick(data->hostcache, entry_id, entry_len+1); + + if (!dns) { + Curl_addrinfo *addr = my_getaddrinfo(data, hostname, port, &bufp); + + if (!addr) { + HOSTCACHE_RETURN(NULL); } - else { - _hostcache_return(p->addr); + + /* Create a new cache entry */ + dns = (struct Curl_dns_entry *) malloc(sizeof(struct Curl_dns_entry)); + if (!dns) { + Curl_freeaddrinfo(addr); + HOSTCACHE_RETURN(NULL); } - } - /* Create a new cache entry */ - p = (struct curl_dns_cache_entry *) malloc(sizeof(struct curl_dns_cache_entry)); - if (!p) { - _hostcache_return(NULL); + dns->inuse = 0; + dns->addr = addr; + /* Save it in our host cache */ + Curl_hash_add(data->hostcache, entry_id, entry_len+1, (const void *) dns); } + time(&now); - p->addr = Curl_getaddrinfo(data, hostname, port, bufp); - if (!p->addr) { - free(p); - _hostcache_return(NULL); - } - p->timestamp = now; + dns->timestamp = now; + dns->inuse++; /* mark entry as in-use */ +#ifdef MALLOCDEBUG + dns->entry_id = entry_id; +#endif - /* Save it in our host cache */ - curl_hash_update(data->hostcache, entry_id, entry_len+1, (const void *) p); + /* Remove outdated and unused entries from the hostcache */ + hostcache_prune(data->hostcache, + data->set.dns_cache_timeout, + now); - _hostcache_return(p->addr); + HOSTCACHE_RETURN(dns); } /* @@ -225,7 +291,7 @@ Curl_addrinfo *Curl_resolv(struct SessionHandle *data, */ void Curl_freeaddrinfo(void *freethis) { - struct curl_dns_cache_entry *p = (struct curl_dns_cache_entry *) freethis; + struct Curl_dns_entry *p = (struct Curl_dns_entry *) freethis; #ifdef ENABLE_IPV6 freeaddrinfo(p->addr); @@ -283,23 +349,37 @@ void curl_freeaddrinfo(struct addrinfo *freethis, * memory we need to free after use. That meory *MUST* be freed with * Curl_freeaddrinfo(), nothing else. */ -Curl_addrinfo *Curl_getaddrinfo(struct SessionHandle *data, - char *hostname, - int port, - char **bufp) +static Curl_addrinfo *my_getaddrinfo(struct SessionHandle *data, + char *hostname, + int port, + char **bufp) { struct addrinfo hints, *res; int error; char sbuf[NI_MAXSERV]; - + int s, pf = PF_UNSPEC; + + /* see if we have an IPv6 stack */ + s = socket(PF_INET6, SOCK_DGRAM, 0); + if (s < 0) + /* Some non-IPv6 stacks have been found to make very slow name resolves + * when PF_UNSPEC is used, so thus we switch to a mere PF_INET lookup if + * the stack seems to be a non-ipv6 one. */ + pf = PF_INET; + else + /* This seems to be an IPv6-capable stack, use PF_UNSPEC for the widest + * possible checks. And close the socket again. + */ + sclose(s); + memset(&hints, 0, sizeof(hints)); - hints.ai_family = PF_INET; + hints.ai_family = pf; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_CANONNAME; snprintf(sbuf, sizeof(sbuf), "%d", port); error = getaddrinfo(hostname, sbuf, &hints, &res); if (error) { - infof(data, "getaddrinfo(3) failed for %s\n", hostname); + infof(data, "getaddrinfo(3) failed for %s:%d\n", hostname, port); return NULL; } *bufp=(char *)res; /* make it point to the result struct */ @@ -309,22 +389,24 @@ Curl_addrinfo *Curl_getaddrinfo(struct SessionHandle *data, #else /* following code is IPv4-only */ #ifndef HAVE_GETHOSTBYNAME_R +static void hostcache_fixoffset(struct hostent *h, int offset); /** * Performs a "deep" copy of a hostent into a buffer (returns a pointer to the * copy). Make absolutely sure the destination buffer is big enough! * * Keith McGuigan * 10/3/2001 */ -static struct hostent* pack_hostent(char* buf, struct hostent* orig) +static struct hostent* pack_hostent(char** buf, struct hostent* orig) { - char* bufptr; + char *bufptr; + char *newbuf; struct hostent* copy; int i; - char* str; + char *str; int len; - bufptr = buf; + bufptr = *buf; copy = (struct hostent*)bufptr; bufptr += sizeof(struct hostent); @@ -334,10 +416,12 @@ static struct hostent* pack_hostent(char* buf, struct hostent* orig) bufptr += len; /* we align on even 64bit boundaries for safety */ -#define MEMALIGN(x) (((unsigned long)(x)&0xfffffff8)+8) +#define MEMALIGN(x) ((x)+(8-(((unsigned long)(x))&0x7))) /* This must be aligned properly to work on many CPU architectures! */ - copy->h_aliases = (char**)MEMALIGN(bufptr); + bufptr = MEMALIGN(bufptr); + + copy->h_aliases = (char**)bufptr; /* Figure out how many aliases there are */ for (i = 0; orig->h_aliases[i] != NULL; ++i); @@ -359,7 +443,7 @@ static struct hostent* pack_hostent(char* buf, struct hostent* orig) copy->h_length = orig->h_length; /* align it for (at least) 32bit accesses */ - bufptr = (char *)MEMALIGN(bufptr); + bufptr = MEMALIGN(bufptr); copy->h_addr_list = (char**)bufptr; @@ -380,6 +464,18 @@ static struct hostent* pack_hostent(char* buf, struct hostent* orig) } copy->h_addr_list[i] = NULL; + /* now, shrink the allocated buffer to the size we actually need, which + most often is only a fraction of the original alloc */ + newbuf=(char *)realloc(*buf, (int)bufptr-(int)(*buf)); + + /* if the alloc moved, we need to adjust things again */ + if(newbuf != *buf) + hostcache_fixoffset((struct hostent*)newbuf, (int)newbuf-(int)*buf); + + /* setup the return */ + *buf = newbuf; + copy = (struct hostent*)newbuf; + return copy; } #endif @@ -405,18 +501,34 @@ static char *MakeIP(unsigned long num,char *addr, int addr_len) return (addr); } -/* The original code to this function was once stolen from the Dancer source - code, written by Bjorn Reese, it has since been patched and modified - considerably. */ - #ifndef INADDR_NONE #define INADDR_NONE (in_addr_t) ~0 #endif -Curl_addrinfo *Curl_getaddrinfo(struct SessionHandle *data, - char *hostname, - int port, - char **bufp) +static void hostcache_fixoffset(struct hostent *h, int offset) +{ + int i=0; + h->h_name=(char *)((long)h->h_name+offset); + h->h_aliases=(char **)((long)h->h_aliases+offset); + while(h->h_aliases[i]) { + h->h_aliases[i]=(char *)((long)h->h_aliases[i]+offset); + i++; + } + h->h_addr_list=(char **)((long)h->h_addr_list+offset); + i=0; + while(h->h_addr_list[i]) { + h->h_addr_list[i]=(char *)((long)h->h_addr_list[i]+offset); + i++; + } +} + +/* The original code to this function was once stolen from the Dancer source + code, written by Bjorn Reese, it has since been patched and modified + considerably. */ +static Curl_addrinfo *my_getaddrinfo(struct SessionHandle *data, + char *hostname, + int port, + char **bufp) { struct hostent *h = NULL; in_addr_t in; @@ -427,51 +539,116 @@ Curl_addrinfo *Curl_getaddrinfo(struct SessionHandle *data, * everything. OSF1 is known to require at least 8872 bytes. The buffer * required for storing all possible aliases and IP numbers is according to * Stevens' Unix Network Programming 2nd editor, p. 304: 8192 bytes! */ - int *buf = (int *)malloc(CURL_NAMELOOKUP_SIZE); - if(!buf) - return NULL; /* major failure */ - *bufp = (char *)buf; - port=0; /* unused in IPv4 code */ ret = 0; /* to prevent the compiler warning */ if ( (in=inet_addr(hostname)) != INADDR_NONE ) { struct in_addr *addrentry; - - h = (struct hostent*)buf; - h->h_addr_list = (char**)(buf + sizeof(*h)); - addrentry = (struct in_addr*)(h->h_addr_list + 2); + struct namebuf { + struct hostent hostentry; + char *h_addr_list[2]; + struct in_addr addrentry; + char h_name[128]; + } *buf = (struct namebuf *)malloc(sizeof(struct namebuf)); + if(!buf) + return NULL; /* major failure */ + *bufp = (char *)buf; + + h = &buf->hostentry; + h->h_addr_list = &buf->h_addr_list[0]; + addrentry = &buf->addrentry; addrentry->s_addr = in; 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 = *(h->h_addr_list) + h->h_length; - /* bad one h->h_name = (char*)(h->h_addr_list + h->h_length); */ - MakeIP(ntohl(in),h->h_name, CURL_NAMELOOKUP_SIZE - (long)(h->h_name) + (long)buf); + h->h_name = &buf->h_name[0]; + MakeIP(ntohl(in), h->h_name, sizeof(buf->h_name)); } #if defined(HAVE_GETHOSTBYNAME_R) else { int h_errnop; + int res=ERANGE; + int step_size=200; + int *buf = (int *)malloc(CURL_NAMELOOKUP_SIZE); + if(!buf) + return NULL; /* major failure */ + *bufp=(char *)buf; + /* Workaround for gethostbyname_r bug in qnx nto. It is also _required_ for some of these functions. */ memset(buf, 0, CURL_NAMELOOKUP_SIZE); #ifdef HAVE_GETHOSTBYNAME_R_5 /* Solaris, IRIX and more */ - if ((h = gethostbyname_r(hostname, - (struct hostent *)buf, - (char *)buf + sizeof(struct hostent), - CURL_NAMELOOKUP_SIZE - sizeof(struct hostent), - &h_errnop)) == NULL ) + (void)res; /* prevent compiler warning */ + while(!h) { + h = gethostbyname_r(hostname, + (struct hostent *)buf, + (char *)buf + sizeof(struct hostent), + step_size - sizeof(struct hostent), + &h_errnop); + + /* If the buffer is too small, it returns NULL and sets errno to + ERANGE. The errno is thread safe if this is compiled with + -D_REENTRANT as then the 'errno' variable is a macro defined to + get used properly for threads. */ + + if(h || (errno != ERANGE)) + break; + + step_size+=200; + } + +#ifdef MALLOCDEBUG + infof(data, "gethostbyname_r() uses %d bytes\n", step_size); +#endif + + if(h) { + int offset; + h=(struct hostent *)realloc(buf, step_size); + offset=(long)h-(long)buf; + hostcache_fixoffset(h, offset); + buf=(int *)h; + *bufp=(char *)buf; + } + else #endif #ifdef HAVE_GETHOSTBYNAME_R_6 /* Linux */ - if( gethostbyname_r(hostname, - (struct hostent *)buf, - (char *)buf + sizeof(struct hostent), - CURL_NAMELOOKUP_SIZE - sizeof(struct hostent), - &h, /* DIFFERENCE */ - &h_errnop)) + do { + res=gethostbyname_r(hostname, + (struct hostent *)buf, + (char *)buf + sizeof(struct hostent), + step_size - sizeof(struct hostent), + &h, /* DIFFERENCE */ + &h_errnop); + /* Redhat 8, using glibc 2.2.93 changed the behavior. Now all of a + sudden this function seems to be setting EAGAIN if the given buffer + size is too small. Previous versions are known to return ERANGE for + the same. */ + + if((ERANGE == res) || (EAGAIN == res)) { + step_size+=200; + continue; + } + break; + } while(1); + + if(!h) /* failure */ + res=1; + +#ifdef MALLOCDEBUG + infof(data, "gethostbyname_r() uses %d bytes\n", step_size); +#endif + if(!res) { + int offset; + h=(struct hostent *)realloc(buf, step_size); + offset=(long)h-(long)buf; + hostcache_fixoffset(h, offset); + buf=(int *)h; + *bufp=(char *)buf; + } + else #endif #ifdef HAVE_GETHOSTBYNAME_R_3 /* AIX, Digital Unix, HPUX 10, more? */ @@ -484,8 +661,8 @@ Curl_addrinfo *Curl_getaddrinfo(struct SessionHandle *data, * size dilemma. */ ret = gethostbyname_r(hostname, - (struct hostent *)buf, - (struct hostent_data *)(buf + sizeof(struct hostent))); + (struct hostent *)buf, + (struct hostent_data *)((char *)buf + sizeof(struct hostent))); else ret = -1; /* failure, too smallish buffer size */ @@ -504,14 +681,17 @@ Curl_addrinfo *Curl_getaddrinfo(struct SessionHandle *data, else { if ((h = gethostbyname(hostname)) == NULL ) { infof(data, "gethostbyname(2) failed for %s\n", hostname); - free(buf); *bufp=NULL; } else + { + char *buf=(char *)malloc(CURL_NAMELOOKUP_SIZE); /* we make a copy of the hostent right now, right here, as the static one we got a pointer to might get removed when we don't want/expect that */ - h = pack_hostent((char *)buf, h); + h = pack_hostent(&buf, h); + *bufp=(char *)buf; + } #endif } return (h); |