summaryrefslogtreecommitdiff
path: root/libs/libcurl/src/hostip.c
diff options
context:
space:
mode:
authorGeorge Hazan <ghazan@miranda.im>2017-12-06 21:44:09 +0300
committerGeorge Hazan <ghazan@miranda.im>2017-12-06 21:44:17 +0300
commit4b6980f68d25901133519bc1ad1c5376819a3876 (patch)
tree0d919622bfc2659f34a7bed303fefb99ecab052a /libs/libcurl/src/hostip.c
parent0112d2767268037cf63e44c4464cf9eed237d06d (diff)
libcurl: update to 7.57
Diffstat (limited to 'libs/libcurl/src/hostip.c')
-rw-r--r--libs/libcurl/src/hostip.c360
1 files changed, 214 insertions, 146 deletions
diff --git a/libs/libcurl/src/hostip.c b/libs/libcurl/src/hostip.c
index f37b4925bb..7f010a0379 100644
--- a/libs/libcurl/src/hostip.c
+++ b/libs/libcurl/src/hostip.c
@@ -5,11 +5,11 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2017, 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.
+ * are also available at https://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
@@ -56,12 +56,9 @@
#include "url.h"
#include "inet_ntop.h"
#include "warnless.h"
-
-#define _MPRINTF_REPLACE /* use our functions only */
-#include <curl/mprintf.h>
-
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
#include "curl_memory.h"
-/* The last #include file should be: */
#include "memdebug.h"
#if defined(CURLRES_SYNCH) && \
@@ -98,8 +95,8 @@
* hostip.c - method-independent resolver functions and utility functions
* hostasyn.c - functions for asynchronous name resolves
* hostsyn.c - functions for synchronous name resolves
- * hostip4.c - ipv4-specific functions
- * hostip6.c - ipv6-specific functions
+ * hostip4.c - IPv4 specific functions
+ * hostip6.c - IPv6 specific functions
*
* The two asynchronous name resolver backends are implemented in:
* asyn-ares.c - functions for ares-using name resolves
@@ -140,17 +137,13 @@ struct curl_hash *Curl_global_host_cache_init(void)
void Curl_global_host_cache_dtor(void)
{
if(host_cache_initialized) {
- /* first make sure that any custom "CURLOPT_RESOLVE" names are
- cleared off */
- Curl_hostcache_clean(NULL, &hostname_cache);
- /* then free the remaining hash completely */
- Curl_hash_clean(&hostname_cache);
+ Curl_hash_destroy(&hostname_cache);
host_cache_initialized = 0;
}
}
/*
- * Return # of adresses in a Curl_addrinfo struct
+ * Return # of addresses in a Curl_addrinfo struct
*/
int Curl_num_addresses(const Curl_addrinfo *addr)
{
@@ -179,7 +172,7 @@ Curl_printable_address(const Curl_addrinfo *ai, char *buf, size_t bufsize)
const struct in6_addr *ipaddr6;
#endif
- switch (ai->ai_family) {
+ switch(ai->ai_family) {
case AF_INET:
sa4 = (const void *)ai->ai_addr;
ipaddr4 = &sa4->sin_addr;
@@ -237,7 +230,8 @@ hostcache_timestamp_remove(void *datap, void *hc)
(struct hostcache_prune_data *) datap;
struct Curl_dns_entry *c = (struct Curl_dns_entry *) hc;
- return (data->now - c->timestamp >= data->cache_timeout);
+ return (0 != c->timestamp)
+ && (data->now - c->timestamp >= data->cache_timeout);
}
/*
@@ -260,7 +254,7 @@ hostcache_prune(struct curl_hash *hostcache, long cache_timeout, time_t now)
* Library-wide function for pruning the DNS cache. This function takes and
* returns the appropriate locks.
*/
-void Curl_hostcache_prune(struct SessionHandle *data)
+void Curl_hostcache_prune(struct Curl_easy *data)
{
time_t now;
@@ -283,40 +277,90 @@ void Curl_hostcache_prune(struct SessionHandle *data)
Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
}
-/*
- * Check if the entry should be pruned. Assumes a locked cache.
- */
-static int
-remove_entry_if_stale(struct SessionHandle *data, struct Curl_dns_entry *dns)
+#ifdef HAVE_SIGSETJMP
+/* Beware this is a global and unique instance. This is used to store the
+ return address that we can jump back to from inside a signal handler. This
+ is not thread-safe stuff. */
+sigjmp_buf curl_jmpenv;
+#endif
+
+/* lookup address, returns entry if found and not stale */
+static struct Curl_dns_entry *
+fetch_addr(struct connectdata *conn,
+ const char *hostname,
+ int port)
{
- struct hostcache_prune_data user;
+ char *entry_id = NULL;
+ struct Curl_dns_entry *dns = NULL;
+ size_t entry_len;
+ struct Curl_easy *data = conn->data;
- if(!dns || (data->set.dns_cache_timeout == -1) || !data->dns.hostcache)
- /* cache forever means never prune, and NULL hostcache means
- we can't do it */
- return 0;
+ /* 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 dns;
- time(&user.now);
- user.cache_timeout = data->set.dns_cache_timeout;
+ entry_len = strlen(entry_id);
- if(!hostcache_timestamp_remove(&user,dns) )
- return 0;
+ /* See if its already in our dns cache */
+ dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len + 1);
- Curl_hash_clean_with_criterium(data->dns.hostcache,
- (void *) &user,
- hostcache_timestamp_remove);
+ if(dns && (data->set.dns_cache_timeout != -1)) {
+ /* See whether the returned entry is stale. Done before we release lock */
+ struct hostcache_prune_data user;
+
+ time(&user.now);
+ user.cache_timeout = data->set.dns_cache_timeout;
- return 1;
+ if(hostcache_timestamp_remove(&user, dns)) {
+ infof(data, "Hostname in DNS cache was stale, zapped\n");
+ dns = NULL; /* the memory deallocation is being handled by the hash */
+ Curl_hash_delete(data->dns.hostcache, entry_id, entry_len + 1);
+ }
+ }
+
+ /* free the allocated entry_id again */
+ free(entry_id);
+
+ return dns;
}
+/*
+ * Curl_fetch_addr() fetches a 'Curl_dns_entry' already in the DNS cache.
+ *
+ * Curl_resolv() checks initially and multi_runsingle() checks each time
+ * it discovers the handle in the state WAITRESOLVE whether the hostname
+ * has already been resolved and the address has already been stored in
+ * the DNS cache. This short circuits waiting for a lot of pending
+ * lookups for the same hostname requested by different handles.
+ *
+ * Returns the Curl_dns_entry entry pointer or NULL if not in the cache.
+ *
+ * The returned data *MUST* be "unlocked" with Curl_resolv_unlock() after
+ * use, or we'll leak memory!
+ */
+struct Curl_dns_entry *
+Curl_fetch_addr(struct connectdata *conn,
+ const char *hostname,
+ int port)
+{
+ struct Curl_easy *data = conn->data;
+ struct Curl_dns_entry *dns = NULL;
+
+ if(data->share)
+ Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
-#ifdef HAVE_SIGSETJMP
-/* Beware this is a global and unique instance. This is used to store the
- return address that we can jump back to from inside a signal handler. This
- is not thread-safe stuff. */
-sigjmp_buf curl_jmpenv;
-#endif
+ dns = fetch_addr(conn, hostname, port);
+
+ if(dns)
+ dns->inuse++; /* we use it! */
+ if(data->share)
+ Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
+
+ return dns;
+}
/*
* Curl_cache_addr() stores a 'Curl_addrinfo' struct in the DNS cache.
@@ -328,7 +372,7 @@ sigjmp_buf curl_jmpenv;
* Returns the Curl_dns_entry entry pointer or NULL if the storage failed.
*/
struct Curl_dns_entry *
-Curl_cache_addr(struct SessionHandle *data,
+Curl_cache_addr(struct Curl_easy *data,
Curl_addrinfo *addr,
const char *hostname,
int port)
@@ -352,14 +396,14 @@ Curl_cache_addr(struct SessionHandle *data,
return NULL;
}
- dns->inuse = 0; /* init to not used */
+ dns->inuse = 1; /* the cache has the first reference */
dns->addr = addr; /* this is the address(es) */
time(&dns->timestamp);
if(dns->timestamp == 0)
- dns->timestamp = 1; /* zero indicates that entry isn't in hash table */
+ dns->timestamp = 1; /* zero indicates CURLOPT_RESOLVE entry */
/* Store the resolved data in our DNS cache. */
- dns2 = Curl_hash_add(data->dns.hostcache, entry_id, entry_len+1,
+ dns2 = Curl_hash_add(data->dns.hostcache, entry_id, entry_len + 1,
(void *)dns);
if(!dns2) {
free(dns);
@@ -402,37 +446,20 @@ int Curl_resolv(struct connectdata *conn,
int port,
struct Curl_dns_entry **entry)
{
- char *entry_id = NULL;
struct Curl_dns_entry *dns = NULL;
- size_t entry_len;
- struct SessionHandle *data = conn->data;
+ struct Curl_easy *data = conn->data;
CURLcode result;
int rc = CURLRESOLV_ERROR; /* default to failure */
*entry = NULL;
- /* 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 rc;
-
- entry_len = strlen(entry_id);
-
if(data->share)
Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
- /* See if its already in our dns cache */
- dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len+1);
-
- /* free the allocated entry_id again */
- free(entry_id);
-
- /* See whether the returned entry is stale. Done before we release lock */
- if(remove_entry_if_stale(data, dns))
- dns = NULL; /* the memory deallocation is being handled by the hash */
+ dns = fetch_addr(conn, hostname, port);
if(dns) {
+ infof(data, "Hostname %s was found in DNS cache\n", hostname);
dns->inuse++; /* we use it! */
rc = CURLRESOLV_RESOLVED;
}
@@ -511,7 +538,6 @@ RETSIGTYPE alarmfunc(int sig)
/* this is for "-ansi -Wall -pedantic" to stop complaining! (rabe) */
(void)sig;
siglongjmp(curl_jmpenv, 1);
- return;
}
#endif /* USE_ALARM_TIMEOUT */
@@ -541,12 +567,12 @@ int Curl_resolv_timeout(struct connectdata *conn,
const char *hostname,
int port,
struct Curl_dns_entry **entry,
- long timeoutms)
+ time_t timeoutms)
{
#ifdef USE_ALARM_TIMEOUT
#ifdef HAVE_SIGACTION
struct sigaction keep_sigact; /* store the old struct here */
- volatile bool keep_copysig = FALSE; /* wether old sigact has been saved */
+ volatile bool keep_copysig = FALSE; /* whether old sigact has been saved */
struct sigaction sigact;
#else
#ifdef HAVE_SIGNAL
@@ -555,7 +581,7 @@ int Curl_resolv_timeout(struct connectdata *conn,
#endif /* HAVE_SIGACTION */
volatile long timeout;
volatile unsigned int prev_alarm = 0;
- struct SessionHandle *data = conn->data;
+ struct Curl_easy *data = conn->data;
#endif /* USE_ALARM_TIMEOUT */
int rc;
@@ -570,43 +596,20 @@ int Curl_resolv_timeout(struct connectdata *conn,
/* Ignore the timeout when signals are disabled */
timeout = 0;
else
- timeout = timeoutms;
+ timeout = (timeoutms > LONG_MAX) ? LONG_MAX : (long)timeoutms;
if(!timeout)
/* USE_ALARM_TIMEOUT defined, but no timeout actually requested */
return Curl_resolv(conn, hostname, port, entry);
- if(timeout < 1000)
+ if(timeout < 1000) {
/* The alarm() function only provides integer second resolution, so if
we want to wait less than one second we must bail out already now. */
+ failf(data,
+ "remaining timeout of %ld too small to resolve via SIGALRM method",
+ timeout);
return CURLRESOLV_TIMEDOUT;
-
- /*************************************************************
- * Set signal handler to catch SIGALRM
- * Store the old value to be able to set it back later!
- *************************************************************/
-#ifdef HAVE_SIGACTION
- sigaction(SIGALRM, NULL, &sigact);
- keep_sigact = sigact;
- keep_copysig = TRUE; /* yes, we have a copy */
- sigact.sa_handler = alarmfunc;
-#ifdef SA_RESTART
- /* HPUX doesn't have SA_RESTART but defaults to that behaviour! */
- sigact.sa_flags &= ~SA_RESTART;
-#endif
- /* now set the new struct */
- sigaction(SIGALRM, &sigact, NULL);
-#else /* HAVE_SIGACTION */
- /* no sigaction(), revert to the much lamer signal() */
-#ifdef HAVE_SIGNAL
- keep_sigact = signal(SIGALRM, alarmfunc);
-#endif
-#endif /* HAVE_SIGACTION */
-
- /* alarm() makes a signal get sent when the timeout fires off, and that
- will abort system calls */
- prev_alarm = alarm(curlx_sltoui(timeout/1000L));
-
+ }
/* This allows us to time-out from the name resolver, as the timeout
will generate a signal and we will siglongjmp() from that here.
This technique has problems (see alarmfunc).
@@ -619,6 +622,33 @@ int Curl_resolv_timeout(struct connectdata *conn,
rc = CURLRESOLV_ERROR;
goto clean_up;
}
+ else {
+ /*************************************************************
+ * Set signal handler to catch SIGALRM
+ * Store the old value to be able to set it back later!
+ *************************************************************/
+#ifdef HAVE_SIGACTION
+ sigaction(SIGALRM, NULL, &sigact);
+ keep_sigact = sigact;
+ keep_copysig = TRUE; /* yes, we have a copy */
+ sigact.sa_handler = alarmfunc;
+#ifdef SA_RESTART
+ /* HPUX doesn't have SA_RESTART but defaults to that behaviour! */
+ sigact.sa_flags &= ~SA_RESTART;
+#endif
+ /* now set the new struct */
+ sigaction(SIGALRM, &sigact, NULL);
+#else /* HAVE_SIGACTION */
+ /* no sigaction(), revert to the much lamer signal() */
+#ifdef HAVE_SIGNAL
+ keep_sigact = signal(SIGALRM, alarmfunc);
+#endif
+#endif /* HAVE_SIGACTION */
+
+ /* alarm() makes a signal get sent when the timeout fires off, and that
+ will abort system calls */
+ prev_alarm = alarm(curlx_sltoui(timeout/1000L));
+ }
#else
#ifndef CURLRES_ASYNCH
@@ -658,10 +688,11 @@ clean_up:
the time we spent until now! */
if(prev_alarm) {
/* there was an alarm() set before us, now put it back */
- unsigned long elapsed_ms = Curl_tvdiff(Curl_tvnow(), conn->created);
+ timediff_t elapsed_secs = Curl_timediff(Curl_now(),
+ conn->created) / 1000;
/* the alarm period is counted in even number of seconds */
- unsigned long alarm_set = prev_alarm - elapsed_ms/1000;
+ unsigned long alarm_set = prev_alarm - elapsed_secs;
if(!alarm_set ||
((alarm_set >= 0x80000000) && (prev_alarm < 0x80000000)) ) {
@@ -688,56 +719,39 @@ clean_up:
*
* May be called with 'data' == NULL for global cache.
*/
-void Curl_resolv_unlock(struct SessionHandle *data, struct Curl_dns_entry *dns)
+void Curl_resolv_unlock(struct Curl_easy *data, struct Curl_dns_entry *dns)
{
- DEBUGASSERT(dns && (dns->inuse>0));
-
if(data && data->share)
Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
- dns->inuse--;
- /* only free if nobody is using AND it is not in hostcache (timestamp ==
- 0) */
- if(dns->inuse == 0 && dns->timestamp == 0) {
- Curl_freeaddrinfo(dns->addr);
- free(dns);
- }
+ freednsentry(dns);
if(data && data->share)
Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
}
/*
- * File-internal: free a cache dns entry.
+ * File-internal: release cache dns entry reference, free if inuse drops to 0
*/
static void freednsentry(void *freethis)
{
- struct Curl_dns_entry *p = (struct Curl_dns_entry *) freethis;
+ struct Curl_dns_entry *dns = (struct Curl_dns_entry *) freethis;
+ DEBUGASSERT(dns && (dns->inuse>0));
- /* mark the entry as not in hostcache */
- p->timestamp = 0;
- if(p->inuse == 0) {
- Curl_freeaddrinfo(p->addr);
- free(p);
+ dns->inuse--;
+ if(dns->inuse == 0) {
+ Curl_freeaddrinfo(dns->addr);
+ free(dns);
}
}
/*
- * Curl_mk_dnscache() creates a new DNS cache and returns the handle for it.
+ * Curl_mk_dnscache() inits a new DNS cache and returns success/failure.
*/
-struct curl_hash *Curl_mk_dnscache(void)
+int Curl_mk_dnscache(struct curl_hash *hash)
{
- return Curl_hash_alloc(7, Curl_hash_str, Curl_str_key_compare, freednsentry);
-}
-
-static int hostcache_inuse(void *data, void *hc)
-{
- struct Curl_dns_entry *c = (struct Curl_dns_entry *) hc;
-
- if(c->inuse == 1)
- Curl_resolv_unlock(data, c);
-
- return 1; /* free all entries */
+ return Curl_hash_init(hash, 7, Curl_hash_str, Curl_str_key_compare,
+ freednsentry);
}
/*
@@ -747,40 +761,87 @@ static int hostcache_inuse(void *data, void *hc)
* can be done!
*/
-void Curl_hostcache_clean(struct SessionHandle *data,
+void Curl_hostcache_clean(struct Curl_easy *data,
struct curl_hash *hash)
{
- /* Entries added to the hostcache with the CURLOPT_RESOLVE function are
- * still present in the cache with the inuse counter set to 1. Detect them
- * and cleanup!
- */
- Curl_hash_clean_with_criterium(hash, data, hostcache_inuse);
+ if(data && data->share)
+ Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
+
+ Curl_hash_clean(hash);
+
+ if(data && data->share)
+ Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
}
-CURLcode Curl_loadhostpairs(struct SessionHandle *data)
+CURLcode Curl_loadhostpairs(struct Curl_easy *data)
{
struct curl_slist *hostp;
char hostname[256];
- char address[256];
int port;
- for(hostp = data->change.resolve; hostp; hostp = hostp->next ) {
+ for(hostp = data->change.resolve; hostp; hostp = hostp->next) {
if(!hostp->data)
continue;
if(hostp->data[0] == '-') {
- /* TODO: mark an entry for removal */
+ char *entry_id;
+ size_t entry_len;
+
+ if(2 != sscanf(hostp->data + 1, "%255[^:]:%d", hostname, &port)) {
+ infof(data, "Couldn't parse CURLOPT_RESOLVE removal entry '%s'!\n",
+ hostp->data);
+ continue;
+ }
+
+ /* 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 CURLE_OUT_OF_MEMORY;
+ }
+
+ entry_len = strlen(entry_id);
+
+ if(data->share)
+ Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
+
+ /* delete entry, ignore if it didn't exist */
+ Curl_hash_delete(data->dns.hostcache, entry_id, entry_len + 1);
+
+ if(data->share)
+ Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
+
+ /* free the allocated entry_id again */
+ free(entry_id);
}
- else if(3 == sscanf(hostp->data, "%255[^:]:%d:%255s", hostname, &port,
- address)) {
+ else {
struct Curl_dns_entry *dns;
Curl_addrinfo *addr;
char *entry_id;
size_t entry_len;
+ char buffer[256];
+ char *address = &buffer[0];
+
+ if(3 != sscanf(hostp->data, "%255[^:]:%d:%255s", hostname, &port,
+ address)) {
+ infof(data, "Couldn't parse CURLOPT_RESOLVE entry '%s'!\n",
+ hostp->data);
+ continue;
+ }
+
+ /* allow IP(v6) address within [brackets] */
+ if(address[0] == '[') {
+ size_t alen = strlen(address);
+ if(address[alen-1] != ']')
+ /* it needs to also end with ] to be valid */
+ continue;
+ address[alen-1] = 0; /* zero terminate there */
+ address++; /* pass the open bracket */
+ }
addr = Curl_str2addr(address, port);
if(!addr) {
- infof(data, "Resolve %s found illegal!\n", hostp->data);
+ infof(data, "Address in '%s' found illegal!\n", hostp->data);
continue;
}
@@ -798,14 +859,21 @@ CURLcode Curl_loadhostpairs(struct SessionHandle *data)
Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
/* See if its already in our dns cache */
- dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len+1);
+ dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len + 1);
/* free the allocated entry_id again */
free(entry_id);
- if(!dns)
+ if(!dns) {
/* if not in the cache already, put this host in the cache */
dns = Curl_cache_addr(data, addr, hostname, port);
+ if(dns) {
+ dns->timestamp = 0; /* mark as added by CURLOPT_RESOLVE */
+ /* release the returned reference; the cache itself will keep the
+ * entry alive: */
+ dns->inuse--;
+ }
+ }
else
/* this is a duplicate, free it again */
Curl_freeaddrinfo(addr);