lib: transfer origin and proxy handling

Add `data->state.origin` as the origin the transfer is sending the
current request to/gets the response from. Use it for request specific
properties like authentication, hsts and cookie handling, etc.

Unless talking to a forwarding HTTP proxy (e.g. not tunneling),
`data->state.origin` and `conn->origin` are the same.

With a forwarding HTTP proxy in play, `conn->origin` is set to
`conn->http_proxy.peer` and `conn->bits.origin_is_proxy` (a new bit) is
set.

Remove the connection bits, now replaced with:

* `conn->bits.socksproxy` -> `conn->socks_proy.peer`
* `conn->bits.httpproxy` -> `conn->http_proy.peer`
* `conn->bits.proxy` -> `(conn->socks_proy.peer || conn->http_proy.peer`)
* `conn->bits.tunnel_proxy` -> (`conn->http_proy.peer && !conn->bits.origin_is_proxy`)
* `(conn->bits.httpproxy && !conn->bits.tunnel_proxy)` -> `conn->bits.origin_is_proxy`

Rename `noproxy.[ch]` to `proxy.[ch]`. Move the connection proxy setup
code from `url.c` to `proxy.c`.

Remove `data->info.conn_remote_port` as no one uses it.

Add test_40_02b for a SOCKS connection to a forwarding HTTPS proxy.

Update internal documentation about peers and creds.

Closes #21967
This commit is contained in:
Stefan Eissing 2026-06-12 12:02:08 +02:00 committed by Daniel Stenberg
parent c951368579
commit 73daec6620
No known key found for this signature in database
GPG key ID: 5CC908FDB71E12C2
30 changed files with 1083 additions and 1014 deletions

View file

@ -38,9 +38,10 @@ suitable connection. For an `easy_perform()` this may happen several times
if, for example, http redirects are followed.
When an `easy_perform()` starts, the transfer's `data->state.initial_origin`
peer is cleared. When creating the connection, `conn->origin` is calculated
(e.g. who the request talks to). If `data->state.initial_origin` is not
set, the first `conn->origin` is linked there. Now `libcurl` knows where
peer is cleared. When creating the connection, `data->state.origin` is
calculated (e.g. who the request talks to). If `data->state.initial_origin`
is not set, the first `data->state.origin` is linked there.
Now `libcurl` knows where
the transfer initially talked to on all possible subsequent requests.
Credential information from `CURLOPT_*` settings is only applicable for the

View file

