113498266Sopenharmony_ci/***************************************************************************
213498266Sopenharmony_ci *                                  _   _ ____  _
313498266Sopenharmony_ci *  Project                     ___| | | |  _ \| |
413498266Sopenharmony_ci *                             / __| | | | |_) | |
513498266Sopenharmony_ci *                            | (__| |_| |  _ <| |___
613498266Sopenharmony_ci *                             \___|\___/|_| \_\_____|
713498266Sopenharmony_ci *
813498266Sopenharmony_ci * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
913498266Sopenharmony_ci *
1013498266Sopenharmony_ci * This software is licensed as described in the file COPYING, which
1113498266Sopenharmony_ci * you should have received as part of this distribution. The terms
1213498266Sopenharmony_ci * are also available at https://curl.se/docs/copyright.html.
1313498266Sopenharmony_ci *
1413498266Sopenharmony_ci * You may opt to use, copy, modify, merge, publish, distribute and/or sell
1513498266Sopenharmony_ci * copies of the Software, and permit persons to whom the Software is
1613498266Sopenharmony_ci * furnished to do so, under the terms of the COPYING file.
1713498266Sopenharmony_ci *
1813498266Sopenharmony_ci * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
1913498266Sopenharmony_ci * KIND, either express or implied.
2013498266Sopenharmony_ci *
2113498266Sopenharmony_ci * SPDX-License-Identifier: curl
2213498266Sopenharmony_ci *
2313498266Sopenharmony_ci ***************************************************************************/
2413498266Sopenharmony_ci
2513498266Sopenharmony_ci#include "curl_setup.h"
2613498266Sopenharmony_ci
2713498266Sopenharmony_ci#ifdef HAVE_NETINET_IN_H
2813498266Sopenharmony_ci#include <netinet/in.h> /* <netinet/tcp.h> may need it */
2913498266Sopenharmony_ci#endif
3013498266Sopenharmony_ci#ifdef HAVE_SYS_UN_H
3113498266Sopenharmony_ci#include <sys/un.h> /* for sockaddr_un */
3213498266Sopenharmony_ci#endif
3313498266Sopenharmony_ci#ifdef HAVE_LINUX_TCP_H
3413498266Sopenharmony_ci#include <linux/tcp.h>
3513498266Sopenharmony_ci#elif defined(HAVE_NETINET_TCP_H)
3613498266Sopenharmony_ci#include <netinet/tcp.h>
3713498266Sopenharmony_ci#endif
3813498266Sopenharmony_ci#ifdef HAVE_SYS_IOCTL_H
3913498266Sopenharmony_ci#include <sys/ioctl.h>
4013498266Sopenharmony_ci#endif
4113498266Sopenharmony_ci#ifdef HAVE_NETDB_H
4213498266Sopenharmony_ci#include <netdb.h>
4313498266Sopenharmony_ci#endif
4413498266Sopenharmony_ci#ifdef HAVE_FCNTL_H
4513498266Sopenharmony_ci#include <fcntl.h>
4613498266Sopenharmony_ci#endif
4713498266Sopenharmony_ci#ifdef HAVE_ARPA_INET_H
4813498266Sopenharmony_ci#include <arpa/inet.h>
4913498266Sopenharmony_ci#endif
5013498266Sopenharmony_ci
5113498266Sopenharmony_ci#ifdef __VMS
5213498266Sopenharmony_ci#include <in.h>
5313498266Sopenharmony_ci#include <inet.h>
5413498266Sopenharmony_ci#endif
5513498266Sopenharmony_ci
5613498266Sopenharmony_ci#include "urldata.h"
5713498266Sopenharmony_ci#include "bufq.h"
5813498266Sopenharmony_ci#include "sendf.h"
5913498266Sopenharmony_ci#include "if2ip.h"
6013498266Sopenharmony_ci#include "strerror.h"
6113498266Sopenharmony_ci#include "cfilters.h"
6213498266Sopenharmony_ci#include "cf-socket.h"
6313498266Sopenharmony_ci#include "connect.h"
6413498266Sopenharmony_ci#include "select.h"
6513498266Sopenharmony_ci#include "url.h" /* for Curl_safefree() */
6613498266Sopenharmony_ci#include "multiif.h"
6713498266Sopenharmony_ci#include "sockaddr.h" /* required for Curl_sockaddr_storage */
6813498266Sopenharmony_ci#include "inet_ntop.h"
6913498266Sopenharmony_ci#include "inet_pton.h"
7013498266Sopenharmony_ci#include "progress.h"
7113498266Sopenharmony_ci#include "warnless.h"
7213498266Sopenharmony_ci#include "conncache.h"
7313498266Sopenharmony_ci#include "multihandle.h"
7413498266Sopenharmony_ci#include "rand.h"
7513498266Sopenharmony_ci#include "share.h"
7613498266Sopenharmony_ci#include "version_win32.h"
7713498266Sopenharmony_ci
7813498266Sopenharmony_ci/* The last 3 #include files should be in this order */
7913498266Sopenharmony_ci#include "curl_printf.h"
8013498266Sopenharmony_ci#include "curl_memory.h"
8113498266Sopenharmony_ci#include "memdebug.h"
8213498266Sopenharmony_ci
8313498266Sopenharmony_ci
8413498266Sopenharmony_ci#if defined(ENABLE_IPV6) && defined(IPV6_V6ONLY) && defined(_WIN32)
8513498266Sopenharmony_ci/* It makes support for IPv4-mapped IPv6 addresses.
8613498266Sopenharmony_ci * Linux kernel, NetBSD, FreeBSD and Darwin: default is off;
8713498266Sopenharmony_ci * Windows Vista and later: default is on;
8813498266Sopenharmony_ci * DragonFly BSD: acts like off, and dummy setting;
8913498266Sopenharmony_ci * OpenBSD and earlier Windows: unsupported.
9013498266Sopenharmony_ci * Linux: controlled by /proc/sys/net/ipv6/bindv6only.
9113498266Sopenharmony_ci */
9213498266Sopenharmony_cistatic void set_ipv6_v6only(curl_socket_t sockfd, int on)
9313498266Sopenharmony_ci{
9413498266Sopenharmony_ci  (void)setsockopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&on, sizeof(on));
9513498266Sopenharmony_ci}
9613498266Sopenharmony_ci#else
9713498266Sopenharmony_ci#define set_ipv6_v6only(x,y)
9813498266Sopenharmony_ci#endif
9913498266Sopenharmony_ci
10013498266Sopenharmony_cistatic void tcpnodelay(struct Curl_easy *data, curl_socket_t sockfd)
10113498266Sopenharmony_ci{
10213498266Sopenharmony_ci#if defined(TCP_NODELAY)
10313498266Sopenharmony_ci  curl_socklen_t onoff = (curl_socklen_t) 1;
10413498266Sopenharmony_ci  int level = IPPROTO_TCP;
10513498266Sopenharmony_ci  char buffer[STRERROR_LEN];
10613498266Sopenharmony_ci
10713498266Sopenharmony_ci  if(setsockopt(sockfd, level, TCP_NODELAY, (void *)&onoff,
10813498266Sopenharmony_ci                sizeof(onoff)) < 0)
10913498266Sopenharmony_ci    infof(data, "Could not set TCP_NODELAY: %s",
11013498266Sopenharmony_ci          Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
11113498266Sopenharmony_ci#else
11213498266Sopenharmony_ci  (void)data;
11313498266Sopenharmony_ci  (void)sockfd;
11413498266Sopenharmony_ci#endif
11513498266Sopenharmony_ci}
11613498266Sopenharmony_ci
11713498266Sopenharmony_ci#ifdef SO_NOSIGPIPE
11813498266Sopenharmony_ci/* The preferred method on Mac OS X (10.2 and later) to prevent SIGPIPEs when
11913498266Sopenharmony_ci   sending data to a dead peer (instead of relying on the 4th argument to send
12013498266Sopenharmony_ci   being MSG_NOSIGNAL). Possibly also existing and in use on other BSD
12113498266Sopenharmony_ci   systems? */
12213498266Sopenharmony_cistatic void nosigpipe(struct Curl_easy *data,
12313498266Sopenharmony_ci                      curl_socket_t sockfd)
12413498266Sopenharmony_ci{
12513498266Sopenharmony_ci  int onoff = 1;
12613498266Sopenharmony_ci  (void)data;
12713498266Sopenharmony_ci  if(setsockopt(sockfd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&onoff,
12813498266Sopenharmony_ci                sizeof(onoff)) < 0) {
12913498266Sopenharmony_ci#if !defined(CURL_DISABLE_VERBOSE_STRINGS)
13013498266Sopenharmony_ci    char buffer[STRERROR_LEN];
13113498266Sopenharmony_ci    infof(data, "Could not set SO_NOSIGPIPE: %s",
13213498266Sopenharmony_ci          Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
13313498266Sopenharmony_ci#endif
13413498266Sopenharmony_ci  }
13513498266Sopenharmony_ci}
13613498266Sopenharmony_ci#else
13713498266Sopenharmony_ci#define nosigpipe(x,y) Curl_nop_stmt
13813498266Sopenharmony_ci#endif
13913498266Sopenharmony_ci
14013498266Sopenharmony_ci#if defined(__DragonFly__) || defined(USE_WINSOCK)
14113498266Sopenharmony_ci/* DragonFlyBSD and Windows use millisecond units */
14213498266Sopenharmony_ci#define KEEPALIVE_FACTOR(x) (x *= 1000)
14313498266Sopenharmony_ci#else
14413498266Sopenharmony_ci#define KEEPALIVE_FACTOR(x)
14513498266Sopenharmony_ci#endif
14613498266Sopenharmony_ci
14713498266Sopenharmony_ci#if defined(USE_WINSOCK) && !defined(SIO_KEEPALIVE_VALS)
14813498266Sopenharmony_ci#define SIO_KEEPALIVE_VALS    _WSAIOW(IOC_VENDOR,4)
14913498266Sopenharmony_ci
15013498266Sopenharmony_cistruct tcp_keepalive {
15113498266Sopenharmony_ci  u_long onoff;
15213498266Sopenharmony_ci  u_long keepalivetime;
15313498266Sopenharmony_ci  u_long keepaliveinterval;
15413498266Sopenharmony_ci};
15513498266Sopenharmony_ci#endif
15613498266Sopenharmony_ci
15713498266Sopenharmony_cistatic void
15813498266Sopenharmony_citcpkeepalive(struct Curl_easy *data,
15913498266Sopenharmony_ci             curl_socket_t sockfd)
16013498266Sopenharmony_ci{
16113498266Sopenharmony_ci  int optval = data->set.tcp_keepalive?1:0;
16213498266Sopenharmony_ci
16313498266Sopenharmony_ci  /* only set IDLE and INTVL if setting KEEPALIVE is successful */
16413498266Sopenharmony_ci  if(setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE,
16513498266Sopenharmony_ci        (void *)&optval, sizeof(optval)) < 0) {
16613498266Sopenharmony_ci    infof(data, "Failed to set SO_KEEPALIVE on fd "
16713498266Sopenharmony_ci          "%" CURL_FORMAT_SOCKET_T ": errno %d",
16813498266Sopenharmony_ci          sockfd, SOCKERRNO);
16913498266Sopenharmony_ci  }
17013498266Sopenharmony_ci  else {
17113498266Sopenharmony_ci#if defined(SIO_KEEPALIVE_VALS)
17213498266Sopenharmony_ci    struct tcp_keepalive vals;
17313498266Sopenharmony_ci    DWORD dummy;
17413498266Sopenharmony_ci    vals.onoff = 1;
17513498266Sopenharmony_ci    optval = curlx_sltosi(data->set.tcp_keepidle);
17613498266Sopenharmony_ci    KEEPALIVE_FACTOR(optval);
17713498266Sopenharmony_ci    vals.keepalivetime = optval;
17813498266Sopenharmony_ci    optval = curlx_sltosi(data->set.tcp_keepintvl);
17913498266Sopenharmony_ci    KEEPALIVE_FACTOR(optval);
18013498266Sopenharmony_ci    vals.keepaliveinterval = optval;
18113498266Sopenharmony_ci    if(WSAIoctl(sockfd, SIO_KEEPALIVE_VALS, (LPVOID) &vals, sizeof(vals),
18213498266Sopenharmony_ci                NULL, 0, &dummy, NULL, NULL) != 0) {
18313498266Sopenharmony_ci      infof(data, "Failed to set SIO_KEEPALIVE_VALS on fd "
18413498266Sopenharmony_ci                  "%" CURL_FORMAT_SOCKET_T ": errno %d",
18513498266Sopenharmony_ci                  sockfd, SOCKERRNO);
18613498266Sopenharmony_ci    }
18713498266Sopenharmony_ci#else
18813498266Sopenharmony_ci#ifdef TCP_KEEPIDLE
18913498266Sopenharmony_ci    optval = curlx_sltosi(data->set.tcp_keepidle);
19013498266Sopenharmony_ci    KEEPALIVE_FACTOR(optval);
19113498266Sopenharmony_ci    if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPIDLE,
19213498266Sopenharmony_ci          (void *)&optval, sizeof(optval)) < 0) {
19313498266Sopenharmony_ci      infof(data, "Failed to set TCP_KEEPIDLE on fd "
19413498266Sopenharmony_ci            "%" CURL_FORMAT_SOCKET_T ": errno %d",
19513498266Sopenharmony_ci            sockfd, SOCKERRNO);
19613498266Sopenharmony_ci    }
19713498266Sopenharmony_ci#elif defined(TCP_KEEPALIVE)
19813498266Sopenharmony_ci    /* Mac OS X style */
19913498266Sopenharmony_ci    optval = curlx_sltosi(data->set.tcp_keepidle);
20013498266Sopenharmony_ci    KEEPALIVE_FACTOR(optval);
20113498266Sopenharmony_ci    if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPALIVE,
20213498266Sopenharmony_ci      (void *)&optval, sizeof(optval)) < 0) {
20313498266Sopenharmony_ci      infof(data, "Failed to set TCP_KEEPALIVE on fd "
20413498266Sopenharmony_ci            "%" CURL_FORMAT_SOCKET_T ": errno %d",
20513498266Sopenharmony_ci            sockfd, SOCKERRNO);
20613498266Sopenharmony_ci    }
20713498266Sopenharmony_ci#endif
20813498266Sopenharmony_ci#ifdef TCP_KEEPINTVL
20913498266Sopenharmony_ci    optval = curlx_sltosi(data->set.tcp_keepintvl);
21013498266Sopenharmony_ci    KEEPALIVE_FACTOR(optval);
21113498266Sopenharmony_ci    if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPINTVL,
21213498266Sopenharmony_ci          (void *)&optval, sizeof(optval)) < 0) {
21313498266Sopenharmony_ci      infof(data, "Failed to set TCP_KEEPINTVL on fd "
21413498266Sopenharmony_ci            "%" CURL_FORMAT_SOCKET_T ": errno %d",
21513498266Sopenharmony_ci            sockfd, SOCKERRNO);
21613498266Sopenharmony_ci    }
21713498266Sopenharmony_ci#endif
21813498266Sopenharmony_ci#endif
21913498266Sopenharmony_ci  }
22013498266Sopenharmony_ci}
22113498266Sopenharmony_ci
22213498266Sopenharmony_ci/**
22313498266Sopenharmony_ci * Assign the address `ai` to the Curl_sockaddr_ex `dest` and
22413498266Sopenharmony_ci * set the transport used.
22513498266Sopenharmony_ci */
22613498266Sopenharmony_civoid Curl_sock_assign_addr(struct Curl_sockaddr_ex *dest,
22713498266Sopenharmony_ci                           const struct Curl_addrinfo *ai,
22813498266Sopenharmony_ci                           int transport)
22913498266Sopenharmony_ci{
23013498266Sopenharmony_ci  /*
23113498266Sopenharmony_ci   * The Curl_sockaddr_ex structure is basically libcurl's external API
23213498266Sopenharmony_ci   * curl_sockaddr structure with enough space available to directly hold
23313498266Sopenharmony_ci   * any protocol-specific address structures. The variable declared here
23413498266Sopenharmony_ci   * will be used to pass / receive data to/from the fopensocket callback
23513498266Sopenharmony_ci   * if this has been set, before that, it is initialized from parameters.
23613498266Sopenharmony_ci   */
23713498266Sopenharmony_ci  dest->family = ai->ai_family;
23813498266Sopenharmony_ci  switch(transport) {
23913498266Sopenharmony_ci  case TRNSPRT_TCP:
24013498266Sopenharmony_ci    dest->socktype = SOCK_STREAM;
24113498266Sopenharmony_ci    dest->protocol = IPPROTO_TCP;
24213498266Sopenharmony_ci    break;
24313498266Sopenharmony_ci  case TRNSPRT_UNIX:
24413498266Sopenharmony_ci    dest->socktype = SOCK_STREAM;
24513498266Sopenharmony_ci    dest->protocol = IPPROTO_IP;
24613498266Sopenharmony_ci    break;
24713498266Sopenharmony_ci  default: /* UDP and QUIC */
24813498266Sopenharmony_ci    dest->socktype = SOCK_DGRAM;
24913498266Sopenharmony_ci    dest->protocol = IPPROTO_UDP;
25013498266Sopenharmony_ci    break;
25113498266Sopenharmony_ci  }
25213498266Sopenharmony_ci  dest->addrlen = ai->ai_addrlen;
25313498266Sopenharmony_ci
25413498266Sopenharmony_ci  if(dest->addrlen > sizeof(struct Curl_sockaddr_storage))
25513498266Sopenharmony_ci    dest->addrlen = sizeof(struct Curl_sockaddr_storage);
25613498266Sopenharmony_ci  memcpy(&dest->sa_addr, ai->ai_addr, dest->addrlen);
25713498266Sopenharmony_ci}
25813498266Sopenharmony_ci
25913498266Sopenharmony_cistatic CURLcode socket_open(struct Curl_easy *data,
26013498266Sopenharmony_ci                            struct Curl_sockaddr_ex *addr,
26113498266Sopenharmony_ci                            curl_socket_t *sockfd)
26213498266Sopenharmony_ci{
26313498266Sopenharmony_ci  DEBUGASSERT(data);
26413498266Sopenharmony_ci  DEBUGASSERT(data->conn);
26513498266Sopenharmony_ci  if(data->set.fopensocket) {
26613498266Sopenharmony_ci   /*
26713498266Sopenharmony_ci    * If the opensocket callback is set, all the destination address
26813498266Sopenharmony_ci    * information is passed to the callback. Depending on this information the
26913498266Sopenharmony_ci    * callback may opt to abort the connection, this is indicated returning
27013498266Sopenharmony_ci    * CURL_SOCKET_BAD; otherwise it will return a not-connected socket. When
27113498266Sopenharmony_ci    * the callback returns a valid socket the destination address information
27213498266Sopenharmony_ci    * might have been changed and this 'new' address will actually be used
27313498266Sopenharmony_ci    * here to connect.
27413498266Sopenharmony_ci    */
27513498266Sopenharmony_ci    Curl_set_in_callback(data, true);
27613498266Sopenharmony_ci    *sockfd = data->set.fopensocket(data->set.opensocket_client,
27713498266Sopenharmony_ci                                    CURLSOCKTYPE_IPCXN,
27813498266Sopenharmony_ci                                    (struct curl_sockaddr *)addr);
27913498266Sopenharmony_ci    Curl_set_in_callback(data, false);
28013498266Sopenharmony_ci  }
28113498266Sopenharmony_ci  else {
28213498266Sopenharmony_ci    /* opensocket callback not set, so simply create the socket now */
28313498266Sopenharmony_ci    *sockfd = socket(addr->family, addr->socktype, addr->protocol);
28413498266Sopenharmony_ci  }
28513498266Sopenharmony_ci
28613498266Sopenharmony_ci  if(*sockfd == CURL_SOCKET_BAD)
28713498266Sopenharmony_ci    /* no socket, no connection */
28813498266Sopenharmony_ci    return CURLE_COULDNT_CONNECT;
28913498266Sopenharmony_ci
29013498266Sopenharmony_ci#if defined(ENABLE_IPV6) && defined(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID)
29113498266Sopenharmony_ci  if(data->conn->scope_id && (addr->family == AF_INET6)) {
29213498266Sopenharmony_ci    struct sockaddr_in6 * const sa6 = (void *)&addr->sa_addr;
29313498266Sopenharmony_ci    sa6->sin6_scope_id = data->conn->scope_id;
29413498266Sopenharmony_ci  }
29513498266Sopenharmony_ci#endif
29613498266Sopenharmony_ci  return CURLE_OK;
29713498266Sopenharmony_ci}
29813498266Sopenharmony_ci
29913498266Sopenharmony_ci/*
30013498266Sopenharmony_ci * Create a socket based on info from 'conn' and 'ai'.
30113498266Sopenharmony_ci *
30213498266Sopenharmony_ci * 'addr' should be a pointer to the correct struct to get data back, or NULL.
30313498266Sopenharmony_ci * 'sockfd' must be a pointer to a socket descriptor.
30413498266Sopenharmony_ci *
30513498266Sopenharmony_ci * If the open socket callback is set, used that!
30613498266Sopenharmony_ci *
30713498266Sopenharmony_ci */
30813498266Sopenharmony_ciCURLcode Curl_socket_open(struct Curl_easy *data,
30913498266Sopenharmony_ci                            const struct Curl_addrinfo *ai,
31013498266Sopenharmony_ci                            struct Curl_sockaddr_ex *addr,
31113498266Sopenharmony_ci                            int transport,
31213498266Sopenharmony_ci                            curl_socket_t *sockfd)
31313498266Sopenharmony_ci{
31413498266Sopenharmony_ci  struct Curl_sockaddr_ex dummy;
31513498266Sopenharmony_ci
31613498266Sopenharmony_ci  if(!addr)
31713498266Sopenharmony_ci    /* if the caller doesn't want info back, use a local temp copy */
31813498266Sopenharmony_ci    addr = &dummy;
31913498266Sopenharmony_ci
32013498266Sopenharmony_ci  Curl_sock_assign_addr(addr, ai, transport);
32113498266Sopenharmony_ci  return socket_open(data, addr, sockfd);
32213498266Sopenharmony_ci}
32313498266Sopenharmony_ci
32413498266Sopenharmony_cistatic int socket_close(struct Curl_easy *data, struct connectdata *conn,
32513498266Sopenharmony_ci                        int use_callback, curl_socket_t sock)
32613498266Sopenharmony_ci{
32713498266Sopenharmony_ci  if(use_callback && conn && conn->fclosesocket) {
32813498266Sopenharmony_ci    int rc;
32913498266Sopenharmony_ci    Curl_multi_closed(data, sock);
33013498266Sopenharmony_ci    Curl_set_in_callback(data, true);
33113498266Sopenharmony_ci    rc = conn->fclosesocket(conn->closesocket_client, sock);
33213498266Sopenharmony_ci    Curl_set_in_callback(data, false);
33313498266Sopenharmony_ci    return rc;
33413498266Sopenharmony_ci  }
33513498266Sopenharmony_ci
33613498266Sopenharmony_ci  if(conn)
33713498266Sopenharmony_ci    /* tell the multi-socket code about this */
33813498266Sopenharmony_ci    Curl_multi_closed(data, sock);
33913498266Sopenharmony_ci
34013498266Sopenharmony_ci  sclose(sock);
34113498266Sopenharmony_ci
34213498266Sopenharmony_ci  return 0;
34313498266Sopenharmony_ci}
34413498266Sopenharmony_ci
34513498266Sopenharmony_ci/*
34613498266Sopenharmony_ci * Close a socket.
34713498266Sopenharmony_ci *
34813498266Sopenharmony_ci * 'conn' can be NULL, beware!
34913498266Sopenharmony_ci */
35013498266Sopenharmony_ciint Curl_socket_close(struct Curl_easy *data, struct connectdata *conn,
35113498266Sopenharmony_ci                      curl_socket_t sock)
35213498266Sopenharmony_ci{
35313498266Sopenharmony_ci  return socket_close(data, conn, FALSE, sock);
35413498266Sopenharmony_ci}
35513498266Sopenharmony_ci
35613498266Sopenharmony_ci#ifdef USE_WINSOCK
35713498266Sopenharmony_ci/* When you run a program that uses the Windows Sockets API, you may
35813498266Sopenharmony_ci   experience slow performance when you copy data to a TCP server.
35913498266Sopenharmony_ci
36013498266Sopenharmony_ci   https://support.microsoft.com/kb/823764
36113498266Sopenharmony_ci
36213498266Sopenharmony_ci   Work-around: Make the Socket Send Buffer Size Larger Than the Program Send
36313498266Sopenharmony_ci   Buffer Size
36413498266Sopenharmony_ci
36513498266Sopenharmony_ci   The problem described in this knowledge-base is applied only to pre-Vista
36613498266Sopenharmony_ci   Windows.  Following function trying to detect OS version and skips
36713498266Sopenharmony_ci   SO_SNDBUF adjustment for Windows Vista and above.
36813498266Sopenharmony_ci*/
36913498266Sopenharmony_ci#define DETECT_OS_NONE 0
37013498266Sopenharmony_ci#define DETECT_OS_PREVISTA 1
37113498266Sopenharmony_ci#define DETECT_OS_VISTA_OR_LATER 2
37213498266Sopenharmony_ci
37313498266Sopenharmony_civoid Curl_sndbufset(curl_socket_t sockfd)
37413498266Sopenharmony_ci{
37513498266Sopenharmony_ci  int val = CURL_MAX_WRITE_SIZE + 32;
37613498266Sopenharmony_ci  int curval = 0;
37713498266Sopenharmony_ci  int curlen = sizeof(curval);
37813498266Sopenharmony_ci
37913498266Sopenharmony_ci  static int detectOsState = DETECT_OS_NONE;
38013498266Sopenharmony_ci
38113498266Sopenharmony_ci  if(detectOsState == DETECT_OS_NONE) {
38213498266Sopenharmony_ci    if(curlx_verify_windows_version(6, 0, 0, PLATFORM_WINNT,
38313498266Sopenharmony_ci                                    VERSION_GREATER_THAN_EQUAL))
38413498266Sopenharmony_ci      detectOsState = DETECT_OS_VISTA_OR_LATER;
38513498266Sopenharmony_ci    else
38613498266Sopenharmony_ci      detectOsState = DETECT_OS_PREVISTA;
38713498266Sopenharmony_ci  }
38813498266Sopenharmony_ci
38913498266Sopenharmony_ci  if(detectOsState == DETECT_OS_VISTA_OR_LATER)
39013498266Sopenharmony_ci    return;
39113498266Sopenharmony_ci
39213498266Sopenharmony_ci  if(getsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (char *)&curval, &curlen) == 0)
39313498266Sopenharmony_ci    if(curval > val)
39413498266Sopenharmony_ci      return;
39513498266Sopenharmony_ci
39613498266Sopenharmony_ci  setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (const char *)&val, sizeof(val));
39713498266Sopenharmony_ci}
39813498266Sopenharmony_ci#endif
39913498266Sopenharmony_ci
40013498266Sopenharmony_ci#ifndef CURL_DISABLE_BINDLOCAL
40113498266Sopenharmony_cistatic CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn,
40213498266Sopenharmony_ci                          curl_socket_t sockfd, int af, unsigned int scope)
40313498266Sopenharmony_ci{
40413498266Sopenharmony_ci  struct Curl_sockaddr_storage sa;
40513498266Sopenharmony_ci  struct sockaddr *sock = (struct sockaddr *)&sa;  /* bind to this address */
40613498266Sopenharmony_ci  curl_socklen_t sizeof_sa = 0; /* size of the data sock points to */
40713498266Sopenharmony_ci  struct sockaddr_in *si4 = (struct sockaddr_in *)&sa;
40813498266Sopenharmony_ci#ifdef ENABLE_IPV6
40913498266Sopenharmony_ci  struct sockaddr_in6 *si6 = (struct sockaddr_in6 *)&sa;
41013498266Sopenharmony_ci#endif
41113498266Sopenharmony_ci
41213498266Sopenharmony_ci  struct Curl_dns_entry *h = NULL;
41313498266Sopenharmony_ci  unsigned short port = data->set.localport; /* use this port number, 0 for
41413498266Sopenharmony_ci                                                "random" */
41513498266Sopenharmony_ci  /* how many port numbers to try to bind to, increasing one at a time */
41613498266Sopenharmony_ci  int portnum = data->set.localportrange;
41713498266Sopenharmony_ci  const char *dev = data->set.str[STRING_DEVICE];
41813498266Sopenharmony_ci  int error;
41913498266Sopenharmony_ci#ifdef IP_BIND_ADDRESS_NO_PORT
42013498266Sopenharmony_ci  int on = 1;
42113498266Sopenharmony_ci#endif
42213498266Sopenharmony_ci#ifndef ENABLE_IPV6
42313498266Sopenharmony_ci  (void)scope;
42413498266Sopenharmony_ci#endif
42513498266Sopenharmony_ci
42613498266Sopenharmony_ci  /*************************************************************
42713498266Sopenharmony_ci   * Select device to bind socket to
42813498266Sopenharmony_ci   *************************************************************/
42913498266Sopenharmony_ci  if(!dev && !port)
43013498266Sopenharmony_ci    /* no local kind of binding was requested */
43113498266Sopenharmony_ci    return CURLE_OK;
43213498266Sopenharmony_ci
43313498266Sopenharmony_ci  memset(&sa, 0, sizeof(struct Curl_sockaddr_storage));
43413498266Sopenharmony_ci
43513498266Sopenharmony_ci  if(dev && (strlen(dev)<255) ) {
43613498266Sopenharmony_ci    char myhost[256] = "";
43713498266Sopenharmony_ci    int done = 0; /* -1 for error, 1 for address found */
43813498266Sopenharmony_ci    bool is_interface = FALSE;
43913498266Sopenharmony_ci    bool is_host = FALSE;
44013498266Sopenharmony_ci    static const char *if_prefix = "if!";
44113498266Sopenharmony_ci    static const char *host_prefix = "host!";
44213498266Sopenharmony_ci
44313498266Sopenharmony_ci    if(strncmp(if_prefix, dev, strlen(if_prefix)) == 0) {
44413498266Sopenharmony_ci      dev += strlen(if_prefix);
44513498266Sopenharmony_ci      is_interface = TRUE;
44613498266Sopenharmony_ci    }
44713498266Sopenharmony_ci    else if(strncmp(host_prefix, dev, strlen(host_prefix)) == 0) {
44813498266Sopenharmony_ci      dev += strlen(host_prefix);
44913498266Sopenharmony_ci      is_host = TRUE;
45013498266Sopenharmony_ci    }
45113498266Sopenharmony_ci
45213498266Sopenharmony_ci    /* interface */
45313498266Sopenharmony_ci    if(!is_host) {
45413498266Sopenharmony_ci#ifdef SO_BINDTODEVICE
45513498266Sopenharmony_ci      /*
45613498266Sopenharmony_ci       * This binds the local socket to a particular interface. This will
45713498266Sopenharmony_ci       * force even requests to other local interfaces to go out the external
45813498266Sopenharmony_ci       * interface. Only bind to the interface when specified as interface,
45913498266Sopenharmony_ci       * not just as a hostname or ip address.
46013498266Sopenharmony_ci       *
46113498266Sopenharmony_ci       * The interface might be a VRF, eg: vrf-blue, which means it cannot be
46213498266Sopenharmony_ci       * converted to an IP address and would fail Curl_if2ip. Simply try to
46313498266Sopenharmony_ci       * use it straight away.
46413498266Sopenharmony_ci       */
46513498266Sopenharmony_ci      if(setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE,
46613498266Sopenharmony_ci                    dev, (curl_socklen_t)strlen(dev) + 1) == 0) {
46713498266Sopenharmony_ci        /* This is often "errno 1, error: Operation not permitted" if you're
46813498266Sopenharmony_ci         * not running as root or another suitable privileged user. If it
46913498266Sopenharmony_ci         * succeeds it means the parameter was a valid interface and not an IP
47013498266Sopenharmony_ci         * address. Return immediately.
47113498266Sopenharmony_ci         */
47213498266Sopenharmony_ci        infof(data, "socket successfully bound to interface '%s'", dev);
47313498266Sopenharmony_ci        return CURLE_OK;
47413498266Sopenharmony_ci      }
47513498266Sopenharmony_ci#endif
47613498266Sopenharmony_ci
47713498266Sopenharmony_ci      switch(Curl_if2ip(af,
47813498266Sopenharmony_ci#ifdef ENABLE_IPV6
47913498266Sopenharmony_ci                        scope, conn->scope_id,
48013498266Sopenharmony_ci#endif
48113498266Sopenharmony_ci                        dev, myhost, sizeof(myhost))) {
48213498266Sopenharmony_ci        case IF2IP_NOT_FOUND:
48313498266Sopenharmony_ci          if(is_interface) {
48413498266Sopenharmony_ci            /* Do not fall back to treating it as a host name */
48513498266Sopenharmony_ci            failf(data, "Couldn't bind to interface '%s'", dev);
48613498266Sopenharmony_ci            return CURLE_INTERFACE_FAILED;
48713498266Sopenharmony_ci          }
48813498266Sopenharmony_ci          break;
48913498266Sopenharmony_ci        case IF2IP_AF_NOT_SUPPORTED:
49013498266Sopenharmony_ci          /* Signal the caller to try another address family if available */
49113498266Sopenharmony_ci          return CURLE_UNSUPPORTED_PROTOCOL;
49213498266Sopenharmony_ci        case IF2IP_FOUND:
49313498266Sopenharmony_ci          is_interface = TRUE;
49413498266Sopenharmony_ci          /*
49513498266Sopenharmony_ci           * We now have the numerical IP address in the 'myhost' buffer
49613498266Sopenharmony_ci           */
49713498266Sopenharmony_ci          infof(data, "Local Interface %s is ip %s using address family %i",
49813498266Sopenharmony_ci                dev, myhost, af);
49913498266Sopenharmony_ci          done = 1;
50013498266Sopenharmony_ci          break;
50113498266Sopenharmony_ci      }
50213498266Sopenharmony_ci    }
50313498266Sopenharmony_ci    if(!is_interface) {
50413498266Sopenharmony_ci      /*
50513498266Sopenharmony_ci       * This was not an interface, resolve the name as a host name
50613498266Sopenharmony_ci       * or IP number
50713498266Sopenharmony_ci       *
50813498266Sopenharmony_ci       * Temporarily force name resolution to use only the address type
50913498266Sopenharmony_ci       * of the connection. The resolve functions should really be changed
51013498266Sopenharmony_ci       * to take a type parameter instead.
51113498266Sopenharmony_ci       */
51213498266Sopenharmony_ci      unsigned char ipver = conn->ip_version;
51313498266Sopenharmony_ci      int rc;
51413498266Sopenharmony_ci
51513498266Sopenharmony_ci      if(af == AF_INET)
51613498266Sopenharmony_ci        conn->ip_version = CURL_IPRESOLVE_V4;
51713498266Sopenharmony_ci#ifdef ENABLE_IPV6
51813498266Sopenharmony_ci      else if(af == AF_INET6)
51913498266Sopenharmony_ci        conn->ip_version = CURL_IPRESOLVE_V6;
52013498266Sopenharmony_ci#endif
52113498266Sopenharmony_ci
52213498266Sopenharmony_ci      rc = Curl_resolv(data, dev, 80, FALSE, &h);
52313498266Sopenharmony_ci      if(rc == CURLRESOLV_PENDING)
52413498266Sopenharmony_ci        (void)Curl_resolver_wait_resolv(data, &h);
52513498266Sopenharmony_ci      conn->ip_version = ipver;
52613498266Sopenharmony_ci
52713498266Sopenharmony_ci      if(h) {
52813498266Sopenharmony_ci        /* convert the resolved address, sizeof myhost >= INET_ADDRSTRLEN */
52913498266Sopenharmony_ci        Curl_printable_address(h->addr, myhost, sizeof(myhost));
53013498266Sopenharmony_ci        infof(data, "Name '%s' family %i resolved to '%s' family %i",
53113498266Sopenharmony_ci              dev, af, myhost, h->addr->ai_family);
53213498266Sopenharmony_ci        Curl_resolv_unlock(data, h);
53313498266Sopenharmony_ci        if(af != h->addr->ai_family) {
53413498266Sopenharmony_ci          /* bad IP version combo, signal the caller to try another address
53513498266Sopenharmony_ci             family if available */
53613498266Sopenharmony_ci          return CURLE_UNSUPPORTED_PROTOCOL;
53713498266Sopenharmony_ci        }
53813498266Sopenharmony_ci        done = 1;
53913498266Sopenharmony_ci      }
54013498266Sopenharmony_ci      else {
54113498266Sopenharmony_ci        /*
54213498266Sopenharmony_ci         * provided dev was no interface (or interfaces are not supported
54313498266Sopenharmony_ci         * e.g. solaris) no ip address and no domain we fail here
54413498266Sopenharmony_ci         */
54513498266Sopenharmony_ci        done = -1;
54613498266Sopenharmony_ci      }
54713498266Sopenharmony_ci    }
54813498266Sopenharmony_ci
54913498266Sopenharmony_ci    if(done > 0) {
55013498266Sopenharmony_ci#ifdef ENABLE_IPV6
55113498266Sopenharmony_ci      /* IPv6 address */
55213498266Sopenharmony_ci      if(af == AF_INET6) {
55313498266Sopenharmony_ci#ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
55413498266Sopenharmony_ci        char *scope_ptr = strchr(myhost, '%');
55513498266Sopenharmony_ci        if(scope_ptr)
55613498266Sopenharmony_ci          *(scope_ptr++) = '\0';
55713498266Sopenharmony_ci#endif
55813498266Sopenharmony_ci        if(Curl_inet_pton(AF_INET6, myhost, &si6->sin6_addr) > 0) {
55913498266Sopenharmony_ci          si6->sin6_family = AF_INET6;
56013498266Sopenharmony_ci          si6->sin6_port = htons(port);
56113498266Sopenharmony_ci#ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
56213498266Sopenharmony_ci          if(scope_ptr) {
56313498266Sopenharmony_ci            /* The "myhost" string either comes from Curl_if2ip or from
56413498266Sopenharmony_ci               Curl_printable_address. The latter returns only numeric scope
56513498266Sopenharmony_ci               IDs and the former returns none at all.  So the scope ID, if
56613498266Sopenharmony_ci               present, is known to be numeric */
56713498266Sopenharmony_ci            unsigned long scope_id = strtoul(scope_ptr, NULL, 10);
56813498266Sopenharmony_ci            if(scope_id > UINT_MAX)
56913498266Sopenharmony_ci              return CURLE_UNSUPPORTED_PROTOCOL;
57013498266Sopenharmony_ci
57113498266Sopenharmony_ci            si6->sin6_scope_id = (unsigned int)scope_id;
57213498266Sopenharmony_ci          }
57313498266Sopenharmony_ci#endif
57413498266Sopenharmony_ci        }
57513498266Sopenharmony_ci        sizeof_sa = sizeof(struct sockaddr_in6);
57613498266Sopenharmony_ci      }
57713498266Sopenharmony_ci      else
57813498266Sopenharmony_ci#endif
57913498266Sopenharmony_ci      /* IPv4 address */
58013498266Sopenharmony_ci      if((af == AF_INET) &&
58113498266Sopenharmony_ci         (Curl_inet_pton(AF_INET, myhost, &si4->sin_addr) > 0)) {
58213498266Sopenharmony_ci        si4->sin_family = AF_INET;
58313498266Sopenharmony_ci        si4->sin_port = htons(port);
58413498266Sopenharmony_ci        sizeof_sa = sizeof(struct sockaddr_in);
58513498266Sopenharmony_ci      }
58613498266Sopenharmony_ci    }
58713498266Sopenharmony_ci
58813498266Sopenharmony_ci    if(done < 1) {
58913498266Sopenharmony_ci      /* errorbuf is set false so failf will overwrite any message already in
59013498266Sopenharmony_ci         the error buffer, so the user receives this error message instead of a
59113498266Sopenharmony_ci         generic resolve error. */
59213498266Sopenharmony_ci      data->state.errorbuf = FALSE;
59313498266Sopenharmony_ci      failf(data, "Couldn't bind to '%s'", dev);
59413498266Sopenharmony_ci      return CURLE_INTERFACE_FAILED;
59513498266Sopenharmony_ci    }
59613498266Sopenharmony_ci  }
59713498266Sopenharmony_ci  else {
59813498266Sopenharmony_ci    /* no device was given, prepare sa to match af's needs */
59913498266Sopenharmony_ci#ifdef ENABLE_IPV6
60013498266Sopenharmony_ci    if(af == AF_INET6) {
60113498266Sopenharmony_ci      si6->sin6_family = AF_INET6;
60213498266Sopenharmony_ci      si6->sin6_port = htons(port);
60313498266Sopenharmony_ci      sizeof_sa = sizeof(struct sockaddr_in6);
60413498266Sopenharmony_ci    }
60513498266Sopenharmony_ci    else
60613498266Sopenharmony_ci#endif
60713498266Sopenharmony_ci    if(af == AF_INET) {
60813498266Sopenharmony_ci      si4->sin_family = AF_INET;
60913498266Sopenharmony_ci      si4->sin_port = htons(port);
61013498266Sopenharmony_ci      sizeof_sa = sizeof(struct sockaddr_in);
61113498266Sopenharmony_ci    }
61213498266Sopenharmony_ci  }
61313498266Sopenharmony_ci#ifdef IP_BIND_ADDRESS_NO_PORT
61413498266Sopenharmony_ci  (void)setsockopt(sockfd, SOL_IP, IP_BIND_ADDRESS_NO_PORT, &on, sizeof(on));
61513498266Sopenharmony_ci#endif
61613498266Sopenharmony_ci  for(;;) {
61713498266Sopenharmony_ci    if(bind(sockfd, sock, sizeof_sa) >= 0) {
61813498266Sopenharmony_ci      /* we succeeded to bind */
61913498266Sopenharmony_ci      struct Curl_sockaddr_storage add;
62013498266Sopenharmony_ci      curl_socklen_t size = sizeof(add);
62113498266Sopenharmony_ci      memset(&add, 0, sizeof(struct Curl_sockaddr_storage));
62213498266Sopenharmony_ci      if(getsockname(sockfd, (struct sockaddr *) &add, &size) < 0) {
62313498266Sopenharmony_ci        char buffer[STRERROR_LEN];
62413498266Sopenharmony_ci        data->state.os_errno = error = SOCKERRNO;
62513498266Sopenharmony_ci        failf(data, "getsockname() failed with errno %d: %s",
62613498266Sopenharmony_ci              error, Curl_strerror(error, buffer, sizeof(buffer)));
62713498266Sopenharmony_ci        return CURLE_INTERFACE_FAILED;
62813498266Sopenharmony_ci      }
62913498266Sopenharmony_ci      infof(data, "Local port: %hu", port);
63013498266Sopenharmony_ci      conn->bits.bound = TRUE;
63113498266Sopenharmony_ci      return CURLE_OK;
63213498266Sopenharmony_ci    }
63313498266Sopenharmony_ci
63413498266Sopenharmony_ci    if(--portnum > 0) {
63513498266Sopenharmony_ci      port++; /* try next port */
63613498266Sopenharmony_ci      if(port == 0)
63713498266Sopenharmony_ci        break;
63813498266Sopenharmony_ci      infof(data, "Bind to local port %d failed, trying next", port - 1);
63913498266Sopenharmony_ci      /* We reuse/clobber the port variable here below */
64013498266Sopenharmony_ci      if(sock->sa_family == AF_INET)
64113498266Sopenharmony_ci        si4->sin_port = ntohs(port);
64213498266Sopenharmony_ci#ifdef ENABLE_IPV6
64313498266Sopenharmony_ci      else
64413498266Sopenharmony_ci        si6->sin6_port = ntohs(port);
64513498266Sopenharmony_ci#endif
64613498266Sopenharmony_ci    }
64713498266Sopenharmony_ci    else
64813498266Sopenharmony_ci      break;
64913498266Sopenharmony_ci  }
65013498266Sopenharmony_ci  {
65113498266Sopenharmony_ci    char buffer[STRERROR_LEN];
65213498266Sopenharmony_ci    data->state.os_errno = error = SOCKERRNO;
65313498266Sopenharmony_ci    failf(data, "bind failed with errno %d: %s",
65413498266Sopenharmony_ci          error, Curl_strerror(error, buffer, sizeof(buffer)));
65513498266Sopenharmony_ci  }
65613498266Sopenharmony_ci
65713498266Sopenharmony_ci  return CURLE_INTERFACE_FAILED;
65813498266Sopenharmony_ci}
65913498266Sopenharmony_ci#endif
66013498266Sopenharmony_ci
66113498266Sopenharmony_ci/*
66213498266Sopenharmony_ci * verifyconnect() returns TRUE if the connect really has happened.
66313498266Sopenharmony_ci */
66413498266Sopenharmony_cistatic bool verifyconnect(curl_socket_t sockfd, int *error)
66513498266Sopenharmony_ci{
66613498266Sopenharmony_ci  bool rc = TRUE;
66713498266Sopenharmony_ci#ifdef SO_ERROR
66813498266Sopenharmony_ci  int err = 0;
66913498266Sopenharmony_ci  curl_socklen_t errSize = sizeof(err);
67013498266Sopenharmony_ci
67113498266Sopenharmony_ci#ifdef _WIN32
67213498266Sopenharmony_ci  /*
67313498266Sopenharmony_ci   * In October 2003 we effectively nullified this function on Windows due to
67413498266Sopenharmony_ci   * problems with it using all CPU in multi-threaded cases.
67513498266Sopenharmony_ci   *
67613498266Sopenharmony_ci   * In May 2004, we bring it back to offer more info back on connect failures.
67713498266Sopenharmony_ci   * Gisle Vanem could reproduce the former problems with this function, but
67813498266Sopenharmony_ci   * could avoid them by adding this SleepEx() call below:
67913498266Sopenharmony_ci   *
68013498266Sopenharmony_ci   *    "I don't have Rational Quantify, but the hint from his post was
68113498266Sopenharmony_ci   *    ntdll::NtRemoveIoCompletion(). So I'd assume the SleepEx (or maybe
68213498266Sopenharmony_ci   *    just Sleep(0) would be enough?) would release whatever
68313498266Sopenharmony_ci   *    mutex/critical-section the ntdll call is waiting on.
68413498266Sopenharmony_ci   *
68513498266Sopenharmony_ci   *    Someone got to verify this on Win-NT 4.0, 2000."
68613498266Sopenharmony_ci   */
68713498266Sopenharmony_ci
68813498266Sopenharmony_ci#ifdef _WIN32_WCE
68913498266Sopenharmony_ci  Sleep(0);
69013498266Sopenharmony_ci#else
69113498266Sopenharmony_ci  SleepEx(0, FALSE);
69213498266Sopenharmony_ci#endif
69313498266Sopenharmony_ci
69413498266Sopenharmony_ci#endif
69513498266Sopenharmony_ci
69613498266Sopenharmony_ci  if(0 != getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (void *)&err, &errSize))
69713498266Sopenharmony_ci    err = SOCKERRNO;
69813498266Sopenharmony_ci#ifdef _WIN32_WCE
69913498266Sopenharmony_ci  /* Old WinCE versions don't support SO_ERROR */
70013498266Sopenharmony_ci  if(WSAENOPROTOOPT == err) {
70113498266Sopenharmony_ci    SET_SOCKERRNO(0);
70213498266Sopenharmony_ci    err = 0;
70313498266Sopenharmony_ci  }
70413498266Sopenharmony_ci#endif
70513498266Sopenharmony_ci#if defined(EBADIOCTL) && defined(__minix)
70613498266Sopenharmony_ci  /* Minix 3.1.x doesn't support getsockopt on UDP sockets */
70713498266Sopenharmony_ci  if(EBADIOCTL == err) {
70813498266Sopenharmony_ci    SET_SOCKERRNO(0);
70913498266Sopenharmony_ci    err = 0;
71013498266Sopenharmony_ci  }
71113498266Sopenharmony_ci#endif
71213498266Sopenharmony_ci  if((0 == err) || (EISCONN == err))
71313498266Sopenharmony_ci    /* we are connected, awesome! */
71413498266Sopenharmony_ci    rc = TRUE;
71513498266Sopenharmony_ci  else
71613498266Sopenharmony_ci    /* This wasn't a successful connect */
71713498266Sopenharmony_ci    rc = FALSE;
71813498266Sopenharmony_ci  if(error)
71913498266Sopenharmony_ci    *error = err;
72013498266Sopenharmony_ci#else
72113498266Sopenharmony_ci  (void)sockfd;
72213498266Sopenharmony_ci  if(error)
72313498266Sopenharmony_ci    *error = SOCKERRNO;
72413498266Sopenharmony_ci#endif
72513498266Sopenharmony_ci  return rc;
72613498266Sopenharmony_ci}
72713498266Sopenharmony_ci
72813498266Sopenharmony_ci/**
72913498266Sopenharmony_ci * Determine the curl code for a socket connect() == -1 with errno.
73013498266Sopenharmony_ci */
73113498266Sopenharmony_cistatic CURLcode socket_connect_result(struct Curl_easy *data,
73213498266Sopenharmony_ci                                      const char *ipaddress, int error)
73313498266Sopenharmony_ci{
73413498266Sopenharmony_ci  switch(error) {
73513498266Sopenharmony_ci  case EINPROGRESS:
73613498266Sopenharmony_ci  case EWOULDBLOCK:
73713498266Sopenharmony_ci#if defined(EAGAIN)
73813498266Sopenharmony_ci#if (EAGAIN) != (EWOULDBLOCK)
73913498266Sopenharmony_ci    /* On some platforms EAGAIN and EWOULDBLOCK are the
74013498266Sopenharmony_ci     * same value, and on others they are different, hence
74113498266Sopenharmony_ci     * the odd #if
74213498266Sopenharmony_ci     */
74313498266Sopenharmony_ci  case EAGAIN:
74413498266Sopenharmony_ci#endif
74513498266Sopenharmony_ci#endif
74613498266Sopenharmony_ci    return CURLE_OK;
74713498266Sopenharmony_ci
74813498266Sopenharmony_ci  default:
74913498266Sopenharmony_ci    /* unknown error, fallthrough and try another address! */
75013498266Sopenharmony_ci#ifdef CURL_DISABLE_VERBOSE_STRINGS
75113498266Sopenharmony_ci    (void)ipaddress;
75213498266Sopenharmony_ci#else
75313498266Sopenharmony_ci    {
75413498266Sopenharmony_ci      char buffer[STRERROR_LEN];
75513498266Sopenharmony_ci      infof(data, "Immediate connect fail for %s: %s",
75613498266Sopenharmony_ci            ipaddress, Curl_strerror(error, buffer, sizeof(buffer)));
75713498266Sopenharmony_ci    }
75813498266Sopenharmony_ci#endif
75913498266Sopenharmony_ci    data->state.os_errno = error;
76013498266Sopenharmony_ci    /* connect failed */
76113498266Sopenharmony_ci    return CURLE_COULDNT_CONNECT;
76213498266Sopenharmony_ci  }
76313498266Sopenharmony_ci}
76413498266Sopenharmony_ci
76513498266Sopenharmony_ci/* We have a recv buffer to enhance reads with len < NW_SMALL_READS.
76613498266Sopenharmony_ci * This happens often on TLS connections where the TLS implementation
76713498266Sopenharmony_ci * tries to read the head of a TLS record, determine the length of the
76813498266Sopenharmony_ci * full record and then make a subsequent read for that.
76913498266Sopenharmony_ci * On large reads, we will not fill the buffer to avoid the double copy. */
77013498266Sopenharmony_ci#define NW_RECV_CHUNK_SIZE    (64 * 1024)
77113498266Sopenharmony_ci#define NW_RECV_CHUNKS         1
77213498266Sopenharmony_ci#define NW_SMALL_READS        (1024)
77313498266Sopenharmony_ci
77413498266Sopenharmony_cistruct cf_socket_ctx {
77513498266Sopenharmony_ci  int transport;
77613498266Sopenharmony_ci  struct Curl_sockaddr_ex addr;      /* address to connect to */
77713498266Sopenharmony_ci  curl_socket_t sock;                /* current attempt socket */
77813498266Sopenharmony_ci  struct bufq recvbuf;               /* used when `buffer_recv` is set */
77913498266Sopenharmony_ci  char r_ip[MAX_IPADR_LEN];          /* remote IP as string */
78013498266Sopenharmony_ci  int r_port;                        /* remote port number */
78113498266Sopenharmony_ci  char l_ip[MAX_IPADR_LEN];          /* local IP as string */
78213498266Sopenharmony_ci  int l_port;                        /* local port number */
78313498266Sopenharmony_ci  struct curltime started_at;        /* when socket was created */
78413498266Sopenharmony_ci  struct curltime connected_at;      /* when socket connected/got first byte */
78513498266Sopenharmony_ci  struct curltime first_byte_at;     /* when first byte was recvd */
78613498266Sopenharmony_ci  int error;                         /* errno of last failure or 0 */
78713498266Sopenharmony_ci#ifdef DEBUGBUILD
78813498266Sopenharmony_ci  int wblock_percent;                /* percent of writes doing EAGAIN */
78913498266Sopenharmony_ci  int wpartial_percent;              /* percent of bytes written in send */
79013498266Sopenharmony_ci  int rblock_percent;                /* percent of reads doing EAGAIN */
79113498266Sopenharmony_ci  size_t recv_max;                  /* max enforced read size */
79213498266Sopenharmony_ci#endif
79313498266Sopenharmony_ci  BIT(got_first_byte);               /* if first byte was received */
79413498266Sopenharmony_ci  BIT(accepted);                     /* socket was accepted, not connected */
79513498266Sopenharmony_ci  BIT(sock_connected);               /* socket is "connected", e.g. in UDP */
79613498266Sopenharmony_ci  BIT(active);
79713498266Sopenharmony_ci  BIT(buffer_recv);
79813498266Sopenharmony_ci};
79913498266Sopenharmony_ci
80013498266Sopenharmony_cistatic void cf_socket_ctx_init(struct cf_socket_ctx *ctx,
80113498266Sopenharmony_ci                               const struct Curl_addrinfo *ai,
80213498266Sopenharmony_ci                               int transport)
80313498266Sopenharmony_ci{
80413498266Sopenharmony_ci  memset(ctx, 0, sizeof(*ctx));
80513498266Sopenharmony_ci  ctx->sock = CURL_SOCKET_BAD;
80613498266Sopenharmony_ci  ctx->transport = transport;
80713498266Sopenharmony_ci  Curl_sock_assign_addr(&ctx->addr, ai, transport);
80813498266Sopenharmony_ci  Curl_bufq_init(&ctx->recvbuf, NW_RECV_CHUNK_SIZE, NW_RECV_CHUNKS);
80913498266Sopenharmony_ci#ifdef DEBUGBUILD
81013498266Sopenharmony_ci  {
81113498266Sopenharmony_ci    char *p = getenv("CURL_DBG_SOCK_WBLOCK");
81213498266Sopenharmony_ci    if(p) {
81313498266Sopenharmony_ci      long l = strtol(p, NULL, 10);
81413498266Sopenharmony_ci      if(l >= 0 && l <= 100)
81513498266Sopenharmony_ci        ctx->wblock_percent = (int)l;
81613498266Sopenharmony_ci    }
81713498266Sopenharmony_ci    p = getenv("CURL_DBG_SOCK_WPARTIAL");
81813498266Sopenharmony_ci    if(p) {
81913498266Sopenharmony_ci      long l = strtol(p, NULL, 10);
82013498266Sopenharmony_ci      if(l >= 0 && l <= 100)
82113498266Sopenharmony_ci        ctx->wpartial_percent = (int)l;
82213498266Sopenharmony_ci    }
82313498266Sopenharmony_ci    p = getenv("CURL_DBG_SOCK_RBLOCK");
82413498266Sopenharmony_ci    if(p) {
82513498266Sopenharmony_ci      long l = strtol(p, NULL, 10);
82613498266Sopenharmony_ci      if(l >= 0 && l <= 100)
82713498266Sopenharmony_ci        ctx->rblock_percent = (int)l;
82813498266Sopenharmony_ci    }
82913498266Sopenharmony_ci    p = getenv("CURL_DBG_SOCK_RMAX");
83013498266Sopenharmony_ci    if(p) {
83113498266Sopenharmony_ci      long l = strtol(p, NULL, 10);
83213498266Sopenharmony_ci      if(l >= 0)
83313498266Sopenharmony_ci        ctx->recv_max = (size_t)l;
83413498266Sopenharmony_ci    }
83513498266Sopenharmony_ci  }
83613498266Sopenharmony_ci#endif
83713498266Sopenharmony_ci}
83813498266Sopenharmony_ci
83913498266Sopenharmony_cistruct reader_ctx {
84013498266Sopenharmony_ci  struct Curl_cfilter *cf;
84113498266Sopenharmony_ci  struct Curl_easy *data;
84213498266Sopenharmony_ci};
84313498266Sopenharmony_ci
84413498266Sopenharmony_cistatic ssize_t nw_in_read(void *reader_ctx,
84513498266Sopenharmony_ci                           unsigned char *buf, size_t len,
84613498266Sopenharmony_ci                           CURLcode *err)
84713498266Sopenharmony_ci{
84813498266Sopenharmony_ci  struct reader_ctx *rctx = reader_ctx;
84913498266Sopenharmony_ci  struct cf_socket_ctx *ctx = rctx->cf->ctx;
85013498266Sopenharmony_ci  ssize_t nread;
85113498266Sopenharmony_ci
85213498266Sopenharmony_ci  *err = CURLE_OK;
85313498266Sopenharmony_ci  nread = sread(ctx->sock, buf, len);
85413498266Sopenharmony_ci
85513498266Sopenharmony_ci  if(-1 == nread) {
85613498266Sopenharmony_ci    int sockerr = SOCKERRNO;
85713498266Sopenharmony_ci
85813498266Sopenharmony_ci    if(
85913498266Sopenharmony_ci#ifdef WSAEWOULDBLOCK
86013498266Sopenharmony_ci      /* This is how Windows does it */
86113498266Sopenharmony_ci      (WSAEWOULDBLOCK == sockerr)
86213498266Sopenharmony_ci#else
86313498266Sopenharmony_ci      /* errno may be EWOULDBLOCK or on some systems EAGAIN when it returned
86413498266Sopenharmony_ci         due to its inability to send off data without blocking. We therefore
86513498266Sopenharmony_ci         treat both error codes the same here */
86613498266Sopenharmony_ci      (EWOULDBLOCK == sockerr) || (EAGAIN == sockerr) || (EINTR == sockerr)
86713498266Sopenharmony_ci#endif
86813498266Sopenharmony_ci      ) {
86913498266Sopenharmony_ci      /* this is just a case of EWOULDBLOCK */
87013498266Sopenharmony_ci      *err = CURLE_AGAIN;
87113498266Sopenharmony_ci      nread = -1;
87213498266Sopenharmony_ci    }
87313498266Sopenharmony_ci    else {
87413498266Sopenharmony_ci      char buffer[STRERROR_LEN];
87513498266Sopenharmony_ci
87613498266Sopenharmony_ci      failf(rctx->data, "Recv failure: %s",
87713498266Sopenharmony_ci            Curl_strerror(sockerr, buffer, sizeof(buffer)));
87813498266Sopenharmony_ci      rctx->data->state.os_errno = sockerr;
87913498266Sopenharmony_ci      *err = CURLE_RECV_ERROR;
88013498266Sopenharmony_ci      nread = -1;
88113498266Sopenharmony_ci    }
88213498266Sopenharmony_ci  }
88313498266Sopenharmony_ci  CURL_TRC_CF(rctx->data, rctx->cf, "nw_in_read(len=%zu) -> %d, err=%d",
88413498266Sopenharmony_ci              len, (int)nread, *err);
88513498266Sopenharmony_ci  return nread;
88613498266Sopenharmony_ci}
88713498266Sopenharmony_ci
88813498266Sopenharmony_cistatic void cf_socket_close(struct Curl_cfilter *cf, struct Curl_easy *data)
88913498266Sopenharmony_ci{
89013498266Sopenharmony_ci  struct cf_socket_ctx *ctx = cf->ctx;
89113498266Sopenharmony_ci
89213498266Sopenharmony_ci  if(ctx && CURL_SOCKET_BAD != ctx->sock) {
89313498266Sopenharmony_ci    CURL_TRC_CF(data, cf, "cf_socket_close(%" CURL_FORMAT_SOCKET_T
89413498266Sopenharmony_ci                ")", ctx->sock);
89513498266Sopenharmony_ci    if(ctx->sock == cf->conn->sock[cf->sockindex])
89613498266Sopenharmony_ci      cf->conn->sock[cf->sockindex] = CURL_SOCKET_BAD;
89713498266Sopenharmony_ci    socket_close(data, cf->conn, !ctx->accepted, ctx->sock);
89813498266Sopenharmony_ci    ctx->sock = CURL_SOCKET_BAD;
89913498266Sopenharmony_ci    if(ctx->active && cf->sockindex == FIRSTSOCKET)
90013498266Sopenharmony_ci      cf->conn->remote_addr = NULL;
90113498266Sopenharmony_ci    Curl_bufq_reset(&ctx->recvbuf);
90213498266Sopenharmony_ci    ctx->active = FALSE;
90313498266Sopenharmony_ci    ctx->buffer_recv = FALSE;
90413498266Sopenharmony_ci    memset(&ctx->started_at, 0, sizeof(ctx->started_at));
90513498266Sopenharmony_ci    memset(&ctx->connected_at, 0, sizeof(ctx->connected_at));
90613498266Sopenharmony_ci  }
90713498266Sopenharmony_ci
90813498266Sopenharmony_ci  cf->connected = FALSE;
90913498266Sopenharmony_ci}
91013498266Sopenharmony_ci
91113498266Sopenharmony_cistatic void cf_socket_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
91213498266Sopenharmony_ci{
91313498266Sopenharmony_ci  struct cf_socket_ctx *ctx = cf->ctx;
91413498266Sopenharmony_ci
91513498266Sopenharmony_ci  cf_socket_close(cf, data);
91613498266Sopenharmony_ci  CURL_TRC_CF(data, cf, "destroy");
91713498266Sopenharmony_ci  Curl_bufq_free(&ctx->recvbuf);
91813498266Sopenharmony_ci  free(ctx);
91913498266Sopenharmony_ci  cf->ctx = NULL;
92013498266Sopenharmony_ci}
92113498266Sopenharmony_ci
92213498266Sopenharmony_cistatic CURLcode set_local_ip(struct Curl_cfilter *cf,
92313498266Sopenharmony_ci                             struct Curl_easy *data)
92413498266Sopenharmony_ci{
92513498266Sopenharmony_ci  struct cf_socket_ctx *ctx = cf->ctx;
92613498266Sopenharmony_ci
92713498266Sopenharmony_ci#ifdef HAVE_GETSOCKNAME
92813498266Sopenharmony_ci  if(!(data->conn->handler->protocol & CURLPROTO_TFTP)) {
92913498266Sopenharmony_ci    /* TFTP does not connect, so it cannot get the IP like this */
93013498266Sopenharmony_ci
93113498266Sopenharmony_ci    char buffer[STRERROR_LEN];
93213498266Sopenharmony_ci    struct Curl_sockaddr_storage ssloc;
93313498266Sopenharmony_ci    curl_socklen_t slen = sizeof(struct Curl_sockaddr_storage);
93413498266Sopenharmony_ci
93513498266Sopenharmony_ci    memset(&ssloc, 0, sizeof(ssloc));
93613498266Sopenharmony_ci    if(getsockname(ctx->sock, (struct sockaddr*) &ssloc, &slen)) {
93713498266Sopenharmony_ci      int error = SOCKERRNO;
93813498266Sopenharmony_ci      failf(data, "getsockname() failed with errno %d: %s",
93913498266Sopenharmony_ci            error, Curl_strerror(error, buffer, sizeof(buffer)));
94013498266Sopenharmony_ci      return CURLE_FAILED_INIT;
94113498266Sopenharmony_ci    }
94213498266Sopenharmony_ci    if(!Curl_addr2string((struct sockaddr*)&ssloc, slen,
94313498266Sopenharmony_ci                         ctx->l_ip, &ctx->l_port)) {
94413498266Sopenharmony_ci      failf(data, "ssloc inet_ntop() failed with errno %d: %s",
94513498266Sopenharmony_ci            errno, Curl_strerror(errno, buffer, sizeof(buffer)));
94613498266Sopenharmony_ci      return CURLE_FAILED_INIT;
94713498266Sopenharmony_ci    }
94813498266Sopenharmony_ci  }
94913498266Sopenharmony_ci#else
95013498266Sopenharmony_ci  (void)data;
95113498266Sopenharmony_ci  ctx->l_ip[0] = 0;
95213498266Sopenharmony_ci  ctx->l_port = -1;
95313498266Sopenharmony_ci#endif
95413498266Sopenharmony_ci  return CURLE_OK;
95513498266Sopenharmony_ci}
95613498266Sopenharmony_ci
95713498266Sopenharmony_cistatic CURLcode set_remote_ip(struct Curl_cfilter *cf,
95813498266Sopenharmony_ci                              struct Curl_easy *data)
95913498266Sopenharmony_ci{
96013498266Sopenharmony_ci  struct cf_socket_ctx *ctx = cf->ctx;
96113498266Sopenharmony_ci
96213498266Sopenharmony_ci  /* store remote address and port used in this connection attempt */
96313498266Sopenharmony_ci  if(!Curl_addr2string(&ctx->addr.sa_addr, ctx->addr.addrlen,
96413498266Sopenharmony_ci                       ctx->r_ip, &ctx->r_port)) {
96513498266Sopenharmony_ci    char buffer[STRERROR_LEN];
96613498266Sopenharmony_ci
96713498266Sopenharmony_ci    ctx->error = errno;
96813498266Sopenharmony_ci    /* malformed address or bug in inet_ntop, try next address */
96913498266Sopenharmony_ci    failf(data, "sa_addr inet_ntop() failed with errno %d: %s",
97013498266Sopenharmony_ci          errno, Curl_strerror(errno, buffer, sizeof(buffer)));
97113498266Sopenharmony_ci    return CURLE_FAILED_INIT;
97213498266Sopenharmony_ci  }
97313498266Sopenharmony_ci  return CURLE_OK;
97413498266Sopenharmony_ci}
97513498266Sopenharmony_ci
97613498266Sopenharmony_cistatic CURLcode cf_socket_open(struct Curl_cfilter *cf,
97713498266Sopenharmony_ci                              struct Curl_easy *data)
97813498266Sopenharmony_ci{
97913498266Sopenharmony_ci  struct cf_socket_ctx *ctx = cf->ctx;
98013498266Sopenharmony_ci  int error = 0;
98113498266Sopenharmony_ci  bool isconnected = FALSE;
98213498266Sopenharmony_ci  CURLcode result = CURLE_COULDNT_CONNECT;
98313498266Sopenharmony_ci  bool is_tcp;
98413498266Sopenharmony_ci
98513498266Sopenharmony_ci  (void)data;
98613498266Sopenharmony_ci  DEBUGASSERT(ctx->sock == CURL_SOCKET_BAD);
98713498266Sopenharmony_ci  ctx->started_at = Curl_now();
98813498266Sopenharmony_ci  result = socket_open(data, &ctx->addr, &ctx->sock);
98913498266Sopenharmony_ci  if(result)
99013498266Sopenharmony_ci    goto out;
99113498266Sopenharmony_ci
99213498266Sopenharmony_ci  result = set_remote_ip(cf, data);
99313498266Sopenharmony_ci  if(result)
99413498266Sopenharmony_ci    goto out;
99513498266Sopenharmony_ci
99613498266Sopenharmony_ci#ifdef ENABLE_IPV6
99713498266Sopenharmony_ci  if(ctx->addr.family == AF_INET6) {
99813498266Sopenharmony_ci    set_ipv6_v6only(ctx->sock, 0);
99913498266Sopenharmony_ci    infof(data, "  Trying [%s]:%d...", ctx->r_ip, ctx->r_port);
100013498266Sopenharmony_ci  }
100113498266Sopenharmony_ci  else
100213498266Sopenharmony_ci#endif
100313498266Sopenharmony_ci    infof(data, "  Trying %s:%d...", ctx->r_ip, ctx->r_port);
100413498266Sopenharmony_ci
100513498266Sopenharmony_ci#ifdef ENABLE_IPV6
100613498266Sopenharmony_ci  is_tcp = (ctx->addr.family == AF_INET
100713498266Sopenharmony_ci            || ctx->addr.family == AF_INET6) &&
100813498266Sopenharmony_ci           ctx->addr.socktype == SOCK_STREAM;
100913498266Sopenharmony_ci#else
101013498266Sopenharmony_ci  is_tcp = (ctx->addr.family == AF_INET) &&
101113498266Sopenharmony_ci           ctx->addr.socktype == SOCK_STREAM;
101213498266Sopenharmony_ci#endif
101313498266Sopenharmony_ci  if(is_tcp && data->set.tcp_nodelay)
101413498266Sopenharmony_ci    tcpnodelay(data, ctx->sock);
101513498266Sopenharmony_ci
101613498266Sopenharmony_ci  nosigpipe(data, ctx->sock);
101713498266Sopenharmony_ci
101813498266Sopenharmony_ci  Curl_sndbufset(ctx->sock);
101913498266Sopenharmony_ci
102013498266Sopenharmony_ci  if(is_tcp && data->set.tcp_keepalive)
102113498266Sopenharmony_ci    tcpkeepalive(data, ctx->sock);
102213498266Sopenharmony_ci
102313498266Sopenharmony_ci  if(data->set.fsockopt) {
102413498266Sopenharmony_ci    /* activate callback for setting socket options */
102513498266Sopenharmony_ci    Curl_set_in_callback(data, true);
102613498266Sopenharmony_ci    error = data->set.fsockopt(data->set.sockopt_client,
102713498266Sopenharmony_ci                               ctx->sock,
102813498266Sopenharmony_ci                               CURLSOCKTYPE_IPCXN);
102913498266Sopenharmony_ci    Curl_set_in_callback(data, false);
103013498266Sopenharmony_ci
103113498266Sopenharmony_ci    if(error == CURL_SOCKOPT_ALREADY_CONNECTED)
103213498266Sopenharmony_ci      isconnected = TRUE;
103313498266Sopenharmony_ci    else if(error) {
103413498266Sopenharmony_ci      result = CURLE_ABORTED_BY_CALLBACK;
103513498266Sopenharmony_ci      goto out;
103613498266Sopenharmony_ci    }
103713498266Sopenharmony_ci  }
103813498266Sopenharmony_ci
103913498266Sopenharmony_ci#ifndef CURL_DISABLE_BINDLOCAL
104013498266Sopenharmony_ci  /* possibly bind the local end to an IP, interface or port */
104113498266Sopenharmony_ci  if(ctx->addr.family == AF_INET
104213498266Sopenharmony_ci#ifdef ENABLE_IPV6
104313498266Sopenharmony_ci     || ctx->addr.family == AF_INET6
104413498266Sopenharmony_ci#endif
104513498266Sopenharmony_ci    ) {
104613498266Sopenharmony_ci    result = bindlocal(data, cf->conn, ctx->sock, ctx->addr.family,
104713498266Sopenharmony_ci                       Curl_ipv6_scope(&ctx->addr.sa_addr));
104813498266Sopenharmony_ci    if(result) {
104913498266Sopenharmony_ci      if(result == CURLE_UNSUPPORTED_PROTOCOL) {
105013498266Sopenharmony_ci        /* The address family is not supported on this interface.
105113498266Sopenharmony_ci           We can continue trying addresses */
105213498266Sopenharmony_ci        result = CURLE_COULDNT_CONNECT;
105313498266Sopenharmony_ci      }
105413498266Sopenharmony_ci      goto out;
105513498266Sopenharmony_ci    }
105613498266Sopenharmony_ci  }
105713498266Sopenharmony_ci#endif
105813498266Sopenharmony_ci
105913498266Sopenharmony_ci  /* set socket non-blocking */
106013498266Sopenharmony_ci  (void)curlx_nonblock(ctx->sock, TRUE);
106113498266Sopenharmony_ci  ctx->sock_connected = (ctx->addr.socktype != SOCK_DGRAM);
106213498266Sopenharmony_ciout:
106313498266Sopenharmony_ci  if(result) {
106413498266Sopenharmony_ci    if(ctx->sock != CURL_SOCKET_BAD) {
106513498266Sopenharmony_ci      socket_close(data, cf->conn, TRUE, ctx->sock);
106613498266Sopenharmony_ci      ctx->sock = CURL_SOCKET_BAD;
106713498266Sopenharmony_ci    }
106813498266Sopenharmony_ci  }
106913498266Sopenharmony_ci  else if(isconnected) {
107013498266Sopenharmony_ci    set_local_ip(cf, data);
107113498266Sopenharmony_ci    ctx->connected_at = Curl_now();
107213498266Sopenharmony_ci    cf->connected = TRUE;
107313498266Sopenharmony_ci  }
107413498266Sopenharmony_ci  CURL_TRC_CF(data, cf, "cf_socket_open() -> %d, fd=%" CURL_FORMAT_SOCKET_T,
107513498266Sopenharmony_ci              result, ctx->sock);
107613498266Sopenharmony_ci  return result;
107713498266Sopenharmony_ci}
107813498266Sopenharmony_ci
107913498266Sopenharmony_cistatic int do_connect(struct Curl_cfilter *cf, struct Curl_easy *data,
108013498266Sopenharmony_ci                      bool is_tcp_fastopen)
108113498266Sopenharmony_ci{
108213498266Sopenharmony_ci  struct cf_socket_ctx *ctx = cf->ctx;
108313498266Sopenharmony_ci#ifdef TCP_FASTOPEN_CONNECT
108413498266Sopenharmony_ci  int optval = 1;
108513498266Sopenharmony_ci#endif
108613498266Sopenharmony_ci  int rc = -1;
108713498266Sopenharmony_ci
108813498266Sopenharmony_ci  (void)data;
108913498266Sopenharmony_ci  if(is_tcp_fastopen) {
109013498266Sopenharmony_ci#if defined(CONNECT_DATA_IDEMPOTENT) /* Darwin */
109113498266Sopenharmony_ci#  if defined(HAVE_BUILTIN_AVAILABLE)
109213498266Sopenharmony_ci    /* while connectx function is available since macOS 10.11 / iOS 9,
109313498266Sopenharmony_ci       it did not have the interface declared correctly until
109413498266Sopenharmony_ci       Xcode 9 / macOS SDK 10.13 */
109513498266Sopenharmony_ci    if(__builtin_available(macOS 10.11, iOS 9.0, tvOS 9.0, watchOS 2.0, *)) {
109613498266Sopenharmony_ci      sa_endpoints_t endpoints;
109713498266Sopenharmony_ci      endpoints.sae_srcif = 0;
109813498266Sopenharmony_ci      endpoints.sae_srcaddr = NULL;
109913498266Sopenharmony_ci      endpoints.sae_srcaddrlen = 0;
110013498266Sopenharmony_ci      endpoints.sae_dstaddr = &ctx->addr.sa_addr;
110113498266Sopenharmony_ci      endpoints.sae_dstaddrlen = ctx->addr.addrlen;
110213498266Sopenharmony_ci
110313498266Sopenharmony_ci      rc = connectx(ctx->sock, &endpoints, SAE_ASSOCID_ANY,
110413498266Sopenharmony_ci                    CONNECT_RESUME_ON_READ_WRITE | CONNECT_DATA_IDEMPOTENT,
110513498266Sopenharmony_ci                    NULL, 0, NULL, NULL);
110613498266Sopenharmony_ci    }
110713498266Sopenharmony_ci    else {
110813498266Sopenharmony_ci      rc = connect(ctx->sock, &ctx->addr.sa_addr, ctx->addr.addrlen);
110913498266Sopenharmony_ci    }
111013498266Sopenharmony_ci#  else
111113498266Sopenharmony_ci    rc = connect(ctx->sock, &ctx->addr.sa_addr, ctx->addr.addrlen);
111213498266Sopenharmony_ci#  endif /* HAVE_BUILTIN_AVAILABLE */
111313498266Sopenharmony_ci#elif defined(TCP_FASTOPEN_CONNECT) /* Linux >= 4.11 */
111413498266Sopenharmony_ci    if(setsockopt(ctx->sock, IPPROTO_TCP, TCP_FASTOPEN_CONNECT,
111513498266Sopenharmony_ci                  (void *)&optval, sizeof(optval)) < 0)
111613498266Sopenharmony_ci      infof(data, "Failed to enable TCP Fast Open on fd %"
111713498266Sopenharmony_ci            CURL_FORMAT_SOCKET_T, ctx->sock);
111813498266Sopenharmony_ci
111913498266Sopenharmony_ci    rc = connect(ctx->sock, &ctx->addr.sa_addr, ctx->addr.addrlen);
112013498266Sopenharmony_ci#elif defined(MSG_FASTOPEN) /* old Linux */
112113498266Sopenharmony_ci    if(cf->conn->given->flags & PROTOPT_SSL)
112213498266Sopenharmony_ci      rc = connect(ctx->sock, &ctx->addr.sa_addr, ctx->addr.addrlen);
112313498266Sopenharmony_ci    else
112413498266Sopenharmony_ci      rc = 0; /* Do nothing */
112513498266Sopenharmony_ci#endif
112613498266Sopenharmony_ci  }
112713498266Sopenharmony_ci  else {
112813498266Sopenharmony_ci    rc = connect(ctx->sock, &ctx->addr.sa_addr, ctx->addr.addrlen);
112913498266Sopenharmony_ci  }
113013498266Sopenharmony_ci  return rc;
113113498266Sopenharmony_ci}
113213498266Sopenharmony_ci
113313498266Sopenharmony_cistatic CURLcode cf_tcp_connect(struct Curl_cfilter *cf,
113413498266Sopenharmony_ci                               struct Curl_easy *data,
113513498266Sopenharmony_ci                               bool blocking, bool *done)
113613498266Sopenharmony_ci{
113713498266Sopenharmony_ci  struct cf_socket_ctx *ctx = cf->ctx;
113813498266Sopenharmony_ci  CURLcode result = CURLE_COULDNT_CONNECT;
113913498266Sopenharmony_ci  int rc = 0;
114013498266Sopenharmony_ci
114113498266Sopenharmony_ci  (void)data;
114213498266Sopenharmony_ci  if(cf->connected) {
114313498266Sopenharmony_ci    *done = TRUE;
114413498266Sopenharmony_ci    return CURLE_OK;
114513498266Sopenharmony_ci  }
114613498266Sopenharmony_ci
114713498266Sopenharmony_ci  /* TODO: need to support blocking connect? */
114813498266Sopenharmony_ci  if(blocking)
114913498266Sopenharmony_ci    return CURLE_UNSUPPORTED_PROTOCOL;
115013498266Sopenharmony_ci
115113498266Sopenharmony_ci  *done = FALSE; /* a very negative world view is best */
115213498266Sopenharmony_ci  if(ctx->sock == CURL_SOCKET_BAD) {
115313498266Sopenharmony_ci    int error;
115413498266Sopenharmony_ci
115513498266Sopenharmony_ci    result = cf_socket_open(cf, data);
115613498266Sopenharmony_ci    if(result)
115713498266Sopenharmony_ci      goto out;
115813498266Sopenharmony_ci
115913498266Sopenharmony_ci    if(cf->connected) {
116013498266Sopenharmony_ci      *done = TRUE;
116113498266Sopenharmony_ci      return CURLE_OK;
116213498266Sopenharmony_ci    }
116313498266Sopenharmony_ci
116413498266Sopenharmony_ci    /* Connect TCP socket */
116513498266Sopenharmony_ci    rc = do_connect(cf, data, cf->conn->bits.tcp_fastopen);
116613498266Sopenharmony_ci    error = SOCKERRNO;
116713498266Sopenharmony_ci    set_local_ip(cf, data);
116813498266Sopenharmony_ci    CURL_TRC_CF(data, cf, "local address %s port %d...",
116913498266Sopenharmony_ci                ctx->l_ip, ctx->l_port);
117013498266Sopenharmony_ci    if(-1 == rc) {
117113498266Sopenharmony_ci      result = socket_connect_result(data, ctx->r_ip, error);
117213498266Sopenharmony_ci      goto out;
117313498266Sopenharmony_ci    }
117413498266Sopenharmony_ci  }
117513498266Sopenharmony_ci
117613498266Sopenharmony_ci#ifdef mpeix
117713498266Sopenharmony_ci  /* Call this function once now, and ignore the results. We do this to
117813498266Sopenharmony_ci     "clear" the error state on the socket so that we can later read it
117913498266Sopenharmony_ci     reliably. This is reported necessary on the MPE/iX operating
118013498266Sopenharmony_ci     system. */
118113498266Sopenharmony_ci  (void)verifyconnect(ctx->sock, NULL);
118213498266Sopenharmony_ci#endif
118313498266Sopenharmony_ci  /* check socket for connect */
118413498266Sopenharmony_ci  rc = SOCKET_WRITABLE(ctx->sock, 0);
118513498266Sopenharmony_ci
118613498266Sopenharmony_ci  if(rc == 0) { /* no connection yet */
118713498266Sopenharmony_ci    CURL_TRC_CF(data, cf, "not connected yet");
118813498266Sopenharmony_ci    return CURLE_OK;
118913498266Sopenharmony_ci  }
119013498266Sopenharmony_ci  else if(rc == CURL_CSELECT_OUT || cf->conn->bits.tcp_fastopen) {
119113498266Sopenharmony_ci    if(verifyconnect(ctx->sock, &ctx->error)) {
119213498266Sopenharmony_ci      /* we are connected with TCP, awesome! */
119313498266Sopenharmony_ci      ctx->connected_at = Curl_now();
119413498266Sopenharmony_ci      set_local_ip(cf, data);
119513498266Sopenharmony_ci      *done = TRUE;
119613498266Sopenharmony_ci      cf->connected = TRUE;
119713498266Sopenharmony_ci      CURL_TRC_CF(data, cf, "connected");
119813498266Sopenharmony_ci      return CURLE_OK;
119913498266Sopenharmony_ci    }
120013498266Sopenharmony_ci  }
120113498266Sopenharmony_ci  else if(rc & CURL_CSELECT_ERR) {
120213498266Sopenharmony_ci    (void)verifyconnect(ctx->sock, &ctx->error);
120313498266Sopenharmony_ci    result = CURLE_COULDNT_CONNECT;
120413498266Sopenharmony_ci  }
120513498266Sopenharmony_ci
120613498266Sopenharmony_ciout:
120713498266Sopenharmony_ci  if(result) {
120813498266Sopenharmony_ci    if(ctx->error) {
120913498266Sopenharmony_ci      set_local_ip(cf, data);
121013498266Sopenharmony_ci      data->state.os_errno = ctx->error;
121113498266Sopenharmony_ci      SET_SOCKERRNO(ctx->error);
121213498266Sopenharmony_ci#ifndef CURL_DISABLE_VERBOSE_STRINGS
121313498266Sopenharmony_ci      {
121413498266Sopenharmony_ci        char buffer[STRERROR_LEN];
121513498266Sopenharmony_ci        infof(data, "connect to %s port %u from %s port %d failed: %s",
121613498266Sopenharmony_ci              ctx->r_ip, ctx->r_port, ctx->l_ip, ctx->l_port,
121713498266Sopenharmony_ci              Curl_strerror(ctx->error, buffer, sizeof(buffer)));
121813498266Sopenharmony_ci      }
121913498266Sopenharmony_ci#endif
122013498266Sopenharmony_ci    }
122113498266Sopenharmony_ci    if(ctx->sock != CURL_SOCKET_BAD) {
122213498266Sopenharmony_ci      socket_close(data, cf->conn, TRUE, ctx->sock);
122313498266Sopenharmony_ci      ctx->sock = CURL_SOCKET_BAD;
122413498266Sopenharmony_ci    }
122513498266Sopenharmony_ci    *done = FALSE;
122613498266Sopenharmony_ci  }
122713498266Sopenharmony_ci  return result;
122813498266Sopenharmony_ci}
122913498266Sopenharmony_ci
123013498266Sopenharmony_cistatic void cf_socket_get_host(struct Curl_cfilter *cf,
123113498266Sopenharmony_ci                               struct Curl_easy *data,
123213498266Sopenharmony_ci                               const char **phost,
123313498266Sopenharmony_ci                               const char **pdisplay_host,
123413498266Sopenharmony_ci                               int *pport)
123513498266Sopenharmony_ci{
123613498266Sopenharmony_ci  (void)data;
123713498266Sopenharmony_ci  *phost = cf->conn->host.name;
123813498266Sopenharmony_ci  *pdisplay_host = cf->conn->host.dispname;
123913498266Sopenharmony_ci  *pport = cf->conn->port;
124013498266Sopenharmony_ci}
124113498266Sopenharmony_ci
124213498266Sopenharmony_cistatic void cf_socket_adjust_pollset(struct Curl_cfilter *cf,
124313498266Sopenharmony_ci                                      struct Curl_easy *data,
124413498266Sopenharmony_ci                                      struct easy_pollset *ps)
124513498266Sopenharmony_ci{
124613498266Sopenharmony_ci  struct cf_socket_ctx *ctx = cf->ctx;
124713498266Sopenharmony_ci
124813498266Sopenharmony_ci  if(ctx->sock != CURL_SOCKET_BAD) {
124913498266Sopenharmony_ci    if(!cf->connected) {
125013498266Sopenharmony_ci      Curl_pollset_set_out_only(data, ps, ctx->sock);
125113498266Sopenharmony_ci      CURL_TRC_CF(data, cf, "adjust_pollset(!connected) -> %d socks", ps->num);
125213498266Sopenharmony_ci    }
125313498266Sopenharmony_ci    else if(!ctx->active) {
125413498266Sopenharmony_ci      Curl_pollset_add_in(data, ps, ctx->sock);
125513498266Sopenharmony_ci      CURL_TRC_CF(data, cf, "adjust_pollset(!active) -> %d socks", ps->num);
125613498266Sopenharmony_ci    }
125713498266Sopenharmony_ci  }
125813498266Sopenharmony_ci}
125913498266Sopenharmony_ci
126013498266Sopenharmony_cistatic bool cf_socket_data_pending(struct Curl_cfilter *cf,
126113498266Sopenharmony_ci                                   const struct Curl_easy *data)
126213498266Sopenharmony_ci{
126313498266Sopenharmony_ci  struct cf_socket_ctx *ctx = cf->ctx;
126413498266Sopenharmony_ci  int readable;
126513498266Sopenharmony_ci
126613498266Sopenharmony_ci  (void)data;
126713498266Sopenharmony_ci  if(!Curl_bufq_is_empty(&ctx->recvbuf))
126813498266Sopenharmony_ci    return TRUE;
126913498266Sopenharmony_ci
127013498266Sopenharmony_ci  readable = SOCKET_READABLE(ctx->sock, 0);
127113498266Sopenharmony_ci  return (readable > 0 && (readable & CURL_CSELECT_IN));
127213498266Sopenharmony_ci}
127313498266Sopenharmony_ci
127413498266Sopenharmony_cistatic ssize_t cf_socket_send(struct Curl_cfilter *cf, struct Curl_easy *data,
127513498266Sopenharmony_ci                              const void *buf, size_t len, CURLcode *err)
127613498266Sopenharmony_ci{
127713498266Sopenharmony_ci  struct cf_socket_ctx *ctx = cf->ctx;
127813498266Sopenharmony_ci  curl_socket_t fdsave;
127913498266Sopenharmony_ci  ssize_t nwritten;
128013498266Sopenharmony_ci  size_t orig_len = len;
128113498266Sopenharmony_ci
128213498266Sopenharmony_ci  *err = CURLE_OK;
128313498266Sopenharmony_ci  fdsave = cf->conn->sock[cf->sockindex];
128413498266Sopenharmony_ci  cf->conn->sock[cf->sockindex] = ctx->sock;
128513498266Sopenharmony_ci
128613498266Sopenharmony_ci#ifdef DEBUGBUILD
128713498266Sopenharmony_ci  /* simulate network blocking/partial writes */
128813498266Sopenharmony_ci  if(ctx->wblock_percent > 0) {
128913498266Sopenharmony_ci    unsigned char c;
129013498266Sopenharmony_ci    Curl_rand(data, &c, 1);
129113498266Sopenharmony_ci    if(c >= ((100-ctx->wblock_percent)*256/100)) {
129213498266Sopenharmony_ci      CURL_TRC_CF(data, cf, "send(len=%zu) SIMULATE EWOULDBLOCK", orig_len);
129313498266Sopenharmony_ci      *err = CURLE_AGAIN;
129413498266Sopenharmony_ci      nwritten = -1;
129513498266Sopenharmony_ci      cf->conn->sock[cf->sockindex] = fdsave;
129613498266Sopenharmony_ci      return nwritten;
129713498266Sopenharmony_ci    }
129813498266Sopenharmony_ci  }
129913498266Sopenharmony_ci  if(cf->cft != &Curl_cft_udp && ctx->wpartial_percent > 0 && len > 8) {
130013498266Sopenharmony_ci    len = len * ctx->wpartial_percent / 100;
130113498266Sopenharmony_ci    if(!len)
130213498266Sopenharmony_ci      len = 1;
130313498266Sopenharmony_ci    CURL_TRC_CF(data, cf, "send(len=%zu) SIMULATE partial write of %zu bytes",
130413498266Sopenharmony_ci                orig_len, len);
130513498266Sopenharmony_ci  }
130613498266Sopenharmony_ci#endif
130713498266Sopenharmony_ci
130813498266Sopenharmony_ci#if defined(MSG_FASTOPEN) && !defined(TCP_FASTOPEN_CONNECT) /* Linux */
130913498266Sopenharmony_ci  if(cf->conn->bits.tcp_fastopen) {
131013498266Sopenharmony_ci    nwritten = sendto(ctx->sock, buf, len, MSG_FASTOPEN,
131113498266Sopenharmony_ci                      &cf->conn->remote_addr->sa_addr,
131213498266Sopenharmony_ci                      cf->conn->remote_addr->addrlen);
131313498266Sopenharmony_ci    cf->conn->bits.tcp_fastopen = FALSE;
131413498266Sopenharmony_ci  }
131513498266Sopenharmony_ci  else
131613498266Sopenharmony_ci#endif
131713498266Sopenharmony_ci    nwritten = swrite(ctx->sock, buf, len);
131813498266Sopenharmony_ci
131913498266Sopenharmony_ci  if(-1 == nwritten) {
132013498266Sopenharmony_ci    int sockerr = SOCKERRNO;
132113498266Sopenharmony_ci
132213498266Sopenharmony_ci    if(
132313498266Sopenharmony_ci#ifdef WSAEWOULDBLOCK
132413498266Sopenharmony_ci      /* This is how Windows does it */
132513498266Sopenharmony_ci      (WSAEWOULDBLOCK == sockerr)
132613498266Sopenharmony_ci#else
132713498266Sopenharmony_ci      /* errno may be EWOULDBLOCK or on some systems EAGAIN when it returned
132813498266Sopenharmony_ci         due to its inability to send off data without blocking. We therefore
132913498266Sopenharmony_ci         treat both error codes the same here */
133013498266Sopenharmony_ci      (EWOULDBLOCK == sockerr) || (EAGAIN == sockerr) || (EINTR == sockerr) ||
133113498266Sopenharmony_ci      (EINPROGRESS == sockerr)
133213498266Sopenharmony_ci#endif
133313498266Sopenharmony_ci      ) {
133413498266Sopenharmony_ci      /* this is just a case of EWOULDBLOCK */
133513498266Sopenharmony_ci      *err = CURLE_AGAIN;
133613498266Sopenharmony_ci    }
133713498266Sopenharmony_ci    else {
133813498266Sopenharmony_ci      char buffer[STRERROR_LEN];
133913498266Sopenharmony_ci      failf(data, "Send failure: %s",
134013498266Sopenharmony_ci            Curl_strerror(sockerr, buffer, sizeof(buffer)));
134113498266Sopenharmony_ci      data->state.os_errno = sockerr;
134213498266Sopenharmony_ci      *err = CURLE_SEND_ERROR;
134313498266Sopenharmony_ci    }
134413498266Sopenharmony_ci  }
134513498266Sopenharmony_ci
134613498266Sopenharmony_ci  CURL_TRC_CF(data, cf, "send(len=%zu) -> %d, err=%d",
134713498266Sopenharmony_ci              orig_len, (int)nwritten, *err);
134813498266Sopenharmony_ci  cf->conn->sock[cf->sockindex] = fdsave;
134913498266Sopenharmony_ci  return nwritten;
135013498266Sopenharmony_ci}
135113498266Sopenharmony_ci
135213498266Sopenharmony_cistatic ssize_t cf_socket_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
135313498266Sopenharmony_ci                              char *buf, size_t len, CURLcode *err)
135413498266Sopenharmony_ci{
135513498266Sopenharmony_ci  struct cf_socket_ctx *ctx = cf->ctx;
135613498266Sopenharmony_ci  curl_socket_t fdsave;
135713498266Sopenharmony_ci  ssize_t nread;
135813498266Sopenharmony_ci
135913498266Sopenharmony_ci  *err = CURLE_OK;
136013498266Sopenharmony_ci
136113498266Sopenharmony_ci  fdsave = cf->conn->sock[cf->sockindex];
136213498266Sopenharmony_ci  cf->conn->sock[cf->sockindex] = ctx->sock;
136313498266Sopenharmony_ci
136413498266Sopenharmony_ci#ifdef DEBUGBUILD
136513498266Sopenharmony_ci  /* simulate network blocking/partial reads */
136613498266Sopenharmony_ci  if(cf->cft != &Curl_cft_udp && ctx->rblock_percent > 0) {
136713498266Sopenharmony_ci    unsigned char c;
136813498266Sopenharmony_ci    Curl_rand(data, &c, 1);
136913498266Sopenharmony_ci    if(c >= ((100-ctx->rblock_percent)*256/100)) {
137013498266Sopenharmony_ci      CURL_TRC_CF(data, cf, "recv(len=%zu) SIMULATE EWOULDBLOCK", len);
137113498266Sopenharmony_ci      *err = CURLE_AGAIN;
137213498266Sopenharmony_ci      nread = -1;
137313498266Sopenharmony_ci      cf->conn->sock[cf->sockindex] = fdsave;
137413498266Sopenharmony_ci      return nread;
137513498266Sopenharmony_ci    }
137613498266Sopenharmony_ci  }
137713498266Sopenharmony_ci  if(cf->cft != &Curl_cft_udp && ctx->recv_max && ctx->recv_max < len) {
137813498266Sopenharmony_ci    size_t orig_len = len;
137913498266Sopenharmony_ci    len = ctx->recv_max;
138013498266Sopenharmony_ci    CURL_TRC_CF(data, cf, "recv(len=%zu) SIMULATE max read of %zu bytes",
138113498266Sopenharmony_ci                orig_len, len);
138213498266Sopenharmony_ci  }
138313498266Sopenharmony_ci#endif
138413498266Sopenharmony_ci
138513498266Sopenharmony_ci  if(ctx->buffer_recv && !Curl_bufq_is_empty(&ctx->recvbuf)) {
138613498266Sopenharmony_ci    CURL_TRC_CF(data, cf, "recv from buffer");
138713498266Sopenharmony_ci    nread = Curl_bufq_read(&ctx->recvbuf, (unsigned char *)buf, len, err);
138813498266Sopenharmony_ci  }
138913498266Sopenharmony_ci  else {
139013498266Sopenharmony_ci    struct reader_ctx rctx;
139113498266Sopenharmony_ci
139213498266Sopenharmony_ci    rctx.cf = cf;
139313498266Sopenharmony_ci    rctx.data = data;
139413498266Sopenharmony_ci
139513498266Sopenharmony_ci    /* "small" reads may trigger filling our buffer, "large" reads
139613498266Sopenharmony_ci     * are probably not worth the additional copy */
139713498266Sopenharmony_ci    if(ctx->buffer_recv && len < NW_SMALL_READS) {
139813498266Sopenharmony_ci      ssize_t nwritten;
139913498266Sopenharmony_ci      nwritten = Curl_bufq_slurp(&ctx->recvbuf, nw_in_read, &rctx, err);
140013498266Sopenharmony_ci      if(nwritten < 0 && !Curl_bufq_is_empty(&ctx->recvbuf)) {
140113498266Sopenharmony_ci        /* we have a partial read with an error. need to deliver
140213498266Sopenharmony_ci         * what we got, return the error later. */
140313498266Sopenharmony_ci        CURL_TRC_CF(data, cf, "partial read: empty buffer first");
140413498266Sopenharmony_ci        nread = Curl_bufq_read(&ctx->recvbuf, (unsigned char *)buf, len, err);
140513498266Sopenharmony_ci      }
140613498266Sopenharmony_ci      else if(nwritten < 0) {
140713498266Sopenharmony_ci        nread = -1;
140813498266Sopenharmony_ci        goto out;
140913498266Sopenharmony_ci      }
141013498266Sopenharmony_ci      else if(nwritten == 0) {
141113498266Sopenharmony_ci        /* eof */
141213498266Sopenharmony_ci        *err = CURLE_OK;
141313498266Sopenharmony_ci        nread = 0;
141413498266Sopenharmony_ci      }
141513498266Sopenharmony_ci      else {
141613498266Sopenharmony_ci        CURL_TRC_CF(data, cf, "buffered %zd additional bytes", nwritten);
141713498266Sopenharmony_ci        nread = Curl_bufq_read(&ctx->recvbuf, (unsigned char *)buf, len, err);
141813498266Sopenharmony_ci      }
141913498266Sopenharmony_ci    }
142013498266Sopenharmony_ci    else {
142113498266Sopenharmony_ci      nread = nw_in_read(&rctx, (unsigned char *)buf, len, err);
142213498266Sopenharmony_ci    }
142313498266Sopenharmony_ci  }
142413498266Sopenharmony_ci
142513498266Sopenharmony_ciout:
142613498266Sopenharmony_ci  CURL_TRC_CF(data, cf, "recv(len=%zu) -> %d, err=%d", len, (int)nread,
142713498266Sopenharmony_ci              *err);
142813498266Sopenharmony_ci  if(nread > 0 && !ctx->got_first_byte) {
142913498266Sopenharmony_ci    ctx->first_byte_at = Curl_now();
143013498266Sopenharmony_ci    ctx->got_first_byte = TRUE;
143113498266Sopenharmony_ci  }
143213498266Sopenharmony_ci  cf->conn->sock[cf->sockindex] = fdsave;
143313498266Sopenharmony_ci  return nread;
143413498266Sopenharmony_ci}
143513498266Sopenharmony_ci
143613498266Sopenharmony_cistatic void conn_set_primary_ip(struct Curl_cfilter *cf,
143713498266Sopenharmony_ci                                struct Curl_easy *data)
143813498266Sopenharmony_ci{
143913498266Sopenharmony_ci  struct cf_socket_ctx *ctx = cf->ctx;
144013498266Sopenharmony_ci
144113498266Sopenharmony_ci  (void)data;
144213498266Sopenharmony_ci  DEBUGASSERT(sizeof(ctx->r_ip) == sizeof(cf->conn->primary_ip));
144313498266Sopenharmony_ci  memcpy(cf->conn->primary_ip, ctx->r_ip, sizeof(cf->conn->primary_ip));
144413498266Sopenharmony_ci}
144513498266Sopenharmony_ci
144613498266Sopenharmony_cistatic void cf_socket_active(struct Curl_cfilter *cf, struct Curl_easy *data)
144713498266Sopenharmony_ci{
144813498266Sopenharmony_ci  struct cf_socket_ctx *ctx = cf->ctx;
144913498266Sopenharmony_ci
145013498266Sopenharmony_ci  /* use this socket from now on */
145113498266Sopenharmony_ci  cf->conn->sock[cf->sockindex] = ctx->sock;
145213498266Sopenharmony_ci  /* the first socket info gets set at conn and data */
145313498266Sopenharmony_ci  if(cf->sockindex == FIRSTSOCKET) {
145413498266Sopenharmony_ci    cf->conn->remote_addr = &ctx->addr;
145513498266Sopenharmony_ci  #ifdef ENABLE_IPV6
145613498266Sopenharmony_ci    cf->conn->bits.ipv6 = (ctx->addr.family == AF_INET6)? TRUE : FALSE;
145713498266Sopenharmony_ci  #endif
145813498266Sopenharmony_ci    conn_set_primary_ip(cf, data);
145913498266Sopenharmony_ci    set_local_ip(cf, data);
146013498266Sopenharmony_ci    Curl_persistconninfo(data, cf->conn, ctx->l_ip, ctx->l_port);
146113498266Sopenharmony_ci    /* buffering is currently disabled by default because we have stalls
146213498266Sopenharmony_ci     * in parallel transfers where not all buffered data is consumed and no
146313498266Sopenharmony_ci     * socket events happen.
146413498266Sopenharmony_ci     */
146513498266Sopenharmony_ci    ctx->buffer_recv = FALSE;
146613498266Sopenharmony_ci  }
146713498266Sopenharmony_ci  ctx->active = TRUE;
146813498266Sopenharmony_ci}
146913498266Sopenharmony_ci
147013498266Sopenharmony_cistatic CURLcode cf_socket_cntrl(struct Curl_cfilter *cf,
147113498266Sopenharmony_ci                                struct Curl_easy *data,
147213498266Sopenharmony_ci                                int event, int arg1, void *arg2)
147313498266Sopenharmony_ci{
147413498266Sopenharmony_ci  struct cf_socket_ctx *ctx = cf->ctx;
147513498266Sopenharmony_ci
147613498266Sopenharmony_ci  (void)arg1;
147713498266Sopenharmony_ci  (void)arg2;
147813498266Sopenharmony_ci  switch(event) {
147913498266Sopenharmony_ci  case CF_CTRL_CONN_INFO_UPDATE:
148013498266Sopenharmony_ci    cf_socket_active(cf, data);
148113498266Sopenharmony_ci    break;
148213498266Sopenharmony_ci  case CF_CTRL_DATA_SETUP:
148313498266Sopenharmony_ci    Curl_persistconninfo(data, cf->conn, ctx->l_ip, ctx->l_port);
148413498266Sopenharmony_ci    break;
148513498266Sopenharmony_ci  case CF_CTRL_FORGET_SOCKET:
148613498266Sopenharmony_ci    ctx->sock = CURL_SOCKET_BAD;
148713498266Sopenharmony_ci    break;
148813498266Sopenharmony_ci  }
148913498266Sopenharmony_ci  return CURLE_OK;
149013498266Sopenharmony_ci}
149113498266Sopenharmony_ci
149213498266Sopenharmony_cistatic bool cf_socket_conn_is_alive(struct Curl_cfilter *cf,
149313498266Sopenharmony_ci                                    struct Curl_easy *data,
149413498266Sopenharmony_ci                                    bool *input_pending)
149513498266Sopenharmony_ci{
149613498266Sopenharmony_ci  struct cf_socket_ctx *ctx = cf->ctx;
149713498266Sopenharmony_ci  struct pollfd pfd[1];
149813498266Sopenharmony_ci  int r;
149913498266Sopenharmony_ci
150013498266Sopenharmony_ci  *input_pending = FALSE;
150113498266Sopenharmony_ci  (void)data;
150213498266Sopenharmony_ci  if(!ctx || ctx->sock == CURL_SOCKET_BAD)
150313498266Sopenharmony_ci    return FALSE;
150413498266Sopenharmony_ci
150513498266Sopenharmony_ci  /* Check with 0 timeout if there are any events pending on the socket */
150613498266Sopenharmony_ci  pfd[0].fd = ctx->sock;
150713498266Sopenharmony_ci  pfd[0].events = POLLRDNORM|POLLIN|POLLRDBAND|POLLPRI;
150813498266Sopenharmony_ci  pfd[0].revents = 0;
150913498266Sopenharmony_ci
151013498266Sopenharmony_ci  r = Curl_poll(pfd, 1, 0);
151113498266Sopenharmony_ci  if(r < 0) {
151213498266Sopenharmony_ci    CURL_TRC_CF(data, cf, "is_alive: poll error, assume dead");
151313498266Sopenharmony_ci    return FALSE;
151413498266Sopenharmony_ci  }
151513498266Sopenharmony_ci  else if(r == 0) {
151613498266Sopenharmony_ci    CURL_TRC_CF(data, cf, "is_alive: poll timeout, assume alive");
151713498266Sopenharmony_ci    return TRUE;
151813498266Sopenharmony_ci  }
151913498266Sopenharmony_ci  else if(pfd[0].revents & (POLLERR|POLLHUP|POLLPRI|POLLNVAL)) {
152013498266Sopenharmony_ci    CURL_TRC_CF(data, cf, "is_alive: err/hup/etc events, assume dead");
152113498266Sopenharmony_ci    return FALSE;
152213498266Sopenharmony_ci  }
152313498266Sopenharmony_ci
152413498266Sopenharmony_ci  CURL_TRC_CF(data, cf, "is_alive: valid events, looks alive");
152513498266Sopenharmony_ci  *input_pending = TRUE;
152613498266Sopenharmony_ci  return TRUE;
152713498266Sopenharmony_ci}
152813498266Sopenharmony_ci
152913498266Sopenharmony_cistatic CURLcode cf_socket_query(struct Curl_cfilter *cf,
153013498266Sopenharmony_ci                                struct Curl_easy *data,
153113498266Sopenharmony_ci                                int query, int *pres1, void *pres2)
153213498266Sopenharmony_ci{
153313498266Sopenharmony_ci  struct cf_socket_ctx *ctx = cf->ctx;
153413498266Sopenharmony_ci
153513498266Sopenharmony_ci  switch(query) {
153613498266Sopenharmony_ci  case CF_QUERY_SOCKET:
153713498266Sopenharmony_ci    DEBUGASSERT(pres2);
153813498266Sopenharmony_ci    *((curl_socket_t *)pres2) = ctx->sock;
153913498266Sopenharmony_ci    return CURLE_OK;
154013498266Sopenharmony_ci  case CF_QUERY_CONNECT_REPLY_MS:
154113498266Sopenharmony_ci    if(ctx->got_first_byte) {
154213498266Sopenharmony_ci      timediff_t ms = Curl_timediff(ctx->first_byte_at, ctx->started_at);
154313498266Sopenharmony_ci      *pres1 = (ms < INT_MAX)? (int)ms : INT_MAX;
154413498266Sopenharmony_ci    }
154513498266Sopenharmony_ci    else
154613498266Sopenharmony_ci      *pres1 = -1;
154713498266Sopenharmony_ci    return CURLE_OK;
154813498266Sopenharmony_ci  case CF_QUERY_TIMER_CONNECT: {
154913498266Sopenharmony_ci    struct curltime *when = pres2;
155013498266Sopenharmony_ci    switch(ctx->transport) {
155113498266Sopenharmony_ci    case TRNSPRT_UDP:
155213498266Sopenharmony_ci    case TRNSPRT_QUIC:
155313498266Sopenharmony_ci      /* Since UDP connected sockets work different from TCP, we use the
155413498266Sopenharmony_ci       * time of the first byte from the peer as the "connect" time. */
155513498266Sopenharmony_ci      if(ctx->got_first_byte) {
155613498266Sopenharmony_ci        *when = ctx->first_byte_at;
155713498266Sopenharmony_ci        break;
155813498266Sopenharmony_ci      }
155913498266Sopenharmony_ci      FALLTHROUGH();
156013498266Sopenharmony_ci    default:
156113498266Sopenharmony_ci      *when = ctx->connected_at;
156213498266Sopenharmony_ci      break;
156313498266Sopenharmony_ci    }
156413498266Sopenharmony_ci    return CURLE_OK;
156513498266Sopenharmony_ci  }
156613498266Sopenharmony_ci  default:
156713498266Sopenharmony_ci    break;
156813498266Sopenharmony_ci  }
156913498266Sopenharmony_ci  return cf->next?
157013498266Sopenharmony_ci    cf->next->cft->query(cf->next, data, query, pres1, pres2) :
157113498266Sopenharmony_ci    CURLE_UNKNOWN_OPTION;
157213498266Sopenharmony_ci}
157313498266Sopenharmony_ci
157413498266Sopenharmony_cistruct Curl_cftype Curl_cft_tcp = {
157513498266Sopenharmony_ci  "TCP",
157613498266Sopenharmony_ci  CF_TYPE_IP_CONNECT,
157713498266Sopenharmony_ci  CURL_LOG_LVL_NONE,
157813498266Sopenharmony_ci  cf_socket_destroy,
157913498266Sopenharmony_ci  cf_tcp_connect,
158013498266Sopenharmony_ci  cf_socket_close,
158113498266Sopenharmony_ci  cf_socket_get_host,
158213498266Sopenharmony_ci  cf_socket_adjust_pollset,
158313498266Sopenharmony_ci  cf_socket_data_pending,
158413498266Sopenharmony_ci  cf_socket_send,
158513498266Sopenharmony_ci  cf_socket_recv,
158613498266Sopenharmony_ci  cf_socket_cntrl,
158713498266Sopenharmony_ci  cf_socket_conn_is_alive,
158813498266Sopenharmony_ci  Curl_cf_def_conn_keep_alive,
158913498266Sopenharmony_ci  cf_socket_query,
159013498266Sopenharmony_ci};
159113498266Sopenharmony_ci
159213498266Sopenharmony_ciCURLcode Curl_cf_tcp_create(struct Curl_cfilter **pcf,
159313498266Sopenharmony_ci                            struct Curl_easy *data,
159413498266Sopenharmony_ci                            struct connectdata *conn,
159513498266Sopenharmony_ci                            const struct Curl_addrinfo *ai,
159613498266Sopenharmony_ci                            int transport)
159713498266Sopenharmony_ci{
159813498266Sopenharmony_ci  struct cf_socket_ctx *ctx = NULL;
159913498266Sopenharmony_ci  struct Curl_cfilter *cf = NULL;
160013498266Sopenharmony_ci  CURLcode result;
160113498266Sopenharmony_ci
160213498266Sopenharmony_ci  (void)data;
160313498266Sopenharmony_ci  (void)conn;
160413498266Sopenharmony_ci  DEBUGASSERT(transport == TRNSPRT_TCP);
160513498266Sopenharmony_ci  ctx = calloc(1, sizeof(*ctx));
160613498266Sopenharmony_ci  if(!ctx) {
160713498266Sopenharmony_ci    result = CURLE_OUT_OF_MEMORY;
160813498266Sopenharmony_ci    goto out;
160913498266Sopenharmony_ci  }
161013498266Sopenharmony_ci  cf_socket_ctx_init(ctx, ai, transport);
161113498266Sopenharmony_ci
161213498266Sopenharmony_ci  result = Curl_cf_create(&cf, &Curl_cft_tcp, ctx);
161313498266Sopenharmony_ci
161413498266Sopenharmony_ciout:
161513498266Sopenharmony_ci  *pcf = (!result)? cf : NULL;
161613498266Sopenharmony_ci  if(result) {
161713498266Sopenharmony_ci    Curl_safefree(cf);
161813498266Sopenharmony_ci    Curl_safefree(ctx);
161913498266Sopenharmony_ci  }
162013498266Sopenharmony_ci
162113498266Sopenharmony_ci  return result;
162213498266Sopenharmony_ci}
162313498266Sopenharmony_ci
162413498266Sopenharmony_cistatic CURLcode cf_udp_setup_quic(struct Curl_cfilter *cf,
162513498266Sopenharmony_ci                               struct Curl_easy *data)
162613498266Sopenharmony_ci{
162713498266Sopenharmony_ci  struct cf_socket_ctx *ctx = cf->ctx;
162813498266Sopenharmony_ci  int rc;
162913498266Sopenharmony_ci
163013498266Sopenharmony_ci  /* QUIC needs a connected socket, nonblocking */
163113498266Sopenharmony_ci  DEBUGASSERT(ctx->sock != CURL_SOCKET_BAD);
163213498266Sopenharmony_ci
163313498266Sopenharmony_ci#if defined(__APPLE__) && defined(USE_OPENSSL_QUIC)
163413498266Sopenharmony_ci  (void)rc;
163513498266Sopenharmony_ci  /* On macOS OpenSSL QUIC fails on connected sockets.
163613498266Sopenharmony_ci   * see: <https://github.com/openssl/openssl/issues/23251> */
163713498266Sopenharmony_ci#else
163813498266Sopenharmony_ci  rc = connect(ctx->sock, &ctx->addr.sa_addr, ctx->addr.addrlen);
163913498266Sopenharmony_ci  if(-1 == rc) {
164013498266Sopenharmony_ci    return socket_connect_result(data, ctx->r_ip, SOCKERRNO);
164113498266Sopenharmony_ci  }
164213498266Sopenharmony_ci  ctx->sock_connected = TRUE;
164313498266Sopenharmony_ci#endif
164413498266Sopenharmony_ci  set_local_ip(cf, data);
164513498266Sopenharmony_ci  CURL_TRC_CF(data, cf, "%s socket %" CURL_FORMAT_SOCKET_T
164613498266Sopenharmony_ci              " connected: [%s:%d] -> [%s:%d]",
164713498266Sopenharmony_ci              (ctx->transport == TRNSPRT_QUIC)? "QUIC" : "UDP",
164813498266Sopenharmony_ci              ctx->sock, ctx->l_ip, ctx->l_port, ctx->r_ip, ctx->r_port);
164913498266Sopenharmony_ci
165013498266Sopenharmony_ci  (void)curlx_nonblock(ctx->sock, TRUE);
165113498266Sopenharmony_ci  switch(ctx->addr.family) {
165213498266Sopenharmony_ci#if defined(__linux__) && defined(IP_MTU_DISCOVER)
165313498266Sopenharmony_ci  case AF_INET: {
165413498266Sopenharmony_ci    int val = IP_PMTUDISC_DO;
165513498266Sopenharmony_ci    (void)setsockopt(ctx->sock, IPPROTO_IP, IP_MTU_DISCOVER, &val,
165613498266Sopenharmony_ci                     sizeof(val));
165713498266Sopenharmony_ci    break;
165813498266Sopenharmony_ci  }
165913498266Sopenharmony_ci#endif
166013498266Sopenharmony_ci#if defined(__linux__) && defined(IPV6_MTU_DISCOVER)
166113498266Sopenharmony_ci  case AF_INET6: {
166213498266Sopenharmony_ci    int val = IPV6_PMTUDISC_DO;
166313498266Sopenharmony_ci    (void)setsockopt(ctx->sock, IPPROTO_IPV6, IPV6_MTU_DISCOVER, &val,
166413498266Sopenharmony_ci                     sizeof(val));
166513498266Sopenharmony_ci    break;
166613498266Sopenharmony_ci  }
166713498266Sopenharmony_ci#endif
166813498266Sopenharmony_ci  }
166913498266Sopenharmony_ci  return CURLE_OK;
167013498266Sopenharmony_ci}
167113498266Sopenharmony_ci
167213498266Sopenharmony_cistatic CURLcode cf_udp_connect(struct Curl_cfilter *cf,
167313498266Sopenharmony_ci                               struct Curl_easy *data,
167413498266Sopenharmony_ci                               bool blocking, bool *done)
167513498266Sopenharmony_ci{
167613498266Sopenharmony_ci  struct cf_socket_ctx *ctx = cf->ctx;
167713498266Sopenharmony_ci  CURLcode result = CURLE_COULDNT_CONNECT;
167813498266Sopenharmony_ci
167913498266Sopenharmony_ci  (void)blocking;
168013498266Sopenharmony_ci  if(cf->connected) {
168113498266Sopenharmony_ci    *done = TRUE;
168213498266Sopenharmony_ci    return CURLE_OK;
168313498266Sopenharmony_ci  }
168413498266Sopenharmony_ci  *done = FALSE;
168513498266Sopenharmony_ci  if(ctx->sock == CURL_SOCKET_BAD) {
168613498266Sopenharmony_ci    result = cf_socket_open(cf, data);
168713498266Sopenharmony_ci    if(result) {
168813498266Sopenharmony_ci      CURL_TRC_CF(data, cf, "cf_udp_connect(), open failed -> %d", result);
168913498266Sopenharmony_ci      goto out;
169013498266Sopenharmony_ci    }
169113498266Sopenharmony_ci
169213498266Sopenharmony_ci    if(ctx->transport == TRNSPRT_QUIC) {
169313498266Sopenharmony_ci      result = cf_udp_setup_quic(cf, data);
169413498266Sopenharmony_ci      if(result)
169513498266Sopenharmony_ci        goto out;
169613498266Sopenharmony_ci      CURL_TRC_CF(data, cf, "cf_udp_connect(), opened socket=%"
169713498266Sopenharmony_ci                  CURL_FORMAT_SOCKET_T " (%s:%d)",
169813498266Sopenharmony_ci                  ctx->sock, ctx->l_ip, ctx->l_port);
169913498266Sopenharmony_ci    }
170013498266Sopenharmony_ci    else {
170113498266Sopenharmony_ci      CURL_TRC_CF(data, cf, "cf_udp_connect(), opened socket=%"
170213498266Sopenharmony_ci                  CURL_FORMAT_SOCKET_T " (unconnected)", ctx->sock);
170313498266Sopenharmony_ci    }
170413498266Sopenharmony_ci    *done = TRUE;
170513498266Sopenharmony_ci    cf->connected = TRUE;
170613498266Sopenharmony_ci  }
170713498266Sopenharmony_ciout:
170813498266Sopenharmony_ci  return result;
170913498266Sopenharmony_ci}
171013498266Sopenharmony_ci
171113498266Sopenharmony_cistruct Curl_cftype Curl_cft_udp = {
171213498266Sopenharmony_ci  "UDP",
171313498266Sopenharmony_ci  CF_TYPE_IP_CONNECT,
171413498266Sopenharmony_ci  CURL_LOG_LVL_NONE,
171513498266Sopenharmony_ci  cf_socket_destroy,
171613498266Sopenharmony_ci  cf_udp_connect,
171713498266Sopenharmony_ci  cf_socket_close,
171813498266Sopenharmony_ci  cf_socket_get_host,
171913498266Sopenharmony_ci  cf_socket_adjust_pollset,
172013498266Sopenharmony_ci  cf_socket_data_pending,
172113498266Sopenharmony_ci  cf_socket_send,
172213498266Sopenharmony_ci  cf_socket_recv,
172313498266Sopenharmony_ci  cf_socket_cntrl,
172413498266Sopenharmony_ci  cf_socket_conn_is_alive,
172513498266Sopenharmony_ci  Curl_cf_def_conn_keep_alive,
172613498266Sopenharmony_ci  cf_socket_query,
172713498266Sopenharmony_ci};
172813498266Sopenharmony_ci
172913498266Sopenharmony_ciCURLcode Curl_cf_udp_create(struct Curl_cfilter **pcf,
173013498266Sopenharmony_ci                            struct Curl_easy *data,
173113498266Sopenharmony_ci                            struct connectdata *conn,
173213498266Sopenharmony_ci                            const struct Curl_addrinfo *ai,
173313498266Sopenharmony_ci                            int transport)
173413498266Sopenharmony_ci{
173513498266Sopenharmony_ci  struct cf_socket_ctx *ctx = NULL;
173613498266Sopenharmony_ci  struct Curl_cfilter *cf = NULL;
173713498266Sopenharmony_ci  CURLcode result;
173813498266Sopenharmony_ci
173913498266Sopenharmony_ci  (void)data;
174013498266Sopenharmony_ci  (void)conn;
174113498266Sopenharmony_ci  DEBUGASSERT(transport == TRNSPRT_UDP || transport == TRNSPRT_QUIC);
174213498266Sopenharmony_ci  ctx = calloc(1, sizeof(*ctx));
174313498266Sopenharmony_ci  if(!ctx) {
174413498266Sopenharmony_ci    result = CURLE_OUT_OF_MEMORY;
174513498266Sopenharmony_ci    goto out;
174613498266Sopenharmony_ci  }
174713498266Sopenharmony_ci  cf_socket_ctx_init(ctx, ai, transport);
174813498266Sopenharmony_ci
174913498266Sopenharmony_ci  result = Curl_cf_create(&cf, &Curl_cft_udp, ctx);
175013498266Sopenharmony_ci
175113498266Sopenharmony_ciout:
175213498266Sopenharmony_ci  *pcf = (!result)? cf : NULL;
175313498266Sopenharmony_ci  if(result) {
175413498266Sopenharmony_ci    Curl_safefree(cf);
175513498266Sopenharmony_ci    Curl_safefree(ctx);
175613498266Sopenharmony_ci  }
175713498266Sopenharmony_ci
175813498266Sopenharmony_ci  return result;
175913498266Sopenharmony_ci}
176013498266Sopenharmony_ci
176113498266Sopenharmony_ci/* this is the TCP filter which can also handle this case */
176213498266Sopenharmony_cistruct Curl_cftype Curl_cft_unix = {
176313498266Sopenharmony_ci  "UNIX",
176413498266Sopenharmony_ci  CF_TYPE_IP_CONNECT,
176513498266Sopenharmony_ci  CURL_LOG_LVL_NONE,
176613498266Sopenharmony_ci  cf_socket_destroy,
176713498266Sopenharmony_ci  cf_tcp_connect,
176813498266Sopenharmony_ci  cf_socket_close,
176913498266Sopenharmony_ci  cf_socket_get_host,
177013498266Sopenharmony_ci  cf_socket_adjust_pollset,
177113498266Sopenharmony_ci  cf_socket_data_pending,
177213498266Sopenharmony_ci  cf_socket_send,
177313498266Sopenharmony_ci  cf_socket_recv,
177413498266Sopenharmony_ci  cf_socket_cntrl,
177513498266Sopenharmony_ci  cf_socket_conn_is_alive,
177613498266Sopenharmony_ci  Curl_cf_def_conn_keep_alive,
177713498266Sopenharmony_ci  cf_socket_query,
177813498266Sopenharmony_ci};
177913498266Sopenharmony_ci
178013498266Sopenharmony_ciCURLcode Curl_cf_unix_create(struct Curl_cfilter **pcf,
178113498266Sopenharmony_ci                             struct Curl_easy *data,
178213498266Sopenharmony_ci                             struct connectdata *conn,
178313498266Sopenharmony_ci                             const struct Curl_addrinfo *ai,
178413498266Sopenharmony_ci                             int transport)
178513498266Sopenharmony_ci{
178613498266Sopenharmony_ci  struct cf_socket_ctx *ctx = NULL;
178713498266Sopenharmony_ci  struct Curl_cfilter *cf = NULL;
178813498266Sopenharmony_ci  CURLcode result;
178913498266Sopenharmony_ci
179013498266Sopenharmony_ci  (void)data;
179113498266Sopenharmony_ci  (void)conn;
179213498266Sopenharmony_ci  DEBUGASSERT(transport == TRNSPRT_UNIX);
179313498266Sopenharmony_ci  ctx = calloc(1, sizeof(*ctx));
179413498266Sopenharmony_ci  if(!ctx) {
179513498266Sopenharmony_ci    result = CURLE_OUT_OF_MEMORY;
179613498266Sopenharmony_ci    goto out;
179713498266Sopenharmony_ci  }
179813498266Sopenharmony_ci  cf_socket_ctx_init(ctx, ai, transport);
179913498266Sopenharmony_ci
180013498266Sopenharmony_ci  result = Curl_cf_create(&cf, &Curl_cft_unix, ctx);
180113498266Sopenharmony_ci
180213498266Sopenharmony_ciout:
180313498266Sopenharmony_ci  *pcf = (!result)? cf : NULL;
180413498266Sopenharmony_ci  if(result) {
180513498266Sopenharmony_ci    Curl_safefree(cf);
180613498266Sopenharmony_ci    Curl_safefree(ctx);
180713498266Sopenharmony_ci  }
180813498266Sopenharmony_ci
180913498266Sopenharmony_ci  return result;
181013498266Sopenharmony_ci}
181113498266Sopenharmony_ci
181213498266Sopenharmony_cistatic CURLcode cf_tcp_accept_connect(struct Curl_cfilter *cf,
181313498266Sopenharmony_ci                                      struct Curl_easy *data,
181413498266Sopenharmony_ci                                      bool blocking, bool *done)
181513498266Sopenharmony_ci{
181613498266Sopenharmony_ci  /* we start accepted, if we ever close, we cannot go on */
181713498266Sopenharmony_ci  (void)data;
181813498266Sopenharmony_ci  (void)blocking;
181913498266Sopenharmony_ci  if(cf->connected) {
182013498266Sopenharmony_ci    *done = TRUE;
182113498266Sopenharmony_ci    return CURLE_OK;
182213498266Sopenharmony_ci  }
182313498266Sopenharmony_ci  return CURLE_FAILED_INIT;
182413498266Sopenharmony_ci}
182513498266Sopenharmony_ci
182613498266Sopenharmony_cistruct Curl_cftype Curl_cft_tcp_accept = {
182713498266Sopenharmony_ci  "TCP-ACCEPT",
182813498266Sopenharmony_ci  CF_TYPE_IP_CONNECT,
182913498266Sopenharmony_ci  CURL_LOG_LVL_NONE,
183013498266Sopenharmony_ci  cf_socket_destroy,
183113498266Sopenharmony_ci  cf_tcp_accept_connect,
183213498266Sopenharmony_ci  cf_socket_close,
183313498266Sopenharmony_ci  cf_socket_get_host,              /* TODO: not accurate */
183413498266Sopenharmony_ci  cf_socket_adjust_pollset,
183513498266Sopenharmony_ci  cf_socket_data_pending,
183613498266Sopenharmony_ci  cf_socket_send,
183713498266Sopenharmony_ci  cf_socket_recv,
183813498266Sopenharmony_ci  cf_socket_cntrl,
183913498266Sopenharmony_ci  cf_socket_conn_is_alive,
184013498266Sopenharmony_ci  Curl_cf_def_conn_keep_alive,
184113498266Sopenharmony_ci  cf_socket_query,
184213498266Sopenharmony_ci};
184313498266Sopenharmony_ci
184413498266Sopenharmony_ciCURLcode Curl_conn_tcp_listen_set(struct Curl_easy *data,
184513498266Sopenharmony_ci                                  struct connectdata *conn,
184613498266Sopenharmony_ci                                  int sockindex, curl_socket_t *s)
184713498266Sopenharmony_ci{
184813498266Sopenharmony_ci  CURLcode result;
184913498266Sopenharmony_ci  struct Curl_cfilter *cf = NULL;
185013498266Sopenharmony_ci  struct cf_socket_ctx *ctx = NULL;
185113498266Sopenharmony_ci
185213498266Sopenharmony_ci  /* replace any existing */
185313498266Sopenharmony_ci  Curl_conn_cf_discard_all(data, conn, sockindex);
185413498266Sopenharmony_ci  DEBUGASSERT(conn->sock[sockindex] == CURL_SOCKET_BAD);
185513498266Sopenharmony_ci
185613498266Sopenharmony_ci  ctx = calloc(1, sizeof(*ctx));
185713498266Sopenharmony_ci  if(!ctx) {
185813498266Sopenharmony_ci    result = CURLE_OUT_OF_MEMORY;
185913498266Sopenharmony_ci    goto out;
186013498266Sopenharmony_ci  }
186113498266Sopenharmony_ci  ctx->transport = conn->transport;
186213498266Sopenharmony_ci  ctx->sock = *s;
186313498266Sopenharmony_ci  ctx->accepted = FALSE;
186413498266Sopenharmony_ci  result = Curl_cf_create(&cf, &Curl_cft_tcp_accept, ctx);
186513498266Sopenharmony_ci  if(result)
186613498266Sopenharmony_ci    goto out;
186713498266Sopenharmony_ci  Curl_conn_cf_add(data, conn, sockindex, cf);
186813498266Sopenharmony_ci
186913498266Sopenharmony_ci  conn->sock[sockindex] = ctx->sock;
187013498266Sopenharmony_ci  set_local_ip(cf, data);
187113498266Sopenharmony_ci  ctx->active = TRUE;
187213498266Sopenharmony_ci  ctx->connected_at = Curl_now();
187313498266Sopenharmony_ci  cf->connected = TRUE;
187413498266Sopenharmony_ci  CURL_TRC_CF(data, cf, "Curl_conn_tcp_listen_set(%"
187513498266Sopenharmony_ci              CURL_FORMAT_SOCKET_T ")", ctx->sock);
187613498266Sopenharmony_ci
187713498266Sopenharmony_ciout:
187813498266Sopenharmony_ci  if(result) {
187913498266Sopenharmony_ci    Curl_safefree(cf);
188013498266Sopenharmony_ci    Curl_safefree(ctx);
188113498266Sopenharmony_ci  }
188213498266Sopenharmony_ci  return result;
188313498266Sopenharmony_ci}
188413498266Sopenharmony_ci
188513498266Sopenharmony_cistatic void set_accepted_remote_ip(struct Curl_cfilter *cf,
188613498266Sopenharmony_ci                                   struct Curl_easy *data)
188713498266Sopenharmony_ci{
188813498266Sopenharmony_ci  struct cf_socket_ctx *ctx = cf->ctx;
188913498266Sopenharmony_ci#ifdef HAVE_GETPEERNAME
189013498266Sopenharmony_ci  char buffer[STRERROR_LEN];
189113498266Sopenharmony_ci  struct Curl_sockaddr_storage ssrem;
189213498266Sopenharmony_ci  curl_socklen_t plen;
189313498266Sopenharmony_ci
189413498266Sopenharmony_ci  ctx->r_ip[0] = 0;
189513498266Sopenharmony_ci  ctx->r_port = 0;
189613498266Sopenharmony_ci  plen = sizeof(ssrem);
189713498266Sopenharmony_ci  memset(&ssrem, 0, plen);
189813498266Sopenharmony_ci  if(getpeername(ctx->sock, (struct sockaddr*) &ssrem, &plen)) {
189913498266Sopenharmony_ci    int error = SOCKERRNO;
190013498266Sopenharmony_ci    failf(data, "getpeername() failed with errno %d: %s",
190113498266Sopenharmony_ci          error, Curl_strerror(error, buffer, sizeof(buffer)));
190213498266Sopenharmony_ci    return;
190313498266Sopenharmony_ci  }
190413498266Sopenharmony_ci  if(!Curl_addr2string((struct sockaddr*)&ssrem, plen,
190513498266Sopenharmony_ci                       ctx->r_ip, &ctx->r_port)) {
190613498266Sopenharmony_ci    failf(data, "ssrem inet_ntop() failed with errno %d: %s",
190713498266Sopenharmony_ci          errno, Curl_strerror(errno, buffer, sizeof(buffer)));
190813498266Sopenharmony_ci    return;
190913498266Sopenharmony_ci  }
191013498266Sopenharmony_ci#else
191113498266Sopenharmony_ci  ctx->r_ip[0] = 0;
191213498266Sopenharmony_ci  ctx->r_port = 0;
191313498266Sopenharmony_ci  (void)data;
191413498266Sopenharmony_ci#endif
191513498266Sopenharmony_ci}
191613498266Sopenharmony_ci
191713498266Sopenharmony_ciCURLcode Curl_conn_tcp_accepted_set(struct Curl_easy *data,
191813498266Sopenharmony_ci                                    struct connectdata *conn,
191913498266Sopenharmony_ci                                    int sockindex, curl_socket_t *s)
192013498266Sopenharmony_ci{
192113498266Sopenharmony_ci  struct Curl_cfilter *cf = NULL;
192213498266Sopenharmony_ci  struct cf_socket_ctx *ctx = NULL;
192313498266Sopenharmony_ci
192413498266Sopenharmony_ci  cf = conn->cfilter[sockindex];
192513498266Sopenharmony_ci  if(!cf || cf->cft != &Curl_cft_tcp_accept)
192613498266Sopenharmony_ci    return CURLE_FAILED_INIT;
192713498266Sopenharmony_ci
192813498266Sopenharmony_ci  ctx = cf->ctx;
192913498266Sopenharmony_ci  /* discard the listen socket */
193013498266Sopenharmony_ci  socket_close(data, conn, TRUE, ctx->sock);
193113498266Sopenharmony_ci  ctx->sock = *s;
193213498266Sopenharmony_ci  conn->sock[sockindex] = ctx->sock;
193313498266Sopenharmony_ci  set_accepted_remote_ip(cf, data);
193413498266Sopenharmony_ci  set_local_ip(cf, data);
193513498266Sopenharmony_ci  ctx->active = TRUE;
193613498266Sopenharmony_ci  ctx->accepted = TRUE;
193713498266Sopenharmony_ci  ctx->connected_at = Curl_now();
193813498266Sopenharmony_ci  cf->connected = TRUE;
193913498266Sopenharmony_ci  CURL_TRC_CF(data, cf, "accepted_set(sock=%" CURL_FORMAT_SOCKET_T
194013498266Sopenharmony_ci              ", remote=%s port=%d)",
194113498266Sopenharmony_ci              ctx->sock, ctx->r_ip, ctx->r_port);
194213498266Sopenharmony_ci
194313498266Sopenharmony_ci  return CURLE_OK;
194413498266Sopenharmony_ci}
194513498266Sopenharmony_ci
194613498266Sopenharmony_ci/**
194713498266Sopenharmony_ci * Return TRUE iff `cf` is a socket filter.
194813498266Sopenharmony_ci */
194913498266Sopenharmony_cistatic bool cf_is_socket(struct Curl_cfilter *cf)
195013498266Sopenharmony_ci{
195113498266Sopenharmony_ci  return cf && (cf->cft == &Curl_cft_tcp ||
195213498266Sopenharmony_ci                cf->cft == &Curl_cft_udp ||
195313498266Sopenharmony_ci                cf->cft == &Curl_cft_unix ||
195413498266Sopenharmony_ci                cf->cft == &Curl_cft_tcp_accept);
195513498266Sopenharmony_ci}
195613498266Sopenharmony_ci
195713498266Sopenharmony_ciCURLcode Curl_cf_socket_peek(struct Curl_cfilter *cf,
195813498266Sopenharmony_ci                             struct Curl_easy *data,
195913498266Sopenharmony_ci                             curl_socket_t *psock,
196013498266Sopenharmony_ci                             const struct Curl_sockaddr_ex **paddr,
196113498266Sopenharmony_ci                             const char **pr_ip_str, int *pr_port,
196213498266Sopenharmony_ci                             const char **pl_ip_str, int *pl_port)
196313498266Sopenharmony_ci{
196413498266Sopenharmony_ci  if(cf_is_socket(cf) && cf->ctx) {
196513498266Sopenharmony_ci    struct cf_socket_ctx *ctx = cf->ctx;
196613498266Sopenharmony_ci
196713498266Sopenharmony_ci    if(psock)
196813498266Sopenharmony_ci      *psock = ctx->sock;
196913498266Sopenharmony_ci    if(paddr)
197013498266Sopenharmony_ci      *paddr = &ctx->addr;
197113498266Sopenharmony_ci    if(pr_ip_str)
197213498266Sopenharmony_ci      *pr_ip_str = ctx->r_ip;
197313498266Sopenharmony_ci    if(pr_port)
197413498266Sopenharmony_ci      *pr_port = ctx->r_port;
197513498266Sopenharmony_ci    if(pl_port ||pl_ip_str) {
197613498266Sopenharmony_ci      set_local_ip(cf, data);
197713498266Sopenharmony_ci      if(pl_ip_str)
197813498266Sopenharmony_ci        *pl_ip_str = ctx->l_ip;
197913498266Sopenharmony_ci      if(pl_port)
198013498266Sopenharmony_ci        *pl_port = ctx->l_port;
198113498266Sopenharmony_ci    }
198213498266Sopenharmony_ci    return CURLE_OK;
198313498266Sopenharmony_ci  }
198413498266Sopenharmony_ci  return CURLE_FAILED_INIT;
198513498266Sopenharmony_ci}
1986