VirtualBox

source: vbox/trunk/src/libs/curl-8.7.1/lib/connect.c@ 106165

最後變更 在這個檔案從106165是 104083,由 vboxsync 提交於 8 月 前

curl-8.7.1: Applied and adjusted our curl changes to 8.4.0. bugref:10639

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

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