summaryrefslogtreecommitdiffstats
path: root/Source/CTest/Curl/hostip.c
diff options
context:
space:
mode:
Diffstat (limited to 'Source/CTest/Curl/hostip.c')
-rw-r--r--Source/CTest/Curl/hostip.c396
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);