diff options
Diffstat (limited to 'Source/CTest/Curl/hostip.c')
-rw-r--r-- | Source/CTest/Curl/hostip.c | 865 |
1 files changed, 330 insertions, 535 deletions
diff --git a/Source/CTest/Curl/hostip.c b/Source/CTest/Curl/hostip.c index b525007..715ec0b 100644 --- a/Source/CTest/Curl/hostip.c +++ b/Source/CTest/Curl/hostip.c @@ -1,16 +1,16 @@ /*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2002, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2004, 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 COPYING file. @@ -29,7 +29,7 @@ #define _REENTRANT #if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__) -#include <winsock.h> +#include <malloc.h> #else #ifdef HAVE_SYS_TYPES_H #include <sys/types.h> @@ -49,6 +49,9 @@ #ifdef HAVE_STDLIB_H #include <stdlib.h> /* required for free() prototypes */ #endif +#ifdef HAVE_UNISTD_H +#include <unistd.h> /* for the close() proto */ +#endif #ifdef VMS #include <in.h> #include <inet.h> @@ -60,10 +63,23 @@ #include <setjmp.h> #endif +#ifdef WIN32 +#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" #include "hash.h" +#include "share.h" +#include "strerror.h" +#include "url.h" +#include "inet_ntop.h" #define _MPRINTF_REPLACE /* use our functions only */ #include <curl/mprintf.h> @@ -72,32 +88,78 @@ #include "inet_ntoa_r.h" #endif +#include "memory.h" /* The last #include file should be: */ -#ifdef MALLOCDEBUG #include "memdebug.h" -#endif +/* + * hostip.c explained + * ================== + * + * The main COMPILE-TIME DEFINES to keep in mind when reading the host*.c + * source file are these: + * + * CURLRES_IPV6 - this host has getaddrinfo() and family, and thus we use + * that. The host may not be able to resolve IPv6, but we don't really have to + * take that into account. Hosts that aren't IPv6-enabled have CURLRES_IPV4 + * 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. + * + * 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 + * supported API will be the same as for ares-builds. + * + * If any of the two previous are defined, CURLRES_ASYNCH is defined too. If + * libcurl is not built to use an asynchronous resolver, CURLRES_SYNCH is + * defined. + * + * The host*.c sources files are split up like this: + * + * hostip.c - method-independent resolver functions and utility functions + * hostasyn.c - functions for asynchronous name resolves + * hostsyn.c - functions for synchronous name resolves + * hostares.c - functions for ares-using name resolves + * hostthre.c - functions for threaded name resolves + * hostip4.c - ipv4-specific functions + * hostip6.c - ipv6-specific functions + * + * The hostip.h is the united header file for all this. It defines the + * CURLRES_* defines based on the config*.h and setup.h defines. + */ + +/* These two symbols are for the global DNS cache */ static curl_hash hostname_cache; static int host_cache_initialized; -static Curl_addrinfo *my_getaddrinfo(struct SessionHandle *data, - char *hostname, - int port, - char **bufp); +static void freednsentry(void *freethis); +/* + * 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! + */ void Curl_global_host_cache_init(void) { if (!host_cache_initialized) { - Curl_hash_init(&hostname_cache, 7, Curl_freednsinfo); + Curl_hash_init(&hostname_cache, 7, freednsentry); host_cache_initialized = 1; } } +/* + * Return a pointer to the global cache + */ curl_hash *Curl_global_host_cache_get(void) { return &hostname_cache; } +/* + * Destroy and cleanup the global DNS cache + */ void Curl_global_host_cache_dtor(void) { if (host_cache_initialized) { @@ -106,625 +168,358 @@ void Curl_global_host_cache_dtor(void) } } -/* count the number of characters that an integer takes up */ -static int _num_chars(int i) +/* + * Return # of adresses in a Curl_addrinfo struct + */ +int Curl_num_addresses(const Curl_addrinfo *addr) { - int chars = 0; - - /* While the number divided by 10 is greater than one, - * re-divide the number by 10, and increment the number of - * characters by 1. - * - * this relies on the fact that for every multiple of 10, - * a new digit is added onto every number - */ - do { - chars++; - - i = (int) i / 10; - } while (i >= 1); - - return chars; + int i; + for (i = 0; addr; addr = addr->ai_next, i++); + return i; } -/* Create a hostcache id */ -static char * -create_hostcache_id(char *server, int port, ssize_t *entry_len) +/* + * 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 + * bufsize bytes big. + * + * If the conversion fails, it returns NULL. + */ +const char *Curl_printable_address(const Curl_addrinfo *ip, + char *buf, size_t bufsize) { - char *id = NULL; - - /* Get the length of the new entry id */ - *entry_len = *entry_len + /* Hostname length */ - 1 + /* The ':' seperator */ - _num_chars(port); /* The number of characters the port will take up */ - - /* Allocate the new entry id */ - id = malloc(*entry_len + 1); - if (!id) { - return NULL; - } + const void *ip4 = &((const struct sockaddr_in*)ip->ai_addr)->sin_addr; + int af = ip->ai_family; +#ifdef CURLRES_IPV6 + const void *ip6 = &((const struct sockaddr_in6*)ip->ai_addr)->sin6_addr; +#else + const void *ip6 = NULL; +#endif - /* Create the new entry */ - /* If sprintf() doesn't return the entry length, that signals failure */ - if (sprintf(id, "%s:%d", server, port) != *entry_len) { - /* Free the allocated id, set length to zero and return NULL */ - *entry_len = 0; - free(id); - return NULL; - } + return Curl_inet_ntop(af, af == AF_INET ? ip4 : ip6, buf, bufsize); +} - return id; +/* + * Return a hostcache id string for the providing host + port, to be used by + * the DNS caching. + */ +static char * +create_hostcache_id(char *server, int port) +{ + /* create and return the new allocated entry */ + return aprintf("%s:%d", server, port); } struct hostcache_prune_data { int cache_timeout; - int now; + time_t now; }; +/* + * This function is set as a callback to be called for every entry in the DNS + * cache when we want to prune old unused entries. + * + * Returning non-zero means remove the entry, return 0 to keep it in the + * cache. + */ static int hostcache_timestamp_remove(void *datap, void *hc) { - struct hostcache_prune_data *data = + 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; } +/* + * Prune the DNS cache. This assumes that a lock has already been taken. + */ static void -hostcache_prune(curl_hash *hostcache, int cache_timeout, int now) +hostcache_prune(curl_hash *hostcache, int cache_timeout, time_t now) { struct hostcache_prune_data user; user.cache_timeout = cache_timeout; user.now = now; - - Curl_hash_clean_with_criterium(hostcache, - (void *) &user, + + 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) +/* + * Library-wide function for pruning the DNS cache. This function takes and + * returns the appropriate locks. + */ +void Curl_hostcache_prune(struct SessionHandle *data) { - 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 + time_t now; + + if(data->set.dns_cache_timeout == -1) + /* cache forever means never prune! */ + return; + + if(data->share) + Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE); + + time(&now); -/* Macro to save redundant free'ing of entry_id */ -#define HOSTCACHE_RETURN(dns) \ -{ \ - free(entry_id); \ - return dns; \ + /* Remove outdated and unused entries from the hostcache */ + hostcache_prune(data->hostcache, + data->set.dns_cache_timeout, + now); + + if(data->share) + Curl_share_unlock(data, CURL_LOCK_DATA_DNS); } #ifdef HAVE_SIGSETJMP -/* Beware this is a global and unique instance */ +/* 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 + is not thread-safe stuff. */ sigjmp_buf curl_jmpenv; #endif -struct Curl_dns_entry *Curl_resolv(struct SessionHandle *data, - char *hostname, - int port) + +/* + * Curl_cache_addr() stores a 'Curl_addrinfo' struct in the DNS cache. + * + * When calling Curl_resolv() has resulted in a response with a returned + * address, we call this function to store the information in the dns + * cache etc + * + * Returns the Curl_dns_entry entry pointer or NULL if the storage failed. + */ +struct Curl_dns_entry * +Curl_cache_addr(struct SessionHandle *data, + Curl_addrinfo *addr, + char *hostname, + int port) { - char *entry_id = NULL; - struct Curl_dns_entry *dns = NULL; - ssize_t entry_len; + char *entry_id; + size_t entry_len; + struct Curl_dns_entry *dns; + struct Curl_dns_entry *dns2; time_t now; - 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 = (int)strlen(hostname); - entry_id = create_hostcache_id(hostname, port, &entry_len); + entry_id = create_hostcache_id(hostname, port); /* If we can't create the entry id, fail */ if (!entry_id) return NULL; - - /* See if its already in our dns cache */ - dns = Curl_hash_pick(data->hostcache, entry_id, entry_len+1); + entry_len = strlen(entry_id); + /* Create a new cache entry */ + dns = (struct Curl_dns_entry *) malloc(sizeof(struct Curl_dns_entry)); if (!dns) { - Curl_addrinfo *addr = my_getaddrinfo(data, hostname, port, &bufp); - - if (!addr) { - HOSTCACHE_RETURN(NULL); - } - - /* Create a new cache entry */ - dns = (struct Curl_dns_entry *) malloc(sizeof(struct Curl_dns_entry)); - if (!dns) { - Curl_freeaddrinfo(addr); - HOSTCACHE_RETURN(NULL); - } + free(entry_id); + 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); + dns->inuse = 0; /* init to not used */ + dns->addr = addr; /* this is the address(es) */ + + /* 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); + if(!dns2) { + /* Major badness, run away. */ + free(dns); + free(entry_id); + return NULL; } time(&now); + dns = dns2; - dns->timestamp = now; + dns->timestamp = now; /* used now */ dns->inuse++; /* mark entry as in-use */ -#ifdef MALLOCDEBUG - dns->entry_id = entry_id; -#endif - /* Remove outdated and unused entries from the hostcache */ - hostcache_prune(data->hostcache, - data->set.dns_cache_timeout, - (int)now); + /* free the allocated entry_id again */ + free(entry_id); - HOSTCACHE_RETURN(dns); + return dns; } /* - * This is a wrapper function for freeing name information in a protocol - * independent way. This takes care of using the appropriate underlaying - * function. + * Curl_resolv() is the main name resolve function within libcurl. It resolves + * a name and returns a pointer to the entry in the 'entry' argument (if one + * is provided). This function might return immediately if we're using asynch + * resolves. See the return codes. + * + * The cache entry we return will get its 'inuse' counter increased when this + * function is used. You MUST call Curl_resolv_unlock() later (when you're + * done using this struct) to decrease the counter again. + * + * Return codes: + * + * CURLRESOLV_ERROR (-1) = error, no pointer + * CURLRESOLV_RESOLVED (0) = OK, pointer provided + * CURLRESOLV_PENDING (1) = waiting for response, no pointer */ -void Curl_freeaddrinfo(Curl_addrinfo *p) -{ -#ifdef ENABLE_IPV6 - freeaddrinfo(p); -#else - free(p); -#endif -} -/* - * Free a cache dns entry. - */ -void Curl_freednsinfo(void *freethis) +int Curl_resolv(struct connectdata *conn, + char *hostname, + int port, + struct Curl_dns_entry **entry) { - struct Curl_dns_entry *p = (struct Curl_dns_entry *) freethis; - - Curl_freeaddrinfo(p->addr); - - free(p); -} - -/* --- resolve name or IP-number --- */ + char *entry_id = NULL; + struct Curl_dns_entry *dns = NULL; + size_t entry_len; + int wait; + struct SessionHandle *data = conn->data; + CURLcode result; -#ifdef ENABLE_IPV6 + /* default to failure */ + int rc = CURLRESOLV_ERROR; + *entry = NULL; -#ifdef MALLOCDEBUG -/* These two are strictly for memory tracing and are using the same - * style as the family otherwise present in memdebug.c. I put these ones - * here since they require a bunch of struct types I didn't wanna include - * in memdebug.c - */ -int curl_getaddrinfo(char *hostname, char *service, - struct addrinfo *hints, - struct addrinfo **result, - int line, const char *source) -{ - int res=(getaddrinfo)(hostname, service, hints, result); - if(0 == res) { - /* success */ - if(logfile) - fprintf(logfile, "ADDR %s:%d getaddrinfo() = %p\n", - source, line, (void *)*result); - } - else { - if(logfile) - fprintf(logfile, "ADDR %s:%d getaddrinfo() failed\n", - source, line); +#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; } - return res; -} +#endif -void curl_freeaddrinfo(struct addrinfo *freethis, - int line, const char *source) -{ - (freeaddrinfo)(freethis); - if(logfile) - fprintf(logfile, "ADDR %s:%d freeaddrinfo(%p)\n", - source, line, (void *)freethis); -} + /* 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; -#endif + entry_len = strlen(entry_id); -/* - * Return name information about the given hostname and port number. If - * successful, the 'addrinfo' is returned and the forth argument will point to - * memory we need to free after use. That meory *MUST* be freed with - * Curl_freeaddrinfo(), nothing else. - */ -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; - 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:%d\n", hostname, port); - return NULL; - } - *bufp=(char *)res; /* make it point to the result struct */ + if(data->share) + Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE); - return res; -} -#else /* following code is IPv4-only */ + /* See if its already in our dns cache */ + dns = Curl_hash_pick(data->hostcache, entry_id, entry_len+1); -#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) -{ - char *bufptr; - struct hostent *newbuf; - struct hostent* copy; + if(data->share) + Curl_share_unlock(data, CURL_LOCK_DATA_DNS); - int i; - char *str; - size_t len; + /* free the allocated entry_id again */ + free(entry_id); - bufptr = *buf; - memcpy(©, &bufptr, sizeof(struct hostent*)); + if (!dns) { + /* The entry was not in the cache. Resolve it to IP address */ - bufptr += sizeof(struct hostent); - copy->h_name = bufptr; - len = strlen(orig->h_name) + 1; - strncpy(bufptr, orig->h_name, len); - bufptr += len; + Curl_addrinfo *addr; - /* we align on even 64bit boundaries for safety */ -#define MEMALIGN(x) ((x)+(8-(((unsigned long)(x))&0x7))) + /* 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; - /* This must be aligned properly to work on many CPU architectures! */ - bufptr = MEMALIGN(bufptr); - - memcpy(©->h_aliases, &bufptr, sizeof(char**)); + /* 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); - /* Figure out how many aliases there are */ - for (i = 0; orig->h_aliases[i] != NULL; ++i); + if (!addr) { + if(wait) { + /* 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 */ + result = Curl_is_resolved(conn, &dns); + if(result) /* error detected */ + return CURLRESOLV_ERROR; + if(dns) + rc = CURLRESOLV_RESOLVED; /* pointer provided */ + else + rc = CURLRESOLV_PENDING; /* no info yet */ + } + } + else { + if(data->share) + Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE); - /* Reserve room for the array */ - bufptr += (i + 1) * sizeof(char*); + /* we got a response, store it in the cache */ + dns = Curl_cache_addr(data, addr, hostname, port); - /* Clone all known aliases */ - for(i = 0; (str = orig->h_aliases[i]); i++) { - len = strlen(str) + 1; - strncpy(bufptr, str, len); - copy->h_aliases[i] = bufptr; - bufptr += len; + if(data->share) + Curl_share_unlock(data, CURL_LOCK_DATA_DNS); + + if(!dns) + /* returned failure, bail out nicely */ + Curl_freeaddrinfo(addr); + else + rc = CURLRESOLV_RESOLVED; + } } - /* Terminate the alias list with a NULL */ - copy->h_aliases[i] = NULL; - - copy->h_addrtype = orig->h_addrtype; - copy->h_length = orig->h_length; - - /* align it for (at least) 32bit accesses */ - bufptr = MEMALIGN(bufptr); - - memcpy(©->h_addr_list, &bufptr, sizeof(char**)); - - /* Figure out how many addresses there are */ - for (i = 0; orig->h_addr_list[i] != NULL; ++i); - - /* Reserve room for the array */ - bufptr += (i + 1) * sizeof(char*); - - i = 0; - len = orig->h_length; - str = orig->h_addr_list[i]; - while (str != NULL) { - memcpy(bufptr, str, len); - copy->h_addr_list[i] = bufptr; - bufptr += len; - str = orig->h_addr_list[++i]; + else { + dns->inuse++; /* we use it! */ + rc = CURLRESOLV_RESOLVED; } - 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=(struct hostent *)realloc(*buf, (int)(bufptr-*buf)); + *entry = dns; - /* if the alloc moved, we need to adjust things again */ - if((char*)newbuf != *buf) - hostcache_fixoffset((struct hostent*)newbuf, (int)((char*)newbuf-*buf)); - - /* setup the return */ - *buf = (char*)newbuf; - copy = (struct hostent*)newbuf; - - return copy; + return rc; } -#endif -static char *MakeIP(unsigned long num,char *addr, int addr_len) +/* + * Curl_resolv_unlock() unlocks the given cached DNS entry. When this has been + * made, the struct may be destroyed due to pruning. It is important that only + * one unlock is made for each Curl_resolv() call. + */ +void Curl_resolv_unlock(struct SessionHandle *data, struct Curl_dns_entry *dns) { -#if defined(HAVE_INET_NTOA) || defined(HAVE_INET_NTOA_R) - struct in_addr in; - in.s_addr = htonl(num); - -#if defined(HAVE_INET_NTOA_R) - inet_ntoa_r(in,addr,addr_len); -#else - strncpy(addr,inet_ntoa(in),addr_len); -#endif -#else - unsigned char *paddr; + curlassert(dns && (dns->inuse>0)); - num = htonl(num); /* htonl() added to avoid endian probs */ - paddr = (unsigned char *)# - sprintf(addr, "%u.%u.%u.%u", paddr[0], paddr[1], paddr[2], paddr[3]); -#endif - return (addr); -} + if(data->share) + Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE); -#ifndef INADDR_NONE -#define INADDR_NONE (in_addr_t) ~0 -#endif + dns->inuse--; -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++; - } + if(data->share) + Curl_share_unlock(data, CURL_LOCK_DATA_DNS); } -/* 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) +/* + * File-internal: free a cache dns entry. + */ +static void freednsentry(void *freethis) { - struct hostent *h = NULL; - in_addr_t in; - int ret; /* this variable is unused on several platforms but used on some */ - -#define CURL_NAMELOOKUP_SIZE 9000 - /* Allocate enough memory to hold the full name information structs and - * 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! */ - port=0; /* unused in IPv4 code */ - ret = 0; /* to prevent the compiler warning */ - (void)ret; - - if ( (in=inet_addr(hostname)) != INADDR_NONE ) { - struct in_addr *addrentry; - 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 = &buf->h_name[0]; - MakeIP(ntohl(in), (char*)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 */ - (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; - } + struct Curl_dns_entry *p = (struct Curl_dns_entry *) freethis; -#ifdef MALLOCDEBUG - infof(data, "gethostbyname_r() uses %d bytes\n", step_size); -#endif + Curl_freeaddrinfo(p->addr); - 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 */ - 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 returns EAGAIN if the given buffer size is too - small. Previous versions are known to return ERANGE for the same - problem. - - This wouldn't be such a big problem if older versions wouldn't - sometimes return EAGAIN on a common failure case. Alas, we can't - assume that EAGAIN *or* ERANGE means ERANGE for any given version of - glibc. - - For now, we do that and thus we may call the function repeatedly and - fail for older glibc versions that return EAGAIN, until we run out - of buffer size (step_size grows beyond CURL_NAMELOOKUP_SIZE). - - If anyone has a better fix, please tell us! - */ - - if((ERANGE == res) || (EAGAIN == res)) { - step_size+=200; - continue; - } - break; - } while(step_size <= CURL_NAMELOOKUP_SIZE); - - 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? */ - - if(CURL_NAMELOOKUP_SIZE >= - (sizeof(struct hostent)+sizeof(struct hostent_data))) - - /* August 22nd, 2000: Albert Chin-A-Young brought an updated version - * that should work! September 20: Richard Prescott worked on the buffer - * size dilemma. */ - - ret = gethostbyname_r(hostname, - (struct hostent *)buf, - (struct hostent_data *)((char *)buf + sizeof(struct hostent))); - else - ret = -1; /* failure, too smallish buffer size */ - - /* result expected in h */ - h = (struct hostent*)buf; - h_errnop= errno; /* we don't deal with this, but set it anyway */ - if(ret) -#endif - { - infof(data, "gethostbyname_r(2) failed for %s\n", hostname); - h = NULL; /* set return code to NULL */ - free(buf); - *bufp=NULL; - } -#else - else { - if ((h = gethostbyname(hostname)) == NULL ) { - infof(data, "gethostbyname(2) failed for %s\n", hostname); - *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(&buf, h); - *bufp=(char *)buf; - } -#endif - } - return (h); + free(p); +} + +/* + * Curl_mk_dnscache() creates a new DNS cache and returns the handle for it. + */ +curl_hash *Curl_mk_dnscache(void) +{ + return Curl_hash_alloc(7, freednsentry); } -#endif /* end of IPv4-specific code */ +#ifdef CURLRES_ADDRINFO_COPY + +/* align on even 64bit boundaries */ +#define MEMALIGN(x) ((x)+(8-(((unsigned long)(x))&0x7))) /* - * local variables: - * eval: (load-file "../curl-mode.el") - * end: - * vim600: fdm=marker - * vim: et sw=2 ts=2 sts=2 tw=78 + * Curl_addrinfo_copy() performs a "deep" copy of a hostent into a buffer and + * 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) +{ + struct hostent *orig = org; + + return Curl_he2ai(orig, port); +} +#endif /* CURLRES_ADDRINFO_COPY */ |