Coverage Report

Created: 2026-10-22 06:49

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/curl/lib/cf-socket.c
Line
Count
Source
1
/***************************************************************************
2
 *                                  _   _ ____  _
3
 *  Project                     ___| | | |  _ \| |
4
 *                             / __| | | | |_) | |
5
 *                            | (__| |_| |  _ <| |___
6
 *                             \___|\___/|_| \_\_____|
7
 *
8
 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
9
 *
10
 * This software is licensed as described in the file COPYING, which
11
 * you should have received as part of this distribution. The terms
12
 * are also available at https://curl.se/docs/copyright.html.
13
 *
14
 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15
 * copies of the Software, and permit persons to whom the Software is
16
 * furnished to do so, under the terms of the COPYING file.
17
 *
18
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19
 * KIND, either express or implied.
20
 *
21
 * SPDX-License-Identifier: curl
22
 *
23
 ***************************************************************************/
24
#include "curl_setup.h"
25
26
#ifdef HAVE_NETINET_IN_H
27
#include <netinet/in.h> /* <netinet/tcp.h> may need it */
28
#endif
29
#ifdef HAVE_SYS_UN_H
30
#include <sys/un.h> /* for sockaddr_un */
31
#endif
32
#ifdef HAVE_LINUX_TCP_H
33
#include <linux/tcp.h>
34
#elif defined(HAVE_NETINET_TCP_H)
35
#include <netinet/tcp.h>
36
#endif
37
#ifdef HAVE_NETINET_UDP_H
38
#include <netinet/udp.h>
39
#endif
40
#ifdef HAVE_NETINET_IP_H
41
#include <netinet/ip.h>
42
#endif
43
#ifdef HAVE_SYS_IOCTL_H
44
#include <sys/ioctl.h>
45
#endif
46
#ifdef HAVE_NETDB_H
47
#include <netdb.h>
48
#endif
49
#ifdef HAVE_ARPA_INET_H
50
#include <arpa/inet.h>
51
#endif
52
53
#ifdef HAVE_IFADDRS_H
54
#include <ifaddrs.h>
55
#endif
56
#ifdef HAVE_NET_IF_H
57
#include <net/if.h>
58
#endif
59
#ifdef __VMS
60
#include <in.h>
61
#include <inet.h>
62
#endif
63
64
#include "urldata.h"
65
#include "curl_trc.h"
66
#include "if2ip.h"
67
#include "cfilters.h"
68
#include "cf-socket.h"
69
#include "connect.h"
70
#include "curl_addrinfo.h"
71
#include "select.h"
72
#include "multiif.h"
73
#include "curlx/inet_ntop.h"
74
#include "curlx/inet_pton.h"
75
#include "progress.h"
76
#include "conncache.h"
77
#include "multihandle.h"
78
#include "rand.h"
79
#include "sockaddr.h"
80
#include "curlx/strdup.h"
81
#include "curlx/nonblock.h"
82
#include "curlx/strcopy.h"
83
#include "curlx/version_win32.h"
84
#include "curlx/strerr.h"
85
#include "curlx/strparse.h"
86
#include "curlx/wait.h"
87
#ifdef _WIN32
88
#include <mstcpip.h> /* for TCP_INITIAL_RTO_PARAMETERS */
89
#endif
90
91
/* retrieves ip address and port from a sockaddr structure. note it calls
92
 * curlx_inet_ntop() and returns CURLcode.
93
 * @unittest 1607
94
 */
95
UNITTEST CURLcode sockaddr2string(struct sockaddr *sa, curl_socklen_t salen,
96
                                  char *addr, uint16_t *port);
97
UNITTEST CURLcode sockaddr2string(struct sockaddr *sa, curl_socklen_t salen,
98
                                  char *addr, uint16_t *port)
99
57.1k
{
100
57.1k
  CURLcode result;
101
57.1k
  struct sockaddr_in *si = NULL;
102
57.1k
#ifdef USE_IPV6
103
57.1k
  struct sockaddr_in6 *si6 = NULL;
104
57.1k
#endif
105
57.1k
#ifdef USE_UNIX_SOCKETS
106
57.1k
  struct sockaddr_un *su = NULL;
107
#else
108
  (void)salen;
109
#endif
110
111
57.1k
  switch(sa->sa_family) {
112
18.9k
  case AF_INET:
113
18.9k
    si = (struct sockaddr_in *)(void *)sa;
114
18.9k
    result = curlx_inet_ntop(sa->sa_family, &si->sin_addr, addr,
115
18.9k
                             MAX_IPADR_LEN);
116
18.9k
    if(!result) {
117
18.9k
      *port = ntohs(si->sin_port);
118
18.9k
      return result;
119
18.9k
    }
120
0
    break;
121
0
#ifdef USE_IPV6
122
100
  case AF_INET6:
123
100
    si6 = (struct sockaddr_in6 *)(void *)sa;
124
100
    result = curlx_inet_ntop(sa->sa_family, &si6->sin6_addr, addr,
125
100
                             MAX_IPADR_LEN);
126
100
    if(!result) {
127
100
      *port = ntohs(si6->sin6_port);
128
100
      return result;
129
100
    }
130
0
    break;
131
0
#endif
132
0
#ifdef USE_UNIX_SOCKETS
133
38.1k
  case AF_UNIX:
134
38.1k
    if(salen > (curl_socklen_t)sizeof(CURL_SA_FAMILY_T)) {
135
0
      su = (struct sockaddr_un *)sa;
136
0
      curl_msnprintf(addr, MAX_IPADR_LEN, "%s", su->sun_path);
137
0
    }
138
38.1k
    else
139
38.1k
      addr[0] = 0; /* socket with no name */
140
38.1k
    *port = 0;
141
38.1k
    return CURLE_OK;
142
0
#endif
143
0
  default:
144
0
    result = CURLE_UNSUPPORTED_PROTOCOL;
145
0
    break;
146
57.1k
  }
147
148
0
  addr[0] = '\0';
149
0
  *port = 0;
150
0
  return result;
151
57.1k
}
152
153
static void tcpnodelay(struct Curl_cfilter *cf,
154
                       struct Curl_easy *data,
155
                       curl_socket_t sockfd)
156
19.0k
{
157
19.0k
#if defined(TCP_NODELAY) && defined(CURL_TCP_NODELAY_SUPPORTED)
158
19.0k
  curl_socklen_t onoff = (curl_socklen_t)1;
159
19.0k
  int level = IPPROTO_TCP;
160
19.0k
  VERBOSE(char buffer[STRERROR_LEN]);
161
162
19.0k
  if(setsockopt(sockfd, level, TCP_NODELAY, (void *)&onoff, sizeof(onoff)) < 0)
163
19.0k
    CURL_TRC_CF(data, cf, "Could not set TCP_NODELAY: %s",
164
19.0k
                curlx_strerror(SOCKERRNO, buffer, sizeof(buffer)));
165
#else
166
  (void)cf;
167
  (void)data;
168
  (void)sockfd;
169
#endif
170
19.0k
}
171
172
#if defined(USE_WINSOCK) || defined(TCP_KEEPIDLE) ||               \
173
  defined(TCP_KEEPALIVE) || defined(TCP_KEEPALIVE_THRESHOLD) ||    \
174
  defined(TCP_KEEPINTVL) || defined(TCP_KEEPALIVE_ABORT_THRESHOLD)
175
#if defined(USE_WINSOCK) || \
176
  (defined(__sun) && !defined(TCP_KEEPIDLE)) || \
177
  (defined(__DragonFly__) && __DragonFly_version < 500702) || \
178
  (defined(_WIN32) && !defined(TCP_KEEPIDLE))
179
/* Solaris < 11.4, DragonFlyBSD < 500702 and Windows < 10.0.16299
180
 * use millisecond units. */
181
static void alive_unit(int *x)
182
{
183
  /* make sure this doesn't wrap */
184
  if(*x < (INT_MAX / 1000))
185
    *x *= 1000;
186
  else
187
    /* this is still almost 25 days */
188
    *x = INT_MAX;
189
}
190
#else
191
#define alive_unit(x)
192
#endif
193
#endif
194
195
static void tcpkeepalive(struct Curl_cfilter *cf,
196
                         struct Curl_easy *data,
197
                         curl_socket_t sockfd)
198
0
{
199
0
  int optval = data->set.tcp_keepalive ? 1 : 0;
200
201
  /* only set IDLE and INTVL if setting KEEPALIVE is successful */
202
0
  if(setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE,
203
0
                (void *)&optval, sizeof(optval)) < 0) {
204
0
    CURL_TRC_CF(data, cf, "Failed to set SO_KEEPALIVE on fd "
205
0
                "%" FMT_SOCKET_T ": errno %d", sockfd, SOCKERRNO);
206
0
  }
207
0
  else {
208
#ifdef USE_WINSOCK
209
    /* Windows 10, version 1709 (10.0.16299) and later versions can use
210
       setsockopt() TCP_KEEP*. Older versions return with failure. */
211
    if(curlx_verify_windows_version(10, 0, 16299, PLATFORM_WINNT,
212
                                    VERSION_GREATER_THAN_EQUAL)) {
213
      CURL_TRC_CF(data, cf, "Set TCP_KEEP* on fd=%" FMT_SOCKET_T, sockfd);
214
      optval = curlx_sltosi(data->set.tcp_keepidle);
215
/* Offered by mingw-w64 v12+, MS SDK 6.0A/VS2008+ */
216
#ifndef TCP_KEEPALIVE
217
#define TCP_KEEPALIVE 3
218
#endif
219
/* Offered by mingw-w64 v12+, MS SDK 10.0.15063.0/VS2017 15.1+ */
220
#ifndef TCP_KEEPCNT
221
#define TCP_KEEPCNT 16
222
#endif
223
/* Offered by mingw-w64 v12+, MS SDK 10.0.16299.0/VS2017 15.4+ */
224
#ifndef TCP_KEEPIDLE
225
#define TCP_KEEPIDLE TCP_KEEPALIVE
226
#endif
227
#ifndef TCP_KEEPINTVL
228
#define TCP_KEEPINTVL 17
229
#endif
230
      if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPIDLE,
231
                    (const char *)&optval, sizeof(optval)) < 0) {
232
        CURL_TRC_CF(data, cf, "Failed to set TCP_KEEPIDLE on fd "
233
                    "%" FMT_SOCKET_T ": errno %d", sockfd, SOCKERRNO);
234
      }
