From 4d883d488eed9431ebd8ec4e2b5a45d9cbf2e0d8 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Tue, 8 Dec 2015 21:57:04 -0800 Subject: [PATCH] Custom BIO infrastructure --- openssl-sys-extras/src/lib.rs | 6 ++ openssl-sys-extras/src/openssl_shim.c | 12 +++ openssl-sys/src/lib.rs | 3 + openssl/src/ssl/bio.rs | 141 ++++++++++++++++++++++++++ openssl/src/ssl/mod.rs | 1 + 5 files changed, 163 insertions(+) create mode 100644 openssl/src/ssl/bio.rs diff --git a/openssl-sys-extras/src/lib.rs b/openssl-sys-extras/src/lib.rs index a768f436..dfeb06e5 100644 --- a/openssl-sys-extras/src/lib.rs +++ b/openssl-sys-extras/src/lib.rs @@ -49,6 +49,12 @@ extern { pub fn BIO_set_nbio(b: *mut BIO, enabled: c_long) -> c_long; #[link_name = "BIO_set_mem_eof_return_shim"] pub fn BIO_set_mem_eof_return(b: *mut BIO, v: c_int); + #[link_name = "BIO_clear_retry_flags_shim"] + pub fn BIO_clear_retry_flags(b: *mut BIO); + #[link_name = "BIO_set_retry_read_shim"] + pub fn BIO_set_retry_read(b: *mut BIO); + #[link_name = "BIO_set_retry_write_shim"] + pub fn BIO_set_retry_write(b: *mut BIO); pub fn SSL_CTX_set_options_shim(ctx: *mut SSL_CTX, options: c_long) -> c_long; pub fn SSL_CTX_get_options_shim(ctx: *mut SSL_CTX) -> c_long; pub fn SSL_CTX_clear_options_shim(ctx: *mut SSL_CTX, options: c_long) -> c_long; diff --git a/openssl-sys-extras/src/openssl_shim.c b/openssl-sys-extras/src/openssl_shim.c index 84adb47b..95847ac1 100644 --- a/openssl-sys-extras/src/openssl_shim.c +++ b/openssl-sys-extras/src/openssl_shim.c @@ -91,6 +91,18 @@ void BIO_set_mem_eof_return_shim(BIO *b, int v) { BIO_set_mem_eof_return(b, v); } +void BIO_clear_retry_flags_shim(BIO *b) { + BIO_clear_retry_flags(b); +} + +void BIO_set_retry_read_shim(BIO *b) { + BIO_set_retry_read(b); +} + +void BIO_set_retry_write_shim(BIO *b) { + BIO_set_retry_write(b); +} + long SSL_CTX_set_options_shim(SSL_CTX *ctx, long options) { return SSL_CTX_set_options(ctx, options); } diff --git a/openssl-sys/src/lib.rs b/openssl-sys/src/lib.rs index 324d7621..f19fa16c 100644 --- a/openssl-sys/src/lib.rs +++ b/openssl-sys/src/lib.rs @@ -167,7 +167,10 @@ pub type PasswordCallback = extern "C" fn(buf: *mut c_char, size: c_int, rwflag: c_int, user_data: *mut c_void) -> c_int; +pub const BIO_TYPE_NONE: c_int = 0; + pub const BIO_CTRL_EOF: c_int = 2; +pub const BIO_CTRL_FLUSH: c_int = 11; pub const BIO_C_SET_BUF_MEM_EOF_RETURN: c_int = 130; pub const CRYPTO_LOCK: c_int = 1; diff --git a/openssl/src/ssl/bio.rs b/openssl/src/ssl/bio.rs new file mode 100644 index 00000000..92479b37 --- /dev/null +++ b/openssl/src/ssl/bio.rs @@ -0,0 +1,141 @@ +use libc::{c_char, c_int, c_long, c_void, strlen}; +use ffi::{BIO, BIO_METHOD, BIO_CTRL_FLUSH, BIO_TYPE_NONE, BIO_new}; +use ffi_extras::{BIO_clear_retry_flags, BIO_set_retry_read, BIO_set_retry_write}; +use std::any::Any; +use std::io; +use std::io::prelude::*; +use std::marker::PhantomData; +use std::mem; +use std::slice; +use std::sync::Mutex; +use std::ptr; + +use ssl::error::SslError; + +pub struct StreamState { + pub stream: S, + pub error: Option, +} + +pub fn new_bio(stream: S) -> Result<(*mut BIO, Box), SslError> { + // "rust" + static NAME: [c_char; 5] = [114, 117, 115, 116, 0]; + + let method = Box::new(BIO_METHOD { + type_: BIO_TYPE_NONE, + name: &NAME[0], + bwrite: Some(bwrite::), + bread: Some(bread::), + bputs: Some(bputs::), + bgets: None, + ctrl: Some(ctrl::), + create: Some(create), + destroy: Some(destroy), + callback_ctrl: None, + }); + + let state = Box::new(StreamState { + stream: stream, + error: None, + }); + + unsafe { + let bio = BIO_new(&*method); + if bio.is_null() { + return Err(SslError::get()); + } + + (*bio).ptr = Box::into_raw(state) as *mut _; + + return Ok((bio, method)); + } +} + +pub unsafe fn take_error(bio: *mut BIO) -> Option { + let state = state::(bio); + state.error.take() +} + +pub unsafe fn take_stream(bio: *mut BIO) -> S { + let state: Box> = Box::from_raw((*bio).ptr as *mut _); + (*bio).ptr = ptr::null_mut(); + state.stream +} + +unsafe fn state<'a, S: 'a>(bio: *mut BIO) -> &'a mut StreamState { + mem::transmute((*bio).ptr) +} + +unsafe extern "C" fn bwrite(bio: *mut BIO, buf: *const c_char, len: c_int) -> c_int { + BIO_clear_retry_flags(bio); + + let state = state::(bio); + let buf = slice::from_raw_parts(buf as *const _, len as usize); + match state.stream.write(buf) { + Ok(len) => len as c_int, + Err(err) => { + if err.kind() == io::ErrorKind::WouldBlock { + BIO_set_retry_write(bio); + } + state.error = Some(err); + -1 + } + } +} + +unsafe extern "C" fn bread(bio: *mut BIO, buf: *mut c_char, len: c_int) -> c_int { + BIO_clear_retry_flags(bio); + + let state = state::(bio); + let buf = slice::from_raw_parts_mut(buf as *mut _, len as usize); + match state.stream.read(buf) { + Ok(len) => len as c_int, + Err(err) => { + if err.kind() == io::ErrorKind::WouldBlock { + BIO_set_retry_read(bio); + } + state.error = Some(err); + -1 + } + } +} + +unsafe extern "C" fn bputs(bio: *mut BIO, s: *const c_char) -> c_int { + bwrite::(bio, s, strlen(s) as c_int) +} + +unsafe extern "C" fn ctrl(bio: *mut BIO, + cmd: c_int, + num: c_long, + ptr: *mut c_void) + -> c_long { + if cmd == BIO_CTRL_FLUSH { + let state = state::(bio); + match state.stream.flush() { + Ok(()) => 1, + Err(err) => { + state.error = Some(err); + 0 + } + } + } else { + 0 + } +} + +unsafe extern "C" fn create(bio: *mut BIO) -> c_int { + (*bio).init = 0; + (*bio).num = 0; + (*bio).ptr = ptr::null_mut(); + (*bio).flags = 0; + 1 +} + +unsafe extern "C" fn destroy(bio: *mut BIO) -> c_int { + if bio.is_null() { + return 0; + } + + assert!((*bio).ptr.is_null()); + 1 +} diff --git a/openssl/src/ssl/mod.rs b/openssl/src/ssl/mod.rs index a26b714a..2f09e4fa 100644 --- a/openssl/src/ssl/mod.rs +++ b/openssl/src/ssl/mod.rs @@ -28,6 +28,7 @@ use x509::{X509StoreContext, X509FileType, X509}; use crypto::pkey::PKey; pub mod error; +mod bio; #[cfg(test)] mod tests;