aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/curl/curl.h3
-rw-r--r--lib/curl_addrinfo.c21
-rw-r--r--lib/curl_addrinfo.h2
-rw-r--r--lib/hostip.c6
-rw-r--r--lib/transfer.c47
-rw-r--r--lib/url.c14
-rw-r--r--lib/urldata.h4
-rw-r--r--src/main.c11
8 files changed, 103 insertions, 5 deletions
diff --git a/include/curl/curl.h b/include/curl/curl.h
index cb9d0fbfb..e95887fe7 100644
--- a/include/curl/curl.h
+++ b/include/curl/curl.h
@@ -1436,6 +1436,9 @@ typedef enum {
/* FNMATCH_FUNCTION user pointer */
CINIT(FNMATCH_DATA, OBJECTPOINT, 202),
+ /* send linked-list of name:port:address sets */
+ CINIT(RESOLVE, OBJECTPOINT, 203),
+
CURLOPT_LASTENTRY /* the last unused */
} CURLoption;
diff --git a/lib/curl_addrinfo.c b/lib/curl_addrinfo.c
index 5098fa431..cfb858c6b 100644
--- a/lib/curl_addrinfo.c
+++ b/lib/curl_addrinfo.c
@@ -49,6 +49,7 @@
#endif
#include "curl_addrinfo.h"
+#include "inet_pton.h"
#define _MPRINTF_REPLACE /* use our functions only */
#include <curl/mprintf.h>
@@ -434,6 +435,26 @@ Curl_ip2addr(int af, const void *inaddr, const char *hostname, int port)
return ai;
}
+/*
+ * Given an IPv4 or IPv6 dotted string address, this converts it to a proper
+ * allocated Curl_addrinfo struct and returns it.
+ */
+Curl_addrinfo *Curl_str2addr(char *address, int port)
+{
+ struct in_addr in;
+ if(Curl_inet_pton(AF_INET, address, &in) > 0)
+ /* This is a dotted IP address 123.123.123.123-style */
+ return Curl_ip2addr(AF_INET, &in, address, port);
+#ifdef ENABLE_IPV6
+ else {
+ struct in6_addr in6;
+ if(Curl_inet_pton(AF_INET6, address, &in6) > 0)
+ /* This is a dotted IPv6 address ::1-style */
+ return Curl_ip2addr(AF_INET6, &in6, address, port);
+ }
+#endif
+ return NULL; /* bad input format */
+}
#if defined(CURLDEBUG) && defined(HAVE_FREEADDRINFO)
/*
diff --git a/lib/curl_addrinfo.h b/lib/curl_addrinfo.h
index 63159cc4a..11c339474 100644
--- a/lib/curl_addrinfo.h
+++ b/lib/curl_addrinfo.h
@@ -80,6 +80,8 @@ Curl_he2ai(const struct hostent *he, int port);
Curl_addrinfo *
Curl_ip2addr(int af, const void *inaddr, const char *hostname, int port);
+Curl_addrinfo *Curl_str2addr(char *dotted, int port);
+
#if defined(CURLDEBUG) && defined(HAVE_FREEADDRINFO)
void
curl_dofreeaddrinfo(struct addrinfo *freethis,
diff --git a/lib/hostip.c b/lib/hostip.c
index 8f6a52e4f..8e1494e3f 100644
--- a/lib/hostip.c
+++ b/lib/hostip.c
@@ -421,6 +421,9 @@ int Curl_resolv(struct connectdata *conn,
/* 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 */
@@ -433,9 +436,6 @@ int Curl_resolv(struct connectdata *conn,
if(data->share)
Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
- /* free the allocated entry_id again */
- free(entry_id);
-
if(!dns) {
/* The entry was not in the cache. Resolve it to IP address */
diff --git a/lib/transfer.c b/lib/transfer.c
index 754f6e621..ead3a4deb 100644
--- a/lib/transfer.c
+++ b/lib/transfer.c
@@ -1382,6 +1382,46 @@ Transfer(struct connectdata *conn)
return CURLE_OK;
}
+static void loadhostpairs(struct SessionHandle *data)
+{
+ struct curl_slist *hostp;
+ char hostname[256];
+ char address[256];
+ int port;
+
+ for(hostp = data->change.resolve; hostp; hostp = hostp->next ) {
+ if(!hostp->data)
+ continue;
+ if(hostp->data[0] == '-') {
+ /* mark an entry for removal */
+ }
+ else if(3 == sscanf(hostp->data, "%255[^:]:%d:%255s", hostname, &port,
+ address)) {
+ struct Curl_dns_entry *dns;
+ Curl_addrinfo *addr;
+
+ addr = Curl_str2addr(address, port);
+ if(!addr) {
+ infof(data, "Resolve %s found illegal!\n", hostp->data);
+ continue;
+ }
+ infof(data, "Added %s:%d:%s to DNS cache\n",
+ hostname, port, address);
+
+ if(data->share)
+ Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
+
+ /* put this host in the cache */
+ dns = Curl_cache_addr(data, addr, hostname, port);
+
+ if(data->share)
+ Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
+ }
+ }
+ data->change.resolve = NULL; /* dealt with now */
+}
+
+
/*
* Curl_pretransfer() is called immediately before a transfer starts.
*/
@@ -1415,9 +1455,12 @@ CURLcode Curl_pretransfer(struct SessionHandle *data)
data->info.wouldredirect = NULL;
/* If there is a list of cookie files to read, do it now! */
- if(data->change.cookielist) {
+ if(data->change.cookielist)
Curl_cookie_loadfiles(data);
- }
+
+ /* If there is a list of host pairs to deal with */
+ if(data->change.resolve)
+ loadhostpairs(data);
/* Allow data->set.use_port to set which port to use. This needs to be
* disabled for example when we follow Location: headers to URLs using
diff --git a/lib/url.c b/lib/url.c
index b715e998f..4a45256d4 100644
--- a/lib/url.c
+++ b/lib/url.c
@@ -1750,6 +1750,20 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option,
*/
data->set.quote = va_arg(param, struct curl_slist *);
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
+ * removed or the handle is cleaned up.
+ *
+ * This API can remove any name 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 *);
+ data->change.resolve = data->set.resolve;
+ break;
case CURLOPT_PROGRESSFUNCTION:
/*
* Progress callback function
diff --git a/lib/urldata.h b/lib/urldata.h
index 4d6059152..489ab16aa 100644
--- a/lib/urldata.h
+++ b/lib/urldata.h
@@ -1186,6 +1186,8 @@ struct DynamicStatic {
bool referer_alloc; /* referer sting is malloc()ed */
struct curl_slist *cookielist; /* list of cookie files set by
curl_easy_setopt(COOKIEFILE) calls */
+ struct curl_slist *resolve; /* set to point to the set.resolve list when
+ this should be dealt with in pretransfer */
};
/*
@@ -1332,6 +1334,8 @@ struct UserDefined {
struct curl_slist *source_postquote; /* in 3rd party transfer mode - after
the transfer on source host */
struct curl_slist *telnet_options; /* linked list of telnet options */
+ struct curl_slist *resolve; /* list of names to add/remove from
+ DNS cache */
curl_TimeCond timecondition; /* kind of time/date comparison */
time_t timevalue; /* what time to compare with */
Curl_HttpReq httpreq; /* what kind of HTTP request (if any) is this */
diff --git a/src/main.c b/src/main.c
index 79a2b30a6..91875e311 100644
--- a/src/main.c
+++ b/src/main.c
@@ -581,6 +581,7 @@ struct Configurable {
struct curl_httppost *httppost;
struct curl_httppost *last_post;
struct curl_slist *telnet_options;
+ struct curl_slist *resolve;
HttpReq httpreq;
/* for bandwidth limiting features: */
@@ -869,6 +870,7 @@ static void help(void)
" --remote-name-all Use the remote file name for all URLs",
" -R/--remote-time Set the remote file's time on the local output",
" -X/--request <command> Specify request command to use",
+ " --resolve <host:port:address> Force resolve of HOST:PORT to ADDRESS",
" --retry <num> Retry request <num> times if transient problems occur",
" --retry-delay <seconds> When retrying, wait this many seconds between each",
" --retry-max-time <seconds> Retry only within this period",
@@ -1881,6 +1883,7 @@ static ParameterError getparameter(char *flag, /* f or -long-flag */
{"$C", "ftp-pret", FALSE},
{"$D", "proto", TRUE},
{"$E", "proto-redir", TRUE},
+ {"$F", "resolve", TRUE},
{"0", "http1.0", FALSE},
{"1", "tlsv1", FALSE},
{"2", "sslv2", FALSE},
@@ -2442,6 +2445,9 @@ static ParameterError getparameter(char *flag, /* f or -long-flag */
if(proto2num(config, &config->proto_redir, nextarg))
return PARAM_BAD_USE;
break;
+ case 'F': /* --resolve */
+ err = add2list(&config->resolve, nextarg);
+ break;
}
break;
case '#': /* --progress-bar */
@@ -4043,6 +4049,7 @@ static void free_config_fields(struct Configurable *config)
curl_slist_free_all(config->headers);
curl_slist_free_all(config->telnet_options);
curl_slist_free_all(config->mail_rcpt);
+ curl_slist_free_all(config->resolve);
if(config->easy)
curl_easy_cleanup(config->easy);
@@ -5438,6 +5445,10 @@ operate(struct Configurable *config, int argc, argv_item_t argv[])
my_setopt(curl, CURLOPT_HEADERDATA, &outs);
}
+ if(config->resolve)
+ /* new in 7.21.3 */
+ my_setopt(curl, CURLOPT_RESOLVE, config->resolve);
+
retry_numretries = config->req_retry;
retrystart = cutil_tvnow();