235
      optval = curlx_sltosi(data->set.tcp_keepintvl);
236
      if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPINTVL,
237
                    (const char *)&optval, sizeof(optval)) < 0) {
238
        CURL_TRC_CF(data, cf, "Failed to set TCP_KEEPINTVL on fd "
239
                    "%" FMT_SOCKET_T ": errno %d", sockfd, SOCKERRNO);
240
      }
241
      optval = curlx_sltosi(data->set.tcp_keepcnt);
242
      if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPCNT,
243
                    (const char *)&optval, sizeof(optval)) < 0) {
244
        CURL_TRC_CF(data, cf, "Failed to set TCP_KEEPCNT on fd "
245
                    "%" FMT_SOCKET_T ": errno %d", sockfd, SOCKERRNO);
246
      }
247
    }
248
    else {
249
/* Offered by mingw-w64 and MS SDK. Latter only when targeting Win7+. */
250
#ifndef SIO_KEEPALIVE_VALS
251
#define SIO_KEEPALIVE_VALS  _WSAIOW(IOC_VENDOR, 4)
252
      struct tcp_keepalive {
253
        u_long onoff;
254
        u_long keepalivetime;
255
        u_long keepaliveinterval;
256
      };
257
#endif
258
      struct tcp_keepalive vals;
259
      DWORD dummy;
260
      vals.onoff = 1;
261
      optval = curlx_sltosi(data->set.tcp_keepidle);
262
      alive_unit(&optval);
263
      vals.keepalivetime = (u_long)optval;
264
      optval = curlx_sltosi(data->set.tcp_keepintvl);
265
      alive_unit(&optval);
266
      vals.keepaliveinterval = (u_long)optval;
267
      if(WSAIoctl(sockfd, SIO_KEEPALIVE_VALS, (LPVOID)&vals, sizeof(vals),
268
                  NULL, 0, &dummy, NULL, NULL) != 0) {
269
        CURL_TRC_CF(data, cf, "Failed to set SIO_KEEPALIVE_VALS on fd "
270
                    "%" FMT_SOCKET_T ": errno %d", sockfd, SOCKERRNO);
271
      }
272
    }
273
#else /* !USE_WINSOCK */
274
0
#ifdef TCP_KEEPIDLE
275
0
    optval = curlx_sltosi(data->set.tcp_keepidle);
276
0
    alive_unit(&optval);
277
0
    if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPIDLE,
278
0
                  (void *)&optval, sizeof(optval)) < 0) {
279
0
      CURL_TRC_CF(data, cf, "Failed to set TCP_KEEPIDLE on fd "
280
0
                  "%" FMT_SOCKET_T ": errno %d", sockfd, SOCKERRNO);
281
0
    }
282
#elif defined(TCP_KEEPALIVE)
283
    /* macOS style */
284
    optval = curlx_sltosi(data->set.tcp_keepidle);
285
    alive_unit(&optval);
286
    if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPALIVE,
287
                  (void *)&optval, sizeof(optval)) < 0) {
288
      CURL_TRC_CF(data, cf, "Failed to set TCP_KEEPALIVE on fd "
289
                  "%" FMT_SOCKET_T ": errno %d", sockfd, SOCKERRNO);
290
    }
291
#elif defined(TCP_KEEPALIVE_THRESHOLD)
292
    /* Solaris <11.4 style */
293
    optval = curlx_sltosi(data->set.tcp_keepidle);
294
    alive_unit(&optval);
295
    if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPALIVE_THRESHOLD,
296
                  (void *)&optval, sizeof(optval)) < 0) {
297
      CURL_TRC_CF(data, cf, "Failed to set TCP_KEEPALIVE_THRESHOLD on fd "
298
                  "%" FMT_SOCKET_T ": errno %d", sockfd, SOCKERRNO);
299
    }
300
#endif
301
0
#ifdef TCP_KEEPINTVL
302
0
    optval = curlx_sltosi(data->set.tcp_keepintvl);
303
0
    alive_unit(&optval);
304
0
    if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPINTVL,
305
0
                  (void *)&optval, sizeof(optval)) < 0) {
306
0
      CURL_TRC_CF(data, cf, "Failed to set TCP_KEEPINTVL on fd "
307
0
                  "%" FMT_SOCKET_T ": errno %d", sockfd, SOCKERRNO);
308
0
    }
309
#elif defined(TCP_KEEPALIVE_ABORT_THRESHOLD)
310
    /* Solaris <11.4 style */
311
    /* TCP_KEEPALIVE_ABORT_THRESHOLD should equal to
312
     * TCP_KEEPCNT * TCP_KEEPINTVL on other platforms.
313
     * The default value of TCP_KEEPCNT is 9 on Linux,
314
     * 8 on *BSD/macOS, 5 or 10 on Windows. We use the
315
     * default config for Solaris <11.4 because there is
316
     * no default value for TCP_KEEPCNT on Solaris 11.4.
317
     *
318
     * Note that the consequent probes will not be sent
319
     * at equal intervals on Solaris, but will be sent
320
     * using the exponential backoff algorithm. */
321
    {
322
      int keepcnt = curlx_sltosi(data->set.tcp_keepcnt);
323
      int keepintvl = curlx_sltosi(data->set.tcp_keepintvl);
324
325
      if(keepcnt > 0 && keepintvl > (INT_MAX / keepcnt))
326
        optval = INT_MAX;
327
      else
328
        optval = keepcnt * keepintvl;
329
    }
330
    alive_unit(&optval);
331
    if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPALIVE_ABORT_THRESHOLD,
332
                  (void *)&optval, sizeof(optval)) < 0) {
333
      CURL_TRC_CF(data, cf, "Failed to set TCP_KEEPALIVE_ABORT_THRESHOLD"
334
                  " on fd %" FMT_SOCKET_T ": errno %d", sockfd, SOCKERRNO);
335
    }
336
#endif
337
0
#ifdef TCP_KEEPCNT
338
0
    optval = curlx_sltosi(data->set.tcp_keepcnt);
339
0
    if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPCNT,
340
0
                  (void *)&optval, sizeof(optval)) < 0) {
341
0
      CURL_TRC_CF(data, cf, "Failed to set TCP_KEEPCNT on fd "
342
0
                  "%" FMT_SOCKET_T ": errno %d", sockfd, SOCKERRNO);
343
0
    }
344
0
#endif
345
0
#endif /* USE_WINSOCK */
346
0
  }
347
0
}
348
349
/**
350
 * Assign the addrinfo `ai` to the Curl_sockaddr_ex `addr` with
351
 * transport determining socktype and protocol.
352
 */
353
CURLcode Curl_socket_addr_from_ai(struct Curl_sockaddr_ex *addr,
354
                                  const struct Curl_addrinfo *ai,
355
                                  uint8_t transport)
356
19.1k
{
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
19.1k
  addr->family = ai->ai_family;
365
19.1k
  addr->socktype = Curl_socktype_for_transport(transport);
366
19.1k
  addr->protocol = Curl_protocol_for_transport(transport);
367
19.1k
  addr->addrlen = (unsigned int)ai->ai_addrlen;
368
369
19.1k
  DEBUGASSERT(addr->addrlen <= sizeof(addr->curl_sa_addrbuf));
370
19.1k
  if(addr->addrlen > sizeof(addr->curl_sa_addrbuf))
371
0
    return CURLE_TOO_LARGE;
372
373
19.1k
  memcpy(&addr->curl_sa_addrbuf, ai->ai_addr, addr->addrlen);
374
19.1k
  return CURLE_OK;
375
19.1k
}
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
100
{
390
100
  uint32_t scope_id = 0;
391
100
  if(data->conn->scope_id)
392
10
    return data->conn->scope_id;
393
  /* NOLINTNEXTLINE(clang-analyzer-core.uninitialized.Assign) */
394
90
  scope_id = sa6->sin6_scope_id;
395
90
  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
6
#if defined(HAVE_GETIFADDRS) && defined(HAVE_NET_IF_H)
404
6
    struct ifaddrs *ifa, *ifa_list;
405
6
    if(getifaddrs(&ifa_list) == 0) {
406
30
      for(ifa = ifa_list; ifa; ifa = ifa->ifa_next) {
407
24
        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
24
      }
421
6
      freeifaddrs(ifa_list);
422
6
    }
423
6
#endif /* HAVE_GETIFADDRS && HAVE_NET_IF_H */
424
6
  }
425
90
  return scope_id;
426
100
}
427
#endif
428
429
static CURLcode socket_open(struct Curl_easy *data,
430
                            struct Curl_sockaddr_ex *addr,
431
                            curl_socket_t *sockfd)
