Coverage Report

Created: 2026-09-04 07:16

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/curl/lib/url.c
Line
Count
Source
1
/***************************************************************************
2
 *                                  _   _ ____  _
3
 *  Project                     ___| | | |  _ \| |
4
 *                             / __| | | | |_) | |
5
 *                            | (__| |_| |  _ <| |___
6
 *                             \___|\___/|_| \_\_____|
7
 *
8
 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
9
 *
10
 * This software is licensed as described in the file COPYING, which
11
 * you should have received as part of this distribution. The terms
12
 * are also available at https://curl.se/docs/copyright.html.
13
 *
14
 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15
 * copies of the Software, and permit persons to whom the Software is
16
 * furnished to do so, under the terms of the COPYING file.
17
 *
18
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19
 * KIND, either express or implied.
20
 *
21
 * SPDX-License-Identifier: curl
22
 *
23
 ***************************************************************************/
24
#include "curl_setup.h"
25
26
#ifdef HAVE_NETINET_IN_H
27
#include <netinet/in.h>
28
#endif
29
#ifdef HAVE_NETDB_H
30
#include <netdb.h>
31
#endif
32
#ifdef HAVE_ARPA_INET_H
33
#include <arpa/inet.h>
34
#endif
35
#ifdef HAVE_NET_IF_H
36
#include <net/if.h>
37
#endif
38
#ifdef HAVE_IPHLPAPI_H
39
#include <Iphlpapi.h>
40
#endif
41
#ifdef HAVE_SYS_IOCTL_H
42
#include <sys/ioctl.h>
43
#endif
44
#ifdef HAVE_SYS_PARAM_H
45
#include <sys/param.h>
46
#endif
47
48
#ifdef __VMS
49
#include <in.h>
50
#include <inet.h>
51
#endif
52
53
#ifdef HAVE_SYS_UN_H
54
#include <sys/un.h>
55
#endif
56
57
#ifndef HAVE_SOCKET
58
#error "We cannot compile without socket() support"
59
#endif
60
61
#if defined(HAVE_IF_NAMETOINDEX) && defined(USE_WINSOCK)
62
#if defined(__MINGW32__) && (__MINGW64_VERSION_MAJOR <= 5)
63
#include <wincrypt.h>  /* workaround for old mingw-w64 missing to include it */
64
#endif
65
#include <iphlpapi.h>
66
#endif
67
68
#include "urldata.h"
69
#include "mime.h"
70
#include "bufref.h"
71
#include "vtls/vtls.h"
72
#include "vssh/vssh.h"
73
#include "transfer.h"
74
#include "curl_addrinfo.h"
75
#include "curl_trc.h"
76
#include "progress.h"
77
#include "cookie.h"
78
#include "strcase.h"
79
#include "escape.h"
80
#include "curl_share.h"
81
#include "http_digest.h"
82
#include "multiif.h"
83
#include "getinfo.h"
84
#include "pop3.h"
85
#include "urlapi-int.h"
86
#include "hsts.h"
87
#include "proxy.h"
88
#include "cfilters.h"
89
#include "idn.h"
90
#include "http_proxy.h"
91
#include "conncache.h"
92
#include "multihandle.h"
93
#include "curlx/strdup.h"
94
#include "setopt.h"
95
#include "altsvc.h"
96
#include "curlx/dynbuf.h"
97
#include "headers.h"
98
#include "curlx/strerr.h"
99
#include "curlx/strparse.h"
100
#include "peer.h"
101
102
/* Now for the protocols */
103
#include "ftp.h"
104
#include "dict.h"
105
#include "telnet.h"
106
#include "tftp.h"
107
#include "http.h"
108
#include "vauth/vauth.h"
109
#include "file.h"
110
#include "curl_ldap.h"
111
#include "vssh/ssh.h"
112
#include "imap.h"
113
#include "url.h"
114
#include "connect.h"
115
#include "gopher.h"
116
#include "mqtt.h"
117
#include "rtsp.h"
118
#include "smtp.h"
119
#include "ws.h"
120
121
/* Some parts of the code (e.g. chunked encoding) assume this buffer has more
122
 * than a few bytes to play with. Do not let it become too small or bad things
123
 * will happen.
124
 */
125
#if READBUFFER_SIZE < READBUFFER_MIN
126
# error READBUFFER_SIZE is too small
127
#endif
128
129
/*
130
 * get_protocol_family()
131
 *
132
 * This is used to return the protocol family for a given protocol.
133
 *
134
 * Parameters:
135
 *
136
 * 's'  [in]  - struct Curl_scheme pointer.
137
 *
138
 * Returns the family as a single bit protocol identifier.
139
 */
140
static curl_prot_t get_protocol_family(const struct Curl_scheme *s)
141
0
{
142
0
  DEBUGASSERT(s);
143
0
  DEBUGASSERT(s->family);
144
0
  return s->family;
145
0
}
146
147
void Curl_freeset(struct Curl_easy *data)
148
16.9k
{
149
  /* Free all dynamic strings stored in the data->set substructure. */
150
16.9k
  enum dupblob j;
151
152
16.9k
  CURL_EASY_STR_CLEAR0(data, STRING_PASSWORD);
153
16.9k
  CURL_EASY_STR_CLEAR0(data, STRING_KEY_PASSWD);
154
16.9k
  CURL_EASY_STR_CLEAR0(data, STRING_BEARER);
155
16.9k
#ifndef CURL_DISABLE_PROXY
156
16.9k
  CURL_EASY_STR_CLEAR0(data, STRING_PROXYPASSWORD);
157
16.9k
  CURL_EASY_STR_CLEAR0(data, STRING_KEY_PASSWD_PROXY);
158
16.9k
#endif
159
16.9k
  Curl_u8_strset_clear(&data->set.strings);
160
16.9k
  curlx_safefree(data->set.str_copypostfields);
161
162
152k
  for(j = (enum dupblob)0; j < BLOB_LAST; j++) {
163
135k
    curlx_safefree(data->set.blobs[j]);
164
135k
  }
165
166
16.9k
  Curl_bufref_free(&data->state.referer);
167
16.9k
  Curl_bufref_free(&data->state.url);
168
169
16.9k
#if !defined(CURL_DISABLE_MIME) || !defined(CURL_DISABLE_FORM_API)
170
16.9k
  Curl_mime_cleanpart(data->set.mimepostp);
171
16.9k
  curlx_safefree(data->set.mimepostp);
172
16.9k
#endif
173
174
16.9k
#ifndef CURL_DISABLE_COOKIES
175
16.9k
  curl_slist_free_all(data->state.cookielist);
176
16.9k
  data->state.cookielist = NULL;
177
16.9k
#endif
178
16.9k
}
179
180
/* free the URL pieces */
181
static void up_free(struct Curl_easy *data)
182
25.3k
{
183
25.3k
  struct urlpieces *up = &data->state.up;
184
25.3k
  curlx_safefree(up->options);
185
25.3k
  curlx_safefree(up->path);
186
25.3k
  curlx_safefree(up->query);
187
25.3k
  curl_url_cleanup(data->state.uh);
188
25.3k
  data->state.uh = NULL;
189
25.3k
}
190
191
/*
192
 * This is the internal function curl_easy_cleanup() calls. This should
193
 * cleanup and free all resources associated with this Curl_easy.
194
 *
195
 * We ignore SIGPIPE when this is called from curl_easy_cleanup.
196
 */
197
CURLcode Curl_close(struct Curl_easy **datap)
198
16.9k
{
199
16.9k
  struct Curl_easy *data;
200
201
16.9k
  if(!datap || !*datap)
202
0
    return CURLE_OK;
203
204
16.9k
  data = *datap;
205
16.9k
  *datap = NULL;
206
207
16.9k
  if(!data->state.internal && data->multi) {
208
    /* This handle is still part of a multi handle, take care of this first
209
       and detach this handle from there.
210
       This detaches the connection. */
211
0
    Curl_multi_remove_handle(data->multi, data);
212
0
  }
213
16.9k
  else {
214
    /* Detach connection if any is left. This should not be normal, but can be
215
       the case for example with CONNECT_ONLY + recv/send (test 556) */
216
16.9k
    Curl_detach_connection(data);
217
16.9k
    if(!data->state.internal && data->multi_easy) {
218
      /* when curl_easy_perform() is used, it creates its own multi handle to
219
         use and this is the one */
220
0
      curl_multi_cleanup(data->multi_easy);
221
0
      data->multi_easy = NULL;
222
0
    }
223
16.9k
  }
224
16.9k
  DEBUGASSERT(!data->conn || data->state.internal);
225
226
16.9k
  Curl_expire_clear_all(data); /* shut off any timers left */
227
228
16.9k
  if(data->state.rangestringalloc)
229
96
    curlx_free(data->state.range);
230
231
  /* release any resolve information this transfer kept */
232
16.9k
  Curl_resolv_destroy_all(data);
233
234
16.9k
  data->set.verbose = FALSE; /* no more calls to DEBUGFUNCTION */
235
16.9k
  data->magic = 0; /* force a clear AFTER the possibly enforced removal from
236
                    * the multi handle and async dns shutdown. The multi
237
                    * handle might check the magic and so might any
238
                    * DEBUGFUNCTION invoked for tracing */
239
240
  /* freed here in case DONE was not called */
241
16.9k
  Curl_req_free(&data->req, data);
242
243
  /* Close down all open SSL info and sessions */
244
16.9k
  Curl_ssl_close_all(data);
245
16.9k
  Curl_peer_unlink(&data->state.origin);
246
16.9k
  Curl_peer_unlink(&data->state.initial_origin);
247
16.9k
  Curl_ssl_free_certinfo(data);
248
249
16.9k
  Curl_bufref_free(&data->state.referer);
250
251
16.9k
  up_free(data);
252
16.9k
  curlx_dyn_free(&data->state.headerb);
253
16.9k
  Curl_flush_cookies(data, TRUE);
254
16.9k
#ifndef CURL_DISABLE_ALTSVC
255
16.9k
  Curl_altsvc_save(data, data->asi, CURL_EASY_STR(data, STRING_ALTSVC));
256
16.9k
  Curl_altsvc_cleanup(&data->asi);
257
16.9k
#endif
258
16.9k
#ifndef CURL_DISABLE_HSTS
259
16.9k
  Curl_hsts_save(data, data->hsts, CURL_EASY_STR(data, STRING_HSTS));
260
16.9k
  if(!data->share || !data->share->hsts)
261
16.9k
    Curl_hsts_cleanup(&data->hsts);
262
16.9k
  curl_slist_free_all(data->state.hstslist); /* clean up list */
263
16.9k
#endif
264
16.9k
#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_DIGEST_AUTH)
265
16.9k
  Curl_http_auth_cleanup_digest(data);
266
16.9k
#endif
267
16.9k
  curlx_safefree(data->state.most_recent_ftp_entrypath);
268
16.9k
  curlx_safefree(data->info.contenttype);
269
16.9k
  curlx_safefree(data->info.wouldredirect);
270
271
  /* No longer a dirty share, if it exists */
272
16.9k
  if(Curl_share_easy_unlink(data))
273
16.9k
    DEBUGASSERT(0);
274
275
16.9k
  Curl_hash_destroy(&data->meta_hash);
276
16.9k
  Curl_creds_unlink(&data->state.creds);
277
16.9k
#ifndef CURL_DISABLE_HTTP
278
16.9k
  curlx_safefree(data->state.rangeline);
279
16.9k
  curlx_safefree(data->state.http_host);
280
16.9k
#endif
281
16.9k
#ifndef CURL_DISABLE_COOKIES
282
16.9k
  curlx_safefree(data->req.cookiehost);
283
16.9k
#endif
284
285
16.9k
#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_FORM_API)
286
16.9k
  Curl_mime_cleanpart(data->state.formp);
287
16.9k
  curlx_safefree(data->state.formp);
288
16.9k
#endif
289
290
  /* destruct wildcard structures if it is needed */
291
16.9k
  Curl_wildcard_dtor(&data->wildcard);
292
16.9k
  Curl_freeset(data);
293
16.9k
  Curl_headers_cleanup(data);
294
16.9k
  Curl_netrc_cleanup(&data->state.netrc);
295
16.9k
#ifndef CURL_DISABLE_DIGEST_AUTH
296
16.9k
  curlx_free(data->state.envproxy);
297
16.9k
#endif
298
16.9k
  Curl_ssl_config_cleanup(&data->set.ssl.primary);
299
16.9k
#ifndef CURL_DISABLE_PROXY
300
16.9k
  Curl_ssl_config_cleanup(&data->set.proxy_ssl.primary);
301
16.9k
#endif
302
16.9k
  curlx_memzero(data, sizeof(*data));
303
16.9k
  curlx_free(data);
304
16.9k
  return CURLE_OK;
305
16.9k
}
306
307
/*
308
 * Initialize the UserDefined fields within a Curl_easy.
309
 * This may be safely called on a new or existing Curl_easy.
310
 */
