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