/* 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;
#endif
#include "curl_addrinfo.h"
+#include "inet_pton.h"
#define _MPRINTF_REPLACE /* use our functions only */
#include <curl/mprintf.h>
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)
/*
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,
/* 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 */
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 */
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.
*/
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
*/
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
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 */
};
/*
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 */
struct curl_httppost *httppost;
struct curl_httppost *last_post;
struct curl_slist *telnet_options;
+ struct curl_slist *resolve;
HttpReq httpreq;
/* for bandwidth limiting features: */
" --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",
{"$C", "ftp-pret", FALSE},
{"$D", "proto", TRUE},
{"$E", "proto-redir", TRUE},
+ {"$F", "resolve", TRUE},
{"0", "http1.0", FALSE},
{"1", "tlsv1", FALSE},
{"2", "sslv2", FALSE},
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 */
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);
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();