xref: /curl/lib/connect.c (revision 9cc24640)
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 #ifdef HAVE_NETINET_IN_H
28 #include <netinet/in.h> /* <netinet/tcp.h> may need it */
29 #endif
30 #ifdef HAVE_SYS_UN_H
31 #include <sys/un.h> /* for sockaddr_un */
32 #endif
33 #ifdef HAVE_LINUX_TCP_H
34 #include <linux/tcp.h>
35 #elif defined(HAVE_NETINET_TCP_H)
36 #include <netinet/tcp.h>
37 #endif
38 #ifdef HAVE_SYS_IOCTL_H
39 #include <sys/ioctl.h>
40 #endif
41 #ifdef HAVE_NETDB_H
42 #include <netdb.h>
43 #endif
44 #ifdef HAVE_FCNTL_H
45 #include <fcntl.h>
46 #endif
47 #ifdef HAVE_ARPA_INET_H
48 #include <arpa/inet.h>
49 #endif
50 
51 #ifdef __VMS
52 #include <in.h>
53 #include <inet.h>
54 #endif
55 
56 #include "urldata.h"
57 #include "sendf.h"
58 #include "if2ip.h"
59 #include "strerror.h"
60 #include "cfilters.h"
61 #include "connect.h"
62 #include "cf-haproxy.h"
63 #include "cf-https-connect.h"
64 #include "cf-socket.h"
65 #include "select.h"
66 #include "url.h" /* for Curl_safefree() */
67 #include "multiif.h"
68 #include "sockaddr.h" /* required for Curl_sockaddr_storage */
69 #include "inet_ntop.h"
70 #include "inet_pton.h"
71 #include "vtls/vtls.h" /* for vtsl cfilters */
72 #include "progress.h"
73 #include "warnless.h"
74 #include "conncache.h"
75 #include "multihandle.h"
76 #include "share.h"
77 #include "version_win32.h"
78 #include "vquic/vquic.h" /* for quic cfilters */
79 #include "http_proxy.h"
80 #include "socks.h"
81 
82 /* The last 3 #include files should be in this order */
83 #include "curl_printf.h"
84 #include "curl_memory.h"
85 #include "memdebug.h"
86 
87 #ifndef ARRAYSIZE
88 #define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0]))
89 #endif
90 
91 /*
92  * Curl_timeleft() returns the amount of milliseconds left allowed for the
93  * transfer/connection. If the value is 0, there is no timeout (ie there is
94  * infinite time left). If the value is negative, the timeout time has already
95  * elapsed.
96  * @param data the transfer to check on
97  * @param nowp timestamp to use for calculation, NULL to use Curl_now()
98  * @param duringconnect TRUE iff connect timeout is also taken into account.
99  * @unittest: 1303
100  */
Curl_timeleft(struct Curl_easy * data,struct curltime * nowp,bool duringconnect)101 timediff_t Curl_timeleft(struct Curl_easy *data,
102                          struct curltime *nowp,
103                          bool duringconnect)
104 {
105   timediff_t timeleft_ms = 0;
106   timediff_t ctimeleft_ms = 0;
107   struct curltime now;
108 
109   /* The duration of a connect and the total transfer are calculated from two
110      different time-stamps. It can end up with the total timeout being reached
111      before the connect timeout expires and we must acknowledge whichever
112      timeout that is reached first. The total timeout is set per entire
113      operation, while the connect timeout is set per connect. */
114   if(data->set.timeout <= 0 && !duringconnect)
115     return 0; /* no timeout in place or checked, return "no limit" */
116 
117   if(!nowp) {
118     now = Curl_now();
119     nowp = &now;
120   }
121 
122   if(data->set.timeout > 0) {
123     timeleft_ms = data->set.timeout -
124                   Curl_timediff(*nowp, data->progress.t_startop);
125     if(!timeleft_ms)
126       timeleft_ms = -1; /* 0 is "no limit", fake 1 ms expiry */
127     if(!duringconnect)
128       return timeleft_ms; /* no connect check, this is it */
129   }
130 
131   if(duringconnect) {
132     timediff_t ctimeout_ms = (data->set.connecttimeout > 0) ?
133       data->set.connecttimeout : DEFAULT_CONNECT_TIMEOUT;
134     ctimeleft_ms = ctimeout_ms -
135                    Curl_timediff(*nowp, data->progress.t_startsingle);
136     if(!ctimeleft_ms)
137       ctimeleft_ms = -1; /* 0 is "no limit", fake 1 ms expiry */
138     if(!timeleft_ms)
139       return ctimeleft_ms; /* no general timeout, this is it */
140   }
141   /* return minimal time left or max amount already expired */
142   return (ctimeleft_ms < timeleft_ms) ? ctimeleft_ms : timeleft_ms;
143 }
144 
Curl_shutdown_start(struct Curl_easy * data,int sockindex,struct curltime * nowp)145 void Curl_shutdown_start(struct Curl_easy *data, int sockindex,
146                          struct curltime *nowp)
147 {
148   struct curltime now;
149 
150   DEBUGASSERT(data->conn);
151   if(!nowp) {
152     now = Curl_now();
153     nowp = &now;
154   }
155   data->conn->shutdown.start[sockindex] = *nowp;
156   data->conn->shutdown.timeout_ms = (data->set.shutdowntimeout > 0) ?
157     data->set.shutdowntimeout : DEFAULT_SHUTDOWN_TIMEOUT_MS;
158 }
159 
Curl_shutdown_timeleft(struct connectdata * conn,int sockindex,struct curltime * nowp)160 timediff_t Curl_shutdown_timeleft(struct connectdata *conn, int sockindex,
161                                   struct curltime *nowp)
162 {
163   struct curltime now;
164   timediff_t left_ms;
165 
166   if(!conn->shutdown.start[sockindex].tv_sec || !conn->shutdown.timeout_ms)
167     return 0; /* not started or no limits */
168 
169   if(!nowp) {
170     now = Curl_now();
171     nowp = &now;
172   }
173   left_ms = conn->shutdown.timeout_ms -
174             Curl_timediff(*nowp, conn->shutdown.start[sockindex]);
175   return left_ms ? left_ms : -1;
176 }
177 
Curl_conn_shutdown_timeleft(struct connectdata * conn,struct curltime * nowp)178 timediff_t Curl_conn_shutdown_timeleft(struct connectdata *conn,
179                                        struct curltime *nowp)
180 {
181   timediff_t left_ms = 0, ms;
182   struct curltime now;
183   int i;
184 
185   for(i = 0; conn->shutdown.timeout_ms && (i < 2); ++i) {
186     if(!conn->shutdown.start[i].tv_sec)
187       continue;
188     if(!nowp) {
189       now = Curl_now();
190       nowp = &now;
191     }
192     ms = Curl_shutdown_timeleft(conn, i, nowp);
193     if(ms && (!left_ms || ms < left_ms))
194       left_ms = ms;
195   }
196   return left_ms;
197 }
198 
Curl_shutdown_clear(struct Curl_easy * data,int sockindex)199 void Curl_shutdown_clear(struct Curl_easy *data, int sockindex)
200 {
201   struct curltime *pt = &data->conn->shutdown.start[sockindex];
202   memset(pt, 0, sizeof(*pt));
203 }
204 
Curl_shutdown_started(struct Curl_easy * data,int sockindex)205 bool Curl_shutdown_started(struct Curl_easy *data, int sockindex)
206 {
207   struct curltime *pt = &data->conn->shutdown.start[sockindex];
208   return (pt->tv_sec > 0) || (pt->tv_usec > 0);
209 }
210 
211 static const struct Curl_addrinfo *
addr_first_match(const struct Curl_addrinfo * addr,int family)212 addr_first_match(const struct Curl_addrinfo *addr, int family)
213 {
214   while(addr) {
215     if(addr->ai_family == family)
216       return addr;
217     addr = addr->ai_next;
218   }
219   return NULL;
220 }
221 
222 static const struct Curl_addrinfo *
addr_next_match(const struct Curl_addrinfo * addr,int family)223 addr_next_match(const struct Curl_addrinfo *addr, int family)
224 {
225   while(addr && addr->ai_next) {
226     addr = addr->ai_next;
227     if(addr->ai_family == family)
228       return addr;
229   }
230   return NULL;
231 }
232 
233 /* retrieves ip address and port from a sockaddr structure.
234    note it calls Curl_inet_ntop which sets errno on fail, not SOCKERRNO. */
Curl_addr2string(struct sockaddr * sa,curl_socklen_t salen,char * addr,int * port)235 bool Curl_addr2string(struct sockaddr *sa, curl_socklen_t salen,
236                       char *addr, int *port)
237 {
238   struct sockaddr_in *si = NULL;
239 #ifdef USE_IPV6
240   struct sockaddr_in6 *si6 = NULL;
241 #endif
242 #if (defined(HAVE_SYS_UN_H) || defined(WIN32_SOCKADDR_UN)) && defined(AF_UNIX)
243   struct sockaddr_un *su = NULL;
244 #else
245   (void)salen;
246 #endif
247 
248   switch(sa->sa_family) {
249     case AF_INET:
250       si = (struct sockaddr_in *)(void *) sa;
251       if(Curl_inet_ntop(sa->sa_family, &si->sin_addr,
252                         addr, MAX_IPADR_LEN)) {
253         unsigned short us_port = ntohs(si->sin_port);
254         *port = us_port;
255         return TRUE;
256       }
257       break;
258 #ifdef USE_IPV6
259     case AF_INET6:
260       si6 = (struct sockaddr_in6 *)(void *) sa;
261       if(Curl_inet_ntop(sa->sa_family, &si6->sin6_addr,
262                         addr, MAX_IPADR_LEN)) {
263         unsigned short us_port = ntohs(si6->sin6_port);
264         *port = us_port;
265         return TRUE;
266       }
267       break;
268 #endif
269 #if (defined(HAVE_SYS_UN_H) || defined(WIN32_SOCKADDR_UN)) && defined(AF_UNIX)
270     case AF_UNIX:
271       if(salen > (curl_socklen_t)sizeof(CURL_SA_FAMILY_T)) {
272         su = (struct sockaddr_un*)sa;
273         msnprintf(addr, MAX_IPADR_LEN, "%s", su->sun_path);
274       }
275       else
276         addr[0] = 0; /* socket with no name */
277       *port = 0;
278       return TRUE;
279 #endif
280     default:
281       break;
282   }
283 
284   addr[0] = '\0';
285   *port = 0;
286   errno = EAFNOSUPPORT;
287   return FALSE;
288 }
289 
290 /*
291  * Used to extract socket and connectdata struct for the most recent
292  * transfer on the given Curl_easy.
293  *
294  * The returned socket will be CURL_SOCKET_BAD in case of failure!
295  */
Curl_getconnectinfo(struct Curl_easy * data,struct connectdata ** connp)296 curl_socket_t Curl_getconnectinfo(struct Curl_easy *data,
297                                   struct connectdata **connp)
298 {
299   DEBUGASSERT(data);
300 
301   /* this works for an easy handle:
302    * - that has been used for curl_easy_perform()
303    * - that is associated with a multi handle, and whose connection
304    *   was detached with CURLOPT_CONNECT_ONLY
305    */
306   if(data->state.lastconnect_id != -1) {
307     struct connectdata *conn;
308 
309     conn = Curl_cpool_get_conn(data, data->state.lastconnect_id);
310     if(!conn) {
311       data->state.lastconnect_id = -1;
312       return CURL_SOCKET_BAD;
313     }
314 
315     if(connp)
316       /* only store this if the caller cares for it */
317       *connp = conn;
318     return conn->sock[FIRSTSOCKET];
319   }
320   return CURL_SOCKET_BAD;
321 }
322 
323 /*
324  * Curl_conncontrol() marks streams or connection for closure.
325  */
Curl_conncontrol(struct connectdata * conn,int ctrl,const char * reason)326 void Curl_conncontrol(struct connectdata *conn,
327                       int ctrl /* see defines in header */
328 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
329                       , const char *reason
330 #endif
331   )
332 {
333   /* close if a connection, or a stream that is not multiplexed. */
334   /* This function will be called both before and after this connection is
335      associated with a transfer. */
336   bool closeit, is_multiplex;
337   DEBUGASSERT(conn);
338 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
339   (void)reason; /* useful for debugging */
340 #endif
341   is_multiplex = Curl_conn_is_multiplex(conn, FIRSTSOCKET);
342   closeit = (ctrl == CONNCTRL_CONNECTION) ||
343     ((ctrl == CONNCTRL_STREAM) && !is_multiplex);
344   if((ctrl == CONNCTRL_STREAM) && is_multiplex)
345     ;  /* stream signal on multiplex conn never affects close state */
346   else if((bit)closeit != conn->bits.close) {
347     conn->bits.close = closeit; /* the only place in the source code that
348                                    should assign this bit */
349   }
350 }
351 
352 /**
353  * job walking the matching addr infos, creating a sub-cfilter with the
354  * provided method `cf_create` and running setup/connect on it.
355  */
356 struct eyeballer {
357   const char *name;
358   const struct Curl_addrinfo *first; /* complete address list, not owned */
359   const struct Curl_addrinfo *addr;  /* List of addresses to try, not owned */
360   int ai_family;                     /* matching address family only */
361   cf_ip_connect_create *cf_create;   /* for creating cf */
362   struct Curl_cfilter *cf;           /* current sub-cfilter connecting */
363   struct eyeballer *primary;         /* eyeballer this one is backup for */
364   timediff_t delay_ms;               /* delay until start */
365   struct curltime started;           /* start of current attempt */
366   timediff_t timeoutms;              /* timeout for current attempt */
367   expire_id timeout_id;              /* ID for Curl_expire() */
368   CURLcode result;
369   int error;
370   BIT(rewinded);                     /* if we rewinded the addr list */
371   BIT(has_started);                  /* attempts have started */
372   BIT(is_done);                      /* out of addresses/time */
373   BIT(connected);                    /* cf has connected */
374   BIT(shutdown);                     /* cf has shutdown */
375   BIT(inconclusive);                 /* connect was not a hard failure, we
376                                       * might talk to a restarting server */
377 };
378 
379 
380 typedef enum {
381   SCFST_INIT,
382   SCFST_WAITING,
383   SCFST_DONE
384 } cf_connect_state;
385 
386 struct cf_he_ctx {
387   int transport;
388   cf_ip_connect_create *cf_create;
389   const struct Curl_dns_entry *remotehost;
390   cf_connect_state state;
391   struct eyeballer *baller[2];
392   struct eyeballer *winner;
393   struct curltime started;
394 };
395 
396 /* when there are more than one IP address left to use, this macro returns how
397    much of the given timeout to spend on *this* attempt */
398 #define TIMEOUT_LARGE 600
399 #define USETIME(ms) ((ms > TIMEOUT_LARGE) ? (ms / 2) : ms)
400 
eyeballer_new(struct eyeballer ** pballer,cf_ip_connect_create * cf_create,const struct Curl_addrinfo * addr,int ai_family,struct eyeballer * primary,timediff_t delay_ms,timediff_t timeout_ms,expire_id timeout_id)401 static CURLcode eyeballer_new(struct eyeballer **pballer,
402                               cf_ip_connect_create *cf_create,
403                               const struct Curl_addrinfo *addr,
404                               int ai_family,
405                               struct eyeballer *primary,
406                               timediff_t delay_ms,
407                               timediff_t timeout_ms,
408                               expire_id timeout_id)
409 {
410   struct eyeballer *baller;
411 
412   *pballer = NULL;
413   baller = calloc(1, sizeof(*baller));
414   if(!baller)
415     return CURLE_OUT_OF_MEMORY;
416 
417   baller->name = ((ai_family == AF_INET) ? "ipv4" : (
418 #ifdef USE_IPV6
419                   (ai_family == AF_INET6) ? "ipv6" :
420 #endif
421                   "ip"));
422   baller->cf_create = cf_create;
423   baller->first = baller->addr = addr;
424   baller->ai_family = ai_family;
425   baller->primary = primary;
426   baller->delay_ms = delay_ms;
427   baller->timeoutms = addr_next_match(baller->addr, baller->ai_family) ?
428     USETIME(timeout_ms) : timeout_ms;
429   baller->timeout_id = timeout_id;
430   baller->result = CURLE_COULDNT_CONNECT;
431 
432   *pballer = baller;
433   return CURLE_OK;
434 }
435 
baller_close(struct eyeballer * baller,struct Curl_easy * data)436 static void baller_close(struct eyeballer *baller,
437                           struct Curl_easy *data)
438 {
439   if(baller && baller->cf) {
440     Curl_conn_cf_discard_chain(&baller->cf, data);
441   }
442 }
443 
baller_free(struct eyeballer * baller,struct Curl_easy * data)444 static void baller_free(struct eyeballer *baller,
445                          struct Curl_easy *data)
446 {
447   if(baller) {
448     baller_close(baller, data);
449     free(baller);
450   }
451 }
452 
baller_rewind(struct eyeballer * baller)453 static void baller_rewind(struct eyeballer *baller)
454 {
455   baller->rewinded = TRUE;
456   baller->addr = baller->first;
457   baller->inconclusive = FALSE;
458 }
459 
baller_next_addr(struct eyeballer * baller)460 static void baller_next_addr(struct eyeballer *baller)
461 {
462   baller->addr = addr_next_match(baller->addr, baller->ai_family);
463 }
464 
465 /*
466  * Initiate a connect attempt walk.
467  *
468  * Note that even on connect fail it returns CURLE_OK, but with 'sock' set to
469  * CURL_SOCKET_BAD. Other errors will however return proper errors.
470  */
baller_initiate(struct Curl_cfilter * cf,struct Curl_easy * data,struct eyeballer * baller)471 static void baller_initiate(struct Curl_cfilter *cf,
472                             struct Curl_easy *data,
473                             struct eyeballer *baller)
474 {
475   struct cf_he_ctx *ctx = cf->ctx;
476   struct Curl_cfilter *cf_prev = baller->cf;
477   struct Curl_cfilter *wcf;
478   CURLcode result;
479 
480 
481   /* Do not close a previous cfilter yet to ensure that the next IP's
482      socket gets a different file descriptor, which can prevent bugs when
483      the curl_multi_socket_action interface is used with certain select()
484      replacements such as kqueue. */
485   result = baller->cf_create(&baller->cf, data, cf->conn, baller->addr,
486                              ctx->transport);
487   if(result)
488     goto out;
489 
490   /* the new filter might have sub-filters */
491   for(wcf = baller->cf; wcf; wcf = wcf->next) {
492     wcf->conn = cf->conn;
493     wcf->sockindex = cf->sockindex;
494   }
495 
496   if(addr_next_match(baller->addr, baller->ai_family)) {
497     Curl_expire(data, baller->timeoutms, baller->timeout_id);
498   }
499 
500 out:
501   if(result) {
502     CURL_TRC_CF(data, cf, "%s failed", baller->name);
503     baller_close(baller, data);
504   }
505   if(cf_prev)
506     Curl_conn_cf_discard_chain(&cf_prev, data);
507   baller->result = result;
508 }
509 
510 /**
511  * Start a connection attempt on the current baller address.
512  * Will return CURLE_OK on the first address where a socket
513  * could be created and the non-blocking connect started.
514  * Returns error when all remaining addresses have been tried.
515  */
baller_start(struct Curl_cfilter * cf,struct Curl_easy * data,struct eyeballer * baller,timediff_t timeoutms)516 static CURLcode baller_start(struct Curl_cfilter *cf,
517                              struct Curl_easy *data,
518                              struct eyeballer *baller,
519                              timediff_t timeoutms)
520 {
521   baller->error = 0;
522   baller->connected = FALSE;
523   baller->has_started = TRUE;
524 
525   while(baller->addr) {
526     baller->started = Curl_now();
527     baller->timeoutms = addr_next_match(baller->addr, baller->ai_family) ?
528       USETIME(timeoutms) : timeoutms;
529     baller_initiate(cf, data, baller);
530     if(!baller->result)
531       break;
532     baller_next_addr(baller);
533   }
534   if(!baller->addr) {
535     baller->is_done = TRUE;
536   }
537   return baller->result;
538 }
539 
540 
541 /* Used within the multi interface. Try next IP address, returns error if no
542    more address exists or error */
baller_start_next(struct Curl_cfilter * cf,struct Curl_easy * data,struct eyeballer * baller,timediff_t timeoutms)543 static CURLcode baller_start_next(struct Curl_cfilter *cf,
544                                   struct Curl_easy *data,
545                                   struct eyeballer *baller,
546                                   timediff_t timeoutms)
547 {
548   if(cf->sockindex == FIRSTSOCKET) {
549     baller_next_addr(baller);
550     /* If we get inconclusive answers from the server(s), we start
551      * again until this whole thing times out. This allows us to
552      * connect to servers that are gracefully restarting and the
553      * packet routing to the new instance has not happened yet (e.g. QUIC). */
554     if(!baller->addr && baller->inconclusive)
555       baller_rewind(baller);
556     baller_start(cf, data, baller, timeoutms);
557   }
558   else {
559     baller->error = 0;
560     baller->connected = FALSE;
561     baller->has_started = TRUE;
562     baller->is_done = TRUE;
563     baller->result = CURLE_COULDNT_CONNECT;
564   }
565   return baller->result;
566 }
567 
baller_connect(struct Curl_cfilter * cf,struct Curl_easy * data,struct eyeballer * baller,struct curltime * now,bool * connected)568 static CURLcode baller_connect(struct Curl_cfilter *cf,
569                                struct Curl_easy *data,
570                                struct eyeballer *baller,
571                                struct curltime *now,
572                                bool *connected)
573 {
574   (void)cf;
575   *connected = baller->connected;
576   if(!baller->result &&  !*connected) {
577     /* evaluate again */
578     baller->result = Curl_conn_cf_connect(baller->cf, data, 0, connected);
579 
580     if(!baller->result) {
581       if(*connected) {
582         baller->connected = TRUE;
583         baller->is_done = TRUE;
584       }
585       else if(Curl_timediff(*now, baller->started) >= baller->timeoutms) {
586         infof(data, "%s connect timeout after %" FMT_TIMEDIFF_T
587               "ms, move on!", baller->name, baller->timeoutms);
588 #if defined(ETIMEDOUT)
589         baller->error = ETIMEDOUT;
590 #endif
591         baller->result = CURLE_OPERATION_TIMEDOUT;
592       }
593     }
594     else if(baller->result == CURLE_WEIRD_SERVER_REPLY)
595       baller->inconclusive = TRUE;
596   }
597   return baller->result;
598 }
599 
600 /*
601  * is_connected() checks if the socket has connected.
602  */
is_connected(struct Curl_cfilter * cf,struct Curl_easy * data,bool * connected)603 static CURLcode is_connected(struct Curl_cfilter *cf,
604                              struct Curl_easy *data,
605                              bool *connected)
606 {
607   struct cf_he_ctx *ctx = cf->ctx;
608   struct connectdata *conn = cf->conn;
609   CURLcode result;
610   struct curltime now;
611   size_t i;
612   int ongoing, not_started;
613   const char *hostname;
614 
615   /* Check if any of the conn->tempsock we use for establishing connections
616    * succeeded and, if so, close any ongoing other ones.
617    * Transfer the successful conn->tempsock to conn->sock[sockindex]
618    * and set conn->tempsock to CURL_SOCKET_BAD.
619    * If transport is QUIC, we need to shutdown the ongoing 'other'
620    * cot ballers in a QUIC appropriate way. */
621 evaluate:
622   *connected = FALSE; /* a negative world view is best */
623   now = Curl_now();
624   ongoing = not_started = 0;
625   for(i = 0; i < ARRAYSIZE(ctx->baller); i++) {
626     struct eyeballer *baller = ctx->baller[i];
627 
628     if(!baller || baller->is_done)
629       continue;
630 
631     if(!baller->has_started) {
632       ++not_started;
633       continue;
634     }
635     baller->result = baller_connect(cf, data, baller, &now, connected);
636     CURL_TRC_CF(data, cf, "%s connect -> %d, connected=%d",
637                 baller->name, baller->result, *connected);
638 
639     if(!baller->result) {
640       if(*connected) {
641         /* connected, declare the winner */
642         ctx->winner = baller;
643         ctx->baller[i] = NULL;
644         break;
645       }
646       else { /* still waiting */
647         ++ongoing;
648       }
649     }
650     else if(!baller->is_done) {
651       /* The baller failed to connect, start its next attempt */
652       if(baller->error) {
653         data->state.os_errno = baller->error;
654         SET_SOCKERRNO(baller->error);
655       }
656       baller_start_next(cf, data, baller, Curl_timeleft(data, &now, TRUE));
657       if(baller->is_done) {
658         CURL_TRC_CF(data, cf, "%s done", baller->name);
659       }
660       else {
661         /* next attempt was started */
662         CURL_TRC_CF(data, cf, "%s trying next", baller->name);
663         ++ongoing;
664         Curl_expire(data, 0, EXPIRE_RUN_NOW);
665       }
666     }
667   }
668 
669   if(ctx->winner) {
670     *connected = TRUE;
671     return CURLE_OK;
672   }
673 
674   /* Nothing connected, check the time before we might
675    * start new ballers or return ok. */
676   if((ongoing || not_started) && Curl_timeleft(data, &now, TRUE) < 0) {
677     failf(data, "Connection timeout after %" FMT_OFF_T " ms",
678           Curl_timediff(now, data->progress.t_startsingle));
679     return CURLE_OPERATION_TIMEDOUT;
680   }
681 
682   /* Check if we have any waiting ballers to start now. */
683   if(not_started > 0) {
684     int added = 0;
685 
686     for(i = 0; i < ARRAYSIZE(ctx->baller); i++) {
687       struct eyeballer *baller = ctx->baller[i];
688 
689       if(!baller || baller->has_started)
690         continue;
691       /* We start its primary baller has failed to connect or if
692        * its start delay_ms have expired */
693       if((baller->primary && baller->primary->is_done) ||
694           Curl_timediff(now, ctx->started) >= baller->delay_ms) {
695         baller_start(cf, data, baller, Curl_timeleft(data, &now, TRUE));
696         if(baller->is_done) {
697           CURL_TRC_CF(data, cf, "%s done", baller->name);
698         }
699         else {
700           CURL_TRC_CF(data, cf, "%s starting (timeout=%" FMT_TIMEDIFF_T "ms)",
701                       baller->name, baller->timeoutms);
702           ++ongoing;
703           ++added;
704         }
705       }
706     }
707     if(added > 0)
708       goto evaluate;
709   }
710 
711   if(ongoing > 0) {
712     /* We are still trying, return for more waiting */
713     *connected = FALSE;
714     return CURLE_OK;
715   }
716 
717   /* all ballers have failed to connect. */
718   CURL_TRC_CF(data, cf, "all eyeballers failed");
719   result = CURLE_COULDNT_CONNECT;
720   for(i = 0; i < ARRAYSIZE(ctx->baller); i++) {
721     struct eyeballer *baller = ctx->baller[i];
722     if(!baller)
723       continue;
724     CURL_TRC_CF(data, cf, "%s assess started=%d, result=%d",
725                 baller->name, baller->has_started, baller->result);
726     if(baller->has_started && baller->result) {
727       result = baller->result;
728       break;
729     }
730   }
731 
732 #ifndef CURL_DISABLE_PROXY
733   if(conn->bits.socksproxy)
734     hostname = conn->socks_proxy.host.name;
735   else if(conn->bits.httpproxy)
736     hostname = conn->http_proxy.host.name;
737   else
738 #endif
739     if(conn->bits.conn_to_host)
740       hostname = conn->conn_to_host.name;
741   else
742     hostname = conn->host.name;
743 
744   failf(data, "Failed to connect to %s port %u after "
745         "%" FMT_TIMEDIFF_T " ms: %s",
746         hostname, conn->primary.remote_port,
747         Curl_timediff(now, data->progress.t_startsingle),
748         curl_easy_strerror(result));
749 
750 #ifdef WSAETIMEDOUT
751   if(WSAETIMEDOUT == data->state.os_errno)
752     result = CURLE_OPERATION_TIMEDOUT;
753 #elif defined(ETIMEDOUT)
754   if(ETIMEDOUT == data->state.os_errno)
755     result = CURLE_OPERATION_TIMEDOUT;
756 #endif
757 
758   return result;
759 }
760 
761 /*
762  * Connect to the given host with timeout, proxy or remote does not matter.
763  * There might be more than one IP address to try out.
764  */
start_connect(struct Curl_cfilter * cf,struct Curl_easy * data,const struct Curl_dns_entry * remotehost)765 static CURLcode start_connect(struct Curl_cfilter *cf,
766                               struct Curl_easy *data,
767                               const struct Curl_dns_entry *remotehost)
768 {
769   struct cf_he_ctx *ctx = cf->ctx;
770   struct connectdata *conn = cf->conn;
771   CURLcode result = CURLE_COULDNT_CONNECT;
772   int ai_family0 = 0, ai_family1 = 0;
773   timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE);
774   const struct Curl_addrinfo *addr0 = NULL, *addr1 = NULL;
775 
776   if(timeout_ms < 0) {
777     /* a precaution, no need to continue if time already is up */
778     failf(data, "Connection time-out");
779     return CURLE_OPERATION_TIMEDOUT;
780   }
781 
782   ctx->started = Curl_now();
783 
784   /* remotehost->addr is the list of addresses from the resolver, each
785    * with an address family. The list has at least one entry, possibly
786    * many more.
787    * We try at most 2 at a time, until we either get a connection or
788    * run out of addresses to try. Since likelihood of success is tied
789    * to the address family (e.g. IPV6 might not work at all ), we want
790    * the 2 connect attempt ballers to try different families, if possible.
791    *
792    */
793   if(conn->ip_version == CURL_IPRESOLVE_V6) {
794 #ifdef USE_IPV6
795     ai_family0 = AF_INET6;
796     addr0 = addr_first_match(remotehost->addr, ai_family0);
797 #endif
798   }
799   else if(conn->ip_version == CURL_IPRESOLVE_V4) {
800     ai_family0 = AF_INET;
801     addr0 = addr_first_match(remotehost->addr, ai_family0);
802   }
803   else {
804     /* no user preference, we try ipv6 always first when available */
805 #ifdef USE_IPV6
806     ai_family0 = AF_INET6;
807     addr0 = addr_first_match(remotehost->addr, ai_family0);
808 #endif
809     /* next candidate is ipv4 */
810     ai_family1 = AF_INET;
811     addr1 = addr_first_match(remotehost->addr, ai_family1);
812     /* no ip address families, probably AF_UNIX or something, use the
813      * address family given to us */
814     if(!addr1  && !addr0 && remotehost->addr) {
815       ai_family0 = remotehost->addr->ai_family;
816       addr0 = addr_first_match(remotehost->addr, ai_family0);
817     }
818   }
819 
820   if(!addr0 && addr1) {
821     /* switch around, so a single baller always uses addr0 */
822     addr0 = addr1;
823     ai_family0 = ai_family1;
824     addr1 = NULL;
825   }
826 
827   /* We found no address that matches our criteria, we cannot connect */
828   if(!addr0) {
829     return CURLE_COULDNT_CONNECT;
830   }
831 
832   memset(ctx->baller, 0, sizeof(ctx->baller));
833   result = eyeballer_new(&ctx->baller[0], ctx->cf_create, addr0, ai_family0,
834                           NULL, 0, /* no primary/delay, start now */
835                           timeout_ms,  EXPIRE_DNS_PER_NAME);
836   if(result)
837     return result;
838   CURL_TRC_CF(data, cf, "created %s (timeout %" FMT_TIMEDIFF_T "ms)",
839               ctx->baller[0]->name, ctx->baller[0]->timeoutms);
840   if(addr1) {
841     /* second one gets a delayed start */
842     result = eyeballer_new(&ctx->baller[1], ctx->cf_create, addr1, ai_family1,
843                             ctx->baller[0], /* wait on that to fail */
844                             /* or start this delayed */
845                             data->set.happy_eyeballs_timeout,
846                             timeout_ms,  EXPIRE_DNS_PER_NAME2);
847     if(result)
848       return result;
849     CURL_TRC_CF(data, cf, "created %s (timeout %" FMT_TIMEDIFF_T "ms)",
850                 ctx->baller[1]->name, ctx->baller[1]->timeoutms);
851     Curl_expire(data, data->set.happy_eyeballs_timeout,
852                 EXPIRE_HAPPY_EYEBALLS);
853   }
854 
855   return CURLE_OK;
856 }
857 
cf_he_ctx_clear(struct Curl_cfilter * cf,struct Curl_easy * data)858 static void cf_he_ctx_clear(struct Curl_cfilter *cf, struct Curl_easy *data)
859 {
860   struct cf_he_ctx *ctx = cf->ctx;
861   size_t i;
862 
863   DEBUGASSERT(ctx);
864   DEBUGASSERT(data);
865   for(i = 0; i < ARRAYSIZE(ctx->baller); i++) {
866     baller_free(ctx->baller[i], data);
867     ctx->baller[i] = NULL;
868   }
869   baller_free(ctx->winner, data);
870   ctx->winner = NULL;
871 }
872 
cf_he_shutdown(struct Curl_cfilter * cf,struct Curl_easy * data,bool * done)873 static CURLcode cf_he_shutdown(struct Curl_cfilter *cf,
874                                struct Curl_easy *data, bool *done)
875 {
876   struct cf_he_ctx *ctx = cf->ctx;
877   size_t i;
878   CURLcode result = CURLE_OK;
879 
880   DEBUGASSERT(data);
881   if(cf->connected) {
882     *done = TRUE;
883     return CURLE_OK;
884   }
885 
886   /* shutdown all ballers that have not done so already. If one fails,
887    * continue shutting down others until all are shutdown. */
888   for(i = 0; i < ARRAYSIZE(ctx->baller); i++) {
889     struct eyeballer *baller = ctx->baller[i];
890     bool bdone = FALSE;
891     if(!baller || !baller->cf || baller->shutdown)
892       continue;
893     baller->result = baller->cf->cft->do_shutdown(baller->cf, data, &bdone);
894     if(baller->result || bdone)
895       baller->shutdown = TRUE; /* treat a failed shutdown as done */
896   }
897 
898   *done = TRUE;
899   for(i = 0; i < ARRAYSIZE(ctx->baller); i++) {
900     if(ctx->baller[i] && !ctx->baller[i]->shutdown)
901       *done = FALSE;
902   }
903   if(*done) {
904     for(i = 0; i < ARRAYSIZE(ctx->baller); i++) {
905       if(ctx->baller[i] && ctx->baller[i]->result)
906         result = ctx->baller[i]->result;
907     }
908   }
909   CURL_TRC_CF(data, cf, "shutdown -> %d, done=%d", result, *done);
910   return result;
911 }
912 
cf_he_adjust_pollset(struct Curl_cfilter * cf,struct Curl_easy * data,struct easy_pollset * ps)913 static void cf_he_adjust_pollset(struct Curl_cfilter *cf,
914                                   struct Curl_easy *data,
915                                   struct easy_pollset *ps)
916 {
917   struct cf_he_ctx *ctx = cf->ctx;
918   size_t i;
919 
920   if(!cf->connected) {
921     for(i = 0; i < ARRAYSIZE(ctx->baller); i++) {
922       struct eyeballer *baller = ctx->baller[i];
923       if(!baller || !baller->cf)
924         continue;
925       Curl_conn_cf_adjust_pollset(baller->cf, data, ps);
926     }
927     CURL_TRC_CF(data, cf, "adjust_pollset -> %d socks", ps->num);
928   }
929 }
930 
cf_he_connect(struct Curl_cfilter * cf,struct Curl_easy * data,bool blocking,bool * done)931 static CURLcode cf_he_connect(struct Curl_cfilter *cf,
932                               struct Curl_easy *data,
933                               bool blocking, bool *done)
934 {
935   struct cf_he_ctx *ctx = cf->ctx;
936   CURLcode result = CURLE_OK;
937 
938   if(cf->connected) {
939     *done = TRUE;
940     return CURLE_OK;
941   }
942 
943   (void)blocking; /* TODO: do we want to support this? */
944   DEBUGASSERT(ctx);
945   *done = FALSE;
946 
947   switch(ctx->state) {
948     case SCFST_INIT:
949       DEBUGASSERT(CURL_SOCKET_BAD == Curl_conn_cf_get_socket(cf, data));
950       DEBUGASSERT(!cf->connected);
951       result = start_connect(cf, data, ctx->remotehost);
952       if(result)
953         return result;
954       ctx->state = SCFST_WAITING;
955       FALLTHROUGH();
956     case SCFST_WAITING:
957       result = is_connected(cf, data, done);
958       if(!result && *done) {
959         DEBUGASSERT(ctx->winner);
960         DEBUGASSERT(ctx->winner->cf);
961         DEBUGASSERT(ctx->winner->cf->connected);
962         /* we have a winner. Install and activate it.
963          * close/free all others. */
964         ctx->state = SCFST_DONE;
965         cf->connected = TRUE;
966         cf->next = ctx->winner->cf;
967         ctx->winner->cf = NULL;
968         cf_he_ctx_clear(cf, data);
969 
970         if(cf->conn->handler->protocol & PROTO_FAMILY_SSH)
971           Curl_pgrsTime(data, TIMER_APPCONNECT); /* we are connected already */
972         if(Curl_trc_cf_is_verbose(cf, data)) {
973           struct ip_quadruple ipquad;
974           int is_ipv6;
975           if(!Curl_conn_cf_get_ip_info(cf->next, data, &is_ipv6, &ipquad)) {
976             const char *host, *disphost;
977             int port;
978             cf->next->cft->get_host(cf->next, data, &host, &disphost, &port);
979             CURL_TRC_CF(data, cf, "Connected to %s (%s) port %u",
980                         disphost, ipquad.remote_ip, ipquad.remote_port);
981           }
982         }
983         data->info.numconnects++; /* to track the # of connections made */
984       }
985       break;
986     case SCFST_DONE:
987       *done = TRUE;
988       break;
989   }
990   return result;
991 }
992 
cf_he_close(struct Curl_cfilter * cf,struct Curl_easy * data)993 static void cf_he_close(struct Curl_cfilter *cf,
994                         struct Curl_easy *data)
995 {
996   struct cf_he_ctx *ctx = cf->ctx;
997 
998   CURL_TRC_CF(data, cf, "close");
999   cf_he_ctx_clear(cf, data);
1000   cf->connected = FALSE;
1001   ctx->state = SCFST_INIT;
1002 
1003   if(cf->next) {
1004     cf->next->cft->do_close(cf->next, data);
1005     Curl_conn_cf_discard_chain(&cf->next, data);
1006   }
1007 }
1008 
cf_he_data_pending(struct Curl_cfilter * cf,const struct Curl_easy * data)1009 static bool cf_he_data_pending(struct Curl_cfilter *cf,
1010                                const struct Curl_easy *data)
1011 {
1012   struct cf_he_ctx *ctx = cf->ctx;
1013   size_t i;
1014 
1015   if(cf->connected)
1016     return cf->next->cft->has_data_pending(cf->next, data);
1017 
1018   for(i = 0; i < ARRAYSIZE(ctx->baller); i++) {
1019     struct eyeballer *baller = ctx->baller[i];
1020     if(!baller || !baller->cf)
1021       continue;
1022     if(baller->cf->cft->has_data_pending(baller->cf, data))
1023       return TRUE;
1024   }
1025   return FALSE;
1026 }
1027 
get_max_baller_time(struct Curl_cfilter * cf,struct Curl_easy * data,int query)1028 static struct curltime get_max_baller_time(struct Curl_cfilter *cf,
1029                                           struct Curl_easy *data,
1030                                           int query)
1031 {
1032   struct cf_he_ctx *ctx = cf->ctx;
1033   struct curltime t, tmax;
1034   size_t i;
1035 
1036   memset(&tmax, 0, sizeof(tmax));
1037   for(i = 0; i < ARRAYSIZE(ctx->baller); i++) {
1038     struct eyeballer *baller = ctx->baller[i];
1039 
1040     memset(&t, 0, sizeof(t));
1041     if(baller && baller->cf &&
1042        !baller->cf->cft->query(baller->cf, data, query, NULL, &t)) {
1043       if((t.tv_sec || t.tv_usec) && Curl_timediff_us(t, tmax) > 0)
1044         tmax = t;
1045     }
1046   }
1047   return tmax;
1048 }
1049 
cf_he_query(struct Curl_cfilter * cf,struct Curl_easy * data,int query,int * pres1,void * pres2)1050 static CURLcode cf_he_query(struct Curl_cfilter *cf,
1051                             struct Curl_easy *data,
1052                             int query, int *pres1, void *pres2)
1053 {
1054   struct cf_he_ctx *ctx = cf->ctx;
1055 
1056   if(!cf->connected) {
1057     switch(query) {
1058     case CF_QUERY_CONNECT_REPLY_MS: {
1059       int reply_ms = -1;
1060       size_t i;
1061 
1062       for(i = 0; i < ARRAYSIZE(ctx->baller); i++) {
1063         struct eyeballer *baller = ctx->baller[i];
1064         int breply_ms;
1065 
1066         if(baller && baller->cf &&
1067            !baller->cf->cft->query(baller->cf, data, query,
1068                                    &breply_ms, NULL)) {
1069           if(breply_ms >= 0 && (reply_ms < 0 || breply_ms < reply_ms))
1070             reply_ms = breply_ms;
1071         }
1072       }
1073       *pres1 = reply_ms;
1074       CURL_TRC_CF(data, cf, "query connect reply: %dms", *pres1);
1075       return CURLE_OK;
1076     }
1077     case CF_QUERY_TIMER_CONNECT: {
1078       struct curltime *when = pres2;
1079       *when = get_max_baller_time(cf, data, CF_QUERY_TIMER_CONNECT);
1080       return CURLE_OK;
1081     }
1082     case CF_QUERY_TIMER_APPCONNECT: {
1083       struct curltime *when = pres2;
1084       *when = get_max_baller_time(cf, data, CF_QUERY_TIMER_APPCONNECT);
1085       return CURLE_OK;
1086     }
1087     default:
1088       break;
1089     }
1090   }
1091 
1092   return cf->next ?
1093     cf->next->cft->query(cf->next, data, query, pres1, pres2) :
1094     CURLE_UNKNOWN_OPTION;
1095 }
1096 
cf_he_destroy(struct Curl_cfilter * cf,struct Curl_easy * data)1097 static void cf_he_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
1098 {
1099   struct cf_he_ctx *ctx = cf->ctx;
1100 
1101   CURL_TRC_CF(data, cf, "destroy");
1102   if(ctx) {
1103     cf_he_ctx_clear(cf, data);
1104   }
1105   /* release any resources held in state */
1106   Curl_safefree(ctx);
1107 }
1108 
1109 struct Curl_cftype Curl_cft_happy_eyeballs = {
1110   "HAPPY-EYEBALLS",
1111   0,
1112   CURL_LOG_LVL_NONE,
1113   cf_he_destroy,
1114   cf_he_connect,
1115   cf_he_close,
1116   cf_he_shutdown,
1117   Curl_cf_def_get_host,
1118   cf_he_adjust_pollset,
1119   cf_he_data_pending,
1120   Curl_cf_def_send,
1121   Curl_cf_def_recv,
1122   Curl_cf_def_cntrl,
1123   Curl_cf_def_conn_is_alive,
1124   Curl_cf_def_conn_keep_alive,
1125   cf_he_query,
1126 };
1127 
1128 /**
1129  * Create a happy eyeball connection filter that uses the, once resolved,
1130  * address information to connect on ip families based on connection
1131  * configuration.
1132  * @param pcf        output, the created cfilter
1133  * @param data       easy handle used in creation
1134  * @param conn       connection the filter is created for
1135  * @param cf_create  method to create the sub-filters performing the
1136  *                   actual connects.
1137  */
1138 static CURLcode
cf_happy_eyeballs_create(struct Curl_cfilter ** pcf,struct Curl_easy * data,struct connectdata * conn,cf_ip_connect_create * cf_create,const struct Curl_dns_entry * remotehost,int transport)1139 cf_happy_eyeballs_create(struct Curl_cfilter **pcf,
1140                          struct Curl_easy *data,
1141                          struct connectdata *conn,
1142                          cf_ip_connect_create *cf_create,
1143                          const struct Curl_dns_entry *remotehost,
1144                          int transport)
1145 {
1146   struct cf_he_ctx *ctx = NULL;
1147   CURLcode result;
1148 
1149   (void)data;
1150   (void)conn;
1151   *pcf = NULL;
1152   ctx = calloc(1, sizeof(*ctx));
1153   if(!ctx) {
1154     result = CURLE_OUT_OF_MEMORY;
1155     goto out;
1156   }
1157   ctx->transport = transport;
1158   ctx->cf_create = cf_create;
1159   ctx->remotehost = remotehost;
1160 
1161   result = Curl_cf_create(pcf, &Curl_cft_happy_eyeballs, ctx);
1162 
1163 out:
1164   if(result) {
1165     Curl_safefree(*pcf);
1166     Curl_safefree(ctx);
1167   }
1168   return result;
1169 }
1170 
1171 struct transport_provider {
1172   int transport;
1173   cf_ip_connect_create *cf_create;
1174 };
1175 
1176 static
1177 #ifndef UNITTESTS
1178 const
1179 #endif
1180 struct transport_provider transport_providers[] = {
1181   { TRNSPRT_TCP, Curl_cf_tcp_create },
1182 #ifdef USE_HTTP3
1183   { TRNSPRT_QUIC, Curl_cf_quic_create },
1184 #endif
1185 #ifndef CURL_DISABLE_TFTP
1186   { TRNSPRT_UDP, Curl_cf_udp_create },
1187 #endif
1188 #ifdef USE_UNIX_SOCKETS
1189   { TRNSPRT_UNIX, Curl_cf_unix_create },
1190 #endif
1191 };
1192 
get_cf_create(int transport)1193 static cf_ip_connect_create *get_cf_create(int transport)
1194 {
1195   size_t i;
1196   for(i = 0; i < ARRAYSIZE(transport_providers); ++i) {
1197     if(transport == transport_providers[i].transport)
1198       return transport_providers[i].cf_create;
1199   }
1200   return NULL;
1201 }
1202 
cf_he_insert_after(struct Curl_cfilter * cf_at,struct Curl_easy * data,const struct Curl_dns_entry * remotehost,int transport)1203 static CURLcode cf_he_insert_after(struct Curl_cfilter *cf_at,
1204                                    struct Curl_easy *data,
1205                                    const struct Curl_dns_entry *remotehost,
1206                                    int transport)
1207 {
1208   cf_ip_connect_create *cf_create;
1209   struct Curl_cfilter *cf;
1210   CURLcode result;
1211 
1212   /* Need to be first */
1213   DEBUGASSERT(cf_at);
1214   cf_create = get_cf_create(transport);
1215   if(!cf_create) {
1216     CURL_TRC_CF(data, cf_at, "unsupported transport type %d", transport);
1217     return CURLE_UNSUPPORTED_PROTOCOL;
1218   }
1219   result = cf_happy_eyeballs_create(&cf, data, cf_at->conn,
1220                                     cf_create, remotehost,
1221                                     transport);
1222   if(result)
1223     return result;
1224 
1225   Curl_conn_cf_insert_after(cf_at, cf);
1226   return CURLE_OK;
1227 }
1228 
1229 typedef enum {
1230   CF_SETUP_INIT,
1231   CF_SETUP_CNNCT_EYEBALLS,
1232   CF_SETUP_CNNCT_SOCKS,
1233   CF_SETUP_CNNCT_HTTP_PROXY,
1234   CF_SETUP_CNNCT_HAPROXY,
1235   CF_SETUP_CNNCT_SSL,
1236   CF_SETUP_DONE
1237 } cf_setup_state;
1238 
1239 struct cf_setup_ctx {
1240   cf_setup_state state;
1241   const struct Curl_dns_entry *remotehost;
1242   int ssl_mode;
1243   int transport;
1244 };
1245 
cf_setup_connect(struct Curl_cfilter * cf,struct Curl_easy * data,bool blocking,bool * done)1246 static CURLcode cf_setup_connect(struct Curl_cfilter *cf,
1247                                  struct Curl_easy *data,
1248                                  bool blocking, bool *done)
1249 {
1250   struct cf_setup_ctx *ctx = cf->ctx;
1251   CURLcode result = CURLE_OK;
1252 
1253   if(cf->connected) {
1254     *done = TRUE;
1255     return CURLE_OK;
1256   }
1257 
1258   /* connect current sub-chain */
1259 connect_sub_chain:
1260   if(cf->next && !cf->next->connected) {
1261     result = Curl_conn_cf_connect(cf->next, data, blocking, done);
1262     if(result || !*done)
1263       return result;
1264   }
1265 
1266   if(ctx->state < CF_SETUP_CNNCT_EYEBALLS) {
1267     result = cf_he_insert_after(cf, data, ctx->remotehost, ctx->transport);
1268     if(result)
1269       return result;
1270     ctx->state = CF_SETUP_CNNCT_EYEBALLS;
1271     if(!cf->next || !cf->next->connected)
1272       goto connect_sub_chain;
1273   }
1274 
1275   /* sub-chain connected, do we need to add more? */
1276 #ifndef CURL_DISABLE_PROXY
1277   if(ctx->state < CF_SETUP_CNNCT_SOCKS && cf->conn->bits.socksproxy) {
1278     result = Curl_cf_socks_proxy_insert_after(cf, data);
1279     if(result)
1280       return result;
1281     ctx->state = CF_SETUP_CNNCT_SOCKS;
1282     if(!cf->next || !cf->next->connected)
1283       goto connect_sub_chain;
1284   }
1285 
1286   if(ctx->state < CF_SETUP_CNNCT_HTTP_PROXY && cf->conn->bits.httpproxy) {
1287 #ifdef USE_SSL
1288     if(IS_HTTPS_PROXY(cf->conn->http_proxy.proxytype)
1289        && !Curl_conn_is_ssl(cf->conn, cf->sockindex)) {
1290       result = Curl_cf_ssl_proxy_insert_after(cf, data);
1291       if(result)
1292         return result;
1293     }
1294 #endif /* USE_SSL */
1295 
1296 #if !defined(CURL_DISABLE_HTTP)
1297     if(cf->conn->bits.tunnel_proxy) {
1298       result = Curl_cf_http_proxy_insert_after(cf, data);
1299       if(result)
1300         return result;
1301     }
1302 #endif /* !CURL_DISABLE_HTTP */
1303     ctx->state = CF_SETUP_CNNCT_HTTP_PROXY;
1304     if(!cf->next || !cf->next->connected)
1305       goto connect_sub_chain;
1306   }
1307 #endif /* !CURL_DISABLE_PROXY */
1308 
1309   if(ctx->state < CF_SETUP_CNNCT_HAPROXY) {
1310 #if !defined(CURL_DISABLE_PROXY)
1311     if(data->set.haproxyprotocol) {
1312       if(Curl_conn_is_ssl(cf->conn, cf->sockindex)) {
1313         failf(data, "haproxy protocol not support with SSL "
1314               "encryption in place (QUIC?)");
1315         return CURLE_UNSUPPORTED_PROTOCOL;
1316       }
1317       result = Curl_cf_haproxy_insert_after(cf, data);
1318       if(result)
1319         return result;
1320     }
1321 #endif /* !CURL_DISABLE_PROXY */
1322     ctx->state = CF_SETUP_CNNCT_HAPROXY;
1323     if(!cf->next || !cf->next->connected)
1324       goto connect_sub_chain;
1325   }
1326 
1327   if(ctx->state < CF_SETUP_CNNCT_SSL) {
1328 #ifdef USE_SSL
1329     if((ctx->ssl_mode == CURL_CF_SSL_ENABLE
1330         || (ctx->ssl_mode != CURL_CF_SSL_DISABLE
1331            && cf->conn->handler->flags & PROTOPT_SSL)) /* we want SSL */
1332        && !Curl_conn_is_ssl(cf->conn, cf->sockindex)) { /* it is missing */
1333       result = Curl_cf_ssl_insert_after(cf, data);
1334       if(result)
1335         return result;
1336     }
1337 #endif /* USE_SSL */
1338     ctx->state = CF_SETUP_CNNCT_SSL;
1339     if(!cf->next || !cf->next->connected)
1340       goto connect_sub_chain;
1341   }
1342 
1343   ctx->state = CF_SETUP_DONE;
1344   cf->connected = TRUE;
1345   *done = TRUE;
1346   return CURLE_OK;
1347 }
1348 
cf_setup_close(struct Curl_cfilter * cf,struct Curl_easy * data)1349 static void cf_setup_close(struct Curl_cfilter *cf,
1350                            struct Curl_easy *data)
1351 {
1352   struct cf_setup_ctx *ctx = cf->ctx;
1353 
1354   CURL_TRC_CF(data, cf, "close");
1355   cf->connected = FALSE;
1356   ctx->state = CF_SETUP_INIT;
1357 
1358   if(cf->next) {
1359     cf->next->cft->do_close(cf->next, data);
1360     Curl_conn_cf_discard_chain(&cf->next, data);
1361   }
1362 }
1363 
cf_setup_destroy(struct Curl_cfilter * cf,struct Curl_easy * data)1364 static void cf_setup_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
1365 {
1366   struct cf_setup_ctx *ctx = cf->ctx;
1367 
1368   (void)data;
1369   CURL_TRC_CF(data, cf, "destroy");
1370   Curl_safefree(ctx);
1371 }
1372 
1373 
1374 struct Curl_cftype Curl_cft_setup = {
1375   "SETUP",
1376   0,
1377   CURL_LOG_LVL_NONE,
1378   cf_setup_destroy,
1379   cf_setup_connect,
1380   cf_setup_close,
1381   Curl_cf_def_shutdown,
1382   Curl_cf_def_get_host,
1383   Curl_cf_def_adjust_pollset,
1384   Curl_cf_def_data_pending,
1385   Curl_cf_def_send,
1386   Curl_cf_def_recv,
1387   Curl_cf_def_cntrl,
1388   Curl_cf_def_conn_is_alive,
1389   Curl_cf_def_conn_keep_alive,
1390   Curl_cf_def_query,
1391 };
1392 
cf_setup_create(struct Curl_cfilter ** pcf,struct Curl_easy * data,const struct Curl_dns_entry * remotehost,int transport,int ssl_mode)1393 static CURLcode cf_setup_create(struct Curl_cfilter **pcf,
1394                                 struct Curl_easy *data,
1395                                 const struct Curl_dns_entry *remotehost,
1396                                 int transport,
1397                                 int ssl_mode)
1398 {
1399   struct Curl_cfilter *cf = NULL;
1400   struct cf_setup_ctx *ctx;
1401   CURLcode result = CURLE_OK;
1402 
1403   (void)data;
1404   ctx = calloc(1, sizeof(*ctx));
1405   if(!ctx) {
1406     result = CURLE_OUT_OF_MEMORY;
1407     goto out;
1408   }
1409   ctx->state = CF_SETUP_INIT;
1410   ctx->remotehost = remotehost;
1411   ctx->ssl_mode = ssl_mode;
1412   ctx->transport = transport;
1413 
1414   result = Curl_cf_create(&cf, &Curl_cft_setup, ctx);
1415   if(result)
1416     goto out;
1417   ctx = NULL;
1418 
1419 out:
1420   *pcf = result ? NULL : cf;
1421   free(ctx);
1422   return result;
1423 }
1424 
cf_setup_add(struct Curl_easy * data,struct connectdata * conn,int sockindex,const struct Curl_dns_entry * remotehost,int transport,int ssl_mode)1425 static CURLcode cf_setup_add(struct Curl_easy *data,
1426                              struct connectdata *conn,
1427                              int sockindex,
1428                              const struct Curl_dns_entry *remotehost,
1429                              int transport,
1430                              int ssl_mode)
1431 {
1432   struct Curl_cfilter *cf;
1433   CURLcode result = CURLE_OK;
1434 
1435   DEBUGASSERT(data);
1436   result = cf_setup_create(&cf, data, remotehost, transport, ssl_mode);
1437   if(result)
1438     goto out;
1439   Curl_conn_cf_add(data, conn, sockindex, cf);
1440 out:
1441   return result;
1442 }
1443 
1444 #ifdef UNITTESTS
1445 /* used by unit2600.c */
Curl_debug_set_transport_provider(int transport,cf_ip_connect_create * cf_create)1446 void Curl_debug_set_transport_provider(int transport,
1447                                        cf_ip_connect_create *cf_create)
1448 {
1449   size_t i;
1450   for(i = 0; i < ARRAYSIZE(transport_providers); ++i) {
1451     if(transport == transport_providers[i].transport) {
1452       transport_providers[i].cf_create = cf_create;
1453       return;
1454     }
1455   }
1456 }
1457 #endif /* UNITTESTS */
1458 
Curl_cf_setup_insert_after(struct Curl_cfilter * cf_at,struct Curl_easy * data,const struct Curl_dns_entry * remotehost,int transport,int ssl_mode)1459 CURLcode Curl_cf_setup_insert_after(struct Curl_cfilter *cf_at,
1460                                     struct Curl_easy *data,
1461                                     const struct Curl_dns_entry *remotehost,
1462                                     int transport,
1463                                     int ssl_mode)
1464 {
1465   struct Curl_cfilter *cf;
1466   CURLcode result;
1467 
1468   DEBUGASSERT(data);
1469   result = cf_setup_create(&cf, data, remotehost, transport, ssl_mode);
1470   if(result)
1471     goto out;
1472   Curl_conn_cf_insert_after(cf_at, cf);
1473 out:
1474   return result;
1475 }
1476 
Curl_conn_setup(struct Curl_easy * data,struct connectdata * conn,int sockindex,const struct Curl_dns_entry * remotehost,int ssl_mode)1477 CURLcode Curl_conn_setup(struct Curl_easy *data,
1478                          struct connectdata *conn,
1479                          int sockindex,
1480                          const struct Curl_dns_entry *remotehost,
1481                          int ssl_mode)
1482 {
1483   CURLcode result = CURLE_OK;
1484 
1485   DEBUGASSERT(data);
1486   DEBUGASSERT(conn->handler);
1487 
1488 #if !defined(CURL_DISABLE_HTTP) && !defined(USE_HYPER)
1489   if(!conn->cfilter[sockindex] &&
1490      conn->handler->protocol == CURLPROTO_HTTPS) {
1491     DEBUGASSERT(ssl_mode != CURL_CF_SSL_DISABLE);
1492     result = Curl_cf_https_setup(data, conn, sockindex, remotehost);
1493     if(result)
1494       goto out;
1495   }
1496 #endif /* !defined(CURL_DISABLE_HTTP) && !defined(USE_HYPER) */
1497 
1498   /* Still no cfilter set, apply default. */
1499   if(!conn->cfilter[sockindex]) {
1500     result = cf_setup_add(data, conn, sockindex, remotehost,
1501                           conn->transport, ssl_mode);
1502     if(result)
1503       goto out;
1504   }
1505 
1506   DEBUGASSERT(conn->cfilter[sockindex]);
1507 out:
1508   return result;
1509 }
1510