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