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