Coverage Report

Created: 2022-10-16 06:45

/src/curl/lib/connect.c
Line
Count
Source (jump to first uncovered line)
1
/***************************************************************************
2
 *                                  _   _ ____  _
3
 *  Project                     ___| | | |  _ \| |
4
 *                             / __| | | | |_) | |
5
 *                            | (__| |_| |  _ <| |___
6
 *                             \___|\___/|_| \_\_____|
7
 *
8
 * Copyright (C) 1998 - 2022, 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
#if (defined(HAVE_IOCTL_FIONBIO) && defined(NETWARE))
52
#include <sys/filio.h>
53
#endif
54
#ifdef NETWARE
55
#undef in_addr_t
56
#define in_addr_t unsigned long
57
#endif
58
#ifdef __VMS
59
#include <in.h>
60
#include <inet.h>
61
#endif
62
63
#include "urldata.h"
64
#include "sendf.h"
65
#include "if2ip.h"
66
#include "strerror.h"
67
#include "connect.h"
68
#include "select.h"
69
#include "url.h" /* for Curl_safefree() */
70
#include "multiif.h"
71
#include "sockaddr.h" /* required for Curl_sockaddr_storage */
72
#include "inet_ntop.h"
73
#include "inet_pton.h"
74
#include "vtls/vtls.h" /* for Curl_ssl_check_cxn() */
75
#include "progress.h"
76
#include "warnless.h"
77
#include "conncache.h"
78
#include "multihandle.h"
79
#include "share.h"
80
#include "version_win32.h"
81
#include "quic.h"
82
#include "socks.h"
83
84
/* The last 3 #include files should be in this order */
85
#include "curl_printf.h"
86
#include "curl_memory.h"
87
#include "memdebug.h"
88
89
static bool verifyconnect(curl_socket_t sockfd, int *error);
90
91
#if defined(__DragonFly__) || defined(HAVE_WINSOCK2_H)
92
/* DragonFlyBSD and Windows use millisecond units */
93
#define KEEPALIVE_FACTOR(x) (x *= 1000)
94
#else
95
#define KEEPALIVE_FACTOR(x)
96
#endif
97
98
#if defined(HAVE_WINSOCK2_H) && !defined(SIO_KEEPALIVE_VALS)
99
#define SIO_KEEPALIVE_VALS    _WSAIOW(IOC_VENDOR,4)
100
101
struct tcp_keepalive {
102
  u_long onoff;
103
  u_long keepalivetime;
104
  u_long keepaliveinterval;
105
};
106
#endif
107
108
static void
109
tcpkeepalive(struct Curl_easy *data,
110
             curl_socket_t sockfd)
111
0
{
112
0
  int optval = data->set.tcp_keepalive?1:0;
113
114
  /* only set IDLE and INTVL if setting KEEPALIVE is successful */
115
0
  if(setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE,
116
0
        (void *)&optval, sizeof(optval)) < 0) {
117
0
    infof(data, "Failed to set SO_KEEPALIVE on fd %d", sockfd);
118
0
  }
119
0
  else {
120
#if defined(SIO_KEEPALIVE_VALS)
121
    struct tcp_keepalive vals;
122
    DWORD dummy;
123
    vals.onoff = 1;
124
    optval = curlx_sltosi(data->set.tcp_keepidle);
125
    KEEPALIVE_FACTOR(optval);
126
    vals.keepalivetime = optval;
127
    optval = curlx_sltosi(data->set.tcp_keepintvl);
128
    KEEPALIVE_FACTOR(optval);
129
    vals.keepaliveinterval = optval;
130
    if(WSAIoctl(sockfd, SIO_KEEPALIVE_VALS, (LPVOID) &vals, sizeof(vals),
131
                NULL, 0, &dummy, NULL, NULL) != 0) {
132
      infof(data, "Failed to set SIO_KEEPALIVE_VALS on fd %d: %d",
133
            (int)sockfd, WSAGetLastError());
134
    }
135
#else
136
0
#ifdef TCP_KEEPIDLE
137
0
    optval = curlx_sltosi(data->set.tcp_keepidle);
138
0
    KEEPALIVE_FACTOR(optval);
139
0
    if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPIDLE,
140
0
          (void *)&optval, sizeof(optval)) < 0) {
141
0
      infof(data, "Failed to set TCP_KEEPIDLE on fd %d", sockfd);
142
0
    }
143
#elif defined(TCP_KEEPALIVE)
144
    /* Mac OS X style */
145
    optval = curlx_sltosi(data->set.tcp_keepidle);
146
    KEEPALIVE_FACTOR(optval);
147
    if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPALIVE,
148
      (void *)&optval, sizeof(optval)) < 0) {
149
      infof(data, "Failed to set TCP_KEEPALIVE on fd %d", sockfd);
150
    }
151
#endif
152
0
#ifdef TCP_KEEPINTVL
153
0
    optval = curlx_sltosi(data->set.tcp_keepintvl);
154
0
    KEEPALIVE_FACTOR(optval);
155
0
    if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPINTVL,
156
0
          (void *)&optval, sizeof(optval)) < 0) {
157
0
      infof(data, "Failed to set TCP_KEEPINTVL on fd %d", sockfd);
158
0
    }
159
0
#endif
160
0
#endif
161
0
  }
162
0
}
163
164
static CURLcode
165
singleipconnect(struct Curl_easy *data,
166
                struct connectdata *conn,
167
                const struct Curl_addrinfo *ai, /* start connecting to this */
168
                int tempindex);          /* 0 or 1 among the temp ones */
169
170
/*
171
 * Curl_timeleft() returns the amount of milliseconds left allowed for the
172
 * transfer/connection. If the value is 0, there's no timeout (ie there's
173
 * infinite time left). If the value is negative, the timeout time has already
174
 * elapsed.
175
 *
176
 * If 'nowp' is non-NULL, it points to the current time.
177
 * 'duringconnect' is FALSE if not during a connect, as then of course the
178
 * connect timeout is not taken into account!
179
 *
180
 * @unittest: 1303
181
 */
182
183
52.3M
#define TIMEOUT_CONNECT 1
184
99.8M
#define TIMEOUT_MAXTIME 2
185
186
timediff_t Curl_timeleft(struct Curl_easy *data,
187
                         struct curltime *nowp,
188
                         bool duringconnect)
189
47.5M
{
190
47.5M
  unsigned int timeout_set = 0;
191
47.5M
  timediff_t connect_timeout_ms = 0;
192
47.5M
  timediff_t maxtime_timeout_ms = 0;
193
47.5M
  timediff_t timeout_ms = 0;
194
47.5M
  struct curltime now;
195
196
  /* The duration of a connect and the total transfer are calculated from two
197
     different time-stamps. It can end up with the total timeout being reached
198
     before the connect timeout expires and we must acknowledge whichever
199
     timeout that is reached first. The total timeout is set per entire
200
     operation, while the connect timeout is set per connect. */
201
202
47.5M
  if(data->set.timeout > 0) {
203
47.5M
    timeout_set = TIMEOUT_MAXTIME;
204
47.5M
    maxtime_timeout_ms = data->set.timeout;
205
47.5M
  }
206
47.5M
  if(duringconnect) {
207
4.79M
    timeout_set |= TIMEOUT_CONNECT;
208
4.79M
    connect_timeout_ms = (data->set.connecttimeout > 0) ?
209
4.79M
      data->set.connecttimeout : DEFAULT_CONNECT_TIMEOUT;
210
4.79M
  }
211
47.5M
  if(!timeout_set)
212
    /* no timeout  */
213
0
    return 0;
214
215
47.5M
  if(!nowp) {
216
166k
    now = Curl_now();
217
166k
    nowp = &now;
218
166k
  }
219
220
47.5M
  if(timeout_set & TIMEOUT_MAXTIME) {
221
47.5M
    maxtime_timeout_ms -= Curl_timediff(*nowp, data->progress.t_startop);
222
47.5M
    timeout_ms = maxtime_timeout_ms;
223
47.5M
  }
224
225
47.5M
  if(timeout_set & TIMEOUT_CONNECT) {
226
4.79M
    connect_timeout_ms -= Curl_timediff(*nowp, data->progress.t_startsingle);
227
228
4.79M
    if(!(timeout_set & TIMEOUT_MAXTIME) ||
229
4.79M
       (connect_timeout_ms < maxtime_timeout_ms))
230
0
      timeout_ms = connect_timeout_ms;
231
4.79M
  }
232
233
47.5M
  if(!timeout_ms)
234
    /* avoid returning 0 as that means no timeout! */
235
1.05k
    return -1;
236
237
47.5M
  return timeout_ms;
238
47.5M
}
239
240
static CURLcode bindlocal(struct Curl_easy *data,
241
                          curl_socket_t sockfd, int af, unsigned int scope)
