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