libstrophe/connection/
internals.rs

1#[cfg(feature = "libstrophe-0_11_0")]
2use core::any::TypeId;
3use core::cell::RefCell;
4#[cfg(feature = "libstrophe-0_14")]
5use core::slice;
6use core::{fmt, ptr};
7#[cfg(feature = "libstrophe-0_12_0")]
8use std::ffi::CString;
9use std::ffi::c_void;
10#[cfg(feature = "libstrophe-0_11_0")]
11use std::os::raw::{c_char, c_int};
12use std::rc::{Rc, Weak};
13
14#[cfg(feature = "libstrophe-0_11_0")]
15pub use libstrophe_0_11::*;
16#[cfg(feature = "libstrophe-0_12_0")]
17pub use libstrophe_0_12::*;
18#[cfg(feature = "libstrophe-0_14")]
19pub use libstrophe_0_14::*;
20
21#[cfg(feature = "libstrophe-0_14")]
22use crate::SerializedSmStateRef;
23use crate::{Connection, ConnectionEvent, Context, Stanza, as_void_ptr, void_ptr_as};
24
25#[cfg(feature = "libstrophe-0_11_0")]
26mod libstrophe_0_11 {
27	use core::any::TypeId;
28	use std::collections::HashMap;
29	use std::sync::RwLock;
30
31	use once_cell::sync::Lazy;
32
33	use crate::TlsCert;
34
35	pub type CertFailCallback = dyn Fn(&TlsCert, &str) -> CertFailResult + Send + Sync;
36	/// Certificate failure and socket option handlers do not receive userdata argument so we use a `TypeId` keyed static to
37	/// store their corresponding closure.
38	pub static CERT_FAIL_HANDLERS: Lazy<RwLock<HashMap<TypeId, Box<CertFailCallback>>>> = Lazy::new(Default::default);
39
40	#[derive(Debug)]
41	#[repr(i32)]
42	pub enum CertFailResult {
43		TerminateConnection = 0,
44		EstablishConnection = 1,
45	}
46}
47
48#[cfg(feature = "libstrophe-0_12_0")]
49mod libstrophe_0_12 {
50	use core::any::TypeId;
51	use core::ffi::c_void;
52	use std::collections::HashMap;
53	use std::sync::RwLock;
54
55	use once_cell::sync::Lazy;
56
57	use super::FatHandler;
58	use crate::Connection;
59
60	pub type SockoptCallback = dyn Fn(*mut c_void) -> SockoptResult + Send + Sync;
61	pub static SOCKOPT_HANDLERS: Lazy<RwLock<HashMap<TypeId, Box<SockoptCallback>>>> = Lazy::new(Default::default);
62
63	#[derive(Debug)]
64	#[repr(i32)]
65	pub enum SockoptResult {
66		Ok = 0,
67		Error = -1,
68	}
69
70	pub type PasswordCallback<'cb, 'cx> = dyn Fn(&Connection<'cb, 'cx>, usize) -> Option<String> + Send + 'cb;
71	pub type PasswordFatHandler<'cb, 'cx> = FatHandler<'cb, 'cx, PasswordCallback<'cb, 'cx>, ()>;
72}
73
74#[cfg(feature = "libstrophe-0_14")]
75mod libstrophe_0_14 {
76	use super::FatHandler;
77	use crate::Connection;
78	use crate::sm_state::SerializedSmStateRef;
79
80	pub type SmStateCallback<'cb, 'cx> = dyn FnMut(&mut Connection<'cb, 'cx>, SerializedSmStateRef) + Send + 'cb;
81	pub type SmStateFatHandler<'cb, 'cx> = FatHandler<'cb, 'cx, SmStateCallback<'cb, 'cx>, ()>;
82}
83
84#[derive(Debug)]
85#[repr(i32)]
86pub enum HandlerResult {
87	RemoveHandler = 0,
88	KeepHandler = 1,
89}
90
91pub type ConnectionCallback<'cb, 'cx> = dyn FnMut(&Context<'cx, 'cb>, &mut Connection<'cb, 'cx>, ConnectionEvent) + Send + 'cb;
92pub type ConnectionFatHandler<'cb, 'cx> = FatHandler<'cb, 'cx, ConnectionCallback<'cb, 'cx>, ()>;
93
94pub type TimedCallback<'cb, 'cx> = dyn FnMut(&Context<'cx, 'cb>, &mut Connection<'cb, 'cx>) -> HandlerResult + Send + 'cb;
95pub type TimedFatHandler<'cb, 'cx> = FatHandler<'cb, 'cx, TimedCallback<'cb, 'cx>, ()>;
96
97pub type StanzaCallback<'cb, 'cx> =
98	dyn FnMut(&Context<'cx, 'cb>, &mut Connection<'cb, 'cx>, &Stanza) -> HandlerResult + Send + 'cb;
99pub type StanzaFatHandler<'cb, 'cx> = FatHandler<'cb, 'cx, StanzaCallback<'cb, 'cx>, Option<String>>;
100
101pub struct ConnectionHandlers<'cb, 'cx> {
102	pub connection: Option<BoxedHandler<'cb, 'cx, ConnectionCallback<'cb, 'cx>, ()>>,
103	pub timed: BoxedHandlers<'cb, 'cx, TimedCallback<'cb, 'cx>, ()>,
104	pub stanza: BoxedHandlers<'cb, 'cx, StanzaCallback<'cb, 'cx>, Option<String>>,
105	#[cfg(feature = "libstrophe-0_11_0")]
106	pub cert_fail_handler_id: Option<TypeId>,
107	#[cfg(feature = "libstrophe-0_12_0")]
108	pub sockopt_handler_id: Option<TypeId>,
109	#[cfg(feature = "libstrophe-0_12_0")]
110	pub password: Option<BoxedHandler<'cb, 'cx, PasswordCallback<'cb, 'cx>, ()>>,
111	#[cfg(feature = "libstrophe-0_14")]
112	pub sm_state: Option<BoxedHandler<'cb, 'cx, SmStateCallback<'cb, 'cx>, ()>>,
113}
114
115impl fmt::Debug for ConnectionHandlers<'_, '_> {
116	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
117		let mut s = f.debug_struct("FatHandlers");
118		s.field(
119			"connection",
120			&if self.connection.is_some() {
121				"set"
122			} else {
123				"unset"
124			},
125		);
126		s.field("timed", &format!("{} handlers", self.timed.count()));
127		s.field("stanza", &format!("{} handlers", self.stanza.count()));
128		#[cfg(feature = "libstrophe-0_11_0")]
129		s.field(
130			"cert_fail_handler_id",
131			&if self.connection.is_some() {
132				"set"
133			} else {
134				"unset"
135			},
136		);
137		#[cfg(feature = "libstrophe-0_12_0")]
138		s.field(
139			"sockopt_handler_id",
140			&if self.connection.is_some() {
141				"set"
142			} else {
143				"unset"
144			},
145		);
146		#[cfg(feature = "libstrophe-0_12_0")]
147		s.field(
148			"password",
149			&if self.password.is_some() {
150				"handler set"
151			} else {
152				"no handler"
153			},
154		);
155		#[cfg(feature = "libstrophe-0_14")]
156		s.field(
157			"sm_state",
158			&if self.sm_state.is_some() {
159				"handler set"
160			} else {
161				"no handler"
162			},
163		);
164		s.finish()
165	}
166}
167
168pub struct FatHandler<'cb, 'cx, CB: ?Sized, Extra> {
169	/// Weak reference back to the `Connection`'s `handlers` to be able to create a `Connection` object in the extern "C" callback
170	pub handlers: Weak<RefCell<ConnectionHandlers<'cb, 'cx>>>,
171	/// The actual Rust handler that will call will be forwarded to
172	pub handler: Box<CB>,
173	/// The address of the extern "C" callback
174	pub cb_addr: CbAddr,
175	/// Extra internal data associated with the handler
176	pub extra: Extra,
177}
178
179impl<CB: ?Sized, Extra> FatHandler<'_, '_, CB, Extra> {
180	pub fn as_userdata(&mut self) -> *mut c_void {
181		as_void_ptr(self)
182	}
183
184	pub fn cb_addr(&self) -> CbAddr {
185		self.cb_addr
186	}
187
188	pub unsafe fn from_userdata<'h>(userdata: *mut c_void) -> &'h mut Self {
189		unsafe { void_ptr_as(userdata) }
190	}
191}
192
193pub enum HandlerCb {}
194pub type CbAddr = *const HandlerCb;
195
196pub struct BoxedHandler<'cb, 'cx, CB: ?Sized, Extra> {
197	handler: Box<FatHandler<'cb, 'cx, CB, Extra>>,
198}
199
200impl<'cb, 'cx, CB: ?Sized, Extra> BoxedHandler<'cb, 'cx, CB, Extra> {
201	pub fn new(handler: FatHandler<'cb, 'cx, CB, Extra>) -> Self {
202		Self {
203			handler: Box::new(handler),
204		}
205	}
206
207	pub fn make(handlers: &Rc<RefCell<ConnectionHandlers<'cb, 'cx>>>, handler: Box<CB>, cb_addr: CbAddr, extra: Extra) -> Self {
208		Self::new(FatHandler {
209			handlers: Rc::downgrade(handlers),
210			handler,
211			cb_addr,
212			extra,
213		})
214	}
215
216	fn cb_addr(&self) -> CbAddr {
217		self.handler.cb_addr
218	}
219
220	fn as_ref(&self) -> &FatHandler<'cb, 'cx, CB, Extra> {
221		self.handler.as_ref()
222	}
223
224	fn as_mut(&mut self) -> &mut FatHandler<'cb, 'cx, CB, Extra> {
225		self.handler.as_mut()
226	}
227}
228
229pub trait MaybeBoxedHandler<'cb, 'cx, CB: ?Sized, Extra> {
230	fn set_handler(
231		&mut self,
232		handler: BoxedHandler<'cb, 'cx, CB, Extra>,
233	) -> (
234		Option<BoxedHandler<'cb, 'cx, CB, Extra>>,
235		&mut FatHandler<'cb, 'cx, CB, Extra>,
236	);
237}
238
239impl<'cb, 'cx, CB: ?Sized, Extra> MaybeBoxedHandler<'cb, 'cx, CB, Extra> for Option<BoxedHandler<'cb, 'cx, CB, Extra>> {
240	fn set_handler(
241		&mut self,
242		handler: BoxedHandler<'cb, 'cx, CB, Extra>,
243	) -> (
244		Option<BoxedHandler<'cb, 'cx, CB, Extra>>,
245		&mut FatHandler<'cb, 'cx, CB, Extra>,
246	) {
247		let old_handler = self.take();
248		let self_ref = self.insert(handler).handler.as_mut();
249		(old_handler, self_ref)
250	}
251}
252
253pub struct BoxedHandlers<'cb, 'cx, CB: ?Sized, Extra> {
254	handlers: Vec<BoxedHandler<'cb, 'cx, CB, Extra>>,
255}
256
257impl<'cb, 'cx, CB: ?Sized, Extra> BoxedHandlers<'cb, 'cx, CB, Extra> {
258	pub fn new(capacity: usize) -> Self {
259		Self {
260			handlers: Vec::with_capacity(capacity),
261		}
262	}
263
264	pub fn count(&self) -> usize {
265		self.handlers.len()
266	}
267
268	pub fn retain(&mut self, mut cb: impl FnMut(&FatHandler<'cb, 'cx, CB, Extra>) -> bool) {
269		self.handlers.retain(|boxed| cb(boxed.as_ref()));
270		self.handlers.shrink_to_fit();
271	}
272
273	pub fn store(&mut self, handler: BoxedHandler<'cb, 'cx, CB, Extra>) -> Option<&mut FatHandler<'cb, 'cx, CB, Extra>> {
274		if !self
275			.handlers
276			.iter()
277			.any(|existing| ptr::eq(handler.cb_addr(), existing.cb_addr()))
278		{
279			self.handlers.push(handler);
280			Some(self.handlers.last_mut().expect("Impossible, just pushed it").as_mut())
281		} else {
282			None
283		}
284	}
285
286	pub fn validate(&self, cb_addr: CbAddr) -> Option<&FatHandler<'cb, 'cx, CB, Extra>> {
287		self
288			.handlers
289			.iter()
290			.find_map(|boxed| ptr::eq(cb_addr, boxed.cb_addr()).then_some(boxed.as_ref()))
291	}
292
293	pub fn drop_handler(&mut self, cb_addr: CbAddr) -> usize {
294		let mut removed_count = 0;
295		self.handlers.retain(|handler| {
296			let keep_it = !ptr::eq(cb_addr, handler.cb_addr());
297			if !keep_it {
298				removed_count += 1;
299			}
300			keep_it
301		});
302		removed_count
303	}
304}
305
306/// In the release mode Rust/LLVM tries to meld functions that have identical bodies together,
307/// but the crate code requires that monomorphized callback functions passed to C remain unique.
308/// Those are `connection_handler_cb`, `timed_handler_cb`, `handler_cb`. They are not making
309/// any use of the type argument in their bodies thus there will be only one function address for
310/// each callback function and libstrophe rejects callback with the same address. This macro
311/// imitates the use of the typed argument so that the code is actually different and those
312/// functions are not melded together.
313macro_rules! ensure_unique {
314	($typ: ty, $conn_ptr: ident, $userdata: ident, $($args: expr),*) => {
315		if $conn_ptr.cast::<::core::ffi::c_void>() == $userdata { // dummy condition that's never true
316			unsafe { $crate::void_ptr_as::<$typ>($userdata)($($args),*) };
317		}
318	};
319}
320
321#[cfg(feature = "libstrophe-0_11_0")]
322pub unsafe extern "C" fn certfail_handler_cb<CB: 'static>(cert: *const sys::xmpp_tlscert_t, errormsg: *const c_char) -> c_int {
323	if let Ok(handlers) = CERT_FAIL_HANDLERS.read() {
324		if let Some(handler) = handlers.get(&TypeId::of::<CB>()) {
325			let cert = unsafe { crate::TlsCert::from_ref(cert) };
326			let error_msg = unsafe { crate::FFI(errormsg).receive() }.unwrap_or("Can't process libstrophe error");
327			return handler(&cert, error_msg) as c_int;
328		}
329	}
330	CertFailResult::TerminateConnection as c_int
331}
332
333#[cfg(feature = "libstrophe-0_12_0")]
334pub unsafe extern "C" fn sockopt_callback<CB: 'static>(_conn: *mut sys::xmpp_conn_t, sock: *mut c_void) -> c_int {
335	if let Ok(handlers) = SOCKOPT_HANDLERS.read() {
336		if let Some(handler) = handlers.get(&TypeId::of::<CB>()) {
337			return handler(sock) as c_int;
338		}
339	}
340	SockoptResult::Error as c_int
341}
342
343#[cfg(feature = "libstrophe-0_12_0")]
344pub unsafe extern "C" fn password_handler_cb(
345	pw: *mut c_char,
346	pw_max: usize,
347	conn_ptr: *mut sys::xmpp_conn_t,
348	userdata: *mut c_void,
349) -> c_int {
350	let password_handler = unsafe { PasswordFatHandler::from_userdata(userdata) };
351	if let Some(fat_handlers) = password_handler.handlers.upgrade() {
352		let conn = unsafe { Connection::from_ref_mut(conn_ptr, fat_handlers) };
353		// we need to leave place for the null byte that will be written by libstrophe
354		let max_password_len = pw_max.saturating_sub(1);
355		let result = (password_handler.handler)(&conn, max_password_len);
356		if let Some(password) = result {
357			if let Ok(password) = CString::new(password) {
358				if password.as_bytes().len() <= max_password_len {
359					let pass_len = password.as_bytes().len();
360					unsafe { ptr::copy_nonoverlapping(password.as_ptr(), pw, pass_len) };
361					return c_int::try_from(pass_len).unwrap_or(c_int::MAX);
362				}
363			}
364		}
365	}
366	-1
367}
368
369#[cfg(feature = "libstrophe-0_14")]
370pub unsafe extern "C" fn sm_state_handler_cb(
371	conn_ptr: *mut sys::xmpp_conn_t,
372	userdata: *mut c_void,
373	buf: *const std::ffi::c_uchar,
374	size: usize,
375) {
376	let buf = if buf.is_null() {
377		&[]
378	} else {
379		unsafe { slice::from_raw_parts(buf, size) }
380	};
381	let serialized = SerializedSmStateRef { buf };
382	let sm_state_handler = unsafe { SmStateFatHandler::from_userdata(userdata) };
383	if let Some(fat_handlers) = sm_state_handler.handlers.upgrade() {
384		let mut conn = unsafe { Connection::from_ref_mut(conn_ptr, fat_handlers) };
385		(sm_state_handler.handler)(&mut conn, serialized);
386	}
387}