1/***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 1998 - 2022, Daniel Stenberg, <[email protected]>, 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 data->info.conn_protocol = conn->handler->protocol;
629 data->info.conn_primary_port = conn->port;
630 data->info.conn_remote_port = conn->remote_port;
631 data->info.conn_local_port = local_port;
632}
633
634/* retrieves ip address and port from a sockaddr structure.
635 note it calls Curl_inet_ntop which sets errno on fail, not SOCKERRNO. */
636bool Curl_addr2string(struct sockaddr *sa, curl_socklen_t salen,
637 char *addr, int *port)
638{
639 struct sockaddr_in *si = NULL;
640#ifdef ENABLE_IPV6
641 struct sockaddr_in6 *si6 = NULL;
642#endif
643#if (defined(HAVE_SYS_UN_H) || defined(WIN32_SOCKADDR_UN)) && defined(AF_UNIX)
644 struct sockaddr_un *su = NULL;
645#else
646 (void)salen;
647#endif
648
649 switch(sa->sa_family) {
650 case AF_INET:
651 si = (struct sockaddr_in *)(void *) sa;
652 if(Curl_inet_ntop(sa->sa_family, &si->sin_addr,
653 addr, MAX_IPADR_LEN)) {
654 unsigned short us_port = ntohs(si->sin_port);
655 *port = us_port;
656 return TRUE;
657 }
658 break;
659#ifdef ENABLE_IPV6
660 case AF_INET6:
661 si6 = (struct sockaddr_in6 *)(void *) sa;
662 if(Curl_inet_ntop(sa->sa_family, &si6->sin6_addr,
663 addr, MAX_IPADR_LEN)) {
664 unsigned short us_port = ntohs(si6->sin6_port);
665 *port = us_port;
666 return TRUE;
667 }
668 break;
669#endif
670#if (defined(HAVE_SYS_UN_H) || defined(WIN32_SOCKADDR_UN)) && defined(AF_UNIX)
671 case AF_UNIX:
672 if(salen > (curl_socklen_t)sizeof(CURL_SA_FAMILY_T)) {
673 su = (struct sockaddr_un*)sa;
674 msnprintf(addr, MAX_IPADR_LEN, "%s", su->sun_path);
675 }
676 else
677 addr[0] = 0; /* socket with no name */
678 *port = 0;
679 return TRUE;
680#endif
681 default:
682 break;
683 }
684
685 addr[0] = '\0';
686 *port = 0;
687 errno = EAFNOSUPPORT;
688 return FALSE;
689}
690
691/* retrieves the start/end point information of a socket of an established
692 connection */
693void Curl_conninfo_remote(struct Curl_easy *data,
694 struct connectdata *conn, curl_socket_t sockfd)
695{
696#ifdef HAVE_GETPEERNAME
697 char buffer[STRERROR_LEN];
698 struct Curl_sockaddr_storage ssrem;
699 curl_socklen_t plen;
700 int port;
701 plen = sizeof(struct Curl_sockaddr_storage);
702 memset(&ssrem, 0, sizeof(ssrem));
703 if(getpeername(sockfd, (struct sockaddr*) &ssrem, &plen)) {
704 int error = SOCKERRNO;
705 failf(data, "getpeername() failed with errno %d: %s",
706 error, Curl_strerror(error, buffer, sizeof(buffer)));
707 return;
708 }
709 if(!Curl_addr2string((struct sockaddr*)&ssrem, plen,
710 conn->primary_ip, &port)) {
711 failf(data, "ssrem inet_ntop() failed with errno %d: %s",
712 errno, Curl_strerror(errno, buffer, sizeof(buffer)));
713 return;
714 }
715#else
716 (void)data;
717 (void)conn;
718 (void)sockfd;
719#endif
720}
721
722/* retrieves the start/end point information of a socket of an established
723 connection */
724void Curl_conninfo_local(struct Curl_easy *data, curl_socket_t sockfd,
725 char *local_ip, int *local_port)
726{
727#ifdef HAVE_GETSOCKNAME
728 char buffer[STRERROR_LEN];
729 struct Curl_sockaddr_storage ssloc;
730 curl_socklen_t slen;
731 slen = sizeof(struct Curl_sockaddr_storage);
732 memset(&ssloc, 0, sizeof(ssloc));
733 if(getsockname(sockfd, (struct sockaddr*) &ssloc, &slen)) {
734 int error = SOCKERRNO;
735 failf(data, "getsockname() failed with errno %d: %s",
736 error, Curl_strerror(error, buffer, sizeof(buffer)));
737 return;
738 }
739 if(!Curl_addr2string((struct sockaddr*)&ssloc, slen,
740 local_ip, local_port)) {
741 failf(data, "ssloc inet_ntop() failed with errno %d: %s",
742 errno, Curl_strerror(errno, buffer, sizeof(buffer)));
743 return;
744 }
745#else
746 (void)data;
747 (void)sockfd;
748 (void)local_ip;
749 (void)local_port;
750#endif
751}
752
753/* retrieves the start/end point information of a socket of an established
754 connection */
755void Curl_updateconninfo(struct Curl_easy *data, struct connectdata *conn,
756 curl_socket_t sockfd)
757{
758 /* 'local_ip' and 'local_port' get filled with local's numerical
759 ip address and port number whenever an outgoing connection is
760 **established** from the primary socket to a remote address. */
761 char local_ip[MAX_IPADR_LEN] = "";
762 int local_port = -1;
763
764 if(conn->transport == TRNSPRT_TCP) {
765 if(!conn->bits.reuse && !conn->bits.tcp_fastopen)
766 Curl_conninfo_remote(data, conn, sockfd);
767 Curl_conninfo_local(data, sockfd, local_ip, &local_port);
768 } /* end of TCP-only section */
769#ifdef ENABLE_QUIC
770 else if(conn->transport == TRNSPRT_QUIC) {
771 if(!conn->bits.reuse)
772 Curl_conninfo_remote(data, conn, sockfd);
773 Curl_conninfo_local(data, sockfd, local_ip, &local_port);
774 }
775#endif
776
777 /* persist connection info in session handle */
778 Curl_persistconninfo(data, conn, local_ip, local_port);
779}
780
781/* After a TCP connection to the proxy has been verified, this function does
782 the next magic steps. If 'done' isn't set TRUE, it is not done yet and
783 must be called again.
784
785 Note: this function's sub-functions call failf()
786
787*/
788static CURLcode connect_SOCKS(struct Curl_easy *data, int sockindex,
789 bool *done)
790{
791 CURLcode result = CURLE_OK;
792#ifndef CURL_DISABLE_PROXY
793 CURLproxycode pxresult = CURLPX_OK;
794 struct connectdata *conn = data->conn;
795 if(conn->bits.socksproxy) {
796 /* for the secondary socket (FTP), use the "connect to host"
797 * but ignore the "connect to port" (use the secondary port)
798 */
799 const char * const host =
800 conn->bits.httpproxy ?
801 conn->http_proxy.host.name :
802 conn->bits.conn_to_host ?
803 conn->conn_to_host.name :
804 sockindex == SECONDARYSOCKET ?
805 conn->secondaryhostname : conn->host.name;
806 const int port =
807 conn->bits.httpproxy ? (int)conn->http_proxy.port :
808 sockindex == SECONDARYSOCKET ? conn->secondary_port :
809 conn->bits.conn_to_port ? conn->conn_to_port :
810 conn->remote_port;
811 switch(conn->socks_proxy.proxytype) {
812 case CURLPROXY_SOCKS5:
813 case CURLPROXY_SOCKS5_HOSTNAME:
814 pxresult = Curl_SOCKS5(conn->socks_proxy.user, conn->socks_proxy.passwd,
815 host, port, sockindex, data, done);
816 break;
817
818 case CURLPROXY_SOCKS4:
819 case CURLPROXY_SOCKS4A:
820 pxresult = Curl_SOCKS4(conn->socks_proxy.user, host, port, sockindex,
821 data, done);
822 break;
823
824 default:
825 failf(data, "unknown proxytype option given");
826 result = CURLE_COULDNT_CONNECT;
827 } /* switch proxytype */
828 if(pxresult) {
829 result = CURLE_PROXY;
830 data->info.pxcode = pxresult;
831 }
832 }
833 else
834#else
835 (void)data;
836 (void)sockindex;
837#endif /* CURL_DISABLE_PROXY */
838 *done = TRUE; /* no SOCKS proxy, so consider us connected */
839
840 return result;
841}
842
843/*
844 * post_SOCKS() is called after a successful connect to the peer, which
845 * *could* be a SOCKS proxy
846 */
847static void post_SOCKS(struct Curl_easy *data,
848 struct connectdata *conn,
849 int sockindex,
850 bool *connected)
851{
852 conn->bits.tcpconnect[sockindex] = TRUE;
853
854 *connected = TRUE;
855 if(sockindex == FIRSTSOCKET)
856 Curl_pgrsTime(data, TIMER_CONNECT); /* connect done */
857 Curl_updateconninfo(data, conn, conn->sock[sockindex]);
858 Curl_verboseconnect(data, conn);
859 data->info.numconnects++; /* to track the number of connections made */
860}
861
862/*
863 * Curl_is_connected() checks if the socket has connected.
864 */
865
866CURLcode Curl_is_connected(struct Curl_easy *data,
867 struct connectdata *conn,
868 int sockindex,
869 bool *connected)
870{
871 CURLcode result = CURLE_OK;
872 timediff_t allow;
873 int error = 0;
874 struct curltime now;
875 int rc = 0;
876 unsigned int i;
877
878 DEBUGASSERT(sockindex >= FIRSTSOCKET && sockindex <= SECONDARYSOCKET);
879
880 *connected = FALSE; /* a very negative world view is best */
881
882 if(conn->bits.tcpconnect[sockindex]) {
883 /* we are connected already! */
884 *connected = TRUE;
885 return CURLE_OK;
886 }
887
888 now = Curl_now();
889
890 if(SOCKS_STATE(conn->cnnct.state)) {
891 /* still doing SOCKS */
892 result = connect_SOCKS(data, sockindex, connected);
893 if(!result && *connected)
894 post_SOCKS(data, conn, sockindex, connected);
895 return result;
896 }
897
898 for(i = 0; i<2; i++) {
899 const int other = i ^ 1;
900 if(conn->tempsock[i] == CURL_SOCKET_BAD)
901 continue;
902 error = 0;
903#ifdef ENABLE_QUIC
904 if(conn->transport == TRNSPRT_QUIC) {
905 result = Curl_quic_is_connected(data, conn, i, connected);
906 if(!result && *connected) {
907 /* use this socket from now on */
908 conn->sock[sockindex] = conn->tempsock[i];
909 conn->ip_addr = conn->tempaddr[i];
910 conn->tempsock[i] = CURL_SOCKET_BAD;
911 post_SOCKS(data, conn, sockindex, connected);
912 connkeep(conn, "HTTP/3 default");
913 if(conn->tempsock[other] != CURL_SOCKET_BAD)
914 Curl_quic_disconnect(data, conn, other);
915 return CURLE_OK;
916 }
917 /* When a QUIC connect attempt fails, the better error explanation is in
918 'result' and not in errno */
919 if(result) {
920 conn->tempsock[i] = CURL_SOCKET_BAD;
921 error = SOCKERRNO;
922 }
923 }
924 else
925#endif
926 {
927#ifdef mpeix
928 /* Call this function once now, and ignore the results. We do this to
929 "clear" the error state on the socket so that we can later read it
930 reliably. This is reported necessary on the MPE/iX operating
931 system. */
932 (void)verifyconnect(conn->tempsock[i], NULL);
933#endif
934
935 /* check socket for connect */
936 rc = SOCKET_WRITABLE(conn->tempsock[i], 0);
937 }
938
939 if(rc == 0) { /* no connection yet */
940 if(Curl_timediff(now, conn->connecttime) >=
941 conn->timeoutms_per_addr[i]) {
942 infof(data, "After %" CURL_FORMAT_TIMEDIFF_T
943 "ms connect time, move on!", conn->timeoutms_per_addr[i]);
944 error = ETIMEDOUT;
945 }
946
947 /* should we try another protocol family? */
948 if(i == 0 && !conn->bits.parallel_connect &&
949 (Curl_timediff(now, conn->connecttime) >=
950 data->set.happy_eyeballs_timeout)) {
951 conn->bits.parallel_connect = TRUE; /* starting now */
952 trynextip(data, conn, sockindex, 1);
953 }
954 }
955 else if(rc == CURL_CSELECT_OUT || conn->bits.tcp_fastopen) {
956 if(verifyconnect(conn->tempsock[i], &error)) {
957 /* we are connected with TCP, awesome! */
958
959 /* use this socket from now on */
960 conn->sock[sockindex] = conn->tempsock[i];
961 conn->ip_addr = conn->tempaddr[i];
962 conn->tempsock[i] = CURL_SOCKET_BAD;
963#ifdef ENABLE_IPV6
964 conn->bits.ipv6 = (conn->ip_addr->ai_family == AF_INET6)?TRUE:FALSE;
965#endif
966
967 /* close the other socket, if open */
968 if(conn->tempsock[other] != CURL_SOCKET_BAD) {
969 Curl_closesocket(data, conn, conn->tempsock[other]);
970 conn->tempsock[other] = CURL_SOCKET_BAD;
971 }
972
973 /* see if we need to kick off any SOCKS proxy magic once we
974 connected */
975 result = connect_SOCKS(data, sockindex, connected);
976 if(result || !*connected)
977 return result;
978
979 post_SOCKS(data, conn, sockindex, connected);
980
981 return CURLE_OK;
982 }
983 }
984 else if(rc & CURL_CSELECT_ERR) {
985 (void)verifyconnect(conn->tempsock[i], &error);
986 }
987
988 /*
989 * The connection failed here, we should attempt to connect to the "next
990 * address" for the given host. But first remember the latest error.
991 */
992 if(error) {
993 data->state.os_errno = error;
994 SET_SOCKERRNO(error);
995 if(conn->tempaddr[i]) {
996 CURLcode status;
997#ifndef CURL_DISABLE_VERBOSE_STRINGS
998 char ipaddress[MAX_IPADR_LEN];
999 char buffer[STRERROR_LEN];
1000 Curl_printable_address(conn->tempaddr[i], ipaddress,
1001 sizeof(ipaddress));
1002#ifdef ENABLE_QUIC
1003 if(conn->transport == TRNSPRT_QUIC) {
1004 infof(data, "connect to %s port %u failed: %s",
1005 ipaddress, conn->port, curl_easy_strerror(result));
1006 }
1007 else
1008#endif
1009 infof(data, "connect to %s port %u failed: %s",
1010 ipaddress, conn->port,
1011 Curl_strerror(error, buffer, sizeof(buffer)));
1012#endif
1013
1014 allow = Curl_timeleft(data, &now, TRUE);
1015 conn->timeoutms_per_addr[i] = conn->tempaddr[i]->ai_next == NULL ?
1016 allow : allow / 2;
1017 ainext(conn, i, TRUE);
1018 status = trynextip(data, conn, sockindex, i);
1019 if((status != CURLE_COULDNT_CONNECT) ||
1020 conn->tempsock[other] == CURL_SOCKET_BAD) {
1021 /* the last attempt failed and no other sockets remain open */
1022 if(!result)
1023 result = status;
1024 }
1025 }
1026 }
1027 }
1028
1029 /*
1030 * Now that we've checked whether we are connected, check whether we've
1031 * already timed out.
1032 *
1033 * First figure out how long time we have left to connect */
1034
1035 allow = Curl_timeleft(data, &now, TRUE);
1036
1037 if(allow < 0) {
1038 /* time-out, bail out, go home */
1039 failf(data, "Connection timeout after %ld ms",
1040 Curl_timediff(now, data->progress.t_startsingle));
1041 return CURLE_OPERATION_TIMEDOUT;
1042 }
1043
1044 if(result &&
1045 (conn->tempsock[0] == CURL_SOCKET_BAD) &&
1046 (conn->tempsock[1] == CURL_SOCKET_BAD)) {
1047 /* no more addresses to try */
1048 const char *hostname;
1049 char buffer[STRERROR_LEN];
1050 CURLcode failreason = result;
1051
1052 /* if the first address family runs out of addresses to try before the
1053 happy eyeball timeout, go ahead and try the next family now */
1054 result = trynextip(data, conn, sockindex, 1);
1055 if(!result)
1056 return result;
1057
1058 result = failreason;
1059
1060#ifndef CURL_DISABLE_PROXY
1061 if(conn->bits.socksproxy)
1062 hostname = conn->socks_proxy.host.name;
1063 else if(conn->bits.httpproxy)
1064 hostname = conn->http_proxy.host.name;
1065 else
1066#endif
1067 if(conn->bits.conn_to_host)
1068 hostname = conn->conn_to_host.name;
1069 else
1070 hostname = conn->host.name;
1071
1072 failf(data, "Failed to connect to %s port %u after "
1073 "%" CURL_FORMAT_TIMEDIFF_T " ms: %s",
1074 hostname, conn->port,
1075 Curl_timediff(now, data->progress.t_startsingle),
1076#ifdef ENABLE_QUIC
1077 (conn->transport == TRNSPRT_QUIC) ?
1078 curl_easy_strerror(result) :
1079#endif
1080 Curl_strerror(error, buffer, sizeof(buffer)));
1081
1082 Curl_quic_disconnect(data, conn, 0);
1083 Curl_quic_disconnect(data, conn, 1);
1084
1085#ifdef WSAETIMEDOUT
1086 if(WSAETIMEDOUT == data->state.os_errno)
1087 result = CURLE_OPERATION_TIMEDOUT;
1088#elif defined(ETIMEDOUT)
1089 if(ETIMEDOUT == data->state.os_errno)
1090 result = CURLE_OPERATION_TIMEDOUT;
1091#endif
1092 }
1093 else
1094 result = CURLE_OK; /* still trying */
1095
1096 return result;
1097}
1098
1099static void tcpnodelay(struct Curl_easy *data, curl_socket_t sockfd)
1100{
1101#if defined(TCP_NODELAY)
1102 curl_socklen_t onoff = (curl_socklen_t) 1;
1103 int level = IPPROTO_TCP;
1104#if !defined(CURL_DISABLE_VERBOSE_STRINGS)
1105 char buffer[STRERROR_LEN];
1106#else
1107 (void) data;
1108#endif
1109
1110 if(setsockopt(sockfd, level, TCP_NODELAY, (void *)&onoff,
1111 sizeof(onoff)) < 0)
1112 infof(data, "Could not set TCP_NODELAY: %s",
1113 Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
1114#else
1115 (void)data;
1116 (void)sockfd;
1117#endif
1118}
1119
1120#ifdef SO_NOSIGPIPE
1121/* The preferred method on Mac OS X (10.2 and later) to prevent SIGPIPEs when
1122 sending data to a dead peer (instead of relying on the 4th argument to send
1123 being MSG_NOSIGNAL). Possibly also existing and in use on other BSD
1124 systems? */
1125static void nosigpipe(struct Curl_easy *data,
1126 curl_socket_t sockfd)
1127{
1128 int onoff = 1;
1129 if(setsockopt(sockfd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&onoff,
1130 sizeof(onoff)) < 0) {
1131#if !defined(CURL_DISABLE_VERBOSE_STRINGS)
1132 char buffer[STRERROR_LEN];
1133 infof(data, "Could not set SO_NOSIGPIPE: %s",
1134 Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
1135#endif
1136 }
1137}
1138#else
1139#define nosigpipe(x,y) Curl_nop_stmt
1140#endif
1141
1142#ifdef USE_WINSOCK
1143/* When you run a program that uses the Windows Sockets API, you may
1144 experience slow performance when you copy data to a TCP server.
1145
1146 https://support.microsoft.com/kb/823764
1147
1148 Work-around: Make the Socket Send Buffer Size Larger Than the Program Send
1149 Buffer Size
1150
1151 The problem described in this knowledge-base is applied only to pre-Vista
1152 Windows. Following function trying to detect OS version and skips
1153 SO_SNDBUF adjustment for Windows Vista and above.
1154*/
1155#define DETECT_OS_NONE 0
1156#define DETECT_OS_PREVISTA 1
1157#define DETECT_OS_VISTA_OR_LATER 2
1158
1159void Curl_sndbufset(curl_socket_t sockfd)
1160{
1161 int val = CURL_MAX_WRITE_SIZE + 32;
1162 int curval = 0;
1163 int curlen = sizeof(curval);
1164
1165 static int detectOsState = DETECT_OS_NONE;
1166
1167 if(detectOsState == DETECT_OS_NONE) {
1168 if(curlx_verify_windows_version(6, 0, 0, PLATFORM_WINNT,
1169 VERSION_GREATER_THAN_EQUAL))
1170 detectOsState = DETECT_OS_VISTA_OR_LATER;
1171 else
1172 detectOsState = DETECT_OS_PREVISTA;
1173 }
1174
1175 if(detectOsState == DETECT_OS_VISTA_OR_LATER)
1176 return;
1177
1178 if(getsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (char *)&curval, &curlen) == 0)
1179 if(curval > val)
1180 return;
1181
1182 setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (const char *)&val, sizeof(val));
1183}
1184#endif
1185
1186/*
1187 * singleipconnect()
1188 *
1189 * Note that even on connect fail it returns CURLE_OK, but with 'sock' set to
1190 * CURL_SOCKET_BAD. Other errors will however return proper errors.
1191 *
1192 * singleipconnect() connects to the given IP only, and it may return without
1193 * having connected.
1194 */
1195static CURLcode singleipconnect(struct Curl_easy *data,
1196 struct connectdata *conn,
1197 const struct Curl_addrinfo *ai,
1198 int tempindex)
1199{
1200 struct Curl_sockaddr_ex addr;
1201 int rc = -1;
1202 int error = 0;
1203 bool isconnected = FALSE;
1204 curl_socket_t sockfd;
1205 CURLcode result;
1206 char ipaddress[MAX_IPADR_LEN];
1207 int port;
1208 bool is_tcp;
1209#ifdef TCP_FASTOPEN_CONNECT
1210 int optval = 1;
1211#endif
1212 char buffer[STRERROR_LEN];
1213 curl_socket_t *sockp = &conn->tempsock[tempindex];
1214 *sockp = CURL_SOCKET_BAD;
1215
1216 result = Curl_socket(data, ai, &addr, &sockfd);
1217 if(result)
1218 return result;
1219
1220 /* store remote address and port used in this connection attempt */
1221 if(!Curl_addr2string((struct sockaddr*)&addr.sa_addr, addr.addrlen,
1222 ipaddress, &port)) {
1223 /* malformed address or bug in inet_ntop, try next address */
1224 failf(data, "sa_addr inet_ntop() failed with errno %d: %s",
1225 errno, Curl_strerror(errno, buffer, sizeof(buffer)));
1226 Curl_closesocket(data, conn, sockfd);
1227 return CURLE_OK;
1228 }
1229 infof(data, " Trying %s:%d...", ipaddress, port);
1230
1231#ifdef ENABLE_IPV6
1232 is_tcp = (addr.family == AF_INET || addr.family == AF_INET6) &&
1233 addr.socktype == SOCK_STREAM;
1234#else
1235 is_tcp = (addr.family == AF_INET) && addr.socktype == SOCK_STREAM;
1236#endif
1237 if(is_tcp && data->set.tcp_nodelay)
1238 tcpnodelay(data, sockfd);
1239
1240 nosigpipe(data, sockfd);
1241
1242 Curl_sndbufset(sockfd);
1243
1244 if(is_tcp && data->set.tcp_keepalive)
1245 tcpkeepalive(data, sockfd);
1246
1247 if(data->set.fsockopt) {
1248 /* activate callback for setting socket options */
1249 Curl_set_in_callback(data, true);
1250 error = data->set.fsockopt(data->set.sockopt_client,
1251 sockfd,
1252 CURLSOCKTYPE_IPCXN);
1253 Curl_set_in_callback(data, false);
1254
1255 if(error == CURL_SOCKOPT_ALREADY_CONNECTED)
1256 isconnected = TRUE;
1257 else if(error) {
1258 Curl_closesocket(data, conn, sockfd); /* close the socket and bail out */
1259 return CURLE_ABORTED_BY_CALLBACK;
1260 }
1261 }
1262
1263 /* possibly bind the local end to an IP, interface or port */
1264 if(addr.family == AF_INET
1265#ifdef ENABLE_IPV6
1266 || addr.family == AF_INET6
1267#endif
1268 ) {
1269 result = bindlocal(data, sockfd, addr.family,
1270 Curl_ipv6_scope((struct sockaddr*)&addr.sa_addr));
1271 if(result) {
1272 Curl_closesocket(data, conn, sockfd); /* close socket and bail out */
1273 if(result == CURLE_UNSUPPORTED_PROTOCOL) {
1274 /* The address family is not supported on this interface.
1275 We can continue trying addresses */
1276 return CURLE_COULDNT_CONNECT;
1277 }
1278 return result;
1279 }
1280 }
1281
1282 /* set socket non-blocking */
1283 (void)curlx_nonblock(sockfd, TRUE);
1284
1285 conn->connecttime = Curl_now();
1286 if(conn->num_addr > 1) {
1287 Curl_expire(data, conn->timeoutms_per_addr[0], EXPIRE_DNS_PER_NAME);
1288 Curl_expire(data, conn->timeoutms_per_addr[1], EXPIRE_DNS_PER_NAME2);
1289 }
1290
1291 /* Connect TCP and QUIC sockets */
1292 if(!isconnected && (conn->transport != TRNSPRT_UDP)) {
1293 if(conn->bits.tcp_fastopen) {
1294#if defined(CONNECT_DATA_IDEMPOTENT) /* Darwin */
1295# if defined(HAVE_BUILTIN_AVAILABLE)
1296 /* while connectx function is available since macOS 10.11 / iOS 9,
1297 it did not have the interface declared correctly until
1298 Xcode 9 / macOS SDK 10.13 */
1299 if(__builtin_available(macOS 10.11, iOS 9.0, tvOS 9.0, watchOS 2.0, *)) {
1300 sa_endpoints_t endpoints;
1301 endpoints.sae_srcif = 0;
1302 endpoints.sae_srcaddr = NULL;
1303 endpoints.sae_srcaddrlen = 0;
1304 endpoints.sae_dstaddr = &addr.sa_addr;
1305 endpoints.sae_dstaddrlen = addr.addrlen;
1306
1307 rc = connectx(sockfd, &endpoints, SAE_ASSOCID_ANY,
1308 CONNECT_RESUME_ON_READ_WRITE | CONNECT_DATA_IDEMPOTENT,
1309 NULL, 0, NULL, NULL);
1310 }
1311 else {
1312 rc = connect(sockfd, &addr.sa_addr, addr.addrlen);
1313 }
1314# else
1315 rc = connect(sockfd, &addr.sa_addr, addr.addrlen);
1316# endif /* HAVE_BUILTIN_AVAILABLE */
1317#elif defined(TCP_FASTOPEN_CONNECT) /* Linux >= 4.11 */
1318 if(setsockopt(sockfd, IPPROTO_TCP, TCP_FASTOPEN_CONNECT,
1319 (void *)&optval, sizeof(optval)) < 0)
1320 infof(data, "Failed to enable TCP Fast Open on fd %d", sockfd);
1321
1322 rc = connect(sockfd, &addr.sa_addr, addr.addrlen);
1323#elif defined(MSG_FASTOPEN) /* old Linux */
1324 if(conn->given->flags & PROTOPT_SSL)
1325 rc = connect(sockfd, &addr.sa_addr, addr.addrlen);
1326 else
1327 rc = 0; /* Do nothing */
1328#endif
1329 }
1330 else {
1331 rc = connect(sockfd, &addr.sa_addr, addr.addrlen);
1332 }
1333
1334 if(-1 == rc)
1335 error = SOCKERRNO;
1336#ifdef ENABLE_QUIC
1337 else if(conn->transport == TRNSPRT_QUIC) {
1338 /* pass in 'sockfd' separately since it hasn't been put into the
1339 tempsock array at this point */
1340 result = Curl_quic_connect(data, conn, sockfd, tempindex,
1341 &addr.sa_addr, addr.addrlen);
1342 if(result)
1343 error = SOCKERRNO;
1344 }
1345#endif
1346 }
1347 else {
1348 *sockp = sockfd;
1349 return CURLE_OK;
1350 }
1351
1352 if(-1 == rc) {
1353 switch(error) {
1354 case EINPROGRESS:
1355 case EWOULDBLOCK:
1356#if defined(EAGAIN)
1357#if (EAGAIN) != (EWOULDBLOCK)
1358 /* On some platforms EAGAIN and EWOULDBLOCK are the
1359 * same value, and on others they are different, hence
1360 * the odd #if
1361 */
1362 case EAGAIN:
1363#endif
1364#endif
1365 result = CURLE_OK;
1366 break;
1367
1368 default:
1369 /* unknown error, fallthrough and try another address! */
1370 infof(data, "Immediate connect fail for %s: %s",
1371 ipaddress, Curl_strerror(error, buffer, sizeof(buffer)));
1372 data->state.os_errno = error;
1373
1374 /* connect failed */
1375 Curl_closesocket(data, conn, sockfd);
1376 result = CURLE_COULDNT_CONNECT;
1377 }
1378 }
1379
1380 if(!result)
1381 *sockp = sockfd;
1382
1383 return result;
1384}
1385
1386/*
1387 * TCP connect to the given host with timeout, proxy or remote doesn't matter.
1388 * There might be more than one IP address to try out. Fill in the passed
1389 * pointer with the connected socket.
1390 */
1391
1392CURLcode Curl_connecthost(struct Curl_easy *data,
1393 struct connectdata *conn, /* context */
1394 const struct Curl_dns_entry *remotehost)
1395{
1396 CURLcode result = CURLE_COULDNT_CONNECT;
1397 int i;
1398 timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE);
1399
1400 if(timeout_ms < 0) {
1401 /* a precaution, no need to continue if time already is up */
1402 failf(data, "Connection time-out");
1403 return CURLE_OPERATION_TIMEDOUT;
1404 }
1405
1406 conn->num_addr = Curl_num_addresses(remotehost->addr);
1407 conn->tempaddr[0] = conn->tempaddr[1] = remotehost->addr;
1408 conn->tempsock[0] = conn->tempsock[1] = CURL_SOCKET_BAD;
1409
1410 /* Max time for the next connection attempt */
1411 conn->timeoutms_per_addr[0] =
1412 conn->tempaddr[0]->ai_next == NULL ? timeout_ms : timeout_ms / 2;
1413 conn->timeoutms_per_addr[1] =
1414 conn->tempaddr[1]->ai_next == NULL ? timeout_ms : timeout_ms / 2;
1415
1416 if(conn->ip_version == CURL_IPRESOLVE_WHATEVER) {
1417 /* any IP version is allowed */
1418 conn->tempfamily[0] = conn->tempaddr[0]?
1419 conn->tempaddr[0]->ai_family:0;
1420#ifdef ENABLE_IPV6
1421 conn->tempfamily[1] = conn->tempfamily[0] == AF_INET6 ?
1422 AF_INET : AF_INET6;
1423#else
1424 conn->tempfamily[1] = AF_UNSPEC;
1425#endif
1426 }
1427 else {
1428 /* only one IP version is allowed */
1429 conn->tempfamily[0] = (conn->ip_version == CURL_IPRESOLVE_V4) ?
1430 AF_INET :
1431#ifdef ENABLE_IPV6
1432 AF_INET6;
1433#else
1434 AF_UNSPEC;
1435#endif
1436 conn->tempfamily[1] = AF_UNSPEC;
1437
1438 ainext(conn, 0, FALSE); /* find first address of the right type */
1439 }
1440
1441 ainext(conn, 1, FALSE); /* assigns conn->tempaddr[1] accordingly */
1442
1443 DEBUGF(infof(data, "family0 == %s, family1 == %s",
1444 conn->tempfamily[0] == AF_INET ? "v4" : "v6",
1445 conn->tempfamily[1] == AF_INET ? "v4" : "v6"));
1446
1447 /* get through the list in family order in case of quick failures */
1448 for(i = 0; (i < 2) && result; i++) {
1449 while(conn->tempaddr[i]) {
1450 result = singleipconnect(data, conn, conn->tempaddr[i], i);
1451 if(!result)
1452 break;
1453 ainext(conn, i, TRUE);
1454 }
1455 }
1456 if(result)
1457 return result;
1458
1459 Curl_expire(data, data->set.happy_eyeballs_timeout,
1460 EXPIRE_HAPPY_EYEBALLS);
1461
1462 return CURLE_OK;
1463}
1464
1465struct connfind {
1466 long id_tofind;
1467 struct connectdata *found;
1468};
1469
1470static int conn_is_conn(struct Curl_easy *data,
1471 struct connectdata *conn, void *param)
1472{
1473 struct connfind *f = (struct connfind *)param;
1474 (void)data;
1475 if(conn->connection_id == f->id_tofind) {
1476 f->found = conn;
1477 return 1;
1478 }
1479 return 0;
1480}
1481
1482/*
1483 * Used to extract socket and connectdata struct for the most recent
1484 * transfer on the given Curl_easy.
1485 *
1486 * The returned socket will be CURL_SOCKET_BAD in case of failure!
1487 */
1488curl_socket_t Curl_getconnectinfo(struct Curl_easy *data,
1489 struct connectdata **connp)
1490{
1491 DEBUGASSERT(data);
1492
1493 /* this works for an easy handle:
1494 * - that has been used for curl_easy_perform()
1495 * - that is associated with a multi handle, and whose connection
1496 * was detached with CURLOPT_CONNECT_ONLY
1497 */
1498 if((data->state.lastconnect_id != -1) && (data->multi_easy || data->multi)) {
1499 struct connectdata *c;
1500 struct connfind find;
1501 find.id_tofind = data->state.lastconnect_id;
1502 find.found = NULL;
1503
1504 Curl_conncache_foreach(data,
1505 data->share && (data->share->specifier
1506 & (1<< CURL_LOCK_DATA_CONNECT))?
1507 &data->share->conn_cache:
1508 data->multi_easy?
1509 &data->multi_easy->conn_cache:
1510 &data->multi->conn_cache, &find, conn_is_conn);
1511
1512 if(!find.found) {
1513 data->state.lastconnect_id = -1;
1514 return CURL_SOCKET_BAD;
1515 }
1516
1517 c = find.found;
1518 if(connp)
1519 /* only store this if the caller cares for it */
1520 *connp = c;
1521 return c->sock[FIRSTSOCKET];
1522 }
1523 return CURL_SOCKET_BAD;
1524}
1525
1526/*
1527 * Check if a connection seems to be alive.
1528 */
1529bool Curl_connalive(struct connectdata *conn)
1530{
1531 /* First determine if ssl */
1532 if(conn->ssl[FIRSTSOCKET].use) {
1533 /* use the SSL context */
1534 if(!Curl_ssl_check_cxn(conn))
1535 return false; /* FIN received */
1536 }
1537/* Minix 3.1 doesn't support any flags on recv; just assume socket is OK */
1538#ifdef MSG_PEEK
1539 else if(conn->sock[FIRSTSOCKET] == CURL_SOCKET_BAD)
1540 return false;
1541 else {
1542 /* use the socket */
1543 char buf;
1544 if(recv((RECV_TYPE_ARG1)conn->sock[FIRSTSOCKET], (RECV_TYPE_ARG2)&buf,
1545 (RECV_TYPE_ARG3)1, (RECV_TYPE_ARG4)MSG_PEEK) == 0) {
1546 return false; /* FIN received */
1547 }
1548 }
1549#endif
1550 return true;
1551}
1552
1553/*
1554 * Close a socket.
1555 *
1556 * 'conn' can be NULL, beware!
1557 */
1558int Curl_closesocket(struct Curl_easy *data, struct connectdata *conn,
1559 curl_socket_t sock)
1560{
1561 if(conn && conn->fclosesocket) {
1562 if((sock == conn->sock[SECONDARYSOCKET]) && conn->bits.sock_accepted)
1563 /* if this socket matches the second socket, and that was created with
1564 accept, then we MUST NOT call the callback but clear the accepted
1565 status */
1566 conn->bits.sock_accepted = FALSE;
1567 else {
1568 int rc;
1569 Curl_multi_closed(data, sock);
1570 Curl_set_in_callback(data, true);
1571 rc = conn->fclosesocket(conn->closesocket_client, sock);
1572 Curl_set_in_callback(data, false);
1573 return rc;
1574 }
1575 }
1576
1577 if(conn)
1578 /* tell the multi-socket code about this */
1579 Curl_multi_closed(data, sock);
1580
1581 sclose(sock);
1582
1583 return 0;
1584}
1585
1586/*
1587 * Create a socket based on info from 'conn' and 'ai'.
1588 *
1589 * 'addr' should be a pointer to the correct struct to get data back, or NULL.
1590 * 'sockfd' must be a pointer to a socket descriptor.
1591 *
1592 * If the open socket callback is set, used that!
1593 *
1594 */
1595CURLcode Curl_socket(struct Curl_easy *data,
1596 const struct Curl_addrinfo *ai,
1597 struct Curl_sockaddr_ex *addr,
1598 curl_socket_t *sockfd)
1599{
1600 struct connectdata *conn = data->conn;
1601 struct Curl_sockaddr_ex dummy;
1602
1603 if(!addr)
1604 /* if the caller doesn't want info back, use a local temp copy */
1605 addr = &dummy;
1606
1607 /*
1608 * The Curl_sockaddr_ex structure is basically libcurl's external API
1609 * curl_sockaddr structure with enough space available to directly hold
1610 * any protocol-specific address structures. The variable declared here
1611 * will be used to pass / receive data to/from the fopensocket callback
1612 * if this has been set, before that, it is initialized from parameters.
1613 */
1614
1615 addr->family = ai->ai_family;
1616 switch(conn->transport) {
1617 case TRNSPRT_TCP:
1618 addr->socktype = SOCK_STREAM;
1619 addr->protocol = IPPROTO_TCP;
1620 break;
1621 case TRNSPRT_UNIX:
1622 addr->socktype = SOCK_STREAM;
1623 addr->protocol = IPPROTO_IP;
1624 break;
1625 default: /* UDP and QUIC */
1626 addr->socktype = SOCK_DGRAM;
1627 addr->protocol = IPPROTO_UDP;
1628 break;
1629 }
1630 addr->addrlen = ai->ai_addrlen;
1631
1632 if(addr->addrlen > sizeof(struct Curl_sockaddr_storage))
1633 addr->addrlen = sizeof(struct Curl_sockaddr_storage);
1634 memcpy(&addr->sa_addr, ai->ai_addr, addr->addrlen);
1635
1636 if(data->set.fopensocket) {
1637 /*
1638 * If the opensocket callback is set, all the destination address
1639 * information is passed to the callback. Depending on this information the
1640 * callback may opt to abort the connection, this is indicated returning
1641 * CURL_SOCKET_BAD; otherwise it will return a not-connected socket. When
1642 * the callback returns a valid socket the destination address information
1643 * might have been changed and this 'new' address will actually be used
1644 * here to connect.
1645 */
1646 Curl_set_in_callback(data, true);
1647 *sockfd = data->set.fopensocket(data->set.opensocket_client,
1648 CURLSOCKTYPE_IPCXN,
1649 (struct curl_sockaddr *)addr);
1650 Curl_set_in_callback(data, false);
1651 }
1652 else
1653 /* opensocket callback not set, so simply create the socket now */
1654 *sockfd = socket(addr->family, addr->socktype, addr->protocol);
1655
1656 if(*sockfd == CURL_SOCKET_BAD)
1657 /* no socket, no connection */
1658 return CURLE_COULDNT_CONNECT;
1659
1660 if(conn->transport == TRNSPRT_QUIC) {
1661 /* QUIC sockets need to be nonblocking */
1662 (void)curlx_nonblock(*sockfd, TRUE);
1663 switch(addr->family) {
1664#if defined(__linux__) && defined(IP_MTU_DISCOVER)
1665 case AF_INET: {
1666 int val = IP_PMTUDISC_DO;
1667 (void)setsockopt(*sockfd, IPPROTO_IP, IP_MTU_DISCOVER, &val,
1668 sizeof(val));
1669 break;
1670 }
1671#endif
1672#if defined(__linux__) && defined(IPV6_MTU_DISCOVER)
1673 case AF_INET6: {
1674 int val = IPV6_PMTUDISC_DO;
1675 (void)setsockopt(*sockfd, IPPROTO_IPV6, IPV6_MTU_DISCOVER, &val,
1676 sizeof(val));
1677 break;
1678 }
1679#endif
1680 }
1681 }
1682
1683#if defined(ENABLE_IPV6) && defined(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID)
1684 if(conn->scope_id && (addr->family == AF_INET6)) {
1685 struct sockaddr_in6 * const sa6 = (void *)&addr->sa_addr;
1686 sa6->sin6_scope_id = conn->scope_id;
1687 }
1688#endif
1689
1690 return CURLE_OK;
1691}
1692
1693/*
1694 * Curl_conncontrol() marks streams or connection for closure.
1695 */
1696void Curl_conncontrol(struct connectdata *conn,
1697 int ctrl /* see defines in header */
1698#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
1699 , const char *reason
1700#endif
1701 )
1702{
1703 /* close if a connection, or a stream that isn't multiplexed. */
1704 /* This function will be called both before and after this connection is
1705 associated with a transfer. */
1706 bool closeit;
1707 DEBUGASSERT(conn);
1708#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
1709 (void)reason; /* useful for debugging */
1710#endif
1711 closeit = (ctrl == CONNCTRL_CONNECTION) ||
1712 ((ctrl == CONNCTRL_STREAM) && !(conn->handler->flags & PROTOPT_STREAM));
1713 if((ctrl == CONNCTRL_STREAM) &&
1714 (conn->handler->flags & PROTOPT_STREAM))
1715 ;
1716 else if((bit)closeit != conn->bits.close) {
1717 conn->bits.close = closeit; /* the only place in the source code that
1718 should assign this bit */
1719 }
1720}
1721
1722/* Data received can be cached at various levels, so check them all here. */
1723bool Curl_conn_data_pending(struct connectdata *conn, int sockindex)
1724{
1725 int readable;
1726 DEBUGASSERT(conn);
1727
1728 if(Curl_ssl_data_pending(conn, sockindex) ||
1729 Curl_recv_has_postponed_data(conn, sockindex))
1730 return true;
1731
1732 readable = SOCKET_READABLE(conn->sock[sockindex], 0);
1733 return (readable > 0 && (readable & CURL_CSELECT_IN));
1734}
1735