Coverage Report

Created: 2026-10-22 06:52

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
26.8k
{
100
26.8k
  CURLcode result;
101
26.8k
  struct sockaddr_in *si = NULL;
102
26.8k
#ifdef USE_IPV6
103
26.8k
  struct sockaddr_in6 *si6 = NULL;
104
26.8k
#endif
105
26.8k
#ifdef USE_UNIX_SOCKETS
106
26.8k
  struct sockaddr_un *su = NULL;
107
#else
108
  (void)salen;
109
#endif
110
111
26.8k
  switch(sa->sa_family) {
112
8.97k
  case AF_INET:
113
8.97k
    si = (struct sockaddr_in *)(void *)sa;
114
8.97k
    result = curlx_inet_ntop(sa->sa_family, &si->sin_addr, addr,
115
8.97k
                             MAX_IPADR_LEN);
116
8.97k
    if(!result) {
117
8.97k
      *port = ntohs(si->sin_port);
118
8.97k
      return result;
119
8.97k
    }
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
17.9k
  case AF_UNIX:
134
17.9k
    if(salen > (curl_socklen_t)sizeof(CURL_SA_FAMILY_T)) {
135
50
      su = (struct sockaddr_un *)sa;
136
50
      curl_msnprintf(addr, MAX_IPADR_LEN, "%s", su->sun_path);
137
50
    }
138
17.8k
    else
139
17.8k
      addr[0] = 0; /* socket with no name */
140
17.9k
    *port = 0;
141
17.9k
    return CURLE_OK;
142
0
#endif
143
0
  default:
144
0
    result = CURLE_UNSUPPORTED_PROTOCOL;
145
0
    break;
146
26.8k
  }
147
148
0
  addr[0] = '\0';
149
0
  *port = 0;
150
0
  return result;
151
26.8k
}
152
153
static void tcpnodelay(struct Curl_cfilter *cf,
154
                       struct Curl_easy *data,
155
                       curl_socket_t sockfd)
156
8.97k
{
157
8.97k
#if defined(TCP_NODELAY) && defined(CURL_TCP_NODELAY_SUPPORTED)
158
8.97k
  curl_socklen_t onoff = (curl_socklen_t)1;
159
8.97k
  int level = IPPROTO_TCP;
160
8.97k
  VERBOSE(char buffer[STRERROR_LEN]);
161
162
8.97k
  if(setsockopt(sockfd, level, TCP_NODELAY, (void *)&onoff, sizeof(onoff)) < 0)
163
8.97k
    CURL_TRC_CF(data, cf, "Could not set TCP_NODELAY: %s",
164
8.97k
                curlx_strerror(SOCKERRNO, buffer, sizeof(buffer)));
165
#else
166
  (void)cf;
167
  (void)data;
168
  (void)sockfd;
169
#endif
170
8.97k
}
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
29
{
199
29
  int optval = data->set.tcp_keepalive ? 1 : 0;
200
201
  /* only set IDLE and INTVL if setting KEEPALIVE is successful */
202
29
  if(setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE,
203
29
                (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
29
  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
29
#ifdef TCP_KEEPIDLE
275
29
    optval = curlx_sltosi(data->set.tcp_keepidle);
276
29
    alive_unit(&optval);
277
29
    if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPIDLE,
278
29
                  (void *)&optval, sizeof(optval)) < 0) {
279
29
      CURL_TRC_CF(data, cf, "Failed to set TCP_KEEPIDLE on fd "
280
29
                  "%" FMT_SOCKET_T ": errno %d", sockfd, SOCKERRNO);
281
29
    }
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
29
#ifdef TCP_KEEPINTVL
302
29
    optval = curlx_sltosi(data->set.tcp_keepintvl);
303
29
    alive_unit(&optval);
304
29
    if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPINTVL,
305
29
                  (void *)&optval, sizeof(optval)) < 0) {
306
29
      CURL_TRC_CF(data, cf, "Failed to set TCP_KEEPINTVL on fd "
307
29
                  "%" FMT_SOCKET_T ": errno %d", sockfd, SOCKERRNO);
308
29
    }
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
29
#ifdef TCP_KEEPCNT
338
29
    optval = curlx_sltosi(data->set.tcp_keepcnt);
339
29
    if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPCNT,
340
29
                  (void *)&optval, sizeof(optval)) < 0) {
341
29
      CURL_TRC_CF(data, cf, "Failed to set TCP_KEEPCNT on fd "
342
29
                  "%" FMT_SOCKET_T ": errno %d", sockfd, SOCKERRNO);
343
29
    }
344
29
#endif
345
29
#endif /* USE_WINSOCK */
346
29
  }
347
29
}
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.02k
{
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.02k
  addr->family = ai->ai_family;
365
9.02k
  addr->socktype = Curl_socktype_for_transport(transport);
366
9.02k
  addr->protocol = Curl_protocol_for_transport(transport);
367
9.02k
  addr->addrlen = (unsigned int)ai->ai_addrlen;
368
369
9.02k
  DEBUGASSERT(addr->addrlen <= sizeof(addr->curl_sa_addrbuf));
370
9.02k
  if(addr->addrlen > sizeof(addr->curl_sa_addrbuf))
371
0
    return CURLE_TOO_LARGE;
372
373
9.02k
  memcpy(&addr->curl_sa_addrbuf, ai->ai_addr, addr->addrlen);
374
9.02k
  return CURLE_OK;
375
9.02k
}
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.02k
{
433
9.02k
  char errbuf[STRERROR_LEN];
434
435
9.02k
#ifdef SOCK_CLOEXEC
436
9.02k
  addr->socktype |= SOCK_CLOEXEC;
437
9.02k
#endif
438
439
9.02k
  DEBUGASSERT(data);
440
9.02k
  DEBUGASSERT(data->conn);
441
9.02k
  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.02k
    struct Curl_mapi_guard guard;
452
9.02k
    CURL_CBAPI_START(&guard, data, easy_fopensocket);
453
9.02k
    *sockfd = data->set.fopensocket(data->set.opensocket_client,
454
9.02k
                                    CURLSOCKTYPE_IPCXN,
455
9.02k
                                    (struct curl_sockaddr *)addr);
456
9.02k
    CURL_CBAPI_END(&guard);
457
9.02k
  }
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.02k
  if(*sockfd == CURL_SOCKET_BAD) {
472
    /* no socket, no connection */
473
2
    failf(data, "failed to open socket: %s",
474
2
          curlx_strerror(SOCKERRNO, errbuf, sizeof(errbuf)));
475
2
    return CURLE_COULDNT_CONNECT;
476
2
  }
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.02k
#if defined(USE_IPV6) && defined(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID)
499
9.02k
  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.02k
