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.c865
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(&copy, &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(&copy->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(&copy->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 *)&num;
- 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 */