Coverage Report

Created: 2026-09-01 06:58

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/curl/lib/cf-socket.c
Line
Count
Source
1
/***************************************************************************
2
 *                                  _   _ ____  _
3
 *  Project                     ___| | | |  _ \| |
4
 *                             / __| | | | |_) | |
5
 *                            | (__| |_| |  _ <| |___
6
 *                             \___|\___/|_| \_\_____|
7
 *
8
 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
9
 *
10
 * This software is licensed as described in the file COPYING, which
11
 * you should have received as part of this distribution. The terms
12
 * are also available at https://curl.se/docs/copyright.html.
13
 *
14
 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15
 * copies of the Software, and permit persons to whom the Software is
16
 * furnished to do so, under the terms of the COPYING file.
17
 *
18
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19
 * KIND, either express or implied.
20
 *
21
 * SPDX-License-Identifier: curl
22
 *
23
 ***************************************************************************/
24
#include "curl_setup.h"
25
26
#ifdef HAVE_NETINET_IN_H
27
#include <netinet/in.h> /* <netinet/tcp.h> may need it */
28
#endif
29
#ifdef HAVE_SYS_UN_H
30
#include <sys/un.h> /* for sockaddr_un */
31
#endif
32
#ifdef HAVE_LINUX_TCP_H
33
#include <linux/tcp.h>
34
#elif defined(HAVE_NETINET_TCP_H)
35
#include <netinet/tcp.h>
36
#endif
37
#ifdef HAVE_NETINET_UDP_H
38
#include <netinet/udp.h>
39
#endif
40
#ifdef HAVE_NETINET_IP_H
41
#include <netinet/ip.h>
42
#endif
43
#ifdef HAVE_SYS_IOCTL_H
44
#include <sys/ioctl.h>
45
#endif
46
#ifdef HAVE_NETDB_H
47
#include <netdb.h>
48
#endif
49
#ifdef HAVE_ARPA_INET_H
50
#include <arpa/inet.h>
51
#endif
52
53
#ifdef HAVE_IFADDRS_H
54
#include <ifaddrs.h>
55
#endif
56
#ifdef HAVE_NET_IF_H
57
#include <net/if.h>
58
#endif
59
#ifdef __VMS
60
#include <in.h>
61
#include <inet.h>
62
#endif
63
64
#include "urldata.h"
65
#include "curl_trc.h"
66
#include "if2ip.h"
67
#include "cfilters.h"
68
#include "cf-socket.h"
69
#include "connect.h"
70
#include "curl_addrinfo.h"
71
#include "select.h"
72
#include "multiif.h"
73
#include "curlx/inet_ntop.h"
74
#include "curlx/inet_pton.h"
75
#include "progress.h"
76
#include "conncache.h"
77
#include "multihandle.h"
78
#include "rand.h"
79
#include "sockaddr.h"
80
#include "curlx/strdup.h"
81
#include "curlx/nonblock.h"
82
#include "curlx/strcopy.h"
83
#include "curlx/version_win32.h"
84
#include "curlx/strerr.h"
85
#include "curlx/strparse.h"
86
#ifdef _WIN32
87
#include <mstcpip.h> /* for TCP_INITIAL_RTO_PARAMETERS */
88
#endif
89
90
/* retrieves ip address and port from a sockaddr structure. note it calls
91
 * curlx_inet_ntop() and returns CURLcode.
92
 * @unittest 1607
93
 */
94
UNITTEST CURLcode sockaddr2string(struct sockaddr *sa, curl_socklen_t salen,
95
                                  char *addr, uint16_t *port);
96
UNITTEST CURLcode sockaddr2string(struct sockaddr *sa, curl_socklen_t salen,
97
                                  char *addr, uint16_t *port)
98
17.4k
{
99
17.4k
  CURLcode result;
100
17.4k
  struct sockaddr_in *si = NULL;
101
17.4k
#ifdef USE_IPV6
102
17.4k
  struct sockaddr_in6 *si6 = NULL;
103
17.4k
#endif
104
17.4k
#ifdef USE_UNIX_SOCKETS
105
17.4k
  struct sockaddr_un *su = NULL;
106
#else
107
  (void)salen;
108
#endif
109
110
17.4k
  switch(sa->sa_family) {
111
6.87k
  case AF_INET:
112
6.87k
    si = (struct sockaddr_in *)(void *)sa;
113
6.87k
    result = curlx_inet_ntop(sa->sa_family, &si->sin_addr, addr,
114
6.87k
                             MAX_IPADR_LEN);
115
6.87k
    if(!result) {
116
6.87k
      *port = ntohs(si->sin_port);
117
6.87k
      return result;
118
6.87k
    }
119
0
    break;
120
0
#ifdef USE_IPV6
121
0
  case AF_INET6:
122
0
    si6 = (struct sockaddr_in6 *)(void *)sa;
123
0
    result = curlx_inet_ntop(sa->sa_family, &si6->sin6_addr, addr,
124
0
                             MAX_IPADR_LEN);
125
0
    if(!result) {
126
0
      *port = ntohs(si6->sin6_port);
127
0
      return result;
128
0
    }
129
0
    break;
130
0
#endif
131
0
#ifdef USE_UNIX_SOCKETS
132
10.6k
  case AF_UNIX:
133
10.6k
    if(salen > (curl_socklen_t)sizeof(CURL_SA_FAMILY_T)) {
134
40
      su = (struct sockaddr_un *)sa;
135
40
      curl_msnprintf(addr, MAX_IPADR_LEN, "%s", su->sun_path);
136
40
    }
137
10.5k
    else
138
10.5k
      addr[0] = 0; /* socket with no name */
139
10.6k
    *port = 0;
140
10.6k
    return CURLE_OK;
141
0
#endif
142
0
  default:
143
0
    result = CURLE_UNSUPPORTED_PROTOCOL;
144
0
    break;
145
17.4k
  }
146
147
0
  addr[0] = '\0';
148
0
  *port = 0;
149
0
  return result;
150
17.4k
}
151
152
static void tcpnodelay(struct Curl_cfilter *cf,
153
                       struct Curl_easy *data,
154
                       curl_socket_t sockfd)
155
6.87k
{
156
6.87k
#if defined(TCP_NODELAY) && defined(CURL_TCP_NODELAY_SUPPORTED)
157
6.87k
  curl_socklen_t onoff = (curl_socklen_t)1;
158
6.87k
  int level = IPPROTO_TCP;
159
6.87k
  VERBOSE(char buffer[STRERROR_LEN]);
160
161
6.87k
  if(setsockopt(sockfd, level, TCP_NODELAY, (void *)&onoff, sizeof(onoff)) < 0)
162
6.87k
    CURL_TRC_CF(data, cf, "Could not set TCP_NODELAY: %s",
163
6.87k
                curlx_strerror(SOCKERRNO, buffer, sizeof(buffer)));
164
#else
165
  (void)cf;
166
  (void)data;
167
  (void)sockfd;
168
#endif
169
6.87k
}
170
171
#if defined(USE_WINSOCK) || defined(TCP_KEEPIDLE) ||               \
172
  defined(TCP_KEEPALIVE) || defined(TCP_KEEPALIVE_THRESHOLD) ||    \
173
  defined(TCP_KEEPINTVL) || defined(TCP_KEEPALIVE_ABORT_THRESHOLD)
174
#if defined(USE_WINSOCK) || \
175
  (defined(__sun) && !defined(TCP_KEEPIDLE)) || \
176
  (defined(__DragonFly__) && __DragonFly_version < 500702) || \
177
  (defined(_WIN32) && !defined(TCP_KEEPIDLE))
178
/* Solaris < 11.4, DragonFlyBSD < 500702 and Windows < 10.0.16299
179
 * use millisecond units. */
180
#define KEEPALIVE_FACTOR(x) ((x) *= 1000)
181
#else
182
#define KEEPALIVE_FACTOR(x)
183
#endif
184
#endif
185
186
static void tcpkeepalive(struct Curl_cfilter *cf,
187
                         struct Curl_easy *data,
188
                         curl_socket_t sockfd)
189
38
{
190
38
  int optval = data->set.tcp_keepalive ? 1 : 0;
191
192
  /* only set IDLE and INTVL if setting KEEPALIVE is successful */
193
38
  if(setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE,
194
38
                (void *)&optval, sizeof(optval)) < 0) {
195
0
    CURL_TRC_CF(data, cf, "Failed to set SO_KEEPALIVE on fd "
196
0
                "%" FMT_SOCKET_T ": errno %d", sockfd, SOCKERRNO);
197
0
  }
198
38
  else {
199
#ifdef USE_WINSOCK
200
    /* Windows 10, version 1709 (10.0.16299) and later versions can use
201
       setsockopt() TCP_KEEP*. Older versions return with failure. */
202
    if(curlx_verify_windows_version(10, 0, 16299, PLATFORM_WINNT,
203
                                    VERSION_GREATER_THAN_EQUAL)) {
204
      CURL_TRC_CF(data, cf, "Set TCP_KEEP* on fd=%" FMT_SOCKET_T, sockfd);
205
      optval = curlx_sltosi(data->set.tcp_keepidle);
206
/* Offered by mingw-w64 v12+, MS SDK 6.0A/VS2008+ */
207
#ifndef TCP_KEEPALIVE
208
#define TCP_KEEPALIVE 3
209
#endif
210
/* Offered by mingw-w64 v12+, MS SDK 10.0.15063.0/VS2017 15.1+ */
211
#ifndef TCP_KEEPCNT
212
#define TCP_KEEPCNT 16
213
#endif
214
/* Offered by mingw-w64 v12+, MS SDK 10.0.16299.0/VS2017 15.4+ */
215
#ifndef TCP_KEEPIDLE
216
#define TCP_KEEPIDLE TCP_KEEPALIVE
217
#endif
218
#ifndef TCP_KEEPINTVL
219
#define TCP_KEEPINTVL 17
220
#endif
221
      if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPIDLE,
222
                    (const char *)&optval, sizeof(optval)) < 0) {
223
        CURL_TRC_CF(data, cf, "Failed to set TCP_KEEPIDLE on fd "
224
                    "%" FMT_SOCKET_T ": errno %d", sockfd, SOCKERRNO);
225
      }
226
      optval = curlx_sltosi(data->set.tcp_keepintvl);
227
      if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPINTVL,
228
                    (const char *)&optval, sizeof(optval)) < 0) {
229
        CURL_TRC_CF(data, cf, "Failed to set TCP_KEEPINTVL on fd "
230
                    "%" FMT_SOCKET_T ": errno %d", sockfd, SOCKERRNO);
231
      }
232
      optval = curlx_sltosi(data->set.tcp_keepcnt);
233
      if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPCNT,
234
                    (const char *)&optval, sizeof(optval)) < 0) {
235
        CURL_TRC_CF(data, cf, "Failed to set TCP_KEEPCNT on fd "
236
                    "%" FMT_SOCKET_T ": errno %d", sockfd, SOCKERRNO);
237
      }
238
    }
239
    else {
240
/* Offered by mingw-w64 and MS SDK. Latter only when targeting Win7+. */
241
#ifndef SIO_KEEPALIVE_VALS
242
#define SIO_KEEPALIVE_VALS  _WSAIOW(IOC_VENDOR, 4)
243
      struct tcp_keepalive {
244
        u_long onoff;
245
        u_long keepalivetime;
246
        u_long keepaliveinterval;
247
      };
248
#endif
249
      struct tcp_keepalive vals;
250
      DWORD dummy;
251
      vals.onoff = 1;
252
      optval = curlx_sltosi(data->set.tcp_keepidle);
253
      KEEPALIVE_FACTOR(optval);
254
      vals.keepalivetime = (u_long)optval;
255
      optval = curlx_sltosi(data->set.tcp_keepintvl);
256
      KEEPALIVE_FACTOR(optval);
257
      vals.keepaliveinterval = (u_long)optval;
258
      if(WSAIoctl(sockfd, SIO_KEEPALIVE_VALS, (LPVOID)&vals, sizeof(vals),
259
                  NULL, 0, &dummy, NULL, NULL) != 0) {
260
        CURL_TRC_CF(data, cf, "Failed to set SIO_KEEPALIVE_VALS on fd "
261
                    "%" FMT_SOCKET_T ": errno %d", sockfd, SOCKERRNO);
262
      }
263
    }
264
#else /* !USE_WINSOCK */
265
38
#ifdef TCP_KEEPIDLE
266
38
    optval = curlx_sltosi(data->set.tcp_keepidle);
267
38
    KEEPALIVE_FACTOR(optval);
268
38
    if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPIDLE,
269
38
                  (void *)&optval, sizeof(optval)) < 0) {
270
38
      CURL_TRC_CF(data, cf, "Failed to set TCP_KEEPIDLE on fd "
271
38
                  "%" FMT_SOCKET_T ": errno %d", sockfd, SOCKERRNO);
272
38
    }
273
#elif defined(TCP_KEEPALIVE)
274
    /* macOS style */
275
    optval = curlx_sltosi(data->set.tcp_keepidle);
276
    KEEPALIVE_FACTOR(optval);
277
    if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPALIVE,
278
                  (void *)&optval, sizeof(optval)) < 0) {
279
      CURL_TRC_CF(data, cf, "Failed to set TCP_KEEPALIVE on fd "
280
                  "%" FMT_SOCKET_T ": errno %d", sockfd, SOCKERRNO);
281
    }
282
#elif defined(TCP_KEEPALIVE_THRESHOLD)
283
    /* Solaris <11.4 style */
284
    optval = curlx_sltosi(data->set.tcp_keepidle);
285
    KEEPALIVE_FACTOR(optval);
286
    if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPALIVE_THRESHOLD,
287
                  (void *)&optval, sizeof(optval)) < 0) {
288
      CURL_TRC_CF(data, cf, "Failed to set TCP_KEEPALIVE_THRESHOLD on fd "
289
                  "%" FMT_SOCKET_T ": errno %d", sockfd, SOCKERRNO);
290
    }
291
#endif
292
38
#ifdef TCP_KEEPINTVL
293
38
    optval = curlx_sltosi(data->set.tcp_keepintvl);
294
38
    KEEPALIVE_FACTOR(optval);
295
38
    if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPINTVL,
296
38
                  (void *)&optval, sizeof(optval)) < 0) {
297
38
      CURL_TRC_CF(data, cf, "Failed to set TCP_KEEPINTVL on fd "
298
38
                  "%" FMT_SOCKET_T ": errno %d", sockfd, SOCKERRNO);
299
38
    }
300
#elif defined(TCP_KEEPALIVE_ABORT_THRESHOLD)
301
    /* Solaris <11.4 style */
