Coverage Report

Created: 2026-09-01 06:58

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