#endif
504
9.02k
  return CURLE_OK;
505
9.02k
}
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.02k
{
539
9.02k
  if(sock == CURL_SOCKET_BAD)
540
0
    return 0;
541
542
9.02k
  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.02k
  if(conn)
553
    /* tell the multi-socket code about this */
554
9.02k
    Curl_multi_will_close(data, sock);
555
556
9.02k
  sclose(sock);
557
558
9.02k
  return 0;
559
9.02k
}
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
259
{
602
259
  static const char if_prefix[] = "if!";
603
259
  static const char host_prefix[] = "host!";
604
259
  static const char if_host_prefix[] = "ifhost!";
605
259
  size_t len;
606
607
259
  len = strlen(input);
608
259
  if(len > 512)
609
1
    return CURLE_BAD_FUNCTION_ARGUMENT;
610
611
258
  if(dev)
612
53
    *dev = NULL;
613
258
  if(iface)
614
53
    *iface = NULL;
615
258
  if(host)
616
53
    *host = NULL;
617
618
258
  if(!strncmp(if_prefix, input, strlen(if_prefix))) {
619
2
    input += strlen(if_prefix);
620
2
    if(!*input)
621
1
      return CURLE_BAD_FUNCTION_ARGUMENT;
622
1
    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
1
    return CURLE_OK;
627
1
  }
628
256
  else if(!strncmp(host_prefix, input, strlen(host_prefix))) {
629
2
    input += strlen(host_prefix);
630
2
    if(!*input)
631
1
      return CURLE_BAD_FUNCTION_ARGUMENT;
632
1
    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
1
    return CURLE_OK;
637
1
  }
638
254
  else if(!strncmp(if_host_prefix, input, strlen(if_host_prefix))) {
639
3
    const char *host_part;
640
3
    input += strlen(if_host_prefix);
641
3
    len -= strlen(if_host_prefix);
642
3
    host_part = memchr(input, '!', len);
643
3
    if(!host_part || !*(host_part + 1))
644
2
      return CURLE_BAD_FUNCTION_ARGUMENT;
645
1
    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
1
    ++host_part;
651
1
    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
1
    return CURLE_OK;
660
1
  }
661
662
251
  if(!*input)
663
0
    return CURLE_BAD_FUNCTION_ARGUMENT;
664
251
  if(dev) {
665
53
    *dev = curlx_memdup0(input, len);
666
53
    return *dev ? CURLE_OK : CURLE_OUT_OF_MEMORY;
667
53
  }
668
198
  return CURLE_OK;
669
251
}
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
8.97k
{
676
8.97k
  struct Curl_sockaddr_storage sa;
677
8.97k
  struct sockaddr *sock = (struct sockaddr *)&sa;  /* bind to this address */
678
8.97k
  curl_socklen_t sizeof_sa = 0; /* size of the data sock points to */
679
8.97k
  struct sockaddr_in *si4 = (struct sockaddr_in *)&sa;
680
8.97k
#ifdef USE_IPV6
681
8.97k
  struct sockaddr_in6 *si6 = (struct sockaddr_in6 *)&sa;
682
8.97k
#endif
683
8.97k
  struct Curl_dns_entry *h = NULL;
684
8.97k
  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
8.97k
  int portnum = data->set.localportrange;
688
8.97k
  char *dev_in = NULL, *iface_in = NULL, *host_in = NULL;
689
8.97k
  const char *iface = NULL, *host = NULL;
690
8.97k
  int sockerr;
691
8.97k
#ifdef IP_BIND_ADDRESS_NO_PORT
692
8.97k
  int on = 1;
693
8.97k
#endif
694
8.97k
  CURLcode result = CURLE_OK;
695
696
#ifndef USE_IPV6
697
  (void)scope;
698
#endif
699
8.97k
  if(conn->interface_opts) {
700
    /*************************************************************
701
     * Select device to bind socket to
702
     *************************************************************/
703
53
    result = Curl_parse_interface(conn->interface_opts,
704
53
                                  &dev_in, &iface_in, &host_in);
705
53
    if(result)
706
0
      goto out;
707
    /* This is how we use the values */
708
53
    iface = iface_in ? iface_in : dev_in;
709
53
    host = host_in ? host_in : dev_in;
710
53
  }
711
712
8.97k
  if(!iface && !host && !port)
713
8.91k
    goto out;
714
715
64
  if(iface && (strlen(iface) >= 255)) {
716
0
    result = CURLE_BAD_FUNCTION_ARGUMENT;
717
0
    goto out;
718
0
  }
719
64
  memset(&sa, 0, sizeof(struct Curl_sockaddr_storage));
720
721
64
  if(iface || host) {
722
53
    char myhost[256] = "";
723
53
    int done = 0; /* -1 for error, 1 for address found */
724
53
    if2ip_result_t if2ip_result = IF2IP_NOT_FOUND;
725
726
53
#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
53
    if(iface &&
738
53
       setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE,
739
53
                  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
53
#endif
751
53
    if(!host_in) {
752
      /* Discover IP from input device, then bind to it */
753
53
      if2ip_result = Curl_if2ip(af,
754
53
#ifdef USE_IPV6
755
53
                                scope, conn->scope_id,
756
53
#endif
757
53
                                iface, myhost, sizeof(myhost));
758
53
    }
759
53
    switch(if2ip_result) {
760
53
    case IF2IP_NOT_FOUND:
761
53
      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
53
      break;
771
53
    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
53
    }
785
53
    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
53
      uint8_t dns_queries = (af == AF_INET) ?
795
53
                            CURL_DNSQ_A : (CURL_DNSQ_A | CURL_DNSQ_AAAA);
796
53
#ifdef USE_IPV6
797
53
      if(af == AF_INET6)
798
0
        dns_queries = CURL_DNSQ_AAAA;
799
53
#endif
800
801
53
      (void)Curl_resolv_blocking(data, dns_queries, host, 80, transport, &h);
802
53
      if(h) {
803
53
        int h_af = h->addr->ai_family;
804
        /* convert the resolved address, sizeof myhost >= INET_ADDRSTRLEN */
805
53
        Curl_printable_address(h->addr, myhost, sizeof(myhost));
806
53
        infof(data, "Name '%s' family %d resolved to '%s' family %d",
807
53
              host, af, myhost, h_af);
808
53
        Curl_dns_entry_unlink(data, &h); /* this will NULL, potential free h */
809
53
        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
53
        done = 1;
816
53
      }
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
53
    }
825
826
53
    if(done > 0) {
827
53
#ifdef USE_IPV6
828
      /* IPv6 address */
829
53
      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
53
      else
857
53
#endif
858
      /* IPv4 address */
859
53
      if((af == AF_INET) &&
860
53
         (curlx_inet_pton(AF_INET, myhost, &si4->sin_addr) > 0)) {
861
53
        si4->sin_family = AF_INET;
862
53
        si4->sin_port = htons(port);
863
53
        sizeof_sa = sizeof(struct sockaddr_in);
864
53
      }
865
53
    }
866
867
53
    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
53
  }
880
11
  else {
881
    /* no device was given, prepare sa to match af's needs */
882
11
#ifdef USE_IPV6
883
11
    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
11
    else
889
11
#endif
890
11
    if(af == AF_INET) {
891
11
      si4->sin_family = AF_INET;
892
11
      si4->sin_port = htons(port);
893
11
      sizeof_sa = sizeof(struct sockaddr_in);
894
11
    }
895
11
  }
896
64
#ifdef IP_BIND_ADDRESS_NO_PORT
897
64
  (void)setsockopt(sockfd, SOL_IP, IP_BIND_ADDRESS_NO_PORT, &on, sizeof(on));
898
64
#endif
899
131k
  for(;;) {
900
131k
    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
131k
    if(--portnum > 0) {
909
131k
      port++; /* try next port */
910
131k
      if(port == 0)
911
4
        break;
912
131k
      infof(data, "Bind to local port %d failed, trying next", port - 1);
913
      /* We reuse/clobber the port variable here below */
914
131k
      if(sock->sa_family == AF_INET)
915
131k
        si4->sin_port = htons(port);
916
0
#ifdef USE_IPV6
917
0
      else
918
0
        si6->sin6_port = htons(port);
919
131k
#endif
920
131k
    }
921
60
    else
922
60
      break;
923
131k
  }
924
64
  {
925
64
    char buffer[STRERROR_LEN];
926
64
    data->state.os_errno = sockerr = SOCKERRNO;
927
64
    failf(data, "bind failed with errno %d: %s",
928
64
          sockerr, curlx_strerror(sockerr, buffer, sizeof(buffer)));
929
64
  }
930
931
64
  result = CURLE_INTERFACE_FAILED;
932
933
8.97k
out:
934
8.97k
  curlx_free(dev_in);
935
8.97k
  curlx_free(iface_in);
936
8.97k
  curlx_free(host_in);
937
8.97k
  return result;
938
64
}
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.02k
{
1050
9.02k
  memset(ctx, 0, sizeof(*ctx));
1051
9.02k
  Curl_peer_link(&ctx->peer, peer);
1052
9.02k
  ctx->sock = CURL_SOCKET_BAD;
1053
9.02k
  ctx->transport = transport;
1054
9.02k
  ctx->addr = *addr;
1055
1056
9.02k
#ifdef DEBUGBUILD
1057
9.02k
  {
1058
9.02k
    const char *p = getenv("CURL_DBG_SOCK_WBLOCK");
1059
9.02k
    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.02k
    p = getenv("CURL_DBG_SOCK_WPARTIAL");
1065
9.02k
    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.02k
    p = getenv("CURL_DBG_SOCK_RBLOCK");
1071
9.02k
    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.02k
    p = getenv("CURL_DBG_SOCK_RMAX");
1077
9.02k
    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.02k
    p = getenv("CURL_DBG_SOCK_CONNECT_WAIT_MS");
1083
9.02k
    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.02k
  }
1089
9.02k
#endif
1090
1091
9.02k
  return CURLE_OK;
1092
9.02k
}
1093
1094
static void cf_socket_ctx_free(struct cf_socket_ctx *ctx)
1095
9.02k
{
1096
9.02k
  if(ctx) {
1097
9.02k
    Curl_peer_unlink(&ctx->peer);
1098
9.02k
    curlx_free(ctx);
1099
9.02k
  }
1100
9.02k
}
1101
1102
static CURLcode cf_socket_shutdown(struct Curl_cfilter *cf,
1103
                                   struct Curl_easy *data,
1104
                                   bool *done)