311
void Curl_init_userdefined(struct Curl_easy *data)
312
16.9k
{
313
16.9k
  struct UserDefined *set = &data->set;
314
315
16.9k
  set->out = stdout;  /* default output to stdout */
316
16.9k
  set->in_set = stdin;  /* default input from stdin */
317
16.9k
  set->err = stderr;  /* default stderr to stderr */
318
319
16.9k
  Curl_u8_strset_init(&data->set.strings);
320
321
16.9k
#if defined(__clang__) && __clang_major__ >= 16
322
16.9k
#pragma clang diagnostic push
323
16.9k
#pragma clang diagnostic ignored "-Wcast-function-type-strict"
324
16.9k
#endif
325
  /* use fwrite as default function to store output */
326
16.9k
  set->fwrite_func = (curl_write_callback)fwrite;
327
328
  /* use fread as default function to read input */
329
16.9k
  set->fread_func_set = (curl_read_callback)fread;
330
16.9k
#if defined(__clang__) && __clang_major__ >= 16
331
16.9k
#pragma clang diagnostic pop
332
16.9k
#endif
333
16.9k
  set->is_fread_set = 0;
334
335
16.9k
  set->seek_client = ZERO_NULL;
336
337
16.9k
  set->filesize = -1;        /* we do not know the size */
338
16.9k
  set->postfieldsize = -1;   /* unknown size */
339
16.9k
  set->maxredirs = 30;       /* sensible default */
340
341
16.9k
  set->method = HTTPREQ_GET; /* Default HTTP request */
342
16.9k
#ifndef CURL_DISABLE_RTSP
343
16.9k
  set->rtspreq = RTSPREQ_OPTIONS; /* Default RTSP request */
344
16.9k
#endif
345
16.9k
#ifndef CURL_DISABLE_FTP
346
16.9k
  set->ftp_use_epsv = TRUE;   /* FTP defaults to EPSV operations */
347
16.9k
  set->ftp_use_eprt = TRUE;   /* FTP defaults to EPRT operations */
348
16.9k
  set->ftp_use_pret = FALSE;  /* mainly useful for drftpd servers */
349
16.9k
  set->ftp_filemethod = FTPFILE_MULTICWD;
350
16.9k
  set->ftp_skip_ip = TRUE;    /* skip PASV IP by default */
351
16.9k
#endif
352
16.9k
  set->dns_cache_timeout_ms = 60000; /* Timeout every 60 seconds by default */
353
354
  /* Timeout every 24 hours by default */
355
16.9k
  set->general_ssl.ca_cache_timeout = 24 * 60 * 60;
356
357
16.9k
  set->httpauth = CURLAUTH_BASIC;  /* defaults to basic */
358
359
16.9k
  Curl_ssl_config_init(&data->set.ssl.primary);
360
16.9k
#ifndef CURL_DISABLE_PROXY
361
16.9k
  Curl_ssl_config_init(&data->set.proxy_ssl.primary);
362
16.9k
  set->proxyport = 0;
363
16.9k
  set->proxytype = CURLPROXY_HTTP; /* defaults to HTTP proxy */
364
16.9k
  set->proxyauth = CURLAUTH_BASIC; /* defaults to basic */
365
  /* SOCKS5 proxy auth defaults to username/password + GSS-API */
366
16.9k
  set->socks5auth = CURLAUTH_BASIC | CURLAUTH_GSSAPI;
367
16.9k
#endif
368
369
16.9k
#ifndef CURL_DISABLE_DOH
370
16.9k
  set->doh_verifyhost = TRUE;
371
16.9k
  set->doh_verifypeer = TRUE;
372
16.9k
#endif
373
#ifdef USE_SSH
374
  /* defaults to any auth type */
375
  set->ssh_auth_types = CURLSSH_AUTH_DEFAULT;
376
  set->new_directory_perms = 0755; /* Default permissions */
377
#endif
378
379
16.9k
  set->new_file_perms = 0644;    /* Default permissions */
380
16.9k
  set->allowed_protocols = (curl_prot_t)CURLPROTO_64ALL;
381
16.9k
  set->redir_protocols = CURLPROTO_REDIR;
382
383
#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
384
  /*
385
   * disallow unprotected protection negotiation NEC reference implementation
386
   * seem not to follow rfc1961 section 4.3/4.4
387
   */
388
  set->socks5_gssapi_nec = FALSE;
389
#endif
390
391
  /* set default minimum TLS version */
392
16.9k
#ifdef USE_SSL
393
16.9k
  Curl_setopt_SSLVERSION(data, CURLOPT_SSLVERSION, CURL_SSLVERSION_DEFAULT);
394
16.9k
#ifndef CURL_DISABLE_PROXY
395
16.9k
  Curl_setopt_SSLVERSION(data, CURLOPT_PROXY_SSLVERSION,
396
16.9k
                         CURL_SSLVERSION_DEFAULT);
397
16.9k
#endif
398
16.9k
#endif
399
16.9k
#ifndef CURL_DISABLE_FTP
400
16.9k
  set->wildcard_enabled = FALSE;
401
16.9k
  set->chunk_bgn = ZERO_NULL;
402
16.9k
  set->chunk_end = ZERO_NULL;
403
16.9k
  set->fnmatch = ZERO_NULL;
404
16.9k
#endif
405
16.9k
  set->tcp_keepalive = FALSE;
406
16.9k
  set->tcp_keepintvl = 60;
407
16.9k
  set->tcp_keepidle = 60;
408
16.9k
  set->tcp_keepcnt = 9;
409
16.9k
  set->tcp_fastopen = FALSE;
410
16.9k
  set->tcp_nodelay = TRUE;
411
16.9k
  set->ssl_enable_alpn = TRUE;
412
16.9k
  set->expect_100_timeout = 1000L; /* Wait for a second by default. */
413
16.9k
  set->sep_headers = TRUE; /* separated header lists by default */
414
16.9k
  set->buffer_size = READBUFFER_SIZE;
415
16.9k
  set->upload_buffer_size = UPLOADBUFFER_DEFAULT;
416
16.9k
  set->upload_flags = CURLULFLAG_SEEN;
417
16.9k
  set->happy_eyeballs_timeout = CURL_HET_DEFAULT;
418
16.9k
  set->upkeep_interval_ms = CURL_UPKEEP_INTERVAL_DEFAULT;
419
16.9k
  set->maxconnects = DEFAULT_CONNCACHE_SIZE; /* for easy handles */
420
16.9k
  set->conn_max_idle_ms = 118 * 1000;
421
16.9k
  set->conn_max_age_ms = 24 * 3600 * 1000;
422
16.9k
  set->http09_allowed = FALSE;
423
16.9k
  set->httpwant = CURL_HTTP_VERSION_NONE;
424
16.9k
#if defined(USE_HTTP2) || defined(USE_HTTP3)
425
16.9k
  set->weight = 0;
426
16.9k
#endif
427
16.9k
  set->quick_exit = 0L;
428
16.9k
#ifndef CURL_DISABLE_WEBSOCKETS
429
16.9k
  set->ws_raw_mode = FALSE;
430
16.9k
  set->ws_no_auto_pong = FALSE;
431
16.9k
#endif
432
16.9k
}
433
434
/* easy->meta_hash destructor. Should never be called as elements
435
 * MUST be added with their own destructor */
436
static void easy_meta_freeentry(void *p)
437
0
{
438
0
  (void)p;
439
  /* Always FALSE. Cannot use a 0 assert here since compilers
440
   * are not in agreement if they then want a NORETURN attribute or
441
   * not. *sigh* */
442
0
  DEBUGASSERT(!p);
443
0
}
444
445
/**
446
 * Curl_open()
447
 *
448
 * @param curl is a pointer to a Curl_easy pointer that gets set by this
449
 * function.
450
 * @return CURLcode
451
 */
452
CURLcode Curl_open(struct Curl_easy **curl)
453
16.9k
{
454
16.9k
  struct Curl_easy *data;
455
456
  /* simple start-up: alloc the struct, init it with zeroes and return */
457
16.9k
  data = curlx_calloc(1, sizeof(struct Curl_easy));
458
16.9k
  if(!data) {
459
    /* this is a serious error */
460
0
    DEBUGF(curl_mfprintf(stderr, "Error: calloc of Curl_easy failed\n"));
461
0
    return CURLE_OUT_OF_MEMORY;
462
0
  }
463
464
16.9k
  data->magic = CURLEASY_MAGIC_NUMBER;
465
  /* most recent connection is not yet defined */
466
16.9k
  data->state.lastconnect_id = -1;
467
  /* and not assigned an id yet */
468
16.9k
  data->id = -1;
469
16.9k
  data->mid = UINT32_MAX;
470
16.9k
  data->master_mid = UINT32_MAX;
471
16.9k
  data->progress.hide = TRUE;
472
473
16.9k
  Curl_hash_init(&data->meta_hash, 23,
474
16.9k
                 Curl_hash_str, curlx_str_key_compare, easy_meta_freeentry);
475
16.9k
  DEBUGASSERT(STRING_LAST <= UINT8_MAX);
476
16.9k
  Curl_u8_strset_init(&data->set.strings);
477
16.9k
  curlx_dyn_init(&data->state.headerb, CURL_MAX_HTTP_HEADER);
478
16.9k
  Curl_bufref_init(&data->state.url);
479
16.9k
  Curl_bufref_init(&data->state.referer);
480
16.9k
  Curl_req_init(&data->req);
481
16.9k
  Curl_initinfo(data);
482
16.9k
#ifndef CURL_DISABLE_HTTP
483
16.9k
  Curl_llist_init(&data->state.httphdrs, NULL);
484
16.9k
#endif
485
16.9k
  Curl_netrc_init(&data->state.netrc);
486
16.9k
  Curl_init_userdefined(data);
487
488
16.9k
  *curl = data;
489
16.9k
  return CURLE_OK;
490
16.9k
}
491
492
void Curl_conn_free(struct Curl_easy *data, struct connectdata *conn)
493
4.88k
{
494
4.88k
  int8_t i;
495
496
4.88k
  DEBUGASSERT(conn);
497
498
4.88k
  if(conn->scheme && conn->scheme->run->disconnect &&
499
0
     !conn->bits.shutdown_handler)
500
0
    conn->scheme->run->disconnect(data, conn, TRUE);
501
502
14.6k
  for(i = 0; i < (int8_t)CURL_ARRAYSIZE(conn->cfilter); ++i) {
503
9.76k
    Curl_conn_cf_discard_all(data, conn, i);
504
9.76k
  }
505
506
4.88k
#ifndef CURL_DISABLE_PROXY
507
4.88k
  Curl_peer_unlink(&conn->http_proxy.peer);
508
4.88k
  Curl_peer_unlink(&conn->socks_proxy.peer);
509
4.88k
  Curl_creds_unlink(&conn->http_proxy.creds);
510
4.88k
  Curl_creds_unlink(&conn->socks_proxy.creds);
511
4.88k
#endif
512
4.88k
  Curl_creds_unlink(&conn->creds);
513
4.88k
  Curl_peer_unlink(&conn->creds_origin);
514
4.88k
  curlx_safefree(conn->options);
515
4.88k
  curlx_safefree(conn->localdev);
516
4.88k
  Curl_ssl_conn_config_cleanup(conn);
517
518
4.88k
  curlx_safefree(conn->destination);
519
4.88k
  Curl_hash_destroy(&conn->meta_hash);
520
4.88k
  Curl_peer_unlink(&conn->origin);
521
4.88k
  Curl_peer_unlink(&conn->via_peer);
522
4.88k
  Curl_peer_unlink(&conn->origin2);
523
4.88k
  Curl_peer_unlink(&conn->via_peer2);
524
525
4.88k
  curlx_free(conn); /* free all the connection oriented data */
526
4.88k
}
527
528
/*
529
 * xfer_may_multiplex()
530
 *
531
 * Return a TRUE, iff the transfer can be done over an (appropriate)
532
 * multiplexed connection.
533
 */
534
static bool xfer_may_multiplex(const struct Curl_easy *data,
535
                               const struct connectdata *conn)
536
4.86k
{
537
4.86k
#ifndef CURL_DISABLE_HTTP
538
  /* If an HTTP protocol and multiplexing is enabled */
539
4.86k
  if((conn->scheme->protocol & PROTO_FAMILY_HTTP) &&
540
4.86k
     (!conn->bits.protoconnstart || !conn->bits.close)) {
541
542
4.86k
    if(Curl_multiplex_wanted(data->multi) &&
543
4.86k
       (data->state.http_neg.allowed & (CURL_HTTP_V2x | CURL_HTTP_V3x)))
544
      /* allows HTTP/2 or newer */
545
0
      return TRUE;
546
4.86k
  }
547
#else
548
  (void)data;
549
  (void)conn;
550
#endif
551
4.86k
  return FALSE;
552
4.86k
}
553
554
#ifndef CURL_DISABLE_PROXY
555
static bool proxy_info_matches(const struct proxy_info *data,
556
                               const struct proxy_info *needle)
557
0
{
558
0
  if((data->proxytype == needle->proxytype) &&
559
0
     Curl_peer_same_destination(data->peer, needle->peer) &&
560
0
     Curl_creds_same(data->creds, needle->creds)) {
561
0
    return TRUE;
562
0
  }
563
0
  return FALSE;
564
0
}
565
#endif
566
567
#ifdef USE_SSH
568
static bool ssh_config_matches(struct connectdata *one,
569
                               struct connectdata *two)
570
{
571
  struct ssh_conn *sshc1, *sshc2;
572
573
  sshc1 = Curl_conn_meta_get(one, CURL_META_SSH_CONN);
574
  sshc2 = Curl_conn_meta_get(two, CURL_META_SSH_CONN);
575
  return sshc1 && sshc2 && Curl_safecmp(sshc1->priv_key, sshc2->priv_key) &&
576
         Curl_safecmp(sshc1->pub_key, sshc2->pub_key);
577
}
578
#endif
579
580
struct url_conn_match {
581
  struct connectdata *found;
582
  struct Curl_easy *data;
583
  struct connectdata *needle;
584
  struct curltime now;
585
  BIT(may_multiplex);
586
  BIT(want_ntlm_http);
587
  BIT(want_proxy_ntlm_http);
588
  BIT(want_nego_http);
589
  BIT(want_proxy_nego_http);
590
  BIT(may_tls); /* May upgrade clear-text connection to TLS, can only reuse
591
                 * connections that have matching TLS configuration.
592
                 * Always TRUE if `req_tls` is TRUE. */
593
  BIT(require_tls); /* Requires TLS use from a clear-text start, can only
594
                 * reuse connections that have TLS. */
595
  BIT(wait_pipe);
596
  BIT(force_reuse);
597
  BIT(seen_pending_conn);
598
  BIT(seen_single_use_conn);
599
  BIT(seen_multiplex_conn);
600
};
601
602
static bool url_match_connect_config(struct connectdata *conn,
603
                                     struct url_conn_match *m)
604
0
{
605
  /* connect-only or to-be-closed connections will not be reused */
606
0
  if(conn->bits.connect_only || conn->bits.close || conn->bits.no_reuse)
607
0
    return FALSE;
608
609
  /* ip_version must match */
610
0
  if(m->data->set.ipver != CURL_IPRESOLVE_WHATEVER &&
611
0
     m->data->set.ipver != conn->ip_version)
612
0
    return FALSE;
613
614
0
  if((m->needle->localdev || m->needle->localport) &&
615
    /* If we are bound to a specific local end (IP+port), we must not reuse a
616
       random other one, although if we did not ask for a particular one we
617
       can reuse one that was bound.
618
619
       This comparison is a bit rough and too strict. Since the input
620
       parameters can be specified in numerous ways and still end up the same
621
       it would take a lot of processing to make it really accurate. Instead,
622
       this matching will assume that reuses of bound connections will most
623
       likely also reuse the exact same binding parameters and missing out a
624
       few edge cases should not hurt anyone much. */
625
0
    ((conn->localport != m->needle->localport) ||
626
0
     (conn->localportrange != m->needle->localportrange) ||
627
0
     (m->needle->localdev &&
628
0
      (!conn->localdev || strcmp(conn->localdev, m->needle->localdev)))))
629
0
    return FALSE;
630
631
0
  if(!m->needle->via_peer != !conn->via_peer)
632
    /* do not mix connections that use the "connect to host" feature and
633
     * connections that do not use this feature */
634
0
    return FALSE;
635
636
0
  return TRUE;
637
0
}
638
639
static bool url_match_fully_connected(struct connectdata *conn,
640
                                      struct url_conn_match *m)
641
0
{
642
0
  if(!Curl_conn_is_connected(conn, FIRSTSOCKET) ||
643
0
     conn->bits.upgrade_in_progress) {
644
    /* Not yet connected, or a protocol upgrade is in progress. The later
645
     * happens for HTTP/2 Upgrade: requests that need a response. */
646
0
    if(m->may_multiplex) {
647
0
      m->seen_pending_conn = TRUE;
648
      /* Do not pick a connection that has not connected yet */
649
0
      infof(m->data, "Connection #%" FMT_OFF_T
650
0
            " is not open enough, cannot reuse", conn->connection_id);
651
0
    }
652
    /* Do not pick a connection that has not connected yet */
653
0
    return FALSE;
654
0
  }
655
0
  return TRUE;
656
0
}
657
658
static bool url_match_multi(struct connectdata *conn,
659
                            struct url_conn_match *m)
