Coverage Report

Created: 2026-09-14 07:05

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