• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 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  * SPDX-License-Identifier: curl
22  *
23  ***************************************************************************/
24 
25 #include "curl_setup.h"
26 
27 #ifdef HAVE_NETINET_IN_H
28 #include <netinet/in.h> /* <netinet/tcp.h> may need it */
29 #endif
30 #ifdef HAVE_SYS_UN_H
31 #include <sys/un.h> /* for sockaddr_un */
32 #endif
33 #ifdef HAVE_LINUX_TCP_H
34 #include <linux/tcp.h>
35 #elif defined(HAVE_NETINET_TCP_H)
36 #include <netinet/tcp.h>
37 #endif
38 #ifdef HAVE_SYS_IOCTL_H
39 #include <sys/ioctl.h>
40 #endif
41 #ifdef HAVE_NETDB_H
42 #include <netdb.h>
43 #endif
44 #ifdef HAVE_FCNTL_H
45 #include <fcntl.h>
46 #endif
47 #ifdef HAVE_ARPA_INET_H
48 #include <arpa/inet.h>
49 #endif
50 
51 #ifdef __VMS
52 #include <in.h>
53 #include <inet.h>
54 #endif
55 
56 #include "urldata.h"
57 #include "bufq.h"
58 #include "sendf.h"
59 #include "if2ip.h"
60 #include "strerror.h"
61 #include "cfilters.h"
62 #include "cf-socket.h"
63 #include "connect.h"
64 #include "select.h"
65 #include "url.h" /* for Curl_safefree() */
66 #include "multiif.h"
67 #include "sockaddr.h" /* required for Curl_sockaddr_storage */
68 #include "inet_ntop.h"
69 #include "inet_pton.h"
70 #include "progress.h"
71 #include "warnless.h"
72 #include "conncache.h"
73 #include "multihandle.h"
74 #include "rand.h"
75 #include "share.h"
76 #include "version_win32.h"
77 
78 /* The last 3 #include files should be in this order */
79 #include "curl_printf.h"
80 #include "curl_memory.h"
81 #include "memdebug.h"
82 
83 
84 #if defined(USE_IPV6) && defined(IPV6_V6ONLY) && defined(_WIN32)
85 /* It makes support for IPv4-mapped IPv6 addresses.
86  * Linux kernel, NetBSD, FreeBSD and Darwin: default is off;
87  * Windows Vista and later: default is on;
88  * DragonFly BSD: acts like off, and dummy setting;
89  * OpenBSD and earlier Windows: unsupported.
90  * Linux: controlled by /proc/sys/net/ipv6/bindv6only.
91  */
set_ipv6_v6only(curl_socket_t sockfd,int on)92 static void set_ipv6_v6only(curl_socket_t sockfd, int on)
93 {
94   (void)setsockopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&on, sizeof(on));
95 }
96 #else
97 #define set_ipv6_v6only(x,y)
98 #endif
99 
tcpnodelay(struct Curl_easy * data,curl_socket_t sockfd)100 static void tcpnodelay(struct Curl_easy *data, curl_socket_t sockfd)
101 {
102 #if defined(TCP_NODELAY)
103   curl_socklen_t onoff = (curl_socklen_t) 1;
104   int level = IPPROTO_TCP;
105   char buffer[STRERROR_LEN];
106 
107   if(setsockopt(sockfd, level, TCP_NODELAY, (void *)&onoff,
108                 sizeof(onoff)) < 0)
109     infof(data, "Could not set TCP_NODELAY: %s",
110           Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
111 #else
112   (void)data;
113   (void)sockfd;
114 #endif
115 }
116 
117 #ifdef SO_NOSIGPIPE
118 /* The preferred method on Mac OS X (10.2 and later) to prevent SIGPIPEs when
119    sending data to a dead peer (instead of relying on the 4th argument to send
120    being MSG_NOSIGNAL). Possibly also existing and in use on other BSD
121    systems? */
nosigpipe(struct Curl_easy * data,curl_socket_t sockfd)122 static void nosigpipe(struct Curl_easy *data,
123                       curl_socket_t sockfd)
124 {
125   int onoff = 1;
126   (void)data;
127   if(setsockopt(sockfd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&onoff,
128                 sizeof(onoff)) < 0) {
129 #if !defined(CURL_DISABLE_VERBOSE_STRINGS)
130     char buffer[STRERROR_LEN];
131     infof(data, "Could not set SO_NOSIGPIPE: %s",
132           Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
133 #endif
134   }
135 }
136 #else
137 #define nosigpipe(x,y) Curl_nop_stmt
138 #endif
139 
140 #if defined(__DragonFly__) || defined(USE_WINSOCK)
141 /* DragonFlyBSD and Windows use millisecond units */
142 #define KEEPALIVE_FACTOR(x) (x *= 1000)
143 #else
144 #define KEEPALIVE_FACTOR(x)
145 #endif
146 
147 #if defined(USE_WINSOCK) && !defined(SIO_KEEPALIVE_VALS)
148 #define SIO_KEEPALIVE_VALS    _WSAIOW(IOC_VENDOR,4)
149 
150 struct tcp_keepalive {
151   u_long onoff;
152   u_long keepalivetime;
153   u_long keepaliveinterval;
154 };
155 #endif
156 
157 static void
tcpkeepalive(struct Curl_easy * data,curl_socket_t sockfd)158 tcpkeepalive(struct Curl_easy *data,
159              curl_socket_t sockfd)
160 {
161   int optval = data->set.tcp_keepalive?1:0;
162 
163   /* only set IDLE and INTVL if setting KEEPALIVE is successful */
164   if(setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE,
165         (void *)&optval, sizeof(optval)) < 0) {
166     infof(data, "Failed to set SO_KEEPALIVE on fd "
167           "%" CURL_FORMAT_SOCKET_T ": errno %d",
168           sockfd, SOCKERRNO);
169   }
170   else {
171 #if defined(SIO_KEEPALIVE_VALS)
172     struct tcp_keepalive vals;
173     DWORD dummy;
174     vals.onoff = 1;
175     optval = curlx_sltosi(data->set.tcp_keepidle);
176     KEEPALIVE_FACTOR(optval);
177     vals.keepalivetime = optval;
178     optval = curlx_sltosi(data->set.tcp_keepintvl);
179     KEEPALIVE_FACTOR(optval);
180     vals.keepaliveinterval = optval;
181     if(WSAIoctl(sockfd, SIO_KEEPALIVE_VALS, (LPVOID) &vals, sizeof(vals),
182                 NULL, 0, &dummy, NULL, NULL) != 0) {
183       infof(data, "Failed to set SIO_KEEPALIVE_VALS on fd "
184                   "%" CURL_FORMAT_SOCKET_T ": errno %d",
185                   sockfd, SOCKERRNO);
186     }
187 #else
188 #ifdef TCP_KEEPIDLE
189     optval = curlx_sltosi(data->set.tcp_keepidle);
190     KEEPALIVE_FACTOR(optval);
191     if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPIDLE,
192           (void *)&optval, sizeof(optval)) < 0) {
193       infof(data, "Failed to set TCP_KEEPIDLE on fd "
194             "%" CURL_FORMAT_SOCKET_T ": errno %d",
195             sockfd, SOCKERRNO);
196     }
197 #elif defined(TCP_KEEPALIVE)
198     /* Mac OS X style */
199     optval = curlx_sltosi(data->set.tcp_keepidle);
200     KEEPALIVE_FACTOR(optval);
201     if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPALIVE,
202       (void *)&optval, sizeof(optval)) < 0) {
203       infof(data, "Failed to set TCP_KEEPALIVE on fd "
204             "%" CURL_FORMAT_SOCKET_T ": errno %d",
205             sockfd, SOCKERRNO);
206     }
207 #endif
208 #ifdef TCP_KEEPINTVL
209     optval = curlx_sltosi(data->set.tcp_keepintvl);
210     KEEPALIVE_FACTOR(optval);
211     if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPINTVL,
212           (void *)&optval, sizeof(optval)) < 0) {
213       infof(data, "Failed to set TCP_KEEPINTVL on fd "
214             "%" CURL_FORMAT_SOCKET_T ": errno %d",
215             sockfd, SOCKERRNO);
216     }
217 #endif
218 #endif
219   }
220 }
221 
222 /**
223  * Assign the address `ai` to the Curl_sockaddr_ex `dest` and
224  * set the transport used.
225  */
Curl_sock_assign_addr(struct Curl_sockaddr_ex * dest,const struct Curl_addrinfo * ai,int transport)226 void Curl_sock_assign_addr(struct Curl_sockaddr_ex *dest,
227                            const struct Curl_addrinfo *ai,
228                            int transport)
229 {
230   /*
231    * The Curl_sockaddr_ex structure is basically libcurl's external API
232    * curl_sockaddr structure with enough space available to directly hold
233    * any protocol-specific address structures. The variable declared here
234    * will be used to pass / receive data to/from the fopensocket callback
235    * if this has been set, before that, it is initialized from parameters.
236    */
237   dest->family = ai->ai_family;
238   switch(transport) {
239   case TRNSPRT_TCP:
240     dest->socktype = SOCK_STREAM;
241     dest->protocol = IPPROTO_TCP;
242     break;
243   case TRNSPRT_UNIX:
244     dest->socktype = SOCK_STREAM;
245     dest->protocol = IPPROTO_IP;
246     break;
247   default: /* UDP and QUIC */
248     dest->socktype = SOCK_DGRAM;
249     dest->protocol = IPPROTO_UDP;
250     break;
251   }
252   dest->addrlen = ai->ai_addrlen;
253 
254   if(dest->addrlen > sizeof(struct Curl_sockaddr_storage))
255     dest->addrlen = sizeof(struct Curl_sockaddr_storage);
256   memcpy(&dest->sa_addr, ai->ai_addr, dest->addrlen);
257 }
258 
socket_open(struct Curl_easy * data,struct Curl_sockaddr_ex * addr,curl_socket_t * sockfd)259 static CURLcode socket_open(struct Curl_easy *data,
260                             struct Curl_sockaddr_ex *addr,
261                             curl_socket_t *sockfd)
262 {
263   DEBUGASSERT(data);
264   DEBUGASSERT(data->conn);
265   if(data->set.fopensocket) {
266    /*
267     * If the opensocket callback is set, all the destination address
268     * information is passed to the callback. Depending on this information the
269     * callback may opt to abort the connection, this is indicated returning
270     * CURL_SOCKET_BAD; otherwise it will return a not-connected socket. When
271     * the callback returns a valid socket the destination address information
272     * might have been changed and this 'new' address will actually be used
273     * here to connect.
274     */
275     Curl_set_in_callback(data, true);
276     *sockfd = data->set.fopensocket(data->set.opensocket_client,
277                                     CURLSOCKTYPE_IPCXN,
278                                     (struct curl_sockaddr *)addr);
279     Curl_set_in_callback(data, false);
280   }
281   else {
282     /* opensocket callback not set, so simply create the socket now */
283     *sockfd = socket(addr->family, addr->socktype, addr->protocol);
284   }
285 
286   if(*sockfd == CURL_SOCKET_BAD)
287     /* no socket, no connection */
288     return CURLE_COULDNT_CONNECT;
289 
290 #if defined(USE_IPV6) && defined(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID)
291   if(data->conn->scope_id && (addr->family == AF_INET6)) {
292     struct sockaddr_in6 * const sa6 = (void *)&addr->sa_addr;
293     sa6->sin6_scope_id = data->conn->scope_id;
294   }
295 #endif
296   return CURLE_OK;
297 }
298 
299 /*
300  * Create a socket based on info from 'conn' and 'ai'.
301  *
302  * 'addr' should be a pointer to the correct struct to get data back, or NULL.
303  * 'sockfd' must be a pointer to a socket descriptor.
304  *
305  * If the open socket callback is set, used that!
306  *
307  */
Curl_socket_open(struct Curl_easy * data,const struct Curl_addrinfo * ai,struct Curl_sockaddr_ex * addr,int transport,curl_socket_t * sockfd)308 CURLcode Curl_socket_open(struct Curl_easy *data,
309                             const struct Curl_addrinfo *ai,
310                             struct Curl_sockaddr_ex *addr,
311                             int transport,
312                             curl_socket_t *sockfd)
313 {
314   struct Curl_sockaddr_ex dummy;
315 
316   if(!addr)
317     /* if the caller doesn't want info back, use a local temp copy */
318     addr = &dummy;
319 
320   Curl_sock_assign_addr(addr, ai, transport);
321   return socket_open(data, addr, sockfd);
322 }
323 
socket_close(struct Curl_easy * data,struct connectdata * conn,int use_callback,curl_socket_t sock)324 static int socket_close(struct Curl_easy *data, struct connectdata *conn,
325                         int use_callback, curl_socket_t sock)
326 {
327   if(use_callback && conn && conn->fclosesocket) {
328     int rc;
329     Curl_multi_closed(data, sock);
330     Curl_set_in_callback(data, true);
331     rc = conn->fclosesocket(conn->closesocket_client, sock);
332     Curl_set_in_callback(data, false);
333     return rc;
334   }
335 
336   if(conn)
337     /* tell the multi-socket code about this */
338     Curl_multi_closed(data, sock);
339 
340   sclose(sock);
341 
342   return 0;
343 }
344 
345 /*
346  * Close a socket.
347  *
348  * 'conn' can be NULL, beware!
349  */
Curl_socket_close(struct Curl_easy * data,struct connectdata * conn,curl_socket_t sock)350 int Curl_socket_close(struct Curl_easy *data, struct connectdata *conn,
351                       curl_socket_t sock)
352 {
353   return socket_close(data, conn, FALSE, sock);
354 }
355 
356 #ifdef USE_WINSOCK
357 /* When you run a program that uses the Windows Sockets API, you may
358    experience slow performance when you copy data to a TCP server.
359 
360    https://support.microsoft.com/kb/823764
361 
362    Work-around: Make the Socket Send Buffer Size Larger Than the Program Send
363    Buffer Size
364 
365    The problem described in this knowledge-base is applied only to pre-Vista
366    Windows.  Following function trying to detect OS version and skips
367    SO_SNDBUF adjustment for Windows Vista and above.
368 */
369 #define DETECT_OS_NONE 0
370 #define DETECT_OS_PREVISTA 1
371 #define DETECT_OS_VISTA_OR_LATER 2
372 
Curl_sndbufset(curl_socket_t sockfd)373 void Curl_sndbufset(curl_socket_t sockfd)
374 {
375   int val = CURL_MAX_WRITE_SIZE + 32;
376   int curval = 0;
377   int curlen = sizeof(curval);
378 
379   static int detectOsState = DETECT_OS_NONE;
380 
381   if(detectOsState == DETECT_OS_NONE) {
382     if(curlx_verify_windows_version(6, 0, 0, PLATFORM_WINNT,
383                                     VERSION_GREATER_THAN_EQUAL))
384       detectOsState = DETECT_OS_VISTA_OR_LATER;
385     else
386       detectOsState = DETECT_OS_PREVISTA;
387   }
388 
389   if(detectOsState == DETECT_OS_VISTA_OR_LATER)
390     return;
391 
392   if(getsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (char *)&curval, &curlen) == 0)
393     if(curval > val)
394       return;
395 
396   setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (const char *)&val, sizeof(val));
397 }
398 #endif
399 
400 #ifndef CURL_DISABLE_BINDLOCAL
bindlocal(struct Curl_easy * data,struct connectdata * conn,curl_socket_t sockfd,int af,unsigned int scope)401 static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn,
402                           curl_socket_t sockfd, int af, unsigned int scope)
403 {
404   struct Curl_sockaddr_storage sa;
405   struct sockaddr *sock = (struct sockaddr *)&sa;  /* bind to this address */
406   curl_socklen_t sizeof_sa = 0; /* size of the data sock points to */
407   struct sockaddr_in *si4 = (struct sockaddr_in *)&sa;
408 #ifdef USE_IPV6
409   struct sockaddr_in6 *si6 = (struct sockaddr_in6 *)&sa;
410 #endif
411 
412   struct Curl_dns_entry *h = NULL;
413   unsigned short port = data->set.localport; /* use this port number, 0 for
414                                                 "random" */
415   /* how many port numbers to try to bind to, increasing one at a time */
416   int portnum = data->set.localportrange;
417   const char *dev = data->set.str[STRING_DEVICE];
418   int error;
419 #ifdef IP_BIND_ADDRESS_NO_PORT
420   int on = 1;
421 #endif
422 #ifndef USE_IPV6
423   (void)scope;
424 #endif
425 
426   /*************************************************************
427    * Select device to bind socket to
428    *************************************************************/
429   if(!dev && !port)
430     /* no local kind of binding was requested */
431     return CURLE_OK;
432 
433   memset(&sa, 0, sizeof(struct Curl_sockaddr_storage));
434 
435   if(dev && (strlen(dev)<255) ) {
436     char myhost[256] = "";
437     int done = 0; /* -1 for error, 1 for address found */
438     bool is_interface = FALSE;
439     bool is_host = FALSE;
440     static const char *if_prefix = "if!";
441     static const char *host_prefix = "host!";
442 
443     if(strncmp(if_prefix, dev, strlen(if_prefix)) == 0) {
444       dev += strlen(if_prefix);
445       is_interface = TRUE;
446     }
447     else if(strncmp(host_prefix, dev, strlen(host_prefix)) == 0) {
448       dev += strlen(host_prefix);
449       is_host = TRUE;
450     }
451 
452     /* interface */
453     if(!is_host) {
454 #ifdef SO_BINDTODEVICE
455       /*
456        * This binds the local socket to a particular interface. This will
457        * force even requests to other local interfaces to go out the external
458        * interface. Only bind to the interface when specified as interface,
459        * not just as a hostname or ip address.
460        *
461        * The interface might be a VRF, eg: vrf-blue, which means it cannot be
462        * converted to an IP address and would fail Curl_if2ip. Simply try to
463        * use it straight away.
464        */
465       if(setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE,
466                     dev, (curl_socklen_t)strlen(dev) + 1) == 0) {
467         /* This is often "errno 1, error: Operation not permitted" if you're
468          * not running as root or another suitable privileged user. If it
469          * succeeds it means the parameter was a valid interface and not an IP
470          * address. Return immediately.
471          */
472         infof(data, "socket successfully bound to interface '%s'", dev);
473         return CURLE_OK;
474       }
475 #endif
476 
477       switch(Curl_if2ip(af,
478 #ifdef USE_IPV6
479                         scope, conn->scope_id,
480 #endif
481                         dev, myhost, sizeof(myhost))) {
482         case IF2IP_NOT_FOUND:
483           if(is_interface) {
484             /* Do not fall back to treating it as a host name */
485             failf(data, "Couldn't bind to interface '%s'", dev);
486             return CURLE_INTERFACE_FAILED;
487           }
488           break;
489         case IF2IP_AF_NOT_SUPPORTED:
490           /* Signal the caller to try another address family if available */
491           return CURLE_UNSUPPORTED_PROTOCOL;
492         case IF2IP_FOUND:
493           is_interface = TRUE;
494           /*
495            * We now have the numerical IP address in the 'myhost' buffer
496            */
497           infof(data, "Local Interface %s is ip %s using address family %i",
498                 dev, myhost, af);
499           done = 1;
500           break;
501       }
502     }
503     if(!is_interface) {
504       /*
505        * This was not an interface, resolve the name as a host name
506        * or IP number
507        *
508        * Temporarily force name resolution to use only the address type
509        * of the connection. The resolve functions should really be changed
510        * to take a type parameter instead.
511        */
512       unsigned char ipver = conn->ip_version;
513       int rc;
514 
515       if(af == AF_INET)
516         conn->ip_version = CURL_IPRESOLVE_V4;
517 #ifdef USE_IPV6
518       else if(af == AF_INET6)
519         conn->ip_version = CURL_IPRESOLVE_V6;
520 #endif
521 
522       rc = Curl_resolv(data, dev, 80, FALSE, &h);
523       if(rc == CURLRESOLV_PENDING)
524         (void)Curl_resolver_wait_resolv(data, &h);
525       conn->ip_version = ipver;
526 
527       if(h) {
528         /* convert the resolved address, sizeof myhost >= INET_ADDRSTRLEN */
529         Curl_printable_address(h->addr, myhost, sizeof(myhost));
530         infof(data, "Name '%s' family %i resolved to '%s' family %i",
531               dev, af, myhost, h->addr->ai_family);
532         Curl_resolv_unlock(data, h);
533         if(af != h->addr->ai_family) {
534           /* bad IP version combo, signal the caller to try another address
535              family if available */
536           return CURLE_UNSUPPORTED_PROTOCOL;
537         }
538         done = 1;
539       }
540       else {
541         /*
542          * provided dev was no interface (or interfaces are not supported
543          * e.g. solaris) no ip address and no domain we fail here
544          */
545         done = -1;
546       }
547     }
548 
549     if(done > 0) {
550 #ifdef USE_IPV6
551       /* IPv6 address */
552       if(af == AF_INET6) {
553 #ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
554         char *scope_ptr = strchr(myhost, '%');
555         if(scope_ptr)
556           *(scope_ptr++) = '\0';
557 #endif
558         if(Curl_inet_pton(AF_INET6, myhost, &si6->sin6_addr) > 0) {
559           si6->sin6_family = AF_INET6;
560           si6->sin6_port = htons(port);
561 #ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
562           if(scope_ptr) {
563             /* The "myhost" string either comes from Curl_if2ip or from
564                Curl_printable_address. The latter returns only numeric scope
565                IDs and the former returns none at all.  So the scope ID, if
566                present, is known to be numeric */
567             unsigned long scope_id = strtoul(scope_ptr, NULL, 10);
568             if(scope_id > UINT_MAX)
569               return CURLE_UNSUPPORTED_PROTOCOL;
570 
571             si6->sin6_scope_id = (unsigned int)scope_id;
572           }
573 #endif
574         }
575         sizeof_sa = sizeof(struct sockaddr_in6);
576       }
577       else
578 #endif
579       /* IPv4 address */
580       if((af == AF_INET) &&
581          (Curl_inet_pton(AF_INET, myhost, &si4->sin_addr) > 0)) {
582         si4->sin_family = AF_INET;
583         si4->sin_port = htons(port);
584         sizeof_sa = sizeof(struct sockaddr_in);
585       }
586     }
587 
588     if(done < 1) {
589       /* errorbuf is set false so failf will overwrite any message already in
590          the error buffer, so the user receives this error message instead of a
591          generic resolve error. */
592       data->state.errorbuf = FALSE;
593       failf(data, "Couldn't bind to '%s'", dev);
594       return CURLE_INTERFACE_FAILED;
595     }
596   }
597   else {
598     /* no device was given, prepare sa to match af's needs */
599 #ifdef USE_IPV6
600     if(af == AF_INET6) {
601       si6->sin6_family = AF_INET6;
602       si6->sin6_port = htons(port);
603       sizeof_sa = sizeof(struct sockaddr_in6);
604     }
605     else
606 #endif
607     if(af == AF_INET) {
608       si4->sin_family = AF_INET;
609       si4->sin_port = htons(port);
610       sizeof_sa = sizeof(struct sockaddr_in);
611     }
612   }
613 #ifdef IP_BIND_ADDRESS_NO_PORT
614   (void)setsockopt(sockfd, SOL_IP, IP_BIND_ADDRESS_NO_PORT, &on, sizeof(on));
615 #endif
616   for(;;) {
617     if(bind(sockfd, sock, sizeof_sa) >= 0) {
618       /* we succeeded to bind */
619       infof(data, "Local port: %hu", port);
620       conn->bits.bound = TRUE;
621       return CURLE_OK;
622     }
623 
624     if(--portnum > 0) {
625       port++; /* try next port */
626       if(port == 0)
627         break;
628       infof(data, "Bind to local port %d failed, trying next", port - 1);
629       /* We reuse/clobber the port variable here below */
630       if(sock->sa_family == AF_INET)
631         si4->sin_port = ntohs(port);
632 #ifdef USE_IPV6
633       else
634         si6->sin6_port = ntohs(port);
635 #endif
636     }
637     else
638       break;
639   }
640   {
641     char buffer[STRERROR_LEN];
642     data->state.os_errno = error = SOCKERRNO;
643     failf(data, "bind failed with errno %d: %s",
644           error, Curl_strerror(error, buffer, sizeof(buffer)));
645   }
646 
647   return CURLE_INTERFACE_FAILED;
648 }
649 #endif
650 
651 /*
652  * verifyconnect() returns TRUE if the connect really has happened.
653  */
verifyconnect(curl_socket_t sockfd,int * error)654 static bool verifyconnect(curl_socket_t sockfd, int *error)
655 {
656   bool rc = TRUE;
657 #ifdef SO_ERROR
658   int err = 0;
659   curl_socklen_t errSize = sizeof(err);
660 
661 #ifdef _WIN32
662   /*
663    * In October 2003 we effectively nullified this function on Windows due to
664    * problems with it using all CPU in multi-threaded cases.
665    *
666    * In May 2004, we bring it back to offer more info back on connect failures.
667    * Gisle Vanem could reproduce the former problems with this function, but
668    * could avoid them by adding this SleepEx() call below:
669    *
670    *    "I don't have Rational Quantify, but the hint from his post was
671    *    ntdll::NtRemoveIoCompletion(). So I'd assume the SleepEx (or maybe
672    *    just Sleep(0) would be enough?) would release whatever
673    *    mutex/critical-section the ntdll call is waiting on.
674    *
675    *    Someone got to verify this on Win-NT 4.0, 2000."
676    */
677 
678 #ifdef _WIN32_WCE
679   Sleep(0);
680 #else
681   SleepEx(0, FALSE);
682 #endif
683 
684 #endif
685 
686   if(0 != getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (void *)&err, &errSize))
687     err = SOCKERRNO;
688 #ifdef _WIN32_WCE
689   /* Old WinCE versions don't support SO_ERROR */
690   if(WSAENOPROTOOPT == err) {
691     SET_SOCKERRNO(0);
692     err = 0;
693   }
694 #endif
695 #if defined(EBADIOCTL) && defined(__minix)
696   /* Minix 3.1.x doesn't support getsockopt on UDP sockets */
697   if(EBADIOCTL == err) {
698     SET_SOCKERRNO(0);
699     err = 0;
700   }
701 #endif
702   if((0 == err) || (EISCONN == err))
703     /* we are connected, awesome! */
704     rc = TRUE;
705   else
706     /* This wasn't a successful connect */
707     rc = FALSE;
708   if(error)
709     *error = err;
710 #else
711   (void)sockfd;
712   if(error)
713     *error = SOCKERRNO;
714 #endif
715   return rc;
716 }
717 
718 /**
719  * Determine the curl code for a socket connect() == -1 with errno.
720  */
socket_connect_result(struct Curl_easy * data,const char * ipaddress,int error)721 static CURLcode socket_connect_result(struct Curl_easy *data,
722                                       const char *ipaddress, int error)
723 {
724   switch(error) {
725   case EINPROGRESS:
726   case EWOULDBLOCK:
727 #if defined(EAGAIN)
728 #if (EAGAIN) != (EWOULDBLOCK)
729     /* On some platforms EAGAIN and EWOULDBLOCK are the
730      * same value, and on others they are different, hence
731      * the odd #if
732      */
733   case EAGAIN:
734 #endif
735 #endif
736     return CURLE_OK;
737 
738   default:
739     /* unknown error, fallthrough and try another address! */
740 #ifdef CURL_DISABLE_VERBOSE_STRINGS
741     (void)ipaddress;
742 #else
743     {
744       char buffer[STRERROR_LEN];
745       infof(data, "Immediate connect fail for %s: %s",
746             ipaddress, Curl_strerror(error, buffer, sizeof(buffer)));
747     }
748 #endif
749     data->state.os_errno = error;
750     /* connect failed */
751     return CURLE_COULDNT_CONNECT;
752   }
753 }
754 
755 /* We have a recv buffer to enhance reads with len < NW_SMALL_READS.
756  * This happens often on TLS connections where the TLS implementation
757  * tries to read the head of a TLS record, determine the length of the
758  * full record and then make a subsequent read for that.
759  * On large reads, we will not fill the buffer to avoid the double copy. */
760 #define NW_RECV_CHUNK_SIZE    (64 * 1024)
761 #define NW_RECV_CHUNKS         1
762 #define NW_SMALL_READS        (1024)
763 
764 struct cf_socket_ctx {
765   int transport;
766   struct Curl_sockaddr_ex addr;      /* address to connect to */
767   curl_socket_t sock;                /* current attempt socket */
768   struct bufq recvbuf;               /* used when `buffer_recv` is set */
769   struct ip_quadruple ip;            /* The IP quadruple 2x(addr+port) */
770   struct curltime started_at;        /* when socket was created */
771   struct curltime connected_at;      /* when socket connected/got first byte */
772   struct curltime first_byte_at;     /* when first byte was recvd */
773   int error;                         /* errno of last failure or 0 */
774 #ifdef DEBUGBUILD
775   int wblock_percent;                /* percent of writes doing EAGAIN */
776   int wpartial_percent;              /* percent of bytes written in send */
777   int rblock_percent;                /* percent of reads doing EAGAIN */
778   size_t recv_max;                  /* max enforced read size */
779 #endif
780   BIT(got_first_byte);               /* if first byte was received */
781   BIT(accepted);                     /* socket was accepted, not connected */
782   BIT(sock_connected);               /* socket is "connected", e.g. in UDP */
783   BIT(active);
784   BIT(buffer_recv);
785 };
786 
cf_socket_ctx_init(struct cf_socket_ctx * ctx,const struct Curl_addrinfo * ai,int transport)787 static void cf_socket_ctx_init(struct cf_socket_ctx *ctx,
788                                const struct Curl_addrinfo *ai,
789                                int transport)
790 {
791   memset(ctx, 0, sizeof(*ctx));
792   ctx->sock = CURL_SOCKET_BAD;
793   ctx->transport = transport;
794   Curl_sock_assign_addr(&ctx->addr, ai, transport);
795   Curl_bufq_init(&ctx->recvbuf, NW_RECV_CHUNK_SIZE, NW_RECV_CHUNKS);
796 #ifdef DEBUGBUILD
797   {
798     char *p = getenv("CURL_DBG_SOCK_WBLOCK");
799     if(p) {
800       long l = strtol(p, NULL, 10);
801       if(l >= 0 && l <= 100)
802         ctx->wblock_percent = (int)l;
803     }
804     p = getenv("CURL_DBG_SOCK_WPARTIAL");
805     if(p) {
806       long l = strtol(p, NULL, 10);
807       if(l >= 0 && l <= 100)
808         ctx->wpartial_percent = (int)l;
809     }
810     p = getenv("CURL_DBG_SOCK_RBLOCK");
811     if(p) {
812       long l = strtol(p, NULL, 10);
813       if(l >= 0 && l <= 100)
814         ctx->rblock_percent = (int)l;
815     }
816     p = getenv("CURL_DBG_SOCK_RMAX");
817     if(p) {
818       long l = strtol(p, NULL, 10);
819       if(l >= 0)
820         ctx->recv_max = (size_t)l;
821     }
822   }
823 #endif
824 }
825 
826 struct reader_ctx {
827   struct Curl_cfilter *cf;
828   struct Curl_easy *data;
829 };
830 
nw_in_read(void * reader_ctx,unsigned char * buf,size_t len,CURLcode * err)831 static ssize_t nw_in_read(void *reader_ctx,
832                            unsigned char *buf, size_t len,
833                            CURLcode *err)
834 {
835   struct reader_ctx *rctx = reader_ctx;
836   struct cf_socket_ctx *ctx = rctx->cf->ctx;
837   ssize_t nread;
838 
839   *err = CURLE_OK;
840   nread = sread(ctx->sock, buf, len);
841 
842   if(-1 == nread) {
843     int sockerr = SOCKERRNO;
844 
845     if(
846 #ifdef WSAEWOULDBLOCK
847       /* This is how Windows does it */
848       (WSAEWOULDBLOCK == sockerr)
849 #else
850       /* errno may be EWOULDBLOCK or on some systems EAGAIN when it returned
851          due to its inability to send off data without blocking. We therefore
852          treat both error codes the same here */
853       (EWOULDBLOCK == sockerr) || (EAGAIN == sockerr) || (EINTR == sockerr)
854 #endif
855       ) {
856       /* this is just a case of EWOULDBLOCK */
857       *err = CURLE_AGAIN;
858       nread = -1;
859     }
860     else {
861       char buffer[STRERROR_LEN];
862 
863       failf(rctx->data, "Recv failure: %s",
864             Curl_strerror(sockerr, buffer, sizeof(buffer)));
865       rctx->data->state.os_errno = sockerr;
866       *err = CURLE_RECV_ERROR;
867       nread = -1;
868     }
869   }
870   CURL_TRC_CF(rctx->data, rctx->cf, "nw_in_read(len=%zu, fd=%"
871               CURL_FORMAT_SOCKET_T ") -> %d, err=%d",
872               len, ctx->sock, (int)nread, *err);
873   return nread;
874 }
875 
cf_socket_close(struct Curl_cfilter * cf,struct Curl_easy * data)876 static void cf_socket_close(struct Curl_cfilter *cf, struct Curl_easy *data)
877 {
878   struct cf_socket_ctx *ctx = cf->ctx;
879 
880   if(ctx && CURL_SOCKET_BAD != ctx->sock) {
881     CURL_TRC_CF(data, cf, "cf_socket_close(%" CURL_FORMAT_SOCKET_T
882                 ")", ctx->sock);
883     if(ctx->sock == cf->conn->sock[cf->sockindex])
884       cf->conn->sock[cf->sockindex] = CURL_SOCKET_BAD;
885     socket_close(data, cf->conn, !ctx->accepted, ctx->sock);
886     ctx->sock = CURL_SOCKET_BAD;
887     if(ctx->active && cf->sockindex == FIRSTSOCKET)
888       cf->conn->remote_addr = NULL;
889     Curl_bufq_reset(&ctx->recvbuf);
890     ctx->active = FALSE;
891     ctx->buffer_recv = FALSE;
892     memset(&ctx->started_at, 0, sizeof(ctx->started_at));
893     memset(&ctx->connected_at, 0, sizeof(ctx->connected_at));
894   }
895 
896   cf->connected = FALSE;
897 }
898 
cf_socket_destroy(struct Curl_cfilter * cf,struct Curl_easy * data)899 static void cf_socket_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
900 {
901   struct cf_socket_ctx *ctx = cf->ctx;
902 
903   cf_socket_close(cf, data);
904   CURL_TRC_CF(data, cf, "destroy");
905   Curl_bufq_free(&ctx->recvbuf);
906   free(ctx);
907   cf->ctx = NULL;
908 }
909 
set_local_ip(struct Curl_cfilter * cf,struct Curl_easy * data)910 static CURLcode set_local_ip(struct Curl_cfilter *cf,
911                              struct Curl_easy *data)
912 {
913   struct cf_socket_ctx *ctx = cf->ctx;
914 
915 #ifdef HAVE_GETSOCKNAME
916   if((ctx->sock != CURL_SOCKET_BAD) &&
917      !(data->conn->handler->protocol & CURLPROTO_TFTP)) {
918     /* TFTP does not connect, so it cannot get the IP like this */
919 
920     char buffer[STRERROR_LEN];
921     struct Curl_sockaddr_storage ssloc;
922     curl_socklen_t slen = sizeof(struct Curl_sockaddr_storage);
923 
924     memset(&ssloc, 0, sizeof(ssloc));
925     if(getsockname(ctx->sock, (struct sockaddr*) &ssloc, &slen)) {
926       int error = SOCKERRNO;
927       failf(data, "getsockname() failed with errno %d: %s",
928             error, Curl_strerror(error, buffer, sizeof(buffer)));
929       return CURLE_FAILED_INIT;
930     }
931     if(!Curl_addr2string((struct sockaddr*)&ssloc, slen,
932                          ctx->ip.local_ip, &ctx->ip.local_port)) {
933       failf(data, "ssloc inet_ntop() failed with errno %d: %s",
934             errno, Curl_strerror(errno, buffer, sizeof(buffer)));
935       return CURLE_FAILED_INIT;
936     }
937   }
938 #else
939   (void)data;
940   ctx->ip.local_ip[0] = 0;
941   ctx->ip.local_port = -1;
942 #endif
943   return CURLE_OK;
944 }
945 
set_remote_ip(struct Curl_cfilter * cf,struct Curl_easy * data)946 static CURLcode set_remote_ip(struct Curl_cfilter *cf,
947                               struct Curl_easy *data)
948 {
949   struct cf_socket_ctx *ctx = cf->ctx;
950 
951   /* store remote address and port used in this connection attempt */
952   if(!Curl_addr2string(&ctx->addr.sa_addr, ctx->addr.addrlen,
953                        ctx->ip.remote_ip, &ctx->ip.remote_port)) {
954     char buffer[STRERROR_LEN];
955 
956     ctx->error = errno;
957     /* malformed address or bug in inet_ntop, try next address */
958     failf(data, "sa_addr inet_ntop() failed with errno %d: %s",
959           errno, Curl_strerror(errno, buffer, sizeof(buffer)));
960     return CURLE_FAILED_INIT;
961   }
962   return CURLE_OK;
963 }
964 
cf_socket_open(struct Curl_cfilter * cf,struct Curl_easy * data)965 static CURLcode cf_socket_open(struct Curl_cfilter *cf,
966                               struct Curl_easy *data)
967 {
968   struct cf_socket_ctx *ctx = cf->ctx;
969   int error = 0;
970   bool isconnected = FALSE;
971   CURLcode result = CURLE_COULDNT_CONNECT;
972   bool is_tcp;
973 
974   (void)data;
975   DEBUGASSERT(ctx->sock == CURL_SOCKET_BAD);
976   ctx->started_at = Curl_now();
977   result = socket_open(data, &ctx->addr, &ctx->sock);
978   if(result)
979     goto out;
980 
981   result = set_remote_ip(cf, data);
982   if(result)
983     goto out;
984 
985 #ifdef USE_IPV6
986   if(ctx->addr.family == AF_INET6) {
987     set_ipv6_v6only(ctx->sock, 0);
988     infof(data, "  Trying [%s]:%d...", ctx->ip.remote_ip, ctx->ip.remote_port);
989   }
990   else
991 #endif
992     infof(data, "  Trying %s:%d...", ctx->ip.remote_ip, ctx->ip.remote_port);
993 
994 #ifdef USE_IPV6
995   is_tcp = (ctx->addr.family == AF_INET
996             || ctx->addr.family == AF_INET6) &&
997            ctx->addr.socktype == SOCK_STREAM;
998 #else
999   is_tcp = (ctx->addr.family == AF_INET) &&
1000            ctx->addr.socktype == SOCK_STREAM;
1001 #endif
1002   if(is_tcp && data->set.tcp_nodelay)
1003     tcpnodelay(data, ctx->sock);
1004 
1005   nosigpipe(data, ctx->sock);
1006 
1007   Curl_sndbufset(ctx->sock);
1008 
1009   if(is_tcp && data->set.tcp_keepalive)
1010     tcpkeepalive(data, ctx->sock);
1011 
1012   if(data->set.fsockopt) {
1013     /* activate callback for setting socket options */
1014     Curl_set_in_callback(data, true);
1015     error = data->set.fsockopt(data->set.sockopt_client,
1016                                ctx->sock,
1017                                CURLSOCKTYPE_IPCXN);
1018     Curl_set_in_callback(data, false);
1019 
1020     if(error == CURL_SOCKOPT_ALREADY_CONNECTED)
1021       isconnected = TRUE;
1022     else if(error) {
1023       result = CURLE_ABORTED_BY_CALLBACK;
1024       goto out;
1025     }
1026   }
1027 
1028 #ifndef CURL_DISABLE_BINDLOCAL
1029   /* possibly bind the local end to an IP, interface or port */
1030   if(ctx->addr.family == AF_INET
1031 #ifdef USE_IPV6
1032      || ctx->addr.family == AF_INET6
1033 #endif
1034     ) {
1035     result = bindlocal(data, cf->conn, ctx->sock, ctx->addr.family,
1036                        Curl_ipv6_scope(&ctx->addr.sa_addr));
1037     if(result) {
1038       if(result == CURLE_UNSUPPORTED_PROTOCOL) {
1039         /* The address family is not supported on this interface.
1040            We can continue trying addresses */
1041         result = CURLE_COULDNT_CONNECT;
1042       }
1043       goto out;
1044     }
1045   }
1046 #endif
1047 
1048   /* set socket non-blocking */
1049   (void)curlx_nonblock(ctx->sock, TRUE);
1050   ctx->sock_connected = (ctx->addr.socktype != SOCK_DGRAM);
1051 out:
1052   if(result) {
1053     if(ctx->sock != CURL_SOCKET_BAD) {
1054       socket_close(data, cf->conn, TRUE, ctx->sock);
1055       ctx->sock = CURL_SOCKET_BAD;
1056     }
1057   }
1058   else if(isconnected) {
1059     set_local_ip(cf, data);
1060     ctx->connected_at = Curl_now();
1061     cf->connected = TRUE;
1062   }
1063   CURL_TRC_CF(data, cf, "cf_socket_open() -> %d, fd=%" CURL_FORMAT_SOCKET_T,
1064               result, ctx->sock);
1065   return result;
1066 }
1067 
do_connect(struct Curl_cfilter * cf,struct Curl_easy * data,bool is_tcp_fastopen)1068 static int do_connect(struct Curl_cfilter *cf, struct Curl_easy *data,
1069                       bool is_tcp_fastopen)
1070 {
1071   struct cf_socket_ctx *ctx = cf->ctx;
1072 #ifdef TCP_FASTOPEN_CONNECT
1073   int optval = 1;
1074 #endif
1075   int rc = -1;
1076 
1077   (void)data;
1078   if(is_tcp_fastopen) {
1079 #if defined(CONNECT_DATA_IDEMPOTENT) /* Darwin */
1080 #  if defined(HAVE_BUILTIN_AVAILABLE)
1081     /* while connectx function is available since macOS 10.11 / iOS 9,
1082        it did not have the interface declared correctly until
1083        Xcode 9 / macOS SDK 10.13 */
1084     if(__builtin_available(macOS 10.11, iOS 9.0, tvOS 9.0, watchOS 2.0, *)) {
1085       sa_endpoints_t endpoints;
1086       endpoints.sae_srcif = 0;
1087       endpoints.sae_srcaddr = NULL;
1088       endpoints.sae_srcaddrlen = 0;
1089       endpoints.sae_dstaddr = &ctx->addr.sa_addr;
1090       endpoints.sae_dstaddrlen = ctx->addr.addrlen;
1091 
1092       rc = connectx(ctx->sock, &endpoints, SAE_ASSOCID_ANY,
1093                     CONNECT_RESUME_ON_READ_WRITE | CONNECT_DATA_IDEMPOTENT,
1094                     NULL, 0, NULL, NULL);
1095     }
1096     else {
1097       rc = connect(ctx->sock, &ctx->addr.sa_addr, ctx->addr.addrlen);
1098     }
1099 #  else
1100     rc = connect(ctx->sock, &ctx->addr.sa_addr, ctx->addr.addrlen);
1101 #  endif /* HAVE_BUILTIN_AVAILABLE */
1102 #elif defined(TCP_FASTOPEN_CONNECT) /* Linux >= 4.11 */
1103     if(setsockopt(ctx->sock, IPPROTO_TCP, TCP_FASTOPEN_CONNECT,
1104                   (void *)&optval, sizeof(optval)) < 0)
1105       infof(data, "Failed to enable TCP Fast Open on fd %"
1106             CURL_FORMAT_SOCKET_T, ctx->sock);
1107 
1108     rc = connect(ctx->sock, &ctx->addr.sa_addr, ctx->addr.addrlen);
1109 #elif defined(MSG_FASTOPEN) /* old Linux */
1110     if(cf->conn->given->flags & PROTOPT_SSL)
1111       rc = connect(ctx->sock, &ctx->addr.sa_addr, ctx->addr.addrlen);
1112     else
1113       rc = 0; /* Do nothing */
1114 #endif
1115   }
1116   else {
1117     rc = connect(ctx->sock, &ctx->addr.sa_addr, ctx->addr.addrlen);
1118   }
1119   return rc;
1120 }
1121 
cf_tcp_connect(struct Curl_cfilter * cf,struct Curl_easy * data,bool blocking,bool * done)1122 static CURLcode cf_tcp_connect(struct Curl_cfilter *cf,
1123                                struct Curl_easy *data,
1124                                bool blocking, bool *done)
1125 {
1126   struct cf_socket_ctx *ctx = cf->ctx;
1127   CURLcode result = CURLE_COULDNT_CONNECT;
1128   int rc = 0;
1129 
1130   (void)data;
1131   if(cf->connected) {
1132     *done = TRUE;
1133     return CURLE_OK;
1134   }
1135 
1136   /* TODO: need to support blocking connect? */
1137   if(blocking)
1138     return CURLE_UNSUPPORTED_PROTOCOL;
1139 
1140   *done = FALSE; /* a very negative world view is best */
1141   if(ctx->sock == CURL_SOCKET_BAD) {
1142     int error;
1143 
1144     result = cf_socket_open(cf, data);
1145     if(result)
1146       goto out;
1147 
1148     if(cf->connected) {
1149       *done = TRUE;
1150       return CURLE_OK;
1151     }
1152 
1153     /* Connect TCP socket */
1154     rc = do_connect(cf, data, cf->conn->bits.tcp_fastopen);
1155     error = SOCKERRNO;
1156     data->tcp_connect_errno = error;
1157     set_local_ip(cf, data);
1158     CURL_TRC_CF(data, cf, "local address %s port %d...",
1159                 ctx->ip.local_ip, ctx->ip.local_port);
1160     if(-1 == rc) {
1161       result = socket_connect_result(data, ctx->ip.remote_ip, error);
1162       goto out;
1163     }
1164   }
1165 
1166 #ifdef mpeix
1167   /* Call this function once now, and ignore the results. We do this to
1168      "clear" the error state on the socket so that we can later read it
1169      reliably. This is reported necessary on the MPE/iX operating
1170      system. */
1171   (void)verifyconnect(ctx->sock, NULL);
1172 #endif
1173   /* check socket for connect */
1174   rc = SOCKET_WRITABLE(ctx->sock, 0);
1175 
1176   if(rc == 0) { /* no connection yet */
1177     CURL_TRC_CF(data, cf, "not connected yet");
1178     return CURLE_OK;
1179   }
1180   else if(rc == CURL_CSELECT_OUT || cf->conn->bits.tcp_fastopen) {
1181     if(verifyconnect(ctx->sock, &ctx->error)) {
1182       /* we are connected with TCP, awesome! */
1183       ctx->connected_at = Curl_now();
1184       set_local_ip(cf, data);
1185       *done = TRUE;
1186       cf->connected = TRUE;
1187       CURL_TRC_CF(data, cf, "connected");
1188       return CURLE_OK;
1189     }
1190   }
1191   else if(rc & CURL_CSELECT_ERR) {
1192     (void)verifyconnect(ctx->sock, &ctx->error);
1193     result = CURLE_COULDNT_CONNECT;
1194   }
1195 
1196 out:
1197   if(result) {
1198     if(ctx->error) {
1199       set_local_ip(cf, data);
1200       data->state.os_errno = ctx->error;
1201       SET_SOCKERRNO(ctx->error);
1202 #ifndef CURL_DISABLE_VERBOSE_STRINGS
1203       {
1204         char buffer[STRERROR_LEN];
1205         infof(data, "connect to %s port %u from %s port %d failed: %s",
1206               ctx->ip.remote_ip, ctx->ip.remote_port,
1207               ctx->ip.local_ip, ctx->ip.local_port,
1208               Curl_strerror(ctx->error, buffer, sizeof(buffer)));
1209       }
1210 #endif
1211     }
1212     if(ctx->sock != CURL_SOCKET_BAD) {
1213       socket_close(data, cf->conn, TRUE, ctx->sock);
1214       ctx->sock = CURL_SOCKET_BAD;
1215     }
1216     *done = FALSE;
1217   }
1218   return result;
1219 }
1220 
cf_socket_get_host(struct Curl_cfilter * cf,struct Curl_easy * data,const char ** phost,const char ** pdisplay_host,int * pport)1221 static void cf_socket_get_host(struct Curl_cfilter *cf,
1222                                struct Curl_easy *data,
1223                                const char **phost,
1224                                const char **pdisplay_host,
1225                                int *pport)
1226 {
1227   struct cf_socket_ctx *ctx = cf->ctx;
1228   (void)data;
1229   *phost = cf->conn->host.name;
1230   *pdisplay_host = cf->conn->host.dispname;
1231   *pport = ctx->ip.remote_port;
1232 }
1233 
cf_socket_adjust_pollset(struct Curl_cfilter * cf,struct Curl_easy * data,struct easy_pollset * ps)1234 static void cf_socket_adjust_pollset(struct Curl_cfilter *cf,
1235                                       struct Curl_easy *data,
1236                                       struct easy_pollset *ps)
1237 {
1238   struct cf_socket_ctx *ctx = cf->ctx;
1239 
1240   if(ctx->sock != CURL_SOCKET_BAD) {
1241     if(!cf->connected) {
1242       Curl_pollset_set_out_only(data, ps, ctx->sock);
1243       CURL_TRC_CF(data, cf, "adjust_pollset, !connected, POLLOUT fd=%"
1244                   CURL_FORMAT_SOCKET_T, ctx->sock);
1245     }
1246     else if(!ctx->active) {
1247       Curl_pollset_add_in(data, ps, ctx->sock);
1248       CURL_TRC_CF(data, cf, "adjust_pollset, !active, POLLIN fd=%"
1249                   CURL_FORMAT_SOCKET_T, ctx->sock);
1250     }
1251   }
1252 }
1253 
cf_socket_data_pending(struct Curl_cfilter * cf,const struct Curl_easy * data)1254 static bool cf_socket_data_pending(struct Curl_cfilter *cf,
1255                                    const struct Curl_easy *data)
1256 {
1257   struct cf_socket_ctx *ctx = cf->ctx;
1258   int readable;
1259 
1260   (void)data;
1261   if(!Curl_bufq_is_empty(&ctx->recvbuf))
1262     return TRUE;
1263 
1264   readable = SOCKET_READABLE(ctx->sock, 0);
1265   return (readable > 0 && (readable & CURL_CSELECT_IN));
1266 }
1267 
cf_socket_send(struct Curl_cfilter * cf,struct Curl_easy * data,const void * buf,size_t len,CURLcode * err)1268 static ssize_t cf_socket_send(struct Curl_cfilter *cf, struct Curl_easy *data,
1269                               const void *buf, size_t len, CURLcode *err)
1270 {
1271   struct cf_socket_ctx *ctx = cf->ctx;
1272   curl_socket_t fdsave;
1273   ssize_t nwritten;
1274   size_t orig_len = len;
1275 
1276   *err = CURLE_OK;
1277   fdsave = cf->conn->sock[cf->sockindex];
1278   cf->conn->sock[cf->sockindex] = ctx->sock;
1279 
1280 #ifdef DEBUGBUILD
1281   /* simulate network blocking/partial writes */
1282   if(ctx->wblock_percent > 0) {
1283     unsigned char c = 0;
1284     Curl_rand(data, &c, 1);
1285     if(c >= ((100-ctx->wblock_percent)*256/100)) {
1286       CURL_TRC_CF(data, cf, "send(len=%zu) SIMULATE EWOULDBLOCK", orig_len);
1287       *err = CURLE_AGAIN;
1288       nwritten = -1;
1289       cf->conn->sock[cf->sockindex] = fdsave;
1290       return nwritten;
1291     }
1292   }
1293   if(cf->cft != &Curl_cft_udp && ctx->wpartial_percent > 0 && len > 8) {
1294     len = len * ctx->wpartial_percent / 100;
1295     if(!len)
1296       len = 1;
1297     CURL_TRC_CF(data, cf, "send(len=%zu) SIMULATE partial write of %zu bytes",
1298                 orig_len, len);
1299   }
1300 #endif
1301 
1302 #if defined(MSG_FASTOPEN) && !defined(TCP_FASTOPEN_CONNECT) /* Linux */
1303   if(cf->conn->bits.tcp_fastopen) {
1304     nwritten = sendto(ctx->sock, buf, len, MSG_FASTOPEN,
1305                       &cf->conn->remote_addr->sa_addr,
1306                       cf->conn->remote_addr->addrlen);
1307     cf->conn->bits.tcp_fastopen = FALSE;
1308   }
1309   else
1310 #endif
1311     nwritten = swrite(ctx->sock, buf, len);
1312 
1313   if(-1 == nwritten) {
1314     int sockerr = SOCKERRNO;
1315 
1316     if(
1317 #ifdef WSAEWOULDBLOCK
1318       /* This is how Windows does it */
1319       (WSAEWOULDBLOCK == sockerr)
1320 #else
1321       /* errno may be EWOULDBLOCK or on some systems EAGAIN when it returned
1322          due to its inability to send off data without blocking. We therefore
1323          treat both error codes the same here */
1324       (EWOULDBLOCK == sockerr) || (EAGAIN == sockerr) || (EINTR == sockerr) ||
1325       (EINPROGRESS == sockerr)
1326 #endif
1327       ) {
1328       /* this is just a case of EWOULDBLOCK */
1329       *err = CURLE_AGAIN;
1330     }
1331     else {
1332       char buffer[STRERROR_LEN];
1333       failf(data, "Send failure: %s",
1334             Curl_strerror(sockerr, buffer, sizeof(buffer)));
1335       data->state.os_errno = sockerr;
1336       *err = CURLE_SEND_ERROR;
1337     }
1338   }
1339 
1340   CURL_TRC_CF(data, cf, "send(len=%zu) -> %d, err=%d",
1341               orig_len, (int)nwritten, *err);
1342   cf->conn->sock[cf->sockindex] = fdsave;
1343   return nwritten;
1344 }
1345 
cf_socket_recv(struct Curl_cfilter * cf,struct Curl_easy * data,char * buf,size_t len,CURLcode * err)1346 static ssize_t cf_socket_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
1347                               char *buf, size_t len, CURLcode *err)
1348 {
1349   struct cf_socket_ctx *ctx = cf->ctx;
1350   curl_socket_t fdsave;
1351   ssize_t nread;
1352 
1353   *err = CURLE_OK;
1354 
1355   fdsave = cf->conn->sock[cf->sockindex];
1356   cf->conn->sock[cf->sockindex] = ctx->sock;
1357 
1358 #ifdef DEBUGBUILD
1359   /* simulate network blocking/partial reads */
1360   if(cf->cft != &Curl_cft_udp && ctx->rblock_percent > 0) {
1361     unsigned char c = 0;
1362     Curl_rand(data, &c, 1);
1363     if(c >= ((100-ctx->rblock_percent)*256/100)) {
1364       CURL_TRC_CF(data, cf, "recv(len=%zu) SIMULATE EWOULDBLOCK", len);
1365       *err = CURLE_AGAIN;
1366       nread = -1;
1367       cf->conn->sock[cf->sockindex] = fdsave;
1368       return nread;
1369     }
1370   }
1371   if(cf->cft != &Curl_cft_udp && ctx->recv_max && ctx->recv_max < len) {
1372     size_t orig_len = len;
1373     len = ctx->recv_max;
1374     CURL_TRC_CF(data, cf, "recv(len=%zu) SIMULATE max read of %zu bytes",
1375                 orig_len, len);
1376   }
1377 #endif
1378 
1379   if(ctx->buffer_recv && !Curl_bufq_is_empty(&ctx->recvbuf)) {
1380     CURL_TRC_CF(data, cf, "recv from buffer");
1381     nread = Curl_bufq_read(&ctx->recvbuf, (unsigned char *)buf, len, err);
1382   }
1383   else {
1384     struct reader_ctx rctx;
1385 
1386     rctx.cf = cf;
1387     rctx.data = data;
1388 
1389     /* "small" reads may trigger filling our buffer, "large" reads
1390      * are probably not worth the additional copy */
1391     if(ctx->buffer_recv && len < NW_SMALL_READS) {
1392       ssize_t nwritten;
1393       nwritten = Curl_bufq_slurp(&ctx->recvbuf, nw_in_read, &rctx, err);
1394       if(nwritten < 0 && !Curl_bufq_is_empty(&ctx->recvbuf)) {
1395         /* we have a partial read with an error. need to deliver
1396          * what we got, return the error later. */
1397         CURL_TRC_CF(data, cf, "partial read: empty buffer first");
1398         nread = Curl_bufq_read(&ctx->recvbuf, (unsigned char *)buf, len, err);
1399       }
1400       else if(nwritten < 0) {
1401         nread = -1;
1402         goto out;
1403       }
1404       else if(nwritten == 0) {
1405         /* eof */
1406         *err = CURLE_OK;
1407         nread = 0;
1408       }
1409       else {
1410         CURL_TRC_CF(data, cf, "buffered %zd additional bytes", nwritten);
1411         nread = Curl_bufq_read(&ctx->recvbuf, (unsigned char *)buf, len, err);
1412       }
1413     }
1414     else {
1415       nread = nw_in_read(&rctx, (unsigned char *)buf, len, err);
1416     }
1417   }
1418 
1419 out:
1420   CURL_TRC_CF(data, cf, "recv(len=%zu) -> %d, err=%d", len, (int)nread,
1421               *err);
1422   if(nread > 0 && !ctx->got_first_byte) {
1423     ctx->first_byte_at = Curl_now();
1424     ctx->got_first_byte = TRUE;
1425   }
1426   cf->conn->sock[cf->sockindex] = fdsave;
1427   return nread;
1428 }
1429 
cf_socket_active(struct Curl_cfilter * cf,struct Curl_easy * data)1430 static void cf_socket_active(struct Curl_cfilter *cf, struct Curl_easy *data)
1431 {
1432   struct cf_socket_ctx *ctx = cf->ctx;
1433 
1434   /* use this socket from now on */
1435   cf->conn->sock[cf->sockindex] = ctx->sock;
1436   set_local_ip(cf, data);
1437   if(cf->sockindex == SECONDARYSOCKET)
1438     cf->conn->secondary = ctx->ip;
1439   else
1440     cf->conn->primary = ctx->ip;
1441   /* the first socket info gets some specials */
1442   if(cf->sockindex == FIRSTSOCKET) {
1443     cf->conn->remote_addr = &ctx->addr;
1444   #ifdef USE_IPV6
1445     cf->conn->bits.ipv6 = (ctx->addr.family == AF_INET6)? TRUE : FALSE;
1446   #endif
1447     Curl_persistconninfo(data, cf->conn, &ctx->ip);
1448     /* buffering is currently disabled by default because we have stalls
1449      * in parallel transfers where not all buffered data is consumed and no
1450      * socket events happen.
1451      */
1452     ctx->buffer_recv = FALSE;
1453   }
1454   ctx->active = TRUE;
1455 }
1456 
cf_socket_cntrl(struct Curl_cfilter * cf,struct Curl_easy * data,int event,int arg1,void * arg2)1457 static CURLcode cf_socket_cntrl(struct Curl_cfilter *cf,
1458                                 struct Curl_easy *data,
1459                                 int event, int arg1, void *arg2)
1460 {
1461   struct cf_socket_ctx *ctx = cf->ctx;
1462 
1463   (void)arg1;
1464   (void)arg2;
1465   switch(event) {
1466   case CF_CTRL_CONN_INFO_UPDATE:
1467     cf_socket_active(cf, data);
1468     break;
1469   case CF_CTRL_DATA_SETUP:
1470     Curl_persistconninfo(data, cf->conn, &ctx->ip);
1471     break;
1472   case CF_CTRL_FORGET_SOCKET:
1473     ctx->sock = CURL_SOCKET_BAD;
1474     break;
1475   }
1476   return CURLE_OK;
1477 }
1478 
cf_socket_conn_is_alive(struct Curl_cfilter * cf,struct Curl_easy * data,bool * input_pending)1479 static bool cf_socket_conn_is_alive(struct Curl_cfilter *cf,
1480                                     struct Curl_easy *data,
1481                                     bool *input_pending)
1482 {
1483   struct cf_socket_ctx *ctx = cf->ctx;
1484   struct pollfd pfd[1];
1485   int r;
1486 
1487   *input_pending = FALSE;
1488   (void)data;
1489   if(!ctx || ctx->sock == CURL_SOCKET_BAD)
1490     return FALSE;
1491 
1492   /* Check with 0 timeout if there are any events pending on the socket */
1493   pfd[0].fd = ctx->sock;
1494   pfd[0].events = POLLRDNORM|POLLIN|POLLRDBAND|POLLPRI;
1495   pfd[0].revents = 0;
1496 
1497   r = Curl_poll(pfd, 1, 0);
1498   if(r < 0) {
1499     CURL_TRC_CF(data, cf, "is_alive: poll error, assume dead");
1500     return FALSE;
1501   }
1502   else if(r == 0) {
1503     CURL_TRC_CF(data, cf, "is_alive: poll timeout, assume alive");
1504     return TRUE;
1505   }
1506   else if(pfd[0].revents & (POLLERR|POLLHUP|POLLPRI|POLLNVAL)) {
1507     CURL_TRC_CF(data, cf, "is_alive: err/hup/etc events, assume dead");
1508     return FALSE;
1509   }
1510 
1511   CURL_TRC_CF(data, cf, "is_alive: valid events, looks alive");
1512   *input_pending = TRUE;
1513   return TRUE;
1514 }
1515 
cf_socket_query(struct Curl_cfilter * cf,struct Curl_easy * data,int query,int * pres1,void * pres2)1516 static CURLcode cf_socket_query(struct Curl_cfilter *cf,
1517                                 struct Curl_easy *data,
1518                                 int query, int *pres1, void *pres2)
1519 {
1520   struct cf_socket_ctx *ctx = cf->ctx;
1521 
1522   switch(query) {
1523   case CF_QUERY_SOCKET:
1524     DEBUGASSERT(pres2);
1525     *((curl_socket_t *)pres2) = ctx->sock;
1526     return CURLE_OK;
1527   case CF_QUERY_CONNECT_REPLY_MS:
1528     if(ctx->got_first_byte) {
1529       timediff_t ms = Curl_timediff(ctx->first_byte_at, ctx->started_at);
1530       *pres1 = (ms < INT_MAX)? (int)ms : INT_MAX;
1531     }
1532     else
1533       *pres1 = -1;
1534     return CURLE_OK;
1535   case CF_QUERY_TIMER_CONNECT: {
1536     struct curltime *when = pres2;
1537     switch(ctx->transport) {
1538     case TRNSPRT_UDP:
1539     case TRNSPRT_QUIC:
1540       /* Since UDP connected sockets work different from TCP, we use the
1541        * time of the first byte from the peer as the "connect" time. */
1542       if(ctx->got_first_byte) {
1543         *when = ctx->first_byte_at;
1544         break;
1545       }
1546       FALLTHROUGH();
1547     default:
1548       *when = ctx->connected_at;
1549       break;
1550     }
1551     return CURLE_OK;
1552   }
1553   default:
1554     break;
1555   }
1556   return cf->next?
1557     cf->next->cft->query(cf->next, data, query, pres1, pres2) :
1558     CURLE_UNKNOWN_OPTION;
1559 }
1560 
1561 struct Curl_cftype Curl_cft_tcp = {
1562   "TCP",
1563   CF_TYPE_IP_CONNECT,
1564   CURL_LOG_LVL_NONE,
1565   cf_socket_destroy,
1566   cf_tcp_connect,
1567   cf_socket_close,
1568   cf_socket_get_host,
1569   cf_socket_adjust_pollset,
1570   cf_socket_data_pending,
1571   cf_socket_send,
1572   cf_socket_recv,
1573   cf_socket_cntrl,
1574   cf_socket_conn_is_alive,
1575   Curl_cf_def_conn_keep_alive,
1576   cf_socket_query,
1577 };
1578 
Curl_cf_tcp_create(struct Curl_cfilter ** pcf,struct Curl_easy * data,struct connectdata * conn,const struct Curl_addrinfo * ai,int transport)1579 CURLcode Curl_cf_tcp_create(struct Curl_cfilter **pcf,
1580                             struct Curl_easy *data,
1581                             struct connectdata *conn,
1582                             const struct Curl_addrinfo *ai,
1583                             int transport)
1584 {
1585   struct cf_socket_ctx *ctx = NULL;
1586   struct Curl_cfilter *cf = NULL;
1587   CURLcode result;
1588 
1589   (void)data;
1590   (void)conn;
1591   DEBUGASSERT(transport == TRNSPRT_TCP);
1592   ctx = calloc(1, sizeof(*ctx));
1593   if(!ctx) {
1594     result = CURLE_OUT_OF_MEMORY;
1595     goto out;
1596   }
1597   cf_socket_ctx_init(ctx, ai, transport);
1598 
1599   result = Curl_cf_create(&cf, &Curl_cft_tcp, ctx);
1600 
1601 out:
1602   *pcf = (!result)? cf : NULL;
1603   if(result) {
1604     Curl_safefree(cf);
1605     Curl_safefree(ctx);
1606   }
1607 
1608   return result;
1609 }
1610 
cf_udp_setup_quic(struct Curl_cfilter * cf,struct Curl_easy * data)1611 static CURLcode cf_udp_setup_quic(struct Curl_cfilter *cf,
1612                                struct Curl_easy *data)
1613 {
1614   struct cf_socket_ctx *ctx = cf->ctx;
1615   int rc;
1616 
1617   /* QUIC needs a connected socket, nonblocking */
1618   DEBUGASSERT(ctx->sock != CURL_SOCKET_BAD);
1619 
1620 #if defined(__APPLE__) && defined(USE_OPENSSL_QUIC)
1621   (void)rc;
1622   /* On macOS OpenSSL QUIC fails on connected sockets.
1623    * see: <https://github.com/openssl/openssl/issues/23251> */
1624 #else
1625   rc = connect(ctx->sock, &ctx->addr.sa_addr, ctx->addr.addrlen);
1626   if(-1 == rc) {
1627     return socket_connect_result(data, ctx->ip.remote_ip, SOCKERRNO);
1628   }
1629   ctx->sock_connected = TRUE;
1630 #endif
1631   set_local_ip(cf, data);
1632   CURL_TRC_CF(data, cf, "%s socket %" CURL_FORMAT_SOCKET_T
1633               " connected: [%s:%d] -> [%s:%d]",
1634               (ctx->transport == TRNSPRT_QUIC)? "QUIC" : "UDP",
1635               ctx->sock, ctx->ip.local_ip, ctx->ip.local_port,
1636               ctx->ip.remote_ip, ctx->ip.remote_port);
1637 
1638   (void)curlx_nonblock(ctx->sock, TRUE);
1639   switch(ctx->addr.family) {
1640 #if defined(__linux__) && defined(IP_MTU_DISCOVER)
1641   case AF_INET: {
1642     int val = IP_PMTUDISC_DO;
1643     (void)setsockopt(ctx->sock, IPPROTO_IP, IP_MTU_DISCOVER, &val,
1644                      sizeof(val));
1645     break;
1646   }
1647 #endif
1648 #if defined(__linux__) && defined(IPV6_MTU_DISCOVER)
1649   case AF_INET6: {
1650     int val = IPV6_PMTUDISC_DO;
1651     (void)setsockopt(ctx->sock, IPPROTO_IPV6, IPV6_MTU_DISCOVER, &val,
1652                      sizeof(val));
1653     break;
1654   }
1655 #endif
1656   }
1657   return CURLE_OK;
1658 }
1659 
cf_udp_connect(struct Curl_cfilter * cf,struct Curl_easy * data,bool blocking,bool * done)1660 static CURLcode cf_udp_connect(struct Curl_cfilter *cf,
1661                                struct Curl_easy *data,
1662                                bool blocking, bool *done)
1663 {
1664   struct cf_socket_ctx *ctx = cf->ctx;
1665   CURLcode result = CURLE_COULDNT_CONNECT;
1666 
1667   (void)blocking;
1668   if(cf->connected) {
1669     *done = TRUE;
1670     return CURLE_OK;
1671   }
1672   *done = FALSE;
1673   if(ctx->sock == CURL_SOCKET_BAD) {
1674     result = cf_socket_open(cf, data);
1675     if(result) {
1676       CURL_TRC_CF(data, cf, "cf_udp_connect(), open failed -> %d", result);
1677       goto out;
1678     }
1679 
1680     if(ctx->transport == TRNSPRT_QUIC) {
1681       result = cf_udp_setup_quic(cf, data);
1682       if(result)
1683         goto out;
1684       CURL_TRC_CF(data, cf, "cf_udp_connect(), opened socket=%"
1685                   CURL_FORMAT_SOCKET_T " (%s:%d)",
1686                   ctx->sock, ctx->ip.local_ip, ctx->ip.local_port);
1687     }
1688     else {
1689       CURL_TRC_CF(data, cf, "cf_udp_connect(), opened socket=%"
1690                   CURL_FORMAT_SOCKET_T " (unconnected)", ctx->sock);
1691     }
1692     *done = TRUE;
1693     cf->connected = TRUE;
1694   }
1695 out:
1696   return result;
1697 }
1698 
1699 struct Curl_cftype Curl_cft_udp = {
1700   "UDP",
1701   CF_TYPE_IP_CONNECT,
1702   CURL_LOG_LVL_NONE,
1703   cf_socket_destroy,
1704   cf_udp_connect,
1705   cf_socket_close,
1706   cf_socket_get_host,
1707   cf_socket_adjust_pollset,
1708   cf_socket_data_pending,
1709   cf_socket_send,
1710   cf_socket_recv,
1711   cf_socket_cntrl,
1712   cf_socket_conn_is_alive,
1713   Curl_cf_def_conn_keep_alive,
1714   cf_socket_query,
1715 };
1716 
Curl_cf_udp_create(struct Curl_cfilter ** pcf,struct Curl_easy * data,struct connectdata * conn,const struct Curl_addrinfo * ai,int transport)1717 CURLcode Curl_cf_udp_create(struct Curl_cfilter **pcf,
1718                             struct Curl_easy *data,
1719                             struct connectdata *conn,
1720                             const struct Curl_addrinfo *ai,
1721                             int transport)
1722 {
1723   struct cf_socket_ctx *ctx = NULL;
1724   struct Curl_cfilter *cf = NULL;
1725   CURLcode result;
1726 
1727   (void)data;
1728   (void)conn;
1729   DEBUGASSERT(transport == TRNSPRT_UDP || transport == TRNSPRT_QUIC);
1730   ctx = calloc(1, sizeof(*ctx));
1731   if(!ctx) {
1732     result = CURLE_OUT_OF_MEMORY;
1733     goto out;
1734   }
1735   cf_socket_ctx_init(ctx, ai, transport);
1736 
1737   result = Curl_cf_create(&cf, &Curl_cft_udp, ctx);
1738 
1739 out:
1740   *pcf = (!result)? cf : NULL;
1741   if(result) {
1742     Curl_safefree(cf);
1743     Curl_safefree(ctx);
1744   }
1745 
1746   return result;
1747 }
1748 
1749 /* this is the TCP filter which can also handle this case */
1750 struct Curl_cftype Curl_cft_unix = {
1751   "UNIX",
1752   CF_TYPE_IP_CONNECT,
1753   CURL_LOG_LVL_NONE,
1754   cf_socket_destroy,
1755   cf_tcp_connect,
1756   cf_socket_close,
1757   cf_socket_get_host,
1758   cf_socket_adjust_pollset,
1759   cf_socket_data_pending,
1760   cf_socket_send,
1761   cf_socket_recv,
1762   cf_socket_cntrl,
1763   cf_socket_conn_is_alive,
1764   Curl_cf_def_conn_keep_alive,
1765   cf_socket_query,
1766 };
1767 
Curl_cf_unix_create(struct Curl_cfilter ** pcf,struct Curl_easy * data,struct connectdata * conn,const struct Curl_addrinfo * ai,int transport)1768 CURLcode Curl_cf_unix_create(struct Curl_cfilter **pcf,
1769                              struct Curl_easy *data,
1770                              struct connectdata *conn,
1771                              const struct Curl_addrinfo *ai,
1772                              int transport)
1773 {
1774   struct cf_socket_ctx *ctx = NULL;
1775   struct Curl_cfilter *cf = NULL;
1776   CURLcode result;
1777 
1778   (void)data;
1779   (void)conn;
1780   DEBUGASSERT(transport == TRNSPRT_UNIX);
1781   ctx = calloc(1, sizeof(*ctx));
1782   if(!ctx) {
1783     result = CURLE_OUT_OF_MEMORY;
1784     goto out;
1785   }
1786   cf_socket_ctx_init(ctx, ai, transport);
1787 
1788   result = Curl_cf_create(&cf, &Curl_cft_unix, ctx);
1789 
1790 out:
1791   *pcf = (!result)? cf : NULL;
1792   if(result) {
1793     Curl_safefree(cf);
1794     Curl_safefree(ctx);
1795   }
1796 
1797   return result;
1798 }
1799 
cf_tcp_accept_connect(struct Curl_cfilter * cf,struct Curl_easy * data,bool blocking,bool * done)1800 static CURLcode cf_tcp_accept_connect(struct Curl_cfilter *cf,
1801                                       struct Curl_easy *data,
1802                                       bool blocking, bool *done)
1803 {
1804   /* we start accepted, if we ever close, we cannot go on */
1805   (void)data;
1806   (void)blocking;
1807   if(cf->connected) {
1808     *done = TRUE;
1809     return CURLE_OK;
1810   }
1811   return CURLE_FAILED_INIT;
1812 }
1813 
1814 struct Curl_cftype Curl_cft_tcp_accept = {
1815   "TCP-ACCEPT",
1816   CF_TYPE_IP_CONNECT,
1817   CURL_LOG_LVL_NONE,
1818   cf_socket_destroy,
1819   cf_tcp_accept_connect,
1820   cf_socket_close,
1821   cf_socket_get_host,              /* TODO: not accurate */
1822   cf_socket_adjust_pollset,
1823   cf_socket_data_pending,
1824   cf_socket_send,
1825   cf_socket_recv,
1826   cf_socket_cntrl,
1827   cf_socket_conn_is_alive,
1828   Curl_cf_def_conn_keep_alive,
1829   cf_socket_query,
1830 };
1831 
Curl_conn_tcp_listen_set(struct Curl_easy * data,struct connectdata * conn,int sockindex,curl_socket_t * s)1832 CURLcode Curl_conn_tcp_listen_set(struct Curl_easy *data,
1833                                   struct connectdata *conn,
1834                                   int sockindex, curl_socket_t *s)
1835 {
1836   CURLcode result;
1837   struct Curl_cfilter *cf = NULL;
1838   struct cf_socket_ctx *ctx = NULL;
1839 
1840   /* replace any existing */
1841   Curl_conn_cf_discard_all(data, conn, sockindex);
1842   DEBUGASSERT(conn->sock[sockindex] == CURL_SOCKET_BAD);
1843 
1844   ctx = calloc(1, sizeof(*ctx));
1845   if(!ctx) {
1846     result = CURLE_OUT_OF_MEMORY;
1847     goto out;
1848   }
1849   ctx->transport = conn->transport;
1850   ctx->sock = *s;
1851   ctx->accepted = FALSE;
1852   result = Curl_cf_create(&cf, &Curl_cft_tcp_accept, ctx);
1853   if(result)
1854     goto out;
1855   Curl_conn_cf_add(data, conn, sockindex, cf);
1856 
1857   conn->sock[sockindex] = ctx->sock;
1858   set_local_ip(cf, data);
1859   ctx->active = TRUE;
1860   ctx->connected_at = Curl_now();
1861   cf->connected = TRUE;
1862   CURL_TRC_CF(data, cf, "Curl_conn_tcp_listen_set(%"
1863               CURL_FORMAT_SOCKET_T ")", ctx->sock);
1864 
1865 out:
1866   if(result) {
1867     Curl_safefree(cf);
1868     Curl_safefree(ctx);
1869   }
1870   return result;
1871 }
1872 
set_accepted_remote_ip(struct Curl_cfilter * cf,struct Curl_easy * data)1873 static void set_accepted_remote_ip(struct Curl_cfilter *cf,
1874                                    struct Curl_easy *data)
1875 {
1876   struct cf_socket_ctx *ctx = cf->ctx;
1877 #ifdef HAVE_GETPEERNAME
1878   char buffer[STRERROR_LEN];
1879   struct Curl_sockaddr_storage ssrem;
1880   curl_socklen_t plen;
1881 
1882   ctx->ip.remote_ip[0] = 0;
1883   ctx->ip.remote_port = 0;
1884   plen = sizeof(ssrem);
1885   memset(&ssrem, 0, plen);
1886   if(getpeername(ctx->sock, (struct sockaddr*) &ssrem, &plen)) {
1887     int error = SOCKERRNO;
1888     failf(data, "getpeername() failed with errno %d: %s",
1889           error, Curl_strerror(error, buffer, sizeof(buffer)));
1890     return;
1891   }
1892   if(!Curl_addr2string((struct sockaddr*)&ssrem, plen,
1893                        ctx->ip.remote_ip, &ctx->ip.remote_port)) {
1894     failf(data, "ssrem inet_ntop() failed with errno %d: %s",
1895           errno, Curl_strerror(errno, buffer, sizeof(buffer)));
1896     return;
1897   }
1898 #else
1899   ctx->ip.remote_ip[0] = 0;
1900   ctx->ip.remote_port = 0;
1901   (void)data;
1902 #endif
1903 }
1904 
Curl_conn_tcp_accepted_set(struct Curl_easy * data,struct connectdata * conn,int sockindex,curl_socket_t * s)1905 CURLcode Curl_conn_tcp_accepted_set(struct Curl_easy *data,
1906                                     struct connectdata *conn,
1907                                     int sockindex, curl_socket_t *s)
1908 {
1909   struct Curl_cfilter *cf = NULL;
1910   struct cf_socket_ctx *ctx = NULL;
1911 
1912   cf = conn->cfilter[sockindex];
1913   if(!cf || cf->cft != &Curl_cft_tcp_accept)
1914     return CURLE_FAILED_INIT;
1915 
1916   ctx = cf->ctx;
1917   /* discard the listen socket */
1918   socket_close(data, conn, TRUE, ctx->sock);
1919   ctx->sock = *s;
1920   conn->sock[sockindex] = ctx->sock;
1921   set_accepted_remote_ip(cf, data);
1922   set_local_ip(cf, data);
1923   ctx->active = TRUE;
1924   ctx->accepted = TRUE;
1925   ctx->connected_at = Curl_now();
1926   cf->connected = TRUE;
1927   CURL_TRC_CF(data, cf, "accepted_set(sock=%" CURL_FORMAT_SOCKET_T
1928               ", remote=%s port=%d)",
1929               ctx->sock, ctx->ip.remote_ip, ctx->ip.remote_port);
1930 
1931   return CURLE_OK;
1932 }
1933 
1934 /**
1935  * Return TRUE iff `cf` is a socket filter.
1936  */
cf_is_socket(struct Curl_cfilter * cf)1937 static bool cf_is_socket(struct Curl_cfilter *cf)
1938 {
1939   return cf && (cf->cft == &Curl_cft_tcp ||
1940                 cf->cft == &Curl_cft_udp ||
1941                 cf->cft == &Curl_cft_unix ||
1942                 cf->cft == &Curl_cft_tcp_accept);
1943 }
1944 
Curl_cf_socket_peek(struct Curl_cfilter * cf,struct Curl_easy * data,curl_socket_t * psock,const struct Curl_sockaddr_ex ** paddr,struct ip_quadruple * pip)1945 CURLcode Curl_cf_socket_peek(struct Curl_cfilter *cf,
1946                              struct Curl_easy *data,
1947                              curl_socket_t *psock,
1948                              const struct Curl_sockaddr_ex **paddr,
1949                              struct ip_quadruple *pip)
1950 {
1951   (void)data;
1952   if(cf_is_socket(cf) && cf->ctx) {
1953     struct cf_socket_ctx *ctx = cf->ctx;
1954 
1955     if(psock)
1956       *psock = ctx->sock;
1957     if(paddr)
1958       *paddr = &ctx->addr;
1959     if(pip)
1960       *pip = ctx->ip;
1961     return CURLE_OK;
1962   }
1963   return CURLE_FAILED_INIT;
1964 }
1965