Coverage Report

Created: 2026-06-15 07:03

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