diff options
author | George Hazan <ghazan@miranda.im> | 2017-12-06 21:44:09 +0300 |
---|---|---|
committer | George Hazan <ghazan@miranda.im> | 2017-12-06 21:44:17 +0300 |
commit | 4b6980f68d25901133519bc1ad1c5376819a3876 (patch) | |
tree | 0d919622bfc2659f34a7bed303fefb99ecab052a /libs/libcurl/src/url.c | |
parent | 0112d2767268037cf63e44c4464cf9eed237d06d (diff) |
libcurl: update to 7.57
Diffstat (limited to 'libs/libcurl/src/url.c')
-rw-r--r-- | libs/libcurl/src/url.c | 5407 |
1 files changed, 2163 insertions, 3244 deletions
diff --git a/libs/libcurl/src/url.c b/libs/libcurl/src/url.c index c1672d08bc..47f69c9f14 100644 --- a/libs/libcurl/src/url.c +++ b/libs/libcurl/src/url.c @@ -5,11 +5,11 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms - * are also available at http://curl.haxx.se/docs/copyright.html. + * are also available at https://curl.haxx.se/docs/copyright.html. * * You may opt to use, copy, modify, merge, publish, distribute and/or sell * copies of the Software, and permit persons to whom the Software is @@ -47,6 +47,10 @@ #include <inet.h> #endif +#ifdef HAVE_SYS_UN_H +#include <sys/un.h> +#endif + #ifndef HAVE_SOCKET #error "We can't compile without socket() support!" #endif @@ -55,36 +59,26 @@ #include <limits.h> #endif -#ifdef USE_LIBIDN -#include <idna.h> -#include <tld.h> -#include <stringprep.h> -#ifdef HAVE_IDN_FREE_H -#include <idn-free.h> -#else -/* prototype from idn-free.h, not provided by libidn 0.4.5's make install! */ -void idn_free (void *ptr); -#endif -#ifndef HAVE_IDN_FREE -/* if idn_free() was not found in this version of libidn use free() instead */ -#define idn_free(x) (free)(x) -#endif +#ifdef USE_LIBIDN2 +#include <idn2.h> + #elif defined(USE_WIN32_IDN) /* prototype for curl_win32_idn_to_ascii() */ -int curl_win32_idn_to_ascii(const char *in, char **out); -#endif /* USE_LIBIDN */ +bool curl_win32_idn_to_ascii(const char *in, char **out); +#endif /* USE_LIBIDN2 */ #include "urldata.h" #include "netrc.h" #include "formdata.h" -#include "sslgen.h" +#include "mime.h" +#include "vtls/vtls.h" #include "hostip.h" #include "transfer.h" #include "sendf.h" #include "progress.h" #include "cookie.h" -#include "strequal.h" +#include "strcase.h" #include "strerror.h" #include "escape.h" #include "strtok.h" @@ -96,10 +90,10 @@ int curl_win32_idn_to_ascii(const char *in, char **out); #include "multiif.h" #include "easyif.h" #include "speedcheck.h" -#include "rawstr.h" #include "warnless.h" #include "non-ascii.h" #include "inet_pton.h" +#include "getinfo.h" /* And now for the protocols */ #include "ftp.h" @@ -107,6 +101,7 @@ int curl_win32_idn_to_ascii(const char *in, char **out); #include "telnet.h" #include "tftp.h" #include "http.h" +#include "http2.h" #include "file.h" #include "curl_ldap.h" #include "ssh.h" @@ -114,41 +109,46 @@ int curl_win32_idn_to_ascii(const char *in, char **out); #include "url.h" #include "connect.h" #include "inet_ntop.h" -#include "curl_ntlm.h" +#include "http_ntlm.h" #include "curl_ntlm_wb.h" #include "socks.h" #include "curl_rtmp.h" #include "gopher.h" #include "http_proxy.h" -#include "bundles.h" #include "conncache.h" #include "multihandle.h" #include "pipeline.h" #include "dotdot.h" +#include "strdup.h" +#include "setopt.h" -#define _MPRINTF_REPLACE /* use our functions only */ -#include <curl/mprintf.h> - +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" #include "curl_memory.h" -/* The last #include file should be: */ #include "memdebug.h" /* Local static prototypes */ static struct connectdata * -find_oldest_idle_connection(struct SessionHandle *data); -static struct connectdata * -find_oldest_idle_connection_in_bundle(struct SessionHandle *data, +find_oldest_idle_connection_in_bundle(struct Curl_easy *data, struct connectbundle *bundle); static void conn_free(struct connectdata *conn); +static void free_fixed_hostname(struct hostname *host); static void signalPipeClose(struct curl_llist *pipeline, bool pipe_broke); -static CURLcode do_init(struct connectdata *conn); -static CURLcode parse_url_login(struct SessionHandle *data, +static CURLcode parse_url_login(struct Curl_easy *data, struct connectdata *conn, char **userptr, char **passwdptr, char **optionsptr); -static CURLcode parse_login_details(const char *login, const size_t len, - char **userptr, char **passwdptr, - char **optionsptr); +static unsigned int get_protocol_family(unsigned int protocol); + +/* Some parts of the code (e.g. chunked encoding) assume this buffer has at + * more than just a few bytes to play with. Don't let it become too small or + * bad things will happen. + */ +#if READBUFFER_SIZE < READBUFFER_MIN +# error READBUFFER_SIZE is too small +#endif + + /* * Protocol table. */ @@ -215,6 +215,15 @@ static const struct Curl_handler * const protocols[] = { #endif #endif +#if !defined(CURL_DISABLE_SMB) && defined(USE_NTLM) && \ + (CURL_SIZEOF_CURL_OFF_T > 4) && \ + (!defined(USE_WINDOWS_SSPI) || defined(USE_WIN32_CRYPTO)) + &Curl_handler_smb, +#ifdef USE_SSL + &Curl_handler_smbs, +#endif +#endif + #ifndef CURL_DISABLE_SMTP &Curl_handler_smtp, #ifdef USE_SSL @@ -261,112 +270,30 @@ static const struct Curl_handler Curl_handler_dummy = { ZERO_NULL, /* perform_getsock */ ZERO_NULL, /* disconnect */ ZERO_NULL, /* readwrite */ + ZERO_NULL, /* connection_check */ 0, /* defport */ 0, /* protocol */ PROTOPT_NONE /* flags */ }; -void Curl_freeset(struct SessionHandle *data) +void Curl_freeset(struct Curl_easy *data) { /* Free all dynamic strings stored in the data->set substructure. */ enum dupstring i; - for(i=(enum dupstring)0; i < STRING_LAST; i++) + for(i = (enum dupstring)0; i < STRING_LAST; i++) { Curl_safefree(data->set.str[i]); + } if(data->change.referer_alloc) { Curl_safefree(data->change.referer); data->change.referer_alloc = FALSE; } data->change.referer = NULL; -} - -static CURLcode setstropt(char **charp, char *s) -{ - /* Release the previous storage at `charp' and replace by a dynamic storage - copy of `s'. Return CURLE_OK or CURLE_OUT_OF_MEMORY. */ - - Curl_safefree(*charp); - - if(s) { - s = strdup(s); - - if(!s) - return CURLE_OUT_OF_MEMORY; - - *charp = s; - } - - return CURLE_OK; -} - -static CURLcode setstropt_userpwd(char *option, char **userp, char **passwdp, - char **optionsp) -{ - CURLcode result = CURLE_OK; - char *user = NULL; - char *passwd = NULL; - char *options = NULL; - - /* Parse the login details if specified. It not then we treat NULL as a hint - to clear the existing data */ - if(option) { - result = parse_login_details(option, strlen(option), - (userp ? &user : NULL), - (passwdp ? &passwd : NULL), - (optionsp ? &options : NULL)); - } - - if(!result) { - /* Store the username part of option if required */ - if(userp) { - if(!user && option && option[0] == ':') { - /* Allocate an empty string instead of returning NULL as user name */ - user = strdup(""); - if(!user) - result = CURLE_OUT_OF_MEMORY; - } - - Curl_safefree(*userp); - *userp = user; - } - - /* Store the password part of option if required */ - if(passwdp) { - Curl_safefree(*passwdp); - *passwdp = passwd; - } - - /* Store the options part of option if required */ - if(optionsp) { - Curl_safefree(*optionsp); - *optionsp = options; - } - } - - return result; -} - -CURLcode Curl_dupset(struct SessionHandle *dst, struct SessionHandle *src) -{ - CURLcode r = CURLE_OK; - enum dupstring i; - - /* Copy src->set into dst->set first, then deal with the strings - afterwards */ - dst->set = src->set; - - /* clear all string pointers first */ - memset(dst->set.str, 0, STRING_LAST * sizeof(char *)); - - /* duplicate all strings */ - for(i=(enum dupstring)0; i< STRING_LAST; i++) { - r = setstropt(&dst->set.str[i], src->set.str[i]); - if(r != CURLE_OK) - break; + if(data->change.url_alloc) { + Curl_safefree(data->change.url); + data->change.url_alloc = FALSE; } - - /* If a failure occurred, freeing has to be performed externally. */ - return r; + data->change.url = NULL; } /* @@ -378,14 +305,14 @@ CURLcode Curl_dupset(struct SessionHandle *dst, struct SessionHandle *src) * when curl_easy_perform() is invoked. */ -CURLcode Curl_close(struct SessionHandle *data) +CURLcode Curl_close(struct Curl_easy *data) { struct Curl_multi *m; if(!data) return CURLE_OK; - Curl_expire(data, 0); /* shut off timers */ + Curl_expire_clear(data); /* shut off timers */ m = data->multi; @@ -402,10 +329,7 @@ CURLcode Curl_close(struct SessionHandle *data) /* Destroy the timeout list that is held in the easy handle. It is /normally/ done by curl_multi_remove_handle() but this is "just in case" */ - if(data->state.timeoutlist) { - Curl_llist_destroy(data->state.timeoutlist, NULL); - data->state.timeoutlist = NULL; - } + Curl_llist_destroy(&data->state.timeoutlist, NULL); data->magic = 0; /* force a clear AFTER the possibly enforced removal from the multi handle, since that function uses the magic @@ -427,6 +351,10 @@ CURLcode Curl_close(struct SessionHandle *data) Curl_safefree(data->state.scratch); Curl_ssl_free_certinfo(data); + /* Cleanup possible redirect junk */ + free(data->req.newurl); + data->req.newurl = NULL; + if(data->change.referer_alloc) { Curl_safefree(data->change.referer); data->change.referer_alloc = FALSE; @@ -439,6 +367,7 @@ CURLcode Curl_close(struct SessionHandle *data) } data->change.url = NULL; + Curl_safefree(data->state.buffer); Curl_safefree(data->state.headerbuff); Curl_flush_cookies(data, 1); @@ -451,8 +380,11 @@ CURLcode Curl_close(struct SessionHandle *data) /* this destroys the channel and we cannot use it anymore after this */ Curl_resolver_cleanup(data->state.resolver); + Curl_http2_cleanup_dependencies(data); Curl_convert_close(data); + Curl_mime_cleanpart(&data->set.mimepost); + /* No longer a dirty share, if it exists */ if(data->share) { Curl_share_lock(data, CURL_LOCK_DATA_SHARE, CURL_LOCK_ACCESS_SINGLE); @@ -460,28 +392,30 @@ CURLcode Curl_close(struct SessionHandle *data) Curl_share_unlock(data, CURL_LOCK_DATA_SHARE); } + /* destruct wildcard structures if it is needed */ + Curl_wildcard_dtor(&data->wildcard); Curl_freeset(data); free(data); return CURLE_OK; } /* - * Initialize the UserDefined fields within a SessionHandle. - * This may be safely called on a new or existing SessionHandle. + * Initialize the UserDefined fields within a Curl_easy. + * This may be safely called on a new or existing Curl_easy. */ CURLcode Curl_init_userdefined(struct UserDefined *set) { - CURLcode res = CURLE_OK; + CURLcode result = CURLE_OK; set->out = stdout; /* default output to stdout */ - set->in = stdin; /* default input from stdin */ + set->in_set = stdin; /* default input from stdin */ set->err = stderr; /* default stderr to stderr */ /* use fwrite as default function to store output */ set->fwrite_func = (curl_write_callback)fwrite; /* use fread as default function to read input */ - set->fread_func = (curl_read_callback)fread; + set->fread_func_set = (curl_read_callback)fread; set->is_fread_set = 0; set->is_fwrite_set = 0; @@ -493,7 +427,7 @@ CURLcode Curl_init_userdefined(struct UserDefined *set) set->convtonetwork = ZERO_NULL; set->convfromutf8 = ZERO_NULL; - set->infilesize = -1; /* we don't know any size */ + set->filesize = -1; /* we don't know the size */ set->postfieldsize = -1; /* unknown size */ set->maxredirs = -1; /* allow any amount by default */ @@ -507,13 +441,16 @@ CURLcode Curl_init_userdefined(struct UserDefined *set) set->dns_cache_timeout = 60; /* Timeout every 60 seconds by default */ /* Set the default size of the SSL session ID cache */ - set->ssl.max_ssl_sessions = 5; + set->general_ssl.max_ssl_sessions = 5; - set->proxyport = CURL_DEFAULT_PROXY_PORT; /* from url.h */ + set->proxyport = 0; set->proxytype = CURLPROXY_HTTP; /* defaults to HTTP proxy */ set->httpauth = CURLAUTH_BASIC; /* defaults to basic */ set->proxyauth = CURLAUTH_BASIC; /* defaults to basic */ + /* SOCKS5 proxy auth defaults to username/password + GSS-API */ + set->socks5auth = CURLAUTH_BASIC | CURLAUTH_GSSAPI; + /* make libcurl quiet by default: */ set->hide_progress = TRUE; /* CURLOPT_NOPROGRESS changes these */ @@ -521,14 +458,16 @@ CURLcode Curl_init_userdefined(struct UserDefined *set) * libcurl 7.10 introduced SSL verification *by default*! This needs to be * switched off unless wanted. */ - set->ssl.verifypeer = TRUE; - set->ssl.verifyhost = TRUE; + set->ssl.primary.verifypeer = TRUE; + set->ssl.primary.verifyhost = TRUE; #ifdef USE_TLS_SRP set->ssl.authtype = CURL_TLSAUTH_NONE; #endif set->ssh_auth_types = CURLSSH_AUTH_DEFAULT; /* defaults to any auth type */ - set->ssl.sessionid = TRUE; /* session ID caching enabled by default */ + set->ssl.primary.sessionid = TRUE; /* session ID caching enabled by + default */ + set->proxy_ssl = set->ssl; set->new_file_perms = 0644; /* Default permissions */ set->new_directory_perms = 0755; /* Default permissions */ @@ -537,8 +476,9 @@ CURLcode Curl_init_userdefined(struct UserDefined *set) define since we internally only use the lower 16 bits for the passed in bitmask to not conflict with the private bits */ set->allowed_protocols = CURLPROTO_ALL; - set->redir_protocols = - CURLPROTO_ALL & ~(CURLPROTO_FILE|CURLPROTO_SCP); /* not FILE or SCP */ + set->redir_protocols = CURLPROTO_ALL & /* All except FILE, SCP and SMB */ + ~(CURLPROTO_FILE | CURLPROTO_SCP | CURLPROTO_SMB | + CURLPROTO_SMBS); #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) /* @@ -546,21 +486,29 @@ CURLcode Curl_init_userdefined(struct UserDefined *set) * seem not to follow rfc1961 section 4.3/4.4 */ set->socks5_gssapi_nec = FALSE; - /* set default gssapi service name */ - res = setstropt(&set->str[STRING_SOCKS5_GSSAPI_SERVICE], - (char *) CURL_DEFAULT_SOCKS5_GSSAPI_SERVICE); - if(res != CURLE_OK) - return res; #endif /* This is our preferred CA cert bundle/path since install time */ #if defined(CURL_CA_BUNDLE) - res = setstropt(&set->str[STRING_SSL_CAFILE], (char *) CURL_CA_BUNDLE); -#elif defined(CURL_CA_PATH) - res = setstropt(&set->str[STRING_SSL_CAPATH], (char *) CURL_CA_PATH); + result = Curl_setstropt(&set->str[STRING_SSL_CAFILE_ORIG], CURL_CA_BUNDLE); + if(result) + return result; + + result = Curl_setstropt(&set->str[STRING_SSL_CAFILE_PROXY], CURL_CA_BUNDLE); + if(result) + return result; #endif +#if defined(CURL_CA_PATH) + result = Curl_setstropt(&set->str[STRING_SSL_CAPATH_ORIG], CURL_CA_PATH); + if(result) + return result; - set->wildcardmatch = FALSE; + result = Curl_setstropt(&set->str[STRING_SSL_CAPATH_PROXY], CURL_CA_PATH); + if(result) + return result; +#endif + + set->wildcard_enabled = FALSE; set->chunk_bgn = ZERO_NULL; set->chunk_end = ZERO_NULL; @@ -570,8 +518,18 @@ CURLcode Curl_init_userdefined(struct UserDefined *set) set->tcp_keepalive = FALSE; set->tcp_keepintvl = 60; set->tcp_keepidle = 60; + set->tcp_fastopen = FALSE; + set->tcp_nodelay = TRUE; + + set->ssl_enable_npn = TRUE; + set->ssl_enable_alpn = TRUE; + + set->expect_100_timeout = 1000L; /* Wait for a second by default. */ + set->sep_headers = TRUE; /* separated header lists by default */ + set->buffer_size = READBUFFER_SIZE; - return res; + Curl_http2_init_userset(set); + return result; } /** @@ -582,59 +540,66 @@ CURLcode Curl_init_userdefined(struct UserDefined *set) * @return CURLcode */ -CURLcode Curl_open(struct SessionHandle **curl) +CURLcode Curl_open(struct Curl_easy **curl) { - CURLcode res = CURLE_OK; - struct SessionHandle *data; - CURLcode status; + CURLcode result; + struct Curl_easy *data; /* Very simple start-up: alloc the struct, init it with zeroes and return */ - data = calloc(1, sizeof(struct SessionHandle)); + data = calloc(1, sizeof(struct Curl_easy)); if(!data) { /* this is a very serious error */ - DEBUGF(fprintf(stderr, "Error: calloc of SessionHandle failed\n")); + DEBUGF(fprintf(stderr, "Error: calloc of Curl_easy failed\n")); return CURLE_OUT_OF_MEMORY; } data->magic = CURLEASY_MAGIC_NUMBER; - status = Curl_resolver_init(&data->state.resolver); - if(status) { + result = Curl_resolver_init(&data->state.resolver); + if(result) { DEBUGF(fprintf(stderr, "Error: resolver_init failed\n")); free(data); - return status; + return result; } /* We do some initial setup here, all those fields that can't be just 0 */ - data->state.headerbuff = malloc(HEADERSIZE); - if(!data->state.headerbuff) { - DEBUGF(fprintf(stderr, "Error: malloc of headerbuff failed\n")); - res = CURLE_OUT_OF_MEMORY; + data->state.buffer = malloc(READBUFFER_SIZE + 1); + if(!data->state.buffer) { + DEBUGF(fprintf(stderr, "Error: malloc of buffer failed\n")); + result = CURLE_OUT_OF_MEMORY; } else { - res = Curl_init_userdefined(&data->set); + Curl_mime_initpart(&data->set.mimepost, data); - data->state.headersize=HEADERSIZE; + data->state.headerbuff = malloc(HEADERSIZE); + if(!data->state.headerbuff) { + DEBUGF(fprintf(stderr, "Error: malloc of headerbuff failed\n")); + result = CURLE_OUT_OF_MEMORY; + } + else { + result = Curl_init_userdefined(&data->set); - Curl_convert_init(data); + data->state.headersize = HEADERSIZE; + Curl_convert_init(data); + Curl_initinfo(data); - /* most recent connection is not yet defined */ - data->state.lastconnect = NULL; + /* most recent connection is not yet defined */ + data->state.lastconnect = NULL; - data->progress.flags |= PGRS_HIDE; - data->state.current_speed = -1; /* init to negative == impossible */ + data->progress.flags |= PGRS_HIDE; + data->state.current_speed = -1; /* init to negative == impossible */ + data->set.fnmatch = ZERO_NULL; + data->set.maxconnects = DEFAULT_CONNCACHE_SIZE; /* for easy handles */ - data->wildcard.state = CURLWC_INIT; - data->wildcard.filelist = NULL; - data->set.fnmatch = ZERO_NULL; - data->set.maxconnects = DEFAULT_CONNCACHE_SIZE; /* for easy handles */ + Curl_http2_init_state(&data->state); + } } - if(res) { + if(result) { Curl_resolver_cleanup(data->state.resolver); - if(data->state.headerbuff) - free(data->state.headerbuff); + free(data->state.buffer); + free(data->state.headerbuff); Curl_freeset(data); free(data); data = NULL; @@ -642,1847 +607,46 @@ CURLcode Curl_open(struct SessionHandle **curl) else *curl = data; - return res; + return result; } -CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, - va_list param) +#ifdef USE_RECV_BEFORE_SEND_WORKAROUND +static void conn_reset_postponed_data(struct connectdata *conn, int num) { - char *argptr; - CURLcode result = CURLE_OK; - long arg; -#ifndef CURL_DISABLE_HTTP - curl_off_t bigsize; -#endif - - switch(option) { - case CURLOPT_DNS_CACHE_TIMEOUT: - data->set.dns_cache_timeout = va_arg(param, long); - break; - case CURLOPT_DNS_USE_GLOBAL_CACHE: - /* remember we want this enabled */ - arg = va_arg(param, long); - data->set.global_dns_cache = (0 != arg)?TRUE:FALSE; - break; - case CURLOPT_SSL_CIPHER_LIST: - /* set a list of cipher we want to use in the SSL connection */ - result = setstropt(&data->set.str[STRING_SSL_CIPHER_LIST], - va_arg(param, char *)); - break; - - case CURLOPT_RANDOM_FILE: - /* - * This is the path name to a file that contains random data to seed - * the random SSL stuff with. The file is only used for reading. - */ - result = setstropt(&data->set.str[STRING_SSL_RANDOM_FILE], - va_arg(param, char *)); - break; - case CURLOPT_EGDSOCKET: - /* - * The Entropy Gathering Daemon socket pathname - */ - result = setstropt(&data->set.str[STRING_SSL_EGDSOCKET], - va_arg(param, char *)); - break; - case CURLOPT_MAXCONNECTS: - /* - * Set the absolute number of maximum simultaneous alive connection that - * libcurl is allowed to have. - */ - data->set.maxconnects = va_arg(param, long); - break; - case CURLOPT_FORBID_REUSE: - /* - * When this transfer is done, it must not be left to be reused by a - * subsequent transfer but shall be closed immediately. - */ - data->set.reuse_forbid = (0 != va_arg(param, long))?TRUE:FALSE; - break; - case CURLOPT_FRESH_CONNECT: - /* - * This transfer shall not use a previously cached connection but - * should be made with a fresh new connect! - */ - data->set.reuse_fresh = (0 != va_arg(param, long))?TRUE:FALSE; - break; - case CURLOPT_VERBOSE: - /* - * Verbose means infof() calls that give a lot of information about - * the connection and transfer procedures as well as internal choices. - */ - data->set.verbose = (0 != va_arg(param, long))?TRUE:FALSE; - break; - case CURLOPT_HEADER: - /* - * Set to include the header in the general data output stream. - */ - data->set.include_header = (0 != va_arg(param, long))?TRUE:FALSE; - break; - case CURLOPT_NOPROGRESS: - /* - * Shut off the internal supported progress meter - */ - data->set.hide_progress = (0 != va_arg(param, long))?TRUE:FALSE; - if(data->set.hide_progress) - data->progress.flags |= PGRS_HIDE; - else - data->progress.flags &= ~PGRS_HIDE; - break; - case CURLOPT_NOBODY: - /* - * Do not include the body part in the output data stream. - */ - data->set.opt_no_body = (0 != va_arg(param, long))?TRUE:FALSE; - break; - case CURLOPT_FAILONERROR: - /* - * Don't output the >=300 error code HTML-page, but instead only - * return error. - */ - data->set.http_fail_on_error = (0 != va_arg(param, long))?TRUE:FALSE; - break; - case CURLOPT_UPLOAD: - case CURLOPT_PUT: - /* - * We want to sent data to the remote host. If this is HTTP, that equals - * using the PUT request. - */ - data->set.upload = (0 != va_arg(param, long))?TRUE:FALSE; - if(data->set.upload) { - /* If this is HTTP, PUT is what's needed to "upload" */ - data->set.httpreq = HTTPREQ_PUT; - data->set.opt_no_body = FALSE; /* this is implied */ - } - else - /* In HTTP, the opposite of upload is GET (unless NOBODY is true as - then this can be changed to HEAD later on) */ - data->set.httpreq = HTTPREQ_GET; - break; - case CURLOPT_FILETIME: - /* - * Try to get the file time of the remote document. The time will - * later (possibly) become available using curl_easy_getinfo(). - */ - data->set.get_filetime = (0 != va_arg(param, long))?TRUE:FALSE; - break; - case CURLOPT_FTP_CREATE_MISSING_DIRS: - /* - * An FTP option that modifies an upload to create missing directories on - * the server. - */ - switch(va_arg(param, long)) { - case 0: - data->set.ftp_create_missing_dirs = 0; - break; - case 1: - data->set.ftp_create_missing_dirs = 1; - break; - case 2: - data->set.ftp_create_missing_dirs = 2; - break; - default: - /* reserve other values for future use */ - result = CURLE_UNKNOWN_OPTION; - break; - } - break; - case CURLOPT_SERVER_RESPONSE_TIMEOUT: - /* - * Option that specifies how quickly an server response must be obtained - * before it is considered failure. For pingpong protocols. - */ - data->set.server_response_timeout = va_arg( param , long ) * 1000; - break; - case CURLOPT_TFTP_BLKSIZE: - /* - * TFTP option that specifies the block size to use for data transmission - */ - data->set.tftp_blksize = va_arg(param, long); - break; - case CURLOPT_DIRLISTONLY: - /* - * An option that changes the command to one that asks for a list - * only, no file info details. - */ - data->set.ftp_list_only = (0 != va_arg(param, long))?TRUE:FALSE; - break; - case CURLOPT_APPEND: - /* - * We want to upload and append to an existing file. - */ - data->set.ftp_append = (0 != va_arg(param, long))?TRUE:FALSE; - break; - case CURLOPT_FTP_FILEMETHOD: - /* - * How do access files over FTP. - */ - data->set.ftp_filemethod = (curl_ftpfile)va_arg(param, long); - break; - case CURLOPT_NETRC: - /* - * Parse the $HOME/.netrc file - */ - data->set.use_netrc = (enum CURL_NETRC_OPTION)va_arg(param, long); - break; - case CURLOPT_NETRC_FILE: - /* - * Use this file instead of the $HOME/.netrc file - */ - result = setstropt(&data->set.str[STRING_NETRC_FILE], - va_arg(param, char *)); - break; - case CURLOPT_TRANSFERTEXT: - /* - * This option was previously named 'FTPASCII'. Renamed to work with - * more protocols than merely FTP. - * - * Transfer using ASCII (instead of BINARY). - */ - data->set.prefer_ascii = (0 != va_arg(param, long))?TRUE:FALSE; - break; - case CURLOPT_TIMECONDITION: - /* - * Set HTTP time condition. This must be one of the defines in the - * curl/curl.h header file. - */ - data->set.timecondition = (curl_TimeCond)va_arg(param, long); - break; - case CURLOPT_TIMEVALUE: - /* - * This is the value to compare with the remote document with the - * method set with CURLOPT_TIMECONDITION - */ - data->set.timevalue = (time_t)va_arg(param, long); - break; - case CURLOPT_SSLVERSION: - /* - * Set explicit SSL version to try to connect with, as some SSL - * implementations are lame. - */ - data->set.ssl.version = va_arg(param, long); - break; - -#ifndef CURL_DISABLE_HTTP - case CURLOPT_AUTOREFERER: - /* - * Switch on automatic referer that gets set if curl follows locations. - */ - data->set.http_auto_referer = (0 != va_arg(param, long))?TRUE:FALSE; - break; - - case CURLOPT_ACCEPT_ENCODING: - /* - * String to use at the value of Accept-Encoding header. - * - * If the encoding is set to "" we use an Accept-Encoding header that - * encompasses all the encodings we support. - * If the encoding is set to NULL we don't send an Accept-Encoding header - * and ignore an received Content-Encoding header. - * - */ - argptr = va_arg(param, char *); - result = setstropt(&data->set.str[STRING_ENCODING], - (argptr && !*argptr)? - (char *) ALL_CONTENT_ENCODINGS: argptr); - break; - - case CURLOPT_TRANSFER_ENCODING: - data->set.http_transfer_encoding = (0 != va_arg(param, long))?TRUE:FALSE; - break; - - case CURLOPT_FOLLOWLOCATION: - /* - * Follow Location: header hints on a HTTP-server. - */ - data->set.http_follow_location = (0 != va_arg(param, long))?TRUE:FALSE; - break; - - case CURLOPT_UNRESTRICTED_AUTH: - /* - * Send authentication (user+password) when following locations, even when - * hostname changed. - */ - data->set.http_disable_hostname_check_before_authentication = - (0 != va_arg(param, long))?TRUE:FALSE; - break; - - case CURLOPT_MAXREDIRS: - /* - * The maximum amount of hops you allow curl to follow Location: - * headers. This should mostly be used to detect never-ending loops. - */ - data->set.maxredirs = va_arg(param, long); - break; - - case CURLOPT_POSTREDIR: - { - /* - * Set the behaviour of POST when redirecting - * CURL_REDIR_GET_ALL - POST is changed to GET after 301 and 302 - * CURL_REDIR_POST_301 - POST is kept as POST after 301 - * CURL_REDIR_POST_302 - POST is kept as POST after 302 - * CURL_REDIR_POST_303 - POST is kept as POST after 303 - * CURL_REDIR_POST_ALL - POST is kept as POST after 301, 302 and 303 - * other - POST is kept as POST after 301 and 302 - */ - int postRedir = curlx_sltosi(va_arg(param, long)); - data->set.keep_post = postRedir & CURL_REDIR_POST_ALL; - } - break; - - case CURLOPT_POST: - /* Does this option serve a purpose anymore? Yes it does, when - CURLOPT_POSTFIELDS isn't used and the POST data is read off the - callback! */ - if(va_arg(param, long)) { - data->set.httpreq = HTTPREQ_POST; - data->set.opt_no_body = FALSE; /* this is implied */ - } - else - data->set.httpreq = HTTPREQ_GET; - break; - - case CURLOPT_COPYPOSTFIELDS: - /* - * A string with POST data. Makes curl HTTP POST. Even if it is NULL. - * If needed, CURLOPT_POSTFIELDSIZE must have been set prior to - * CURLOPT_COPYPOSTFIELDS and not altered later. - */ - argptr = va_arg(param, char *); - - if(!argptr || data->set.postfieldsize == -1) - result = setstropt(&data->set.str[STRING_COPYPOSTFIELDS], argptr); - else { - /* - * Check that requested length does not overflow the size_t type. - */ - - if((data->set.postfieldsize < 0) || - ((sizeof(curl_off_t) != sizeof(size_t)) && - (data->set.postfieldsize > (curl_off_t)((size_t)-1)))) - result = CURLE_OUT_OF_MEMORY; - else { - char * p; - - (void) setstropt(&data->set.str[STRING_COPYPOSTFIELDS], NULL); - - /* Allocate even when size == 0. This satisfies the need of possible - later address compare to detect the COPYPOSTFIELDS mode, and - to mark that postfields is used rather than read function or - form data. - */ - p = malloc((size_t)(data->set.postfieldsize? - data->set.postfieldsize:1)); - - if(!p) - result = CURLE_OUT_OF_MEMORY; - else { - if(data->set.postfieldsize) - memcpy(p, argptr, (size_t)data->set.postfieldsize); - - data->set.str[STRING_COPYPOSTFIELDS] = p; - } - } - } - - data->set.postfields = data->set.str[STRING_COPYPOSTFIELDS]; - data->set.httpreq = HTTPREQ_POST; - break; - - case CURLOPT_POSTFIELDS: - /* - * Like above, but use static data instead of copying it. - */ - data->set.postfields = va_arg(param, void *); - /* Release old copied data. */ - (void) setstropt(&data->set.str[STRING_COPYPOSTFIELDS], NULL); - data->set.httpreq = HTTPREQ_POST; - break; - - case CURLOPT_POSTFIELDSIZE: - /* - * The size of the POSTFIELD data to prevent libcurl to do strlen() to - * figure it out. Enables binary posts. - */ - bigsize = va_arg(param, long); - - if(data->set.postfieldsize < bigsize && - data->set.postfields == data->set.str[STRING_COPYPOSTFIELDS]) { - /* Previous CURLOPT_COPYPOSTFIELDS is no longer valid. */ - (void) setstropt(&data->set.str[STRING_COPYPOSTFIELDS], NULL); - data->set.postfields = NULL; - } - - data->set.postfieldsize = bigsize; - break; - - case CURLOPT_POSTFIELDSIZE_LARGE: - /* - * The size of the POSTFIELD data to prevent libcurl to do strlen() to - * figure it out. Enables binary posts. - */ - bigsize = va_arg(param, curl_off_t); - - if(data->set.postfieldsize < bigsize && - data->set.postfields == data->set.str[STRING_COPYPOSTFIELDS]) { - /* Previous CURLOPT_COPYPOSTFIELDS is no longer valid. */ - (void) setstropt(&data->set.str[STRING_COPYPOSTFIELDS], NULL); - data->set.postfields = NULL; - } - - data->set.postfieldsize = bigsize; - break; - - case CURLOPT_HTTPPOST: - /* - * Set to make us do HTTP POST - */ - data->set.httppost = va_arg(param, struct curl_httppost *); - data->set.httpreq = HTTPREQ_POST_FORM; - data->set.opt_no_body = FALSE; /* this is implied */ - break; - - case CURLOPT_REFERER: - /* - * String to set in the HTTP Referer: field. - */ - if(data->change.referer_alloc) { - Curl_safefree(data->change.referer); - data->change.referer_alloc = FALSE; - } - result = setstropt(&data->set.str[STRING_SET_REFERER], - va_arg(param, char *)); - data->change.referer = data->set.str[STRING_SET_REFERER]; - break; - - case CURLOPT_USERAGENT: - /* - * String to use in the HTTP User-Agent field - */ - result = setstropt(&data->set.str[STRING_USERAGENT], - va_arg(param, char *)); - break; - - case CURLOPT_HTTPHEADER: - /* - * Set a list with HTTP headers to use (or replace internals with) - */ - data->set.headers = va_arg(param, struct curl_slist *); - break; - - case CURLOPT_HTTP200ALIASES: - /* - * Set a list of aliases for HTTP 200 in response header - */ - data->set.http200aliases = va_arg(param, struct curl_slist *); - break; - -#if !defined(CURL_DISABLE_COOKIES) - case CURLOPT_COOKIE: - /* - * Cookie string to send to the remote server in the request. - */ - result = setstropt(&data->set.str[STRING_COOKIE], - va_arg(param, char *)); - break; - - case CURLOPT_COOKIEFILE: - /* - * Set cookie file to read and parse. Can be used multiple times. - */ - argptr = (char *)va_arg(param, void *); - if(argptr) { - struct curl_slist *cl; - /* append the cookie file name to the list of file names, and deal with - them later */ - cl = curl_slist_append(data->change.cookielist, argptr); - if(!cl) { - curl_slist_free_all(data->change.cookielist); - data->change.cookielist = NULL; - return CURLE_OUT_OF_MEMORY; - } - data->change.cookielist = cl; /* store the list for later use */ - } - break; - - case CURLOPT_COOKIEJAR: - /* - * Set cookie file name to dump all cookies to when we're done. - */ - result = setstropt(&data->set.str[STRING_COOKIEJAR], - va_arg(param, char *)); - - /* - * Activate the cookie parser. This may or may not already - * have been made. - */ - data->cookies = Curl_cookie_init(data, NULL, data->cookies, - data->set.cookiesession); - break; - - case CURLOPT_COOKIESESSION: - /* - * Set this option to TRUE to start a new "cookie session". It will - * prevent the forthcoming read-cookies-from-file actions to accept - * cookies that are marked as being session cookies, as they belong to a - * previous session. - * - * In the original Netscape cookie spec, "session cookies" are cookies - * with no expire date set. RFC2109 describes the same action if no - * 'Max-Age' is set and RFC2965 includes the RFC2109 description and adds - * a 'Discard' action that can enforce the discard even for cookies that - * have a Max-Age. - * - * We run mostly with the original cookie spec, as hardly anyone implements - * anything else. - */ - data->set.cookiesession = (0 != va_arg(param, long))?TRUE:FALSE; - break; - - case CURLOPT_COOKIELIST: - argptr = va_arg(param, char *); - - if(argptr == NULL) - break; - - Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); - - if(Curl_raw_equal(argptr, "ALL")) { - /* clear all cookies */ - Curl_cookie_clearall(data->cookies); - } - else if(Curl_raw_equal(argptr, "SESS")) { - /* clear session cookies */ - Curl_cookie_clearsess(data->cookies); - } - else if(Curl_raw_equal(argptr, "FLUSH")) { - /* flush cookies to file */ - Curl_flush_cookies(data, 0); - } - else { - if(!data->cookies) - /* if cookie engine was not running, activate it */ - data->cookies = Curl_cookie_init(data, NULL, NULL, TRUE); - - argptr = strdup(argptr); - if(!argptr) { - result = CURLE_OUT_OF_MEMORY; - } - else { - - if(checkprefix("Set-Cookie:", argptr)) - /* HTTP Header format line */ - Curl_cookie_add(data, data->cookies, TRUE, argptr + 11, NULL, NULL); - - else - /* Netscape format line */ - Curl_cookie_add(data, data->cookies, FALSE, argptr, NULL, NULL); - - free(argptr); - } - } - Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE); - - break; -#endif /* CURL_DISABLE_COOKIES */ - - case CURLOPT_HTTPGET: - /* - * Set to force us do HTTP GET - */ - if(va_arg(param, long)) { - data->set.httpreq = HTTPREQ_GET; - data->set.upload = FALSE; /* switch off upload */ - data->set.opt_no_body = FALSE; /* this is implied */ - } - break; - - case CURLOPT_HTTP_VERSION: - /* - * This sets a requested HTTP version to be used. The value is one of - * the listed enums in curl/curl.h. - */ - arg = va_arg(param, long); -#ifndef USE_NGHTTP2 - if(arg == CURL_HTTP_VERSION_2_0) - return CURLE_UNSUPPORTED_PROTOCOL; -#endif - data->set.httpversion = arg; - break; - - case CURLOPT_HTTPAUTH: - /* - * Set HTTP Authentication type BITMASK. - */ - { - int bitcheck; - bool authbits; - unsigned long auth = va_arg(param, unsigned long); - - if(auth == CURLAUTH_NONE) { - data->set.httpauth = auth; - break; - } - - /* the DIGEST_IE bit is only used to set a special marker, for all the - rest we need to handle it as normal DIGEST */ - data->state.authhost.iestyle = (auth & CURLAUTH_DIGEST_IE)?TRUE:FALSE; - - if(auth & CURLAUTH_DIGEST_IE) { - auth |= CURLAUTH_DIGEST; /* set standard digest bit */ - auth &= ~CURLAUTH_DIGEST_IE; /* unset ie digest bit */ - } - - /* switch off bits we can't support */ -#ifndef USE_NTLM - auth &= ~CURLAUTH_NTLM; /* no NTLM support */ - auth &= ~CURLAUTH_NTLM_WB; /* no NTLM_WB support */ -#elif !defined(NTLM_WB_ENABLED) - auth &= ~CURLAUTH_NTLM_WB; /* no NTLM_WB support */ -#endif -#ifndef USE_HTTP_NEGOTIATE - auth &= ~CURLAUTH_GSSNEGOTIATE; /* no GSS-Negotiate without GSSAPI or - WINDOWS_SSPI */ -#endif - - /* check if any auth bit lower than CURLAUTH_ONLY is still set */ - bitcheck = 0; - authbits = FALSE; - while(bitcheck < 31) { - if(auth & (1UL << bitcheck++)) { - authbits = TRUE; - break; - } - } - if(!authbits) - return CURLE_NOT_BUILT_IN; /* no supported types left! */ - - data->set.httpauth = auth; - } - break; - -#endif /* CURL_DISABLE_HTTP */ - - case CURLOPT_CUSTOMREQUEST: - /* - * Set a custom string to use as request - */ - result = setstropt(&data->set.str[STRING_CUSTOMREQUEST], - va_arg(param, char *)); - - /* we don't set - data->set.httpreq = HTTPREQ_CUSTOM; - here, we continue as if we were using the already set type - and this just changes the actual request keyword */ - break; - -#ifndef CURL_DISABLE_PROXY - case CURLOPT_HTTPPROXYTUNNEL: - /* - * Tunnel operations through the proxy instead of normal proxy use - */ - data->set.tunnel_thru_httpproxy = (0 != va_arg(param, long))?TRUE:FALSE; - break; - - case CURLOPT_PROXYPORT: - /* - * Explicitly set HTTP proxy port number. - */ - data->set.proxyport = va_arg(param, long); - break; - - case CURLOPT_PROXYAUTH: - /* - * Set HTTP Authentication type BITMASK. - */ - { - int bitcheck; - bool authbits; - unsigned long auth = va_arg(param, unsigned long); - - if(auth == CURLAUTH_NONE) { - data->set.proxyauth = auth; - break; - } - - /* the DIGEST_IE bit is only used to set a special marker, for all the - rest we need to handle it as normal DIGEST */ - data->state.authproxy.iestyle = (auth & CURLAUTH_DIGEST_IE)?TRUE:FALSE; - - if(auth & CURLAUTH_DIGEST_IE) { - auth |= CURLAUTH_DIGEST; /* set standard digest bit */ - auth &= ~CURLAUTH_DIGEST_IE; /* unset ie digest bit */ - } - /* switch off bits we can't support */ -#ifndef USE_NTLM - auth &= ~CURLAUTH_NTLM; /* no NTLM support */ - auth &= ~CURLAUTH_NTLM_WB; /* no NTLM_WB support */ -#elif !defined(NTLM_WB_ENABLED) - auth &= ~CURLAUTH_NTLM_WB; /* no NTLM_WB support */ -#endif -#ifndef USE_HTTP_NEGOTIATE - auth &= ~CURLAUTH_GSSNEGOTIATE; /* no GSS-Negotiate without GSSAPI or - WINDOWS_SSPI */ -#endif - - /* check if any auth bit lower than CURLAUTH_ONLY is still set */ - bitcheck = 0; - authbits = FALSE; - while(bitcheck < 31) { - if(auth & (1UL << bitcheck++)) { - authbits = TRUE; - break; - } - } - if(!authbits) - return CURLE_NOT_BUILT_IN; /* no supported types left! */ - - data->set.proxyauth = auth; - } - break; - - case CURLOPT_PROXY: - /* - * Set proxy server:port to use as HTTP proxy. - * - * If the proxy is set to "" we explicitly say that we don't want to use a - * proxy (even though there might be environment variables saying so). - * - * Setting it to NULL, means no proxy but allows the environment variables - * to decide for us. - */ - result = setstropt(&data->set.str[STRING_PROXY], - va_arg(param, char *)); - break; - - case CURLOPT_PROXYTYPE: - /* - * Set proxy type. HTTP/HTTP_1_0/SOCKS4/SOCKS4a/SOCKS5/SOCKS5_HOSTNAME - */ - data->set.proxytype = (curl_proxytype)va_arg(param, long); - break; - - case CURLOPT_PROXY_TRANSFER_MODE: - /* - * set transfer mode (;type=<a|i>) when doing FTP via an HTTP proxy - */ - switch (va_arg(param, long)) { - case 0: - data->set.proxy_transfer_mode = FALSE; - break; - case 1: - data->set.proxy_transfer_mode = TRUE; - break; - default: - /* reserve other values for future use */ - result = CURLE_UNKNOWN_OPTION; - break; - } - break; -#endif /* CURL_DISABLE_PROXY */ - -#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) - case CURLOPT_SOCKS5_GSSAPI_SERVICE: - /* - * Set gssapi service name - */ - result = setstropt(&data->set.str[STRING_SOCKS5_GSSAPI_SERVICE], - va_arg(param, char *)); - break; - - case CURLOPT_SOCKS5_GSSAPI_NEC: - /* - * set flag for nec socks5 support - */ - data->set.socks5_gssapi_nec = (0 != va_arg(param, long))?TRUE:FALSE; - break; -#endif - - case CURLOPT_WRITEHEADER: - /* - * Custom pointer to pass the header write callback function - */ - data->set.writeheader = (void *)va_arg(param, void *); - break; - case CURLOPT_ERRORBUFFER: - /* - * Error buffer provided by the caller to get the human readable - * error string in. - */ - data->set.errorbuffer = va_arg(param, char *); - break; - case CURLOPT_FILE: - /* - * FILE pointer to write to. Or possibly - * used as argument to the write callback. - */ - data->set.out = va_arg(param, void *); - break; - case CURLOPT_FTPPORT: - /* - * Use FTP PORT, this also specifies which IP address to use - */ - result = setstropt(&data->set.str[STRING_FTPPORT], - va_arg(param, char *)); - data->set.ftp_use_port = (NULL != data->set.str[STRING_FTPPORT]) ? - TRUE:FALSE; - break; - - case CURLOPT_FTP_USE_EPRT: - data->set.ftp_use_eprt = (0 != va_arg(param, long))?TRUE:FALSE; - break; - - case CURLOPT_FTP_USE_EPSV: - data->set.ftp_use_epsv = (0 != va_arg(param, long))?TRUE:FALSE; - break; - - case CURLOPT_FTP_USE_PRET: - data->set.ftp_use_pret = (0 != va_arg(param, long))?TRUE:FALSE; - break; - - case CURLOPT_FTP_SSL_CCC: - data->set.ftp_ccc = (curl_ftpccc)va_arg(param, long); - break; - - case CURLOPT_FTP_SKIP_PASV_IP: - /* - * Enable or disable FTP_SKIP_PASV_IP, which will disable/enable the - * bypass of the IP address in PASV responses. - */ - data->set.ftp_skip_ip = (0 != va_arg(param, long))?TRUE:FALSE; - break; - - case CURLOPT_INFILE: - /* - * FILE pointer to read the file to be uploaded from. Or possibly - * used as argument to the read callback. - */ - data->set.in = va_arg(param, void *); - break; - case CURLOPT_INFILESIZE: - /* - * If known, this should inform curl about the file size of the - * to-be-uploaded file. - */ - data->set.infilesize = va_arg(param, long); - break; - case CURLOPT_INFILESIZE_LARGE: - /* - * If known, this should inform curl about the file size of the - * to-be-uploaded file. - */ - data->set.infilesize = va_arg(param, curl_off_t); - break; - case CURLOPT_LOW_SPEED_LIMIT: - /* - * The low speed limit that if transfers are below this for - * CURLOPT_LOW_SPEED_TIME, the transfer is aborted. - */ - data->set.low_speed_limit=va_arg(param, long); - break; - case CURLOPT_MAX_SEND_SPEED_LARGE: - /* - * When transfer uploads are faster then CURLOPT_MAX_SEND_SPEED_LARGE - * bytes per second the transfer is throttled.. - */ - data->set.max_send_speed=va_arg(param, curl_off_t); - break; - case CURLOPT_MAX_RECV_SPEED_LARGE: - /* - * When receiving data faster than CURLOPT_MAX_RECV_SPEED_LARGE bytes per - * second the transfer is throttled.. - */ - data->set.max_recv_speed=va_arg(param, curl_off_t); - break; - case CURLOPT_LOW_SPEED_TIME: - /* - * The low speed time that if transfers are below the set - * CURLOPT_LOW_SPEED_LIMIT during this time, the transfer is aborted. - */ - data->set.low_speed_time=va_arg(param, long); - break; - case CURLOPT_URL: - /* - * The URL to fetch. - */ - if(data->change.url_alloc) { - /* the already set URL is allocated, free it first! */ - Curl_safefree(data->change.url); - data->change.url_alloc = FALSE; - } - result = setstropt(&data->set.str[STRING_SET_URL], - va_arg(param, char *)); - data->change.url = data->set.str[STRING_SET_URL]; - break; - case CURLOPT_PORT: - /* - * The port number to use when getting the URL - */ - data->set.use_port = va_arg(param, long); - break; - case CURLOPT_TIMEOUT: - /* - * The maximum time you allow curl to use for a single transfer - * operation. - */ - data->set.timeout = va_arg(param, long) * 1000L; - break; - - case CURLOPT_TIMEOUT_MS: - data->set.timeout = va_arg(param, long); - break; - - case CURLOPT_CONNECTTIMEOUT: - /* - * The maximum time you allow curl to use to connect. - */ - data->set.connecttimeout = va_arg(param, long) * 1000L; - break; - - case CURLOPT_CONNECTTIMEOUT_MS: - data->set.connecttimeout = va_arg(param, long); - break; - - case CURLOPT_ACCEPTTIMEOUT_MS: - /* - * The maximum time you allow curl to wait for server connect - */ - data->set.accepttimeout = va_arg(param, long); - break; - - case CURLOPT_USERPWD: - /* - * user:password;options to use in the operation - */ - result = setstropt_userpwd(va_arg(param, char *), - &data->set.str[STRING_USERNAME], - &data->set.str[STRING_PASSWORD], - &data->set.str[STRING_OPTIONS]); - break; - case CURLOPT_USERNAME: - /* - * authentication user name to use in the operation - */ - result = setstropt(&data->set.str[STRING_USERNAME], - va_arg(param, char *)); - break; - case CURLOPT_PASSWORD: - /* - * authentication password to use in the operation - */ - result = setstropt(&data->set.str[STRING_PASSWORD], - va_arg(param, char *)); - break; - case CURLOPT_XOAUTH2_BEARER: - /* - * XOAUTH2 bearer token to use in the operation - */ - result = setstropt(&data->set.str[STRING_BEARER], - va_arg(param, char *)); - break; - case CURLOPT_POSTQUOTE: - /* - * List of RAW FTP commands to use after a transfer - */ - data->set.postquote = va_arg(param, struct curl_slist *); - break; - case CURLOPT_PREQUOTE: - /* - * List of RAW FTP commands to use prior to RETR (Wesley Laxton) - */ - data->set.prequote = va_arg(param, struct curl_slist *); - break; - case CURLOPT_QUOTE: - /* - * List of RAW FTP commands to use before a transfer - */ - 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 - */ - data->set.fprogress = va_arg(param, curl_progress_callback); - if(data->set.fprogress) - data->progress.callback = TRUE; /* no longer internal */ - else - data->progress.callback = FALSE; /* NULL enforces internal */ - break; - - case CURLOPT_XFERINFOFUNCTION: - /* - * Transfer info callback function - */ - data->set.fxferinfo = va_arg(param, curl_xferinfo_callback); - if(data->set.fxferinfo) - data->progress.callback = TRUE; /* no longer internal */ - else - data->progress.callback = FALSE; /* NULL enforces internal */ - - break; - - case CURLOPT_PROGRESSDATA: - /* - * Custom client data to pass to the progress callback - */ - data->set.progress_client = va_arg(param, void *); - break; - -#ifndef CURL_DISABLE_PROXY - case CURLOPT_PROXYUSERPWD: - /* - * user:password needed to use the proxy - */ - result = setstropt_userpwd(va_arg(param, char *), - &data->set.str[STRING_PROXYUSERNAME], - &data->set.str[STRING_PROXYPASSWORD], NULL); - break; - case CURLOPT_PROXYUSERNAME: - /* - * authentication user name to use in the operation - */ - result = setstropt(&data->set.str[STRING_PROXYUSERNAME], - va_arg(param, char *)); - break; - case CURLOPT_PROXYPASSWORD: - /* - * authentication password to use in the operation - */ - result = setstropt(&data->set.str[STRING_PROXYPASSWORD], - va_arg(param, char *)); - break; - case CURLOPT_NOPROXY: - /* - * proxy exception list - */ - result = setstropt(&data->set.str[STRING_NOPROXY], - va_arg(param, char *)); - break; -#endif - - case CURLOPT_RANGE: - /* - * What range of the file you want to transfer - */ - result = setstropt(&data->set.str[STRING_SET_RANGE], - va_arg(param, char *)); - break; - case CURLOPT_RESUME_FROM: - /* - * Resume transfer at the give file position - */ - data->set.set_resume_from = va_arg(param, long); - break; - case CURLOPT_RESUME_FROM_LARGE: - /* - * Resume transfer at the give file position - */ - data->set.set_resume_from = va_arg(param, curl_off_t); - break; - case CURLOPT_DEBUGFUNCTION: - /* - * stderr write callback. - */ - data->set.fdebug = va_arg(param, curl_debug_callback); - /* - * if the callback provided is NULL, it'll use the default callback - */ - break; - case CURLOPT_DEBUGDATA: - /* - * Set to a void * that should receive all error writes. This - * defaults to CURLOPT_STDERR for normal operations. - */ - data->set.debugdata = va_arg(param, void *); - break; - case CURLOPT_STDERR: - /* - * Set to a FILE * that should receive all error writes. This - * defaults to stderr for normal operations. - */ - data->set.err = va_arg(param, FILE *); - if(!data->set.err) - data->set.err = stderr; - break; - case CURLOPT_HEADERFUNCTION: - /* - * Set header write callback - */ - data->set.fwrite_header = va_arg(param, curl_write_callback); - break; - case CURLOPT_WRITEFUNCTION: - /* - * Set data write callback - */ - data->set.fwrite_func = va_arg(param, curl_write_callback); - if(!data->set.fwrite_func) { - data->set.is_fwrite_set = 0; - /* When set to NULL, reset to our internal default function */ - data->set.fwrite_func = (curl_write_callback)fwrite; - } - else - data->set.is_fwrite_set = 1; - break; - case CURLOPT_READFUNCTION: - /* - * Read data callback - */ - data->set.fread_func = va_arg(param, curl_read_callback); - if(!data->set.fread_func) { - data->set.is_fread_set = 0; - /* When set to NULL, reset to our internal default function */ - data->set.fread_func = (curl_read_callback)fread; - } - else - data->set.is_fread_set = 1; - break; - case CURLOPT_SEEKFUNCTION: - /* - * Seek callback. Might be NULL. - */ - data->set.seek_func = va_arg(param, curl_seek_callback); - break; - case CURLOPT_SEEKDATA: - /* - * Seek control callback. Might be NULL. - */ - data->set.seek_client = va_arg(param, void *); - break; - case CURLOPT_CONV_FROM_NETWORK_FUNCTION: - /* - * "Convert from network encoding" callback - */ - data->set.convfromnetwork = va_arg(param, curl_conv_callback); - break; - case CURLOPT_CONV_TO_NETWORK_FUNCTION: - /* - * "Convert to network encoding" callback - */ - data->set.convtonetwork = va_arg(param, curl_conv_callback); - break; - case CURLOPT_CONV_FROM_UTF8_FUNCTION: - /* - * "Convert from UTF-8 encoding" callback - */ - data->set.convfromutf8 = va_arg(param, curl_conv_callback); - break; - case CURLOPT_IOCTLFUNCTION: - /* - * I/O control callback. Might be NULL. - */ - data->set.ioctl_func = va_arg(param, curl_ioctl_callback); - break; - case CURLOPT_IOCTLDATA: - /* - * I/O control data pointer. Might be NULL. - */ - data->set.ioctl_client = va_arg(param, void *); - break; - case CURLOPT_SSLCERT: - /* - * String that holds file name of the SSL certificate to use - */ - result = setstropt(&data->set.str[STRING_CERT], - va_arg(param, char *)); - break; - case CURLOPT_SSLCERTTYPE: - /* - * String that holds file type of the SSL certificate to use - */ - result = setstropt(&data->set.str[STRING_CERT_TYPE], - va_arg(param, char *)); - break; - case CURLOPT_SSLKEY: - /* - * String that holds file name of the SSL key to use - */ - result = setstropt(&data->set.str[STRING_KEY], - va_arg(param, char *)); - break; - case CURLOPT_SSLKEYTYPE: - /* - * String that holds file type of the SSL key to use - */ - result = setstropt(&data->set.str[STRING_KEY_TYPE], - va_arg(param, char *)); - break; - case CURLOPT_KEYPASSWD: - /* - * String that holds the SSL or SSH private key password. - */ - result = setstropt(&data->set.str[STRING_KEY_PASSWD], - va_arg(param, char *)); - break; - case CURLOPT_SSLENGINE: - /* - * String that holds the SSL crypto engine. - */ - argptr = va_arg(param, char *); - if(argptr && argptr[0]) - result = Curl_ssl_set_engine(data, argptr); - break; - - case CURLOPT_SSLENGINE_DEFAULT: - /* - * flag to set engine as default. - */ - result = Curl_ssl_set_engine_default(data); - break; - case CURLOPT_CRLF: - /* - * Kludgy option to enable CRLF conversions. Subject for removal. - */ - data->set.crlf = (0 != va_arg(param, long))?TRUE:FALSE; - break; - - case CURLOPT_INTERFACE: - /* - * Set what interface or address/hostname to bind the socket to when - * performing an operation and thus what from-IP your connection will use. - */ - result = setstropt(&data->set.str[STRING_DEVICE], - va_arg(param, char *)); - break; - case CURLOPT_LOCALPORT: - /* - * Set what local port to bind the socket to when performing an operation. - */ - data->set.localport = curlx_sltous(va_arg(param, long)); - break; - case CURLOPT_LOCALPORTRANGE: - /* - * Set number of local ports to try, starting with CURLOPT_LOCALPORT. - */ - data->set.localportrange = curlx_sltosi(va_arg(param, long)); - break; - case CURLOPT_KRBLEVEL: - /* - * A string that defines the kerberos security level. - */ - result = setstropt(&data->set.str[STRING_KRB_LEVEL], - va_arg(param, char *)); - data->set.krb = (NULL != data->set.str[STRING_KRB_LEVEL])?TRUE:FALSE; - break; - case CURLOPT_GSSAPI_DELEGATION: - /* - * GSSAPI credential delegation - */ - data->set.gssapi_delegation = va_arg(param, long); - break; - case CURLOPT_SSL_VERIFYPEER: - /* - * Enable peer SSL verifying. - */ - data->set.ssl.verifypeer = (0 != va_arg(param, long))?TRUE:FALSE; - break; - case CURLOPT_SSL_VERIFYHOST: - /* - * Enable verification of the host name in the peer certificate - */ - arg = va_arg(param, long); - - /* Obviously people are not reading documentation and too many thought - this argument took a boolean when it wasn't and misused it. We thus ban - 1 as a sensible input and we warn about its use. Then we only have the - 2 action internally stored as TRUE. */ - - if(1 == arg) { - failf(data, "CURLOPT_SSL_VERIFYHOST no longer supports 1 as value!"); - return CURLE_BAD_FUNCTION_ARGUMENT; - } - - data->set.ssl.verifyhost = (0 != arg)?TRUE:FALSE; - break; -#ifdef USE_SSLEAY - /* since these two options are only possible to use on an OpenSSL- - powered libcurl we #ifdef them on this condition so that libcurls - built against other SSL libs will return a proper error when trying - to set this option! */ - case CURLOPT_SSL_CTX_FUNCTION: - /* - * Set a SSL_CTX callback - */ - data->set.ssl.fsslctx = va_arg(param, curl_ssl_ctx_callback); - break; - case CURLOPT_SSL_CTX_DATA: - /* - * Set a SSL_CTX callback parameter pointer - */ - data->set.ssl.fsslctxp = va_arg(param, void *); - break; -#endif -#if defined(USE_SSLEAY) || defined(USE_QSOSSL) || defined(USE_GSKIT) - case CURLOPT_CERTINFO: - data->set.ssl.certinfo = (0 != va_arg(param, long))?TRUE:FALSE; - break; -#endif - case CURLOPT_CAINFO: - /* - * Set CA info for SSL connection. Specify file name of the CA certificate - */ - result = setstropt(&data->set.str[STRING_SSL_CAFILE], - va_arg(param, char *)); - break; - case CURLOPT_CAPATH: - /* - * Set CA path info for SSL connection. Specify directory name of the CA - * certificates which have been prepared using openssl c_rehash utility. - */ - /* This does not work on windows. */ - result = setstropt(&data->set.str[STRING_SSL_CAPATH], - va_arg(param, char *)); - break; - case CURLOPT_CRLFILE: - /* - * Set CRL file info for SSL connection. Specify file name of the CRL - * to check certificates revocation - */ - result = setstropt(&data->set.str[STRING_SSL_CRLFILE], - va_arg(param, char *)); - break; - case CURLOPT_ISSUERCERT: - /* - * Set Issuer certificate file - * to check certificates issuer - */ - result = setstropt(&data->set.str[STRING_SSL_ISSUERCERT], - va_arg(param, char *)); - break; - case CURLOPT_TELNETOPTIONS: - /* - * Set a linked list of telnet options - */ - data->set.telnet_options = va_arg(param, struct curl_slist *); - break; - - case CURLOPT_BUFFERSIZE: - /* - * The application kindly asks for a differently sized receive buffer. - * If it seems reasonable, we'll use it. - */ - data->set.buffer_size = va_arg(param, long); - - if((data->set.buffer_size> (BUFSIZE -1 )) || - (data->set.buffer_size < 1)) - data->set.buffer_size = 0; /* huge internal default */ - - break; - - case CURLOPT_NOSIGNAL: - /* - * The application asks not to set any signal() or alarm() handlers, - * even when using a timeout. - */ - data->set.no_signal = (0 != va_arg(param, long))?TRUE:FALSE; - break; - - case CURLOPT_SHARE: - { - struct Curl_share *set; - set = va_arg(param, struct Curl_share *); - - /* disconnect from old share, if any */ - if(data->share) { - Curl_share_lock(data, CURL_LOCK_DATA_SHARE, CURL_LOCK_ACCESS_SINGLE); - - if(data->dns.hostcachetype == HCACHE_SHARED) { - data->dns.hostcache = NULL; - data->dns.hostcachetype = HCACHE_NONE; - } - -#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES) - if(data->share->cookies == data->cookies) - data->cookies = NULL; -#endif - - if(data->share->sslsession == data->state.session) - data->state.session = NULL; - - data->share->dirty--; - - Curl_share_unlock(data, CURL_LOCK_DATA_SHARE); - data->share = NULL; - } - - /* use new share if it set */ - data->share = set; - if(data->share) { - - Curl_share_lock(data, CURL_LOCK_DATA_SHARE, CURL_LOCK_ACCESS_SINGLE); - - data->share->dirty++; - - if(data->share->hostcache) { - /* use shared host cache */ - data->dns.hostcache = data->share->hostcache; - data->dns.hostcachetype = HCACHE_SHARED; - } -#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES) - if(data->share->cookies) { - /* use shared cookie list, first free own one if any */ - if(data->cookies) - Curl_cookie_cleanup(data->cookies); - /* enable cookies since we now use a share that uses cookies! */ - data->cookies = data->share->cookies; - } -#endif /* CURL_DISABLE_HTTP */ - if(data->share->sslsession) { - data->set.ssl.max_ssl_sessions = data->share->max_ssl_sessions; - data->state.session = data->share->sslsession; - } - Curl_share_unlock(data, CURL_LOCK_DATA_SHARE); - - } - /* check for host cache not needed, - * it will be done by curl_easy_perform */ + struct postponed_data * const psnd = &(conn->postponed[num]); + if(psnd->buffer) { + DEBUGASSERT(psnd->allocated_size > 0); + DEBUGASSERT(psnd->recv_size <= psnd->allocated_size); + DEBUGASSERT(psnd->recv_size ? + (psnd->recv_processed < psnd->recv_size) : + (psnd->recv_processed == 0)); + DEBUGASSERT(psnd->bindsock != CURL_SOCKET_BAD); + free(psnd->buffer); + psnd->buffer = NULL; + psnd->allocated_size = 0; + psnd->recv_size = 0; + psnd->recv_processed = 0; +#ifdef DEBUGBUILD + psnd->bindsock = CURL_SOCKET_BAD; /* used only for DEBUGASSERT */ +#endif /* DEBUGBUILD */ } - break; - - case CURLOPT_PRIVATE: - /* - * Set private data pointer. - */ - data->set.private_data = va_arg(param, void *); - break; - - case CURLOPT_MAXFILESIZE: - /* - * Set the maximum size of a file to download. - */ - data->set.max_filesize = va_arg(param, long); - break; - -#ifdef USE_SSL - case CURLOPT_USE_SSL: - /* - * Make transfers attempt to use SSL/TLS. - */ - data->set.use_ssl = (curl_usessl)va_arg(param, long); - break; - - case CURLOPT_SSL_OPTIONS: - arg = va_arg(param, long); - data->set.ssl_enable_beast = arg&CURLSSLOPT_ALLOW_BEAST?TRUE:FALSE; - break; - -#endif - case CURLOPT_FTPSSLAUTH: - /* - * Set a specific auth for FTP-SSL transfers. - */ - data->set.ftpsslauth = (curl_ftpauth)va_arg(param, long); - break; - - case CURLOPT_IPRESOLVE: - data->set.ipver = va_arg(param, long); - break; - - case CURLOPT_MAXFILESIZE_LARGE: - /* - * Set the maximum size of a file to download. - */ - data->set.max_filesize = va_arg(param, curl_off_t); - break; - - case CURLOPT_TCP_NODELAY: - /* - * Enable or disable TCP_NODELAY, which will disable/enable the Nagle - * algorithm - */ - data->set.tcp_nodelay = (0 != va_arg(param, long))?TRUE:FALSE; - break; - - case CURLOPT_FTP_ACCOUNT: - result = setstropt(&data->set.str[STRING_FTP_ACCOUNT], - va_arg(param, char *)); - break; - - case CURLOPT_IGNORE_CONTENT_LENGTH: - data->set.ignorecl = (0 != va_arg(param, long))?TRUE:FALSE; - break; - - case CURLOPT_CONNECT_ONLY: - /* - * No data transfer, set up connection and let application use the socket - */ - data->set.connect_only = (0 != va_arg(param, long))?TRUE:FALSE; - break; - - case CURLOPT_FTP_ALTERNATIVE_TO_USER: - result = setstropt(&data->set.str[STRING_FTP_ALTERNATIVE_TO_USER], - va_arg(param, char *)); - break; - - case CURLOPT_SOCKOPTFUNCTION: - /* - * socket callback function: called after socket() but before connect() - */ - data->set.fsockopt = va_arg(param, curl_sockopt_callback); - break; - - case CURLOPT_SOCKOPTDATA: - /* - * socket callback data pointer. Might be NULL. - */ - data->set.sockopt_client = va_arg(param, void *); - break; - - case CURLOPT_OPENSOCKETFUNCTION: - /* - * open/create socket callback function: called instead of socket(), - * before connect() - */ - data->set.fopensocket = va_arg(param, curl_opensocket_callback); - break; - - case CURLOPT_OPENSOCKETDATA: - /* - * socket callback data pointer. Might be NULL. - */ - data->set.opensocket_client = va_arg(param, void *); - break; - - case CURLOPT_CLOSESOCKETFUNCTION: - /* - * close socket callback function: called instead of close() - * when shutting down a connection - */ - data->set.fclosesocket = va_arg(param, curl_closesocket_callback); - break; - - case CURLOPT_CLOSESOCKETDATA: - /* - * socket callback data pointer. Might be NULL. - */ - data->set.closesocket_client = va_arg(param, void *); - break; - - case CURLOPT_SSL_SESSIONID_CACHE: - data->set.ssl.sessionid = (0 != va_arg(param, long))?TRUE:FALSE; - break; - -#ifdef USE_LIBSSH2 - /* we only include SSH options if explicitly built to support SSH */ - case CURLOPT_SSH_AUTH_TYPES: - data->set.ssh_auth_types = va_arg(param, long); - break; - - case CURLOPT_SSH_PUBLIC_KEYFILE: - /* - * Use this file instead of the $HOME/.ssh/id_dsa.pub file - */ - result = setstropt(&data->set.str[STRING_SSH_PUBLIC_KEY], - va_arg(param, char *)); - break; - - case CURLOPT_SSH_PRIVATE_KEYFILE: - /* - * Use this file instead of the $HOME/.ssh/id_dsa file - */ - result = setstropt(&data->set.str[STRING_SSH_PRIVATE_KEY], - va_arg(param, char *)); - break; - case CURLOPT_SSH_HOST_PUBLIC_KEY_MD5: - /* - * Option to allow for the MD5 of the host public key to be checked - * for validation purposes. - */ - result = setstropt(&data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5], - va_arg(param, char *)); - break; -#ifdef HAVE_LIBSSH2_KNOWNHOST_API - case CURLOPT_SSH_KNOWNHOSTS: - /* - * Store the file name to read known hosts from. - */ - result = setstropt(&data->set.str[STRING_SSH_KNOWNHOSTS], - va_arg(param, char *)); - break; - - case CURLOPT_SSH_KEYFUNCTION: - /* setting to NULL is fine since the ssh.c functions themselves will - then rever to use the internal default */ - data->set.ssh_keyfunc = va_arg(param, curl_sshkeycallback); - break; - - case CURLOPT_SSH_KEYDATA: - /* - * Custom client data to pass to the SSH keyfunc callback - */ - data->set.ssh_keyfunc_userp = va_arg(param, void *); - break; -#endif /* HAVE_LIBSSH2_KNOWNHOST_API */ - -#endif /* USE_LIBSSH2 */ - - case CURLOPT_HTTP_TRANSFER_DECODING: - /* - * disable libcurl transfer encoding is used - */ - data->set.http_te_skip = (0 == va_arg(param, long))?TRUE:FALSE; - break; - - case CURLOPT_HTTP_CONTENT_DECODING: - /* - * raw data passed to the application when content encoding is used - */ - data->set.http_ce_skip = (0 == va_arg(param, long))?TRUE:FALSE; - break; - - case CURLOPT_NEW_FILE_PERMS: - /* - * Uses these permissions instead of 0644 - */ - data->set.new_file_perms = va_arg(param, long); - break; - - case CURLOPT_NEW_DIRECTORY_PERMS: - /* - * Uses these permissions instead of 0755 - */ - data->set.new_directory_perms = va_arg(param, long); - break; - - case CURLOPT_ADDRESS_SCOPE: - /* - * We always get longs when passed plain numericals, but for this value we - * know that an unsigned int will always hold the value so we blindly - * typecast to this type - */ - data->set.scope = curlx_sltoui(va_arg(param, long)); - break; - - case CURLOPT_PROTOCOLS: - /* set the bitmask for the protocols that are allowed to be used for the - transfer, which thus helps the app which takes URLs from users or other - external inputs and want to restrict what protocol(s) to deal - with. Defaults to CURLPROTO_ALL. */ - data->set.allowed_protocols = va_arg(param, long); - break; - - case CURLOPT_REDIR_PROTOCOLS: - /* set the bitmask for the protocols that libcurl is allowed to follow to, - as a subset of the CURLOPT_PROTOCOLS ones. That means the protocol needs - to be set in both bitmasks to be allowed to get redirected to. Defaults - to all protocols except FILE and SCP. */ - data->set.redir_protocols = va_arg(param, long); - break; - - case CURLOPT_MAIL_FROM: - /* Set the SMTP mail originator */ - result = setstropt(&data->set.str[STRING_MAIL_FROM], - va_arg(param, char *)); - break; - - case CURLOPT_MAIL_AUTH: - /* Set the SMTP auth originator */ - result = setstropt(&data->set.str[STRING_MAIL_AUTH], - va_arg(param, char *)); - break; - - case CURLOPT_MAIL_RCPT: - /* Set the list of mail recipients */ - data->set.mail_rcpt = va_arg(param, struct curl_slist *); - break; - - case CURLOPT_SASL_IR: - /* Enable/disable SASL initial response */ - data->set.sasl_ir = (0 != va_arg(param, long)) ? TRUE : FALSE; - break; - - case CURLOPT_RTSP_REQUEST: - { - /* - * Set the RTSP request method (OPTIONS, SETUP, PLAY, etc...) - * Would this be better if the RTSPREQ_* were just moved into here? - */ - long curl_rtspreq = va_arg(param, long); - Curl_RtspReq rtspreq = RTSPREQ_NONE; - switch(curl_rtspreq) { - case CURL_RTSPREQ_OPTIONS: - rtspreq = RTSPREQ_OPTIONS; - break; - - case CURL_RTSPREQ_DESCRIBE: - rtspreq = RTSPREQ_DESCRIBE; - break; - - case CURL_RTSPREQ_ANNOUNCE: - rtspreq = RTSPREQ_ANNOUNCE; - break; - - case CURL_RTSPREQ_SETUP: - rtspreq = RTSPREQ_SETUP; - break; - - case CURL_RTSPREQ_PLAY: - rtspreq = RTSPREQ_PLAY; - break; - - case CURL_RTSPREQ_PAUSE: - rtspreq = RTSPREQ_PAUSE; - break; - - case CURL_RTSPREQ_TEARDOWN: - rtspreq = RTSPREQ_TEARDOWN; - break; - - case CURL_RTSPREQ_GET_PARAMETER: - rtspreq = RTSPREQ_GET_PARAMETER; - break; - - case CURL_RTSPREQ_SET_PARAMETER: - rtspreq = RTSPREQ_SET_PARAMETER; - break; - - case CURL_RTSPREQ_RECORD: - rtspreq = RTSPREQ_RECORD; - break; - - case CURL_RTSPREQ_RECEIVE: - rtspreq = RTSPREQ_RECEIVE; - break; - default: - rtspreq = RTSPREQ_NONE; - } - - data->set.rtspreq = rtspreq; - break; - } - - - case CURLOPT_RTSP_SESSION_ID: - /* - * Set the RTSP Session ID manually. Useful if the application is - * resuming a previously established RTSP session - */ - result = setstropt(&data->set.str[STRING_RTSP_SESSION_ID], - va_arg(param, char *)); - break; - - case CURLOPT_RTSP_STREAM_URI: - /* - * Set the Stream URI for the RTSP request. Unless the request is - * for generic server options, the application will need to set this. - */ - result = setstropt(&data->set.str[STRING_RTSP_STREAM_URI], - va_arg(param, char *)); - break; - - case CURLOPT_RTSP_TRANSPORT: - /* - * The content of the Transport: header for the RTSP request - */ - result = setstropt(&data->set.str[STRING_RTSP_TRANSPORT], - va_arg(param, char *)); - break; - - case CURLOPT_RTSP_CLIENT_CSEQ: - /* - * Set the CSEQ number to issue for the next RTSP request. Useful if the - * application is resuming a previously broken connection. The CSEQ - * will increment from this new number henceforth. - */ - data->state.rtsp_next_client_CSeq = va_arg(param, long); - break; - - case CURLOPT_RTSP_SERVER_CSEQ: - /* Same as the above, but for server-initiated requests */ - data->state.rtsp_next_client_CSeq = va_arg(param, long); - break; - - case CURLOPT_INTERLEAVEDATA: - data->set.rtp_out = va_arg(param, void *); - break; - case CURLOPT_INTERLEAVEFUNCTION: - /* Set the user defined RTP write function */ - data->set.fwrite_rtp = va_arg(param, curl_write_callback); - break; - - case CURLOPT_WILDCARDMATCH: - data->set.wildcardmatch = (0 != va_arg(param, long))?TRUE:FALSE; - break; - case CURLOPT_CHUNK_BGN_FUNCTION: - data->set.chunk_bgn = va_arg(param, curl_chunk_bgn_callback); - break; - case CURLOPT_CHUNK_END_FUNCTION: - data->set.chunk_end = va_arg(param, curl_chunk_end_callback); - break; - case CURLOPT_FNMATCH_FUNCTION: - data->set.fnmatch = va_arg(param, curl_fnmatch_callback); - break; - case CURLOPT_CHUNK_DATA: - data->wildcard.customptr = va_arg(param, void *); - break; - case CURLOPT_FNMATCH_DATA: - data->set.fnmatch_data = va_arg(param, void *); - break; -#ifdef USE_TLS_SRP - case CURLOPT_TLSAUTH_USERNAME: - result = setstropt(&data->set.str[STRING_TLSAUTH_USERNAME], - va_arg(param, char *)); - if(data->set.str[STRING_TLSAUTH_USERNAME] && !data->set.ssl.authtype) - data->set.ssl.authtype = CURL_TLSAUTH_SRP; /* default to SRP */ - break; - case CURLOPT_TLSAUTH_PASSWORD: - result = setstropt(&data->set.str[STRING_TLSAUTH_PASSWORD], - va_arg(param, char *)); - if(data->set.str[STRING_TLSAUTH_USERNAME] && !data->set.ssl.authtype) - data->set.ssl.authtype = CURL_TLSAUTH_SRP; /* default to SRP */ - break; - case CURLOPT_TLSAUTH_TYPE: - if(strnequal((char *)va_arg(param, char *), "SRP", strlen("SRP"))) - data->set.ssl.authtype = CURL_TLSAUTH_SRP; - else - data->set.ssl.authtype = CURL_TLSAUTH_NONE; - break; -#endif - case CURLOPT_DNS_SERVERS: - result = Curl_set_dns_servers(data, va_arg(param, char *)); - break; - case CURLOPT_DNS_INTERFACE: - result = Curl_set_dns_interface(data, va_arg(param, char *)); - break; - case CURLOPT_DNS_LOCAL_IP4: - result = Curl_set_dns_local_ip4(data, va_arg(param, char *)); - break; - case CURLOPT_DNS_LOCAL_IP6: - result = Curl_set_dns_local_ip6(data, va_arg(param, char *)); - break; - - case CURLOPT_TCP_KEEPALIVE: - data->set.tcp_keepalive = (0 != va_arg(param, long))?TRUE:FALSE; - break; - case CURLOPT_TCP_KEEPIDLE: - data->set.tcp_keepidle = va_arg(param, long); - break; - case CURLOPT_TCP_KEEPINTVL: - data->set.tcp_keepintvl = va_arg(param, long); - break; - - default: - /* unknown tag and its companion, just ignore: */ - result = CURLE_UNKNOWN_OPTION; - break; + else { + DEBUGASSERT(psnd->allocated_size == 0); + DEBUGASSERT(psnd->recv_size == 0); + DEBUGASSERT(psnd->recv_processed == 0); + DEBUGASSERT(psnd->bindsock == CURL_SOCKET_BAD); } +} - return result; +static void conn_reset_all_postponed_data(struct connectdata *conn) +{ + conn_reset_postponed_data(conn, 0); + conn_reset_postponed_data(conn, 1); } +#else /* ! USE_RECV_BEFORE_SEND_WORKAROUND */ +/* Use "do-nothing" macro instead of function when workaround not used */ +#define conn_reset_all_postponed_data(c) do {} WHILE_FALSE +#endif /* ! USE_RECV_BEFORE_SEND_WORKAROUND */ static void conn_free(struct connectdata *conn) { @@ -2502,17 +666,24 @@ static void conn_free(struct connectdata *conn) Curl_closesocket(conn, conn->sock[SECONDARYSOCKET]); if(CURL_SOCKET_BAD != conn->sock[FIRSTSOCKET]) Curl_closesocket(conn, conn->sock[FIRSTSOCKET]); + if(CURL_SOCKET_BAD != conn->tempsock[0]) + Curl_closesocket(conn, conn->tempsock[0]); + if(CURL_SOCKET_BAD != conn->tempsock[1]) + Curl_closesocket(conn, conn->tempsock[1]); -#if defined(USE_NTLM) && defined(NTLM_WB_ENABLED) +#if !defined(CURL_DISABLE_HTTP) && defined(USE_NTLM) && \ + defined(NTLM_WB_ENABLED) Curl_ntlm_wb_cleanup(conn); #endif Curl_safefree(conn->user); Curl_safefree(conn->passwd); - Curl_safefree(conn->xoauth2_bearer); + Curl_safefree(conn->oauth_bearer); Curl_safefree(conn->options); - Curl_safefree(conn->proxyuser); - Curl_safefree(conn->proxypasswd); + Curl_safefree(conn->http_proxy.user); + Curl_safefree(conn->socks_proxy.user); + Curl_safefree(conn->http_proxy.passwd); + Curl_safefree(conn->socks_proxy.passwd); Curl_safefree(conn->allocptr.proxyuserpwd); Curl_safefree(conn->allocptr.uagent); Curl_safefree(conn->allocptr.userpwd); @@ -2525,24 +696,42 @@ static void conn_free(struct connectdata *conn) Curl_safefree(conn->allocptr.rtsp_transport); Curl_safefree(conn->trailer); Curl_safefree(conn->host.rawalloc); /* host name buffer */ - Curl_safefree(conn->proxy.rawalloc); /* proxy name buffer */ + Curl_safefree(conn->conn_to_host.rawalloc); /* host name buffer */ + Curl_safefree(conn->secondaryhostname); + Curl_safefree(conn->http_proxy.host.rawalloc); /* http proxy name buffer */ + Curl_safefree(conn->socks_proxy.host.rawalloc); /* socks proxy name buffer */ Curl_safefree(conn->master_buffer); + Curl_safefree(conn->connect_state); - Curl_llist_destroy(conn->send_pipe, NULL); - Curl_llist_destroy(conn->recv_pipe, NULL); + conn_reset_all_postponed_data(conn); - conn->send_pipe = NULL; - conn->recv_pipe = NULL; + Curl_llist_destroy(&conn->send_pipe, NULL); + Curl_llist_destroy(&conn->recv_pipe, NULL); Curl_safefree(conn->localdev); - Curl_free_ssl_config(&conn->ssl_config); + Curl_free_primary_ssl_config(&conn->ssl_config); + Curl_free_primary_ssl_config(&conn->proxy_ssl_config); + +#ifdef USE_UNIX_SOCKETS + Curl_safefree(conn->unix_domain_socket); +#endif free(conn); /* free all the connection oriented data */ } +/* + * Disconnects the given connection. Note the connection may not be the + * primary connection, like when freeing room in the connection cache or + * killing of a dead old connection. + * + * This function MUST NOT reset state in the Curl_easy struct if that + * isn't strictly bound to the life-time of *this* particular connection. + * + */ + CURLcode Curl_disconnect(struct connectdata *conn, bool dead_connection) { - struct SessionHandle *data; + struct Curl_easy *data; if(!conn) return CURLE_OK; /* this is closed and fine already */ data = conn->data; @@ -2552,6 +741,17 @@ CURLcode Curl_disconnect(struct connectdata *conn, bool dead_connection) return CURLE_OK; } + /* + * If this connection isn't marked to force-close, leave it open if there + * are other users of it + */ + if(!conn->bits.close && + (conn->send_pipe.size + conn->recv_pipe.size)) { + DEBUGF(infof(data, "Curl_disconnect, usecounter: %d\n", + conn->send_pipe.size + conn->recv_pipe.size)); + return CURLE_OK; + } + if(conn->dns_entry != NULL) { Curl_resolv_unlock(data, conn->dns_entry); conn->dns_entry = NULL; @@ -2559,38 +759,10 @@ CURLcode Curl_disconnect(struct connectdata *conn, bool dead_connection) Curl_hostcache_prune(data); /* kill old DNS cache entries */ - { - int has_host_ntlm = (conn->ntlm.state != NTLMSTATE_NONE); - int has_proxy_ntlm = (conn->proxyntlm.state != NTLMSTATE_NONE); - - /* Authentication data is a mix of connection-related and sessionhandle- - related stuff. NTLM is connection-related so when we close the shop - we shall forget. */ - - if(has_host_ntlm) { - data->state.authhost.done = FALSE; - data->state.authhost.picked = - data->state.authhost.want; - } - - if(has_proxy_ntlm) { - data->state.authproxy.done = FALSE; - data->state.authproxy.picked = - data->state.authproxy.want; - } - - if(has_host_ntlm || has_proxy_ntlm) - data->state.authproblem = FALSE; - } - +#if !defined(CURL_DISABLE_HTTP) && defined(USE_NTLM) /* Cleanup NTLM connection-related data */ Curl_http_ntlm_cleanup(conn); - - /* Cleanup possible redirect junk */ - if(data->req.newurl) { - free(data->req.newurl); - data->req.newurl = NULL; - } +#endif if(conn->handler->disconnect) /* This is set if protocol-specific cleanups should be made */ @@ -2600,37 +772,21 @@ CURLcode Curl_disconnect(struct connectdata *conn, bool dead_connection) infof(data, "Closing connection %ld\n", conn->connection_id); Curl_conncache_remove_conn(data->state.conn_cache, conn); -#if defined(USE_LIBIDN) - if(conn->host.encalloc) - idn_free(conn->host.encalloc); /* encoded host name buffer, must be freed - with idn_free() since this was allocated - by libidn */ - if(conn->proxy.encalloc) - idn_free(conn->proxy.encalloc); /* encoded proxy name buffer, must be - freed with idn_free() since this was - allocated by libidn */ -#elif defined(USE_WIN32_IDN) - free(conn->host.encalloc); /* encoded host name buffer, must be freed with - idn_free() since this was allocated by - curl_win32_idn_to_ascii */ - if(conn->proxy.encalloc) - free(conn->proxy.encalloc); /* encoded proxy name buffer, must be freed - with idn_free() since this was allocated by - curl_win32_idn_to_ascii */ -#endif + free_fixed_hostname(&conn->host); + free_fixed_hostname(&conn->conn_to_host); + free_fixed_hostname(&conn->http_proxy.host); + free_fixed_hostname(&conn->socks_proxy.host); Curl_ssl_close(conn, FIRSTSOCKET); /* Indicate to all handles on the pipe that we're dead */ - if(Curl_multi_pipeline_enabled(data->multi)) { - signalPipeClose(conn->send_pipe, TRUE); - signalPipeClose(conn->recv_pipe, TRUE); + if(Curl_pipeline_wanted(data->multi, CURLPIPE_ANY)) { + signalPipeClose(&conn->send_pipe, TRUE); + signalPipeClose(&conn->recv_pipe, TRUE); } conn_free(conn); - Curl_speedinit(data); - return CURLE_OK; } @@ -2644,7 +800,7 @@ static bool SocketIsDead(curl_socket_t sock) int sval; bool ret_val = TRUE; - sval = Curl_socket_ready(sock, CURL_SOCKET_BAD, 0); + sval = SOCKET_READABLE(sock, 0); if(sval == 0) /* timeout */ ret_val = FALSE; @@ -2652,45 +808,50 @@ static bool SocketIsDead(curl_socket_t sock) return ret_val; } -static bool IsPipeliningPossible(const struct SessionHandle *handle, - const struct connectdata *conn) -{ - if((conn->handler->protocol & CURLPROTO_HTTP) && - Curl_multi_pipeline_enabled(handle->multi) && - (handle->set.httpreq == HTTPREQ_GET || - handle->set.httpreq == HTTPREQ_HEAD) && - handle->set.httpversion != CURL_HTTP_VERSION_1_0) - return TRUE; - - return FALSE; -} - -bool Curl_isPipeliningEnabled(const struct SessionHandle *handle) -{ - return Curl_multi_pipeline_enabled(handle->multi); -} - -CURLcode Curl_addHandleToPipeline(struct SessionHandle *data, - struct curl_llist *pipeline) +/* + * IsPipeliningPossible() + * + * Return a bitmask with the available pipelining and multiplexing options for + * the given requested connection. + */ +static int IsPipeliningPossible(const struct Curl_easy *handle, + const struct connectdata *conn) { - if(!Curl_llist_insert_next(pipeline, pipeline->tail, data)) - return CURLE_OUT_OF_MEMORY; - infof(data, "Curl_addHandleToPipeline: length: %d\n", pipeline->size); - return CURLE_OK; + int avail = 0; + + /* If a HTTP protocol and pipelining is enabled */ + if((conn->handler->protocol & PROTO_FAMILY_HTTP) && + (!conn->bits.protoconnstart || !conn->bits.close)) { + + if(Curl_pipeline_wanted(handle->multi, CURLPIPE_HTTP1) && + (handle->set.httpversion != CURL_HTTP_VERSION_1_0) && + (handle->set.httpreq == HTTPREQ_GET || + handle->set.httpreq == HTTPREQ_HEAD)) + /* didn't ask for HTTP/1.0 and a GET or HEAD */ + avail |= CURLPIPE_HTTP1; + + if(Curl_pipeline_wanted(handle->multi, CURLPIPE_MULTIPLEX) && + (handle->set.httpversion >= CURL_HTTP_VERSION_2)) + /* allows HTTP/2 */ + avail |= CURLPIPE_MULTIPLEX; + } + return avail; } -int Curl_removeHandleFromPipeline(struct SessionHandle *handle, +int Curl_removeHandleFromPipeline(struct Curl_easy *handle, struct curl_llist *pipeline) { - struct curl_llist_element *curr; + if(pipeline) { + struct curl_llist_element *curr; - curr = pipeline->head; - while(curr) { - if(curr->ptr == handle) { - Curl_llist_remove(pipeline, curr, NULL); - return 1; /* we removed a handle */ + curr = pipeline->head; + while(curr) { + if(curr->ptr == handle) { + Curl_llist_remove(pipeline, curr, NULL); + return 1; /* we removed a handle */ + } + curr = curr->next; } - curr = curr->next; } return 0; @@ -2703,18 +864,18 @@ static void Curl_printPipeline(struct curl_llist *pipeline) curr = pipeline->head; while(curr) { - struct SessionHandle *data = (struct SessionHandle *) curr->ptr; + struct Curl_easy *data = (struct Curl_easy *) curr->ptr; infof(data, "Handle in pipeline: %s\n", data->state.path); curr = curr->next; } } #endif -static struct SessionHandle* gethandleathead(struct curl_llist *pipeline) +static struct Curl_easy* gethandleathead(struct curl_llist *pipeline) { struct curl_llist_element *curr = pipeline->head; if(curr) { - return (struct SessionHandle *) curr->ptr; + return (struct Curl_easy *) curr->ptr; } return NULL; @@ -2722,19 +883,18 @@ static struct SessionHandle* gethandleathead(struct curl_llist *pipeline) /* remove the specified connection from all (possible) pipelines and related queues */ -void Curl_getoff_all_pipelines(struct SessionHandle *data, +void Curl_getoff_all_pipelines(struct Curl_easy *data, struct connectdata *conn) { bool recv_head = (conn->readchannel_inuse && - (gethandleathead(conn->recv_pipe) == data)) ? TRUE : FALSE; - + Curl_recvpipe_head(data, conn)); bool send_head = (conn->writechannel_inuse && - (gethandleathead(conn->send_pipe) == data)) ? TRUE : FALSE; + Curl_sendpipe_head(data, conn)); - if(Curl_removeHandleFromPipeline(data, conn->recv_pipe) && recv_head) - conn->readchannel_inuse = FALSE; - if(Curl_removeHandleFromPipeline(data, conn->send_pipe) && send_head) - conn->writechannel_inuse = FALSE; + if(Curl_removeHandleFromPipeline(data, &conn->recv_pipe) && recv_head) + Curl_pipeline_leave_read(conn); + if(Curl_removeHandleFromPipeline(data, &conn->send_pipe) && send_head) + Curl_pipeline_leave_write(conn); } static void signalPipeClose(struct curl_llist *pipeline, bool pipe_broke) @@ -2747,7 +907,7 @@ static void signalPipeClose(struct curl_llist *pipeline, bool pipe_broke) curr = pipeline->head; while(curr) { struct curl_llist_element *next = curr->next; - struct SessionHandle *data = (struct SessionHandle *) curr->ptr; + struct Curl_easy *data = (struct Curl_easy *) curr->ptr; #ifdef DEBUGBUILD /* debug-only code */ if(data->magic != CURLEASY_MAGIC_NUMBER) { @@ -2764,58 +924,19 @@ static void signalPipeClose(struct curl_llist *pipeline, bool pipe_broke) } } -/* - * This function finds the connection in the connection - * cache that has been unused for the longest time. - * - * Returns the pointer to the oldest idle connection, or NULL if none was - * found. - */ -static struct connectdata * -find_oldest_idle_connection(struct SessionHandle *data) +static bool +proxy_info_matches(const struct proxy_info* data, + const struct proxy_info* needle) { - struct conncache *bc = data->state.conn_cache; - struct curl_hash_iterator iter; - struct curl_llist_element *curr; - struct curl_hash_element *he; - long highscore=-1; - long score; - struct timeval now; - struct connectdata *conn_candidate = NULL; - struct connectbundle *bundle; - - now = Curl_tvnow(); - - Curl_hash_start_iterate(bc->hash, &iter); - - he = Curl_hash_next_element(&iter); - while(he) { - struct connectdata *conn; - - bundle = he->ptr; - - curr = bundle->conn_list->head; - while(curr) { - conn = curr->ptr; - - if(!conn->inuse) { - /* Set higher score for the age passed since the connection was used */ - score = Curl_tvdiff(now, conn->now); - - if(score > highscore) { - highscore = score; - conn_candidate = conn; - } - } - curr = curr->next; - } - - he = Curl_hash_next_element(&iter); - } + if((data->proxytype == needle->proxytype) && + (data->port == needle->port) && + Curl_safe_strcasecompare(data->host.name, needle->host.name)) + return TRUE; - return conn_candidate; + return FALSE; } + /* * This function finds the connection in the connection * bundle that has been unused for the longest time. @@ -2824,27 +945,27 @@ find_oldest_idle_connection(struct SessionHandle *data) * found. */ static struct connectdata * -find_oldest_idle_connection_in_bundle(struct SessionHandle *data, +find_oldest_idle_connection_in_bundle(struct Curl_easy *data, struct connectbundle *bundle) { struct curl_llist_element *curr; - long highscore=-1; - long score; - struct timeval now; + timediff_t highscore = -1; + timediff_t score; + struct curltime now; struct connectdata *conn_candidate = NULL; struct connectdata *conn; (void)data; - now = Curl_tvnow(); + now = Curl_now(); - curr = bundle->conn_list->head; + curr = bundle->conn_list.head; while(curr) { conn = curr->ptr; if(!conn->inuse) { /* Set higher score for the age passed since the connection was used */ - score = Curl_tvdiff(now, conn->now); + score = Curl_timediff(now, conn->now); if(score > highscore) { highscore = score; @@ -2858,6 +979,84 @@ find_oldest_idle_connection_in_bundle(struct SessionHandle *data, } /* + * This function checks if given connection is dead and disconnects if so. + * (That also removes it from the connection cache.) + * + * Returns TRUE if the connection actually was dead and disconnected. + */ +static bool disconnect_if_dead(struct connectdata *conn, + struct Curl_easy *data) +{ + size_t pipeLen = conn->send_pipe.size + conn->recv_pipe.size; + if(!pipeLen && !conn->inuse) { + /* The check for a dead socket makes sense only if there are no + handles in pipeline and the connection isn't already marked in + use */ + bool dead; + + if(conn->handler->connection_check) { + /* The protocol has a special method for checking the state of the + connection. Use it to check if the connection is dead. */ + unsigned int state; + + state = conn->handler->connection_check(conn, CONNCHECK_ISDEAD); + dead = (state & CONNRESULT_DEAD); + } + else { + /* Use the general method for determining the death of a connection */ + dead = SocketIsDead(conn->sock[FIRSTSOCKET]); + } + + if(dead) { + conn->data = data; + infof(data, "Connection %ld seems to be dead!\n", conn->connection_id); + + /* disconnect resources */ + Curl_disconnect(conn, /* dead_connection */TRUE); + return TRUE; + } + } + return FALSE; +} + +/* + * Wrapper to use disconnect_if_dead() function in Curl_conncache_foreach() + * + * Returns always 0. + */ +static int call_disconnect_if_dead(struct connectdata *conn, + void *param) +{ + struct Curl_easy* data = (struct Curl_easy*)param; + disconnect_if_dead(conn, data); + return 0; /* continue iteration */ +} + +/* + * This function scans the connection cache for half-open/dead connections, + * closes and removes them. + * The cleanup is done at most once per second. + */ +static void prune_dead_connections(struct Curl_easy *data) +{ + struct curltime now = Curl_now(); + time_t elapsed = Curl_timediff(now, data->state.conn_cache->last_cleanup); + + if(elapsed >= 1000L) { + Curl_conncache_foreach(data, data->state.conn_cache, data, + call_disconnect_if_dead); + data->state.conn_cache->last_cleanup = now; + } +} + + +static size_t max_pipeline_length(struct Curl_multi *multi) +{ + return multi ? multi->max_pipeline_length : 0; +} + + +/* * Given one filled in connection struct (named needle), this function should * detect if there already is one that has all the significant details * exactly the same and thus should be used instead. @@ -2870,91 +1069,113 @@ find_oldest_idle_connection_in_bundle(struct SessionHandle *data, * the pipelining strategy wants to open a new connection instead of reusing. */ static bool -ConnectionExists(struct SessionHandle *data, +ConnectionExists(struct Curl_easy *data, struct connectdata *needle, struct connectdata **usethis, - bool *force_reuse) + bool *force_reuse, + bool *waitpipe) { struct connectdata *check; struct connectdata *chosen = 0; - bool canPipeline = IsPipeliningPossible(data, needle); - bool wantNTLM = (data->state.authhost.want==CURLAUTH_NTLM) || - (data->state.authhost.want==CURLAUTH_NTLM_WB) ? TRUE : FALSE; + bool foundPendingCandidate = FALSE; + int canpipe = IsPipeliningPossible(data, needle); struct connectbundle *bundle; +#ifdef USE_NTLM + bool wantNTLMhttp = ((data->state.authhost.want & + (CURLAUTH_NTLM | CURLAUTH_NTLM_WB)) && + (needle->handler->protocol & PROTO_FAMILY_HTTP)); + bool wantProxyNTLMhttp = (needle->bits.proxy_user_passwd && + ((data->state.authproxy.want & + (CURLAUTH_NTLM | CURLAUTH_NTLM_WB)) && + (needle->handler->protocol & PROTO_FAMILY_HTTP))); +#endif + *force_reuse = FALSE; + *waitpipe = FALSE; - /* We can't pipe if the site is blacklisted */ - if(canPipeline && Curl_pipeline_site_blacklisted(data, needle)) { - canPipeline = FALSE; - } + /* We can't pipeline if the site is blacklisted */ + if((canpipe & CURLPIPE_HTTP1) && + Curl_pipeline_site_blacklisted(data, needle)) + canpipe &= ~ CURLPIPE_HTTP1; /* Look up the bundle with all the connections to this particular host */ - bundle = Curl_conncache_find_bundle(data->state.conn_cache, - needle->host.name); + bundle = Curl_conncache_find_bundle(needle, data->state.conn_cache); if(bundle) { - size_t max_pipe_len = Curl_multi_max_pipeline_length(data->multi); + /* Max pipe length is zero (unlimited) for multiplexed connections */ + size_t max_pipe_len = (bundle->multiuse != BUNDLE_MULTIPLEX)? + max_pipeline_length(data->multi):0; size_t best_pipe_len = max_pipe_len; struct curl_llist_element *curr; - infof(data, "Found bundle for host %s: %p\n", - needle->host.name, (void *)bundle); + infof(data, "Found bundle for host %s: %p [%s]\n", + (needle->bits.conn_to_host ? needle->conn_to_host.name : + needle->host.name), (void *)bundle, + (bundle->multiuse == BUNDLE_PIPELINING ? + "can pipeline" : + (bundle->multiuse == BUNDLE_MULTIPLEX ? + "can multiplex" : "serially"))); + + /* We can't pipeline if we don't know anything about the server */ + if(canpipe) { + if(bundle->multiuse <= BUNDLE_UNKNOWN) { + if((bundle->multiuse == BUNDLE_UNKNOWN) && data->set.pipewait) { + infof(data, "Server doesn't support multi-use yet, wait\n"); + *waitpipe = TRUE; + return FALSE; /* no re-use */ + } - /* We can't pipe if we don't know anything about the server */ - if(canPipeline && !bundle->server_supports_pipelining) { - infof(data, "Server doesn't support pipelining\n"); - canPipeline = FALSE; + infof(data, "Server doesn't support multi-use (yet)\n"); + canpipe = 0; + } + if((bundle->multiuse == BUNDLE_PIPELINING) && + !Curl_pipeline_wanted(data->multi, CURLPIPE_HTTP1)) { + /* not asked for, switch off */ + infof(data, "Could pipeline, but not asked to!\n"); + canpipe = 0; + } + else if((bundle->multiuse == BUNDLE_MULTIPLEX) && + !Curl_pipeline_wanted(data->multi, CURLPIPE_MULTIPLEX)) { + infof(data, "Could multiplex, but not asked to!\n"); + canpipe = 0; + } } - curr = bundle->conn_list->head; + curr = bundle->conn_list.head; while(curr) { bool match = FALSE; - bool credentialsMatch = FALSE; size_t pipeLen; /* - * Note that if we use a HTTP proxy, we check connections to that - * proxy and not to the actual remote server. + * Note that if we use a HTTP proxy in normal mode (no tunneling), we + * check connections to that proxy and not to the actual remote server. */ check = curr->ptr; curr = curr->next; - pipeLen = check->send_pipe->size + check->recv_pipe->size; - - if(!pipeLen && !check->inuse) { - /* The check for a dead socket makes sense only if there are no - handles in pipeline and the connection isn't already marked in - use */ - bool dead; - if(check->handler->protocol & CURLPROTO_RTSP) - /* RTSP is a special case due to RTP interleaving */ - dead = Curl_rtsp_connisdead(check); - else - dead = SocketIsDead(check->sock[FIRSTSOCKET]); + if(disconnect_if_dead(check, data)) + continue; - if(dead) { - check->data = data; - infof(data, "Connection %ld seems to be dead!\n", - check->connection_id); + pipeLen = check->send_pipe.size + check->recv_pipe.size; - /* disconnect resources */ - Curl_disconnect(check, /* dead_connection */ TRUE); + if(canpipe) { + if(check->bits.protoconnstart && check->bits.close) continue; - } - } - if(canPipeline) { - /* Make sure the pipe has only GET requests */ - struct SessionHandle* sh = gethandleathead(check->send_pipe); - struct SessionHandle* rh = gethandleathead(check->recv_pipe); - if(sh) { - if(!IsPipeliningPossible(sh, check)) - continue; - } - else if(rh) { - if(!IsPipeliningPossible(rh, check)) - continue; + if(!check->bits.multiplex) { + /* If not multiplexing, make sure the connection is fine for HTTP/1 + pipelining */ + struct Curl_easy* sh = gethandleathead(&check->send_pipe); + struct Curl_easy* rh = gethandleathead(&check->recv_pipe); + if(sh) { + if(!(IsPipeliningPossible(sh, check) & CURLPIPE_HTTP1)) + continue; + } + else if(rh) { + if(!(IsPipeliningPossible(rh, check) & CURLPIPE_HTTP1)) + continue; + } } } else { @@ -2977,12 +1198,14 @@ ConnectionExists(struct SessionHandle *data, if((check->sock[FIRSTSOCKET] == CURL_SOCKET_BAD) || check->bits.close) { + if(!check->bits.close) + foundPendingCandidate = TRUE; /* Don't pick a connection that hasn't connected yet or that is going to get closed. */ infof(data, "Connection #%ld isn't open enough, can't reuse\n", check->connection_id); #ifdef DEBUGBUILD - if(check->recv_pipe->size > 0) { + if(check->recv_pipe.size > 0) { infof(data, "BAD! Unconnected #%ld has a non-empty recv pipeline!\n", check->connection_id); @@ -2992,24 +1215,73 @@ ConnectionExists(struct SessionHandle *data, } } +#ifdef USE_UNIX_SOCKETS + if(needle->unix_domain_socket) { + if(!check->unix_domain_socket) + continue; + if(strcmp(needle->unix_domain_socket, check->unix_domain_socket)) + continue; + if(needle->abstract_unix_socket != check->abstract_unix_socket) + continue; + } + else if(check->unix_domain_socket) + continue; +#endif + if((needle->handler->flags&PROTOPT_SSL) != (check->handler->flags&PROTOPT_SSL)) /* don't do mixed SSL and non-SSL connections */ - if(!(needle->handler->protocol & check->handler->protocol)) + if(get_protocol_family(check->handler->protocol) != + needle->handler->protocol || !check->tls_upgraded) /* except protocols that have been upgraded via TLS */ continue; - if(needle->handler->flags&PROTOPT_SSL) { - if((data->set.ssl.verifypeer != check->verifypeer) || - (data->set.ssl.verifyhost != check->verifyhost)) - continue; - } + if(needle->bits.httpproxy != check->bits.httpproxy || + needle->bits.socksproxy != check->bits.socksproxy) + continue; - if(needle->bits.proxy != check->bits.proxy) - /* don't do mixed proxy and non-proxy connections */ + if(needle->bits.socksproxy && !proxy_info_matches(&needle->socks_proxy, + &check->socks_proxy)) continue; - if(!canPipeline && check->inuse) + if(needle->bits.conn_to_host != check->bits.conn_to_host) + /* don't mix connections that use the "connect to host" feature and + * connections that don't use this feature */ + continue; + + if(needle->bits.conn_to_port != check->bits.conn_to_port) + /* don't mix connections that use the "connect to port" feature and + * connections that don't use this feature */ + continue; + + if(needle->bits.httpproxy) { + if(!proxy_info_matches(&needle->http_proxy, &check->http_proxy)) + continue; + + if(needle->bits.tunnel_proxy != check->bits.tunnel_proxy) + continue; + + if(needle->http_proxy.proxytype == CURLPROXY_HTTPS) { + /* use https proxy */ + if(needle->handler->flags&PROTOPT_SSL) { + /* use double layer ssl */ + if(!Curl_ssl_config_matches(&needle->proxy_ssl_config, + &check->proxy_ssl_config)) + continue; + if(check->proxy_ssl[FIRSTSOCKET].state != ssl_connection_complete) + continue; + } + else { + if(!Curl_ssl_config_matches(&needle->ssl_config, + &check->ssl_config)) + continue; + if(check->ssl[FIRSTSOCKET].state != ssl_connection_complete) + continue; + } + } + } + + if(!canpipe && check->inuse) /* this request can't be pipelined but the checked connection is already in use so we skip it */ continue; @@ -3028,38 +1300,39 @@ ConnectionExists(struct SessionHandle *data, */ if((check->localport != needle->localport) || (check->localportrange != needle->localportrange) || - !check->localdev || - !needle->localdev || - strcmp(check->localdev, needle->localdev)) + (needle->localdev && + (!check->localdev || strcmp(check->localdev, needle->localdev)))) continue; } - if((needle->handler->protocol & CURLPROTO_FTP) || - ((needle->handler->protocol & CURLPROTO_HTTP) && wantNTLM)) { - /* This is FTP or HTTP+NTLM, verify that we're using the same name - and password as well */ - if(!strequal(needle->user, check->user) || - !strequal(needle->passwd, check->passwd)) { - /* one of them was different */ - continue; - } - credentialsMatch = TRUE; + if(!(needle->handler->flags & PROTOPT_CREDSPERREQUEST)) { + /* This protocol requires credentials per connection, + so verify that we're using the same name and password as well */ + if(strcmp(needle->user, check->user) || + strcmp(needle->passwd, check->passwd)) { + /* one of them was different */ + continue; + } } - if(!needle->bits.httpproxy || needle->handler->flags&PROTOPT_SSL || - (needle->bits.httpproxy && check->bits.httpproxy && - needle->bits.tunnel_proxy && check->bits.tunnel_proxy && - Curl_raw_equal(needle->proxy.name, check->proxy.name) && - (needle->port == check->port))) { + if(!needle->bits.httpproxy || (needle->handler->flags&PROTOPT_SSL) || + needle->bits.tunnel_proxy) { /* The requested connection does not use a HTTP proxy or it uses SSL or - it is a non-SSL protocol tunneled over the same http proxy name and - port number or it is a non-SSL protocol which is allowed to be - upgraded via TLS */ - - if((Curl_raw_equal(needle->handler->scheme, check->handler->scheme) || - needle->handler->protocol & check->handler->protocol) && - Curl_raw_equal(needle->host.name, check->host.name) && + it is a non-SSL protocol tunneled or it is a non-SSL protocol which + is allowed to be upgraded via TLS */ + + if((strcasecompare(needle->handler->scheme, check->handler->scheme) || + (get_protocol_family(check->handler->protocol) == + needle->handler->protocol && check->tls_upgraded)) && + (!needle->bits.conn_to_host || strcasecompare( + needle->conn_to_host.name, check->conn_to_host.name)) && + (!needle->bits.conn_to_port || + needle->conn_to_port == check->conn_to_port) && + strcasecompare(needle->host.name, check->host.name) && needle->remote_port == check->remote_port) { + /* The schemes match or the the protocol family is the same and the + previous connection was TLS upgraded, and the hostname and host + port match */ if(needle->handler->flags & PROTOPT_SSL) { /* This is a SSL connection so verify that we're using the same SSL options as well */ @@ -3071,7 +1344,8 @@ ConnectionExists(struct SessionHandle *data, check->connection_id)); continue; } - else if(check->ssl[FIRSTSOCKET].state != ssl_connection_complete) { + if(check->ssl[FIRSTSOCKET].state != ssl_connection_complete) { + foundPendingCandidate = TRUE; DEBUGF(infof(data, "Connection #%ld has not started SSL connect, " "can't reuse\n", @@ -3082,37 +1356,63 @@ ConnectionExists(struct SessionHandle *data, match = TRUE; } } - else { /* The requested needle connection is using a proxy, - is the checked one using the same host, port and type? */ - if(check->bits.proxy && - (needle->proxytype == check->proxytype) && - (needle->bits.tunnel_proxy == check->bits.tunnel_proxy) && - Curl_raw_equal(needle->proxy.name, check->proxy.name) && - needle->port == check->port) { - /* This is the same proxy connection, use it! */ - match = TRUE; - } + else { + /* The requested connection is using the same HTTP proxy in normal + mode (no tunneling) */ + match = TRUE; } if(match) { - /* If we are looking for an NTLM connection, check if this is already - authenticating with the right credentials. If not, keep looking so - that we can reuse NTLM connections if possible. (Especially we - must not reuse the same connection if partway through - a handshake!) */ - if(wantNTLM) { - if(credentialsMatch && check->ntlm.state != NTLMSTATE_NONE) { - chosen = check; +#if defined(USE_NTLM) + /* If we are looking for an HTTP+NTLM connection, check if this is + already authenticating with the right credentials. If not, keep + looking so that we can reuse NTLM connections if + possible. (Especially we must not reuse the same connection if + partway through a handshake!) */ + if(wantNTLMhttp) { + if(strcmp(needle->user, check->user) || + strcmp(needle->passwd, check->passwd)) + continue; + } + else if(check->ntlm.state != NTLMSTATE_NONE) { + /* Connection is using NTLM auth but we don't want NTLM */ + continue; + } + /* Same for Proxy NTLM authentication */ + if(wantProxyNTLMhttp) { + /* Both check->http_proxy.user and check->http_proxy.passwd can be + * NULL */ + if(!check->http_proxy.user || !check->http_proxy.passwd) + continue; + + if(strcmp(needle->http_proxy.user, check->http_proxy.user) || + strcmp(needle->http_proxy.passwd, check->http_proxy.passwd)) + continue; + } + else if(check->proxyntlm.state != NTLMSTATE_NONE) { + /* Proxy connection is using NTLM auth but we don't want NTLM */ + continue; + } + + if(wantNTLMhttp || wantProxyNTLMhttp) { + /* Credentials are already checked, we can use this connection */ + chosen = check; + + if((wantNTLMhttp && + (check->ntlm.state != NTLMSTATE_NONE)) || + (wantProxyNTLMhttp && + (check->proxyntlm.state != NTLMSTATE_NONE))) { /* We must use this connection, no other */ *force_reuse = TRUE; break; } - else - continue; - } - if(canPipeline) { + /* Continue look up for a better connection */ + continue; + } +#endif + if(canpipe) { /* We can pipeline if we want to. Let's continue looking for the optimal connection to use, i.e the shortest pipe that is not blacklisted. */ @@ -3124,19 +1424,42 @@ ConnectionExists(struct SessionHandle *data, } /* We can't use the connection if the pipe is full */ - if(pipeLen >= max_pipe_len) + if(max_pipe_len && (pipeLen >= max_pipe_len)) { + infof(data, "Pipe is full, skip (%zu)\n", pipeLen); continue; - + } +#ifdef USE_NGHTTP2 + /* If multiplexed, make sure we don't go over concurrency limit */ + if(check->bits.multiplex) { + /* Multiplexed connections can only be HTTP/2 for now */ + struct http_conn *httpc = &check->proto.httpc; + if(pipeLen >= httpc->settings.max_concurrent_streams) { + infof(data, "MAX_CONCURRENT_STREAMS reached, skip (%zu)\n", + pipeLen); + continue; + } + } +#endif /* We can't use the connection if the pipe is penalized */ - if(Curl_pipeline_penalized(data, check)) + if(Curl_pipeline_penalized(data, check)) { + infof(data, "Penalized, skip\n"); continue; + } - if(pipeLen < best_pipe_len) { - /* This connection has a shorter pipe so far. We'll pick this - and continue searching */ + if(max_pipe_len) { + if(pipeLen < best_pipe_len) { + /* This connection has a shorter pipe so far. We'll pick this + and continue searching */ + chosen = check; + best_pipe_len = pipeLen; + continue; + } + } + else { + /* When not pipelining (== multiplexed), we have a match here! */ chosen = check; - best_pipe_len = pipeLen; - continue; + infof(data, "Multiplexed connection found!\n"); + break; } } else { @@ -3153,64 +1476,13 @@ ConnectionExists(struct SessionHandle *data, return TRUE; /* yes, we found one to use! */ } - return FALSE; /* no matching connecting exists */ -} - -/* Mark the connection as 'idle', or close it if the cache is full. - Returns TRUE if the connection is kept, or FALSE if it was closed. */ -static bool -ConnectionDone(struct SessionHandle *data, struct connectdata *conn) -{ - /* data->multi->maxconnects can be negative, deal with it. */ - size_t maxconnects = - (data->multi->maxconnects < 0) ? 0 : data->multi->maxconnects; - struct connectdata *conn_candidate = NULL; - - /* Mark the current connection as 'unused' */ - conn->inuse = FALSE; - - if(maxconnects > 0 && - data->state.conn_cache->num_connections > maxconnects) { - infof(data, "Connection cache is full, closing the oldest one.\n"); - - conn_candidate = find_oldest_idle_connection(data); - - if(conn_candidate) { - /* Set the connection's owner correctly */ - conn_candidate->data = data; - - /* the winner gets the honour of being disconnected */ - (void)Curl_disconnect(conn_candidate, /* dead_connection */ FALSE); - } + if(foundPendingCandidate && data->set.pipewait) { + infof(data, + "Found pending candidate for reuse and CURLOPT_PIPEWAIT is set\n"); + *waitpipe = TRUE; } - return (conn_candidate == conn) ? FALSE : TRUE; -} - -/* - * The given input connection struct pointer is to be stored in the connection - * cache. If the cache is already full, least interesting existing connection - * (if any) gets closed. - * - * The given connection should be unique. That must've been checked prior to - * this call. - */ -static CURLcode ConnectionStore(struct SessionHandle *data, - struct connectdata *conn) -{ - static int connection_id_counter = 0; - - CURLcode result; - - /* Assign a number to the connection for easier tracking in the log - output */ - conn->connection_id = connection_id_counter++; - - result = Curl_conncache_add_conn(data->state.conn_cache, conn); - if(result != CURLE_OK) - conn->connection_id = -1; - - return result; + return FALSE; /* no matching connecting exists */ } /* after a TCP connection to the proxy has been verified, this function does @@ -3219,79 +1491,49 @@ static CURLcode ConnectionStore(struct SessionHandle *data, Note: this function's sub-functions call failf() */ -CURLcode Curl_connected_proxy(struct connectdata *conn) +CURLcode Curl_connected_proxy(struct connectdata *conn, int sockindex) { - if(!conn->bits.proxy) - return CURLE_OK; + CURLcode result = CURLE_OK; - switch(conn->proxytype) { + if(conn->bits.socksproxy) { #ifndef CURL_DISABLE_PROXY - case CURLPROXY_SOCKS5: - case CURLPROXY_SOCKS5_HOSTNAME: - return Curl_SOCKS5(conn->proxyuser, conn->proxypasswd, - conn->host.name, conn->remote_port, - FIRSTSOCKET, conn); - - case CURLPROXY_SOCKS4: - return Curl_SOCKS4(conn->proxyuser, conn->host.name, - conn->remote_port, FIRSTSOCKET, conn, FALSE); - - case CURLPROXY_SOCKS4A: - return Curl_SOCKS4(conn->proxyuser, conn->host.name, - conn->remote_port, FIRSTSOCKET, conn, TRUE); - -#endif /* CURL_DISABLE_PROXY */ - case CURLPROXY_HTTP: - case CURLPROXY_HTTP_1_0: - /* do nothing here. handled later. */ - break; - default: - break; - } /* switch proxytype */ - - return CURLE_OK; -} + /* for the secondary socket (FTP), use the "connect to host" + * but ignore the "connect to port" (use the secondary port) + */ + const char * const host = conn->bits.httpproxy ? + conn->http_proxy.host.name : + conn->bits.conn_to_host ? + conn->conn_to_host.name : + sockindex == SECONDARYSOCKET ? + conn->secondaryhostname : conn->host.name; + const int port = conn->bits.httpproxy ? (int)conn->http_proxy.port : + sockindex == SECONDARYSOCKET ? conn->secondary_port : + conn->bits.conn_to_port ? conn->conn_to_port : + conn->remote_port; + conn->bits.socksproxy_connecting = TRUE; + switch(conn->socks_proxy.proxytype) { + case CURLPROXY_SOCKS5: + case CURLPROXY_SOCKS5_HOSTNAME: + result = Curl_SOCKS5(conn->socks_proxy.user, conn->socks_proxy.passwd, + host, port, sockindex, conn); + break; -static CURLcode ConnectPlease(struct SessionHandle *data, - struct connectdata *conn, - bool *connected) -{ - CURLcode result; - Curl_addrinfo *addr; -#ifndef CURL_DISABLE_VERBOSE_STRINGS - char *hostname = conn->bits.proxy?conn->proxy.name:conn->host.name; + case CURLPROXY_SOCKS4: + case CURLPROXY_SOCKS4A: + result = Curl_SOCKS4(conn->socks_proxy.user, host, port, sockindex, + conn); + break; - infof(data, "About to connect() to %s%s port %ld (#%ld)\n", - conn->bits.proxy?"proxy ":"", - hostname, conn->port, conn->connection_id); + default: + failf(conn->data, "unknown proxytype option given"); + result = CURLE_COULDNT_CONNECT; + } /* switch proxytype */ + conn->bits.socksproxy_connecting = FALSE; #else - (void)data; -#endif - - /************************************************************* - * Connect to server/proxy - *************************************************************/ - result= Curl_connecthost(conn, - conn->dns_entry, - &conn->sock[FIRSTSOCKET], - &addr, - connected); - if(CURLE_OK == result) { - /* All is cool, we store the current information */ - conn->ip_addr = addr; - - if(*connected) { - result = Curl_connected_proxy(conn); - if(!result) { - conn->bits.tcpconnect[FIRSTSOCKET] = TRUE; - Curl_pgrsTime(data, TIMER_CONNECT); /* connect done */ - } - } + (void)sockindex; +#endif /* CURL_DISABLE_PROXY */ } - if(result) - *connected = FALSE; /* mark it as not connected */ - return result; } @@ -3303,7 +1545,10 @@ void Curl_verboseconnect(struct connectdata *conn) { if(conn->data->set.verbose) infof(conn->data, "Connected to %s (%s) port %ld (#%ld)\n", - conn->bits.proxy ? conn->proxy.dispname : conn->host.dispname, + conn->bits.socksproxy ? conn->socks_proxy.host.dispname : + conn->bits.httpproxy ? conn->http_proxy.host.dispname : + conn->bits.conn_to_host ? conn->conn_to_host.dispname : + conn->host.dispname, conn->ip_addr_str, conn->port, conn->connection_id); } #endif @@ -3335,7 +1580,7 @@ int Curl_doing_getsock(struct connectdata *conn, CURLcode Curl_protocol_connecting(struct connectdata *conn, bool *done) { - CURLcode result=CURLE_OK; + CURLcode result = CURLE_OK; if(conn && conn->handler->connecting) { *done = FALSE; @@ -3354,7 +1599,7 @@ CURLcode Curl_protocol_connecting(struct connectdata *conn, CURLcode Curl_protocol_doing(struct connectdata *conn, bool *done) { - CURLcode result=CURLE_OK; + CURLcode result = CURLE_OK; if(conn && conn->handler->doing) { *done = FALSE; @@ -3374,7 +1619,7 @@ CURLcode Curl_protocol_doing(struct connectdata *conn, bool *done) CURLcode Curl_protocol_connect(struct connectdata *conn, bool *protocol_done) { - CURLcode result=CURLE_OK; + CURLcode result = CURLE_OK; *protocol_done = FALSE; @@ -3393,12 +1638,16 @@ CURLcode Curl_protocol_connect(struct connectdata *conn, if(!conn->bits.protoconnstart) { - result = Curl_proxy_connect(conn); + result = Curl_proxy_connect(conn, FIRSTSOCKET); if(result) return result; + if(CONNECT_FIRSTSOCKET_PROXY_SSL()) + /* wait for HTTPS proxy SSL initialization to complete */ + return CURLE_OK; + if(conn->bits.tunnel_proxy && conn->bits.httpproxy && - (conn->tunnel_state[FIRSTSOCKET] != TUNNEL_COMPLETE)) + Curl_connect_ongoing(conn)) /* when using an HTTP tunnel proxy, await complete tunnel establishment before proceeding further. Return CURLE_OK so we'll be called again */ return CURLE_OK; @@ -3422,11 +1671,11 @@ CURLcode Curl_protocol_connect(struct connectdata *conn, } /* - * Helpers for IDNA convertions. + * Helpers for IDNA conversions. */ static bool is_ASCII_name(const char *hostname) { - const unsigned char *ch = (const unsigned char*)hostname; + const unsigned char *ch = (const unsigned char *)hostname; while(*ch) { if(*ch++ & 0x80) @@ -3435,57 +1684,15 @@ static bool is_ASCII_name(const char *hostname) return TRUE; } -#ifdef USE_LIBIDN -/* - * Check if characters in hostname is allowed in Top Level Domain. - */ -static bool tld_check_name(struct SessionHandle *data, - const char *ace_hostname) -{ - size_t err_pos; - char *uc_name = NULL; - int rc; -#ifndef CURL_DISABLE_VERBOSE_STRINGS - const char *tld_errmsg = "<no msg>"; -#else - (void)data; -#endif - - /* Convert (and downcase) ACE-name back into locale's character set */ - rc = idna_to_unicode_lzlz(ace_hostname, &uc_name, 0); - if(rc != IDNA_SUCCESS) - return FALSE; - - rc = tld_check_lz(uc_name, &err_pos, NULL); -#ifndef CURL_DISABLE_VERBOSE_STRINGS -#ifdef HAVE_TLD_STRERROR - if(rc != TLD_SUCCESS) - tld_errmsg = tld_strerror((Tld_rc)rc); -#endif - if(rc == TLD_INVALID) - infof(data, "WARNING: %s; pos %u = `%c'/0x%02X\n", - tld_errmsg, err_pos, uc_name[err_pos], - uc_name[err_pos] & 255); - else if(rc != TLD_SUCCESS) - infof(data, "WARNING: TLD check for %s failed; %s\n", - uc_name, tld_errmsg); -#endif /* CURL_DISABLE_VERBOSE_STRINGS */ - if(uc_name) - idn_free(uc_name); - if(rc != TLD_SUCCESS) - return FALSE; - - return TRUE; -} -#endif - /* * Perform any necessary IDN conversion of hostname */ -static void fix_hostname(struct SessionHandle *data, - struct connectdata *conn, struct hostname *host) +static CURLcode fix_hostname(struct connectdata *conn, struct hostname *host) { -#ifndef USE_LIBIDN + size_t len; + struct Curl_easy *data = conn->data; + +#ifndef USE_LIBIDN2 (void)data; (void)conn; #elif defined(CURL_DISABLE_VERBOSE_STRINGS) @@ -3494,47 +1701,84 @@ static void fix_hostname(struct SessionHandle *data, /* set the name we use to display the host name */ host->dispname = host->name; + + len = strlen(host->name); + if(len && (host->name[len-1] == '.')) + /* strip off a single trailing dot if present, primarily for SNI but + there's no use for it */ + host->name[len-1] = 0; + + /* Check name for non-ASCII and convert hostname to ACE form if we can */ if(!is_ASCII_name(host->name)) { -#ifdef USE_LIBIDN - /************************************************************* - * Check name for non-ASCII and convert hostname to ACE form. - *************************************************************/ - if(stringprep_check_version(LIBIDN_REQUIRED_VERSION)) { +#ifdef USE_LIBIDN2 + if(idn2_check_version(IDN2_VERSION)) { + char *ace_hostname = NULL; +#if IDN2_VERSION_NUMBER >= 0x00140000 + /* IDN2_NFC_INPUT: Normalize input string using normalization form C. + IDN2_NONTRANSITIONAL: Perform Unicode TR46 non-transitional + processing. */ + int flags = IDN2_NFC_INPUT | IDN2_NONTRANSITIONAL; +#else + int flags = IDN2_NFC_INPUT; +#endif + int rc = idn2_lookup_ul((const char *)host->name, &ace_hostname, flags); + if(rc == IDN2_OK) { + host->encalloc = (char *)ace_hostname; + /* change the name pointer to point to the encoded hostname */ + host->name = host->encalloc; + } + else { + failf(data, "Failed to convert %s to ACE; %s\n", host->name, + idn2_strerror(rc)); + return CURLE_URL_MALFORMAT; + } + } +#elif defined(USE_WIN32_IDN) char *ace_hostname = NULL; - int rc = idna_to_ascii_lz(host->name, &ace_hostname, 0); - infof (data, "Input domain encoded as `%s'\n", - stringprep_locale_charset ()); - if(rc != IDNA_SUCCESS) - infof(data, "Failed to convert %s to ACE; %s\n", - host->name, Curl_idn_strerror(conn,rc)); - else { - /* tld_check_name() displays a warning if the host name contains - "illegal" characters for this TLD */ - (void)tld_check_name(data, ace_hostname); + if(curl_win32_idn_to_ascii(host->name, &ace_hostname)) { host->encalloc = ace_hostname; /* change the name pointer to point to the encoded hostname */ host->name = host->encalloc; } - } -#elif defined(USE_WIN32_IDN) - /************************************************************* - * Check name for non-ASCII and convert hostname to ACE form. - *************************************************************/ - char *ace_hostname = NULL; - int rc = curl_win32_idn_to_ascii(host->name, &ace_hostname); - if(rc == 0) - infof(data, "Failed to convert %s to ACE;\n", - host->name); else { - host->encalloc = ace_hostname; - /* change the name pointer to point to the encoded hostname */ - host->name = host->encalloc; + failf(data, "Failed to convert %s to ACE;\n", host->name); + return CURLE_URL_MALFORMAT; } #else infof(data, "IDN support not present, can't parse Unicode domains\n"); #endif } + { + char *hostp; + for(hostp = host->name; *hostp; hostp++) { + if(*hostp <= 32) { + failf(data, "Host name '%s' contains bad letter", host->name); + return CURLE_URL_MALFORMAT; + } + } + } + return CURLE_OK; +} + +/* + * Frees data allocated by fix_hostname() + */ +static void free_fixed_hostname(struct hostname *host) +{ +#if defined(USE_LIBIDN2) + if(host->encalloc) { + idn2_free(host->encalloc); /* must be freed with idn2_free() since this was + allocated by libidn */ + host->encalloc = NULL; + } +#elif defined(USE_WIN32_IDN) + free(host->encalloc); /* must be freed with free() since this was + allocated by curl_win32_idn_to_ascii */ + host->encalloc = NULL; +#else + (void)host; +#endif } static void llist_dtor(void *user, void *element) @@ -3547,12 +1791,43 @@ static void llist_dtor(void *user, void *element) /* * Allocate and initialize a new connectdata object. */ -static struct connectdata *allocate_conn(struct SessionHandle *data) +static struct connectdata *allocate_conn(struct Curl_easy *data) { - struct connectdata *conn = calloc(1, sizeof(struct connectdata)); + struct connectdata *conn; + size_t connsize = sizeof(struct connectdata); + +#ifdef USE_SSL +/* SSLBK_MAX_ALIGN: The max byte alignment a CPU would use */ +#define SSLBK_MAX_ALIGN 32 + /* The SSL backend-specific data (ssl_backend_data) objects are allocated as + part of connectdata at the end. To ensure suitable alignment we will + assume a maximum of SSLBK_MAX_ALIGN for alignment. Since calloc returns a + pointer suitably aligned for any variable this will ensure the + ssl_backend_data array has proper alignment, even if that alignment turns + out to be less than SSLBK_MAX_ALIGN. */ + size_t paddingsize = sizeof(struct connectdata) % SSLBK_MAX_ALIGN; + size_t alignsize = paddingsize ? (SSLBK_MAX_ALIGN - paddingsize) : 0; + size_t sslbksize = Curl_ssl->sizeof_ssl_backend_data; + connsize += alignsize + (4 * sslbksize); +#endif + + conn = calloc(1, connsize); if(!conn) return NULL; +#ifdef USE_SSL + /* Point to the ssl_backend_data objects at the end of connectdata. + Note that these backend pointers can be swapped by vtls (eg ssl backend + data becomes proxy backend data). */ + { + char *end = (char *)conn + connsize; + conn->ssl[0].backend = ((void *)(end - (4 * sslbksize))); + conn->ssl[1].backend = ((void *)(end - (3 * sslbksize))); + conn->proxy_ssl[0].backend = ((void *)(end - (2 * sslbksize))); + conn->proxy_ssl[1].backend = ((void *)(end - (1 * sslbksize))); + } +#endif + conn->handler = &Curl_handler_dummy; /* Be sure we have a handler defined already from start to avoid NULL situations and checks */ @@ -3561,26 +1836,35 @@ static struct connectdata *allocate_conn(struct SessionHandle *data) conn->sock[FIRSTSOCKET] = CURL_SOCKET_BAD; /* no file descriptor */ conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD; /* no file descriptor */ + conn->tempsock[0] = CURL_SOCKET_BAD; /* no file descriptor */ + conn->tempsock[1] = CURL_SOCKET_BAD; /* no file descriptor */ conn->connection_id = -1; /* no ID */ conn->port = -1; /* unknown at this point */ + conn->remote_port = -1; /* unknown at this point */ +#if defined(USE_RECV_BEFORE_SEND_WORKAROUND) && defined(DEBUGBUILD) + conn->postponed[0].bindsock = CURL_SOCKET_BAD; /* no file descriptor */ + conn->postponed[1].bindsock = CURL_SOCKET_BAD; /* no file descriptor */ +#endif /* USE_RECV_BEFORE_SEND_WORKAROUND && DEBUGBUILD */ /* Default protocol-independent behavior doesn't support persistent connections, so we set this to force-close. Protocols that support this need to set this to FALSE in their "curl_do" functions. */ - conn->bits.close = TRUE; + connclose(conn, "Default to force-close"); /* Store creation time to help future close decision making */ - conn->created = Curl_tvnow(); + conn->created = Curl_now(); conn->data = data; /* Setup the association between this connection - and the SessionHandle */ + and the Curl_easy */ - conn->proxytype = data->set.proxytype; /* type */ + conn->http_proxy.proxytype = data->set.proxytype; + conn->socks_proxy.proxytype = CURLPROXY_SOCKS4; #ifdef CURL_DISABLE_PROXY conn->bits.proxy = FALSE; conn->bits.httpproxy = FALSE; + conn->bits.socksproxy = FALSE; conn->bits.proxy_user_passwd = FALSE; conn->bits.tunnel_proxy = FALSE; @@ -3589,45 +1873,59 @@ static struct connectdata *allocate_conn(struct SessionHandle *data) /* note that these two proxy bits are now just on what looks to be requested, they may be altered down the road */ conn->bits.proxy = (data->set.str[STRING_PROXY] && - *data->set.str[STRING_PROXY])?TRUE:FALSE; + *data->set.str[STRING_PROXY]) ? TRUE : FALSE; conn->bits.httpproxy = (conn->bits.proxy && - (conn->proxytype == CURLPROXY_HTTP || - conn->proxytype == CURLPROXY_HTTP_1_0))?TRUE:FALSE; + (conn->http_proxy.proxytype == CURLPROXY_HTTP || + conn->http_proxy.proxytype == CURLPROXY_HTTP_1_0 || + conn->http_proxy.proxytype == CURLPROXY_HTTPS)) ? + TRUE : FALSE; + conn->bits.socksproxy = (conn->bits.proxy && + !conn->bits.httpproxy) ? TRUE : FALSE; + + if(data->set.str[STRING_PRE_PROXY] && *data->set.str[STRING_PRE_PROXY]) { + conn->bits.proxy = TRUE; + conn->bits.socksproxy = TRUE; + } + conn->bits.proxy_user_passwd = - (NULL != data->set.str[STRING_PROXYUSERNAME])?TRUE:FALSE; + (data->set.str[STRING_PROXYUSERNAME]) ? TRUE : FALSE; conn->bits.tunnel_proxy = data->set.tunnel_thru_httpproxy; #endif /* CURL_DISABLE_PROXY */ - conn->bits.user_passwd = (NULL != data->set.str[STRING_USERNAME])?TRUE:FALSE; + conn->bits.user_passwd = (data->set.str[STRING_USERNAME]) ? TRUE : FALSE; conn->bits.ftp_use_epsv = data->set.ftp_use_epsv; conn->bits.ftp_use_eprt = data->set.ftp_use_eprt; - conn->verifypeer = data->set.ssl.verifypeer; - conn->verifyhost = data->set.ssl.verifyhost; + conn->ssl_config.verifystatus = data->set.ssl.primary.verifystatus; + conn->ssl_config.verifypeer = data->set.ssl.primary.verifypeer; + conn->ssl_config.verifyhost = data->set.ssl.primary.verifyhost; + conn->proxy_ssl_config.verifystatus = + data->set.proxy_ssl.primary.verifystatus; + conn->proxy_ssl_config.verifypeer = data->set.proxy_ssl.primary.verifypeer; + conn->proxy_ssl_config.verifyhost = data->set.proxy_ssl.primary.verifyhost; conn->ip_version = data->set.ipver; -#if defined(USE_NTLM) && defined(NTLM_WB_ENABLED) +#if !defined(CURL_DISABLE_HTTP) && defined(USE_NTLM) && \ + defined(NTLM_WB_ENABLED) conn->ntlm_auth_hlpr_socket = CURL_SOCKET_BAD; conn->ntlm_auth_hlpr_pid = 0; conn->challenge_header = NULL; conn->response_header = NULL; #endif - if(Curl_multi_pipeline_enabled(data->multi) && - !conn->master_buffer) { - /* Allocate master_buffer to be used for pipelining */ - conn->master_buffer = calloc(BUFSIZE, sizeof (char)); + if(Curl_pipeline_wanted(data->multi, CURLPIPE_HTTP1) && + !conn->master_buffer) { + /* Allocate master_buffer to be used for HTTP/1 pipelining */ + conn->master_buffer = calloc(MASTERBUF_SIZE, sizeof(char)); if(!conn->master_buffer) goto error; } /* Initialize the pipeline lists */ - conn->send_pipe = Curl_llist_alloc((curl_llist_dtor) llist_dtor); - conn->recv_pipe = Curl_llist_alloc((curl_llist_dtor) llist_dtor); - if(!conn->send_pipe || !conn->recv_pipe) - goto error; + Curl_llist_init(&conn->send_pipe, (curl_llist_dtor) llist_dtor); + Curl_llist_init(&conn->recv_pipe, (curl_llist_dtor) llist_dtor); #ifdef HAVE_GSSAPI conn->data_prot = PROT_CLEAR; @@ -3643,26 +1941,23 @@ static struct connectdata *allocate_conn(struct SessionHandle *data) conn->localport = data->set.localport; /* the close socket stuff needs to be copied to the connection struct as - it may live on without (this specific) SessionHandle */ + it may live on without (this specific) Curl_easy */ conn->fclosesocket = data->set.fclosesocket; conn->closesocket_client = data->set.closesocket_client; return conn; error: - Curl_llist_destroy(conn->send_pipe, NULL); - Curl_llist_destroy(conn->recv_pipe, NULL); + Curl_llist_destroy(&conn->send_pipe, NULL); + Curl_llist_destroy(&conn->recv_pipe, NULL); - conn->send_pipe = NULL; - conn->recv_pipe = NULL; - - Curl_safefree(conn->master_buffer); - Curl_safefree(conn->localdev); - Curl_safefree(conn); + free(conn->master_buffer); + free(conn->localdev); + free(conn); return NULL; } -static CURLcode findprotocol(struct SessionHandle *data, +static CURLcode findprotocol(struct Curl_easy *data, struct connectdata *conn, const char *protostr) { @@ -3673,7 +1968,7 @@ static CURLcode findprotocol(struct SessionHandle *data, variables based on the URL. Now that the handler may be changed later when the protocol specific setup function is called. */ for(pp = protocols; (p = *pp) != NULL; pp++) { - if(Curl_raw_equal(p->scheme, protostr)) { + if(strcasecompare(p->scheme, protostr)) { /* Protocol found in table. Check if allowed */ if(!(data->set.allowed_protocols & p->protocol)) /* nope, get out */ @@ -3698,7 +1993,7 @@ static CURLcode findprotocol(struct SessionHandle *data, /* The protocol was not found in the table, but we don't have to assign it to anything since it is already assigned to a dummy-struct in the create_conn() function when the connectdata struct is allocated. */ - failf(data, "Protocol %s not supported or disabled in " LIBCURL_NAME, + failf(data, "Protocol \"%s\" not supported or disabled in " LIBCURL_NAME, protostr); return CURLE_UNSUPPORTED_PROTOCOL; @@ -3707,7 +2002,7 @@ static CURLcode findprotocol(struct SessionHandle *data, /* * Parse URL and fill in the relevant members of the connection struct. */ -static CURLcode parseurlandfillconn(struct SessionHandle *data, +static CURLcode parseurlandfillconn(struct Curl_easy *data, struct connectdata *conn, bool *prot_missing, char **userp, char **passwdp, @@ -3717,14 +2012,23 @@ static CURLcode parseurlandfillconn(struct SessionHandle *data, char *fragment; char *path = data->state.path; char *query; + int i; int rc; - char protobuf[16]; - const char *protop; + const char *protop = ""; CURLcode result; bool rebuild_url = FALSE; + bool url_has_scheme = FALSE; + char protobuf[16]; *prot_missing = FALSE; + /* We might pass the entire URL into the request so we need to make sure + * there are no bad characters in there.*/ + if(strpbrk(data->change.url, "\r\n")) { + failf(data, "Illegal characters found in URL"); + return CURLE_URL_MALFORMAT; + } + /************************************************************* * Parse the URL. * @@ -3733,70 +2037,165 @@ static CURLcode parseurlandfillconn(struct SessionHandle *data, * proxy -- and we don't know if we will need to use SSL until we parse the * url ... ************************************************************/ - if((2 == sscanf(data->change.url, "%15[^:]:%[^\n]", - protobuf, path)) && - Curl_raw_equal(protobuf, "file")) { - if(path[0] == '/' && path[1] == '/') { - /* Allow omitted hostname (e.g. file:/<path>). This is not strictly - * speaking a valid file: URL by RFC 1738, but treating file:/<path> as - * file://localhost/<path> is similar to how other schemes treat missing - * hostnames. See RFC 1808. */ - - /* This cannot be done with strcpy() in a portable manner, since the - memory areas overlap! */ - memmove(path, path + 2, strlen(path + 2)+1); + if(data->change.url[0] == ':') { + failf(data, "Bad URL, colon is first character"); + return CURLE_URL_MALFORMAT; + } + + /* MSDOS/Windows style drive prefix, eg c: in c:foo */ +#define STARTS_WITH_DRIVE_PREFIX(str) \ + ((('a' <= str[0] && str[0] <= 'z') || \ + ('A' <= str[0] && str[0] <= 'Z')) && \ + (str[1] == ':')) + + /* MSDOS/Windows style drive prefix, optionally with + * a '|' instead of ':', followed by a slash or NUL */ +#define STARTS_WITH_URL_DRIVE_PREFIX(str) \ + ((('a' <= (str)[0] && (str)[0] <= 'z') || \ + ('A' <= (str)[0] && (str)[0] <= 'Z')) && \ + ((str)[1] == ':' || (str)[1] == '|') && \ + ((str)[2] == '/' || (str)[2] == 0)) + + /* Don't mistake a drive letter for a scheme if the default protocol is file. + curld --proto-default file c:/foo/bar.txt */ + if(STARTS_WITH_DRIVE_PREFIX(data->change.url) && + data->set.str[STRING_DEFAULT_PROTOCOL] && + strcasecompare(data->set.str[STRING_DEFAULT_PROTOCOL], "file")) { + ; /* do nothing */ + } + else { /* check for a scheme */ + for(i = 0; i < 16 && data->change.url[i]; ++i) { + if(data->change.url[i] == '/') + break; + if(data->change.url[i] == ':') { + url_has_scheme = TRUE; + break; + } } - /* - * we deal with file://<host>/<path> differently since it supports no - * hostname other than "localhost" and "127.0.0.1", which is unique among - * the URL protocols specified in RFC 1738 - */ - if(path[0] != '/') { - /* the URL included a host name, we ignore host names in file:// URLs - as the standards don't define what to do with them */ - char *ptr=strchr(path, '/'); - if(ptr) { - /* there was a slash present - - RFC1738 (section 3.1, page 5) says: - - The rest of the locator consists of data specific to the scheme, - and is known as the "url-path". It supplies the details of how the - specified resource can be accessed. Note that the "/" between the - host (or port) and the url-path is NOT part of the url-path. - - As most agents use file://localhost/foo to get '/foo' although the - slash preceding foo is a separator and not a slash for the path, - a URL as file://localhost//foo must be valid as well, to refer to - the same file with an absolute path. - */ + } - if(ptr[1] && ('/' == ptr[1])) - /* if there was two slashes, we skip the first one as that is then - used truly as a separator */ - ptr++; + /* handle the file: scheme */ + if((url_has_scheme && strncasecompare(data->change.url, "file:", 5)) || + (!url_has_scheme && data->set.str[STRING_DEFAULT_PROTOCOL] && + strcasecompare(data->set.str[STRING_DEFAULT_PROTOCOL], "file"))) { + if(url_has_scheme) + rc = sscanf(data->change.url, "%*15[^\n/:]:%[^\n]", path); + else + rc = sscanf(data->change.url, "%[^\n]", path); - /* This cannot be made with strcpy, as the memory chunks overlap! */ - memmove(path, ptr, strlen(ptr)+1); + if(rc != 1) { + failf(data, "Bad URL"); + return CURLE_URL_MALFORMAT; + } + + if(url_has_scheme && path[0] == '/' && path[1] == '/' && + path[2] == '/' && path[3] == '/') { + /* This appears to be a UNC string (usually indicating a SMB share). + * We don't do SMB in file: URLs. (TODO?) + */ + failf(data, "SMB shares are not supported in file: URLs."); + return CURLE_URL_MALFORMAT; + } + + /* Extra handling URLs with an authority component (i.e. that start with + * "file://") + * + * We allow omitted hostname (e.g. file:/<path>) -- valid according to + * RFC 8089, but not the (current) WHAT-WG URL spec. + */ + if(url_has_scheme && path[0] == '/' && path[1] == '/') { + /* swallow the two slashes */ + char *ptr = &path[2]; + + /* + * According to RFC 8089, a file: URL can be reliably dereferenced if: + * + * o it has no/blank hostname, or + * + * o the hostname matches "localhost" (case-insensitively), or + * + * o the hostname is a FQDN that resolves to this machine. + * + * For brevity, we only consider URLs with empty, "localhost", or + * "127.0.0.1" hostnames as local. + * + * Additionally, there is an exception for URLs with a Windows drive + * letter in the authority (which was accidentally omitted from RFC 8089 + * Appendix E, but believe me, it was meant to be there. --MK) + */ + if(ptr[0] != '/' && !STARTS_WITH_URL_DRIVE_PREFIX(ptr)) { + /* the URL includes a host name, it must match "localhost" or + "127.0.0.1" to be valid */ + if(!checkprefix("localhost/", ptr) && + !checkprefix("127.0.0.1/", ptr)) { + failf(data, "Invalid file://hostname/, " + "expected localhost or 127.0.0.1 or none"); + return CURLE_URL_MALFORMAT; + } + ptr += 9; /* now points to the slash after the host */ } + + /* + * RFC 8089, Appendix D, Section D.1, says: + * + * > In a POSIX file system, the root of the file system is represented + * > as a directory with a zero-length name, usually written as "/"; the + * > presence of this root in a file URI can be taken as given by the + * > initial slash in the "path-absolute" rule. + * + * i.e. the first slash is part of the path. + * + * However in RFC 1738 the "/" between the host (or port) and the + * URL-path was NOT part of the URL-path. Any agent that followed the + * older spec strictly, and wanted to refer to a file with an absolute + * path, would have included a second slash. So if there are two + * slashes, swallow one. + */ + if('/' == ptr[1]) /* note: the only way ptr[0]!='/' is if ptr[1]==':' */ + ptr++; + + /* This cannot be done with strcpy, as the memory chunks overlap! */ + memmove(path, ptr, strlen(ptr) + 1); } +#if !defined(MSDOS) && !defined(WIN32) && !defined(__CYGWIN__) + /* Don't allow Windows drive letters when not in Windows. + * This catches both "file:/c:" and "file:c:" */ + if(('/' == path[0] && STARTS_WITH_URL_DRIVE_PREFIX(&path[1])) || + STARTS_WITH_URL_DRIVE_PREFIX(path)) { + failf(data, "File drive letters are only accepted in MSDOS/Windows."); + return CURLE_URL_MALFORMAT; + } +#else + /* If the path starts with a slash and a drive letter, ditch the slash */ + if('/' == path[0] && STARTS_WITH_URL_DRIVE_PREFIX(&path[1])) { + /* This cannot be done with strcpy, as the memory chunks overlap! */ + memmove(path, &path[1], strlen(&path[1]) + 1); + } +#endif + protop = "file"; /* protocol string */ + *prot_missing = !url_has_scheme; } else { /* clear path */ - path[0]=0; - - if(2 > sscanf(data->change.url, - "%15[^\n:]://%[^\n/?]%[^\n]", - protobuf, - conn->host.name, path)) { + char slashbuf[4]; + path[0] = 0; + + rc = sscanf(data->change.url, + "%15[^\n/:]:%3[/]%[^\n/?#]%[^\n]", + protobuf, slashbuf, conn->host.name, path); + if(2 == rc) { + failf(data, "Bad URL"); + return CURLE_URL_MALFORMAT; + } + if(3 > rc) { /* * The URL was badly formatted, let's try the browser-style _without_ * protocol specified like 'http://'. */ - rc = sscanf(data->change.url, "%[^\n/?]%[^\n]", conn->host.name, path); + rc = sscanf(data->change.url, "%[^\n/?#]%[^\n]", conn->host.name, path); if(1 > rc) { /* * We couldn't even get this format. @@ -3813,33 +2212,51 @@ static CURLcode parseurlandfillconn(struct SessionHandle *data, } /* - * Since there was no protocol part specified, we guess what protocol it - * is based on the first letters of the server name. + * Since there was no protocol part specified in the URL use the + * user-specified default protocol. If we weren't given a default make a + * guess by matching some protocols against the host's outermost + * sub-domain name. Finally if there was no match use HTTP. */ - /* Note: if you add a new protocol, please update the list in - * lib/version.c too! */ - - if(checkprefix("FTP.", conn->host.name)) - protop = "ftp"; - else if(checkprefix("DICT.", conn->host.name)) - protop = "DICT"; - else if(checkprefix("LDAP.", conn->host.name)) - protop = "LDAP"; - else if(checkprefix("IMAP.", conn->host.name)) - protop = "IMAP"; - else if(checkprefix("SMTP.", conn->host.name)) - protop = "smtp"; - else if(checkprefix("POP3.", conn->host.name)) - protop = "pop3"; - else { - protop = "http"; + protop = data->set.str[STRING_DEFAULT_PROTOCOL]; + if(!protop) { + /* Note: if you add a new protocol, please update the list in + * lib/version.c too! */ + if(checkprefix("FTP.", conn->host.name)) + protop = "ftp"; + else if(checkprefix("DICT.", conn->host.name)) + protop = "DICT"; + else if(checkprefix("LDAP.", conn->host.name)) + protop = "LDAP"; + else if(checkprefix("IMAP.", conn->host.name)) + protop = "IMAP"; + else if(checkprefix("SMTP.", conn->host.name)) + protop = "smtp"; + else if(checkprefix("POP3.", conn->host.name)) + protop = "pop3"; + else + protop = "http"; } *prot_missing = TRUE; /* not given in URL */ } - else + else { + size_t s = strlen(slashbuf); protop = protobuf; + if(s != 2) { + infof(data, "Unwillingly accepted illegal URL using %d slash%s!\n", + s, s>1?"es":""); + + if(data->change.url_alloc) + free(data->change.url); + /* repair the URL to use two slashes */ + data->change.url = aprintf("%s://%s%s", + protobuf, conn->host.name, path); + if(!data->change.url) + return CURLE_OUT_OF_MEMORY; + data->change.url_alloc = TRUE; + } + } } /* We search for '?' in the host name (but only on the right side of a @@ -3848,7 +2265,7 @@ static CURLcode parseurlandfillconn(struct SessionHandle *data, */ at = strchr(conn->host.name, '@'); if(at) - query = strchr(at+1, '?'); + query = strchr(at + 1, '?'); else query = strchr(conn->host.name, '?'); @@ -3866,15 +2283,15 @@ static CURLcode parseurlandfillconn(struct SessionHandle *data, /* move the existing path plus the zero byte forward, to make room for the host-name part */ - memmove(path+hostlen+1, path, pathlen+1); + memmove(path + hostlen + 1, path, pathlen + 1); /* now copy the trailing host part in front of the existing path */ - memcpy(path+1, query, hostlen); + memcpy(path + 1, query, hostlen); path[0]='/'; /* prepend the missing slash */ rebuild_url = TRUE; - *query=0; /* now cut off the hostname at the ? */ + *query = 0; /* now cut off the hostname at the ? */ } else if(!path[0]) { /* if there's no path set, use a single slash */ @@ -3883,18 +2300,18 @@ static CURLcode parseurlandfillconn(struct SessionHandle *data, } /* If the URL is malformatted (missing a '/' after hostname before path) we - * insert a slash here. The only letter except '/' we accept to start a path - * is '?'. + * insert a slash here. The only letters except '/' that can start a path is + * '?' and '#' - as controlled by the two sscanf() patterns above. */ - if(path[0] == '?') { + if(path[0] != '/') { /* We need this function to deal with overlapping memory areas. We know that the memory area 'path' points to is 'urllen' bytes big and that is bigger than the path. Use +1 to move the zero byte too. */ - memmove(&path[1], path, strlen(path)+1); + memmove(&path[1], path, strlen(path) + 1); path[0] = '/'; rebuild_url = TRUE; } - else { + else if(!data->set.path_as_is) { /* sanitise paths and remove ../ and ./ sequences according to RFC3986 */ char *newp = Curl_dedotdotify(path); if(!newp) @@ -3922,14 +2339,30 @@ static CURLcode parseurlandfillconn(struct SessionHandle *data, size_t plen = strlen(path); /* new path, should be 1 byte longer than the original */ - size_t urllen = strlen(data->change.url); /* original URL length */ - size_t prefixlen = strlen(conn->host.name); - if(!*prot_missing) - prefixlen += strlen(protop) + strlen("://"); + if(!*prot_missing) { + size_t protolen = strlen(protop); - reurl = malloc(urllen + 2); /* 2 for zerobyte + slash */ + if(curl_strnequal(protop, data->change.url, protolen)) + prefixlen += protolen; + else { + failf(data, "<url> malformed"); + return CURLE_URL_MALFORMAT; + } + + if(curl_strnequal("://", &data->change.url[protolen], 3)) + prefixlen += 3; + /* only file: is allowed to omit one or both slashes */ + else if(curl_strnequal("file:", data->change.url, 5)) + prefixlen += 1 + (data->change.url[5] == '/'); + else { + failf(data, "<url> malformed"); + return CURLE_URL_MALFORMAT; + } + } + + reurl = malloc(prefixlen + plen + 1); if(!reurl) return CURLE_OUT_OF_MEMORY; @@ -3951,37 +2384,74 @@ static CURLcode parseurlandfillconn(struct SessionHandle *data, data->change.url_alloc = TRUE; /* free this later */ } + result = findprotocol(data, conn, protop); + if(result) + return result; + /* * Parse the login details from the URL and strip them out of * the host name */ result = parse_url_login(data, conn, userp, passwdp, optionsp); - if(result != CURLE_OK) + if(result) return result; if(conn->host.name[0] == '[') { /* This looks like an IPv6 address literal. See if there is an address - scope. */ - char *percent = strstr (conn->host.name, "%25"); + scope if there is no location header */ + char *percent = strchr(conn->host.name, '%'); if(percent) { + unsigned int identifier_offset = 3; char *endp; - unsigned long scope = strtoul (percent + 3, &endp, 10); + unsigned long scope; + if(strncmp("%25", percent, 3) != 0) { + infof(data, + "Please URL encode %% as %%25, see RFC 6874.\n"); + identifier_offset = 1; + } + scope = strtoul(percent + identifier_offset, &endp, 10); if(*endp == ']') { /* The address scope was well formed. Knock it out of the hostname. */ - memmove(percent, endp, strlen(endp)+1); - if(!data->state.this_is_a_follow) - /* Don't honour a scope given in a Location: header */ - conn->scope = (unsigned int)scope; + memmove(percent, endp, strlen(endp) + 1); + conn->scope_id = (unsigned int)scope; + } + else { + /* Zone identifier is not numeric */ +#if defined(HAVE_NET_IF_H) && defined(IFNAMSIZ) && defined(HAVE_IF_NAMETOINDEX) + char ifname[IFNAMSIZ + 2]; + char *square_bracket; + unsigned int scopeidx = 0; + strncpy(ifname, percent + identifier_offset, IFNAMSIZ + 2); + /* Ensure nullbyte termination */ + ifname[IFNAMSIZ + 1] = '\0'; + square_bracket = strchr(ifname, ']'); + if(square_bracket) { + /* Remove ']' */ + *square_bracket = '\0'; + scopeidx = if_nametoindex(ifname); + if(scopeidx == 0) { + infof(data, "Invalid network interface: %s; %s\n", ifname, + strerror(errno)); + } + } + if(scopeidx > 0) { + char *p = percent + identifier_offset + strlen(ifname); + + /* Remove zone identifier from hostname */ + memmove(percent, p, strlen(p) + 1); + conn->scope_id = scopeidx; + } + else +#endif /* HAVE_NET_IF_H && IFNAMSIZ */ + infof(data, "Invalid IPv6 address format\n"); } - else - infof(data, "Invalid IPv6 address format\n"); } } - if(data->set.scope) + if(data->set.scope_id) /* Override any scope that was set above. */ - conn->scope = data->set.scope; + conn->scope_id = data->set.scope_id; /* Remove the fragment part of the path. Per RFC 2396, this is always the last part of the URI. We are looking for the first '#' so that we deal @@ -4004,15 +2474,14 @@ static CURLcode parseurlandfillconn(struct SessionHandle *data, * conn->host.name is B * data->state.path is /C */ - - return findprotocol(data, conn, protop); + return CURLE_OK; } /* * If we're doing a resumed transfer, we need to setup our stuff * properly. */ -static CURLcode setup_range(struct SessionHandle *data) +static CURLcode setup_range(struct Curl_easy *data) { struct UrlState *s = &data->state; s->resume_from = data->set.set_resume_from; @@ -4021,11 +2490,11 @@ static CURLcode setup_range(struct SessionHandle *data) free(s->range); if(s->resume_from) - s->range = aprintf("%" FORMAT_OFF_TU "-", s->resume_from); + s->range = aprintf("%" CURL_FORMAT_CURL_OFF_TU "-", s->resume_from); else s->range = strdup(data->set.str[STRING_SET_RANGE]); - s->rangestringalloc = (s->range)?TRUE:FALSE; + s->rangestringalloc = (s->range) ? TRUE : FALSE; if(!s->range) return CURLE_OUT_OF_MEMORY; @@ -4044,7 +2513,7 @@ static CURLcode setup_range(struct SessionHandle *data) * setup_connection_internals() - * * Setup connection internals specific to the requested protocol in the - * SessionHandle. This is inited and setup before the connection is made but + * Curl_easy. This is inited and setup before the connection is made but * is about the particular protocol that is to be used. * * This MUST get called after proxy magic has been figured out. @@ -4053,16 +2522,17 @@ static CURLcode setup_connection_internals(struct connectdata *conn) { const struct Curl_handler * p; CURLcode result; + struct Curl_easy *data = conn->data; /* in some case in the multi state-machine, we go back to the CONNECT state and then a second (or third or...) call to this function will be made without doing a DISCONNECT or DONE in between (since the connection is yet in place) and therefore this function needs to first make sure there's no lingering previous data allocated. */ - Curl_free_request_state(conn->data); + Curl_free_request_state(data); - memset(&conn->data->req, 0, sizeof(struct SingleRequest)); - conn->data->req.maxdownload = -1; + memset(&data->req, 0, sizeof(struct SingleRequest)); + data->req.maxdownload = -1; conn->socktype = SOCK_STREAM; /* most of them are TCP streams */ @@ -4072,7 +2542,7 @@ static CURLcode setup_connection_internals(struct connectdata *conn) if(p->setup_connection) { result = (*p->setup_connection)(conn); - if(result != CURLE_OK) + if(result) return result; p = conn->handler; /* May have changed. */ @@ -4083,22 +2553,18 @@ static CURLcode setup_connection_internals(struct connectdata *conn) was very likely already set to the proxy port */ conn->port = p->defport; - /* only if remote_port was not already parsed off the URL we use the - default port number */ - if(!conn->remote_port) - conn->remote_port = (unsigned short)conn->given->defport; - return CURLE_OK; } /* * Curl_free_request_state() should free temp data that was allocated in the - * SessionHandle for this single request. + * Curl_easy for this single request. */ -void Curl_free_request_state(struct SessionHandle *data) +void Curl_free_request_state(struct Curl_easy *data) { Curl_safefree(data->req.protop); + Curl_safefree(data->req.newurl); } @@ -4107,7 +2573,7 @@ void Curl_free_request_state(struct SessionHandle *data) * Checks if the host is in the noproxy list. returns true if it matches * and therefore the proxy should NOT be used. ****************************************************************/ -static bool check_noproxy(const char* name, const char* no_proxy) +static bool check_noproxy(const char *name, const char *no_proxy) { /* no_proxy=domain1.dom,host.domain2.dom * (a comma-separated list of hosts which should @@ -4116,13 +2582,13 @@ static bool check_noproxy(const char* name, const char* no_proxy) */ size_t tok_start; size_t tok_end; - const char* separator = ", "; + const char *separator = ", "; size_t no_proxy_len; size_t namelen; char *endptr; if(no_proxy && no_proxy[0]) { - if(Curl_raw_equal("*", no_proxy)) { + if(strcasecompare("*", no_proxy)) { return TRUE; } @@ -4160,7 +2626,7 @@ static bool check_noproxy(const char* name, const char* no_proxy) if((tok_end - tok_start) <= namelen) { /* Match the last part of the name to the domain we are checking. */ const char *checkn = name + namelen - (tok_end - tok_start); - if(Curl_raw_nequal(no_proxy + tok_start, checkn, + if(strncasecompare(no_proxy + tok_start, checkn, tok_end - tok_start)) { if((tok_end - tok_start) == namelen || *(checkn - 1) == '.') { /* We either have an exact match, or the previous character is a . @@ -4177,6 +2643,7 @@ static bool check_noproxy(const char* name, const char* no_proxy) return FALSE; } +#ifndef CURL_DISABLE_HTTP /**************************************************************** * Detect what (if any) proxy to use. Remember that this selects a host * name and is not limited to HTTP proxies only. @@ -4186,7 +2653,6 @@ static char *detect_proxy(struct connectdata *conn) { char *proxy = NULL; -#ifndef CURL_DISABLE_HTTP /* If proxy was not specified, we check for default proxy environment * variables, to enable i.e Lynx compliance: * @@ -4204,76 +2670,59 @@ static char *detect_proxy(struct connectdata *conn) * For compatibility, the all-uppercase versions of these variables are * checked if the lowercase versions don't exist. */ - char *no_proxy=NULL; char proxy_env[128]; + const char *protop = conn->handler->scheme; + char *envp = proxy_env; + char *prox; - no_proxy=curl_getenv("no_proxy"); - if(!no_proxy) - no_proxy=curl_getenv("NO_PROXY"); + /* Now, build <protocol>_proxy and check for such a one to use */ + while(*protop) + *envp++ = (char)tolower((int)*protop++); - if(!check_noproxy(conn->host.name, no_proxy)) { - /* It was not listed as without proxy */ - const char *protop = conn->handler->scheme; - char *envp = proxy_env; - char *prox; + /* append _proxy */ + strcpy(envp, "_proxy"); - /* Now, build <protocol>_proxy and check for such a one to use */ - while(*protop) - *envp++ = (char)tolower((int)*protop++); + /* read the protocol proxy: */ + prox = curl_getenv(proxy_env); - /* append _proxy */ - strcpy(envp, "_proxy"); - - /* read the protocol proxy: */ - prox=curl_getenv(proxy_env); - - /* - * We don't try the uppercase version of HTTP_PROXY because of - * security reasons: - * - * When curl is used in a webserver application - * environment (cgi or php), this environment variable can - * be controlled by the web server user by setting the - * http header 'Proxy:' to some value. - * - * This can cause 'internal' http/ftp requests to be - * arbitrarily redirected by any external attacker. - */ - if(!prox && !Curl_raw_equal("http_proxy", proxy_env)) { - /* There was no lowercase variable, try the uppercase version: */ - Curl_strntoupper(proxy_env, proxy_env, sizeof(proxy_env)); - prox=curl_getenv(proxy_env); - } - - if(prox && *prox) { /* don't count "" strings */ - proxy = prox; /* use this */ - } - else { - proxy = curl_getenv("all_proxy"); /* default proxy to use */ - if(!proxy) - proxy=curl_getenv("ALL_PROXY"); - } - } /* if(!check_noproxy(conn->host.name, no_proxy)) - it wasn't specified - non-proxy */ - if(no_proxy) - free(no_proxy); - -#else /* !CURL_DISABLE_HTTP */ + /* + * We don't try the uppercase version of HTTP_PROXY because of + * security reasons: + * + * When curl is used in a webserver application + * environment (cgi or php), this environment variable can + * be controlled by the web server user by setting the + * http header 'Proxy:' to some value. + * + * This can cause 'internal' http/ftp requests to be + * arbitrarily redirected by any external attacker. + */ + if(!prox && !strcasecompare("http_proxy", proxy_env)) { + /* There was no lowercase variable, try the uppercase version: */ + Curl_strntoupper(proxy_env, proxy_env, sizeof(proxy_env)); + prox = curl_getenv(proxy_env); + } - (void)conn; -#endif /* CURL_DISABLE_HTTP */ + if(prox) + proxy = prox; /* use this */ + else { + proxy = curl_getenv("all_proxy"); /* default proxy to use */ + if(!proxy) + proxy = curl_getenv("ALL_PROXY"); + } return proxy; } +#endif /* CURL_DISABLE_HTTP */ /* * If this is supposed to use a proxy, we need to figure out the proxy * host name, so that we can re-use an existing connection * that may exist registered to the same proxy host. - * proxy will be freed before this function returns. */ -static CURLcode parse_proxy(struct SessionHandle *data, - struct connectdata *conn, char *proxy) +static CURLcode parse_proxy(struct Curl_easy *data, + struct connectdata *conn, char *proxy, + curl_proxytype proxytype) { char *prox_portno; char *endofprot; @@ -4282,6 +2731,10 @@ static CURLcode parse_proxy(struct SessionHandle *data, char *proxyptr; char *portptr; char *atsign; + long port = -1; + char *proxyuser = NULL; + char *proxypasswd = NULL; + bool sockstype; /* We do the proxy host string parsing here. We want the host name and the * port name. Accept a protocol:// prefix @@ -4290,80 +2743,71 @@ static CURLcode parse_proxy(struct SessionHandle *data, /* Parse the protocol part if present */ endofprot = strstr(proxy, "://"); if(endofprot) { - proxyptr = endofprot+3; - if(checkprefix("socks5h", proxy)) - conn->proxytype = CURLPROXY_SOCKS5_HOSTNAME; + proxyptr = endofprot + 3; + if(checkprefix("https", proxy)) + proxytype = CURLPROXY_HTTPS; + else if(checkprefix("socks5h", proxy)) + proxytype = CURLPROXY_SOCKS5_HOSTNAME; else if(checkprefix("socks5", proxy)) - conn->proxytype = CURLPROXY_SOCKS5; + proxytype = CURLPROXY_SOCKS5; else if(checkprefix("socks4a", proxy)) - conn->proxytype = CURLPROXY_SOCKS4A; + proxytype = CURLPROXY_SOCKS4A; else if(checkprefix("socks4", proxy) || checkprefix("socks", proxy)) - conn->proxytype = CURLPROXY_SOCKS4; - /* Any other xxx:// : change to http proxy */ + proxytype = CURLPROXY_SOCKS4; + else if(checkprefix("http:", proxy)) + ; /* leave it as HTTP or HTTP/1.0 */ + else { + /* Any other xxx:// reject! */ + failf(data, "Unsupported proxy scheme for \'%s\'", proxy); + return CURLE_COULDNT_CONNECT; + } } else proxyptr = proxy; /* No xxx:// head: It's a HTTP proxy */ - /* Is there a username and password given in this proxy url? */ - atsign = strchr(proxyptr, '@'); - if(atsign) { - CURLcode res = CURLE_OK; - char *proxyuser = NULL; - char *proxypasswd = NULL; - - res = parse_login_details(proxyptr, atsign - proxyptr, - &proxyuser, &proxypasswd, NULL); - if(!res) { - /* found user and password, rip them out. note that we are - unescaping them, as there is otherwise no way to have a - username or password with reserved characters like ':' in - them. */ - Curl_safefree(conn->proxyuser); - if(proxyuser && strlen(proxyuser) < MAX_CURL_USER_LENGTH) - conn->proxyuser = curl_easy_unescape(data, proxyuser, 0, NULL); - else - conn->proxyuser = strdup(""); - - if(!conn->proxyuser) - res = CURLE_OUT_OF_MEMORY; - else { - Curl_safefree(conn->proxypasswd); - if(proxypasswd && strlen(proxypasswd) < MAX_CURL_PASSWORD_LENGTH) - conn->proxypasswd = curl_easy_unescape(data, proxypasswd, 0, NULL); - else - conn->proxypasswd = strdup(""); - - if(!conn->proxypasswd) - res = CURLE_OUT_OF_MEMORY; - } - - if(!res) { - conn->bits.proxy_user_passwd = TRUE; /* enable it */ - atsign++; /* the right side of the @-letter */ - - if(atsign) - proxyptr = atsign; /* now use this instead */ - else - res = CURLE_OUT_OF_MEMORY; - } +#ifdef USE_SSL + if(!Curl_ssl->support_https_proxy) +#endif + if(proxytype == CURLPROXY_HTTPS) { + failf(data, "Unsupported proxy \'%s\', libcurl is built without the " + "HTTPS-proxy support.", proxy); + return CURLE_NOT_BUILT_IN; } - Curl_safefree(proxyuser); - Curl_safefree(proxypasswd); + sockstype = proxytype == CURLPROXY_SOCKS5_HOSTNAME || + proxytype == CURLPROXY_SOCKS5 || + proxytype == CURLPROXY_SOCKS4A || + proxytype == CURLPROXY_SOCKS4; - if(res) - return res; + /* Is there a username and password given in this proxy url? */ + atsign = strchr(proxyptr, '@'); + if(atsign) { + CURLcode result = + Curl_parse_login_details(proxyptr, atsign - proxyptr, + &proxyuser, &proxypasswd, NULL); + if(result) + return result; + proxyptr = atsign + 1; } /* start scanning for port number at this point */ portptr = proxyptr; - /* detect and extract RFC2732-style IPv6-addresses */ + /* detect and extract RFC6874-style IPv6-addresses */ if(*proxyptr == '[') { char *ptr = ++proxyptr; /* advance beyond the initial bracket */ - while(*ptr && (ISXDIGIT(*ptr) || (*ptr == ':') || (*ptr == '%') || - (*ptr == '.'))) + while(*ptr && (ISXDIGIT(*ptr) || (*ptr == ':') || (*ptr == '.'))) ptr++; + if(*ptr == '%') { + /* There might be a zone identifier */ + if(strncmp("%25", ptr, 3)) + infof(data, "Please URL encode %% as %%25, see RFC 6874.\n"); + ptr++; + /* Allow unreserved characters as defined in RFC 3986 */ + while(*ptr && (ISALPHA(*ptr) || ISXDIGIT(*ptr) || (*ptr == '-') || + (*ptr == '.') || (*ptr == '_') || (*ptr == '~'))) + ptr++; + } if(*ptr == ']') /* yeps, it ended nicely with a bracket as well */ *ptr++ = 0; @@ -4379,36 +2823,101 @@ static CURLcode parse_proxy(struct SessionHandle *data, /* Get port number off proxy.server.com:1080 */ prox_portno = strchr(portptr, ':'); if(prox_portno) { + char *endp = NULL; + *prox_portno = 0x0; /* cut off number from host name */ prox_portno ++; /* now set the local port number */ - conn->port = strtol(prox_portno, NULL, 10); + port = strtol(prox_portno, &endp, 10); + if((endp && *endp && (*endp != '/') && (*endp != ' ')) || + (port < 0) || (port > 65535)) { + /* meant to detect for example invalid IPv6 numerical addresses without + brackets: "2a00:fac0:a000::7:13". Accept a trailing slash only + because we then allow "URL style" with the number followed by a + slash, used in curl test cases already. Space is also an acceptable + terminating symbol. */ + infof(data, "No valid port number in proxy string (%s)\n", + prox_portno); + } + else + conn->port = port; } else { - if(proxyptr[0]=='/') + if(proxyptr[0]=='/') { /* If the first character in the proxy string is a slash, fail immediately. The following code will otherwise clear the string which will lead to code running as if no proxy was set! */ + Curl_safefree(proxyuser); + Curl_safefree(proxypasswd); return CURLE_COULDNT_RESOLVE_PROXY; + } /* without a port number after the host name, some people seem to use a slash so we strip everything from the first slash */ atsign = strchr(proxyptr, '/'); if(atsign) - *atsign = 0x0; /* cut off path part from host name */ + *atsign = '\0'; /* cut off path part from host name */ if(data->set.proxyport) /* None given in the proxy string, then get the default one if it is given */ - conn->port = data->set.proxyport; + port = data->set.proxyport; + else { + if(proxytype == CURLPROXY_HTTPS) + port = CURL_DEFAULT_HTTPS_PROXY_PORT; + else + port = CURL_DEFAULT_PROXY_PORT; + } } - /* now, clone the cleaned proxy host name */ - conn->proxy.rawalloc = strdup(proxyptr); - conn->proxy.name = conn->proxy.rawalloc; + if(*proxyptr) { + struct proxy_info *proxyinfo = + sockstype ? &conn->socks_proxy : &conn->http_proxy; + proxyinfo->proxytype = proxytype; + + if(proxyuser) { + /* found user and password, rip them out. note that we are unescaping + them, as there is otherwise no way to have a username or password + with reserved characters like ':' in them. */ + Curl_safefree(proxyinfo->user); + proxyinfo->user = curl_easy_unescape(data, proxyuser, 0, NULL); + Curl_safefree(proxyuser); + + if(!proxyinfo->user) { + Curl_safefree(proxypasswd); + return CURLE_OUT_OF_MEMORY; + } - if(!conn->proxy.rawalloc) - return CURLE_OUT_OF_MEMORY; + Curl_safefree(proxyinfo->passwd); + if(proxypasswd && strlen(proxypasswd) < MAX_CURL_PASSWORD_LENGTH) + proxyinfo->passwd = curl_easy_unescape(data, proxypasswd, 0, NULL); + else + proxyinfo->passwd = strdup(""); + Curl_safefree(proxypasswd); + + if(!proxyinfo->passwd) + return CURLE_OUT_OF_MEMORY; + + conn->bits.proxy_user_passwd = TRUE; /* enable it */ + } + + if(port >= 0) { + proxyinfo->port = port; + if(conn->port < 0 || sockstype || !conn->socks_proxy.host.rawalloc) + conn->port = port; + } + + /* now, clone the cleaned proxy host name */ + Curl_safefree(proxyinfo->host.rawalloc); + proxyinfo->host.rawalloc = strdup(proxyptr); + proxyinfo->host.name = proxyinfo->host.rawalloc; + + if(!proxyinfo->host.rawalloc) + return CURLE_OUT_OF_MEMORY; + } + + Curl_safefree(proxyuser); + Curl_safefree(proxypasswd); return CURLE_OK; } @@ -4416,11 +2925,12 @@ static CURLcode parse_proxy(struct SessionHandle *data, /* * Extract the user and password from the authentication string */ -static CURLcode parse_proxy_auth(struct SessionHandle *data, +static CURLcode parse_proxy_auth(struct Curl_easy *data, struct connectdata *conn) { char proxyuser[MAX_CURL_USER_LENGTH]=""; char proxypasswd[MAX_CURL_PASSWORD_LENGTH]=""; + CURLcode result; if(data->set.str[STRING_PROXYUSERNAME] != NULL) { strncpy(proxyuser, data->set.str[STRING_PROXYUSERNAME], @@ -4433,15 +2943,176 @@ static CURLcode parse_proxy_auth(struct SessionHandle *data, proxypasswd[MAX_CURL_PASSWORD_LENGTH-1] = '\0'; /*To be on safe side*/ } - conn->proxyuser = curl_easy_unescape(data, proxyuser, 0, NULL); - if(!conn->proxyuser) - return CURLE_OUT_OF_MEMORY; + result = Curl_urldecode(data, proxyuser, 0, &conn->http_proxy.user, NULL, + FALSE); + if(!result) + result = Curl_urldecode(data, proxypasswd, 0, &conn->http_proxy.passwd, + NULL, FALSE); + return result; +} - conn->proxypasswd = curl_easy_unescape(data, proxypasswd, 0, NULL); - if(!conn->proxypasswd) - return CURLE_OUT_OF_MEMORY; +/* create_conn helper to parse and init proxy values. to be called after unix + socket init but before any proxy vars are evaluated. */ +static CURLcode create_conn_helper_init_proxy(struct connectdata *conn) +{ + char *proxy = NULL; + char *socksproxy = NULL; + char *no_proxy = NULL; + CURLcode result = CURLE_OK; + struct Curl_easy *data = conn->data; - return CURLE_OK; + /************************************************************* + * Extract the user and password from the authentication string + *************************************************************/ + if(conn->bits.proxy_user_passwd) { + result = parse_proxy_auth(data, conn); + if(result) + goto out; + } + + /************************************************************* + * Detect what (if any) proxy to use + *************************************************************/ + if(data->set.str[STRING_PROXY]) { + proxy = strdup(data->set.str[STRING_PROXY]); + /* if global proxy is set, this is it */ + if(NULL == proxy) { + failf(data, "memory shortage"); + result = CURLE_OUT_OF_MEMORY; + goto out; + } + } + + if(data->set.str[STRING_PRE_PROXY]) { + socksproxy = strdup(data->set.str[STRING_PRE_PROXY]); + /* if global socks proxy is set, this is it */ + if(NULL == socksproxy) { + failf(data, "memory shortage"); + result = CURLE_OUT_OF_MEMORY; + goto out; + } + } + + if(!data->set.str[STRING_NOPROXY]) { + no_proxy = curl_getenv("no_proxy"); + if(!no_proxy) + no_proxy = curl_getenv("NO_PROXY"); + } + + if(check_noproxy(conn->host.name, data->set.str[STRING_NOPROXY] ? + data->set.str[STRING_NOPROXY] : no_proxy)) { + Curl_safefree(proxy); + Curl_safefree(socksproxy); + } +#ifndef CURL_DISABLE_HTTP + else if(!proxy && !socksproxy) + /* if the host is not in the noproxy list, detect proxy. */ + proxy = detect_proxy(conn); +#endif /* CURL_DISABLE_HTTP */ + + Curl_safefree(no_proxy); + +#ifdef USE_UNIX_SOCKETS + /* For the time being do not mix proxy and unix domain sockets. See #1274 */ + if(proxy && conn->unix_domain_socket) { + free(proxy); + proxy = NULL; + } +#endif + + if(proxy && (!*proxy || (conn->handler->flags & PROTOPT_NONETWORK))) { + free(proxy); /* Don't bother with an empty proxy string or if the + protocol doesn't work with network */ + proxy = NULL; + } + if(socksproxy && (!*socksproxy || + (conn->handler->flags & PROTOPT_NONETWORK))) { + free(socksproxy); /* Don't bother with an empty socks proxy string or if + the protocol doesn't work with network */ + socksproxy = NULL; + } + + /*********************************************************************** + * If this is supposed to use a proxy, we need to figure out the proxy host + * name, proxy type and port number, so that we can re-use an existing + * connection that may exist registered to the same proxy host. + ***********************************************************************/ + if(proxy || socksproxy) { + if(proxy) { + result = parse_proxy(data, conn, proxy, conn->http_proxy.proxytype); + Curl_safefree(proxy); /* parse_proxy copies the proxy string */ + if(result) + goto out; + } + + if(socksproxy) { + result = parse_proxy(data, conn, socksproxy, + conn->socks_proxy.proxytype); + /* parse_proxy copies the socks proxy string */ + Curl_safefree(socksproxy); + if(result) + goto out; + } + + if(conn->http_proxy.host.rawalloc) { +#ifdef CURL_DISABLE_HTTP + /* asking for a HTTP proxy is a bit funny when HTTP is disabled... */ + result = CURLE_UNSUPPORTED_PROTOCOL; + goto out; +#else + /* force this connection's protocol to become HTTP if compatible */ + if(!(conn->handler->protocol & PROTO_FAMILY_HTTP)) { + if((conn->handler->flags & PROTOPT_PROXY_AS_HTTP) && + !conn->bits.tunnel_proxy) + conn->handler = &Curl_handler_http; + else + /* if not converting to HTTP over the proxy, enforce tunneling */ + conn->bits.tunnel_proxy = TRUE; + } + conn->bits.httpproxy = TRUE; +#endif + } + else { + conn->bits.httpproxy = FALSE; /* not a HTTP proxy */ + conn->bits.tunnel_proxy = FALSE; /* no tunneling if not HTTP */ + } + + if(conn->socks_proxy.host.rawalloc) { + if(!conn->http_proxy.host.rawalloc) { + /* once a socks proxy */ + if(!conn->socks_proxy.user) { + conn->socks_proxy.user = conn->http_proxy.user; + conn->http_proxy.user = NULL; + Curl_safefree(conn->socks_proxy.passwd); + conn->socks_proxy.passwd = conn->http_proxy.passwd; + conn->http_proxy.passwd = NULL; + } + } + conn->bits.socksproxy = TRUE; + } + else + conn->bits.socksproxy = FALSE; /* not a socks proxy */ + } + else { + conn->bits.socksproxy = FALSE; + conn->bits.httpproxy = FALSE; + } + conn->bits.proxy = conn->bits.httpproxy || conn->bits.socksproxy; + + if(!conn->bits.proxy) { + /* we aren't using the proxy after all... */ + conn->bits.proxy = FALSE; + conn->bits.httpproxy = FALSE; + conn->bits.socksproxy = FALSE; + conn->bits.proxy_user_passwd = FALSE; + conn->bits.tunnel_proxy = FALSE; + } + +out: + + free(socksproxy); + free(proxy); + return result; } #endif /* CURL_DISABLE_PROXY */ @@ -4461,7 +3132,7 @@ static CURLcode parse_proxy_auth(struct SessionHandle *data, * options - non-zero length if defined * conn->host.name - remove user name and password */ -static CURLcode parse_url_login(struct SessionHandle *data, +static CURLcode parse_url_login(struct Curl_easy *data, struct connectdata *conn, char **user, char **passwd, char **options) { @@ -4483,6 +3154,7 @@ static CURLcode parse_url_login(struct SessionHandle *data, DEBUGASSERT(!**user); DEBUGASSERT(!**passwd); DEBUGASSERT(!**options); + DEBUGASSERT(conn->handler); if(!ptr) goto out; @@ -4501,10 +3173,14 @@ static CURLcode parse_url_login(struct SessionHandle *data, if(data->set.use_netrc == CURL_NETRC_REQUIRED) goto out; - /* We could use the login information in the URL so extract it */ - result = parse_login_details(login, ptr - login - 1, - &userp, &passwdp, &optionsp); - if(result != CURLE_OK) + /* We could use the login information in the URL so extract it. Only parse + options if the handler says we should. */ + result = + Curl_parse_login_details(login, ptr - login - 1, + &userp, &passwdp, + (conn->handler->flags & PROTOPT_URLOPTIONS)? + &optionsp:NULL); + if(result) goto out; if(userp) { @@ -4515,9 +3191,8 @@ static CURLcode parse_url_login(struct SessionHandle *data, conn->bits.user_passwd = TRUE; /* enable user+password */ /* Decode the user */ - newname = curl_easy_unescape(data, userp, 0, NULL); - if(!newname) { - result = CURLE_OUT_OF_MEMORY; + result = Curl_urldecode(data, userp, 0, &newname, NULL, FALSE); + if(result) { goto out; } @@ -4527,9 +3202,9 @@ static CURLcode parse_url_login(struct SessionHandle *data, if(passwdp) { /* We have a password in the URL so decode it */ - char *newpasswd = curl_easy_unescape(data, passwdp, 0, NULL); - if(!newpasswd) { - result = CURLE_OUT_OF_MEMORY; + char *newpasswd; + result = Curl_urldecode(data, passwdp, 0, &newpasswd, NULL, FALSE); + if(result) { goto out; } @@ -4539,9 +3214,9 @@ static CURLcode parse_url_login(struct SessionHandle *data, if(optionsp) { /* We have an options list in the URL so decode it */ - char *newoptions = curl_easy_unescape(data, optionsp, 0, NULL); - if(!newoptions) { - result = CURLE_OUT_OF_MEMORY; + char *newoptions; + result = Curl_urldecode(data, optionsp, 0, &newoptions, NULL, FALSE); + if(result) { goto out; } @@ -4552,15 +3227,15 @@ static CURLcode parse_url_login(struct SessionHandle *data, out: - Curl_safefree(userp); - Curl_safefree(passwdp); - Curl_safefree(optionsp); + free(userp); + free(passwdp); + free(optionsp); return result; } /* - * parse_login_details() + * Curl_parse_login_details() * * This is used to parse a login string for user name, password and options in * the following formats: @@ -4588,9 +3263,9 @@ static CURLcode parse_url_login(struct SessionHandle *data, * * Returns CURLE_OK on success. */ -static CURLcode parse_login_details(const char *login, const size_t len, - char **userp, char **passwdp, - char **optionsp) +CURLcode Curl_parse_login_details(const char *login, const size_t len, + char **userp, char **passwdp, + char **optionsp) { CURLcode result = CURLE_OK; char *ubuf = NULL; @@ -4642,7 +3317,7 @@ static CURLcode parse_login_details(const char *login, const size_t len, if(!result && passwdp && plen) { pbuf = malloc(plen + 1); if(!pbuf) { - Curl_safefree(ubuf); + free(ubuf); result = CURLE_OUT_OF_MEMORY; } } @@ -4651,8 +3326,8 @@ static CURLcode parse_login_details(const char *login, const size_t len, if(!result && optionsp && olen) { obuf = malloc(olen + 1); if(!obuf) { - Curl_safefree(pbuf); - Curl_safefree(ubuf); + free(pbuf); + free(ubuf); result = CURLE_OUT_OF_MEMORY; } } @@ -4700,7 +3375,7 @@ static CURLcode parse_login_details(const char *login, const size_t len, * * The port number embedded in the URL is replaced, if necessary. *************************************************************/ -static CURLcode parse_remote_port(struct SessionHandle *data, +static CURLcode parse_remote_port(struct Curl_easy *data, struct connectdata *conn) { char *portptr; @@ -4719,7 +3394,13 @@ static CURLcode parse_remote_port(struct SessionHandle *data, portptr = strchr(conn->host.name, ']'); if(portptr) { *portptr++ = '\0'; /* zero terminate, killing the bracket */ - if(':' != *portptr) + if(*portptr) { + if (*portptr != ':') { + failf(data, "IPv6 closing bracket followed by '%c'", *portptr); + return CURLE_URL_MALFORMAT; + } + } + else portptr = NULL; /* no port number available */ } } @@ -4734,7 +3415,7 @@ static CURLcode parse_remote_port(struct SessionHandle *data, } #endif - portptr = strrchr(conn->host.name, ':'); + portptr = strchr(conn->host.name, ':'); } if(data->set.use_port && data->state.allow_port) { @@ -4779,29 +3460,38 @@ static CURLcode parse_remote_port(struct SessionHandle *data, /* no CURLOPT_PORT given, extract the one from the URL */ char *rest; - unsigned long port; + long port; - port=strtoul(portptr+1, &rest, 10); /* Port number must be decimal */ + port = strtol(portptr + 1, &rest, 10); /* Port number must be decimal */ - if(rest != (portptr+1) && *rest == '\0') { - /* The colon really did have only digits after it, - * so it is either a port number or a mistake */ + if((port < 0) || (port > 0xffff)) { + /* Single unix standard says port numbers are 16 bits long */ + failf(data, "Port number out of range"); + return CURLE_URL_MALFORMAT; + } - if(port > 0xffff) { /* Single unix standard says port numbers are - * 16 bits long */ - failf(data, "Port number too large: %lu", port); - return CURLE_URL_MALFORMAT; - } + if(rest[0]) { + failf(data, "Port number ended with '%c'", rest[0]); + return CURLE_URL_MALFORMAT; + } + if(rest != &portptr[1]) { *portptr = '\0'; /* cut off the name there */ conn->remote_port = curlx_ultous(port); } - else if(!port) + else { /* Browser behavior adaptation. If there's a colon with no digits after, just cut off the name there which makes us ignore the colon and just use the default port. Firefox and Chrome both do that. */ *portptr = '\0'; + } } + + /* only if remote_port was not already parsed off the URL we use the + default port number */ + if(conn->remote_port < 0) + conn->remote_port = (unsigned short)conn->given->defport; + return CURLE_OK; } @@ -4809,7 +3499,7 @@ static CURLcode parse_remote_port(struct SessionHandle *data, * Override the login details from the URL with that in the CURLOPT_USERPWD * option or a .netrc file, if applicable. */ -static CURLcode override_login(struct SessionHandle *data, +static CURLcode override_login(struct Curl_easy *data, struct connectdata *conn, char **userp, char **passwdp, char **optionsp) { @@ -4836,13 +3526,17 @@ static CURLcode override_login(struct SessionHandle *data, conn->bits.netrc = FALSE; if(data->set.use_netrc != CURL_NETRC_IGNORED) { - if(Curl_parsenetrc(conn->host.name, - userp, passwdp, - data->set.str[STRING_NETRC_FILE])) { + int ret = Curl_parsenetrc(conn->host.name, + userp, passwdp, + data->set.str[STRING_NETRC_FILE]); + if(ret > 0) { infof(data, "Couldn't find host %s in the " DOT_CHAR "netrc file; using defaults\n", conn->host.name); } + else if(ret < 0) { + return CURLE_OUT_OF_MEMORY; + } else { /* set bits.netrc TRUE to remember that we got the name from a .netrc file, so that it is safe to use even if we followed a Location: to a @@ -4857,7 +3551,7 @@ static CURLcode override_login(struct SessionHandle *data, } /* - * Set password so it's available in the connection. + * Set the login details so they're available in the connection */ static CURLcode set_login(struct connectdata *conn, const char *user, const char *passwd, @@ -4903,15 +3597,229 @@ static CURLcode set_login(struct connectdata *conn, return result; } +/* + * Parses a "host:port" string to connect to. + * The hostname and the port may be empty; in this case, NULL is returned for + * the hostname and -1 for the port. + */ +static CURLcode parse_connect_to_host_port(struct Curl_easy *data, + const char *host, + char **hostname_result, + int *port_result) +{ + char *host_dup; + char *hostptr; + char *host_portno; + char *portptr; + int port = -1; + +#if defined(CURL_DISABLE_VERBOSE_STRINGS) + (void) data; +#endif + + *hostname_result = NULL; + *port_result = -1; + + if(!host || !*host) + return CURLE_OK; + + host_dup = strdup(host); + if(!host_dup) + return CURLE_OUT_OF_MEMORY; + + hostptr = host_dup; + + /* start scanning for port number at this point */ + portptr = hostptr; + + /* detect and extract RFC6874-style IPv6-addresses */ + if(*hostptr == '[') { + char *ptr = ++hostptr; /* advance beyond the initial bracket */ + while(*ptr && (ISXDIGIT(*ptr) || (*ptr == ':') || (*ptr == '.'))) + ptr++; + if(*ptr == '%') { + /* There might be a zone identifier */ + if(strncmp("%25", ptr, 3)) + infof(data, "Please URL encode %% as %%25, see RFC 6874.\n"); + ptr++; + /* Allow unreserved characters as defined in RFC 3986 */ + while(*ptr && (ISALPHA(*ptr) || ISXDIGIT(*ptr) || (*ptr == '-') || + (*ptr == '.') || (*ptr == '_') || (*ptr == '~'))) + ptr++; + } + if(*ptr == ']') + /* yeps, it ended nicely with a bracket as well */ + *ptr++ = '\0'; + else + infof(data, "Invalid IPv6 address format\n"); + portptr = ptr; + /* Note that if this didn't end with a bracket, we still advanced the + * hostptr first, but I can't see anything wrong with that as no host + * name nor a numeric can legally start with a bracket. + */ + } + + /* Get port number off server.com:1080 */ + host_portno = strchr(portptr, ':'); + if(host_portno) { + char *endp = NULL; + *host_portno = '\0'; /* cut off number from host name */ + host_portno++; + if(*host_portno) { + long portparse = strtol(host_portno, &endp, 10); + if((endp && *endp) || (portparse < 0) || (portparse > 65535)) { + infof(data, "No valid port number in connect to host string (%s)\n", + host_portno); + hostptr = NULL; + port = -1; + } + else + port = (int)portparse; /* we know it will fit */ + } + } + + /* now, clone the cleaned host name */ + if(hostptr) { + *hostname_result = strdup(hostptr); + if(!*hostname_result) { + free(host_dup); + return CURLE_OUT_OF_MEMORY; + } + } + + *port_result = port; + + free(host_dup); + return CURLE_OK; +} + +/* + * Parses one "connect to" string in the form: + * "HOST:PORT:CONNECT-TO-HOST:CONNECT-TO-PORT". + */ +static CURLcode parse_connect_to_string(struct Curl_easy *data, + struct connectdata *conn, + const char *conn_to_host, + char **host_result, + int *port_result) +{ + CURLcode result = CURLE_OK; + const char *ptr = conn_to_host; + int host_match = FALSE; + int port_match = FALSE; + + *host_result = NULL; + *port_result = -1; + + if(*ptr == ':') { + /* an empty hostname always matches */ + host_match = TRUE; + ptr++; + } + else { + /* check whether the URL's hostname matches */ + size_t hostname_to_match_len; + char *hostname_to_match = aprintf("%s%s%s", + conn->bits.ipv6_ip ? "[" : "", + conn->host.name, + conn->bits.ipv6_ip ? "]" : ""); + if(!hostname_to_match) + return CURLE_OUT_OF_MEMORY; + hostname_to_match_len = strlen(hostname_to_match); + host_match = strncasecompare(ptr, hostname_to_match, + hostname_to_match_len); + free(hostname_to_match); + ptr += hostname_to_match_len; + + host_match = host_match && *ptr == ':'; + ptr++; + } + + if(host_match) { + if(*ptr == ':') { + /* an empty port always matches */ + port_match = TRUE; + ptr++; + } + else { + /* check whether the URL's port matches */ + char *ptr_next = strchr(ptr, ':'); + if(ptr_next) { + char *endp = NULL; + long port_to_match = strtol(ptr, &endp, 10); + if((endp == ptr_next) && (port_to_match == conn->remote_port)) { + port_match = TRUE; + ptr = ptr_next + 1; + } + } + } + } + + if(host_match && port_match) { + /* parse the hostname and port to connect to */ + result = parse_connect_to_host_port(data, ptr, host_result, port_result); + } + + return result; +} + +/* + * Processes all strings in the "connect to" slist, and uses the "connect + * to host" and "connect to port" of the first string that matches. + */ +static CURLcode parse_connect_to_slist(struct Curl_easy *data, + struct connectdata *conn, + struct curl_slist *conn_to_host) +{ + CURLcode result = CURLE_OK; + char *host = NULL; + int port = -1; + + while(conn_to_host && !host && port == -1) { + result = parse_connect_to_string(data, conn, conn_to_host->data, + &host, &port); + if(result) + return result; + + if(host && *host) { + conn->conn_to_host.rawalloc = host; + conn->conn_to_host.name = host; + conn->bits.conn_to_host = TRUE; + + infof(data, "Connecting to hostname: %s\n", host); + } + else { + /* no "connect to host" */ + conn->bits.conn_to_host = FALSE; + Curl_safefree(host); + } + + if(port >= 0) { + conn->conn_to_port = port; + conn->bits.conn_to_port = TRUE; + infof(data, "Connecting to port: %d\n", port); + } + else { + /* no "connect to port" */ + conn->bits.conn_to_port = FALSE; + port = -1; + } + + conn_to_host = conn_to_host->next; + } + + return result; +} + /************************************************************* * Resolve the address of the server or proxy *************************************************************/ -static CURLcode resolve_server(struct SessionHandle *data, +static CURLcode resolve_server(struct Curl_easy *data, struct connectdata *conn, bool *async) { - CURLcode result=CURLE_OK; - long timeout_ms = Curl_timeleft(data, NULL, TRUE); + CURLcode result = CURLE_OK; + timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE); /************************************************************* * Resolve the name of the server or proxy @@ -4927,16 +3835,53 @@ static CURLcode resolve_server(struct SessionHandle *data, int rc; struct Curl_dns_entry *hostaddr; - /* set a pointer to the hostname we display */ - fix_hostname(data, conn, &conn->host); +#ifdef USE_UNIX_SOCKETS + if(conn->unix_domain_socket) { + /* Unix domain sockets are local. The host gets ignored, just use the + * specified domain socket address. Do not cache "DNS entries". There is + * no DNS involved and we already have the filesystem path available */ + const char *path = conn->unix_domain_socket; + + hostaddr = calloc(1, sizeof(struct Curl_dns_entry)); + if(!hostaddr) + result = CURLE_OUT_OF_MEMORY; + else { + bool longpath = FALSE; + hostaddr->addr = Curl_unix2addr(path, &longpath, + conn->abstract_unix_socket); + if(hostaddr->addr) + hostaddr->inuse++; + else { + /* Long paths are not supported for now */ + if(longpath) { + failf(data, "Unix socket path too long: '%s'", path); + result = CURLE_COULDNT_RESOLVE_HOST; + } + else + result = CURLE_OUT_OF_MEMORY; + free(hostaddr); + hostaddr = NULL; + } + } + } + else +#endif + if(!conn->bits.proxy) { + struct hostname *connhost; + if(conn->bits.conn_to_host) + connhost = &conn->conn_to_host; + else + connhost = &conn->host; - if(!conn->proxy.name || !*conn->proxy.name) { /* 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->port = conn->remote_port; /* it is the same port */ + if(conn->bits.conn_to_port) + conn->port = conn->conn_to_port; + else + conn->port = conn->remote_port; /* Resolve target host right on */ - rc = Curl_resolv_timeout(conn, conn->host.name, (int)conn->port, + rc = Curl_resolv_timeout(conn, connhost->name, (int)conn->port, &hostaddr, timeout_ms); if(rc == CURLRESOLV_PENDING) *async = TRUE; @@ -4945,7 +3890,7 @@ static CURLcode resolve_server(struct SessionHandle *data, result = CURLE_OPERATION_TIMEDOUT; else if(!hostaddr) { - failf(data, "Couldn't resolve host '%s'", conn->host.dispname); + failf(data, "Couldn't resolve host '%s'", connhost->dispname); result = CURLE_COULDNT_RESOLVE_HOST; /* don't return yet, we need to clean up the timeout first */ } @@ -4953,11 +3898,11 @@ static CURLcode resolve_server(struct SessionHandle *data, else { /* This is a proxy that hasn't been resolved yet. */ - /* IDN-fix the proxy name */ - fix_hostname(data, conn, &conn->proxy); + struct hostname * const host = conn->bits.socksproxy ? + &conn->socks_proxy.host : &conn->http_proxy.host; /* resolve proxy */ - rc = Curl_resolv_timeout(conn, conn->proxy.name, (int)conn->port, + rc = Curl_resolv_timeout(conn, host->name, (int)conn->port, &hostaddr, timeout_ms); if(rc == CURLRESOLV_PENDING) @@ -4967,7 +3912,7 @@ static CURLcode resolve_server(struct SessionHandle *data, result = CURLE_OPERATION_TIMEDOUT; else if(!hostaddr) { - failf(data, "Couldn't resolve proxy '%s'", conn->proxy.dispname); + failf(data, "Couldn't resolve proxy '%s'", host->dispname); result = CURLE_COULDNT_RESOLVE_PROXY; /* don't return yet, we need to clean up the timeout first */ } @@ -4987,12 +3932,16 @@ static CURLcode resolve_server(struct SessionHandle *data, static void reuse_conn(struct connectdata *old_conn, struct connectdata *conn) { - if(old_conn->proxy.rawalloc) - free(old_conn->proxy.rawalloc); + free_fixed_hostname(&old_conn->http_proxy.host); + free_fixed_hostname(&old_conn->socks_proxy.host); + + free(old_conn->http_proxy.host.rawalloc); + free(old_conn->socks_proxy.host.rawalloc); /* free the SSL config struct from this connection struct as this was allocated in vain and is targeted for destruction */ - Curl_free_ssl_config(&old_conn->ssl_config); + Curl_free_primary_ssl_config(&old_conn->ssl_config); + Curl_free_primary_ssl_config(&old_conn->proxy_ssl_config); conn->data = old_conn->data; @@ -5012,38 +3961,55 @@ static void reuse_conn(struct connectdata *old_conn, conn->bits.proxy_user_passwd = old_conn->bits.proxy_user_passwd; if(conn->bits.proxy_user_passwd) { /* use the new proxy user name and proxy password though */ - Curl_safefree(conn->proxyuser); - Curl_safefree(conn->proxypasswd); - conn->proxyuser = old_conn->proxyuser; - conn->proxypasswd = old_conn->proxypasswd; - old_conn->proxyuser = NULL; - old_conn->proxypasswd = NULL; + Curl_safefree(conn->http_proxy.user); + Curl_safefree(conn->socks_proxy.user); + Curl_safefree(conn->http_proxy.passwd); + Curl_safefree(conn->socks_proxy.passwd); + conn->http_proxy.user = old_conn->http_proxy.user; + conn->socks_proxy.user = old_conn->socks_proxy.user; + conn->http_proxy.passwd = old_conn->http_proxy.passwd; + conn->socks_proxy.passwd = old_conn->socks_proxy.passwd; + old_conn->http_proxy.user = NULL; + old_conn->socks_proxy.user = NULL; + old_conn->http_proxy.passwd = NULL; + old_conn->socks_proxy.passwd = NULL; } /* host can change, when doing keepalive with a proxy or if the case is different this time etc */ + free_fixed_hostname(&conn->host); + free_fixed_hostname(&conn->conn_to_host); Curl_safefree(conn->host.rawalloc); - conn->host=old_conn->host; + Curl_safefree(conn->conn_to_host.rawalloc); + conn->host = old_conn->host; + conn->conn_to_host = old_conn->conn_to_host; + conn->conn_to_port = old_conn->conn_to_port; + conn->remote_port = old_conn->remote_port; /* persist connection info in session handle */ Curl_persistconninfo(conn); + conn_reset_all_postponed_data(old_conn); /* free buffers */ + /* re-use init */ conn->bits.reuse = TRUE; /* yes, we're re-using here */ Curl_safefree(old_conn->user); Curl_safefree(old_conn->passwd); - Curl_safefree(old_conn->proxyuser); - Curl_safefree(old_conn->proxypasswd); + Curl_safefree(old_conn->http_proxy.user); + Curl_safefree(old_conn->socks_proxy.user); + Curl_safefree(old_conn->http_proxy.passwd); + Curl_safefree(old_conn->socks_proxy.passwd); Curl_safefree(old_conn->localdev); - Curl_llist_destroy(old_conn->send_pipe, NULL); - Curl_llist_destroy(old_conn->recv_pipe, NULL); - - old_conn->send_pipe = NULL; - old_conn->recv_pipe = NULL; + Curl_llist_destroy(&old_conn->send_pipe, NULL); + Curl_llist_destroy(&old_conn->recv_pipe, NULL); Curl_safefree(old_conn->master_buffer); + +#ifdef USE_UNIX_SOCKETS + Curl_safefree(old_conn->unix_domain_socket); +#endif } /** @@ -5062,7 +4028,7 @@ static void reuse_conn(struct connectdata *old_conn, * *NOTE* this function assigns the conn->data pointer! */ -static CURLcode create_conn(struct SessionHandle *data, +static CURLcode create_conn(struct Curl_easy *data, struct connectdata **in_connect, bool *async) { @@ -5074,10 +4040,10 @@ static CURLcode create_conn(struct SessionHandle *data, char *passwd = NULL; char *options = NULL; bool reuse; - char *proxy = NULL; bool prot_missing = FALSE; - bool no_connections_available = FALSE; - bool force_reuse; + bool connections_available = TRUE; + bool force_reuse = FALSE; + bool waitpipe = FALSE; size_t max_host_connections = Curl_multi_max_host_connections(data->multi); size_t max_total_connections = Curl_multi_max_total_connections(data->multi); @@ -5117,9 +4083,9 @@ static CURLcode create_conn(struct SessionHandle *data, * other parts of the code will rely on this fact ***********************************************************/ #define LEAST_PATH_ALLOC 256 - urllen=strlen(data->change.url); + urllen = strlen(data->change.url); if(urllen < LEAST_PATH_ALLOC) - urllen=LEAST_PATH_ALLOC; + urllen = LEAST_PATH_ALLOC; /* * We malloc() the buffers below urllen+2 to make room for 2 possibilities: @@ -5130,14 +4096,14 @@ static CURLcode create_conn(struct SessionHandle *data, Curl_safefree(data->state.pathbuffer); data->state.path = NULL; - data->state.pathbuffer = malloc(urllen+2); + data->state.pathbuffer = malloc(urllen + 2); if(NULL == data->state.pathbuffer) { result = CURLE_OUT_OF_MEMORY; /* really bad error */ goto out; } data->state.path = data->state.pathbuffer; - conn->host.rawalloc = malloc(urllen+2); + conn->host.rawalloc = malloc(urllen + 2); if(NULL == conn->host.rawalloc) { Curl_safefree(data->state.pathbuffer); data->state.path = NULL; @@ -5158,7 +4124,7 @@ static CURLcode create_conn(struct SessionHandle *data, result = parseurlandfillconn(data, conn, &prot_missing, &user, &passwd, &options); - if(result != CURLE_OK) + if(result) goto out; /************************************************************* @@ -5169,6 +4135,7 @@ static CURLcode create_conn(struct SessionHandle *data, we're gonna follow a Location: later or... then we need the protocol part added so that we have a valid URL. */ char *reurl; + char *ch_lower; reurl = aprintf("%s://%s", conn->handler->scheme, data->change.url); @@ -5177,6 +4144,10 @@ static CURLcode create_conn(struct SessionHandle *data, goto out; } + /* Change protocol prefix to lower-case */ + for(ch_lower = reurl; *ch_lower != ':'; ch_lower++) + *ch_lower = (char)TOLOWER(*ch_lower); + if(data->change.url_alloc) { Curl_safefree(data->change.url); data->change.url_alloc = FALSE; @@ -5188,7 +4159,7 @@ static CURLcode create_conn(struct SessionHandle *data, /************************************************************* * If the protocol can't handle url query strings, then cut - * of the unhandable part + * off the unhandable part *************************************************************/ if((conn->given->flags&PROTOPT_NOURLQUERY)) { char *path_q_sep = strchr(conn->data->state.path, '?'); @@ -5205,93 +4176,31 @@ static CURLcode create_conn(struct SessionHandle *data, } if(data->set.str[STRING_BEARER]) { - conn->xoauth2_bearer = strdup(data->set.str[STRING_BEARER]); - if(!conn->xoauth2_bearer) { - return CURLE_OUT_OF_MEMORY; - } - } - -#ifndef CURL_DISABLE_PROXY - /************************************************************* - * Extract the user and password from the authentication string - *************************************************************/ - if(conn->bits.proxy_user_passwd) { - result = parse_proxy_auth(data, conn); - if(result != CURLE_OK) - goto out; - } - - /************************************************************* - * Detect what (if any) proxy to use - *************************************************************/ - if(data->set.str[STRING_PROXY]) { - proxy = strdup(data->set.str[STRING_PROXY]); - /* if global proxy is set, this is it */ - if(NULL == proxy) { - failf(data, "memory shortage"); + conn->oauth_bearer = strdup(data->set.str[STRING_BEARER]); + if(!conn->oauth_bearer) { result = CURLE_OUT_OF_MEMORY; goto out; } } - if(data->set.str[STRING_NOPROXY] && - check_noproxy(conn->host.name, data->set.str[STRING_NOPROXY])) { - if(proxy) { - free(proxy); /* proxy is in exception list */ - proxy = NULL; - } - } - else if(!proxy) - proxy = detect_proxy(conn); - - if(proxy && (!*proxy || (conn->handler->flags & PROTOPT_NONETWORK))) { - free(proxy); /* Don't bother with an empty proxy string or if the - protocol doesn't work with network */ - proxy = NULL; - } - - /*********************************************************************** - * If this is supposed to use a proxy, we need to figure out the proxy host - * name, proxy type and port number, so that we can re-use an existing - * connection that may exist registered to the same proxy host. - ***********************************************************************/ - if(proxy) { - result = parse_proxy(data, conn, proxy); - - Curl_safefree(proxy); /* parse_proxy copies the proxy string */ - - if(result) - goto out; - - if((conn->proxytype == CURLPROXY_HTTP) || - (conn->proxytype == CURLPROXY_HTTP_1_0)) { -#ifdef CURL_DISABLE_HTTP - /* asking for a HTTP proxy is a bit funny when HTTP is disabled... */ - result = CURLE_UNSUPPORTED_PROTOCOL; +#ifdef USE_UNIX_SOCKETS + if(data->set.str[STRING_UNIX_SOCKET_PATH]) { + conn->unix_domain_socket = strdup(data->set.str[STRING_UNIX_SOCKET_PATH]); + if(conn->unix_domain_socket == NULL) { + result = CURLE_OUT_OF_MEMORY; goto out; -#else - /* force this connection's protocol to become HTTP if not already - compatible - if it isn't tunneling through */ - if(!(conn->handler->protocol & CURLPROTO_HTTP) && - !conn->bits.tunnel_proxy) - conn->handler = &Curl_handler_http; - - conn->bits.httpproxy = TRUE; -#endif } - else - conn->bits.httpproxy = FALSE; /* not a HTTP proxy */ - conn->bits.proxy = TRUE; - } - else { - /* we aren't using the proxy after all... */ - conn->bits.proxy = FALSE; - conn->bits.httpproxy = FALSE; - conn->bits.proxy_user_passwd = FALSE; - conn->bits.tunnel_proxy = FALSE; + conn->abstract_unix_socket = data->set.abstract_unix_socket; } +#endif -#endif /* CURL_DISABLE_PROXY */ + /* After the unix socket init but before the proxy vars are used, parse and + initialize the proxy vars */ +#ifndef CURL_DISABLE_PROXY + result = create_conn_helper_init_proxy(conn); + if(result) + goto out; +#endif /************************************************************* * If the protocol is using SSL and HTTP proxy is used, we set @@ -5304,24 +4213,79 @@ static CURLcode create_conn(struct SessionHandle *data, * Figure out the remote port number and fix it in the URL *************************************************************/ result = parse_remote_port(data, conn); - if(result != CURLE_OK) + if(result) goto out; /* Check for overridden login details and set them accordingly so they they are known when protocol->setup_connection is called! */ result = override_login(data, conn, &user, &passwd, &options); - if(result != CURLE_OK) + if(result) goto out; result = set_login(conn, user, passwd, options); - if(result != CURLE_OK) + if(result) + goto out; + + /************************************************************* + * Process the "connect to" linked list of hostname/port mappings. + * Do this after the remote port number has been fixed in the URL. + *************************************************************/ + result = parse_connect_to_slist(data, conn, data->set.connect_to); + if(result) goto out; /************************************************************* + * IDN-fix the hostnames + *************************************************************/ + result = fix_hostname(conn, &conn->host); + if(result) + goto out; + if(conn->bits.conn_to_host) { + result = fix_hostname(conn, &conn->conn_to_host); + if(result) + goto out; + } + if(conn->bits.httpproxy) { + result = fix_hostname(conn, &conn->http_proxy.host); + if(result) + goto out; + } + if(conn->bits.socksproxy) { + result = fix_hostname(conn, &conn->socks_proxy.host); + if(result) + goto out; + } + + /************************************************************* + * Check whether the host and the "connect to host" are equal. + * Do this after the hostnames have been IDN-fixed. + *************************************************************/ + if(conn->bits.conn_to_host && + strcasecompare(conn->conn_to_host.name, conn->host.name)) { + conn->bits.conn_to_host = FALSE; + } + + /************************************************************* + * Check whether the port and the "connect to port" are equal. + * Do this after the remote port number has been fixed in the URL. + *************************************************************/ + if(conn->bits.conn_to_port && conn->conn_to_port == conn->remote_port) { + conn->bits.conn_to_port = FALSE; + } + + /************************************************************* + * If the "connect to" feature is used with an HTTP proxy, + * we set the tunnel_proxy bit. + *************************************************************/ + if((conn->bits.conn_to_host || conn->bits.conn_to_port) && + conn->bits.httpproxy) + conn->bits.tunnel_proxy = TRUE; + + /************************************************************* * Setup internals depending on protocol. Needs to be done after * we figured out what/if proxy to use. *************************************************************/ result = setup_connection_internals(conn); - if(result != CURLE_OK) + if(result) goto out; conn->recv[FIRSTSOCKET] = Curl_recv_plain; @@ -5329,6 +4293,8 @@ static CURLcode create_conn(struct SessionHandle *data, conn->recv[SECONDARYSOCKET] = Curl_recv_plain; conn->send[SECONDARYSOCKET] = Curl_send_plain; + conn->bits.tcp_fastopen = data->set.tcp_fastopen; + /*********************************************************************** * file: is a special case in that it doesn't need a network connection ***********************************************************************/ @@ -5341,11 +4307,11 @@ static CURLcode create_conn(struct SessionHandle *data, result = conn->handler->connect_it(conn, &done); /* Setup a "faked" transfer that'll do nothing */ - if(CURLE_OK == result) { + if(!result) { conn->data = data; conn->bits.tcpconnect[FIRSTSOCKET] = TRUE; /* we are "connected */ - ConnectionStore(data, conn); + Curl_conncache_add_conn(data->state.conn_cache, conn); /* * Setup whatever necessary for a resumed transfer @@ -5363,7 +4329,7 @@ static CURLcode create_conn(struct SessionHandle *data, } /* since we skip do_init() */ - do_init(conn); + Curl_init_do(data, conn); goto out; } @@ -5375,26 +4341,60 @@ static CURLcode create_conn(struct SessionHandle *data, strings in the session handle strings array! Keep in mind that the pointers in the master copy are pointing to strings - that will be freed as part of the SessionHandle struct, but all cloned + that will be freed as part of the Curl_easy struct, but all cloned copies will be separately allocated. */ - data->set.ssl.CApath = data->set.str[STRING_SSL_CAPATH]; - data->set.ssl.CAfile = data->set.str[STRING_SSL_CAFILE]; - data->set.ssl.CRLfile = data->set.str[STRING_SSL_CRLFILE]; - data->set.ssl.issuercert = data->set.str[STRING_SSL_ISSUERCERT]; - data->set.ssl.random_file = data->set.str[STRING_SSL_RANDOM_FILE]; - data->set.ssl.egdsocket = data->set.str[STRING_SSL_EGDSOCKET]; - data->set.ssl.cipher_list = data->set.str[STRING_SSL_CIPHER_LIST]; + data->set.ssl.primary.CApath = data->set.str[STRING_SSL_CAPATH_ORIG]; + data->set.proxy_ssl.primary.CApath = data->set.str[STRING_SSL_CAPATH_PROXY]; + data->set.ssl.primary.CAfile = data->set.str[STRING_SSL_CAFILE_ORIG]; + data->set.proxy_ssl.primary.CAfile = data->set.str[STRING_SSL_CAFILE_PROXY]; + data->set.ssl.primary.random_file = data->set.str[STRING_SSL_RANDOM_FILE]; + data->set.proxy_ssl.primary.random_file = + data->set.str[STRING_SSL_RANDOM_FILE]; + data->set.ssl.primary.egdsocket = data->set.str[STRING_SSL_EGDSOCKET]; + data->set.proxy_ssl.primary.egdsocket = data->set.str[STRING_SSL_EGDSOCKET]; + data->set.ssl.primary.cipher_list = + data->set.str[STRING_SSL_CIPHER_LIST_ORIG]; + data->set.proxy_ssl.primary.cipher_list = + data->set.str[STRING_SSL_CIPHER_LIST_PROXY]; + + data->set.ssl.CRLfile = data->set.str[STRING_SSL_CRLFILE_ORIG]; + data->set.proxy_ssl.CRLfile = data->set.str[STRING_SSL_CRLFILE_PROXY]; + data->set.ssl.issuercert = data->set.str[STRING_SSL_ISSUERCERT_ORIG]; + data->set.proxy_ssl.issuercert = data->set.str[STRING_SSL_ISSUERCERT_PROXY]; + data->set.ssl.cert = data->set.str[STRING_CERT_ORIG]; + data->set.proxy_ssl.cert = data->set.str[STRING_CERT_PROXY]; + data->set.ssl.cert_type = data->set.str[STRING_CERT_TYPE_ORIG]; + data->set.proxy_ssl.cert_type = data->set.str[STRING_CERT_TYPE_PROXY]; + data->set.ssl.key = data->set.str[STRING_KEY_ORIG]; + data->set.proxy_ssl.key = data->set.str[STRING_KEY_PROXY]; + data->set.ssl.key_type = data->set.str[STRING_KEY_TYPE_ORIG]; + data->set.proxy_ssl.key_type = data->set.str[STRING_KEY_TYPE_PROXY]; + data->set.ssl.key_passwd = data->set.str[STRING_KEY_PASSWD_ORIG]; + data->set.proxy_ssl.key_passwd = data->set.str[STRING_KEY_PASSWD_PROXY]; + data->set.ssl.primary.clientcert = data->set.str[STRING_CERT_ORIG]; + data->set.proxy_ssl.primary.clientcert = data->set.str[STRING_CERT_PROXY]; #ifdef USE_TLS_SRP - data->set.ssl.username = data->set.str[STRING_TLSAUTH_USERNAME]; - data->set.ssl.password = data->set.str[STRING_TLSAUTH_PASSWORD]; + data->set.ssl.username = data->set.str[STRING_TLSAUTH_USERNAME_ORIG]; + data->set.proxy_ssl.username = data->set.str[STRING_TLSAUTH_USERNAME_PROXY]; + data->set.ssl.password = data->set.str[STRING_TLSAUTH_PASSWORD_ORIG]; + data->set.proxy_ssl.password = data->set.str[STRING_TLSAUTH_PASSWORD_PROXY]; #endif - if(!Curl_clone_ssl_config(&data->set.ssl, &conn->ssl_config)) { + if(!Curl_clone_primary_ssl_config(&data->set.ssl.primary, + &conn->ssl_config)) { result = CURLE_OUT_OF_MEMORY; goto out; } + if(!Curl_clone_primary_ssl_config(&data->set.proxy_ssl.primary, + &conn->proxy_ssl_config)) { + result = CURLE_OUT_OF_MEMORY; + goto out; + } + + prune_dead_connections(data); + /************************************************************* * Check the current list of connections to see if we can * re-use an already existing one or if we have to create a @@ -5408,12 +4408,12 @@ static CURLcode create_conn(struct SessionHandle *data, if(data->set.reuse_fresh && !data->state.this_is_a_follow) reuse = FALSE; else - reuse = ConnectionExists(data, conn, &conn_temp, &force_reuse); + reuse = ConnectionExists(data, conn, &conn_temp, &force_reuse, &waitpipe); /* If we found a reusable connection, we may still want to open a new connection if we are pipelining. */ if(reuse && !force_reuse && IsPipeliningPossible(data, conn_temp)) { - size_t pipelen = conn_temp->send_pipe->size + conn_temp->recv_pipe->size; + size_t pipelen = conn_temp->send_pipe.size + conn_temp->recv_pipe.size; if(pipelen > 0) { infof(data, "Found connection %ld, with requests in the pipe (%zu)\n", conn_temp->connection_id, pipelen); @@ -5442,21 +4442,35 @@ static CURLcode create_conn(struct SessionHandle *data, conn = conn_temp; *in_connect = conn; - /* set a pointer to the hostname we display */ - fix_hostname(data, conn, &conn->host); - - infof(data, "Re-using existing connection! (#%ld) with host %s\n", + infof(data, "Re-using existing connection! (#%ld) with %s %s\n", conn->connection_id, - conn->proxy.name?conn->proxy.dispname:conn->host.dispname); + conn->bits.proxy?"proxy":"host", + conn->socks_proxy.host.name ? conn->socks_proxy.host.dispname : + conn->http_proxy.host.name ? conn->http_proxy.host.dispname : + conn->host.dispname); } else { /* We have decided that we want a new connection. However, we may not be able to do that if we have reached the limit of how many connections we are allowed to open. */ - struct connectbundle *bundle; + struct connectbundle *bundle = NULL; + + if(conn->handler->flags & PROTOPT_ALPN_NPN) { + /* The protocol wants it, so set the bits if enabled in the easy handle + (default) */ + if(data->set.ssl_enable_alpn) + conn->bits.tls_enable_alpn = TRUE; + if(data->set.ssl_enable_npn) + conn->bits.tls_enable_npn = TRUE; + } + + if(waitpipe) + /* There is a connection that *might* become usable for pipelining + "soon", and we wait for that */ + connections_available = FALSE; + else + bundle = Curl_conncache_find_bundle(conn, data->state.conn_cache); - bundle = Curl_conncache_find_bundle(data->state.conn_cache, - conn->host.name); if(max_host_connections > 0 && bundle && (bundle->num_connections >= max_host_connections)) { struct connectdata *conn_candidate; @@ -5469,28 +4483,33 @@ static CURLcode create_conn(struct SessionHandle *data, conn_candidate->data = data; (void)Curl_disconnect(conn_candidate, /* dead_connection */ FALSE); } - else - no_connections_available = TRUE; + else { + infof(data, "No more connections allowed to host: %d\n", + max_host_connections); + connections_available = FALSE; + } } - if(max_total_connections > 0 && + if(connections_available && + (max_total_connections > 0) && (data->state.conn_cache->num_connections >= max_total_connections)) { struct connectdata *conn_candidate; /* The cache is full. Let's see if we can kill a connection. */ - conn_candidate = find_oldest_idle_connection(data); + conn_candidate = Curl_conncache_oldest_idle(data); if(conn_candidate) { /* Set the connection's owner correctly, then kill it */ conn_candidate->data = data; (void)Curl_disconnect(conn_candidate, /* dead_connection */ FALSE); } - else - no_connections_available = TRUE; + else { + infof(data, "No connections available in cache\n"); + connections_available = FALSE; + } } - - if(no_connections_available) { + if(!connections_available) { infof(data, "No connections available.\n"); conn_free(conn); @@ -5504,15 +4523,34 @@ static CURLcode create_conn(struct SessionHandle *data, * This is a brand new connection, so let's store it in the connection * cache of ours! */ - ConnectionStore(data, conn); + Curl_conncache_add_conn(data->state.conn_cache, conn); } + +#if defined(USE_NTLM) + /* If NTLM is requested in a part of this connection, make sure we don't + assume the state is fine as this is a fresh connection and NTLM is + connection based. */ + if((data->state.authhost.picked & (CURLAUTH_NTLM | CURLAUTH_NTLM_WB)) && + data->state.authhost.done) { + infof(data, "NTLM picked AND auth done set, clear picked!\n"); + data->state.authhost.picked = CURLAUTH_NONE; + data->state.authhost.done = FALSE; + } + + if((data->state.authproxy.picked & (CURLAUTH_NTLM | CURLAUTH_NTLM_WB)) && + data->state.authproxy.done) { + infof(data, "NTLM-proxy picked AND auth done set, clear picked!\n"); + data->state.authproxy.picked = CURLAUTH_NONE; + data->state.authproxy.done = FALSE; + } +#endif } /* Mark the connection as used */ conn->inuse = TRUE; /* Setup and init stuff before DO starts, in preparing for the transfer. */ - do_init(conn); + Curl_init_do(data, conn); /* * Setup whatever necessary for a resumed transfer @@ -5527,8 +4565,6 @@ static CURLcode create_conn(struct SessionHandle *data, * Inherit the proper values from the urldata struct AFTER we have arranged * the persistent connection stuff */ - conn->fread_func = data->set.fread_func; - conn->fread_in = data->set.in; conn->seek_func = data->set.seek_func; conn->seek_client = data->set.seek_client; @@ -5537,12 +4573,11 @@ static CURLcode create_conn(struct SessionHandle *data, *************************************************************/ result = resolve_server(data, conn, async); - out: +out: - Curl_safefree(options); - Curl_safefree(passwd); - Curl_safefree(user); - Curl_safefree(proxy); + free(options); + free(passwd); + free(user); return result; } @@ -5558,7 +4593,7 @@ CURLcode Curl_setup_conn(struct connectdata *conn, bool *protocol_done) { CURLcode result = CURLE_OK; - struct SessionHandle *data = conn->data; + struct Curl_easy *data = conn->data; Curl_pgrsTime(data, TIMER_NAMELOOKUP); @@ -5596,58 +4631,24 @@ CURLcode Curl_setup_conn(struct connectdata *conn, /* set start time here for timeout purposes in the connect procedure, it is later set again for the progress meter purpose */ - conn->now = Curl_tvnow(); - - for(;;) { - /* loop for CURL_SERVER_CLOSED_CONNECTION */ - - if(CURL_SOCKET_BAD == conn->sock[FIRSTSOCKET]) { - /* Try to connect only if not already connected */ - bool connected = FALSE; - - result = ConnectPlease(data, conn, &connected); - - if(result && !conn->ip_addr) { - /* transport connection failure not related with authentication */ - conn->bits.tcpconnect[FIRSTSOCKET] = FALSE; - return result; - } - - if(connected) { - result = Curl_protocol_connect(conn, protocol_done); - if(CURLE_OK == result) - conn->bits.tcpconnect[FIRSTSOCKET] = TRUE; - } - else - conn->bits.tcpconnect[FIRSTSOCKET] = FALSE; - - /* if the connection was closed by the server while exchanging - authentication informations, retry with the new set - authentication information */ - if(conn->bits.proxy_connect_closed) { - /* reset the error buffer */ - if(data->set.errorbuffer) - data->set.errorbuffer[0] = '\0'; - data->state.errorbuf = FALSE; - continue; - } + conn->now = Curl_now(); - if(CURLE_OK != result) - return result; - } - else { - Curl_pgrsTime(data, TIMER_CONNECT); /* we're connected already */ - Curl_pgrsTime(data, TIMER_APPCONNECT); /* we're connected already */ - conn->bits.tcpconnect[FIRSTSOCKET] = TRUE; - *protocol_done = TRUE; - Curl_verboseconnect(conn); - Curl_updateconninfo(conn, conn->sock[FIRSTSOCKET]); - } - /* Stop the loop now */ - break; + if(CURL_SOCKET_BAD == conn->sock[FIRSTSOCKET]) { + conn->bits.tcpconnect[FIRSTSOCKET] = FALSE; + result = Curl_connecthost(conn, conn->dns_entry); + if(result) + return result; + } + else { + Curl_pgrsTime(data, TIMER_CONNECT); /* we're connected already */ + Curl_pgrsTime(data, TIMER_APPCONNECT); /* we're connected already */ + conn->bits.tcpconnect[FIRSTSOCKET] = TRUE; + *protocol_done = TRUE; + Curl_updateconninfo(conn, conn->sock[FIRSTSOCKET]); + Curl_verboseconnect(conn); } - conn->now = Curl_tvnow(); /* time this *after* the connect is done, we + conn->now = Curl_now(); /* time this *after* the connect is done, we set this here perhaps a second time */ #ifdef __EMX__ @@ -5666,182 +4667,71 @@ CURLcode Curl_setup_conn(struct connectdata *conn, return result; } -CURLcode Curl_connect(struct SessionHandle *data, +CURLcode Curl_connect(struct Curl_easy *data, struct connectdata **in_connect, bool *asyncp, bool *protocol_done) { - CURLcode code; + CURLcode result; *asyncp = FALSE; /* assume synchronous resolves by default */ /* call the stuff that needs to be called */ - code = create_conn(data, in_connect, asyncp); + result = create_conn(data, in_connect, asyncp); - if(CURLE_OK == code) { + if(!result) { /* no error */ - if((*in_connect)->send_pipe->size || (*in_connect)->recv_pipe->size) + if((*in_connect)->send_pipe.size || (*in_connect)->recv_pipe.size) /* pipelining */ *protocol_done = TRUE; else if(!*asyncp) { /* DNS resolution is done: that's either because this is a reused connection, in which case DNS was unnecessary, or because DNS really did finish already (synch resolver/fast async resolve) */ - code = Curl_setup_conn(*in_connect, protocol_done); + result = Curl_setup_conn(*in_connect, protocol_done); } } - if(code == CURLE_NO_CONNECTION_AVAILABLE) { + if(result == CURLE_NO_CONNECTION_AVAILABLE) { *in_connect = NULL; - return code; + return result; } - if(code && *in_connect) { + if(result && *in_connect) { /* We're not allowed to return failure with memory left allocated in the connectdata struct, free those here */ Curl_disconnect(*in_connect, FALSE); /* close the connection */ *in_connect = NULL; /* return a NULL */ } - return code; -} - -CURLcode Curl_done(struct connectdata **connp, - CURLcode status, /* an error if this is called after an - error was detected */ - bool premature) -{ - CURLcode result; - struct connectdata *conn; - struct SessionHandle *data; - - DEBUGASSERT(*connp); - - conn = *connp; - data = conn->data; - - if(conn->bits.done) - /* Stop if Curl_done() has already been called */ - return CURLE_OK; - - Curl_getoff_all_pipelines(data, conn); - - if((conn->send_pipe->size + conn->recv_pipe->size != 0 && - !data->set.reuse_forbid && - !conn->bits.close)) - /* Stop if pipeline is not empty and we do not have to close - connection. */ - return CURLE_OK; - - conn->bits.done = TRUE; /* called just now! */ - - /* Cleanup possible redirect junk */ - if(data->req.newurl) { - free(data->req.newurl); - data->req.newurl = NULL; - } - if(data->req.location) { - free(data->req.location); - data->req.location = NULL; - } - - Curl_resolver_cancel(conn); - - if(conn->dns_entry) { - Curl_resolv_unlock(data, conn->dns_entry); /* done with this */ - conn->dns_entry = NULL; - } - - switch(status) { - case CURLE_ABORTED_BY_CALLBACK: - case CURLE_READ_ERROR: - case CURLE_WRITE_ERROR: - /* When we're aborted due to a callback return code it basically have to - be counted as premature as there is trouble ahead if we don't. We have - many callbacks and protocols work differently, we could potentially do - this more fine-grained in the future. */ - premature = TRUE; - default: - break; - } - - /* this calls the protocol-specific function pointer previously set */ - if(conn->handler->done) - result = conn->handler->done(conn, status, premature); - else - result = CURLE_OK; - - if(Curl_pgrsDone(conn) && !result) - result = CURLE_ABORTED_BY_CALLBACK; - - /* if the transfer was completed in a paused state there can be buffered - data left to write and then kill */ - if(data->state.tempwrite) { - free(data->state.tempwrite); - data->state.tempwrite = NULL; - } - - /* if data->set.reuse_forbid is TRUE, it means the libcurl client has - forced us to close this no matter what we think. - - if conn->bits.close is TRUE, it means that the connection should be - closed in spite of all our efforts to be nice, due to protocol - restrictions in our or the server's end - - if premature is TRUE, it means this connection was said to be DONE before - the entire request operation is complete and thus we can't know in what - state it is for re-using, so we're forced to close it. In a perfect world - we can add code that keep track of if we really must close it here or not, - but currently we have no such detail knowledge. - */ - if(data->set.reuse_forbid || conn->bits.close || premature) { - CURLcode res2 = Curl_disconnect(conn, premature); /* close connection */ - - /* If we had an error already, make sure we return that one. But - if we got a new error, return that. */ - if(!result && res2) - result = res2; - } - else { - /* the connection is no longer in use */ - if(ConnectionDone(data, conn)) { - /* remember the most recently used connection */ - data->state.lastconnect = conn; - - infof(data, "Connection #%ld to host %s left intact\n", - conn->connection_id, - conn->bits.httpproxy?conn->proxy.dispname:conn->host.dispname); - } - else - data->state.lastconnect = NULL; - } - - *connp = NULL; /* to make the caller of this function better detect that - this was either closed or handed over to the connection - cache here, and therefore cannot be used from this point on - */ - Curl_free_request_state(data); - return result; } /* - * do_init() inits the readwrite session. This is inited each time (in the DO - * function before the protocol-specific DO functions are invoked) for a - * transfer, sometimes multiple times on the same SessionHandle. Make sure + * Curl_init_do() inits the readwrite session. This is inited each time (in + * the DO function before the protocol-specific DO functions are invoked) for + * a transfer, sometimes multiple times on the same Curl_easy. Make sure * nothing in here depends on stuff that are setup dynamically for the * transfer. + * + * Allow this function to get called with 'conn' set to NULL. */ -static CURLcode do_init(struct connectdata *conn) +CURLcode Curl_init_do(struct Curl_easy *data, struct connectdata *conn) { - struct SessionHandle *data = conn->data; struct SingleRequest *k = &data->req; - conn->bits.done = FALSE; /* Curl_done() is not called yet */ - conn->bits.do_more = FALSE; /* by default there's no curl_do_more() to use */ + conn->bits.do_more = FALSE; /* by default there's no curl_do_more() to + use */ + + data->state.done = FALSE; /* *_done() is not called yet */ data->state.expect100header = FALSE; + /* if the protocol used doesn't support wildcards, switch it off */ + if(data->state.wildcardmatch && + !(conn->handler->flags & PROTOPT_WILDCARD)) + data->state.wildcardmatch = FALSE; + if(data->set.opt_no_body) /* in HTTP lingo, no body means using the HEAD request... */ data->set.httpreq = HTTPREQ_HEAD; @@ -5853,16 +4743,15 @@ static CURLcode do_init(struct connectdata *conn) HTTP. */ data->set.httpreq = HTTPREQ_GET; - k->start = Curl_tvnow(); /* start time */ + k->start = Curl_now(); /* start time */ k->now = k->start; /* current time is now */ k->header = TRUE; /* assume header */ k->bytecount = 0; k->buf = data->state.buffer; - k->uploadbuf = data->state.uploadbuffer; k->hbufp = data->state.headerbuff; - k->ignorebody=FALSE; + k->ignorebody = FALSE; Curl_speedinit(data); @@ -5873,81 +4762,111 @@ static CURLcode do_init(struct connectdata *conn) } /* - * do_complete is called when the DO actions are complete. - * - * We init chunking and trailer bits to their default values here immediately - * before receiving any header data for the current request in the pipeline. - */ -static void do_complete(struct connectdata *conn) -{ - conn->data->req.chunk=FALSE; - conn->data->req.maxfd = (conn->sockfd>conn->writesockfd? - conn->sockfd:conn->writesockfd)+1; - Curl_pgrsTime(conn->data, TIMER_PRETRANSFER); -} +* get_protocol_family() +* +* This is used to return the protocol family for a given protocol. +* +* Parameters: +* +* protocol [in] - A single bit protocol identifier such as HTTP or HTTPS. +* +* Returns the family as a single bit protocol identifier. +*/ -CURLcode Curl_do(struct connectdata **connp, bool *done) +static unsigned int get_protocol_family(unsigned int protocol) { - CURLcode result=CURLE_OK; - struct connectdata *conn = *connp; - struct SessionHandle *data = conn->data; + unsigned int family; - if(conn->handler->do_it) { - /* generic protocol-specific function pointer set in curl_connect() */ - result = conn->handler->do_it(conn, done); + switch(protocol) { + case CURLPROTO_HTTP: + case CURLPROTO_HTTPS: + family = CURLPROTO_HTTP; + break; - /* This was formerly done in transfer.c, but we better do it here */ - if((CURLE_SEND_ERROR == result) && conn->bits.reuse) { - /* - * If the connection is using an easy handle, call reconnect - * to re-establish the connection. Otherwise, let the multi logic - * figure out how to re-establish the connection. - */ - if(!data->multi) { - result = Curl_reconnect_request(connp); - - if(result == CURLE_OK) { - /* ... finally back to actually retry the DO phase */ - conn = *connp; /* re-assign conn since Curl_reconnect_request - creates a new connection */ - result = conn->handler->do_it(conn, done); - } - } - else - return result; - } + case CURLPROTO_FTP: + case CURLPROTO_FTPS: + family = CURLPROTO_FTP; + break; - if((result == CURLE_OK) && *done) - /* do_complete must be called after the protocol-specific DO function */ - do_complete(conn); - } - return result; -} + case CURLPROTO_SCP: + family = CURLPROTO_SCP; + break; -/* - * Curl_do_more() is called during the DO_MORE multi state. It is basically a - * second stage DO state which (wrongly) was introduced to support FTP's - * second connection. - * - * TODO: A future libcurl should be able to work away this state. - * - * 'complete' can return 0 for incomplete, 1 for done and -1 for go back to - * DOING state there's more work to do! - */ + case CURLPROTO_SFTP: + family = CURLPROTO_SFTP; + break; -CURLcode Curl_do_more(struct connectdata *conn, int *complete) -{ - CURLcode result=CURLE_OK; + case CURLPROTO_TELNET: + family = CURLPROTO_TELNET; + break; + + case CURLPROTO_LDAP: + case CURLPROTO_LDAPS: + family = CURLPROTO_LDAP; + break; - *complete = 0; + case CURLPROTO_DICT: + family = CURLPROTO_DICT; + break; - if(conn->handler->do_more) - result = conn->handler->do_more(conn, complete); + case CURLPROTO_FILE: + family = CURLPROTO_FILE; + break; - if(!result && (*complete == 1)) - /* do_complete must be called after the protocol-specific DO function */ - do_complete(conn); + case CURLPROTO_TFTP: + family = CURLPROTO_TFTP; + break; - return result; -} + case CURLPROTO_IMAP: + case CURLPROTO_IMAPS: + family = CURLPROTO_IMAP; + break; + + case CURLPROTO_POP3: + case CURLPROTO_POP3S: + family = CURLPROTO_POP3; + break; + case CURLPROTO_SMTP: + case CURLPROTO_SMTPS: + family = CURLPROTO_SMTP; + break; + + case CURLPROTO_RTSP: + family = CURLPROTO_RTSP; + break; + + case CURLPROTO_RTMP: + case CURLPROTO_RTMPS: + family = CURLPROTO_RTMP; + break; + + case CURLPROTO_RTMPT: + case CURLPROTO_RTMPTS: + family = CURLPROTO_RTMPT; + break; + + case CURLPROTO_RTMPE: + family = CURLPROTO_RTMPE; + break; + + case CURLPROTO_RTMPTE: + family = CURLPROTO_RTMPTE; + break; + + case CURLPROTO_GOPHER: + family = CURLPROTO_GOPHER; + break; + + case CURLPROTO_SMB: + case CURLPROTO_SMBS: + family = CURLPROTO_SMB; + break; + + default: + family = 0; + break; + } + + return family; +} |