Skip to main content

ios_core/services/ostrace/
mod.rs

1//! OS trace relay helpers.
2//!
3//! Service: `com.apple.os_trace_relay`
4//! Reference: pymobiledevice3 `os_trace.py`
5
6use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
7
8pub const SERVICE_NAME: &str = "com.apple.os_trace_relay";
9pub const SHIM_SERVICE_NAME: &str = "com.apple.os_trace_relay.shim.remote";
10
11service_error!(OsTraceError);
12
13pub struct OsTraceClient<S> {
14    stream: S,
15}
16
17impl<S: AsyncRead + AsyncWrite + Unpin> OsTraceClient<S> {
18    pub fn new(stream: S) -> Self {
19        Self { stream }
20    }
21
22    pub async fn get_pid_list(&mut self) -> Result<plist::Dictionary, OsTraceError> {
23        let request = plist::Dictionary::from_iter([(
24            "Request".to_string(),
25            plist::Value::String("PidList".into()),
26        )]);
27        send_plist(&mut self.stream, &plist::Value::Dictionary(request)).await?;
28
29        let _marker = self.stream.read_u8().await?;
30        recv_prefixed_plist(&mut self.stream).await
31    }
32}
33
34async fn send_plist<S: AsyncWrite + Unpin>(
35    stream: &mut S,
36    value: &plist::Value,
37) -> Result<(), OsTraceError> {
38    let mut buf = Vec::new();
39    plist::to_writer_xml(&mut buf, value)?;
40    stream.write_all(&(buf.len() as u32).to_be_bytes()).await?;
41    stream.write_all(&buf).await?;
42    stream.flush().await?;
43    Ok(())
44}
45
46async fn recv_prefixed_plist<S: AsyncRead + Unpin>(
47    stream: &mut S,
48) -> Result<plist::Dictionary, OsTraceError> {
49    let len = stream.read_u32().await? as usize;
50    const MAX_PLIST_SIZE: usize = 8 * 1024 * 1024;
51    if len > MAX_PLIST_SIZE {
52        return Err(OsTraceError::Protocol(format!(
53            "plist length {len} exceeds max {MAX_PLIST_SIZE}"
54        )));
55    }
56
57    let mut buf = vec![0u8; len];
58    stream.read_exact(&mut buf).await?;
59    Ok(plist::from_bytes(&buf)?)
60}