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