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