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