Initial project import
This commit is contained in:
commit
23f2d29e32
11957 files changed
+1754577
No files matched your search
@@ -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-----\n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\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 */
|
||||
@@ -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)
|
||||
}
|
||||
Reference in new issue
Block a user