ios_core/services/ostrace/
mod.rs1use 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}