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