mirror of
https://github.com/jkcoxson/idevice.git
synced 2026-03-02 14:36:16 +01:00
feat: impl parts of diagnostics and mobilebackup2 (#20)
* feat: add udid cache to idevice * feat: impl diagnostics * feat: impl mobilebackup2 * docs: update README.md * fix: make clippy happy * fix: make linux clippy happy * fix: make linux clippy happy again * fix: make clippy happy again * fix: small updates
This commit is contained in:
@@ -75,6 +75,7 @@ installation_proxy = []
|
||||
springboardservices = []
|
||||
misagent = []
|
||||
mobile_image_mounter = ["dep:sha2"]
|
||||
mobilebackup2 = []
|
||||
location_simulation = []
|
||||
pair = ["chrono/default", "tokio/time", "dep:sha2", "dep:rsa", "dep:x509-cert"]
|
||||
obfuscate = ["dep:obfstr"]
|
||||
@@ -109,6 +110,7 @@ full = [
|
||||
"location_simulation",
|
||||
"misagent",
|
||||
"mobile_image_mounter",
|
||||
"mobilebackup2",
|
||||
"pair",
|
||||
"restore_service",
|
||||
"rsd",
|
||||
@@ -123,4 +125,4 @@ full = [
|
||||
]
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
all-features = true
|
||||
@@ -74,6 +74,11 @@ pub trait IdeviceService: Sized {
|
||||
lockdown
|
||||
.start_session(&provider.get_pairing_file().await?)
|
||||
.await?;
|
||||
// Best-effort fetch UDID for downstream defaults (e.g., MobileBackup2 Target/Source identifiers)
|
||||
let udid_value = match lockdown.get_value(Some("UniqueDeviceID"), None).await {
|
||||
Ok(v) => v.as_string().map(|s| s.to_string()),
|
||||
Err(_) => None,
|
||||
};
|
||||
|
||||
let (port, ssl) = lockdown.start_service(Self::service_name()).await?;
|
||||
|
||||
@@ -84,6 +89,10 @@ pub trait IdeviceService: Sized {
|
||||
.await?;
|
||||
}
|
||||
|
||||
if let Some(udid) = udid_value {
|
||||
idevice.set_udid(udid);
|
||||
}
|
||||
|
||||
Self::from_stream(idevice).await
|
||||
}
|
||||
|
||||
@@ -123,6 +132,8 @@ pub struct Idevice {
|
||||
socket: Option<Box<dyn ReadWrite>>,
|
||||
/// Unique label identifying this connection
|
||||
label: String,
|
||||
/// Cached device UDID for convenience in higher-level protocols
|
||||
udid: Option<String>,
|
||||
}
|
||||
|
||||
impl Idevice {
|
||||
@@ -135,6 +146,7 @@ impl Idevice {
|
||||
Self {
|
||||
socket: Some(socket),
|
||||
label: label.into(),
|
||||
udid: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,6 +154,16 @@ impl Idevice {
|
||||
self.socket
|
||||
}
|
||||
|
||||
/// Sets cached UDID
|
||||
pub fn set_udid(&mut self, udid: impl Into<String>) {
|
||||
self.udid = Some(udid.into());
|
||||
}
|
||||
|
||||
/// Returns cached UDID if available
|
||||
pub fn udid(&self) -> Option<&str> {
|
||||
self.udid.as_deref()
|
||||
}
|
||||
|
||||
/// Queries the device type
|
||||
///
|
||||
/// Sends a QueryType request and parses the response
|
||||
|
||||
@@ -20,7 +20,7 @@ impl IdeviceService for DiagnosticsRelayClient {
|
||||
}
|
||||
|
||||
impl DiagnosticsRelayClient {
|
||||
/// Creates a new client from an existing device connection
|
||||
/// Creates a new client from an existing device connection
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `idevice` - Pre-established device connection
|
||||
@@ -74,7 +74,223 @@ impl DiagnosticsRelayClient {
|
||||
.and_then(|x| x.into_dictionary())
|
||||
.and_then(|mut x| x.remove("IORegistry"))
|
||||
.and_then(|x| x.into_dictionary());
|
||||
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
||||
/// Requests MobileGestalt information from the device
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `keys` - Optional list of specific keys to request. If None, requests all available keys
|
||||
///
|
||||
/// # Returns
|
||||
/// A dictionary containing the requested MobileGestalt information
|
||||
pub async fn mobilegestalt(
|
||||
&mut self,
|
||||
keys: Option<Vec<String>>,
|
||||
) -> Result<Option<plist::Dictionary>, IdeviceError> {
|
||||
let mut req = plist::Dictionary::new();
|
||||
req.insert("Request".into(), "MobileGestalt".into());
|
||||
|
||||
if let Some(keys) = keys {
|
||||
let keys_array: Vec<plist::Value> = keys.into_iter().map(|k| k.into()).collect();
|
||||
req.insert("MobileGestaltKeys".into(), plist::Value::Array(keys_array));
|
||||
}
|
||||
|
||||
self.idevice
|
||||
.send_plist(plist::Value::Dictionary(req))
|
||||
.await?;
|
||||
let mut res = self.idevice.read_plist().await?;
|
||||
|
||||
match res.get("Status").and_then(|x| x.as_string()) {
|
||||
Some("Success") => {}
|
||||
_ => {
|
||||
return Err(IdeviceError::UnexpectedResponse);
|
||||
}
|
||||
}
|
||||
|
||||
let res = res
|
||||
.remove("Diagnostics")
|
||||
.and_then(|x| x.into_dictionary());
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
/// Requests gas gauge information from the device
|
||||
///
|
||||
/// # Returns
|
||||
/// A dictionary containing gas gauge (battery) information
|
||||
pub async fn gasguage(&mut self) -> Result<Option<plist::Dictionary>, IdeviceError> {
|
||||
let mut req = plist::Dictionary::new();
|
||||
req.insert("Request".into(), "GasGauge".into());
|
||||
|
||||
self.idevice
|
||||
.send_plist(plist::Value::Dictionary(req))
|
||||
.await?;
|
||||
let mut res = self.idevice.read_plist().await?;
|
||||
|
||||
match res.get("Status").and_then(|x| x.as_string()) {
|
||||
Some("Success") => {}
|
||||
_ => {
|
||||
return Err(IdeviceError::UnexpectedResponse);
|
||||
}
|
||||
}
|
||||
|
||||
let res = res
|
||||
.remove("Diagnostics")
|
||||
.and_then(|x| x.into_dictionary());
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
/// Requests NAND information from the device
|
||||
///
|
||||
/// # Returns
|
||||
/// A dictionary containing NAND flash information
|
||||
pub async fn nand(&mut self) -> Result<Option<plist::Dictionary>, IdeviceError> {
|
||||
let mut req = plist::Dictionary::new();
|
||||
req.insert("Request".into(), "NAND".into());
|
||||
|
||||
self.idevice
|
||||
.send_plist(plist::Value::Dictionary(req))
|
||||
.await?;
|
||||
let mut res = self.idevice.read_plist().await?;
|
||||
|
||||
match res.get("Status").and_then(|x| x.as_string()) {
|
||||
Some("Success") => {}
|
||||
_ => {
|
||||
return Err(IdeviceError::UnexpectedResponse);
|
||||
}
|
||||
}
|
||||
|
||||
let res = res
|
||||
.remove("Diagnostics")
|
||||
.and_then(|x| x.into_dictionary());
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
/// Requests all available diagnostics information
|
||||
///
|
||||
/// # Returns
|
||||
/// A dictionary containing all diagnostics information
|
||||
pub async fn all(&mut self) -> Result<Option<plist::Dictionary>, IdeviceError> {
|
||||
let mut req = plist::Dictionary::new();
|
||||
req.insert("Request".into(), "All".into());
|
||||
|
||||
self.idevice
|
||||
.send_plist(plist::Value::Dictionary(req))
|
||||
.await?;
|
||||
let mut res = self.idevice.read_plist().await?;
|
||||
|
||||
match res.get("Status").and_then(|x| x.as_string()) {
|
||||
Some("Success") => {}
|
||||
_ => {
|
||||
return Err(IdeviceError::UnexpectedResponse);
|
||||
}
|
||||
}
|
||||
|
||||
let res = res
|
||||
.remove("Diagnostics")
|
||||
.and_then(|x| x.into_dictionary());
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
/// Restarts the device
|
||||
///
|
||||
/// # Returns
|
||||
/// Result indicating success or failure
|
||||
pub async fn restart(&mut self) -> Result<(), IdeviceError> {
|
||||
let mut req = plist::Dictionary::new();
|
||||
req.insert("Request".into(), "Restart".into());
|
||||
|
||||
self.idevice
|
||||
.send_plist(plist::Value::Dictionary(req))
|
||||
.await?;
|
||||
let res = self.idevice.read_plist().await?;
|
||||
|
||||
match res.get("Status").and_then(|x| x.as_string()) {
|
||||
Some("Success") => Ok(()),
|
||||
_ => Err(IdeviceError::UnexpectedResponse),
|
||||
}
|
||||
}
|
||||
|
||||
/// Shuts down the device
|
||||
///
|
||||
/// # Returns
|
||||
/// Result indicating success or failure
|
||||
pub async fn shutdown(&mut self) -> Result<(), IdeviceError> {
|
||||
let mut req = plist::Dictionary::new();
|
||||
req.insert("Request".into(), "Shutdown".into());
|
||||
|
||||
self.idevice
|
||||
.send_plist(plist::Value::Dictionary(req))
|
||||
.await?;
|
||||
let res = self.idevice.read_plist().await?;
|
||||
|
||||
match res.get("Status").and_then(|x| x.as_string()) {
|
||||
Some("Success") => Ok(()),
|
||||
_ => Err(IdeviceError::UnexpectedResponse),
|
||||
}
|
||||
}
|
||||
|
||||
/// Puts the device to sleep
|
||||
///
|
||||
/// # Returns
|
||||
/// Result indicating success or failure
|
||||
pub async fn sleep(&mut self) -> Result<(), IdeviceError> {
|
||||
let mut req = plist::Dictionary::new();
|
||||
req.insert("Request".into(), "Sleep".into());
|
||||
|
||||
self.idevice
|
||||
.send_plist(plist::Value::Dictionary(req))
|
||||
.await?;
|
||||
let res = self.idevice.read_plist().await?;
|
||||
|
||||
match res.get("Status").and_then(|x| x.as_string()) {
|
||||
Some("Success") => Ok(()),
|
||||
_ => Err(IdeviceError::UnexpectedResponse),
|
||||
}
|
||||
}
|
||||
|
||||
/// Requests WiFi diagnostics from the device
|
||||
pub async fn wifi(&mut self) -> Result<Option<plist::Dictionary>, IdeviceError> {
|
||||
let mut req = plist::Dictionary::new();
|
||||
req.insert("Request".into(), "WiFi".into());
|
||||
|
||||
self.idevice
|
||||
.send_plist(plist::Value::Dictionary(req))
|
||||
.await?;
|
||||
let mut res = self.idevice.read_plist().await?;
|
||||
|
||||
match res.get("Status").and_then(|x| x.as_string()) {
|
||||
Some("Success") => {}
|
||||
_ => {
|
||||
return Err(IdeviceError::UnexpectedResponse);
|
||||
}
|
||||
}
|
||||
|
||||
let res = res
|
||||
.remove("Diagnostics")
|
||||
.and_then(|x| x.into_dictionary());
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
/// Sends Goodbye request signaling end of communication
|
||||
pub async fn goodbye(&mut self) -> Result<(), IdeviceError> {
|
||||
let mut req = plist::Dictionary::new();
|
||||
req.insert("Request".into(), "Goodbye".into());
|
||||
self.idevice
|
||||
.send_plist(plist::Value::Dictionary(req))
|
||||
.await?;
|
||||
let res = self.idevice.read_plist().await?;
|
||||
match res.get("Status").and_then(|x| x.as_string()) {
|
||||
Some("Success") => Ok(()),
|
||||
Some("UnknownRequest") => Err(IdeviceError::UnexpectedResponse),
|
||||
_ => Err(IdeviceError::UnexpectedResponse),
|
||||
}
|
||||
}
|
||||
}
|
||||
1115
idevice/src/services/mobilebackup2.rs
Normal file
1115
idevice/src/services/mobilebackup2.rs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -27,6 +27,8 @@ pub mod lockdown;
|
||||
pub mod misagent;
|
||||
#[cfg(feature = "mobile_image_mounter")]
|
||||
pub mod mobile_image_mounter;
|
||||
#[cfg(feature = "mobilebackup2")]
|
||||
pub mod mobilebackup2;
|
||||
#[cfg(feature = "syslog_relay")]
|
||||
pub mod os_trace_relay;
|
||||
#[cfg(feature = "restore_service")]
|
||||
|
||||
Reference in New Issue
Block a user