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