if, for example, http redirects are followed.
When an `easy_perform()` starts, the transfer's `data->state.initial_origin`
-peer is cleared. When creating the connection, `conn->origin` is calculated
-(e.g. who the request talks to). If `data->state.initial_origin` is not
-set, the first `conn->origin` is linked there. Now `libcurl` knows where
+peer is cleared. When creating the connection, `data->state.origin` is
+calculated (e.g. who the request talks to). If `data->state.initial_origin`
+is not set, the first `data->state.origin` is linked there.
+Now `libcurl` knows where
the transfer initially talked to on all possible subsequent requests.
Credential information from `CURLOPT_*` settings is only applicable for the
A peer, in short, is a communication endpoint.
+## peers and transfers
+
+The peer a transfer, e.g. easy handle, works against is determined at the
+start of each request. It is kept in `data->state.origin`. For the first
+request done in a `curl_easy_perform()` or equivalent, this origin is
+linked to `data->state.initial_origin`. This allows checks if properties
+of `data->set.*` should apply to a request or not.
+
+`data->state.origin` is relevant for cookie processing, signing requests
+and other request/response based processing.
+
## peers and connections
A network connection always goes *somewhere*. That *somewhere* is called
the `origin` of the connection (e.g. the source of responses/downloads).
It is kept in `conn->origin` and is always present in a connection.
-The `origin` is *logical* endpoint a connection talks to.
+The `origin` is *logical* endpoint a connection talks to. In most
+configurations it is the same as `data->state.origin` (see proxies below).
For most connections, the `origin` is connected to *directly*. It
can be directed to another peer, however.
5. curl --> socks_proxy.peer --> http_proxy.peer --> conn->via_peer/origin
```
+A `conn->(socks|http)_proxy.peer` is only ever present when the proxy
+is in use and `NULL` otherwise.
+
+SOCKS proxies are always used for tunneling, either to the origin or
+the HTTP proxy. They operate in a connection filter.
+
+HTTP proxies can operate in two modes: tunneling or forwarding. When tunneling,
+they also operate in a connection filter. In forwarding mode however, they
+become the `origin` the connection talks to.
+
+Therefore, connections that talk to a forwarding HTTP proxy have `conn->origin`
+set to `conn->http_proxy.peer` and `conn->bits.origin_is_proxy` is set.
+
The connection filter `SETUP`, that assembles the filters for a connection,
figures out which peer to pass to which filter in order to make it all work.
The individual filters get passed a specific peer and do not need be concerned
multi_ev.c \
multi_ntfy.c \
netrc.c \
- noproxy.c \
openldap.c \
parsedate.c \
peer.c \
pop3.c \
progress.c \
protocol.c \
+ proxy.c \
psl.c \
rand.c \
ratelimit.c \
multi_ntfy.h \
multiif.h \
netrc.h \
- noproxy.h \
parsedate.h \
peer.h \
pingpong.h \
pop3.h \
progress.h \
protocol.h \
+ proxy.h \
psl.h \
rand.h \
ratelimit.h \
return CURLE_FAILED_INIT;
#ifndef CURL_DISABLE_PROXY
- if(conn->bits.socksproxy)
+ if(conn->socks_proxy.peer)
proxy_peer = conn->socks_proxy.peer;
- else if(conn->bits.httpproxy)
+ else if(conn->http_proxy.peer)
proxy_peer = conn->http_proxy.peer;
#endif
viamsg[0] = 0;
- if((peer != conn->origin) && (peer != proxy_peer)) {
+ if(!Curl_peer_equal(peer, conn->origin) &&
+ !Curl_peer_equal(peer, proxy_peer)) {
#ifdef USE_UNIX_SOCKETS
if(peer->unix_socket)
curl_msnprintf(viamsg, sizeof(viamsg), " over unix://%s",
if(cf->connected && (cf->sockindex == FIRSTSOCKET)) {
struct cf_socket_ctx *ctx = cf->ctx;
data->info.primary = ctx->ip;
- /* not sure if this is redundant... */
- data->info.conn_remote_port = cf->conn->origin->port;
}
}
return FALSE;
}
+#ifndef CURL_DISABLE_PROXY
+static bool cf_is_tunneling(struct Curl_cfilter *cf)
+{
+ for(; cf; cf = cf->next) {
+ if((cf->cft->flags & CF_TYPE_PROXY))
+ return TRUE;
+ }
+ return FALSE;
+}
+
+bool Curl_conn_is_tunneling(struct connectdata *conn, int sockindex)
+{
+ if(!CONN_SOCK_IDX_VALID(sockindex))
+ return FALSE;
+ return conn ? cf_is_tunneling(conn->cfilter[sockindex]) : FALSE;
+}
+#else
+bool Curl_conn_is_tunneling(struct connectdata *conn, int sockindex)
+{
+ (void)conn;
+ (void)sockindex;
+ return FALSE;
+}
+#endif /* CURL_DISABLE_PROXY */
+
static bool cf_is_ssl(struct Curl_cfilter *cf)
{
for(; cf; cf = cf->next) {
*/
bool Curl_conn_is_ssl(struct connectdata *conn, int sockindex);
+/* Determine if the connection has one or more proxy filters.
+ * e.g. is tunneling. */
+bool Curl_conn_is_tunneling(struct connectdata *conn, int sockindex);
+
/*
* Fill `info` with information about the TLS instance securing the connection
* when available, otherwise e.g. when Curl_conn_is_ssl() is FALSE, return
{
struct cf_setup_ctx *ctx = cf->ctx;
CURLcode result = CURLE_OK;
- if(ctx->state < CF_SETUP_CNNCT_SOCKS && cf->conn->bits.socksproxy) {
+ if(ctx->state < CF_SETUP_CNNCT_SOCKS && cf->conn->socks_proxy.peer) {
/* Add a SOCKS proxy to go through `first_peer` to `second_peer`*/
struct Curl_peer *second_peer;
- if(cf->conn->bits.httpproxy)
+ if(cf->conn->http_proxy.peer)
second_peer = cf->conn->http_proxy.peer;
else
second_peer = Curl_conn_get_destination(cf->conn, cf->sockindex);
struct cf_setup_ctx *ctx = cf->ctx;
CURLcode result = CURLE_OK;
- if(ctx->state < CF_SETUP_CNNCT_HTTP_PROXY && cf->conn->bits.httpproxy) {
+ if(ctx->state < CF_SETUP_CNNCT_HTTP_PROXY &&
+ cf->conn->http_proxy.peer && !cf->conn->bits.origin_is_proxy) {
+ struct Curl_peer *peer = cf->conn->http_proxy.peer;
+ struct Curl_peer *tunnel_peer =
+ Curl_conn_get_destination(cf->conn, cf->sockindex);
+
#ifdef USE_SSL
- if(IS_HTTPS_PROXY(cf->conn->http_proxy.proxytype) &&
+ if(CURL_PROXY_IS_HTTPS(cf->conn->http_proxy.proxytype) &&
!Curl_conn_is_ssl(cf->conn, cf->sockindex)) {
result = Curl_cf_ssl_proxy_insert_after(
cf, data, cf->conn->http_proxy.peer);
}
#endif /* USE_SSL */
- if(cf->conn->bits.tunnel_proxy) {
- struct Curl_peer *peer = cf->conn->http_proxy.peer;
- struct Curl_peer *tunnel_peer; /* where HTTP should tunnel to */
- tunnel_peer = Curl_conn_get_destination(cf->conn, cf->sockindex);
- result = Curl_cf_http_proxy_insert_after(
- cf, data, peer, tunnel_peer,
- ctx->transport, cf->conn->http_proxy.proxytype);
- if(result) {
- CURL_TRC_CF(data, cf, "adding HTTP proxy tunnel filter failed -> %d",
- (int)result);
- return result;
- }
- CURL_TRC_CF(data, cf, "added HTTP proxy tunnel filter");
+ result = Curl_cf_http_proxy_insert_after(
+ cf, data, peer, tunnel_peer,
+ ctx->transport, cf->conn->http_proxy.proxytype);
+ if(result) {
+ CURL_TRC_CF(data, cf, "adding HTTP proxy tunnel filter failed -> %d",
+ (int)result);
+ return result;
}
+ CURL_TRC_CF(data, cf, "added HTTP proxy tunnel filter");
ctx->state = CF_SETUP_CNNCT_HTTP_PROXY;
}
return result;
return CURLE_FAILED_INIT;
#if !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP)
- if(cf->conn->bits.httpproxy && cf->conn->bits.tunnel_proxy) {
+ if(cf->conn->http_proxy.peer && !cf->conn->bits.origin_is_proxy) {
first_transport =
Curl_http_proxy_transport(cf->conn->http_proxy.proxytype);
tunnel_peer = Curl_conn_get_destination(cf->conn, cf->sockindex);
- if((first_transport == TRNSPRT_QUIC) && (cf->conn->bits.socksproxy)) {
+ if((first_transport == TRNSPRT_QUIC) && cf->conn->socks_proxy.peer) {
failf(data, "HTTP/3 proxy not possible via SOCKS");
return CURLE_UNSUPPORTED_PROTOCOL;
}
/* Wanting QUIC with a HTTP tunneling filter, we now need to add
* the QUIC filter on top. Without tunneling, this has already
* happened in the Happy Eyeball filter. */
- if(ctx->transport == TRNSPRT_QUIC && cf->conn->bits.httpproxy &&
- cf->conn->bits.tunnel_proxy) {
+ if(ctx->transport == TRNSPRT_QUIC &&
+ cf->conn->http_proxy.peer && !cf->conn->bits.origin_is_proxy) {
struct Curl_peer *origin = Curl_conn_get_origin(cf->conn, cf->sockindex);
struct Curl_peer *peer =
Curl_conn_get_destination(cf->conn, cf->sockindex);
(ctx->ssl_mode != CURL_CF_SSL_DISABLE &&
cf->conn->scheme->flags & PROTOPT_SSL)) && /* we want SSL */
!Curl_conn_is_ssl(cf->conn, cf->sockindex)) { /* it is missing */
- /* Another FTP quirk: when adding SSL verification, to a DATA
- * connection, always verify against the control's origin */
- struct Curl_peer *origin = Curl_conn_get_origin(cf->conn, FIRSTSOCKET);
- struct Curl_peer *peer =
- Curl_conn_get_destination(cf->conn, cf->sockindex);
- result = Curl_cf_ssl_insert_after(cf, data, origin, peer);
+#ifndef CURL_DISABLE_PROXY
+ if(cf->conn->bits.origin_is_proxy) {
+ result = Curl_cf_ssl_proxy_insert_after(cf, data, cf->conn->origin);
+ }
+ else
+#endif
+ {
+ /* Another FTP quirk: when adding SSL verification, to a DATA
+ * connection, always verify against the control's origin */
+ struct Curl_peer *origin = Curl_conn_get_origin(cf->conn, FIRSTSOCKET);
+ struct Curl_peer *peer =
+ Curl_conn_get_destination(cf->conn, cf->sockindex);
+ result = Curl_cf_ssl_insert_after(cf, data, origin, peer);
+ }
if(result) {
CURL_TRC_CF(data, cf, "adding SSL filter for origin failed -> %d",
(int)result);
struct Curl_peer *Curl_conn_get_destination(struct connectdata *conn,
int sockindex)
{
-#ifndef CURL_DISABLE_PROXY
- if(conn->http_proxy.peer && !conn->bits.tunnel_proxy)
- return conn->http_proxy.peer;
-#endif
return (sockindex == SECONDARYSOCKET) ?
(conn->via_peer2 ? conn->via_peer2 : conn->origin2) :
(conn->via_peer ? conn->via_peer : conn->origin);
{
CURLcode result = CURLE_OK;
- if(conn->bits.ipv6
-#ifndef CURL_DISABLE_PROXY
- && !(conn->bits.tunnel_proxy || conn->bits.socksproxy)
-#endif
- ) {
+ if(conn->bits.ipv6 && !Curl_conn_is_tunneling(conn, FIRSTSOCKET)) {
/* We cannot disable EPSV when doing IPv6, so this is instead a fail */
failf(data, "Failed EPSV attempt, exiting");
return CURLE_WEIRD_SERVER_REPLY;
the effective control connection address is the proxy address,
not the ftp host. */
#ifndef CURL_DISABLE_PROXY
- if(conn->bits.tunnel_proxy || conn->bits.socksproxy)
+ if(Curl_conn_is_tunneling(conn, FIRSTSOCKET))
*newhostp = curlx_strdup(conn->origin->hostname);
else
#endif
{
struct curl_slist *head;
- for(head = (conn->bits.proxy && data->set.sep_headers) ?
+ for(head = (conn->http_proxy.peer && data->set.sep_headers) ?
data->set.proxyheaders : data->set.headers;
head; head = head->next) {
if(curl_strnequal(head->data, thisheader, thislen) &&
if(
#ifndef CURL_DISABLE_PROXY
- (!conn->bits.httpproxy || !conn->http_proxy.creds) &&
+ (!conn->http_proxy.peer || !conn->http_proxy.creds) &&
#endif
#ifdef USE_SPNEGO
!(authhost->want & CURLAUTH_NEGOTIATE) &&
#ifndef CURL_DISABLE_PROXY
/* Send proxy authentication header if needed */
- if(conn->bits.httpproxy && (!conn->bits.tunnel_proxy || is_connect)) {
+ if(conn->bits.origin_is_proxy || is_connect) {
result = output_auth_headers(data, conn, authproxy, request,
path_and_query, TRUE);
if(result)
if(is_connect)
proxy = HEADER_CONNECT;
else
- proxy = data->conn->bits.httpproxy && !data->conn->bits.tunnel_proxy ?
- HEADER_PROXY : HEADER_SERVER;
+ proxy = data->conn->bits.origin_is_proxy ? HEADER_PROXY : HEADER_SERVER;
switch(proxy) {
case HEADER_SERVER:
#endif
ptr = Curl_checkheaders(data, STRCONST("Host"));
- if(ptr && (!data->state.this_is_a_follow ||
- Curl_peer_equal(data->state.initial_origin, conn->origin))) {
+ if(ptr &&
+ (!data->state.this_is_a_follow ||
+ Curl_peer_equal(data->state.initial_origin, data->state.origin))) {
#ifndef CURL_DISABLE_COOKIES
/* If we have a given custom Host: header, we extract the hostname in
order to possibly use it for cookie reasons later on. We only allow the
}
else {
/* Use the hostname as present in the URL if it was IPv6. */
- char *host = (conn->origin->user_hostname[0] == '[') ?
- conn->origin->user_hostname : conn->origin->hostname;
+ char *host = (data->state.origin->user_hostname[0] == '[') ?
+ data->state.origin->user_hostname : data->state.origin->hostname;
if(((conn->given->protocol & (CURLPROTO_HTTPS | CURLPROTO_WSS)) &&
- (conn->origin->port == PORT_HTTPS)) ||
+ (data->state.origin->port == PORT_HTTPS)) ||
((conn->given->protocol & (CURLPROTO_HTTP | CURLPROTO_WS)) &&
- (conn->origin->port == PORT_HTTP)))
+ (data->state.origin->port == PORT_HTTP)))
/* if(HTTPS on port 443) OR (HTTP on port 80) then do not include
the port number in the host string */
aptr->host = curl_maprintf("Host: %s\r\n", host);
else
- aptr->host = curl_maprintf("Host: %s:%d\r\n", host, conn->origin->port);
+ aptr->host = curl_maprintf("Host: %s:%d\r\n",
+ host, data->state.origin->port);
if(!aptr->host)
/* without Host: we cannot make a nice request */
}
#ifndef CURL_DISABLE_PROXY
- if(conn->bits.httpproxy && !conn->bits.tunnel_proxy) {
+ if(conn->bits.origin_is_proxy) {
/* Using a proxy but does not tunnel through it */
/* The path sent to the proxy is in fact the entire URL, but if the remote
if(!h)
return CURLE_OUT_OF_MEMORY;
- if(conn->origin->user_hostname != conn->origin->hostname) {
- uc = curl_url_set(h, CURLUPART_HOST, conn->origin->hostname, 0);
+ if(data->state.origin->user_hostname != data->state.origin->hostname) {
+ uc = curl_url_set(h, CURLUPART_HOST, data->state.origin->hostname, 0);
if(uc) {
curl_url_cleanup(h);
return CURLE_OUT_OF_MEMORY;
if(data->cookies && data->state.cookie_engine) {
bool okay;
const char *host = data->req.cookiehost ?
- data->req.cookiehost : data->conn->origin->hostname;
+ data->req.cookiehost : data->state.origin->hostname;
Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
result = Curl_cookie_getlist(data, data->conn, &okay, host, &list);
if(!result && okay) {
alpn = Curl_conn_get_alpn_negotiated(data, conn);
if(alpn && !strcmp("h3", alpn)) {
#ifndef CURL_DISABLE_PROXY
- if((Curl_conn_http_version(data, conn) == 30) || !conn->bits.proxy ||
- conn->bits.tunnel_proxy)
+ if(!conn->bits.origin_is_proxy)
#endif
DEBUGASSERT(Curl_conn_http_version(data, conn) == 30);
info_version = "HTTP/3";
else if(alpn && !strcmp("h2", alpn)) {
#ifndef CURL_DISABLE_PROXY
if((Curl_conn_http_version(data, conn) != 20) &&
- conn->bits.proxy && !conn->bits.tunnel_proxy) {
+ conn->bits.origin_is_proxy) {
result = Curl_http2_switch(data);
if(result)
return result;
#ifndef CURL_DISABLE_PROXY
case H1_HD_PROXY_CONNECTION:
- if(conn->bits.httpproxy &&
- !conn->bits.tunnel_proxy &&
+ if(conn->bits.origin_is_proxy &&
!Curl_checkheaders(data, STRCONST("Proxy-Connection")) &&
!Curl_checkProxyheaders(data, data->conn, STRCONST("Proxy-Connection")))
result = curlx_dyn_add(req, "Proxy-Connection: Keep-Alive\r\n");
{
#ifndef CURL_DISABLE_ALTSVC
const char *v;
- struct connectdata *conn = data->conn;
v = (data->asi &&
(Curl_xfer_is_secure(data) ||
#ifdef DEBUGBUILD
struct SingleRequest *k = &data->req;
enum alpnid id = (k->httpversion == 30) ? ALPN_h3 :
(k->httpversion == 20) ? ALPN_h2 : ALPN_h1;
- return Curl_altsvc_parse(data, data->asi, v, id, conn->origin->hostname,
- curlx_uitous((unsigned int)conn->origin->port));
+ return Curl_altsvc_parse(
+ data, data->asi, v, id, data->state.origin->hostname,
+ curlx_uitous((unsigned int)data->state.origin->port));
}
#else
(void)data;
const char *v = HD_VAL(hd, hdlen, "Proxy-Connection:");
if(v) {
struct connectdata *conn = data->conn;
- if((k->httpversion == 10) && conn->bits.httpproxy &&
+ if((k->httpversion == 10) && conn->http_proxy.peer &&
HD_IS_AND_SAYS(hd, hdlen, "Proxy-Connection:", "keep-alive")) {
/*
* When an HTTP/1.0 reply comes when using a proxy, the
connkeep(conn, "Proxy-Connection keep-alive"); /* do not close */
infof(data, "HTTP/1.0 proxy connection set to keep alive");
}
- else if((k->httpversion == 11) && conn->bits.httpproxy &&
+ else if((k->httpversion == 11) && conn->http_proxy.peer &&
HD_IS_AND_SAYS(hd, hdlen, "Proxy-Connection:", "close")) {
/*
* We get an HTTP/1.1 response from a proxy and it says it will
/* If there is a custom-set Host: name, use it here, or else use
* real peer hostname. */
const char *host = data->req.cookiehost ?
- data->req.cookiehost : conn->origin->hostname;
+ data->req.cookiehost : data->state.origin->hostname;
const bool secure_context = Curl_secure_context(conn, host);
CURLcode result;
Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
)
) ? HD_VAL(hd, hdlen, "Strict-Transport-Security:") : NULL;
if(v) {
- CURLcode result =
- Curl_hsts_parse(data->hsts, conn->origin->hostname, v);
+ CURLcode result = Curl_hsts_parse(
+ data->hsts, data->state.origin->hostname, v);
if(result) {
if(result == CURLE_OUT_OF_MEMORY)
return result;
struct Curl_cfilter *cf = userp;
struct cf_h2_ctx *ctx = cf->ctx;
struct h2_stream_ctx *stream;
- struct Curl_easy *data_s;
+ struct Curl_easy *data;
int32_t stream_id = frame->hd.stream_id;
CURLcode result;
(void)flags;
DEBUGASSERT(stream_id); /* should never be a zero stream ID here */
/* get the stream from the hash based on Stream ID */
- data_s = nghttp2_session_get_stream_user_data(session, stream_id);
- if(!GOOD_EASY_HANDLE(data_s))
+ data = nghttp2_session_get_stream_user_data(session, stream_id);
+ if(!GOOD_EASY_HANDLE(data))
/* Receiving a Stream ID not in the hash should not happen, this is an
internal error more than anything else! */
return NGHTTP2_ERR_CALLBACK_FAILURE;
- stream = H2_STREAM_CTX(ctx, data_s);
+ stream = H2_STREAM_CTX(ctx, data);
if(!stream) {
- failf(data_s, "Internal NULL stream");
+ failf(data, "Internal NULL stream");
return NGHTTP2_ERR_CALLBACK_FAILURE;
}
!strncmp(HTTP_PSEUDO_AUTHORITY, (const char *)name, namelen)) {
/* pseudo headers are lower case */
int rc = 0;
- char *check = curl_maprintf("%s:%d", cf->conn->origin->hostname,
- cf->conn->origin->port);
+ char *check = curl_maprintf("%s:%d", data->state.origin->hostname,
+ data->state.origin->port);
if(!check)
/* no memory */
return NGHTTP2_ERR_CALLBACK_FAILURE;
if(!curl_strequal(check, (const char *)value) &&
- ((cf->conn->origin->port != cf->conn->given->defport) ||
- !curl_strequal(cf->conn->origin->hostname, (const char *)value))) {
+ ((data->state.origin->port != cf->conn->given->defport) ||
+ !curl_strequal(data->state.origin->hostname, (const char *)value))) {
/* This is push is not for the same authority that was asked for in
* the URL. RFC 7540 section 8.2 says: "A client MUST treat a
* PUSH_PROMISE for which the server is not authoritative as a stream
char **headp;
if(stream->push_headers_alloc > 1000) {
/* this is beyond crazy many headers, bail out */
- failf(data_s, "Too many PUSH_PROMISE headers");
+ failf(data, "Too many PUSH_PROMISE headers");
free_push_headers(stream);
return NGHTTP2_ERR_CALLBACK_FAILURE;
}
if(stream->bodystarted) {
/* This is a trailer */
- CURL_TRC_CF(data_s, cf, "[%d] trailer: %.*s: %.*s",
+ CURL_TRC_CF(data, cf, "[%d] trailer: %.*s: %.*s",
stream->id, (int)namelen, name, (int)valuelen, value);
result = Curl_dynhds_add(&stream->resp_trailers,
(const char *)name, namelen,
(const char *)value, valuelen);
if(result) {
- cf_h2_header_error(cf, data_s, stream, result);
+ cf_h2_header_error(cf, data, stream, result);
return NGHTTP2_ERR_CALLBACK_FAILURE;
}
result = Curl_http_decode_status(&stream->status_code,
(const char *)value, valuelen);
if(result) {
- cf_h2_header_error(cf, data_s, stream, result);
+ cf_h2_header_error(cf, data, stream, result);
return NGHTTP2_ERR_CALLBACK_FAILURE;
}
hlen = curl_msnprintf(buffer, sizeof(buffer), HTTP_PSEUDO_STATUS ":%d\r",
stream->status_code);
- result = Curl_headers_push(data_s, buffer, hlen, CURLH_PSEUDO);
+ result = Curl_headers_push(data, buffer, hlen, CURLH_PSEUDO);
if(result) {
- cf_h2_header_error(cf, data_s, stream, result);
+ cf_h2_header_error(cf, data, stream, result);
return NGHTTP2_ERR_CALLBACK_FAILURE;
}
curlx_dyn_reset(&ctx->scratch);
if(!result)
result = curlx_dyn_addn(&ctx->scratch, STRCONST(" \r\n"));
if(!result)
- h2_xfer_write_resp_hd(cf, data_s, stream, curlx_dyn_ptr(&ctx->scratch),
+ h2_xfer_write_resp_hd(cf, data, stream, curlx_dyn_ptr(&ctx->scratch),
curlx_dyn_len(&ctx->scratch), FALSE);
if(result) {
- cf_h2_header_error(cf, data_s, stream, result);
+ cf_h2_header_error(cf, data, stream, result);
return NGHTTP2_ERR_CALLBACK_FAILURE;
}
/* if we receive data for another handle, wake that up */
- if(CF_DATA_CURRENT(cf) != data_s)
- Curl_multi_mark_dirty(data_s);
+ if(CF_DATA_CURRENT(cf) != data)
+ Curl_multi_mark_dirty(data);
- CURL_TRC_CF(data_s, cf, "[%d] status: HTTP/2 %03d",
+ CURL_TRC_CF(data, cf, "[%d] status: HTTP/2 %03d",
stream->id, stream->status_code);
return 0;
}
if(!result)
result = curlx_dyn_addn(&ctx->scratch, STRCONST("\r\n"));
if(!result)
- h2_xfer_write_resp_hd(cf, data_s, stream, curlx_dyn_ptr(&ctx->scratch),
+ h2_xfer_write_resp_hd(cf, data, stream, curlx_dyn_ptr(&ctx->scratch),
curlx_dyn_len(&ctx->scratch), FALSE);
if(result) {
- cf_h2_header_error(cf, data_s, stream, result);
+ cf_h2_header_error(cf, data, stream, result);
return NGHTTP2_ERR_CALLBACK_FAILURE;
}
/* if we receive data for another handle, wake that up */
- if(CF_DATA_CURRENT(cf) != data_s)
- Curl_multi_mark_dirty(data_s);
+ if(CF_DATA_CURRENT(cf) != data)
+ Curl_multi_mark_dirty(data);
- CURL_TRC_CF(data_s, cf, "[%d] header: %.*s: %.*s",
+ CURL_TRC_CF(data, cf, "[%d] header: %.*s: %.*s",
stream->id, (int)namelen, name, (int)valuelen, value);
return 0; /* 0 is successful */
(data->state.http_neg.wanted & CURL_HTTP_V2x) &&
data->state.http_neg.h2_prior_knowledge) {
#ifndef CURL_DISABLE_PROXY
- if(data->conn->bits.httpproxy && !data->conn->bits.tunnel_proxy) {
- /* We do not support HTTP/2 proxies yet. Also it is debatable
- whether or not this setting should apply to HTTP/2 proxies. */
+ if(data->conn->bits.origin_is_proxy) {
infof(data, "Ignoring HTTP/2 prior knowledge due to proxy");
return FALSE;
}
CURLcode Curl_output_aws_sigv4(struct Curl_easy *data)
{
CURLcode result = CURLE_OUT_OF_MEMORY;
- struct connectdata *conn = data->conn;
struct Curl_str provider0 = { NULL, 0 };
struct Curl_str provider1 = { NULL, 0 };
struct Curl_str region = { NULL, 0 };
struct Curl_str service = { NULL, 0 };
- const char *hostname = conn->origin->hostname;
+ const char *hostname = data->state.origin->hostname;
char timestamp[TIMESTAMP_SIZE];
char date[9];
struct dynbuf canonical_headers;
#endif
}
else {
- DEBUGASSERT(data->conn->origin);
+ DEBUGASSERT(data->state.origin);
digest = &data->state.digest;
- digest_flush_stale(digest, data->conn->origin, data->state.creds);
+ digest_flush_stale(digest, data->state.origin, data->state.creds);
allocuserpwd = &data->req.hd_auth;
creds = data->state.creds;
authp = &data->state.authhost;
}
else {
creds = data->state.creds;
- host = conn->origin->hostname;
+ host = data->state.origin->hostname;
state = conn->http_negotiate_state;
}
else {
allocuserpwd = &data->req.hd_auth;
creds = data->state.creds;
- hostname = conn->origin->hostname;
+ hostname = data->state.origin->hostname;
state = &conn->http_ntlm_state;
authp = &data->state.authhost;
}
else if(is_connect && is_udp)
proxy = HEADER_CONNECT_UDP;
else
- proxy = (conn->bits.httpproxy && !conn->bits.tunnel_proxy) ?
- HEADER_PROXY : HEADER_SERVER;
+ proxy = conn->bits.origin_is_proxy ? HEADER_PROXY : HEADER_SERVER;
switch(proxy) {
case HEADER_SERVER:
extern struct Curl_cftype Curl_cft_http_proxy;
-#endif /* !CURL_DISABLE_PROXY && !CURL_DISABLE_HTTP */
-
-#define IS_HTTPS_PROXY(t) \
- (((t) == CURLPROXY_HTTPS) || \
- ((t) == CURLPROXY_HTTPS2) || \
- ((t) == CURLPROXY_HTTPS3))
-
-#define IS_QUIC_PROXY(t) ((t) == CURLPROXY_HTTPS3)
-
uint8_t Curl_http_proxy_transport(uint8_t proxytype);
+#endif /* !CURL_DISABLE_PROXY && !CURL_DISABLE_HTTP */
+
#endif /* HEADER_CURL_HTTP_PROXY_H */
+++ /dev/null
-/***************************************************************************
- * _ _ ____ _
- * 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 */
}
DEBUGASSERT(pp.scheme);
- if(IS_HTTPS_PROXY(proxytype) &&
+ if(CURL_PROXY_IS_HTTPS(proxytype) &&
!Curl_ssl_supports(data, SSLSUPP_HTTPS_PROXY)) {
failf(data, "Unsupported proxy \'%s\', libcurl is built without the "
"HTTPS-proxy support.", url);
--- /dev/null
+/***************************************************************************
+ * _ _ ____ _
+ * 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 */
-#ifndef HEADER_CURL_NOPROXY_H
-#define HEADER_CURL_NOPROXY_H
+#ifndef HEADER_CURL_PROXY_H
+#define HEADER_CURL_PROXY_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
#include "curl_setup.h"
#ifndef CURL_DISABLE_PROXY
-bool Curl_check_noproxy(const char *name, const char *no_proxy);
-#endif
-#endif /* HEADER_CURL_NOPROXY_H */
+struct Curl_easy;
+struct Curl_peer;
+struct Curl_creds;
+struct connectdata;
+
+struct proxy_info {
+ struct Curl_peer *peer; /* proxy to this peer */
+ struct Curl_creds *creds; /* use these credentials, maybe NULL */
+ uint8_t proxytype; /* what kind of proxy that is in use */
+};
+
+#define CURL_PROXY_IS_HTTPS(t) \
+ (((t) == CURLPROXY_HTTPS) || \
+ ((t) == CURLPROXY_HTTPS2) || \
+ ((t) == CURLPROXY_HTTPS3))
+
+#define CURL_PROXY_IS_HTTP(t) \
+ (((t) == CURLPROXY_HTTP) || \
+ ((t) == CURLPROXY_HTTP_1_0))
+
+#define CURL_PROXY_IS_ANY_HTTP(t) \
+ (CURL_PROXY_IS_HTTP(t) || \
+ CURL_PROXY_IS_HTTPS(t))
+
+CURLcode Curl_proxy_init_conn(struct Curl_easy *data,
+ struct connectdata *conn);
+
+#endif /* !CURL_DISABLE_PROXY */
+
+#endif /* HEADER_CURL_PROXY_H */
/* initial transfer request coming up, forget the initial origin
* from a previous perform() on this handle. */
Curl_peer_unlink(&data->state.initial_origin);
+ Curl_peer_unlink(&data->state.origin);
data->state.requests = 0;
data->state.followlocation = 0; /* reset the location-follow counter */
data->state.this_is_a_follow = FALSE; /* reset this */
#include "urlapi-int.h"
#include "system_win32.h"
#include "hsts.h"
-#include "noproxy.h"
+#include "proxy.h"
#include "cfilters.h"
#include "idn.h"
#include "http_proxy.h"
/* Close down all open SSL info and sessions */
Curl_ssl_close_all(data);
+ Curl_peer_unlink(&data->state.origin);
Curl_peer_unlink(&data->state.initial_origin);
Curl_ssl_free_certinfo(data);
static bool url_match_proxy_use(struct connectdata *conn,
struct url_conn_match *m)
{
- if(m->needle->bits.httpproxy != conn->bits.httpproxy ||
- m->needle->bits.socksproxy != conn->bits.socksproxy)
+ if(m->needle->bits.origin_is_proxy != conn->bits.origin_is_proxy)
return FALSE;
- if(m->needle->bits.socksproxy &&
- !proxy_info_matches(&m->needle->socks_proxy, &conn->socks_proxy))
+ if(!proxy_info_matches(&m->needle->socks_proxy, &conn->socks_proxy))
return FALSE;
- if(m->needle->bits.httpproxy) {
- if(m->needle->bits.tunnel_proxy != conn->bits.tunnel_proxy)
- return FALSE;
+ if(!proxy_info_matches(&m->needle->http_proxy, &conn->http_proxy))
+ return FALSE;
- if(!proxy_info_matches(&m->needle->http_proxy, &conn->http_proxy))
+ if(CURL_PROXY_IS_HTTPS(m->needle->http_proxy.proxytype)) {
+ /* https proxies come in different types, http/1.1, h2, ... */
+ /* match SSL config to proxy */
+ if(!Curl_ssl_conn_config_match(m->data, conn, TRUE)) {
+ DEBUGF(infof(m->data,
+ "Connection #%" FMT_OFF_T
+ " has different SSL proxy parameters, cannot reuse",
+ conn->connection_id));
return FALSE;
-
- if(IS_HTTPS_PROXY(m->needle->http_proxy.proxytype)) {
- /* https proxies come in different types, http/1.1, h2, ... */
- if(m->needle->http_proxy.proxytype != conn->http_proxy.proxytype)
- return FALSE;
- /* match SSL config to proxy */
- if(!Curl_ssl_conn_config_match(m->data, conn, TRUE)) {
- DEBUGF(infof(m->data,
- "Connection #%" FMT_OFF_T
- " has different SSL proxy parameters, cannot reuse",
- conn->connection_id));
- return FALSE;
- }
- /* the SSL config to the server, which may apply here is checked
- * further below */
}
+ /* the SSL config to the server, which may apply here is checked
+ * further below */
}
return TRUE;
}
if(!Curl_peer_same_destination(m->needle->via_peer, conn->via_peer))
return FALSE;
-#ifndef CURL_DISABLE_PROXY
- if(m->needle->bits.httpproxy && !m->needle->bits.tunnel_proxy) {
- /* Talking to a non-tunneling HTTP proxy matches on proxy peers. */
- return Curl_peer_equal(m->needle->http_proxy.peer,
- conn->http_proxy.peer);
- }
-#endif
-
if(m->needle->origin->scheme != conn->origin->scheme) {
/* `needle` and `conn` not having the same scheme.
* This is allowed for the same family *if* conn is using TLS.
/* Store current time to give a baseline to keepalive connection times. */
conn->keepalive = conn->created;
-#ifndef CURL_DISABLE_PROXY
- conn->http_proxy.proxytype = data->set.proxytype;
- conn->socks_proxy.proxytype = CURLPROXY_SOCKS4;
-
- /* note that these two proxy bits are set on what looks to be
- requested, they may be altered down the road */
- conn->bits.proxy = (data->set.str[STRING_PROXY] &&
- *data->set.str[STRING_PROXY]);
- conn->bits.httpproxy = (conn->bits.proxy &&
- (conn->http_proxy.proxytype == CURLPROXY_HTTP ||
- conn->http_proxy.proxytype == CURLPROXY_HTTP_1_0 ||
- IS_HTTPS_PROXY(conn->http_proxy.proxytype)));
- conn->bits.socksproxy = (conn->bits.proxy && !conn->bits.httpproxy);
-
- if(data->set.str[STRING_PRE_PROXY] && *data->set.str[STRING_PRE_PROXY]) {
- conn->bits.proxy = TRUE;
- conn->bits.socksproxy = TRUE;
- }
-
- conn->bits.tunnel_proxy = data->set.tunnel_thru_httpproxy;
-#endif /* CURL_DISABLE_PROXY */
-
#ifndef CURL_DISABLE_FTP
conn->bits.ftp_use_epsv = data->set.ftp_use_epsv;
conn->bits.ftp_use_eprt = data->set.ftp_use_eprt;
#ifndef CURL_DISABLE_HSTS
static CURLcode hsts_upgrade(struct Curl_easy *data,
- struct connectdata *conn,
CURLU *uh,
uint16_t port_override,
uint32_t scope_id)
{
/* HSTS upgrade */
- if(data->hsts && (conn->origin->scheme == &Curl_scheme_http) &&
- Curl_hsts_applies(data->hsts, conn->origin)) {
+ if(data->hsts && (data->state.origin->scheme == &Curl_scheme_http) &&
+ Curl_hsts_applies(data->hsts, data->state.origin)) {
char *url;
CURLUcode uc;
CURLcode result;
Curl_bufref_set(&data->state.url, url, 0, curl_free);
result = Curl_peer_from_url(uh, data, port_override, scope_id,
- &data->state.up, &conn->origin);
+ &data->state.up, &data->state.origin);
if(result)
return result;
infof(data, "Switched from HTTP to HTTPS due to HSTS => %s", url);
return CURLE_OK;
}
#else
-#define hsts_upgrade(x, y, z, a, b) CURLE_OK
+#define hsts_upgrade(x, y, z, a) CURLE_OK
#endif
#ifndef CURL_DISABLE_NETRC
* option or a .netrc file, if applicable.
*/
static CURLcode url_set_data_creds_netrc(struct Curl_easy *data,
- struct connectdata *conn,
struct Curl_creds **pcreds)
{
struct Curl_creds *ncreds_out = NULL;
goto out;
ret = Curl_netrc_scan(data, &data->state.netrc,
- conn->origin->hostname,
+ data->state.origin->hostname,
Curl_creds_user(ncreds_in),
data->set.str[STRING_NETRC_FILE],
&ncreds_out);
else if(ret && ((ret == NETRC_NO_MATCH) ||
(data->set.use_netrc == CURL_NETRC_OPTIONAL))) {
infof(data, "Could not find host %s in the %s file; using defaults",
- conn->origin->hostname,
+ data->state.origin->hostname,
(data->set.str[STRING_NETRC_FILE] ?
data->set.str[STRING_NETRC_FILE] : ".netrc"));
}
goto out;
}
else if(ncreds_out) {
- if(!(conn->scheme->flags & PROTOPT_USERPWDCTRL)) {
+ if(!(data->state.origin->scheme->flags & PROTOPT_USERPWDCTRL)) {
/* if the protocol cannot handle control codes in credentials, make
sure there are none */
if(str_has_ctrl(ncreds_out->user) ||
}
}
CURL_TRC_M(data, "netrc: using credentials for %s as %s",
- conn->origin->hostname, ncreds_out->user);
+ data->state.origin->hostname, ncreds_out->user);
result = Curl_creds_merge(ncreds_out->user, ncreds_out->passwd,
*pcreds, CREDS_NETRC, pcreds);
if(result)
DEBUGASSERT(0);
}
+#ifdef CURLVERBOSE
+ Curl_creds_trace(data, data->state.creds, "transfer credentials");
+#endif
+
out:
Curl_creds_unlink(&ncreds_out);
return result;
}
#endif /* CURL_DISABLE_NETRC */
-static CURLcode url_set_data_creds(struct Curl_easy *data,
- struct connectdata *conn,
- CURLU *uh)
+static CURLcode url_set_data_creds(struct Curl_easy *data, CURLU *uh)
{
struct Curl_creds *newcreds = NULL;
CURLcode result = CURLE_OK;
data->set.str[STRING_BEARER] ||
data->set.str[STRING_SASL_AUTHZID] ||
data->set.str[STRING_SERVICE_NAME]) &&
- Curl_auth_allowed_to_origin(data, conn->origin)) {
+ Curl_auth_allowed_to_origin(data, data->state.origin)) {
result = Curl_creds_create(data->set.str[STRING_USERNAME],
data->set.str[STRING_PASSWORD],
data->set.str[STRING_BEARER],
}
if(!result && data->state.up.user) {
result = Curl_urldecode(data->state.up.user, 0, &udecoded, NULL,
- conn->scheme->flags&PROTOPT_USERPWDCTRL ?
+ (data->state.origin->scheme->flags &
+ PROTOPT_USERPWDCTRL) ?
REJECT_ZERO : REJECT_CTRL);
}
if(!result && data->state.up.password) {
result = Curl_urldecode(data->state.up.password, 0, &pdecoded, NULL,
- conn->scheme->flags&PROTOPT_USERPWDCTRL ?
+ (data->state.origin->scheme->flags &
+ PROTOPT_USERPWDCTRL) ?
REJECT_ZERO : REJECT_CTRL);
}
if(!result)
#ifndef CURL_DISABLE_NETRC
/* Check for overridden login details and set them accordingly so that
they are known when protocol->setup_connection is called! */
- result = url_set_data_creds_netrc(data, conn, &newcreds);
+ result = url_set_data_creds_netrc(data, &newcreds);
#endif /* CURL_DISABLE_NETRC */
out:
return result;
}
-/*
- * Parse URL and fill in the relevant members of the connection struct.
- */
-static CURLcode parseurlandfillconn(struct Curl_easy *data,
- struct connectdata *conn)
+static CURLcode url_set_conn_origin_etc(struct Curl_easy *data,
+ struct connectdata *conn)
{
CURLcode result = CURLE_OK;
- CURLU *uh;
- CURLUcode uc;
- bool use_set_uh = (data->set.uh && !data->state.this_is_a_follow);
- uint16_t port_override = data->state.allow_port ? data->set.use_port : 0;
- uint32_t scope_id = 0;
- up_free(data); /* cleanup previous leftovers first */
+ Curl_peer_link(&conn->origin, data->state.origin);
- /* parse the URL */
- if(use_set_uh)
- uh = data->state.uh = curl_url_dup(data->set.uh);
- else
- uh = data->state.uh = curl_url();
- if(!uh) {
- result = CURLE_OUT_OF_MEMORY;
- goto out;
- }
-
- /* Calculate the *real* URL this transfer uses, applying defaults
- * where information is missing. */
- if(data->set.str[STRING_DEFAULT_PROTOCOL] &&
- !Curl_is_absolute_url(Curl_bufref_ptr(&data->state.url), NULL, 0, TRUE)) {
- char *url = curl_maprintf("%s://%s",
- data->set.str[STRING_DEFAULT_PROTOCOL],
- Curl_bufref_ptr(&data->state.url));
- if(!url) {
- result = CURLE_OUT_OF_MEMORY;
- goto out;
- }
- Curl_bufref_set(&data->state.url, url, 0, curl_free);
- }
-
- if(!use_set_uh) {
- char *newurl;
- uc = curl_url_set(uh, CURLUPART_URL, Curl_bufref_ptr(&data->state.url),
- (unsigned int)(CURLU_GUESS_SCHEME |
- CURLU_NON_SUPPORT_SCHEME |
- (data->set.disallow_username_in_url ?
- CURLU_DISALLOW_USER : 0) |
- (data->set.path_as_is ? CURLU_PATH_AS_IS : 0)));
- if(uc) {
- failf(data, "URL rejected: %s", curl_url_strerror(uc));
- result = Curl_uc_to_curlcode(uc);
- goto out;
- }
-
- /* after it was parsed, get the generated normalized version */
- uc = curl_url_get(uh, CURLUPART_URL, &newurl, CURLU_GET_EMPTY);
- if(uc) {
- result = Curl_uc_to_curlcode(uc);
- goto out;
- }
- Curl_bufref_set(&data->state.url, newurl, 0, curl_free);
- }
-
-#ifdef USE_IPV6
- scope_id = data->set.scope_id;
-#endif
-
- /* `uh` is now as the connection should use it, probably. */
- result = Curl_peer_from_url(uh, data, port_override, scope_id,
- &data->state.up, &conn->origin);
- if(result)
- goto out;
-
- result = hsts_upgrade(data, conn, uh, port_override, scope_id);
- if(result)
- goto out;
-
- /* now that the origin is fixed, check and set the connection scheme */
+ /* set the connection scheme */
result = url_set_conn_scheme(data, conn, conn->origin->scheme);
if(result)
goto out;
- /* When the transfers initial_origin is not set, this is the initial
- * request. Remember this starting point. This is used to
- * select credentials. */
- if(!data->state.initial_origin)
- Curl_peer_link(&data->state.initial_origin, conn->origin);
-
- result = url_set_data_creds(data, conn, uh);
- if(result)
- goto out;
-
- uc = curl_url_get(uh, CURLUPART_OPTIONS, &data->state.up.options,
- CURLU_URLDECODE);
- if(!uc) {
- conn->options = curlx_strdup(data->state.up.options);
+ /* set the connection options */
+ if(data->set.str[STRING_OPTIONS]) {
+ conn->options = curlx_strdup(data->set.str[STRING_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]) {
- curlx_free(conn->options);
- conn->options = curlx_strdup(data->set.str[STRING_OPTIONS]);
+ else if(data->state.up.options) {
+ conn->options = curlx_strdup(data->state.up.options);
if(!conn->options) {
result = CURLE_OUT_OF_MEMORY;
goto out;
}
}
-#ifdef CURLVERBOSE
- Curl_creds_trace(data, data->state.creds, "transfer credentials");
+#ifdef USE_IPV6
+ conn->scope_id = data->set.scope_id ?
+ data->set.scope_id : data->state.origin->scopeid;
#endif
out:
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()
*
CURLcode result = CURLE_OK;
bool network_scheme = TRUE; /* almost all are */
- /*************************************************************
- * Check input data
- *************************************************************/
- if(!Curl_bufref_ptr(&data->state.url)) {
- result = CURLE_URL_MALFORMAT;
- goto out;
- }
-
/* First, split up the current URL in parts so that we can use the
parts for checking against the already present connections. In order
to not have to modify everything at once, we allocate a temporary
* Determine `conn->origin` and propulate `data->state.up` and
* other URL related properties.
*************************************************************/
- result = parseurlandfillconn(data, needle);
+ result = url_set_conn_origin_etc(data, needle);
if(result)
goto out;
#ifndef CURL_DISABLE_PROXY
/* Going via a unix socket ignores any proxy settings */
- if(needle->via_peer && needle->via_peer->unix_socket) {
- needle->bits.socksproxy = FALSE;
- needle->bits.httpproxy = FALSE;
- needle->bits.proxy = FALSE;
- }
- else if(network_scheme) {
- result = url_set_conn_proxies(data, needle);
+ if(network_scheme &&
+ (!needle->via_peer || !needle->via_peer->unix_socket)) {
+ result = Curl_proxy_init_conn(data, needle);
if(result)
goto out;
-
- /*************************************************************
- * If the protocol is using SSL and HTTP proxy is used, we set
- * the tunnel_proxy bit.
- *************************************************************/
- if((needle->given->flags & PROTOPT_SSL) && needle->bits.httpproxy)
- needle->bits.tunnel_proxy = TRUE;
}
#endif /* CURL_DISABLE_PROXY */
Curl_peer_unlink(&needle->via_peer);
}
-#ifndef CURL_DISABLE_PROXY
- /*************************************************************
- * If the "connect to" feature is used with an HTTP proxy,
- * we set the tunnel_proxy bit.
- *************************************************************/
- if(needle->via_peer && needle->bits.httpproxy)
- needle->bits.tunnel_proxy = TRUE;
-#endif
-
/*************************************************************
* Setup internals depending on protocol. Needs to be done after
* we figured out what/if proxy to use.
return result;
}
+static CURLcode url_set_data_origin_and_creds(struct Curl_easy *data)
+{
+ CURLcode result = CURLE_OK;
+ CURLU *uh;
+ CURLUcode uc;
+ bool use_set_uh = (data->set.uh && !data->state.this_is_a_follow);
+ uint16_t port_override = data->state.allow_port ? data->set.use_port : 0;
+ uint32_t scope_id = 0;
+
+ /*************************************************************
+ * Check input data
+ *************************************************************/
+ if(!Curl_bufref_ptr(&data->state.url)) {
+ result = CURLE_URL_MALFORMAT;
+ goto out;
+ }
+
+ up_free(data); /* cleanup previous leftovers first */
+
+ /* parse the URL */
+ if(use_set_uh)
+ uh = data->state.uh = curl_url_dup(data->set.uh);
+ else
+ uh = data->state.uh = curl_url();
+ if(!uh) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto out;
+ }
+
+ /* Calculate the *real* URL this transfer uses, applying defaults
+ * where information is missing. */
+ if(data->set.str[STRING_DEFAULT_PROTOCOL] &&
+ !Curl_is_absolute_url(Curl_bufref_ptr(&data->state.url), NULL, 0, TRUE)) {
+ char *url = curl_maprintf("%s://%s",
+ data->set.str[STRING_DEFAULT_PROTOCOL],
+ Curl_bufref_ptr(&data->state.url));
+ if(!url) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto out;
+ }
+ Curl_bufref_set(&data->state.url, url, 0, curl_free);
+ }
+
+ if(!use_set_uh) {
+ char *newurl;
+ uc = curl_url_set(uh, CURLUPART_URL, Curl_bufref_ptr(&data->state.url),
+ (unsigned int)(CURLU_GUESS_SCHEME |
+ CURLU_NON_SUPPORT_SCHEME |
+ (data->set.disallow_username_in_url ?
+ CURLU_DISALLOW_USER : 0) |
+ (data->set.path_as_is ? CURLU_PATH_AS_IS : 0)));
+ if(uc) {
+ failf(data, "URL rejected: %s", curl_url_strerror(uc));
+ result = Curl_uc_to_curlcode(uc);
+ goto out;
+ }
+
+ /* after it was parsed, get the generated normalized version */
+ uc = curl_url_get(uh, CURLUPART_URL, &newurl, CURLU_GET_EMPTY);
+ if(uc) {
+ result = Curl_uc_to_curlcode(uc);
+ goto out;
+ }
+ Curl_bufref_set(&data->state.url, newurl, 0, curl_free);
+ }
+
+#ifdef USE_IPV6
+ scope_id = data->set.scope_id;
+#endif
+
+ /* `uh` is now as the connection should use it, probably. */
+ result = Curl_peer_from_url(uh, data, port_override, scope_id,
+ &data->state.up, &data->state.origin);
+ if(result)
+ goto out;
+ /* The origin might get changed when HSTS applies */
+ result = hsts_upgrade(data, uh, port_override, scope_id);
+ if(result)
+ goto out;
+
+ /* When the transfers initial_origin is not set, this is the initial
+ * request. Remember this starting point. */
+ if(!data->state.initial_origin)
+ Curl_peer_link(&data->state.initial_origin, data->state.origin);
+
+ uc = curl_url_get(uh, CURLUPART_PATH, &data->state.up.path, CURLU_URLENCODE);
+ if(uc) {
+ result = Curl_uc_to_curlcode(uc);
+ goto out;
+ }
+ uc = curl_url_get(uh, CURLUPART_QUERY, &data->state.up.query,
+ CURLU_GET_EMPTY);
+ if(uc && (uc != CURLUE_NO_QUERY)) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto out;
+ }
+
+ uc = curl_url_get(uh, CURLUPART_OPTIONS, &data->state.up.options,
+ CURLU_URLDECODE);
+ if(uc && (uc != CURLUE_NO_OPTIONS)) {
+ result = Curl_uc_to_curlcode(uc);
+ goto out;
+ }
+
+ result = url_set_data_creds(data, uh);
+ if(result)
+ goto out;
+
+out:
+ return result;
+}
+
/**
* Find an existing connection for the transfer or create a new one.
* Returns
infof(data, "Reusing existing %s: connection%s with %s %s",
conn->given->name,
tls_upgraded ? " (upgraded to SSL)" : "",
- conn->bits.proxy ? "proxy" : "host",
+ (conn->socks_proxy.peer || conn->http_proxy.peer) ? "proxy" : "host",
conn->socks_proxy.peer ? conn->socks_proxy.peer->user_hostname :
conn->http_proxy.peer ? conn->http_proxy.peer->user_hostname :
conn->origin->hostname);
#ifdef CURL_DISABLE_PROXY
0
#else
- data->conn->bits.proxy
+ (data->conn->socks_proxy.peer || data->conn->http_proxy.peer)
#endif
;
CURLcode Curl_connect(struct Curl_easy *data, bool *pconnected)
{
CURLcode result;
- struct connectdata *conn;
+ struct connectdata *conn = NULL;
*pconnected = FALSE;
/* Set the request to virgin state based on transfer settings */
Curl_req_hard_reset(&data->req, data);
+ /* Determine the origin of the transfer and what credentials to use */
+ result = url_set_data_origin_and_creds(data);
+ if(result)
+ goto out;
+ if(!data->state.origin) { /* just make really sure */
+ DEBUGASSERT(0);
+ result = CURLE_FAILED_INIT;
+ goto out;
+ }
/* Get or create a connection for the transfer. */
result = url_find_or_create_conn(data);
conn = data->conn;
-
if(result)
goto out;
+ if(!data->conn) { /* just make really sure */
+ DEBUGASSERT(0);
+ result = CURLE_FAILED_INIT;
+ goto out;
+ }
- DEBUGASSERT(conn);
Curl_pgrsTime(data, TIMER_POSTQUEUE);
if(conn->bits.reuse) {
if(conn->attached_xfers > 1)
#include "hostip.h"
#include "hash.h"
#include "peer.h"
+#include "proxy.h"
#include "splay.h"
#include "curlx/dynbuf.h"
#include "bufref.h"
struct ConnectBits {
BIT(connect_only);
#ifndef CURL_DISABLE_PROXY
- BIT(httpproxy); /* if set, this transfer is done through an HTTP proxy */
- BIT(socksproxy); /* if set, this transfer is done through a socks proxy */
- BIT(tunnel_proxy); /* if CONNECT is used to "tunnel" through the proxy.
- This is implicit when SSL-protocols are used through
- proxies, but can also be enabled explicitly by
- apps */
- BIT(proxy); /* if set, this transfer is done through a proxy - any type */
+ BIT(origin_is_proxy); /* if set, the connection's origin is a proxy */
#endif
/* always modify bits.close with the connclose() and connkeep() macros! */
BIT(close); /* if set, we close the connection after this request */
((x)->transport == TRNSPRT_UDP) || \
((x)->transport == TRNSPRT_QUIC))
-struct proxy_info {
- struct Curl_peer *peer; /* proxy to this peer */
- struct Curl_creds *creds; /* use these credentials, maybe NULL */
- uint8_t proxytype; /* what kind of proxy that is in use */
-};
-
/*
* The connectdata struct contains all fields and variables that should be
* unique for an entire connection.
session handle without disturbing information which is still alive, and
that might be reused, in the connection pool. */
struct ip_quadruple primary;
- int conn_remote_port; /* this is the "remote port", which is the port
- number of the used URL, independent of proxy or
- not */
const char *conn_scheme;
uint32_t conn_protocol;
struct curl_certinfo certs; /* info about the certs. Asked for with
Credentials from CURLOPT_* are only valid for this origin.
Always set once a transfer starts searching for connections. */
struct Curl_peer *initial_origin;
+ /* Current origin of the transfer, changes to origin of follow
+ * requests. */
+ struct Curl_peer *origin;
int os_errno; /* filled in with errno whenever an error occurs */
int requests; /* request counter: redirects + authentication retakes */
curl_msnprintf(&HA1_hex[2 * i], 3, "%02x", digest[i]);
/* Generate our SPN */
- spn = Curl_auth_build_spn(service, data->conn->origin->hostname, NULL);
+ spn = Curl_auth_build_spn(service, data->state.origin->hostname, NULL);
if(!spn)
return CURLE_OUT_OF_MEMORY;
return CURLE_OUT_OF_MEMORY;
/* Generate our SPN */
- spn = Curl_auth_build_spn(service, data->conn->origin->hostname, NULL);
+ spn = Curl_auth_build_spn(service, data->state.origin->hostname, NULL);
if(!spn) {
curlx_free(output_token);
return CURLE_OUT_OF_MEMORY;
*/
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,
return CURLE_URL_MALFORMAT;
}
#ifndef CURL_DISABLE_PROXY
- if(conn->bits.socksproxy) {
+ if(conn->socks_proxy.peer) {
failf(data, "HTTP/3 is not supported over a SOCKS proxy");
return CURLE_URL_MALFORMAT;
}
else:
r.check_response(http_status=200)
+ # download via socks to https: proxy (no tunnel)
+ @pytest.mark.parametrize("sproto", ['socks4', 'socks5'])
+ @pytest.mark.parametrize("proto", Env.http_h1_h2_protos())
+ def test_40_02b_socks_https_proxy(self, env: Env, sproto, proto, danted: Dante, httpd):
+ if proto == 'h2' and not env.curl_uses_lib('nghttp2'):
+ pytest.skip('only supported with nghttp2')
+ curl = CurlClient(env=env, socks_args=[
+ f'--{sproto}', f'127.0.0.1:{danted.port}'
+ ])
+ url = f'http://localhost:{env.http_port}/data.json'
+ xargs = curl.get_proxy_args(proto=proto, tunnel=False)
+ r = curl.http_download(urls=[url], alpn_proto=proto, with_stats=True,
+ extra_args=xargs)
+ r.check_response(http_status=200)
+ exp_http_version = '2' if proto == 'h2' else '1.1'
+ assert r.stats[0]['proxy_used'] == 1, f'{r}'
+ assert r.stats[0]['http_version'] == exp_http_version, f'{r}'
+
@pytest.mark.parametrize("sproto", ['socks4', 'socks5'])
@pytest.mark.parametrize("proto", Env.http_h1_h2_protos())
def test_40_03_dl_serial(self, env: Env, httpd, danted, proto, sproto):
*
***************************************************************************/
#include "unitcheck.h"
-#include "noproxy.h"
+#include "proxy.h"
static CURLcode test_unit1614(const char *arg)
{
}
#endif
for(i = 0; list[i].a; i++) {
- bool match = Curl_check_noproxy(list[i].a, list[i].n);
+ bool match = proxy_check_noproxy(list[i].a, list[i].n);
if(match != list[i].match) {
curl_mfprintf(stderr, "%s in %s should %smatch\n",
list[i].a, list[i].n,