idevice/services/
core_device_proxy.rs

1//! CoreDeviceProxy and CDTunnelPacket utilities for interacting with
2//! iOS's CoreDeviceProxy service. This service starts an L3 (TUN) tunnel
3//! for "trusted" services introduced in iOS 17.
4//!
5//! This module handles the construction and parsing of `CDTunnelPacket` messages
6//! and manages a handshake and data tunnel to the CoreDeviceProxy daemon on iOS devices.
7//!
8//! # Overview
9//! - `CDTunnelPacket` is used to parse and serialize packets sent over the CoreDeviceProxy channel.
10//! - `CoreDeviceProxy` is a service client that initializes the tunnel, handles handshakes,
11//!   and optionally supports creating a software-based TCP/IP tunnel (behind a feature flag).
12//!
13//! # Features
14//! - `tunnel_tcp_stack`: Enables software TCP/IP tunnel creation using a virtual adapter. See the tcp moduel.
15
16use crate::{Idevice, IdeviceError, IdeviceService, obf};
17
18use byteorder::{BigEndian, WriteBytesExt};
19use serde::{Deserialize, Serialize};
20use std::io::{self, Write};
21
22/// A representation of a CDTunnel packet used in the CoreDeviceProxy protocol.
23#[derive(Debug, PartialEq)]
24pub struct CDTunnelPacket {
25    /// The body of the packet, typically JSON-encoded data.
26    body: Vec<u8>,
27}
28
29impl CDTunnelPacket {
30    const MAGIC: &'static [u8] = b"CDTunnel";
31
32    /// Parses a byte slice into a `CDTunnelPacket`.
33    ///
34    /// # Arguments
35    ///
36    /// * `input` - A byte slice containing the raw packet data.
37    ///
38    /// # Returns
39    ///
40    /// * `Ok(CDTunnelPacket)` if the input is a valid packet.
41    /// * `Err(IdeviceError)` if parsing fails due to invalid magic, length, or size.
42    pub fn parse(input: &[u8]) -> Result<Self, IdeviceError> {
43        if input.len() < Self::MAGIC.len() + 2 {
44            return Err(IdeviceError::CdtunnelPacketTooShort);
45        }
46
47        if &input[0..Self::MAGIC.len()] != Self::MAGIC {
48            return Err(IdeviceError::CdtunnelPacketInvalidMagic);
49        }
50
51        let length_offset = Self::MAGIC.len();
52        let body_length =
53            u16::from_be_bytes([input[length_offset], input[length_offset + 1]]) as usize;
54
55        if input.len() < length_offset + 2 + body_length {
56            return Err(IdeviceError::PacketSizeMismatch);
57        }
58
59        let body_start = length_offset + 2;
60        let body = input[body_start..body_start + body_length].to_vec();
61
62        Ok(Self { body })
63    }
64
65    /// Serializes the `CDTunnelPacket` into a byte vector.
66    ///
67    /// # Returns
68    ///
69    /// * `Ok(Vec<u8>)` containing the serialized packet.
70    /// * `Err(io::Error)` if writing to the output buffer fails.
71    pub fn serialize(&self) -> io::Result<Vec<u8>> {
72        let mut output = Vec::new();
73
74        output.write_all(Self::MAGIC)?;
75        output.write_u16::<BigEndian>(self.body.len() as u16)?;
76        output.write_all(&self.body)?;
77
78        Ok(output)
79    }
80}
81
82/// A high-level client for the `com.apple.internal.devicecompute.CoreDeviceProxy` service.
83///
84/// Handles session negotiation, handshake, and tunnel communication.
85pub struct CoreDeviceProxy {
86    /// The underlying idevice connection used for communication.
87    pub idevice: Idevice,
88    /// The handshake response received during initialization.
89    pub handshake: HandshakeResponse,
90    /// The maximum transmission unit used for reading.
91    pub mtu: u32,
92}
93
94impl IdeviceService for CoreDeviceProxy {
95    /// Returns the name of the service used for launching the CoreDeviceProxy.
96    fn service_name() -> std::borrow::Cow<'static, str> {
97        obf!("com.apple.internal.devicecompute.CoreDeviceProxy")
98    }
99
100    async fn from_stream(idevice: Idevice) -> Result<Self, crate::IdeviceError> {
101        Self::new(idevice).await
102    }
103}
104
105/// Request sent to initiate the handshake with the CoreDeviceProxy.
106#[derive(Serialize)]
107struct HandshakeRequest {
108    #[serde(rename = "type")]
109    packet_type: String,
110    mtu: u32,
111}
112
113/// Parameters returned as part of the handshake response from the proxy server.
114#[derive(Debug, Serialize, Deserialize)]
115pub struct ClientParameters {
116    /// The MTU (maximum transmission unit) for the connection.
117    pub mtu: u16,
118    /// The IP address assigned to the client.
119    pub address: String,
120    /// The subnet mask for the tunnel.
121    pub netmask: String,
122}
123
124/// Handshake response structure received from the CoreDeviceProxy.
125#[derive(Debug, Serialize, Deserialize)]
126pub struct HandshakeResponse {
127    #[serde(rename = "clientParameters")]
128    pub client_parameters: ClientParameters,
129    #[serde(rename = "serverAddress")]
130    pub server_address: String,
131    #[serde(rename = "type")]
132    pub response_type: String,
133    #[serde(rename = "serverRSDPort")]
134    pub server_rsd_port: u16,
135}
136
137impl CoreDeviceProxy {
138    const DEFAULT_MTU: u32 = 16000;
139
140    /// Constructs a new `CoreDeviceProxy` by performing a handshake on the given `Idevice`.
141    ///
142    /// # Arguments
143    ///
144    /// * `idevice` - The connected `Idevice` socket.
145    ///
146    /// # Returns
147    ///
148    /// * `Ok(CoreDeviceProxy)` on successful handshake.
149    /// * `Err(IdeviceError)` if the handshake fails.
150    pub async fn new(mut idevice: Idevice) -> Result<Self, IdeviceError> {
151        let req = HandshakeRequest {
152            packet_type: "clientHandshakeRequest".to_string(),
153            mtu: Self::DEFAULT_MTU,
154        };
155
156        let req = CDTunnelPacket::serialize(&CDTunnelPacket {
157            body: serde_json::to_vec(&req)?,
158        })?;
159
160        idevice.send_raw(&req).await?;
161        let recv = idevice.read_raw(CDTunnelPacket::MAGIC.len() + 2).await?;
162
163        if recv.len() < CDTunnelPacket::MAGIC.len() + 2 {
164            return Err(IdeviceError::CdtunnelPacketTooShort);
165        }
166
167        let len = u16::from_be_bytes([
168            recv[CDTunnelPacket::MAGIC.len()],
169            recv[CDTunnelPacket::MAGIC.len() + 1],
170        ]) as usize;
171
172        let recv = idevice.read_raw(len).await?;
173        let res = serde_json::from_slice::<HandshakeResponse>(&recv)?;
174
175        Ok(Self {
176            idevice,
177            handshake: res,
178            mtu: Self::DEFAULT_MTU,
179        })
180    }
181
182    /// Sends a raw data packet through the tunnel.
183    ///
184    /// # Arguments
185    ///
186    /// * `data` - The raw bytes to send.
187    ///
188    /// # Returns
189    ///
190    /// * `Ok(())` if the data is successfully sent.
191    /// * `Err(IdeviceError)` if sending fails.
192    pub async fn send(&mut self, data: &[u8]) -> Result<(), IdeviceError> {
193        self.idevice.send_raw(data).await?;
194        Ok(())
195    }
196
197    /// Receives up to `mtu` bytes from the tunnel.
198    ///
199    /// # Returns
200    ///
201    /// * `Ok(Vec<u8>)` containing the received data.
202    /// * `Err(IdeviceError)` if reading fails.
203    pub async fn recv(&mut self) -> Result<Vec<u8>, IdeviceError> {
204        self.idevice.read_any(self.mtu).await
205    }
206
207    /// Creates a software-based TCP tunnel adapter, if the `tunnel_tcp_stack` feature is enabled.
208    ///
209    /// # Returns
210    ///
211    /// * `Ok(Adapter)` for the software TCP stack.
212    /// * `Err(IdeviceError)` if IP parsing or socket extraction fails.
213    #[cfg(feature = "tunnel_tcp_stack")]
214    pub fn create_software_tunnel(self) -> Result<crate::tcp::adapter::Adapter, IdeviceError> {
215        let our_ip = self
216            .handshake
217            .client_parameters
218            .address
219            .parse::<std::net::IpAddr>()?;
220        let their_ip = self.handshake.server_address.parse::<std::net::IpAddr>()?;
221        Ok(crate::tcp::adapter::Adapter::new(
222            Box::new(self.idevice.socket.unwrap()),
223            our_ip,
224            their_ip,
225        ))
226    }
227}