1105
1.38k
{
1106
1.38k
  if(cf->connected) {
1107
1.38k
    struct cf_socket_ctx *ctx = cf->ctx;
1108
1109
1.38k
    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
1.38k
    if(ctx->sock != CURL_SOCKET_BAD &&
1114
1.38k
       ctx->transport == TRNSPRT_TCP &&
1115
1.37k
       (curlx_nonblock(ctx->sock, TRUE) >= 0)) {
1116
1.37k
      unsigned char buf[1024];
1117
1.37k
      (void)sread(ctx->sock, buf, sizeof(buf));
1118
1.37k
    }
1119
1.38k
  }
1120
1.38k
  *done = TRUE;
1121
1.38k
  return CURLE_OK;
1122
1.38k
}
1123
1124
static void cf_socket_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
1125
9.02k
{
1126
9.02k
  struct cf_socket_ctx *ctx = cf->ctx;
1127
1128
9.02k
  CURL_TRC_CF(data, cf, "destroy");
1129
9.02k
  if(ctx) {
1130
9.02k
    if(ctx->sock != CURL_SOCKET_BAD) {
1131
8.96k
      CURL_TRC_CF(data, cf, "cf_socket_close, fd=%" FMT_SOCKET_T, ctx->sock);
1132
8.96k
      if(ctx->sock == cf->conn->sock[cf->sockindex])
1133
8.89k
        cf->conn->sock[cf->sockindex] = CURL_SOCKET_BAD;
1134
8.96k
      socket_close(data, cf->conn, !ctx->accepted, ctx->sock);
1135
8.96k
    }
1136
9.02k
    cf_socket_ctx_free(ctx);
1137
9.02k
  }
1138
9.02k
}
1139
1140
static void set_local_ip(struct Curl_cfilter *cf,
1141
                         struct Curl_easy *data)