302
    /* TCP_KEEPALIVE_ABORT_THRESHOLD should equal to
303
     * TCP_KEEPCNT * TCP_KEEPINTVL on other platforms.
304
     * The default value of TCP_KEEPCNT is 9 on Linux,
305
     * 8 on *BSD/macOS, 5 or 10 on Windows. We use the
306
     * default config for Solaris <11.4 because there is
307
     * no default value for TCP_KEEPCNT on Solaris 11.4.
308
     *
309
     * Note that the consequent probes will not be sent
310
     * at equal intervals on Solaris, but will be sent
311
     * using the exponential backoff algorithm. */
312
    {
313
      int keepcnt = curlx_sltosi(data->set.tcp_keepcnt);
314
      int keepintvl = curlx_sltosi(data->set.tcp_keepintvl);
315
316
      if(keepcnt > 0 && keepintvl > (INT_MAX / keepcnt))
317
        optval = INT_MAX;
318
      else
319
        optval = keepcnt * keepintvl;
320
    }
321
    KEEPALIVE_FACTOR(optval);
322
    if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPALIVE_ABORT_THRESHOLD,
323
                  (void *)&optval, sizeof(optval)) < 0) {
324
      CURL_TRC_CF(data, cf, "Failed to set TCP_KEEPALIVE_ABORT_THRESHOLD"
325
                  " on fd %" FMT_SOCKET_T ": errno %d", sockfd, SOCKERRNO);
326
    }
327
#endif
328
38
#ifdef TCP_KEEPCNT
329
38
    optval = curlx_sltosi(data->set.tcp_keepcnt);
330
38
    if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPCNT,
331
38
                  (void *)&optval, sizeof(optval)) < 0) {
332
38
      CURL_TRC_CF(data, cf, "Failed to set TCP_KEEPCNT on fd "
333
38
                  "%" FMT_SOCKET_T ": errno %d", sockfd, SOCKERRNO);
334
38
    }
335
38
#endif
336
38
#endif /* USE_WINSOCK */
337
38
  }
338
38
}
339
340
/**
341
 * Assign the addrinfo `ai` to the Curl_sockaddr_ex `addr` with
342
 * transport determining socktype and protocol.
343
 */
344
CURLcode Curl_socket_addr_from_ai(struct Curl_sockaddr_ex *addr,
345
                                  const struct Curl_addrinfo *ai,
346
                                  uint8_t transport)
347
6.91k
{
348
  /*
349
   * The Curl_sockaddr_ex structure is libcurl's external API
350
   * curl_sockaddr structure with enough space available to directly hold
351
   * any protocol-specific address structures. The variable declared here
352
   * will be used to pass / receive data to/from the fopensocket callback
353
   * if this has been set, before that, it is initialized from parameters.
354
   */
355
6.91k
  addr->family = ai->ai_family;
356
6.91k
  addr->socktype = Curl_socktype_for_transport(transport);
357
6.91k
  addr->protocol = Curl_protocol_for_transport(transport);
358
6.91k
  addr->addrlen = (unsigned int)ai->ai_addrlen;
359
360
6.91k
  DEBUGASSERT(addr->addrlen <= sizeof(addr->curl_sa_addrbuf));
361
6.91k
  if(addr->addrlen > sizeof(addr->curl_sa_addrbuf))
362
0
    return CURLE_TOO_LARGE;
363
364
6.91k
  memcpy(&addr->curl_sa_addrbuf, ai->ai_addr, addr->addrlen);
365
6.91k
  return CURLE_OK;
366
6.91k
}
367
368
#ifdef USE_SO_NOSIGPIPE
369
int Curl_sock_nosigpipe(curl_socket_t sockfd)
370
{
371
  int onoff = 1;
372
  return setsockopt(sockfd, SOL_SOCKET, SO_NOSIGPIPE,
373
                    (void *)&onoff, sizeof(onoff));
374
}
375
#endif /* USE_SO_NOSIGPIPE */
376
377
#if defined(USE_IPV6) && defined(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID)
378
static uint32_t get_scope_id(struct Curl_easy *data,
379
                             struct sockaddr_in6 *sa6)
380
0
{
381
0
  uint32_t scope_id = 0;
382
0
  if(data->conn->scope_id)
383
0
    return data->conn->scope_id;
384
  /* NOLINTNEXTLINE(clang-analyzer-core.uninitialized.Assign) */
385
0
  scope_id = sa6->sin6_scope_id;
386
0
  if(!scope_id && IN6_IS_ADDR_LINKLOCAL(&sa6->sin6_addr)) {
387
    /* The resolver did not set scope_id for this link-local address.
388
     * Try to determine it from the system's network interfaces.
389
     * Without a scope_id, connect() to a link-local address fails
390
     * with EINVAL on Linux.
391
     * NOTE: On multi-homed hosts with several interfaces having
392
     * link-local addresses, this picks the first one found, which
393
     * may not be the correct outgoing interface. */
394
0
#if defined(HAVE_GETIFADDRS) && defined(HAVE_NET_IF_H)
395
0
    struct ifaddrs *ifa, *ifa_list;
396
0
    if(getifaddrs(&ifa_list) == 0) {
397
0
      for(ifa = ifa_list; ifa; ifa = ifa->ifa_next) {
398
0
        if(ifa->ifa_addr && ifa->ifa_addr->sa_family == AF_INET6 &&
399
0
           (ifa->ifa_flags & IFF_UP) &&
400
0
           !(ifa->ifa_flags & IFF_LOOPBACK)) {
401
0
          struct sockaddr_in6 *s6 = (void *)ifa->ifa_addr;
402
0
          if(IN6_IS_ADDR_LINKLOCAL(&s6->sin6_addr) && s6->sin6_scope_id) {
403
0
            scope_id = s6->sin6_scope_id;
404
0
            infof(data,
405
0
                  "determined scope_id=%lu for link-local address "
406
0
                  "from local interface",
407
0
                  (unsigned long)scope_id);
408
0
            break;
409
0
          }
410
0
        }
411
0
      }
412
0
      freeifaddrs(ifa_list);
413
0
    }
414
0
#endif /* HAVE_GETIFADDRS && HAVE_NET_IF_H */
415
0
  }
416
0
  return scope_id;
417
0
}
418
#endif
419
420
static CURLcode socket_open(struct Curl_easy *data,
421
                            struct Curl_sockaddr_ex *addr,
422
                            curl_socket_t *sockfd)
423
6.91k
{
424
6.91k
  char errbuf[STRERROR_LEN];
425
426
6.91k
#ifdef SOCK_CLOEXEC
427
6.91k
  addr->socktype |= SOCK_CLOEXEC;
428
6.91k
#endif
429
430
6.91k
  DEBUGASSERT(data);
431
6.91k
  DEBUGASSERT(data->conn);
432
6.91k
  if(data->set.fopensocket) {
433
    /*
434
     * If the opensocket callback is set, all the destination address
435
     * information is passed to the callback. Depending on this information the
436
     * callback may opt to abort the connection, this is indicated returning
437
     * CURL_SOCKET_BAD; otherwise it will return a not-connected socket. When
438
     * the callback returns a valid socket the destination address information
439
     * might have been changed and this 'new' address will actually be used
440
     * here to connect.
441
     */
442
6.91k
    struct Curl_mapi_guard guard;
443
6.91k
    CURL_CBAPI_START(&guard, data, easy_fopensocket);
444
6.91k
    *sockfd = data->set.fopensocket(data->set.opensocket_client,
445
6.91k
                                    CURLSOCKTYPE_IPCXN,
446
6.91k
                                    (struct curl_sockaddr *)addr);
447
6.91k
    CURL_CBAPI_END(&guard);
448
6.91k
  }
449
0
  else {
450
    /* opensocket callback not set, so create the socket now */
451
0
#ifdef DEBUGBUILD
452
0
    if((addr->family == AF_INET6) && getenv("CURL_DBG_SOCK_FAIL_IPV6")) {
453
0
      failf(data, "CURL_DBG_SOCK_FAIL_IPV6: failed to open socket");
454
0
      return CURLE_COULDNT_CONNECT;
455
0
    }
456
0
#endif
457
0
    *sockfd = CURL_SOCKET(addr->family, addr->socktype, addr->protocol);
458
0
    if((*sockfd == CURL_SOCKET_BAD) && (SOCKERRNO == SOCKENOMEM))
459
0
      return CURLE_OUT_OF_MEMORY;
460
0
  }
461
462
6.91k
  if(*sockfd == CURL_SOCKET_BAD) {
463
    /* no socket, no connection */
464
0
    failf(data, "failed to open socket: %s",
465
0
          curlx_strerror(SOCKERRNO, errbuf, sizeof(errbuf)));
466
0
    return CURLE_COULDNT_CONNECT;
467
0
  }
468
469
#ifdef USE_SO_NOSIGPIPE
470
  if(Curl_sock_nosigpipe(*sockfd) < 0) {
471
    failf(data, "setsockopt enable SO_NOSIGPIPE: %s",
472
          curlx_strerror(SOCKERRNO, errbuf, sizeof(errbuf)));
473
    sclose(*sockfd);
474
    *sockfd = CURL_SOCKET_BAD;
475
    return CURLE_COULDNT_CONNECT;
476
  }
477
#endif /* USE_SO_NOSIGPIPE */
478
479
#if defined(HAVE_FCNTL) && !defined(SOCK_CLOEXEC)
480
  if(fcntl(*sockfd, F_SETFD, FD_CLOEXEC) < 0) {
481
    failf(data, "fcntl set CLOEXEC: %s",
482
          curlx_strerror(SOCKERRNO, errbuf, sizeof(errbuf)));
483
    sclose(*sockfd);
484
    *sockfd = CURL_SOCKET_BAD;
485
    return CURLE_COULDNT_CONNECT;
486
  }
487
#endif
488
489
6.91k
#if defined(USE_IPV6) && defined(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID)
490
6.91k
  if(addr->family == AF_INET6) {
491
0
    struct sockaddr_in6 * const sa6 = (void *)&addr->curl_sa_addr;
492
0
    sa6->sin6_scope_id = get_scope_id(data, sa6);
493
0
  }
494
6.91k
#endif
495
6.91k
  return CURLE_OK;
496
6.91k
}
497
498
/*
499
 * Create a socket based on info from 'conn' and 'ai'.
500
 *
501
 * 'addr' should be a pointer to the correct struct to get data back, or NULL.
502
 * 'sockfd' must be a pointer to a socket descriptor.
503
 *
504
 * If the open socket callback is set, used that!
505
 *
506
 */
507
CURLcode Curl_socket_open(struct Curl_easy *data,
508
                          const struct Curl_addrinfo *ai,
509
                          struct Curl_sockaddr_ex *addr,
510
                          uint8_t transport,
511
                          curl_socket_t *sockfd)
512
0
{
513
0
  struct Curl_sockaddr_ex dummy;
514
0
  CURLcode result;
515
516
0
  if(!addr)
517
    /* if the caller does not want info back, use a local temp copy */
518
0
    addr = &dummy;
519
520
0
  result = Curl_socket_addr_from_ai(addr, ai, transport);
521
0
  if(result)
522
0
    return result;
523
524
0
  return socket_open(data, addr, sockfd);
525
0
}
526
527
static int socket_close(struct Curl_easy *data, struct connectdata *conn,
528
                        int use_callback, curl_socket_t sock)
529
6.91k
{
530
6.91k
  if(sock == CURL_SOCKET_BAD)
531
0
    return 0;
532
533
6.91k
  if(use_callback && conn && conn->fclosesocket) {
534
0
    struct Curl_mapi_guard guard;
535
0
    int rc;
536
0
    Curl_multi_will_close(data, sock);
537
0
    CURL_CBAPI_START(&guard, data, easy_closesocket);
538
0
    rc = conn->fclosesocket(conn->closesocket_client, sock);
539
0
    CURL_CBAPI_END(&guard);
540
0
    return rc;
541
0
  }
542
543
6.91k
  if(conn)
544
    /* tell the multi-socket code about this */
545
6.91k
    Curl_multi_will_close(data, sock);
546
547
6.91k
  sclose(sock);
548
549
6.91k
  return 0;
550
6.91k
}
551
552
/*
553
 * Close a socket.
554
 *
555
 * 'conn' can be NULL, beware!
556
 */
557
int Curl_socket_close(struct Curl_easy *data, struct connectdata *conn,
558
                      curl_socket_t sock)
559
0
{
560
0
  return socket_close(data, conn, FALSE, sock);
561
0
}
562
563
/*
564
 * Curl_parse_interface()
565
 *
566
 * This is used to parse interface argument in the following formats.
567
 * In all the examples, `host` can be an IP address or a hostname.
568
 *
569
 *   <iface_or_host> - can be either an interface name or a host.
570
 *   if!<iface> - interface name.
571
 *   host!<host> - hostname.
572
 *   ifhost!<iface>!<host> - interface name and hostname.
573
 *
574
 * Parameters:
575
 *
576
 * input  [in]     - input string.
577
 * len    [in]     - length of the input string.
578
 * dev    [in/out] - address where a pointer to newly allocated memory
579
 *                   holding the interface-or-host will be stored upon
580
 *                   completion.
581
 * iface  [in/out] - address where a pointer to newly allocated memory
582
 *                   holding the interface will be stored upon completion.
583
 * host   [in/out] - address where a pointer to newly allocated memory
584
 *                   holding the host will be stored upon completion.
585
 *
586
 * Returns CURLE_OK on success.
587
 */
588
CURLcode Curl_parse_interface(const char *input,
589
                              char **dev, char **iface, char **host)
