VirtualBox

source: vbox/trunk/src/libs/curl-7.87.0/lib/http_proxy.c@ 98326

Last change on this file since 98326 was 98326, checked in by vboxsync, 2 years ago

curl-7.87.0: Applied and adjusted our curl changes to 7.83.1. bugref:10356

  • Property svn:eol-style set to native
File size: 36.9 KB
Line 
1/***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 1998 - 2022, Daniel Stenberg, <[email protected]>, 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#include "http_proxy.h"
28
29#if !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP)
30
31#include <curl/curl.h>
32#ifdef USE_HYPER
33#include <hyper.h>
34#endif
35#include "sendf.h"
36#include "http.h"
37#include "url.h"
38#include "select.h"
39#include "progress.h"
40#include "cfilters.h"
41#include "connect.h"
42#include "curlx.h"
43#include "vtls/vtls.h"
44#include "transfer.h"
45#include "multiif.h"
46
47/* The last 3 #include files should be in this order */
48#include "curl_printf.h"
49#include "curl_memory.h"
50#include "memdebug.h"
51
52typedef enum {
53 TUNNEL_INIT, /* init/default/no tunnel state */
54 TUNNEL_CONNECT, /* CONNECT request is being send */
55 TUNNEL_RECEIVE, /* CONNECT answer is being received */
56 TUNNEL_RESPONSE, /* CONNECT response received completely */
57 TUNNEL_ESTABLISHED,
58 TUNNEL_FAILED
59} tunnel_state;
60
61/* struct for HTTP CONNECT tunneling */
62struct tunnel_state {
63 int sockindex;
64 const char *hostname;
65 int remote_port;
66 struct HTTP http_proxy;
67 struct HTTP *prot_save;
68 struct dynbuf rcvbuf;
69 struct dynbuf req;
70 size_t nsend;
71 size_t headerlines;
72 enum keeponval {
73 KEEPON_DONE,
74 KEEPON_CONNECT,
75 KEEPON_IGNORE
76 } keepon;
77 curl_off_t cl; /* size of content to read and ignore */
78 tunnel_state tunnel_state;
79 BIT(chunked_encoding);
80 BIT(close_connection);
81};
82
83
84static bool tunnel_is_established(struct tunnel_state *ts)
85{
86 return ts && (ts->tunnel_state == TUNNEL_ESTABLISHED);
87}
88
89static bool tunnel_is_failed(struct tunnel_state *ts)
90{
91 return ts && (ts->tunnel_state == TUNNEL_FAILED);
92}
93
94static CURLcode tunnel_reinit(struct tunnel_state *ts,
95 struct connectdata *conn,
96 struct Curl_easy *data)
97{
98 (void)data;
99 DEBUGASSERT(ts);
100 Curl_dyn_reset(&ts->rcvbuf);
101 Curl_dyn_reset(&ts->req);
102 ts->tunnel_state = TUNNEL_INIT;
103 ts->keepon = KEEPON_CONNECT;
104 ts->cl = 0;
105 ts->close_connection = FALSE;
106
107 if(conn->bits.conn_to_host)
108 ts->hostname = conn->conn_to_host.name;
109 else if(ts->sockindex == SECONDARYSOCKET)
110 ts->hostname = conn->secondaryhostname;
111 else
112 ts->hostname = conn->host.name;
113
114 if(ts->sockindex == SECONDARYSOCKET)
115 ts->remote_port = conn->secondary_port;
116 else if(conn->bits.conn_to_port)
117 ts->remote_port = conn->conn_to_port;
118 else
119 ts->remote_port = conn->remote_port;
120
121 return CURLE_OK;
122}
123
124static CURLcode tunnel_init(struct tunnel_state **pts,
125 struct Curl_easy *data,
126 struct connectdata *conn,
127 int sockindex)
128{
129 struct tunnel_state *ts;
130 CURLcode result;
131
132 if(conn->handler->flags & PROTOPT_NOTCPPROXY) {
133 failf(data, "%s cannot be done over CONNECT", conn->handler->scheme);
134 return CURLE_UNSUPPORTED_PROTOCOL;
135 }
136
137 /* we might need the upload buffer for streaming a partial request */
138 result = Curl_get_upload_buffer(data);
139 if(result)
140 return result;
141
142 ts = calloc(1, sizeof(*ts));
143 if(!ts)
144 return CURLE_OUT_OF_MEMORY;
145
146 ts->sockindex = sockindex;
147 infof(data, "allocate connect buffer");
148
149 Curl_dyn_init(&ts->rcvbuf, DYN_PROXY_CONNECT_HEADERS);
150 Curl_dyn_init(&ts->req, DYN_HTTP_REQUEST);
151
152 /* Curl_proxyCONNECT is based on a pointer to a struct HTTP at the
153 * member conn->proto.http; we want [protocol] through HTTP and we have
154 * to change the member temporarily for connecting to the HTTP
155 * proxy. After Curl_proxyCONNECT we have to set back the member to the
156 * original pointer
157 *
158 * This function might be called several times in the multi interface case
159 * if the proxy's CONNECT response is not instant.
160 */
161 ts->prot_save = data->req.p.http;
162 data->req.p.http = &ts->http_proxy;
163 *pts = ts;
164 connkeep(conn, "HTTP proxy CONNECT");
165 return tunnel_reinit(ts, conn, data);
166}
167
168static void tunnel_go_state(struct Curl_cfilter *cf,
169 struct tunnel_state *ts,
170 tunnel_state new_state,
171 struct Curl_easy *data)
172{
173 if(ts->tunnel_state == new_state)
174 return;
175 /* leaving this one */
176 switch(ts->tunnel_state) {
177 case TUNNEL_CONNECT:
178 data->req.ignorebody = FALSE;
179 break;
180 default:
181 break;
182 }
183 /* entering this one */
184 switch(new_state) {
185 case TUNNEL_INIT:
186 tunnel_reinit(ts, cf->conn, data);
187 break;
188
189 case TUNNEL_CONNECT:
190 ts->tunnel_state = TUNNEL_CONNECT;
191 ts->keepon = KEEPON_CONNECT;
192 Curl_dyn_reset(&ts->rcvbuf);
193 break;
194
195 case TUNNEL_RECEIVE:
196 ts->tunnel_state = TUNNEL_RECEIVE;
197 break;
198
199 case TUNNEL_RESPONSE:
200 ts->tunnel_state = TUNNEL_RESPONSE;
201 break;
202
203 case TUNNEL_ESTABLISHED:
204 infof(data, "CONNECT phase completed");
205 data->state.authproxy.done = TRUE;
206 data->state.authproxy.multipass = FALSE;
207 /* FALLTHROUGH */
208 case TUNNEL_FAILED:
209 ts->tunnel_state = new_state;
210 Curl_dyn_reset(&ts->rcvbuf);
211 Curl_dyn_reset(&ts->req);
212 /* restore the protocol pointer */
213 data->req.p.http = ts->prot_save;
214 data->info.httpcode = 0; /* clear it as it might've been used for the
215 proxy */
216 /* If a proxy-authorization header was used for the proxy, then we should
217 make sure that it isn't accidentally used for the document request
218 after we've connected. So let's free and clear it here. */
219 Curl_safefree(data->state.aptr.proxyuserpwd);
220 data->state.aptr.proxyuserpwd = NULL;
221#ifdef USE_HYPER
222 data->state.hconnect = FALSE;
223#endif
224 break;
225 }
226}
227
228static void tunnel_free(struct Curl_cfilter *cf,
229 struct Curl_easy *data)
230{
231 struct tunnel_state *ts = cf->ctx;
232 if(ts) {
233 tunnel_go_state(cf, ts, TUNNEL_FAILED, data);
234 Curl_dyn_free(&ts->rcvbuf);
235 Curl_dyn_free(&ts->req);
236 free(ts);
237 cf->ctx = NULL;
238 }
239}
240
241static CURLcode CONNECT_host(struct Curl_easy *data,
242 struct connectdata *conn,
243 const char *hostname,
244 int remote_port,
245 char **connecthostp,
246 char **hostp)
247{
248 char *hostheader; /* for CONNECT */
249 char *host = NULL; /* Host: */
250 bool ipv6_ip = conn->bits.ipv6_ip;
251
252 /* the hostname may be different */
253 if(hostname != conn->host.name)
254 ipv6_ip = (strchr(hostname, ':') != NULL);
255 hostheader = /* host:port with IPv6 support */
256 aprintf("%s%s%s:%d", ipv6_ip?"[":"", hostname, ipv6_ip?"]":"",
257 remote_port);
258 if(!hostheader)
259 return CURLE_OUT_OF_MEMORY;
260
261 if(!Curl_checkProxyheaders(data, conn, STRCONST("Host"))) {
262 host = aprintf("Host: %s\r\n", hostheader);
263 if(!host) {
264 free(hostheader);
265 return CURLE_OUT_OF_MEMORY;
266 }
267 }
268 *connecthostp = hostheader;
269 *hostp = host;
270 return CURLE_OK;
271}
272
273#ifndef USE_HYPER
274static CURLcode start_CONNECT(struct Curl_easy *data,
275 struct connectdata *conn,
276 struct tunnel_state *ts)
277{
278 char *hostheader = NULL;
279 char *host = NULL;
280 const char *httpv;
281 CURLcode result;
282
283 infof(data, "Establish HTTP proxy tunnel to %s:%d",
284 ts->hostname, ts->remote_port);
285
286 /* This only happens if we've looped here due to authentication
287 reasons, and we don't really use the newly cloned URL here
288 then. Just free() it. */
289 Curl_safefree(data->req.newurl);
290
291 result = CONNECT_host(data, conn,
292 ts->hostname, ts->remote_port,
293 &hostheader, &host);
294 if(result)
295 goto out;
296
297 /* Setup the proxy-authorization header, if any */
298 result = Curl_http_output_auth(data, conn, "CONNECT", HTTPREQ_GET,
299 hostheader, TRUE);
300 if(result)
301 goto out;
302
303 httpv = (conn->http_proxy.proxytype == CURLPROXY_HTTP_1_0) ? "1.0" : "1.1";
304
305 result =
306 Curl_dyn_addf(&ts->req,
307 "CONNECT %s HTTP/%s\r\n"
308 "%s" /* Host: */
309 "%s", /* Proxy-Authorization */
310 hostheader,
311 httpv,
312 host?host:"",
313 data->state.aptr.proxyuserpwd?
314 data->state.aptr.proxyuserpwd:"");
315 if(result)
316 goto out;
317
318 if(!Curl_checkProxyheaders(data, conn, STRCONST("User-Agent"))
319 && data->set.str[STRING_USERAGENT])
320 result = Curl_dyn_addf(&ts->req, "User-Agent: %s\r\n",
321 data->set.str[STRING_USERAGENT]);
322 if(result)
323 goto out;
324
325 if(!Curl_checkProxyheaders(data, conn, STRCONST("Proxy-Connection")))
326 result = Curl_dyn_addn(&ts->req,
327 STRCONST("Proxy-Connection: Keep-Alive\r\n"));
328 if(result)
329 goto out;
330
331 result = Curl_add_custom_headers(data, TRUE, &ts->req);
332 if(result)
333 goto out;
334
335 /* CRLF terminate the request */
336 result = Curl_dyn_addn(&ts->req, STRCONST("\r\n"));
337 if(result)
338 goto out;
339
340 /* Send the connect request to the proxy */
341 result = Curl_buffer_send(&ts->req, data, &data->info.request_size, 0,
342 ts->sockindex);
343 ts->headerlines = 0;
344
345out:
346 if(result)
347 failf(data, "Failed sending CONNECT to proxy");
348 free(host);
349 free(hostheader);
350 return result;
351}
352
353static CURLcode send_CONNECT(struct Curl_easy *data,
354 struct connectdata *conn,
355 struct tunnel_state *ts,
356 bool *done)
357{
358 struct SingleRequest *k = &data->req;
359 struct HTTP *http = data->req.p.http;
360 CURLcode result = CURLE_OK;
361
362 if(http->sending != HTTPSEND_REQUEST)
363 goto out;
364
365 if(!ts->nsend) {
366 size_t fillcount;
367 k->upload_fromhere = data->state.ulbuf;
368 result = Curl_fillreadbuffer(data, data->set.upload_buffer_size,
369 &fillcount);
370 if(result)
371 goto out;
372 ts->nsend = fillcount;
373 }
374 if(ts->nsend) {
375 ssize_t bytes_written;
376 /* write to socket (send away data) */
377 result = Curl_write(data,
378 conn->writesockfd, /* socket to send to */
379 k->upload_fromhere, /* buffer pointer */
380 ts->nsend, /* buffer size */
381 &bytes_written); /* actually sent */
382 if(result)
383 goto out;
384 /* send to debug callback! */
385 Curl_debug(data, CURLINFO_HEADER_OUT,
386 k->upload_fromhere, bytes_written);
387
388 ts->nsend -= bytes_written;
389 k->upload_fromhere += bytes_written;
390 }
391 if(!ts->nsend)
392 http->sending = HTTPSEND_NADA;
393
394out:
395 if(result)
396 failf(data, "Failed sending CONNECT to proxy");
397 *done = (http->sending != HTTPSEND_REQUEST);
398 return result;
399}
400
401static CURLcode on_resp_header(struct Curl_easy *data,
402 struct tunnel_state *ts,
403 const char *header)
404{
405 CURLcode result = CURLE_OK;
406 struct SingleRequest *k = &data->req;
407 int subversion = 0;
408
409 if((checkprefix("WWW-Authenticate:", header) &&
410 (401 == k->httpcode)) ||
411 (checkprefix("Proxy-authenticate:", header) &&
412 (407 == k->httpcode))) {
413
414 bool proxy = (k->httpcode == 407) ? TRUE : FALSE;
415 char *auth = Curl_copy_header_value(header);
416 if(!auth)
417 return CURLE_OUT_OF_MEMORY;
418
419 DEBUGF(infof(data, "CONNECT: fwd auth header '%s'",
420 header));
421 result = Curl_http_input_auth(data, proxy, auth);
422
423 free(auth);
424
425 if(result)
426 return result;
427 }
428 else if(checkprefix("Content-Length:", header)) {
429 if(k->httpcode/100 == 2) {
430 /* A client MUST ignore any Content-Length or Transfer-Encoding
431 header fields received in a successful response to CONNECT.
432 "Successful" described as: 2xx (Successful). RFC 7231 4.3.6 */
433 infof(data, "Ignoring Content-Length in CONNECT %03d response",
434 k->httpcode);
435 }
436 else {
437 (void)curlx_strtoofft(header + strlen("Content-Length:"),
438 NULL, 10, &ts->cl);
439 }
440 }
441 else if(Curl_compareheader(header,
442 STRCONST("Connection:"), STRCONST("close")))
443 ts->close_connection = TRUE;
444 else if(checkprefix("Transfer-Encoding:", header)) {
445 if(k->httpcode/100 == 2) {
446 /* A client MUST ignore any Content-Length or Transfer-Encoding
447 header fields received in a successful response to CONNECT.
448 "Successful" described as: 2xx (Successful). RFC 7231 4.3.6 */
449 infof(data, "Ignoring Transfer-Encoding in "
450 "CONNECT %03d response", k->httpcode);
451 }
452 else if(Curl_compareheader(header,
453 STRCONST("Transfer-Encoding:"),
454 STRCONST("chunked"))) {
455 infof(data, "CONNECT responded chunked");
456 ts->chunked_encoding = TRUE;
457 /* init our chunky engine */
458 Curl_httpchunk_init(data);
459 }
460 }
461 else if(Curl_compareheader(header,
462 STRCONST("Proxy-Connection:"),
463 STRCONST("close")))
464 ts->close_connection = TRUE;
465 else if(2 == sscanf(header, "HTTP/1.%d %d",
466 &subversion,
467 &k->httpcode)) {
468 /* store the HTTP code from the proxy */
469 data->info.httpproxycode = k->httpcode;
470 }
471 return result;
472}
473
474static CURLcode recv_CONNECT_resp(struct Curl_easy *data,
475 struct connectdata *conn,
476 struct tunnel_state *ts,
477 bool *done)
478{
479 CURLcode result = CURLE_OK;
480 struct SingleRequest *k = &data->req;
481 curl_socket_t tunnelsocket = conn->sock[ts->sockindex];
482 char *linep;
483 size_t perline;
484 int error;
485
486#define SELECT_OK 0
487#define SELECT_ERROR 1
488
489 error = SELECT_OK;
490 *done = FALSE;
491
492 if(!Curl_conn_data_pending(data, ts->sockindex))
493 return CURLE_OK;
494
495 while(ts->keepon) {
496 ssize_t gotbytes;
497 char byte;
498
499 /* Read one byte at a time to avoid a race condition. Wait at most one
500 second before looping to ensure continuous pgrsUpdates. */
501 result = Curl_read(data, tunnelsocket, &byte, 1, &gotbytes);
502 if(result == CURLE_AGAIN)
503 /* socket buffer drained, return */
504 return CURLE_OK;
505
506 if(Curl_pgrsUpdate(data))
507 return CURLE_ABORTED_BY_CALLBACK;
508
509 if(result) {
510 ts->keepon = KEEPON_DONE;
511 break;
512 }
513
514 if(gotbytes <= 0) {
515 if(data->set.proxyauth && data->state.authproxy.avail &&
516 data->state.aptr.proxyuserpwd) {
517 /* proxy auth was requested and there was proxy auth available,
518 then deem this as "mere" proxy disconnect */
519 ts->close_connection = TRUE;
520 infof(data, "Proxy CONNECT connection closed");
521 }
522 else {
523 error = SELECT_ERROR;
524 failf(data, "Proxy CONNECT aborted");
525 }
526 ts->keepon = KEEPON_DONE;
527 break;
528 }
529
530 if(ts->keepon == KEEPON_IGNORE) {
531 /* This means we are currently ignoring a response-body */
532
533 if(ts->cl) {
534 /* A Content-Length based body: simply count down the counter
535 and make sure to break out of the loop when we're done! */
536 ts->cl--;
537 if(ts->cl <= 0) {
538 ts->keepon = KEEPON_DONE;
539 break;
540 }
541 }
542 else {
543 /* chunked-encoded body, so we need to do the chunked dance
544 properly to know when the end of the body is reached */
545 CHUNKcode r;
546 CURLcode extra;
547 ssize_t tookcareof = 0;
548
549 /* now parse the chunked piece of data so that we can
550 properly tell when the stream ends */
551 r = Curl_httpchunk_read(data, &byte, 1, &tookcareof, &extra);
552 if(r == CHUNKE_STOP) {
553 /* we're done reading chunks! */
554 infof(data, "chunk reading DONE");
555 ts->keepon = KEEPON_DONE;
556 }
557 }
558 continue;
559 }
560
561 if(Curl_dyn_addn(&ts->rcvbuf, &byte, 1)) {
562 failf(data, "CONNECT response too large");
563 return CURLE_RECV_ERROR;
564 }
565
566 /* if this is not the end of a header line then continue */
567 if(byte != 0x0a)
568 continue;
569
570 ts->headerlines++;
571 linep = Curl_dyn_ptr(&ts->rcvbuf);
572 perline = Curl_dyn_len(&ts->rcvbuf); /* amount of bytes in this line */
573
574 /* output debug if that is requested */
575 Curl_debug(data, CURLINFO_HEADER_IN, linep, perline);
576
577 if(!data->set.suppress_connect_headers) {
578 /* send the header to the callback */
579 int writetype = CLIENTWRITE_HEADER | CLIENTWRITE_CONNECT |
580 (data->set.include_header ? CLIENTWRITE_BODY : 0) |
581 (ts->headerlines == 1 ? CLIENTWRITE_STATUS : 0);
582
583 result = Curl_client_write(data, writetype, linep, perline);
584 if(result)
585 return result;
586 }
587
588 data->info.header_size += (long)perline;
589
590 /* Newlines are CRLF, so the CR is ignored as the line isn't
591 really terminated until the LF comes. Treat a following CR
592 as end-of-headers as well.*/
593
594 if(('\r' == linep[0]) ||
595 ('\n' == linep[0])) {
596 /* end of response-headers from the proxy */
597
598 if((407 == k->httpcode) && !data->state.authproblem) {
599 /* If we get a 407 response code with content length
600 when we have no auth problem, we must ignore the
601 whole response-body */
602 ts->keepon = KEEPON_IGNORE;
603
604 if(ts->cl) {
605 infof(data, "Ignore %" CURL_FORMAT_CURL_OFF_T
606 " bytes of response-body", ts->cl);
607 }
608 else if(ts->chunked_encoding) {
609 CHUNKcode r;
610 CURLcode extra;
611
612 infof(data, "Ignore chunked response-body");
613
614 /* We set ignorebody true here since the chunked decoder
615 function will acknowledge that. Pay attention so that this is
616 cleared again when this function returns! */
617 k->ignorebody = TRUE;
618
619 if(linep[1] == '\n')
620 /* this can only be a LF if the letter at index 0 was a CR */
621 linep++;
622
623 /* now parse the chunked piece of data so that we can properly
624 tell when the stream ends */
625 r = Curl_httpchunk_read(data, linep + 1, 1, &gotbytes,
626 &extra);
627 if(r == CHUNKE_STOP) {
628 /* we're done reading chunks! */
629 infof(data, "chunk reading DONE");
630 ts->keepon = KEEPON_DONE;
631 }
632 }
633 else {
634 /* without content-length or chunked encoding, we
635 can't keep the connection alive since the close is
636 the end signal so we bail out at once instead */
637 DEBUGF(infof(data, "CONNECT: no content-length or chunked"));
638 ts->keepon = KEEPON_DONE;
639 }
640 }
641 else {
642 ts->keepon = KEEPON_DONE;
643 }
644
645 DEBUGASSERT(ts->keepon == KEEPON_IGNORE
646 || ts->keepon == KEEPON_DONE);
647 continue;
648 }
649
650 result = on_resp_header(data, ts, linep);
651 if(result)
652 return result;
653
654 Curl_dyn_reset(&ts->rcvbuf);
655 } /* while there's buffer left and loop is requested */
656
657 if(error)
658 result = CURLE_RECV_ERROR;
659 *done = (ts->keepon == KEEPON_DONE);
660 if(!result && *done && data->info.httpproxycode/100 != 2) {
661 /* Deal with the possibly already received authenticate
662 headers. 'newurl' is set to a new URL if we must loop. */
663 result = Curl_http_auth_act(data);
664 }
665 return result;
666}
667
668#else /* USE_HYPER */
669/* The Hyper version of CONNECT */
670static CURLcode start_CONNECT(struct Curl_easy *data,
671 struct connectdata *conn,
672 struct tunnel_state *ts)
673{
674 struct hyptransfer *h = &data->hyp;
675 curl_socket_t tunnelsocket = conn->sock[ts->sockindex];
676 hyper_io *io = NULL;
677 hyper_request *req = NULL;
678 hyper_headers *headers = NULL;
679 hyper_clientconn_options *options = NULL;
680 hyper_task *handshake = NULL;
681 hyper_task *task = NULL; /* for the handshake */
682 hyper_clientconn *client = NULL;
683 hyper_task *sendtask = NULL; /* for the send */
684 char *hostheader = NULL; /* for CONNECT */
685 char *host = NULL; /* Host: */
686 CURLcode result = CURLE_OUT_OF_MEMORY;
687
688 io = hyper_io_new();
689 if(!io) {
690 failf(data, "Couldn't create hyper IO");
691 result = CURLE_OUT_OF_MEMORY;
692 goto error;
693 }
694 /* tell Hyper how to read/write network data */
695 hyper_io_set_userdata(io, data);
696 hyper_io_set_read(io, Curl_hyper_recv);
697 hyper_io_set_write(io, Curl_hyper_send);
698 conn->sockfd = tunnelsocket;
699
700 data->state.hconnect = TRUE;
701
702 /* create an executor to poll futures */
703 if(!h->exec) {
704 h->exec = hyper_executor_new();
705 if(!h->exec) {
706 failf(data, "Couldn't create hyper executor");
707 result = CURLE_OUT_OF_MEMORY;
708 goto error;
709 }
710 }
711
712 options = hyper_clientconn_options_new();
713 hyper_clientconn_options_set_preserve_header_case(options, 1);
714 hyper_clientconn_options_set_preserve_header_order(options, 1);
715
716 if(!options) {
717 failf(data, "Couldn't create hyper client options");
718 result = CURLE_OUT_OF_MEMORY;
719 goto error;
720 }
721
722 hyper_clientconn_options_exec(options, h->exec);
723
724 /* "Both the `io` and the `options` are consumed in this function
725 call" */
726 handshake = hyper_clientconn_handshake(io, options);
727 if(!handshake) {
728 failf(data, "Couldn't create hyper client handshake");
729 result = CURLE_OUT_OF_MEMORY;
730 goto error;
731 }
732 io = NULL;
733 options = NULL;
734
735 if(HYPERE_OK != hyper_executor_push(h->exec, handshake)) {
736 failf(data, "Couldn't hyper_executor_push the handshake");
737 result = CURLE_OUT_OF_MEMORY;
738 goto error;
739 }
740 handshake = NULL; /* ownership passed on */
741
742 task = hyper_executor_poll(h->exec);
743 if(!task) {
744 failf(data, "Couldn't hyper_executor_poll the handshake");
745 result = CURLE_OUT_OF_MEMORY;
746 goto error;
747 }
748
749 client = hyper_task_value(task);
750 hyper_task_free(task);
751 req = hyper_request_new();
752 if(!req) {
753 failf(data, "Couldn't hyper_request_new");
754 result = CURLE_OUT_OF_MEMORY;
755 goto error;
756 }
757 if(hyper_request_set_method(req, (uint8_t *)"CONNECT",
758 strlen("CONNECT"))) {
759 failf(data, "error setting method");
760 result = CURLE_OUT_OF_MEMORY;
761 goto error;
762 }
763
764 infof(data, "Establish HTTP proxy tunnel to %s:%d",
765 ts->hostname, ts->remote_port);
766
767 /* This only happens if we've looped here due to authentication
768 reasons, and we don't really use the newly cloned URL here
769 then. Just free() it. */
770 Curl_safefree(data->req.newurl);
771
772 result = CONNECT_host(data, conn, ts->hostname, ts->remote_port,
773 &hostheader, &host);
774 if(result)
775 goto error;
776
777 if(hyper_request_set_uri(req, (uint8_t *)hostheader,
778 strlen(hostheader))) {
779 failf(data, "error setting path");
780 result = CURLE_OUT_OF_MEMORY;
781 goto error;
782 }
783 if(data->set.verbose) {
784 char *se = aprintf("CONNECT %s HTTP/1.1\r\n", hostheader);
785 if(!se) {
786 result = CURLE_OUT_OF_MEMORY;
787 goto error;
788 }
789 Curl_debug(data, CURLINFO_HEADER_OUT, se, strlen(se));
790 free(se);
791 }
792 /* Setup the proxy-authorization header, if any */
793 result = Curl_http_output_auth(data, conn, "CONNECT", HTTPREQ_GET,
794 hostheader, TRUE);
795 if(result)
796 goto error;
797 Curl_safefree(hostheader);
798
799 /* default is 1.1 */
800 if((conn->http_proxy.proxytype == CURLPROXY_HTTP_1_0) &&
801 (HYPERE_OK != hyper_request_set_version(req,
802 HYPER_HTTP_VERSION_1_0))) {
803 failf(data, "error setting HTTP version");
804 result = CURLE_OUT_OF_MEMORY;
805 goto error;
806 }
807
808 headers = hyper_request_headers(req);
809 if(!headers) {
810 failf(data, "hyper_request_headers");
811 result = CURLE_OUT_OF_MEMORY;
812 goto error;
813 }
814 if(host) {
815 result = Curl_hyper_header(data, headers, host);
816 if(result)
817 goto error;
818 Curl_safefree(host);
819 }
820
821 if(data->state.aptr.proxyuserpwd) {
822 result = Curl_hyper_header(data, headers,
823 data->state.aptr.proxyuserpwd);
824 if(result)
825 goto error;
826 }
827
828 if(!Curl_checkProxyheaders(data, conn, STRCONST("User-Agent")) &&
829 data->set.str[STRING_USERAGENT]) {
830 struct dynbuf ua;
831 Curl_dyn_init(&ua, DYN_HTTP_REQUEST);
832 result = Curl_dyn_addf(&ua, "User-Agent: %s\r\n",
833 data->set.str[STRING_USERAGENT]);
834 if(result)
835 goto error;
836 result = Curl_hyper_header(data, headers, Curl_dyn_ptr(&ua));
837 if(result)
838 goto error;
839 Curl_dyn_free(&ua);
840 }
841
842 if(!Curl_checkProxyheaders(data, conn, STRCONST("Proxy-Connection"))) {
843 result = Curl_hyper_header(data, headers,
844 "Proxy-Connection: Keep-Alive");
845 if(result)
846 goto error;
847 }
848
849 result = Curl_add_custom_headers(data, TRUE, headers);
850 if(result)
851 goto error;
852
853 sendtask = hyper_clientconn_send(client, req);
854 if(!sendtask) {
855 failf(data, "hyper_clientconn_send");
856 result = CURLE_OUT_OF_MEMORY;
857 goto error;
858 }
859
860 if(HYPERE_OK != hyper_executor_push(h->exec, sendtask)) {
861 failf(data, "Couldn't hyper_executor_push the send");
862 result = CURLE_OUT_OF_MEMORY;
863 goto error;
864 }
865
866error:
867 free(host);
868 free(hostheader);
869 if(io)
870 hyper_io_free(io);
871 if(options)
872 hyper_clientconn_options_free(options);
873 if(handshake)
874 hyper_task_free(handshake);
875 if(client)
876 hyper_clientconn_free(client);
877 return result;
878}
879
880static CURLcode send_CONNECT(struct Curl_easy *data,
881 struct connectdata *conn,
882 struct tunnel_state *ts,
883 bool *done)
884{
885 struct hyptransfer *h = &data->hyp;
886 hyper_task *task = NULL;
887 hyper_error *hypererr = NULL;
888 CURLcode result = CURLE_OK;
889
890 (void)ts;
891 (void)conn;
892 do {
893 task = hyper_executor_poll(h->exec);
894 if(task) {
895 bool error = hyper_task_type(task) == HYPER_TASK_ERROR;
896 if(error)
897 hypererr = hyper_task_value(task);
898 hyper_task_free(task);
899 if(error) {
900 /* this could probably use a better error code? */
901 result = CURLE_OUT_OF_MEMORY;
902 goto error;
903 }
904 }
905 } while(task);
906error:
907 *done = (result == CURLE_OK);
908 if(hypererr) {
909 uint8_t errbuf[256];
910 size_t errlen = hyper_error_print(hypererr, errbuf, sizeof(errbuf));
911 failf(data, "Hyper: %.*s", (int)errlen, errbuf);
912 hyper_error_free(hypererr);
913 }
914 return result;
915}
916
917static CURLcode recv_CONNECT_resp(struct Curl_easy *data,
918 struct connectdata *conn,
919 struct tunnel_state *ts,
920 bool *done)
921{
922 struct hyptransfer *h = &data->hyp;
923 CURLcode result;
924 int didwhat;
925
926 (void)ts;
927 *done = FALSE;
928 result = Curl_hyper_stream(data, conn, &didwhat, done,
929 CURL_CSELECT_IN | CURL_CSELECT_OUT);
930 if(result || !*done)
931 return result;
932 if(h->exec) {
933 hyper_executor_free(h->exec);
934 h->exec = NULL;
935 }
936 if(h->read_waker) {
937 hyper_waker_free(h->read_waker);
938 h->read_waker = NULL;
939 }
940 if(h->write_waker) {
941 hyper_waker_free(h->write_waker);
942 h->write_waker = NULL;
943 }
944 return result;
945}
946
947#endif /* USE_HYPER */
948
949static CURLcode CONNECT(struct Curl_cfilter *cf,
950 struct Curl_easy *data,
951 struct tunnel_state *ts)
952{
953 struct connectdata *conn = cf->conn;
954 CURLcode result;
955 bool done;
956
957 if(tunnel_is_established(ts))
958 return CURLE_OK;
959 if(tunnel_is_failed(ts))
960 return CURLE_RECV_ERROR; /* Need a cfilter close and new bootstrap */
961
962 do {
963 timediff_t check;
964
965 check = Curl_timeleft(data, NULL, TRUE);
966 if(check <= 0) {
967 failf(data, "Proxy CONNECT aborted due to timeout");
968 result = CURLE_OPERATION_TIMEDOUT;
969 goto out;
970 }
971
972 switch(ts->tunnel_state) {
973 case TUNNEL_INIT:
974 /* Prepare the CONNECT request and make a first attempt to send. */
975 result = start_CONNECT(data, cf->conn, ts);
976 if(result)
977 goto out;
978 tunnel_go_state(cf, ts, TUNNEL_CONNECT, data);
979 /* FALLTHROUGH */
980
981 case TUNNEL_CONNECT:
982 /* see that the request is completely sent */
983 result = send_CONNECT(data, cf->conn, ts, &done);
984 if(result || !done)
985 goto out;
986 tunnel_go_state(cf, ts, TUNNEL_RECEIVE, data);
987 /* FALLTHROUGH */
988
989 case TUNNEL_RECEIVE:
990 /* read what is there */
991 result = recv_CONNECT_resp(data, cf->conn, ts, &done);
992 if(Curl_pgrsUpdate(data)) {
993 result = CURLE_ABORTED_BY_CALLBACK;
994 goto out;
995 }
996 /* error or not complete yet. return for more multi-multi */
997 if(result || !done)
998 goto out;
999 /* got it */
1000 tunnel_go_state(cf, ts, TUNNEL_RESPONSE, data);
1001 /* FALLTHROUGH */
1002
1003 case TUNNEL_RESPONSE:
1004 if(data->req.newurl) {
1005 /* not the "final" response, we need to do a follow up request.
1006 * If the other side indicated a connection close, or if someone
1007 * else told us to close this connection, do so now. */
1008 if(ts->close_connection || conn->bits.close) {
1009 /* Close the filter chain and trigger connect, non-blocking
1010 * again, so the process is ongoing. This will
1011 * a) the close resets our tunnel state
1012 * b) the connect makes sure that there will be a socket
1013 * to select on again.
1014 * We return and expect to be called again. */
1015 infof(data, "Connect me again please");
1016 Curl_conn_close(data, cf->sockindex);
1017 result = cf->next->cft->connect(cf->next, data, FALSE, &done);
1018 goto out;
1019 }
1020 /* staying on this connection, reset state */
1021 tunnel_go_state(cf, ts, TUNNEL_INIT, data);
1022 }
1023 break;
1024
1025 default:
1026 break;
1027 }
1028
1029 } while(data->req.newurl);
1030
1031 DEBUGASSERT(ts->tunnel_state == TUNNEL_RESPONSE);
1032 if(data->info.httpproxycode/100 != 2) {
1033 /* a non-2xx response and we have no next url to try. */
1034 free(data->req.newurl);
1035 data->req.newurl = NULL;
1036 /* failure, close this connection to avoid re-use */
1037 streamclose(conn, "proxy CONNECT failure");
1038 tunnel_go_state(cf, ts, TUNNEL_FAILED, data);
1039 failf(data, "CONNECT tunnel failed, response %d", data->req.httpcode);
1040 return CURLE_RECV_ERROR;
1041 }
1042 /* 2xx response, SUCCESS! */
1043 tunnel_go_state(cf, ts, TUNNEL_ESTABLISHED, data);
1044 infof(data, "CONNECT tunnel established, response %d",
1045 data->info.httpproxycode);
1046 result = CURLE_OK;
1047
1048out:
1049 if(result)
1050 tunnel_go_state(cf, ts, TUNNEL_FAILED, data);
1051 return result;
1052}
1053
1054static CURLcode http_proxy_cf_connect(struct Curl_cfilter *cf,
1055 struct Curl_easy *data,
1056 bool blocking, bool *done)
1057{
1058 CURLcode result;
1059 struct tunnel_state *ts = cf->ctx;
1060
1061 if(cf->connected) {
1062 *done = TRUE;
1063 return CURLE_OK;
1064 }
1065
1066 result = cf->next->cft->connect(cf->next, data, blocking, done);
1067 if(result || !*done)
1068 return result;
1069
1070 /* TODO: can we do blocking? */
1071 /* We want "seamless" operations through HTTP proxy tunnel */
1072
1073 /* for the secondary socket (FTP), use the "connect to host"
1074 * but ignore the "connect to port" (use the secondary port)
1075 */
1076 *done = FALSE;
1077 if(!ts) {
1078 result = tunnel_init(&ts, data, cf->conn, cf->sockindex);
1079 if(result)
1080 return result;
1081 cf->ctx = ts;
1082 }
1083
1084 result = CONNECT(cf, data, ts);
1085 if(result)
1086 goto out;
1087 Curl_safefree(data->state.aptr.proxyuserpwd);
1088
1089out:
1090 *done = (result == CURLE_OK) && tunnel_is_established(cf->ctx);
1091 if (*done) {
1092 cf->connected = TRUE;
1093 tunnel_free(cf, data);
1094 }
1095 return result;
1096}
1097
1098static void http_proxy_cf_get_host(struct Curl_cfilter *cf,
1099 struct Curl_easy *data,
1100 const char **phost,
1101 const char **pdisplay_host,
1102 int *pport)
1103{
1104 (void)data;
1105 if(!cf->connected) {
1106 *phost = cf->conn->http_proxy.host.name;
1107 *pdisplay_host = cf->conn->http_proxy.host.dispname;
1108 *pport = (int)cf->conn->http_proxy.port;
1109 }
1110 else {
1111 cf->next->cft->get_host(cf->next, data, phost, pdisplay_host, pport);
1112 }
1113}
1114
1115static int http_proxy_cf_get_select_socks(struct Curl_cfilter *cf,
1116 struct Curl_easy *data,
1117 curl_socket_t *socks)
1118{
1119 struct tunnel_state *ts = cf->ctx;
1120 struct connectdata *conn = cf->conn;
1121 int fds;
1122
1123 DEBUGASSERT(conn);
1124 fds = cf->next->cft->get_select_socks(cf->next, data, socks);
1125 if(!fds && cf->next->connected && !cf->connected) {
1126 /* If we are not connected, but the filter "below" is
1127 * and not waiting on something, we are tunneling. */
1128 socks[0] = conn->sock[cf->sockindex];
1129 if(ts) {
1130 /* when we've sent a CONNECT to a proxy, we should rather either
1131 wait for the socket to become readable to be able to get the
1132 response headers or if we're still sending the request, wait
1133 for write. */
1134 if(ts->http_proxy.sending == HTTPSEND_REQUEST)
1135 return GETSOCK_WRITESOCK(0);
1136 return GETSOCK_READSOCK(0);
1137 }
1138 return GETSOCK_WRITESOCK(0);
1139 }
1140 return fds;
1141}
1142
1143static void http_proxy_cf_detach_data(struct Curl_cfilter *cf,
1144 struct Curl_easy *data)
1145{
1146 if(cf->ctx) {
1147 tunnel_free(cf, data);
1148 }
1149}
1150
1151static void http_proxy_cf_destroy(struct Curl_cfilter *cf,
1152 struct Curl_easy *data)
1153{
1154 http_proxy_cf_detach_data(cf, data);
1155}
1156
1157static void http_proxy_cf_close(struct Curl_cfilter *cf,
1158 struct Curl_easy *data)
1159{
1160 DEBUGASSERT(cf->next);
1161 cf->connected = FALSE;
1162 cf->next->cft->close(cf->next, data);
1163 if(cf->ctx) {
1164 tunnel_go_state(cf, cf->ctx, TUNNEL_INIT, data);
1165 }
1166}
1167
1168
1169static const struct Curl_cftype cft_http_proxy = {
1170 "HTTP-PROXY",
1171 CF_TYPE_IP_CONNECT,
1172 http_proxy_cf_destroy,
1173 Curl_cf_def_setup,
1174 http_proxy_cf_connect,
1175 http_proxy_cf_close,
1176 http_proxy_cf_get_host,
1177 http_proxy_cf_get_select_socks,
1178 Curl_cf_def_data_pending,
1179 Curl_cf_def_send,
1180 Curl_cf_def_recv,
1181 Curl_cf_def_attach_data,
1182 http_proxy_cf_detach_data,
1183};
1184
1185CURLcode Curl_conn_http_proxy_add(struct Curl_easy *data,
1186 struct connectdata *conn,
1187 int sockindex)
1188{
1189 struct Curl_cfilter *cf;
1190 CURLcode result;
1191
1192 result = Curl_cf_create(&cf, &cft_http_proxy, NULL);
1193 if(!result)
1194 Curl_conn_cf_add(data, conn, sockindex, cf);
1195 return result;
1196}
1197
1198
1199static CURLcode send_haproxy_header(struct Curl_cfilter*cf,
1200 struct Curl_easy *data)
1201{
1202 struct dynbuf req;
1203 CURLcode result;
1204 const char *tcp_version;
1205 Curl_dyn_init(&req, DYN_HAXPROXY);
1206
1207#ifdef USE_UNIX_SOCKETS
1208 if(cf->conn->unix_domain_socket)
1209 /* the buffer is large enough to hold this! */
1210 result = Curl_dyn_addn(&req, STRCONST("PROXY UNKNOWN\r\n"));
1211 else {
1212#endif /* USE_UNIX_SOCKETS */
1213 /* Emit the correct prefix for IPv6 */
1214 tcp_version = cf->conn->bits.ipv6 ? "TCP6" : "TCP4";
1215
1216 result = Curl_dyn_addf(&req, "PROXY %s %s %s %i %i\r\n",
1217 tcp_version,
1218 data->info.conn_local_ip,
1219 data->info.conn_primary_ip,
1220 data->info.conn_local_port,
1221 data->info.conn_primary_port);
1222
1223#ifdef USE_UNIX_SOCKETS
1224 }
1225#endif /* USE_UNIX_SOCKETS */
1226
1227 if(!result)
1228 result = Curl_buffer_send(&req, data, &data->info.request_size,
1229 0, FIRSTSOCKET);
1230 return result;
1231}
1232
1233static CURLcode haproxy_cf_connect(struct Curl_cfilter *cf,
1234 struct Curl_easy *data,
1235 bool blocking, bool *done)
1236{
1237 CURLcode result;
1238
1239 if(cf->connected) {
1240 *done = TRUE;
1241 return CURLE_OK;
1242 }
1243
1244 result = cf->next->cft->connect(cf->next, data, blocking, done);
1245 if(result || !*done)
1246 return result;
1247
1248 result = send_haproxy_header(cf, data);
1249 *done = (!result);
1250 cf->connected = *done;
1251 return result;
1252}
1253
1254static const struct Curl_cftype cft_haproxy = {
1255 "HAPROXY",
1256 0,
1257 Curl_cf_def_destroy_this,
1258 Curl_cf_def_setup,
1259 haproxy_cf_connect,
1260 Curl_cf_def_close,
1261 Curl_cf_def_get_host,
1262 Curl_cf_def_get_select_socks,
1263 Curl_cf_def_data_pending,
1264 Curl_cf_def_send,
1265 Curl_cf_def_recv,
1266 Curl_cf_def_attach_data,
1267 Curl_cf_def_detach_data,
1268};
1269
1270CURLcode Curl_conn_haproxy_add(struct Curl_easy *data,
1271 struct connectdata *conn,
1272 int sockindex)
1273{
1274 struct Curl_cfilter *cf;
1275 CURLcode result;
1276
1277 result = Curl_cf_create(&cf, &cft_haproxy, NULL);
1278 if(!result)
1279 Curl_conn_cf_add(data, conn, sockindex, cf);
1280 return result;
1281}
1282
1283#endif /* !CURL_DISABLE_PROXY &6 ! CURL_DISABLE_HTTP */
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette