From 3032b3d161b14d4253abe85ad7701f996ca5c5d9 Mon Sep 17 00:00:00 2001 From: Joseph Birr-Pixton Date: Fri, 23 Feb 2024 14:52:03 +0000 Subject: [PATCH] Implement `SSL_read`, `SSL_write` and associated --- rustls-libssl/build.rs | 5 +++ rustls-libssl/src/entry.rs | 81 +++++++++++++++++++++++++++++++++++++- rustls-libssl/src/lib.rs | 64 ++++++++++++++++++++++++++++++ 3 files changed, 148 insertions(+), 2 deletions(-) diff --git a/rustls-libssl/build.rs b/rustls-libssl/build.rs index 1a7498c..afbe673 100644 --- a/rustls-libssl/build.rs +++ b/rustls-libssl/build.rs @@ -71,8 +71,11 @@ const ENTRYPOINTS: &[&str] = &[ "SSL_free", "SSL_get_options", "SSL_get_shutdown", + "SSL_has_pending", "SSL_is_server", "SSL_new", + "SSL_pending", + "SSL_read", "SSL_set0_rbio", "SSL_set0_wbio", "SSL_set1_host", @@ -85,6 +88,8 @@ const ENTRYPOINTS: &[&str] = &[ "SSL_set_shutdown", "SSL_shutdown", "SSL_up_ref", + "SSL_want", + "SSL_write", "TLS_client_method", "TLS_method", "TLS_server_method", diff --git a/rustls-libssl/src/entry.rs b/rustls-libssl/src/entry.rs index 6131f5b..e5519a2 100644 --- a/rustls-libssl/src/entry.rs +++ b/rustls-libssl/src/entry.rs @@ -13,8 +13,8 @@ use openssl_sys::{OPENSSL_malloc, X509_STORE, X509_STORE_CTX}; use crate::bio::{Bio, BIO}; use crate::error::{ffi_panic_boundary, Error, MysteriouslyOppositeReturnValue}; use crate::ffi::{ - free_arc, str_from_cstring, to_arc_mut_ptr, try_clone_arc, try_from, try_ref_from_ptr, - try_slice, try_str, Castable, OwnershipArc, OwnershipRef, + free_arc, str_from_cstring, to_arc_mut_ptr, try_clone_arc, try_from, try_mut_slice_int, + try_ref_from_ptr, try_slice, try_slice_int, try_str, Castable, OwnershipArc, OwnershipRef, }; use crate::ShutdownResult; @@ -515,6 +515,65 @@ entry! { } } +entry! { + pub fn _SSL_write(ssl: *mut SSL, buf: *const c_void, num: c_int) -> c_int { + const ERROR: c_int = -1; + let ssl = try_clone_arc!(ssl, ERROR); + let slice = try_slice_int!(buf as *const u8, num, ERROR); + + if slice.is_empty() { + return ERROR; + } + + match ssl + .lock() + .map_err(|_| Error::cannot_lock()) + .and_then(|mut ssl| ssl.write(slice)) + .map_err(|err| err.raise()) + { + Err(_e) => ERROR, + Ok(written) => written as c_int, + } + } +} + +entry! { + pub fn _SSL_read(ssl: *mut SSL, buf: *mut c_void, num: c_int) -> c_int { + const ERROR: c_int = -1; + let ssl = try_clone_arc!(ssl, ERROR); + let slice = try_mut_slice_int!(buf as *mut u8, num, ERROR); + + match ssl + .lock() + .map_err(|_| Error::cannot_lock()) + .and_then(|mut ssl| ssl.read(slice)) + .map_err(|err| err.raise()) + { + Err(_e) => ERROR, + Ok(read) => read as c_int, + } + } +} + +entry! { + pub fn _SSL_want(ssl: *const SSL) -> c_int { + let ssl = try_clone_arc!(ssl); + let want = ssl.lock().ok().map(|ssl| ssl.want()).unwrap_or_default(); + + if want.read { + SSL_READING + } else if want.write { + SSL_WRITING + } else { + SSL_NOTHING + } + } +} + +pub const SSL_NOTHING: i32 = 1; +pub const SSL_WRITING: i32 = 2; +pub const SSL_READING: i32 = 3; + entry! { pub fn _SSL_shutdown(ssl: *mut SSL) -> c_int { const ERROR: c_int = -1; @@ -555,6 +614,24 @@ entry! { } } +entry! { + pub fn _SSL_pending(ssl: *const SSL) -> c_int { + let ssl = try_clone_arc!(ssl); + + ssl.lock() + .map_err(|_| Error::cannot_lock()) + .map(|mut ssl| ssl.get_pending_plaintext() as c_int) + .map_err(|err| err.raise()) + .unwrap_or_default() + } +} + +entry! { + pub fn _SSL_has_pending(ssl: *const SSL) -> c_int { + (_SSL_pending(ssl) > 0) as c_int + } +} + impl Castable for SSL { type Ownership = OwnershipArc; type RustType = Mutex; diff --git a/rustls-libssl/src/lib.rs b/rustls-libssl/src/lib.rs index 44c9954..3c1e3bf 100644 --- a/rustls-libssl/src/lib.rs +++ b/rustls-libssl/src/lib.rs @@ -1,4 +1,5 @@ use core::ffi::CStr; +use std::io::{ErrorKind, Read, Write}; use std::sync::{Arc, Mutex}; use openssl_sys::X509_STORE; @@ -427,6 +428,53 @@ impl Ssl { Ok(()) } + fn want(&self) -> Want { + match &self.conn { + Some(conn) => Want { + read: conn.wants_read(), + write: conn.wants_write(), + }, + None => Want::default(), + } + } + + fn write(&mut self, slice: &[u8]) -> Result { + let written = match &mut self.conn { + Some(ref mut conn) => conn.writer().write(slice).map_err(error::Error::from_io)?, + None => 0, + }; + self.try_io()?; + Ok(written) + } + + fn read(&mut self, slice: &mut [u8]) -> Result { + let (late_err, read_count) = loop { + let late_err = self.try_io(); + + match &mut self.conn { + Some(ref mut conn) => match conn.reader().read(slice) { + Ok(read) => break (late_err, read), + Err(err) if err.kind() == ErrorKind::WouldBlock && late_err.is_ok() => { + // no data available, go around again. + continue; + } + Err(err) => { + return Err(error::Error::from_io(err)); + } + }, + None => break (late_err, 0), + }; + }; + + if read_count > 0 { + Ok(read_count) + } else { + // Only raise IO errors after all data has been read. + late_err?; + Ok(0) + } + } + fn try_io(&mut self) -> Result<(), error::Error> { let bio = match self.bio.as_mut() { Some(bio) => bio, @@ -478,6 +526,22 @@ impl Ssl { fn set_shutdown(&mut self, flags: i32) { self.shutdown_flags.set(flags); } + + fn get_pending_plaintext(&mut self) -> usize { + self.conn + .as_mut() + .and_then(|conn| { + let io_state = conn.process_new_packets().ok()?; + Some(io_state.plaintext_bytes_to_read()) + }) + .unwrap_or_default() + } +} + +#[derive(Default)] +struct Want { + read: bool, + write: bool, } #[derive(PartialEq)]