242
69.1k
{
243
69.1k
  struct connectdata *conn = data->conn;
244
69.1k
  struct Curl_sockaddr_storage sa;
245
69.1k
  struct sockaddr *sock = (struct sockaddr *)&sa;  /* bind to this address */
246
69.1k
  curl_socklen_t sizeof_sa = 0; /* size of the data sock points to */
247
69.1k
  struct sockaddr_in *si4 = (struct sockaddr_in *)&sa;
248
69.1k
#ifdef ENABLE_IPV6
249
69.1k
  struct sockaddr_in6 *si6 = (struct sockaddr_in6 *)&sa;
250
69.1k
#endif
251
252
69.1k
  struct Curl_dns_entry *h = NULL;
253
69.1k
  unsigned short port = data->set.localport; /* use this port number, 0 for
254
                                                "random" */
255
  /* how many port numbers to try to bind to, increasing one at a time */
256
69.1k
  int portnum = data->set.localportrange;
257
69.1k
  const char *dev = data->set.str[STRING_DEVICE];
258
69.1k
  int error;
259
69.1k
#ifdef IP_BIND_ADDRESS_NO_PORT
260
69.1k
  int on = 1;
261
69.1k
#endif
262
#ifndef ENABLE_IPV6
263
  (void)scope;
264
#endif
265
266
  /*************************************************************
267
   * Select device to bind socket to
268
   *************************************************************/
269
69.1k
  if(!dev && !port)
270
    /* no local kind of binding was requested */
271
69.1k
    return CURLE_OK;
272
273
0
  memset(&sa, 0, sizeof(struct Curl_sockaddr_storage));
274
275
0
  if(dev && (strlen(dev)<255) ) {
276
0
    char myhost[256] = "";
277
0
    int done = 0; /* -1 for error, 1 for address found */
278
0
    bool is_interface = FALSE;
279
0
    bool is_host = FALSE;
280
0
    static const char *if_prefix = "if!";
281
0
    static const char *host_prefix = "host!";
282
283
0
    if(strncmp(if_prefix, dev, strlen(if_prefix)) == 0) {
284
0
      dev += strlen(if_prefix);
285
0
      is_interface = TRUE;
286
0
    }
287
0
    else if(strncmp(host_prefix, dev, strlen(host_prefix)) == 0) {
288
0
      dev += strlen(host_prefix);
289
0
      is_host = TRUE;
290
0
    }
291
292
    /* interface */
293
0
    if(!is_host) {
294
0
#ifdef SO_BINDTODEVICE
295
      /* I am not sure any other OSs than Linux that provide this feature,
296
       * and at the least I cannot test. --Ben
297
       *
298
       * This feature allows one to tightly bind the local socket to a
299
       * particular interface.  This will force even requests to other
300
       * local interfaces to go out the external interface.
301
       *
302
       *
303
       * Only bind to the interface when specified as interface, not just
304
       * as a hostname or ip address.
305
       *
306
       * interface might be a VRF, eg: vrf-blue, which means it cannot be
307
       * converted to an IP address and would fail Curl_if2ip. Simply try
308
       * to use it straight away.
309
       */
310
0
      if(setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE,
311
0
                    dev, (curl_socklen_t)strlen(dev) + 1) == 0) {
312
        /* This is typically "errno 1, error: Operation not permitted" if
313
         * you're not running as root or another suitable privileged
314
         * user.
315
         * If it succeeds it means the parameter was a valid interface and
316
         * not an IP address. Return immediately.
317
         */
318
0
        return CURLE_OK;
319
0
      }
320
0
#endif
321
322
0
      switch(Curl_if2ip(af,
323
0
#ifdef ENABLE_IPV6
324
0
                        scope, conn->scope_id,
325
0
#endif
326
0
                        dev, myhost, sizeof(myhost))) {
327
0
        case IF2IP_NOT_FOUND:
328
0
          if(is_interface) {
329
            /* Do not fall back to treating it as a host name */
330
0
            failf(data, "Couldn't bind to interface '%s'", dev);
331
0
            return CURLE_INTERFACE_FAILED;
332
0
          }
333
0
          break;
334
0
        case IF2IP_AF_NOT_SUPPORTED:
335
          /* Signal the caller to try another address family if available */
336
0
          return CURLE_UNSUPPORTED_PROTOCOL;
337
0
        case IF2IP_FOUND:
338
0
          is_interface = TRUE;
339
          /*
340
           * We now have the numerical IP address in the 'myhost' buffer
341
           */
342
0
          infof(data, "Local Interface %s is ip %s using address family %i",
343
0
                dev, myhost, af);
344
0
          done = 1;
345
0
          break;
346
0
      }
347
0
    }
348
0
    if(!is_interface) {
349
      /*
350
       * This was not an interface, resolve the name as a host name
351
       * or IP number
352
       *
353
       * Temporarily force name resolution to use only the address type
354
       * of the connection. The resolve functions should really be changed
355
       * to take a type parameter instead.
356
       */
357
0
      unsigned char ipver = conn->ip_version;
358
0
      int rc;
359
360
0
      if(af == AF_INET)
361
0
        conn->ip_version = CURL_IPRESOLVE_V4;
362
0
#ifdef ENABLE_IPV6
363
0
      else if(af == AF_INET6)
364
0
        conn->ip_version = CURL_IPRESOLVE_V6;
365
0
#endif
366
367
0
      rc = Curl_resolv(data, dev, 0, FALSE, &h);
368
0
      if(rc == CURLRESOLV_PENDING)
369
0
        (void)Curl_resolver_wait_resolv(data, &h);
370
0
      conn->ip_version = ipver;
371
372
0
      if(h) {
373
        /* convert the resolved address, sizeof myhost >= INET_ADDRSTRLEN */
374
0
        Curl_printable_address(h->addr, myhost, sizeof(myhost));
375
0
        infof(data, "Name '%s' family %i resolved to '%s' family %i",
376
0
              dev, af, myhost, h->addr->ai_family);
377
0
        Curl_resolv_unlock(data, h);
378
0
        if(af != h->addr->ai_family) {
379
          /* bad IP version combo, signal the caller to try another address
380
             family if available */
381
0
          return CURLE_UNSUPPORTED_PROTOCOL;
382
0
        }
383
0
        done = 1;
384
0
      }
385
0
      else {
386
        /*
387
         * provided dev was no interface (or interfaces are not supported
388
         * e.g. solaris) no ip address and no domain we fail here
389
         */
390
0
        done = -1;
391
0
      }
392
0
    }
393
394
0
    if(done > 0) {
395
0
#ifdef ENABLE_IPV6
396
      /* IPv6 address */
397
0
      if(af == AF_INET6) {
398
0
#ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
399
0
        char *scope_ptr = strchr(myhost, '%');
400
0
        if(scope_ptr)
401
0
          *(scope_ptr++) = 0;
402
0
#endif
403
0
        if(Curl_inet_pton(AF_INET6, myhost, &si6->sin6_addr) > 0) {
404
0
          si6->sin6_family = AF_INET6;
405
0
          si6->sin6_port = htons(port);
406
0
#ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
407
0
          if(scope_ptr)
408
            /* The "myhost" string either comes from Curl_if2ip or from
409
               Curl_printable_address. The latter returns only numeric scope
410
               IDs and the former returns none at all.  So the scope ID, if
411
               present, is known to be numeric */
412
0
            si6->sin6_scope_id = atoi(scope_ptr);
413
0
#endif
414
0
        }
415
0
        sizeof_sa = sizeof(struct sockaddr_in6);
416
0
      }
417
0
      else
418
0
#endif
419
      /* IPv4 address */
420
0
      if((af == AF_INET) &&
421
0
         (Curl_inet_pton(AF_INET, myhost, &si4->sin_addr) > 0)) {
422
0
        si4->sin_family = AF_INET;
423
0
        si4->sin_port = htons(port);
424
0
        sizeof_sa = sizeof(struct sockaddr_in);
425
0
      }
426
0
    }
427
428
0
    if(done < 1) {
429
      /* errorbuf is set false so failf will overwrite any message already in
430
         the error buffer, so the user receives this error message instead of a
431
         generic resolve error. */
432
0
      data->state.errorbuf = FALSE;
433
0
      failf(data, "Couldn't bind to '%s'", dev);
434
0
      return CURLE_INTERFACE_FAILED;
435
0
    }
436
0
  }
437
0
  else {
438
    /* no device was given, prepare sa to match af's needs */
439
0
#ifdef ENABLE_IPV6
440
0
    if(af == AF_INET6) {
441
0
      si6->sin6_family = AF_INET6;
442
0
      si6->sin6_port = htons(port);
443
0
      sizeof_sa = sizeof(struct sockaddr_in6);
444
0
    }
445
0
    else
446
0
#endif
447
0
    if(af == AF_INET) {
448
0
      si4->sin_family = AF_INET;
449
0
      si4->sin_port = htons(port);
450
0
      sizeof_sa = sizeof(struct sockaddr_in);
451
0
    }
452
0
  }
453
0
#ifdef IP_BIND_ADDRESS_NO_PORT
454
0
  (void)setsockopt(sockfd, SOL_IP, IP_BIND_ADDRESS_NO_PORT, &on, sizeof(on));
455
0
#endif
456
0
  for(;;) {
457
0
    if(bind(sockfd, sock, sizeof_sa) >= 0) {
458
      /* we succeeded to bind */
459
0
      struct Curl_sockaddr_storage add;
460
0
      curl_socklen_t size = sizeof(add);
461
0
      memset(&add, 0, sizeof(struct Curl_sockaddr_storage));
462
0
      if(getsockname(sockfd, (struct sockaddr *) &add, &size) < 0) {
463
0
        char buffer[STRERROR_LEN];
464
0
        data->state.os_errno = error = SOCKERRNO;
465
0
        failf(data, "getsockname() failed with errno %d: %s",
466
0
              error, Curl_strerror(error, buffer, sizeof(buffer)));
467
0
        return CURLE_INTERFACE_FAILED;
468
0
      }
469
0
      infof(data, "Local port: %hu", port);
470
0
      conn->bits.bound = TRUE;
471
0
      return CURLE_OK;
472
0
    }
473
474
0
    if(--portnum > 0) {
475
0
      port++; /* try next port */
476
0
      if(port == 0)
477
0
        break;
478
0
      infof(data, "Bind to local port %hu failed, trying next", port - 1);
479
      /* We re-use/clobber the port variable here below */
480
0
      if(sock->sa_family == AF_INET)
481
0
        si4->sin_port = ntohs(port);
482
0
#ifdef ENABLE_IPV6
483
0
      else
484
0
        si6->sin6_port = ntohs(port);
485
0
#endif
486
0
    }
487
0
    else
488
0
      break;
489
0
  }
