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