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 "connect.h"
41#include "curlx.h"
42#include "vtls/vtls.h"
43#include "transfer.h"
44#include "multiif.h"
45
46/* The last 3 #include files should be in this order */
47#include "curl_printf.h"
48#include "curl_memory.h"
49#include "memdebug.h"
50
51/*
52 * Perform SSL initialization for HTTPS proxy. Sets
53 * proxy_ssl_connected connection bit when complete. Can be
54 * called multiple times.
55 */
56static CURLcode https_proxy_connect(struct Curl_easy *data, int sockindex)
57{
58#ifdef USE_SSL
59 struct connectdata *conn = data->conn;
60 CURLcode result = CURLE_OK;
61 DEBUGASSERT(conn->http_proxy.proxytype == CURLPROXY_HTTPS);
62 if(!conn->bits.proxy_ssl_connected[sockindex]) {
63 /* perform SSL initialization for this socket */
64 result =
65 Curl_ssl_connect_nonblocking(data, conn, TRUE, sockindex,
66 &conn->bits.proxy_ssl_connected[sockindex]);
67 if(result)
68 /* a failed connection is marked for closure to prevent (bad) re-use or
69 similar */
70 connclose(conn, "TLS handshake failed");
71 }
72 return result;
73#else
74 (void) data;
75 (void) sockindex;
76 return CURLE_NOT_BUILT_IN;
77#endif
78}
79
80CURLcode Curl_proxy_connect(struct Curl_easy *data, int sockindex)
81{
82 struct connectdata *conn = data->conn;
83 if(conn->http_proxy.proxytype == CURLPROXY_HTTPS) {
84 const CURLcode result = https_proxy_connect(data, sockindex);
85 if(result)
86 return result;
87 if(!conn->bits.proxy_ssl_connected[sockindex])
88 return result; /* wait for HTTPS proxy SSL initialization to complete */
89 }
90
91 if(conn->bits.tunnel_proxy && conn->bits.httpproxy) {
92#ifndef CURL_DISABLE_PROXY
93 /* for [protocol] tunneled through HTTP proxy */
94 const char *hostname;
95 int remote_port;
96 CURLcode result;
97
98 /* We want "seamless" operations through HTTP proxy tunnel */
99
100 /* for the secondary socket (FTP), use the "connect to host"
101 * but ignore the "connect to port" (use the secondary port)
102 */
103
104 if(conn->bits.conn_to_host)
105 hostname = conn->conn_to_host.name;
106 else if(sockindex == SECONDARYSOCKET)
107 hostname = conn->secondaryhostname;
108 else
109 hostname = conn->host.name;
110
111 if(sockindex == SECONDARYSOCKET)
112 remote_port = conn->secondary_port;
113 else if(conn->bits.conn_to_port)
114 remote_port = conn->conn_to_port;
115 else
116 remote_port = conn->remote_port;
117
118 result = Curl_proxyCONNECT(data, sockindex, hostname, remote_port);
119 if(CURLE_OK != result)
120 return result;
121 Curl_safefree(data->state.aptr.proxyuserpwd);
122#else
123 return CURLE_NOT_BUILT_IN;
124#endif
125 }
126 /* no HTTP tunnel proxy, just return */
127 return CURLE_OK;
128}
129
130bool Curl_connect_complete(struct connectdata *conn)
131{
132 return !conn->connect_state ||
133 (conn->connect_state->tunnel_state >= TUNNEL_COMPLETE);
134}
135
136bool Curl_connect_ongoing(struct connectdata *conn)
137{
138 return conn->connect_state &&
139 (conn->connect_state->tunnel_state <= TUNNEL_COMPLETE);
140}
141
142/* when we've sent a CONNECT to a proxy, we should rather either wait for the
143 socket to become readable to be able to get the response headers or if
144 we're still sending the request, wait for write. */
145int Curl_connect_getsock(struct connectdata *conn)
146{
147 struct HTTP *http;
148 DEBUGASSERT(conn);
149 DEBUGASSERT(conn->connect_state);
150 http = &conn->connect_state->http_proxy;
151
152 if(http->sending == HTTPSEND_REQUEST)
153 return GETSOCK_WRITESOCK(0);
154
155 return GETSOCK_READSOCK(0);
156}
157
158static CURLcode connect_init(struct Curl_easy *data, bool reinit)
159{
160 struct http_connect_state *s;
161 struct connectdata *conn = data->conn;
162 if(conn->handler->flags & PROTOPT_NOTCPPROXY) {
163 failf(data, "%s cannot be done over CONNECT", conn->handler->scheme);
164 return CURLE_UNSUPPORTED_PROTOCOL;
165 }
166 if(!reinit) {
167 CURLcode result;
168 DEBUGASSERT(!conn->connect_state);
169 /* we might need the upload buffer for streaming a partial request */
170 result = Curl_get_upload_buffer(data);
171 if(result)
172 return result;
173
174 s = calloc(1, sizeof(struct http_connect_state));
175 if(!s)
176 return CURLE_OUT_OF_MEMORY;
177 infof(data, "allocate connect buffer");
178 conn->connect_state = s;
179 Curl_dyn_init(&s->rcvbuf, DYN_PROXY_CONNECT_HEADERS);
180
181 /* Curl_proxyCONNECT is based on a pointer to a struct HTTP at the
182 * member conn->proto.http; we want [protocol] through HTTP and we have
183 * to change the member temporarily for connecting to the HTTP
184 * proxy. After Curl_proxyCONNECT we have to set back the member to the
185 * original pointer
186 *
187 * This function might be called several times in the multi interface case
188 * if the proxy's CONNECT response is not instant.
189 */
190 s->prot_save = data->req.p.http;
191 data->req.p.http = &s->http_proxy;
192 connkeep(conn, "HTTP proxy CONNECT");
193 }
194 else {
195 DEBUGASSERT(conn->connect_state);
196 s = conn->connect_state;
197 Curl_dyn_reset(&s->rcvbuf);
198 }
199 s->tunnel_state = TUNNEL_INIT;
200 s->keepon = KEEPON_CONNECT;
201 s->cl = 0;
202 s->close_connection = FALSE;
203 return CURLE_OK;
204}
205
206void Curl_connect_done(struct Curl_easy *data)
207{
208 struct connectdata *conn = data->conn;
209 struct http_connect_state *s = conn->connect_state;
210 if(s && (s->tunnel_state != TUNNEL_EXIT)) {
211 s->tunnel_state = TUNNEL_EXIT;
212 Curl_dyn_free(&s->rcvbuf);
213 Curl_dyn_free(&s->req);
214
215 /* restore the protocol pointer, if not already done */
216 if(s->prot_save)
217 data->req.p.http = s->prot_save;
218 s->prot_save = NULL;
219 data->info.httpcode = 0; /* clear it as it might've been used for the
220 proxy */
221 data->req.ignorebody = FALSE;
222#ifdef USE_HYPER
223 data->state.hconnect = FALSE;
224#endif
225 infof(data, "CONNECT phase completed");
226 }
227}
228
229static CURLcode CONNECT_host(struct Curl_easy *data,
230 struct connectdata *conn,
231 const char *hostname,
232 int remote_port,
233 char **connecthostp,
234 char **hostp)
235{
236 char *hostheader; /* for CONNECT */
237 char *host = NULL; /* Host: */
238 bool ipv6_ip = conn->bits.ipv6_ip;
239
240 /* the hostname may be different */
241 if(hostname != conn->host.name)
242 ipv6_ip = (strchr(hostname, ':') != NULL);
243 hostheader = /* host:port with IPv6 support */
244 aprintf("%s%s%s:%d", ipv6_ip?"[":"", hostname, ipv6_ip?"]":"",
245 remote_port);
246 if(!hostheader)
247 return CURLE_OUT_OF_MEMORY;
248
249 if(!Curl_checkProxyheaders(data, conn, STRCONST("Host"))) {
250 host = aprintf("Host: %s\r\n", hostheader);
251 if(!host) {
252 free(hostheader);
253 return CURLE_OUT_OF_MEMORY;
254 }
255 }
256 *connecthostp = hostheader;
257 *hostp = host;
258 return CURLE_OK;
259}
260
261#ifndef USE_HYPER
262static CURLcode CONNECT(struct Curl_easy *data,
263 int sockindex,
264 const char *hostname,
265 int remote_port)
266{
267 int subversion = 0;
268 struct SingleRequest *k = &data->req;
269 CURLcode result;
270 struct connectdata *conn = data->conn;
271 curl_socket_t tunnelsocket = conn->sock[sockindex];
272 struct http_connect_state *s = conn->connect_state;
273 struct HTTP *http = data->req.p.http;
274 char *linep;
275 size_t perline;
276
277#define SELECT_OK 0
278#define SELECT_ERROR 1
279
280 if(Curl_connect_complete(conn))
281 return CURLE_OK; /* CONNECT is already completed */
282
283 conn->bits.proxy_connect_closed = FALSE;
284
285 do {
286 timediff_t check;
287 if(TUNNEL_INIT == s->tunnel_state) {
288 /* BEGIN CONNECT PHASE */
289 struct dynbuf *req = &s->req;
290 char *hostheader = NULL;
291 char *host = NULL;
292
293 infof(data, "Establish HTTP proxy tunnel to %s:%d",
294 hostname, remote_port);
295
296 /* This only happens if we've looped here due to authentication
297 reasons, and we don't really use the newly cloned URL here
298 then. Just free() it. */
299 Curl_safefree(data->req.newurl);
300
301 /* initialize send-buffer */
302 Curl_dyn_init(req, DYN_HTTP_REQUEST);
303
304 result = CONNECT_host(data, conn,
305 hostname, remote_port, &hostheader, &host);
306 if(result)
307 return result;
308
309 /* Setup the proxy-authorization header, if any */
310 result = Curl_http_output_auth(data, conn, "CONNECT", HTTPREQ_GET,
311 hostheader, TRUE);
312
313 if(!result) {
314 const char *httpv =
315 (conn->http_proxy.proxytype == CURLPROXY_HTTP_1_0) ? "1.0" : "1.1";
316
317 result =
318 Curl_dyn_addf(req,
319 "CONNECT %s HTTP/%s\r\n"
320 "%s" /* Host: */
321 "%s", /* Proxy-Authorization */
322 hostheader,
323 httpv,
324 host?host:"",
325 data->state.aptr.proxyuserpwd?
326 data->state.aptr.proxyuserpwd:"");
327
328 if(!result && !Curl_checkProxyheaders(data,
329 conn, STRCONST("User-Agent")) &&
330 data->set.str[STRING_USERAGENT])
331 result = Curl_dyn_addf(req, "User-Agent: %s\r\n",
332 data->set.str[STRING_USERAGENT]);
333
334 if(!result && !Curl_checkProxyheaders(data, conn,
335 STRCONST("Proxy-Connection")))
336 result = Curl_dyn_addn(req,
337 STRCONST("Proxy-Connection: Keep-Alive\r\n"));
338
339 if(!result)
340 result = Curl_add_custom_headers(data, TRUE, req);
341
342 if(!result)
343 /* CRLF terminate the request */
344 result = Curl_dyn_addn(req, STRCONST("\r\n"));
345
346 if(!result) {
347 /* Send the connect request to the proxy */
348 result = Curl_buffer_send(req, data, &data->info.request_size, 0,
349 sockindex);
350 s->headerlines = 0;
351 }
352 if(result)
353 failf(data, "Failed sending CONNECT to proxy");
354 }
355 free(host);
356 free(hostheader);
357 if(result)
358 return result;
359
360 s->tunnel_state = TUNNEL_CONNECT;
361 } /* END CONNECT PHASE */
362
363 check = Curl_timeleft(data, NULL, TRUE);
364 if(check <= 0) {
365 failf(data, "Proxy CONNECT aborted due to timeout");
366 return CURLE_OPERATION_TIMEDOUT;
367 }
368
369 if(!Curl_conn_data_pending(conn, sockindex) && !http->sending)
370 /* return so we'll be called again polling-style */
371 return CURLE_OK;
372
373 /* at this point, the tunnel_connecting phase is over. */
374
375 if(http->sending == HTTPSEND_REQUEST) {
376 if(!s->nsend) {
377 size_t fillcount;
378 k->upload_fromhere = data->state.ulbuf;
379 result = Curl_fillreadbuffer(data, data->set.upload_buffer_size,
380 &fillcount);
381 if(result)
382 return result;
383 s->nsend = fillcount;
384 }
385 if(s->nsend) {
386 ssize_t bytes_written;
387 /* write to socket (send away data) */
388 result = Curl_write(data,
389 conn->writesockfd, /* socket to send to */
390 k->upload_fromhere, /* buffer pointer */
391 s->nsend, /* buffer size */
392 &bytes_written); /* actually sent */
393
394 if(!result)
395 /* send to debug callback! */
396 Curl_debug(data, CURLINFO_HEADER_OUT,
397 k->upload_fromhere, bytes_written);
398
399 s->nsend -= bytes_written;
400 k->upload_fromhere += bytes_written;
401 return result;
402 }
403 http->sending = HTTPSEND_NADA;
404 /* if nothing left to send, continue */
405 }
406 { /* READING RESPONSE PHASE */
407 int error = SELECT_OK;
408
409 while(s->keepon) {
410 ssize_t gotbytes;
411 char byte;
412
413 /* Read one byte at a time to avoid a race condition. Wait at most one
414 second before looping to ensure continuous pgrsUpdates. */
415 result = Curl_read(data, tunnelsocket, &byte, 1, &gotbytes);
416 if(result == CURLE_AGAIN)
417 /* socket buffer drained, return */
418 return CURLE_OK;
419
420 if(Curl_pgrsUpdate(data))
421 return CURLE_ABORTED_BY_CALLBACK;
422
423 if(result) {
424 s->keepon = KEEPON_DONE;
425 break;
426 }
427 else if(gotbytes <= 0) {
428 if(data->set.proxyauth && data->state.authproxy.avail &&
429 data->state.aptr.proxyuserpwd) {
430 /* proxy auth was requested and there was proxy auth available,
431 then deem this as "mere" proxy disconnect */
432 conn->bits.proxy_connect_closed = TRUE;
433 infof(data, "Proxy CONNECT connection closed");
434 }
435 else {
436 error = SELECT_ERROR;
437 failf(data, "Proxy CONNECT aborted");
438 }
439 s->keepon = KEEPON_DONE;
440 break;
441 }
442
443 if(s->keepon == KEEPON_IGNORE) {
444 /* This means we are currently ignoring a response-body */
445
446 if(s->cl) {
447 /* A Content-Length based body: simply count down the counter
448 and make sure to break out of the loop when we're done! */
449 s->cl--;
450 if(s->cl <= 0) {
451 s->keepon = KEEPON_DONE;
452 s->tunnel_state = TUNNEL_COMPLETE;
453 break;
454 }
455 }
456 else {
457 /* chunked-encoded body, so we need to do the chunked dance
458 properly to know when the end of the body is reached */
459 CHUNKcode r;
460 CURLcode extra;
461 ssize_t tookcareof = 0;
462
463 /* now parse the chunked piece of data so that we can
464 properly tell when the stream ends */
465 r = Curl_httpchunk_read(data, &byte, 1, &tookcareof, &extra);
466 if(r == CHUNKE_STOP) {
467 /* we're done reading chunks! */
468 infof(data, "chunk reading DONE");
469 s->keepon = KEEPON_DONE;
470 /* we did the full CONNECT treatment, go COMPLETE */
471 s->tunnel_state = TUNNEL_COMPLETE;
472 }
473 }
474 continue;
475 }
476
477 if(Curl_dyn_addn(&s->rcvbuf, &byte, 1)) {
478 failf(data, "CONNECT response too large");
479 return CURLE_RECV_ERROR;
480 }
481
482 /* if this is not the end of a header line then continue */
483 if(byte != 0x0a)
484 continue;
485
486 s->headerlines++;
487 linep = Curl_dyn_ptr(&s->rcvbuf);
488 perline = Curl_dyn_len(&s->rcvbuf); /* amount of bytes in this line */
489
490 /* output debug if that is requested */
491 Curl_debug(data, CURLINFO_HEADER_IN, linep, perline);
492
493 if(!data->set.suppress_connect_headers) {
494 /* send the header to the callback */
495 int writetype = CLIENTWRITE_HEADER | CLIENTWRITE_CONNECT |
496 (data->set.include_header ? CLIENTWRITE_BODY : 0) |
497 (s->headerlines == 1 ? CLIENTWRITE_STATUS : 0);
498
499 result = Curl_client_write(data, writetype, linep, perline);
500 if(result)
501 return result;
502 }
503
504 data->info.header_size += (long)perline;
505
506 /* Newlines are CRLF, so the CR is ignored as the line isn't
507 really terminated until the LF comes. Treat a following CR
508 as end-of-headers as well.*/
509
510 if(('\r' == linep[0]) ||
511 ('\n' == linep[0])) {
512 /* end of response-headers from the proxy */
513
514 if((407 == k->httpcode) && !data->state.authproblem) {
515 /* If we get a 407 response code with content length
516 when we have no auth problem, we must ignore the
517 whole response-body */
518 s->keepon = KEEPON_IGNORE;
519
520 if(s->cl) {
521 infof(data, "Ignore %" CURL_FORMAT_CURL_OFF_T
522 " bytes of response-body", s->cl);
523 }
524 else if(s->chunked_encoding) {
525 CHUNKcode r;
526 CURLcode extra;
527
528 infof(data, "Ignore chunked response-body");
529
530 /* We set ignorebody true here since the chunked decoder
531 function will acknowledge that. Pay attention so that this is
532 cleared again when this function returns! */
533 k->ignorebody = TRUE;
534
535 if(linep[1] == '\n')
536 /* this can only be a LF if the letter at index 0 was a CR */
537 linep++;
538
539 /* now parse the chunked piece of data so that we can properly
540 tell when the stream ends */
541 r = Curl_httpchunk_read(data, linep + 1, 1, &gotbytes,
542 &extra);
543 if(r == CHUNKE_STOP) {
544 /* we're done reading chunks! */
545 infof(data, "chunk reading DONE");
546 s->keepon = KEEPON_DONE;
547 /* we did the full CONNECT treatment, go to COMPLETE */
548 s->tunnel_state = TUNNEL_COMPLETE;
549 }
550 }
551 else {
552 /* without content-length or chunked encoding, we
553 can't keep the connection alive since the close is
554 the end signal so we bail out at once instead */
555 s->keepon = KEEPON_DONE;
556 }
557 }
558 else
559 s->keepon = KEEPON_DONE;
560
561 if(s->keepon == KEEPON_DONE && !s->cl)
562 /* we did the full CONNECT treatment, go to COMPLETE */
563 s->tunnel_state = TUNNEL_COMPLETE;
564
565 DEBUGASSERT(s->keepon == KEEPON_IGNORE || s->keepon == KEEPON_DONE);
566 continue;
567 }
568
569 if((checkprefix("WWW-Authenticate:", linep) &&
570 (401 == k->httpcode)) ||
571 (checkprefix("Proxy-authenticate:", linep) &&
572 (407 == k->httpcode))) {
573
574 bool proxy = (k->httpcode == 407) ? TRUE : FALSE;
575 char *auth = Curl_copy_header_value(linep);
576 if(!auth)
577 return CURLE_OUT_OF_MEMORY;
578
579 result = Curl_http_input_auth(data, proxy, auth);
580
581 free(auth);
582
583 if(result)
584 return result;
585 }
586 else if(checkprefix("Content-Length:", linep)) {
587 if(k->httpcode/100 == 2) {
588 /* A client MUST ignore any Content-Length or Transfer-Encoding
589 header fields received in a successful response to CONNECT.
590 "Successful" described as: 2xx (Successful). RFC 7231 4.3.6 */
591 infof(data, "Ignoring Content-Length in CONNECT %03d response",
592 k->httpcode);
593 }
594 else {
595 (void)curlx_strtoofft(linep +
596 strlen("Content-Length:"), NULL, 10, &s->cl);
597 }
598 }
599 else if(Curl_compareheader(linep,
600 STRCONST("Connection:"), STRCONST("close")))
601 s->close_connection = TRUE;
602 else if(checkprefix("Transfer-Encoding:", linep)) {
603 if(k->httpcode/100 == 2) {
604 /* A client MUST ignore any Content-Length or Transfer-Encoding
605 header fields received in a successful response to CONNECT.
606 "Successful" described as: 2xx (Successful). RFC 7231 4.3.6 */
607 infof(data, "Ignoring Transfer-Encoding in "
608 "CONNECT %03d response", k->httpcode);
609 }
610 else if(Curl_compareheader(linep,
611 STRCONST("Transfer-Encoding:"),
612 STRCONST("chunked"))) {
613 infof(data, "CONNECT responded chunked");
614 s->chunked_encoding = TRUE;
615 /* init our chunky engine */
616 Curl_httpchunk_init(data);
617 }
618 }
619 else if(Curl_compareheader(linep,
620 STRCONST("Proxy-Connection:"),
621 STRCONST("close")))
622 s->close_connection = TRUE;
623 else if(2 == sscanf(linep, "HTTP/1.%d %d",
624 &subversion,
625 &k->httpcode)) {
626 /* store the HTTP code from the proxy */
627 data->info.httpproxycode = k->httpcode;
628 }
629
630 Curl_dyn_reset(&s->rcvbuf);
631 } /* while there's buffer left and loop is requested */
632
633 if(Curl_pgrsUpdate(data))
634 return CURLE_ABORTED_BY_CALLBACK;
635
636 if(error)
637 return CURLE_RECV_ERROR;
638
639 if(data->info.httpproxycode/100 != 2) {
640 /* Deal with the possibly already received authenticate
641 headers. 'newurl' is set to a new URL if we must loop. */
642 result = Curl_http_auth_act(data);
643 if(result)
644 return result;
645
646 if(conn->bits.close)
647 /* the connection has been marked for closure, most likely in the
648 Curl_http_auth_act() function and thus we can kill it at once
649 below */
650 s->close_connection = TRUE;
651 }
652
653 if(s->close_connection && data->req.newurl) {
654 /* Connection closed by server. Don't use it anymore */
655 Curl_closesocket(data, conn, conn->sock[sockindex]);
656 conn->sock[sockindex] = CURL_SOCKET_BAD;
657 break;
658 }
659 } /* END READING RESPONSE PHASE */
660
661 /* If we are supposed to continue and request a new URL, which basically
662 * means the HTTP authentication is still going on so if the tunnel
663 * is complete we start over in INIT state */
664 if(data->req.newurl && (TUNNEL_COMPLETE == s->tunnel_state)) {
665 connect_init(data, TRUE); /* reinit */
666 }
667
668 } while(data->req.newurl);
669
670 if(data->info.httpproxycode/100 != 2) {
671 if(s->close_connection && data->req.newurl) {
672 conn->bits.proxy_connect_closed = TRUE;
673 infof(data, "Connect me again please");
674 Curl_connect_done(data);
675 }
676 else {
677 free(data->req.newurl);
678 data->req.newurl = NULL;
679 /* failure, close this connection to avoid re-use */
680 streamclose(conn, "proxy CONNECT failure");
681 }
682
683 /* to back to init state */
684 s->tunnel_state = TUNNEL_INIT;
685
686 if(conn->bits.proxy_connect_closed)
687 /* this is not an error, just part of the connection negotiation */
688 return CURLE_OK;
689 Curl_dyn_free(&s->rcvbuf);
690 failf(data, "Received HTTP code %d from proxy after CONNECT",
691 data->req.httpcode);
692 return CURLE_RECV_ERROR;
693 }
694
695 s->tunnel_state = TUNNEL_COMPLETE;
696
697 /* If a proxy-authorization header was used for the proxy, then we should
698 make sure that it isn't accidentally used for the document request
699 after we've connected. So let's free and clear it here. */
700 Curl_safefree(data->state.aptr.proxyuserpwd);
701 data->state.aptr.proxyuserpwd = NULL;
702
703 data->state.authproxy.done = TRUE;
704 data->state.authproxy.multipass = FALSE;
705
706 infof(data, "Proxy replied %d to CONNECT request",
707 data->info.httpproxycode);
708 data->req.ignorebody = FALSE; /* put it (back) to non-ignore state */
709 conn->bits.rewindaftersend = FALSE; /* make sure this isn't set for the
710 document request */
711 Curl_dyn_free(&s->rcvbuf);
712 return CURLE_OK;
713}
714#else
715/* The Hyper version of CONNECT */
716static CURLcode CONNECT(struct Curl_easy *data,
717 int sockindex,
718 const char *hostname,
719 int remote_port)
720{
721 struct connectdata *conn = data->conn;
722 struct hyptransfer *h = &data->hyp;
723 curl_socket_t tunnelsocket = conn->sock[sockindex];
724 struct http_connect_state *s = conn->connect_state;
725 CURLcode result = CURLE_OUT_OF_MEMORY;
726 hyper_io *io = NULL;
727 hyper_request *req = NULL;
728 hyper_headers *headers = NULL;
729 hyper_clientconn_options *options = NULL;
730 hyper_task *handshake = NULL;
731 hyper_task *task = NULL; /* for the handshake */
732 hyper_task *sendtask = NULL; /* for the send */
733 hyper_clientconn *client = NULL;
734 hyper_error *hypererr = NULL;
735 char *hostheader = NULL; /* for CONNECT */
736 char *host = NULL; /* Host: */
737
738 if(Curl_connect_complete(conn))
739 return CURLE_OK; /* CONNECT is already completed */
740
741 conn->bits.proxy_connect_closed = FALSE;
742
743 do {
744 switch(s->tunnel_state) {
745 case TUNNEL_INIT:
746 /* BEGIN CONNECT PHASE */
747 io = hyper_io_new();
748 if(!io) {
749 failf(data, "Couldn't create hyper IO");
750 result = CURLE_OUT_OF_MEMORY;
751 goto error;
752 }
753 /* tell Hyper how to read/write network data */
754 hyper_io_set_userdata(io, data);
755 hyper_io_set_read(io, Curl_hyper_recv);
756 hyper_io_set_write(io, Curl_hyper_send);
757 conn->sockfd = tunnelsocket;
758
759 data->state.hconnect = TRUE;
760
761 /* create an executor to poll futures */
762 if(!h->exec) {
763 h->exec = hyper_executor_new();
764 if(!h->exec) {
765 failf(data, "Couldn't create hyper executor");
766 result = CURLE_OUT_OF_MEMORY;
767 goto error;
768 }
769 }
770
771 options = hyper_clientconn_options_new();
772 hyper_clientconn_options_set_preserve_header_case(options, 1);
773 hyper_clientconn_options_set_preserve_header_order(options, 1);
774
775 if(!options) {
776 failf(data, "Couldn't create hyper client options");
777 result = CURLE_OUT_OF_MEMORY;
778 goto error;
779 }
780
781 hyper_clientconn_options_exec(options, h->exec);
782
783 /* "Both the `io` and the `options` are consumed in this function
784 call" */
785 handshake = hyper_clientconn_handshake(io, options);
786 if(!handshake) {
787 failf(data, "Couldn't create hyper client handshake");
788 result = CURLE_OUT_OF_MEMORY;
789 goto error;
790 }
791 io = NULL;
792 options = NULL;
793
794 if(HYPERE_OK != hyper_executor_push(h->exec, handshake)) {
795 failf(data, "Couldn't hyper_executor_push the handshake");
796 result = CURLE_OUT_OF_MEMORY;
797 goto error;
798 }
799 handshake = NULL; /* ownership passed on */
800
801 task = hyper_executor_poll(h->exec);
802 if(!task) {
803 failf(data, "Couldn't hyper_executor_poll the handshake");
804 result = CURLE_OUT_OF_MEMORY;
805 goto error;
806 }
807
808 client = hyper_task_value(task);
809 hyper_task_free(task);
810 req = hyper_request_new();
811 if(!req) {
812 failf(data, "Couldn't hyper_request_new");
813 result = CURLE_OUT_OF_MEMORY;
814 goto error;
815 }
816 if(hyper_request_set_method(req, (uint8_t *)"CONNECT",
817 strlen("CONNECT"))) {
818 failf(data, "error setting method");
819 result = CURLE_OUT_OF_MEMORY;
820 goto error;
821 }
822
823 infof(data, "Establish HTTP proxy tunnel to %s:%d",
824 hostname, remote_port);
825
826 /* This only happens if we've looped here due to authentication
827 reasons, and we don't really use the newly cloned URL here
828 then. Just free() it. */
829 Curl_safefree(data->req.newurl);
830
831 result = CONNECT_host(data, conn, hostname, remote_port,
832 &hostheader, &host);
833 if(result)
834 goto error;
835
836 if(hyper_request_set_uri(req, (uint8_t *)hostheader,
837 strlen(hostheader))) {
838 failf(data, "error setting path");
839 result = CURLE_OUT_OF_MEMORY;
840 goto error;
841 }
842 if(data->set.verbose) {
843 char *se = aprintf("CONNECT %s HTTP/1.1\r\n", hostheader);
844 if(!se) {
845 result = CURLE_OUT_OF_MEMORY;
846 goto error;
847 }
848 Curl_debug(data, CURLINFO_HEADER_OUT, se, strlen(se));
849 free(se);
850 }
851 /* Setup the proxy-authorization header, if any */
852 result = Curl_http_output_auth(data, conn, "CONNECT", HTTPREQ_GET,
853 hostheader, TRUE);
854 if(result)
855 goto error;
856 Curl_safefree(hostheader);
857
858 /* default is 1.1 */
859 if((conn->http_proxy.proxytype == CURLPROXY_HTTP_1_0) &&
860 (HYPERE_OK != hyper_request_set_version(req,
861 HYPER_HTTP_VERSION_1_0))) {
862 failf(data, "error setting HTTP version");
863 result = CURLE_OUT_OF_MEMORY;
864 goto error;
865 }
866
867 headers = hyper_request_headers(req);
868 if(!headers) {
869 failf(data, "hyper_request_headers");
870 result = CURLE_OUT_OF_MEMORY;
871 goto error;
872 }
873 if(host) {
874 result = Curl_hyper_header(data, headers, host);
875 if(result)
876 goto error;
877 Curl_safefree(host);
878 }
879
880 if(data->state.aptr.proxyuserpwd) {
881 result = Curl_hyper_header(data, headers,
882 data->state.aptr.proxyuserpwd);
883 if(result)
884 goto error;
885 }
886
887 if(!Curl_checkProxyheaders(data, conn, STRCONST("User-Agent")) &&
888 data->set.str[STRING_USERAGENT]) {
889 struct dynbuf ua;
890 Curl_dyn_init(&ua, DYN_HTTP_REQUEST);
891 result = Curl_dyn_addf(&ua, "User-Agent: %s\r\n",
892 data->set.str[STRING_USERAGENT]);
893 if(result)
894 goto error;
895 result = Curl_hyper_header(data, headers, Curl_dyn_ptr(&ua));
896 if(result)
897 goto error;
898 Curl_dyn_free(&ua);
899 }
900
901 if(!Curl_checkProxyheaders(data, conn, STRCONST("Proxy-Connection"))) {
902 result = Curl_hyper_header(data, headers,
903 "Proxy-Connection: Keep-Alive");
904 if(result)
905 goto error;
906 }
907
908 result = Curl_add_custom_headers(data, TRUE, headers);
909 if(result)
910 goto error;
911
912 sendtask = hyper_clientconn_send(client, req);
913 if(!sendtask) {
914 failf(data, "hyper_clientconn_send");
915 result = CURLE_OUT_OF_MEMORY;
916 goto error;
917 }
918
919 if(HYPERE_OK != hyper_executor_push(h->exec, sendtask)) {
920 failf(data, "Couldn't hyper_executor_push the send");
921 result = CURLE_OUT_OF_MEMORY;
922 goto error;
923 }
924
925 hyper_clientconn_free(client);
926
927 do {
928 task = hyper_executor_poll(h->exec);
929 if(task) {
930 bool error = hyper_task_type(task) == HYPER_TASK_ERROR;
931 if(error)
932 hypererr = hyper_task_value(task);
933 hyper_task_free(task);
934 if(error) {
935 /* this could probably use a better error code? */
936 result = CURLE_OUT_OF_MEMORY;
937 goto error;
938 }
939 }
940 } while(task);
941 s->tunnel_state = TUNNEL_CONNECT;
942 /* FALLTHROUGH */
943 case TUNNEL_CONNECT: {
944 int didwhat;
945 bool done = FALSE;
946 result = Curl_hyper_stream(data, conn, &didwhat, &done,
947 CURL_CSELECT_IN | CURL_CSELECT_OUT);
948 if(result)
949 goto error;
950 if(!done)
951 break;
952 s->tunnel_state = TUNNEL_COMPLETE;
953 if(h->exec) {
954 hyper_executor_free(h->exec);
955 h->exec = NULL;
956 }
957 if(h->read_waker) {
958 hyper_waker_free(h->read_waker);
959 h->read_waker = NULL;
960 }
961 if(h->write_waker) {
962 hyper_waker_free(h->write_waker);
963 h->write_waker = NULL;
964 }
965 }
966 break;
967
968 default:
969 break;
970 }
971
972 if(conn->bits.close && data->req.newurl) {
973 /* Connection closed by server. Don't use it anymore */
974 Curl_closesocket(data, conn, conn->sock[sockindex]);
975 conn->sock[sockindex] = CURL_SOCKET_BAD;
976 break;
977 }
978
979 /* If we are supposed to continue and request a new URL, which basically
980 * means the HTTP authentication is still going on so if the tunnel
981 * is complete we start over in INIT state */
982 if(data->req.newurl && (TUNNEL_COMPLETE == s->tunnel_state)) {
983 infof(data, "CONNECT request done, loop to make another");
984 connect_init(data, TRUE); /* reinit */
985 }
986 } while(data->req.newurl);
987
988 result = CURLE_OK;
989 if(s->tunnel_state == TUNNEL_COMPLETE) {
990 if(data->info.httpproxycode/100 != 2) {
991 if(conn->bits.close && data->req.newurl) {
992 conn->bits.proxy_connect_closed = TRUE;
993 infof(data, "Connect me again please");
994 Curl_connect_done(data);
995 }
996 else {
997 free(data->req.newurl);
998 data->req.newurl = NULL;
999 /* failure, close this connection to avoid re-use */
1000 streamclose(conn, "proxy CONNECT failure");
1001 Curl_closesocket(data, conn, conn->sock[sockindex]);
1002 conn->sock[sockindex] = CURL_SOCKET_BAD;
1003 }
1004
1005 /* to back to init state */
1006 s->tunnel_state = TUNNEL_INIT;
1007
1008 if(!conn->bits.proxy_connect_closed) {
1009 failf(data, "Received HTTP code %d from proxy after CONNECT",
1010 data->req.httpcode);
1011 result = CURLE_RECV_ERROR;
1012 }
1013 }
1014 }
1015 error:
1016 free(host);
1017 free(hostheader);
1018 if(io)
1019 hyper_io_free(io);
1020
1021 if(options)
1022 hyper_clientconn_options_free(options);
1023
1024 if(handshake)
1025 hyper_task_free(handshake);
1026
1027 if(hypererr) {
1028 uint8_t errbuf[256];
1029 size_t errlen = hyper_error_print(hypererr, errbuf, sizeof(errbuf));
1030 failf(data, "Hyper: %.*s", (int)errlen, errbuf);
1031 hyper_error_free(hypererr);
1032 }
1033 return result;
1034}
1035#endif
1036
1037void Curl_connect_free(struct Curl_easy *data)
1038{
1039 struct connectdata *conn = data->conn;
1040 struct http_connect_state *s = conn->connect_state;
1041 if(s) {
1042 free(s);
1043 conn->connect_state = NULL;
1044 }
1045}
1046
1047/*
1048 * Curl_proxyCONNECT() requires that we're connected to a HTTP proxy. This
1049 * function will issue the necessary commands to get a seamless tunnel through
1050 * this proxy. After that, the socket can be used just as a normal socket.
1051 */
1052
1053CURLcode Curl_proxyCONNECT(struct Curl_easy *data,
1054 int sockindex,
1055 const char *hostname,
1056 int remote_port)
1057{
1058 CURLcode result;
1059 struct connectdata *conn = data->conn;
1060 if(!conn->connect_state) {
1061 result = connect_init(data, FALSE);
1062 if(result)
1063 return result;
1064 }
1065 result = CONNECT(data, sockindex, hostname, remote_port);
1066
1067 if(result || Curl_connect_complete(conn))
1068 Curl_connect_done(data);
1069
1070 return result;
1071}
1072
1073#else
1074void Curl_connect_free(struct Curl_easy *data)
1075{
1076 (void)data;
1077}
1078
1079#endif /* CURL_DISABLE_PROXY */
1080