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