From fa88c2c87d5e3b6edfbed8ec996529e1baed9fb7 Mon Sep 17 00:00:00 2001 From: Jackson Coxson Date: Mon, 26 May 2025 12:52:23 -0600 Subject: [PATCH] Refactor FFI bindings --- ffi/Cargo.toml | 51 +- ffi/src/adapter.rs | 28 +- ffi/src/afc.rs | 162 ++---- ffi/src/amfi.rs | 128 +---- ffi/src/core_device_proxy.rs | 112 ++-- ffi/src/debug_proxy.rs | 109 ++-- ffi/src/errors.rs | 38 +- ffi/src/heartbeat.rs | 110 +--- ffi/src/installation_proxy.rs | 100 +--- ffi/src/lib.rs | 22 +- ffi/src/location_simulation.rs | 20 +- ffi/src/lockdown.rs | 95 +--- ffi/src/misagent.rs | 79 +-- .../{mounter.rs => mobile_image_mounter.rs} | 360 ++----------- ffi/src/process_control.rs | 20 +- ffi/src/provider.rs | 55 +- ffi/src/remote_server.rs | 62 +-- ffi/src/remotexpc.rs | 288 ---------- ffi/src/rsd.rs | 502 ++++++++++++++++++ .../{sbservices.rs => springboardservices.rs} | 82 +-- ffi/src/syslog_relay.rs | 47 +- idevice/src/tcp/stream.rs | 11 + 22 files changed, 1005 insertions(+), 1476 deletions(-) rename ffi/src/{mounter.rs => mobile_image_mounter.rs} (67%) delete mode 100644 ffi/src/remotexpc.rs create mode 100644 ffi/src/rsd.rs rename ffi/src/{sbservices.rs => springboardservices.rs} (64%) diff --git a/ffi/Cargo.toml b/ffi/Cargo.toml index cc6c97f..fef6311 100644 --- a/ffi/Cargo.toml +++ b/ffi/Cargo.toml @@ -5,7 +5,7 @@ edition = "2024" [dependencies] -idevice = { path = "../idevice", features = ["full"] } +idevice = { path = "../idevice" } log = "0.4.26" simplelog = "0.12.2" once_cell = "1.21.1" @@ -14,6 +14,55 @@ libc = "0.2.171" plist = "1.7.1" plist_plus = { version = "0.2.6", features = ["dynamic"] } +[features] +afc = ["idevice/afc"] +amfi = ["idevice/amfi"] +core_device_proxy = ["idevice/core_device_proxy"] +crashreportcopymobile = ["idevice/crashreportcopymobile"] +debug_proxy = ["idevice/debug_proxy"] +dvt = ["idevice/dvt"] +heartbeat = ["idevice/heartbeat"] +house_arrest = ["idevice/house_arrest"] +installation_proxy = ["idevice/installation_proxy"] +springboardservices = ["idevice/springboardservices"] +misagent = ["idevice/misagent"] +mobile_image_mounter = ["idevice/mobile_image_mounter"] +location_simulation = ["idevice/location_simulation"] +pair = ["idevice/pair"] +rsd = ["idevice/rsd"] +syslog_relay = ["idevice/syslog_relay"] +tcp = ["idevice/tcp"] +tunnel_tcp_stack = ["idevice/tunnel_tcp_stack"] +tss = ["idevice/tss"] +tunneld = ["idevice/tunneld"] +usbmuxd = ["idevice/usbmuxd"] +xpc = ["idevice/xpc"] +full = [ + "afc", + "amfi", + "core_device_proxy", + "crashreportcopymobile", + "debug_proxy", + "dvt", + "heartbeat", + "house_arrest", + "installation_proxy", + "misagent", + "mobile_image_mounter", + "pair", + "usbmuxd", + "xpc", + "location_simulation", + "rsd", + "tcp", + "tunnel_tcp_stack", + "tss", + "tunneld", + "springboardservices", + "syslog_relay", +] +default = ["full"] + [build-dependencies] cbindgen = "0.28.0" diff --git a/ffi/src/adapter.rs b/ffi/src/adapter.rs index e6f0d8b..20fdb88 100644 --- a/ffi/src/adapter.rs +++ b/ffi/src/adapter.rs @@ -2,31 +2,39 @@ use std::ffi::{CString, c_char}; +use idevice::tcp::stream::AdapterStream; + use crate::core_device_proxy::AdapterHandle; use crate::{IdeviceErrorCode, RUNTIME}; +pub struct AdapterStreamHandle<'a>(pub AdapterStream<'a>); + /// Connects the adapter to a specific port /// /// # Arguments -/// * [`handle`] - The adapter handle +/// * [`adapter_handle`] - The adapter handle /// * [`port`] - The port to connect to +/// * [`stream_handle`] - A pointer to allocate the new stream to /// /// # Returns /// An error code indicating success or failure /// /// # Safety -/// `handle` must be a valid pointer to a handle allocated by this library +/// `handle` must be a valid pointer to a handle allocated by this library. +/// Any stream allocated must be used in the same thread as the adapter. The handles are NOT thread +/// safe. #[unsafe(no_mangle)] pub unsafe extern "C" fn adapter_connect( - handle: *mut AdapterHandle, + adapter_handle: *mut AdapterHandle, port: u16, + stream_handle: *mut *mut AdapterStreamHandle, ) -> IdeviceErrorCode { - if handle.is_null() { + if adapter_handle.is_null() || stream_handle.is_null() { return IdeviceErrorCode::InvalidArg; } - let adapter = unsafe { &mut (*handle).0 }; - let res = RUNTIME.block_on(async move { adapter.connect(port).await }); + let adapter = unsafe { &mut (*adapter_handle).0 }; + let res = RUNTIME.block_on(async move { AdapterStream::connect(adapter, port).await }); match res { Ok(_) => IdeviceErrorCode::IdeviceSuccess, @@ -79,7 +87,7 @@ pub unsafe extern "C" fn adapter_pcap( /// Closes the adapter connection /// /// # Arguments -/// * [`handle`] - The adapter handle +/// * [`handle`] - The adapter stream handle /// /// # Returns /// An error code indicating success or failure @@ -87,7 +95,7 @@ pub unsafe extern "C" fn adapter_pcap( /// # Safety /// `handle` must be a valid pointer to a handle allocated by this library #[unsafe(no_mangle)] -pub unsafe extern "C" fn adapter_close(handle: *mut AdapterHandle) -> IdeviceErrorCode { +pub unsafe extern "C" fn adapter_close(handle: *mut AdapterStreamHandle) -> IdeviceErrorCode { if handle.is_null() { return IdeviceErrorCode::InvalidArg; } @@ -119,7 +127,7 @@ pub unsafe extern "C" fn adapter_close(handle: *mut AdapterHandle) -> IdeviceErr /// `data` must be a valid pointer to at least `length` bytes #[unsafe(no_mangle)] pub unsafe extern "C" fn adapter_send( - handle: *mut AdapterHandle, + handle: *mut AdapterStreamHandle, data: *const u8, length: usize, ) -> IdeviceErrorCode { @@ -158,7 +166,7 @@ pub unsafe extern "C" fn adapter_send( /// `length` must be a valid pointer to a usize #[unsafe(no_mangle)] pub unsafe extern "C" fn adapter_recv( - handle: *mut AdapterHandle, + handle: *mut AdapterStreamHandle, data: *mut u8, length: *mut usize, max_length: usize, diff --git a/ffi/src/afc.rs b/ffi/src/afc.rs index 0b5c704..8b8a016 100644 --- a/ffi/src/afc.rs +++ b/ffi/src/afc.rs @@ -1,23 +1,19 @@ // Jackson Coxson -use std::ffi::c_void; - use idevice::{ IdeviceError, IdeviceService, afc::{AfcClient, DeviceInfo, FileInfo}, + provider::IdeviceProvider, }; -use crate::{ - IdeviceErrorCode, IdeviceHandle, RUNTIME, - provider::{TcpProviderHandle, UsbmuxdProviderHandle}, -}; +use crate::{IdeviceErrorCode, IdeviceHandle, RUNTIME, provider::IdeviceProviderHandle}; pub struct AfcClientHandle(pub AfcClient); /// Connects to the AFC service using a TCP provider /// /// # Arguments -/// * [`provider`] - A TcpProvider +/// * [`provider`] - An IdeviceProvider /// * [`client`] - On success, will be set to point to a newly allocated AfcClient handle /// /// # Returns @@ -27,8 +23,8 @@ pub struct AfcClientHandle(pub AfcClient); /// `provider` must be a valid pointer to a handle allocated by this library /// `client` must be a valid, non-null pointer to a location where the handle will be stored #[unsafe(no_mangle)] -pub unsafe extern "C" fn afc_client_connect_tcp( - provider: *mut TcpProviderHandle, +pub unsafe extern "C" fn afc_client_connect( + provider: *mut IdeviceProviderHandle, client: *mut *mut AfcClientHandle, ) -> IdeviceErrorCode { if provider.is_null() || client.is_null() { @@ -36,55 +32,10 @@ pub unsafe extern "C" fn afc_client_connect_tcp( return IdeviceErrorCode::InvalidArg; } - let res: Result = RUNTIME.block_on(async move { - let provider_box = unsafe { Box::from_raw(provider) }; - let provider_ref = &provider_box.0; - let result = AfcClient::connect(provider_ref).await; - std::mem::forget(provider_box); - result - }); + let res = RUNTIME.block_on(async { + let provider_ref: &dyn IdeviceProvider = unsafe { &*(*provider).0 }; - match res { - Ok(r) => { - let boxed = Box::new(AfcClientHandle(r)); - unsafe { *client = Box::into_raw(boxed) }; - IdeviceErrorCode::IdeviceSuccess - } - Err(e) => { - let _ = unsafe { Box::from_raw(provider) }; - e.into() - } - } -} - -/// Connects to the AFC service using a Usbmuxd provider -/// -/// # Arguments -/// * [`provider`] - A UsbmuxdProvider -/// * [`client`] - On success, will be set to point to a newly allocated AfcClient handle -/// -/// # Returns -/// An error code indicating success or failure -/// -/// # Safety -/// `provider` must be a valid pointer to a handle allocated by this library -/// `client` must be a valid, non-null pointer to a location where the handle will be stored -#[unsafe(no_mangle)] -pub unsafe extern "C" fn afc_client_connect_usbmuxd( - provider: *mut UsbmuxdProviderHandle, - client: *mut *mut AfcClientHandle, -) -> IdeviceErrorCode { - if provider.is_null() { - log::error!("Provider is null"); - return IdeviceErrorCode::InvalidArg; - } - - let res: Result = RUNTIME.block_on(async move { - let provider_box = unsafe { Box::from_raw(provider) }; - let provider_ref = &provider_box.0; - let result = AfcClient::connect(provider_ref).await; - std::mem::forget(provider_box); - result + AfcClient::connect(provider_ref).await }); match res { @@ -114,7 +65,7 @@ pub unsafe extern "C" fn afc_client_new( socket: *mut IdeviceHandle, client: *mut *mut AfcClientHandle, ) -> IdeviceErrorCode { - if socket.is_null() { + if socket.is_null() || client.is_null() { return IdeviceErrorCode::InvalidArg; } let socket = unsafe { Box::from_raw(socket) }.0; @@ -231,7 +182,7 @@ pub unsafe extern "C" fn afc_make_directory( client: *mut AfcClientHandle, path: *const libc::c_char, ) -> IdeviceErrorCode { - if path.is_null() { + if client.is_null() || path.is_null() { return IdeviceErrorCode::InvalidArg; } @@ -242,11 +193,8 @@ pub unsafe extern "C" fn afc_make_directory( }; let res: Result<(), IdeviceError> = RUNTIME.block_on(async move { - let mut client_box = unsafe { Box::from_raw(client) }; - let client_ref = &mut client_box.0; - let result = client_ref.mk_dir(path).await; - std::mem::forget(client_box); - result + let client_ref = unsafe { &mut (*client).0 }; + client_ref.mk_dir(path).await }); match res { @@ -286,7 +234,7 @@ pub unsafe extern "C" fn afc_get_file_info( path: *const libc::c_char, info: *mut AfcFileInfo, ) -> IdeviceErrorCode { - if path.is_null() || info.is_null() { + if client.is_null() || path.is_null() || info.is_null() { return IdeviceErrorCode::InvalidArg; } @@ -297,11 +245,8 @@ pub unsafe extern "C" fn afc_get_file_info( }; let res: Result = RUNTIME.block_on(async move { - let mut client_box = unsafe { Box::from_raw(client) }; - let client_ref = &mut client_box.0; - let result = client_ref.get_file_info(path).await; - std::mem::forget(client_box); - result + let client_ref = unsafe { &mut (*client).0 }; + client_ref.get_file_info(path).await }); match res { @@ -380,16 +325,13 @@ pub unsafe extern "C" fn afc_get_device_info( client: *mut AfcClientHandle, info: *mut AfcDeviceInfo, ) -> IdeviceErrorCode { - if info.is_null() { + if client.is_null() || info.is_null() { return IdeviceErrorCode::InvalidArg; } let res: Result = RUNTIME.block_on(async move { - let mut client_box = unsafe { Box::from_raw(client) }; - let client_ref = &mut client_box.0; - let result = client_ref.get_device_info().await; - std::mem::forget(client_box); - result + let client_ref = unsafe { &mut (*client).0 }; + client_ref.get_device_info().await }); match res { @@ -441,7 +383,7 @@ pub unsafe extern "C" fn afc_remove_path( client: *mut AfcClientHandle, path: *const libc::c_char, ) -> IdeviceErrorCode { - if path.is_null() { + if client.is_null() || path.is_null() { return IdeviceErrorCode::InvalidArg; } @@ -452,11 +394,8 @@ pub unsafe extern "C" fn afc_remove_path( }; let res: Result<(), IdeviceError> = RUNTIME.block_on(async move { - let mut client_box = unsafe { Box::from_raw(client) }; - let client_ref = &mut client_box.0; - let result = client_ref.remove(path).await; - std::mem::forget(client_box); - result + let client_ref = unsafe { &mut (*client).0 }; + client_ref.remove(path).await }); match res { @@ -482,7 +421,7 @@ pub unsafe extern "C" fn afc_remove_path_and_contents( client: *mut AfcClientHandle, path: *const libc::c_char, ) -> IdeviceErrorCode { - if path.is_null() { + if client.is_null() || path.is_null() { return IdeviceErrorCode::InvalidArg; } @@ -493,11 +432,8 @@ pub unsafe extern "C" fn afc_remove_path_and_contents( }; let res: Result<(), IdeviceError> = RUNTIME.block_on(async move { - let mut client_box = unsafe { Box::from_raw(client) }; - let client_ref = &mut client_box.0; - let result = client_ref.remove_all(path).await; - std::mem::forget(client_box); - result + let client_ref = unsafe { &mut (*client).0 }; + client_ref.remove_all(path).await }); match res { @@ -531,7 +467,7 @@ impl From for idevice::afc::opcode::AfcFopenMode { /// Handle for an open file on the device #[allow(dead_code)] -pub struct AfcFileHandle(*mut c_void); // Opaque pointer +pub struct AfcFileHandle<'a>(Box>); // Opaque pointer /// Opens a file on the device /// @@ -546,7 +482,9 @@ pub struct AfcFileHandle(*mut c_void); // Opaque pointer /// /// # Safety /// All pointers must be valid and non-null -/// `path` must be a valid null-terminated C string +/// `path` must be a valid null-terminated C string. +/// The file handle MAY NOT be used from another thread, and is +/// dependant upon the client it was created by. #[unsafe(no_mangle)] pub unsafe extern "C" fn afc_file_open( client: *mut AfcClientHandle, @@ -554,7 +492,7 @@ pub unsafe extern "C" fn afc_file_open( mode: AfcFopenMode, handle: *mut *mut AfcFileHandle, ) -> IdeviceErrorCode { - if path.is_null() || handle.is_null() { + if client.is_null() || path.is_null() || handle.is_null() { return IdeviceErrorCode::InvalidArg; } @@ -567,18 +505,15 @@ pub unsafe extern "C" fn afc_file_open( let mode = mode.into(); let res: Result<*mut AfcFileHandle, IdeviceError> = RUNTIME.block_on(async move { - let mut client_box = unsafe { Box::from_raw(client) }; - let client_ref = &mut client_box.0; + let client_ref = unsafe { &mut (*client).0 }; let result = client_ref.open(path, mode).await; - let res = match result { + match result { Ok(f) => { let boxed = Box::new(f); Ok(Box::into_raw(boxed) as *mut AfcFileHandle) } Err(e) => Err(e), - }; - std::mem::forget(client_box); - res + } }); match res { @@ -716,7 +651,7 @@ pub unsafe extern "C" fn afc_make_link( source: *const libc::c_char, link_type: AfcLinkType, ) -> IdeviceErrorCode { - if target.is_null() || source.is_null() { + if client.is_null() || target.is_null() || source.is_null() { return IdeviceErrorCode::InvalidArg; } @@ -738,11 +673,8 @@ pub unsafe extern "C" fn afc_make_link( }; let res: Result<(), IdeviceError> = RUNTIME.block_on(async move { - let mut client_box = unsafe { Box::from_raw(client) }; - let client_ref = &mut client_box.0; - let result = client_ref.link(target, source, link_type).await; - std::mem::forget(client_box); - result + let client_ref = unsafe { &mut (*client).0 }; + client_ref.link(target, source, link_type).await }); match res { @@ -770,7 +702,7 @@ pub unsafe extern "C" fn afc_rename_path( source: *const libc::c_char, target: *const libc::c_char, ) -> IdeviceErrorCode { - if source.is_null() || target.is_null() { + if client.is_null() || source.is_null() || target.is_null() { return IdeviceErrorCode::InvalidArg; } @@ -787,11 +719,8 @@ pub unsafe extern "C" fn afc_rename_path( }; let res: Result<(), IdeviceError> = RUNTIME.block_on(async move { - let mut client_box = unsafe { Box::from_raw(client) }; - let client_ref = &mut client_box.0; - let result = client_ref.rename(source, target).await; - std::mem::forget(client_box); - result + let client_ref = unsafe { &mut (*client).0 }; + client_ref.rename(source, target).await }); match res { @@ -799,3 +728,18 @@ pub unsafe extern "C" fn afc_rename_path( Err(e) => e.into(), } } + +/// Frees memory allocated by a file read function allocated by this library +/// +/// # Arguments +/// * [`info`] - Pointer to AfcDeviceInfo struct to free +/// +/// # Safety +/// `info` must be a valid pointer to an AfcDeviceInfo struct previously returned by afc_get_device_info +#[unsafe(no_mangle)] +pub unsafe extern "C" fn afc_file_read_data_free(data: *mut u8, length: libc::size_t) { + if !data.is_null() { + let boxed = unsafe { Box::from_raw(std::slice::from_raw_parts_mut(data, length)) }; + drop(boxed); + } +} diff --git a/ffi/src/amfi.rs b/ffi/src/amfi.rs index 56daf7e..4cd5ce8 100644 --- a/ffi/src/amfi.rs +++ b/ffi/src/amfi.rs @@ -1,18 +1,15 @@ // Jackson Coxson -use idevice::{IdeviceError, IdeviceService, amfi::AmfiClient}; +use idevice::{IdeviceError, IdeviceService, amfi::AmfiClient, provider::IdeviceProvider}; -use crate::{ - IdeviceErrorCode, IdeviceHandle, RUNTIME, - provider::{TcpProviderHandle, UsbmuxdProviderHandle}, -}; +use crate::{IdeviceErrorCode, IdeviceHandle, RUNTIME, provider::IdeviceProviderHandle}; pub struct AmfiClientHandle(pub AmfiClient); /// Automatically creates and connects to AMFI service, returning a client handle /// /// # Arguments -/// * [`provider`] - A TcpProvider +/// * [`provider`] - An IdeviceProvider /// * [`client`] - On success, will be set to point to a newly allocated AmfiClient handle /// /// # Returns @@ -22,8 +19,8 @@ pub struct AmfiClientHandle(pub AmfiClient); /// `provider` must be a valid pointer to a handle allocated by this library /// `client` must be a valid, non-null pointer to a location where the handle will be stored #[unsafe(no_mangle)] -pub unsafe extern "C" fn amfi_connect_tcp( - provider: *mut TcpProviderHandle, +pub unsafe extern "C" fn amfi_connect( + provider: *mut IdeviceProviderHandle, client: *mut *mut AmfiClientHandle, ) -> IdeviceErrorCode { if provider.is_null() || client.is_null() { @@ -32,70 +29,10 @@ pub unsafe extern "C" fn amfi_connect_tcp( } let res: Result = RUNTIME.block_on(async move { - // Take ownership of the provider (without immediately dropping it) - let provider_box = unsafe { Box::from_raw(provider) }; - - // Get a reference to the inner value - let provider_ref = &provider_box.0; + let provider_ref: &dyn IdeviceProvider = unsafe { &*(*provider).0 }; // Connect using the reference - let result = AmfiClient::connect(provider_ref).await; - - // Explicitly keep the provider_box alive until after connect completes - std::mem::forget(provider_box); - result - }); - - match res { - Ok(r) => { - let boxed = Box::new(AmfiClientHandle(r)); - unsafe { *client = Box::into_raw(boxed) }; - IdeviceErrorCode::IdeviceSuccess - } - Err(e) => { - // If connection failed, the provider_box was already forgotten, - // so we need to reconstruct it to avoid leak - let _ = unsafe { Box::from_raw(provider) }; - e.into() - } - } -} - -/// Automatically creates and connects to AMFI service, returning a client handle -/// -/// # Arguments -/// * [`provider`] - A UsbmuxdProvider -/// * [`client`] - On success, will be set to point to a newly allocated AmfiClient handle -/// -/// # Returns -/// An error code indicating success or failure -/// -/// # Safety -/// `provider` must be a valid pointer to a handle allocated by this library -/// `client` must be a valid, non-null pointer to a location where the handle will be stored -#[unsafe(no_mangle)] -pub unsafe extern "C" fn amfi_connect_usbmuxd( - provider: *mut UsbmuxdProviderHandle, - client: *mut *mut AmfiClientHandle, -) -> IdeviceErrorCode { - if provider.is_null() { - log::error!("Provider is null"); - return IdeviceErrorCode::InvalidArg; - } - - let res: Result = RUNTIME.block_on(async move { - // Take ownership of the provider (without immediately dropping it) - let provider_box = unsafe { Box::from_raw(provider) }; - - // Get a reference to the inner value - let provider_ref = &provider_box.0; - - // Connect using the reference - let result = AmfiClient::connect(provider_ref).await; - - // Explicitly keep the provider_box alive until after connect completes - std::mem::forget(provider_box); - result + AmfiClient::connect(provider_ref).await }); match res { @@ -118,16 +55,18 @@ pub unsafe extern "C" fn amfi_connect_usbmuxd( /// An error code indicating success or failure /// /// # Safety -/// `socket` must be a valid pointer to a handle allocated by this library +/// `socket` must be a valid pointer to a handle allocated by this library. It is consumed, and +/// should not be used again. /// `client` must be a valid, non-null pointer to a location where the handle will be stored #[unsafe(no_mangle)] pub unsafe extern "C" fn amfi_new( socket: *mut IdeviceHandle, client: *mut *mut AmfiClientHandle, ) -> IdeviceErrorCode { - if socket.is_null() { + if socket.is_null() || client.is_null() { return IdeviceErrorCode::InvalidArg; } + let socket = unsafe { Box::from_raw(socket) }.0; let r = AmfiClient::new(socket); let boxed = Box::new(AmfiClientHandle(r)); @@ -149,16 +88,13 @@ pub unsafe extern "C" fn amfi_new( pub unsafe extern "C" fn amfi_reveal_developer_mode_option_in_ui( client: *mut AmfiClientHandle, ) -> IdeviceErrorCode { + if client.is_null() { + return IdeviceErrorCode::InvalidArg; + } + let res: Result<(), IdeviceError> = RUNTIME.block_on(async move { - // Take ownership of the client - let mut client_box = unsafe { Box::from_raw(client) }; - - // Get a reference to the inner value - let client_ref = &mut client_box.0; - let res = client_ref.reveal_developer_mode_option_in_ui().await; - - std::mem::forget(client_box); - res + let client_ref = unsafe { &mut (*client).0 }; + client_ref.reveal_developer_mode_option_in_ui().await }); match res { Ok(_) => IdeviceErrorCode::IdeviceSuccess, @@ -180,16 +116,13 @@ pub unsafe extern "C" fn amfi_reveal_developer_mode_option_in_ui( pub unsafe extern "C" fn amfi_enable_developer_mode( client: *mut AmfiClientHandle, ) -> IdeviceErrorCode { + if client.is_null() { + return IdeviceErrorCode::InvalidArg; + } + let res: Result<(), IdeviceError> = RUNTIME.block_on(async move { - // Take ownership of the client - let mut client_box = unsafe { Box::from_raw(client) }; - - // Get a reference to the inner value - let client_ref = &mut client_box.0; - let res = client_ref.enable_developer_mode().await; - - std::mem::forget(client_box); - res + let client_ref = unsafe { &mut (*client).0 }; + client_ref.enable_developer_mode().await }); match res { Ok(_) => IdeviceErrorCode::IdeviceSuccess, @@ -211,16 +144,13 @@ pub unsafe extern "C" fn amfi_enable_developer_mode( pub unsafe extern "C" fn amfi_accept_developer_mode( client: *mut AmfiClientHandle, ) -> IdeviceErrorCode { + if client.is_null() { + return IdeviceErrorCode::InvalidArg; + } + let res: Result<(), IdeviceError> = RUNTIME.block_on(async move { - // Take ownership of the client - let mut client_box = unsafe { Box::from_raw(client) }; - - // Get a reference to the inner value - let client_ref = &mut client_box.0; - let res = client_ref.accept_developer_mode().await; - - std::mem::forget(client_box); - res + let client_ref = unsafe { &mut (*client).0 }; + client_ref.accept_developer_mode().await }); match res { Ok(_) => IdeviceErrorCode::IdeviceSuccess, diff --git a/ffi/src/core_device_proxy.rs b/ffi/src/core_device_proxy.rs index 124980d..4e027bd 100644 --- a/ffi/src/core_device_proxy.rs +++ b/ffi/src/core_device_proxy.rs @@ -3,13 +3,11 @@ use std::ffi::{CString, c_char}; use idevice::{ - IdeviceError, IdeviceService, core_device_proxy::CoreDeviceProxy, tcp::adapter::Adapter, + IdeviceError, IdeviceService, core_device_proxy::CoreDeviceProxy, provider::IdeviceProvider, + tcp::adapter::Adapter, }; -use crate::{ - IdeviceErrorCode, IdeviceHandle, RUNTIME, - provider::{TcpProviderHandle, UsbmuxdProviderHandle}, -}; +use crate::{IdeviceErrorCode, IdeviceHandle, RUNTIME, provider::IdeviceProviderHandle}; pub struct CoreDeviceProxyHandle(pub CoreDeviceProxy); pub struct AdapterHandle(pub Adapter); @@ -17,7 +15,7 @@ pub struct AdapterHandle(pub Adapter); /// Automatically creates and connects to Core Device Proxy, returning a client handle /// /// # Arguments -/// * [`provider`] - A TcpProvider +/// * [`provider`] - An IdeviceProvider /// * [`client`] - On success, will be set to point to a newly allocated CoreDeviceProxy handle /// /// # Returns @@ -27,8 +25,8 @@ pub struct AdapterHandle(pub Adapter); /// `provider` must be a valid pointer to a handle allocated by this library /// `client` must be a valid, non-null pointer to a location where the handle will be stored #[unsafe(no_mangle)] -pub unsafe extern "C" fn core_device_proxy_connect_tcp( - provider: *mut TcpProviderHandle, +pub unsafe extern "C" fn core_device_proxy_connect( + provider: *mut IdeviceProviderHandle, client: *mut *mut CoreDeviceProxyHandle, ) -> IdeviceErrorCode { if provider.is_null() || client.is_null() { @@ -37,70 +35,10 @@ pub unsafe extern "C" fn core_device_proxy_connect_tcp( } let res: Result = RUNTIME.block_on(async move { - // Take ownership of the provider (without immediately dropping it) - let provider_box = unsafe { Box::from_raw(provider) }; - - // Get a reference to the inner value - let provider_ref = &provider_box.0; + let provider_ref: &dyn IdeviceProvider = unsafe { &*(*provider).0 }; // Connect using the reference - let result = CoreDeviceProxy::connect(provider_ref).await; - - // Explicitly keep the provider_box alive until after connect completes - std::mem::forget(provider_box); - result - }); - - match res { - Ok(r) => { - let boxed = Box::new(CoreDeviceProxyHandle(r)); - unsafe { *client = Box::into_raw(boxed) }; - IdeviceErrorCode::IdeviceSuccess - } - Err(e) => { - // If connection failed, the provider_box was already forgotten, - // so we need to reconstruct it to avoid leak - let _ = unsafe { Box::from_raw(provider) }; - e.into() - } - } -} - -/// Automatically creates and connects to Core Device Proxy, returning a client handle -/// -/// # Arguments -/// * [`provider`] - A UsbmuxdProvider -/// * [`client`] - On success, will be set to point to a newly allocated CoreDeviceProxy handle -/// -/// # Returns -/// An error code indicating success or failure -/// -/// # Safety -/// `provider` must be a valid pointer to a handle allocated by this library -/// `client` must be a valid, non-null pointer to a location where the handle will be stored -#[unsafe(no_mangle)] -pub unsafe extern "C" fn core_device_proxy_connect_usbmuxd( - provider: *mut UsbmuxdProviderHandle, - client: *mut *mut CoreDeviceProxyHandle, -) -> IdeviceErrorCode { - if provider.is_null() { - log::error!("Provider is null"); - return IdeviceErrorCode::InvalidArg; - } - - let res: Result = RUNTIME.block_on(async move { - // Take ownership of the provider (without immediately dropping it) - let provider_box = unsafe { Box::from_raw(provider) }; - - // Get a reference to the inner value - let provider_ref = &provider_box.0; - - // Connect using the reference - let result = CoreDeviceProxy::connect(provider_ref).await; - - // Explicitly keep the provider_box alive until after connect completes - std::mem::forget(provider_box); - result + CoreDeviceProxy::connect(provider_ref).await }); match res { @@ -123,14 +61,15 @@ pub unsafe extern "C" fn core_device_proxy_connect_usbmuxd( /// An error code indicating success or failure /// /// # Safety -/// `socket` must be a valid pointer to a handle allocated by this library +/// `socket` must be a valid pointer to a handle allocated by this library. It is consumed and +/// may not be used again. /// `client` must be a valid, non-null pointer to a location where the handle will be stored #[unsafe(no_mangle)] pub unsafe extern "C" fn core_device_proxy_new( socket: *mut IdeviceHandle, client: *mut *mut CoreDeviceProxyHandle, ) -> IdeviceErrorCode { - if socket.is_null() { + if socket.is_null() || client.is_null() { return IdeviceErrorCode::InvalidArg; } let socket = unsafe { Box::from_raw(socket) }.0; @@ -233,8 +172,8 @@ pub unsafe extern "C" fn core_device_proxy_recv( /// # Arguments /// * [`handle`] - The CoreDeviceProxy handle /// * [`mtu`] - Pointer to store the MTU value -/// * [`address`] - Pointer to store the IP address string (must be at least 16 bytes) -/// * [`netmask`] - Pointer to store the netmask string (must be at least 16 bytes) +/// * [`address`] - Pointer to store the IP address string +/// * [`netmask`] - Pointer to store the netmask string /// /// # Returns /// An error code indicating success or failure @@ -262,9 +201,21 @@ pub unsafe extern "C" fn core_device_proxy_get_client_parameters( *mtu = params.mtu; } + // Allocate both strings, but handle partial failure + let address_cstring = match CString::new(params.address.clone()) { + Ok(s) => s, + Err(_) => return IdeviceErrorCode::InvalidString, + }; + + let netmask_cstring = match CString::new(params.netmask.clone()) { + Ok(s) => s, + Err(_) => return IdeviceErrorCode::InvalidString, + }; + + // Only assign to output pointers after both succeed unsafe { - *address = CString::new(params.address.clone()).unwrap().into_raw(); - *netmask = CString::new(params.netmask.clone()).unwrap().into_raw(); + *address = address_cstring.into_raw(); + *netmask = netmask_cstring.into_raw(); } IdeviceErrorCode::IdeviceSuccess @@ -274,7 +225,7 @@ pub unsafe extern "C" fn core_device_proxy_get_client_parameters( /// /// # Arguments /// * [`handle`] - The CoreDeviceProxy handle -/// * [`address`] - Pointer to store the server address string (must be at least 16 bytes) +/// * [`address`] - Pointer to store the server address string /// /// # Returns /// An error code indicating success or failure @@ -294,9 +245,10 @@ pub unsafe extern "C" fn core_device_proxy_get_server_address( let proxy = unsafe { &(*handle).0 }; unsafe { - *address = CString::new(proxy.handshake.server_address.clone()) - .unwrap() - .into_raw(); + *address = match CString::new(proxy.handshake.server_address.clone()) { + Ok(s) => s.into_raw(), + Err(_) => return IdeviceErrorCode::InvalidString, + }; } IdeviceErrorCode::IdeviceSuccess diff --git a/ffi/src/debug_proxy.rs b/ffi/src/debug_proxy.rs index b3334ff..800c4dd 100644 --- a/ffi/src/debug_proxy.rs +++ b/ffi/src/debug_proxy.rs @@ -4,14 +4,13 @@ use std::ffi::{CStr, CString, c_char}; use std::os::raw::c_int; use std::ptr; +use idevice::ReadWrite; use idevice::debug_proxy::{DebugProxyClient, DebugserverCommand}; -use idevice::tcp::adapter::Adapter; -use crate::core_device_proxy::AdapterHandle; use crate::{IdeviceErrorCode, RUNTIME}; /// Opaque handle to a DebugProxyClient -pub struct DebugProxyAdapterHandle(pub DebugProxyClient); +pub struct DebugProxyHandle(pub DebugProxyClient>); /// Represents a debugserver command #[repr(C)] @@ -56,16 +55,26 @@ pub unsafe extern "C" fn debugserver_command_new( let argv_len = argv_vec.len(); let boxed = Box::new(DebugserverCommandHandle { - name: CString::new(name).unwrap().into_raw(), + name: match CString::new(name) { + Ok(n) => n.into_raw(), + Err(_) => return ptr::null_mut(), + }, argv: if argv_vec.is_empty() { ptr::null_mut() } else { - let mut argv_ptrs: Vec<*mut c_char> = argv_vec + let argv_ptrs: Result, _> = argv_vec .into_iter() - .map(|s| CString::new(s).unwrap().into_raw()) + .map(|s| CString::new(s).map(|cs| cs.into_raw())) .collect(); + + let mut argv_ptrs = match argv_ptrs { + Ok(ptrs) => ptrs, + Err(_) => return ptr::null_mut(), + }; argv_ptrs.shrink_to_fit(); - argv_ptrs.as_mut_ptr() + let ptr = argv_ptrs.as_mut_ptr(); + std::mem::forget(argv_ptrs); + ptr }, argv_count: argv_len, }); @@ -106,7 +115,7 @@ pub unsafe extern "C" fn debugserver_command_free(command: *mut DebugserverComma /// Creates a new DebugProxyClient /// /// # Arguments -/// * [`socket`] - The socket to use for communication +/// * [`socket`] - The socket to use for communication. Any object that supports ReadWrite. /// * [`handle`] - Pointer to store the newly created DebugProxyClient handle /// /// # Returns @@ -116,19 +125,19 @@ pub unsafe extern "C" fn debugserver_command_free(command: *mut DebugserverComma /// `socket` must be a valid pointer to a handle allocated by this library /// `handle` must be a valid pointer to a location where the handle will be stored #[unsafe(no_mangle)] -pub unsafe extern "C" fn debug_proxy_adapter_new( - socket: *mut AdapterHandle, - handle: *mut *mut DebugProxyAdapterHandle, +pub unsafe extern "C" fn debug_proxy_new( + socket: *mut Box, + handle: *mut *mut DebugProxyHandle, ) -> IdeviceErrorCode { if socket.is_null() || handle.is_null() { return IdeviceErrorCode::InvalidArg; } let socket = unsafe { Box::from_raw(socket) }; - let client = DebugProxyClient::new(socket.0); + let client = DebugProxyClient::new(*socket); + let new_handle = DebugProxyHandle(client); - let boxed = Box::new(DebugProxyAdapterHandle(client)); - unsafe { *handle = Box::into_raw(boxed) }; + unsafe { *handle = Box::into_raw(Box::new(new_handle)) }; IdeviceErrorCode::IdeviceSuccess } @@ -140,7 +149,7 @@ pub unsafe extern "C" fn debug_proxy_adapter_new( /// # Safety /// `handle` must be a valid pointer to a handle allocated by this library or NULL #[unsafe(no_mangle)] -pub unsafe extern "C" fn debug_proxy_free(handle: *mut DebugProxyAdapterHandle) { +pub unsafe extern "C" fn debug_proxy_free(handle: *mut DebugProxyHandle) { if !handle.is_null() { let _ = unsafe { Box::from_raw(handle) }; } @@ -161,7 +170,7 @@ pub unsafe extern "C" fn debug_proxy_free(handle: *mut DebugProxyAdapterHandle) /// `response` must be a valid pointer to a location where the string will be stored #[unsafe(no_mangle)] pub unsafe extern "C" fn debug_proxy_send_command( - handle: *mut DebugProxyAdapterHandle, + handle: *mut DebugProxyHandle, command: *mut DebugserverCommandHandle, response: *mut *mut c_char, ) -> IdeviceErrorCode { @@ -192,7 +201,10 @@ pub unsafe extern "C" fn debug_proxy_send_command( match res { Ok(Some(r)) => { - let cstr = CString::new(r).unwrap(); + let cstr = match CString::new(r) { + Ok(c) => c, + Err(_) => return IdeviceErrorCode::InvalidString, + }; unsafe { *response = cstr.into_raw() }; IdeviceErrorCode::IdeviceSuccess } @@ -218,7 +230,7 @@ pub unsafe extern "C" fn debug_proxy_send_command( /// `response` must be a valid pointer to a location where the string will be stored #[unsafe(no_mangle)] pub unsafe extern "C" fn debug_proxy_read_response( - handle: *mut DebugProxyAdapterHandle, + handle: *mut DebugProxyHandle, response: *mut *mut c_char, ) -> IdeviceErrorCode { if handle.is_null() || response.is_null() { @@ -230,7 +242,10 @@ pub unsafe extern "C" fn debug_proxy_read_response( match res { Ok(Some(r)) => { - let cstr = CString::new(r).unwrap(); + let cstr = match CString::new(r) { + Ok(c) => c, + Err(_) => return IdeviceErrorCode::InvalidString, + }; unsafe { *response = cstr.into_raw() }; IdeviceErrorCode::IdeviceSuccess } @@ -257,7 +272,7 @@ pub unsafe extern "C" fn debug_proxy_read_response( /// `data` must be a valid pointer to `len` bytes #[unsafe(no_mangle)] pub unsafe extern "C" fn debug_proxy_send_raw( - handle: *mut DebugProxyAdapterHandle, + handle: *mut DebugProxyHandle, data: *const u8, len: usize, ) -> IdeviceErrorCode { @@ -290,7 +305,7 @@ pub unsafe extern "C" fn debug_proxy_send_raw( /// `response` must be a valid pointer to a location where the string will be stored #[unsafe(no_mangle)] pub unsafe extern "C" fn debug_proxy_read( - handle: *mut DebugProxyAdapterHandle, + handle: *mut DebugProxyHandle, len: usize, response: *mut *mut c_char, ) -> IdeviceErrorCode { @@ -303,7 +318,10 @@ pub unsafe extern "C" fn debug_proxy_read( match res { Ok(r) => { - let cstr = CString::new(r).unwrap(); + let cstr = match CString::new(r) { + Ok(c) => c, + Err(_) => return IdeviceErrorCode::InvalidString, + }; unsafe { *response = cstr.into_raw() }; IdeviceErrorCode::IdeviceSuccess } @@ -328,7 +346,7 @@ pub unsafe extern "C" fn debug_proxy_read( /// `response` must be a valid pointer to a location where the string will be stored #[unsafe(no_mangle)] pub unsafe extern "C" fn debug_proxy_set_argv( - handle: *mut DebugProxyAdapterHandle, + handle: *mut DebugProxyHandle, argv: *const *const c_char, argv_count: usize, response: *mut *mut c_char, @@ -358,7 +376,10 @@ pub unsafe extern "C" fn debug_proxy_set_argv( match res { Ok(r) => { - let cstr = CString::new(r).unwrap(); + let cstr = match CString::new(r) { + Ok(c) => c, + Err(_) => return IdeviceErrorCode::InvalidString, + }; unsafe { *response = cstr.into_raw() }; IdeviceErrorCode::IdeviceSuccess } @@ -377,9 +398,7 @@ pub unsafe extern "C" fn debug_proxy_set_argv( /// # Safety /// `handle` must be a valid pointer #[unsafe(no_mangle)] -pub unsafe extern "C" fn debug_proxy_send_ack( - handle: *mut DebugProxyAdapterHandle, -) -> IdeviceErrorCode { +pub unsafe extern "C" fn debug_proxy_send_ack(handle: *mut DebugProxyHandle) -> IdeviceErrorCode { if handle.is_null() { return IdeviceErrorCode::InvalidArg; } @@ -404,9 +423,7 @@ pub unsafe extern "C" fn debug_proxy_send_ack( /// # Safety /// `handle` must be a valid pointer #[unsafe(no_mangle)] -pub unsafe extern "C" fn debug_proxy_send_nack( - handle: *mut DebugProxyAdapterHandle, -) -> IdeviceErrorCode { +pub unsafe extern "C" fn debug_proxy_send_nack(handle: *mut DebugProxyHandle) -> IdeviceErrorCode { if handle.is_null() { return IdeviceErrorCode::InvalidArg; } @@ -429,39 +446,9 @@ pub unsafe extern "C" fn debug_proxy_send_nack( /// # Safety /// `handle` must be a valid pointer #[unsafe(no_mangle)] -pub unsafe extern "C" fn debug_proxy_set_ack_mode( - handle: *mut DebugProxyAdapterHandle, - enabled: c_int, -) { +pub unsafe extern "C" fn debug_proxy_set_ack_mode(handle: *mut DebugProxyHandle, enabled: c_int) { if !handle.is_null() { let client = unsafe { &mut (*handle).0 }; client.set_ack_mode(enabled != 0); } } - -/// Returns the underlying socket from a DebugProxyClient -/// -/// # Arguments -/// * [`handle`] - The handle to get the socket from -/// * [`adapter`] - The newly allocated ConnectionHandle -/// -/// # Returns -/// An error code indicating success or failure -/// -/// # Safety -/// `handle` must be a valid pointer to a handle allocated by this library or NULL, and never used again -#[unsafe(no_mangle)] -pub unsafe extern "C" fn debug_proxy_adapter_into_inner( - handle: *mut DebugProxyAdapterHandle, - adapter: *mut *mut AdapterHandle, -) -> IdeviceErrorCode { - if handle.is_null() { - return IdeviceErrorCode::InvalidArg; - } - - let client = unsafe { Box::from_raw(handle) }; - let socket_obj = client.0.into_inner(); - let boxed = Box::new(AdapterHandle(socket_obj)); - unsafe { *adapter = Box::into_raw(boxed) }; - IdeviceErrorCode::IdeviceSuccess -} diff --git a/ffi/src/errors.rs b/ffi/src/errors.rs index 53a44c3..375430f 100644 --- a/ffi/src/errors.rs +++ b/ffi/src/errors.rs @@ -33,7 +33,6 @@ pub enum IdeviceErrorCode { ImageNotMounted = -25, Reqwest = -26, InternalError = -27, - Xpc = -28, NsKeyedArchiveError = -29, UnknownAuxValueType = -30, UnknownChannel = -31, @@ -50,9 +49,24 @@ pub enum IdeviceErrorCode { UnknownAfcOpcode = -42, InvalidAfcMagic = -43, AfcMissingAttribute = -44, + ServiceNotFound = -45, + PairingDialogResponsePending = -46, + UserDeniedPairing = -47, + PasswordProtected = -48, + CrashReportMoverBadResponse = -49, + UnknownFrame = -50, + UnknownHttpSetting = -51, + UninitializedStreamId = -52, + UnknownXpcType = -53, + MalformedXpc = -54, + InvalidXpcMagic = -55, + UnexpectedXpcVersion = -56, + InvalidCString = -57, + HttpStreamReset = -58, + HttpGoAway = -59, + // FFI specific bindings AdapterIOFailed = -996, - ServiceNotFound = -997, BufferTooSmall = -998, InvalidString = -999, InvalidArg = -1000, @@ -90,7 +104,6 @@ impl From for IdeviceErrorCode { IdeviceError::ImageNotMounted => IdeviceErrorCode::ImageNotMounted, IdeviceError::Reqwest(_) => IdeviceErrorCode::Reqwest, IdeviceError::InternalError(_) => IdeviceErrorCode::InternalError, - IdeviceError::Xpc(_) => IdeviceErrorCode::Xpc, IdeviceError::NsKeyedArchiveError(_) => IdeviceErrorCode::NsKeyedArchiveError, IdeviceError::UnknownAuxValueType(_) => IdeviceErrorCode::UnknownAuxValueType, IdeviceError::UnknownChannel(_) => IdeviceErrorCode::UnknownChannel, @@ -109,6 +122,25 @@ impl From for IdeviceErrorCode { IdeviceError::UnknownAfcOpcode => IdeviceErrorCode::UnknownAfcOpcode, IdeviceError::InvalidAfcMagic => IdeviceErrorCode::InvalidAfcMagic, IdeviceError::AfcMissingAttribute => IdeviceErrorCode::AfcMissingAttribute, + IdeviceError::ServiceNotFound => IdeviceErrorCode::ServiceNotFound, + IdeviceError::PairingDialogResponsePending => { + IdeviceErrorCode::PairingDialogResponsePending + } + IdeviceError::UserDeniedPairing => IdeviceErrorCode::UserDeniedPairing, + IdeviceError::PasswordProtected => IdeviceErrorCode::PasswordProtected, + IdeviceError::CrashReportMoverBadResponse(_) => { + IdeviceErrorCode::CrashReportMoverBadResponse + } + IdeviceError::UnknownFrame(_) => IdeviceErrorCode::UnknownFrame, + IdeviceError::UnknownHttpSetting(_) => IdeviceErrorCode::UnknownHttpSetting, + IdeviceError::UninitializedStreamId => IdeviceErrorCode::UninitializedStreamId, + IdeviceError::UnknownXpcType(_) => IdeviceErrorCode::UnknownXpcType, + IdeviceError::MalformedXpc => IdeviceErrorCode::MalformedXpc, + IdeviceError::InvalidXpcMagic => IdeviceErrorCode::InvalidXpcMagic, + IdeviceError::UnexpectedXpcVersion => IdeviceErrorCode::UnexpectedXpcVersion, + IdeviceError::InvalidCString => IdeviceErrorCode::InvalidCString, + IdeviceError::HttpStreamReset => IdeviceErrorCode::HttpStreamReset, + IdeviceError::HttpGoAway(_) => IdeviceErrorCode::HttpGoAway, _ => IdeviceErrorCode::InternalError, } } diff --git a/ffi/src/heartbeat.rs b/ffi/src/heartbeat.rs index 822722f..6b30261 100644 --- a/ffi/src/heartbeat.rs +++ b/ffi/src/heartbeat.rs @@ -1,20 +1,17 @@ // Jackson Coxson -use idevice::{IdeviceError, IdeviceService, heartbeat::HeartbeatClient}; - -use crate::{ - IdeviceErrorCode, IdeviceHandle, RUNTIME, - provider::{TcpProviderHandle, UsbmuxdProviderHandle}, +use idevice::{ + IdeviceError, IdeviceService, heartbeat::HeartbeatClient, provider::IdeviceProvider, }; +use crate::{IdeviceErrorCode, IdeviceHandle, RUNTIME, provider::IdeviceProviderHandle}; + pub struct HeartbeatClientHandle(pub HeartbeatClient); -#[allow(non_camel_case_types)] -pub struct plist_t; /// Automatically creates and connects to Installation Proxy, returning a client handle /// /// # Arguments -/// * [`provider`] - A TcpProvider +/// * [`provider`] - An IdeviceProvider /// * [`client`] - On success, will be set to point to a newly allocated InstallationProxyClient handle /// /// # Returns @@ -24,8 +21,8 @@ pub struct plist_t; /// `provider` must be a valid pointer to a handle allocated by this library /// `client` must be a valid, non-null pointer to a location where the handle will be stored #[unsafe(no_mangle)] -pub unsafe extern "C" fn heartbeat_connect_tcp( - provider: *mut TcpProviderHandle, +pub unsafe extern "C" fn heartbeat_connect( + provider: *mut IdeviceProviderHandle, client: *mut *mut HeartbeatClientHandle, ) -> IdeviceErrorCode { if provider.is_null() || client.is_null() { @@ -34,18 +31,9 @@ pub unsafe extern "C" fn heartbeat_connect_tcp( } let res: Result = RUNTIME.block_on(async move { - // Take ownership of the provider (without immediately dropping it) - let provider_box = unsafe { Box::from_raw(provider) }; - - // Get a reference to the inner value - let provider_ref = &provider_box.0; - + let provider_ref: &dyn IdeviceProvider = unsafe { &*(*provider).0 }; // Connect using the reference - let result = HeartbeatClient::connect(provider_ref).await; - - // Explicitly keep the provider_box alive until after connect completes - std::mem::forget(provider_box); - result + HeartbeatClient::connect(provider_ref).await }); match res { @@ -63,53 +51,6 @@ pub unsafe extern "C" fn heartbeat_connect_tcp( } } -/// Automatically creates and connects to Installation Proxy, returning a client handle -/// -/// # Arguments -/// * [`provider`] - A UsbmuxdProvider -/// * [`client`] - On success, will be set to point to a newly allocated InstallationProxyClient handle -/// -/// # Returns -/// An error code indicating success or failure -/// -/// # Safety -/// `provider` must be a valid pointer to a handle allocated by this library -/// `client` must be a valid, non-null pointer to a location where the handle will be stored -#[unsafe(no_mangle)] -pub unsafe extern "C" fn heartbeat_connect_usbmuxd( - provider: *mut UsbmuxdProviderHandle, - client: *mut *mut HeartbeatClientHandle, -) -> IdeviceErrorCode { - if provider.is_null() { - log::error!("Provider is null"); - return IdeviceErrorCode::InvalidArg; - } - - let res: Result = RUNTIME.block_on(async move { - // Take ownership of the provider (without immediately dropping it) - let provider_box = unsafe { Box::from_raw(provider) }; - - // Get a reference to the inner value - let provider_ref = &provider_box.0; - - // Connect using the reference - let result = HeartbeatClient::connect(provider_ref).await; - - // Explicitly keep the provider_box alive until after connect completes - std::mem::forget(provider_box); - result - }); - - match res { - Ok(r) => { - let boxed = Box::new(HeartbeatClientHandle(r)); - unsafe { *client = Box::into_raw(boxed) }; - IdeviceErrorCode::IdeviceSuccess - } - Err(e) => e.into(), - } -} - /// Automatically creates and connects to Installation Proxy, returning a client handle /// /// # Arguments @@ -120,14 +61,15 @@ pub unsafe extern "C" fn heartbeat_connect_usbmuxd( /// An error code indicating success or failure /// /// # Safety -/// `socket` must be a valid pointer to a handle allocated by this library +/// `socket` must be a valid pointer to a handle allocated by this library. The socket is consumed, +/// and may not be used again. /// `client` must be a valid, non-null pointer to a location where the handle will be stored #[unsafe(no_mangle)] pub unsafe extern "C" fn heartbeat_new( socket: *mut IdeviceHandle, client: *mut *mut HeartbeatClientHandle, ) -> IdeviceErrorCode { - if socket.is_null() { + if socket.is_null() || client.is_null() { return IdeviceErrorCode::InvalidArg; } let socket = unsafe { Box::from_raw(socket) }.0; @@ -151,16 +93,12 @@ pub unsafe extern "C" fn heartbeat_new( pub unsafe extern "C" fn heartbeat_send_polo( client: *mut HeartbeatClientHandle, ) -> IdeviceErrorCode { + if client.is_null() { + return IdeviceErrorCode::InvalidArg; + } let res: Result<(), IdeviceError> = RUNTIME.block_on(async move { - // Take ownership of the client - let mut client_box = unsafe { Box::from_raw(client) }; - - // Get a reference to the inner value - let client_ref = &mut client_box.0; - let res = client_ref.send_polo().await; - - std::mem::forget(client_box); - res + let client_ref = unsafe { &mut (*client).0 }; + client_ref.send_polo().await }); match res { Ok(_) => IdeviceErrorCode::IdeviceSuccess, @@ -186,16 +124,12 @@ pub unsafe extern "C" fn heartbeat_get_marco( interval: u64, new_interval: *mut u64, ) -> IdeviceErrorCode { + if client.is_null() || new_interval.is_null() { + return IdeviceErrorCode::InvalidArg; + } let res: Result = RUNTIME.block_on(async move { - // Take ownership of the client - let mut client_box = unsafe { Box::from_raw(client) }; - - // Get a reference to the inner value - let client_ref = &mut client_box.0; - let new = client_ref.get_marco(interval).await; - - std::mem::forget(client_box); - new + let client_ref = unsafe { &mut (*client).0 }; + client_ref.get_marco(interval).await }); match res { Ok(n) => { diff --git a/ffi/src/installation_proxy.rs b/ffi/src/installation_proxy.rs index 108aa82..42c50dc 100644 --- a/ffi/src/installation_proxy.rs +++ b/ffi/src/installation_proxy.rs @@ -2,20 +2,19 @@ use std::ffi::c_void; -use idevice::{IdeviceError, IdeviceService, installation_proxy::InstallationProxyClient}; - -use crate::{ - IdeviceErrorCode, IdeviceHandle, RUNTIME, - provider::{TcpProviderHandle, UsbmuxdProviderHandle}, - util, +use idevice::{ + IdeviceError, IdeviceService, installation_proxy::InstallationProxyClient, + provider::IdeviceProvider, }; +use crate::{IdeviceErrorCode, IdeviceHandle, RUNTIME, provider::IdeviceProviderHandle, util}; + pub struct InstallationProxyClientHandle(pub InstallationProxyClient); /// Automatically creates and connects to Installation Proxy, returning a client handle /// /// # Arguments -/// * [`provider`] - A TcpProvider +/// * [`provider`] - An IdeviceProvider /// * [`client`] - On success, will be set to point to a newly allocated InstallationProxyClient handle /// /// # Returns @@ -26,7 +25,7 @@ pub struct InstallationProxyClientHandle(pub InstallationProxyClient); /// `client` must be a valid, non-null pointer to a location where the handle will be stored #[unsafe(no_mangle)] pub unsafe extern "C" fn installation_proxy_connect_tcp( - provider: *mut TcpProviderHandle, + provider: *mut IdeviceProviderHandle, client: *mut *mut InstallationProxyClientHandle, ) -> IdeviceErrorCode { if provider.is_null() || client.is_null() { @@ -35,70 +34,8 @@ pub unsafe extern "C" fn installation_proxy_connect_tcp( } let res: Result = RUNTIME.block_on(async move { - // Take ownership of the provider (without immediately dropping it) - let provider_box = unsafe { Box::from_raw(provider) }; - - // Get a reference to the inner value - let provider_ref = &provider_box.0; - - // Connect using the reference - let result = InstallationProxyClient::connect(provider_ref).await; - - // Explicitly keep the provider_box alive until after connect completes - std::mem::forget(provider_box); - result - }); - - match res { - Ok(r) => { - let boxed = Box::new(InstallationProxyClientHandle(r)); - unsafe { *client = Box::into_raw(boxed) }; - IdeviceErrorCode::IdeviceSuccess - } - Err(e) => { - // If connection failed, the provider_box was already forgotten, - // so we need to reconstruct it to avoid leak - let _ = unsafe { Box::from_raw(provider) }; - e.into() - } - } -} - -/// Automatically creates and connects to Installation Proxy, returning a client handle -/// -/// # Arguments -/// * [`provider`] - A UsbmuxdProvider -/// * [`client`] - On success, will be set to point to a newly allocated InstallationProxyClient handle -/// -/// # Returns -/// An error code indicating success or failure -/// -/// # Safety -/// `provider` must be a valid pointer to a handle allocated by this library -/// `client` must be a valid, non-null pointer to a location where the handle will be stored -#[unsafe(no_mangle)] -pub unsafe extern "C" fn installation_proxy_connect_usbmuxd( - provider: *mut UsbmuxdProviderHandle, - client: *mut *mut InstallationProxyClientHandle, -) -> IdeviceErrorCode { - if provider.is_null() { - log::error!("Provider is null"); - return IdeviceErrorCode::InvalidArg; - } - - let res: Result = RUNTIME.block_on(async move { - // Take ownership of the provider (without immediately dropping it) - let provider_box = unsafe { Box::from_raw(provider) }; - - // Get a reference to the inner value - let provider_ref = &provider_box.0; - - // Connect using the reference - let result = InstallationProxyClient::connect(provider_ref).await; - - // Explicitly keep the provider_box alive until after connect completes - std::mem::forget(provider_box); - result + let provider_ref: &dyn IdeviceProvider = unsafe { &*(*provider).0 }; + InstallationProxyClient::connect(provider_ref).await }); match res { @@ -121,14 +58,15 @@ pub unsafe extern "C" fn installation_proxy_connect_usbmuxd( /// An error code indicating success or failure /// /// # Safety -/// `socket` must be a valid pointer to a handle allocated by this library +/// `socket` must be a valid pointer to a handle allocated by this library. The socket is consumed, +/// and may not be used again. /// `client` must be a valid, non-null pointer to a location where the handle will be stored #[unsafe(no_mangle)] pub unsafe extern "C" fn installation_proxy_new( socket: *mut IdeviceHandle, client: *mut *mut InstallationProxyClientHandle, ) -> IdeviceErrorCode { - if socket.is_null() { + if socket.is_null() || client.is_null() { return IdeviceErrorCode::InvalidArg; } let socket = unsafe { Box::from_raw(socket) }.0; @@ -201,11 +139,10 @@ pub unsafe extern "C" fn installation_proxy_get_apps( }); match res { - Ok(r) => { + Ok(mut r) => { + let ptr = r.as_mut_ptr(); let len = r.len(); - let boxed_slice = r.into_boxed_slice(); - let ptr = boxed_slice.as_ptr(); - std::mem::forget(boxed_slice); + std::mem::forget(r); unsafe { *out_result = ptr as *mut c_void; @@ -637,11 +574,10 @@ pub unsafe extern "C" fn installation_proxy_browse( }); match res { - Ok(r) => { + Ok(mut r) => { + let ptr = r.as_mut_ptr(); let len = r.len(); - let boxed_slice = r.into_boxed_slice(); - let ptr = boxed_slice.as_ptr(); - std::mem::forget(boxed_slice); + std::mem::forget(r); unsafe { *out_result = ptr as *mut c_void; diff --git a/ffi/src/lib.rs b/ffi/src/lib.rs index f850e44..abb86bb 100644 --- a/ffi/src/lib.rs +++ b/ffi/src/lib.rs @@ -1,25 +1,41 @@ // Jackson Coxson +#[cfg(feature = "tunnel_tcp_stack")] pub mod adapter; +#[cfg(feature = "afc")] pub mod afc; +#[cfg(feature = "amfi")] pub mod amfi; +#[cfg(feature = "core_device_proxy")] pub mod core_device_proxy; +#[cfg(feature = "debug_proxy")] pub mod debug_proxy; mod errors; +#[cfg(feature = "heartbeat")] pub mod heartbeat; +#[cfg(feature = "installation_proxy")] pub mod installation_proxy; +#[cfg(feature = "location_simulation")] pub mod location_simulation; pub mod lockdown; pub mod logging; +#[cfg(feature = "misagent")] pub mod misagent; -pub mod mounter; +#[cfg(feature = "mobile_image_mounter")] +pub mod mobile_image_mounter; mod pairing_file; +#[cfg(feature = "dvt")] pub mod process_control; pub mod provider; +#[cfg(feature = "dvt")] pub mod remote_server; -pub mod remotexpc; -pub mod sbservices; +#[cfg(feature = "xpc")] +pub mod rsd; +#[cfg(feature = "springboardservices")] +pub mod springboardservices; +#[cfg(feature = "syslog_relay")] pub mod syslog_relay; +#[cfg(feature = "usbmuxd")] pub mod usbmuxd; pub mod util; diff --git a/ffi/src/location_simulation.rs b/ffi/src/location_simulation.rs index 7e21c7c..e4ca9fe 100644 --- a/ffi/src/location_simulation.rs +++ b/ffi/src/location_simulation.rs @@ -1,11 +1,11 @@ // Jackson Coxson -use idevice::{dvt::location_simulation::LocationSimulationClient, tcp::adapter::Adapter}; +use idevice::{ReadWrite, dvt::location_simulation::LocationSimulationClient}; -use crate::{IdeviceErrorCode, RUNTIME, remote_server::RemoteServerAdapterHandle}; +use crate::{IdeviceErrorCode, RUNTIME, remote_server::RemoteServerHandle}; /// Opaque handle to a ProcessControlClient -pub struct LocationSimulationAdapterHandle<'a>(pub LocationSimulationClient<'a, Adapter>); +pub struct LocationSimulationHandle<'a>(pub LocationSimulationClient<'a, Box>); /// Creates a new ProcessControlClient from a RemoteServerClient /// @@ -21,8 +21,8 @@ pub struct LocationSimulationAdapterHandle<'a>(pub LocationSimulationClient<'a, /// `handle` must be a valid pointer to a location where the handle will be stored #[unsafe(no_mangle)] pub unsafe extern "C" fn location_simulation_new( - server: *mut RemoteServerAdapterHandle, - handle: *mut *mut LocationSimulationAdapterHandle<'static>, + server: *mut RemoteServerHandle, + handle: *mut *mut LocationSimulationHandle<'static>, ) -> IdeviceErrorCode { if server.is_null() || handle.is_null() { return IdeviceErrorCode::InvalidArg; @@ -33,7 +33,7 @@ pub unsafe extern "C" fn location_simulation_new( match res { Ok(client) => { - let boxed = Box::new(LocationSimulationAdapterHandle(client)); + let boxed = Box::new(LocationSimulationHandle(client)); unsafe { *handle = Box::into_raw(boxed) }; IdeviceErrorCode::IdeviceSuccess } @@ -49,9 +49,7 @@ pub unsafe extern "C" fn location_simulation_new( /// # Safety /// `handle` must be a valid pointer to a handle allocated by this library or NULL #[unsafe(no_mangle)] -pub unsafe extern "C" fn location_simulation_free( - handle: *mut LocationSimulationAdapterHandle<'static>, -) { +pub unsafe extern "C" fn location_simulation_free(handle: *mut LocationSimulationHandle<'static>) { if !handle.is_null() { let _ = unsafe { Box::from_raw(handle) }; } @@ -69,7 +67,7 @@ pub unsafe extern "C" fn location_simulation_free( /// All pointers must be valid or NULL where appropriate #[unsafe(no_mangle)] pub unsafe extern "C" fn location_simulation_clear( - handle: *mut LocationSimulationAdapterHandle<'static>, + handle: *mut LocationSimulationHandle<'static>, ) -> IdeviceErrorCode { if handle.is_null() { return IdeviceErrorCode::InvalidArg; @@ -98,7 +96,7 @@ pub unsafe extern "C" fn location_simulation_clear( /// All pointers must be valid or NULL where appropriate #[unsafe(no_mangle)] pub unsafe extern "C" fn location_simulation_set( - handle: *mut LocationSimulationAdapterHandle<'static>, + handle: *mut LocationSimulationHandle<'static>, latitude: f64, longitude: f64, ) -> IdeviceErrorCode { diff --git a/ffi/src/lockdown.rs b/ffi/src/lockdown.rs index 1034394..66aefe9 100644 --- a/ffi/src/lockdown.rs +++ b/ffi/src/lockdown.rs @@ -2,11 +2,10 @@ use std::ffi::c_void; -use idevice::{IdeviceError, IdeviceService, lockdown::LockdownClient}; +use idevice::{IdeviceError, IdeviceService, lockdown::LockdownClient, provider::IdeviceProvider}; use crate::{ - IdeviceErrorCode, IdeviceHandle, IdevicePairingFile, RUNTIME, - provider::{TcpProviderHandle, UsbmuxdProviderHandle}, + IdeviceErrorCode, IdeviceHandle, IdevicePairingFile, RUNTIME, provider::IdeviceProviderHandle, }; pub struct LockdowndClientHandle(pub LockdownClient); @@ -14,7 +13,7 @@ pub struct LockdowndClientHandle(pub LockdownClient); /// Connects to lockdownd service using TCP provider /// /// # Arguments -/// * [`provider`] - A TcpProvider +/// * [`provider`] - An IdeviceProvider /// * [`client`] - On success, will be set to point to a newly allocated LockdowndClient handle /// /// # Returns @@ -24,8 +23,8 @@ pub struct LockdowndClientHandle(pub LockdownClient); /// `provider` must be a valid pointer to a handle allocated by this library /// `client` must be a valid, non-null pointer to a location where the handle will be stored #[unsafe(no_mangle)] -pub unsafe extern "C" fn lockdownd_connect_tcp( - provider: *mut TcpProviderHandle, +pub unsafe extern "C" fn lockdownd_connect( + provider: *mut IdeviceProviderHandle, client: *mut *mut LockdowndClientHandle, ) -> IdeviceErrorCode { if provider.is_null() || client.is_null() { @@ -34,11 +33,8 @@ pub unsafe extern "C" fn lockdownd_connect_tcp( } let res: Result = RUNTIME.block_on(async move { - let provider_box = unsafe { Box::from_raw(provider) }; - let provider_ref = &provider_box.0; - let result = LockdownClient::connect(provider_ref).await; - std::mem::forget(provider_box); - result + let provider_ref: &dyn IdeviceProvider = unsafe { &*(*provider).0 }; + LockdownClient::connect(provider_ref).await }); match res { @@ -54,57 +50,18 @@ pub unsafe extern "C" fn lockdownd_connect_tcp( } } -/// Connects to lockdownd service using Usbmuxd provider -/// -/// # Arguments -/// * [`provider`] - A UsbmuxdProvider -/// * [`client`] - On success, will be set to point to a newly allocated LockdowndClient handle -/// -/// # Returns -/// An error code indicating success or failure -/// -/// # Safety -/// `provider` must be a valid pointer to a handle allocated by this library -/// `client` must be a valid, non-null pointer to a location where the handle will be stored -#[unsafe(no_mangle)] -pub unsafe extern "C" fn lockdownd_connect_usbmuxd( - provider: *mut UsbmuxdProviderHandle, - client: *mut *mut LockdowndClientHandle, -) -> IdeviceErrorCode { - if provider.is_null() || client.is_null() { - log::error!("Null pointer provided"); - return IdeviceErrorCode::InvalidArg; - } - - let res: Result = RUNTIME.block_on(async move { - let provider_box = unsafe { Box::from_raw(provider) }; - let provider_ref = &provider_box.0; - let result = LockdownClient::connect(provider_ref).await; - std::mem::forget(provider_box); - result - }); - - match res { - Ok(r) => { - let boxed = Box::new(LockdowndClientHandle(r)); - unsafe { *client = Box::into_raw(boxed) }; - IdeviceErrorCode::IdeviceSuccess - } - Err(e) => e.into(), - } -} - /// Creates a new LockdowndClient from an existing Idevice connection /// /// # Arguments -/// * [`socket`] - An IdeviceSocket handle +/// * [`socket`] - An IdeviceSocket handle. /// * [`client`] - On success, will be set to point to a newly allocated LockdowndClient handle /// /// # Returns /// An error code indicating success or failure /// /// # Safety -/// `socket` must be a valid pointer to a handle allocated by this library +/// `socket` must be a valid pointer to a handle allocated by this library. The socket is consumed, +/// and maybe not be used again. /// `client` must be a valid, non-null pointer to a location where the handle will be stored #[unsafe(no_mangle)] pub unsafe extern "C" fn lockdownd_new( @@ -139,15 +96,10 @@ pub unsafe extern "C" fn lockdownd_start_session( pairing_file: *mut IdevicePairingFile, ) -> IdeviceErrorCode { let res: Result<(), IdeviceError> = RUNTIME.block_on(async move { - let mut client_box = unsafe { Box::from_raw(client) }; - let pairing_file = unsafe { Box::from_raw(pairing_file) }; + let client_ref = unsafe { &mut (*client).0 }; + let pairing_file_ref = unsafe { &(*pairing_file).0 }; - let client_ref = &mut client_box.0; - let res = client_ref.start_session(&pairing_file.0).await; - - std::mem::forget(client_box); - std::mem::forget(pairing_file); - res + client_ref.start_session(pairing_file_ref).await }); match res { @@ -187,11 +139,8 @@ pub unsafe extern "C" fn lockdownd_start_service( .into_owned(); let res: Result<(u16, bool), IdeviceError> = RUNTIME.block_on(async move { - let mut client_box = unsafe { Box::from_raw(client) }; - let client_ref = &mut client_box.0; - let res = client_ref.start_service(identifier).await; - std::mem::forget(client_box); - res + let client_ref = unsafe { &mut (*client).0 }; + client_ref.start_service(identifier).await }); match res { @@ -247,11 +196,8 @@ pub unsafe extern "C" fn lockdownd_get_value( }; let res: Result = RUNTIME.block_on(async move { - let mut client_box = unsafe { Box::from_raw(client) }; - let client_ref = &mut client_box.0; - let res = client_ref.get_value(value, domain).await; - std::mem::forget(client_box); - res + let client_ref = unsafe { &mut (*client).0 }; + client_ref.get_value(value, domain).await }); match res { @@ -287,11 +233,8 @@ pub unsafe extern "C" fn lockdownd_get_all_values( } let res: Result = RUNTIME.block_on(async move { - let mut client_box = unsafe { Box::from_raw(client) }; - let client_ref = &mut client_box.0; - let res = client_ref.get_all_values().await; - std::mem::forget(client_box); - res + let client_ref = unsafe { &mut (*client).0 }; + client_ref.get_all_values().await }); match res { diff --git a/ffi/src/misagent.rs b/ffi/src/misagent.rs index 70c9bac..704d58b 100644 --- a/ffi/src/misagent.rs +++ b/ffi/src/misagent.rs @@ -2,19 +2,16 @@ //! //! Provides C-compatible bindings for interacting with the misagent service on iOS devices. -use idevice::{IdeviceError, IdeviceService, misagent::MisagentClient}; +use idevice::{IdeviceError, IdeviceService, misagent::MisagentClient, provider::IdeviceProvider}; -use crate::{ - IdeviceErrorCode, RUNTIME, - provider::{TcpProviderHandle, UsbmuxdProviderHandle}, -}; +use crate::{IdeviceErrorCode, RUNTIME, provider::IdeviceProviderHandle}; pub struct MisagentClientHandle(pub MisagentClient); /// Automatically creates and connects to Misagent, returning a client handle /// /// # Arguments -/// * [`provider`] - A TcpProvider +/// * [`provider`] - An IdeviceProvider /// * [`client`] - On success, will be set to point to a newly allocated MisagentClient handle /// /// # Returns @@ -24,8 +21,8 @@ pub struct MisagentClientHandle(pub MisagentClient); /// `provider` must be a valid pointer to a handle allocated by this library /// `client` must be a valid, non-null pointer to a location where the handle will be stored #[unsafe(no_mangle)] -pub unsafe extern "C" fn misagent_connect_tcp( - provider: *mut TcpProviderHandle, +pub unsafe extern "C" fn misagent_connect( + provider: *mut IdeviceProviderHandle, client: *mut *mut MisagentClientHandle, ) -> IdeviceErrorCode { if provider.is_null() || client.is_null() { @@ -34,70 +31,8 @@ pub unsafe extern "C" fn misagent_connect_tcp( } let res: Result = RUNTIME.block_on(async move { - // Take ownership of the provider (without immediately dropping it) - let provider_box = unsafe { Box::from_raw(provider) }; - - // Get a reference to the inner value - let provider_ref = &provider_box.0; - - // Connect using the reference - let result = MisagentClient::connect(provider_ref).await; - - // Explicitly keep the provider_box alive until after connect completes - std::mem::forget(provider_box); - result - }); - - match res { - Ok(r) => { - let boxed = Box::new(MisagentClientHandle(r)); - unsafe { *client = Box::into_raw(boxed) }; - IdeviceErrorCode::IdeviceSuccess - } - Err(e) => { - // If connection failed, the provider_box was already forgotten, - // so we need to reconstruct it to avoid leak - let _ = unsafe { Box::from_raw(provider) }; - e.into() - } - } -} - -/// Automatically creates and connects to Misagent, returning a client handle -/// -/// # Arguments -/// * [`provider`] - A UsbmuxdProvider -/// * [`client`] - On success, will be set to point to a newly allocated MisagentClient handle -/// -/// # Returns -/// An error code indicating success or failure -/// -/// # Safety -/// `provider` must be a valid pointer to a handle allocated by this library -/// `client` must be a valid, non-null pointer to a location where the handle will be stored -#[unsafe(no_mangle)] -pub unsafe extern "C" fn misagent_connect_usbmuxd( - provider: *mut UsbmuxdProviderHandle, - client: *mut *mut MisagentClientHandle, -) -> IdeviceErrorCode { - if provider.is_null() { - log::error!("Provider is null"); - return IdeviceErrorCode::InvalidArg; - } - - let res: Result = RUNTIME.block_on(async move { - // Take ownership of the provider (without immediately dropping it) - let provider_box = unsafe { Box::from_raw(provider) }; - - // Get a reference to the inner value - let provider_ref = &provider_box.0; - - // Connect using the reference - let result = MisagentClient::connect(provider_ref).await; - - // Explicitly keep the provider_box alive until after connect completes - std::mem::forget(provider_box); - result + let provider_ref: &dyn IdeviceProvider = unsafe { &*(*provider).0 }; + MisagentClient::connect(provider_ref).await }); match res { diff --git a/ffi/src/mounter.rs b/ffi/src/mobile_image_mounter.rs similarity index 67% rename from ffi/src/mounter.rs rename to ffi/src/mobile_image_mounter.rs index 4100418..c20839c 100644 --- a/ffi/src/mounter.rs +++ b/ffi/src/mobile_image_mounter.rs @@ -2,21 +2,19 @@ use std::ffi::c_void; -use idevice::{IdeviceError, IdeviceService, mobile_image_mounter::ImageMounter}; +use idevice::{ + IdeviceError, IdeviceService, mobile_image_mounter::ImageMounter, provider::IdeviceProvider, +}; use plist::Value; -use crate::{ - IdeviceErrorCode, IdeviceHandle, RUNTIME, - provider::{TcpProviderHandle, UsbmuxdProviderHandle}, - util, -}; +use crate::{IdeviceErrorCode, IdeviceHandle, RUNTIME, provider::IdeviceProviderHandle, util}; pub struct ImageMounterHandle(pub ImageMounter); -/// Connects to the Image Mounter service using a TCP provider +/// Connects to the Image Mounter service using a provider /// /// # Arguments -/// * [`provider`] - A TcpProvider +/// * [`provider`] - An IdeviceProvider /// * [`client`] - On success, will be set to point to a newly allocated ImageMounter handle /// /// # Returns @@ -26,8 +24,8 @@ pub struct ImageMounterHandle(pub ImageMounter); /// `provider` must be a valid pointer to a handle allocated by this library /// `client` must be a valid, non-null pointer to a location where the handle will be stored #[unsafe(no_mangle)] -pub unsafe extern "C" fn image_mounter_connect_tcp( - provider: *mut TcpProviderHandle, +pub unsafe extern "C" fn image_mounter_connect( + provider: *mut IdeviceProviderHandle, client: *mut *mut ImageMounterHandle, ) -> IdeviceErrorCode { if provider.is_null() || client.is_null() { @@ -36,11 +34,8 @@ pub unsafe extern "C" fn image_mounter_connect_tcp( } let res: Result = RUNTIME.block_on(async move { - let provider_box = unsafe { Box::from_raw(provider) }; - let provider_ref = &provider_box.0; - let result = ImageMounter::connect(provider_ref).await; - std::mem::forget(provider_box); - result + let provider_ref: &dyn IdeviceProvider = unsafe { &*(*provider).0 }; + ImageMounter::connect(provider_ref).await }); match res { @@ -56,46 +51,6 @@ pub unsafe extern "C" fn image_mounter_connect_tcp( } } -/// Connects to the Image Mounter service using a Usbmuxd provider -/// -/// # Arguments -/// * [`provider`] - A UsbmuxdProvider -/// * [`client`] - On success, will be set to point to a newly allocated ImageMounter handle -/// -/// # Returns -/// An error code indicating success or failure -/// -/// # Safety -/// `provider` must be a valid pointer to a handle allocated by this library -/// `client` must be a valid, non-null pointer to a location where the handle will be stored -#[unsafe(no_mangle)] -pub unsafe extern "C" fn image_mounter_connect_usbmuxd( - provider: *mut UsbmuxdProviderHandle, - client: *mut *mut ImageMounterHandle, -) -> IdeviceErrorCode { - if provider.is_null() { - log::error!("Provider is null"); - return IdeviceErrorCode::InvalidArg; - } - - let res: Result = RUNTIME.block_on(async move { - let provider_box = unsafe { Box::from_raw(provider) }; - let provider_ref = &provider_box.0; - let result = ImageMounter::connect(provider_ref).await; - std::mem::forget(provider_box); - result - }); - - match res { - Ok(r) => { - let boxed = Box::new(ImageMounterHandle(r)); - unsafe { *client = Box::into_raw(boxed) }; - IdeviceErrorCode::IdeviceSuccess - } - Err(e) => e.into(), - } -} - /// Creates a new ImageMounter client from an existing Idevice connection /// /// # Arguments @@ -159,11 +114,8 @@ pub unsafe extern "C" fn image_mounter_copy_devices( devices_len: *mut libc::size_t, ) -> IdeviceErrorCode { let res: Result, IdeviceError> = RUNTIME.block_on(async move { - let mut client_box = unsafe { Box::from_raw(client) }; - let client_ref = &mut client_box.0; - let result = client_ref.copy_devices().await; - std::mem::forget(client_box); - result + let client_ref = unsafe { &mut (*client).0 }; + client_ref.copy_devices().await }); match res { @@ -219,11 +171,8 @@ pub unsafe extern "C" fn image_mounter_lookup_image( }; let res: Result, IdeviceError> = RUNTIME.block_on(async move { - let mut client_box = unsafe { Box::from_raw(client) }; - let client_ref = &mut client_box.0; - let result = client_ref.lookup_image(image_type).await; - std::mem::forget(client_box); - result + let client_ref = unsafe { &mut (*client).0 }; + client_ref.lookup_image(image_type).await }); match res { @@ -279,13 +228,10 @@ pub unsafe extern "C" fn image_mounter_upload_image( let signature_slice = unsafe { std::slice::from_raw_parts(signature, signature_len) }; let res: Result<(), IdeviceError> = RUNTIME.block_on(async move { - let mut client_box = unsafe { Box::from_raw(client) }; - let client_ref = &mut client_box.0; - let result = client_ref + let client_ref = unsafe { &mut (*client).0 }; + client_ref .upload_image(image_type, image_slice, signature_slice.to_vec()) - .await; - std::mem::forget(client_box); - result + .await }); match res { @@ -349,18 +295,15 @@ pub unsafe extern "C" fn image_mounter_mount_image( }; let res: Result<(), IdeviceError> = RUNTIME.block_on(async move { - let mut client_box = unsafe { Box::from_raw(client) }; - let client_ref = &mut client_box.0; - let result = client_ref + let client_ref = unsafe { &mut (*client).0 }; + client_ref .mount_image( image_type, signature_slice.to_vec(), trust_cache, info_plist, ) - .await; - std::mem::forget(client_box); - result + .await }); match res { @@ -397,11 +340,8 @@ pub unsafe extern "C" fn image_mounter_unmount_image( }; let res: Result<(), IdeviceError> = RUNTIME.block_on(async move { - let mut client_box = unsafe { Box::from_raw(client) }; - let client_ref = &mut client_box.0; - let result = client_ref.unmount_image(mount_path).await; - std::mem::forget(client_box); - result + let client_ref = unsafe { &mut (*client).0 }; + client_ref.unmount_image(mount_path).await }); match res { @@ -432,11 +372,8 @@ pub unsafe extern "C" fn image_mounter_query_developer_mode_status( } let res: Result = RUNTIME.block_on(async move { - let mut client_box = unsafe { Box::from_raw(client) }; - let client_ref = &mut client_box.0; - let result = client_ref.query_developer_mode_status().await; - std::mem::forget(client_box); - result + let client_ref = unsafe { &mut (*client).0 }; + client_ref.query_developer_mode_status().await }); match res { @@ -478,13 +415,10 @@ pub unsafe extern "C" fn image_mounter_mount_developer( let signature_slice = unsafe { std::slice::from_raw_parts(signature, signature_len) }; let res: Result<(), IdeviceError> = RUNTIME.block_on(async move { - let mut client_box = unsafe { Box::from_raw(client) }; - let client_ref = &mut client_box.0; - let result = client_ref + let client_ref = unsafe { &mut (*client).0 }; + client_ref .mount_developer(image_slice, signature_slice.to_vec()) - .await; - std::mem::forget(client_box); - result + .await }); match res { @@ -531,13 +465,10 @@ pub unsafe extern "C" fn image_mounter_query_personalization_manifest( let signature_slice = unsafe { std::slice::from_raw_parts(signature, signature_len) }; let res: Result, IdeviceError> = RUNTIME.block_on(async move { - let mut client_box = unsafe { Box::from_raw(client) }; - let client_ref = &mut client_box.0; - let result = client_ref + let client_ref = unsafe { &mut (*client).0 }; + client_ref .query_personalization_manifest(image_type, signature_slice.to_vec()) - .await; - std::mem::forget(client_box); - result + .await }); match res { @@ -590,11 +521,8 @@ pub unsafe extern "C" fn image_mounter_query_nonce( }; let res: Result, IdeviceError> = RUNTIME.block_on(async move { - let mut client_box = unsafe { Box::from_raw(client) }; - let client_ref = &mut client_box.0; - let result = client_ref.query_nonce(image_type).await; - std::mem::forget(client_box); - result + let client_ref = unsafe { &mut (*client).0 }; + client_ref.query_nonce(image_type).await }); match res { @@ -645,13 +573,10 @@ pub unsafe extern "C" fn image_mounter_query_personalization_identifiers( }; let res: Result = RUNTIME.block_on(async move { - let mut client_box = unsafe { Box::from_raw(client) }; - let client_ref = &mut client_box.0; - let result = client_ref + let client_ref = unsafe { &mut (*client).0 }; + client_ref .query_personalization_identifiers(image_type) - .await; - std::mem::forget(client_box); - result + .await }); match res { @@ -679,11 +604,8 @@ pub unsafe extern "C" fn image_mounter_roll_personalization_nonce( client: *mut ImageMounterHandle, ) -> IdeviceErrorCode { let res: Result<(), IdeviceError> = RUNTIME.block_on(async move { - let mut client_box = unsafe { Box::from_raw(client) }; - let client_ref = &mut client_box.0; - let result = client_ref.roll_personalization_nonce().await; - std::mem::forget(client_box); - result + let client_ref = unsafe { &mut (*client).0 }; + client_ref.roll_personalization_nonce().await }); match res { @@ -707,11 +629,8 @@ pub unsafe extern "C" fn image_mounter_roll_cryptex_nonce( client: *mut ImageMounterHandle, ) -> IdeviceErrorCode { let res: Result<(), IdeviceError> = RUNTIME.block_on(async move { - let mut client_box = unsafe { Box::from_raw(client) }; - let client_ref = &mut client_box.0; - let result = client_ref.roll_cryptex_nonce().await; - std::mem::forget(client_box); - result + let client_ref = unsafe { &mut (*client).0 }; + client_ref.roll_cryptex_nonce().await }); match res { @@ -739,10 +658,11 @@ pub unsafe extern "C" fn image_mounter_roll_cryptex_nonce( /// /// # Safety /// All pointers must be valid (except optional ones which can be null) +#[cfg(feature = "tss")] #[unsafe(no_mangle)] -pub unsafe extern "C" fn image_mounter_mount_personalized_usbmuxd( +pub unsafe extern "C" fn image_mounter_mount_personalized( client: *mut ImageMounterHandle, - provider: *mut UsbmuxdProviderHandle, + provider: *mut IdeviceProviderHandle, image: *const u8, image_len: libc::size_t, trust_cache: *const u8, @@ -772,11 +692,9 @@ pub unsafe extern "C" fn image_mounter_mount_personalized_usbmuxd( }; let res: Result<(), IdeviceError> = RUNTIME.block_on(async move { - let mut client_box = unsafe { Box::from_raw(client) }; - let provider_box = unsafe { Box::from_raw(provider) }; - let client_ref = &mut client_box.0; - let provider_ref = &provider_box.0; - let result = client_ref + let client_ref = unsafe { &mut (*client).0 }; + let provider_ref: &dyn IdeviceProvider = unsafe { &*(*provider).0 }; + client_ref .mount_personalized( provider_ref, image_slice.to_vec(), @@ -785,87 +703,7 @@ pub unsafe extern "C" fn image_mounter_mount_personalized_usbmuxd( info_plist, unique_chip_id, ) - .await; - std::mem::forget(client_box); - std::mem::forget(provider_box); - result - }); - - match res { - Ok(_) => IdeviceErrorCode::IdeviceSuccess, - Err(e) => e.into(), - } -} - -/// Mounts a personalized developer image -/// -/// # Arguments -/// * [`client`] - A valid ImageMounter handle -/// * [`provider`] - A valid provider handle -/// * [`image`] - Pointer to the image data -/// * [`image_len`] - Length of the image data -/// * [`trust_cache`] - Pointer to the trust cache data -/// * [`trust_cache_len`] - Length of the trust cache data -/// * [`build_manifest`] - Pointer to the build manifest data -/// * [`build_manifest_len`] - Length of the build manifest data -/// * [`info_plist`] - Pointer to info plist (optional) -/// * [`unique_chip_id`] - The device's unique chip ID -/// -/// # Returns -/// An error code indicating success or failure -/// -/// # Safety -/// All pointers must be valid (except optional ones which can be null) -#[unsafe(no_mangle)] -pub unsafe extern "C" fn image_mounter_mount_personalized_tcp( - client: *mut ImageMounterHandle, - provider: *mut TcpProviderHandle, - image: *const u8, - image_len: libc::size_t, - trust_cache: *const u8, - trust_cache_len: libc::size_t, - build_manifest: *const u8, - build_manifest_len: libc::size_t, - info_plist: *const c_void, - unique_chip_id: u64, -) -> IdeviceErrorCode { - if provider.is_null() || image.is_null() || trust_cache.is_null() || build_manifest.is_null() { - return IdeviceErrorCode::InvalidArg; - } - - let image_slice = unsafe { std::slice::from_raw_parts(image, image_len) }; - let trust_cache_slice = unsafe { std::slice::from_raw_parts(trust_cache, trust_cache_len) }; - let build_manifest_slice = - unsafe { std::slice::from_raw_parts(build_manifest, build_manifest_len) }; - - let info_plist = if !info_plist.is_null() { - Some( - unsafe { Box::from_raw(info_plist as *mut Value) } - .as_ref() - .clone(), - ) - } else { - None - }; - - let res: Result<(), IdeviceError> = RUNTIME.block_on(async move { - let mut client_box = unsafe { Box::from_raw(client) }; - let provider_box = unsafe { Box::from_raw(provider) }; - let client_ref = &mut client_box.0; - let provider_ref = &provider_box.0; - let result = client_ref - .mount_personalized( - provider_ref, - image_slice.to_vec(), - trust_cache_slice.to_vec(), - build_manifest_slice, - info_plist, - unique_chip_id, - ) - .await; - std::mem::forget(client_box); - std::mem::forget(provider_box); - result + .await }); match res { @@ -895,10 +733,11 @@ pub unsafe extern "C" fn image_mounter_mount_personalized_tcp( /// /// # Safety /// All pointers must be valid (except optional ones which can be null) +#[cfg(feature = "tss")] #[unsafe(no_mangle)] -pub unsafe extern "C" fn image_mounter_mount_personalized_usbmuxd_with_callback( +pub unsafe extern "C" fn image_mounter_mount_personalized_with_callback( client: *mut ImageMounterHandle, - provider: *mut UsbmuxdProviderHandle, + provider: *mut IdeviceProviderHandle, image: *const u8, image_len: libc::size_t, trust_cache: *const u8, @@ -930,16 +769,14 @@ pub unsafe extern "C" fn image_mounter_mount_personalized_usbmuxd_with_callback( }; let res: Result<(), IdeviceError> = RUNTIME.block_on(async move { - let mut client_box = unsafe { Box::from_raw(client) }; - let provider_box = unsafe { Box::from_raw(provider) }; - let client_ref = &mut client_box.0; - let provider_ref = &provider_box.0; + let client_ref = unsafe { &mut (*client).0 }; + let provider_ref: &dyn IdeviceProvider = unsafe { &*(*provider).0 }; let callback_wrapper = |((progress, total), context)| async move { callback(progress, total, context); }; - let result = client_ref + client_ref .mount_personalized_with_callback( provider_ref, image_slice.to_vec(), @@ -950,98 +787,7 @@ pub unsafe extern "C" fn image_mounter_mount_personalized_usbmuxd_with_callback( callback_wrapper, context, ) - .await; - std::mem::forget(client_box); - std::mem::forget(provider_box); - result - }); - - match res { - Ok(_) => IdeviceErrorCode::IdeviceSuccess, - Err(e) => e.into(), - } -} - -/// Mounts a personalized developer image with progress callback -/// -/// # Arguments -/// * [`client`] - A valid ImageMounter handle -/// * [`provider`] - A valid provider handle -/// * [`image`] - Pointer to the image data -/// * [`image_len`] - Length of the image data -/// * [`trust_cache`] - Pointer to the trust cache data -/// * [`trust_cache_len`] - Length of the trust cache data -/// * [`build_manifest`] - Pointer to the build manifest data -/// * [`build_manifest_len`] - Length of the build manifest data -/// * [`info_plist`] - Pointer to info plist (optional) -/// * [`unique_chip_id`] - The device's unique chip ID -/// * [`callback`] - Progress callback function -/// * [`context`] - User context to pass to callback -/// -/// # Returns -/// An error code indicating success or failure -/// -/// # Safety -/// All pointers must be valid (except optional ones which can be null) -#[unsafe(no_mangle)] -pub unsafe extern "C" fn image_mounter_mount_personalized_tcp_with_callback( - client: *mut ImageMounterHandle, - provider: *mut TcpProviderHandle, - image: *const u8, - image_len: libc::size_t, - trust_cache: *const u8, - trust_cache_len: libc::size_t, - build_manifest: *const u8, - build_manifest_len: libc::size_t, - info_plist: *const c_void, - unique_chip_id: u64, - callback: extern "C" fn(progress: libc::size_t, total: libc::size_t, context: *mut c_void), - context: *mut c_void, -) -> IdeviceErrorCode { - if provider.is_null() || image.is_null() || trust_cache.is_null() || build_manifest.is_null() { - return IdeviceErrorCode::InvalidArg; - } - - let image_slice = unsafe { std::slice::from_raw_parts(image, image_len) }; - let trust_cache_slice = unsafe { std::slice::from_raw_parts(trust_cache, trust_cache_len) }; - let build_manifest_slice = - unsafe { std::slice::from_raw_parts(build_manifest, build_manifest_len) }; - - let info_plist = if !info_plist.is_null() { - Some( - unsafe { Box::from_raw(info_plist as *mut Value) } - .as_ref() - .clone(), - ) - } else { - None - }; - - let res: Result<(), IdeviceError> = RUNTIME.block_on(async move { - let mut client_box = unsafe { Box::from_raw(client) }; - let provider_box = unsafe { Box::from_raw(provider) }; - let client_ref = &mut client_box.0; - let provider_ref = &provider_box.0; - - let callback_wrapper = |((progress, total), context)| async move { - callback(progress, total, context); - }; - - let result = client_ref - .mount_personalized_with_callback( - provider_ref, - image_slice.to_vec(), - trust_cache_slice.to_vec(), - build_manifest_slice, - info_plist, - unique_chip_id, - callback_wrapper, - context, - ) - .await; - std::mem::forget(client_box); - std::mem::forget(provider_box); - result + .await }); match res { diff --git a/ffi/src/process_control.rs b/ffi/src/process_control.rs index d8eae35..ef69ad0 100644 --- a/ffi/src/process_control.rs +++ b/ffi/src/process_control.rs @@ -2,13 +2,13 @@ use std::ffi::{CStr, c_char}; -use idevice::{dvt::process_control::ProcessControlClient, tcp::adapter::Adapter}; +use idevice::{ReadWrite, dvt::process_control::ProcessControlClient}; use plist::{Dictionary, Value}; -use crate::{IdeviceErrorCode, RUNTIME, remote_server::RemoteServerAdapterHandle}; +use crate::{IdeviceErrorCode, RUNTIME, remote_server::RemoteServerHandle}; /// Opaque handle to a ProcessControlClient -pub struct ProcessControlAdapterHandle<'a>(pub ProcessControlClient<'a, Adapter>); +pub struct ProcessControlHandle<'a>(pub ProcessControlClient<'a, Box>); /// Creates a new ProcessControlClient from a RemoteServerClient /// @@ -24,8 +24,8 @@ pub struct ProcessControlAdapterHandle<'a>(pub ProcessControlClient<'a, Adapter> /// `handle` must be a valid pointer to a location where the handle will be stored #[unsafe(no_mangle)] pub unsafe extern "C" fn process_control_new( - server: *mut RemoteServerAdapterHandle, - handle: *mut *mut ProcessControlAdapterHandle<'static>, + server: *mut RemoteServerHandle, + handle: *mut *mut ProcessControlHandle<'static>, ) -> IdeviceErrorCode { if server.is_null() || handle.is_null() { return IdeviceErrorCode::InvalidArg; @@ -36,7 +36,7 @@ pub unsafe extern "C" fn process_control_new( match res { Ok(client) => { - let boxed = Box::new(ProcessControlAdapterHandle(client)); + let boxed = Box::new(ProcessControlHandle(client)); unsafe { *handle = Box::into_raw(boxed) }; IdeviceErrorCode::IdeviceSuccess } @@ -52,7 +52,7 @@ pub unsafe extern "C" fn process_control_new( /// # Safety /// `handle` must be a valid pointer to a handle allocated by this library or NULL #[unsafe(no_mangle)] -pub unsafe extern "C" fn process_control_free(handle: *mut ProcessControlAdapterHandle<'static>) { +pub unsafe extern "C" fn process_control_free(handle: *mut ProcessControlHandle<'static>) { if !handle.is_null() { let _ = unsafe { Box::from_raw(handle) }; } @@ -76,7 +76,7 @@ pub unsafe extern "C" fn process_control_free(handle: *mut ProcessControlAdapter /// All pointers must be valid or NULL where appropriate #[unsafe(no_mangle)] pub unsafe extern "C" fn process_control_launch_app( - handle: *mut ProcessControlAdapterHandle<'static>, + handle: *mut ProcessControlHandle<'static>, bundle_id: *const c_char, env_vars: *const *const c_char, env_vars_count: usize, @@ -159,7 +159,7 @@ pub unsafe extern "C" fn process_control_launch_app( /// `handle` must be a valid pointer to a handle allocated by this library #[unsafe(no_mangle)] pub unsafe extern "C" fn process_control_kill_app( - handle: *mut ProcessControlAdapterHandle<'static>, + handle: *mut ProcessControlHandle<'static>, pid: u64, ) -> IdeviceErrorCode { if handle.is_null() { @@ -188,7 +188,7 @@ pub unsafe extern "C" fn process_control_kill_app( /// `handle` must be a valid pointer to a handle allocated by this library #[unsafe(no_mangle)] pub unsafe extern "C" fn process_control_disable_memory_limit( - handle: *mut ProcessControlAdapterHandle<'static>, + handle: *mut ProcessControlHandle<'static>, pid: u64, ) -> IdeviceErrorCode { if handle.is_null() { diff --git a/ffi/src/provider.rs b/ffi/src/provider.rs index 19dacac..0fe624b 100644 --- a/ffi/src/provider.rs +++ b/ffi/src/provider.rs @@ -1,13 +1,12 @@ // Jackson Coxson -use idevice::provider::{TcpProvider, UsbmuxdProvider}; +use idevice::provider::{IdeviceProvider, TcpProvider, UsbmuxdProvider}; use std::ffi::CStr; use std::os::raw::c_char; use crate::{IdeviceErrorCode, usbmuxd::UsbmuxdAddrHandle, util}; -pub struct TcpProviderHandle(pub TcpProvider); -pub struct UsbmuxdProviderHandle(pub UsbmuxdProvider); +pub struct IdeviceProviderHandle(pub Box); /// Creates a TCP provider for idevice /// @@ -25,13 +24,18 @@ pub struct UsbmuxdProviderHandle(pub UsbmuxdProvider); /// `pairing_file` must never be used again /// `label` must be a valid Cstr /// `provider` must be a valid, non-null pointer to a location where the handle will be stored +#[cfg(feature = "tcp")] #[unsafe(no_mangle)] pub unsafe extern "C" fn idevice_tcp_provider_new( ip: *const libc::sockaddr, pairing_file: *mut crate::pairing_file::IdevicePairingFile, label: *const c_char, - provider: *mut *mut TcpProviderHandle, + provider: *mut *mut IdeviceProviderHandle, ) -> IdeviceErrorCode { + if ip.is_null() || label.is_null() || provider.is_null() { + return IdeviceErrorCode::InvalidArg; + } + let addr = match util::c_addr_to_rust(ip) { Ok(i) => i, Err(e) => { @@ -53,23 +57,23 @@ pub unsafe extern "C" fn idevice_tcp_provider_new( label, }; - let boxed = Box::new(TcpProviderHandle(t)); + let boxed = Box::new(IdeviceProviderHandle(Box::new(t))); unsafe { *provider = Box::into_raw(boxed) }; IdeviceErrorCode::IdeviceSuccess } -/// Frees a TcpProvider handle +/// Frees an IdeviceProvider handle /// /// # Arguments /// * [`provider`] - The provider handle to free /// /// # Safety -/// `provider` must be a valid pointer to a TcpProvider handle that was allocated by this library, -/// or NULL (in which case this function does nothing) +/// `provider` must be a valid pointer to a IdeviceProvider handle that was allocated this library +/// or NULL (in which case this function does nothing) #[unsafe(no_mangle)] -pub unsafe extern "C" fn tcp_provider_free(provider: *mut TcpProviderHandle) { +pub unsafe extern "C" fn idevice_provider_free(provider: *mut IdeviceProviderHandle) { if !provider.is_null() { - log::debug!("Freeing TCP provider"); + log::debug!("Freeing provider"); unsafe { drop(Box::from_raw(provider)) }; } } @@ -81,7 +85,6 @@ pub unsafe extern "C" fn tcp_provider_free(provider: *mut TcpProviderHandle) { /// * [`tag`] - The tag returned in usbmuxd responses /// * [`udid`] - The UDID of the device to connect to /// * [`device_id`] - The muxer ID of the device to connect to -/// * [`pairing_file`] - The pairing file handle to use /// * [`label`] - The label to use with the connection /// * [`provider`] - A pointer to a newly allocated provider /// @@ -91,7 +94,6 @@ pub unsafe extern "C" fn tcp_provider_free(provider: *mut TcpProviderHandle) { /// # Safety /// `addr` must be a valid pointer to UsbmuxdAddrHandle created by this library, and never used again /// `udid` must be a valid CStr -/// `pairing_file` must never be used again /// `label` must be a valid Cstr /// `provider` must be a valid, non-null pointer to a location where the handle will be stored #[unsafe(no_mangle)] @@ -101,21 +103,25 @@ pub unsafe extern "C" fn usbmuxd_provider_new( udid: *const c_char, device_id: u32, label: *const c_char, - provider: *mut *mut UsbmuxdProviderHandle, + provider: *mut *mut IdeviceProviderHandle, ) -> IdeviceErrorCode { + if addr.is_null() || udid.is_null() || label.is_null() || provider.is_null() { + return IdeviceErrorCode::InvalidArg; + } + let udid = match unsafe { CStr::from_ptr(udid) }.to_str() { Ok(u) => u.to_string(), Err(e) => { log::error!("Invalid UDID string: {e:?}"); - return IdeviceErrorCode::InvalidArgument; + return IdeviceErrorCode::InvalidString; } }; let label = match unsafe { CStr::from_ptr(label) }.to_str() { Ok(l) => l.to_string(), Err(e) => { - log::error!("Invalid UDID string: {e:?}"); - return IdeviceErrorCode::InvalidArgument; + log::error!("Invalid label string: {e:?}"); + return IdeviceErrorCode::InvalidArg; } }; @@ -129,23 +135,8 @@ pub unsafe extern "C" fn usbmuxd_provider_new( label, }; - let boxed = Box::new(UsbmuxdProviderHandle(p)); + let boxed = Box::new(IdeviceProviderHandle(Box::new(p))); unsafe { *provider = Box::into_raw(boxed) }; IdeviceErrorCode::IdeviceSuccess } - -/// Frees a UsbmuxdProvider handle -/// -/// # Arguments -/// * [`provider`] - The provider handle to free -/// -/// # Safety -/// `provider` must be a valid pointer to a UsbmuxdProvider handle that was allocated by this library, -/// or NULL (in which case this function does nothing) -#[unsafe(no_mangle)] -pub unsafe extern "C" fn usbmuxd_provider_free(provider: *mut UsbmuxdProviderHandle) { - if !provider.is_null() { - unsafe { drop(Box::from_raw(provider)) }; - } -} diff --git a/ffi/src/remote_server.rs b/ffi/src/remote_server.rs index f39615c..1570dd5 100644 --- a/ffi/src/remote_server.rs +++ b/ffi/src/remote_server.rs @@ -1,46 +1,45 @@ // Jackson Coxson -use crate::core_device_proxy::AdapterHandle; use crate::{IdeviceErrorCode, RUNTIME}; -use idevice::IdeviceError; use idevice::dvt::remote_server::RemoteServerClient; -use idevice::tcp::adapter::Adapter; +use idevice::{IdeviceError, ReadWrite}; /// Opaque handle to a RemoteServerClient -pub struct RemoteServerAdapterHandle(pub RemoteServerClient); +pub struct RemoteServerHandle(pub RemoteServerClient>); /// Creates a new RemoteServerClient from a ReadWrite connection /// /// # Arguments -/// * [`connection`] - The connection to use for communication +/// * [`socket`] - The connection to use for communication, an object that implements ReadWrite /// * [`handle`] - Pointer to store the newly created RemoteServerClient handle /// /// # Returns /// An error code indicating success or failure /// /// # Safety -/// `connection` must be a valid pointer to a handle allocated by this library +/// `socket` must be a valid pointer to a handle allocated by this library /// `handle` must be a valid pointer to a location where the handle will be stored #[unsafe(no_mangle)] -pub unsafe extern "C" fn remote_server_adapter_new( - adapter: *mut crate::core_device_proxy::AdapterHandle, - handle: *mut *mut RemoteServerAdapterHandle, +pub unsafe extern "C" fn remote_server_new( + socket: *mut Box, + handle: *mut *mut RemoteServerHandle, ) -> IdeviceErrorCode { - if adapter.is_null() { + if socket.is_null() { return IdeviceErrorCode::InvalidArg; } - let connection = unsafe { Box::from_raw(adapter) }; + let connection = unsafe { Box::from_raw(socket) }; - let res: Result, IdeviceError> = RUNTIME.block_on(async move { - let mut client = RemoteServerClient::new(connection.0); - client.read_message(0).await?; // Until Message has bindings, we'll do the first read - Ok(client) - }); + let res: Result>, IdeviceError> = + RUNTIME.block_on(async move { + let mut client = RemoteServerClient::new(*connection); + client.read_message(0).await?; // Until Message has bindings, we'll do the first read + Ok(client) + }); match res { Ok(client) => { - let boxed = Box::new(RemoteServerAdapterHandle(client)); + let boxed = Box::new(RemoteServerHandle(client)); unsafe { *handle = Box::into_raw(boxed) }; IdeviceErrorCode::IdeviceSuccess } @@ -56,35 +55,8 @@ pub unsafe extern "C" fn remote_server_adapter_new( /// # Safety /// `handle` must be a valid pointer to a handle allocated by this library or NULL #[unsafe(no_mangle)] -pub unsafe extern "C" fn remote_server_free(handle: *mut RemoteServerAdapterHandle) { +pub unsafe extern "C" fn remote_server_free(handle: *mut RemoteServerHandle) { if !handle.is_null() { let _ = unsafe { Box::from_raw(handle) }; } } - -/// Returns the underlying connection from a RemoteServerClient -/// -/// # Arguments -/// * [`handle`] - The handle to get the connection from -/// * [`connection`] - The newly allocated ConnectionHandle -/// -/// # Returns -/// An error code indicating success or failure -/// -/// # Safety -/// `handle` must be a valid pointer to a handle allocated by this library or NULL, and never used again -#[unsafe(no_mangle)] -pub unsafe extern "C" fn remote_server_adapter_into_inner( - handle: *mut RemoteServerAdapterHandle, - connection: *mut *mut AdapterHandle, -) -> IdeviceErrorCode { - if handle.is_null() || connection.is_null() { - return IdeviceErrorCode::InvalidArg; - } - - let server = unsafe { Box::from_raw(handle) }; - let connection_obj = server.0.into_inner(); - let boxed = Box::new(AdapterHandle(connection_obj)); - unsafe { *connection = Box::into_raw(boxed) }; - IdeviceErrorCode::IdeviceSuccess -} diff --git a/ffi/src/remotexpc.rs b/ffi/src/remotexpc.rs deleted file mode 100644 index dcd7f8e..0000000 --- a/ffi/src/remotexpc.rs +++ /dev/null @@ -1,288 +0,0 @@ -// Jackson Coxson - -use std::ffi::{CStr, CString, c_char}; - -use idevice::{tcp::adapter::Adapter, xpc::XPCDevice}; - -use crate::{IdeviceErrorCode, RUNTIME, core_device_proxy::AdapterHandle}; - -/// Opaque handle to an XPCDevice -pub struct XPCDeviceAdapterHandle(pub XPCDevice); - -/// Opaque handle to an XPCService -#[repr(C)] -pub struct XPCServiceHandle { - pub entitlement: *mut c_char, - pub port: u16, - pub uses_remote_xpc: bool, - pub features: *mut *mut c_char, - pub features_count: usize, - pub service_version: i64, -} - -impl XPCServiceHandle { - pub fn new( - entitlement: *mut c_char, - port: u16, - uses_remote_xpc: bool, - features: *mut *mut c_char, - features_count: usize, - service_version: i64, - ) -> Self { - Self { - entitlement, - port, - uses_remote_xpc, - features, - features_count, - service_version, - } - } -} - -/// Creates a new XPCDevice from an adapter -/// -/// # Arguments -/// * [`adapter`] - The adapter to use for communication -/// * [`device`] - Pointer to store the newly created XPCDevice handle -/// -/// # Returns -/// An error code indicating success or failure -/// -/// # Safety -/// `adapter` must be a valid pointer to a handle allocated by this library -/// `device` must be a valid pointer to a location where the handle will be stored -#[unsafe(no_mangle)] -pub unsafe extern "C" fn xpc_device_new( - adapter: *mut AdapterHandle, - device: *mut *mut XPCDeviceAdapterHandle, -) -> IdeviceErrorCode { - if adapter.is_null() || device.is_null() { - return IdeviceErrorCode::InvalidArg; - } - - let adapter = unsafe { Box::from_raw(adapter) }; - let res = RUNTIME.block_on(async move { XPCDevice::new(adapter.0).await }); - - match res { - // we have to unwrap res to avoid just getting a reference - Ok(_) => { - let boxed = Box::new(XPCDeviceAdapterHandle(res.unwrap())); - unsafe { *device = Box::into_raw(boxed) }; - IdeviceErrorCode::IdeviceSuccess - } - Err(e) => e.into(), - } -} - -/// Frees an XPCDevice handle -/// -/// # Arguments -/// * [`handle`] - The handle to free -/// -/// # Safety -/// `handle` must be a valid pointer to a handle allocated by this library or NULL -#[unsafe(no_mangle)] -pub unsafe extern "C" fn xpc_device_free(handle: *mut XPCDeviceAdapterHandle) { - if !handle.is_null() { - let _ = unsafe { Box::from_raw(handle) }; - } -} - -/// Gets a service by name from the XPCDevice -/// -/// # Arguments -/// * [`handle`] - The XPCDevice handle -/// * [`service_name`] - The name of the service to get -/// * [`service`] - Pointer to store the newly created XPCService handle -/// -/// # Returns -/// An error code indicating success or failure -/// -/// # Safety -/// `handle` must be a valid pointer to a handle allocated by this library -/// `service_name` must be a valid null-terminated C string -/// `service` must be a valid pointer to a location where the handle will be stored -#[unsafe(no_mangle)] -pub unsafe extern "C" fn xpc_device_get_service( - handle: *mut XPCDeviceAdapterHandle, - service_name: *const c_char, - service: *mut *mut XPCServiceHandle, -) -> IdeviceErrorCode { - if handle.is_null() || service_name.is_null() || service.is_null() { - return IdeviceErrorCode::InvalidArg; - } - - let device = unsafe { &(*handle).0 }; - let service_name_cstr = unsafe { CStr::from_ptr(service_name) }; - let service_name = match service_name_cstr.to_str() { - Ok(s) => s, - Err(_) => return IdeviceErrorCode::InvalidArg, - }; - - let xpc_service = match device.services.get(service_name) { - Some(s) => s, - None => return IdeviceErrorCode::ServiceNotFound, - }; - - // Convert features to C array if they exist - let (features_ptr, features_count) = if let Some(features) = &xpc_service.features { - let mut features_vec: Vec<*mut c_char> = features - .iter() - .map(|f| CString::new(f.as_str()).unwrap().into_raw()) - .collect(); - features_vec.shrink_to_fit(); - - let mut features_vec = Box::new(features_vec); - let features_ptr = features_vec.as_mut_ptr(); - let features_len = features_vec.len(); - - Box::leak(features_vec); - (features_ptr, features_len) - } else { - (std::ptr::null_mut(), 0) - }; - - let boxed = Box::new(XPCServiceHandle { - entitlement: CString::new(xpc_service.entitlement.as_str()) - .unwrap() - .into_raw(), - port: xpc_service.port, - uses_remote_xpc: xpc_service.uses_remote_xpc, - features: features_ptr, - features_count, - service_version: xpc_service.service_version.unwrap_or(-1), - }); - - unsafe { *service = Box::into_raw(boxed) }; - IdeviceErrorCode::IdeviceSuccess -} - -/// Returns the adapter in the RemoteXPC Device -/// -/// # Arguments -/// * [`handle`] - The handle to get the adapter from -/// * [`adapter`] - The newly allocated AdapterHandle -/// -/// # Safety -/// `handle` must be a valid pointer to a handle allocated by this library or NULL, and never used again -#[unsafe(no_mangle)] -pub unsafe extern "C" fn xpc_device_adapter_into_inner( - handle: *mut XPCDeviceAdapterHandle, - adapter: *mut *mut AdapterHandle, -) -> IdeviceErrorCode { - if handle.is_null() { - return IdeviceErrorCode::InvalidArg; - } - let device = unsafe { Box::from_raw(handle).0 }; - let adapter_obj = device.into_inner(); - let boxed = Box::new(AdapterHandle(adapter_obj)); - unsafe { *adapter = Box::into_raw(boxed) }; - IdeviceErrorCode::IdeviceSuccess -} - -/// Frees an XPCService handle -/// -/// # Arguments -/// * [`handle`] - The handle to free -/// -/// # Safety -/// `handle` must be a valid pointer to a handle allocated by this library or NULL -#[unsafe(no_mangle)] -pub unsafe extern "C" fn xpc_service_free(handle: *mut XPCServiceHandle) { - if !handle.is_null() { - let handle = unsafe { Box::from_raw(handle) }; - - // Free the entitlement string - if !handle.entitlement.is_null() { - let _ = unsafe { CString::from_raw(handle.entitlement) }; - } - - // Free the features array - if !handle.features.is_null() { - for i in 0..handle.features_count { - let feature_ptr = unsafe { *handle.features.add(i) }; - if !feature_ptr.is_null() { - let _ = unsafe { CString::from_raw(feature_ptr) }; - } - } - let _ = unsafe { - Vec::from_raw_parts( - handle.features, - handle.features_count, - handle.features_count, - ) - }; - } - } -} - -/// Gets the list of available service names -/// -/// # Arguments -/// * [`handle`] - The XPCDevice handle -/// * [`names`] - Pointer to store the array of service names -/// * [`count`] - Pointer to store the number of services -/// -/// # Returns -/// An error code indicating success or failure -/// -/// # Safety -/// `handle` must be a valid pointer to a handle allocated by this library -/// `names` must be a valid pointer to a location where the array will be stored -/// `count` must be a valid pointer to a location where the count will be stored -#[unsafe(no_mangle)] -pub unsafe extern "C" fn xpc_device_get_service_names( - handle: *mut XPCDeviceAdapterHandle, - names: *mut *mut *mut c_char, - count: *mut usize, -) -> IdeviceErrorCode { - if handle.is_null() || names.is_null() || count.is_null() { - return IdeviceErrorCode::InvalidArg; - } - - let device = unsafe { &(*handle).0 }; - let service_names: Vec = device - .services - .keys() - .map(|k| CString::new(k.as_str()).unwrap()) - .collect(); - - let mut name_ptrs: Vec<*mut c_char> = service_names.into_iter().map(|s| s.into_raw()).collect(); - - if name_ptrs.is_empty() { - unsafe { - *names = std::ptr::null_mut(); - *count = 0; - } - } else { - name_ptrs.shrink_to_fit(); - unsafe { - *names = name_ptrs.as_mut_ptr(); - *count = name_ptrs.len(); - } - std::mem::forget(name_ptrs); - } - - IdeviceErrorCode::IdeviceSuccess -} - -/// Frees a list of service names -/// -/// # Arguments -/// * [`names`] - The array of service names to free -/// * [`count`] - The number of services in the array -/// -/// # Safety -/// `names` must be a valid pointer to an array of `count` C strings -#[unsafe(no_mangle)] -pub unsafe extern "C" fn xpc_device_free_service_names(names: *mut *mut c_char, count: usize) { - if !names.is_null() && count > 0 { - let names_vec = unsafe { Vec::from_raw_parts(names, count, count) }; - for name in names_vec { - if !name.is_null() { - let _ = unsafe { CString::from_raw(name) }; - } - } - } -} diff --git a/ffi/src/rsd.rs b/ffi/src/rsd.rs new file mode 100644 index 0000000..44c41ec --- /dev/null +++ b/ffi/src/rsd.rs @@ -0,0 +1,502 @@ +//! Remote Service Discovery (RSD) Client Bindings +//! +//! Provides C-compatible bindings for RSD handshake and service discovery on iOS devices. + +use std::ffi::{CStr, CString}; +use std::ptr; + +use idevice::{ReadWrite, rsd::RsdHandshake}; + +use crate::{IdeviceErrorCode, RUNTIME}; + +/// Opaque handle to an RsdHandshake +pub struct RsdHandshakeHandle(pub RsdHandshake); + +/// C-compatible representation of an RSD service +#[repr(C)] +pub struct CRsdService { + /// Service name (null-terminated string) + pub name: *mut libc::c_char, + /// Required entitlement (null-terminated string) + pub entitlement: *mut libc::c_char, + /// Port number + pub port: u16, + /// Whether service uses remote XPC + pub uses_remote_xpc: bool, + /// Number of features + pub features_count: libc::size_t, + /// Array of feature strings + pub features: *mut *mut libc::c_char, + /// Service version (-1 if not present) + pub service_version: i64, +} + +/// Array of RSD services returned by rsd_get_services +#[repr(C)] +pub struct CRsdServiceArray { + /// Array of services + pub services: *mut CRsdService, + /// Number of services in array + pub count: libc::size_t, +} + +/// Creates a new RSD handshake from a ReadWrite connection +/// +/// # Arguments +/// * [`socket`] - The connection to use for communication +/// * [`handle`] - Pointer to store the newly created RsdHandshake handle +/// +/// # Returns +/// An error code indicating success or failure +/// +/// # Safety +/// `socket` must be a valid pointer to a ReadWrite handle allocated by this library +/// `handle` must be a valid pointer to a location where the handle will be stored +#[unsafe(no_mangle)] +pub unsafe extern "C" fn rsd_handshake_new( + socket: *mut Box, + handle: *mut *mut RsdHandshakeHandle, +) -> IdeviceErrorCode { + if socket.is_null() || handle.is_null() { + return IdeviceErrorCode::InvalidArg; + } + + let connection = unsafe { Box::from_raw(socket) }; + + let res = RUNTIME.block_on(async move { RsdHandshake::new(*connection).await }); + + match res { + Ok(handshake) => { + let boxed = Box::new(RsdHandshakeHandle(handshake)); + unsafe { *handle = Box::into_raw(boxed) }; + IdeviceErrorCode::IdeviceSuccess + } + Err(e) => e.into(), + } +} + +/// Gets the protocol version from the RSD handshake +/// +/// # Arguments +/// * [`handle`] - A valid RsdHandshake handle +/// * [`version`] - Pointer to store the protocol version +/// +/// # Returns +/// An error code indicating success or failure +/// +/// # Safety +/// `handle` must be a valid pointer to a handle allocated by this library +/// `version` must be a valid pointer to store the version +#[unsafe(no_mangle)] +pub unsafe extern "C" fn rsd_get_protocol_version( + handle: *mut RsdHandshakeHandle, + version: *mut libc::size_t, +) -> IdeviceErrorCode { + if handle.is_null() || version.is_null() { + return IdeviceErrorCode::InvalidArg; + } + + unsafe { + *version = (*handle).0.protocol_version; + } + IdeviceErrorCode::IdeviceSuccess +} + +/// Gets the UUID from the RSD handshake +/// +/// # Arguments +/// * [`handle`] - A valid RsdHandshake handle +/// * [`uuid`] - Pointer to store the UUID string (caller must free with rsd_free_string) +/// +/// # Returns +/// An error code indicating success or failure +/// +/// # Safety +/// `handle` must be a valid pointer to a handle allocated by this library +/// `uuid` must be a valid pointer to store the string pointer +#[unsafe(no_mangle)] +pub unsafe extern "C" fn rsd_get_uuid( + handle: *mut RsdHandshakeHandle, + uuid: *mut *mut libc::c_char, +) -> IdeviceErrorCode { + if handle.is_null() || uuid.is_null() { + return IdeviceErrorCode::InvalidArg; + } + + let uuid_str = &unsafe { &*handle }.0.uuid; + match CString::new(uuid_str.as_str()) { + Ok(c_str) => { + unsafe { *uuid = c_str.into_raw() }; + IdeviceErrorCode::IdeviceSuccess + } + Err(_) => IdeviceErrorCode::InvalidString, + } +} + +/// Gets all available services from the RSD handshake +/// +/// # Arguments +/// * [`handle`] - A valid RsdHandshake handle +/// * [`services`] - Pointer to store the services array +/// +/// # Returns +/// An error code indicating success or failure +/// +/// # Safety +/// `handle` must be a valid pointer to a handle allocated by this library +/// `services` must be a valid pointer to store the services array +/// Caller must free the returned array with rsd_free_services +#[unsafe(no_mangle)] +pub unsafe extern "C" fn rsd_get_services( + handle: *mut RsdHandshakeHandle, + services: *mut *mut CRsdServiceArray, +) -> IdeviceErrorCode { + if handle.is_null() || services.is_null() { + return IdeviceErrorCode::InvalidArg; + } + + let handshake = unsafe { &*handle }; + let service_map = &handshake.0.services; + + let count = service_map.len(); + let mut c_services = Vec::with_capacity(count); + + for (name, service) in service_map.iter() { + // Convert name + let c_name = match CString::new(name.as_str()) { + Ok(s) => s.into_raw(), + Err(_) => continue, + }; + + // Convert entitlement + let c_entitlement = match CString::new(service.entitlement.as_str()) { + Ok(s) => s.into_raw(), + Err(_) => { + unsafe { + let _ = CString::from_raw(c_name); + } + continue; + } + }; + + // Convert features + let (features_ptr, features_count) = match &service.features { + Some(features) => { + let mut c_features = Vec::with_capacity(features.len()); + + for feature in features { + match CString::new(feature.as_str()) { + Ok(s) => c_features.push(s.into_raw()), + Err(_) => { + // Clean up already allocated features + for f in c_features { + unsafe { + let _ = CString::from_raw(f); + } + } + // Return early to avoid the move below + return IdeviceErrorCode::InvalidString; + } + } + } + + // All features converted successfully + let boxed = c_features.into_boxed_slice(); + let ptr = Box::into_raw(boxed) as *mut *mut libc::c_char; + (ptr, features.len()) + } + None => (ptr::null_mut(), 0), + }; + + let c_service = CRsdService { + name: c_name, + entitlement: c_entitlement, + port: service.port, + uses_remote_xpc: service.uses_remote_xpc, + features_count, + features: features_ptr, + service_version: service.service_version.unwrap_or(-1), + }; + + c_services.push(c_service); + } + + let boxed_services = c_services.into_boxed_slice(); + let array = Box::new(CRsdServiceArray { + services: Box::into_raw(boxed_services) as *mut CRsdService, + count, + }); + + unsafe { *services = Box::into_raw(array) }; + IdeviceErrorCode::IdeviceSuccess +} + +/// Checks if a specific service is available +/// +/// # Arguments +/// * [`handle`] - A valid RsdHandshake handle +/// * [`service_name`] - Name of the service to check for +/// * [`available`] - Pointer to store the availability result +/// +/// # Returns +/// An error code indicating success or failure +/// +/// # Safety +/// `handle` must be a valid pointer to a handle allocated by this library +/// `service_name` must be a valid C string +/// `available` must be a valid pointer to store the boolean result +#[unsafe(no_mangle)] +pub unsafe extern "C" fn rsd_service_available( + handle: *mut RsdHandshakeHandle, + service_name: *const libc::c_char, + available: *mut bool, +) -> IdeviceErrorCode { + if handle.is_null() || service_name.is_null() || available.is_null() { + return IdeviceErrorCode::InvalidArg; + } + + let name = match unsafe { CStr::from_ptr(service_name) }.to_str() { + Ok(s) => s, + Err(_) => return IdeviceErrorCode::InvalidArg, + }; + + let handshake = unsafe { &*handle }; + unsafe { *available = handshake.0.services.contains_key(name) }; + + IdeviceErrorCode::IdeviceSuccess +} + +/// Gets information about a specific service +/// +/// # Arguments +/// * [`handle`] - A valid RsdHandshake handle +/// * [`service_name`] - Name of the service to get info for +/// * [`service_info`] - Pointer to store the service information +/// +/// # Returns +/// An error code indicating success or failure +/// +/// # Safety +/// `handle` must be a valid pointer to a handle allocated by this library +/// `service_name` must be a valid C string +/// `service_info` must be a valid pointer to store the service info +/// Caller must free the returned service with rsd_free_service +#[unsafe(no_mangle)] +pub unsafe extern "C" fn rsd_get_service_info( + handle: *mut RsdHandshakeHandle, + service_name: *const libc::c_char, + service_info: *mut *mut CRsdService, +) -> IdeviceErrorCode { + if handle.is_null() || service_name.is_null() || service_info.is_null() { + return IdeviceErrorCode::InvalidArg; + } + + let name = match unsafe { CStr::from_ptr(service_name) }.to_str() { + Ok(s) => s, + Err(_) => return IdeviceErrorCode::InvalidArg, + }; + + let handshake = unsafe { &*handle }; + let service = match handshake.0.services.get(name) { + Some(s) => s, + None => return IdeviceErrorCode::ServiceNotFound, + }; + + // Convert service to C representation (similar to rsd_get_services logic) + let c_name = match CString::new(name) { + Ok(s) => s.into_raw(), + Err(_) => return IdeviceErrorCode::InvalidString, + }; + + let c_entitlement = match CString::new(service.entitlement.as_str()) { + Ok(s) => s.into_raw(), + Err(_) => { + unsafe { + let _ = CString::from_raw(c_name); + } + return IdeviceErrorCode::InvalidString; + } + }; + + // Convert features + let (features_ptr, features_count) = match &service.features { + Some(features) => { + let mut c_features = Vec::with_capacity(features.len()); + for feature in features { + match CString::new(feature.as_str()) { + Ok(s) => c_features.push(s.into_raw()), + Err(_) => { + // Clean up already allocated features + for f in c_features { + unsafe { + let _ = CString::from_raw(f); + } + } + // Clean up name and entitlement + unsafe { + let _ = CString::from_raw(c_name); + let _ = CString::from_raw(c_entitlement); + } + return IdeviceErrorCode::InvalidString; + } + } + } + let boxed = c_features.into_boxed_slice(); + ( + Box::into_raw(boxed) as *mut *mut libc::c_char, + features.len(), + ) + } + None => (ptr::null_mut(), 0), + }; + + let c_service = Box::new(CRsdService { + name: c_name, + entitlement: c_entitlement, + port: service.port, + uses_remote_xpc: service.uses_remote_xpc, + features_count, + features: features_ptr, + service_version: service.service_version.unwrap_or(-1), + }); + + unsafe { *service_info = Box::into_raw(c_service) }; + IdeviceErrorCode::IdeviceSuccess +} + +/// Frees a string returned by RSD functions +/// +/// # Arguments +/// * [`string`] - The string to free +/// +/// # Safety +/// Must only be called with strings returned from RSD functions +#[unsafe(no_mangle)] +pub unsafe extern "C" fn rsd_free_string(string: *mut libc::c_char) { + if !string.is_null() { + unsafe { + let _ = CString::from_raw(string); + } + } +} + +/// Frees a single service returned by rsd_get_service_info +/// +/// # Arguments +/// * [`service`] - The service to free +/// +/// # Safety +/// Must only be called with services returned from rsd_get_service_info +#[unsafe(no_mangle)] +pub unsafe extern "C" fn rsd_free_service(service: *mut CRsdService) { + if service.is_null() { + return; + } + + let service_box = unsafe { Box::from_raw(service) }; + + // Free name + if !service_box.name.is_null() { + unsafe { + let _ = CString::from_raw(service_box.name); + } + } + + // Free entitlement + if !service_box.entitlement.is_null() { + unsafe { + let _ = CString::from_raw(service_box.entitlement); + } + } + + // Free features array + if !service_box.features.is_null() && service_box.features_count > 0 { + let features_slice = unsafe { + std::slice::from_raw_parts_mut(service_box.features, service_box.features_count) + }; + for feature_ptr in features_slice.iter() { + if !feature_ptr.is_null() { + unsafe { + let _ = CString::from_raw(*feature_ptr); + } + } + } + unsafe { + let _ = Box::from_raw(features_slice); + } + } +} + +/// Frees services array returned by rsd_get_services +/// +/// # Arguments +/// * [`services`] - The services array to free +/// +/// # Safety +/// Must only be called with arrays returned from rsd_get_services +#[unsafe(no_mangle)] +pub unsafe extern "C" fn rsd_free_services(services: *mut CRsdServiceArray) { + if services.is_null() { + return; + } + + let services_box = unsafe { Box::from_raw(services) }; + + if !services_box.services.is_null() && services_box.count > 0 { + let services_slice = + unsafe { std::slice::from_raw_parts_mut(services_box.services, services_box.count) }; + + // Free each service + for service in services_slice.iter() { + // Free name + if !service.name.is_null() { + unsafe { + let _ = CString::from_raw(service.name); + } + } + + // Free entitlement + if !service.entitlement.is_null() { + unsafe { + let _ = CString::from_raw(service.entitlement); + } + } + + // Free features array + if !service.features.is_null() && service.features_count > 0 { + let features_slice = unsafe { + std::slice::from_raw_parts_mut(service.features, service.features_count) + }; + for feature_ptr in features_slice.iter() { + if !feature_ptr.is_null() { + unsafe { + let _ = CString::from_raw(*feature_ptr); + } + } + } + unsafe { + let _ = Box::from_raw(features_slice); + } + } + } + + unsafe { + let _ = Box::from_raw(services_slice); + } + } +} + +/// Frees an RSD handshake handle +/// +/// # Arguments +/// * [`handle`] - The handle to free +/// +/// # Safety +/// `handle` must be a valid pointer to a handle allocated by this library, +/// or NULL (in which case this function does nothing) +#[unsafe(no_mangle)] +pub unsafe extern "C" fn rsd_handshake_free(handle: *mut RsdHandshakeHandle) { + if !handle.is_null() { + let _ = unsafe { Box::from_raw(handle) }; + } +} diff --git a/ffi/src/sbservices.rs b/ffi/src/springboardservices.rs similarity index 64% rename from ffi/src/sbservices.rs rename to ffi/src/springboardservices.rs index 657c4d2..67d6533 100644 --- a/ffi/src/sbservices.rs +++ b/ffi/src/springboardservices.rs @@ -1,18 +1,18 @@ use std::ffi::{CStr, c_void}; -use idevice::{IdeviceError, IdeviceService, springboardservices::SpringBoardServicesClient}; - -use crate::{ - IdeviceErrorCode, IdeviceHandle, RUNTIME, - provider::{TcpProviderHandle, UsbmuxdProviderHandle}, +use idevice::{ + IdeviceError, IdeviceService, provider::IdeviceProvider, + springboardservices::SpringBoardServicesClient, }; +use crate::{IdeviceErrorCode, IdeviceHandle, RUNTIME, provider::IdeviceProviderHandle}; + pub struct SpringBoardServicesClientHandle(pub SpringBoardServicesClient); -/// Connects to the Springboard service using a TCP provider +/// Connects to the Springboard service using a provider /// /// # Arguments -/// * [`provider`] - A TcpProvider +/// * [`provider`] - An IdeviceProvider /// * [`client`] - On success, will be set to point to a newly allocated SpringBoardServicesClient handle /// /// # Returns @@ -22,8 +22,8 @@ pub struct SpringBoardServicesClientHandle(pub SpringBoardServicesClient); /// `provider` must be a valid pointer to a handle allocated by this library /// `client` must be a valid, non-null pointer to a location where the handle will be stored #[unsafe(no_mangle)] -pub unsafe extern "C" fn springboard_services_connect_tcp( - provider: *mut TcpProviderHandle, +pub unsafe extern "C" fn springboard_services_connect( + provider: *mut IdeviceProviderHandle, client: *mut *mut SpringBoardServicesClientHandle, ) -> IdeviceErrorCode { if provider.is_null() || client.is_null() { @@ -32,18 +32,8 @@ pub unsafe extern "C" fn springboard_services_connect_tcp( } let res: Result = RUNTIME.block_on(async move { - // Take ownership of the provider (without immediately dropping it) - let provider_box = unsafe { Box::from_raw(provider) }; - - // Get a reference to the inner value - let provider_ref = &provider_box.0; - - // Connect using the reference - let result = SpringBoardServicesClient::connect(provider_ref).await; - - // Explicitly keep the provider_box alive until after connect completes - std::mem::forget(provider_box); - result + let provider_ref: &dyn IdeviceProvider = unsafe { &*(*provider).0 }; + SpringBoardServicesClient::connect(provider_ref).await }); match res { @@ -61,53 +51,6 @@ pub unsafe extern "C" fn springboard_services_connect_tcp( } } -/// Connects to the Springboard service using a usbmuxd provider -/// -/// # Arguments -/// * [`provider`] - A UsbmuxdProvider -/// * [`client`] - On success, will be set to point to a newly allocated SpringBoardServicesClient handle -/// -/// # Returns -/// An error code indicating success or failure -/// -/// # Safety -/// `provider` must be a valid pointer to a handle allocated by this library -/// `client` must be a valid, non-null pointer to a location where the handle will be stored -#[unsafe(no_mangle)] -pub unsafe extern "C" fn springboard_services_connect_usbmuxd( - provider: *mut UsbmuxdProviderHandle, - client: *mut *mut SpringBoardServicesClientHandle, -) -> IdeviceErrorCode { - if provider.is_null() { - log::error!("Provider is null"); - return IdeviceErrorCode::InvalidArg; - } - - let res: Result = RUNTIME.block_on(async move { - // Take ownership of the provider (without immediately dropping it) - let provider_box = unsafe { Box::from_raw(provider) }; - - // Get a reference to the inner value - let provider_ref = &provider_box.0; - - // Connect using the reference - let result = SpringBoardServicesClient::connect(provider_ref).await; - - // Explicitly keep the provider_box alive until after connect completes - std::mem::forget(provider_box); - result - }); - - match res { - Ok(r) => { - let boxed = Box::new(SpringBoardServicesClientHandle(r)); - unsafe { *client = Box::into_raw(boxed) }; - IdeviceErrorCode::IdeviceSuccess - } - Err(e) => e.into(), - } -} - /// Creates a new SpringBoardServices client from an existing Idevice connection /// /// # Arguments @@ -118,7 +61,8 @@ pub unsafe extern "C" fn springboard_services_connect_usbmuxd( /// An error code indicating success or failure /// /// # Safety -/// `socket` must be a valid pointer to a handle allocated by this library +/// `socket` must be a valid pointer to a handle allocated by this library. The socket is consumed, +/// and may not be used again. /// `client` must be a valid, non-null pointer to a location where the handle will be stored #[unsafe(no_mangle)] pub unsafe extern "C" fn springboard_services_new( diff --git a/ffi/src/syslog_relay.rs b/ffi/src/syslog_relay.rs index be30b72..0c00c18 100644 --- a/ffi/src/syslog_relay.rs +++ b/ffi/src/syslog_relay.rs @@ -1,26 +1,26 @@ use std::os::raw::c_char; -use idevice::{syslog_relay::SyslogRelayClient, IdeviceError, IdeviceService}; - -use crate::{ - provider::TcpProviderHandle, IdeviceErrorCode, RUNTIME +use idevice::{ + IdeviceError, IdeviceService, provider::IdeviceProvider, syslog_relay::SyslogRelayClient, }; +use crate::{IdeviceErrorCode, RUNTIME, provider::IdeviceProviderHandle}; + pub struct SyslogRelayClientHandle(pub SyslogRelayClient); /// Automatically creates and connects to syslog relay, returning a client handle /// /// # Arguments -/// * [`provider`] - A TcpProvider +/// * [`provider`] - An IdeviceProvider /// * [`client`] - On success, will be set to point to a newly allocated SyslogRelayClient handle /// /// # Safety /// `provider` must be a valid pointer to a handle allocated by this library /// `client` must be a valid, non-null pointer to a location where the handle will be stored #[unsafe(no_mangle)] -pub extern "C" fn syslog_relay_connect_tcp( - provider: *mut TcpProviderHandle, - client: *mut *mut SyslogRelayClientHandle +pub unsafe extern "C" fn syslog_relay_connect_tcp( + provider: *mut IdeviceProviderHandle, + client: *mut *mut SyslogRelayClientHandle, ) -> IdeviceErrorCode { if provider.is_null() { log::error!("Null pointer provided"); @@ -28,14 +28,8 @@ pub extern "C" fn syslog_relay_connect_tcp( } let res: Result = RUNTIME.block_on(async move { - let provider_box = unsafe { Box::from_raw(provider) }; - - let provider_ref = &provider_box.0; - - let result = SyslogRelayClient::connect(provider_ref).await; - - std::mem::forget(provider_box); - result + let provider_ref: &dyn IdeviceProvider = unsafe { &*(*provider).0 }; + SyslogRelayClient::connect(provider_ref).await }); match res { @@ -62,9 +56,7 @@ pub extern "C" fn syslog_relay_connect_tcp( /// `handle` must be a valid pointer to the handle that was allocated by this library, /// or NULL (in which case this function does nothing) #[unsafe(no_mangle)] -pub extern "C" fn syslog_relay_client_free( - handle: *mut SyslogRelayClientHandle -) { +pub unsafe extern "C" fn syslog_relay_client_free(handle: *mut SyslogRelayClientHandle) { if !handle.is_null() { log::debug!("Freeing syslog relay client"); let _ = unsafe { Box::from_raw(handle) }; @@ -76,12 +68,12 @@ pub extern "C" fn syslog_relay_client_free( /// # Arguments /// * [`client`] - The SyslogRelayClient handle /// * [`log_message`] - On success a newly allocated cstring will be set to point to the log message -/// +/// /// # Safety /// `client` must be a valid pointer to a handle allocated by this library /// `log_message` must be a valid, non-null pointer to a location where the log message will be stored #[unsafe(no_mangle)] -pub extern "C" fn syslog_relay_next( +pub unsafe extern "C" fn syslog_relay_next( client: *mut SyslogRelayClientHandle, log_message: *mut *mut c_char, ) -> IdeviceErrorCode { @@ -89,20 +81,15 @@ pub extern "C" fn syslog_relay_next( return IdeviceErrorCode::InvalidArg; } - let res = RUNTIME.block_on(async { - unsafe { &mut *client } - .0 - .next() - .await - }); + let res = RUNTIME.block_on(async { unsafe { &mut *client }.0.next().await }); match res { Ok(log) => { use std::ffi::CString; - + // null bytes are a curse in C, so just use spaces let safe_log = log.replace('\0', " "); - + match CString::new(safe_log) { Ok(c_string) => { unsafe { *log_message = c_string.into_raw() }; @@ -116,4 +103,4 @@ pub extern "C" fn syslog_relay_next( } Err(e) => e.into(), } -} \ No newline at end of file +} diff --git a/idevice/src/tcp/stream.rs b/idevice/src/tcp/stream.rs index 54dfde7..6de84e4 100644 --- a/idevice/src/tcp/stream.rs +++ b/idevice/src/tcp/stream.rs @@ -31,6 +31,17 @@ impl<'a> AdapterStream<'a> { pub async fn close(&mut self) -> Result<(), std::io::Error> { self.adapter.close(self.host_port).await } + + /// Sends data to the target + pub async fn psh(&mut self, payload: &[u8]) -> Result<(), std::io::Error> { + self.adapter.queue_send(payload, self.host_port)?; + self.adapter.write_buffer_flush().await?; + Ok(()) + } + + pub async fn recv(&mut self) -> Result, std::io::Error> { + self.adapter.recv(self.host_port).await + } } impl AsyncRead for AdapterStream<'_> {