#![crate_type = "staticlib"]
#![allow(non_camel_case_types)]
#![allow(clippy::not_unsafe_ptr_arg_deref)]
#![cfg_attr(feature = "read_buf", feature(read_buf))]
use crate::rslice::rustls_str;
use libc::c_void;
use std::cell::RefCell;
use std::mem;
use std::sync::Arc;
pub mod cipher;
pub mod client;
pub mod connection;
pub mod enums;
mod error;
pub mod io;
pub mod log;
mod panic;
pub mod rslice;
pub mod server;
pub mod session;
pub use error::rustls_result;
pub use error::*;
use crate::log::rustls_log_callback;
use crate::panic::PanicOrDefault;
include!(concat!(env!("OUT_DIR"), "/version.rs"));
thread_local! {
pub(crate) static USERDATA: RefCell<Vec<Userdata>> = RefCell::new(Vec::new());
}
pub(crate) struct Userdata {
userdata: *mut c_void,
log_callback: rustls_log_callback,
}
pub(crate) struct UserdataGuard {
data: Option<Userdata>,
}
impl UserdataGuard {
fn new(u: *mut c_void) -> Self {
UserdataGuard {
data: Some(Userdata {
userdata: u,
log_callback: None,
}),
}
}
fn try_drop(mut self) -> Result<(), UserdataError> {
self.try_pop()
}
fn try_pop(&mut self) -> Result<(), UserdataError> {
let expected_data = self
.data
.as_ref()
.ok_or(UserdataError::AlreadyPopped)?
.userdata;
USERDATA
.try_with(|userdata| {
userdata.try_borrow_mut().map_or_else(
|_| Err(UserdataError::AlreadyBorrowed),
|mut v| {
let u = v.pop().ok_or(UserdataError::EmptyStack)?;
self.data = None;
if u.userdata == expected_data {
Ok(())
} else {
Err(UserdataError::WrongData)
}
},
)
})
.unwrap_or(Err(UserdataError::AccessError))
}
}
impl Drop for UserdataGuard {
fn drop(&mut self) {
self.try_pop().ok();
}
}
#[derive(Clone, Debug)]
pub(crate) enum UserdataError {
AlreadyPopped,
AlreadyBorrowed,
EmptyStack,
AccessError,
WrongData,
}
#[must_use = "If you drop the guard, userdata will be immediately cleared"]
pub(crate) fn userdata_push(
u: *mut c_void,
cb: rustls_log_callback,
) -> Result<UserdataGuard, UserdataError> {
USERDATA
.try_with(|userdata| {
userdata.try_borrow_mut().map_or_else(
|_| Err(UserdataError::AlreadyBorrowed),
|mut v| {
v.push(Userdata {
userdata: u,
log_callback: cb,
});
Ok(())
},
)
})
.unwrap_or(Err(UserdataError::AccessError))?;
Ok(UserdataGuard::new(u))
}
pub(crate) fn userdata_get() -> Result<*mut c_void, UserdataError> {
USERDATA
.try_with(|userdata| {
userdata.try_borrow_mut().map_or_else(
|_| Err(UserdataError::AlreadyBorrowed),
|v| match v.last() {
Some(u) => Ok(u.userdata),
None => Err(UserdataError::EmptyStack),
},
)
})
.unwrap_or(Err(UserdataError::AccessError))
}
pub(crate) fn log_callback_get() -> Result<(rustls_log_callback, *mut c_void), UserdataError> {
USERDATA
.try_with(|userdata| {
userdata.try_borrow_mut().map_or_else(
|_| Err(UserdataError::AlreadyBorrowed),
|v| match v.last() {
Some(u) => Ok((u.log_callback, u.userdata)),
None => Err(UserdataError::EmptyStack),
},
)
})
.unwrap_or(Err(UserdataError::AccessError))
}
#[cfg(test)]
mod tests {
use super::*;
use std::thread;
#[test]
fn guard_try_pop() {
let data = "hello";
let data_ptr: *mut c_void = data as *const _ as _;
let mut guard = userdata_push(data_ptr, None).unwrap();
assert_eq!(userdata_get().unwrap(), data_ptr);
guard.try_pop().unwrap();
assert!(matches!(guard.try_pop(), Err(_)));
}
#[test]
fn guard_try_drop() {
let data = "hello";
let data_ptr: *mut c_void = data as *const _ as _;
let guard = userdata_push(data_ptr, None).unwrap();
assert_eq!(userdata_get().unwrap(), data_ptr);
guard.try_drop().unwrap();
assert!(matches!(userdata_get(), Err(_)));
}
#[test]
fn guard_drop() {
let data = "hello";
let data_ptr: *mut c_void = data as *const _ as _;
{
let _guard = userdata_push(data_ptr, None).unwrap();
assert_eq!(userdata_get().unwrap(), data_ptr);
}
assert!(matches!(userdata_get(), Err(_)));
}
#[test]
fn nested_guards() {
let hello = "hello";
let hello_ptr: *mut c_void = hello as *const _ as _;
{
let guard = userdata_push(hello_ptr, None).unwrap();
assert_eq!(userdata_get().unwrap(), hello_ptr);
{
let yo = "yo";
let yo_ptr: *mut c_void = yo as *const _ as _;
let guard2 = userdata_push(yo_ptr, None).unwrap();
assert_eq!(userdata_get().unwrap(), yo_ptr);
guard2.try_drop().unwrap();
}
assert_eq!(userdata_get().unwrap(), hello_ptr);
guard.try_drop().unwrap();
}
assert!(matches!(userdata_get(), Err(_)));
}
#[test]
fn out_of_order_drop() {
let hello = "hello";
let hello_ptr: *mut c_void = hello as *const _ as _;
let guard = userdata_push(hello_ptr, None).unwrap();
assert_eq!(userdata_get().unwrap(), hello_ptr);
let yo = "yo";
let yo_ptr: *mut c_void = yo as *const _ as _;
let guard2 = userdata_push(yo_ptr, None).unwrap();
assert_eq!(userdata_get().unwrap(), yo_ptr);
assert!(matches!(guard.try_drop(), Err(UserdataError::WrongData)));
assert!(matches!(guard2.try_drop(), Err(UserdataError::WrongData)));
}
#[test]
fn userdata_multi_threads() {
let hello = "hello";
let hello_ptr: *mut c_void = hello as *const _ as _;
let guard = userdata_push(hello_ptr, None).unwrap();
assert_eq!(userdata_get().unwrap(), hello_ptr);
let thread1 = thread::spawn(|| {
let yo = "yo";
let yo_ptr: *mut c_void = yo as *const _ as _;
let guard2 = userdata_push(yo_ptr, None).unwrap();
assert_eq!(userdata_get().unwrap(), yo_ptr);
let greetz = "greetz";
let greetz_ptr: *mut c_void = greetz as *const _ as _;
let guard3 = userdata_push(greetz_ptr, None).unwrap();
assert_eq!(userdata_get().unwrap(), greetz_ptr);
guard3.try_drop().unwrap();
assert_eq!(userdata_get().unwrap(), yo_ptr);
guard2.try_drop().unwrap();
});
assert_eq!(userdata_get().unwrap(), hello_ptr);
guard.try_drop().unwrap();
thread1.join().unwrap();
}
}
pub(crate) trait CastPtr {
type RustType;
fn cast_mut_ptr(ptr: *mut Self) -> *mut Self::RustType {
ptr as *mut _
}
}
pub(crate) trait CastConstPtr {
type RustType;
fn cast_const_ptr(ptr: *const Self) -> *const Self::RustType {
ptr as *const _
}
}
impl<T, R> CastConstPtr for T
where
T: CastPtr<RustType = R>,
{
type RustType = R;
}
pub(crate) trait BoxCastPtr: CastPtr + Sized {
fn to_box(ptr: *mut Self) -> Option<Box<Self::RustType>> {
if ptr.is_null() {
return None;
}
let rs_typed = Self::cast_mut_ptr(ptr);
unsafe { Some(Box::from_raw(rs_typed)) }
}
fn to_mut_ptr(src: Self::RustType) -> *mut Self {
Box::into_raw(Box::new(src)) as *mut _
}
fn set_mut_ptr(dst: *mut *mut Self, src: Self::RustType) {
unsafe {
*dst = Self::to_mut_ptr(src);
}
}
}
pub(crate) trait ArcCastPtr: CastConstPtr + Sized {
fn to_arc(ptr: *const Self) -> Option<Arc<Self::RustType>> {
if ptr.is_null() {
return None;
}
let rs_typed = Self::cast_const_ptr(ptr);
let r = unsafe { Arc::from_raw(rs_typed) };
let val = Arc::clone(&r);
mem::forget(r);
Some(val)
}
fn to_const_ptr(src: Self::RustType) -> *const Self {
Arc::into_raw(Arc::new(src)) as *const _
}
}
#[doc(hidden)]
#[macro_export]
macro_rules! try_slice {
( $ptr:expr, $count:expr ) => {
if $ptr.is_null() {
return crate::panic::NullParameterOrDefault::value();
} else {
unsafe { slice::from_raw_parts($ptr, $count as usize) }
}
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! try_mut_slice {
( $ptr:expr, $count:expr ) => {
if $ptr.is_null() {
return crate::panic::NullParameterOrDefault::value();
} else {
unsafe { slice::from_raw_parts_mut($ptr, $count as usize) }
}
};
}
pub(crate) fn try_from<'a, F, T>(from: *const F) -> Option<&'a T>
where
F: CastConstPtr<RustType = T>,
{
unsafe { F::cast_const_ptr(from).as_ref() }
}
pub(crate) fn try_from_mut<'a, F, T>(from: *mut F) -> Option<&'a mut T>
where
F: CastPtr<RustType = T>,
{
unsafe { F::cast_mut_ptr(from).as_mut() }
}
pub(crate) fn try_box_from<F, T>(from: *mut F) -> Option<Box<T>>
where
F: BoxCastPtr<RustType = T>,
{
F::to_box(from)
}
pub(crate) fn try_arc_from<F, T>(from: *const F) -> Option<Arc<T>>
where
F: ArcCastPtr<RustType = T>,
{
F::to_arc(from)
}
#[doc(hidden)]
#[macro_export]
macro_rules! try_ref_from_ptr {
( $var:ident ) => {
match crate::try_from($var) {
Some(c) => c,
None => return crate::panic::NullParameterOrDefault::value(),
}
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! try_mut_from_ptr {
( $var:ident ) => {
match crate::try_from_mut($var) {
Some(c) => c,
None => return crate::panic::NullParameterOrDefault::value(),
}
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! try_box_from_ptr {
( $var:ident ) => {
match crate::try_box_from($var) {
Some(c) => c,
None => return crate::panic::NullParameterOrDefault::value(),
}
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! try_arc_from_ptr {
( $var:ident ) => {
match crate::try_arc_from($var) {
Some(c) => c,
None => return crate::panic::NullParameterOrDefault::value(),
}
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! try_callback {
( $var:ident ) => {
match $var {
Some(c) => c,
None => return crate::panic::NullParameterOrDefault::value(),
}
};
}
#[no_mangle]
pub extern "C" fn rustls_version() -> rustls_str<'static> {
rustls_str::from_str_unchecked(RUSTLS_FFI_VERSION)
}
#[test]
fn test_rustls_version() {
assert!(RUSTLS_FFI_VERSION.contains("/0."));
let vsn = rustls_version();
assert!(vsn.len > 4)
}