• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 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  * SPDX-License-Identifier: curl
22  *
23  ***************************************************************************/
24 
25 #include "curl_setup.h"
26 
27 #include "http_proxy.h"
28 
29 #if !defined(CURL_DISABLE_PROXY)
30 
31 #include <curl/curl.h>
32 #ifdef USE_HYPER
33 #include <hyper.h>
34 #endif
35 #include "sendf.h"
36 #include "http.h"
37 #include "url.h"
38 #include "select.h"
39 #include "progress.h"
40 #include "cfilters.h"
41 #include "connect.h"
42 #include "curlx.h"
43 #include "vtls/vtls.h"
44 #include "transfer.h"
45 #include "multiif.h"
46 
47 /* The last 3 #include files should be in this order */
48 #include "curl_printf.h"
49 #include "curl_memory.h"
50 #include "memdebug.h"
51 
52 
53 #if !defined(CURL_DISABLE_HTTP)
54 
55 typedef enum {
56     TUNNEL_INIT,     /* init/default/no tunnel state */
57     TUNNEL_CONNECT,  /* CONNECT request is being send */
58     TUNNEL_RECEIVE,  /* CONNECT answer is being received */
59     TUNNEL_RESPONSE, /* CONNECT response received completely */
60     TUNNEL_ESTABLISHED,
61     TUNNEL_FAILED
62 } tunnel_state;
63 
64 /* struct for HTTP CONNECT tunneling */
65 struct tunnel_state {
66   int sockindex;
67   const char *hostname;
68   int remote_port;
69   struct HTTP CONNECT;
70   struct dynbuf rcvbuf;
71   struct dynbuf req;
72   size_t nsend;
73   size_t headerlines;
74   enum keeponval {
75     KEEPON_DONE,
76     KEEPON_CONNECT,
77     KEEPON_IGNORE
78   } keepon;
79   curl_off_t cl; /* size of content to read and ignore */
80   tunnel_state tunnel_state;
81   BIT(chunked_encoding);
82   BIT(close_connection);
83 };
84 
85 
tunnel_is_established(struct tunnel_state * ts)86 static bool tunnel_is_established(struct tunnel_state *ts)
87 {
88   return ts && (ts->tunnel_state == TUNNEL_ESTABLISHED);
89 }
90 
tunnel_is_failed(struct tunnel_state * ts)91 static bool tunnel_is_failed(struct tunnel_state *ts)
92 {
93   return ts && (ts->tunnel_state == TUNNEL_FAILED);
94 }
95 
tunnel_reinit(struct tunnel_state * ts,struct connectdata * conn,struct Curl_easy * data)96 static CURLcode tunnel_reinit(struct tunnel_state *ts,
97                               struct connectdata *conn,
98                               struct Curl_easy *data)
99 {
100   (void)data;
101   DEBUGASSERT(ts);
102   Curl_dyn_reset(&ts->rcvbuf);
103   Curl_dyn_reset(&ts->req);
104   ts->tunnel_state = TUNNEL_INIT;
105   ts->keepon = KEEPON_CONNECT;
106   ts->cl = 0;
107   ts->close_connection = FALSE;
108 
109   if(conn->bits.conn_to_host)
110     ts->hostname = conn->conn_to_host.name;
111   else if(ts->sockindex == SECONDARYSOCKET)
112     ts->hostname = conn->secondaryhostname;
113   else
114     ts->hostname = conn->host.name;
115 
116   if(ts->sockindex == SECONDARYSOCKET)
117     ts->remote_port = conn->secondary_port;
118   else if(conn->bits.conn_to_port)
119     ts->remote_port = conn->conn_to_port;
120   else
121     ts->remote_port = conn->remote_port;
122 
123   return CURLE_OK;
124 }
125 
tunnel_init(struct tunnel_state ** pts,struct Curl_easy * data,struct connectdata * conn,int sockindex)126 static CURLcode tunnel_init(struct tunnel_state **pts,
127                             struct Curl_easy *data,
128                             struct connectdata *conn,
129                             int sockindex)
130 {
131   struct tunnel_state *ts;
132   CURLcode result;
133 
134   if(conn->handler->flags & PROTOPT_NOTCPPROXY) {
135     failf(data, "%s cannot be done over CONNECT", conn->handler->scheme);
136     return CURLE_UNSUPPORTED_PROTOCOL;
137   }
138 
139   /* we might need the upload buffer for streaming a partial request */
140   result = Curl_get_upload_buffer(data);
141   if(result)
142     return result;
143 
144   ts = calloc(1, sizeof(*ts));
145   if(!ts)
146     return CURLE_OUT_OF_MEMORY;
147 
148   ts->sockindex = sockindex;
149   infof(data, "allocate connect buffer");
150 
151   Curl_dyn_init(&ts->rcvbuf, DYN_PROXY_CONNECT_HEADERS);
152   Curl_dyn_init(&ts->req, DYN_HTTP_REQUEST);
153 
154   *pts =  ts;
155   connkeep(conn, "HTTP proxy CONNECT");
156   return tunnel_reinit(ts, conn, data);
157 }
158 
tunnel_go_state(struct Curl_cfilter * cf,struct tunnel_state * ts,tunnel_state new_state,struct Curl_easy * data)159 static void tunnel_go_state(struct Curl_cfilter *cf,
160                             struct tunnel_state *ts,
161                             tunnel_state new_state,
162                             struct Curl_easy *data)
163 {
164   if(ts->tunnel_state == new_state)
165     return;
166   /* leaving this one */
167   switch(ts->tunnel_state) {
168   case TUNNEL_CONNECT:
169     data->req.ignorebody = FALSE;
170     break;
171   default:
172     break;
173   }
174   /* entering this one */
175   switch(new_state) {
176   case TUNNEL_INIT:
177     DEBUGF(LOG_CF(data, cf, "new tunnel state 'init'"));
178     tunnel_reinit(ts, cf->conn, data);
179     break;
180 
181   case TUNNEL_CONNECT:
182     DEBUGF(LOG_CF(data, cf, "new tunnel state 'connect'"));
183     ts->tunnel_state = TUNNEL_CONNECT;
184     ts->keepon = KEEPON_CONNECT;
185     Curl_dyn_reset(&ts->rcvbuf);
186     break;
187 
188   case TUNNEL_RECEIVE:
189     DEBUGF(LOG_CF(data, cf, "new tunnel state 'receive'"));
190     ts->tunnel_state = TUNNEL_RECEIVE;
191     break;
192 
193   case TUNNEL_RESPONSE:
194     DEBUGF(LOG_CF(data, cf, "new tunnel state 'response'"));
195     ts->tunnel_state = TUNNEL_RESPONSE;
196     break;
197 
198   case TUNNEL_ESTABLISHED:
199     DEBUGF(LOG_CF(data, cf, "new tunnel state 'established'"));
200     infof(data, "CONNECT phase completed");
201     data->state.authproxy.done = TRUE;
202     data->state.authproxy.multipass = FALSE;
203     /* FALLTHROUGH */
204   case TUNNEL_FAILED:
205     DEBUGF(LOG_CF(data, cf, "new tunnel state 'failed'"));
206     ts->tunnel_state = new_state;
207     Curl_dyn_reset(&ts->rcvbuf);
208     Curl_dyn_reset(&ts->req);
209     /* restore the protocol pointer */
210     data->info.httpcode = 0; /* clear it as it might've been used for the
211                                 proxy */
212     /* If a proxy-authorization header was used for the proxy, then we should
213        make sure that it isn't accidentally used for the document request
214        after we've connected. So let's free and clear it here. */
215     Curl_safefree(data->state.aptr.proxyuserpwd);
216     data->state.aptr.proxyuserpwd = NULL;
217 #ifdef USE_HYPER
218     data->state.hconnect = FALSE;
219 #endif
220     break;
221   }
222 }
223 
tunnel_free(struct Curl_cfilter * cf,struct Curl_easy * data)224 static void tunnel_free(struct Curl_cfilter *cf,
225                         struct Curl_easy *data)
226 {
227   struct tunnel_state *ts = cf->ctx;
228   if(ts) {
229     tunnel_go_state(cf, ts, TUNNEL_FAILED, data);
230     Curl_dyn_free(&ts->rcvbuf);
231     Curl_dyn_free(&ts->req);
232     free(ts);
233     cf->ctx = NULL;
234   }
235 }
236 
CONNECT_host(struct Curl_easy * data,struct connectdata * conn,const char * hostname,int remote_port,char ** connecthostp,char ** hostp)237 static CURLcode CONNECT_host(struct Curl_easy *data,
238                              struct connectdata *conn,
239                              const char *hostname,
240                              int remote_port,
241                              char **connecthostp,
242                              char **hostp)
243 {
244   char *hostheader; /* for CONNECT */
245   char *host = NULL; /* Host: */
246   bool ipv6_ip = conn->bits.ipv6_ip;
247 
248   /* the hostname may be different */
249   if(hostname != conn->host.name)
250     ipv6_ip = (strchr(hostname, ':') != NULL);
251   hostheader = /* host:port with IPv6 support */
252     aprintf("%s%s%s:%d", ipv6_ip?"[":"", hostname, ipv6_ip?"]":"",
253             remote_port);
254   if(!hostheader)
255     return CURLE_OUT_OF_MEMORY;
256 
257   if(!Curl_checkProxyheaders(data, conn, STRCONST("Host"))) {
258     host = aprintf("Host: %s\r\n", hostheader);
259     if(!host) {
260       free(hostheader);
261       return CURLE_OUT_OF_MEMORY;
262     }
263   }
264   *connecthostp = hostheader;
265   *hostp = host;
266   return CURLE_OK;
267 }
268 
269 #ifndef USE_HYPER
start_CONNECT(struct Curl_cfilter * cf,struct Curl_easy * data,struct tunnel_state * ts)270 static CURLcode start_CONNECT(struct Curl_cfilter *cf,
271                               struct Curl_easy *data,
272                               struct tunnel_state *ts)
273 {
274   struct connectdata *conn = cf->conn;
275   char *hostheader = NULL;
276   char *host = NULL;
277   const char *httpv;
278   CURLcode result;
279 
280   infof(data, "Establish HTTP proxy tunnel to %s:%d",
281         ts->hostname, ts->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   Curl_safefree(data->req.newurl);
287 
288   result = CONNECT_host(data, conn,
289                         ts->hostname, ts->remote_port,
290                         &hostheader, &host);
291   if(result)
292     goto out;
293 
294   /* Setup the proxy-authorization header, if any */
295   result = Curl_http_output_auth(data, conn, "CONNECT", HTTPREQ_GET,
296                                  hostheader, TRUE);
297   if(result)
298     goto out;
299 
300   httpv = (conn->http_proxy.proxytype == CURLPROXY_HTTP_1_0) ? "1.0" : "1.1";
301 
302   result =
303       Curl_dyn_addf(&ts->req,
304                     "CONNECT %s HTTP/%s\r\n"
305                     "%s"  /* Host: */
306                     "%s", /* Proxy-Authorization */
307                     hostheader,
308                     httpv,
309                     host?host:"",
310                     data->state.aptr.proxyuserpwd?
311                     data->state.aptr.proxyuserpwd:"");
312   if(result)
313     goto out;
314 
315   if(!Curl_checkProxyheaders(data, conn, STRCONST("User-Agent"))
316      && data->set.str[STRING_USERAGENT])
317     result = Curl_dyn_addf(&ts->req, "User-Agent: %s\r\n",
318                            data->set.str[STRING_USERAGENT]);
319   if(result)
320     goto out;
321 
322   if(!Curl_checkProxyheaders(data, conn, STRCONST("Proxy-Connection")))
323     result = Curl_dyn_addn(&ts->req,
324                            STRCONST("Proxy-Connection: Keep-Alive\r\n"));
325   if(result)
326     goto out;
327 
328   result = Curl_add_custom_headers(data, TRUE, &ts->req);
329   if(result)
330     goto out;
331 
332   /* CRLF terminate the request */
333   result = Curl_dyn_addn(&ts->req, STRCONST("\r\n"));
334   if(result)
335     goto out;
336 
337   /* Send the connect request to the proxy */
338   result = Curl_buffer_send(&ts->req, data, &ts->CONNECT,
339                             &data->info.request_size, 0,
340                             ts->sockindex);
341   ts->headerlines = 0;
342 
343 out:
344   if(result)
345     failf(data, "Failed sending CONNECT to proxy");
346   free(host);
347   free(hostheader);
348   return result;
349 }
350 
send_CONNECT(struct Curl_easy * data,struct connectdata * conn,struct tunnel_state * ts,bool * done)351 static CURLcode send_CONNECT(struct Curl_easy *data,
352                              struct connectdata *conn,
353                              struct tunnel_state *ts,
354                              bool *done)
355 {
356   struct SingleRequest *k = &data->req;
357   struct HTTP *http = &ts->CONNECT;
358   CURLcode result = CURLE_OK;
359 
360   if(http->sending != HTTPSEND_REQUEST)
361     goto out;
362 
363   if(!ts->nsend) {
364     size_t fillcount;
365     k->upload_fromhere = data->state.ulbuf;
366     result = Curl_fillreadbuffer(data, data->set.upload_buffer_size,
367                                  &fillcount);
368     if(result)
369       goto out;
370     ts->nsend = fillcount;
371   }
372   if(ts->nsend) {
373     ssize_t bytes_written;
374     /* write to socket (send away data) */
375     result = Curl_write(data,
376                         conn->writesockfd,  /* socket to send to */
377                         k->upload_fromhere, /* buffer pointer */
378                         ts->nsend,          /* buffer size */
379                         &bytes_written);    /* actually sent */
380     if(result)
381       goto out;
382     /* send to debug callback! */
383     Curl_debug(data, CURLINFO_HEADER_OUT,
384                k->upload_fromhere, bytes_written);
385 
386     ts->nsend -= bytes_written;
387     k->upload_fromhere += bytes_written;
388   }
389   if(!ts->nsend)
390     http->sending = HTTPSEND_NADA;
391 
392 out:
393   if(result)
394     failf(data, "Failed sending CONNECT to proxy");
395   *done = (http->sending != HTTPSEND_REQUEST);
396   return result;
397 }
398 
on_resp_header(struct Curl_cfilter * cf,struct Curl_easy * data,struct tunnel_state * ts,const char * header)399 static CURLcode on_resp_header(struct Curl_cfilter *cf,
400                                struct Curl_easy *data,
401                                struct tunnel_state *ts,
402                                const char *header)
403 {
404   CURLcode result = CURLE_OK;
405   struct SingleRequest *k = &data->req;
406   (void)cf;
407 
408   if((checkprefix("WWW-Authenticate:", header) &&
409       (401 == k->httpcode)) ||
410      (checkprefix("Proxy-authenticate:", header) &&
411       (407 == k->httpcode))) {
412 
413     bool proxy = (k->httpcode == 407) ? TRUE : FALSE;
414     char *auth = Curl_copy_header_value(header);
415     if(!auth)
416       return CURLE_OUT_OF_MEMORY;
417 
418     DEBUGF(LOG_CF(data, cf, "CONNECT: fwd auth header '%s'", header));
419     result = Curl_http_input_auth(data, proxy, auth);
420 
421     free(auth);
422 
423     if(result)
424       return result;
425   }
426   else if(checkprefix("Content-Length:", header)) {
427     if(k->httpcode/100 == 2) {
428       /* A client MUST ignore any Content-Length or Transfer-Encoding
429          header fields received in a successful response to CONNECT.
430          "Successful" described as: 2xx (Successful). RFC 7231 4.3.6 */
431       infof(data, "Ignoring Content-Length in CONNECT %03d response",
432             k->httpcode);
433     }
434     else {
435       (void)curlx_strtoofft(header + strlen("Content-Length:"),
436                             NULL, 10, &ts->cl);
437     }
438   }
439   else if(Curl_compareheader(header,
440                              STRCONST("Connection:"), STRCONST("close")))
441     ts->close_connection = TRUE;
442   else if(checkprefix("Transfer-Encoding:", header)) {
443     if(k->httpcode/100 == 2) {
444       /* A client MUST ignore any Content-Length or Transfer-Encoding
445          header fields received in a successful response to CONNECT.
446          "Successful" described as: 2xx (Successful). RFC 7231 4.3.6 */
447       infof(data, "Ignoring Transfer-Encoding in "
448             "CONNECT %03d response", k->httpcode);
449     }
450     else if(Curl_compareheader(header,
451                                STRCONST("Transfer-Encoding:"),
452                                STRCONST("chunked"))) {
453       infof(data, "CONNECT responded chunked");
454       ts->chunked_encoding = TRUE;
455       /* init our chunky engine */
456       Curl_httpchunk_init(data);
457     }
458   }
459   else if(Curl_compareheader(header,
460                              STRCONST("Proxy-Connection:"),
461                              STRCONST("close")))
462     ts->close_connection = TRUE;
463   else if(!strncmp(header, "HTTP/1.", 7) &&
464           ((header[7] == '0') || (header[7] == '1')) &&
465           (header[8] == ' ') &&
466           ISDIGIT(header[9]) && ISDIGIT(header[10]) && ISDIGIT(header[11]) &&
467           !ISDIGIT(header[12])) {
468     /* store the HTTP code from the proxy */
469     data->info.httpproxycode =  k->httpcode = (header[9] - '0') * 100 +
470       (header[10] - '0') * 10 + (header[11] - '0');
471   }
472   return result;
473 }
474 
recv_CONNECT_resp(struct Curl_cfilter * cf,struct Curl_easy * data,struct tunnel_state * ts,bool * done)475 static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf,
476                                   struct Curl_easy *data,
477                                   struct tunnel_state *ts,
478                                   bool *done)
479 {
480   CURLcode result = CURLE_OK;
481   struct SingleRequest *k = &data->req;
482   curl_socket_t tunnelsocket = Curl_conn_cf_get_socket(cf, data);
483   char *linep;
484   size_t perline;
485   int error;
486 
487 #define SELECT_OK      0
488 #define SELECT_ERROR   1
489 
490   error = SELECT_OK;
491   *done = FALSE;
492 
493   if(!Curl_conn_data_pending(data, ts->sockindex))
494     return CURLE_OK;
495 
496   while(ts->keepon) {
497     ssize_t gotbytes;
498     char byte;
499 
500     /* Read one byte at a time to avoid a race condition. Wait at most one
501        second before looping to ensure continuous pgrsUpdates. */
502     result = Curl_read(data, tunnelsocket, &byte, 1, &gotbytes);
503     if(result == CURLE_AGAIN)
504       /* socket buffer drained, return */
505       return CURLE_OK;
506 
507     if(Curl_pgrsUpdate(data))
508       return CURLE_ABORTED_BY_CALLBACK;
509 
510     if(result) {
511       ts->keepon = KEEPON_DONE;
512       break;
513     }
514 
515     if(gotbytes <= 0) {
516       if(data->set.proxyauth && data->state.authproxy.avail &&
517          data->state.aptr.proxyuserpwd) {
518         /* proxy auth was requested and there was proxy auth available,
519            then deem this as "mere" proxy disconnect */
520         ts->close_connection = TRUE;
521         infof(data, "Proxy CONNECT connection closed");
522       }
523       else {
524         error = SELECT_ERROR;
525         failf(data, "Proxy CONNECT aborted");
526       }
527       ts->keepon = KEEPON_DONE;
528       break;
529     }
530 
531     if(ts->keepon == KEEPON_IGNORE) {
532       /* This means we are currently ignoring a response-body */
533 
534       if(ts->cl) {
535         /* A Content-Length based body: simply count down the counter
536            and make sure to break out of the loop when we're done! */
537         ts->cl--;
538         if(ts->cl <= 0) {
539           ts->keepon = KEEPON_DONE;
540           break;
541         }
542       }
543       else {
544         /* chunked-encoded body, so we need to do the chunked dance
545            properly to know when the end of the body is reached */
546         CHUNKcode r;
547         CURLcode extra;
548         ssize_t tookcareof = 0;
549 
550         /* now parse the chunked piece of data so that we can
551            properly tell when the stream ends */
552         r = Curl_httpchunk_read(data, &byte, 1, &tookcareof, &extra);
553         if(r == CHUNKE_STOP) {
554           /* we're done reading chunks! */
555           infof(data, "chunk reading DONE");
556           ts->keepon = KEEPON_DONE;
557         }
558       }
559       continue;
560     }
561 
562     if(Curl_dyn_addn(&ts->rcvbuf, &byte, 1)) {
563       failf(data, "CONNECT response too large");
564       return CURLE_RECV_ERROR;
565     }
566 
567     /* if this is not the end of a header line then continue */
568     if(byte != 0x0a)
569       continue;
570 
571     ts->headerlines++;
572     linep = Curl_dyn_ptr(&ts->rcvbuf);
573     perline = Curl_dyn_len(&ts->rcvbuf); /* amount of bytes in this line */
574 
575     /* output debug if that is requested */
576     Curl_debug(data, CURLINFO_HEADER_IN, linep, perline);
577 
578     if(!data->set.suppress_connect_headers) {
579       /* send the header to the callback */
580       int writetype = CLIENTWRITE_HEADER | CLIENTWRITE_CONNECT |
581         (data->set.include_header ? CLIENTWRITE_BODY : 0) |
582         (ts->headerlines == 1 ? CLIENTWRITE_STATUS : 0);
583 
584       result = Curl_client_write(data, writetype, linep, perline);
585       if(result)
586         return result;
587     }
588 
589     data->info.header_size += (long)perline;
590 
591     /* Newlines are CRLF, so the CR is ignored as the line isn't
592        really terminated until the LF comes. Treat a following CR
593        as end-of-headers as well.*/
594 
595     if(('\r' == linep[0]) ||
596        ('\n' == linep[0])) {
597       /* end of response-headers from the proxy */
598 
599       if((407 == k->httpcode) && !data->state.authproblem) {
600         /* If we get a 407 response code with content length
601            when we have no auth problem, we must ignore the
602            whole response-body */
603         ts->keepon = KEEPON_IGNORE;
604 
605         if(ts->cl) {
606           infof(data, "Ignore %" CURL_FORMAT_CURL_OFF_T
607                 " bytes of response-body", ts->cl);
608         }
609         else if(ts->chunked_encoding) {
610           CHUNKcode r;
611           CURLcode extra;
612 
613           infof(data, "Ignore chunked response-body");
614 
615           /* We set ignorebody true here since the chunked decoder
616              function will acknowledge that. Pay attention so that this is
617              cleared again when this function returns! */
618           k->ignorebody = TRUE;
619 
620           if(linep[1] == '\n')
621             /* this can only be a LF if the letter at index 0 was a CR */
622             linep++;
623 
624           /* now parse the chunked piece of data so that we can properly
625              tell when the stream ends */
626           r = Curl_httpchunk_read(data, linep + 1, 1, &gotbytes,
627                                   &extra);
628           if(r == CHUNKE_STOP) {
629             /* we're done reading chunks! */
630             infof(data, "chunk reading DONE");
631             ts->keepon = KEEPON_DONE;
632           }
633         }
634         else {
635           /* without content-length or chunked encoding, we
636              can't keep the connection alive since the close is
637              the end signal so we bail out at once instead */
638           DEBUGF(LOG_CF(data, cf, "CONNECT: no content-length or chunked"));
639           ts->keepon = KEEPON_DONE;
640         }
641       }
642       else {
643         ts->keepon = KEEPON_DONE;
644       }
645 
646       DEBUGASSERT(ts->keepon == KEEPON_IGNORE
647                   || ts->keepon == KEEPON_DONE);
648       continue;
649     }
650 
651     result = on_resp_header(cf, data, ts, linep);
652     if(result)
653       return result;
654 
655     Curl_dyn_reset(&ts->rcvbuf);
656   } /* while there's buffer left and loop is requested */
657 
658   if(error)
659     result = CURLE_RECV_ERROR;
660   *done = (ts->keepon == KEEPON_DONE);
661   if(!result && *done && data->info.httpproxycode/100 != 2) {
662     /* Deal with the possibly already received authenticate
663        headers. 'newurl' is set to a new URL if we must loop. */
664     result = Curl_http_auth_act(data);
665   }
666   return result;
667 }
668 
669 #else /* USE_HYPER */
670 /* The Hyper version of CONNECT */
start_CONNECT(struct Curl_cfilter * cf,struct Curl_easy * data,struct tunnel_state * ts)671 static CURLcode start_CONNECT(struct Curl_cfilter *cf,
672                               struct Curl_easy *data,
673                               struct tunnel_state *ts)
674 {
675   struct connectdata *conn = cf->conn;
676   struct hyptransfer *h = &data->hyp;
677   curl_socket_t tunnelsocket = Curl_conn_cf_get_socket(cf, data);
678   hyper_io *io = NULL;
679   hyper_request *req = NULL;
680   hyper_headers *headers = NULL;
681   hyper_clientconn_options *options = NULL;
682   hyper_task *handshake = NULL;
683   hyper_task *task = NULL; /* for the handshake */
684   hyper_clientconn *client = NULL;
685   hyper_task *sendtask = NULL; /* for the send */
686   char *hostheader = NULL; /* for CONNECT */
687   char *host = NULL; /* Host: */
688   CURLcode result = CURLE_OUT_OF_MEMORY;
689 
690   io = hyper_io_new();
691   if(!io) {
692     failf(data, "Couldn't create hyper IO");
693     result = CURLE_OUT_OF_MEMORY;
694     goto error;
695   }
696   /* tell Hyper how to read/write network data */
697   hyper_io_set_userdata(io, data);
698   hyper_io_set_read(io, Curl_hyper_recv);
699   hyper_io_set_write(io, Curl_hyper_send);
700   conn->sockfd = tunnelsocket;
701 
702   data->state.hconnect = TRUE;
703 
704   /* create an executor to poll futures */
705   if(!h->exec) {
706     h->exec = hyper_executor_new();
707     if(!h->exec) {
708       failf(data, "Couldn't create hyper executor");
709       result = CURLE_OUT_OF_MEMORY;
710       goto error;
711     }
712   }
713 
714   options = hyper_clientconn_options_new();
715   hyper_clientconn_options_set_preserve_header_case(options, 1);
716   hyper_clientconn_options_set_preserve_header_order(options, 1);
717 
718   if(!options) {
719     failf(data, "Couldn't create hyper client options");
720     result = CURLE_OUT_OF_MEMORY;
721     goto error;
722   }
723 
724   hyper_clientconn_options_exec(options, h->exec);
725 
726   /* "Both the `io` and the `options` are consumed in this function
727      call" */
728   handshake = hyper_clientconn_handshake(io, options);
729   if(!handshake) {
730     failf(data, "Couldn't create hyper client handshake");
731     result = CURLE_OUT_OF_MEMORY;
732     goto error;
733   }
734   io = NULL;
735   options = NULL;
736 
737   if(HYPERE_OK != hyper_executor_push(h->exec, handshake)) {
738     failf(data, "Couldn't hyper_executor_push the handshake");
739     result = CURLE_OUT_OF_MEMORY;
740     goto error;
741   }
742   handshake = NULL; /* ownership passed on */
743 
744   task = hyper_executor_poll(h->exec);
745   if(!task) {
746     failf(data, "Couldn't hyper_executor_poll the handshake");
747     result = CURLE_OUT_OF_MEMORY;
748     goto error;
749   }
750 
751   client = hyper_task_value(task);
752   hyper_task_free(task);
753   req = hyper_request_new();
754   if(!req) {
755     failf(data, "Couldn't hyper_request_new");
756     result = CURLE_OUT_OF_MEMORY;
757     goto error;
758   }
759   if(hyper_request_set_method(req, (uint8_t *)"CONNECT",
760                               strlen("CONNECT"))) {
761     failf(data, "error setting method");
762     result = CURLE_OUT_OF_MEMORY;
763     goto error;
764   }
765 
766   infof(data, "Establish HTTP proxy tunnel to %s:%d",
767         ts->hostname, ts->remote_port);
768 
769     /* This only happens if we've looped here due to authentication
770        reasons, and we don't really use the newly cloned URL here
771        then. Just free() it. */
772   Curl_safefree(data->req.newurl);
773 
774   result = CONNECT_host(data, conn, ts->hostname, ts->remote_port,
775                         &hostheader, &host);
776   if(result)
777     goto error;
778 
779   if(hyper_request_set_uri(req, (uint8_t *)hostheader,
780                            strlen(hostheader))) {
781     failf(data, "error setting path");
782     result = CURLE_OUT_OF_MEMORY;
783     goto error;
784   }
785   if(data->set.verbose) {
786     char *se = aprintf("CONNECT %s HTTP/1.1\r\n", hostheader);
787     if(!se) {
788       result = CURLE_OUT_OF_MEMORY;
789       goto error;
790     }
791     Curl_debug(data, CURLINFO_HEADER_OUT, se, strlen(se));
792     free(se);
793   }
794   /* Setup the proxy-authorization header, if any */
795   result = Curl_http_output_auth(data, conn, "CONNECT", HTTPREQ_GET,
796                                  hostheader, TRUE);
797   if(result)
798     goto error;
799   Curl_safefree(hostheader);
800 
801   /* default is 1.1 */
802   if((conn->http_proxy.proxytype == CURLPROXY_HTTP_1_0) &&
803      (HYPERE_OK != hyper_request_set_version(req,
804                                              HYPER_HTTP_VERSION_1_0))) {
805     failf(data, "error setting HTTP version");
806     result = CURLE_OUT_OF_MEMORY;
807     goto error;
808   }
809 
810   headers = hyper_request_headers(req);
811   if(!headers) {
812     failf(data, "hyper_request_headers");
813     result = CURLE_OUT_OF_MEMORY;
814     goto error;
815   }
816   if(host) {
817     result = Curl_hyper_header(data, headers, host);
818     if(result)
819       goto error;
820     Curl_safefree(host);
821   }
822 
823   if(data->state.aptr.proxyuserpwd) {
824     result = Curl_hyper_header(data, headers,
825                                data->state.aptr.proxyuserpwd);
826     if(result)
827       goto error;
828   }
829 
830   if(!Curl_checkProxyheaders(data, conn, STRCONST("User-Agent")) &&
831      data->set.str[STRING_USERAGENT]) {
832     struct dynbuf ua;
833     Curl_dyn_init(&ua, DYN_HTTP_REQUEST);
834     result = Curl_dyn_addf(&ua, "User-Agent: %s\r\n",
835                            data->set.str[STRING_USERAGENT]);
836     if(result)
837       goto error;
838     result = Curl_hyper_header(data, headers, Curl_dyn_ptr(&ua));
839     if(result)
840       goto error;
841     Curl_dyn_free(&ua);
842   }
843 
844   if(!Curl_checkProxyheaders(data, conn, STRCONST("Proxy-Connection"))) {
845     result = Curl_hyper_header(data, headers,
846                                "Proxy-Connection: Keep-Alive");
847     if(result)
848       goto error;
849   }
850 
851   result = Curl_add_custom_headers(data, TRUE, headers);
852   if(result)
853     goto error;
854 
855   sendtask = hyper_clientconn_send(client, req);
856   if(!sendtask) {
857     failf(data, "hyper_clientconn_send");
858     result = CURLE_OUT_OF_MEMORY;
859     goto error;
860   }
861 
862   if(HYPERE_OK != hyper_executor_push(h->exec, sendtask)) {
863     failf(data, "Couldn't hyper_executor_push the send");
864     result = CURLE_OUT_OF_MEMORY;
865     goto error;
866   }
867 
868 error:
869   free(host);
870   free(hostheader);
871   if(io)
872     hyper_io_free(io);
873   if(options)
874     hyper_clientconn_options_free(options);
875   if(handshake)
876     hyper_task_free(handshake);
877   if(client)
878     hyper_clientconn_free(client);
879   return result;
880 }
881 
send_CONNECT(struct Curl_easy * data,struct connectdata * conn,struct tunnel_state * ts,bool * done)882 static CURLcode send_CONNECT(struct Curl_easy *data,
883                              struct connectdata *conn,
884                              struct tunnel_state *ts,
885                              bool *done)
886 {
887   struct hyptransfer *h = &data->hyp;
888   hyper_task *task = NULL;
889   hyper_error *hypererr = NULL;
890   CURLcode result = CURLE_OK;
891 
892   (void)ts;
893   (void)conn;
894   do {
895     task = hyper_executor_poll(h->exec);
896     if(task) {
897       bool error = hyper_task_type(task) == HYPER_TASK_ERROR;
898       if(error)
899         hypererr = hyper_task_value(task);
900       hyper_task_free(task);
901       if(error) {
902         /* this could probably use a better error code? */
903         result = CURLE_OUT_OF_MEMORY;
904         goto error;
905       }
906     }
907   } while(task);
908 error:
909   *done = (result == CURLE_OK);
910   if(hypererr) {
911     uint8_t errbuf[256];
912     size_t errlen = hyper_error_print(hypererr, errbuf, sizeof(errbuf));
913     failf(data, "Hyper: %.*s", (int)errlen, errbuf);
914     hyper_error_free(hypererr);
915   }
916   return result;
917 }
918 
recv_CONNECT_resp(struct Curl_cfilter * cf,struct Curl_easy * data,struct tunnel_state * ts,bool * done)919 static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf,
920                                   struct Curl_easy *data,
921                                   struct tunnel_state *ts,
922                                   bool *done)
923 {
924   struct hyptransfer *h = &data->hyp;
925   CURLcode result;
926   int didwhat;
927 
928   (void)ts;
929   *done = FALSE;
930   result = Curl_hyper_stream(data, cf->conn, &didwhat, done,
931                              CURL_CSELECT_IN | CURL_CSELECT_OUT);
932   if(result || !*done)
933     return result;
934   if(h->exec) {
935     hyper_executor_free(h->exec);
936     h->exec = NULL;
937   }
938   if(h->read_waker) {
939     hyper_waker_free(h->read_waker);
940     h->read_waker = NULL;
941   }
942   if(h->write_waker) {
943     hyper_waker_free(h->write_waker);
944     h->write_waker = NULL;
945   }
946   return result;
947 }
948 
949 #endif /* USE_HYPER */
950 
CONNECT(struct Curl_cfilter * cf,struct Curl_easy * data,struct tunnel_state * ts)951 static CURLcode CONNECT(struct Curl_cfilter *cf,
952                         struct Curl_easy *data,
953                         struct tunnel_state *ts)
954 {
955   struct connectdata *conn = cf->conn;
956   CURLcode result;
957   bool done;
958 
959   if(tunnel_is_established(ts))
960     return CURLE_OK;
961   if(tunnel_is_failed(ts))
962     return CURLE_RECV_ERROR; /* Need a cfilter close and new bootstrap */
963 
964   do {
965     timediff_t check;
966 
967     check = Curl_timeleft(data, NULL, TRUE);
968     if(check <= 0) {
969       failf(data, "Proxy CONNECT aborted due to timeout");
970       result = CURLE_OPERATION_TIMEDOUT;
971       goto out;
972     }
973 
974     switch(ts->tunnel_state) {
975     case TUNNEL_INIT:
976       /* Prepare the CONNECT request and make a first attempt to send. */
977       DEBUGF(LOG_CF(data, cf, "CONNECT start"));
978       result = start_CONNECT(cf, data, ts);
979       if(result)
980         goto out;
981       tunnel_go_state(cf, ts, TUNNEL_CONNECT, data);
982       /* FALLTHROUGH */
983 
984     case TUNNEL_CONNECT:
985       /* see that the request is completely sent */
986       DEBUGF(LOG_CF(data, cf, "CONNECT send"));
987       result = send_CONNECT(data, cf->conn, ts, &done);
988       if(result || !done)
989         goto out;
990       tunnel_go_state(cf, ts, TUNNEL_RECEIVE, data);
991       /* FALLTHROUGH */
992 
993     case TUNNEL_RECEIVE:
994       /* read what is there */
995       DEBUGF(LOG_CF(data, cf, "CONNECT receive"));
996       result = recv_CONNECT_resp(cf, data, ts, &done);
997       if(Curl_pgrsUpdate(data)) {
998         result = CURLE_ABORTED_BY_CALLBACK;
999         goto out;
1000       }
1001       /* error or not complete yet. return for more multi-multi */
1002       if(result || !done)
1003         goto out;
1004       /* got it */
1005       tunnel_go_state(cf, ts, TUNNEL_RESPONSE, data);
1006       /* FALLTHROUGH */
1007 
1008     case TUNNEL_RESPONSE:
1009       DEBUGF(LOG_CF(data, cf, "CONNECT response"));
1010       if(data->req.newurl) {
1011         /* not the "final" response, we need to do a follow up request.
1012          * If the other side indicated a connection close, or if someone
1013          * else told us to close this connection, do so now.
1014          */
1015         if(ts->close_connection || conn->bits.close) {
1016           /* Close this filter and the sub-chain, re-connect the
1017            * sub-chain and continue. Closing this filter will
1018            * reset our tunnel state. To avoid recursion, we return
1019            * and expect to be called again.
1020            */
1021           DEBUGF(LOG_CF(data, cf, "CONNECT need to close+open"));
1022           infof(data, "Connect me again please");
1023           Curl_conn_cf_close(cf, data);
1024           connkeep(conn, "HTTP proxy CONNECT");
1025           result = Curl_conn_cf_connect(cf->next, data, FALSE, &done);
1026           goto out;
1027         }
1028         else {
1029           /* staying on this connection, reset state */
1030           tunnel_go_state(cf, ts, TUNNEL_INIT, data);
1031         }
1032       }
1033       break;
1034 
1035     default:
1036       break;
1037     }
1038 
1039   } while(data->req.newurl);
1040 
1041   DEBUGASSERT(ts->tunnel_state == TUNNEL_RESPONSE);
1042   if(data->info.httpproxycode/100 != 2) {
1043     /* a non-2xx response and we have no next url to try. */
1044     free(data->req.newurl);
1045     data->req.newurl = NULL;
1046     /* failure, close this connection to avoid re-use */
1047     streamclose(conn, "proxy CONNECT failure");
1048     tunnel_go_state(cf, ts, TUNNEL_FAILED, data);
1049     failf(data, "CONNECT tunnel failed, response %d", data->req.httpcode);
1050     return CURLE_RECV_ERROR;
1051   }
1052   /* 2xx response, SUCCESS! */
1053   tunnel_go_state(cf, ts, TUNNEL_ESTABLISHED, data);
1054   infof(data, "CONNECT tunnel established, response %d",
1055         data->info.httpproxycode);
1056   result = CURLE_OK;
1057 
1058 out:
1059   if(result)
1060     tunnel_go_state(cf, ts, TUNNEL_FAILED, data);
1061   return result;
1062 }
1063 
http_proxy_cf_connect(struct Curl_cfilter * cf,struct Curl_easy * data,bool blocking,bool * done)1064 static CURLcode http_proxy_cf_connect(struct Curl_cfilter *cf,
1065                                       struct Curl_easy *data,
1066                                       bool blocking, bool *done)
1067 {
1068   CURLcode result;
1069   struct tunnel_state *ts = cf->ctx;
1070 
1071   if(cf->connected) {
1072     *done = TRUE;
1073     return CURLE_OK;
1074   }
1075 
1076   DEBUGF(LOG_CF(data, cf, "connect"));
1077   result = cf->next->cft->connect(cf->next, data, blocking, done);
1078   if(result || !*done)
1079     return result;
1080 
1081   DEBUGF(LOG_CF(data, cf, "subchain is connected"));
1082   /* TODO: can we do blocking? */
1083   /* We want "seamless" operations through HTTP proxy tunnel */
1084 
1085   /* for the secondary socket (FTP), use the "connect to host"
1086    * but ignore the "connect to port" (use the secondary port)
1087    */
1088   *done = FALSE;
1089   if(!ts) {
1090     result = tunnel_init(&ts, data, cf->conn, cf->sockindex);
1091     if(result)
1092       return result;
1093     cf->ctx = ts;
1094   }
1095 
1096   result = CONNECT(cf, data, ts);
1097   if(result)
1098     goto out;
1099   Curl_safefree(data->state.aptr.proxyuserpwd);
1100 
1101 out:
1102   *done = (result == CURLE_OK) && tunnel_is_established(cf->ctx);
1103   if (*done) {
1104     cf->connected = TRUE;
1105     tunnel_free(cf, data);
1106   }
1107   return result;
1108 }
1109 
http_proxy_cf_get_host(struct Curl_cfilter * cf,struct Curl_easy * data,const char ** phost,const char ** pdisplay_host,int * pport)1110 static void http_proxy_cf_get_host(struct Curl_cfilter *cf,
1111                                    struct Curl_easy *data,
1112                                    const char **phost,
1113                                    const char **pdisplay_host,
1114                                    int *pport)
1115 {
1116   (void)data;
1117   if(!cf->connected) {
1118     *phost = cf->conn->http_proxy.host.name;
1119     *pdisplay_host = cf->conn->http_proxy.host.dispname;
1120     *pport = (int)cf->conn->http_proxy.port;
1121   }
1122   else {
1123     cf->next->cft->get_host(cf->next, data, phost, pdisplay_host, pport);
1124   }
1125 }
1126 
http_proxy_cf_get_select_socks(struct Curl_cfilter * cf,struct Curl_easy * data,curl_socket_t * socks)1127 static int http_proxy_cf_get_select_socks(struct Curl_cfilter *cf,
1128                                           struct Curl_easy *data,
1129                                           curl_socket_t *socks)
1130 {
1131   struct tunnel_state *ts = cf->ctx;
1132   int fds;
1133 
1134   fds = cf->next->cft->get_select_socks(cf->next, data, socks);
1135   if(!fds && cf->next->connected && !cf->connected) {
1136     /* If we are not connected, but the filter "below" is
1137      * and not waiting on something, we are tunneling. */
1138     socks[0] = Curl_conn_cf_get_socket(cf, data);
1139     if(ts) {
1140       /* when we've sent a CONNECT to a proxy, we should rather either
1141          wait for the socket to become readable to be able to get the
1142          response headers or if we're still sending the request, wait
1143          for write. */
1144       if(ts->CONNECT.sending == HTTPSEND_REQUEST) {
1145         return GETSOCK_WRITESOCK(0);
1146       }
1147       return GETSOCK_READSOCK(0);
1148     }
1149     return GETSOCK_WRITESOCK(0);
1150   }
1151   return fds;
1152 }
1153 
http_proxy_cf_destroy(struct Curl_cfilter * cf,struct Curl_easy * data)1154 static void http_proxy_cf_destroy(struct Curl_cfilter *cf,
1155                                   struct Curl_easy *data)
1156 {
1157   DEBUGF(LOG_CF(data, cf, "destroy"));
1158   tunnel_free(cf, data);
1159 }
1160 
http_proxy_cf_close(struct Curl_cfilter * cf,struct Curl_easy * data)1161 static void http_proxy_cf_close(struct Curl_cfilter *cf,
1162                                 struct Curl_easy *data)
1163 {
1164   DEBUGASSERT(cf->next);
1165   DEBUGF(LOG_CF(data, cf, "close"));
1166   cf->connected = FALSE;
1167   cf->next->cft->close(cf->next, data);
1168   if(cf->ctx) {
1169     tunnel_go_state(cf, cf->ctx, TUNNEL_INIT, data);
1170   }
1171 }
1172 
1173 
1174 struct Curl_cftype Curl_cft_http_proxy = {
1175   "HTTP-PROXY",
1176   CF_TYPE_IP_CONNECT,
1177   0,
1178   http_proxy_cf_destroy,
1179   http_proxy_cf_connect,
1180   http_proxy_cf_close,
1181   http_proxy_cf_get_host,
1182   http_proxy_cf_get_select_socks,
1183   Curl_cf_def_data_pending,
1184   Curl_cf_def_send,
1185   Curl_cf_def_recv,
1186   Curl_cf_def_cntrl,
1187   Curl_cf_def_conn_is_alive,
1188   Curl_cf_def_conn_keep_alive,
1189   Curl_cf_def_query,
1190 };
1191 
Curl_conn_http_proxy_add(struct Curl_easy * data,struct connectdata * conn,int sockindex)1192 CURLcode Curl_conn_http_proxy_add(struct Curl_easy *data,
1193                                   struct connectdata *conn,
1194                                   int sockindex)
1195 {
1196   struct Curl_cfilter *cf;
1197   CURLcode result;
1198 
1199   result = Curl_cf_create(&cf, &Curl_cft_http_proxy, NULL);
1200   if(!result)
1201     Curl_conn_cf_add(data, conn, sockindex, cf);
1202   return result;
1203 }
1204 
Curl_cf_http_proxy_insert_after(struct Curl_cfilter * cf_at,struct Curl_easy * data)1205 CURLcode Curl_cf_http_proxy_insert_after(struct Curl_cfilter *cf_at,
1206                                          struct Curl_easy *data)
1207 {
1208   struct Curl_cfilter *cf;
1209   CURLcode result;
1210 
1211   (void)data;
1212   result = Curl_cf_create(&cf, &Curl_cft_http_proxy, NULL);
1213   if(!result)
1214     Curl_conn_cf_insert_after(cf_at, cf);
1215   return result;
1216 }
1217 
1218 #endif /* ! CURL_DISABLE_HTTP */
1219 
1220 
1221 typedef enum {
1222     HAPROXY_INIT,     /* init/default/no tunnel state */
1223     HAPROXY_SEND,     /* data_out being sent */
1224     HAPROXY_DONE      /* all work done */
1225 } haproxy_state;
1226 
1227 struct cf_haproxy_ctx {
1228   int state;
1229   struct dynbuf data_out;
1230 };
1231 
cf_haproxy_ctx_reset(struct cf_haproxy_ctx * ctx)1232 static void cf_haproxy_ctx_reset(struct cf_haproxy_ctx *ctx)
1233 {
1234   DEBUGASSERT(ctx);
1235   ctx->state = HAPROXY_INIT;
1236   Curl_dyn_reset(&ctx->data_out);
1237 }
1238 
cf_haproxy_ctx_free(struct cf_haproxy_ctx * ctx)1239 static void cf_haproxy_ctx_free(struct cf_haproxy_ctx *ctx)
1240 {
1241   if(ctx) {
1242     Curl_dyn_free(&ctx->data_out);
1243     free(ctx);
1244   }
1245 }
1246 
cf_haproxy_date_out_set(struct Curl_cfilter * cf,struct Curl_easy * data)1247 static CURLcode cf_haproxy_date_out_set(struct Curl_cfilter*cf,
1248                                         struct Curl_easy *data)
1249 {
1250   struct cf_haproxy_ctx *ctx = cf->ctx;
1251   CURLcode result;
1252   const char *tcp_version;
1253 
1254   DEBUGASSERT(ctx);
1255   DEBUGASSERT(ctx->state == HAPROXY_INIT);
1256 #ifdef USE_UNIX_SOCKETS
1257   if(cf->conn->unix_domain_socket)
1258     /* the buffer is large enough to hold this! */
1259     result = Curl_dyn_addn(&ctx->data_out, STRCONST("PROXY UNKNOWN\r\n"));
1260   else {
1261 #endif /* USE_UNIX_SOCKETS */
1262   /* Emit the correct prefix for IPv6 */
1263   tcp_version = cf->conn->bits.ipv6 ? "TCP6" : "TCP4";
1264 
1265   result = Curl_dyn_addf(&ctx->data_out, "PROXY %s %s %s %i %i\r\n",
1266                          tcp_version,
1267                          data->info.conn_local_ip,
1268                          data->info.conn_primary_ip,
1269                          data->info.conn_local_port,
1270                          data->info.conn_primary_port);
1271 
1272 #ifdef USE_UNIX_SOCKETS
1273   }
1274 #endif /* USE_UNIX_SOCKETS */
1275   return result;
1276 }
1277 
cf_haproxy_connect(struct Curl_cfilter * cf,struct Curl_easy * data,bool blocking,bool * done)1278 static CURLcode cf_haproxy_connect(struct Curl_cfilter *cf,
1279                                    struct Curl_easy *data,
1280                                    bool blocking, bool *done)
1281 {
1282   struct cf_haproxy_ctx *ctx = cf->ctx;
1283   CURLcode result;
1284   size_t len;
1285 
1286   DEBUGASSERT(ctx);
1287   if(cf->connected) {
1288     *done = TRUE;
1289     return CURLE_OK;
1290   }
1291 
1292   result = cf->next->cft->connect(cf->next, data, blocking, done);
1293   if(result || !*done)
1294     return result;
1295 
1296   switch(ctx->state) {
1297   case HAPROXY_INIT:
1298     result = cf_haproxy_date_out_set(cf, data);
1299     if(result)
1300       goto out;
1301     ctx->state = HAPROXY_SEND;
1302     /* FALLTHROUGH */
1303   case HAPROXY_SEND:
1304     len = Curl_dyn_len(&ctx->data_out);
1305     if(len > 0) {
1306       ssize_t written = Curl_conn_send(data, cf->sockindex,
1307                                        Curl_dyn_ptr(&ctx->data_out),
1308                                        len, &result);
1309       if(written < 0)
1310         goto out;
1311       Curl_dyn_tail(&ctx->data_out, len - (size_t)written);
1312       if(Curl_dyn_len(&ctx->data_out) > 0) {
1313         result = CURLE_OK;
1314         goto out;
1315       }
1316     }
1317     ctx->state = HAPROXY_DONE;
1318     /* FALLTHROUGH */
1319   default:
1320     Curl_dyn_free(&ctx->data_out);
1321     break;
1322   }
1323 
1324 out:
1325   *done = (!result) && (ctx->state == HAPROXY_DONE);
1326   cf->connected = *done;
1327   return result;
1328 }
1329 
cf_haproxy_destroy(struct Curl_cfilter * cf,struct Curl_easy * data)1330 static void cf_haproxy_destroy(struct Curl_cfilter *cf,
1331                                struct Curl_easy *data)
1332 {
1333   (void)data;
1334   DEBUGF(LOG_CF(data, cf, "destroy"));
1335   cf_haproxy_ctx_free(cf->ctx);
1336 }
1337 
cf_haproxy_close(struct Curl_cfilter * cf,struct Curl_easy * data)1338 static void cf_haproxy_close(struct Curl_cfilter *cf,
1339                              struct Curl_easy *data)
1340 {
1341   DEBUGF(LOG_CF(data, cf, "close"));
1342   cf->connected = FALSE;
1343   cf_haproxy_ctx_reset(cf->ctx);
1344   if(cf->next)
1345     cf->next->cft->close(cf->next, data);
1346 }
1347 
cf_haproxy_get_select_socks(struct Curl_cfilter * cf,struct Curl_easy * data,curl_socket_t * socks)1348 static int cf_haproxy_get_select_socks(struct Curl_cfilter *cf,
1349                                        struct Curl_easy *data,
1350                                        curl_socket_t *socks)
1351 {
1352   int fds;
1353 
1354   fds = cf->next->cft->get_select_socks(cf->next, data, socks);
1355   if(!fds && cf->next->connected && !cf->connected) {
1356     /* If we are not connected, but the filter "below" is
1357      * and not waiting on something, we are sending. */
1358     socks[0] = Curl_conn_cf_get_socket(cf, data);
1359     return GETSOCK_WRITESOCK(0);
1360   }
1361   return fds;
1362 }
1363 
1364 
1365 struct Curl_cftype Curl_cft_haproxy = {
1366   "HAPROXY",
1367   0,
1368   0,
1369   cf_haproxy_destroy,
1370   cf_haproxy_connect,
1371   cf_haproxy_close,
1372   Curl_cf_def_get_host,
1373   cf_haproxy_get_select_socks,
1374   Curl_cf_def_data_pending,
1375   Curl_cf_def_send,
1376   Curl_cf_def_recv,
1377   Curl_cf_def_cntrl,
1378   Curl_cf_def_conn_is_alive,
1379   Curl_cf_def_conn_keep_alive,
1380   Curl_cf_def_query,
1381 };
1382 
cf_haproxy_create(struct Curl_cfilter ** pcf,struct Curl_easy * data)1383 static CURLcode cf_haproxy_create(struct Curl_cfilter **pcf,
1384                                   struct Curl_easy *data)
1385 {
1386   struct Curl_cfilter *cf = NULL;
1387   struct cf_haproxy_ctx *ctx;
1388   CURLcode result;
1389 
1390   (void)data;
1391   ctx = calloc(sizeof(*ctx), 1);
1392   if(!ctx) {
1393     result = CURLE_OUT_OF_MEMORY;
1394     goto out;
1395   }
1396   ctx->state = HAPROXY_INIT;
1397   Curl_dyn_init(&ctx->data_out, DYN_HAXPROXY);
1398 
1399   result = Curl_cf_create(&cf, &Curl_cft_haproxy, ctx);
1400   if(result)
1401     goto out;
1402   ctx = NULL;
1403 
1404 out:
1405   cf_haproxy_ctx_free(ctx);
1406   *pcf = result? NULL : cf;
1407   return result;
1408 }
1409 
Curl_conn_haproxy_add(struct Curl_easy * data,struct connectdata * conn,int sockindex)1410 CURLcode Curl_conn_haproxy_add(struct Curl_easy *data,
1411                                struct connectdata *conn,
1412                                int sockindex)
1413 {
1414   struct Curl_cfilter *cf;
1415   CURLcode result;
1416 
1417   result = cf_haproxy_create(&cf, data);
1418   if(result)
1419     goto out;
1420   Curl_conn_cf_add(data, conn, sockindex, cf);
1421 
1422 out:
1423   return result;
1424 }
1425 
Curl_cf_haproxy_insert_after(struct Curl_cfilter * cf_at,struct Curl_easy * data)1426 CURLcode Curl_cf_haproxy_insert_after(struct Curl_cfilter *cf_at,
1427                                       struct Curl_easy *data)
1428 {
1429   struct Curl_cfilter *cf;
1430   CURLcode result;
1431 
1432   result = cf_haproxy_create(&cf, data);
1433   if(result)
1434     goto out;
1435   Curl_conn_cf_insert_after(cf_at, cf);
1436 
1437 out:
1438   return result;
1439 }
1440 
1441 #endif /* !CURL_DISABLE_PROXY */
1442