Change to tokio for io

This commit is contained in:
Jackson Coxson
2025-01-14 21:13:32 -07:00
parent d9aa9c1798
commit 45811dc977
12 changed files with 302 additions and 93 deletions

185
Cargo.lock generated
View File

@@ -2,6 +2,15 @@
# It is not intended for manual editing. # It is not intended for manual editing.
version = 4 version = 4
[[package]]
name = "addr2line"
version = "0.24.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1"
dependencies = [
"gimli",
]
[[package]] [[package]]
name = "adler2" name = "adler2"
version = "2.0.0" version = "2.0.0"
@@ -66,6 +75,27 @@ dependencies = [
"windows-sys 0.59.0", "windows-sys 0.59.0",
] ]
[[package]]
name = "autocfg"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
[[package]]
name = "backtrace"
version = "0.3.74"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a"
dependencies = [
"addr2line",
"cfg-if",
"libc",
"miniz_oxide",
"object",
"rustc-demangle",
"windows-targets",
]
[[package]] [[package]]
name = "base64" name = "base64"
version = "0.22.1" version = "0.22.1"
@@ -87,6 +117,12 @@ dependencies = [
"generic-array", "generic-array",
] ]
[[package]]
name = "bytes"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b"
[[package]] [[package]]
name = "cc" name = "cc"
version = "1.2.5" version = "1.2.5"
@@ -250,6 +286,12 @@ dependencies = [
"wasi", "wasi",
] ]
[[package]]
name = "gimli"
version = "0.31.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
[[package]] [[package]]
name = "hashbrown" name = "hashbrown"
version = "0.15.2" version = "0.15.2"
@@ -382,7 +424,7 @@ dependencies = [
[[package]] [[package]]
name = "idevice" name = "idevice"
version = "0.1.5" version = "0.1.6"
dependencies = [ dependencies = [
"env_logger", "env_logger",
"log", "log",
@@ -391,6 +433,8 @@ dependencies = [
"serde", "serde",
"sha2", "sha2",
"thiserror", "thiserror",
"tokio",
"tokio-openssl",
"ureq", "ureq",
] ]
@@ -449,6 +493,16 @@ version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104"
[[package]]
name = "lock_api"
version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
dependencies = [
"autocfg",
"scopeguard",
]
[[package]] [[package]]
name = "log" name = "log"
version = "0.4.22" version = "0.4.22"
@@ -470,12 +524,32 @@ dependencies = [
"adler2", "adler2",
] ]
[[package]]
name = "mio"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd"
dependencies = [
"libc",
"wasi",
"windows-sys 0.52.0",
]
[[package]] [[package]]
name = "num-conv" name = "num-conv"
version = "0.1.0" version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
[[package]]
name = "object"
version = "0.36.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87"
dependencies = [
"memchr",
]
[[package]] [[package]]
name = "once_cell" name = "once_cell"
version = "1.20.2" version = "1.20.2"
@@ -520,12 +594,41 @@ dependencies = [
"vcpkg", "vcpkg",
] ]
[[package]]
name = "parking_lot"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27"
dependencies = [
"lock_api",
"parking_lot_core",
]
[[package]]
name = "parking_lot_core"
version = "0.9.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
dependencies = [
"cfg-if",
"libc",
"redox_syscall",
"smallvec",
"windows-targets",
]
[[package]] [[package]]
name = "percent-encoding" name = "percent-encoding"
version = "2.3.1" version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
[[package]]
name = "pin-project-lite"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
[[package]] [[package]]
name = "pkg-config" name = "pkg-config"
version = "0.3.31" version = "0.3.31"
@@ -578,6 +681,15 @@ dependencies = [
"proc-macro2", "proc-macro2",
] ]
[[package]]
name = "redox_syscall"
version = "0.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834"
dependencies = [
"bitflags",
]
[[package]] [[package]]
name = "regex" name = "regex"
version = "1.11.1" version = "1.11.1"
@@ -622,6 +734,12 @@ dependencies = [
"windows-sys 0.52.0", "windows-sys 0.52.0",
] ]
[[package]]
name = "rustc-demangle"
version = "0.1.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
[[package]] [[package]]
name = "rustls" name = "rustls"
version = "0.23.21" version = "0.23.21"
@@ -654,6 +772,12 @@ dependencies = [
"untrusted", "untrusted",
] ]
[[package]]
name = "scopeguard"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.216" version = "1.0.216"
@@ -691,12 +815,31 @@ version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "signal-hook-registry"
version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1"
dependencies = [
"libc",
]
[[package]] [[package]]
name = "smallvec" name = "smallvec"
version = "1.13.2" version = "1.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
[[package]]
name = "socket2"
version = "0.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8"
dependencies = [
"libc",
"windows-sys 0.52.0",
]
[[package]] [[package]]
name = "spin" name = "spin"
version = "0.9.8" version = "0.9.8"
@@ -798,6 +941,46 @@ dependencies = [
"zerovec", "zerovec",
] ]
[[package]]
name = "tokio"
version = "1.43.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d61fa4ffa3de412bfea335c6ecff681de2b609ba3c77ef3e00e521813a9ed9e"
dependencies = [
"backtrace",
"bytes",
"libc",
"mio",
"parking_lot",
"pin-project-lite",
"signal-hook-registry",
"socket2",
"tokio-macros",
"windows-sys 0.52.0",
]
[[package]]
name = "tokio-macros"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "tokio-openssl"
version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59df6849caa43bb7567f9a36f863c447d95a11d5903c9cc334ba32576a27eadd"
dependencies = [
"openssl",
"openssl-sys",
"tokio",
]
[[package]] [[package]]
name = "typenum" name = "typenum"
version = "1.17.0" version = "1.17.0"

