Coverage Report

Created: 2026-09-01 06:59

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