Refactor FFI bindings

This commit is contained in:
Jackson Coxson
2025-05-26 12:52:23 -06:00
parent 15261861e0
commit fa88c2c87d
22 changed files with 1005 additions and 1476 deletions

View File

@@ -5,7 +5,7 @@ edition = "2024"
[dependencies] [dependencies]
idevice = { path = "../idevice", features = ["full"] } idevice = { path = "../idevice" }
log = "0.4.26" log = "0.4.26"
simplelog = "0.12.2" simplelog = "0.12.2"
once_cell = "1.21.1" once_cell = "1.21.1"
@@ -14,6 +14,55 @@ libc = "0.2.171"
plist = "1.7.1" plist = "1.7.1"
plist_plus = { version = "0.2.6", features = ["dynamic"] } 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] [build-dependencies]
cbindgen = "0.28.0" cbindgen = "0.28.0"

View File

@@ -2,31 +2,39 @@
use std::ffi::{CString, c_char}; use std::ffi::{CString, c_char};
use idevice::tcp::stream::AdapterStream;
use crate::core_device_proxy::AdapterHandle; use crate::core_device_proxy::AdapterHandle;
use crate::{IdeviceErrorCode, RUNTIME}; use crate::{IdeviceErrorCode, RUNTIME};
pub struct AdapterStreamHandle<'a>(pub AdapterStream<'a>);
/// Connects the adapter to a specific port /// Connects the adapter to a specific port
/// ///
/// # Arguments /// # Arguments
/// * [`handle`] - The adapter handle /// * [`adapter_handle`] - The adapter handle
/// * [`port`] - The port to connect to /// * [`port`] - The port to connect to
/// * [`stream_handle`] - A pointer to allocate the new stream to
/// ///
/// # Returns /// # Returns
/// An error code indicating success or failure /// An error code indicating success or failure
/// ///
/// # Safety /// # 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)] #[unsafe(no_mangle)]
pub unsafe extern "C" fn adapter_connect( pub unsafe extern "C" fn adapter_connect(
handle: *mut AdapterHandle, adapter_handle: *mut AdapterHandle,
port: u16, port: u16,
stream_handle: *mut *mut AdapterStreamHandle,
) -> IdeviceErrorCode { ) -> IdeviceErrorCode {
if handle.is_null() { if adapter_handle.is_null() || stream_handle.is_null() {
return IdeviceErrorCode::InvalidArg; return IdeviceErrorCode::InvalidArg;
} }
let adapter = unsafe { &mut (*handle).0 }; let adapter = unsafe { &mut (*adapter_handle).0 };
let res = RUNTIME.block_on(async move { adapter.connect(port).await }); let res = RUNTIME.block_on(async move { AdapterStream::connect(adapter, port).await });
match res { match res {
Ok(_) => IdeviceErrorCode::IdeviceSuccess, Ok(_) => IdeviceErrorCode::IdeviceSuccess,
@@ -79,7 +87,7 @@ pub unsafe extern "C" fn adapter_pcap(
/// Closes the adapter connection /// Closes the adapter connection
/// ///
/// # Arguments /// # Arguments
/// * [`handle`] - The adapter handle /// * [`handle`] - The adapter stream handle
/// ///
/// # Returns /// # Returns
/// An error code indicating success or failure /// An error code indicating success or failure
@@ -87,7 +95,7 @@ pub unsafe extern "C" fn adapter_pcap(
/// # Safety /// # 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
#[unsafe(no_mangle)] #[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() { if handle.is_null() {
return IdeviceErrorCode::InvalidArg; 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 /// `data` must be a valid pointer to at least `length` bytes
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
pub unsafe extern "C" fn adapter_send( pub unsafe extern "C" fn adapter_send(
handle: *mut AdapterHandle, handle: *mut AdapterStreamHandle,
data: *const u8, data: *const u8,
length: usize, length: usize,
) -> IdeviceErrorCode { ) -> IdeviceErrorCode {
@@ -158,7 +166,7 @@ pub unsafe extern "C" fn adapter_send(
/// `length` must be a valid pointer to a usize /// `length` must be a valid pointer to a usize
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
pub unsafe extern "C" fn adapter_recv( pub unsafe extern "C" fn adapter_recv(
handle: *mut AdapterHandle, handle: *mut AdapterStreamHandle,
data: *mut u8, data: *mut u8,
length: *mut usize, length: *mut usize,
max_length: usize, max_length: usize,

View File

@@ -1,23 +1,19 @@
// Jackson Coxson // Jackson Coxson
use std::ffi::c_void;
use idevice::{ use idevice::{
IdeviceError, IdeviceService, IdeviceError, IdeviceService,
afc::{AfcClient, DeviceInfo, FileInfo}, afc::{AfcClient, DeviceInfo, FileInfo},
provider::IdeviceProvider,
}; };
use crate::{ use crate::{IdeviceErrorCode, IdeviceHandle, RUNTIME, provider::IdeviceProviderHandle};
IdeviceErrorCode, IdeviceHandle, RUNTIME,
provider::{TcpProviderHandle, UsbmuxdProviderHandle},
};
pub struct AfcClientHandle(pub AfcClient); pub struct AfcClientHandle(pub AfcClient);
/// Connects to the AFC service using a TCP provider /// Connects to the AFC service using a TCP provider
/// ///
/// # Arguments /// # Arguments
/// * [`provider`] - A TcpProvider /// * [`provider`] - An IdeviceProvider
/// * [`client`] - On success, will be set to point to a newly allocated AfcClient handle /// * [`client`] - On success, will be set to point to a newly allocated AfcClient handle
/// ///
/// # Returns /// # Returns
@@ -27,8 +23,8 @@ pub struct AfcClientHandle(pub AfcClient);
/// `provider` must be a valid pointer to a handle allocated by this library /// `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 /// `client` must be a valid, non-null pointer to a location where the handle will be stored
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
pub unsafe extern "C" fn afc_client_connect_tcp( pub unsafe extern "C" fn afc_client_connect(
provider: *mut TcpProviderHandle, provider: *mut IdeviceProviderHandle,
client: *mut *mut AfcClientHandle, client: *mut *mut AfcClientHandle,
) -> IdeviceErrorCode { ) -> IdeviceErrorCode {
if provider.is_null() || client.is_null() { if provider.is_null() || client.is_null() {
@@ -36,55 +32,10 @@ pub unsafe extern "C" fn afc_client_connect_tcp(
return IdeviceErrorCode::InvalidArg; return IdeviceErrorCode::InvalidArg;
} }
let res: Result<AfcClient, IdeviceError> = RUNTIME.block_on(async move { let res = RUNTIME.block_on(async {
let provider_box = unsafe { Box::from_raw(provider) }; let provider_ref: &dyn IdeviceProvider = unsafe { &*(*provider).0 };
let provider_ref = &provider_box.0;
let result = AfcClient::connect(provider_ref).await;
std::mem::forget(provider_box);
result
});
match res { AfcClient::connect(provider_ref).await
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<AfcClient, IdeviceError> = 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
}); });
match res { match res {
@@ -114,7 +65,7 @@ pub unsafe extern "C" fn afc_client_new(
socket: *mut IdeviceHandle, socket: *mut IdeviceHandle,
client: *mut *mut AfcClientHandle, client: *mut *mut AfcClientHandle,
) -> IdeviceErrorCode { ) -> IdeviceErrorCode {
if socket.is_null() { if socket.is_null() || client.is_null() {
return IdeviceErrorCode::InvalidArg; return IdeviceErrorCode::InvalidArg;
} }
let socket = unsafe { Box::from_raw(socket) }.0; let socket = unsafe { Box::from_raw(socket) }.0;
@@ -231,7 +182,7 @@ pub unsafe extern "C" fn afc_make_directory(
client: *mut AfcClientHandle, client: *mut AfcClientHandle,
path: *const libc::c_char, path: *const libc::c_char,
) -> IdeviceErrorCode { ) -> IdeviceErrorCode {
if path.is_null() { if client.is_null() || path.is_null() {
return IdeviceErrorCode::InvalidArg; 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 res: Result<(), IdeviceError> = RUNTIME.block_on(async move {
let mut client_box = unsafe { Box::from_raw(client) }; let client_ref = unsafe { &mut (*client).0 };
let client_ref = &mut client_box.0; client_ref.mk_dir(path).await
let result = client_ref.mk_dir(path).await;
std::mem::forget(client_box);
result
}); });
match res { match res {
@@ -286,7 +234,7 @@ pub unsafe extern "C" fn afc_get_file_info(
path: *const libc::c_char, path: *const libc::c_char,
info: *mut AfcFileInfo, info: *mut AfcFileInfo,
) -> IdeviceErrorCode { ) -> IdeviceErrorCode {
if path.is_null() || info.is_null() { if client.is_null() || path.is_null() || info.is_null() {
return IdeviceErrorCode::InvalidArg; return IdeviceErrorCode::InvalidArg;
} }
@@ -297,11 +245,8 @@ pub unsafe extern "C" fn afc_get_file_info(
}; };
let res: Result<FileInfo, IdeviceError> = RUNTIME.block_on(async move { let res: Result<FileInfo, IdeviceError> = RUNTIME.block_on(async move {
let mut client_box = unsafe { Box::from_raw(client) }; let client_ref = unsafe { &mut (*client).0 };
let client_ref = &mut client_box.0; client_ref.get_file_info(path).await
let result = client_ref.get_file_info(path).await;
std::mem::forget(client_box);
result
}); });
match res { match res {
@@ -380,16 +325,13 @@ pub unsafe extern "C" fn afc_get_device_info(
client: *mut AfcClientHandle, client: *mut AfcClientHandle,
info: *mut AfcDeviceInfo, info: *mut AfcDeviceInfo,
) -> IdeviceErrorCode { ) -> IdeviceErrorCode {
if info.is_null() { if client.is_null() || info.is_null() {
return IdeviceErrorCode::InvalidArg; return IdeviceErrorCode::InvalidArg;
} }
let res: Result<DeviceInfo, IdeviceError> = RUNTIME.block_on(async move { let res: Result<DeviceInfo, IdeviceError> = RUNTIME.block_on(async move {
let mut client_box = unsafe { Box::from_raw(client) }; let client_ref = unsafe { &mut (*client).0 };
let client_ref = &mut client_box.0; client_ref.get_device_info().await
let result = client_ref.get_device_info().await;
std::mem::forget(client_box);
result
}); });
match res { match res {
@@ -441,7 +383,7 @@ pub unsafe extern "C" fn afc_remove_path(
client: *mut AfcClientHandle, client: *mut AfcClientHandle,
path: *const libc::c_char, path: *const libc::c_char,
) -> IdeviceErrorCode { ) -> IdeviceErrorCode {
if path.is_null() { if client.is_null() || path.is_null() {
return IdeviceErrorCode::InvalidArg; 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 res: Result<(), IdeviceError> = RUNTIME.block_on(async move {
let mut client_box = unsafe { Box::from_raw(client) }; let client_ref = unsafe { &mut (*client).0 };
let client_ref = &mut client_box.0; client_ref.remove(path).await
let result = client_ref.remove(path).await;
std::mem::forget(client_box);
result
}); });
match res { match res {
@@ -482,7 +421,7 @@ pub unsafe extern "C" fn afc_remove_path_and_contents(
client: *mut AfcClientHandle, client: *mut AfcClientHandle,
path: *const libc::c_char, path: *const libc::c_char,
) -> IdeviceErrorCode { ) -> IdeviceErrorCode {
if path.is_null() { if client.is_null() || path.is_null() {
return IdeviceErrorCode::InvalidArg; 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 res: Result<(), IdeviceError> = RUNTIME.block_on(async move {
let mut client_box = unsafe { Box::from_raw(client) }; let client_ref = unsafe { &mut (*client).0 };
let client_ref = &mut client_box.0; client_ref.remove_all(path).await
let result = client_ref.remove_all(path).await;
std::mem::forget(client_box);
result
}); });
match res { match res {
@@ -531,7 +467,7 @@ impl From<AfcFopenMode> for idevice::afc::opcode::AfcFopenMode {
/// Handle for an open file on the device /// Handle for an open file on the device
#[allow(dead_code)] #[allow(dead_code)]
pub struct AfcFileHandle(*mut c_void); // Opaque pointer pub struct AfcFileHandle<'a>(Box<idevice::afc::file::FileDescriptor<'a>>); // Opaque pointer
/// Opens a file on the device /// Opens a file on the device
/// ///
@@ -546,7 +482,9 @@ pub struct AfcFileHandle(*mut c_void); // Opaque pointer
/// ///
/// # Safety /// # Safety
/// All pointers must be valid and non-null /// 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)] #[unsafe(no_mangle)]
pub unsafe extern "C" fn afc_file_open( pub unsafe extern "C" fn afc_file_open(
client: *mut AfcClientHandle, client: *mut AfcClientHandle,
@@ -554,7 +492,7 @@ pub unsafe extern "C" fn afc_file_open(
mode: AfcFopenMode, mode: AfcFopenMode,
handle: *mut *mut AfcFileHandle, handle: *mut *mut AfcFileHandle,
) -> IdeviceErrorCode { ) -> IdeviceErrorCode {
if path.is_null() || handle.is_null() { if client.is_null() || path.is_null() || handle.is_null() {
return IdeviceErrorCode::InvalidArg; return IdeviceErrorCode::InvalidArg;
} }
@@ -567,18 +505,15 @@ pub unsafe extern "C" fn afc_file_open(
let mode = mode.into(); let mode = mode.into();
let res: Result<*mut AfcFileHandle, IdeviceError> = RUNTIME.block_on(async move { let res: Result<*mut AfcFileHandle, IdeviceError> = RUNTIME.block_on(async move {
let mut client_box = unsafe { Box::from_raw(client) }; let client_ref = unsafe { &mut (*client).0 };
let client_ref = &mut client_box.0;
let result = client_ref.open(path, mode).await; let result = client_ref.open(path, mode).await;
let res = match result { match result {
Ok(f) => { Ok(f) => {
let boxed = Box::new(f); let boxed = Box::new(f);
Ok(Box::into_raw(boxed) as *mut AfcFileHandle) Ok(Box::into_raw(boxed) as *mut AfcFileHandle)
} }
Err(e) => Err(e), Err(e) => Err(e),
}; }
std::mem::forget(client_box);
res
}); });
match res { match res {
@@ -716,7 +651,7 @@ pub unsafe extern "C" fn afc_make_link(
source: *const libc::c_char, source: *const libc::c_char,
link_type: AfcLinkType, link_type: AfcLinkType,
) -> IdeviceErrorCode { ) -> IdeviceErrorCode {
if target.is_null() || source.is_null() { if client.is_null() || target.is_null() || source.is_null() {
return IdeviceErrorCode::InvalidArg; 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 res: Result<(), IdeviceError> = RUNTIME.block_on(async move {
let mut client_box = unsafe { Box::from_raw(client) }; let client_ref = unsafe { &mut (*client).0 };
let client_ref = &mut client_box.0; client_ref.link(target, source, link_type).await
let result = client_ref.link(target, source, link_type).await;
std::mem::forget(client_box);
result
}); });
match res { match res {
@@ -770,7 +702,7 @@ pub unsafe extern "C" fn afc_rename_path(
source: *const libc::c_char, source: *const libc::c_char,
target: *const libc::c_char, target: *const libc::c_char,
) -> IdeviceErrorCode { ) -> IdeviceErrorCode {
if source.is_null() || target.is_null() { if client.is_null() || source.is_null() || target.is_null() {
return IdeviceErrorCode::InvalidArg; 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 res: Result<(), IdeviceError> = RUNTIME.block_on(async move {
let mut client_box = unsafe { Box::from_raw(client) }; let client_ref = unsafe { &mut (*client).0 };
let client_ref = &mut client_box.0; client_ref.rename(source, target).await
let result = client_ref.rename(source, target).await;
std::mem::forget(client_box);
result
}); });
match res { match res {
@@ -799,3 +728,18 @@ pub unsafe extern "C" fn afc_rename_path(
Err(e) => e.into(), 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);
}
}

View File

@@ -1,18 +1,15 @@
// Jackson Coxson // Jackson Coxson
use idevice::{IdeviceError, IdeviceService, amfi::AmfiClient}; use idevice::{IdeviceError, IdeviceService, amfi::AmfiClient, provider::IdeviceProvider};
use crate::{ use crate::{IdeviceErrorCode, IdeviceHandle, RUNTIME, provider::IdeviceProviderHandle};
IdeviceErrorCode, IdeviceHandle, RUNTIME,
provider::{TcpProviderHandle, UsbmuxdProviderHandle},
};
pub struct AmfiClientHandle(pub AmfiClient); pub struct AmfiClientHandle(pub AmfiClient);
/// Automatically creates and connects to AMFI service, returning a client handle /// Automatically creates and connects to AMFI service, returning a client handle
/// ///
/// # Arguments /// # Arguments
/// * [`provider`] - A TcpProvider /// * [`provider`] - An IdeviceProvider
/// * [`client`] - On success, will be set to point to a newly allocated AmfiClient handle /// * [`client`] - On success, will be set to point to a newly allocated AmfiClient handle
/// ///
/// # Returns /// # Returns
@@ -22,8 +19,8 @@ pub struct AmfiClientHandle(pub AmfiClient);
/// `provider` must be a valid pointer to a handle allocated by this library /// `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 /// `client` must be a valid, non-null pointer to a location where the handle will be stored
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
pub unsafe extern "C" fn amfi_connect_tcp( pub unsafe extern "C" fn amfi_connect(
provider: *mut TcpProviderHandle, provider: *mut IdeviceProviderHandle,
client: *mut *mut AmfiClientHandle, client: *mut *mut AmfiClientHandle,
) -> IdeviceErrorCode { ) -> IdeviceErrorCode {
if provider.is_null() || client.is_null() { if provider.is_null() || client.is_null() {
@@ -32,70 +29,10 @@ pub unsafe extern "C" fn amfi_connect_tcp(
} }
let res: Result<AmfiClient, IdeviceError> = RUNTIME.block_on(async move { let res: Result<AmfiClient, IdeviceError> = RUNTIME.block_on(async move {
// Take ownership of the provider (without immediately dropping it) let provider_ref: &dyn IdeviceProvider = unsafe { &*(*provider).0 };
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 // Connect using the reference
let result = AmfiClient::connect(provider_ref).await; 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<AmfiClient, IdeviceError> = 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
}); });
match res { match res {
@@ -118,16 +55,18 @@ pub unsafe extern "C" fn amfi_connect_usbmuxd(
/// An error code indicating success or failure /// An error code indicating success or failure
/// ///
/// # Safety /// # 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 /// `client` must be a valid, non-null pointer to a location where the handle will be stored
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
pub unsafe extern "C" fn amfi_new( pub unsafe extern "C" fn amfi_new(
socket: *mut IdeviceHandle, socket: *mut IdeviceHandle,
client: *mut *mut AmfiClientHandle, client: *mut *mut AmfiClientHandle,
) -> IdeviceErrorCode { ) -> IdeviceErrorCode {
if socket.is_null() { if socket.is_null() || client.is_null() {
return IdeviceErrorCode::InvalidArg; return IdeviceErrorCode::InvalidArg;
} }
let socket = unsafe { Box::from_raw(socket) }.0; let socket = unsafe { Box::from_raw(socket) }.0;
let r = AmfiClient::new(socket); let r = AmfiClient::new(socket);
let boxed = Box::new(AmfiClientHandle(r)); 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( pub unsafe extern "C" fn amfi_reveal_developer_mode_option_in_ui(
client: *mut AmfiClientHandle, client: *mut AmfiClientHandle,
) -> IdeviceErrorCode { ) -> IdeviceErrorCode {
if client.is_null() {
return IdeviceErrorCode::InvalidArg;
}
let res: Result<(), IdeviceError> = RUNTIME.block_on(async move { let res: Result<(), IdeviceError> = RUNTIME.block_on(async move {
// Take ownership of the client let client_ref = unsafe { &mut (*client).0 };
let mut client_box = unsafe { Box::from_raw(client) }; client_ref.reveal_developer_mode_option_in_ui().await
// 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
}); });
match res { match res {
Ok(_) => IdeviceErrorCode::IdeviceSuccess, 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( pub unsafe extern "C" fn amfi_enable_developer_mode(
client: *mut AmfiClientHandle, client: *mut AmfiClientHandle,
) -> IdeviceErrorCode { ) -> IdeviceErrorCode {
if client.is_null() {
return IdeviceErrorCode::InvalidArg;
}
let res: Result<(), IdeviceError> = RUNTIME.block_on(async move { let res: Result<(), IdeviceError> = RUNTIME.block_on(async move {
// Take ownership of the client let client_ref = unsafe { &mut (*client).0 };
let mut client_box = unsafe { Box::from_raw(client) }; client_ref.enable_developer_mode().await
// 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
}); });
match res { match res {
Ok(_) => IdeviceErrorCode::IdeviceSuccess, 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( pub unsafe extern "C" fn amfi_accept_developer_mode(
client: *mut AmfiClientHandle, client: *mut AmfiClientHandle,
) -> IdeviceErrorCode { ) -> IdeviceErrorCode {
if client.is_null() {
return IdeviceErrorCode::InvalidArg;
}
let res: Result<(), IdeviceError> = RUNTIME.block_on(async move { let res: Result<(), IdeviceError> = RUNTIME.block_on(async move {
// Take ownership of the client let client_ref = unsafe { &mut (*client).0 };
let mut client_box = unsafe { Box::from_raw(client) }; client_ref.accept_developer_mode().await
// 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
}); });
match res { match res {
Ok(_) => IdeviceErrorCode::IdeviceSuccess, Ok(_) => IdeviceErrorCode::IdeviceSuccess,

View File

@@ -3,13 +3,11 @@
use std::ffi::{CString, c_char}; use std::ffi::{CString, c_char};
use idevice::{ use idevice::{
IdeviceError, IdeviceService, core_device_proxy::CoreDeviceProxy, tcp::adapter::Adapter, IdeviceError, IdeviceService, core_device_proxy::CoreDeviceProxy, provider::IdeviceProvider,
tcp::adapter::Adapter,
}; };
use crate::{ use crate::{IdeviceErrorCode, IdeviceHandle, RUNTIME, provider::IdeviceProviderHandle};
IdeviceErrorCode, IdeviceHandle, RUNTIME,
provider::{TcpProviderHandle, UsbmuxdProviderHandle},
};
pub struct CoreDeviceProxyHandle(pub CoreDeviceProxy); pub struct CoreDeviceProxyHandle(pub CoreDeviceProxy);
pub struct AdapterHandle(pub Adapter); 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 /// Automatically creates and connects to Core Device Proxy, returning a client handle
/// ///
/// # Arguments /// # Arguments
/// * [`provider`] - A TcpProvider /// * [`provider`] - An IdeviceProvider
/// * [`client`] - On success, will be set to point to a newly allocated CoreDeviceProxy handle /// * [`client`] - On success, will be set to point to a newly allocated CoreDeviceProxy handle
/// ///
/// # Returns /// # Returns
@@ -27,8 +25,8 @@ pub struct AdapterHandle(pub Adapter);
/// `provider` must be a valid pointer to a handle allocated by this library /// `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 /// `client` must be a valid, non-null pointer to a location where the handle will be stored
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
pub unsafe extern "C" fn core_device_proxy_connect_tcp( pub unsafe extern "C" fn core_device_proxy_connect(
provider: *mut TcpProviderHandle, provider: *mut IdeviceProviderHandle,
client: *mut *mut CoreDeviceProxyHandle, client: *mut *mut CoreDeviceProxyHandle,
) -> IdeviceErrorCode { ) -> IdeviceErrorCode {
if provider.is_null() || client.is_null() { if provider.is_null() || client.is_null() {
@@ -37,70 +35,10 @@ pub unsafe extern "C" fn core_device_proxy_connect_tcp(
} }
let res: Result<CoreDeviceProxy, IdeviceError> = RUNTIME.block_on(async move { let res: Result<CoreDeviceProxy, IdeviceError> = RUNTIME.block_on(async move {
// Take ownership of the provider (without immediately dropping it) let provider_ref: &dyn IdeviceProvider = unsafe { &*(*provider).0 };
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 // Connect using the reference
let result = CoreDeviceProxy::connect(provider_ref).await; 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<CoreDeviceProxy, IdeviceError> = 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
}); });
match res { match res {
@@ -123,14 +61,15 @@ pub unsafe extern "C" fn core_device_proxy_connect_usbmuxd(
/// An error code indicating success or failure /// An error code indicating success or failure
/// ///
/// # Safety /// # 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 /// `client` must be a valid, non-null pointer to a location where the handle will be stored
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
pub unsafe extern "C" fn core_device_proxy_new( pub unsafe extern "C" fn core_device_proxy_new(
socket: *mut IdeviceHandle, socket: *mut IdeviceHandle,
client: *mut *mut CoreDeviceProxyHandle, client: *mut *mut CoreDeviceProxyHandle,
) -> IdeviceErrorCode { ) -> IdeviceErrorCode {
if socket.is_null() { if socket.is_null() || client.is_null() {
return IdeviceErrorCode::InvalidArg; return IdeviceErrorCode::InvalidArg;
} }
let socket = unsafe { Box::from_raw(socket) }.0; let socket = unsafe { Box::from_raw(socket) }.0;
@@ -233,8 +172,8 @@ pub unsafe extern "C" fn core_device_proxy_recv(
/// # Arguments /// # Arguments
/// * [`handle`] - The CoreDeviceProxy handle /// * [`handle`] - The CoreDeviceProxy handle
/// * [`mtu`] - Pointer to store the MTU value /// * [`mtu`] - Pointer to store the MTU value
/// * [`address`] - Pointer to store the IP address string (must be at least 16 bytes) /// * [`address`] - Pointer to store the IP address string
/// * [`netmask`] - Pointer to store the netmask string (must be at least 16 bytes) /// * [`netmask`] - Pointer to store the netmask string
/// ///
/// # Returns /// # Returns
/// An error code indicating success or failure /// 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; *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 { unsafe {
*address = CString::new(params.address.clone()).unwrap().into_raw(); *address = address_cstring.into_raw();
*netmask = CString::new(params.netmask.clone()).unwrap().into_raw(); *netmask = netmask_cstring.into_raw();
} }
IdeviceErrorCode::IdeviceSuccess IdeviceErrorCode::IdeviceSuccess
@@ -274,7 +225,7 @@ pub unsafe extern "C" fn core_device_proxy_get_client_parameters(
/// ///
/// # Arguments /// # Arguments
/// * [`handle`] - The CoreDeviceProxy handle /// * [`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 /// # Returns
/// An error code indicating success or failure /// 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 }; let proxy = unsafe { &(*handle).0 };
unsafe { unsafe {
*address = CString::new(proxy.handshake.server_address.clone()) *address = match CString::new(proxy.handshake.server_address.clone()) {
.unwrap() Ok(s) => s.into_raw(),
.into_raw(); Err(_) => return IdeviceErrorCode::InvalidString,
};
} }
IdeviceErrorCode::IdeviceSuccess IdeviceErrorCode::IdeviceSuccess

View File

@@ -4,14 +4,13 @@ use std::ffi::{CStr, CString, c_char};
use std::os::raw::c_int; use std::os::raw::c_int;
use std::ptr; use std::ptr;
use idevice::ReadWrite;
use idevice::debug_proxy::{DebugProxyClient, DebugserverCommand}; use idevice::debug_proxy::{DebugProxyClient, DebugserverCommand};
use idevice::tcp::adapter::Adapter;
use crate::core_device_proxy::AdapterHandle;
use crate::{IdeviceErrorCode, RUNTIME}; use crate::{IdeviceErrorCode, RUNTIME};
/// Opaque handle to a DebugProxyClient /// Opaque handle to a DebugProxyClient
pub struct DebugProxyAdapterHandle(pub DebugProxyClient<Adapter>); pub struct DebugProxyHandle(pub DebugProxyClient<Box<dyn ReadWrite>>);
/// Represents a debugserver command /// Represents a debugserver command
#[repr(C)] #[repr(C)]
@@ -56,16 +55,26 @@ pub unsafe extern "C" fn debugserver_command_new(
let argv_len = argv_vec.len(); let argv_len = argv_vec.len();
let boxed = Box::new(DebugserverCommandHandle { 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() { argv: if argv_vec.is_empty() {
ptr::null_mut() ptr::null_mut()
} else { } else {
let mut argv_ptrs: Vec<*mut c_char> = argv_vec let argv_ptrs: Result<Vec<*mut c_char>, _> = argv_vec
.into_iter() .into_iter()
.map(|s| CString::new(s).unwrap().into_raw()) .map(|s| CString::new(s).map(|cs| cs.into_raw()))
.collect(); .collect();
let mut argv_ptrs = match argv_ptrs {
Ok(ptrs) => ptrs,
Err(_) => return ptr::null_mut(),
};
argv_ptrs.shrink_to_fit(); 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, argv_count: argv_len,
}); });
@@ -106,7 +115,7 @@ pub unsafe extern "C" fn debugserver_command_free(command: *mut DebugserverComma
/// Creates a new DebugProxyClient /// Creates a new DebugProxyClient
/// ///
/// # Arguments /// # 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 /// * [`handle`] - Pointer to store the newly created DebugProxyClient handle
/// ///
/// # Returns /// # 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 /// `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 /// `handle` must be a valid pointer to a location where the handle will be stored
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
pub unsafe extern "C" fn debug_proxy_adapter_new( pub unsafe extern "C" fn debug_proxy_new(
socket: *mut AdapterHandle, socket: *mut Box<dyn ReadWrite>,
handle: *mut *mut DebugProxyAdapterHandle, handle: *mut *mut DebugProxyHandle,
) -> IdeviceErrorCode { ) -> IdeviceErrorCode {
if socket.is_null() || handle.is_null() { if socket.is_null() || handle.is_null() {
return IdeviceErrorCode::InvalidArg; return IdeviceErrorCode::InvalidArg;
} }
let socket = unsafe { Box::from_raw(socket) }; 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(Box::new(new_handle)) };
unsafe { *handle = Box::into_raw(boxed) };
IdeviceErrorCode::IdeviceSuccess IdeviceErrorCode::IdeviceSuccess
} }
@@ -140,7 +149,7 @@ pub unsafe extern "C" fn debug_proxy_adapter_new(
/// # Safety /// # Safety
/// `handle` must be a valid pointer to a handle allocated by this library or NULL /// `handle` must be a valid pointer to a handle allocated by this library or NULL
#[unsafe(no_mangle)] #[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() { if !handle.is_null() {
let _ = unsafe { Box::from_raw(handle) }; 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 /// `response` must be a valid pointer to a location where the string will be stored
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
pub unsafe extern "C" fn debug_proxy_send_command( pub unsafe extern "C" fn debug_proxy_send_command(
handle: *mut DebugProxyAdapterHandle, handle: *mut DebugProxyHandle,
command: *mut DebugserverCommandHandle, command: *mut DebugserverCommandHandle,
response: *mut *mut c_char, response: *mut *mut c_char,
) -> IdeviceErrorCode { ) -> IdeviceErrorCode {
@@ -192,7 +201,10 @@ pub unsafe extern "C" fn debug_proxy_send_command(
match res { match res {
Ok(Some(r)) => { 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() }; unsafe { *response = cstr.into_raw() };
IdeviceErrorCode::IdeviceSuccess 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 /// `response` must be a valid pointer to a location where the string will be stored
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
pub unsafe extern "C" fn debug_proxy_read_response( pub unsafe extern "C" fn debug_proxy_read_response(
handle: *mut DebugProxyAdapterHandle, handle: *mut DebugProxyHandle,
response: *mut *mut c_char, response: *mut *mut c_char,
) -> IdeviceErrorCode { ) -> IdeviceErrorCode {
if handle.is_null() || response.is_null() { if handle.is_null() || response.is_null() {
@@ -230,7 +242,10 @@ pub unsafe extern "C" fn debug_proxy_read_response(
match res { match res {
Ok(Some(r)) => { 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() }; unsafe { *response = cstr.into_raw() };
IdeviceErrorCode::IdeviceSuccess IdeviceErrorCode::IdeviceSuccess
} }
@@ -257,7 +272,7 @@ pub unsafe extern "C" fn debug_proxy_read_response(
/// `data` must be a valid pointer to `len` bytes /// `data` must be a valid pointer to `len` bytes
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
pub unsafe extern "C" fn debug_proxy_send_raw( pub unsafe extern "C" fn debug_proxy_send_raw(
handle: *mut DebugProxyAdapterHandle, handle: *mut DebugProxyHandle,
data: *const u8, data: *const u8,
len: usize, len: usize,
) -> IdeviceErrorCode { ) -> 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 /// `response` must be a valid pointer to a location where the string will be stored
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
pub unsafe extern "C" fn debug_proxy_read( pub unsafe extern "C" fn debug_proxy_read(
handle: *mut DebugProxyAdapterHandle, handle: *mut DebugProxyHandle,
len: usize, len: usize,
response: *mut *mut c_char, response: *mut *mut c_char,
) -> IdeviceErrorCode { ) -> IdeviceErrorCode {
@@ -303,7 +318,10 @@ pub unsafe extern "C" fn debug_proxy_read(
match res { match res {
Ok(r) => { 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() }; unsafe { *response = cstr.into_raw() };
IdeviceErrorCode::IdeviceSuccess 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 /// `response` must be a valid pointer to a location where the string will be stored
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
pub unsafe extern "C" fn debug_proxy_set_argv( pub unsafe extern "C" fn debug_proxy_set_argv(
handle: *mut DebugProxyAdapterHandle, handle: *mut DebugProxyHandle,
argv: *const *const c_char, argv: *const *const c_char,
argv_count: usize, argv_count: usize,
response: *mut *mut c_char, response: *mut *mut c_char,
@@ -358,7 +376,10 @@ pub unsafe extern "C" fn debug_proxy_set_argv(
match res { match res {
Ok(r) => { 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() }; unsafe { *response = cstr.into_raw() };
IdeviceErrorCode::IdeviceSuccess IdeviceErrorCode::IdeviceSuccess
} }
@@ -377,9 +398,7 @@ pub unsafe extern "C" fn debug_proxy_set_argv(
/// # Safety /// # Safety
/// `handle` must be a valid pointer /// `handle` must be a valid pointer
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
pub unsafe extern "C" fn debug_proxy_send_ack( pub unsafe extern "C" fn debug_proxy_send_ack(handle: *mut DebugProxyHandle) -> IdeviceErrorCode {
handle: *mut DebugProxyAdapterHandle,
) -> IdeviceErrorCode {
if handle.is_null() { if handle.is_null() {
return IdeviceErrorCode::InvalidArg; return IdeviceErrorCode::InvalidArg;
} }
@@ -404,9 +423,7 @@ pub unsafe extern "C" fn debug_proxy_send_ack(
/// # Safety /// # Safety
/// `handle` must be a valid pointer /// `handle` must be a valid pointer
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
pub unsafe extern "C" fn debug_proxy_send_nack( pub unsafe extern "C" fn debug_proxy_send_nack(handle: *mut DebugProxyHandle) -> IdeviceErrorCode {
handle: *mut DebugProxyAdapterHandle,
) -> IdeviceErrorCode {
if handle.is_null() { if handle.is_null() {
return IdeviceErrorCode::InvalidArg; return IdeviceErrorCode::InvalidArg;
} }
@@ -429,39 +446,9 @@ pub unsafe extern "C" fn debug_proxy_send_nack(
/// # Safety /// # Safety
/// `handle` must be a valid pointer /// `handle` must be a valid pointer
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
pub unsafe extern "C" fn debug_proxy_set_ack_mode( pub unsafe extern "C" fn debug_proxy_set_ack_mode(handle: *mut DebugProxyHandle, enabled: c_int) {
handle: *mut DebugProxyAdapterHandle,
enabled: c_int,
) {
if !handle.is_null() { if !handle.is_null() {
let client = unsafe { &mut (*handle).0 }; let client = unsafe { &mut (*handle).0 };
client.set_ack_mode(enabled != 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
}

View File

@@ -33,7 +33,6 @@ pub enum IdeviceErrorCode {
ImageNotMounted = -25, ImageNotMounted = -25,
Reqwest = -26, Reqwest = -26,
InternalError = -27, InternalError = -27,
Xpc = -28,
NsKeyedArchiveError = -29, NsKeyedArchiveError = -29,
UnknownAuxValueType = -30, UnknownAuxValueType = -30,
UnknownChannel = -31, UnknownChannel = -31,
@@ -50,9 +49,24 @@ pub enum IdeviceErrorCode {
UnknownAfcOpcode = -42, UnknownAfcOpcode = -42,
InvalidAfcMagic = -43, InvalidAfcMagic = -43,
AfcMissingAttribute = -44, 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 // FFI specific bindings
AdapterIOFailed = -996, AdapterIOFailed = -996,
ServiceNotFound = -997,
BufferTooSmall = -998, BufferTooSmall = -998,
InvalidString = -999, InvalidString = -999,
InvalidArg = -1000, InvalidArg = -1000,
@@ -90,7 +104,6 @@ impl From<IdeviceError> for IdeviceErrorCode {
IdeviceError::ImageNotMounted => IdeviceErrorCode::ImageNotMounted, IdeviceError::ImageNotMounted => IdeviceErrorCode::ImageNotMounted,
IdeviceError::Reqwest(_) => IdeviceErrorCode::Reqwest, IdeviceError::Reqwest(_) => IdeviceErrorCode::Reqwest,
IdeviceError::InternalError(_) => IdeviceErrorCode::InternalError, IdeviceError::InternalError(_) => IdeviceErrorCode::InternalError,
IdeviceError::Xpc(_) => IdeviceErrorCode::Xpc,
IdeviceError::NsKeyedArchiveError(_) => IdeviceErrorCode::NsKeyedArchiveError, IdeviceError::NsKeyedArchiveError(_) => IdeviceErrorCode::NsKeyedArchiveError,
IdeviceError::UnknownAuxValueType(_) => IdeviceErrorCode::UnknownAuxValueType, IdeviceError::UnknownAuxValueType(_) => IdeviceErrorCode::UnknownAuxValueType,
IdeviceError::UnknownChannel(_) => IdeviceErrorCode::UnknownChannel, IdeviceError::UnknownChannel(_) => IdeviceErrorCode::UnknownChannel,
@@ -109,6 +122,25 @@ impl From<IdeviceError> for IdeviceErrorCode {
IdeviceError::UnknownAfcOpcode => IdeviceErrorCode::UnknownAfcOpcode, IdeviceError::UnknownAfcOpcode => IdeviceErrorCode::UnknownAfcOpcode,
IdeviceError::InvalidAfcMagic => IdeviceErrorCode::InvalidAfcMagic, IdeviceError::InvalidAfcMagic => IdeviceErrorCode::InvalidAfcMagic,
IdeviceError::AfcMissingAttribute => IdeviceErrorCode::AfcMissingAttribute, 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, _ => IdeviceErrorCode::InternalError,
} }
} }

View File

@@ -1,20 +1,17 @@
// Jackson Coxson // Jackson Coxson
use idevice::{IdeviceError, IdeviceService, heartbeat::HeartbeatClient}; use idevice::{
IdeviceError, IdeviceService, heartbeat::HeartbeatClient, provider::IdeviceProvider,
use crate::{
IdeviceErrorCode, IdeviceHandle, RUNTIME,
provider::{TcpProviderHandle, UsbmuxdProviderHandle},
}; };
use crate::{IdeviceErrorCode, IdeviceHandle, RUNTIME, provider::IdeviceProviderHandle};
pub struct HeartbeatClientHandle(pub HeartbeatClient); 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 /// Automatically creates and connects to Installation Proxy, returning a client handle
/// ///
/// # Arguments /// # Arguments
/// * [`provider`] - A TcpProvider /// * [`provider`] - An IdeviceProvider
/// * [`client`] - On success, will be set to point to a newly allocated InstallationProxyClient handle /// * [`client`] - On success, will be set to point to a newly allocated InstallationProxyClient handle
/// ///
/// # Returns /// # Returns
@@ -24,8 +21,8 @@ pub struct plist_t;
/// `provider` must be a valid pointer to a handle allocated by this library /// `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 /// `client` must be a valid, non-null pointer to a location where the handle will be stored
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
pub unsafe extern "C" fn heartbeat_connect_tcp( pub unsafe extern "C" fn heartbeat_connect(
provider: *mut TcpProviderHandle, provider: *mut IdeviceProviderHandle,
client: *mut *mut HeartbeatClientHandle, client: *mut *mut HeartbeatClientHandle,
) -> IdeviceErrorCode { ) -> IdeviceErrorCode {
if provider.is_null() || client.is_null() { if provider.is_null() || client.is_null() {
@@ -34,18 +31,9 @@ pub unsafe extern "C" fn heartbeat_connect_tcp(
} }
let res: Result<HeartbeatClient, IdeviceError> = RUNTIME.block_on(async move { let res: Result<HeartbeatClient, IdeviceError> = RUNTIME.block_on(async move {
// Take ownership of the provider (without immediately dropping it) let provider_ref: &dyn IdeviceProvider = unsafe { &*(*provider).0 };
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 // Connect using the reference
let result = HeartbeatClient::connect(provider_ref).await; HeartbeatClient::connect(provider_ref).await
// Explicitly keep the provider_box alive until after connect completes
std::mem::forget(provider_box);
result
}); });
match res { 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<HeartbeatClient, IdeviceError> = 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 /// Automatically creates and connects to Installation Proxy, returning a client handle
/// ///
/// # Arguments /// # Arguments
@@ -120,14 +61,15 @@ pub unsafe extern "C" fn heartbeat_connect_usbmuxd(
/// An error code indicating success or failure /// An error code indicating success or failure
/// ///
/// # Safety /// # 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 /// `client` must be a valid, non-null pointer to a location where the handle will be stored
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
pub unsafe extern "C" fn heartbeat_new( pub unsafe extern "C" fn heartbeat_new(
socket: *mut IdeviceHandle, socket: *mut IdeviceHandle,
client: *mut *mut HeartbeatClientHandle, client: *mut *mut HeartbeatClientHandle,
) -> IdeviceErrorCode { ) -> IdeviceErrorCode {
if socket.is_null() { if socket.is_null() || client.is_null() {
return IdeviceErrorCode::InvalidArg; return IdeviceErrorCode::InvalidArg;
} }
let socket = unsafe { Box::from_raw(socket) }.0; 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( pub unsafe extern "C" fn heartbeat_send_polo(
client: *mut HeartbeatClientHandle, client: *mut HeartbeatClientHandle,
) -> IdeviceErrorCode { ) -> IdeviceErrorCode {
if client.is_null() {
return IdeviceErrorCode::InvalidArg;
}
let res: Result<(), IdeviceError> = RUNTIME.block_on(async move { let res: Result<(), IdeviceError> = RUNTIME.block_on(async move {
// Take ownership of the client let client_ref = unsafe { &mut (*client).0 };
let mut client_box = unsafe { Box::from_raw(client) }; client_ref.send_polo().await
// 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
}); });
match res { match res {
Ok(_) => IdeviceErrorCode::IdeviceSuccess, Ok(_) => IdeviceErrorCode::IdeviceSuccess,
@@ -186,16 +124,12 @@ pub unsafe extern "C" fn heartbeat_get_marco(
interval: u64, interval: u64,
new_interval: *mut u64, new_interval: *mut u64,
) -> IdeviceErrorCode { ) -> IdeviceErrorCode {
if client.is_null() || new_interval.is_null() {
return IdeviceErrorCode::InvalidArg;
}
let res: Result<u64, IdeviceError> = RUNTIME.block_on(async move { let res: Result<u64, IdeviceError> = RUNTIME.block_on(async move {
// Take ownership of the client let client_ref = unsafe { &mut (*client).0 };
let mut client_box = unsafe { Box::from_raw(client) }; client_ref.get_marco(interval).await
// 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
}); });
match res { match res {
Ok(n) => { Ok(n) => {

View File

@@ -2,20 +2,19 @@
use std::ffi::c_void; use std::ffi::c_void;
use idevice::{IdeviceError, IdeviceService, installation_proxy::InstallationProxyClient}; use idevice::{
IdeviceError, IdeviceService, installation_proxy::InstallationProxyClient,
use crate::{ provider::IdeviceProvider,
IdeviceErrorCode, IdeviceHandle, RUNTIME,
provider::{TcpProviderHandle, UsbmuxdProviderHandle},
util,
}; };
use crate::{IdeviceErrorCode, IdeviceHandle, RUNTIME, provider::IdeviceProviderHandle, util};
pub struct InstallationProxyClientHandle(pub InstallationProxyClient); pub struct InstallationProxyClientHandle(pub InstallationProxyClient);
/// Automatically creates and connects to Installation Proxy, returning a client handle /// Automatically creates and connects to Installation Proxy, returning a client handle
/// ///
/// # Arguments /// # Arguments
/// * [`provider`] - A TcpProvider /// * [`provider`] - An IdeviceProvider
/// * [`client`] - On success, will be set to point to a newly allocated InstallationProxyClient handle /// * [`client`] - On success, will be set to point to a newly allocated InstallationProxyClient handle
/// ///
/// # Returns /// # 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 /// `client` must be a valid, non-null pointer to a location where the handle will be stored
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
pub unsafe extern "C" fn installation_proxy_connect_tcp( pub unsafe extern "C" fn installation_proxy_connect_tcp(
provider: *mut TcpProviderHandle, provider: *mut IdeviceProviderHandle,
client: *mut *mut InstallationProxyClientHandle, client: *mut *mut InstallationProxyClientHandle,
) -> IdeviceErrorCode { ) -> IdeviceErrorCode {
if provider.is_null() || client.is_null() { if provider.is_null() || client.is_null() {
@@ -35,70 +34,8 @@ pub unsafe extern "C" fn installation_proxy_connect_tcp(
} }
let res: Result<InstallationProxyClient, IdeviceError> = RUNTIME.block_on(async move { let res: Result<InstallationProxyClient, IdeviceError> = RUNTIME.block_on(async move {
// Take ownership of the provider (without immediately dropping it) let provider_ref: &dyn IdeviceProvider = unsafe { &*(*provider).0 };
let provider_box = unsafe { Box::from_raw(provider) }; InstallationProxyClient::connect(provider_ref).await
// 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<InstallationProxyClient, IdeviceError> = 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 { match res {
@@ -121,14 +58,15 @@ pub unsafe extern "C" fn installation_proxy_connect_usbmuxd(
/// An error code indicating success or failure /// An error code indicating success or failure
/// ///
/// # Safety /// # 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 /// `client` must be a valid, non-null pointer to a location where the handle will be stored
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
pub unsafe extern "C" fn installation_proxy_new( pub unsafe extern "C" fn installation_proxy_new(
socket: *mut IdeviceHandle, socket: *mut IdeviceHandle,
client: *mut *mut InstallationProxyClientHandle, client: *mut *mut InstallationProxyClientHandle,
) -> IdeviceErrorCode { ) -> IdeviceErrorCode {
if socket.is_null() { if socket.is_null() || client.is_null() {
return IdeviceErrorCode::InvalidArg; return IdeviceErrorCode::InvalidArg;
} }
let socket = unsafe { Box::from_raw(socket) }.0; let socket = unsafe { Box::from_raw(socket) }.0;
@@ -201,11 +139,10 @@ pub unsafe extern "C" fn installation_proxy_get_apps(
}); });
match res { match res {
Ok(r) => { Ok(mut r) => {
let ptr = r.as_mut_ptr();
let len = r.len(); let len = r.len();
let boxed_slice = r.into_boxed_slice(); std::mem::forget(r);
let ptr = boxed_slice.as_ptr();
std::mem::forget(boxed_slice);
unsafe { unsafe {
*out_result = ptr as *mut c_void; *out_result = ptr as *mut c_void;
@@ -637,11 +574,10 @@ pub unsafe extern "C" fn installation_proxy_browse(
}); });
match res { match res {
Ok(r) => { Ok(mut r) => {
let ptr = r.as_mut_ptr();
let len = r.len(); let len = r.len();
let boxed_slice = r.into_boxed_slice(); std::mem::forget(r);
let ptr = boxed_slice.as_ptr();
std::mem::forget(boxed_slice);
unsafe { unsafe {
*out_result = ptr as *mut c_void; *out_result = ptr as *mut c_void;

View File

@@ -1,25 +1,41 @@
// Jackson Coxson // Jackson Coxson
#[cfg(feature = "tunnel_tcp_stack")]
pub mod adapter; pub mod adapter;
#[cfg(feature = "afc")]
pub mod afc; pub mod afc;
#[cfg(feature = "amfi")]
pub mod amfi; pub mod amfi;
#[cfg(feature = "core_device_proxy")]
pub mod core_device_proxy; pub mod core_device_proxy;
#[cfg(feature = "debug_proxy")]
pub mod debug_proxy; pub mod debug_proxy;
mod errors; mod errors;
#[cfg(feature = "heartbeat")]
pub mod heartbeat; pub mod heartbeat;
#[cfg(feature = "installation_proxy")]
pub mod installation_proxy; pub mod installation_proxy;
#[cfg(feature = "location_simulation")]
pub mod location_simulation; pub mod location_simulation;
pub mod lockdown; pub mod lockdown;
pub mod logging; pub mod logging;
#[cfg(feature = "misagent")]
pub mod misagent; pub mod misagent;
pub mod mounter; #[cfg(feature = "mobile_image_mounter")]
pub mod mobile_image_mounter;
mod pairing_file; mod pairing_file;
#[cfg(feature = "dvt")]
pub mod process_control; pub mod process_control;
pub mod provider; pub mod provider;
#[cfg(feature = "dvt")]
pub mod remote_server; pub mod remote_server;
pub mod remotexpc; #[cfg(feature = "xpc")]
pub mod sbservices; pub mod rsd;
#[cfg(feature = "springboardservices")]
pub mod springboardservices;
#[cfg(feature = "syslog_relay")]
pub mod syslog_relay; pub mod syslog_relay;
#[cfg(feature = "usbmuxd")]
pub mod usbmuxd; pub mod usbmuxd;
pub mod util; pub mod util;

View File

@@ -1,11 +1,11 @@
// Jackson Coxson // 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 /// Opaque handle to a ProcessControlClient
pub struct LocationSimulationAdapterHandle<'a>(pub LocationSimulationClient<'a, Adapter>); pub struct LocationSimulationHandle<'a>(pub LocationSimulationClient<'a, Box<dyn ReadWrite>>);
/// Creates a new ProcessControlClient from a RemoteServerClient /// 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 /// `handle` must be a valid pointer to a location where the handle will be stored
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
pub unsafe extern "C" fn location_simulation_new( pub unsafe extern "C" fn location_simulation_new(
server: *mut RemoteServerAdapterHandle, server: *mut RemoteServerHandle,
handle: *mut *mut LocationSimulationAdapterHandle<'static>, handle: *mut *mut LocationSimulationHandle<'static>,
) -> IdeviceErrorCode { ) -> IdeviceErrorCode {
if server.is_null() || handle.is_null() { if server.is_null() || handle.is_null() {
return IdeviceErrorCode::InvalidArg; return IdeviceErrorCode::InvalidArg;
@@ -33,7 +33,7 @@ pub unsafe extern "C" fn location_simulation_new(
match res { match res {
Ok(client) => { Ok(client) => {
let boxed = Box::new(LocationSimulationAdapterHandle(client)); let boxed = Box::new(LocationSimulationHandle(client));
unsafe { *handle = Box::into_raw(boxed) }; unsafe { *handle = Box::into_raw(boxed) };
IdeviceErrorCode::IdeviceSuccess IdeviceErrorCode::IdeviceSuccess
} }
@@ -49,9 +49,7 @@ pub unsafe extern "C" fn location_simulation_new(
/// # Safety /// # Safety
/// `handle` must be a valid pointer to a handle allocated by this library or NULL /// `handle` must be a valid pointer to a handle allocated by this library or NULL
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
pub unsafe extern "C" fn location_simulation_free( pub unsafe extern "C" fn location_simulation_free(handle: *mut LocationSimulationHandle<'static>) {
handle: *mut LocationSimulationAdapterHandle<'static>,
) {
if !handle.is_null() { if !handle.is_null() {
let _ = unsafe { Box::from_raw(handle) }; 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 /// All pointers must be valid or NULL where appropriate
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
pub unsafe extern "C" fn location_simulation_clear( pub unsafe extern "C" fn location_simulation_clear(
handle: *mut LocationSimulationAdapterHandle<'static>, handle: *mut LocationSimulationHandle<'static>,
) -> IdeviceErrorCode { ) -> IdeviceErrorCode {
if handle.is_null() { if handle.is_null() {
return IdeviceErrorCode::InvalidArg; return IdeviceErrorCode::InvalidArg;
@@ -98,7 +96,7 @@ pub unsafe extern "C" fn location_simulation_clear(
/// All pointers must be valid or NULL where appropriate /// All pointers must be valid or NULL where appropriate
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
pub unsafe extern "C" fn location_simulation_set( pub unsafe extern "C" fn location_simulation_set(
handle: *mut LocationSimulationAdapterHandle<'static>, handle: *mut LocationSimulationHandle<'static>,
latitude: f64, latitude: f64,
longitude: f64, longitude: f64,
) -> IdeviceErrorCode { ) -> IdeviceErrorCode {

View File

@@ -2,11 +2,10 @@
use std::ffi::c_void; use std::ffi::c_void;
use idevice::{IdeviceError, IdeviceService, lockdown::LockdownClient}; use idevice::{IdeviceError, IdeviceService, lockdown::LockdownClient, provider::IdeviceProvider};
use crate::{ use crate::{
IdeviceErrorCode, IdeviceHandle, IdevicePairingFile, RUNTIME, IdeviceErrorCode, IdeviceHandle, IdevicePairingFile, RUNTIME, provider::IdeviceProviderHandle,
provider::{TcpProviderHandle, UsbmuxdProviderHandle},
}; };
pub struct LockdowndClientHandle(pub LockdownClient); pub struct LockdowndClientHandle(pub LockdownClient);
@@ -14,7 +13,7 @@ pub struct LockdowndClientHandle(pub LockdownClient);
/// Connects to lockdownd service using TCP provider /// Connects to lockdownd service using TCP provider
/// ///
/// # Arguments /// # Arguments
/// * [`provider`] - A TcpProvider /// * [`provider`] - An IdeviceProvider
/// * [`client`] - On success, will be set to point to a newly allocated LockdowndClient handle /// * [`client`] - On success, will be set to point to a newly allocated LockdowndClient handle
/// ///
/// # Returns /// # Returns
@@ -24,8 +23,8 @@ pub struct LockdowndClientHandle(pub LockdownClient);
/// `provider` must be a valid pointer to a handle allocated by this library /// `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 /// `client` must be a valid, non-null pointer to a location where the handle will be stored
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
pub unsafe extern "C" fn lockdownd_connect_tcp( pub unsafe extern "C" fn lockdownd_connect(
provider: *mut TcpProviderHandle, provider: *mut IdeviceProviderHandle,
client: *mut *mut LockdowndClientHandle, client: *mut *mut LockdowndClientHandle,
) -> IdeviceErrorCode { ) -> IdeviceErrorCode {
if provider.is_null() || client.is_null() { if provider.is_null() || client.is_null() {
@@ -34,11 +33,8 @@ pub unsafe extern "C" fn lockdownd_connect_tcp(
} }
let res: Result<LockdownClient, IdeviceError> = RUNTIME.block_on(async move { let res: Result<LockdownClient, IdeviceError> = RUNTIME.block_on(async move {
let provider_box = unsafe { Box::from_raw(provider) }; let provider_ref: &dyn IdeviceProvider = unsafe { &*(*provider).0 };
let provider_ref = &provider_box.0; LockdownClient::connect(provider_ref).await
let result = LockdownClient::connect(provider_ref).await;
std::mem::forget(provider_box);
result
}); });
match res { 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<LockdownClient, IdeviceError> = 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 /// Creates a new LockdowndClient from an existing Idevice connection
/// ///
/// # Arguments /// # Arguments
/// * [`socket`] - An IdeviceSocket handle /// * [`socket`] - An IdeviceSocket handle.
/// * [`client`] - On success, will be set to point to a newly allocated LockdowndClient handle /// * [`client`] - On success, will be set to point to a newly allocated LockdowndClient handle
/// ///
/// # Returns /// # Returns
/// An error code indicating success or failure /// An error code indicating success or failure
/// ///
/// # Safety /// # 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 /// `client` must be a valid, non-null pointer to a location where the handle will be stored
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
pub unsafe extern "C" fn lockdownd_new( pub unsafe extern "C" fn lockdownd_new(
@@ -139,15 +96,10 @@ pub unsafe extern "C" fn lockdownd_start_session(
pairing_file: *mut IdevicePairingFile, pairing_file: *mut IdevicePairingFile,
) -> IdeviceErrorCode { ) -> IdeviceErrorCode {
let res: Result<(), IdeviceError> = RUNTIME.block_on(async move { let res: Result<(), IdeviceError> = RUNTIME.block_on(async move {
let mut client_box = unsafe { Box::from_raw(client) }; let client_ref = unsafe { &mut (*client).0 };
let pairing_file = unsafe { Box::from_raw(pairing_file) }; let pairing_file_ref = unsafe { &(*pairing_file).0 };
let client_ref = &mut client_box.0; client_ref.start_session(pairing_file_ref).await
let res = client_ref.start_session(&pairing_file.0).await;
std::mem::forget(client_box);
std::mem::forget(pairing_file);
res
}); });
match res { match res {
@@ -187,11 +139,8 @@ pub unsafe extern "C" fn lockdownd_start_service(
.into_owned(); .into_owned();
let res: Result<(u16, bool), IdeviceError> = RUNTIME.block_on(async move { let res: Result<(u16, bool), IdeviceError> = RUNTIME.block_on(async move {
let mut client_box = unsafe { Box::from_raw(client) }; let client_ref = unsafe { &mut (*client).0 };
let client_ref = &mut client_box.0; client_ref.start_service(identifier).await
let res = client_ref.start_service(identifier).await;
std::mem::forget(client_box);
res
}); });
match res { match res {
@@ -247,11 +196,8 @@ pub unsafe extern "C" fn lockdownd_get_value(
}; };
let res: Result<plist::Value, IdeviceError> = RUNTIME.block_on(async move { let res: Result<plist::Value, IdeviceError> = RUNTIME.block_on(async move {
let mut client_box = unsafe { Box::from_raw(client) }; let client_ref = unsafe { &mut (*client).0 };
let client_ref = &mut client_box.0; client_ref.get_value(value, domain).await
let res = client_ref.get_value(value, domain).await;
std::mem::forget(client_box);
res
}); });
match res { match res {
@@ -287,11 +233,8 @@ pub unsafe extern "C" fn lockdownd_get_all_values(
} }
let res: Result<plist::Dictionary, IdeviceError> = RUNTIME.block_on(async move { let res: Result<plist::Dictionary, IdeviceError> = RUNTIME.block_on(async move {
let mut client_box = unsafe { Box::from_raw(client) }; let client_ref = unsafe { &mut (*client).0 };
let client_ref = &mut client_box.0; client_ref.get_all_values().await
let res = client_ref.get_all_values().await;
std::mem::forget(client_box);
res
}); });
match res { match res {

View File

@@ -2,19 +2,16 @@
//! //!
//! Provides C-compatible bindings for interacting with the misagent service on iOS devices. //! 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::{ use crate::{IdeviceErrorCode, RUNTIME, provider::IdeviceProviderHandle};
IdeviceErrorCode, RUNTIME,
provider::{TcpProviderHandle, UsbmuxdProviderHandle},
};
pub struct MisagentClientHandle(pub MisagentClient); pub struct MisagentClientHandle(pub MisagentClient);
/// Automatically creates and connects to Misagent, returning a client handle /// Automatically creates and connects to Misagent, returning a client handle
/// ///
/// # Arguments /// # Arguments
/// * [`provider`] - A TcpProvider /// * [`provider`] - An IdeviceProvider
/// * [`client`] - On success, will be set to point to a newly allocated MisagentClient handle /// * [`client`] - On success, will be set to point to a newly allocated MisagentClient handle
/// ///
/// # Returns /// # Returns
@@ -24,8 +21,8 @@ pub struct MisagentClientHandle(pub MisagentClient);
/// `provider` must be a valid pointer to a handle allocated by this library /// `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 /// `client` must be a valid, non-null pointer to a location where the handle will be stored
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
pub unsafe extern "C" fn misagent_connect_tcp( pub unsafe extern "C" fn misagent_connect(
provider: *mut TcpProviderHandle, provider: *mut IdeviceProviderHandle,
client: *mut *mut MisagentClientHandle, client: *mut *mut MisagentClientHandle,
) -> IdeviceErrorCode { ) -> IdeviceErrorCode {
if provider.is_null() || client.is_null() { if provider.is_null() || client.is_null() {
@@ -34,70 +31,8 @@ pub unsafe extern "C" fn misagent_connect_tcp(
} }
let res: Result<MisagentClient, IdeviceError> = RUNTIME.block_on(async move { let res: Result<MisagentClient, IdeviceError> = RUNTIME.block_on(async move {
// Take ownership of the provider (without immediately dropping it) let provider_ref: &dyn IdeviceProvider = unsafe { &*(*provider).0 };
let provider_box = unsafe { Box::from_raw(provider) }; MisagentClient::connect(provider_ref).await
// 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<MisagentClient, IdeviceError> = 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 { match res {

View File

@@ -2,21 +2,19 @@
use std::ffi::c_void; 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 plist::Value;
use crate::{ use crate::{IdeviceErrorCode, IdeviceHandle, RUNTIME, provider::IdeviceProviderHandle, util};
IdeviceErrorCode, IdeviceHandle, RUNTIME,
provider::{TcpProviderHandle, UsbmuxdProviderHandle},
util,
};
pub struct ImageMounterHandle(pub ImageMounter); 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 /// # Arguments
/// * [`provider`] - A TcpProvider /// * [`provider`] - An IdeviceProvider
/// * [`client`] - On success, will be set to point to a newly allocated ImageMounter handle /// * [`client`] - On success, will be set to point to a newly allocated ImageMounter handle
/// ///
/// # Returns /// # Returns
@@ -26,8 +24,8 @@ pub struct ImageMounterHandle(pub ImageMounter);
/// `provider` must be a valid pointer to a handle allocated by this library /// `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 /// `client` must be a valid, non-null pointer to a location where the handle will be stored
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
pub unsafe extern "C" fn image_mounter_connect_tcp( pub unsafe extern "C" fn image_mounter_connect(
provider: *mut TcpProviderHandle, provider: *mut IdeviceProviderHandle,
client: *mut *mut ImageMounterHandle, client: *mut *mut ImageMounterHandle,
) -> IdeviceErrorCode { ) -> IdeviceErrorCode {
if provider.is_null() || client.is_null() { if provider.is_null() || client.is_null() {
@@ -36,11 +34,8 @@ pub unsafe extern "C" fn image_mounter_connect_tcp(
} }
let res: Result<ImageMounter, IdeviceError> = RUNTIME.block_on(async move { let res: Result<ImageMounter, IdeviceError> = RUNTIME.block_on(async move {
let provider_box = unsafe { Box::from_raw(provider) }; let provider_ref: &dyn IdeviceProvider = unsafe { &*(*provider).0 };
let provider_ref = &provider_box.0; ImageMounter::connect(provider_ref).await
let result = ImageMounter::connect(provider_ref).await;
std::mem::forget(provider_box);
result
}); });
match res { 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<ImageMounter, IdeviceError> = 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 /// Creates a new ImageMounter client from an existing Idevice connection
/// ///
/// # Arguments /// # Arguments
@@ -159,11 +114,8 @@ pub unsafe extern "C" fn image_mounter_copy_devices(
devices_len: *mut libc::size_t, devices_len: *mut libc::size_t,
) -> IdeviceErrorCode { ) -> IdeviceErrorCode {
let res: Result<Vec<Value>, IdeviceError> = RUNTIME.block_on(async move { let res: Result<Vec<Value>, IdeviceError> = RUNTIME.block_on(async move {
let mut client_box = unsafe { Box::from_raw(client) }; let client_ref = unsafe { &mut (*client).0 };
let client_ref = &mut client_box.0; client_ref.copy_devices().await
let result = client_ref.copy_devices().await;
std::mem::forget(client_box);
result
}); });
match res { match res {
@@ -219,11 +171,8 @@ pub unsafe extern "C" fn image_mounter_lookup_image(
}; };
let res: Result<Vec<u8>, IdeviceError> = RUNTIME.block_on(async move { let res: Result<Vec<u8>, IdeviceError> = RUNTIME.block_on(async move {
let mut client_box = unsafe { Box::from_raw(client) }; let client_ref = unsafe { &mut (*client).0 };
let client_ref = &mut client_box.0; client_ref.lookup_image(image_type).await
let result = client_ref.lookup_image(image_type).await;
std::mem::forget(client_box);
result
}); });
match res { 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 signature_slice = unsafe { std::slice::from_raw_parts(signature, signature_len) };
let res: Result<(), IdeviceError> = RUNTIME.block_on(async move { let res: Result<(), IdeviceError> = RUNTIME.block_on(async move {
let mut client_box = unsafe { Box::from_raw(client) }; let client_ref = unsafe { &mut (*client).0 };
let client_ref = &mut client_box.0; client_ref
let result = client_ref
.upload_image(image_type, image_slice, signature_slice.to_vec()) .upload_image(image_type, image_slice, signature_slice.to_vec())
.await; .await
std::mem::forget(client_box);
result
}); });
match res { 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 res: Result<(), IdeviceError> = RUNTIME.block_on(async move {
let mut client_box = unsafe { Box::from_raw(client) }; let client_ref = unsafe { &mut (*client).0 };
let client_ref = &mut client_box.0; client_ref
let result = client_ref
.mount_image( .mount_image(
image_type, image_type,
signature_slice.to_vec(), signature_slice.to_vec(),
trust_cache, trust_cache,
info_plist, info_plist,
) )
.await; .await
std::mem::forget(client_box);
result
}); });
match res { 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 res: Result<(), IdeviceError> = RUNTIME.block_on(async move {
let mut client_box = unsafe { Box::from_raw(client) }; let client_ref = unsafe { &mut (*client).0 };
let client_ref = &mut client_box.0; client_ref.unmount_image(mount_path).await
let result = client_ref.unmount_image(mount_path).await;
std::mem::forget(client_box);
result
}); });
match res { match res {
@@ -432,11 +372,8 @@ pub unsafe extern "C" fn image_mounter_query_developer_mode_status(
} }
let res: Result<bool, IdeviceError> = RUNTIME.block_on(async move { let res: Result<bool, IdeviceError> = RUNTIME.block_on(async move {
let mut client_box = unsafe { Box::from_raw(client) }; let client_ref = unsafe { &mut (*client).0 };
let client_ref = &mut client_box.0; client_ref.query_developer_mode_status().await
let result = client_ref.query_developer_mode_status().await;
std::mem::forget(client_box);
result
}); });
match res { 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 signature_slice = unsafe { std::slice::from_raw_parts(signature, signature_len) };
let res: Result<(), IdeviceError> = RUNTIME.block_on(async move { let res: Result<(), IdeviceError> = RUNTIME.block_on(async move {
let mut client_box = unsafe { Box::from_raw(client) }; let client_ref = unsafe { &mut (*client).0 };
let client_ref = &mut client_box.0; client_ref
let result = client_ref
.mount_developer(image_slice, signature_slice.to_vec()) .mount_developer(image_slice, signature_slice.to_vec())
.await; .await
std::mem::forget(client_box);
result
}); });
match res { 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 signature_slice = unsafe { std::slice::from_raw_parts(signature, signature_len) };
let res: Result<Vec<u8>, IdeviceError> = RUNTIME.block_on(async move { let res: Result<Vec<u8>, IdeviceError> = RUNTIME.block_on(async move {
let mut client_box = unsafe { Box::from_raw(client) }; let client_ref = unsafe { &mut (*client).0 };
let client_ref = &mut client_box.0; client_ref
let result = client_ref
.query_personalization_manifest(image_type, signature_slice.to_vec()) .query_personalization_manifest(image_type, signature_slice.to_vec())
.await; .await
std::mem::forget(client_box);
result
}); });
match res { match res {
@@ -590,11 +521,8 @@ pub unsafe extern "C" fn image_mounter_query_nonce(
}; };
let res: Result<Vec<u8>, IdeviceError> = RUNTIME.block_on(async move { let res: Result<Vec<u8>, IdeviceError> = RUNTIME.block_on(async move {
let mut client_box = unsafe { Box::from_raw(client) }; let client_ref = unsafe { &mut (*client).0 };
let client_ref = &mut client_box.0; client_ref.query_nonce(image_type).await
let result = client_ref.query_nonce(image_type).await;
std::mem::forget(client_box);
result
}); });
match res { match res {
@@ -645,13 +573,10 @@ pub unsafe extern "C" fn image_mounter_query_personalization_identifiers(
}; };
let res: Result<plist::Dictionary, IdeviceError> = RUNTIME.block_on(async move { let res: Result<plist::Dictionary, IdeviceError> = RUNTIME.block_on(async move {
let mut client_box = unsafe { Box::from_raw(client) }; let client_ref = unsafe { &mut (*client).0 };
let client_ref = &mut client_box.0; client_ref
let result = client_ref
.query_personalization_identifiers(image_type) .query_personalization_identifiers(image_type)
.await; .await
std::mem::forget(client_box);
result
}); });
match res { match res {
@@ -679,11 +604,8 @@ pub unsafe extern "C" fn image_mounter_roll_personalization_nonce(
client: *mut ImageMounterHandle, client: *mut ImageMounterHandle,
) -> IdeviceErrorCode { ) -> IdeviceErrorCode {
let res: Result<(), IdeviceError> = RUNTIME.block_on(async move { let res: Result<(), IdeviceError> = RUNTIME.block_on(async move {
let mut client_box = unsafe { Box::from_raw(client) }; let client_ref = unsafe { &mut (*client).0 };
let client_ref = &mut client_box.0; client_ref.roll_personalization_nonce().await
let result = client_ref.roll_personalization_nonce().await;
std::mem::forget(client_box);
result
}); });
match res { match res {
@@ -707,11 +629,8 @@ pub unsafe extern "C" fn image_mounter_roll_cryptex_nonce(
client: *mut ImageMounterHandle, client: *mut ImageMounterHandle,
) -> IdeviceErrorCode { ) -> IdeviceErrorCode {
let res: Result<(), IdeviceError> = RUNTIME.block_on(async move { let res: Result<(), IdeviceError> = RUNTIME.block_on(async move {
let mut client_box = unsafe { Box::from_raw(client) }; let client_ref = unsafe { &mut (*client).0 };
let client_ref = &mut client_box.0; client_ref.roll_cryptex_nonce().await
let result = client_ref.roll_cryptex_nonce().await;
std::mem::forget(client_box);
result
}); });
match res { match res {
@@ -739,10 +658,11 @@ pub unsafe extern "C" fn image_mounter_roll_cryptex_nonce(
/// ///
/// # Safety /// # Safety
/// All pointers must be valid (except optional ones which can be null) /// All pointers must be valid (except optional ones which can be null)
#[cfg(feature = "tss")]
#[unsafe(no_mangle)] #[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, client: *mut ImageMounterHandle,
provider: *mut UsbmuxdProviderHandle, provider: *mut IdeviceProviderHandle,
image: *const u8, image: *const u8,
image_len: libc::size_t, image_len: libc::size_t,
trust_cache: *const u8, 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 res: Result<(), IdeviceError> = RUNTIME.block_on(async move {
let mut client_box = unsafe { Box::from_raw(client) }; let client_ref = unsafe { &mut (*client).0 };
let provider_box = unsafe { Box::from_raw(provider) }; let provider_ref: &dyn IdeviceProvider = unsafe { &*(*provider).0 };
let client_ref = &mut client_box.0; client_ref
let provider_ref = &provider_box.0;
let result = client_ref
.mount_personalized( .mount_personalized(
provider_ref, provider_ref,
image_slice.to_vec(), image_slice.to_vec(),
@@ -785,87 +703,7 @@ pub unsafe extern "C" fn image_mounter_mount_personalized_usbmuxd(
info_plist, info_plist,
unique_chip_id, unique_chip_id,
) )
.await; .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
}); });
match res { match res {
@@ -895,10 +733,11 @@ pub unsafe extern "C" fn image_mounter_mount_personalized_tcp(
/// ///
/// # Safety /// # Safety
/// All pointers must be valid (except optional ones which can be null) /// All pointers must be valid (except optional ones which can be null)
#[cfg(feature = "tss")]
#[unsafe(no_mangle)] #[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, client: *mut ImageMounterHandle,
provider: *mut UsbmuxdProviderHandle, provider: *mut IdeviceProviderHandle,
image: *const u8, image: *const u8,
image_len: libc::size_t, image_len: libc::size_t,
trust_cache: *const u8, 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 res: Result<(), IdeviceError> = RUNTIME.block_on(async move {
let mut client_box = unsafe { Box::from_raw(client) }; let client_ref = unsafe { &mut (*client).0 };
let provider_box = unsafe { Box::from_raw(provider) }; let provider_ref: &dyn IdeviceProvider = unsafe { &*(*provider).0 };
let client_ref = &mut client_box.0;
let provider_ref = &provider_box.0;
let callback_wrapper = |((progress, total), context)| async move { let callback_wrapper = |((progress, total), context)| async move {
callback(progress, total, context); callback(progress, total, context);
}; };
let result = client_ref client_ref
.mount_personalized_with_callback( .mount_personalized_with_callback(
provider_ref, provider_ref,
image_slice.to_vec(), image_slice.to_vec(),
@@ -950,98 +787,7 @@ pub unsafe extern "C" fn image_mounter_mount_personalized_usbmuxd_with_callback(
callback_wrapper, callback_wrapper,
context, context,
) )
.await; .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
}); });
match res { match res {

View File

@@ -2,13 +2,13 @@
use std::ffi::{CStr, c_char}; 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 plist::{Dictionary, Value};
use crate::{IdeviceErrorCode, RUNTIME, remote_server::RemoteServerAdapterHandle}; use crate::{IdeviceErrorCode, RUNTIME, remote_server::RemoteServerHandle};
/// Opaque handle to a ProcessControlClient /// Opaque handle to a ProcessControlClient
pub struct ProcessControlAdapterHandle<'a>(pub ProcessControlClient<'a, Adapter>); pub struct ProcessControlHandle<'a>(pub ProcessControlClient<'a, Box<dyn ReadWrite>>);
/// Creates a new ProcessControlClient from a RemoteServerClient /// 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 /// `handle` must be a valid pointer to a location where the handle will be stored
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
pub unsafe extern "C" fn process_control_new( pub unsafe extern "C" fn process_control_new(
server: *mut RemoteServerAdapterHandle, server: *mut RemoteServerHandle,
handle: *mut *mut ProcessControlAdapterHandle<'static>, handle: *mut *mut ProcessControlHandle<'static>,
) -> IdeviceErrorCode { ) -> IdeviceErrorCode {
if server.is_null() || handle.is_null() { if server.is_null() || handle.is_null() {
return IdeviceErrorCode::InvalidArg; return IdeviceErrorCode::InvalidArg;
@@ -36,7 +36,7 @@ pub unsafe extern "C" fn process_control_new(
match res { match res {
Ok(client) => { Ok(client) => {
let boxed = Box::new(ProcessControlAdapterHandle(client)); let boxed = Box::new(ProcessControlHandle(client));
unsafe { *handle = Box::into_raw(boxed) }; unsafe { *handle = Box::into_raw(boxed) };
IdeviceErrorCode::IdeviceSuccess IdeviceErrorCode::IdeviceSuccess
} }
@@ -52,7 +52,7 @@ pub unsafe extern "C" fn process_control_new(
/// # Safety /// # Safety
/// `handle` must be a valid pointer to a handle allocated by this library or NULL /// `handle` must be a valid pointer to a handle allocated by this library or NULL
#[unsafe(no_mangle)] #[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() { if !handle.is_null() {
let _ = unsafe { Box::from_raw(handle) }; 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 /// All pointers must be valid or NULL where appropriate
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
pub unsafe extern "C" fn process_control_launch_app( pub unsafe extern "C" fn process_control_launch_app(
handle: *mut ProcessControlAdapterHandle<'static>, handle: *mut ProcessControlHandle<'static>,
bundle_id: *const c_char, bundle_id: *const c_char,
env_vars: *const *const c_char, env_vars: *const *const c_char,
env_vars_count: usize, 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 /// `handle` must be a valid pointer to a handle allocated by this library
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
pub unsafe extern "C" fn process_control_kill_app( pub unsafe extern "C" fn process_control_kill_app(
handle: *mut ProcessControlAdapterHandle<'static>, handle: *mut ProcessControlHandle<'static>,
pid: u64, pid: u64,
) -> IdeviceErrorCode { ) -> IdeviceErrorCode {
if handle.is_null() { 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 /// `handle` must be a valid pointer to a handle allocated by this library
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
pub unsafe extern "C" fn process_control_disable_memory_limit( pub unsafe extern "C" fn process_control_disable_memory_limit(
handle: *mut ProcessControlAdapterHandle<'static>, handle: *mut ProcessControlHandle<'static>,
pid: u64, pid: u64,
) -> IdeviceErrorCode { ) -> IdeviceErrorCode {
if handle.is_null() { if handle.is_null() {

View File

@@ -1,13 +1,12 @@
// Jackson Coxson // Jackson Coxson
use idevice::provider::{TcpProvider, UsbmuxdProvider}; use idevice::provider::{IdeviceProvider, TcpProvider, UsbmuxdProvider};
use std::ffi::CStr; use std::ffi::CStr;
use std::os::raw::c_char; use std::os::raw::c_char;
use crate::{IdeviceErrorCode, usbmuxd::UsbmuxdAddrHandle, util}; use crate::{IdeviceErrorCode, usbmuxd::UsbmuxdAddrHandle, util};
pub struct TcpProviderHandle(pub TcpProvider); pub struct IdeviceProviderHandle(pub Box<dyn IdeviceProvider>);
pub struct UsbmuxdProviderHandle(pub UsbmuxdProvider);
/// Creates a TCP provider for idevice /// Creates a TCP provider for idevice
/// ///
@@ -25,13 +24,18 @@ pub struct UsbmuxdProviderHandle(pub UsbmuxdProvider);
/// `pairing_file` must never be used again /// `pairing_file` must never be used again
/// `label` must be a valid Cstr /// `label` must be a valid Cstr
/// `provider` must be a valid, non-null pointer to a location where the handle will be stored /// `provider` must be a valid, non-null pointer to a location where the handle will be stored
#[cfg(feature = "tcp")]
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
pub unsafe extern "C" fn idevice_tcp_provider_new( pub unsafe extern "C" fn idevice_tcp_provider_new(
ip: *const libc::sockaddr, ip: *const libc::sockaddr,
pairing_file: *mut crate::pairing_file::IdevicePairingFile, pairing_file: *mut crate::pairing_file::IdevicePairingFile,
label: *const c_char, label: *const c_char,
provider: *mut *mut TcpProviderHandle, provider: *mut *mut IdeviceProviderHandle,
) -> IdeviceErrorCode { ) -> IdeviceErrorCode {
if ip.is_null() || label.is_null() || provider.is_null() {
return IdeviceErrorCode::InvalidArg;
}
let addr = match util::c_addr_to_rust(ip) { let addr = match util::c_addr_to_rust(ip) {
Ok(i) => i, Ok(i) => i,
Err(e) => { Err(e) => {
@@ -53,23 +57,23 @@ pub unsafe extern "C" fn idevice_tcp_provider_new(
label, label,
}; };
let boxed = Box::new(TcpProviderHandle(t)); let boxed = Box::new(IdeviceProviderHandle(Box::new(t)));
unsafe { *provider = Box::into_raw(boxed) }; unsafe { *provider = Box::into_raw(boxed) };
IdeviceErrorCode::IdeviceSuccess IdeviceErrorCode::IdeviceSuccess
} }
/// Frees a TcpProvider handle /// Frees an IdeviceProvider handle
/// ///
/// # Arguments /// # Arguments
/// * [`provider`] - The provider handle to free /// * [`provider`] - The provider handle to free
/// ///
/// # Safety /// # Safety
/// `provider` must be a valid pointer to a TcpProvider handle that was allocated by this library, /// `provider` must be a valid pointer to a IdeviceProvider handle that was allocated this library
/// or NULL (in which case this function does nothing) /// or NULL (in which case this function does nothing)
#[unsafe(no_mangle)] #[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() { if !provider.is_null() {
log::debug!("Freeing TCP provider"); log::debug!("Freeing provider");
unsafe { drop(Box::from_raw(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 /// * [`tag`] - The tag returned in usbmuxd responses
/// * [`udid`] - The UDID of the device to connect to /// * [`udid`] - The UDID of the device to connect to
/// * [`device_id`] - The muxer ID 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 /// * [`label`] - The label to use with the connection
/// * [`provider`] - A pointer to a newly allocated provider /// * [`provider`] - A pointer to a newly allocated provider
/// ///
@@ -91,7 +94,6 @@ pub unsafe extern "C" fn tcp_provider_free(provider: *mut TcpProviderHandle) {
/// # Safety /// # Safety
/// `addr` must be a valid pointer to UsbmuxdAddrHandle created by this library, and never used again /// `addr` must be a valid pointer to UsbmuxdAddrHandle created by this library, and never used again
/// `udid` must be a valid CStr /// `udid` must be a valid CStr
/// `pairing_file` must never be used again
/// `label` must be a valid Cstr /// `label` must be a valid Cstr
/// `provider` must be a valid, non-null pointer to a location where the handle will be stored /// `provider` must be a valid, non-null pointer to a location where the handle will be stored
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
@@ -101,21 +103,25 @@ pub unsafe extern "C" fn usbmuxd_provider_new(
udid: *const c_char, udid: *const c_char,
device_id: u32, device_id: u32,
label: *const c_char, label: *const c_char,
provider: *mut *mut UsbmuxdProviderHandle, provider: *mut *mut IdeviceProviderHandle,
) -> IdeviceErrorCode { ) -> 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() { let udid = match unsafe { CStr::from_ptr(udid) }.to_str() {
Ok(u) => u.to_string(), Ok(u) => u.to_string(),
Err(e) => { Err(e) => {
log::error!("Invalid UDID string: {e:?}"); log::error!("Invalid UDID string: {e:?}");
return IdeviceErrorCode::InvalidArgument; return IdeviceErrorCode::InvalidString;
} }
}; };
let label = match unsafe { CStr::from_ptr(label) }.to_str() { let label = match unsafe { CStr::from_ptr(label) }.to_str() {
Ok(l) => l.to_string(), Ok(l) => l.to_string(),
Err(e) => { Err(e) => {
log::error!("Invalid UDID string: {e:?}"); log::error!("Invalid label string: {e:?}");
return IdeviceErrorCode::InvalidArgument; return IdeviceErrorCode::InvalidArg;
} }
}; };
@@ -129,23 +135,8 @@ pub unsafe extern "C" fn usbmuxd_provider_new(
label, label,
}; };
let boxed = Box::new(UsbmuxdProviderHandle(p)); let boxed = Box::new(IdeviceProviderHandle(Box::new(p)));
unsafe { *provider = Box::into_raw(boxed) }; unsafe { *provider = Box::into_raw(boxed) };
IdeviceErrorCode::IdeviceSuccess 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)) };
}
}

View File

@@ -1,46 +1,45 @@
// Jackson Coxson // Jackson Coxson
use crate::core_device_proxy::AdapterHandle;
use crate::{IdeviceErrorCode, RUNTIME}; use crate::{IdeviceErrorCode, RUNTIME};
use idevice::IdeviceError;
use idevice::dvt::remote_server::RemoteServerClient; use idevice::dvt::remote_server::RemoteServerClient;
use idevice::tcp::adapter::Adapter; use idevice::{IdeviceError, ReadWrite};
/// Opaque handle to a RemoteServerClient /// Opaque handle to a RemoteServerClient
pub struct RemoteServerAdapterHandle(pub RemoteServerClient<Adapter>); pub struct RemoteServerHandle(pub RemoteServerClient<Box<dyn ReadWrite>>);
/// Creates a new RemoteServerClient from a ReadWrite connection /// Creates a new RemoteServerClient from a ReadWrite connection
/// ///
/// # Arguments /// # 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 /// * [`handle`] - Pointer to store the newly created RemoteServerClient handle
/// ///
/// # Returns /// # Returns
/// An error code indicating success or failure /// An error code indicating success or failure
/// ///
/// # Safety /// # 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 /// `handle` must be a valid pointer to a location where the handle will be stored
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
pub unsafe extern "C" fn remote_server_adapter_new( pub unsafe extern "C" fn remote_server_new(
adapter: *mut crate::core_device_proxy::AdapterHandle, socket: *mut Box<dyn ReadWrite>,
handle: *mut *mut RemoteServerAdapterHandle, handle: *mut *mut RemoteServerHandle,
) -> IdeviceErrorCode { ) -> IdeviceErrorCode {
if adapter.is_null() { if socket.is_null() {
return IdeviceErrorCode::InvalidArg; return IdeviceErrorCode::InvalidArg;
} }
let connection = unsafe { Box::from_raw(adapter) }; let connection = unsafe { Box::from_raw(socket) };
let res: Result<RemoteServerClient<Adapter>, IdeviceError> = RUNTIME.block_on(async move { let res: Result<RemoteServerClient<Box<dyn ReadWrite>>, IdeviceError> =
let mut client = RemoteServerClient::new(connection.0); 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 client.read_message(0).await?; // Until Message has bindings, we'll do the first read
Ok(client) Ok(client)
}); });
match res { match res {
Ok(client) => { Ok(client) => {
let boxed = Box::new(RemoteServerAdapterHandle(client)); let boxed = Box::new(RemoteServerHandle(client));
unsafe { *handle = Box::into_raw(boxed) }; unsafe { *handle = Box::into_raw(boxed) };
IdeviceErrorCode::IdeviceSuccess IdeviceErrorCode::IdeviceSuccess
} }
@@ -56,35 +55,8 @@ pub unsafe extern "C" fn remote_server_adapter_new(
/// # Safety /// # Safety
/// `handle` must be a valid pointer to a handle allocated by this library or NULL /// `handle` must be a valid pointer to a handle allocated by this library or NULL
#[unsafe(no_mangle)] #[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() { if !handle.is_null() {
let _ = unsafe { Box::from_raw(handle) }; 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
}

View File

@@ -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<Adapter>);
/// 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<CString> = 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) };
}
}
}
}

502
ffi/src/rsd.rs Normal file
View File

@@ -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<dyn ReadWrite>,
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) };
}
}

View File

@@ -1,18 +1,18 @@
use std::ffi::{CStr, c_void}; use std::ffi::{CStr, c_void};
use idevice::{IdeviceError, IdeviceService, springboardservices::SpringBoardServicesClient}; use idevice::{
IdeviceError, IdeviceService, provider::IdeviceProvider,
use crate::{ springboardservices::SpringBoardServicesClient,
IdeviceErrorCode, IdeviceHandle, RUNTIME,
provider::{TcpProviderHandle, UsbmuxdProviderHandle},
}; };
use crate::{IdeviceErrorCode, IdeviceHandle, RUNTIME, provider::IdeviceProviderHandle};
pub struct SpringBoardServicesClientHandle(pub SpringBoardServicesClient); pub struct SpringBoardServicesClientHandle(pub SpringBoardServicesClient);
/// Connects to the Springboard service using a TCP provider /// Connects to the Springboard service using a provider
/// ///
/// # Arguments /// # Arguments
/// * [`provider`] - A TcpProvider /// * [`provider`] - An IdeviceProvider
/// * [`client`] - On success, will be set to point to a newly allocated SpringBoardServicesClient handle /// * [`client`] - On success, will be set to point to a newly allocated SpringBoardServicesClient handle
/// ///
/// # Returns /// # Returns
@@ -22,8 +22,8 @@ pub struct SpringBoardServicesClientHandle(pub SpringBoardServicesClient);
/// `provider` must be a valid pointer to a handle allocated by this library /// `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 /// `client` must be a valid, non-null pointer to a location where the handle will be stored
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
pub unsafe extern "C" fn springboard_services_connect_tcp( pub unsafe extern "C" fn springboard_services_connect(
provider: *mut TcpProviderHandle, provider: *mut IdeviceProviderHandle,
client: *mut *mut SpringBoardServicesClientHandle, client: *mut *mut SpringBoardServicesClientHandle,
) -> IdeviceErrorCode { ) -> IdeviceErrorCode {
if provider.is_null() || client.is_null() { if provider.is_null() || client.is_null() {
@@ -32,18 +32,8 @@ pub unsafe extern "C" fn springboard_services_connect_tcp(
} }
let res: Result<SpringBoardServicesClient, IdeviceError> = RUNTIME.block_on(async move { let res: Result<SpringBoardServicesClient, IdeviceError> = RUNTIME.block_on(async move {
// Take ownership of the provider (without immediately dropping it) let provider_ref: &dyn IdeviceProvider = unsafe { &*(*provider).0 };
let provider_box = unsafe { Box::from_raw(provider) }; SpringBoardServicesClient::connect(provider_ref).await
// 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 { 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<SpringBoardServicesClient, IdeviceError> = 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 /// Creates a new SpringBoardServices client from an existing Idevice connection
/// ///
/// # Arguments /// # Arguments
@@ -118,7 +61,8 @@ pub unsafe extern "C" fn springboard_services_connect_usbmuxd(
/// An error code indicating success or failure /// An error code indicating success or failure
/// ///
/// # Safety /// # 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 /// `client` must be a valid, non-null pointer to a location where the handle will be stored
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
pub unsafe extern "C" fn springboard_services_new( pub unsafe extern "C" fn springboard_services_new(

View File

@@ -1,26 +1,26 @@
use std::os::raw::c_char; use std::os::raw::c_char;
use idevice::{syslog_relay::SyslogRelayClient, IdeviceError, IdeviceService}; use idevice::{
IdeviceError, IdeviceService, provider::IdeviceProvider, syslog_relay::SyslogRelayClient,
use crate::{
provider::TcpProviderHandle, IdeviceErrorCode, RUNTIME
}; };
use crate::{IdeviceErrorCode, RUNTIME, provider::IdeviceProviderHandle};
pub struct SyslogRelayClientHandle(pub SyslogRelayClient); pub struct SyslogRelayClientHandle(pub SyslogRelayClient);
/// Automatically creates and connects to syslog relay, returning a client handle /// Automatically creates and connects to syslog relay, returning a client handle
/// ///
/// # Arguments /// # Arguments
/// * [`provider`] - A TcpProvider /// * [`provider`] - An IdeviceProvider
/// * [`client`] - On success, will be set to point to a newly allocated SyslogRelayClient handle /// * [`client`] - On success, will be set to point to a newly allocated SyslogRelayClient handle
/// ///
/// # Safety /// # Safety
/// `provider` must be a valid pointer to a handle allocated by this library /// `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 /// `client` must be a valid, non-null pointer to a location where the handle will be stored
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
pub extern "C" fn syslog_relay_connect_tcp( pub unsafe extern "C" fn syslog_relay_connect_tcp(
provider: *mut TcpProviderHandle, provider: *mut IdeviceProviderHandle,
client: *mut *mut SyslogRelayClientHandle client: *mut *mut SyslogRelayClientHandle,
) -> IdeviceErrorCode { ) -> IdeviceErrorCode {
if provider.is_null() { if provider.is_null() {
log::error!("Null pointer provided"); log::error!("Null pointer provided");
@@ -28,14 +28,8 @@ pub extern "C" fn syslog_relay_connect_tcp(
} }
let res: Result<SyslogRelayClient, IdeviceError> = RUNTIME.block_on(async move { let res: Result<SyslogRelayClient, IdeviceError> = RUNTIME.block_on(async move {
let provider_box = unsafe { Box::from_raw(provider) }; let provider_ref: &dyn IdeviceProvider = unsafe { &*(*provider).0 };
SyslogRelayClient::connect(provider_ref).await
let provider_ref = &provider_box.0;
let result = SyslogRelayClient::connect(provider_ref).await;
std::mem::forget(provider_box);
result
}); });
match res { 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, /// `handle` must be a valid pointer to the handle that was allocated by this library,
/// or NULL (in which case this function does nothing) /// or NULL (in which case this function does nothing)
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
pub extern "C" fn syslog_relay_client_free( pub unsafe extern "C" fn syslog_relay_client_free(handle: *mut SyslogRelayClientHandle) {
handle: *mut SyslogRelayClientHandle
) {
if !handle.is_null() { if !handle.is_null() {
log::debug!("Freeing syslog relay client"); log::debug!("Freeing syslog relay client");
let _ = unsafe { Box::from_raw(handle) }; let _ = unsafe { Box::from_raw(handle) };
@@ -81,7 +73,7 @@ pub extern "C" fn syslog_relay_client_free(
/// `client` must be a valid pointer to a handle allocated by this library /// `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 /// `log_message` must be a valid, non-null pointer to a location where the log message will be stored
#[unsafe(no_mangle)] #[unsafe(no_mangle)]
pub extern "C" fn syslog_relay_next( pub unsafe extern "C" fn syslog_relay_next(
client: *mut SyslogRelayClientHandle, client: *mut SyslogRelayClientHandle,
log_message: *mut *mut c_char, log_message: *mut *mut c_char,
) -> IdeviceErrorCode { ) -> IdeviceErrorCode {
@@ -89,12 +81,7 @@ pub extern "C" fn syslog_relay_next(
return IdeviceErrorCode::InvalidArg; return IdeviceErrorCode::InvalidArg;
} }
let res = RUNTIME.block_on(async { let res = RUNTIME.block_on(async { unsafe { &mut *client }.0.next().await });
unsafe { &mut *client }
.0
.next()
.await
});
match res { match res {
Ok(log) => { Ok(log) => {

View File

@@ -31,6 +31,17 @@ impl<'a> AdapterStream<'a> {
pub async fn close(&mut self) -> Result<(), std::io::Error> { pub async fn close(&mut self) -> Result<(), std::io::Error> {
self.adapter.close(self.host_port).await 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<Vec<u8>, std::io::Error> {
self.adapter.recv(self.host_port).await
}
} }
impl AsyncRead for AdapterStream<'_> { impl AsyncRead for AdapterStream<'_> {