use std::io::{Read, Write};
use wolfcrypt_sys::*;
use crate::config::TlsClientConfig;
use crate::error::{self, Result, TlsError};
use crate::{SslGuard, TlsSocket};
pub struct TlsClient<S> {
ssl: *mut WOLFSSL,
#[allow(dead_code)]
stream: S,
#[allow(dead_code)]
config: TlsClientConfig,
}
impl<S> std::fmt::Debug for TlsClient<S> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("TlsClient")
.field("ssl", &self.ssl)
.finish()
}
}
unsafe impl<S: Send> Send for TlsClient<S> {}
impl<S: Read + Write + TlsSocket> TlsClient<S> {
pub fn new(config: TlsClientConfig, server_name: &str, stream: S) -> Result<Self> {
if server_name.len() > u16::MAX as usize {
return Err(TlsError::InvalidConfig(
"server name exceeds maximum SNI length",
));
}
let ssl = unsafe { wolfSSL_new(config.inner.ctx) };
if ssl.is_null() {
return Err(TlsError::AllocFailed {
func: "wolfSSL_new",
});
}
let guard = SslGuard(ssl);
if !server_name.is_empty() {
let ret = unsafe {
wolfSSL_UseSNI(
guard.as_ptr(),
WOLFSSL_SNI_HOST_NAME as core::ffi::c_uchar,
server_name.as_ptr() as *const core::ffi::c_void,
server_name.len() as u16,
)
};
if ret != WOLFSSL_SUCCESS as core::ffi::c_int {
return Err(TlsError::Ffi {
code: ret,
func: "wolfSSL_UseSNI",
});
}
}
let fd = stream.tls_raw_fd();
let ret = unsafe { wolfSSL_set_fd(guard.as_ptr(), fd) };
if ret != WOLFSSL_SUCCESS as core::ffi::c_int {
return Err(TlsError::Ffi {
code: ret,
func: "wolfSSL_set_fd",
});
}
let ret = unsafe { wolfSSL_connect(guard.as_ptr()) };
if ret != WOLFSSL_SUCCESS as core::ffi::c_int {
let (err, verify_result) = unsafe {
let e = wolfSSL_get_error(guard.as_ptr(), ret);
let v = wolfSSL_get_verify_result(guard.as_ptr());
(e, v)
};
drop(guard);
if verify_result != X509_V_OK as core::ffi::c_long {
let reason = error::verify_error_string(verify_result);
return Err(TlsError::CertificateVerification(format!(
"{reason} (X509 error {verify_result})"
)));
}
return Err(TlsError::Ffi {
code: err,
func: "wolfSSL_connect",
});
}
Ok(TlsClient {
ssl: guard.into_raw(),
stream,
config,
})
}
}
crate::impl_tls_io!(TlsClient);