C interface write passthrough and opaque allocation ok

ffi
Avril 3 years ago
parent 90d3c2615f
commit bf63998e73
Signed by: flanchan
GPG Key ID: 284488987C31F630

@ -4,19 +4,22 @@
//! Intended to be linked to the C wrapper object `wrapper.c`. //! Intended to be linked to the C wrapper object `wrapper.c`.
use super::*; use super::*;
use std::ptr; use std::ptr;
use std::io::{
self, Write,
};
#[derive(Debug, Clone, PartialEq, Eq, Hash, Copy, PartialOrd, Ord, Default)] #[derive(Debug, Clone, PartialEq, Eq, Hash, Copy, PartialOrd, Ord, Default)]
#[repr(C)] #[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)] #[derive(Debug, Clone, PartialEq, Eq, Hash, Copy, PartialOrd, Ord, Default)]
#[repr(C)] #[repr(C)]
struct CIv([u8; key::IV_SIZE]); pub struct CIv([u8; key::IV_SIZE]);
/// Non-encrypted wrapper /// Non-encrypted wrapper
#[derive(Debug)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[repr(C)] #[repr(C)]
struct CPassthrough pub struct CPassthrough
{ {
backing: *mut libc::FILE, backing: *mut libc::FILE,
key: key::Key, 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. //TODO: Create a Custom Stream in `wrapper.c` that allows creating a FILE* object with `fopencookie()` from this.
#[derive(Debug)] #[derive(Debug)]
#[repr(C)] #[repr(C)]
struct CSink pub struct CSink
{ {
sink: Sink<CPassthrough>, sink: Sink<CPassthrough>,
} }
#[derive(Debug, Clone, PartialEq, Eq, Hash, Copy)] #[derive(Debug, Clone, PartialEq, Eq, Hash, Copy)]
#[repr(C)] #[repr(C)]
enum CMode pub enum CMode
{ {
Encrypt, Encrypt,
Decrypt, Decrypt,
@ -44,72 +47,105 @@ enum CMode
mod interop; mod interop;
mod error; 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!({ no_unwind!({
if file.is_null() { if file.is_null() {
return CErr::InvalidFile; return CErr::InvalidFile;
} }
let key = nullchk!(<- key); let key = nullchk!(move key);
let iv = nullchk!(<- iv); let iv = nullchk!(move iv);
let write = CPassthrough { let write = CPassthrough {
backing: file, backing: file,
key: key::Key::from_bytes(key.0), key: key::Key::from_bytes(key.0),
iv: key::IV::from_bytes(iv.0), iv: key::IV::from_bytes(iv.0),
mode, mode,
}; };
unsafe { nullchk!(output);
nullchk!(output); output.write(write);
output.write(write);
}
CErr::Success CErr::Success
}).unwrap_or(CErr::Panic).into() }).unwrap_or(CErr::Panic).into()
} }
/// Create an encrypting `Sink` over a `FILE*`. /// Create an encrypting `Sink` over a `FILE*` from this metadata struct.
#[no_mangle] extern "C" fn cc20_gen_sink(file: *mut libc::FILE, key: *const CKey, iv: *const CIv, mode: CMode) -> *mut CSink #[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<CPassthrough> = 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!({ no_unwind!({
//TODO: Create CSink from `meta` (`CPassthrough`). let meta = nullchk!(ref meta);
let sink = CSink { let sink = CSink {
sink: match meta.mode { sink: match meta.mode {
CMode::Encrypt => Sink::encrypt(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, meta.key, meta.iv), CMode::Decrypt => Sink::decrypt(meta.clone(), meta.key, meta.iv).map_err(|_| CErr::SslError).unwrap(),
}, },
}; };
interop::give(sink) interop::give(sink)
}).unwrap_or(ptr::null_mut()) }).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<CPassthrough> = 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. /// 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. // 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() { if csink.is_null() {
return ptr::null_mut(); return ptr::null_mut();
} }
cc20_wrap_sink(csink) cc20_wrap_sink(csink)
} }
/// Closes the wrapper `sink`, and writes inner `FILE*` pointer to `file`, if `file` is non-null. /// Closes and frees the wrapper `sink`, and writes inner metadata struct to `meta`, if `file` is non-null.
#[no_mangle] extern "C" fn cc20_close_sink(sink: *mut CSink, file: *mut *mut libc::FILE) #[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*`. /// 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!("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<usize> {
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)),
}
}
}

@ -9,6 +9,8 @@ pub enum CErr
InvalidFile, InvalidFile,
/// Unexpected null pointer /// Unexpected null pointer
NullPointer, NullPointer,
/// Internal SSL error
SslError,
Panic = -1, Panic = -1,
} }
@ -42,9 +44,19 @@ impl From<CErr> for i32
} }
} }
impl<T> From<CErr> 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. /// Null check a pointer. If it's not null, dereference it.
#[macro_export] macro_rules! nullchk { #[macro_export] macro_rules! nullchk {
(<- $ptr:expr) => { (move $ptr:expr) => {
{ {
let ptr = $ptr; let ptr = $ptr;
if ptr.is_null() { if ptr.is_null() {
@ -56,12 +68,39 @@ impl From<CErr> 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) => { ($ptr:expr) => {
{ {
if $ptr.is_null() { let ptr = $ptr;
if ptr.is_null() {
return From::from(CErr::NullPointer); return From::from(CErr::NullPointer);
} else {
ptr
} }
()
} }
} }
} }

@ -1,4 +1,3 @@
use super::*;
#[macro_export] macro_rules! no_unwind { #[macro_export] macro_rules! no_unwind {
($expr:expr) => { ($expr:expr) => {

Loading…
Cancel
Save