490
0
  {
491
0
    char buffer[STRERROR_LEN];
492
0
    data->state.os_errno = error = SOCKERRNO;
493
0
    failf(data, "bind failed with errno %d: %s",
494
0
          error, Curl_strerror(error, buffer, sizeof(buffer)));
495
0
  }
496
497
0
  return CURLE_INTERFACE_FAILED;
498
0
}
499
500
/*
501
 * verifyconnect() returns TRUE if the connect really has happened.
502
 */
503
static bool verifyconnect(curl_socket_t sockfd, int *error)
504
69.1k
{
505
69.1k
  bool rc = TRUE;
506
69.1k
#ifdef SO_ERROR
507
69.1k
  int err = 0;
508
69.1k
  curl_socklen_t errSize = sizeof(err);
509
510
#ifdef WIN32
511
  /*
512
   * In October 2003 we effectively nullified this function on Windows due to
513
   * problems with it using all CPU in multi-threaded cases.
514
   *
515
   * In May 2004, we bring it back to offer more info back on connect failures.
516
   * Gisle Vanem could reproduce the former problems with this function, but
517
   * could avoid them by adding this SleepEx() call below:
518
   *
519
   *    "I don't have Rational Quantify, but the hint from his post was
520
   *    ntdll::NtRemoveIoCompletion(). So I'd assume the SleepEx (or maybe
521
   *    just Sleep(0) would be enough?) would release whatever
522
   *    mutex/critical-section the ntdll call is waiting on.
523
   *
524
   *    Someone got to verify this on Win-NT 4.0, 2000."
525
   */
526
527
#ifdef _WIN32_WCE
528
  Sleep(0);
529
#else
530
  SleepEx(0, FALSE);
531
#endif
532
533
#endif
534
535
69.1k
  if(0 != getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (void *)&err, &errSize))
536
0
    err = SOCKERRNO;
537
#ifdef _WIN32_WCE
538
  /* Old WinCE versions don't support SO_ERROR */
539
  if(WSAENOPROTOOPT == err) {
540
    SET_SOCKERRNO(0);
541
    err = 0;
542
  }
543
#endif
544
#if defined(EBADIOCTL) && defined(__minix)
545
  /* Minix 3.1.x doesn't support getsockopt on UDP sockets */
546
  if(EBADIOCTL == err) {
547
    SET_SOCKERRNO(0);
548
    err = 0;
549
  }
550
#endif
551
69.1k
  if((0 == err) || (EISCONN == err))
552
    /* we are connected, awesome! */
553
69.1k
    rc = TRUE;
554
0
  else
555
    /* This wasn't a successful connect */
556
0
    rc = FALSE;
557
69.1k
  if(error)
558
69.1k
    *error = err;
559
#else
560
  (void)sockfd;
561
  if(error)
562
    *error = SOCKERRNO;
563
#endif
564
69.1k
  return rc;
565
69.1k
}
566
567
/* update tempaddr[tempindex] (to the next entry), makes sure to stick
568
   to the correct family */
569
static struct Curl_addrinfo *ainext(struct connectdata *conn,
570
                                    int tempindex,
571
                                    bool next) /* use next entry? */
572
69.4k
{
573
69.4k
  struct Curl_addrinfo *ai = conn->tempaddr[tempindex];
574
69.4k
  if(ai && next)
575
160
    ai = ai->ai_next;
576
138k
  while(ai && (ai->ai_family != conn->tempfamily[tempindex]))
577
69.3k
    ai = ai->ai_next;
578
69.4k
  conn->tempaddr[tempindex] = ai;
579
69.4k
  return ai;
580
69.4k
}
581
582
/* Used within the multi interface. Try next IP address, returns error if no
583
   more address exists or error */
584
static CURLcode trynextip(struct Curl_easy *data,
585
                          struct connectdata *conn,
586
                          int sockindex,
587
                          int tempindex)
588
0
{
589
0
  CURLcode result = CURLE_COULDNT_CONNECT;
590
591
  /* First clean up after the failed socket.
592
     Don't close it yet to ensure that the next IP's socket gets a different
593
     file descriptor, which can prevent bugs when the curl_multi_socket_action
594
     interface is used with certain select() replacements such as kqueue. */
595
0
  curl_socket_t fd_to_close = conn->tempsock[tempindex];
596
0
  conn->tempsock[tempindex] = CURL_SOCKET_BAD;
597
598
0
  if(sockindex == FIRSTSOCKET) {
599
0
    struct Curl_addrinfo *ai = conn->tempaddr[tempindex];
600
601
0
    while(ai) {
602
0
      result = singleipconnect(data, conn, ai, tempindex);
603
0
      if(result == CURLE_COULDNT_CONNECT) {
604
0
        ai = ainext(conn, tempindex, TRUE);
605
0
        continue;
606
0
      }
607
0
      break;
608
0
    }
609
0
  }
610
611
0
  if(fd_to_close != CURL_SOCKET_BAD)
612
0
    Curl_closesocket(data, conn, fd_to_close);
613
614
0
  return result;
615
0
}
616
617
/* Copies connection info into the transfer handle to make it available when
618
   the transfer handle is no longer associated with the connection. */
619
void Curl_persistconninfo(struct Curl_easy *data, struct connectdata *conn,
620
                          char *local_ip, int local_port)
621
72.9k
{
622
72.9k
  memcpy(data->info.conn_primary_ip, conn->primary_ip, MAX_IPADR_LEN);
623
72.9k
  if(local_ip && local_ip[0])
624
0
    memcpy(data->info.conn_local_ip, local_ip, MAX_IPADR_LEN);
625
72.9k
  else
626
72.9k
    data->info.conn_local_ip[0] = 0;
627
72.9k
  data->info.conn_scheme = conn->handler->scheme;
628
  /* conn_protocol can only provide "old" protocols */
629
72.9k
  data->info.conn_protocol = (conn->handler->protocol) & CURLPROTO_MASK;
630
72.9k
  data->info.conn_primary_port = conn->port;
631
72.9k
  data->info.conn_remote_port = conn->remote_port;
632
72.9k
  data->info.conn_local_port = local_port;
633
72.9k
}
634
635
/* retrieves ip address and port from a sockaddr structure.
636
   note it calls Curl_inet_ntop which sets errno on fail, not SOCKERRNO. */
637
bool Curl_addr2string(struct sockaddr *sa, curl_socklen_t salen,
638
                      char *addr, int *port)
639
209k
{
640
209k
  struct sockaddr_in *si = NULL;
641
209k
#ifdef ENABLE_IPV6
642
209k
  struct sockaddr_in6 *si6 = NULL;
643
209k
#endif
644
209k
#if (defined(HAVE_SYS_UN_H) || defined(WIN32_SOCKADDR_UN)) && defined(AF_UNIX)
645
209k
  struct sockaddr_un *su = NULL;
646
#else
647
  (void)salen;
648
#endif
649
650
209k
  switch(sa->sa_family) {
651
69.1k
    case AF_INET:
652
69.1k
      si = (struct sockaddr_in *)(void *) sa;
653
69.1k
      if(Curl_inet_ntop(sa->sa_family, &si->sin_addr,
654
69.1k
                        addr, MAX_IPADR_LEN)) {
655
69.1k
        unsigned short us_port = ntohs(si->sin_port);
656
69.1k
        *port = us_port;
657
69.1k
        return TRUE;
658
69.1k
      }
659
0
      break;
660
0
#ifdef ENABLE_IPV6
661
0
    case AF_INET6:
662
0
      si6 = (struct sockaddr_in6 *)(void *) sa;
663
0
      if(Curl_inet_ntop(sa->sa_family, &si6->sin6_addr,
664
0
                        addr, MAX_IPADR_LEN)) {
665
0
        unsigned short us_port = ntohs(si6->sin6_port);
666
0
        *port = us_port;
667
0
        return TRUE;
668
0
      }
669
0
      break;
670
0
#endif
671
0
#if (defined(HAVE_SYS_UN_H) || defined(WIN32_SOCKADDR_UN)) && defined(AF_UNIX)
672
140k
    case AF_UNIX:
673
140k
      if(salen > (curl_socklen_t)sizeof(CURL_SA_FAMILY_T)) {
674
0
        su = (struct sockaddr_un*)sa;
675
0
        msnprintf(addr, MAX_IPADR_LEN, "%s", su->sun_path);
676
0
      }
677
140k
      else
678
140k
        addr[0] = 0; /* socket with no name */
679
140k
      *port = 0;
680
140k
      return TRUE;
681
0
#endif
682
0
    default:
683
0
      break;
684
209k
  }
685
686
0
  addr[0] = '\0';
687
0
  *port = 0;
688
0
  errno = EAFNOSUPPORT;
689
0
  return FALSE;
690
209k
}
691
692
/* retrieves the start/end point information of a socket of an established
693
   connection */
