Coverage Report

Created: 2026-08-13 07:18

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