590
324
{
591
324
  static const char if_prefix[] = "if!";
592
324
  static const char host_prefix[] = "host!";
593
324
  static const char if_host_prefix[] = "ifhost!";
594
324
  size_t len;
595
596
324
  DEBUGASSERT(dev);
597
324
  DEBUGASSERT(iface);
598
324
  DEBUGASSERT(host);
599
600
324
  len = strlen(input);
601
324
  if(len > 512)
602
1
    return CURLE_BAD_FUNCTION_ARGUMENT;
603
604
323
  if(!strncmp(if_prefix, input, strlen(if_prefix))) {
605
2
    input += strlen(if_prefix);
606
2
    if(!*input)
607
1
      return CURLE_BAD_FUNCTION_ARGUMENT;
608
1
    *iface = curlx_memdup0(input, len - strlen(if_prefix));
609
1
    return *iface ? CURLE_OK : CURLE_OUT_OF_MEMORY;
610
2
  }
611
321
  else if(!strncmp(host_prefix, input, strlen(host_prefix))) {
612
2
    input += strlen(host_prefix);
613
2
    if(!*input)
614
1
      return CURLE_BAD_FUNCTION_ARGUMENT;
615
1
    *host = curlx_memdup0(input, len - strlen(host_prefix));
616
1
    return *host ? CURLE_OK : CURLE_OUT_OF_MEMORY;
617
2
  }
618
319
  else if(!strncmp(if_host_prefix, input, strlen(if_host_prefix))) {
619
3
    const char *host_part;
620
3
    input += strlen(if_host_prefix);
621
3
    len -= strlen(if_host_prefix);
622
3
    host_part = memchr(input, '!', len);
623
3
    if(!host_part || !*(host_part + 1))
624
2
      return CURLE_BAD_FUNCTION_ARGUMENT;
625
1
    *iface = curlx_memdup0(input, host_part - input);
626
1
    if(!*iface)
627
0
      return CURLE_OUT_OF_MEMORY;
628
1
    ++host_part;
629
1
    *host = curlx_memdup0(host_part, len - (host_part - input));
630
1
    if(!*host) {
631
0
      curlx_safefree(*iface);
632
0
      return CURLE_OUT_OF_MEMORY;
633
0
    }
634
1
    return CURLE_OK;
635
1
  }
636
637
316
  if(!*input)
638
2
    return CURLE_BAD_FUNCTION_ARGUMENT;
639
314
  *dev = curlx_memdup0(input, len);
640
314
  return *dev ? CURLE_OK : CURLE_OUT_OF_MEMORY;
641
316
}
642
643
#ifndef CURL_DISABLE_BINDLOCAL
644
static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn,
645
                          curl_socket_t sockfd, int af, unsigned int scope,
646
                          uint8_t transport)
647
6.87k
{
648
6.87k
  struct Curl_sockaddr_storage sa;
649
6.87k
  struct sockaddr *sock = (struct sockaddr *)&sa;  /* bind to this address */
650
6.87k
  curl_socklen_t sizeof_sa = 0; /* size of the data sock points to */
651
6.87k
  struct sockaddr_in *si4 = (struct sockaddr_in *)&sa;
652
6.87k
#ifdef USE_IPV6
653
6.87k
  struct sockaddr_in6 *si6 = (struct sockaddr_in6 *)&sa;
654
6.87k
#endif
655
656
6.87k
  struct Curl_dns_entry *h = NULL;
657
6.87k
  unsigned short port = data->set.localport; /* use this port number, 0 for
658
                                                "random" */
659
  /* how many port numbers to try to bind to, increasing one at a time */
660
6.87k
  int portnum = data->set.localportrange;
661
6.87k
  const char *dev = CURL_EASY_STR(data, STRING_DEVICE);
662
6.87k
  const char *iface_input = CURL_EASY_STR(data, STRING_INTERFACE);
663
6.87k
  const char *host_input = CURL_EASY_STR(data, STRING_BINDHOST);
664
6.87k
  const char *iface = iface_input ? iface_input : dev;
665
6.87k
  const char *host = host_input ? host_input : dev;
666
6.87k
  int sockerr;
667
6.87k
#ifdef IP_BIND_ADDRESS_NO_PORT
668
6.87k
  int on = 1;
669
6.87k
#endif
670
#ifndef USE_IPV6
671
  (void)scope;
672
#endif
673
674
  /*************************************************************
675
   * Select device to bind socket to
676
   *************************************************************/
677
6.87k
  if(!iface && !host && !port)
678
    /* no local kind of binding was requested */
679
6.80k
    return CURLE_OK;
680
74
  else if(iface && (strlen(iface) >= 255))
681
0
    return CURLE_BAD_FUNCTION_ARGUMENT;
682
683
74
  memset(&sa, 0, sizeof(struct Curl_sockaddr_storage));
684
685
74
  if(iface || host) {
686
63
    char myhost[256] = "";
687
63
    int done = 0; /* -1 for error, 1 for address found */
688
63
    if2ip_result_t if2ip_result = IF2IP_NOT_FOUND;
689
690
63
#ifdef SO_BINDTODEVICE
691
63
    if(iface) {
692
      /*
693
       * This binds the local socket to a particular interface. This will
694
       * force even requests to other local interfaces to go out the external
695
       * interface. Only bind to the interface when specified as interface,
696
       * not as a hostname or ip address.
697
       *
698
       * The interface might be a VRF, eg: vrf-blue, which means it cannot be
699
       * converted to an IP address and would fail Curl_if2ip. Try to
700
       * use it straight away.
701
       */
702
63
      if(setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE,
703
63
                    iface, (curl_socklen_t)strlen(iface) + 1) == 0) {
704
        /* This is often "errno 1, error: Operation not permitted" if you are
705
         * not running as root or another suitable privileged user. If it
706
         * succeeds it means the parameter was a valid interface and not an IP
707
         * address. Return immediately.
708
         */
709
0
        if(!host_input) {
710
0
          infof(data, "socket successfully bound to interface '%s'", iface);
711
0
          return CURLE_OK;
712
0
        }
713
0
      }
714
63
    }
715
63
#endif
716
63
    if(!host_input) {
717
      /* Discover IP from input device, then bind to it */
718
63
      if2ip_result = Curl_if2ip(af,
719
63
#ifdef USE_IPV6
720
63
                                scope, conn->scope_id,
721
63
#endif
722
63
                                iface, myhost, sizeof(myhost));
723
63
    }
724
63
    switch(if2ip_result) {
725
63
    case IF2IP_NOT_FOUND:
726
63
      if(iface_input && !host_input) {
727
        /* Do not fall back to treating it as a hostname */
728
0
        char buffer[STRERROR_LEN];
729
0
        data->state.os_errno = sockerr = SOCKERRNO;
730
0
        failf(data, "Could not bind to interface '%s' with errno %d: %s",
731
0
              iface, sockerr, curlx_strerror(sockerr, buffer, sizeof(buffer)));
732
0
        return CURLE_INTERFACE_FAILED;
733
0
      }
734
63
      break;
735
63
    case IF2IP_AF_NOT_SUPPORTED:
736
      /* Signal the caller to try another address family if available */
737
0
      return CURLE_UNSUPPORTED_PROTOCOL;
738
0
    case IF2IP_FOUND:
739
      /*
740
       * We now have the numerical IP address in the 'myhost' buffer
741
       */
742
0
      host = myhost;
743
0
      infof(data, "Local Interface %s is ip %s using address family %d",
744
0
            iface, host, af);
745
0
      done = 1;
746
0
      break;
747
63
    }
748
63
    if(!iface_input || host_input) {
749
      /*
750
       * This was not an interface, resolve the name as a hostname
751
       * or IP number
752
       *
753
       * Temporarily force name resolution to use only the address type
754
       * of the connection. The resolve functions should really be changed
755
       * to take a type parameter instead.
756
       */
757
63
      uint8_t dns_queries = (af == AF_INET) ?
758
63
                            CURL_DNSQ_A : (CURL_DNSQ_A | CURL_DNSQ_AAAA);
759
63
#ifdef USE_IPV6
760
63
      if(af == AF_INET6)
761
0
        dns_queries = CURL_DNSQ_AAAA;
762
63
#endif
763
764
63
      (void)Curl_resolv_blocking(data, dns_queries, host, 80, transport, &h);
765
63
      if(h) {
766
63
        int h_af = h->addr->ai_family;
767
        /* convert the resolved address, sizeof myhost >= INET_ADDRSTRLEN */
768
63
        Curl_printable_address(h->addr, myhost, sizeof(myhost));
769
63
        infof(data, "Name '%s' family %d resolved to '%s' family %d",
770
63
              host, af, myhost, h_af);
771
63
        Curl_dns_entry_unlink(data, &h); /* this will NULL, potential free h */
772
63
        if(af != h_af) {
773
          /* bad IP version combo, signal the caller to try another address
774
             family if available */
775
0
          return CURLE_UNSUPPORTED_PROTOCOL;
776
0
        }
777
63
        done = 1;
778
63
      }
779
0
      else {
780
        /*
781
         * provided dev was no interface (or interfaces are not supported
782
         * e.g. Solaris) no ip address and no domain we fail here
783
         */
784
0
        done = -1;
785
0
      }
786
63
    }
787
788
63
    if(done > 0) {
789
63
#ifdef USE_IPV6
790
      /* IPv6 address */
791
63
      if(af == AF_INET6) {
792
0
#ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
793
0
        char *scope_ptr = strchr(myhost, '%');
794
0
        if(scope_ptr)
795
0
          *(scope_ptr++) = '\0';
796
0
#endif
797
0
        if(curlx_inet_pton(AF_INET6, myhost, &si6->sin6_addr) > 0) {
798
0
          si6->sin6_family = AF_INET6;
799
0
          si6->sin6_port = htons(port);
800
0
#ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
801
0
          if(scope_ptr) {
802
            /* The "myhost" string either comes from Curl_if2ip or from
803
               Curl_printable_address. The latter returns only numeric scope
804
               IDs and the former returns none at all. Making the scope ID,
805
               if present, known to be numeric */
806
0
            curl_off_t scope_id;
807
0
            if(curlx_str_number((const char **)CURL_UNCONST(&scope_ptr),
808
0
                                &scope_id, UINT_MAX))
809
0
              return CURLE_UNSUPPORTED_PROTOCOL;
810
0
            si6->sin6_scope_id = (unsigned int)scope_id;
811
0
          }
812
0
#endif
813
0
        }
814
0
        sizeof_sa = sizeof(struct sockaddr_in6);
815
0
      }
816
63
      else
817
63
#endif
818
      /* IPv4 address */
819
63
      if((af == AF_INET) &&
820
63
         (curlx_inet_pton(AF_INET, myhost, &si4->sin_addr) > 0)) {
821
63
        si4->sin_family = AF_INET;
822
63
        si4->sin_port = htons(port);
823
63
        sizeof_sa = sizeof(struct sockaddr_in);
824
63
      }
825
63
    }
826
827
63
    if(done < 1) {
828
      /* errorbuf is set false so failf will overwrite any message already in
829
         the error buffer, so the user receives this error message instead of a
830
         generic resolve error. */
831
0
      char buffer[STRERROR_LEN];
832
0
      data->state.errorbuf = FALSE;
833
0
      data->state.os_errno = sockerr = SOCKERRNO;
834
0
      failf(data, "Could not bind to '%s' with errno %d: %s", host,
835
0
            sockerr, curlx_strerror(sockerr, buffer, sizeof(buffer)));
836
0
      return CURLE_INTERFACE_FAILED;
837
0
    }
838
63
  }
839
11
  else {
840
    /* no device was given, prepare sa to match af's needs */
841
11
#ifdef USE_IPV6
842
11
    if(af == AF_INET6) {
843
0
      si6->sin6_family = AF_INET6;
844
0
      si6->sin6_port = htons(port);
845
0
      sizeof_sa = sizeof(struct sockaddr_in6);
846
0
    }
847
11
    else
848
11
#endif
849
11
    if(af == AF_INET) {
850
11
      si4->sin_family = AF_INET;
851
11
      si4->sin_port = htons(port);
852
11
      sizeof_sa = sizeof(struct sockaddr_in);
853
11
    }
854
11
  }
855
74
#ifdef IP_BIND_ADDRESS_NO_PORT
856
74
  (void)setsockopt(sockfd, SOL_IP, IP_BIND_ADDRESS_NO_PORT, &on, sizeof(on));
857
74
#endif
858
53.2k
  for(;;) {
859
53.2k
    if(bind(sockfd, sock, sizeof_sa) >= 0) {
860
      /* we succeeded to bind */
861
0
      infof(data, "Local port: %hu", port);
862
0
      conn->bits.bound = TRUE;
863
0
      return CURLE_OK;
864
0
    }
865
866
53.2k
    if(--portnum > 0) {
867
53.2k
      port++; /* try next port */
868
53.2k
      if(port == 0)
869
7
        break;
870
53.1k
      infof(data, "Bind to local port %d failed, trying next", port - 1);
871
      /* We reuse/clobber the port variable here below */
872
53.1k
      if(sock->sa_family == AF_INET)
873
53.1k
        si4->sin_port = htons(port);
874
0
#ifdef USE_IPV6
875
0
      else
876
0
        si6->sin6_port = htons(port);
877
53.1k
#endif
878
53.1k
    }
879
67
    else
880
67
      break;
881
53.2k
  }
882
74
  {
883
74
    char buffer[STRERROR_LEN];
884
74
    data->state.os_errno = sockerr = SOCKERRNO;
885
74
    failf(data, "bind failed with errno %d: %s",
886
74
          sockerr, curlx_strerror(sockerr, buffer, sizeof(buffer)));
887
74
  }
888
889
74
  return CURLE_INTERFACE_FAILED;
890
74
}
891
#endif
892
893
/*
894
 * verifyconnect() returns TRUE if the connect really has happened.
895
 */
896
static bool verifyconnect(curl_socket_t sockfd, int *psockerr)
897
0
{
898
0
  bool rc = TRUE;
899
0
#ifdef SO_ERROR
900
0
  int sockerr = 0;
901
0
  curl_socklen_t errSize = sizeof(sockerr);
902
903
#ifdef _WIN32
904
  /*
905
   * In October 2003 we effectively nullified this function on Windows due to
906
   * problems with it using all CPU in multi-threaded cases.
907
   *
908
   * In May 2004, we brought it back to offer more info back on connect
909
   * failures. We could reproduce the former problems with this function, but
910
   * could avoid them by adding this SleepEx() call below:
911
   *
912
   *    "I do not have Rational Quantify, but the hint from his post was
913
   *    ntdll::NtRemoveIoCompletion(). I would assume the SleepEx (or maybe
914
   *    Sleep(0) would be enough?) would release whatever
915
   *    mutex/critical-section the ntdll call is waiting on.
916
   *
917
   *    Someone got to verify this on Win-NT 4.0, 2000."
918
   */
919
  SleepEx(0, FALSE);
920
#endif
921
922
0
  if(getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (void *)&sockerr, &errSize))
923
0
    sockerr = SOCKERRNO;
924
#if defined(EBADIOCTL) && defined(__minix)
925
  /* Minix 3.1.x does not support getsockopt on UDP sockets */
926
  if(EBADIOCTL == sockerr) {
927
    SET_SOCKERRNO(0);
928
    sockerr = 0;
929
  }
930
#endif
931
0
  if((sockerr == 0) || (SOCKEISCONN == sockerr))
932
    /* we are connected, awesome! */
933
0
    rc = TRUE;
934
0
  else
935
    /* This was not a successful connect */
936
0
    rc = FALSE;
937
0
  if(psockerr)
938
0
    *psockerr = sockerr;
939
#else
940
  (void)sockfd;
941
  if(psockerr)
942
    *psockerr = SOCKERRNO;
943
#endif
944
0
  return rc;
945
0
}
946
947
/**
948
 * Determine the curl code for a socket connect() == -1 with errno.
949
 */
950
static CURLcode socket_connect_result(struct Curl_easy *data,
951
                                      const char *ipaddress, int sockerr)
