1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162
use crate::error::rustls_result;
use crate::rslice::rustls_slice_bytes;
use crate::userdata_get;
use libc::{c_int, c_void, size_t};
use std::fmt::{Debug, Formatter};
/// Any context information the callback will receive when invoked.
pub type rustls_session_store_userdata = *mut c_void;
/// Prototype of a callback that can be installed by the application at the
/// `rustls_server_config` or `rustls_client_config`. This callback will be
/// invoked by a TLS session when looking up the data for a TLS session id.
/// `userdata` will be supplied based on rustls_{client,server}_session_set_userdata.
///
/// The `buf` points to `count` consecutive bytes where the
/// callback is expected to copy the result to. The number of copied bytes
/// needs to be written to `out_n`. The callback should not read any
/// data from `buf`.
///
/// If the value to copy is larger than `count`, the callback should never
/// do a partial copy but instead remove the value from its store and
/// act as if it was never found.
///
/// The callback should return RUSTLS_RESULT_OK to indicate that a value was
/// retrieved and written in its entirety into `buf`, or RUSTLS_RESULT_NOT_FOUND
/// if no session was retrieved.
///
/// When `remove_after` is != 0, the returned data needs to be removed
/// from the store.
///
/// NOTE: the passed in `key` and `buf` are only available during the
/// callback invocation.
/// NOTE: callbacks used in several sessions via a common config
/// must be implemented thread-safe.
pub type rustls_session_store_get_callback = Option<
unsafe extern "C" fn(
userdata: rustls_session_store_userdata,
key: *const rustls_slice_bytes,
remove_after: c_int,
buf: *mut u8,
count: size_t,
out_n: *mut size_t,
) -> u32,
>;
pub(crate) type SessionStoreGetCallback = unsafe extern "C" fn(
userdata: rustls_session_store_userdata,
key: *const rustls_slice_bytes,
remove_after: c_int,
buf: *mut u8,
count: size_t,
out_n: *mut size_t,
) -> u32;
/// Prototype of a callback that can be installed by the application at the
/// `rustls_server_config` or `rustls_client_config`. This callback will be
/// invoked by a TLS session when a TLS session has been created and an id
/// for later use is handed to the client/has been received from the server.
/// `userdata` will be supplied based on rustls_{client,server}_session_set_userdata.
///
/// The callback should return RUSTLS_RESULT_OK to indicate that a value was
/// successfully stored, or RUSTLS_RESULT_IO on failure.
///
/// NOTE: the passed in `key` and `val` are only available during the
/// callback invocation.
/// NOTE: callbacks used in several sessions via a common config
/// must be implemented thread-safe.
pub type rustls_session_store_put_callback = Option<
unsafe extern "C" fn(
userdata: rustls_session_store_userdata,
key: *const rustls_slice_bytes,
val: *const rustls_slice_bytes,
) -> u32,
>;
pub(crate) type SessionStorePutCallback = unsafe extern "C" fn(
userdata: rustls_session_store_userdata,
key: *const rustls_slice_bytes,
val: *const rustls_slice_bytes,
) -> u32;
pub(crate) struct SessionStoreBroker {
pub get_cb: SessionStoreGetCallback,
pub put_cb: SessionStorePutCallback,
}
impl SessionStoreBroker {
pub fn new(get_cb: SessionStoreGetCallback, put_cb: SessionStorePutCallback) -> Self {
SessionStoreBroker { get_cb, put_cb }
}
fn retrieve(&self, key: &[u8], remove: bool) -> Option<Vec<u8>> {
let key: rustls_slice_bytes = key.into();
let userdata = userdata_get().ok()?;
// This is excessive in size, but the returned data in rustls is
// only read once and then dropped.
// See <https://github.com/rustls/rustls-ffi/pull/64#issuecomment-800766940>
let mut data: Vec<u8> = vec![0; 65 * 1024];
let mut out_n: size_t = 0;
let cb = self.get_cb;
let result = unsafe {
cb(
userdata,
&key,
remove as c_int,
data.as_mut_ptr(),
data.len(),
&mut out_n,
)
};
match rustls_result::from(result) {
rustls_result::Ok => {
unsafe { data.set_len(out_n) };
Some(data)
}
_ => None,
}
}
fn store(&self, key: Vec<u8>, value: Vec<u8>) -> bool {
let key: rustls_slice_bytes = key.as_slice().into();
let value: rustls_slice_bytes = value.as_slice().into();
let cb = self.put_cb;
let userdata = match userdata_get() {
Ok(u) => u,
Err(_) => return false,
};
let result = unsafe { cb(userdata, &key, &value) };
result == rustls_result::Ok as u32
}
}
impl rustls::server::StoresServerSessions for SessionStoreBroker {
fn put(&self, key: Vec<u8>, value: Vec<u8>) -> bool {
self.store(key, value)
}
fn get(&self, key: &[u8]) -> Option<Vec<u8>> {
self.retrieve(key, false)
}
fn take(&self, key: &[u8]) -> Option<Vec<u8>> {
self.retrieve(key, true)
}
fn can_cache(&self) -> bool {
true
}
}
impl Debug for SessionStoreBroker {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("SessionStoreBroker").finish()
}
}
/// This struct can be considered thread safe, as long
/// as the registered callbacks are thread safe. This is
/// documented as a requirement in the API.
unsafe impl Sync for SessionStoreBroker {}
unsafe impl Send for SessionStoreBroker {}