660
0
{
661
0
  if(CONN_INUSE(conn)) {
662
0
    DEBUGASSERT(conn->attached_multi);
663
0
    if(conn->attached_multi != m->data->multi)
664
0
      return FALSE;
665
0
  }
666
0
  return TRUE;
667
0
}
668
669
static bool url_match_multiplex_needs(struct connectdata *conn,
670
                                      struct url_conn_match *m)
671
0
{
672
0
  if(CONN_INUSE(conn)) {
673
0
    if(!conn->bits.multiplex) {
674
      /* conn busy and conn cannot take more transfers */
675
0
      m->seen_single_use_conn = TRUE;
676
0
      return FALSE;
677
0
    }
678
0
    m->seen_multiplex_conn = TRUE;
679
0
    if(!m->may_multiplex || !url_match_multi(conn, m))
680
      /* conn busy and transfer cannot be multiplexed */
681
0
      return FALSE;
682
0
  }
683
0
  return TRUE;
684
0
}
685
686
static bool url_match_multiplex_limits(struct connectdata *conn,
687
                                       struct url_conn_match *m)
688
0
{
689
0
  if(CONN_INUSE(conn) && m->may_multiplex) {
690
0
    DEBUGASSERT(conn->bits.multiplex);
691
    /* If multiplexed, make sure we do not go over concurrency limit */
692
0
    if(conn->attached_xfers >=
693
0
            Curl_multi_max_concurrent_streams(m->data->multi)) {
694
0
      infof(m->data, "client side MAX_CONCURRENT_STREAMS reached"
695
0
            ", skip (%u)", conn->attached_xfers);
696
0
      return FALSE;
697
0
    }
698
0
    if(conn->attached_xfers >=
699
0
       Curl_conn_get_max_concurrent(m->data, conn, FIRSTSOCKET)) {
700
0
      infof(m->data, "MAX_CONCURRENT_STREAMS reached, skip (%u)",
701
0
            conn->attached_xfers);
702
0
      return FALSE;
703
0
    }
704
    /* When not multiplexed, we have a match here! */
705
0
    infof(m->data, "Multiplexed connection found");
706
0
  }
707
0
  return TRUE;
708
0
}
709
710
static bool url_match_ssl_use(struct connectdata *conn,
711
                              struct url_conn_match *m)
712
0
{
713
0
  if(m->needle->scheme->flags & PROTOPT_SSL) {
714
    /* We are looking for SSL, if `conn` does not do it, not a match. */
715
0
    if(!Curl_conn_is_ssl(conn, FIRSTSOCKET))
716
0
      return FALSE;
717
0
  }
718
0
  else if(Curl_conn_is_ssl(conn, FIRSTSOCKET)) {
719
    /* If the protocol does not allow reuse of SSL connections OR
720
       is of another protocol family, not a match. */
721
0
    if(!(m->needle->scheme->flags & PROTOPT_SSL_REUSE) ||
722
0
       (get_protocol_family(conn->scheme) != m->needle->scheme->protocol))
723
0
      return FALSE;
724
    /* We may reuse this as an auto-TLS upgrade, but only if the SSL
725
     * config parameters match. */
726
0
    if(!Curl_ssl_conn_config_match(m->data, conn, FALSE))
727
0
      return FALSE;
728
0
  }
729
0
  else if(m->require_tls)
730
    /* a clear-text STARTTLS protocol with required TLS */
731
0
    return FALSE;
732
0
  return TRUE;
733
0
}
734
735
#ifndef CURL_DISABLE_PROXY
736
static bool url_match_proxy_use(struct connectdata *conn,
737
                                struct url_conn_match *m)
738
0
{
739
0
  if(m->needle->bits.origin_is_proxy != conn->bits.origin_is_proxy)
740
0
    return FALSE;
741
742
0
  if(!proxy_info_matches(&m->needle->socks_proxy, &conn->socks_proxy))
743
0
    return FALSE;
744
745
0
  if(!proxy_info_matches(&m->needle->http_proxy, &conn->http_proxy))
746
0
    return FALSE;
747
748
0
  if(CURL_PROXY_IS_HTTPS(m->needle->http_proxy.proxytype)) {
749
    /* https proxies come in different types, http/1.1, h2, ... */
750
    /* match SSL config to proxy */
751
0
    if(!Curl_ssl_conn_config_match(m->data, conn, TRUE)) {
752
0
      DEBUGF(infof(m->data,
753
0
                   "Connection #%" FMT_OFF_T
754
0
                   " has different SSL proxy parameters, cannot reuse",
755
0
                   conn->connection_id));
756
0
      return FALSE;
757
0
    }
758
    /* the SSL config to the server, which may apply here is checked
759
     * further below */
760
0
  }
761
0
  return TRUE;
762
0
}
763
#else
764
#define url_match_proxy_use(c, m) ((void)(c), (void)(m), TRUE)
765
#endif
766
767
#ifndef CURL_DISABLE_HTTP
768
static bool url_match_http_multiplex(struct connectdata *conn,
769
                                     struct url_conn_match *m)
770
0
{
771
0
  if(m->may_multiplex &&
772
0
     (m->data->state.http_neg.allowed & (CURL_HTTP_V2x | CURL_HTTP_V3x)) &&
773
0
     (m->needle->scheme->protocol & CURLPROTO_HTTP) &&
774
0
     !conn->httpversion_seen) {
775
0
    if(m->data->set.pipewait) {
776
0
      infof(m->data, "Server upgrade does not support multiplex yet, wait");
777
0
      m->found = NULL;
778
0
      m->wait_pipe = TRUE;
779
0
      return TRUE; /* stop searching, we want to wait */
780
0
    }
781
0
    infof(m->data, "Server upgrade cannot be used");
782
0
    return FALSE;
783
0
  }
784
0
  return TRUE;
785
0
}
786
787
static bool url_match_http_version(struct connectdata *conn,
788
                                   struct url_conn_match *m)
789
0
{
790
  /* If looking for HTTP and the HTTP versions allowed do not include
791
   * the HTTP version of conn, continue looking. */
792
0
  if((m->needle->scheme->protocol & PROTO_FAMILY_HTTP)) {
793
0
    switch(Curl_conn_http_version(m->data, conn)) {
794
0
    case 30:
795
0
      if(!(m->data->state.http_neg.allowed & CURL_HTTP_V3x)) {
796
0
        DEBUGF(infof(m->data, "not reusing conn #%" CURL_FORMAT_CURL_OFF_T
797
0
                     ", we do not want h3", conn->connection_id));
798
0
        return FALSE;
799
0
      }
800
0
      break;
801
0
    case 20:
802
0
      if(!(m->data->state.http_neg.allowed & CURL_HTTP_V2x)) {
803
0
        DEBUGF(infof(m->data, "not reusing conn #%" CURL_FORMAT_CURL_OFF_T
804
0
                     ", we do not want h2", conn->connection_id));
805
0
        return FALSE;
806
0
      }
807
0
      break;
808
0
    default:
809
0
      if(!(m->data->state.http_neg.allowed & CURL_HTTP_V1x)) {
810
0
        DEBUGF(infof(m->data, "not reusing conn #%" CURL_FORMAT_CURL_OFF_T
811
0
                     ", we do not want h1", conn->connection_id));
812
0
        return FALSE;
813
0
      }
814
0
      break;
815
0
    }
816
0
  }
817
0
  return TRUE;
818
0
}
819
#else
820
#define url_match_http_multiplex(c, m) ((void)(c), (void)(m), TRUE)
821
#define url_match_http_version(c, m)   ((void)(c), (void)(m), TRUE)
822
#endif
823
824
static bool url_match_proto_config(struct connectdata *conn,
825
                                   struct url_conn_match *m)
826
0
{
827
0
  if(!url_match_http_version(conn, m))
828
0
    return FALSE;
829
830
#ifdef USE_SSH
831
  if(get_protocol_family(m->needle->scheme) & PROTO_FAMILY_SSH) {
832
    if(!ssh_config_matches(m->needle, conn))
833
      return FALSE;
834
  }
835
#endif
836
0
#ifndef CURL_DISABLE_FTP
837
0
  else if(get_protocol_family(m->needle->scheme) & PROTO_FAMILY_FTP) {
838
0
    if(!Curl_ftp_conns_match(m->needle, conn))
839
0
      return FALSE;
840
0
  }
841
0
#endif
842
0
  return TRUE;
843
0
}
844
845
static bool url_match_auth(struct connectdata *conn,
846
                           struct url_conn_match *m)
847
0
{
848
0
  if(!Curl_creds_same(m->needle->creds, conn->creds)) {
849
0
    if(m->needle->creds)
850
0
      return FALSE;
851
0
    if(!Curl_creds_same(m->data->state.creds, conn->creds))
852
0
      return FALSE;
853
0
  }
854
#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
855
  /* GSS delegation differences do not actually affect every connection and
856
     auth method, but this check takes precaution before efficiency */
857
  if(m->needle->gssapi_delegation != conn->gssapi_delegation)
858
    return FALSE;
859
#endif
860
861
0
  return TRUE;
862
0
}
863
864
static bool url_match_destination(struct connectdata *conn,
865
                                  struct url_conn_match *m)
866
0
{
867
  /* Different connect-to peers never match */
868
0
  if(!Curl_peer_same_destination(m->needle->via_peer, conn->via_peer))
869
0
    return FALSE;
870
871
0
  if(m->needle->origin->scheme != conn->origin->scheme &&
872
    /* `needle` and `conn` not having the same scheme.
873
     * This is allowed for the same family *if* conn is using TLS.
874
     * - IMAP+STARTTLS works for IMAPS.
875
     * - IMAPS works for IMAP. */
876
0
     get_protocol_family(conn->origin->scheme) !=
877
0
     m->needle->scheme->protocol)
878
0
    return FALSE;
879
880
  /* Scheme mismatch is acceptable, compare hostname/port */
881
0
  return Curl_peer_same_destination(m->needle->origin, conn->origin);
882
0
}
883
884
static bool url_match_ssl_config(struct connectdata *conn,
885
                                 struct url_conn_match *m)
886
0
{
887
  /* If talking/upgrading to TLS, conn needs to use the same SSL options. */
888
0
  if(((m->needle->scheme->flags & PROTOPT_SSL) || m->may_tls) &&
889
0
     !Curl_ssl_conn_config_match(m->data, conn, FALSE)) {
890
0
    DEBUGF(infof(m->data, "Connection #%" FMT_OFF_T
891
0
                 " has different SSL parameters, cannot reuse",
892
0
                 conn->connection_id));
893
0
    return FALSE;
894
0
  }
895
0
  return TRUE;
896
0
}
897
898
#ifdef USE_NTLM
899
static bool url_match_auth_ntlm(struct connectdata *conn,
900
                                struct url_conn_match *m)
901
{
902
  if(conn->http_ntlm_state != NTLMSTATE_NONE) {
903
    /* Connection is using NTLM. We cannot reuse if transfer
904
     * has different Auth input parameters. */
905
    if(!m->want_ntlm_http ||
906
       !Curl_creds_same(conn->creds, m->data->state.creds) ||
907
       !Curl_peer_equal(conn->creds_origin, m->data->state.origin))
908
      return FALSE;
909
  }
910
  else if(m->want_ntlm_http) {
911
    /* Transfer wants NTLM, connection is not using it.
912
     * Do not reuse when connection has credentials and they differ. */
913
    if(conn->creds &&
914
       (!Curl_creds_same(conn->creds, m->data->state.creds) ||
915
        !Curl_peer_equal(conn->creds_origin, m->data->state.origin)))
916
      return FALSE;
917
  }
918
919
#ifndef CURL_DISABLE_PROXY
920
  /* Same for Proxy NTLM authentication */
921
  if(conn->proxy_ntlm_state != NTLMSTATE_NONE) {
922
    if(!m->want_proxy_ntlm_http ||
923
       !Curl_creds_same(m->needle->http_proxy.creds, conn->http_proxy.creds))
924
      return FALSE;
925
  }
926
  else if(m->want_proxy_ntlm_http) {
927
    if(conn->http_proxy.creds &&
928
       !Curl_creds_same(m->needle->http_proxy.creds, conn->http_proxy.creds))
929
      return FALSE;
930
  }
931
#endif
932
  if(m->want_ntlm_http || m->want_proxy_ntlm_http) {
933
    /* Credentials are already checked, we may use this connection.
934
     * With NTLM being weird as it is, we MUST use a
935
     * connection where it has already been fully negotiated.
936
     * If it has not, we keep on looking for a better one. */
937
    m->found = conn;
938
939
    if((m->want_ntlm_http &&
940
       (conn->http_ntlm_state != NTLMSTATE_NONE)) ||
941
        (m->want_proxy_ntlm_http &&
942
         (conn->proxy_ntlm_state != NTLMSTATE_NONE))) {
943
      /* We must use this connection, no other */
944
      m->force_reuse = TRUE;
945
      return TRUE;
946
    }
947
    /* Continue look up for a better connection */
948
    return FALSE;
949
  }
950
  return TRUE;
951
}
952
#else
953
0
#define url_match_auth_ntlm(c, m) ((void)(c), (void)(m), TRUE)
954
#endif
955
956
#ifdef USE_SPNEGO
957
static bool url_match_auth_nego(struct connectdata *conn,
958
                                struct url_conn_match *m)