694
void Curl_conninfo_remote(struct Curl_easy *data,
695
                          struct connectdata *conn, curl_socket_t sockfd)
696
69.1k
{
697
69.1k
#ifdef HAVE_GETPEERNAME
698
69.1k
  char buffer[STRERROR_LEN];
699
69.1k
  struct Curl_sockaddr_storage ssrem;
700
69.1k
  curl_socklen_t plen;
701
69.1k
  int port;
702
69.1k
  plen = sizeof(struct Curl_sockaddr_storage);
703
69.1k
  memset(&ssrem, 0, sizeof(ssrem));
704
69.1k
  if(getpeername(sockfd, (struct sockaddr*) &ssrem, &plen)) {
705
0
    int error = SOCKERRNO;
706
0
    failf(data, "getpeername() failed with errno %d: %s",
707
0
          error, Curl_strerror(error, buffer, sizeof(buffer)));
708
0
    return;
709
0
  }
710
69.1k
  if(!Curl_addr2string((struct sockaddr*)&ssrem, plen,
711
69.1k
                       conn->primary_ip, &port)) {
712
0
    failf(data, "ssrem inet_ntop() failed with errno %d: %s",
713
0
          errno, Curl_strerror(errno, buffer, sizeof(buffer)));
714
0
    return;
715
0
  }
716
#else
717
  (void)data;
718
  (void)conn;
719
  (void)sockfd;
720
#endif
721
69.1k
}
722
723
/* retrieves the start/end point information of a socket of an established
724
   connection */
725
void Curl_conninfo_local(struct Curl_easy *data, curl_socket_t sockfd,
726
                         char *local_ip, int *local_port)
727
71.5k
{
728
71.5k
#ifdef HAVE_GETSOCKNAME
729
71.5k
  char buffer[STRERROR_LEN];
730
71.5k
  struct Curl_sockaddr_storage ssloc;
731
71.5k
  curl_socklen_t slen;
732
71.5k
  slen = sizeof(struct Curl_sockaddr_storage);
733
71.5k
  memset(&ssloc, 0, sizeof(ssloc));
734
71.5k
  if(getsockname(sockfd, (struct sockaddr*) &ssloc, &slen)) {
735
0
    int error = SOCKERRNO;
736
0
    failf(data, "getsockname() failed with errno %d: %s",
737
0
          error, Curl_strerror(error, buffer, sizeof(buffer)));
738
0
    return;
739
0
  }
740
71.5k
  if(!Curl_addr2string((struct sockaddr*)&ssloc, slen,
741
71.5k
                       local_ip, local_port)) {
742
0
    failf(data, "ssloc inet_ntop() failed with errno %d: %s",
743
0
          errno, Curl_strerror(errno, buffer, sizeof(buffer)));
744
0
    return;
745
0
  }
746
#else
747
  (void)data;
748
  (void)sockfd;
749
  (void)local_ip;
750
  (void)local_port;
751
#endif
752
71.5k
}
753
754
/* retrieves the start/end point information of a socket of an established
755
   connection */
756
void Curl_updateconninfo(struct Curl_easy *data, struct connectdata *conn,
757
                         curl_socket_t sockfd)
758
70.3k
{
759
  /* 'local_ip' and 'local_port' get filled with local's numerical
760
     ip address and port number whenever an outgoing connection is
761
     **established** from the primary socket to a remote address. */
762
70.3k
  char local_ip[MAX_IPADR_LEN] = "";
763
70.3k
  int local_port = -1;
764
765
70.3k
  if(!conn->bits.reuse &&
766
70.3k
     (conn->transport != TRNSPRT_TCP || !conn->bits.tcp_fastopen))
767
69.1k
    Curl_conninfo_remote(data, conn, sockfd);
768
70.3k
  Curl_conninfo_local(data, sockfd, local_ip, &local_port);
769
770
  /* persist connection info in session handle */
771
70.3k
  Curl_persistconninfo(data, conn, local_ip, local_port);
772
70.3k
}
773
774
/* After a TCP connection to the proxy has been verified, this function does
775
   the next magic steps. If 'done' isn't set TRUE, it is not done yet and
776
   must be called again.
777
778
   Note: this function's sub-functions call failf()
779
780
*/
781
static CURLcode connect_SOCKS(struct Curl_easy *data, int sockindex,
782
                              bool *done)
783
69.1k
{
784
69.1k
  CURLcode result = CURLE_OK;
785
69.1k
#ifndef CURL_DISABLE_PROXY
786
69.1k
  CURLproxycode pxresult = CURLPX_OK;
787
69.1k
  struct connectdata *conn = data->conn;
788
69.1k
  if(conn->bits.socksproxy) {
789
    /* for the secondary socket (FTP), use the "connect to host"
790
     * but ignore the "connect to port" (use the secondary port)
791
     */
792
0
    const char * const host =
793
0
      conn->bits.httpproxy ?
794
0
      conn->http_proxy.host.name :
795
0
      conn->bits.conn_to_host ?
796
0
      conn->conn_to_host.name :
797
0
      sockindex == SECONDARYSOCKET ?
798
0
      conn->secondaryhostname : conn->host.name;
799
0
    const int port =
800
0
      conn->bits.httpproxy ? (int)conn->http_proxy.port :
801
0
      sockindex == SECONDARYSOCKET ? conn->secondary_port :
802
0
      conn->bits.conn_to_port ? conn->conn_to_port :
803
0
      conn->remote_port;
804
0
    switch(conn->socks_proxy.proxytype) {
805
0
    case CURLPROXY_SOCKS5:
806
0
    case CURLPROXY_SOCKS5_HOSTNAME:
807
0
      pxresult = Curl_SOCKS5(conn->socks_proxy.user, conn->socks_proxy.passwd,
808
0
                             host, port, sockindex, data, done);
809
0
      break;
810
811
0
    case CURLPROXY_SOCKS4:
812
0
    case CURLPROXY_SOCKS4A:
813
0
      pxresult = Curl_SOCKS4(conn->socks_proxy.user, host, port, sockindex,
814
0
                             data, done);
815
0
      break;
816
817
0
    default:
818
0
      failf(data, "unknown proxytype option given");
819
0
      result = CURLE_COULDNT_CONNECT;
820
0
    } /* switch proxytype */
821
0
    if(pxresult) {
822
0
      result = CURLE_PROXY;
823
0
      data->info.pxcode = pxresult;
824
0
    }
825
0
  }
826
69.1k
  else
827
#else
828
    (void)data;
829
    (void)sockindex;
830
#endif /* CURL_DISABLE_PROXY */
831
69.1k
    *done = TRUE; /* no SOCKS proxy, so consider us connected */
832
833
69.1k
  return result;
834
69.1k
}
835
836
/*
837
 * post_SOCKS() is called after a successful connect to the peer, which
838
 * *could* be a SOCKS proxy
839
 */
840
static void post_SOCKS(struct Curl_easy *data,
841
                       struct connectdata *conn,
842
                       int sockindex,
843
                       bool *connected)
844
69.1k
{
845
69.1k
  conn->bits.tcpconnect[sockindex] = TRUE;
846
847
69.1k
  *connected = TRUE;
848
69.1k
  if(sockindex == FIRSTSOCKET)
849
69.1k
    Curl_pgrsTime(data, TIMER_CONNECT); /* connect done */
850
69.1k
  Curl_updateconninfo(data, conn, conn->sock[sockindex]);
851
69.1k
  Curl_verboseconnect(data, conn);
852
69.1k
  data->info.numconnects++; /* to track the number of connections made */
853
69.1k
}
854
855
/*
856
 * Curl_is_connected() checks if the socket has connected.
857
 */
858
859
CURLcode Curl_is_connected(struct Curl_easy *data,
860
                           struct connectdata *conn,
861
                           int sockindex,
862
                           bool *connected)
