Annotation of embedaddon/curl/lib/connect.c, revision 1.1
1.1 ! misho 1: /***************************************************************************
! 2: * _ _ ____ _
! 3: * Project ___| | | | _ \| |
! 4: * / __| | | | |_) | |
! 5: * | (__| |_| | _ <| |___
! 6: * \___|\___/|_| \_\_____|
! 7: *
! 8: * Copyright (C) 1998 - 2020, 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.haxx.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: ***************************************************************************/
! 22:
! 23: #include "curl_setup.h"
! 24:
! 25: #ifdef HAVE_NETINET_IN_H
! 26: #include <netinet/in.h> /* <netinet/tcp.h> may need it */
! 27: #endif
! 28: #ifdef HAVE_SYS_UN_H
! 29: #include <sys/un.h> /* for sockaddr_un */
! 30: #endif
! 31: #ifdef HAVE_LINUX_TCP_H
! 32: #include <linux/tcp.h>
! 33: #elif defined(HAVE_NETINET_TCP_H)
! 34: #include <netinet/tcp.h>
! 35: #endif
! 36: #ifdef HAVE_SYS_IOCTL_H
! 37: #include <sys/ioctl.h>
! 38: #endif
! 39: #ifdef HAVE_NETDB_H
! 40: #include <netdb.h>
! 41: #endif
! 42: #ifdef HAVE_FCNTL_H
! 43: #include <fcntl.h>
! 44: #endif
! 45: #ifdef HAVE_ARPA_INET_H
! 46: #include <arpa/inet.h>
! 47: #endif
! 48:
! 49: #if (defined(HAVE_IOCTL_FIONBIO) && defined(NETWARE))
! 50: #include <sys/filio.h>
! 51: #endif
! 52: #ifdef NETWARE
! 53: #undef in_addr_t
! 54: #define in_addr_t unsigned long
! 55: #endif
! 56: #ifdef __VMS
! 57: #include <in.h>
! 58: #include <inet.h>
! 59: #endif
! 60:
! 61: #include "urldata.h"
! 62: #include "sendf.h"
! 63: #include "if2ip.h"
! 64: #include "strerror.h"
! 65: #include "connect.h"
! 66: #include "select.h"
! 67: #include "url.h" /* for Curl_safefree() */
! 68: #include "multiif.h"
! 69: #include "sockaddr.h" /* required for Curl_sockaddr_storage */
! 70: #include "inet_ntop.h"
! 71: #include "inet_pton.h"
! 72: #include "vtls/vtls.h" /* for Curl_ssl_check_cxn() */
! 73: #include "progress.h"
! 74: #include "warnless.h"
! 75: #include "conncache.h"
! 76: #include "multihandle.h"
! 77: #include "system_win32.h"
! 78: #include "quic.h"
! 79: #include "socks.h"
! 80:
! 81: /* The last 3 #include files should be in this order */
! 82: #include "curl_printf.h"
! 83: #include "curl_memory.h"
! 84: #include "memdebug.h"
! 85:
! 86: #ifdef __SYMBIAN32__
! 87: /* This isn't actually supported under Symbian OS */
! 88: #undef SO_NOSIGPIPE
! 89: #endif
! 90:
! 91: static bool verifyconnect(curl_socket_t sockfd, int *error);
! 92:
! 93: #if defined(__DragonFly__) || defined(HAVE_WINSOCK_H)
! 94: /* DragonFlyBSD and Windows use millisecond units */
! 95: #define KEEPALIVE_FACTOR(x) (x *= 1000)
! 96: #else
! 97: #define KEEPALIVE_FACTOR(x)
! 98: #endif
! 99:
! 100: #if defined(HAVE_WINSOCK2_H) && !defined(SIO_KEEPALIVE_VALS)
! 101: #define SIO_KEEPALIVE_VALS _WSAIOW(IOC_VENDOR,4)
! 102:
! 103: struct tcp_keepalive {
! 104: u_long onoff;
! 105: u_long keepalivetime;
! 106: u_long keepaliveinterval;
! 107: };
! 108: #endif
! 109:
! 110: static void
! 111: tcpkeepalive(struct Curl_easy *data,
! 112: curl_socket_t sockfd)
! 113: {
! 114: int optval = data->set.tcp_keepalive?1:0;
! 115:
! 116: /* only set IDLE and INTVL if setting KEEPALIVE is successful */
! 117: if(setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE,
! 118: (void *)&optval, sizeof(optval)) < 0) {
! 119: infof(data, "Failed to set SO_KEEPALIVE on fd %d\n", sockfd);
! 120: }
! 121: else {
! 122: #if defined(SIO_KEEPALIVE_VALS)
! 123: struct tcp_keepalive vals;
! 124: DWORD dummy;
! 125: vals.onoff = 1;
! 126: optval = curlx_sltosi(data->set.tcp_keepidle);
! 127: KEEPALIVE_FACTOR(optval);
! 128: vals.keepalivetime = optval;
! 129: optval = curlx_sltosi(data->set.tcp_keepintvl);
! 130: KEEPALIVE_FACTOR(optval);
! 131: vals.keepaliveinterval = optval;
! 132: if(WSAIoctl(sockfd, SIO_KEEPALIVE_VALS, (LPVOID) &vals, sizeof(vals),
! 133: NULL, 0, &dummy, NULL, NULL) != 0) {
! 134: infof(data, "Failed to set SIO_KEEPALIVE_VALS on fd %d: %d\n",
! 135: (int)sockfd, WSAGetLastError());
! 136: }
! 137: #else
! 138: #ifdef TCP_KEEPIDLE
! 139: optval = curlx_sltosi(data->set.tcp_keepidle);
! 140: KEEPALIVE_FACTOR(optval);
! 141: if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPIDLE,
! 142: (void *)&optval, sizeof(optval)) < 0) {
! 143: infof(data, "Failed to set TCP_KEEPIDLE on fd %d\n", sockfd);
! 144: }
! 145: #endif
! 146: #ifdef TCP_KEEPINTVL
! 147: optval = curlx_sltosi(data->set.tcp_keepintvl);
! 148: KEEPALIVE_FACTOR(optval);
! 149: if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPINTVL,
! 150: (void *)&optval, sizeof(optval)) < 0) {
! 151: infof(data, "Failed to set TCP_KEEPINTVL on fd %d\n", sockfd);
! 152: }
! 153: #endif
! 154: #ifdef TCP_KEEPALIVE
! 155: /* Mac OS X style */
! 156: optval = curlx_sltosi(data->set.tcp_keepidle);
! 157: KEEPALIVE_FACTOR(optval);
! 158: if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPALIVE,
! 159: (void *)&optval, sizeof(optval)) < 0) {
! 160: infof(data, "Failed to set TCP_KEEPALIVE on fd %d\n", sockfd);
! 161: }
! 162: #endif
! 163: #endif
! 164: }
! 165: }
! 166:
! 167: static CURLcode
! 168: singleipconnect(struct connectdata *conn,
! 169: const Curl_addrinfo *ai, /* start connecting to this */
! 170: int tempindex); /* 0 or 1 among the temp ones */
! 171:
! 172: /*
! 173: * Curl_timeleft() returns the amount of milliseconds left allowed for the
! 174: * transfer/connection. If the value is 0, there's no timeout (ie there's
! 175: * infinite time left). If the value is negative, the timeout time has already
! 176: * elapsed.
! 177: *
! 178: * The start time is stored in progress.t_startsingle - as set with
! 179: * Curl_pgrsTime(..., TIMER_STARTSINGLE);
! 180: *
! 181: * If 'nowp' is non-NULL, it points to the current time.
! 182: * 'duringconnect' is FALSE if not during a connect, as then of course the
! 183: * connect timeout is not taken into account!
! 184: *
! 185: * @unittest: 1303
! 186: */
! 187: timediff_t Curl_timeleft(struct Curl_easy *data,
! 188: struct curltime *nowp,
! 189: bool duringconnect)
! 190: {
! 191: int timeout_set = 0;
! 192: timediff_t timeout_ms = duringconnect?DEFAULT_CONNECT_TIMEOUT:0;
! 193: struct curltime now;
! 194:
! 195: /* if a timeout is set, use the most restrictive one */
! 196:
! 197: if(data->set.timeout > 0)
! 198: timeout_set |= 1;
! 199: if(duringconnect && (data->set.connecttimeout > 0))
! 200: timeout_set |= 2;
! 201:
! 202: switch(timeout_set) {
! 203: case 1:
! 204: timeout_ms = data->set.timeout;
! 205: break;
! 206: case 2:
! 207: timeout_ms = data->set.connecttimeout;
! 208: break;
! 209: case 3:
! 210: if(data->set.timeout < data->set.connecttimeout)
! 211: timeout_ms = data->set.timeout;
! 212: else
! 213: timeout_ms = data->set.connecttimeout;
! 214: break;
! 215: default:
! 216: /* use the default */
! 217: if(!duringconnect)
! 218: /* if we're not during connect, there's no default timeout so if we're
! 219: at zero we better just return zero and not make it a negative number
! 220: by the math below */
! 221: return 0;
! 222: break;
! 223: }
! 224:
! 225: if(!nowp) {
! 226: now = Curl_now();
! 227: nowp = &now;
! 228: }
! 229:
! 230: /* subtract elapsed time */
! 231: if(duringconnect)
! 232: /* since this most recent connect started */
! 233: timeout_ms -= Curl_timediff(*nowp, data->progress.t_startsingle);
! 234: else
! 235: /* since the entire operation started */
! 236: timeout_ms -= Curl_timediff(*nowp, data->progress.t_startop);
! 237: if(!timeout_ms)
! 238: /* avoid returning 0 as that means no timeout! */
! 239: return -1;
! 240:
! 241: return timeout_ms;
! 242: }
! 243:
! 244: static CURLcode bindlocal(struct connectdata *conn,
! 245: curl_socket_t sockfd, int af, unsigned int scope)
! 246: {
! 247: struct Curl_easy *data = conn->data;
! 248:
! 249: struct Curl_sockaddr_storage sa;
! 250: struct sockaddr *sock = (struct sockaddr *)&sa; /* bind to this address */
! 251: curl_socklen_t sizeof_sa = 0; /* size of the data sock points to */
! 252: struct sockaddr_in *si4 = (struct sockaddr_in *)&sa;
! 253: #ifdef ENABLE_IPV6
! 254: struct sockaddr_in6 *si6 = (struct sockaddr_in6 *)&sa;
! 255: #endif
! 256:
! 257: struct Curl_dns_entry *h = NULL;
! 258: unsigned short port = data->set.localport; /* use this port number, 0 for
! 259: "random" */
! 260: /* how many port numbers to try to bind to, increasing one at a time */
! 261: int portnum = data->set.localportrange;
! 262: const char *dev = data->set.str[STRING_DEVICE];
! 263: int error;
! 264:
! 265: /*************************************************************
! 266: * Select device to bind socket to
! 267: *************************************************************/
! 268: if(!dev && !port)
! 269: /* no local kind of binding was requested */
! 270: return CURLE_OK;
! 271:
! 272: memset(&sa, 0, sizeof(struct Curl_sockaddr_storage));
! 273:
! 274: if(dev && (strlen(dev)<255) ) {
! 275: char myhost[256] = "";
! 276: int done = 0; /* -1 for error, 1 for address found */
! 277: bool is_interface = FALSE;
! 278: bool is_host = FALSE;
! 279: static const char *if_prefix = "if!";
! 280: static const char *host_prefix = "host!";
! 281:
! 282: if(strncmp(if_prefix, dev, strlen(if_prefix)) == 0) {
! 283: dev += strlen(if_prefix);
! 284: is_interface = TRUE;
! 285: }
! 286: else if(strncmp(host_prefix, dev, strlen(host_prefix)) == 0) {
! 287: dev += strlen(host_prefix);
! 288: is_host = TRUE;
! 289: }
! 290:
! 291: /* interface */
! 292: if(!is_host) {
! 293: #ifdef SO_BINDTODEVICE
! 294: /* I am not sure any other OSs than Linux that provide this feature,
! 295: * and at the least I cannot test. --Ben
! 296: *
! 297: * This feature allows one to tightly bind the local socket to a
! 298: * particular interface. This will force even requests to other
! 299: * local interfaces to go out the external interface.
! 300: *
! 301: *
! 302: * Only bind to the interface when specified as interface, not just
! 303: * as a hostname or ip address.
! 304: *
! 305: * interface might be a VRF, eg: vrf-blue, which means it cannot be
! 306: * converted to an IP address and would fail Curl_if2ip. Simply try
! 307: * to use it straight away.
! 308: */
! 309: if(setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE,
! 310: dev, (curl_socklen_t)strlen(dev) + 1) == 0) {
! 311: /* This is typically "errno 1, error: Operation not permitted" if
! 312: * you're not running as root or another suitable privileged
! 313: * user.
! 314: * If it succeeds it means the parameter was a valid interface and
! 315: * not an IP address. Return immediately.
! 316: */
! 317: return CURLE_OK;
! 318: }
! 319: #endif
! 320:
! 321: switch(Curl_if2ip(af, scope, conn->scope_id, dev,
! 322: myhost, sizeof(myhost))) {
! 323: case IF2IP_NOT_FOUND:
! 324: if(is_interface) {
! 325: /* Do not fall back to treating it as a host name */
! 326: failf(data, "Couldn't bind to interface '%s'", dev);
! 327: return CURLE_INTERFACE_FAILED;
! 328: }
! 329: break;
! 330: case IF2IP_AF_NOT_SUPPORTED:
! 331: /* Signal the caller to try another address family if available */
! 332: return CURLE_UNSUPPORTED_PROTOCOL;
! 333: case IF2IP_FOUND:
! 334: is_interface = TRUE;
! 335: /*
! 336: * We now have the numerical IP address in the 'myhost' buffer
! 337: */
! 338: infof(data, "Local Interface %s is ip %s using address family %i\n",
! 339: dev, myhost, af);
! 340: done = 1;
! 341: break;
! 342: }
! 343: }
! 344: if(!is_interface) {
! 345: /*
! 346: * This was not an interface, resolve the name as a host name
! 347: * or IP number
! 348: *
! 349: * Temporarily force name resolution to use only the address type
! 350: * of the connection. The resolve functions should really be changed
! 351: * to take a type parameter instead.
! 352: */
! 353: long ipver = conn->ip_version;
! 354: int rc;
! 355:
! 356: if(af == AF_INET)
! 357: conn->ip_version = CURL_IPRESOLVE_V4;
! 358: #ifdef ENABLE_IPV6
! 359: else if(af == AF_INET6)
! 360: conn->ip_version = CURL_IPRESOLVE_V6;
! 361: #endif
! 362:
! 363: rc = Curl_resolv(conn, dev, 0, FALSE, &h);
! 364: if(rc == CURLRESOLV_PENDING)
! 365: (void)Curl_resolver_wait_resolv(conn, &h);
! 366: conn->ip_version = ipver;
! 367:
! 368: if(h) {
! 369: /* convert the resolved address, sizeof myhost >= INET_ADDRSTRLEN */
! 370: Curl_printable_address(h->addr, myhost, sizeof(myhost));
! 371: infof(data, "Name '%s' family %i resolved to '%s' family %i\n",
! 372: dev, af, myhost, h->addr->ai_family);
! 373: Curl_resolv_unlock(data, h);
! 374: if(af != h->addr->ai_family) {
! 375: /* bad IP version combo, signal the caller to try another address
! 376: family if available */
! 377: return CURLE_UNSUPPORTED_PROTOCOL;
! 378: }
! 379: done = 1;
! 380: }
! 381: else {
! 382: /*
! 383: * provided dev was no interface (or interfaces are not supported
! 384: * e.g. solaris) no ip address and no domain we fail here
! 385: */
! 386: done = -1;
! 387: }
! 388: }
! 389:
! 390: if(done > 0) {
! 391: #ifdef ENABLE_IPV6
! 392: /* IPv6 address */
! 393: if(af == AF_INET6) {
! 394: #ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
! 395: char *scope_ptr = strchr(myhost, '%');
! 396: if(scope_ptr)
! 397: *(scope_ptr++) = 0;
! 398: #endif
! 399: if(Curl_inet_pton(AF_INET6, myhost, &si6->sin6_addr) > 0) {
! 400: si6->sin6_family = AF_INET6;
! 401: si6->sin6_port = htons(port);
! 402: #ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
! 403: if(scope_ptr)
! 404: /* The "myhost" string either comes from Curl_if2ip or from
! 405: Curl_printable_address. The latter returns only numeric scope
! 406: IDs and the former returns none at all. So the scope ID, if
! 407: present, is known to be numeric */
! 408: si6->sin6_scope_id = atoi(scope_ptr);
! 409: #endif
! 410: }
! 411: sizeof_sa = sizeof(struct sockaddr_in6);
! 412: }
! 413: else
! 414: #endif
! 415: /* IPv4 address */
! 416: if((af == AF_INET) &&
! 417: (Curl_inet_pton(AF_INET, myhost, &si4->sin_addr) > 0)) {
! 418: si4->sin_family = AF_INET;
! 419: si4->sin_port = htons(port);
! 420: sizeof_sa = sizeof(struct sockaddr_in);
! 421: }
! 422: }
! 423:
! 424: if(done < 1) {
! 425: /* errorbuf is set false so failf will overwrite any message already in
! 426: the error buffer, so the user receives this error message instead of a
! 427: generic resolve error. */
! 428: data->state.errorbuf = FALSE;
! 429: failf(data, "Couldn't bind to '%s'", dev);
! 430: return CURLE_INTERFACE_FAILED;
! 431: }
! 432: }
! 433: else {
! 434: /* no device was given, prepare sa to match af's needs */
! 435: #ifdef ENABLE_IPV6
! 436: if(af == AF_INET6) {
! 437: si6->sin6_family = AF_INET6;
! 438: si6->sin6_port = htons(port);
! 439: sizeof_sa = sizeof(struct sockaddr_in6);
! 440: }
! 441: else
! 442: #endif
! 443: if(af == AF_INET) {
! 444: si4->sin_family = AF_INET;
! 445: si4->sin_port = htons(port);
! 446: sizeof_sa = sizeof(struct sockaddr_in);
! 447: }
! 448: }
! 449:
! 450: for(;;) {
! 451: if(bind(sockfd, sock, sizeof_sa) >= 0) {
! 452: /* we succeeded to bind */
! 453: struct Curl_sockaddr_storage add;
! 454: curl_socklen_t size = sizeof(add);
! 455: memset(&add, 0, sizeof(struct Curl_sockaddr_storage));
! 456: if(getsockname(sockfd, (struct sockaddr *) &add, &size) < 0) {
! 457: char buffer[STRERROR_LEN];
! 458: data->state.os_errno = error = SOCKERRNO;
! 459: failf(data, "getsockname() failed with errno %d: %s",
! 460: error, Curl_strerror(error, buffer, sizeof(buffer)));
! 461: return CURLE_INTERFACE_FAILED;
! 462: }
! 463: infof(data, "Local port: %hu\n", port);
! 464: conn->bits.bound = TRUE;
! 465: return CURLE_OK;
! 466: }
! 467:
! 468: if(--portnum > 0) {
! 469: infof(data, "Bind to local port %hu failed, trying next\n", port);
! 470: port++; /* try next port */
! 471: /* We re-use/clobber the port variable here below */
! 472: if(sock->sa_family == AF_INET)
! 473: si4->sin_port = ntohs(port);
! 474: #ifdef ENABLE_IPV6
! 475: else
! 476: si6->sin6_port = ntohs(port);
! 477: #endif
! 478: }
! 479: else
! 480: break;
! 481: }
! 482: {
! 483: char buffer[STRERROR_LEN];
! 484: data->state.os_errno = error = SOCKERRNO;
! 485: failf(data, "bind failed with errno %d: %s",
! 486: error, Curl_strerror(error, buffer, sizeof(buffer)));
! 487: }
! 488:
! 489: return CURLE_INTERFACE_FAILED;
! 490: }
! 491:
! 492: /*
! 493: * verifyconnect() returns TRUE if the connect really has happened.
! 494: */
! 495: static bool verifyconnect(curl_socket_t sockfd, int *error)
! 496: {
! 497: bool rc = TRUE;
! 498: #ifdef SO_ERROR
! 499: int err = 0;
! 500: curl_socklen_t errSize = sizeof(err);
! 501:
! 502: #ifdef WIN32
! 503: /*
! 504: * In October 2003 we effectively nullified this function on Windows due to
! 505: * problems with it using all CPU in multi-threaded cases.
! 506: *
! 507: * In May 2004, we bring it back to offer more info back on connect failures.
! 508: * Gisle Vanem could reproduce the former problems with this function, but
! 509: * could avoid them by adding this SleepEx() call below:
! 510: *
! 511: * "I don't have Rational Quantify, but the hint from his post was
! 512: * ntdll::NtRemoveIoCompletion(). So I'd assume the SleepEx (or maybe
! 513: * just Sleep(0) would be enough?) would release whatever
! 514: * mutex/critical-section the ntdll call is waiting on.
! 515: *
! 516: * Someone got to verify this on Win-NT 4.0, 2000."
! 517: */
! 518:
! 519: #ifdef _WIN32_WCE
! 520: Sleep(0);
! 521: #else
! 522: SleepEx(0, FALSE);
! 523: #endif
! 524:
! 525: #endif
! 526:
! 527: if(0 != getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (void *)&err, &errSize))
! 528: err = SOCKERRNO;
! 529: #ifdef _WIN32_WCE
! 530: /* Old WinCE versions don't support SO_ERROR */
! 531: if(WSAENOPROTOOPT == err) {
! 532: SET_SOCKERRNO(0);
! 533: err = 0;
! 534: }
! 535: #endif
! 536: #if defined(EBADIOCTL) && defined(__minix)
! 537: /* Minix 3.1.x doesn't support getsockopt on UDP sockets */
! 538: if(EBADIOCTL == err) {
! 539: SET_SOCKERRNO(0);
! 540: err = 0;
! 541: }
! 542: #endif
! 543: if((0 == err) || (EISCONN == err))
! 544: /* we are connected, awesome! */
! 545: rc = TRUE;
! 546: else
! 547: /* This wasn't a successful connect */
! 548: rc = FALSE;
! 549: if(error)
! 550: *error = err;
! 551: #else
! 552: (void)sockfd;
! 553: if(error)
! 554: *error = SOCKERRNO;
! 555: #endif
! 556: return rc;
! 557: }
! 558:
! 559: /* update tempaddr[tempindex] (to the next entry), makes sure to stick
! 560: to the correct family */
! 561: static Curl_addrinfo *ainext(struct connectdata *conn,
! 562: int tempindex,
! 563: bool next) /* use current or next entry */
! 564: {
! 565: Curl_addrinfo *ai = conn->tempaddr[tempindex];
! 566: if(ai && next)
! 567: ai = ai->ai_next;
! 568: while(ai && (ai->ai_family != conn->tempfamily[tempindex]))
! 569: ai = ai->ai_next;
! 570: conn->tempaddr[tempindex] = ai;
! 571: return ai;
! 572: }
! 573:
! 574: /* Used within the multi interface. Try next IP address, return TRUE if no
! 575: more address exists or error */
! 576: static CURLcode trynextip(struct connectdata *conn,
! 577: int sockindex,
! 578: int tempindex)
! 579: {
! 580: CURLcode result = CURLE_COULDNT_CONNECT;
! 581:
! 582: /* First clean up after the failed socket.
! 583: Don't close it yet to ensure that the next IP's socket gets a different
! 584: file descriptor, which can prevent bugs when the curl_multi_socket_action
! 585: interface is used with certain select() replacements such as kqueue. */
! 586: curl_socket_t fd_to_close = conn->tempsock[tempindex];
! 587: conn->tempsock[tempindex] = CURL_SOCKET_BAD;
! 588:
! 589: if(sockindex == FIRSTSOCKET) {
! 590: Curl_addrinfo *ai = conn->tempaddr[tempindex];
! 591:
! 592: while(ai) {
! 593: if(ai) {
! 594: result = singleipconnect(conn, ai, tempindex);
! 595: if(result == CURLE_COULDNT_CONNECT) {
! 596: ai = ainext(conn, tempindex, TRUE);
! 597: continue;
! 598: }
! 599: }
! 600: break;
! 601: }
! 602: }
! 603:
! 604: if(fd_to_close != CURL_SOCKET_BAD)
! 605: Curl_closesocket(conn, fd_to_close);
! 606:
! 607: return result;
! 608: }
! 609:
! 610: /* Copies connection info into the session handle to make it available
! 611: when the session handle is no longer associated with a connection. */
! 612: void Curl_persistconninfo(struct connectdata *conn)
! 613: {
! 614: memcpy(conn->data->info.conn_primary_ip, conn->primary_ip, MAX_IPADR_LEN);
! 615: memcpy(conn->data->info.conn_local_ip, conn->local_ip, MAX_IPADR_LEN);
! 616: conn->data->info.conn_scheme = conn->handler->scheme;
! 617: conn->data->info.conn_protocol = conn->handler->protocol;
! 618: conn->data->info.conn_primary_port = conn->primary_port;
! 619: conn->data->info.conn_local_port = conn->local_port;
! 620: }
! 621:
! 622: /* retrieves ip address and port from a sockaddr structure.
! 623: note it calls Curl_inet_ntop which sets errno on fail, not SOCKERRNO. */
! 624: bool Curl_addr2string(struct sockaddr *sa, curl_socklen_t salen,
! 625: char *addr, long *port)
! 626: {
! 627: struct sockaddr_in *si = NULL;
! 628: #ifdef ENABLE_IPV6
! 629: struct sockaddr_in6 *si6 = NULL;
! 630: #endif
! 631: #if defined(HAVE_SYS_UN_H) && defined(AF_UNIX)
! 632: struct sockaddr_un *su = NULL;
! 633: #else
! 634: (void)salen;
! 635: #endif
! 636:
! 637: switch(sa->sa_family) {
! 638: case AF_INET:
! 639: si = (struct sockaddr_in *)(void *) sa;
! 640: if(Curl_inet_ntop(sa->sa_family, &si->sin_addr,
! 641: addr, MAX_IPADR_LEN)) {
! 642: unsigned short us_port = ntohs(si->sin_port);
! 643: *port = us_port;
! 644: return TRUE;
! 645: }
! 646: break;
! 647: #ifdef ENABLE_IPV6
! 648: case AF_INET6:
! 649: si6 = (struct sockaddr_in6 *)(void *) sa;
! 650: if(Curl_inet_ntop(sa->sa_family, &si6->sin6_addr,
! 651: addr, MAX_IPADR_LEN)) {
! 652: unsigned short us_port = ntohs(si6->sin6_port);
! 653: *port = us_port;
! 654: return TRUE;
! 655: }
! 656: break;
! 657: #endif
! 658: #if defined(HAVE_SYS_UN_H) && defined(AF_UNIX)
! 659: case AF_UNIX:
! 660: if(salen > (curl_socklen_t)sizeof(sa_family_t)) {
! 661: su = (struct sockaddr_un*)sa;
! 662: msnprintf(addr, MAX_IPADR_LEN, "%s", su->sun_path);
! 663: }
! 664: else
! 665: addr[0] = 0; /* socket with no name */
! 666: *port = 0;
! 667: return TRUE;
! 668: #endif
! 669: default:
! 670: break;
! 671: }
! 672:
! 673: addr[0] = '\0';
! 674: *port = 0;
! 675: errno = EAFNOSUPPORT;
! 676: return FALSE;
! 677: }
! 678:
! 679: /* retrieves the start/end point information of a socket of an established
! 680: connection */
! 681: void Curl_updateconninfo(struct connectdata *conn, curl_socket_t sockfd)
! 682: {
! 683: if(conn->transport == TRNSPRT_TCP) {
! 684: #if defined(HAVE_GETPEERNAME) || defined(HAVE_GETSOCKNAME)
! 685: if(!conn->bits.reuse && !conn->bits.tcp_fastopen) {
! 686: struct Curl_easy *data = conn->data;
! 687: char buffer[STRERROR_LEN];
! 688: struct Curl_sockaddr_storage ssrem;
! 689: struct Curl_sockaddr_storage ssloc;
! 690: curl_socklen_t plen;
! 691: curl_socklen_t slen;
! 692: #ifdef HAVE_GETPEERNAME
! 693: plen = sizeof(struct Curl_sockaddr_storage);
! 694: if(getpeername(sockfd, (struct sockaddr*) &ssrem, &plen)) {
! 695: int error = SOCKERRNO;
! 696: failf(data, "getpeername() failed with errno %d: %s",
! 697: error, Curl_strerror(error, buffer, sizeof(buffer)));
! 698: return;
! 699: }
! 700: #endif
! 701: #ifdef HAVE_GETSOCKNAME
! 702: slen = sizeof(struct Curl_sockaddr_storage);
! 703: memset(&ssloc, 0, sizeof(ssloc));
! 704: if(getsockname(sockfd, (struct sockaddr*) &ssloc, &slen)) {
! 705: int error = SOCKERRNO;
! 706: failf(data, "getsockname() failed with errno %d: %s",
! 707: error, Curl_strerror(error, buffer, sizeof(buffer)));
! 708: return;
! 709: }
! 710: #endif
! 711: #ifdef HAVE_GETPEERNAME
! 712: if(!Curl_addr2string((struct sockaddr*)&ssrem, plen,
! 713: conn->primary_ip, &conn->primary_port)) {
! 714: failf(data, "ssrem inet_ntop() failed with errno %d: %s",
! 715: errno, Curl_strerror(errno, buffer, sizeof(buffer)));
! 716: return;
! 717: }
! 718: memcpy(conn->ip_addr_str, conn->primary_ip, MAX_IPADR_LEN);
! 719: #endif
! 720: #ifdef HAVE_GETSOCKNAME
! 721: if(!Curl_addr2string((struct sockaddr*)&ssloc, slen,
! 722: conn->local_ip, &conn->local_port)) {
! 723: failf(data, "ssloc inet_ntop() failed with errno %d: %s",
! 724: errno, Curl_strerror(errno, buffer, sizeof(buffer)));
! 725: return;
! 726: }
! 727: #endif
! 728: }
! 729: #else /* !HAVE_GETSOCKNAME && !HAVE_GETPEERNAME */
! 730: (void)sockfd; /* unused */
! 731: #endif
! 732: } /* end of TCP-only section */
! 733:
! 734: /* persist connection info in session handle */
! 735: Curl_persistconninfo(conn);
! 736: }
! 737:
! 738: /* After a TCP connection to the proxy has been verified, this function does
! 739: the next magic steps. If 'done' isn't set TRUE, it is not done yet and
! 740: must be called again.
! 741:
! 742: Note: this function's sub-functions call failf()
! 743:
! 744: */
! 745: static CURLcode connect_SOCKS(struct connectdata *conn, int sockindex,
! 746: bool *done)
! 747: {
! 748: CURLcode result = CURLE_OK;
! 749:
! 750: if(conn->bits.socksproxy) {
! 751: #ifndef CURL_DISABLE_PROXY
! 752: /* for the secondary socket (FTP), use the "connect to host"
! 753: * but ignore the "connect to port" (use the secondary port)
! 754: */
! 755: const char * const host =
! 756: conn->bits.httpproxy ?
! 757: conn->http_proxy.host.name :
! 758: conn->bits.conn_to_host ?
! 759: conn->conn_to_host.name :
! 760: sockindex == SECONDARYSOCKET ?
! 761: conn->secondaryhostname : conn->host.name;
! 762: const int port =
! 763: conn->bits.httpproxy ? (int)conn->http_proxy.port :
! 764: sockindex == SECONDARYSOCKET ? conn->secondary_port :
! 765: conn->bits.conn_to_port ? conn->conn_to_port :
! 766: conn->remote_port;
! 767: switch(conn->socks_proxy.proxytype) {
! 768: case CURLPROXY_SOCKS5:
! 769: case CURLPROXY_SOCKS5_HOSTNAME:
! 770: result = Curl_SOCKS5(conn->socks_proxy.user, conn->socks_proxy.passwd,
! 771: host, port, sockindex, conn, done);
! 772: break;
! 773:
! 774: case CURLPROXY_SOCKS4:
! 775: case CURLPROXY_SOCKS4A:
! 776: result = Curl_SOCKS4(conn->socks_proxy.user, host, port, sockindex,
! 777: conn, done);
! 778: break;
! 779:
! 780: default:
! 781: failf(conn->data, "unknown proxytype option given");
! 782: result = CURLE_COULDNT_CONNECT;
! 783: } /* switch proxytype */
! 784: #else
! 785: (void)sockindex;
! 786: #endif /* CURL_DISABLE_PROXY */
! 787: }
! 788: else
! 789: *done = TRUE; /* no SOCKS proxy, so consider us connected */
! 790:
! 791: return result;
! 792: }
! 793:
! 794: /*
! 795: * post_SOCKS() is called after a successful connect to the peer, which
! 796: * *could* be a SOCKS proxy
! 797: */
! 798: static void post_SOCKS(struct connectdata *conn,
! 799: int sockindex,
! 800: bool *connected)
! 801: {
! 802: conn->bits.tcpconnect[sockindex] = TRUE;
! 803:
! 804: *connected = TRUE;
! 805: if(sockindex == FIRSTSOCKET)
! 806: Curl_pgrsTime(conn->data, TIMER_CONNECT); /* connect done */
! 807: Curl_updateconninfo(conn, conn->sock[sockindex]);
! 808: Curl_verboseconnect(conn);
! 809: conn->data->info.numconnects++; /* to track the number of connections made */
! 810: }
! 811:
! 812: /*
! 813: * Curl_is_connected() checks if the socket has connected.
! 814: */
! 815:
! 816: CURLcode Curl_is_connected(struct connectdata *conn,
! 817: int sockindex,
! 818: bool *connected)
! 819: {
! 820: struct Curl_easy *data = conn->data;
! 821: CURLcode result = CURLE_OK;
! 822: timediff_t allow;
! 823: int error = 0;
! 824: struct curltime now;
! 825: int rc;
! 826: int i;
! 827:
! 828: DEBUGASSERT(sockindex >= FIRSTSOCKET && sockindex <= SECONDARYSOCKET);
! 829:
! 830: *connected = FALSE; /* a very negative world view is best */
! 831:
! 832: if(conn->bits.tcpconnect[sockindex]) {
! 833: /* we are connected already! */
! 834: *connected = TRUE;
! 835: return CURLE_OK;
! 836: }
! 837:
! 838: now = Curl_now();
! 839:
! 840: /* figure out how long time we have left to connect */
! 841: allow = Curl_timeleft(data, &now, TRUE);
! 842:
! 843: if(allow < 0) {
! 844: /* time-out, bail out, go home */
! 845: failf(data, "Connection time-out");
! 846: return CURLE_OPERATION_TIMEDOUT;
! 847: }
! 848:
! 849: if(SOCKS_STATE(conn->cnnct.state)) {
! 850: /* still doing SOCKS */
! 851: result = connect_SOCKS(conn, sockindex, connected);
! 852: if(!result && *connected)
! 853: post_SOCKS(conn, sockindex, connected);
! 854: return result;
! 855: }
! 856:
! 857: for(i = 0; i<2; i++) {
! 858: const int other = i ^ 1;
! 859: if(conn->tempsock[i] == CURL_SOCKET_BAD)
! 860: continue;
! 861:
! 862: #ifdef ENABLE_QUIC
! 863: if(conn->transport == TRNSPRT_QUIC) {
! 864: result = Curl_quic_is_connected(conn, i, connected);
! 865: if(result) {
! 866: error = SOCKERRNO;
! 867: goto error;
! 868: }
! 869: if(*connected) {
! 870: /* use this socket from now on */
! 871: conn->sock[sockindex] = conn->tempsock[i];
! 872: conn->ip_addr = conn->tempaddr[i];
! 873: conn->tempsock[i] = CURL_SOCKET_BAD;
! 874: post_SOCKS(conn, sockindex, connected);
! 875: connkeep(conn, "HTTP/3 default");
! 876: }
! 877: return result;
! 878: }
! 879: #endif
! 880:
! 881: #ifdef mpeix
! 882: /* Call this function once now, and ignore the results. We do this to
! 883: "clear" the error state on the socket so that we can later read it
! 884: reliably. This is reported necessary on the MPE/iX operating system. */
! 885: (void)verifyconnect(conn->tempsock[i], NULL);
! 886: #endif
! 887:
! 888: /* check socket for connect */
! 889: rc = SOCKET_WRITABLE(conn->tempsock[i], 0);
! 890:
! 891: if(rc == 0) { /* no connection yet */
! 892: error = 0;
! 893: if(Curl_timediff(now, conn->connecttime) >= conn->timeoutms_per_addr) {
! 894: infof(data, "After %" CURL_FORMAT_TIMEDIFF_T
! 895: "ms connect time, move on!\n", conn->timeoutms_per_addr);
! 896: error = ETIMEDOUT;
! 897: }
! 898:
! 899: /* should we try another protocol family? */
! 900: if(i == 0 && !conn->parallel_connect &&
! 901: (Curl_timediff(now, conn->connecttime) >=
! 902: data->set.happy_eyeballs_timeout)) {
! 903: conn->parallel_connect = TRUE; /* starting now */
! 904: trynextip(conn, sockindex, 1);
! 905: }
! 906: }
! 907: else if(rc == CURL_CSELECT_OUT || conn->bits.tcp_fastopen) {
! 908: if(verifyconnect(conn->tempsock[i], &error)) {
! 909: /* we are connected with TCP, awesome! */
! 910:
! 911: /* use this socket from now on */
! 912: conn->sock[sockindex] = conn->tempsock[i];
! 913: conn->ip_addr = conn->tempaddr[i];
! 914: conn->tempsock[i] = CURL_SOCKET_BAD;
! 915: #ifdef ENABLE_IPV6
! 916: conn->bits.ipv6 = (conn->ip_addr->ai_family == AF_INET6)?TRUE:FALSE;
! 917: #endif
! 918:
! 919: /* close the other socket, if open */
! 920: if(conn->tempsock[other] != CURL_SOCKET_BAD) {
! 921: Curl_closesocket(conn, conn->tempsock[other]);
! 922: conn->tempsock[other] = CURL_SOCKET_BAD;
! 923: }
! 924:
! 925: /* see if we need to kick off any SOCKS proxy magic once we
! 926: connected */
! 927: result = connect_SOCKS(conn, sockindex, connected);
! 928: if(result || !*connected)
! 929: return result;
! 930:
! 931: post_SOCKS(conn, sockindex, connected);
! 932:
! 933: return CURLE_OK;
! 934: }
! 935: infof(data, "Connection failed\n");
! 936: }
! 937: else if(rc & CURL_CSELECT_ERR)
! 938: (void)verifyconnect(conn->tempsock[i], &error);
! 939:
! 940: #ifdef ENABLE_QUIC
! 941: error:
! 942: #endif
! 943: /*
! 944: * The connection failed here, we should attempt to connect to the "next
! 945: * address" for the given host. But first remember the latest error.
! 946: */
! 947: if(error) {
! 948: data->state.os_errno = error;
! 949: SET_SOCKERRNO(error);
! 950: if(conn->tempaddr[i]) {
! 951: CURLcode status;
! 952: #ifndef CURL_DISABLE_VERBOSE_STRINGS
! 953: char ipaddress[MAX_IPADR_LEN];
! 954: char buffer[STRERROR_LEN];
! 955: Curl_printable_address(conn->tempaddr[i], ipaddress, MAX_IPADR_LEN);
! 956: #endif
! 957: infof(data, "connect to %s port %ld failed: %s\n",
! 958: ipaddress, conn->port,
! 959: Curl_strerror(error, buffer, sizeof(buffer)));
! 960:
! 961: conn->timeoutms_per_addr = conn->tempaddr[i]->ai_next == NULL ?
! 962: allow : allow / 2;
! 963: ainext(conn, i, TRUE);
! 964: status = trynextip(conn, sockindex, i);
! 965: if((status != CURLE_COULDNT_CONNECT) ||
! 966: conn->tempsock[other] == CURL_SOCKET_BAD)
! 967: /* the last attempt failed and no other sockets remain open */
! 968: result = status;
! 969: }
! 970: }
! 971: }
! 972:
! 973: if(result) {
! 974: /* no more addresses to try */
! 975: const char *hostname;
! 976: char buffer[STRERROR_LEN];
! 977:
! 978: /* if the first address family runs out of addresses to try before
! 979: the happy eyeball timeout, go ahead and try the next family now */
! 980: {
! 981: result = trynextip(conn, sockindex, 1);
! 982: if(!result)
! 983: return result;
! 984: }
! 985:
! 986: if(conn->bits.socksproxy)
! 987: hostname = conn->socks_proxy.host.name;
! 988: else if(conn->bits.httpproxy)
! 989: hostname = conn->http_proxy.host.name;
! 990: else if(conn->bits.conn_to_host)
! 991: hostname = conn->conn_to_host.name;
! 992: else
! 993: hostname = conn->host.name;
! 994:
! 995: failf(data, "Failed to connect to %s port %ld: %s",
! 996: hostname, conn->port,
! 997: Curl_strerror(error, buffer, sizeof(buffer)));
! 998:
! 999: #ifdef WSAETIMEDOUT
! 1000: if(WSAETIMEDOUT == data->state.os_errno)
! 1001: result = CURLE_OPERATION_TIMEDOUT;
! 1002: #elif defined(ETIMEDOUT)
! 1003: if(ETIMEDOUT == data->state.os_errno)
! 1004: result = CURLE_OPERATION_TIMEDOUT;
! 1005: #endif
! 1006: }
! 1007:
! 1008: return result;
! 1009: }
! 1010:
! 1011: static void tcpnodelay(struct connectdata *conn, curl_socket_t sockfd)
! 1012: {
! 1013: #if defined(TCP_NODELAY)
! 1014: curl_socklen_t onoff = (curl_socklen_t) 1;
! 1015: int level = IPPROTO_TCP;
! 1016: #if !defined(CURL_DISABLE_VERBOSE_STRINGS)
! 1017: struct Curl_easy *data = conn->data;
! 1018: char buffer[STRERROR_LEN];
! 1019: #else
! 1020: (void) conn;
! 1021: #endif
! 1022:
! 1023: if(setsockopt(sockfd, level, TCP_NODELAY, (void *)&onoff,
! 1024: sizeof(onoff)) < 0)
! 1025: infof(data, "Could not set TCP_NODELAY: %s\n",
! 1026: Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
! 1027: #else
! 1028: (void)conn;
! 1029: (void)sockfd;
! 1030: #endif
! 1031: }
! 1032:
! 1033: #ifdef SO_NOSIGPIPE
! 1034: /* The preferred method on Mac OS X (10.2 and later) to prevent SIGPIPEs when
! 1035: sending data to a dead peer (instead of relying on the 4th argument to send
! 1036: being MSG_NOSIGNAL). Possibly also existing and in use on other BSD
! 1037: systems? */
! 1038: static void nosigpipe(struct connectdata *conn,
! 1039: curl_socket_t sockfd)
! 1040: {
! 1041: struct Curl_easy *data = conn->data;
! 1042: int onoff = 1;
! 1043: if(setsockopt(sockfd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&onoff,
! 1044: sizeof(onoff)) < 0) {
! 1045: char buffer[STRERROR_LEN];
! 1046: infof(data, "Could not set SO_NOSIGPIPE: %s\n",
! 1047: Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
! 1048: }
! 1049: }
! 1050: #else
! 1051: #define nosigpipe(x,y) Curl_nop_stmt
! 1052: #endif
! 1053:
! 1054: #ifdef USE_WINSOCK
! 1055: /* When you run a program that uses the Windows Sockets API, you may
! 1056: experience slow performance when you copy data to a TCP server.
! 1057:
! 1058: https://support.microsoft.com/kb/823764
! 1059:
! 1060: Work-around: Make the Socket Send Buffer Size Larger Than the Program Send
! 1061: Buffer Size
! 1062:
! 1063: The problem described in this knowledge-base is applied only to pre-Vista
! 1064: Windows. Following function trying to detect OS version and skips
! 1065: SO_SNDBUF adjustment for Windows Vista and above.
! 1066: */
! 1067: #define DETECT_OS_NONE 0
! 1068: #define DETECT_OS_PREVISTA 1
! 1069: #define DETECT_OS_VISTA_OR_LATER 2
! 1070:
! 1071: void Curl_sndbufset(curl_socket_t sockfd)
! 1072: {
! 1073: int val = CURL_MAX_WRITE_SIZE + 32;
! 1074: int curval = 0;
! 1075: int curlen = sizeof(curval);
! 1076:
! 1077: static int detectOsState = DETECT_OS_NONE;
! 1078:
! 1079: if(detectOsState == DETECT_OS_NONE) {
! 1080: if(Curl_verify_windows_version(6, 0, PLATFORM_WINNT,
! 1081: VERSION_GREATER_THAN_EQUAL))
! 1082: detectOsState = DETECT_OS_VISTA_OR_LATER;
! 1083: else
! 1084: detectOsState = DETECT_OS_PREVISTA;
! 1085: }
! 1086:
! 1087: if(detectOsState == DETECT_OS_VISTA_OR_LATER)
! 1088: return;
! 1089:
! 1090: if(getsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (char *)&curval, &curlen) == 0)
! 1091: if(curval > val)
! 1092: return;
! 1093:
! 1094: setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (const char *)&val, sizeof(val));
! 1095: }
! 1096: #endif
! 1097:
! 1098: /*
! 1099: * singleipconnect()
! 1100: *
! 1101: * Note that even on connect fail it returns CURLE_OK, but with 'sock' set to
! 1102: * CURL_SOCKET_BAD. Other errors will however return proper errors.
! 1103: *
! 1104: * singleipconnect() connects to the given IP only, and it may return without
! 1105: * having connected.
! 1106: */
! 1107: static CURLcode singleipconnect(struct connectdata *conn,
! 1108: const Curl_addrinfo *ai,
! 1109: int tempindex)
! 1110: {
! 1111: struct Curl_sockaddr_ex addr;
! 1112: int rc = -1;
! 1113: int error = 0;
! 1114: bool isconnected = FALSE;
! 1115: struct Curl_easy *data = conn->data;
! 1116: curl_socket_t sockfd;
! 1117: CURLcode result;
! 1118: char ipaddress[MAX_IPADR_LEN];
! 1119: long port;
! 1120: bool is_tcp;
! 1121: #ifdef TCP_FASTOPEN_CONNECT
! 1122: int optval = 1;
! 1123: #endif
! 1124: char buffer[STRERROR_LEN];
! 1125: curl_socket_t *sockp = &conn->tempsock[tempindex];
! 1126: *sockp = CURL_SOCKET_BAD;
! 1127:
! 1128: result = Curl_socket(conn, ai, &addr, &sockfd);
! 1129: if(result)
! 1130: return result;
! 1131:
! 1132: /* store remote address and port used in this connection attempt */
! 1133: if(!Curl_addr2string((struct sockaddr*)&addr.sa_addr, addr.addrlen,
! 1134: ipaddress, &port)) {
! 1135: /* malformed address or bug in inet_ntop, try next address */
! 1136: failf(data, "sa_addr inet_ntop() failed with errno %d: %s",
! 1137: errno, Curl_strerror(errno, buffer, sizeof(buffer)));
! 1138: Curl_closesocket(conn, sockfd);
! 1139: return CURLE_OK;
! 1140: }
! 1141: infof(data, " Trying %s:%ld...\n", ipaddress, port);
! 1142:
! 1143: #ifdef ENABLE_IPV6
! 1144: is_tcp = (addr.family == AF_INET || addr.family == AF_INET6) &&
! 1145: addr.socktype == SOCK_STREAM;
! 1146: #else
! 1147: is_tcp = (addr.family == AF_INET) && addr.socktype == SOCK_STREAM;
! 1148: #endif
! 1149: if(is_tcp && data->set.tcp_nodelay)
! 1150: tcpnodelay(conn, sockfd);
! 1151:
! 1152: nosigpipe(conn, sockfd);
! 1153:
! 1154: Curl_sndbufset(sockfd);
! 1155:
! 1156: if(is_tcp && data->set.tcp_keepalive)
! 1157: tcpkeepalive(data, sockfd);
! 1158:
! 1159: if(data->set.fsockopt) {
! 1160: /* activate callback for setting socket options */
! 1161: Curl_set_in_callback(data, true);
! 1162: error = data->set.fsockopt(data->set.sockopt_client,
! 1163: sockfd,
! 1164: CURLSOCKTYPE_IPCXN);
! 1165: Curl_set_in_callback(data, false);
! 1166:
! 1167: if(error == CURL_SOCKOPT_ALREADY_CONNECTED)
! 1168: isconnected = TRUE;
! 1169: else if(error) {
! 1170: Curl_closesocket(conn, sockfd); /* close the socket and bail out */
! 1171: return CURLE_ABORTED_BY_CALLBACK;
! 1172: }
! 1173: }
! 1174:
! 1175: /* possibly bind the local end to an IP, interface or port */
! 1176: if(addr.family == AF_INET
! 1177: #ifdef ENABLE_IPV6
! 1178: || addr.family == AF_INET6
! 1179: #endif
! 1180: ) {
! 1181: result = bindlocal(conn, sockfd, addr.family,
! 1182: Curl_ipv6_scope((struct sockaddr*)&addr.sa_addr));
! 1183: if(result) {
! 1184: Curl_closesocket(conn, sockfd); /* close socket and bail out */
! 1185: if(result == CURLE_UNSUPPORTED_PROTOCOL) {
! 1186: /* The address family is not supported on this interface.
! 1187: We can continue trying addresses */
! 1188: return CURLE_COULDNT_CONNECT;
! 1189: }
! 1190: return result;
! 1191: }
! 1192: }
! 1193:
! 1194: /* set socket non-blocking */
! 1195: (void)curlx_nonblock(sockfd, TRUE);
! 1196:
! 1197: conn->connecttime = Curl_now();
! 1198: if(conn->num_addr > 1)
! 1199: Curl_expire(data, conn->timeoutms_per_addr, EXPIRE_DNS_PER_NAME);
! 1200:
! 1201: /* Connect TCP and QUIC sockets */
! 1202: if(!isconnected && (conn->transport != TRNSPRT_UDP)) {
! 1203: if(conn->bits.tcp_fastopen) {
! 1204: #if defined(CONNECT_DATA_IDEMPOTENT) /* Darwin */
! 1205: # if defined(HAVE_BUILTIN_AVAILABLE)
! 1206: /* while connectx function is available since macOS 10.11 / iOS 9,
! 1207: it did not have the interface declared correctly until
! 1208: Xcode 9 / macOS SDK 10.13 */
! 1209: if(__builtin_available(macOS 10.11, iOS 9.0, tvOS 9.0, watchOS 2.0, *)) {
! 1210: sa_endpoints_t endpoints;
! 1211: endpoints.sae_srcif = 0;
! 1212: endpoints.sae_srcaddr = NULL;
! 1213: endpoints.sae_srcaddrlen = 0;
! 1214: endpoints.sae_dstaddr = &addr.sa_addr;
! 1215: endpoints.sae_dstaddrlen = addr.addrlen;
! 1216:
! 1217: rc = connectx(sockfd, &endpoints, SAE_ASSOCID_ANY,
! 1218: CONNECT_RESUME_ON_READ_WRITE | CONNECT_DATA_IDEMPOTENT,
! 1219: NULL, 0, NULL, NULL);
! 1220: }
! 1221: else {
! 1222: rc = connect(sockfd, &addr.sa_addr, addr.addrlen);
! 1223: }
! 1224: # else
! 1225: rc = connect(sockfd, &addr.sa_addr, addr.addrlen);
! 1226: # endif /* HAVE_BUILTIN_AVAILABLE */
! 1227: #elif defined(TCP_FASTOPEN_CONNECT) /* Linux >= 4.11 */
! 1228: if(setsockopt(sockfd, IPPROTO_TCP, TCP_FASTOPEN_CONNECT,
! 1229: (void *)&optval, sizeof(optval)) < 0)
! 1230: infof(data, "Failed to enable TCP Fast Open on fd %d\n", sockfd);
! 1231:
! 1232: rc = connect(sockfd, &addr.sa_addr, addr.addrlen);
! 1233: #elif defined(MSG_FASTOPEN) /* old Linux */
! 1234: if(conn->given->flags & PROTOPT_SSL)
! 1235: rc = connect(sockfd, &addr.sa_addr, addr.addrlen);
! 1236: else
! 1237: rc = 0; /* Do nothing */
! 1238: #endif
! 1239: }
! 1240: else {
! 1241: rc = connect(sockfd, &addr.sa_addr, addr.addrlen);
! 1242: }
! 1243:
! 1244: if(-1 == rc)
! 1245: error = SOCKERRNO;
! 1246: #ifdef ENABLE_QUIC
! 1247: else if(conn->transport == TRNSPRT_QUIC) {
! 1248: /* pass in 'sockfd' separately since it hasn't been put into the
! 1249: tempsock array at this point */
! 1250: result = Curl_quic_connect(conn, sockfd, tempindex,
! 1251: &addr.sa_addr, addr.addrlen);
! 1252: if(result)
! 1253: error = SOCKERRNO;
! 1254: }
! 1255: #endif
! 1256: }
! 1257: else {
! 1258: *sockp = sockfd;
! 1259: return CURLE_OK;
! 1260: }
! 1261:
! 1262: if(-1 == rc) {
! 1263: switch(error) {
! 1264: case EINPROGRESS:
! 1265: case EWOULDBLOCK:
! 1266: #if defined(EAGAIN)
! 1267: #if (EAGAIN) != (EWOULDBLOCK)
! 1268: /* On some platforms EAGAIN and EWOULDBLOCK are the
! 1269: * same value, and on others they are different, hence
! 1270: * the odd #if
! 1271: */
! 1272: case EAGAIN:
! 1273: #endif
! 1274: #endif
! 1275: result = CURLE_OK;
! 1276: break;
! 1277:
! 1278: default:
! 1279: /* unknown error, fallthrough and try another address! */
! 1280: infof(data, "Immediate connect fail for %s: %s\n",
! 1281: ipaddress, Curl_strerror(error, buffer, sizeof(buffer)));
! 1282: data->state.os_errno = error;
! 1283:
! 1284: /* connect failed */
! 1285: Curl_closesocket(conn, sockfd);
! 1286: result = CURLE_COULDNT_CONNECT;
! 1287: }
! 1288: }
! 1289:
! 1290: if(!result)
! 1291: *sockp = sockfd;
! 1292:
! 1293: return result;
! 1294: }
! 1295:
! 1296: /*
! 1297: * TCP connect to the given host with timeout, proxy or remote doesn't matter.
! 1298: * There might be more than one IP address to try out. Fill in the passed
! 1299: * pointer with the connected socket.
! 1300: */
! 1301:
! 1302: CURLcode Curl_connecthost(struct connectdata *conn, /* context */
! 1303: const struct Curl_dns_entry *remotehost)
! 1304: {
! 1305: struct Curl_easy *data = conn->data;
! 1306: struct curltime before = Curl_now();
! 1307: CURLcode result = CURLE_COULDNT_CONNECT;
! 1308: int i;
! 1309: timediff_t timeout_ms = Curl_timeleft(data, &before, TRUE);
! 1310:
! 1311: if(timeout_ms < 0) {
! 1312: /* a precaution, no need to continue if time already is up */
! 1313: failf(data, "Connection time-out");
! 1314: return CURLE_OPERATION_TIMEDOUT;
! 1315: }
! 1316:
! 1317: conn->num_addr = Curl_num_addresses(remotehost->addr);
! 1318: conn->tempaddr[0] = conn->tempaddr[1] = remotehost->addr;
! 1319: conn->tempsock[0] = conn->tempsock[1] = CURL_SOCKET_BAD;
! 1320:
! 1321: /* Max time for the next connection attempt */
! 1322: conn->timeoutms_per_addr =
! 1323: conn->tempaddr[0]->ai_next == NULL ? timeout_ms : timeout_ms / 2;
! 1324:
! 1325: conn->tempfamily[0] = conn->tempaddr[0]?
! 1326: conn->tempaddr[0]->ai_family:0;
! 1327: conn->tempfamily[1] = conn->tempfamily[0] == AF_INET6 ?
! 1328: AF_INET : AF_INET6;
! 1329: ainext(conn, 1, FALSE); /* assigns conn->tempaddr[1] accordingly */
! 1330:
! 1331: DEBUGF(infof(data, "family0 == %s, family1 == %s\n",
! 1332: conn->tempfamily[0] == AF_INET ? "v4" : "v6",
! 1333: conn->tempfamily[1] == AF_INET ? "v4" : "v6"));
! 1334:
! 1335: /* get through the list in family order in case of quick failures */
! 1336: for(i = 0; (i < 2) && result; i++) {
! 1337: while(conn->tempaddr[i]) {
! 1338: result = singleipconnect(conn, conn->tempaddr[i], i);
! 1339: if(!result)
! 1340: break;
! 1341: ainext(conn, i, TRUE);
! 1342: }
! 1343: }
! 1344: if(result)
! 1345: return result;
! 1346:
! 1347: Curl_expire(conn->data, data->set.happy_eyeballs_timeout,
! 1348: EXPIRE_HAPPY_EYEBALLS);
! 1349:
! 1350: return CURLE_OK;
! 1351: }
! 1352:
! 1353: struct connfind {
! 1354: struct connectdata *tofind;
! 1355: bool found;
! 1356: };
! 1357:
! 1358: static int conn_is_conn(struct connectdata *conn, void *param)
! 1359: {
! 1360: struct connfind *f = (struct connfind *)param;
! 1361: if(conn == f->tofind) {
! 1362: f->found = TRUE;
! 1363: return 1;
! 1364: }
! 1365: return 0;
! 1366: }
! 1367:
! 1368: /*
! 1369: * Used to extract socket and connectdata struct for the most recent
! 1370: * transfer on the given Curl_easy.
! 1371: *
! 1372: * The returned socket will be CURL_SOCKET_BAD in case of failure!
! 1373: */
! 1374: curl_socket_t Curl_getconnectinfo(struct Curl_easy *data,
! 1375: struct connectdata **connp)
! 1376: {
! 1377: DEBUGASSERT(data);
! 1378:
! 1379: /* this works for an easy handle:
! 1380: * - that has been used for curl_easy_perform()
! 1381: * - that is associated with a multi handle, and whose connection
! 1382: * was detached with CURLOPT_CONNECT_ONLY
! 1383: */
! 1384: if(data->state.lastconnect && (data->multi_easy || data->multi)) {
! 1385: struct connectdata *c = data->state.lastconnect;
! 1386: struct connfind find;
! 1387: find.tofind = data->state.lastconnect;
! 1388: find.found = FALSE;
! 1389:
! 1390: Curl_conncache_foreach(data, data->multi_easy?
! 1391: &data->multi_easy->conn_cache:
! 1392: &data->multi->conn_cache, &find, conn_is_conn);
! 1393:
! 1394: if(!find.found) {
! 1395: data->state.lastconnect = NULL;
! 1396: return CURL_SOCKET_BAD;
! 1397: }
! 1398:
! 1399: if(connp) {
! 1400: /* only store this if the caller cares for it */
! 1401: *connp = c;
! 1402: c->data = data;
! 1403: }
! 1404: return c->sock[FIRSTSOCKET];
! 1405: }
! 1406: else
! 1407: return CURL_SOCKET_BAD;
! 1408: }
! 1409:
! 1410: /*
! 1411: * Check if a connection seems to be alive.
! 1412: */
! 1413: bool Curl_connalive(struct connectdata *conn)
! 1414: {
! 1415: /* First determine if ssl */
! 1416: if(conn->ssl[FIRSTSOCKET].use) {
! 1417: /* use the SSL context */
! 1418: if(!Curl_ssl_check_cxn(conn))
! 1419: return false; /* FIN received */
! 1420: }
! 1421: /* Minix 3.1 doesn't support any flags on recv; just assume socket is OK */
! 1422: #ifdef MSG_PEEK
! 1423: else if(conn->sock[FIRSTSOCKET] == CURL_SOCKET_BAD)
! 1424: return false;
! 1425: else {
! 1426: /* use the socket */
! 1427: char buf;
! 1428: if(recv((RECV_TYPE_ARG1)conn->sock[FIRSTSOCKET], (RECV_TYPE_ARG2)&buf,
! 1429: (RECV_TYPE_ARG3)1, (RECV_TYPE_ARG4)MSG_PEEK) == 0) {
! 1430: return false; /* FIN received */
! 1431: }
! 1432: }
! 1433: #endif
! 1434: return true;
! 1435: }
! 1436:
! 1437: /*
! 1438: * Close a socket.
! 1439: *
! 1440: * 'conn' can be NULL, beware!
! 1441: */
! 1442: int Curl_closesocket(struct connectdata *conn,
! 1443: curl_socket_t sock)
! 1444: {
! 1445: if(conn && conn->fclosesocket) {
! 1446: if((sock == conn->sock[SECONDARYSOCKET]) && conn->sock_accepted)
! 1447: /* if this socket matches the second socket, and that was created with
! 1448: accept, then we MUST NOT call the callback but clear the accepted
! 1449: status */
! 1450: conn->sock_accepted = FALSE;
! 1451: else {
! 1452: int rc;
! 1453: Curl_multi_closed(conn->data, sock);
! 1454: Curl_set_in_callback(conn->data, true);
! 1455: rc = conn->fclosesocket(conn->closesocket_client, sock);
! 1456: Curl_set_in_callback(conn->data, false);
! 1457: return rc;
! 1458: }
! 1459: }
! 1460:
! 1461: if(conn)
! 1462: /* tell the multi-socket code about this */
! 1463: Curl_multi_closed(conn->data, sock);
! 1464:
! 1465: sclose(sock);
! 1466:
! 1467: return 0;
! 1468: }
! 1469:
! 1470: /*
! 1471: * Create a socket based on info from 'conn' and 'ai'.
! 1472: *
! 1473: * 'addr' should be a pointer to the correct struct to get data back, or NULL.
! 1474: * 'sockfd' must be a pointer to a socket descriptor.
! 1475: *
! 1476: * If the open socket callback is set, used that!
! 1477: *
! 1478: */
! 1479: CURLcode Curl_socket(struct connectdata *conn,
! 1480: const Curl_addrinfo *ai,
! 1481: struct Curl_sockaddr_ex *addr,
! 1482: curl_socket_t *sockfd)
! 1483: {
! 1484: struct Curl_easy *data = conn->data;
! 1485: struct Curl_sockaddr_ex dummy;
! 1486:
! 1487: if(!addr)
! 1488: /* if the caller doesn't want info back, use a local temp copy */
! 1489: addr = &dummy;
! 1490:
! 1491: /*
! 1492: * The Curl_sockaddr_ex structure is basically libcurl's external API
! 1493: * curl_sockaddr structure with enough space available to directly hold
! 1494: * any protocol-specific address structures. The variable declared here
! 1495: * will be used to pass / receive data to/from the fopensocket callback
! 1496: * if this has been set, before that, it is initialized from parameters.
! 1497: */
! 1498:
! 1499: addr->family = ai->ai_family;
! 1500: addr->socktype = (conn->transport == TRNSPRT_TCP) ? SOCK_STREAM : SOCK_DGRAM;
! 1501: addr->protocol = conn->transport != TRNSPRT_TCP ? IPPROTO_UDP :
! 1502: ai->ai_protocol;
! 1503: addr->addrlen = ai->ai_addrlen;
! 1504:
! 1505: if(addr->addrlen > sizeof(struct Curl_sockaddr_storage))
! 1506: addr->addrlen = sizeof(struct Curl_sockaddr_storage);
! 1507: memcpy(&addr->sa_addr, ai->ai_addr, addr->addrlen);
! 1508:
! 1509: if(data->set.fopensocket) {
! 1510: /*
! 1511: * If the opensocket callback is set, all the destination address
! 1512: * information is passed to the callback. Depending on this information the
! 1513: * callback may opt to abort the connection, this is indicated returning
! 1514: * CURL_SOCKET_BAD; otherwise it will return a not-connected socket. When
! 1515: * the callback returns a valid socket the destination address information
! 1516: * might have been changed and this 'new' address will actually be used
! 1517: * here to connect.
! 1518: */
! 1519: Curl_set_in_callback(data, true);
! 1520: *sockfd = data->set.fopensocket(data->set.opensocket_client,
! 1521: CURLSOCKTYPE_IPCXN,
! 1522: (struct curl_sockaddr *)addr);
! 1523: Curl_set_in_callback(data, false);
! 1524: }
! 1525: else
! 1526: /* opensocket callback not set, so simply create the socket now */
! 1527: *sockfd = socket(addr->family, addr->socktype, addr->protocol);
! 1528:
! 1529: if(*sockfd == CURL_SOCKET_BAD)
! 1530: /* no socket, no connection */
! 1531: return CURLE_COULDNT_CONNECT;
! 1532:
! 1533: if(conn->transport == TRNSPRT_QUIC) {
! 1534: /* QUIC sockets need to be nonblocking */
! 1535: (void)curlx_nonblock(*sockfd, TRUE);
! 1536: }
! 1537:
! 1538: #if defined(ENABLE_IPV6) && defined(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID)
! 1539: if(conn->scope_id && (addr->family == AF_INET6)) {
! 1540: struct sockaddr_in6 * const sa6 = (void *)&addr->sa_addr;
! 1541: sa6->sin6_scope_id = conn->scope_id;
! 1542: }
! 1543: #endif
! 1544:
! 1545: return CURLE_OK;
! 1546:
! 1547: }
! 1548:
! 1549: /*
! 1550: * Curl_conncontrol() marks streams or connection for closure.
! 1551: */
! 1552: void Curl_conncontrol(struct connectdata *conn,
! 1553: int ctrl /* see defines in header */
! 1554: #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
! 1555: , const char *reason
! 1556: #endif
! 1557: )
! 1558: {
! 1559: /* close if a connection, or a stream that isn't multiplexed */
! 1560: bool closeit = (ctrl == CONNCTRL_CONNECTION) ||
! 1561: ((ctrl == CONNCTRL_STREAM) && !(conn->handler->flags & PROTOPT_STREAM));
! 1562: if((ctrl == CONNCTRL_STREAM) &&
! 1563: (conn->handler->flags & PROTOPT_STREAM))
! 1564: DEBUGF(infof(conn->data, "Kill stream: %s\n", reason));
! 1565: else if((bit)closeit != conn->bits.close) {
! 1566: DEBUGF(infof(conn->data, "Marked for [%s]: %s\n",
! 1567: closeit?"closure":"keep alive", reason));
! 1568: conn->bits.close = closeit; /* the only place in the source code that
! 1569: should assign this bit */
! 1570: }
! 1571: }
! 1572:
! 1573: /* Data received can be cached at various levels, so check them all here. */
! 1574: bool Curl_conn_data_pending(struct connectdata *conn, int sockindex)
! 1575: {
! 1576: int readable;
! 1577:
! 1578: if(Curl_ssl_data_pending(conn, sockindex) ||
! 1579: Curl_recv_has_postponed_data(conn, sockindex))
! 1580: return true;
! 1581:
! 1582: readable = SOCKET_READABLE(conn->sock[sockindex], 0);
! 1583: return (readable > 0 && (readable & CURL_CSELECT_IN));
! 1584: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>