959
{
960
  if(conn->http_negotiate_state != GSS_AUTHNONE) {
961
    /* Connection is using Negotiate. We cannot reuse if transfer
962
     * has different Auth input parameters. */
963
    if(!m->want_nego_http ||
964
       !Curl_creds_same(conn->creds, m->data->state.creds) ||
965
       !Curl_peer_equal(conn->creds_origin, m->data->state.origin))
966
      return FALSE;
967
  }
968
  else if(m->want_nego_http) {
969
    /* Transfer wants Negotiate, connection is not using it.
970
     * Do not reuse when connection has credentials and they differ. */
971
    if(conn->creds &&
972
       (!Curl_creds_same(conn->creds, m->data->state.creds) ||
973
        !Curl_peer_equal(conn->creds_origin, m->data->state.origin)))
974
      return FALSE;
975
  }
976
977
#ifndef CURL_DISABLE_PROXY
978
  /* Same for Proxy Negotiate authentication */
979
  if(conn->proxy_negotiate_state != GSS_AUTHNONE) {
980
    if(!m->want_proxy_nego_http ||
981
       !Curl_creds_same(m->needle->http_proxy.creds, conn->http_proxy.creds))
982
      return FALSE;
983
  }
984
  else if(m->want_proxy_nego_http) {
985
    if(conn->http_proxy.creds &&
986
       !Curl_creds_same(m->needle->http_proxy.creds, conn->http_proxy.creds))
987
      return FALSE;
988
  }
989
#endif
990
  if(m->want_nego_http || m->want_proxy_nego_http) {
991
    /* Credentials are already checked, we may use this connection. We MUST
992
     * use a connection where it has already been fully negotiated. If it has
993
     * not, we keep on looking for a better one. */
994
    m->found = conn;
995
    if((m->want_nego_http &&
996
        (conn->http_negotiate_state != GSS_AUTHNONE)) ||
997
       (m->want_proxy_nego_http &&
998
        (conn->proxy_negotiate_state != GSS_AUTHNONE))) {
999
      /* We must use this connection, no other */
1000
      m->force_reuse = TRUE;
1001
      return TRUE;
1002
    }
1003
    return FALSE; /* get another */
1004
  }
1005
  return TRUE;
1006
}
1007
#else
1008
0
#define url_match_auth_nego(c, m) ((void)(c), (void)(m), TRUE)
1009
#endif
1010
1011
static bool url_match_conn(struct connectdata *conn, void *userdata)
1012
0
{
1013
0
  struct url_conn_match *m = userdata;
1014
  /* Check if `conn` can be used for transfer `m->data` */
1015
1016
  /* general connect config setting match? */
1017
0
  if(!url_match_connect_config(conn, m))
1018
0
    return FALSE;
1019
1020
  /* match for destination and protocol? */
1021
0
  if(!url_match_destination(conn, m))
1022
0
    return FALSE;
1023
1024
0
  if(!url_match_fully_connected(conn, m))
1025
0
    return FALSE;
1026
1027
0
  if(!url_match_multiplex_needs(conn, m))
1028
0
    return FALSE;
1029
1030
0
  if(!url_match_ssl_use(conn, m))
1031
0
    return FALSE;
1032
1033
0
  if(!url_match_proxy_use(conn, m))
1034
0
    return FALSE;
1035
0
  if(!url_match_ssl_config(conn, m))
1036
0
    return FALSE;
1037
1038
0
  if(!url_match_http_multiplex(conn, m))
1039
0
    return FALSE;
1040
0
  else if(m->wait_pipe)
1041
    /* wait on multiplexing */
1042
0
    return TRUE;
1043
1044
0
  if(!url_match_auth(conn, m))
1045
0
    return FALSE;
1046
1047
0
  if(!url_match_proto_config(conn, m))
1048
0
    return FALSE;
1049
1050
0
  if(!url_match_auth_ntlm(conn, m))
1051
0
    return FALSE;
1052
0
  else if(m->force_reuse)
1053
0
    return TRUE;
1054
1055
0
  if(!url_match_auth_nego(conn, m))
1056
0
    return FALSE;
1057
0
  else if(m->force_reuse)
1058
0
    return TRUE;
1059
1060
0
  if(!url_match_multiplex_limits(conn, m))
1061
0
    return FALSE;
1062
1063
0
  if(m->data->set.conn_max_age_ms > 0) {
1064
0
    timediff_t age_ms = curlx_ptimediff_ms(&m->now, &conn->created);
1065
0
    if(age_ms > m->data->set.conn_max_age_ms) {
1066
      /* Transfer is looking for a younger connection. */
1067
0
      if(!CONN_INUSE(conn))
1068
0
        Curl_conn_close(m->data, conn, FALSE);
1069
0
      return FALSE;
1070
0
    }
1071
0
  }
1072
1073
  /* If we are going to pick an idle connection, do an extra
1074
   * health check before we reuse it. */
1075
0
  if(!CONN_INUSE(conn) &&
1076
0
     !Curl_cpool_conn_seems_healthy(conn, m->data, &m->now)) {
1077
0
    infof(m->data, "Connection %" FMT_OFF_T " seems to be dead, terminating",
1078
0
          conn->connection_id);
1079
0
    Curl_conn_close(m->data, conn, FALSE);
1080
0
    return FALSE;
1081
0
  }
1082
1083
  /* conn matches our needs. */
1084
0
  m->found = conn;
1085
0
  return TRUE;
1086
0
}
1087
1088
static bool url_match_result(void *userdata)
1089
4.86k
{
1090
4.86k
  struct url_conn_match *match = userdata;
1091
4.86k
  if(match->found) {
1092
    /* Attach it now while still under lock, so the connection does
1093
     * no longer appear idle and can be reaped. */
1094
0
    Curl_attach_connection(match->data, match->found, TRUE);
1095
0
    return TRUE;
1096
0
  }
1097
4.86k
  else if(match->seen_single_use_conn && !match->seen_multiplex_conn) {
1098
    /* We have seen a single-use, existing connection to the destination and
1099
     * no multiplexed one. It seems safe to assume that the server does
1100
     * not support multiplexing. */
1101
0
    match->wait_pipe = FALSE;
1102
0
  }
1103
4.86k
  else if(match->seen_pending_conn && match->data->set.pipewait) {
1104
0
    infof(match->data,
1105
0
          "Found pending candidate for reuse and CURLOPT_PIPEWAIT is set");
1106
0
    match->wait_pipe = TRUE;
1107
0
  }
1108
4.86k
  match->force_reuse = FALSE;
1109
4.86k
  return FALSE;
1110
4.86k
}
1111
1112
/*
1113
 * Given a transfer and a prototype connection (needle),
1114
 * find and attach an existing connection that matches.
1115
 *
1116
 * Return TRUE if an existing connection was attached.
1117
 * `waitpipe` is TRUE if no existing connection matched, but there
1118
 * might be suitable one in the near future (common cause: multiplexing
1119
 * capability has not been determined yet, e.g. ALPN handshake).
1120
 */
1121
static bool url_attach_existing(struct Curl_easy *data,
1122
                                struct connectdata *needle,
1123
                                bool *waitpipe)
1124
4.86k
{
1125
4.86k
  struct cpool *cpool = Curl_cpool_get_instance(data);
1126
4.86k
  struct url_conn_match match;
1127
4.86k
  bool success;
1128
1129
4.86k
  DEBUGASSERT(!data->conn);
1130
1131
4.86k
  memset(&match, 0, sizeof(match));
1132
4.86k
  match.data = data;
1133
4.86k
  match.needle = needle;
1134
4.86k
  match.now = *Curl_pgrs_now(data);
1135
4.86k
  match.may_multiplex = xfer_may_multiplex(data, needle);
1136
1137
4.86k
  Curl_cpool_prune_dead(cpool, data);
1138
1139
#ifdef USE_NTLM
1140
  match.want_ntlm_http =
1141
    (data->state.authhost.want & CURLAUTH_NTLM) &&
1142
    (needle->scheme->protocol & PROTO_FAMILY_HTTP);
1143
#ifndef CURL_DISABLE_PROXY
1144
  match.want_proxy_ntlm_http =
1145
    needle->http_proxy.creds &&
1146
    (data->state.authproxy.want & CURLAUTH_NTLM) &&
1147
    (needle->scheme->protocol & PROTO_FAMILY_HTTP);
1148
#endif
1149
#endif
1150
1151
#if !defined(CURL_DISABLE_HTTP) && defined(USE_SPNEGO)
1152
  match.want_nego_http =
1153
    (data->state.authhost.want & CURLAUTH_NEGOTIATE) &&
1154
    (needle->scheme->protocol & PROTO_FAMILY_HTTP);
1155
#ifndef CURL_DISABLE_PROXY
1156
  match.want_proxy_nego_http =
1157
    needle->http_proxy.creds &&
1158
    (data->state.authproxy.want & CURLAUTH_NEGOTIATE) &&
1159
    (needle->scheme->protocol & PROTO_FAMILY_HTTP);
1160
#endif
1161
#endif
1162
4.86k
  match.require_tls = data->set.use_ssl >= CURLUSESSL_CONTROL;
1163
4.86k
  match.may_tls = data->set.use_ssl > CURLUSESSL_NONE;
1164
1165
  /* Find a connection in the pool that matches what "data + needle"
1166
   * requires. If a suitable candidate is found, it is attached to "data". */
1167
4.86k
  success = Curl_cpool_find(data, needle->destination,
1168
4.86k
                            url_match_conn, url_match_result, &match);
1169
1170
  /* wait_pipe is TRUE if we encounter a bundle that is undecided. There
1171
   * is no matching connection then, yet. */
1172
4.86k
  *waitpipe = (bool)match.wait_pipe;
1173
4.86k
  return success;
1174
4.86k
}
1175
1176
/*
1177
 * Allocate and initialize a new connectdata object.
1178
 */
1179
static struct connectdata *allocate_conn(struct Curl_easy *data,
1180
                                         const struct curltime *pnow)
1181
4.88k
{
1182
4.88k
  struct connectdata *conn = curlx_calloc(1, sizeof(struct connectdata));
1183
4.88k
  if(!conn)
1184
0
    return NULL;
1185
1186
  /* and we setup a few fields in case we end up actually using this struct */
1187
1188
4.88k
  conn->sock[FIRSTSOCKET] = CURL_SOCKET_BAD;     /* no file descriptor */
1189
4.88k
  conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD; /* no file descriptor */
1190
4.88k
  conn->recv_idx = 0; /* default for receiving transfer data */
1191
4.88k
  conn->send_idx = 0; /* default for sending transfer data */
1192
4.88k
  conn->connection_id = -1;    /* no ID */
1193
4.88k
  conn->attached_xfers = 0;
1194
1195
  /* Remember time this connection started */
1196
4.88k
  conn->created = *pnow;
1197
1198
4.88k
#ifndef CURL_DISABLE_FTP
1199
4.88k
  conn->bits.ftp_use_epsv = data->set.ftp_use_epsv;
1200
4.88k
  conn->bits.ftp_use_eprt = data->set.ftp_use_eprt;
1201
4.88k
#endif
1202
4.88k
  conn->ip_version = data->set.ipver;
1203
4.88k
  conn->bits.connect_only = (bool)data->set.connect_only;
1204
4.88k
  conn->transport_wanted = TRNSPRT_TCP; /* most of them are TCP streams */
1205
1206
  /* Store the local bind parameters that will be used for this connection */
1207
4.88k
  if(CURL_EASY_STR(data, STRING_DEVICE)) {
1208
0
    conn->localdev = curlx_strdup(CURL_EASY_STR(data, STRING_DEVICE));
1209
0
    if(!conn->localdev)
1210
0
      goto error;
1211
0
  }
1212
4.88k
#ifndef CURL_DISABLE_BINDLOCAL
1213
4.88k
  conn->localportrange = data->set.localportrange;
1214
4.88k
  conn->localport = data->set.localport;
1215
4.88k
#endif
1216
1217
  /* the close socket stuff needs to be copied to the connection struct as
1218
     it may live on without (this specific) Curl_easy */
1219
4.88k
  conn->fclosesocket = data->set.fclosesocket;
1220
4.88k
  conn->closesocket_client = data->set.closesocket_client;
1221
#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
1222
  conn->gssapi_delegation = data->set.gssapi_delegation;
1223
#endif
1224
4.88k
  return conn;
1225
0
error:
1226
1227
0
  curlx_free(conn->localdev);
1228
0
  curlx_free(conn);
1229
0
  return NULL;
1230
4.88k
}
1231
1232
static CURLcode url_set_conn_scheme(struct Curl_easy *data,
1233
                                    struct connectdata *conn,
1234
                                    const struct Curl_scheme *scheme)
1235
4.88k
{
1236
  /* URL scheme is usable for connection when it is
1237
   * - allowed
1238
   * - not from a redirect or an allowed redirect protocol */
1239
4.88k
  if(scheme->run &&
1240
4.88k
     (data->set.allowed_protocols & scheme->protocol) &&
1241
4.88k
     (!data->state.this_is_a_follow ||
1242
4.88k
       (data->set.redir_protocols & scheme->protocol))) {
1243
4.88k
    conn->scheme = conn->given = scheme;
1244
4.88k
    return CURLE_OK;
1245
4.88k
  }
1246
0
  if(scheme->flags & PROTOPT_NO_TRANSFER)
1247
0
    failf(data, "Protocol \"%s\" is not for transfers", scheme->name);
1248
0
  else
1249
0
    failf(data, "Protocol \"%s\" is disabled%s", scheme->name,
1250
0
          data->state.this_is_a_follow ? " (in redirect)" : "");
1251
0
  return CURLE_UNSUPPORTED_PROTOCOL;
1252
4.88k
}
1253
1254
CURLcode Curl_uc_to_curlcode(CURLUcode uc)
1255
2.45k
{
1256
2.45k
  switch(uc) {
1257
2.45k
  default:
1258
2.45k
    return CURLE_URL_MALFORMAT;
1259
0
  case CURLUE_UNSUPPORTED_SCHEME:
1260
0
    return CURLE_UNSUPPORTED_PROTOCOL;
1261
0
  case CURLUE_OUT_OF_MEMORY:
1262
0
    return CURLE_OUT_OF_MEMORY;
1263
0
  case CURLUE_USER_NOT_ALLOWED:
1264
0
    return CURLE_LOGIN_DENIED;
1265
2.45k
  }
1266
2.45k
}
1267
1268
#ifndef CURL_DISABLE_HSTS
1269
static CURLcode hsts_upgrade(struct Curl_easy *data,
1270
                             CURLU *uh,
1271
                             uint16_t port_override,
1272
                             uint32_t scope_id)
1273
4.88k
{
1274
  /* HSTS upgrade */
1275
4.88k
  if(data->hsts && (data->state.origin->scheme == &Curl_scheme_http) &&
1276
0
     Curl_hsts_applies(data->hsts, data->state.origin)) {
1277
0
    char *url;
1278
0
    CURLUcode uc;
1279
0
    CURLcode result;
1280
1281
0
    uc = curl_url_set(uh, CURLUPART_SCHEME, "https", 0);
1282
0
    if(uc)
1283
0
      return Curl_uc_to_curlcode(uc);
1284
0
    Curl_bufref_free(&data->state.url);
1285
    /* after update, get the updated version */
1286
0
    uc = curl_url_get(uh, CURLUPART_URL, &url, 0);
1287
0
    if(uc)
1288
0
      return Curl_uc_to_curlcode(uc);
1289
0
    Curl_bufref_set(&data->state.url, url, 0, curl_free);
1290
1291
0
    result = Curl_peer_from_url(uh, data, port_override, scope_id,
1292
0
                                &data->state.origin);
1293
0
    if(result)
1294
0
      return result;
1295
0
    infof(data, "Switched from HTTP to HTTPS due to HSTS => %s", url);
1296
0
  }
1297
4.88k
  return CURLE_OK;
1298
4.88k
}
1299
#else
1300
#define hsts_upgrade(x, y, z, a) CURLE_OK
1301
#endif
1302
1303
static bool str_has_ctrl(const char *input)
1304
0
{
1305
0
  if(input) {
1306
0
    const unsigned char *str = (const unsigned char *)input;
1307
0
    while(*str) {
1308
0
      if(*str < 0x20)
1309
0
        return TRUE;
1310
0
      str++;
1311
0
    }
1312
0
  }
1313
0
  return FALSE;
1314
0
}
1315
1316
#ifndef CURL_DISABLE_NETRC
1317
/*
1318
 * Override the login details from the URL with that in the CURLOPT_USERPWD
1319
 * option or a .netrc file, if applicable.
1320
 */
1321
static CURLcode url_set_data_creds_netrc(struct Curl_easy *data,
1322
                                         struct Curl_creds **pcreds)
