rustls_ffi/
session.rs

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