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