Expose SSL_CTX_set_info_callback (#266)
Model callback arguments as structs
This commit is contained in:
parent
7324db2b75
commit
b2525f2ed2
|
|
@ -2,8 +2,9 @@
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
AlpnError, ClientHello, GetSessionPendingError, PrivateKeyMethod, PrivateKeyMethodError,
|
AlpnError, ClientHello, GetSessionPendingError, PrivateKeyMethod, PrivateKeyMethodError,
|
||||||
SelectCertError, SniError, Ssl, SslAlert, SslContext, SslContextRef, SslRef, SslSession,
|
SelectCertError, SniError, Ssl, SslAlert, SslContext, SslContextRef, SslInfoCallbackAlert,
|
||||||
SslSessionRef, SslSignatureAlgorithm, SslVerifyError, SESSION_CTX_INDEX,
|
SslInfoCallbackMode, SslInfoCallbackValue, SslRef, SslSession, SslSessionRef,
|
||||||
|
SslSignatureAlgorithm, SslVerifyError, SESSION_CTX_INDEX,
|
||||||
};
|
};
|
||||||
use crate::error::ErrorStack;
|
use crate::error::ErrorStack;
|
||||||
use crate::ffi;
|
use crate::ffi;
|
||||||
|
|
@ -521,3 +522,32 @@ where
|
||||||
Err(err) => err.0,
|
Err(err) => err.0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(super) unsafe extern "C" fn raw_info_callback<F>(
|
||||||
|
ssl: *const ffi::SSL,
|
||||||
|
mode: c_int,
|
||||||
|
value: c_int,
|
||||||
|
) where
|
||||||
|
F: Fn(&SslRef, SslInfoCallbackMode, SslInfoCallbackValue) + Send + Sync + 'static,
|
||||||
|
{
|
||||||
|
// Due to FFI signature requirements we have to pass a *const SSL into this function, but
|
||||||
|
// foreign-types requires a *mut SSL to get the Rust SslRef
|
||||||
|
let mut_ref = ssl as *mut ffi::SSL;
|
||||||
|
|
||||||
|
// SAFETY: boring provides valid inputs.
|
||||||
|
let ssl = unsafe { SslRef::from_ptr(mut_ref) };
|
||||||
|
let ssl_context = ssl.ssl_context();
|
||||||
|
|
||||||
|
let callback = ssl_context
|
||||||
|
.ex_data(SslContext::cached_ex_index::<F>())
|
||||||
|
.expect("BUG: info callback missing");
|
||||||
|
|
||||||
|
let value = match mode {
|
||||||
|
ffi::SSL_CB_READ_ALERT | ffi::SSL_CB_WRITE_ALERT => {
|
||||||
|
SslInfoCallbackValue::Alert(SslInfoCallbackAlert(value))
|
||||||
|
}
|
||||||
|
_ => SslInfoCallbackValue::Unit,
|
||||||
|
};
|
||||||
|
|
||||||
|
callback(ssl, SslInfoCallbackMode(mode), value);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -834,6 +834,66 @@ pub fn select_next_proto<'a>(server: &[u8], client: &'a [u8]) -> Option<&'a [u8]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Options controlling the behavior of the info callback.
|
||||||
|
#[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord, Hash)]
|
||||||
|
pub struct SslInfoCallbackMode(i32);
|
||||||
|
|
||||||
|
impl SslInfoCallbackMode {
|
||||||
|
/// Signaled for each alert received, warning or fatal.
|
||||||
|
pub const READ_ALERT: Self = Self(ffi::SSL_CB_READ_ALERT);
|
||||||
|
|
||||||
|
/// Signaled for each alert sent, warning or fatal.
|
||||||
|
pub const WRITE_ALERT: Self = Self(ffi::SSL_CB_WRITE_ALERT);
|
||||||
|
|
||||||
|
/// Signaled when a handshake begins.
|
||||||
|
pub const HANDSHAKE_START: Self = Self(ffi::SSL_CB_HANDSHAKE_START);
|
||||||
|
|
||||||
|
/// Signaled when a handshake completes successfully.
|
||||||
|
pub const HANDSHAKE_DONE: Self = Self(ffi::SSL_CB_HANDSHAKE_DONE);
|
||||||
|
|
||||||
|
/// Signaled when a handshake progresses to a new state.
|
||||||
|
pub const ACCEPT_LOOP: Self = Self(ffi::SSL_CB_ACCEPT_LOOP);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The `value` argument to an info callback. The most-significant byte is the alert level, while
|
||||||
|
/// the least significant byte is the alert itself.
|
||||||
|
#[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord, Hash)]
|
||||||
|
pub enum SslInfoCallbackValue {
|
||||||
|
/// The unit value (1). Some BoringSSL info callback modes, like ACCEPT_LOOP, always call the
|
||||||
|
/// callback with `value` set to the unit value. If the [`SslInfoCallbackValue`] is a
|
||||||
|
/// `Unit`, it can safely be disregarded.
|
||||||
|
Unit,
|
||||||
|
/// An alert. See [`SslInfoCallbackAlert`] for details on how to manipulate the alert. This
|
||||||
|
/// variant should only be present if the info callback was called with a `READ_ALERT` or
|
||||||
|
/// `WRITE_ALERT` mode.
|
||||||
|
Alert(SslInfoCallbackAlert),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Hash, Copy, Clone, PartialOrd, Ord, Eq, PartialEq, Debug)]
|
||||||
|
pub struct SslInfoCallbackAlert(c_int);
|
||||||
|
|
||||||
|
impl SslInfoCallbackAlert {
|
||||||
|
/// The level of the SSL alert.
|
||||||
|
pub fn alert_level(&self) -> Ssl3AlertLevel {
|
||||||
|
let value = self.0 >> 8;
|
||||||
|
Ssl3AlertLevel(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The value of the SSL alert.
|
||||||
|
pub fn alert(&self) -> SslAlert {
|
||||||
|
let value = self.0 & (u8::MAX as i32);
|
||||||
|
SslAlert(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
|
pub struct Ssl3AlertLevel(c_int);
|
||||||
|
|
||||||
|
impl Ssl3AlertLevel {
|
||||||
|
pub const WARNING: Ssl3AlertLevel = Self(ffi::SSL3_AL_WARNING);
|
||||||
|
pub const FATAL: Ssl3AlertLevel = Self(ffi::SSL3_AL_FATAL);
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "rpk")]
|
#[cfg(feature = "rpk")]
|
||||||
extern "C" fn rpk_verify_failure_callback(
|
extern "C" fn rpk_verify_failure_callback(
|
||||||
_ssl: *mut ffi::SSL,
|
_ssl: *mut ffi::SSL,
|
||||||
|
|
@ -1818,6 +1878,18 @@ impl SslContextBuilder {
|
||||||
unsafe { cvt_0i(ffi::SSL_CTX_set_compliance_policy(self.as_ptr(), policy.0)).map(|_| ()) }
|
unsafe { cvt_0i(ffi::SSL_CTX_set_compliance_policy(self.as_ptr(), policy.0)).map(|_| ()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets the context's info callback.
|
||||||
|
#[corresponds(SSL_CTX_set_info_callback)]
|
||||||
|
pub fn set_info_callback<F>(&mut self, callback: F)
|
||||||
|
where
|
||||||
|
F: Fn(&SslRef, SslInfoCallbackMode, SslInfoCallbackValue) + Send + Sync + 'static,
|
||||||
|
{
|
||||||
|
unsafe {
|
||||||
|
self.replace_ex_data(SslContext::cached_ex_index::<F>(), callback);
|
||||||
|
ffi::SSL_CTX_set_info_callback(self.as_ptr(), Some(callbacks::raw_info_callback::<F>));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Consumes the builder, returning a new `SslContext`.
|
/// Consumes the builder, returning a new `SslContext`.
|
||||||
pub fn build(self) -> SslContext {
|
pub fn build(self) -> SslContext {
|
||||||
self.ctx
|
self.ctx
|
||||||
|
|
|
||||||
|
|
@ -1052,3 +1052,17 @@ fn drop_ex_data_in_ssl() {
|
||||||
assert_eq!(ssl.replace_ex_data(index, "camembert"), Some("comté"));
|
assert_eq!(ssl.replace_ex_data(index, "camembert"), Some("comté"));
|
||||||
assert_eq!(ssl.replace_ex_data(index, "raclette"), Some("camembert"));
|
assert_eq!(ssl.replace_ex_data(index, "raclette"), Some("camembert"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_info_callback() {
|
||||||
|
static CALLED_BACK: AtomicBool = AtomicBool::new(false);
|
||||||
|
|
||||||
|
let server = Server::builder().build();
|
||||||
|
let mut client = server.client();
|
||||||
|
client.ctx().set_info_callback(move |_, _, _| {
|
||||||
|
CALLED_BACK.store(true, Ordering::Relaxed);
|
||||||
|
});
|
||||||
|
|
||||||
|
client.connect();
|
||||||
|
assert!(CALLED_BACK.load(Ordering::Relaxed));
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue