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 > 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
981 return result;
982 }
983
tcpnodelay(struct connectdata * conn,curl_socket_t sockfd)984 static void tcpnodelay(struct connectdata *conn, curl_socket_t sockfd)
985 {
986 #if defined(TCP_NODELAY)
987 #if !defined(CURL_DISABLE_VERBOSE_STRINGS)
988 struct Curl_easy *data = conn->data;
989 #endif
990 curl_socklen_t onoff = (curl_socklen_t) 1;
991 int level = IPPROTO_TCP;
992 char buffer[STRERROR_LEN];
993
994 #if defined(CURL_DISABLE_VERBOSE_STRINGS)
995 (void) conn;
996 #endif
997
998 if(setsockopt(sockfd, level, TCP_NODELAY, (void *)&onoff,
999 sizeof(onoff)) < 0)
1000 infof(data, "Could not set TCP_NODELAY: %s\n",
1001 Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
1002 else
1003 infof(data, "TCP_NODELAY set\n");
1004 #else
1005 (void)conn;
1006 (void)sockfd;
1007 #endif
1008 }
1009
1010 #ifdef SO_NOSIGPIPE
1011 /* The preferred method on Mac OS X (10.2 and later) to prevent SIGPIPEs when
1012 sending data to a dead peer (instead of relying on the 4th argument to send
1013 being MSG_NOSIGNAL). Possibly also existing and in use on other BSD
1014 systems? */
nosigpipe(struct connectdata * conn,curl_socket_t sockfd)1015 static void nosigpipe(struct connectdata *conn,
1016 curl_socket_t sockfd)
1017 {
1018 struct Curl_easy *data = conn->data;
1019 int onoff = 1;
1020 if(setsockopt(sockfd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&onoff,
1021 sizeof(onoff)) < 0) {
1022 char buffer[STRERROR_LEN];
1023 infof(data, "Could not set SO_NOSIGPIPE: %s\n",
1024 Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
1025 }
1026 }
1027 #else
1028 #define nosigpipe(x,y) Curl_nop_stmt
1029 #endif
1030
1031 #ifdef USE_WINSOCK
1032 /* When you run a program that uses the Windows Sockets API, you may
1033 experience slow performance when you copy data to a TCP server.
1034
1035 https://support.microsoft.com/kb/823764
1036
1037 Work-around: Make the Socket Send Buffer Size Larger Than the Program Send
1038 Buffer Size
1039
1040 The problem described in this knowledge-base is applied only to pre-Vista
1041 Windows. Following function trying to detect OS version and skips
1042 SO_SNDBUF adjustment for Windows Vista and above.
1043 */
1044 #define DETECT_OS_NONE 0
1045 #define DETECT_OS_PREVISTA 1
1046 #define DETECT_OS_VISTA_OR_LATER 2
1047
Curl_sndbufset(curl_socket_t sockfd)1048 void Curl_sndbufset(curl_socket_t sockfd)
1049 {
1050 int val = CURL_MAX_WRITE_SIZE + 32;
1051 int curval = 0;
1052 int curlen = sizeof(curval);
1053
1054 static int detectOsState = DETECT_OS_NONE;
1055
1056 if(detectOsState == DETECT_OS_NONE) {
1057 if(Curl_verify_windows_version(6, 0, PLATFORM_WINNT,
1058 VERSION_GREATER_THAN_EQUAL))
1059 detectOsState = DETECT_OS_VISTA_OR_LATER;
1060 else
1061 detectOsState = DETECT_OS_PREVISTA;
1062 }
1063
1064 if(detectOsState == DETECT_OS_VISTA_OR_LATER)
1065 return;
1066
1067 if(getsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (char *)&curval, &curlen) == 0)
1068 if(curval > val)
1069 return;
1070
1071 setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (const char *)&val, sizeof(val));
1072 }
1073 #endif
1074
1075 /*
1076 * singleipconnect()
1077 *
1078 * Note that even on connect fail it returns CURLE_OK, but with 'sock' set to
1079 * CURL_SOCKET_BAD. Other errors will however return proper errors.
1080 *
1081 * singleipconnect() connects to the given IP only, and it may return without
1082 * having connected.
1083 */
singleipconnect(struct connectdata * conn,const Curl_addrinfo * ai,int sockindex)1084 static CURLcode singleipconnect(struct connectdata *conn,
1085 const Curl_addrinfo *ai,
1086 int sockindex)
1087 {
1088 struct Curl_sockaddr_ex addr;
1089 int rc = -1;
1090 int error = 0;
1091 bool isconnected = FALSE;
1092 struct Curl_easy *data = conn->data;
1093 curl_socket_t sockfd;
1094 CURLcode result;
1095 char ipaddress[MAX_IPADR_LEN];
1096 long port;
1097 bool is_tcp;
1098 #ifdef TCP_FASTOPEN_CONNECT
1099 int optval = 1;
1100 #endif
1101 char buffer[STRERROR_LEN];
1102 curl_socket_t *sockp = &conn->tempsock[sockindex];
1103 *sockp = CURL_SOCKET_BAD;
1104
1105 result = Curl_socket(conn, ai, &addr, &sockfd);
1106 if(result)
1107 /* Failed to create the socket, but still return OK since we signal the
1108 lack of socket as well. This allows the parent function to keep looping
1109 over alternative addresses/socket families etc. */
1110 return CURLE_OK;
1111
1112 /* store remote address and port used in this connection attempt */
1113 if(!Curl_addr2string((struct sockaddr*)&addr.sa_addr, addr.addrlen,
1114 ipaddress, &port)) {
1115 /* malformed address or bug in inet_ntop, try next address */
1116 failf(data, "sa_addr inet_ntop() failed with errno %d: %s",
1117 errno, Curl_strerror(errno, buffer, sizeof(buffer)));
1118 Curl_closesocket(conn, sockfd);
1119 return CURLE_OK;
1120 }
1121 infof(data, " Trying %s:%ld...\n", ipaddress, port);
1122
1123 #ifdef ENABLE_IPV6
1124 is_tcp = (addr.family == AF_INET || addr.family == AF_INET6) &&
1125 addr.socktype == SOCK_STREAM;
1126 #else
1127 is_tcp = (addr.family == AF_INET) && addr.socktype == SOCK_STREAM;
1128 #endif
1129 if(is_tcp && data->set.tcp_nodelay)
1130 tcpnodelay(conn, sockfd);
1131
1132 nosigpipe(conn, sockfd);
1133
1134 Curl_sndbufset(sockfd);
1135
1136 if(is_tcp && data->set.tcp_keepalive)
1137 tcpkeepalive(data, sockfd);
1138
1139 if(data->set.fsockopt) {
1140 /* activate callback for setting socket options */
1141 Curl_set_in_callback(data, true);
1142 error = data->set.fsockopt(data->set.sockopt_client,
1143 sockfd,
1144 CURLSOCKTYPE_IPCXN);
1145 Curl_set_in_callback(data, false);
1146
1147 if(error == CURL_SOCKOPT_ALREADY_CONNECTED)
1148 isconnected = TRUE;
1149 else if(error) {
1150 Curl_closesocket(conn, sockfd); /* close the socket and bail out */
1151 return CURLE_ABORTED_BY_CALLBACK;
1152 }
1153 }
1154
1155 /* possibly bind the local end to an IP, interface or port */
1156 if(addr.family == AF_INET
1157 #ifdef ENABLE_IPV6
1158 || addr.family == AF_INET6
1159 #endif
1160 ) {
1161 result = bindlocal(conn, sockfd, addr.family,
1162 Curl_ipv6_scope((struct sockaddr*)&addr.sa_addr));
1163 if(result) {
1164 Curl_closesocket(conn, sockfd); /* close socket and bail out */
1165 if(result == CURLE_UNSUPPORTED_PROTOCOL) {
1166 /* The address family is not supported on this interface.
1167 We can continue trying addresses */
1168 return CURLE_COULDNT_CONNECT;
1169 }
1170 return result;
1171 }
1172 }
1173
1174 /* set socket non-blocking */
1175 (void)curlx_nonblock(sockfd, TRUE);
1176
1177 conn->connecttime = Curl_now();
1178 if(conn->num_addr > 1)
1179 Curl_expire(data, conn->timeoutms_per_addr, EXPIRE_DNS_PER_NAME);
1180
1181 /* Connect TCP and QUIC sockets */
1182 if(!isconnected && (conn->transport != TRNSPRT_UDP)) {
1183 if(conn->bits.tcp_fastopen) {
1184 #if defined(CONNECT_DATA_IDEMPOTENT) /* Darwin */
1185 # if defined(HAVE_BUILTIN_AVAILABLE)
1186 /* while connectx function is available since macOS 10.11 / iOS 9,
1187 it did not have the interface declared correctly until
1188 Xcode 9 / macOS SDK 10.13 */
1189 if(__builtin_available(macOS 10.11, iOS 9.0, tvOS 9.0, watchOS 2.0, *)) {
1190 sa_endpoints_t endpoints;
1191 endpoints.sae_srcif = 0;
1192 endpoints.sae_srcaddr = NULL;
1193 endpoints.sae_srcaddrlen = 0;
1194 endpoints.sae_dstaddr = &addr.sa_addr;
1195 endpoints.sae_dstaddrlen = addr.addrlen;
1196
1197 rc = connectx(sockfd, &endpoints, SAE_ASSOCID_ANY,
1198 CONNECT_RESUME_ON_READ_WRITE | CONNECT_DATA_IDEMPOTENT,
1199 NULL, 0, NULL, NULL);
1200 }
1201 else {
1202 rc = connect(sockfd, &addr.sa_addr, addr.addrlen);
1203 }
1204 # else
1205 rc = connect(sockfd, &addr.sa_addr, addr.addrlen);
1206 # endif /* HAVE_BUILTIN_AVAILABLE */
1207 #elif defined(TCP_FASTOPEN_CONNECT) /* Linux >= 4.11 */
1208 if(setsockopt(sockfd, IPPROTO_TCP, TCP_FASTOPEN_CONNECT,
1209 (void *)&optval, sizeof(optval)) < 0)
1210 infof(data, "Failed to enable TCP Fast Open on fd %d\n", sockfd);
1211 else
1212 infof(data, "TCP_FASTOPEN_CONNECT set\n");
1213
1214 rc = connect(sockfd, &addr.sa_addr, addr.addrlen);
1215 #elif defined(MSG_FASTOPEN) /* old Linux */
1216 if(conn->given->flags & PROTOPT_SSL)
1217 rc = connect(sockfd, &addr.sa_addr, addr.addrlen);
1218 else
1219 rc = 0; /* Do nothing */
1220 #endif
1221 }
1222 else {
1223 rc = connect(sockfd, &addr.sa_addr, addr.addrlen);
1224 }
1225
1226 if(-1 == rc)
1227 error = SOCKERRNO;
1228 #ifdef ENABLE_QUIC
1229 else if(conn->transport == TRNSPRT_QUIC) {
1230 /* pass in 'sockfd' separately since it hasn't been put into the
1231 tempsock array at this point */
1232 result = Curl_quic_connect(conn, sockfd, sockindex,
1233 &addr.sa_addr, addr.addrlen);
1234 if(result)
1235 error = SOCKERRNO;
1236 }
1237 #endif
1238 }
1239 else {
1240 *sockp = sockfd;
1241 return CURLE_OK;
1242 }
1243
1244 if(-1 == rc) {
1245 switch(error) {
1246 case EINPROGRESS:
1247 case EWOULDBLOCK:
1248 #if defined(EAGAIN)
1249 #if (EAGAIN) != (EWOULDBLOCK)
1250 /* On some platforms EAGAIN and EWOULDBLOCK are the
1251 * same value, and on others they are different, hence
1252 * the odd #if
1253 */
1254 case EAGAIN:
1255 #endif
1256 #endif
1257 result = CURLE_OK;
1258 break;
1259
1260 default:
1261 /* unknown error, fallthrough and try another address! */
1262 infof(data, "Immediate connect fail for %s: %s\n",
1263 ipaddress, Curl_strerror(error, buffer, sizeof(buffer)));
1264 data->state.os_errno = error;
1265
1266 /* connect failed */
1267 Curl_closesocket(conn, sockfd);
1268 result = CURLE_COULDNT_CONNECT;
1269 }
1270 }
1271
1272 if(!result)
1273 *sockp = sockfd;
1274
1275 return result;
1276 }
1277
1278 /*
1279 * TCP connect to the given host with timeout, proxy or remote doesn't matter.
1280 * There might be more than one IP address to try out. Fill in the passed
1281 * pointer with the connected socket.
1282 */
1283
Curl_connecthost(struct connectdata * conn,const struct Curl_dns_entry * remotehost)1284 CURLcode Curl_connecthost(struct connectdata *conn, /* context */
1285 const struct Curl_dns_entry *remotehost)
1286 {
1287 struct Curl_easy *data = conn->data;
1288 struct curltime before = Curl_now();
1289 CURLcode result = CURLE_COULDNT_CONNECT;
1290
1291 timediff_t timeout_ms = Curl_timeleft(data, &before, TRUE);
1292
1293 if(timeout_ms < 0) {
1294 /* a precaution, no need to continue if time already is up */
1295 failf(data, "Connection time-out");
1296 return CURLE_OPERATION_TIMEDOUT;
1297 }
1298
1299 conn->num_addr = Curl_num_addresses(remotehost->addr);
1300 conn->tempaddr[0] = remotehost->addr;
1301 conn->tempaddr[1] = NULL;
1302 conn->tempsock[0] = CURL_SOCKET_BAD;
1303 conn->tempsock[1] = CURL_SOCKET_BAD;
1304
1305 /* Max time for the next connection attempt */
1306 conn->timeoutms_per_addr =
1307 conn->tempaddr[0]->ai_next == NULL ? timeout_ms : timeout_ms / 2;
1308
1309 /* start connecting to first IP */
1310 while(conn->tempaddr[0]) {
1311 result = singleipconnect(conn, conn->tempaddr[0], 0);
1312 if(!result)
1313 break;
1314 conn->tempaddr[0] = conn->tempaddr[0]->ai_next;
1315 }
1316
1317 if(conn->tempsock[0] == CURL_SOCKET_BAD) {
1318 if(!result)
1319 result = CURLE_COULDNT_CONNECT;
1320 return result;
1321 }
1322
1323 data->info.numconnects++; /* to track the number of connections made */
1324 Curl_expire(conn->data, data->set.happy_eyeballs_timeout,
1325 EXPIRE_HAPPY_EYEBALLS);
1326
1327 return CURLE_OK;
1328 }
1329
1330 struct connfind {
1331 struct connectdata *tofind;
1332 bool found;
1333 };
1334
conn_is_conn(struct connectdata * conn,void * param)1335 static int conn_is_conn(struct connectdata *conn, void *param)
1336 {
1337 struct connfind *f = (struct connfind *)param;
1338 if(conn == f->tofind) {
1339 f->found = TRUE;
1340 return 1;
1341 }
1342 return 0;
1343 }
1344
1345 /*
1346 * Used to extract socket and connectdata struct for the most recent
1347 * transfer on the given Curl_easy.
1348 *
1349 * The returned socket will be CURL_SOCKET_BAD in case of failure!
1350 */
Curl_getconnectinfo(struct Curl_easy * data,struct connectdata ** connp)1351 curl_socket_t Curl_getconnectinfo(struct Curl_easy *data,
1352 struct connectdata **connp)
1353 {
1354 DEBUGASSERT(data);
1355
1356 /* this works for an easy handle:
1357 * - that has been used for curl_easy_perform()
1358 * - that is associated with a multi handle, and whose connection
1359 * was detached with CURLOPT_CONNECT_ONLY
1360 */
1361 if(data->state.lastconnect && (data->multi_easy || data->multi)) {
1362 struct connectdata *c = data->state.lastconnect;
1363 struct connfind find;
1364 find.tofind = data->state.lastconnect;
1365 find.found = FALSE;
1366
1367 Curl_conncache_foreach(data, data->multi_easy?
1368 &data->multi_easy->conn_cache:
1369 &data->multi->conn_cache, &find, conn_is_conn);
1370
1371 if(!find.found) {
1372 data->state.lastconnect = NULL;
1373 return CURL_SOCKET_BAD;
1374 }
1375
1376 if(connp) {
1377 /* only store this if the caller cares for it */
1378 *connp = c;
1379 c->data = data;
1380 }
1381 return c->sock[FIRSTSOCKET];
1382 }
1383 else
1384 return CURL_SOCKET_BAD;
1385 }
1386
1387 /*
1388 * Check if a connection seems to be alive.
1389 */
Curl_connalive(struct connectdata * conn)1390 bool Curl_connalive(struct connectdata *conn)
1391 {
1392 /* First determine if ssl */
1393 if(conn->ssl[FIRSTSOCKET].use) {
1394 /* use the SSL context */
1395 if(!Curl_ssl_check_cxn(conn))
1396 return false; /* FIN received */
1397 }
1398 /* Minix 3.1 doesn't support any flags on recv; just assume socket is OK */
1399 #ifdef MSG_PEEK
1400 else if(conn->sock[FIRSTSOCKET] == CURL_SOCKET_BAD)
1401 return false;
1402 else {
1403 /* use the socket */
1404 char buf;
1405 if(recv((RECV_TYPE_ARG1)conn->sock[FIRSTSOCKET], (RECV_TYPE_ARG2)&buf,
1406 (RECV_TYPE_ARG3)1, (RECV_TYPE_ARG4)MSG_PEEK) == 0) {
1407 return false; /* FIN received */
1408 }
1409 }
1410 #endif
1411 return true;
1412 }
1413
1414 /*
1415 * Close a socket.
1416 *
1417 * 'conn' can be NULL, beware!
1418 */
Curl_closesocket(struct connectdata * conn,curl_socket_t sock)1419 int Curl_closesocket(struct connectdata *conn,
1420 curl_socket_t sock)
1421 {
1422 if(conn && conn->fclosesocket) {
1423 if((sock == conn->sock[SECONDARYSOCKET]) &&
1424 conn->sock_accepted[SECONDARYSOCKET])
1425 /* if this socket matches the second socket, and that was created with
1426 accept, then we MUST NOT call the callback but clear the accepted
1427 status */
1428 conn->sock_accepted[SECONDARYSOCKET] = FALSE;
1429 else {
1430 int rc;
1431 Curl_multi_closed(conn->data, sock);
1432 Curl_set_in_callback(conn->data, true);
1433 rc = conn->fclosesocket(conn->closesocket_client, sock);
1434 Curl_set_in_callback(conn->data, false);
1435 return rc;
1436 }
1437 }
1438
1439 if(conn)
1440 /* tell the multi-socket code about this */
1441 Curl_multi_closed(conn->data, sock);
1442
1443 sclose(sock);
1444
1445 return 0;
1446 }
1447
1448 /*
1449 * Create a socket based on info from 'conn' and 'ai'.
1450 *
1451 * 'addr' should be a pointer to the correct struct to get data back, or NULL.
1452 * 'sockfd' must be a pointer to a socket descriptor.
1453 *
1454 * If the open socket callback is set, used that!
1455 *
1456 */
Curl_socket(struct connectdata * conn,const Curl_addrinfo * ai,struct Curl_sockaddr_ex * addr,curl_socket_t * sockfd)1457 CURLcode Curl_socket(struct connectdata *conn,
1458 const Curl_addrinfo *ai,
1459 struct Curl_sockaddr_ex *addr,
1460 curl_socket_t *sockfd)
1461 {
1462 struct Curl_easy *data = conn->data;
1463 struct Curl_sockaddr_ex dummy;
1464
1465 if(!addr)
1466 /* if the caller doesn't want info back, use a local temp copy */
1467 addr = &dummy;
1468
1469 /*
1470 * The Curl_sockaddr_ex structure is basically libcurl's external API
1471 * curl_sockaddr structure with enough space available to directly hold
1472 * any protocol-specific address structures. The variable declared here
1473 * will be used to pass / receive data to/from the fopensocket callback
1474 * if this has been set, before that, it is initialized from parameters.
1475 */
1476
1477 addr->family = ai->ai_family;
1478 addr->socktype = (conn->transport == TRNSPRT_TCP) ? SOCK_STREAM : SOCK_DGRAM;
1479 addr->protocol = conn->transport != TRNSPRT_TCP ? IPPROTO_UDP :
1480 ai->ai_protocol;
1481 addr->addrlen = ai->ai_addrlen;
1482
1483 if(addr->addrlen > sizeof(struct Curl_sockaddr_storage))
1484 addr->addrlen = sizeof(struct Curl_sockaddr_storage);
1485 memcpy(&addr->sa_addr, ai->ai_addr, addr->addrlen);
1486
1487 if(data->set.fopensocket) {
1488 /*
1489 * If the opensocket callback is set, all the destination address
1490 * information is passed to the callback. Depending on this information the
1491 * callback may opt to abort the connection, this is indicated returning
1492 * CURL_SOCKET_BAD; otherwise it will return a not-connected socket. When
1493 * the callback returns a valid socket the destination address information
1494 * might have been changed and this 'new' address will actually be used
1495 * here to connect.
1496 */
1497 Curl_set_in_callback(data, true);
1498 *sockfd = data->set.fopensocket(data->set.opensocket_client,
1499 CURLSOCKTYPE_IPCXN,
1500 (struct curl_sockaddr *)addr);
1501 Curl_set_in_callback(data, false);
1502 }
1503 else
1504 /* opensocket callback not set, so simply create the socket now */
1505 *sockfd = socket(addr->family, addr->socktype, addr->protocol);
1506
1507 if(*sockfd == CURL_SOCKET_BAD)
1508 /* no socket, no connection */
1509 return CURLE_COULDNT_CONNECT;
1510
1511 #if defined(ENABLE_IPV6) && defined(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID)
1512 if(conn->scope_id && (addr->family == AF_INET6)) {
1513 struct sockaddr_in6 * const sa6 = (void *)&addr->sa_addr;
1514 sa6->sin6_scope_id = conn->scope_id;
1515 }
1516 #endif
1517
1518 return CURLE_OK;
1519
1520 }
1521
1522 /*
1523 * Curl_conncontrol() marks streams or connection for closure.
1524 */
Curl_conncontrol(struct connectdata * conn,int ctrl,const char * reason)1525 void Curl_conncontrol(struct connectdata *conn,
1526 int ctrl /* see defines in header */
1527 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
1528 , const char *reason
1529 #endif
1530 )
1531 {
1532 /* close if a connection, or a stream that isn't multiplexed */
1533 bool closeit = (ctrl == CONNCTRL_CONNECTION) ||
1534 ((ctrl == CONNCTRL_STREAM) && !(conn->handler->flags & PROTOPT_STREAM));
1535 if((ctrl == CONNCTRL_STREAM) &&
1536 (conn->handler->flags & PROTOPT_STREAM))
1537 DEBUGF(infof(conn->data, "Kill stream: %s\n", reason));
1538 else if((bit)closeit != conn->bits.close) {
1539 DEBUGF(infof(conn->data, "Marked for [%s]: %s\n",
1540 closeit?"closure":"keep alive", reason));
1541 conn->bits.close = closeit; /* the only place in the source code that
1542 should assign this bit */
1543 }
1544 }
1545
1546 /* Data received can be cached at various levels, so check them all here. */
Curl_conn_data_pending(struct connectdata * conn,int sockindex)1547 bool Curl_conn_data_pending(struct connectdata *conn, int sockindex)
1548 {
1549 int readable;
1550
1551 if(Curl_ssl_data_pending(conn, sockindex) ||
1552 Curl_recv_has_postponed_data(conn, sockindex))
1553 return true;
1554
1555 readable = SOCKET_READABLE(conn->sock[sockindex], 0);
1556 return (readable > 0 && (readable & CURL_CSELECT_IN));
1557 }
1558