Coverage Report

Created: 2026-09-14 07:04

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