diff --git a/ffi/src/afc.rs b/ffi/src/afc.rs index 8a3d692..71ae9af 100644 --- a/ffi/src/afc.rs +++ b/ffi/src/afc.rs @@ -54,6 +54,44 @@ pub unsafe extern "C" fn afc_client_connect( } } +/// Connects to the AFC2 service using a TCP provider +/// +/// # Arguments +/// * [`provider`] - An IdeviceProvider +/// * [`client`] - On success, will be set to point to a newly allocated AfcClient handle +/// +/// # Returns +/// An IdeviceFfiError on error, null on success +/// +/// # 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 afc2_client_connect( + provider: *mut IdeviceProviderHandle, + client: *mut *mut AfcClientHandle, +) -> *mut IdeviceFfiError { + if provider.is_null() || client.is_null() { + tracing::error!("Null pointer provided"); + return ffi_err!(IdeviceError::FfiInvalidArg); + } + + let res = run_sync_local(async { + let provider_ref: &dyn IdeviceProvider = unsafe { &*(*provider).0 }; + + AfcClient::new_afc2(provider_ref).await + }); + + match res { + Ok(r) => { + let boxed = Box::new(AfcClientHandle(r)); + unsafe { *client = Box::into_raw(boxed) }; + null_mut() + } + Err(e) => ffi_err!(e), + } +} + /// Creates a new AfcClient from an existing Idevice connection /// /// # Arguments diff --git a/idevice/src/lib.rs b/idevice/src/lib.rs index 5aa23d6..5081f33 100644 --- a/idevice/src/lib.rs +++ b/idevice/src/lib.rs @@ -76,6 +76,7 @@ pub trait IdeviceService: Sized { async fn connect(provider: &dyn IdeviceProvider) -> Result { let mut lockdown = LockdownClient::connect(provider).await?; + #[cfg(feature = "openssl")] let legacy = lockdown .get_value(Some("ProductVersion"), None) .await @@ -87,6 +88,9 @@ pub trait IdeviceService: Sized { .map(|x| x < 5) .unwrap_or(false); + #[cfg(not(feature = "openssl"))] + let legacy = false; + lockdown .start_session(&provider.get_pairing_file().await?) .await?; diff --git a/idevice/src/services/afc/mod.rs b/idevice/src/services/afc/mod.rs index f868585..8f8d71d 100644 --- a/idevice/src/services/afc/mod.rs +++ b/idevice/src/services/afc/mod.rs @@ -13,6 +13,7 @@ use tracing::warn; use crate::{ Idevice, IdeviceError, IdeviceService, afc::file::{FileDescriptor, OwnedFileDescriptor}, + lockdown::LockdownClient, obf, }; @@ -91,6 +92,43 @@ impl AfcClient { } } + /// Connects to afc2 from a provider + pub async fn new_afc2( + provider: &dyn crate::provider::IdeviceProvider, + ) -> Result { + let mut lockdown = LockdownClient::connect(provider).await?; + + #[cfg(feature = "openssl")] + let legacy = lockdown + .get_value(Some("ProductVersion"), None) + .await + .ok() + .as_ref() + .and_then(|x| x.as_string()) + .and_then(|x| x.split(".").next()) + .and_then(|x| x.parse::().ok()) + .map(|x| x < 5) + .unwrap_or(false); + + #[cfg(not(feature = "openssl"))] + let legacy = false; + + lockdown + .start_session(&provider.get_pairing_file().await?) + .await?; + + let (port, ssl) = lockdown.start_service(obf!("com.apple.afc2")).await?; + + let mut idevice = provider.connect(port).await?; + if ssl { + idevice + .start_session(&provider.get_pairing_file().await?, legacy) + .await?; + } + + Self::from_stream(idevice).await + } + /// Lists the contents of a directory on the device /// /// # Arguments