mirror of
https://github.com/jkcoxson/idevice.git
synced 2026-03-02 06:26:15 +01:00
Move service-specific code to different modules
This commit is contained in:
46
src/heartbeat.rs
Normal file
46
src/heartbeat.rs
Normal 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(())
|
||||||
|
}
|
||||||
|
}
|
||||||
134
src/lib.rs
134
src/lib.rs
@@ -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
146
src/lockdownd.rs
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user