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#if !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP) 2813498266Sopenharmony_ci 2913498266Sopenharmony_ci#include <curl/curl.h> 3013498266Sopenharmony_ci#ifdef USE_HYPER 3113498266Sopenharmony_ci#include <hyper.h> 3213498266Sopenharmony_ci#endif 3313498266Sopenharmony_ci#include "urldata.h" 3413498266Sopenharmony_ci#include "dynbuf.h" 3513498266Sopenharmony_ci#include "sendf.h" 3613498266Sopenharmony_ci#include "http.h" 3713498266Sopenharmony_ci#include "http1.h" 3813498266Sopenharmony_ci#include "http_proxy.h" 3913498266Sopenharmony_ci#include "url.h" 4013498266Sopenharmony_ci#include "select.h" 4113498266Sopenharmony_ci#include "progress.h" 4213498266Sopenharmony_ci#include "cfilters.h" 4313498266Sopenharmony_ci#include "cf-h1-proxy.h" 4413498266Sopenharmony_ci#include "connect.h" 4513498266Sopenharmony_ci#include "curl_trc.h" 4613498266Sopenharmony_ci#include "curlx.h" 4713498266Sopenharmony_ci#include "vtls/vtls.h" 4813498266Sopenharmony_ci#include "transfer.h" 4913498266Sopenharmony_ci#include "multiif.h" 5013498266Sopenharmony_ci 5113498266Sopenharmony_ci/* The last 3 #include files should be in this order */ 5213498266Sopenharmony_ci#include "curl_printf.h" 5313498266Sopenharmony_ci#include "curl_memory.h" 5413498266Sopenharmony_ci#include "memdebug.h" 5513498266Sopenharmony_ci 5613498266Sopenharmony_ci 5713498266Sopenharmony_citypedef enum { 5813498266Sopenharmony_ci H1_TUNNEL_INIT, /* init/default/no tunnel state */ 5913498266Sopenharmony_ci H1_TUNNEL_CONNECT, /* CONNECT request is being send */ 6013498266Sopenharmony_ci H1_TUNNEL_RECEIVE, /* CONNECT answer is being received */ 6113498266Sopenharmony_ci H1_TUNNEL_RESPONSE, /* CONNECT response received completely */ 6213498266Sopenharmony_ci H1_TUNNEL_ESTABLISHED, 6313498266Sopenharmony_ci H1_TUNNEL_FAILED 6413498266Sopenharmony_ci} h1_tunnel_state; 6513498266Sopenharmony_ci 6613498266Sopenharmony_ci/* struct for HTTP CONNECT tunneling */ 6713498266Sopenharmony_cistruct h1_tunnel_state { 6813498266Sopenharmony_ci struct HTTP CONNECT; 6913498266Sopenharmony_ci struct dynbuf rcvbuf; 7013498266Sopenharmony_ci struct dynbuf request_data; 7113498266Sopenharmony_ci size_t nsent; 7213498266Sopenharmony_ci size_t headerlines; 7313498266Sopenharmony_ci struct Curl_chunker ch; 7413498266Sopenharmony_ci enum keeponval { 7513498266Sopenharmony_ci KEEPON_DONE, 7613498266Sopenharmony_ci KEEPON_CONNECT, 7713498266Sopenharmony_ci KEEPON_IGNORE 7813498266Sopenharmony_ci } keepon; 7913498266Sopenharmony_ci curl_off_t cl; /* size of content to read and ignore */ 8013498266Sopenharmony_ci h1_tunnel_state tunnel_state; 8113498266Sopenharmony_ci BIT(chunked_encoding); 8213498266Sopenharmony_ci BIT(close_connection); 8313498266Sopenharmony_ci}; 8413498266Sopenharmony_ci 8513498266Sopenharmony_ci 8613498266Sopenharmony_cistatic bool tunnel_is_established(struct h1_tunnel_state *ts) 8713498266Sopenharmony_ci{ 8813498266Sopenharmony_ci return ts && (ts->tunnel_state == H1_TUNNEL_ESTABLISHED); 8913498266Sopenharmony_ci} 9013498266Sopenharmony_ci 9113498266Sopenharmony_cistatic bool tunnel_is_failed(struct h1_tunnel_state *ts) 9213498266Sopenharmony_ci{ 9313498266Sopenharmony_ci return ts && (ts->tunnel_state == H1_TUNNEL_FAILED); 9413498266Sopenharmony_ci} 9513498266Sopenharmony_ci 9613498266Sopenharmony_cistatic CURLcode tunnel_reinit(struct Curl_cfilter *cf, 9713498266Sopenharmony_ci struct Curl_easy *data, 9813498266Sopenharmony_ci struct h1_tunnel_state *ts) 9913498266Sopenharmony_ci{ 10013498266Sopenharmony_ci (void)data; 10113498266Sopenharmony_ci (void)cf; 10213498266Sopenharmony_ci DEBUGASSERT(ts); 10313498266Sopenharmony_ci Curl_dyn_reset(&ts->rcvbuf); 10413498266Sopenharmony_ci Curl_dyn_reset(&ts->request_data); 10513498266Sopenharmony_ci ts->tunnel_state = H1_TUNNEL_INIT; 10613498266Sopenharmony_ci ts->keepon = KEEPON_CONNECT; 10713498266Sopenharmony_ci ts->cl = 0; 10813498266Sopenharmony_ci ts->close_connection = FALSE; 10913498266Sopenharmony_ci return CURLE_OK; 11013498266Sopenharmony_ci} 11113498266Sopenharmony_ci 11213498266Sopenharmony_cistatic CURLcode tunnel_init(struct Curl_cfilter *cf, 11313498266Sopenharmony_ci struct Curl_easy *data, 11413498266Sopenharmony_ci struct h1_tunnel_state **pts) 11513498266Sopenharmony_ci{ 11613498266Sopenharmony_ci struct h1_tunnel_state *ts; 11713498266Sopenharmony_ci CURLcode result; 11813498266Sopenharmony_ci 11913498266Sopenharmony_ci if(cf->conn->handler->flags & PROTOPT_NOTCPPROXY) { 12013498266Sopenharmony_ci failf(data, "%s cannot be done over CONNECT", cf->conn->handler->scheme); 12113498266Sopenharmony_ci return CURLE_UNSUPPORTED_PROTOCOL; 12213498266Sopenharmony_ci } 12313498266Sopenharmony_ci 12413498266Sopenharmony_ci /* we might need the upload buffer for streaming a partial request */ 12513498266Sopenharmony_ci result = Curl_get_upload_buffer(data); 12613498266Sopenharmony_ci if(result) 12713498266Sopenharmony_ci return result; 12813498266Sopenharmony_ci 12913498266Sopenharmony_ci ts = calloc(1, sizeof(*ts)); 13013498266Sopenharmony_ci if(!ts) 13113498266Sopenharmony_ci return CURLE_OUT_OF_MEMORY; 13213498266Sopenharmony_ci 13313498266Sopenharmony_ci infof(data, "allocate connect buffer"); 13413498266Sopenharmony_ci 13513498266Sopenharmony_ci Curl_dyn_init(&ts->rcvbuf, DYN_PROXY_CONNECT_HEADERS); 13613498266Sopenharmony_ci Curl_dyn_init(&ts->request_data, DYN_HTTP_REQUEST); 13713498266Sopenharmony_ci Curl_httpchunk_init(data, &ts->ch, TRUE); 13813498266Sopenharmony_ci 13913498266Sopenharmony_ci *pts = ts; 14013498266Sopenharmony_ci connkeep(cf->conn, "HTTP proxy CONNECT"); 14113498266Sopenharmony_ci return tunnel_reinit(cf, data, ts); 14213498266Sopenharmony_ci} 14313498266Sopenharmony_ci 14413498266Sopenharmony_cistatic void h1_tunnel_go_state(struct Curl_cfilter *cf, 14513498266Sopenharmony_ci struct h1_tunnel_state *ts, 14613498266Sopenharmony_ci h1_tunnel_state new_state, 14713498266Sopenharmony_ci struct Curl_easy *data) 14813498266Sopenharmony_ci{ 14913498266Sopenharmony_ci if(ts->tunnel_state == new_state) 15013498266Sopenharmony_ci return; 15113498266Sopenharmony_ci /* entering this one */ 15213498266Sopenharmony_ci switch(new_state) { 15313498266Sopenharmony_ci case H1_TUNNEL_INIT: 15413498266Sopenharmony_ci CURL_TRC_CF(data, cf, "new tunnel state 'init'"); 15513498266Sopenharmony_ci tunnel_reinit(cf, data, ts); 15613498266Sopenharmony_ci break; 15713498266Sopenharmony_ci 15813498266Sopenharmony_ci case H1_TUNNEL_CONNECT: 15913498266Sopenharmony_ci CURL_TRC_CF(data, cf, "new tunnel state 'connect'"); 16013498266Sopenharmony_ci ts->tunnel_state = H1_TUNNEL_CONNECT; 16113498266Sopenharmony_ci ts->keepon = KEEPON_CONNECT; 16213498266Sopenharmony_ci Curl_dyn_reset(&ts->rcvbuf); 16313498266Sopenharmony_ci break; 16413498266Sopenharmony_ci 16513498266Sopenharmony_ci case H1_TUNNEL_RECEIVE: 16613498266Sopenharmony_ci CURL_TRC_CF(data, cf, "new tunnel state 'receive'"); 16713498266Sopenharmony_ci ts->tunnel_state = H1_TUNNEL_RECEIVE; 16813498266Sopenharmony_ci break; 16913498266Sopenharmony_ci 17013498266Sopenharmony_ci case H1_TUNNEL_RESPONSE: 17113498266Sopenharmony_ci CURL_TRC_CF(data, cf, "new tunnel state 'response'"); 17213498266Sopenharmony_ci ts->tunnel_state = H1_TUNNEL_RESPONSE; 17313498266Sopenharmony_ci break; 17413498266Sopenharmony_ci 17513498266Sopenharmony_ci case H1_TUNNEL_ESTABLISHED: 17613498266Sopenharmony_ci CURL_TRC_CF(data, cf, "new tunnel state 'established'"); 17713498266Sopenharmony_ci infof(data, "CONNECT phase completed"); 17813498266Sopenharmony_ci data->state.authproxy.done = TRUE; 17913498266Sopenharmony_ci data->state.authproxy.multipass = FALSE; 18013498266Sopenharmony_ci FALLTHROUGH(); 18113498266Sopenharmony_ci case H1_TUNNEL_FAILED: 18213498266Sopenharmony_ci if(new_state == H1_TUNNEL_FAILED) 18313498266Sopenharmony_ci CURL_TRC_CF(data, cf, "new tunnel state 'failed'"); 18413498266Sopenharmony_ci ts->tunnel_state = new_state; 18513498266Sopenharmony_ci Curl_dyn_reset(&ts->rcvbuf); 18613498266Sopenharmony_ci Curl_dyn_reset(&ts->request_data); 18713498266Sopenharmony_ci /* restore the protocol pointer */ 18813498266Sopenharmony_ci data->info.httpcode = 0; /* clear it as it might've been used for the 18913498266Sopenharmony_ci proxy */ 19013498266Sopenharmony_ci /* If a proxy-authorization header was used for the proxy, then we should 19113498266Sopenharmony_ci make sure that it isn't accidentally used for the document request 19213498266Sopenharmony_ci after we've connected. So let's free and clear it here. */ 19313498266Sopenharmony_ci Curl_safefree(data->state.aptr.proxyuserpwd); 19413498266Sopenharmony_ci#ifdef USE_HYPER 19513498266Sopenharmony_ci data->state.hconnect = FALSE; 19613498266Sopenharmony_ci#endif 19713498266Sopenharmony_ci break; 19813498266Sopenharmony_ci } 19913498266Sopenharmony_ci} 20013498266Sopenharmony_ci 20113498266Sopenharmony_cistatic void tunnel_free(struct Curl_cfilter *cf, 20213498266Sopenharmony_ci struct Curl_easy *data) 20313498266Sopenharmony_ci{ 20413498266Sopenharmony_ci struct h1_tunnel_state *ts = cf->ctx; 20513498266Sopenharmony_ci if(ts) { 20613498266Sopenharmony_ci h1_tunnel_go_state(cf, ts, H1_TUNNEL_FAILED, data); 20713498266Sopenharmony_ci Curl_dyn_free(&ts->rcvbuf); 20813498266Sopenharmony_ci Curl_dyn_free(&ts->request_data); 20913498266Sopenharmony_ci Curl_httpchunk_free(data, &ts->ch); 21013498266Sopenharmony_ci free(ts); 21113498266Sopenharmony_ci cf->ctx = NULL; 21213498266Sopenharmony_ci } 21313498266Sopenharmony_ci} 21413498266Sopenharmony_ci 21513498266Sopenharmony_ci#ifndef USE_HYPER 21613498266Sopenharmony_cistatic CURLcode start_CONNECT(struct Curl_cfilter *cf, 21713498266Sopenharmony_ci struct Curl_easy *data, 21813498266Sopenharmony_ci struct h1_tunnel_state *ts) 21913498266Sopenharmony_ci{ 22013498266Sopenharmony_ci struct httpreq *req = NULL; 22113498266Sopenharmony_ci int http_minor; 22213498266Sopenharmony_ci CURLcode result; 22313498266Sopenharmony_ci 22413498266Sopenharmony_ci /* This only happens if we've looped here due to authentication 22513498266Sopenharmony_ci reasons, and we don't really use the newly cloned URL here 22613498266Sopenharmony_ci then. Just free() it. */ 22713498266Sopenharmony_ci Curl_safefree(data->req.newurl); 22813498266Sopenharmony_ci 22913498266Sopenharmony_ci result = Curl_http_proxy_create_CONNECT(&req, cf, data, 1); 23013498266Sopenharmony_ci if(result) 23113498266Sopenharmony_ci goto out; 23213498266Sopenharmony_ci 23313498266Sopenharmony_ci infof(data, "Establish HTTP proxy tunnel to %s", req->authority); 23413498266Sopenharmony_ci 23513498266Sopenharmony_ci Curl_dyn_reset(&ts->request_data); 23613498266Sopenharmony_ci ts->nsent = 0; 23713498266Sopenharmony_ci ts->headerlines = 0; 23813498266Sopenharmony_ci http_minor = (cf->conn->http_proxy.proxytype == CURLPROXY_HTTP_1_0) ? 0 : 1; 23913498266Sopenharmony_ci 24013498266Sopenharmony_ci result = Curl_h1_req_write_head(req, http_minor, &ts->request_data); 24113498266Sopenharmony_ci 24213498266Sopenharmony_ciout: 24313498266Sopenharmony_ci if(result) 24413498266Sopenharmony_ci failf(data, "Failed sending CONNECT to proxy"); 24513498266Sopenharmony_ci if(req) 24613498266Sopenharmony_ci Curl_http_req_free(req); 24713498266Sopenharmony_ci return result; 24813498266Sopenharmony_ci} 24913498266Sopenharmony_ci 25013498266Sopenharmony_cistatic CURLcode send_CONNECT(struct Curl_cfilter *cf, 25113498266Sopenharmony_ci struct Curl_easy *data, 25213498266Sopenharmony_ci struct h1_tunnel_state *ts, 25313498266Sopenharmony_ci bool *done) 25413498266Sopenharmony_ci{ 25513498266Sopenharmony_ci char *buf = Curl_dyn_ptr(&ts->request_data); 25613498266Sopenharmony_ci size_t request_len = Curl_dyn_len(&ts->request_data); 25713498266Sopenharmony_ci size_t blen = request_len; 25813498266Sopenharmony_ci CURLcode result = CURLE_OK; 25913498266Sopenharmony_ci ssize_t nwritten; 26013498266Sopenharmony_ci 26113498266Sopenharmony_ci if(blen <= ts->nsent) 26213498266Sopenharmony_ci goto out; /* we are done */ 26313498266Sopenharmony_ci 26413498266Sopenharmony_ci blen -= ts->nsent; 26513498266Sopenharmony_ci buf += ts->nsent; 26613498266Sopenharmony_ci 26713498266Sopenharmony_ci nwritten = cf->next->cft->do_send(cf->next, data, buf, blen, &result); 26813498266Sopenharmony_ci if(nwritten < 0) { 26913498266Sopenharmony_ci if(result == CURLE_AGAIN) { 27013498266Sopenharmony_ci result = CURLE_OK; 27113498266Sopenharmony_ci } 27213498266Sopenharmony_ci goto out; 27313498266Sopenharmony_ci } 27413498266Sopenharmony_ci 27513498266Sopenharmony_ci DEBUGASSERT(blen >= (size_t)nwritten); 27613498266Sopenharmony_ci ts->nsent += (size_t)nwritten; 27713498266Sopenharmony_ci Curl_debug(data, CURLINFO_HEADER_OUT, buf, (size_t)nwritten); 27813498266Sopenharmony_ci 27913498266Sopenharmony_ciout: 28013498266Sopenharmony_ci if(result) 28113498266Sopenharmony_ci failf(data, "Failed sending CONNECT to proxy"); 28213498266Sopenharmony_ci *done = (!result && (ts->nsent >= request_len)); 28313498266Sopenharmony_ci return result; 28413498266Sopenharmony_ci} 28513498266Sopenharmony_ci 28613498266Sopenharmony_cistatic CURLcode on_resp_header(struct Curl_cfilter *cf, 28713498266Sopenharmony_ci struct Curl_easy *data, 28813498266Sopenharmony_ci struct h1_tunnel_state *ts, 28913498266Sopenharmony_ci const char *header) 29013498266Sopenharmony_ci{ 29113498266Sopenharmony_ci CURLcode result = CURLE_OK; 29213498266Sopenharmony_ci struct SingleRequest *k = &data->req; 29313498266Sopenharmony_ci (void)cf; 29413498266Sopenharmony_ci 29513498266Sopenharmony_ci if((checkprefix("WWW-Authenticate:", header) && 29613498266Sopenharmony_ci (401 == k->httpcode)) || 29713498266Sopenharmony_ci (checkprefix("Proxy-authenticate:", header) && 29813498266Sopenharmony_ci (407 == k->httpcode))) { 29913498266Sopenharmony_ci 30013498266Sopenharmony_ci bool proxy = (k->httpcode == 407) ? TRUE : FALSE; 30113498266Sopenharmony_ci char *auth = Curl_copy_header_value(header); 30213498266Sopenharmony_ci if(!auth) 30313498266Sopenharmony_ci return CURLE_OUT_OF_MEMORY; 30413498266Sopenharmony_ci 30513498266Sopenharmony_ci CURL_TRC_CF(data, cf, "CONNECT: fwd auth header '%s'", header); 30613498266Sopenharmony_ci result = Curl_http_input_auth(data, proxy, auth); 30713498266Sopenharmony_ci 30813498266Sopenharmony_ci free(auth); 30913498266Sopenharmony_ci 31013498266Sopenharmony_ci if(result) 31113498266Sopenharmony_ci return result; 31213498266Sopenharmony_ci } 31313498266Sopenharmony_ci else if(checkprefix("Content-Length:", header)) { 31413498266Sopenharmony_ci if(k->httpcode/100 == 2) { 31513498266Sopenharmony_ci /* A client MUST ignore any Content-Length or Transfer-Encoding 31613498266Sopenharmony_ci header fields received in a successful response to CONNECT. 31713498266Sopenharmony_ci "Successful" described as: 2xx (Successful). RFC 7231 4.3.6 */ 31813498266Sopenharmony_ci infof(data, "Ignoring Content-Length in CONNECT %03d response", 31913498266Sopenharmony_ci k->httpcode); 32013498266Sopenharmony_ci } 32113498266Sopenharmony_ci else { 32213498266Sopenharmony_ci (void)curlx_strtoofft(header + strlen("Content-Length:"), 32313498266Sopenharmony_ci NULL, 10, &ts->cl); 32413498266Sopenharmony_ci } 32513498266Sopenharmony_ci } 32613498266Sopenharmony_ci else if(Curl_compareheader(header, 32713498266Sopenharmony_ci STRCONST("Connection:"), STRCONST("close"))) 32813498266Sopenharmony_ci ts->close_connection = TRUE; 32913498266Sopenharmony_ci else if(checkprefix("Transfer-Encoding:", header)) { 33013498266Sopenharmony_ci if(k->httpcode/100 == 2) { 33113498266Sopenharmony_ci /* A client MUST ignore any Content-Length or Transfer-Encoding 33213498266Sopenharmony_ci header fields received in a successful response to CONNECT. 33313498266Sopenharmony_ci "Successful" described as: 2xx (Successful). RFC 7231 4.3.6 */ 33413498266Sopenharmony_ci infof(data, "Ignoring Transfer-Encoding in " 33513498266Sopenharmony_ci "CONNECT %03d response", k->httpcode); 33613498266Sopenharmony_ci } 33713498266Sopenharmony_ci else if(Curl_compareheader(header, 33813498266Sopenharmony_ci STRCONST("Transfer-Encoding:"), 33913498266Sopenharmony_ci STRCONST("chunked"))) { 34013498266Sopenharmony_ci infof(data, "CONNECT responded chunked"); 34113498266Sopenharmony_ci ts->chunked_encoding = TRUE; 34213498266Sopenharmony_ci /* reset our chunky engine */ 34313498266Sopenharmony_ci Curl_httpchunk_reset(data, &ts->ch, TRUE); 34413498266Sopenharmony_ci } 34513498266Sopenharmony_ci } 34613498266Sopenharmony_ci else if(Curl_compareheader(header, 34713498266Sopenharmony_ci STRCONST("Proxy-Connection:"), 34813498266Sopenharmony_ci STRCONST("close"))) 34913498266Sopenharmony_ci ts->close_connection = TRUE; 35013498266Sopenharmony_ci else if(!strncmp(header, "HTTP/1.", 7) && 35113498266Sopenharmony_ci ((header[7] == '0') || (header[7] == '1')) && 35213498266Sopenharmony_ci (header[8] == ' ') && 35313498266Sopenharmony_ci ISDIGIT(header[9]) && ISDIGIT(header[10]) && ISDIGIT(header[11]) && 35413498266Sopenharmony_ci !ISDIGIT(header[12])) { 35513498266Sopenharmony_ci /* store the HTTP code from the proxy */ 35613498266Sopenharmony_ci data->info.httpproxycode = k->httpcode = (header[9] - '0') * 100 + 35713498266Sopenharmony_ci (header[10] - '0') * 10 + (header[11] - '0'); 35813498266Sopenharmony_ci } 35913498266Sopenharmony_ci return result; 36013498266Sopenharmony_ci} 36113498266Sopenharmony_ci 36213498266Sopenharmony_cistatic CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf, 36313498266Sopenharmony_ci struct Curl_easy *data, 36413498266Sopenharmony_ci struct h1_tunnel_state *ts, 36513498266Sopenharmony_ci bool *done) 36613498266Sopenharmony_ci{ 36713498266Sopenharmony_ci CURLcode result = CURLE_OK; 36813498266Sopenharmony_ci struct SingleRequest *k = &data->req; 36913498266Sopenharmony_ci curl_socket_t tunnelsocket = Curl_conn_cf_get_socket(cf, data); 37013498266Sopenharmony_ci char *linep; 37113498266Sopenharmony_ci size_t line_len; 37213498266Sopenharmony_ci int error, writetype; 37313498266Sopenharmony_ci 37413498266Sopenharmony_ci#define SELECT_OK 0 37513498266Sopenharmony_ci#define SELECT_ERROR 1 37613498266Sopenharmony_ci 37713498266Sopenharmony_ci error = SELECT_OK; 37813498266Sopenharmony_ci *done = FALSE; 37913498266Sopenharmony_ci 38013498266Sopenharmony_ci if(!Curl_conn_data_pending(data, cf->sockindex)) 38113498266Sopenharmony_ci return CURLE_OK; 38213498266Sopenharmony_ci 38313498266Sopenharmony_ci while(ts->keepon) { 38413498266Sopenharmony_ci ssize_t nread; 38513498266Sopenharmony_ci char byte; 38613498266Sopenharmony_ci 38713498266Sopenharmony_ci /* Read one byte at a time to avoid a race condition. Wait at most one 38813498266Sopenharmony_ci second before looping to ensure continuous pgrsUpdates. */ 38913498266Sopenharmony_ci result = Curl_read(data, tunnelsocket, &byte, 1, &nread); 39013498266Sopenharmony_ci if(result == CURLE_AGAIN) 39113498266Sopenharmony_ci /* socket buffer drained, return */ 39213498266Sopenharmony_ci return CURLE_OK; 39313498266Sopenharmony_ci 39413498266Sopenharmony_ci if(Curl_pgrsUpdate(data)) 39513498266Sopenharmony_ci return CURLE_ABORTED_BY_CALLBACK; 39613498266Sopenharmony_ci 39713498266Sopenharmony_ci if(result) { 39813498266Sopenharmony_ci ts->keepon = KEEPON_DONE; 39913498266Sopenharmony_ci break; 40013498266Sopenharmony_ci } 40113498266Sopenharmony_ci 40213498266Sopenharmony_ci if(nread <= 0) { 40313498266Sopenharmony_ci if(data->set.proxyauth && data->state.authproxy.avail && 40413498266Sopenharmony_ci data->state.aptr.proxyuserpwd) { 40513498266Sopenharmony_ci /* proxy auth was requested and there was proxy auth available, 40613498266Sopenharmony_ci then deem this as "mere" proxy disconnect */ 40713498266Sopenharmony_ci ts->close_connection = TRUE; 40813498266Sopenharmony_ci infof(data, "Proxy CONNECT connection closed"); 40913498266Sopenharmony_ci } 41013498266Sopenharmony_ci else { 41113498266Sopenharmony_ci error = SELECT_ERROR; 41213498266Sopenharmony_ci failf(data, "Proxy CONNECT aborted"); 41313498266Sopenharmony_ci } 41413498266Sopenharmony_ci ts->keepon = KEEPON_DONE; 41513498266Sopenharmony_ci break; 41613498266Sopenharmony_ci } 41713498266Sopenharmony_ci 41813498266Sopenharmony_ci if(ts->keepon == KEEPON_IGNORE) { 41913498266Sopenharmony_ci /* This means we are currently ignoring a response-body */ 42013498266Sopenharmony_ci 42113498266Sopenharmony_ci if(ts->cl) { 42213498266Sopenharmony_ci /* A Content-Length based body: simply count down the counter 42313498266Sopenharmony_ci and make sure to break out of the loop when we're done! */ 42413498266Sopenharmony_ci ts->cl--; 42513498266Sopenharmony_ci if(ts->cl <= 0) { 42613498266Sopenharmony_ci ts->keepon = KEEPON_DONE; 42713498266Sopenharmony_ci break; 42813498266Sopenharmony_ci } 42913498266Sopenharmony_ci } 43013498266Sopenharmony_ci else if(ts->chunked_encoding) { 43113498266Sopenharmony_ci /* chunked-encoded body, so we need to do the chunked dance 43213498266Sopenharmony_ci properly to know when the end of the body is reached */ 43313498266Sopenharmony_ci size_t consumed = 0; 43413498266Sopenharmony_ci 43513498266Sopenharmony_ci /* now parse the chunked piece of data so that we can 43613498266Sopenharmony_ci properly tell when the stream ends */ 43713498266Sopenharmony_ci result = Curl_httpchunk_read(data, &ts->ch, &byte, 1, &consumed); 43813498266Sopenharmony_ci if(result) 43913498266Sopenharmony_ci return result; 44013498266Sopenharmony_ci if(Curl_httpchunk_is_done(data, &ts->ch)) { 44113498266Sopenharmony_ci /* we're done reading chunks! */ 44213498266Sopenharmony_ci infof(data, "chunk reading DONE"); 44313498266Sopenharmony_ci ts->keepon = KEEPON_DONE; 44413498266Sopenharmony_ci } 44513498266Sopenharmony_ci } 44613498266Sopenharmony_ci continue; 44713498266Sopenharmony_ci } 44813498266Sopenharmony_ci 44913498266Sopenharmony_ci if(Curl_dyn_addn(&ts->rcvbuf, &byte, 1)) { 45013498266Sopenharmony_ci failf(data, "CONNECT response too large"); 45113498266Sopenharmony_ci return CURLE_RECV_ERROR; 45213498266Sopenharmony_ci } 45313498266Sopenharmony_ci 45413498266Sopenharmony_ci /* if this is not the end of a header line then continue */ 45513498266Sopenharmony_ci if(byte != 0x0a) 45613498266Sopenharmony_ci continue; 45713498266Sopenharmony_ci 45813498266Sopenharmony_ci ts->headerlines++; 45913498266Sopenharmony_ci linep = Curl_dyn_ptr(&ts->rcvbuf); 46013498266Sopenharmony_ci line_len = Curl_dyn_len(&ts->rcvbuf); /* amount of bytes in this line */ 46113498266Sopenharmony_ci 46213498266Sopenharmony_ci /* output debug if that is requested */ 46313498266Sopenharmony_ci Curl_debug(data, CURLINFO_HEADER_IN, linep, line_len); 46413498266Sopenharmony_ci 46513498266Sopenharmony_ci /* send the header to the callback */ 46613498266Sopenharmony_ci writetype = CLIENTWRITE_HEADER | CLIENTWRITE_CONNECT | 46713498266Sopenharmony_ci (ts->headerlines == 1 ? CLIENTWRITE_STATUS : 0); 46813498266Sopenharmony_ci result = Curl_client_write(data, writetype, linep, line_len); 46913498266Sopenharmony_ci if(result) 47013498266Sopenharmony_ci return result; 47113498266Sopenharmony_ci 47213498266Sopenharmony_ci result = Curl_bump_headersize(data, line_len, TRUE); 47313498266Sopenharmony_ci if(result) 47413498266Sopenharmony_ci return result; 47513498266Sopenharmony_ci 47613498266Sopenharmony_ci /* Newlines are CRLF, so the CR is ignored as the line isn't 47713498266Sopenharmony_ci really terminated until the LF comes. Treat a following CR 47813498266Sopenharmony_ci as end-of-headers as well.*/ 47913498266Sopenharmony_ci 48013498266Sopenharmony_ci if(('\r' == linep[0]) || 48113498266Sopenharmony_ci ('\n' == linep[0])) { 48213498266Sopenharmony_ci /* end of response-headers from the proxy */ 48313498266Sopenharmony_ci 48413498266Sopenharmony_ci if((407 == k->httpcode) && !data->state.authproblem) { 48513498266Sopenharmony_ci /* If we get a 407 response code with content length 48613498266Sopenharmony_ci when we have no auth problem, we must ignore the 48713498266Sopenharmony_ci whole response-body */ 48813498266Sopenharmony_ci ts->keepon = KEEPON_IGNORE; 48913498266Sopenharmony_ci 49013498266Sopenharmony_ci if(ts->cl) { 49113498266Sopenharmony_ci infof(data, "Ignore %" CURL_FORMAT_CURL_OFF_T 49213498266Sopenharmony_ci " bytes of response-body", ts->cl); 49313498266Sopenharmony_ci } 49413498266Sopenharmony_ci else if(ts->chunked_encoding) { 49513498266Sopenharmony_ci infof(data, "Ignore chunked response-body"); 49613498266Sopenharmony_ci } 49713498266Sopenharmony_ci else { 49813498266Sopenharmony_ci /* without content-length or chunked encoding, we 49913498266Sopenharmony_ci can't keep the connection alive since the close is 50013498266Sopenharmony_ci the end signal so we bail out at once instead */ 50113498266Sopenharmony_ci CURL_TRC_CF(data, cf, "CONNECT: no content-length or chunked"); 50213498266Sopenharmony_ci ts->keepon = KEEPON_DONE; 50313498266Sopenharmony_ci } 50413498266Sopenharmony_ci } 50513498266Sopenharmony_ci else { 50613498266Sopenharmony_ci ts->keepon = KEEPON_DONE; 50713498266Sopenharmony_ci } 50813498266Sopenharmony_ci 50913498266Sopenharmony_ci DEBUGASSERT(ts->keepon == KEEPON_IGNORE 51013498266Sopenharmony_ci || ts->keepon == KEEPON_DONE); 51113498266Sopenharmony_ci continue; 51213498266Sopenharmony_ci } 51313498266Sopenharmony_ci 51413498266Sopenharmony_ci result = on_resp_header(cf, data, ts, linep); 51513498266Sopenharmony_ci if(result) 51613498266Sopenharmony_ci return result; 51713498266Sopenharmony_ci 51813498266Sopenharmony_ci Curl_dyn_reset(&ts->rcvbuf); 51913498266Sopenharmony_ci } /* while there's buffer left and loop is requested */ 52013498266Sopenharmony_ci 52113498266Sopenharmony_ci if(error) 52213498266Sopenharmony_ci result = CURLE_RECV_ERROR; 52313498266Sopenharmony_ci *done = (ts->keepon == KEEPON_DONE); 52413498266Sopenharmony_ci if(!result && *done && data->info.httpproxycode/100 != 2) { 52513498266Sopenharmony_ci /* Deal with the possibly already received authenticate 52613498266Sopenharmony_ci headers. 'newurl' is set to a new URL if we must loop. */ 52713498266Sopenharmony_ci result = Curl_http_auth_act(data); 52813498266Sopenharmony_ci } 52913498266Sopenharmony_ci return result; 53013498266Sopenharmony_ci} 53113498266Sopenharmony_ci 53213498266Sopenharmony_ci#else /* USE_HYPER */ 53313498266Sopenharmony_ci 53413498266Sopenharmony_cistatic CURLcode CONNECT_host(struct Curl_cfilter *cf, 53513498266Sopenharmony_ci struct Curl_easy *data, 53613498266Sopenharmony_ci char **pauthority, 53713498266Sopenharmony_ci char **phost_header) 53813498266Sopenharmony_ci{ 53913498266Sopenharmony_ci const char *hostname; 54013498266Sopenharmony_ci int port; 54113498266Sopenharmony_ci bool ipv6_ip; 54213498266Sopenharmony_ci CURLcode result; 54313498266Sopenharmony_ci char *authority; /* for CONNECT, the destination host + port */ 54413498266Sopenharmony_ci char *host_header = NULL; /* Host: authority */ 54513498266Sopenharmony_ci 54613498266Sopenharmony_ci result = Curl_http_proxy_get_destination(cf, &hostname, &port, &ipv6_ip); 54713498266Sopenharmony_ci if(result) 54813498266Sopenharmony_ci return result; 54913498266Sopenharmony_ci 55013498266Sopenharmony_ci authority = aprintf("%s%s%s:%d", ipv6_ip?"[":"", hostname, ipv6_ip?"]":"", 55113498266Sopenharmony_ci port); 55213498266Sopenharmony_ci if(!authority) 55313498266Sopenharmony_ci return CURLE_OUT_OF_MEMORY; 55413498266Sopenharmony_ci 55513498266Sopenharmony_ci /* If user is not overriding the Host header later */ 55613498266Sopenharmony_ci if(!Curl_checkProxyheaders(data, cf->conn, STRCONST("Host"))) { 55713498266Sopenharmony_ci host_header = aprintf("Host: %s\r\n", authority); 55813498266Sopenharmony_ci if(!host_header) { 55913498266Sopenharmony_ci free(authority); 56013498266Sopenharmony_ci return CURLE_OUT_OF_MEMORY; 56113498266Sopenharmony_ci } 56213498266Sopenharmony_ci } 56313498266Sopenharmony_ci *pauthority = authority; 56413498266Sopenharmony_ci *phost_header = host_header; 56513498266Sopenharmony_ci return CURLE_OK; 56613498266Sopenharmony_ci} 56713498266Sopenharmony_ci 56813498266Sopenharmony_ci/* The Hyper version of CONNECT */ 56913498266Sopenharmony_cistatic CURLcode start_CONNECT(struct Curl_cfilter *cf, 57013498266Sopenharmony_ci struct Curl_easy *data, 57113498266Sopenharmony_ci struct h1_tunnel_state *ts) 57213498266Sopenharmony_ci{ 57313498266Sopenharmony_ci struct connectdata *conn = cf->conn; 57413498266Sopenharmony_ci struct hyptransfer *h = &data->hyp; 57513498266Sopenharmony_ci curl_socket_t tunnelsocket = Curl_conn_cf_get_socket(cf, data); 57613498266Sopenharmony_ci hyper_io *io = NULL; 57713498266Sopenharmony_ci hyper_request *req = NULL; 57813498266Sopenharmony_ci hyper_headers *headers = NULL; 57913498266Sopenharmony_ci hyper_clientconn_options *options = NULL; 58013498266Sopenharmony_ci hyper_task *handshake = NULL; 58113498266Sopenharmony_ci hyper_task *task = NULL; /* for the handshake */ 58213498266Sopenharmony_ci hyper_clientconn *client = NULL; 58313498266Sopenharmony_ci hyper_task *sendtask = NULL; /* for the send */ 58413498266Sopenharmony_ci char *authority = NULL; /* for CONNECT */ 58513498266Sopenharmony_ci char *host_header = NULL; /* Host: */ 58613498266Sopenharmony_ci CURLcode result = CURLE_OUT_OF_MEMORY; 58713498266Sopenharmony_ci (void)ts; 58813498266Sopenharmony_ci 58913498266Sopenharmony_ci io = hyper_io_new(); 59013498266Sopenharmony_ci if(!io) { 59113498266Sopenharmony_ci failf(data, "Couldn't create hyper IO"); 59213498266Sopenharmony_ci result = CURLE_OUT_OF_MEMORY; 59313498266Sopenharmony_ci goto error; 59413498266Sopenharmony_ci } 59513498266Sopenharmony_ci /* tell Hyper how to read/write network data */ 59613498266Sopenharmony_ci hyper_io_set_userdata(io, data); 59713498266Sopenharmony_ci hyper_io_set_read(io, Curl_hyper_recv); 59813498266Sopenharmony_ci hyper_io_set_write(io, Curl_hyper_send); 59913498266Sopenharmony_ci conn->sockfd = tunnelsocket; 60013498266Sopenharmony_ci 60113498266Sopenharmony_ci data->state.hconnect = TRUE; 60213498266Sopenharmony_ci 60313498266Sopenharmony_ci /* create an executor to poll futures */ 60413498266Sopenharmony_ci if(!h->exec) { 60513498266Sopenharmony_ci h->exec = hyper_executor_new(); 60613498266Sopenharmony_ci if(!h->exec) { 60713498266Sopenharmony_ci failf(data, "Couldn't create hyper executor"); 60813498266Sopenharmony_ci result = CURLE_OUT_OF_MEMORY; 60913498266Sopenharmony_ci goto error; 61013498266Sopenharmony_ci } 61113498266Sopenharmony_ci } 61213498266Sopenharmony_ci 61313498266Sopenharmony_ci options = hyper_clientconn_options_new(); 61413498266Sopenharmony_ci if(!options) { 61513498266Sopenharmony_ci failf(data, "Couldn't create hyper client options"); 61613498266Sopenharmony_ci result = CURLE_OUT_OF_MEMORY; 61713498266Sopenharmony_ci goto error; 61813498266Sopenharmony_ci } 61913498266Sopenharmony_ci hyper_clientconn_options_set_preserve_header_case(options, 1); 62013498266Sopenharmony_ci hyper_clientconn_options_set_preserve_header_order(options, 1); 62113498266Sopenharmony_ci 62213498266Sopenharmony_ci hyper_clientconn_options_exec(options, h->exec); 62313498266Sopenharmony_ci 62413498266Sopenharmony_ci /* "Both the `io` and the `options` are consumed in this function 62513498266Sopenharmony_ci call" */ 62613498266Sopenharmony_ci handshake = hyper_clientconn_handshake(io, options); 62713498266Sopenharmony_ci if(!handshake) { 62813498266Sopenharmony_ci failf(data, "Couldn't create hyper client handshake"); 62913498266Sopenharmony_ci result = CURLE_OUT_OF_MEMORY; 63013498266Sopenharmony_ci goto error; 63113498266Sopenharmony_ci } 63213498266Sopenharmony_ci io = NULL; 63313498266Sopenharmony_ci options = NULL; 63413498266Sopenharmony_ci 63513498266Sopenharmony_ci if(HYPERE_OK != hyper_executor_push(h->exec, handshake)) { 63613498266Sopenharmony_ci failf(data, "Couldn't hyper_executor_push the handshake"); 63713498266Sopenharmony_ci result = CURLE_OUT_OF_MEMORY; 63813498266Sopenharmony_ci goto error; 63913498266Sopenharmony_ci } 64013498266Sopenharmony_ci handshake = NULL; /* ownership passed on */ 64113498266Sopenharmony_ci 64213498266Sopenharmony_ci task = hyper_executor_poll(h->exec); 64313498266Sopenharmony_ci if(!task) { 64413498266Sopenharmony_ci failf(data, "Couldn't hyper_executor_poll the handshake"); 64513498266Sopenharmony_ci result = CURLE_OUT_OF_MEMORY; 64613498266Sopenharmony_ci goto error; 64713498266Sopenharmony_ci } 64813498266Sopenharmony_ci 64913498266Sopenharmony_ci client = hyper_task_value(task); 65013498266Sopenharmony_ci hyper_task_free(task); 65113498266Sopenharmony_ci 65213498266Sopenharmony_ci req = hyper_request_new(); 65313498266Sopenharmony_ci if(!req) { 65413498266Sopenharmony_ci failf(data, "Couldn't hyper_request_new"); 65513498266Sopenharmony_ci result = CURLE_OUT_OF_MEMORY; 65613498266Sopenharmony_ci goto error; 65713498266Sopenharmony_ci } 65813498266Sopenharmony_ci if(hyper_request_set_method(req, (uint8_t *)"CONNECT", 65913498266Sopenharmony_ci strlen("CONNECT"))) { 66013498266Sopenharmony_ci failf(data, "error setting method"); 66113498266Sopenharmony_ci result = CURLE_OUT_OF_MEMORY; 66213498266Sopenharmony_ci goto error; 66313498266Sopenharmony_ci } 66413498266Sopenharmony_ci 66513498266Sopenharmony_ci /* This only happens if we've looped here due to authentication 66613498266Sopenharmony_ci reasons, and we don't really use the newly cloned URL here 66713498266Sopenharmony_ci then. Just free() it. */ 66813498266Sopenharmony_ci Curl_safefree(data->req.newurl); 66913498266Sopenharmony_ci 67013498266Sopenharmony_ci result = CONNECT_host(cf, data, &authority, &host_header); 67113498266Sopenharmony_ci if(result) 67213498266Sopenharmony_ci goto error; 67313498266Sopenharmony_ci 67413498266Sopenharmony_ci infof(data, "Establish HTTP proxy tunnel to %s", authority); 67513498266Sopenharmony_ci 67613498266Sopenharmony_ci if(hyper_request_set_uri(req, (uint8_t *)authority, 67713498266Sopenharmony_ci strlen(authority))) { 67813498266Sopenharmony_ci failf(data, "error setting path"); 67913498266Sopenharmony_ci result = CURLE_OUT_OF_MEMORY; 68013498266Sopenharmony_ci goto error; 68113498266Sopenharmony_ci } 68213498266Sopenharmony_ci if(data->set.verbose) { 68313498266Sopenharmony_ci char *se = aprintf("CONNECT %s HTTP/1.1\r\n", authority); 68413498266Sopenharmony_ci if(!se) { 68513498266Sopenharmony_ci result = CURLE_OUT_OF_MEMORY; 68613498266Sopenharmony_ci goto error; 68713498266Sopenharmony_ci } 68813498266Sopenharmony_ci Curl_debug(data, CURLINFO_HEADER_OUT, se, strlen(se)); 68913498266Sopenharmony_ci free(se); 69013498266Sopenharmony_ci } 69113498266Sopenharmony_ci /* Setup the proxy-authorization header, if any */ 69213498266Sopenharmony_ci result = Curl_http_output_auth(data, conn, "CONNECT", HTTPREQ_GET, 69313498266Sopenharmony_ci authority, TRUE); 69413498266Sopenharmony_ci if(result) 69513498266Sopenharmony_ci goto error; 69613498266Sopenharmony_ci Curl_safefree(authority); 69713498266Sopenharmony_ci 69813498266Sopenharmony_ci /* default is 1.1 */ 69913498266Sopenharmony_ci if((conn->http_proxy.proxytype == CURLPROXY_HTTP_1_0) && 70013498266Sopenharmony_ci (HYPERE_OK != hyper_request_set_version(req, 70113498266Sopenharmony_ci HYPER_HTTP_VERSION_1_0))) { 70213498266Sopenharmony_ci failf(data, "error setting HTTP version"); 70313498266Sopenharmony_ci result = CURLE_OUT_OF_MEMORY; 70413498266Sopenharmony_ci goto error; 70513498266Sopenharmony_ci } 70613498266Sopenharmony_ci 70713498266Sopenharmony_ci headers = hyper_request_headers(req); 70813498266Sopenharmony_ci if(!headers) { 70913498266Sopenharmony_ci failf(data, "hyper_request_headers"); 71013498266Sopenharmony_ci result = CURLE_OUT_OF_MEMORY; 71113498266Sopenharmony_ci goto error; 71213498266Sopenharmony_ci } 71313498266Sopenharmony_ci if(host_header) { 71413498266Sopenharmony_ci result = Curl_hyper_header(data, headers, host_header); 71513498266Sopenharmony_ci if(result) 71613498266Sopenharmony_ci goto error; 71713498266Sopenharmony_ci Curl_safefree(host_header); 71813498266Sopenharmony_ci } 71913498266Sopenharmony_ci 72013498266Sopenharmony_ci if(data->state.aptr.proxyuserpwd) { 72113498266Sopenharmony_ci result = Curl_hyper_header(data, headers, 72213498266Sopenharmony_ci data->state.aptr.proxyuserpwd); 72313498266Sopenharmony_ci if(result) 72413498266Sopenharmony_ci goto error; 72513498266Sopenharmony_ci } 72613498266Sopenharmony_ci 72713498266Sopenharmony_ci if(!Curl_checkProxyheaders(data, conn, STRCONST("User-Agent")) && 72813498266Sopenharmony_ci data->set.str[STRING_USERAGENT] && *data->set.str[STRING_USERAGENT]) { 72913498266Sopenharmony_ci struct dynbuf ua; 73013498266Sopenharmony_ci Curl_dyn_init(&ua, DYN_HTTP_REQUEST); 73113498266Sopenharmony_ci result = Curl_dyn_addf(&ua, "User-Agent: %s\r\n", 73213498266Sopenharmony_ci data->set.str[STRING_USERAGENT]); 73313498266Sopenharmony_ci if(result) 73413498266Sopenharmony_ci goto error; 73513498266Sopenharmony_ci result = Curl_hyper_header(data, headers, Curl_dyn_ptr(&ua)); 73613498266Sopenharmony_ci if(result) 73713498266Sopenharmony_ci goto error; 73813498266Sopenharmony_ci Curl_dyn_free(&ua); 73913498266Sopenharmony_ci } 74013498266Sopenharmony_ci 74113498266Sopenharmony_ci if(!Curl_checkProxyheaders(data, conn, STRCONST("Proxy-Connection"))) { 74213498266Sopenharmony_ci result = Curl_hyper_header(data, headers, 74313498266Sopenharmony_ci "Proxy-Connection: Keep-Alive"); 74413498266Sopenharmony_ci if(result) 74513498266Sopenharmony_ci goto error; 74613498266Sopenharmony_ci } 74713498266Sopenharmony_ci 74813498266Sopenharmony_ci result = Curl_add_custom_headers(data, TRUE, headers); 74913498266Sopenharmony_ci if(result) 75013498266Sopenharmony_ci goto error; 75113498266Sopenharmony_ci 75213498266Sopenharmony_ci sendtask = hyper_clientconn_send(client, req); 75313498266Sopenharmony_ci if(!sendtask) { 75413498266Sopenharmony_ci failf(data, "hyper_clientconn_send"); 75513498266Sopenharmony_ci result = CURLE_OUT_OF_MEMORY; 75613498266Sopenharmony_ci goto error; 75713498266Sopenharmony_ci } 75813498266Sopenharmony_ci req = NULL; 75913498266Sopenharmony_ci 76013498266Sopenharmony_ci if(HYPERE_OK != hyper_executor_push(h->exec, sendtask)) { 76113498266Sopenharmony_ci failf(data, "Couldn't hyper_executor_push the send"); 76213498266Sopenharmony_ci result = CURLE_OUT_OF_MEMORY; 76313498266Sopenharmony_ci goto error; 76413498266Sopenharmony_ci } 76513498266Sopenharmony_ci sendtask = NULL; /* ownership passed on */ 76613498266Sopenharmony_ci 76713498266Sopenharmony_ci hyper_clientconn_free(client); 76813498266Sopenharmony_ci client = NULL; 76913498266Sopenharmony_ci 77013498266Sopenharmony_cierror: 77113498266Sopenharmony_ci free(host_header); 77213498266Sopenharmony_ci free(authority); 77313498266Sopenharmony_ci if(io) 77413498266Sopenharmony_ci hyper_io_free(io); 77513498266Sopenharmony_ci if(options) 77613498266Sopenharmony_ci hyper_clientconn_options_free(options); 77713498266Sopenharmony_ci if(handshake) 77813498266Sopenharmony_ci hyper_task_free(handshake); 77913498266Sopenharmony_ci if(client) 78013498266Sopenharmony_ci hyper_clientconn_free(client); 78113498266Sopenharmony_ci if(req) 78213498266Sopenharmony_ci hyper_request_free(req); 78313498266Sopenharmony_ci 78413498266Sopenharmony_ci return result; 78513498266Sopenharmony_ci} 78613498266Sopenharmony_ci 78713498266Sopenharmony_cistatic CURLcode send_CONNECT(struct Curl_cfilter *cf, 78813498266Sopenharmony_ci struct Curl_easy *data, 78913498266Sopenharmony_ci struct h1_tunnel_state *ts, 79013498266Sopenharmony_ci bool *done) 79113498266Sopenharmony_ci{ 79213498266Sopenharmony_ci struct hyptransfer *h = &data->hyp; 79313498266Sopenharmony_ci struct connectdata *conn = cf->conn; 79413498266Sopenharmony_ci hyper_task *task = NULL; 79513498266Sopenharmony_ci hyper_error *hypererr = NULL; 79613498266Sopenharmony_ci CURLcode result = CURLE_OK; 79713498266Sopenharmony_ci 79813498266Sopenharmony_ci (void)ts; 79913498266Sopenharmony_ci (void)conn; 80013498266Sopenharmony_ci do { 80113498266Sopenharmony_ci task = hyper_executor_poll(h->exec); 80213498266Sopenharmony_ci if(task) { 80313498266Sopenharmony_ci bool error = hyper_task_type(task) == HYPER_TASK_ERROR; 80413498266Sopenharmony_ci if(error) 80513498266Sopenharmony_ci hypererr = hyper_task_value(task); 80613498266Sopenharmony_ci hyper_task_free(task); 80713498266Sopenharmony_ci if(error) { 80813498266Sopenharmony_ci /* this could probably use a better error code? */ 80913498266Sopenharmony_ci result = CURLE_OUT_OF_MEMORY; 81013498266Sopenharmony_ci goto error; 81113498266Sopenharmony_ci } 81213498266Sopenharmony_ci } 81313498266Sopenharmony_ci } while(task); 81413498266Sopenharmony_cierror: 81513498266Sopenharmony_ci *done = (result == CURLE_OK); 81613498266Sopenharmony_ci if(hypererr) { 81713498266Sopenharmony_ci uint8_t errbuf[256]; 81813498266Sopenharmony_ci size_t errlen = hyper_error_print(hypererr, errbuf, sizeof(errbuf)); 81913498266Sopenharmony_ci failf(data, "Hyper: %.*s", (int)errlen, errbuf); 82013498266Sopenharmony_ci hyper_error_free(hypererr); 82113498266Sopenharmony_ci } 82213498266Sopenharmony_ci return result; 82313498266Sopenharmony_ci} 82413498266Sopenharmony_ci 82513498266Sopenharmony_cistatic CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf, 82613498266Sopenharmony_ci struct Curl_easy *data, 82713498266Sopenharmony_ci struct h1_tunnel_state *ts, 82813498266Sopenharmony_ci bool *done) 82913498266Sopenharmony_ci{ 83013498266Sopenharmony_ci struct hyptransfer *h = &data->hyp; 83113498266Sopenharmony_ci CURLcode result; 83213498266Sopenharmony_ci int didwhat; 83313498266Sopenharmony_ci 83413498266Sopenharmony_ci (void)ts; 83513498266Sopenharmony_ci *done = FALSE; 83613498266Sopenharmony_ci result = Curl_hyper_stream(data, cf->conn, &didwhat, done, 83713498266Sopenharmony_ci CURL_CSELECT_IN | CURL_CSELECT_OUT); 83813498266Sopenharmony_ci if(result || !*done) 83913498266Sopenharmony_ci return result; 84013498266Sopenharmony_ci if(h->exec) { 84113498266Sopenharmony_ci hyper_executor_free(h->exec); 84213498266Sopenharmony_ci h->exec = NULL; 84313498266Sopenharmony_ci } 84413498266Sopenharmony_ci if(h->read_waker) { 84513498266Sopenharmony_ci hyper_waker_free(h->read_waker); 84613498266Sopenharmony_ci h->read_waker = NULL; 84713498266Sopenharmony_ci } 84813498266Sopenharmony_ci if(h->write_waker) { 84913498266Sopenharmony_ci hyper_waker_free(h->write_waker); 85013498266Sopenharmony_ci h->write_waker = NULL; 85113498266Sopenharmony_ci } 85213498266Sopenharmony_ci return result; 85313498266Sopenharmony_ci} 85413498266Sopenharmony_ci 85513498266Sopenharmony_ci#endif /* USE_HYPER */ 85613498266Sopenharmony_ci 85713498266Sopenharmony_cistatic CURLcode H1_CONNECT(struct Curl_cfilter *cf, 85813498266Sopenharmony_ci struct Curl_easy *data, 85913498266Sopenharmony_ci struct h1_tunnel_state *ts) 86013498266Sopenharmony_ci{ 86113498266Sopenharmony_ci struct connectdata *conn = cf->conn; 86213498266Sopenharmony_ci CURLcode result; 86313498266Sopenharmony_ci bool done; 86413498266Sopenharmony_ci 86513498266Sopenharmony_ci if(tunnel_is_established(ts)) 86613498266Sopenharmony_ci return CURLE_OK; 86713498266Sopenharmony_ci if(tunnel_is_failed(ts)) 86813498266Sopenharmony_ci return CURLE_RECV_ERROR; /* Need a cfilter close and new bootstrap */ 86913498266Sopenharmony_ci 87013498266Sopenharmony_ci do { 87113498266Sopenharmony_ci timediff_t check; 87213498266Sopenharmony_ci 87313498266Sopenharmony_ci check = Curl_timeleft(data, NULL, TRUE); 87413498266Sopenharmony_ci if(check <= 0) { 87513498266Sopenharmony_ci failf(data, "Proxy CONNECT aborted due to timeout"); 87613498266Sopenharmony_ci result = CURLE_OPERATION_TIMEDOUT; 87713498266Sopenharmony_ci goto out; 87813498266Sopenharmony_ci } 87913498266Sopenharmony_ci 88013498266Sopenharmony_ci switch(ts->tunnel_state) { 88113498266Sopenharmony_ci case H1_TUNNEL_INIT: 88213498266Sopenharmony_ci /* Prepare the CONNECT request and make a first attempt to send. */ 88313498266Sopenharmony_ci CURL_TRC_CF(data, cf, "CONNECT start"); 88413498266Sopenharmony_ci result = start_CONNECT(cf, data, ts); 88513498266Sopenharmony_ci if(result) 88613498266Sopenharmony_ci goto out; 88713498266Sopenharmony_ci h1_tunnel_go_state(cf, ts, H1_TUNNEL_CONNECT, data); 88813498266Sopenharmony_ci FALLTHROUGH(); 88913498266Sopenharmony_ci 89013498266Sopenharmony_ci case H1_TUNNEL_CONNECT: 89113498266Sopenharmony_ci /* see that the request is completely sent */ 89213498266Sopenharmony_ci CURL_TRC_CF(data, cf, "CONNECT send"); 89313498266Sopenharmony_ci result = send_CONNECT(cf, data, ts, &done); 89413498266Sopenharmony_ci if(result || !done) 89513498266Sopenharmony_ci goto out; 89613498266Sopenharmony_ci h1_tunnel_go_state(cf, ts, H1_TUNNEL_RECEIVE, data); 89713498266Sopenharmony_ci FALLTHROUGH(); 89813498266Sopenharmony_ci 89913498266Sopenharmony_ci case H1_TUNNEL_RECEIVE: 90013498266Sopenharmony_ci /* read what is there */ 90113498266Sopenharmony_ci CURL_TRC_CF(data, cf, "CONNECT receive"); 90213498266Sopenharmony_ci result = recv_CONNECT_resp(cf, data, ts, &done); 90313498266Sopenharmony_ci if(Curl_pgrsUpdate(data)) { 90413498266Sopenharmony_ci result = CURLE_ABORTED_BY_CALLBACK; 90513498266Sopenharmony_ci goto out; 90613498266Sopenharmony_ci } 90713498266Sopenharmony_ci /* error or not complete yet. return for more multi-multi */ 90813498266Sopenharmony_ci if(result || !done) 90913498266Sopenharmony_ci goto out; 91013498266Sopenharmony_ci /* got it */ 91113498266Sopenharmony_ci h1_tunnel_go_state(cf, ts, H1_TUNNEL_RESPONSE, data); 91213498266Sopenharmony_ci FALLTHROUGH(); 91313498266Sopenharmony_ci 91413498266Sopenharmony_ci case H1_TUNNEL_RESPONSE: 91513498266Sopenharmony_ci CURL_TRC_CF(data, cf, "CONNECT response"); 91613498266Sopenharmony_ci if(data->req.newurl) { 91713498266Sopenharmony_ci /* not the "final" response, we need to do a follow up request. 91813498266Sopenharmony_ci * If the other side indicated a connection close, or if someone 91913498266Sopenharmony_ci * else told us to close this connection, do so now. 92013498266Sopenharmony_ci */ 92113498266Sopenharmony_ci if(ts->close_connection || conn->bits.close) { 92213498266Sopenharmony_ci /* Close this filter and the sub-chain, re-connect the 92313498266Sopenharmony_ci * sub-chain and continue. Closing this filter will 92413498266Sopenharmony_ci * reset our tunnel state. To avoid recursion, we return 92513498266Sopenharmony_ci * and expect to be called again. 92613498266Sopenharmony_ci */ 92713498266Sopenharmony_ci CURL_TRC_CF(data, cf, "CONNECT need to close+open"); 92813498266Sopenharmony_ci infof(data, "Connect me again please"); 92913498266Sopenharmony_ci Curl_conn_cf_close(cf, data); 93013498266Sopenharmony_ci connkeep(conn, "HTTP proxy CONNECT"); 93113498266Sopenharmony_ci result = Curl_conn_cf_connect(cf->next, data, FALSE, &done); 93213498266Sopenharmony_ci goto out; 93313498266Sopenharmony_ci } 93413498266Sopenharmony_ci else { 93513498266Sopenharmony_ci /* staying on this connection, reset state */ 93613498266Sopenharmony_ci h1_tunnel_go_state(cf, ts, H1_TUNNEL_INIT, data); 93713498266Sopenharmony_ci } 93813498266Sopenharmony_ci } 93913498266Sopenharmony_ci break; 94013498266Sopenharmony_ci 94113498266Sopenharmony_ci default: 94213498266Sopenharmony_ci break; 94313498266Sopenharmony_ci } 94413498266Sopenharmony_ci 94513498266Sopenharmony_ci } while(data->req.newurl); 94613498266Sopenharmony_ci 94713498266Sopenharmony_ci DEBUGASSERT(ts->tunnel_state == H1_TUNNEL_RESPONSE); 94813498266Sopenharmony_ci if(data->info.httpproxycode/100 != 2) { 94913498266Sopenharmony_ci /* a non-2xx response and we have no next url to try. */ 95013498266Sopenharmony_ci Curl_safefree(data->req.newurl); 95113498266Sopenharmony_ci /* failure, close this connection to avoid reuse */ 95213498266Sopenharmony_ci streamclose(conn, "proxy CONNECT failure"); 95313498266Sopenharmony_ci h1_tunnel_go_state(cf, ts, H1_TUNNEL_FAILED, data); 95413498266Sopenharmony_ci failf(data, "CONNECT tunnel failed, response %d", data->req.httpcode); 95513498266Sopenharmony_ci return CURLE_RECV_ERROR; 95613498266Sopenharmony_ci } 95713498266Sopenharmony_ci /* 2xx response, SUCCESS! */ 95813498266Sopenharmony_ci h1_tunnel_go_state(cf, ts, H1_TUNNEL_ESTABLISHED, data); 95913498266Sopenharmony_ci infof(data, "CONNECT tunnel established, response %d", 96013498266Sopenharmony_ci data->info.httpproxycode); 96113498266Sopenharmony_ci result = CURLE_OK; 96213498266Sopenharmony_ci 96313498266Sopenharmony_ciout: 96413498266Sopenharmony_ci if(result) 96513498266Sopenharmony_ci h1_tunnel_go_state(cf, ts, H1_TUNNEL_FAILED, data); 96613498266Sopenharmony_ci return result; 96713498266Sopenharmony_ci} 96813498266Sopenharmony_ci 96913498266Sopenharmony_cistatic CURLcode cf_h1_proxy_connect(struct Curl_cfilter *cf, 97013498266Sopenharmony_ci struct Curl_easy *data, 97113498266Sopenharmony_ci bool blocking, bool *done) 97213498266Sopenharmony_ci{ 97313498266Sopenharmony_ci CURLcode result; 97413498266Sopenharmony_ci struct h1_tunnel_state *ts = cf->ctx; 97513498266Sopenharmony_ci 97613498266Sopenharmony_ci if(cf->connected) { 97713498266Sopenharmony_ci *done = TRUE; 97813498266Sopenharmony_ci return CURLE_OK; 97913498266Sopenharmony_ci } 98013498266Sopenharmony_ci 98113498266Sopenharmony_ci CURL_TRC_CF(data, cf, "connect"); 98213498266Sopenharmony_ci result = cf->next->cft->do_connect(cf->next, data, blocking, done); 98313498266Sopenharmony_ci if(result || !*done) 98413498266Sopenharmony_ci return result; 98513498266Sopenharmony_ci 98613498266Sopenharmony_ci *done = FALSE; 98713498266Sopenharmony_ci if(!ts) { 98813498266Sopenharmony_ci result = tunnel_init(cf, data, &ts); 98913498266Sopenharmony_ci if(result) 99013498266Sopenharmony_ci return result; 99113498266Sopenharmony_ci cf->ctx = ts; 99213498266Sopenharmony_ci } 99313498266Sopenharmony_ci 99413498266Sopenharmony_ci /* TODO: can we do blocking? */ 99513498266Sopenharmony_ci /* We want "seamless" operations through HTTP proxy tunnel */ 99613498266Sopenharmony_ci 99713498266Sopenharmony_ci result = H1_CONNECT(cf, data, ts); 99813498266Sopenharmony_ci if(result) 99913498266Sopenharmony_ci goto out; 100013498266Sopenharmony_ci Curl_safefree(data->state.aptr.proxyuserpwd); 100113498266Sopenharmony_ci 100213498266Sopenharmony_ciout: 100313498266Sopenharmony_ci *done = (result == CURLE_OK) && tunnel_is_established(cf->ctx); 100413498266Sopenharmony_ci if(*done) { 100513498266Sopenharmony_ci cf->connected = TRUE; 100613498266Sopenharmony_ci /* Restore `data->req` fields that may habe been touched */ 100713498266Sopenharmony_ci data->req.header = TRUE; /* assume header */ 100813498266Sopenharmony_ci data->req.bytecount = 0; 100913498266Sopenharmony_ci data->req.ignorebody = FALSE; 101013498266Sopenharmony_ci Curl_client_cleanup(data); 101113498266Sopenharmony_ci Curl_pgrsSetUploadCounter(data, 0); 101213498266Sopenharmony_ci Curl_pgrsSetDownloadCounter(data, 0); 101313498266Sopenharmony_ci 101413498266Sopenharmony_ci tunnel_free(cf, data); 101513498266Sopenharmony_ci } 101613498266Sopenharmony_ci return result; 101713498266Sopenharmony_ci} 101813498266Sopenharmony_ci 101913498266Sopenharmony_cistatic void cf_h1_proxy_adjust_pollset(struct Curl_cfilter *cf, 102013498266Sopenharmony_ci struct Curl_easy *data, 102113498266Sopenharmony_ci struct easy_pollset *ps) 102213498266Sopenharmony_ci{ 102313498266Sopenharmony_ci struct h1_tunnel_state *ts = cf->ctx; 102413498266Sopenharmony_ci 102513498266Sopenharmony_ci if(!cf->connected) { 102613498266Sopenharmony_ci /* If we are not connected, but the filter "below" is 102713498266Sopenharmony_ci * and not waiting on something, we are tunneling. */ 102813498266Sopenharmony_ci curl_socket_t sock = Curl_conn_cf_get_socket(cf, data); 102913498266Sopenharmony_ci if(ts) { 103013498266Sopenharmony_ci /* when we've sent a CONNECT to a proxy, we should rather either 103113498266Sopenharmony_ci wait for the socket to become readable to be able to get the 103213498266Sopenharmony_ci response headers or if we're still sending the request, wait 103313498266Sopenharmony_ci for write. */ 103413498266Sopenharmony_ci if(ts->CONNECT.sending == HTTPSEND_REQUEST) 103513498266Sopenharmony_ci Curl_pollset_set_out_only(data, ps, sock); 103613498266Sopenharmony_ci else 103713498266Sopenharmony_ci Curl_pollset_set_in_only(data, ps, sock); 103813498266Sopenharmony_ci } 103913498266Sopenharmony_ci else 104013498266Sopenharmony_ci Curl_pollset_set_out_only(data, ps, sock); 104113498266Sopenharmony_ci } 104213498266Sopenharmony_ci} 104313498266Sopenharmony_ci 104413498266Sopenharmony_cistatic void cf_h1_proxy_destroy(struct Curl_cfilter *cf, 104513498266Sopenharmony_ci struct Curl_easy *data) 104613498266Sopenharmony_ci{ 104713498266Sopenharmony_ci CURL_TRC_CF(data, cf, "destroy"); 104813498266Sopenharmony_ci tunnel_free(cf, data); 104913498266Sopenharmony_ci} 105013498266Sopenharmony_ci 105113498266Sopenharmony_cistatic void cf_h1_proxy_close(struct Curl_cfilter *cf, 105213498266Sopenharmony_ci struct Curl_easy *data) 105313498266Sopenharmony_ci{ 105413498266Sopenharmony_ci CURL_TRC_CF(data, cf, "close"); 105513498266Sopenharmony_ci cf->connected = FALSE; 105613498266Sopenharmony_ci if(cf->ctx) { 105713498266Sopenharmony_ci h1_tunnel_go_state(cf, cf->ctx, H1_TUNNEL_INIT, data); 105813498266Sopenharmony_ci } 105913498266Sopenharmony_ci if(cf->next) 106013498266Sopenharmony_ci cf->next->cft->do_close(cf->next, data); 106113498266Sopenharmony_ci} 106213498266Sopenharmony_ci 106313498266Sopenharmony_ci 106413498266Sopenharmony_cistruct Curl_cftype Curl_cft_h1_proxy = { 106513498266Sopenharmony_ci "H1-PROXY", 106613498266Sopenharmony_ci CF_TYPE_IP_CONNECT, 106713498266Sopenharmony_ci 0, 106813498266Sopenharmony_ci cf_h1_proxy_destroy, 106913498266Sopenharmony_ci cf_h1_proxy_connect, 107013498266Sopenharmony_ci cf_h1_proxy_close, 107113498266Sopenharmony_ci Curl_cf_http_proxy_get_host, 107213498266Sopenharmony_ci cf_h1_proxy_adjust_pollset, 107313498266Sopenharmony_ci Curl_cf_def_data_pending, 107413498266Sopenharmony_ci Curl_cf_def_send, 107513498266Sopenharmony_ci Curl_cf_def_recv, 107613498266Sopenharmony_ci Curl_cf_def_cntrl, 107713498266Sopenharmony_ci Curl_cf_def_conn_is_alive, 107813498266Sopenharmony_ci Curl_cf_def_conn_keep_alive, 107913498266Sopenharmony_ci Curl_cf_def_query, 108013498266Sopenharmony_ci}; 108113498266Sopenharmony_ci 108213498266Sopenharmony_ciCURLcode Curl_cf_h1_proxy_insert_after(struct Curl_cfilter *cf_at, 108313498266Sopenharmony_ci struct Curl_easy *data) 108413498266Sopenharmony_ci{ 108513498266Sopenharmony_ci struct Curl_cfilter *cf; 108613498266Sopenharmony_ci CURLcode result; 108713498266Sopenharmony_ci 108813498266Sopenharmony_ci (void)data; 108913498266Sopenharmony_ci result = Curl_cf_create(&cf, &Curl_cft_h1_proxy, NULL); 109013498266Sopenharmony_ci if(!result) 109113498266Sopenharmony_ci Curl_conn_cf_insert_after(cf_at, cf); 109213498266Sopenharmony_ci return result; 109313498266Sopenharmony_ci} 109413498266Sopenharmony_ci 109513498266Sopenharmony_ci#endif /* !CURL_DISABLE_PROXY && ! CURL_DISABLE_HTTP */ 1096