432
19.1k
{
433
19.1k
  char errbuf[STRERROR_LEN];
434
435
19.1k
#ifdef SOCK_CLOEXEC
436
19.1k
  addr->socktype |= SOCK_CLOEXEC;
437
19.1k
#endif
438
439
19.1k
  DEBUGASSERT(data);
440
19.1k
  DEBUGASSERT(data->conn);
441
19.1k
  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
19.1k
    struct Curl_mapi_guard guard;
452
19.1k
    CURL_CBAPI_START(&guard, data, easy_fopensocket);
453
19.1k
    *sockfd = data->set.fopensocket(data->set.opensocket_client,
454
19.1k
                                    CURLSOCKTYPE_IPCXN,
455
19.1k
                                    (struct curl_sockaddr *)addr);
456
19.1k
    CURL_CBAPI_END(&guard);
457
19.1k
  }
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
19.1k
  if(*sockfd == CURL_SOCKET_BAD) {
472
    /* no socket, no connection */
473
90
    failf(data, "failed to open socket: %s",
474
90
          curlx_strerror(SOCKERRNO, errbuf, sizeof(errbuf)));
475
90
    return CURLE_COULDNT_CONNECT;
476
90
  }
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
19.0k
#if defined(USE_IPV6) && defined(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID)
499
19.0k
  if(addr->family == AF_INET6) {
500
100
    struct sockaddr_in6 * const sa6 = (void *)&addr->curl_sa_addr;
501
100
    sa6->sin6_scope_id = get_scope_id(data, sa6);
502
100
  }
503
19.0k
#endif
504
19.0k
  return CURLE_OK;
505
19.1k
}
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
19.0k
{
539
19.0k
  if(sock == CURL_SOCKET_BAD)
540
0
    return 0;
541
542
19.0k
  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
19.0k
  if(conn)
553
    /* tell the multi-socket code about this */
554
19.0k
    Curl_multi_will_close(data, sock);
555
556
19.0k
  sclose(sock);
557
558
19.0k
  return 0;
559
19.0k
}
560
561
/*
562
 * Close a socket.
563
 *
564
 * 'conn' can be NULL, beware!
565
 */
566
int Curl_socket_close(struct Curl_easy *data, struct connectdata *conn,
567
                      curl_socket_t sock)
568
0
{
569
0
  return socket_close(data, conn, FALSE, sock);
570
0
}
571
572
/*
573
 * Curl_parse_interface()
574
 *
575
 * This is used to parse interface argument in the following formats.
576
 * In all the examples, `host` can be an IP address or a hostname.
577
 *
578
 *   <iface_or_host> - can be either an interface name or a host.
579
 *   if!<iface> - interface name.
580
 *   host!<host> - hostname.
581
 *   ifhost!<iface>!<host> - interface name and hostname.
582
 *
583
 * Parameters:
584
 *
585
 * input  [in]     - input string.
586
 * len    [in]     - length of the input string.
587
 * dev    [in/out] - address where a pointer to newly allocated memory
588
 *                   holding the interface-or-host will be stored upon
589
 *                   completion or NULL if the caller is not interested.
590
 * iface  [in/out] - address where a pointer to newly allocated memory
591
 *                   holding the interface will be stored upon completion
592
 *                   or NULL if the caller is not interested.
593
 * host   [in/out] - address where a pointer to newly allocated memory
594
 *                   holding the host will be stored upon completion
595
 *                   or NULL if the caller is not interested.
596
 *
597
 * Returns CURLE_OK on success.
598
 */
599
CURLcode Curl_parse_interface(const char *input,
600
                              char **dev, char **iface, char **host)
601
0
{
602
0
  static const char if_prefix[] = "if!";
603
0
  static const char host_prefix[] = "host!";
604
0
  static const char if_host_prefix[] = "ifhost!";
605
0
  size_t len;
606
607
0
  len = strlen(input);
608
0
  if(len > 512)
609
0
    return CURLE_BAD_FUNCTION_ARGUMENT;
610
611
0
  if(dev)
612
0
    *dev = NULL;
613
0
  if(iface)
614
0
    *iface = NULL;
615
0
  if(host)
616
0
    *host = NULL;
617
618
0
  if(!strncmp(if_prefix, input, strlen(if_prefix))) {
619
0
    input += strlen(if_prefix);
620
0
    if(!*input)
621
0
      return CURLE_BAD_FUNCTION_ARGUMENT;
622
0
    if(iface) {
623
0
      *iface = curlx_memdup0(input, len - strlen(if_prefix));
624
0
      return *iface ? CURLE_OK : CURLE_OUT_OF_MEMORY;
625
0
    }
626
0
    return CURLE_OK;
627
0
  }
628
0
  else if(!strncmp(host_prefix, input, strlen(host_prefix))) {
629
0
    input += strlen(host_prefix);
630
0
    if(!*input)
631
0
      return CURLE_BAD_FUNCTION_ARGUMENT;
632
0
    if(host) {
633
0
      *host = curlx_memdup0(input, len - strlen(host_prefix));
634
0
      return *host ? CURLE_OK : CURLE_OUT_OF_MEMORY;
635
0
    }
636
0
    return CURLE_OK;
637
0
  }
638
0
  else if(!strncmp(if_host_prefix, input, strlen(if_host_prefix))) {
639
0
    const char *host_part;
640
0
    input += strlen(if_host_prefix);
641
0
    len -= strlen(if_host_prefix);
642
0
    host_part = memchr(input, '!', len);
643
0
    if(!host_part || !*(host_part + 1))
644
0
      return CURLE_BAD_FUNCTION_ARGUMENT;
645
0
    if(iface) {
646
0
      *iface = curlx_memdup0(input, host_part - input);
647
0
      if(!*iface)
648
0
        return CURLE_OUT_OF_MEMORY;
649
0
    }
650
0
    ++host_part;
651
0
    if(host) {
652
0
      *host = curlx_memdup0(host_part, len - (host_part - input));
653
0
      if(!*host) {
654
0
        if(iface)
655
0
          curlx_safefree(*iface);
656
0
        return CURLE_OUT_OF_MEMORY;
657
0
      }
658
0
    }
659
0
    return CURLE_OK;
660
0
  }
661
662
0
  if(!*input)
663
0
    return CURLE_BAD_FUNCTION_ARGUMENT;
664
0
  if(dev) {
665
0
    *dev = curlx_memdup0(input, len);
666
0
    return *dev ? CURLE_OK : CURLE_OUT_OF_MEMORY;
667
0
  }
668
0
  return CURLE_OK;
669
0
}
670
671
#ifndef CURL_DISABLE_BINDLOCAL
672
static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn,
673
                          curl_socket_t sockfd, int af, unsigned int scope,
674
                          uint8_t transport)
