1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
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.
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.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
21 ***************************************************************************/
23 #include "curl_setup.h"
25 #ifdef HAVE_NETINET_IN_H
26 #include <netinet/in.h> /* <netinet/tcp.h> may need it */
29 #include <sys/un.h> /* for sockaddr_un */
31 #ifdef HAVE_LINUX_TCP_H
32 #include <linux/tcp.h>
33 #elif defined(HAVE_NETINET_TCP_H)
34 #include <netinet/tcp.h>
36 #ifdef HAVE_SYS_IOCTL_H
37 #include <sys/ioctl.h>
45 #ifdef HAVE_ARPA_INET_H
46 #include <arpa/inet.h>
49 #if (defined(HAVE_IOCTL_FIONBIO) && defined(NETWARE))
50 #include <sys/filio.h>
54 #define in_addr_t unsigned long
67 #include "url.h" /* for Curl_safefree() */
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() */
75 #include "conncache.h"
76 #include "multihandle.h"
78 #include "version_win32.h"
82 /* The last 3 #include files should be in this order */
83 #include "curl_printf.h"
84 #include "curl_memory.h"
87 static bool verifyconnect(curl_socket_t sockfd, int *error);
89 #if defined(__DragonFly__) || defined(HAVE_WINSOCK2_H)
90 /* DragonFlyBSD and Windows use millisecond units */
91 #define KEEPALIVE_FACTOR(x) (x *= 1000)
93 #define KEEPALIVE_FACTOR(x)
96 #if defined(HAVE_WINSOCK2_H) && !defined(SIO_KEEPALIVE_VALS)
97 #define SIO_KEEPALIVE_VALS _WSAIOW(IOC_VENDOR,4)
99 struct tcp_keepalive {
101 u_long keepalivetime;
102 u_long keepaliveinterval;
107 tcpkeepalive(struct Curl_easy *data,
108 curl_socket_t sockfd)
110 int optval = data->set.tcp_keepalive?1:0;
112 /* only set IDLE and INTVL if setting KEEPALIVE is successful */
113 if(setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE,
114 (void *)&optval, sizeof(optval)) < 0) {
115 infof(data, "Failed to set SO_KEEPALIVE on fd %d", sockfd);
118 #if defined(SIO_KEEPALIVE_VALS)
119 struct tcp_keepalive vals;
122 optval = curlx_sltosi(data->set.tcp_keepidle);
123 KEEPALIVE_FACTOR(optval);
124 vals.keepalivetime = optval;
125 optval = curlx_sltosi(data->set.tcp_keepintvl);
126 KEEPALIVE_FACTOR(optval);
127 vals.keepaliveinterval = optval;
128 if(WSAIoctl(sockfd, SIO_KEEPALIVE_VALS, (LPVOID) &vals, sizeof(vals),
129 NULL, 0, &dummy, NULL, NULL) != 0) {
130 infof(data, "Failed to set SIO_KEEPALIVE_VALS on fd %d: %d",
131 (int)sockfd, WSAGetLastError());
135 optval = curlx_sltosi(data->set.tcp_keepidle);
136 KEEPALIVE_FACTOR(optval);
137 if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPIDLE,
138 (void *)&optval, sizeof(optval)) < 0) {
139 infof(data, "Failed to set TCP_KEEPIDLE on fd %d", sockfd);
141 #elif defined(TCP_KEEPALIVE)
143 optval = curlx_sltosi(data->set.tcp_keepidle);
144 KEEPALIVE_FACTOR(optval);
145 if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPALIVE,
146 (void *)&optval, sizeof(optval)) < 0) {
147 infof(data, "Failed to set TCP_KEEPALIVE on fd %d", sockfd);
151 optval = curlx_sltosi(data->set.tcp_keepintvl);
152 KEEPALIVE_FACTOR(optval);
153 if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPINTVL,
154 (void *)&optval, sizeof(optval)) < 0) {
155 infof(data, "Failed to set TCP_KEEPINTVL on fd %d", sockfd);
163 singleipconnect(struct Curl_easy *data,
164 struct connectdata *conn,
165 const struct Curl_addrinfo *ai, /* start connecting to this */
166 int tempindex); /* 0 or 1 among the temp ones */
169 * Curl_timeleft() returns the amount of milliseconds left allowed for the
170 * transfer/connection. If the value is 0, there's no timeout (ie there's
171 * infinite time left). If the value is negative, the timeout time has already
174 * If 'nowp' is non-NULL, it points to the current time.
175 * 'duringconnect' is FALSE if not during a connect, as then of course the
176 * connect timeout is not taken into account!
181 #define TIMEOUT_CONNECT 1
182 #define TIMEOUT_MAXTIME 2
184 timediff_t Curl_timeleft(struct Curl_easy *data,
185 struct curltime *nowp,
188 unsigned int timeout_set = 0;
189 timediff_t connect_timeout_ms = 0;
190 timediff_t maxtime_timeout_ms = 0;
191 timediff_t timeout_ms = 0;
194 /* The duration of a connect and the total transfer are calculated from two
195 different time-stamps. It can end up with the total timeout being reached
196 before the connect timeout expires and we must acknowledge whichever
197 timeout that is reached first. The total timeout is set per entire
198 operation, while the connect timeout is set per connect. */
200 if(data->set.timeout > 0) {
201 timeout_set = TIMEOUT_MAXTIME;
202 maxtime_timeout_ms = data->set.timeout;
205 timeout_set |= TIMEOUT_CONNECT;
206 connect_timeout_ms = (data->set.connecttimeout > 0) ?
207 data->set.connecttimeout : DEFAULT_CONNECT_TIMEOUT;
218 if(timeout_set & TIMEOUT_MAXTIME) {
219 maxtime_timeout_ms -= Curl_timediff(*nowp, data->progress.t_startop);
220 timeout_ms = maxtime_timeout_ms;
223 if(timeout_set & TIMEOUT_CONNECT) {
224 connect_timeout_ms -= Curl_timediff(*nowp, data->progress.t_startsingle);
226 if(!(timeout_set & TIMEOUT_MAXTIME) ||
227 (connect_timeout_ms < maxtime_timeout_ms))
228 timeout_ms = connect_timeout_ms;
232 /* avoid returning 0 as that means no timeout! */
238 static CURLcode bindlocal(struct Curl_easy *data,
239 curl_socket_t sockfd, int af, unsigned int scope)
241 struct connectdata *conn = data->conn;
242 struct Curl_sockaddr_storage sa;
243 struct sockaddr *sock = (struct sockaddr *)&sa; /* bind to this address */
244 curl_socklen_t sizeof_sa = 0; /* size of the data sock points to */
245 struct sockaddr_in *si4 = (struct sockaddr_in *)&sa;
247 struct sockaddr_in6 *si6 = (struct sockaddr_in6 *)&sa;
250 struct Curl_dns_entry *h = NULL;
251 unsigned short port = data->set.localport; /* use this port number, 0 for
253 /* how many port numbers to try to bind to, increasing one at a time */
254 int portnum = data->set.localportrange;
255 const char *dev = data->set.str[STRING_DEVICE];
257 #ifdef IP_BIND_ADDRESS_NO_PORT
264 /*************************************************************
265 * Select device to bind socket to
266 *************************************************************/
268 /* no local kind of binding was requested */
271 memset(&sa, 0, sizeof(struct Curl_sockaddr_storage));
273 if(dev && (strlen(dev)<255) ) {
274 char myhost[256] = "";
275 int done = 0; /* -1 for error, 1 for address found */
276 bool is_interface = FALSE;
277 bool is_host = FALSE;
278 static const char *if_prefix = "if!";
279 static const char *host_prefix = "host!";
281 if(strncmp(if_prefix, dev, strlen(if_prefix)) == 0) {
282 dev += strlen(if_prefix);
285 else if(strncmp(host_prefix, dev, strlen(host_prefix)) == 0) {
286 dev += strlen(host_prefix);
292 #ifdef SO_BINDTODEVICE
293 /* I am not sure any other OSs than Linux that provide this feature,
294 * and at the least I cannot test. --Ben
296 * This feature allows one to tightly bind the local socket to a
297 * particular interface. This will force even requests to other
298 * local interfaces to go out the external interface.
301 * Only bind to the interface when specified as interface, not just
302 * as a hostname or ip address.
304 * interface might be a VRF, eg: vrf-blue, which means it cannot be
305 * converted to an IP address and would fail Curl_if2ip. Simply try
306 * to use it straight away.
308 if(setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE,
309 dev, (curl_socklen_t)strlen(dev) + 1) == 0) {
310 /* This is typically "errno 1, error: Operation not permitted" if
311 * you're not running as root or another suitable privileged
313 * If it succeeds it means the parameter was a valid interface and
314 * not an IP address. Return immediately.
320 switch(Curl_if2ip(af,
322 scope, conn->scope_id,
324 dev, myhost, sizeof(myhost))) {
325 case IF2IP_NOT_FOUND:
327 /* Do not fall back to treating it as a host name */
328 failf(data, "Couldn't bind to interface '%s'", dev);
329 return CURLE_INTERFACE_FAILED;
332 case IF2IP_AF_NOT_SUPPORTED:
333 /* Signal the caller to try another address family if available */
334 return CURLE_UNSUPPORTED_PROTOCOL;
338 * We now have the numerical IP address in the 'myhost' buffer
340 infof(data, "Local Interface %s is ip %s using address family %i",
348 * This was not an interface, resolve the name as a host name
351 * Temporarily force name resolution to use only the address type
352 * of the connection. The resolve functions should really be changed
353 * to take a type parameter instead.
355 unsigned char ipver = conn->ip_version;
359 conn->ip_version = CURL_IPRESOLVE_V4;
361 else if(af == AF_INET6)
362 conn->ip_version = CURL_IPRESOLVE_V6;
365 rc = Curl_resolv(data, dev, 0, FALSE, &h);
366 if(rc == CURLRESOLV_PENDING)
367 (void)Curl_resolver_wait_resolv(data, &h);
368 conn->ip_version = ipver;
371 /* convert the resolved address, sizeof myhost >= INET_ADDRSTRLEN */
372 Curl_printable_address(h->addr, myhost, sizeof(myhost));
373 infof(data, "Name '%s' family %i resolved to '%s' family %i",
374 dev, af, myhost, h->addr->ai_family);
375 Curl_resolv_unlock(data, h);
376 if(af != h->addr->ai_family) {
377 /* bad IP version combo, signal the caller to try another address
378 family if available */
379 return CURLE_UNSUPPORTED_PROTOCOL;
385 * provided dev was no interface (or interfaces are not supported
386 * e.g. solaris) no ip address and no domain we fail here
396 #ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
397 char *scope_ptr = strchr(myhost, '%');
401 if(Curl_inet_pton(AF_INET6, myhost, &si6->sin6_addr) > 0) {
402 si6->sin6_family = AF_INET6;
403 si6->sin6_port = htons(port);
404 #ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
406 /* The "myhost" string either comes from Curl_if2ip or from
407 Curl_printable_address. The latter returns only numeric scope
408 IDs and the former returns none at all. So the scope ID, if
409 present, is known to be numeric */
410 si6->sin6_scope_id = atoi(scope_ptr);
413 sizeof_sa = sizeof(struct sockaddr_in6);
418 if((af == AF_INET) &&
419 (Curl_inet_pton(AF_INET, myhost, &si4->sin_addr) > 0)) {
420 si4->sin_family = AF_INET;
421 si4->sin_port = htons(port);
422 sizeof_sa = sizeof(struct sockaddr_in);
427 /* errorbuf is set false so failf will overwrite any message already in
428 the error buffer, so the user receives this error message instead of a
429 generic resolve error. */
430 data->state.errorbuf = FALSE;
431 failf(data, "Couldn't bind to '%s'", dev);
432 return CURLE_INTERFACE_FAILED;
436 /* no device was given, prepare sa to match af's needs */
439 si6->sin6_family = AF_INET6;
440 si6->sin6_port = htons(port);
441 sizeof_sa = sizeof(struct sockaddr_in6);
446 si4->sin_family = AF_INET;
447 si4->sin_port = htons(port);
448 sizeof_sa = sizeof(struct sockaddr_in);
451 #ifdef IP_BIND_ADDRESS_NO_PORT
452 (void)setsockopt(sockfd, SOL_IP, IP_BIND_ADDRESS_NO_PORT, &on, sizeof(on));
455 if(bind(sockfd, sock, sizeof_sa) >= 0) {
456 /* we succeeded to bind */
457 struct Curl_sockaddr_storage add;
458 curl_socklen_t size = sizeof(add);
459 memset(&add, 0, sizeof(struct Curl_sockaddr_storage));
460 if(getsockname(sockfd, (struct sockaddr *) &add, &size) < 0) {
461 char buffer[STRERROR_LEN];
462 data->state.os_errno = error = SOCKERRNO;
463 failf(data, "getsockname() failed with errno %d: %s",
464 error, Curl_strerror(error, buffer, sizeof(buffer)));
465 return CURLE_INTERFACE_FAILED;
467 infof(data, "Local port: %hu", port);
468 conn->bits.bound = TRUE;
473 infof(data, "Bind to local port %hu failed, trying next", port);
474 port++; /* try next port */
475 /* We re-use/clobber the port variable here below */
476 if(sock->sa_family == AF_INET)
477 si4->sin_port = ntohs(port);
480 si6->sin6_port = ntohs(port);
487 char buffer[STRERROR_LEN];
488 data->state.os_errno = error = SOCKERRNO;
489 failf(data, "bind failed with errno %d: %s",
490 error, Curl_strerror(error, buffer, sizeof(buffer)));
493 return CURLE_INTERFACE_FAILED;
497 * verifyconnect() returns TRUE if the connect really has happened.
499 static bool verifyconnect(curl_socket_t sockfd, int *error)
504 curl_socklen_t errSize = sizeof(err);
508 * In October 2003 we effectively nullified this function on Windows due to
509 * problems with it using all CPU in multi-threaded cases.
511 * In May 2004, we bring it back to offer more info back on connect failures.
512 * Gisle Vanem could reproduce the former problems with this function, but
513 * could avoid them by adding this SleepEx() call below:
515 * "I don't have Rational Quantify, but the hint from his post was
516 * ntdll::NtRemoveIoCompletion(). So I'd assume the SleepEx (or maybe
517 * just Sleep(0) would be enough?) would release whatever
518 * mutex/critical-section the ntdll call is waiting on.
520 * Someone got to verify this on Win-NT 4.0, 2000."
531 if(0 != getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (void *)&err, &errSize))
534 /* Old WinCE versions don't support SO_ERROR */
535 if(WSAENOPROTOOPT == err) {
540 #if defined(EBADIOCTL) && defined(__minix)
541 /* Minix 3.1.x doesn't support getsockopt on UDP sockets */
542 if(EBADIOCTL == err) {
547 if((0 == err) || (EISCONN == err))
548 /* we are connected, awesome! */
551 /* This wasn't a successful connect */
563 /* update tempaddr[tempindex] (to the next entry), makes sure to stick
564 to the correct family */
565 static struct Curl_addrinfo *ainext(struct connectdata *conn,
567 bool next) /* use next entry? */
569 struct Curl_addrinfo *ai = conn->tempaddr[tempindex];
572 while(ai && (ai->ai_family != conn->tempfamily[tempindex]))
574 conn->tempaddr[tempindex] = ai;
578 /* Used within the multi interface. Try next IP address, returns error if no
579 more address exists or error */
580 static CURLcode trynextip(struct Curl_easy *data,
581 struct connectdata *conn,
585 CURLcode result = CURLE_COULDNT_CONNECT;
587 /* First clean up after the failed socket.
588 Don't close it yet to ensure that the next IP's socket gets a different
589 file descriptor, which can prevent bugs when the curl_multi_socket_action
590 interface is used with certain select() replacements such as kqueue. */
591 curl_socket_t fd_to_close = conn->tempsock[tempindex];
592 conn->tempsock[tempindex] = CURL_SOCKET_BAD;
594 if(sockindex == FIRSTSOCKET) {
595 struct Curl_addrinfo *ai = conn->tempaddr[tempindex];
598 result = singleipconnect(data, conn, ai, tempindex);
599 if(result == CURLE_COULDNT_CONNECT) {
600 ai = ainext(conn, tempindex, TRUE);
607 if(fd_to_close != CURL_SOCKET_BAD)
608 Curl_closesocket(data, conn, fd_to_close);
613 /* Copies connection info into the transfer handle to make it available when
614 the transfer handle is no longer associated with the connection. */
615 void Curl_persistconninfo(struct Curl_easy *data, struct connectdata *conn,
616 char *local_ip, int local_port)
618 memcpy(data->info.conn_primary_ip, conn->primary_ip, MAX_IPADR_LEN);
619 if(local_ip && local_ip[0])
620 memcpy(data->info.conn_local_ip, local_ip, MAX_IPADR_LEN);
622 data->info.conn_local_ip[0] = 0;
623 data->info.conn_scheme = conn->handler->scheme;
624 data->info.conn_protocol = conn->handler->protocol;
625 data->info.conn_primary_port = conn->port;
626 data->info.conn_remote_port = conn->remote_port;
627 data->info.conn_local_port = local_port;
630 /* retrieves ip address and port from a sockaddr structure.
631 note it calls Curl_inet_ntop which sets errno on fail, not SOCKERRNO. */
632 bool Curl_addr2string(struct sockaddr *sa, curl_socklen_t salen,
633 char *addr, int *port)
635 struct sockaddr_in *si = NULL;
637 struct sockaddr_in6 *si6 = NULL;
639 #if (defined(HAVE_SYS_UN_H) || defined(WIN32_SOCKADDR_UN)) && defined(AF_UNIX)
640 struct sockaddr_un *su = NULL;
645 switch(sa->sa_family) {
647 si = (struct sockaddr_in *)(void *) sa;
648 if(Curl_inet_ntop(sa->sa_family, &si->sin_addr,
649 addr, MAX_IPADR_LEN)) {
650 unsigned short us_port = ntohs(si->sin_port);
657 si6 = (struct sockaddr_in6 *)(void *) sa;
658 if(Curl_inet_ntop(sa->sa_family, &si6->sin6_addr,
659 addr, MAX_IPADR_LEN)) {
660 unsigned short us_port = ntohs(si6->sin6_port);
666 #if (defined(HAVE_SYS_UN_H) || defined(WIN32_SOCKADDR_UN)) && defined(AF_UNIX)
668 if(salen > (curl_socklen_t)sizeof(CURL_SA_FAMILY_T)) {
669 su = (struct sockaddr_un*)sa;
670 msnprintf(addr, MAX_IPADR_LEN, "%s", su->sun_path);
673 addr[0] = 0; /* socket with no name */
683 errno = EAFNOSUPPORT;
687 /* retrieves the start/end point information of a socket of an established
689 void Curl_conninfo_remote(struct Curl_easy *data,
690 struct connectdata *conn, curl_socket_t sockfd)
692 #ifdef HAVE_GETPEERNAME
693 char buffer[STRERROR_LEN];
694 struct Curl_sockaddr_storage ssrem;
697 plen = sizeof(struct Curl_sockaddr_storage);
698 memset(&ssrem, 0, sizeof(ssrem));
699 if(getpeername(sockfd, (struct sockaddr*) &ssrem, &plen)) {
700 int error = SOCKERRNO;
701 failf(data, "getpeername() failed with errno %d: %s",
702 error, Curl_strerror(error, buffer, sizeof(buffer)));
705 if(!Curl_addr2string((struct sockaddr*)&ssrem, plen,
706 conn->primary_ip, &port)) {
707 failf(data, "ssrem inet_ntop() failed with errno %d: %s",
708 errno, Curl_strerror(errno, buffer, sizeof(buffer)));
718 /* retrieves the start/end point information of a socket of an established
720 void Curl_conninfo_local(struct Curl_easy *data, curl_socket_t sockfd,
721 char *local_ip, int *local_port)
723 #ifdef HAVE_GETSOCKNAME
724 char buffer[STRERROR_LEN];
725 struct Curl_sockaddr_storage ssloc;
727 slen = sizeof(struct Curl_sockaddr_storage);
728 memset(&ssloc, 0, sizeof(ssloc));
729 if(getsockname(sockfd, (struct sockaddr*) &ssloc, &slen)) {
730 int error = SOCKERRNO;
731 failf(data, "getsockname() failed with errno %d: %s",
732 error, Curl_strerror(error, buffer, sizeof(buffer)));
735 if(!Curl_addr2string((struct sockaddr*)&ssloc, slen,
736 local_ip, local_port)) {
737 failf(data, "ssloc inet_ntop() failed with errno %d: %s",
738 errno, Curl_strerror(errno, buffer, sizeof(buffer)));
749 /* retrieves the start/end point information of a socket of an established
751 void Curl_updateconninfo(struct Curl_easy *data, struct connectdata *conn,
752 curl_socket_t sockfd)
754 /* 'local_ip' and 'local_port' get filled with local's numerical
755 ip address and port number whenever an outgoing connection is
756 **established** from the primary socket to a remote address. */
757 char local_ip[MAX_IPADR_LEN] = "";
760 if(conn->transport == TRNSPRT_TCP) {
761 if(!conn->bits.reuse && !conn->bits.tcp_fastopen)
762 Curl_conninfo_remote(data, conn, sockfd);
763 Curl_conninfo_local(data, sockfd, local_ip, &local_port);
764 } /* end of TCP-only section */
766 /* persist connection info in session handle */
767 Curl_persistconninfo(data, conn, local_ip, local_port);
770 /* After a TCP connection to the proxy has been verified, this function does
771 the next magic steps. If 'done' isn't set TRUE, it is not done yet and
772 must be called again.
774 Note: this function's sub-functions call failf()
777 static CURLcode connect_SOCKS(struct Curl_easy *data, int sockindex,
780 CURLcode result = CURLE_OK;
781 #ifndef CURL_DISABLE_PROXY
782 CURLproxycode pxresult = CURLPX_OK;
783 struct connectdata *conn = data->conn;
784 if(conn->bits.socksproxy) {
785 /* for the secondary socket (FTP), use the "connect to host"
786 * but ignore the "connect to port" (use the secondary port)
788 const char * const host =
789 conn->bits.httpproxy ?
790 conn->http_proxy.host.name :
791 conn->bits.conn_to_host ?
792 conn->conn_to_host.name :
793 sockindex == SECONDARYSOCKET ?
794 conn->secondaryhostname : conn->host.name;
796 conn->bits.httpproxy ? (int)conn->http_proxy.port :
797 sockindex == SECONDARYSOCKET ? conn->secondary_port :
798 conn->bits.conn_to_port ? conn->conn_to_port :
800 switch(conn->socks_proxy.proxytype) {
801 case CURLPROXY_SOCKS5:
802 case CURLPROXY_SOCKS5_HOSTNAME:
803 pxresult = Curl_SOCKS5(conn->socks_proxy.user, conn->socks_proxy.passwd,
804 host, port, sockindex, data, done);
807 case CURLPROXY_SOCKS4:
808 case CURLPROXY_SOCKS4A:
809 pxresult = Curl_SOCKS4(conn->socks_proxy.user, host, port, sockindex,
814 failf(data, "unknown proxytype option given");
815 result = CURLE_COULDNT_CONNECT;
816 } /* switch proxytype */
818 result = CURLE_PROXY;
819 data->info.pxcode = pxresult;
826 #endif /* CURL_DISABLE_PROXY */
827 *done = TRUE; /* no SOCKS proxy, so consider us connected */
833 * post_SOCKS() is called after a successful connect to the peer, which
834 * *could* be a SOCKS proxy
836 static void post_SOCKS(struct Curl_easy *data,
837 struct connectdata *conn,
841 conn->bits.tcpconnect[sockindex] = TRUE;
844 if(sockindex == FIRSTSOCKET)
845 Curl_pgrsTime(data, TIMER_CONNECT); /* connect done */
846 Curl_updateconninfo(data, conn, conn->sock[sockindex]);
847 Curl_verboseconnect(data, conn);
848 data->info.numconnects++; /* to track the number of connections made */
852 * Curl_is_connected() checks if the socket has connected.
855 CURLcode Curl_is_connected(struct Curl_easy *data,
856 struct connectdata *conn,
860 CURLcode result = CURLE_OK;
867 DEBUGASSERT(sockindex >= FIRSTSOCKET && sockindex <= SECONDARYSOCKET);
869 *connected = FALSE; /* a very negative world view is best */
871 if(conn->bits.tcpconnect[sockindex]) {
872 /* we are connected already! */
879 if(SOCKS_STATE(conn->cnnct.state)) {
880 /* still doing SOCKS */
881 result = connect_SOCKS(data, sockindex, connected);
882 if(!result && *connected)
883 post_SOCKS(data, conn, sockindex, connected);
887 for(i = 0; i<2; i++) {
888 const int other = i ^ 1;
889 if(conn->tempsock[i] == CURL_SOCKET_BAD)
893 if(conn->transport == TRNSPRT_QUIC) {
894 result = Curl_quic_is_connected(data, conn, i, connected);
895 if(!result && *connected) {
896 /* use this socket from now on */
897 conn->sock[sockindex] = conn->tempsock[i];
898 conn->ip_addr = conn->tempaddr[i];
899 conn->tempsock[i] = CURL_SOCKET_BAD;
900 post_SOCKS(data, conn, sockindex, connected);
901 connkeep(conn, "HTTP/3 default");
904 /* When a QUIC connect attempt fails, the better error explanation is in
905 'result' and not in errno */
907 conn->tempsock[i] = CURL_SOCKET_BAD;
915 /* Call this function once now, and ignore the results. We do this to
916 "clear" the error state on the socket so that we can later read it
917 reliably. This is reported necessary on the MPE/iX operating
919 (void)verifyconnect(conn->tempsock[i], NULL);
922 /* check socket for connect */
923 rc = SOCKET_WRITABLE(conn->tempsock[i], 0);
926 if(rc == 0) { /* no connection yet */
927 if(Curl_timediff(now, conn->connecttime) >=
928 conn->timeoutms_per_addr[i]) {
929 infof(data, "After %" CURL_FORMAT_TIMEDIFF_T
930 "ms connect time, move on!", conn->timeoutms_per_addr[i]);
934 /* should we try another protocol family? */
935 if(i == 0 && !conn->bits.parallel_connect &&
936 (Curl_timediff(now, conn->connecttime) >=
937 data->set.happy_eyeballs_timeout)) {
938 conn->bits.parallel_connect = TRUE; /* starting now */
939 trynextip(data, conn, sockindex, 1);
942 else if(rc == CURL_CSELECT_OUT || conn->bits.tcp_fastopen) {
943 if(verifyconnect(conn->tempsock[i], &error)) {
944 /* we are connected with TCP, awesome! */
946 /* use this socket from now on */
947 conn->sock[sockindex] = conn->tempsock[i];
948 conn->ip_addr = conn->tempaddr[i];
949 conn->tempsock[i] = CURL_SOCKET_BAD;
951 conn->bits.ipv6 = (conn->ip_addr->ai_family == AF_INET6)?TRUE:FALSE;
954 /* close the other socket, if open */
955 if(conn->tempsock[other] != CURL_SOCKET_BAD) {
956 Curl_closesocket(data, conn, conn->tempsock[other]);
957 conn->tempsock[other] = CURL_SOCKET_BAD;
960 /* see if we need to kick off any SOCKS proxy magic once we
962 result = connect_SOCKS(data, sockindex, connected);
963 if(result || !*connected)
966 post_SOCKS(data, conn, sockindex, connected);
971 else if(rc & CURL_CSELECT_ERR) {
972 (void)verifyconnect(conn->tempsock[i], &error);
976 * The connection failed here, we should attempt to connect to the "next
977 * address" for the given host. But first remember the latest error.
980 data->state.os_errno = error;
981 SET_SOCKERRNO(error);
982 if(conn->tempaddr[i]) {
984 #ifndef CURL_DISABLE_VERBOSE_STRINGS
985 char ipaddress[MAX_IPADR_LEN];
986 char buffer[STRERROR_LEN];
987 Curl_printable_address(conn->tempaddr[i], ipaddress,
990 if(conn->transport == TRNSPRT_QUIC) {
991 infof(data, "connect to %s port %u failed: %s",
992 ipaddress, conn->port, curl_easy_strerror(result));
996 infof(data, "connect to %s port %u failed: %s",
997 ipaddress, conn->port,
998 Curl_strerror(error, buffer, sizeof(buffer)));
1001 allow = Curl_timeleft(data, &now, TRUE);
1002 conn->timeoutms_per_addr[i] = conn->tempaddr[i]->ai_next == NULL ?
1004 ainext(conn, i, TRUE);
1005 status = trynextip(data, conn, sockindex, i);
1006 if((status != CURLE_COULDNT_CONNECT) ||
1007 conn->tempsock[other] == CURL_SOCKET_BAD) {
1008 /* the last attempt failed and no other sockets remain open */
1017 * Now that we've checked whether we are connected, check whether we've
1018 * already timed out.
1020 * First figure out how long time we have left to connect */
1022 allow = Curl_timeleft(data, &now, TRUE);
1025 /* time-out, bail out, go home */
1026 failf(data, "Connection timeout after %ld ms",
1027 Curl_timediff(now, data->progress.t_startsingle));
1028 return CURLE_OPERATION_TIMEDOUT;
1032 (conn->tempsock[0] == CURL_SOCKET_BAD) &&
1033 (conn->tempsock[1] == CURL_SOCKET_BAD)) {
1034 /* no more addresses to try */
1035 const char *hostname;
1036 char buffer[STRERROR_LEN];
1037 CURLcode failreason = result;
1039 /* if the first address family runs out of addresses to try before the
1040 happy eyeball timeout, go ahead and try the next family now */
1041 result = trynextip(data, conn, sockindex, 1);
1045 result = failreason;
1047 #ifndef CURL_DISABLE_PROXY
1048 if(conn->bits.socksproxy)
1049 hostname = conn->socks_proxy.host.name;
1050 else if(conn->bits.httpproxy)
1051 hostname = conn->http_proxy.host.name;
1054 if(conn->bits.conn_to_host)
1055 hostname = conn->conn_to_host.name;
1057 hostname = conn->host.name;
1059 failf(data, "Failed to connect to %s port %u after "
1060 "%" CURL_FORMAT_TIMEDIFF_T " ms: %s",
1061 hostname, conn->port,
1062 Curl_timediff(now, data->progress.t_startsingle),
1064 (conn->transport == TRNSPRT_QUIC) ?
1065 curl_easy_strerror(result) :
1067 Curl_strerror(error, buffer, sizeof(buffer)));
1069 Curl_quic_disconnect(data, conn, 0);
1070 Curl_quic_disconnect(data, conn, 1);
1073 if(WSAETIMEDOUT == data->state.os_errno)
1074 result = CURLE_OPERATION_TIMEDOUT;
1075 #elif defined(ETIMEDOUT)
1076 if(ETIMEDOUT == data->state.os_errno)
1077 result = CURLE_OPERATION_TIMEDOUT;
1081 result = CURLE_OK; /* still trying */
1086 static void tcpnodelay(struct Curl_easy *data, curl_socket_t sockfd)
1088 #if defined(TCP_NODELAY)
1089 curl_socklen_t onoff = (curl_socklen_t) 1;
1090 int level = IPPROTO_TCP;
1091 #if !defined(CURL_DISABLE_VERBOSE_STRINGS)
1092 char buffer[STRERROR_LEN];
1097 if(setsockopt(sockfd, level, TCP_NODELAY, (void *)&onoff,
1099 infof(data, "Could not set TCP_NODELAY: %s",
1100 Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
1108 /* The preferred method on Mac OS X (10.2 and later) to prevent SIGPIPEs when
1109 sending data to a dead peer (instead of relying on the 4th argument to send
1110 being MSG_NOSIGNAL). Possibly also existing and in use on other BSD
1112 static void nosigpipe(struct Curl_easy *data,
1113 curl_socket_t sockfd)
1116 if(setsockopt(sockfd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&onoff,
1117 sizeof(onoff)) < 0) {
1118 #if !defined(CURL_DISABLE_VERBOSE_STRINGS)
1119 char buffer[STRERROR_LEN];
1120 infof(data, "Could not set SO_NOSIGPIPE: %s",
1121 Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
1126 #define nosigpipe(x,y) Curl_nop_stmt
1130 /* When you run a program that uses the Windows Sockets API, you may
1131 experience slow performance when you copy data to a TCP server.
1133 https://support.microsoft.com/kb/823764
1135 Work-around: Make the Socket Send Buffer Size Larger Than the Program Send
1138 The problem described in this knowledge-base is applied only to pre-Vista
1139 Windows. Following function trying to detect OS version and skips
1140 SO_SNDBUF adjustment for Windows Vista and above.
1142 #define DETECT_OS_NONE 0
1143 #define DETECT_OS_PREVISTA 1
1144 #define DETECT_OS_VISTA_OR_LATER 2
1146 void Curl_sndbufset(curl_socket_t sockfd)
1148 int val = CURL_MAX_WRITE_SIZE + 32;
1150 int curlen = sizeof(curval);
1152 static int detectOsState = DETECT_OS_NONE;
1154 if(detectOsState == DETECT_OS_NONE) {
1155 if(curlx_verify_windows_version(6, 0, 0, PLATFORM_WINNT,
1156 VERSION_GREATER_THAN_EQUAL))
1157 detectOsState = DETECT_OS_VISTA_OR_LATER;
1159 detectOsState = DETECT_OS_PREVISTA;
1162 if(detectOsState == DETECT_OS_VISTA_OR_LATER)
1165 if(getsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (char *)&curval, &curlen) == 0)
1169 setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (const char *)&val, sizeof(val));
1176 * Note that even on connect fail it returns CURLE_OK, but with 'sock' set to
1177 * CURL_SOCKET_BAD. Other errors will however return proper errors.
1179 * singleipconnect() connects to the given IP only, and it may return without
1182 static CURLcode singleipconnect(struct Curl_easy *data,
1183 struct connectdata *conn,
1184 const struct Curl_addrinfo *ai,
1187 struct Curl_sockaddr_ex addr;
1190 bool isconnected = FALSE;
1191 curl_socket_t sockfd;
1193 char ipaddress[MAX_IPADR_LEN];
1196 #ifdef TCP_FASTOPEN_CONNECT
1199 char buffer[STRERROR_LEN];
1200 curl_socket_t *sockp = &conn->tempsock[tempindex];
1201 *sockp = CURL_SOCKET_BAD;
1203 result = Curl_socket(data, ai, &addr, &sockfd);
1207 /* store remote address and port used in this connection attempt */
1208 if(!Curl_addr2string((struct sockaddr*)&addr.sa_addr, addr.addrlen,
1209 ipaddress, &port)) {
1210 /* malformed address or bug in inet_ntop, try next address */
1211 failf(data, "sa_addr inet_ntop() failed with errno %d: %s",
1212 errno, Curl_strerror(errno, buffer, sizeof(buffer)));
1213 Curl_closesocket(data, conn, sockfd);
1216 infof(data, " Trying %s:%d...", ipaddress, port);
1219 is_tcp = (addr.family == AF_INET || addr.family == AF_INET6) &&
1220 addr.socktype == SOCK_STREAM;
1222 is_tcp = (addr.family == AF_INET) && addr.socktype == SOCK_STREAM;
1224 if(is_tcp && data->set.tcp_nodelay)
1225 tcpnodelay(data, sockfd);
1227 nosigpipe(data, sockfd);
1229 Curl_sndbufset(sockfd);
1231 if(is_tcp && data->set.tcp_keepalive)
1232 tcpkeepalive(data, sockfd);
1234 if(data->set.fsockopt) {
1235 /* activate callback for setting socket options */
1236 Curl_set_in_callback(data, true);
1237 error = data->set.fsockopt(data->set.sockopt_client,
1239 CURLSOCKTYPE_IPCXN);
1240 Curl_set_in_callback(data, false);
1242 if(error == CURL_SOCKOPT_ALREADY_CONNECTED)
1245 Curl_closesocket(data, conn, sockfd); /* close the socket and bail out */
1246 return CURLE_ABORTED_BY_CALLBACK;
1250 /* possibly bind the local end to an IP, interface or port */
1251 if(addr.family == AF_INET
1253 || addr.family == AF_INET6
1256 result = bindlocal(data, sockfd, addr.family,
1257 Curl_ipv6_scope((struct sockaddr*)&addr.sa_addr));
1259 Curl_closesocket(data, conn, sockfd); /* close socket and bail out */
1260 if(result == CURLE_UNSUPPORTED_PROTOCOL) {
1261 /* The address family is not supported on this interface.
1262 We can continue trying addresses */
1263 return CURLE_COULDNT_CONNECT;
1269 /* set socket non-blocking */
1270 (void)curlx_nonblock(sockfd, TRUE);
1272 conn->connecttime = Curl_now();
1273 if(conn->num_addr > 1) {
1274 Curl_expire(data, conn->timeoutms_per_addr[0], EXPIRE_DNS_PER_NAME);
1275 Curl_expire(data, conn->timeoutms_per_addr[1], EXPIRE_DNS_PER_NAME2);
1278 /* Connect TCP and QUIC sockets */
1279 if(!isconnected && (conn->transport != TRNSPRT_UDP)) {
1280 if(conn->bits.tcp_fastopen) {
1281 #if defined(CONNECT_DATA_IDEMPOTENT) /* Darwin */
1282 # if defined(HAVE_BUILTIN_AVAILABLE)
1283 /* while connectx function is available since macOS 10.11 / iOS 9,
1284 it did not have the interface declared correctly until
1285 Xcode 9 / macOS SDK 10.13 */
1286 if(__builtin_available(macOS 10.11, iOS 9.0, tvOS 9.0, watchOS 2.0, *)) {
1287 sa_endpoints_t endpoints;
1288 endpoints.sae_srcif = 0;
1289 endpoints.sae_srcaddr = NULL;
1290 endpoints.sae_srcaddrlen = 0;
1291 endpoints.sae_dstaddr = &addr.sa_addr;
1292 endpoints.sae_dstaddrlen = addr.addrlen;
1294 rc = connectx(sockfd, &endpoints, SAE_ASSOCID_ANY,
1295 CONNECT_RESUME_ON_READ_WRITE | CONNECT_DATA_IDEMPOTENT,
1296 NULL, 0, NULL, NULL);
1299 rc = connect(sockfd, &addr.sa_addr, addr.addrlen);
1302 rc = connect(sockfd, &addr.sa_addr, addr.addrlen);
1303 # endif /* HAVE_BUILTIN_AVAILABLE */
1304 #elif defined(TCP_FASTOPEN_CONNECT) /* Linux >= 4.11 */
1305 if(setsockopt(sockfd, IPPROTO_TCP, TCP_FASTOPEN_CONNECT,
1306 (void *)&optval, sizeof(optval)) < 0)
1307 infof(data, "Failed to enable TCP Fast Open on fd %d", sockfd);
1309 rc = connect(sockfd, &addr.sa_addr, addr.addrlen);
1310 #elif defined(MSG_FASTOPEN) /* old Linux */
1311 if(conn->given->flags & PROTOPT_SSL)
1312 rc = connect(sockfd, &addr.sa_addr, addr.addrlen);
1314 rc = 0; /* Do nothing */
1318 rc = connect(sockfd, &addr.sa_addr, addr.addrlen);
1324 else if(conn->transport == TRNSPRT_QUIC) {
1325 /* pass in 'sockfd' separately since it hasn't been put into the
1326 tempsock array at this point */
1327 result = Curl_quic_connect(data, conn, sockfd, tempindex,
1328 &addr.sa_addr, addr.addrlen);
1344 #if (EAGAIN) != (EWOULDBLOCK)
1345 /* On some platforms EAGAIN and EWOULDBLOCK are the
1346 * same value, and on others they are different, hence
1356 /* unknown error, fallthrough and try another address! */
1357 infof(data, "Immediate connect fail for %s: %s",
1358 ipaddress, Curl_strerror(error, buffer, sizeof(buffer)));
1359 data->state.os_errno = error;
1361 /* connect failed */
1362 Curl_closesocket(data, conn, sockfd);
1363 result = CURLE_COULDNT_CONNECT;
1374 * TCP connect to the given host with timeout, proxy or remote doesn't matter.
1375 * There might be more than one IP address to try out. Fill in the passed
1376 * pointer with the connected socket.
1379 CURLcode Curl_connecthost(struct Curl_easy *data,
1380 struct connectdata *conn, /* context */
1381 const struct Curl_dns_entry *remotehost)
1383 CURLcode result = CURLE_COULDNT_CONNECT;
1385 timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE);
1387 if(timeout_ms < 0) {
1388 /* a precaution, no need to continue if time already is up */
1389 failf(data, "Connection time-out");
1390 return CURLE_OPERATION_TIMEDOUT;
1393 conn->num_addr = Curl_num_addresses(remotehost->addr);
1394 conn->tempaddr[0] = conn->tempaddr[1] = remotehost->addr;
1395 conn->tempsock[0] = conn->tempsock[1] = CURL_SOCKET_BAD;
1397 /* Max time for the next connection attempt */
1398 conn->timeoutms_per_addr[0] =
1399 conn->tempaddr[0]->ai_next == NULL ? timeout_ms : timeout_ms / 2;
1400 conn->timeoutms_per_addr[1] =
1401 conn->tempaddr[1]->ai_next == NULL ? timeout_ms : timeout_ms / 2;
1403 if(conn->ip_version == CURL_IPRESOLVE_WHATEVER) {
1404 /* any IP version is allowed */
1405 conn->tempfamily[0] = conn->tempaddr[0]?
1406 conn->tempaddr[0]->ai_family:0;
1408 conn->tempfamily[1] = conn->tempfamily[0] == AF_INET6 ?
1411 conn->tempfamily[1] = AF_UNSPEC;
1415 /* only one IP version is allowed */
1416 conn->tempfamily[0] = (conn->ip_version == CURL_IPRESOLVE_V4) ?
1423 conn->tempfamily[1] = AF_UNSPEC;
1425 ainext(conn, 0, FALSE); /* find first address of the right type */
1428 ainext(conn, 1, FALSE); /* assigns conn->tempaddr[1] accordingly */
1430 DEBUGF(infof(data, "family0 == %s, family1 == %s",
1431 conn->tempfamily[0] == AF_INET ? "v4" : "v6",
1432 conn->tempfamily[1] == AF_INET ? "v4" : "v6"));
1434 /* get through the list in family order in case of quick failures */
1435 for(i = 0; (i < 2) && result; i++) {
1436 while(conn->tempaddr[i]) {
1437 result = singleipconnect(data, conn, conn->tempaddr[i], i);
1440 ainext(conn, i, TRUE);
1446 Curl_expire(data, data->set.happy_eyeballs_timeout,
1447 EXPIRE_HAPPY_EYEBALLS);
1454 struct connectdata *found;
1457 static int conn_is_conn(struct Curl_easy *data,
1458 struct connectdata *conn, void *param)
1460 struct connfind *f = (struct connfind *)param;
1462 if(conn->connection_id == f->id_tofind) {
1470 * Used to extract socket and connectdata struct for the most recent
1471 * transfer on the given Curl_easy.
1473 * The returned socket will be CURL_SOCKET_BAD in case of failure!
1475 curl_socket_t Curl_getconnectinfo(struct Curl_easy *data,
1476 struct connectdata **connp)
1480 /* this works for an easy handle:
1481 * - that has been used for curl_easy_perform()
1482 * - that is associated with a multi handle, and whose connection
1483 * was detached with CURLOPT_CONNECT_ONLY
1485 if((data->state.lastconnect_id != -1) && (data->multi_easy || data->multi)) {
1486 struct connectdata *c;
1487 struct connfind find;
1488 find.id_tofind = data->state.lastconnect_id;
1491 Curl_conncache_foreach(data,
1492 data->share && (data->share->specifier
1493 & (1<< CURL_LOCK_DATA_CONNECT))?
1494 &data->share->conn_cache:
1496 &data->multi_easy->conn_cache:
1497 &data->multi->conn_cache, &find, conn_is_conn);
1500 data->state.lastconnect_id = -1;
1501 return CURL_SOCKET_BAD;
1506 /* only store this if the caller cares for it */
1508 return c->sock[FIRSTSOCKET];
1510 return CURL_SOCKET_BAD;
1514 * Check if a connection seems to be alive.
1516 bool Curl_connalive(struct connectdata *conn)
1518 /* First determine if ssl */
1519 if(conn->ssl[FIRSTSOCKET].use) {
1520 /* use the SSL context */
1521 if(!Curl_ssl_check_cxn(conn))
1522 return false; /* FIN received */
1524 /* Minix 3.1 doesn't support any flags on recv; just assume socket is OK */
1526 else if(conn->sock[FIRSTSOCKET] == CURL_SOCKET_BAD)
1529 /* use the socket */
1531 if(recv((RECV_TYPE_ARG1)conn->sock[FIRSTSOCKET], (RECV_TYPE_ARG2)&buf,
1532 (RECV_TYPE_ARG3)1, (RECV_TYPE_ARG4)MSG_PEEK) == 0) {
1533 return false; /* FIN received */
1543 * 'conn' can be NULL, beware!
1545 int Curl_closesocket(struct Curl_easy *data, struct connectdata *conn,
1548 if(conn && conn->fclosesocket) {
1549 if((sock == conn->sock[SECONDARYSOCKET]) && conn->bits.sock_accepted)
1550 /* if this socket matches the second socket, and that was created with
1551 accept, then we MUST NOT call the callback but clear the accepted
1553 conn->bits.sock_accepted = FALSE;
1556 Curl_multi_closed(data, sock);
1557 Curl_set_in_callback(data, true);
1558 rc = conn->fclosesocket(conn->closesocket_client, sock);
1559 Curl_set_in_callback(data, false);
1565 /* tell the multi-socket code about this */
1566 Curl_multi_closed(data, sock);
1574 * Create a socket based on info from 'conn' and 'ai'.
1576 * 'addr' should be a pointer to the correct struct to get data back, or NULL.
1577 * 'sockfd' must be a pointer to a socket descriptor.
1579 * If the open socket callback is set, used that!
1582 CURLcode Curl_socket(struct Curl_easy *data,
1583 const struct Curl_addrinfo *ai,
1584 struct Curl_sockaddr_ex *addr,
1585 curl_socket_t *sockfd)
1587 struct connectdata *conn = data->conn;
1588 struct Curl_sockaddr_ex dummy;
1591 /* if the caller doesn't want info back, use a local temp copy */
1595 * The Curl_sockaddr_ex structure is basically libcurl's external API
1596 * curl_sockaddr structure with enough space available to directly hold
1597 * any protocol-specific address structures. The variable declared here
1598 * will be used to pass / receive data to/from the fopensocket callback
1599 * if this has been set, before that, it is initialized from parameters.
1602 addr->family = ai->ai_family;
1603 addr->socktype = (conn->transport == TRNSPRT_TCP) ? SOCK_STREAM : SOCK_DGRAM;
1604 addr->protocol = conn->transport != TRNSPRT_TCP ? IPPROTO_UDP :
1606 addr->addrlen = ai->ai_addrlen;
1608 if(addr->addrlen > sizeof(struct Curl_sockaddr_storage))
1609 addr->addrlen = sizeof(struct Curl_sockaddr_storage);
1610 memcpy(&addr->sa_addr, ai->ai_addr, addr->addrlen);
1612 if(data->set.fopensocket) {
1614 * If the opensocket callback is set, all the destination address
1615 * information is passed to the callback. Depending on this information the
1616 * callback may opt to abort the connection, this is indicated returning
1617 * CURL_SOCKET_BAD; otherwise it will return a not-connected socket. When
1618 * the callback returns a valid socket the destination address information
1619 * might have been changed and this 'new' address will actually be used
1622 Curl_set_in_callback(data, true);
1623 *sockfd = data->set.fopensocket(data->set.opensocket_client,
1625 (struct curl_sockaddr *)addr);
1626 Curl_set_in_callback(data, false);
1629 /* opensocket callback not set, so simply create the socket now */
1630 *sockfd = socket(addr->family, addr->socktype, addr->protocol);
1632 if(*sockfd == CURL_SOCKET_BAD)
1633 /* no socket, no connection */
1634 return CURLE_COULDNT_CONNECT;
1636 if(conn->transport == TRNSPRT_QUIC) {
1637 /* QUIC sockets need to be nonblocking */
1638 (void)curlx_nonblock(*sockfd, TRUE);
1641 #if defined(ENABLE_IPV6) && defined(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID)
1642 if(conn->scope_id && (addr->family == AF_INET6)) {
1643 struct sockaddr_in6 * const sa6 = (void *)&addr->sa_addr;
1644 sa6->sin6_scope_id = conn->scope_id;
1648 #if defined(__linux__) && defined(IP_RECVERR)
1649 if(addr->socktype == SOCK_DGRAM) {
1651 switch(addr->family) {
1653 (void)setsockopt(*sockfd, SOL_IP, IP_RECVERR, &one, sizeof(one));
1656 (void)setsockopt(*sockfd, SOL_IPV6, IPV6_RECVERR, &one, sizeof(one));
1666 * Curl_conncontrol() marks streams or connection for closure.
1668 void Curl_conncontrol(struct connectdata *conn,
1669 int ctrl /* see defines in header */
1670 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
1671 , const char *reason
1675 /* close if a connection, or a stream that isn't multiplexed. */
1676 /* This function will be called both before and after this connection is
1677 associated with a transfer. */
1680 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
1681 (void)reason; /* useful for debugging */
1683 closeit = (ctrl == CONNCTRL_CONNECTION) ||
1684 ((ctrl == CONNCTRL_STREAM) && !(conn->handler->flags & PROTOPT_STREAM));
1685 if((ctrl == CONNCTRL_STREAM) &&
1686 (conn->handler->flags & PROTOPT_STREAM))
1688 else if((bit)closeit != conn->bits.close) {
1689 conn->bits.close = closeit; /* the only place in the source code that
1690 should assign this bit */
1694 /* Data received can be cached at various levels, so check them all here. */
1695 bool Curl_conn_data_pending(struct connectdata *conn, int sockindex)
1700 if(Curl_ssl_data_pending(conn, sockindex) ||
1701 Curl_recv_has_postponed_data(conn, sockindex))
1704 readable = SOCKET_READABLE(conn->sock[sockindex], 0);
1705 return (readable > 0 && (readable & CURL_CSELECT_IN));