863
69.1k
{
864
69.1k
  CURLcode result = CURLE_OK;
865
69.1k
  timediff_t allow;
866
69.1k
  int error = 0;
867
69.1k
  struct curltime now;
868
69.1k
  int rc = 0;
869
69.1k
  unsigned int i;
870
871
69.1k
  DEBUGASSERT(sockindex >= FIRSTSOCKET && sockindex <= SECONDARYSOCKET);
872
873
69.1k
  *connected = FALSE; /* a very negative world view is best */
874
875
69.1k
  if(conn->bits.tcpconnect[sockindex]) {
876
    /* we are connected already! */
877
0
    *connected = TRUE;
878
0
    return CURLE_OK;
879
0
  }
880
881
69.1k
  now = Curl_now();
882
883
69.1k
  if(SOCKS_STATE(conn->cnnct.state)) {
884
    /* still doing SOCKS */
885
0
    result = connect_SOCKS(data, sockindex, connected);
886
0
    if(!result && *connected)
887
0
      post_SOCKS(data, conn, sockindex, connected);
888
0
    return result;
889
0
  }
890
891
69.1k
  for(i = 0; i<2; i++) {
892
69.1k
    const int other = i ^ 1;
893
69.1k
    if(conn->tempsock[i] == CURL_SOCKET_BAD)
894
0
      continue;
895
69.1k
    error = 0;
896
#ifdef ENABLE_QUIC
897
    if(conn->transport == TRNSPRT_QUIC) {
898
      result = Curl_quic_is_connected(data, conn, i, connected);
899
      if(!result && *connected) {
900
        /* use this socket from now on */
901
        conn->sock[sockindex] = conn->tempsock[i];
902
        conn->ip_addr = conn->tempaddr[i];
903
        conn->tempsock[i] = CURL_SOCKET_BAD;
904
        post_SOCKS(data, conn, sockindex, connected);
905
        connkeep(conn, "HTTP/3 default");
906
        if(conn->tempsock[other] != CURL_SOCKET_BAD)
907
          Curl_quic_disconnect(data, conn, other);
908
        return CURLE_OK;
909
      }
910
      /* When a QUIC connect attempt fails, the better error explanation is in
911
         'result' and not in errno */
912
      if(result) {
913
        conn->tempsock[i] = CURL_SOCKET_BAD;
914
        error = SOCKERRNO;
915
      }
916
    }
917
    else
918
#endif
919
69.1k
    {
920
#ifdef mpeix
921
      /* Call this function once now, and ignore the results. We do this to
922
         "clear" the error state on the socket so that we can later read it
923
         reliably. This is reported necessary on the MPE/iX operating
924
         system. */
925
      (void)verifyconnect(conn->tempsock[i], NULL);
926
#endif
927
928
      /* check socket for connect */
929
69.1k
      rc = SOCKET_WRITABLE(conn->tempsock[i], 0);
930
69.1k
    }
931
932
69.1k
    if(rc == 0) { /* no connection yet */
933
0
      if(Curl_timediff(now, conn->connecttime) >=
934
0
         conn->timeoutms_per_addr[i]) {
935
0
        infof(data, "After %" CURL_FORMAT_TIMEDIFF_T
936
0
              "ms connect time, move on!", conn->timeoutms_per_addr[i]);
937
0
        error = ETIMEDOUT;
938
0
      }
939
940
      /* should we try another protocol family? */
941
0
      if(i == 0 && !conn->bits.parallel_connect &&
942
0
         (Curl_timediff(now, conn->connecttime) >=
943
0
          data->set.happy_eyeballs_timeout)) {
944
0
        conn->bits.parallel_connect = TRUE; /* starting now */
945
0
        trynextip(data, conn, sockindex, 1);
946
0
      }
947
0
    }
948
69.1k
    else if(rc == CURL_CSELECT_OUT || conn->bits.tcp_fastopen) {
949
69.1k
      if(verifyconnect(conn->tempsock[i], &error)) {
950
        /* we are connected with TCP, awesome! */
951
952
        /* use this socket from now on */
953
69.1k
        conn->sock[sockindex] = conn->tempsock[i];
954
69.1k
        conn->ip_addr = conn->tempaddr[i];
955
69.1k
        conn->tempsock[i] = CURL_SOCKET_BAD;
956
69.1k
#ifdef ENABLE_IPV6
957
69.1k
        conn->bits.ipv6 = (conn->ip_addr->ai_family == AF_INET6)?TRUE:FALSE;
958
69.1k
#endif
959
960
        /* close the other socket, if open */
961
69.1k
        if(conn->tempsock[other] != CURL_SOCKET_BAD) {
962
0
          Curl_closesocket(data, conn, conn->tempsock[other]);
963
0
          conn->tempsock[other] = CURL_SOCKET_BAD;
964
0
        }
965
966
        /* see if we need to kick off any SOCKS proxy magic once we
967
           connected */
968
69.1k
        result = connect_SOCKS(data, sockindex, connected);
969
69.1k
        if(result || !*connected)
970
0
          return result;
971
972
69.1k
        post_SOCKS(data, conn, sockindex, connected);
973
974
69.1k
        return CURLE_OK;
975
69.1k
      }
976
69.1k
    }
977
0
    else if(rc & CURL_CSELECT_ERR) {
978
0
      (void)verifyconnect(conn->tempsock[i], &error);
979
0
    }
980
981
    /*
982
     * The connection failed here, we should attempt to connect to the "next
983
     * address" for the given host. But first remember the latest error.
984
     */
985
0
    if(error) {
986
0
      data->state.os_errno = error;
987
0
      SET_SOCKERRNO(error);
988
0
      if(conn->tempaddr[i]) {
989
0
        CURLcode status;
990
0
#ifndef CURL_DISABLE_VERBOSE_STRINGS
991
0
        char ipaddress[MAX_IPADR_LEN];
992
0
        char buffer[STRERROR_LEN];
993
0
        Curl_printable_address(conn->tempaddr[i], ipaddress,
994
0
                               sizeof(ipaddress));
995
#ifdef ENABLE_QUIC
996
        if(conn->transport == TRNSPRT_QUIC) {
997
          infof(data, "connect to %s port %u failed: %s",
998
                ipaddress, conn->port, curl_easy_strerror(result));
999
        }
1000
        else
1001
#endif
1002
0
        infof(data, "connect to %s port %u failed: %s",
1003
0
              ipaddress, conn->port,
1004
0
              Curl_strerror(error, buffer, sizeof(buffer)));
1005
0
#endif
1006
1007
0
        allow = Curl_timeleft(data, &now, TRUE);
1008
0
        conn->timeoutms_per_addr[i] = conn->tempaddr[i]->ai_next == NULL ?
1009
0
          allow : allow / 2;
1010
0
        ainext(conn, i, TRUE);
1011
0
        status = trynextip(data, conn, sockindex, i);
1012
0
        if((status != CURLE_COULDNT_CONNECT) ||
1013
0
           conn->tempsock[other] == CURL_SOCKET_BAD) {
1014
          /* the last attempt failed and no other sockets remain open */
1015
0
          if(!result)
1016
0
            result = status;
1017
0
        }
1018
0
      }
1019
0
    }
1020
0
  }
1021
1022
  /*
1023
   * Now that we've checked whether we are connected, check whether we've
1024
   * already timed out.
1025
   *
1026
   * First figure out how long time we have left to connect */
1027
1028
0
  allow = Curl_timeleft(data, &now, TRUE);
1029
1030
0
  if(allow < 0) {
1031
    /* time-out, bail out, go home */
1032
0
    failf(data, "Connection timeout after %ld ms",
1033
0
          Curl_timediff(now, data->progress.t_startsingle));
1034
0
    return CURLE_OPERATION_TIMEDOUT;
1035
0
  }
1036
1037
0
  if(result &&
1038
0
     (conn->tempsock[0] == CURL_SOCKET_BAD) &&
1039
0
     (conn->tempsock[1] == CURL_SOCKET_BAD)) {
1040
    /* no more addresses to try */
1041
0
    const char *hostname;
1042
0
    CURLcode failreason = result;
1043
1044
    /* if the first address family runs out of addresses to try before the
1045
       happy eyeball timeout, go ahead and try the next family now */
1046
0
    result = trynextip(data, conn, sockindex, 1);
1047
0
    if(!result)
1048
0
      return result;
1049
1050
0
    result = failreason;
1051
1052
0
#ifndef CURL_DISABLE_PROXY
1053
0
    if(conn->bits.socksproxy)
1054
0
      hostname = conn->socks_proxy.host.name;
1055
0
    else if(conn->bits.httpproxy)
1056
0
      hostname = conn->http_proxy.host.name;
1057
0
    else
1058
0
#endif
1059
0
      if(conn->bits.conn_to_host)
1060
0
        hostname = conn->conn_to_host.name;
1061
0
    else
1062
0
      hostname = conn->host.name;
1063
1064
0
    failf(data, "Failed to connect to %s port %u after "
1065
0
          "%" CURL_FORMAT_TIMEDIFF_T " ms: %s",
1066
0
          hostname, conn->port,
1067
0
          Curl_timediff(now, data->progress.t_startsingle),
1068
0
          curl_easy_strerror(result));
1069
1070
0
    Curl_quic_disconnect(data, conn, 0);
1071
0
    Curl_quic_disconnect(data, conn, 1);
1072
1073
#ifdef WSAETIMEDOUT
1074
    if(WSAETIMEDOUT == data->state.os_errno)
1075
      result = CURLE_OPERATION_TIMEDOUT;
1076
#elif defined(ETIMEDOUT)
1077
0
    if(ETIMEDOUT == data->state.os_errno)
1078
0
      result = CURLE_OPERATION_TIMEDOUT;
1079
0
#endif
1080
0
  }
1081
0
  else
1082
0
    result = CURLE_OK; /* still trying */
1083
1084
0
  return result;
1085
0
}
1086
1087
static void tcpnodelay(struct Curl_easy *data, curl_socket_t sockfd)
1088
68.1k
{
1089
68.1k
#if defined(TCP_NODELAY)
1090
68.1k
  curl_socklen_t onoff = (curl_socklen_t) 1;
1091
68.1k
  int level = IPPROTO_TCP;
1092
68.1k
#if !defined(CURL_DISABLE_VERBOSE_STRINGS)
1093
68.1k
  char buffer[STRERROR_LEN];
1094
#else
1095
  (void) data;
1096
#endif
1097
1098
68.1k
  if(setsockopt(sockfd, level, TCP_NODELAY, (void *)&onoff,
1099
68.1k
                sizeof(onoff)) < 0)
1100
68.1k
    infof(data, "Could not set TCP_NODELAY: %s",
1101
68.1k
          Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
1102
#else
1103
  (void)data;
1104
  (void)sockfd;
1105
#endif
1106
68.1k
}
1107
1108
#ifdef SO_NOSIGPIPE
1109
/* The preferred method on Mac OS X (10.2 and later) to prevent SIGPIPEs when
1110
   sending data to a dead peer (instead of relying on the 4th argument to send
1111
   being MSG_NOSIGNAL). Possibly also existing and in use on other BSD
1112
   systems? */
1113
static void nosigpipe(struct Curl_easy *data,
1114
                      curl_socket_t sockfd)
1115
{
1116
  int onoff = 1;
1117
  if(setsockopt(sockfd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&onoff,
1118
                sizeof(onoff)) < 0) {
1119
#if !defined(CURL_DISABLE_VERBOSE_STRINGS)
1120
    char buffer[STRERROR_LEN];
1121
    infof(data, "Could not set SO_NOSIGPIPE: %s",
1122
          Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
1123
#endif
1124
  }
1125
}
1126
#else
1127
69.1k
#define nosigpipe(x,y) Curl_nop_stmt
1128
#endif
1129
1130
#ifdef USE_WINSOCK
1131
/* When you run a program that uses the Windows Sockets API, you may
1132
   experience slow performance when you copy data to a TCP server.
1133
1134
   https://support.microsoft.com/kb/823764
1135
1136
   Work-around: Make the Socket Send Buffer Size Larger Than the Program Send
1137
   Buffer Size
1138
1139
   The problem described in this knowledge-base is applied only to pre-Vista
1140
   Windows.  Following function trying to detect OS version and skips
1141
   SO_SNDBUF adjustment for Windows Vista and above.
1142
*/
1143
#define DETECT_OS_NONE 0
1144
#define DETECT_OS_PREVISTA 1
1145
#define DETECT_OS_VISTA_OR_LATER 2
1146
1147
void Curl_sndbufset(curl_socket_t sockfd)
1148
{
1149
  int val = CURL_MAX_WRITE_SIZE + 32;
1150
  int curval = 0;
1151
  int curlen = sizeof(curval);
1152
1153
  static int detectOsState = DETECT_OS_NONE;
1154
1155
  if(detectOsState == DETECT_OS_NONE) {
1156
    if(curlx_verify_windows_version(6, 0, 0, PLATFORM_WINNT,
1157
                                    VERSION_GREATER_THAN_EQUAL))
1158
      detectOsState = DETECT_OS_VISTA_OR_LATER;
1159
    else
1160
      detectOsState = DETECT_OS_PREVISTA;
1161
  }
1162
1163
  if(detectOsState == DETECT_OS_VISTA_OR_LATER)
1164
    return;
1165
1166
  if(getsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (char *)&curval, &curlen) == 0)
1167
    if(curval > val)
1168
      return;
1169
1170
  setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (const char *)&val, sizeof(val));
1171
}
1172
#endif
1173
1174
/*
1175
 * singleipconnect()
1176
 *
1177
 * Note that even on connect fail it returns CURLE_OK, but with 'sock' set to
1178
 * CURL_SOCKET_BAD. Other errors will however return proper errors.
1179
 *
1180
 * singleipconnect() connects to the given IP only, and it may return without
1181
 * having connected.
1182
 */
