diff options
Diffstat (limited to 'Utilities/cmcurl/lib/url.c')
-rw-r--r-- | Utilities/cmcurl/lib/url.c | 656 |
1 files changed, 275 insertions, 381 deletions
diff --git a/Utilities/cmcurl/lib/url.c b/Utilities/cmcurl/lib/url.c index c441ae7165..47fc66aedd 100644 --- a/Utilities/cmcurl/lib/url.c +++ b/Utilities/cmcurl/lib/url.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2020, 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 @@ -34,10 +34,12 @@ #ifdef HAVE_NET_IF_H #include <net/if.h> #endif +#ifdef HAVE_IPHLPAPI_H +#include <Iphlpapi.h> +#endif #ifdef HAVE_SYS_IOCTL_H #include <sys/ioctl.h> #endif - #ifdef HAVE_SYS_PARAM_H #include <sys/param.h> #endif @@ -93,6 +95,7 @@ bool curl_win32_idn_to_ascii(const char *in, char **out); #include "inet_pton.h" #include "getinfo.h" #include "urlapi-int.h" +#include "system_win32.h" /* And now for the protocols */ #include "ftp.h" @@ -103,13 +106,12 @@ bool curl_win32_idn_to_ascii(const char *in, char **out); #include "http2.h" #include "file.h" #include "curl_ldap.h" -#include "ssh.h" +#include "vssh/ssh.h" #include "imap.h" #include "url.h" #include "connect.h" #include "inet_ntop.h" #include "http_ntlm.h" -#include "socks.h" #include "curl_rtmp.h" #include "gopher.h" #include "http_proxy.h" @@ -126,7 +128,6 @@ bool curl_win32_idn_to_ascii(const char *in, char **out); #include "memdebug.h" static void conn_free(struct connectdata *conn); -static void free_idnconverted_hostname(struct hostname *host); static unsigned int get_protocol_family(unsigned int protocol); /* Some parts of the code (e.g. chunked encoding) assume this buffer has at @@ -185,7 +186,7 @@ static const struct Curl_handler * const protocols[] = { &Curl_handler_tftp, #endif -#if defined(USE_SSH) +#if defined(USE_SSH) && !defined(USE_WOLFSSH) &Curl_handler_scp, #endif @@ -315,13 +316,17 @@ static void up_free(struct Curl_easy *data) * when curl_easy_perform() is invoked. */ -CURLcode Curl_close(struct Curl_easy *data) +CURLcode Curl_close(struct Curl_easy **datap) { struct Curl_multi *m; + struct Curl_easy *data; - if(!data) + if(!datap || !*datap) return CURLE_OK; + data = *datap; + *datap = NULL; + Curl_expire_clear(data); /* shut off timers */ m = data->multi; @@ -372,9 +377,9 @@ CURLcode Curl_close(struct Curl_easy *data) Curl_safefree(data->state.buffer); Curl_safefree(data->state.headerbuff); Curl_safefree(data->state.ulbuf); - Curl_flush_cookies(data, 1); + Curl_flush_cookies(data, TRUE); #ifdef USE_ALTSVC - Curl_altsvc_save(data->asi, data->set.str[STRING_ALTSVC]); + Curl_altsvc_save(data, data->asi, data->set.str[STRING_ALTSVC]); Curl_altsvc_cleanup(data->asi); data->asi = NULL; #endif @@ -397,6 +402,12 @@ CURLcode Curl_close(struct Curl_easy *data) Curl_share_unlock(data, CURL_LOCK_DATA_SHARE); } +#ifndef CURL_DISABLE_DOH + free(data->req.doh.probe[0].serverdoh.memory); + free(data->req.doh.probe[1].serverdoh.memory); + curl_slist_free_all(data->req.doh.headers); +#endif + /* destruct wildcard structures if it is needed */ Curl_wildcard_dtor(&data->wildcard); Curl_freeset(data); @@ -439,7 +450,7 @@ CURLcode Curl_init_userdefined(struct Curl_easy *data) set->httpreq = HTTPREQ_GET; /* Default HTTP request */ set->rtspreq = RTSPREQ_OPTIONS; /* Default RTSP request */ -#ifndef CURL_DISABLE_FILE +#ifndef CURL_DISABLE_FTP set->ftp_use_epsv = TRUE; /* FTP defaults to EPSV operations */ set->ftp_use_eprt = TRUE; /* FTP defaults to EPRT operations */ set->ftp_use_pret = FALSE; /* mainly useful for drftpd servers */ @@ -485,9 +496,8 @@ CURLcode Curl_init_userdefined(struct Curl_easy *data) 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 & /* All except FILE, SCP and SMB */ - ~(CURLPROTO_FILE | CURLPROTO_SCP | CURLPROTO_SMB | - CURLPROTO_SMBS); + set->redir_protocols = CURLPROTO_HTTP | CURLPROTO_HTTPS | CURLPROTO_FTP | + CURLPROTO_FTPS; #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) /* @@ -544,7 +554,7 @@ CURLcode Curl_init_userdefined(struct Curl_easy *data) set->upkeep_interval_ms = CURL_UPKEEP_INTERVAL_DEFAULT; set->maxconnects = DEFAULT_CONNCACHE_SIZE; /* for easy handles */ set->maxage_conn = 118; - set->http09_allowed = TRUE; + set->http09_allowed = FALSE; set->httpversion = #ifdef USE_NGHTTP2 CURL_HTTP_VERSION_2TLS @@ -611,8 +621,6 @@ CURLcode Curl_open(struct Curl_easy **curl) data->progress.flags |= PGRS_HIDE; data->state.current_speed = -1; /* init to negative == impossible */ - - Curl_http2_init_state(&data->state); } } @@ -665,7 +673,7 @@ static void conn_reset_all_postponed_data(struct connectdata *conn) } #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 +#define conn_reset_all_postponed_data(c) do {} while(0) #endif /* ! USE_RECV_BEFORE_SEND_WORKAROUND */ @@ -705,14 +713,14 @@ static void conn_free(struct connectdata *conn) if(!conn) return; - free_idnconverted_hostname(&conn->host); - free_idnconverted_hostname(&conn->conn_to_host); - free_idnconverted_hostname(&conn->http_proxy.host); - free_idnconverted_hostname(&conn->socks_proxy.host); + Curl_free_idnconverted_hostname(&conn->host); + Curl_free_idnconverted_hostname(&conn->conn_to_host); + Curl_free_idnconverted_hostname(&conn->http_proxy.host); + Curl_free_idnconverted_hostname(&conn->socks_proxy.host); Curl_safefree(conn->user); Curl_safefree(conn->passwd); - Curl_safefree(conn->oauth_bearer); + Curl_safefree(conn->sasl_authzid); Curl_safefree(conn->options); Curl_safefree(conn->http_proxy.user); Curl_safefree(conn->socks_proxy.user); @@ -873,11 +881,59 @@ proxy_info_matches(const struct proxy_info* data, return FALSE; } + +static bool +socks_proxy_info_matches(const struct proxy_info* data, + const struct proxy_info* needle) +{ + if(!proxy_info_matches(data, needle)) + return FALSE; + + /* the user information is case-sensitive + or at least it is not defined as case-insensitive + see https://tools.ietf.org/html/rfc3986#section-3.2.1 */ + if((data->user == NULL) != (needle->user == NULL)) + return FALSE; + /* curl_strequal does a case insentive comparison, so do not use it here! */ + if(data->user && + needle->user && + strcmp(data->user, needle->user) != 0) + return FALSE; + if((data->passwd == NULL) != (needle->passwd == NULL)) + return FALSE; + /* curl_strequal does a case insentive comparison, so do not use it here! */ + if(data->passwd && + needle->passwd && + strcmp(data->passwd, needle->passwd) != 0) + return FALSE; + return TRUE; +} #else /* disabled, won't get called */ #define proxy_info_matches(x,y) FALSE +#define socks_proxy_info_matches(x,y) FALSE #endif +/* A connection has to have been idle for a shorter time than 'maxage_conn' to + be subject for reuse. The success rate is just too low after this. */ + +static bool conn_maxage(struct Curl_easy *data, + struct connectdata *conn, + struct curltime now) +{ + if(!conn->data) { + timediff_t idletime = Curl_timediff(now, conn->lastused); + idletime /= 1000; /* integer seconds is fine */ + + if(idletime > data->set.maxage_conn) { + infof(data, "Too old connection (%ld seconds), disconnect it\n", + idletime); + return TRUE; + } + } + return FALSE; +} + /* * This function checks if the given connection is dead and extracts it from * the connection cache if so. @@ -894,7 +950,11 @@ static bool extract_if_dead(struct connectdata *conn, /* The check for a dead socket makes sense only if the connection isn't in use */ bool dead; - if(conn->handler->connection_check) { + struct curltime now = Curl_now(); + if(conn_maxage(data, conn, now)) { + dead = TRUE; + } + else 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; @@ -946,7 +1006,8 @@ static int call_extract_if_dead(struct connectdata *conn, void *param) 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); + timediff_t elapsed = + Curl_timediff(now, data->state.conn_cache->last_cleanup); if(elapsed >= 1000L) { struct prunedead prune; @@ -961,25 +1022,6 @@ static void prune_dead_connections(struct Curl_easy *data) } } -/* A connection has to have been idle for a shorter time than 'maxage_conn' to - be subject for reuse. The success rate is just too low after this. */ - -static bool conn_maxage(struct Curl_easy *data, - struct connectdata *conn, - struct curltime now) -{ - if(!conn->data) { - timediff_t idletime = Curl_timediff(now, conn->lastused); - idletime /= 1000; /* integer seconds is fine */ - - if(idletime/1000 > data->set.maxage_conn) { - infof(data, "Too old connection (%ld seconds), disconnect it\n", - idletime); - return TRUE; - } - } - return FALSE; -} /* * Given one filled in connection struct (named needle), this function should * detect if there already is one that has all the significant details @@ -1003,7 +1045,7 @@ ConnectionExists(struct Curl_easy *data, bool foundPendingCandidate = FALSE; bool canmultiplex = IsMultiplexingPossible(data, needle); struct connectbundle *bundle; - struct curltime now = Curl_now(); + const char *hostbundle; #ifdef USE_NTLM bool wantNTLMhttp = ((data->state.authhost.want & @@ -1020,21 +1062,20 @@ ConnectionExists(struct Curl_easy *data, /* Look up the bundle with all the connections to this particular host. Locks the connection cache, beware of early returns! */ - bundle = Curl_conncache_find_bundle(needle, data->state.conn_cache); + bundle = Curl_conncache_find_bundle(needle, data->state.conn_cache, + &hostbundle); if(bundle) { /* Max pipe length is zero (unlimited) for multiplexed connections */ struct curl_llist_element *curr; 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_MULTIPLEX ? - "can multiplex" : "serially")); + hostbundle, (void *)bundle, (bundle->multiuse == BUNDLE_MULTIPLEX ? + "can multiplex" : "serially")); /* We can't multiplex if we don't know anything about the server */ if(canmultiplex) { if(bundle->multiuse == BUNDLE_UNKNOWN) { - if((bundle->multiuse == BUNDLE_UNKNOWN) && data->set.pipewait) { + if(data->set.pipewait) { infof(data, "Server doesn't support multiplex yet, wait\n"); *waitpipe = TRUE; Curl_conncache_unlock(data); @@ -1058,7 +1099,7 @@ ConnectionExists(struct Curl_easy *data, curr = bundle->conn_list.head; while(curr) { bool match = FALSE; - size_t multiplexed; + size_t multiplexed = 0; /* * Note that if we use a HTTP proxy in normal mode (no tunneling), we @@ -1067,22 +1108,15 @@ ConnectionExists(struct Curl_easy *data, check = curr->ptr; curr = curr->next; - if(check->bits.connect_only) - /* connect-only connections will not be reused */ - continue; - - if(conn_maxage(data, check, now) || extract_if_dead(check, data)) { - /* disconnect it */ - (void)Curl_disconnect(data, check, /* dead_connection */TRUE); + if(check->bits.connect_only || check->bits.close) + /* connect-only or to-be-closed connections will not be reused */ continue; - } - multiplexed = CONN_INUSE(check) && - (bundle->multiuse == BUNDLE_MULTIPLEX); + if(bundle->multiuse == BUNDLE_MULTIPLEX) + multiplexed = CONN_INUSE(check); if(canmultiplex) { - if(check->bits.protoconnstart && check->bits.close) - continue; + ; } else { if(multiplexed) { @@ -1102,12 +1136,9 @@ ConnectionExists(struct Curl_easy *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. */ + if(check->sock[FIRSTSOCKET] == CURL_SOCKET_BAD) { + foundPendingCandidate = TRUE; + /* Don't pick a connection that hasn't connected yet */ infof(data, "Connection #%ld isn't open enough, can't reuse\n", check->connection_id); continue; @@ -1139,8 +1170,9 @@ ConnectionExists(struct Curl_easy *data, needle->bits.socksproxy != check->bits.socksproxy) continue; - if(needle->bits.socksproxy && !proxy_info_matches(&needle->socks_proxy, - &check->socks_proxy)) + if(needle->bits.socksproxy && + !socks_proxy_info_matches(&needle->socks_proxy, + &check->socks_proxy)) continue; if(needle->bits.conn_to_host != check->bits.conn_to_host) @@ -1180,13 +1212,14 @@ ConnectionExists(struct Curl_easy *data, } } + DEBUGASSERT(!check->data || GOOD_EASY_HANDLE(check->data)); + if(!canmultiplex && check->data) /* this request can't be multiplexed but the checked connection is already in use so we skip it */ continue; - if(CONN_INUSE(check) && check->data && - (check->data->multi != needle->data->multi)) + if(check->data && (check->data->multi != needle->data->multi)) /* this could be subject for multiplex use, but only if they belong to * the same multi handle */ continue; @@ -1235,7 +1268,7 @@ ConnectionExists(struct Curl_easy *data, 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 + /* The schemes match or 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) { @@ -1276,8 +1309,14 @@ ConnectionExists(struct Curl_easy *data, partway through a handshake!) */ if(wantNTLMhttp) { if(strcmp(needle->user, check->user) || - strcmp(needle->passwd, check->passwd)) + strcmp(needle->passwd, check->passwd)) { + + /* we prefer a credential match, but this is at least a connection + that can be reused and "upgraded" to NTLM */ + if(check->http_ntlm_state == NTLMSTATE_NONE) + chosen = check; continue; + } } else if(check->http_ntlm_state != NTLMSTATE_NONE) { /* Connection is using NTLM auth but we don't want NTLM */ @@ -1337,6 +1376,13 @@ ConnectionExists(struct Curl_easy *data, multiplexed); continue; } + else if(multiplexed >= + Curl_multi_max_concurrent_streams(needle->data->multi)) { + infof(data, "client side MAX_CONCURRENT_STREAMS reached" + ", skip (%zu)\n", + multiplexed); + continue; + } } #endif /* When not multiplexed, we have a match here! */ @@ -1371,58 +1417,6 @@ ConnectionExists(struct Curl_easy *data, return FALSE; /* no matching connecting exists */ } -/* after a TCP connection to the proxy has been verified, this function does - the next magic step. - - Note: this function's sub-functions call failf() - -*/ -CURLcode Curl_connected_proxy(struct connectdata *conn, int sockindex) -{ - CURLcode result = CURLE_OK; - - if(conn->bits.socksproxy) { -#ifndef CURL_DISABLE_PROXY - /* 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; - - case CURLPROXY_SOCKS4: - case CURLPROXY_SOCKS4A: - result = Curl_SOCKS4(conn->socks_proxy.user, host, port, sockindex, - conn); - break; - - default: - failf(conn->data, "unknown proxytype option given"); - result = CURLE_COULDNT_CONNECT; - } /* switch proxytype */ - conn->bits.socksproxy_connecting = FALSE; -#else - (void)sockindex; -#endif /* CURL_DISABLE_PROXY */ - } - - return result; -} - /* * verboseconnect() displays verbose information after a connect */ @@ -1439,134 +1433,17 @@ void Curl_verboseconnect(struct connectdata *conn) } #endif -int Curl_protocol_getsock(struct connectdata *conn, - curl_socket_t *socks, - int numsocks) -{ - if(conn->handler->proto_getsock) - return conn->handler->proto_getsock(conn, socks, numsocks); - /* Backup getsock logic. Since there is a live socket in use, we must wait - for it or it will be removed from watching when the multi_socket API is - used. */ - socks[0] = conn->sock[FIRSTSOCKET]; - return GETSOCK_READSOCK(0) | GETSOCK_WRITESOCK(0); -} - -int Curl_doing_getsock(struct connectdata *conn, - curl_socket_t *socks, - int numsocks) -{ - if(conn && conn->handler->doing_getsock) - return conn->handler->doing_getsock(conn, socks, numsocks); - return GETSOCK_BLANK; -} - -/* - * We are doing protocol-specific connecting and this is being called over and - * over from the multi interface until the connection phase is done on - * protocol layer. - */ - -CURLcode Curl_protocol_connecting(struct connectdata *conn, - bool *done) -{ - CURLcode result = CURLE_OK; - - if(conn && conn->handler->connecting) { - *done = FALSE; - result = conn->handler->connecting(conn, done); - } - else - *done = TRUE; - - return result; -} - -/* - * We are DOING this is being called over and over from the multi interface - * until the DOING phase is done on protocol layer. - */ - -CURLcode Curl_protocol_doing(struct connectdata *conn, bool *done) -{ - CURLcode result = CURLE_OK; - - if(conn && conn->handler->doing) { - *done = FALSE; - result = conn->handler->doing(conn, done); - } - else - *done = TRUE; - - return result; -} - -/* - * We have discovered that the TCP connection has been successful, we can now - * proceed with some action. - * - */ -CURLcode Curl_protocol_connect(struct connectdata *conn, - bool *protocol_done) -{ - CURLcode result = CURLE_OK; - - *protocol_done = FALSE; - - if(conn->bits.tcpconnect[FIRSTSOCKET] && conn->bits.protoconnstart) { - /* We already are connected, get back. This may happen when the connect - worked fine in the first call, like when we connect to a local server - or proxy. Note that we don't know if the protocol is actually done. - - Unless this protocol doesn't have any protocol-connect callback, as - then we know we're done. */ - if(!conn->handler->connecting) - *protocol_done = TRUE; - - return CURLE_OK; - } - - if(!conn->bits.protoconnstart) { - - 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 && - 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; - - if(conn->handler->connect_it) { - /* is there a protocol-specific connect() procedure? */ - - /* Call the protocol-specific connect function */ - result = conn->handler->connect_it(conn, protocol_done); - } - else - *protocol_done = TRUE; - - /* it has started, possibly even completed but that knowledge isn't stored - in this bit! */ - if(!result) - conn->bits.protoconnstart = TRUE; - } - - return result; /* pass back status */ -} - /* * Helpers for IDNA conversions. */ -static bool is_ASCII_name(const char *hostname) +bool Curl_is_ASCII_name(const char *hostname) { + /* get an UNSIGNED local version of the pointer */ const unsigned char *ch = (const unsigned char *)hostname; + if(!hostname) /* bad input, consider it ASCII! */ + return TRUE; + while(*ch) { if(*ch++ & 0x80) return FALSE; @@ -1591,8 +1468,8 @@ static void strip_trailing_dot(struct hostname *host) /* * Perform any necessary IDN conversion of hostname */ -static CURLcode idnconvert_hostname(struct connectdata *conn, - struct hostname *host) +CURLcode Curl_idnconvert_hostname(struct connectdata *conn, + struct hostname *host) { struct Curl_easy *data = conn->data; @@ -1607,7 +1484,7 @@ static CURLcode idnconvert_hostname(struct connectdata *conn, host->dispname = host->name; /* Check name for non-ASCII and convert hostname to ACE form if we can */ - if(!is_ASCII_name(host->name)) { + if(!Curl_is_ASCII_name(host->name)) { #ifdef USE_LIBIDN2 if(idn2_check_version(IDN2_VERSION)) { char *ace_hostname = NULL; @@ -1640,7 +1517,9 @@ static CURLcode idnconvert_hostname(struct connectdata *conn, host->name = host->encalloc; } else { - failf(data, "Failed to convert %s to ACE;\n", host->name); + char buffer[STRERROR_LEN]; + failf(data, "Failed to convert %s to ACE; %s\n", host->name, + Curl_winapi_strerror(GetLastError(), buffer, sizeof(buffer))); return CURLE_URL_MALFORMAT; } #else @@ -1653,7 +1532,7 @@ static CURLcode idnconvert_hostname(struct connectdata *conn, /* * Frees data allocated by idnconvert_hostname() */ -static void free_idnconverted_hostname(struct hostname *host) +void Curl_free_idnconverted_hostname(struct hostname *host) { #if defined(USE_LIBIDN2) if(host->encalloc) { @@ -1670,13 +1549,6 @@ static void free_idnconverted_hostname(struct hostname *host) #endif } -static void llist_dtor(void *user, void *element) -{ - (void)user; - (void)element; - /* Do nothing */ -} - /* * Allocate and initialize a new connectdata object. */ @@ -1781,14 +1653,16 @@ static struct connectdata *allocate_conn(struct Curl_easy *data) conn->proxy_ssl_config.verifyhost = data->set.proxy_ssl.primary.verifyhost; conn->ip_version = data->set.ipver; conn->bits.connect_only = data->set.connect_only; + conn->transport = TRNSPRT_TCP; /* most of them are TCP streams */ #if !defined(CURL_DISABLE_HTTP) && defined(USE_NTLM) && \ defined(NTLM_WB_ENABLED) - conn->ntlm_auth_hlpr_socket = CURL_SOCKET_BAD; + conn->ntlm.ntlm_auth_hlpr_socket = CURL_SOCKET_BAD; + conn->proxyntlm.ntlm_auth_hlpr_socket = CURL_SOCKET_BAD; #endif /* Initialize the easy handle list */ - Curl_llist_init(&conn->easyq, (curl_llist_dtor) llist_dtor); + Curl_llist_init(&conn->easyq, NULL); #ifdef HAVE_GSSAPI conn->data_prot = PROT_CLEAR; @@ -1807,6 +1681,7 @@ static struct connectdata *allocate_conn(struct Curl_easy *data) it may live on without (this specific) Curl_easy */ conn->fclosesocket = data->set.fclosesocket; conn->closesocket_client = data->set.closesocket_client; + conn->lastused = Curl_now(); /* used now */ return conn; error: @@ -1884,6 +1759,50 @@ CURLcode Curl_uc_to_curlcode(CURLUcode uc) } /* + * If the URL was set with an IPv6 numerical address with a zone id part, set + * the scope_id based on that! + */ + +static void zonefrom_url(CURLU *uh, struct connectdata *conn) +{ + char *zoneid; + CURLUcode uc; + + uc = curl_url_get(uh, CURLUPART_ZONEID, &zoneid, 0); + + if(!uc && zoneid) { + char *endp; + unsigned long scope = strtoul(zoneid, &endp, 10); + if(!*endp && (scope < UINT_MAX)) + /* A plain number, use it directly as a scope id. */ + conn->scope_id = (unsigned int)scope; +#if defined(HAVE_IF_NAMETOINDEX) + else { +#elif defined(WIN32) + else if(Curl_if_nametoindex) { +#endif + +#if defined(HAVE_IF_NAMETOINDEX) || defined(WIN32) + /* Zone identifier is not numeric */ + unsigned int scopeidx = 0; +#if defined(WIN32) + scopeidx = Curl_if_nametoindex(zoneid); +#else + scopeidx = if_nametoindex(zoneid); +#endif + if(!scopeidx) + infof(conn->data, "Invalid zoneid: %s; %s\n", zoneid, + strerror(errno)); + else + conn->scope_id = scopeidx; + } +#endif /* HAVE_IF_NAMETOINDEX || WIN32 */ + + free(zoneid); + } +} + +/* * Parse URL and fill in the relevant members of the connection struct. */ static CURLcode parseurlandfillconn(struct Curl_easy *data, @@ -1921,6 +1840,7 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data, } if(!data->set.uh) { + char *newurl; uc = curl_url_set(uh, CURLUPART_URL, data->change.url, CURLU_GUESS_SCHEME | CURLU_NON_SUPPORT_SCHEME | @@ -1931,6 +1851,15 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data, DEBUGF(infof(data, "curl_url_set rejected %s\n", data->change.url)); return Curl_uc_to_curlcode(uc); } + + /* after it was parsed, get the generated normalized version */ + uc = curl_url_get(uh, CURLUPART_URL, &newurl, 0); + if(uc) + return Curl_uc_to_curlcode(uc); + if(data->change.url_alloc) + free(data->change.url); + data->change.url = newurl; + data->change.url_alloc = TRUE; } uc = curl_url_get(uh, CURLUPART_SCHEME, &data->state.up.scheme, 0); @@ -1991,55 +1920,27 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data, } else { unsigned long port = strtoul(data->state.up.port, NULL, 10); - conn->remote_port = curlx_ultous(port); + conn->port = conn->remote_port = curlx_ultous(port); } (void)curl_url_get(uh, CURLUPART_QUERY, &data->state.up.query, 0); hostname = data->state.up.hostname; - if(!hostname) - /* this is for file:// transfers, get a dummy made */ - hostname = (char *)""; - - if(hostname[0] == '[') { + if(hostname && hostname[0] == '[') { /* This looks like an IPv6 address literal. See if there is an address scope. */ - char *zoneid; size_t hlen; - uc = curl_url_get(uh, CURLUPART_ZONEID, &zoneid, 0); conn->bits.ipv6_ip = TRUE; - /* cut off the brackets! */ hostname++; hlen = strlen(hostname); hostname[hlen - 1] = 0; - if(!uc && zoneid) { - char *endp; - unsigned long scope; - scope = strtoul(zoneid, &endp, 10); - if(!*endp && (scope < UINT_MAX)) { - /* A plain number, use it direcly as a scope id. */ - conn->scope_id = (unsigned int)scope; - } -#ifdef HAVE_IF_NAMETOINDEX - else { - /* Zone identifier is not numeric */ - unsigned int scopeidx = 0; - scopeidx = if_nametoindex(zoneid); - if(!scopeidx) - infof(data, "Invalid zoneid id: %s; %s\n", zoneid, - strerror(errno)); - else - conn->scope_id = scopeidx; - } -#endif /* HAVE_IF_NAMETOINDEX */ - free(zoneid); - } + zonefrom_url(uh, conn); } /* make sure the connect struct gets its own copy of the host name */ - conn->host.rawalloc = strdup(hostname); + conn->host.rawalloc = strdup(hostname ? hostname : ""); if(!conn->host.rawalloc) return CURLE_OUT_OF_MEMORY; conn->host.name = conn->host.rawalloc; @@ -2097,7 +1998,6 @@ static CURLcode setup_connection_internals(struct connectdata *conn) { const struct Curl_handler * p; CURLcode result; - conn->socktype = SOCK_STREAM; /* most of them are TCP streams */ /* Perform setup complement if some. */ p = conn->handler; @@ -2128,6 +2028,11 @@ void Curl_free_request_state(struct Curl_easy *data) { Curl_safefree(data->req.protop); Curl_safefree(data->req.newurl); + +#ifndef CURL_DISABLE_DOH + Curl_close(&data->req.doh.probe[0].easy); + Curl_close(&data->req.doh.probe[1].easy); +#endif } @@ -2298,7 +2203,7 @@ static CURLcode parse_proxy(struct Curl_easy *data, struct connectdata *conn, char *proxy, curl_proxytype proxytype) { - char *portptr; + char *portptr = NULL; long port = -1; char *proxyuser = NULL; char *proxypasswd = NULL; @@ -2422,6 +2327,7 @@ static CURLcode parse_proxy(struct Curl_easy *data, size_t len = strlen(host); host[len-1] = 0; /* clear the trailing bracket */ host++; + zonefrom_url(uhp, conn); } proxyinfo->host.name = host; @@ -2846,8 +2752,7 @@ static CURLcode override_login(struct Curl_easy *data, &netrc_user_changed, &netrc_passwd_changed, 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", + infof(data, "Couldn't find host %s in the .netrc file; using defaults\n", conn->host.name); } else if(ret < 0) { @@ -2913,13 +2818,6 @@ static CURLcode set_login(struct connectdata *conn) result = CURLE_OUT_OF_MEMORY; } - /* if there's a user without password, consider password blank */ - if(conn->user && !conn->passwd) { - conn->passwd = strdup(""); - if(!conn->passwd) - result = CURLE_OUT_OF_MEMORY; - } - return result; } @@ -3144,26 +3042,65 @@ static CURLcode parse_connect_to_slist(struct Curl_easy *data, if(data->asi && !host && (port == -1) && (conn->handler->protocol == CURLPROTO_HTTPS)) { /* no connect_to match, try alt-svc! */ - const char *nhost; - int nport; - enum alpnid nalpnid; + enum alpnid srcalpnid; bool hit; + struct altsvc *as; + const int allowed_versions = ( ALPN_h1 +#ifdef USE_NGHTTP2 + | ALPN_h2 +#endif +#ifdef ENABLE_QUIC + | ALPN_h3 +#endif + ) & data->asi->flags; + host = conn->host.rawalloc; +#ifdef USE_NGHTTP2 + /* with h2 support, check that first */ + srcalpnid = ALPN_h2; hit = Curl_altsvc_lookup(data->asi, - ALPN_h1, host, conn->remote_port, /* from */ - &nalpnid, &nhost, &nport /* to */); + srcalpnid, host, conn->remote_port, /* from */ + &as /* to */, + allowed_versions); + if(!hit) +#endif + { + srcalpnid = ALPN_h1; + hit = Curl_altsvc_lookup(data->asi, + srcalpnid, host, conn->remote_port, /* from */ + &as /* to */, + allowed_versions); + } if(hit) { - char *hostd = strdup((char *)nhost); + char *hostd = strdup((char *)as->dst.host); if(!hostd) return CURLE_OUT_OF_MEMORY; conn->conn_to_host.rawalloc = hostd; conn->conn_to_host.name = hostd; conn->bits.conn_to_host = TRUE; - conn->conn_to_port = nport; + conn->conn_to_port = as->dst.port; conn->bits.conn_to_port = TRUE; + conn->bits.altused = TRUE; infof(data, "Alt-svc connecting from [%s]%s:%d to [%s]%s:%d\n", - Curl_alpnid2str(ALPN_h1), host, conn->remote_port, - Curl_alpnid2str(nalpnid), hostd, nport); + Curl_alpnid2str(srcalpnid), host, conn->remote_port, + Curl_alpnid2str(as->dst.alpnid), hostd, as->dst.port); + if(srcalpnid != as->dst.alpnid) { + /* protocol version switch */ + switch(as->dst.alpnid) { + case ALPN_h1: + conn->httpversion = 11; + break; + case ALPN_h2: + conn->httpversion = 20; + break; + case ALPN_h3: + conn->transport = TRNSPRT_QUIC; + conn->httpversion = 30; + break; + default: /* shouldn't be possible */ + break; + } + } } } #endif @@ -3300,8 +3237,8 @@ static CURLcode resolve_server(struct Curl_easy *data, static void reuse_conn(struct connectdata *old_conn, struct connectdata *conn) { - free_idnconverted_hostname(&old_conn->http_proxy.host); - free_idnconverted_hostname(&old_conn->socks_proxy.host); + Curl_free_idnconverted_hostname(&old_conn->http_proxy.host); + Curl_free_idnconverted_hostname(&old_conn->socks_proxy.host); free(old_conn->http_proxy.host.rawalloc); free(old_conn->socks_proxy.host.rawalloc); @@ -3345,8 +3282,8 @@ static void reuse_conn(struct connectdata *old_conn, /* host can change, when doing keepalive with a proxy or if the case is different this time etc */ - free_idnconverted_hostname(&conn->host); - free_idnconverted_hostname(&conn->conn_to_host); + Curl_free_idnconverted_hostname(&conn->host); + Curl_free_idnconverted_hostname(&conn->conn_to_host); Curl_safefree(conn->host.rawalloc); Curl_safefree(conn->conn_to_host.rawalloc); conn->host = old_conn->host; @@ -3442,9 +3379,9 @@ static CURLcode create_conn(struct Curl_easy *data, if(result) goto out; - if(data->set.str[STRING_BEARER]) { - conn->oauth_bearer = strdup(data->set.str[STRING_BEARER]); - if(!conn->oauth_bearer) { + if(data->set.str[STRING_SASL_AUTHZID]) { + conn->sasl_authzid = strdup(data->set.str[STRING_SASL_AUTHZID]); + if(!conn->sasl_authzid) { result = CURLE_OUT_OF_MEMORY; goto out; } @@ -3505,21 +3442,21 @@ static CURLcode create_conn(struct Curl_easy *data, /************************************************************* * IDN-convert the hostnames *************************************************************/ - result = idnconvert_hostname(conn, &conn->host); + result = Curl_idnconvert_hostname(conn, &conn->host); if(result) goto out; if(conn->bits.conn_to_host) { - result = idnconvert_hostname(conn, &conn->conn_to_host); + result = Curl_idnconvert_hostname(conn, &conn->conn_to_host); if(result) goto out; } if(conn->bits.httpproxy) { - result = idnconvert_hostname(conn, &conn->http_proxy.host); + result = Curl_idnconvert_hostname(conn, &conn->http_proxy.host); if(result) goto out; } if(conn->bits.socksproxy) { - result = idnconvert_hostname(conn, &conn->socks_proxy.host); + result = Curl_idnconvert_hostname(conn, &conn->socks_proxy.host); if(result) goto out; } @@ -3631,6 +3568,10 @@ static CURLcode create_conn(struct Curl_easy *data, data->set.str[STRING_SSL_CIPHER13_LIST_ORIG]; data->set.proxy_ssl.primary.cipher_list13 = data->set.str[STRING_SSL_CIPHER13_LIST_PROXY]; + data->set.ssl.primary.pinned_key = + data->set.str[STRING_SSL_PINNEDPUBLICKEY_ORIG]; + data->set.proxy_ssl.primary.pinned_key = + data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY]; data->set.ssl.CRLfile = data->set.str[STRING_SSL_CRLFILE_ORIG]; data->set.proxy_ssl.CRLfile = data->set.str[STRING_SSL_CRLFILE_PROXY]; @@ -3688,25 +3629,6 @@ static CURLcode create_conn(struct Curl_easy *data, else reuse = ConnectionExists(data, conn, &conn_temp, &force_reuse, &waitpipe); - /* If we found a reusable connection that is now marked as in use, we may - still want to open a new connection if we are multiplexing. */ - if(reuse && !force_reuse && IsMultiplexingPossible(data, conn_temp)) { - size_t multiplexed = CONN_INUSE(conn_temp); - if(multiplexed > 0) { - infof(data, "Found connection %ld, with %zu requests on it\n", - conn_temp->connection_id, multiplexed); - - if(Curl_conncache_bundle_size(conn_temp) < max_host_connections && - Curl_conncache_size(data) < max_total_connections) { - /* We want a new connection anyway */ - reuse = FALSE; - - infof(data, "We can reuse, but we want a new connection anyway\n"); - Curl_conncache_return_conn(conn_temp); - } - } - } - if(reuse) { /* * We already have a connection for this, we got the former connection @@ -3749,8 +3671,9 @@ static CURLcode create_conn(struct Curl_easy *data, connections_available = FALSE; else { /* this gets a lock on the conncache */ + const char *bundlehost; struct connectbundle *bundle = - Curl_conncache_find_bundle(conn, data->state.conn_cache); + Curl_conncache_find_bundle(conn, data->state.conn_cache, &bundlehost); if(max_host_connections > 0 && bundle && (bundle->num_connections >= max_host_connections)) { @@ -3764,8 +3687,8 @@ static CURLcode create_conn(struct Curl_easy *data, (void)Curl_disconnect(data, conn_candidate, /* dead_connection */ FALSE); else { - infof(data, "No more connections allowed to host: %zu\n", - max_host_connections); + infof(data, "No more connections allowed to host %s: %zu\n", + bundlehost, max_host_connections); connections_available = FALSE; } } @@ -3926,7 +3849,9 @@ CURLcode Curl_setup_conn(struct connectdata *conn, } else { Curl_pgrsTime(data, TIMER_CONNECT); /* we're connected already */ - Curl_pgrsTime(data, TIMER_APPCONNECT); /* we're connected already */ + if(conn->ssl[FIRSTSOCKET].use || + (conn->handler->protocol & PROTO_FAMILY_SSH)) + Curl_pgrsTime(data, TIMER_APPCONNECT); /* we're connected already */ conn->bits.tcpconnect[FIRSTSOCKET] = TRUE; *protocol_done = TRUE; Curl_updateconninfo(conn, conn->sock[FIRSTSOCKET]); @@ -4147,34 +4072,3 @@ static unsigned int get_protocol_family(unsigned int protocol) return family; } - - -/* - * Wrapper to call functions in Curl_conncache_foreach() - * - * Returns always 0. - */ -static int conn_upkeep(struct connectdata *conn, - void *param) -{ - /* Param is unused. */ - (void)param; - - if(conn->handler->connection_check) { - /* Do a protocol-specific keepalive check on the connection. */ - conn->handler->connection_check(conn, CONNCHECK_KEEPALIVE); - } - - return 0; /* continue iteration */ -} - -CURLcode Curl_upkeep(struct conncache *conn_cache, - void *data) -{ - /* Loop over every connection and make connection alive. */ - Curl_conncache_foreach(data, - conn_cache, - data, - conn_upkeep); - return CURLE_OK; -} |