Long: resolve
-Arg: <host:port:addr[,addr]...>
+Arg: <[+]host:port:addr[,addr]...>
Help: Resolve the host+port to this address
Added: 7.21.3
Category: connection
The provided address set by this option will be used even if --ipv4 or --ipv6
is set to make curl use another IP version.
+By prefixing the host with a '+' you can make the entry time out after curl's
+default timeout (1 minute). Note that this will only make sense for long
+running parallel transfers with a lot of files. In such cases, if this option
+is used curl will try to resolve the host as it normally would once the
+timeout has expired.
+
Support for providing the IP address within [brackets] was added in 7.57.0.
Support for providing multiple IP addresses per entry was added in 7.59.0.
Support for resolving with wildcard was added in 7.64.0.
+Support for the '+' prefix was was added in 7.75.0.
+
This option can be used many times to add many host names to resolve.
to clean up an entire list.
Each single name resolve string should be written using the format
-HOST:PORT:ADDRESS[,ADDRESS]... where HOST is the name libcurl will try
+[+]HOST:PORT:ADDRESS[,ADDRESS]... where HOST is the name libcurl will try
to resolve, PORT is the port number of the service where libcurl wants
to connect to the HOST and ADDRESS is one or more numerical IP
addresses. If you specify multiple ip addresses they need to be
This option effectively pre-populates the DNS cache with entries for the
host+port pair so redirects and everything that operations against the
-HOST+PORT will instead use your provided ADDRESS. Addresses set with
-\fICURLOPT_RESOLVE(3)\fP will not time-out from the DNS cache like ordinary
-entries.
+HOST+PORT will instead use your provided ADDRESS.
-If the DNS cache already have an entry for the given host+port pair, then
+The optional leading "+" signifies whether the new entry should time-out or
+not. Entires added with "HOST:..." will never time-out whereas entries added
+with "+HOST:..." will time-out just like ordinary DNS cache entries.
+
+If the DNS cache already has an entry for the given host+port pair, then
this entry will be removed and a new entry will be created. This is because
-old entry may have have different addresses or be ordinary entries with
-time-outs.
+the old entry may have have different addresses or a different time-out
+setting.
The provided ADDRESS set by this option will be used even if
\fICURLOPT_IPRESOLVE(3)\fP is set to make libcurl use another IP version.
Support for providing the ADDRESS within [brackets] was added in 7.57.0.
Support for providing multiple IP addresses per entry was added in 7.59.0.
+
+Support for adding non-permanent entries by using the "+" prefix was added in
+7.75.0.
.SH DEFAULT
NULL
.SH PROTOCOLS
dns->addr = addr; /* this is the address(es) */
time(&dns->timestamp);
if(dns->timestamp == 0)
- dns->timestamp = 1; /* zero indicates CURLOPT_RESOLVE entry */
+ dns->timestamp = 1; /* zero indicates permanent CURLOPT_RESOLVE entry */
/* Store the resolved data in our DNS cache. */
dns2 = Curl_hash_add(data->dns.hostcache, entry_id, entry_len + 1,
char *addr_end;
char *port_ptr;
char *end_ptr;
+ bool permanent = TRUE;
+ char *host_begin;
char *host_end;
unsigned long tmp_port;
bool error = true;
- host_end = strchr(hostp->data, ':');
+ host_begin = hostp->data;
+ if(host_begin[0] == '+') {
+ host_begin++;
+ permanent = FALSE;
+ }
+ host_end = strchr(host_begin, ':');
if(!host_end ||
- ((host_end - hostp->data) >= (ptrdiff_t)sizeof(hostname)))
+ ((host_end - host_begin) >= (ptrdiff_t)sizeof(hostname)))
goto err;
- memcpy(hostname, hostp->data, host_end - hostp->data);
- hostname[host_end - hostp->data] = '\0';
+ memcpy(hostname, host_begin, host_end - host_begin);
+ hostname[host_end - host_begin] = '\0';
port_ptr = host_end + 1;
tmp_port = strtoul(port_ptr, &end_ptr, 10);
if(data->share)
Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
- /* See if its already in our dns cache */
+ /* See if it's already in our dns cache */
dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len + 1);
if(dns) {
infof(data, "RESOLVE %s:%d is - old addresses discarded!\n",
hostname, port);
- /* delete old entry entry, there are two reasons for this
+ /* delete old entry, there are two reasons for this
1. old entry may have different addresses.
2. even if entry with correct addresses is already in the cache,
but if it is close to expire, then by the time next http
request is made, it can get expired and pruned because old
- entry is not necessarily marked as added by CURLOPT_RESOLVE. */
+ entry is not necessarily marked as permanent.
+ 3. when adding a non-permanent entry, we want it to remove and
+ replace an existing permanent entry.
+ 4. when adding a non-permanent entry, we want it to get a "fresh"
+ timeout that starts _now_. */
Curl_hash_delete(data->dns.hostcache, entry_id, entry_len + 1);
}
/* put this new host in the cache */
dns = Curl_cache_addr(data, head, hostname, port);
if(dns) {
- dns->timestamp = 0; /* mark as added by CURLOPT_RESOLVE */
+ if(permanent)
+ dns->timestamp = 0; /* mark as permanent */
/* release the returned reference; the cache itself will keep the
* entry alive: */
- dns->inuse--;
+ dns->inuse--;
}
if(data->share)
Curl_freeaddrinfo(head);
return CURLE_OUT_OF_MEMORY;
}
- infof(data, "Added %s:%d:%s to DNS cache\n",
- hostname, port, addresses);
+ infof(data, "Added %s:%d:%s to DNS cache%s\n",
+ hostname, port, addresses, permanent ? "" : " (non-permanent)");
/* Wildcard hostname */
if(hostname[0] == '*' && hostname[1] == '\0') {
struct Curl_dns_entry {
struct Curl_addrinfo *addr;
- /* timestamp == 0 -- CURLOPT_RESOLVE entry, doesn't timeout */
+ /* timestamp == 0 -- permanent CURLOPT_RESOLVE entry (doesn't time out) */
time_t timestamp;
/* use-counter, use Curl_resolv_unlock to release reference */
long inuse;
break;
case CURLOPT_RESOLVE:
/*
- * List of NAME:[address] names to populate the DNS cache with
- * Prefix the NAME with dash (-) to _remove_ the name from the cache.
- *
- * Names added with this API will remain in the cache until explicitly
+ * List of HOST:PORT:[addresses] strings to populate the DNS cache with
+ * Entries added this way will remain in the cache until explicitly
* removed or the handle is cleaned up.
*
- * This API can remove any name from the DNS cache, but only entries
+ * Prefix the HOST with plus sign (+) to have the entry expire just like
+ * automatically added entries.
+ *
+ * Prefix the HOST with dash (-) to _remove_ the entry from the cache.
+ *
+ * This API can remove any entry from the DNS cache, but only entries
* that aren't actually in use right now will be pruned immediately.
*/
data->set.resolve = va_arg(param, struct curl_slist *);
const char *host;
int port;
+ /* whether we expect a permanent or non-permanent cache entry */
+ bool permanent;
+
/* 0 to 9 addresses expected from hostcache */
const char *address[10];
};
static const struct testcase tests[] = {
/* spaces aren't allowed, for now */
{ "test.com:80:127.0.0.1, 127.0.0.2",
- "test.com", 80, { NULL, }
+ "test.com", 80, TRUE, { NULL, }
},
{ "TEST.com:80:,,127.0.0.1,,,127.0.0.2,,,,::1,,,",
- "test.com", 80, { "127.0.0.1", "127.0.0.2", IPV6ONLY("::1"), }
+ "test.com", 80, TRUE, { "127.0.0.1", "127.0.0.2", IPV6ONLY("::1"), }
},
{ "test.com:80:::1,127.0.0.1",
- "test.com", 80, { IPV6ONLY("::1"), "127.0.0.1", }
+ "test.com", 80, TRUE, { IPV6ONLY("::1"), "127.0.0.1", }
},
{ "test.com:80:[::1],127.0.0.1",
- "test.com", 80, { IPV6ONLY("::1"), "127.0.0.1", }
+ "test.com", 80, TRUE, { IPV6ONLY("::1"), "127.0.0.1", }
},
{ "test.com:80:::1",
- "test.com", 80, { IPV6ONLY("::1"), }
+ "test.com", 80, TRUE, { IPV6ONLY("::1"), }
},
{ "test.com:80:[::1]",
- "test.com", 80, { IPV6ONLY("::1"), }
+ "test.com", 80, TRUE, { IPV6ONLY("::1"), }
},
{ "test.com:80:127.0.0.1",
- "test.com", 80, { "127.0.0.1", }
+ "test.com", 80, TRUE, { "127.0.0.1", }
},
{ "test.com:80:,127.0.0.1",
- "test.com", 80, { "127.0.0.1", }
+ "test.com", 80, TRUE, { "127.0.0.1", }
},
{ "test.com:80:127.0.0.1,",
- "test.com", 80, { "127.0.0.1", }
+ "test.com", 80, TRUE, { "127.0.0.1", }
},
{ "test.com:0:127.0.0.1",
- "test.com", 0, { "127.0.0.1", }
+ "test.com", 0, TRUE, { "127.0.0.1", }
+ },
+ { "+test.com:80:127.0.0.1,",
+ "test.com", 80, FALSE, { "127.0.0.1", }
},
};
break;
}
- if(dns->timestamp != 0) {
- fprintf(stderr, "%s:%d tests[%d] failed. the timestamp is not zero. "
- "for tests[%d].address[%d\n",
- __FILE__, __LINE__, i, i, j);
+ if(dns->timestamp != 0 && tests[i].permanent) {
+ fprintf(stderr, "%s:%d tests[%d] failed. the timestamp is not zero "
+ "but tests[%d].permanent is TRUE\n",
+ __FILE__, __LINE__, i, i);
+ problem = true;
+ break;
+ }
+
+ if(dns->timestamp == 0 && !tests[i].permanent) {
+ fprintf(stderr, "%s:%d tests[%d] failed. the timestamp is zero "
+ "but tests[%d].permanent is FALSE\n",
+ __FILE__, __LINE__, i, i);
problem = true;
break;
}