/*************************************************************************** * _ _ ____ _ * Project ___| | | | _ \| | * / __| | | | |_) | | * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * * Copyright (C) Daniel Stenberg, , 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" #include "http_proxy.h" #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_PROXY) #include #ifdef USE_HYPER #include #endif #include "sendf.h" #include "http.h" #include "url.h" #include "select.h" #include "progress.h" #include "cfilters.h" #include "cf-h1-proxy.h" #include "cf-h2-proxy.h" #include "connect.h" #include "curlx.h" #include "vtls/vtls.h" #include "transfer.h" #include "multiif.h" /* The last 3 #include files should be in this order */ #include "curl_printf.h" #include "curl_memory.h" #include "memdebug.h" struct cf_proxy_ctx { /* the protocol specific sub-filter we install during connect */ struct Curl_cfilter *cf_protocol; }; static CURLcode http_proxy_cf_connect(struct Curl_cfilter *cf, struct Curl_easy *data, bool blocking, bool *done) { struct cf_proxy_ctx *ctx = cf->ctx; CURLcode result; if(cf->connected) { *done = TRUE; return CURLE_OK; } DEBUGF(LOG_CF(data, cf, "connect")); connect_sub: result = cf->next->cft->connect(cf->next, data, blocking, done); if(result || !*done) return result; *done = FALSE; if(!ctx->cf_protocol) { struct Curl_cfilter *cf_protocol = NULL; int alpn = Curl_conn_cf_is_ssl(cf->next)? cf->conn->proxy_alpn : CURL_HTTP_VERSION_1_1; /* First time call after the subchain connected */ switch(alpn) { case CURL_HTTP_VERSION_NONE: case CURL_HTTP_VERSION_1_0: case CURL_HTTP_VERSION_1_1: DEBUGF(LOG_CF(data, cf, "installing subfilter for HTTP/1.1")); infof(data, "CONNECT tunnel: HTTP/1.%d negotiated", (alpn == CURL_HTTP_VERSION_1_0)? 0 : 1); result = Curl_cf_h1_proxy_insert_after(cf, data); if(result) goto out; cf_protocol = cf->next; break; #ifdef USE_NGHTTP2 case CURL_HTTP_VERSION_2: DEBUGF(LOG_CF(data, cf, "installing subfilter for HTTP/2")); infof(data, "CONNECT tunnel: HTTP/2 negotiated"); result = Curl_cf_h2_proxy_insert_after(cf, data); if(result) goto out; cf_protocol = cf->next; break; #endif default: DEBUGF(LOG_CF(data, cf, "installing subfilter for default HTTP/1.1")); infof(data, "CONNECT tunnel: unsupported ALPN(%d) negotiated", alpn); result = CURLE_COULDNT_CONNECT; goto out; } ctx->cf_protocol = cf_protocol; /* after we installed the filter "below" us, we call connect * on out sub-chain again. */ goto connect_sub; } else { /* subchain connected and we had already installed the protocol filter. * This means the protocol tunnel is established, we are done. */ DEBUGASSERT(ctx->cf_protocol); result = CURLE_OK; } out: if(!result) { cf->connected = TRUE; *done = TRUE; } return result; } void Curl_cf_http_proxy_get_host(struct Curl_cfilter *cf, struct Curl_easy *data, const char **phost, const char **pdisplay_host, int *pport) { (void)data; if(!cf->connected) { *phost = cf->conn->http_proxy.host.name; *pdisplay_host = cf->conn->http_proxy.host.dispname; *pport = (int)cf->conn->http_proxy.port; } else { cf->next->cft->get_host(cf->next, data, phost, pdisplay_host, pport); } } static void http_proxy_cf_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) { struct cf_proxy_ctx *ctx = cf->ctx; (void)data; DEBUGF(LOG_CF(data, cf, "destroy")); free(ctx); } static void http_proxy_cf_close(struct Curl_cfilter *cf, struct Curl_easy *data) { struct cf_proxy_ctx *ctx = cf->ctx; DEBUGF(LOG_CF(data, cf, "close")); cf->connected = FALSE; if(ctx->cf_protocol) { struct Curl_cfilter *f; /* if someone already removed it, we assume he also * took care of destroying it. */ for(f = cf->next; f; f = f->next) { if(f == ctx->cf_protocol) { /* still in our sub-chain */ Curl_conn_cf_discard_sub(cf, ctx->cf_protocol, data, FALSE); break; } } ctx->cf_protocol = NULL; } if(cf->next) cf->next->cft->close(cf->next, data); } struct Curl_cftype Curl_cft_http_proxy = { "HTTP-PROXY", CF_TYPE_IP_CONNECT, 0, http_proxy_cf_destroy, http_proxy_cf_connect, http_proxy_cf_close, Curl_cf_http_proxy_get_host, Curl_cf_def_get_select_socks, Curl_cf_def_data_pending, Curl_cf_def_send, Curl_cf_def_recv, Curl_cf_def_cntrl, Curl_cf_def_conn_is_alive, Curl_cf_def_conn_keep_alive, Curl_cf_def_query, }; CURLcode Curl_cf_http_proxy_insert_after(struct Curl_cfilter *cf_at, struct Curl_easy *data) { struct Curl_cfilter *cf; struct cf_proxy_ctx *ctx = NULL; CURLcode result; (void)data; ctx = calloc(1, sizeof(*ctx)); if(!ctx) { result = CURLE_OUT_OF_MEMORY; goto out; } result = Curl_cf_create(&cf, &Curl_cft_http_proxy, ctx); if(result) goto out; ctx = NULL; Curl_conn_cf_insert_after(cf_at, cf); out: free(ctx); return result; } #endif /* ! CURL_DISABLE_HTTP && !CURL_DISABLE_PROXY */