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