diff --git a/src/ffi.rs b/src/ffi.rs index 5e12242..5ea18d1 100644 --- a/src/ffi.rs +++ b/src/ffi.rs @@ -4,19 +4,22 @@ //! Intended to be linked to the C wrapper object `wrapper.c`. use super::*; use std::ptr; +use std::io::{ + self, Write, +}; #[derive(Debug, Clone, PartialEq, Eq, Hash, Copy, PartialOrd, Ord, Default)] #[repr(C)] -struct CKey([u8; key::KEY_SIZE]); +pub struct CKey([u8; key::KEY_SIZE]); #[derive(Debug, Clone, PartialEq, Eq, Hash, Copy, PartialOrd, Ord, Default)] #[repr(C)] -struct CIv([u8; key::IV_SIZE]); +pub struct CIv([u8; key::IV_SIZE]); /// Non-encrypted wrapper -#[derive(Debug)] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[repr(C)] -struct CPassthrough +pub struct CPassthrough { backing: *mut libc::FILE, key: key::Key, @@ -28,14 +31,14 @@ struct CPassthrough //TODO: Create a Custom Stream in `wrapper.c` that allows creating a FILE* object with `fopencookie()` from this. #[derive(Debug)] #[repr(C)] -struct CSink +pub struct CSink { sink: Sink, } #[derive(Debug, Clone, PartialEq, Eq, Hash, Copy)] #[repr(C)] -enum CMode +pub enum CMode { Encrypt, Decrypt, @@ -44,72 +47,105 @@ enum CMode mod interop; mod error; -use error::*; +pub use error::*; -#[no_mangle] extern "C" fn cc20_gen_meta(file: *mut libc::FILE, key: *const CKey, iv: *const CIv, mode: CMode, output: *mut CPassthrough) -> i32 +#[no_mangle] pub unsafe extern "C" fn cc20_gen_meta(file: *mut libc::FILE, key: *const CKey, iv: *const CIv, mode: CMode, output: *mut CPassthrough) -> i32 { no_unwind!({ if file.is_null() { return CErr::InvalidFile; } - let key = nullchk!(<- key); - let iv = nullchk!(<- iv); + let key = nullchk!(move key); + let iv = nullchk!(move iv); let write = CPassthrough { backing: file, key: key::Key::from_bytes(key.0), iv: key::IV::from_bytes(iv.0), mode, }; - unsafe { - nullchk!(output); - output.write(write); - } + nullchk!(output); + output.write(write); CErr::Success }).unwrap_or(CErr::Panic).into() } -/// Create an encrypting `Sink` over a `FILE*`. -#[no_mangle] extern "C" fn cc20_gen_sink(file: *mut libc::FILE, key: *const CKey, iv: *const CIv, mode: CMode) -> *mut CSink +/// Create an encrypting `Sink` over a `FILE*` from this metadata struct. +#[no_mangle] pub unsafe extern "C" fn cc20_gen_sink(meta: *const CPassthrough) -> *mut CSink { - let meta = { - // No need to `no_unwind` this, `cc20_gen_meta` already does it, and nothing else here can panic. - let mut meta: std::mem::MaybeUninit = std::mem::MaybeUninit::uninit(); - match cc20_gen_meta(file, key, iv, mode, &mut meta as *mut _ as *mut CPassthrough) { - 0 => unsafe { meta.assume_init() }, - _ => return ptr::null_mut(), - } - }; no_unwind!({ - //TODO: Create CSink from `meta` (`CPassthrough`). + let meta = nullchk!(ref meta); + let sink = CSink { sink: match meta.mode { - CMode::Encrypt => Sink::encrypt(meta, meta.key, meta.iv), - CMode::Decrypt => Sink::decrypt(meta, meta.key, meta.iv), + CMode::Encrypt => Sink::encrypt(meta.clone(), meta.key, meta.iv).map_err(|_| CErr::SslError).unwrap(), + CMode::Decrypt => Sink::decrypt(meta.clone(), meta.key, meta.iv).map_err(|_| CErr::SslError).unwrap(), }, }; + interop::give(sink) }).unwrap_or(ptr::null_mut()) } +/// Create an encrypting `Sink` over a `FILE*` with these options. +#[no_mangle] pub unsafe extern "C" fn cc20_gen_sink_full(file: *mut libc::FILE, key: *const CKey, iv: *const CIv, mode: CMode) -> *mut CSink +{ + let meta = { + // No need to `no_unwind` this, `cc20_gen_meta` already does it, and nothing else here can panic. + let mut meta: std::mem::MaybeUninit = std::mem::MaybeUninit::uninit(); + match cc20_gen_meta(file, key, iv, mode, &mut meta as *mut _ as *mut CPassthrough) { + 0 => meta.assume_init(), + _ => return ptr::null_mut(), + } + }; + cc20_gen_sink(&meta as *const _) +} /// Create a wrapper `FILE*` that acts as a `Sink` when written to. -#[no_mangle] extern "C" fn cc20_wrap(file: *mut libc::FILE, key: *const CKey, iv: *const CIv, mode: CMode) -> *mut libc::FILE +#[no_mangle] pub unsafe extern "C" fn cc20_wrap(file: *mut libc::FILE, key: *const CKey, iv: *const CIv, mode: CMode) -> *mut libc::FILE { // No need to `no_unwind` this, nothing here can panic. - let csink = cc20_gen_sink(file, key, iv, mode); + let csink = cc20_gen_sink_full(file, key, iv, mode); if csink.is_null() { return ptr::null_mut(); } cc20_wrap_sink(csink) } -/// Closes the wrapper `sink`, and writes inner `FILE*` pointer to `file`, if `file` is non-null. -#[no_mangle] extern "C" fn cc20_close_sink(sink: *mut CSink, file: *mut *mut libc::FILE) +/// Closes and frees the wrapper `sink`, and writes inner metadata struct to `meta`, if `file` is non-null. +#[no_mangle] pub unsafe extern "C" fn cc20_close_sink(sink: *mut CSink, meta: *mut CPassthrough) -> i32 { - todo!() + no_unwind!({ + let sink = interop::take(nullchk!(sink)); + if !meta.is_null() { + *meta = sink.sink.into_inner(); + } + CErr::Success + }).unwrap_or(CErr::Panic) + .into() } /// Convert a `Sink` into a `FILE*`. -#[no_mangle] extern "C" fn cc20_wrap_sink(sink: *mut CSink) -> *mut libc::FILE +#[no_mangle] pub unsafe extern "C" fn cc20_wrap_sink(sink: *mut CSink) -> *mut libc::FILE { todo!("Create a Custom Stream in `wrapper.c` that allows creating a FILE* object from `sink`.") } -//TODO: `impl io::Write for CPassthrough`... +impl Write for CPassthrough +{ + fn write(&mut self, buf: &[u8]) -> io::Result { + match unsafe { libc::fwrite(buf.as_ptr() as *const _, 1, buf.len(), self.backing) } { + full if full == buf.len() => Ok(full), + _ if unsafe { libc::ferror(self.backing) == 1 } => { + //unsafe { libc::clearerr(self.backing) }; + Err(io::Error::last_os_error()) + }, + 0 if unsafe { libc::feof(self.backing) == 1 } => { + Ok(0) + }, + x => Ok(x), + } + } + fn flush(&mut self) -> io::Result<()> { + match unsafe { libc::fflush(self.backing) } { + 0 => Ok(()), + n => Err(io::Error::from_raw_os_error(n)), + } + } +} diff --git a/src/ffi/error.rs b/src/ffi/error.rs index 1bc1ce3..ac06914 100644 --- a/src/ffi/error.rs +++ b/src/ffi/error.rs @@ -9,6 +9,8 @@ pub enum CErr InvalidFile, /// Unexpected null pointer NullPointer, + /// Internal SSL error + SslError, Panic = -1, } @@ -42,9 +44,19 @@ impl From for i32 } } +impl From for *mut T +{ + fn from(from: CErr) -> Self + { + if from.is_error() { ptr::null_mut() } + else { panic!("invalid conversion of successful operation to non-null output pointer") } + } +} + + /// Null check a pointer. If it's not null, dereference it. #[macro_export] macro_rules! nullchk { - (<- $ptr:expr) => { + (move $ptr:expr) => { { let ptr = $ptr; if ptr.is_null() { @@ -56,12 +68,39 @@ impl From for i32 } } }; + (ref $ptr:expr) => { + { + let ptr = $ptr; + if ptr.is_null() { + return From::from(CErr::NullPointer); + } else { + unsafe { + & *ptr + } + } + } + }; + (ref mut $ptr:expr) => { + { + let ptr = $ptr; + if ptr.is_null() { + return From::from(CErr::NullPointer); + } else { + unsafe { + &mut *ptr + } + } + } + }; + ($ptr:expr) => { { - if $ptr.is_null() { + let ptr = $ptr; + if ptr.is_null() { return From::from(CErr::NullPointer); + } else { + ptr } - () } } } diff --git a/src/ffi/interop.rs b/src/ffi/interop.rs index 205ef45..a84273c 100644 --- a/src/ffi/interop.rs +++ b/src/ffi/interop.rs @@ -1,4 +1,3 @@ -use super::*; #[macro_export] macro_rules! no_unwind { ($expr:expr) => {