952
0
{
953
0
  if(sockerr == SOCKEINPROGRESS || SOCK_EAGAIN(sockerr))
954
0
    return CURLE_OK;
955
956
  /* unknown error, fallthrough and try another address! */
957
0
  {
958
0
    VERBOSE(char buffer[STRERROR_LEN]);
959
0
    infof(data, "Immediate connect fail for %s: %s", ipaddress,
960
0
          curlx_strerror(sockerr, buffer, sizeof(buffer)));
961
0
    NOVERBOSE((void)ipaddress);
962
0
  }
963
0
  data->state.os_errno = sockerr;
964
  /* connect failed */
965
0
  return CURLE_COULDNT_CONNECT;
966
0
}
967
968
struct cf_socket_ctx {
969
  struct Curl_peer *peer;
970
  struct Curl_sockaddr_ex addr;      /* address to connect to */
971
  curl_socket_t sock;                /* current attempt socket */
972
  struct ip_quadruple ip;            /* The IP quadruple 2x(addr+port) */
973
  struct curltime started_at;        /* when socket was created */
974
  struct curltime connected_at;      /* when socket connected/got first byte */
975
  struct curltime first_byte_at;     /* when first byte was recvd */
976
#ifdef USE_WINSOCK
977
  struct curltime last_sndbuf_query_at;  /* when SO_SNDBUF last queried */
978
  ULONG sndbuf_size;                     /* the last set SO_SNDBUF size */
979
#endif
980
  int sockerr;                       /* socket error of last failure or 0 */
981
#ifdef DEBUGBUILD
982
  int wblock_percent;                /* percent of writes doing EAGAIN */
983
  int wpartial_percent;              /* percent of bytes written in send */
984
  int rblock_percent;                /* percent of reads doing EAGAIN */
985
  size_t recv_max;                   /* max enforced read size */
986
#endif
987
  uint8_t transport;
988
  BIT(got_first_byte);               /* if first byte was received */
989
  BIT(listening);                    /* socket is listening */
990
  BIT(accepted);                     /* socket was accepted, not connected */
991
  BIT(sock_connected);               /* socket is "connected", e.g. in UDP */
992
  BIT(active);
993
  BIT(stats_reported);
994
};
995
996
static CURLcode cf_socket_ctx_init(struct cf_socket_ctx *ctx,
997
                                   struct Curl_peer *peer,
998
                                   struct Curl_sockaddr_ex *addr,
999
                                   uint8_t transport)
1000
6.91k
{
1001
6.91k
  memset(ctx, 0, sizeof(*ctx));
1002
6.91k
  Curl_peer_link(&ctx->peer, peer);
1003
6.91k
  ctx->sock = CURL_SOCKET_BAD;
1004
6.91k
  ctx->transport = transport;
1005
6.91k
  ctx->addr = *addr;
1006
1007
6.91k
#ifdef DEBUGBUILD
1008
6.91k
  {
1009
6.91k
    const char *p = getenv("CURL_DBG_SOCK_WBLOCK");
1010
6.91k
    if(p) {
1011
0
      curl_off_t l;
1012
0
      if(!curlx_str_number(&p, &l, 100))
1013
0
        ctx->wblock_percent = (int)l;
1014
0
    }
1015
6.91k
    p = getenv("CURL_DBG_SOCK_WPARTIAL");
1016
6.91k
    if(p) {
1017
0
      curl_off_t l;
1018
0
      if(!curlx_str_number(&p, &l, 100))
1019
0
        ctx->wpartial_percent = (int)l;
1020
0
    }
1021
6.91k
    p = getenv("CURL_DBG_SOCK_RBLOCK");
1022
6.91k
    if(p) {
1023
0
      curl_off_t l;
1024
0
      if(!curlx_str_number(&p, &l, 100))
1025
0
        ctx->rblock_percent = (int)l;
1026
0
    }
1027
6.91k
    p = getenv("CURL_DBG_SOCK_RMAX");
1028
6.91k
    if(p) {
1029
0
      curl_off_t l;
1030
0
      if(!curlx_str_number(&p, &l, CURL_OFF_T_MAX))
1031
0
        ctx->recv_max = (size_t)l;
1032
0
    }
1033
6.91k
  }
1034
6.91k
#endif
1035
1036
6.91k
  return CURLE_OK;
1037
6.91k
}
1038
1039
static void cf_socket_ctx_free(struct cf_socket_ctx *ctx)
1040
6.91k
{
1041
6.91k
  if(ctx) {
1042
6.91k
    Curl_peer_unlink(&ctx->peer);
1043
6.91k
    curlx_free(ctx);
1044
6.91k
  }
1045
6.91k
}
1046
1047
static CURLcode cf_socket_shutdown(struct Curl_cfilter *cf,
1048
                                   struct Curl_easy *data,
1049
                                   bool *done)
1050
420
{
1051
420
  if(cf->connected) {
1052
420
    struct cf_socket_ctx *ctx = cf->ctx;
1053
1054
420
    CURL_TRC_CF(data, cf, "cf_socket_shutdown, fd=%" FMT_SOCKET_T, ctx->sock);
1055
    /* On TCP, and when the socket looks well and non-blocking mode
1056
     * can be enabled, receive dangling bytes before close to avoid
1057
     * entering RST states unnecessarily. */
1058
420
    if(ctx->sock != CURL_SOCKET_BAD &&
1059
420
       ctx->transport == TRNSPRT_TCP &&
1060
419
       (curlx_nonblock(ctx->sock, TRUE) >= 0)) {
1061
0
      unsigned char buf[1024];
1062
0
      (void)sread(ctx->sock, buf, sizeof(buf));
1063
0
    }
1064
420
  }
1065
420
  *done = TRUE;
1066
420
  return CURLE_OK;
1067
420
}
1068
1069
static void cf_socket_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
1070
6.91k
{
1071
6.91k
  struct cf_socket_ctx *ctx = cf->ctx;
1072
1073
6.91k
  CURL_TRC_CF(data, cf, "destroy");
1074
6.91k
  if(ctx) {
1075
6.91k
    if(ctx->sock != CURL_SOCKET_BAD) {
1076
6.84k
      CURL_TRC_CF(data, cf, "cf_socket_close, fd=%" FMT_SOCKET_T, ctx->sock);
1077
6.84k
      if(ctx->sock == cf->conn->sock[cf->sockindex])
1078
3.73k
        cf->conn->sock[cf->sockindex] = CURL_SOCKET_BAD;
1079
6.84k
      socket_close(data, cf->conn, !ctx->accepted, ctx->sock);
1080
6.84k
    }
1081
6.91k
    cf_socket_ctx_free(ctx);
1082
6.91k
  }
1083
6.91k
}
1084
1085
static void set_local_ip(struct Curl_cfilter *cf,
1086
                         struct Curl_easy *data)
1087
10.6k
{
1088
10.6k
  struct cf_socket_ctx *ctx = cf->ctx;
1089
10.6k
  ctx->ip.local_ip[0] = 0;
1090
10.6k
  ctx->ip.local_port = 0;
1091
1092
10.6k
#ifdef HAVE_GETSOCKNAME
1093
10.6k
  if((ctx->sock != CURL_SOCKET_BAD) &&
1094
10.5k
     !(data->conn->scheme->protocol & CURLPROTO_TFTP)) {
1095
    /* TFTP does not connect, so it cannot get the IP like this */
1096
10.5k
    struct Curl_sockaddr_storage ssloc;
1097
10.5k
    curl_socklen_t slen = sizeof(struct Curl_sockaddr_storage);
1098
1099
10.5k
    memset(&ssloc, 0, sizeof(ssloc));
1100
10.5k
    if(getsockname(ctx->sock, (struct sockaddr *)&ssloc, &slen)) {
1101
0
      VERBOSE(char buffer[STRERROR_LEN]);
1102
0
      VERBOSE(int sockerr = SOCKERRNO);
1103
0
      infof(data, "getsockname() failed with errno %d: %s",
1104
0
            sockerr, curlx_strerror(sockerr, buffer, sizeof(buffer)));
1105
0
    }
1106
10.5k
    else {
1107
10.5k
      CURLcode result = sockaddr2string((struct sockaddr *)&ssloc, slen,
1108
10.5k
                                        ctx->ip.local_ip, &ctx->ip.local_port);
1109
10.5k
      if(result)
1110
0
        infof(data, "ssloc inet_ntop() failed with %d", (int)result);
1111
10.5k
    }
1112
10.5k
  }
1113
#else
1114
  (void)data;
1115
#endif
1116
10.6k
}
1117
1118
static CURLcode set_remote_ip(struct Curl_cfilter *cf,
1119
                              struct Curl_easy *data)
1120
6.91k
{
1121
6.91k
  struct cf_socket_ctx *ctx = cf->ctx;
1122
6.91k
  CURLcode result;
1123
1124
  /* store remote address and port used in this connection attempt */
1125
6.91k
  ctx->ip.transport = ctx->transport;
1126
6.91k
  result = sockaddr2string(&ctx->addr.curl_sa_addr,
1127
6.91k
                           (curl_socklen_t)ctx->addr.addrlen,
1128
6.91k
                           ctx->ip.remote_ip, &ctx->ip.remote_port);
1129
6.91k
  if(result) {
1130
0
    ctx->sockerr = SOCKEAFNOSUPPORT;
1131
    /* malformed address or bug in inet_ntop, try next address */
1132
0
    failf(data, "curl_sa_addr inet_ntop() failed with %d", (int)result);
1133
0
    return CURLE_FAILED_INIT;
1134
0
  }
1135
6.91k
  return CURLE_OK;
1136
6.91k
}
1137
1138
/* to figure out the type of the socket safely, remove the possibly ORed
1139
   bits before comparing */
1140
static int cf_socktype(int x)
1141
13.7k
{
1142
13.7k
#ifdef SOCK_CLOEXEC
1143
13.7k
  x &= ~SOCK_CLOEXEC;
1144
13.7k
#endif
1145
13.7k
#ifdef CURL_USE_SOCK_NONBLOCK
1146
13.7k
  x &= ~SOCK_NONBLOCK;
1147
13.7k
#endif
1148
13.7k
  return x;
1149
13.7k
}
1150
1151
#ifdef _WIN32
1152
/* Offered by mingw-w64 v10+, MS SDK 8.0/~VS2012+ */
1153
#ifndef SIO_TCP_INITIAL_RTO
1154
#define SIO_TCP_INITIAL_RTO _WSAIOW(IOC_VENDOR, 17)
1155
#define TCP_INITIAL_RTO_DEFAULT_RTT 0
1156
1157
/* !checksrc! disable TYPEDEFSTRUCT 1 */
1158
typedef struct _TCP_INITIAL_RTO_PARAMETERS {
1159
  USHORT Rtt;
1160
  UCHAR MaxSynRetransmissions;
1161
} TCP_INITIAL_RTO_PARAMETERS;
1162
#endif /* SIO_TCP_INITIAL_RTO */
1163
1164
#ifndef TCP_INITIAL_RTO_NO_SYN_RETRANSMISSIONS
1165
#define TCP_INITIAL_RTO_NO_SYN_RETRANSMISSIONS 0xFE /* -2 */
1166
#endif
1167
1168
static bool targets_localhost(struct cf_socket_ctx *ctx)
1169
{
1170
  return (((ctx->addr.family == AF_INET) &&
1171
           !strcmp(ctx->ip.remote_ip, "127.0.0.1")) ||
1172
          ((ctx->addr.family == AF_INET6) &&
1173
           !strcmp(ctx->ip.remote_ip, "::1")));
1174
}
1175
1176
/* disable TCP SYN retransmissions for localhost connection on Windows to
1177
   detect problems faster */
1178
static void tcplocalhost(struct Curl_cfilter *cf,
1179
                         curl_socket_t sockfd)
1180
{
1181
  if(targets_localhost(cf->ctx)) {
1182
    TCP_INITIAL_RTO_PARAMETERS rto;
1183
    DWORD bytes = 0;
1184
    memset(&rto, 0, sizeof(rto));
1185
    rto.Rtt = TCP_INITIAL_RTO_DEFAULT_RTT;
1186
    rto.MaxSynRetransmissions = TCP_INITIAL_RTO_NO_SYN_RETRANSMISSIONS;
1187
    (void)WSAIoctl(sockfd, SIO_TCP_INITIAL_RTO, &rto, sizeof(rto),
1188
                   NULL, 0, &bytes, NULL, NULL);
1189
  }
1190
}
1191
#else
1192
#define tcplocalhost(x, y)
1193
#endif /* _WIN32 */
1194
1195
static CURLcode cf_socket_open(struct Curl_cfilter *cf,
1196
                               struct Curl_easy *data)
