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