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