• 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_WINSOCK_H)
89 /* DragonFlyBSD and Windows use millisecond units */
90 #define KEEPALIVE_FACTOR(x) (x *= 1000)
91 #else
92 #define KEEPALIVE_FACTOR(x)
93 #endif
94 
95 #if defined(HAVE_WINSOCK2_H) && !defined(SIO_KEEPALIVE_VALS)
96 #define SIO_KEEPALIVE_VALS    _WSAIOW(IOC_VENDOR,4)
97 
98 struct tcp_keepalive {
99   u_long onoff;
100   u_long keepalivetime;
101   u_long keepaliveinterval;
102 };
103 #endif
104 
105 static void
tcpkeepalive(struct 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       if(ai) {
593         result = singleipconnect(data, conn, ai, tempindex);
594         if(result == CURLE_COULDNT_CONNECT) {
595           ai = ainext(conn, tempindex, TRUE);
596           continue;
597         }
598       }
599       break;
600     }
601   }
602 
603   if(fd_to_close != CURL_SOCKET_BAD)
604     Curl_closesocket(data, conn, fd_to_close);
605 
606   return result;
607 }
608 
609 /* Copies connection info into the transfer handle to make it available when
610    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)611 void Curl_persistconninfo(struct Curl_easy *data, struct connectdata *conn,
612                           char *local_ip, int local_port)
613 {
614   memcpy(data->info.conn_primary_ip, conn->primary_ip, MAX_IPADR_LEN);
615   if(local_ip && local_ip[0])
616     memcpy(data->info.conn_local_ip, local_ip, MAX_IPADR_LEN);
617   else
618     data->info.conn_local_ip[0] = 0;
619   data->info.conn_scheme = conn->handler->scheme;
620   data->info.conn_protocol = conn->handler->protocol;
621   data->info.conn_primary_port = conn->port;
622   data->info.conn_remote_port = conn->remote_port;
623   data->info.conn_local_port = local_port;
624 }
625 
626 /* retrieves ip address and port from a sockaddr structure.
627    note it calls Curl_inet_ntop which sets errno on fail, not SOCKERRNO. */
Curl_addr2string(struct sockaddr * sa,curl_socklen_t salen,char * addr,int * port)628 bool Curl_addr2string(struct sockaddr *sa, curl_socklen_t salen,
629                       char *addr, int *port)
630 {
631   struct sockaddr_in *si = NULL;
632 #ifdef ENABLE_IPV6
633   struct sockaddr_in6 *si6 = NULL;
634 #endif
635 #if defined(HAVE_SYS_UN_H) && defined(AF_UNIX)
636   struct sockaddr_un *su = NULL;
637 #else
638   (void)salen;
639 #endif
640 
641   switch(sa->sa_family) {
642     case AF_INET:
643       si = (struct sockaddr_in *)(void *) sa;
644       if(Curl_inet_ntop(sa->sa_family, &si->sin_addr,
645                         addr, MAX_IPADR_LEN)) {
646         unsigned short us_port = ntohs(si->sin_port);
647         *port = us_port;
648         return TRUE;
649       }
650       break;
651 #ifdef ENABLE_IPV6
652     case AF_INET6:
653       si6 = (struct sockaddr_in6 *)(void *) sa;
654       if(Curl_inet_ntop(sa->sa_family, &si6->sin6_addr,
655                         addr, MAX_IPADR_LEN)) {
656         unsigned short us_port = ntohs(si6->sin6_port);
657         *port = us_port;
658         return TRUE;
659       }
660       break;
661 #endif
662 #if defined(HAVE_SYS_UN_H) && defined(AF_UNIX)
663     case AF_UNIX:
664       if(salen > (curl_socklen_t)sizeof(CURL_SA_FAMILY_T)) {
665         su = (struct sockaddr_un*)sa;
666         msnprintf(addr, MAX_IPADR_LEN, "%s", su->sun_path);
667       }
668       else
669         addr[0] = 0; /* socket with no name */
670       *port = 0;
671       return TRUE;
672 #endif
673     default:
674       break;
675   }
676 
677   addr[0] = '\0';
678   *port = 0;
679   errno = EAFNOSUPPORT;
680   return FALSE;
681 }
682 
683 /* retrieves the start/end point information of a socket of an established
684    connection */
Curl_conninfo_remote(struct Curl_easy * data,struct connectdata * conn,curl_socket_t sockfd)685 void Curl_conninfo_remote(struct Curl_easy *data,
686                           struct connectdata *conn, curl_socket_t sockfd)
687 {
688 #ifdef HAVE_GETPEERNAME
689   char buffer[STRERROR_LEN];
690   struct Curl_sockaddr_storage ssrem;
691   curl_socklen_t plen;
692   int port;
693   plen = sizeof(struct Curl_sockaddr_storage);
694   memset(&ssrem, 0, sizeof(ssrem));
695   if(getpeername(sockfd, (struct sockaddr*) &ssrem, &plen)) {
696     int error = SOCKERRNO;
697     failf(data, "getpeername() failed with errno %d: %s",
698           error, Curl_strerror(error, buffer, sizeof(buffer)));
699     return;
700   }
701   if(!Curl_addr2string((struct sockaddr*)&ssrem, plen,
702                        conn->primary_ip, &port)) {
703     failf(data, "ssrem inet_ntop() failed with errno %d: %s",
704           errno, Curl_strerror(errno, buffer, sizeof(buffer)));
705     return;
706   }
707 #else
708   (void)data;
709   (void)conn;
710   (void)sockfd;
711 #endif
712 }
713 
714 /* retrieves the start/end point information of a socket of an established
715    connection */
Curl_conninfo_local(struct Curl_easy * data,curl_socket_t sockfd,char * local_ip,int * local_port)716 void Curl_conninfo_local(struct Curl_easy *data, curl_socket_t sockfd,
717                          char *local_ip, int *local_port)
718 {
719 #ifdef HAVE_GETSOCKNAME
720   char buffer[STRERROR_LEN];
721   struct Curl_sockaddr_storage ssloc;
722   curl_socklen_t slen;
723   slen = sizeof(struct Curl_sockaddr_storage);
724   memset(&ssloc, 0, sizeof(ssloc));
725   if(getsockname(sockfd, (struct sockaddr*) &ssloc, &slen)) {
726     int error = SOCKERRNO;
727     failf(data, "getsockname() failed with errno %d: %s",
728           error, Curl_strerror(error, buffer, sizeof(buffer)));
729     return;
730   }
731   if(!Curl_addr2string((struct sockaddr*)&ssloc, slen,
732                        local_ip, local_port)) {
733     failf(data, "ssloc inet_ntop() failed with errno %d: %s",
734           errno, Curl_strerror(errno, buffer, sizeof(buffer)));
735     return;
736   }
737 #else
738   (void)data;
739   (void)sockfd;
740   (void)local_ip;
741   (void)local_port;
742 #endif
743 }
744 
745 /* retrieves the start/end point information of a socket of an established
746    connection */
Curl_updateconninfo(struct Curl_easy * data,struct connectdata * conn,curl_socket_t sockfd)747 void Curl_updateconninfo(struct Curl_easy *data, struct connectdata *conn,
748                          curl_socket_t sockfd)
749 {
750   /* 'local_ip' and 'local_port' get filled with local's numerical
751      ip address and port number whenever an outgoing connection is
752      **established** from the primary socket to a remote address. */
753   char local_ip[MAX_IPADR_LEN] = "";
754   int local_port = -1;
755 
756   if(conn->transport == TRNSPRT_TCP) {
757     if(!conn->bits.reuse && !conn->bits.tcp_fastopen) {
758       Curl_conninfo_remote(data, conn, sockfd);
759       Curl_conninfo_local(data, sockfd, local_ip, &local_port);
760     }
761   } /* end of TCP-only section */
762 
763   /* persist connection info in session handle */
764   Curl_persistconninfo(data, conn, local_ip, local_port);
765 }
766 
767 /* After a TCP connection to the proxy has been verified, this function does
768    the next magic steps. If 'done' isn't set TRUE, it is not done yet and
769    must be called again.
770 
771    Note: this function's sub-functions call failf()
772 
773 */
connect_SOCKS(struct Curl_easy * data,int sockindex,bool * done)774 static CURLcode connect_SOCKS(struct Curl_easy *data, int sockindex,
775                               bool *done)
776 {
777   CURLcode result = CURLE_OK;
778 #ifndef CURL_DISABLE_PROXY
779   CURLproxycode pxresult = CURLPX_OK;
780   struct connectdata *conn = data->conn;
781   if(conn->bits.socksproxy) {
782     /* for the secondary socket (FTP), use the "connect to host"
783      * but ignore the "connect to port" (use the secondary port)
784      */
785     const char * const host =
786       conn->bits.httpproxy ?
787       conn->http_proxy.host.name :
788       conn->bits.conn_to_host ?
789       conn->conn_to_host.name :
790       sockindex == SECONDARYSOCKET ?
791       conn->secondaryhostname : conn->host.name;
792     const int port =
793       conn->bits.httpproxy ? (int)conn->http_proxy.port :
794       sockindex == SECONDARYSOCKET ? conn->secondary_port :
795       conn->bits.conn_to_port ? conn->conn_to_port :
796       conn->remote_port;
797     switch(conn->socks_proxy.proxytype) {
798     case CURLPROXY_SOCKS5:
799     case CURLPROXY_SOCKS5_HOSTNAME:
800       pxresult = Curl_SOCKS5(conn->socks_proxy.user, conn->socks_proxy.passwd,
801                              host, port, sockindex, data, done);
802       break;
803 
804     case CURLPROXY_SOCKS4:
805     case CURLPROXY_SOCKS4A:
806       pxresult = Curl_SOCKS4(conn->socks_proxy.user, host, port, sockindex,
807                              data, done);
808       break;
809 
810     default:
811       failf(data, "unknown proxytype option given");
812       result = CURLE_COULDNT_CONNECT;
813     } /* switch proxytype */
814     if(pxresult) {
815       result = CURLE_PROXY;
816       data->info.pxcode = pxresult;
817     }
818   }
819   else
820 #else
821     (void)data;
822     (void)sockindex;
823 #endif /* CURL_DISABLE_PROXY */
824     *done = TRUE; /* no SOCKS proxy, so consider us connected */
825 
826   return result;
827 }
828 
829 /*
830  * post_SOCKS() is called after a successful connect to the peer, which
831  * *could* be a SOCKS proxy
832  */
post_SOCKS(struct Curl_easy * data,struct connectdata * conn,int sockindex,bool * connected)833 static void post_SOCKS(struct Curl_easy *data,
834                        struct connectdata *conn,
835                        int sockindex,
836                        bool *connected)
837 {
838   conn->bits.tcpconnect[sockindex] = TRUE;
839 
840   *connected = TRUE;
841   if(sockindex == FIRSTSOCKET)
842     Curl_pgrsTime(data, TIMER_CONNECT); /* connect done */
843   Curl_updateconninfo(data, conn, conn->sock[sockindex]);
844   Curl_verboseconnect(data, conn);
845   data->info.numconnects++; /* to track the number of connections made */
846 }
847 
848 /*
849  * Curl_is_connected() checks if the socket has connected.
850  */
851 
Curl_is_connected(struct Curl_easy * data,struct connectdata * conn,int sockindex,bool * connected)852 CURLcode Curl_is_connected(struct Curl_easy *data,
853                            struct connectdata *conn,
854                            int sockindex,
855                            bool *connected)
856 {
857   CURLcode result = CURLE_OK;
858   timediff_t allow;
859   int error = 0;
860   struct curltime now;
861   int rc = 0;
862   unsigned int i;
863 
864   DEBUGASSERT(sockindex >= FIRSTSOCKET && sockindex <= SECONDARYSOCKET);
865 
866   *connected = FALSE; /* a very negative world view is best */
867 
868   if(conn->bits.tcpconnect[sockindex]) {
869     /* we are connected already! */
870     *connected = TRUE;
871     return CURLE_OK;
872   }
873 
874   now = Curl_now();
875 
876   if(SOCKS_STATE(conn->cnnct.state)) {
877     /* still doing SOCKS */
878     result = connect_SOCKS(data, sockindex, connected);
879     if(!result && *connected)
880       post_SOCKS(data, conn, sockindex, connected);
881     return result;
882   }
883 
884   for(i = 0; i<2; i++) {
885     const int other = i ^ 1;
886     if(conn->tempsock[i] == CURL_SOCKET_BAD)
887       continue;
888     error = 0;
889 #ifdef ENABLE_QUIC
890     if(conn->transport == TRNSPRT_QUIC) {
891       result = Curl_quic_is_connected(data, conn, i, connected);
892       if(!result && *connected) {
893         /* use this socket from now on */
894         conn->sock[sockindex] = conn->tempsock[i];
895         conn->ip_addr = conn->tempaddr[i];
896         conn->tempsock[i] = CURL_SOCKET_BAD;
897         post_SOCKS(data, conn, sockindex, connected);
898         connkeep(conn, "HTTP/3 default");
899         return CURLE_OK;
900       }
901       if(result) {
902         conn->tempsock[i] = CURL_SOCKET_BAD;
903         error = SOCKERRNO;
904       }
905     }
906     else
907 #endif
908     {
909 #ifdef mpeix
910       /* Call this function once now, and ignore the results. We do this to
911          "clear" the error state on the socket so that we can later read it
912          reliably. This is reported necessary on the MPE/iX operating
913          system. */
914       (void)verifyconnect(conn->tempsock[i], NULL);
915 #endif
916 
917       /* check socket for connect */
918       rc = SOCKET_WRITABLE(conn->tempsock[i], 0);
919     }
920 
921     if(rc == 0) { /* no connection yet */
922       if(Curl_timediff(now, conn->connecttime) >=
923          conn->timeoutms_per_addr[i]) {
924         infof(data, "After %" CURL_FORMAT_TIMEDIFF_T
925               "ms connect time, move on!", conn->timeoutms_per_addr[i]);
926         error = ETIMEDOUT;
927       }
928 
929       /* should we try another protocol family? */
930       if(i == 0 && !conn->bits.parallel_connect &&
931          (Curl_timediff(now, conn->connecttime) >=
932           data->set.happy_eyeballs_timeout)) {
933         conn->bits.parallel_connect = TRUE; /* starting now */
934         trynextip(data, conn, sockindex, 1);
935       }
936     }
937     else if(rc == CURL_CSELECT_OUT || conn->bits.tcp_fastopen) {
938       if(verifyconnect(conn->tempsock[i], &error)) {
939         /* we are connected with TCP, awesome! */
940 
941         /* use this socket from now on */
942         conn->sock[sockindex] = conn->tempsock[i];
943         conn->ip_addr = conn->tempaddr[i];
944         conn->tempsock[i] = CURL_SOCKET_BAD;
945 #ifdef ENABLE_IPV6
946         conn->bits.ipv6 = (conn->ip_addr->ai_family == AF_INET6)?TRUE:FALSE;
947 #endif
948 
949         /* close the other socket, if open */
950         if(conn->tempsock[other] != CURL_SOCKET_BAD) {
951           Curl_closesocket(data, conn, conn->tempsock[other]);
952           conn->tempsock[other] = CURL_SOCKET_BAD;
953         }
954 
955         /* see if we need to kick off any SOCKS proxy magic once we
956            connected */
957         result = connect_SOCKS(data, sockindex, connected);
958         if(result || !*connected)
959           return result;
960 
961         post_SOCKS(data, conn, sockindex, connected);
962 
963         return CURLE_OK;
964       }
965     }
966     else if(rc & CURL_CSELECT_ERR) {
967       (void)verifyconnect(conn->tempsock[i], &error);
968     }
969 
970     /*
971      * The connection failed here, we should attempt to connect to the "next
972      * address" for the given host. But first remember the latest error.
973      */
974     if(error) {
975       data->state.os_errno = error;
976       SET_SOCKERRNO(error);
977       if(conn->tempaddr[i]) {
978         CURLcode status;
979 #ifndef CURL_DISABLE_VERBOSE_STRINGS
980         char ipaddress[MAX_IPADR_LEN];
981         char buffer[STRERROR_LEN];
982         Curl_printable_address(conn->tempaddr[i], ipaddress,
983                                sizeof(ipaddress));
984         infof(data, "connect to %s port %u failed: %s",
985               ipaddress, conn->port,
986               Curl_strerror(error, buffer, sizeof(buffer)));
987 #endif
988 
989         allow = Curl_timeleft(data, &now, TRUE);
990         conn->timeoutms_per_addr[i] = conn->tempaddr[i]->ai_next == NULL ?
991           allow : allow / 2;
992         ainext(conn, i, TRUE);
993         status = trynextip(data, conn, sockindex, i);
994         if((status != CURLE_COULDNT_CONNECT) ||
995            conn->tempsock[other] == CURL_SOCKET_BAD)
996           /* the last attempt failed and no other sockets remain open */
997           result = status;
998       }
999     }
1000   }
1001 
1002   /*
1003    * Now that we've checked whether we are connected, check whether we've
1004    * already timed out.
1005    *
1006    * First figure out how long time we have left to connect */
1007 
1008   allow = Curl_timeleft(data, &now, TRUE);
1009 
1010   if(allow < 0) {
1011     /* time-out, bail out, go home */
1012     failf(data, "Connection timeout after %ld ms",
1013           Curl_timediff(now, data->progress.t_startsingle));
1014     return CURLE_OPERATION_TIMEDOUT;
1015   }
1016 
1017   if(result &&
1018      (conn->tempsock[0] == CURL_SOCKET_BAD) &&
1019      (conn->tempsock[1] == CURL_SOCKET_BAD)) {
1020     /* no more addresses to try */
1021     const char *hostname;
1022     char buffer[STRERROR_LEN];
1023 
1024     /* if the first address family runs out of addresses to try before the
1025        happy eyeball timeout, go ahead and try the next family now */
1026     result = trynextip(data, conn, sockindex, 1);
1027     if(!result)
1028       return result;
1029 
1030 #ifndef CURL_DISABLE_PROXY
1031     if(conn->bits.socksproxy)
1032       hostname = conn->socks_proxy.host.name;
1033     else if(conn->bits.httpproxy)
1034       hostname = conn->http_proxy.host.name;
1035     else
1036 #endif
1037       if(conn->bits.conn_to_host)
1038         hostname = conn->conn_to_host.name;
1039     else
1040       hostname = conn->host.name;
1041 
1042     failf(data, "Failed to connect to %s port %u after "
1043                 "%" CURL_FORMAT_TIMEDIFF_T " ms: %s",
1044         hostname, conn->port,
1045         Curl_timediff(now, data->progress.t_startsingle),
1046         Curl_strerror(error, buffer, sizeof(buffer)));
1047 
1048     Curl_quic_disconnect(data, conn, 0);
1049     Curl_quic_disconnect(data, conn, 1);
1050 
1051 #ifdef WSAETIMEDOUT
1052     if(WSAETIMEDOUT == data->state.os_errno)
1053       result = CURLE_OPERATION_TIMEDOUT;
1054 #elif defined(ETIMEDOUT)
1055     if(ETIMEDOUT == data->state.os_errno)
1056       result = CURLE_OPERATION_TIMEDOUT;
1057 #endif
1058   }
1059   else
1060     result = CURLE_OK; /* still trying */
1061 
1062   return result;
1063 }
1064 
tcpnodelay(struct Curl_easy * data,curl_socket_t sockfd)1065 static void tcpnodelay(struct Curl_easy *data, curl_socket_t sockfd)
1066 {
1067 #if defined(TCP_NODELAY)
1068   curl_socklen_t onoff = (curl_socklen_t) 1;
1069   int level = IPPROTO_TCP;
1070 #if !defined(CURL_DISABLE_VERBOSE_STRINGS)
1071   char buffer[STRERROR_LEN];
1072 #else
1073   (void) data;
1074 #endif
1075 
1076   if(setsockopt(sockfd, level, TCP_NODELAY, (void *)&onoff,
1077                 sizeof(onoff)) < 0)
1078     infof(data, "Could not set TCP_NODELAY: %s",
1079           Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
1080 #else
1081   (void)data;
1082   (void)sockfd;
1083 #endif
1084 }
1085 
1086 #ifdef SO_NOSIGPIPE
1087 /* The preferred method on Mac OS X (10.2 and later) to prevent SIGPIPEs when
1088    sending data to a dead peer (instead of relying on the 4th argument to send
1089    being MSG_NOSIGNAL). Possibly also existing and in use on other BSD
1090    systems? */
nosigpipe(struct Curl_easy * data,curl_socket_t sockfd)1091 static void nosigpipe(struct Curl_easy *data,
1092                       curl_socket_t sockfd)
1093 {
1094   int onoff = 1;
1095   if(setsockopt(sockfd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&onoff,
1096                 sizeof(onoff)) < 0) {
1097 #if !defined(CURL_DISABLE_VERBOSE_STRINGS)
1098     char buffer[STRERROR_LEN];
1099     infof(data, "Could not set SO_NOSIGPIPE: %s",
1100           Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
1101 #endif
1102   }
1103 }
1104 #else
1105 #define nosigpipe(x,y) Curl_nop_stmt
1106 #endif
1107 
1108 #ifdef USE_WINSOCK
1109 /* When you run a program that uses the Windows Sockets API, you may
1110    experience slow performance when you copy data to a TCP server.
1111 
1112    https://support.microsoft.com/kb/823764
1113 
1114    Work-around: Make the Socket Send Buffer Size Larger Than the Program Send
1115    Buffer Size
1116 
1117    The problem described in this knowledge-base is applied only to pre-Vista
1118    Windows.  Following function trying to detect OS version and skips
1119    SO_SNDBUF adjustment for Windows Vista and above.
1120 */
1121 #define DETECT_OS_NONE 0
1122 #define DETECT_OS_PREVISTA 1
1123 #define DETECT_OS_VISTA_OR_LATER 2
1124 
Curl_sndbufset(curl_socket_t sockfd)1125 void Curl_sndbufset(curl_socket_t sockfd)
1126 {
1127   int val = CURL_MAX_WRITE_SIZE + 32;
1128   int curval = 0;
1129   int curlen = sizeof(curval);
1130 
1131   static int detectOsState = DETECT_OS_NONE;
1132 
1133   if(detectOsState == DETECT_OS_NONE) {
1134     if(curlx_verify_windows_version(6, 0, PLATFORM_WINNT,
1135                                     VERSION_GREATER_THAN_EQUAL))
1136       detectOsState = DETECT_OS_VISTA_OR_LATER;
1137     else
1138       detectOsState = DETECT_OS_PREVISTA;
1139   }
1140 
1141   if(detectOsState == DETECT_OS_VISTA_OR_LATER)
1142     return;
1143 
1144   if(getsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (char *)&curval, &curlen) == 0)
1145     if(curval > val)
1146       return;
1147 
1148   setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (const char *)&val, sizeof(val));
1149 }
1150 #endif
1151 
1152 /*
1153  * singleipconnect()
1154  *
1155  * Note that even on connect fail it returns CURLE_OK, but with 'sock' set to
1156  * CURL_SOCKET_BAD. Other errors will however return proper errors.
1157  *
1158  * singleipconnect() connects to the given IP only, and it may return without
1159  * having connected.
1160  */
singleipconnect(struct Curl_easy * data,struct connectdata * conn,const struct Curl_addrinfo * ai,int tempindex)1161 static CURLcode singleipconnect(struct Curl_easy *data,
1162                                 struct connectdata *conn,
1163                                 const struct Curl_addrinfo *ai,
1164                                 int tempindex)
1165 {
1166   struct Curl_sockaddr_ex addr;
1167   int rc = -1;
1168   int error = 0;
1169   bool isconnected = FALSE;
1170   curl_socket_t sockfd;
1171   CURLcode result;
1172   char ipaddress[MAX_IPADR_LEN];
1173   int port;
1174   bool is_tcp;
1175 #ifdef TCP_FASTOPEN_CONNECT
1176   int optval = 1;
1177 #endif
1178   char buffer[STRERROR_LEN];
1179   curl_socket_t *sockp = &conn->tempsock[tempindex];
1180   *sockp = CURL_SOCKET_BAD;
1181 
1182   result = Curl_socket(data, ai, &addr, &sockfd);
1183   if(result)
1184     return result;
1185 
1186   /* store remote address and port used in this connection attempt */
1187   if(!Curl_addr2string((struct sockaddr*)&addr.sa_addr, addr.addrlen,
1188                        ipaddress, &port)) {
1189     /* malformed address or bug in inet_ntop, try next address */
1190     failf(data, "sa_addr inet_ntop() failed with errno %d: %s",
1191           errno, Curl_strerror(errno, buffer, sizeof(buffer)));
1192     Curl_closesocket(data, conn, sockfd);
1193     return CURLE_OK;
1194   }
1195   infof(data, "  Trying %s:%d...", ipaddress, port);
1196 
1197 #ifdef ENABLE_IPV6
1198   is_tcp = (addr.family == AF_INET || addr.family == AF_INET6) &&
1199     addr.socktype == SOCK_STREAM;
1200 #else
1201   is_tcp = (addr.family == AF_INET) && addr.socktype == SOCK_STREAM;
1202 #endif
1203   if(is_tcp && data->set.tcp_nodelay)
1204     tcpnodelay(data, sockfd);
1205 
1206   nosigpipe(data, sockfd);
1207 
1208   Curl_sndbufset(sockfd);
1209 
1210   if(is_tcp && data->set.tcp_keepalive)
1211     tcpkeepalive(data, sockfd);
1212 
1213   if(data->set.fsockopt) {
1214     /* activate callback for setting socket options */
1215     Curl_set_in_callback(data, true);
1216     error = data->set.fsockopt(data->set.sockopt_client,
1217                                sockfd,
1218                                CURLSOCKTYPE_IPCXN);
1219     Curl_set_in_callback(data, false);
1220 
1221     if(error == CURL_SOCKOPT_ALREADY_CONNECTED)
1222       isconnected = TRUE;
1223     else if(error) {
1224       Curl_closesocket(data, conn, sockfd); /* close the socket and bail out */
1225       return CURLE_ABORTED_BY_CALLBACK;
1226     }
1227   }
1228 
1229   /* possibly bind the local end to an IP, interface or port */
1230   if(addr.family == AF_INET
1231 #ifdef ENABLE_IPV6
1232      || addr.family == AF_INET6
1233 #endif
1234     ) {
1235     result = bindlocal(data, sockfd, addr.family,
1236                        Curl_ipv6_scope((struct sockaddr*)&addr.sa_addr));
1237     if(result) {
1238       Curl_closesocket(data, conn, sockfd); /* close socket and bail out */
1239       if(result == CURLE_UNSUPPORTED_PROTOCOL) {
1240         /* The address family is not supported on this interface.
1241            We can continue trying addresses */
1242         return CURLE_COULDNT_CONNECT;
1243       }
1244       return result;
1245     }
1246   }
1247 
1248   /* set socket non-blocking */
1249   (void)curlx_nonblock(sockfd, TRUE);
1250 
1251   conn->connecttime = Curl_now();
1252   if(conn->num_addr > 1) {
1253     Curl_expire(data, conn->timeoutms_per_addr[0], EXPIRE_DNS_PER_NAME);
1254     Curl_expire(data, conn->timeoutms_per_addr[1], EXPIRE_DNS_PER_NAME2);
1255   }
1256 
1257   /* Connect TCP and QUIC sockets */
1258   if(!isconnected && (conn->transport != TRNSPRT_UDP)) {
1259     if(conn->bits.tcp_fastopen) {
1260 #if defined(CONNECT_DATA_IDEMPOTENT) /* Darwin */
1261 #  if defined(HAVE_BUILTIN_AVAILABLE)
1262       /* while connectx function is available since macOS 10.11 / iOS 9,
1263          it did not have the interface declared correctly until
1264          Xcode 9 / macOS SDK 10.13 */
1265       if(__builtin_available(macOS 10.11, iOS 9.0, tvOS 9.0, watchOS 2.0, *)) {
1266         sa_endpoints_t endpoints;
1267         endpoints.sae_srcif = 0;
1268         endpoints.sae_srcaddr = NULL;
1269         endpoints.sae_srcaddrlen = 0;
1270         endpoints.sae_dstaddr = &addr.sa_addr;
1271         endpoints.sae_dstaddrlen = addr.addrlen;
1272 
1273         rc = connectx(sockfd, &endpoints, SAE_ASSOCID_ANY,
1274                       CONNECT_RESUME_ON_READ_WRITE | CONNECT_DATA_IDEMPOTENT,
1275                       NULL, 0, NULL, NULL);
1276       }
1277       else {
1278         rc = connect(sockfd, &addr.sa_addr, addr.addrlen);
1279       }
1280 #  else
1281       rc = connect(sockfd, &addr.sa_addr, addr.addrlen);
1282 #  endif /* HAVE_BUILTIN_AVAILABLE */
1283 #elif defined(TCP_FASTOPEN_CONNECT) /* Linux >= 4.11 */
1284       if(setsockopt(sockfd, IPPROTO_TCP, TCP_FASTOPEN_CONNECT,
1285                     (void *)&optval, sizeof(optval)) < 0)
1286         infof(data, "Failed to enable TCP Fast Open on fd %d", sockfd);
1287 
1288       rc = connect(sockfd, &addr.sa_addr, addr.addrlen);
1289 #elif defined(MSG_FASTOPEN) /* old Linux */
1290       if(conn->given->flags & PROTOPT_SSL)
1291         rc = connect(sockfd, &addr.sa_addr, addr.addrlen);
1292       else
1293         rc = 0; /* Do nothing */
1294 #endif
1295     }
1296     else {
1297       rc = connect(sockfd, &addr.sa_addr, addr.addrlen);
1298     }
1299 
1300     if(-1 == rc)
1301       error = SOCKERRNO;
1302 #ifdef ENABLE_QUIC
1303     else if(conn->transport == TRNSPRT_QUIC) {
1304       /* pass in 'sockfd' separately since it hasn't been put into the
1305          tempsock array at this point */
1306       result = Curl_quic_connect(data, conn, sockfd, tempindex,
1307                                  &addr.sa_addr, addr.addrlen);
1308       if(result)
1309         error = SOCKERRNO;
1310     }
1311 #endif
1312   }
1313   else {
1314     *sockp = sockfd;
1315     return CURLE_OK;
1316   }
1317 
1318   if(-1 == rc) {
1319     switch(error) {
1320     case EINPROGRESS:
1321     case EWOULDBLOCK:
1322 #if defined(EAGAIN)
1323 #if (EAGAIN) != (EWOULDBLOCK)
1324       /* On some platforms EAGAIN and EWOULDBLOCK are the
1325        * same value, and on others they are different, hence
1326        * the odd #if
1327        */
1328     case EAGAIN:
1329 #endif
1330 #endif
1331       result = CURLE_OK;
1332       break;
1333 
1334     default:
1335       /* unknown error, fallthrough and try another address! */
1336       infof(data, "Immediate connect fail for %s: %s",
1337             ipaddress, Curl_strerror(error, buffer, sizeof(buffer)));
1338       data->state.os_errno = error;
1339 
1340       /* connect failed */
1341       Curl_closesocket(data, conn, sockfd);
1342       result = CURLE_COULDNT_CONNECT;
1343     }
1344   }
1345 
1346   if(!result)
1347     *sockp = sockfd;
1348 
1349   return result;
1350 }
1351 
1352 /*
1353  * TCP connect to the given host with timeout, proxy or remote doesn't matter.
1354  * There might be more than one IP address to try out. Fill in the passed
1355  * pointer with the connected socket.
1356  */
1357 
Curl_connecthost(struct Curl_easy * data,struct connectdata * conn,const struct Curl_dns_entry * remotehost)1358 CURLcode Curl_connecthost(struct Curl_easy *data,
1359                           struct connectdata *conn,  /* context */
1360                           const struct Curl_dns_entry *remotehost)
1361 {
1362   CURLcode result = CURLE_COULDNT_CONNECT;
1363   int i;
1364   timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE);
1365 
1366   if(timeout_ms < 0) {
1367     /* a precaution, no need to continue if time already is up */
1368     failf(data, "Connection time-out");
1369     return CURLE_OPERATION_TIMEDOUT;
1370   }
1371 
1372   conn->num_addr = Curl_num_addresses(remotehost->addr);
1373   conn->tempaddr[0] = conn->tempaddr[1] = remotehost->addr;
1374   conn->tempsock[0] = conn->tempsock[1] = CURL_SOCKET_BAD;
1375 
1376   /* Max time for the next connection attempt */
1377   conn->timeoutms_per_addr[0] =
1378     conn->tempaddr[0]->ai_next == NULL ? timeout_ms : timeout_ms / 2;
1379   conn->timeoutms_per_addr[1] =
1380     conn->tempaddr[1]->ai_next == NULL ? timeout_ms : timeout_ms / 2;
1381 
1382   if(conn->ip_version == CURL_IPRESOLVE_WHATEVER) {
1383     /* any IP version is allowed */
1384     conn->tempfamily[0] = conn->tempaddr[0]?
1385       conn->tempaddr[0]->ai_family:0;
1386 #ifdef ENABLE_IPV6
1387     conn->tempfamily[1] = conn->tempfamily[0] == AF_INET6 ?
1388       AF_INET : AF_INET6;
1389 #else
1390     conn->tempfamily[1] = AF_UNSPEC;
1391 #endif
1392   }
1393   else {
1394     /* only one IP version is allowed */
1395     conn->tempfamily[0] = (conn->ip_version == CURL_IPRESOLVE_V4) ?
1396       AF_INET :
1397 #ifdef ENABLE_IPV6
1398       AF_INET6;
1399 #else
1400       AF_UNSPEC;
1401 #endif
1402     conn->tempfamily[1] = AF_UNSPEC;
1403 
1404     ainext(conn, 0, FALSE); /* find first address of the right type */
1405   }
1406 
1407   ainext(conn, 1, FALSE); /* assigns conn->tempaddr[1] accordingly */
1408 
1409   DEBUGF(infof(data, "family0 == %s, family1 == %s",
1410                conn->tempfamily[0] == AF_INET ? "v4" : "v6",
1411                conn->tempfamily[1] == AF_INET ? "v4" : "v6"));
1412 
1413   /* get through the list in family order in case of quick failures */
1414   for(i = 0; (i < 2) && result; i++) {
1415     while(conn->tempaddr[i]) {
1416       result = singleipconnect(data, conn, conn->tempaddr[i], i);
1417       if(!result)
1418         break;
1419       ainext(conn, i, TRUE);
1420     }
1421   }
1422   if(result)
1423     return result;
1424 
1425   Curl_expire(data, data->set.happy_eyeballs_timeout,
1426               EXPIRE_HAPPY_EYEBALLS);
1427 
1428   return CURLE_OK;
1429 }
1430 
1431 struct connfind {
1432   long id_tofind;
1433   struct connectdata *found;
1434 };
1435 
conn_is_conn(struct Curl_easy * data,struct connectdata * conn,void * param)1436 static int conn_is_conn(struct Curl_easy *data,
1437                         struct connectdata *conn, void *param)
1438 {
1439   struct connfind *f = (struct connfind *)param;
1440   (void)data;
1441   if(conn->connection_id == f->id_tofind) {
1442     f->found = conn;
1443     return 1;
1444   }
1445   return 0;
1446 }
1447 
1448 /*
1449  * Used to extract socket and connectdata struct for the most recent
1450  * transfer on the given Curl_easy.
1451  *
1452  * The returned socket will be CURL_SOCKET_BAD in case of failure!
1453  */
Curl_getconnectinfo(struct Curl_easy * data,struct connectdata ** connp)1454 curl_socket_t Curl_getconnectinfo(struct Curl_easy *data,
1455                                   struct connectdata **connp)
1456 {
1457   DEBUGASSERT(data);
1458 
1459   /* this works for an easy handle:
1460    * - that has been used for curl_easy_perform()
1461    * - that is associated with a multi handle, and whose connection
1462    *   was detached with CURLOPT_CONNECT_ONLY
1463    */
1464   if((data->state.lastconnect_id != -1) && (data->multi_easy || data->multi)) {
1465     struct connectdata *c;
1466     struct connfind find;
1467     find.id_tofind = data->state.lastconnect_id;
1468     find.found = NULL;
1469 
1470     Curl_conncache_foreach(data, data->multi_easy?
1471                            &data->multi_easy->conn_cache:
1472                            &data->multi->conn_cache, &find, conn_is_conn);
1473 
1474     if(!find.found) {
1475       data->state.lastconnect_id = -1;
1476       return CURL_SOCKET_BAD;
1477     }
1478 
1479     c = find.found;
1480     if(connp)
1481       /* only store this if the caller cares for it */
1482       *connp = c;
1483     return c->sock[FIRSTSOCKET];
1484   }
1485   return CURL_SOCKET_BAD;
1486 }
1487 
1488 /*
1489  * Check if a connection seems to be alive.
1490  */
Curl_connalive(struct connectdata * conn)1491 bool Curl_connalive(struct connectdata *conn)
1492 {
1493   /* First determine if ssl */
1494   if(conn->ssl[FIRSTSOCKET].use) {
1495     /* use the SSL context */
1496     if(!Curl_ssl_check_cxn(conn))
1497       return false;   /* FIN received */
1498   }
1499 /* Minix 3.1 doesn't support any flags on recv; just assume socket is OK */
1500 #ifdef MSG_PEEK
1501   else if(conn->sock[FIRSTSOCKET] == CURL_SOCKET_BAD)
1502     return false;
1503   else {
1504     /* use the socket */
1505     char buf;
1506     if(recv((RECV_TYPE_ARG1)conn->sock[FIRSTSOCKET], (RECV_TYPE_ARG2)&buf,
1507             (RECV_TYPE_ARG3)1, (RECV_TYPE_ARG4)MSG_PEEK) == 0) {
1508       return false;   /* FIN received */
1509     }
1510   }
1511 #endif
1512   return true;
1513 }
1514 
1515 /*
1516  * Close a socket.
1517  *
1518  * 'conn' can be NULL, beware!
1519  */
Curl_closesocket(struct Curl_easy * data,struct connectdata * conn,curl_socket_t sock)1520 int Curl_closesocket(struct Curl_easy *data, struct connectdata *conn,
1521                      curl_socket_t sock)
1522 {
1523   if(conn && conn->fclosesocket) {
1524     if((sock == conn->sock[SECONDARYSOCKET]) && conn->bits.sock_accepted)
1525       /* if this socket matches the second socket, and that was created with
1526          accept, then we MUST NOT call the callback but clear the accepted
1527          status */
1528       conn->bits.sock_accepted = FALSE;
1529     else {
1530       int rc;
1531       Curl_multi_closed(data, sock);
1532       Curl_set_in_callback(data, true);
1533       rc = conn->fclosesocket(conn->closesocket_client, sock);
1534       Curl_set_in_callback(data, false);
1535       return rc;
1536     }
1537   }
1538 
1539   if(conn)
1540     /* tell the multi-socket code about this */
1541     Curl_multi_closed(data, sock);
1542 
1543   sclose(sock);
1544 
1545   return 0;
1546 }
1547 
1548 /*
1549  * Create a socket based on info from 'conn' and 'ai'.
1550  *
1551  * 'addr' should be a pointer to the correct struct to get data back, or NULL.
1552  * 'sockfd' must be a pointer to a socket descriptor.
1553  *
1554  * If the open socket callback is set, used that!
1555  *
1556  */
Curl_socket(struct Curl_easy * data,const struct Curl_addrinfo * ai,struct Curl_sockaddr_ex * addr,curl_socket_t * sockfd)1557 CURLcode Curl_socket(struct Curl_easy *data,
1558                      const struct Curl_addrinfo *ai,
1559                      struct Curl_sockaddr_ex *addr,
1560                      curl_socket_t *sockfd)
1561 {
1562   struct connectdata *conn = data->conn;
1563   struct Curl_sockaddr_ex dummy;
1564 
1565   if(!addr)
1566     /* if the caller doesn't want info back, use a local temp copy */
1567     addr = &dummy;
1568 
1569   /*
1570    * The Curl_sockaddr_ex structure is basically libcurl's external API
1571    * curl_sockaddr structure with enough space available to directly hold
1572    * any protocol-specific address structures. The variable declared here
1573    * will be used to pass / receive data to/from the fopensocket callback
1574    * if this has been set, before that, it is initialized from parameters.
1575    */
1576 
1577   addr->family = ai->ai_family;
1578   addr->socktype = (conn->transport == TRNSPRT_TCP) ? SOCK_STREAM : SOCK_DGRAM;
1579   addr->protocol = conn->transport != TRNSPRT_TCP ? IPPROTO_UDP :
1580     ai->ai_protocol;
1581   addr->addrlen = ai->ai_addrlen;
1582 
1583   if(addr->addrlen > sizeof(struct Curl_sockaddr_storage))
1584      addr->addrlen = sizeof(struct Curl_sockaddr_storage);
1585   memcpy(&addr->sa_addr, ai->ai_addr, addr->addrlen);
1586 
1587   if(data->set.fopensocket) {
1588    /*
1589     * If the opensocket callback is set, all the destination address
1590     * information is passed to the callback. Depending on this information the
1591     * callback may opt to abort the connection, this is indicated returning
1592     * CURL_SOCKET_BAD; otherwise it will return a not-connected socket. When
1593     * the callback returns a valid socket the destination address information
1594     * might have been changed and this 'new' address will actually be used
1595     * here to connect.
1596     */
1597     Curl_set_in_callback(data, true);
1598     *sockfd = data->set.fopensocket(data->set.opensocket_client,
1599                                     CURLSOCKTYPE_IPCXN,
1600                                     (struct curl_sockaddr *)addr);
1601     Curl_set_in_callback(data, false);
1602   }
1603   else
1604     /* opensocket callback not set, so simply create the socket now */
1605     *sockfd = socket(addr->family, addr->socktype, addr->protocol);
1606 
1607   if(*sockfd == CURL_SOCKET_BAD)
1608     /* no socket, no connection */
1609     return CURLE_COULDNT_CONNECT;
1610 
1611   if(conn->transport == TRNSPRT_QUIC) {
1612     /* QUIC sockets need to be nonblocking */
1613     (void)curlx_nonblock(*sockfd, TRUE);
1614   }
1615 
1616 #if defined(ENABLE_IPV6) && defined(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID)
1617   if(conn->scope_id && (addr->family == AF_INET6)) {
1618     struct sockaddr_in6 * const sa6 = (void *)&addr->sa_addr;
1619     sa6->sin6_scope_id = conn->scope_id;
1620   }
1621 #endif
1622 
1623 #if defined(__linux__) && defined(IP_RECVERR)
1624   if(addr->socktype == SOCK_DGRAM) {
1625     int one = 1;
1626     switch(addr->family) {
1627     case AF_INET:
1628       (void)setsockopt(*sockfd, SOL_IP, IP_RECVERR, &one, sizeof(one));
1629       break;
1630     case AF_INET6:
1631       (void)setsockopt(*sockfd, SOL_IPV6, IPV6_RECVERR, &one, sizeof(one));
1632       break;
1633     }
1634   }
1635 #endif
1636 
1637   return CURLE_OK;
1638 }
1639 
1640 /*
1641  * Curl_conncontrol() marks streams or connection for closure.
1642  */
Curl_conncontrol(struct connectdata * conn,int ctrl,const char * reason)1643 void Curl_conncontrol(struct connectdata *conn,
1644                       int ctrl /* see defines in header */
1645 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
1646                       , const char *reason
1647 #endif
1648   )
1649 {
1650   /* close if a connection, or a stream that isn't multiplexed. */
1651   /* This function will be called both before and after this connection is
1652      associated with a transfer. */
1653   bool closeit;
1654   DEBUGASSERT(conn);
1655 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
1656   (void)reason; /* useful for debugging */
1657 #endif
1658   closeit = (ctrl == CONNCTRL_CONNECTION) ||
1659     ((ctrl == CONNCTRL_STREAM) && !(conn->handler->flags & PROTOPT_STREAM));
1660   if((ctrl == CONNCTRL_STREAM) &&
1661      (conn->handler->flags & PROTOPT_STREAM))
1662     ;
1663   else if((bit)closeit != conn->bits.close) {
1664     conn->bits.close = closeit; /* the only place in the source code that
1665                                    should assign this bit */
1666   }
1667 }
1668 
1669 /* Data received can be cached at various levels, so check them all here. */
Curl_conn_data_pending(struct connectdata * conn,int sockindex)1670 bool Curl_conn_data_pending(struct connectdata *conn, int sockindex)
1671 {
1672   int readable;
1673   DEBUGASSERT(conn);
1674 
1675   if(Curl_ssl_data_pending(conn, sockindex) ||
1676      Curl_recv_has_postponed_data(conn, sockindex))
1677     return true;
1678 
1679   readable = SOCKET_READABLE(conn->sock[sockindex], 0);
1680   return (readable > 0 && (readable & CURL_CSELECT_IN));
1681 }
1682