• 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(ENABLE_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(ENABLE_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 ENABLE_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 ENABLE_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 ENABLE_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 ENABLE_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 ENABLE_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 ENABLE_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       struct Curl_sockaddr_storage add;
620       curl_socklen_t size = sizeof(add);
621       memset(&add, 0, sizeof(struct Curl_sockaddr_storage));
622       if(getsockname(sockfd, (struct sockaddr *) &add, &size) < 0) {
623         char buffer[STRERROR_LEN];
624         data->state.os_errno = error = SOCKERRNO;
625         failf(data, "getsockname() failed with errno %d: %s",
626               error, Curl_strerror(error, buffer, sizeof(buffer)));
627         return CURLE_INTERFACE_FAILED;
628       }
629       infof(data, "Local port: %hu", port);
630       conn->bits.bound = TRUE;
631       return CURLE_OK;
632     }
633 
634     if(--portnum > 0) {
635       port++; /* try next port */
636       if(port == 0)
637         break;
638       infof(data, "Bind to local port %d failed, trying next", port - 1);
639       /* We reuse/clobber the port variable here below */
640       if(sock->sa_family == AF_INET)
641         si4->sin_port = ntohs(port);
642 #ifdef ENABLE_IPV6
643       else
644         si6->sin6_port = ntohs(port);
645 #endif
646     }
647     else
648       break;
649   }
650   {
651     char buffer[STRERROR_LEN];
652     data->state.os_errno = error = SOCKERRNO;
653     failf(data, "bind failed with errno %d: %s",
654           error, Curl_strerror(error, buffer, sizeof(buffer)));
655   }
656 
657   return CURLE_INTERFACE_FAILED;
658 }
659 #endif
660 
661 /*
662  * verifyconnect() returns TRUE if the connect really has happened.
663  */
verifyconnect(curl_socket_t sockfd,int * error)664 static bool verifyconnect(curl_socket_t sockfd, int *error)
665 {
666   bool rc = TRUE;
667 #ifdef SO_ERROR
668   int err = 0;
669   curl_socklen_t errSize = sizeof(err);
670 
671 #ifdef _WIN32
672   /*
673    * In October 2003 we effectively nullified this function on Windows due to
674    * problems with it using all CPU in multi-threaded cases.
675    *
676    * In May 2004, we bring it back to offer more info back on connect failures.
677    * Gisle Vanem could reproduce the former problems with this function, but
678    * could avoid them by adding this SleepEx() call below:
679    *
680    *    "I don't have Rational Quantify, but the hint from his post was
681    *    ntdll::NtRemoveIoCompletion(). So I'd assume the SleepEx (or maybe
682    *    just Sleep(0) would be enough?) would release whatever
683    *    mutex/critical-section the ntdll call is waiting on.
684    *
685    *    Someone got to verify this on Win-NT 4.0, 2000."
686    */
687 
688 #ifdef _WIN32_WCE
689   Sleep(0);
690 #else
691   SleepEx(0, FALSE);
692 #endif
693 
694 #endif
695 
696   if(0 != getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (void *)&err, &errSize))
697     err = SOCKERRNO;
698 #ifdef _WIN32_WCE
699   /* Old WinCE versions don't support SO_ERROR */
700   if(WSAENOPROTOOPT == err) {
701     SET_SOCKERRNO(0);
702     err = 0;
703   }
704 #endif
705 #if defined(EBADIOCTL) && defined(__minix)
706   /* Minix 3.1.x doesn't support getsockopt on UDP sockets */
707   if(EBADIOCTL == err) {
708     SET_SOCKERRNO(0);
709     err = 0;
710   }
711 #endif
712   if((0 == err) || (EISCONN == err))
713     /* we are connected, awesome! */
714     rc = TRUE;
715   else
716     /* This wasn't a successful connect */
717     rc = FALSE;
718   if(error)
719     *error = err;
720 #else
721   (void)sockfd;
722   if(error)
723     *error = SOCKERRNO;
724 #endif
725   return rc;
726 }
727 
728 /**
729  * Determine the curl code for a socket connect() == -1 with errno.
730  */
socket_connect_result(struct Curl_easy * data,const char * ipaddress,int error)731 static CURLcode socket_connect_result(struct Curl_easy *data,
732                                       const char *ipaddress, int error)
733 {
734   switch(error) {
735   case EINPROGRESS:
736   case EWOULDBLOCK:
737 #if defined(EAGAIN)
738 #if (EAGAIN) != (EWOULDBLOCK)
739     /* On some platforms EAGAIN and EWOULDBLOCK are the
740      * same value, and on others they are different, hence
741      * the odd #if
742      */
743   case EAGAIN:
744 #endif
745 #endif
746     return CURLE_OK;
747 
748   default:
749     /* unknown error, fallthrough and try another address! */
750 #ifdef CURL_DISABLE_VERBOSE_STRINGS
751     (void)ipaddress;
752 #else
753     {
754       char buffer[STRERROR_LEN];
755       infof(data, "Immediate connect fail for %s: %s",
756             ipaddress, Curl_strerror(error, buffer, sizeof(buffer)));
757     }
758 #endif
759     data->state.os_errno = error;
760     /* connect failed */
761     return CURLE_COULDNT_CONNECT;
762   }
763 }
764 
765 /* We have a recv buffer to enhance reads with len < NW_SMALL_READS.
766  * This happens often on TLS connections where the TLS implementation
767  * tries to read the head of a TLS record, determine the length of the
768  * full record and then make a subsequent read for that.
769  * On large reads, we will not fill the buffer to avoid the double copy. */
770 #define NW_RECV_CHUNK_SIZE    (64 * 1024)
771 #define NW_RECV_CHUNKS         1
772 #define NW_SMALL_READS        (1024)
773 
774 struct cf_socket_ctx {
775   int transport;
776   struct Curl_sockaddr_ex addr;      /* address to connect to */
777   curl_socket_t sock;                /* current attempt socket */
778   struct bufq recvbuf;               /* used when `buffer_recv` is set */
779   char r_ip[MAX_IPADR_LEN];          /* remote IP as string */
780   int r_port;                        /* remote port number */
781   char l_ip[MAX_IPADR_LEN];          /* local IP as string */
782   int l_port;                        /* local port number */
783   struct curltime started_at;        /* when socket was created */
784   struct curltime connected_at;      /* when socket connected/got first byte */
785   struct curltime first_byte_at;     /* when first byte was recvd */
786   int error;                         /* errno of last failure or 0 */
787 #ifdef DEBUGBUILD
788   int wblock_percent;                /* percent of writes doing EAGAIN */
789   int wpartial_percent;              /* percent of bytes written in send */
790   int rblock_percent;                /* percent of reads doing EAGAIN */
791   size_t recv_max;                  /* max enforced read size */
792 #endif
793   BIT(got_first_byte);               /* if first byte was received */
794   BIT(accepted);                     /* socket was accepted, not connected */
795   BIT(sock_connected);               /* socket is "connected", e.g. in UDP */
796   BIT(active);
797   BIT(buffer_recv);
798 };
799 
cf_socket_ctx_init(struct cf_socket_ctx * ctx,const struct Curl_addrinfo * ai,int transport)800 static void cf_socket_ctx_init(struct cf_socket_ctx *ctx,
801                                const struct Curl_addrinfo *ai,
802                                int transport)
803 {
804   memset(ctx, 0, sizeof(*ctx));
805   ctx->sock = CURL_SOCKET_BAD;
806   ctx->transport = transport;
807   Curl_sock_assign_addr(&ctx->addr, ai, transport);
808   Curl_bufq_init(&ctx->recvbuf, NW_RECV_CHUNK_SIZE, NW_RECV_CHUNKS);
809 #ifdef DEBUGBUILD
810   {
811     char *p = getenv("CURL_DBG_SOCK_WBLOCK");
812     if(p) {
813       long l = strtol(p, NULL, 10);
814       if(l >= 0 && l <= 100)
815         ctx->wblock_percent = (int)l;
816     }
817     p = getenv("CURL_DBG_SOCK_WPARTIAL");
818     if(p) {
819       long l = strtol(p, NULL, 10);
820       if(l >= 0 && l <= 100)
821         ctx->wpartial_percent = (int)l;
822     }
823     p = getenv("CURL_DBG_SOCK_RBLOCK");
824     if(p) {
825       long l = strtol(p, NULL, 10);
826       if(l >= 0 && l <= 100)
827         ctx->rblock_percent = (int)l;
828     }
829     p = getenv("CURL_DBG_SOCK_RMAX");
830     if(p) {
831       long l = strtol(p, NULL, 10);
832       if(l >= 0)
833         ctx->recv_max = (size_t)l;
834     }
835   }
836 #endif
837 }
838 
839 struct reader_ctx {
840   struct Curl_cfilter *cf;
841   struct Curl_easy *data;
842 };
843 
nw_in_read(void * reader_ctx,unsigned char * buf,size_t len,CURLcode * err)844 static ssize_t nw_in_read(void *reader_ctx,
845                            unsigned char *buf, size_t len,
846                            CURLcode *err)
847 {
848   struct reader_ctx *rctx = reader_ctx;
849   struct cf_socket_ctx *ctx = rctx->cf->ctx;
850   ssize_t nread;
851 
852   *err = CURLE_OK;
853   nread = sread(ctx->sock, buf, len);
854 
855   if(-1 == nread) {
856     int sockerr = SOCKERRNO;
857 
858     if(
859 #ifdef WSAEWOULDBLOCK
860       /* This is how Windows does it */
861       (WSAEWOULDBLOCK == sockerr)
862 #else
863       /* errno may be EWOULDBLOCK or on some systems EAGAIN when it returned
864          due to its inability to send off data without blocking. We therefore
865          treat both error codes the same here */
866       (EWOULDBLOCK == sockerr) || (EAGAIN == sockerr) || (EINTR == sockerr)
867 #endif
868       ) {
869       /* this is just a case of EWOULDBLOCK */
870       *err = CURLE_AGAIN;
871       nread = -1;
872     }
873     else {
874       char buffer[STRERROR_LEN];
875 
876       failf(rctx->data, "Recv failure: %s",
877             Curl_strerror(sockerr, buffer, sizeof(buffer)));
878       rctx->data->state.os_errno = sockerr;
879       *err = CURLE_RECV_ERROR;
880       nread = -1;
881     }
882   }
883   CURL_TRC_CF(rctx->data, rctx->cf, "nw_in_read(len=%zu) -> %d, err=%d",
884               len, (int)nread, *err);
885   return nread;
886 }
887 
cf_socket_close(struct Curl_cfilter * cf,struct Curl_easy * data)888 static void cf_socket_close(struct Curl_cfilter *cf, struct Curl_easy *data)
889 {
890   struct cf_socket_ctx *ctx = cf->ctx;
891 
892   if(ctx && CURL_SOCKET_BAD != ctx->sock) {
893     CURL_TRC_CF(data, cf, "cf_socket_close(%" CURL_FORMAT_SOCKET_T
894                 ")", ctx->sock);
895     if(ctx->sock == cf->conn->sock[cf->sockindex])
896       cf->conn->sock[cf->sockindex] = CURL_SOCKET_BAD;
897     socket_close(data, cf->conn, !ctx->accepted, ctx->sock);
898     ctx->sock = CURL_SOCKET_BAD;
899     if(ctx->active && cf->sockindex == FIRSTSOCKET)
900       cf->conn->remote_addr = NULL;
901     Curl_bufq_reset(&ctx->recvbuf);
902     ctx->active = FALSE;
903     ctx->buffer_recv = FALSE;
904     memset(&ctx->started_at, 0, sizeof(ctx->started_at));
905     memset(&ctx->connected_at, 0, sizeof(ctx->connected_at));
906   }
907 
908   cf->connected = FALSE;
909 }
910 
cf_socket_destroy(struct Curl_cfilter * cf,struct Curl_easy * data)911 static void cf_socket_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
912 {
913   struct cf_socket_ctx *ctx = cf->ctx;
914 
915   cf_socket_close(cf, data);
916   CURL_TRC_CF(data, cf, "destroy");
917   Curl_bufq_free(&ctx->recvbuf);
918   free(ctx);
919   cf->ctx = NULL;
920 }
921 
set_local_ip(struct Curl_cfilter * cf,struct Curl_easy * data)922 static CURLcode set_local_ip(struct Curl_cfilter *cf,
923                              struct Curl_easy *data)
924 {
925   struct cf_socket_ctx *ctx = cf->ctx;
926 
927 #ifdef HAVE_GETSOCKNAME
928   if(!(data->conn->handler->protocol & CURLPROTO_TFTP)) {
929     /* TFTP does not connect, so it cannot get the IP like this */
930 
931     char buffer[STRERROR_LEN];
932     struct Curl_sockaddr_storage ssloc;
933     curl_socklen_t slen = sizeof(struct Curl_sockaddr_storage);
934 
935     memset(&ssloc, 0, sizeof(ssloc));
936     if(getsockname(ctx->sock, (struct sockaddr*) &ssloc, &slen)) {
937       int error = SOCKERRNO;
938       failf(data, "getsockname() failed with errno %d: %s",
939             error, Curl_strerror(error, buffer, sizeof(buffer)));
940       return CURLE_FAILED_INIT;
941     }
942     if(!Curl_addr2string((struct sockaddr*)&ssloc, slen,
943                          ctx->l_ip, &ctx->l_port)) {
944       failf(data, "ssloc inet_ntop() failed with errno %d: %s",
945             errno, Curl_strerror(errno, buffer, sizeof(buffer)));
946       return CURLE_FAILED_INIT;
947     }
948   }
949 #else
950   (void)data;
951   ctx->l_ip[0] = 0;
952   ctx->l_port = -1;
953 #endif
954   return CURLE_OK;
955 }
956 
set_remote_ip(struct Curl_cfilter * cf,struct Curl_easy * data)957 static CURLcode set_remote_ip(struct Curl_cfilter *cf,
958                               struct Curl_easy *data)
959 {
960   struct cf_socket_ctx *ctx = cf->ctx;
961 
962   /* store remote address and port used in this connection attempt */
963   if(!Curl_addr2string(&ctx->addr.sa_addr, ctx->addr.addrlen,
964                        ctx->r_ip, &ctx->r_port)) {
965     char buffer[STRERROR_LEN];
966 
967     ctx->error = errno;
968     /* malformed address or bug in inet_ntop, try next address */
969     failf(data, "sa_addr inet_ntop() failed with errno %d: %s",
970           errno, Curl_strerror(errno, buffer, sizeof(buffer)));
971     return CURLE_FAILED_INIT;
972   }
973   return CURLE_OK;
974 }
975 
cf_socket_open(struct Curl_cfilter * cf,struct Curl_easy * data)976 static CURLcode cf_socket_open(struct Curl_cfilter *cf,
977                               struct Curl_easy *data)
978 {
979   struct cf_socket_ctx *ctx = cf->ctx;
980   int error = 0;
981   bool isconnected = FALSE;
982   CURLcode result = CURLE_COULDNT_CONNECT;
983   bool is_tcp;
984 
985   (void)data;
986   DEBUGASSERT(ctx->sock == CURL_SOCKET_BAD);
987   ctx->started_at = Curl_now();
988   result = socket_open(data, &ctx->addr, &ctx->sock);
989   if(result)
990     goto out;
991 
992   result = set_remote_ip(cf, data);
993   if(result)
994     goto out;
995 
996 #ifdef ENABLE_IPV6
997   if(ctx->addr.family == AF_INET6) {
998     set_ipv6_v6only(ctx->sock, 0);
999     infof(data, "  Trying [%s]:%d...", ctx->r_ip, ctx->r_port);
1000   }
1001   else
1002 #endif
1003     infof(data, "  Trying %s:%d...", ctx->r_ip, ctx->r_port);
1004 
1005 #ifdef ENABLE_IPV6
1006   is_tcp = (ctx->addr.family == AF_INET
1007             || ctx->addr.family == AF_INET6) &&
1008            ctx->addr.socktype == SOCK_STREAM;
1009 #else
1010   is_tcp = (ctx->addr.family == AF_INET) &&
1011            ctx->addr.socktype == SOCK_STREAM;
1012 #endif
1013   if(is_tcp && data->set.tcp_nodelay)
1014     tcpnodelay(data, ctx->sock);
1015 
1016   nosigpipe(data, ctx->sock);
1017 
1018   Curl_sndbufset(ctx->sock);
1019 
1020   if(is_tcp && data->set.tcp_keepalive)
1021     tcpkeepalive(data, ctx->sock);
1022 
1023   if(data->set.fsockopt) {
1024     /* activate callback for setting socket options */
1025     Curl_set_in_callback(data, true);
1026     error = data->set.fsockopt(data->set.sockopt_client,
1027                                ctx->sock,
1028                                CURLSOCKTYPE_IPCXN);
1029     Curl_set_in_callback(data, false);
1030 
1031     if(error == CURL_SOCKOPT_ALREADY_CONNECTED)
1032       isconnected = TRUE;
1033     else if(error) {
1034       result = CURLE_ABORTED_BY_CALLBACK;
1035       goto out;
1036     }
1037   }
1038 
1039 #ifndef CURL_DISABLE_BINDLOCAL
1040   /* possibly bind the local end to an IP, interface or port */
1041   if(ctx->addr.family == AF_INET
1042 #ifdef ENABLE_IPV6
1043      || ctx->addr.family == AF_INET6
1044 #endif
1045     ) {
1046     result = bindlocal(data, cf->conn, ctx->sock, ctx->addr.family,
1047                        Curl_ipv6_scope(&ctx->addr.sa_addr));
1048     if(result) {
1049       if(result == CURLE_UNSUPPORTED_PROTOCOL) {
1050         /* The address family is not supported on this interface.
1051            We can continue trying addresses */
1052         result = CURLE_COULDNT_CONNECT;
1053       }
1054       goto out;
1055     }
1056   }
1057 #endif
1058 
1059   /* set socket non-blocking */
1060   (void)curlx_nonblock(ctx->sock, TRUE);
1061   ctx->sock_connected = (ctx->addr.socktype != SOCK_DGRAM);
1062 out:
1063   if(result) {
1064     if(ctx->sock != CURL_SOCKET_BAD) {
1065       socket_close(data, cf->conn, TRUE, ctx->sock);
1066       ctx->sock = CURL_SOCKET_BAD;
1067     }
1068   }
1069   else if(isconnected) {
1070     set_local_ip(cf, data);
1071     ctx->connected_at = Curl_now();
1072     cf->connected = TRUE;
1073   }
1074   CURL_TRC_CF(data, cf, "cf_socket_open() -> %d, fd=%" CURL_FORMAT_SOCKET_T,
1075               result, ctx->sock);
1076   return result;
1077 }
1078 
do_connect(struct Curl_cfilter * cf,struct Curl_easy * data,bool is_tcp_fastopen)1079 static int do_connect(struct Curl_cfilter *cf, struct Curl_easy *data,
1080                       bool is_tcp_fastopen)
1081 {
1082   struct cf_socket_ctx *ctx = cf->ctx;
1083 #ifdef TCP_FASTOPEN_CONNECT
1084   int optval = 1;
1085 #endif
1086   int rc = -1;
1087 
1088   (void)data;
1089   if(is_tcp_fastopen) {
1090 #if defined(CONNECT_DATA_IDEMPOTENT) /* Darwin */
1091 #  if defined(HAVE_BUILTIN_AVAILABLE)
1092     /* while connectx function is available since macOS 10.11 / iOS 9,
1093        it did not have the interface declared correctly until
1094        Xcode 9 / macOS SDK 10.13 */
1095     if(__builtin_available(macOS 10.11, iOS 9.0, tvOS 9.0, watchOS 2.0, *)) {
1096       sa_endpoints_t endpoints;
1097       endpoints.sae_srcif = 0;
1098       endpoints.sae_srcaddr = NULL;
1099       endpoints.sae_srcaddrlen = 0;
1100       endpoints.sae_dstaddr = &ctx->addr.sa_addr;
1101       endpoints.sae_dstaddrlen = ctx->addr.addrlen;
1102 
1103       rc = connectx(ctx->sock, &endpoints, SAE_ASSOCID_ANY,
1104                     CONNECT_RESUME_ON_READ_WRITE | CONNECT_DATA_IDEMPOTENT,
1105                     NULL, 0, NULL, NULL);
1106     }
1107     else {
1108       rc = connect(ctx->sock, &ctx->addr.sa_addr, ctx->addr.addrlen);
1109     }
1110 #  else
1111     rc = connect(ctx->sock, &ctx->addr.sa_addr, ctx->addr.addrlen);
1112 #  endif /* HAVE_BUILTIN_AVAILABLE */
1113 #elif defined(TCP_FASTOPEN_CONNECT) /* Linux >= 4.11 */
1114     if(setsockopt(ctx->sock, IPPROTO_TCP, TCP_FASTOPEN_CONNECT,
1115                   (void *)&optval, sizeof(optval)) < 0)
1116       infof(data, "Failed to enable TCP Fast Open on fd %"
1117             CURL_FORMAT_SOCKET_T, ctx->sock);
1118 
1119     rc = connect(ctx->sock, &ctx->addr.sa_addr, ctx->addr.addrlen);
1120 #elif defined(MSG_FASTOPEN) /* old Linux */
1121     if(cf->conn->given->flags & PROTOPT_SSL)
1122       rc = connect(ctx->sock, &ctx->addr.sa_addr, ctx->addr.addrlen);
1123     else
1124       rc = 0; /* Do nothing */
1125 #endif
1126   }
1127   else {
1128     rc = connect(ctx->sock, &ctx->addr.sa_addr, ctx->addr.addrlen);
1129   }
1130   return rc;
1131 }
1132 
cf_tcp_connect(struct Curl_cfilter * cf,struct Curl_easy * data,bool blocking,bool * done)1133 static CURLcode cf_tcp_connect(struct Curl_cfilter *cf,
1134                                struct Curl_easy *data,
1135                                bool blocking, bool *done)
1136 {
1137   struct cf_socket_ctx *ctx = cf->ctx;
1138   CURLcode result = CURLE_COULDNT_CONNECT;
1139   int rc = 0;
1140 
1141   (void)data;
1142   if(cf->connected) {
1143     *done = TRUE;
1144     return CURLE_OK;
1145   }
1146 
1147   /* TODO: need to support blocking connect? */
1148   if(blocking)
1149     return CURLE_UNSUPPORTED_PROTOCOL;
1150 
1151   *done = FALSE; /* a very negative world view is best */
1152   if(ctx->sock == CURL_SOCKET_BAD) {
1153     int error;
1154 
1155     result = cf_socket_open(cf, data);
1156     if(result)
1157       goto out;
1158 
1159     if(cf->connected) {
1160       *done = TRUE;
1161       return CURLE_OK;
1162     }
1163 
1164     /* Connect TCP socket */
1165     rc = do_connect(cf, data, cf->conn->bits.tcp_fastopen);
1166     error = SOCKERRNO;
1167     set_local_ip(cf, data);
1168     CURL_TRC_CF(data, cf, "local address %s port %d...",
1169                 ctx->l_ip, ctx->l_port);
1170     if(-1 == rc) {
1171       result = socket_connect_result(data, ctx->r_ip, error);
1172       goto out;
1173     }
1174   }
1175 
1176 #ifdef mpeix
1177   /* Call this function once now, and ignore the results. We do this to
1178      "clear" the error state on the socket so that we can later read it
1179      reliably. This is reported necessary on the MPE/iX operating
1180      system. */
1181   (void)verifyconnect(ctx->sock, NULL);
1182 #endif
1183   /* check socket for connect */
1184   rc = SOCKET_WRITABLE(ctx->sock, 0);
1185 
1186   if(rc == 0) { /* no connection yet */
1187     CURL_TRC_CF(data, cf, "not connected yet");
1188     return CURLE_OK;
1189   }
1190   else if(rc == CURL_CSELECT_OUT || cf->conn->bits.tcp_fastopen) {
1191     if(verifyconnect(ctx->sock, &ctx->error)) {
1192       /* we are connected with TCP, awesome! */
1193       ctx->connected_at = Curl_now();
1194       set_local_ip(cf, data);
1195       *done = TRUE;
1196       cf->connected = TRUE;
1197       CURL_TRC_CF(data, cf, "connected");
1198       return CURLE_OK;
1199     }
1200   }
1201   else if(rc & CURL_CSELECT_ERR) {
1202     (void)verifyconnect(ctx->sock, &ctx->error);
1203     result = CURLE_COULDNT_CONNECT;
1204   }
1205 
1206 out:
1207   if(result) {
1208     if(ctx->error) {
1209       set_local_ip(cf, data);
1210       data->state.os_errno = ctx->error;
1211       SET_SOCKERRNO(ctx->error);
1212 #ifndef CURL_DISABLE_VERBOSE_STRINGS
1213       {
1214         char buffer[STRERROR_LEN];
1215         infof(data, "connect to %s port %u from %s port %d failed: %s",
1216               ctx->r_ip, ctx->r_port, ctx->l_ip, ctx->l_port,
1217               Curl_strerror(ctx->error, buffer, sizeof(buffer)));
1218       }
1219 #endif
1220     }
1221     if(ctx->sock != CURL_SOCKET_BAD) {
1222       socket_close(data, cf->conn, TRUE, ctx->sock);
1223       ctx->sock = CURL_SOCKET_BAD;
1224     }
1225     *done = FALSE;
1226   }
1227   return result;
1228 }
1229 
cf_socket_get_host(struct Curl_cfilter * cf,struct Curl_easy * data,const char ** phost,const char ** pdisplay_host,int * pport)1230 static void cf_socket_get_host(struct Curl_cfilter *cf,
1231                                struct Curl_easy *data,
1232                                const char **phost,
1233                                const char **pdisplay_host,
1234                                int *pport)
1235 {
1236   (void)data;
1237   *phost = cf->conn->host.name;
1238   *pdisplay_host = cf->conn->host.dispname;
1239   *pport = cf->conn->port;
1240 }
1241 
cf_socket_adjust_pollset(struct Curl_cfilter * cf,struct Curl_easy * data,struct easy_pollset * ps)1242 static void cf_socket_adjust_pollset(struct Curl_cfilter *cf,
1243                                       struct Curl_easy *data,
1244                                       struct easy_pollset *ps)
1245 {
1246   struct cf_socket_ctx *ctx = cf->ctx;
1247 
1248   if(ctx->sock != CURL_SOCKET_BAD) {
1249     if(!cf->connected) {
1250       Curl_pollset_set_out_only(data, ps, ctx->sock);
1251       CURL_TRC_CF(data, cf, "adjust_pollset(!connected) -> %d socks", ps->num);
1252     }
1253     else if(!ctx->active) {
1254       Curl_pollset_add_in(data, ps, ctx->sock);
1255       CURL_TRC_CF(data, cf, "adjust_pollset(!active) -> %d socks", ps->num);
1256     }
1257   }
1258 }
1259 
cf_socket_data_pending(struct Curl_cfilter * cf,const struct Curl_easy * data)1260 static bool cf_socket_data_pending(struct Curl_cfilter *cf,
1261                                    const struct Curl_easy *data)
1262 {
1263   struct cf_socket_ctx *ctx = cf->ctx;
1264   int readable;
1265 
1266   (void)data;
1267   if(!Curl_bufq_is_empty(&ctx->recvbuf))
1268     return TRUE;
1269 
1270   readable = SOCKET_READABLE(ctx->sock, 0);
1271   return (readable > 0 && (readable & CURL_CSELECT_IN));
1272 }
1273 
cf_socket_send(struct Curl_cfilter * cf,struct Curl_easy * data,const void * buf,size_t len,CURLcode * err)1274 static ssize_t cf_socket_send(struct Curl_cfilter *cf, struct Curl_easy *data,
1275                               const void *buf, size_t len, CURLcode *err)
1276 {
1277   struct cf_socket_ctx *ctx = cf->ctx;
1278   curl_socket_t fdsave;
1279   ssize_t nwritten;
1280   size_t orig_len = len;
1281 
1282   *err = CURLE_OK;
1283   fdsave = cf->conn->sock[cf->sockindex];
1284   cf->conn->sock[cf->sockindex] = ctx->sock;
1285 
1286 #ifdef DEBUGBUILD
1287   /* simulate network blocking/partial writes */
1288   if(ctx->wblock_percent > 0) {
1289     unsigned char c;
1290     Curl_rand(data, &c, 1);
1291     if(c >= ((100-ctx->wblock_percent)*256/100)) {
1292       CURL_TRC_CF(data, cf, "send(len=%zu) SIMULATE EWOULDBLOCK", orig_len);
1293       *err = CURLE_AGAIN;
1294       nwritten = -1;
1295       cf->conn->sock[cf->sockindex] = fdsave;
1296       return nwritten;
1297     }
1298   }
1299   if(cf->cft != &Curl_cft_udp && ctx->wpartial_percent > 0 && len > 8) {
1300     len = len * ctx->wpartial_percent / 100;
1301     if(!len)
1302       len = 1;
1303     CURL_TRC_CF(data, cf, "send(len=%zu) SIMULATE partial write of %zu bytes",
1304                 orig_len, len);
1305   }
1306 #endif
1307 
1308 #if defined(MSG_FASTOPEN) && !defined(TCP_FASTOPEN_CONNECT) /* Linux */
1309   if(cf->conn->bits.tcp_fastopen) {
1310     nwritten = sendto(ctx->sock, buf, len, MSG_FASTOPEN,
1311                       &cf->conn->remote_addr->sa_addr,
1312                       cf->conn->remote_addr->addrlen);
1313     cf->conn->bits.tcp_fastopen = FALSE;
1314   }
1315   else
1316 #endif
1317     nwritten = swrite(ctx->sock, buf, len);
1318 
1319   if(-1 == nwritten) {
1320     int sockerr = SOCKERRNO;
1321 
1322     if(
1323 #ifdef WSAEWOULDBLOCK
1324       /* This is how Windows does it */
1325       (WSAEWOULDBLOCK == sockerr)
1326 #else
1327       /* errno may be EWOULDBLOCK or on some systems EAGAIN when it returned
1328          due to its inability to send off data without blocking. We therefore
1329          treat both error codes the same here */
1330       (EWOULDBLOCK == sockerr) || (EAGAIN == sockerr) || (EINTR == sockerr) ||
1331       (EINPROGRESS == sockerr)
1332 #endif
1333       ) {
1334       /* this is just a case of EWOULDBLOCK */
1335       *err = CURLE_AGAIN;
1336     }
1337     else {
1338       char buffer[STRERROR_LEN];
1339       failf(data, "Send failure: %s",
1340             Curl_strerror(sockerr, buffer, sizeof(buffer)));
1341       data->state.os_errno = sockerr;
1342       *err = CURLE_SEND_ERROR;
1343     }
1344   }
1345 
1346   CURL_TRC_CF(data, cf, "send(len=%zu) -> %d, err=%d",
1347               orig_len, (int)nwritten, *err);
1348   cf->conn->sock[cf->sockindex] = fdsave;
1349   return nwritten;
1350 }
1351 
cf_socket_recv(struct Curl_cfilter * cf,struct Curl_easy * data,char * buf,size_t len,CURLcode * err)1352 static ssize_t cf_socket_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
1353                               char *buf, size_t len, CURLcode *err)
1354 {
1355   struct cf_socket_ctx *ctx = cf->ctx;
1356   curl_socket_t fdsave;
1357   ssize_t nread;
1358 
1359   *err = CURLE_OK;
1360 
1361   fdsave = cf->conn->sock[cf->sockindex];
1362   cf->conn->sock[cf->sockindex] = ctx->sock;
1363 
1364 #ifdef DEBUGBUILD
1365   /* simulate network blocking/partial reads */
1366   if(cf->cft != &Curl_cft_udp && ctx->rblock_percent > 0) {
1367     unsigned char c;
1368     Curl_rand(data, &c, 1);
1369     if(c >= ((100-ctx->rblock_percent)*256/100)) {
1370       CURL_TRC_CF(data, cf, "recv(len=%zu) SIMULATE EWOULDBLOCK", len);
1371       *err = CURLE_AGAIN;
1372       nread = -1;
1373       cf->conn->sock[cf->sockindex] = fdsave;
1374       return nread;
1375     }
1376   }
1377   if(cf->cft != &Curl_cft_udp && ctx->recv_max && ctx->recv_max < len) {
1378     size_t orig_len = len;
1379     len = ctx->recv_max;
1380     CURL_TRC_CF(data, cf, "recv(len=%zu) SIMULATE max read of %zu bytes",
1381                 orig_len, len);
1382   }
1383 #endif
1384 
1385   if(ctx->buffer_recv && !Curl_bufq_is_empty(&ctx->recvbuf)) {
1386     CURL_TRC_CF(data, cf, "recv from buffer");
1387     nread = Curl_bufq_read(&ctx->recvbuf, (unsigned char *)buf, len, err);
1388   }
1389   else {
1390     struct reader_ctx rctx;
1391 
1392     rctx.cf = cf;
1393     rctx.data = data;
1394 
1395     /* "small" reads may trigger filling our buffer, "large" reads
1396      * are probably not worth the additional copy */
1397     if(ctx->buffer_recv && len < NW_SMALL_READS) {
1398       ssize_t nwritten;
1399       nwritten = Curl_bufq_slurp(&ctx->recvbuf, nw_in_read, &rctx, err);
1400       if(nwritten < 0 && !Curl_bufq_is_empty(&ctx->recvbuf)) {
1401         /* we have a partial read with an error. need to deliver
1402          * what we got, return the error later. */
1403         CURL_TRC_CF(data, cf, "partial read: empty buffer first");
1404         nread = Curl_bufq_read(&ctx->recvbuf, (unsigned char *)buf, len, err);
1405       }
1406       else if(nwritten < 0) {
1407         nread = -1;
1408         goto out;
1409       }
1410       else if(nwritten == 0) {
1411         /* eof */
1412         *err = CURLE_OK;
1413         nread = 0;
1414       }
1415       else {
1416         CURL_TRC_CF(data, cf, "buffered %zd additional bytes", nwritten);
1417         nread = Curl_bufq_read(&ctx->recvbuf, (unsigned char *)buf, len, err);
1418       }
1419     }
1420     else {
1421       nread = nw_in_read(&rctx, (unsigned char *)buf, len, err);
1422     }
1423   }
1424 
1425 out:
1426   CURL_TRC_CF(data, cf, "recv(len=%zu) -> %d, err=%d", len, (int)nread,
1427               *err);
1428   if(nread > 0 && !ctx->got_first_byte) {
1429     ctx->first_byte_at = Curl_now();
1430     ctx->got_first_byte = TRUE;
1431   }
1432   cf->conn->sock[cf->sockindex] = fdsave;
1433   return nread;
1434 }
1435 
conn_set_primary_ip(struct Curl_cfilter * cf,struct Curl_easy * data)1436 static void conn_set_primary_ip(struct Curl_cfilter *cf,
1437                                 struct Curl_easy *data)
1438 {
1439   struct cf_socket_ctx *ctx = cf->ctx;
1440 
1441   (void)data;
1442   DEBUGASSERT(sizeof(ctx->r_ip) == sizeof(cf->conn->primary_ip));
1443   memcpy(cf->conn->primary_ip, ctx->r_ip, sizeof(cf->conn->primary_ip));
1444 }
1445 
cf_socket_active(struct Curl_cfilter * cf,struct Curl_easy * data)1446 static void cf_socket_active(struct Curl_cfilter *cf, struct Curl_easy *data)
1447 {
1448   struct cf_socket_ctx *ctx = cf->ctx;
1449 
1450   /* use this socket from now on */
1451   cf->conn->sock[cf->sockindex] = ctx->sock;
1452   /* the first socket info gets set at conn and data */
1453   if(cf->sockindex == FIRSTSOCKET) {
1454     cf->conn->remote_addr = &ctx->addr;
1455   #ifdef ENABLE_IPV6
1456     cf->conn->bits.ipv6 = (ctx->addr.family == AF_INET6)? TRUE : FALSE;
1457   #endif
1458     conn_set_primary_ip(cf, data);
1459     set_local_ip(cf, data);
1460     Curl_persistconninfo(data, cf->conn, ctx->l_ip, ctx->l_port);
1461     /* buffering is currently disabled by default because we have stalls
1462      * in parallel transfers where not all buffered data is consumed and no
1463      * socket events happen.
1464      */
1465     ctx->buffer_recv = FALSE;
1466   }
1467   ctx->active = TRUE;
1468 }
1469 
cf_socket_cntrl(struct Curl_cfilter * cf,struct Curl_easy * data,int event,int arg1,void * arg2)1470 static CURLcode cf_socket_cntrl(struct Curl_cfilter *cf,
1471                                 struct Curl_easy *data,
1472                                 int event, int arg1, void *arg2)
1473 {
1474   struct cf_socket_ctx *ctx = cf->ctx;
1475 
1476   (void)arg1;
1477   (void)arg2;
1478   switch(event) {
1479   case CF_CTRL_CONN_INFO_UPDATE:
1480     cf_socket_active(cf, data);
1481     break;
1482   case CF_CTRL_DATA_SETUP:
1483     Curl_persistconninfo(data, cf->conn, ctx->l_ip, ctx->l_port);
1484     break;
1485   case CF_CTRL_FORGET_SOCKET:
1486     ctx->sock = CURL_SOCKET_BAD;
1487     break;
1488   }
1489   return CURLE_OK;
1490 }
1491 
cf_socket_conn_is_alive(struct Curl_cfilter * cf,struct Curl_easy * data,bool * input_pending)1492 static bool cf_socket_conn_is_alive(struct Curl_cfilter *cf,
1493                                     struct Curl_easy *data,
1494                                     bool *input_pending)
1495 {
1496   struct cf_socket_ctx *ctx = cf->ctx;
1497   struct pollfd pfd[1];
1498   int r;
1499 
1500   *input_pending = FALSE;
1501   (void)data;
1502   if(!ctx || ctx->sock == CURL_SOCKET_BAD)
1503     return FALSE;
1504 
1505   /* Check with 0 timeout if there are any events pending on the socket */
1506   pfd[0].fd = ctx->sock;
1507   pfd[0].events = POLLRDNORM|POLLIN|POLLRDBAND|POLLPRI;
1508   pfd[0].revents = 0;
1509 
1510   r = Curl_poll(pfd, 1, 0);
1511   if(r < 0) {
1512     CURL_TRC_CF(data, cf, "is_alive: poll error, assume dead");
1513     return FALSE;
1514   }
1515   else if(r == 0) {
1516     CURL_TRC_CF(data, cf, "is_alive: poll timeout, assume alive");
1517     return TRUE;
1518   }
1519   else if(pfd[0].revents & (POLLERR|POLLHUP|POLLPRI|POLLNVAL)) {
1520     CURL_TRC_CF(data, cf, "is_alive: err/hup/etc events, assume dead");
1521     return FALSE;
1522   }
1523 
1524   CURL_TRC_CF(data, cf, "is_alive: valid events, looks alive");
1525   *input_pending = TRUE;
1526   return TRUE;
1527 }
1528 
cf_socket_query(struct Curl_cfilter * cf,struct Curl_easy * data,int query,int * pres1,void * pres2)1529 static CURLcode cf_socket_query(struct Curl_cfilter *cf,
1530                                 struct Curl_easy *data,
1531                                 int query, int *pres1, void *pres2)
1532 {
1533   struct cf_socket_ctx *ctx = cf->ctx;
1534 
1535   switch(query) {
1536   case CF_QUERY_SOCKET:
1537     DEBUGASSERT(pres2);
1538     *((curl_socket_t *)pres2) = ctx->sock;
1539     return CURLE_OK;
1540   case CF_QUERY_CONNECT_REPLY_MS:
1541     if(ctx->got_first_byte) {
1542       timediff_t ms = Curl_timediff(ctx->first_byte_at, ctx->started_at);
1543       *pres1 = (ms < INT_MAX)? (int)ms : INT_MAX;
1544     }
1545     else
1546       *pres1 = -1;
1547     return CURLE_OK;
1548   case CF_QUERY_TIMER_CONNECT: {
1549     struct curltime *when = pres2;
1550     switch(ctx->transport) {
1551     case TRNSPRT_UDP:
1552     case TRNSPRT_QUIC:
1553       /* Since UDP connected sockets work different from TCP, we use the
1554        * time of the first byte from the peer as the "connect" time. */
1555       if(ctx->got_first_byte) {
1556         *when = ctx->first_byte_at;
1557         break;
1558       }
1559       FALLTHROUGH();
1560     default:
1561       *when = ctx->connected_at;
1562       break;
1563     }
1564     return CURLE_OK;
1565   }
1566   default:
1567     break;
1568   }
1569   return cf->next?
1570     cf->next->cft->query(cf->next, data, query, pres1, pres2) :
1571     CURLE_UNKNOWN_OPTION;
1572 }
1573 
1574 struct Curl_cftype Curl_cft_tcp = {
1575   "TCP",
1576   CF_TYPE_IP_CONNECT,
1577   CURL_LOG_LVL_NONE,
1578   cf_socket_destroy,
1579   cf_tcp_connect,
1580   cf_socket_close,
1581   cf_socket_get_host,
1582   cf_socket_adjust_pollset,
1583   cf_socket_data_pending,
1584   cf_socket_send,
1585   cf_socket_recv,
1586   cf_socket_cntrl,
1587   cf_socket_conn_is_alive,
1588   Curl_cf_def_conn_keep_alive,
1589   cf_socket_query,
1590 };
1591 
Curl_cf_tcp_create(struct Curl_cfilter ** pcf,struct Curl_easy * data,struct connectdata * conn,const struct Curl_addrinfo * ai,int transport)1592 CURLcode Curl_cf_tcp_create(struct Curl_cfilter **pcf,
1593                             struct Curl_easy *data,
1594                             struct connectdata *conn,
1595                             const struct Curl_addrinfo *ai,
1596                             int transport)
1597 {
1598   struct cf_socket_ctx *ctx = NULL;
1599   struct Curl_cfilter *cf = NULL;
1600   CURLcode result;
1601 
1602   (void)data;
1603   (void)conn;
1604   DEBUGASSERT(transport == TRNSPRT_TCP);
1605   ctx = calloc(1, sizeof(*ctx));
1606   if(!ctx) {
1607     result = CURLE_OUT_OF_MEMORY;
1608     goto out;
1609   }
1610   cf_socket_ctx_init(ctx, ai, transport);
1611 
1612   result = Curl_cf_create(&cf, &Curl_cft_tcp, ctx);
1613 
1614 out:
1615   *pcf = (!result)? cf : NULL;
1616   if(result) {
1617     Curl_safefree(cf);
1618     Curl_safefree(ctx);
1619   }
1620 
1621   return result;
1622 }
1623 
cf_udp_setup_quic(struct Curl_cfilter * cf,struct Curl_easy * data)1624 static CURLcode cf_udp_setup_quic(struct Curl_cfilter *cf,
1625                                struct Curl_easy *data)
1626 {
1627   struct cf_socket_ctx *ctx = cf->ctx;
1628   int rc;
1629 
1630   /* QUIC needs a connected socket, nonblocking */
1631   DEBUGASSERT(ctx->sock != CURL_SOCKET_BAD);
1632 
1633 #if defined(__APPLE__) && defined(USE_OPENSSL_QUIC)
1634   (void)rc;
1635   /* On macOS OpenSSL QUIC fails on connected sockets.
1636    * see: <https://github.com/openssl/openssl/issues/23251> */
1637 #else
1638   rc = connect(ctx->sock, &ctx->addr.sa_addr, ctx->addr.addrlen);
1639   if(-1 == rc) {
1640     return socket_connect_result(data, ctx->r_ip, SOCKERRNO);
1641   }
1642   ctx->sock_connected = TRUE;
1643 #endif
1644   set_local_ip(cf, data);
1645   CURL_TRC_CF(data, cf, "%s socket %" CURL_FORMAT_SOCKET_T
1646               " connected: [%s:%d] -> [%s:%d]",
1647               (ctx->transport == TRNSPRT_QUIC)? "QUIC" : "UDP",
1648               ctx->sock, ctx->l_ip, ctx->l_port, ctx->r_ip, ctx->r_port);
1649 
1650   (void)curlx_nonblock(ctx->sock, TRUE);
1651   switch(ctx->addr.family) {
1652 #if defined(__linux__) && defined(IP_MTU_DISCOVER)
1653   case AF_INET: {
1654     int val = IP_PMTUDISC_DO;
1655     (void)setsockopt(ctx->sock, IPPROTO_IP, IP_MTU_DISCOVER, &val,
1656                      sizeof(val));
1657     break;
1658   }
1659 #endif
1660 #if defined(__linux__) && defined(IPV6_MTU_DISCOVER)
1661   case AF_INET6: {
1662     int val = IPV6_PMTUDISC_DO;
1663     (void)setsockopt(ctx->sock, IPPROTO_IPV6, IPV6_MTU_DISCOVER, &val,
1664                      sizeof(val));
1665     break;
1666   }
1667 #endif
1668   }
1669   return CURLE_OK;
1670 }
1671 
cf_udp_connect(struct Curl_cfilter * cf,struct Curl_easy * data,bool blocking,bool * done)1672 static CURLcode cf_udp_connect(struct Curl_cfilter *cf,
1673                                struct Curl_easy *data,
1674                                bool blocking, bool *done)
1675 {
1676   struct cf_socket_ctx *ctx = cf->ctx;
1677   CURLcode result = CURLE_COULDNT_CONNECT;
1678 
1679   (void)blocking;
1680   if(cf->connected) {
1681     *done = TRUE;
1682     return CURLE_OK;
1683   }
1684   *done = FALSE;
1685   if(ctx->sock == CURL_SOCKET_BAD) {
1686     result = cf_socket_open(cf, data);
1687     if(result) {
1688       CURL_TRC_CF(data, cf, "cf_udp_connect(), open failed -> %d", result);
1689       goto out;
1690     }
1691 
1692     if(ctx->transport == TRNSPRT_QUIC) {
1693       result = cf_udp_setup_quic(cf, data);
1694       if(result)
1695         goto out;
1696       CURL_TRC_CF(data, cf, "cf_udp_connect(), opened socket=%"
1697                   CURL_FORMAT_SOCKET_T " (%s:%d)",
1698                   ctx->sock, ctx->l_ip, ctx->l_port);
1699     }
1700     else {
1701       CURL_TRC_CF(data, cf, "cf_udp_connect(), opened socket=%"
1702                   CURL_FORMAT_SOCKET_T " (unconnected)", ctx->sock);
1703     }
1704     *done = TRUE;
1705     cf->connected = TRUE;
1706   }
1707 out:
1708   return result;
1709 }
1710 
1711 struct Curl_cftype Curl_cft_udp = {
1712   "UDP",
1713   CF_TYPE_IP_CONNECT,
1714   CURL_LOG_LVL_NONE,
1715   cf_socket_destroy,
1716   cf_udp_connect,
1717   cf_socket_close,
1718   cf_socket_get_host,
1719   cf_socket_adjust_pollset,
1720   cf_socket_data_pending,
1721   cf_socket_send,
1722   cf_socket_recv,
1723   cf_socket_cntrl,
1724   cf_socket_conn_is_alive,
1725   Curl_cf_def_conn_keep_alive,
1726   cf_socket_query,
1727 };
1728 
Curl_cf_udp_create(struct Curl_cfilter ** pcf,struct Curl_easy * data,struct connectdata * conn,const struct Curl_addrinfo * ai,int transport)1729 CURLcode Curl_cf_udp_create(struct Curl_cfilter **pcf,
1730                             struct Curl_easy *data,
1731                             struct connectdata *conn,
1732                             const struct Curl_addrinfo *ai,
1733                             int transport)
1734 {
1735   struct cf_socket_ctx *ctx = NULL;
1736   struct Curl_cfilter *cf = NULL;
1737   CURLcode result;
1738 
1739   (void)data;
1740   (void)conn;
1741   DEBUGASSERT(transport == TRNSPRT_UDP || transport == TRNSPRT_QUIC);
1742   ctx = calloc(1, sizeof(*ctx));
1743   if(!ctx) {
1744     result = CURLE_OUT_OF_MEMORY;
1745     goto out;
1746   }
1747   cf_socket_ctx_init(ctx, ai, transport);
1748 
1749   result = Curl_cf_create(&cf, &Curl_cft_udp, ctx);
1750 
1751 out:
1752   *pcf = (!result)? cf : NULL;
1753   if(result) {
1754     Curl_safefree(cf);
1755     Curl_safefree(ctx);
1756   }
1757 
1758   return result;
1759 }
1760 
1761 /* this is the TCP filter which can also handle this case */
1762 struct Curl_cftype Curl_cft_unix = {
1763   "UNIX",
1764   CF_TYPE_IP_CONNECT,
1765   CURL_LOG_LVL_NONE,
1766   cf_socket_destroy,
1767   cf_tcp_connect,
1768   cf_socket_close,
1769   cf_socket_get_host,
1770   cf_socket_adjust_pollset,
1771   cf_socket_data_pending,
1772   cf_socket_send,
1773   cf_socket_recv,
1774   cf_socket_cntrl,
1775   cf_socket_conn_is_alive,
1776   Curl_cf_def_conn_keep_alive,
1777   cf_socket_query,
1778 };
1779 
Curl_cf_unix_create(struct Curl_cfilter ** pcf,struct Curl_easy * data,struct connectdata * conn,const struct Curl_addrinfo * ai,int transport)1780 CURLcode Curl_cf_unix_create(struct Curl_cfilter **pcf,
1781                              struct Curl_easy *data,
1782                              struct connectdata *conn,
1783                              const struct Curl_addrinfo *ai,
1784                              int transport)
1785 {
1786   struct cf_socket_ctx *ctx = NULL;
1787   struct Curl_cfilter *cf = NULL;
1788   CURLcode result;
1789 
1790   (void)data;
1791   (void)conn;
1792   DEBUGASSERT(transport == TRNSPRT_UNIX);
1793   ctx = calloc(1, sizeof(*ctx));
1794   if(!ctx) {
1795     result = CURLE_OUT_OF_MEMORY;
1796     goto out;
1797   }
1798   cf_socket_ctx_init(ctx, ai, transport);
1799 
1800   result = Curl_cf_create(&cf, &Curl_cft_unix, ctx);
1801 
1802 out:
1803   *pcf = (!result)? cf : NULL;
1804   if(result) {
1805     Curl_safefree(cf);
1806     Curl_safefree(ctx);
1807   }
1808 
1809   return result;
1810 }
1811 
cf_tcp_accept_connect(struct Curl_cfilter * cf,struct Curl_easy * data,bool blocking,bool * done)1812 static CURLcode cf_tcp_accept_connect(struct Curl_cfilter *cf,
1813                                       struct Curl_easy *data,
1814                                       bool blocking, bool *done)
1815 {
1816   /* we start accepted, if we ever close, we cannot go on */
1817   (void)data;
1818   (void)blocking;
1819   if(cf->connected) {
1820     *done = TRUE;
1821     return CURLE_OK;
1822   }
1823   return CURLE_FAILED_INIT;
1824 }
1825 
1826 struct Curl_cftype Curl_cft_tcp_accept = {
1827   "TCP-ACCEPT",
1828   CF_TYPE_IP_CONNECT,
1829   CURL_LOG_LVL_NONE,
1830   cf_socket_destroy,
1831   cf_tcp_accept_connect,
1832   cf_socket_close,
1833   cf_socket_get_host,              /* TODO: not accurate */
1834   cf_socket_adjust_pollset,
1835   cf_socket_data_pending,
1836   cf_socket_send,
1837   cf_socket_recv,
1838   cf_socket_cntrl,
1839   cf_socket_conn_is_alive,
1840   Curl_cf_def_conn_keep_alive,
1841   cf_socket_query,
1842 };
1843 
Curl_conn_tcp_listen_set(struct Curl_easy * data,struct connectdata * conn,int sockindex,curl_socket_t * s)1844 CURLcode Curl_conn_tcp_listen_set(struct Curl_easy *data,
1845                                   struct connectdata *conn,
1846                                   int sockindex, curl_socket_t *s)
1847 {
1848   CURLcode result;
1849   struct Curl_cfilter *cf = NULL;
1850   struct cf_socket_ctx *ctx = NULL;
1851 
1852   /* replace any existing */
1853   Curl_conn_cf_discard_all(data, conn, sockindex);
1854   DEBUGASSERT(conn->sock[sockindex] == CURL_SOCKET_BAD);
1855 
1856   ctx = calloc(1, sizeof(*ctx));
1857   if(!ctx) {
1858     result = CURLE_OUT_OF_MEMORY;
1859     goto out;
1860   }
1861   ctx->transport = conn->transport;
1862   ctx->sock = *s;
1863   ctx->accepted = FALSE;
1864   result = Curl_cf_create(&cf, &Curl_cft_tcp_accept, ctx);
1865   if(result)
1866     goto out;
1867   Curl_conn_cf_add(data, conn, sockindex, cf);
1868 
1869   conn->sock[sockindex] = ctx->sock;
1870   set_local_ip(cf, data);
1871   ctx->active = TRUE;
1872   ctx->connected_at = Curl_now();
1873   cf->connected = TRUE;
1874   CURL_TRC_CF(data, cf, "Curl_conn_tcp_listen_set(%"
1875               CURL_FORMAT_SOCKET_T ")", ctx->sock);
1876 
1877 out:
1878   if(result) {
1879     Curl_safefree(cf);
1880     Curl_safefree(ctx);
1881   }
1882   return result;
1883 }
1884 
set_accepted_remote_ip(struct Curl_cfilter * cf,struct Curl_easy * data)1885 static void set_accepted_remote_ip(struct Curl_cfilter *cf,
1886                                    struct Curl_easy *data)
1887 {
1888   struct cf_socket_ctx *ctx = cf->ctx;
1889 #ifdef HAVE_GETPEERNAME
1890   char buffer[STRERROR_LEN];
1891   struct Curl_sockaddr_storage ssrem;
1892   curl_socklen_t plen;
1893 
1894   ctx->r_ip[0] = 0;
1895   ctx->r_port = 0;
1896   plen = sizeof(ssrem);
1897   memset(&ssrem, 0, plen);
1898   if(getpeername(ctx->sock, (struct sockaddr*) &ssrem, &plen)) {
1899     int error = SOCKERRNO;
1900     failf(data, "getpeername() failed with errno %d: %s",
1901           error, Curl_strerror(error, buffer, sizeof(buffer)));
1902     return;
1903   }
1904   if(!Curl_addr2string((struct sockaddr*)&ssrem, plen,
1905                        ctx->r_ip, &ctx->r_port)) {
1906     failf(data, "ssrem inet_ntop() failed with errno %d: %s",
1907           errno, Curl_strerror(errno, buffer, sizeof(buffer)));
1908     return;
1909   }
1910 #else
1911   ctx->r_ip[0] = 0;
1912   ctx->r_port = 0;
1913   (void)data;
1914 #endif
1915 }
1916 
Curl_conn_tcp_accepted_set(struct Curl_easy * data,struct connectdata * conn,int sockindex,curl_socket_t * s)1917 CURLcode Curl_conn_tcp_accepted_set(struct Curl_easy *data,
1918                                     struct connectdata *conn,
1919                                     int sockindex, curl_socket_t *s)
1920 {
1921   struct Curl_cfilter *cf = NULL;
1922   struct cf_socket_ctx *ctx = NULL;
1923 
1924   cf = conn->cfilter[sockindex];
1925   if(!cf || cf->cft != &Curl_cft_tcp_accept)
1926     return CURLE_FAILED_INIT;
1927 
1928   ctx = cf->ctx;
1929   /* discard the listen socket */
1930   socket_close(data, conn, TRUE, ctx->sock);
1931   ctx->sock = *s;
1932   conn->sock[sockindex] = ctx->sock;
1933   set_accepted_remote_ip(cf, data);
1934   set_local_ip(cf, data);
1935   ctx->active = TRUE;
1936   ctx->accepted = TRUE;
1937   ctx->connected_at = Curl_now();
1938   cf->connected = TRUE;
1939   CURL_TRC_CF(data, cf, "accepted_set(sock=%" CURL_FORMAT_SOCKET_T
1940               ", remote=%s port=%d)",
1941               ctx->sock, ctx->r_ip, ctx->r_port);
1942 
1943   return CURLE_OK;
1944 }
1945 
1946 /**
1947  * Return TRUE iff `cf` is a socket filter.
1948  */
cf_is_socket(struct Curl_cfilter * cf)1949 static bool cf_is_socket(struct Curl_cfilter *cf)
1950 {
1951   return cf && (cf->cft == &Curl_cft_tcp ||
1952                 cf->cft == &Curl_cft_udp ||
1953                 cf->cft == &Curl_cft_unix ||
1954                 cf->cft == &Curl_cft_tcp_accept);
1955 }
1956 
Curl_cf_socket_peek(struct Curl_cfilter * cf,struct Curl_easy * data,curl_socket_t * psock,const struct Curl_sockaddr_ex ** paddr,const char ** pr_ip_str,int * pr_port,const char ** pl_ip_str,int * pl_port)1957 CURLcode Curl_cf_socket_peek(struct Curl_cfilter *cf,
1958                              struct Curl_easy *data,
1959                              curl_socket_t *psock,
1960                              const struct Curl_sockaddr_ex **paddr,
1961                              const char **pr_ip_str, int *pr_port,
1962                              const char **pl_ip_str, int *pl_port)
1963 {
1964   if(cf_is_socket(cf) && cf->ctx) {
1965     struct cf_socket_ctx *ctx = cf->ctx;
1966 
1967     if(psock)
1968       *psock = ctx->sock;
1969     if(paddr)
1970       *paddr = &ctx->addr;
1971     if(pr_ip_str)
1972       *pr_ip_str = ctx->r_ip;
1973     if(pr_port)
1974       *pr_port = ctx->r_port;
1975     if(pl_port ||pl_ip_str) {
1976       set_local_ip(cf, data);
1977       if(pl_ip_str)
1978         *pl_ip_str = ctx->l_ip;
1979       if(pl_port)
1980         *pl_port = ctx->l_port;
1981     }
1982     return CURLE_OK;
1983   }
1984   return CURLE_FAILED_INIT;
1985 }
1986