mirror of
https://github.com/curl/curl.git
synced 2026-07-26 18:17:17 +03:00
cf-setup: own source file
Move the setup filter into its own source file. Move the main connect driving function, `Curl_conn_connect()`, from cfilters.c to connect.c. Closes #22022
This commit is contained in:
parent
8932063828
commit
f497b25672
9 changed files with 718 additions and 662 deletions
|
|
@ -165,6 +165,7 @@ LIB_CFILES = \
|
|||
cf-haproxy.c \
|
||||
cf-https-connect.c \
|
||||
cf-ip-happy.c \
|
||||
cf-setup.c \
|
||||
cf-socket.c \
|
||||
cfilters.c \
|
||||
conncache.c \
|
||||
|
|
@ -297,6 +298,7 @@ LIB_HFILES = \
|
|||
cf-haproxy.h \
|
||||
cf-https-connect.h \
|
||||
cf-ip-happy.h \
|
||||
cf-setup.h \
|
||||
cf-socket.h \
|
||||
cfilters.h \
|
||||
conncache.h \
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@
|
|||
#include "curl_trc.h"
|
||||
#include "cfilters.h"
|
||||
#include "cf-dns.h"
|
||||
#include "cf-setup.h"
|
||||
#include "connect.h"
|
||||
#include "hostip.h"
|
||||
#include "httpsrr.h"
|
||||
|
|
|
|||
476
lib/cf-setup.c
Normal file
476
lib/cf-setup.c
Normal file
|
|
@ -0,0 +1,476 @@
|
|||
/***************************************************************************
|
||||
* _ _ ____ _
|
||||
* Project ___| | | | _ \| |
|
||||
* / __| | | | |_) | |
|
||||
* | (__| |_| | _ <| |___
|
||||
* \___|\___/|_| \_\_____|
|
||||
*
|
||||
* Copyright (C) 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 https://curl.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
|
||||
* furnished to do so, under the terms of the COPYING file.
|
||||
*
|
||||
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
* SPDX-License-Identifier: curl
|
||||
*
|
||||
***************************************************************************/
|
||||
#include "curl_setup.h"
|
||||
|
||||
#include "urldata.h"
|
||||
#include "cfilters.h"
|
||||
#include "cf-haproxy.h"
|
||||
#include "cf-ip-happy.h"
|
||||
#include "cf-setup.h"
|
||||
#include "curl_trc.h"
|
||||
#include "connect.h"
|
||||
#include "http_proxy.h"
|
||||
#include "socks.h"
|
||||
#include "vquic/cf-capsule.h"
|
||||
#include "vquic/vquic.h"
|
||||
#include "vtls/vtls.h"
|
||||
|
||||
|
||||
typedef enum {
|
||||
CF_SETUP_INIT,
|
||||
CF_SETUP_CNNCT_EYEBALLS,
|
||||
CF_SETUP_CNNCT_SOCKS,
|
||||
CF_SETUP_CNNCT_HTTP_PROXY,
|
||||
CF_SETUP_CNNCT_HAPROXY,
|
||||
CF_SETUP_CNNCT_SSL,
|
||||
CF_SETUP_DONE
|
||||
} cf_setup_state;
|
||||
|
||||
struct cf_setup_ctx {
|
||||
cf_setup_state state;
|
||||
int ssl_mode;
|
||||
uint8_t transport;
|
||||
uint8_t retry_count;
|
||||
};
|
||||
|
||||
#ifndef CURL_DISABLE_PROXY
|
||||
|
||||
static CURLcode cf_setup_add_haproxy(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data)
|
||||
{
|
||||
struct cf_setup_ctx *ctx = cf->ctx;
|
||||
CURLcode result = CURLE_OK;
|
||||
|
||||
if(ctx->state < CF_SETUP_CNNCT_HAPROXY) {
|
||||
if(data->set.haproxyprotocol) {
|
||||
if(ctx->transport == TRNSPRT_QUIC) {
|
||||
failf(data, "haproxy protocol does not support QUIC");
|
||||
return CURLE_UNSUPPORTED_PROTOCOL;
|
||||
}
|
||||
result = Curl_cf_haproxy_insert_after(cf, data);
|
||||
if(result) {
|
||||
CURL_TRC_CF(data, cf, "adding HAPROXY filter failed -> %d",
|
||||
(int)result);
|
||||
return result;
|
||||
}
|
||||
CURL_TRC_CF(data, cf, "added HAPROXY filter");
|
||||
}
|
||||
ctx->state = CF_SETUP_CNNCT_HAPROXY;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static CURLcode cf_setup_add_socks(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data)
|
||||
{
|
||||
struct cf_setup_ctx *ctx = cf->ctx;
|
||||
CURLcode result = CURLE_OK;
|
||||
if(ctx->state < CF_SETUP_CNNCT_SOCKS && cf->conn->socks_proxy.peer) {
|
||||
/* Add a SOCKS proxy to go through `first_peer` to `second_peer`*/
|
||||
struct Curl_peer *second_peer;
|
||||
|
||||
if(cf->conn->http_proxy.peer)
|
||||
second_peer = cf->conn->http_proxy.peer;
|
||||
else
|
||||
second_peer = Curl_conn_get_destination(cf->conn, cf->sockindex);
|
||||
if(!second_peer)
|
||||
return CURLE_FAILED_INIT;
|
||||
|
||||
result = Curl_cf_socks_proxy_insert_after(
|
||||
cf, data, second_peer, cf->conn->ip_version,
|
||||
cf->conn->socks_proxy.proxytype,
|
||||
cf->conn->socks_proxy.creds);
|
||||
if(result) {
|
||||
CURL_TRC_CF(data, cf, "adding SOCKS filter failed -> %d", (int)result);
|
||||
return result;
|
||||
}
|
||||
|
||||
CURL_TRC_CF(data, cf, "added SOCKS filter to %s:%u",
|
||||
second_peer->hostname, second_peer->port);
|
||||
ctx->state = CF_SETUP_CNNCT_SOCKS;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#ifndef CURL_DISABLE_HTTP
|
||||
static CURLcode cf_setup_add_http_proxy(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data)
|
||||
{
|
||||
struct cf_setup_ctx *ctx = cf->ctx;
|
||||
CURLcode result = CURLE_OK;
|
||||
|
||||
if(ctx->state < CF_SETUP_CNNCT_HTTP_PROXY &&
|
||||
cf->conn->http_proxy.peer && !cf->conn->bits.origin_is_proxy) {
|
||||
struct Curl_peer *peer = cf->conn->http_proxy.peer;
|
||||
struct Curl_peer *tunnel_peer =
|
||||
Curl_conn_get_destination(cf->conn, cf->sockindex);
|
||||
|
||||
#ifdef USE_SSL
|
||||
if(CURL_PROXY_IS_HTTPS(cf->conn->http_proxy.proxytype) &&
|
||||
!Curl_conn_is_ssl(cf->conn, cf->sockindex)) {
|
||||
result = Curl_cf_ssl_proxy_insert_after(
|
||||
cf, data, cf->conn->http_proxy.peer);
|
||||
if(result) {
|
||||
CURL_TRC_CF(data, cf, "adding SSL filter for HTTP proxy failed -> %d",
|
||||
(int)result);
|
||||
return result;
|
||||
}
|
||||
CURL_TRC_CF(data, cf, "added SSL filter for HTTP proxy");
|
||||
}
|
||||
#endif /* USE_SSL */
|
||||
|
||||
result = Curl_cf_http_proxy_insert_after(
|
||||
cf, data, peer, tunnel_peer,
|
||||
ctx->transport, cf->conn->http_proxy.proxytype);
|
||||
if(result) {
|
||||
CURL_TRC_CF(data, cf, "adding HTTP proxy tunnel filter failed -> %d",
|
||||
(int)result);
|
||||
return result;
|
||||
}
|
||||
CURL_TRC_CF(data, cf, "added HTTP proxy tunnel filter");
|
||||
ctx->state = CF_SETUP_CNNCT_HTTP_PROXY;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
#endif /* !CURL_DISABLE_HTTP */
|
||||
#endif /* CURL_DISABLE_PROXY */
|
||||
|
||||
/* Get the origin curl connects its socket to.
|
||||
* Can be origin or the first proxy. */
|
||||
static struct Curl_peer *conn_get_first_origin(struct connectdata *conn,
|
||||
int sockindex)
|
||||
{
|
||||
#ifndef CURL_DISABLE_PROXY
|
||||
if(conn->socks_proxy.peer)
|
||||
return conn->socks_proxy.peer;
|
||||
if(conn->http_proxy.peer)
|
||||
return conn->http_proxy.peer;
|
||||
#endif
|
||||
return (sockindex == SECONDARYSOCKET) ? conn->origin2 : conn->origin;
|
||||
}
|
||||
|
||||
static CURLcode cf_setup_add_ip_happy(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data)
|
||||
{
|
||||
struct cf_setup_ctx *ctx = cf->ctx;
|
||||
CURLcode result = CURLE_OK;
|
||||
|
||||
if(ctx->state < CF_SETUP_CNNCT_EYEBALLS) {
|
||||
/* What is the first hop we directly connect to and what transport
|
||||
* do we use for it? Only on the first hop we can do Happy Eyeballs.
|
||||
* first_origin and first_peer differ on --connect-to. */
|
||||
struct Curl_peer *first_origin =
|
||||
conn_get_first_origin(cf->conn, cf->sockindex);
|
||||
struct Curl_peer *first_peer =
|
||||
Curl_conn_get_first_peer(cf->conn, cf->sockindex);
|
||||
struct Curl_peer *tunnel_peer = NULL;
|
||||
uint8_t first_transport = ctx->transport;
|
||||
|
||||
if(!first_peer)
|
||||
return CURLE_FAILED_INIT;
|
||||
|
||||
#if !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP)
|
||||
if(cf->conn->http_proxy.peer && !cf->conn->bits.origin_is_proxy) {
|
||||
first_transport =
|
||||
Curl_http_proxy_transport(cf->conn->http_proxy.proxytype);
|
||||
tunnel_peer = Curl_conn_get_destination(cf->conn, cf->sockindex);
|
||||
if((first_transport == TRNSPRT_QUIC) && cf->conn->socks_proxy.peer) {
|
||||
failf(data, "HTTP/3 proxy not possible via SOCKS");
|
||||
return CURLE_UNSUPPORTED_PROTOCOL;
|
||||
}
|
||||
}
|
||||
#endif /* !CURL_DISABLE_PROXY && !CURL_DISABLE_HTTP */
|
||||
|
||||
result = cf_ip_happy_insert_after(cf, data, first_origin, first_peer,
|
||||
first_transport,
|
||||
tunnel_peer, ctx->transport);
|
||||
if(result) {
|
||||
CURL_TRC_CF(data, cf, "adding happy eyeballs failed -> %d", (int)result);
|
||||
return result;
|
||||
}
|
||||
|
||||
if(tunnel_peer && (first_transport == TRNSPRT_QUIC)) {
|
||||
CURL_TRC_CF(data, cf, "happy eyeballing to HTTP/3 proxy %s:%u",
|
||||
first_peer->hostname, first_peer->port);
|
||||
ctx->state = CF_SETUP_CNNCT_HTTP_PROXY;
|
||||
}
|
||||
else {
|
||||
CURL_TRC_CF(data, cf, "happy eyeballing to %s %s:%u",
|
||||
tunnel_peer ? "proxy" : "origin",
|
||||
first_peer->hostname, first_peer->port);
|
||||
ctx->state = CF_SETUP_CNNCT_EYEBALLS;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static CURLcode cf_setup_add_origin_filters(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data)
|
||||
{
|
||||
struct cf_setup_ctx *ctx = cf->ctx;
|
||||
CURLcode result = CURLE_OK;
|
||||
|
||||
(void)data; /* not used in all builds */
|
||||
if(ctx->state < CF_SETUP_CNNCT_SSL) {
|
||||
#if !defined(CURL_DISABLE_HTTP) && defined(USE_HTTP3) && \
|
||||
!defined(CURL_DISABLE_PROXY)
|
||||
|
||||
/* Wanting QUIC with an HTTP tunneling filter, we now need to add
|
||||
* the QUIC filter on top. Without tunneling, this has already
|
||||
* happened in the Happy Eyeball filter. */
|
||||
if(ctx->transport == TRNSPRT_QUIC &&
|
||||
cf->conn->http_proxy.peer && !cf->conn->bits.origin_is_proxy) {
|
||||
struct Curl_peer *origin = Curl_conn_get_origin(cf->conn, cf->sockindex);
|
||||
struct Curl_peer *peer =
|
||||
Curl_conn_get_destination(cf->conn, cf->sockindex);
|
||||
|
||||
result = Curl_cf_capsule_insert_after(cf, data);
|
||||
if(result) {
|
||||
CURL_TRC_CF(data, cf, "adding capsule filter failed -> %d",
|
||||
(int)result);
|
||||
return result;
|
||||
}
|
||||
result = Curl_cf_quic_insert_after(cf, origin, peer);
|
||||
if(result) {
|
||||
CURL_TRC_CF(data, cf, "adding QUIC filter failed -> %d", (int)result);
|
||||
return result;
|
||||
}
|
||||
CURL_TRC_CF(data, cf, "added QUIC filter for origin");
|
||||
}
|
||||
else
|
||||
#endif /* !CURL_DISABLE_HTTP && USE_HTTP3 && CURL_DISABLE_PROXY */
|
||||
#ifdef USE_SSL
|
||||
if((ctx->ssl_mode == CURL_CF_SSL_ENABLE ||
|
||||
(ctx->ssl_mode != CURL_CF_SSL_DISABLE &&
|
||||
cf->conn->scheme->flags & PROTOPT_SSL)) && /* we want SSL */
|
||||
!Curl_conn_is_ssl(cf->conn, cf->sockindex)) { /* it is missing */
|
||||
|
||||
#ifndef CURL_DISABLE_PROXY
|
||||
if(cf->conn->bits.origin_is_proxy) {
|
||||
result = Curl_cf_ssl_proxy_insert_after(cf, data, cf->conn->origin);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
/* Another FTP quirk: when adding SSL verification, to a DATA
|
||||
* connection, always verify against the control's origin */
|
||||
struct Curl_peer *origin = Curl_conn_get_origin(cf->conn, FIRSTSOCKET);
|
||||
struct Curl_peer *peer =
|
||||
Curl_conn_get_destination(cf->conn, cf->sockindex);
|
||||
result = Curl_cf_ssl_insert_after(cf, data, origin, peer);
|
||||
}
|
||||
if(result) {
|
||||
CURL_TRC_CF(data, cf, "adding SSL filter for origin failed -> %d",
|
||||
(int)result);
|
||||
return result;
|
||||
}
|
||||
CURL_TRC_CF(data, cf, "added SSL filter for origin");
|
||||
}
|
||||
#endif /* USE_SSL */
|
||||
ctx->state = CF_SETUP_CNNCT_SSL;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static CURLcode cf_setup_connect_steps(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
bool *done)
|
||||
{
|
||||
struct cf_setup_ctx *ctx = cf->ctx;
|
||||
CURLcode result = CURLE_OK;
|
||||
|
||||
if(cf->connected) {
|
||||
*done = TRUE;
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
/* connect current sub-chain */
|
||||
connect_sub_chain:
|
||||
VERBOSE(Curl_conn_trc_filters(data, cf->sockindex, "cf_setup_connect"));
|
||||
|
||||
if(cf->next && !cf->next->connected) {
|
||||
result = Curl_conn_cf_connect(cf->next, data, done);
|
||||
if(result || !*done)
|
||||
return result;
|
||||
}
|
||||
|
||||
result = cf_setup_add_ip_happy(cf, data);
|
||||
if(result)
|
||||
return result;
|
||||
if(!cf->next || !cf->next->connected)
|
||||
goto connect_sub_chain;
|
||||
|
||||
#ifndef CURL_DISABLE_PROXY
|
||||
result = cf_setup_add_socks(cf, data);
|
||||
if(result)
|
||||
return result;
|
||||
if(!cf->next || !cf->next->connected)
|
||||
goto connect_sub_chain;
|
||||
|
||||
#ifndef CURL_DISABLE_HTTP
|
||||
result = cf_setup_add_http_proxy(cf, data);
|
||||
if(result)
|
||||
return result;
|
||||
if(!cf->next || !cf->next->connected)
|
||||
goto connect_sub_chain;
|
||||
#endif /* !CURL_DISABLE_HTTP */
|
||||
|
||||
result = cf_setup_add_haproxy(cf, data);
|
||||
if(result)
|
||||
return result;
|
||||
if(!cf->next || !cf->next->connected)
|
||||
goto connect_sub_chain;
|
||||
#endif /* !CURL_DISABLE_PROXY */
|
||||
|
||||
result = cf_setup_add_origin_filters(cf, data);
|
||||
if(result)
|
||||
return result;
|
||||
if(!cf->next || !cf->next->connected)
|
||||
goto connect_sub_chain;
|
||||
|
||||
ctx->state = CF_SETUP_DONE;
|
||||
cf->connected = TRUE;
|
||||
*done = TRUE;
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
static CURLcode cf_setup_connect(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
bool *done)
|
||||
{
|
||||
struct cf_setup_ctx *ctx = cf->ctx;
|
||||
CURLcode result;
|
||||
|
||||
/* In some situations, a server/proxy may close the connection and
|
||||
* we need to connect again (HTTP/1.x proxy auth, for example).
|
||||
* We used to close the filters and reuse them for another attempt,
|
||||
* however that complicates filter code and it is simpler to tear them
|
||||
* all down and start over. */
|
||||
retry:
|
||||
result = cf_setup_connect_steps(cf, data, done);
|
||||
|
||||
if(result == CURLE_AGAIN) {
|
||||
++ctx->retry_count;
|
||||
if(ctx->retry_count > 5) /* arbitrary limit, better just timeout? */
|
||||
return CURLE_COULDNT_CONNECT;
|
||||
|
||||
CURL_TRC_CF(data, cf, "retrying connect, %d. time", ctx->retry_count);
|
||||
Curl_conn_cf_discard_chain(&cf->next, data);
|
||||
ctx->state = CF_SETUP_INIT;
|
||||
goto retry;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static void cf_setup_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
|
||||
{
|
||||
struct cf_setup_ctx *ctx = cf->ctx;
|
||||
|
||||
CURL_TRC_CF(data, cf, "destroy");
|
||||
curlx_safefree(ctx);
|
||||
}
|
||||
|
||||
struct Curl_cftype Curl_cft_setup = {
|
||||
"SETUP",
|
||||
CF_TYPE_SETUP,
|
||||
CURL_LOG_LVL_NONE,
|
||||
cf_setup_destroy,
|
||||
cf_setup_connect,
|
||||
Curl_cf_def_shutdown,
|
||||
Curl_cf_def_adjust_pollset,
|
||||
Curl_cf_def_data_pending,
|
||||
Curl_cf_def_send,
|
||||
Curl_cf_def_recv,
|
||||
Curl_cf_def_cntrl,
|
||||
Curl_cf_def_conn_is_alive,
|
||||
Curl_cf_def_conn_keep_alive,
|
||||
Curl_cf_def_query,
|
||||
};
|
||||
|
||||
static CURLcode cf_setup_create(struct Curl_cfilter **pcf,
|
||||
struct Curl_easy *data,
|
||||
uint8_t transport,
|
||||
int ssl_mode)
|
||||
{
|
||||
struct Curl_cfilter *cf = NULL;
|
||||
struct cf_setup_ctx *ctx;
|
||||
CURLcode result = CURLE_OK;
|
||||
|
||||
(void)data;
|
||||
ctx = curlx_calloc(1, sizeof(*ctx));
|
||||
if(!ctx) {
|
||||
result = CURLE_OUT_OF_MEMORY;
|
||||
goto out;
|
||||
}
|
||||
ctx->state = CF_SETUP_INIT;
|
||||
ctx->ssl_mode = ssl_mode;
|
||||
ctx->transport = transport;
|
||||
|
||||
result = Curl_cf_create(&cf, &Curl_cft_setup, ctx);
|
||||
if(result)
|
||||
goto out;
|
||||
ctx = NULL;
|
||||
|
||||
out:
|
||||
*pcf = result ? NULL : cf;
|
||||
if(ctx) {
|
||||
curlx_free(ctx);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
CURLcode Curl_cf_setup_add(struct Curl_easy *data,
|
||||
struct connectdata *conn,
|
||||
int sockindex,
|
||||
uint8_t transport,
|
||||
int ssl_mode)
|
||||
{
|
||||
struct Curl_cfilter *cf;
|
||||
CURLcode result = CURLE_OK;
|
||||
|
||||
DEBUGASSERT(data);
|
||||
result = cf_setup_create(&cf, data, transport, ssl_mode);
|
||||
if(result)
|
||||
goto out;
|
||||
Curl_conn_cf_add(data, conn, sockindex, cf);
|
||||
out:
|
||||
return result;
|
||||
}
|
||||
|
||||
CURLcode Curl_cf_setup_insert_after(struct Curl_cfilter *cf_at,
|
||||
struct Curl_easy *data,
|
||||
uint8_t transport,
|
||||
int ssl_mode)
|
||||
{
|
||||
struct Curl_cfilter *cf;
|
||||
CURLcode result;
|
||||
|
||||
DEBUGASSERT(data);
|
||||
result = cf_setup_create(&cf, data, transport, ssl_mode);
|
||||
if(result)
|
||||
goto out;
|
||||
Curl_conn_cf_insert_after(cf_at, cf);
|
||||
out:
|
||||
return result;
|
||||
}
|
||||
46
lib/cf-setup.h
Normal file
46
lib/cf-setup.h
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
#ifndef HEADER_CURL_CF_SETUP_H
|
||||
#define HEADER_CURL_CF_SETUP_H
|
||||
/***************************************************************************
|
||||
* _ _ ____ _
|
||||
* Project ___| | | | _ \| |
|
||||
* / __| | | | |_) | |
|
||||
* | (__| |_| | _ <| |___
|
||||
* \___|\___/|_| \_\_____|
|
||||
*
|
||||
* Copyright (C) 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 https://curl.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
|
||||
* furnished to do so, under the terms of the COPYING file.
|
||||
*
|
||||
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
* SPDX-License-Identifier: curl
|
||||
*
|
||||
***************************************************************************/
|
||||
#include "curl_setup.h"
|
||||
|
||||
struct Curl_dns_entry;
|
||||
struct ip_quadruple;
|
||||
struct Curl_peer;
|
||||
struct Curl_str;
|
||||
|
||||
CURLcode Curl_cf_setup_add(struct Curl_easy *data,
|
||||
struct connectdata *conn,
|
||||
int sockindex,
|
||||
uint8_t transport,
|
||||
int ssl_mode);
|
||||
|
||||
CURLcode Curl_cf_setup_insert_after(struct Curl_cfilter *cf_at,
|
||||
struct Curl_easy *data,
|
||||
uint8_t transport,
|
||||
int ssl_mode);
|
||||
|
||||
extern struct Curl_cftype Curl_cft_setup;
|
||||
|
||||
#endif /* HEADER_CURL_CF_SETUP_H */
|
||||
174
lib/cfilters.c
174
lib/cfilters.c
|
|
@ -423,29 +423,6 @@ CURLcode Curl_conn_cf_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
|
|||
return CURLE_RECV_ERROR;
|
||||
}
|
||||
|
||||
#ifdef CURLVERBOSE
|
||||
static CURLcode cf_verboseconnect(struct Curl_easy *data,
|
||||
struct Curl_cfilter *cf)
|
||||
{
|
||||
if(Curl_trc_is_verbose(data)) {
|
||||
struct ip_quadruple ipquad;
|
||||
bool is_ipv6;
|
||||
CURLcode result;
|
||||
|
||||
result = Curl_conn_cf_get_ip_info(cf, data, &is_ipv6, &ipquad);
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
infof(data, "Established %sconnection to %s (%s port %u) from %s port %u ",
|
||||
(cf->sockindex == SECONDARYSOCKET) ? "2nd " : "",
|
||||
CURL_CONN_HOST_DISPNAME(data->conn),
|
||||
ipquad.remote_ip, ipquad.remote_port,
|
||||
ipquad.local_ip, ipquad.local_port);
|
||||
}
|
||||
return CURLE_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
static CURLcode cf_cntrl_all(struct connectdata *conn,
|
||||
struct Curl_easy *data,
|
||||
bool ignore_result,
|
||||
|
|
@ -463,36 +440,14 @@ static CURLcode cf_cntrl_all(struct connectdata *conn,
|
|||
return result;
|
||||
}
|
||||
|
||||
static void cf_cntrl_update_info(struct Curl_easy *data,
|
||||
struct connectdata *conn)
|
||||
void Curl_conn_cntrl_update_info(struct Curl_easy *data,
|
||||
struct connectdata *conn)
|
||||
{
|
||||
cf_cntrl_all(conn, data, TRUE, CF_CTRL_CONN_INFO_UPDATE, 0, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update connection statistics
|
||||
*/
|
||||
static void conn_report_connect_stats(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data)
|
||||
{
|
||||
if(cf) {
|
||||
struct curltime connected;
|
||||
struct curltime appconnected;
|
||||
|
||||
memset(&connected, 0, sizeof(connected));
|
||||
cf->cft->query(cf, data, CF_QUERY_TIMER_CONNECT, NULL, &connected);
|
||||
if(connected.tv_sec || connected.tv_usec)
|
||||
Curl_pgrsTimeWas(data, TIMER_CONNECT, connected);
|
||||
|
||||
memset(&appconnected, 0, sizeof(appconnected));
|
||||
cf->cft->query(cf, data, CF_QUERY_TIMER_APPCONNECT, NULL, &appconnected);
|
||||
if(appconnected.tv_sec || appconnected.tv_usec)
|
||||
Curl_pgrsTimeWas(data, TIMER_APPCONNECT, appconnected);
|
||||
}
|
||||
}
|
||||
|
||||
static void conn_remove_setup_filters(struct Curl_easy *data,
|
||||
int sockindex)
|
||||
void Curl_conn_remove_setup_filters(struct Curl_easy *data,
|
||||
int sockindex)
|
||||
{
|
||||
struct Curl_cfilter **anchor = &data->conn->cfilter[sockindex];
|
||||
while(*anchor) {
|
||||
|
|
@ -509,127 +464,6 @@ static void conn_remove_setup_filters(struct Curl_easy *data,
|
|||
}
|
||||
}
|
||||
|
||||
CURLcode Curl_conn_connect(struct Curl_easy *data,
|
||||
int sockindex,
|
||||
bool blocking,
|
||||
bool *done)
|
||||
{
|
||||
#define CF_CONN_NUM_POLLS_ON_STACK 5
|
||||
struct pollfd a_few_on_stack[CF_CONN_NUM_POLLS_ON_STACK];
|
||||
struct easy_pollset ps;
|
||||
struct curl_pollfds cpfds;
|
||||
struct Curl_cfilter *cf;
|
||||
CURLcode result = CURLE_OK;
|
||||
|
||||
DEBUGASSERT(data);
|
||||
DEBUGASSERT(data->conn);
|
||||
if(!CONN_SOCK_IDX_VALID(sockindex))
|
||||
return CURLE_BAD_FUNCTION_ARGUMENT;
|
||||
|
||||
if(data->conn->scheme->flags & PROTOPT_NONETWORK) {
|
||||
*done = TRUE;
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
cf = data->conn->cfilter[sockindex];
|
||||
if(!cf) {
|
||||
*done = FALSE;
|
||||
return CURLE_FAILED_INIT;
|
||||
}
|
||||
|
||||
*done = (bool)cf->connected;
|
||||
if(*done)
|
||||
return CURLE_OK;
|
||||
|
||||
Curl_pollset_init(&ps);
|
||||
Curl_pollfds_init(&cpfds, a_few_on_stack, CF_CONN_NUM_POLLS_ON_STACK);
|
||||
while(!*done) {
|
||||
if(Curl_conn_needs_flush(data, sockindex)) {
|
||||
DEBUGF(infof(data, "Curl_conn_connect(index=%d), flush", sockindex));
|
||||
result = Curl_conn_flush(data, sockindex);
|
||||
if(result && (result != CURLE_AGAIN))
|
||||
return result;
|
||||
}
|
||||
|
||||
result = cf->cft->do_connect(cf, data, done);
|
||||
CURL_TRC_CF(data, cf, "Curl_conn_connect(block=%d) -> %d, done=%d",
|
||||
blocking, (int)result, *done);
|
||||
if(!result && *done) {
|
||||
/* A final sanity check on connection security */
|
||||
if((data->state.origin->scheme->flags & PROTOPT_SSL) &&
|
||||
(sockindex == FIRSTSOCKET) &&
|
||||
!Curl_conn_is_ssl(data->conn, FIRSTSOCKET)) {
|
||||
DEBUGASSERT(0);
|
||||
failf(data, "transfer requires SSL, but not connected via SSL");
|
||||
return CURLE_FAILED_INIT;
|
||||
}
|
||||
/* Now that the complete filter chain is connected, let all filters
|
||||
* persist information at the connection. E.g. cf-socket sets the
|
||||
* socket and ip related information. */
|
||||
cf_cntrl_update_info(data, data->conn);
|
||||
conn_report_connect_stats(cf, data);
|
||||
data->conn->keepalive = *Curl_pgrs_now(data);
|
||||
VERBOSE(result = cf_verboseconnect(data, cf));
|
||||
VERBOSE(Curl_conn_trc_filters(data, sockindex, "connected"));
|
||||
conn_remove_setup_filters(data, sockindex);
|
||||
VERBOSE(Curl_conn_trc_filters(data, sockindex, "reduced to"));
|
||||
goto out;
|
||||
}
|
||||
else if(result) {
|
||||
CURL_TRC_CF(data, cf, "Curl_conn_connect(), filter returned %d",
|
||||
(int)result);
|
||||
VERBOSE(Curl_conn_trc_filters(data, sockindex, "failed to connect"));
|
||||
conn_report_connect_stats(cf, data);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if(!blocking)
|
||||
goto out;
|
||||
else {
|
||||
/* check allowed time left */
|
||||
const timediff_t timeout_ms = Curl_timeleft_ms(data);
|
||||
curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data);
|
||||
int rc;
|
||||
|
||||
if(timeout_ms < 0) {
|
||||
/* no need to continue if time already is up */
|
||||
failf(data, "connect timeout");
|
||||
result = CURLE_OPERATION_TIMEDOUT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
CURL_TRC_CF(data, cf, "Curl_conn_connect(block=1), do poll");
|
||||
Curl_pollset_reset(&ps);
|
||||
Curl_pollfds_reset(&cpfds);
|
||||
/* In general, we want to send after connect, wait on that. */
|
||||
if(sockfd != CURL_SOCKET_BAD)
|
||||
result = Curl_pollset_set_out_only(data, &ps, sockfd);
|
||||
if(!result)
|
||||
result = Curl_conn_adjust_pollset(data, data->conn, &ps);
|
||||
if(result)
|
||||
goto out;
|
||||
result = Curl_pollfds_add_ps(&cpfds, &ps);
|
||||
if(result)
|
||||
goto out;
|
||||
|
||||
rc = Curl_poll(cpfds.pfds, cpfds.n,
|
||||
CURLMIN(timeout_ms, (cpfds.n ? 1000 : 10)));
|
||||
CURL_TRC_CF(data, cf, "Curl_conn_connect(block=1), Curl_poll() -> %d",
|
||||
rc);
|
||||
if(rc < 0) {
|
||||
result = CURLE_COULDNT_CONNECT;
|
||||
goto out;
|
||||
}
|
||||
/* continue iterating */
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
Curl_pollset_cleanup(&ps);
|
||||
Curl_pollfds_cleanup(&cpfds);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Curl_conn_is_setup(struct connectdata *conn, int sockindex)
|
||||
{
|
||||
if(!CONN_SOCK_IDX_VALID(sockindex))
|
||||
|
|
|
|||
|
|
@ -358,16 +358,6 @@ bool Curl_conn_cf_wants_httpsrr(struct Curl_cfilter *cf,
|
|||
#define CURL_CF_SSL_DISABLE 0
|
||||
#define CURL_CF_SSL_ENABLE 1
|
||||
|
||||
/**
|
||||
* Bring the filter chain at `sockindex` for connection `data->conn` into
|
||||
* connected state. Which will set `*done` to TRUE.
|
||||
* This can be called on an already connected chain with no side effects.
|
||||
* When not `blocking`, calls may return without error and `*done != TRUE`,
|
||||
* while the individual filters negotiated the connection.
|
||||
*/
|
||||
CURLcode Curl_conn_connect(struct Curl_easy *data, int sockindex,
|
||||
bool blocking, bool *done);
|
||||
|
||||
/**
|
||||
* Check if a filter chain at `sockindex` for connection `conn` exists.
|
||||
*/
|
||||
|
|
@ -431,6 +421,12 @@ unsigned char Curl_conn_get_transport(struct Curl_easy *data,
|
|||
const char *Curl_conn_get_alpn_negotiated(struct Curl_easy *data,
|
||||
struct connectdata *conn);
|
||||
|
||||
void Curl_conn_cntrl_update_info(struct Curl_easy *data,
|
||||
struct connectdata *conn);
|
||||
|
||||
void Curl_conn_remove_setup_filters(struct Curl_easy *data,
|
||||
int sockindex);
|
||||
|
||||
/**
|
||||
* Shutdown the connection at `sockindex` non-blocking, using timeout
|
||||
* from `data->set.shutdowntimeout`, default DEFAULT_SHUTDOWN_TIMEOUT_MS.
|
||||
|
|
|
|||
644
lib/connect.c
644
lib/connect.c
|
|
@ -23,49 +23,20 @@
|
|||
***************************************************************************/
|
||||
#include "curl_setup.h"
|
||||
|
||||
#ifdef HAVE_NETINET_IN_H
|
||||
#include <netinet/in.h> /* <netinet/tcp.h> may need it */
|
||||
#endif
|
||||
#ifdef HAVE_LINUX_TCP_H
|
||||
#include <linux/tcp.h>
|
||||
#elif defined(HAVE_NETINET_TCP_H)
|
||||
#include <netinet/tcp.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYS_IOCTL_H
|
||||
#include <sys/ioctl.h>
|
||||
#endif
|
||||
#ifdef HAVE_NETDB_H
|
||||
#include <netdb.h>
|
||||
#endif
|
||||
#ifdef HAVE_ARPA_INET_H
|
||||
#include <arpa/inet.h>
|
||||
#endif
|
||||
|
||||
#ifdef __VMS
|
||||
#include <in.h>
|
||||
#include <inet.h>
|
||||
#endif
|
||||
|
||||
#include "urldata.h"
|
||||
#include "curl_trc.h"
|
||||
#include "strerror.h"
|
||||
#include "cfilters.h"
|
||||
#include "connect.h"
|
||||
#include "cf-dns.h"
|
||||
#include "cf-haproxy.h"
|
||||
#include "cf-https-connect.h"
|
||||
#include "cf-ip-happy.h"
|
||||
#include "cf-socket.h"
|
||||
#include "cf-setup.h"
|
||||
#include "multiif.h"
|
||||
#include "curlx/strparse.h"
|
||||
#include "vtls/vtls.h" /* for vtls cfilters */
|
||||
#include "vquic/vquic.h" /* for QUIC cfilters */
|
||||
#include "vquic/cf-capsule.h"
|
||||
#include "progress.h"
|
||||
#include "conncache.h"
|
||||
#include "multihandle.h"
|
||||
#include "http_proxy.h"
|
||||
#include "socks.h"
|
||||
#include "select.h"
|
||||
#include "curlx/strparse.h"
|
||||
|
||||
#if !defined(CURL_DISABLE_ALTSVC) || defined(USE_HTTPSRR)
|
||||
|
||||
|
|
@ -270,444 +241,6 @@ void Curl_conncontrol(struct connectdata *conn,
|
|||
}
|
||||
}
|
||||
|
||||
typedef enum {
|
||||
CF_SETUP_INIT,
|
||||
CF_SETUP_CNNCT_EYEBALLS,
|
||||
CF_SETUP_CNNCT_SOCKS,
|
||||
CF_SETUP_CNNCT_HTTP_PROXY,
|
||||
CF_SETUP_CNNCT_HAPROXY,
|
||||
CF_SETUP_CNNCT_SSL,
|
||||
CF_SETUP_DONE
|
||||
} cf_setup_state;
|
||||
|
||||
struct cf_setup_ctx {
|
||||
cf_setup_state state;
|
||||
int ssl_mode;
|
||||
uint8_t transport;
|
||||
uint8_t retry_count;
|
||||
};
|
||||
|
||||
#ifndef CURL_DISABLE_PROXY
|
||||
|
||||
static CURLcode cf_setup_add_haproxy(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data)
|
||||
{
|
||||
struct cf_setup_ctx *ctx = cf->ctx;
|
||||
CURLcode result = CURLE_OK;
|
||||
|
||||
if(ctx->state < CF_SETUP_CNNCT_HAPROXY) {
|
||||
if(data->set.haproxyprotocol) {
|
||||
if(ctx->transport == TRNSPRT_QUIC) {
|
||||
failf(data, "haproxy protocol does not support QUIC");
|
||||
return CURLE_UNSUPPORTED_PROTOCOL;
|
||||
}
|
||||
result = Curl_cf_haproxy_insert_after(cf, data);
|
||||
if(result) {
|
||||
CURL_TRC_CF(data, cf, "adding HAPROXY filter failed -> %d",
|
||||
(int)result);
|
||||
return result;
|
||||
}
|
||||
CURL_TRC_CF(data, cf, "added HAPROXY filter");
|
||||
}
|
||||
ctx->state = CF_SETUP_CNNCT_HAPROXY;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static CURLcode cf_setup_add_socks(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data)
|
||||
{
|
||||
struct cf_setup_ctx *ctx = cf->ctx;
|
||||
CURLcode result = CURLE_OK;
|
||||
if(ctx->state < CF_SETUP_CNNCT_SOCKS && cf->conn->socks_proxy.peer) {
|
||||
/* Add a SOCKS proxy to go through `first_peer` to `second_peer`*/
|
||||
struct Curl_peer *second_peer;
|
||||
|
||||
if(cf->conn->http_proxy.peer)
|
||||
second_peer = cf->conn->http_proxy.peer;
|
||||
else
|
||||
second_peer = Curl_conn_get_destination(cf->conn, cf->sockindex);
|
||||
if(!second_peer)
|
||||
return CURLE_FAILED_INIT;
|
||||
|
||||
result = Curl_cf_socks_proxy_insert_after(
|
||||
cf, data, second_peer, cf->conn->ip_version,
|
||||
cf->conn->socks_proxy.proxytype,
|
||||
cf->conn->socks_proxy.creds);
|
||||
if(result) {
|
||||
CURL_TRC_CF(data, cf, "adding SOCKS filter failed -> %d", (int)result);
|
||||
return result;
|
||||
}
|
||||
|
||||
CURL_TRC_CF(data, cf, "added SOCKS filter to %s:%u",
|
||||
second_peer->hostname, second_peer->port);
|
||||
ctx->state = CF_SETUP_CNNCT_SOCKS;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#ifndef CURL_DISABLE_HTTP
|
||||
static CURLcode cf_setup_add_http_proxy(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data)
|
||||
{
|
||||
struct cf_setup_ctx *ctx = cf->ctx;
|
||||
CURLcode result = CURLE_OK;
|
||||
|
||||
if(ctx->state < CF_SETUP_CNNCT_HTTP_PROXY &&
|
||||
cf->conn->http_proxy.peer && !cf->conn->bits.origin_is_proxy) {
|
||||
struct Curl_peer *peer = cf->conn->http_proxy.peer;
|
||||
struct Curl_peer *tunnel_peer =
|
||||
Curl_conn_get_destination(cf->conn, cf->sockindex);
|
||||
|
||||
#ifdef USE_SSL
|
||||
if(CURL_PROXY_IS_HTTPS(cf->conn->http_proxy.proxytype) &&
|
||||
!Curl_conn_is_ssl(cf->conn, cf->sockindex)) {
|
||||
result = Curl_cf_ssl_proxy_insert_after(
|
||||
cf, data, cf->conn->http_proxy.peer);
|
||||
if(result) {
|
||||
CURL_TRC_CF(data, cf, "adding SSL filter for HTTP proxy failed -> %d",
|
||||
(int)result);
|
||||
return result;
|
||||
}
|
||||
CURL_TRC_CF(data, cf, "added SSL filter for HTTP proxy");
|
||||
}
|
||||
#endif /* USE_SSL */
|
||||
|
||||
result = Curl_cf_http_proxy_insert_after(
|
||||
cf, data, peer, tunnel_peer,
|
||||
ctx->transport, cf->conn->http_proxy.proxytype);
|
||||
if(result) {
|
||||
CURL_TRC_CF(data, cf, "adding HTTP proxy tunnel filter failed -> %d",
|
||||
(int)result);
|
||||
return result;
|
||||
}
|
||||
CURL_TRC_CF(data, cf, "added HTTP proxy tunnel filter");
|
||||
ctx->state = CF_SETUP_CNNCT_HTTP_PROXY;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
#endif /* !CURL_DISABLE_HTTP */
|
||||
#endif /* CURL_DISABLE_PROXY */
|
||||
|
||||
/* Get the origin curl connects its socket to.
|
||||
* Can be origin or the first proxy. */
|
||||
static struct Curl_peer *conn_get_first_origin(struct connectdata *conn,
|
||||
int sockindex)
|
||||
{
|
||||
#ifndef CURL_DISABLE_PROXY
|
||||
if(conn->socks_proxy.peer)
|
||||
return conn->socks_proxy.peer;
|
||||
if(conn->http_proxy.peer)
|
||||
return conn->http_proxy.peer;
|
||||
#endif
|
||||
return (sockindex == SECONDARYSOCKET) ? conn->origin2 : conn->origin;
|
||||
}
|
||||
|
||||
static CURLcode cf_setup_add_ip_happy(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data)
|
||||
{
|
||||
struct cf_setup_ctx *ctx = cf->ctx;
|
||||
CURLcode result = CURLE_OK;
|
||||
|
||||
if(ctx->state < CF_SETUP_CNNCT_EYEBALLS) {
|
||||
/* What is the first hop we directly connect to and what transport
|
||||
* do we use for it? Only on the first hop we can do Happy Eyeballs.
|
||||
* first_origin and first_peer differ on --connect-to. */
|
||||
struct Curl_peer *first_origin =
|
||||
conn_get_first_origin(cf->conn, cf->sockindex);
|
||||
struct Curl_peer *first_peer =
|
||||
Curl_conn_get_first_peer(cf->conn, cf->sockindex);
|
||||
struct Curl_peer *tunnel_peer = NULL;
|
||||
uint8_t first_transport = ctx->transport;
|
||||
|
||||
if(!first_peer)
|
||||
return CURLE_FAILED_INIT;
|
||||
|
||||
#if !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP)
|
||||
if(cf->conn->http_proxy.peer && !cf->conn->bits.origin_is_proxy) {
|
||||
first_transport =
|
||||
Curl_http_proxy_transport(cf->conn->http_proxy.proxytype);
|
||||
tunnel_peer = Curl_conn_get_destination(cf->conn, cf->sockindex);
|
||||
if((first_transport == TRNSPRT_QUIC) && cf->conn->socks_proxy.peer) {
|
||||
failf(data, "HTTP/3 proxy not possible via SOCKS");
|
||||
return CURLE_UNSUPPORTED_PROTOCOL;
|
||||
}
|
||||
}
|
||||
#endif /* !CURL_DISABLE_PROXY && !CURL_DISABLE_HTTP */
|
||||
|
||||
result = cf_ip_happy_insert_after(cf, data, first_origin, first_peer,
|
||||
first_transport,
|
||||
tunnel_peer, ctx->transport);
|
||||
if(result) {
|
||||
CURL_TRC_CF(data, cf, "adding happy eyeballs failed -> %d", (int)result);
|
||||
return result;
|
||||
}
|
||||
|
||||
if(tunnel_peer && (first_transport == TRNSPRT_QUIC)) {
|
||||
CURL_TRC_CF(data, cf, "happy eyeballing to HTTP/3 proxy %s:%u",
|
||||
first_peer->hostname, first_peer->port);
|
||||
ctx->state = CF_SETUP_CNNCT_HTTP_PROXY;
|
||||
}
|
||||
else {
|
||||
CURL_TRC_CF(data, cf, "happy eyeballing to %s %s:%u",
|
||||
tunnel_peer ? "proxy" : "origin",
|
||||
first_peer->hostname, first_peer->port);
|
||||
ctx->state = CF_SETUP_CNNCT_EYEBALLS;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static CURLcode cf_setup_add_origin_filters(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data)
|
||||
{
|
||||
struct cf_setup_ctx *ctx = cf->ctx;
|
||||
CURLcode result = CURLE_OK;
|
||||
|
||||
(void)data; /* not used in all builds */
|
||||
if(ctx->state < CF_SETUP_CNNCT_SSL) {
|
||||
#if !defined(CURL_DISABLE_HTTP) && defined(USE_HTTP3) && \
|
||||
!defined(CURL_DISABLE_PROXY)
|
||||
|
||||
/* Wanting QUIC with an HTTP tunneling filter, we now need to add
|
||||
* the QUIC filter on top. Without tunneling, this has already
|
||||
* happened in the Happy Eyeball filter. */
|
||||
if(ctx->transport == TRNSPRT_QUIC &&
|
||||
cf->conn->http_proxy.peer && !cf->conn->bits.origin_is_proxy) {
|
||||
struct Curl_peer *origin = Curl_conn_get_origin(cf->conn, cf->sockindex);
|
||||
struct Curl_peer *peer =
|
||||
Curl_conn_get_destination(cf->conn, cf->sockindex);
|
||||
|
||||
result = Curl_cf_capsule_insert_after(cf, data);
|
||||
if(result) {
|
||||
CURL_TRC_CF(data, cf, "adding capsule filter failed -> %d",
|
||||
(int)result);
|
||||
return result;
|
||||
}
|
||||
result = Curl_cf_quic_insert_after(cf, origin, peer);
|
||||
if(result) {
|
||||
CURL_TRC_CF(data, cf, "adding QUIC filter failed -> %d", (int)result);
|
||||
return result;
|
||||
}
|
||||
CURL_TRC_CF(data, cf, "added QUIC filter for origin");
|
||||
}
|
||||
else
|
||||
#endif /* !CURL_DISABLE_HTTP && USE_HTTP3 && CURL_DISABLE_PROXY */
|
||||
#ifdef USE_SSL
|
||||
if((ctx->ssl_mode == CURL_CF_SSL_ENABLE ||
|
||||
(ctx->ssl_mode != CURL_CF_SSL_DISABLE &&
|
||||
data->state.origin->scheme->flags & PROTOPT_SSL)) && /* we want SSL */
|
||||
!Curl_conn_is_ssl(cf->conn, cf->sockindex)) { /* it is missing */
|
||||
|
||||
#ifndef CURL_DISABLE_PROXY
|
||||
if(cf->conn->bits.origin_is_proxy) {
|
||||
result = Curl_cf_ssl_proxy_insert_after(cf, data, cf->conn->origin);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
/* Another FTP quirk: when adding SSL verification, to a DATA
|
||||
* connection, always verify against the control's origin */
|
||||
struct Curl_peer *origin = Curl_conn_get_origin(cf->conn, FIRSTSOCKET);
|
||||
struct Curl_peer *peer =
|
||||
Curl_conn_get_destination(cf->conn, cf->sockindex);
|
||||
result = Curl_cf_ssl_insert_after(cf, data, origin, peer);
|
||||
}
|
||||
if(result) {
|
||||
CURL_TRC_CF(data, cf, "adding SSL filter for origin failed -> %d",
|
||||
(int)result);
|
||||
return result;
|
||||
}
|
||||
CURL_TRC_CF(data, cf, "added SSL filter for origin");
|
||||
}
|
||||
#endif /* USE_SSL */
|
||||
ctx->state = CF_SETUP_CNNCT_SSL;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static CURLcode cf_setup_connect_steps(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
bool *done)
|
||||
{
|
||||
struct cf_setup_ctx *ctx = cf->ctx;
|
||||
CURLcode result = CURLE_OK;
|
||||
|
||||
if(cf->connected) {
|
||||
*done = TRUE;
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
/* connect current sub-chain */
|
||||
connect_sub_chain:
|
||||
VERBOSE(Curl_conn_trc_filters(data, cf->sockindex, "cf_setup_connect"));
|
||||
|
||||
if(cf->next && !cf->next->connected) {
|
||||
result = Curl_conn_cf_connect(cf->next, data, done);
|
||||
if(result || !*done)
|
||||
return result;
|
||||
}
|
||||
|
||||
result = cf_setup_add_ip_happy(cf, data);
|
||||
if(result)
|
||||
return result;
|
||||
if(!cf->next || !cf->next->connected)
|
||||
goto connect_sub_chain;
|
||||
|
||||
#ifndef CURL_DISABLE_PROXY
|
||||
result = cf_setup_add_socks(cf, data);
|
||||
if(result)
|
||||
return result;
|
||||
if(!cf->next || !cf->next->connected)
|
||||
goto connect_sub_chain;
|
||||
|
||||
#ifndef CURL_DISABLE_HTTP
|
||||
result = cf_setup_add_http_proxy(cf, data);
|
||||
if(result)
|
||||
return result;
|
||||
if(!cf->next || !cf->next->connected)
|
||||
goto connect_sub_chain;
|
||||
#endif /* !CURL_DISABLE_HTTP */
|
||||
|
||||
result = cf_setup_add_haproxy(cf, data);
|
||||
if(result)
|
||||
return result;
|
||||
if(!cf->next || !cf->next->connected)
|
||||
goto connect_sub_chain;
|
||||
#endif /* !CURL_DISABLE_PROXY */
|
||||
|
||||
result = cf_setup_add_origin_filters(cf, data);
|
||||
if(result)
|
||||
return result;
|
||||
if(!cf->next || !cf->next->connected)
|
||||
goto connect_sub_chain;
|
||||
|
||||
ctx->state = CF_SETUP_DONE;
|
||||
cf->connected = TRUE;
|
||||
*done = TRUE;
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
static CURLcode cf_setup_connect(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
bool *done)
|
||||
{
|
||||
struct cf_setup_ctx *ctx = cf->ctx;
|
||||
CURLcode result;
|
||||
|
||||
/* In some situations, a server/proxy may close the connection and
|
||||
* we need to connect again (HTTP/1.x proxy auth, for example).
|
||||
* We used to close the filters and reuse them for another attempt,
|
||||
* however that complicates filter code and it is simpler to tear them
|
||||
* all down and start over. */
|
||||
retry:
|
||||
result = cf_setup_connect_steps(cf, data, done);
|
||||
|
||||
if(result == CURLE_AGAIN) {
|
||||
++ctx->retry_count;
|
||||
if(ctx->retry_count > 5) /* arbitrary limit, better just timeout? */
|
||||
return CURLE_COULDNT_CONNECT;
|
||||
|
||||
CURL_TRC_CF(data, cf, "retrying connect, %d. time", ctx->retry_count);
|
||||
Curl_conn_cf_discard_chain(&cf->next, data);
|
||||
ctx->state = CF_SETUP_INIT;
|
||||
goto retry;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static void cf_setup_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
|
||||
{
|
||||
struct cf_setup_ctx *ctx = cf->ctx;
|
||||
|
||||
CURL_TRC_CF(data, cf, "destroy");
|
||||
curlx_safefree(ctx);
|
||||
}
|
||||
|
||||
struct Curl_cftype Curl_cft_setup = {
|
||||
"SETUP",
|
||||
CF_TYPE_SETUP,
|
||||
CURL_LOG_LVL_NONE,
|
||||
cf_setup_destroy,
|
||||
cf_setup_connect,
|
||||
Curl_cf_def_shutdown,
|
||||
Curl_cf_def_adjust_pollset,
|
||||
Curl_cf_def_data_pending,
|
||||
Curl_cf_def_send,
|
||||
Curl_cf_def_recv,
|
||||
Curl_cf_def_cntrl,
|
||||
Curl_cf_def_conn_is_alive,
|
||||
Curl_cf_def_conn_keep_alive,
|
||||
Curl_cf_def_query,
|
||||
};
|
||||
|
||||
static CURLcode cf_setup_create(struct Curl_cfilter **pcf,
|
||||
struct Curl_easy *data,
|
||||
uint8_t transport,
|
||||
int ssl_mode)
|
||||
{
|
||||
struct Curl_cfilter *cf = NULL;
|
||||
struct cf_setup_ctx *ctx;
|
||||
CURLcode result = CURLE_OK;
|
||||
|
||||
(void)data;
|
||||
ctx = curlx_calloc(1, sizeof(*ctx));
|
||||
if(!ctx) {
|
||||
result = CURLE_OUT_OF_MEMORY;
|
||||
goto out;
|
||||
}
|
||||
ctx->state = CF_SETUP_INIT;
|
||||
ctx->ssl_mode = ssl_mode;
|
||||
ctx->transport = transport;
|
||||
|
||||
result = Curl_cf_create(&cf, &Curl_cft_setup, ctx);
|
||||
if(result)
|
||||
goto out;
|
||||
ctx = NULL;
|
||||
|
||||
out:
|
||||
*pcf = result ? NULL : cf;
|
||||
if(ctx) {
|
||||
curlx_free(ctx);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static CURLcode cf_setup_add(struct Curl_easy *data,
|
||||
struct connectdata *conn,
|
||||
int sockindex,
|
||||
uint8_t transport,
|
||||
int ssl_mode)
|
||||
{
|
||||
struct Curl_cfilter *cf;
|
||||
CURLcode result = CURLE_OK;
|
||||
|
||||
DEBUGASSERT(data);
|
||||
result = cf_setup_create(&cf, data, transport, ssl_mode);
|
||||
if(result)
|
||||
goto out;
|
||||
Curl_conn_cf_add(data, conn, sockindex, cf);
|
||||
out:
|
||||
return result;
|
||||
}
|
||||
|
||||
CURLcode Curl_cf_setup_insert_after(struct Curl_cfilter *cf_at,
|
||||
struct Curl_easy *data,
|
||||
uint8_t transport,
|
||||
int ssl_mode)
|
||||
{
|
||||
struct Curl_cfilter *cf;
|
||||
CURLcode result;
|
||||
|
||||
DEBUGASSERT(data);
|
||||
result = cf_setup_create(&cf, data, transport, ssl_mode);
|
||||
if(result)
|
||||
goto out;
|
||||
Curl_conn_cf_insert_after(cf_at, cf);
|
||||
out:
|
||||
return result;
|
||||
}
|
||||
|
||||
CURLcode Curl_conn_setup(struct Curl_easy *data,
|
||||
struct connectdata *conn,
|
||||
int sockindex,
|
||||
|
|
@ -736,8 +269,8 @@ CURLcode Curl_conn_setup(struct Curl_easy *data,
|
|||
|
||||
/* Still no cfilter set, apply default. */
|
||||
if(!conn->cfilter[sockindex]) {
|
||||
result = cf_setup_add(data, conn, sockindex,
|
||||
conn->transport_wanted, ssl_mode);
|
||||
result = Curl_cf_setup_add(data, conn, sockindex,
|
||||
conn->transport_wanted, ssl_mode);
|
||||
if(result)
|
||||
goto out;
|
||||
}
|
||||
|
|
@ -754,6 +287,173 @@ out:
|
|||
return result;
|
||||
}
|
||||
|
||||
#ifdef CURLVERBOSE
|
||||
static CURLcode conn_connect_trace(struct Curl_easy *data,
|
||||
struct Curl_cfilter *cf)
|
||||
{
|
||||
if(Curl_trc_is_verbose(data)) {
|
||||
struct ip_quadruple ipquad;
|
||||
bool is_ipv6;
|
||||
CURLcode result;
|
||||
|
||||
result = Curl_conn_cf_get_ip_info(cf, data, &is_ipv6, &ipquad);
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
infof(data, "Established %sconnection to %s (%s port %u) from %s port %u ",
|
||||
(cf->sockindex == SECONDARYSOCKET) ? "2nd " : "",
|
||||
CURL_CONN_HOST_DISPNAME(data->conn),
|
||||
ipquad.remote_ip, ipquad.remote_port,
|
||||
ipquad.local_ip, ipquad.local_port);
|
||||
}
|
||||
return CURLE_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Update connection statistics
|
||||
*/
|
||||
static void conn_report_connect_stats(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data)
|
||||
{
|
||||
if(cf) {
|
||||
struct curltime connected;
|
||||
struct curltime appconnected;
|
||||
|
||||
memset(&connected, 0, sizeof(connected));
|
||||
cf->cft->query(cf, data, CF_QUERY_TIMER_CONNECT, NULL, &connected);
|
||||
if(connected.tv_sec || connected.tv_usec)
|
||||
Curl_pgrsTimeWas(data, TIMER_CONNECT, connected);
|
||||
|
||||
memset(&appconnected, 0, sizeof(appconnected));
|
||||
cf->cft->query(cf, data, CF_QUERY_TIMER_APPCONNECT, NULL, &appconnected);
|
||||
if(appconnected.tv_sec || appconnected.tv_usec)
|
||||
Curl_pgrsTimeWas(data, TIMER_APPCONNECT, appconnected);
|
||||
}
|
||||
}
|
||||
|
||||
CURLcode Curl_conn_connect(struct Curl_easy *data,
|
||||
int sockindex,
|
||||
bool blocking,
|
||||
bool *done)
|
||||
{
|
||||
#define CF_CONN_NUM_POLLS_ON_STACK 5
|
||||
struct pollfd a_few_on_stack[CF_CONN_NUM_POLLS_ON_STACK];
|
||||
struct easy_pollset ps;
|
||||
struct curl_pollfds cpfds;
|
||||
struct Curl_cfilter *cf;
|
||||
CURLcode result = CURLE_OK;
|
||||
|
||||
DEBUGASSERT(data);
|
||||
DEBUGASSERT(data->conn);
|
||||
if(!CONN_SOCK_IDX_VALID(sockindex))
|
||||
return CURLE_BAD_FUNCTION_ARGUMENT;
|
||||
|
||||
if(data->conn->scheme->flags & PROTOPT_NONETWORK) {
|
||||
*done = TRUE;
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
cf = data->conn->cfilter[sockindex];
|
||||
if(!cf) {
|
||||
*done = FALSE;
|
||||
return CURLE_FAILED_INIT;
|
||||
}
|
||||
|
||||
*done = (bool)cf->connected;
|
||||
if(*done)
|
||||
return CURLE_OK;
|
||||
|
||||
Curl_pollset_init(&ps);
|
||||
Curl_pollfds_init(&cpfds, a_few_on_stack, CF_CONN_NUM_POLLS_ON_STACK);
|
||||
while(!*done) {
|
||||
if(Curl_conn_needs_flush(data, sockindex)) {
|
||||
DEBUGF(infof(data, "Curl_conn_connect(index=%d), flush", sockindex));
|
||||
result = Curl_conn_flush(data, sockindex);
|
||||
if(result && (result != CURLE_AGAIN))
|
||||
goto out;
|
||||
}
|
||||
|
||||
result = cf->cft->do_connect(cf, data, done);
|
||||
CURL_TRC_CF(data, cf, "Curl_conn_connect(block=%d) -> %d, done=%d",
|
||||
blocking, (int)result, *done);
|
||||
if(!result && *done) {
|
||||
/* A final sanity check on connection security */
|
||||
if((data->state.origin->scheme->flags & PROTOPT_SSL) &&
|
||||
(sockindex == FIRSTSOCKET) &&
|
||||
!Curl_conn_is_ssl(data->conn, FIRSTSOCKET)) {
|
||||
DEBUGASSERT(0);
|
||||
failf(data, "transfer requires SSL, but not connected via SSL");
|
||||
result = CURLE_FAILED_INIT;
|
||||
goto out;
|
||||
}
|
||||
/* Now that the complete filter chain is connected, let all filters
|
||||
* persist information at the connection. E.g. cf-socket sets the
|
||||
* socket and ip related information. */
|
||||
Curl_conn_cntrl_update_info(data, data->conn);
|
||||
conn_report_connect_stats(cf, data);
|
||||
data->conn->keepalive = *Curl_pgrs_now(data);
|
||||
VERBOSE(result = conn_connect_trace(data, cf));
|
||||
VERBOSE(Curl_conn_trc_filters(data, sockindex, "connected"));
|
||||
Curl_conn_remove_setup_filters(data, sockindex);
|
||||
VERBOSE(Curl_conn_trc_filters(data, sockindex, "reduced to"));
|
||||
goto out;
|
||||
}
|
||||
else if(result) {
|
||||
CURL_TRC_CF(data, cf, "Curl_conn_connect(), filter returned %d",
|
||||
(int)result);
|
||||
VERBOSE(Curl_conn_trc_filters(data, sockindex, "failed to connect"));
|
||||
conn_report_connect_stats(cf, data);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if(!blocking)
|
||||
goto out;
|
||||
else {
|
||||
/* check allowed time left */
|
||||
const timediff_t timeout_ms = Curl_timeleft_ms(data);
|
||||
curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data);
|
||||
int rc;
|
||||
|
||||
if(timeout_ms < 0) {
|
||||
/* no need to continue if time already is up */
|
||||
failf(data, "connect timeout");
|
||||
result = CURLE_OPERATION_TIMEDOUT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
CURL_TRC_CF(data, cf, "Curl_conn_connect(block=1), do poll");
|
||||
Curl_pollset_reset(&ps);
|
||||
Curl_pollfds_reset(&cpfds);
|
||||
/* In general, we want to send after connect, wait on that. */
|
||||
if(sockfd != CURL_SOCKET_BAD)
|
||||
result = Curl_pollset_set_out_only(data, &ps, sockfd);
|
||||
if(!result)
|
||||
result = Curl_conn_adjust_pollset(data, data->conn, &ps);
|
||||
if(result)
|
||||
goto out;
|
||||
result = Curl_pollfds_add_ps(&cpfds, &ps);
|
||||
if(result)
|
||||
goto out;
|
||||
|
||||
rc = Curl_poll(cpfds.pfds, cpfds.n,
|
||||
CURLMIN(timeout_ms, (cpfds.n ? 1000 : 10)));
|
||||
CURL_TRC_CF(data, cf, "Curl_conn_connect(block=1), Curl_poll() -> %d",
|
||||
rc);
|
||||
if(rc < 0) {
|
||||
result = CURLE_COULDNT_CONNECT;
|
||||
goto out;
|
||||
}
|
||||
/* continue iterating */
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
Curl_pollset_cleanup(&ps);
|
||||
Curl_pollfds_cleanup(&cpfds);
|
||||
return result;
|
||||
}
|
||||
|
||||
void Curl_conn_set_multiplex(struct connectdata *conn)
|
||||
{
|
||||
if(!conn->bits.multiplex) {
|
||||
|
|
|
|||
|
|
@ -28,8 +28,6 @@
|
|||
#include "hostip.h"
|
||||
#include "curlx/timeval.h"
|
||||
|
||||
struct Curl_dns_entry;
|
||||
struct ip_quadruple;
|
||||
struct Curl_peer;
|
||||
struct Curl_str;
|
||||
|
||||
|
|
@ -105,11 +103,6 @@ void Curl_conncontrol(struct connectdata *conn,
|
|||
#define connkeep(x, y) Curl_conncontrol(x, CONNCTRL_KEEP)
|
||||
#endif
|
||||
|
||||
CURLcode Curl_cf_setup_insert_after(struct Curl_cfilter *cf_at,
|
||||
struct Curl_easy *data,
|
||||
uint8_t transport,
|
||||
int ssl_mode);
|
||||
|
||||
/**
|
||||
* Setup the cfilters at `sockindex` in connection `conn`.
|
||||
* If no filter chain is installed yet, inspects the configuration
|
||||
|
|
@ -120,6 +113,16 @@ CURLcode Curl_conn_setup(struct Curl_easy *data,
|
|||
int sockindex,
|
||||
int ssl_mode);
|
||||
|
||||
/**
|
||||
* Bring the filter chain at `sockindex` for connection `data->conn` into
|
||||
* connected state. Which will set `*done` to TRUE.
|
||||
* This can be called on an already connected chain with no side effects.
|
||||
* When not `blocking`, calls may return without error and `*done != TRUE`,
|
||||
* while the individual filters negotiated the connection.
|
||||
*/
|
||||
CURLcode Curl_conn_connect(struct Curl_easy *data, int sockindex,
|
||||
bool blocking, bool *done);
|
||||
|
||||
/* Set conn to allow multiplexing. */
|
||||
void Curl_conn_set_multiplex(struct connectdata *conn);
|
||||
|
||||
|
|
@ -138,6 +141,4 @@ struct Curl_peer *Curl_conn_get_destination(struct connectdata *conn,
|
|||
struct Curl_peer *Curl_conn_get_first_peer(struct connectdata *conn,
|
||||
int sockindex);
|
||||
|
||||
extern struct Curl_cftype Curl_cft_setup;
|
||||
|
||||
#endif /* HEADER_CURL_CONNECT_H */
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@
|
|||
|
||||
#include "cf-dns.h"
|
||||
#include "cf-socket.h"
|
||||
#include "connect.h"
|
||||
#include "cf-setup.h"
|
||||
#include "http2.h"
|
||||
#include "http_proxy.h"
|
||||
#include "cf-h1-proxy.h"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue