VirtualBox

source: vbox/trunk/src/libs/curl-7.87.0/lib/connect.c@ 98929

Last change on this file since 98929 was 98326, checked in by vboxsync, 2 years ago

curl-7.87.0: Applied and adjusted our curl changes to 7.83.1. bugref:10356

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette