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
57 typedef 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 */
67 struct 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
tunnel_is_established(struct h1_tunnel_state *ts)86 static bool tunnel_is_established(struct h1_tunnel_state *ts)
87 {
88 return ts && (ts->tunnel_state == H1_TUNNEL_ESTABLISHED);
89 }
90
tunnel_is_failed(struct h1_tunnel_state *ts)91 static bool tunnel_is_failed(struct h1_tunnel_state *ts)
92 {
93 return ts && (ts->tunnel_state == H1_TUNNEL_FAILED);
94 }
95
tunnel_reinit(struct Curl_cfilter *cf, struct Curl_easy *data, struct h1_tunnel_state *ts)96 static 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
tunnel_init(struct Curl_cfilter *cf, struct Curl_easy *data, struct h1_tunnel_state **pts)112 static 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
h1_tunnel_go_state(struct Curl_cfilter *cf, struct h1_tunnel_state *ts, h1_tunnel_state new_state, struct Curl_easy *data)144 static 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
tunnel_free(struct Curl_cfilter *cf, struct Curl_easy *data)201 static 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
start_CONNECT(struct Curl_cfilter *cf, struct Curl_easy *data, struct h1_tunnel_state *ts)216 static 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
242 out:
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
send_CONNECT(struct Curl_cfilter *cf, struct Curl_easy *data, struct h1_tunnel_state *ts, bool *done)250 static 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
279 out:
280 if(result)
281 failf(data, "Failed sending CONNECT to proxy");
282 *done = (!result && (ts->nsent >= request_len));
283 return result;
284 }
285
on_resp_header(struct Curl_cfilter *cf, struct Curl_easy *data, struct h1_tunnel_state *ts, const char *header)286 static 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
recv_CONNECT_resp(struct Curl_cfilter *cf, struct Curl_easy *data, struct h1_tunnel_state *ts, bool *done)362 static 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
CONNECT_host(struct Curl_cfilter *cf, struct Curl_easy *data, char **pauthority, char **phost_header)534 static 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 */
start_CONNECT(struct Curl_cfilter *cf, struct Curl_easy *data, struct h1_tunnel_state *ts)569 static 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
770 error:
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
send_CONNECT(struct Curl_cfilter *cf, struct Curl_easy *data, struct h1_tunnel_state *ts, bool *done)787 static 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);
814 error:
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
recv_CONNECT_resp(struct Curl_cfilter *cf, struct Curl_easy *data, struct h1_tunnel_state *ts, bool *done)825 static 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
H1_CONNECT(struct Curl_cfilter *cf, struct Curl_easy *data, struct h1_tunnel_state *ts)857 static 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
963 out:
964 if(result)
965 h1_tunnel_go_state(cf, ts, H1_TUNNEL_FAILED, data);
966 return result;
967 }
968
cf_h1_proxy_connect(struct Curl_cfilter *cf, struct Curl_easy *data, bool blocking, bool *done)969 static 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
1002 out:
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
cf_h1_proxy_adjust_pollset(struct Curl_cfilter *cf, struct Curl_easy *data, struct easy_pollset *ps)1019 static 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
cf_h1_proxy_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)1044 static 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
cf_h1_proxy_close(struct Curl_cfilter *cf, struct Curl_easy *data)1051 static 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
1064 struct 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
Curl_cf_h1_proxy_insert_after(struct Curl_cfilter *cf_at, struct Curl_easy *data)1082 CURLcode 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