Coverage Report

Created: 2023-12-08 06:48

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