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