1197
6.91k
{
1198
6.91k
  struct cf_socket_ctx *ctx = cf->ctx;
1199
6.91k
  int error = 0;
1200
6.91k
  bool isconnected = FALSE;
1201
6.91k
  CURLcode result = CURLE_COULDNT_CONNECT;
1202
6.91k
  bool is_tcp;
1203
1204
6.91k
  DEBUGASSERT(ctx->sock == CURL_SOCKET_BAD);
1205
6.91k
  ctx->started_at = *Curl_pgrs_now(data);
1206
6.91k
#ifdef CURL_USE_SOCK_NONBLOCK
1207
  /* Do not tuck SOCK_NONBLOCK into socktype when opensocket callback is set
1208
   * because we would not know how socktype is about to be used in the
1209
   * callback, SOCK_NONBLOCK might get factored out before calling socket().
1210
   */
1211
6.91k
  if(!data->set.fopensocket)
1212
0
    ctx->addr.socktype |= SOCK_NONBLOCK;
1213
6.91k
#endif
1214
6.91k
  result = socket_open(data, &ctx->addr, &ctx->sock);
1215
6.91k
#ifdef CURL_USE_SOCK_NONBLOCK
1216
  /* Restore the socktype after the socket is created. */
1217
6.91k
  if(!data->set.fopensocket)
1218
0
    ctx->addr.socktype &= ~SOCK_NONBLOCK;
1219
6.91k
#endif
1220
6.91k
  if(result)
1221
0
    goto out;
1222
1223
6.91k
  result = set_remote_ip(cf, data);
1224
6.91k
  if(result)
1225
0
    goto out;
1226
1227
6.91k
#ifdef USE_IPV6
1228
6.91k
  if(ctx->addr.family == AF_INET6) {
1229
#ifdef USE_WINSOCK
1230
    /* Turn on support for IPv4-mapped IPv6 addresses.
1231
     * Linux kernel, NetBSD, FreeBSD, Darwin, lwIP: default is off;
1232
     * Windows Vista and later: default is on;
1233
     * DragonFly BSD: acts like off, and dummy setting;
1234
     * OpenBSD and earlier Windows: unsupported.
1235
     * Linux: controlled by /proc/sys/net/ipv6/bindv6only.
1236
     */
1237
    int on = 0;
1238
    (void)setsockopt(ctx->sock, IPPROTO_IPV6, IPV6_V6ONLY,
1239
                     (void *)&on, sizeof(on));
1240
#endif
1241
0
#ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
1242
0
    {
1243
0
      struct sockaddr_in6 *sa6 = (void *)&ctx->addr.curl_sa_addr;
1244
0
      if(sa6->sin6_scope_id)
1245
0
        infof(data, "  Trying [%s]:%d scope_id=%lu...",
1246
0
              ctx->ip.remote_ip, ctx->ip.remote_port,
1247
0
              (unsigned long)sa6->sin6_scope_id);
1248
0
      else
1249
0
#endif
1250
0
        infof(data, "  Trying [%s]:%d...",
1251
0
              ctx->ip.remote_ip, ctx->ip.remote_port);
1252
0
#ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
1253
0
    }
1254
0
#endif
1255
0
  }
1256
6.91k
  else
1257
6.91k
#endif
1258
6.91k
    infof(data, "  Trying %s:%d...", ctx->ip.remote_ip, ctx->ip.remote_port);
1259
1260
6.91k
#ifdef USE_IPV6
1261
6.91k
  is_tcp = (ctx->addr.family == AF_INET ||
1262
40
            ctx->addr.family == AF_INET6) &&
1263
6.87k
    cf_socktype(ctx->addr.socktype) == SOCK_STREAM;
1264
#else
1265
  is_tcp = (ctx->addr.family == AF_INET) &&
1266
    cf_socktype(ctx->addr.socktype) == SOCK_STREAM;
1267
#endif
1268
6.91k
  if(is_tcp) {
1269
6.87k
    if(data->set.tcp_nodelay)
1270
6.87k
      tcpnodelay(cf, data, ctx->sock);
1271
1272
6.87k
    if(data->set.tcp_keepalive)
1273
38
      tcpkeepalive(cf, data, ctx->sock);
1274
1275
6.87k
    tcplocalhost(cf, ctx->sock);
1276
6.87k
  }
1277
1278
6.91k
  if(data->set.fsockopt) {
1279
    /* activate callback for setting socket options */
1280
6.91k
    struct Curl_mapi_guard guard;
1281
6.91k
    CURL_CBAPI_START(&guard, data, easy_fsockopt);
1282
6.91k
    error = data->set.fsockopt(data->set.sockopt_client,
1283
6.91k
                               ctx->sock,
1284
6.91k
                               CURLSOCKTYPE_IPCXN);
1285
6.91k
    CURL_CBAPI_END(&guard);
1286
1287
6.91k
    if(error == CURL_SOCKOPT_ALREADY_CONNECTED)
1288
6.91k
      isconnected = TRUE;
1289
0
    else if(error) {
1290
0
      result = CURLE_ABORTED_BY_CALLBACK;
1291
0
      goto out;
1292
0
    }
1293
6.91k
  }
1294
1295
6.91k
#ifndef CURL_DISABLE_BINDLOCAL
1296
  /* possibly bind the local end to an IP, interface or port */
1297
6.91k
  if(ctx->addr.family == AF_INET
1298
40
#ifdef USE_IPV6
1299
40
     || ctx->addr.family == AF_INET6
1300
6.91k
#endif
1301
6.91k
    ) {
1302
6.87k
    result = bindlocal(data, cf->conn, ctx->sock, ctx->addr.family,
1303
6.87k
                       Curl_ipv6_scope(&ctx->addr.curl_sa_addr),
1304
6.87k
                       ctx->transport);
1305
6.87k
    if(result) {
1306
74
      if(result == CURLE_UNSUPPORTED_PROTOCOL) {
1307
        /* The address family is not supported on this interface.
1308
           We can continue trying addresses */
1309
0
        result = CURLE_COULDNT_CONNECT;
1310
0
      }
1311
74
      goto out;
1312
74
    }
1313
6.87k
  }
1314
6.84k
#endif
1315
1316
#ifndef CURL_USE_SOCK_NONBLOCK
1317
  /* Set socket non-blocking, must be a non-blocking socket for
1318
   * a non-blocking connect. */
1319
  error = curlx_nonblock(ctx->sock, TRUE);
1320
  if(error < 0) {
1321
    result = CURLE_UNSUPPORTED_PROTOCOL;
1322
    ctx->sockerr = SOCKERRNO;
1323
    goto out;
1324
  }
1325
#else
1326
6.84k
  if(data->set.fopensocket) {
1327
    /* Set socket non-blocking, must be a non-blocking socket for
1328
     * a non-blocking connect. */
1329
6.84k
    error = curlx_nonblock(ctx->sock, TRUE);
1330
6.84k
    if(error < 0) {
1331
0
      result = CURLE_UNSUPPORTED_PROTOCOL;
1332
0
      ctx->sockerr = SOCKERRNO;
1333
0
      goto out;
1334
0
    }
1335
6.84k
  }
1336
6.84k
#endif
1337
6.84k
  ctx->sock_connected = (cf_socktype(ctx->addr.socktype) != SOCK_DGRAM);
1338
6.91k
out:
1339
6.91k
  if(result) {
1340
74
    if(ctx->sock != CURL_SOCKET_BAD) {
1341
74
      socket_close(data, cf->conn, TRUE, ctx->sock);
1342
74
      ctx->sock = CURL_SOCKET_BAD;
1343
74
    }
1344
74
  }
1345
6.84k
  else if(isconnected) {
1346
6.84k
    set_local_ip(cf, data);
1347
6.84k
    ctx->connected_at = *Curl_pgrs_now(data);
1348
6.84k
    cf->connected = TRUE;
1349
6.84k
  }
1350
6.91k
  CURL_TRC_CF(data, cf, "cf_socket_open() -> %d, fd=%" FMT_SOCKET_T,
1351
6.91k
              (int)result, ctx->sock);
1352
6.91k
  return result;
1353
6.84k
}
1354
1355
static int do_connect(struct Curl_cfilter *cf, struct Curl_easy *data,
1356
                      bool is_tcp_fastopen)
1357
0
{
1358
0
  struct cf_socket_ctx *ctx = cf->ctx;
1359
0
#ifdef TCP_FASTOPEN_CONNECT
1360
0
  int optval = 1;
1361
0
#endif
1362
0
  int rc = -1;
1363
1364
0
  (void)data;
1365
0
  if(is_tcp_fastopen) {
1366
#ifdef CONNECT_DATA_IDEMPOTENT /* Darwin */
1367
#  ifdef HAVE_BUILTIN_AVAILABLE
1368
    /* while connectx function is available since macOS 10.11 / iOS 9,
1369
       it did not have the interface declared correctly until
1370
       Xcode 9 / macOS SDK 10.13 */
1371
    if(__builtin_available(macOS 10.11, iOS 9.0, tvOS 9.0, watchOS 2.0, *)) {
1372
      sa_endpoints_t endpoints;
1373
      endpoints.sae_srcif = 0;
1374
      endpoints.sae_srcaddr = NULL;
1375
      endpoints.sae_srcaddrlen = 0;
1376
      endpoints.sae_dstaddr = &ctx->addr.curl_sa_addr;
1377
      endpoints.sae_dstaddrlen = ctx->addr.addrlen;
1378
1379
      rc = connectx(ctx->sock, &endpoints, SAE_ASSOCID_ANY,
1380
                    CONNECT_RESUME_ON_READ_WRITE | CONNECT_DATA_IDEMPOTENT,
1381
                    NULL, 0, NULL, NULL);
1382
    }
1383
    else {
1384
      rc = connect(ctx->sock, &ctx->addr.curl_sa_addr, ctx->addr.addrlen);
1385
    }
1386
#  else
1387
    rc = connect(ctx->sock, &ctx->addr.curl_sa_addr, ctx->addr.addrlen);
1388
#  endif /* HAVE_BUILTIN_AVAILABLE */
1389
#elif defined(TCP_FASTOPEN_CONNECT) /* Linux >= 4.11 */
1390
0
    if(setsockopt(ctx->sock, IPPROTO_TCP, TCP_FASTOPEN_CONNECT,
1391
0
                  (void *)&optval, sizeof(optval)) < 0)
1392
0
      CURL_TRC_CF(data, cf, "Failed to enable TCP Fast Open on fd %"
1393
0
                  FMT_SOCKET_T, ctx->sock);
1394
1395
0
    rc = connect(ctx->sock, &ctx->addr.curl_sa_addr, ctx->addr.addrlen);
1396
#elif defined(MSG_FASTOPEN) /* old Linux */
1397
    if(Curl_conn_is_ssl(cf->conn, cf->sockindex))
1398
      rc = connect(ctx->sock, &ctx->addr.curl_sa_addr, ctx->addr.addrlen);
1399
    else
1400
      rc = 0; /* Do nothing */
1401
#endif
1402
0
  }
1403
0
  else {
1404
0
    rc = connect(ctx->sock, &ctx->addr.curl_sa_addr,
1405
0
                 (curl_socklen_t)ctx->addr.addrlen);
1406
0
  }
1407
0
  return rc;
1408
0
}
1409
1410
static CURLcode cf_tcp_connect(struct Curl_cfilter *cf,
1411
                               struct Curl_easy *data,
1412
                               bool *done)
1413
6.91k
{
1414
6.91k
  struct cf_socket_ctx *ctx = cf->ctx;
1415
6.91k
  CURLcode result = CURLE_COULDNT_CONNECT;
1416
6.91k
  int rc = 0;
1417
1418
6.91k
  if(cf->connected) {
1419
0
    *done = TRUE;
1420
0
    return CURLE_OK;
1421
0
  }
1422
1423
6.91k
  *done = FALSE; /* a negative world view is best */
1424
6.91k
  if(ctx->sock == CURL_SOCKET_BAD) {
1425
6.91k
    int sockerr;
1426
1427
6.91k
    result = cf_socket_open(cf, data);
1428
6.91k
    if(result)
1429
74
      goto out;
1430
1431
6.84k
    if(cf->connected) {
1432
6.84k
      *done = TRUE;
1433
6.84k
      return CURLE_OK;
1434
6.84k
    }
1435
1436
    /* Connect TCP socket */
1437
0
    rc = do_connect(cf, data, (bool)cf->conn->bits.tcp_fastopen);
1438
0
    sockerr = SOCKERRNO;
1439
0
    set_local_ip(cf, data);
1440
0
    CURL_TRC_CF(data, cf, "local address %s port %d...",
1441
0
                ctx->ip.local_ip, ctx->ip.local_port);
1442
0
    if(rc == -1) {
1443
0
      ctx->sockerr = sockerr;
1444
0
      result = socket_connect_result(data, ctx->ip.remote_ip, sockerr);
1445
0
      goto out;
1446
0
    }
1447
0
  }
1448
1449
#ifdef mpeix
1450
  /* Call this function once now, and ignore the results. We do this to
1451
     "clear" the error state on the socket so that we can later read it
1452
     reliably. This is reported necessary on the MPE/iX operating
1453
     system. */
1454
  (void)verifyconnect(ctx->sock, NULL);
1455
#endif
1456
  /* check socket for connect */
1457
0
  rc = SOCKET_WRITABLE(ctx->sock, 0);
1458
1459
0
  if(rc == 0) { /* no connection yet */
1460
0
    CURL_TRC_CF(data, cf, "not connected yet on fd=%" FMT_SOCKET_T, ctx->sock);
1461
0
    return CURLE_OK;
1462
0
  }
1463
0
  else if(rc == CURL_CSELECT_OUT || cf->conn->bits.tcp_fastopen) {
1464
0
    if(verifyconnect(ctx->sock, &ctx->sockerr)) {
1465
      /* we are connected with TCP, awesome! */
1466
0
      ctx->connected_at = *Curl_pgrs_now(data);
1467
0
      set_local_ip(cf, data);
1468
0
      *done = TRUE;
1469
0
      cf->connected = TRUE;
1470
0
      CURL_TRC_CF(data, cf, "connected on fd=%" FMT_SOCKET_T, ctx->sock);
1471
0
      return CURLE_OK;
1472
0
    }
1473
0
  }
1474
0
  else if(rc & CURL_CSELECT_ERR) {
1475
0
    CURL_TRC_CF(data, cf, "poll/select error on fd=%" FMT_SOCKET_T, ctx->sock);
1476
0
    (void)verifyconnect(ctx->sock, &ctx->sockerr);
1477
0
    result = CURLE_COULDNT_CONNECT;
1478
0
  }
1479
1480
74
out:
1481
74
  if(result) {
1482
74
    VERBOSE(char buffer[STRERROR_LEN]);
1483
74
    set_local_ip(cf, data);
1484
74
    if(ctx->sockerr) {
1485
0
      data->state.os_errno = ctx->sockerr;
1486
0
      SET_SOCKERRNO(ctx->sockerr);
1487
0
      VERBOSE(curlx_strerror(ctx->sockerr, buffer, sizeof(buffer)));
1488
0
    }
1489
74
    else {
1490
74
      VERBOSE(curlx_strcopy(buffer, sizeof(buffer), STRCONST("peer closed")));
1491
74
    }
1492
74
    if(ctx->sock != CURL_SOCKET_BAD) {
1493
0
      socket_close(data, cf->conn, TRUE, ctx->sock);
1494
0
      ctx->sock = CURL_SOCKET_BAD;
1495
0
    }
1496
74
    infof(data, "connect to %s port %u from %s port %d failed: %s",
1497
74
          ctx->ip.remote_ip, ctx->ip.remote_port,
1498
74
          ctx->ip.local_ip, ctx->ip.local_port,
1499
74
          curlx_strerror(ctx->sockerr, buffer, sizeof(buffer)));
1500
74
    *done = FALSE;
1501
74
  }
1502
74
  return result;
1503
0
}
1504
1505
static CURLcode cf_socket_adjust_pollset(struct Curl_cfilter *cf,
1506
                                         struct Curl_easy *data,
1507
                                         struct easy_pollset *ps)