675
19.0k
{
676
19.0k
  struct Curl_sockaddr_storage sa;
677
19.0k
  struct sockaddr *sock = (struct sockaddr *)&sa;  /* bind to this address */
678
19.0k
  curl_socklen_t sizeof_sa = 0; /* size of the data sock points to */
679
19.0k
  struct sockaddr_in *si4 = (struct sockaddr_in *)&sa;
680
19.0k
#ifdef USE_IPV6
681
19.0k
  struct sockaddr_in6 *si6 = (struct sockaddr_in6 *)&sa;
682
19.0k
#endif
683
19.0k
  struct Curl_dns_entry *h = NULL;
684
19.0k
  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
19.0k
  int portnum = data->set.localportrange;
688
19.0k
  char *dev_in = NULL, *iface_in = NULL, *host_in = NULL;
689
19.0k
  const char *iface = NULL, *host = NULL;
690
19.0k
  int sockerr;
691
19.0k
#ifdef IP_BIND_ADDRESS_NO_PORT
692
19.0k
  int on = 1;
693
19.0k
#endif
694
19.0k
  CURLcode result = CURLE_OK;
695
696
#ifndef USE_IPV6
697
  (void)scope;
698
#endif
699
19.0k
  if(conn->interface_opts) {
700
    /*************************************************************
701
     * Select device to bind socket to
702
     *************************************************************/
703
0
    result = Curl_parse_interface(conn->interface_opts,
704
0
                                  &dev_in, &iface_in, &host_in);
705
0
    if(result)
706
0
      goto out;
707
    /* This is how we use the values */
708
0
    iface = iface_in ? iface_in : dev_in;
709
0
    host = host_in ? host_in : dev_in;
710
0
  }
711
712
19.0k
  if(!iface && !host && !port)
713
19.0k
    goto out;
714
715
0
  if(iface && (strlen(iface) >= 255)) {
716
0
    result = CURLE_BAD_FUNCTION_ARGUMENT;
717
0
    goto out;
718
0
  }
719
0
  memset(&sa, 0, sizeof(struct Curl_sockaddr_storage));
720
721
0
  if(iface || host) {
722
0
    char myhost[256] = "";
723
0
    int done = 0; /* -1 for error, 1 for address found */
724
0
    if2ip_result_t if2ip_result = IF2IP_NOT_FOUND;
725
726
0
#ifdef SO_BINDTODEVICE
727
    /*
728
     * This binds the local socket to a particular interface. This will
729
     * force even requests to other local interfaces to go out the external
730
     * interface. Only bind to the interface when specified as interface,
731
     * not as a hostname or ip address.
732
     *
733
     * The interface might be a VRF, eg: vrf-blue, which means it cannot be
734
     * converted to an IP address and would fail Curl_if2ip. Try to
735
     * use it straight away.
736
     */
737
0
    if(iface &&
738
0
       setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE,
739
0
                  iface, (curl_socklen_t)strlen(iface) + 1) == 0 &&
740
       /* This is often "errno 1, error: Operation not permitted" if you are
741
        * not running as root or another suitable privileged user. If it
742
        * succeeds it means the parameter was a valid interface and not an IP
743
        * address. Return immediately.
744
        */
745
0
       !host_in) {
746
0
      infof(data, "socket successfully bound to interface '%s'", iface);
747
0
      result = CURLE_OK;
748
0
      goto out;
749
0
    }
750
0
#endif
751
0
    if(!host_in) {
752
      /* Discover IP from input device, then bind to it */
753
0
      if2ip_result = Curl_if2ip(af,
754
0
#ifdef USE_IPV6
755
0
                                scope, conn->scope_id,
756
0
#endif
757
0
                                iface, myhost, sizeof(myhost));
758
0
    }
759
0
    switch(if2ip_result) {
760
0
    case IF2IP_NOT_FOUND:
761
0
      if(iface_in && !host_in) {
762
        /* Do not fall back to treating it as a hostname */
763
0
        char buffer[STRERROR_LEN];
764
0
        data->state.os_errno = sockerr = SOCKERRNO;
765
0
        failf(data, "Could not bind to interface '%s' with errno %d: %s",
766
0
              iface, sockerr, curlx_strerror(sockerr, buffer, sizeof(buffer)));
767
0
        result = CURLE_INTERFACE_FAILED;
768
0
        goto out;
769
0
      }
770
0
      break;
771
0
    case IF2IP_AF_NOT_SUPPORTED:
772
      /* Signal the caller to try another address family if available */
773
0
      result = CURLE_UNSUPPORTED_PROTOCOL;
774
0
      goto out;
775
0
    case IF2IP_FOUND:
776
      /*
777
       * We now have the numerical IP address in the 'myhost' buffer
778
       */
779
0
      host = myhost;
780
0
      infof(data, "Local Interface %s is ip %s using address family %d",
781
0
            iface, host, af);
782
0
      done = 1;
783
0
      break;
784
0
    }
785
0
    if(!iface_in || host_in) {
786
      /*
787
       * This was not an interface, resolve the name as a hostname
788
       * or IP number
789
       *
790
       * Temporarily force name resolution to use only the address type
791
       * of the connection. The resolve functions should really be changed
792
       * to take a type parameter instead.
793
       */
794
0
      uint8_t dns_queries = (af == AF_INET) ?
795
0
                            CURL_DNSQ_A : (CURL_DNSQ_A | CURL_DNSQ_AAAA);
796
0
#ifdef USE_IPV6
797
0
      if(af == AF_INET6)
798
0
        dns_queries = CURL_DNSQ_AAAA;
799
0
#endif
800
801
0
      (void)Curl_resolv_blocking(data, dns_queries, host, 80, transport, &h);
802
0
      if(h) {
803
0
        int h_af = h->addr->ai_family;
804
        /* convert the resolved address, sizeof myhost >= INET_ADDRSTRLEN */
805
0
        Curl_printable_address(h->addr, myhost, sizeof(myhost));
806
0
        infof(data, "Name '%s' family %d resolved to '%s' family %d",
807
0
              host, af, myhost, h_af);
808
0
        Curl_dns_entry_unlink(data, &h); /* this will NULL, potential free h */
809
0
        if(af != h_af) {
810
          /* bad IP version combo, signal the caller to try another address
811
             family if available */
812
0
          result = CURLE_UNSUPPORTED_PROTOCOL;
813
0
          goto out;
814
0
        }
815
0
        done = 1;
816
0
      }
817
0
      else {
818
        /*
819
         * provided dev was no interface (or interfaces are not supported
820
         * e.g. Solaris) no ip address and no domain we fail here
821
         */
822
0
        done = -1;
823
0
      }
824
0
    }
825
826
0
    if(done > 0) {
827
0
#ifdef USE_IPV6
828
      /* IPv6 address */
829
0
      if(af == AF_INET6) {
830
0
#ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
831
0
        char *scope_ptr = strchr(myhost, '%');
832
0
        if(scope_ptr)
833
0
          *(scope_ptr++) = '\0';
834
0
#endif
835
0
        if(curlx_inet_pton(AF_INET6, myhost, &si6->sin6_addr) > 0) {
836
0
          si6->sin6_family = AF_INET6;
837
0
          si6->sin6_port = htons(port);
838
0
#ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
839
0
          if(scope_ptr) {
840
            /* The "myhost" string either comes from Curl_if2ip or from
841
               Curl_printable_address. The latter returns only numeric scope
842
               IDs and the former returns none at all. Making the scope ID,
843
               if present, known to be numeric */
844
0
            curl_off_t scope_id;
845
0
            if(curlx_str_number((const char **)CURL_UNCONST(&scope_ptr),
846
0
                                &scope_id, UINT_MAX)) {
847
0
              result = CURLE_UNSUPPORTED_PROTOCOL;
848
0
              goto out;
849
0
            }
850
0
            si6->sin6_scope_id = (unsigned int)scope_id;
851
0
          }
852
0
#endif
853
0
        }
854
0
        sizeof_sa = sizeof(struct sockaddr_in6);
855
0
      }
856
0
      else
857
0
#endif
858
      /* IPv4 address */
859
0
      if((af == AF_INET) &&
860
0
         (curlx_inet_pton(AF_INET, myhost, &si4->sin_addr) > 0)) {
861
0
        si4->sin_family = AF_INET;
862
0
        si4->sin_port = htons(port);
863
0
        sizeof_sa = sizeof(struct sockaddr_in);
864
0
      }
865
0
    }
866
867
0
    if(done < 1) {
868
      /* errorbuf is set false so failf will overwrite any message already in
869
         the error buffer, so the user receives this error message instead of a
870
         generic resolve error. */
871
0
      char buffer[STRERROR_LEN];
872
0
      data->state.errorbuf = FALSE;
873
0
      data->state.os_errno = sockerr = SOCKERRNO;
874
0
      failf(data, "Could not bind to '%s' with errno %d: %s", host,
875
0
            sockerr, curlx_strerror(sockerr, buffer, sizeof(buffer)));
876
0
      result = CURLE_INTERFACE_FAILED;
877
0
      goto out;
878
0
    }
879
0
  }
880
0
  else {
881
    /* no device was given, prepare sa to match af's needs */
882
0
#ifdef USE_IPV6
883
0
    if(af == AF_INET6) {
884
0
      si6->sin6_family = AF_INET6;
885
0
      si6->sin6_port = htons(port);
886
0
      sizeof_sa = sizeof(struct sockaddr_in6);
887
0
    }
888
0
    else
889
0
#endif
890
0
    if(af == AF_INET) {
891
0
      si4->sin_family = AF_INET;
892
0
      si4->sin_port = htons(port);
893
0
      sizeof_sa = sizeof(struct sockaddr_in);
894
0
    }
895
0
  }
896
0
#ifdef IP_BIND_ADDRESS_NO_PORT
897
0
  (void)setsockopt(sockfd, SOL_IP, IP_BIND_ADDRESS_NO_PORT, &on, sizeof(on));
898
0
#endif
899
0
  for(;;) {
900
0
    if(bind(sockfd, sock, sizeof_sa) >= 0) {
901
      /* we succeeded to bind */
902
0
      infof(data, "Local port: %hu", port);
903
0
      conn->bits.bound = TRUE;
904
0
      result = CURLE_OK;
905
0
      goto out;
906
0
    }
907
908
0
    if(--portnum > 0) {
909
0
      port++; /* try next port */
910
0
      if(port == 0)
911
0
        break;
912
0
      infof(data, "Bind to local port %d failed, trying next", port - 1);
913
      /* We reuse/clobber the port variable here below */
914
0
      if(sock->sa_family == AF_INET)
915
0
        si4->sin_port = htons(port);
916
0
#ifdef USE_IPV6
917
0
      else
918
0
        si6->sin6_port = htons(port);
919
0
#endif
920
0
    }
921
0
    else
922
0
      break;
923
0
  }
924
0
  {
925
0
    char buffer[STRERROR_LEN];
926
0
    data->state.os_errno = sockerr = SOCKERRNO;
927
0
    failf(data, "bind failed with errno %d: %s",
928
0
          sockerr, curlx_strerror(sockerr, buffer, sizeof(buffer)));
929
0
  }
930
931
0
  result = CURLE_INTERFACE_FAILED;
932
933
19.0k
out:
934
19.0k
  curlx_free(dev_in);
935
19.0k
  curlx_free(iface_in);
936
19.0k
  curlx_free(host_in);
937
19.0k
  return result;
938
0
}
939
#endif
940
941
/*
942
 * verifyconnect() returns TRUE if the connect really has happened.
943
 */
944
static bool verifyconnect(curl_socket_t sockfd, int *psockerr)
945
0
{
946
0
  bool rc = TRUE;
947
0
#ifdef SO_ERROR
948
0
  int sockerr = 0;
949
0
  curl_socklen_t errSize = sizeof(sockerr);
950
951
#ifdef _WIN32
952
  /*
953
   * In October 2003 we effectively nullified this function on Windows due to
954
   * problems with it using all CPU in multi-threaded cases.
955
   *
956
   * In May 2004, we brought it back to offer more info back on connect
957
   * failures. We could reproduce the former problems with this function, but
958
   * could avoid them by adding this SleepEx() call below:
959
   *
960
   *    "I do not have Rational Quantify, but the hint from his post was
961
   *    ntdll::NtRemoveIoCompletion(). I would assume the SleepEx (or maybe
962
   *    Sleep(0) would be enough?) would release whatever
963
   *    mutex/critical-section the ntdll call is waiting on.
964
   *
965
   *    Someone got to verify this on Win-NT 4.0, 2000."
966
   */
967
  SleepEx(0, FALSE);
968
#endif
969
970
0
  if(getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (void *)&sockerr, &errSize))
971
0
    sockerr = SOCKERRNO;
972
#if defined(EBADIOCTL) && defined(__minix)
973
  /* Minix 3.1.x does not support getsockopt on UDP sockets */
974
  if(EBADIOCTL == sockerr) {
975
    SET_SOCKERRNO(0);
976
    sockerr = 0;
977
  }
978
#endif
979
0
  if((sockerr == 0) || (SOCKEISCONN == sockerr))
980
    /* we are connected, awesome! */
981
0
    rc = TRUE;
982
0
  else
983
    /* This was not a successful connect */
984
0
    rc = FALSE;
985
0
  if(psockerr)
986
0
    *psockerr = sockerr;
987
#else
988
  (void)sockfd;
989
  if(psockerr)
990
    *psockerr = SOCKERRNO;
991
#endif
992
0
  return rc;