1142
17.9k
{
1143
17.9k
  struct cf_socket_ctx *ctx = cf->ctx;
1144
17.9k
  ctx->ip.local_ip[0] = 0;
1145
17.9k
  ctx->ip.local_port = 0;
1146
1147
17.9k
#ifdef HAVE_GETSOCKNAME
1148
17.9k
  if((ctx->sock != CURL_SOCKET_BAD) &&
1149
17.8k
     !(data->conn->scheme->protocol & CURLPROTO_TFTP)) {
1150
    /* TFTP does not connect, so it cannot get the IP like this */
1151
17.8k
    struct Curl_sockaddr_storage ssloc;
1152
17.8k
    curl_socklen_t slen = sizeof(struct Curl_sockaddr_storage);
1153
1154
17.8k
    memset(&ssloc, 0, sizeof(ssloc));
1155
17.8k
    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
17.8k
    else {
1162
17.8k
      CURLcode result = sockaddr2string((struct sockaddr *)&ssloc, slen,
1163
17.8k
                                        ctx->ip.local_ip, &ctx->ip.local_port);
1164
17.8k
      if(result)
1165
0
        infof(data, "ssloc inet_ntop() failed with %d", (int)result);
1166
17.8k
    }
1167
17.8k
  }
1168
#else
1169
  (void)data;
1170
#endif
1171
17.9k
}
1172
1173
static CURLcode set_remote_ip(struct Curl_cfilter *cf,
1174
                              struct Curl_easy *data)
