lib: transfer origin and proxy handling
No binary (build failed)
—
(log yok)
| @@ -38,9 +38,10 @@ suitable connection. For an `easy_perform()` this may happen several times | |
| if, for example, http redirects are followed. | |
| When an `easy_perform()` starts, the transfer's `data->state.initial_origin` | |
| - | peer is cleared. When creating the connection, `conn->origin` is calculated |
| - | (e.g. who the request talks to). If `data->state.initial_origin` is not |
| - | set, the first `conn->origin` is linked there. Now `libcurl` knows where |
| + | peer is cleared. When creating the connection, `data->state.origin` is |
| + | calculated (e.g. who the request talks to). If `data->state.initial_origin` |
| + | is not set, the first `data->state.origin` is linked there. |
| + | Now `libcurl` knows where |
| the transfer initially talked to on all possible subsequent requests. | |
| Credential information from `CURLOPT_*` settings is only applicable for the | |
| @@ -20,13 +20,25 @@ A `peer` in curl internals is represented by a `struct Curl_peer`. It has the fo | |
| A peer, in short, is a communication endpoint. | |
| + | ## peers and transfers |
| + | |
| + | The peer a transfer, e.g. easy handle, works against is determined at the |
| + | start of each request. It is kept in `data->state.origin`. For the first |
| + | request done in a `curl_easy_perform()` or equivalent, this origin is |
| + | linked to `data->state.initial_origin`. This allows checks if properties |
| + | of `data->set.*` should apply to a request or not. |
| + | |
| + | `data->state.origin` is relevant for cookie processing, signing requests |
| + | and other request/response based processing. |
| + | |
| ## peers and connections | |
| A network connection always goes *somewhere*. That *somewhere* is called | |
| the `origin` of the connection (e.g. the source of responses/downloads). | |
| It is kept in `conn->origin` and is always present in a connection. | |
| - | The `origin` is *logical* endpoint a connection talks to. |
| + | The `origin` is *logical* endpoint a connection talks to. In most |
| + | configurations it is the same as `data->state.origin` (see proxies below). |
| For most connections, the `origin` is connected to *directly*. It | |
| can be directed to another peer, however. | |
| @@ -56,6 +68,19 @@ might connect as: | |
| 5. curl --> socks_proxy.peer --> http_proxy.peer --> conn->via_peer/origin | |
| ``` | |
| + | A `conn->(socks|http)_proxy.peer` is only ever present when the proxy |
| + | is in use and `NULL` otherwise. |
| + | |
| + | SOCKS proxies are always used for tunneling, either to the origin or |
| + | the HTTP proxy. They operate in a connection filter. |
| + | |
| + | HTTP proxies can operate in two modes: tunneling or forwarding. When tunneling, |
| + | they also operate in a connection filter. In forwarding mode however, they |
| + | become the `origin` the connection talks to. |
| + | |
| + | Therefore, connections that talk to a forwarding HTTP proxy have `conn->origin` |
| + | set to `conn->http_proxy.peer` and `conn->bits.origin_is_proxy` is set. |
| + | |
| The connection filter `SETUP`, that assembles the filters for a connection, | |
| figures out which peer to pass to which filter in order to make it all work. | |
| The individual filters get passed a specific peer and do not need be concerned | |
| @@ -241,7 +241,6 @@ LIB_CFILES = \ | |
| multi_ev.c \ | |
| multi_ntfy.c \ | |
| netrc.c \ | |
| - | noproxy.c \ |
| openldap.c \ | |
| parsedate.c \ | |
| peer.c \ | |
| @@ -249,6 +248,7 @@ LIB_CFILES = \ | |
| pop3.c \ | |
| progress.c \ | |
| protocol.c \ | |
| + | proxy.c \ |
| psl.c \ | |
| rand.c \ | |
| ratelimit.c \ | |
| @@ -375,13 +375,13 @@ LIB_HFILES = \ | |
| multi_ntfy.h \ | |
| multiif.h \ | |
| netrc.h \ | |
| - | noproxy.h \ |
| parsedate.h \ | |
| peer.h \ | |
| pingpong.h \ | |
| pop3.h \ | |
| progress.h \ | |
| protocol.h \ | |
| + | proxy.h \ |
| psl.h \ | |
| rand.h \ | |
| ratelimit.h \ | |
| @@ -713,14 +713,15 @@ static CURLcode is_connected(struct Curl_cfilter *cf, | |
| return CURLE_FAILED_INIT; | |
| #ifndef CURL_DISABLE_PROXY | |
| - | if(conn->bits.socksproxy) |
| + | if(conn->socks_proxy.peer) |
| proxy_peer = conn->socks_proxy.peer; | |
| - | else if(conn->bits.httpproxy) |
| + | else if(conn->http_proxy.peer) |
| proxy_peer = conn->http_proxy.peer; | |
| #endif | |
| viamsg[0] = 0; | |
| - | if((peer != conn->origin) && (peer != proxy_peer)) { |
| + | if(!Curl_peer_equal(peer, conn->origin) && |
| + | !Curl_peer_equal(peer, proxy_peer)) { |
| #ifdef USE_UNIX_SOCKETS | |
| if(peer->unix_socket) | |
| curl_msnprintf(viamsg, sizeof(viamsg), " over unix://%s", | |
| @@ -1634,8 +1634,6 @@ static void cf_socket_update_data(struct Curl_cfilter *cf, | |
| if(cf->connected && (cf->sockindex == FIRSTSOCKET)) { | |
| struct cf_socket_ctx *ctx = cf->ctx; | |
| data->info.primary = ctx->ip; | |
| - | /* not sure if this is redundant... */ |
| - | data->info.conn_remote_port = cf->conn->origin->port; |
| } | |
| } | |
| @@ -660,6 +660,31 @@ bool Curl_conn_is_ip_connected(struct Curl_easy *data, int sockindex) | |
| return FALSE; | |
| } | |
| + | #ifndef CURL_DISABLE_PROXY |
| + | static bool cf_is_tunneling(struct Curl_cfilter *cf) |
| + | { |
| + | for(; cf; cf = cf->next) { |
| + | if((cf->cft->flags & CF_TYPE_PROXY)) |
| + | return TRUE; |
| + | } |
| + | return FALSE; |
| + | } |
| + | |
| + | bool Curl_conn_is_tunneling(struct connectdata *conn, int sockindex) |
| + | { |
| + | if(!CONN_SOCK_IDX_VALID(sockindex)) |
| + | return FALSE; |
| + | return conn ? cf_is_tunneling(conn->cfilter[sockindex]) : FALSE; |
| + | } |
| + | #else |
| + | bool Curl_conn_is_tunneling(struct connectdata *conn, int sockindex) |
| + | { |
| + | (void)conn; |
| + | (void)sockindex; |
| + | return FALSE; |
| + | } |
| + | #endif /* CURL_DISABLE_PROXY */ |
| + | |
| static bool cf_is_ssl(struct Curl_cfilter *cf) | |
| { | |
| for(; cf; cf = cf->next) { | |
| @@ -393,6 +393,10 @@ bool Curl_conn_is_ip_connected(struct Curl_easy *data, int sockindex); | |
| */ | |
| bool Curl_conn_is_ssl(struct connectdata *conn, int sockindex); | |
| + | /* Determine if the connection has one or more proxy filters. |
| + | * e.g. is tunneling. */ |
| + | bool Curl_conn_is_tunneling(struct connectdata *conn, int sockindex); |
| + | |
| /* | |
| * Fill `info` with information about the TLS instance securing the connection | |
| * when available, otherwise e.g. when Curl_conn_is_ssl() is FALSE, return | |
| @@ -319,11 +319,11 @@ static CURLcode cf_setup_add_socks(struct Curl_cfilter *cf, | |
| { | |
| struct cf_setup_ctx *ctx = cf->ctx; | |
| CURLcode result = CURLE_OK; | |
| - | if(ctx->state < CF_SETUP_CNNCT_SOCKS && cf->conn->bits.socksproxy) { |
| + | if(ctx->state < CF_SETUP_CNNCT_SOCKS && cf->conn->socks_proxy.peer) { |
| /* Add a SOCKS proxy to go through `first_peer` to `second_peer`*/ | |
| struct Curl_peer *second_peer; | |
| - | if(cf->conn->bits.httpproxy) |
| + | if(cf->conn->http_proxy.peer) |
| second_peer = cf->conn->http_proxy.peer; | |
| else | |
| second_peer = Curl_conn_get_destination(cf->conn, cf->sockindex); | |
| @@ -353,9 +353,14 @@ static CURLcode cf_setup_add_http_proxy(struct Curl_cfilter *cf, | |
| struct cf_setup_ctx *ctx = cf->ctx; | |
| CURLcode result = CURLE_OK; | |
| - | if(ctx->state < CF_SETUP_CNNCT_HTTP_PROXY && cf->conn->bits.httpproxy) { |
| + | if(ctx->state < CF_SETUP_CNNCT_HTTP_PROXY && |
| + | cf->conn->http_proxy.peer && !cf->conn->bits.origin_is_proxy) { |
| + | struct Curl_peer *peer = cf->conn->http_proxy.peer; |
| + | struct Curl_peer *tunnel_peer = |
| + | Curl_conn_get_destination(cf->conn, cf->sockindex); |
| + | |
| #ifdef USE_SSL | |
| - | if(IS_HTTPS_PROXY(cf->conn->http_proxy.proxytype) && |
| + | if(CURL_PROXY_IS_HTTPS(cf->conn->http_proxy.proxytype) && |
| !Curl_conn_is_ssl(cf->conn, cf->sockindex)) { | |
| result = Curl_cf_ssl_proxy_insert_after( | |
| cf, data, cf->conn->http_proxy.peer); | |
| @@ -368,20 +373,15 @@ static CURLcode cf_setup_add_http_proxy(struct Curl_cfilter *cf, | |
| } | |
| #endif /* USE_SSL */ | |
| - | if(cf->conn->bits.tunnel_proxy) { |
| - | struct Curl_peer *peer = cf->conn->http_proxy.peer; |
| - | struct Curl_peer *tunnel_peer; /* where HTTP should tunnel to */ |
| - | tunnel_peer = Curl_conn_get_destination(cf->conn, cf->sockindex); |
| - | result = Curl_cf_http_proxy_insert_after( |
| - | cf, data, peer, tunnel_peer, |
| - | ctx->transport, cf->conn->http_proxy.proxytype); |
| - | if(result) { |
| - | CURL_TRC_CF(data, cf, "adding HTTP proxy tunnel filter failed -> %d", |
| - | (int)result); |
| - | return result; |
| - | } |
| - | CURL_TRC_CF(data, cf, "added HTTP proxy tunnel filter"); |
| + | result = Curl_cf_http_proxy_insert_after( |
| + | cf, data, peer, tunnel_peer, |
| + | ctx->transport, cf->conn->http_proxy.proxytype); |
| + | if(result) { |
| + | CURL_TRC_CF(data, cf, "adding HTTP proxy tunnel filter failed -> %d", |
| + | (int)result); |
| + | return result; |
| } | |
| + | CURL_TRC_CF(data, cf, "added HTTP proxy tunnel filter"); |
| ctx->state = CF_SETUP_CNNCT_HTTP_PROXY; | |
| } | |
| return result; | |
| @@ -424,11 +424,11 @@ static CURLcode cf_setup_add_ip_happy(struct Curl_cfilter *cf, | |
| return CURLE_FAILED_INIT; | |
| #if !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP) | |
| - | if(cf->conn->bits.httpproxy && cf->conn->bits.tunnel_proxy) { |
| + | if(cf->conn->http_proxy.peer && !cf->conn->bits.origin_is_proxy) { |
| first_transport = | |
| Curl_http_proxy_transport(cf->conn->http_proxy.proxytype); | |
| tunnel_peer = Curl_conn_get_destination(cf->conn, cf->sockindex); | |
| - | if((first_transport == TRNSPRT_QUIC) && (cf->conn->bits.socksproxy)) { |
| + | if((first_transport == TRNSPRT_QUIC) && cf->conn->socks_proxy.peer) { |
| failf(data, "HTTP/3 proxy not possible via SOCKS"); | |
| return CURLE_UNSUPPORTED_PROTOCOL; | |
| } | |
| @@ -472,8 +472,8 @@ static CURLcode cf_setup_add_origin_filters(struct Curl_cfilter *cf, | |
| /* Wanting QUIC with a HTTP tunneling filter, we now need to add | |
| * the QUIC filter on top. Without tunneling, this has already | |
| * happened in the Happy Eyeball filter. */ | |
| - | if(ctx->transport == TRNSPRT_QUIC && cf->conn->bits.httpproxy && |
| - | cf->conn->bits.tunnel_proxy) { |
| + | if(ctx->transport == TRNSPRT_QUIC && |
| + | cf->conn->http_proxy.peer && !cf->conn->bits.origin_is_proxy) { |
| struct Curl_peer *origin = Curl_conn_get_origin(cf->conn, cf->sockindex); | |
| struct Curl_peer *peer = | |
| Curl_conn_get_destination(cf->conn, cf->sockindex); | |
| @@ -498,13 +498,21 @@ static CURLcode cf_setup_add_origin_filters(struct Curl_cfilter *cf, | |
| (ctx->ssl_mode != CURL_CF_SSL_DISABLE && | |
| cf->conn->scheme->flags & PROTOPT_SSL)) && /* we want SSL */ | |
| !Curl_conn_is_ssl(cf->conn, cf->sockindex)) { /* it is missing */ | |
| - | /* Another FTP quirk: when adding SSL verification, to a DATA |
| - | * connection, always verify against the control's origin */ |
| - | struct Curl_peer *origin = Curl_conn_get_origin(cf->conn, FIRSTSOCKET); |
| - | struct Curl_peer *peer = |
| - | Curl_conn_get_destination(cf->conn, cf->sockindex); |
| - | result = Curl_cf_ssl_insert_after(cf, data, origin, peer); |
| + | #ifndef CURL_DISABLE_PROXY |
| + | if(cf->conn->bits.origin_is_proxy) { |
| + | result = Curl_cf_ssl_proxy_insert_after(cf, data, cf->conn->origin); |
| + | } |
| + | else |
| + | #endif |
| + | { |
| + | /* Another FTP quirk: when adding SSL verification, to a DATA |
| + | * connection, always verify against the control's origin */ |
| + | struct Curl_peer *origin = Curl_conn_get_origin(cf->conn, FIRSTSOCKET); |
| + | struct Curl_peer *peer = |
| + | Curl_conn_get_destination(cf->conn, cf->sockindex); |
| + | result = Curl_cf_ssl_insert_after(cf, data, origin, peer); |
| + | } |
| if(result) { | |
| CURL_TRC_CF(data, cf, "adding SSL filter for origin failed -> %d", | |
| (int)result); | |
| @@ -766,10 +774,6 @@ struct Curl_peer *Curl_conn_get_origin(struct connectdata *conn, | |
| struct Curl_peer *Curl_conn_get_destination(struct connectdata *conn, | |
| int sockindex) | |
| { | |
| - | #ifndef CURL_DISABLE_PROXY |
| - | if(conn->http_proxy.peer && !conn->bits.tunnel_proxy) |
| - | return conn->http_proxy.peer; |
| - | #endif |
| return (sockindex == SECONDARYSOCKET) ? | |
| (conn->via_peer2 ? conn->via_peer2 : conn->origin2) : | |
| (conn->via_peer ? conn->via_peer : conn->origin); | |
| @@ -1983,11 +1983,7 @@ static CURLcode ftp_epsv_disable(struct Curl_easy *data, | |
| { | |
| CURLcode result = CURLE_OK; | |
| - | if(conn->bits.ipv6 |
| - | #ifndef CURL_DISABLE_PROXY |
| - | && !(conn->bits.tunnel_proxy || conn->bits.socksproxy) |
| - | #endif |
| - | ) { |
| + | if(conn->bits.ipv6 && !Curl_conn_is_tunneling(conn, FIRSTSOCKET)) { |
| /* We cannot disable EPSV when doing IPv6, so this is instead a fail */ | |
| failf(data, "Failed EPSV attempt, exiting"); | |
| return CURLE_WEIRD_SERVER_REPLY; | |
| @@ -2019,7 +2015,7 @@ static CURLcode ftp_control_addr_dup(struct Curl_easy *data, char **newhostp) | |
| the effective control connection address is the proxy address, | |
| not the ftp host. */ | |
| #ifndef CURL_DISABLE_PROXY | |
| - | if(conn->bits.tunnel_proxy || conn->bits.socksproxy) |
| + | if(Curl_conn_is_tunneling(conn, FIRSTSOCKET)) |
| *newhostp = curlx_strdup(conn->origin->hostname); | |
| else | |
| #endif | |
| @@ -154,7 +154,7 @@ char *Curl_checkProxyheaders(struct Curl_easy *data, | |
| { | |
| struct curl_slist *head; | |
| - | for(head = (conn->bits.proxy && data->set.sep_headers) ? |
| + | for(head = (conn->http_proxy.peer && data->set.sep_headers) ? |
| data->set.proxyheaders : data->set.headers; | |
| head; head = head->next) { | |
| if(curl_strnequal(head->data, thisheader, thislen) && | |
| @@ -783,7 +783,7 @@ CURLcode Curl_http_output_auth(struct Curl_easy *data, | |
| if( | |
| #ifndef CURL_DISABLE_PROXY | |
| - | (!conn->bits.httpproxy || !conn->http_proxy.creds) && |
| + | (!conn->http_proxy.peer || !conn->http_proxy.creds) && |
| #endif | |
| #ifdef USE_SPNEGO | |
| !(authhost->want & CURLAUTH_NEGOTIATE) && | |
| @@ -820,7 +820,7 @@ CURLcode Curl_http_output_auth(struct Curl_easy *data, | |
| #ifndef CURL_DISABLE_PROXY | |
| /* Send proxy authentication header if needed */ | |
| - | if(conn->bits.httpproxy && (!conn->bits.tunnel_proxy || is_connect)) { |
| + | if(conn->bits.origin_is_proxy || is_connect) { |
| result = output_auth_headers(data, conn, authproxy, request, | |
| path_and_query, TRUE); | |
| if(result) | |
| @@ -1736,8 +1736,7 @@ CURLcode Curl_add_custom_headers(struct Curl_easy *data, | |
| if(is_connect) | |
| proxy = HEADER_CONNECT; | |
| else | |
| - | proxy = data->conn->bits.httpproxy && !data->conn->bits.tunnel_proxy ? |
| - | HEADER_PROXY : HEADER_SERVER; |
| + | proxy = data->conn->bits.origin_is_proxy ? HEADER_PROXY : HEADER_SERVER; |
| switch(proxy) { | |
| case HEADER_SERVER: | |
| @@ -1998,8 +1997,9 @@ static CURLcode http_set_aptr_host(struct Curl_easy *data) | |
| #endif | |
| ptr = Curl_checkheaders(data, STRCONST("Host")); | |
| - | if(ptr && (!data->state.this_is_a_follow || |
| - | Curl_peer_equal(data->state.initial_origin, conn->origin))) { |
| + | if(ptr && |
| + | (!data->state.this_is_a_follow || |
| + | Curl_peer_equal(data->state.initial_origin, data->state.origin))) { |
| #ifndef CURL_DISABLE_COOKIES | |
| /* If we have a given custom Host: header, we extract the hostname in | |
| order to possibly use it for cookie reasons later on. We only allow the | |
| @@ -2043,18 +2043,19 @@ static CURLcode http_set_aptr_host(struct Curl_easy *data) | |
| } | |
| else { | |
| /* Use the hostname as present in the URL if it was IPv6. */ | |
| - | char *host = (conn->origin->user_hostname[0] == '[') ? |
| - | conn->origin->user_hostname : conn->origin->hostname; |
| + | char *host = (data->state.origin->user_hostname[0] == '[') ? |
| + | data->state.origin->user_hostname : data->state.origin->hostname; |
| if(((conn->given->protocol & (CURLPROTO_HTTPS | CURLPROTO_WSS)) && | |
| - | (conn->origin->port == PORT_HTTPS)) || |
| + | (data->state.origin->port == PORT_HTTPS)) || |
| ((conn->given->protocol & (CURLPROTO_HTTP | CURLPROTO_WS)) && | |
| - | (conn->origin->port == PORT_HTTP))) |
| + | (data->state.origin->port == PORT_HTTP))) |
| /* if(HTTPS on port 443) OR (HTTP on port 80) then do not include | |
| the port number in the host string */ | |
| aptr->host = curl_maprintf("Host: %s\r\n", host); | |
| else | |
| - | aptr->host = curl_maprintf("Host: %s:%d\r\n", host, conn->origin->port); |
| + | aptr->host = curl_maprintf("Host: %s:%d\r\n", |
| + | host, data->state.origin->port); |
| if(!aptr->host) | |
| /* without Host: we cannot make a nice request */ | |
| @@ -2082,7 +2083,7 @@ static CURLcode http_target(struct Curl_easy *data, | |
| } | |
| #ifndef CURL_DISABLE_PROXY | |
| - | if(conn->bits.httpproxy && !conn->bits.tunnel_proxy) { |
| + | if(conn->bits.origin_is_proxy) { |
| /* Using a proxy but does not tunnel through it */ | |
| /* The path sent to the proxy is in fact the entire URL, but if the remote | |
| @@ -2096,8 +2097,8 @@ static CURLcode http_target(struct Curl_easy *data, | |
| if(!h) | |
| return CURLE_OUT_OF_MEMORY; | |
| - | if(conn->origin->user_hostname != conn->origin->hostname) { |
| - | uc = curl_url_set(h, CURLUPART_HOST, conn->origin->hostname, 0); |
| + | if(data->state.origin->user_hostname != data->state.origin->hostname) { |
| + | uc = curl_url_set(h, CURLUPART_HOST, data->state.origin->hostname, 0); |
| if(uc) { | |
| curl_url_cleanup(h); | |
| return CURLE_OUT_OF_MEMORY; | |
| @@ -2541,7 +2542,7 @@ static CURLcode http_cookies(struct Curl_easy *data, | |
| if(data->cookies && data->state.cookie_engine) { | |
| bool okay; | |
| const char *host = data->req.cookiehost ? | |
| - | data->req.cookiehost : data->conn->origin->hostname; |
| + | data->req.cookiehost : data->state.origin->hostname; |
| Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); | |
| result = Curl_cookie_getlist(data, data->conn, &okay, host, &list); | |
| if(!result && okay) { | |
| @@ -2728,8 +2729,7 @@ static CURLcode http_check_new_conn(struct Curl_easy *data) | |
| alpn = Curl_conn_get_alpn_negotiated(data, conn); | |
| if(alpn && !strcmp("h3", alpn)) { | |
| #ifndef CURL_DISABLE_PROXY | |
| - | if((Curl_conn_http_version(data, conn) == 30) || !conn->bits.proxy || |
| - | conn->bits.tunnel_proxy) |
| + | if(!conn->bits.origin_is_proxy) |
| #endif | |
| DEBUGASSERT(Curl_conn_http_version(data, conn) == 30); | |
| info_version = "HTTP/3"; | |
| @@ -2737,7 +2737,7 @@ static CURLcode http_check_new_conn(struct Curl_easy *data) | |
| else if(alpn && !strcmp("h2", alpn)) { | |
| #ifndef CURL_DISABLE_PROXY | |
| if((Curl_conn_http_version(data, conn) != 20) && | |
| - | conn->bits.proxy && !conn->bits.tunnel_proxy) { |
| + | conn->bits.origin_is_proxy) { |
| result = Curl_http2_switch(data); | |
| if(result) | |
| return result; | |
| @@ -2946,8 +2946,7 @@ static CURLcode http_add_hd(struct Curl_easy *data, | |
| #ifndef CURL_DISABLE_PROXY | |
| case H1_HD_PROXY_CONNECTION: | |
| - | if(conn->bits.httpproxy && |
| - | !conn->bits.tunnel_proxy && |
| + | if(conn->bits.origin_is_proxy && |
| !Curl_checkheaders(data, STRCONST("Proxy-Connection")) && | |
| !Curl_checkProxyheaders(data, data->conn, STRCONST("Proxy-Connection"))) | |
| result = curlx_dyn_add(req, "Proxy-Connection: Keep-Alive\r\n"); | |
| @@ -3190,7 +3189,6 @@ static CURLcode http_header_a(struct Curl_easy *data, | |
| { | |
| #ifndef CURL_DISABLE_ALTSVC | |
| const char *v; | |
| - | struct connectdata *conn = data->conn; |
| v = (data->asi && | |
| (Curl_xfer_is_secure(data) || | |
| #ifdef DEBUGBUILD | |
| @@ -3205,8 +3203,9 @@ static CURLcode http_header_a(struct Curl_easy *data, | |
| struct SingleRequest *k = &data->req; | |
| enum alpnid id = (k->httpversion == 30) ? ALPN_h3 : | |
| (k->httpversion == 20) ? ALPN_h2 : ALPN_h1; | |
| - | return Curl_altsvc_parse(data, data->asi, v, id, conn->origin->hostname, |
| - | curlx_uitous((unsigned int)conn->origin->port)); |
| + | return Curl_altsvc_parse( |
| + | data, data->asi, v, id, data->state.origin->hostname, |
| + | curlx_uitous((unsigned int)data->state.origin->port)); |
| } | |
| #else | |
| (void)data; | |
| @@ -3424,7 +3423,7 @@ static CURLcode http_header_p(struct Curl_easy *data, | |
| const char *v = HD_VAL(hd, hdlen, "Proxy-Connection:"); | |
| if(v) { | |
| struct connectdata *conn = data->conn; | |
| - | if((k->httpversion == 10) && conn->bits.httpproxy && |
| + | if((k->httpversion == 10) && conn->http_proxy.peer && |
| HD_IS_AND_SAYS(hd, hdlen, "Proxy-Connection:", "keep-alive")) { | |
| /* | |
| * When an HTTP/1.0 reply comes when using a proxy, the | |
| @@ -3435,7 +3434,7 @@ static CURLcode http_header_p(struct Curl_easy *data, | |
| connkeep(conn, "Proxy-Connection keep-alive"); /* do not close */ | |
| infof(data, "HTTP/1.0 proxy connection set to keep alive"); | |
| } | |
| - | else if((k->httpversion == 11) && conn->bits.httpproxy && |
| + | else if((k->httpversion == 11) && conn->http_proxy.peer && |
| HD_IS_AND_SAYS(hd, hdlen, "Proxy-Connection:", "close")) { | |
| /* | |
| * We get an HTTP/1.1 response from a proxy and it says it will | |
| @@ -3533,7 +3532,7 @@ static CURLcode http_header_s(struct Curl_easy *data, | |
| /* If there is a custom-set Host: name, use it here, or else use | |
| * real peer hostname. */ | |
| const char *host = data->req.cookiehost ? | |
| - | data->req.cookiehost : conn->origin->hostname; |
| + | data->req.cookiehost : data->state.origin->hostname; |
| const bool secure_context = Curl_secure_context(conn, host); | |
| CURLcode result; | |
| Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); | |
| @@ -3556,8 +3555,8 @@ static CURLcode http_header_s(struct Curl_easy *data, | |
| ) | |
| ) ? HD_VAL(hd, hdlen, "Strict-Transport-Security:") : NULL; | |
| if(v) { | |
| - | CURLcode result = |
| - | Curl_hsts_parse(data->hsts, conn->origin->hostname, v); |
| + | CURLcode result = Curl_hsts_parse( |
| + | data->hsts, data->state.origin->hostname, v); |
| if(result) { | |
| if(result == CURLE_OUT_OF_MEMORY) | |
| return result; | |
| @@ -1403,7 +1403,7 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame, | |
| struct Curl_cfilter *cf = userp; | |
| struct cf_h2_ctx *ctx = cf->ctx; | |
| struct h2_stream_ctx *stream; | |
| - | struct Curl_easy *data_s; |
| + | struct Curl_easy *data; |
| int32_t stream_id = frame->hd.stream_id; | |
| CURLcode result; | |
| (void)flags; | |
| @@ -1411,15 +1411,15 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame, | |
| DEBUGASSERT(stream_id); /* should never be a zero stream ID here */ | |
| /* get the stream from the hash based on Stream ID */ | |
| - | data_s = nghttp2_session_get_stream_user_data(session, stream_id); |
| - | if(!GOOD_EASY_HANDLE(data_s)) |
| + | data = nghttp2_session_get_stream_user_data(session, stream_id); |
| + | if(!GOOD_EASY_HANDLE(data)) |
| /* Receiving a Stream ID not in the hash should not happen, this is an | |
| internal error more than anything else! */ | |
| return NGHTTP2_ERR_CALLBACK_FAILURE; | |
| - | stream = H2_STREAM_CTX(ctx, data_s); |
| + | stream = H2_STREAM_CTX(ctx, data); |
| if(!stream) { | |
| - | failf(data_s, "Internal NULL stream"); |
| + | failf(data, "Internal NULL stream"); |
| return NGHTTP2_ERR_CALLBACK_FAILURE; | |
| } | |
| @@ -1432,14 +1432,14 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame, | |
| !strncmp(HTTP_PSEUDO_AUTHORITY, (const char *)name, namelen)) { | |
| /* pseudo headers are lower case */ | |
| int rc = 0; | |
| - | char *check = curl_maprintf("%s:%d", cf->conn->origin->hostname, |
| - | cf->conn->origin->port); |
| + | char *check = curl_maprintf("%s:%d", data->state.origin->hostname, |
| + | data->state.origin->port); |
| if(!check) | |
| /* no memory */ | |
| return NGHTTP2_ERR_CALLBACK_FAILURE; | |
| if(!curl_strequal(check, (const char *)value) && | |
| - | ((cf->conn->origin->port != cf->conn->given->defport) || |
| - | !curl_strequal(cf->conn->origin->hostname, (const char *)value))) { |
| + | ((data->state.origin->port != cf->conn->given->defport) || |
| + | !curl_strequal(data->state.origin->hostname, (const char *)value))) { |
| /* This is push is not for the same authority that was asked for in | |
| * the URL. RFC 7540 section 8.2 says: "A client MUST treat a | |
| * PUSH_PROMISE for which the server is not authoritative as a stream | |
| @@ -1467,7 +1467,7 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame, | |
| char **headp; | |
| if(stream->push_headers_alloc > 1000) { | |
| /* this is beyond crazy many headers, bail out */ | |
| - | failf(data_s, "Too many PUSH_PROMISE headers"); |
| + | failf(data, "Too many PUSH_PROMISE headers"); |
| free_push_headers(stream); | |
| return NGHTTP2_ERR_CALLBACK_FAILURE; | |
| } | |
| @@ -1491,13 +1491,13 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame, | |
| if(stream->bodystarted) { | |
| /* This is a trailer */ | |
| - | CURL_TRC_CF(data_s, cf, "[%d] trailer: %.*s: %.*s", |
| + | CURL_TRC_CF(data, cf, "[%d] trailer: %.*s: %.*s", |
| stream->id, (int)namelen, name, (int)valuelen, value); | |
| result = Curl_dynhds_add(&stream->resp_trailers, | |
| (const char *)name, namelen, | |
| (const char *)value, valuelen); | |
| if(result) { | |
| - | cf_h2_header_error(cf, data_s, stream, result); |
| + | cf_h2_header_error(cf, data, stream, result); |
| return NGHTTP2_ERR_CALLBACK_FAILURE; | |
| } | |
| @@ -1512,14 +1512,14 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame, | |
| result = Curl_http_decode_status(&stream->status_code, | |
| (const char *)value, valuelen); | |
| if(result) { | |
| - | cf_h2_header_error(cf, data_s, stream, result); |
| + | cf_h2_header_error(cf, data, stream, result); |
| return NGHTTP2_ERR_CALLBACK_FAILURE; | |
| } | |
| hlen = curl_msnprintf(buffer, sizeof(buffer), HTTP_PSEUDO_STATUS ":%d\r", | |
| stream->status_code); | |
| - | result = Curl_headers_push(data_s, buffer, hlen, CURLH_PSEUDO); |
| + | result = Curl_headers_push(data, buffer, hlen, CURLH_PSEUDO); |
| if(result) { | |
| - | cf_h2_header_error(cf, data_s, stream, result); |
| + | cf_h2_header_error(cf, data, stream, result); |
| return NGHTTP2_ERR_CALLBACK_FAILURE; | |
| } | |
| curlx_dyn_reset(&ctx->scratch); | |
| @@ -1529,17 +1529,17 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame, | |
| if(!result) | |
| result = curlx_dyn_addn(&ctx->scratch, STRCONST(" \r\n")); | |
| if(!result) | |
| - | h2_xfer_write_resp_hd(cf, data_s, stream, curlx_dyn_ptr(&ctx->scratch), |
| + | h2_xfer_write_resp_hd(cf, data, stream, curlx_dyn_ptr(&ctx->scratch), |
| curlx_dyn_len(&ctx->scratch), FALSE); | |
| if(result) { | |
| - | cf_h2_header_error(cf, data_s, stream, result); |
| + | cf_h2_header_error(cf, data, stream, result); |
| return NGHTTP2_ERR_CALLBACK_FAILURE; | |
| } | |
| /* if we receive data for another handle, wake that up */ | |
| - | if(CF_DATA_CURRENT(cf) != data_s) |
| - | Curl_multi_mark_dirty(data_s); |
| + | if(CF_DATA_CURRENT(cf) != data) |
| + | Curl_multi_mark_dirty(data); |
| - | CURL_TRC_CF(data_s, cf, "[%d] status: HTTP/2 %03d", |
| + | CURL_TRC_CF(data, cf, "[%d] status: HTTP/2 %03d", |
| stream->id, stream->status_code); | |
| return 0; | |
| } | |
| @@ -1556,17 +1556,17 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame, | |
| if(!result) | |
| result = curlx_dyn_addn(&ctx->scratch, STRCONST("\r\n")); | |
| if(!result) | |
| - | h2_xfer_write_resp_hd(cf, data_s, stream, curlx_dyn_ptr(&ctx->scratch), |
| + | h2_xfer_write_resp_hd(cf, data, stream, curlx_dyn_ptr(&ctx->scratch), |
| curlx_dyn_len(&ctx->scratch), FALSE); | |
| if(result) { | |
| - | cf_h2_header_error(cf, data_s, stream, result); |
| + | cf_h2_header_error(cf, data, stream, result); |
| return NGHTTP2_ERR_CALLBACK_FAILURE; | |
| } | |
| /* if we receive data for another handle, wake that up */ | |
| - | if(CF_DATA_CURRENT(cf) != data_s) |
| - | Curl_multi_mark_dirty(data_s); |
| + | if(CF_DATA_CURRENT(cf) != data) |
| + | Curl_multi_mark_dirty(data); |
| - | CURL_TRC_CF(data_s, cf, "[%d] header: %.*s: %.*s", |
| + | CURL_TRC_CF(data, cf, "[%d] header: %.*s: %.*s", |
| stream->id, (int)namelen, name, (int)valuelen, value); | |
| return 0; /* 0 is successful */ | |
| @@ -2840,9 +2840,7 @@ bool Curl_http2_may_switch(struct Curl_easy *data) | |
| (data->state.http_neg.wanted & CURL_HTTP_V2x) && | |
| data->state.http_neg.h2_prior_knowledge) { | |
| #ifndef CURL_DISABLE_PROXY | |
| - | if(data->conn->bits.httpproxy && !data->conn->bits.tunnel_proxy) { |
| - | /* We do not support HTTP/2 proxies yet. Also it is debatable |
| - | whether or not this setting should apply to HTTP/2 proxies. */ |
| + | if(data->conn->bits.origin_is_proxy) { |
| infof(data, "Ignoring HTTP/2 prior knowledge due to proxy"); | |
| return FALSE; | |
| } | |
| @@ -1165,12 +1165,11 @@ fail: | |
| CURLcode Curl_output_aws_sigv4(struct Curl_easy *data) | |
| { | |
| CURLcode result = CURLE_OUT_OF_MEMORY; | |
| - | struct connectdata *conn = data->conn; |
| struct Curl_str provider0 = { NULL, 0 }; | |
| struct Curl_str provider1 = { NULL, 0 }; | |
| struct Curl_str region = { NULL, 0 }; | |
| struct Curl_str service = { NULL, 0 }; | |
| - | const char *hostname = conn->origin->hostname; |
| + | const char *hostname = data->state.origin->hostname; |
| char timestamp[TIMESTAMP_SIZE]; | |
| char date[9]; | |
| struct dynbuf canonical_headers; | |
| @@ -117,9 +117,9 @@ CURLcode Curl_output_digest(struct Curl_easy *data, | |
| #endif | |
| } | |
| else { | |
| - | DEBUGASSERT(data->conn->origin); |
| + | DEBUGASSERT(data->state.origin); |
| digest = &data->state.digest; | |
| - | digest_flush_stale(digest, data->conn->origin, data->state.creds); |
| + | digest_flush_stale(digest, data->state.origin, data->state.creds); |
| allocuserpwd = &data->req.hd_auth; | |
| creds = data->state.creds; | |
| authp = &data->state.authhost; | |
| @@ -73,7 +73,7 @@ CURLcode Curl_input_negotiate(struct Curl_easy *data, struct connectdata *conn, | |
| } | |
| else { | |
| creds = data->state.creds; | |
| - | host = conn->origin->hostname; |
| + | host = data->state.origin->hostname; |
| state = conn->http_negotiate_state; | |
| } | |
| @@ -149,7 +149,7 @@ CURLcode Curl_output_ntlm(struct Curl_easy *data, bool proxy) | |
| else { | |
| allocuserpwd = &data->req.hd_auth; | |
| creds = data->state.creds; | |
| - | hostname = conn->origin->hostname; |
| + | hostname = data->state.origin->hostname; |
| state = &conn->http_ntlm_state; | |
| authp = &data->state.authhost; | |
| } | |
| @@ -55,8 +55,7 @@ static CURLcode dynhds_add_custom(struct Curl_easy *data, | |
| else if(is_connect && is_udp) | |
| proxy = HEADER_CONNECT_UDP; | |
| else | |
| - | proxy = (conn->bits.httpproxy && !conn->bits.tunnel_proxy) ? |
| - | HEADER_PROXY : HEADER_SERVER; |
| + | proxy = conn->bits.origin_is_proxy ? HEADER_PROXY : HEADER_SERVER; |
| switch(proxy) { | |
| case HEADER_SERVER: | |
| @@ -75,15 +75,8 @@ CURLcode Curl_cf_http_proxy_insert_after(struct Curl_cfilter *cf_at, | |
| extern struct Curl_cftype Curl_cft_http_proxy; | |
| - | #endif /* !CURL_DISABLE_PROXY && !CURL_DISABLE_HTTP */ |
| - | |
| - | #define IS_HTTPS_PROXY(t) \ |
| - | (((t) == CURLPROXY_HTTPS) || \ |
| - | ((t) == CURLPROXY_HTTPS2) || \ |
| - | ((t) == CURLPROXY_HTTPS3)) |
| - | |
| - | #define IS_QUIC_PROXY(t) ((t) == CURLPROXY_HTTPS3) |
| - | |
| uint8_t Curl_http_proxy_transport(uint8_t proxytype); | |
| + | #endif /* !CURL_DISABLE_PROXY && !CURL_DISABLE_HTTP */ |
| + | |
| #endif /* HEADER_CURL_HTTP_PROXY_H */ | |
| @@ -1,266 +0,0 @@ | |
| - | /*************************************************************************** |
| - | * _ _ ____ _ |
| - | * Project ___| | | | _ \| | |
| - | * / __| | | | |_) | | |
| - | * | (__| |_| | _ <| |___ |
| - | * \___|\___/|_| \_\_____| |
| - | * |
| - | * Copyright (C) Daniel Stenberg, <[email protected]>, et al. |
| - | * |
| - | * This software is licensed as described in the file COPYING, which |
| - | * you should have received as part of this distribution. The terms |
| - | * are also available at https://curl.se/docs/copyright.html. |
| - | * |
| - | * You may opt to use, copy, modify, merge, publish, distribute and/or sell |
| - | * copies of the Software, and permit persons to whom the Software is |
| - | * furnished to do so, under the terms of the COPYING file. |
| - | * |
| - | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY |
| - | * KIND, either express or implied. |
| - | * |
| - | * SPDX-License-Identifier: curl |
| - | * |
| - | ***************************************************************************/ |
| - | #include "curl_setup.h" |
| - | |
| - | #ifndef CURL_DISABLE_PROXY |
| - | |
| - | #include "curlx/inet_pton.h" |
| - | #include "noproxy.h" |
| - | #include "curlx/strparse.h" |
| - | |
| - | #ifdef HAVE_NETINET_IN_H |
| - | #include <netinet/in.h> |
| - | #endif |
| - | |
| - | #ifdef HAVE_ARPA_INET_H |
| - | #include <arpa/inet.h> |
| - | #endif |
| - | |
| - | /* |
| - | * cidr4_match() returns TRUE if the given IPv4 address is within the |
| - | * specified CIDR address range. |
| - | * |
| - | * @unittest 1614 |
| - | */ |
| - | UNITTEST bool cidr4_match(const char *ipv4, /* 1.2.3.4 address */ |
| - | const char *network, /* 1.2.3.4 address */ |
| - | unsigned int bits); |
| - | UNITTEST bool cidr4_match(const char *ipv4, /* 1.2.3.4 address */ |
| - | const char *network, /* 1.2.3.4 address */ |
| - | unsigned int bits) |
| - | { |
| - | unsigned int address = 0; |
| - | unsigned int check = 0; |
| - | |
| - | if(bits > 32) |
| - | /* strange input */ |
| - | return FALSE; |
| - | |
| - | if(curlx_inet_pton(AF_INET, ipv4, &address) != 1) |
| - | return FALSE; |
| - | if(curlx_inet_pton(AF_INET, network, &check) != 1) |
| - | return FALSE; |
| - | |
| - | if(bits && (bits != 32)) { |
| - | unsigned int mask = 0xffffffff << (32 - bits); |
| - | unsigned int haddr = htonl(address); |
| - | unsigned int hcheck = htonl(check); |
| - | #if 0 |
| - | curl_mfprintf(stderr, "Host %s (%x) network %s (%x) " |
| - | "bits %u mask %x => %x\n", |
| - | ipv4, haddr, network, hcheck, bits, mask, |
| - | (haddr ^ hcheck) & mask); |
| - | #endif |
| - | if((haddr ^ hcheck) & mask) |
| - | return FALSE; |
| - | return TRUE; |
| - | } |
| - | return address == check; |
| - | } |
| - | |
| - | /* @unittest 1614 */ |
| - | UNITTEST bool cidr6_match(const char *ipv6, const char *network, |
| - | unsigned int bits); |
| - | UNITTEST bool cidr6_match(const char *ipv6, const char *network, |
| - | unsigned int bits) |
| - | { |
| - | #ifdef USE_IPV6 |
| - | unsigned int bytes; |
| - | unsigned int rest; |
| - | unsigned char address[16]; |
| - | unsigned char check[16]; |
| - | |
| - | if(!bits) |
| - | bits = 128; |
| - | |
| - | bytes = bits / 8; |
| - | rest = bits & 0x07; |
| - | if((bytes > 16) || ((bytes == 16) && rest)) |
| - | return FALSE; |
| - | if(curlx_inet_pton(AF_INET6, ipv6, address) != 1) |
| - | return FALSE; |
| - | if(curlx_inet_pton(AF_INET6, network, check) != 1) |
| - | return FALSE; |
| - | if(bytes && memcmp(address, check, bytes)) |
| - | return FALSE; |
| - | if(rest && ((address[bytes] ^ check[bytes]) & (0xff << (8 - rest)))) |
| - | return FALSE; |
| - | |
| - | return TRUE; |
| - | #else |
| - | (void)ipv6; |
| - | (void)network; |
| - | (void)bits; |
| - | return FALSE; |
| - | #endif |
| - | } |
| - | |
| - | enum nametype { |
| - | TYPE_HOST, |
| - | TYPE_IPV4, |
| - | TYPE_IPV6 |
| - | }; |
| - | |
| - | static bool match_host(const char *token, size_t tokenlen, |
| - | const char *name, size_t namelen) |
| - | { |
| - | bool match = FALSE; |
| - | |
| - | /* ignore trailing dots in the token to check */ |
| - | if(token[tokenlen - 1] == '.') |
| - | tokenlen--; |
| - | |
| - | if(tokenlen && (*token == '.')) { |
| - | /* ignore leading token dot as well */ |
| - | token++; |
| - | tokenlen--; |
| - | } |
| - | /* A: example.com matches 'example.com' |
| - | B: www.example.com matches 'example.com' |
| - | C: nonexample.com DOES NOT match 'example.com' |
| - | */ |
| - | if(tokenlen == namelen) |
| - | /* case A, exact match */ |
| - | match = curl_strnequal(token, name, namelen); |
| - | else if(tokenlen < namelen) { |
| - | /* case B, tailmatch domain */ |
| - | match = (name[namelen - tokenlen - 1] == '.') && |
| - | curl_strnequal(token, name + (namelen - tokenlen), tokenlen); |
| - | } |
| - | /* case C passes through, not a match */ |
| - | return match; |
| - | } |
| - | |
| - | static bool match_ip(int type, const char *token, size_t tokenlen, |
| - | const char *name) |
| - | { |
| - | char *slash; |
| - | unsigned int bits = 0; |
| - | char checkip[128]; |
| - | if(tokenlen >= sizeof(checkip)) |
| - | /* this cannot match */ |
| - | return FALSE; |
| - | /* copy the check name to a temp buffer */ |
| - | memcpy(checkip, token, tokenlen); |
| - | checkip[tokenlen] = 0; |
| - | |
| - | slash = strchr(checkip, '/'); |
| - | /* if the slash is part of this token, use it */ |
| - | if(slash) { |
| - | curl_off_t value; |
| - | const char *p = &slash[1]; |
| - | if(curlx_str_number(&p, &value, 128) || *p) |
| - | return FALSE; |
| - | /* a too large value is rejected in the cidr function below */ |
| - | bits = (unsigned int)value; |
| - | *slash = 0; /* null-terminate there */ |
| - | } |
| - | if(type == TYPE_IPV6) |
| - | return cidr6_match(name, checkip, bits); |
| - | else |
| - | return cidr4_match(name, checkip, bits); |
| - | } |
| - | |
| - | /**************************************************************** |
| - | * Checks if the host is in the noproxy list. returns TRUE if it matches and |
| - | * therefore the proxy should NOT be used. |
| - | ****************************************************************/ |
| - | bool Curl_check_noproxy(const char *name, const char *no_proxy) |
| - | { |
| - | /* |
| - | * If we do not have a hostname at all, like for example with a FILE |
| - | * transfer, we have nothing to interrogate the noproxy list with. |
| - | */ |
| - | if(!name || name[0] == '\0') |
| - | return FALSE; |
| - | |
| - | /* no_proxy=domain1.dom,host.domain2.dom |
| - | * (a comma-separated list of hosts which should |
| - | * not be proxied, or an asterisk to override |
| - | * all proxy variables) |
| - | */ |
| - | if(no_proxy && no_proxy[0]) { |
| - | const char *p = no_proxy; |
| - | size_t namelen; |
| - | char address[16]; |
| - | enum nametype type = TYPE_HOST; |
| - | if(!strcmp("*", no_proxy)) |
| - | return TRUE; |
| - | |
| - | /* NO_PROXY was specified and it was not only an asterisk */ |
| - | |
| - | /* Check if name is an IP address; if not, assume it being a hostname. */ |
| - | namelen = strlen(name); |
| - | if(curlx_inet_pton(AF_INET, name, &address) == 1) |
| - | type = TYPE_IPV4; |
| - | #ifdef USE_IPV6 |
| - | else if(curlx_inet_pton(AF_INET6, name, &address) == 1) |
| - | type = TYPE_IPV6; |
| - | #endif |
| - | else { |
| - | /* ignore trailing dots in the hostname */ |
| - | if(name[namelen - 1] == '.') |
| - | namelen--; |
| - | } |
| - | |
| - | while(*p) { |
| - | const char *token; |
| - | size_t tokenlen = 0; |
| - | |
| - | /* pass blanks */ |
| - | curlx_str_passblanks(&p); |
| - | |
| - | token = p; |
| - | /* pass over the pattern */ |
| - | while(*p && !ISBLANK(*p) && (*p != ',')) { |
| - | p++; |
| - | tokenlen++; |
| - | } |
| - | |
| - | if(tokenlen) { |
| - | bool match = FALSE; |
| - | if(type == TYPE_HOST) |
| - | match = match_host(token, tokenlen, name, namelen); |
| - | else |
| - | match = match_ip(type, token, tokenlen, name); |
| - | |
| - | if(match) |
| - | return TRUE; |
| - | } |
| - | |
| - | /* pass blanks after pattern */ |
| - | curlx_str_passblanks(&p); |
| - | /* if not a comma, this ends the loop */ |
| - | if(*p != ',') |
| - | break; |
| - | /* pass any number of commas */ |
| - | while(*p == ',') |
| - | p++; |
| - | } /* while(*p) */ |
| - | } /* NO_PROXY was specified and it was not only an asterisk */ |
| - | |
| - | return FALSE; |
| - | } |
| - | |
| - | #endif /* CURL_DISABLE_PROXY */ |
| @@ -630,7 +630,7 @@ CURLcode Curl_peer_from_proxy_url(CURLU *uh, | |
| } | |
| DEBUGASSERT(pp.scheme); | |
| - | if(IS_HTTPS_PROXY(proxytype) && |
| + | if(CURL_PROXY_IS_HTTPS(proxytype) && |
| !Curl_ssl_supports(data, SSLSUPP_HTTPS_PROXY)) { | |
| failf(data, "Unsupported proxy \'%s\', libcurl is built without the " | |
| "HTTPS-proxy support.", url); | |
| @@ -0,0 +1,673 @@ | |
| + | /*************************************************************************** |
| + | * _ _ ____ _ |
| + | * Project ___| | | | _ \| | |
| + | * / __| | | | |_) | | |
| + | * | (__| |_| | _ <| |___ |
| + | * \___|\___/|_| \_\_____| |
| + | * |
| + | * Copyright (C) Daniel Stenberg, <[email protected]>, et al. |
| + | * |
| + | * This software is licensed as described in the file COPYING, which |
| + | * you should have received as part of this distribution. The terms |
| + | * are also available at https://curl.se/docs/copyright.html. |
| + | * |
| + | * You may opt to use, copy, modify, merge, publish, distribute and/or sell |
| + | * copies of the Software, and permit persons to whom the Software is |
| + | * furnished to do so, under the terms of the COPYING file. |
| + | * |
| + | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY |
| + | * KIND, either express or implied. |
| + | * |
| + | * SPDX-License-Identifier: curl |
| + | * |
| + | ***************************************************************************/ |
| + | #include "curl_setup.h" |
| + | |
| + | #ifndef CURL_DISABLE_PROXY |
| + | |
| + | #include "urldata.h" |
| + | #include "curl_trc.h" |
| + | #include "protocol.h" |
| + | #include "proxy.h" |
| + | #include "http_proxy.h" |
| + | #include "strcase.h" |
| + | #include "url.h" |
| + | #include "vauth/vauth.h" |
| + | #include "curlx/inet_pton.h" |
| + | #include "curlx/strparse.h" |
| + | |
| + | #ifdef HAVE_NETINET_IN_H |
| + | #include <netinet/in.h> |
| + | #endif |
| + | |
| + | #ifdef HAVE_ARPA_INET_H |
| + | #include <arpa/inet.h> |
| + | #endif |
| + | |
| + | /* |
| + | * cidr4_match() returns TRUE if the given IPv4 address is within the |
| + | * specified CIDR address range. |
| + | * |
| + | * @unittest 1614 |
| + | */ |
| + | UNITTEST bool cidr4_match(const char *ipv4, /* 1.2.3.4 address */ |
| + | const char *network, /* 1.2.3.4 address */ |
| + | unsigned int bits); |
| + | UNITTEST bool cidr4_match(const char *ipv4, /* 1.2.3.4 address */ |
| + | const char *network, /* 1.2.3.4 address */ |
| + | unsigned int bits) |
| + | { |
| + | unsigned int address = 0; |
| + | unsigned int check = 0; |
| + | |
| + | if(bits > 32) |
| + | /* strange input */ |
| + | return FALSE; |
| + | |
| + | if(curlx_inet_pton(AF_INET, ipv4, &address) != 1) |
| + | return FALSE; |
| + | if(curlx_inet_pton(AF_INET, network, &check) != 1) |
| + | return FALSE; |
| + | |
| + | if(bits && (bits != 32)) { |
| + | unsigned int mask = 0xffffffff << (32 - bits); |
| + | unsigned int haddr = htonl(address); |
| + | unsigned int hcheck = htonl(check); |
| + | #if 0 |
| + | curl_mfprintf(stderr, "Host %s (%x) network %s (%x) " |
| + | "bits %u mask %x => %x\n", |
| + | ipv4, haddr, network, hcheck, bits, mask, |
| + | (haddr ^ hcheck) & mask); |
| + | #endif |
| + | if((haddr ^ hcheck) & mask) |
| + | return FALSE; |
| + | return TRUE; |
| + | } |
| + | return address == check; |
| + | } |
| + | |
| + | /* @unittest 1614 */ |
| + | UNITTEST bool cidr6_match(const char *ipv6, const char *network, |
| + | unsigned int bits); |
| + | UNITTEST bool cidr6_match(const char *ipv6, const char *network, |
| + | unsigned int bits) |
| + | { |
| + | #ifdef USE_IPV6 |
| + | unsigned int bytes; |
| + | unsigned int rest; |
| + | unsigned char address[16]; |
| + | unsigned char check[16]; |
| + | |
| + | if(!bits) |
| + | bits = 128; |
| + | |
| + | bytes = bits / 8; |
| + | rest = bits & 0x07; |
| + | if((bytes > 16) || ((bytes == 16) && rest)) |
| + | return FALSE; |
| + | if(curlx_inet_pton(AF_INET6, ipv6, address) != 1) |
| + | return FALSE; |
| + | if(curlx_inet_pton(AF_INET6, network, check) != 1) |
| + | return FALSE; |
| + | if(bytes && memcmp(address, check, bytes)) |
| + | return FALSE; |
| + | if(rest && ((address[bytes] ^ check[bytes]) & (0xff << (8 - rest)))) |
| + | return FALSE; |
| + | |
| + | return TRUE; |
| + | #else |
| + | (void)ipv6; |
| + | (void)network; |
| + | (void)bits; |
| + | return FALSE; |
| + | #endif |
| + | } |
| + | |
| + | enum nametype { |
| + | TYPE_HOST, |
| + | TYPE_IPV4, |
| + | TYPE_IPV6 |
| + | }; |
| + | |
| + | static bool match_host(const char *token, size_t tokenlen, |
| + | const char *name, size_t namelen) |
| + | { |
| + | bool match = FALSE; |
| + | |
| + | /* ignore trailing dots in the token to check */ |
| + | if(token[tokenlen - 1] == '.') |
| + | tokenlen--; |
| + | |
| + | if(tokenlen && (*token == '.')) { |
| + | /* ignore leading token dot as well */ |
| + | token++; |
| + | tokenlen--; |
| + | } |
| + | /* A: example.com matches 'example.com' |
| + | B: www.example.com matches 'example.com' |
| + | C: nonexample.com DOES NOT match 'example.com' |
| + | */ |
| + | if(tokenlen == namelen) |
| + | /* case A, exact match */ |
| + | match = curl_strnequal(token, name, namelen); |
| + | else if(tokenlen < namelen) { |
| + | /* case B, tailmatch domain */ |
| + | match = (name[namelen - tokenlen - 1] == '.') && |
| + | curl_strnequal(token, name + (namelen - tokenlen), tokenlen); |
| + | } |
| + | /* case C passes through, not a match */ |
| + | return match; |
| + | } |
| + | |
| + | static bool match_ip(int type, const char *token, size_t tokenlen, |
| + | const char *name) |
| + | { |
| + | char *slash; |
| + | unsigned int bits = 0; |
| + | char checkip[128]; |
| + | if(tokenlen >= sizeof(checkip)) |
| + | /* this cannot match */ |
| + | return FALSE; |
| + | /* copy the check name to a temp buffer */ |
| + | memcpy(checkip, token, tokenlen); |
| + | checkip[tokenlen] = 0; |
| + | |
| + | slash = strchr(checkip, '/'); |
| + | /* if the slash is part of this token, use it */ |
| + | if(slash) { |
| + | curl_off_t value; |
| + | const char *p = &slash[1]; |
| + | if(curlx_str_number(&p, &value, 128) || *p) |
| + | return FALSE; |
| + | /* a too large value is rejected in the cidr function below */ |
| + | bits = (unsigned int)value; |
| + | *slash = 0; /* null-terminate there */ |
| + | } |
| + | if(type == TYPE_IPV6) |
| + | return cidr6_match(name, checkip, bits); |
| + | else |
| + | return cidr4_match(name, checkip, bits); |
| + | } |
| + | |
| + | /**************************************************************** |
| + | * Checks if the host is in the noproxy list. returns TRUE if it matches and |
| + | * therefore the proxy should NOT be used. |
| + | ****************************************************************/ |
| + | /* @unittest 1614 */ |
| + | UNITTEST bool proxy_check_noproxy(const char *name, const char *no_proxy); |
| + | UNITTEST bool proxy_check_noproxy(const char *name, const char *no_proxy) |
| + | { |
| + | /* |
| + | * If we do not have a hostname at all, like for example with a FILE |
| + | * transfer, we have nothing to interrogate the noproxy list with. |
| + | */ |
| + | if(!name || name[0] == '\0') |
| + | return FALSE; |
| + | |
| + | /* no_proxy=domain1.dom,host.domain2.dom |
| + | * (a comma-separated list of hosts which should |
| + | * not be proxied, or an asterisk to override |
| + | * all proxy variables) |
| + | */ |
| + | if(no_proxy && no_proxy[0]) { |
| + | const char *p = no_proxy; |
| + | size_t namelen; |
| + | char address[16]; |
| + | enum nametype type = TYPE_HOST; |
| + | if(!strcmp("*", no_proxy)) |
| + | return TRUE; |
| + | |
| + | /* NO_PROXY was specified and it was not only an asterisk */ |
| + | |
| + | /* Check if name is an IP address; if not, assume it being a hostname. */ |
| + | namelen = strlen(name); |
| + | if(curlx_inet_pton(AF_INET, name, &address) == 1) |
| + | type = TYPE_IPV4; |
| + | #ifdef USE_IPV6 |
| + | else if(curlx_inet_pton(AF_INET6, name, &address) == 1) |
| + | type = TYPE_IPV6; |
| + | #endif |
| + | else { |
| + | /* ignore trailing dots in the hostname */ |
| + | if(name[namelen - 1] == '.') |
| + | namelen--; |
| + | } |
| + | |
| + | while(*p) { |
| + | const char *token; |
| + | size_t tokenlen = 0; |
| + | |
| + | /* pass blanks */ |
| + | curlx_str_passblanks(&p); |
| + | |
| + | token = p; |
| + | /* pass over the pattern */ |
| + | while(*p && !ISBLANK(*p) && (*p != ',')) { |
| + | p++; |
| + | tokenlen++; |
| + | } |
| + | |
| + | if(tokenlen) { |
| + | bool match = FALSE; |
| + | if(type == TYPE_HOST) |
| + | match = match_host(token, tokenlen, name, namelen); |
| + | else |
| + | match = match_ip(type, token, tokenlen, name); |
| + | |
| + | if(match) |
| + | return TRUE; |
| + | } |
| + | |
| + | /* pass blanks after pattern */ |
| + | curlx_str_passblanks(&p); |
| + | /* if not a comma, this ends the loop */ |
| + | if(*p != ',') |
| + | break; |
| + | /* pass any number of commas */ |
| + | while(*p == ',') |
| + | p++; |
| + | } /* while(*p) */ |
| + | } /* NO_PROXY was specified and it was not only an asterisk */ |
| + | |
| + | return FALSE; |
| + | } |
| + | |
| + | #ifndef CURL_DISABLE_HTTP |
| + | |
| + | /**************************************************************** |
| + | * Detect what (if any) proxy to use. Remember that this selects a host |
| + | * name and is not limited to HTTP proxies only. |
| + | * The returned pointer must be freed by the caller. |
| + | ****************************************************************/ |
| + | static char *proxy_detect_proxy(struct Curl_easy *data, |
| + | const struct Curl_scheme *scheme) |
| + | { |
| + | char *proxy = NULL; |
| + | |
| + | /* If proxy was not specified, we check for default proxy environment |
| + | * variables, to enable i.e Lynx compliance: |
| + | * |
| + | * http_proxy=http://some.server.dom:port/ |
| + | * https_proxy=http://some.server.dom:port/ |
| + | * ftp_proxy=http://some.server.dom:port/ |
| + | * no_proxy=domain1.dom,host.domain2.dom |
| + | * (a comma-separated list of hosts which should |
| + | * not be proxied, or an asterisk to override |
| + | * all proxy variables) |
| + | * all_proxy=http://some.server.dom:port/ |
| + | * (seems to exist for the CERN www lib. Probably |
| + | * the first to check for.) |
| + | * |
| + | * For compatibility, the all-uppercase versions of these variables are |
| + | * checked if the lowercase versions do not exist. |
| + | */ |
| + | char proxy_env[20]; |
| + | const char *envp; |
| + | VERBOSE(envp = proxy_env); |
| + | |
| + | curl_msnprintf(proxy_env, sizeof(proxy_env), "%s_proxy", scheme->name); |
| + | |
| + | /* read the protocol proxy: */ |
| + | proxy = curl_getenv(proxy_env); |
| + | |
| + | /* |
| + | * We do not try the uppercase version of HTTP_PROXY because of |
| + | * security reasons: |
| + | * |
| + | * When curl is used in a webserver application |
| + | * environment (cgi or php), this environment variable can |
| + | * be controlled by the web server user by setting the |
| + | * http header 'Proxy:' to some value. |
| + | * |
| + | * This can cause 'internal' http/ftp requests to be |
| + | * arbitrarily redirected by any external attacker. |
| + | */ |
| + | if(!proxy && !curl_strequal("http_proxy", proxy_env)) { |
| + | /* There was no lowercase variable, try the uppercase version: */ |
| + | Curl_strntoupper(proxy_env, proxy_env, sizeof(proxy_env)); |
| + | proxy = curl_getenv(proxy_env); |
| + | } |
| + | |
| + | if(!proxy) { |
| + | #ifndef CURL_DISABLE_WEBSOCKETS |
| + | /* websocket proxy fallbacks */ |
| + | if(curl_strequal("ws_proxy", proxy_env)) { |
| + | proxy = curl_getenv("http_proxy"); |
| + | } |
| + | else if(curl_strequal("wss_proxy", proxy_env)) { |
| + | proxy = curl_getenv("https_proxy"); |
| + | if(!proxy) |
| + | proxy = curl_getenv("HTTPS_PROXY"); |
| + | } |
| + | if(!proxy) { |
| + | #endif |
| + | envp = "all_proxy"; |
| + | proxy = curl_getenv(envp); /* default proxy to use */ |
| + | if(!proxy) { |
| + | envp = "ALL_PROXY"; |
| + | proxy = curl_getenv(envp); |
| + | } |
| + | #ifndef CURL_DISABLE_WEBSOCKETS |
| + | } |
| + | #endif |
| + | } |
| + | if(proxy) |
| + | infof(data, "Uses proxy env variable %s == '%s'", envp, proxy); |
| + | |
| + | return proxy; |
| + | } |
| + | #endif /* CURL_DISABLE_HTTP */ |
| + | |
| + | /* |
| + | * If this is supposed to use a proxy, we need to figure out the proxy |
| + | * hostname, so that we can reuse an existing connection |
| + | * that may exist registered to the same proxy host. |
| + | */ |
| + | static CURLcode parse_proxy(struct Curl_easy *data, |
| + | const char *proxy, |
| + | bool for_pre_proxy, |
| + | struct proxy_info *proxyinfo) |
| + | { |
| + | char *proxyuser = NULL; |
| + | char *proxypasswd = NULL; |
| + | char *scheme = NULL; |
| + | CURLcode result = CURLE_OK; |
| + | /* Set the start proxy type for url scheme guessing */ |
| + | uint8_t proxytype = for_pre_proxy ? CURLPROXY_SOCKS4 : data->set.proxytype; |
| + | CURLU *uhp = curl_url(); |
| + | CURLUcode uc; |
| + | |
| + | if(!uhp) { |
| + | result = CURLE_OUT_OF_MEMORY; |
| + | goto error; |
| + | } |
| + | /* When parsing the proxy, allowing non-supported schemes since we have |
| + | these made up ones for proxies. Guess scheme for URLs without it. */ |
| + | uc = curl_url_set(uhp, CURLUPART_URL, proxy, |
| + | CURLU_NON_SUPPORT_SCHEME | CURLU_GUESS_SCHEME); |
| + | if(!uc) { |
| + | /* parsed okay as a URL - only update proxytype when scheme was explicit */ |
| + | uc = curl_url_get(uhp, CURLUPART_SCHEME, &scheme, CURLU_NO_GUESS_SCHEME); |
| + | if(!uc) { |
| + | result = Curl_scheme_to_proxytype(data, scheme, &proxytype, proxy); |
| + | if(result) |
| + | goto error; |
| + | } |
| + | else if(uc != CURLUE_NO_SCHEME) { |
| + | result = CURLE_OUT_OF_MEMORY; |
| + | goto error; |
| + | } |
| + | /* else: no explicit scheme, keep the configured proxytype */ |
| + | } |
| + | else { |
| + | failf(data, "Unsupported proxy syntax in \'%s\': %s", proxy, |
| + | curl_url_strerror(uc)); |
| + | result = CURLE_COULDNT_RESOLVE_PROXY; |
| + | goto error; |
| + | } |
| + | |
| + | result = Curl_peer_from_proxy_url(uhp, data, proxy, proxytype, |
| + | &proxyinfo->peer, &proxytype); |
| + | if(result) |
| + | goto error; |
| + | |
| + | switch(proxytype) { |
| + | case CURLPROXY_HTTP: |
| + | case CURLPROXY_HTTP_1_0: |
| + | case CURLPROXY_HTTPS: |
| + | case CURLPROXY_HTTPS2: |
| + | case CURLPROXY_HTTPS3: |
| + | if(for_pre_proxy) { |
| + | failf(data, "Unsupported pre-proxy type for \'%s\'", proxy); |
| + | result = CURLE_COULDNT_RESOLVE_PROXY; |
| + | goto error; |
| + | } |
| + | break; |
| + | case CURLPROXY_SOCKS4: |
| + | case CURLPROXY_SOCKS4A: |
| + | case CURLPROXY_SOCKS5: |
| + | case CURLPROXY_SOCKS5_HOSTNAME: |
| + | break; |
| + | default: |
| + | failf(data, "Unsupported proxy type %u for \'%s\'", proxytype, proxy); |
| + | result = CURLE_COULDNT_RESOLVE_PROXY; |
| + | goto error; |
| + | } |
| + | |
| + | /* Is there a username and password given in this proxy URL? */ |
| + | uc = curl_url_get(uhp, CURLUPART_USER, &proxyuser, CURLU_URLDECODE); |
| + | if(uc && (uc != CURLUE_NO_USER)) { |
| + | result = Curl_uc_to_curlcode(uc); |
| + | goto error; |
| + | } |
| + | uc = curl_url_get(uhp, CURLUPART_PASSWORD, &proxypasswd, CURLU_URLDECODE); |
| + | if(uc && (uc != CURLUE_NO_PASSWORD)) { |
| + | result = Curl_uc_to_curlcode(uc); |
| + | goto error; |
| + | } |
| + | |
| + | if(proxyuser || proxypasswd) { |
| + | result = Curl_creds_create(proxyuser, proxypasswd, NULL, NULL, |
| + | data->set.str[STRING_PROXY_SERVICE_NAME], |
| + | CREDS_URL, &proxyinfo->creds); |
| + | if(result) |
| + | goto error; |
| + | } |
| + | else if(!for_pre_proxy && |
| + | (data->set.str[STRING_PROXYUSERNAME] || |
| + | data->set.str[STRING_PROXYPASSWORD] || |
| + | data->set.str[STRING_PROXY_SERVICE_NAME])) { |
| + | /* No user/passwd in URL, if this is not a pre-proxy, the |
| + | * CURLOPT_PROXY* settings apply. */ |
| + | result = Curl_creds_create(data->set.str[STRING_PROXYUSERNAME], |
| + | data->set.str[STRING_PROXYPASSWORD], |
| + | NULL, NULL, |
| + | data->set.str[STRING_PROXY_SERVICE_NAME], |
| + | CREDS_OPTION, &proxyinfo->creds); |
| + | } |
| + | else |
| + | Curl_creds_unlink(&proxyinfo->creds); |
| + | |
| + | proxyinfo->proxytype = proxytype; |
| + | |
| + | error: |
| + | curlx_free(scheme); |
| + | curlx_free(proxyuser); |
| + | curlx_free(proxypasswd); |
| + | curl_url_cleanup(uhp); |
| + | #ifdef DEBUGBUILD |
| + | if(!result) { |
| + | DEBUGASSERT(proxyinfo); |
| + | DEBUGASSERT(proxyinfo->peer); |
| + | } |
| + | #endif |
| + | return result; |
| + | } |
| + | |
| + | /* Is transfer's origin exempted from proxy use? */ |
| + | static bool proxy_do_not_proxy(struct Curl_easy *data) |
| + | { |
| + | const char *no_proxy; |
| + | char *env_no_proxy = NULL; |
| + | bool do_not_proxy; |
| + | |
| + | /* no proxying if the transfer does not use the network */ |
| + | if(data->state.origin->scheme->flags & PROTOPT_NONETWORK) |
| + | return TRUE; |
| + | |
| + | no_proxy = data->set.str[STRING_NOPROXY]; |
| + | if(!no_proxy) { |
| + | const char *p = "no_proxy"; |
| + | env_no_proxy = curl_getenv(p); |
| + | if(!env_no_proxy) { |
| + | p = "NO_PROXY"; |
| + | env_no_proxy = curl_getenv(p); |
| + | } |
| + | if(env_no_proxy) |
| + | infof(data, "Uses proxy env variable %s == '%s'", p, env_no_proxy); |
| + | no_proxy = env_no_proxy; |
| + | } |
| + | |
| + | do_not_proxy = proxy_check_noproxy(data->state.origin->hostname, no_proxy); |
| + | curlx_safefree(env_no_proxy); |
| + | return do_not_proxy; |
| + | } |
| + | |
| + | CURLcode Curl_proxy_init_conn(struct Curl_easy *data, |
| + | struct connectdata *conn) |
| + | { |
| + | char *proxy = NULL; |
| + | char *pre_proxy = NULL; |
| + | bool do_env_detect = TRUE; |
| + | CURLcode result = CURLE_OK; |
| + | |
| + | /* Enforce no proxy use unless we decide to use one */ |
| + | conn->bits.origin_is_proxy = FALSE; |
| + | DEBUGASSERT(!conn->socks_proxy.peer); |
| + | DEBUGASSERT(!conn->http_proxy.peer); |
| + | |
| + | if(proxy_do_not_proxy(data)) |
| + | goto out; |
| + | |
| + | /************************************************************* |
| + | * Detect what (if any) proxy to use |
| + | *************************************************************/ |
| + | /* the empty config strings disable proxy use and env detects */ |
| + | if(data->set.str[STRING_PROXY]) { |
| + | if(*data->set.str[STRING_PROXY]) { |
| + | proxy = curlx_strdup(data->set.str[STRING_PROXY]); |
| + | /* if global proxy is set, this is it */ |
| + | if(!proxy) { |
| + | failf(data, "memory shortage"); |
| + | result = CURLE_OUT_OF_MEMORY; |
| + | goto out; |
| + | } |
| + | } |
| + | else |
| + | do_env_detect = FALSE; |
| + | } |
| + | |
| + | if(data->set.str[STRING_PRE_PROXY]) { |
| + | if(*data->set.str[STRING_PRE_PROXY]) { |
| + | pre_proxy = curlx_strdup(data->set.str[STRING_PRE_PROXY]); |
| + | /* if global socks proxy is set, this is it */ |
| + | if(!pre_proxy) { |
| + | failf(data, "memory shortage"); |
| + | result = CURLE_OUT_OF_MEMORY; |
| + | goto out; |
| + | } |
| + | } |
| + | else |
| + | do_env_detect = FALSE; |
| + | } |
| + | |
| + | #ifndef CURL_DISABLE_HTTP |
| + | /* None configured, detect possible proxy from environment. */ |
| + | if(!proxy && !pre_proxy && do_env_detect) |
| + | proxy = proxy_detect_proxy(data, conn->scheme); |
| + | #else |
| + | (void)do_env_detect; |
| + | #endif /* CURL_DISABLE_HTTP */ |
| + | |
| + | if(!proxy && !pre_proxy) |
| + | goto out; |
| + | |
| + | if(pre_proxy) { |
| + | result = parse_proxy(data, pre_proxy, TRUE, &conn->socks_proxy); |
| + | if(result) |
| + | goto out; |
| + | } |
| + | |
| + | if(proxy) { |
| + | result = parse_proxy(data, proxy, FALSE, &conn->http_proxy); |
| + | if(result) |
| + | goto out; |
| + | |
| + | switch(conn->http_proxy.proxytype) { |
| + | case CURLPROXY_SOCKS4: |
| + | case CURLPROXY_SOCKS4A: |
| + | case CURLPROXY_SOCKS5: |
| + | case CURLPROXY_SOCKS5_HOSTNAME: |
| + | /* Whoops, it's not a HTTP proxy */ |
| + | if(pre_proxy) { |
| + | /* and we already have a SOCKS pre-proxy. Cannot have both */ |
| + | failf(data, "Having a SOCKS pre-proxy and proxy is not " |
| + | "supported with \'%s\'", proxy); |
| + | result = CURLE_COULDNT_RESOLVE_PROXY; |
| + | goto out; |
| + | } |
| + | /* switch */ |
| + | conn->socks_proxy = conn->http_proxy; |
| + | memset(&conn->http_proxy, 0, sizeof(conn->http_proxy)); |
| + | break; |
| + | default: |
| + | /* all other types are HTTP */ |
| + | break; |
| + | } |
| + | } |
| + | |
| + | if(conn->socks_proxy.peer) { |
| + | DEBUGASSERT(!CURL_PROXY_IS_ANY_HTTP(conn->socks_proxy.proxytype)); |
| + | } |
| + | |
| + | #ifdef CURL_DISABLE_HTTP |
| + | if(conn->http_proxy.peer) { |
| + | /* asking for an HTTP proxy is a bit funny when HTTP is disabled... */ |
| + | result = CURLE_UNSUPPORTED_PROTOCOL; |
| + | goto out; |
| + | } |
| + | |
| + | #else /* CURL_DISABLE_HTTP */ |
| + | if(conn->http_proxy.peer) { |
| + | const struct Curl_scheme *scheme = data->state.origin->scheme; |
| + | bool tunnel_proxy = (bool)data->set.tunnel_thru_httpproxy; |
| + | DEBUGASSERT(CURL_PROXY_IS_ANY_HTTP(conn->http_proxy.proxytype)); |
| + | |
| + | if(!tunnel_proxy) { |
| + | /* Decide if we tunnel through proxy automatically */ |
| + | if(conn->via_peer) { |
| + | /* With connect-to, we always tunnel */ |
| + | tunnel_proxy = TRUE; |
| + | } |
| + | else if(scheme->flags & PROTOPT_SSL) { |
| + | /* If the transfer is supposed to be secure, we tunnel */ |
| + | tunnel_proxy = TRUE; |
| + | } |
| + | else if(scheme->flags & PROTOPT_HTTP_PROXY_TUNNEL) { |
| + | /* transfer scheme required tunneling */ |
| + | tunnel_proxy = TRUE; |
| + | } |
| + | else if(!(scheme->protocol & PROTO_FAMILY_HTTP) && |
| + | !(scheme->flags & PROTOPT_PROXY_AS_HTTP)) { |
| + | /* Cannot delegate transfer URL to HTTP proxy */ |
| + | tunnel_proxy = TRUE; |
| + | } |
| + | } |
| + | |
| + | if(!tunnel_proxy) { |
| + | /* HTTP proxy used in forwarding mode. This means the connection |
| + | * is really to the proxy and NOT the origin of the transfer. */ |
| + | DEBUGASSERT(!conn->via_peer); |
| + | Curl_peer_link(&conn->origin, conn->http_proxy.peer); |
| + | conn->scheme = conn->http_proxy.peer->scheme; |
| + | conn->bits.origin_is_proxy = TRUE; |
| + | } |
| + | |
| + | #ifndef CURL_DISABLE_DIGEST_AUTH |
| + | if(!Curl_safecmp(data->state.envproxy, proxy)) { |
| + | /* proxy changed */ |
| + | Curl_auth_digest_cleanup(&data->state.proxydigest); |
| + | curlx_free(data->state.envproxy); |
| + | data->state.envproxy = curlx_strdup(proxy); |
| + | } |
| + | #endif |
| + | } |
| + | #endif /* !CURL_DISABLE_HTTP */ |
| + | |
| + | out: |
| + | curlx_free(pre_proxy); |
| + | curlx_free(proxy); |
| + | return result; |
| + | } |
| + | |
| + | #endif /* CURL_DISABLE_PROXY */ |
| @@ -1,5 +1,5 @@ | |
| - | #ifndef HEADER_CURL_NOPROXY_H |
| - | #define HEADER_CURL_NOPROXY_H |
| + | #ifndef HEADER_CURL_PROXY_H |
| + | #define HEADER_CURL_PROXY_H |
| /*************************************************************************** | |
| * _ _ ____ _ | |
| * Project ___| | | | _ \| | | |
| @@ -26,7 +26,34 @@ | |
| #include "curl_setup.h" | |
| #ifndef CURL_DISABLE_PROXY | |
| - | bool Curl_check_noproxy(const char *name, const char *no_proxy); |
| - | #endif |
| - | #endif /* HEADER_CURL_NOPROXY_H */ |
| + | struct Curl_easy; |
| + | struct Curl_peer; |
| + | struct Curl_creds; |
| + | struct connectdata; |
| + | |
| + | struct proxy_info { |
| + | struct Curl_peer *peer; /* proxy to this peer */ |
| + | struct Curl_creds *creds; /* use these credentials, maybe NULL */ |
| + | uint8_t proxytype; /* what kind of proxy that is in use */ |
| + | }; |
| + | |
| + | #define CURL_PROXY_IS_HTTPS(t) \ |
| + | (((t) == CURLPROXY_HTTPS) || \ |
| + | ((t) == CURLPROXY_HTTPS2) || \ |
| + | ((t) == CURLPROXY_HTTPS3)) |
| + | |
| + | #define CURL_PROXY_IS_HTTP(t) \ |
| + | (((t) == CURLPROXY_HTTP) || \ |
| + | ((t) == CURLPROXY_HTTP_1_0)) |
| + | |
| + | #define CURL_PROXY_IS_ANY_HTTP(t) \ |
| + | (CURL_PROXY_IS_HTTP(t) || \ |
| + | CURL_PROXY_IS_HTTPS(t)) |
| + | |
| + | CURLcode Curl_proxy_init_conn(struct Curl_easy *data, |
| + | struct connectdata *conn); |
| + | |
| + | #endif /* !CURL_DISABLE_PROXY */ |
| + | |
| + | #endif /* HEADER_CURL_PROXY_H */ |
| @@ -493,6 +493,7 @@ CURLcode Curl_pretransfer(struct Curl_easy *data) | |
| /* initial transfer request coming up, forget the initial origin | |
| * from a previous perform() on this handle. */ | |
| Curl_peer_unlink(&data->state.initial_origin); | |
| + | Curl_peer_unlink(&data->state.origin); |
| data->state.requests = 0; | |
| data->state.followlocation = 0; /* reset the location-follow counter */ | |
| data->state.this_is_a_follow = FALSE; /* reset this */ | |
| @@ -86,7 +86,7 @@ | |
| #include "urlapi-int.h" | |
| #include "system_win32.h" | |
| #include "hsts.h" | |
| - | #include "noproxy.h" |
| + | #include "proxy.h" |
| #include "cfilters.h" | |
| #include "idn.h" | |
| #include "http_proxy.h" | |
| @@ -245,6 +245,7 @@ CURLcode Curl_close(struct Curl_easy **datap) | |
| /* Close down all open SSL info and sessions */ | |
| Curl_ssl_close_all(data); | |
| + | Curl_peer_unlink(&data->state.origin); |
| Curl_peer_unlink(&data->state.initial_origin); | |
| Curl_ssl_free_certinfo(data); | |
| @@ -840,36 +841,27 @@ static bool url_match_ssl_use(struct connectdata *conn, | |
| static bool url_match_proxy_use(struct connectdata *conn, | |
| struct url_conn_match *m) | |
| { | |
| - | if(m->needle->bits.httpproxy != conn->bits.httpproxy || |
| - | m->needle->bits.socksproxy != conn->bits.socksproxy) |
| + | if(m->needle->bits.origin_is_proxy != conn->bits.origin_is_proxy) |
| return FALSE; | |
| - | if(m->needle->bits.socksproxy && |
| - | !proxy_info_matches(&m->needle->socks_proxy, &conn->socks_proxy)) |
| + | if(!proxy_info_matches(&m->needle->socks_proxy, &conn->socks_proxy)) |
| return FALSE; | |
| - | if(m->needle->bits.httpproxy) { |
| - | if(m->needle->bits.tunnel_proxy != conn->bits.tunnel_proxy) |
| - | return FALSE; |
| + | if(!proxy_info_matches(&m->needle->http_proxy, &conn->http_proxy)) |
| + | return FALSE; |
| - | if(!proxy_info_matches(&m->needle->http_proxy, &conn->http_proxy)) |
| + | if(CURL_PROXY_IS_HTTPS(m->needle->http_proxy.proxytype)) { |
| + | /* https proxies come in different types, http/1.1, h2, ... */ |
| + | /* match SSL config to proxy */ |
| + | if(!Curl_ssl_conn_config_match(m->data, conn, TRUE)) { |
| + | DEBUGF(infof(m->data, |
| + | "Connection #%" FMT_OFF_T |
| + | " has different SSL proxy parameters, cannot reuse", |
| + | conn->connection_id)); |
| return FALSE; | |
| - | |
| - | if(IS_HTTPS_PROXY(m->needle->http_proxy.proxytype)) { |
| - | /* https proxies come in different types, http/1.1, h2, ... */ |
| - | if(m->needle->http_proxy.proxytype != conn->http_proxy.proxytype) |
| - | return FALSE; |
| - | /* match SSL config to proxy */ |
| - | if(!Curl_ssl_conn_config_match(m->data, conn, TRUE)) { |
| - | DEBUGF(infof(m->data, |
| - | "Connection #%" FMT_OFF_T |
| - | " has different SSL proxy parameters, cannot reuse", |
| - | conn->connection_id)); |
| - | return FALSE; |
| - | } |
| - | /* the SSL config to the server, which may apply here is checked |
| - | * further below */ |
| } | |
| + | /* the SSL config to the server, which may apply here is checked |
| + | * further below */ |
| } | |
| return TRUE; | |
| } | |
| @@ -981,14 +973,6 @@ static bool url_match_destination(struct connectdata *conn, | |
| if(!Curl_peer_same_destination(m->needle->via_peer, conn->via_peer)) | |
| return FALSE; | |
| - | #ifndef CURL_DISABLE_PROXY |
| - | if(m->needle->bits.httpproxy && !m->needle->bits.tunnel_proxy) { |
| - | /* Talking to a non-tunneling HTTP proxy matches on proxy peers. */ |
| - | return Curl_peer_equal(m->needle->http_proxy.peer, |
| - | conn->http_proxy.peer); |
| - | } |
| - | #endif |
| - | |
| if(m->needle->origin->scheme != conn->origin->scheme) { | |
| /* `needle` and `conn` not having the same scheme. | |
| * This is allowed for the same family *if* conn is using TLS. | |
| @@ -1310,28 +1294,6 @@ static struct connectdata *allocate_conn(struct Curl_easy *data) | |
| /* Store current time to give a baseline to keepalive connection times. */ | |
| conn->keepalive = conn->created; | |
| - | #ifndef CURL_DISABLE_PROXY |
| - | conn->http_proxy.proxytype = data->set.proxytype; |
| - | conn->socks_proxy.proxytype = CURLPROXY_SOCKS4; |
| - | |
| - | /* note that these two proxy bits are set on what looks to be |
| - | requested, they may be altered down the road */ |
| - | conn->bits.proxy = (data->set.str[STRING_PROXY] && |
| - | *data->set.str[STRING_PROXY]); |
| - | conn->bits.httpproxy = (conn->bits.proxy && |
| - | (conn->http_proxy.proxytype == CURLPROXY_HTTP || |
| - | conn->http_proxy.proxytype == CURLPROXY_HTTP_1_0 || |
| - | IS_HTTPS_PROXY(conn->http_proxy.proxytype))); |
| - | conn->bits.socksproxy = (conn->bits.proxy && !conn->bits.httpproxy); |
| - | |
| - | if(data->set.str[STRING_PRE_PROXY] && *data->set.str[STRING_PRE_PROXY]) { |
| - | conn->bits.proxy = TRUE; |
| - | conn->bits.socksproxy = TRUE; |
| - | } |
| - | |
| - | conn->bits.tunnel_proxy = data->set.tunnel_thru_httpproxy; |
| - | #endif /* CURL_DISABLE_PROXY */ |
| - | |
| #ifndef CURL_DISABLE_FTP | |
| conn->bits.ftp_use_epsv = data->set.ftp_use_epsv; | |
| conn->bits.ftp_use_eprt = data->set.ftp_use_eprt; | |
| @@ -1406,14 +1368,13 @@ CURLcode Curl_uc_to_curlcode(CURLUcode uc) | |
| #ifndef CURL_DISABLE_HSTS | |
| static CURLcode hsts_upgrade(struct Curl_easy *data, | |
| - | struct connectdata *conn, |
| CURLU *uh, | |
| uint16_t port_override, | |
| uint32_t scope_id) | |
| { | |
| /* HSTS upgrade */ | |
| - | if(data->hsts && (conn->origin->scheme == &Curl_scheme_http) && |
| - | Curl_hsts_applies(data->hsts, conn->origin)) { |
| + | if(data->hsts && (data->state.origin->scheme == &Curl_scheme_http) && |
| + | Curl_hsts_applies(data->hsts, data->state.origin)) { |
| char *url; | |
| CURLUcode uc; | |
| CURLcode result; | |
| @@ -1430,7 +1391,7 @@ static CURLcode hsts_upgrade(struct Curl_easy *data, | |
| Curl_bufref_set(&data->state.url, url, 0, curl_free); | |
| result = Curl_peer_from_url(uh, data, port_override, scope_id, | |
| - | &data->state.up, &conn->origin); |
| + | &data->state.up, &data->state.origin); |
| if(result) | |
| return result; | |
| infof(data, "Switched from HTTP to HTTPS due to HSTS => %s", url); | |
| @@ -1438,7 +1399,7 @@ static CURLcode hsts_upgrade(struct Curl_easy *data, | |
| return CURLE_OK; | |
| } | |
| #else | |
| - | #define hsts_upgrade(x, y, z, a, b) CURLE_OK |
| + | #define hsts_upgrade(x, y, z, a) CURLE_OK |
| #endif | |
| #ifndef CURL_DISABLE_NETRC | |
| @@ -1460,7 +1421,6 @@ static bool str_has_ctrl(const char *input) | |
| * option or a .netrc file, if applicable. | |
| */ | |
| static CURLcode url_set_data_creds_netrc(struct Curl_easy *data, | |
| - | struct connectdata *conn, |
| struct Curl_creds **pcreds) | |
| { | |
| struct Curl_creds *ncreds_out = NULL; | |
| @@ -1500,7 +1460,7 @@ static CURLcode url_set_data_creds_netrc(struct Curl_easy *data, | |
| goto out; | |
| ret = Curl_netrc_scan(data, &data->state.netrc, | |
| - | conn->origin->hostname, |
| + | data->state.origin->hostname, |
| Curl_creds_user(ncreds_in), | |
| data->set.str[STRING_NETRC_FILE], | |
| &ncreds_out); | |
| @@ -1512,7 +1472,7 @@ static CURLcode url_set_data_creds_netrc(struct Curl_easy *data, | |
| else if(ret && ((ret == NETRC_NO_MATCH) || | |
| (data->set.use_netrc == CURL_NETRC_OPTIONAL))) { | |
| infof(data, "Could not find host %s in the %s file; using defaults", | |
| - | conn->origin->hostname, |
| + | data->state.origin->hostname, |
| (data->set.str[STRING_NETRC_FILE] ? | |
| data->set.str[STRING_NETRC_FILE] : ".netrc")); | |
| } | |
| @@ -1523,7 +1483,7 @@ static CURLcode url_set_data_creds_netrc(struct Curl_easy *data, | |
| goto out; | |
| } | |
| else if(ncreds_out) { | |
| - | if(!(conn->scheme->flags & PROTOPT_USERPWDCTRL)) { |
| + | if(!(data->state.origin->scheme->flags & PROTOPT_USERPWDCTRL)) { |
| /* if the protocol cannot handle control codes in credentials, make | |
| sure there are none */ | |
| if(str_has_ctrl(ncreds_out->user) || | |
| @@ -1534,7 +1494,7 @@ static CURLcode url_set_data_creds_netrc(struct Curl_easy *data, | |
| } | |
| } | |
| CURL_TRC_M(data, "netrc: using credentials for %s as %s", | |
| - | conn->origin->hostname, ncreds_out->user); |
| + | data->state.origin->hostname, ncreds_out->user); |
| result = Curl_creds_merge(ncreds_out->user, ncreds_out->passwd, | |
| *pcreds, CREDS_NETRC, pcreds); | |
| if(result) | |
| @@ -1553,15 +1513,17 @@ static CURLcode url_set_data_creds_netrc(struct Curl_easy *data, | |
| DEBUGASSERT(0); | |
| } | |
| + | #ifdef CURLVERBOSE |
| + | Curl_creds_trace(data, data->state.creds, "transfer credentials"); |
| + | #endif |
| + | |
| out: | |
| Curl_creds_unlink(&ncreds_out); | |
| return result; | |
| } | |
| #endif /* CURL_DISABLE_NETRC */ | |
| - | static CURLcode url_set_data_creds(struct Curl_easy *data, |
| - | struct connectdata *conn, |
| - | CURLU *uh) |
| + | static CURLcode url_set_data_creds(struct Curl_easy *data, CURLU *uh) |
| { | |
| struct Curl_creds *newcreds = NULL; | |
| CURLcode result = CURLE_OK; | |
| @@ -1571,7 +1533,7 @@ static CURLcode url_set_data_creds(struct Curl_easy *data, | |
| data->set.str[STRING_BEARER] || | |
| data->set.str[STRING_SASL_AUTHZID] || | |
| data->set.str[STRING_SERVICE_NAME]) && | |
| - | Curl_auth_allowed_to_origin(data, conn->origin)) { |
| + | Curl_auth_allowed_to_origin(data, data->state.origin)) { |
| result = Curl_creds_create(data->set.str[STRING_USERNAME], | |
| data->set.str[STRING_PASSWORD], | |
| data->set.str[STRING_BEARER], | |
| @@ -1599,12 +1561,14 @@ static CURLcode url_set_data_creds(struct Curl_easy *data, | |
| } | |
| if(!result && data->state.up.user) { | |
| result = Curl_urldecode(data->state.up.user, 0, &udecoded, NULL, | |
| - | conn->scheme->flags&PROTOPT_USERPWDCTRL ? |
| + | (data->state.origin->scheme->flags & |
| + | PROTOPT_USERPWDCTRL) ? |
| REJECT_ZERO : REJECT_CTRL); | |
| } | |
| if(!result && data->state.up.password) { | |
| result = Curl_urldecode(data->state.up.password, 0, &pdecoded, NULL, | |
| - | conn->scheme->flags&PROTOPT_USERPWDCTRL ? |
| + | (data->state.origin->scheme->flags & |
| + | PROTOPT_USERPWDCTRL) ? |
| REJECT_ZERO : REJECT_CTRL); | |
| } | |
| if(!result) | |
| @@ -1622,7 +1586,7 @@ static CURLcode url_set_data_creds(struct Curl_easy *data, | |
| #ifndef CURL_DISABLE_NETRC | |
| /* Check for overridden login details and set them accordingly so that | |
| they are known when protocol->setup_connection is called! */ | |
| - | result = url_set_data_creds_netrc(data, conn, &newcreds); |
| + | result = url_set_data_creds_netrc(data, &newcreds); |
| #endif /* CURL_DISABLE_NETRC */ | |
| out: | |
| @@ -1634,139 +1598,37 @@ out: | |
| return result; | |
| } | |
| - | /* |
| - | * Parse URL and fill in the relevant members of the connection struct. |
| - | */ |
| - | static CURLcode parseurlandfillconn(struct Curl_easy *data, |
| - | struct connectdata *conn) |
| + | static CURLcode url_set_conn_origin_etc(struct Curl_easy *data, |
| + | struct connectdata *conn) |
| { | |
| CURLcode result = CURLE_OK; | |
| - | CURLU *uh; |
| - | CURLUcode uc; |
| - | bool use_set_uh = (data->set.uh && !data->state.this_is_a_follow); |
| - | uint16_t port_override = data->state.allow_port ? data->set.use_port : 0; |
| - | uint32_t scope_id = 0; |
| - | up_free(data); /* cleanup previous leftovers first */ |
| + | Curl_peer_link(&conn->origin, data->state.origin); |
| - | /* parse the URL */ |
| - | if(use_set_uh) |
| - | uh = data->state.uh = curl_url_dup(data->set.uh); |
| - | else |
| - | uh = data->state.uh = curl_url(); |
| - | if(!uh) { |
| - | result = CURLE_OUT_OF_MEMORY; |
| - | goto out; |
| - | } |
| - | |
| - | /* Calculate the *real* URL this transfer uses, applying defaults |
| - | * where information is missing. */ |
| - | if(data->set.str[STRING_DEFAULT_PROTOCOL] && |
| - | !Curl_is_absolute_url(Curl_bufref_ptr(&data->state.url), NULL, 0, TRUE)) { |
| - | char *url = curl_maprintf("%s://%s", |
| - | data->set.str[STRING_DEFAULT_PROTOCOL], |
| - | Curl_bufref_ptr(&data->state.url)); |
| - | if(!url) { |
| - | result = CURLE_OUT_OF_MEMORY; |
| - | goto out; |
| - | } |
| - | Curl_bufref_set(&data->state.url, url, 0, curl_free); |
| - | } |
| - | |
| - | if(!use_set_uh) { |
| - | char *newurl; |
| - | uc = curl_url_set(uh, CURLUPART_URL, Curl_bufref_ptr(&data->state.url), |
| - | (unsigned int)(CURLU_GUESS_SCHEME | |
| - | CURLU_NON_SUPPORT_SCHEME | |
| - | (data->set.disallow_username_in_url ? |
| - | CURLU_DISALLOW_USER : 0) | |
| - | (data->set.path_as_is ? CURLU_PATH_AS_IS : 0))); |
| - | if(uc) { |
| - | failf(data, "URL rejected: %s", curl_url_strerror(uc)); |
| - | result = Curl_uc_to_curlcode(uc); |
| - | goto out; |
| - | } |
| - | |
| - | /* after it was parsed, get the generated normalized version */ |
| - | uc = curl_url_get(uh, CURLUPART_URL, &newurl, CURLU_GET_EMPTY); |
| - | if(uc) { |
| - | result = Curl_uc_to_curlcode(uc); |
| - | goto out; |
| - | } |
| - | Curl_bufref_set(&data->state.url, newurl, 0, curl_free); |
| - | } |
| - | |
| - | #ifdef USE_IPV6 |
| - | scope_id = data->set.scope_id; |
| - | #endif |
| - | |
| - | /* `uh` is now as the connection should use it, probably. */ |
| - | result = Curl_peer_from_url(uh, data, port_override, scope_id, |
| - | &data->state.up, &conn->origin); |
| - | if(result) |
| - | goto out; |
| - | |
| - | result = hsts_upgrade(data, conn, uh, port_override, scope_id); |
| - | if(result) |
| - | goto out; |
| - | |
| - | /* now that the origin is fixed, check and set the connection scheme */ |
| + | /* set the connection scheme */ |
| result = url_set_conn_scheme(data, conn, conn->origin->scheme); | |
| if(result) | |
| goto out; | |
| - | /* When the transfers initial_origin is not set, this is the initial |
| - | * request. Remember this starting point. This is used to |
| - | * select credentials. */ |
| - | if(!data->state.initial_origin) |
| - | Curl_peer_link(&data->state.initial_origin, conn->origin); |
| - | |
| - | result = url_set_data_creds(data, conn, uh); |
| - | if(result) |
| - | goto out; |
| - | |
| - | uc = curl_url_get(uh, CURLUPART_OPTIONS, &data->state.up.options, |
| - | CURLU_URLDECODE); |
| - | if(!uc) { |
| - | conn->options = curlx_strdup(data->state.up.options); |
| + | /* 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: | |
| @@ -1852,379 +1714,6 @@ static CURLcode setup_connection_internals(struct Curl_easy *data, | |
| return CURLE_OK; | |
| } | |
| - | #ifndef CURL_DISABLE_PROXY |
| - | |
| - | #ifndef CURL_DISABLE_HTTP |
| - | /**************************************************************** |
| - | * Detect what (if any) proxy to use. Remember that this selects a host |
| - | * name and is not limited to HTTP proxies only. |
| - | * The returned pointer must be freed by the caller (unless NULL) |
| - | ****************************************************************/ |
| - | static char *url_detect_proxy(struct Curl_easy *data, |
| - | struct connectdata *conn) |
| - | { |
| - | char *proxy = NULL; |
| - | |
| - | /* If proxy was not specified, we check for default proxy environment |
| - | * variables, to enable i.e Lynx compliance: |
| - | * |
| - | * http_proxy=http://some.server.dom:port/ |
| - | * https_proxy=http://some.server.dom:port/ |
| - | * ftp_proxy=http://some.server.dom:port/ |
| - | * no_proxy=domain1.dom,host.domain2.dom |
| - | * (a comma-separated list of hosts which should |
| - | * not be proxied, or an asterisk to override |
| - | * all proxy variables) |
| - | * all_proxy=http://some.server.dom:port/ |
| - | * (seems to exist for the CERN www lib. Probably |
| - | * the first to check for.) |
| - | * |
| - | * For compatibility, the all-uppercase versions of these variables are |
| - | * checked if the lowercase versions do not exist. |
| - | */ |
| - | char proxy_env[20]; |
| - | const char *envp; |
| - | VERBOSE(envp = proxy_env); |
| - | |
| - | curl_msnprintf(proxy_env, sizeof(proxy_env), "%s_proxy", |
| - | conn->scheme->name); |
| - | |
| - | /* read the protocol proxy: */ |
| - | proxy = curl_getenv(proxy_env); |
| - | |
| - | /* |
| - | * We do not try the uppercase version of HTTP_PROXY because of |
| - | * security reasons: |
| - | * |
| - | * When curl is used in a webserver application |
| - | * environment (cgi or php), this environment variable can |
| - | * be controlled by the web server user by setting the |
| - | * http header 'Proxy:' to some value. |
| - | * |
| - | * This can cause 'internal' http/ftp requests to be |
| - | * arbitrarily redirected by any external attacker. |
| - | */ |
| - | if(!proxy && !curl_strequal("http_proxy", proxy_env)) { |
| - | /* There was no lowercase variable, try the uppercase version: */ |
| - | Curl_strntoupper(proxy_env, proxy_env, sizeof(proxy_env)); |
| - | proxy = curl_getenv(proxy_env); |
| - | } |
| - | |
| - | if(!proxy) { |
| - | #ifndef CURL_DISABLE_WEBSOCKETS |
| - | /* websocket proxy fallbacks */ |
| - | if(curl_strequal("ws_proxy", proxy_env)) { |
| - | proxy = curl_getenv("http_proxy"); |
| - | } |
| - | else if(curl_strequal("wss_proxy", proxy_env)) { |
| - | proxy = curl_getenv("https_proxy"); |
| - | if(!proxy) |
| - | proxy = curl_getenv("HTTPS_PROXY"); |
| - | } |
| - | if(!proxy) { |
| - | #endif |
| - | envp = "all_proxy"; |
| - | proxy = curl_getenv(envp); /* default proxy to use */ |
| - | if(!proxy) { |
| - | envp = "ALL_PROXY"; |
| - | proxy = curl_getenv(envp); |
| - | } |
| - | #ifndef CURL_DISABLE_WEBSOCKETS |
| - | } |
| - | #endif |
| - | } |
| - | if(proxy) |
| - | infof(data, "Uses proxy env variable %s == '%s'", envp, proxy); |
| - | |
| - | return proxy; |
| - | } |
| - | #endif /* CURL_DISABLE_HTTP */ |
| - | |
| - | /* |
| - | * If this is supposed to use a proxy, we need to figure out the proxy |
| - | * hostname, so that we can reuse an existing connection |
| - | * that may exist registered to the same proxy host. |
| - | */ |
| - | static CURLcode parse_proxy(struct Curl_easy *data, |
| - | const char *proxy, |
| - | bool for_pre_proxy, |
| - | struct proxy_info *proxyinfo) |
| - | { |
| - | char *proxyuser = NULL; |
| - | char *proxypasswd = NULL; |
| - | char *scheme = NULL; |
| - | CURLcode result = CURLE_OK; |
| - | /* Set the start proxy type for url scheme guessing */ |
| - | uint8_t proxytype = for_pre_proxy ? CURLPROXY_SOCKS4 : data->set.proxytype; |
| - | CURLU *uhp = curl_url(); |
| - | CURLUcode uc; |
| - | |
| - | if(!uhp) { |
| - | result = CURLE_OUT_OF_MEMORY; |
| - | goto error; |
| - | } |
| - | /* When parsing the proxy, allowing non-supported schemes since we have |
| - | these made up ones for proxies. Guess scheme for URLs without it. */ |
| - | uc = curl_url_set(uhp, CURLUPART_URL, proxy, |
| - | CURLU_NON_SUPPORT_SCHEME | CURLU_GUESS_SCHEME); |
| - | if(!uc) { |
| - | /* parsed okay as a URL - only update proxytype when scheme was explicit */ |
| - | uc = curl_url_get(uhp, CURLUPART_SCHEME, &scheme, CURLU_NO_GUESS_SCHEME); |
| - | if(!uc) { |
| - | result = Curl_scheme_to_proxytype(data, scheme, &proxytype, proxy); |
| - | if(result) |
| - | goto error; |
| - | } |
| - | else if(uc != CURLUE_NO_SCHEME) { |
| - | result = CURLE_OUT_OF_MEMORY; |
| - | goto error; |
| - | } |
| - | /* else: no explicit scheme, keep the configured proxytype */ |
| - | } |
| - | else { |
| - | failf(data, "Unsupported proxy syntax in \'%s\': %s", proxy, |
| - | curl_url_strerror(uc)); |
| - | result = CURLE_COULDNT_RESOLVE_PROXY; |
| - | goto error; |
| - | } |
| - | |
| - | result = Curl_peer_from_proxy_url(uhp, data, proxy, proxytype, |
| - | &proxyinfo->peer, &proxytype); |
| - | if(result) |
| - | goto error; |
| - | |
| - | switch(proxytype) { |
| - | case CURLPROXY_HTTP: |
| - | case CURLPROXY_HTTP_1_0: |
| - | case CURLPROXY_HTTPS: |
| - | case CURLPROXY_HTTPS2: |
| - | case CURLPROXY_HTTPS3: |
| - | if(for_pre_proxy) { |
| - | failf(data, "Unsupported pre-proxy type for \'%s\'", proxy); |
| - | result = CURLE_COULDNT_RESOLVE_PROXY; |
| - | goto error; |
| - | } |
| - | break; |
| - | case CURLPROXY_SOCKS4: |
| - | case CURLPROXY_SOCKS4A: |
| - | case CURLPROXY_SOCKS5: |
| - | case CURLPROXY_SOCKS5_HOSTNAME: |
| - | break; |
| - | default: |
| - | failf(data, "Unsupported proxy type %u for \'%s\'", proxytype, proxy); |
| - | result = CURLE_COULDNT_RESOLVE_PROXY; |
| - | goto error; |
| - | } |
| - | |
| - | /* Is there a username and password given in this proxy URL? */ |
| - | uc = curl_url_get(uhp, CURLUPART_USER, &proxyuser, CURLU_URLDECODE); |
| - | if(uc && (uc != CURLUE_NO_USER)) { |
| - | result = Curl_uc_to_curlcode(uc); |
| - | goto error; |
| - | } |
| - | uc = curl_url_get(uhp, CURLUPART_PASSWORD, &proxypasswd, CURLU_URLDECODE); |
| - | if(uc && (uc != CURLUE_NO_PASSWORD)) { |
| - | result = Curl_uc_to_curlcode(uc); |
| - | goto error; |
| - | } |
| - | |
| - | if(proxyuser || proxypasswd) { |
| - | result = Curl_creds_create(proxyuser, proxypasswd, NULL, NULL, |
| - | data->set.str[STRING_PROXY_SERVICE_NAME], |
| - | CREDS_URL, &proxyinfo->creds); |
| - | if(result) |
| - | goto error; |
| - | } |
| - | else if(!for_pre_proxy && |
| - | (data->set.str[STRING_PROXYUSERNAME] || |
| - | data->set.str[STRING_PROXYPASSWORD] || |
| - | data->set.str[STRING_PROXY_SERVICE_NAME])) { |
| - | /* No user/passwd in URL, if this is not a pre-proxy, the |
| - | * CURLOPT_PROXY* settings apply. */ |
| - | result = Curl_creds_create(data->set.str[STRING_PROXYUSERNAME], |
| - | data->set.str[STRING_PROXYPASSWORD], |
| - | NULL, NULL, |
| - | data->set.str[STRING_PROXY_SERVICE_NAME], |
| - | CREDS_OPTION, &proxyinfo->creds); |
| - | } |
| - | else |
| - | Curl_creds_unlink(&proxyinfo->creds); |
| - | |
| - | proxyinfo->proxytype = proxytype; |
| - | |
| - | error: |
| - | curlx_free(scheme); |
| - | curlx_free(proxyuser); |
| - | curlx_free(proxypasswd); |
| - | curl_url_cleanup(uhp); |
| - | #ifdef DEBUGBUILD |
| - | if(!result) { |
| - | DEBUGASSERT(proxyinfo); |
| - | DEBUGASSERT(proxyinfo->peer); |
| - | } |
| - | #endif |
| - | return result; |
| - | } |
| - | |
| - | static CURLcode url_set_conn_proxies(struct Curl_easy *data, |
| - | struct connectdata *conn) |
| - | { |
| - | char *proxy = NULL; |
| - | char *pre_proxy = NULL; |
| - | char *no_proxy = NULL; |
| - | CURLcode result = CURLE_OK; |
| - | |
| - | /************************************************************* |
| - | * Detect what (if any) proxy to use |
| - | *************************************************************/ |
| - | if(data->set.str[STRING_PROXY]) { |
| - | proxy = curlx_strdup(data->set.str[STRING_PROXY]); |
| - | /* if global proxy is set, this is it */ |
| - | if(!proxy) { |
| - | failf(data, "memory shortage"); |
| - | result = CURLE_OUT_OF_MEMORY; |
| - | goto out; |
| - | } |
| - | } |
| - | |
| - | if(data->set.str[STRING_PRE_PROXY]) { |
| - | pre_proxy = curlx_strdup(data->set.str[STRING_PRE_PROXY]); |
| - | /* if global socks proxy is set, this is it */ |
| - | if(!pre_proxy) { |
| - | failf(data, "memory shortage"); |
| - | result = CURLE_OUT_OF_MEMORY; |
| - | goto out; |
| - | } |
| - | } |
| - | |
| - | if(!data->set.str[STRING_NOPROXY]) { |
| - | const char *p = "no_proxy"; |
| - | no_proxy = curl_getenv(p); |
| - | if(!no_proxy) { |
| - | p = "NO_PROXY"; |
| - | no_proxy = curl_getenv(p); |
| - | } |
| - | if(no_proxy) { |
| - | infof(data, "Uses proxy env variable %s == '%s'", p, no_proxy); |
| - | } |
| - | } |
| - | |
| - | if(Curl_check_noproxy(conn->origin->hostname, data->set.str[STRING_NOPROXY] ? |
| - | data->set.str[STRING_NOPROXY] : no_proxy)) { |
| - | curlx_safefree(proxy); |
| - | curlx_safefree(pre_proxy); |
| - | } |
| - | #ifndef CURL_DISABLE_HTTP |
| - | else if(!proxy && !pre_proxy) |
| - | /* if the host is not in the noproxy list, detect proxy. */ |
| - | proxy = url_detect_proxy(data, conn); |
| - | #endif /* CURL_DISABLE_HTTP */ |
| - | curlx_safefree(no_proxy); |
| - | |
| - | if(proxy && (!*proxy || (conn->scheme->flags & PROTOPT_NONETWORK))) { |
| - | curlx_safefree(proxy); /* Do not bother with an empty proxy string |
| - | or if the protocol does not work with network */ |
| - | } |
| - | if(pre_proxy && (!*pre_proxy || |
| - | (conn->scheme->flags & PROTOPT_NONETWORK))) { |
| - | curlx_safefree(pre_proxy); /* Do not bother with an empty socks proxy |
| - | string or if the protocol does not work |
| - | with network */ |
| - | } |
| - | |
| - | /*********************************************************************** |
| - | * If this is supposed to use a proxy, we need to figure out the proxy host |
| - | * name, proxy type and port number, so that we can reuse an existing |
| - | * connection that may exist registered to the same proxy host. |
| - | ***********************************************************************/ |
| - | if(proxy || pre_proxy) { |
| - | if(pre_proxy) { |
| - | result = parse_proxy(data, pre_proxy, TRUE, &conn->socks_proxy); |
| - | if(result) |
| - | goto out; |
| - | } |
| - | |
| - | if(proxy) { |
| - | result = parse_proxy(data, proxy, FALSE, &conn->http_proxy); |
| - | if(result) |
| - | goto out; |
| - | switch(conn->http_proxy.proxytype) { |
| - | case CURLPROXY_SOCKS4: |
| - | case CURLPROXY_SOCKS4A: |
| - | case CURLPROXY_SOCKS5: |
| - | case CURLPROXY_SOCKS5_HOSTNAME: |
| - | /* Whoops, it's not a HTTP proxy */ |
| - | if(conn->socks_proxy.peer) { |
| - | /* and we already have a SOCKS pre-proxy. Cannot have both */ |
| - | failf(data, "Having a SOCKS pre-proxy and proxy is not " |
| - | "supported with \'%s\'", proxy); |
| - | result = CURLE_COULDNT_RESOLVE_PROXY; |
| - | goto out; |
| - | } |
| - | /* switch */ |
| - | conn->socks_proxy = conn->http_proxy; |
| - | memset(&conn->http_proxy, 0, sizeof(conn->http_proxy)); |
| - | break; |
| - | default: |
| - | break; |
| - | } |
| - | } |
| - | |
| - | if(conn->http_proxy.peer) { |
| - | #ifdef CURL_DISABLE_HTTP |
| - | /* asking for an HTTP proxy is a bit funny when HTTP is disabled... */ |
| - | result = CURLE_UNSUPPORTED_PROTOCOL; |
| - | goto out; |
| - | #else |
| - | #ifndef CURL_DISABLE_DIGEST_AUTH |
| - | if(!Curl_safecmp(data->state.envproxy, proxy)) { |
| - | /* proxy changed */ |
| - | Curl_auth_digest_cleanup(&data->state.proxydigest); |
| - | curlx_free(data->state.envproxy); |
| - | data->state.envproxy = curlx_strdup(proxy); |
| - | } |
| - | #endif |
| - | if(conn->scheme->flags & PROTOPT_HTTP_PROXY_TUNNEL) { |
| - | conn->bits.tunnel_proxy = TRUE; |
| - | } |
| - | else if(!(conn->scheme->protocol & PROTO_FAMILY_HTTP)) { |
| - | /* force this connection's protocol to become HTTP if compatible */ |
| - | if((conn->scheme->flags & PROTOPT_PROXY_AS_HTTP) && |
| - | !conn->bits.tunnel_proxy) |
| - | conn->scheme = &Curl_scheme_http; |
| - | else |
| - | /* if not converting to HTTP over the proxy, enforce tunneling */ |
| - | conn->bits.tunnel_proxy = TRUE; |
| - | } |
| - | #endif |
| - | } |
| - | else |
| - | conn->bits.tunnel_proxy = FALSE; /* no tunneling if not HTTP */ |
| - | } |
| - | |
| - | conn->bits.socksproxy = !!conn->socks_proxy.peer; |
| - | conn->bits.httpproxy = !!conn->http_proxy.peer; |
| - | conn->bits.proxy = conn->bits.httpproxy || conn->bits.socksproxy; |
| - | |
| - | if(!conn->bits.proxy) { |
| - | /* we are not using the proxy after all... */ |
| - | conn->bits.proxy = FALSE; |
| - | conn->bits.httpproxy = FALSE; |
| - | conn->bits.socksproxy = FALSE; |
| - | conn->bits.tunnel_proxy = FALSE; |
| - | /* CURLPROXY_HTTPS does not have its own flag in conn->bits, yet we need |
| - | to signal that CURLPROXY_HTTPS is not used for this connection */ |
| - | conn->http_proxy.proxytype = CURLPROXY_HTTP; |
| - | } |
| - | |
| - | out: |
| - | |
| - | curlx_free(pre_proxy); |
| - | curlx_free(proxy); |
| - | return result; |
| - | } |
| - | #endif /* CURL_DISABLE_PROXY */ |
| - | |
| /* | |
| * Curl_parse_login_details() | |
| * | |
| @@ -2601,14 +2090,6 @@ static CURLcode url_create_needle(struct Curl_easy *data, | |
| CURLcode result = CURLE_OK; | |
| bool network_scheme = TRUE; /* almost all are */ | |
| - | /************************************************************* |
| - | * Check input data |
| - | *************************************************************/ |
| - | if(!Curl_bufref_ptr(&data->state.url)) { |
| - | result = CURLE_URL_MALFORMAT; |
| - | goto out; |
| - | } |
| - | |
| /* First, split up the current URL in parts so that we can use the | |
| parts for checking against the already present connections. In order | |
| to not have to modify everything at once, we allocate a temporary | |
| @@ -2627,7 +2108,7 @@ static CURLcode url_create_needle(struct Curl_easy *data, | |
| * Determine `conn->origin` and propulate `data->state.up` and | |
| * other URL related properties. | |
| *************************************************************/ | |
| - | result = parseurlandfillconn(data, needle); |
| + | result = url_set_conn_origin_etc(data, needle); |
| if(result) | |
| goto out; | |
| @@ -2661,22 +2142,11 @@ static CURLcode url_create_needle(struct Curl_easy *data, | |
| #ifndef CURL_DISABLE_PROXY | |
| /* Going via a unix socket ignores any proxy settings */ | |
| - | if(needle->via_peer && needle->via_peer->unix_socket) { |
| - | needle->bits.socksproxy = FALSE; |
| - | needle->bits.httpproxy = FALSE; |
| - | needle->bits.proxy = FALSE; |
| - | } |
| - | else if(network_scheme) { |
| - | result = url_set_conn_proxies(data, needle); |
| + | if(network_scheme && |
| + | (!needle->via_peer || !needle->via_peer->unix_socket)) { |
| + | result = Curl_proxy_init_conn(data, needle); |
| if(result) | |
| goto out; | |
| - | |
| - | /************************************************************* |
| - | * If the protocol is using SSL and HTTP proxy is used, we set |
| - | * the tunnel_proxy bit. |
| - | *************************************************************/ |
| - | if((needle->given->flags & PROTOPT_SSL) && needle->bits.httpproxy) |
| - | needle->bits.tunnel_proxy = TRUE; |
| } | |
| #endif /* CURL_DISABLE_PROXY */ | |
| @@ -2692,15 +2162,6 @@ static CURLcode url_create_needle(struct Curl_easy *data, | |
| Curl_peer_unlink(&needle->via_peer); | |
| } | |
| - | #ifndef CURL_DISABLE_PROXY |
| - | /************************************************************* |
| - | * If the "connect to" feature is used with an HTTP proxy, |
| - | * we set the tunnel_proxy bit. |
| - | *************************************************************/ |
| - | if(needle->via_peer && needle->bits.httpproxy) |
| - | needle->bits.tunnel_proxy = TRUE; |
| - | #endif |
| - | |
| /************************************************************* | |
| * Setup internals depending on protocol. Needs to be done after | |
| * we figured out what/if proxy to use. | |
| @@ -2743,6 +2204,118 @@ out: | |
| return result; | |
| } | |
| + | static CURLcode url_set_data_origin_and_creds(struct Curl_easy *data) |
| + | { |
| + | CURLcode result = CURLE_OK; |
| + | CURLU *uh; |
| + | CURLUcode uc; |
| + | bool use_set_uh = (data->set.uh && !data->state.this_is_a_follow); |
| + | uint16_t port_override = data->state.allow_port ? data->set.use_port : 0; |
| + | uint32_t scope_id = 0; |
| + | |
| + | /************************************************************* |
| + | * Check input data |
| + | *************************************************************/ |
| + | if(!Curl_bufref_ptr(&data->state.url)) { |
| + | result = CURLE_URL_MALFORMAT; |
| + | goto out; |
| + | } |
| + | |
| + | up_free(data); /* cleanup previous leftovers first */ |
| + | |
| + | /* parse the URL */ |
| + | if(use_set_uh) |
| + | uh = data->state.uh = curl_url_dup(data->set.uh); |
| + | else |
| + | uh = data->state.uh = curl_url(); |
| + | if(!uh) { |
| + | result = CURLE_OUT_OF_MEMORY; |
| + | goto out; |
| + | } |
| + | |
| + | /* Calculate the *real* URL this transfer uses, applying defaults |
| + | * where information is missing. */ |
| + | if(data->set.str[STRING_DEFAULT_PROTOCOL] && |
| + | !Curl_is_absolute_url(Curl_bufref_ptr(&data->state.url), NULL, 0, TRUE)) { |
| + | char *url = curl_maprintf("%s://%s", |
| + | data->set.str[STRING_DEFAULT_PROTOCOL], |
| + | Curl_bufref_ptr(&data->state.url)); |
| + | if(!url) { |
| + | result = CURLE_OUT_OF_MEMORY; |
| + | goto out; |
| + | } |
| + | Curl_bufref_set(&data->state.url, url, 0, curl_free); |
| + | } |
| + | |
| + | if(!use_set_uh) { |
| + | char *newurl; |
| + | uc = curl_url_set(uh, CURLUPART_URL, Curl_bufref_ptr(&data->state.url), |
| + | (unsigned int)(CURLU_GUESS_SCHEME | |
| + | CURLU_NON_SUPPORT_SCHEME | |
| + | (data->set.disallow_username_in_url ? |
| + | CURLU_DISALLOW_USER : 0) | |
| + | (data->set.path_as_is ? CURLU_PATH_AS_IS : 0))); |
| + | if(uc) { |
| + | failf(data, "URL rejected: %s", curl_url_strerror(uc)); |
| + | result = Curl_uc_to_curlcode(uc); |
| + | goto out; |
| + | } |
| + | |
| + | /* after it was parsed, get the generated normalized version */ |
| + | uc = curl_url_get(uh, CURLUPART_URL, &newurl, CURLU_GET_EMPTY); |
| + | if(uc) { |
| + | result = Curl_uc_to_curlcode(uc); |
| + | goto out; |
| + | } |
| + | Curl_bufref_set(&data->state.url, newurl, 0, curl_free); |
| + | } |
| + | |
| + | #ifdef USE_IPV6 |
| + | scope_id = data->set.scope_id; |
| + | #endif |
| + | |
| + | /* `uh` is now as the connection should use it, probably. */ |
| + | result = Curl_peer_from_url(uh, data, port_override, scope_id, |
| + | &data->state.up, &data->state.origin); |
| + | if(result) |
| + | goto out; |
| + | /* The origin might get changed when HSTS applies */ |
| + | result = hsts_upgrade(data, uh, port_override, scope_id); |
| + | if(result) |
| + | goto out; |
| + | |
| + | /* When the transfers initial_origin is not set, this is the initial |
| + | * request. Remember this starting point. */ |
| + | if(!data->state.initial_origin) |
| + | Curl_peer_link(&data->state.initial_origin, data->state.origin); |
| + | |
| + | uc = curl_url_get(uh, CURLUPART_PATH, &data->state.up.path, CURLU_URLENCODE); |
| + | if(uc) { |
| + | result = Curl_uc_to_curlcode(uc); |
| + | goto out; |
| + | } |
| + | uc = curl_url_get(uh, CURLUPART_QUERY, &data->state.up.query, |
| + | CURLU_GET_EMPTY); |
| + | if(uc && (uc != CURLUE_NO_QUERY)) { |
| + | result = CURLE_OUT_OF_MEMORY; |
| + | goto out; |
| + | } |
| + | |
| + | uc = curl_url_get(uh, CURLUPART_OPTIONS, &data->state.up.options, |
| + | CURLU_URLDECODE); |
| + | if(uc && (uc != CURLUE_NO_OPTIONS)) { |
| + | result = Curl_uc_to_curlcode(uc); |
| + | goto out; |
| + | } |
| + | |
| + | result = url_set_data_creds(data, uh); |
| + | if(result) |
| + | goto out; |
| + | |
| + | out: |
| + | return result; |
| + | } |
| + | |
| /** | |
| * Find an existing connection for the transfer or create a new one. | |
| * Returns | |
| @@ -2837,7 +2410,7 @@ static CURLcode url_find_or_create_conn(struct Curl_easy *data) | |
| infof(data, "Reusing existing %s: connection%s with %s %s", | |
| conn->given->name, | |
| tls_upgraded ? " (upgraded to SSL)" : "", | |
| - | conn->bits.proxy ? "proxy" : "host", |
| + | (conn->socks_proxy.peer || conn->http_proxy.peer) ? "proxy" : "host", |
| conn->socks_proxy.peer ? conn->socks_proxy.peer->user_hostname : | |
| conn->http_proxy.peer ? conn->http_proxy.peer->user_hostname : | |
| conn->origin->hostname); | |
| @@ -2936,7 +2509,7 @@ static CURLcode url_find_or_create_conn(struct Curl_easy *data) | |
| #ifdef CURL_DISABLE_PROXY | |
| 0 | |
| #else | |
| - | data->conn->bits.proxy |
| + | (data->conn->socks_proxy.peer || data->conn->http_proxy.peer) |
| #endif | |
| ; | |
| @@ -2953,21 +2526,33 @@ out: | |
| CURLcode Curl_connect(struct Curl_easy *data, bool *pconnected) | |
| { | |
| CURLcode result; | |
| - | struct connectdata *conn; |
| + | struct connectdata *conn = NULL; |
| *pconnected = FALSE; | |
| /* Set the request to virgin state based on transfer settings */ | |
| Curl_req_hard_reset(&data->req, data); | |
| + | /* Determine the origin of the transfer and what credentials to use */ |
| + | result = url_set_data_origin_and_creds(data); |
| + | if(result) |
| + | goto out; |
| + | if(!data->state.origin) { /* just make really sure */ |
| + | DEBUGASSERT(0); |
| + | result = CURLE_FAILED_INIT; |
| + | goto out; |
| + | } |
| /* Get or create a connection for the transfer. */ | |
| result = url_find_or_create_conn(data); | |
| conn = data->conn; | |
| - | |
| if(result) | |
| goto out; | |
| + | if(!data->conn) { /* just make really sure */ |
| + | DEBUGASSERT(0); |
| + | result = CURLE_FAILED_INIT; |
| + | goto out; |
| + | } |
| - | DEBUGASSERT(conn); |
| Curl_pgrsTime(data, TIMER_POSTQUEUE); | |
| if(conn->bits.reuse) { | |
| if(conn->attached_xfers > 1) | |
| @@ -62,6 +62,7 @@ | |
| #include "hostip.h" | |
| #include "hash.h" | |
| #include "peer.h" | |
| + | #include "proxy.h" |
| #include "splay.h" | |
| #include "curlx/dynbuf.h" | |
| #include "bufref.h" | |
| @@ -191,13 +192,7 @@ typedef enum { | |
| struct ConnectBits { | |
| BIT(connect_only); | |
| #ifndef CURL_DISABLE_PROXY | |
| - | BIT(httpproxy); /* if set, this transfer is done through an HTTP proxy */ |
| - | BIT(socksproxy); /* if set, this transfer is done through a socks proxy */ |
| - | BIT(tunnel_proxy); /* if CONNECT is used to "tunnel" through the proxy. |
| - | This is implicit when SSL-protocols are used through |
| - | proxies, but can also be enabled explicitly by |
| - | apps */ |
| - | BIT(proxy); /* if set, this transfer is done through a proxy - any type */ |
| + | BIT(origin_is_proxy); /* if set, the connection's origin is a proxy */ |
| #endif | |
| /* always modify bits.close with the connclose() and connkeep() macros! */ | |
| BIT(close); /* if set, we close the connection after this request */ | |
| @@ -268,12 +263,6 @@ struct ip_quadruple { | |
| ((x)->transport == TRNSPRT_UDP) || \ | |
| ((x)->transport == TRNSPRT_QUIC)) | |
| - | struct proxy_info { |
| - | struct Curl_peer *peer; /* proxy to this peer */ |
| - | struct Curl_creds *creds; /* use these credentials, maybe NULL */ |
| - | uint8_t proxytype; /* what kind of proxy that is in use */ |
| - | }; |
| - | |
| /* | |
| * The connectdata struct contains all fields and variables that should be | |
| * unique for an entire connection. | |
| @@ -437,9 +426,6 @@ struct PureInfo { | |
| session handle without disturbing information which is still alive, and | |
| that might be reused, in the connection pool. */ | |
| struct ip_quadruple primary; | |
| - | int conn_remote_port; /* this is the "remote port", which is the port |
| - | number of the used URL, independent of proxy or |
| - | not */ |
| const char *conn_scheme; | |
| uint32_t conn_protocol; | |
| struct curl_certinfo certs; /* info about the certs. Asked for with | |
| @@ -601,6 +587,9 @@ struct UrlState { | |
| Credentials from CURLOPT_* are only valid for this origin. | |
| Always set once a transfer starts searching for connections. */ | |
| struct Curl_peer *initial_origin; | |
| + | /* Current origin of the transfer, changes to origin of follow |
| + | * requests. */ |
| + | struct Curl_peer *origin; |
| int os_errno; /* filled in with errno whenever an error occurs */ | |
| int requests; /* request counter: redirects + authentication retakes */ | |
| @@ -427,7 +427,7 @@ CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data, | |
| curl_msnprintf(&HA1_hex[2 * i], 3, "%02x", digest[i]); | |
| /* Generate our SPN */ | |
| - | spn = Curl_auth_build_spn(service, data->conn->origin->hostname, NULL); |
| + | spn = Curl_auth_build_spn(service, data->state.origin->hostname, NULL); |
| if(!spn) | |
| return CURLE_OUT_OF_MEMORY; | |
| @@ -133,7 +133,7 @@ CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data, | |
| return CURLE_OUT_OF_MEMORY; | |
| /* Generate our SPN */ | |
| - | spn = Curl_auth_build_spn(service, data->conn->origin->hostname, NULL); |
| + | spn = Curl_auth_build_spn(service, data->state.origin->hostname, NULL); |
| if(!spn) { | |
| curlx_free(output_token); | |
| return CURLE_OUT_OF_MEMORY; | |
| @@ -139,7 +139,7 @@ bool Curl_auth_user_contains_domain(struct Curl_creds *creds) | |
| */ | |
| bool Curl_auth_allowed_to_host(struct Curl_easy *data) | |
| { | |
| - | return Curl_auth_allowed_to_origin(data, data->conn->origin); |
| + | return Curl_auth_allowed_to_origin(data, data->state.origin); |
| } | |
| bool Curl_auth_allowed_to_origin(struct Curl_easy *data, | |
| @@ -872,7 +872,7 @@ CURLcode Curl_conn_may_http3(struct Curl_easy *data, | |
| return CURLE_URL_MALFORMAT; | |
| } | |
| #ifndef CURL_DISABLE_PROXY | |
| - | if(conn->bits.socksproxy) { |
| + | if(conn->socks_proxy.peer) { |
| failf(data, "HTTP/3 is not supported over a SOCKS proxy"); | |
| return CURLE_URL_MALFORMAT; | |
| } | |
| @@ -74,6 +74,24 @@ class TestSocks: | |
| else: | |
| r.check_response(http_status=200) | |
| + | # download via socks to https: proxy (no tunnel) |
| + | @pytest.mark.parametrize("sproto", ['socks4', 'socks5']) |
| + | @pytest.mark.parametrize("proto", Env.http_h1_h2_protos()) |
| + | def test_40_02b_socks_https_proxy(self, env: Env, sproto, proto, danted: Dante, httpd): |
| + | if proto == 'h2' and not env.curl_uses_lib('nghttp2'): |
| + | pytest.skip('only supported with nghttp2') |
| + | curl = CurlClient(env=env, socks_args=[ |
| + | f'--{sproto}', f'127.0.0.1:{danted.port}' |
| + | ]) |
| + | url = f'http://localhost:{env.http_port}/data.json' |
| + | xargs = curl.get_proxy_args(proto=proto, tunnel=False) |
| + | r = curl.http_download(urls=[url], alpn_proto=proto, with_stats=True, |
| + | extra_args=xargs) |
| + | r.check_response(http_status=200) |
| + | exp_http_version = '2' if proto == 'h2' else '1.1' |
| + | assert r.stats[0]['proxy_used'] == 1, f'{r}' |
| + | assert r.stats[0]['http_version'] == exp_http_version, f'{r}' |
| + | |
| @pytest.mark.parametrize("sproto", ['socks4', 'socks5']) | |
| @pytest.mark.parametrize("proto", Env.http_h1_h2_protos()) | |
| def test_40_03_dl_serial(self, env: Env, httpd, danted, proto, sproto): | |
| @@ -22,7 +22,7 @@ | |
| * | |
| ***************************************************************************/ | |
| #include "unitcheck.h" | |
| - | #include "noproxy.h" |
| + | #include "proxy.h" |
| static CURLcode test_unit1614(const char *arg) | |
| { | |
| @@ -176,7 +176,7 @@ static CURLcode test_unit1614(const char *arg) | |
| } | |
| #endif | |
| for(i = 0; list[i].a; i++) { | |
| - | bool match = Curl_check_noproxy(list[i].a, list[i].n); |
| + | bool match = proxy_check_noproxy(list[i].a, list[i].n); |
| if(match != list[i].match) { | |
| curl_mfprintf(stderr, "%s in %s should %smatch\n", | |
| list[i].a, list[i].n, | |