1183
static CURLcode singleipconnect(struct Curl_easy *data,
1184
                                struct connectdata *conn,
1185
                                const struct Curl_addrinfo *ai,
1186
                                int tempindex)
1187
69.3k
{
1188
69.3k
  struct Curl_sockaddr_ex addr;
1189
69.3k
  int rc = -1;
1190
69.3k
  int error = 0;
1191
69.3k
  bool isconnected = FALSE;
1192
69.3k
  curl_socket_t sockfd;
1193
69.3k
  CURLcode result;
1194
69.3k
  char ipaddress[MAX_IPADR_LEN];
1195
69.3k
  int port;
1196
69.3k
  bool is_tcp;
1197
69.3k
#ifdef TCP_FASTOPEN_CONNECT
1198
69.3k
  int optval = 1;
1199
69.3k
#endif
1200
69.3k
  char buffer[STRERROR_LEN];
1201
69.3k
  curl_socket_t *sockp = &conn->tempsock[tempindex];
1202
69.3k
  *sockp = CURL_SOCKET_BAD;
1203
1204
69.3k
  result = Curl_socket(data, ai, &addr, &sockfd);
1205
69.3k
  if(result)
1206
160
    return result;
1207
1208
  /* store remote address and port used in this connection attempt */
1209
69.1k
  if(!Curl_addr2string((struct sockaddr*)&addr.sa_addr, addr.addrlen,
1210
69.1k
                       ipaddress, &port)) {
1211
    /* malformed address or bug in inet_ntop, try next address */
1212
0
    failf(data, "sa_addr inet_ntop() failed with errno %d: %s",
1213
0
          errno, Curl_strerror(errno, buffer, sizeof(buffer)));
1214
0
    Curl_closesocket(data, conn, sockfd);
1215
0
    return CURLE_OK;
1216
0
  }
1217
69.1k
  infof(data, "  Trying %s%s%s:%d...",
1218
69.1k
        (addr.family == AF_INET6 ? "[" : ""),
1219
69.1k
        ipaddress,
1220
69.1k
        (addr.family == AF_INET6 ? "]" : ""),
1221
69.1k
        port);
1222
1223
69.1k
#ifdef ENABLE_IPV6
1224
69.1k
  is_tcp = (addr.family == AF_INET || addr.family == AF_INET6) &&
1225
69.1k
    addr.socktype == SOCK_STREAM;
1226
#else
1227
  is_tcp = (addr.family == AF_INET) && addr.socktype == SOCK_STREAM;
1228
#endif
1229
69.1k
  if(is_tcp && data->set.tcp_nodelay)
1230
68.1k
    tcpnodelay(data, sockfd);
1231
1232
69.1k
  nosigpipe(data, sockfd);
1233
1234
69.1k
  Curl_sndbufset(sockfd);
1235
1236
69.1k
  if(is_tcp && data->set.tcp_keepalive)
1237
0
    tcpkeepalive(data, sockfd);
1238
1239
69.1k
  if(data->set.fsockopt) {
1240
    /* activate callback for setting socket options */
1241
69.1k
    Curl_set_in_callback(data, true);
1242
69.1k
    error = data->set.fsockopt(data->set.sockopt_client,
1243
69.1k
                               sockfd,
1244
69.1k
                               CURLSOCKTYPE_IPCXN);
1245
69.1k
    Curl_set_in_callback(data, false);
1246
1247
69.1k
    if(error == CURL_SOCKOPT_ALREADY_CONNECTED)
1248
69.1k
      isconnected = TRUE;
1249
0
    else if(error) {
1250
0
      Curl_closesocket(data, conn, sockfd); /* close the socket and bail out */
1251
0
      return CURLE_ABORTED_BY_CALLBACK;
1252
0
    }
1253
69.1k
  }
1254
1255
  /* possibly bind the local end to an IP, interface or port */
1256
69.1k
  if(addr.family == AF_INET
1257
69.1k
#ifdef ENABLE_IPV6
1258
69.1k
     || addr.family == AF_INET6
1259
69.1k
#endif
1260
69.1k
    ) {
1261
69.1k
    result = bindlocal(data, sockfd, addr.family,
1262
69.1k
                       Curl_ipv6_scope((struct sockaddr*)&addr.sa_addr));
1263
69.1k
    if(result) {
1264
0
      Curl_closesocket(data, conn, sockfd); /* close socket and bail out */
1265
0
      if(result == CURLE_UNSUPPORTED_PROTOCOL) {
1266
        /* The address family is not supported on this interface.
1267
           We can continue trying addresses */
1268
0
        return CURLE_COULDNT_CONNECT;
1269
0
      }
1270
0
      return result;
1271
0
    }
1272
69.1k
  }
1273
1274
  /* set socket non-blocking */
1275
69.1k
  (void)curlx_nonblock(sockfd, TRUE);
1276
1277
69.1k
  conn->connecttime = Curl_now();
1278
69.1k
  if(conn->num_addr > 1) {
1279
0
    Curl_expire(data, conn->timeoutms_per_addr[0], EXPIRE_DNS_PER_NAME);
1280
0
    Curl_expire(data, conn->timeoutms_per_addr[1], EXPIRE_DNS_PER_NAME2);
1281
0
  }
1282
1283
  /* Connect TCP and QUIC sockets */
1284
69.1k
  if(!isconnected && (conn->transport != TRNSPRT_UDP)) {
1285
0
    if(conn->bits.tcp_fastopen) {
1286
#if defined(CONNECT_DATA_IDEMPOTENT) /* Darwin */
1287
#  if defined(HAVE_BUILTIN_AVAILABLE)
1288
      /* while connectx function is available since macOS 10.11 / iOS 9,
1289
         it did not have the interface declared correctly until
1290
         Xcode 9 / macOS SDK 10.13 */
1291
      if(__builtin_available(macOS 10.11, iOS 9.0, tvOS 9.0, watchOS 2.0, *)) {
1292
        sa_endpoints_t endpoints;
1293
        endpoints.sae_srcif = 0;
1294
        endpoints.sae_srcaddr = NULL;
1295
        endpoints.sae_srcaddrlen = 0;
1296
        endpoints.sae_dstaddr = &addr.sa_addr;
1297
        endpoints.sae_dstaddrlen = addr.addrlen;
1298
1299
        rc = connectx(sockfd, &endpoints, SAE_ASSOCID_ANY,
1300
                      CONNECT_RESUME_ON_READ_WRITE | CONNECT_DATA_IDEMPOTENT,
1301
                      NULL, 0, NULL, NULL);
1302
      }
1303
      else {
1304
        rc = connect(sockfd, &addr.sa_addr, addr.addrlen);
1305
      }
1306
#  else
1307
      rc = connect(sockfd, &addr.sa_addr, addr.addrlen);
1308
#  endif /* HAVE_BUILTIN_AVAILABLE */
1309
#elif defined(TCP_FASTOPEN_CONNECT) /* Linux >= 4.11 */
1310
0
      if(setsockopt(sockfd, IPPROTO_TCP, TCP_FASTOPEN_CONNECT,
1311
0
                    (void *)&optval, sizeof(optval)) < 0)
1312
0
        infof(data, "Failed to enable TCP Fast Open on fd %d", sockfd);
1313
1314
0
      rc = connect(sockfd, &addr.sa_addr, addr.addrlen);
1315
#elif defined(MSG_FASTOPEN) /* old Linux */
1316
      if(conn->given->flags & PROTOPT_SSL)
1317
        rc = connect(sockfd, &addr.sa_addr, addr.addrlen);
1318
      else
1319
        rc = 0; /* Do nothing */
1320
#endif
1321
0
    }
1322
0
    else {
1323
0
      rc = connect(sockfd, &addr.sa_addr, addr.addrlen);
1324
0
    }
1325
1326
0
    if(-1 == rc)
1327
0
      error = SOCKERRNO;
1328
#ifdef ENABLE_QUIC
1329
    else if(conn->transport == TRNSPRT_QUIC) {
1330
      /* pass in 'sockfd' separately since it hasn't been put into the
1331
         tempsock array at this point */
1332
      result = Curl_quic_connect(data, conn, sockfd, tempindex,
1333
                                 &addr.sa_addr, addr.addrlen);
1334
      if(result)
1335
        error = SOCKERRNO;
1336
    }
1337
#endif
1338
0
  }
1339
69.1k
  else {
1340
69.1k
    *sockp = sockfd;
1341
69.1k
    return CURLE_OK;
1342
69.1k
  }
1343
1344
0
  if(-1 == rc) {
1345
0
    switch(error) {
1346
0
    case EINPROGRESS:
1347
0
    case EWOULDBLOCK:
1348
0
#if defined(EAGAIN)
1349
#if (EAGAIN) != (EWOULDBLOCK)
1350
      /* On some platforms EAGAIN and EWOULDBLOCK are the
1351
       * same value, and on others they are different, hence
1352
       * the odd #if
1353
       */
1354
    case EAGAIN:
1355
#endif
1356
0
#endif
1357
0
      result = CURLE_OK;
1358
0
      break;
1359
1360
0
    default:
1361
      /* unknown error, fallthrough and try another address! */
1362
0
      infof(data, "Immediate connect fail for %s: %s",
1363
0
            ipaddress, Curl_strerror(error, buffer, sizeof(buffer)));
1364
0
      data->state.os_errno = error;
1365
1366
      /* connect failed */
1367
0
      Curl_closesocket(data, conn, sockfd);
1368
0
      result = CURLE_COULDNT_CONNECT;
1369
0
    }
1370
0
  }
1371
1372
0
  if(!result)
1373
0
    *sockp = sockfd;
1374
1375
0
  return result;
1376
0
}
1377
1378
/*
1379
 * TCP connect to the given host with timeout, proxy or remote doesn't matter.
1380
 * There might be more than one IP address to try out. Fill in the passed
1381
 * pointer with the connected socket.
1382
 */
