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