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