@ -20,13 +20,25 @@ A `peer` in curl internals is represented by a `struct Curl_peer`. It has the fo
A peer, in short, is a communication endpoint.
## peers and transfers
The peer a transfer, e.g. easy handle, works against is determined at the
start of each request. It is kept in `data->state.origin`. For the first
request done in a `curl_easy_perform()` or equivalent, this origin is
linked to `data->state.initial_origin`. This allows checks if properties
of `data->set.*` should apply to a request or not.
`data->state.origin` is relevant for cookie processing, signing requests
and other request/response based processing.
## peers and connections
A network connection always goes *somewhere*. That *somewhere* is called
the `origin` of the connection (e.g. the source of responses/downloads).
It is kept in `conn->origin` and is always present in a connection.
The `origin` is *logical* endpoint a connection talks to.
The `origin` is *logical* endpoint a connection talks to. In most
configurations it is the same as `data->state.origin` (see proxies below).
For most connections, the `origin` is connected to *directly*. It
can be directed to another peer, however.
@ -56,6 +68,19 @@ might connect as:
5. curl --> socks_proxy.peer --> http_proxy.peer --> conn->via_peer/origin
```
A `conn->(socks|http)_proxy.peer` is only ever present when the proxy
is in use and `NULL` otherwise.
SOCKS proxies are always used for tunneling, either to the origin or
the HTTP proxy. They operate in a connection filter.
HTTP proxies can operate in two modes: tunneling or forwarding. When tunneling,
they also operate in a connection filter. In forwarding mode however, they
become the `origin` the connection talks to.
Therefore, connections that talk to a forwarding HTTP proxy have `conn->origin`
set to `conn->http_proxy.peer` and `conn->bits.origin_is_proxy` is set.
The connection filter `SETUP`, that assembles the filters for a connection,
figures out which peer to pass to which filter in order to make it all work.
The individual filters get passed a specific peer and do not need be concerned

View file

@ -241,7 +241,6 @@ LIB_CFILES = \
multi_ev.c \
multi_ntfy.c \
netrc.c \
noproxy.c \
openldap.c \
parsedate.c \
peer.c \
@ -249,6 +248,7 @@ LIB_CFILES = \
pop3.c \
progress.c \
protocol.c \
proxy.c \
psl.c \
rand.c \
ratelimit.c \
@ -375,13 +375,13 @@ LIB_HFILES = \
multi_ntfy.h \
multiif.h \
netrc.h \
noproxy.h \
parsedate.h \
peer.h \
pingpong.h \
pop3.h \
progress.h \
protocol.h \
proxy.h \
psl.h \
rand.h \
ratelimit.h \

View file

@ -713,14 +713,15 @@ static CURLcode is_connected(struct Curl_cfilter *cf,
return CURLE_FAILED_INIT;
#ifndef CURL_DISABLE_PROXY
if(conn->bits.socksproxy)
if(conn->socks_proxy.peer)
proxy_peer = conn->socks_proxy.peer;
else if(conn->bits.httpproxy)
else if(conn->http_proxy.peer)
proxy_peer = conn->http_proxy.peer;
#endif
viamsg[0] = 0;
if((peer != conn->origin) && (peer != proxy_peer)) {
if(!Curl_peer_equal(peer, conn->origin) &&
!Curl_peer_equal(peer, proxy_peer)) {
#ifdef USE_UNIX_SOCKETS
if(peer->unix_socket)
curl_msnprintf(viamsg, sizeof(viamsg), " over unix://%s",

View file

@ -1634,8 +1634,6 @@ static void cf_socket_update_data(struct Curl_cfilter *cf,
if(cf->connected && (cf->sockindex == FIRSTSOCKET)) {
struct cf_socket_ctx *ctx = cf->ctx;
data->info.primary = ctx->ip;
/* not sure if this is redundant... */
data->info.conn_remote_port = cf->conn->origin->port;
}
}

View file

@ -660,6 +660,31 @@ bool Curl_conn_is_ip_connected(struct Curl_easy *data, int sockindex)
return FALSE;
}
#ifndef CURL_DISABLE_PROXY
static bool cf_is_tunneling(struct Curl_cfilter *cf)
{
for(; cf; cf = cf->next) {
if((cf->cft->flags & CF_TYPE_PROXY))
return TRUE;
}
return FALSE;
}
bool Curl_conn_is_tunneling(struct connectdata *conn, int sockindex)
{
if(!CONN_SOCK_IDX_VALID(sockindex))
return FALSE;
return conn ? cf_is_tunneling(conn->cfilter[sockindex]) : FALSE;
}
#else
bool Curl_conn_is_tunneling(struct connectdata *conn, int sockindex)
{
(void)conn;
(void)sockindex;
return FALSE;
}
#endif /* CURL_DISABLE_PROXY */
static bool cf_is_ssl(struct Curl_cfilter *cf)
{
for(; cf; cf = cf->next) {

View file

@ -393,6 +393,10 @@ bool Curl_conn_is_ip_connected(struct Curl_easy *data, int sockindex);
*/
bool Curl_conn_is_ssl(struct connectdata *conn, int sockindex);
/* Determine if the connection has one or more proxy filters.
* e.g. is tunneling. */
bool Curl_conn_is_tunneling(struct connectdata *conn, int sockindex);
/*
* Fill `info` with information about the TLS instance securing the connection
* when available, otherwise e.g. when Curl_conn_is_ssl() is FALSE, return

View file

@ -319,11 +319,11 @@ static CURLcode cf_setup_add_socks(struct Curl_cfilter *cf,
{
struct cf_setup_ctx *ctx = cf->ctx;
CURLcode result = CURLE_OK;
if(ctx->state < CF_SETUP_CNNCT_SOCKS && cf->conn->bits.socksproxy) {
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->bits.httpproxy)
if(cf->conn->http_proxy.peer)
second_peer = cf->conn->http_proxy.peer;
else
second_peer = Curl_conn_get_destination(cf->conn, cf->sockindex);
@ -353,9 +353,14 @@ static CURLcode cf_setup_add_http_proxy(struct Curl_cfilter *cf,
struct cf_setup_ctx *ctx = cf->ctx;
CURLcode result = CURLE_OK;
if(ctx->state < CF_SETUP_CNNCT_HTTP_PROXY && cf->conn->bits.httpproxy) {
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(IS_HTTPS_PROXY(cf->conn->http_proxy.proxytype) &&
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);
@ -368,20 +373,15 @@ static CURLcode cf_setup_add_http_proxy(struct Curl_cfilter *cf,
}
#endif /* USE_SSL */
if(cf->conn->bits.tunnel_proxy) {
struct Curl_peer *peer = cf->conn->http_proxy.peer;
struct Curl_peer *tunnel_peer; /* where HTTP should tunnel to */
tunnel_peer = Curl_conn_get_destination(cf->conn, cf->sockindex);
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");
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;
@ -424,11 +424,11 @@ static CURLcode cf_setup_add_ip_happy(struct Curl_cfilter *cf,
return CURLE_FAILED_INIT;
#if !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP)
if(cf->conn->bits.httpproxy && cf->conn->bits.tunnel_proxy) {
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->bits.socksproxy)) {
if((first_transport == TRNSPRT_QUIC) && cf->conn->socks_proxy.peer) {
failf(data, "HTTP/3 proxy not possible via SOCKS");
return CURLE_UNSUPPORTED_PROTOCOL;
}
@ -472,8 +472,8 @@ static CURLcode cf_setup_add_origin_filters(struct Curl_cfilter *cf,
/* Wanting QUIC with a 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->bits.httpproxy &&
cf->conn->bits.tunnel_proxy) {
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);
@ -498,13 +498,21 @@ static CURLcode cf_setup_add_origin_filters(struct Curl_cfilter *cf,
(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 */
/* 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);
#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);
@ -766,10 +774,6 @@ struct Curl_peer *Curl_conn_get_origin(struct connectdata *conn,
struct Curl_peer *Curl_conn_get_destination(struct connectdata *conn,
int sockindex)
{
#ifndef CURL_DISABLE_PROXY
if(conn->http_proxy.peer && !conn->bits.tunnel_proxy)
return conn->http_proxy.peer;
#endif
return (sockindex == SECONDARYSOCKET) ?
(conn->via_peer2 ? conn->via_peer2 : conn->origin2) :
(conn->via_peer ? conn->via_peer : conn->origin);

View file

@ -1983,11 +1983,7 @@ static CURLcode ftp_epsv_disable(struct Curl_easy *data,
{
CURLcode result = CURLE_OK;
if(conn->bits.ipv6
#ifndef CURL_DISABLE_PROXY
&& !(conn->bits.tunnel_proxy || conn->bits.socksproxy)
#endif
) {
if(conn->bits.ipv6 && !Curl_conn_is_tunneling(conn, FIRSTSOCKET)) {
/* We cannot disable EPSV when doing IPv6, so this is instead a fail */
failf(data, "Failed EPSV attempt, exiting");
return CURLE_WEIRD_SERVER_REPLY;
@ -2019,7 +2015,7 @@ static CURLcode ftp_control_addr_dup(struct Curl_easy *data, char **newhostp)
the effective control connection address is the proxy address,
not the ftp host. */
#ifndef CURL_DISABLE_PROXY
if(conn->bits.tunnel_proxy || conn->bits.socksproxy)
if(Curl_conn_is_tunneling(conn, FIRSTSOCKET))
*newhostp = curlx_strdup(conn->origin->hostname);
else
#endif

View file

@ -154,7 +154,7 @@ char *Curl_checkProxyheaders(struct Curl_easy *data,
{
struct curl_slist *head;
for(head = (conn->bits.proxy && data->set.sep_headers) ?
for(head = (conn->http_proxy.peer && data->set.sep_headers) ?
data->set.proxyheaders : data->set.headers;
head; head = head->next) {
if(curl_strnequal(head->data, thisheader, thislen) &&
@ -783,7 +783,7 @@ CURLcode Curl_http_output_auth(struct Curl_easy *data,
if(
#ifndef CURL_DISABLE_PROXY
(!conn->bits.httpproxy || !conn->http_proxy.creds) &&
(!conn->http_proxy.peer || !conn->http_proxy.creds) &&
#endif
#ifdef USE_SPNEGO
!(authhost->want & CURLAUTH_NEGOTIATE) &&
@ -820,7 +820,7 @@ CURLcode Curl_http_output_auth(struct Curl_easy *data,
#ifndef CURL_DISABLE_PROXY
/* Send proxy authentication header if needed */
if(conn->bits.httpproxy && (!conn->bits.tunnel_proxy || is_connect)) {
if(conn->bits.origin_is_proxy || is_connect) {
result = output_auth_headers(data, conn, authproxy, request,
path_and_query, TRUE);
if(result)
@ -1736,8 +1736,7 @@ CURLcode Curl_add_custom_headers(struct Curl_easy *data,
if(is_connect)
proxy = HEADER_CONNECT;
else
proxy = data->conn->bits.httpproxy && !data->conn->bits.tunnel_proxy ?
HEADER_PROXY : HEADER_SERVER;
proxy = data->conn->bits.origin_is_proxy ? HEADER_PROXY : HEADER_SERVER;
switch(proxy) {
case HEADER_SERVER:
@ -1998,8 +1997,9 @@ static CURLcode http_set_aptr_host(struct Curl_easy *data)
#endif
ptr = Curl_checkheaders(data, STRCONST("Host"));
if(ptr && (!data->state.this_is_a_follow ||
Curl_peer_equal(data->state.initial_origin, conn->origin))) {
if(ptr &&
(!data->state.this_is_a_follow ||
Curl_peer_equal(data->state.initial_origin, data->state.origin))) {
#ifndef CURL_DISABLE_COOKIES
/* If we have a given custom Host: header, we extract the hostname in
order to possibly use it for cookie reasons later on. We only allow the
@ -2043,18 +2043,19 @@ static CURLcode http_set_aptr_host(struct Curl_easy *data)
}
else {
/* Use the hostname as present in the URL if it was IPv6. */
char *host = (conn->origin->user_hostname[0] == '[') ?
conn->origin->user_hostname : conn->origin->hostname;
char *host = (data->state.origin->user_hostname[0] == '[') ?
data->state.origin->user_hostname : data->state.origin->hostname;
if(((conn->given->protocol & (CURLPROTO_HTTPS | CURLPROTO_WSS)) &&
(conn->origin->port == PORT_HTTPS)) ||
(data->state.origin->port == PORT_HTTPS)) ||
((conn->given->protocol & (CURLPROTO_HTTP | CURLPROTO_WS)) &&
(conn->origin->port == PORT_HTTP)))
(data->state.origin->port == PORT_HTTP)))
/* if(HTTPS on port 443) OR (HTTP on port 80) then do not include
the port number in the host string */
aptr->host = curl_maprintf("Host: %s\r\n", host);
else
aptr->host = curl_maprintf("Host: %s:%d\r\n", host, conn->origin->port);
aptr->host = curl_maprintf("Host: %s:%d\r\n",
host, data->state.origin->port);
if(!aptr->host)
/* without Host: we cannot make a nice request */
@ -2082,7 +2083,7 @@ static CURLcode http_target(struct Curl_easy *data,
}
#ifndef CURL_DISABLE_PROXY
if(conn->bits.httpproxy && !conn->bits.tunnel_proxy) {
if(conn->bits.origin_is_proxy) {
/* Using a proxy but does not tunnel through it */
/* The path sent to the proxy is in fact the entire URL, but if the remote
@ -2096,8 +2097,8 @@ static CURLcode http_target(struct Curl_easy *data,
if(!h)
return CURLE_OUT_OF_MEMORY;
if(conn->origin->user_hostname != conn->origin->hostname) {
uc = curl_url_set(h, CURLUPART_HOST, conn->origin->hostname, 0);
if(data->state.origin->user_hostname != data->state.origin->hostname) {
uc = curl_url_set(h, CURLUPART_HOST, data->state.origin->hostname, 0);
if(uc) {
curl_url_cleanup(h);
return CURLE_OUT_OF_MEMORY;
@ -2541,7 +2542,7 @@ static CURLcode http_cookies(struct Curl_easy *data,
if(data->cookies && data->state.cookie_engine) {
bool okay;
const char *host = data->req.cookiehost ?
data->req.cookiehost : data->conn->origin->hostname;
data->req.cookiehost : data->state.origin->hostname;
Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
result = Curl_cookie_getlist(data, data->conn, &okay, host, &list);
if(!result && okay) {
@ -2728,8 +2729,7 @@ static CURLcode http_check_new_conn(struct Curl_easy *data)
alpn = Curl_conn_get_alpn_negotiated(data, conn);
if(alpn && !strcmp("h3", alpn)) {
#ifndef CURL_DISABLE_PROXY
if((Curl_conn_http_version(data, conn) == 30) || !conn->bits.proxy ||
conn->bits.tunnel_proxy)
if(!conn->bits.origin_is_proxy)
#endif
DEBUGASSERT(Curl_conn_http_version(data, conn) == 30);
info_version = "HTTP/3";
@ -2737,7 +2737,7 @@ static CURLcode http_check_new_conn(struct Curl_easy *data)
else if(alpn && !strcmp("h2", alpn)) {
#ifndef CURL_DISABLE_PROXY
if((Curl_conn_http_version(data, conn) != 20) &&
conn->bits.proxy && !conn->bits.tunnel_proxy) {
conn->bits.origin_is_proxy) {
result = Curl_http2_switch(data);
if(result)
return result;
@ -2946,8 +2946,7 @@ static CURLcode http_add_hd(struct Curl_easy *data,
#ifndef CURL_DISABLE_PROXY
case H1_HD_PROXY_CONNECTION:
if(conn->bits.httpproxy &&
!conn->bits.tunnel_proxy &&
if(conn->bits.origin_is_proxy &&
!Curl_checkheaders(data, STRCONST("Proxy-Connection")) &&
!Curl_checkProxyheaders(data, data->conn, STRCONST("Proxy-Connection")))
result = curlx_dyn_add(req, "Proxy-Connection: Keep-Alive\r\n");
@ -3190,7 +3189,6 @@ static CURLcode http_header_a(struct Curl_easy *data,
{
#ifndef CURL_DISABLE_ALTSVC
const char *v;
struct connectdata *conn = data->conn;
v = (data->asi &&
(Curl_xfer_is_secure(data) ||
#ifdef DEBUGBUILD
@ -3205,8 +3203,9 @@ static CURLcode http_header_a(struct Curl_easy *data,
struct SingleRequest *k = &data->req;
enum alpnid id = (k->httpversion == 30) ? ALPN_h3 :
(k->httpversion == 20) ? ALPN_h2 : ALPN_h1;
return Curl_altsvc_parse(data, data->asi, v, id, conn->origin->hostname,
curlx_uitous((unsigned int)conn->origin->port));
return Curl_altsvc_parse(
data, data->asi, v, id, data->state.origin->hostname,
curlx_uitous((unsigned int)data->state.origin->port));
}
#else
(void)data;
@ -3424,7 +3423,7 @@ static CURLcode http_header_p(struct Curl_easy *data,
const char *v = HD_VAL(hd, hdlen, "Proxy-Connection:");
if(v) {
struct connectdata *conn = data->conn;
if((k->httpversion == 10) && conn->bits.httpproxy &&
if((k->httpversion == 10) && conn->http_proxy.peer &&
HD_IS_AND_SAYS(hd, hdlen, "Proxy-Connection:", "keep-alive")) {
/*
* When an HTTP/1.0 reply comes when using a proxy, the
@ -3435,7 +3434,7 @@ static CURLcode http_header_p(struct Curl_easy *data,
connkeep(conn, "Proxy-Connection keep-alive"); /* do not close */
infof(data, "HTTP/1.0 proxy connection set to keep alive");
}
else if((k->httpversion == 11) && conn->bits.httpproxy &&
else if((k->httpversion == 11) && conn->http_proxy.peer &&
HD_IS_AND_SAYS(hd, hdlen, "Proxy-Connection:", "close")) {
/*
* We get an HTTP/1.1 response from a proxy and it says it will
@ -3533,7 +3532,7 @@ static CURLcode http_header_s(struct Curl_easy *data,
/* If there is a custom-set Host: name, use it here, or else use
* real peer hostname. */
const char *host = data->req.cookiehost ?
data->req.cookiehost : conn->origin->hostname;
data->req.cookiehost : data->state.origin->hostname;
const bool secure_context = Curl_secure_context(conn, host);
CURLcode result;
Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
@ -3556,8 +3555,8 @@ static CURLcode http_header_s(struct Curl_easy *data,
)
) ? HD_VAL(hd, hdlen, "Strict-Transport-Security:") : NULL;
if(v) {
CURLcode result =
Curl_hsts_parse(data->hsts, conn->origin->hostname, v);
CURLcode result = Curl_hsts_parse(
data->hsts, data->state.origin->hostname, v);
if(result) {
if(result == CURLE_OUT_OF_MEMORY)
return result;

View file

@ -1403,7 +1403,7 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
struct Curl_cfilter *cf = userp;
struct cf_h2_ctx *ctx = cf->ctx;
struct h2_stream_ctx *stream;
struct Curl_easy *data_s;
struct Curl_easy *data;
int32_t stream_id = frame->hd.stream_id;
CURLcode result;
(void)flags;
@ -1411,15 +1411,15 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
DEBUGASSERT(stream_id); /* should never be a zero stream ID here */
/* get the stream from the hash based on Stream ID */
data_s = nghttp2_session_get_stream_user_data(session, stream_id);
if(!GOOD_EASY_HANDLE(data_s))
data = nghttp2_session_get_stream_user_data(session, stream_id);
if(!GOOD_EASY_HANDLE(data))
/* Receiving a Stream ID not in the hash should not happen, this is an
internal error more than anything else! */
return NGHTTP2_ERR_CALLBACK_FAILURE;
stream = H2_STREAM_CTX(ctx, data_s);
stream = H2_STREAM_CTX(ctx, data);
if(!stream) {
failf(data_s, "Internal NULL stream");
failf(data, "Internal NULL stream");
return NGHTTP2_ERR_CALLBACK_FAILURE;
}
@ -1432,14 +1432,14 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
!strncmp(HTTP_PSEUDO_AUTHORITY, (const char *)name, namelen)) {
/* pseudo headers are lower case */
int rc = 0;
char *check = curl_maprintf("%s:%d", cf->conn->origin->hostname,
cf->conn->origin->port);
char *check = curl_maprintf("%s:%d", data->state.origin->hostname,
data->state.origin->port);
if(!check)
/* no memory */
return NGHTTP2_ERR_CALLBACK_FAILURE;
if(!curl_strequal(check, (const char *)value) &&
((cf->conn->origin->port != cf->conn->given->defport) ||
!curl_strequal(cf->conn->origin->hostname, (const char *)value))) {
((data->state.origin->port != cf->conn->given->defport) ||
!curl_strequal(data->state.origin->hostname, (const char *)value))) {
/* This is push is not for the same authority that was asked for in
* the URL. RFC 7540 section 8.2 says: "A client MUST treat a
* PUSH_PROMISE for which the server is not authoritative as a stream
@ -1467,7 +1467,7 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
char **headp;
if(stream->push_headers_alloc > 1000) {
/* this is beyond crazy many headers, bail out */
failf(data_s, "Too many PUSH_PROMISE headers");
failf(data, "Too many PUSH_PROMISE headers");
free_push_headers(stream);
return NGHTTP2_ERR_CALLBACK_FAILURE;
}
@ -1491,13 +1491,13 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
if(stream->bodystarted) {
/* This is a trailer */
CURL_TRC_CF(data_s, cf, "[%d] trailer: %.*s: %.*s",
CURL_TRC_CF(data, cf, "[%d] trailer: %.*s: %.*s",
stream->id, (int)namelen, name, (int)valuelen, value);
result = Curl_dynhds_add(&stream->resp_trailers,
(const char *)name, namelen,
(const char *)value, valuelen);
if(result) {
cf_h2_header_error(cf, data_s, stream, result);
cf_h2_header_error(cf, data, stream, result);
return NGHTTP2_ERR_CALLBACK_FAILURE;
}
@ -1512,14 +1512,14 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
result = Curl_http_decode_status(&stream->status_code,
(const char *)value, valuelen);
if(result) {
cf_h2_header_error(cf, data_s, stream, result);
cf_h2_header_error(cf, data, stream, result);
return NGHTTP2_ERR_CALLBACK_FAILURE;
}
hlen = curl_msnprintf(buffer, sizeof(buffer), HTTP_PSEUDO_STATUS ":%d\r",
stream->status_code);
result = Curl_headers_push(data_s, buffer, hlen, CURLH_PSEUDO);
result = Curl_headers_push(data, buffer, hlen, CURLH_PSEUDO);
if(result) {
cf_h2_header_error(cf, data_s, stream, result);
cf_h2_header_error(cf, data, stream, result);
return NGHTTP2_ERR_CALLBACK_FAILURE;
}
curlx_dyn_reset(&ctx->scratch);
@ -1529,17 +1529,17 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
if(!result)
result = curlx_dyn_addn(&ctx->scratch, STRCONST(" \r\n"));
if(!result)
h2_xfer_write_resp_hd(cf, data_s, stream, curlx_dyn_ptr(&ctx->scratch),
h2_xfer_write_resp_hd(cf, data, stream, curlx_dyn_ptr(&ctx->scratch),
curlx_dyn_len(&ctx->scratch), FALSE);
if(result) {
cf_h2_header_error(cf, data_s, stream, result);
cf_h2_header_error(cf, data, stream, result);
return NGHTTP2_ERR_CALLBACK_FAILURE;
}
/* if we receive data for another handle, wake that up */
if(CF_DATA_CURRENT(cf) != data_s)
Curl_multi_mark_dirty(data_s);
if(CF_DATA_CURRENT(cf) != data)
Curl_multi_mark_dirty(data);
CURL_TRC_CF(data_s, cf, "[%d] status: HTTP/2 %03d",
CURL_TRC_CF(data, cf, "[%d] status: HTTP/2 %03d",
stream->id, stream->status_code);
return 0;
}
@ -1556,17 +1556,17 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
if(!result)
result = curlx_dyn_addn(&ctx->scratch, STRCONST("\r\n"));
if(!result)
h2_xfer_write_resp_hd(cf, data_s, stream, curlx_dyn_ptr(&ctx->scratch),
h2_xfer_write_resp_hd(cf, data, stream, curlx_dyn_ptr(&ctx->scratch),
curlx_dyn_len(&ctx->scratch), FALSE);
if(result) {
cf_h2_header_error(cf, data_s, stream, result);
cf_h2_header_error(cf, data, stream, result);
return NGHTTP2_ERR_CALLBACK_FAILURE;
}
/* if we receive data for another handle, wake that up */
if(CF_DATA_CURRENT(cf) != data_s)
Curl_multi_mark_dirty(data_s);
if(CF_DATA_CURRENT(cf) != data)
Curl_multi_mark_dirty(data);
CURL_TRC_CF(data_s, cf, "[%d] header: %.*s: %.*s",
CURL_TRC_CF(data, cf, "[%d] header: %.*s: %.*s",
stream->id, (int)namelen, name, (int)valuelen, value);
return 0; /* 0 is successful */
@ -2840,9 +2840,7 @@ bool Curl_http2_may_switch(struct Curl_easy *data)
(data->state.http_neg.wanted & CURL_HTTP_V2x) &&
data->state.http_neg.h2_prior_knowledge) {
#ifndef CURL_DISABLE_PROXY
if(data->conn->bits.httpproxy && !data->conn->bits.tunnel_proxy) {
/* We do not support HTTP/2 proxies yet. Also it is debatable
whether or not this setting should apply to HTTP/2 proxies. */
if(data->conn->bits.origin_is_proxy) {
infof(data, "Ignoring HTTP/2 prior knowledge due to proxy");
return FALSE;
}

View file

@ -1165,12 +1165,11 @@ fail:
CURLcode Curl_output_aws_sigv4(struct Curl_easy *data)
{
CURLcode result = CURLE_OUT_OF_MEMORY;
struct connectdata *conn = data->conn;
struct Curl_str provider0 = { NULL, 0 };
struct Curl_str provider1 = { NULL, 0 };
struct Curl_str region = { NULL, 0 };
struct Curl_str service = { NULL, 0 };
const char *hostname = conn->origin->hostname;
const char *hostname = data->state.origin->hostname;
char timestamp[TIMESTAMP_SIZE];
char date[9];
struct dynbuf canonical_headers;

View file

@ -117,9 +117,9 @@ CURLcode Curl_output_digest(struct Curl_easy *data,
#endif
}
else {
DEBUGASSERT(data->conn->origin);
DEBUGASSERT(data->state.origin);
digest = &data->state.digest;
digest_flush_stale(digest, data->conn->origin, data->state.creds);
digest_flush_stale(digest, data->state.origin, data->state.creds);
allocuserpwd = &data->req.hd_auth;
creds = data->state.creds;
authp = &data->state.authhost;

View file

@ -73,7 +73,7 @@ CURLcode Curl_input_negotiate(struct Curl_easy *data, struct connectdata *conn,
}
else {
creds = data->state.creds;
host = conn->origin->hostname;
host = data->state.origin->hostname;
state = conn->http_negotiate_state;
}

View file

@ -149,7 +149,7 @@ CURLcode Curl_output_ntlm(struct Curl_easy *data, bool proxy)
else {
allocuserpwd = &data->req.hd_auth;
creds = data->state.creds;
hostname = conn->origin->hostname;
hostname = data->state.origin->hostname;
state = &conn->http_ntlm_state;
authp = &data->state.authhost;
}

View file

@ -55,8 +55,7 @@ static CURLcode dynhds_add_custom(struct Curl_easy *data,
else if(is_connect && is_udp)
proxy = HEADER_CONNECT_UDP;
else
proxy = (conn->bits.httpproxy && !conn->bits.tunnel_proxy) ?
HEADER_PROXY : HEADER_SERVER;
proxy = conn->bits.origin_is_proxy ? HEADER_PROXY : HEADER_SERVER;
switch(proxy) {
case HEADER_SERVER:

View file

@ -75,15 +75,8 @@ CURLcode Curl_cf_http_proxy_insert_after(struct Curl_cfilter *cf_at,
extern struct Curl_cftype Curl_cft_http_proxy;
#endif /* !CURL_DISABLE_PROXY && !CURL_DISABLE_HTTP */
#define IS_HTTPS_PROXY(t) \
(((t) == CURLPROXY_HTTPS) || \
((t) == CURLPROXY_HTTPS2) || \
((t) == CURLPROXY_HTTPS3))
#define IS_QUIC_PROXY(t) ((t) == CURLPROXY_HTTPS3)
uint8_t Curl_http_proxy_transport(uint8_t proxytype);
#endif /* !CURL_DISABLE_PROXY && !CURL_DISABLE_HTTP */
#endif /* HEADER_CURL_HTTP_PROXY_H */

View file

@ -1,266 +0,0 @@
/***************************************************************************
* _ _ ____ _
* 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"
#ifndef CURL_DISABLE_PROXY
#include "curlx/inet_pton.h"
#include "noproxy.h"
#include "curlx/strparse.h"
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
#ifdef HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif
/*
* cidr4_match() returns TRUE if the given IPv4 address is within the
* specified CIDR address range.
*
* @unittest 1614
*/
UNITTEST bool cidr4_match(const char *ipv4, /* 1.2.3.4 address */
const char *network, /* 1.2.3.4 address */
unsigned int bits);
UNITTEST bool cidr4_match(const char *ipv4, /* 1.2.3.4 address */
const char *network, /* 1.2.3.4 address */
unsigned int bits)
{
unsigned int address = 0;
unsigned int check = 0;
if(bits > 32)
/* strange input */
return FALSE;
if(curlx_inet_pton(AF_INET, ipv4, &address) != 1)
return FALSE;
if(curlx_inet_pton(AF_INET, network, &check) != 1)
return FALSE;
if(bits && (bits != 32)) {
unsigned int mask = 0xffffffff << (32 - bits);
unsigned int haddr = htonl(address);
unsigned int hcheck = htonl(check);
#if 0
curl_mfprintf(stderr, "Host %s (%x) network %s (%x) "
"bits %u mask %x => %x\n",
ipv4, haddr, network, hcheck, bits, mask,
(haddr ^ hcheck) & mask);
#endif
if((haddr ^ hcheck) & mask)
return FALSE;
return TRUE;
}
return address == check;
}
/* @unittest 1614 */
UNITTEST bool cidr6_match(const char *ipv6, const char *network,
unsigned int bits);
UNITTEST bool cidr6_match(const char *ipv6, const char *network,
unsigned int bits)
{
#ifdef USE_IPV6
unsigned int bytes;
unsigned int rest;
unsigned char address[16];
unsigned char check[16];
if(!bits)
bits = 128;
bytes = bits / 8;
rest = bits & 0x07;
if((bytes > 16) || ((bytes == 16) && rest))
return FALSE;
if(curlx_inet_pton(AF_INET6, ipv6, address) != 1)
return FALSE;
if(curlx_inet_pton(AF_INET6, network, check) != 1)
return FALSE;
if(bytes && memcmp(address, check, bytes))
return FALSE;
if(rest && ((address[bytes] ^ check[bytes]) & (0xff << (8 - rest))))
return FALSE;
return TRUE;
#else
(void)ipv6;
(void)network;
(void)bits;
return FALSE;
#endif
}
enum nametype {
TYPE_HOST,
TYPE_IPV4,
TYPE_IPV6
};
static bool match_host(const char *token, size_t tokenlen,
const char *name, size_t namelen)
{
bool match = FALSE;
/* ignore trailing dots in the token to check */
if(token[tokenlen - 1] == '.')
tokenlen--;
if(tokenlen && (*token == '.')) {
/* ignore leading token dot as well */
token++;
tokenlen--;
}
/* A: example.com matches 'example.com'
B: www.example.com matches 'example.com'
C: nonexample.com DOES NOT match 'example.com'
*/
if(tokenlen == namelen)
/* case A, exact match */
match = curl_strnequal(token, name, namelen);
else if(tokenlen < namelen) {
/* case B, tailmatch domain */
match = (name[namelen - tokenlen - 1] == '.') &&
curl_strnequal(token, name + (namelen - tokenlen), tokenlen);
}
/* case C passes through, not a match */
return match;
}
static bool match_ip(int type, const char *token, size_t tokenlen,
const char *name)
{
char *slash;
unsigned int bits = 0;
char checkip[128];
if(tokenlen >= sizeof(checkip))
/* this cannot match */
return FALSE;
/* copy the check name to a temp buffer */
memcpy(checkip, token, tokenlen);
checkip[tokenlen] = 0;
slash = strchr(checkip, '/');
/* if the slash is part of this token, use it */
if(slash) {
curl_off_t value;
const char *p = &slash[1];
if(curlx_str_number(&p, &value, 128) || *p)
return FALSE;
/* a too large value is rejected in the cidr function below */
bits = (unsigned int)value;
*slash = 0; /* null-terminate there */
}
if(type == TYPE_IPV6)
return cidr6_match(name, checkip, bits);
else
return cidr4_match(name, checkip, bits);
}
/****************************************************************
* Checks if the host is in the noproxy list. returns TRUE if it matches and
* therefore the proxy should NOT be used.
****************************************************************/
bool Curl_check_noproxy(const char *name, const char *no_proxy)
{
/*
* If we do not have a hostname at all, like for example with a FILE
* transfer, we have nothing to interrogate the noproxy list with.
*/
if(!name || name[0] == '\0')
return FALSE;
/* no_proxy=domain1.dom,host.domain2.dom
* (a comma-separated list of hosts which should
* not be proxied, or an asterisk to override
* all proxy variables)
*/
if(no_proxy && no_proxy[0]) {
const char *p = no_proxy;
size_t namelen;
char address[16];
enum nametype type = TYPE_HOST;
if(!strcmp("*", no_proxy))
return TRUE;
/* NO_PROXY was specified and it was not only an asterisk */
/* Check if name is an IP address; if not, assume it being a hostname. */
namelen = strlen(name);
if(curlx_inet_pton(AF_INET, name, &address) == 1)
type = TYPE_IPV4;
#ifdef USE_IPV6
else if(curlx_inet_pton(AF_INET6, name, &address) == 1)
type = TYPE_IPV6;
#endif
else {
/* ignore trailing dots in the hostname */
if(name[namelen - 1] == '.')
namelen--;
}
while(*p) {
const char *token;
size_t tokenlen = 0;
/* pass blanks */
curlx_str_passblanks(&p);
token = p;
/* pass over the pattern */
while(*p && !ISBLANK(*p) && (*p != ',')) {
p++;
tokenlen++;
}
if(tokenlen) {
bool match = FALSE;
if(type == TYPE_HOST)
match = match_host(token, tokenlen, name, namelen);
else
match = match_ip(type, token, tokenlen, name);
if(match)
return TRUE;
}
/* pass blanks after pattern */
curlx_str_passblanks(&p);
/* if not a comma, this ends the loop */
if(*p != ',')
break;
/* pass any number of commas */
while(*p == ',')
p++;
} /* while(*p) */
} /* NO_PROXY was specified and it was not only an asterisk */
return FALSE;
}
#endif /* CURL_DISABLE_PROXY */

View file

@ -630,7 +630,7 @@ CURLcode Curl_peer_from_proxy_url(CURLU *uh,
}
DEBUGASSERT(pp.scheme);
if(IS_HTTPS_PROXY(proxytype) &&
if(CURL_PROXY_IS_HTTPS(proxytype) &&
!Curl_ssl_supports(data, SSLSUPP_HTTPS_PROXY)) {
failf(data, "Unsupported proxy \'%s\', libcurl is built without the "
"HTTPS-proxy support.", url);

673
lib/proxy.c Normal file
View file

@ -0,0 +1,673 @@
/***************************************************************************
* _ _ ____ _
* 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"
#ifndef CURL_DISABLE_PROXY
#include "urldata.h"
#include "curl_trc.h"
#include "protocol.h"
#include "proxy.h"
#include "http_proxy.h"
#include "strcase.h"
#include "url.h"
#include "vauth/vauth.h"
#include "curlx/inet_pton.h"
#include "curlx/strparse.h"
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
#ifdef HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif
/*
* cidr4_match() returns TRUE if the given IPv4 address is within the
* specified CIDR address range.
*
* @unittest 1614
*/
UNITTEST bool cidr4_match(const char *ipv4, /* 1.2.3.4 address */
const char *network, /* 1.2.3.4 address */
unsigned int bits);
UNITTEST bool cidr4_match(const char *ipv4, /* 1.2.3.4 address */
const char *network, /* 1.2.3.4 address */
unsigned int bits)
{
unsigned int address = 0;
unsigned int check = 0;
if(bits > 32)
/* strange input */
return FALSE;
if(curlx_inet_pton(AF_INET, ipv4, &address) != 1)
return FALSE;
if(curlx_inet_pton(AF_INET, network, &check) != 1)
return FALSE;
if(bits && (bits != 32)) {
unsigned int mask = 0xffffffff << (32 - bits);
unsigned int haddr = htonl(address);
unsigned int hcheck = htonl(check);
#if 0
curl_mfprintf(stderr, "Host %s (%x) network %s (%x) "
"bits %u mask %x => %x\n",
ipv4, haddr, network, hcheck, bits, mask,
(haddr ^ hcheck) & mask);
#endif
if((haddr ^ hcheck) & mask)
return FALSE;
return TRUE;
}
return address == check;
}
/* @unittest 1614 */
UNITTEST bool cidr6_match(const char *ipv6, const char *network,
unsigned int bits);
UNITTEST bool cidr6_match(const char *ipv6, const char *network,
unsigned int bits)
{
#ifdef USE_IPV6
unsigned int bytes;
unsigned int rest;
unsigned char address[16];
unsigned char check[16];
if(!bits)
bits = 128;
bytes = bits / 8;
rest = bits & 0x07;
if((bytes > 16) || ((bytes == 16) && rest))
return FALSE;
if(curlx_inet_pton(AF_INET6, ipv6, address) != 1)
return FALSE;
if(curlx_inet_pton(AF_INET6, network, check) != 1)
return FALSE;
if(bytes && memcmp(address, check, bytes))
return FALSE;
if(rest && ((address[bytes] ^ check[bytes]) & (0xff << (8 - rest))))
return FALSE;
return TRUE;
#else
(void)ipv6;
(void)network;
(void)bits;
return FALSE;
#endif
}
enum nametype {
TYPE_HOST,
TYPE_IPV4,
TYPE_IPV6
};
static bool match_host(const char *token, size_t tokenlen,
const char *name, size_t namelen)
{
bool match = FALSE;
/* ignore trailing dots in the token to check */
if(token[tokenlen - 1] == '.')
tokenlen--;
if(tokenlen && (*token == '.')) {
/* ignore leading token dot as well */
token++;
tokenlen--;
}
/* A: example.com matches 'example.com'
B: www.example.com matches 'example.com'
C: nonexample.com DOES NOT match 'example.com'
*/
if(tokenlen == namelen)
/* case A, exact match */
match = curl_strnequal(token, name, namelen);
else if(tokenlen < namelen) {
/* case B, tailmatch domain */
match = (name[namelen - tokenlen - 1] == '.') &&
curl_strnequal(token, name + (namelen - tokenlen), tokenlen);
}
/* case C passes through, not a match */
return match;
}
static bool match_ip(int type, const char *token, size_t tokenlen,
const char *name)
{
char *slash;
unsigned int bits = 0;
char checkip[128];
if(tokenlen >= sizeof(checkip))
/* this cannot match */
return FALSE;
/* copy the check name to a temp buffer */
memcpy(checkip, token, tokenlen);
checkip[tokenlen] = 0;
slash = strchr(checkip, '/');
/* if the slash is part of this token, use it */
if(slash) {
curl_off_t value;
const char *p = &slash[1];
if(curlx_str_number(&p, &value, 128) || *p)
return FALSE;
/* a too large value is rejected in the cidr function below */
bits = (unsigned int)value;
*slash = 0; /* null-terminate there */
}
if(type == TYPE_IPV6)
return cidr6_match(name, checkip, bits);
else
return cidr4_match(name, checkip, bits);
}
/****************************************************************
* Checks if the host is in the noproxy list. returns TRUE if it matches and
* therefore the proxy should NOT be used.
****************************************************************/
/* @unittest 1614 */
UNITTEST bool proxy_check_noproxy(const char *name, const char *no_proxy);
UNITTEST bool proxy_check_noproxy(const char *name, const char *no_proxy)
{
/*
* If we do not have a hostname at all, like for example with a FILE
* transfer, we have nothing to interrogate the noproxy list with.
*/
if(!name || name[0] == '\0')
return FALSE;
/* no_proxy=domain1.dom,host.domain2.dom
* (a comma-separated list of hosts which should
* not be proxied, or an asterisk to override
* all proxy variables)
*/
if(no_proxy && no_proxy[0]) {
const char *p = no_proxy;
size_t namelen;
char address[16];
enum nametype type = TYPE_HOST;
if(!strcmp("*", no_proxy))
return TRUE;
/* NO_PROXY was specified and it was not only an asterisk */
/* Check if name is an IP address; if not, assume it being a hostname. */
namelen = strlen(name);
if(curlx_inet_pton(AF_INET, name, &address) == 1)
type = TYPE_IPV4;
#ifdef USE_IPV6
else if(curlx_inet_pton(AF_INET6, name, &address) == 1)
type = TYPE_IPV6;
#endif
else {
/* ignore trailing dots in the hostname */
if(name[namelen - 1] == '.')
namelen--;
}
while(*p) {
const char *token;
size_t tokenlen = 0;
/* pass blanks */
curlx_str_passblanks(&p);
token = p;
/* pass over the pattern */
while(*p && !ISBLANK(*p) && (*p != ',')) {
p++;
tokenlen++;
}
if(tokenlen) {
bool match = FALSE;
if(type == TYPE_HOST)
match = match_host(token, tokenlen, name, namelen);
else
match = match_ip(type, token, tokenlen, name);
if(match)
return TRUE;
}
/* pass blanks after pattern */
curlx_str_passblanks(&p);
/* if not a comma, this ends the loop */
if(*p != ',')
break;
/* pass any number of commas */
while(*p == ',')
p++;
} /* while(*p) */
} /* NO_PROXY was specified and it was not only an asterisk */
return FALSE;
}
#ifndef CURL_DISABLE_HTTP
/****************************************************************
* Detect what (if any) proxy to use. Remember that this selects a host
* name and is not limited to HTTP proxies only.
* The returned pointer must be freed by the caller.
****************************************************************/
static char *proxy_detect_proxy(struct Curl_easy *data,
const struct Curl_scheme *scheme)
{
char *proxy = NULL;
/* If proxy was not specified, we check for default proxy environment
* variables, to enable i.e Lynx compliance:
*
* http_proxy=http://some.server.dom:port/
* https_proxy=http://some.server.dom:port/
* ftp_proxy=http://some.server.dom:port/
* no_proxy=domain1.dom,host.domain2.dom
* (a comma-separated list of hosts which should
* not be proxied, or an asterisk to override
* all proxy variables)
* all_proxy=http://some.server.dom:port/
* (seems to exist for the CERN www lib. Probably
* the first to check for.)
*
* For compatibility, the all-uppercase versions of these variables are
* checked if the lowercase versions do not exist.
*/
char proxy_env[20];
const char *envp;
VERBOSE(envp = proxy_env);
curl_msnprintf(proxy_env, sizeof(proxy_env), "%s_proxy", scheme->name);
/* read the protocol proxy: */
proxy = curl_getenv(proxy_env);
/*
* We do not try the uppercase version of HTTP_PROXY because of
* security reasons:
*
* When curl is used in a webserver application
* environment (cgi or php), this environment variable can
* be controlled by the web server user by setting the
* http header 'Proxy:' to some value.
*
* This can cause 'internal' http/ftp requests to be
* arbitrarily redirected by any external attacker.
*/
if(!proxy && !curl_strequal("http_proxy", proxy_env)) {
/* There was no lowercase variable, try the uppercase version: */
Curl_strntoupper(proxy_env, proxy_env, sizeof(proxy_env));
proxy = curl_getenv(proxy_env);
}
if(!proxy) {
#ifndef CURL_DISABLE_WEBSOCKETS
/* websocket proxy fallbacks */
if(curl_strequal("ws_proxy", proxy_env)) {
proxy = curl_getenv("http_proxy");
}
else if(curl_strequal("wss_proxy", proxy_env)) {
proxy = curl_getenv("https_proxy");
if(!proxy)
proxy = curl_getenv("HTTPS_PROXY");
}
if(!proxy) {
#endif
envp = "all_proxy";
proxy = curl_getenv(envp); /* default proxy to use */
if(!proxy) {
envp = "ALL_PROXY";
proxy = curl_getenv(envp);
}
#ifndef CURL_DISABLE_WEBSOCKETS
}
#endif
}
if(proxy)
infof(data, "Uses proxy env variable %s == '%s'", envp, proxy);
return proxy;
}
#endif /* CURL_DISABLE_HTTP */
/*
* If this is supposed to use a proxy, we need to figure out the proxy
* hostname, so that we can reuse an existing connection
* that may exist registered to the same proxy host.
*/
static CURLcode parse_proxy(struct Curl_easy *data,
const char *proxy,
bool for_pre_proxy,
struct proxy_info *proxyinfo)
{
char *proxyuser = NULL;
char *proxypasswd = NULL;
char *scheme = NULL;
CURLcode result = CURLE_OK;
/* Set the start proxy type for url scheme guessing */
uint8_t proxytype = for_pre_proxy ? CURLPROXY_SOCKS4 : data->set.proxytype;
CURLU *uhp = curl_url();
CURLUcode uc;
if(!uhp) {
result = CURLE_OUT_OF_MEMORY;
goto error;
}
/* When parsing the proxy, allowing non-supported schemes since we have
these made up ones for proxies. Guess scheme for URLs without it. */
uc = curl_url_set(uhp, CURLUPART_URL, proxy,
CURLU_NON_SUPPORT_SCHEME | CURLU_GUESS_SCHEME);
if(!uc) {
/* parsed okay as a URL - only update proxytype when scheme was explicit */
uc = curl_url_get(uhp, CURLUPART_SCHEME, &scheme, CURLU_NO_GUESS_SCHEME);
if(!uc) {
result = Curl_scheme_to_proxytype(data, scheme, &proxytype, proxy);
if(result)
goto error;
}
else if(uc != CURLUE_NO_SCHEME) {
result = CURLE_OUT_OF_MEMORY;
goto error;
}
/* else: no explicit scheme, keep the configured proxytype */
}
else {
failf(data, "Unsupported proxy syntax in \'%s\': %s", proxy,
curl_url_strerror(uc));
result = CURLE_COULDNT_RESOLVE_PROXY;
goto error;
}
result = Curl_peer_from_proxy_url(uhp, data, proxy, proxytype,
&proxyinfo->peer, &proxytype);
if(result)
goto error;
switch(proxytype) {
case CURLPROXY_HTTP:
case CURLPROXY_HTTP_1_0:
case CURLPROXY_HTTPS:
case CURLPROXY_HTTPS2:
case CURLPROXY_HTTPS3:
if(for_pre_proxy) {
failf(data, "Unsupported pre-proxy type for \'%s\'", proxy);
result = CURLE_COULDNT_RESOLVE_PROXY;
goto error;
}
break;
case CURLPROXY_SOCKS4:
case CURLPROXY_SOCKS4A:
case CURLPROXY_SOCKS5:
case CURLPROXY_SOCKS5_HOSTNAME:
break;
default:
failf(data, "Unsupported proxy type %u for \'%s\'", proxytype, proxy);
result = CURLE_COULDNT_RESOLVE_PROXY;
goto error;
}
/* Is there a username and password given in this proxy URL? */
uc = curl_url_get(uhp, CURLUPART_USER, &proxyuser, CURLU_URLDECODE);
if(uc && (uc != CURLUE_NO_USER)) {
result = Curl_uc_to_curlcode(uc);
goto error;
}
uc = curl_url_get(uhp, CURLUPART_PASSWORD, &proxypasswd, CURLU_URLDECODE);
if(uc && (uc != CURLUE_NO_PASSWORD)) {
result = Curl_uc_to_curlcode(uc);
goto error;
}
if(proxyuser || proxypasswd) {
result = Curl_creds_create(proxyuser, proxypasswd, NULL, NULL,
data->set.str[STRING_PROXY_SERVICE_NAME],
CREDS_URL, &proxyinfo->creds);
if(result)
goto error;
}
else if(!for_pre_proxy &&
(data->set.str[STRING_PROXYUSERNAME] ||
data->set.str[STRING_PROXYPASSWORD] ||
data->set.str[STRING_PROXY_SERVICE_NAME])) {
/* No user/passwd in URL, if this is not a pre-proxy, the
* CURLOPT_PROXY* settings apply. */
result = Curl_creds_create(data->set.str[STRING_PROXYUSERNAME],
data->set.str[STRING_PROXYPASSWORD],
NULL, NULL,
data->set.str[STRING_PROXY_SERVICE_NAME],
CREDS_OPTION, &proxyinfo->creds);
}
else
Curl_creds_unlink(&proxyinfo->creds);
proxyinfo->proxytype = proxytype;
error:
curlx_free(scheme);
curlx_free(proxyuser);
curlx_free(proxypasswd);
curl_url_cleanup(uhp);
#ifdef DEBUGBUILD
if(!result) {
DEBUGASSERT(proxyinfo);
DEBUGASSERT(proxyinfo->peer);
}
#endif
return result;
}
/* Is transfer's origin exempted from proxy use? */
static bool proxy_do_not_proxy(struct Curl_easy *data)
{
const char *no_proxy;
char *env_no_proxy = NULL;
bool do_not_proxy;
/* no proxying if the transfer does not use the network */
if(data->state.origin->scheme->flags & PROTOPT_NONETWORK)
return TRUE;
no_proxy = data->set.str[STRING_NOPROXY];
if(!no_proxy) {
const char *p = "no_proxy";
env_no_proxy = curl_getenv(p);
if(!env_no_proxy) {
p = "NO_PROXY";
env_no_proxy = curl_getenv(p);
}
if(env_no_proxy)
infof(data, "Uses proxy env variable %s == '%s'", p, env_no_proxy);
no_proxy = env_no_proxy;
}
do_not_proxy = proxy_check_noproxy(data->state.origin->hostname, no_proxy);
curlx_safefree(env_no_proxy);
return do_not_proxy;
}
CURLcode Curl_proxy_init_conn(struct Curl_easy *data,
struct connectdata *conn)
{
char *proxy = NULL;
char *pre_proxy = NULL;
bool do_env_detect = TRUE;
CURLcode result = CURLE_OK;
/* Enforce no proxy use unless we decide to use one */
conn->bits.origin_is_proxy = FALSE;
DEBUGASSERT(!conn->socks_proxy.peer);
DEBUGASSERT(!conn->http_proxy.peer);
if(proxy_do_not_proxy(data))
goto out;
/*************************************************************
* Detect what (if any) proxy to use
*************************************************************/
/* the empty config strings disable proxy use and env detects */
if(data->set.str[STRING_PROXY]) {
if(*data->set.str[STRING_PROXY]) {
proxy = curlx_strdup(data->set.str[STRING_PROXY]);
/* if global proxy is set, this is it */
if(!proxy) {
failf(data, "memory shortage");
result = CURLE_OUT_OF_MEMORY;
goto out;
}
}
else
do_env_detect = FALSE;
}
if(data->set.str[STRING_PRE_PROXY]) {
if(*data->set.str[STRING_PRE_PROXY]) {
pre_proxy = curlx_strdup(data->set.str[STRING_PRE_PROXY]);
/* if global socks proxy is set, this is it */
if(!pre_proxy) {
failf(data, "memory shortage");
result = CURLE_OUT_OF_MEMORY;
goto out;
}
}
else
do_env_detect = FALSE;
}
#ifndef CURL_DISABLE_HTTP
/* None configured, detect possible proxy from environment. */
if(!proxy && !pre_proxy && do_env_detect)
proxy = proxy_detect_proxy(data, conn->scheme);
#else
(void)do_env_detect;
#endif /* CURL_DISABLE_HTTP */
if(!proxy && !pre_proxy)
goto out;
if(pre_proxy) {
result = parse_proxy(data, pre_proxy, TRUE, &conn->socks_proxy);
if(result)
goto out;
}
if(proxy) {
result = parse_proxy(data, proxy, FALSE, &conn->http_proxy);
if(result)
goto out;
switch(conn->http_proxy.proxytype) {
case CURLPROXY_SOCKS4:
case CURLPROXY_SOCKS4A:
case CURLPROXY_SOCKS5:
case CURLPROXY_SOCKS5_HOSTNAME:
/* Whoops, it's not a HTTP proxy */
if(pre_proxy) {
/* and we already have a SOCKS pre-proxy. Cannot have both */
failf(data, "Having a SOCKS pre-proxy and proxy is not "
"supported with \'%s\'", proxy);
result = CURLE_COULDNT_RESOLVE_PROXY;
goto out;
}
/* switch */
conn->socks_proxy = conn->http_proxy;
memset(&conn->http_proxy, 0, sizeof(conn->http_proxy));
break;
default:
/* all other types are HTTP */
break;
}
}
if(conn->socks_proxy.peer) {
DEBUGASSERT(!CURL_PROXY_IS_ANY_HTTP(conn->socks_proxy.proxytype));
}
#ifdef CURL_DISABLE_HTTP
if(conn->http_proxy.peer) {
/* asking for an HTTP proxy is a bit funny when HTTP is disabled... */
result = CURLE_UNSUPPORTED_PROTOCOL;
goto out;
}
#else /* CURL_DISABLE_HTTP */
if(conn->http_proxy.peer) {
const struct Curl_scheme *scheme = data->state.origin->scheme;
bool tunnel_proxy = (bool)data->set.tunnel_thru_httpproxy;
DEBUGASSERT(CURL_PROXY_IS_ANY_HTTP(conn->http_proxy.proxytype));
if(!tunnel_proxy) {
/* Decide if we tunnel through proxy automatically */
if(conn->via_peer) {
/* With connect-to, we always tunnel */
tunnel_proxy = TRUE;
}
else if(scheme->flags & PROTOPT_SSL) {
/* If the transfer is supposed to be secure, we tunnel */
tunnel_proxy = TRUE;
}
else if(scheme->flags & PROTOPT_HTTP_PROXY_TUNNEL) {
/* transfer scheme required tunneling */
tunnel_proxy = TRUE;
}
else if(!(scheme->protocol & PROTO_FAMILY_HTTP) &&
!(scheme->flags & PROTOPT_PROXY_AS_HTTP)) {
/* Cannot delegate transfer URL to HTTP proxy */
tunnel_proxy = TRUE;
}
}
if(!tunnel_proxy) {
/* HTTP proxy used in forwarding mode. This means the connection
* is really to the proxy and NOT the origin of the transfer. */
DEBUGASSERT(!conn->via_peer);
Curl_peer_link(&conn->origin, conn->http_proxy.peer);
conn->scheme = conn->http_proxy.peer->scheme;
conn->bits.origin_is_proxy = TRUE;
}
#ifndef CURL_DISABLE_DIGEST_AUTH
if(!Curl_safecmp(data->state.envproxy, proxy)) {
/* proxy changed */
Curl_auth_digest_cleanup(&data->state.proxydigest);
curlx_free(data->state.envproxy);
data->state.envproxy = curlx_strdup(proxy);
}
#endif
}
#endif /* !CURL_DISABLE_HTTP */
out:
curlx_free(pre_proxy);
curlx_free(proxy);
return result;
}
#endif /* CURL_DISABLE_PROXY */

View file

@ -1,5 +1,5 @@
#ifndef HEADER_CURL_NOPROXY_H
#define HEADER_CURL_NOPROXY_H
#ifndef HEADER_CURL_PROXY_H
#define HEADER_CURL_PROXY_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
@ -26,7 +26,34 @@
#include "curl_setup.h"
#ifndef CURL_DISABLE_PROXY
bool Curl_check_noproxy(const char *name, const char *no_proxy);
#endif
#endif /* HEADER_CURL_NOPROXY_H */
struct Curl_easy;
struct Curl_peer;
struct Curl_creds;
struct connectdata;
struct proxy_info {
struct Curl_peer *peer; /* proxy to this peer */
struct Curl_creds *creds; /* use these credentials, maybe NULL */
uint8_t proxytype; /* what kind of proxy that is in use */
};
#define CURL_PROXY_IS_HTTPS(t) \
(((t) == CURLPROXY_HTTPS) || \
((t) == CURLPROXY_HTTPS2) || \
((t) == CURLPROXY_HTTPS3))
#define CURL_PROXY_IS_HTTP(t) \
(((t) == CURLPROXY_HTTP) || \
((t) == CURLPROXY_HTTP_1_0))
#define CURL_PROXY_IS_ANY_HTTP(t) \
(CURL_PROXY_IS_HTTP(t) || \
CURL_PROXY_IS_HTTPS(t))
CURLcode Curl_proxy_init_conn(struct Curl_easy *data,
struct connectdata *conn);
#endif /* !CURL_DISABLE_PROXY */
#endif /* HEADER_CURL_PROXY_H */

View file

@ -493,6 +493,7 @@ CURLcode Curl_pretransfer(struct Curl_easy *data)
/* initial transfer request coming up, forget the initial origin
* from a previous perform() on this handle. */
Curl_peer_unlink(&data->state.initial_origin);
Curl_peer_unlink(&data->state.origin);
data->state.requests = 0;
data->state.followlocation = 0; /* reset the location-follow counter */
data->state.this_is_a_follow = FALSE; /* reset this */

781
lib/url.c
View file

@ -86,7 +86,7 @@
#include "urlapi-int.h"
#include "system_win32.h"
#include "hsts.h"
#include "noproxy.h"
#include "proxy.h"
#include "cfilters.h"
#include "idn.h"
#include "http_proxy.h"
@ -245,6 +245,7 @@ CURLcode Curl_close(struct Curl_easy **datap)
/* Close down all open SSL info and sessions */
Curl_ssl_close_all(data);
Curl_peer_unlink(&data->state.origin);
Curl_peer_unlink(&data->state.initial_origin);
Curl_ssl_free_certinfo(data);
@ -840,36 +841,27 @@ static bool url_match_ssl_use(struct connectdata *conn,
static bool url_match_proxy_use(struct connectdata *conn,
struct url_conn_match *m)
{
if(m->needle->bits.httpproxy != conn->bits.httpproxy ||
m->needle->bits.socksproxy != conn->bits.socksproxy)
if(m->needle->bits.origin_is_proxy != conn->bits.origin_is_proxy)
return FALSE;
if(m->needle->bits.socksproxy &&
!proxy_info_matches(&m->needle->socks_proxy, &conn->socks_proxy))
if(!proxy_info_matches(&m->needle->socks_proxy, &conn->socks_proxy))
return FALSE;
if(m->needle->bits.httpproxy) {
if(m->needle->bits.tunnel_proxy != conn->bits.tunnel_proxy)
return FALSE;
if(!proxy_info_matches(&m->needle->http_proxy, &conn->http_proxy))
return FALSE;
if(!proxy_info_matches(&m->needle->http_proxy, &conn->http_proxy))
if(CURL_PROXY_IS_HTTPS(m->needle->http_proxy.proxytype)) {
/* https proxies come in different types, http/1.1, h2, ... */
/* match SSL config to proxy */
if(!Curl_ssl_conn_config_match(m->data, conn, TRUE)) {
DEBUGF(infof(m->data,
"Connection #%" FMT_OFF_T
" has different SSL proxy parameters, cannot reuse",
conn->connection_id));
return FALSE;
if(IS_HTTPS_PROXY(m->needle->http_proxy.proxytype)) {
/* https proxies come in different types, http/1.1, h2, ... */
if(m->needle->http_proxy.proxytype != conn->http_proxy.proxytype)
return FALSE;
/* match SSL config to proxy */
if(!Curl_ssl_conn_config_match(m->data, conn, TRUE)) {
DEBUGF(infof(m->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 */
}
/* the SSL config to the server, which may apply here is checked
* further below */
}
return TRUE;
}
@ -981,14 +973,6 @@ static bool url_match_destination(struct connectdata *conn,
if(!Curl_peer_same_destination(m->needle->via_peer, conn->via_peer))
return FALSE;
#ifndef CURL_DISABLE_PROXY
if(m->needle->bits.httpproxy && !m->needle->bits.tunnel_proxy) {
/* Talking to a non-tunneling HTTP proxy matches on proxy peers. */
return Curl_peer_equal(m->needle->http_proxy.peer,
conn->http_proxy.peer);
}
#endif
if(m->needle->origin->scheme != conn->origin->scheme) {
/* `needle` and `conn` not having the same scheme.
* This is allowed for the same family *if* conn is using TLS.
@ -1310,28 +1294,6 @@ static struct connectdata *allocate_conn(struct Curl_easy *data)
/* Store current time to give a baseline to keepalive connection times. */
conn->keepalive = conn->created;
#ifndef CURL_DISABLE_PROXY
conn->http_proxy.proxytype = data->set.proxytype;
conn->socks_proxy.proxytype = CURLPROXY_SOCKS4;
/* note that these two proxy bits are set on what looks to be
requested, they may be altered down the road */
conn->bits.proxy = (data->set.str[STRING_PROXY] &&
*data->set.str[STRING_PROXY]);
conn->bits.httpproxy = (conn->bits.proxy &&
(conn->http_proxy.proxytype == CURLPROXY_HTTP ||
conn->http_proxy.proxytype == CURLPROXY_HTTP_1_0 ||
IS_HTTPS_PROXY(conn->http_proxy.proxytype)));
conn->bits.socksproxy = (conn->bits.proxy && !conn->bits.httpproxy);
if(data->set.str[STRING_PRE_PROXY] && *data->set.str[STRING_PRE_PROXY]) {
conn->bits.proxy = TRUE;
conn->bits.socksproxy = TRUE;
}
conn->bits.tunnel_proxy = data->set.tunnel_thru_httpproxy;
#endif /* CURL_DISABLE_PROXY */
#ifndef CURL_DISABLE_FTP
conn->bits.ftp_use_epsv = data->set.ftp_use_epsv;
conn->bits.ftp_use_eprt = data->set.ftp_use_eprt;
@ -1406,14 +1368,13 @@ CURLcode Curl_uc_to_curlcode(CURLUcode uc)
#ifndef CURL_DISABLE_HSTS
static CURLcode hsts_upgrade(struct Curl_easy *data,
struct connectdata *conn,
CURLU *uh,
uint16_t port_override,
uint32_t scope_id)
{
/* HSTS upgrade */
if(data->hsts && (conn->origin->scheme == &Curl_scheme_http) &&
Curl_hsts_applies(data->hsts, conn->origin)) {
if(data->hsts && (data->state.origin->scheme == &Curl_scheme_http) &&
Curl_hsts_applies(data->hsts, data->state.origin)) {
char *url;
CURLUcode uc;
CURLcode result;
@ -1430,7 +1391,7 @@ static CURLcode hsts_upgrade(struct Curl_easy *data,
Curl_bufref_set(&data->state.url, url, 0, curl_free);
result = Curl_peer_from_url(uh, data, port_override, scope_id,
&data->state.up, &conn->origin);
&data->state.up, &data->state.origin);
if(result)
return result;
infof(data, "Switched from HTTP to HTTPS due to HSTS => %s", url);
@ -1438,7 +1399,7 @@ static CURLcode hsts_upgrade(struct Curl_easy *data,
return CURLE_OK;
}
#else
#define hsts_upgrade(x, y, z, a, b) CURLE_OK
#define hsts_upgrade(x, y, z, a) CURLE_OK
#endif
#ifndef CURL_DISABLE_NETRC
@ -1460,7 +1421,6 @@ static bool str_has_ctrl(const char *input)
* option or a .netrc file, if applicable.
*/
static CURLcode url_set_data_creds_netrc(struct Curl_easy *data,
struct connectdata *conn,
struct Curl_creds **pcreds)
{
struct Curl_creds *ncreds_out = NULL;
@ -1500,7 +1460,7 @@ static CURLcode url_set_data_creds_netrc(struct Curl_easy *data,
goto out;
ret = Curl_netrc_scan(data, &data->state.netrc,
conn->origin->hostname,
data->state.origin->hostname,
Curl_creds_user(ncreds_in),
data->set.str[STRING_NETRC_FILE],
&ncreds_out);
@ -1512,7 +1472,7 @@ static CURLcode url_set_data_creds_netrc(struct Curl_easy *data,
else if(ret && ((ret == NETRC_NO_MATCH) ||
(data->set.use_netrc == CURL_NETRC_OPTIONAL))) {
infof(data, "Could not find host %s in the %s file; using defaults",
conn->origin->hostname,
data->state.origin->hostname,
(data->set.str[STRING_NETRC_FILE] ?
data->set.str[STRING_NETRC_FILE] : ".netrc"));
}
@ -1523,7 +1483,7 @@ static CURLcode url_set_data_creds_netrc(struct Curl_easy *data,
goto out;
}
else if(ncreds_out) {
if(!(conn->scheme->flags & PROTOPT_USERPWDCTRL)) {
if(!(data->state.origin->scheme->flags & PROTOPT_USERPWDCTRL)) {
/* if the protocol cannot handle control codes in credentials, make
sure there are none */
if(str_has_ctrl(ncreds_out->user) ||
@ -1534,7 +1494,7 @@ static CURLcode url_set_data_creds_netrc(struct Curl_easy *data,
}
}
CURL_TRC_M(data, "netrc: using credentials for %s as %s",
conn->origin->hostname, ncreds_out->user);
data->state.origin->hostname, ncreds_out->user);
result = Curl_creds_merge(ncreds_out->user, ncreds_out->passwd,
*pcreds, CREDS_NETRC, pcreds);
if(result)
@ -1553,15 +1513,17 @@ static CURLcode url_set_data_creds_netrc(struct Curl_easy *data,
DEBUGASSERT(0);
}
#ifdef CURLVERBOSE
Curl_creds_trace(data, data->state.creds, "transfer credentials");
#endif
out:
Curl_creds_unlink(&ncreds_out);
return result;
}
#endif /* CURL_DISABLE_NETRC */
static CURLcode url_set_data_creds(struct Curl_easy *data,
struct connectdata *conn,
CURLU *uh)
static CURLcode url_set_data_creds(struct Curl_easy *data, CURLU *uh)
{
struct Curl_creds *newcreds = NULL;
CURLcode result = CURLE_OK;
@ -1571,7 +1533,7 @@ static CURLcode url_set_data_creds(struct Curl_easy *data,
data->set.str[STRING_BEARER] ||
data->set.str[STRING_SASL_AUTHZID] ||
data->set.str[STRING_SERVICE_NAME]) &&
Curl_auth_allowed_to_origin(data, conn->origin)) {
Curl_auth_allowed_to_origin(data, data->state.origin)) {
result = Curl_creds_create(data->set.str[STRING_USERNAME],
data->set.str[STRING_PASSWORD],
data->set.str[STRING_BEARER],
@ -1599,12 +1561,14 @@ static CURLcode url_set_data_creds(struct Curl_easy *data,
}
if(!result && data->state.up.user) {
result = Curl_urldecode(data->state.up.user, 0, &udecoded, NULL,
conn->scheme->flags&PROTOPT_USERPWDCTRL ?
(data->state.origin->scheme->flags &
PROTOPT_USERPWDCTRL) ?
REJECT_ZERO : REJECT_CTRL);
}
if(!result && data->state.up.password) {
result = Curl_urldecode(data->state.up.password, 0, &pdecoded, NULL,
conn->scheme->flags&PROTOPT_USERPWDCTRL ?
(data->state.origin->scheme->flags &
PROTOPT_USERPWDCTRL) ?
REJECT_ZERO : REJECT_CTRL);
}
if(!result)
@ -1622,7 +1586,7 @@ static CURLcode url_set_data_creds(struct Curl_easy *data,
#ifndef CURL_DISABLE_NETRC
/* Check for overridden login details and set them accordingly so that
they are known when protocol->setup_connection is called! */
result = url_set_data_creds_netrc(data, conn, &newcreds);
result = url_set_data_creds_netrc(data, &newcreds);
#endif /* CURL_DISABLE_NETRC */
out:
@ -1634,139 +1598,37 @@ out:
return result;
}
/*
* Parse URL and fill in the relevant members of the connection struct.
*/
static CURLcode parseurlandfillconn(struct Curl_easy *data,
struct connectdata *conn)
static CURLcode url_set_conn_origin_etc(struct Curl_easy *data,
struct connectdata *conn)
{
CURLcode result = CURLE_OK;
CURLU *uh;
CURLUcode uc;
bool use_set_uh = (data->set.uh && !data->state.this_is_a_follow);
uint16_t port_override = data->state.allow_port ? data->set.use_port : 0;
uint32_t scope_id = 0;
up_free(data); /* cleanup previous leftovers first */
Curl_peer_link(&conn->origin, data->state.origin);
/* parse the URL */
if(use_set_uh)
uh = data->state.uh = curl_url_dup(data->set.uh);
else
uh = data->state.uh = curl_url();
if(!uh) {
result = CURLE_OUT_OF_MEMORY;
goto out;
}
/* Calculate the *real* URL this transfer uses, applying defaults
* where information is missing. */
if(data->set.str[STRING_DEFAULT_PROTOCOL] &&
!Curl_is_absolute_url(Curl_bufref_ptr(&data->state.url), NULL, 0, TRUE)) {
char *url = curl_maprintf("%s://%s",
data->set.str[STRING_DEFAULT_PROTOCOL],
Curl_bufref_ptr(&data->state.url));
if(!url) {
result = CURLE_OUT_OF_MEMORY;
goto out;
}
Curl_bufref_set(&data->state.url, url, 0, curl_free);
}
if(!use_set_uh) {
char *newurl;
uc = curl_url_set(uh, CURLUPART_URL, Curl_bufref_ptr(&data->state.url),
(unsigned int)(CURLU_GUESS_SCHEME |
CURLU_NON_SUPPORT_SCHEME |
(data->set.disallow_username_in_url ?
CURLU_DISALLOW_USER : 0) |
(data->set.path_as_is ? CURLU_PATH_AS_IS : 0)));
if(uc) {
failf(data, "URL rejected: %s", curl_url_strerror(uc));
result = Curl_uc_to_curlcode(uc);
goto out;
}
/* after it was parsed, get the generated normalized version */
uc = curl_url_get(uh, CURLUPART_URL, &newurl, CURLU_GET_EMPTY);
if(uc) {
result = Curl_uc_to_curlcode(uc);
goto out;
}
Curl_bufref_set(&data->state.url, newurl, 0, curl_free);
}
#ifdef USE_IPV6
scope_id = data->set.scope_id;
#endif
/* `uh` is now as the connection should use it, probably. */
result = Curl_peer_from_url(uh, data, port_override, scope_id,
&data->state.up, &conn->origin);
if(result)
goto out;
result = hsts_upgrade(data, conn, uh, port_override, scope_id);
if(result)
goto out;
/* now that the origin is fixed, check and set the connection scheme */
/* set the connection scheme */
result = url_set_conn_scheme(data, conn, conn->origin->scheme);
if(result)
goto out;
/* When the transfers initial_origin is not set, this is the initial
* request. Remember this starting point. This is used to
* select credentials. */
if(!data->state.initial_origin)
Curl_peer_link(&data->state.initial_origin, conn->origin);
result = url_set_data_creds(data, conn, uh);
if(result)
goto out;
uc = curl_url_get(uh, CURLUPART_OPTIONS, &data->state.up.options,
CURLU_URLDECODE);
if(!uc) {
conn->options = curlx_strdup(data->state.up.options);
if(!conn->options) {
result = CURLE_OUT_OF_MEMORY;
goto out;
}
}
else if(uc != CURLUE_NO_OPTIONS) {
result = Curl_uc_to_curlcode(uc);
goto out;
}
uc = curl_url_get(uh, CURLUPART_PATH, &data->state.up.path, CURLU_URLENCODE);
if(uc) {
result = Curl_uc_to_curlcode(uc);
goto out;
}
uc = curl_url_get(uh, CURLUPART_QUERY, &data->state.up.query,
CURLU_GET_EMPTY);
if(uc && (uc != CURLUE_NO_QUERY)) {
result = CURLE_OUT_OF_MEMORY;
goto out;
}
#ifdef USE_IPV6
/* Fill in the conn parts that do not use authority, yet. */
conn->scope_id = conn->origin->scopeid;
#endif
/* set the connection options */
if(data->set.str[STRING_OPTIONS]) {
curlx_free(conn->options);
conn->options = curlx_strdup(data->set.str[STRING_OPTIONS]);
if(!conn->options) {
result = CURLE_OUT_OF_MEMORY;
goto out;
}
}
else if(data->state.up.options) {
conn->options = curlx_strdup(data->state.up.options);
if(!conn->options) {
result = CURLE_OUT_OF_MEMORY;
goto out;
}
}
#ifdef CURLVERBOSE
Curl_creds_trace(data, data->state.creds, "transfer credentials");
#ifdef USE_IPV6
conn->scope_id = data->set.scope_id ?
data->set.scope_id : data->state.origin->scopeid;
#endif
out:
@ -1852,379 +1714,6 @@ static CURLcode setup_connection_internals(struct Curl_easy *data,
return CURLE_OK;
}
#ifndef CURL_DISABLE_PROXY
#ifndef CURL_DISABLE_HTTP
/****************************************************************
* Detect what (if any) proxy to use. Remember that this selects a host
* name and is not limited to HTTP proxies only.
* The returned pointer must be freed by the caller (unless NULL)
****************************************************************/
static char *url_detect_proxy(struct Curl_easy *data,
struct connectdata *conn)
{
char *proxy = NULL;
/* If proxy was not specified, we check for default proxy environment
* variables, to enable i.e Lynx compliance:
*
* http_proxy=http://some.server.dom:port/
* https_proxy=http://some.server.dom:port/
* ftp_proxy=http://some.server.dom:port/
* no_proxy=domain1.dom,host.domain2.dom
* (a comma-separated list of hosts which should
* not be proxied, or an asterisk to override
* all proxy variables)
* all_proxy=http://some.server.dom:port/
* (seems to exist for the CERN www lib. Probably
* the first to check for.)
*
* For compatibility, the all-uppercase versions of these variables are
* checked if the lowercase versions do not exist.
*/
char proxy_env[20];
const char *envp;
VERBOSE(envp = proxy_env);
curl_msnprintf(proxy_env, sizeof(proxy_env), "%s_proxy",
conn->scheme->name);
/* read the protocol proxy: */
proxy = curl_getenv(proxy_env);
/*
* We do not try the uppercase version of HTTP_PROXY because of
* security reasons:
*
* When curl is used in a webserver application
* environment (cgi or php), this environment variable can
* be controlled by the web server user by setting the
* http header 'Proxy:' to some value.
*
* This can cause 'internal' http/ftp requests to be
* arbitrarily redirected by any external attacker.
*/
if(!proxy && !curl_strequal("http_proxy", proxy_env)) {
/* There was no lowercase variable, try the uppercase version: */
Curl_strntoupper(proxy_env, proxy_env, sizeof(proxy_env));
proxy = curl_getenv(proxy_env);
}
if(!proxy) {
#ifndef CURL_DISABLE_WEBSOCKETS
/* websocket proxy fallbacks */
if(curl_strequal("ws_proxy", proxy_env)) {
proxy = curl_getenv("http_proxy");
}
else if(curl_strequal("wss_proxy", proxy_env)) {
proxy = curl_getenv("https_proxy");
if(!proxy)
proxy = curl_getenv("HTTPS_PROXY");
}
if(!proxy) {
#endif
envp = "all_proxy";
proxy = curl_getenv(envp); /* default proxy to use */
if(!proxy) {
envp = "ALL_PROXY";
proxy = curl_getenv(envp);
}
#ifndef CURL_DISABLE_WEBSOCKETS
}
#endif
}
if(proxy)
infof(data, "Uses proxy env variable %s == '%s'", envp, proxy);
return proxy;
}
#endif /* CURL_DISABLE_HTTP */
/*
* If this is supposed to use a proxy, we need to figure out the proxy
* hostname, so that we can reuse an existing connection
* that may exist registered to the same proxy host.
*/
static CURLcode parse_proxy(struct Curl_easy *data,
const char *proxy,
bool for_pre_proxy,
struct proxy_info *proxyinfo)
{
char *proxyuser = NULL;
char *proxypasswd = NULL;
char *scheme = NULL;
CURLcode result = CURLE_OK;
/* Set the start proxy type for url scheme guessing */
uint8_t proxytype = for_pre_proxy ? CURLPROXY_SOCKS4 : data->set.proxytype;
CURLU *uhp = curl_url();
CURLUcode uc;
if(!uhp) {
result = CURLE_OUT_OF_MEMORY;
goto error;
}
/* When parsing the proxy, allowing non-supported schemes since we have
these made up ones for proxies. Guess scheme for URLs without it. */
uc = curl_url_set(uhp, CURLUPART_URL, proxy,
CURLU_NON_SUPPORT_SCHEME | CURLU_GUESS_SCHEME);
if(!uc) {
/* parsed okay as a URL - only update proxytype when scheme was explicit */
uc = curl_url_get(uhp, CURLUPART_SCHEME, &scheme, CURLU_NO_GUESS_SCHEME);
if(!uc) {
result = Curl_scheme_to_proxytype(data, scheme, &proxytype, proxy);
if(result)
goto error;
}
else if(uc != CURLUE_NO_SCHEME) {
result = CURLE_OUT_OF_MEMORY;
goto error;
}
/* else: no explicit scheme, keep the configured proxytype */
}
else {
failf(data, "Unsupported proxy syntax in \'%s\': %s", proxy,
curl_url_strerror(uc));
result = CURLE_COULDNT_RESOLVE_PROXY;
goto error;
}
result = Curl_peer_from_proxy_url(uhp, data, proxy, proxytype,
&proxyinfo->peer, &proxytype);
if(result)
goto error;
switch(proxytype) {
case CURLPROXY_HTTP:
case CURLPROXY_HTTP_1_0:
case CURLPROXY_HTTPS:
case CURLPROXY_HTTPS2:
case CURLPROXY_HTTPS3:
if(for_pre_proxy) {
failf(data, "Unsupported pre-proxy type for \'%s\'", proxy);
result = CURLE_COULDNT_RESOLVE_PROXY;
goto error;
}
break;
case CURLPROXY_SOCKS4:
case CURLPROXY_SOCKS4A:
case CURLPROXY_SOCKS5:
case CURLPROXY_SOCKS5_HOSTNAME:
break;
default:
failf(data, "Unsupported proxy type %u for \'%s\'", proxytype, proxy);
result = CURLE_COULDNT_RESOLVE_PROXY;
goto error;
}
/* Is there a username and password given in this proxy URL? */
uc = curl_url_get(uhp, CURLUPART_USER, &proxyuser, CURLU_URLDECODE);
if(uc && (uc != CURLUE_NO_USER)) {
result = Curl_uc_to_curlcode(uc);
goto error;
}
uc = curl_url_get(uhp, CURLUPART_PASSWORD, &proxypasswd, CURLU_URLDECODE);
if(uc && (uc != CURLUE_NO_PASSWORD)) {
result = Curl_uc_to_curlcode(uc);
goto error;
}
if(proxyuser || proxypasswd) {
result = Curl_creds_create(proxyuser, proxypasswd, NULL, NULL,
data->set.str[STRING_PROXY_SERVICE_NAME],
CREDS_URL, &proxyinfo->creds);
if(result)
goto error;
}
else if(!for_pre_proxy &&
(data->set.str[STRING_PROXYUSERNAME] ||
data->set.str[STRING_PROXYPASSWORD] ||
data->set.str[STRING_PROXY_SERVICE_NAME])) {
/* No user/passwd in URL, if this is not a pre-proxy, the
* CURLOPT_PROXY* settings apply. */
result = Curl_creds_create(data->set.str[STRING_PROXYUSERNAME],
data->set.str[STRING_PROXYPASSWORD],
NULL, NULL,
data->set.str[STRING_PROXY_SERVICE_NAME],
CREDS_OPTION, &proxyinfo->creds);
}
else
Curl_creds_unlink(&proxyinfo->creds);
proxyinfo->proxytype = proxytype;
error:
curlx_free(scheme);
curlx_free(proxyuser);
curlx_free(proxypasswd);
curl_url_cleanup(uhp);
#ifdef DEBUGBUILD
if(!result) {
DEBUGASSERT(proxyinfo);
DEBUGASSERT(proxyinfo->peer);
}
#endif
return result;
}
static CURLcode url_set_conn_proxies(struct Curl_easy *data,
struct connectdata *conn)
{
char *proxy = NULL;
char *pre_proxy = NULL;
char *no_proxy = NULL;
CURLcode result = CURLE_OK;
/*************************************************************
* Detect what (if any) proxy to use
*************************************************************/
if(data->set.str[STRING_PROXY]) {
proxy = curlx_strdup(data->set.str[STRING_PROXY]);
/* if global proxy is set, this is it */
if(!proxy) {
failf(data, "memory shortage");
result = CURLE_OUT_OF_MEMORY;
goto out;
}
}
if(data->set.str[STRING_PRE_PROXY]) {
pre_proxy = curlx_strdup(data->set.str[STRING_PRE_PROXY]);
/* if global socks proxy is set, this is it */
if(!pre_proxy) {
failf(data, "memory shortage");
result = CURLE_OUT_OF_MEMORY;
goto out;
}
}
if(!data->set.str[STRING_NOPROXY]) {
const char *p = "no_proxy";
no_proxy = curl_getenv(p);
if(!no_proxy) {
p = "NO_PROXY";
no_proxy = curl_getenv(p);
}
if(no_proxy) {
infof(data, "Uses proxy env variable %s == '%s'", p, no_proxy);
}
}
if(Curl_check_noproxy(conn->origin->hostname, data->set.str[STRING_NOPROXY] ?
data->set.str[STRING_NOPROXY] : no_proxy)) {
curlx_safefree(proxy);
curlx_safefree(pre_proxy);
}
#ifndef CURL_DISABLE_HTTP
else if(!proxy && !pre_proxy)
/* if the host is not in the noproxy list, detect proxy. */
proxy = url_detect_proxy(data, conn);
#endif /* CURL_DISABLE_HTTP */
curlx_safefree(no_proxy);
if(proxy && (!*proxy || (conn->scheme->flags & PROTOPT_NONETWORK))) {
curlx_safefree(proxy); /* Do not bother with an empty proxy string
or if the protocol does not work with network */
}
if(pre_proxy && (!*pre_proxy ||
(conn->scheme->flags & PROTOPT_NONETWORK))) {
curlx_safefree(pre_proxy); /* Do not bother with an empty socks proxy
string or if the protocol does not work
with network */
}
/***********************************************************************
* If this is supposed to use a proxy, we need to figure out the proxy host
* name, proxy type and port number, so that we can reuse an existing
* connection that may exist registered to the same proxy host.
***********************************************************************/
if(proxy || pre_proxy) {
if(pre_proxy) {
result = parse_proxy(data, pre_proxy, TRUE, &conn->socks_proxy);
if(result)
goto out;
}
if(proxy) {
result = parse_proxy(data, proxy, FALSE, &conn->http_proxy);
if(result)
goto out;
switch(conn->http_proxy.proxytype) {
case CURLPROXY_SOCKS4:
case CURLPROXY_SOCKS4A:
case CURLPROXY_SOCKS5:
case CURLPROXY_SOCKS5_HOSTNAME:
/* Whoops, it's not a HTTP proxy */
if(conn->socks_proxy.peer) {
/* and we already have a SOCKS pre-proxy. Cannot have both */
failf(data, "Having a SOCKS pre-proxy and proxy is not "
"supported with \'%s\'", proxy);
result = CURLE_COULDNT_RESOLVE_PROXY;
goto out;
}
/* switch */
conn->socks_proxy = conn->http_proxy;
memset(&conn->http_proxy, 0, sizeof(conn->http_proxy));
break;
default:
break;
}
}
if(conn->http_proxy.peer) {
#ifdef CURL_DISABLE_HTTP
/* asking for an HTTP proxy is a bit funny when HTTP is disabled... */
result = CURLE_UNSUPPORTED_PROTOCOL;
goto out;
#else
#ifndef CURL_DISABLE_DIGEST_AUTH
if(!Curl_safecmp(data->state.envproxy, proxy)) {
/* proxy changed */
Curl_auth_digest_cleanup(&data->state.proxydigest);
curlx_free(data->state.envproxy);
data->state.envproxy = curlx_strdup(proxy);
}
#endif
if(conn->scheme->flags & PROTOPT_HTTP_PROXY_TUNNEL) {
conn->bits.tunnel_proxy = TRUE;
}
else if(!(conn->scheme->protocol & PROTO_FAMILY_HTTP)) {
/* force this connection's protocol to become HTTP if compatible */
if((conn->scheme->flags & PROTOPT_PROXY_AS_HTTP) &&
!conn->bits.tunnel_proxy)
conn->scheme = &Curl_scheme_http;
else
/* if not converting to HTTP over the proxy, enforce tunneling */
conn->bits.tunnel_proxy = TRUE;
}
#endif
}
else
conn->bits.tunnel_proxy = FALSE; /* no tunneling if not HTTP */
}
conn->bits.socksproxy = !!conn->socks_proxy.peer;
conn->bits.httpproxy = !!conn->http_proxy.peer;
conn->bits.proxy = conn->bits.httpproxy || conn->bits.socksproxy;
if(!conn->bits.proxy) {
/* we are not using the proxy after all... */
conn->bits.proxy = FALSE;
conn->bits.httpproxy = FALSE;
conn->bits.socksproxy = FALSE;
conn->bits.tunnel_proxy = FALSE;
/* CURLPROXY_HTTPS does not have its own flag in conn->bits, yet we need
to signal that CURLPROXY_HTTPS is not used for this connection */
conn->http_proxy.proxytype = CURLPROXY_HTTP;
}
out:
curlx_free(pre_proxy);
curlx_free(proxy);
return result;
}
#endif /* CURL_DISABLE_PROXY */
/*
* Curl_parse_login_details()
*
@ -2601,14 +2090,6 @@ static CURLcode url_create_needle(struct Curl_easy *data,
CURLcode result = CURLE_OK;
bool network_scheme = TRUE; /* almost all are */
/*************************************************************
* Check input data
*************************************************************/
if(!Curl_bufref_ptr(&data->state.url)) {
result = CURLE_URL_MALFORMAT;
goto out;
}
/* First, split up the current URL in parts so that we can use the
parts for checking against the already present connections. In order
to not have to modify everything at once, we allocate a temporary
@ -2627,7 +2108,7 @@ static CURLcode url_create_needle(struct Curl_easy *data,
* Determine `conn->origin` and propulate `data->state.up` and
* other URL related properties.
*************************************************************/
result = parseurlandfillconn(data, needle);
result = url_set_conn_origin_etc(data, needle);
if(result)
goto out;
@ -2661,22 +2142,11 @@ static CURLcode url_create_needle(struct Curl_easy *data,
#ifndef CURL_DISABLE_PROXY
/* Going via a unix socket ignores any proxy settings */
if(needle->via_peer && needle->via_peer->unix_socket) {
needle->bits.socksproxy = FALSE;
needle->bits.httpproxy = FALSE;
needle->bits.proxy = FALSE;
}
else if(network_scheme) {
result = url_set_conn_proxies(data, needle);
if(network_scheme &&
(!needle->via_peer || !needle->via_peer->unix_socket)) {
result = Curl_proxy_init_conn(data, needle);
if(result)
goto out;
/*************************************************************
* If the protocol is using SSL and HTTP proxy is used, we set
* the tunnel_proxy bit.
*************************************************************/
if((needle->given->flags & PROTOPT_SSL) && needle->bits.httpproxy)
needle->bits.tunnel_proxy = TRUE;
}
#endif /* CURL_DISABLE_PROXY */
@ -2692,15 +2162,6 @@ static CURLcode url_create_needle(struct Curl_easy *data,
Curl_peer_unlink(&needle->via_peer);
}
#ifndef CURL_DISABLE_PROXY
/*************************************************************
* If the "connect to" feature is used with an HTTP proxy,
* we set the tunnel_proxy bit.
*************************************************************/
if(needle->via_peer && needle->bits.httpproxy)
needle->bits.tunnel_proxy = TRUE;
#endif
/*************************************************************
* Setup internals depending on protocol. Needs to be done after
* we figured out what/if proxy to use.
@ -2743,6 +2204,118 @@ out:
return result;
}
static CURLcode url_set_data_origin_and_creds(struct Curl_easy *data)
{
CURLcode result = CURLE_OK;
CURLU *uh;
CURLUcode uc;
bool use_set_uh = (data->set.uh && !data->state.this_is_a_follow);
uint16_t port_override = data->state.allow_port ? data->set.use_port : 0;
uint32_t scope_id = 0;
/*************************************************************
* Check input data
*************************************************************/
if(!Curl_bufref_ptr(&data->state.url)) {
result = CURLE_URL_MALFORMAT;
goto out;
}
up_free(data); /* cleanup previous leftovers first */
/* parse the URL */
if(use_set_uh)
uh = data->state.uh = curl_url_dup(data->set.uh);
else
uh = data->state.uh = curl_url();
if(!uh) {
result = CURLE_OUT_OF_MEMORY;
goto out;
}
/* Calculate the *real* URL this transfer uses, applying defaults
* where information is missing. */
if(data->set.str[STRING_DEFAULT_PROTOCOL] &&
!Curl_is_absolute_url(Curl_bufref_ptr(&data->state.url), NULL, 0, TRUE)) {
char *url = curl_maprintf("%s://%s",
data->set.str[STRING_DEFAULT_PROTOCOL],
Curl_bufref_ptr(&data->state.url));
if(!url) {
result = CURLE_OUT_OF_MEMORY;
goto out;
}
Curl_bufref_set(&data->state.url, url, 0, curl_free);
}
if(!use_set_uh) {
char *newurl;
uc = curl_url_set(uh, CURLUPART_URL, Curl_bufref_ptr(&data->state.url),
(unsigned int)(CURLU_GUESS_SCHEME |
CURLU_NON_SUPPORT_SCHEME |
(data->set.disallow_username_in_url ?
CURLU_DISALLOW_USER : 0) |
(data->set.path_as_is ? CURLU_PATH_AS_IS : 0)));
if(uc) {
failf(data, "URL rejected: %s", curl_url_strerror(uc));
result = Curl_uc_to_curlcode(uc);
goto out;
}
/* after it was parsed, get the generated normalized version */
uc = curl_url_get(uh, CURLUPART_URL, &newurl, CURLU_GET_EMPTY);
if(uc) {
result = Curl_uc_to_curlcode(uc);
goto out;
}
Curl_bufref_set(&data->state.url, newurl, 0, curl_free);
}
#ifdef USE_IPV6
scope_id = data->set.scope_id;
#endif
/* `uh` is now as the connection should use it, probably. */
result = Curl_peer_from_url(uh, data, port_override, scope_id,
&data->state.up, &data->state.origin);
if(result)
goto out;
/* The origin might get changed when HSTS applies */
result = hsts_upgrade(data, uh, port_override, scope_id);
if(result)
goto out;
/* When the transfers initial_origin is not set, this is the initial
* request. Remember this starting point. */
if(!data->state.initial_origin)
Curl_peer_link(&data->state.initial_origin, data->state.origin);
uc = curl_url_get(uh, CURLUPART_PATH, &data->state.up.path, CURLU_URLENCODE);
if(uc) {
result = Curl_uc_to_curlcode(uc);
goto out;
}
uc = curl_url_get(uh, CURLUPART_QUERY, &data->state.up.query,
CURLU_GET_EMPTY);
if(uc && (uc != CURLUE_NO_QUERY)) {
result = CURLE_OUT_OF_MEMORY;
goto out;
}
uc = curl_url_get(uh, CURLUPART_OPTIONS, &data->state.up.options,
CURLU_URLDECODE);
if(uc && (uc != CURLUE_NO_OPTIONS)) {
result = Curl_uc_to_curlcode(uc);
goto out;
}
result = url_set_data_creds(data, uh);
if(result)
goto out;
out:
return result;
}
/**
* Find an existing connection for the transfer or create a new one.
* Returns
@ -2837,7 +2410,7 @@ static CURLcode url_find_or_create_conn(struct Curl_easy *data)
infof(data, "Reusing existing %s: connection%s with %s %s",
conn->given->name,
tls_upgraded ? " (upgraded to SSL)" : "",
conn->bits.proxy ? "proxy" : "host",
(conn->socks_proxy.peer || conn->http_proxy.peer) ? "proxy" : "host",
conn->socks_proxy.peer ? conn->socks_proxy.peer->user_hostname :
conn->http_proxy.peer ? conn->http_proxy.peer->user_hostname :
conn->origin->hostname);
@ -2936,7 +2509,7 @@ static CURLcode url_find_or_create_conn(struct Curl_easy *data)
#ifdef CURL_DISABLE_PROXY
0
#else
data->conn->bits.proxy
(data->conn->socks_proxy.peer || data->conn->http_proxy.peer)
#endif
;
@ -2953,21 +2526,33 @@ out:
CURLcode Curl_connect(struct Curl_easy *data, bool *pconnected)
{
CURLcode result;
struct connectdata *conn;
struct connectdata *conn = NULL;
*pconnected = FALSE;
/* Set the request to virgin state based on transfer settings */
Curl_req_hard_reset(&data->req, data);
/* Determine the origin of the transfer and what credentials to use */
result = url_set_data_origin_and_creds(data);
if(result)
goto out;
if(!data->state.origin) { /* just make really sure */
DEBUGASSERT(0);
result = CURLE_FAILED_INIT;
goto out;
}
/* Get or create a connection for the transfer. */
result = url_find_or_create_conn(data);
conn = data->conn;
if(result)
goto out;
if(!data->conn) { /* just make really sure */
DEBUGASSERT(0);
result = CURLE_FAILED_INIT;
goto out;
}
DEBUGASSERT(conn);
Curl_pgrsTime(data, TIMER_POSTQUEUE);
if(conn->bits.reuse) {
if(conn->attached_xfers > 1)

View file

@ -62,6 +62,7 @@
#include "hostip.h"
#include "hash.h"
#include "peer.h"
#include "proxy.h"
#include "splay.h"
#include "curlx/dynbuf.h"
#include "bufref.h"
@ -191,13 +192,7 @@ typedef enum {
struct ConnectBits {
BIT(connect_only);
#ifndef CURL_DISABLE_PROXY
BIT(httpproxy); /* if set, this transfer is done through an HTTP proxy */
BIT(socksproxy); /* if set, this transfer is done through a socks proxy */
BIT(tunnel_proxy); /* if CONNECT is used to "tunnel" through the proxy.
This is implicit when SSL-protocols are used through
proxies, but can also be enabled explicitly by
apps */
BIT(proxy); /* if set, this transfer is done through a proxy - any type */
BIT(origin_is_proxy); /* if set, the connection's origin is a proxy */
#endif
/* always modify bits.close with the connclose() and connkeep() macros! */
BIT(close); /* if set, we close the connection after this request */
@ -268,12 +263,6 @@ struct ip_quadruple {
((x)->transport == TRNSPRT_UDP) || \
((x)->transport == TRNSPRT_QUIC))
struct proxy_info {
struct Curl_peer *peer; /* proxy to this peer */
struct Curl_creds *creds; /* use these credentials, maybe NULL */
uint8_t proxytype; /* what kind of proxy that is in use */
};
/*
* The connectdata struct contains all fields and variables that should be
* unique for an entire connection.
@ -437,9 +426,6 @@ struct PureInfo {
session handle without disturbing information which is still alive, and
that might be reused, in the connection pool. */
struct ip_quadruple primary;
int conn_remote_port; /* this is the "remote port", which is the port
number of the used URL, independent of proxy or
not */
const char *conn_scheme;
uint32_t conn_protocol;
struct curl_certinfo certs; /* info about the certs. Asked for with
@ -601,6 +587,9 @@ struct UrlState {
Credentials from CURLOPT_* are only valid for this origin.
Always set once a transfer starts searching for connections. */
struct Curl_peer *initial_origin;
/* Current origin of the transfer, changes to origin of follow
* requests. */
struct Curl_peer *origin;
int os_errno; /* filled in with errno whenever an error occurs */
int requests; /* request counter: redirects + authentication retakes */

View file

@ -427,7 +427,7 @@ CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data,
curl_msnprintf(&HA1_hex[2 * i], 3, "%02x", digest[i]);
/* Generate our SPN */
spn = Curl_auth_build_spn(service, data->conn->origin->hostname, NULL);
spn = Curl_auth_build_spn(service, data->state.origin->hostname, NULL);
if(!spn)
return CURLE_OUT_OF_MEMORY;

View file

@ -133,7 +133,7 @@ CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data,
return CURLE_OUT_OF_MEMORY;
/* Generate our SPN */
spn = Curl_auth_build_spn(service, data->conn->origin->hostname, NULL);
spn = Curl_auth_build_spn(service, data->state.origin->hostname, NULL);
if(!spn) {
curlx_free(output_token);
return CURLE_OUT_OF_MEMORY;

View file

@ -139,7 +139,7 @@ bool Curl_auth_user_contains_domain(struct Curl_creds *creds)
*/
bool Curl_auth_allowed_to_host(struct Curl_easy *data)
{
return Curl_auth_allowed_to_origin(data, data->conn->origin);
return Curl_auth_allowed_to_origin(data, data->state.origin);
}
bool Curl_auth_allowed_to_origin(struct Curl_easy *data,

View file

@ -872,7 +872,7 @@ CURLcode Curl_conn_may_http3(struct Curl_easy *data,
return CURLE_URL_MALFORMAT;
}
#ifndef CURL_DISABLE_PROXY
if(conn->bits.socksproxy) {
if(conn->socks_proxy.peer) {
failf(data, "HTTP/3 is not supported over a SOCKS proxy");
return CURLE_URL_MALFORMAT;
}

View file

@ -74,6 +74,24 @@ class TestSocks:
else:
r.check_response(http_status=200)
# download via socks to https: proxy (no tunnel)
@pytest.mark.parametrize("sproto", ['socks4', 'socks5'])
@pytest.mark.parametrize("proto", Env.http_h1_h2_protos())
def test_40_02b_socks_https_proxy(self, env: Env, sproto, proto, danted: Dante, httpd):
if proto == 'h2' and not env.curl_uses_lib('nghttp2'):
pytest.skip('only supported with nghttp2')
curl = CurlClient(env=env, socks_args=[
f'--{sproto}', f'127.0.0.1:{danted.port}'
])
url = f'http://localhost:{env.http_port}/data.json'
xargs = curl.get_proxy_args(proto=proto, tunnel=False)
r = curl.http_download(urls=[url], alpn_proto=proto, with_stats=True,
extra_args=xargs)
r.check_response(http_status=200)
exp_http_version = '2' if proto == 'h2' else '1.1'
assert r.stats[0]['proxy_used'] == 1, f'{r}'
assert r.stats[0]['http_version'] == exp_http_version, f'{r}'
@pytest.mark.parametrize("sproto", ['socks4', 'socks5'])
@pytest.mark.parametrize("proto", Env.http_h1_h2_protos())
def test_40_03_dl_serial(self, env: Env, httpd, danted, proto, sproto):

View file

@ -22,7 +22,7 @@
*
***************************************************************************/
#include "unitcheck.h"
#include "noproxy.h"
#include "proxy.h"
static CURLcode test_unit1614(const char *arg)
{
@ -176,7 +176,7 @@ static CURLcode test_unit1614(const char *arg)
}
#endif
for(i = 0; list[i].a; i++) {
bool match = Curl_check_noproxy(list[i].a, list[i].n);
bool match = proxy_check_noproxy(list[i].a, list[i].n);
if(match != list[i].match) {
curl_mfprintf(stderr, "%s in %s should %smatch\n",
list[i].a, list[i].n,