113498266Sopenharmony_ci/*************************************************************************** 213498266Sopenharmony_ci * _ _ ____ _ 313498266Sopenharmony_ci * Project ___| | | | _ \| | 413498266Sopenharmony_ci * / __| | | | |_) | | 513498266Sopenharmony_ci * | (__| |_| | _ <| |___ 613498266Sopenharmony_ci * \___|\___/|_| \_\_____| 713498266Sopenharmony_ci * 813498266Sopenharmony_ci * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. 913498266Sopenharmony_ci * 1013498266Sopenharmony_ci * This software is licensed as described in the file COPYING, which 1113498266Sopenharmony_ci * you should have received as part of this distribution. The terms 1213498266Sopenharmony_ci * are also available at https://curl.se/docs/copyright.html. 1313498266Sopenharmony_ci * 1413498266Sopenharmony_ci * You may opt to use, copy, modify, merge, publish, distribute and/or sell 1513498266Sopenharmony_ci * copies of the Software, and permit persons to whom the Software is 1613498266Sopenharmony_ci * furnished to do so, under the terms of the COPYING file. 1713498266Sopenharmony_ci * 1813498266Sopenharmony_ci * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 1913498266Sopenharmony_ci * KIND, either express or implied. 2013498266Sopenharmony_ci * 2113498266Sopenharmony_ci * SPDX-License-Identifier: curl 2213498266Sopenharmony_ci * 2313498266Sopenharmony_ci ***************************************************************************/ 2413498266Sopenharmony_ci 2513498266Sopenharmony_ci#include "curl_setup.h" 2613498266Sopenharmony_ci 2713498266Sopenharmony_ci#include "http_proxy.h" 2813498266Sopenharmony_ci 2913498266Sopenharmony_ci#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_PROXY) 3013498266Sopenharmony_ci 3113498266Sopenharmony_ci#include <curl/curl.h> 3213498266Sopenharmony_ci#ifdef USE_HYPER 3313498266Sopenharmony_ci#include <hyper.h> 3413498266Sopenharmony_ci#endif 3513498266Sopenharmony_ci#include "sendf.h" 3613498266Sopenharmony_ci#include "http.h" 3713498266Sopenharmony_ci#include "url.h" 3813498266Sopenharmony_ci#include "select.h" 3913498266Sopenharmony_ci#include "progress.h" 4013498266Sopenharmony_ci#include "cfilters.h" 4113498266Sopenharmony_ci#include "cf-h1-proxy.h" 4213498266Sopenharmony_ci#include "cf-h2-proxy.h" 4313498266Sopenharmony_ci#include "connect.h" 4413498266Sopenharmony_ci#include "curlx.h" 4513498266Sopenharmony_ci#include "vtls/vtls.h" 4613498266Sopenharmony_ci#include "transfer.h" 4713498266Sopenharmony_ci#include "multiif.h" 4813498266Sopenharmony_ci 4913498266Sopenharmony_ci/* The last 3 #include files should be in this order */ 5013498266Sopenharmony_ci#include "curl_printf.h" 5113498266Sopenharmony_ci#include "curl_memory.h" 5213498266Sopenharmony_ci#include "memdebug.h" 5313498266Sopenharmony_ci 5413498266Sopenharmony_ci 5513498266Sopenharmony_ciCURLcode Curl_http_proxy_get_destination(struct Curl_cfilter *cf, 5613498266Sopenharmony_ci const char **phostname, 5713498266Sopenharmony_ci int *pport, bool *pipv6_ip) 5813498266Sopenharmony_ci{ 5913498266Sopenharmony_ci DEBUGASSERT(cf); 6013498266Sopenharmony_ci DEBUGASSERT(cf->conn); 6113498266Sopenharmony_ci 6213498266Sopenharmony_ci if(cf->conn->bits.conn_to_host) 6313498266Sopenharmony_ci *phostname = cf->conn->conn_to_host.name; 6413498266Sopenharmony_ci else if(cf->sockindex == SECONDARYSOCKET) 6513498266Sopenharmony_ci *phostname = cf->conn->secondaryhostname; 6613498266Sopenharmony_ci else 6713498266Sopenharmony_ci *phostname = cf->conn->host.name; 6813498266Sopenharmony_ci 6913498266Sopenharmony_ci if(cf->sockindex == SECONDARYSOCKET) 7013498266Sopenharmony_ci *pport = cf->conn->secondary_port; 7113498266Sopenharmony_ci else if(cf->conn->bits.conn_to_port) 7213498266Sopenharmony_ci *pport = cf->conn->conn_to_port; 7313498266Sopenharmony_ci else 7413498266Sopenharmony_ci *pport = cf->conn->remote_port; 7513498266Sopenharmony_ci 7613498266Sopenharmony_ci if(*phostname != cf->conn->host.name) 7713498266Sopenharmony_ci *pipv6_ip = (strchr(*phostname, ':') != NULL); 7813498266Sopenharmony_ci else 7913498266Sopenharmony_ci *pipv6_ip = cf->conn->bits.ipv6_ip; 8013498266Sopenharmony_ci 8113498266Sopenharmony_ci return CURLE_OK; 8213498266Sopenharmony_ci} 8313498266Sopenharmony_ci 8413498266Sopenharmony_ciCURLcode Curl_http_proxy_create_CONNECT(struct httpreq **preq, 8513498266Sopenharmony_ci struct Curl_cfilter *cf, 8613498266Sopenharmony_ci struct Curl_easy *data, 8713498266Sopenharmony_ci int http_version_major) 8813498266Sopenharmony_ci{ 8913498266Sopenharmony_ci const char *hostname = NULL; 9013498266Sopenharmony_ci char *authority = NULL; 9113498266Sopenharmony_ci int port; 9213498266Sopenharmony_ci bool ipv6_ip; 9313498266Sopenharmony_ci CURLcode result; 9413498266Sopenharmony_ci struct httpreq *req = NULL; 9513498266Sopenharmony_ci 9613498266Sopenharmony_ci result = Curl_http_proxy_get_destination(cf, &hostname, &port, &ipv6_ip); 9713498266Sopenharmony_ci if(result) 9813498266Sopenharmony_ci goto out; 9913498266Sopenharmony_ci 10013498266Sopenharmony_ci authority = aprintf("%s%s%s:%d", ipv6_ip?"[":"", hostname, 10113498266Sopenharmony_ci ipv6_ip?"]":"", port); 10213498266Sopenharmony_ci if(!authority) { 10313498266Sopenharmony_ci result = CURLE_OUT_OF_MEMORY; 10413498266Sopenharmony_ci goto out; 10513498266Sopenharmony_ci } 10613498266Sopenharmony_ci 10713498266Sopenharmony_ci result = Curl_http_req_make(&req, "CONNECT", sizeof("CONNECT")-1, 10813498266Sopenharmony_ci NULL, 0, authority, strlen(authority), 10913498266Sopenharmony_ci NULL, 0); 11013498266Sopenharmony_ci if(result) 11113498266Sopenharmony_ci goto out; 11213498266Sopenharmony_ci 11313498266Sopenharmony_ci /* Setup the proxy-authorization header, if any */ 11413498266Sopenharmony_ci result = Curl_http_output_auth(data, cf->conn, req->method, HTTPREQ_GET, 11513498266Sopenharmony_ci req->authority, TRUE); 11613498266Sopenharmony_ci if(result) 11713498266Sopenharmony_ci goto out; 11813498266Sopenharmony_ci 11913498266Sopenharmony_ci /* If user is not overriding Host: header, we add for HTTP/1.x */ 12013498266Sopenharmony_ci if(http_version_major == 1 && 12113498266Sopenharmony_ci !Curl_checkProxyheaders(data, cf->conn, STRCONST("Host"))) { 12213498266Sopenharmony_ci result = Curl_dynhds_cadd(&req->headers, "Host", authority); 12313498266Sopenharmony_ci if(result) 12413498266Sopenharmony_ci goto out; 12513498266Sopenharmony_ci } 12613498266Sopenharmony_ci 12713498266Sopenharmony_ci if(data->state.aptr.proxyuserpwd) { 12813498266Sopenharmony_ci result = Curl_dynhds_h1_cadd_line(&req->headers, 12913498266Sopenharmony_ci data->state.aptr.proxyuserpwd); 13013498266Sopenharmony_ci if(result) 13113498266Sopenharmony_ci goto out; 13213498266Sopenharmony_ci } 13313498266Sopenharmony_ci 13413498266Sopenharmony_ci if(!Curl_checkProxyheaders(data, cf->conn, STRCONST("User-Agent")) && 13513498266Sopenharmony_ci data->set.str[STRING_USERAGENT] && *data->set.str[STRING_USERAGENT]) { 13613498266Sopenharmony_ci result = Curl_dynhds_cadd(&req->headers, "User-Agent", 13713498266Sopenharmony_ci data->set.str[STRING_USERAGENT]); 13813498266Sopenharmony_ci if(result) 13913498266Sopenharmony_ci goto out; 14013498266Sopenharmony_ci } 14113498266Sopenharmony_ci 14213498266Sopenharmony_ci if(http_version_major == 1 && 14313498266Sopenharmony_ci !Curl_checkProxyheaders(data, cf->conn, STRCONST("Proxy-Connection"))) { 14413498266Sopenharmony_ci result = Curl_dynhds_cadd(&req->headers, "Proxy-Connection", "Keep-Alive"); 14513498266Sopenharmony_ci if(result) 14613498266Sopenharmony_ci goto out; 14713498266Sopenharmony_ci } 14813498266Sopenharmony_ci 14913498266Sopenharmony_ci result = Curl_dynhds_add_custom(data, TRUE, &req->headers); 15013498266Sopenharmony_ci 15113498266Sopenharmony_ciout: 15213498266Sopenharmony_ci if(result && req) { 15313498266Sopenharmony_ci Curl_http_req_free(req); 15413498266Sopenharmony_ci req = NULL; 15513498266Sopenharmony_ci } 15613498266Sopenharmony_ci free(authority); 15713498266Sopenharmony_ci *preq = req; 15813498266Sopenharmony_ci return result; 15913498266Sopenharmony_ci} 16013498266Sopenharmony_ci 16113498266Sopenharmony_ci 16213498266Sopenharmony_cistruct cf_proxy_ctx { 16313498266Sopenharmony_ci /* the protocol specific sub-filter we install during connect */ 16413498266Sopenharmony_ci struct Curl_cfilter *cf_protocol; 16513498266Sopenharmony_ci}; 16613498266Sopenharmony_ci 16713498266Sopenharmony_cistatic CURLcode http_proxy_cf_connect(struct Curl_cfilter *cf, 16813498266Sopenharmony_ci struct Curl_easy *data, 16913498266Sopenharmony_ci bool blocking, bool *done) 17013498266Sopenharmony_ci{ 17113498266Sopenharmony_ci struct cf_proxy_ctx *ctx = cf->ctx; 17213498266Sopenharmony_ci CURLcode result; 17313498266Sopenharmony_ci 17413498266Sopenharmony_ci if(cf->connected) { 17513498266Sopenharmony_ci *done = TRUE; 17613498266Sopenharmony_ci return CURLE_OK; 17713498266Sopenharmony_ci } 17813498266Sopenharmony_ci 17913498266Sopenharmony_ci CURL_TRC_CF(data, cf, "connect"); 18013498266Sopenharmony_ciconnect_sub: 18113498266Sopenharmony_ci result = cf->next->cft->do_connect(cf->next, data, blocking, done); 18213498266Sopenharmony_ci if(result || !*done) 18313498266Sopenharmony_ci return result; 18413498266Sopenharmony_ci 18513498266Sopenharmony_ci *done = FALSE; 18613498266Sopenharmony_ci if(!ctx->cf_protocol) { 18713498266Sopenharmony_ci struct Curl_cfilter *cf_protocol = NULL; 18813498266Sopenharmony_ci int alpn = Curl_conn_cf_is_ssl(cf->next)? 18913498266Sopenharmony_ci cf->conn->proxy_alpn : CURL_HTTP_VERSION_1_1; 19013498266Sopenharmony_ci 19113498266Sopenharmony_ci /* First time call after the subchain connected */ 19213498266Sopenharmony_ci switch(alpn) { 19313498266Sopenharmony_ci case CURL_HTTP_VERSION_NONE: 19413498266Sopenharmony_ci case CURL_HTTP_VERSION_1_0: 19513498266Sopenharmony_ci case CURL_HTTP_VERSION_1_1: 19613498266Sopenharmony_ci CURL_TRC_CF(data, cf, "installing subfilter for HTTP/1.1"); 19713498266Sopenharmony_ci infof(data, "CONNECT tunnel: HTTP/1.%d negotiated", 19813498266Sopenharmony_ci (alpn == CURL_HTTP_VERSION_1_0)? 0 : 1); 19913498266Sopenharmony_ci result = Curl_cf_h1_proxy_insert_after(cf, data); 20013498266Sopenharmony_ci if(result) 20113498266Sopenharmony_ci goto out; 20213498266Sopenharmony_ci cf_protocol = cf->next; 20313498266Sopenharmony_ci break; 20413498266Sopenharmony_ci#ifdef USE_NGHTTP2 20513498266Sopenharmony_ci case CURL_HTTP_VERSION_2: 20613498266Sopenharmony_ci CURL_TRC_CF(data, cf, "installing subfilter for HTTP/2"); 20713498266Sopenharmony_ci infof(data, "CONNECT tunnel: HTTP/2 negotiated"); 20813498266Sopenharmony_ci result = Curl_cf_h2_proxy_insert_after(cf, data); 20913498266Sopenharmony_ci if(result) 21013498266Sopenharmony_ci goto out; 21113498266Sopenharmony_ci cf_protocol = cf->next; 21213498266Sopenharmony_ci break; 21313498266Sopenharmony_ci#endif 21413498266Sopenharmony_ci default: 21513498266Sopenharmony_ci infof(data, "CONNECT tunnel: unsupported ALPN(%d) negotiated", alpn); 21613498266Sopenharmony_ci result = CURLE_COULDNT_CONNECT; 21713498266Sopenharmony_ci goto out; 21813498266Sopenharmony_ci } 21913498266Sopenharmony_ci 22013498266Sopenharmony_ci ctx->cf_protocol = cf_protocol; 22113498266Sopenharmony_ci /* after we installed the filter "below" us, we call connect 22213498266Sopenharmony_ci * on out sub-chain again. 22313498266Sopenharmony_ci */ 22413498266Sopenharmony_ci goto connect_sub; 22513498266Sopenharmony_ci } 22613498266Sopenharmony_ci else { 22713498266Sopenharmony_ci /* subchain connected and we had already installed the protocol filter. 22813498266Sopenharmony_ci * This means the protocol tunnel is established, we are done. 22913498266Sopenharmony_ci */ 23013498266Sopenharmony_ci DEBUGASSERT(ctx->cf_protocol); 23113498266Sopenharmony_ci result = CURLE_OK; 23213498266Sopenharmony_ci } 23313498266Sopenharmony_ci 23413498266Sopenharmony_ciout: 23513498266Sopenharmony_ci if(!result) { 23613498266Sopenharmony_ci cf->connected = TRUE; 23713498266Sopenharmony_ci *done = TRUE; 23813498266Sopenharmony_ci } 23913498266Sopenharmony_ci return result; 24013498266Sopenharmony_ci} 24113498266Sopenharmony_ci 24213498266Sopenharmony_civoid Curl_cf_http_proxy_get_host(struct Curl_cfilter *cf, 24313498266Sopenharmony_ci struct Curl_easy *data, 24413498266Sopenharmony_ci const char **phost, 24513498266Sopenharmony_ci const char **pdisplay_host, 24613498266Sopenharmony_ci int *pport) 24713498266Sopenharmony_ci{ 24813498266Sopenharmony_ci (void)data; 24913498266Sopenharmony_ci if(!cf->connected) { 25013498266Sopenharmony_ci *phost = cf->conn->http_proxy.host.name; 25113498266Sopenharmony_ci *pdisplay_host = cf->conn->http_proxy.host.dispname; 25213498266Sopenharmony_ci *pport = (int)cf->conn->http_proxy.port; 25313498266Sopenharmony_ci } 25413498266Sopenharmony_ci else { 25513498266Sopenharmony_ci cf->next->cft->get_host(cf->next, data, phost, pdisplay_host, pport); 25613498266Sopenharmony_ci } 25713498266Sopenharmony_ci} 25813498266Sopenharmony_ci 25913498266Sopenharmony_cistatic void http_proxy_cf_destroy(struct Curl_cfilter *cf, 26013498266Sopenharmony_ci struct Curl_easy *data) 26113498266Sopenharmony_ci{ 26213498266Sopenharmony_ci struct cf_proxy_ctx *ctx = cf->ctx; 26313498266Sopenharmony_ci 26413498266Sopenharmony_ci (void)data; 26513498266Sopenharmony_ci CURL_TRC_CF(data, cf, "destroy"); 26613498266Sopenharmony_ci free(ctx); 26713498266Sopenharmony_ci} 26813498266Sopenharmony_ci 26913498266Sopenharmony_cistatic void http_proxy_cf_close(struct Curl_cfilter *cf, 27013498266Sopenharmony_ci struct Curl_easy *data) 27113498266Sopenharmony_ci{ 27213498266Sopenharmony_ci struct cf_proxy_ctx *ctx = cf->ctx; 27313498266Sopenharmony_ci 27413498266Sopenharmony_ci CURL_TRC_CF(data, cf, "close"); 27513498266Sopenharmony_ci cf->connected = FALSE; 27613498266Sopenharmony_ci if(ctx->cf_protocol) { 27713498266Sopenharmony_ci struct Curl_cfilter *f; 27813498266Sopenharmony_ci /* if someone already removed it, we assume he also 27913498266Sopenharmony_ci * took care of destroying it. */ 28013498266Sopenharmony_ci for(f = cf->next; f; f = f->next) { 28113498266Sopenharmony_ci if(f == ctx->cf_protocol) { 28213498266Sopenharmony_ci /* still in our sub-chain */ 28313498266Sopenharmony_ci Curl_conn_cf_discard_sub(cf, ctx->cf_protocol, data, FALSE); 28413498266Sopenharmony_ci break; 28513498266Sopenharmony_ci } 28613498266Sopenharmony_ci } 28713498266Sopenharmony_ci ctx->cf_protocol = NULL; 28813498266Sopenharmony_ci } 28913498266Sopenharmony_ci if(cf->next) 29013498266Sopenharmony_ci cf->next->cft->do_close(cf->next, data); 29113498266Sopenharmony_ci} 29213498266Sopenharmony_ci 29313498266Sopenharmony_ci 29413498266Sopenharmony_cistruct Curl_cftype Curl_cft_http_proxy = { 29513498266Sopenharmony_ci "HTTP-PROXY", 29613498266Sopenharmony_ci CF_TYPE_IP_CONNECT, 29713498266Sopenharmony_ci 0, 29813498266Sopenharmony_ci http_proxy_cf_destroy, 29913498266Sopenharmony_ci http_proxy_cf_connect, 30013498266Sopenharmony_ci http_proxy_cf_close, 30113498266Sopenharmony_ci Curl_cf_http_proxy_get_host, 30213498266Sopenharmony_ci Curl_cf_def_adjust_pollset, 30313498266Sopenharmony_ci Curl_cf_def_data_pending, 30413498266Sopenharmony_ci Curl_cf_def_send, 30513498266Sopenharmony_ci Curl_cf_def_recv, 30613498266Sopenharmony_ci Curl_cf_def_cntrl, 30713498266Sopenharmony_ci Curl_cf_def_conn_is_alive, 30813498266Sopenharmony_ci Curl_cf_def_conn_keep_alive, 30913498266Sopenharmony_ci Curl_cf_def_query, 31013498266Sopenharmony_ci}; 31113498266Sopenharmony_ci 31213498266Sopenharmony_ciCURLcode Curl_cf_http_proxy_insert_after(struct Curl_cfilter *cf_at, 31313498266Sopenharmony_ci struct Curl_easy *data) 31413498266Sopenharmony_ci{ 31513498266Sopenharmony_ci struct Curl_cfilter *cf; 31613498266Sopenharmony_ci struct cf_proxy_ctx *ctx = NULL; 31713498266Sopenharmony_ci CURLcode result; 31813498266Sopenharmony_ci 31913498266Sopenharmony_ci (void)data; 32013498266Sopenharmony_ci ctx = calloc(1, sizeof(*ctx)); 32113498266Sopenharmony_ci if(!ctx) { 32213498266Sopenharmony_ci result = CURLE_OUT_OF_MEMORY; 32313498266Sopenharmony_ci goto out; 32413498266Sopenharmony_ci } 32513498266Sopenharmony_ci result = Curl_cf_create(&cf, &Curl_cft_http_proxy, ctx); 32613498266Sopenharmony_ci if(result) 32713498266Sopenharmony_ci goto out; 32813498266Sopenharmony_ci ctx = NULL; 32913498266Sopenharmony_ci Curl_conn_cf_insert_after(cf_at, cf); 33013498266Sopenharmony_ci 33113498266Sopenharmony_ciout: 33213498266Sopenharmony_ci free(ctx); 33313498266Sopenharmony_ci return result; 33413498266Sopenharmony_ci} 33513498266Sopenharmony_ci 33613498266Sopenharmony_ci#endif /* ! CURL_DISABLE_HTTP && !CURL_DISABLE_PROXY */ 337