Skip to main content

ios_core/mux/
protocol.rs

1use serde::{Deserialize, Serialize};
2use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
3
4use crate::mux::MuxError;
5
6pub fn encode_message(payload: &[u8], tag: u32) -> Vec<u8> {
7    let total = 16 + payload.len();
8    let mut buf = Vec::with_capacity(total);
9    buf.extend_from_slice(&(total as u32).to_le_bytes());
10    buf.extend_from_slice(&1u32.to_le_bytes()); // version
11    buf.extend_from_slice(&8u32.to_le_bytes()); // type = plist
12    buf.extend_from_slice(&tag.to_le_bytes());
13    buf.extend_from_slice(payload);
14    buf
15}
16
17pub async fn send_plist<W, T>(writer: &mut W, value: &T, tag: u32) -> Result<(), MuxError>
18where
19    W: AsyncWrite + Unpin,
20    T: Serialize,
21{
22    let mut plist_bytes = Vec::new();
23    plist::to_writer_xml(&mut plist_bytes, value).map_err(|e| MuxError::Protocol(e.to_string()))?;
24    let msg = encode_message(&plist_bytes, tag);
25    writer.write_all(&msg).await?;
26    writer.flush().await?;
27    Ok(())
28}
29
30pub async fn recv_plist<R, T>(reader: &mut R) -> Result<T, MuxError>
31where
32    R: AsyncRead + Unpin,
33    T: for<'de> Deserialize<'de>,
34{
35    let mut header = [0u8; 16];
36    reader.read_exact(&mut header).await?;
37    let length = u32::from_le_bytes(header[0..4].try_into().unwrap()) as usize;
38    if length < 16 {
39        return Err(MuxError::Protocol(format!(
40            "invalid message length: {length}"
41        )));
42    }
43    let mut payload = vec![0u8; length - 16];
44    reader.read_exact(&mut payload).await?;
45    let value = plist::from_bytes(&payload).map_err(|e| MuxError::Protocol(e.to_string()))?;
46    Ok(value)
47}
48
49// ── Device discovery messages ────────────────────────
50
51#[derive(Serialize)]
52#[serde(rename_all = "PascalCase")]
53pub(crate) struct ListDevicesRequest {
54    pub message_type: &'static str,
55    pub prog_name: &'static str,
56    pub client_version_string: &'static str,
57}
58
59#[derive(Debug, Deserialize)]
60#[serde(rename_all = "PascalCase")]
61pub(crate) struct DeviceList {
62    pub device_list: Vec<DeviceEntryRaw>,
63}
64
65#[derive(Debug, Deserialize, Clone)]
66#[serde(rename_all = "PascalCase")]
67pub(crate) struct DeviceEntryRaw {
68    #[serde(rename = "DeviceID")]
69    pub device_id: u32,
70    pub properties: DevicePropertiesRaw,
71}
72
73#[derive(Debug, Deserialize, Clone)]
74#[serde(rename_all = "PascalCase")]
75pub(crate) struct DevicePropertiesRaw {
76    pub serial_number: String,
77    pub connection_type: String,
78    pub product_id: Option<u16>,
79}
80
81// ── ReadPairRecord ───────────────────────────────────
82
83#[derive(Serialize)]
84#[serde(rename_all = "PascalCase")]
85pub(crate) struct ReadPairRecordRequest {
86    pub message_type: &'static str,
87    pub prog_name: &'static str,
88    pub client_version_string: &'static str,
89    pub bundle_id: &'static str,
90    #[serde(rename = "kLibUSBMuxVersion")]
91    pub lib_usbmux_version: u32,
92    #[serde(rename = "PairRecordID")]
93    pub pair_record_id: String,
94}
95
96// ── ReadBUID ─────────────────────────────────────────
97
98#[derive(Serialize)]
99#[serde(rename_all = "PascalCase")]
100pub(crate) struct ReadBuidRequest {
101    pub message_type: &'static str,
102    pub prog_name: &'static str,
103    pub client_version_string: &'static str,
104    pub bundle_id: &'static str,
105    #[serde(rename = "kLibUSBMuxVersion")]
106    pub lib_usbmux_version: u32,
107}
108
109#[derive(Debug, Deserialize)]
110#[serde(rename_all = "PascalCase")]
111pub(crate) struct ReadBuidResponse {
112    #[serde(rename = "BUID")]
113    pub buid: String,
114}
115
116// ── Connect messages ─────────────────────────────────
117
118#[derive(Serialize)]
119#[serde(rename_all = "PascalCase")]
120pub(crate) struct ConnectRequest {
121    pub message_type: &'static str,
122    pub prog_name: &'static str,
123    pub client_version_string: &'static str,
124    pub bundle_id: &'static str,
125    #[serde(rename = "kLibUSBMuxVersion")]
126    pub lib_usbmux_version: u32,
127    #[serde(rename = "DeviceID")]
128    pub device_id: u32,
129    pub port_number: u16,
130}
131
132#[derive(Debug, Deserialize)]
133#[serde(rename_all = "PascalCase")]
134pub(crate) struct ConnectResponse {
135    #[allow(dead_code)]
136    pub message_type: String,
137    pub number: u32,
138}
139
140// ── Listen message ───────────────────────────────────
141
142#[derive(Serialize)]
143#[serde(rename_all = "PascalCase")]
144pub(crate) struct ListenRequest {
145    pub message_type: &'static str,
146    pub prog_name: &'static str,
147    pub client_version_string: &'static str,
148}
149
150#[derive(Debug, Deserialize)]
151#[serde(rename_all = "PascalCase")]
152pub(crate) struct DeviceEvent {
153    pub message_type: String,
154    #[serde(rename = "DeviceID")]
155    pub device_id: u32,
156    pub properties: Option<DevicePropertiesRaw>,
157}