From 899eb4954fbdfaba4a55d620d546e0498e1db881 Mon Sep 17 00:00:00 2001 From: Jackson Coxson Date: Mon, 24 Mar 2025 15:29:59 -0600 Subject: [PATCH] Bindings for pairing file --- ffi/src/pairing_file.rs | 141 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 141 insertions(+) create mode 100644 ffi/src/pairing_file.rs diff --git a/ffi/src/pairing_file.rs b/ffi/src/pairing_file.rs new file mode 100644 index 0000000..67bd870 --- /dev/null +++ b/ffi/src/pairing_file.rs @@ -0,0 +1,141 @@ +// Jackson Coxson + +use idevice::pairing_file::PairingFile; +use std::ffi::{CStr, c_char}; + +use crate::IdeviceErrorCode; + +/// Opaque C-compatible handle to a PairingFile +pub struct IdevicePairingFile(pub PairingFile); + +/// Reads a pairing file from the specified path +/// +/// # Arguments +/// * [`path`] - Path to the pairing file +/// * [`pairing_file`] - On success, will be set to point to a newly allocated pairing file instance +/// +/// # Returns +/// An error code indicating success or failure +/// +/// # Safety +/// `path` must be a valid null-terminated C string +/// `pairing_file` must be a valid, non-null pointer to a location where the handle will be stored +#[unsafe(no_mangle)] +pub unsafe extern "C" fn idevice_pairing_file_read( + path: *const c_char, + pairing_file: *mut *mut IdevicePairingFile, +) -> IdeviceErrorCode { + if path.is_null() || pairing_file.is_null() { + return IdeviceErrorCode::InvalidArg; + } + + // Convert C string to Rust path + let c_str = match unsafe { CStr::from_ptr(path) }.to_str() { + Ok(s) => s, + Err(_) => return IdeviceErrorCode::InvalidArg, + }; + + // Read the pairing file + match PairingFile::read_from_file(c_str) { + Ok(pf) => { + let boxed = Box::new(IdevicePairingFile(pf)); + unsafe { + *pairing_file = Box::into_raw(boxed); + } + IdeviceErrorCode::IdeviceSuccess + } + Err(e) => e.into(), + } +} + +/// Parses a pairing file from a byte buffer +/// +/// # Arguments +/// * [`data`] - Pointer to the buffer containing pairing file data +/// * [`size`] - Size of the buffer in bytes +/// * [`pairing_file`] - On success, will be set to point to a newly allocated pairing file instance +/// +/// # Returns +/// An error code indicating success or failure +/// +/// # Safety +/// `data` must be a valid pointer to a buffer of at least `size` bytes +/// `pairing_file` must be a valid, non-null pointer to a location where the handle will be stored +#[unsafe(no_mangle)] +pub unsafe extern "C" fn idevice_pairing_file_from_bytes( + data: *const u8, + size: usize, + pairing_file: *mut *mut IdevicePairingFile, +) -> IdeviceErrorCode { + if data.is_null() || pairing_file.is_null() || size == 0 { + return IdeviceErrorCode::InvalidArg; + } + + // Convert to Rust slice + let bytes = unsafe { std::slice::from_raw_parts(data, size) }; + + // Parse the pairing file + match PairingFile::from_bytes(bytes) { + Ok(pf) => { + let boxed = Box::new(IdevicePairingFile(pf)); + unsafe { *pairing_file = Box::into_raw(boxed) }; + IdeviceErrorCode::IdeviceSuccess + } + Err(e) => e.into(), + } +} + +/// Serializes a pairing file to XML format +/// +/// # Arguments +/// * [`pairing_file`] - The pairing file to serialize +/// * [`data`] - On success, will be set to point to a newly allocated buffer containing the serialized data +/// * [`size`] - On success, will be set to the size of the allocated buffer +/// +/// # Returns +/// An error code indicating success or failure +/// +/// # Safety +/// `pairing_file` must be a valid, non-null pointer to a pairing file instance +/// `data` must be a valid, non-null pointer to a location where the buffer pointer will be stored +/// `size` must be a valid, non-null pointer to a location where the buffer size will be stored +#[unsafe(no_mangle)] +pub unsafe extern "C" fn idevice_pairing_file_serialize( + pairing_file: *const IdevicePairingFile, + data: *mut *mut u8, + size: *mut usize, +) -> IdeviceErrorCode { + if pairing_file.is_null() || data.is_null() || size.is_null() { + return IdeviceErrorCode::InvalidArg; + } + + // Get the pairing file + let pf = unsafe { &(*pairing_file).0 }; + + // Serialize the pairing file + match pf.clone().serialize() { + Ok(buffer) => { + let buffer_size = buffer.len(); + let buffer_ptr = Box::into_raw(buffer.into_boxed_slice()) as *mut u8; + unsafe { *data = buffer_ptr }; + unsafe { *size = buffer_size }; + IdeviceErrorCode::IdeviceSuccess + } + Err(e) => e.into(), + } +} + +/// Frees a pairing file instance +/// +/// # Arguments +/// * [`pairing_file`] - The pairing file to free +/// +/// # Safety +/// `pairing_file` must be a valid pointer to a pairing file instance that was allocated by this library, +/// or NULL (in which case this function does nothing) +#[unsafe(no_mangle)] +pub unsafe extern "C" fn idevice_pairing_file_free(pairing_file: *mut IdevicePairingFile) { + if !pairing_file.is_null() { + let _ = unsafe { Box::from_raw(pairing_file) }; + } +}