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