Coverage Report

Created: 2025-07-11 06:57

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