Implement the service trait

This commit is contained in:
Jackson Coxson
2025-02-01 14:15:10 -07:00
parent c4e5dfa209
commit 046be92bba
8 changed files with 139 additions and 15 deletions

View File

@@ -1,14 +1,11 @@
// Jackson Coxson // Jackson Coxson
use crate::{Idevice, IdeviceError}; use crate::{lockdownd::LockdowndClient, Idevice, IdeviceError, IdeviceService};
use byteorder::{BigEndian, WriteBytesExt}; use byteorder::{BigEndian, WriteBytesExt};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::io::{self, Write}; use std::io::{self, Write};
pub const SERVCE_NAME: &str = "com.apple.internal.devicecompute.CoreDeviceProxy";
const DEFAULT_MTU: u32 = 16000;
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub struct CDTunnelPacket { pub struct CDTunnelPacket {
body: Vec<u8>, body: Vec<u8>,
@@ -67,6 +64,28 @@ pub struct CoreDeviceProxy {
pub mtu: u32, pub mtu: u32,
} }
impl IdeviceService for CoreDeviceProxy {
fn service_name() -> &'static str {
"com.apple.internal.devicecompute.CoreDeviceProxy"
}
async fn connect(
provider: &impl crate::provider::IdeviceProvider,
) -> Result<Self, IdeviceError> {
let mut lockdown = LockdowndClient::connect(provider).await?;
let (port, ssl) = lockdown.start_service(Self::service_name()).await?;
let mut idevice = provider.connect(port).await?;
if ssl {
idevice
.start_session(&provider.get_pairing_file().await?)
.await?;
}
Ok(Self::new(idevice))
}
}
#[derive(Serialize)] #[derive(Serialize)]
struct HandshakeRequest { struct HandshakeRequest {
#[serde(rename = "type")] #[serde(rename = "type")]
@@ -94,17 +113,19 @@ pub struct HandshakeResponse {
} }
impl CoreDeviceProxy { impl CoreDeviceProxy {
const DEFAULT_MTU: u32 = 16000;
pub fn new(idevice: Idevice) -> Self { pub fn new(idevice: Idevice) -> Self {
Self { Self {
idevice, idevice,
mtu: DEFAULT_MTU, mtu: Self::DEFAULT_MTU,
} }
} }
pub async fn establish_tunnel(&mut self) -> Result<HandshakeResponse, IdeviceError> { pub async fn establish_tunnel(&mut self) -> Result<HandshakeResponse, IdeviceError> {
let req = HandshakeRequest { let req = HandshakeRequest {
packet_type: "clientHandshakeRequest".to_string(), packet_type: "clientHandshakeRequest".to_string(),
mtu: DEFAULT_MTU, mtu: Self::DEFAULT_MTU,
}; };
let req = CDTunnelPacket::serialize(&CDTunnelPacket { let req = CDTunnelPacket::serialize(&CDTunnelPacket {

View File

@@ -1,12 +1,34 @@
// Jackson Coxson // Jackson Coxson
// Abstractions for the heartbeat service on iOS // Abstractions for the heartbeat service on iOS
use crate::{Idevice, IdeviceError}; use crate::{lockdownd::LockdowndClient, Idevice, IdeviceError, IdeviceService};
pub struct HeartbeatClient { pub struct HeartbeatClient {
pub idevice: Idevice, pub idevice: Idevice,
} }
impl IdeviceService for HeartbeatClient {
fn service_name() -> &'static str {
"com.apple.mobile.heartbeat"
}
async fn connect(
provider: &impl crate::provider::IdeviceProvider,
) -> Result<Self, IdeviceError> {
let mut lockdown = LockdowndClient::connect(provider).await?;
let (port, ssl) = lockdown.start_service(Self::service_name()).await?;
let mut idevice = provider.connect(port).await?;
if ssl {
idevice
.start_session(&provider.get_pairing_file().await?)
.await?;
}
Ok(Self { idevice })
}
}
impl HeartbeatClient { impl HeartbeatClient {
pub fn new(idevice: Idevice) -> Self { pub fn new(idevice: Idevice) -> Self {
Self { idevice } Self { idevice }

View File

@@ -3,12 +3,34 @@
use std::collections::HashMap; use std::collections::HashMap;
use crate::{Idevice, IdeviceError}; use crate::{lockdownd::LockdowndClient, Idevice, IdeviceError, IdeviceService};
pub struct InstallationProxyClient { pub struct InstallationProxyClient {
pub idevice: Idevice, pub idevice: Idevice,
} }
impl IdeviceService for InstallationProxyClient {
fn service_name() -> &'static str {
"com.apple.mobile.installation_proxy"
}
async fn connect(
provider: &impl crate::provider::IdeviceProvider,
) -> Result<Self, IdeviceError> {
let mut lockdown = LockdowndClient::connect(provider).await?;
let (port, ssl) = lockdown.start_service(Self::service_name()).await?;
let mut idevice = provider.connect(port).await?;
if ssl {
idevice
.start_session(&provider.get_pairing_file().await?)
.await?;
}
Ok(Self::new(idevice))
}
}
impl InstallationProxyClient { impl InstallationProxyClient {
pub fn new(idevice: Idevice) -> Self { pub fn new(idevice: Idevice) -> Self {
Self { idevice } Self { idevice }

View File

@@ -20,6 +20,7 @@ pub mod xpc;
use log::{debug, error}; use log::{debug, error};
use openssl::ssl::{SslConnector, SslMethod, SslVerifyMode}; use openssl::ssl::{SslConnector, SslMethod, SslVerifyMode};
use provider::IdeviceProvider;
use std::io::{self, BufWriter}; use std::io::{self, BufWriter};
use thiserror::Error; use thiserror::Error;
use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}; use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
@@ -27,6 +28,13 @@ use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
pub trait ReadWrite: AsyncRead + AsyncWrite + Unpin + Send + Sync + std::fmt::Debug {} pub trait ReadWrite: AsyncRead + AsyncWrite + Unpin + Send + Sync + std::fmt::Debug {}
impl<T: AsyncRead + AsyncWrite + Unpin + Send + Sync + std::fmt::Debug> ReadWrite for T {} impl<T: AsyncRead + AsyncWrite + Unpin + Send + Sync + std::fmt::Debug> ReadWrite for T {}
pub trait IdeviceService: Sized {
fn service_name() -> &'static str;
fn connect(
provider: &impl IdeviceProvider,
) -> impl std::future::Future<Output = Result<Self, IdeviceError>> + Send;
}
pub type IdeviceSocket = Box<dyn ReadWrite>; pub type IdeviceSocket = Box<dyn ReadWrite>;
pub struct Idevice { pub struct Idevice {

View File

@@ -1,17 +1,28 @@
// Jackson Coxson // Jackson Coxson
// Abstractions for lockdownd // Abstractions for lockdownd
pub const LOCKDOWND_PORT: u16 = 62078;
use log::error; use log::error;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{pairing_file, Idevice, IdeviceError}; use crate::{pairing_file, Idevice, IdeviceError, IdeviceService};
pub struct LockdowndClient { pub struct LockdowndClient {
pub idevice: crate::Idevice, pub idevice: crate::Idevice,
} }
impl IdeviceService for LockdowndClient {
fn service_name() -> &'static str {
"com.apple.mobile.lockdown"
}
async fn connect(
provider: &impl crate::provider::IdeviceProvider,
) -> Result<Self, IdeviceError> {
let idevice = provider.connect(Self::LOCKDOWND_PORT).await?;
Ok(Self { idevice })
}
}
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")] #[serde(rename_all = "PascalCase")]
struct LockdowndRequest { struct LockdowndRequest {
@@ -21,6 +32,8 @@ struct LockdowndRequest {
} }
impl LockdowndClient { impl LockdowndClient {
pub const LOCKDOWND_PORT: u16 = 62078;
pub fn new(idevice: Idevice) -> Self { pub fn new(idevice: Idevice) -> Self {
Self { idevice } Self { idevice }
} }

View File

@@ -1,11 +1,33 @@
// Jackson Coxson // Jackson Coxson
use crate::{Idevice, IdeviceError}; use crate::{lockdownd::LockdowndClient, Idevice, IdeviceError, IdeviceService};
pub struct ImageMounter { pub struct ImageMounter {
idevice: Idevice, idevice: Idevice,
} }
impl IdeviceService for ImageMounter {
fn service_name() -> &'static str {
"com.apple.mobile.mobile_image_mounter"
}
async fn connect(
provider: &impl crate::provider::IdeviceProvider,
) -> Result<Self, IdeviceError> {
let mut lockdown = LockdowndClient::connect(provider).await?;
let (port, ssl) = lockdown.start_service(Self::service_name()).await?;
let mut idevice = provider.connect(port).await?;
if ssl {
idevice
.start_session(&provider.get_pairing_file().await?)
.await?;
}
Ok(Self { idevice })
}
}
impl ImageMounter { impl ImageMounter {
pub fn new(idevice: Idevice) -> Self { pub fn new(idevice: Idevice) -> Self {
Self { idevice } Self { idevice }

View File

@@ -4,19 +4,24 @@ use std::net::{IpAddr, SocketAddr};
use tokio::net::TcpStream; use tokio::net::TcpStream;
use crate::{usbmuxd::UsbmuxdAddr, Idevice, IdeviceError}; use crate::{pairing_file::PairingFile, usbmuxd::UsbmuxdAddr, Idevice, IdeviceError};
pub trait IdeviceProvider { pub trait IdeviceProvider: Unpin + Send + Sync + std::fmt::Debug {
// https://blog.rust-lang.org/2023/12/21/async-fn-rpit-in-traits.html#is-it-okay-to-use-async-fn-in-traits-what-are-the-limitations // https://blog.rust-lang.org/2023/12/21/async-fn-rpit-in-traits.html#is-it-okay-to-use-async-fn-in-traits-what-are-the-limitations
fn connect( fn connect(
&self, &self,
port: u16, port: u16,
) -> impl std::future::Future<Output = Result<Idevice, IdeviceError>> + Send; ) -> impl std::future::Future<Output = Result<Idevice, IdeviceError>> + Send;
fn label(&self) -> &str; fn label(&self) -> &str;
fn get_pairing_file(
&self,
) -> impl std::future::Future<Output = Result<PairingFile, IdeviceError>> + Send;
} }
#[derive(Debug)]
pub struct TcpProvider { pub struct TcpProvider {
addr: IpAddr, addr: IpAddr,
pairing_file: PairingFile,
label: String, label: String,
} }
@@ -29,12 +34,18 @@ impl IdeviceProvider for TcpProvider {
fn label(&self) -> &str { fn label(&self) -> &str {
self.label.as_str() self.label.as_str()
} }
async fn get_pairing_file(&self) -> Result<PairingFile, IdeviceError> {
Ok(self.pairing_file.clone())
}
} }
#[cfg(feature = "usbmuxd")] #[cfg(feature = "usbmuxd")]
#[derive(Debug)]
pub struct UsbmuxdProvider { pub struct UsbmuxdProvider {
addr: UsbmuxdAddr, addr: UsbmuxdAddr,
tag: u32, tag: u32,
udid: String,
device_id: u32, device_id: u32,
label: String, label: String,
} }
@@ -51,4 +62,9 @@ impl IdeviceProvider for UsbmuxdProvider {
fn label(&self) -> &str { fn label(&self) -> &str {
self.label.as_str() self.label.as_str()
} }
async fn get_pairing_file(&self) -> Result<PairingFile, IdeviceError> {
let mut usbmuxd = self.addr.connect(self.tag).await?;
usbmuxd.get_pair_record(&self.udid).await
}
} }

View File

@@ -29,7 +29,7 @@ pub struct UsbmuxdConnection {
tag: u32, tag: u32,
} }
#[derive()] #[derive(Debug)]
pub enum UsbmuxdAddr { pub enum UsbmuxdAddr {
UnixSocket(String), UnixSocket(String),
TcpSocket(SocketAddr), TcpSocket(SocketAddr),