2020-12-12 23:55:09 -08:00
|
|
|
/***************************************************************************
|
|
|
|
|
* _ _ ____ _
|
|
|
|
|
* Project ___| | | | _ \| |
|
|
|
|
|
* / __| | | | |_) | |
|
|
|
|
|
* | (__| |_| | _ <| |___
|
|
|
|
|
* \___|\___/|_| \_\_____|
|
|
|
|
|
*
|
2025-12-20 14:32:49 +01:00
|
|
|
* Copyright (C) Jacob Hoffman-Andrews, <github@hoffman-andrews.com>
|
2024-02-25 21:50:18 +01:00
|
|
|
* Copyright (C) kpcyrd, <kpcyrd@archlinux.org>
|
2025-03-21 14:43:07 -04:00
|
|
|
* Copyright (C) Daniel McCarney, <daniel@binaryparadox.net>
|
2020-12-12 23:55:09 -08:00
|
|
|
*
|
|
|
|
|
* This software is licensed as described in the file COPYING, which
|
|
|
|
|
* you should have received as part of this distribution. The terms
|
|
|
|
|
* are also available at https://curl.se/docs/copyright.html.
|
|
|
|
|
*
|
|
|
|
|
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
|
|
|
|
|
* copies of the Software, and permit persons to whom the Software is
|
|
|
|
|
* furnished to do so, under the terms of the COPYING file.
|
|
|
|
|
*
|
|
|
|
|
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
|
|
|
|
* KIND, either express or implied.
|
|
|
|
|
*
|
|
|
|
|
* SPDX-License-Identifier: curl
|
2022-05-17 11:16:50 +02:00
|
|
|
*
|
2020-12-12 23:55:09 -08:00
|
|
|
***************************************************************************/
|
lib: use lib source directory as base include path
Backtrack on previous change that aimed to solve the wrong `share.h`
being included. It turns out it did not fix this issue. At the same time
it introduced relative header filenames and the need to include the same
headers differently depending on the source files' location, reducing
readability and editability.
Replace this method by re-adding curl's lib source directory to the
header path and addressing headers by the their full, relative name to
that base directory. Aligning with this method already used in src and
tests.
With these advantages:
- makes includes easier to read, recognize, grep, sort, write, and copy
between sources,
- syncs the way these headers are included across curl components,
- avoids the ambiguity between system `schannel.h`, `rustls.h` vs.
local headers using the same names in `lib/vtls`,
- silences clang-tidy `readability-duplicate-include` checker, which
detects the above issue,
Ref: https://clang.llvm.org/extra/clang-tidy/checks/readability/duplicate-include.html
- possibly silences TIOBE coding standard warnings:
`6.10.2.a: Don't use relative paths in #include statements.`
- long shot: it works well with concatenated test sources, for
clang-tidy-friendly custom unity builds. Ref: #20667
Slight downside: it's not enforced.
If there happens to be a collision between a local `lib/*.h` header and
a system one, the solution is to rename (possibly with its `.c`
counterpart) into the `curl_` namespace. This is also the method used by
curl in the past.
Also:
- curlx/inet_pton: reduce scope of an include.
- toolx/tool_time: apply this to an include, and update VS project
files accordingly. Also dropping unnecessary lib/curlx header path.
- clang-tidy: enable `readability-duplicate-include`.
Follow-up to 3887069c661b40e76b053a4867eb565d4761ab3e #19676
Follow-up to 625f2c1644da58b9617479775badea21f125ce6d #16991 #16949
Closes #20623
2026-02-19 00:44:43 +01:00
|
|
|
#include "curl_setup.h"
|
2020-12-12 23:55:09 -08:00
|
|
|
|
|
|
|
|
#ifdef USE_RUSTLS
|
|
|
|
|
|
2021-11-02 22:14:02 -07:00
|
|
|
#include <rustls.h>
|
2020-12-12 23:55:09 -08:00
|
|
|
|
lib: use lib source directory as base include path
Backtrack on previous change that aimed to solve the wrong `share.h`
being included. It turns out it did not fix this issue. At the same time
it introduced relative header filenames and the need to include the same
headers differently depending on the source files' location, reducing
readability and editability.
Replace this method by re-adding curl's lib source directory to the
header path and addressing headers by the their full, relative name to
that base directory. Aligning with this method already used in src and
tests.
With these advantages:
- makes includes easier to read, recognize, grep, sort, write, and copy
between sources,
- syncs the way these headers are included across curl components,
- avoids the ambiguity between system `schannel.h`, `rustls.h` vs.
local headers using the same names in `lib/vtls`,
- silences clang-tidy `readability-duplicate-include` checker, which
detects the above issue,
Ref: https://clang.llvm.org/extra/clang-tidy/checks/readability/duplicate-include.html
- possibly silences TIOBE coding standard warnings:
`6.10.2.a: Don't use relative paths in #include statements.`
- long shot: it works well with concatenated test sources, for
clang-tidy-friendly custom unity builds. Ref: #20667
Slight downside: it's not enforced.
If there happens to be a collision between a local `lib/*.h` header and
a system one, the solution is to rename (possibly with its `.c`
counterpart) into the `curl_` namespace. This is also the method used by
curl in the past.
Also:
- curlx/inet_pton: reduce scope of an include.
- toolx/tool_time: apply this to an include, and update VS project
files accordingly. Also dropping unnecessary lib/curlx header path.
- clang-tidy: enable `readability-duplicate-include`.
Follow-up to 3887069c661b40e76b053a4867eb565d4761ab3e #19676
Follow-up to 625f2c1644da58b9617479775badea21f125ce6d #16991 #16949
Closes #20623
2026-02-19 00:44:43 +01:00
|
|
|
#include "curlx/fopen.h"
|
|
|
|
|
#include "curlx/strerr.h"
|
|
|
|
|
#include "urldata.h"
|
|
|
|
|
#include "curl_trc.h"
|
2026-08-04 11:52:00 +02:00
|
|
|
#include "vdns/cf-dns.h"
|
|
|
|
|
#include "vdns/httpsrr.h"
|
lib: use lib source directory as base include path
Backtrack on previous change that aimed to solve the wrong `share.h`
being included. It turns out it did not fix this issue. At the same time
it introduced relative header filenames and the need to include the same
headers differently depending on the source files' location, reducing
readability and editability.
Replace this method by re-adding curl's lib source directory to the
header path and addressing headers by the their full, relative name to
that base directory. Aligning with this method already used in src and
tests.
With these advantages:
- makes includes easier to read, recognize, grep, sort, write, and copy
between sources,
- syncs the way these headers are included across curl components,
- avoids the ambiguity between system `schannel.h`, `rustls.h` vs.
local headers using the same names in `lib/vtls`,
- silences clang-tidy `readability-duplicate-include` checker, which
detects the above issue,
Ref: https://clang.llvm.org/extra/clang-tidy/checks/readability/duplicate-include.html
- possibly silences TIOBE coding standard warnings:
`6.10.2.a: Don't use relative paths in #include statements.`
- long shot: it works well with concatenated test sources, for
clang-tidy-friendly custom unity builds. Ref: #20667
Slight downside: it's not enforced.
If there happens to be a collision between a local `lib/*.h` header and
a system one, the solution is to rename (possibly with its `.c`
counterpart) into the `curl_` namespace. This is also the method used by
curl in the past.
Also:
- curlx/inet_pton: reduce scope of an include.
- toolx/tool_time: apply this to an include, and update VS project
files accordingly. Also dropping unnecessary lib/curlx header path.
- clang-tidy: enable `readability-duplicate-include`.
Follow-up to 3887069c661b40e76b053a4867eb565d4761ab3e #19676
Follow-up to 625f2c1644da58b9617479775badea21f125ce6d #16991 #16949
Closes #20623
2026-02-19 00:44:43 +01:00
|
|
|
#include "vtls/vtls.h"
|
|
|
|
|
#include "vtls/vtls_int.h"
|
|
|
|
|
#include "vtls/rustls.h"
|
|
|
|
|
#include "vtls/keylog.h"
|
|
|
|
|
#include "vtls/cipher_suite.h"
|
|
|
|
|
#include "vtls/x509asn1.h"
|
2026-03-11 14:16:31 +01:00
|
|
|
#ifdef USE_ECH
|
|
|
|
|
#include "curlx/base64.h"
|
|
|
|
|
#endif
|
2020-12-12 23:55:09 -08:00
|
|
|
|
socket: introduce `SOCK_EAGAIN()` and use it
To contain the logic of checking for both `EWOULDBLOCK` and/or `EAGAIN`
depending on platform/availability. Also to avoid checking for both if
they mapp to the same value, and to avoid PP guards around use.
This also ensures `EAGAIN` is consistently not checked on Windows, where
headers defined it, but `SOCKERRNO` never returns it, because curl maps
it to `WSAGetLastError()`.
If they map to the same value, checking them both in an `if` expression
trips GCC warning `-Wlogical-op` (the same way it triggers duplicate
case value error in `switch`).
Also:
- replace two `switch()` statements with the new macro.
- tests/server/sws: make two outliers use the new macro that were only
checking for `EWOULDBLOCK` before this patch, in `connect_to()`.
- move variables to the left-side of expressions, where missing.
- rustls: use a variant of this macro that uses raw `EWOULDBLOCK`.
Tried tracing it back to the origins, but I couldn't figure out if
this is working as expected on all supported Windows versions in
Rust. It seems to be using `GetLastError()`, according to
https://docs.rs/system_error/0.2.0/system_error/, which would be
probably incorrect.
Notes:
- it's probably a good idea to assign `SOCKERRNO` to a variable before
passing it to this macro.
Cherry-picked from #21893
Closes #21992
2026-06-08 01:21:44 +02:00
|
|
|
#if EAGAIN != EWOULDBLOCK
|
|
|
|
|
#define RAW_EAGAIN(e) ((e) == EWOULDBLOCK || (e) == EAGAIN)
|
|
|
|
|
#else
|
|
|
|
|
#define RAW_EAGAIN(e) ((e) == EWOULDBLOCK)
|
|
|
|
|
#endif
|
|
|
|
|
|
2025-11-29 02:16:52 +01:00
|
|
|
struct rustls_ssl_backend_data {
|
2020-12-12 23:55:09 -08:00
|
|
|
const struct rustls_client_config *config;
|
2021-05-14 18:45:49 -07:00
|
|
|
struct rustls_connection *conn;
|
2024-05-16 14:59:25 +02:00
|
|
|
size_t plain_out_buffered;
|
|
|
|
|
BIT(data_in_pending);
|
2024-06-07 10:12:39 +02:00
|
|
|
BIT(sent_shutdown);
|
2020-12-12 23:55:09 -08:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* For a given rustls_result error code, return the best-matching CURLcode. */
|
2025-03-21 14:43:07 -04:00
|
|
|
static CURLcode map_error(const rustls_result r)
|
2020-12-12 23:55:09 -08:00
|
|
|
{
|
|
|
|
|
if(rustls_result_is_cert_error(r)) {
|
|
|
|
|
return CURLE_PEER_FAILED_VERIFICATION;
|
|
|
|
|
}
|
|
|
|
|
switch(r) {
|
2025-11-29 02:16:52 +01:00
|
|
|
case RUSTLS_RESULT_OK:
|
|
|
|
|
return CURLE_OK;
|
|
|
|
|
case RUSTLS_RESULT_NULL_PARAMETER:
|
|
|
|
|
return CURLE_BAD_FUNCTION_ARGUMENT;
|
|
|
|
|
default:
|
|
|
|
|
return CURLE_RECV_ERROR;
|
2020-12-12 23:55:09 -08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-29 02:16:52 +01:00
|
|
|
static void rustls_failf(struct Curl_easy *data, const rustls_result rr,
|
|
|
|
|
const char *msg)
|
2025-03-21 14:43:07 -04:00
|
|
|
{
|
|
|
|
|
char errorbuf[STRERROR_LEN];
|
|
|
|
|
size_t errorlen;
|
|
|
|
|
rustls_error(rr, errorbuf, sizeof(errorbuf), &errorlen);
|
|
|
|
|
failf(data, "%s: %.*s", msg, (int)errorlen, errorbuf);
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-29 02:16:52 +01:00
|
|
|
static bool cr_data_pending(struct Curl_cfilter *cf,
|
|
|
|
|
const struct Curl_easy *data)
|
2020-12-12 23:55:09 -08:00
|
|
|
{
|
2025-03-21 14:43:07 -04:00
|
|
|
const struct ssl_connect_data *ctx = cf->ctx;
|
2023-05-09 10:10:40 +00:00
|
|
|
struct rustls_ssl_backend_data *backend;
|
2022-11-22 09:55:41 +01:00
|
|
|
|
|
|
|
|
(void)data;
|
|
|
|
|
DEBUGASSERT(ctx && ctx->backend);
|
2023-05-09 10:10:40 +00:00
|
|
|
backend = (struct rustls_ssl_backend_data *)ctx->backend;
|
2026-01-01 16:38:56 +01:00
|
|
|
return (bool)backend->data_in_pending;
|
2020-12-12 23:55:09 -08:00
|
|
|
}
|
|
|
|
|
|
2022-11-25 14:06:43 +01:00
|
|
|
struct io_ctx {
|
|
|
|
|
struct Curl_cfilter *cf;
|
|
|
|
|
struct Curl_easy *data;
|
|
|
|
|
};
|
|
|
|
|
|
2025-11-29 02:16:52 +01:00
|
|
|
static int read_cb(void *userdata, uint8_t *buf, uintptr_t len,
|
|
|
|
|
uintptr_t *out_n)
|
2021-05-14 18:45:49 -07:00
|
|
|
{
|
2025-03-21 14:43:07 -04:00
|
|
|
const struct io_ctx *io_ctx = userdata;
|
2025-11-29 02:16:52 +01:00
|
|
|
struct ssl_connect_data * const connssl = io_ctx->cf->ctx;
|
2022-11-25 14:06:43 +01:00
|
|
|
CURLcode result;
|
|
|
|
|
int ret = 0;
|
2025-06-11 10:18:15 +02:00
|
|
|
size_t nread;
|
|
|
|
|
|
|
|
|
|
result = Curl_conn_cf_recv(io_ctx->cf->next, io_ctx->data,
|
|
|
|
|
(char *)buf, len, &nread);
|
|
|
|
|
if(result) {
|
2022-11-25 14:06:43 +01:00
|
|
|
nread = 0;
|
core: stop redefining `E*` macros on Windows, map `EACCES`, related fixes
Before this patch, standard `E*` errno codes were redefined on Windows,
onto matching winsock2 `WSA*` error codes, which have different values.
This broke uses where using the `E*` value in non-socket context, or
other places expecting a POSIX `errno`, e.g. file I/O, threads, IDN or
interfacing with dependencies.
Fix it by introducing a curl-specific `SOCKE*` set of macros that map to
`WSA*` on Windows and standard POSIX codes on other platforms. Then
verify and update the code to use `SOCKE*` or `E*` macro depending on
context.
- Add `SOCKE*` macros that map to either winsock2 or POSIX error codes.
And use them with `SOCKERRNO` or in contexts requiring
platform-dependent socket error codes.
This fixes `E*` uses which were supposed be POSIX values, not `WSA*`
socket errors, on Windows:
- lib/curl_multibyte.c
- lib/curl_threads.c
- lib/idn.c
- lib/vtls/gtls.c
- lib/vtls/rustls.c
- src/tool_cb_wrt.c
- src/tool_dirhie.c
- Ban `E*` codes having a `SOCKE*` mapping, via checksrc.
Authored-by: Daniel Stenberg
- Add exceptions for `E*` codes used in file I/O, or other contexts
requiring POSIX error codes.
Also:
- ftp: fix missing `SOCKEACCES` mapping for Windows.
- add `SOCKENOMEM` for `Curl_getaddrinfo()` via `asyn-thread.c`.
- tests/server/sockfilt: fix to set `SOCKERRNO` in local `select()`
override on Windows.
- lib/inet_ntop: fix to return `WSAEINVAL` on Windows, where `ENOSPC` is
used on other platforms. To simulate Windows' built-in `inet_ntop()`,
as tested on a Win10 machine.
Note:
- WINE returns `STATUS_INVALID_PARAMETER` = `0xC000000D`.
- Microsoft documentation says it returns `WSA_INVALID_PARAMETER`
(= `ERROR_INVALID_PARAMETER`) 87:
https://learn.microsoft.com/windows/win32/api/ws2tcpip/nf-ws2tcpip-inet_ntop#return-value
- lib/inet_ntop: drop redundant `CURL_SETERRNO(ENOSPC)`.
`inet_ntop4()` already sets it before returning `NULL`.
- replace stray `WSAEWOULDBLOCK` with `USE_WINSOCK` macro to detect
winsock2.
- move existing `SOCKE*` mappings from `tests/server` to
`curl_setup_once.h`.
- add missing `EINTR`, `EINVAL` constants for WinCE.
Follow-up to abf80aae384319ef9b19ffbd0d69a1fbe7421f1f #16612
Follow-up to d69425ed7d0918aceddd96048b146a9df85638ec #16615
Bug: https://github.com/curl/curl/pull/16553#issuecomment-2704679377
Closes #16621
2025-03-08 02:18:35 +01:00
|
|
|
/* !checksrc! disable ERRNOVAR 4 */
|
2026-02-07 15:59:59 +01:00
|
|
|
if(result == CURLE_AGAIN)
|
2022-11-25 14:06:43 +01:00
|
|
|
ret = EAGAIN;
|
|
|
|
|
else
|
|
|
|
|
ret = EINVAL;
|
2021-05-14 18:45:49 -07:00
|
|
|
}
|
2024-03-08 09:37:27 +01:00
|
|
|
else if(nread == 0)
|
|
|
|
|
connssl->peer_closed = TRUE;
|
2024-05-13 17:56:19 +02:00
|
|
|
*out_n = (uintptr_t)nread;
|
2025-06-11 10:18:15 +02:00
|
|
|
CURL_TRC_CF(io_ctx->data, io_ctx->cf, "cf->next recv(len=%zu) -> %d, %zu",
|
2026-04-16 10:52:57 +02:00
|
|
|
(size_t)len, (int)result, nread);
|
2022-11-25 14:06:43 +01:00
|
|
|
return ret;
|
2021-05-14 18:45:49 -07:00
|
|
|
}
|
|
|
|
|
|
2025-11-29 02:16:52 +01:00
|
|
|
static int write_cb(void *userdata, const uint8_t *buf, uintptr_t len,
|
|
|
|
|
uintptr_t *out_n)
|
2021-05-14 18:45:49 -07:00
|
|
|
{
|
2025-03-21 14:43:07 -04:00
|
|
|
const struct io_ctx *io_ctx = userdata;
|
2022-11-25 14:06:43 +01:00
|
|
|
CURLcode result;
|
|
|
|
|
int ret = 0;
|
2025-06-11 10:18:15 +02:00
|
|
|
size_t nwritten;
|
|
|
|
|
|
|
|
|
|
result = Curl_conn_cf_send(io_ctx->cf->next, io_ctx->data,
|
2025-11-27 15:13:11 +01:00
|
|
|
buf, len, FALSE, &nwritten);
|
2025-06-11 10:18:15 +02:00
|
|
|
if(result) {
|
2022-11-25 14:06:43 +01:00
|
|
|
nwritten = 0;
|
2026-02-07 15:59:59 +01:00
|
|
|
if(result == CURLE_AGAIN)
|
2022-11-25 14:06:43 +01:00
|
|
|
ret = EAGAIN;
|
|
|
|
|
else
|
|
|
|
|
ret = EINVAL;
|
2021-05-14 18:45:49 -07:00
|
|
|
}
|
2024-05-13 17:56:19 +02:00
|
|
|
*out_n = (uintptr_t)nwritten;
|
2025-06-11 10:18:15 +02:00
|
|
|
CURL_TRC_CF(io_ctx->data, io_ctx->cf, "cf->next send(len=%zu) -> %d, %zu",
|
2026-04-16 10:52:57 +02:00
|
|
|
len, (int)result, nwritten);
|
2022-11-25 14:06:43 +01:00
|
|
|
return ret;
|
2021-05-14 18:45:49 -07:00
|
|
|
}
|
|
|
|
|
|
2023-03-31 11:07:18 +02:00
|
|
|
static ssize_t tls_recv_more(struct Curl_cfilter *cf,
|
|
|
|
|
struct Curl_easy *data, CURLcode *err)
|
2020-12-12 23:55:09 -08:00
|
|
|
{
|
2025-11-29 02:16:52 +01:00
|
|
|
const struct ssl_connect_data * const connssl = cf->ctx;
|
|
|
|
|
struct rustls_ssl_backend_data * const backend =
|
2023-05-09 10:10:40 +00:00
|
|
|
(struct rustls_ssl_backend_data *)connssl->backend;
|
2022-11-25 14:06:43 +01:00
|
|
|
struct io_ctx io_ctx;
|
2021-05-14 18:45:49 -07:00
|
|
|
size_t tls_bytes_read = 0;
|
|
|
|
|
rustls_io_result io_error;
|
2023-03-31 11:07:18 +02:00
|
|
|
rustls_result rresult = 0;
|
2022-02-17 17:48:48 +01:00
|
|
|
|
2022-11-25 14:06:43 +01:00
|
|
|
io_ctx.cf = cf;
|
|
|
|
|
io_ctx.data = data;
|
2023-03-31 11:07:18 +02:00
|
|
|
io_error = rustls_connection_read_tls(backend->conn, read_cb, &io_ctx,
|
2022-11-25 14:06:43 +01:00
|
|
|
&tls_bytes_read);
|
socket: introduce `SOCK_EAGAIN()` and use it
To contain the logic of checking for both `EWOULDBLOCK` and/or `EAGAIN`
depending on platform/availability. Also to avoid checking for both if
they mapp to the same value, and to avoid PP guards around use.
This also ensures `EAGAIN` is consistently not checked on Windows, where
headers defined it, but `SOCKERRNO` never returns it, because curl maps
it to `WSAGetLastError()`.
If they map to the same value, checking them both in an `if` expression
trips GCC warning `-Wlogical-op` (the same way it triggers duplicate
case value error in `switch`).
Also:
- replace two `switch()` statements with the new macro.
- tests/server/sws: make two outliers use the new macro that were only
checking for `EWOULDBLOCK` before this patch, in `connect_to()`.
- move variables to the left-side of expressions, where missing.
- rustls: use a variant of this macro that uses raw `EWOULDBLOCK`.
Tried tracing it back to the origins, but I couldn't figure out if
this is working as expected on all supported Windows versions in
Rust. It seems to be using `GetLastError()`, according to
https://docs.rs/system_error/0.2.0/system_error/, which would be
probably incorrect.
Notes:
- it's probably a good idea to assign `SOCKERRNO` to a variable before
passing it to this macro.
Cherry-picked from #21893
Closes #21992
2026-06-08 01:21:44 +02:00
|
|
|
if(RAW_EAGAIN(io_error)) {
|
2023-03-31 11:07:18 +02:00
|
|
|
*err = CURLE_AGAIN;
|
|
|
|
|
return -1;
|
2021-05-14 18:45:49 -07:00
|
|
|
}
|
|
|
|
|
else if(io_error) {
|
2021-09-08 11:27:49 +02:00
|
|
|
char buffer[STRERROR_LEN];
|
|
|
|
|
failf(data, "reading from socket: %s",
|
2025-10-04 03:10:37 +02:00
|
|
|
curlx_strerror(io_error, buffer, sizeof(buffer)));
|
2024-05-16 08:55:14 +02:00
|
|
|
*err = CURLE_RECV_ERROR;
|
2020-12-12 23:55:09 -08:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-31 11:07:18 +02:00
|
|
|
rresult = rustls_connection_process_new_packets(backend->conn);
|
2021-05-14 18:45:49 -07:00
|
|
|
if(rresult != RUSTLS_RESULT_OK) {
|
2025-03-21 14:43:07 -04:00
|
|
|
rustls_failf(data, rresult, "rustls_connection_process_new_packets");
|
2021-05-14 18:45:49 -07:00
|
|
|
*err = map_error(rresult);
|
|
|
|
|
return -1;
|
2020-12-12 23:55:09 -08:00
|
|
|
}
|
|
|
|
|
|
2024-05-16 14:59:25 +02:00
|
|
|
backend->data_in_pending = TRUE;
|
2023-03-31 11:07:18 +02:00
|
|
|
*err = CURLE_OK;
|
|
|
|
|
return (ssize_t)tls_bytes_read;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
2025-09-25 12:59:36 +02:00
|
|
|
* Filter receive method implementation. `plainbuf` and `plainlen`
|
|
|
|
|
* are always not NULL/0.
|
2023-03-31 11:07:18 +02:00
|
|
|
*/
|
2025-11-29 02:16:52 +01:00
|
|
|
static CURLcode cr_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
|
|
|
|
|
char *plainbuf, size_t plainlen, size_t *pnread)
|
2023-03-31 11:07:18 +02:00
|
|
|
{
|
2025-11-29 02:16:52 +01:00
|
|
|
const struct ssl_connect_data * const connssl = cf->ctx;
|
|
|
|
|
struct rustls_ssl_backend_data * const backend =
|
2023-05-09 10:10:40 +00:00
|
|
|
(struct rustls_ssl_backend_data *)connssl->backend;
|
2023-03-31 11:07:18 +02:00
|
|
|
struct rustls_connection *rconn = NULL;
|
2025-06-11 14:50:15 +02:00
|
|
|
CURLcode result = CURLE_OK;
|
2023-03-31 11:07:18 +02:00
|
|
|
size_t n = 0;
|
|
|
|
|
rustls_result rresult = 0;
|
|
|
|
|
bool eof = FALSE;
|
|
|
|
|
|
|
|
|
|
DEBUGASSERT(backend);
|
2025-06-11 14:50:15 +02:00
|
|
|
*pnread = 0;
|
2023-03-31 11:07:18 +02:00
|
|
|
rconn = backend->conn;
|
2021-05-14 18:45:49 -07:00
|
|
|
|
2025-06-11 14:50:15 +02:00
|
|
|
while(*pnread < plainlen) {
|
2024-05-16 14:59:25 +02:00
|
|
|
if(!backend->data_in_pending) {
|
2025-06-11 14:50:15 +02:00
|
|
|
if(tls_recv_more(cf, data, &result) < 0) {
|
|
|
|
|
if(result != CURLE_AGAIN) {
|
2023-03-31 11:07:18 +02:00
|
|
|
goto out;
|
|
|
|
|
}
|
2025-06-11 14:50:15 +02:00
|
|
|
result = CURLE_OK;
|
2023-03-31 11:07:18 +02:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-14 18:45:49 -07:00
|
|
|
rresult = rustls_connection_read(rconn,
|
2025-06-11 14:50:15 +02:00
|
|
|
(uint8_t *)plainbuf + *pnread,
|
|
|
|
|
plainlen - *pnread,
|
2024-09-12 12:38:51 -04:00
|
|
|
&n);
|
2021-11-04 10:37:32 +01:00
|
|
|
if(rresult == RUSTLS_RESULT_PLAINTEXT_EMPTY) {
|
2024-05-16 14:59:25 +02:00
|
|
|
backend->data_in_pending = FALSE;
|
2021-03-17 21:27:12 -07:00
|
|
|
}
|
2023-02-09 17:12:07 -08:00
|
|
|
else if(rresult == RUSTLS_RESULT_UNEXPECTED_EOF) {
|
|
|
|
|
failf(data, "rustls: peer closed TCP connection "
|
2024-09-12 12:38:51 -04:00
|
|
|
"without first closing TLS connection");
|
2025-06-11 14:50:15 +02:00
|
|
|
result = CURLE_RECV_ERROR;
|
2023-03-31 11:07:18 +02:00
|
|
|
goto out;
|
2023-02-09 17:12:07 -08:00
|
|
|
}
|
2021-11-04 10:37:32 +01:00
|
|
|
else if(rresult != RUSTLS_RESULT_OK) {
|
2024-07-01 16:47:21 +02:00
|
|
|
/* n always equals 0 in this case, do not need to check it */
|
2025-03-21 14:43:07 -04:00
|
|
|
rustls_failf(data, rresult, "rustls_connection_read");
|
2025-06-11 14:50:15 +02:00
|
|
|
result = CURLE_RECV_ERROR;
|
2023-03-31 11:07:18 +02:00
|
|
|
goto out;
|
2020-12-12 23:55:09 -08:00
|
|
|
}
|
2021-11-04 10:37:32 +01:00
|
|
|
else if(n == 0) {
|
2021-11-12 19:33:19 -08:00
|
|
|
/* n == 0 indicates clean EOF, but we may have read some other
|
|
|
|
|
plaintext bytes before we reached this. Break out of the loop
|
|
|
|
|
so we can figure out whether to return success or EOF. */
|
2023-03-31 11:07:18 +02:00
|
|
|
eof = TRUE;
|
2021-11-12 19:33:19 -08:00
|
|
|
break;
|
2020-12-12 23:55:09 -08:00
|
|
|
}
|
|
|
|
|
else {
|
2025-06-11 14:50:15 +02:00
|
|
|
*pnread += n;
|
2020-12-12 23:55:09 -08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-11 14:50:15 +02:00
|
|
|
if(!eof && !*pnread) {
|
|
|
|
|
result = CURLE_AGAIN;
|
2020-12-12 23:55:09 -08:00
|
|
|
}
|
|
|
|
|
|
2023-03-31 11:07:18 +02:00
|
|
|
out:
|
2025-06-11 14:50:15 +02:00
|
|
|
CURL_TRC_CF(data, cf, "rustls_recv(len=%zu) -> %d, %zu",
|
2026-04-16 10:52:57 +02:00
|
|
|
plainlen, (int)result, *pnread);
|
2025-06-11 14:50:15 +02:00
|
|
|
return result;
|
2020-12-12 23:55:09 -08:00
|
|
|
}
|
|
|
|
|
|
2024-05-16 14:59:25 +02:00
|
|
|
static CURLcode cr_flush_out(struct Curl_cfilter *cf, struct Curl_easy *data,
|
|
|
|
|
struct rustls_connection *rconn)
|
|
|
|
|
{
|
|
|
|
|
struct io_ctx io_ctx;
|
|
|
|
|
rustls_io_result io_error;
|
|
|
|
|
size_t tlswritten = 0;
|
|
|
|
|
size_t tlswritten_total = 0;
|
|
|
|
|
|
|
|
|
|
io_ctx.cf = cf;
|
|
|
|
|
io_ctx.data = data;
|
|
|
|
|
|
|
|
|
|
while(rustls_connection_wants_write(rconn)) {
|
|
|
|
|
io_error = rustls_connection_write_tls(rconn, write_cb, &io_ctx,
|
|
|
|
|
&tlswritten);
|
socket: introduce `SOCK_EAGAIN()` and use it
To contain the logic of checking for both `EWOULDBLOCK` and/or `EAGAIN`
depending on platform/availability. Also to avoid checking for both if
they mapp to the same value, and to avoid PP guards around use.
This also ensures `EAGAIN` is consistently not checked on Windows, where
headers defined it, but `SOCKERRNO` never returns it, because curl maps
it to `WSAGetLastError()`.
If they map to the same value, checking them both in an `if` expression
trips GCC warning `-Wlogical-op` (the same way it triggers duplicate
case value error in `switch`).
Also:
- replace two `switch()` statements with the new macro.
- tests/server/sws: make two outliers use the new macro that were only
checking for `EWOULDBLOCK` before this patch, in `connect_to()`.
- move variables to the left-side of expressions, where missing.
- rustls: use a variant of this macro that uses raw `EWOULDBLOCK`.
Tried tracing it back to the origins, but I couldn't figure out if
this is working as expected on all supported Windows versions in
Rust. It seems to be using `GetLastError()`, according to
https://docs.rs/system_error/0.2.0/system_error/, which would be
probably incorrect.
Notes:
- it's probably a good idea to assign `SOCKERRNO` to a variable before
passing it to this macro.
Cherry-picked from #21893
Closes #21992
2026-06-08 01:21:44 +02:00
|
|
|
if(RAW_EAGAIN(io_error)) {
|
2024-05-16 14:59:25 +02:00
|
|
|
CURL_TRC_CF(data, cf, "cf_send: EAGAIN after %zu bytes",
|
|
|
|
|
tlswritten_total);
|
|
|
|
|
return CURLE_AGAIN;
|
|
|
|
|
}
|
|
|
|
|
else if(io_error) {
|
|
|
|
|
char buffer[STRERROR_LEN];
|
|
|
|
|
failf(data, "writing to socket: %s",
|
2025-10-04 03:10:37 +02:00
|
|
|
curlx_strerror(io_error, buffer, sizeof(buffer)));
|
2024-05-16 14:59:25 +02:00
|
|
|
return CURLE_SEND_ERROR;
|
|
|
|
|
}
|
|
|
|
|
if(tlswritten == 0) {
|
|
|
|
|
failf(data, "EOF in swrite");
|
|
|
|
|
return CURLE_SEND_ERROR;
|
|
|
|
|
}
|
|
|
|
|
CURL_TRC_CF(data, cf, "cf_send: wrote %zu TLS bytes", tlswritten);
|
|
|
|
|
tlswritten_total += tlswritten;
|
|
|
|
|
}
|
2025-03-21 14:43:07 -04:00
|
|
|
return CURLE_OK;
|
2024-05-16 14:59:25 +02:00
|
|
|
}
|
|
|
|
|
|
2020-12-12 23:55:09 -08:00
|
|
|
/*
|
|
|
|
|
* On each call:
|
2024-08-19 16:53:40 +02:00
|
|
|
* - Copy `plainlen` bytes into Rustls' plaintext input buffer (if > 0).
|
|
|
|
|
* - Fully drain Rustls' plaintext output buffer into the socket until
|
2020-12-12 23:55:09 -08:00
|
|
|
* we get either an error or EAGAIN/EWOULDBLOCK.
|
|
|
|
|
*
|
2024-07-01 16:47:21 +02:00
|
|
|
* it is okay to call this function with plainbuf == NULL and plainlen == 0.
|
2026-03-11 10:17:10 +01:00
|
|
|
* In that case, it does not read anything into Rustls' plaintext input buffer.
|
|
|
|
|
* It only drains Rustls' plaintext output buffer into the socket.
|
2020-12-12 23:55:09 -08:00
|
|
|
*/
|
2025-11-29 02:16:52 +01:00
|
|
|
static CURLcode cr_send(struct Curl_cfilter *cf, struct Curl_easy *data,
|
|
|
|
|
const void *plainbuf, size_t plainlen,
|
|
|
|
|
size_t *pnwritten)
|
2020-12-12 23:55:09 -08:00
|
|
|
{
|
2025-11-29 02:16:52 +01:00
|
|
|
const struct ssl_connect_data * const connssl = cf->ctx;
|
|
|
|
|
struct rustls_ssl_backend_data * const backend =
|
2023-05-09 10:10:40 +00:00
|
|
|
(struct rustls_ssl_backend_data *)connssl->backend;
|
2022-02-17 17:48:48 +01:00
|
|
|
struct rustls_connection *rconn = NULL;
|
2020-12-12 23:55:09 -08:00
|
|
|
size_t plainwritten = 0;
|
2024-05-16 14:59:25 +02:00
|
|
|
const unsigned char *buf = plainbuf;
|
2025-06-11 14:50:15 +02:00
|
|
|
CURLcode result = CURLE_OK;
|
2024-05-16 14:59:25 +02:00
|
|
|
size_t blen = plainlen;
|
2021-05-14 18:45:49 -07:00
|
|
|
|
2022-02-17 17:48:48 +01:00
|
|
|
DEBUGASSERT(backend);
|
2025-06-11 14:50:15 +02:00
|
|
|
*pnwritten = 0;
|
2022-02-17 17:48:48 +01:00
|
|
|
rconn = backend->conn;
|
2024-05-17 13:23:19 +02:00
|
|
|
DEBUGASSERT(rconn);
|
2022-02-17 17:48:48 +01:00
|
|
|
|
2024-05-16 14:59:25 +02:00
|
|
|
CURL_TRC_CF(data, cf, "cf_send(len=%zu)", plainlen);
|
2023-03-31 11:07:18 +02:00
|
|
|
|
2024-05-16 14:59:25 +02:00
|
|
|
/* If a previous send blocked, we already added its plain bytes
|
|
|
|
|
* to rustsls and must not do that again. Flush the TLS bytes and,
|
|
|
|
|
* if successful, deduct the previous plain bytes from the current
|
|
|
|
|
* send. */
|
|
|
|
|
if(backend->plain_out_buffered) {
|
2025-06-11 14:50:15 +02:00
|
|
|
result = cr_flush_out(cf, data, rconn);
|
2024-05-16 14:59:25 +02:00
|
|
|
CURL_TRC_CF(data, cf, "cf_send: flushing %zu previously added bytes -> %d",
|
2026-04-16 10:52:57 +02:00
|
|
|
backend->plain_out_buffered, (int)result);
|
2025-06-11 14:50:15 +02:00
|
|
|
if(result)
|
|
|
|
|
return result;
|
2024-05-16 14:59:25 +02:00
|
|
|
if(blen > backend->plain_out_buffered) {
|
|
|
|
|
blen -= backend->plain_out_buffered;
|
|
|
|
|
buf += backend->plain_out_buffered;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
blen = 0;
|
2026-05-15 11:54:14 +02:00
|
|
|
*pnwritten += backend->plain_out_buffered;
|
2024-05-16 14:59:25 +02:00
|
|
|
backend->plain_out_buffered = 0;
|
|
|
|
|
}
|
2020-12-12 23:55:09 -08:00
|
|
|
|
2024-05-16 14:59:25 +02:00
|
|
|
if(blen > 0) {
|
2025-03-21 14:43:07 -04:00
|
|
|
rustls_result rresult;
|
2024-08-19 16:53:40 +02:00
|
|
|
CURL_TRC_CF(data, cf, "cf_send: adding %zu plain bytes to Rustls", blen);
|
2024-05-16 14:59:25 +02:00
|
|
|
rresult = rustls_connection_write(rconn, buf, blen, &plainwritten);
|
2020-12-12 23:55:09 -08:00
|
|
|
if(rresult != RUSTLS_RESULT_OK) {
|
2025-03-21 14:43:07 -04:00
|
|
|
rustls_failf(data, rresult, "rustls_connection_write");
|
2025-06-11 14:50:15 +02:00
|
|
|
result = CURLE_WRITE_ERROR;
|
|
|
|
|
goto out;
|
2020-12-12 23:55:09 -08:00
|
|
|
}
|
|
|
|
|
else if(plainwritten == 0) {
|
2023-02-09 17:12:07 -08:00
|
|
|
failf(data, "rustls_connection_write: EOF");
|
2025-06-11 14:50:15 +02:00
|
|
|
result = CURLE_WRITE_ERROR;
|
|
|
|
|
goto out;
|
2020-12-12 23:55:09 -08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-11 14:50:15 +02:00
|
|
|
result = cr_flush_out(cf, data, rconn);
|
|
|
|
|
if(result) {
|
2026-02-07 15:59:59 +01:00
|
|
|
if(result == CURLE_AGAIN) {
|
2024-05-16 14:59:25 +02:00
|
|
|
/* The TLS bytes may have been partially written, but we fail the
|
2024-08-19 16:53:40 +02:00
|
|
|
* complete send() and remember how much we already added to Rustls. */
|
2024-05-16 14:59:25 +02:00
|
|
|
backend->plain_out_buffered = plainwritten;
|
2025-06-11 14:50:15 +02:00
|
|
|
if(*pnwritten) {
|
|
|
|
|
result = CURLE_OK;
|
2024-05-16 14:59:25 +02:00
|
|
|
}
|
2020-12-12 23:55:09 -08:00
|
|
|
}
|
2025-06-11 14:50:15 +02:00
|
|
|
goto out;
|
2020-12-12 23:55:09 -08:00
|
|
|
}
|
2024-05-16 14:59:25 +02:00
|
|
|
else
|
2026-05-15 11:54:14 +02:00
|
|
|
*pnwritten += plainwritten;
|
2020-12-12 23:55:09 -08:00
|
|
|
|
2025-06-11 14:50:15 +02:00
|
|
|
out:
|
2026-04-16 03:16:20 +02:00
|
|
|
CURL_TRC_CF(data, cf, "rustls_send(len=%zu) -> %d, %zu",
|
2026-04-16 10:52:57 +02:00
|
|
|
plainlen, (int)result, *pnwritten);
|
2025-06-11 14:50:15 +02:00
|
|
|
return result;
|
2020-12-12 23:55:09 -08:00
|
|
|
}
|
|
|
|
|
|
2024-08-19 16:53:40 +02:00
|
|
|
/* A server certificate verify callback for Rustls that always returns
|
2021-02-26 17:29:36 -08:00
|
|
|
RUSTLS_RESULT_OK, or in other words disable certificate verification. */
|
2025-11-29 02:16:52 +01:00
|
|
|
static uint32_t cr_verify_none(void *userdata,
|
|
|
|
|
const rustls_verify_server_cert_params *params)
|
2021-02-26 17:29:36 -08:00
|
|
|
{
|
2025-09-02 15:40:12 +02:00
|
|
|
(void)userdata;
|
|
|
|
|
(void)params;
|
2021-02-26 17:29:36 -08:00
|
|
|
return RUSTLS_RESULT_OK;
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-29 02:16:52 +01:00
|
|
|
static int read_file_into(const char *filename, struct dynbuf *out)
|
2021-02-26 17:29:36 -08:00
|
|
|
{
|
2025-09-14 15:34:18 +02:00
|
|
|
FILE *f = curlx_fopen(filename, FOPEN_READTEXT);
|
2024-07-30 14:23:50 +01:00
|
|
|
if(!f) {
|
|
|
|
|
return 0;
|
2021-02-26 17:29:36 -08:00
|
|
|
}
|
2024-07-30 14:23:50 +01:00
|
|
|
|
2025-09-21 21:55:30 +02:00
|
|
|
for(;;) {
|
2024-07-30 14:23:50 +01:00
|
|
|
uint8_t buf[256];
|
2025-03-21 14:43:07 -04:00
|
|
|
const size_t rr = fread(buf, 1, sizeof(buf), f);
|
2025-10-17 17:05:08 +02:00
|
|
|
if((!rr && !feof(f)) ||
|
|
|
|
|
curlx_dyn_addn(out, buf, rr)) {
|
2025-09-14 15:34:18 +02:00
|
|
|
curlx_fclose(f);
|
2024-07-30 14:23:50 +01:00
|
|
|
return 0;
|
|
|
|
|
}
|
2025-09-21 21:55:30 +02:00
|
|
|
if(rr < sizeof(buf))
|
|
|
|
|
break;
|
2021-02-26 17:29:36 -08:00
|
|
|
}
|
2024-07-30 14:23:50 +01:00
|
|
|
|
2025-09-14 15:34:18 +02:00
|
|
|
return curlx_fclose(f) == 0;
|
2021-02-26 17:29:36 -08:00
|
|
|
}
|
|
|
|
|
|
2024-08-13 22:01:07 +02:00
|
|
|
static void
|
|
|
|
|
cr_get_selected_ciphers(struct Curl_easy *data,
|
|
|
|
|
const char *ciphers12,
|
|
|
|
|
const char *ciphers13,
|
|
|
|
|
const struct rustls_supported_ciphersuite **selected,
|
|
|
|
|
size_t *selected_size)
|
|
|
|
|
{
|
2025-03-21 14:43:07 -04:00
|
|
|
const size_t supported_len = *selected_size;
|
|
|
|
|
const size_t default_len = rustls_default_crypto_provider_ciphersuites_len();
|
build: address some `-Weverything` warnings, update picky warnings
`-Weverything` is not enabled by curl, and not recommended by LLVM,
because it may enable experimental options, and will result in new
fallouts after toolchain upgrades. This patch aims to fix/silence as much
as possible as found with llvm/clang 21.1.0. It also permanently enables
warnings that were fixed in source and deemed manageable in the future.
`-Wformat` warnings are addressed separately via #18343.
Fix/silence warnings in the source:
- typecheck-gcc.h: fix `-Wreserved-identifier`.
- lib: silence `-Wcast-function-type-strict`.
For llvm 16+ or Apple clang 16+.
- asyn-ares: limit `HAPPY_EYEBALLS_DNS_TIMEOUT` to old c-ares versions.
- curl_trc: fix `-Wc++-hidden-decl`.
- doh: fix `-Wc++-keyword`.
- ftp: fix `-Wreserved-identifier`.
- ldap: fix `-Wreserved-identifier`.
- mqtt: comment unused macro to avoid warning.
- multi_ev: drop unused macros to avoid warnings.
- setopt: fix useless `break;` after `return;`.
- gtls, mbedtls, rustls: silence `-Wconditional-uninitialized`.
- socks_sspi, schannel, x509asn1: fix `-Wimplicit-int-enum-cast`.
- x509asn1: fix `-Wc++-keyword`.
- openssl: scope `OSSL_UI_METHOD_CAST` to avoid unused macro warning.
- libssh2, wolfssl: drop unused macros.
- curl_ngtcp2, curl_quiche, httpsrr, urlapi: drop/limit unused macros.
- tool_getparam: fix useless `break;` after `return;` or `break;`.
Not normally enabled because it doesn't work with unity.
https://github.com/llvm/llvm-project/issues/71046
- tool_operate: fix `-Wc++-keyword`.
- curlinfo: fix a `-Wunsafe-buffer-usage`.
- tests: silence `-Wformat-non-iso`.
- lib557: fix `-Wreserved-identifier`.
- lib1565: silence `-Wconditional-uninitialized`.
Enable the above clang warnings permanently in picky mode:
- `-Wc++-hidden-decl`
- `-Wc++-keyword` (except for Windows, where it collides with `wchar_t`)
- `-Wcast-function-type-strict`
- `-Wcast-function-type`
- `-Wconditional-uninitialized`
- `-Wformat-non-iso` (except for clang-cl)
- `-Wreserved-identifier`
- `-Wtentative-definition-compat`
Silence problematic `-Weverything` warnings globally (in picky mode):
- `-Wused-but-marked-unused` (88000+ hits) and
`-Wdisabled-macro-expansion` (2600+ hits).
Triggered by `typecheck-gcc.h` when building with clang 14+.
Maybe there exists a way to fix within that header?
Ref: https://discourse.llvm.org/t/removing-wused-but-marked-unused/55310
- `-Wunsafe-buffer-usage`. clang 16+. 7000+ hits.
May be useful in theory, but such high volume of hits makes it
impractical to review and possibly address. Meant for C++.
Ref: https://clang.llvm.org/docs/SafeBuffers.html
Ref: https://stackoverflow.com/questions/77017567/how-to-fix-code-to-avoid-warning-wunsafe-buffer-usage
Ref: https://discourse.llvm.org/t/rfc-c-buffer-hardening/65734
Ref: https://github.com/llvm/llvm-project/pull/111624
- `-Wimplicit-void-ptr-cast`. clang 21+. 1700+ hits.
C++ warning, deemed pure noise.
Ref: https://github.com/curl/curl/issues/18470#issuecomment-3253506266
- `-Wswitch-default` (180+ hits), `-Wswitch-enum` (190+ hits),
`-Wcovered-switch-default` (20+ hits).
Next to impossible to fix cleanly, esp. when the covered `case`
branches depend on compile-time options.
- `-Wdocumentation-unknown-command` (8+ hits).
Triggered in a few sources. Seems arbitrary and bogus.
- `-Wpadded` (550+ hits).
- `-Wc++-keyword` on Windows, where it collides with `wchar_t`.
(100+ hits)
Ref: https://github.com/llvm/llvm-project/issues/155988
- `-Wreserved-macro-identifier`. clang 13+. 5+ hits.
Sometimes it's necessary to set external macros that use
the reserved namespace. E.g. `_CRT_NONSTDC_NO_DEPRECATE`,
`__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__`, `__NO_NET_API`,
possibly `_REENTRANT`, and more.
It's not worth trying to silence them individually.
- `-Wnonportable-system-include-path` with `clang-cl`.
It'd be broken by doing what the warning suggests.
- `-Wformat-non-iso` for clang-cl.
CMake `PICKY_COMPILER=ON` (the default) or `./configure`
`--enable-warnings` (not the default) is required to enable these
silencing rules.
Also:
- autotools, cmake: fix Apple clang and mainline llvm version translations.
Ref: https://en.wikipedia.org/wiki/Xcode#Toolchain_versions
- autotools, cmake: enable `-Warray-compare` for clang 20+.
Follow-up to 4b7accda5ae3f2e663aa3f3853805241ef87c2fe #17196
- cmake: fix to enable `-Wmissing-variable-declarations` at an earlier
clang version.
- cmake: update internal logic to handle warning options with `+` in
them.
- cmake: fix internal logic to match the whole option when looking
into `CMAKE_C_FLAGS` for custom-disabled warnings.
Follow-up to b85cb8cb4e143d1615d4fcc1ce8f2f7b66453995 #18485
Closes #18477
2025-09-04 11:56:33 +02:00
|
|
|
const struct rustls_supported_ciphersuite *entry = NULL;
|
2024-08-13 22:01:07 +02:00
|
|
|
const char *ciphers = ciphers12;
|
|
|
|
|
size_t count = 0, default13_count = 0, i, j;
|
|
|
|
|
const char *ptr, *end;
|
|
|
|
|
|
|
|
|
|
DEBUGASSERT(default_len <= supported_len);
|
|
|
|
|
|
|
|
|
|
if(!ciphers13) {
|
|
|
|
|
/* Add default TLSv1.3 ciphers to selection */
|
|
|
|
|
for(j = 0; j < default_len; j++) {
|
2024-09-12 12:18:26 -04:00
|
|
|
entry = rustls_default_crypto_provider_ciphersuites_get(j);
|
2024-09-12 12:31:59 -04:00
|
|
|
if(rustls_supported_ciphersuite_protocol_version(entry) !=
|
2024-09-12 12:38:51 -04:00
|
|
|
RUSTLS_TLS_VERSION_TLSV1_3)
|
2024-08-13 22:01:07 +02:00
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
selected[count++] = entry;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
default13_count = count;
|
|
|
|
|
|
|
|
|
|
if(!ciphers)
|
|
|
|
|
ciphers = "";
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
ciphers = ciphers13;
|
|
|
|
|
|
|
|
|
|
add_ciphers:
|
|
|
|
|
for(ptr = ciphers; ptr[0] != '\0' && count < supported_len; ptr = end) {
|
|
|
|
|
uint16_t id = Curl_cipher_suite_walk_str(&ptr, &end);
|
|
|
|
|
|
|
|
|
|
/* Check if cipher is supported */
|
|
|
|
|
if(id) {
|
|
|
|
|
for(i = 0; i < supported_len; i++) {
|
2024-09-12 12:18:26 -04:00
|
|
|
entry = rustls_default_crypto_provider_ciphersuites_get(i);
|
2024-08-13 22:01:07 +02:00
|
|
|
if(rustls_supported_ciphersuite_get_suite(entry) == id)
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if(i == supported_len)
|
|
|
|
|
id = 0;
|
|
|
|
|
}
|
|
|
|
|
if(!id) {
|
|
|
|
|
if(ptr[0] != '\0')
|
|
|
|
|
infof(data, "rustls: unknown cipher in list: \"%.*s\"",
|
2025-11-29 02:16:52 +01:00
|
|
|
(int)(end - ptr), ptr);
|
2024-08-13 22:01:07 +02:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* No duplicates allowed (so selected cannot overflow) */
|
2025-11-29 02:16:52 +01:00
|
|
|
for(i = 0; i < count && selected[i] != entry; i++)
|
|
|
|
|
;
|
2024-08-13 22:01:07 +02:00
|
|
|
if(i < count) {
|
|
|
|
|
if(i >= default13_count)
|
|
|
|
|
infof(data, "rustls: duplicate cipher in list: \"%.*s\"",
|
2025-11-29 02:16:52 +01:00
|
|
|
(int)(end - ptr), ptr);
|
2024-08-13 22:01:07 +02:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
selected[count++] = entry;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(ciphers == ciphers13 && ciphers12) {
|
|
|
|
|
ciphers = ciphers12;
|
|
|
|
|
goto add_ciphers;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(!ciphers12) {
|
|
|
|
|
/* Add default TLSv1.2 ciphers to selection */
|
|
|
|
|
for(j = 0; j < default_len; j++) {
|
2024-09-12 12:18:26 -04:00
|
|
|
entry = rustls_default_crypto_provider_ciphersuites_get(j);
|
2024-09-12 12:31:59 -04:00
|
|
|
if(rustls_supported_ciphersuite_protocol_version(entry) ==
|
2025-10-25 23:32:14 +02:00
|
|
|
RUSTLS_TLS_VERSION_TLSV1_3)
|
2024-08-13 22:01:07 +02:00
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
/* No duplicates allowed (so selected cannot overflow) */
|
2025-11-29 02:16:52 +01:00
|
|
|
for(i = 0; i < count && selected[i] != entry; i++)
|
|
|
|
|
;
|
2024-08-13 22:01:07 +02:00
|
|
|
if(i < count)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
selected[count++] = entry;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*selected_size = count;
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-29 02:16:52 +01:00
|
|
|
static void cr_keylog_log_cb(struct rustls_str label,
|
|
|
|
|
const uint8_t *client_random,
|
|
|
|
|
size_t client_random_len, const uint8_t *secret,
|
|
|
|
|
size_t secret_len)
|
2024-10-21 23:28:28 +03:00
|
|
|
{
|
|
|
|
|
char clabel[KEYLOG_LABEL_MAXLEN];
|
|
|
|
|
DEBUGASSERT(client_random_len == CLIENT_RANDOM_SIZE);
|
|
|
|
|
/* Turning a "rustls_str" into a null delimited "c" string */
|
2025-10-17 11:24:36 +02:00
|
|
|
curl_msnprintf(clabel, sizeof(clabel), "%.*s", (int)label.len, label.data);
|
2026-08-12 14:04:48 +02:00
|
|
|
Curl_tls_keylog_write(clabel, client_random, client_random_len,
|
|
|
|
|
secret, secret_len);
|
2024-10-21 23:28:28 +03:00
|
|
|
}
|
|
|
|
|
|
2021-02-26 17:29:36 -08:00
|
|
|
static CURLcode
|
2025-03-21 14:43:07 -04:00
|
|
|
init_config_builder(struct Curl_easy *data,
|
|
|
|
|
const struct ssl_primary_config *conn_config,
|
|
|
|
|
struct rustls_client_config_builder **config_builder)
|
2021-02-26 17:29:36 -08:00
|
|
|
{
|
2025-03-21 14:43:07 -04:00
|
|
|
const struct rustls_supported_ciphersuite **cipher_suites = NULL;
|
2024-09-12 12:18:26 -04:00
|
|
|
struct rustls_crypto_provider_builder *custom_provider_builder = NULL;
|
|
|
|
|
const struct rustls_crypto_provider *custom_provider = NULL;
|
2021-02-26 17:29:36 -08:00
|
|
|
|
2025-03-21 14:43:07 -04:00
|
|
|
uint16_t tls_versions[2] = {
|
2025-11-29 02:16:52 +01:00
|
|
|
RUSTLS_TLS_VERSION_TLSV1_2,
|
|
|
|
|
RUSTLS_TLS_VERSION_TLSV1_3,
|
2025-03-21 14:43:07 -04:00
|
|
|
};
|
|
|
|
|
size_t tls_versions_len = 2;
|
2025-11-29 02:16:52 +01:00
|
|
|
size_t cipher_suites_len = rustls_default_crypto_provider_ciphersuites_len();
|
2022-02-17 17:48:48 +01:00
|
|
|
|
2025-03-21 14:43:07 -04:00
|
|
|
CURLcode result = CURLE_OK;
|
|
|
|
|
rustls_result rr;
|
|
|
|
|
|
2026-01-27 16:59:32 +01:00
|
|
|
DEBUGASSERT(conn_config->version != CURL_SSLVERSION_DEFAULT);
|
2025-03-21 14:43:07 -04:00
|
|
|
switch(conn_config->version) {
|
|
|
|
|
case CURL_SSLVERSION_TLSv1:
|
|
|
|
|
case CURL_SSLVERSION_TLSv1_0:
|
|
|
|
|
case CURL_SSLVERSION_TLSv1_1:
|
|
|
|
|
case CURL_SSLVERSION_TLSv1_2:
|
|
|
|
|
break;
|
|
|
|
|
case CURL_SSLVERSION_TLSv1_3:
|
|
|
|
|
tls_versions[0] = RUSTLS_TLS_VERSION_TLSV1_3;
|
|
|
|
|
tls_versions_len = 1;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
failf(data, "rustls: unsupported minimum TLS version value");
|
|
|
|
|
result = CURLE_BAD_FUNCTION_ARGUMENT;
|
|
|
|
|
goto cleanup;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch(conn_config->version_max) {
|
|
|
|
|
case CURL_SSLVERSION_MAX_DEFAULT:
|
|
|
|
|
case CURL_SSLVERSION_MAX_NONE:
|
|
|
|
|
case CURL_SSLVERSION_MAX_TLSv1_3:
|
|
|
|
|
break;
|
|
|
|
|
case CURL_SSLVERSION_MAX_TLSv1_2:
|
|
|
|
|
if(tls_versions[0] == RUSTLS_TLS_VERSION_TLSV1_2) {
|
2024-08-13 22:01:07 +02:00
|
|
|
tls_versions_len = 1;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2025-03-21 14:43:07 -04:00
|
|
|
FALLTHROUGH();
|
|
|
|
|
case CURL_SSLVERSION_MAX_TLSv1_1:
|
|
|
|
|
case CURL_SSLVERSION_MAX_TLSv1_0:
|
|
|
|
|
default:
|
|
|
|
|
failf(data, "rustls: unsupported maximum TLS version value");
|
|
|
|
|
result = CURLE_BAD_FUNCTION_ARGUMENT;
|
|
|
|
|
goto cleanup;
|
|
|
|
|
}
|
2024-08-13 22:01:07 +02:00
|
|
|
|
2025-07-25 14:31:16 +02:00
|
|
|
#ifdef USE_ECH
|
2026-03-11 14:16:31 +01:00
|
|
|
if(CURLECH_ENABLED(data)) {
|
2025-03-24 12:01:30 -04:00
|
|
|
tls_versions[0] = RUSTLS_TLS_VERSION_TLSV1_3;
|
|
|
|
|
tls_versions_len = 1;
|
|
|
|
|
infof(data, "rustls: ECH enabled, forcing TLSv1.3");
|
|
|
|
|
}
|
|
|
|
|
#endif /* USE_ECH */
|
|
|
|
|
|
2026-02-28 11:29:16 +01:00
|
|
|
cipher_suites = curlx_malloc(sizeof(*cipher_suites) * cipher_suites_len);
|
2025-03-21 14:43:07 -04:00
|
|
|
if(!cipher_suites) {
|
|
|
|
|
result = CURLE_OUT_OF_MEMORY;
|
|
|
|
|
goto cleanup;
|
|
|
|
|
}
|
2024-08-13 22:01:07 +02:00
|
|
|
|
2025-03-21 14:43:07 -04:00
|
|
|
cr_get_selected_ciphers(data,
|
|
|
|
|
conn_config->cipher_list,
|
|
|
|
|
conn_config->cipher_list13,
|
|
|
|
|
cipher_suites, &cipher_suites_len);
|
|
|
|
|
if(cipher_suites_len == 0) {
|
|
|
|
|
failf(data, "rustls: no supported cipher in list");
|
|
|
|
|
result = CURLE_SSL_CIPHER;
|
|
|
|
|
goto cleanup;
|
|
|
|
|
}
|
2024-08-13 22:01:07 +02:00
|
|
|
|
2025-03-21 14:43:07 -04:00
|
|
|
rr = rustls_crypto_provider_builder_new_from_default(
|
|
|
|
|
&custom_provider_builder);
|
|
|
|
|
if(rr != RUSTLS_RESULT_OK) {
|
|
|
|
|
rustls_failf(data, rr,
|
2025-11-29 02:16:52 +01:00
|
|
|
"failed to create crypto provider builder from default");
|
2025-03-21 14:43:07 -04:00
|
|
|
result = CURLE_SSL_CIPHER;
|
|
|
|
|
goto cleanup;
|
|
|
|
|
}
|
2024-09-12 12:18:26 -04:00
|
|
|
|
2025-03-21 14:43:07 -04:00
|
|
|
rr =
|
|
|
|
|
rustls_crypto_provider_builder_set_cipher_suites(
|
|
|
|
|
custom_provider_builder,
|
|
|
|
|
cipher_suites,
|
|
|
|
|
cipher_suites_len);
|
|
|
|
|
if(rr != RUSTLS_RESULT_OK) {
|
|
|
|
|
rustls_failf(data, rr,
|
2025-11-29 02:16:52 +01:00
|
|
|
"failed to set ciphersuites for crypto provider builder");
|
2025-03-21 14:43:07 -04:00
|
|
|
result = CURLE_SSL_CIPHER;
|
|
|
|
|
goto cleanup;
|
|
|
|
|
}
|
2024-09-12 12:18:26 -04:00
|
|
|
|
2025-11-29 02:16:52 +01:00
|
|
|
rr = rustls_crypto_provider_builder_build(custom_provider_builder,
|
|
|
|
|
&custom_provider);
|
2025-03-21 14:43:07 -04:00
|
|
|
if(rr != RUSTLS_RESULT_OK) {
|
|
|
|
|
rustls_failf(data, rr, "failed to build custom crypto provider");
|
|
|
|
|
result = CURLE_SSL_CIPHER;
|
|
|
|
|
goto cleanup;
|
|
|
|
|
}
|
2024-09-12 12:18:26 -04:00
|
|
|
|
2025-03-21 14:43:07 -04:00
|
|
|
rr = rustls_client_config_builder_new_custom(custom_provider,
|
2025-11-29 02:16:52 +01:00
|
|
|
tls_versions,
|
|
|
|
|
tls_versions_len,
|
|
|
|
|
config_builder);
|
2025-03-21 14:43:07 -04:00
|
|
|
if(rr != RUSTLS_RESULT_OK) {
|
|
|
|
|
rustls_failf(data, rr, "failed to create client config builder");
|
|
|
|
|
result = CURLE_SSL_CIPHER;
|
|
|
|
|
goto cleanup;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
|
if(cipher_suites) {
|
build: stop overriding standard memory allocation functions
Before this patch curl used the C preprocessor to override standard
memory allocation symbols: malloc, calloc, strdup, realloc, free.
The goal of these is to replace them with curl's debug wrappers in
`CURLDEBUG` builds, another was to replace them with the wrappers
calling user-defined allocators in libcurl. This solution needed a bunch
of workarounds to avoid breaking external headers: it relied on include
order to do the overriding last. For "unity" builds it needed to reset
overrides before external includes. Also in test apps, which are always
built as single source files. It also needed the `(symbol)` trick
to avoid overrides in some places. This would still not fix cases where
the standard symbols were macros. It was also fragile and difficult
to figure out which was the actual function behind an alloc or free call
in a specific piece of code. This in turn caused bugs where the wrong
allocator was accidentally called.
To avoid these problems, this patch replaces this solution with
`curlx_`-prefixed allocator macros, and mapping them _once_ to either
the libcurl wrappers, the debug wrappers or the standard ones, matching
the rest of the code in libtests.
This concludes the long journey to avoid redefining standard functions
in the curl codebase.
Note: I did not update `packages/OS400/*.c` sources. They did not
`#include` `curl_setup.h`, `curl_memory.h` or `memdebug.h`, meaning
the overrides were never applied to them. This may or may not have been
correct. For now I suppressed the direct use of standard allocators
via a local `.checksrc`. Probably they (except for `curlcl.c`) should be
updated to include `curl_setup.h` and use the `curlx_` macros.
This patch changes mappings in two places:
- `lib/curl_threads.c` in libtests: Before this patch it mapped to
libcurl allocators. After, it maps to standard allocators, like
the rest of libtests code.
- `units`: before this patch it mapped to standard allocators. After, it
maps to libcurl allocators.
Also:
- drop all position-dependent `curl_memory.h` and `memdebug.h` includes,
and delete the now unnecessary headers.
- rename `Curl_tcsdup` macro to `curlx_tcsdup` and define like the other
allocators.
- map `curlx_strdup()` to `_strdup()` on Windows (was: `strdup()`).
To fix warnings silenced via `_CRT_NONSTDC_NO_DEPRECATE`.
- multibyte: map `curlx_convert_*()` to `_strdup()` on Windows
(was: `strdup()`).
- src: do not reuse the `strdup` name for the local replacement.
- lib509: call `_strdup()` on Windows (was: `strdup()`).
- test1132: delete test obsoleted by this patch.
- CHECKSRC.md: update text for `SNPRINTF`.
- checksrc: ban standard allocator symbols.
Follow-up to b12da22db1f11da51082977dc21a7edee7858911 #18866
Follow-up to db98daab05aec251bcb6615d2d38dfebec291736 #18844
Follow-up to 4deea9396bc7dd25c6362fa746a57bf309c74ada #18814
Follow-up to 9678ff5b1bfea1c847aee4f9edf023e8f01c9293 #18776
Follow-up to 10bac43b873fe45869e15b36aac1c1e5bc89b6e0 #18774
Follow-up to 20142f5d06f7120ba94cbcc25c998e8d81aec85b #18634
Follow-up to bf7375ecc50e857760b0d0a668c436e208a400bd #18503
Follow-up to 9863599d69b79d290928a89bf9160f4e4e023d4e #18502
Follow-up to 3bb5e58c105d7be450b667858d1b8e7ae3ded555 #17827
Closes #19626
2025-10-08 02:33:19 +02:00
|
|
|
curlx_free(cipher_suites);
|
2024-08-13 22:01:07 +02:00
|
|
|
}
|
2025-03-21 14:43:07 -04:00
|
|
|
if(custom_provider_builder) {
|
|
|
|
|
rustls_crypto_provider_builder_free(custom_provider_builder);
|
|
|
|
|
}
|
|
|
|
|
if(custom_provider) {
|
|
|
|
|
rustls_crypto_provider_free(custom_provider);
|
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
}
|
2024-08-13 22:01:07 +02:00
|
|
|
|
2025-03-21 14:43:07 -04:00
|
|
|
static void
|
|
|
|
|
init_config_builder_alpn(struct Curl_easy *data,
|
|
|
|
|
const struct ssl_connect_data *connssl,
|
2025-11-05 15:28:56 +08:00
|
|
|
struct rustls_client_config_builder *config_builder)
|
|
|
|
|
{
|
2025-03-21 14:43:07 -04:00
|
|
|
struct alpn_proto_buf proto;
|
|
|
|
|
rustls_slice_bytes alpn[ALPN_ENTRIES_MAX];
|
|
|
|
|
size_t i;
|
2024-09-12 12:18:26 -04:00
|
|
|
|
2025-03-21 14:43:07 -04:00
|
|
|
for(i = 0; i < connssl->alpn->count; ++i) {
|
|
|
|
|
alpn[i].data = (const uint8_t *)connssl->alpn->entries[i];
|
|
|
|
|
alpn[i].len = strlen(connssl->alpn->entries[i]);
|
|
|
|
|
}
|
|
|
|
|
rustls_client_config_builder_set_alpn_protocols(config_builder, alpn,
|
|
|
|
|
connssl->alpn->count);
|
|
|
|
|
Curl_alpn_to_proto_str(&proto, connssl->alpn);
|
|
|
|
|
infof(data, VTLS_INFOF_ALPN_OFFER_1STR, proto.data);
|
|
|
|
|
}
|
connections: introduce http/3 happy eyeballs
New cfilter HTTP-CONNECT for h3/h2/http1.1 eyeballing.
- filter is installed when `--http3` in the tool is used (or
the equivalent CURLOPT_ done in the library)
- starts a QUIC/HTTP/3 connect right away. Should that not
succeed after 100ms (subject to change), a parallel attempt
is started for HTTP/2 and HTTP/1.1 via TCP
- both attempts are subject to IPv6/IPv4 eyeballing, same
as happens for other connections
- tie timeout to the ip-version HAPPY_EYEBALLS_TIMEOUT
- use a `soft` timeout at half the value. When the soft timeout
expires, the HTTPS-CONNECT filter checks if the QUIC filter
has received any data from the server. If not, it will start
the HTTP/2 attempt.
HTTP/3(ngtcp2) improvements.
- setting call_data in all cfilter calls similar to http/2 and vtls filters
for use in callback where no stream data is available.
- returning CURLE_PARTIAL_FILE for prematurely terminated transfers
- enabling pytest test_05 for h3
- shifting functionality to "connect" UDP sockets from ngtcp2
implementation into the udp socket cfilter. Because unconnected
UDP sockets are weird. For example they error when adding to a
pollset.
HTTP/3(quiche) improvements.
- fixed upload bug in quiche implementation, now passes 251 and pytest
- error codes on stream RESET
- improved debug logs
- handling of DRAIN during connect
- limiting pending event queue
HTTP/2 cfilter improvements.
- use LOG_CF macros for dynamic logging in debug build
- fix CURLcode on RST streams to be CURLE_PARTIAL_FILE
- enable pytest test_05 for h2
- fix upload pytests and improve parallel transfer performance.
GOAWAY handling for ngtcp2/quiche
- during connect, when the remote server refuses to accept new connections
and closes immediately (so the local conn goes into DRAIN phase), the
connection is torn down and a another attempt is made after a short grace
period.
This is the behaviour observed with nghttpx when we tell it to shut
down gracefully. Tested in pytest test_03_02.
TLS improvements
- ALPN selection for SSL/SSL-PROXY filters in one vtls set of functions, replaces
copy of logic in all tls backends.
- standardized the infof logging of offered ALPNs
- ALPN negotiated: have common function for all backends that sets alpn proprty
and connection related things based on the negotiated protocol (or lack thereof).
- new tests/tests-httpd/scorecard.py for testing h3/h2 protocol implementation.
Invoke:
python3 tests/tests-httpd/scorecard.py --help
for usage.
Improvements on gathering connect statistics and socket access.
- new CF_CTRL_CONN_REPORT_STATS cfilter control for having cfilters
report connection statistics. This is triggered when the connection
has completely connected.
- new void Curl_pgrsTimeWas(..) method to report a timer update with
a timestamp of when it happend. This allows for updating timers
"later", e.g. a connect statistic after full connectivity has been
reached.
- in case of HTTP eyeballing, the previous changes will update
statistics only from the filter chain that "won" the eyeballing.
- new cfilter query CF_QUERY_SOCKET for retrieving the socket used
by a filter chain.
Added methods Curl_conn_cf_get_socket() and Curl_conn_get_socket()
for convenient use of this query.
- Change VTLS backend to query their sub-filters for the socket when
checks during the handshake are made.
HTTP/3 documentation on how https eyeballing works.
TLS improvements
- ALPN selection for SSL/SSL-PROXY filters in one vtls set of functions, replaces
copy of logic in all tls backends.
- standardized the infof logging of offered ALPNs
- ALPN negotiated: have common function for all backends that sets alpn proprty
and connection related things based on the negotiated protocol (or lack thereof).
Scorecard with Caddy.
- configure can be run with `--with-test-caddy=path` to specify which caddy to use for testing
- tests/tests-httpd/scorecard.py now measures download speeds with caddy
pytest improvements
- adding Makfile to clean gen dir
- adding nghttpx rundir creation on start
- checking httpd version 2.4.55 for test_05 cases where it is needed. Skipping with message if too old.
- catch exception when checking for caddy existance on system.
Closes #10349
2023-02-01 17:13:12 +01:00
|
|
|
|
2025-11-29 02:16:52 +01:00
|
|
|
static CURLcode init_config_builder_verifier_crl(
|
2025-03-21 14:43:07 -04:00
|
|
|
struct Curl_easy *data,
|
|
|
|
|
const struct ssl_primary_config *conn_config,
|
|
|
|
|
struct rustls_web_pki_server_cert_verifier_builder *builder)
|
|
|
|
|
{
|
|
|
|
|
CURLcode result = CURLE_OK;
|
|
|
|
|
struct dynbuf crl_contents;
|
|
|
|
|
rustls_result rr;
|
|
|
|
|
|
curlx: move into to curlx/
Move curlx_ functions into its own subdir.
The idea is to use the curlx_ prefix proper on these functions, and use
these same function names both in tool, lib and test suite source code.
Stop the previous special #define setup for curlx_ names.
The printf defines are now done for the library alone. Tests no longer
use the printf defines. The tool code sets its own defines. The printf
functions are not curlx, they are publicly available.
The strcase defines are not curlx_ functions and should not be used by
tool or server code.
dynbuf, warnless, base64, strparse, timeval, timediff are now proper
curlx functions.
When libcurl is built statically, the functions from the library can be
used as-is. The key is then that the functions must work as-is, without
having to be recompiled for use in tool/tests. This avoids symbol
collisions - when libcurl is built statically, we use those functions
directly when building the tool/tests. When libcurl is shared, we
build/link them separately for the tool/tests.
Assisted-by: Jay Satiro
Closes #17253
2025-05-07 11:00:59 +02:00
|
|
|
curlx_dyn_init(&crl_contents, DYN_CRLFILE_SIZE);
|
2025-03-21 14:43:07 -04:00
|
|
|
if(!read_file_into(conn_config->CRLfile, &crl_contents)) {
|
|
|
|
|
failf(data, "rustls: failed to read revocation list file");
|
|
|
|
|
result = CURLE_SSL_CRL_BADFILE;
|
|
|
|
|
goto cleanup;
|
2022-12-30 17:37:11 +01:00
|
|
|
}
|
2025-03-21 14:43:07 -04:00
|
|
|
|
|
|
|
|
rr = rustls_web_pki_server_cert_verifier_builder_add_crl(
|
|
|
|
|
builder,
|
curlx: move into to curlx/
Move curlx_ functions into its own subdir.
The idea is to use the curlx_ prefix proper on these functions, and use
these same function names both in tool, lib and test suite source code.
Stop the previous special #define setup for curlx_ names.
The printf defines are now done for the library alone. Tests no longer
use the printf defines. The tool code sets its own defines. The printf
functions are not curlx, they are publicly available.
The strcase defines are not curlx_ functions and should not be used by
tool or server code.
dynbuf, warnless, base64, strparse, timeval, timediff are now proper
curlx functions.
When libcurl is built statically, the functions from the library can be
used as-is. The key is then that the functions must work as-is, without
having to be recompiled for use in tool/tests. This avoids symbol
collisions - when libcurl is built statically, we use those functions
directly when building the tool/tests. When libcurl is shared, we
build/link them separately for the tool/tests.
Assisted-by: Jay Satiro
Closes #17253
2025-05-07 11:00:59 +02:00
|
|
|
curlx_dyn_uptr(&crl_contents),
|
|
|
|
|
curlx_dyn_len(&crl_contents));
|
2025-03-21 14:43:07 -04:00
|
|
|
if(rr != RUSTLS_RESULT_OK) {
|
|
|
|
|
rustls_failf(data, rr, "failed to parse revocation list");
|
|
|
|
|
result = CURLE_SSL_CRL_BADFILE;
|
|
|
|
|
goto cleanup;
|
2021-02-26 17:29:36 -08:00
|
|
|
}
|
2025-03-21 14:43:07 -04:00
|
|
|
|
|
|
|
|
cleanup:
|
curlx: move into to curlx/
Move curlx_ functions into its own subdir.
The idea is to use the curlx_ prefix proper on these functions, and use
these same function names both in tool, lib and test suite source code.
Stop the previous special #define setup for curlx_ names.
The printf defines are now done for the library alone. Tests no longer
use the printf defines. The tool code sets its own defines. The printf
functions are not curlx, they are publicly available.
The strcase defines are not curlx_ functions and should not be used by
tool or server code.
dynbuf, warnless, base64, strparse, timeval, timediff are now proper
curlx functions.
When libcurl is built statically, the functions from the library can be
used as-is. The key is then that the functions must work as-is, without
having to be recompiled for use in tool/tests. This avoids symbol
collisions - when libcurl is built statically, we use those functions
directly when building the tool/tests. When libcurl is shared, we
build/link them separately for the tool/tests.
Assisted-by: Jay Satiro
Closes #17253
2025-05-07 11:00:59 +02:00
|
|
|
curlx_dyn_free(&crl_contents);
|
2025-03-21 14:43:07 -04:00
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static CURLcode
|
|
|
|
|
init_config_builder_verifier(struct Curl_easy *data,
|
|
|
|
|
struct rustls_client_config_builder *builder,
|
|
|
|
|
const struct ssl_primary_config *conn_config,
|
|
|
|
|
const struct curl_blob *ca_info_blob,
|
2025-11-05 15:28:56 +08:00
|
|
|
const char * const ssl_cafile)
|
|
|
|
|
{
|
2025-03-21 14:43:07 -04:00
|
|
|
const struct rustls_root_cert_store *roots = NULL;
|
|
|
|
|
struct rustls_root_cert_store_builder *roots_builder = NULL;
|
|
|
|
|
struct rustls_web_pki_server_cert_verifier_builder *verifier_builder = NULL;
|
|
|
|
|
struct rustls_server_cert_verifier *server_cert_verifier = NULL;
|
|
|
|
|
rustls_result rr = RUSTLS_RESULT_OK;
|
|
|
|
|
CURLcode result = CURLE_OK;
|
|
|
|
|
|
|
|
|
|
roots_builder = rustls_root_cert_store_builder_new();
|
|
|
|
|
if(ca_info_blob) {
|
|
|
|
|
rr = rustls_root_cert_store_builder_add_pem(roots_builder,
|
|
|
|
|
ca_info_blob->data,
|
|
|
|
|
ca_info_blob->len,
|
|
|
|
|
1);
|
|
|
|
|
if(rr != RUSTLS_RESULT_OK) {
|
|
|
|
|
rustls_failf(data, rr, "failed to parse trusted certificates from blob");
|
|
|
|
|
result = CURLE_SSL_CACERT_BADFILE;
|
|
|
|
|
goto cleanup;
|
2024-02-25 21:50:18 +01:00
|
|
|
}
|
2025-03-21 14:43:07 -04:00
|
|
|
}
|
|
|
|
|
else if(ssl_cafile) {
|
|
|
|
|
rr = rustls_root_cert_store_builder_load_roots_from_file(roots_builder,
|
|
|
|
|
ssl_cafile,
|
|
|
|
|
1);
|
|
|
|
|
if(rr != RUSTLS_RESULT_OK) {
|
|
|
|
|
rustls_failf(data, rr, "failed to load trusted certificates");
|
|
|
|
|
result = CURLE_SSL_CACERT_BADFILE;
|
|
|
|
|
goto cleanup;
|
2022-01-09 15:59:30 -06:00
|
|
|
}
|
2025-03-21 14:43:07 -04:00
|
|
|
}
|
2022-01-09 15:59:30 -06:00
|
|
|
|
2025-03-21 14:43:07 -04:00
|
|
|
rr = rustls_root_cert_store_builder_build(roots_builder, &roots);
|
|
|
|
|
if(rr != RUSTLS_RESULT_OK) {
|
|
|
|
|
rustls_failf(data, rr, "failed to build trusted root certificate store");
|
|
|
|
|
result = CURLE_SSL_CACERT_BADFILE;
|
2025-11-29 09:30:48 -05:00
|
|
|
goto cleanup;
|
2025-03-21 14:43:07 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
verifier_builder = rustls_web_pki_server_cert_verifier_builder_new(roots);
|
2025-11-28 23:59:23 +01:00
|
|
|
if(!verifier_builder) {
|
|
|
|
|
result = CURLE_OUT_OF_MEMORY;
|
|
|
|
|
goto cleanup;
|
|
|
|
|
}
|
2025-03-21 14:43:07 -04:00
|
|
|
|
|
|
|
|
if(conn_config->CRLfile) {
|
|
|
|
|
result = init_config_builder_verifier_crl(data,
|
2025-11-29 02:16:52 +01:00
|
|
|
conn_config,
|
|
|
|
|
verifier_builder);
|
2025-10-18 23:34:52 +02:00
|
|
|
if(result) {
|
2025-03-21 14:43:07 -04:00
|
|
|
goto cleanup;
|
2022-01-09 15:59:30 -06:00
|
|
|
}
|
2025-03-21 14:43:07 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
rr = rustls_web_pki_server_cert_verifier_builder_build(
|
|
|
|
|
verifier_builder, &server_cert_verifier);
|
|
|
|
|
if(rr != RUSTLS_RESULT_OK) {
|
|
|
|
|
rustls_failf(data, rr, "failed to build certificate verifier");
|
|
|
|
|
result = CURLE_SSL_CACERT_BADFILE;
|
|
|
|
|
goto cleanup;
|
|
|
|
|
}
|
2024-02-25 21:50:18 +01:00
|
|
|
|
2025-03-21 14:43:07 -04:00
|
|
|
rustls_client_config_builder_set_server_verifier(builder,
|
|
|
|
|
server_cert_verifier);
|
|
|
|
|
cleanup:
|
|
|
|
|
if(roots_builder) {
|
|
|
|
|
rustls_root_cert_store_builder_free(roots_builder);
|
|
|
|
|
}
|
|
|
|
|
if(roots) {
|
2024-07-30 14:23:50 +01:00
|
|
|
rustls_root_cert_store_free(roots);
|
2025-03-21 14:43:07 -04:00
|
|
|
}
|
|
|
|
|
if(verifier_builder) {
|
|
|
|
|
rustls_web_pki_server_cert_verifier_builder_free(verifier_builder);
|
|
|
|
|
}
|
|
|
|
|
if(server_cert_verifier) {
|
|
|
|
|
rustls_server_cert_verifier_free(server_cert_verifier);
|
|
|
|
|
}
|
2024-07-30 14:23:50 +01:00
|
|
|
|
2025-03-21 14:43:07 -04:00
|
|
|
return result;
|
|
|
|
|
}
|
2024-07-30 14:23:50 +01:00
|
|
|
|
2025-11-29 02:16:52 +01:00
|
|
|
static CURLcode init_config_builder_platform_verifier(
|
2025-03-24 12:43:22 -04:00
|
|
|
struct Curl_easy *data,
|
|
|
|
|
struct rustls_client_config_builder *builder)
|
|
|
|
|
{
|
|
|
|
|
struct rustls_server_cert_verifier *server_cert_verifier = NULL;
|
|
|
|
|
CURLcode result = CURLE_OK;
|
|
|
|
|
rustls_result rr;
|
|
|
|
|
|
|
|
|
|
rr = rustls_platform_server_cert_verifier(&server_cert_verifier);
|
|
|
|
|
if(rr != RUSTLS_RESULT_OK) {
|
|
|
|
|
rustls_failf(data, rr, "failed to create platform certificate verifier");
|
|
|
|
|
result = CURLE_SSL_CACERT_BADFILE;
|
|
|
|
|
goto cleanup;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
rustls_client_config_builder_set_server_verifier(builder,
|
|
|
|
|
server_cert_verifier);
|
|
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
|
if(server_cert_verifier) {
|
|
|
|
|
rustls_server_cert_verifier_free(server_cert_verifier);
|
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-21 23:28:28 +03:00
|
|
|
static CURLcode
|
|
|
|
|
init_config_builder_keylog(struct Curl_easy *data,
|
|
|
|
|
struct rustls_client_config_builder *builder)
|
|
|
|
|
{
|
|
|
|
|
rustls_result rr;
|
|
|
|
|
|
|
|
|
|
Curl_tls_keylog_open();
|
|
|
|
|
if(!Curl_tls_keylog_enabled()) {
|
|
|
|
|
return CURLE_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
rr = rustls_client_config_builder_set_key_log(builder,
|
|
|
|
|
cr_keylog_log_cb,
|
|
|
|
|
NULL);
|
|
|
|
|
if(rr != RUSTLS_RESULT_OK) {
|
|
|
|
|
rustls_failf(data, rr, "rustls_client_config_builder_set_key_log");
|
|
|
|
|
Curl_tls_keylog_close();
|
|
|
|
|
return map_error(rr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return CURLE_OK;
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-01 21:14:49 +02:00
|
|
|
static CURLcode
|
|
|
|
|
init_config_builder_client_auth(struct Curl_easy *data,
|
|
|
|
|
const struct ssl_primary_config *conn_config,
|
|
|
|
|
const struct ssl_config_data *ssl_config,
|
|
|
|
|
struct rustls_client_config_builder *builder)
|
|
|
|
|
{
|
|
|
|
|
struct dynbuf cert_contents;
|
|
|
|
|
struct dynbuf key_contents;
|
|
|
|
|
rustls_result rr;
|
|
|
|
|
const struct rustls_certified_key *certified_key = NULL;
|
|
|
|
|
CURLcode result = CURLE_OK;
|
|
|
|
|
|
2026-05-19 11:47:50 +02:00
|
|
|
if(conn_config->clientcert && !ssl_config->primary.key) {
|
2025-01-01 21:14:49 +02:00
|
|
|
failf(data, "rustls: must provide key with certificate '%s'",
|
|
|
|
|
conn_config->clientcert);
|
|
|
|
|
return CURLE_SSL_CERTPROBLEM;
|
|
|
|
|
}
|
2026-05-19 11:47:50 +02:00
|
|
|
else if(!conn_config->clientcert && ssl_config->primary.key) {
|
2025-01-01 21:14:49 +02:00
|
|
|
failf(data, "rustls: must provide certificate with key '%s'",
|
2026-05-19 11:47:50 +02:00
|
|
|
ssl_config->primary.key);
|
2025-01-01 21:14:49 +02:00
|
|
|
return CURLE_SSL_CERTPROBLEM;
|
|
|
|
|
}
|
|
|
|
|
|
curlx: move into to curlx/
Move curlx_ functions into its own subdir.
The idea is to use the curlx_ prefix proper on these functions, and use
these same function names both in tool, lib and test suite source code.
Stop the previous special #define setup for curlx_ names.
The printf defines are now done for the library alone. Tests no longer
use the printf defines. The tool code sets its own defines. The printf
functions are not curlx, they are publicly available.
The strcase defines are not curlx_ functions and should not be used by
tool or server code.
dynbuf, warnless, base64, strparse, timeval, timediff are now proper
curlx functions.
When libcurl is built statically, the functions from the library can be
used as-is. The key is then that the functions must work as-is, without
having to be recompiled for use in tool/tests. This avoids symbol
collisions - when libcurl is built statically, we use those functions
directly when building the tool/tests. When libcurl is shared, we
build/link them separately for the tool/tests.
Assisted-by: Jay Satiro
Closes #17253
2025-05-07 11:00:59 +02:00
|
|
|
curlx_dyn_init(&cert_contents, DYN_CERTFILE_SIZE);
|
|
|
|
|
curlx_dyn_init(&key_contents, DYN_KEYFILE_SIZE);
|
2025-01-01 21:14:49 +02:00
|
|
|
|
|
|
|
|
if(!read_file_into(conn_config->clientcert, &cert_contents)) {
|
|
|
|
|
failf(data, "rustls: failed to read client certificate file: '%s'",
|
|
|
|
|
conn_config->clientcert);
|
|
|
|
|
result = CURLE_SSL_CERTPROBLEM;
|
|
|
|
|
goto cleanup;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-19 11:47:50 +02:00
|
|
|
if(!read_file_into(ssl_config->primary.key, &key_contents)) {
|
|
|
|
|
failf(data, "rustls: failed to read key file: '%s'",
|
|
|
|
|
ssl_config->primary.key);
|
2025-01-01 21:14:49 +02:00
|
|
|
result = CURLE_SSL_CERTPROBLEM;
|
|
|
|
|
goto cleanup;
|
|
|
|
|
}
|
|
|
|
|
|
curlx: move into to curlx/
Move curlx_ functions into its own subdir.
The idea is to use the curlx_ prefix proper on these functions, and use
these same function names both in tool, lib and test suite source code.
Stop the previous special #define setup for curlx_ names.
The printf defines are now done for the library alone. Tests no longer
use the printf defines. The tool code sets its own defines. The printf
functions are not curlx, they are publicly available.
The strcase defines are not curlx_ functions and should not be used by
tool or server code.
dynbuf, warnless, base64, strparse, timeval, timediff are now proper
curlx functions.
When libcurl is built statically, the functions from the library can be
used as-is. The key is then that the functions must work as-is, without
having to be recompiled for use in tool/tests. This avoids symbol
collisions - when libcurl is built statically, we use those functions
directly when building the tool/tests. When libcurl is shared, we
build/link them separately for the tool/tests.
Assisted-by: Jay Satiro
Closes #17253
2025-05-07 11:00:59 +02:00
|
|
|
rr = rustls_certified_key_build(curlx_dyn_uptr(&cert_contents),
|
|
|
|
|
curlx_dyn_len(&cert_contents),
|
|
|
|
|
curlx_dyn_uptr(&key_contents),
|
|
|
|
|
curlx_dyn_len(&key_contents),
|
2025-01-01 21:14:49 +02:00
|
|
|
&certified_key);
|
|
|
|
|
if(rr != RUSTLS_RESULT_OK) {
|
|
|
|
|
rustls_failf(data, rr, "rustls: failed to build certified key");
|
|
|
|
|
result = CURLE_SSL_CERTPROBLEM;
|
|
|
|
|
goto cleanup;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
rr = rustls_certified_key_keys_match(certified_key);
|
|
|
|
|
if(rr != RUSTLS_RESULT_OK) {
|
2025-11-29 02:16:52 +01:00
|
|
|
rustls_failf(data, rr,
|
2025-01-01 21:14:49 +02:00
|
|
|
"rustls: client certificate and keypair files do not match:");
|
|
|
|
|
result = CURLE_SSL_CERTPROBLEM;
|
|
|
|
|
goto cleanup;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
rr = rustls_client_config_builder_set_certified_key(builder,
|
|
|
|
|
&certified_key,
|
|
|
|
|
1);
|
|
|
|
|
if(rr != RUSTLS_RESULT_OK) {
|
|
|
|
|
rustls_failf(data, rr, "rustls: failed to set certified key");
|
|
|
|
|
result = CURLE_SSL_CERTPROBLEM;
|
|
|
|
|
goto cleanup;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cleanup:
|
curlx: move into to curlx/
Move curlx_ functions into its own subdir.
The idea is to use the curlx_ prefix proper on these functions, and use
these same function names both in tool, lib and test suite source code.
Stop the previous special #define setup for curlx_ names.
The printf defines are now done for the library alone. Tests no longer
use the printf defines. The tool code sets its own defines. The printf
functions are not curlx, they are publicly available.
The strcase defines are not curlx_ functions and should not be used by
tool or server code.
dynbuf, warnless, base64, strparse, timeval, timediff are now proper
curlx functions.
When libcurl is built statically, the functions from the library can be
used as-is. The key is then that the functions must work as-is, without
having to be recompiled for use in tool/tests. This avoids symbol
collisions - when libcurl is built statically, we use those functions
directly when building the tool/tests. When libcurl is shared, we
build/link them separately for the tool/tests.
Assisted-by: Jay Satiro
Closes #17253
2025-05-07 11:00:59 +02:00
|
|
|
curlx_dyn_free(&cert_contents);
|
|
|
|
|
curlx_dyn_free(&key_contents);
|
2025-01-01 21:14:49 +02:00
|
|
|
if(certified_key) {
|
|
|
|
|
rustls_certified_key_free(certified_key);
|
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-25 14:31:16 +02:00
|
|
|
#ifdef USE_ECH
|
2026-03-31 11:45:21 +02:00
|
|
|
|
|
|
|
|
static bool cr_ech_need_httpsrr(struct Curl_easy *data)
|
|
|
|
|
{
|
|
|
|
|
if(!CURLECH_ENABLED(data))
|
|
|
|
|
return FALSE;
|
2026-05-07 23:07:54 +02:00
|
|
|
if((data->set.tls_ech == CURLECH_GREASE) ||
|
lib: new easy option string storage
Change the storage of easy handle option strings from an array sized for
all possible options to a hash set to reduce memory footprint.
Give the hash set initially room for 4 strings, with first allocation
happening when it goes beyond that. Measurements without test suite
and a forced fail on growing the set gives:
Size Result
2 1261 tests out of 1951 reported OK: 64%
4 1792 tests out of 1951 reported OK: 91%
8 1944 tests out of 1951 reported OK: 99%
16 1949 tests out of 1951 reported OK: 99%
32 single fail of 3211, unit test for u8_strset
Add u8_strset that keeps the tuples (uint8_t id, char *str)
and allows set/unset by `id`. Add that as data->set.strings.
Define MACROS
* CURL_EASY_STR(data, id) for access
* CURL_EASY_STR_SET(data, id, s) for setting, making a copy
* CURL_EASY_STR_SETN(data, id, s) for setting, no copy
* CURL_EASY_STR_CLEAR(data, id) for unsetting
* CURL_EASY_STR_CLEAR0(data, id) for unsetting and zero-ing value
Add `data->set.str_copypostfields` to handle former `STRING_COPYPOSTFIELDS`
string that was not always a string and could carry NUL bytes.
Add unit tests to test3211.
Closes #22628
2026-08-20 16:06:17 +02:00
|
|
|
CURL_EASY_STR(data, STRING_ECH_CONFIG))
|
2026-05-07 23:07:54 +02:00
|
|
|
return FALSE;
|
2026-03-31 11:45:21 +02:00
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-24 12:01:30 -04:00
|
|
|
static CURLcode
|
|
|
|
|
init_config_builder_ech(struct Curl_easy *data,
|
2026-03-31 11:45:21 +02:00
|
|
|
struct Curl_cfilter *cf,
|
2025-03-24 12:01:30 -04:00
|
|
|
struct rustls_client_config_builder *builder)
|
|
|
|
|
{
|
|
|
|
|
const rustls_hpke *hpke = rustls_supported_hpke();
|
2025-03-24 12:11:54 -04:00
|
|
|
unsigned char *ech_config = NULL;
|
|
|
|
|
size_t ech_config_len = 0;
|
2025-03-24 12:26:21 -04:00
|
|
|
struct Curl_dns_entry *dns = NULL;
|
|
|
|
|
CURLcode result = CURLE_OK;
|
|
|
|
|
rustls_result rr;
|
2025-03-24 12:01:30 -04:00
|
|
|
|
|
|
|
|
if(!hpke) {
|
|
|
|
|
failf(data,
|
|
|
|
|
"rustls: ECH unavailable, rustls-ffi built without "
|
|
|
|
|
"HPKE compatible crypto provider");
|
2025-03-24 12:26:21 -04:00
|
|
|
result = CURLE_SSL_CONNECT_ERROR;
|
|
|
|
|
goto cleanup;
|
2025-03-24 12:01:30 -04:00
|
|
|
}
|
|
|
|
|
|
lib: new easy option string storage
Change the storage of easy handle option strings from an array sized for
all possible options to a hash set to reduce memory footprint.
Give the hash set initially room for 4 strings, with first allocation
happening when it goes beyond that. Measurements without test suite
and a forced fail on growing the set gives:
Size Result
2 1261 tests out of 1951 reported OK: 64%
4 1792 tests out of 1951 reported OK: 91%
8 1944 tests out of 1951 reported OK: 99%
16 1949 tests out of 1951 reported OK: 99%
32 single fail of 3211, unit test for u8_strset
Add u8_strset that keeps the tuples (uint8_t id, char *str)
and allows set/unset by `id`. Add that as data->set.strings.
Define MACROS
* CURL_EASY_STR(data, id) for access
* CURL_EASY_STR_SET(data, id, s) for setting, making a copy
* CURL_EASY_STR_SETN(data, id, s) for setting, no copy
* CURL_EASY_STR_CLEAR(data, id) for unsetting
* CURL_EASY_STR_CLEAR0(data, id) for unsetting and zero-ing value
Add `data->set.str_copypostfields` to handle former `STRING_COPYPOSTFIELDS`
string that was not always a string and could carry NUL bytes.
Add unit tests to test3211.
Closes #22628
2026-08-20 16:06:17 +02:00
|
|
|
if(CURL_EASY_STR(data, STRING_ECH_PUBLIC)) {
|
2025-03-24 12:01:30 -04:00
|
|
|
failf(data, "rustls: ECH outername not supported");
|
2025-03-24 12:26:21 -04:00
|
|
|
result = CURLE_SSL_CONNECT_ERROR;
|
|
|
|
|
goto cleanup;
|
2025-03-24 12:01:30 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(data->set.tls_ech == CURLECH_GREASE) {
|
|
|
|
|
rr = rustls_client_config_builder_enable_ech_grease(builder, hpke);
|
|
|
|
|
if(rr != RUSTLS_RESULT_OK) {
|
|
|
|
|
rustls_failf(data, rr, "rustls: failed to configure ECH GREASE");
|
2025-03-24 12:26:21 -04:00
|
|
|
result = CURLE_SSL_CONNECT_ERROR;
|
|
|
|
|
goto cleanup;
|
2025-03-24 12:01:30 -04:00
|
|
|
}
|
2025-03-24 12:26:21 -04:00
|
|
|
return CURLE_OK;
|
2025-03-24 12:01:30 -04:00
|
|
|
}
|
2025-03-24 12:26:21 -04:00
|
|
|
|
lib: new easy option string storage
Change the storage of easy handle option strings from an array sized for
all possible options to a hash set to reduce memory footprint.
Give the hash set initially room for 4 strings, with first allocation
happening when it goes beyond that. Measurements without test suite
and a forced fail on growing the set gives:
Size Result
2 1261 tests out of 1951 reported OK: 64%
4 1792 tests out of 1951 reported OK: 91%
8 1944 tests out of 1951 reported OK: 99%
16 1949 tests out of 1951 reported OK: 99%
32 single fail of 3211, unit test for u8_strset
Add u8_strset that keeps the tuples (uint8_t id, char *str)
and allows set/unset by `id`. Add that as data->set.strings.
Define MACROS
* CURL_EASY_STR(data, id) for access
* CURL_EASY_STR_SET(data, id, s) for setting, making a copy
* CURL_EASY_STR_SETN(data, id, s) for setting, no copy
* CURL_EASY_STR_CLEAR(data, id) for unsetting
* CURL_EASY_STR_CLEAR0(data, id) for unsetting and zero-ing value
Add `data->set.str_copypostfields` to handle former `STRING_COPYPOSTFIELDS`
string that was not always a string and could carry NUL bytes.
Add unit tests to test3211.
Closes #22628
2026-08-20 16:06:17 +02:00
|
|
|
if(data->set.tls_ech && CURL_EASY_STR(data, STRING_ECH_CONFIG)) {
|
|
|
|
|
const char *b64 = CURL_EASY_STR(data, STRING_ECH_CONFIG);
|
2025-03-24 12:11:54 -04:00
|
|
|
size_t decode_result;
|
|
|
|
|
if(!b64) {
|
|
|
|
|
infof(data, "rustls: ECHConfig from command line empty");
|
2025-03-24 12:26:21 -04:00
|
|
|
result = CURLE_SSL_CONNECT_ERROR;
|
|
|
|
|
goto cleanup;
|
2025-03-24 12:11:54 -04:00
|
|
|
}
|
|
|
|
|
/* rustls-ffi expects the raw TLS encoded ECHConfigList bytes */
|
curlx: move into to curlx/
Move curlx_ functions into its own subdir.
The idea is to use the curlx_ prefix proper on these functions, and use
these same function names both in tool, lib and test suite source code.
Stop the previous special #define setup for curlx_ names.
The printf defines are now done for the library alone. Tests no longer
use the printf defines. The tool code sets its own defines. The printf
functions are not curlx, they are publicly available.
The strcase defines are not curlx_ functions and should not be used by
tool or server code.
dynbuf, warnless, base64, strparse, timeval, timediff are now proper
curlx functions.
When libcurl is built statically, the functions from the library can be
used as-is. The key is then that the functions must work as-is, without
having to be recompiled for use in tool/tests. This avoids symbol
collisions - when libcurl is built statically, we use those functions
directly when building the tool/tests. When libcurl is shared, we
build/link them separately for the tool/tests.
Assisted-by: Jay Satiro
Closes #17253
2025-05-07 11:00:59 +02:00
|
|
|
decode_result = curlx_base64_decode(b64, &ech_config, &ech_config_len);
|
2025-03-24 12:11:54 -04:00
|
|
|
if(decode_result || !ech_config) {
|
|
|
|
|
infof(data, "rustls: cannot base64 decode ECHConfig from command line");
|
2025-03-24 12:26:21 -04:00
|
|
|
result = CURLE_SSL_CONNECT_ERROR;
|
|
|
|
|
goto cleanup;
|
2025-03-24 12:11:54 -04:00
|
|
|
}
|
2025-03-24 12:26:21 -04:00
|
|
|
}
|
|
|
|
|
else {
|
2026-06-16 12:07:08 +02:00
|
|
|
const struct ssl_connect_data *connssl = cf->ctx;
|
2026-03-31 11:45:21 +02:00
|
|
|
const struct Curl_https_rrinfo *rinfo =
|
2026-06-17 11:50:35 +02:00
|
|
|
Curl_conn_dns_get_https(data, cf->sockindex, connssl->peer.origin);
|
2026-03-31 11:45:21 +02:00
|
|
|
|
2025-03-24 12:26:21 -04:00
|
|
|
if(!rinfo || !rinfo->echconfiglist) {
|
|
|
|
|
failf(data, "rustls: ECH requested but no ECHConfig available");
|
|
|
|
|
result = CURLE_SSL_CONNECT_ERROR;
|
|
|
|
|
goto cleanup;
|
|
|
|
|
}
|
|
|
|
|
ech_config = rinfo->echconfiglist;
|
|
|
|
|
ech_config_len = rinfo->echconfiglist_len;
|
2025-03-24 12:11:54 -04:00
|
|
|
}
|
2025-03-24 12:01:30 -04:00
|
|
|
|
2025-03-24 12:26:21 -04:00
|
|
|
rr = rustls_client_config_builder_enable_ech(builder,
|
|
|
|
|
ech_config,
|
|
|
|
|
ech_config_len,
|
|
|
|
|
hpke);
|
|
|
|
|
if(rr != RUSTLS_RESULT_OK) {
|
|
|
|
|
rustls_failf(data, rr, "rustls: failed to configure ECH");
|
|
|
|
|
result = CURLE_SSL_CONNECT_ERROR;
|
|
|
|
|
goto cleanup;
|
|
|
|
|
}
|
|
|
|
|
cleanup:
|
2025-04-25 18:26:04 +01:00
|
|
|
/* if we base64 decoded, we can free now */
|
lib: new easy option string storage
Change the storage of easy handle option strings from an array sized for
all possible options to a hash set to reduce memory footprint.
Give the hash set initially room for 4 strings, with first allocation
happening when it goes beyond that. Measurements without test suite
and a forced fail on growing the set gives:
Size Result
2 1261 tests out of 1951 reported OK: 64%
4 1792 tests out of 1951 reported OK: 91%
8 1944 tests out of 1951 reported OK: 99%
16 1949 tests out of 1951 reported OK: 99%
32 single fail of 3211, unit test for u8_strset
Add u8_strset that keeps the tuples (uint8_t id, char *str)
and allows set/unset by `id`. Add that as data->set.strings.
Define MACROS
* CURL_EASY_STR(data, id) for access
* CURL_EASY_STR_SET(data, id, s) for setting, making a copy
* CURL_EASY_STR_SETN(data, id, s) for setting, no copy
* CURL_EASY_STR_CLEAR(data, id) for unsetting
* CURL_EASY_STR_CLEAR0(data, id) for unsetting and zero-ing value
Add `data->set.str_copypostfields` to handle former `STRING_COPYPOSTFIELDS`
string that was not always a string and could carry NUL bytes.
Add unit tests to test3211.
Closes #22628
2026-08-20 16:06:17 +02:00
|
|
|
if(data->set.tls_ech && CURL_EASY_STR(data, STRING_ECH_CONFIG)) {
|
build: stop overriding standard memory allocation functions
Before this patch curl used the C preprocessor to override standard
memory allocation symbols: malloc, calloc, strdup, realloc, free.
The goal of these is to replace them with curl's debug wrappers in
`CURLDEBUG` builds, another was to replace them with the wrappers
calling user-defined allocators in libcurl. This solution needed a bunch
of workarounds to avoid breaking external headers: it relied on include
order to do the overriding last. For "unity" builds it needed to reset
overrides before external includes. Also in test apps, which are always
built as single source files. It also needed the `(symbol)` trick
to avoid overrides in some places. This would still not fix cases where
the standard symbols were macros. It was also fragile and difficult
to figure out which was the actual function behind an alloc or free call
in a specific piece of code. This in turn caused bugs where the wrong
allocator was accidentally called.
To avoid these problems, this patch replaces this solution with
`curlx_`-prefixed allocator macros, and mapping them _once_ to either
the libcurl wrappers, the debug wrappers or the standard ones, matching
the rest of the code in libtests.
This concludes the long journey to avoid redefining standard functions
in the curl codebase.
Note: I did not update `packages/OS400/*.c` sources. They did not
`#include` `curl_setup.h`, `curl_memory.h` or `memdebug.h`, meaning
the overrides were never applied to them. This may or may not have been
correct. For now I suppressed the direct use of standard allocators
via a local `.checksrc`. Probably they (except for `curlcl.c`) should be
updated to include `curl_setup.h` and use the `curlx_` macros.
This patch changes mappings in two places:
- `lib/curl_threads.c` in libtests: Before this patch it mapped to
libcurl allocators. After, it maps to standard allocators, like
the rest of libtests code.
- `units`: before this patch it mapped to standard allocators. After, it
maps to libcurl allocators.
Also:
- drop all position-dependent `curl_memory.h` and `memdebug.h` includes,
and delete the now unnecessary headers.
- rename `Curl_tcsdup` macro to `curlx_tcsdup` and define like the other
allocators.
- map `curlx_strdup()` to `_strdup()` on Windows (was: `strdup()`).
To fix warnings silenced via `_CRT_NONSTDC_NO_DEPRECATE`.
- multibyte: map `curlx_convert_*()` to `_strdup()` on Windows
(was: `strdup()`).
- src: do not reuse the `strdup` name for the local replacement.
- lib509: call `_strdup()` on Windows (was: `strdup()`).
- test1132: delete test obsoleted by this patch.
- CHECKSRC.md: update text for `SNPRINTF`.
- checksrc: ban standard allocator symbols.
Follow-up to b12da22db1f11da51082977dc21a7edee7858911 #18866
Follow-up to db98daab05aec251bcb6615d2d38dfebec291736 #18844
Follow-up to 4deea9396bc7dd25c6362fa746a57bf309c74ada #18814
Follow-up to 9678ff5b1bfea1c847aee4f9edf023e8f01c9293 #18776
Follow-up to 10bac43b873fe45869e15b36aac1c1e5bc89b6e0 #18774
Follow-up to 20142f5d06f7120ba94cbcc25c998e8d81aec85b #18634
Follow-up to bf7375ecc50e857760b0d0a668c436e208a400bd #18503
Follow-up to 9863599d69b79d290928a89bf9160f4e4e023d4e #18502
Follow-up to 3bb5e58c105d7be450b667858d1b8e7ae3ded555 #17827
Closes #19626
2025-10-08 02:33:19 +02:00
|
|
|
curlx_free(ech_config);
|
2025-04-25 18:26:04 +01:00
|
|
|
}
|
2025-03-24 12:26:21 -04:00
|
|
|
if(dns) {
|
2026-03-06 09:22:26 +01:00
|
|
|
Curl_dns_entry_unlink(data, &dns);
|
2025-03-24 12:26:21 -04:00
|
|
|
}
|
|
|
|
|
return result;
|
2025-03-24 12:01:30 -04:00
|
|
|
}
|
|
|
|
|
#endif /* USE_ECH */
|
|
|
|
|
|
2025-11-29 02:16:52 +01:00
|
|
|
static CURLcode cr_init_backend(struct Curl_cfilter *cf,
|
|
|
|
|
struct Curl_easy *data,
|
|
|
|
|
struct rustls_ssl_backend_data * const backend)
|
2025-03-21 14:43:07 -04:00
|
|
|
{
|
|
|
|
|
const struct ssl_connect_data *connssl = cf->ctx;
|
|
|
|
|
const struct ssl_primary_config *conn_config =
|
|
|
|
|
Curl_ssl_cf_get_primary_config(cf);
|
2025-01-01 21:14:49 +02:00
|
|
|
struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
|
2025-03-21 14:43:07 -04:00
|
|
|
struct rustls_connection *rconn = NULL;
|
|
|
|
|
struct rustls_client_config_builder *config_builder = NULL;
|
2024-02-25 21:50:18 +01:00
|
|
|
|
2025-03-21 14:43:07 -04:00
|
|
|
const struct curl_blob *ca_info_blob = conn_config->ca_info_blob;
|
|
|
|
|
const char * const ssl_cafile =
|
|
|
|
|
/* CURLOPT_CAINFO_BLOB overrides CURLOPT_CAINFO */
|
|
|
|
|
(ca_info_blob ? NULL : conn_config->CAfile);
|
|
|
|
|
CURLcode result = CURLE_OK;
|
|
|
|
|
rustls_result rr;
|
|
|
|
|
|
|
|
|
|
DEBUGASSERT(backend);
|
|
|
|
|
rconn = backend->conn;
|
|
|
|
|
|
|
|
|
|
result = init_config_builder(data, conn_config, &config_builder);
|
|
|
|
|
if(result != CURLE_OK) {
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(connssl->alpn) {
|
|
|
|
|
init_config_builder_alpn(data, connssl, config_builder);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(!conn_config->verifypeer) {
|
|
|
|
|
rustls_client_config_builder_dangerous_set_certificate_verifier(
|
|
|
|
|
config_builder, cr_verify_none);
|
|
|
|
|
}
|
2026-08-25 13:47:11 +02:00
|
|
|
else if(conn_config->native_ca_store) {
|
2026-05-14 23:19:54 +02:00
|
|
|
if(conn_config->CRLfile) {
|
|
|
|
|
failf(data, "rustls: CRL file not supported with native CA store; "
|
|
|
|
|
"the platform verifier has no CRL attachment API");
|
|
|
|
|
rustls_client_config_builder_free(config_builder);
|
|
|
|
|
return CURLE_NOT_BUILT_IN;
|
|
|
|
|
}
|
2025-03-24 12:43:22 -04:00
|
|
|
result = init_config_builder_platform_verifier(data, config_builder);
|
|
|
|
|
if(result != CURLE_OK) {
|
|
|
|
|
rustls_client_config_builder_free(config_builder);
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-03-21 14:43:07 -04:00
|
|
|
else if(ca_info_blob || ssl_cafile) {
|
|
|
|
|
result = init_config_builder_verifier(data,
|
|
|
|
|
config_builder,
|
|
|
|
|
conn_config,
|
|
|
|
|
ca_info_blob,
|
|
|
|
|
ssl_cafile);
|
|
|
|
|
if(result != CURLE_OK) {
|
2024-07-19 12:23:18 -04:00
|
|
|
rustls_client_config_builder_free(config_builder);
|
2025-03-21 14:43:07 -04:00
|
|
|
return result;
|
2021-02-26 17:29:36 -08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-19 11:47:50 +02:00
|
|
|
if(conn_config->clientcert || ssl_config->primary.key) {
|
2025-01-01 21:14:49 +02:00
|
|
|
result = init_config_builder_client_auth(data,
|
|
|
|
|
conn_config,
|
|
|
|
|
ssl_config,
|
|
|
|
|
config_builder);
|
|
|
|
|
if(result != CURLE_OK) {
|
|
|
|
|
rustls_client_config_builder_free(config_builder);
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-25 14:31:16 +02:00
|
|
|
#ifdef USE_ECH
|
2026-03-11 14:16:31 +01:00
|
|
|
if(CURLECH_ENABLED(data)) {
|
2026-03-31 11:45:21 +02:00
|
|
|
result = init_config_builder_ech(data, cf, config_builder);
|
2026-05-07 23:07:54 +02:00
|
|
|
if((result != CURLE_OK) && (data->set.tls_ech == CURLECH_HARD)) {
|
2025-03-24 12:01:30 -04:00
|
|
|
rustls_client_config_builder_free(config_builder);
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif /* USE_ECH */
|
|
|
|
|
|
2024-10-21 23:28:28 +03:00
|
|
|
result = init_config_builder_keylog(data, config_builder);
|
|
|
|
|
if(result != CURLE_OK) {
|
|
|
|
|
rustls_client_config_builder_free(config_builder);
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-29 02:16:52 +01:00
|
|
|
rr = rustls_client_config_builder_build(config_builder, &backend->config);
|
2025-03-21 14:43:07 -04:00
|
|
|
if(rr != RUSTLS_RESULT_OK) {
|
|
|
|
|
rustls_failf(data, rr, "failed to build client config");
|
2024-10-28 13:27:58 +01:00
|
|
|
return CURLE_SSL_CONNECT_ERROR;
|
2024-09-12 12:18:26 -04:00
|
|
|
}
|
|
|
|
|
|
2026-06-09 14:18:02 +02:00
|
|
|
DEBUGASSERT(!rconn);
|
2025-03-21 14:43:07 -04:00
|
|
|
rr = rustls_client_connection_new(backend->config,
|
2026-06-05 12:55:50 +02:00
|
|
|
connssl->peer.origin->hostname,
|
2025-03-21 14:43:07 -04:00
|
|
|
&rconn);
|
|
|
|
|
if(rr != RUSTLS_RESULT_OK) {
|
2025-10-09 09:55:38 +02:00
|
|
|
rustls_failf(data, rr, "rustls_client_connection_new");
|
2025-11-10 00:24:10 +08:00
|
|
|
rustls_client_config_free(backend->config);
|
|
|
|
|
backend->config = NULL;
|
2021-02-26 17:29:36 -08:00
|
|
|
return CURLE_COULDNT_CONNECT;
|
|
|
|
|
}
|
2024-05-17 13:23:19 +02:00
|
|
|
DEBUGASSERT(rconn);
|
2021-05-14 18:45:49 -07:00
|
|
|
rustls_connection_set_userdata(rconn, backend);
|
|
|
|
|
backend->conn = rconn;
|
2025-03-21 14:43:07 -04:00
|
|
|
|
|
|
|
|
return result;
|
2021-02-26 17:29:36 -08:00
|
|
|
}
|
|
|
|
|
|
2025-11-29 02:16:52 +01:00
|
|
|
static void cr_set_negotiated_alpn(struct Curl_cfilter *cf,
|
|
|
|
|
struct Curl_easy *data,
|
|
|
|
|
const struct rustls_connection *rconn)
|
2021-04-24 18:02:24 -07:00
|
|
|
{
|
2025-11-29 02:16:52 +01:00
|
|
|
struct ssl_connect_data * const connssl = cf->ctx;
|
2021-04-24 18:02:24 -07:00
|
|
|
const uint8_t *protocol = NULL;
|
|
|
|
|
size_t len = 0;
|
|
|
|
|
|
2021-05-14 18:45:49 -07:00
|
|
|
rustls_connection_get_alpn_protocol(rconn, &protocol, &len);
|
TLS: TLSv1.3 earlydata support for curl
Based on #14135, implement TLSv1.3 earlydata support for the curl
command line, libcurl and its implementation in GnuTLS.
If a known TLS session announces early data support, and the feature is
enabled *and* it is not a "connect-only" transfer, delay the TLS
handshake until the first request is being sent.
- Add --tls-earldata as new boolean command line option for curl.
- Add CURLSSLOPT_EARLYDATA to libcurl to enable use of the feature.
- Add CURLINFO_EARLYDATA_SENT_T to libcurl, reporting the amount of
bytes sent and accepted/rejected by the server.
Implementation details:
- store the ALPN protocol selected at the SSL session.
- When reusing the session and enabling earlydata, use exactly
that ALPN protocol for negoptiation with the server. When the
sessions ALPN does not match the connections ALPN, earlydata
will not be enabled.
- Check that the server selected the correct ALPN protocol for
an earlydata connect. If the server does not confirm or reports
something different, the connect fails.
- HTTP/2: delay sending the initial SETTINGS frames during connect,
if not connect-only.
Verification:
- add test_02_32 to verify earlydata GET with nghttpx.
- add test_07_70 to verify earlydata PUT with nghttpx.
- add support in 'hx-download', 'hx-upload' clients for the feature
Assisted-by: ad-chaos on github
Closes #15211
2024-10-09 14:46:32 +02:00
|
|
|
Curl_alpn_set_negotiated(cf, data, connssl, protocol, len);
|
2021-04-24 18:02:24 -07:00
|
|
|
}
|
|
|
|
|
|
2023-06-23 16:58:54 -07:00
|
|
|
/* Given an established network connection, do a TLS handshake.
|
|
|
|
|
*
|
2026-03-11 10:17:10 +01:00
|
|
|
* This function sets `*done` to true once the handshake is complete.
|
2025-02-19 16:49:31 +01:00
|
|
|
* This function never reads the value of `*done*`.
|
2023-06-23 16:58:54 -07:00
|
|
|
*/
|
2025-11-29 02:16:52 +01:00
|
|
|
static CURLcode cr_connect(struct Curl_cfilter *cf, struct Curl_easy *data,
|
|
|
|
|
bool *done)
|
2020-12-12 23:55:09 -08:00
|
|
|
{
|
2025-11-29 02:16:52 +01:00
|
|
|
struct ssl_connect_data * const connssl = cf->ctx;
|
|
|
|
|
const struct rustls_ssl_backend_data * const backend =
|
2023-05-09 10:10:40 +00:00
|
|
|
(struct rustls_ssl_backend_data *)connssl->backend;
|
2025-03-21 14:43:07 -04:00
|
|
|
const struct rustls_connection *rconn = NULL;
|
2020-12-12 23:55:09 -08:00
|
|
|
CURLcode tmperr = CURLE_OK;
|
2026-04-06 22:21:12 +02:00
|
|
|
CURLcode result;
|
2020-12-12 23:55:09 -08:00
|
|
|
bool wants_read;
|
|
|
|
|
bool wants_write;
|
2026-04-06 18:45:19 +02:00
|
|
|
ssize_t nread;
|
2020-12-12 23:55:09 -08:00
|
|
|
|
2022-02-17 17:48:48 +01:00
|
|
|
DEBUGASSERT(backend);
|
|
|
|
|
|
2026-04-16 10:52:57 +02:00
|
|
|
CURL_TRC_CF(data, cf, "cr_connect, state=%d", (int)connssl->state);
|
2024-05-17 13:23:19 +02:00
|
|
|
*done = FALSE;
|
2025-02-19 16:49:31 +01:00
|
|
|
|
2026-03-31 11:45:21 +02:00
|
|
|
#ifdef USE_ECH
|
|
|
|
|
/* if we do ECH and need the HTTPS-RR information for it,
|
|
|
|
|
* we delay the connect until it arrives or DNS resolve fails. */
|
|
|
|
|
if(cr_ech_need_httpsrr(data) &&
|
2026-06-16 12:07:08 +02:00
|
|
|
!Curl_conn_dns_resolved_https(data, cf->sockindex,
|
|
|
|
|
connssl->peer.peer)) {
|
2026-03-31 11:45:21 +02:00
|
|
|
CURL_TRC_CF(data, cf, "need HTTPS-RR for ECH, delaying connect");
|
|
|
|
|
return CURLE_OK;
|
|
|
|
|
}
|
|
|
|
|
#endif /* USE_ECH */
|
|
|
|
|
|
2024-05-17 13:23:19 +02:00
|
|
|
if(!backend->conn) {
|
2026-04-06 22:21:12 +02:00
|
|
|
result =
|
|
|
|
|
cr_init_backend(cf, data,
|
|
|
|
|
(struct rustls_ssl_backend_data *)connssl->backend);
|
2026-04-16 10:52:57 +02:00
|
|
|
CURL_TRC_CF(data, cf, "cr_connect, init backend -> %d", (int)result);
|
2026-04-06 22:21:12 +02:00
|
|
|
if(result)
|
2021-02-26 17:29:36 -08:00
|
|
|
return result;
|
2020-12-12 23:55:09 -08:00
|
|
|
connssl->state = ssl_connection_negotiating;
|
|
|
|
|
}
|
2021-05-14 18:45:49 -07:00
|
|
|
rconn = backend->conn;
|
2021-02-26 17:29:36 -08:00
|
|
|
|
2020-12-12 23:55:09 -08:00
|
|
|
/* Read/write data until the handshake is done or the socket would block. */
|
|
|
|
|
for(;;) {
|
|
|
|
|
/*
|
2025-11-29 02:16:52 +01:00
|
|
|
* Connection has been established according to Rustls. Set send/recv
|
|
|
|
|
* handlers, and update the state machine.
|
|
|
|
|
*/
|
2024-06-04 14:00:12 +02:00
|
|
|
connssl->io_need = CURL_SSL_IO_NEED_NONE;
|
2021-05-14 18:45:49 -07:00
|
|
|
if(!rustls_connection_is_handshaking(rconn)) {
|
2024-08-19 16:53:40 +02:00
|
|
|
/* Rustls claims it is no longer handshaking *before* it has
|
2024-05-17 13:23:19 +02:00
|
|
|
* send its FINISHED message off. We attempt to let it write
|
|
|
|
|
* one more time. Oh my.
|
|
|
|
|
*/
|
2025-06-11 14:50:15 +02:00
|
|
|
size_t nwritten;
|
2022-11-22 09:55:41 +01:00
|
|
|
cr_set_negotiated_alpn(cf, data, rconn);
|
2025-06-11 14:50:15 +02:00
|
|
|
tmperr = cr_send(cf, data, NULL, 0, &nwritten);
|
2024-05-17 13:23:19 +02:00
|
|
|
if(tmperr == CURLE_AGAIN) {
|
2024-06-04 14:00:12 +02:00
|
|
|
connssl->io_need = CURL_SSL_IO_NEED_SEND;
|
2024-05-17 13:23:19 +02:00
|
|
|
return CURLE_OK;
|
|
|
|
|
}
|
|
|
|
|
else if(tmperr != CURLE_OK) {
|
|
|
|
|
return tmperr;
|
|
|
|
|
}
|
|
|
|
|
/* REALLY Done with the handshake. */
|
2024-08-13 22:01:07 +02:00
|
|
|
{
|
2026-01-24 00:03:23 +01:00
|
|
|
#ifdef CURLVERBOSE
|
2025-11-29 02:16:52 +01:00
|
|
|
const uint16_t proto = rustls_connection_get_protocol_version(rconn);
|
2025-07-11 15:56:04 -04:00
|
|
|
const rustls_str ciphersuite_name =
|
2025-07-11 15:52:08 -04:00
|
|
|
rustls_connection_get_negotiated_ciphersuite_name(rconn);
|
2025-07-11 16:00:01 -04:00
|
|
|
const rustls_str kex_group_name =
|
|
|
|
|
rustls_connection_get_negotiated_key_exchange_group_name(rconn);
|
2024-08-13 22:01:07 +02:00
|
|
|
const char *ver = "TLS version unknown";
|
|
|
|
|
if(proto == RUSTLS_TLS_VERSION_TLSV1_3)
|
|
|
|
|
ver = "TLSv1.3";
|
|
|
|
|
if(proto == RUSTLS_TLS_VERSION_TLSV1_2)
|
|
|
|
|
ver = "TLSv1.2";
|
2026-01-24 00:03:23 +01:00
|
|
|
#endif
|
2025-07-11 15:56:04 -04:00
|
|
|
infof(data,
|
2025-07-11 16:00:01 -04:00
|
|
|
"rustls: handshake complete, %s, ciphersuite: %.*s, "
|
|
|
|
|
"key exchange group: %.*s",
|
2025-07-11 15:56:04 -04:00
|
|
|
ver,
|
|
|
|
|
(int) ciphersuite_name.len,
|
2025-07-11 16:00:01 -04:00
|
|
|
ciphersuite_name.data,
|
|
|
|
|
(int) kex_group_name.len,
|
|
|
|
|
kex_group_name.data);
|
2024-08-13 22:01:07 +02:00
|
|
|
}
|
2025-02-24 21:40:19 +02:00
|
|
|
if(data->set.ssl.certinfo) {
|
|
|
|
|
size_t num_certs = 0;
|
2025-08-21 22:27:41 +02:00
|
|
|
size_t i;
|
2025-11-07 20:51:22 +08:00
|
|
|
while(rustls_connection_get_peer_certificate(rconn, num_certs)) {
|
2025-02-24 21:40:19 +02:00
|
|
|
num_certs++;
|
2025-11-07 20:51:22 +08:00
|
|
|
if(num_certs > MAX_ALLOWED_CERT_AMOUNT) {
|
2026-04-16 03:16:20 +02:00
|
|
|
failf(data, "%zu certificates is more than allowed (%d)",
|
2025-11-07 20:51:22 +08:00
|
|
|
num_certs, MAX_ALLOWED_CERT_AMOUNT);
|
|
|
|
|
return CURLE_SSL_CONNECT_ERROR;
|
|
|
|
|
}
|
2025-02-24 21:40:19 +02:00
|
|
|
}
|
|
|
|
|
result = Curl_ssl_init_certinfo(data, (int)num_certs);
|
|
|
|
|
if(result)
|
|
|
|
|
return result;
|
2025-08-21 22:27:41 +02:00
|
|
|
for(i = 0; i < num_certs; i++) {
|
2025-02-24 21:40:19 +02:00
|
|
|
const rustls_certificate *cert;
|
|
|
|
|
const unsigned char *der_data;
|
|
|
|
|
size_t der_len;
|
|
|
|
|
rustls_result rresult = RUSTLS_RESULT_OK;
|
|
|
|
|
cert = rustls_connection_get_peer_certificate(rconn, i);
|
|
|
|
|
DEBUGASSERT(cert); /* Should exist since we counted already */
|
|
|
|
|
rresult = rustls_certificate_get_der(cert, &der_data, &der_len);
|
|
|
|
|
if(rresult != RUSTLS_RESULT_OK) {
|
|
|
|
|
char errorbuf[255];
|
|
|
|
|
size_t errorlen;
|
|
|
|
|
rustls_error(rresult, errorbuf, sizeof(errorbuf), &errorlen);
|
|
|
|
|
failf(data,
|
2025-09-20 23:33:05 +02:00
|
|
|
"Failed getting DER of server certificate #%zu: %.*s", i,
|
|
|
|
|
(int)errorlen, errorbuf);
|
2025-02-24 21:40:19 +02:00
|
|
|
return map_error(rresult);
|
|
|
|
|
}
|
|
|
|
|
{
|
|
|
|
|
const char *beg;
|
|
|
|
|
const char *end;
|
|
|
|
|
beg = (const char *)der_data;
|
|
|
|
|
end = (const char *)(der_data + der_len);
|
|
|
|
|
result = Curl_extract_certinfo(data, (int)i, beg, end);
|
|
|
|
|
if(result)
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-17 13:23:19 +02:00
|
|
|
connssl->state = ssl_connection_complete;
|
2020-12-12 23:55:09 -08:00
|
|
|
*done = TRUE;
|
|
|
|
|
return CURLE_OK;
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-04 14:00:12 +02:00
|
|
|
connssl->connecting_state = ssl_connect_2;
|
2021-05-14 18:45:49 -07:00
|
|
|
wants_read = rustls_connection_wants_read(rconn);
|
2024-05-17 13:23:19 +02:00
|
|
|
wants_write = rustls_connection_wants_write(rconn) ||
|
|
|
|
|
backend->plain_out_buffered;
|
2020-12-12 23:55:09 -08:00
|
|
|
DEBUGASSERT(wants_read || wants_write);
|
|
|
|
|
|
|
|
|
|
if(wants_write) {
|
2025-06-11 14:50:15 +02:00
|
|
|
size_t nwritten;
|
2024-05-17 13:23:19 +02:00
|
|
|
CURL_TRC_CF(data, cf, "rustls_connection wants us to write_tls.");
|
2025-06-11 14:50:15 +02:00
|
|
|
tmperr = cr_send(cf, data, NULL, 0, &nwritten);
|
2020-12-12 23:55:09 -08:00
|
|
|
if(tmperr == CURLE_AGAIN) {
|
2024-05-17 13:23:19 +02:00
|
|
|
CURL_TRC_CF(data, cf, "writing would block");
|
2025-02-19 16:49:31 +01:00
|
|
|
connssl->io_need = CURL_SSL_IO_NEED_SEND;
|
|
|
|
|
return CURLE_OK;
|
2020-12-12 23:55:09 -08:00
|
|
|
}
|
|
|
|
|
else if(tmperr != CURLE_OK) {
|
|
|
|
|
return tmperr;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(wants_read) {
|
2024-05-17 13:23:19 +02:00
|
|
|
CURL_TRC_CF(data, cf, "rustls_connection wants us to read_tls.");
|
2026-04-06 18:45:19 +02:00
|
|
|
nread = tls_recv_more(cf, data, &tmperr);
|
|
|
|
|
if(nread == 0) {
|
|
|
|
|
connssl->peer_closed = TRUE;
|
|
|
|
|
failf(data, "TLS connect error: Connection closed abruptly");
|
|
|
|
|
return CURLE_SSL_CONNECT_ERROR;
|
|
|
|
|
}
|
|
|
|
|
if(nread < 0) {
|
2023-03-31 11:07:18 +02:00
|
|
|
if(tmperr == CURLE_AGAIN) {
|
2024-05-17 13:23:19 +02:00
|
|
|
CURL_TRC_CF(data, cf, "reading would block");
|
2025-02-19 16:49:31 +01:00
|
|
|
connssl->io_need = CURL_SSL_IO_NEED_RECV;
|
|
|
|
|
return CURLE_OK;
|
2023-03-31 11:07:18 +02:00
|
|
|
}
|
2024-05-16 08:55:14 +02:00
|
|
|
else if(tmperr == CURLE_RECV_ERROR) {
|
2020-12-12 23:55:09 -08:00
|
|
|
return CURLE_SSL_CONNECT_ERROR;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
return tmperr;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* We should never fall through the loop. We should return either because
|
2024-07-01 16:47:21 +02:00
|
|
|
the handshake is done or because we cannot read/write without blocking. */
|
2024-10-02 11:53:20 +02:00
|
|
|
DEBUGASSERT(FALSE);
|
2020-12-12 23:55:09 -08:00
|
|
|
}
|
|
|
|
|
|
2025-11-29 02:16:52 +01:00
|
|
|
static void *cr_get_internals(struct ssl_connect_data *connssl, CURLINFO info)
|
2020-12-12 23:55:09 -08:00
|
|
|
{
|
2023-05-09 10:10:40 +00:00
|
|
|
struct rustls_ssl_backend_data *backend =
|
|
|
|
|
(struct rustls_ssl_backend_data *)connssl->backend;
|
2025-09-02 15:40:12 +02:00
|
|
|
(void)info;
|
2022-02-17 17:48:48 +01:00
|
|
|
DEBUGASSERT(backend);
|
2025-07-23 11:21:36 +02:00
|
|
|
return backend->conn;
|
2020-12-12 23:55:09 -08:00
|
|
|
}
|
|
|
|
|
|
2025-11-29 02:16:52 +01:00
|
|
|
static CURLcode cr_shutdown(struct Curl_cfilter *cf, struct Curl_easy *data,
|
|
|
|
|
const bool send_shutdown, bool *done)
|
2024-06-07 10:12:39 +02:00
|
|
|
{
|
|
|
|
|
struct ssl_connect_data *connssl = cf->ctx;
|
|
|
|
|
struct rustls_ssl_backend_data *backend =
|
|
|
|
|
(struct rustls_ssl_backend_data *)connssl->backend;
|
|
|
|
|
CURLcode result = CURLE_OK;
|
build: address some `-Weverything` warnings, update picky warnings
`-Weverything` is not enabled by curl, and not recommended by LLVM,
because it may enable experimental options, and will result in new
fallouts after toolchain upgrades. This patch aims to fix/silence as much
as possible as found with llvm/clang 21.1.0. It also permanently enables
warnings that were fixed in source and deemed manageable in the future.
`-Wformat` warnings are addressed separately via #18343.
Fix/silence warnings in the source:
- typecheck-gcc.h: fix `-Wreserved-identifier`.
- lib: silence `-Wcast-function-type-strict`.
For llvm 16+ or Apple clang 16+.
- asyn-ares: limit `HAPPY_EYEBALLS_DNS_TIMEOUT` to old c-ares versions.
- curl_trc: fix `-Wc++-hidden-decl`.
- doh: fix `-Wc++-keyword`.
- ftp: fix `-Wreserved-identifier`.
- ldap: fix `-Wreserved-identifier`.
- mqtt: comment unused macro to avoid warning.
- multi_ev: drop unused macros to avoid warnings.
- setopt: fix useless `break;` after `return;`.
- gtls, mbedtls, rustls: silence `-Wconditional-uninitialized`.
- socks_sspi, schannel, x509asn1: fix `-Wimplicit-int-enum-cast`.
- x509asn1: fix `-Wc++-keyword`.
- openssl: scope `OSSL_UI_METHOD_CAST` to avoid unused macro warning.
- libssh2, wolfssl: drop unused macros.
- curl_ngtcp2, curl_quiche, httpsrr, urlapi: drop/limit unused macros.
- tool_getparam: fix useless `break;` after `return;` or `break;`.
Not normally enabled because it doesn't work with unity.
https://github.com/llvm/llvm-project/issues/71046
- tool_operate: fix `-Wc++-keyword`.
- curlinfo: fix a `-Wunsafe-buffer-usage`.
- tests: silence `-Wformat-non-iso`.
- lib557: fix `-Wreserved-identifier`.
- lib1565: silence `-Wconditional-uninitialized`.
Enable the above clang warnings permanently in picky mode:
- `-Wc++-hidden-decl`
- `-Wc++-keyword` (except for Windows, where it collides with `wchar_t`)
- `-Wcast-function-type-strict`
- `-Wcast-function-type`
- `-Wconditional-uninitialized`
- `-Wformat-non-iso` (except for clang-cl)
- `-Wreserved-identifier`
- `-Wtentative-definition-compat`
Silence problematic `-Weverything` warnings globally (in picky mode):
- `-Wused-but-marked-unused` (88000+ hits) and
`-Wdisabled-macro-expansion` (2600+ hits).
Triggered by `typecheck-gcc.h` when building with clang 14+.
Maybe there exists a way to fix within that header?
Ref: https://discourse.llvm.org/t/removing-wused-but-marked-unused/55310
- `-Wunsafe-buffer-usage`. clang 16+. 7000+ hits.
May be useful in theory, but such high volume of hits makes it
impractical to review and possibly address. Meant for C++.
Ref: https://clang.llvm.org/docs/SafeBuffers.html
Ref: https://stackoverflow.com/questions/77017567/how-to-fix-code-to-avoid-warning-wunsafe-buffer-usage
Ref: https://discourse.llvm.org/t/rfc-c-buffer-hardening/65734
Ref: https://github.com/llvm/llvm-project/pull/111624
- `-Wimplicit-void-ptr-cast`. clang 21+. 1700+ hits.
C++ warning, deemed pure noise.
Ref: https://github.com/curl/curl/issues/18470#issuecomment-3253506266
- `-Wswitch-default` (180+ hits), `-Wswitch-enum` (190+ hits),
`-Wcovered-switch-default` (20+ hits).
Next to impossible to fix cleanly, esp. when the covered `case`
branches depend on compile-time options.
- `-Wdocumentation-unknown-command` (8+ hits).
Triggered in a few sources. Seems arbitrary and bogus.
- `-Wpadded` (550+ hits).
- `-Wc++-keyword` on Windows, where it collides with `wchar_t`.
(100+ hits)
Ref: https://github.com/llvm/llvm-project/issues/155988
- `-Wreserved-macro-identifier`. clang 13+. 5+ hits.
Sometimes it's necessary to set external macros that use
the reserved namespace. E.g. `_CRT_NONSTDC_NO_DEPRECATE`,
`__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__`, `__NO_NET_API`,
possibly `_REENTRANT`, and more.
It's not worth trying to silence them individually.
- `-Wnonportable-system-include-path` with `clang-cl`.
It'd be broken by doing what the warning suggests.
- `-Wformat-non-iso` for clang-cl.
CMake `PICKY_COMPILER=ON` (the default) or `./configure`
`--enable-warnings` (not the default) is required to enable these
silencing rules.
Also:
- autotools, cmake: fix Apple clang and mainline llvm version translations.
Ref: https://en.wikipedia.org/wiki/Xcode#Toolchain_versions
- autotools, cmake: enable `-Warray-compare` for clang 20+.
Follow-up to 4b7accda5ae3f2e663aa3f3853805241ef87c2fe #17196
- cmake: fix to enable `-Wmissing-variable-declarations` at an earlier
clang version.
- cmake: update internal logic to handle warning options with `+` in
them.
- cmake: fix internal logic to match the whole option when looking
into `CMAKE_C_FLAGS` for custom-disabled warnings.
Follow-up to b85cb8cb4e143d1615d4fcc1ce8f2f7b66453995 #18485
Closes #18477
2025-09-04 11:56:33 +02:00
|
|
|
size_t i, nread = 0, nwritten;
|
2024-06-07 10:12:39 +02:00
|
|
|
|
|
|
|
|
DEBUGASSERT(backend);
|
lib: graceful connection shutdown
When libcurl discards a connection there are two phases this may go
through: "shutdown" and "closing". If a connection is aborted, the
shutdown phase is skipped and it is closed right away.
The connection filters attached to the connection implement the phases
in their `do_shutdown()` and `do_close()` callbacks. Filters carry now a
`shutdown` flags next to `connected` to keep track of the shutdown
operation.
Filters are shut down from top to bottom. If a filter is not connected,
its shutdown is skipped. Notable filters that *do* something during
shutdown are HTTP/2 and TLS. HTTP/2 sends the GOAWAY frame. TLS sends
its close notify and expects to receive a close notify from the server.
As sends and receives may EAGAIN on the network, a shutdown is often not
successful right away and needs to poll the connection's socket(s). To
facilitate this, such connections are placed on a new shutdown list
inside the connection cache.
Since managing this list requires the cooperation of a multi handle,
only the connection cache belonging to a multi handle is used. If a
connection was in another cache when being discarded, it is removed
there and added to the multi's cache. If no multi handle is available at
that time, the connection is shutdown and closed in a one-time,
best-effort attempt.
When a multi handle is destroyed, all connection still on the shutdown
list are discarded with a final shutdown attempt and close. In curl
debug builds, the environment variable `CURL_GRACEFUL_SHUTDOWN` can be
set to make this graceful with a timeout in milliseconds given by the
variable.
The shutdown list is limited to the max number of connections configured
for a multi cache. Set via CURLMOPT_MAX_TOTAL_CONNECTIONS. When the
limit is reached, the oldest connection on the shutdown list is
discarded.
- In multi_wait() and multi_waitfds(), collect all connection caches
involved (each transfer might carry its own) into a temporary list.
Let each connection cache on the list contribute sockets and
POLLIN/OUT events it's connections are waiting for.
- in multi_perform() collect the connection caches the same way and let
them peform their maintenance. This will make another non-blocking
attempt to shutdown all connections on its shutdown list.
- for event based multis (multi->socket_cb set), add the sockets and
their poll events via the callback. When `multi_socket()` is invoked
for a socket not known by an active transfer, forward this to the
multi's cache for processing. On closing a connection, remove its
socket(s) via the callback.
TLS connection filters MUST NOT send close nofity messages in their
`do_close()` implementation. The reason is that a TLS close notify
signals a success. When a connection is aborted and skips its shutdown
phase, the server needs to see a missing close notify to detect
something has gone wrong.
A graceful shutdown of FTP's data connection is performed implicitly
before regarding the upload/download as complete and continuing on the
control connection. For FTP without TLS, there is just the socket close
happening. But with TLS, the sent/received close notify signals that the
transfer is complete and healthy. Servers like `vsftpd` verify that and
reject uploads without a TLS close notify.
- added test_19_* for shutdown related tests
- test_19_01 and test_19_02 test for TCP RST packets
which happen without a graceful shutdown and should
no longer appear otherwise.
- add test_19_03 for handling shutdowns by the server
- add test_19_04 for handling shutdowns by curl
- add test_19_05 for event based shutdowny by server
- add test_30_06/07 and test_31_06/07 for shutdown checks
on FTP up- and downloads.
Closes #13976
2024-06-19 12:40:06 +02:00
|
|
|
if(!backend->conn || cf->shutdown) {
|
2024-06-07 10:12:39 +02:00
|
|
|
*done = TRUE;
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
connssl->io_need = CURL_SSL_IO_NEED_NONE;
|
|
|
|
|
*done = FALSE;
|
|
|
|
|
|
|
|
|
|
if(!backend->sent_shutdown) {
|
|
|
|
|
/* do this only once */
|
|
|
|
|
backend->sent_shutdown = TRUE;
|
|
|
|
|
if(send_shutdown) {
|
|
|
|
|
rustls_connection_send_close_notify(backend->conn);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-11 14:50:15 +02:00
|
|
|
result = cr_send(cf, data, NULL, 0, &nwritten);
|
|
|
|
|
if(result) {
|
2024-06-07 10:12:39 +02:00
|
|
|
if(result == CURLE_AGAIN) {
|
|
|
|
|
connssl->io_need = CURL_SSL_IO_NEED_SEND;
|
|
|
|
|
result = CURLE_OK;
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
DEBUGASSERT(result);
|
2026-04-16 10:52:57 +02:00
|
|
|
CURL_TRC_CF(data, cf, "shutdown send failed: %d", (int)result);
|
2024-06-07 10:12:39 +02:00
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for(i = 0; i < 10; ++i) {
|
2025-03-21 14:43:07 -04:00
|
|
|
char buf[1024];
|
2025-06-11 14:50:15 +02:00
|
|
|
result = cr_recv(cf, data, buf, (int)sizeof(buf), &nread);
|
|
|
|
|
if(result)
|
2024-06-07 10:12:39 +02:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-11 14:50:15 +02:00
|
|
|
if(result == CURLE_AGAIN) {
|
2024-06-07 10:12:39 +02:00
|
|
|
connssl->io_need = CURL_SSL_IO_NEED_RECV;
|
|
|
|
|
result = CURLE_OK;
|
|
|
|
|
}
|
2025-06-11 14:50:15 +02:00
|
|
|
else if(result) {
|
2024-06-07 10:12:39 +02:00
|
|
|
DEBUGASSERT(result);
|
2026-04-16 10:52:57 +02:00
|
|
|
CURL_TRC_CF(data, cf, "shutdown, error: %d", (int)result);
|
2024-06-07 10:12:39 +02:00
|
|
|
}
|
2025-06-11 14:50:15 +02:00
|
|
|
else if(nread == 0) {
|
|
|
|
|
/* We got the close notify alert and are done. */
|
|
|
|
|
*done = TRUE;
|
|
|
|
|
}
|
2024-06-07 10:12:39 +02:00
|
|
|
|
|
|
|
|
out:
|
lib: graceful connection shutdown
When libcurl discards a connection there are two phases this may go
through: "shutdown" and "closing". If a connection is aborted, the
shutdown phase is skipped and it is closed right away.
The connection filters attached to the connection implement the phases
in their `do_shutdown()` and `do_close()` callbacks. Filters carry now a
`shutdown` flags next to `connected` to keep track of the shutdown
operation.
Filters are shut down from top to bottom. If a filter is not connected,
its shutdown is skipped. Notable filters that *do* something during
shutdown are HTTP/2 and TLS. HTTP/2 sends the GOAWAY frame. TLS sends
its close notify and expects to receive a close notify from the server.
As sends and receives may EAGAIN on the network, a shutdown is often not
successful right away and needs to poll the connection's socket(s). To
facilitate this, such connections are placed on a new shutdown list
inside the connection cache.
Since managing this list requires the cooperation of a multi handle,
only the connection cache belonging to a multi handle is used. If a
connection was in another cache when being discarded, it is removed
there and added to the multi's cache. If no multi handle is available at
that time, the connection is shutdown and closed in a one-time,
best-effort attempt.
When a multi handle is destroyed, all connection still on the shutdown
list are discarded with a final shutdown attempt and close. In curl
debug builds, the environment variable `CURL_GRACEFUL_SHUTDOWN` can be
set to make this graceful with a timeout in milliseconds given by the
variable.
The shutdown list is limited to the max number of connections configured
for a multi cache. Set via CURLMOPT_MAX_TOTAL_CONNECTIONS. When the
limit is reached, the oldest connection on the shutdown list is
discarded.
- In multi_wait() and multi_waitfds(), collect all connection caches
involved (each transfer might carry its own) into a temporary list.
Let each connection cache on the list contribute sockets and
POLLIN/OUT events it's connections are waiting for.
- in multi_perform() collect the connection caches the same way and let
them peform their maintenance. This will make another non-blocking
attempt to shutdown all connections on its shutdown list.
- for event based multis (multi->socket_cb set), add the sockets and
their poll events via the callback. When `multi_socket()` is invoked
for a socket not known by an active transfer, forward this to the
multi's cache for processing. On closing a connection, remove its
socket(s) via the callback.
TLS connection filters MUST NOT send close nofity messages in their
`do_close()` implementation. The reason is that a TLS close notify
signals a success. When a connection is aborted and skips its shutdown
phase, the server needs to see a missing close notify to detect
something has gone wrong.
A graceful shutdown of FTP's data connection is performed implicitly
before regarding the upload/download as complete and continuing on the
control connection. For FTP without TLS, there is just the socket close
happening. But with TLS, the sent/received close notify signals that the
transfer is complete and healthy. Servers like `vsftpd` verify that and
reject uploads without a TLS close notify.
- added test_19_* for shutdown related tests
- test_19_01 and test_19_02 test for TCP RST packets
which happen without a graceful shutdown and should
no longer appear otherwise.
- add test_19_03 for handling shutdowns by the server
- add test_19_04 for handling shutdowns by curl
- add test_19_05 for event based shutdowny by server
- add test_30_06/07 and test_31_06/07 for shutdown checks
on FTP up- and downloads.
Closes #13976
2024-06-19 12:40:06 +02:00
|
|
|
cf->shutdown = (result || *done);
|
2024-06-07 10:12:39 +02:00
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-29 02:16:52 +01:00
|
|
|
static void cr_close(struct Curl_cfilter *cf, struct Curl_easy *data)
|
2020-12-12 23:55:09 -08:00
|
|
|
{
|
2025-03-21 14:43:07 -04:00
|
|
|
const struct ssl_connect_data *connssl = cf->ctx;
|
2023-05-09 10:10:40 +00:00
|
|
|
struct rustls_ssl_backend_data *backend =
|
|
|
|
|
(struct rustls_ssl_backend_data *)connssl->backend;
|
2020-12-12 23:55:09 -08:00
|
|
|
|
lib: graceful connection shutdown
When libcurl discards a connection there are two phases this may go
through: "shutdown" and "closing". If a connection is aborted, the
shutdown phase is skipped and it is closed right away.
The connection filters attached to the connection implement the phases
in their `do_shutdown()` and `do_close()` callbacks. Filters carry now a
`shutdown` flags next to `connected` to keep track of the shutdown
operation.
Filters are shut down from top to bottom. If a filter is not connected,
its shutdown is skipped. Notable filters that *do* something during
shutdown are HTTP/2 and TLS. HTTP/2 sends the GOAWAY frame. TLS sends
its close notify and expects to receive a close notify from the server.
As sends and receives may EAGAIN on the network, a shutdown is often not
successful right away and needs to poll the connection's socket(s). To
facilitate this, such connections are placed on a new shutdown list
inside the connection cache.
Since managing this list requires the cooperation of a multi handle,
only the connection cache belonging to a multi handle is used. If a
connection was in another cache when being discarded, it is removed
there and added to the multi's cache. If no multi handle is available at
that time, the connection is shutdown and closed in a one-time,
best-effort attempt.
When a multi handle is destroyed, all connection still on the shutdown
list are discarded with a final shutdown attempt and close. In curl
debug builds, the environment variable `CURL_GRACEFUL_SHUTDOWN` can be
set to make this graceful with a timeout in milliseconds given by the
variable.
The shutdown list is limited to the max number of connections configured
for a multi cache. Set via CURLMOPT_MAX_TOTAL_CONNECTIONS. When the
limit is reached, the oldest connection on the shutdown list is
discarded.
- In multi_wait() and multi_waitfds(), collect all connection caches
involved (each transfer might carry its own) into a temporary list.
Let each connection cache on the list contribute sockets and
POLLIN/OUT events it's connections are waiting for.
- in multi_perform() collect the connection caches the same way and let
them peform their maintenance. This will make another non-blocking
attempt to shutdown all connections on its shutdown list.
- for event based multis (multi->socket_cb set), add the sockets and
their poll events via the callback. When `multi_socket()` is invoked
for a socket not known by an active transfer, forward this to the
multi's cache for processing. On closing a connection, remove its
socket(s) via the callback.
TLS connection filters MUST NOT send close nofity messages in their
`do_close()` implementation. The reason is that a TLS close notify
signals a success. When a connection is aborted and skips its shutdown
phase, the server needs to see a missing close notify to detect
something has gone wrong.
A graceful shutdown of FTP's data connection is performed implicitly
before regarding the upload/download as complete and continuing on the
control connection. For FTP without TLS, there is just the socket close
happening. But with TLS, the sent/received close notify signals that the
transfer is complete and healthy. Servers like `vsftpd` verify that and
reject uploads without a TLS close notify.
- added test_19_* for shutdown related tests
- test_19_01 and test_19_02 test for TCP RST packets
which happen without a graceful shutdown and should
no longer appear otherwise.
- add test_19_03 for handling shutdowns by the server
- add test_19_04 for handling shutdowns by curl
- add test_19_05 for event based shutdowny by server
- add test_30_06/07 and test_31_06/07 for shutdown checks
on FTP up- and downloads.
Closes #13976
2024-06-19 12:40:06 +02:00
|
|
|
(void)data;
|
2022-02-17 17:48:48 +01:00
|
|
|
DEBUGASSERT(backend);
|
2024-06-07 10:12:39 +02:00
|
|
|
if(backend->conn) {
|
2021-05-14 18:45:49 -07:00
|
|
|
rustls_connection_free(backend->conn);
|
|
|
|
|
backend->conn = NULL;
|
2020-12-12 23:55:09 -08:00
|
|
|
}
|
|
|
|
|
if(backend->config) {
|
|
|
|
|
rustls_client_config_free(backend->config);
|
|
|
|
|
backend->config = NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-04 10:37:32 +01:00
|
|
|
static size_t cr_version(char *buffer, size_t size)
|
|
|
|
|
{
|
2025-03-21 14:43:07 -04:00
|
|
|
const struct rustls_str ver = rustls_version();
|
2025-10-04 12:58:49 +02:00
|
|
|
return curl_msnprintf(buffer, size, "%.*s", (int)ver.len, ver.data);
|
2021-11-04 10:37:32 +01:00
|
|
|
}
|
|
|
|
|
|
2025-11-29 02:16:52 +01:00
|
|
|
static CURLcode cr_random(struct Curl_easy *data, unsigned char *entropy,
|
|
|
|
|
size_t length)
|
2024-09-12 12:38:51 -04:00
|
|
|
{
|
|
|
|
|
rustls_result rresult = 0;
|
|
|
|
|
(void)data;
|
2025-11-29 02:16:52 +01:00
|
|
|
rresult = rustls_default_crypto_provider_random(entropy, length);
|
2024-09-12 12:38:51 -04:00
|
|
|
return map_error(rresult);
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-21 23:28:28 +03:00
|
|
|
static void cr_cleanup(void)
|
|
|
|
|
{
|
|
|
|
|
Curl_tls_keylog_close();
|
|
|
|
|
}
|
|
|
|
|
|
2020-12-12 23:55:09 -08:00
|
|
|
const struct Curl_ssl Curl_ssl_rustls = {
|
|
|
|
|
{ CURLSSLBACKEND_RUSTLS, "rustls" },
|
2022-01-09 15:59:30 -06:00
|
|
|
SSLSUPP_CAINFO_BLOB | /* supports */
|
2024-08-13 22:01:07 +02:00
|
|
|
SSLSUPP_HTTPS_PROXY |
|
|
|
|
|
SSLSUPP_CIPHER_LIST |
|
2025-02-24 21:40:19 +02:00
|
|
|
SSLSUPP_TLS13_CIPHERSUITES |
|
2025-03-24 12:01:30 -04:00
|
|
|
SSLSUPP_CERTINFO |
|
2026-01-20 00:03:00 +01:00
|
|
|
SSLSUPP_ECH |
|
|
|
|
|
SSLSUPP_CRLFILE,
|
2023-05-09 10:10:40 +00:00
|
|
|
sizeof(struct rustls_ssl_backend_data),
|
2020-12-12 23:55:09 -08:00
|
|
|
|
2024-12-18 11:46:37 +01:00
|
|
|
NULL, /* init */
|
2024-10-21 23:28:28 +03:00
|
|
|
cr_cleanup, /* cleanup */
|
2021-11-04 10:37:32 +01:00
|
|
|
cr_version, /* version */
|
2024-06-07 10:12:39 +02:00
|
|
|
cr_shutdown, /* shutdown */
|
2020-12-12 23:55:09 -08:00
|
|
|
cr_data_pending, /* data_pending */
|
2024-09-12 12:38:51 -04:00
|
|
|
cr_random, /* random */
|
2024-12-18 11:46:37 +01:00
|
|
|
NULL, /* cert_status_request */
|
2025-02-19 16:49:31 +01:00
|
|
|
cr_connect, /* connect */
|
2024-05-17 13:23:19 +02:00
|
|
|
Curl_ssl_adjust_pollset, /* adjust_pollset */
|
2020-12-12 23:55:09 -08:00
|
|
|
cr_get_internals, /* get_internals */
|
|
|
|
|
cr_close, /* close_one */
|
2024-12-18 11:46:37 +01:00
|
|
|
NULL, /* close_all */
|
|
|
|
|
NULL, /* set_engine */
|
|
|
|
|
NULL, /* set_engine_default */
|
|
|
|
|
NULL, /* engines_list */
|
2021-05-05 13:42:26 +02:00
|
|
|
NULL, /* sha256sum */
|
2022-11-11 11:45:34 +01:00
|
|
|
cr_recv, /* recv decrypted data */
|
|
|
|
|
cr_send, /* send data to encrypt */
|
2024-02-29 09:12:59 +01:00
|
|
|
NULL, /* get_channel_binding */
|
2020-12-12 23:55:09 -08:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
#endif /* USE_RUSTLS */
|