1508
4.05k
{
1509
4.05k
  struct cf_socket_ctx *ctx = cf->ctx;
1510
4.05k
  CURLcode result = CURLE_OK;
1511
1512
4.05k
  if(ctx->sock != CURL_SOCKET_BAD) {
1513
    /* A listening socket filter needs to be connected before the accept
1514
     * for some weird FTP interaction. This should be rewritten, so that
1515
     * FTP no longer does the socket checks and accept calls and delegates
1516
     * all that to the filter. */
1517
4.05k
    if(ctx->listening) {
1518
0
      result = Curl_pollset_set_in_only(data, ps, ctx->sock);
1519
0
      CURL_TRC_CF(data, cf, "adjust_pollset, listening, POLLIN fd=%"
1520
0
                  FMT_SOCKET_T, ctx->sock);
1521
0
    }
1522
4.05k
    else if(!cf->connected) {
1523
0
      result = Curl_pollset_set_out_only(data, ps, ctx->sock);
1524
0
      CURL_TRC_CF(data, cf, "adjust_pollset, !connected, POLLOUT fd=%"
1525
0
                  FMT_SOCKET_T, ctx->sock);
1526
0
    }
1527
4.05k
    else if(!ctx->active) {
1528
119
      result = Curl_pollset_add_in(data, ps, ctx->sock);
1529
119
      CURL_TRC_CF(data, cf, "adjust_pollset, !active, POLLIN fd=%"
1530
119
                  FMT_SOCKET_T, ctx->sock);
1531
119
    }
1532
4.05k
  }
1533
4.05k
  return result;
1534
4.05k
}
1535
1536
#ifdef USE_WINSOCK
1537
1538
/* Offered by mingw-w64 v13+, MS SDK 7.0A/VS2010+ */
1539
#ifndef SIO_IDEAL_SEND_BACKLOG_QUERY
1540
#define SIO_IDEAL_SEND_BACKLOG_QUERY 0x4004747B
1541
#endif
1542
1543
static void win_update_sndbuf_size(struct Curl_easy *data,
1544
                                   struct cf_socket_ctx *ctx)
1545
{
1546
  ULONG ideal;
1547
  DWORD ideallen;
1548
1549
  if(curlx_ptimediff_ms(Curl_pgrs_now(data),
1550
                        &ctx->last_sndbuf_query_at) > 1000) {
1551
    if(!WSAIoctl(ctx->sock, SIO_IDEAL_SEND_BACKLOG_QUERY, 0, 0,
1552
                 &ideal, sizeof(ideal), &ideallen, 0, 0) &&
1553
       ideal != ctx->sndbuf_size &&
1554
       !setsockopt(ctx->sock, SOL_SOCKET, SO_SNDBUF,
1555
                   (const char *)&ideal, sizeof(ideal))) {
1556
      ctx->sndbuf_size = ideal;
1557
    }
1558
    ctx->last_sndbuf_query_at = *Curl_pgrs_now(data);
1559
  }
1560
}
1561
1562
#endif /* USE_WINSOCK */
1563
1564
static CURLcode cf_socket_send(struct Curl_cfilter *cf, struct Curl_easy *data,
1565
                               const uint8_t *buf, size_t len, bool eos,
1566
                               size_t *pnwritten)
1567
3.40k
{
1568
3.40k
  struct cf_socket_ctx *ctx = cf->ctx;
1569
3.40k
  curl_socket_t fdsave;
1570
3.40k
  ssize_t rv;
1571
3.40k
  CURLcode result = CURLE_OK;
1572
3.40k
  VERBOSE(size_t orig_len = len);
1573
1574
3.40k
  (void)eos;
1575
3.40k
  *pnwritten = 0;
1576
3.40k
  fdsave = cf->conn->sock[cf->sockindex];
1577
3.40k
  cf->conn->sock[cf->sockindex] = ctx->sock;
1578
1579
3.40k
#ifdef DEBUGBUILD
1580
  /* simulate network blocking/partial writes */
1581
3.40k
  if(ctx->wblock_percent > 0) {
1582
0
    unsigned char c = 0;
1583
0
    Curl_rand_bytes(data, FALSE, &c, 1);
1584
0
    if(c >= ((100 - ctx->wblock_percent) * 256 / 100)) {
1585
0
      CURL_TRC_CF(data, cf, "send(len=%zu) SIMULATE EWOULDBLOCK", orig_len);
1586
0
      cf->conn->sock[cf->sockindex] = fdsave;
1587
0
      return CURLE_AGAIN;
1588
0
    }
1589
0
  }
1590
3.40k
  if(cf->cft != &Curl_cft_udp && ctx->wpartial_percent > 0 && len > 8) {
1591
0
    len = len * ctx->wpartial_percent / 100;
1592
0
    if(!len)
1593
0
      len = 1;
1594
0
    CURL_TRC_CF(data, cf, "send(len=%zu) SIMULATE partial write of %zu bytes",
1595
0
                orig_len, len);
1596
0
  }
1597
3.40k
#endif
1598
1599
#if defined(MSG_FASTOPEN) && !defined(TCP_FASTOPEN_CONNECT) /* Linux */
1600
  if(cf->conn->bits.tcp_fastopen) {
1601
    rv = sendto(ctx->sock, buf, len, MSG_FASTOPEN,
1602
                &ctx->addr.curl_sa_addr, ctx->addr.addrlen);
1603
    cf->conn->bits.tcp_fastopen = FALSE;
1604
  }
1605
  else
1606
#endif
1607
3.40k
    rv = swrite(ctx->sock, buf, len);
1608
1609
3.40k
  if(!curlx_sztouz(rv, pnwritten)) {
1610
1
    int sockerr = SOCKERRNO;
1611
1
    if(SOCK_EAGAIN(sockerr)
1612
0
#ifndef USE_WINSOCK
1613
0
       || (sockerr == SOCKEINTR) || (sockerr == SOCKEINPROGRESS)
1614
1
#endif
1615
1
      ) {
1616
1
      result = CURLE_AGAIN;  /* EWOULDBLOCK */
1617
1
    }
1618
0
    else {
1619
0
      char buffer[STRERROR_LEN];
1620
0
      failf(data, "Send failure: %s",
1621
0
            curlx_strerror(sockerr, buffer, sizeof(buffer)));
1622
0
      data->state.os_errno = sockerr;
1623
0
      result = CURLE_SEND_ERROR;
1624
0
    }
1625
1
  }
1626
1627
#ifdef USE_WINSOCK
1628
  if(!result)
1629
    win_update_sndbuf_size(data, ctx);
1630
#endif
1631
1632
3.40k
  CURL_TRC_CF(data, cf, "send(len=%zu) -> %d, %zu",
1633
3.40k
              orig_len, (int)result, *pnwritten);
1634
3.40k
  cf->conn->sock[cf->sockindex] = fdsave;
1635
3.40k
  return result;
1636
3.40k
}
1637
1638
static CURLcode cf_socket_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
1639
                               char *buf, size_t len, size_t *pnread)
1640
34.4k
{
1641
34.4k
  struct cf_socket_ctx *ctx = cf->ctx;
1642
34.4k
  CURLcode result = CURLE_OK;
1643
34.4k
  ssize_t rv;
1644
1645
34.4k
  *pnread = 0;
1646
34.4k
#ifdef DEBUGBUILD
1647
  /* simulate network blocking/partial reads */
1648
34.4k
  if(cf->cft != &Curl_cft_udp && ctx->rblock_percent > 0) {
1649
0
    unsigned char c = 0;
1650
0
    Curl_rand(data, &c, 1);
1651
0
    if(c >= ((100 - ctx->rblock_percent) * 256 / 100)) {
1652
0
      CURL_TRC_CF(data, cf, "recv(len=%zu) SIMULATE EWOULDBLOCK", len);
1653
0
      return CURLE_AGAIN;
1654
0
    }
1655
0
  }
1656
34.4k
  if(cf->cft != &Curl_cft_udp && ctx->recv_max && ctx->recv_max < len) {
1657
0
    CURL_TRC_CF(data, cf, "recv(len=%zu) SIMULATE max read of %zu bytes",
1658
0
                len, ctx->recv_max);
1659
0
    len = ctx->recv_max;
1660
0
  }
1661
34.4k
#endif
1662
1663
34.4k
  rv = sread(ctx->sock, buf, len);
1664
1665
34.4k
  if(!curlx_sztouz(rv, pnread)) {
1666
123
    int sockerr = SOCKERRNO;
1667
123
    if(SOCK_EAGAIN(sockerr)
1668
0
#ifndef USE_WINSOCK
1669
0
       || (sockerr == SOCKEINTR)
1670
123
#endif
1671
123
      ) {
1672
123
      result = CURLE_AGAIN;  /* EWOULDBLOCK */
1673
123
    }
1674
0
    else {
1675
0
      char buffer[STRERROR_LEN];
1676
0
      failf(data, "Recv failure: %s",
1677
0
            curlx_strerror(sockerr, buffer, sizeof(buffer)));
1678
0
      data->state.os_errno = sockerr;
1679
0
      result = CURLE_RECV_ERROR;
1680
0
    }
1681
123
  }
1682
1683
34.4k
  CURL_TRC_CF(data, cf, "recv(len=%zu) -> %d, %zu", len, (int)result, *pnread);
1684
34.4k
  if(!result && !ctx->got_first_byte) {
1685
2.24k
    ctx->first_byte_at = *Curl_pgrs_now(data);
1686
2.24k
    ctx->got_first_byte = TRUE;
1687
2.24k
  }
1688
34.4k
  return result;
1689
34.4k
}
1690
1691
static void cf_socket_update_data(struct Curl_cfilter *cf,
1692
                                  struct Curl_easy *data)
1693
3.73k
{
1694
  /* Update the IP info held in the transfer, if we have that. */
1695
3.73k
  if(cf->connected && (cf->sockindex == FIRSTSOCKET)) {
1696
3.73k
    struct cf_socket_ctx *ctx = cf->ctx;
1697
3.73k
    data->info.primary = ctx->ip;
1698
3.73k
  }
1699
3.73k
}
1700
1701
static void cf_socket_active(struct Curl_cfilter *cf, struct Curl_easy *data)
1702
3.73k
{
1703
3.73k
  struct cf_socket_ctx *ctx = cf->ctx;
1704
1705
  /* use this socket from now on */
1706
3.73k
  cf->conn->sock[cf->sockindex] = ctx->sock;
1707
3.73k
  set_local_ip(cf, data);
1708
3.73k
#ifdef USE_IPV6
1709
3.73k
  if(cf->sockindex == FIRSTSOCKET)
1710
3.73k
    cf->conn->bits.ipv6 = (ctx->addr.family == AF_INET6);
1711
3.73k
#endif
1712
3.73k
  ctx->active = TRUE;
1713
3.73k
}
1714
1715
static CURLcode cf_socket_cntrl(struct Curl_cfilter *cf,
1716
                                struct Curl_easy *data,
1717
                                int event, int arg1, void *arg2)
1718
17.4k
{
1719
17.4k
  struct cf_socket_ctx *ctx = cf->ctx;
1720
1721
17.4k
  (void)arg1;
1722
17.4k
  (void)arg2;
1723
17.4k
  switch(event) {
1724
3.73k
  case CF_CTRL_CONN_INFO_UPDATE:
1725
3.73k
    cf_socket_active(cf, data);
1726
3.73k
    cf_socket_update_data(cf, data);
1727
3.73k
    break;
1728
0
  case CF_CTRL_DATA_SETUP:
1729
0
    cf_socket_update_data(cf, data);
1730
0
    break;
1731
0
  case CF_CTRL_FORGET_SOCKET:
1732
0
    ctx->sock = CURL_SOCKET_BAD;
1733
0
    break;
1734
6.84k
  case CF_CTRL_REPORT_STATS:
1735
6.84k
    if(cf->connected && !ctx->stats_reported) {
1736
6.83k
      struct curltime *ts = NULL;
1737
6.83k
      switch(ctx->transport) {
1738
0
      case TRNSPRT_UDP:
1739
0
      case TRNSPRT_QUIC:
1740
        /* Since UDP connected sockets work different from TCP, we use the
1741
         * time of the first byte from the peer as the "connect" time. */
1742
0
        if(ctx->got_first_byte)
1743
0
          ts = &ctx->first_byte_at;
1744
0
        break;
1745
6.83k
      default:
1746
6.83k
        ts = &ctx->connected_at;
1747
6.83k
        break;
1748
6.83k
      }
1749
6.83k
      if(ts) {
1750
6.83k
        Curl_pgrsTimeWas(data, TIMER_CONNECT, *ts);
1751
6.83k
        ctx->stats_reported = TRUE;
1752
6.83k
      }
1753
6.83k
    }
1754
17.4k
  }
1755
17.4k
  return CURLE_OK;
1756
17.4k
}
1757
1758
static bool cf_socket_conn_is_alive(struct Curl_cfilter *cf,
1759
                                    struct Curl_easy *data,
1760
                                    bool *input_pending)
1761
0
{
1762
0
  struct cf_socket_ctx *ctx = cf->ctx;
1763
0
  struct pollfd pfd[1];
1764
0
  int r;
1765
1766
0
  *input_pending = FALSE;
1767
1768
0
  if(!ctx || ctx->sock == CURL_SOCKET_BAD)
1769
0
    return FALSE;
1770
1771
  /* Check with 0 timeout if there are any events pending on the socket */
1772
0
  pfd[0].fd = ctx->sock;
1773
0
  pfd[0].events = POLLRDNORM | POLLIN | POLLRDBAND | POLLPRI;
1774
0
  pfd[0].revents = 0;
1775
1776
0
  r = Curl_poll(pfd, 1, 0);
1777
0
  if(r < 0) {
1778
0
    CURL_TRC_CF(data, cf, "is_alive: poll error, assume dead");
1779
0
    return FALSE;
1780
0
  }
1781
0
  else if(r == 0) {
1782
0
    CURL_TRC_CF(data, cf, "is_alive: poll timeout, assume alive");
1783
0
    return TRUE;
1784
0
  }
1785
0
  else if(pfd[0].revents & (POLLERR | POLLHUP | POLLPRI | POLLNVAL)) {
1786
0
    CURL_TRC_CF(data, cf, "is_alive: err/hup/etc events, assume dead");
1787
0
    return FALSE;
1788
0
  }
1789
1790
0
  CURL_TRC_CF(data, cf, "is_alive: valid events, looks alive");
1791
0
  *input_pending = TRUE;
1792
0
  return TRUE;
1793
0
}
1794
1795
static CURLcode cf_socket_query(struct Curl_cfilter *cf,
1796
                                struct Curl_easy *data,
1797
                                int query, int *pres1, void *pres2)
