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