Coverage Report

Created: 2026-09-01 06:58

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