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