1175
9.02k
{
1176
9.02k
  struct cf_socket_ctx *ctx = cf->ctx;
1177
9.02k
  CURLcode result;
1178
1179
  /* store remote address and port used in this connection attempt */
1180
9.02k
  ctx->ip.transport = ctx->transport;
1181
9.02k
  result = sockaddr2string(&ctx->addr.curl_sa_addr,
1182
9.02k
                           (curl_socklen_t)ctx->addr.addrlen,
1183
9.02k
                           ctx->ip.remote_ip, &ctx->ip.remote_port);
1184
9.02k
  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.02k
  return CURLE_OK;
1191
9.02k
}
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
17.9k
{
1197
17.9k
#ifdef SOCK_CLOEXEC
1198
17.9k
  x &= ~SOCK_CLOEXEC;
1199
17.9k
#endif
1200
17.9k
#ifdef CURL_USE_SOCK_NONBLOCK
1201
17.9k
  x &= ~SOCK_NONBLOCK;
1202
17.9k
#endif
1203
17.9k
  return x;
1204
17.9k
}
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.02k
{
1259
9.02k
  struct cf_socket_ctx *ctx = cf->ctx;
1260
9.02k
  int error = 0;
1261
9.02k
  bool isconnected = FALSE;
1262
9.02k
  CURLcode result = CURLE_COULDNT_CONNECT;
1263
9.02k
  bool is_tcp;
1264
1265
9.02k
  DEBUGASSERT(ctx->sock == CURL_SOCKET_BAD);
1266
9.02k
  ctx->started_at = *Curl_pgrs_now(data);
1267
9.02k
#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.02k
  if(!data->set.fopensocket)
1273
0
    ctx->addr.socktype |= SOCK_NONBLOCK;
1274
9.02k
#endif
1275
9.02k
  result = socket_open(data, &ctx->addr, &ctx->sock);
1276
9.02k
#ifdef CURL_USE_SOCK_NONBLOCK
1277
  /* Restore the socktype after the socket is created. */
1278
9.02k
  if(!data->set.fopensocket)
1279
0
    ctx->addr.socktype &= ~SOCK_NONBLOCK;
1280
9.02k
#endif
1281
9.02k
  if(result)
1282
2
    goto out;
1283
1284
9.02k
  result = set_remote_ip(cf, data);
1285
9.02k
  if(result)
1286
0
    goto out;
1287
1288
9.02k
#ifdef USE_IPV6
1289
9.02k
  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.02k
  else
1318
9.02k
#endif
1319
9.02k
    infof(data, "  Trying %s:%d...", ctx->ip.remote_ip, ctx->ip.remote_port);
1320
1321
9.02k
#ifdef USE_IPV6
1322
9.02k
  is_tcp = (ctx->addr.family == AF_INET ||
1323
50
            ctx->addr.family == AF_INET6) &&
1324
8.97k
    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.02k
  if(is_tcp) {
1330
8.97k
    if(data->set.tcp_nodelay)
1331
8.97k
      tcpnodelay(cf, data, ctx->sock);
1332
1333
8.97k
    if(data->set.tcp_keepalive)
1334
29
      tcpkeepalive(cf, data, ctx->sock);
1335
1336
8.97k
    tcplocalhost(cf, ctx->sock);
1337
8.97k
  }
1338
1339
9.02k
  if(data->set.fsockopt) {
1340
    /* activate callback for setting socket options */
1341
9.02k
    struct Curl_mapi_guard guard;
1342
9.02k
    CURL_CBAPI_START(&guard, data, easy_fsockopt);
1343
9.02k
    error = data->set.fsockopt(data->set.sockopt_client,
1344
9.02k
                               ctx->sock,
1345
9.02k
                               CURLSOCKTYPE_IPCXN);
1346
9.02k
    CURL_CBAPI_END(&guard);
1347
1348
9.02k
    if(error == CURL_SOCKOPT_ALREADY_CONNECTED)
1349
9.02k
      isconnected = TRUE;
1350
0
    else if(error) {
1351
0
      result = CURLE_ABORTED_BY_CALLBACK;
1352
0
      goto out;
1353
0
    }
1354
9.02k
  }
1355
1356
9.02k
#ifndef CURL_DISABLE_BINDLOCAL
1357
  /* possibly bind the local end to an IP, interface or port */
1358
9.02k
  if(ctx->addr.family == AF_INET
1359
50
#ifdef USE_IPV6
1360
50
     || ctx->addr.family == AF_INET6
1361
9.02k
#endif
1362
9.02k
    ) {
1363
8.97k
    result = bindlocal(data, cf->conn, ctx->sock, ctx->addr.family,
1364
8.97k
                       Curl_ipv6_scope(&ctx->addr.curl_sa_addr),
1365
8.97k
                       ctx->transport);
1366
8.97k
    if(result) {
1367
64
      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
64
      goto out;
1373
64
    }
1374
8.97k
  }
1375
8.96k
#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
8.96k
  if(data->set.fopensocket) {
1388
    /* Set socket non-blocking, must be a non-blocking socket for
1389
     * a non-blocking connect. */
1390
8.96k
    error = curlx_nonblock(ctx->sock, TRUE);
1391
8.96k
    if(error < 0) {
1392
0
      result = CURLE_UNSUPPORTED_PROTOCOL;
1393
0
      ctx->sockerr = SOCKERRNO;
1394
0
      goto out;
1395
0
    }
1396
8.96k
  }
1397
8.96k
#endif
1398
8.96k
  ctx->sock_connected = (cf_socktype(ctx->addr.socktype) != SOCK_DGRAM);
1399
9.02k
out:
1400
9.02k
  if(result) {
1401
66
    if(ctx->sock != CURL_SOCKET_BAD) {
1402
64
      socket_close(data, cf->conn, TRUE, ctx->sock);
1403
64
      ctx->sock = CURL_SOCKET_BAD;
1404
64
    }
1405
66
  }
1406
8.96k
  else if(isconnected) {
1407
8.96k
    set_local_ip(cf, data);
1408
8.96k
    ctx->connected_at = *Curl_pgrs_now(data);
1409
8.96k
    cf->connected = TRUE;
1410
8.96k
  }
1411
9.02k
  CURL_TRC_CF(data, cf, "cf_socket_open() -> %d, fd=%" FMT_SOCKET_T,
1412
9.02k
              (int)result, ctx->sock);
1413
9.02k
  return result;
1414
8.96k
}
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
9.02k
{
1475
9.02k
  struct cf_socket_ctx *ctx = cf->ctx;
1476
9.02k
  CURLcode result = CURLE_COULDNT_CONNECT;
1477
9.02k
  int rc = 0;
1478
1479
9.02k
  if(cf->connected) {
1480
0
    *done = TRUE;
1481
0
    return CURLE_OK;
1482
0
  }
1483
1484
9.02k
  *done = FALSE; /* a negative world view is best */
1485
9.02k
  if(ctx->sock == CURL_SOCKET_BAD) {
1486
9.02k
    int sockerr;
1487
1488
9.02k
    result = cf_socket_open(cf, data);
1489
9.02k
    if(result)
1490
66
      goto out;
1491
1492
8.96k
    if(cf->connected) {
1493
8.96k
      *done = TRUE;
1494
8.96k
      return CURLE_OK;
1495
8.96k
    }
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
66
out:
1556
66
  if(result) {
1557
66
    VERBOSE(char buffer[STRERROR_LEN]);
1558
66
    set_local_ip(cf, data);
1559
66
    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
66
    else {
1565
66
      VERBOSE(curlx_strcopy(buffer, sizeof(buffer), STRCONST("peer closed")));
1566
66
    }
1567
66
    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
66
    infof(data, "connect to %s port %u from %s port %d failed: %s",
1572
66
          ctx->ip.remote_ip, ctx->ip.remote_port,
1573
66
          ctx->ip.local_ip, ctx->ip.local_port,
1574
66
          curlx_strerror(ctx->sockerr, buffer, sizeof(buffer)));
1575
66
    *done = FALSE;
1576
66
  }
1577
66
  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
2.09k
{
1584
2.09k
  struct cf_socket_ctx *ctx = cf->ctx;
1585
2.09k
  CURLcode result = CURLE_OK;
1586
1587
2.09k
  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
2.09k
    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
2.09k
    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
2.09k
    else if(!ctx->active) {
1603
586
      result = Curl_pollset_add_in(data, ps, ctx->sock);
1604
586
      CURL_TRC_CF(data, cf, "adjust_pollset, !active, POLLIN fd=%"
1605
586
                  FMT_SOCKET_T, ctx->sock);
1606
586
    }
1607
2.09k
  }
1608
2.09k
  return result;
1609
2.09k
}
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
844k
{
1643
844k
  struct cf_socket_ctx *ctx = cf->ctx;
1644
844k
  curl_socket_t fdsave;
1645
844k
  ssize_t rv;
1646
844k
  CURLcode result = CURLE_OK;
1647
844k
  VERBOSE(size_t orig_len = len);
1648
1649
844k
  (void)eos;
1650
844k
  *pnwritten = 0;
1651
844k
  fdsave = cf->conn->sock[cf->sockindex];
1652
844k
  cf->conn->sock[cf->sockindex] = ctx->sock;
1653
1654
844k
#ifdef DEBUGBUILD
1655
  /* simulate network blocking/partial writes */
1656
844k
  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
844k
  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
844k
#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
844k
    rv = swrite(ctx->sock, buf, len);
1683
1684
844k
  if(!curlx_sztouz(rv, pnwritten)) {
1685
828k
    int sockerr = SOCKERRNO;
1686
828k
    if(SOCK_EAGAIN(sockerr)
1687
0
#ifndef USE_WINSOCK
1688
0
       || (sockerr == SOCKEINTR) || (sockerr == SOCKEINPROGRESS)
1689
828k
#endif
1690
828k
      ) {
1691
828k
      result = CURLE_AGAIN;  /* EWOULDBLOCK */
1692
828k
    }
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
828k
  }
1701
1702
#ifdef USE_WINSOCK
1703
  if(!result)
1704
    win_update_sndbuf_size(data, ctx);
1705
#endif
1706
1707
844k
  CURL_TRC_CF(data, cf, "send(len=%zu) -> %d, %zu",
1708
844k
              orig_len, (int)result, *pnwritten);
1709
844k
  cf->conn->sock[cf->sockindex] = fdsave;
1710
844k
  return result;
1711
844k
}
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
86.1k
{
1716
86.1k
  struct cf_socket_ctx *ctx = cf->ctx;
1717
86.1k
  CURLcode result = CURLE_OK;
1718
86.1k
  ssize_t rv;
1719
1720
86.1k
  *pnread = 0;
1721
86.1k
#ifdef DEBUGBUILD
1722
  /* simulate network blocking/partial reads */
1723
86.1k
  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
86.1k
  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
86.1k
#endif
1737
1738
86.1k
  rv = sread(ctx->sock, buf, len);
1739
1740
86.1k
  if(!curlx_sztouz(rv, pnread)) {
1741
598
    int sockerr = SOCKERRNO;
1742
598
    if(SOCK_EAGAIN(sockerr)
1743
0
#ifndef USE_WINSOCK
1744
0
       || (sockerr == SOCKEINTR)
1745
598
#endif
1746
598
      ) {
1747
598
      result = CURLE_AGAIN;  /* EWOULDBLOCK */
1748
598
    }
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
598
  }
1757
1758
86.1k
  CURL_TRC_CF(data, cf, "recv(len=%zu) -> %d, %zu", len, (int)result, *pnread);
1759
86.1k
  if(!result && !ctx->got_first_byte) {
1760
7.49k
    ctx->first_byte_at = *Curl_pgrs_now(data);
1761
7.49k
    ctx->got_first_byte = TRUE;
1762
7.49k
  }
1763
86.1k
  return result;
1764
86.1k
}
1765
1766
static void cf_socket_update_data(struct Curl_cfilter *cf,
1767
                                  struct Curl_easy *data)
1768
8.89k
{
1769
  /* Update the IP info held in the transfer, if we have that. */
1770
8.89k
  if(cf->connected && (cf->sockindex == FIRSTSOCKET)) {
1771
8.89k
    struct cf_socket_ctx *ctx = cf->ctx;
1772
8.89k
    data->info.primary = ctx->ip;
1773
8.89k
  }
1774
8.89k
}
1775
1776
static void cf_socket_active(struct Curl_cfilter *cf, struct Curl_easy *data)
1777
8.89k
{
1778
8.89k
  struct cf_socket_ctx *ctx = cf->ctx;
1779
1780
  /* use this socket from now on */
1781
8.89k
  cf->conn->sock[cf->sockindex] = ctx->sock;
1782
8.89k
  set_local_ip(cf, data);
1783
8.89k
#ifdef USE_IPV6
1784
8.89k
  if(cf->sockindex == FIRSTSOCKET)
1785
8.89k
    cf->conn->bits.ipv6 = (ctx->addr.family == AF_INET6);
1786
8.89k
#endif
1787
8.89k
  ctx->active = TRUE;
1788
8.89k
}
1789
1790
static CURLcode cf_socket_cntrl(struct Curl_cfilter *cf,
1791
                                struct Curl_easy *data,
1792
                                int event, int arg1, void *arg2)
1793
27.1k
{
1794
27.1k
  struct cf_socket_ctx *ctx = cf->ctx;
1795
1796
27.1k
  (void)arg1;
1797
27.1k
  (void)arg2;
1798
27.1k
  switch(event) {
1799
8.89k
  case CF_CTRL_CONN_INFO_UPDATE:
1800
8.89k
    cf_socket_active(cf, data);
1801
8.89k
    cf_socket_update_data(cf, data);
1802
8.89k
    break;
1803
0
  case CF_CTRL_DATA_SETUP:
1804
0
    cf_socket_update_data(cf, data);
1805
0
    break;
1806
0
  case CF_CTRL_FORGET_SOCKET:
1807
0
    ctx->sock = CURL_SOCKET_BAD;
1808
0
    break;
1809
8.89k
  case CF_CTRL_REPORT_STATS:
1810
8.89k
    if(cf->connected && !ctx->stats_reported) {
1811
8.89k
      struct curltime *ts = NULL;
1812
8.89k
      switch(ctx->transport) {
1813
0
      case TRNSPRT_UDP:
1814
0
      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
0
        if(ctx->got_first_byte)
1818
0
          ts = &ctx->first_byte_at;
1819
0
        break;
1820
8.89k
      default:
1821
8.89k
        ts = &ctx->connected_at;
1822
8.89k
        break;
1823
8.89k
      }
1824
8.89k
      if(ts) {
1825
8.89k
        Curl_pgrsTimeWas(data, TIMER_CONNECT, *ts);
1826
8.89k
        ctx->stats_reported = TRUE;
1827
8.89k
      }
1828
8.89k
    }
1829
27.1k
  }
1830
27.1k
  return CURLE_OK;
1831
27.1k
}
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
4.80k
{
1874
4.80k
  struct cf_socket_ctx *ctx = cf->ctx;
1875
1876
4.80k
  switch(query) {
1877
1.17k
  case CF_QUERY_SOCKET:
1878
1.17k
    DEBUGASSERT(pres2);
1879
1.17k
    *((curl_socket_t *)pres2) = ctx->sock;
1880
1.17k
    return CURLE_OK;
1881
0
  case CF_QUERY_TRANSPORT:
1882
0
    DEBUGASSERT(pres1);
1883
0
    *pres1 = ctx->transport;
1884
0
    return CURLE_OK;
1885
0
  case CF_QUERY_REMOTE_ADDR:
1886
0
    DEBUGASSERT(pres2);
1887
0
    *((const struct Curl_sockaddr_ex **)pres2) = cf->connected ?
1888
0
                                                 &ctx->addr : NULL;
1889
0
    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
61
  case CF_QUERY_IP_INFO:
1900
61
#ifdef USE_IPV6
1901
61
    *pres1 = (ctx->addr.family == AF_INET6);
1902
#else
1903
    *pres1 = FALSE;
1904
#endif
1905
61
    *(struct ip_quadruple *)pres2 = ctx->ip;
1906
61
    return CURLE_OK;
1907
0
  case CF_QUERY_REALLY_CONNECTED:
1908
0
    if(cf->cft != &Curl_cft_udp)
1909
0
      *pres1 = cf->connected;
1910
0
    else
1911
0
      *pres1 = ctx->got_first_byte;
1912
0
    return CURLE_OK;
1913
3.57k
  default:
1914
3.57k
    break;
1915
4.80k
  }
1916
3.57k
  return cf->next ?
1917
0
    cf->next->cft->query(cf->next, data, query, pres1, pres2) :
1918
3.57k
    CURLE_UNKNOWN_OPTION;
1919
4.80k
}
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
8.97k
{
1948
8.97k
  struct cf_socket_ctx *ctx = NULL;
1949
8.97k
  struct Curl_cfilter *cf = NULL;
1950
8.97k
  CURLcode result;
1951
1952
8.97k
  (void)data;
1953
8.97k
  (void)origin;
1954
8.97k
  (void)conn;
1955
8.97k
  (void)tunnel_peer;
1956
8.97k
  (void)tunnel_transport;
1957
8.97k
  DEBUGASSERT(transport_peer == TRNSPRT_TCP);
1958
8.97k
  if(!addr) {
1959
0
    result = CURLE_BAD_FUNCTION_ARGUMENT;
1960
0
    goto out;
1961
0
  }
1962
1963
8.97k
  ctx = curlx_calloc(1, sizeof(*ctx));
1964
8.97k
  if(!ctx) {
1965
0
    result = CURLE_OUT_OF_MEMORY;
1966
0
    goto out;
1967
0
  }
1968
1969
8.97k
  result = cf_socket_ctx_init(ctx, peer, addr, transport_peer);
1970
8.97k
  if(result)
1971
0
    goto out;
1972
1973
8.97k
  result = Curl_cf_create(&cf, &Curl_cft_tcp, ctx);
1974
1975
8.97k
out:
1976
8.97k
  *pcf = (!result) ? cf : NULL;
1977
8.97k
  if(result) {
1978
0
    curlx_safefree(cf);
1979
0
    cf_socket_ctx_free(ctx);
1980
0
  }
1981
1982
8.97k
  return result;
1983
8.97k
}
1984
1985
#ifdef __linux__
1986
static void linux_quic_mtu(struct cf_socket_ctx *ctx)
1987
0
{
1988
0
  int val;
1989
0
  switch(ctx->addr.family) {
1990
0
#ifdef IP_MTU_DISCOVER
1991
0
  case AF_INET:
1992
0
    val = IP_PMTUDISC_DO;
1993
0
    (void)setsockopt(ctx->sock, IPPROTO_IP, IP_MTU_DISCOVER, &val,
1994
0
                     sizeof(val));
1995
0
    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
0
#endif
2004
0
  }
2005
0
}
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
{
2015
  int one = 1;
2016
  (void)setsockopt(ctx->sock, IPPROTO_UDP, UDP_GRO, &one,
2017
                   (socklen_t)sizeof(one));
2018
}
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
0
{
2026
0
  unsigned int tos = 1;
2027
0
  switch(ctx->addr.family) {
2028
0
  case AF_INET:
2029
0
    (void)setsockopt(ctx->sock, IPPROTO_IP, IP_RECVTOS, &tos, sizeof(tos));
2030
0
    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
0
#endif
2037
0
  }
2038
0
}
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
0
{
2067
0
  struct cf_socket_ctx *ctx = cf->ctx;
2068
0
  int rc;
2069
2070
  /* QUIC needs a connected socket, nonblocking */
2071
0
  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
0
  rc = connect(ctx->sock, &ctx->addr.curl_sa_addr,
2076
0
               (curl_socklen_t)ctx->addr.addrlen);
2077
0
  if(rc == -1) {
2078
0
    return socket_connect_result(data, ctx->ip.remote_ip, SOCKERRNO);
2079
0
  }
2080
0
  ctx->sock_connected = TRUE;
2081
0
  set_local_ip(cf, data);
2082
0
  CURL_TRC_CF(data, cf, "%s socket %" FMT_SOCKET_T
2083
0
              " connected: [%s:%d] -> [%s:%d]",
2084
0
              (ctx->transport == TRNSPRT_QUIC) ? "QUIC" : "UDP",
2085
0
              ctx->sock, ctx->ip.local_ip, ctx->ip.local_port,
2086
0
              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
0
  linux_quic_ecn(ctx);
2094
0
  linux_quic_mtu(ctx);
2095
0
  linux_ip_dontfrag(ctx);
2096
0
  linux_quic_gro(ctx);
2097
2098
0
  return CURLE_OK;
2099
0
}
2100
2101
static CURLcode cf_udp_connect(struct Curl_cfilter *cf,
2102
                               struct Curl_easy *data,
2103
                               bool *done)
2104
0
{
2105
0
  struct cf_socket_ctx *ctx = cf->ctx;
2106
0
  CURLcode result = CURLE_COULDNT_CONNECT;
2107
2108
0
  if(cf->connected) {
2109
0
    *done = TRUE;
2110
0
    return CURLE_OK;
2111
0
  }
2112
2113
0
  *done = FALSE;
2114
0
  if(ctx->sock == CURL_SOCKET_BAD) {
2115
0
    result = cf_socket_open(cf, data);
2116
0
    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
0
    if(ctx->transport == TRNSPRT_QUIC) {
2123
0
      result = cf_udp_setup_quic(cf, data);
2124
0
      if(result)
2125
0
        goto out;
2126
0
      CURL_TRC_CF(data, cf, "cf_udp_connect(), opened socket=%"
2127
0
                  FMT_SOCKET_T " (%s:%d)",
2128
0
                  ctx->sock, ctx->ip.local_ip, ctx->ip.local_port);
2129
0
    }
2130
0
    *done = TRUE;
2131
0
    cf->connected = TRUE;
2132
0
  }
2133
0
out:
2134
0
  return result;
2135
0
}
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
0
{
2164
0
  struct cf_socket_ctx *ctx = NULL;
2165
0
  struct Curl_cfilter *cf = NULL;
2166
0
  CURLcode result;
2167
2168
0
  (void)data;
2169
0
  (void)origin;
2170
0
  (void)conn;
2171
0
  (void)tunnel_peer;
2172
0
  (void)tunnel_transport;
2173
0
  DEBUGASSERT(transport_peer == TRNSPRT_UDP || transport_peer == TRNSPRT_QUIC);
2174
0
  ctx = curlx_calloc(1, sizeof(*ctx));
2175
0
  if(!ctx) {
2176
0
    result = CURLE_OUT_OF_MEMORY;
2177
0
    goto out;
2178
0
  }
2179
2180
0
  result = cf_socket_ctx_init(ctx, peer, addr, transport_peer);
2181
0
  if(result)
2182
0
    goto out;
2183
2184
0
  result = Curl_cf_create(&cf, &Curl_cft_udp, ctx);
2185
2186
0
out:
2187
0
  *pcf = (!result) ? cf : NULL;
2188
0
  if(result) {
2189
0
    curlx_safefree(cf);
2190
0
    cf_socket_ctx_free(ctx);
2191
0
  }
2192
2193
0
  return result;
2194
0
}
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
51
{
2224
51
  struct cf_socket_ctx *ctx = NULL;
2225
51
  struct Curl_cfilter *cf = NULL;
2226
51
  CURLcode result;
2227
2228
51
  (void)data;
2229
51
  (void)origin;
2230
51
  (void)conn;
2231
51
  (void)tunnel_peer;
2232
51
  (void)tunnel_transport;
2233
51
  DEBUGASSERT(transport_peer == TRNSPRT_UNIX);
2234
51
  ctx = curlx_calloc(1, sizeof(*ctx));
2235
51
  if(!ctx) {
2236
0
    result = CURLE_OUT_OF_MEMORY;
2237
0
    goto out;
2238
0
  }
2239
2240
51
  result = cf_socket_ctx_init(ctx, peer, addr, transport_peer);
2241
51
  if(result)
2242
0
    goto out;
2243
2244
51
  result = Curl_cf_create(&cf, &Curl_cft_unix, ctx);
2245
2246
51
out:
2247
51
  *pcf = (!result) ? cf : NULL;
2248
51
  if(result) {
2249
0
    curlx_safefree(cf);
2250
0
    cf_socket_ctx_free(ctx);
2251
0
  }
2252
2253
51
  return result;
2254
51
}
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
0
{
2510
0
  return cf && (cf->cft == &Curl_cft_tcp ||
2511
0
                cf->cft == &Curl_cft_udp ||
2512
0
                cf->cft == &Curl_cft_unix ||
2513
0
                cf->cft == &Curl_cft_tcp_accept);
2514
0
}
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
0
{
2522
0
  (void)data;
2523
0
  if(cf_is_socket(cf) && cf->ctx) {
2524
0
    struct cf_socket_ctx *ctx = cf->ctx;
2525
2526
0
    if(psock)
2527
0
      *psock = ctx->sock;
2528
0
    if(paddr)
2529
0
      *paddr = &ctx->addr;
2530
0
    if(pip)
2531
0
      *pip = ctx->ip;
2532
0
    return CURLE_OK;
2533
0
  }
2534
0
  return CURLE_FAILED_INIT;
2535
0
}