Coverage Report

Created: 2026-10-22 06:48

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