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