// Jackson Coxson #[cfg(feature = "tunnel_tcp_stack")] pub mod adapter; #[cfg(feature = "afc")] pub mod afc; #[cfg(feature = "amfi")] pub mod amfi; #[cfg(feature = "core_device")] pub mod core_device; #[cfg(feature = "core_device_proxy")] pub mod core_device_proxy; #[cfg(feature = "debug_proxy")] pub mod debug_proxy; mod errors; #[cfg(feature = "heartbeat")] pub mod heartbeat; #[cfg(feature = "installation_proxy")] pub mod installation_proxy; #[cfg(feature = "location_simulation")] pub mod location_simulation; pub mod lockdown; pub mod logging; #[cfg(feature = "misagent")] pub mod misagent; #[cfg(feature = "mobile_image_mounter")] pub mod mobile_image_mounter; #[cfg(feature = "syslog_relay")] pub mod os_trace_relay; mod pairing_file; #[cfg(feature = "dvt")] pub mod process_control; pub mod provider; #[cfg(feature = "dvt")] pub mod remote_server; #[cfg(feature = "xpc")] pub mod rsd; #[cfg(feature = "springboardservices")] pub mod springboardservices; #[cfg(feature = "syslog_relay")] pub mod syslog_relay; #[cfg(feature = "usbmuxd")] pub mod usbmuxd; pub mod util; pub use errors::*; pub use pairing_file::*; use idevice::{Idevice, IdeviceSocket, ReadWrite}; use once_cell::sync::Lazy; use std::{ ffi::{CStr, CString, c_char}, ptr::null_mut, }; use tokio::runtime::{self, Runtime}; #[cfg(unix)] use crate::util::{idevice_sockaddr, idevice_socklen_t}; static RUNTIME: Lazy = Lazy::new(|| { runtime::Builder::new_multi_thread() .enable_io() .enable_time() .build() .unwrap() }); pub const LOCKDOWN_PORT: u16 = 62078; #[repr(C)] pub struct ReadWriteOpaque { pub inner: Option>, } /// Opaque C-compatible handle to an Idevice connection pub struct IdeviceHandle(pub Idevice); pub struct IdeviceSocketHandle(IdeviceSocket); /// Stub to avoid header problems #[allow(non_camel_case_types)] pub type plist_t = *mut std::ffi::c_void; // https://github.com/mozilla/cbindgen/issues/539 #[allow(non_camel_case_types, unused)] struct sockaddr; /// Creates a new Idevice connection /// /// # Arguments /// * [`socket`] - Socket for communication with the device /// * [`label`] - Label for the connection /// * [`idevice`] - On success, will be set to point to a newly allocated Idevice handle /// /// # Returns /// An IdeviceFfiError on error, null on success /// /// # Safety /// `label` must be a valid null-terminated C string /// `idevice` must be a valid, non-null pointer to a location where the handle will be stored #[unsafe(no_mangle)] pub unsafe extern "C" fn idevice_new( socket: *mut IdeviceSocketHandle, label: *const c_char, idevice: *mut *mut IdeviceHandle, ) -> *mut IdeviceFfiError { if socket.is_null() || label.is_null() || idevice.is_null() { return ffi_err!(IdeviceError::FfiInvalidArg); } // Get socket ownership let socket_box = unsafe { Box::from_raw(socket) }; // Convert C string to Rust string let c_str = match unsafe { CStr::from_ptr(label).to_str() } { Ok(s) => s, Err(_) => return ffi_err!(IdeviceError::FfiInvalidString), }; // Create new Idevice instance let dev = Idevice::new((*socket_box).0, c_str); let boxed = Box::new(IdeviceHandle(dev)); unsafe { *idevice = Box::into_raw(boxed) }; null_mut() } /// Creates a new Idevice connection /// /// # Arguments /// * [`addr`] - The socket address to connect to /// * [`addr_len`] - Length of the socket /// * [`label`] - Label for the connection /// * [`idevice`] - On success, will be set to point to a newly allocated Idevice handle /// /// # Returns /// An IdeviceFfiError on error, null on success /// /// # Safety /// `addr` must be a valid sockaddr /// `label` must be a valid null-terminated C string /// `idevice` must be a valid, non-null pointer to a location where the handle will be stored #[cfg(unix)] #[unsafe(no_mangle)] pub unsafe extern "C" fn idevice_new_tcp_socket( addr: *const idevice_sockaddr, addr_len: idevice_socklen_t, label: *const c_char, idevice: *mut *mut IdeviceHandle, ) -> *mut IdeviceFfiError { use crate::util::SockAddr; if addr.is_null() || label.is_null() || idevice.is_null() { log::error!("null pointer(s) to idevice_new_tcp_socket"); return ffi_err!(IdeviceError::FfiInvalidArg); } let addr = addr as *const SockAddr; let label = match unsafe { CStr::from_ptr(label).to_str() } { Ok(s) => s, Err(_) => return ffi_err!(IdeviceError::FfiInvalidString), }; let addr = match util::c_socket_to_rust(addr, addr_len) { Ok(a) => a, Err(e) => return ffi_err!(e), }; let device = RUNTIME.block_on(async move { let stream = tokio::net::TcpStream::connect(addr).await?; Ok::(idevice::Idevice::new( Box::new(stream), label, )) }); match device { Ok(dev) => { let boxed = Box::new(IdeviceHandle(dev)); unsafe { *idevice = Box::into_raw(boxed) }; std::ptr::null_mut() } Err(e) => ffi_err!(e), } } /// Gets the device type /// /// # Arguments /// * [`idevice`] - The Idevice handle /// * [`device_type`] - On success, will be set to point to a newly allocated string containing the device type /// /// # Returns /// An IdeviceFfiError on error, null on success /// /// # Safety /// `idevice` must be a valid, non-null pointer to an Idevice handle /// `device_type` must be a valid, non-null pointer to a location where the string pointer will be stored #[unsafe(no_mangle)] pub unsafe extern "C" fn idevice_get_type( idevice: *mut IdeviceHandle, device_type: *mut *mut c_char, ) -> *mut IdeviceFfiError { if idevice.is_null() || device_type.is_null() { return ffi_err!(IdeviceError::FfiInvalidArg); } // Get the Idevice reference let dev = unsafe { &mut (*idevice).0 }; // Run the get_type method in the runtime let result = RUNTIME.block_on(async { dev.get_type().await }); match result { Ok(type_str) => match CString::new(type_str) { Ok(c_string) => { unsafe { *device_type = c_string.into_raw() }; null_mut() } Err(_) => ffi_err!(IdeviceError::FfiInvalidString), }, Err(e) => ffi_err!(e), } } /// Performs RSD checkin /// /// # Arguments /// * [`idevice`] - The Idevice handle /// /// # Returns /// An IdeviceFfiError on error, null on success /// /// # Safety /// `idevice` must be a valid, non-null pointer to an Idevice handle #[unsafe(no_mangle)] pub unsafe extern "C" fn idevice_rsd_checkin(idevice: *mut IdeviceHandle) -> *mut IdeviceFfiError { if idevice.is_null() { return ffi_err!(IdeviceError::FfiInvalidArg); } // Get the Idevice reference let dev = unsafe { &mut (*idevice).0 }; // Run the rsd_checkin method in the runtime let result = RUNTIME.block_on(async { dev.rsd_checkin().await }); match result { Ok(_) => null_mut(), Err(e) => ffi_err!(e), } } /// Starts a TLS session /// /// # Arguments /// * [`idevice`] - The Idevice handle /// * [`pairing_file`] - The pairing file to use for TLS /// /// # Returns /// An IdeviceFfiError on error, null on success /// /// # Safety /// `idevice` must be a valid, non-null pointer to an Idevice handle /// `pairing_file` must be a valid, non-null pointer to a pairing file handle #[unsafe(no_mangle)] pub unsafe extern "C" fn idevice_start_session( idevice: *mut IdeviceHandle, pairing_file: *const IdevicePairingFile, ) -> *mut IdeviceFfiError { if idevice.is_null() || pairing_file.is_null() { return ffi_err!(IdeviceError::FfiInvalidArg); } // Get the Idevice reference let dev = unsafe { &mut (*idevice).0 }; // Get the pairing file reference let pf = unsafe { &(*pairing_file).0 }; // Run the start_session method in the runtime let result = RUNTIME.block_on(async { dev.start_session(pf).await }); match result { Ok(_) => null_mut(), Err(e) => ffi_err!(e), } } /// Frees an Idevice handle /// /// # Arguments /// * [`idevice`] - The Idevice handle to free /// /// # Safety /// `idevice` must be a valid pointer to an Idevice handle that was allocated by this library, /// or NULL (in which case this function does nothing) #[unsafe(no_mangle)] pub unsafe extern "C" fn idevice_free(idevice: *mut IdeviceHandle) { if !idevice.is_null() { let _ = unsafe { Box::from_raw(idevice) }; } } /// Frees a string allocated by this library /// /// # Arguments /// * [`string`] - The string to free /// /// # Safety /// `string` must be a valid pointer to a string that was allocated by this library, /// or NULL (in which case this function does nothing) #[unsafe(no_mangle)] pub unsafe extern "C" fn idevice_string_free(string: *mut c_char) { if !string.is_null() { let _ = unsafe { CString::from_raw(string) }; } } /// Frees data allocated by this library /// /// # Arguments /// * [`data`] - The data to free /// /// # Safety /// `data` must be a valid pointer to data that was allocated by this library, /// or NULL (in which case this function does nothing) #[unsafe(no_mangle)] pub unsafe extern "C" fn idevice_data_free(data: *mut u8, len: usize) { if !data.is_null() { let _ = unsafe { std::slice::from_raw_parts(data, len) }; } }