Coverage Report

Created: 2026-10-15 06:18

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