use crate::error::ErrorStack;
use native_ossl_sys as sys;
use std::marker::PhantomData;
use std::mem::ManuallyDrop;
use std::os::raw::c_int;
use std::ptr;
pub struct MemBio {
ptr: *mut sys::BIO,
}
impl MemBio {
pub fn new() -> Result<Self, ErrorStack> {
let method = unsafe { sys::BIO_s_mem() };
if method.is_null() {
return Err(ErrorStack::drain());
}
let ptr = unsafe { sys::BIO_new(method) };
if ptr.is_null() {
return Err(ErrorStack::drain());
}
Ok(MemBio { ptr })
}
pub fn write(&mut self, data: &[u8]) -> Result<(), ErrorStack> {
let mut written: usize = 0;
let rc = unsafe {
sys::BIO_write_ex(
self.ptr,
data.as_ptr().cast(),
data.len(),
std::ptr::addr_of_mut!(written),
)
};
if rc != 1 || written != data.len() {
return Err(ErrorStack::drain());
}
Ok(())
}
#[must_use]
pub fn data(&self) -> &[u8] {
let mut ptr: *mut std::os::raw::c_char = ptr::null_mut();
let len = unsafe {
sys::BIO_ctrl(
self.ptr,
3, 0,
(&raw mut ptr).cast::<std::os::raw::c_void>(),
)
};
if len <= 0 || ptr.is_null() {
return &[];
}
let n = usize::try_from(len).unwrap_or(0);
unsafe { std::slice::from_raw_parts(ptr.cast::<u8>(), n) }
}
#[must_use]
pub fn into_vec(self) -> Vec<u8> {
self.data().to_vec()
}
#[must_use]
#[allow(dead_code)] pub(crate) fn as_ptr(&mut self) -> *mut sys::BIO {
self.ptr
}
}
impl Drop for MemBio {
fn drop(&mut self) {
unsafe { sys::BIO_free_all(self.ptr) };
}
}
unsafe impl Send for MemBio {}
pub struct MemBioBuf<'a> {
ptr: *mut sys::BIO,
_data: PhantomData<&'a [u8]>,
}
impl<'a> MemBioBuf<'a> {
pub fn new(data: &'a [u8]) -> Result<Self, ErrorStack> {
let len = i32::try_from(data.len()).map_err(|_| ErrorStack::drain())?;
let ptr = unsafe { sys::BIO_new_mem_buf(data.as_ptr().cast(), len) };
if ptr.is_null() {
return Err(ErrorStack::drain());
}
Ok(MemBioBuf {
ptr,
_data: PhantomData,
})
}
#[must_use]
#[allow(dead_code)] pub(crate) fn as_ptr(&self) -> *mut sys::BIO {
self.ptr
}
}
impl Drop for MemBioBuf<'_> {
fn drop(&mut self) {
unsafe { sys::BIO_free(self.ptr) };
}
}
unsafe impl Send for MemBioBuf<'_> {}
pub struct Bio {
ptr: *mut sys::BIO,
}
impl Bio {
pub fn new_pair() -> Result<(Self, Self), crate::error::ErrorStack> {
let mut b1: *mut sys::BIO = std::ptr::null_mut();
let mut b2: *mut sys::BIO = std::ptr::null_mut();
let rc = unsafe {
sys::BIO_new_bio_pair(std::ptr::addr_of_mut!(b1), 0, std::ptr::addr_of_mut!(b2), 0)
};
if rc != 1 {
return Err(crate::error::ErrorStack::drain());
}
Ok((Bio { ptr: b1 }, Bio { ptr: b2 }))
}
#[must_use]
#[allow(dead_code)] pub(crate) unsafe fn from_ptr_owned(ptr: *mut sys::BIO) -> Self {
Bio { ptr }
}
#[must_use]
pub(crate) fn as_ptr(&self) -> *mut sys::BIO {
self.ptr
}
pub fn read(&mut self, buf: &mut [u8]) -> Result<usize, ErrorStack> {
let len = i32::try_from(buf.len()).unwrap_or(i32::MAX);
let n = unsafe { sys::BIO_read(self.ptr, buf.as_mut_ptr().cast(), len) };
if n < 0 {
return Err(ErrorStack::drain());
}
Ok(usize::try_from(n).unwrap_or(0))
}
pub fn read_ex(&mut self, buf: &mut [u8]) -> Result<usize, ErrorStack> {
let mut readbytes: usize = 0;
crate::ossl_call!(sys::BIO_read_ex(
self.ptr,
buf.as_mut_ptr().cast(),
buf.len(),
&raw mut readbytes
))?;
Ok(readbytes)
}
pub fn write(&mut self, buf: &[u8]) -> Result<usize, ErrorStack> {
let len = i32::try_from(buf.len()).unwrap_or(i32::MAX);
let n = unsafe { sys::BIO_write(self.ptr, buf.as_ptr().cast(), len) };
if n < 0 {
return Err(ErrorStack::drain());
}
Ok(usize::try_from(n).unwrap_or(0))
}
#[must_use]
pub fn push(self, next: Bio) -> Bio {
let next_raw = ManuallyDrop::new(next).ptr;
let self_raw = ManuallyDrop::new(self).ptr;
let result = unsafe { sys::BIO_push(self_raw, next_raw) };
Bio { ptr: result }
}
#[must_use]
pub fn pop(&mut self) -> Option<Bio> {
let next = unsafe { sys::BIO_pop(self.ptr) };
if next.is_null() {
None
} else {
Some(Bio { ptr: next })
}
}
#[must_use]
pub fn next(&self) -> Option<BorrowedBio<'_>> {
let next = unsafe { sys::BIO_next(self.ptr) };
if next.is_null() {
None
} else {
Some(BorrowedBio {
inner: ManuallyDrop::new(Bio { ptr: next }),
_marker: PhantomData,
})
}
}
#[must_use]
pub fn pending(&self) -> usize {
let n = unsafe {
sys::BIO_ctrl(
self.ptr,
10, 0,
std::ptr::null_mut(),
)
};
usize::try_from(n).unwrap_or(0)
}
#[must_use]
pub fn wpending(&self) -> usize {
let n = unsafe {
sys::BIO_ctrl(
self.ptr,
13, 0,
std::ptr::null_mut(),
)
};
usize::try_from(n).unwrap_or(0)
}
#[must_use]
pub fn find_type(&self, bio_type: c_int) -> Option<BorrowedBio<'_>> {
let found = unsafe { sys::BIO_find_type(self.ptr, bio_type) };
if found.is_null() {
None
} else {
Some(BorrowedBio {
inner: ManuallyDrop::new(Bio { ptr: found }),
_marker: PhantomData,
})
}
}
}
impl Clone for Bio {
fn clone(&self) -> Self {
unsafe { sys::BIO_up_ref(self.ptr) };
Bio { ptr: self.ptr }
}
}
impl Drop for Bio {
fn drop(&mut self) {
unsafe { sys::BIO_free(self.ptr) };
}
}
unsafe impl Send for Bio {}
unsafe impl Sync for Bio {}
pub struct BorrowedBio<'a> {
inner: ManuallyDrop<Bio>,
_marker: PhantomData<&'a Bio>,
}
impl BorrowedBio<'_> {
#[must_use]
pub fn as_ptr(&self) -> *mut sys::BIO {
self.inner.ptr
}
}
unsafe impl Send for BorrowedBio<'_> {}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn mem_bio_write_and_data() {
let mut bio = MemBio::new().unwrap();
bio.write(b"hello").unwrap();
bio.write(b" world").unwrap();
assert_eq!(bio.data(), b"hello world");
}
#[test]
fn mem_bio_empty() {
let bio = MemBio::new().unwrap();
assert_eq!(bio.data(), b"");
}
#[test]
fn mem_bio_buf_zero_copy() {
let source = b"PEM data goes here";
let bio = MemBioBuf::new(source).unwrap();
let mut char_ptr: *mut std::os::raw::c_char = ptr::null_mut();
let len = unsafe {
sys::BIO_ctrl(
bio.as_ptr(),
3, 0,
(&raw mut char_ptr).cast::<std::os::raw::c_void>(),
)
};
assert_eq!(usize::try_from(len).unwrap(), source.len());
assert_eq!(char_ptr.cast::<u8>().cast_const(), source.as_ptr());
}
#[test]
fn bio_clone_shares_object() {
let mut mem = MemBio::new().unwrap();
mem.write(b"test").unwrap();
let raw = mem.as_ptr();
unsafe { sys::BIO_up_ref(raw) };
let bio = unsafe { Bio::from_ptr_owned(raw) };
let bio2 = bio.clone();
assert_eq!(bio.as_ptr(), bio2.as_ptr());
}
#[test]
fn bio_mem_write_then_read() {
let raw = unsafe { sys::BIO_new(sys::BIO_s_mem()) };
assert!(!raw.is_null());
let mut bio = unsafe { Bio::from_ptr_owned(raw) };
let payload = b"hello, BIO";
let written = bio.write(payload).unwrap();
assert_eq!(written, payload.len());
let mut buf = [0u8; 64];
let nread = bio.read(&mut buf).unwrap();
assert_eq!(nread, payload.len());
assert_eq!(&buf[..nread], payload);
}
#[test]
fn bio_read_ex() {
let raw = unsafe { sys::BIO_new(sys::BIO_s_mem()) };
assert!(!raw.is_null());
let mut bio = unsafe { Bio::from_ptr_owned(raw) };
let payload = b"read_ex test";
bio.write(payload).unwrap();
let mut buf = [0u8; 64];
let nread = bio.read_ex(&mut buf).unwrap();
assert_eq!(nread, payload.len());
assert_eq!(&buf[..nread], payload);
}
#[test]
fn bio_pending_on_mem_bio() {
let raw = unsafe { sys::BIO_new(sys::BIO_s_mem()) };
assert!(!raw.is_null());
let mut bio = unsafe { Bio::from_ptr_owned(raw) };
let payload = b"hello, pending";
bio.write(payload).unwrap();
assert_eq!(bio.pending(), payload.len());
assert_eq!(bio.wpending(), 0);
let mut buf = vec![0u8; payload.len()];
let n = bio.read(&mut buf).unwrap();
assert_eq!(n, payload.len());
assert_eq!(bio.pending(), 0);
}
#[test]
fn bio_chain_push_next_pop() {
let ptr1 = unsafe { sys::BIO_new(sys::BIO_s_mem()) };
let ptr2 = unsafe { sys::BIO_new(sys::BIO_s_mem()) };
assert!(!ptr1.is_null());
assert!(!ptr2.is_null());
let bio1 = unsafe { Bio::from_ptr_owned(ptr1) };
let bio2 = unsafe { Bio::from_ptr_owned(ptr2) };
let raw2 = bio2.as_ptr();
let mut chain = bio1.push(bio2);
{
let next = chain.next().expect("chain must have a next BIO");
assert_eq!(next.as_ptr(), raw2);
}
let detached = chain.pop().expect("pop must return the detached tail");
assert_eq!(detached.as_ptr(), raw2);
assert!(chain.next().is_none());
}
}