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}