xref: /curl/lib/cf-h1-proxy.c (revision 32101010)
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   struct h1_tunnel_state *ts = cf->ctx;
199   if(ts) {
200     h1_tunnel_go_state(cf, ts, H1_TUNNEL_FAILED, data);
201     Curl_dyn_free(&ts->rcvbuf);
202     Curl_dyn_free(&ts->request_data);
203     Curl_httpchunk_free(data, &ts->ch);
204     free(ts);
205     cf->ctx = NULL;
206   }
207 }
208 
tunnel_want_send(struct h1_tunnel_state * ts)209 static bool tunnel_want_send(struct h1_tunnel_state *ts)
210 {
211   return (ts->tunnel_state == H1_TUNNEL_CONNECT);
212 }
213 
214 #ifndef USE_HYPER
start_CONNECT(struct Curl_cfilter * cf,struct Curl_easy * data,struct h1_tunnel_state * ts)215 static CURLcode start_CONNECT(struct Curl_cfilter *cf,
216                               struct Curl_easy *data,
217                               struct h1_tunnel_state *ts)
218 {
219   struct httpreq *req = NULL;
220   int http_minor;
221   CURLcode result;
222 
223     /* This only happens if we've looped here due to authentication
224        reasons, and we don't really use the newly cloned URL here
225        then. Just free() it. */
226   Curl_safefree(data->req.newurl);
227 
228   result = Curl_http_proxy_create_CONNECT(&req, cf, data, 1);
229   if(result)
230     goto out;
231 
232   infof(data, "Establish HTTP proxy tunnel to %s", req->authority);
233 
234   Curl_dyn_reset(&ts->request_data);
235   ts->nsent = 0;
236   ts->headerlines = 0;
237   http_minor = (cf->conn->http_proxy.proxytype == CURLPROXY_HTTP_1_0) ? 0 : 1;
238 
239   result = Curl_h1_req_write_head(req, http_minor, &ts->request_data);
240   if(!result)
241     result = Curl_creader_set_null(data);
242 
243 out:
244   if(result)
245     failf(data, "Failed sending CONNECT to proxy");
246   if(req)
247     Curl_http_req_free(req);
248   return result;
249 }
250 
send_CONNECT(struct Curl_cfilter * cf,struct Curl_easy * data,struct h1_tunnel_state * ts,bool * done)251 static CURLcode send_CONNECT(struct Curl_cfilter *cf,
252                              struct Curl_easy *data,
253                              struct h1_tunnel_state *ts,
254                              bool *done)
255 {
256   char *buf = Curl_dyn_ptr(&ts->request_data);
257   size_t request_len = Curl_dyn_len(&ts->request_data);
258   size_t blen = request_len;
259   CURLcode result = CURLE_OK;
260   ssize_t nwritten;
261 
262   if(blen <= ts->nsent)
263     goto out;  /* we are done */
264 
265   blen -= ts->nsent;
266   buf += ts->nsent;
267 
268   nwritten = cf->next->cft->do_send(cf->next, data, buf, blen, &result);
269   if(nwritten < 0) {
270     if(result == CURLE_AGAIN) {
271       result = CURLE_OK;
272     }
273     goto out;
274   }
275 
276   DEBUGASSERT(blen >= (size_t)nwritten);
277   ts->nsent += (size_t)nwritten;
278   Curl_debug(data, CURLINFO_HEADER_OUT, buf, (size_t)nwritten);
279 
280 out:
281   if(result)
282     failf(data, "Failed sending CONNECT to proxy");
283   *done = (!result && (ts->nsent >= request_len));
284   return result;
285 }
286 
on_resp_header(struct Curl_cfilter * cf,struct Curl_easy * data,struct h1_tunnel_state * ts,const char * header)287 static CURLcode on_resp_header(struct Curl_cfilter *cf,
288                                struct Curl_easy *data,
289                                struct h1_tunnel_state *ts,
290                                const char *header)
291 {
292   CURLcode result = CURLE_OK;
293   struct SingleRequest *k = &data->req;
294   (void)cf;
295 
296   if((checkprefix("WWW-Authenticate:", header) &&
297       (401 == k->httpcode)) ||
298      (checkprefix("Proxy-authenticate:", header) &&
299       (407 == k->httpcode))) {
300 
301     bool proxy = (k->httpcode == 407) ? TRUE : FALSE;
302     char *auth = Curl_copy_header_value(header);
303     if(!auth)
304       return CURLE_OUT_OF_MEMORY;
305 
306     CURL_TRC_CF(data, cf, "CONNECT: fwd auth header '%s'", header);
307     result = Curl_http_input_auth(data, proxy, auth);
308 
309     free(auth);
310 
311     if(result)
312       return result;
313   }
314   else if(checkprefix("Content-Length:", header)) {
315     if(k->httpcode/100 == 2) {
316       /* A client MUST ignore any Content-Length or Transfer-Encoding
317          header fields received in a successful response to CONNECT.
318          "Successful" described as: 2xx (Successful). RFC 7231 4.3.6 */
319       infof(data, "Ignoring Content-Length in CONNECT %03d response",
320             k->httpcode);
321     }
322     else {
323       (void)curlx_strtoofft(header + strlen("Content-Length:"),
324                             NULL, 10, &ts->cl);
325     }
326   }
327   else if(Curl_compareheader(header,
328                              STRCONST("Connection:"), STRCONST("close")))
329     ts->close_connection = TRUE;
330   else if(checkprefix("Transfer-Encoding:", header)) {
331     if(k->httpcode/100 == 2) {
332       /* A client MUST ignore any Content-Length or Transfer-Encoding
333          header fields received in a successful response to CONNECT.
334          "Successful" described as: 2xx (Successful). RFC 7231 4.3.6 */
335       infof(data, "Ignoring Transfer-Encoding in "
336             "CONNECT %03d response", k->httpcode);
337     }
338     else if(Curl_compareheader(header,
339                                STRCONST("Transfer-Encoding:"),
340                                STRCONST("chunked"))) {
341       infof(data, "CONNECT responded chunked");
342       ts->chunked_encoding = TRUE;
343       /* reset our chunky engine */
344       Curl_httpchunk_reset(data, &ts->ch, TRUE);
345     }
346   }
347   else if(Curl_compareheader(header,
348                              STRCONST("Proxy-Connection:"),
349                              STRCONST("close")))
350     ts->close_connection = TRUE;
351   else if(!strncmp(header, "HTTP/1.", 7) &&
352           ((header[7] == '0') || (header[7] == '1')) &&
353           (header[8] == ' ') &&
354           ISDIGIT(header[9]) && ISDIGIT(header[10]) && ISDIGIT(header[11]) &&
355           !ISDIGIT(header[12])) {
356     /* store the HTTP code from the proxy */
357     data->info.httpproxycode =  k->httpcode = (header[9] - '0') * 100 +
358       (header[10] - '0') * 10 + (header[11] - '0');
359   }
360   return result;
361 }
362 
recv_CONNECT_resp(struct Curl_cfilter * cf,struct Curl_easy * data,struct h1_tunnel_state * ts,bool * done)363 static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf,
364                                   struct Curl_easy *data,
365                                   struct h1_tunnel_state *ts,
366                                   bool *done)
367 {
368   CURLcode result = CURLE_OK;
369   struct SingleRequest *k = &data->req;
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_conn_recv(data, cf->sockindex, &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   h->io_ctx.data = data;
597   h->io_ctx.sockindex = cf->sockindex;
598   hyper_io_set_userdata(io, &h->io_ctx);
599   hyper_io_set_read(io, Curl_hyper_recv);
600   hyper_io_set_write(io, Curl_hyper_send);
601   conn->sockfd = tunnelsocket;
602 
603   data->state.hconnect = TRUE;
604 
605   /* create an executor to poll futures */
606   if(!h->exec) {
607     h->exec = hyper_executor_new();
608     if(!h->exec) {
609       failf(data, "Couldn't create hyper executor");
610       result = CURLE_OUT_OF_MEMORY;
611       goto error;
612     }
613   }
614 
615   options = hyper_clientconn_options_new();
616   if(!options) {
617     failf(data, "Couldn't create hyper client options");
618     result = CURLE_OUT_OF_MEMORY;
619     goto error;
620   }
621   hyper_clientconn_options_set_preserve_header_case(options, 1);
622   hyper_clientconn_options_set_preserve_header_order(options, 1);
623 
624   hyper_clientconn_options_exec(options, h->exec);
625 
626   /* "Both the `io` and the `options` are consumed in this function
627      call" */
628   handshake = hyper_clientconn_handshake(io, options);
629   if(!handshake) {
630     failf(data, "Couldn't create hyper client handshake");
631     result = CURLE_OUT_OF_MEMORY;
632     goto error;
633   }
634   io = NULL;
635   options = NULL;
636 
637   if(HYPERE_OK != hyper_executor_push(h->exec, handshake)) {
638     failf(data, "Couldn't hyper_executor_push the handshake");
639     result = CURLE_OUT_OF_MEMORY;
640     goto error;
641   }
642   handshake = NULL; /* ownership passed on */
643 
644   task = hyper_executor_poll(h->exec);
645   if(!task) {
646     failf(data, "Couldn't hyper_executor_poll the handshake");
647     result = CURLE_OUT_OF_MEMORY;
648     goto error;
649   }
650 
651   client = hyper_task_value(task);
652   hyper_task_free(task);
653 
654   req = hyper_request_new();
655   if(!req) {
656     failf(data, "Couldn't hyper_request_new");
657     result = CURLE_OUT_OF_MEMORY;
658     goto error;
659   }
660   if(hyper_request_set_method(req, (uint8_t *)"CONNECT",
661                               strlen("CONNECT"))) {
662     failf(data, "error setting method");
663     result = CURLE_OUT_OF_MEMORY;
664     goto error;
665   }
666 
667     /* This only happens if we've looped here due to authentication
668        reasons, and we don't really use the newly cloned URL here
669        then. Just free() it. */
670   Curl_safefree(data->req.newurl);
671 
672   result = CONNECT_host(cf, data, &authority, &host_header);
673   if(result)
674     goto error;
675 
676   infof(data, "Establish HTTP proxy tunnel to %s", authority);
677 
678   if(hyper_request_set_uri(req, (uint8_t *)authority,
679                            strlen(authority))) {
680     failf(data, "error setting path");
681     result = CURLE_OUT_OF_MEMORY;
682     goto error;
683   }
684   if(data->set.verbose) {
685     char *se = aprintf("CONNECT %s HTTP/1.1\r\n", authority);
686     if(!se) {
687       result = CURLE_OUT_OF_MEMORY;
688       goto error;
689     }
690     Curl_debug(data, CURLINFO_HEADER_OUT, se, strlen(se));
691     free(se);
692   }
693   /* Setup the proxy-authorization header, if any */
694   result = Curl_http_output_auth(data, conn, "CONNECT", HTTPREQ_GET,
695                                  authority, TRUE);
696   if(result)
697     goto error;
698   Curl_safefree(authority);
699 
700   /* default is 1.1 */
701   if((conn->http_proxy.proxytype == CURLPROXY_HTTP_1_0) &&
702      (HYPERE_OK != hyper_request_set_version(req,
703                                              HYPER_HTTP_VERSION_1_0))) {
704     failf(data, "error setting HTTP version");
705     result = CURLE_OUT_OF_MEMORY;
706     goto error;
707   }
708 
709   headers = hyper_request_headers(req);
710   if(!headers) {
711     failf(data, "hyper_request_headers");
712     result = CURLE_OUT_OF_MEMORY;
713     goto error;
714   }
715   if(host_header) {
716     result = Curl_hyper_header(data, headers, host_header);
717     if(result)
718       goto error;
719     Curl_safefree(host_header);
720   }
721 
722   if(data->state.aptr.proxyuserpwd) {
723     result = Curl_hyper_header(data, headers,
724                                data->state.aptr.proxyuserpwd);
725     if(result)
726       goto error;
727   }
728 
729   if(!Curl_checkProxyheaders(data, conn, STRCONST("User-Agent")) &&
730      data->set.str[STRING_USERAGENT] && *data->set.str[STRING_USERAGENT]) {
731     struct dynbuf ua;
732     Curl_dyn_init(&ua, DYN_HTTP_REQUEST);
733     result = Curl_dyn_addf(&ua, "User-Agent: %s\r\n",
734                            data->set.str[STRING_USERAGENT]);
735     if(result)
736       goto error;
737     result = Curl_hyper_header(data, headers, Curl_dyn_ptr(&ua));
738     if(result)
739       goto error;
740     Curl_dyn_free(&ua);
741   }
742 
743   if(!Curl_checkProxyheaders(data, conn, STRCONST("Proxy-Connection"))) {
744     result = Curl_hyper_header(data, headers,
745                                "Proxy-Connection: Keep-Alive");
746     if(result)
747       goto error;
748   }
749 
750   result = Curl_add_custom_headers(data, TRUE, headers);
751   if(result)
752     goto error;
753 
754   result = Curl_creader_set_null(data);
755   if(result)
756     goto error;
757 
758   sendtask = hyper_clientconn_send(client, req);
759   if(!sendtask) {
760     failf(data, "hyper_clientconn_send");
761     result = CURLE_OUT_OF_MEMORY;
762     goto error;
763   }
764   req = NULL;
765 
766   if(HYPERE_OK != hyper_executor_push(h->exec, sendtask)) {
767     failf(data, "Couldn't hyper_executor_push the send");
768     result = CURLE_OUT_OF_MEMORY;
769     goto error;
770   }
771   sendtask = NULL; /* ownership passed on */
772 
773   hyper_clientconn_free(client);
774   client = NULL;
775 
776 error:
777   free(host_header);
778   free(authority);
779   if(io)
780     hyper_io_free(io);
781   if(options)
782     hyper_clientconn_options_free(options);
783   if(handshake)
784     hyper_task_free(handshake);
785   if(client)
786     hyper_clientconn_free(client);
787   if(req)
788     hyper_request_free(req);
789 
790   return result;
791 }
792 
send_CONNECT(struct Curl_cfilter * cf,struct Curl_easy * data,struct h1_tunnel_state * ts,bool * done)793 static CURLcode send_CONNECT(struct Curl_cfilter *cf,
794                              struct Curl_easy *data,
795                              struct h1_tunnel_state *ts,
796                              bool *done)
797 {
798   struct hyptransfer *h = &data->hyp;
799   struct connectdata *conn = cf->conn;
800   hyper_task *task = NULL;
801   hyper_error *hypererr = NULL;
802   CURLcode result = CURLE_OK;
803 
804   (void)ts;
805   (void)conn;
806   do {
807     task = hyper_executor_poll(h->exec);
808     if(task) {
809       bool error = hyper_task_type(task) == HYPER_TASK_ERROR;
810       if(error)
811         hypererr = hyper_task_value(task);
812       hyper_task_free(task);
813       if(error) {
814         /* this could probably use a better error code? */
815         result = CURLE_OUT_OF_MEMORY;
816         goto error;
817       }
818     }
819   } while(task);
820 error:
821   *done = (result == CURLE_OK);
822   if(hypererr) {
823     uint8_t errbuf[256];
824     size_t errlen = hyper_error_print(hypererr, errbuf, sizeof(errbuf));
825     failf(data, "Hyper: %.*s", (int)errlen, errbuf);
826     hyper_error_free(hypererr);
827   }
828   return result;
829 }
830 
recv_CONNECT_resp(struct Curl_cfilter * cf,struct Curl_easy * data,struct h1_tunnel_state * ts,bool * done)831 static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf,
832                                   struct Curl_easy *data,
833                                   struct h1_tunnel_state *ts,
834                                   bool *done)
835 {
836   struct hyptransfer *h = &data->hyp;
837   CURLcode result;
838   int didwhat;
839 
840   (void)ts;
841   result = Curl_hyper_stream(data, cf->conn, &didwhat,
842                              CURL_CSELECT_IN | CURL_CSELECT_OUT);
843   *done = data->req.done;
844   if(result || !*done)
845     return result;
846   if(h->exec) {
847     hyper_executor_free(h->exec);
848     h->exec = NULL;
849   }
850   if(h->read_waker) {
851     hyper_waker_free(h->read_waker);
852     h->read_waker = NULL;
853   }
854   if(h->write_waker) {
855     hyper_waker_free(h->write_waker);
856     h->write_waker = NULL;
857   }
858   return result;
859 }
860 
861 #endif /* USE_HYPER */
862 
H1_CONNECT(struct Curl_cfilter * cf,struct Curl_easy * data,struct h1_tunnel_state * ts)863 static CURLcode H1_CONNECT(struct Curl_cfilter *cf,
864                            struct Curl_easy *data,
865                            struct h1_tunnel_state *ts)
866 {
867   struct connectdata *conn = cf->conn;
868   CURLcode result;
869   bool done;
870 
871   if(tunnel_is_established(ts))
872     return CURLE_OK;
873   if(tunnel_is_failed(ts))
874     return CURLE_RECV_ERROR; /* Need a cfilter close and new bootstrap */
875 
876   do {
877     timediff_t check;
878 
879     check = Curl_timeleft(data, NULL, TRUE);
880     if(check <= 0) {
881       failf(data, "Proxy CONNECT aborted due to timeout");
882       result = CURLE_OPERATION_TIMEDOUT;
883       goto out;
884     }
885 
886     switch(ts->tunnel_state) {
887     case H1_TUNNEL_INIT:
888       /* Prepare the CONNECT request and make a first attempt to send. */
889       CURL_TRC_CF(data, cf, "CONNECT start");
890       result = start_CONNECT(cf, data, ts);
891       if(result)
892         goto out;
893       h1_tunnel_go_state(cf, ts, H1_TUNNEL_CONNECT, data);
894       FALLTHROUGH();
895 
896     case H1_TUNNEL_CONNECT:
897       /* see that the request is completely sent */
898       CURL_TRC_CF(data, cf, "CONNECT send");
899       result = send_CONNECT(cf, data, ts, &done);
900       if(result || !done)
901         goto out;
902       h1_tunnel_go_state(cf, ts, H1_TUNNEL_RECEIVE, data);
903       FALLTHROUGH();
904 
905     case H1_TUNNEL_RECEIVE:
906       /* read what is there */
907       CURL_TRC_CF(data, cf, "CONNECT receive");
908       result = recv_CONNECT_resp(cf, data, ts, &done);
909       if(Curl_pgrsUpdate(data)) {
910         result = CURLE_ABORTED_BY_CALLBACK;
911         goto out;
912       }
913       /* error or not complete yet. return for more multi-multi */
914       if(result || !done)
915         goto out;
916       /* got it */
917       h1_tunnel_go_state(cf, ts, H1_TUNNEL_RESPONSE, data);
918       FALLTHROUGH();
919 
920     case H1_TUNNEL_RESPONSE:
921       CURL_TRC_CF(data, cf, "CONNECT response");
922       if(data->req.newurl) {
923         /* not the "final" response, we need to do a follow up request.
924          * If the other side indicated a connection close, or if someone
925          * else told us to close this connection, do so now.
926          */
927         Curl_req_soft_reset(&data->req, data);
928         if(ts->close_connection || conn->bits.close) {
929           /* Close this filter and the sub-chain, re-connect the
930            * sub-chain and continue. Closing this filter will
931            * reset our tunnel state. To avoid recursion, we return
932            * and expect to be called again.
933            */
934           CURL_TRC_CF(data, cf, "CONNECT need to close+open");
935           infof(data, "Connect me again please");
936           Curl_conn_cf_close(cf, data);
937           connkeep(conn, "HTTP proxy CONNECT");
938           result = Curl_conn_cf_connect(cf->next, data, FALSE, &done);
939           goto out;
940         }
941         else {
942           /* staying on this connection, reset state */
943           h1_tunnel_go_state(cf, ts, H1_TUNNEL_INIT, data);
944         }
945       }
946       break;
947 
948     default:
949       break;
950     }
951 
952   } while(data->req.newurl);
953 
954   DEBUGASSERT(ts->tunnel_state == H1_TUNNEL_RESPONSE);
955   if(data->info.httpproxycode/100 != 2) {
956     /* a non-2xx response and we have no next url to try. */
957     Curl_safefree(data->req.newurl);
958     /* failure, close this connection to avoid reuse */
959     streamclose(conn, "proxy CONNECT failure");
960     h1_tunnel_go_state(cf, ts, H1_TUNNEL_FAILED, data);
961     failf(data, "CONNECT tunnel failed, response %d", data->req.httpcode);
962     return CURLE_RECV_ERROR;
963   }
964   /* 2xx response, SUCCESS! */
965   h1_tunnel_go_state(cf, ts, H1_TUNNEL_ESTABLISHED, data);
966   infof(data, "CONNECT tunnel established, response %d",
967         data->info.httpproxycode);
968   result = CURLE_OK;
969 
970 out:
971   if(result)
972     h1_tunnel_go_state(cf, ts, H1_TUNNEL_FAILED, data);
973   return result;
974 }
975 
cf_h1_proxy_connect(struct Curl_cfilter * cf,struct Curl_easy * data,bool blocking,bool * done)976 static CURLcode cf_h1_proxy_connect(struct Curl_cfilter *cf,
977                                     struct Curl_easy *data,
978                                     bool blocking, bool *done)
979 {
980   CURLcode result;
981   struct h1_tunnel_state *ts = cf->ctx;
982 
983   if(cf->connected) {
984     *done = TRUE;
985     return CURLE_OK;
986   }
987 
988   CURL_TRC_CF(data, cf, "connect");
989   result = cf->next->cft->do_connect(cf->next, data, blocking, done);
990   if(result || !*done)
991     return result;
992 
993   *done = FALSE;
994   if(!ts) {
995     result = tunnel_init(cf, data, &ts);
996     if(result)
997       return result;
998     cf->ctx = ts;
999   }
1000 
1001   /* TODO: can we do blocking? */
1002   /* We want "seamless" operations through HTTP proxy tunnel */
1003 
1004   result = H1_CONNECT(cf, data, ts);
1005   if(result)
1006     goto out;
1007   Curl_safefree(data->state.aptr.proxyuserpwd);
1008 
1009 out:
1010   *done = (result == CURLE_OK) && tunnel_is_established(cf->ctx);
1011   if(*done) {
1012     cf->connected = TRUE;
1013     /* The real request will follow the CONNECT, reset request partially */
1014     Curl_req_soft_reset(&data->req, data);
1015     Curl_client_reset(data);
1016     Curl_pgrsSetUploadCounter(data, 0);
1017     Curl_pgrsSetDownloadCounter(data, 0);
1018 
1019     tunnel_free(cf, data);
1020   }
1021   return result;
1022 }
1023 
cf_h1_proxy_adjust_pollset(struct Curl_cfilter * cf,struct Curl_easy * data,struct easy_pollset * ps)1024 static void cf_h1_proxy_adjust_pollset(struct Curl_cfilter *cf,
1025                                         struct Curl_easy *data,
1026                                         struct easy_pollset *ps)
1027 {
1028   struct h1_tunnel_state *ts = cf->ctx;
1029 
1030   if(!cf->connected) {
1031     /* If we are not connected, but the filter "below" is
1032      * and not waiting on something, we are tunneling. */
1033     curl_socket_t sock = Curl_conn_cf_get_socket(cf, data);
1034     if(ts) {
1035       /* when we've sent a CONNECT to a proxy, we should rather either
1036          wait for the socket to become readable to be able to get the
1037          response headers or if we're still sending the request, wait
1038          for write. */
1039       if(tunnel_want_send(ts))
1040         Curl_pollset_set_out_only(data, ps, sock);
1041       else
1042         Curl_pollset_set_in_only(data, ps, sock);
1043     }
1044     else
1045       Curl_pollset_set_out_only(data, ps, sock);
1046   }
1047 }
1048 
cf_h1_proxy_destroy(struct Curl_cfilter * cf,struct Curl_easy * data)1049 static void cf_h1_proxy_destroy(struct Curl_cfilter *cf,
1050                                 struct Curl_easy *data)
1051 {
1052   CURL_TRC_CF(data, cf, "destroy");
1053   tunnel_free(cf, data);
1054 }
1055 
cf_h1_proxy_close(struct Curl_cfilter * cf,struct Curl_easy * data)1056 static void cf_h1_proxy_close(struct Curl_cfilter *cf,
1057                               struct Curl_easy *data)
1058 {
1059   CURL_TRC_CF(data, cf, "close");
1060   cf->connected = FALSE;
1061   if(cf->ctx) {
1062     h1_tunnel_go_state(cf, cf->ctx, H1_TUNNEL_INIT, data);
1063   }
1064   if(cf->next)
1065     cf->next->cft->do_close(cf->next, data);
1066 }
1067 
1068 
1069 struct Curl_cftype Curl_cft_h1_proxy = {
1070   "H1-PROXY",
1071   CF_TYPE_IP_CONNECT|CF_TYPE_PROXY,
1072   0,
1073   cf_h1_proxy_destroy,
1074   cf_h1_proxy_connect,
1075   cf_h1_proxy_close,
1076   Curl_cf_http_proxy_get_host,
1077   cf_h1_proxy_adjust_pollset,
1078   Curl_cf_def_data_pending,
1079   Curl_cf_def_send,
1080   Curl_cf_def_recv,
1081   Curl_cf_def_cntrl,
1082   Curl_cf_def_conn_is_alive,
1083   Curl_cf_def_conn_keep_alive,
1084   Curl_cf_def_query,
1085 };
1086 
Curl_cf_h1_proxy_insert_after(struct Curl_cfilter * cf_at,struct Curl_easy * data)1087 CURLcode Curl_cf_h1_proxy_insert_after(struct Curl_cfilter *cf_at,
1088                                        struct Curl_easy *data)
1089 {
1090   struct Curl_cfilter *cf;
1091   CURLcode result;
1092 
1093   (void)data;
1094   result = Curl_cf_create(&cf, &Curl_cft_h1_proxy, NULL);
1095   if(!result)
1096     Curl_conn_cf_insert_after(cf_at, cf);
1097   return result;
1098 }
1099 
1100 #endif /* !CURL_DISABLE_PROXY && ! CURL_DISABLE_HTTP */
1101