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 #include "urldata.h"
31 #include "dynbuf.h"
32 #include "sendf.h"
33 #include "http.h"
34 #include "http1.h"
35 #include "http_proxy.h"
36 #include "url.h"
37 #include "select.h"
38 #include "progress.h"
39 #include "cfilters.h"
40 #include "cf-h1-proxy.h"
41 #include "connect.h"
42 #include "curl_trc.h"
43 #include "curlx.h"
44 #include "vtls/vtls.h"
45 #include "transfer.h"
46 #include "multiif.h"
47
48 /* The last 3 #include files should be in this order */
49 #include "curl_printf.h"
50 #include "curl_memory.h"
51 #include "memdebug.h"
52
53
54 typedef enum {
55 H1_TUNNEL_INIT, /* init/default/no tunnel state */
56 H1_TUNNEL_CONNECT, /* CONNECT request is being send */
57 H1_TUNNEL_RECEIVE, /* CONNECT answer is being received */
58 H1_TUNNEL_RESPONSE, /* CONNECT response received completely */
59 H1_TUNNEL_ESTABLISHED,
60 H1_TUNNEL_FAILED
61 } h1_tunnel_state;
62
63 /* struct for HTTP CONNECT tunneling */
64 struct h1_tunnel_state {
65 struct dynbuf rcvbuf;
66 struct dynbuf request_data;
67 size_t nsent;
68 size_t headerlines;
69 struct Curl_chunker ch;
70 enum keeponval {
71 KEEPON_DONE,
72 KEEPON_CONNECT,
73 KEEPON_IGNORE
74 } keepon;
75 curl_off_t cl; /* size of content to read and ignore */
76 h1_tunnel_state tunnel_state;
77 BIT(chunked_encoding);
78 BIT(close_connection);
79 };
80
81
tunnel_is_established(struct h1_tunnel_state * ts)82 static bool tunnel_is_established(struct h1_tunnel_state *ts)
83 {
84 return ts && (ts->tunnel_state == H1_TUNNEL_ESTABLISHED);
85 }
86
tunnel_is_failed(struct h1_tunnel_state * ts)87 static bool tunnel_is_failed(struct h1_tunnel_state *ts)
88 {
89 return ts && (ts->tunnel_state == H1_TUNNEL_FAILED);
90 }
91
tunnel_reinit(struct Curl_cfilter * cf,struct Curl_easy * data,struct h1_tunnel_state * ts)92 static CURLcode tunnel_reinit(struct Curl_cfilter *cf,
93 struct Curl_easy *data,
94 struct h1_tunnel_state *ts)
95 {
96 (void)data;
97 (void)cf;
98 DEBUGASSERT(ts);
99 Curl_dyn_reset(&ts->rcvbuf);
100 Curl_dyn_reset(&ts->request_data);
101 ts->tunnel_state = H1_TUNNEL_INIT;
102 ts->keepon = KEEPON_CONNECT;
103 ts->cl = 0;
104 ts->close_connection = FALSE;
105 return CURLE_OK;
106 }
107
tunnel_init(struct Curl_cfilter * cf,struct Curl_easy * data,struct h1_tunnel_state ** pts)108 static CURLcode tunnel_init(struct Curl_cfilter *cf,
109 struct Curl_easy *data,
110 struct h1_tunnel_state **pts)
111 {
112 struct h1_tunnel_state *ts;
113
114 if(cf->conn->handler->flags & PROTOPT_NOTCPPROXY) {
115 failf(data, "%s cannot be done over CONNECT", cf->conn->handler->scheme);
116 return CURLE_UNSUPPORTED_PROTOCOL;
117 }
118
119 ts = calloc(1, sizeof(*ts));
120 if(!ts)
121 return CURLE_OUT_OF_MEMORY;
122
123 infof(data, "allocate connect buffer");
124
125 Curl_dyn_init(&ts->rcvbuf, DYN_PROXY_CONNECT_HEADERS);
126 Curl_dyn_init(&ts->request_data, DYN_HTTP_REQUEST);
127 Curl_httpchunk_init(data, &ts->ch, TRUE);
128
129 *pts = ts;
130 connkeep(cf->conn, "HTTP proxy CONNECT");
131 return tunnel_reinit(cf, data, ts);
132 }
133
h1_tunnel_go_state(struct Curl_cfilter * cf,struct h1_tunnel_state * ts,h1_tunnel_state new_state,struct Curl_easy * data)134 static void h1_tunnel_go_state(struct Curl_cfilter *cf,
135 struct h1_tunnel_state *ts,
136 h1_tunnel_state new_state,
137 struct Curl_easy *data)
138 {
139 if(ts->tunnel_state == new_state)
140 return;
141 /* entering this one */
142 switch(new_state) {
143 case H1_TUNNEL_INIT:
144 CURL_TRC_CF(data, cf, "new tunnel state 'init'");
145 tunnel_reinit(cf, data, ts);
146 break;
147
148 case H1_TUNNEL_CONNECT:
149 CURL_TRC_CF(data, cf, "new tunnel state 'connect'");
150 ts->tunnel_state = H1_TUNNEL_CONNECT;
151 ts->keepon = KEEPON_CONNECT;
152 Curl_dyn_reset(&ts->rcvbuf);
153 break;
154
155 case H1_TUNNEL_RECEIVE:
156 CURL_TRC_CF(data, cf, "new tunnel state 'receive'");
157 ts->tunnel_state = H1_TUNNEL_RECEIVE;
158 break;
159
160 case H1_TUNNEL_RESPONSE:
161 CURL_TRC_CF(data, cf, "new tunnel state 'response'");
162 ts->tunnel_state = H1_TUNNEL_RESPONSE;
163 break;
164
165 case H1_TUNNEL_ESTABLISHED:
166 CURL_TRC_CF(data, cf, "new tunnel state 'established'");
167 infof(data, "CONNECT phase completed");
168 data->state.authproxy.done = TRUE;
169 data->state.authproxy.multipass = FALSE;
170 FALLTHROUGH();
171 case H1_TUNNEL_FAILED:
172 if(new_state == H1_TUNNEL_FAILED)
173 CURL_TRC_CF(data, cf, "new tunnel state 'failed'");
174 ts->tunnel_state = new_state;
175 Curl_dyn_reset(&ts->rcvbuf);
176 Curl_dyn_reset(&ts->request_data);
177 /* restore the protocol pointer */
178 data->info.httpcode = 0; /* clear it as it might've been used for the
179 proxy */
180 /* If a proxy-authorization header was used for the proxy, then we should
181 make sure that it is not accidentally used for the document request
182 after we have connected. So let's free and clear it here. */
183 Curl_safefree(data->state.aptr.proxyuserpwd);
184 break;
185 }
186 }
187
tunnel_free(struct Curl_cfilter * cf,struct Curl_easy * data)188 static void tunnel_free(struct Curl_cfilter *cf,
189 struct Curl_easy *data)
190 {
191 if(cf) {
192 struct h1_tunnel_state *ts = cf->ctx;
193 if(ts) {
194 h1_tunnel_go_state(cf, ts, H1_TUNNEL_FAILED, data);
195 Curl_dyn_free(&ts->rcvbuf);
196 Curl_dyn_free(&ts->request_data);
197 Curl_httpchunk_free(data, &ts->ch);
198 free(ts);
199 cf->ctx = NULL;
200 }
201 }
202 }
203
tunnel_want_send(struct h1_tunnel_state * ts)204 static bool tunnel_want_send(struct h1_tunnel_state *ts)
205 {
206 return (ts->tunnel_state == H1_TUNNEL_CONNECT);
207 }
208
start_CONNECT(struct Curl_cfilter * cf,struct Curl_easy * data,struct h1_tunnel_state * ts)209 static CURLcode start_CONNECT(struct Curl_cfilter *cf,
210 struct Curl_easy *data,
211 struct h1_tunnel_state *ts)
212 {
213 struct httpreq *req = NULL;
214 int http_minor;
215 CURLcode result;
216
217 /* This only happens if we have looped here due to authentication
218 reasons, and we do not really use the newly cloned URL here
219 then. Just free() it. */
220 Curl_safefree(data->req.newurl);
221
222 result = Curl_http_proxy_create_CONNECT(&req, cf, data, 1);
223 if(result)
224 goto out;
225
226 infof(data, "Establish HTTP proxy tunnel to %s", req->authority);
227
228 Curl_dyn_reset(&ts->request_data);
229 ts->nsent = 0;
230 ts->headerlines = 0;
231 http_minor = (cf->conn->http_proxy.proxytype == CURLPROXY_HTTP_1_0) ? 0 : 1;
232
233 result = Curl_h1_req_write_head(req, http_minor, &ts->request_data);
234 if(!result)
235 result = Curl_creader_set_null(data);
236
237 out:
238 if(result)
239 failf(data, "Failed sending CONNECT to proxy");
240 if(req)
241 Curl_http_req_free(req);
242 return result;
243 }
244
send_CONNECT(struct Curl_cfilter * cf,struct Curl_easy * data,struct h1_tunnel_state * ts,bool * done)245 static CURLcode send_CONNECT(struct Curl_cfilter *cf,
246 struct Curl_easy *data,
247 struct h1_tunnel_state *ts,
248 bool *done)
249 {
250 char *buf = Curl_dyn_ptr(&ts->request_data);
251 size_t request_len = Curl_dyn_len(&ts->request_data);
252 size_t blen = request_len;
253 CURLcode result = CURLE_OK;
254 ssize_t nwritten;
255
256 if(blen <= ts->nsent)
257 goto out; /* we are done */
258
259 blen -= ts->nsent;
260 buf += ts->nsent;
261
262 nwritten = cf->next->cft->do_send(cf->next, data, buf, blen, FALSE, &result);
263 if(nwritten < 0) {
264 if(result == CURLE_AGAIN) {
265 result = CURLE_OK;
266 }
267 goto out;
268 }
269
270 DEBUGASSERT(blen >= (size_t)nwritten);
271 ts->nsent += (size_t)nwritten;
272 Curl_debug(data, CURLINFO_HEADER_OUT, buf, (size_t)nwritten);
273
274 out:
275 if(result)
276 failf(data, "Failed sending CONNECT to proxy");
277 *done = (!result && (ts->nsent >= request_len));
278 return result;
279 }
280
on_resp_header(struct Curl_cfilter * cf,struct Curl_easy * data,struct h1_tunnel_state * ts,const char * header)281 static CURLcode on_resp_header(struct Curl_cfilter *cf,
282 struct Curl_easy *data,
283 struct h1_tunnel_state *ts,
284 const char *header)
285 {
286 CURLcode result = CURLE_OK;
287 struct SingleRequest *k = &data->req;
288 (void)cf;
289
290 if((checkprefix("WWW-Authenticate:", header) &&
291 (401 == k->httpcode)) ||
292 (checkprefix("Proxy-authenticate:", header) &&
293 (407 == k->httpcode))) {
294
295 bool proxy = (k->httpcode == 407);
296 char *auth = Curl_copy_header_value(header);
297 if(!auth)
298 return CURLE_OUT_OF_MEMORY;
299
300 CURL_TRC_CF(data, cf, "CONNECT: fwd auth header '%s'", header);
301 result = Curl_http_input_auth(data, proxy, auth);
302
303 free(auth);
304
305 if(result)
306 return result;
307 }
308 else if(checkprefix("Content-Length:", header)) {
309 if(k->httpcode/100 == 2) {
310 /* A client MUST ignore any Content-Length or Transfer-Encoding
311 header fields received in a successful response to CONNECT.
312 "Successful" described as: 2xx (Successful). RFC 7231 4.3.6 */
313 infof(data, "Ignoring Content-Length in CONNECT %03d response",
314 k->httpcode);
315 }
316 else {
317 (void)curlx_strtoofft(header + strlen("Content-Length:"),
318 NULL, 10, &ts->cl);
319 }
320 }
321 else if(Curl_compareheader(header,
322 STRCONST("Connection:"), STRCONST("close")))
323 ts->close_connection = TRUE;
324 else if(checkprefix("Transfer-Encoding:", header)) {
325 if(k->httpcode/100 == 2) {
326 /* A client MUST ignore any Content-Length or Transfer-Encoding
327 header fields received in a successful response to CONNECT.
328 "Successful" described as: 2xx (Successful). RFC 7231 4.3.6 */
329 infof(data, "Ignoring Transfer-Encoding in "
330 "CONNECT %03d response", k->httpcode);
331 }
332 else if(Curl_compareheader(header,
333 STRCONST("Transfer-Encoding:"),
334 STRCONST("chunked"))) {
335 infof(data, "CONNECT responded chunked");
336 ts->chunked_encoding = TRUE;
337 /* reset our chunky engine */
338 Curl_httpchunk_reset(data, &ts->ch, TRUE);
339 }
340 }
341 else if(Curl_compareheader(header,
342 STRCONST("Proxy-Connection:"),
343 STRCONST("close")))
344 ts->close_connection = TRUE;
345 else if(!strncmp(header, "HTTP/1.", 7) &&
346 ((header[7] == '0') || (header[7] == '1')) &&
347 (header[8] == ' ') &&
348 ISDIGIT(header[9]) && ISDIGIT(header[10]) && ISDIGIT(header[11]) &&
349 !ISDIGIT(header[12])) {
350 /* store the HTTP code from the proxy */
351 data->info.httpproxycode = k->httpcode = (header[9] - '0') * 100 +
352 (header[10] - '0') * 10 + (header[11] - '0');
353 }
354 return result;
355 }
356
recv_CONNECT_resp(struct Curl_cfilter * cf,struct Curl_easy * data,struct h1_tunnel_state * ts,bool * done)357 static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf,
358 struct Curl_easy *data,
359 struct h1_tunnel_state *ts,
360 bool *done)
361 {
362 CURLcode result = CURLE_OK;
363 struct SingleRequest *k = &data->req;
364 char *linep;
365 size_t line_len;
366 int error, writetype;
367
368 #define SELECT_OK 0
369 #define SELECT_ERROR 1
370
371 error = SELECT_OK;
372 *done = FALSE;
373
374 if(!Curl_conn_data_pending(data, cf->sockindex))
375 return CURLE_OK;
376
377 while(ts->keepon) {
378 ssize_t nread;
379 char byte;
380
381 /* Read one byte at a time to avoid a race condition. Wait at most one
382 second before looping to ensure continuous pgrsUpdates. */
383 result = Curl_conn_recv(data, cf->sockindex, &byte, 1, &nread);
384 if(result == CURLE_AGAIN)
385 /* socket buffer drained, return */
386 return CURLE_OK;
387
388 if(Curl_pgrsUpdate(data))
389 return CURLE_ABORTED_BY_CALLBACK;
390
391 if(result) {
392 ts->keepon = KEEPON_DONE;
393 break;
394 }
395
396 if(nread <= 0) {
397 if(data->set.proxyauth && data->state.authproxy.avail &&
398 data->state.aptr.proxyuserpwd) {
399 /* proxy auth was requested and there was proxy auth available,
400 then deem this as "mere" proxy disconnect */
401 ts->close_connection = TRUE;
402 infof(data, "Proxy CONNECT connection closed");
403 }
404 else {
405 error = SELECT_ERROR;
406 failf(data, "Proxy CONNECT aborted");
407 }
408 ts->keepon = KEEPON_DONE;
409 break;
410 }
411
412 if(ts->keepon == KEEPON_IGNORE) {
413 /* This means we are currently ignoring a response-body */
414
415 if(ts->cl) {
416 /* A Content-Length based body: simply count down the counter
417 and make sure to break out of the loop when we are done! */
418 ts->cl--;
419 if(ts->cl <= 0) {
420 ts->keepon = KEEPON_DONE;
421 break;
422 }
423 }
424 else if(ts->chunked_encoding) {
425 /* chunked-encoded body, so we need to do the chunked dance
426 properly to know when the end of the body is reached */
427 size_t consumed = 0;
428
429 /* now parse the chunked piece of data so that we can
430 properly tell when the stream ends */
431 result = Curl_httpchunk_read(data, &ts->ch, &byte, 1, &consumed);
432 if(result)
433 return result;
434 if(Curl_httpchunk_is_done(data, &ts->ch)) {
435 /* we are done reading chunks! */
436 infof(data, "chunk reading DONE");
437 ts->keepon = KEEPON_DONE;
438 }
439 }
440 continue;
441 }
442
443 if(Curl_dyn_addn(&ts->rcvbuf, &byte, 1)) {
444 failf(data, "CONNECT response too large");
445 return CURLE_RECV_ERROR;
446 }
447
448 /* if this is not the end of a header line then continue */
449 if(byte != 0x0a)
450 continue;
451
452 ts->headerlines++;
453 linep = Curl_dyn_ptr(&ts->rcvbuf);
454 line_len = Curl_dyn_len(&ts->rcvbuf); /* amount of bytes in this line */
455
456 /* output debug if that is requested */
457 Curl_debug(data, CURLINFO_HEADER_IN, linep, line_len);
458
459 /* send the header to the callback */
460 writetype = CLIENTWRITE_HEADER | CLIENTWRITE_CONNECT |
461 (ts->headerlines == 1 ? CLIENTWRITE_STATUS : 0);
462 result = Curl_client_write(data, writetype, linep, line_len);
463 if(result)
464 return result;
465
466 result = Curl_bump_headersize(data, line_len, TRUE);
467 if(result)
468 return result;
469
470 /* Newlines are CRLF, so the CR is ignored as the line is not
471 really terminated until the LF comes. Treat a following CR
472 as end-of-headers as well.*/
473
474 if(('\r' == linep[0]) ||
475 ('\n' == linep[0])) {
476 /* end of response-headers from the proxy */
477
478 if((407 == k->httpcode) && !data->state.authproblem) {
479 /* If we get a 407 response code with content length
480 when we have no auth problem, we must ignore the
481 whole response-body */
482 ts->keepon = KEEPON_IGNORE;
483
484 if(ts->cl) {
485 infof(data, "Ignore %" FMT_OFF_T " bytes of response-body", ts->cl);
486 }
487 else if(ts->chunked_encoding) {
488 infof(data, "Ignore chunked response-body");
489 }
490 else {
491 /* without content-length or chunked encoding, we
492 cannot keep the connection alive since the close is
493 the end signal so we bail out at once instead */
494 CURL_TRC_CF(data, cf, "CONNECT: no content-length or chunked");
495 ts->keepon = KEEPON_DONE;
496 }
497 }
498 else {
499 ts->keepon = KEEPON_DONE;
500 }
501
502 DEBUGASSERT(ts->keepon == KEEPON_IGNORE
503 || ts->keepon == KEEPON_DONE);
504 continue;
505 }
506
507 result = on_resp_header(cf, data, ts, linep);
508 if(result)
509 return result;
510
511 Curl_dyn_reset(&ts->rcvbuf);
512 } /* while there is buffer left and loop is requested */
513
514 if(error)
515 result = CURLE_RECV_ERROR;
516 *done = (ts->keepon == KEEPON_DONE);
517 if(!result && *done && data->info.httpproxycode/100 != 2) {
518 /* Deal with the possibly already received authenticate
519 headers. 'newurl' is set to a new URL if we must loop. */
520 result = Curl_http_auth_act(data);
521 }
522 return result;
523 }
524
H1_CONNECT(struct Curl_cfilter * cf,struct Curl_easy * data,struct h1_tunnel_state * ts)525 static CURLcode H1_CONNECT(struct Curl_cfilter *cf,
526 struct Curl_easy *data,
527 struct h1_tunnel_state *ts)
528 {
529 struct connectdata *conn = cf->conn;
530 CURLcode result;
531 bool done;
532
533 if(tunnel_is_established(ts))
534 return CURLE_OK;
535 if(tunnel_is_failed(ts))
536 return CURLE_RECV_ERROR; /* Need a cfilter close and new bootstrap */
537
538 do {
539 timediff_t check;
540
541 check = Curl_timeleft(data, NULL, TRUE);
542 if(check <= 0) {
543 failf(data, "Proxy CONNECT aborted due to timeout");
544 result = CURLE_OPERATION_TIMEDOUT;
545 goto out;
546 }
547
548 switch(ts->tunnel_state) {
549 case H1_TUNNEL_INIT:
550 /* Prepare the CONNECT request and make a first attempt to send. */
551 CURL_TRC_CF(data, cf, "CONNECT start");
552 result = start_CONNECT(cf, data, ts);
553 if(result)
554 goto out;
555 h1_tunnel_go_state(cf, ts, H1_TUNNEL_CONNECT, data);
556 FALLTHROUGH();
557
558 case H1_TUNNEL_CONNECT:
559 /* see that the request is completely sent */
560 CURL_TRC_CF(data, cf, "CONNECT send");
561 result = send_CONNECT(cf, data, ts, &done);
562 if(result || !done)
563 goto out;
564 h1_tunnel_go_state(cf, ts, H1_TUNNEL_RECEIVE, data);
565 FALLTHROUGH();
566
567 case H1_TUNNEL_RECEIVE:
568 /* read what is there */
569 CURL_TRC_CF(data, cf, "CONNECT receive");
570 result = recv_CONNECT_resp(cf, data, ts, &done);
571 if(Curl_pgrsUpdate(data)) {
572 result = CURLE_ABORTED_BY_CALLBACK;
573 goto out;
574 }
575 /* error or not complete yet. return for more multi-multi */
576 if(result || !done)
577 goto out;
578 /* got it */
579 h1_tunnel_go_state(cf, ts, H1_TUNNEL_RESPONSE, data);
580 FALLTHROUGH();
581
582 case H1_TUNNEL_RESPONSE:
583 CURL_TRC_CF(data, cf, "CONNECT response");
584 if(data->req.newurl) {
585 /* not the "final" response, we need to do a follow up request.
586 * If the other side indicated a connection close, or if someone
587 * else told us to close this connection, do so now.
588 */
589 Curl_req_soft_reset(&data->req, data);
590 if(ts->close_connection || conn->bits.close) {
591 /* Close this filter and the sub-chain, re-connect the
592 * sub-chain and continue. Closing this filter will
593 * reset our tunnel state. To avoid recursion, we return
594 * and expect to be called again.
595 */
596 CURL_TRC_CF(data, cf, "CONNECT need to close+open");
597 infof(data, "Connect me again please");
598 Curl_conn_cf_close(cf, data);
599 connkeep(conn, "HTTP proxy CONNECT");
600 result = Curl_conn_cf_connect(cf->next, data, FALSE, &done);
601 goto out;
602 }
603 else {
604 /* staying on this connection, reset state */
605 h1_tunnel_go_state(cf, ts, H1_TUNNEL_INIT, data);
606 }
607 }
608 break;
609
610 default:
611 break;
612 }
613
614 } while(data->req.newurl);
615
616 DEBUGASSERT(ts->tunnel_state == H1_TUNNEL_RESPONSE);
617 if(data->info.httpproxycode/100 != 2) {
618 /* a non-2xx response and we have no next URL to try. */
619 Curl_safefree(data->req.newurl);
620 /* failure, close this connection to avoid reuse */
621 streamclose(conn, "proxy CONNECT failure");
622 h1_tunnel_go_state(cf, ts, H1_TUNNEL_FAILED, data);
623 failf(data, "CONNECT tunnel failed, response %d", data->req.httpcode);
624 return CURLE_RECV_ERROR;
625 }
626 /* 2xx response, SUCCESS! */
627 h1_tunnel_go_state(cf, ts, H1_TUNNEL_ESTABLISHED, data);
628 infof(data, "CONNECT tunnel established, response %d",
629 data->info.httpproxycode);
630 result = CURLE_OK;
631
632 out:
633 if(result)
634 h1_tunnel_go_state(cf, ts, H1_TUNNEL_FAILED, data);
635 return result;
636 }
637
cf_h1_proxy_connect(struct Curl_cfilter * cf,struct Curl_easy * data,bool blocking,bool * done)638 static CURLcode cf_h1_proxy_connect(struct Curl_cfilter *cf,
639 struct Curl_easy *data,
640 bool blocking, bool *done)
641 {
642 CURLcode result;
643 struct h1_tunnel_state *ts = cf->ctx;
644
645 if(cf->connected) {
646 *done = TRUE;
647 return CURLE_OK;
648 }
649
650 CURL_TRC_CF(data, cf, "connect");
651 result = cf->next->cft->do_connect(cf->next, data, blocking, done);
652 if(result || !*done)
653 return result;
654
655 *done = FALSE;
656 if(!ts) {
657 result = tunnel_init(cf, data, &ts);
658 if(result)
659 return result;
660 cf->ctx = ts;
661 }
662
663 /* TODO: can we do blocking? */
664 /* We want "seamless" operations through HTTP proxy tunnel */
665
666 result = H1_CONNECT(cf, data, ts);
667 if(result)
668 goto out;
669 Curl_safefree(data->state.aptr.proxyuserpwd);
670
671 out:
672 *done = (result == CURLE_OK) && tunnel_is_established(cf->ctx);
673 if(*done) {
674 cf->connected = TRUE;
675 /* The real request will follow the CONNECT, reset request partially */
676 Curl_req_soft_reset(&data->req, data);
677 Curl_client_reset(data);
678 Curl_pgrsSetUploadCounter(data, 0);
679 Curl_pgrsSetDownloadCounter(data, 0);
680
681 tunnel_free(cf, data);
682 }
683 return result;
684 }
685
cf_h1_proxy_adjust_pollset(struct Curl_cfilter * cf,struct Curl_easy * data,struct easy_pollset * ps)686 static void cf_h1_proxy_adjust_pollset(struct Curl_cfilter *cf,
687 struct Curl_easy *data,
688 struct easy_pollset *ps)
689 {
690 struct h1_tunnel_state *ts = cf->ctx;
691
692 if(!cf->connected) {
693 /* If we are not connected, but the filter "below" is
694 * and not waiting on something, we are tunneling. */
695 curl_socket_t sock = Curl_conn_cf_get_socket(cf, data);
696 if(ts) {
697 /* when we have sent a CONNECT to a proxy, we should rather either
698 wait for the socket to become readable to be able to get the
699 response headers or if we are still sending the request, wait
700 for write. */
701 if(tunnel_want_send(ts))
702 Curl_pollset_set_out_only(data, ps, sock);
703 else
704 Curl_pollset_set_in_only(data, ps, sock);
705 }
706 else
707 Curl_pollset_set_out_only(data, ps, sock);
708 }
709 }
710
cf_h1_proxy_destroy(struct Curl_cfilter * cf,struct Curl_easy * data)711 static void cf_h1_proxy_destroy(struct Curl_cfilter *cf,
712 struct Curl_easy *data)
713 {
714 CURL_TRC_CF(data, cf, "destroy");
715 tunnel_free(cf, data);
716 }
717
cf_h1_proxy_close(struct Curl_cfilter * cf,struct Curl_easy * data)718 static void cf_h1_proxy_close(struct Curl_cfilter *cf,
719 struct Curl_easy *data)
720 {
721 CURL_TRC_CF(data, cf, "close");
722 if(cf) {
723 cf->connected = FALSE;
724 if(cf->ctx) {
725 h1_tunnel_go_state(cf, cf->ctx, H1_TUNNEL_INIT, data);
726 }
727 if(cf->next)
728 cf->next->cft->do_close(cf->next, data);
729 }
730 }
731
732
733 struct Curl_cftype Curl_cft_h1_proxy = {
734 "H1-PROXY",
735 CF_TYPE_IP_CONNECT|CF_TYPE_PROXY,
736 0,
737 cf_h1_proxy_destroy,
738 cf_h1_proxy_connect,
739 cf_h1_proxy_close,
740 Curl_cf_def_shutdown,
741 Curl_cf_http_proxy_get_host,
742 cf_h1_proxy_adjust_pollset,
743 Curl_cf_def_data_pending,
744 Curl_cf_def_send,
745 Curl_cf_def_recv,
746 Curl_cf_def_cntrl,
747 Curl_cf_def_conn_is_alive,
748 Curl_cf_def_conn_keep_alive,
749 Curl_cf_def_query,
750 };
751
Curl_cf_h1_proxy_insert_after(struct Curl_cfilter * cf_at,struct Curl_easy * data)752 CURLcode Curl_cf_h1_proxy_insert_after(struct Curl_cfilter *cf_at,
753 struct Curl_easy *data)
754 {
755 struct Curl_cfilter *cf;
756 CURLcode result;
757
758 (void)data;
759 result = Curl_cf_create(&cf, &Curl_cft_h1_proxy, NULL);
760 if(!result)
761 Curl_conn_cf_insert_after(cf_at, cf);
762 return result;
763 }
764
765 #endif /* !CURL_DISABLE_PROXY && ! CURL_DISABLE_HTTP */
766