conn->ip_version = ipver;
if(h) {
+ int h_af = h->addr->ai_family;
/* convert the resolved address, sizeof myhost >= INET_ADDRSTRLEN */
Curl_printable_address(h->addr, myhost, sizeof(myhost));
infof(data, "Name '%s' family %i resolved to '%s' family %i",
- host, af, myhost, h->addr->ai_family);
- Curl_resolv_unlock(data, h);
- if(af != h->addr->ai_family) {
+ host, af, myhost, h_af);
+ Curl_resolv_unlink(data, &h); /* this will NULL, potential free h */
+ if(af != h_af) {
/* bad IP version combo, signal the caller to try another address
family if available */
return CURLE_UNSUPPORTED_PROTOCOL;
struct connectdata *conn)
{
if(!conn->bits.shutdown_handler) {
- if(conn->dns_entry) {
- Curl_resolv_unlock(data, conn->dns_entry);
- conn->dns_entry = NULL;
- }
+ if(conn->dns_entry)
+ Curl_resolv_unlink(data, &conn->dns_entry);
/* Cleanup NTLM connection-related data */
Curl_http_auth_cleanup_ntlm(conn);
while(curr) {
conn = curr->ptr;
- fprintf(stderr, " [%p %d]", (void *)conn, conn->inuse);
+ fprintf(stderr, " [%p %d]", (void *)conn, conn->refcount);
curr = curr->next;
}
fprintf(stderr, "\n");
Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
/* we got a response, store it in the cache */
- dns = Curl_cache_addr(data, ai, dohp->host, 0, dohp->port);
+ dns = Curl_cache_addr(data, ai, dohp->host, 0, dohp->port, FALSE);
if(data->share)
Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
int error;
char *host = NULL;
char *string_ftpport = data->set.str[STRING_FTPPORT];
- struct Curl_dns_entry *h = NULL;
+ struct Curl_dns_entry *dns_entry = NULL;
unsigned short port_min = 0;
unsigned short port_max = 0;
unsigned short port;
}
/* resolv ip/host to ip */
- rc = Curl_resolv(data, host, 0, FALSE, &h);
+ rc = Curl_resolv(data, host, 0, FALSE, &dns_entry);
if(rc == CURLRESOLV_PENDING)
- (void)Curl_resolver_wait_resolv(data, &h);
- if(h) {
- res = h->addr;
- /* when we return from this function, we can forget about this entry
- to we can unlock it now already */
- Curl_resolv_unlock(data, h);
- } /* (h) */
+ (void)Curl_resolver_wait_resolv(data, &dns_entry);
+ if(dns_entry) {
+ res = dns_entry->addr;
+ }
else
res = NULL; /* failure! */
ftp_state(data, FTP_PORT);
out:
+ /* If we looked up a dns_entry, now is the time to safely release it */
+ if(dns_entry)
+ Curl_resolv_unlink(data, &dns_entry);
if(result) {
ftp_state(data, FTP_STOP);
}
CURL_CF_SSL_ENABLE : CURL_CF_SSL_DISABLE);
if(result) {
- Curl_resolv_unlock(data, addr); /* we are done using this address */
+ Curl_resolv_unlink(data, &addr); /* we are done using this address */
if(ftpc->count1 == 0 && ftpcode == 229)
return ftp_epsv_disable(data, conn);
/* this just dumps information about this second connection */
ftp_pasv_verbose(data, addr->addr, ftpc->newhost, connectport);
- Curl_resolv_unlock(data, addr); /* we are done using this address */
+ Curl_resolv_unlink(data, &addr); /* we are done using this address */
Curl_safefree(conn->secondaryhostname);
conn->secondary_port = ftpc->newport;
dns = Curl_cache_addr(data, ai,
data->state.async.hostname, 0,
- data->state.async.port);
+ data->state.async.port, FALSE);
if(data->share)
Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
* CURLRES_* defines based on the config*.h and curl_setup.h defines.
*/
-static void freednsentry(void *freethis);
+static void hostcache_unlink_entry(void *entry);
#ifndef CURL_DISABLE_VERBOSE_STRINGS
static void show_resolve_info(struct Curl_easy *data,
struct hostcache_prune_data {
time_t now;
time_t oldest; /* oldest time in cache not pruned. */
- int cache_timeout;
+ int max_age_sec;
};
/*
* cache.
*/
static int
-hostcache_timestamp_remove(void *datap, void *hc)
+hostcache_entry_is_stale(void *datap, void *hc)
{
struct hostcache_prune_data *prune =
(struct hostcache_prune_data *) datap;
- struct Curl_dns_entry *c = (struct Curl_dns_entry *) hc;
+ struct Curl_dns_entry *dns = (struct Curl_dns_entry *) hc;
- if(c->timestamp) {
+ if(dns->timestamp) {
/* age in seconds */
- time_t age = prune->now - c->timestamp;
- if(age >= prune->cache_timeout)
+ time_t age = prune->now - dns->timestamp;
+ if(age >= prune->max_age_sec)
return TRUE;
if(age > prune->oldest)
prune->oldest = age;
{
struct hostcache_prune_data user;
- user.cache_timeout = cache_timeout;
+ user.max_age_sec = cache_timeout;
user.now = now;
user.oldest = 0;
Curl_hash_clean_with_criterium(hostcache,
(void *) &user,
- hostcache_timestamp_remove);
+ hostcache_entry_is_stale);
return user.oldest;
}
struct hostcache_prune_data user;
user.now = time(NULL);
- user.cache_timeout = data->set.dns_cache_timeout;
+ user.max_age_sec = data->set.dns_cache_timeout;
user.oldest = 0;
- if(hostcache_timestamp_remove(&user, dns)) {
+ if(hostcache_entry_is_stale(&user, dns)) {
infof(data, "Hostname in DNS cache was stale, zapped");
dns = NULL; /* the memory deallocation is being handled by the hash */
Curl_hash_delete(data->dns.hostcache, entry_id, entry_len + 1);
*
* 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
+ * The returned data *MUST* be "released" with Curl_resolv_unlink() after
* use, or we will leak memory!
*/
struct Curl_dns_entry *
dns = fetch_addr(data, hostname, port);
if(dns)
- dns->inuse++; /* we use it! */
+ dns->refcount++; /* we use it! */
if(data->share)
Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
struct Curl_addrinfo *addr,
const char *hostname,
size_t hostlen, /* length or zero */
- int port)
+ int port,
+ bool permanent)
{
char entry_id[MAX_HOSTCACHE_LEN];
size_t entry_len;
entry_len = create_hostcache_id(hostname, hostlen, port,
entry_id, sizeof(entry_id));
- dns->inuse = 1; /* the cache has the first reference */
+ dns->refcount = 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 permanent CURLOPT_RESOLVE entry */
+ if(permanent)
+ dns->timestamp = 0; /* an entry that never goes stale */
+ else {
+ dns->timestamp = time(NULL);
+ if(dns->timestamp == 0)
+ dns->timestamp = 1;
+ }
dns->hostport = port;
if(hostlen)
memcpy(dns->hostname, hostname, hostlen);
}
dns = dns2;
- dns->inuse++; /* mark entry as in-use */
+ dns->refcount++; /* mark entry as in-use */
return dns;
}
* 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 are
- * done using this struct) to decrease the counter again.
+ * function is used. You MUST call Curl_resolv_unlink() later (when you are
+ * done using this struct) to decrease the reference counter again.
*
* Return codes:
*
if(dns) {
infof(data, "Hostname %s was found in DNS cache", hostname);
- dns->inuse++; /* we use it! */
+ dns->refcount++; /* we use it! */
rc = CURLRESOLV_RESOLVED;
}
Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
/* we got a response, store it in the cache */
- dns = Curl_cache_addr(data, addr, hostname, 0, port);
+ dns = Curl_cache_addr(data, addr, hostname, 0, port, FALSE);
if(data->share)
Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
* 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 are
- * done using this struct) to decrease the counter again.
+ * function is used. You MUST call Curl_resolv_unlink() later (when you are
+ * done using this struct) to decrease the reference counter again.
*
* If built with a synchronous resolver and use of signals is not
* disabled by the application, then a nonzero timeout will cause a
}
/*
- * 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.
+ * Curl_resolv_unlink() releases a reference to the given cached DNS entry.
+ * When the reference count reaches 0, the entry is destroyed. It is important
+ * that only one unlink is made for each Curl_resolv() call.
*
* May be called with 'data' == NULL for global cache.
*/
-void Curl_resolv_unlock(struct Curl_easy *data, struct Curl_dns_entry *dns)
+void Curl_resolv_unlink(struct Curl_easy *data, struct Curl_dns_entry **pdns)
{
+ struct Curl_dns_entry *dns = *pdns;
+ *pdns = NULL;
if(data && data->share)
Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
- freednsentry(dns);
+ hostcache_unlink_entry(dns);
if(data && data->share)
Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
/*
* File-internal: release cache dns entry reference, free if inuse drops to 0
*/
-static void freednsentry(void *freethis)
+static void hostcache_unlink_entry(void *entry)
{
- struct Curl_dns_entry *dns = (struct Curl_dns_entry *) freethis;
- DEBUGASSERT(dns && (dns->inuse>0));
+ struct Curl_dns_entry *dns = (struct Curl_dns_entry *) entry;
+ DEBUGASSERT(dns && (dns->refcount>0));
- dns->inuse--;
- if(dns->inuse == 0) {
+ dns->refcount--;
+ if(dns->refcount == 0) {
Curl_freeaddrinfo(dns->addr);
#ifdef USE_HTTPSRR
if(dns->hinfo) {
void Curl_init_dnscache(struct Curl_hash *hash, size_t size)
{
Curl_hash_init(hash, size, Curl_hash_str, Curl_str_key_compare,
- freednsentry);
+ hostcache_unlink_entry);
}
/*
}
/* put this new host in the cache */
- dns = Curl_cache_addr(data, head, host_begin, hlen, port);
+ dns = Curl_cache_addr(data, head, host_begin, hlen, port, permanent);
if(dns) {
- if(permanent)
- dns->timestamp = 0; /* mark as permanent */
/* release the returned reference; the cache itself will keep the
* entry alive: */
- dns->inuse--;
+ dns->refcount--;
}
if(data->share)
#endif
/* timestamp == 0 -- permanent CURLOPT_RESOLVE entry (does not time out) */
time_t timestamp;
- /* use-counter, use Curl_resolv_unlock to release reference */
- long inuse;
+ /* reference counter, entry is freed on reaching 0 */
+ size_t refcount;
/* hostname port number that resolved to addr. */
int hostport;
/* hostname that resolved to addr. may be NULL (unix domain sockets). */
* Curl_resolv() returns an entry with the info for the specified host
* and port.
*
- * The returned data *MUST* be "unlocked" with Curl_resolv_unlock() after
+ * The returned data *MUST* be "released" with Curl_resolv_unlink() after
* use, or we will leak memory!
*/
/* return codes */
int *waitp);
-/* unlock a previously resolved dns entry */
-void Curl_resolv_unlock(struct Curl_easy *data,
- struct Curl_dns_entry *dns);
+/* unlink a dns entry, potentially shared with a cache */
+void Curl_resolv_unlink(struct Curl_easy *data,
+ struct Curl_dns_entry **pdns);
/* init a new dns cache */
void Curl_init_dnscache(struct Curl_hash *hash, size_t hashsize);
*
* 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
+ * The returned data *MUST* be "released" with Curl_resolv_unlink() after
* use, or we will leak memory!
*/
struct Curl_dns_entry *
/*
* Curl_cache_addr() stores a 'Curl_addrinfo' struct in the DNS cache.
- *
+ * @param permanent iff TRUE, entry will never become stale
* Returns the Curl_dns_entry entry pointer or NULL if the storage failed.
*/
struct Curl_dns_entry *
Curl_cache_addr(struct Curl_easy *data, struct Curl_addrinfo *addr,
- const char *hostname, size_t hostlen, int port);
+ const char *hostname, size_t hostlen, int port,
+ bool permanent);
#ifndef INADDR_NONE
#define CURL_INADDR_NONE (in_addr_t) ~0
data->state.done = TRUE; /* called just now! */
- if(conn->dns_entry) {
- Curl_resolv_unlock(data, conn->dns_entry); /* done with this */
- conn->dns_entry = NULL;
- }
+ if(conn->dns_entry)
+ Curl_resolv_unlink(data, &conn->dns_entry); /* done with this */
Curl_hostcache_prune(data);
/* if data->set.reuse_forbid is TRUE, it means the libcurl client has
infof(data, "SOCKS4 connect to IPv4 %s (locally resolved)", buf);
- Curl_resolv_unlock(data, dns); /* not used anymore from now on */
+ Curl_resolv_unlink(data, &dns); /* not used anymore from now on */
}
else
failf(data, "SOCKS4 connection to %s not supported", sx->hostname);
failf(data, "SOCKS5 connection to %s not supported", dest);
}
- Curl_resolv_unlock(data, dns); /* not used anymore from now on */
+ Curl_resolv_unlink(data, &dns); /* not used anymore from now on */
goto CONNECT_REQ_SEND;
}
CONNECT_RESOLVE_REMOTE:
return;
}
- if(conn->dns_entry) {
- Curl_resolv_unlock(data, conn->dns_entry);
- conn->dns_entry = NULL;
- }
+ if(conn->dns_entry)
+ Curl_resolv_unlink(data, &conn->dns_entry);
/* Cleanup NTLM connection-related data */
Curl_http_auth_cleanup_ntlm(conn);
return longpath ? CURLE_COULDNT_RESOLVE_HOST : CURLE_OUT_OF_MEMORY;
}
- hostaddr->inuse++;
+ hostaddr->refcount = 1; /* connection is the only one holding this */
conn->dns_entry = hostaddr;
return CURLE_OK;
}
#endif
-#ifndef CURL_DISABLE_PROXY
-static CURLcode resolve_proxy(struct Curl_easy *data,
- struct connectdata *conn,
- bool *async)
+/*************************************************************
+ * Resolve the address of the server or proxy
+ *************************************************************/
+static CURLcode resolve_server(struct Curl_easy *data,
+ struct connectdata *conn,
+ bool *async)
{
- struct Curl_dns_entry *hostaddr = NULL;
- struct hostname *host;
+ struct hostname *ehost;
timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE);
+ const char *peertype = "host";
int rc;
+#ifdef USE_UNIX_SOCKETS
+ char *unix_path = conn->unix_domain_socket;
- DEBUGASSERT(conn->dns_entry == NULL);
-
- host = conn->bits.socksproxy ? &conn->socks_proxy.host :
- &conn->http_proxy.host;
-
- conn->hostname_resolve = strdup(host->name);
- if(!conn->hostname_resolve)
- return CURLE_OUT_OF_MEMORY;
+#ifndef CURL_DISABLE_PROXY
+ if(!unix_path && CONN_IS_PROXIED(conn) && conn->socks_proxy.host.name &&
+ !strncmp(UNIX_SOCKET_PREFIX"/",
+ conn->socks_proxy.host.name, sizeof(UNIX_SOCKET_PREFIX)))
+ unix_path = conn->socks_proxy.host.name + sizeof(UNIX_SOCKET_PREFIX) - 1;
+#endif
- rc = Curl_resolv_timeout(data, conn->hostname_resolve,
- conn->primary.remote_port, &hostaddr, timeout_ms);
- conn->dns_entry = hostaddr;
- if(rc == CURLRESOLV_PENDING)
- *async = TRUE;
- else if(rc == CURLRESOLV_TIMEDOUT)
- return CURLE_OPERATION_TIMEDOUT;
- else if(!hostaddr) {
- failf(data, "Couldn't resolve proxy '%s'", host->dispname);
- return CURLE_COULDNT_RESOLVE_PROXY;
+ if(unix_path) {
+ /* TODO, this only works if previous transport is TRNSPRT_TCP. Check it? */
+ conn->transport = TRNSPRT_UNIX;
+ return resolve_unix(data, conn, unix_path);
}
-
- return CURLE_OK;
-}
#endif
-static CURLcode resolve_host(struct Curl_easy *data,
- struct connectdata *conn,
- bool *async)
-{
- struct Curl_dns_entry *hostaddr = NULL;
- struct hostname *connhost;
- timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE);
- int rc;
-
DEBUGASSERT(conn->dns_entry == NULL);
- connhost = conn->bits.conn_to_host ? &conn->conn_to_host : &conn->host;
-
- /* If not connecting via a proxy, extract the port from the URL, if it is
- * there, thus overriding any defaults that might have been set above. */
- conn->primary.remote_port = conn->bits.conn_to_port ? conn->conn_to_port :
- conn->remote_port;
+#ifndef CURL_DISABLE_PROXY
+ if(CONN_IS_PROXIED(conn)) {
+ ehost = conn->bits.socksproxy ? &conn->socks_proxy.host :
+ &conn->http_proxy.host;
+ peertype = "proxy";
+ }
+ else
+#endif
+ {
+ ehost = conn->bits.conn_to_host ? &conn->conn_to_host : &conn->host;
+ /* If not connecting via a proxy, extract the port from the URL, if it is
+ * there, thus overriding any defaults that might have been set above. */
+ conn->primary.remote_port = conn->bits.conn_to_port ? conn->conn_to_port :
+ conn->remote_port;
+ }
/* Resolve target host right on */
- conn->hostname_resolve = strdup(connhost->name);
+ conn->hostname_resolve = strdup(ehost->name);
if(!conn->hostname_resolve)
return CURLE_OUT_OF_MEMORY;
rc = Curl_resolv_timeout(data, conn->hostname_resolve,
- conn->primary.remote_port, &hostaddr, timeout_ms);
- conn->dns_entry = hostaddr;
+ conn->primary.remote_port,
+ &conn->dns_entry, timeout_ms);
if(rc == CURLRESOLV_PENDING)
*async = TRUE;
else if(rc == CURLRESOLV_TIMEDOUT) {
- failf(data, "Failed to resolve host '%s' with timeout after %"
- CURL_FORMAT_TIMEDIFF_T " ms", connhost->dispname,
+ failf(data, "Failed to resolve %s '%s' with timeout after %"
+ CURL_FORMAT_TIMEDIFF_T " ms", peertype, ehost->dispname,
Curl_timediff(Curl_now(), data->progress.t_startsingle));
return CURLE_OPERATION_TIMEDOUT;
}
- else if(!hostaddr) {
- failf(data, "Could not resolve host: %s", connhost->dispname);
+ else if(!conn->dns_entry) {
+ failf(data, "Could not resolve %s: %s", peertype, ehost->dispname);
return CURLE_COULDNT_RESOLVE_HOST;
}
return CURLE_OK;
}
-/* Perform a fresh resolve */
-static CURLcode resolve_fresh(struct Curl_easy *data,
- struct connectdata *conn,
- bool *async)
-{
-#ifdef USE_UNIX_SOCKETS
- char *unix_path = conn->unix_domain_socket;
-
-#ifndef CURL_DISABLE_PROXY
- if(!unix_path && conn->socks_proxy.host.name &&
- !strncmp(UNIX_SOCKET_PREFIX"/",
- conn->socks_proxy.host.name, sizeof(UNIX_SOCKET_PREFIX)))
- unix_path = conn->socks_proxy.host.name + sizeof(UNIX_SOCKET_PREFIX) - 1;
-#endif
-
- if(unix_path) {
- conn->transport = TRNSPRT_UNIX;
- return resolve_unix(data, conn, unix_path);
- }
-#endif
-
-#ifndef CURL_DISABLE_PROXY
- if(CONN_IS_PROXIED(conn))
- return resolve_proxy(data, conn, async);
-#endif
-
- return resolve_host(data, conn, async);
-}
-
-/*************************************************************
- * Resolve the address of the server or proxy
- *************************************************************/
-static CURLcode resolve_server(struct Curl_easy *data,
- struct connectdata *conn,
- bool *async)
-{
- DEBUGASSERT(conn);
- DEBUGASSERT(data);
-
- /* Resolve the name of the server or proxy */
- if(conn->bits.reuse) {
- /* We are reusing the connection - no need to resolve anything, and
- idnconvert_hostname() was called already in create_conn() for the reuse
- case. */
- *async = FALSE;
- return CURLE_OK;
- }
-
- return resolve_fresh(data, conn, async);
-}
-
/*
* Cleanup the connection `temp`, just allocated for `data`, before using the
* previously `existing` one for `data`. All relevant info is copied over
/* Continue connectdata initialization here. */
- /*************************************************************
- * Resolve the address of the server or proxy
- *************************************************************/
- result = resolve_server(data, conn, async);
- if(result)
- goto out;
+ if(conn->bits.reuse) {
+ /* We are reusing the connection - no need to resolve anything, and
+ idnconvert_hostname() was called already in create_conn() for the reuse
+ case. */
+ *async = FALSE;
+ }
+ else {
+ /*************************************************************
+ * Resolve the address of the server or proxy
+ *************************************************************/
+ result = resolve_server(data, conn, async);
+ if(result)
+ goto out;
+ }
/* Everything general done, inform filters that they need
* to prepare for a data transfer.
struct proxy_info http_proxy;
#endif
/* 'primary' and 'secondary' get filled with IP quadruple
- (local/remote numerical ip address and port) whenever a is *attempted*.
+ (local/remote numerical ip address and port) whenever a connect is
+ *attempted*.
When more than one address is tried for a connection these will hold data
for the last attempt. When the connection is actually established
these are updated with data which comes directly from the socket. */
if(data->set.tls_ech & CURLECH_HARD)
return CURLE_SSL_CONNECT_ERROR;
}
- Curl_resolv_unlock(data, dns);
+ Curl_resolv_unlink(data, &dns);
}
}
# ifdef OPENSSL_IS_BORINGSSL
if(data->set.tls_ech & CURLECH_HARD)
return CURLE_SSL_CONNECT_ERROR;
}
- Curl_resolv_unlock(data, dns);
+ Curl_resolv_unlink(data, &dns);
}
}
abort_unless(rc == CURLE_OK, "data node creation failed");
key_len = strlen(data_key);
- data_node->inuse = 1; /* hash will hold the reference */
+ data_node->refcount = 1; /* hash will hold the reference */
nodep = Curl_hash_add(&hp, data_key, key_len + 1, data_node);
abort_unless(nodep, "insertion into hash failed");
/* Freeing will now be done by Curl_hash_destroy */