From a39cbd1d3d5ed309e2895f21c5f0e88badb19508 Mon Sep 17 00:00:00 2001 From: Jackson Coxson Date: Sun, 25 May 2025 13:11:59 -0600 Subject: [PATCH] Create new RSD service trait --- idevice/src/lib.rs | 23 +++++++- idevice/src/provider.rs | 10 +++- idevice/src/services/debug_proxy.rs | 19 +++++-- idevice/src/services/dvt/mod.rs | 14 ++++- idevice/src/services/rsd.rs | 88 +++++++++++++++++++++++------ idevice/src/tcp/mod.rs | 15 +++++ tools/src/debug_proxy.rs | 22 ++------ tools/src/location_simulation.rs | 23 +++----- tools/src/process_control.rs | 23 +++----- 9 files changed, 166 insertions(+), 71 deletions(-) diff --git a/idevice/src/lib.rs b/idevice/src/lib.rs index b7ead1d..550112a 100644 --- a/idevice/src/lib.rs +++ b/idevice/src/lib.rs @@ -25,7 +25,7 @@ pub use services::*; pub use xpc::RemoteXpcClient; use log::{debug, error, trace}; -use provider::IdeviceProvider; +use provider::{IdeviceProvider, RsdProvider}; use rustls::{crypto::CryptoProvider, pki_types::ServerName}; use std::{ io::{self, BufWriter}, @@ -65,6 +65,25 @@ pub trait IdeviceService: Sized { ) -> impl std::future::Future> + Send; } +pub trait RsdService: Sized { + fn rsd_service_name() -> &'static str; + fn from_stream( + stream: Self::Stream, + ) -> impl std::future::Future> + Send; + fn connect_rsd<'a, S>( + provider: &'a mut impl RsdProvider<'a, Stream = S>, + handshake: &mut rsd::RsdHandshake, + ) -> impl std::future::Future> + where + Self: crate::RsdService, + S: ReadWrite, + { + handshake.connect(provider) + } + + type Stream: ReadWrite; +} + /// Type alias for boxed device connection sockets /// /// Used to enable dynamic dispatch of different connection types while maintaining @@ -424,6 +443,8 @@ pub enum IdeviceError { HeartbeatTimeout, #[error("not found")] NotFound, + #[error("service not found")] + ServiceNotFound, #[error("CDTunnel packet too short")] CdtunnelPacketTooShort, #[error("CDTunnel packet invalid magic")] diff --git a/idevice/src/provider.rs b/idevice/src/provider.rs index 740f6a1..bab7aeb 100644 --- a/idevice/src/provider.rs +++ b/idevice/src/provider.rs @@ -8,7 +8,7 @@ use std::{future::Future, pin::Pin}; #[cfg(feature = "tcp")] use tokio::net::TcpStream; -use crate::{pairing_file::PairingFile, Idevice, IdeviceError}; +use crate::{pairing_file::PairingFile, Idevice, IdeviceError, ReadWrite}; #[cfg(feature = "usbmuxd")] use crate::usbmuxd::UsbmuxdAddr; @@ -42,6 +42,14 @@ pub trait IdeviceProvider: Unpin + Send + Sync + std::fmt::Debug { ) -> Pin> + Send>>; } +pub trait RsdProvider<'a>: Unpin + Send + Sync + std::fmt::Debug { + fn connect_to_service_port( + &'a mut self, + port: u16, + ) -> impl std::future::Future> + Send; + type Stream: ReadWrite; +} + /// TCP-based device connection provider #[cfg(feature = "tcp")] #[derive(Debug)] diff --git a/idevice/src/services/debug_proxy.rs b/idevice/src/services/debug_proxy.rs index a8de60e..022d5f2 100644 --- a/idevice/src/services/debug_proxy.rs +++ b/idevice/src/services/debug_proxy.rs @@ -8,10 +8,22 @@ use log::debug; use std::fmt::Write; use tokio::io::{AsyncReadExt, AsyncWriteExt}; -use crate::{IdeviceError, ReadWrite}; +use crate::{IdeviceError, ReadWrite, RsdService}; -/// The service name for the debug proxy as registered with lockdownd -pub const SERVICE_NAME: &str = "com.apple.internal.dt.remote.debugproxy"; +impl RsdService for DebugProxyClient { + fn rsd_service_name() -> &'static str { + "com.apple.internal.dt.remote.debugproxy" + } + + async fn from_stream(stream: R) -> Result { + Ok(Self { + socket: stream, + noack_mode: false, + }) + } + + type Stream = R; +} /// Client for interacting with the iOS debug proxy service /// @@ -300,4 +312,3 @@ impl From<&str> for DebugserverCommand { s.to_string().into() } } - diff --git a/idevice/src/services/dvt/mod.rs b/idevice/src/services/dvt/mod.rs index 55063a6..5764ded 100644 --- a/idevice/src/services/dvt/mod.rs +++ b/idevice/src/services/dvt/mod.rs @@ -1,9 +1,21 @@ // Jackson Coxson +use crate::{IdeviceError, ReadWrite, RsdService}; + #[cfg(feature = "location_simulation")] pub mod location_simulation; pub mod message; pub mod process_control; pub mod remote_server; -pub const SERVICE_NAME: &str = "com.apple.instruments.dtservicehub"; +impl RsdService for remote_server::RemoteServerClient { + fn rsd_service_name() -> &'static str { + "com.apple.instruments.dtservicehub" + } + + async fn from_stream(stream: R) -> Result { + Ok(Self::new(stream)) + } + + type Stream = R; +} diff --git a/idevice/src/services/rsd.rs b/idevice/src/services/rsd.rs index 5844b24..7073b44 100644 --- a/idevice/src/services/rsd.rs +++ b/idevice/src/services/rsd.rs @@ -6,7 +6,7 @@ use std::collections::HashMap; use log::warn; use serde::Deserialize; -use crate::{IdeviceError, ReadWrite, RemoteXpcClient}; +use crate::{provider::RsdProvider, IdeviceError, ReadWrite, RemoteXpcClient}; /// Describes an available XPC service #[derive(Debug, Clone, Deserialize)] @@ -23,21 +23,19 @@ pub struct RsdService { pub service_version: Option, } -pub struct RsdClient { - inner: RemoteXpcClient, +pub struct RsdHandshake { + pub services: HashMap, + pub protocol_version: usize, + pub properties: HashMap, + pub uuid: String, } -impl RsdClient { - pub async fn new(socket: R) -> Result { - Ok(Self { - inner: RemoteXpcClient::new(socket).await?, - }) - } +impl RsdHandshake { + pub async fn new(socket: impl ReadWrite) -> Result { + let mut xpc_client = RemoteXpcClient::new(socket).await?; + let data = xpc_client.do_handshake().await?; - pub async fn get_services(&mut self) -> Result, IdeviceError> { - let data = self.inner.do_handshake().await?; - - let data = match data + let services_dict = match data .as_dictionary() .and_then(|x| x.get("Services")) .and_then(|x| x.as_dictionary()) @@ -47,8 +45,8 @@ impl RsdClient { }; // Parse available services - let mut services = HashMap::new(); - for (name, service) in data.into_iter() { + let mut services: HashMap = HashMap::new(); + for (name, service) in services_dict.into_iter() { match service.as_dictionary() { Some(service) => { let entitlement = match service.get("Entitlement").and_then(|x| x.as_string()) { @@ -116,6 +114,64 @@ impl RsdClient { } } - Ok(services) + let protocol_version = match data.as_dictionary().and_then(|x| { + x.get("MessagingProtocolVersion") + .and_then(|x| x.as_signed_integer()) + }) { + Some(p) => p as usize, + None => { + return Err(IdeviceError::UnexpectedResponse); + } + }; + + let uuid = match data + .as_dictionary() + .and_then(|x| x.get("UUID").and_then(|x| x.as_string())) + { + Some(u) => u.to_string(), + None => { + return Err(IdeviceError::UnexpectedResponse); + } + }; + + let properties = match data + .as_dictionary() + .and_then(|x| x.get("Properties").and_then(|x| x.as_dictionary())) + { + Some(d) => d + .into_iter() + .map(|(name, prop)| (name.to_owned(), prop.to_owned())) + .collect::>(), + None => { + return Err(IdeviceError::UnexpectedResponse); + } + }; + + Ok(Self { + services, + protocol_version, + properties, + uuid, + }) + } + + pub async fn connect<'a, T, S>( + &mut self, + provider: &'a mut impl RsdProvider<'a, Stream = S>, + ) -> Result + where + T: crate::RsdService, + S: ReadWrite, + { + let service_name = T::rsd_service_name(); + let service = match self.services.get(service_name) { + Some(s) => s, + None => { + return Err(IdeviceError::ServiceNotFound); + } + }; + + let stream = provider.connect_to_service_port(service.port).await?; + T::from_stream(stream).await } } diff --git a/idevice/src/tcp/mod.rs b/idevice/src/tcp/mod.rs index c8fda3e..0c4424d 100644 --- a/idevice/src/tcp/mod.rs +++ b/idevice/src/tcp/mod.rs @@ -6,8 +6,11 @@ use std::{ }; use log::debug; +use stream::AdapterStream; use tokio::io::AsyncWriteExt; +use crate::provider::RsdProvider; + pub mod adapter; pub mod packets; pub mod stream; @@ -36,6 +39,18 @@ pub(crate) fn log_packet(file: &Arc>, packet }); } +impl<'a> RsdProvider<'a> for adapter::Adapter { + async fn connect_to_service_port( + &'a mut self, + port: u16, + ) -> Result, crate::IdeviceError> { + let s = stream::AdapterStream::connect(self, port).await?; + Ok(s) + } + + type Stream = AdapterStream<'a>; +} + #[cfg(test)] mod tests { use std::{ diff --git a/tools/src/debug_proxy.rs b/tools/src/debug_proxy.rs index 809b24d..ba3be4d 100644 --- a/tools/src/debug_proxy.rs +++ b/tools/src/debug_proxy.rs @@ -4,8 +4,8 @@ use std::io::Write; use clap::{Arg, Command}; use idevice::{ - core_device_proxy::CoreDeviceProxy, debug_proxy::DebugProxyClient, rsd::RsdClient, - tcp::stream::AdapterStream, IdeviceService, + core_device_proxy::CoreDeviceProxy, debug_proxy::DebugProxyClient, rsd::RsdHandshake, + tcp::stream::AdapterStream, IdeviceService, RsdService, }; mod common; @@ -77,22 +77,12 @@ async fn main() { .expect("no RSD connect"); // Make the connection to RemoteXPC - let mut client = RsdClient::new(stream).await.unwrap(); + let mut handshake = RsdHandshake::new(stream).await.unwrap(); + println!("{:?}", handshake.services); - // Get the debug proxy - let service = client - .get_services() + let mut dp = DebugProxyClient::connect_rsd(&mut adapter, &mut handshake) .await - .unwrap() - .get(idevice::debug_proxy::SERVICE_NAME) - .expect("Client did not contain debug proxy service") - .to_owned(); - - let stream = AdapterStream::connect(&mut adapter, service.port) - .await - .unwrap(); - - let mut dp = DebugProxyClient::new(stream); + .expect("no connect"); println!("Shell connected!"); loop { diff --git a/tools/src/location_simulation.rs b/tools/src/location_simulation.rs index 471a239..146bf64 100644 --- a/tools/src/location_simulation.rs +++ b/tools/src/location_simulation.rs @@ -3,7 +3,8 @@ use clap::{Arg, Command}; use idevice::{ - core_device_proxy::CoreDeviceProxy, rsd::RsdClient, tcp::stream::AdapterStream, IdeviceService, + core_device_proxy::CoreDeviceProxy, rsd::RsdHandshake, tcp::stream::AdapterStream, + IdeviceService, RsdService, }; mod common; @@ -76,22 +77,12 @@ async fn main() { .expect("no RSD connect"); // Make the connection to RemoteXPC - let mut client = RsdClient::new(stream).await.unwrap(); + let mut handshake = RsdHandshake::new(stream).await.unwrap(); - // Get the debug proxy - let service = client - .get_services() - .await - .unwrap() - .get(idevice::dvt::SERVICE_NAME) - .expect("Client did not contain DVT service") - .to_owned(); - - let stream = AdapterStream::connect(&mut adapter, service.port) - .await - .unwrap(); - - let mut ls_client = idevice::dvt::remote_server::RemoteServerClient::new(stream); + let mut ls_client = + idevice::dvt::remote_server::RemoteServerClient::connect_rsd(&mut adapter, &mut handshake) + .await + .expect("Failed to connect"); ls_client.read_message(0).await.expect("no read??"); let mut ls_client = diff --git a/tools/src/process_control.rs b/tools/src/process_control.rs index 0f7f84d..173633d 100644 --- a/tools/src/process_control.rs +++ b/tools/src/process_control.rs @@ -2,7 +2,8 @@ use clap::{Arg, Command}; use idevice::{ - core_device_proxy::CoreDeviceProxy, rsd::RsdClient, tcp::stream::AdapterStream, IdeviceService, + core_device_proxy::CoreDeviceProxy, rsd::RsdHandshake, tcp::stream::AdapterStream, + IdeviceService, RsdService, }; mod common; @@ -84,22 +85,12 @@ async fn main() { .expect("no RSD connect"); // Make the connection to RemoteXPC - let mut client = RsdClient::new(stream).await.unwrap(); + let mut handshake = RsdHandshake::new(stream).await.unwrap(); - // Get the debug proxy - let service = client - .get_services() - .await - .unwrap() - .get(idevice::dvt::SERVICE_NAME) - .expect("Client did not contain DVT service") - .to_owned(); - - let stream = AdapterStream::connect(&mut adapter, service.port) - .await - .unwrap(); - - let mut rs_client = idevice::dvt::remote_server::RemoteServerClient::new(stream); + let mut rs_client = + idevice::dvt::remote_server::RemoteServerClient::connect_rsd(&mut adapter, &mut handshake) + .await + .expect("no connect"); rs_client.read_message(0).await.expect("no read??"); let mut pc_client = idevice::dvt::process_control::ProcessControlClient::new(&mut rs_client) .await