1323
4.88k
{
1324
4.88k
  struct Curl_creds *ncreds_out = NULL;
1325
4.88k
  CURLcode result = CURLE_OK;
1326
1327
4.88k
  if(data->set.use_netrc) { /* not CURL_NETRC_IGNORED */
1328
0
    struct Curl_creds *ncreds_in = NULL;
1329
0
    bool scan_netrc = TRUE;
1330
0
    NETRCcode ret;
1331
0
    CURLUcode uc;
1332
1333
0
    if(*pcreds) {
1334
0
      switch((*pcreds)->source) {
1335
0
      case CREDS_OPTION:
1336
        /* we never override credentials set via CURLOPT_*, leave. */
1337
0
        scan_netrc = FALSE;
1338
0
        break;
1339
0
      case CREDS_URL: /* only apply when netrc is not required */
1340
0
        if(data->set.use_netrc == CURL_NETRC_REQUIRED) {
1341
          /* We ignore password from URL */
1342
0
          ncreds_in = *pcreds;
1343
0
        }
1344
0
        else if(!Curl_creds_has_user(*pcreds) ||
1345
0
                !Curl_creds_has_passwd(*pcreds)) {
1346
          /* We use netrc to complete what is missing */
1347
0
          ncreds_in = *pcreds;
1348
0
        }
1349
0
        else
1350
0
          scan_netrc = FALSE;
1351
0
        break;
1352
0
      default: /* ignore credentials from other sources */
1353
0
        break;
1354
0
      }
1355
0
    }
1356
1357
0
    if(!scan_netrc)
1358
0
      goto out;
1359
1360
0
    ret = Curl_netrc_scan(data, &data->state.netrc,
1361
0
                          data->state.origin->hostname,
1362
0
                          Curl_creds_user(ncreds_in),
1363
0
                          CURL_EASY_STR(data, STRING_NETRC_FILE),
1364
0
                          &ncreds_out);
1365
0
    DEBUGASSERT(!ret || !ncreds_out);
1366
0
    if(ret == NETRC_OUT_OF_MEMORY) {
1367
0
      result = CURLE_OUT_OF_MEMORY;
1368
0
      goto out;
1369
0
    }
1370
0
    else if(ret && ((ret == NETRC_NO_MATCH) ||
1371
0
                    (data->set.use_netrc == CURL_NETRC_OPTIONAL))) {
1372
0
      infof(data, "Could not find host %s in the %s file; using defaults",
1373
0
            data->state.origin->hostname,
1374
0
            (CURL_EASY_STR(data, STRING_NETRC_FILE) ?
1375
0
             CURL_EASY_STR(data, STRING_NETRC_FILE) : ".netrc"));
1376
0
    }
1377
0
    else if(ret) {
1378
0
      const char *m = Curl_netrc_strerror(ret);
1379
0
      failf(data, ".netrc error: %s", m);
1380
0
      result = CURLE_READ_ERROR;
1381
0
      goto out;
1382
0
    }
1383
0
    else if(ncreds_out) {
1384
0
      if(!(data->state.origin->scheme->flags & PROTOPT_USERPWDCTRL) &&
1385
         /* if the protocol cannot handle control codes in credentials, make
1386
            sure there are none */
1387
0
         (str_has_ctrl(ncreds_out->user) ||
1388
0
          str_has_ctrl(ncreds_out->passwd))) {
1389
0
        failf(data, "control code detected in .netrc credentials");
1390
0
        result = CURLE_READ_ERROR;
1391
0
        goto out;
1392
0
      }
1393
0
      CURL_TRC_M(data, "netrc: using credentials for %s as %s",
1394
0
                 data->state.origin->hostname, ncreds_out->user);
1395
0
      result = Curl_creds_merge(ncreds_out->user, ncreds_out->passwd,
1396
0
                                *pcreds, CREDS_NETRC, pcreds);
1397
0
      if(result)
1398
0
        goto out;
1399
      /* for updated strings, we update them in the URL */
1400
0
      uc = curl_url_set(data->state.uh, CURLUPART_USER,
1401
0
                        Curl_creds_user(*pcreds), CURLU_URLENCODE);
1402
0
      if(!uc)
1403
0
        uc = curl_url_set(data->state.uh, CURLUPART_PASSWORD,
1404
0
                          Curl_creds_passwd(*pcreds),
1405
0
                          CURLU_URLENCODE);
1406
0
      if(uc)
1407
0
        result = Curl_uc_to_curlcode(uc);
1408
0
    }
1409
0
    else
1410
0
      DEBUGASSERT(0);
1411
0
  }
1412
1413
4.88k
#ifdef CURLVERBOSE
1414
4.88k
  Curl_creds_trace(data, data->state.creds, "transfer credentials");
1415
4.88k
#endif
1416
1417
4.88k
out:
1418
4.88k
  Curl_creds_unlink(&ncreds_out);
1419
4.88k
  return result;
1420
4.88k
}
1421
#endif /* CURL_DISABLE_NETRC */
1422
1423
static CURLcode url_set_data_creds(struct Curl_easy *data, CURLU *uh)
1424
4.88k
{
1425
4.88k
  struct Curl_creds *newcreds = NULL;
1426
4.88k
  CURLcode result = CURLE_OK;
1427
1428
4.88k
  if((CURL_EASY_STR(data, STRING_USERNAME) ||
1429
4.77k
      CURL_EASY_STR(data, STRING_PASSWORD) ||
1430
4.75k
      CURL_EASY_STR(data, STRING_BEARER) ||
1431
4.73k
      CURL_EASY_STR(data, STRING_SASL_AUTHZID) ||
1432
4.73k
      CURL_EASY_STR(data, STRING_SERVICE_NAME)) &&
1433
150
     Curl_auth_allowed_to_origin(data, data->state.origin)) {
1434
150
    result = Curl_creds_create(CURL_EASY_STR(data, STRING_USERNAME),
1435
150
                               CURL_EASY_STR(data, STRING_PASSWORD),
1436
150
                               CURL_EASY_STR(data, STRING_BEARER),
1437
150
                               CURL_EASY_STR(data, STRING_SASL_AUTHZID),
1438
150
                               CURL_EASY_STR(data, STRING_SERVICE_NAME),
1439
150
                               CREDS_OPTION, &newcreds);
1440
150
    if(result)
1441
0
      goto out;
1442
150
    if(newcreds &&
1443
142
       !(data->state.origin->scheme->flags & PROTOPT_USERPWDCTRL) &&
1444
0
       (str_has_ctrl(Curl_creds_user(newcreds)) ||
1445
0
        str_has_ctrl(Curl_creds_passwd(newcreds)))) {
1446
      /* if the protocol cannot handle control codes in credentials, make
1447
         sure there are none */
1448
0
      failf(data, "control code detected in credentials");
1449
0
      result = CURLE_BAD_FUNCTION_ARGUMENT;
1450
0
      goto out;
1451
0
    }
1452
150
  }
1453
1454
  /* Extract credentials from the URL only if there are none OR
1455
   * if no CURLOPT_USER was set. */
1456
4.88k
  if(!newcreds || !Curl_creds_has_user(newcreds)) {
1457
4.85k
    char *user = NULL;
1458
4.85k
    char *passwd = NULL;
1459
4.85k
    char *udecoded = NULL;
1460
4.85k
    char *pdecoded = NULL;
1461
4.85k
    CURLUcode uc;
1462
1463
4.85k
    uc = curl_url_get(uh, CURLUPART_USER, &user, 0);
1464
4.85k
    if(uc && (uc != CURLUE_NO_USER))
1465
0
      result = Curl_uc_to_curlcode(uc);
1466
4.85k
    if(!result) {
1467
4.85k
      uc = curl_url_get(uh, CURLUPART_PASSWORD, &passwd, 0);
1468
4.85k
      if(uc && (uc != CURLUE_NO_PASSWORD))
1469
0
        result = Curl_uc_to_curlcode(uc);
1470
4.85k
    }
1471
4.85k
    if(!result && user) {
1472
42
      result = Curl_urldecode(user, 0, &udecoded, NULL,
1473
42
                              (data->state.origin->scheme->flags &
1474
42
                               PROTOPT_USERPWDCTRL) ?
1475
42
                              REJECT_ZERO : REJECT_CTRL);
1476
42
    }
1477
4.85k
    if(!result && passwd) {
1478
8
      result = Curl_urldecode(passwd, 0, &pdecoded, NULL,
1479
8
                              (data->state.origin->scheme->flags &
1480
8
                               PROTOPT_USERPWDCTRL) ?
1481
8
                              REJECT_ZERO : REJECT_CTRL);
1482
8
    }
1483
4.85k
    if(!result)
1484
4.85k
      result = Curl_creds_merge(udecoded, pdecoded, newcreds,
1485
4.85k
                                CREDS_URL, &newcreds);
1486
1487
4.85k
    curlx_free(udecoded);
1488
4.85k
    curlx_free(pdecoded);
1489
4.85k
    curlx_free(passwd);
1490
4.85k
    curlx_free(user);
1491
4.85k
    if(result) {
1492
3
      failf(data, "error extracting credentials from URL");
1493
3
      goto out;
1494
3
    }
1495
4.85k
  }
1496
1497
4.88k
#ifndef CURL_DISABLE_NETRC
1498
  /* Check for overridden login details and set them accordingly so that
1499
     they are known when protocol->setup_connection is called! */
1500
4.88k
  result = url_set_data_creds_netrc(data, &newcreds);
1501
4.88k
#endif /* CURL_DISABLE_NETRC */
1502
1503
4.88k
out:
1504
4.88k
  if(!result && !Curl_creds_equal(data->state.creds, newcreds)) {
1505
    /* Do we have more things to trigger on credentials change? */
1506
176
    Curl_creds_link(&data->state.creds, newcreds);
1507
176
  }
1508
4.88k
  Curl_creds_unlink(&newcreds);
1509
4.88k
  return result;
1510
4.88k
}
1511
1512
static CURLcode url_set_conn_origin_etc(struct Curl_easy *data,
1513
                                        struct connectdata *conn)
1514
4.88k
{
1515
4.88k
  CURLcode result = CURLE_OK;
1516
1517
4.88k
  Curl_peer_link(&conn->origin, data->state.origin);
1518
1519
  /* set the connection scheme */
1520
4.88k
  result = url_set_conn_scheme(data, conn, conn->origin->scheme);
1521
4.88k
  if(result)
1522
0
    goto out;
1523
1524
  /* set the connection options */
1525
4.88k
  if(CURL_EASY_STR(data, STRING_OPTIONS)) {
1526
0
    conn->options = curlx_strdup(CURL_EASY_STR(data, STRING_OPTIONS));
1527
0
    if(!conn->options) {
1528
0
      result = CURLE_OUT_OF_MEMORY;
1529
0
      goto out;
1530
0
    }
1531
0
  }
1532
4.88k
  else if(data->state.up.options) {
1533
0
    conn->options = curlx_strdup(data->state.up.options);
1534
0
    if(!conn->options) {
1535
0
      result = CURLE_OUT_OF_MEMORY;
1536
0
      goto out;
1537
0
    }
1538
0
  }
1539
1540
4.88k
out:
1541
4.88k
  return result;
1542
4.88k
}
1543
1544
/*
1545
 * If we are doing a resumed transfer, we need to setup our stuff
1546
 * properly.
1547
 */
1548
static CURLcode setup_range(struct Curl_easy *data)
1549
4.88k
{
1550
4.88k
  struct UrlState *s = &data->state;
1551
4.88k
  s->resume_from = data->set.set_resume_from;
1552
4.88k
  if(s->resume_from || CURL_EASY_STR(data, STRING_SET_RANGE)) {
1553
96
    if(s->rangestringalloc)
1554
0
      curlx_free(s->range);
1555
1556
96
    if(s->resume_from)
1557
72
      s->range = curl_maprintf("%" FMT_OFF_T "-", s->resume_from);
1558
24
    else
1559
24
      s->range = curlx_strdup(CURL_EASY_STR(data, STRING_SET_RANGE));
1560
1561
96
    if(!s->range)
1562
0
      return CURLE_OUT_OF_MEMORY;
1563
1564
96
    s->rangestringalloc = TRUE;
1565
1566
    /* tell ourselves to fetch this range */
1567
96
    s->use_range = TRUE;        /* enable range download */
1568
96
  }
1569
4.78k
  else
1570
4.78k
    s->use_range = FALSE; /* disable range download */
1571
1572
4.88k
  return CURLE_OK;
1573
4.88k
}
1574
1575
/*
1576
 * setup_connection_internals() -
1577
 *
1578
 * Setup connection internals specific to the requested protocol in the
1579
 * Curl_easy. This is inited and setup before the connection is made but
1580
 * is about the particular protocol that is to be used.
1581
 *
1582
 * This MUST get called after proxy magic has been figured out.
1583
 */
1584
static CURLcode setup_connection_internals(struct Curl_easy *data,
1585
                                           struct connectdata *conn)
1586
4.88k
{
1587
4.88k
  struct Curl_peer *peer = NULL;
1588
4.88k
  CURLcode result;
1589
1590
4.88k
  if(conn->scheme->run->setup_connection) {
1591
4.88k
    result = conn->scheme->run->setup_connection(data, conn);
1592
4.88k
    if(result)
1593
0
      return result;
1594
4.88k
  }
1595
1596
  /* Now create the destination name */
1597
4.88k
  peer = Curl_conn_get_destination(conn, FIRSTSOCKET);
1598
4.88k
  if(!peer)
1599
0
    return CURLE_FAILED_INIT;
1600
1601
  /* IPv6 addresses with a scope_id (0 is default == global) have a
1602
   * printable representation with a '%<scope_id>' suffix. */
1603
4.88k
  if(peer->ipv6)
1604
0
    if(peer->scopeid)
1605
0
      conn->destination = curl_maprintf("[%s%%%u]:%u",
1606
0
        peer->hostname, peer->scopeid, peer->port);
1607
0
    else
1608
0
      conn->destination = curl_maprintf("[%s]:%u",
1609
0
        peer->hostname, peer->port);
1610
4.88k
  else
1611
4.88k
    conn->destination = curl_maprintf("%s:%u", peer->hostname, peer->port);
1612
4.88k
  if(!conn->destination)
1613
0
    return CURLE_OUT_OF_MEMORY;
1614
1615
4.88k
  Curl_strntolower(conn->destination, conn->destination,
1616
4.88k
                   strlen(conn->destination));
1617
1618
4.88k
#ifdef USE_IPV6
1619
4.88k
  if(data->set.scope_id)
1620
0
    conn->scope_id = data->set.scope_id;
1621
4.88k
  else {
1622
4.88k
    struct Curl_peer *first = Curl_conn_get_first_peer(conn, FIRSTSOCKET);
1623
4.88k
    if(!first)
1624
0
      return CURLE_FAILED_INIT;
1625
4.88k
    conn->scope_id = first->scopeid;
1626
4.88k
  }
1627
4.88k
#endif
1628
1629
4.88k
  return CURLE_OK;
1630
4.88k
}
1631
1632
/*
1633
 * Curl_parse_login_details()
1634
 *
1635
 * This is used to parse a login string for username, password and options in
1636
 * the following formats:
1637
 *
1638
 *   user
1639
 *   user:password
1640
 *   user:password;options
1641
 *   user;options
1642
 *   user;options:password
1643
 *   :password
1644
 *   :password;options
1645
 *   ;options
1646
 *   ;options:password
1647
 *
1648
 * Parameters:
1649
 *
1650
 * login    [in]     - login string.
1651
 * len      [in]     - length of the login string.
1652
 * userp    [in/out] - address where a pointer to newly allocated memory
1653
 *                     holding the user will be stored upon completion.
1654
 * passwdp  [in/out] - address where a pointer to newly allocated memory
1655
 *                     holding the password will be stored upon completion.
1656
 * optionsp [in/out] - OPTIONAL address where a pointer to newly allocated
1657
 *                     memory holding the options will be stored upon
1658
 *                     completion.
1659
 *
1660
 * Returns CURLE_OK on success.
1661
 */
