Move service-specific code to different modules

This commit is contained in:
Jackson Coxson
2025-01-08 19:12:38 -07:00
parent 3536281a74
commit f055b933da
3 changed files with 202 additions and 124 deletions

46
src/heartbeat.rs Normal file
View File

@@ -0,0 +1,46 @@
// Jackson Coxson
// Abstractions for the heartbeat service on iOS
use crate::{Idevice, IdeviceError};
pub struct HeartbeatClient {
pub idevice: Idevice,
}
impl HeartbeatClient {
pub fn new(idevice: Idevice) -> Self {
Self { idevice }
}
pub fn get_marco(&mut self) -> Result<u64, IdeviceError> {
let rec = self.idevice.read_plist()?;
match rec.get("Interval") {
Some(plist::Value::Integer(interval)) => {
if let Some(interval) = interval.as_unsigned() {
Ok(interval)
} else {
Err(IdeviceError::UnexpectedResponse)
}
}
_ => match rec.get("Command") {
Some(plist::Value::String(command)) => {
if command.as_str() == "SleepyTime" {
Err(IdeviceError::HeartbeatSleepyTime)
} else {
Err(IdeviceError::UnexpectedResponse)
}
}
_ => Err(IdeviceError::UnexpectedResponse),
},
}
}
pub fn send_polo(&mut self) -> Result<(), IdeviceError> {
let mut req = plist::Dictionary::new();
req.insert("Command".into(), "Polo".into());
self.idevice
.send_plist(plist::Value::Dictionary(req.clone()))
.unwrap();
Ok(())
}
}

View File

