1/*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. 9 * 10 * This software is licensed as described in the file COPYING, which 11 * you should have received as part of this distribution. The terms 12 * are also available at https://curl.se/docs/copyright.html. 13 * 14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell 15 * copies of the Software, and permit persons to whom the Software is 16 * furnished to do so, under the terms of the COPYING file. 17 * 18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 19 * KIND, either express or implied. 20 * 21 * SPDX-License-Identifier: curl 22 * 23 ***************************************************************************/ 24 25#include "curl_setup.h" 26 27#if !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP) 28 29#include <curl/curl.h> 30#ifdef USE_HYPER 31#include <hyper.h> 32#endif 33#include "urldata.h" 34#include "dynbuf.h" 35#include "sendf.h" 36#include "http.h" 37#include "http1.h" 38#include "http_proxy.h" 39#include "url.h" 40#include "select.h" 41#include "progress.h" 42#include "cfilters.h" 43#include "cf-h1-proxy.h" 44#include "connect.h" 45#include "curl_trc.h" 46#include "curlx.h" 47#include "vtls/vtls.h" 48#include "transfer.h" 49#include "multiif.h" 50 51/* The last 3 #include files should be in this order */ 52#include "curl_printf.h" 53#include "curl_memory.h" 54#include "memdebug.h" 55 56 57typedef enum { 58 H1_TUNNEL_INIT, /* init/default/no tunnel state */ 59 H1_TUNNEL_CONNECT, /* CONNECT request is being send */ 60 H1_TUNNEL_RECEIVE, /* CONNECT answer is being received */ 61 H1_TUNNEL_RESPONSE, /* CONNECT response received completely */ 62 H1_TUNNEL_ESTABLISHED, 63 H1_TUNNEL_FAILED 64} h1_tunnel_state; 65 66/* struct for HTTP CONNECT tunneling */ 67struct h1_tunnel_state { 68 struct HTTP CONNECT; 69 struct dynbuf rcvbuf; 70 struct dynbuf request_data; 71 size_t nsent; 72 size_t headerlines; 73 struct Curl_chunker ch; 74 enum keeponval { 75 KEEPON_DONE, 76 KEEPON_CONNECT, 77 KEEPON_IGNORE 78 } keepon; 79 curl_off_t cl; /* size of content to read and ignore */ 80 h1_tunnel_state tunnel_state; 81 BIT(chunked_encoding); 82 BIT(close_connection); 83}; 84 85 86static bool tunnel_is_established(struct h1_tunnel_state *ts) 87{ 88 return ts && (ts->tunnel_state == H1_TUNNEL_ESTABLISHED); 89} 90 91static bool tunnel_is_failed(struct h1_tunnel_state *ts) 92{ 93 return ts && (ts->tunnel_state == H1_TUNNEL_FAILED); 94} 95 96static CURLcode tunnel_reinit(struct Curl_cfilter *cf, 97 struct Curl_easy *data, 98 struct h1_tunnel_state *ts) 99{ 100 (void)data; 101 (void)cf; 102 DEBUGASSERT(ts); 103 Curl_dyn_reset(&ts->rcvbuf); 104 Curl_dyn_reset(&ts->request_data); 105 ts->tunnel_state = H1_TUNNEL_INIT; 106 ts->keepon = KEEPON_CONNECT; 107 ts->cl = 0; 108 ts->close_connection = FALSE; 109 return CURLE_OK; 110} 111 112static CURLcode tunnel_init(struct Curl_cfilter *cf, 113 struct Curl_easy *data, 114 struct h1_tunnel_state **pts) 115{ 116 struct h1_tunnel_state *ts; 117 CURLcode result; 118 119 if(cf->conn->handler->flags & PROTOPT_NOTCPPROXY) { 120 failf(data, "%s cannot be done over CONNECT", cf->conn->handler->scheme); 121 return CURLE_UNSUPPORTED_PROTOCOL; 122 } 123 124 /* we might need the upload buffer for streaming a partial request */ 125 result = Curl_get_upload_buffer(data); 126 if(result) 127 return result; 128 129 ts = calloc(1, sizeof(*ts)); 130 if(!ts) 131 return CURLE_OUT_OF_MEMORY; 132 133 infof(data, "allocate connect buffer"); 134 135 Curl_dyn_init(&ts->rcvbuf, DYN_PROXY_CONNECT_HEADERS); 136 Curl_dyn_init(&ts->request_data, DYN_HTTP_REQUEST); 137 Curl_httpchunk_init(data, &ts->ch, TRUE); 138 139 *pts = ts; 140 connkeep(cf->conn, "HTTP proxy CONNECT"); 141 return tunnel_reinit(cf, data, ts); 142} 143 144static void h1_tunnel_go_state(struct Curl_cfilter *cf, 145 struct h1_tunnel_state *ts, 146 h1_tunnel_state new_state, 147 struct Curl_easy *data) 148{ 149 if(ts->tunnel_state == new_state) 150 return; 151 /* entering this one */ 152 switch(new_state) { 153 case H1_TUNNEL_INIT: 154 CURL_TRC_CF(data, cf, "new tunnel state 'init'"); 155 tunnel_reinit(cf, data, ts); 156 break; 157 158 case H1_TUNNEL_CONNECT: 159 CURL_TRC_CF(data, cf, "new tunnel state 'connect'"); 160 ts->tunnel_state = H1_TUNNEL_CONNECT; 161 ts->keepon = KEEPON_CONNECT; 162 Curl_dyn_reset(&ts->rcvbuf); 163 break; 164 165 case H1_TUNNEL_RECEIVE: 166 CURL_TRC_CF(data, cf, "new tunnel state 'receive'"); 167 ts->tunnel_state = H1_TUNNEL_RECEIVE; 168 break; 169 170 case H1_TUNNEL_RESPONSE: 171 CURL_TRC_CF(data, cf, "new tunnel state 'response'"); 172 ts->tunnel_state = H1_TUNNEL_RESPONSE; 173 break; 174 175 case H1_TUNNEL_ESTABLISHED: 176 CURL_TRC_CF(data, cf, "new tunnel state 'established'"); 177 infof(data, "CONNECT phase completed"); 178 data->state.authproxy.done = TRUE; 179 data->state.authproxy.multipass = FALSE; 180 FALLTHROUGH(); 181 case H1_TUNNEL_FAILED: 182 if(new_state == H1_TUNNEL_FAILED) 183 CURL_TRC_CF(data, cf, "new tunnel state 'failed'"); 184 ts->tunnel_state = new_state; 185 Curl_dyn_reset(&ts->rcvbuf); 186 Curl_dyn_reset(&ts->request_data); 187 /* restore the protocol pointer */ 188 data->info.httpcode = 0; /* clear it as it might've been used for the 189 proxy */ 190 /* If a proxy-authorization header was used for the proxy, then we should 191 make sure that it isn't accidentally used for the document request 192 after we've connected. So let's free and clear it here. */ 193 Curl_safefree(data->state.aptr.proxyuserpwd); 194#ifdef USE_HYPER 195 data->state.hconnect = FALSE; 196#endif 197 break; 198 } 199} 200 201static void tunnel_free(struct Curl_cfilter *cf, 202 struct Curl_easy *data) 203{ 204 struct h1_tunnel_state *ts = cf->ctx; 205 if(ts) { 206 h1_tunnel_go_state(cf, ts, H1_TUNNEL_FAILED, data); 207 Curl_dyn_free(&ts->rcvbuf); 208 Curl_dyn_free(&ts->request_data); 209 Curl_httpchunk_free(data, &ts->ch); 210 free(ts); 211 cf->ctx = NULL; 212 } 213} 214 215#ifndef USE_HYPER 216static CURLcode start_CONNECT(struct Curl_cfilter *cf, 217 struct Curl_easy *data, 218 struct h1_tunnel_state *ts) 219{ 220 struct httpreq *req = NULL; 221 int http_minor; 222 CURLcode result; 223 224 /* This only happens if we've looped here due to authentication 225 reasons, and we don't really use the newly cloned URL here 226 then. Just free() it. */ 227 Curl_safefree(data->req.newurl); 228 229 result = Curl_http_proxy_create_CONNECT(&req, cf, data, 1); 230 if(result) 231 goto out; 232 233 infof(data, "Establish HTTP proxy tunnel to %s", req->authority); 234 235 Curl_dyn_reset(&ts->request_data); 236 ts->nsent = 0; 237 ts->headerlines = 0; 238 http_minor = (cf->conn->http_proxy.proxytype == CURLPROXY_HTTP_1_0) ? 0 : 1; 239 240 result = Curl_h1_req_write_head(req, http_minor, &ts->request_data); 241 242out: 243 if(result) 244 failf(data, "Failed sending CONNECT to proxy"); 245 if(req) 246 Curl_http_req_free(req); 247 return result; 248} 249 250static CURLcode send_CONNECT(struct Curl_cfilter *cf, 251 struct Curl_easy *data, 252 struct h1_tunnel_state *ts, 253 bool *done) 254{ 255 char *buf = Curl_dyn_ptr(&ts->request_data); 256 size_t request_len = Curl_dyn_len(&ts->request_data); 257 size_t blen = request_len; 258 CURLcode result = CURLE_OK; 259 ssize_t nwritten; 260 261 if(blen <= ts->nsent) 262 goto out; /* we are done */ 263 264 blen -= ts->nsent; 265 buf += ts->nsent; 266 267 nwritten = cf->next->cft->do_send(cf->next, data, buf, blen, &result); 268 if(nwritten < 0) { 269 if(result == CURLE_AGAIN) { 270 result = CURLE_OK; 271 } 272 goto out; 273 } 274 275 DEBUGASSERT(blen >= (size_t)nwritten); 276 ts->nsent += (size_t)nwritten; 277 Curl_debug(data, CURLINFO_HEADER_OUT, buf, (size_t)nwritten); 278 279out: 280 if(result) 281 failf(data, "Failed sending CONNECT to proxy"); 282 *done = (!result && (ts->nsent >= request_len)); 283 return result; 284} 285 286static CURLcode on_resp_header(struct Curl_cfilter *cf, 287 struct Curl_easy *data, 288 struct h1_tunnel_state *ts, 289 const char *header) 290{ 291 CURLcode result = CURLE_OK; 292 struct SingleRequest *k = &data->req; 293 (void)cf; 294 295 if((checkprefix("WWW-Authenticate:", header) && 296 (401 == k->httpcode)) || 297 (checkprefix("Proxy-authenticate:", header) && 298 (407 == k->httpcode))) { 299 300 bool proxy = (k->httpcode == 407) ? TRUE : FALSE; 301 char *auth = Curl_copy_header_value(header); 302 if(!auth) 303 return CURLE_OUT_OF_MEMORY; 304 305 CURL_TRC_CF(data, cf, "CONNECT: fwd auth header '%s'", header); 306 result = Curl_http_input_auth(data, proxy, auth); 307 308 free(auth); 309 310 if(result) 311 return result; 312 } 313 else if(checkprefix("Content-Length:", header)) { 314 if(k->httpcode/100 == 2) { 315 /* A client MUST ignore any Content-Length or Transfer-Encoding 316 header fields received in a successful response to CONNECT. 317 "Successful" described as: 2xx (Successful). RFC 7231 4.3.6 */ 318 infof(data, "Ignoring Content-Length in CONNECT %03d response", 319 k->httpcode); 320 } 321 else { 322 (void)curlx_strtoofft(header + strlen("Content-Length:"), 323 NULL, 10, &ts->cl); 324 } 325 } 326 else if(Curl_compareheader(header, 327 STRCONST("Connection:"), STRCONST("close"))) 328 ts->close_connection = TRUE; 329 else if(checkprefix("Transfer-Encoding:", header)) { 330 if(k->httpcode/100 == 2) { 331 /* A client MUST ignore any Content-Length or Transfer-Encoding 332 header fields received in a successful response to CONNECT. 333 "Successful" described as: 2xx (Successful). RFC 7231 4.3.6 */ 334 infof(data, "Ignoring Transfer-Encoding in " 335 "CONNECT %03d response", k->httpcode); 336 } 337 else if(Curl_compareheader(header, 338 STRCONST("Transfer-Encoding:"), 339 STRCONST("chunked"))) { 340 infof(data, "CONNECT responded chunked"); 341 ts->chunked_encoding = TRUE; 342 /* reset our chunky engine */ 343 Curl_httpchunk_reset(data, &ts->ch, TRUE); 344 } 345 } 346 else if(Curl_compareheader(header, 347 STRCONST("Proxy-Connection:"), 348 STRCONST("close"))) 349 ts->close_connection = TRUE; 350 else if(!strncmp(header, "HTTP/1.", 7) && 351 ((header[7] == '0') || (header[7] == '1')) && 352 (header[8] == ' ') && 353 ISDIGIT(header[9]) && ISDIGIT(header[10]) && ISDIGIT(header[11]) && 354 !ISDIGIT(header[12])) { 355 /* store the HTTP code from the proxy */ 356 data->info.httpproxycode = k->httpcode = (header[9] - '0') * 100 + 357 (header[10] - '0') * 10 + (header[11] - '0'); 358 } 359 return result; 360} 361 362static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf, 363 struct Curl_easy *data, 364 struct h1_tunnel_state *ts, 365 bool *done) 366{ 367 CURLcode result = CURLE_OK; 368 struct SingleRequest *k = &data->req; 369 curl_socket_t tunnelsocket = Curl_conn_cf_get_socket(cf, data); 370 char *linep; 371 size_t line_len; 372 int error, writetype; 373 374#define SELECT_OK 0 375#define SELECT_ERROR 1 376 377 error = SELECT_OK; 378 *done = FALSE; 379 380 if(!Curl_conn_data_pending(data, cf->sockindex)) 381 return CURLE_OK; 382 383 while(ts->keepon) { 384 ssize_t nread; 385 char byte; 386 387 /* Read one byte at a time to avoid a race condition. Wait at most one 388 second before looping to ensure continuous pgrsUpdates. */ 389 result = Curl_read(data, tunnelsocket, &byte, 1, &nread); 390 if(result == CURLE_AGAIN) 391 /* socket buffer drained, return */ 392 return CURLE_OK; 393 394 if(Curl_pgrsUpdate(data)) 395 return CURLE_ABORTED_BY_CALLBACK; 396 397 if(result) { 398 ts->keepon = KEEPON_DONE; 399 break; 400 } 401 402 if(nread <= 0) { 403 if(data->set.proxyauth && data->state.authproxy.avail && 404 data->state.aptr.proxyuserpwd) { 405 /* proxy auth was requested and there was proxy auth available, 406 then deem this as "mere" proxy disconnect */ 407 ts->close_connection = TRUE; 408 infof(data, "Proxy CONNECT connection closed"); 409 } 410 else { 411 error = SELECT_ERROR; 412 failf(data, "Proxy CONNECT aborted"); 413 } 414 ts->keepon = KEEPON_DONE; 415 break; 416 } 417 418 if(ts->keepon == KEEPON_IGNORE) { 419 /* This means we are currently ignoring a response-body */ 420 421 if(ts->cl) { 422 /* A Content-Length based body: simply count down the counter 423 and make sure to break out of the loop when we're done! */ 424 ts->cl--; 425 if(ts->cl <= 0) { 426 ts->keepon = KEEPON_DONE; 427 break; 428 } 429 } 430 else if(ts->chunked_encoding) { 431 /* chunked-encoded body, so we need to do the chunked dance 432 properly to know when the end of the body is reached */ 433 size_t consumed = 0; 434 435 /* now parse the chunked piece of data so that we can 436 properly tell when the stream ends */ 437 result = Curl_httpchunk_read(data, &ts->ch, &byte, 1, &consumed); 438 if(result) 439 return result; 440 if(Curl_httpchunk_is_done(data, &ts->ch)) { 441 /* we're done reading chunks! */ 442 infof(data, "chunk reading DONE"); 443 ts->keepon = KEEPON_DONE; 444 } 445 } 446 continue; 447 } 448 449 if(Curl_dyn_addn(&ts->rcvbuf, &byte, 1)) { 450 failf(data, "CONNECT response too large"); 451 return CURLE_RECV_ERROR; 452 } 453 454 /* if this is not the end of a header line then continue */ 455 if(byte != 0x0a) 456 continue; 457 458 ts->headerlines++; 459 linep = Curl_dyn_ptr(&ts->rcvbuf); 460 line_len = Curl_dyn_len(&ts->rcvbuf); /* amount of bytes in this line */ 461 462 /* output debug if that is requested */ 463 Curl_debug(data, CURLINFO_HEADER_IN, linep, line_len); 464 465 /* send the header to the callback */ 466 writetype = CLIENTWRITE_HEADER | CLIENTWRITE_CONNECT | 467 (ts->headerlines == 1 ? CLIENTWRITE_STATUS : 0); 468 result = Curl_client_write(data, writetype, linep, line_len); 469 if(result) 470 return result; 471 472 result = Curl_bump_headersize(data, line_len, TRUE); 473 if(result) 474 return result; 475 476 /* Newlines are CRLF, so the CR is ignored as the line isn't 477 really terminated until the LF comes. Treat a following CR 478 as end-of-headers as well.*/ 479 480 if(('\r' == linep[0]) || 481 ('\n' == linep[0])) { 482 /* end of response-headers from the proxy */ 483 484 if((407 == k->httpcode) && !data->state.authproblem) { 485 /* If we get a 407 response code with content length 486 when we have no auth problem, we must ignore the 487 whole response-body */ 488 ts->keepon = KEEPON_IGNORE; 489 490 if(ts->cl) { 491 infof(data, "Ignore %" CURL_FORMAT_CURL_OFF_T 492 " bytes of response-body", ts->cl); 493 } 494 else if(ts->chunked_encoding) { 495 infof(data, "Ignore chunked response-body"); 496 } 497 else { 498 /* without content-length or chunked encoding, we 499 can't keep the connection alive since the close is 500 the end signal so we bail out at once instead */ 501 CURL_TRC_CF(data, cf, "CONNECT: no content-length or chunked"); 502 ts->keepon = KEEPON_DONE; 503 } 504 } 505 else { 506 ts->keepon = KEEPON_DONE; 507 } 508 509 DEBUGASSERT(ts->keepon == KEEPON_IGNORE 510 || ts->keepon == KEEPON_DONE); 511 continue; 512 } 513 514 result = on_resp_header(cf, data, ts, linep); 515 if(result) 516 return result; 517 518 Curl_dyn_reset(&ts->rcvbuf); 519 } /* while there's buffer left and loop is requested */ 520 521 if(error) 522 result = CURLE_RECV_ERROR; 523 *done = (ts->keepon == KEEPON_DONE); 524 if(!result && *done && data->info.httpproxycode/100 != 2) { 525 /* Deal with the possibly already received authenticate 526 headers. 'newurl' is set to a new URL if we must loop. */ 527 result = Curl_http_auth_act(data); 528 } 529 return result; 530} 531 532#else /* USE_HYPER */ 533 534static CURLcode CONNECT_host(struct Curl_cfilter *cf, 535 struct Curl_easy *data, 536 char **pauthority, 537 char **phost_header) 538{ 539 const char *hostname; 540 int port; 541 bool ipv6_ip; 542 CURLcode result; 543 char *authority; /* for CONNECT, the destination host + port */ 544 char *host_header = NULL; /* Host: authority */ 545 546 result = Curl_http_proxy_get_destination(cf, &hostname, &port, &ipv6_ip); 547 if(result) 548 return result; 549 550 authority = aprintf("%s%s%s:%d", ipv6_ip?"[":"", hostname, ipv6_ip?"]":"", 551 port); 552 if(!authority) 553 return CURLE_OUT_OF_MEMORY; 554 555 /* If user is not overriding the Host header later */ 556 if(!Curl_checkProxyheaders(data, cf->conn, STRCONST("Host"))) { 557 host_header = aprintf("Host: %s\r\n", authority); 558 if(!host_header) { 559 free(authority); 560 return CURLE_OUT_OF_MEMORY; 561 } 562 } 563 *pauthority = authority; 564 *phost_header = host_header; 565 return CURLE_OK; 566} 567 568/* The Hyper version of CONNECT */ 569static CURLcode start_CONNECT(struct Curl_cfilter *cf, 570 struct Curl_easy *data, 571 struct h1_tunnel_state *ts) 572{ 573 struct connectdata *conn = cf->conn; 574 struct hyptransfer *h = &data->hyp; 575 curl_socket_t tunnelsocket = Curl_conn_cf_get_socket(cf, data); 576 hyper_io *io = NULL; 577 hyper_request *req = NULL; 578 hyper_headers *headers = NULL; 579 hyper_clientconn_options *options = NULL; 580 hyper_task *handshake = NULL; 581 hyper_task *task = NULL; /* for the handshake */ 582 hyper_clientconn *client = NULL; 583 hyper_task *sendtask = NULL; /* for the send */ 584 char *authority = NULL; /* for CONNECT */ 585 char *host_header = NULL; /* Host: */ 586 CURLcode result = CURLE_OUT_OF_MEMORY; 587 (void)ts; 588 589 io = hyper_io_new(); 590 if(!io) { 591 failf(data, "Couldn't create hyper IO"); 592 result = CURLE_OUT_OF_MEMORY; 593 goto error; 594 } 595 /* tell Hyper how to read/write network data */ 596 hyper_io_set_userdata(io, data); 597 hyper_io_set_read(io, Curl_hyper_recv); 598 hyper_io_set_write(io, Curl_hyper_send); 599 conn->sockfd = tunnelsocket; 600 601 data->state.hconnect = TRUE; 602 603 /* create an executor to poll futures */ 604 if(!h->exec) { 605 h->exec = hyper_executor_new(); 606 if(!h->exec) { 607 failf(data, "Couldn't create hyper executor"); 608 result = CURLE_OUT_OF_MEMORY; 609 goto error; 610 } 611 } 612 613 options = hyper_clientconn_options_new(); 614 if(!options) { 615 failf(data, "Couldn't create hyper client options"); 616 result = CURLE_OUT_OF_MEMORY; 617 goto error; 618 } 619 hyper_clientconn_options_set_preserve_header_case(options, 1); 620 hyper_clientconn_options_set_preserve_header_order(options, 1); 621 622 hyper_clientconn_options_exec(options, h->exec); 623 624 /* "Both the `io` and the `options` are consumed in this function 625 call" */ 626 handshake = hyper_clientconn_handshake(io, options); 627 if(!handshake) { 628 failf(data, "Couldn't create hyper client handshake"); 629 result = CURLE_OUT_OF_MEMORY; 630 goto error; 631 } 632 io = NULL; 633 options = NULL; 634 635 if(HYPERE_OK != hyper_executor_push(h->exec, handshake)) { 636 failf(data, "Couldn't hyper_executor_push the handshake"); 637 result = CURLE_OUT_OF_MEMORY; 638 goto error; 639 } 640 handshake = NULL; /* ownership passed on */ 641 642 task = hyper_executor_poll(h->exec); 643 if(!task) { 644 failf(data, "Couldn't hyper_executor_poll the handshake"); 645 result = CURLE_OUT_OF_MEMORY; 646 goto error; 647 } 648 649 client = hyper_task_value(task); 650 hyper_task_free(task); 651 652 req = hyper_request_new(); 653 if(!req) { 654 failf(data, "Couldn't hyper_request_new"); 655 result = CURLE_OUT_OF_MEMORY; 656 goto error; 657 } 658 if(hyper_request_set_method(req, (uint8_t *)"CONNECT", 659 strlen("CONNECT"))) { 660 failf(data, "error setting method"); 661 result = CURLE_OUT_OF_MEMORY; 662 goto error; 663 } 664 665 /* This only happens if we've looped here due to authentication 666 reasons, and we don't really use the newly cloned URL here 667 then. Just free() it. */ 668 Curl_safefree(data->req.newurl); 669 670 result = CONNECT_host(cf, data, &authority, &host_header); 671 if(result) 672 goto error; 673 674 infof(data, "Establish HTTP proxy tunnel to %s", authority); 675 676 if(hyper_request_set_uri(req, (uint8_t *)authority, 677 strlen(authority))) { 678 failf(data, "error setting path"); 679 result = CURLE_OUT_OF_MEMORY; 680 goto error; 681 } 682 if(data->set.verbose) { 683 char *se = aprintf("CONNECT %s HTTP/1.1\r\n", authority); 684 if(!se) { 685 result = CURLE_OUT_OF_MEMORY; 686 goto error; 687 } 688 Curl_debug(data, CURLINFO_HEADER_OUT, se, strlen(se)); 689 free(se); 690 } 691 /* Setup the proxy-authorization header, if any */ 692 result = Curl_http_output_auth(data, conn, "CONNECT", HTTPREQ_GET, 693 authority, TRUE); 694 if(result) 695 goto error; 696 Curl_safefree(authority); 697 698 /* default is 1.1 */ 699 if((conn->http_proxy.proxytype == CURLPROXY_HTTP_1_0) && 700 (HYPERE_OK != hyper_request_set_version(req, 701 HYPER_HTTP_VERSION_1_0))) { 702 failf(data, "error setting HTTP version"); 703 result = CURLE_OUT_OF_MEMORY; 704 goto error; 705 } 706 707 headers = hyper_request_headers(req); 708 if(!headers) { 709 failf(data, "hyper_request_headers"); 710 result = CURLE_OUT_OF_MEMORY; 711 goto error; 712 } 713 if(host_header) { 714 result = Curl_hyper_header(data, headers, host_header); 715 if(result) 716 goto error; 717 Curl_safefree(host_header); 718 } 719 720 if(data->state.aptr.proxyuserpwd) { 721 result = Curl_hyper_header(data, headers, 722 data->state.aptr.proxyuserpwd); 723 if(result) 724 goto error; 725 } 726 727 if(!Curl_checkProxyheaders(data, conn, STRCONST("User-Agent")) && 728 data->set.str[STRING_USERAGENT] && *data->set.str[STRING_USERAGENT]) { 729 struct dynbuf ua; 730 Curl_dyn_init(&ua, DYN_HTTP_REQUEST); 731 result = Curl_dyn_addf(&ua, "User-Agent: %s\r\n", 732 data->set.str[STRING_USERAGENT]); 733 if(result) 734 goto error; 735 result = Curl_hyper_header(data, headers, Curl_dyn_ptr(&ua)); 736 if(result) 737 goto error; 738 Curl_dyn_free(&ua); 739 } 740 741 if(!Curl_checkProxyheaders(data, conn, STRCONST("Proxy-Connection"))) { 742 result = Curl_hyper_header(data, headers, 743 "Proxy-Connection: Keep-Alive"); 744 if(result) 745 goto error; 746 } 747 748 result = Curl_add_custom_headers(data, TRUE, headers); 749 if(result) 750 goto error; 751 752 sendtask = hyper_clientconn_send(client, req); 753 if(!sendtask) { 754 failf(data, "hyper_clientconn_send"); 755 result = CURLE_OUT_OF_MEMORY; 756 goto error; 757 } 758 req = NULL; 759 760 if(HYPERE_OK != hyper_executor_push(h->exec, sendtask)) { 761 failf(data, "Couldn't hyper_executor_push the send"); 762 result = CURLE_OUT_OF_MEMORY; 763 goto error; 764 } 765 sendtask = NULL; /* ownership passed on */ 766 767 hyper_clientconn_free(client); 768 client = NULL; 769 770error: 771 free(host_header); 772 free(authority); 773 if(io) 774 hyper_io_free(io); 775 if(options) 776 hyper_clientconn_options_free(options); 777 if(handshake) 778 hyper_task_free(handshake); 779 if(client) 780 hyper_clientconn_free(client); 781 if(req) 782 hyper_request_free(req); 783 784 return result; 785} 786 787static CURLcode send_CONNECT(struct Curl_cfilter *cf, 788 struct Curl_easy *data, 789 struct h1_tunnel_state *ts, 790 bool *done) 791{ 792 struct hyptransfer *h = &data->hyp; 793 struct connectdata *conn = cf->conn; 794 hyper_task *task = NULL; 795 hyper_error *hypererr = NULL; 796 CURLcode result = CURLE_OK; 797 798 (void)ts; 799 (void)conn; 800 do { 801 task = hyper_executor_poll(h->exec); 802 if(task) { 803 bool error = hyper_task_type(task) == HYPER_TASK_ERROR; 804 if(error) 805 hypererr = hyper_task_value(task); 806 hyper_task_free(task); 807 if(error) { 808 /* this could probably use a better error code? */ 809 result = CURLE_OUT_OF_MEMORY; 810 goto error; 811 } 812 } 813 } while(task); 814error: 815 *done = (result == CURLE_OK); 816 if(hypererr) { 817 uint8_t errbuf[256]; 818 size_t errlen = hyper_error_print(hypererr, errbuf, sizeof(errbuf)); 819 failf(data, "Hyper: %.*s", (int)errlen, errbuf); 820 hyper_error_free(hypererr); 821 } 822 return result; 823} 824 825static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf, 826 struct Curl_easy *data, 827 struct h1_tunnel_state *ts, 828 bool *done) 829{ 830 struct hyptransfer *h = &data->hyp; 831 CURLcode result; 832 int didwhat; 833 834 (void)ts; 835 *done = FALSE; 836 result = Curl_hyper_stream(data, cf->conn, &didwhat, done, 837 CURL_CSELECT_IN | CURL_CSELECT_OUT); 838 if(result || !*done) 839 return result; 840 if(h->exec) { 841 hyper_executor_free(h->exec); 842 h->exec = NULL; 843 } 844 if(h->read_waker) { 845 hyper_waker_free(h->read_waker); 846 h->read_waker = NULL; 847 } 848 if(h->write_waker) { 849 hyper_waker_free(h->write_waker); 850 h->write_waker = NULL; 851 } 852 return result; 853} 854 855#endif /* USE_HYPER */ 856 857static CURLcode H1_CONNECT(struct Curl_cfilter *cf, 858 struct Curl_easy *data, 859 struct h1_tunnel_state *ts) 860{ 861 struct connectdata *conn = cf->conn; 862 CURLcode result; 863 bool done; 864 865 if(tunnel_is_established(ts)) 866 return CURLE_OK; 867 if(tunnel_is_failed(ts)) 868 return CURLE_RECV_ERROR; /* Need a cfilter close and new bootstrap */ 869 870 do { 871 timediff_t check; 872 873 check = Curl_timeleft(data, NULL, TRUE); 874 if(check <= 0) { 875 failf(data, "Proxy CONNECT aborted due to timeout"); 876 result = CURLE_OPERATION_TIMEDOUT; 877 goto out; 878 } 879 880 switch(ts->tunnel_state) { 881 case H1_TUNNEL_INIT: 882 /* Prepare the CONNECT request and make a first attempt to send. */ 883 CURL_TRC_CF(data, cf, "CONNECT start"); 884 result = start_CONNECT(cf, data, ts); 885 if(result) 886 goto out; 887 h1_tunnel_go_state(cf, ts, H1_TUNNEL_CONNECT, data); 888 FALLTHROUGH(); 889 890 case H1_TUNNEL_CONNECT: 891 /* see that the request is completely sent */ 892 CURL_TRC_CF(data, cf, "CONNECT send"); 893 result = send_CONNECT(cf, data, ts, &done); 894 if(result || !done) 895 goto out; 896 h1_tunnel_go_state(cf, ts, H1_TUNNEL_RECEIVE, data); 897 FALLTHROUGH(); 898 899 case H1_TUNNEL_RECEIVE: 900 /* read what is there */ 901 CURL_TRC_CF(data, cf, "CONNECT receive"); 902 result = recv_CONNECT_resp(cf, data, ts, &done); 903 if(Curl_pgrsUpdate(data)) { 904 result = CURLE_ABORTED_BY_CALLBACK; 905 goto out; 906 } 907 /* error or not complete yet. return for more multi-multi */ 908 if(result || !done) 909 goto out; 910 /* got it */ 911 h1_tunnel_go_state(cf, ts, H1_TUNNEL_RESPONSE, data); 912 FALLTHROUGH(); 913 914 case H1_TUNNEL_RESPONSE: 915 CURL_TRC_CF(data, cf, "CONNECT response"); 916 if(data->req.newurl) { 917 /* not the "final" response, we need to do a follow up request. 918 * If the other side indicated a connection close, or if someone 919 * else told us to close this connection, do so now. 920 */ 921 if(ts->close_connection || conn->bits.close) { 922 /* Close this filter and the sub-chain, re-connect the 923 * sub-chain and continue. Closing this filter will 924 * reset our tunnel state. To avoid recursion, we return 925 * and expect to be called again. 926 */ 927 CURL_TRC_CF(data, cf, "CONNECT need to close+open"); 928 infof(data, "Connect me again please"); 929 Curl_conn_cf_close(cf, data); 930 connkeep(conn, "HTTP proxy CONNECT"); 931 result = Curl_conn_cf_connect(cf->next, data, FALSE, &done); 932 goto out; 933 } 934 else { 935 /* staying on this connection, reset state */ 936 h1_tunnel_go_state(cf, ts, H1_TUNNEL_INIT, data); 937 } 938 } 939 break; 940 941 default: 942 break; 943 } 944 945 } while(data->req.newurl); 946 947 DEBUGASSERT(ts->tunnel_state == H1_TUNNEL_RESPONSE); 948 if(data->info.httpproxycode/100 != 2) { 949 /* a non-2xx response and we have no next url to try. */ 950 Curl_safefree(data->req.newurl); 951 /* failure, close this connection to avoid reuse */ 952 streamclose(conn, "proxy CONNECT failure"); 953 h1_tunnel_go_state(cf, ts, H1_TUNNEL_FAILED, data); 954 failf(data, "CONNECT tunnel failed, response %d", data->req.httpcode); 955 return CURLE_RECV_ERROR; 956 } 957 /* 2xx response, SUCCESS! */ 958 h1_tunnel_go_state(cf, ts, H1_TUNNEL_ESTABLISHED, data); 959 infof(data, "CONNECT tunnel established, response %d", 960 data->info.httpproxycode); 961 result = CURLE_OK; 962 963out: 964 if(result) 965 h1_tunnel_go_state(cf, ts, H1_TUNNEL_FAILED, data); 966 return result; 967} 968 969static CURLcode cf_h1_proxy_connect(struct Curl_cfilter *cf, 970 struct Curl_easy *data, 971 bool blocking, bool *done) 972{ 973 CURLcode result; 974 struct h1_tunnel_state *ts = cf->ctx; 975 976 if(cf->connected) { 977 *done = TRUE; 978 return CURLE_OK; 979 } 980 981 CURL_TRC_CF(data, cf, "connect"); 982 result = cf->next->cft->do_connect(cf->next, data, blocking, done); 983 if(result || !*done) 984 return result; 985 986 *done = FALSE; 987 if(!ts) { 988 result = tunnel_init(cf, data, &ts); 989 if(result) 990 return result; 991 cf->ctx = ts; 992 } 993 994 /* TODO: can we do blocking? */ 995 /* We want "seamless" operations through HTTP proxy tunnel */ 996 997 result = H1_CONNECT(cf, data, ts); 998 if(result) 999 goto out; 1000 Curl_safefree(data->state.aptr.proxyuserpwd); 1001 1002out: 1003 *done = (result == CURLE_OK) && tunnel_is_established(cf->ctx); 1004 if(*done) { 1005 cf->connected = TRUE; 1006 /* Restore `data->req` fields that may habe been touched */ 1007 data->req.header = TRUE; /* assume header */ 1008 data->req.bytecount = 0; 1009 data->req.ignorebody = FALSE; 1010 Curl_client_cleanup(data); 1011 Curl_pgrsSetUploadCounter(data, 0); 1012 Curl_pgrsSetDownloadCounter(data, 0); 1013 1014 tunnel_free(cf, data); 1015 } 1016 return result; 1017} 1018 1019static void cf_h1_proxy_adjust_pollset(struct Curl_cfilter *cf, 1020 struct Curl_easy *data, 1021 struct easy_pollset *ps) 1022{ 1023 struct h1_tunnel_state *ts = cf->ctx; 1024 1025 if(!cf->connected) { 1026 /* If we are not connected, but the filter "below" is 1027 * and not waiting on something, we are tunneling. */ 1028 curl_socket_t sock = Curl_conn_cf_get_socket(cf, data); 1029 if(ts) { 1030 /* when we've sent a CONNECT to a proxy, we should rather either 1031 wait for the socket to become readable to be able to get the 1032 response headers or if we're still sending the request, wait 1033 for write. */ 1034 if(ts->CONNECT.sending == HTTPSEND_REQUEST) 1035 Curl_pollset_set_out_only(data, ps, sock); 1036 else 1037 Curl_pollset_set_in_only(data, ps, sock); 1038 } 1039 else 1040 Curl_pollset_set_out_only(data, ps, sock); 1041 } 1042} 1043 1044static void cf_h1_proxy_destroy(struct Curl_cfilter *cf, 1045 struct Curl_easy *data) 1046{ 1047 CURL_TRC_CF(data, cf, "destroy"); 1048 tunnel_free(cf, data); 1049} 1050 1051static void cf_h1_proxy_close(struct Curl_cfilter *cf, 1052 struct Curl_easy *data) 1053{ 1054 CURL_TRC_CF(data, cf, "close"); 1055 cf->connected = FALSE; 1056 if(cf->ctx) { 1057 h1_tunnel_go_state(cf, cf->ctx, H1_TUNNEL_INIT, data); 1058 } 1059 if(cf->next) 1060 cf->next->cft->do_close(cf->next, data); 1061} 1062 1063 1064struct Curl_cftype Curl_cft_h1_proxy = { 1065 "H1-PROXY", 1066 CF_TYPE_IP_CONNECT, 1067 0, 1068 cf_h1_proxy_destroy, 1069 cf_h1_proxy_connect, 1070 cf_h1_proxy_close, 1071 Curl_cf_http_proxy_get_host, 1072 cf_h1_proxy_adjust_pollset, 1073 Curl_cf_def_data_pending, 1074 Curl_cf_def_send, 1075 Curl_cf_def_recv, 1076 Curl_cf_def_cntrl, 1077 Curl_cf_def_conn_is_alive, 1078 Curl_cf_def_conn_keep_alive, 1079 Curl_cf_def_query, 1080}; 1081 1082CURLcode Curl_cf_h1_proxy_insert_after(struct Curl_cfilter *cf_at, 1083 struct Curl_easy *data) 1084{ 1085 struct Curl_cfilter *cf; 1086 CURLcode result; 1087 1088 (void)data; 1089 result = Curl_cf_create(&cf, &Curl_cft_h1_proxy, NULL); 1090 if(!result) 1091 Curl_conn_cf_insert_after(cf_at, cf); 1092 return result; 1093} 1094 1095#endif /* !CURL_DISABLE_PROXY && ! CURL_DISABLE_HTTP */ 1096