From 3dd920cd4d569f2cff96b2191563547755477c57 Mon Sep 17 00:00:00 2001 From: Jackson Coxson Date: Mon, 24 Mar 2025 18:21:32 -0600 Subject: [PATCH] Installation proxy bindings --- ffi/examples/list_apps.c | 90 ++++++++++++++ ffi/src/installation_proxy.rs | 215 ++++++++++++++++++++++++++++++++++ 2 files changed, 305 insertions(+) create mode 100644 ffi/examples/list_apps.c diff --git a/ffi/examples/list_apps.c b/ffi/examples/list_apps.c new file mode 100644 index 0000000..7b72deb --- /dev/null +++ b/ffi/examples/list_apps.c @@ -0,0 +1,90 @@ +// Jackson Coxson + +#include "idevice.h" +#include +#include +#include +#include + +int main() { + // Initialize logger + idevice_init_logger(Debug, Disabled, NULL); + + // Create the socket address (replace with your device's IP) + struct sockaddr_in addr; + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(LOCKDOWN_PORT); + inet_pton(AF_INET, "10.7.0.2", &addr.sin_addr); + + // Read pairing file (replace with your pairing file path) + IdevicePairingFile *pairing_file = NULL; + IdeviceErrorCode err = idevice_pairing_file_read( + "/Users/jacksoncoxson/Desktop/storage/00008140-001809302684801C.plist", + &pairing_file); + if (err != IdeviceSuccess) { + fprintf(stderr, "Failed to read pairing file: %d\n", err); + return 1; + } + printf("Read the pairing file"); + + // Create TCP provider + TcpProviderHandle *provider = NULL; + err = idevice_tcp_provider_new((struct sockaddr *)&addr, pairing_file, + "ExampleProvider", &provider); + if (err != IdeviceSuccess) { + fprintf(stderr, "Failed to create TCP provider: %d\n", err); + idevice_pairing_file_free(pairing_file); + return 1; + } + + // Connect to installation proxy + InstallationProxyClientHandle *client = NULL; + err = installation_proxy_connect_tcp(provider, &client); + if (err != IdeviceSuccess) { + fprintf(stderr, "Failed to connect to installation proxy: %d\n", err); + tcp_provider_free(provider); + return 1; + } + + // Get all apps (pass NULL for both filters to get everything) + void *apps = NULL; + size_t apps_len = 0; + err = installation_proxy_get_apps(client, + NULL, // application_type filter + NULL, // bundle_identifiers filter + 0, // bundle_identifiers length + &apps, &apps_len); + if (err != IdeviceSuccess) { + fprintf(stderr, "Failed to get apps: %d\n", err); + installation_proxy_client_free(client); + tcp_provider_free(provider); + return 1; + } + + // Cast the result to plist_t array (assuming the binding returns plist_t) + plist_t *app_list = (plist_t *)apps; + + // Iterate through apps (this is a simplified example - you'd need proper + // plist handling) + printf("Found %zu apps:\n", apps_len); + for (size_t i = 0; i < apps_len; i++) { + plist_t app = app_list[i]; + + // Get CFBundleIdentifier (you'd need proper plist dict access here) + plist_t bundle_id_node = plist_dict_get_item(app, "CFBundleIdentifier"); + if (bundle_id_node) { + char *bundle_id = NULL; + plist_get_string_val(bundle_id_node, &bundle_id); + printf("- %s\n", bundle_id); + free(bundle_id); + } + } + + // Cleanup + // Note: You'd need to properly free the plist array here + installation_proxy_client_free(client); + tcp_provider_free(provider); + + return 0; +} diff --git a/ffi/src/installation_proxy.rs b/ffi/src/installation_proxy.rs index 447a0b2..8973a50 100644 --- a/ffi/src/installation_proxy.rs +++ b/ffi/src/installation_proxy.rs @@ -1 +1,216 @@ // Jackson Coxson + +use std::ffi::c_void; + +use idevice::{IdeviceError, IdeviceService, installation_proxy::InstallationProxyClient}; + +use crate::{ + IdeviceErrorCode, IdeviceHandle, RUNTIME, + provider::{TcpProviderHandle, UsbmuxdProviderHandle}, + util, +}; + +pub struct InstallationProxyClientHandle(pub InstallationProxyClient); +#[allow(non_camel_case_types)] +pub struct plist_t; + +/// Automatically creates and connects to Installation Proxy, returning a client handle +/// +/// # Arguments +/// * [`provider`] - A TcpProvider +/// * [`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_tcp( + provider: *mut TcpProviderHandle, + client: *mut *mut InstallationProxyClientHandle, +) -> IdeviceErrorCode { + if provider.is_null() { + log::error!("Provider is null"); + return IdeviceErrorCode::InvalidArg; + } + let provider = unsafe { Box::from_raw(provider) }.0; + + let res: Result = RUNTIME.block_on(async move { + let res = InstallationProxyClient::connect(&provider).await; + std::mem::forget(provider); + res + }); + + match res { + Ok(r) => { + let boxed = Box::new(InstallationProxyClientHandle(r)); + unsafe { *client = Box::into_raw(boxed) }; + IdeviceErrorCode::IdeviceSuccess + } + Err(e) => e.into(), + } +} + +/// Automatically creates and connects to Installation Proxy, returning a client handle +/// +/// # Arguments +/// * [`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 provider = unsafe { Box::from_raw(provider) }.0; + + let res: Result = RUNTIME.block_on(async move { + let res = InstallationProxyClient::connect(&provider).await; + std::mem::forget(provider); + res + }); + + match res { + Ok(r) => { + let boxed = Box::new(InstallationProxyClientHandle(r)); + unsafe { *client = Box::into_raw(boxed) }; + IdeviceErrorCode::IdeviceSuccess + } + Err(e) => e.into(), + } +} + +/// Automatically creates and connects to Installation Proxy, returning a client handle +/// +/// # Arguments +/// * [`socket`] - An IdeviceSocket handle +/// * [`client`] - On success, will be set to point to a newly allocated InstallationProxyClient handle +/// +/// # Returns +/// An error code indicating success or failure +/// +/// # Safety +/// `socket` 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_new( + socket: *mut IdeviceHandle, + client: *mut *mut InstallationProxyClientHandle, +) -> IdeviceErrorCode { + if socket.is_null() { + return IdeviceErrorCode::InvalidArg; + } + let socket = unsafe { Box::from_raw(socket) }.0; + let r = InstallationProxyClient::new(socket); + let boxed = Box::new(InstallationProxyClientHandle(r)); + unsafe { *client = Box::into_raw(boxed) }; + IdeviceErrorCode::IdeviceSuccess +} + +/// Gets installed apps on the device +/// +/// # Arguments +/// * `client` - A valid InstallationProxyClient handle +/// * `application_type` - The application type to filter by (optional, NULL for "Any") +/// * `bundle_identifiers` - The identifiers to filter by (optional, NULL for all apps) +/// * `out_result` - On success, will be set to point to a newly allocated array of PlistRef +/// +/// # Returns +/// An error code indicating success or failure +/// +/// # Safety +/// `client` must be a valid pointer to a handle allocated by this library +/// `out_result` must be a valid, non-null pointer to a location where the result will be stored +#[unsafe(no_mangle)] +pub unsafe extern "C" fn installation_proxy_get_apps( + client: *mut InstallationProxyClientHandle, + application_type: *const libc::c_char, + bundle_identifiers: *const *const libc::c_char, + bundle_identifiers_len: libc::size_t, + out_result: *mut *mut c_void, + out_result_len: *mut libc::size_t, +) -> IdeviceErrorCode { + if client.is_null() || out_result.is_null() || out_result_len.is_null() { + log::error!("Invalid arguments: {client:?}, {out_result:?}"); + return IdeviceErrorCode::InvalidArg; + } + let client = unsafe { &mut *client }; + + let app_type = if application_type.is_null() { + None + } else { + Some(unsafe { + std::ffi::CStr::from_ptr(application_type) + .to_string_lossy() + .into_owned() + }) + }; + + let bundle_ids = if bundle_identifiers.is_null() { + None + } else { + let ids = unsafe { std::slice::from_raw_parts(bundle_identifiers, bundle_identifiers_len) }; + Some( + ids.iter() + .map(|&s| { + unsafe { std::ffi::CStr::from_ptr(s) } + .to_string_lossy() + .into_owned() + }) + .collect(), + ) + }; + + let res: Result, IdeviceError> = RUNTIME.block_on(async { + client.0.get_apps(app_type, bundle_ids).await.map(|apps| { + apps.into_values() + .map(|v| util::plist_to_libplist(&v).get_pointer()) + .collect() + }) + }); + + match res { + Ok(r) => { + let len = r.len(); + let boxed_slice = r.into_boxed_slice(); + let ptr = boxed_slice.as_ptr(); + std::mem::forget(boxed_slice); + + unsafe { + *out_result = ptr as *mut c_void; + *out_result_len = len; + } + IdeviceErrorCode::IdeviceSuccess + } + Err(e) => e.into(), + } +} + +/// Frees a handle +/// +/// # Arguments +/// * [`handle`] - The handle to free +/// +/// # Safety +/// `handle` must be a valid pointer to the handle that was allocated by this library, +/// or NULL (in which case this function does nothing) +#[unsafe(no_mangle)] +pub unsafe extern "C" fn installation_proxy_client_free( + handle: *mut InstallationProxyClientHandle, +) { + if !handle.is_null() { + let _ = unsafe { Box::from_raw(handle) }; + } +}