993
0
}
994
995
/**
996
 * Determine the curl code for a socket connect() == -1 with errno.
997
 */
998
static CURLcode socket_connect_result(struct Curl_easy *data,
999
                                      const char *ipaddress, int sockerr)
1000
0
{
1001
0
  if(sockerr == SOCKEINPROGRESS || SOCK_EAGAIN(sockerr))
1002
0
    return CURLE_OK;
1003
1004
  /* unknown error, fallthrough and try another address! */
1005
0
  {
1006
0
    VERBOSE(char buffer[STRERROR_LEN]);
1007
0
    infof(data, "Immediate connect fail for %s: %s", ipaddress,
1008
0
          curlx_strerror(sockerr, buffer, sizeof(buffer)));
1009
0
    NOVERBOSE((void)ipaddress);
1010
0
  }
1011
0
  data->state.os_errno = sockerr;
1012
  /* connect failed */
1013
0
  return CURLE_COULDNT_CONNECT;
1014
0
}
1015
1016
struct cf_socket_ctx {
1017
  struct Curl_peer *peer;
1018
  struct Curl_sockaddr_ex addr;      /* address to connect to */
1019
  curl_socket_t sock;                /* current attempt socket */
1020
  struct ip_quadruple ip;            /* The IP quadruple 2x(addr+port) */
1021
  struct curltime started_at;        /* when socket was created */
1022
  struct curltime connected_at;      /* when socket connected/got first byte */
1023
  struct curltime first_byte_at;     /* when first byte was recvd */
1024
#ifdef USE_WINSOCK
1025
  struct curltime last_sndbuf_query_at;  /* when SO_SNDBUF last queried */
1026
  ULONG sndbuf_size;                     /* the last set SO_SNDBUF size */
1027
#endif
1028
  int sockerr;                       /* socket error of last failure or 0 */
1029
#ifdef DEBUGBUILD
1030
  int connect_wait_ms;               /* ms to wait after 1st connect() */
1031
  int wblock_percent;                /* percent of writes doing EAGAIN */
1032
  int wpartial_percent;              /* percent of bytes written in send */
1033
  int rblock_percent;                /* percent of reads doing EAGAIN */
1034
  size_t recv_max;                   /* max enforced read size */
1035
#endif
1036
  uint8_t transport;
1037
  BIT(got_first_byte);               /* if first byte was received */
1038
  BIT(listening);                    /* socket is listening */
1039
  BIT(accepted);                     /* socket was accepted, not connected */
1040
  BIT(sock_connected);               /* socket is "connected", e.g. in UDP */
1041
  BIT(active);
1042
  BIT(stats_reported);
1043
};
1044
1045
static CURLcode cf_socket_ctx_init(struct cf_socket_ctx *ctx,
1046
                                   struct Curl_peer *peer,
1047
                                   struct Curl_sockaddr_ex *addr,
1048
                                   uint8_t transport)
1049
19.1k
{
1050
19.1k
  memset(ctx, 0, sizeof(*ctx));
1051
19.1k
  Curl_peer_link(&ctx->peer, peer);
1052
19.1k
  ctx->sock = CURL_SOCKET_BAD;
1053
19.1k
  ctx->transport = transport;
1054
19.1k
  ctx->addr = *addr;
1055
1056
19.1k
#ifdef DEBUGBUILD
1057
19.1k
  {
1058
19.1k
    const char *p = getenv("CURL_DBG_SOCK_WBLOCK");
1059
19.1k
    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
19.1k
    p = getenv("CURL_DBG_SOCK_WPARTIAL");
1065
19.1k
    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
19.1k
    p = getenv("CURL_DBG_SOCK_RBLOCK");
1071
19.1k
    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
19.1k
    p = getenv("CURL_DBG_SOCK_RMAX");
1077
19.1k
    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
19.1k
    p = getenv("CURL_DBG_SOCK_CONNECT_WAIT_MS");
1083
19.1k
    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
19.1k
  }
1089
19.1k
#endif
1090
1091
19.1k
  return CURLE_OK;
1092
19.1k
}
1093
1094
static void cf_socket_ctx_free(struct cf_socket_ctx *ctx)
1095
19.1k
{
1096
19.1k
  if(ctx) {
1097
19.1k
    Curl_peer_unlink(&ctx->peer);
1098
19.1k
    curlx_free(ctx);
1099
19.1k
  }
1100
19.1k
}
1101
1102
static CURLcode cf_socket_shutdown(struct Curl_cfilter *cf,
1103
                                   struct Curl_easy *data,
1104
                                   bool *done)
1105
9.86k
{
1106
9.86k
  if(cf->connected) {
1107
9.86k
    struct cf_socket_ctx *ctx = cf->ctx;
1108
1109
9.86k
    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
9.86k
    if(ctx->sock != CURL_SOCKET_BAD &&
1114
9.86k
       ctx->transport == TRNSPRT_TCP &&
1115
9.86k
       (curlx_nonblock(ctx->sock, TRUE) >= 0)) {
1116
9.86k
      unsigned char buf[1024];
1117
9.86k
      (void)sread(ctx->sock, buf, sizeof(buf));
1118
9.86k
    }
1119
9.86k
  }
1120
9.86k
  *done = TRUE;
1121
9.86k
  return CURLE_OK;
1122
9.86k
}
1123
1124
static void cf_socket_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
1125
19.1k
{
1126
19.1k
  struct cf_socket_ctx *ctx = cf->ctx;
1127
1128
19.1k
  CURL_TRC_CF(data, cf, "destroy");
1129
19.1k
  if(ctx) {
1130
19.1k
    if(ctx->sock != CURL_SOCKET_BAD) {
1131
19.0k
      CURL_TRC_CF(data, cf, "cf_socket_close, fd=%" FMT_SOCKET_T, ctx->sock);
1132
19.0k
      if(ctx->sock == cf->conn->sock[cf->sockindex])
1133
19.0k
        cf->conn->sock[cf->sockindex] = CURL_SOCKET_BAD;
1134
19.0k
      socket_close(data, cf->conn, !ctx->accepted, ctx->sock);
1135
19.0k
    }
1136
19.1k
    cf_socket_ctx_free(ctx);
1137
19.1k
  }
1138
19.1k
}
1139
1140
static void set_local_ip(struct Curl_cfilter *cf,
1141
                         struct Curl_easy *data)
1142
38.2k
{
1143
38.2k
  struct cf_socket_ctx *ctx = cf->ctx;
1144
38.2k
  ctx->ip.local_ip[0] = 0;
1145
38.2k
  ctx->ip.local_port = 0;
1146
1147
38.2k
#ifdef HAVE_GETSOCKNAME
1148
38.2k
  if((ctx->sock != CURL_SOCKET_BAD) &&
1149
38.1k
     !(data->conn->scheme->protocol & CURLPROTO_TFTP)) {
1150
    /* TFTP does not connect, so it cannot get the IP like this */
1151
38.1k
    struct Curl_sockaddr_storage ssloc;
1152
38.1k
    curl_socklen_t slen = sizeof(struct Curl_sockaddr_storage);
1153
1154
38.1k
    memset(&ssloc, 0, sizeof(ssloc));
1155
38.1k
    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
38.1k
    else {
1162
38.1k
      CURLcode result = sockaddr2string((struct sockaddr *)&ssloc, slen,
1163
38.1k
                                        ctx->ip.local_ip, &ctx->ip.local_port);
1164
38.1k
      if(result)
1165
0
        infof(data, "ssloc inet_ntop() failed with %d", (int)result);
1166
38.1k
    }
1167
38.1k
  }
1168
#else
1169
  (void)data;
1170
#endif
1171
38.2k
}
1172
1173
static CURLcode set_remote_ip(struct Curl_cfilter *cf,
1174
                              struct Curl_easy *data)
1175
19.0k
{
1176
19.0k
  struct cf_socket_ctx *ctx = cf->ctx;
1177
19.0k
  CURLcode result;
1178
1179
  /* store remote address and port used in this connection attempt */
1180
19.0k
  ctx->ip.transport = ctx->transport;
1181
19.0k
  result = sockaddr2string(&ctx->addr.curl_sa_addr,
1182
19.0k
                           (curl_socklen_t)ctx->addr.addrlen,
1183
19.0k
                           ctx->ip.remote_ip, &ctx->ip.remote_port);
1184
19.0k
  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
19.0k
  return CURLE_OK;
1191
19.0k
}
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
38.1k
{
1197
38.1k
#ifdef SOCK_CLOEXEC
1198
38.1k
  x &= ~SOCK_CLOEXEC;
1199
38.1k
#endif
1200
38.1k
#ifdef CURL_USE_SOCK_NONBLOCK
1201
38.1k
  x &= ~SOCK_NONBLOCK;
1202
38.1k
#endif
1203
38.1k
  return x;
1204
38.1k
}
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
19.1k
{
1259
19.1k
  struct cf_socket_ctx *ctx = cf->ctx;
1260
19.1k
  int error = 0;
1261
19.1k
  bool isconnected = FALSE;
1262
19.1k
  CURLcode result = CURLE_COULDNT_CONNECT;
1263
19.1k
  bool is_tcp;
1264
1265
19.1k
  DEBUGASSERT(ctx->sock == CURL_SOCKET_BAD);
1266
19.1k
  ctx->started_at = *Curl_pgrs_now(data);
1267
19.1k
#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
19.1k
  if(!data->set.fopensocket)
1273
0
    ctx->addr.socktype |= SOCK_NONBLOCK;
1274
19.1k
#endif
1275
19.1k
  result = socket_open(data, &ctx->addr, &ctx->sock);
1276
19.1k
#ifdef CURL_USE_SOCK_NONBLOCK
1277
  /* Restore the socktype after the socket is created. */
1278
19.1k
  if(!data->set.fopensocket)
1279
0
    ctx->addr.socktype &= ~SOCK_NONBLOCK;
1280
19.1k
#endif
1281
19.1k
  if(result)
1282
90
    goto out;
1283
1284
19.0k
  result = set_remote_ip(cf, data);
1285
19.0k
  if(result)
1286
0
    goto out;
1287
1288
19.0k
#ifdef USE_IPV6
1289
19.0k
  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
100
#ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
1303
100
    {
1304
100
      struct sockaddr_in6 *sa6 = (void *)&ctx->addr.curl_sa_addr;
1305
100
      if(sa6->sin6_scope_id)
1306
10
        infof(data, "  Trying [%s]:%d scope_id=%lu...",
1307
100
              ctx->ip.remote_ip, ctx->ip.remote_port,
1308
100
              (unsigned long)sa6->sin6_scope_id);
1309
90
      else
1310
90
#endif
1311
90
        infof(data, "  Trying [%s]:%d...",
1312
100
              ctx->ip.remote_ip, ctx->ip.remote_port);
1313
100
#ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
1314
100
    }
1315
100
#endif
1316
100
  }
