Skip to main content

ios_core/services/debugserver/
mod.rs

1//! Minimal debugserver transport helpers.
2//!
3//! Reference: go-ios/ios/debugserver/*
4
5use semver::Version;
6use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
7
8pub const LEGACY_SERVICE_NAME: &str = "com.apple.debugserver";
9pub const SECURE_SERVICE_NAME: &str = "com.apple.debugserver.DVTSecureSocketProxy";
10
11#[derive(Debug, Clone, PartialEq, Eq)]
12pub struct ParsedPacket {
13    pub payload: String,
14    pub consumed: usize,
15}
16
17#[derive(Debug, thiserror::Error)]
18pub enum DebugserverError {
19    #[error("IO error: {0}")]
20    Io(#[from] std::io::Error),
21    #[error("invalid debugserver payload")]
22    InvalidPayload,
23    #[error("invalid UTF-8 payload: {0}")]
24    Utf8(#[from] std::string::FromUtf8Error),
25}
26
27pub fn select_service_name(version: &Version) -> &'static str {
28    if version.major >= 15 {
29        SECURE_SERVICE_NAME
30    } else {
31        LEGACY_SERVICE_NAME
32    }
33}
34
35pub fn checksum(payload: &str) -> String {
36    format!(
37        "{:02x}",
38        payload
39            .bytes()
40            .fold(0u8, |acc, byte| acc.wrapping_add(byte))
41    )
42}
43
44pub fn format_packet(payload: &str) -> String {
45    format!("+${payload}#{}", checksum(payload))
46}
47
48pub fn parse_packet(data: &[u8]) -> Option<ParsedPacket> {
49    const PACKET_SUFFIX_LEN: usize = 3; // "#xx"
50
51    let start = data.iter().position(|&b| b == b'$')?;
52    let end = data.iter().position(|&b| b == b'#')?;
53    if end < start {
54        return None;
55    }
56    if data.len() < end + PACKET_SUFFIX_LEN {
57        return None;
58    }
59
60    let payload = String::from_utf8(data[start + 1..end].to_vec()).ok()?;
61    Some(ParsedPacket {
62        payload,
63        consumed: end + PACKET_SUFFIX_LEN,
64    })
65}
66
67pub struct GdbRemoteClient<S> {
68    stream: S,
69    read_buf: Vec<u8>,
70}
71
72impl<S> GdbRemoteClient<S>
73where
74    S: AsyncRead + AsyncWrite + Unpin,
75{
76    pub fn new(stream: S) -> Self {
77        Self {
78            stream,
79            read_buf: Vec::with_capacity(4096),
80        }
81    }
82
83    pub fn into_inner(self) -> S {
84        self.stream
85    }
86
87    pub async fn send(&mut self, payload: &str) -> Result<(), DebugserverError> {
88        self.stream
89            .write_all(format_packet(payload).as_bytes())
90            .await?;
91        self.stream.flush().await?;
92        Ok(())
93    }
94
95    pub async fn recv(&mut self) -> Result<String, DebugserverError> {
96        let mut scratch = [0u8; 1024];
97        loop {
98            if let Some(packet) = parse_packet(&self.read_buf) {
99                self.read_buf.drain(..packet.consumed);
100                return Ok(packet.payload);
101            }
102
103            let read = self.stream.read(&mut scratch).await?;
104            if read == 0 {
105                return Err(DebugserverError::InvalidPayload);
106            }
107            self.read_buf.extend_from_slice(&scratch[..read]);
108        }
109    }
110
111    pub async fn request(&mut self, payload: &str) -> Result<String, DebugserverError> {
112        self.send(payload).await?;
113        self.recv().await
114    }
115}