1662
CURLcode Curl_parse_login_details(const char *login, const size_t len,
1663
                                  char **userp, char **passwdp,
1664
                                  char **optionsp)
1665
1.22k
{
1666
1.22k
  char *ubuf = NULL;
1667
1.22k
  char *pbuf = NULL;
1668
1.22k
  const char *psep = NULL;
1669
1.22k
  const char *osep = NULL;
1670
1.22k
  size_t ulen;
1671
1.22k
  size_t plen;
1672
1.22k
  size_t olen;
1673
1674
1.22k
  DEBUGASSERT(userp);
1675
1.22k
  DEBUGASSERT(passwdp);
1676
1677
  /* Attempt to find the password separator */
1678
1.22k
  psep = memchr(login, ':', len);
1679
1680
  /* Attempt to find the options separator */
1681
1.22k
  if(optionsp)
1682
0
    osep = memchr(login, ';', len);
1683
1684
  /* Calculate the portion lengths */
1685
1.22k
  ulen = (psep ?
1686
264
          (size_t)(osep && psep > osep ? osep - login : psep - login) :
1687
1.22k
          (osep ? (size_t)(osep - login) : len));
1688
1.22k
  plen = (psep ?
1689
264
          (osep && osep > psep ? (size_t)(osep - psep) :
1690
958
           (size_t)(login + len - psep)) - 1 : 0);
1691
1.22k
  olen = (osep ?
1692
0
          (psep && psep > osep ? (size_t)(psep - osep) :
1693
1.22k
           (size_t)(login + len - osep)) - 1 : 0);
1694
1695
  /* Clone the user portion buffer, which can be zero length */
1696
1.22k
  ubuf = curlx_memdup0(login, ulen);
1697
1.22k
  if(!ubuf)
1698
0
    goto error;
1699
1700
  /* Clone the password portion buffer */
1701
1.22k
  if(psep) {
1702
264
    pbuf = curlx_memdup0(&psep[1], plen);
1703
264
    if(!pbuf)
1704
0
      goto error;
1705
264
  }
1706
1707
  /* Allocate the options portion buffer */
1708
1.22k
  if(optionsp) {
1709
0
    char *obuf = NULL;
1710
0
    if(olen) {
1711
0
      obuf = curlx_memdup0(&osep[1], olen);
1712
0
      if(!obuf)
1713
0
        goto error;
1714
0
    }
1715
0
    *optionsp = obuf;
1716
0
  }
1717
1.22k
  *userp = ubuf;
1718
1.22k
  *passwdp = pbuf;
1719
1.22k
  return CURLE_OK;
1720
0
error:
1721
0
  curlx_free(ubuf);
1722
0
  curlx_free(pbuf);
1723
0
  return CURLE_OUT_OF_MEMORY;
1724
1.22k
}
1725
1726
/*
1727
 * Set the login details so they are available in the connection
1728
 */
1729
static CURLcode url_set_conn_login(struct Curl_easy *data,
1730
                                   struct connectdata *conn)
1731
4.88k
{
1732
  /* If our protocol needs a password and we have none, use the defaults */
1733
4.88k
  if((conn->scheme->flags & PROTOPT_NEEDSPWD) && !conn->creds) {
1734
0
    Curl_peer_link(&conn->creds_origin, data->state.origin);
1735
0
    if(data->state.creds)
1736
0
      Curl_creds_link(&conn->creds, data->state.creds);
1737
0
    else
1738
0
      return Curl_creds_create(CURL_DEFAULT_USER, CURL_DEFAULT_PASSWORD,
1739
0
                               NULL, NULL, NULL, CREDS_NONE, &conn->creds);
1740
0
  }
1741
4.88k
  else if(!(conn->scheme->flags & PROTOPT_CREDSPERREQUEST)) {
1742
    /* for protocols that do not handle credentials per request,
1743
     * the connection credentials are set by the initial transfer. */
1744
0
    Curl_peer_link(&conn->creds_origin, data->state.origin);
1745
0
    Curl_creds_link(&conn->creds, data->state.creds);
1746
0
  }
1747
1748
4.88k
  return CURLE_OK;
1749
4.88k
}
1750
1751
/*
1752
 * Parses one "connect to" string in the form:
1753
 * "HOST:PORT:CONNECT-TO-HOST:CONNECT-TO-PORT".
1754
 */
1755
static CURLcode parse_connect_to_string(struct Curl_easy *data,
1756
                                        const struct Curl_peer *dest,
1757
                                        const char *conn_to_line,
1758
                                        struct Curl_peer **pvia_dest)
1759
4.88k
{
1760
4.88k
  CURLcode result = CURLE_OK;
1761
4.88k
  const char *ptr = conn_to_line;
1762
4.88k
  bool host_match = FALSE;
1763
4.88k
  bool port_match = FALSE;
1764
1765
4.88k
  *pvia_dest = NULL;
1766
1767
4.88k
  if(*ptr == ':') {
1768
    /* an empty hostname always matches */
1769
4.88k
    host_match = TRUE;
1770
4.88k
    ptr++;
1771
4.88k
  }
1772
0
  else {
1773
    /* check whether the URL's hostname matches. Use the URL hostname
1774
     * when it was an IPv6 address. Otherwise use the connection's hostname
1775
     * that has IDN conversion. */
1776
0
    size_t hlen = strlen(dest->hostname);
1777
0
    host_match = curl_strnequal(ptr, dest->hostname, hlen);
1778
0
    if(!host_match && (dest->user_hostname != dest->hostname)) {
1779
      /* hostname was normalized, could be IPv6 or IDN */
1780
0
      hlen = strlen(dest->user_hostname);
1781
0
      host_match = curl_strnequal(ptr, dest->user_hostname, hlen);
1782
0
    }
1783
0
    host_match = host_match && ptr[hlen] == ':';
1784
0
    if(host_match)
1785
0
      ptr += hlen + 1;
1786
0
  }
1787
1788
4.88k
  if(host_match) {
1789
4.88k
    if(*ptr == ':') {
1790
      /* an empty port always matches */
1791
4.88k
      port_match = TRUE;
1792
4.88k
      ptr++;
1793
4.88k
    }
1794
0
    else {
1795
      /* check whether the URL's port matches */
1796
0
      const char *ptr_next = strchr(ptr, ':');
1797
0
      if(ptr_next) {
1798
0
        curl_off_t port_to_match;
1799
0
        if(!curlx_str_number(&ptr, &port_to_match, 0xffff) &&
1800
0
           ((uint16_t)port_to_match == dest->port)) {
1801
0
          port_match = TRUE;
1802
0
        }
1803
0
        ptr = ptr_next + 1;
1804
0
      }
1805
0
    }
1806
4.88k
  }
1807
1808
4.88k
  if(host_match && port_match && ptr && *ptr)
1809
4.88k
    result = Curl_peer_from_connect_to(data, dest, ptr, pvia_dest);
1810
1811
4.88k
  return result;
1812
4.88k
}
1813
1814
/* With `conn->origin` known, determine if we should talk to that
1815
 * directly or via another peer. This is the result of inspecting
1816
 * the "connect to" slist and "alt-svc" settings. */
1817
static CURLcode url_set_conn_peer(struct Curl_easy *data,
1818
                                  struct connectdata *conn)
1819
4.88k
{
1820
4.88k
  CURLcode result = CURLE_OK;
1821
4.88k
  struct Curl_peer *origin = conn->origin;
1822
4.88k
  struct Curl_peer *via_peer = NULL;
1823
4.88k
  struct curl_slist *conn_to_entry = data->set.connect_to;
1824
1825
4.88k
  DEBUGASSERT(!conn->via_peer);
1826
4.88k
  Curl_peer_unlink(&conn->via_peer);
1827
1828
9.76k
  while(conn_to_entry && !via_peer) {
1829
4.88k
    result = parse_connect_to_string(data, origin, conn_to_entry->data,
1830
4.88k
                                     &via_peer);
1831
4.88k
    if(result)
1832
0
      return result;
1833
4.88k
    conn_to_entry = conn_to_entry->next;
1834
4.88k
  }
1835
1836
4.88k
#ifndef CURL_DISABLE_ALTSVC
1837
4.88k
  if(data->asi && !via_peer &&
1838
0
     ((conn->scheme->protocol == CURLPROTO_HTTPS) ||
1839
0
#ifdef DEBUGBUILD
1840
      /* allow debug builds to circumvent the HTTPS restriction */
1841
0
      getenv("CURL_ALTSVC_HTTP")
1842
#else
1843
      0
1844
#endif
1845
0
       )) {
1846
    /* no connect_to match, try alt-svc! */
1847
0
    enum alpnid srcalpnid = ALPN_none;
1848
0
    bool hit = FALSE;
1849
0
    struct altsvc *as = NULL;
1850
0
    int allowed_alpns = ALPN_none;
1851
0
    struct http_negotiation *neg = &data->state.http_neg;
1852
0
    bool same_dest = FALSE;
1853
1854
0
    DEBUGF(infof(data, "Alt-svc check wanted=%x, allowed=%x",
1855
0
                 neg->wanted, neg->allowed));
1856
#ifdef USE_HTTP3
1857
    if(neg->allowed & CURL_HTTP_V3x)
1858
      allowed_alpns |= ALPN_h3;
1859
#endif
1860
0
#ifdef USE_HTTP2
1861
0
    if(neg->allowed & CURL_HTTP_V2x)
1862
0
      allowed_alpns |= ALPN_h2;
1863
0
#endif
1864
0
    if(neg->allowed & CURL_HTTP_V1x)
1865
0
      allowed_alpns |= ALPN_h1;
1866
0
    allowed_alpns &= (int)data->asi->flags;
1867
1868
0
    DEBUGF(infof(data, "check Alt-Svc for host '%s'", origin->hostname));
1869
#ifdef USE_HTTP3
1870
    if(!hit && (neg->wanted & CURL_HTTP_V3x)) {
1871
      srcalpnid = ALPN_h3;
1872
      hit = Curl_altsvc_lookup(data->asi,
1873
                               origin, ALPN_h3, /* from */
1874
                               &as /* to */,
1875
                               allowed_alpns, &same_dest);
1876
    }
1877
#endif
1878
0
#ifdef USE_HTTP2
1879
0
    if(!hit && (neg->wanted & CURL_HTTP_V2x) &&
1880
0
       !neg->h2_prior_knowledge) {
1881
0
      srcalpnid = ALPN_h2;
1882
0
      hit = Curl_altsvc_lookup(data->asi,
1883
0
                               origin, ALPN_h2, /* from */
1884
0
                               &as /* to */,
1885
0
                               allowed_alpns, &same_dest);
1886
0
    }
1887
0
#endif
1888
0
    if(!hit && (neg->wanted & CURL_HTTP_V1x) &&
1889
0
       !neg->only_10) {
1890
0
      srcalpnid = ALPN_h1;
1891
0
      hit = Curl_altsvc_lookup(data->asi,
1892
0
                               origin, ALPN_h1, /* from */
1893
0
                               &as /* to */,
1894
0
                               allowed_alpns, &same_dest);
1895
0
    }
1896
1897
0
    if(hit && same_dest) {
1898
      /* same destination, but more HTTPS version options */
1899
0
      switch(as->dst.alpnid) {
1900
0
      case ALPN_h1:
1901
0
        neg->wanted |= CURL_HTTP_V1x;
1902
0
        neg->preferred = CURL_HTTP_V1x;
1903
0
        break;
1904
0
      case ALPN_h2:
1905
0
        neg->wanted |= CURL_HTTP_V2x;
1906
0
        neg->preferred = CURL_HTTP_V2x;
1907
0
        break;
1908
0
      case ALPN_h3:
1909
0
        neg->wanted |= CURL_HTTP_V3x;
1910
0
        neg->preferred = CURL_HTTP_V3x;
1911
0
        break;
1912
0
      default: /* should not be possible */
1913
0
        break;
1914
0
      }
1915
0
    }
1916
0
    else if(hit) {
1917
0
      result = Curl_peer_create(data, conn->origin->scheme,
1918
0
                                as->dst.host, as->dst.port,
1919
0
                                &via_peer);
1920
0
      if(result)
1921
0
        return result;
1922
0
      infof(data, "Alt-svc connecting from [%s]%s:%u to [%s]%s:%u",
1923
0
            Curl_alpnid2str(srcalpnid), origin->hostname, origin->port,
1924
0
            Curl_alpnid2str(as->dst.alpnid),
1925
0
            via_peer->hostname, via_peer->port);
1926
0
      conn->bits.altused = TRUE;
1927
0
      if(srcalpnid != as->dst.alpnid) {
1928
        /* protocol version switch */
1929
0
        switch(as->dst.alpnid) {
1930
0
        case ALPN_h1:
1931
0
          neg->wanted = neg->allowed = CURL_HTTP_V1x;
1932
0
          neg->only_10 = FALSE;
1933
0
          break;
1934
0
        case ALPN_h2:
1935
0
          neg->wanted = neg->allowed = CURL_HTTP_V2x;
1936
0
          break;
1937
0
        case ALPN_h3:
1938
0
          conn->transport_wanted = TRNSPRT_QUIC;
1939
0
          neg->wanted = neg->allowed = CURL_HTTP_V3x;
1940
0
          break;
1941
0
        default: /* should not be possible */
1942
0
          break;
1943
0
        }
1944
0
      }
1945
0
    }
1946
0
  }
1947
4.88k
#endif
1948
1949
4.88k
  if(via_peer)
1950
4.88k
    conn->via_peer = via_peer;
1951
1952
4.88k
  return result;
1953
4.88k
}
1954
1955
/*
1956
 * Adjust reused connection settings to the transfer/needle.
1957
 */
1958
static void url_conn_reuse_adjust(struct Curl_easy *data,
1959
                                  struct connectdata *needle)