1317
18.9k
  else
1318
18.9k
#endif
1319
18.9k
    infof(data, "  Trying %s:%d...", ctx->ip.remote_ip, ctx->ip.remote_port);
1320
1321
19.0k
#ifdef USE_IPV6
1322
19.0k
  is_tcp = (ctx->addr.family == AF_INET ||
1323
100
            ctx->addr.family == AF_INET6) &&
1324
19.0k
    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
19.0k
  if(is_tcp) {
1330
19.0k
    if(data->set.tcp_nodelay)
1331
19.0k
      tcpnodelay(cf, data, ctx->sock);
1332
1333
19.0k
    if(data->set.tcp_keepalive)
1334
0
      tcpkeepalive(cf, data, ctx->sock);
1335
1336
19.0k
    tcplocalhost(cf, ctx->sock);
1337
19.0k
  }
1338
1339
19.0k
  if(data->set.fsockopt) {
1340
    /* activate callback for setting socket options */
1341
19.0k
    struct Curl_mapi_guard guard;
1342
19.0k
    CURL_CBAPI_START(&guard, data, easy_fsockopt);
1343
19.0k
    error = data->set.fsockopt(data->set.sockopt_client,
1344
19.0k
                               ctx->sock,
1345
19.0k
                               CURLSOCKTYPE_IPCXN);
1346
19.0k
    CURL_CBAPI_END(&guard);
1347
1348
19.0k
    if(error == CURL_SOCKOPT_ALREADY_CONNECTED)
1349
19.0k
      isconnected = TRUE;
1350
0
    else if(error) {
1351
0
      result = CURLE_ABORTED_BY_CALLBACK;
1352
0
      goto out;
1353
0
    }
1354
19.0k
  }
1355
1356
19.0k
#ifndef CURL_DISABLE_BINDLOCAL
1357
  /* possibly bind the local end to an IP, interface or port */
1358
19.0k
  if(ctx->addr.family == AF_INET
1359
100
#ifdef USE_IPV6
1360
100
     || ctx->addr.family == AF_INET6
1361
19.0k
#endif
1362
19.0k
    ) {
1363
19.0k
    result = bindlocal(data, cf->conn, ctx->sock, ctx->addr.family,
1364
19.0k
                       Curl_ipv6_scope(&ctx->addr.curl_sa_addr),
1365
19.0k
                       ctx->transport);
1366
19.0k
    if(result) {
1367
0
      if(result == CURLE_UNSUPPORTED_PROTOCOL) {
1368
        /* The address family is not supported on this interface.
1369
           We can continue trying addresses */
1370
0
        result = CURLE_COULDNT_CONNECT;
1371
0
      }
1372
0
      goto out;
1373
0
    }
1374
19.0k
  }
1375
19.0k
#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
19.0k
  if(data->set.fopensocket) {
1388
    /* Set socket non-blocking, must be a non-blocking socket for
1389
     * a non-blocking connect. */
1390
19.0k
    error = curlx_nonblock(ctx->sock, TRUE);
1391
19.0k
    if(error < 0) {
1392
0
      result = CURLE_UNSUPPORTED_PROTOCOL;
1393
0
      ctx->sockerr = SOCKERRNO;
1394
0
      goto out;
1395
0
    }
1396
19.0k
  }
1397
19.0k
#endif
1398
19.0k
  ctx->sock_connected = (cf_socktype(ctx->addr.socktype) != SOCK_DGRAM);
1399
19.1k
out:
1400
19.1k
  if(result) {
1401
90
    if(ctx->sock != CURL_SOCKET_BAD) {
1402
0
      socket_close(data, cf->conn, TRUE, ctx->sock);
1403
0
      ctx->sock = CURL_SOCKET_BAD;
1404
0
    }
1405
90
  }
1406
19.0k
  else if(isconnected) {
1407
19.0k
    set_local_ip(cf, data);
1408
19.0k
    ctx->connected_at = *Curl_pgrs_now(data);
1409
19.0k
    cf->connected = TRUE;
1410
19.0k
  }
1411
19.1k
  CURL_TRC_CF(data, cf, "cf_socket_open() -> %d, fd=%" FMT_SOCKET_T,
1412
19.1k
              (int)result, ctx->sock);
1413
19.1k
  return result;