@@ -1,38 +1,27 @@
// Jackson Coxson // Jackson Coxson
const LOCKDOWND_PORT: u16 = 62078; pub mod heartbeat;
pub mod lockdownd;
mod pairing_file; mod pairing_file;
use log::{debug, error}; use log::{debug, error};
use openssl::ssl::{SslConnector, SslMethod, SslVerifyMode}; use openssl::ssl::{SslConnector, SslMethod, SslVerifyMode};
use serde::{Deserialize, Serialize};
use std::io::{self, BufWriter, Read, Write}; use std::io::{self, BufWriter, Read, Write};
use thiserror::Error; use thiserror::Error;
trait ReadWrite: Read + Write + std::fmt::Debug {} trait ReadWrite: Read + Write + std::fmt::Debug {}
impl<T: Read + Write + std::fmt::Debug> ReadWrite for T {} impl<T: Read + Write + std::fmt::Debug> ReadWrite for T {}
pub struct LockdowndClient { pub struct Idevice {
socket: Option<Box<dyn ReadWrite>>, // in a box for now to use the ReadWrite trait for further uses socket: Option<Box<dyn ReadWrite>>, // in a box for now to use the ReadWrite trait for further uses
label: String, label: String,
} }
#[derive(Serialize, Deserialize)] impl Idevice {
#[serde(rename_all = "PascalCase")]
struct LockdowndRequest {
label: String,
key: Option<String>,
request: String,
}
impl LockdowndClient {
pub fn get_type(&mut self) -> Result<String, IdeviceError> { pub fn get_type(&mut self) -> Result<String, IdeviceError> {
let req = LockdowndRequest { let mut req = plist::Dictionary::new();
label: self.label.clone(), req.insert("Label".into(), self.label.clone().into());
key: None, req.insert("Request".into(), "QueryType".into());
request: "QueryType".to_string(),
};
let message = plist::to_value(&req)?; let message = plist::to_value(&req)?;
self.send_plist(message)?; self.send_plist(message)?;
let message: plist::Dictionary = self.read_plist()?; let message: plist::Dictionary = self.read_plist()?;
@@ -42,36 +31,6 @@ impl LockdowndClient {
} }
} }
pub fn get_value(&mut self, value: impl Into<String>) -> Result<String, IdeviceError> {
let req = LockdowndRequest {
label: self.label.clone(),
key: Some(value.into()),
request: "GetValue".to_string(),
};
let message = plist::to_value(&req)?;
self.send_plist(message)?;
let message: plist::Dictionary = self.read_plist()?;
match message.get("Value") {
Some(m) => Ok(plist::from_value(m)?),
None => Err(IdeviceError::UnexpectedResponse),
}
}
pub fn get_all_values(&mut self) -> Result<plist::Dictionary, IdeviceError> {
let req = LockdowndRequest {
label: self.label.clone(),
key: None,
request: "GetValue".to_string(),
};
let message = plist::to_value(&req)?;
self.send_plist(message)?;
let message: plist::Dictionary = self.read_plist()?;
match message.get("Value") {
Some(m) => Ok(plist::from_value(m)?),
None => Err(IdeviceError::UnexpectedResponse),
}
}
/// Sends a plist to the socket /// Sends a plist to the socket
fn send_plist(&mut self, message: plist::Value) -> Result<(), IdeviceError> { fn send_plist(&mut self, message: plist::Value) -> Result<(), IdeviceError> {
if let Some(socket) = &mut self.socket { if let Some(socket) = &mut self.socket {
@@ -115,48 +74,11 @@ impl LockdowndClient {
} }
} }
/// Starts a TLS session with the client /// Wraps current connection in TLS
pub fn start_session( pub fn start_session(
&mut self, &mut self,
pairing_file: pairing_file::PairingFile, pairing_file: pairing_file::PairingFile,
) -> Result<(), IdeviceError> { ) -> Result<(), IdeviceError> {
if self.socket.is_none() {
return Err(IdeviceError::NoEstablishedConnection);
}
let mut request = plist::Dictionary::new();
request.insert(
"Label".to_string(),
plist::Value::String(self.label.clone()),
);
request.insert(
"Request".to_string(),
plist::Value::String("StartSession".to_string()),
);
request.insert(
"HostID".to_string(),
plist::Value::String(pairing_file.host_id.clone()),
);
request.insert(
"SystemBUID".to_string(),
plist::Value::String(pairing_file.system_buid.clone()),
);
self.send_plist(plist::Value::Dictionary(request))?;
let response = self.read_plist()?;
match response.get("EnableSessionSSL") {
Some(plist::Value::Boolean(enable)) => {
if !enable {
return Err(IdeviceError::UnexpectedResponse);
}
}
_ => {
return Err(IdeviceError::UnexpectedResponse);
}
}
let mut connector = SslConnector::builder(SslMethod::tls()).unwrap(); let mut connector = SslConnector::builder(SslMethod::tls()).unwrap();
connector connector
.set_certificate(&pairing_file.host_certificate) .set_certificate(&pairing_file.host_certificate)
@@ -173,44 +95,6 @@ impl LockdowndClient {
Ok(()) Ok(())
} }
/// Asks lockdownd to pretty please start a service for us
/// # Arguments
/// `identifier` - The identifier for the service you want to start
/// # Returns
/// The port number and whether to enable SSL on success, `IdeviceError` on failure
pub fn start_service(
&mut self,
identifier: impl Into<String>,
) -> Result<(u16, bool), IdeviceError> {
let identifier = identifier.into();
let mut req = plist::Dictionary::new();
req.insert("Request".into(), "StartService".into());
req.insert("Service".into(), identifier.into());
self.send_plist(plist::Value::Dictionary(req))?;
let response = self.read_plist()?;
println!("{response:?}");
match response.get("EnableServiceSSL") {
Some(plist::Value::Boolean(ssl)) => match response.get("Port") {
Some(plist::Value::Integer(port)) => {
if let Some(port) = port.as_unsigned() {
Ok((port as u16, *ssl))
} else {
error!("Port isn't an unsiged integer!");
Err(IdeviceError::UnexpectedResponse)
}
}
_ => {
error!("Response didn't contain an integer port");
Err(IdeviceError::UnexpectedResponse)
}
},
_ => {
error!("Response didn't contain EnableServiceSSL bool!");
Err(IdeviceError::UnexpectedResponse)
}
}
}
} }
#[derive(Error, Debug)] #[derive(Error, Debug)]
@@ -227,6 +111,8 @@ pub enum IdeviceError {
GetProhibited, GetProhibited,
#[error("no established connection")] #[error("no established connection")]
NoEstablishedConnection, NoEstablishedConnection,
#[error("device went to sleep")]
HeartbeatSleepyTime,
#[error("unknown error `{0}` returned from device")] #[error("unknown error `{0}` returned from device")]
UnknownErrorType(String), UnknownErrorType(String),
} }

