Coverage Report

Created: 2026-10-22 06:49

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