VirtualBox

source: vbox/trunk/src/libs/curl-8.0.1/lib/connect.c@ 99371

Last change on this file since 99371 was 99344, checked in by vboxsync, 2 years ago

curl-8.0.1: Applied and adjusted our curl changes to 7.87.0 bugref:10417

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette