From 1b24b89cca3c06e36b69969af8edeeaca659515b Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Fri, 5 Nov 2010 22:31:40 +0100 Subject: [PATCH] CURLOPT_RESOLVE: added CURLOPT_RESOLVE is a new option that sends along a curl_slist with name:port:address sets that will populate the DNS cache with entries so that request can be "fooled" to use another host than what otherwise would've been used. Previously we've encouraged the use of Host: for that when dealing with HTTP, but this new feature has the added bonus that it allows the name from the URL to be used for TLS SNI and server certificate name checks as well. This is a first change. Surely more will follow to make it decent. --- include/curl/curl.h | 3 +++ lib/curl_addrinfo.c | 21 ++++++++++++++++++++ lib/curl_addrinfo.h | 2 ++ lib/hostip.c | 6 +++--- lib/transfer.c | 47 +++++++++++++++++++++++++++++++++++++++++++-- lib/url.c | 14 ++++++++++++++ lib/urldata.h | 4 ++++ src/main.c | 11 +++++++++++ 8 files changed, 103 insertions(+), 5 deletions(-) diff --git a/include/curl/curl.h b/include/curl/curl.h index cb9d0fbfbf..e95887fe7a 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 5098fa4314..cfb858c6bd 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 @@ -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 63159cc4a6..11c339474c 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 8f6a52e4f0..8e1494e3f2 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 754f6e621c..ead3a4deb0 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 b715e998fc..4a45256d40 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 4d6059152a..489ab16aa5 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 79a2b30a6f..91875e311e 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 Specify request command to use", + " --resolve Force resolve of HOST:PORT to ADDRESS", " --retry Retry request times if transient problems occur", " --retry-delay When retrying, wait this many seconds between each", " --retry-max-time 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();