use std::ffi::CString;
use std::pin::Pin;
use wolfhsm_sys::{
posixTransportShmClientContext, posixTransportShmConfig, posixTransportShm_Cleanup,
posixTransportShm_ClientInit, posixTransportShm_RecvResponse, posixTransportShm_SendRequest,
posixTransportTcpClientContext, posixTransportTcpConfig, posixTransportTcp_CleanupConnect,
posixTransportTcp_InitConnect, posixTransportTcp_RecvResponse, posixTransportTcp_SendRequest,
posixTransportUdsClientContext, posixTransportUdsConfig, posixTransportUds_CleanupConnect,
posixTransportUds_InitConnect, posixTransportUds_RecvResponse, posixTransportUds_SendRequest,
whClientConfig, whClientContext, whCommClientConfig, whTransportClientCb, wh_Client_Cleanup,
wh_Client_CommInfo, wh_Client_Echo, wh_Client_Init,
};
use crate::error::Error;
use crate::transport::Transport;
struct TcpInner {
transport_ctx: posixTransportTcpClientContext,
transport_cfg: posixTransportTcpConfig,
transport_cb: whTransportClientCb,
_ip: CString,
}
struct UdsInner {
transport_ctx: posixTransportUdsClientContext,
transport_cfg: posixTransportUdsConfig,
transport_cb: whTransportClientCb,
_path: CString,
}
struct ShmInner {
transport_ctx: posixTransportShmClientContext,
transport_cfg: posixTransportShmConfig,
transport_cb: whTransportClientCb,
_name: CString,
}
enum TransportInner {
Tcp(Box<TcpInner>),
Uds(Box<UdsInner>),
Shm(Box<ShmInner>),
}
impl TransportInner {
fn comm_pointers(
&mut self,
) -> (
*const whTransportClientCb,
*mut core::ffi::c_void,
*const core::ffi::c_void,
) {
match self {
TransportInner::Tcp(inner) => (
&inner.transport_cb as *const _,
&mut inner.transport_ctx as *mut _ as *mut core::ffi::c_void,
&inner.transport_cfg as *const _ as *const core::ffi::c_void,
),
TransportInner::Uds(inner) => (
&inner.transport_cb as *const _,
&mut inner.transport_ctx as *mut _ as *mut core::ffi::c_void,
&inner.transport_cfg as *const _ as *const core::ffi::c_void,
),
TransportInner::Shm(inner) => (
&inner.transport_cb as *const _,
&mut inner.transport_ctx as *mut _ as *mut core::ffi::c_void,
&inner.transport_cfg as *const _ as *const core::ffi::c_void,
),
}
}
}
struct ClientInner {
client_ctx: whClientContext,
comm_cfg: whCommClientConfig,
transport: TransportInner,
}
#[derive(Debug, Clone)]
pub struct ServerInfo {
pub version: u8,
pub build: u8,
pub comm_data_len: u32,
pub nvm_object_count: u32,
pub keycache_count: u32,
pub keycache_bufsize: u32,
pub keycache_bigcount: u32,
pub keycache_bigbufsize: u32,
pub customcb_count: u32,
pub dmaaddr_count: u32,
pub debug_state: u32,
pub boot_state: u32,
pub lifecycle_state: u32,
pub nvm_state: u32,
}
pub struct Client {
inner: Pin<Box<ClientInner>>,
}
unsafe impl Send for Client {}
impl Client {
pub fn connect(transport: Transport, client_id: u8) -> Result<Self, Error> {
let transport_inner = match transport {
Transport::Tcp { ip, port } => {
let ip_cstr = CString::new(ip).map_err(|_| Error::BadArgs {
msg: "ip contains an interior NUL byte",
})?;
let transport_cb = whTransportClientCb {
Init: Some(posixTransportTcp_InitConnect),
Send: Some(posixTransportTcp_SendRequest),
Recv: Some(posixTransportTcp_RecvResponse),
Cleanup: Some(posixTransportTcp_CleanupConnect),
};
let port_i16 = i16::try_from(port).map_err(|_| Error::BadArgs {
msg: "TCP port must be \u{2264} 32767 (C transport uses i16)",
})?;
let transport_ctx: posixTransportTcpClientContext = unsafe { core::mem::zeroed() };
let transport_cfg = posixTransportTcpConfig {
server_ip_string: ip_cstr.as_ptr() as *mut _,
server_port: port_i16,
};
TransportInner::Tcp(Box::new(TcpInner {
transport_ctx,
transport_cfg,
transport_cb,
_ip: ip_cstr,
}))
}
Transport::Uds { path } => {
let path_cstr = CString::new(path).map_err(|_| Error::BadArgs {
msg: "path contains an interior NUL byte",
})?;
let transport_cb = whTransportClientCb {
Init: Some(posixTransportUds_InitConnect),
Send: Some(posixTransportUds_SendRequest),
Recv: Some(posixTransportUds_RecvResponse),
Cleanup: Some(posixTransportUds_CleanupConnect),
};
let transport_ctx: posixTransportUdsClientContext = unsafe { core::mem::zeroed() };
let transport_cfg = posixTransportUdsConfig {
server_path: path_cstr.as_ptr(),
};
TransportInner::Uds(Box::new(UdsInner {
transport_ctx,
transport_cfg,
transport_cb,
_path: path_cstr,
}))
}
Transport::Shm {
name,
req_size,
resp_size,
} => {
let name_cstr = CString::new(name).map_err(|_| Error::BadArgs {
msg: "name contains an interior NUL byte",
})?;
let transport_cb = whTransportClientCb {
Init: Some(posixTransportShm_ClientInit),
Send: Some(posixTransportShm_SendRequest),
Recv: Some(posixTransportShm_RecvResponse),
Cleanup: Some(posixTransportShm_Cleanup),
};
let transport_ctx: posixTransportShmClientContext = unsafe { core::mem::zeroed() };
let transport_cfg = posixTransportShmConfig {
name: name_cstr.as_ptr() as *mut _,
dma_size: 0,
req_size,
resp_size,
};
TransportInner::Shm(Box::new(ShmInner {
transport_ctx,
transport_cfg,
transport_cb,
_name: name_cstr,
}))
}
};
let mut inner: Pin<Box<ClientInner>> = Box::pin(ClientInner {
client_ctx: unsafe { core::mem::zeroed() },
comm_cfg: unsafe { core::mem::zeroed() },
transport: transport_inner,
});
let inner_mut = unsafe { inner.as_mut().get_unchecked_mut() };
let (cb_ptr, ctx_ptr, cfg_ptr) = inner_mut.transport.comm_pointers();
inner_mut.comm_cfg.transport_cb = cb_ptr;
inner_mut.comm_cfg.transport_context = ctx_ptr;
inner_mut.comm_cfg.transport_config = cfg_ptr;
inner_mut.comm_cfg.connect_cb = None;
inner_mut.comm_cfg.client_id = client_id;
let client_cfg = whClientConfig {
comm: &mut inner_mut.comm_cfg as *mut _,
};
let rc = unsafe { wh_Client_Init(&mut inner_mut.client_ctx, &client_cfg) };
Error::check(rc, "wh_Client_Init")?;
Ok(Client { inner })
}
pub fn echo(&mut self, data: &[u8], buf: &mut [u8]) -> Result<usize, Error> {
let snd_len = u16::try_from(data.len()).map_err(|_| Error::BadArgs {
msg: "echo data exceeds u16::MAX bytes",
})?;
let mut rcv_len: u16 = u16::try_from(buf.len()).map_err(|_| Error::BadArgs {
msg: "echo: buf exceeds u16::MAX bytes",
})?;
let rc = unsafe {
wh_Client_Echo(
self.ctx_ptr(),
snd_len,
data.as_ptr() as *const core::ffi::c_void,
&mut rcv_len,
buf.as_mut_ptr() as *mut core::ffi::c_void,
)
};
Error::check(rc, "wh_Client_Echo")?;
Ok(rcv_len as usize)
}
pub fn info(&mut self) -> Result<ServerInfo, Error> {
let mut version: u8 = 0;
let mut build: u8 = 0;
let mut comm_data_len: u32 = 0;
let mut nvm_object_count: u32 = 0;
let mut keycache_count: u32 = 0;
let mut keycache_bufsize: u32 = 0;
let mut keycache_bigcount: u32 = 0;
let mut keycache_bigbufsize: u32 = 0;
let mut customcb_count: u32 = 0;
let mut dmaaddr_count: u32 = 0;
let mut debug_state: u32 = 0;
let mut boot_state: u32 = 0;
let mut lifecycle_state: u32 = 0;
let mut nvm_state: u32 = 0;
let rc = unsafe {
wh_Client_CommInfo(
self.ctx_ptr(),
&mut version,
&mut build,
&mut comm_data_len,
&mut nvm_object_count,
&mut keycache_count,
&mut keycache_bufsize,
&mut keycache_bigcount,
&mut keycache_bigbufsize,
&mut customcb_count,
&mut dmaaddr_count,
&mut debug_state,
&mut boot_state,
&mut lifecycle_state,
&mut nvm_state,
)
};
Error::check(rc, "wh_Client_CommInfo")?;
Ok(ServerInfo {
version,
build,
comm_data_len,
nvm_object_count,
keycache_count,
keycache_bufsize,
keycache_bigcount,
keycache_bigbufsize,
customcb_count,
dmaaddr_count,
debug_state,
boot_state,
lifecycle_state,
nvm_state,
})
}
pub(crate) fn ctx_ptr(&mut self) -> *mut whClientContext {
unsafe { &mut self.inner.as_mut().get_unchecked_mut().client_ctx }
}
}
impl Drop for Client {
fn drop(&mut self) {
let rc = unsafe { wh_Client_Cleanup(self.ctx_ptr()) };
if rc != 0 {
log::warn!("wh_Client_Cleanup failed: {rc}");
}
}
}