Coverage Report

Created: 2023-06-07 07:02

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