• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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