Create RSD service

This commit is contained in:
Jackson Coxson
2025-05-22 22:42:39 -06:00
parent 9a02e2bb6d
commit 6bf32afe82
8 changed files with 128 additions and 2 deletions

View File

@@ -69,6 +69,7 @@ misagent = []
mobile_image_mounter = ["dep:sha2"]
location_simulation = []
pair = ["chrono/default", "tokio/time", "dep:sha2", "dep:rsa", "dep:x509-cert"]
rsd = ["xpc"]
syslog_relay = ["dep:bytes"]
tcp = ["tokio/net"]
tunnel_tcp_stack = ["dep:rand", "dep:futures", "tokio/fs", "tokio/sync"]
@@ -98,6 +99,7 @@ full = [
"usbmuxd",
"xpc",
"location_simulation",
"rsd",
"tcp",
"tunnel_tcp_stack",
"tss",

View File

@@ -15,9 +15,12 @@ pub mod tunneld;
#[cfg(feature = "usbmuxd")]
pub mod usbmuxd;
mod util;
#[cfg(feature = "xpc")]
pub mod xpc;
pub mod services;
pub use services::*;
pub use xpc::RemoteXpcClient;
use log::{debug, error, trace};
use provider::IdeviceProvider;

View File

@@ -23,9 +23,9 @@ pub mod misagent;
pub mod mobile_image_mounter;
#[cfg(feature = "syslog_relay")]
pub mod os_trace_relay;
#[cfg(feature = "rsd")]
pub mod rsd;
#[cfg(feature = "springboardservices")]
pub mod springboardservices;
#[cfg(feature = "syslog_relay")]
pub mod syslog_relay;
#[cfg(feature = "xpc")]
pub mod xpc;

121
idevice/src/services/rsd.rs Normal file
View File

@@ -0,0 +1,121 @@
//! Remote Service Discovery
//! Communicates via XPC and returns advertised services
use std::collections::HashMap;
use log::warn;
use serde::Deserialize;
use crate::{IdeviceError, ReadWrite, RemoteXpcClient};
/// Describes an available XPC service
#[derive(Debug, Clone, Deserialize)]
pub struct XPCService {
/// Required entitlement to access this service
pub entitlement: String,
/// Port number where the service is available
pub port: u16,
/// Whether the service uses remote XPC
pub uses_remote_xpc: bool,
/// Optional list of supported features
pub features: Option<Vec<String>>,
/// Optional service version number
pub service_version: Option<i64>,
}
pub struct RsdClient<R: ReadWrite> {
inner: RemoteXpcClient<R>,
}
impl<R: ReadWrite> RsdClient<R> {
pub async fn new(socket: R) -> Result<Self, IdeviceError> {
Ok(Self {
inner: RemoteXpcClient::new(socket).await?,
})
}
pub async fn get_services(&mut self) -> Result<HashMap<String, XPCService>, IdeviceError> {
let data = self.inner.do_handshake().await?;
let data = match data
.as_dictionary()
.and_then(|x| x.get("Services"))
.and_then(|x| x.as_dictionary())
{
Some(d) => d,
None => return Err(IdeviceError::UnexpectedResponse),
};
// Parse available services
let mut services = HashMap::new();
for (name, service) in data.into_iter() {
match service.as_dictionary() {
Some(service) => {
let entitlement = match service.get("Entitlement").and_then(|x| x.as_string()) {
Some(e) => e.to_string(),
None => {
warn!("Service did not contain entitlement string");
continue;
}
};
let port = match service
.get("Port")
.and_then(|x| x.as_string())
.and_then(|x| x.parse::<u16>().ok())
{
Some(e) => e,
None => {
warn!("Service did not contain port string");
continue;
}
};
let uses_remote_xpc = match service
.get("Properties")
.and_then(|x| x.as_dictionary())
.and_then(|x| x.get("UsesRemoteXPC"))
.and_then(|x| x.as_boolean())
{
Some(e) => e.to_owned(),
None => false, // default is false
};
let features = service
.get("Properties")
.and_then(|x| x.as_dictionary())
.and_then(|x| x.get("Features"))
.and_then(|x| x.as_array())
.map(|f| {
f.iter()
.filter_map(|x| x.as_string())
.map(|x| x.to_string())
.collect::<Vec<String>>()
});
let service_version = service
.get("Properties")
.and_then(|x| x.as_dictionary())
.and_then(|x| x.get("ServiceVersion"))
.and_then(|x| x.as_signed_integer())
.map(|e| e.to_owned());
services.insert(
name.to_string(),
XPCService {
entitlement,
port,
uses_remote_xpc,
features,
service_version,
},
);
}
None => {
warn!("Service is not a dictionary!");
continue;
}
}
}
Ok(services)
}
}