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 {}