mirror of
https://github.com/curl/curl.git
synced 2026-06-18 19: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.
|
if, for example, http redirects are followed.
|
||||||
|
|
||||||
When an `easy_perform()` starts, the transfer's `data->state.initial_origin`
|
When an `easy_perform()` starts, the transfer's `data->state.initial_origin`
|
||||||
peer is cleared. When creating the connection, `conn->origin` is calculated
|
peer is cleared. When creating the connection, `data->state.origin` is
|
||||||
(e.g. who the request talks to). If `data->state.initial_origin` is not
|
calculated (e.g. who the request talks to). If `data->state.initial_origin`
|
||||||
set, the first `conn->origin` is linked there. Now `libcurl` knows where
|
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.
|
the transfer initially talked to on all possible subsequent requests.
|
||||||
|
|
||||||
Credential information from `CURLOPT_*` settings is only applicable for the
|
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.
|
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
|
## peers and connections
|
||||||
|
|
||||||
A network connection always goes *somewhere*. That *somewhere* is called
|
A network connection always goes *somewhere*. That *somewhere* is called
|
||||||
the `origin` of the connection (e.g. the source of responses/downloads).
|
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.
|
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
|
For most connections, the `origin` is connected to *directly*. It
|
||||||
can be directed to another peer, however.
|
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
|
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,
|
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.
|
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
|
The individual filters get passed a specific peer and do not need be concerned
|
||||||
|
|
|
||||||
|
|
@ -241,7 +241,6 @@ LIB_CFILES = \
|
||||||
multi_ev.c \
|
multi_ev.c \
|
||||||
multi_ntfy.c \
|
multi_ntfy.c \
|
||||||
netrc.c \
|
netrc.c \
|
||||||
noproxy.c \
|
|
||||||
openldap.c \
|
openldap.c \
|
||||||
parsedate.c \
|
parsedate.c \
|
||||||
peer.c \
|
peer.c \
|
||||||
|
|
@ -249,6 +248,7 @@ LIB_CFILES = \
|
||||||
pop3.c \
|
pop3.c \
|
||||||
progress.c \
|
progress.c \
|
||||||
protocol.c \
|
protocol.c \
|
||||||
|
proxy.c \
|
||||||
psl.c \
|
psl.c \
|
||||||
rand.c \
|
rand.c \
|
||||||
ratelimit.c \
|
ratelimit.c \
|
||||||
|
|
@ -375,13 +375,13 @@ LIB_HFILES = \
|
||||||
multi_ntfy.h \
|
multi_ntfy.h \
|
||||||
multiif.h \
|
multiif.h \
|
||||||
netrc.h \
|
netrc.h \
|
||||||
noproxy.h \
|
|
||||||
parsedate.h \
|
parsedate.h \
|
||||||
peer.h \
|
peer.h \
|
||||||
pingpong.h \
|
pingpong.h \
|
||||||
pop3.h \
|
pop3.h \
|
||||||
progress.h \
|
progress.h \
|
||||||
protocol.h \
|
protocol.h \
|
||||||
|
proxy.h \
|
||||||
psl.h \
|
psl.h \
|
||||||
rand.h \
|
rand.h \
|
||||||
ratelimit.h \
|
ratelimit.h \
|
||||||
|
|
|
||||||
|
|
@ -713,14 +713,15 @@ static CURLcode is_connected(struct Curl_cfilter *cf,
|
||||||
return CURLE_FAILED_INIT;
|
return CURLE_FAILED_INIT;
|
||||||
|
|
||||||
#ifndef CURL_DISABLE_PROXY
|
#ifndef CURL_DISABLE_PROXY
|
||||||
if(conn->bits.socksproxy)
|
if(conn->socks_proxy.peer)
|
||||||
proxy_peer = 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;
|
proxy_peer = conn->http_proxy.peer;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
viamsg[0] = 0;
|
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
|
#ifdef USE_UNIX_SOCKETS
|
||||||
if(peer->unix_socket)
|
if(peer->unix_socket)
|
||||||
curl_msnprintf(viamsg, sizeof(viamsg), " over unix://%s",
|
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)) {
|
if(cf->connected && (cf->sockindex == FIRSTSOCKET)) {
|
||||||
struct cf_socket_ctx *ctx = cf->ctx;
|
struct cf_socket_ctx *ctx = cf->ctx;
|
||||||
data->info.primary = ctx->ip;
|
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;
|
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)
|
static bool cf_is_ssl(struct Curl_cfilter *cf)
|
||||||
{
|
{
|
||||||
for(; cf; cf = cf->next) {
|
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);
|
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
|
* Fill `info` with information about the TLS instance securing the connection
|
||||||
* when available, otherwise e.g. when Curl_conn_is_ssl() is FALSE, return
|
* 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;
|
struct cf_setup_ctx *ctx = cf->ctx;
|
||||||
CURLcode result = CURLE_OK;
|
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`*/
|
/* Add a SOCKS proxy to go through `first_peer` to `second_peer`*/
|
||||||
struct Curl_peer *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;
|
second_peer = cf->conn->http_proxy.peer;
|
||||||
else
|
else
|
||||||
second_peer = Curl_conn_get_destination(cf->conn, cf->sockindex);
|
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;
|
struct cf_setup_ctx *ctx = cf->ctx;
|
||||||
CURLcode result = CURLE_OK;
|
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
|
#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)) {
|
!Curl_conn_is_ssl(cf->conn, cf->sockindex)) {
|
||||||
result = Curl_cf_ssl_proxy_insert_after(
|
result = Curl_cf_ssl_proxy_insert_after(
|
||||||
cf, data, cf->conn->http_proxy.peer);
|
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 */
|
#endif /* USE_SSL */
|
||||||
|
|
||||||
if(cf->conn->bits.tunnel_proxy) {
|
result = Curl_cf_http_proxy_insert_after(
|
||||||
struct Curl_peer *peer = cf->conn->http_proxy.peer;
|
cf, data, peer, tunnel_peer,
|
||||||
struct Curl_peer *tunnel_peer; /* where HTTP should tunnel to */
|
ctx->transport, cf->conn->http_proxy.proxytype);
|
||||||
tunnel_peer = Curl_conn_get_destination(cf->conn, cf->sockindex);
|
if(result) {
|
||||||
result = Curl_cf_http_proxy_insert_after(
|
CURL_TRC_CF(data, cf, "adding HTTP proxy tunnel filter failed -> %d",
|
||||||
cf, data, peer, tunnel_peer,
|
(int)result);
|
||||||
ctx->transport, cf->conn->http_proxy.proxytype);
|
return result;
|
||||||
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");
|
|
||||||
}
|
}
|
||||||
|
CURL_TRC_CF(data, cf, "added HTTP proxy tunnel filter");
|
||||||
ctx->state = CF_SETUP_CNNCT_HTTP_PROXY;
|
ctx->state = CF_SETUP_CNNCT_HTTP_PROXY;
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
|
@ -424,11 +424,11 @@ static CURLcode cf_setup_add_ip_happy(struct Curl_cfilter *cf,
|
||||||
return CURLE_FAILED_INIT;
|
return CURLE_FAILED_INIT;
|
||||||
|
|
||||||
#if !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP)
|
#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 =
|
first_transport =
|
||||||
Curl_http_proxy_transport(cf->conn->http_proxy.proxytype);
|
Curl_http_proxy_transport(cf->conn->http_proxy.proxytype);
|
||||||
tunnel_peer = Curl_conn_get_destination(cf->conn, cf->sockindex);
|
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");
|
failf(data, "HTTP/3 proxy not possible via SOCKS");
|
||||||
return CURLE_UNSUPPORTED_PROTOCOL;
|
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
|
/* Wanting QUIC with a HTTP tunneling filter, we now need to add
|
||||||
* the QUIC filter on top. Without tunneling, this has already
|
* the QUIC filter on top. Without tunneling, this has already
|
||||||
* happened in the Happy Eyeball filter. */
|
* happened in the Happy Eyeball filter. */
|
||||||
if(ctx->transport == TRNSPRT_QUIC && cf->conn->bits.httpproxy &&
|
if(ctx->transport == TRNSPRT_QUIC &&
|
||||||
cf->conn->bits.tunnel_proxy) {
|
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 *origin = Curl_conn_get_origin(cf->conn, cf->sockindex);
|
||||||
struct Curl_peer *peer =
|
struct Curl_peer *peer =
|
||||||
Curl_conn_get_destination(cf->conn, cf->sockindex);
|
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 &&
|
(ctx->ssl_mode != CURL_CF_SSL_DISABLE &&
|
||||||
cf->conn->scheme->flags & PROTOPT_SSL)) && /* we want SSL */
|
cf->conn->scheme->flags & PROTOPT_SSL)) && /* we want SSL */
|
||||||
!Curl_conn_is_ssl(cf->conn, cf->sockindex)) { /* it is missing */
|
!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) {
|
if(result) {
|
||||||
CURL_TRC_CF(data, cf, "adding SSL filter for origin failed -> %d",
|
CURL_TRC_CF(data, cf, "adding SSL filter for origin failed -> %d",
|
||||||
(int)result);
|
(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,
|
struct Curl_peer *Curl_conn_get_destination(struct connectdata *conn,
|
||||||
int sockindex)
|
int sockindex)
|
||||||
{
|
{
|
||||||
#ifndef CURL_DISABLE_PROXY
|
|
||||||
if(conn->http_proxy.peer && !conn->bits.tunnel_proxy)
|
|
||||||
return conn->http_proxy.peer;
|
|
||||||
#endif
|
|
||||||
return (sockindex == SECONDARYSOCKET) ?
|
return (sockindex == SECONDARYSOCKET) ?
|
||||||
(conn->via_peer2 ? conn->via_peer2 : conn->origin2) :
|
(conn->via_peer2 ? conn->via_peer2 : conn->origin2) :
|
||||||
(conn->via_peer ? conn->via_peer : conn->origin);
|
(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;
|
CURLcode result = CURLE_OK;
|
||||||
|
|
||||||
if(conn->bits.ipv6
|
if(conn->bits.ipv6 && !Curl_conn_is_tunneling(conn, FIRSTSOCKET)) {
|
||||||
#ifndef CURL_DISABLE_PROXY
|
|
||||||
&& !(conn->bits.tunnel_proxy || conn->bits.socksproxy)
|
|
||||||
#endif
|
|
||||||
) {
|
|
||||||
/* We cannot disable EPSV when doing IPv6, so this is instead a fail */
|
/* We cannot disable EPSV when doing IPv6, so this is instead a fail */
|
||||||
failf(data, "Failed EPSV attempt, exiting");
|
failf(data, "Failed EPSV attempt, exiting");
|
||||||
return CURLE_WEIRD_SERVER_REPLY;
|
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,
|
the effective control connection address is the proxy address,
|
||||||
not the ftp host. */
|
not the ftp host. */
|
||||||
#ifndef CURL_DISABLE_PROXY
|
#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);
|
*newhostp = curlx_strdup(conn->origin->hostname);
|
||||||
else
|
else
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
57
lib/http.c
57
lib/http.c
|
|
@ -154,7 +154,7 @@ char *Curl_checkProxyheaders(struct Curl_easy *data,
|
||||||
{
|
{
|
||||||
struct curl_slist *head;
|
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;
|
data->set.proxyheaders : data->set.headers;
|
||||||
head; head = head->next) {
|
head; head = head->next) {
|
||||||
if(curl_strnequal(head->data, thisheader, thislen) &&
|
if(curl_strnequal(head->data, thisheader, thislen) &&
|
||||||
|
|
@ -783,7 +783,7 @@ CURLcode Curl_http_output_auth(struct Curl_easy *data,
|
||||||
|
|
||||||
if(
|
if(
|
||||||
#ifndef CURL_DISABLE_PROXY
|
#ifndef CURL_DISABLE_PROXY
|
||||||
(!conn->bits.httpproxy || !conn->http_proxy.creds) &&
|
(!conn->http_proxy.peer || !conn->http_proxy.creds) &&
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_SPNEGO
|
#ifdef USE_SPNEGO
|
||||||
!(authhost->want & CURLAUTH_NEGOTIATE) &&
|
!(authhost->want & CURLAUTH_NEGOTIATE) &&
|
||||||
|
|
@ -820,7 +820,7 @@ CURLcode Curl_http_output_auth(struct Curl_easy *data,
|
||||||
|
|
||||||
#ifndef CURL_DISABLE_PROXY
|
#ifndef CURL_DISABLE_PROXY
|
||||||
/* Send proxy authentication header if needed */
|
/* 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,
|
result = output_auth_headers(data, conn, authproxy, request,
|
||||||
path_and_query, TRUE);
|
path_and_query, TRUE);
|
||||||
if(result)
|
if(result)
|
||||||
|
|
@ -1736,8 +1736,7 @@ CURLcode Curl_add_custom_headers(struct Curl_easy *data,
|
||||||
if(is_connect)
|
if(is_connect)
|
||||||
proxy = HEADER_CONNECT;
|
proxy = HEADER_CONNECT;
|
||||||
else
|
else
|
||||||
proxy = data->conn->bits.httpproxy && !data->conn->bits.tunnel_proxy ?
|
proxy = data->conn->bits.origin_is_proxy ? HEADER_PROXY : HEADER_SERVER;
|
||||||
HEADER_PROXY : HEADER_SERVER;
|
|
||||||
|
|
||||||
switch(proxy) {
|
switch(proxy) {
|
||||||
case HEADER_SERVER:
|
case HEADER_SERVER:
|
||||||
|
|
@ -1998,8 +1997,9 @@ static CURLcode http_set_aptr_host(struct Curl_easy *data)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
ptr = Curl_checkheaders(data, STRCONST("Host"));
|
ptr = Curl_checkheaders(data, STRCONST("Host"));
|
||||||
if(ptr && (!data->state.this_is_a_follow ||
|
if(ptr &&
|
||||||
Curl_peer_equal(data->state.initial_origin, conn->origin))) {
|
(!data->state.this_is_a_follow ||
|
||||||
|
Curl_peer_equal(data->state.initial_origin, data->state.origin))) {
|
||||||
#ifndef CURL_DISABLE_COOKIES
|
#ifndef CURL_DISABLE_COOKIES
|
||||||
/* If we have a given custom Host: header, we extract the hostname in
|
/* 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
|
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 {
|
else {
|
||||||
/* Use the hostname as present in the URL if it was IPv6. */
|
/* Use the hostname as present in the URL if it was IPv6. */
|
||||||
char *host = (conn->origin->user_hostname[0] == '[') ?
|
char *host = (data->state.origin->user_hostname[0] == '[') ?
|
||||||
conn->origin->user_hostname : conn->origin->hostname;
|
data->state.origin->user_hostname : data->state.origin->hostname;
|
||||||
|
|
||||||
if(((conn->given->protocol & (CURLPROTO_HTTPS | CURLPROTO_WSS)) &&
|
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->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
|
/* if(HTTPS on port 443) OR (HTTP on port 80) then do not include
|
||||||
the port number in the host string */
|
the port number in the host string */
|
||||||
aptr->host = curl_maprintf("Host: %s\r\n", host);
|
aptr->host = curl_maprintf("Host: %s\r\n", host);
|
||||||
else
|
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)
|
if(!aptr->host)
|
||||||
/* without Host: we cannot make a nice request */
|
/* without Host: we cannot make a nice request */
|
||||||
|
|
@ -2082,7 +2083,7 @@ static CURLcode http_target(struct Curl_easy *data,
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef CURL_DISABLE_PROXY
|
#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 */
|
/* 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
|
/* 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)
|
if(!h)
|
||||||
return CURLE_OUT_OF_MEMORY;
|
return CURLE_OUT_OF_MEMORY;
|
||||||
|
|
||||||
if(conn->origin->user_hostname != conn->origin->hostname) {
|
if(data->state.origin->user_hostname != data->state.origin->hostname) {
|
||||||
uc = curl_url_set(h, CURLUPART_HOST, conn->origin->hostname, 0);
|
uc = curl_url_set(h, CURLUPART_HOST, data->state.origin->hostname, 0);
|
||||||
if(uc) {
|
if(uc) {
|
||||||
curl_url_cleanup(h);
|
curl_url_cleanup(h);
|
||||||
return CURLE_OUT_OF_MEMORY;
|
return CURLE_OUT_OF_MEMORY;
|
||||||
|
|
@ -2541,7 +2542,7 @@ static CURLcode http_cookies(struct Curl_easy *data,
|
||||||
if(data->cookies && data->state.cookie_engine) {
|
if(data->cookies && data->state.cookie_engine) {
|
||||||
bool okay;
|
bool okay;
|
||||||
const char *host = data->req.cookiehost ?
|
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);
|
Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
|
||||||
result = Curl_cookie_getlist(data, data->conn, &okay, host, &list);
|
result = Curl_cookie_getlist(data, data->conn, &okay, host, &list);
|
||||||
if(!result && okay) {
|
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);
|
alpn = Curl_conn_get_alpn_negotiated(data, conn);
|
||||||
if(alpn && !strcmp("h3", alpn)) {
|
if(alpn && !strcmp("h3", alpn)) {
|
||||||
#ifndef CURL_DISABLE_PROXY
|
#ifndef CURL_DISABLE_PROXY
|
||||||
if((Curl_conn_http_version(data, conn) == 30) || !conn->bits.proxy ||
|
if(!conn->bits.origin_is_proxy)
|
||||||
conn->bits.tunnel_proxy)
|
|
||||||
#endif
|
#endif
|
||||||
DEBUGASSERT(Curl_conn_http_version(data, conn) == 30);
|
DEBUGASSERT(Curl_conn_http_version(data, conn) == 30);
|
||||||
info_version = "HTTP/3";
|
info_version = "HTTP/3";
|
||||||
|
|
@ -2737,7 +2737,7 @@ static CURLcode http_check_new_conn(struct Curl_easy *data)
|
||||||
else if(alpn && !strcmp("h2", alpn)) {
|
else if(alpn && !strcmp("h2", alpn)) {
|
||||||
#ifndef CURL_DISABLE_PROXY
|
#ifndef CURL_DISABLE_PROXY
|
||||||
if((Curl_conn_http_version(data, conn) != 20) &&
|
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);
|
result = Curl_http2_switch(data);
|
||||||
if(result)
|
if(result)
|
||||||
return result;
|
return result;
|
||||||
|
|
@ -2946,8 +2946,7 @@ static CURLcode http_add_hd(struct Curl_easy *data,
|
||||||
|
|
||||||
#ifndef CURL_DISABLE_PROXY
|
#ifndef CURL_DISABLE_PROXY
|
||||||
case H1_HD_PROXY_CONNECTION:
|
case H1_HD_PROXY_CONNECTION:
|
||||||
if(conn->bits.httpproxy &&
|
if(conn->bits.origin_is_proxy &&
|
||||||
!conn->bits.tunnel_proxy &&
|
|
||||||
!Curl_checkheaders(data, STRCONST("Proxy-Connection")) &&
|
!Curl_checkheaders(data, STRCONST("Proxy-Connection")) &&
|
||||||
!Curl_checkProxyheaders(data, data->conn, STRCONST("Proxy-Connection")))
|
!Curl_checkProxyheaders(data, data->conn, STRCONST("Proxy-Connection")))
|
||||||
result = curlx_dyn_add(req, "Proxy-Connection: Keep-Alive\r\n");
|
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
|
#ifndef CURL_DISABLE_ALTSVC
|
||||||
const char *v;
|
const char *v;
|
||||||
struct connectdata *conn = data->conn;
|
|
||||||
v = (data->asi &&
|
v = (data->asi &&
|
||||||
(Curl_xfer_is_secure(data) ||
|
(Curl_xfer_is_secure(data) ||
|
||||||
#ifdef DEBUGBUILD
|
#ifdef DEBUGBUILD
|
||||||
|
|
@ -3205,8 +3203,9 @@ static CURLcode http_header_a(struct Curl_easy *data,
|
||||||
struct SingleRequest *k = &data->req;
|
struct SingleRequest *k = &data->req;
|
||||||
enum alpnid id = (k->httpversion == 30) ? ALPN_h3 :
|
enum alpnid id = (k->httpversion == 30) ? ALPN_h3 :
|
||||||
(k->httpversion == 20) ? ALPN_h2 : ALPN_h1;
|
(k->httpversion == 20) ? ALPN_h2 : ALPN_h1;
|
||||||
return Curl_altsvc_parse(data, data->asi, v, id, conn->origin->hostname,
|
return Curl_altsvc_parse(
|
||||||
curlx_uitous((unsigned int)conn->origin->port));
|
data, data->asi, v, id, data->state.origin->hostname,
|
||||||
|
curlx_uitous((unsigned int)data->state.origin->port));
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
(void)data;
|
(void)data;
|
||||||
|
|
@ -3424,7 +3423,7 @@ static CURLcode http_header_p(struct Curl_easy *data,
|
||||||
const char *v = HD_VAL(hd, hdlen, "Proxy-Connection:");
|
const char *v = HD_VAL(hd, hdlen, "Proxy-Connection:");
|
||||||
if(v) {
|
if(v) {
|
||||||
struct connectdata *conn = data->conn;
|
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")) {
|
HD_IS_AND_SAYS(hd, hdlen, "Proxy-Connection:", "keep-alive")) {
|
||||||
/*
|
/*
|
||||||
* When an HTTP/1.0 reply comes when using a proxy, the
|
* 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 */
|
connkeep(conn, "Proxy-Connection keep-alive"); /* do not close */
|
||||||
infof(data, "HTTP/1.0 proxy connection set to keep alive");
|
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")) {
|
HD_IS_AND_SAYS(hd, hdlen, "Proxy-Connection:", "close")) {
|
||||||
/*
|
/*
|
||||||
* We get an HTTP/1.1 response from a proxy and it says it will
|
* 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
|
/* If there is a custom-set Host: name, use it here, or else use
|
||||||
* real peer hostname. */
|
* real peer hostname. */
|
||||||
const char *host = data->req.cookiehost ?
|
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);
|
const bool secure_context = Curl_secure_context(conn, host);
|
||||||
CURLcode result;
|
CURLcode result;
|
||||||
Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
|
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;
|
) ? HD_VAL(hd, hdlen, "Strict-Transport-Security:") : NULL;
|
||||||
if(v) {
|
if(v) {
|
||||||
CURLcode result =
|
CURLcode result = Curl_hsts_parse(
|
||||||
Curl_hsts_parse(data->hsts, conn->origin->hostname, v);
|
data->hsts, data->state.origin->hostname, v);
|
||||||
if(result) {
|
if(result) {
|
||||||
if(result == CURLE_OUT_OF_MEMORY)
|
if(result == CURLE_OUT_OF_MEMORY)
|
||||||
return result;
|
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 Curl_cfilter *cf = userp;
|
||||||
struct cf_h2_ctx *ctx = cf->ctx;
|
struct cf_h2_ctx *ctx = cf->ctx;
|
||||||
struct h2_stream_ctx *stream;
|
struct h2_stream_ctx *stream;
|
||||||
struct Curl_easy *data_s;
|
struct Curl_easy *data;
|
||||||
int32_t stream_id = frame->hd.stream_id;
|
int32_t stream_id = frame->hd.stream_id;
|
||||||
CURLcode result;
|
CURLcode result;
|
||||||
(void)flags;
|
(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 */
|
DEBUGASSERT(stream_id); /* should never be a zero stream ID here */
|
||||||
|
|
||||||
/* get the stream from the hash based on Stream ID */
|
/* get the stream from the hash based on Stream ID */
|
||||||
data_s = nghttp2_session_get_stream_user_data(session, stream_id);
|
data = nghttp2_session_get_stream_user_data(session, stream_id);
|
||||||
if(!GOOD_EASY_HANDLE(data_s))
|
if(!GOOD_EASY_HANDLE(data))
|
||||||
/* Receiving a Stream ID not in the hash should not happen, this is an
|
/* Receiving a Stream ID not in the hash should not happen, this is an
|
||||||
internal error more than anything else! */
|
internal error more than anything else! */
|
||||||
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||||
|
|
||||||
stream = H2_STREAM_CTX(ctx, data_s);
|
stream = H2_STREAM_CTX(ctx, data);
|
||||||
if(!stream) {
|
if(!stream) {
|
||||||
failf(data_s, "Internal NULL stream");
|
failf(data, "Internal NULL stream");
|
||||||
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
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)) {
|
!strncmp(HTTP_PSEUDO_AUTHORITY, (const char *)name, namelen)) {
|
||||||
/* pseudo headers are lower case */
|
/* pseudo headers are lower case */
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
char *check = curl_maprintf("%s:%d", cf->conn->origin->hostname,
|
char *check = curl_maprintf("%s:%d", data->state.origin->hostname,
|
||||||
cf->conn->origin->port);
|
data->state.origin->port);
|
||||||
if(!check)
|
if(!check)
|
||||||
/* no memory */
|
/* no memory */
|
||||||
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||||
if(!curl_strequal(check, (const char *)value) &&
|
if(!curl_strequal(check, (const char *)value) &&
|
||||||
((cf->conn->origin->port != cf->conn->given->defport) ||
|
((data->state.origin->port != cf->conn->given->defport) ||
|
||||||
!curl_strequal(cf->conn->origin->hostname, (const char *)value))) {
|
!curl_strequal(data->state.origin->hostname, (const char *)value))) {
|
||||||
/* This is push is not for the same authority that was asked for in
|
/* 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
|
* 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
|
* 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;
|
char **headp;
|
||||||
if(stream->push_headers_alloc > 1000) {
|
if(stream->push_headers_alloc > 1000) {
|
||||||
/* this is beyond crazy many headers, bail out */
|
/* 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);
|
free_push_headers(stream);
|
||||||
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
@ -1491,13 +1491,13 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
|
||||||
|
|
||||||
if(stream->bodystarted) {
|
if(stream->bodystarted) {
|
||||||
/* This is a trailer */
|
/* 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);
|
stream->id, (int)namelen, name, (int)valuelen, value);
|
||||||
result = Curl_dynhds_add(&stream->resp_trailers,
|
result = Curl_dynhds_add(&stream->resp_trailers,
|
||||||
(const char *)name, namelen,
|
(const char *)name, namelen,
|
||||||
(const char *)value, valuelen);
|
(const char *)value, valuelen);
|
||||||
if(result) {
|
if(result) {
|
||||||
cf_h2_header_error(cf, data_s, stream, result);
|
cf_h2_header_error(cf, data, stream, result);
|
||||||
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
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,
|
result = Curl_http_decode_status(&stream->status_code,
|
||||||
(const char *)value, valuelen);
|
(const char *)value, valuelen);
|
||||||
if(result) {
|
if(result) {
|
||||||
cf_h2_header_error(cf, data_s, stream, result);
|
cf_h2_header_error(cf, data, stream, result);
|
||||||
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||||
}
|
}
|
||||||
hlen = curl_msnprintf(buffer, sizeof(buffer), HTTP_PSEUDO_STATUS ":%d\r",
|
hlen = curl_msnprintf(buffer, sizeof(buffer), HTTP_PSEUDO_STATUS ":%d\r",
|
||||||
stream->status_code);
|
stream->status_code);
|
||||||
result = Curl_headers_push(data_s, buffer, hlen, CURLH_PSEUDO);
|
result = Curl_headers_push(data, buffer, hlen, CURLH_PSEUDO);
|
||||||
if(result) {
|
if(result) {
|
||||||
cf_h2_header_error(cf, data_s, stream, result);
|
cf_h2_header_error(cf, data, stream, result);
|
||||||
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||||
}
|
}
|
||||||
curlx_dyn_reset(&ctx->scratch);
|
curlx_dyn_reset(&ctx->scratch);
|
||||||
|
|
@ -1529,17 +1529,17 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
|
||||||
if(!result)
|
if(!result)
|
||||||
result = curlx_dyn_addn(&ctx->scratch, STRCONST(" \r\n"));
|
result = curlx_dyn_addn(&ctx->scratch, STRCONST(" \r\n"));
|
||||||
if(!result)
|
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);
|
curlx_dyn_len(&ctx->scratch), FALSE);
|
||||||
if(result) {
|
if(result) {
|
||||||
cf_h2_header_error(cf, data_s, stream, result);
|
cf_h2_header_error(cf, data, stream, result);
|
||||||
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||||
}
|
}
|
||||||
/* if we receive data for another handle, wake that up */
|
/* if we receive data for another handle, wake that up */
|
||||||
if(CF_DATA_CURRENT(cf) != data_s)
|
if(CF_DATA_CURRENT(cf) != data)
|
||||||
Curl_multi_mark_dirty(data_s);
|
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);
|
stream->id, stream->status_code);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -1556,17 +1556,17 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
|
||||||
if(!result)
|
if(!result)
|
||||||
result = curlx_dyn_addn(&ctx->scratch, STRCONST("\r\n"));
|
result = curlx_dyn_addn(&ctx->scratch, STRCONST("\r\n"));
|
||||||
if(!result)
|
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);
|
curlx_dyn_len(&ctx->scratch), FALSE);
|
||||||
if(result) {
|
if(result) {
|
||||||
cf_h2_header_error(cf, data_s, stream, result);
|
cf_h2_header_error(cf, data, stream, result);
|
||||||
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||||
}
|
}
|
||||||
/* if we receive data for another handle, wake that up */
|
/* if we receive data for another handle, wake that up */
|
||||||
if(CF_DATA_CURRENT(cf) != data_s)
|
if(CF_DATA_CURRENT(cf) != data)
|
||||||
Curl_multi_mark_dirty(data_s);
|
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);
|
stream->id, (int)namelen, name, (int)valuelen, value);
|
||||||
|
|
||||||
return 0; /* 0 is successful */
|
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.wanted & CURL_HTTP_V2x) &&
|
||||||
data->state.http_neg.h2_prior_knowledge) {
|
data->state.http_neg.h2_prior_knowledge) {
|
||||||
#ifndef CURL_DISABLE_PROXY
|
#ifndef CURL_DISABLE_PROXY
|
||||||
if(data->conn->bits.httpproxy && !data->conn->bits.tunnel_proxy) {
|
if(data->conn->bits.origin_is_proxy) {
|
||||||
/* We do not support HTTP/2 proxies yet. Also it is debatable
|
|
||||||
whether or not this setting should apply to HTTP/2 proxies. */
|
|
||||||
infof(data, "Ignoring HTTP/2 prior knowledge due to proxy");
|
infof(data, "Ignoring HTTP/2 prior knowledge due to proxy");
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1165,12 +1165,11 @@ fail:
|
||||||
CURLcode Curl_output_aws_sigv4(struct Curl_easy *data)
|
CURLcode Curl_output_aws_sigv4(struct Curl_easy *data)
|
||||||
{
|
{
|
||||||
CURLcode result = CURLE_OUT_OF_MEMORY;
|
CURLcode result = CURLE_OUT_OF_MEMORY;
|
||||||
struct connectdata *conn = data->conn;
|
|
||||||
struct Curl_str provider0 = { NULL, 0 };
|
struct Curl_str provider0 = { NULL, 0 };
|
||||||
struct Curl_str provider1 = { NULL, 0 };
|
struct Curl_str provider1 = { NULL, 0 };
|
||||||
struct Curl_str region = { NULL, 0 };
|
struct Curl_str region = { NULL, 0 };
|
||||||
struct Curl_str service = { 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 timestamp[TIMESTAMP_SIZE];
|
||||||
char date[9];
|
char date[9];
|
||||||
struct dynbuf canonical_headers;
|
struct dynbuf canonical_headers;
|
||||||
|
|
|
||||||
|
|
@ -117,9 +117,9 @@ CURLcode Curl_output_digest(struct Curl_easy *data,
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
DEBUGASSERT(data->conn->origin);
|
DEBUGASSERT(data->state.origin);
|
||||||
digest = &data->state.digest;
|
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;
|
allocuserpwd = &data->req.hd_auth;
|
||||||
creds = data->state.creds;
|
creds = data->state.creds;
|
||||||
authp = &data->state.authhost;
|
authp = &data->state.authhost;
|
||||||
|
|
|
||||||
|
|
@ -73,7 +73,7 @@ CURLcode Curl_input_negotiate(struct Curl_easy *data, struct connectdata *conn,
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
creds = data->state.creds;
|
creds = data->state.creds;
|
||||||
host = conn->origin->hostname;
|
host = data->state.origin->hostname;
|
||||||
state = conn->http_negotiate_state;
|
state = conn->http_negotiate_state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -149,7 +149,7 @@ CURLcode Curl_output_ntlm(struct Curl_easy *data, bool proxy)
|
||||||
else {
|
else {
|
||||||
allocuserpwd = &data->req.hd_auth;
|
allocuserpwd = &data->req.hd_auth;
|
||||||
creds = data->state.creds;
|
creds = data->state.creds;
|
||||||
hostname = conn->origin->hostname;
|
hostname = data->state.origin->hostname;
|
||||||
state = &conn->http_ntlm_state;
|
state = &conn->http_ntlm_state;
|
||||||
authp = &data->state.authhost;
|
authp = &data->state.authhost;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -55,8 +55,7 @@ static CURLcode dynhds_add_custom(struct Curl_easy *data,
|
||||||
else if(is_connect && is_udp)
|
else if(is_connect && is_udp)
|
||||||
proxy = HEADER_CONNECT_UDP;
|
proxy = HEADER_CONNECT_UDP;
|
||||||
else
|
else
|
||||||
proxy = (conn->bits.httpproxy && !conn->bits.tunnel_proxy) ?
|
proxy = conn->bits.origin_is_proxy ? HEADER_PROXY : HEADER_SERVER;
|
||||||
HEADER_PROXY : HEADER_SERVER;
|
|
||||||
|
|
||||||
switch(proxy) {
|
switch(proxy) {
|
||||||
case HEADER_SERVER:
|
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;
|
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);
|
uint8_t Curl_http_proxy_transport(uint8_t proxytype);
|
||||||
|
|
||||||
|
#endif /* !CURL_DISABLE_PROXY && !CURL_DISABLE_HTTP */
|
||||||
|
|
||||||
#endif /* HEADER_CURL_HTTP_PROXY_H */
|
#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);
|
DEBUGASSERT(pp.scheme);
|
||||||
|
|
||||||
if(IS_HTTPS_PROXY(proxytype) &&
|
if(CURL_PROXY_IS_HTTPS(proxytype) &&
|
||||||
!Curl_ssl_supports(data, SSLSUPP_HTTPS_PROXY)) {
|
!Curl_ssl_supports(data, SSLSUPP_HTTPS_PROXY)) {
|
||||||
failf(data, "Unsupported proxy \'%s\', libcurl is built without the "
|
failf(data, "Unsupported proxy \'%s\', libcurl is built without the "
|
||||||
"HTTPS-proxy support.", url);
|
"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
|
#ifndef HEADER_CURL_PROXY_H
|
||||||
#define HEADER_CURL_NOPROXY_H
|
#define HEADER_CURL_PROXY_H
|
||||||
/***************************************************************************
|
/***************************************************************************
|
||||||
* _ _ ____ _
|
* _ _ ____ _
|
||||||
* Project ___| | | | _ \| |
|
* Project ___| | | | _ \| |
|
||||||
|
|
@ -26,7 +26,34 @@
|
||||||
#include "curl_setup.h"
|
#include "curl_setup.h"
|
||||||
|
|
||||||
#ifndef CURL_DISABLE_PROXY
|
#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
|
/* initial transfer request coming up, forget the initial origin
|
||||||
* from a previous perform() on this handle. */
|
* from a previous perform() on this handle. */
|
||||||
Curl_peer_unlink(&data->state.initial_origin);
|
Curl_peer_unlink(&data->state.initial_origin);
|
||||||
|
Curl_peer_unlink(&data->state.origin);
|
||||||
data->state.requests = 0;
|
data->state.requests = 0;
|
||||||
data->state.followlocation = 0; /* reset the location-follow counter */
|
data->state.followlocation = 0; /* reset the location-follow counter */
|
||||||
data->state.this_is_a_follow = FALSE; /* reset this */
|
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 "urlapi-int.h"
|
||||||
#include "system_win32.h"
|
#include "system_win32.h"
|
||||||
#include "hsts.h"
|
#include "hsts.h"
|
||||||
#include "noproxy.h"
|
#include "proxy.h"
|
||||||
#include "cfilters.h"
|
#include "cfilters.h"
|
||||||
#include "idn.h"
|
#include "idn.h"
|
||||||
#include "http_proxy.h"
|
#include "http_proxy.h"
|
||||||
|
|
@ -245,6 +245,7 @@ CURLcode Curl_close(struct Curl_easy **datap)
|
||||||
|
|
||||||
/* Close down all open SSL info and sessions */
|
/* Close down all open SSL info and sessions */
|
||||||
Curl_ssl_close_all(data);
|
Curl_ssl_close_all(data);
|
||||||
|
Curl_peer_unlink(&data->state.origin);
|
||||||
Curl_peer_unlink(&data->state.initial_origin);
|
Curl_peer_unlink(&data->state.initial_origin);
|
||||||
Curl_ssl_free_certinfo(data);
|
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,
|
static bool url_match_proxy_use(struct connectdata *conn,
|
||||||
struct url_conn_match *m)
|
struct url_conn_match *m)
|
||||||
{
|
{
|
||||||
if(m->needle->bits.httpproxy != conn->bits.httpproxy ||
|
if(m->needle->bits.origin_is_proxy != conn->bits.origin_is_proxy)
|
||||||
m->needle->bits.socksproxy != conn->bits.socksproxy)
|
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
if(m->needle->bits.socksproxy &&
|
if(!proxy_info_matches(&m->needle->socks_proxy, &conn->socks_proxy))
|
||||||
!proxy_info_matches(&m->needle->socks_proxy, &conn->socks_proxy))
|
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
if(m->needle->bits.httpproxy) {
|
if(!proxy_info_matches(&m->needle->http_proxy, &conn->http_proxy))
|
||||||
if(m->needle->bits.tunnel_proxy != conn->bits.tunnel_proxy)
|
return FALSE;
|
||||||
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;
|
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;
|
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))
|
if(!Curl_peer_same_destination(m->needle->via_peer, conn->via_peer))
|
||||||
return FALSE;
|
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) {
|
if(m->needle->origin->scheme != conn->origin->scheme) {
|
||||||
/* `needle` and `conn` not having the same scheme.
|
/* `needle` and `conn` not having the same scheme.
|
||||||
* This is allowed for the same family *if* conn is using TLS.
|
* 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. */
|
/* Store current time to give a baseline to keepalive connection times. */
|
||||||
conn->keepalive = conn->created;
|
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
|
#ifndef CURL_DISABLE_FTP
|
||||||
conn->bits.ftp_use_epsv = data->set.ftp_use_epsv;
|
conn->bits.ftp_use_epsv = data->set.ftp_use_epsv;
|
||||||
conn->bits.ftp_use_eprt = data->set.ftp_use_eprt;
|
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
|
#ifndef CURL_DISABLE_HSTS
|
||||||
static CURLcode hsts_upgrade(struct Curl_easy *data,
|
static CURLcode hsts_upgrade(struct Curl_easy *data,
|
||||||
struct connectdata *conn,
|
|
||||||
CURLU *uh,
|
CURLU *uh,
|
||||||
uint16_t port_override,
|
uint16_t port_override,
|
||||||
uint32_t scope_id)
|
uint32_t scope_id)
|
||||||
{
|
{
|
||||||
/* HSTS upgrade */
|
/* HSTS upgrade */
|
||||||
if(data->hsts && (conn->origin->scheme == &Curl_scheme_http) &&
|
if(data->hsts && (data->state.origin->scheme == &Curl_scheme_http) &&
|
||||||
Curl_hsts_applies(data->hsts, conn->origin)) {
|
Curl_hsts_applies(data->hsts, data->state.origin)) {
|
||||||
char *url;
|
char *url;
|
||||||
CURLUcode uc;
|
CURLUcode uc;
|
||||||
CURLcode result;
|
CURLcode result;
|
||||||
|
|
@ -1430,7 +1391,7 @@ static CURLcode hsts_upgrade(struct Curl_easy *data,
|
||||||
Curl_bufref_set(&data->state.url, url, 0, curl_free);
|
Curl_bufref_set(&data->state.url, url, 0, curl_free);
|
||||||
|
|
||||||
result = Curl_peer_from_url(uh, data, port_override, scope_id,
|
result = Curl_peer_from_url(uh, data, port_override, scope_id,
|
||||||
&data->state.up, &conn->origin);
|
&data->state.up, &data->state.origin);
|
||||||
if(result)
|
if(result)
|
||||||
return result;
|
return result;
|
||||||
infof(data, "Switched from HTTP to HTTPS due to HSTS => %s", url);
|
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;
|
return CURLE_OK;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
#define hsts_upgrade(x, y, z, a, b) CURLE_OK
|
#define hsts_upgrade(x, y, z, a) CURLE_OK
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef CURL_DISABLE_NETRC
|
#ifndef CURL_DISABLE_NETRC
|
||||||
|
|
@ -1460,7 +1421,6 @@ static bool str_has_ctrl(const char *input)
|
||||||
* option or a .netrc file, if applicable.
|
* option or a .netrc file, if applicable.
|
||||||
*/
|
*/
|
||||||
static CURLcode url_set_data_creds_netrc(struct Curl_easy *data,
|
static CURLcode url_set_data_creds_netrc(struct Curl_easy *data,
|
||||||
struct connectdata *conn,
|
|
||||||
struct Curl_creds **pcreds)
|
struct Curl_creds **pcreds)
|
||||||
{
|
{
|
||||||
struct Curl_creds *ncreds_out = NULL;
|
struct Curl_creds *ncreds_out = NULL;
|
||||||
|
|
@ -1500,7 +1460,7 @@ static CURLcode url_set_data_creds_netrc(struct Curl_easy *data,
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
ret = Curl_netrc_scan(data, &data->state.netrc,
|
ret = Curl_netrc_scan(data, &data->state.netrc,
|
||||||
conn->origin->hostname,
|
data->state.origin->hostname,
|
||||||
Curl_creds_user(ncreds_in),
|
Curl_creds_user(ncreds_in),
|
||||||
data->set.str[STRING_NETRC_FILE],
|
data->set.str[STRING_NETRC_FILE],
|
||||||
&ncreds_out);
|
&ncreds_out);
|
||||||
|
|
@ -1512,7 +1472,7 @@ static CURLcode url_set_data_creds_netrc(struct Curl_easy *data,
|
||||||
else if(ret && ((ret == NETRC_NO_MATCH) ||
|
else if(ret && ((ret == NETRC_NO_MATCH) ||
|
||||||
(data->set.use_netrc == CURL_NETRC_OPTIONAL))) {
|
(data->set.use_netrc == CURL_NETRC_OPTIONAL))) {
|
||||||
infof(data, "Could not find host %s in the %s file; using defaults",
|
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] ?
|
||||||
data->set.str[STRING_NETRC_FILE] : ".netrc"));
|
data->set.str[STRING_NETRC_FILE] : ".netrc"));
|
||||||
}
|
}
|
||||||
|
|
@ -1523,7 +1483,7 @@ static CURLcode url_set_data_creds_netrc(struct Curl_easy *data,
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
else if(ncreds_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
|
/* if the protocol cannot handle control codes in credentials, make
|
||||||
sure there are none */
|
sure there are none */
|
||||||
if(str_has_ctrl(ncreds_out->user) ||
|
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",
|
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,
|
result = Curl_creds_merge(ncreds_out->user, ncreds_out->passwd,
|
||||||
*pcreds, CREDS_NETRC, pcreds);
|
*pcreds, CREDS_NETRC, pcreds);
|
||||||
if(result)
|
if(result)
|
||||||
|
|
@ -1553,15 +1513,17 @@ static CURLcode url_set_data_creds_netrc(struct Curl_easy *data,
|
||||||
DEBUGASSERT(0);
|
DEBUGASSERT(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CURLVERBOSE
|
||||||
|
Curl_creds_trace(data, data->state.creds, "transfer credentials");
|
||||||
|
#endif
|
||||||
|
|
||||||
out:
|
out:
|
||||||
Curl_creds_unlink(&ncreds_out);
|
Curl_creds_unlink(&ncreds_out);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
#endif /* CURL_DISABLE_NETRC */
|
#endif /* CURL_DISABLE_NETRC */
|
||||||
|
|
||||||
static CURLcode url_set_data_creds(struct Curl_easy *data,
|
static CURLcode url_set_data_creds(struct Curl_easy *data, CURLU *uh)
|
||||||
struct connectdata *conn,
|
|
||||||
CURLU *uh)
|
|
||||||
{
|
{
|
||||||
struct Curl_creds *newcreds = NULL;
|
struct Curl_creds *newcreds = NULL;
|
||||||
CURLcode result = CURLE_OK;
|
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_BEARER] ||
|
||||||
data->set.str[STRING_SASL_AUTHZID] ||
|
data->set.str[STRING_SASL_AUTHZID] ||
|
||||||
data->set.str[STRING_SERVICE_NAME]) &&
|
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],
|
result = Curl_creds_create(data->set.str[STRING_USERNAME],
|
||||||
data->set.str[STRING_PASSWORD],
|
data->set.str[STRING_PASSWORD],
|
||||||
data->set.str[STRING_BEARER],
|
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) {
|
if(!result && data->state.up.user) {
|
||||||
result = Curl_urldecode(data->state.up.user, 0, &udecoded, NULL,
|
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);
|
REJECT_ZERO : REJECT_CTRL);
|
||||||
}
|
}
|
||||||
if(!result && data->state.up.password) {
|
if(!result && data->state.up.password) {
|
||||||
result = Curl_urldecode(data->state.up.password, 0, &pdecoded, NULL,
|
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);
|
REJECT_ZERO : REJECT_CTRL);
|
||||||
}
|
}
|
||||||
if(!result)
|
if(!result)
|
||||||
|
|
@ -1622,7 +1586,7 @@ static CURLcode url_set_data_creds(struct Curl_easy *data,
|
||||||
#ifndef CURL_DISABLE_NETRC
|
#ifndef CURL_DISABLE_NETRC
|
||||||
/* Check for overridden login details and set them accordingly so that
|
/* Check for overridden login details and set them accordingly so that
|
||||||
they are known when protocol->setup_connection is called! */
|
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 */
|
#endif /* CURL_DISABLE_NETRC */
|
||||||
|
|
||||||
out:
|
out:
|
||||||
|
|
@ -1634,139 +1598,37 @@ out:
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
static CURLcode url_set_conn_origin_etc(struct Curl_easy *data,
|
||||||
* Parse URL and fill in the relevant members of the connection struct.
|
struct connectdata *conn)
|
||||||
*/
|
|
||||||
static CURLcode parseurlandfillconn(struct Curl_easy *data,
|
|
||||||
struct connectdata *conn)
|
|
||||||
{
|
{
|
||||||
CURLcode result = CURLE_OK;
|
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 */
|
/* set the connection scheme */
|
||||||
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 */
|
|
||||||
result = url_set_conn_scheme(data, conn, conn->origin->scheme);
|
result = url_set_conn_scheme(data, conn, conn->origin->scheme);
|
||||||
if(result)
|
if(result)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
/* When the transfers initial_origin is not set, this is the initial
|
/* set the connection options */
|
||||||
* 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
|
|
||||||
if(data->set.str[STRING_OPTIONS]) {
|
if(data->set.str[STRING_OPTIONS]) {
|
||||||
curlx_free(conn->options);
|
|
||||||
conn->options = curlx_strdup(data->set.str[STRING_OPTIONS]);
|
conn->options = curlx_strdup(data->set.str[STRING_OPTIONS]);
|
||||||
if(!conn->options) {
|
if(!conn->options) {
|
||||||
result = CURLE_OUT_OF_MEMORY;
|
result = CURLE_OUT_OF_MEMORY;
|
||||||
goto out;
|
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
|
#ifdef USE_IPV6
|
||||||
Curl_creds_trace(data, data->state.creds, "transfer credentials");
|
conn->scope_id = data->set.scope_id ?
|
||||||
|
data->set.scope_id : data->state.origin->scopeid;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
out:
|
out:
|
||||||
|
|
@ -1852,379 +1714,6 @@ static CURLcode setup_connection_internals(struct Curl_easy *data,
|
||||||
return CURLE_OK;
|
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()
|
* Curl_parse_login_details()
|
||||||
*
|
*
|
||||||
|
|
@ -2601,14 +2090,6 @@ static CURLcode url_create_needle(struct Curl_easy *data,
|
||||||
CURLcode result = CURLE_OK;
|
CURLcode result = CURLE_OK;
|
||||||
bool network_scheme = TRUE; /* almost all are */
|
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
|
/* First, split up the current URL in parts so that we can use the
|
||||||
parts for checking against the already present connections. In order
|
parts for checking against the already present connections. In order
|
||||||
to not have to modify everything at once, we allocate a temporary
|
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
|
* Determine `conn->origin` and propulate `data->state.up` and
|
||||||
* other URL related properties.
|
* other URL related properties.
|
||||||
*************************************************************/
|
*************************************************************/
|
||||||
result = parseurlandfillconn(data, needle);
|
result = url_set_conn_origin_etc(data, needle);
|
||||||
if(result)
|
if(result)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
|
@ -2661,22 +2142,11 @@ static CURLcode url_create_needle(struct Curl_easy *data,
|
||||||
|
|
||||||
#ifndef CURL_DISABLE_PROXY
|
#ifndef CURL_DISABLE_PROXY
|
||||||
/* Going via a unix socket ignores any proxy settings */
|
/* Going via a unix socket ignores any proxy settings */
|
||||||
if(needle->via_peer && needle->via_peer->unix_socket) {
|
if(network_scheme &&
|
||||||
needle->bits.socksproxy = FALSE;
|
(!needle->via_peer || !needle->via_peer->unix_socket)) {
|
||||||
needle->bits.httpproxy = FALSE;
|
result = Curl_proxy_init_conn(data, needle);
|
||||||
needle->bits.proxy = FALSE;
|
|
||||||
}
|
|
||||||
else if(network_scheme) {
|
|
||||||
result = url_set_conn_proxies(data, needle);
|
|
||||||
if(result)
|
if(result)
|
||||||
goto out;
|
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 */
|
#endif /* CURL_DISABLE_PROXY */
|
||||||
|
|
||||||
|
|
@ -2692,15 +2162,6 @@ static CURLcode url_create_needle(struct Curl_easy *data,
|
||||||
Curl_peer_unlink(&needle->via_peer);
|
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
|
* Setup internals depending on protocol. Needs to be done after
|
||||||
* we figured out what/if proxy to use.
|
* we figured out what/if proxy to use.
|
||||||
|
|
@ -2743,6 +2204,118 @@ out:
|
||||||
return result;
|
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.
|
* Find an existing connection for the transfer or create a new one.
|
||||||
* Returns
|
* 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",
|
infof(data, "Reusing existing %s: connection%s with %s %s",
|
||||||
conn->given->name,
|
conn->given->name,
|
||||||
tls_upgraded ? " (upgraded to SSL)" : "",
|
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->socks_proxy.peer ? conn->socks_proxy.peer->user_hostname :
|
||||||
conn->http_proxy.peer ? conn->http_proxy.peer->user_hostname :
|
conn->http_proxy.peer ? conn->http_proxy.peer->user_hostname :
|
||||||
conn->origin->hostname);
|
conn->origin->hostname);
|
||||||
|
|
@ -2936,7 +2509,7 @@ static CURLcode url_find_or_create_conn(struct Curl_easy *data)
|
||||||
#ifdef CURL_DISABLE_PROXY
|
#ifdef CURL_DISABLE_PROXY
|
||||||
0
|
0
|
||||||
#else
|
#else
|
||||||
data->conn->bits.proxy
|
(data->conn->socks_proxy.peer || data->conn->http_proxy.peer)
|
||||||
#endif
|
#endif
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
@ -2953,21 +2526,33 @@ out:
|
||||||
CURLcode Curl_connect(struct Curl_easy *data, bool *pconnected)
|
CURLcode Curl_connect(struct Curl_easy *data, bool *pconnected)
|
||||||
{
|
{
|
||||||
CURLcode result;
|
CURLcode result;
|
||||||
struct connectdata *conn;
|
struct connectdata *conn = NULL;
|
||||||
|
|
||||||
*pconnected = FALSE;
|
*pconnected = FALSE;
|
||||||
|
|
||||||
/* Set the request to virgin state based on transfer settings */
|
/* Set the request to virgin state based on transfer settings */
|
||||||
Curl_req_hard_reset(&data->req, data);
|
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. */
|
/* Get or create a connection for the transfer. */
|
||||||
result = url_find_or_create_conn(data);
|
result = url_find_or_create_conn(data);
|
||||||
conn = data->conn;
|
conn = data->conn;
|
||||||
|
|
||||||
if(result)
|
if(result)
|
||||||
goto out;
|
goto out;
|
||||||
|
if(!data->conn) { /* just make really sure */
|
||||||
|
DEBUGASSERT(0);
|
||||||
|
result = CURLE_FAILED_INIT;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
DEBUGASSERT(conn);
|
|
||||||
Curl_pgrsTime(data, TIMER_POSTQUEUE);
|
Curl_pgrsTime(data, TIMER_POSTQUEUE);
|
||||||
if(conn->bits.reuse) {
|
if(conn->bits.reuse) {
|
||||||
if(conn->attached_xfers > 1)
|
if(conn->attached_xfers > 1)
|
||||||
|
|
|
||||||
|
|
@ -62,6 +62,7 @@
|
||||||
#include "hostip.h"
|
#include "hostip.h"
|
||||||
#include "hash.h"
|
#include "hash.h"
|
||||||
#include "peer.h"
|
#include "peer.h"
|
||||||
|
#include "proxy.h"
|
||||||
#include "splay.h"
|
#include "splay.h"
|
||||||
#include "curlx/dynbuf.h"
|
#include "curlx/dynbuf.h"
|
||||||
#include "bufref.h"
|
#include "bufref.h"
|
||||||
|
|
@ -191,13 +192,7 @@ typedef enum {
|
||||||
struct ConnectBits {
|
struct ConnectBits {
|
||||||
BIT(connect_only);
|
BIT(connect_only);
|
||||||
#ifndef CURL_DISABLE_PROXY
|
#ifndef CURL_DISABLE_PROXY
|
||||||
BIT(httpproxy); /* if set, this transfer is done through an HTTP proxy */
|
BIT(origin_is_proxy); /* if set, the connection's origin is a 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 */
|
|
||||||
#endif
|
#endif
|
||||||
/* always modify bits.close with the connclose() and connkeep() macros! */
|
/* always modify bits.close with the connclose() and connkeep() macros! */
|
||||||
BIT(close); /* if set, we close the connection after this request */
|
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_UDP) || \
|
||||||
((x)->transport == TRNSPRT_QUIC))
|
((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
|
* The connectdata struct contains all fields and variables that should be
|
||||||
* unique for an entire connection.
|
* unique for an entire connection.
|
||||||
|
|
@ -437,9 +426,6 @@ struct PureInfo {
|
||||||
session handle without disturbing information which is still alive, and
|
session handle without disturbing information which is still alive, and
|
||||||
that might be reused, in the connection pool. */
|
that might be reused, in the connection pool. */
|
||||||
struct ip_quadruple primary;
|
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;
|
const char *conn_scheme;
|
||||||
uint32_t conn_protocol;
|
uint32_t conn_protocol;
|
||||||
struct curl_certinfo certs; /* info about the certs. Asked for with
|
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.
|
Credentials from CURLOPT_* are only valid for this origin.
|
||||||
Always set once a transfer starts searching for connections. */
|
Always set once a transfer starts searching for connections. */
|
||||||
struct Curl_peer *initial_origin;
|
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 os_errno; /* filled in with errno whenever an error occurs */
|
||||||
int requests; /* request counter: redirects + authentication retakes */
|
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]);
|
curl_msnprintf(&HA1_hex[2 * i], 3, "%02x", digest[i]);
|
||||||
|
|
||||||
/* Generate our SPN */
|
/* 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)
|
if(!spn)
|
||||||
return CURLE_OUT_OF_MEMORY;
|
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;
|
return CURLE_OUT_OF_MEMORY;
|
||||||
|
|
||||||
/* Generate our SPN */
|
/* 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) {
|
if(!spn) {
|
||||||
curlx_free(output_token);
|
curlx_free(output_token);
|
||||||
return CURLE_OUT_OF_MEMORY;
|
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)
|
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,
|
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;
|
return CURLE_URL_MALFORMAT;
|
||||||
}
|
}
|
||||||
#ifndef CURL_DISABLE_PROXY
|
#ifndef CURL_DISABLE_PROXY
|
||||||
if(conn->bits.socksproxy) {
|
if(conn->socks_proxy.peer) {
|
||||||
failf(data, "HTTP/3 is not supported over a SOCKS proxy");
|
failf(data, "HTTP/3 is not supported over a SOCKS proxy");
|
||||||
return CURLE_URL_MALFORMAT;
|
return CURLE_URL_MALFORMAT;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -74,6 +74,24 @@ class TestSocks:
|
||||||
else:
|
else:
|
||||||
r.check_response(http_status=200)
|
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("sproto", ['socks4', 'socks5'])
|
||||||
@pytest.mark.parametrize("proto", Env.http_h1_h2_protos())
|
@pytest.mark.parametrize("proto", Env.http_h1_h2_protos())
|
||||||
def test_40_03_dl_serial(self, env: Env, httpd, danted, proto, sproto):
|
def test_40_03_dl_serial(self, env: Env, httpd, danted, proto, sproto):
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@
|
||||||
*
|
*
|
||||||
***************************************************************************/
|
***************************************************************************/
|
||||||
#include "unitcheck.h"
|
#include "unitcheck.h"
|
||||||
#include "noproxy.h"
|
#include "proxy.h"
|
||||||
|
|
||||||
static CURLcode test_unit1614(const char *arg)
|
static CURLcode test_unit1614(const char *arg)
|
||||||
{
|
{
|
||||||
|
|
@ -176,7 +176,7 @@ static CURLcode test_unit1614(const char *arg)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
for(i = 0; list[i].a; i++) {
|
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) {
|
if(match != list[i].match) {
|
||||||
curl_mfprintf(stderr, "%s in %s should %smatch\n",
|
curl_mfprintf(stderr, "%s in %s should %smatch\n",
|
||||||
list[i].a, list[i].n,
|
list[i].a, list[i].n,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue