1use serde::{Deserialize, Serialize};
2use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
3
4use crate::mux::MuxError;
5
6const MAX_MUX_MESSAGE_SIZE: usize = 16 * 1024 * 1024;
7
8pub fn encode_message(payload: &[u8], tag: u32) -> Vec<u8> {
9 let total = 16 + payload.len();
10 let mut buf = Vec::with_capacity(total);
11 buf.extend_from_slice(&(total as u32).to_le_bytes());
12 buf.extend_from_slice(&1u32.to_le_bytes()); buf.extend_from_slice(&8u32.to_le_bytes()); buf.extend_from_slice(&tag.to_le_bytes());
15 buf.extend_from_slice(payload);
16 buf
17}
18
19pub async fn send_plist<W, T>(writer: &mut W, value: &T, tag: u32) -> Result<(), MuxError>
20where
21 W: AsyncWrite + Unpin,
22 T: Serialize,
23{
24 let mut plist_bytes = Vec::new();
25 plist::to_writer_xml(&mut plist_bytes, value).map_err(|e| MuxError::Protocol(e.to_string()))?;
26 let msg = encode_message(&plist_bytes, tag);
27 writer.write_all(&msg).await?;
28 writer.flush().await?;
29 Ok(())
30}
31
32pub async fn recv_plist<R, T>(reader: &mut R) -> Result<T, MuxError>
33where
34 R: AsyncRead + Unpin,
35 T: for<'de> Deserialize<'de>,
36{
37 let mut header = [0u8; 16];
38 reader.read_exact(&mut header).await?;
39 let length = u32::from_le_bytes(header[0..4].try_into().unwrap()) as usize;
40 if length < 16 {
41 return Err(MuxError::Protocol(format!(
42 "invalid message length: {length}"
43 )));
44 }
45 if length > MAX_MUX_MESSAGE_SIZE {
46 return Err(MuxError::Protocol(format!(
47 "message too large: {length} bytes exceeds {MAX_MUX_MESSAGE_SIZE}"
48 )));
49 }
50 let mut payload = vec![0u8; length - 16];
51 reader.read_exact(&mut payload).await?;
52 let value = plist::from_bytes(&payload).map_err(|e| MuxError::Protocol(e.to_string()))?;
53 Ok(value)
54}
55
56#[derive(Serialize)]
59#[serde(rename_all = "PascalCase")]
60pub(crate) struct ListDevicesRequest {
61 pub message_type: &'static str,
62 pub prog_name: &'static str,
63 pub client_version_string: &'static str,
64}
65
66#[derive(Debug, Deserialize)]
67#[serde(rename_all = "PascalCase")]
68pub(crate) struct DeviceList {
69 pub device_list: Vec<DeviceEntryRaw>,
70}
71
72#[derive(Debug, Deserialize, Clone)]
73#[serde(rename_all = "PascalCase")]
74pub(crate) struct DeviceEntryRaw {
75 #[serde(rename = "DeviceID")]
76 pub device_id: u32,
77 pub properties: DevicePropertiesRaw,
78}
79
80#[derive(Debug, Deserialize, Clone)]
81#[serde(rename_all = "PascalCase")]
82pub(crate) struct DevicePropertiesRaw {
83 pub serial_number: String,
84 pub connection_type: String,
85 pub product_id: Option<u16>,
86}
87
88#[derive(Serialize)]
91#[serde(rename_all = "PascalCase")]
92pub(crate) struct ReadPairRecordRequest {
93 pub message_type: &'static str,
94 pub prog_name: &'static str,
95 pub client_version_string: &'static str,
96 pub bundle_id: &'static str,
97 #[serde(rename = "kLibUSBMuxVersion")]
98 pub lib_usbmux_version: u32,
99 #[serde(rename = "PairRecordID")]
100 pub pair_record_id: String,
101}
102
103#[derive(Serialize)]
106#[serde(rename_all = "PascalCase")]
107pub(crate) struct ReadBuidRequest {
108 pub message_type: &'static str,
109 pub prog_name: &'static str,
110 pub client_version_string: &'static str,
111 pub bundle_id: &'static str,
112 #[serde(rename = "kLibUSBMuxVersion")]
113 pub lib_usbmux_version: u32,
114}
115
116#[derive(Debug, Deserialize)]
117#[serde(rename_all = "PascalCase")]
118pub(crate) struct ReadBuidResponse {
119 #[serde(rename = "BUID")]
120 pub buid: String,
121}
122
123#[derive(Serialize)]
126#[serde(rename_all = "PascalCase")]
127pub(crate) struct ConnectRequest {
128 pub message_type: &'static str,
129 pub prog_name: &'static str,
130 pub client_version_string: &'static str,
131 pub bundle_id: &'static str,
132 #[serde(rename = "kLibUSBMuxVersion")]
133 pub lib_usbmux_version: u32,
134 #[serde(rename = "DeviceID")]
135 pub device_id: u32,
136 pub port_number: u16,
137}
138
139#[derive(Debug, Deserialize)]
140#[serde(rename_all = "PascalCase")]
141pub(crate) struct ConnectResponse {
142 #[allow(dead_code)]
143 pub message_type: String,
144 pub number: u32,
145}
146
147#[derive(Serialize)]
150#[serde(rename_all = "PascalCase")]
151pub(crate) struct ListenRequest {
152 pub message_type: &'static str,
153 pub prog_name: &'static str,
154 pub client_version_string: &'static str,
155}
156
157#[derive(Debug, Deserialize)]
158#[serde(rename_all = "PascalCase")]
159pub(crate) struct DeviceEvent {
160 pub message_type: String,
161 #[serde(rename = "DeviceID")]
162 pub device_id: u32,
163 pub properties: Option<DevicePropertiesRaw>,
164}
165
166#[cfg(test)]
167mod tests {
168 use super::*;
169
170 #[tokio::test]
171 async fn recv_plist_rejects_oversized_message() {
172 let mut header = [0u8; 16];
173 header[..4].copy_from_slice(&((MAX_MUX_MESSAGE_SIZE as u32) + 1).to_le_bytes());
174 let mut cursor = std::io::Cursor::new(header);
175
176 let err = recv_plist::<_, plist::Value>(&mut cursor)
177 .await
178 .unwrap_err();
179 assert!(
180 matches!(err, MuxError::Protocol(message) if message.contains("message too large"))
181 );
182 }
183}