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