View File

@@ -2,7 +2,7 @@
name = "idevice" name = "idevice"
description = "A Rust library to interact with services on iOS devices." description = "A Rust library to interact with services on iOS devices."
authors = ["Jackson Coxson"] authors = ["Jackson Coxson"]
version = "0.1.5" version = "0.1.6"
edition = "2021" edition = "2021"
license = "MIT" license = "MIT"
documentation = "https://docs.rs/idevice" documentation = "https://docs.rs/idevice"
@@ -12,6 +12,7 @@ keywords = ["lockdownd", "ios"]
[[bin]] [[bin]]
name = "ideviceinfo" name = "ideviceinfo"
path = "src/tools/ideviceinfo.rs" path = "src/tools/ideviceinfo.rs"
required-features = ["bin"]
[[bin]] [[bin]]
name = "heartbeat_client" name = "heartbeat_client"
@@ -27,6 +28,8 @@ path = "src/tools/mounter.rs"
required-features = ["sha2", "ureq"] required-features = ["sha2", "ureq"]
[dependencies] [dependencies]
tokio = { version = "1.43", features = ["io-util"] }
tokio-openssl = { version = "0.6" }
plist = { version = "1.7" } plist = { version = "1.7" }
serde = { version = "1", features = ["derive"] } serde = { version = "1", features = ["derive"] }
thiserror = { version = "2" } thiserror = { version = "2" }
@@ -39,4 +42,4 @@ sha2 = { version = "0.10", optional = true }
ureq = { version = "2.12", optional = true } ureq = { version = "2.12", optional = true }
[features] [features]
std-tcp = [] bin = ["tokio/full", "sha2", "ureq"]

View File

@@ -8,6 +8,10 @@ on an iOS device that a Mac normally would.
## State ## State
**IMPORTANT**: Breaking changes will happen at each point release until 0.2.0.
The library is still in the development and brainstorming stage.
Pin your `Cargo.toml` to a specific version to avoid breakage.
This library is in development and research stage. This library is in development and research stage.
Releases are being published to crates.io for use in other projects, Releases are being published to crates.io for use in other projects,
but the API and feature-set are far from final or even planned. but the API and feature-set are far from final or even planned.

View File

