• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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