1960
0
{
1961
0
  struct connectdata *conn = data->conn;
1962
1963
  /* get the user+password information from the needle since it may
1964
   * be new for this request even when we reuse conn */
1965
0
  if(needle->creds) {
1966
    /* use the new username and password though */
1967
0
    Curl_creds_link(&conn->creds, needle->creds);
1968
0
  }
1969
1970
0
#ifndef CURL_DISABLE_PROXY
1971
  /* use the new proxy username and proxy password though */
1972
0
  Curl_creds_link(&conn->http_proxy.creds, needle->http_proxy.creds);
1973
0
  Curl_creds_link(&conn->socks_proxy.creds, needle->socks_proxy.creds);
1974
0
#endif
1975
1976
  /* Finding a connection for reuse in the cpool matches, among other
1977
   * things on the "remote-relevant" hostname. This is not necessarily
1978
   * the authority of the URL, e.g. conn->origin. For example:
1979
   * - we use a proxy (not tunneling). we want to send all requests
1980
   *   that use the same proxy on this connection.
1981
   * - we have a "connect-to" setting that may redirect the hostname of
1982
   *   a new request to the same remote endpoint of an existing conn.
1983
   *   We want to reuse an existing conn to the remote endpoint.
1984
   * Since connection reuse does not match on conn->origin necessarily, we
1985
   * switch conn to needle's host settings.
1986
   */
1987
0
  Curl_peer_link(&conn->origin, needle->origin);
1988
0
  Curl_peer_link(&conn->via_peer, needle->via_peer);
1989
0
  Curl_peer_link(&conn->origin2, needle->origin2);
1990
0
  Curl_peer_link(&conn->via_peer2, needle->via_peer2);
1991
0
}
1992
1993
static void conn_meta_freeentry(void *p)
1994
0
{
1995
0
  (void)p;
1996
  /* Always FALSE. Cannot use a 0 assert here since compilers
1997
   * are not in agreement if they then want a NORETURN attribute or
1998
   * not. *sigh* */
1999
0
  DEBUGASSERT(!p);
2000
0
}
2001
2002
static CURLcode url_create_needle(struct Curl_easy *data,
2003
                                  const struct curltime *pnow,
2004
                                  struct connectdata **pneedle)
2005
4.88k
{
2006
4.88k
  struct connectdata *needle = NULL;
2007
4.88k
  CURLcode result = CURLE_OK;
2008
4.88k
  bool network_scheme = TRUE; /* almost all are */
2009
2010
  /* Allocate a temporary connection data struct (needle) and fill in for
2011
     comparison purposes. */
2012
4.88k
  needle = allocate_conn(data, pnow);
2013
4.88k
  if(!needle) {
2014
0
    result = CURLE_OUT_OF_MEMORY;
2015
0
    goto out;
2016
0
  }
2017
2018
  /* Do the unfailable inits first, before checks that may early return */
2019
4.88k
  Curl_hash_init(&needle->meta_hash, 23,
2020
4.88k
                 Curl_hash_str, curlx_str_key_compare, conn_meta_freeentry);
2021
2022
  /*************************************************************
2023
   * Determine `conn->origin` and populate `data->state.up` and
2024
   * other URL related properties.
2025
   *************************************************************/
2026
4.88k
  result = url_set_conn_origin_etc(data, needle);
2027
4.88k
  if(result)
2028
0
    goto out;
2029
2030
4.88k
  DEBUGASSERT(needle->origin);
2031
4.88k
  network_scheme = !(needle->origin->scheme->flags & PROTOPT_NONETWORK);
2032
2033
4.88k
#ifdef USE_UNIX_SOCKETS
2034
  /*************************************************************
2035
   * Set UDS first. It overrides "via_peer" and proxy settings.
2036
   *************************************************************/
2037
4.88k
  if(network_scheme && CURL_EASY_STR(data, STRING_UNIX_SOCKET_PATH)) {
2038
0
    result = Curl_peer_uds_create(
2039
0
      needle->origin->scheme, CURL_EASY_STR(data, STRING_UNIX_SOCKET_PATH),
2040
0
      (bool)data->set.abstract_unix_socket, &needle->via_peer);
2041
0
    if(result)
2042
0
      goto out;
2043
0
  }
2044
4.88k
#endif /* USE_UNIX_SOCKETS */
2045
2046
4.88k
  if(network_scheme && !needle->via_peer) {
2047
    /*************************************************************
2048
     * If the `via_peer` is not already set (via UDS above),
2049
     * determine if we talk to `conn->origin` directly or use
2050
     * `conn->via_peer` using "connect to" and "alt-svc" properties.
2051
     *************************************************************/
2052
4.88k
    result = url_set_conn_peer(data, needle);
2053
4.88k
    if(result)
2054
0
      goto out;
2055
4.88k
  }
2056
2057
  /*************************************************************
2058
   * Check whether the host and the "connect to host" are equal.
2059
   * Do this after the hostnames have been IDN-converted and
2060
   * before initializing the proxy.
2061
   *************************************************************/
2062
4.88k
  if(Curl_peer_equal(needle->origin, needle->via_peer)) {
2063
1
    Curl_peer_unlink(&needle->via_peer);
2064
1
  }
2065
2066
4.88k
#ifndef CURL_DISABLE_PROXY
2067
  /* Going via a unix socket ignores any proxy settings */
2068
4.88k
  if(network_scheme &&
2069
4.88k
     (!needle->via_peer || !needle->via_peer->unix_socket)) {
2070
4.88k
    result = Curl_proxy_init_conn(data, needle);
2071
4.88k
    if(result)
2072
0
      goto out;
2073
4.88k
  }
2074
4.88k
#endif /* CURL_DISABLE_PROXY */
2075
2076
4.88k
  result = url_set_conn_login(data, needle); /* default credentials */
2077
4.88k
  if(result)
2078
0
    goto out;
2079
2080
  /*************************************************************
2081
   * Setup internals depending on protocol. Needs to be done after
2082
   * we figured out what/if proxy to use.
2083
   *************************************************************/
2084
4.88k
  result = setup_connection_internals(data, needle);
2085
4.88k
  if(result)
2086
0
    goto out;
2087
2088
4.88k
  if(needle->scheme->flags & PROTOPT_ALPN) {
2089
    /* The protocol wants it, so set the bits if enabled in the easy handle
2090
       (default) */
2091
0
    if(data->set.ssl_enable_alpn)
2092
0
      needle->bits.tls_enable_alpn = TRUE;
2093
0
  }
2094
2095
4.88k
  if(network_scheme) {
2096
    /* Setup callbacks for network connections */
2097
4.88k
    needle->recv[FIRSTSOCKET] = Curl_cf_recv;
2098
4.88k
    needle->send[FIRSTSOCKET] = Curl_cf_send;
2099
4.88k
    needle->recv[SECONDARYSOCKET] = Curl_cf_recv;
2100
4.88k
    needle->send[SECONDARYSOCKET] = Curl_cf_send;
2101
4.88k
    needle->bits.tcp_fastopen = data->set.tcp_fastopen;
2102
4.88k
#ifdef USE_UNIX_SOCKETS
2103
4.88k
    if(Curl_conn_get_first_peer(needle, FIRSTSOCKET)->unix_socket)
2104
0
      needle->transport_wanted = TRNSPRT_UNIX;
2105
4.88k
#endif
2106
4.88k
  }
2107
2108
4.88k
out:
2109
4.88k
  if(!result) {
2110
4.88k
    DEBUGASSERT(needle);
2111
4.88k
    DEBUGASSERT(needle->origin);
2112
4.88k
    *pneedle = needle;
2113
4.88k
  }
2114
0
  else {
2115
0
    *pneedle = NULL;
2116
0
    if(needle)
2117
0
      Curl_conn_free(data, needle);
2118
0
  }
2119
4.88k
  return result;
2120
4.88k
}
2121
2122
static CURLcode url_set_data_origin_and_creds(struct Curl_easy *data)
2123
8.38k
{
2124
8.38k
  CURLcode result = CURLE_OK;
2125
8.38k
  CURLU *uh;
2126
8.38k
  CURLUcode uc;
2127
8.38k
  bool use_set_uh = (data->set.uh && !data->state.this_is_a_follow);
2128
8.38k
  uint16_t port_override = data->state.allow_port ? data->set.use_port : 0;
2129
8.38k
  uint32_t scope_id = 0;
2130
2131
  /*************************************************************
2132
   * Check input data
2133
   *************************************************************/
2134
8.38k
  if(!Curl_bufref_ptr(&data->state.url)) {
2135
0
    result = CURLE_URL_MALFORMAT;
2136
0
    goto out;
2137
0
  }
2138
2139
8.38k
  up_free(data); /* cleanup previous leftovers first */
2140
2141
  /* parse the URL */
2142
8.38k
  if(use_set_uh)
2143
0
    uh = data->state.uh = curl_url_dup(data->set.uh);
2144
8.38k
  else
2145
8.38k
    uh = data->state.uh = curl_url();
2146
8.38k
  if(!uh) {
2147
0
    result = CURLE_OUT_OF_MEMORY;
2148
0
    goto out;
2149
0
  }
2150
2151
  /* Calculate the *real* URL this transfer uses, applying defaults
2152
   * where information is missing. */
2153
8.38k
  if(CURL_EASY_STR(data, STRING_DEFAULT_PROTOCOL) &&
2154
0
     !Curl_is_absolute_url(Curl_bufref_ptr(&data->state.url), NULL, 0, TRUE)) {
2155
0
    char *url = curl_maprintf("%s://%s",
2156
0
                              CURL_EASY_STR(data, STRING_DEFAULT_PROTOCOL),
2157
0
                              Curl_bufref_ptr(&data->state.url));
2158
0
    if(!url) {
2159
0
      result = CURLE_OUT_OF_MEMORY;
2160
0
      goto out;
2161
0
    }
2162
0
    Curl_bufref_set(&data->state.url, url, 0, curl_free);
2163
0
  }
2164
2165
8.38k
  if(!use_set_uh) {
2166
8.38k
    char *newurl;
2167
8.38k
    uc = curl_url_set(uh, CURLUPART_URL, Curl_bufref_ptr(&data->state.url),
2168
8.38k
                      (unsigned int)(CURLU_GUESS_SCHEME |
2169
8.38k
                       CURLU_NON_SUPPORT_SCHEME |
2170
8.38k
                       (data->set.disallow_username_in_url ?
2171
8.38k
                        CURLU_DISALLOW_USER : 0) |
2172
8.38k
                       (data->set.path_as_is ? CURLU_PATH_AS_IS : 0)));
2173
8.38k
    if(uc) {
2174
2.45k
      failf(data, "URL rejected: %s", curl_url_strerror(uc));
2175
2.45k
      result = Curl_uc_to_curlcode(uc);
2176
2.45k
      goto out;
2177
2.45k
    }
2178
2179
    /* after it was parsed, get the generated normalized version */
2180
5.93k
    uc = curl_url_get(uh, CURLUPART_URL, &newurl, CURLU_GET_EMPTY);
2181
5.93k
    if(uc) {
2182
0
      result = Curl_uc_to_curlcode(uc);
2183
0
      goto out;
2184
0
    }
2185
5.93k
    Curl_bufref_set(&data->state.url, newurl, 0, curl_free);
2186
5.93k
  }
2187
2188
5.93k
#ifdef USE_IPV6
2189
5.93k
  scope_id = data->set.scope_id;
2190
5.93k
#endif
2191
2192
  /* `uh` is now as the connection should use it, probably. */
2193
5.93k
  result = Curl_peer_from_url(uh, data, port_override, scope_id,
2194
5.93k
                              &data->state.origin);
2195
5.93k
  if(result)
2196
1.05k
    goto out;
2197
  /* The origin might get changed when HSTS applies */
2198
4.88k
  result = hsts_upgrade(data, uh, port_override, scope_id);
2199
4.88k
  if(result)
2200
0
    goto out;
2201
2202
  /* When the transfers initial_origin is not set, this is the initial
2203
   * request. Remember this starting point. */
2204
4.88k
  if(!data->state.initial_origin)
2205
4.88k
    Curl_peer_link(&data->state.initial_origin, data->state.origin);
2206
2207
4.88k
  uc = curl_url_get(uh, CURLUPART_PATH, &data->state.up.path, CURLU_URLENCODE);
2208
4.88k
  if(uc) {
2209
0
    result = Curl_uc_to_curlcode(uc);
2210
0
    goto out;
2211
0
  }
2212
4.88k
  uc = curl_url_get(uh, CURLUPART_QUERY, &data->state.up.query,
2213
4.88k
                    CURLU_GET_EMPTY);
2214
4.88k
  if(uc && (uc != CURLUE_NO_QUERY)) {
2215
0
    result = CURLE_OUT_OF_MEMORY;
2216
0
    goto out;
2217
0
  }
2218
2219
4.88k
  uc = curl_url_get(uh, CURLUPART_OPTIONS, &data->state.up.options,
2220
4.88k
                    CURLU_URLDECODE);
2221
4.88k
  if(uc && (uc != CURLUE_NO_OPTIONS)) {
2222
0
    result = Curl_uc_to_curlcode(uc);
2223
0
    goto out;
2224
0
  }
2225
2226
4.88k
  result = url_set_data_creds(data, uh);
2227
4.88k
  if(result)
2228
3
    goto out;
2229
2230
8.38k
out:
2231
8.38k
  return result;
2232
4.88k
}
2233
2234
/**
2235
 * Find an existing connection for the transfer or create a new one.
2236
 * Returns
2237
 * - CURLE_OK on success with a connection attached to data
2238
 * - CURLE_NO_CONNECTION_AVAILABLE when connection limits apply or when
2239
 *   a suitable connection has not determined its multiplex capability.
2240
 * - a fatal error
2241
 */
2242
static CURLcode url_find_or_create_conn(struct Curl_easy *data,
2243
                                        const struct curltime *pnow)
