1use bytes::{Buf, BufMut, Bytes, BytesMut};
2use indexmap::IndexMap;
3
4pub const XPC_MAGIC: u32 = 0x29B00B92;
6pub const XPC_BODY_MAGIC: u32 = 0x42133742;
7pub const XPC_ALWAYS_SET: u32 = 0x00000001;
8pub const XPC_DATA_LEN_OFFSET: u32 = 0x00000002; #[derive(Debug, Clone, PartialEq)]
12pub enum XpcValue {
13 Null,
14 Bool(bool),
15 Int64(i64),
16 Uint64(u64),
17 Double(f64),
18 Date(i64),
19 Data(Bytes),
20 String(String),
21 Uuid([u8; 16]),
22 Array(Vec<XpcValue>),
23 Dictionary(IndexMap<String, XpcValue>),
24}
25
26#[derive(Debug, Clone)]
28pub struct XpcMessage {
29 pub flags: u32,
30 pub msg_id: u64,
31 pub body: Option<XpcValue>,
32}
33
34#[derive(Debug, thiserror::Error)]
35pub enum XpcError {
36 #[error("buffer too short")]
37 BufferTooShort,
38 #[error("bad magic: expected 0x{expected:08X}, got 0x{got:08X}")]
39 BadMagic { expected: u32, got: u32 },
40 #[error("unknown XPC type: 0x{0:08X}")]
41 UnknownType(u32),
42 #[error("invalid UTF-8 in string")]
43 InvalidUtf8,
44}
45
46const TYPE_NULL: u32 = 0x00001000;
48const TYPE_BOOL: u32 = 0x00002000;
49const TYPE_INT64: u32 = 0x00003000;
50const TYPE_UINT64: u32 = 0x00004000;
51const TYPE_DOUBLE: u32 = 0x00005000;
52const TYPE_DATE: u32 = 0x00007000;
53const TYPE_DATA: u32 = 0x00008000;
54const TYPE_STRING: u32 = 0x00009000;
55const TYPE_UUID: u32 = 0x0000A000;
56const TYPE_ARRAY: u32 = 0x0000E000;
57const TYPE_DICTIONARY: u32 = 0x0000F000;
58
59pub fn encode_xpc(msg: &XpcMessage) -> Bytes {
61 let mut buf = BytesMut::new();
62 buf.put_u32_le(XPC_MAGIC);
63 buf.put_u32_le(msg.flags);
64 buf.put_u64_le(msg.msg_id);
65
66 if let Some(body) = &msg.body {
67 let body_bytes = encode_body(body);
68 buf.put_u32_le(body_bytes.len() as u32);
69 buf.put_slice(&body_bytes);
70 } else {
71 buf.put_u32_le(0);
72 }
73
74 buf.freeze()
75}
76
77fn encode_body(value: &XpcValue) -> BytesMut {
78 let mut buf = BytesMut::new();
79 buf.put_u32_le(XPC_BODY_MAGIC);
80 encode_value(value, &mut buf);
81 buf
82}
83
84fn encode_value(value: &XpcValue, buf: &mut BytesMut) {
85 match value {
86 XpcValue::Null => {
87 buf.put_u32_le(TYPE_NULL);
88 buf.put_u32_le(0);
89 }
90 XpcValue::Bool(b) => {
91 buf.put_u32_le(TYPE_BOOL);
92 buf.put_u32_le(4);
93 buf.put_u32_le(if *b { 1 } else { 0 });
94 }
96 XpcValue::Int64(n) => {
97 buf.put_u32_le(TYPE_INT64);
98 buf.put_u32_le(8);
99 buf.put_i64_le(*n);
100 }
101 XpcValue::Uint64(n) => {
102 buf.put_u32_le(TYPE_UINT64);
103 buf.put_u32_le(8);
104 buf.put_u64_le(*n);
105 }
106 XpcValue::Double(f) => {
107 buf.put_u32_le(TYPE_DOUBLE);
108 buf.put_u32_le(8);
109 buf.put_f64_le(*f);
110 }
111 XpcValue::Date(n) => {
112 buf.put_u32_le(TYPE_DATE);
113 buf.put_u32_le(8);
114 buf.put_i64_le(*n);
115 }
116 XpcValue::Data(d) => {
117 buf.put_u32_le(TYPE_DATA);
118 let padded = (d.len() + 3) & !3;
119 buf.put_u32_le(d.len() as u32);
120 buf.put_slice(d);
121 for _ in d.len()..padded {
122 buf.put_u8(0);
123 }
124 }
125 XpcValue::String(s) => {
126 buf.put_u32_le(TYPE_STRING);
127 let bytes = s.as_bytes();
128 let total = bytes.len() + 1; let padded = (total + 3) & !3;
130 buf.put_u32_le(total as u32);
131 buf.put_slice(bytes);
132 for _ in bytes.len()..padded {
133 buf.put_u8(0);
134 }
135 }
136 XpcValue::Uuid(u) => {
137 buf.put_u32_le(TYPE_UUID);
138 buf.put_u32_le(16);
139 buf.put_slice(u);
140 }
141 XpcValue::Array(arr) => {
142 buf.put_u32_le(TYPE_ARRAY);
143 let len_offset = buf.len();
144 buf.put_u32_le(0); let start = buf.len();
146 buf.put_u32_le(arr.len() as u32);
147 for v in arr {
148 encode_value(v, buf);
149 }
150 let len = (buf.len() - start) as u32;
151 buf[len_offset..len_offset + 4].copy_from_slice(&len.to_le_bytes());
152 }
153 XpcValue::Dictionary(map) => {
154 buf.put_u32_le(TYPE_DICTIONARY);
155 let len_offset = buf.len();
156 buf.put_u32_le(0); let start = buf.len();
158 buf.put_u32_le(map.len() as u32);
159 for (k, v) in map {
160 encode_value(&XpcValue::String(k.clone()), buf);
161 encode_value(v, buf);
162 }
163 let len = (buf.len() - start) as u32;
164 buf[len_offset..len_offset + 4].copy_from_slice(&len.to_le_bytes());
165 }
166 }
167}
168
169pub fn decode_xpc(buf: &mut Bytes) -> Result<XpcMessage, XpcError> {
171 if buf.remaining() < 16 {
172 return Err(XpcError::BufferTooShort);
173 }
174 let magic = buf.get_u32_le();
175 if magic != XPC_MAGIC {
176 return Err(XpcError::BadMagic {
177 expected: XPC_MAGIC,
178 got: magic,
179 });
180 }
181 let flags = buf.get_u32_le();
182 let msg_id = buf.get_u64_le();
183
184 if buf.remaining() < 4 {
185 return Err(XpcError::BufferTooShort);
186 }
187 let body_len = buf.get_u32_le() as usize;
188
189 let body = if body_len > 0 {
190 if buf.remaining() < body_len {
191 return Err(XpcError::BufferTooShort);
192 }
193 let body_magic = buf.get_u32_le();
194 if body_magic != XPC_BODY_MAGIC {
195 return Err(XpcError::BadMagic {
196 expected: XPC_BODY_MAGIC,
197 got: body_magic,
198 });
199 }
200 Some(decode_value(buf)?)
201 } else {
202 None
203 };
204
205 Ok(XpcMessage {
206 flags,
207 msg_id,
208 body,
209 })
210}
211
212fn decode_value(buf: &mut Bytes) -> Result<XpcValue, XpcError> {
213 if buf.remaining() < 8 {
214 return Err(XpcError::BufferTooShort);
215 }
216 let type_tag = buf.get_u32_le();
217 let data_len = buf.get_u32_le() as usize;
218
219 match type_tag {
220 TYPE_NULL => Ok(XpcValue::Null),
221 TYPE_BOOL => {
222 if buf.remaining() < 4 {
223 return Err(XpcError::BufferTooShort);
224 }
225 let v = buf.get_u32_le();
226 Ok(XpcValue::Bool(v != 0))
227 }
228 TYPE_INT64 => {
229 if buf.remaining() < 8 {
230 return Err(XpcError::BufferTooShort);
231 }
232 Ok(XpcValue::Int64(buf.get_i64_le()))
233 }
234 TYPE_UINT64 => {
235 if buf.remaining() < 8 {
236 return Err(XpcError::BufferTooShort);
237 }
238 Ok(XpcValue::Uint64(buf.get_u64_le()))
239 }
240 TYPE_DOUBLE => {
241 if buf.remaining() < 8 {
242 return Err(XpcError::BufferTooShort);
243 }
244 Ok(XpcValue::Double(buf.get_f64_le()))
245 }
246 TYPE_DATE => {
247 if buf.remaining() < 8 {
248 return Err(XpcError::BufferTooShort);
249 }
250 Ok(XpcValue::Date(buf.get_i64_le()))
251 }
252 TYPE_DATA => {
253 let padded = (data_len + 3) & !3;
254 if buf.remaining() < padded {
255 return Err(XpcError::BufferTooShort);
256 }
257 let d = buf.copy_to_bytes(data_len);
258 let pad = padded - data_len;
260 if pad > 0 {
261 buf.advance(pad);
262 }
263 Ok(XpcValue::Data(d))
264 }
265 TYPE_STRING => {
266 let padded = (data_len + 3) & !3;
267 if buf.remaining() < padded {
268 return Err(XpcError::BufferTooShort);
269 }
270 let raw = buf.copy_to_bytes(data_len);
271 let pad = padded - data_len;
273 if pad > 0 {
274 buf.advance(pad);
275 }
276 let end = raw.iter().position(|&b| b == 0).unwrap_or(raw.len());
278 let s = std::str::from_utf8(&raw[..end]).map_err(|_| XpcError::InvalidUtf8)?;
279 Ok(XpcValue::String(s.to_string()))
280 }
281 TYPE_UUID => {
282 if buf.remaining() < 16 {
283 return Err(XpcError::BufferTooShort);
284 }
285 let mut u = [0u8; 16];
286 buf.copy_to_slice(&mut u);
287 Ok(XpcValue::Uuid(u))
288 }
289 TYPE_ARRAY => {
290 if buf.remaining() < 4 {
291 return Err(XpcError::BufferTooShort);
292 }
293 let count = buf.get_u32_le() as usize;
294 let mut arr = Vec::with_capacity(count);
295 for _ in 0..count {
296 arr.push(decode_value(buf)?);
297 }
298 Ok(XpcValue::Array(arr))
299 }
300 TYPE_DICTIONARY => {
301 if buf.remaining() < 4 {
302 return Err(XpcError::BufferTooShort);
303 }
304 let count = buf.get_u32_le() as usize;
305 let mut map = IndexMap::new();
306 for _ in 0..count {
307 let key = match decode_value(buf)? {
308 XpcValue::String(s) => s,
309 _ => return Err(XpcError::UnknownType(0)),
310 };
311 let val = decode_value(buf)?;
312 map.insert(key, val);
313 }
314 Ok(XpcValue::Dictionary(map))
315 }
316 other => Err(XpcError::UnknownType(other)),
317 }
318}