@@ -12,8 +12,8 @@ impl HeartbeatClient {
Self { idevice } Self { idevice }
} }
pub fn get_marco(&mut self) -> Result<u64, IdeviceError> { pub async fn get_marco(&mut self) -> Result<u64, IdeviceError> {
let rec = self.idevice.read_plist()?; let rec = self.idevice.read_plist().await?;
match rec.get("Interval") { match rec.get("Interval") {
Some(plist::Value::Integer(interval)) => { Some(plist::Value::Integer(interval)) => {
if let Some(interval) = interval.as_unsigned() { if let Some(interval) = interval.as_unsigned() {
@@ -35,12 +35,12 @@ impl HeartbeatClient {
} }
} }
pub fn send_polo(&mut self) -> Result<(), IdeviceError> { pub async fn send_polo(&mut self) -> Result<(), IdeviceError> {
let mut req = plist::Dictionary::new(); let mut req = plist::Dictionary::new();
req.insert("Command".into(), "Polo".into()); req.insert("Command".into(), "Polo".into());
self.idevice self.idevice
.send_plist(plist::Value::Dictionary(req.clone())) .send_plist(plist::Value::Dictionary(req.clone()))
.unwrap(); .await?;
Ok(()) Ok(())
} }
} }

View File

@@ -18,7 +18,7 @@ impl InstallationProxyClient {
/// # Arguments /// # Arguments
/// `application_type` - The application type to filter by /// `application_type` - The application type to filter by
/// `bundle_identifiers` - The identifiers to filter by /// `bundle_identifiers` - The identifiers to filter by
pub fn get_apps( pub async fn get_apps(
&mut self, &mut self,
application_type: Option<String>, application_type: Option<String>,
bundle_identifiers: Option<Vec<String>>, bundle_identifiers: Option<Vec<String>>,
@@ -37,9 +37,11 @@ impl InstallationProxyClient {
let mut req = plist::Dictionary::new(); let mut req = plist::Dictionary::new();
req.insert("Command".into(), "Lookup".into()); req.insert("Command".into(), "Lookup".into());
// req.insert("ClientOptions".into(), plist::Value::Dictionary(options)); // req.insert("ClientOptions".into(), plist::Value::Dictionary(options));
self.idevice.send_plist(plist::Value::Dictionary(req))?; self.idevice
.send_plist(plist::Value::Dictionary(req))
.await?;
let mut res = self.idevice.read_plist()?; let mut res = self.idevice.read_plist().await?;
match res.remove("LookupResult") { match res.remove("LookupResult") {
Some(plist::Value::Dictionary(res)) => { Some(plist::Value::Dictionary(res)) => {
Ok(res.into_iter().collect::<HashMap<String, plist::Value>>()) Ok(res.into_iter().collect::<HashMap<String, plist::Value>>())

View File

@@ -8,11 +8,12 @@ pub mod pairing_file;
use log::{debug, error}; use log::{debug, error};
use openssl::ssl::{SslConnector, SslMethod, SslVerifyMode}; use openssl::ssl::{SslConnector, SslMethod, SslVerifyMode};
use std::io::{self, BufWriter, Read, Write}; use std::io::{self, BufWriter};
use thiserror::Error; use thiserror::Error;
use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
pub trait ReadWrite: Read + Write + Send + Sync + std::fmt::Debug {} pub trait ReadWrite: AsyncRead + AsyncWrite + Unpin + Send + Sync + std::fmt::Debug {}
impl<T: Read + Write + Send + Sync + std::fmt::Debug> ReadWrite for T {} impl<T: AsyncRead + AsyncWrite + Unpin + Send + Sync + std::fmt::Debug> ReadWrite for T {}
pub struct Idevice { 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
@@ -27,26 +28,13 @@ impl Idevice {
} }
} }
#[cfg(feature = "std-tcp")] pub async fn get_type(&mut self) -> Result<String, IdeviceError> {
pub fn connect_tcp(
addr: std::net::SocketAddr,
label: impl Into<String>,
) -> Result<Self, std::io::Error> {
let socket = std::net::TcpStream::connect(addr)?;
let label = label.into();
Ok(Self {
socket: Some(Box::new(socket)),
label,
})
}
pub fn get_type(&mut self) -> Result<String, IdeviceError> {
let mut req = plist::Dictionary::new(); let mut req = plist::Dictionary::new();
req.insert("Label".into(), self.label.clone().into()); req.insert("Label".into(), self.label.clone().into());
req.insert("Request".into(), "QueryType".into()); req.insert("Request".into(), "QueryType".into());
let message = plist::to_value(&req)?; let message = plist::to_value(&req)?;
self.send_plist(message)?; self.send_plist(message).await?;
let message: plist::Dictionary = self.read_plist()?; let message: plist::Dictionary = self.read_plist().await?;
match message.get("Type") { match message.get("Type") {
Some(m) => Ok(plist::from_value(m)?), Some(m) => Ok(plist::from_value(m)?),
None => Err(IdeviceError::UnexpectedResponse), None => Err(IdeviceError::UnexpectedResponse),
@@ -54,7 +42,7 @@ impl Idevice {
} }
/// Sends a plist to the socket /// Sends a plist to the socket
fn send_plist(&mut self, message: plist::Value) -> Result<(), IdeviceError> { async fn send_plist(&mut self, message: plist::Value) -> Result<(), IdeviceError> {
if let Some(socket) = &mut self.socket { if let Some(socket) = &mut self.socket {
let buf = Vec::new(); let buf = Vec::new();
let mut writer = BufWriter::new(buf); let mut writer = BufWriter::new(buf);
@@ -62,8 +50,8 @@ impl Idevice {
let message = writer.into_inner().unwrap(); let message = writer.into_inner().unwrap();
let message = String::from_utf8(message)?; let message = String::from_utf8(message)?;
let len = message.len() as u32; let len = message.len() as u32;
socket.write_all(&len.to_be_bytes())?; socket.write_all(&len.to_be_bytes()).await?;
socket.write_all(message.as_bytes())?; socket.write_all(message.as_bytes()).await?;
Ok(()) Ok(())
} else { } else {
Err(IdeviceError::NoEstablishedConnection) Err(IdeviceError::NoEstablishedConnection)
@@ -71,23 +59,23 @@ impl Idevice {
} }
/// Sends raw bytes to the socket /// Sends raw bytes to the socket
fn send_raw(&mut self, message: &[u8]) -> Result<(), IdeviceError> { async fn send_raw(&mut self, message: &[u8]) -> Result<(), IdeviceError> {
if let Some(socket) = &mut self.socket { if let Some(socket) = &mut self.socket {
Ok(socket.write_all(message)?) Ok(socket.write_all(message).await?)
} else { } else {
Err(IdeviceError::NoEstablishedConnection) Err(IdeviceError::NoEstablishedConnection)
} }
} }
/// Read a plist from the socket /// Read a plist from the socket
fn read_plist(&mut self) -> Result<plist::Dictionary, IdeviceError> { async fn read_plist(&mut self) -> Result<plist::Dictionary, IdeviceError> {
if let Some(socket) = &mut self.socket { if let Some(socket) = &mut self.socket {
debug!("Reading response size"); debug!("Reading response size");
let mut buf = [0u8; 4]; let mut buf = [0u8; 4];
socket.read_exact(&mut buf)?; socket.read_exact(&mut buf).await?;
let len = u32::from_be_bytes(buf); let len = u32::from_be_bytes(buf);
let mut buf = vec![0; len as usize]; let mut buf = vec![0; len as usize];
socket.read_exact(&mut buf)?; socket.read_exact(&mut buf).await?;
let res: plist::Dictionary = plist::from_bytes(&buf)?; let res: plist::Dictionary = plist::from_bytes(&buf)?;
debug!("Received plist: {res:#?}"); debug!("Received plist: {res:#?}");
@@ -106,11 +94,19 @@ impl Idevice {
} }
/// Wraps current connection in TLS /// Wraps current connection in TLS
pub fn start_session( pub async fn start_session(
&mut self, &mut self,
pairing_file: &pairing_file::PairingFile, pairing_file: &pairing_file::PairingFile,
) -> Result<(), IdeviceError> { ) -> Result<(), IdeviceError> {
let mut connector = SslConnector::builder(SslMethod::tls()).unwrap(); let connector = SslConnector::builder(SslMethod::tls()).unwrap();
let mut connector = connector
.build()
.configure()
.unwrap()
.into_ssl("ur mom")
.unwrap();
connector connector
.set_certificate(&pairing_file.host_certificate) .set_certificate(&pairing_file.host_certificate)
.unwrap(); .unwrap();
@@ -119,9 +115,10 @@ impl Idevice {
.unwrap(); .unwrap();
connector.set_verify(SslVerifyMode::empty()); connector.set_verify(SslVerifyMode::empty());
let connector = connector.build();
let socket = self.socket.take().unwrap(); let socket = self.socket.take().unwrap();
let ssl_stream = connector.connect("ur mom", socket).unwrap();
let mut ssl_stream = tokio_openssl::SslStream::new(connector, socket).unwrap();
std::pin::Pin::new(&mut ssl_stream).connect().await.unwrap();
self.socket = Some(Box::new(ssl_stream)); self.socket = Some(Box::new(ssl_stream));
Ok(()) Ok(())

View File

@@ -24,30 +24,30 @@ impl LockdowndClient {
pub fn new(idevice: Idevice) -> Self { pub fn new(idevice: Idevice) -> Self {
Self { idevice } Self { idevice }
} }
pub fn get_value(&mut self, value: impl Into<String>) -> Result<String, IdeviceError> { pub async fn get_value(&mut self, value: impl Into<String>) -> Result<String, IdeviceError> {
let req = LockdowndRequest { let req = LockdowndRequest {
label: self.idevice.label.clone(), label: self.idevice.label.clone(),
key: Some(value.into()), key: Some(value.into()),
request: "GetValue".to_string(), request: "GetValue".to_string(),
}; };
let message = plist::to_value(&req)?; let message = plist::to_value(&req)?;
self.idevice.send_plist(message)?; self.idevice.send_plist(message).await?;
let message: plist::Dictionary = self.idevice.read_plist()?; let message: plist::Dictionary = self.idevice.read_plist().await?;
match message.get("Value") { match message.get("Value") {
Some(m) => Ok(plist::from_value(m)?), Some(m) => Ok(plist::from_value(m)?),
None => Err(IdeviceError::UnexpectedResponse), None => Err(IdeviceError::UnexpectedResponse),
} }
} }
pub fn get_all_values(&mut self) -> Result<plist::Dictionary, IdeviceError> { pub async fn get_all_values(&mut self) -> Result<plist::Dictionary, IdeviceError> {
let req = LockdowndRequest { let req = LockdowndRequest {
label: self.idevice.label.clone(), label: self.idevice.label.clone(),
key: None, key: None,
request: "GetValue".to_string(), request: "GetValue".to_string(),
}; };
let message = plist::to_value(&req)?; let message = plist::to_value(&req)?;
self.idevice.send_plist(message)?; self.idevice.send_plist(message).await?;
let message: plist::Dictionary = self.idevice.read_plist()?; let message: plist::Dictionary = self.idevice.read_plist().await?;
match message.get("Value") { match message.get("Value") {
Some(m) => Ok(plist::from_value(m)?), Some(m) => Ok(plist::from_value(m)?),
None => Err(IdeviceError::UnexpectedResponse), None => Err(IdeviceError::UnexpectedResponse),
@@ -55,7 +55,7 @@ impl LockdowndClient {
} }
/// Starts a TLS session with the client /// Starts a TLS session with the client
pub fn start_session( pub async fn start_session(
&mut self, &mut self,
pairing_file: &pairing_file::PairingFile, pairing_file: &pairing_file::PairingFile,
) -> Result<(), IdeviceError> { ) -> Result<(), IdeviceError> {
@@ -82,9 +82,11 @@ impl LockdowndClient {
plist::Value::String(pairing_file.system_buid.clone()), plist::Value::String(pairing_file.system_buid.clone()),
); );
self.idevice.send_plist(plist::Value::Dictionary(request))?; self.idevice
.send_plist(plist::Value::Dictionary(request))
.await?;
let response = self.idevice.read_plist()?; let response = self.idevice.read_plist().await?;
match response.get("EnableSessionSSL") { match response.get("EnableSessionSSL") {
Some(plist::Value::Boolean(enable)) => { Some(plist::Value::Boolean(enable)) => {
if !enable { if !enable {
@@ -96,7 +98,7 @@ impl LockdowndClient {
} }
} }
self.idevice.start_session(pairing_file)?; self.idevice.start_session(pairing_file).await?;
Ok(()) Ok(())
} }
@@ -105,7 +107,7 @@ impl LockdowndClient {
/// `identifier` - The identifier for the service you want to start /// `identifier` - The identifier for the service you want to start
/// # Returns /// # Returns
/// The port number and whether to enable SSL on success, `IdeviceError` on failure /// The port number and whether to enable SSL on success, `IdeviceError` on failure
pub fn start_service( pub async fn start_service(
&mut self, &mut self,
identifier: impl Into<String>, identifier: impl Into<String>,
) -> Result<(u16, bool), IdeviceError> { ) -> Result<(u16, bool), IdeviceError> {
@@ -113,8 +115,10 @@ impl LockdowndClient {
let mut req = plist::Dictionary::new(); let mut req = plist::Dictionary::new();
req.insert("Request".into(), "StartService".into()); req.insert("Request".into(), "StartService".into());
req.insert("Service".into(), identifier.into()); req.insert("Service".into(), identifier.into());
self.idevice.send_plist(plist::Value::Dictionary(req))?; self.idevice
let response = self.idevice.read_plist()?; .send_plist(plist::Value::Dictionary(req))
.await?;
let response = self.idevice.read_plist().await?;
match response.get("EnableServiceSSL") { match response.get("EnableServiceSSL") {
Some(plist::Value::Boolean(ssl)) => match response.get("Port") { Some(plist::Value::Boolean(ssl)) => match response.get("Port") {
Some(plist::Value::Integer(port)) => { Some(plist::Value::Integer(port)) => {

View File

@@ -11,11 +11,13 @@ impl ImageMounter {
Self { idevice } Self { idevice }
} }
pub fn copy_devices(&mut self) -> Result<Vec<plist::Value>, IdeviceError> { pub async fn copy_devices(&mut self) -> Result<Vec<plist::Value>, IdeviceError> {
let mut req = plist::Dictionary::new(); let mut req = plist::Dictionary::new();
req.insert("Command".into(), "CopyDevices".into()); req.insert("Command".into(), "CopyDevices".into());
self.idevice.send_plist(plist::Value::Dictionary(req))?; self.idevice
let mut res = self.idevice.read_plist()?; .send_plist(plist::Value::Dictionary(req))
.await?;
let mut res = self.idevice.read_plist().await?;
match res.remove("EntryList") { match res.remove("EntryList") {
Some(plist::Value::Array(i)) => Ok(i), Some(plist::Value::Array(i)) => Ok(i),
@@ -23,7 +25,7 @@ impl ImageMounter {
} }
} }
pub fn upload_image( pub async fn upload_image(
&mut self, &mut self,
image_type: impl Into<String>, image_type: impl Into<String>,
image: &[u8], image: &[u8],
@@ -36,9 +38,11 @@ impl ImageMounter {
req.insert("ImageType".into(), image_type.into()); req.insert("ImageType".into(), image_type.into());
req.insert("ImageSize".into(), (image.len() as u64).into()); req.insert("ImageSize".into(), (image.len() as u64).into());
req.insert("ImageSignature".into(), plist::Value::Data(signature)); req.insert("ImageSignature".into(), plist::Value::Data(signature));
self.idevice.send_plist(plist::Value::Dictionary(req))?; self.idevice
.send_plist(plist::Value::Dictionary(req))
.await?;
let res = self.idevice.read_plist()?; let res = self.idevice.read_plist().await?;
match res.get("Status") { match res.get("Status") {
Some(plist::Value::String(s)) => { Some(plist::Value::String(s)) => {
if s.as_str() != "ReceiveBytesAck" { if s.as_str() != "ReceiveBytesAck" {
@@ -49,9 +53,9 @@ impl ImageMounter {
_ => return Err(IdeviceError::UnexpectedResponse), _ => return Err(IdeviceError::UnexpectedResponse),
} }
self.idevice.send_raw(image)?; self.idevice.send_raw(image).await?;
let res = self.idevice.read_plist()?; let res = self.idevice.read_plist().await?;
match res.get("Status") { match res.get("Status") {
Some(plist::Value::String(s)) => { Some(plist::Value::String(s)) => {
if s.as_str() != "Success" { if s.as_str() != "Success" {
@@ -65,7 +69,7 @@ impl ImageMounter {
Ok(()) Ok(())
} }
pub fn mount_image( pub async fn mount_image(
&mut self, &mut self,
image_type: impl Into<String>, image_type: impl Into<String>,
signature: Vec<u8>, signature: Vec<u8>,
@@ -80,9 +84,11 @@ impl ImageMounter {
req.insert("ImageSignature".into(), plist::Value::Data(signature)); req.insert("ImageSignature".into(), plist::Value::Data(signature));
req.insert("ImageTrustCache".into(), plist::Value::Data(trust_cache)); req.insert("ImageTrustCache".into(), plist::Value::Data(trust_cache));
req.insert("ImageInfoPlist".into(), info_plist); req.insert("ImageInfoPlist".into(), info_plist);
self.idevice.send_plist(plist::Value::Dictionary(req))?; self.idevice
.send_plist(plist::Value::Dictionary(req))
.await?;
let res = self.idevice.read_plist()?; let res = self.idevice.read_plist().await?;
match res.get("Status") { match res.get("Status") {
Some(plist::Value::String(s)) => { Some(plist::Value::String(s)) => {
@@ -99,7 +105,7 @@ impl ImageMounter {
/// Queries the personalization manifest from the device. /// Queries the personalization manifest from the device.
/// On failure, the socket must be closed and reestablished. /// On failure, the socket must be closed and reestablished.
pub fn query_personalization_manifest( pub async fn query_personalization_manifest(
&mut self, &mut self,
image_type: impl Into<String>, image_type: impl Into<String>,
signature: Vec<u8>, signature: Vec<u8>,
@@ -111,9 +117,11 @@ impl ImageMounter {
req.insert("PersonalizedImageType".into(), image_type.clone().into()); req.insert("PersonalizedImageType".into(), image_type.clone().into());
req.insert("ImageType".into(), image_type.into()); req.insert("ImageType".into(), image_type.into());
req.insert("ImageSignature".into(), plist::Value::Data(signature)); req.insert("ImageSignature".into(), plist::Value::Data(signature));
self.idevice.send_plist(plist::Value::Dictionary(req))?; self.idevice
.send_plist(plist::Value::Dictionary(req))
.await?;
let mut res = self.idevice.read_plist()?; let mut res = self.idevice.read_plist().await?;
match res.remove("ImageSignature") { match res.remove("ImageSignature") {
Some(plist::Value::Data(i)) => Ok(i), Some(plist::Value::Data(i)) => Ok(i),
_ => Err(IdeviceError::NotFound), _ => Err(IdeviceError::NotFound),

View File

@@ -13,7 +13,8 @@ use std::{
str::FromStr, str::FromStr,
}; };
fn main() { #[tokio::main]
async fn main() {
env_logger::init(); env_logger::init();
let mut host = None; let mut host = None;
let mut pairing_file = None; let mut pairing_file = None;
@@ -62,32 +63,33 @@ fn main() {
let ip = Ipv4Addr::from_str(host.unwrap().as_str()).unwrap(); let ip = Ipv4Addr::from_str(host.unwrap().as_str()).unwrap();
let socket = SocketAddrV4::new(ip, lockdownd::LOCKDOWND_PORT); let socket = SocketAddrV4::new(ip, lockdownd::LOCKDOWND_PORT);
let socket = std::net::TcpStream::connect(socket).unwrap(); let socket = tokio::net::TcpStream::connect(socket).await.unwrap();
let socket = Box::new(socket); let socket = Box::new(socket);
let idevice = Idevice::new(socket, "heartbeat_client"); let idevice = Idevice::new(socket, "heartbeat_client");
let p = PairingFile::read_from_file(pairing_file.as_ref().unwrap()).unwrap(); let p = PairingFile::read_from_file(pairing_file.as_ref().unwrap()).unwrap();
let mut lockdown_client = LockdowndClient { idevice }; let mut lockdown_client = LockdowndClient { idevice };
lockdown_client.start_session(&p).unwrap(); lockdown_client.start_session(&p).await.unwrap();
let (port, _) = lockdown_client let (port, _) = lockdown_client
.start_service("com.apple.mobile.heartbeat") .start_service("com.apple.mobile.heartbeat")
.await
.unwrap(); .unwrap();
let socket = SocketAddrV4::new(ip, port); let socket = SocketAddrV4::new(ip, port);
let socket = std::net::TcpStream::connect(socket).unwrap(); let socket = tokio::net::TcpStream::connect(socket).await.unwrap();
let socket = Box::new(socket); let socket = Box::new(socket);
let mut idevice = Idevice::new(socket, "heartbeat_client"); let mut idevice = Idevice::new(socket, "heartbeat_client");
let p = PairingFile::read_from_file(pairing_file.unwrap()).unwrap(); let p = PairingFile::read_from_file(pairing_file.unwrap()).unwrap();
idevice.start_session(&p).unwrap(); idevice.start_session(&p).await.unwrap();
let mut heartbeat_client = HeartbeatClient { idevice }; let mut heartbeat_client = HeartbeatClient { idevice };
loop { loop {
heartbeat_client.get_marco().unwrap(); heartbeat_client.get_marco().await.unwrap();
heartbeat_client.send_polo().unwrap(); heartbeat_client.send_polo().await.unwrap();
} }
} }

View File

@@ -12,7 +12,8 @@ use idevice::{
Idevice, Idevice,
}; };
fn main() { #[tokio::main]
async fn main() {
env_logger::init(); env_logger::init();
let mut host = None; let mut host = None;
let mut pairing_file = None; let mut pairing_file = None;
@@ -61,15 +62,15 @@ fn main() {
let ip = Ipv4Addr::from_str(host.unwrap().as_str()).unwrap(); let ip = Ipv4Addr::from_str(host.unwrap().as_str()).unwrap();
let socket = SocketAddrV4::new(ip, lockdownd::LOCKDOWND_PORT); let socket = SocketAddrV4::new(ip, lockdownd::LOCKDOWND_PORT);
let socket = std::net::TcpStream::connect(socket).unwrap(); let socket = tokio::net::TcpStream::connect(socket).await.unwrap();
let socket = Box::new(socket); let socket = Box::new(socket);
let idevice = Idevice::new(socket, "ideviceinfo-jkcoxson"); let idevice = Idevice::new(socket, "ideviceinfo-jkcoxson");
let mut lockdown_client = LockdowndClient::new(idevice); let mut lockdown_client = LockdowndClient::new(idevice);
println!("{:?}", lockdown_client.get_value("ProductVersion")); println!("{:?}", lockdown_client.get_value("ProductVersion").await);
let p = PairingFile::read_from_file(pairing_file.unwrap()).unwrap(); let p = PairingFile::read_from_file(pairing_file.unwrap()).unwrap();
println!("{:?}", lockdown_client.start_session(&p)); println!("{:?}", lockdown_client.start_session(&p).await);
println!("{:?}", lockdown_client.idevice.get_type().unwrap()); println!("{:?}", lockdown_client.idevice.get_type().await.unwrap());
println!("{:#?}", lockdown_client.get_all_values()); println!("{:#?}", lockdown_client.get_all_values().await);
} }

View File

@@ -13,7 +13,8 @@ use std::{
str::FromStr, str::FromStr,
}; };
fn main() { #[tokio::main]
async fn main() {
env_logger::init(); env_logger::init();
let mut host = None; let mut host = None;
let mut pairing_file = None; let mut pairing_file = None;
@@ -62,30 +63,31 @@ fn main() {
let ip = Ipv4Addr::from_str(host.unwrap().as_str()).unwrap(); let ip = Ipv4Addr::from_str(host.unwrap().as_str()).unwrap();
let socket = SocketAddrV4::new(ip, lockdownd::LOCKDOWND_PORT); let socket = SocketAddrV4::new(ip, lockdownd::LOCKDOWND_PORT);
let socket = std::net::TcpStream::connect(socket).unwrap(); let socket = tokio::net::TcpStream::connect(socket).await.unwrap();
let socket = Box::new(socket); let socket = Box::new(socket);
let idevice = Idevice::new(socket, "heartbeat_client"); let idevice = Idevice::new(socket, "heartbeat_client");
let p = PairingFile::read_from_file(pairing_file.as_ref().unwrap()).unwrap(); let p = PairingFile::read_from_file(pairing_file.as_ref().unwrap()).unwrap();
let mut lockdown_client = LockdowndClient { idevice }; let mut lockdown_client = LockdowndClient { idevice };
lockdown_client.start_session(&p).unwrap(); lockdown_client.start_session(&p).await.unwrap();
let (port, _) = lockdown_client let (port, _) = lockdown_client
.start_service("com.apple.mobile.installation_proxy") .start_service("com.apple.mobile.installation_proxy")
.await
.unwrap(); .unwrap();
let socket = SocketAddrV4::new(ip, port); let socket = SocketAddrV4::new(ip, port);
let socket = std::net::TcpStream::connect(socket).unwrap(); let socket = tokio::net::TcpStream::connect(socket).await.unwrap();
let socket = Box::new(socket); let socket = Box::new(socket);
let mut idevice = Idevice::new(socket, "instproxy-client"); let mut idevice = Idevice::new(socket, "instproxy-client");
let p = PairingFile::read_from_file(pairing_file.unwrap()).unwrap(); let p = PairingFile::read_from_file(pairing_file.unwrap()).unwrap();
idevice.start_session(&p).unwrap(); idevice.start_session(&p).await.unwrap();
let mut instproxy_client = InstallationProxyClient::new(idevice); let mut instproxy_client = InstallationProxyClient::new(idevice);
let apps = instproxy_client.get_apps(None, None).unwrap(); let apps = instproxy_client.get_apps(None, None).await.unwrap();
for app in apps.keys() { for app in apps.keys() {
println!("{app}"); println!("{app}");
} }

View File

@@ -15,7 +15,8 @@ use std::{
str::FromStr, str::FromStr,
}; };
fn main() { #[tokio::main]
async fn main() {
env_logger::init(); env_logger::init();
let mut host = None; let mut host = None;
let mut pairing_file = None; let mut pairing_file = None;
@@ -64,30 +65,31 @@ fn main() {
let ip = Ipv4Addr::from_str(host.unwrap().as_str()).unwrap(); let ip = Ipv4Addr::from_str(host.unwrap().as_str()).unwrap();
let socket = SocketAddrV4::new(ip, lockdownd::LOCKDOWND_PORT); let socket = SocketAddrV4::new(ip, lockdownd::LOCKDOWND_PORT);
let socket = std::net::TcpStream::connect(socket).unwrap(); let socket = tokio::net::TcpStream::connect(socket).await.unwrap();
let socket = Box::new(socket); let socket = Box::new(socket);
let idevice = Idevice::new(socket, "mounter_client"); let idevice = Idevice::new(socket, "mounter_client");
let p = PairingFile::read_from_file(pairing_file.as_ref().unwrap()).unwrap(); let p = PairingFile::read_from_file(pairing_file.as_ref().unwrap()).unwrap();
let mut lockdown_client = LockdowndClient { idevice }; let mut lockdown_client = LockdowndClient { idevice };
lockdown_client.start_session(&p).unwrap(); lockdown_client.start_session(&p).await.unwrap();
let (port, _) = lockdown_client let (port, _) = lockdown_client
.start_service("com.apple.mobile.mobile_image_mounter") .start_service("com.apple.mobile.mobile_image_mounter")
.await
.unwrap(); .unwrap();
let socket = SocketAddrV4::new(ip, port); let socket = SocketAddrV4::new(ip, port);
let socket = std::net::TcpStream::connect(socket).unwrap(); let socket = tokio::net::TcpStream::connect(socket).await.unwrap();
let socket = Box::new(socket); let socket = Box::new(socket);
let mut idevice = Idevice::new(socket, "mounter_client"); let mut idevice = Idevice::new(socket, "mounter_client");
let p = PairingFile::read_from_file(pairing_file.unwrap()).unwrap(); let p = PairingFile::read_from_file(pairing_file.unwrap()).unwrap();
idevice.start_session(&p).unwrap(); idevice.start_session(&p).await.unwrap();
let mut mounter_client = ImageMounter::new(idevice); let mut mounter_client = ImageMounter::new(idevice);
let images = mounter_client.copy_devices().unwrap(); let images = mounter_client.copy_devices().await.unwrap();
println!("Images: {images:#?}"); println!("Images: {images:#?}");
let image = std::fs::read("Image.dmg").unwrap(); let image = std::fs::read("Image.dmg").unwrap();
@@ -97,6 +99,7 @@ fn main() {
let manifest = mounter_client let manifest = mounter_client
.query_personalization_manifest("DeveloperDiskImage", hash.to_vec()) .query_personalization_manifest("DeveloperDiskImage", hash.to_vec())
.await
.unwrap(); .unwrap();
println!("len: {}", manifest.len()); println!("len: {}", manifest.len());
} }