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
11#[derive(Debug, thiserror::Error)]
12pub enum OsTraceError {
13    #[error("IO error: {0}")]
14    Io(#[from] std::io::Error),
15    #[error("plist error: {0}")]
16    Plist(String),
17    #[error("protocol error: {0}")]
18    Protocol(String),
19}
20
21pub struct OsTraceClient<S> {
22    stream: S,
23}
24
25impl<S: AsyncRead + AsyncWrite + Unpin> OsTraceClient<S> {
26    pub fn new(stream: S) -> Self {
27        Self { stream }
28    }
29
30    pub async fn get_pid_list(&mut self) -> Result<plist::Dictionary, OsTraceError> {
31        let request = plist::Dictionary::from_iter([(
32            "Request".to_string(),
33            plist::Value::String("PidList".into()),
34        )]);
35        send_plist(&mut self.stream, &plist::Value::Dictionary(request)).await?;
36
37        let _marker = self.stream.read_u8().await?;
38        recv_prefixed_plist(&mut self.stream).await
39    }
40}
41
42async fn send_plist<S: AsyncWrite + Unpin>(
43    stream: &mut S,
44    value: &plist::Value,
45) -> Result<(), OsTraceError> {
46    let mut buf = Vec::new();
47    plist::to_writer_xml(&mut buf, value).map_err(|err| OsTraceError::Plist(err.to_string()))?;
48    stream.write_all(&(buf.len() as u32).to_be_bytes()).await?;
49    stream.write_all(&buf).await?;
50    stream.flush().await?;
51    Ok(())
52}
53
54async fn recv_prefixed_plist<S: AsyncRead + Unpin>(
55    stream: &mut S,
56) -> Result<plist::Dictionary, OsTraceError> {
57    let len = stream.read_u32().await? as usize;
58    const MAX_PLIST_SIZE: usize = 8 * 1024 * 1024;
59    if len > MAX_PLIST_SIZE {
60        return Err(OsTraceError::Protocol(format!(
61            "plist length {len} exceeds max {MAX_PLIST_SIZE}"
62        )));
63    }
64
65    let mut buf = vec![0u8; len];
66    stream.read_exact(&mut buf).await?;
67    plist::from_bytes(&buf).map_err(|err| OsTraceError::Plist(err.to_string()))
68}