Coverage Report

Created: 2026-10-15 06:17

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