in Utilities/cmcurl/lib/url.c [858:1197]
static bool url_match_conn(struct connectdata *conn, void *userdata)
{
struct url_conn_match *match = userdata;
struct Curl_easy *data = match->data;
struct connectdata *needle = match->needle;
/* Check if `conn` can be used for transfer `data` */
if(conn->connect_only || conn->bits.close)
/* connect-only or to-be-closed connections will not be reused */
return FALSE;
if(data->set.ipver != CURL_IPRESOLVE_WHATEVER
&& data->set.ipver != conn->ip_version) {
/* skip because the connection is not via the requested IP version */
return FALSE;
}
if(needle->localdev || needle->localport) {
/* If we are bound to a specific local end (IP+port), we must not reuse a
random other one, although if we did not ask for a particular one we
can reuse one that was bound.
This comparison is a bit rough and too strict. Since the input
parameters can be specified in numerous ways and still end up the same
it would take a lot of processing to make it really accurate. Instead,
this matching will assume that reuses of bound connections will most
likely also reuse the exact same binding parameters and missing out a
few edge cases should not hurt anyone much.
*/
if((conn->localport != needle->localport) ||
(conn->localportrange != needle->localportrange) ||
(needle->localdev &&
(!conn->localdev || strcmp(conn->localdev, needle->localdev))))
return FALSE;
}
if(needle->bits.conn_to_host != conn->bits.conn_to_host)
/* do not mix connections that use the "connect to host" feature and
* connections that do not use this feature */
return FALSE;
if(needle->bits.conn_to_port != conn->bits.conn_to_port)
/* do not mix connections that use the "connect to port" feature and
* connections that do not use this feature */
return FALSE;
if(!Curl_conn_is_connected(conn, FIRSTSOCKET) ||
conn->bits.asks_multiplex) {
/* Not yet connected, or not yet decided if it multiplexes. The later
* happens for HTTP/2 Upgrade: requests that need a response. */
if(match->may_multiplex) {
match->seen_pending_conn = TRUE;
/* Do not pick a connection that has not connected yet */
infof(data, "Connection #%" FMT_OFF_T
" is not open enough, cannot reuse", conn->connection_id);
}
/* Do not pick a connection that has not connected yet */
return FALSE;
}
/* `conn` is connected. If it has transfers, can we add ours to it? */
if(CONN_INUSE(conn)) {
if(!conn->bits.multiplex) {
/* conn busy and conn cannot take more transfers */
match->seen_single_use_conn = TRUE;
return FALSE;
}
match->seen_multiplex_conn = TRUE;
if(!match->may_multiplex)
/* conn busy and transfer cannot be multiplexed */
return FALSE;
else {
/* transfer and conn multiplex. Are they on the same multi? */
struct Curl_llist_node *e = Curl_llist_head(&conn->easyq);
struct Curl_easy *entry = Curl_node_elem(e);
if(entry->multi != data->multi)
return FALSE;
}
}
/* `conn` is connected and we could add the transfer to it, if
* all the other criteria do match. */
/* Does `conn` use the correct protocol? */
#ifdef USE_UNIX_SOCKETS
if(needle->unix_domain_socket) {
if(!conn->unix_domain_socket)
return FALSE;
if(strcmp(needle->unix_domain_socket, conn->unix_domain_socket))
return FALSE;
if(needle->bits.abstract_unix_socket != conn->bits.abstract_unix_socket)
return FALSE;
}
else if(conn->unix_domain_socket)
return FALSE;
#endif
if((!(needle->handler->flags&PROTOPT_SSL) !=
!Curl_conn_is_ssl(conn, FIRSTSOCKET)) &&
!(get_protocol_family(conn->handler) == needle->handler->protocol &&
conn->bits.tls_upgraded))
/* Deny `conn` if it is not fit for `needle`'s SSL needs,
* UNLESS `conn` is the same protocol family and was upgraded to SSL. */
return FALSE;
#ifndef CURL_DISABLE_PROXY
if(needle->bits.httpproxy != conn->bits.httpproxy ||
needle->bits.socksproxy != conn->bits.socksproxy)
return FALSE;
if(needle->bits.socksproxy &&
!socks_proxy_info_matches(&needle->socks_proxy,
&conn->socks_proxy))
return FALSE;
if(needle->bits.httpproxy) {
if(needle->bits.tunnel_proxy != conn->bits.tunnel_proxy)
return FALSE;
if(!proxy_info_matches(&needle->http_proxy, &conn->http_proxy))
return FALSE;
if(IS_HTTPS_PROXY(needle->http_proxy.proxytype)) {
/* https proxies come in different types, http/1.1, h2, ... */
if(needle->http_proxy.proxytype != conn->http_proxy.proxytype)
return FALSE;
/* match SSL config to proxy */
if(!Curl_ssl_conn_config_match(data, conn, TRUE)) {
DEBUGF(infof(data,
"Connection #%" FMT_OFF_T
" has different SSL proxy parameters, cannot reuse",
conn->connection_id));
return FALSE;
}
/* the SSL config to the server, which may apply here is checked
* further below */
}
}
#endif
if(match->may_multiplex &&
(data->state.httpwant == CURL_HTTP_VERSION_2_0) &&
(needle->handler->protocol & CURLPROTO_HTTP) &&
!conn->httpversion_seen) {
if(data->set.pipewait) {
infof(data, "Server upgrade does not support multiplex yet, wait");
match->found = NULL;
match->wait_pipe = TRUE;
return TRUE; /* stop searching, we want to wait */
}
infof(data, "Server upgrade cannot be used");
return FALSE;
}
if(!(needle->handler->flags & PROTOPT_CREDSPERREQUEST)) {
/* This protocol requires credentials per connection,
so verify that we are using the same name and password as well */
if(Curl_timestrcmp(needle->user, conn->user) ||
Curl_timestrcmp(needle->passwd, conn->passwd) ||
Curl_timestrcmp(needle->sasl_authzid, conn->sasl_authzid) ||
Curl_timestrcmp(needle->oauth_bearer, conn->oauth_bearer)) {
/* one of them was different */
return FALSE;
}
}
#ifdef HAVE_GSSAPI
/* GSS delegation differences do not actually affect every connection
and auth method, but this check takes precaution before efficiency */
if(needle->gssapi_delegation != conn->gssapi_delegation)
return FALSE;
#endif
/* If looking for HTTP and the HTTP version we want is less
* than the HTTP version of conn, continue looking.
* CURL_HTTP_VERSION_2TLS is default which indicates no preference,
* so we take any existing connection. */
if((needle->handler->protocol & PROTO_FAMILY_HTTP) &&
(data->state.httpwant != CURL_HTTP_VERSION_2TLS)) {
unsigned char httpversion = Curl_conn_http_version(data);
if((httpversion >= 20) &&
(data->state.httpwant < CURL_HTTP_VERSION_2_0)) {
DEBUGF(infof(data, "nor reusing conn #%" CURL_FORMAT_CURL_OFF_T
" with httpversion=%d, we want a version less than h2",
conn->connection_id, httpversion));
}
if((httpversion >= 30) &&
(data->state.httpwant < CURL_HTTP_VERSION_3)) {
DEBUGF(infof(data, "nor reusing conn #%" CURL_FORMAT_CURL_OFF_T
" with httpversion=%d, we want a version less than h3",
conn->connection_id, httpversion));
return FALSE;
}
}
#ifdef USE_SSH
else if(get_protocol_family(needle->handler) & PROTO_FAMILY_SSH) {
if(!ssh_config_matches(needle, conn))
return FALSE;
}
#endif
#ifndef CURL_DISABLE_FTP
else if(get_protocol_family(needle->handler) & PROTO_FAMILY_FTP) {
/* Also match ACCOUNT, ALTERNATIVE-TO-USER, USE_SSL and CCC options */
if(Curl_timestrcmp(needle->proto.ftpc.account,
conn->proto.ftpc.account) ||
Curl_timestrcmp(needle->proto.ftpc.alternative_to_user,
conn->proto.ftpc.alternative_to_user) ||
(needle->proto.ftpc.use_ssl != conn->proto.ftpc.use_ssl) ||
(needle->proto.ftpc.ccc != conn->proto.ftpc.ccc))
return FALSE;
}
#endif
/* Additional match requirements if talking TLS OR
* not talking to an HTTP proxy OR using a tunnel through a proxy */
if((needle->handler->flags&PROTOPT_SSL)
#ifndef CURL_DISABLE_PROXY
|| !needle->bits.httpproxy || needle->bits.tunnel_proxy
#endif
) {
/* Talking the same protocol scheme or a TLS upgraded protocol in the
* same protocol family? */
if(!strcasecompare(needle->handler->scheme, conn->handler->scheme) &&
(get_protocol_family(conn->handler) !=
needle->handler->protocol || !conn->bits.tls_upgraded))
return FALSE;
/* If needle has "conn_to_*" set, conn must match this */
if((needle->bits.conn_to_host && !strcasecompare(
needle->conn_to_host.name, conn->conn_to_host.name)) ||
(needle->bits.conn_to_port &&
needle->conn_to_port != conn->conn_to_port))
return FALSE;
/* hostname and port must match */
if(!strcasecompare(needle->host.name, conn->host.name) ||
needle->remote_port != conn->remote_port)
return FALSE;
/* If talking TLS, conn needs to use the same SSL options. */
if((needle->handler->flags & PROTOPT_SSL) &&
!Curl_ssl_conn_config_match(data, conn, FALSE)) {
DEBUGF(infof(data,
"Connection #%" FMT_OFF_T
" has different SSL parameters, cannot reuse",
conn->connection_id));
return FALSE;
}
}
#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(match->want_ntlm_http) {
if(Curl_timestrcmp(needle->user, conn->user) ||
Curl_timestrcmp(needle->passwd, conn->passwd)) {
/* we prefer a credential match, but this is at least a connection
that can be reused and "upgraded" to NTLM */
if(conn->http_ntlm_state == NTLMSTATE_NONE)
match->found = conn;
return FALSE;
}
}
else if(conn->http_ntlm_state != NTLMSTATE_NONE) {
/* Connection is using NTLM auth but we do not want NTLM */
return FALSE;
}
#ifndef CURL_DISABLE_PROXY
/* Same for Proxy NTLM authentication */
if(match->want_proxy_ntlm_http) {
/* Both conn->http_proxy.user and conn->http_proxy.passwd can be
* NULL */
if(!conn->http_proxy.user || !conn->http_proxy.passwd)
return FALSE;
if(Curl_timestrcmp(needle->http_proxy.user,
conn->http_proxy.user) ||
Curl_timestrcmp(needle->http_proxy.passwd,
conn->http_proxy.passwd))
return FALSE;
}
else if(conn->proxy_ntlm_state != NTLMSTATE_NONE) {
/* Proxy connection is using NTLM auth but we do not want NTLM */
return FALSE;
}
#endif
if(match->want_ntlm_http || match->want_proxy_ntlm_http) {
/* Credentials are already checked, we may use this connection.
* With NTLM being weird as it is, we MUST use a
* connection where it has already been fully negotiated.
* If it has not, we keep on looking for a better one. */
match->found = conn;
if((match->want_ntlm_http &&
(conn->http_ntlm_state != NTLMSTATE_NONE)) ||
(match->want_proxy_ntlm_http &&
(conn->proxy_ntlm_state != NTLMSTATE_NONE))) {
/* We must use this connection, no other */
match->force_reuse = TRUE;
return TRUE;
}
/* Continue look up for a better connection */
return FALSE;
}
#endif
if(CONN_INUSE(conn)) {
DEBUGASSERT(match->may_multiplex);
DEBUGASSERT(conn->bits.multiplex);
/* If multiplexed, make sure we do not go over concurrency limit */
if(CONN_INUSE(conn) >=
Curl_multi_max_concurrent_streams(data->multi)) {
infof(data, "client side MAX_CONCURRENT_STREAMS reached"
", skip (%zu)", CONN_INUSE(conn));
return FALSE;
}
if(CONN_INUSE(conn) >=
Curl_conn_get_max_concurrent(data, conn, FIRSTSOCKET)) {
infof(data, "MAX_CONCURRENT_STREAMS reached, skip (%zu)",
CONN_INUSE(conn));
return FALSE;
}
/* When not multiplexed, we have a match here! */
infof(data, "Multiplexed connection found");
}
else if(Curl_conn_seems_dead(conn, data, NULL)) {
/* removed and disconnect. Do not treat as aborted. */
Curl_cpool_disconnect(data, conn, FALSE);
return FALSE;
}
/* We have found a connection. Let's stop searching. */
match->found = conn;
return TRUE;
}