1383
1384
CURLcode Curl_connecthost(struct Curl_easy *data,
1385
                          struct connectdata *conn,  /* context */
1386
                          const struct Curl_dns_entry *remotehost)
1387
69.3k
{
1388
69.3k
  CURLcode result = CURLE_COULDNT_CONNECT;
1389
69.3k
  int i;
1390
69.3k
  timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE);
1391
1392
69.3k
  if(timeout_ms < 0) {
1393
    /* a precaution, no need to continue if time already is up */
1394
0
    failf(data, "Connection time-out");
1395
0
    return CURLE_OPERATION_TIMEDOUT;
1396
0
  }
1397
1398
69.3k
  conn->num_addr = Curl_num_addresses(remotehost->addr);
1399
69.3k
  conn->tempaddr[0] = conn->tempaddr[1] = remotehost->addr;
1400
69.3k
  conn->tempsock[0] = conn->tempsock[1] = CURL_SOCKET_BAD;
1401
1402
  /* Max time for the next connection attempt */
1403
69.3k
  conn->timeoutms_per_addr[0] =
1404
69.3k
    conn->tempaddr[0]->ai_next == NULL ? timeout_ms : timeout_ms / 2;
1405
69.3k
  conn->timeoutms_per_addr[1] =
1406
69.3k
    conn->tempaddr[1]->ai_next == NULL ? timeout_ms : timeout_ms / 2;
1407
1408
69.3k
  if(conn->ip_version == CURL_IPRESOLVE_WHATEVER) {
1409
    /* any IP version is allowed */
1410
69.3k
    conn->tempfamily[0] = conn->tempaddr[0]?
1411
69.3k
      conn->tempaddr[0]->ai_family:0;
1412
69.3k
#ifdef ENABLE_IPV6
1413
69.3k
    conn->tempfamily[1] = conn->tempfamily[0] == AF_INET6 ?
1414
0
      AF_INET : AF_INET6;
1415
#else
1416
    conn->tempfamily[1] = AF_UNSPEC;
1417
#endif
1418
69.3k
  }
1419
0
  else {
1420
    /* only one IP version is allowed */
1421
0
    conn->tempfamily[0] = (conn->ip_version == CURL_IPRESOLVE_V4) ?
1422
0
      AF_INET :
1423
0
#ifdef ENABLE_IPV6
1424
0
      AF_INET6;
1425
#else
1426
      AF_UNSPEC;
1427
#endif
1428
0
    conn->tempfamily[1] = AF_UNSPEC;
1429
1430
0
    ainext(conn, 0, FALSE); /* find first address of the right type */
1431
0
  }
1432
1433
69.3k
  ainext(conn, 1, FALSE); /* assigns conn->tempaddr[1] accordingly */
1434
1435
69.3k
  DEBUGF(infof(data, "family0 == %s, family1 == %s",
1436
69.3k
               conn->tempfamily[0] == AF_INET ? "v4" : "v6",
1437
69.3k
               conn->tempfamily[1] == AF_INET ? "v4" : "v6"));
1438
1439
  /* get through the list in family order in case of quick failures */
1440
138k
  for(i = 0; (i < 2) && result; i++) {
1441
69.6k
    while(conn->tempaddr[i]) {
1442
69.3k
      result = singleipconnect(data, conn, conn->tempaddr[i], i);
1443
69.3k
      if(!result)
1444
69.1k
        break;
1445
160
      ainext(conn, i, TRUE);
1446
160
    }
1447
69.4k
  }
1448
69.3k
  if(result)
1449
160
    return result;
1450
1451
69.1k
  Curl_expire(data, data->set.happy_eyeballs_timeout,
1452
69.1k
              EXPIRE_HAPPY_EYEBALLS);
1453
1454
69.1k
  return CURLE_OK;
1455
69.3k
}
1456
1457
struct connfind {
1458
  long id_tofind;
1459
  struct connectdata *found;
1460
};
1461
1462
static int conn_is_conn(struct Curl_easy *data,
1463
                        struct connectdata *conn, void *param)
1464
0
{
1465
0
  struct connfind *f = (struct connfind *)param;
1466
0
  (void)data;
1467
0
  if(conn->connection_id == f->id_tofind) {
1468
0
    f->found = conn;
1469
0
    return 1;
1470
0
  }
1471
0
  return 0;
1472
0
}
1473
1474
/*
1475
 * Used to extract socket and connectdata struct for the most recent
1476
 * transfer on the given Curl_easy.
1477
 *
1478
 * The returned socket will be CURL_SOCKET_BAD in case of failure!
1479
 */
1480
curl_socket_t Curl_getconnectinfo(struct Curl_easy *data,
1481
                                  struct connectdata **connp)
1482
0
{
1483
0
  DEBUGASSERT(data);
1484
1485
  /* this works for an easy handle:
1486
   * - that has been used for curl_easy_perform()
1487
   * - that is associated with a multi handle, and whose connection
1488
   *   was detached with CURLOPT_CONNECT_ONLY
1489
   */
1490
0
  if((data->state.lastconnect_id != -1) && (data->multi_easy || data->multi)) {
1491
0
    struct connectdata *c;
1492
0
    struct connfind find;
1493
0
    find.id_tofind = data->state.lastconnect_id;
1494
0
    find.found = NULL;
1495
1496
0
    Curl_conncache_foreach(data,
1497
0
                           data->share && (data->share->specifier
1498
0
                           & (1<< CURL_LOCK_DATA_CONNECT))?
1499
0
                           &data->share->conn_cache:
1500
0
                           data->multi_easy?
1501
0
                           &data->multi_easy->conn_cache:
1502
0
                           &data->multi->conn_cache, &find, conn_is_conn);
1503
1504
0
    if(!find.found) {
1505
0
      data->state.lastconnect_id = -1;
1506
0
      return CURL_SOCKET_BAD;
1507
0
    }
1508
1509
0
    c = find.found;
1510
0
    if(connp)
1511
      /* only store this if the caller cares for it */
1512
0
      *connp = c;
1513
0
    return c->sock[FIRSTSOCKET];
1514
0
  }
1515
0
  return CURL_SOCKET_BAD;
1516
0
}
1517
1518
/*
1519
 * Check if a connection seems to be alive.
1520
 */
