Skip to main content

jokoway_core/
tls.rs

1use arc_swap::ArcSwap;
2use boring::ssl::{
3    AlpnError, ClientHello, SelectCertError, SniError, SslAlert, SslContextRef, SslRef, SslSession,
4    SslSessionRef,
5};
6use boring::x509::X509StoreContextRef;
7use std::any::Any;
8use std::sync::Arc;
9
10/// Known ALPN protocols.
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub enum AlpnProtocol {
13    H1,
14    H2,
15    AcmeTls,
16}
17
18impl AlpnProtocol {
19    pub fn as_bytes(&self) -> &'static [u8] {
20        match self {
21            AlpnProtocol::H1 => b"http/1.1",
22            AlpnProtocol::H2 => b"h2",
23            AlpnProtocol::AcmeTls => b"acme-tls/1",
24        }
25    }
26}
27
28/// Helper to check if a specific ALPN protocol is present in the client's offered list.
29pub fn contains_alpn_protocol(client_protos: &[u8], target: AlpnProtocol) -> bool {
30    let target_bytes = target.as_bytes();
31    let mut pos = 0;
32    while pos < client_protos.len() {
33        let len = client_protos[pos] as usize;
34        pos += 1;
35        if pos + len > client_protos.len() {
36            break;
37        }
38        let proto = &client_protos[pos..pos + len];
39        if proto == target_bytes {
40            return true;
41        }
42        pos += len;
43    }
44    false
45}
46
47/// Trait for handling all BoringSSL TLS events.
48///
49/// Default implementations return "pass-through" or "not handled" values, allowing
50/// extensions to implement only the callbacks they need.
51pub trait TlsCallbackHandler: Send + Sync + Any {
52    // --- Handshake & Certificate Selection ---
53
54    /// SNI Callback: Called when ClientHello SNI extension is parsed.
55    fn servername_callback(
56        &self,
57        _ssl: &mut SslRef,
58        _alert: &mut SslAlert,
59    ) -> Result<(), SniError> {
60        Ok(())
61    }
62
63    /// Application-Layer Protocol Negotiation (ALPN) selection.
64    fn alpn_select_callback<'a>(
65        &self,
66        _ssl: &mut SslRef,
67        _client_protos: &'a [u8],
68    ) -> Result<&'a [u8], AlpnError> {
69        Err(AlpnError::NOACK)
70    }
71
72    /// Certificate Selection Callback (ClientHello inspection).
73    fn select_certificate_callback(
74        &self,
75        _client_hello: ClientHello<'_>,
76    ) -> Result<(), SelectCertError> {
77        Ok(())
78    }
79
80    // --- Verification ---
81
82    /// Custom certificate verification logic.
83    /// Returns true if verification succeeds.
84    fn verify_callback(&self, preverify_ok: bool, _x509_ctx: &mut X509StoreContextRef) -> bool {
85        // Default: trust the pre-verification result
86        preverify_ok
87    }
88
89    // --- Session Management ---
90
91    /// Called when a new session is negotiated (for caching).
92    fn new_session_callback(&self, _ssl: &mut SslRef, _session: SslSession) {}
93
94    /// Called when a session is removed from the cache.
95    fn remove_session_callback(&self, _ctx: &SslContextRef, _session: &SslSessionRef) {}
96
97    /// Called to look up a session by ID (server-side resume).
98    fn get_session_callback(
99        &self,
100        _ssl: &mut SslRef,
101        _session_id: &[u8],
102    ) -> Result<Option<SslSession>, boring::ssl::GetSessionPendingError> {
103        Ok(None)
104    }
105
106    // --- PSK (Pre-Shared Key) ---
107
108    fn psk_server_callback(
109        &self,
110        _ssl: &mut SslRef,
111        _identity: Option<&[u8]>,
112        _psk: &mut [u8],
113    ) -> Result<usize, boring::error::ErrorStack> {
114        // Return 0 to indicate no PSK
115        Ok(0)
116    }
117
118    // --- OCSP ---
119
120    fn status_callback(&self, _ssl: &mut SslRef) -> Result<bool, boring::error::ErrorStack> {
121        Ok(true)
122    }
123
124    // --- Key Logging ---
125
126    fn keylog_callback(&self, _ssl: &SslRef, _line: &str) {}
127}
128
129/// Thread-safe container for dynamic TLS callback handlers.
130///
131/// Uses ArcSwap to allow lock-free replacement of the handler at runtime.
132#[derive(Clone)]
133pub struct TlsCallback {
134    inner: Arc<ArcSwap<Option<Box<dyn TlsCallbackHandler>>>>,
135}
136
137impl Default for TlsCallback {
138    fn default() -> Self {
139        Self::new()
140    }
141}
142
143impl TlsCallback {
144    pub fn new() -> Self {
145        Self {
146            inner: Arc::new(ArcSwap::from_pointee(None)),
147        }
148    }
149
150    pub fn set_handler(&self, handler: impl TlsCallbackHandler + 'static) {
151        self.inner.store(Arc::new(Some(
152            Box::new(handler) as Box<dyn TlsCallbackHandler>
153        )));
154    }
155
156    pub fn get_handler(&self) -> Arc<Option<Box<dyn TlsCallbackHandler>>> {
157        self.inner.load().clone()
158    }
159}