1414
19.0k
}
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
19.1k
{
1475
19.1k
  struct cf_socket_ctx *ctx = cf->ctx;
1476
19.1k
  CURLcode result = CURLE_COULDNT_CONNECT;
1477
19.1k
  int rc = 0;
1478
1479
19.1k
  if(cf->connected) {
1480
0
    *done = TRUE;
1481
0
    return CURLE_OK;
1482
0
  }
1483
1484
19.1k
  *done = FALSE; /* a negative world view is best */
1485
19.1k
  if(ctx->sock == CURL_SOCKET_BAD) {
1486
19.1k
    int sockerr;
1487
1488
19.1k
    result = cf_socket_open(cf, data);
1489
19.1k
    if(result)
1490
90
      goto out;
1491
1492
19.0k
    if(cf->connected) {
1493
19.0k
      *done = TRUE;
1494
19.0k
      return CURLE_OK;
1495
19.0k
    }
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
90
out:
1556
90
  if(result) {
1557
90
    VERBOSE(char buffer[STRERROR_LEN]);
1558
90
    set_local_ip(cf, data);
1559
90
    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
90
    else {
1565
90
      VERBOSE(curlx_strcopy(buffer, sizeof(buffer), STRCONST("peer closed")));
1566
90
    }
1567
90
    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
90
    infof(data, "connect to %s port %u from %s port %d failed: %s",
1572
90
          ctx->ip.remote_ip, ctx->ip.remote_port,
1573
90
          ctx->ip.local_ip, ctx->ip.local_port,
1574
90
          curlx_strerror(ctx->sockerr, buffer, sizeof(buffer)));
1575
90
    *done = FALSE;
1576
90
  }
1577
90
  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
0
{
1584
0
  struct cf_socket_ctx *ctx = cf->ctx;
1585
0
  CURLcode result = CURLE_OK;
1586
1587
0
  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
0
    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
0
    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
0
    else if(!ctx->active) {
1603
0
      result = Curl_pollset_add_in(data, ps, ctx->sock);
1604
0
      CURL_TRC_CF(data, cf, "adjust_pollset, !active, POLLIN fd=%"
1605
0
                  FMT_SOCKET_T, ctx->sock);
1606
0
    }
1607
0
  }
1608
0
  return result;
1609
0
}
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
30.4k
{
1643
30.4k
  struct cf_socket_ctx *ctx = cf->ctx;
1644
30.4k
  curl_socket_t fdsave;
1645
30.4k
  ssize_t rv;
1646
30.4k
  CURLcode result = CURLE_OK;
1647
30.4k
  VERBOSE(size_t orig_len = len);
1648
1649
30.4k
  (void)eos;
1650
30.4k
  *pnwritten = 0;
1651
30.4k
  fdsave = cf->conn->sock[cf->sockindex];
1652
30.4k
  cf->conn->sock[cf->sockindex] = ctx->sock;
1653
1654
30.4k
#ifdef DEBUGBUILD
1655
  /* simulate network blocking/partial writes */
1656
30.4k
  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
30.4k
  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
30.4k
#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
30.4k
    rv = swrite(ctx->sock, buf, len);
1683
1684
30.4k
  if(!curlx_sztouz(rv, pnwritten)) {
1685
0
    int sockerr = SOCKERRNO;
1686
0
    if(SOCK_EAGAIN(sockerr)
1687
0
#ifndef USE_WINSOCK
1688
0
       || (sockerr == SOCKEINTR) || (sockerr == SOCKEINPROGRESS)
1689
0
#endif
1690
0
      ) {
1691
0
      result = CURLE_AGAIN;  /* EWOULDBLOCK */
1692
0
    }
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
0
  }
1701
1702
#ifdef USE_WINSOCK
1703
  if(!result)
1704
    win_update_sndbuf_size(data, ctx);
1705
#endif
1706
1707
30.4k
  CURL_TRC_CF(data, cf, "send(len=%zu) -> %d, %zu",
1708
30.4k
              orig_len, (int)result, *pnwritten);
1709
30.4k
  cf->conn->sock[cf->sockindex] = fdsave;
1710
30.4k
  return result;
1711
30.4k
}
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
155k
{
1716
155k
  struct cf_socket_ctx *ctx = cf->ctx;
1717
155k
  CURLcode result = CURLE_OK;
1718
155k
  ssize_t rv;
1719
1720
155k
  *pnread = 0;
1721
155k
#ifdef DEBUGBUILD
1722
  /* simulate network blocking/partial reads */
1723
155k
  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
155k
  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
155k
#endif
1737
1738
155k
  rv = sread(ctx->sock, buf, len);
1739
1740
155k
  if(!curlx_sztouz(rv, pnread)) {
1741
66.1k
    int sockerr = SOCKERRNO;
1742
66.1k
    if(SOCK_EAGAIN(sockerr)
1743
0
#ifndef USE_WINSOCK
1744
0
       || (sockerr == SOCKEINTR)
1745
66.1k
#endif
1746
66.1k
      ) {
1747
66.1k
      result = CURLE_AGAIN;  /* EWOULDBLOCK */
1748
66.1k
    }
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
66.1k
  }
1757
1758
155k
  CURL_TRC_CF(data, cf, "recv(len=%zu) -> %d, %zu", len, (int)result, *pnread);
1759
155k
  if(!result && !ctx->got_first_byte) {
1760
19.0k
    ctx->first_byte_at = *Curl_pgrs_now(data);
1761
19.0k
    ctx->got_first_byte = TRUE;
1762
19.0k
  }
1763
155k
  return result;
1764
155k
}
1765
1766
static void cf_socket_update_data(struct Curl_cfilter *cf,
1767
                                  struct Curl_easy *data)
1768
19.0k
{
1769
  /* Update the IP info held in the transfer, if we have that. */
1770
19.0k
  if(cf->connected && (cf->sockindex == FIRSTSOCKET)) {
1771
19.0k
    struct cf_socket_ctx *ctx = cf->ctx;
1772
19.0k
    data->info.primary = ctx->ip;
1773
19.0k
  }
1774
19.0k
}
1775
1776
static void cf_socket_active(struct Curl_cfilter *cf, struct Curl_easy *data)
1777
19.0k
{
1778
19.0k
  struct cf_socket_ctx *ctx = cf->ctx;
1779
1780
  /* use this socket from now on */
1781
19.0k
  cf->conn->sock[cf->sockindex] = ctx->sock;
1782
19.0k
  set_local_ip(cf, data);
1783
19.0k
#ifdef USE_IPV6
1784
19.0k
  if(cf->sockindex == FIRSTSOCKET)
1785
19.0k
    cf->conn->bits.ipv6 = (ctx->addr.family == AF_INET6);
1786
19.0k
#endif
1787
19.0k
  ctx->active = TRUE;
1788
19.0k
}
1789
1790
static CURLcode cf_socket_cntrl(struct Curl_cfilter *cf,
1791
                                struct Curl_easy *data,
1792
                                int event, int arg1, void *arg2)
1793
76.3k
{
1794
76.3k
  struct cf_socket_ctx *ctx = cf->ctx;
1795
1796
76.3k
  (void)arg1;
1797
76.3k
  (void)arg2;
1798
76.3k
  switch(event) {
1799
19.0k
  case CF_CTRL_CONN_INFO_UPDATE:
1800
19.0k
    cf_socket_active(cf, data);
1801
19.0k
    cf_socket_update_data(cf, data);
1802
19.0k
    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
19.0k
  case CF_CTRL_REPORT_STATS:
1810
19.0k
    if(cf->connected && !ctx->stats_reported) {
1811
19.0k
      struct curltime *ts = NULL;
1812
19.0k
      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
19.0k
      default:
1821
19.0k
        ts = &ctx->connected_at;
1822
19.0k
        break;
1823
19.0k
      }
1824
19.0k
      if(ts) {
1825
19.0k
        Curl_pgrsTimeWas(data, TIMER_CONNECT, *ts);
1826
19.0k
        ctx->stats_reported = TRUE;
1827
19.0k
      }
1828
19.0k
    }
1829
76.3k
  }
1830
76.3k
  return CURLE_OK;
1831
76.3k
}
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
102k
{
1874
102k
  struct cf_socket_ctx *ctx = cf->ctx;
1875
1876
102k
  switch(query) {
1877
0
  case CF_QUERY_SOCKET:
1878
0
    DEBUGASSERT(pres2);
1879
0
    *((curl_socket_t *)pres2) = ctx->sock;
1880
0
    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
711
  case CF_QUERY_IP_INFO:
1900
711
#ifdef USE_IPV6
1901
711
    *pres1 = (ctx->addr.family == AF_INET6);
1902
#else
1903
    *pres1 = FALSE;
1904
#endif
1905
711
    *(struct ip_quadruple *)pres2 = ctx->ip;
1906
711
    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
102k
  default:
1914
102k
    break;
1915
102k
  }
1916
102k
  return cf->next ?
1917
0
    cf->next->cft->query(cf->next, data, query, pres1, pres2) :
1918
102k
    CURLE_UNKNOWN_OPTION;
1919
102k
}
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
19.1k
{
1948
19.1k
  struct cf_socket_ctx *ctx = NULL;
1949
19.1k
  struct Curl_cfilter *cf = NULL;
1950
19.1k
  CURLcode result;
1951
1952
19.1k
  (void)data;
1953
19.1k
  (void)origin;
1954
19.1k
  (void)conn;
1955
19.1k
  (void)tunnel_peer;
1956
19.1k
  (void)tunnel_transport;
1957
19.1k
  DEBUGASSERT(transport_peer == TRNSPRT_TCP);
1958
19.1k
  if(!addr) {
1959
0
    result = CURLE_BAD_FUNCTION_ARGUMENT;
1960
0
    goto out;
1961
0
  }
1962
1963
19.1k
  ctx = curlx_calloc(1, sizeof(*ctx));
1964
19.1k
  if(!ctx) {
1965
5
    result = CURLE_OUT_OF_MEMORY;
1966
5
    goto out;
1967
5
  }
1968
1969
19.1k
  result = cf_socket_ctx_init(ctx, peer, addr, transport_peer);
1970
19.1k
  if(result)
1971
0
    goto out;
1972
1973
19.1k
  result = Curl_cf_create(&cf, &Curl_cft_tcp, ctx);
1974
1975
19.1k
out:
1976
19.1k
  *pcf = (!result) ? cf : NULL;
1977
19.1k
  if(result) {
1978
10
    curlx_safefree(cf);
1979
10
    cf_socket_ctx_free(ctx);
1980
10
  }
1981
1982
19.1k
  return result;
1983
19.1k
}
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
0
{
2224
0
  struct cf_socket_ctx *ctx = NULL;
2225
0
  struct Curl_cfilter *cf = NULL;
2226
0
  CURLcode result;
2227
2228
0
  (void)data;
2229
0
  (void)origin;
2230
0
  (void)conn;
2231
0
  (void)tunnel_peer;
2232
0
  (void)tunnel_transport;
2233
0
  DEBUGASSERT(transport_peer == TRNSPRT_UNIX);
2234
0
  ctx = curlx_calloc(1, sizeof(*ctx));
2235
0
  if(!ctx) {
2236
0
    result = CURLE_OUT_OF_MEMORY;
2237
0
    goto out;
2238
0
  }
2239
2240
0
  result = cf_socket_ctx_init(ctx, peer, addr, transport_peer);
2241
0
  if(result)
2242
0
    goto out;
2243
2244
0
  result = Curl_cf_create(&cf, &Curl_cft_unix, ctx);
2245
2246
0
out:
2247
0
  *pcf = (!result) ? cf : NULL;
2248
0
  if(result) {
2249
0
    curlx_safefree(cf);
2250
0
    cf_socket_ctx_free(ctx);
2251
0
  }
2252
2253
0
  return result;
2254
0
}
2255
2256
static timediff_t cf_tcp_accept_timeleft(struct Curl_cfilter *cf,
2257
                                         struct Curl_easy *data)
2258
0
{
2259
0
  struct cf_socket_ctx *ctx = cf->ctx;
2260
0
  timediff_t timeout_ms = DEFAULT_ACCEPT_TIMEOUT;
2261
0
  timediff_t other_ms;
2262
2263
0
#ifndef CURL_DISABLE_FTP
2264
0
  if(data->set.accepttimeout > 0)
2265
0
    timeout_ms = data->set.accepttimeout;
2266
0
#endif
2267
2268
  /* check if the generic timeout possibly is set shorter */
2269
0
  other_ms = Curl_timeleft_ms(data);
2270
0
  if(other_ms && (other_ms < timeout_ms))
2271
    /* note that this also works fine for when other_ms happens to be negative
2272
       due to it already having elapsed */
2273
0
    timeout_ms = other_ms;
2274
0
  else {
2275
    /* subtract elapsed time */
2276
0
    timeout_ms -= curlx_ptimediff_ms(Curl_pgrs_now(data), &ctx->started_at);
2277
0
    if(!timeout_ms)
2278
      /* avoid returning 0 as that means no timeout! */
2279
0
      timeout_ms = -1;
2280
0
  }
2281
0
  return timeout_ms;
2282
0
}
2283
2284
static void cf_tcp_set_accepted_remote_ip(struct Curl_cfilter *cf,
2285
                                          struct Curl_easy *data)
2286
0
{
2287
0
  struct cf_socket_ctx *ctx = cf->ctx;
2288
0
#ifdef HAVE_GETPEERNAME
2289
0
  CURLcode result;
2290
0
  struct Curl_sockaddr_storage ssrem;
2291
0
  curl_socklen_t plen;
2292
2293
0
  ctx->ip.remote_ip[0] = 0;
2294
0
  ctx->ip.remote_port = 0;
2295
0
  plen = sizeof(ssrem);
2296
0
  memset(&ssrem, 0, plen);
2297
0
  if(getpeername(ctx->sock, (struct sockaddr *)&ssrem, &plen)) {
2298
0
    char buffer[STRERROR_LEN];
2299
0
    int sockerr = SOCKERRNO;
2300
0
    failf(data, "getpeername() failed with errno %d: %s",
2301
0
          sockerr, curlx_strerror(sockerr, buffer, sizeof(buffer)));
2302
0
    return;
2303
0
  }
2304
0
  result = sockaddr2string((struct sockaddr *)&ssrem, plen,
2305
0
                            ctx->ip.remote_ip, &ctx->ip.remote_port);
2306
0
  if(result) {
2307
0
    failf(data, "ssrem inet_ntop() failed with %d", (int)result);
2308
0
    return;
2309
0
  }
2310
#else
2311
  ctx->ip.remote_ip[0] = 0;
2312
  ctx->ip.remote_port = 0;
2313
  (void)data;
2314
#endif
2315
0
}
2316
2317
static CURLcode cf_tcp_accept_connect(struct Curl_cfilter *cf,
2318
                                      struct Curl_easy *data,
2319
                                      bool *done)
