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