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