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