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