1798
2.64k
{
1799
2.64k
  struct cf_socket_ctx *ctx = cf->ctx;
1800
1801
2.64k
  switch(query) {
1802
238
  case CF_QUERY_SOCKET:
1803
238
    DEBUGASSERT(pres2);
1804
238
    *((curl_socket_t *)pres2) = ctx->sock;
1805
238
    return CURLE_OK;
1806
0
  case CF_QUERY_TRANSPORT:
1807
0
    DEBUGASSERT(pres1);
1808
0
    *pres1 = ctx->transport;
1809
0
    return CURLE_OK;
1810
0
  case CF_QUERY_REMOTE_ADDR:
1811
0
    DEBUGASSERT(pres2);
1812
0
    *((const struct Curl_sockaddr_ex **)pres2) = cf->connected ?
1813
0
                                                 &ctx->addr : NULL;
1814
0
    return CURLE_OK;
1815
0
  case CF_QUERY_CONNECT_REPLY_MS:
1816
0
    if(ctx->got_first_byte) {
1817
0
      timediff_t ms = curlx_ptimediff_ms(&ctx->first_byte_at,
1818
0
                                         &ctx->started_at);
1819
0
      *pres1 = (ms < INT_MAX) ? (int)ms : INT_MAX;
1820
0
    }
1821
0
    else
1822
0
      *pres1 = -1;
1823
0
    return CURLE_OK;
1824
16
  case CF_QUERY_IP_INFO:
1825
16
#ifdef USE_IPV6
1826
16
    *pres1 = (ctx->addr.family == AF_INET6);
1827
#else
1828
    *pres1 = FALSE;
1829
#endif
1830
16
    *(struct ip_quadruple *)pres2 = ctx->ip;
1831
16
    return CURLE_OK;
1832
0
  case CF_QUERY_REALLY_CONNECTED:
1833
0
    if(cf->cft != &Curl_cft_udp)
1834
0
      *pres1 = cf->connected;
1835
0
    else
1836
0
      *pres1 = ctx->got_first_byte;
1837
0
    return CURLE_OK;
1838
2.39k
  default:
1839
2.39k
    break;
1840
2.64k
  }
1841
2.39k
  return cf->next ?
1842
0
    cf->next->cft->query(cf->next, data, query, pres1, pres2) :
1843
2.39k
    CURLE_UNKNOWN_OPTION;
1844
2.64k
}
1845
1846
struct Curl_cftype Curl_cft_tcp = {
1847
  "TCP",
1848
  CF_TYPE_IP_CONNECT,
1849
  CURL_LOG_LVL_NONE,
1850
  cf_socket_destroy,
1851
  cf_tcp_connect,
1852
  cf_socket_shutdown,
1853
  cf_socket_adjust_pollset,
1854
  Curl_cf_def_data_pending,
1855
  cf_socket_send,
1856
  cf_socket_recv,
1857
  cf_socket_cntrl,
1858
  cf_socket_conn_is_alive,
1859
  Curl_cf_def_conn_keep_alive,
1860
  cf_socket_query,
1861
};
1862
1863
CURLcode Curl_cf_tcp_create(struct Curl_cfilter **pcf,
1864
                            struct Curl_easy *data,
1865
                            struct Curl_peer *origin,
1866
                            struct Curl_peer *peer,
1867
                            uint8_t transport_peer,
1868
                            struct connectdata *conn,
1869
                            struct Curl_sockaddr_ex *addr,
1870
                            struct Curl_peer *tunnel_peer,
1871
                            uint8_t tunnel_transport)
1872
6.87k
{
1873
6.87k
  struct cf_socket_ctx *ctx = NULL;
1874
6.87k
  struct Curl_cfilter *cf = NULL;
1875
6.87k
  CURLcode result;
1876
1877
6.87k
  (void)data;
1878
6.87k
  (void)origin;
1879
6.87k
  (void)conn;
1880
6.87k
  (void)tunnel_peer;
1881
6.87k
  (void)tunnel_transport;
1882
6.87k
  DEBUGASSERT(transport_peer == TRNSPRT_TCP);
1883
6.87k
  if(!addr) {
1884
0
    result = CURLE_BAD_FUNCTION_ARGUMENT;
1885
0
    goto out;
1886
0
  }
1887
1888
6.87k
  ctx = curlx_calloc(1, sizeof(*ctx));
1889
6.87k
  if(!ctx) {
1890
0
    result = CURLE_OUT_OF_MEMORY;
1891
0
    goto out;
1892
0
  }
1893
1894
6.87k
  result = cf_socket_ctx_init(ctx, peer, addr, transport_peer);
1895
6.87k
  if(result)
1896
0
    goto out;
1897
1898
6.87k
  result = Curl_cf_create(&cf, &Curl_cft_tcp, ctx);
1899
1900
6.87k
out:
1901
6.87k
  *pcf = (!result) ? cf : NULL;
1902
6.87k
  if(result) {
1903
0
    curlx_safefree(cf);
1904
0
    cf_socket_ctx_free(ctx);
1905
0
  }
1906
1907
6.87k
  return result;
1908
6.87k
}
1909
1910
#ifdef __linux__
1911
static void linux_quic_mtu(struct cf_socket_ctx *ctx)
1912
0
{
1913
0
  int val;
1914
0
  switch(ctx->addr.family) {
1915
0
#ifdef IP_MTU_DISCOVER
1916
0
  case AF_INET:
1917
0
    val = IP_PMTUDISC_DO;
1918
0
    (void)setsockopt(ctx->sock, IPPROTO_IP, IP_MTU_DISCOVER, &val,
1919
0
                     sizeof(val));
1920
0
    break;
1921
0
#endif
1922
0
#ifdef IPV6_MTU_DISCOVER
1923
0
  case AF_INET6:
1924
0
    val = IPV6_PMTUDISC_DO;
1925
0
    (void)setsockopt(ctx->sock, IPPROTO_IPV6, IPV6_MTU_DISCOVER, &val,
1926
0
                     sizeof(val));
1927
0
    break;
1928
0
#endif
1929
0
  }
1930
0
}
1931
#else
1932
#define linux_quic_mtu(x)
1933
#endif
1934
1935
#if defined(UDP_GRO) &&                                                 \
1936
  (defined(HAVE_SENDMMSG) || defined(HAVE_SENDMSG)) &&                  \
1937
  ((defined(USE_NGTCP2) && defined(USE_NGHTTP3)) || defined(USE_QUICHE))
1938
static void linux_quic_gro(struct cf_socket_ctx *ctx)
1939
{
1940
  int one = 1;
1941
  (void)setsockopt(ctx->sock, IPPROTO_UDP, UDP_GRO, &one,
1942
                   (socklen_t)sizeof(one));
1943
}
1944
#else
1945
#define linux_quic_gro(x)
1946
#endif
1947
1948
#if (defined(__linux__) || defined(__APPLE__)) && defined(IP_RECVTOS)
1949
static void linux_quic_ecn(struct cf_socket_ctx *ctx)
1950
0
{
1951
0
  unsigned int tos = 1;
1952
0
  switch(ctx->addr.family) {
1953
0
  case AF_INET:
1954
0
    (void)setsockopt(ctx->sock, IPPROTO_IP, IP_RECVTOS, &tos, sizeof(tos));
1955
0
    break;
1956
0
#ifdef IPV6_RECVTCLASS
1957
0
  case AF_INET6:
1958
0
    (void)setsockopt(ctx->sock, IPPROTO_IPV6, IPV6_RECVTCLASS,
1959
0
                     &tos, sizeof(tos));
1960
0
    break;
1961
0
#endif
1962
0
  }
1963
0
}
1964
#else
1965
#define linux_quic_ecn(x)
1966
#endif
1967
1968
#if (defined(__linux__) || defined(__APPLE__)) && defined(IP_DONTFRAG)
1969
static void linux_ip_dontfrag(struct cf_socket_ctx *ctx)
1970
{
1971
  int val = 1;
1972
1973
  switch(ctx->addr.family) {
1974
  case AF_INET:
1975
    (void)setsockopt(ctx->sock, IPPROTO_IP, IP_DONTFRAG, &val, sizeof(val));
1976
    break;
1977
#ifdef IPV6_DONTFRAG
1978
  case AF_INET6:
1979
    (void)setsockopt(ctx->sock, IPPROTO_IPV6, IPV6_DONTFRAG,
1980
                     &val, sizeof(val));
1981
    break;
1982
#endif
1983
  }
1984
}
1985
#else
1986
#define linux_ip_dontfrag(x)
1987
#endif
1988
1989
static CURLcode cf_udp_setup_quic(struct Curl_cfilter *cf,
1990
                                  struct Curl_easy *data)
1991
0
{
1992
0
  struct cf_socket_ctx *ctx = cf->ctx;
1993
0
  int rc;
1994
1995
  /* QUIC needs a connected socket, nonblocking */
1996
0
  DEBUGASSERT(ctx->sock != CURL_SOCKET_BAD);
1997
1998
  /* error: The 1st argument to 'connect' is -1 but should be >= 0
1999
     NOLINTNEXTLINE(clang-analyzer-unix.StdCLibraryFunctions) */
2000
0
  rc = connect(ctx->sock, &ctx->addr.curl_sa_addr,
2001
0
               (curl_socklen_t)ctx->addr.addrlen);
2002
0
  if(rc == -1) {
2003
0
    return socket_connect_result(data, ctx->ip.remote_ip, SOCKERRNO);
2004
0
  }
2005
0
  ctx->sock_connected = TRUE;
2006
0
  set_local_ip(cf, data);
2007
0
  CURL_TRC_CF(data, cf, "%s socket %" FMT_SOCKET_T
2008
0
              " connected: [%s:%d] -> [%s:%d]",
2009
0
              (ctx->transport == TRNSPRT_QUIC) ? "QUIC" : "UDP",
2010
0
              ctx->sock, ctx->ip.local_ip, ctx->ip.local_port,
2011
0
              ctx->ip.remote_ip, ctx->ip.remote_port);
2012
2013
  /* Currently, cf->ctx->sock is always non-blocking because the only
2014
   * caller to cf_udp_setup_quic() is cf_udp_connect() that passes the
2015
   * non-blocking socket created by cf_socket_open() to it. Thus, we
2016
   * do not need to call curlx_nonblock() in cf_udp_setup_quic() anymore.
2017
   */
2018
0
  linux_quic_ecn(ctx);
2019
0
  linux_quic_mtu(ctx);
2020
0
  linux_ip_dontfrag(ctx);
2021
0
  linux_quic_gro(ctx);
2022
2023
0
  return CURLE_OK;
2024
0
}
2025
2026
static CURLcode cf_udp_connect(struct Curl_cfilter *cf,
2027
                               struct Curl_easy *data,
2028
                               bool *done)
2029
0
{
2030
0
  struct cf_socket_ctx *ctx = cf->ctx;
2031
0
  CURLcode result = CURLE_COULDNT_CONNECT;
2032
2033
0
  if(cf->connected) {
2034
0
    *done = TRUE;
2035
0
    return CURLE_OK;
2036
0
  }
2037
2038
0
  *done = FALSE;
2039
0
  if(ctx->sock == CURL_SOCKET_BAD) {
2040
0
    result = cf_socket_open(cf, data);
2041
0
    if(result) {
2042
0
      CURL_TRC_CF(data, cf, "cf_udp_connect(), open failed -> %d",
2043
0
                  (int)result);
2044
0
      goto out;
2045
0
    }
2046
2047
0
    if(ctx->transport == TRNSPRT_QUIC) {
2048
0
      result = cf_udp_setup_quic(cf, data);
2049
0
      if(result)
2050
0
        goto out;
2051
0
      CURL_TRC_CF(data, cf, "cf_udp_connect(), opened socket=%"
2052
0
                  FMT_SOCKET_T " (%s:%d)",
2053
0
                  ctx->sock, ctx->ip.local_ip, ctx->ip.local_port);
2054
0
    }
2055
0
    *done = TRUE;
2056
0
    cf->connected = TRUE;
2057
0
  }
2058
0
out:
2059
0
  return result;
2060
0
}
2061
2062
struct Curl_cftype Curl_cft_udp = {
2063
  "UDP",
2064
  CF_TYPE_IP_CONNECT,
2065
  CURL_LOG_LVL_NONE,
2066
  cf_socket_destroy,
2067
  cf_udp_connect,
2068
  cf_socket_shutdown,
2069
  cf_socket_adjust_pollset,
2070
  Curl_cf_def_data_pending,
2071
  cf_socket_send,
2072
  cf_socket_recv,
2073
  cf_socket_cntrl,
2074
  cf_socket_conn_is_alive,
2075
  Curl_cf_def_conn_keep_alive,
2076
  cf_socket_query,
2077
};
2078
2079
CURLcode Curl_cf_udp_create(struct Curl_cfilter **pcf,
2080
                            struct Curl_easy *data,
2081
                            struct Curl_peer *origin,
2082
                            struct Curl_peer *peer,
2083
                            uint8_t transport_peer,
2084
                            struct connectdata *conn,
2085
                            struct Curl_sockaddr_ex *addr,
2086
                            struct Curl_peer *tunnel_peer,
2087
                            uint8_t tunnel_transport)
2088
0
{
2089
0
  struct cf_socket_ctx *ctx = NULL;
2090
0
  struct Curl_cfilter *cf = NULL;
2091
0
  CURLcode result;
2092
2093
0
  (void)data;
2094
0
  (void)origin;
2095
0
  (void)conn;
2096
0
  (void)tunnel_peer;
2097
0
  (void)tunnel_transport;
2098
0
  DEBUGASSERT(transport_peer == TRNSPRT_UDP || transport_peer == TRNSPRT_QUIC);
2099
0
  ctx = curlx_calloc(1, sizeof(*ctx));
2100
0
  if(!ctx) {
2101
0
    result = CURLE_OUT_OF_MEMORY;
2102
0
    goto out;
2103
0
  }
2104
2105
0
  result = cf_socket_ctx_init(ctx, peer, addr, transport_peer);
2106
0
  if(result)
2107
0
    goto out;
2108
2109
0
  result = Curl_cf_create(&cf, &Curl_cft_udp, ctx);
2110
2111
0
out:
2112
0
  *pcf = (!result) ? cf : NULL;
2113
0
  if(result) {
2114
0
    curlx_safefree(cf);
2115
0
    cf_socket_ctx_free(ctx);
2116
0
  }
2117
2118
0
  return result;
2119
0
}
2120
2121
/* this is the TCP filter which can also handle this case */
2122
struct Curl_cftype Curl_cft_unix = {
2123
  "UNIX",
2124
  CF_TYPE_IP_CONNECT,
2125
  CURL_LOG_LVL_NONE,
2126
  cf_socket_destroy,
2127
  cf_tcp_connect,
2128
  cf_socket_shutdown,
2129
  cf_socket_adjust_pollset,
2130
  Curl_cf_def_data_pending,
2131
  cf_socket_send,
2132
  cf_socket_recv,
2133
  cf_socket_cntrl,
2134
  cf_socket_conn_is_alive,
2135
  Curl_cf_def_conn_keep_alive,
2136
  cf_socket_query,
2137
};
2138
2139
CURLcode Curl_cf_unix_create(struct Curl_cfilter **pcf,
2140
                            struct Curl_easy *data,
2141
                            struct Curl_peer *origin,
2142
                            struct Curl_peer *peer,
2143
                            uint8_t transport_peer,
2144
                            struct connectdata *conn,
2145
                            struct Curl_sockaddr_ex *addr,
2146
                            struct Curl_peer *tunnel_peer,
2147
                            uint8_t tunnel_transport)