2320
0
{
2321
0
  struct cf_socket_ctx *ctx = cf->ctx;
2322
0
  char errbuf[STRERROR_LEN];
2323
0
#ifdef USE_IPV6
2324
0
  struct Curl_sockaddr_storage add;
2325
#else
2326
  struct sockaddr_in add;
2327
#endif
2328
0
  curl_socklen_t size = (curl_socklen_t)sizeof(add);
2329
0
  curl_socket_t s_accepted = CURL_SOCKET_BAD;
2330
0
  timediff_t timeout_ms;
2331
0
  int socketstate = 0;
2332
0
  bool incoming = FALSE;
2333
2334
  /* we start accepted, if we ever close, we cannot go on */
2335
0
  (void)data;
2336
0
  if(cf->connected) {
2337
0
    *done = TRUE;
2338
0
    return CURLE_OK;
2339
0
  }
2340
2341
0
  *done = FALSE;
2342
0
  timeout_ms = cf_tcp_accept_timeleft(cf, data);
2343
0
  if(timeout_ms < 0) {
2344
    /* if a timeout was already reached, bail out */
2345
0
    failf(data, "Accept timeout occurred while waiting server connect");
2346
0
    return CURLE_FTP_ACCEPT_TIMEOUT;
2347
0
  }
2348
2349
0
  CURL_TRC_CF(data, cf, "Checking for incoming on fd=%" FMT_SOCKET_T
2350
0
              " ip=%s:%d", ctx->sock, ctx->ip.local_ip, ctx->ip.local_port);
2351
0
  socketstate = SOCKET_READABLE(ctx->sock, 0);
2352
0
  CURL_TRC_CF(data, cf, "socket_check -> %x", (unsigned int)socketstate);
2353
0
  switch(socketstate) {
2354
0
  case -1: /* error */
2355
    /* let's die here */
2356
0
    failf(data, "Error while waiting for server connect");
2357
0
    return CURLE_FTP_ACCEPT_FAILED;
2358
0
  default:
2359
0
    if(socketstate & CURL_CSELECT_IN) {
2360
0
      infof(data, "Ready to accept data connection from server");
2361
0
      incoming = TRUE;
2362
0
    }
2363
0
    break;
2364
0
  }
2365
2366
0
  if(!incoming) {
2367
0
    CURL_TRC_CF(data, cf, "nothing heard from the server yet");
2368
0
    return CURLE_OK;
2369
0
  }
2370
2371
0
  size = sizeof(add);
2372
0
#ifdef HAVE_ACCEPT4
2373
0
  s_accepted = CURL_ACCEPT4(ctx->sock, (struct sockaddr *)&add, &size,
2374
0
                            SOCK_NONBLOCK | SOCK_CLOEXEC);
2375
#else
2376
  s_accepted = CURL_ACCEPT(ctx->sock, (struct sockaddr *)&add, &size);
2377
#endif
2378
2379
0
  if(s_accepted == CURL_SOCKET_BAD) {
2380
0
    failf(data, "Error accept()ing server connect: %s",
2381
0
          curlx_strerror(SOCKERRNO, errbuf, sizeof(errbuf)));
2382
0
    return CURLE_FTP_ACCEPT_FAILED;
2383
0
  }
2384
#ifndef HAVE_ACCEPT4
2385
#ifdef HAVE_FCNTL
2386
  if(fcntl(s_accepted, F_SETFD, FD_CLOEXEC) < 0) {
2387
    failf(data, "fcntl set CLOEXEC: %s",
2388
          curlx_strerror(SOCKERRNO, errbuf, sizeof(errbuf)));
2389
    Curl_socket_close(data, cf->conn, s_accepted);
2390
    return CURLE_FTP_ACCEPT_FAILED;
2391
  }
2392
#endif /* HAVE_FCNTL */
2393
  if(curlx_nonblock(s_accepted, TRUE) < 0) {
2394
    failf(data, "set socket NONBLOCK: %s",
2395
          curlx_strerror(SOCKERRNO, errbuf, sizeof(errbuf)));
2396
    Curl_socket_close(data, cf->conn, s_accepted);
2397
    return CURLE_FTP_ACCEPT_FAILED;
2398
  }
2399
#endif /* !HAVE_ACCEPT4 */
2400
0
  infof(data, "Connection accepted from server");
2401
2402
  /* Replace any filter on SECONDARY with one listening on this socket */
2403
0
  ctx->listening = FALSE;
2404
0
  ctx->accepted = TRUE;
2405
0
  socket_close(data, cf->conn, TRUE, ctx->sock);
2406
0
  ctx->sock = s_accepted;
2407
2408
0
  cf->conn->sock[cf->sockindex] = ctx->sock;
2409
0
  cf_tcp_set_accepted_remote_ip(cf, data);
2410
0
  set_local_ip(cf, data);
2411
0
  ctx->active = TRUE;
2412
0
  ctx->connected_at = *Curl_pgrs_now(data);
2413
0
  cf->connected = TRUE;
2414
0
  CURL_TRC_CF(data, cf, "accepted_set(sock=%" FMT_SOCKET_T
2415
0
              ", remote=%s port=%d)",
2416
0
              ctx->sock, ctx->ip.remote_ip, ctx->ip.remote_port);
2417
2418
0
  if(data->set.fsockopt) {
2419
0
    struct Curl_mapi_guard guard;
2420
0
    int error = 0;
2421
2422
    /* activate callback for setting socket options */
2423
0
    CURL_CBAPI_START(&guard, data, easy_fsockopt);
2424
0
    error = data->set.fsockopt(data->set.sockopt_client,
2425
0
                               ctx->sock, CURLSOCKTYPE_ACCEPT);
2426
0
    CURL_CBAPI_END(&guard);
2427
2428
0
    if(error)
2429
0
      return CURLE_ABORTED_BY_CALLBACK;
2430
0
  }
2431
0
  *done = TRUE;
2432
0
  return CURLE_OK;
2433
0
}
2434
2435
struct Curl_cftype Curl_cft_tcp_accept = {
2436
  "TCP-ACCEPT",
2437
  CF_TYPE_IP_CONNECT,
2438
  CURL_LOG_LVL_NONE,
2439
  cf_socket_destroy,
2440
  cf_tcp_accept_connect,
2441
  cf_socket_shutdown,
2442
  cf_socket_adjust_pollset,
2443
  Curl_cf_def_data_pending,
2444
  cf_socket_send,
2445
  cf_socket_recv,
2446
  cf_socket_cntrl,
2447
  cf_socket_conn_is_alive,
2448
  Curl_cf_def_conn_keep_alive,
2449
  cf_socket_query,
2450
};
2451
2452
CURLcode Curl_conn_tcp_listen_set(struct Curl_easy *data,
2453
                                  struct connectdata *conn,
2454
                                  int8_t sockindex, curl_socket_t *s)
2455
0
{
2456
0
  CURLcode result;
2457
0
  struct Curl_cfilter *cf = NULL;
2458
0
  struct cf_socket_ctx *ctx = NULL;
2459
2460
  /* replace any existing */
2461
0
  Curl_conn_cf_discard_all(data, conn, sockindex);
2462
0
  DEBUGASSERT(conn->sock[sockindex] == CURL_SOCKET_BAD);
2463
2464
0
  ctx = curlx_calloc(1, sizeof(*ctx));
2465
0
  if(!ctx) {
2466
0
    result = CURLE_OUT_OF_MEMORY;
2467
0
    goto out;
2468
0
  }
2469
0
  ctx->transport = TRNSPRT_TCP;
2470
0
  ctx->sock = *s;
2471
0
  ctx->listening = TRUE;
2472
0
  ctx->accepted = FALSE;
2473
0
  result = Curl_cf_create(&cf, &Curl_cft_tcp_accept, ctx);
2474
0
  if(result)
2475
0
    goto out;
2476
0
  Curl_conn_cf_add(data, conn, sockindex, cf);
2477
2478
0
  ctx->started_at = *Curl_pgrs_now(data);
2479
0
  conn->sock[sockindex] = ctx->sock;
2480
0
  set_local_ip(cf, data);
2481
0
  CURL_TRC_CF(data, cf, "set filter for listen socket fd=%" FMT_SOCKET_T
2482
0
              " ip=%s:%d", ctx->sock,
2483
0
              ctx->ip.local_ip, ctx->ip.local_port);
2484
2485
0
out:
2486
0
  if(result) {
2487
0
    curlx_safefree(cf);
2488
0
    curlx_safefree(ctx);
2489
0
  }
2490
0
  return result;
2491
0
}
2492
2493
bool Curl_conn_is_tcp_listen(struct Curl_easy *data,
2494
                             int8_t sockindex)
2495
0
{
2496
0
  struct Curl_cfilter *cf = data->conn->cfilter[sockindex];
2497
0
  while(cf) {
2498
0
    if(cf->cft == &Curl_cft_tcp_accept)
2499
0
      return TRUE;
2500
0
    cf = cf->next;
2501
0
  }
2502
0
  return FALSE;
2503
0
}
2504
2505
/**
2506
 * Return TRUE iff `cf` is a socket filter.
2507
 */
2508
static bool cf_is_socket(struct Curl_cfilter *cf)
2509
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
}