Coverage Report

Created: 2026-09-14 07:08

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