1521
bool Curl_connalive(struct connectdata *conn)
1522
143
{
1523
  /* First determine if ssl */
1524
143
  if(conn->ssl[FIRSTSOCKET].use) {
1525
    /* use the SSL context */
1526
0
    if(!Curl_ssl_check_cxn(conn))
1527
0
      return false;   /* FIN received */
1528
0
  }
1529
/* Minix 3.1 doesn't support any flags on recv; just assume socket is OK */
1530
143
#ifdef MSG_PEEK
1531
143
  else if(conn->sock[FIRSTSOCKET] == CURL_SOCKET_BAD)
1532
0
    return false;
1533
143
  else {
1534
    /* use the socket */
1535
143
    char buf;
1536
143
    if(recv((RECV_TYPE_ARG1)conn->sock[FIRSTSOCKET], (RECV_TYPE_ARG2)&buf,
1537
143
            (RECV_TYPE_ARG3)1, (RECV_TYPE_ARG4)MSG_PEEK) == 0) {
1538
141
      return false;   /* FIN received */
1539
141
    }
1540
143
  }
1541
2
#endif
1542
2
  return true;
1543
143
}
1544
1545
/*
1546
 * Close a socket.
1547
 *
1548
 * 'conn' can be NULL, beware!
1549
 */
1550
int Curl_closesocket(struct Curl_easy *data, struct connectdata *conn,
1551
                     curl_socket_t sock)
1552
69.1k
{
1553
69.1k
  if(conn && conn->fclosesocket) {
1554
0
    if((sock == conn->sock[SECONDARYSOCKET]) && conn->bits.sock_accepted)
1555
      /* if this socket matches the second socket, and that was created with
1556
         accept, then we MUST NOT call the callback but clear the accepted
1557
         status */
1558
0
      conn->bits.sock_accepted = FALSE;
1559
0
    else {
1560
0
      int rc;
1561
0
      Curl_multi_closed(data, sock);
1562
0
      Curl_set_in_callback(data, true);
1563
0
      rc = conn->fclosesocket(conn->closesocket_client, sock);
1564
0
      Curl_set_in_callback(data, false);
1565
0
      return rc;
1566
0
    }
1567
0
  }
1568
1569
69.1k
  if(conn)
1570
    /* tell the multi-socket code about this */
1571
69.1k
    Curl_multi_closed(data, sock);
1572
1573
69.1k
  sclose(sock);
1574
1575
69.1k
  return 0;
1576
69.1k
}
1577
1578
/*
1579
 * Create a socket based on info from 'conn' and 'ai'.
1580
 *
1581
 * 'addr' should be a pointer to the correct struct to get data back, or NULL.
1582
 * 'sockfd' must be a pointer to a socket descriptor.
1583
 *
1584
 * If the open socket callback is set, used that!
1585
 *
1586
 */
1587
CURLcode Curl_socket(struct Curl_easy *data,
1588
                     const struct Curl_addrinfo *ai,
1589
                     struct Curl_sockaddr_ex *addr,
1590
                     curl_socket_t *sockfd)
1591
69.3k
{
1592
69.3k
  struct connectdata *conn = data->conn;
1593
69.3k
  struct Curl_sockaddr_ex dummy;
1594
1595
69.3k
  if(!addr)
1596
    /* if the caller doesn't want info back, use a local temp copy */
1597
0
    addr = &dummy;
1598
1599
  /*
1600
   * The Curl_sockaddr_ex structure is basically libcurl's external API
1601
   * curl_sockaddr structure with enough space available to directly hold
1602
   * any protocol-specific address structures. The variable declared here
1603
   * will be used to pass / receive data to/from the fopensocket callback
1604
   * if this has been set, before that, it is initialized from parameters.
1605
   */
1606
1607
69.3k
  addr->family = ai->ai_family;
1608
69.3k
  switch(conn->transport) {
1609
68.2k
  case TRNSPRT_TCP:
1610
68.2k
    addr->socktype = SOCK_STREAM;
1611
68.2k
    addr->protocol = IPPROTO_TCP;
1612
68.2k
    break;
1613
0
  case TRNSPRT_UNIX:
1614
0
    addr->socktype = SOCK_STREAM;
1615
0
    addr->protocol = IPPROTO_IP;
1616
0
    break;
1617
1.05k
  default: /* UDP and QUIC */
1618
1.05k
    addr->socktype = SOCK_DGRAM;
1619
1.05k
    addr->protocol = IPPROTO_UDP;
1620
1.05k
    break;
1621
69.3k
  }
1622
69.3k
  addr->addrlen = ai->ai_addrlen;
1623
1624
69.3k
  if(addr->addrlen > sizeof(struct Curl_sockaddr_storage))
1625
0
     addr->addrlen = sizeof(struct Curl_sockaddr_storage);
1626
69.3k
  memcpy(&addr->sa_addr, ai->ai_addr, addr->addrlen);
1627
1628
69.3k
  if(data->set.fopensocket) {
1629
   /*
1630
    * If the opensocket callback is set, all the destination address
1631
    * information is passed to the callback. Depending on this information the
1632
    * callback may opt to abort the connection, this is indicated returning
1633
    * CURL_SOCKET_BAD; otherwise it will return a not-connected socket. When
1634
    * the callback returns a valid socket the destination address information
1635
    * might have been changed and this 'new' address will actually be used
1636
    * here to connect.
1637
    */
1638
69.3k
    Curl_set_in_callback(data, true);
1639
69.3k
    *sockfd = data->set.fopensocket(data->set.opensocket_client,
1640
69.3k
                                    CURLSOCKTYPE_IPCXN,
1641
69.3k
                                    (struct curl_sockaddr *)addr);
1642
69.3k
    Curl_set_in_callback(data, false);
1643
69.3k
  }
1644
0
  else
1645
    /* opensocket callback not set, so simply create the socket now */
1646
0
    *sockfd = socket(addr->family, addr->socktype, addr->protocol);
1647
1648
69.3k
  if(*sockfd == CURL_SOCKET_BAD)
1649
    /* no socket, no connection */
1650
160
    return CURLE_COULDNT_CONNECT;
1651
1652
69.1k
  if(conn->transport == TRNSPRT_QUIC) {
1653
    /* QUIC sockets need to be nonblocking */
1654
1
    (void)curlx_nonblock(*sockfd, TRUE);
1655
1
    switch(addr->family) {
1656
0
#if defined(__linux__) && defined(IP_MTU_DISCOVER)
1657
1
    case AF_INET: {
1658
1
      int val = IP_PMTUDISC_DO;
1659
1
      (void)setsockopt(*sockfd, IPPROTO_IP, IP_MTU_DISCOVER, &val,
1660
1
                       sizeof(val));
1661
1
      break;
1662
0
    }
1663
0
#endif
1664
0
#if defined(__linux__) && defined(IPV6_MTU_DISCOVER)
1665
0
    case AF_INET6: {
1666
0
      int val = IPV6_PMTUDISC_DO;
1667
0
      (void)setsockopt(*sockfd, IPPROTO_IPV6, IPV6_MTU_DISCOVER, &val,
1668
0
                       sizeof(val));
1669
0
      break;
1670
0
    }
1671
1
#endif
1672
1
    }
1673
1
  }
1674
1675
69.1k
#if defined(ENABLE_IPV6) && defined(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID)
1676
69.1k
  if(conn->scope_id && (addr->family == AF_INET6)) {
1677
0
    struct sockaddr_in6 * const sa6 = (void *)&addr->sa_addr;
1678
0
    sa6->sin6_scope_id = conn->scope_id;
1679
0
  }
1680
69.1k
#endif
1681
1682
69.1k
  return CURLE_OK;
1683
69.1k
}
1684
1685
/*
1686
 * Curl_conncontrol() marks streams or connection for closure.
1687
 */
1688
void Curl_conncontrol(struct connectdata *conn,
1689
                      int ctrl /* see defines in header */
1690
#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
1691
                      , const char *reason
1692
#endif
1693
  )
1694
326k
{
1695
  /* close if a connection, or a stream that isn't multiplexed. */
1696
  /* This function will be called both before and after this connection is
1697
     associated with a transfer. */
1698
326k
  bool closeit;
1699
326k
  DEBUGASSERT(conn);
1700
326k
#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
1701
326k
  (void)reason; /* useful for debugging */
1702
326k
#endif
1703
326k
  closeit = (ctrl == CONNCTRL_CONNECTION) ||
1704
326k
    ((ctrl == CONNCTRL_STREAM) && !(conn->handler->flags & PROTOPT_STREAM));
1705
326k
  if((ctrl == CONNCTRL_STREAM) &&
1706
326k
     (conn->handler->flags & PROTOPT_STREAM))
1707
1.87k
    ;
1708
324k
  else if((bit)closeit != conn->bits.close) {
1709
216k
    conn->bits.close = closeit; /* the only place in the source code that
1710
                                   should assign this bit */
1711
216k
  }
1712
326k
}
1713
1714
/* Data received can be cached at various levels, so check them all here. */
1715
bool Curl_conn_data_pending(struct connectdata *conn, int sockindex)
1716
0
{
1717
0
  int readable;
1718
0
  DEBUGASSERT(conn);
1719
1720
0
  if(Curl_ssl_data_pending(conn, sockindex) ||
1721
0
     Curl_recv_has_postponed_data(conn, sockindex))
1722
0
    return true;
1723
1724
0
  readable = SOCKET_READABLE(conn->sock[sockindex], 0);
1725
0
  return (readable > 0 && (readable & CURL_CSELECT_IN));
1726
0
}