Add basic image mounter support

This commit is contained in:
Jackson Coxson
2025-01-10 15:05:51 -07:00
parent c35560a0c4
commit 629dd2b538
6 changed files with 689 additions and 2 deletions

View File

@@ -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
View 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),
}
}
}