146
src/lockdownd.rs Normal file
View File

@@ -0,0 +1,146 @@
// Jackson Coxson
// Abstractions for lockdownd
pub const LOCKDOWND_PORT: u16 = 62078;
use log::error;
use serde::{Deserialize, Serialize};
use crate::{pairing_file, Idevice, IdeviceError};
pub struct LockdowndClient {
pub idevice: crate::Idevice,
}
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
struct LockdowndRequest {
label: String,
key: Option<String>,
request: String,
}
impl LockdowndClient {
pub fn new(idevice: Idevice) -> Self {
Self { idevice }
}
pub fn get_value(&mut self, value: impl Into<String>) -> Result<String, IdeviceError> {
let req = LockdowndRequest {
label: self.idevice.label.clone(),
key: Some(value.into()),
request: "GetValue".to_string(),
};
let message = plist::to_value(&req)?;
self.idevice.send_plist(message)?;
let message: plist::Dictionary = self.idevice.read_plist()?;
match message.get("Value") {
Some(m) => Ok(plist::from_value(m)?),
None => Err(IdeviceError::UnexpectedResponse),
}
}
pub fn get_all_values(&mut self) -> Result<plist::Dictionary, IdeviceError> {
let req = LockdowndRequest {
label: self.idevice.label.clone(),
key: None,
request: "GetValue".to_string(),
};
let message = plist::to_value(&req)?;
self.idevice.send_plist(message)?;
let message: plist::Dictionary = self.idevice.read_plist()?;
match message.get("Value") {
Some(m) => Ok(plist::from_value(m)?),
None => Err(IdeviceError::UnexpectedResponse),
}
}
/// Starts a TLS session with the client
pub fn start_session(
&mut self,
pairing_file: pairing_file::PairingFile,
) -> Result<(), IdeviceError> {
if self.idevice.socket.is_none() {
return Err(IdeviceError::NoEstablishedConnection);
}
let mut request = plist::Dictionary::new();
request.insert(
"Label".to_string(),
plist::Value::String(self.idevice.label.clone()),
);
request.insert(
"Request".to_string(),
plist::Value::String("StartSession".to_string()),
);
request.insert(
"HostID".to_string(),
plist::Value::String(pairing_file.host_id.clone()),
);
request.insert(
"SystemBUID".to_string(),
plist::Value::String(pairing_file.system_buid.clone()),
);
self.idevice.send_plist(plist::Value::Dictionary(request))?;
let response = self.idevice.read_plist()?;
match response.get("EnableSessionSSL") {
Some(plist::Value::Boolean(enable)) => {
if !enable {
return Err(IdeviceError::UnexpectedResponse);
}
}
_ => {
return Err(IdeviceError::UnexpectedResponse);
}
}
self.idevice.start_session(pairing_file)?;
Ok(())
}
/// Asks lockdownd to pretty please start a service for us
/// # Arguments
/// `identifier` - The identifier for the service you want to start
/// # Returns
/// The port number and whether to enable SSL on success, `IdeviceError` on failure
pub fn start_service(
&mut self,
identifier: impl Into<String>,
) -> Result<(u16, bool), IdeviceError> {
let identifier = identifier.into();
let mut req = plist::Dictionary::new();
req.insert("Request".into(), "StartService".into());
req.insert("Service".into(), identifier.into());
self.idevice.send_plist(plist::Value::Dictionary(req))?;
let response = self.idevice.read_plist()?;
println!("{response:?}");
match response.get("EnableServiceSSL") {
Some(plist::Value::Boolean(ssl)) => match response.get("Port") {
Some(plist::Value::Integer(port)) => {
if let Some(port) = port.as_unsigned() {
Ok((port as u16, *ssl))
} else {
error!("Port isn't an unsiged integer!");
Err(IdeviceError::UnexpectedResponse)
}
}
_ => {
error!("Response didn't contain an integer port");
Err(IdeviceError::UnexpectedResponse)
}
},
_ => {
error!("Response didn't contain EnableServiceSSL bool!");
Err(IdeviceError::UnexpectedResponse)
}
}
}
}
impl From<Idevice> for LockdowndClient {
fn from(value: Idevice) -> Self {
Self::new(value)
}
}