mirror of
https://github.com/curl/curl.git
synced 2026-06-13 08:15:37 +03:00
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:
parent
c951368579
commit
73daec6620
30 changed files with 1083 additions and 1014 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 \
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
57
lib/http.c
57
lib/http.c
|
|
@ -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;
|
||||
|
|
|
|||
54
lib/http2.c
54
lib/http2.c
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
266
lib/noproxy.c
266
lib/noproxy.c
|
|
@ -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 */
|
||||
|
|
@ -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
673
lib/proxy.c
Normal 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 */
|
||||
|
|
@ -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 */
|
||||
|
|
@ -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
781
lib/url.c
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue