Initial project import

This commit is contained in:
Agila Forge Importer committed 2026-07-13 15:20:32 +00:00
commit 23f2d29e32
11957 files changed
+1754577

No files matched your search

+253
View File
@@ -0,0 +1,253 @@
module mbedtls
#flag -I @VEXEROOT/thirdparty/mbedtls/library
#flag -I @VEXEROOT/thirdparty/mbedtls/include
#flag windows -DWIN32_LEAN_AND_MEAN
// #flag -D _FILE_OFFSET_BITS=64
#flag -I @VEXEROOT/thirdparty/mbedtls/3rdparty/everest/include
#flag -I @VEXEROOT/thirdparty/mbedtls/3rdparty/everest/include/everest
#flag -I @VEXEROOT/thirdparty/mbedtls/3rdparty/everest/include/everest/kremlib
$if macos && arm64 && tinyc {
// Ensure mbedtls disables unsupported ARM asm paths for tinycc on Apple Silicon.
#flag -D__TINYC__
}
// TODO: this should be built-in to the compiler
$if prod && opt_size ? {
#flag -Os
}
#flag @VEXEROOT/thirdparty/mbedtls/library/aes.o
#flag @VEXEROOT/thirdparty/mbedtls/library/aesce.o
#flag @VEXEROOT/thirdparty/mbedtls/library/aesni.o
#flag @VEXEROOT/thirdparty/mbedtls/library/aria.o
#flag @VEXEROOT/thirdparty/mbedtls/library/asn1parse.o
#flag @VEXEROOT/thirdparty/mbedtls/library/asn1write.o
#flag @VEXEROOT/thirdparty/mbedtls/library/base64.o
#flag @VEXEROOT/thirdparty/mbedtls/library/bignum.o
#flag @VEXEROOT/thirdparty/mbedtls/library/bignum_core.o
#flag @VEXEROOT/thirdparty/mbedtls/library/bignum_mod.o
#flag @VEXEROOT/thirdparty/mbedtls/library/bignum_mod_raw.o
#flag @VEXEROOT/thirdparty/mbedtls/library/block_cipher.o
#flag @VEXEROOT/thirdparty/mbedtls/library/camellia.o
#flag @VEXEROOT/thirdparty/mbedtls/library/ccm.o
#flag @VEXEROOT/thirdparty/mbedtls/library/chacha20.o
#flag @VEXEROOT/thirdparty/mbedtls/library/chachapoly.o
#flag @VEXEROOT/thirdparty/mbedtls/library/cipher.o
#flag @VEXEROOT/thirdparty/mbedtls/library/cipher_wrap.o
#flag @VEXEROOT/thirdparty/mbedtls/library/cmac.o
#flag @VEXEROOT/thirdparty/mbedtls/library/constant_time.o
#flag @VEXEROOT/thirdparty/mbedtls/library/ctr_drbg.o
#flag @VEXEROOT/thirdparty/mbedtls/library/debug.o
#flag @VEXEROOT/thirdparty/mbedtls/library/des.o
#flag @VEXEROOT/thirdparty/mbedtls/library/dhm.o
#flag @VEXEROOT/thirdparty/mbedtls/library/ecdh.o
#flag @VEXEROOT/thirdparty/mbedtls/library/ecdsa.o
#flag @VEXEROOT/thirdparty/mbedtls/library/ecjpake.o
#flag @VEXEROOT/thirdparty/mbedtls/library/ecp.o
#flag @VEXEROOT/thirdparty/mbedtls/library/ecp_curves.o
#flag @VEXEROOT/thirdparty/mbedtls/library/ecp_curves_new.o
#flag @VEXEROOT/thirdparty/mbedtls/library/entropy.o
#flag @VEXEROOT/thirdparty/mbedtls/library/entropy_poll.o
#flag @VEXEROOT/thirdparty/mbedtls/library/error.o
#flag @VEXEROOT/thirdparty/mbedtls/library/gcm.o
#flag @VEXEROOT/thirdparty/mbedtls/library/hkdf.o
#flag @VEXEROOT/thirdparty/mbedtls/library/hmac_drbg.o
#flag @VEXEROOT/thirdparty/mbedtls/library/lmots.o
#flag @VEXEROOT/thirdparty/mbedtls/library/lms.o
#flag @VEXEROOT/thirdparty/mbedtls/library/md5.o
#flag @VEXEROOT/thirdparty/mbedtls/library/md.o
#flag @VEXEROOT/thirdparty/mbedtls/library/memory_buffer_alloc.o
#flag @VEXEROOT/thirdparty/mbedtls/library/mps_reader.o
#flag @VEXEROOT/thirdparty/mbedtls/library/mps_trace.o
#flag @VEXEROOT/thirdparty/mbedtls/library/net_sockets.o
#flag @VEXEROOT/thirdparty/mbedtls/library/nist_kw.o
#flag @VEXEROOT/thirdparty/mbedtls/library/oid.o
#flag @VEXEROOT/thirdparty/mbedtls/library/padlock.o
#flag @VEXEROOT/thirdparty/mbedtls/library/pem.o
#flag @VEXEROOT/thirdparty/mbedtls/library/pk.o
#flag @VEXEROOT/thirdparty/mbedtls/library/pkcs12.o
#flag @VEXEROOT/thirdparty/mbedtls/library/pkcs5.o
#flag @VEXEROOT/thirdparty/mbedtls/library/pkcs7.o
#flag @VEXEROOT/thirdparty/mbedtls/library/pk_ecc.o
#flag @VEXEROOT/thirdparty/mbedtls/library/pkparse.o
#flag @VEXEROOT/thirdparty/mbedtls/library/pk_wrap.o
#flag @VEXEROOT/thirdparty/mbedtls/library/pkwrite.o
#flag @VEXEROOT/thirdparty/mbedtls/library/platform.o
#flag @VEXEROOT/thirdparty/mbedtls/library/platform_util.o
#flag @VEXEROOT/thirdparty/mbedtls/library/poly1305.o
#flag @VEXEROOT/thirdparty/mbedtls/library/psa_crypto_aead.o
#flag @VEXEROOT/thirdparty/mbedtls/library/psa_crypto.o
#flag @VEXEROOT/thirdparty/mbedtls/library/psa_crypto_cipher.o
#flag @VEXEROOT/thirdparty/mbedtls/library/psa_crypto_client.o
#flag @VEXEROOT/thirdparty/mbedtls/library/psa_crypto_driver_wrappers_no_static.o
#flag @VEXEROOT/thirdparty/mbedtls/library/psa_crypto_ecp.o
#flag @VEXEROOT/thirdparty/mbedtls/library/psa_crypto_ffdh.o
#flag @VEXEROOT/thirdparty/mbedtls/library/psa_crypto_hash.o
#flag @VEXEROOT/thirdparty/mbedtls/library/psa_crypto_mac.o
#flag @VEXEROOT/thirdparty/mbedtls/library/psa_crypto_pake.o
#flag @VEXEROOT/thirdparty/mbedtls/library/psa_crypto_random.o
#flag @VEXEROOT/thirdparty/mbedtls/library/psa_crypto_rsa.o
#flag @VEXEROOT/thirdparty/mbedtls/library/psa_crypto_se.o
#flag @VEXEROOT/thirdparty/mbedtls/library/psa_crypto_slot_management.o
#flag @VEXEROOT/thirdparty/mbedtls/library/psa_crypto_storage.o
#flag @VEXEROOT/thirdparty/mbedtls/library/psa_its_file.o
#flag @VEXEROOT/thirdparty/mbedtls/library/psa_util.o
#flag @VEXEROOT/thirdparty/mbedtls/library/ripemd160.o
#flag @VEXEROOT/thirdparty/mbedtls/library/rsa_alt_helpers.o
#flag @VEXEROOT/thirdparty/mbedtls/library/rsa.o
#flag @VEXEROOT/thirdparty/mbedtls/library/sha1.o
#flag @VEXEROOT/thirdparty/mbedtls/library/sha256.o
#flag @VEXEROOT/thirdparty/mbedtls/library/sha3.o
#flag @VEXEROOT/thirdparty/mbedtls/library/sha512.o
#flag @VEXEROOT/thirdparty/mbedtls/library/ssl_cache.o
#flag @VEXEROOT/thirdparty/mbedtls/library/ssl_ciphersuites.o
#flag @VEXEROOT/thirdparty/mbedtls/library/ssl_client.o
#flag @VEXEROOT/thirdparty/mbedtls/library/ssl_cookie.o
#flag @VEXEROOT/thirdparty/mbedtls/library/ssl_debug_helpers_generated.o
#flag @VEXEROOT/thirdparty/mbedtls/library/ssl_msg.o
#flag @VEXEROOT/thirdparty/mbedtls/library/ssl_ticket.o
#flag @VEXEROOT/thirdparty/mbedtls/library/ssl_tls12_client.o
#flag @VEXEROOT/thirdparty/mbedtls/library/ssl_tls12_server.o
#flag @VEXEROOT/thirdparty/mbedtls/library/ssl_tls13_client.o
#flag @VEXEROOT/thirdparty/mbedtls/library/ssl_tls13_generic.o
#flag @VEXEROOT/thirdparty/mbedtls/library/ssl_tls13_keys.o
#flag @VEXEROOT/thirdparty/mbedtls/library/ssl_tls13_server.o
#flag @VEXEROOT/thirdparty/mbedtls/library/ssl_tls.o
#flag @VEXEROOT/thirdparty/mbedtls/library/threading.o
#flag @VEXEROOT/thirdparty/mbedtls/library/timing.o
#flag @VEXEROOT/thirdparty/mbedtls/library/version.o
#flag @VEXEROOT/thirdparty/mbedtls/library/version_features.o
#flag @VEXEROOT/thirdparty/mbedtls/library/x509.o
#flag @VEXEROOT/thirdparty/mbedtls/library/x509_create.o
#flag @VEXEROOT/thirdparty/mbedtls/library/x509_crl.o
#flag @VEXEROOT/thirdparty/mbedtls/library/x509_crt.o
#flag @VEXEROOT/thirdparty/mbedtls/library/x509_csr.o
#flag @VEXEROOT/thirdparty/mbedtls/library/x509write.o
#flag @VEXEROOT/thirdparty/mbedtls/library/x509write_crt.o
#flag @VEXEROOT/thirdparty/mbedtls/library/x509write_csr.o
#flag @VEXEROOT/thirdparty/mbedtls/3rdparty/everest/library/Hacl_Curve25519_joined.o
#flag @VEXEROOT/thirdparty/mbedtls/3rdparty/everest/library/everest.o
#flag @VEXEROOT/thirdparty/mbedtls/3rdparty/everest/library/x25519.o
#include <mbedtls/net_sockets.h>
#include <mbedtls/ssl.h>
#include <mbedtls/entropy.h>
#include <mbedtls/ctr_drbg.h>
#include <mbedtls/error.h>
#include <mbedtls/threading.h>
#insert "@VEXEROOT/vlib/net/mbedtls/mbedtls_helpers.h"
#insert "@VEXEROOT/vlib/net/mbedtls/mbedtls_threading.h"
// v_mbedtls_threading_setup installs the mutex callbacks mbedtls needs when it
// is built with MBEDTLS_THREADING_ALT (Windows). On platforms that use pthread
// threading or no threading it is a no-op. Defined in mbedtls_threading.h.
fn C.v_mbedtls_threading_setup()
// init installs mbedtls' thread-safety callbacks once, before any TLS use, so
// the library's shared state (RNG, key blinding, internal globals) is safe to
// use across threads. A no-op on non-Windows builds.
fn init() {
C.v_mbedtls_threading_setup()
}
@[typedef]
pub struct C.mbedtls_net_context {
mut:
fd int
}
@[typedef]
pub struct C.mbedtls_ssl_context {}
@[typedef]
pub struct C.mbedtls_ssl_config {}
@[typedef]
pub struct C.mbedtls_ssl_send_t {}
@[typedef]
pub struct C.mbedtls_ssl_recv_t {}
@[typedef]
pub struct C.mbedtls_ssl_recv_timeout_t {}
@[typedef]
pub struct C.mbedtls_pk_context {}
@[typedef]
pub struct C.mbedtls_ctr_drbg_context {}
@[typedef]
pub struct C.mbedtls_entropy_context {}
@[typedef]
pub struct C.mbedtls_x509_crt {}
@[typedef]
pub struct C.mbedtls_x509_crl {}
fn C.mbedtls_net_init(&C.mbedtls_net_context)
fn C.mbedtls_net_connect(&C.mbedtls_net_context, &char, &char, i32) i32
fn C.mbedtls_net_bind(&C.mbedtls_net_context, &char, &char, i32) i32
fn C.mbedtls_net_accept(&C.mbedtls_net_context, &C.mbedtls_net_context, voidptr, usize, &usize) i32
fn C.mbedtls_net_recv(voidptr, &u8, usize) i32
fn C.mbedtls_net_send(voidptr, &u8, usize) i32
fn C.mbedtls_net_recv_timeout(voidptr, &u8, usize, u32) i32
fn C.mbedtls_net_free(&C.mbedtls_net_context)
fn C.mbedtls_ssl_init(&C.mbedtls_ssl_context)
fn C.mbedtls_ssl_setup(&C.mbedtls_ssl_context, &C.mbedtls_ssl_config) i32
fn C.mbedtls_ssl_session_reset(&C.mbedtls_ssl_context)
fn C.mbedtls_ssl_conf_authmode(&C.mbedtls_ssl_config, i32)
fn C.mbedtls_ssl_conf_rng(&C.mbedtls_ssl_config, fn (voidptr, &u8, usize) int, &C.mbedtls_ctr_drbg_context)
fn C.mbedtls_ssl_set_bio(&C.mbedtls_ssl_context, &C.mbedtls_net_context, fn (voidptr, &u8, usize) i32, fn (voidptr, &u8, usize) i32, fn (voidptr, &u8, usize, u32) i32)
fn C.mbedtls_ssl_conf_own_cert(&C.mbedtls_ssl_config, &C.mbedtls_x509_crt, &C.mbedtls_pk_context) i32
fn C.mbedtls_ssl_conf_ca_chain(&C.mbedtls_ssl_config, &C.mbedtls_x509_crt, &C.mbedtls_x509_crl)
fn C.mbedtls_ssl_set_hostname(&C.mbedtls_ssl_context, &char) i32
fn C.mbedtls_ssl_handshake(&C.mbedtls_ssl_context) i32
fn C.mbedtls_ssl_read(&C.mbedtls_ssl_context, &u8, usize) i32
fn C.mbedtls_ssl_write(&C.mbedtls_ssl_context, &u8, usize) i32
fn C.mbedtls_ssl_free(&C.mbedtls_ssl_context)
fn C.mbedtls_ssl_config_init(&C.mbedtls_ssl_config)
fn C.mbedtls_ssl_config_defaults(&C.mbedtls_ssl_config, i32, i32, i32) i32
fn C.mbedtls_ssl_config_free(&C.mbedtls_ssl_config)
fn C.mbedtls_ssl_conf_sni(&C.mbedtls_ssl_config, fn (voidptr, &C.mbedtls_ssl_context, &char, int) int, voidptr)
fn C.mbedtls_ssl_set_hs_ca_chain(&C.mbedtls_ssl_config, &C.mbedtls_x509_crt, &C.mbedtls_x509_crl)
fn C.mbedtls_ssl_set_hs_own_cert(&C.mbedtls_ssl_context, &C.mbedtls_x509_crt, &C.mbedtls_pk_context) i32
fn C.mbedtls_ssl_set_hs_authmode(&C.mbedtls_ssl_context, i32)
fn C.mbedtls_pk_init(&C.mbedtls_pk_context)
fn C.mbedtls_pk_free(&C.mbedtls_pk_context)
fn C.mbedtls_pk_parse_key(&C.mbedtls_pk_context, &u8, usize, &u8, usize, fn (voidptr, &u8, usize) int,
voidptr) i32
fn C.mbedtls_pk_parse_keyfile(&C.mbedtls_pk_context, &char, &char, fn (voidptr, &u8, usize) int, voidptr) i32
fn C.mbedtls_ctr_drbg_init(&C.mbedtls_ctr_drbg_context)
fn C.mbedtls_ctr_drbg_seed(&C.mbedtls_ctr_drbg_context, fn (voidptr, &u8, usize), voidptr, &u8, usize) i32
fn C.mbedtls_ctr_drbg_free(&C.mbedtls_ctr_drbg_context)
fn C.mbedtls_ctr_drbg_random(voidptr, &u8, usize) i32
fn C.mbedtls_entropy_init(&C.mbedtls_entropy_context)
fn C.mbedtls_entropy_free(&C.mbedtls_entropy_context)
fn C.mbedtls_entropy_func(voidptr, &u8, usize)
fn C.mbedtls_x509_crt_init(&C.mbedtls_x509_crt)
fn C.mbedtls_x509_crt_free(&C.mbedtls_x509_crt)
fn C.mbedtls_x509_crt_parse(&C.mbedtls_x509_crt, &u8, usize) i32
fn C.mbedtls_x509_crt_parse_file(&C.mbedtls_x509_crt, &char) i32
fn C.mbedtls_high_level_strerr(i32) &char
fn C.mbedtls_debug_set_threshold(level i32)
fn C.mbedtls_ssl_conf_read_timeout(conf &C.mbedtls_ssl_config, timeout u32)
// protos is `const char **`; declared as voidptr so V emits a clean
// `(void*)` cast and avoids -cstrict nested-pointer const warnings.
fn C.mbedtls_ssl_conf_alpn_protocols(conf &C.mbedtls_ssl_config, protos voidptr) i32
fn C.mbedtls_ssl_get_alpn_protocol(&C.mbedtls_ssl_context) voidptr
fn C.v_mbedtls_ssl_set_bio_nonblocking(&C.mbedtls_ssl_context, &C.mbedtls_net_context)
@@ -0,0 +1,6 @@
// vtest build: !(windows && tinyc) // TODO: fix these by adding declarations for the missing functions in the prebuilt tcc
import net.mbedtls as _
fn test_mbedtls_compiles() {
assert true
}
@@ -0,0 +1,222 @@
// vtest flaky: true
// vtest retry: 3
module main
import net
import net.html
import net.http
import net.mbedtls
import sync.pool
import time
const concurrent_large_response_requests = 6
const concurrent_large_response_workers = 3
const concurrent_large_response_links = 2_000
const concurrent_large_response_chunk_size = 8_192
const concurrent_large_response_test_cert = '-----BEGIN CERTIFICATE-----\nMIIEOTCCAyECFG64Q2g46jZb3kRbDOJWX/BwjSp6MA0GCSqGSIb3DQEBCwUAMEUx\nCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRl\ncm5ldCBXaWRnaXRzIFB0eSBMdGQwIBcNMjMwODAyMTcyOTQyWhgPMjA1MDEyMTcx\nNzI5NDJaMGsxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRQwEgYD\nVQQHDAtMb3MgQW5nZWxlczEdMBsGA1UECgwUQ2F0YWx5c3QgRGV2ZWxvcG1lbnQx\nEjAQBgNVBAMMCWxvY2FsaG9zdDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC\nggIBALqAI4fqUi+QBVWcsXglouLdOML5+w0+1hSR1KdO0Q5XPdQAs/yYWJ+KUkDw\nG++rfy9DUPq7FNRBVurXQkcAtn6gXdllGUSjwUiDo/N4mMOyS/2sufBuaeww7jVi\nrppH+zwP1tUnjRd6khl6bi1Ian9VSzr3Iy9CkXIg1GU4CPXkOydLeoQfepXxWoK1\nOUNwT3VKC/stAfY3j/NIIeiJYkyuRGFCkxn/BUjN+AsXiTugRcYKEFHdIPkOuCXp\nYbhf+lLsczpxCs3rdZG9b/N6mEDCzXTmeHkmsjdPTf+1k5DZZvKzVBBrgdxCgBb7\n5RwjF5v9WmnIc33wWgfJC6FaUzj9NYxYUbPHD+jTz0rJB/jj4u/xJlM/e5NRmXdW\n70pOMKXtWjRSolLOFIPKLY1qs3KMTAZxKKWPDDF7WlMJxMRt7nnnks5yw43Nog4C\njDLk1ZgETnPpLgo3jbmJdIv+OHKTJrBlVvDq7VTyixCoS5G8KoOmyQJhaXG6NwE2\niVhH5JIKgzgCfetfDsnjxqJ/qtrFXPa8FF2TsomD0NK/GZmIcs+9OeVB75Jn5uhF\nfLHScpiTbuu5w3P/LI/MqihLRB6RRNnRzPH8fIg5bYC9b770ta/8GcFRuYE8t+UR\nGtqXJoIKixbDlqV54kal8FQzYzhETf9+NM6Kb/lKEfG/pslvAgMBAAEwDQYJKoZI\nhvcNAQELBQADggEBALI3uNiNO0QE1brA3QYFK+d9ZroB72NrJ0UNkzYHDg2Fc6xg\n4aVVfaxY08+TmKc0JlMOW+pUxeCW/+UBSngdQiR9EE9xm0k0XIrAsy9RXxRvEtPu\nM1VI2h7ayp1Y2BrnQinevTSgtqLRyS1VbOFRl1FiyVvinw2I0KsDdAMNevAPXcOa\nQ8pUgUq6f56DkhocQaj+hxD/uV8HryNxuoSXnPhvfTN3z4YRGzsaWevJ9EYJliOM\n+XugcqfFJ+W7/QCEcAHCL+Bw6OydG5NFORr3p57PXjjcL/uKmxPBrWg2Bz6uT4uR\nMhj0zttiFHLAt9jGfyk6W57UNUja1e1ggftJJhs=\n-----END CERTIFICATE-----\n'
const concurrent_large_response_test_key = '-----BEGIN RSA PRIVATE KEY-----\nMIIJKQIBAAKCAgEAuoAjh+pSL5AFVZyxeCWi4t04wvn7DT7WFJHUp07RDlc91ACz\n/JhYn4pSQPAb76t/L0NQ+rsU1EFW6tdCRwC2fqBd2WUZRKPBSIOj83iYw7JL/ay5\n8G5p7DDuNWKumkf7PA/W1SeNF3qSGXpuLUhqf1VLOvcjL0KRciDUZTgI9eQ7J0t6\nhB96lfFagrU5Q3BPdUoL+y0B9jeP80gh6IliTK5EYUKTGf8FSM34CxeJO6BFxgoQ\nUd0g+Q64JelhuF/6UuxzOnEKzet1kb1v83qYQMLNdOZ4eSayN09N/7WTkNlm8rNU\nEGuB3EKAFvvlHCMXm/1aachzffBaB8kLoVpTOP01jFhRs8cP6NPPSskH+OPi7/Em\nUz97k1GZd1bvSk4wpe1aNFKiUs4Ug8otjWqzcoxMBnEopY8MMXtaUwnExG3ueeeS\nznLDjc2iDgKMMuTVmAROc+kuCjeNuYl0i/44cpMmsGVW8OrtVPKLEKhLkbwqg6bJ\nAmFpcbo3ATaJWEfkkgqDOAJ9618OyePGon+q2sVc9rwUXZOyiYPQ0r8ZmYhyz705\n5UHvkmfm6EV8sdJymJNu67nDc/8sj8yqKEtEHpFE2dHM8fx8iDltgL1vvvS1r/wZ\nwVG5gTy35REa2pcmggqLFsOWpXniRqXwVDNjOERN/340zopv+UoR8b+myW8CAwEA\nAQKCAgEAkcoffF0JOBMOiHlAJhrNtSiX+ZruzNDlCxlgshUjyWEbfQG7sWbqSHUZ\njZflTrqyZqDpyca7Jp2ZM2Vocxa0klIMayfj08trCaOWY3pPeROE4d3HUJMPjEpH\nvEXTFdnVJIOBPgl3+vWfBfm17QIh9j4X3BVbVNNl3WCaiDGAl699Kl+Pe38cFeCh\nD3JZPEWsZ5SlvwjU8sNGbThjAWN8C1NjMuCXG4hGej5Ae3M/nPPR91jgnw4Me4Ut\nIL3K3RVyGqaqAPJjLsu0kWQUArJAGMfvUkXjwVklkaUV5SHtJBs+pdTXjyprTmJR\nvSXWWON5zkAEEJNY7QcZaeKYi96PFLUFI+ciEdnXn74CfSKhgZCBo+OyFZjDWW5R\nNmgAbZTN2RW0z+V54Lg36JfJrmiGs8TN06KwNjFo+iOJCdQnoUSIhTlmMfVbXPah\ntRfQvwqtfqVS9W/jkiGq9yDDqyXx093R/QTM/XqDlWJ2iOJFppOJefGFCWF6Fwll\nVT9povTAGQmXFiAxwFZxWtbFa0i8fP5QG80X6l/gRklSd6ZXAVvcLkaFGqxunDAe\nrYC2jBwHWRpVmbxw880SWRzlAsJXc7M8PQnBTlyX1mFZNnwAJgqplz0BQHQhQh4V\nqNfisUm9smtda+Hr9GBBUxs09ulery3I0lQjsArVxPqPVgUbFPECggEBANqLA5fH\n2LupOBoFH/fK5jixyGdSB8eJvU+XuS8RBBexnzTQApmDHiU7Axa/cKvxAfUgwBpU\n6OIsL6Lq6wowVInBgo7GraACwspGMIP8Z7+A8qDgSWIcpXP21Ny2RW+nukdH8ZnV\nTFtiFxLYU9GRfzSUcqvE0miKfMGP/S9Cqbew00K6CQ2xurLTR2AchfUQZJJIg7eF\nRBoftthXLQ+s1JoiLJX2gqCliFy32RMAUP+pKvKVJmVQh8bxEkoEzTV2eY7eTxsH\nJDH5hD66EZ5bW/nVAMruJ3iKjy3WvjDbnddNAz9IFKrd1RMP9dgSEKuSv/HhqwPe\n1q9Wm6LWZo8BlYcCggEBANp3M14QMcMxRlZE0TiSopi1CaE8OG0C9apToS1dol2s\n4lCsWHVPIC516LMPGU0bmCdtwJey1mgXQEKVxCWHkVhhoCKT/tN53o5qkptrhrXL\npbqmRfoMXI7LwJU+Vqi5fwSPGrSR/IzHwCUL7pHTbYN7wT5rr2rcC84XYSX31TFm\nNfMnbDuUk33ycAo07Vqts5A5FN+xViEUMFSDmfA2XmOAV77awz0l/3n3qOg9lQYe\nU4Av2nT19lGELirLInkB1ndLirWAcLaCBXKOLW4bzpNm9Bt8aiziVzcUzlJlLa+1\nnb/7//xzKi0eM/BhyJfhsmOz5B8AQ6Ca/keDk8M7JtkCggEARl8DDinE6VCpBv/l\ndlX4YgMlQ9fPN3pr4ig58iTpi3Ofj1L3s1TcLSLecMG+Vy9o8PTVxuTWhJWz1SMO\nAh7j6ePM1Yq2N9MLxDRrxOROyASOnCz8lEIjKL8vdc6fdz+sJO3OpzleuAJS6beM\n7euK6XRvpE3hbtZBK9bgsQonOkYPEOp0pds4AgM0dYdZvzrDF7OP7lVUQ5E4wFr5\n4JVHdEZS0wsoru/+g9STaqHscxaXBLvwPCl9Pxs7R2haZ7+5jr6Y/FwFVK5C3ivu\nJm7GpCDpe27KeO8tAZancXYWUlCzHfpo5Ug/Jz85a5UNlyHO+uUuuzVTLeyWew3M\nwnnBGwKCAQEAqGTBP3wUH3TX1p9s9cJxemvxZEra44woeIXF8wX9pV8hgzWVabb4\nA1f3ai31Pq5KdfnvPf8nrUxex/RRIOyCaDG4EW8qOS/zEKutHgef6nly4ZBQ2BC3\nN4pug5ttiNiSw5za5NyyYoGF5ghweA8UlwjJR6gRqri6kL0MsQt7VXyHkUmN787y\ncV5yZiut2PuTMVQOdu5miVDagAqAmdwOnXvMJtzRKU0kw4rWs0zklbbCfkhkh0sf\n9m2AeJPjmoqEGags3wKF3ugR8t8MvZbJgG0XNCiOXtKIj3iGIJTExm+jjNxd0OWk\nWOqy9lMpH4lky91ZtVuqxR0za0RMnWv24QKCAQBe8l0w9AYVNGDLv1jyPcbsncty\nNYI81yqe2mL+TC00sMCeil7C7WCP7kRklY01rH5q5gJ9Q1UV+bOj2fQdXDmQ5Bgo\n41jseh44gkbuXAeWcSDrDkJCrfvlNqFobTmUb8cdb9aQlHYfOJ31367LJspiw2SY\nmCbnLQ5sMnyBiMkcn0GfBV6IAkZVN73DPa8a1m/0Qrrv1GmBJFVbuZd9d/hAWpHa\nekhXPq0Sta+RNDfBR3aI5lAmVA17qRGiubQYJ+Ldq0aRJ40fGE51ctoSU/5RMcmh\n6+Qro+jSC94L46xMFp+1J5atgB1p/jVzTT/Ws7SLyotYUSL8zU7tcLiycQXs\n-----END RSA PRIVATE KEY-----\n'
struct ConcurrentLargeResponseContext {
expected_body string
expected_link_count int
}
struct ConcurrentLargeResponseResult {
status_code int
body_matches bool
link_count int
err string
}
fn test_https_large_bodies_can_be_processed_concurrently() {
// Watchdog thread to prevent the test from hanging indefinitely on CI.
spawn fn () {
time.sleep(120 * time.second)
eprintln('mbedtls concurrent large response test: timeout reached, exiting.')
exit(1)
}()
mut port_listener := net.listen_tcp(.ip, '127.0.0.1:0')!
port := port_listener.addr()!.port()!
port_listener.close()!
mut listener := mbedtls.new_ssl_listener('127.0.0.1:${port}', mbedtls.SSLConnectConfig{
cert: concurrent_large_response_test_cert
cert_key: concurrent_large_response_test_key
validate: false
in_memory_verification: true
})!
server := spawn serve_large_html_responses(mut listener, concurrent_large_response_body(),
concurrent_large_response_requests)
mut urls := []string{cap: concurrent_large_response_requests}
for i in 0 .. concurrent_large_response_requests {
urls << 'https://127.0.0.1:${port}/?req=${i}'
}
expected_body := concurrent_large_response_body()
mut pp := pool.new_pool_processor(callback: concurrent_large_response_worker)
pp.set_max_jobs(concurrent_large_response_workers)
pp.set_shared_context(&ConcurrentLargeResponseContext{
expected_body: expected_body
expected_link_count: concurrent_large_response_links
})
pp.work_on_items(urls)
server.wait()
for result in pp.get_results_ref[ConcurrentLargeResponseResult]() {
assert result.err == ''
assert result.status_code == 200
assert result.body_matches
assert result.link_count == concurrent_large_response_links
}
}
fn test_https_request_timeout_closes_the_connection() {
mut port_listener := net.listen_tcp(.ip, '127.0.0.1:0')!
port := port_listener.addr()!.port()!
port_listener.close()!
mut listener := mbedtls.new_ssl_listener('127.0.0.1:${port}', mbedtls.SSLConnectConfig{
cert: concurrent_large_response_test_cert
cert_key: concurrent_large_response_test_key
validate: false
in_memory_verification: true
})!
server := spawn serve_incomplete_https_response(mut listener)
mut req := http.new_request(.get, 'https://127.0.0.1:${port}', '')
req.read_timeout = 250 * time.millisecond
req.validate = false
req.do() or {
assert server.wait()
return
}
panic('expected the HTTPS request to time out')
}
fn test_https_chunked_body_is_returned_by_http_get_text() {
mut port_listener := net.listen_tcp(.ip, '127.0.0.1:0')!
port := port_listener.addr()!.port()!
port_listener.close()!
mut listener := mbedtls.new_ssl_listener('127.0.0.1:${port}', mbedtls.SSLConnectConfig{
cert: concurrent_large_response_test_cert
cert_key: concurrent_large_response_test_key
validate: false
in_memory_verification: true
})!
expected_body := concurrent_large_response_body()
server := spawn serve_chunked_html_response(mut listener, expected_body)
body := http.get_text('https://127.0.0.1:${port}/chunked')
server.wait()
assert body == expected_body
}
fn concurrent_large_response_body() string {
return '<html><body>' +
'<article><a href="/item">payload</a></article>'.repeat(concurrent_large_response_links) +
'</body></html>'
}
fn serve_incomplete_https_response(mut listener mbedtls.SSLListener) bool {
defer {
listener.shutdown() or {}
}
mut conn := listener.accept() or { panic(err) }
defer {
conn.shutdown() or {}
}
mut request_buf := []u8{len: 2048}
_ = conn.read(mut request_buf) or { return false }
conn.write_string('HTTP/1.1 200 OK\r\nContent-Length: 2\r\n') or { return false }
conn.set_read_timeout(time.second)
mut drain_buf := []u8{len: 128}
_ = conn.read(mut drain_buf) or { return err.code() != net.err_timed_out_code }
return false
}
fn serve_large_html_responses(mut listener mbedtls.SSLListener, body string, request_count int) {
defer {
listener.shutdown() or {}
}
mut handlers := []thread{cap: request_count}
for _ in 0 .. request_count {
mut conn := listener.accept() or { panic(err) }
handlers << spawn write_large_html_response(mut conn, body)
}
handlers.wait()
}
fn serve_chunked_html_response(mut listener mbedtls.SSLListener, body string) {
defer {
listener.shutdown() or {}
}
mut conn := listener.accept() or { panic(err) }
write_chunked_html_response(mut conn, body)
}
fn write_large_html_response(mut conn mbedtls.SSLConn, body string) {
defer {
conn.shutdown() or {}
}
mut request_buf := []u8{len: 2048}
_ = conn.read(mut request_buf) or { return }
header := 'HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nContent-Length: ${body.len}\r\nConnection: close\r\n\r\n'
conn.write_string(header) or { return }
mut start := 0
for start < body.len {
end := if start + concurrent_large_response_chunk_size > body.len {
body.len
} else {
start + concurrent_large_response_chunk_size
}
conn.write_string(body[start..end]) or { return }
start = end
time.sleep(1 * time.millisecond)
}
}
fn write_chunked_html_response(mut conn mbedtls.SSLConn, body string) {
defer {
conn.shutdown() or {}
}
mut request_buf := []u8{len: 2048}
_ = conn.read(mut request_buf) or { return }
header := 'HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nTransfer-Encoding: chunked\r\nConnection: close\r\n\r\n'
conn.write_string(header) or { return }
mut start := 0
for start < body.len {
end := if start + concurrent_large_response_chunk_size > body.len {
body.len
} else {
start + concurrent_large_response_chunk_size
}
chunk := body[start..end]
conn.write_string('${chunk.len:x}\r\n') or { return }
conn.write_string(chunk) or { return }
conn.write_string('\r\n') or { return }
start = end
time.sleep(1 * time.millisecond)
}
conn.write_string('0\r\n\r\n') or { return }
}
fn concurrent_large_response_worker(mut pp pool.PoolProcessor, idx int, _wid int) &ConcurrentLargeResponseResult {
url := pp.get_item[string](idx)
ctx := unsafe { &ConcurrentLargeResponseContext(pp.get_shared_context()) }
resp := http.fetch(
method: .get
url: url
validate: false
) or { return &ConcurrentLargeResponseResult{
err: '${err}'
} }
doc := html.parse(resp.body)
return &ConcurrentLargeResponseResult{
status_code: resp.status_code
body_matches: resp.body == ctx.expected_body
link_count: doc.get_tags(name: 'a').len
}
}
@@ -0,0 +1,77 @@
module main
import net
import net.http
import net.mbedtls
// HTTPS counterpart to vlib/net/http/head_with_content_length_test.v.
// This is the exact path that emitted `received 0 of N bytes` against
// real-world servers (mbedtls `PEER_CLOSE_NOTIFY` → `socket_read_into_ptr`
// returns 0 → `len <= 0` branch in receive_all_data_from_cb_in_builder).
const head_cl_test_cert = '-----BEGIN CERTIFICATE-----\nMIIEOTCCAyECFG64Q2g46jZb3kRbDOJWX/BwjSp6MA0GCSqGSIb3DQEBCwUAMEUx\nCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRl\ncm5ldCBXaWRnaXRzIFB0eSBMdGQwIBcNMjMwODAyMTcyOTQyWhgPMjA1MDEyMTcx\nNzI5NDJaMGsxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRQwEgYD\nVQQHDAtMb3MgQW5nZWxlczEdMBsGA1UECgwUQ2F0YWx5c3QgRGV2ZWxvcG1lbnQx\nEjAQBgNVBAMMCWxvY2FsaG9zdDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC\nggIBALqAI4fqUi+QBVWcsXglouLdOML5+w0+1hSR1KdO0Q5XPdQAs/yYWJ+KUkDw\nG++rfy9DUPq7FNRBVurXQkcAtn6gXdllGUSjwUiDo/N4mMOyS/2sufBuaeww7jVi\nrppH+zwP1tUnjRd6khl6bi1Ian9VSzr3Iy9CkXIg1GU4CPXkOydLeoQfepXxWoK1\nOUNwT3VKC/stAfY3j/NIIeiJYkyuRGFCkxn/BUjN+AsXiTugRcYKEFHdIPkOuCXp\nYbhf+lLsczpxCs3rdZG9b/N6mEDCzXTmeHkmsjdPTf+1k5DZZvKzVBBrgdxCgBb7\n5RwjF5v9WmnIc33wWgfJC6FaUzj9NYxYUbPHD+jTz0rJB/jj4u/xJlM/e5NRmXdW\n70pOMKXtWjRSolLOFIPKLY1qs3KMTAZxKKWPDDF7WlMJxMRt7nnnks5yw43Nog4C\njDLk1ZgETnPpLgo3jbmJdIv+OHKTJrBlVvDq7VTyixCoS5G8KoOmyQJhaXG6NwE2\niVhH5JIKgzgCfetfDsnjxqJ/qtrFXPa8FF2TsomD0NK/GZmIcs+9OeVB75Jn5uhF\nfLHScpiTbuu5w3P/LI/MqihLRB6RRNnRzPH8fIg5bYC9b770ta/8GcFRuYE8t+UR\nGtqXJoIKixbDlqV54kal8FQzYzhETf9+NM6Kb/lKEfG/pslvAgMBAAEwDQYJKoZI\nhvcNAQELBQADggEBALI3uNiNO0QE1brA3QYFK+d9ZroB72NrJ0UNkzYHDg2Fc6xg\n4aVVfaxY08+TmKc0JlMOW+pUxeCW/+UBSngdQiR9EE9xm0k0XIrAsy9RXxRvEtPu\nM1VI2h7ayp1Y2BrnQinevTSgtqLRyS1VbOFRl1FiyVvinw2I0KsDdAMNevAPXcOa\nQ8pUgUq6f56DkhocQaj+hxD/uV8HryNxuoSXnPhvfTN3z4YRGzsaWevJ9EYJliOM\n+XugcqfFJ+W7/QCEcAHCL+Bw6OydG5NFORr3p57PXjjcL/uKmxPBrWg2Bz6uT4uR\nMhj0zttiFHLAt9jGfyk6W57UNUja1e1ggftJJhs=\n-----END CERTIFICATE-----\n'
const head_cl_test_key = '-----BEGIN RSA PRIVATE KEY-----\n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\n-----END RSA PRIVATE KEY-----\n'
fn serve_https_head_response(mut listener mbedtls.SSLListener, response string) {
defer {
listener.shutdown() or {}
}
mut conn := listener.accept() or { return }
defer {
conn.shutdown() or {}
}
mut request_buf := []u8{len: 2048}
_ = conn.read(mut request_buf) or { return }
conn.write_string(response) or { return }
}
fn start_https_server(response string) !int {
mut port_listener := net.listen_tcp(.ip, '127.0.0.1:0')!
port := port_listener.addr()!.port()!
port_listener.close()!
mut listener := mbedtls.new_ssl_listener('127.0.0.1:${port}', mbedtls.SSLConnectConfig{
cert: head_cl_test_cert
cert_key: head_cl_test_key
validate: false
in_memory_verification: true
})!
spawn serve_https_head_response(mut listener, response)
return port
}
fn test_https_head_does_not_wait_for_body_when_content_length_is_set() {
port := start_https_server('HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nContent-Length: 1234\r\nConnection: close\r\n\r\n') or {
assert false, 'failed to start TLS server: ${err}'
return
}
resp := http.fetch(
method: .head
url: 'https://127.0.0.1:${port}/'
validate: false
) or {
assert false, 'HTTPS HEAD should not error, got: ${err}'
return
}
assert resp.status_code == 200
assert resp.body == ''
cl := resp.header.get(.content_length) or { '' }
assert cl == '1234'
}
fn test_https_get_204_with_content_length_returns_empty_body() {
port := start_https_server('HTTP/1.1 204 No Content\r\nContent-Length: 5\r\nConnection: close\r\n\r\n') or {
assert false, 'failed to start TLS server: ${err}'
return
}
resp := http.fetch(
method: .get
url: 'https://127.0.0.1:${port}/'
validate: false
) or {
assert false, 'HTTPS GET 204 should not error, got: ${err}'
return
}
assert resp.status_code == 204
assert resp.body == ''
}
@@ -0,0 +1,9 @@
#ifndef V_NET_MBEDTLS_HELPERS_H
#define V_NET_MBEDTLS_HELPERS_H
static inline void v_mbedtls_ssl_set_bio_nonblocking(mbedtls_ssl_context *ssl, mbedtls_net_context *net)
{
mbedtls_ssl_set_bio(ssl, net, mbedtls_net_send, mbedtls_net_recv, NULL);
}
#endif
@@ -0,0 +1,25 @@
// vtest build: !(windows && tinyc) // TODO: fix these by adding declarations for the missing functions in the prebuilt tcc
module mbedtls
import net
import time
fn test_ssl_conn_read_timeout_can_be_configured_at_runtime() ! {
config := SSLConnectConfig{}
assert config.read_timeout == default_mbedtls_client_read_timeout
mut conn := new_ssl_conn(config)!
assert conn.read_timeout() == default_mbedtls_client_read_timeout
timeout := 5 * time.minute
conn.set_read_timeout(timeout)
assert conn.read_timeout() == timeout
conn.set_read_timeout(net.infinite_timeout)
assert conn.read_timeout() == net.infinite_timeout
}
fn test_ssl_remaining_timeout_clamps_expired_deadlines() {
assert ssl_remaining_timeout(time.unix(0)) == net.infinite_timeout
assert ssl_remaining_timeout(time.now().add(-time.second)) == time.nanosecond
}
@@ -0,0 +1,59 @@
import time
import context
import net.mbedtls
fn server() ! {
cfg := mbedtls.SSLConnectConfig{
cert: '-----BEGIN CERTIFICATE-----\nMIIEOTCCAyECFG64Q2g46jZb3kRbDOJWX/BwjSp6MA0GCSqGSIb3DQEBCwUAMEUx\nCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRl\ncm5ldCBXaWRnaXRzIFB0eSBMdGQwIBcNMjMwODAyMTcyOTQyWhgPMjA1MDEyMTcx\nNzI5NDJaMGsxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRQwEgYD\nVQQHDAtMb3MgQW5nZWxlczEdMBsGA1UECgwUQ2F0YWx5c3QgRGV2ZWxvcG1lbnQx\nEjAQBgNVBAMMCWxvY2FsaG9zdDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC\nggIBALqAI4fqUi+QBVWcsXglouLdOML5+w0+1hSR1KdO0Q5XPdQAs/yYWJ+KUkDw\nG++rfy9DUPq7FNRBVurXQkcAtn6gXdllGUSjwUiDo/N4mMOyS/2sufBuaeww7jVi\nrppH+zwP1tUnjRd6khl6bi1Ian9VSzr3Iy9CkXIg1GU4CPXkOydLeoQfepXxWoK1\nOUNwT3VKC/stAfY3j/NIIeiJYkyuRGFCkxn/BUjN+AsXiTugRcYKEFHdIPkOuCXp\nYbhf+lLsczpxCs3rdZG9b/N6mEDCzXTmeHkmsjdPTf+1k5DZZvKzVBBrgdxCgBb7\n5RwjF5v9WmnIc33wWgfJC6FaUzj9NYxYUbPHD+jTz0rJB/jj4u/xJlM/e5NRmXdW\n70pOMKXtWjRSolLOFIPKLY1qs3KMTAZxKKWPDDF7WlMJxMRt7nnnks5yw43Nog4C\njDLk1ZgETnPpLgo3jbmJdIv+OHKTJrBlVvDq7VTyixCoS5G8KoOmyQJhaXG6NwE2\niVhH5JIKgzgCfetfDsnjxqJ/qtrFXPa8FF2TsomD0NK/GZmIcs+9OeVB75Jn5uhF\nfLHScpiTbuu5w3P/LI/MqihLRB6RRNnRzPH8fIg5bYC9b770ta/8GcFRuYE8t+UR\nGtqXJoIKixbDlqV54kal8FQzYzhETf9+NM6Kb/lKEfG/pslvAgMBAAEwDQYJKoZI\nhvcNAQELBQADggEBALI3uNiNO0QE1brA3QYFK+d9ZroB72NrJ0UNkzYHDg2Fc6xg\n4aVVfaxY08+TmKc0JlMOW+pUxeCW/+UBSngdQiR9EE9xm0k0XIrAsy9RXxRvEtPu\nM1VI2h7ayp1Y2BrnQinevTSgtqLRyS1VbOFRl1FiyVvinw2I0KsDdAMNevAPXcOa\nQ8pUgUq6f56DkhocQaj+hxD/uV8HryNxuoSXnPhvfTN3z4YRGzsaWevJ9EYJliOM\n+XugcqfFJ+W7/QCEcAHCL+Bw6OydG5NFORr3p57PXjjcL/uKmxPBrWg2Bz6uT4uR\nMhj0zttiFHLAt9jGfyk6W57UNUja1e1ggftJJhs=\n-----END CERTIFICATE-----\n'
cert_key: '-----BEGIN RSA PRIVATE KEY-----\n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\n-----END RSA PRIVATE KEY-----\n'
validate: false
in_memory_verification: true
}
mut srv := mbedtls.new_ssl_listener('127.0.0.1:64443', cfg) or {
eprintln('Listen: ${err}')
return err
}
eprintln('[+] Listening')
mut cli := srv.accept() or {
eprintln('Accept: ${err}')
return err
}
eprintln('[+] Accepted connection')
cli.shutdown()!
}
@[if network ?]
fn test_shutdown_does_not_panic() {
_ := spawn server()
time.sleep(1 * time.second)
mut client := mbedtls.new_ssl_conn(validate: false)!
client.dial('127.0.0.1', 64443) or {
eprintln('Connect: ${err}')
return
}
eprintln('[+] Connected')
time.sleep(1 * time.second)
mut background := context.background()
mut ctx, cancel := context.with_timeout(mut background, 2 * time.second)
spawn fn () {
mut i := 0
for {
i++
print('\r${i}...')
flush_stdout()
time.sleep(1 * time.second)
}
}()
mut done := ctx.done()
for {
select {
_ := <-done {
break
}
}
}
eprintln('\nTimeout without panic - OK')
assert true
}
@@ -0,0 +1,25 @@
import os
import rand
const vexe = @VEXE
fn test_mbedtls_compiles_with_tcc_on_arm64_macos() {
$if !(macos && arm64) {
return
}
workdir := os.join_path(os.vtmp_dir(), 'v_mbedtls_tcc_arm64_${rand.ulid()}')
os.mkdir_all(workdir) or { panic(err) }
defer {
os.rmdir_all(workdir) or {}
}
vcache := os.join_path(workdir, 'vcache')
os.mkdir_all(vcache) or { panic(err) }
src := os.join_path(workdir, 'main.v')
out := os.join_path(workdir, 'main')
os.write_file(src, 'import net.mbedtls as _\n\nfn main() {}\n') or { panic(err) }
cmd := 'env VCACHE=${os.quoted_path(vcache)} ${os.quoted_path(vexe)} -nocache -cc tcc -gc none -no-retry-compilation -o ${os.quoted_path(out)} ${os.quoted_path(src)}'
res := os.execute(cmd)
if res.exit_code != 0 {
panic('command failed:\n${cmd}\n${res.output}')
}
}
@@ -0,0 +1,65 @@
/*
* mbedtls_threading.h - Win32 mutex callbacks for MBEDTLS_THREADING_ALT.
*
* On Windows, mbedtls is built with MBEDTLS_THREADING_C + MBEDTLS_THREADING_ALT
* (see thirdparty/mbedtls/include/mbedtls/mbedtls_config.h). The library then
* expects the embedder to supply the four mutex primitives via
* mbedtls_threading_set_alt(). These wrap a Win32 CRITICAL_SECTION; the matching
* mbedtls_threading_mutex_t type is defined in
* thirdparty/mbedtls/include/mbedtls/threading_alt.h.
*
* v_mbedtls_threading_setup() is called once from net.mbedtls' init() before any
* TLS use. On every other platform it is a no-op (pthread threading, or none).
*/
#ifndef V_MBEDTLS_THREADING_H
#define V_MBEDTLS_THREADING_H
#if defined(_WIN32) && defined(MBEDTLS_THREADING_ALT)
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#include <mbedtls/threading.h>
#include <mbedtls/error.h>
static void v_mbedtls_mutex_init(mbedtls_threading_mutex_t *m) {
InitializeCriticalSection(&m->cs);
m->is_valid = 1;
}
static void v_mbedtls_mutex_free(mbedtls_threading_mutex_t *m) {
if (m->is_valid) {
DeleteCriticalSection(&m->cs);
m->is_valid = 0;
}
}
static int v_mbedtls_mutex_lock(mbedtls_threading_mutex_t *m) {
if (!m->is_valid) {
return MBEDTLS_ERR_THREADING_BAD_INPUT_DATA;
}
EnterCriticalSection(&m->cs);
return 0;
}
static int v_mbedtls_mutex_unlock(mbedtls_threading_mutex_t *m) {
if (!m->is_valid) {
return MBEDTLS_ERR_THREADING_BAD_INPUT_DATA;
}
LeaveCriticalSection(&m->cs);
return 0;
}
static void v_mbedtls_threading_setup(void) {
mbedtls_threading_set_alt(v_mbedtls_mutex_init, v_mbedtls_mutex_free,
v_mbedtls_mutex_lock, v_mbedtls_mutex_unlock);
}
#else /* not (Windows + THREADING_ALT) */
static void v_mbedtls_threading_setup(void) { }
#endif
#endif /* V_MBEDTLS_THREADING_H */
+996
View File
@@ -0,0 +1,996 @@
module mbedtls
import io
import net
import time
const mbedtls_client_read_timeout_ms = $d('mbedtls_client_read_timeout_ms', 10_000)
const mbedtls_server_read_timeout_ms = $d('mbedtls_server_read_timeout_ms', 41_000)
const default_mbedtls_client_read_timeout = mbedtls_client_read_timeout_ms * time.millisecond
const default_mbedtls_server_read_timeout = mbedtls_server_read_timeout_ms * time.millisecond
fn init_rng(mut ctr_drbg C.mbedtls_ctr_drbg_context, mut entropy C.mbedtls_entropy_context) ! {
$if trace_ssl ? {
eprintln(@METHOD)
}
C.mbedtls_ctr_drbg_init(&ctr_drbg)
C.mbedtls_entropy_init(&entropy)
ret := C.mbedtls_ctr_drbg_seed(&ctr_drbg, C.mbedtls_entropy_func, &entropy, 0, 0)
if ret != 0 {
C.mbedtls_ctr_drbg_free(&ctr_drbg)
C.mbedtls_entropy_free(&entropy)
return error_with_code('net.mbedtls init_rng, failed to seed ssl context: ${ret}', ret)
}
// C.mbedtls_debug_set_threshold(5)
}
fn free_rng(mut ctr_drbg C.mbedtls_ctr_drbg_context, mut entropy C.mbedtls_entropy_context) {
C.mbedtls_ctr_drbg_free(&ctr_drbg)
C.mbedtls_entropy_free(&entropy)
}
// SSLCerts represents a pair of CA and client certificates + key
pub struct SSLCerts {
pub mut:
cacert C.mbedtls_x509_crt
client_cert C.mbedtls_x509_crt
client_key C.mbedtls_pk_context
}
// new_sslcerts initializes and returns a pair of SSL certificates and key
pub fn new_sslcerts() &SSLCerts {
mut certs := SSLCerts{}
C.mbedtls_x509_crt_init(&certs.cacert)
C.mbedtls_x509_crt_init(&certs.client_cert)
C.mbedtls_pk_init(&certs.client_key)
return &certs
}
// new_sslcerts_in_memory creates a pair of SSL certificates, given their contents (not paths).
pub fn new_sslcerts_in_memory(verify string, cert string, cert_key string) !&SSLCerts {
mut ctr_drbg := C.mbedtls_ctr_drbg_context{}
mut entropy := C.mbedtls_entropy_context{}
init_rng(mut ctr_drbg, mut entropy)!
defer {
free_rng(mut ctr_drbg, mut entropy)
}
return new_sslcerts_in_memory_with_rng(verify, cert, cert_key, &ctr_drbg)
}
fn new_sslcerts_in_memory_with_rng(verify string, cert string, cert_key string, rng &C.mbedtls_ctr_drbg_context) !&SSLCerts {
mut certs := new_sslcerts()
if verify != '' {
ret := C.mbedtls_x509_crt_parse(&certs.cacert, verify.str, verify.len + 1)
if ret != 0 {
return error_with_code('net.mbedtls new_sslcerts_in_memory, mbedtls_x509_crt_parse error 1 ret: ${ret}',
ret)
}
}
if cert != '' {
ret := C.mbedtls_x509_crt_parse(&certs.client_cert, cert.str, cert.len + 1)
if ret != 0 {
return error_with_code('net.mbedtls new_sslcerts_in_memory, mbedtls_x509_crt_parse error 2 ret: ${ret}',
ret)
}
}
if cert_key != '' {
unsafe {
ret := C.mbedtls_pk_parse_key(&certs.client_key, cert_key.str, cert_key.len + 1, 0, 0,
C.mbedtls_ctr_drbg_random, rng)
if ret != 0 {
return error_with_code('net.mbedtls new_sslcerts_in_memory, mbedtls_pk_parse_key error ret: ${ret}',
ret)
}
}
}
return certs
}
// new_sslcerts_from_file creates a new pair of SSL certificates, given their paths on the filesystem.
pub fn new_sslcerts_from_file(verify string, cert string, cert_key string) !&SSLCerts {
mut ctr_drbg := C.mbedtls_ctr_drbg_context{}
mut entropy := C.mbedtls_entropy_context{}
init_rng(mut ctr_drbg, mut entropy)!
defer {
free_rng(mut ctr_drbg, mut entropy)
}
return new_sslcerts_from_file_with_rng(verify, cert, cert_key, &ctr_drbg)
}
fn new_sslcerts_from_file_with_rng(verify string, cert string, cert_key string, rng &C.mbedtls_ctr_drbg_context) !&SSLCerts {
mut certs := new_sslcerts()
if verify != '' {
ret := C.mbedtls_x509_crt_parse_file(&certs.cacert, &char(verify.str))
if ret != 0 {
return error_with_code('net.mbedtls new_sslcerts_from_file, mbedtls_x509_crt_parse_file error 1 ret: ${ret}',
ret)
}
}
if cert != '' {
ret := C.mbedtls_x509_crt_parse_file(&certs.client_cert, &char(cert.str))
if ret != 0 {
return error_with_code('net.mbedtls new_sslcerts_from_file, mbedtls_x509_crt_parse_file error 2 ret: ${ret}',
ret)
}
}
if cert_key != '' {
unsafe {
ret := C.mbedtls_pk_parse_keyfile(&certs.client_key, &char(cert_key.str), 0,
C.mbedtls_ctr_drbg_random, rng)
if ret != 0 {
return error_with_code('net.mbedtls new_sslcerts_from_file, mbedtls_pk_parse_keyfile error ret: ${ret}',
ret)
}
}
}
return certs
}
// cleanup frees the SSL certificates
pub fn (mut c SSLCerts) cleanup() {
C.mbedtls_x509_crt_free(&c.cacert)
C.mbedtls_x509_crt_free(&c.client_cert)
C.mbedtls_pk_free(&c.client_key)
}
// SSLConn is the current connection
pub struct SSLConn {
pub:
config SSLConnectConfig
pub mut:
server_fd C.mbedtls_net_context
ssl C.mbedtls_ssl_context
conf C.mbedtls_ssl_config
certs &SSLCerts = unsafe { nil }
ctr_drbg C.mbedtls_ctr_drbg_context
entropy C.mbedtls_entropy_context
handle int
duration time.Duration
opened bool
ip string
read_timeout time.Duration
owns_socket bool
// alpn_list is a NUL-terminated C array of pointers to the protocol
// strings in config.alpn_protocols. mbedtls stores this pointer without
// copying, so it must outlive the SSL config; it is freed in shutdown().
alpn_list &&char = unsafe { nil }
// last_write_sent reports the most recent write_ptr's progress for retry
// decisions: 0 = provably nothing was sent (safe to replay), or -1 = the
// count is indeterminate because a failed/retryable write may have already
// flushed a record to the peer (TLS cannot prove zero). On full success it
// equals the bytes written.
last_write_sent int
}
// SSLListener listens on a TCP port and accepts connection secured with TLS
pub struct SSLListener {
saddr string
config SSLConnectConfig
mut:
server_fd C.mbedtls_net_context
ssl C.mbedtls_ssl_context
conf C.mbedtls_ssl_config
certs &SSLCerts = unsafe { nil }
ctr_drbg C.mbedtls_ctr_drbg_context
entropy C.mbedtls_entropy_context
opened bool
// alpn_list is a NUL-terminated C array of pointers to the protocol
// strings in config.alpn_protocols, advertised by accepted connections.
// It must outlive the SSL config and is freed in shutdown().
alpn_list &&char = unsafe { nil }
// handle int
// duration time.Duration
}
// create a new SSLListener binding to `saddr`
pub fn new_ssl_listener(saddr string, config SSLConnectConfig) !&SSLListener {
mut listener := &SSLListener{
saddr: saddr
config: config
}
listener.init()!
listener.opened = true
return listener
}
// finish the listener and clean up resources
pub fn (mut l SSLListener) shutdown() ! {
$if trace_ssl ? {
eprintln(@METHOD)
}
if unsafe { l.certs != nil } {
l.certs.cleanup()
}
C.mbedtls_ssl_free(&l.ssl)
C.mbedtls_ssl_config_free(&l.conf)
free_rng(mut l.ctr_drbg, mut l.entropy)
if l.alpn_list != unsafe { nil } {
unsafe {
C.free(l.alpn_list)
l.alpn_list = nil
}
}
if l.opened {
C.mbedtls_net_free(&l.server_fd)
}
}
// internal function to init and bind the listener
fn (mut l SSLListener) init() ! {
$if trace_ssl ? {
eprintln(@METHOD)
}
lhost, lport := net.split_address(l.saddr)!
if l.config.cert == '' || l.config.cert_key == '' {
return error('net.mbedtls SSLListener.init, no certificate or key provided')
}
if l.config.validate && l.config.verify == '' {
return error('net.mbedtls SSLListener.init, no root CA provided')
}
C.mbedtls_net_init(&l.server_fd)
C.mbedtls_ssl_init(&l.ssl)
C.mbedtls_ssl_config_init(&l.conf)
init_rng(mut l.ctr_drbg, mut l.entropy)!
l.certs = &SSLCerts{}
C.mbedtls_x509_crt_init(&l.certs.client_cert)
C.mbedtls_pk_init(&l.certs.client_key)
unsafe {
C.mbedtls_ssl_conf_rng(&l.conf, C.mbedtls_ctr_drbg_random, &l.ctr_drbg)
}
mut ret := 0
if l.config.in_memory_verification {
l.certs = new_sslcerts_in_memory_with_rng(l.config.verify, l.config.cert,
l.config.cert_key, &l.ctr_drbg) or {
return error('net.mbedtls SSLListener.init, cert failure 1, err: ${err}')
}
} else {
l.certs = new_sslcerts_from_file_with_rng(l.config.verify, l.config.cert,
l.config.cert_key, &l.ctr_drbg) or {
return error('net.mbedtls SSLListener.init, cert failure 2, err: ${err}')
}
}
if l.config.validate {
C.mbedtls_ssl_conf_authmode(&l.conf, C.MBEDTLS_SSL_VERIFY_REQUIRED)
}
mut bind_ip := unsafe { nil }
if lhost != '' {
bind_ip = voidptr(lhost.str)
}
bind_port := lport.str()
ret = C.mbedtls_net_bind(&l.server_fd, bind_ip, voidptr(bind_port.str), C.MBEDTLS_NET_PROTO_TCP)
if ret != 0 {
return error_with_code("net.mbedtls SSLListener.init, mbedtls_net_bind can't bind to ${l.saddr} error ret: ${ret}",
ret)
}
ret = C.mbedtls_ssl_config_defaults(&l.conf, C.MBEDTLS_SSL_IS_SERVER,
C.MBEDTLS_SSL_TRANSPORT_STREAM, C.MBEDTLS_SSL_PRESET_DEFAULT)
if ret != 0 {
return error_with_code("net.mbedtls SSLListener.init, mbedtls_ssl_config_defaults can't set config defaults ret: ${ret}",
ret)
}
listener_read_timeout := ssl_listener_read_timeout(l.config)
$if trace_mbedtls_timeouts ? {
dump(listener_read_timeout)
}
C.mbedtls_ssl_conf_read_timeout(&l.conf, ssl_read_timeout_ms(listener_read_timeout))
C.mbedtls_ssl_conf_ca_chain(&l.conf, &l.certs.cacert, unsafe { nil })
ret = C.mbedtls_ssl_conf_own_cert(&l.conf, &l.certs.client_cert, &l.certs.client_key)
if ret != 0 {
return error_with_code("net.mbedtls SSLListener.init, mbedtls_ssl_conf_own_cert can't load certificate ret: ${ret}",
ret)
}
// Advertise ALPN protocols for accepted connections to select from.
// See the matching client-side logic in SSLConn.init for lifetime notes.
if l.config.alpn_protocols.len > 0 {
n := l.config.alpn_protocols.len
l.alpn_list = unsafe { &&char(C.malloc(isize((n + 1) * int(sizeof(voidptr))))) }
if l.alpn_list == unsafe { nil } {
return error('net.mbedtls SSLListener.init, failed to allocate ALPN list')
}
unsafe {
for i, proto in l.config.alpn_protocols {
l.alpn_list[i] = &char(proto.str)
}
l.alpn_list[n] = &char(0)
}
ret = C.mbedtls_ssl_conf_alpn_protocols(&l.conf, voidptr(l.alpn_list))
if ret != 0 {
return error_with_code('net.mbedtls SSLListener.init, mbedtls_ssl_conf_alpn_protocols failed ret: ${ret}',
ret)
}
}
ret = C.mbedtls_ssl_setup(&l.ssl, &l.conf)
if ret != 0 {
return error_with_code("net.mbedtls SSLListener.init, mbedtls_ssl_setup can't setup ssl ret: ${ret}",
ret)
}
if get_cert_callback := l.config.get_certificate {
l.init_sni(get_cert_callback)
}
}
// setup SNI callback
fn (mut l SSLListener) init_sni(get_cert_callback fn (mut SSLListener, string) !&SSLCerts) {
$if trace_ssl ? {
eprintln(@METHOD)
}
C.mbedtls_ssl_conf_sni(&l.conf, fn [get_cert_callback, mut l] (p_info voidptr, ssl &C.mbedtls_ssl_context, name &char, lng int) int {
host := unsafe { name.vstring_literal_with_len(lng) }
if certs := get_cert_callback(mut l, host) {
return C.mbedtls_ssl_set_hs_own_cert(ssl, &certs.client_cert, &certs.client_key)
} else {
return -1
}
}, &l.conf)
}
// accepts a new connection and returns a SSLConn of the connected client
pub fn (mut l SSLListener) accept() !&SSLConn {
mut conn := l.accept_tcp_connection()!
C.mbedtls_ssl_init(&conn.ssl)
C.mbedtls_ssl_config_init(&conn.conf)
ret := C.mbedtls_ssl_setup(&conn.ssl, &l.conf)
if ret != 0 {
conn.shutdown() or {}
return error_with_code('net.mbedtls SSLListener.accept, mbedtls_ssl_setup SSL setup failed ret: ${ret}',
ret)
}
C.mbedtls_ssl_set_bio(&conn.ssl, &conn.server_fd, C.mbedtls_net_send, C.mbedtls_net_recv,
C.mbedtls_net_recv_timeout)
conn.server_handshake(net.infinite_timeout)!
return conn
}
fn (mut l SSLListener) accept_tcp_connection() !&SSLConn {
mut conn := &SSLConn{
config: l.config
duration: ssl_listener_read_timeout(l.config)
read_timeout: ssl_listener_read_timeout(l.config)
opened: true
}
ip := [16]u8{}
iplen := usize(0)
ret := C.mbedtls_net_accept(&l.server_fd, &conn.server_fd, &ip, 16, &iplen)
if ret != 0 {
return error_with_code("net.mbedtls SSLListener.accept, mbedtls_net_accept can't accept connection ret: ${ret}",
ret)
}
conn.handle = conn.server_fd.fd
conn.owns_socket = true
if iplen == 4 {
conn.ip = '${ip[0]}.${ip[1]}.${ip[2]}.${ip[3]}'
}
return conn
}
fn (mut conn SSLConn) server_handshake(timeout time.Duration) ! {
deadline := ssl_timeout_deadline(timeout)
mut ret := C.mbedtls_ssl_handshake(&conn.ssl)
for ret != 0 {
match ret {
C.MBEDTLS_ERR_SSL_WANT_READ {
conn.wait_for_read(ssl_remaining_timeout(deadline)) or {
conn.shutdown() or {}
return err
}
}
C.MBEDTLS_ERR_SSL_WANT_WRITE {
conn.wait_for_write(ssl_remaining_timeout(deadline)) or {
conn.shutdown() or {}
return err
}
}
else {
conn.shutdown() or {
$if trace_ssl ? {
eprintln('${@METHOD} shutdown ---> res: ${err}')
}
}
return error_with_code('net.mbedtls SSLListener.accept, mbedtls_ssl_handshake failed 1; handshake ret: ${ret}',
ret)
}
}
ret = C.mbedtls_ssl_handshake(&conn.ssl)
}
}
// accept_with_timeout waits up to `timeout` for a new client before accepting it.
pub fn (mut l SSLListener) accept_with_timeout(timeout time.Duration) !&SSLConn {
return l.accept_with_timeouts(timeout, timeout)
}
// accept_with_timeouts waits up to `accept_timeout` for a new client, then
// waits up to `handshake_timeout` for the TLS server handshake to complete.
pub fn (mut l SSLListener) accept_with_timeouts(accept_timeout time.Duration, handshake_timeout time.Duration) !&SSLConn {
wait_for(l.server_fd.fd, .read, accept_timeout)!
mut conn := l.accept_tcp_connection()!
C.mbedtls_ssl_init(&conn.ssl)
C.mbedtls_ssl_config_init(&conn.conf)
net.set_blocking(conn.handle, false) or {
conn.shutdown() or {}
return err
}
ret := C.mbedtls_ssl_setup(&conn.ssl, &l.conf)
if ret != 0 {
conn.shutdown() or {}
return error_with_code('net.mbedtls SSLListener.accept, mbedtls_ssl_setup SSL setup failed ret: ${ret}',
ret)
}
C.v_mbedtls_ssl_set_bio_nonblocking(&conn.ssl, &conn.server_fd)
conn.server_handshake(handshake_timeout)!
net.set_blocking(conn.handle, true) or {
conn.shutdown() or {}
return err
}
C.mbedtls_ssl_set_bio(&conn.ssl, &conn.server_fd, C.mbedtls_net_send, C.mbedtls_net_recv,
C.mbedtls_net_recv_timeout)
return conn
}
@[params]
pub struct SSLConnectConfig {
pub:
verify string // the path to a rootca.pem file, containing trusted CA certificate(s)
cert string // the path to a cert.pem file, containing client certificate(s) for the request
cert_key string // the path to a key.pem file, containing private keys for the client certificate(s)
validate bool // set this to true, if you want to stop requests, when their certificates are found to be invalid
in_memory_verification bool // if true, verify, cert, and cert_key are read from memory, not from a file
get_certificate ?fn (mut SSLListener, string) !&SSLCerts
read_timeout time.Duration = default_mbedtls_client_read_timeout // the SSL client read timeout
alpn_protocols []string // the list of ALPN protocols to advertise, e.g. ['h2', 'http/1.1']; empty means no ALPN extension is sent
}
fn ssl_read_timeout_ms(timeout time.Duration) u32 {
if timeout <= 0 || timeout == net.infinite_timeout {
return 0
}
timeout_ms := timeout.milliseconds()
if timeout_ms > i64(max_u32) {
return max_u32
}
return u32(timeout_ms)
}
fn ssl_listener_read_timeout(config SSLConnectConfig) time.Duration {
if config.read_timeout == default_mbedtls_client_read_timeout {
return default_mbedtls_server_read_timeout
}
return config.read_timeout
}
fn ssl_timeout_deadline(timeout time.Duration) time.Time {
if timeout <= 0 || timeout == net.infinite_timeout {
return time.unix(0)
}
return time.now().add(timeout)
}
fn ssl_remaining_timeout(deadline time.Time) time.Duration {
if deadline.unix() == 0 {
return net.infinite_timeout
}
remaining := deadline - time.now()
if remaining <= 0 {
return time.nanosecond
}
return remaining
}
// read_timeout returns the current SSL read timeout.
pub fn (s &SSLConn) read_timeout() time.Duration {
return s.read_timeout
}
// set_read_timeout sets the SSL read timeout for subsequent operations.
pub fn (mut s SSLConn) set_read_timeout(timeout time.Duration) {
s.read_timeout = timeout
s.duration = timeout
C.mbedtls_ssl_conf_read_timeout(&s.conf, ssl_read_timeout_ms(timeout))
}
// new_ssl_conn returns a new SSLConn with the given config.
pub fn new_ssl_conn(config SSLConnectConfig) !&SSLConn {
$if trace_ssl ? {
eprintln(@METHOD)
}
mut conn := &SSLConn{
config: config
duration: config.read_timeout
read_timeout: config.read_timeout
}
conn.init()!
return conn
}
// Select operation
enum Select {
read
write
except
}
// close terminates the ssl connection and does cleanup
pub fn (mut s SSLConn) close() ! {
s.shutdown()!
}
// shutdown terminates the ssl connection and does cleanup
pub fn (mut s SSLConn) shutdown() ! {
$if trace_ssl ? {
eprintln(@METHOD)
}
if !s.opened {
return error('net.mbedtls SSLConn.shutdown, connection was not open')
}
if unsafe { s.certs != nil } {
C.mbedtls_x509_crt_free(&s.certs.cacert)
C.mbedtls_x509_crt_free(&s.certs.client_cert)
C.mbedtls_pk_free(&s.certs.client_key)
}
C.mbedtls_ssl_free(&s.ssl)
C.mbedtls_ssl_config_free(&s.conf)
free_rng(mut s.ctr_drbg, mut s.entropy)
if s.alpn_list != unsafe { nil } {
unsafe {
C.free(s.alpn_list)
s.alpn_list = nil
}
}
if s.owns_socket {
net.shutdown(s.handle)
net.close(s.handle)!
}
}
// negotiated_alpn returns the ALPN protocol selected during the TLS
// handshake (e.g. 'h2' or 'http/1.1'), or an empty string if no protocol
// was negotiated.
pub fn (s &SSLConn) negotiated_alpn() string {
// mbedtls_ssl_get_alpn_protocol returns a `const char *`; cast away const
// for V, since we only read from it (and copy it below).
p := &char(C.mbedtls_ssl_get_alpn_protocol(&s.ssl))
if p == unsafe { nil } {
return ''
}
return unsafe { cstring_to_vstring(p) }
}
// connect to server using mbedtls
fn (mut s SSLConn) init() ! {
$if trace_ssl ? {
eprintln(@METHOD)
}
C.mbedtls_net_init(&s.server_fd)
C.mbedtls_ssl_init(&s.ssl)
C.mbedtls_ssl_config_init(&s.conf)
init_rng(mut s.ctr_drbg, mut s.entropy)!
mut ret := 0
ret = C.mbedtls_ssl_config_defaults(&s.conf, C.MBEDTLS_SSL_IS_CLIENT,
C.MBEDTLS_SSL_TRANSPORT_STREAM, C.MBEDTLS_SSL_PRESET_DEFAULT)
if ret != 0 {
return error_with_code('net.mbedtls SSLConn.init, mbedtls_ssl_config_defaults failed to set SSL configuration ret: ${ret}',
ret)
}
$if trace_mbedtls_timeouts ? {
dump(s.read_timeout)
}
s.set_read_timeout(s.read_timeout)
unsafe {
C.mbedtls_ssl_conf_rng(&s.conf, C.mbedtls_ctr_drbg_random, &s.ctr_drbg)
}
// Advertise ALPN protocols (e.g. ['h2', 'http/1.1']) when requested.
// mbedtls expects a NUL-terminated array of NUL-terminated C strings, and
// keeps the pointer without copying, so both the array and the backing
// strings must outlive the config. The strings live in s.config; the array
// is allocated here and freed in shutdown().
if s.config.alpn_protocols.len > 0 {
n := s.config.alpn_protocols.len
s.alpn_list = unsafe { &&char(C.malloc(isize((n + 1) * int(sizeof(voidptr))))) }
if s.alpn_list == unsafe { nil } {
return error('net.mbedtls SSLConn.init, failed to allocate ALPN list')
}
unsafe {
for i, proto in s.config.alpn_protocols {
s.alpn_list[i] = &char(proto.str)
}
s.alpn_list[n] = &char(0)
}
ret = C.mbedtls_ssl_conf_alpn_protocols(&s.conf, voidptr(s.alpn_list))
if ret != 0 {
return error_with_code('net.mbedtls SSLConn.init, mbedtls_ssl_conf_alpn_protocols failed ret: ${ret}',
ret)
}
}
if s.config.verify != '' || s.config.cert != '' || s.config.cert_key != '' {
s.certs = &SSLCerts{}
C.mbedtls_x509_crt_init(&s.certs.cacert)
C.mbedtls_x509_crt_init(&s.certs.client_cert)
C.mbedtls_pk_init(&s.certs.client_key)
}
if s.config.in_memory_verification {
if s.config.verify != '' {
ret = C.mbedtls_x509_crt_parse(&s.certs.cacert, s.config.verify.str,
s.config.verify.len + 1)
}
if s.config.cert != '' {
ret = C.mbedtls_x509_crt_parse(&s.certs.client_cert, s.config.cert.str,
s.config.cert.len + 1)
}
if s.config.cert_key != '' {
unsafe {
ret = C.mbedtls_pk_parse_key(&s.certs.client_key, s.config.cert_key.str,
s.config.cert_key.len + 1, 0, 0, C.mbedtls_ctr_drbg_random, &s.ctr_drbg)
}
}
} else {
if s.config.verify != '' {
ret = C.mbedtls_x509_crt_parse_file(&s.certs.cacert, &char(s.config.verify.str))
}
if s.config.cert != '' {
ret = C.mbedtls_x509_crt_parse_file(&s.certs.client_cert, &char(s.config.cert.str))
}
if s.config.cert_key != '' {
unsafe {
ret = C.mbedtls_pk_parse_keyfile(&s.certs.client_key, &char(s.config.cert_key.str),
0, C.mbedtls_ctr_drbg_random, &s.ctr_drbg)
}
}
}
if ret < 0 {
return error_with_code('net.mbedtls SSLConn.init, failed to set certificates, ret: ${ret}',
ret)
}
if unsafe { s.certs != nil } {
C.mbedtls_ssl_conf_ca_chain(&s.conf, &s.certs.cacert, 0)
C.mbedtls_ssl_conf_own_cert(&s.conf, &s.certs.client_cert, &s.certs.client_key)
}
if s.config.validate {
C.mbedtls_ssl_conf_authmode(&s.conf, C.MBEDTLS_SSL_VERIFY_REQUIRED)
} else {
C.mbedtls_ssl_conf_authmode(&s.conf, C.MBEDTLS_SSL_VERIFY_OPTIONAL)
}
ret = C.mbedtls_ssl_setup(&s.ssl, &s.conf)
if ret != 0 {
return error_with_code('net.mbedtls SSLConn.init, mbedtls_ssl_setup failed to setup SSL connection ret: ${ret}',
ret)
}
}
// connect sets up an ssl connection on an existing TCP connection
pub fn (mut s SSLConn) connect(mut tcp_conn net.TcpConn, hostname string) ! {
$if trace_ssl ? {
eprintln('${@METHOD} hostname: ${hostname}')
}
if s.opened {
return error('net.mbedtls SSLConn.connect, ssl connection was already open')
}
s.handle = tcp_conn.sock.handle
s.set_read_timeout(tcp_conn.read_timeout())
mut ret := C.mbedtls_ssl_set_hostname(&s.ssl, &char(hostname.str))
if ret != 0 {
return error_with_code('net.mbedtls SSLConn.connect, mbedtls_ssl_set_hostname failed to set hostname',
ret)
}
s.server_fd.fd = s.handle
C.mbedtls_ssl_set_bio(&s.ssl, &s.server_fd, C.mbedtls_net_send, C.mbedtls_net_recv,
C.mbedtls_net_recv_timeout)
ret = C.mbedtls_ssl_handshake(&s.ssl)
if ret != 0 {
return error_with_code('net.mbedtls SSLConn.connect, mbedtls_ssl_handshake failed 2; ret: ${ret}',
ret)
}
s.opened = true
}
// dial opens an ssl connection on hostname:port
pub fn (mut s SSLConn) dial(hostname string, port int) ! {
$if trace_ssl ? {
eprintln('${@METHOD} hostname: ${hostname} | port: ${port}')
}
if s.opened {
return error('net.mbedtls SSLConn.dial, the ssl connection was already open')
}
mut connected := false
defer {
if !connected {
if unsafe { s.certs != nil } {
C.mbedtls_x509_crt_free(&s.certs.cacert)
C.mbedtls_x509_crt_free(&s.certs.client_cert)
C.mbedtls_pk_free(&s.certs.client_key)
s.certs = unsafe { nil }
}
C.mbedtls_net_free(&s.server_fd)
C.mbedtls_ssl_free(&s.ssl)
C.mbedtls_ssl_config_free(&s.conf)
free_rng(mut s.ctr_drbg, mut s.entropy)
s.handle = 0
s.owns_socket = false
}
}
s.owns_socket = true
mut ret := C.mbedtls_ssl_set_hostname(&s.ssl, &char(hostname.str))
if ret != 0 {
return error_with_code('net.mbedtls SSLConn.dial, failed to set hostname', ret)
}
port_str := port.str()
ret = C.mbedtls_net_connect(&s.server_fd, &char(hostname.str), &char(port_str.str),
C.MBEDTLS_NET_PROTO_TCP)
if ret != 0 {
return error_with_code('net.mbedtls SSLConn.dial, failed to connect to host', ret)
}
C.mbedtls_ssl_set_bio(&s.ssl, &s.server_fd, C.mbedtls_net_send, C.mbedtls_net_recv,
C.mbedtls_net_recv_timeout)
s.handle = s.server_fd.fd
ret = C.mbedtls_ssl_handshake(&s.ssl)
if ret != 0 {
return error_with_code('net.mbedtls SSLConn.dial, mbedtls_ssl_handshake failed 3; ret: ${ret}',
ret)
}
s.opened = true
connected = true
}
// addr retrieves the local ip address and port number for this connection
pub fn (s &SSLConn) addr() !net.Addr {
return net.addr_from_socket_handle(s.handle)
}
// peer_addr retrieves the ip address and port number used by the peer
pub fn (s &SSLConn) peer_addr() !net.Addr {
return net.peer_addr_from_socket_handle(s.handle)
}
// socket_read_into_ptr reads `len` bytes into `buf`
pub fn (mut s SSLConn) socket_read_into_ptr(buf_ptr &u8, len int) !int {
mut res := 0
$if trace_ssl ? {
defer(fn) {
if len > 0 {
eprintln('${@METHOD} res: ${res}: buf_ptr: ${voidptr(buf_ptr):x}, len: ${len}, hex: ${unsafe { buf_ptr.vbytes(len).hex() }} data: `${unsafe { buf_ptr.vstring_with_len(len) }}`')
}
}
}
deadline := ssl_timeout_deadline(s.duration)
// s.wait_for_read(deadline - time.now())!
for {
res = C.mbedtls_ssl_read(&s.ssl, buf_ptr, len)
if res > 0 {
return res
} else if res == 0 {
$if trace_ssl ? {
eprintln('${@METHOD} ---> res: io.Eof')
}
return io.Eof{}
} else {
match res {
C.MBEDTLS_ERR_SSL_WANT_READ {
s.wait_for_read(ssl_remaining_timeout(deadline)) or {
$if trace_ssl ? {
eprintln('${@METHOD} ---> res: ${err}, C.MBEDTLS_ERR_SSL_WANT_READ')
}
return err
}
}
C.MBEDTLS_ERR_SSL_WANT_WRITE {
s.wait_for_write(ssl_remaining_timeout(deadline)) or {
$if trace_ssl ? {
eprintln('${@METHOD} ---> res: ${err}, C.MBEDTLS_ERR_SSL_WANT_WRITE')
}
return err
}
}
C.MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET {
// TLS 1.3 servers can deliver tickets asynchronously while the
// connection is otherwise healthy. Keep reading application data.
continue
}
C.MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY {
$if trace_ssl ? {
eprintln('${@METHOD} ---> res: 0 C.MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY')
}
return 0
}
C.MBEDTLS_ERR_SSL_TIMEOUT {
$if trace_ssl ? {
eprintln('${@METHOD} ---> res: C.MBEDTLS_ERR_SSL_TIMEOUT')
}
return error_with_code('net.mbedtls SSLConn.socket_read_into_ptr, did not receive any data within ${s.read_timeout.milliseconds()}ms. Use conn.set_read_timeout(...) to increase the timeout',
res)
}
else {
$if trace_ssl ? {
eprintln('${@METHOD} ---> res: could not read using SSL')
}
return error_with_code('net.mbedtls SSLConn.socket_read_into_ptr, could not read using SSL',
res)
}
}
}
}
// Dead code, just to satisfy the compiler:
return error('net.mbedtls SSLConn.socket_read_into_ptr, unknown error')
}
// read reads data from the ssl connection into `buffer`
pub fn (mut s SSLConn) read(mut buffer []u8) !int {
$if trace_ssl ? {
eprintln('${@METHOD} buffer.len: ${buffer.len}')
}
return s.socket_read_into_ptr(&u8(buffer.data), buffer.len)
}
// write_ptr writes `len` bytes from `bytes` to the ssl connection
pub fn (mut s SSLConn) write_ptr(bytes &u8, len int) !int {
mut total_sent := 0
$if trace_ssl ? {
defer(fn) {
eprintln('${@METHOD} total_sent: ${total_sent}, bytes: ${voidptr(bytes):x}, len: ${len}, hex: ${unsafe { bytes.vbytes(len).hex() }}, data:-=-=-=-\n${unsafe { bytes.vstring_with_len(len) }}\n-=-=-=-')
}
}
s.last_write_sent = 0
deadline := ssl_timeout_deadline(s.duration)
unsafe {
mut ptr_base := bytes
for total_sent < len {
ptr := ptr_base + total_sent
remaining := len - total_sent
mut sent := C.mbedtls_ssl_write(&s.ssl, ptr, remaining)
if sent <= 0 {
// The write did not fully complete; a retryable error can leave a
// record partially flushed, so the sent count is no longer
// provable. Mark it indeterminate (a later full success below
// resets it to the exact length).
s.last_write_sent = -1
match sent {
C.MBEDTLS_ERR_SSL_WANT_READ {
s.wait_for_read(ssl_remaining_timeout(deadline))!
continue
}
C.MBEDTLS_ERR_SSL_WANT_WRITE {
s.wait_for_write(ssl_remaining_timeout(deadline))!
continue
}
else {
$if trace_ssl ? {
eprintln('${@METHOD} ---> res: could not write SSL, sent: ${sent}')
}
return error_with_code('net.mbedtls SSLConn.write_ptr, could not write using SSL',
sent)
}
}
}
total_sent += sent
s.last_write_sent = total_sent
}
}
return total_sent
}
// write writes data from `bytes` to the ssl connection
pub fn (mut s SSLConn) write(bytes []u8) !int {
return s.write_ptr(&u8(bytes.data), bytes.len)
}
// write_string writes a string to the ssl connection
pub fn (mut s SSLConn) write_string(str string) !int {
$if trace_ssl ? {
eprintln('${@METHOD} str: ${str}')
}
return s.write_ptr(str.str, str.len)
}
// Select waits for an io operation (specified by parameter `test`) to be available
fn select(handle int, test Select, timeout time.Duration) !bool {
$if trace_ssl ? {
eprintln('${@METHOD} handle: ${handle}, timeout: ${timeout}')
}
set := C.fd_set{}
C.FD_ZERO(&set)
C.FD_SET(handle, &set)
is_infinite := timeout <= 0 || timeout == net.infinite_timeout
deadline := ssl_timeout_deadline(timeout)
mut remaining_time := if is_infinite { i64(0) } else { timeout.milliseconds() }
for is_infinite || remaining_time > 0 {
seconds := remaining_time / 1000
microseconds := (remaining_time % 1000) * 1000
tt := C.timeval{
tv_sec: u64(seconds)
tv_usec: u64(microseconds)
}
timeval_timeout := if is_infinite { &C.timeval(unsafe { nil }) } else { &tt }
mut res := -1
match test {
.read {
res = net.socket_error(C.select(handle + 1, &set, C.NULL, C.NULL, timeval_timeout))!
}
.write {
res = net.socket_error(C.select(handle + 1, C.NULL, &set, C.NULL, timeval_timeout))!
}
.except {
res = net.socket_error(C.select(handle + 1, C.NULL, C.NULL, &set, timeval_timeout))!
}
}
if res < 0 {
if C.errno == C.EINTR {
// errno is 4, Spurious wakeup from signal, keep waiting
if !is_infinite {
remaining_time = ssl_remaining_timeout(deadline).milliseconds()
}
continue
}
cerr := C.errno
return error_with_code('net.mbedtls select, failed, res: ${res}', cerr)
} else if res == 0 {
return net.err_timed_out
}
res = C.FD_ISSET(handle, &set)
$if trace_ssl ? {
eprintln('${@METHOD} ---> res: ${res}')
}
return res != 0
}
return net.err_timed_out
}
// wait_for wraps the common wait code
fn wait_for(handle int, what Select, timeout time.Duration) ! {
ready := select(handle, what, timeout)!
if ready {
return
}
return net.err_timed_out
}
// wait_for_write waits for a write io operation to be available
fn (mut s SSLConn) wait_for_write(timeout time.Duration) ! {
return wait_for(s.handle, .write, timeout)
}
// wait_for_read waits for a read io operation to be available
fn (mut s SSLConn) wait_for_read(timeout time.Duration) ! {
return wait_for(s.handle, .read, timeout)
}