2244
4.88k
{
2245
4.88k
  struct connectdata *needle = NULL;
2246
4.88k
  bool waitpipe = FALSE;
2247
4.88k
  CURLcode result;
2248
2249
  /* create the template connection for transfer data. Use this needle to
2250
   * find an existing connection or, if none exists, convert needle
2251
   * to a full connection and attach it to data. */
2252
4.88k
  result = url_create_needle(data, pnow, &needle);
2253
4.88k
  if(result)
2254
0
    goto out;
2255
4.88k
  DEBUGASSERT(needle);
2256
2257
  /***********************************************************************
2258
   * file: is a special case in that it does not need a network connection
2259
   ***********************************************************************/
2260
4.88k
#ifndef CURL_DISABLE_FILE
2261
4.88k
  if(needle->scheme->flags & PROTOPT_NONETWORK) {
2262
0
    bool done;
2263
    /* this is supposed to be the connect function so we better at least check
2264
       that the file is present here! */
2265
0
    DEBUGASSERT(needle->scheme->run->connect_it);
2266
0
    data->info.conn_scheme = needle->scheme->name;
2267
    /* conn_protocol can only provide "old" protocols */
2268
0
    data->info.conn_protocol = needle->scheme->protocol & CURLPROTO_MASK;
2269
0
    result = needle->scheme->run->connect_it(data, &done);
2270
0
    if(result)
2271
0
      goto out;
2272
2273
    /* Setup a "faked" transfer that will do nothing */
2274
0
    result = Curl_cpool_add(data, needle);
2275
0
    Curl_attach_connection(data, needle, TRUE);
2276
0
    needle = NULL;
2277
0
    if(!result) {
2278
      /* Setup whatever necessary for a resumed transfer */
2279
0
      result = setup_range(data);
2280
0
      if(!result) {
2281
0
        Curl_xfer_setup_nop(data);
2282
0
        result = Curl_init_transfer(data, data->conn);
2283
0
      }
2284
0
    }
2285
2286
0
    if(result) {
2287
0
      DEBUGASSERT(data->conn->scheme->run->done);
2288
      /* we ignore the return code for the protocol-specific DONE */
2289
0
      (void)data->conn->scheme->run->done(data, result, FALSE);
2290
0
    }
2291
0
    goto out;
2292
0
  }
2293
4.88k
#endif
2294
2295
  /* Complete the easy's SSL configuration for connection cache matching */
2296
4.88k
  result = Curl_ssl_easy_config_complete(data, needle->origin);
2297
4.88k
  if(result)
2298
0
    goto out;
2299
2300
  /*************************************************************
2301
   * Reuse of existing connection is not allowed when
2302
   * - connect_only is set or
2303
   * - reuse_fresh is set and this is not a follow-up request
2304
   *   (like with HTTP followlocation)
2305
   *************************************************************/
2306
4.88k
  if((!data->set.reuse_fresh || data->state.followlocation) &&
2307
4.87k
     !data->set.connect_only) {
2308
    /* Ok, try to find and attach an existing one */
2309
4.86k
    url_attach_existing(data, needle, &waitpipe);
2310
4.86k
  }
2311
2312
4.88k
  if(data->conn) {
2313
    /* We attached an existing connection for this transfer. Copy
2314
     * over transfer specific properties over from needle. */
2315
0
    struct connectdata *conn = data->conn;
2316
0
    VERBOSE(bool tls_upgraded = (!(needle->given->flags & PROTOPT_SSL) &&
2317
0
                                 Curl_conn_is_ssl(conn, FIRSTSOCKET)));
2318
2319
0
    conn->bits.reuse = TRUE;
2320
0
    url_conn_reuse_adjust(data, needle);
2321
2322
0
#ifndef CURL_DISABLE_PROXY
2323
0
    infof(data, "Reusing existing %s: connection%s with %s %s",
2324
0
          conn->given->name,
2325
0
          tls_upgraded ? " (upgraded to SSL)" : "",
2326
0
          (conn->socks_proxy.peer || conn->http_proxy.peer) ? "proxy" : "host",
2327
0
          conn->socks_proxy.peer ? conn->socks_proxy.peer->user_hostname :
2328
0
          conn->http_proxy.peer ? conn->http_proxy.peer->user_hostname :
2329
0
          conn->origin->hostname);
2330
#else
2331
    infof(data, "Reusing existing %s: connection%s with host %s",
2332
          conn->given->name,
2333
          tls_upgraded ? " (upgraded to SSL)" : "",
2334
          conn->origin->hostname);
2335
#endif
2336
0
  }
2337
4.88k
  else {
2338
    /* We have decided that we want a new connection. We may not be able to do
2339
       that if we have reached the limit of how many connections we are
2340
       allowed to open. */
2341
2342
4.88k
    if(waitpipe) {
2343
      /* There is a connection that *might* become usable for multiplexing
2344
         "soon", and we wait for that */
2345
0
      infof(data, "Waiting on connection to negotiate possible multiplexing.");
2346
0
      result = CURLE_NO_CONNECTION_AVAILABLE;
2347
0
      goto out;
2348
0
    }
2349
4.88k
    else {
2350
4.88k
      switch(Curl_cpool_check_limits(data, needle, &needle->created)) {
2351
0
      case CPOOL_LIMIT_DEST:
2352
0
        infof(data, "No more connections allowed to host");
2353
0
        result = CURLE_NO_CONNECTION_AVAILABLE;
2354
0
        goto out;
2355
0
      case CPOOL_LIMIT_TOTAL:
2356
0
        if(data->master_mid != UINT32_MAX)
2357
0
          CURL_TRC_M(data, "Allowing sub-requests (like DoH) to override "
2358
0
                     "max connection limit");
2359
0
        else {
2360
0
          infof(data, "No connections available, total of %u reached.",
2361
0
                data->multi->max_total_connections);
2362
0
          result = CURLE_NO_CONNECTION_AVAILABLE;
2363
0
          goto out;
2364
0
        }
2365
0
        break;
2366
4.88k
      default:
2367
4.88k
        break;
2368
4.88k
      }
2369
4.88k
    }
2370
2371
    /* Convert needle into a full connection by filling in all the
2372
     * remaining parts like the cloned SSL configuration. */
2373
4.88k
    result = Curl_ssl_conn_config_init(data, needle);
2374
4.88k
    if(result) {
2375
0
      DEBUGF(curl_mfprintf(stderr, "Error: init connection SSL config\n"));
2376
0
      goto out;
2377
0
    }
2378
2379
    /* Add needle to conn pool, which assigns the connection id.
2380
     * Attach regardless of result, for correct handling. */
2381
4.88k
    result = Curl_cpool_add(data, needle);
2382
4.88k
    Curl_attach_connection(data, needle, TRUE);
2383
4.88k
    needle = NULL;
2384
4.88k
    if(result)
2385
0
      goto out;
2386
2387
#ifdef USE_NTLM
2388
    /* If NTLM is requested in a part of this connection, make sure we do not
2389
       assume the state is fine as this is a fresh connection and NTLM is
2390
       connection based. */
2391
    if((data->state.authhost.picked & CURLAUTH_NTLM) &&
2392
       data->state.authhost.done) {
2393
      infof(data, "NTLM picked AND auth done set, clear picked");
2394
      data->state.authhost.picked = CURLAUTH_NONE;
2395
      data->state.authhost.done = FALSE;
2396
    }
2397
2398
    if((data->state.authproxy.picked & CURLAUTH_NTLM) &&
2399
       data->state.authproxy.done) {
2400
      infof(data, "NTLM-proxy picked AND auth done set, clear picked");
2401
      data->state.authproxy.picked = CURLAUTH_NONE;
2402
      data->state.authproxy.done = FALSE;
2403
    }
2404
#endif
2405
4.88k
  }
2406
2407
  /* Setup and init stuff before DO starts, in preparing for the transfer. */
2408
4.88k
  result = Curl_init_transfer(data, data->conn);
2409
4.88k
  if(result)
2410
0
    goto out;
2411
2412
  /* Setup whatever necessary for a resumed transfer */
2413
4.88k
  result = setup_range(data);
2414
4.88k
  if(result)
2415
0
    goto out;
2416
2417
  /* persist the scheme and handler the transfer is using */
2418
4.88k
  data->info.conn_scheme = data->conn->scheme->name;
2419
  /* conn_protocol can only provide "old" protocols */
2420
4.88k
  data->info.conn_protocol = data->conn->scheme->protocol & CURLPROTO_MASK;
2421
4.88k
  data->info.used_proxy =
2422
#ifdef CURL_DISABLE_PROXY
2423
    0
2424
#else
2425
4.88k
    (data->conn->socks_proxy.peer || data->conn->http_proxy.peer)
2426
4.88k
#endif
2427
4.88k
    ;
2428
2429
  /* Lastly, inform connection filters that a new transfer is attached */
2430
4.88k
  result = Curl_conn_ev_data_setup(data);
2431
2432
4.88k
out:
2433
4.88k
  if(needle)
2434
0
    Curl_conn_free(data, needle);
2435
4.88k
  DEBUGASSERT(result || data->conn);
2436
4.88k
  return result;
2437
4.88k
}
2438
2439
CURLcode Curl_connect(struct Curl_easy *data, bool *pconnected)
2440
8.38k
{
2441
8.38k
  CURLcode result;
2442
8.38k
  struct connectdata *conn = NULL;
2443
8.38k
  const struct curltime *pnow = NULL;
2444
8.38k
  *pconnected = FALSE;
2445
2446
  /* Set the request to virgin state based on transfer settings */
2447
8.38k
  Curl_req_hard_reset(&data->req, data);
2448
  /* Determine the origin of the transfer and what credentials to use */
2449
8.38k
  result = url_set_data_origin_and_creds(data);
2450
8.38k
  if(result)
2451
3.50k
    goto out;
2452
4.88k
  if(!data->state.origin) { /* just make really sure */
2453
0
    DEBUGASSERT(0);
2454
0
    result = CURLE_FAILED_INIT;
2455
0
    goto out;
2456
0
  }
2457
2458
  /* Get or create a connection for the transfer. */
2459
4.88k
  pnow = Curl_pgrs_now(data);
2460
4.88k
  Curl_pgrsTimeWas(data, TIMER_POSTQUEUE, *pnow);
2461
4.88k
  result = url_find_or_create_conn(data, pnow);
2462
4.88k
  conn = data->conn;
2463
4.88k
  if(result)
2464
0
    goto out;
2465
4.88k
  if(!data->conn) { /* just make really sure */
2466
0
    DEBUGASSERT(0);
2467
0
    result = CURLE_FAILED_INIT;
2468
0
    goto out;
2469
0
  }
2470
2471
4.88k
  if(conn->bits.reuse) {
2472
0
    if(conn->attached_xfers > 1)
2473
      /* multiplexed */
2474
0
      *pconnected = TRUE;
2475
0
  }
2476
4.88k
  else if(conn->scheme->flags & PROTOPT_NONETWORK) {
2477
0
    Curl_pgrsTime(data, TIMER_NAMELOOKUP);
2478
0
    *pconnected = TRUE;
2479
0
  }
2480
4.88k
  else {
2481
4.88k
    result = Curl_conn_setup(data, conn, FIRSTSOCKET, CURL_CF_SSL_DEFAULT);
2482
4.88k
    if(!result)
2483
4.88k
      result = Curl_headers_init(data);
2484
4.88k
    CURL_TRC_M(data, "Curl_conn_setup() -> %d", (int)result);
2485
4.88k
  }
2486
2487
8.38k
out:
2488
8.38k
  if(result == CURLE_NO_CONNECTION_AVAILABLE)
2489
8.38k
    DEBUGASSERT(!conn);
2490
2491
8.38k
  if(result && conn) {
2492
    /* We are not allowed to return failure with memory left allocated in the
2493
       connectdata struct, free those here */
2494
0
    Curl_detach_connection(data);
2495
0
    Curl_conn_close(data, conn, TRUE);
2496
0
  }
2497
2498
8.38k
  return result;
2499
8.38k
}
2500
2501
/*
2502
 * Curl_init_transfer() is called each time before the transfer starts - to
2503
 * prepare for a transfer, sometimes multiple times on the same Curl_easy.
2504
 * Make sure nothing in here depends on stuff that is setup dynamically for
2505
 * the transfer.
2506
 *
2507
 * Allow this function to get called with 'conn' set to NULL.
2508
 */
2509
2510
CURLcode Curl_init_transfer(struct Curl_easy *data, struct connectdata *conn)
2511
4.88k
{
2512
4.88k
  CURLcode result;
2513
2514
4.88k
  if(conn) {
2515
4.88k
    conn->bits.do_more = FALSE; /* by default there is no curl_do_more() to
2516
                                   use */
2517
    /* if the protocol used does not support wildcards, switch it off */
2518
4.88k
    if(data->state.wildcardmatch &&
2519
0
       !(conn->scheme->flags & PROTOPT_WILDCARD))
2520
0
      data->state.wildcardmatch = FALSE;
2521
4.88k
  }
2522
2523
4.88k
  data->state.done = FALSE; /* *_done() is not called yet */
2524
2525
4.88k
  data->req.no_body = data->set.opt_no_body;
2526
4.88k
  if(data->req.no_body)
2527
    /* in HTTP lingo, no body means using the HEAD request... */
2528
10
    data->state.httpreq = HTTPREQ_HEAD;
2529
2530
4.88k
  result = Curl_req_start(&data->req, data);
2531
4.88k
  if(!result) {
2532
4.88k
    Curl_pgrsReset(data);
2533
4.88k
  }
2534
4.88k
  return result;
2535
4.88k
}
2536
2537
#if defined(USE_HTTP2) || defined(USE_HTTP3)
2538
2539
void Curl_data_priority_clear_state(struct Curl_easy *data)
2540
8.38k
{
2541
8.38k
  data->state.weight = 0;
2542
8.38k
}
2543
2544
#endif /* USE_HTTP2 || USE_HTTP3 */
2545
2546
CURLcode Curl_conn_meta_set(struct connectdata *conn, const char *key,
2547
                            void *meta_data, Curl_meta_dtor *meta_dtor)
2548
0
{
2549
0
  if(!Curl_hash_add2(&conn->meta_hash, CURL_UNCONST(key), strlen(key) + 1,
2550
0
                     meta_data, meta_dtor)) {
2551
0
    meta_dtor(CURL_UNCONST(key), strlen(key) + 1, meta_data);
2552
0
    return CURLE_OUT_OF_MEMORY;
2553
0
  }
2554
0
  return CURLE_OK;
2555
0
}
2556
2557
void Curl_conn_meta_remove(struct connectdata *conn, const char *key)
2558
4.88k
{
2559
4.88k
  Curl_hash_delete(&conn->meta_hash, CURL_UNCONST(key), strlen(key) + 1);
2560
4.88k
}
2561
2562
void *Curl_conn_meta_get(struct connectdata *conn, const char *key)
2563
0
{
2564
0
  return Curl_hash_pick(&conn->meta_hash, CURL_UNCONST(key), strlen(key) + 1);
2565
0
}
2566
2567
struct Curl_easy *Curl_get_admin(struct Curl_easy *data)
2568
14.6k
{
2569
14.6k
  struct Curl_easy *admin;
2570
2571
14.6k
  if(!data->mid) /* already an admin handle */
2572
0
    admin = data;
2573
14.6k
  else if(data->multi)
2574
14.6k
    admin = data->multi->admin;
2575
0
  else if(data->multi_easy)
2576
0
    admin = data->multi_easy->admin;
2577
0
  else {
2578
0
    DEBUGASSERT(0); /* we do not want this. does it happen? */
2579
0
    admin = data;
2580
0
  }
2581
14.6k
  if(admin != data) {
2582
14.6k
    admin->set.conn_max_idle_ms = data->set.conn_max_idle_ms;
2583
14.6k
    admin->set.conn_max_age_ms = data->set.conn_max_age_ms;
2584
14.6k
    admin->set.upkeep_interval_ms = data->set.upkeep_interval_ms;
2585
14.6k
    admin->set.timeout = data->set.timeout;
2586
14.6k
    admin->set.server_response_timeout = data->set.server_response_timeout;
2587
14.6k
    admin->set.no_signal = data->set.no_signal;
2588
14.6k
  }
2589
14.6k
  return admin;
2590
14.6k
}
2591
2592
CURLcode Curl_1st_fatal(CURLcode r1, CURLcode r2)
2593
4.88k
{
2594
4.88k
  if(r1 && (r1 != CURLE_AGAIN))
2595
836
    return r1;
2596
4.04k
  if(r2 && (r2 != CURLE_AGAIN))
2597
0
    return r2;
2598
4.04k
  return r1;
2599
4.04k
}