mirror of
https://github.com/jkcoxson/idevice.git
synced 2026-03-02 14:36:16 +01:00
Add basic image mounter support
This commit is contained in:
12
src/lib.rs
12
src/lib.rs
@@ -3,6 +3,7 @@
|
||||
pub mod heartbeat;
|
||||
pub mod installation_proxy;
|
||||
pub mod lockdownd;
|
||||
pub mod mounter;
|
||||
pub mod pairing_file;
|
||||
|
||||
use log::{debug, error};
|
||||
@@ -55,6 +56,15 @@ impl Idevice {
|
||||
}
|
||||
}
|
||||
|
||||
/// Sends raw bytes to the socket
|
||||
fn send_raw(&mut self, message: &[u8]) -> Result<(), IdeviceError> {
|
||||
if let Some(socket) = &mut self.socket {
|
||||
Ok(socket.write_all(message)?)
|
||||
} else {
|
||||
Err(IdeviceError::NoEstablishedConnection)
|
||||
}
|
||||
}
|
||||
|
||||
/// Read a plist from the socket
|
||||
fn read_plist(&mut self) -> Result<plist::Dictionary, IdeviceError> {
|
||||
if let Some(socket) = &mut self.socket {
|
||||
@@ -120,6 +130,8 @@ pub enum IdeviceError {
|
||||
NoEstablishedConnection,
|
||||
#[error("device went to sleep")]
|
||||
HeartbeatSleepyTime,
|
||||
#[error("not found")]
|
||||
NotFound,
|
||||
#[error("unknown error `{0}` returned from device")]
|
||||
UnknownErrorType(String),
|
||||
}
|
||||
|
||||
122
src/mounter.rs
Normal file
122
src/mounter.rs
Normal file
@@ -0,0 +1,122 @@
|
||||
// Jackson Coxson
|
||||
|
||||
use crate::{Idevice, IdeviceError};
|
||||
|
||||
pub struct ImageMounter {
|
||||
idevice: Idevice,
|
||||
}
|
||||
|
||||
impl ImageMounter {
|
||||
pub fn new(idevice: Idevice) -> Self {
|
||||
Self { idevice }
|
||||
}
|
||||
|
||||
pub fn copy_devices(&mut self) -> Result<Vec<plist::Value>, IdeviceError> {
|
||||
let mut req = plist::Dictionary::new();
|
||||
req.insert("Command".into(), "CopyDevices".into());
|
||||
self.idevice.send_plist(plist::Value::Dictionary(req))?;
|
||||
let mut res = self.idevice.read_plist()?;
|
||||
|
||||
match res.remove("EntryList") {
|
||||
Some(plist::Value::Array(i)) => Ok(i),
|
||||
_ => Err(IdeviceError::UnexpectedResponse),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn upload_image(
|
||||
&mut self,
|
||||
image_type: impl Into<String>,
|
||||
image: &[u8],
|
||||
signature: Vec<u8>,
|
||||
) -> Result<(), IdeviceError> {
|
||||
let image_type = image_type.into();
|
||||
|
||||
let mut req = plist::Dictionary::new();
|
||||
req.insert("Command".into(), "ReceiveBytes".into());
|
||||
req.insert("ImageType".into(), image_type.into());
|
||||
req.insert("ImageSize".into(), (image.len() as u64).into());
|
||||
req.insert("ImageSignature".into(), plist::Value::Data(signature));
|
||||
self.idevice.send_plist(plist::Value::Dictionary(req))?;
|
||||
|
||||
let res = self.idevice.read_plist()?;
|
||||
match res.get("Status") {
|
||||
Some(plist::Value::String(s)) => {
|
||||
if s.as_str() != "ReceiveBytesAck" {
|
||||
log::error!("Received bad response to SendBytes: {s:?}");
|
||||
return Err(IdeviceError::UnexpectedResponse);
|
||||
}
|
||||
}
|
||||
_ => return Err(IdeviceError::UnexpectedResponse),
|
||||
}
|
||||
|
||||
self.idevice.send_raw(image)?;
|
||||
|
||||
let res = self.idevice.read_plist()?;
|
||||
match res.get("Status") {
|
||||
Some(plist::Value::String(s)) => {
|
||||
if s.as_str() != "Success" {
|
||||
log::error!("Image send failure: {s:?}");
|
||||
return Err(IdeviceError::UnexpectedResponse);
|
||||
}
|
||||
}
|
||||
_ => return Err(IdeviceError::UnexpectedResponse),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn mount_image(
|
||||
&mut self,
|
||||
image_type: impl Into<String>,
|
||||
signature: Vec<u8>,
|
||||
trust_cache: Vec<u8>,
|
||||
info_plist: plist::Value,
|
||||
) -> Result<(), IdeviceError> {
|
||||
let image_type = image_type.into();
|
||||
|
||||
let mut req = plist::Dictionary::new();
|
||||
req.insert("Command".into(), "MountImage".into());
|
||||
req.insert("ImageType".into(), image_type.into());
|
||||
req.insert("ImageSignature".into(), plist::Value::Data(signature));
|
||||
req.insert("ImageTrustCache".into(), plist::Value::Data(trust_cache));
|
||||
req.insert("ImageInfoPlist".into(), info_plist);
|
||||
self.idevice.send_plist(plist::Value::Dictionary(req))?;
|
||||
|
||||
let res = self.idevice.read_plist()?;
|
||||
|
||||
match res.get("Status") {
|
||||
Some(plist::Value::String(s)) => {
|
||||
if s.as_str() != "Success" {
|
||||
log::error!("Image send failure: {s:?}");
|
||||
return Err(IdeviceError::UnexpectedResponse);
|
||||
}
|
||||
}
|
||||
_ => return Err(IdeviceError::UnexpectedResponse),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Queries the personalization manifest from the device.
|
||||
/// On failure, the socket must be closed and reestablished.
|
||||
pub fn query_personalization_manifest(
|
||||
&mut self,
|
||||
image_type: impl Into<String>,
|
||||
signature: Vec<u8>,
|
||||
) -> Result<Vec<u8>, IdeviceError> {
|
||||
let image_type = image_type.into();
|
||||
|
||||
let mut req = plist::Dictionary::new();
|
||||
req.insert("Command".into(), "QueryPersonalizationManifest".into());
|
||||
req.insert("PersonalizedImageType".into(), image_type.clone().into());
|
||||
req.insert("ImageType".into(), image_type.into());
|
||||
req.insert("ImageSignature".into(), plist::Value::Data(signature));
|
||||
self.idevice.send_plist(plist::Value::Dictionary(req))?;
|
||||
|
||||
let mut res = self.idevice.read_plist()?;
|
||||
match res.remove("ImageSignature") {
|
||||
Some(plist::Value::Data(i)) => Ok(i),
|
||||
_ => Err(IdeviceError::NotFound),
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user