2148
40
{
2149
40
  struct cf_socket_ctx *ctx = NULL;
2150
40
  struct Curl_cfilter *cf = NULL;
2151
40
  CURLcode result;
2152
2153
40
  (void)data;
2154
40
  (void)origin;
2155
40
  (void)conn;
2156
40
  (void)tunnel_peer;
2157
40
  (void)tunnel_transport;
2158
40
  DEBUGASSERT(transport_peer == TRNSPRT_UNIX);
2159
40
  ctx = curlx_calloc(1, sizeof(*ctx));
2160
40
  if(!ctx) {
2161
0
    result = CURLE_OUT_OF_MEMORY;
2162
0
    goto out;
2163
0
  }
2164
2165
40
  result = cf_socket_ctx_init(ctx, peer, addr, transport_peer);
2166
40
  if(result)
2167
0
    goto out;
2168
2169
40
  result = Curl_cf_create(&cf, &Curl_cft_unix, ctx);
2170
2171
40
out:
2172
40
  *pcf = (!result) ? cf : NULL;
2173
40
  if(result) {
2174
0
    curlx_safefree(cf);
2175
0
    cf_socket_ctx_free(ctx);
2176
0
  }
2177
2178
40
  return result;
2179
40
}
2180
2181
static timediff_t cf_tcp_accept_timeleft(struct Curl_cfilter *cf,
2182
                                         struct Curl_easy *data)
2183
0
{
2184
0
  struct cf_socket_ctx *ctx = cf->ctx;
2185
0
  timediff_t timeout_ms = DEFAULT_ACCEPT_TIMEOUT;
2186
0
  timediff_t other_ms;
2187
2188
0
#ifndef CURL_DISABLE_FTP
2189
0
  if(data->set.accepttimeout > 0)
2190
0
    timeout_ms = data->set.accepttimeout;
2191
0
#endif
2192
2193
  /* check if the generic timeout possibly is set shorter */
2194
0
  other_ms = Curl_timeleft_ms(data);
2195
0
  if(other_ms && (other_ms < timeout_ms))
2196
    /* note that this also works fine for when other_ms happens to be negative
2197
       due to it already having elapsed */
2198
0
    timeout_ms = other_ms;
2199
0
  else {
2200
    /* subtract elapsed time */
2201
0
    timeout_ms -= curlx_ptimediff_ms(Curl_pgrs_now(data), &ctx->started_at);
2202
0
    if(!timeout_ms)
2203
      /* avoid returning 0 as that means no timeout! */
2204
0
      timeout_ms = -1;
2205
0
  }
2206
0
  return timeout_ms;
2207
0
}
2208
2209
static void cf_tcp_set_accepted_remote_ip(struct Curl_cfilter *cf,
2210
                                          struct Curl_easy *data)
2211
0
{
2212
0
  struct cf_socket_ctx *ctx = cf->ctx;
2213
0
#ifdef HAVE_GETPEERNAME
2214
0
  CURLcode result;
2215
0
  struct Curl_sockaddr_storage ssrem;
2216
0
  curl_socklen_t plen;
2217
2218
0
  ctx->ip.remote_ip[0] = 0;
2219
0
  ctx->ip.remote_port = 0;
2220
0
  plen = sizeof(ssrem);
2221
0
  memset(&ssrem, 0, plen);
2222
0
  if(getpeername(ctx->sock, (struct sockaddr *)&ssrem, &plen)) {
2223
0
    char buffer[STRERROR_LEN];
2224
0
    int sockerr = SOCKERRNO;
2225
0
    failf(data, "getpeername() failed with errno %d: %s",
2226
0
          sockerr, curlx_strerror(sockerr, buffer, sizeof(buffer)));
2227
0
    return;
2228
0
  }
2229
0
  result = sockaddr2string((struct sockaddr *)&ssrem, plen,
2230
0
                            ctx->ip.remote_ip, &ctx->ip.remote_port);
2231
0
  if(result) {
2232
0
    failf(data, "ssrem inet_ntop() failed with %d", (int)result);
2233
0
    return;
2234
0
  }
2235
#else
2236
  ctx->ip.remote_ip[0] = 0;
2237
  ctx->ip.remote_port = 0;
2238
  (void)data;
2239
#endif
2240
0
}
2241
2242
static CURLcode cf_tcp_accept_connect(struct Curl_cfilter *cf,
2243
                                      struct Curl_easy *data,
2244
                                      bool *done)
2245
0
{
2246
0
  struct cf_socket_ctx *ctx = cf->ctx;
2247
0
  char errbuf[STRERROR_LEN];
2248
0
#ifdef USE_IPV6
2249
0
  struct Curl_sockaddr_storage add;
2250
#else
2251
  struct sockaddr_in add;
2252
#endif
2253
0
  curl_socklen_t size = (curl_socklen_t)sizeof(add);
2254
0
  curl_socket_t s_accepted = CURL_SOCKET_BAD;
2255
0
  timediff_t timeout_ms;
2256
0
  int socketstate = 0;
2257
0
  bool incoming = FALSE;
2258
2259
  /* we start accepted, if we ever close, we cannot go on */
2260
0
  (void)data;
2261
0
  if(cf->connected) {
2262
0
    *done = TRUE;
2263
0
    return CURLE_OK;
2264
0
  }
2265
2266
0
  *done = FALSE;
2267
0
  timeout_ms = cf_tcp_accept_timeleft(cf, data);
2268
0
  if(timeout_ms < 0) {
2269
    /* if a timeout was already reached, bail out */
2270
0
    failf(data, "Accept timeout occurred while waiting server connect");
2271
0
    return CURLE_FTP_ACCEPT_TIMEOUT;
2272
0
  }
2273
2274
0
  CURL_TRC_CF(data, cf, "Checking for incoming on fd=%" FMT_SOCKET_T
2275
0
              " ip=%s:%d", ctx->sock, ctx->ip.local_ip, ctx->ip.local_port);
2276
0
  socketstate = SOCKET_READABLE(ctx->sock, 0);
2277
0
  CURL_TRC_CF(data, cf, "socket_check -> %x", (unsigned int)socketstate);
2278
0
  switch(socketstate) {
2279
0
  case -1: /* error */
2280
    /* let's die here */
2281
0
    failf(data, "Error while waiting for server connect");
2282
0
    return CURLE_FTP_ACCEPT_FAILED;
2283
0
  default:
2284
0
    if(socketstate & CURL_CSELECT_IN) {
2285
0
      infof(data, "Ready to accept data connection from server");
2286
0
      incoming = TRUE;
2287
0
    }
2288
0
    break;
2289
0
  }
2290
2291
0
  if(!incoming) {
2292
0
    CURL_TRC_CF(data, cf, "nothing heard from the server yet");
2293
0
    return CURLE_OK;
2294
0
  }
2295
2296
0
  size = sizeof(add);
2297
0
#ifdef HAVE_ACCEPT4
2298
0
  s_accepted = CURL_ACCEPT4(ctx->sock, (struct sockaddr *)&add, &size,
2299
0
                            SOCK_NONBLOCK | SOCK_CLOEXEC);
2300
#else
2301
  s_accepted = CURL_ACCEPT(ctx->sock, (struct sockaddr *)&add, &size);
2302
#endif
2303
2304
0
  if(s_accepted == CURL_SOCKET_BAD) {
2305
0
    failf(data, "Error accept()ing server connect: %s",
2306
0
          curlx_strerror(SOCKERRNO, errbuf, sizeof(errbuf)));
2307
0
    return CURLE_FTP_ACCEPT_FAILED;
2308
0
  }
2309
#ifndef HAVE_ACCEPT4
2310
#ifdef HAVE_FCNTL
2311
  if(fcntl(s_accepted, F_SETFD, FD_CLOEXEC) < 0) {
2312
    failf(data, "fcntl set CLOEXEC: %s",
2313
          curlx_strerror(SOCKERRNO, errbuf, sizeof(errbuf)));
2314
    Curl_socket_close(data, cf->conn, s_accepted);
2315
    return CURLE_FTP_ACCEPT_FAILED;
2316
  }
2317
#endif /* HAVE_FCNTL */
2318
  if(curlx_nonblock(s_accepted, TRUE) < 0) {
2319
    failf(data, "set socket NONBLOCK: %s",
2320
          curlx_strerror(SOCKERRNO, errbuf, sizeof(errbuf)));
2321
    Curl_socket_close(data, cf->conn, s_accepted);
2322
    return CURLE_FTP_ACCEPT_FAILED;
2323
  }
2324
#endif /* !HAVE_ACCEPT4 */
2325
0
  infof(data, "Connection accepted from server");
2326
2327
  /* Replace any filter on SECONDARY with one listening on this socket */
2328
0
  ctx->listening = FALSE;
2329
0
  ctx->accepted = TRUE;
2330
0
  socket_close(data, cf->conn, TRUE, ctx->sock);
2331
0
  ctx->sock = s_accepted;
2332
2333
0
  cf->conn->sock[cf->sockindex] = ctx->sock;
2334
0
  cf_tcp_set_accepted_remote_ip(cf, data);
2335
0
  set_local_ip(cf, data);
2336
0
  ctx->active = TRUE;
2337
0
  ctx->connected_at = *Curl_pgrs_now(data);
2338
0
  cf->connected = TRUE;
2339
0
  CURL_TRC_CF(data, cf, "accepted_set(sock=%" FMT_SOCKET_T
2340
0
              ", remote=%s port=%d)",
2341
0
              ctx->sock, ctx->ip.remote_ip, ctx->ip.remote_port);
2342
2343
0
  if(data->set.fsockopt) {
2344
0
    struct Curl_mapi_guard guard;
2345
0
    int error = 0;
2346
2347
    /* activate callback for setting socket options */
2348
0
    CURL_CBAPI_START(&guard, data, easy_fsockopt);
2349
0
    error = data->set.fsockopt(data->set.sockopt_client,
2350
0
                               ctx->sock, CURLSOCKTYPE_ACCEPT);
2351
0
    CURL_CBAPI_END(&guard);
2352
2353
0
    if(error)
2354
0
      return CURLE_ABORTED_BY_CALLBACK;
2355
0
  }
2356
0
  *done = TRUE;
2357
0
  return CURLE_OK;
2358
0
}
2359
2360
struct Curl_cftype Curl_cft_tcp_accept = {
2361
  "TCP-ACCEPT",
2362
  CF_TYPE_IP_CONNECT,
2363
  CURL_LOG_LVL_NONE,
2364
  cf_socket_destroy,
2365
  cf_tcp_accept_connect,
2366
  cf_socket_shutdown,
2367
  cf_socket_adjust_pollset,
2368
  Curl_cf_def_data_pending,
2369
  cf_socket_send,
2370
  cf_socket_recv,
2371
  cf_socket_cntrl,
2372
  cf_socket_conn_is_alive,
2373
  Curl_cf_def_conn_keep_alive,
2374
  cf_socket_query,
2375
};
2376
2377
CURLcode Curl_conn_tcp_listen_set(struct Curl_easy *data,
2378
                                  struct connectdata *conn,
2379
                                  int8_t sockindex, curl_socket_t *s)
2380
0
{
2381
0
  CURLcode result;
2382
0
  struct Curl_cfilter *cf = NULL;
2383
0
  struct cf_socket_ctx *ctx = NULL;
2384
2385
  /* replace any existing */
2386
0
  Curl_conn_cf_discard_all(data, conn, sockindex);
2387
0
  DEBUGASSERT(conn->sock[sockindex] == CURL_SOCKET_BAD);
2388
2389
0
  ctx = curlx_calloc(1, sizeof(*ctx));
2390
0
  if(!ctx) {
2391
0
    result = CURLE_OUT_OF_MEMORY;
2392
0
    goto out;
2393
0
  }
2394
0
  ctx->transport = TRNSPRT_TCP;
2395
0
  ctx->sock = *s;
2396
0
  ctx->listening = TRUE;
2397
0
  ctx->accepted = FALSE;
2398
0
  result = Curl_cf_create(&cf, &Curl_cft_tcp_accept, ctx);
2399
0
  if(result)
2400
0
    goto out;
2401
0
  Curl_conn_cf_add(data, conn, sockindex, cf);
2402
2403
0
  ctx->started_at = *Curl_pgrs_now(data);
2404
0
  conn->sock[sockindex] = ctx->sock;
2405
0
  set_local_ip(cf, data);
2406
0
  CURL_TRC_CF(data, cf, "set filter for listen socket fd=%" FMT_SOCKET_T
2407
0
              " ip=%s:%d", ctx->sock,
2408
0
              ctx->ip.local_ip, ctx->ip.local_port);
2409
2410
0
out:
2411
0
  if(result) {
2412
0
    curlx_safefree(cf);
2413
0
    curlx_safefree(ctx);
2414
0
  }
2415
0
  return result;
2416
0
}
2417
2418
bool Curl_conn_is_tcp_listen(struct Curl_easy *data,
2419
                             int8_t sockindex)
2420
0
{
2421
0
  struct Curl_cfilter *cf = data->conn->cfilter[sockindex];
2422
0
  while(cf) {
2423
0
    if(cf->cft == &Curl_cft_tcp_accept)
2424
0
      return TRUE;
2425
0
    cf = cf->next;
2426
0
  }
2427
0
  return FALSE;
2428
0
}
2429
2430
/**
2431
 * Return TRUE iff `cf` is a socket filter.
2432
 */
2433
static bool cf_is_socket(struct Curl_cfilter *cf)
2434
0
{
2435
0
  return cf && (cf->cft == &Curl_cft_tcp ||
2436
0
                cf->cft == &Curl_cft_udp ||
2437
0
                cf->cft == &Curl_cft_unix ||
2438
0
                cf->cft == &Curl_cft_tcp_accept);
2439
0
}
2440
2441
CURLcode Curl_cf_socket_peek(struct Curl_cfilter *cf,
2442
                             struct Curl_easy *data,
2443
                             curl_socket_t *psock,
2444
                             const struct Curl_sockaddr_ex **paddr,
2445
                             struct ip_quadruple *pip)
2446
0
{
2447
0
  (void)data;
2448
0
  if(cf_is_socket(cf) && cf->ctx) {
2449
0
    struct cf_socket_ctx *ctx = cf->ctx;
2450
2451
0
    if(psock)
2452
0
      *psock = ctx->sock;
2453
0
    if(paddr)
2454
0
      *paddr = &ctx->addr;
2455
0
    if(pip)
2456
0
      *pip = ctx->ip;
2457
0
    return CURLE_OK;
2458
0
  }
2459
0
  return CURLE_FAILED_INIT;
2460
0
}