ffi: Make &SignalFfiError a valid bridge_fn argument

This commit is contained in:
Jordan Rose
2025-07-02 13:37:53 -07:00
committed by GitHub
parent c904fc88bd
commit 3c1ee4aee2
9 changed files with 272 additions and 246 deletions

View File

@@ -7,16 +7,14 @@
#![warn(clippy::unwrap_used)]
use std::ffi::{c_char, c_uchar, CString};
use std::panic::AssertUnwindSafe;
use libsignal_bridge::ffi::*;
#[cfg(feature = "libsignal-bridge-testing")]
#[allow(unused_imports)]
use libsignal_bridge_testing::*;
use libsignal_core::try_scoped;
use libsignal_protocol::*;
use paste::paste;
pub mod error;
pub mod logging;
#[no_mangle]
@@ -79,225 +77,6 @@ pub unsafe extern "C" fn signal_free_bytestring_array(array: BytestringArray) {
drop(array.into_boxed_parts())
}
#[no_mangle]
pub unsafe extern "C" fn signal_error_get_message(
err: *const SignalFfiError,
out: *mut *const c_char,
) -> *mut SignalFfiError {
let result = try_scoped(|| {
let err = err.as_ref().ok_or(NullPointerError)?;
write_result_to(out, err.to_string())
});
match result {
Ok(()) => std::ptr::null_mut(),
Err(e) => Box::into_raw(Box::new(e)),
}
}
#[no_mangle]
pub unsafe extern "C" fn signal_error_get_address(
err: *const SignalFfiError,
out: *mut MutPointer<ProtocolAddress>,
) -> *mut SignalFfiError {
let err = AssertUnwindSafe(err);
run_ffi_safe(|| {
let err = err.as_ref().ok_or(NullPointerError)?;
let value = err.provide_address().map_err(|_| {
SignalProtocolError::InvalidArgument(format!("cannot get address from error ({err})"))
})?;
write_result_to(out, value)
})
}
#[no_mangle]
pub unsafe extern "C" fn signal_error_get_uuid(
err: *const SignalFfiError,
out: *mut [u8; 16],
) -> *mut SignalFfiError {
let err = AssertUnwindSafe(err);
run_ffi_safe(|| {
let err = err.as_ref().ok_or(NullPointerError)?;
let value = err.provide_uuid().map_err(|_| {
SignalProtocolError::InvalidArgument(format!("cannot get UUID from error ({err})"))
})?;
write_result_to(out, value.into_bytes())
})
}
#[no_mangle]
pub unsafe extern "C" fn signal_error_get_type(err: *const SignalFfiError) -> u32 {
match err.as_ref() {
Some(err) => err.code() as u32,
None => 0,
}
}
#[no_mangle]
pub unsafe extern "C" fn signal_error_get_invalid_protocol_address(
err: *const SignalFfiError,
name_out: *mut *const c_char,
device_id_out: *mut u32,
) -> *mut SignalFfiError {
let err = AssertUnwindSafe(err);
run_ffi_safe(|| {
let err = err.as_ref().ok_or(NullPointerError)?;
let (name, device_id) = err.provide_invalid_address().map_err(|_| {
SignalProtocolError::InvalidArgument(format!("cannot get address from error ({err})"))
})?;
write_result_to(name_out, name)?;
write_result_to(device_id_out, device_id)
})
}
#[no_mangle]
pub unsafe extern "C" fn signal_error_get_unknown_fields(
err: *const SignalFfiError,
out: *mut StringArray,
) -> *mut SignalFfiError {
let err = AssertUnwindSafe(err);
run_ffi_safe(|| {
let err = err.as_ref().ok_or(NullPointerError)?;
let value = err
.provide_unknown_fields()
.map_err(|_| {
SignalProtocolError::InvalidArgument(format!(
"cannot get unknown_fields from error ({err})"
))
})?
.into_boxed_slice();
write_result_to(out, value)
})
}
#[no_mangle]
pub unsafe extern "C" fn signal_error_get_registration_error_not_deliverable(
err: *const SignalFfiError,
out_reason: *mut *const c_char,
out_permanent: *mut bool,
) -> *mut SignalFfiError {
let err = AssertUnwindSafe(err);
run_ffi_safe(|| {
let err = err.as_ref().ok_or(NullPointerError)?;
let libsignal_net_chat::api::registration::VerificationCodeNotDeliverable {
reason,
permanent_failure,
} = err
.provide_registration_code_not_deliverable()
.map_err(|_| {
SignalProtocolError::InvalidArgument(format!(
"cannot get registration error from error ({err})"
))
})?;
write_result_to(out_reason, reason.as_str())?;
write_result_to(out_permanent, *permanent_failure)?;
Ok(())
})
}
#[no_mangle]
pub unsafe extern "C" fn signal_error_get_registration_lock(
err: *const SignalFfiError,
out_time_remaining_seconds: *mut u64,
out_svr2_username: *mut *const c_char,
out_svr2_password: *mut *const c_char,
) -> *mut SignalFfiError {
let err = AssertUnwindSafe(err);
run_ffi_safe(|| {
let err = err.as_ref().ok_or(NullPointerError)?;
let libsignal_net_chat::api::registration::RegistrationLock {
time_remaining,
svr2_credentials:
libsignal_net::auth::Auth {
username: svr2_username,
password: svr2_password,
},
} = err.provide_registration_lock().map_err(|_| {
SignalProtocolError::InvalidArgument(format!(
"cannot get registration error from error ({err})"
))
})?;
write_result_to(out_time_remaining_seconds, time_remaining.as_secs())?;
write_result_to(out_svr2_username, svr2_username.as_str())?;
write_result_to(out_svr2_password, svr2_password.as_str())?;
Ok(())
})
}
macro_rules! get_named_u32_from_err_impl {
($name:ident) => {
paste! {
#[no_mangle]
pub unsafe extern "C" fn [< signal_error_get_ $name >](
err: *const SignalFfiError,
out: *mut u32,
) -> *mut SignalFfiError {
let err = AssertUnwindSafe(err);
run_ffi_safe(|| {
let err = err.as_ref().ok_or(NullPointerError)?;
let value = err.[< provide_ $name >]().map_err(|_| {
SignalProtocolError::InvalidArgument(format!(
"cannot get $name from error ({err})"
))
})?;
write_result_to(out, value)
})
}
}
};
// Similar to the above, only adds an extra .map(...) step to extract the final u32
($name:ident, $field:ident, $c_name:ident) => {
paste! {
#[no_mangle]
pub unsafe extern "C" fn [< signal_error_get_ $c_name >](
err: *const SignalFfiError,
out: *mut u32,
) -> *mut SignalFfiError {
let err = AssertUnwindSafe(err);
run_ffi_safe(|| {
let err = err.as_ref().ok_or(NullPointerError)?;
let value = err.[< provide_ $name >]()
.map(|x| x.$field)
.map_err(|_| {
SignalProtocolError::InvalidArgument(format!(
"cannot get $name from error ({err})"
))}
)?;
write_result_to(out, value)
})
}
}
};
}
get_named_u32_from_err_impl!(fingerprint_versions, ours, our_fingerprint_version);
get_named_u32_from_err_impl!(fingerprint_versions, theirs, their_fingerprint_version);
get_named_u32_from_err_impl!(retry_after_seconds);
get_named_u32_from_err_impl!(tries_remaining);
#[no_mangle]
pub unsafe extern "C" fn signal_error_get_rate_limit_challenge(
err: *const SignalFfiError,
out_token: *mut *const c_char,
out_options: *mut OwnedBufferOf<c_uchar>,
) -> *mut SignalFfiError {
let err = AssertUnwindSafe(err);
run_ffi_safe(|| {
let err = err.as_ref().ok_or(NullPointerError)?;
let libsignal_net_chat::api::RateLimitChallenge { token, options } =
err.provide_rate_limit_challenge().map_err(|_| {
SignalProtocolError::InvalidArgument(format!(
"cannot get rate limit challenge error from error ({err})"
))
})?;
write_result_to(out_token, token.as_str())?;
write_result_to(out_options, options.as_slice())?;
Ok(())
})
}
#[no_mangle]
pub unsafe extern "C" fn signal_error_free(err: *mut SignalFfiError) {
if !err.is_null() {