1use anyhow::{anyhow, bail, ensure, Context, Result};
20use tokio::io::{AsyncRead, AsyncReadExt};
21
22pub const MAGIC: [u8; 4] = *b"REDQ";
24pub const VERSION_MAJOR: u32 = 2;
25pub const VERSION_MINOR: u32 = 2;
26pub const LINK_HEADER_BYTES: usize = 16;
28pub const LINK_MESS_BYTES: usize = 18;
30pub const LINK_REPLY_BYTES: usize = 4 + TICKET_PUBKEY_BYTES + 12;
32pub const TICKET_PUBKEY_BYTES: usize = 162;
34pub const TICKET_BYTES: usize = 128;
36pub const MAX_PASSWORD_LENGTH: usize = 60;
38pub const MAX_LINK_BYTES: u32 = 4096;
41pub const MAX_CAP_WORDS: u32 = 64;
44pub const MINI_HEADER_BYTES: usize = 6;
45pub const FULL_HEADER_BYTES: usize = 18;
46
47pub mod common_cap {
49 pub const AUTH_SELECTION: u32 = 0;
50 pub const AUTH_SPICE: u32 = 1;
51 pub const AUTH_SASL: u32 = 2;
52 pub const MINI_HEADER: u32 = 3;
53}
54
55pub const AUTH_MECHANISM_SPICE: u32 = 1;
57
58pub mod main_cap {
60 pub const NAME_AND_UUID: u32 = 1;
61}
62
63pub mod display_cap {
65 pub const PREF_COMPRESSION: u32 = 6;
66}
67
68pub mod channel {
70 pub const MAIN: u8 = 1;
71 pub const DISPLAY: u8 = 2;
72 pub const INPUTS: u8 = 3;
73 pub const CURSOR: u8 = 4;
74 pub const PLAYBACK: u8 = 5;
75 pub const RECORD: u8 = 6;
76
77 pub fn name(t: u8) -> &'static str {
78 match t {
79 MAIN => "main",
80 DISPLAY => "display",
81 INPUTS => "inputs",
82 CURSOR => "cursor",
83 PLAYBACK => "playback",
84 RECORD => "record",
85 8 => "smartcard",
86 9 => "usbredir",
87 10 => "port",
88 11 => "webdav",
89 _ => "unknown",
90 }
91 }
92}
93
94pub mod link_err {
96 pub const OK: u32 = 0;
97 pub const ERROR: u32 = 1;
98 pub const INVALID_MAGIC: u32 = 2;
99 pub const INVALID_DATA: u32 = 3;
100 pub const VERSION_MISMATCH: u32 = 4;
101 pub const NEED_SECURED: u32 = 5;
102 pub const NEED_UNSECURED: u32 = 6;
103 pub const PERMISSION_DENIED: u32 = 7;
104 pub const BAD_CONNECTION_ID: u32 = 8;
105 pub const CHANNEL_NOT_AVAILABLE: u32 = 9;
106
107 pub fn name(code: u32) -> &'static str {
108 match code {
109 OK => "ok",
110 ERROR => "error",
111 INVALID_MAGIC => "invalid magic",
112 INVALID_DATA => "invalid data",
113 VERSION_MISMATCH => "version mismatch",
114 NEED_SECURED => "the server requires TLS",
115 NEED_UNSECURED => "the server requires a plain connection",
116 PERMISSION_DENIED => "permission denied",
117 BAD_CONNECTION_ID => "bad connection id",
118 CHANNEL_NOT_AVAILABLE => "channel not available",
119 _ => "unknown link error",
120 }
121 }
122}
123
124pub mod msg {
126 pub const SET_ACK: u16 = 3;
127 pub const PING: u16 = 4;
128 pub const WAIT_FOR_CHANNELS: u16 = 5;
129 pub const DISCONNECTING: u16 = 6;
130 pub const NOTIFY: u16 = 7;
131
132 pub const MAIN_INIT: u16 = 103;
133 pub const MAIN_CHANNELS_LIST: u16 = 104;
134 pub const MAIN_MOUSE_MODE: u16 = 105;
135 pub const MAIN_MULTI_MEDIA_TIME: u16 = 106;
136 pub const MAIN_AGENT_CONNECTED: u16 = 107;
137 pub const MAIN_AGENT_DISCONNECTED: u16 = 108;
138 pub const MAIN_NAME: u16 = 113;
139 pub const MAIN_UUID: u16 = 114;
140
141 pub const DISPLAY_MODE: u16 = 101;
142 pub const DISPLAY_MARK: u16 = 102;
143 pub const DISPLAY_RESET: u16 = 103;
144 pub const DISPLAY_COPY_BITS: u16 = 104;
145 pub const DISPLAY_INVAL_LIST: u16 = 105;
146 pub const DISPLAY_INVAL_ALL_PIXMAPS: u16 = 106;
147 pub const DISPLAY_INVAL_PALETTE: u16 = 107;
148 pub const DISPLAY_INVAL_ALL_PALETTES: u16 = 108;
149 pub const DISPLAY_STREAM_CREATE: u16 = 122;
150 pub const DISPLAY_STREAM_DATA: u16 = 123;
151 pub const DISPLAY_STREAM_CLIP: u16 = 124;
152 pub const DISPLAY_STREAM_DESTROY: u16 = 125;
153 pub const DISPLAY_STREAM_DESTROY_ALL: u16 = 126;
154 pub const DISPLAY_DRAW_FILL: u16 = 302;
155 pub const DISPLAY_DRAW_OPAQUE: u16 = 303;
156 pub const DISPLAY_DRAW_COPY: u16 = 304;
157 pub const DISPLAY_DRAW_BLEND: u16 = 305;
158 pub const DISPLAY_DRAW_BLACKNESS: u16 = 306;
159 pub const DISPLAY_DRAW_WHITENESS: u16 = 307;
160 pub const DISPLAY_DRAW_INVERS: u16 = 308;
161 pub const DISPLAY_DRAW_ROP3: u16 = 309;
162 pub const DISPLAY_DRAW_STROKE: u16 = 310;
163 pub const DISPLAY_DRAW_TEXT: u16 = 311;
164 pub const DISPLAY_DRAW_TRANSPARENT: u16 = 312;
165 pub const DISPLAY_DRAW_ALPHA_BLEND: u16 = 313;
166 pub const DISPLAY_SURFACE_CREATE: u16 = 314;
167 pub const DISPLAY_SURFACE_DESTROY: u16 = 315;
168 pub const DISPLAY_STREAM_DATA_SIZED: u16 = 316;
169 pub const DISPLAY_MONITORS_CONFIG: u16 = 317;
170 pub const DISPLAY_DRAW_COMPOSITE: u16 = 318;
171
172 pub const INPUTS_INIT: u16 = 101;
173 pub const INPUTS_KEY_MODIFIERS: u16 = 102;
174 pub const INPUTS_MOUSE_MOTION_ACK: u16 = 111;
175}
176
177pub mod msgc {
179 pub const ACK_SYNC: u16 = 1;
180 pub const ACK: u16 = 2;
181 pub const PONG: u16 = 3;
182 pub const DISCONNECTING: u16 = 6;
183
184 pub const MAIN_CLIENT_INFO: u16 = 101;
185 pub const MAIN_ATTACH_CHANNELS: u16 = 104;
186 pub const MAIN_MOUSE_MODE_REQUEST: u16 = 105;
187 pub const MAIN_AGENT_START: u16 = 106;
188 pub const MAIN_AGENT_DATA: u16 = 107;
189 pub const MAIN_AGENT_TOKEN: u16 = 108;
190
191 pub const DISPLAY_INIT: u16 = 101;
192 pub const DISPLAY_STREAM_REPORT: u16 = 102;
193 pub const DISPLAY_PREFERRED_COMPRESSION: u16 = 103;
194
195 pub const INPUTS_KEY_DOWN: u16 = 101;
196 pub const INPUTS_KEY_UP: u16 = 102;
197 pub const INPUTS_KEY_MODIFIERS: u16 = 103;
198 pub const INPUTS_KEY_SCANCODE: u16 = 104;
199 pub const INPUTS_MOUSE_MOTION: u16 = 111;
200 pub const INPUTS_MOUSE_POSITION: u16 = 112;
201 pub const INPUTS_MOUSE_PRESS: u16 = 113;
202 pub const INPUTS_MOUSE_RELEASE: u16 = 114;
203}
204
205pub const MOUSE_MODE_SERVER: u32 = 1;
207pub const MOUSE_MODE_CLIENT: u32 = 2;
208
209pub mod button {
211 pub const LEFT: u8 = 1;
212 pub const MIDDLE: u8 = 2;
213 pub const RIGHT: u8 = 3;
214 pub const UP: u8 = 4;
215 pub const DOWN: u8 = 5;
216
217 pub fn name(b: u8) -> &'static str {
218 match b {
219 LEFT => "left",
220 MIDDLE => "middle",
221 RIGHT => "right",
222 UP => "scroll_up",
223 DOWN => "scroll_down",
224 _ => "other",
225 }
226 }
227
228 pub fn mask(b: u8) -> u16 {
230 match b {
231 LEFT => 1,
232 MIDDLE => 2,
233 RIGHT => 4,
234 _ => 0,
235 }
236 }
237
238 pub fn names_in(state: u16) -> Vec<&'static str> {
239 [(1u16, "left"), (2, "middle"), (4, "right")]
240 .iter()
241 .filter(|(bit, _)| state & bit != 0)
242 .map(|(_, n)| *n)
243 .collect()
244 }
245}
246
247pub const SURFACE_FMT_32_XRGB: u32 = 32;
249pub const SURFACE_FLAGS_PRIMARY: u32 = 1;
250
251pub mod image_type {
253 pub const BITMAP: u8 = 0;
254 pub const QUIC: u8 = 1;
255 pub const LZ_PLT: u8 = 100;
256 pub const LZ_RGB: u8 = 101;
257 pub const GLZ_RGB: u8 = 102;
258 pub const FROM_CACHE: u8 = 103;
259 pub const SURFACE: u8 = 104;
260 pub const JPEG: u8 = 105;
261 pub const FROM_CACHE_LOSSLESS: u8 = 106;
262 pub const ZLIB_GLZ_RGB: u8 = 107;
263 pub const JPEG_ALPHA: u8 = 108;
264 pub const LZ4: u8 = 109;
265
266 pub fn name(t: u8) -> &'static str {
267 match t {
268 BITMAP => "bitmap",
269 QUIC => "quic",
270 LZ_PLT => "lz_plt",
271 LZ_RGB => "lz_rgb",
272 GLZ_RGB => "glz_rgb",
273 FROM_CACHE => "from_cache",
274 SURFACE => "surface",
275 JPEG => "jpeg",
276 FROM_CACHE_LOSSLESS => "from_cache_lossless",
277 ZLIB_GLZ_RGB => "zlib_glz_rgb",
278 JPEG_ALPHA => "jpeg_alpha",
279 LZ4 => "lz4",
280 _ => "unknown",
281 }
282 }
283}
284
285pub const IMAGE_FLAGS_CACHE_ME: u8 = 1;
287pub const BITMAP_FMT_24BIT: u8 = 7;
289pub const BITMAP_FMT_32BIT: u8 = 8;
290pub const BITMAP_FMT_RGBA: u8 = 9;
291pub const BITMAP_FLAGS_PAL_FROM_CACHE: u8 = 2;
293pub const BITMAP_FLAGS_TOP_DOWN: u8 = 4;
294pub const ROPD_OP_PUT: u16 = 1 << 3;
296pub const SCALE_MODE_NEAREST: u8 = 1;
298pub const IMAGE_COMPRESSION_OFF: u8 = 1;
300
301pub const NOTIFY_SEVERITY_INFO: u32 = 0;
303pub const NOTIFY_SEVERITY_WARN: u32 = 1;
304pub const NOTIFY_SEVERITY_ERROR: u32 = 2;
305pub const NOTIFY_VISIBILITY_HIGH: u32 = 2;
306
307pub fn severity_name(s: u32) -> &'static str {
308 match s {
309 NOTIFY_SEVERITY_INFO => "info",
310 NOTIFY_SEVERITY_WARN => "warn",
311 NOTIFY_SEVERITY_ERROR => "error",
312 _ => "unknown",
313 }
314}
315
316pub struct Cursor<'a> {
323 data: &'a [u8],
324 pos: usize,
325}
326
327impl<'a> Cursor<'a> {
328 pub fn new(data: &'a [u8]) -> Self {
329 Self { data, pos: 0 }
330 }
331 pub fn at(data: &'a [u8], pos: usize) -> Self {
332 Self { data, pos }
333 }
334 pub fn pos(&self) -> usize {
335 self.pos
336 }
337 pub fn remaining(&self) -> usize {
338 self.data.len().saturating_sub(self.pos)
339 }
340 pub fn bytes(&mut self, n: usize, what: &str) -> Result<&'a [u8]> {
341 let end = self
342 .pos
343 .checked_add(n)
344 .filter(|e| *e <= self.data.len())
345 .ok_or_else(|| anyhow!("{what}: {n} bytes needed, {} left", self.remaining()))?;
346 let out = &self.data[self.pos..end];
347 self.pos = end;
348 Ok(out)
349 }
350 pub fn u8(&mut self, what: &str) -> Result<u8> {
351 Ok(self.bytes(1, what)?[0])
352 }
353 pub fn u16(&mut self, what: &str) -> Result<u16> {
354 let b = self.bytes(2, what)?;
355 Ok(u16::from_le_bytes([b[0], b[1]]))
356 }
357 pub fn u32(&mut self, what: &str) -> Result<u32> {
358 let b = self.bytes(4, what)?;
359 Ok(u32::from_le_bytes([b[0], b[1], b[2], b[3]]))
360 }
361 pub fn i32(&mut self, what: &str) -> Result<i32> {
362 Ok(self.u32(what)? as i32)
363 }
364 pub fn u64(&mut self, what: &str) -> Result<u64> {
365 let b = self.bytes(8, what)?;
366 let mut a = [0u8; 8];
367 a.copy_from_slice(b);
368 Ok(u64::from_le_bytes(a))
369 }
370}
371
372pub fn has_cap(caps: &[u32], bit: u32) -> bool {
374 caps.get((bit / 32) as usize)
375 .is_some_and(|w| w & (1 << (bit % 32)) != 0)
376}
377
378pub fn caps_word(bits: &[u32]) -> u32 {
380 bits.iter()
381 .filter(|b| **b < 32)
382 .fold(0, |w, b| w | (1 << b))
383}
384
385pub fn link_header(size: u32) -> Vec<u8> {
391 let mut v = Vec::with_capacity(LINK_HEADER_BYTES);
392 v.extend_from_slice(&MAGIC);
393 v.extend_from_slice(&VERSION_MAJOR.to_le_bytes());
394 v.extend_from_slice(&VERSION_MINOR.to_le_bytes());
395 v.extend_from_slice(&size.to_le_bytes());
396 v
397}
398
399#[derive(Debug, Clone, Copy, PartialEq, Eq)]
401pub struct LinkHeaderError(pub u32);
402
403pub fn parse_link_header(h: &[u8; LINK_HEADER_BYTES], min: usize) -> Result<u32, LinkHeaderError> {
406 if h[0..4] != MAGIC {
407 return Err(LinkHeaderError(link_err::INVALID_MAGIC));
408 }
409 let major = u32::from_le_bytes([h[4], h[5], h[6], h[7]]);
410 if major != VERSION_MAJOR {
411 return Err(LinkHeaderError(link_err::VERSION_MISMATCH));
412 }
413 let size = u32::from_le_bytes([h[12], h[13], h[14], h[15]]);
414 if (size as usize) < min || size > MAX_LINK_BYTES {
415 return Err(LinkHeaderError(link_err::INVALID_DATA));
416 }
417 Ok(size)
418}
419
420fn read_caps(body: &[u8], common: u32, channel: u32, offset: u32) -> Result<(Vec<u32>, Vec<u32>)> {
422 ensure!(
423 common <= MAX_CAP_WORDS && channel <= MAX_CAP_WORDS,
424 "{common} common and {channel} channel capability words; at most {MAX_CAP_WORDS} each"
425 );
426 let mut c = Cursor::at(body, offset as usize);
427 let mut read = |n: u32| -> Result<Vec<u32>> { (0..n).map(|_| c.u32("capability")).collect() };
428 let common = read(common)?;
429 let channel = read(channel)?;
430 Ok((common, channel))
431}
432
433#[derive(Debug, Clone, PartialEq, Eq)]
435pub struct LinkMess {
436 pub connection_id: u32,
437 pub channel_type: u8,
438 pub channel_id: u8,
439 pub common_caps: Vec<u32>,
440 pub channel_caps: Vec<u32>,
441}
442
443impl LinkMess {
444 pub fn encode(&self) -> Vec<u8> {
446 let caps = (self.common_caps.len() + self.channel_caps.len()) * 4;
447 let mut v = link_header((LINK_MESS_BYTES + caps) as u32);
448 v.extend_from_slice(&self.connection_id.to_le_bytes());
449 v.push(self.channel_type);
450 v.push(self.channel_id);
451 v.extend_from_slice(&(self.common_caps.len() as u32).to_le_bytes());
452 v.extend_from_slice(&(self.channel_caps.len() as u32).to_le_bytes());
453 v.extend_from_slice(&(LINK_MESS_BYTES as u32).to_le_bytes());
454 for w in self.common_caps.iter().chain(&self.channel_caps) {
455 v.extend_from_slice(&w.to_le_bytes());
456 }
457 v
458 }
459
460 pub fn decode(body: &[u8]) -> Result<Self> {
461 let mut c = Cursor::new(body);
462 let connection_id = c.u32("connection id")?;
463 let channel_type = c.u8("channel type")?;
464 let channel_id = c.u8("channel id")?;
465 let common = c.u32("common caps count")?;
466 let channel = c.u32("channel caps count")?;
467 let offset = c.u32("caps offset")?;
468 ensure!(
469 offset as usize >= LINK_MESS_BYTES,
470 "capabilities at offset {offset} overlap the link message"
471 );
472 let (common_caps, channel_caps) = read_caps(body, common, channel, offset)?;
473 Ok(Self {
474 connection_id,
475 channel_type,
476 channel_id,
477 common_caps,
478 channel_caps,
479 })
480 }
481}
482
483#[derive(Debug, Clone, PartialEq, Eq)]
485pub struct LinkReply {
486 pub error: u32,
487 pub pubkey: Vec<u8>,
488 pub common_caps: Vec<u32>,
489 pub channel_caps: Vec<u32>,
490}
491
492impl LinkReply {
493 pub fn encode(&self) -> Vec<u8> {
494 let caps = (self.common_caps.len() + self.channel_caps.len()) * 4;
495 let mut v = link_header((LINK_REPLY_BYTES + caps) as u32);
496 v.extend_from_slice(&self.error.to_le_bytes());
497 let mut key = [0u8; TICKET_PUBKEY_BYTES];
498 let n = self.pubkey.len().min(TICKET_PUBKEY_BYTES);
499 key[..n].copy_from_slice(&self.pubkey[..n]);
500 v.extend_from_slice(&key);
501 v.extend_from_slice(&(self.common_caps.len() as u32).to_le_bytes());
502 v.extend_from_slice(&(self.channel_caps.len() as u32).to_le_bytes());
503 v.extend_from_slice(&(LINK_REPLY_BYTES as u32).to_le_bytes());
504 for w in self.common_caps.iter().chain(&self.channel_caps) {
505 v.extend_from_slice(&w.to_le_bytes());
506 }
507 v
508 }
509
510 pub fn error(code: u32) -> Vec<u8> {
512 Self {
513 error: code,
514 pubkey: Vec::new(),
515 common_caps: Vec::new(),
516 channel_caps: Vec::new(),
517 }
518 .encode()
519 }
520
521 pub fn decode(body: &[u8]) -> Result<Self> {
522 let mut c = Cursor::new(body);
523 let error = c.u32("link error")?;
524 if error != link_err::OK {
525 return Ok(Self {
526 error,
527 pubkey: Vec::new(),
528 common_caps: Vec::new(),
529 channel_caps: Vec::new(),
530 });
531 }
532 let pubkey = c.bytes(TICKET_PUBKEY_BYTES, "public key")?.to_vec();
533 let common = c.u32("common caps count")?;
534 let channel = c.u32("channel caps count")?;
535 let offset = c.u32("caps offset")?;
536 ensure!(
537 offset as usize >= LINK_REPLY_BYTES,
538 "capabilities at offset {offset} overlap the link reply"
539 );
540 let (common_caps, channel_caps) = read_caps(body, common, channel, offset)?;
541 Ok(Self {
542 error,
543 pubkey,
544 common_caps,
545 channel_caps,
546 })
547 }
548}
549
550pub fn generate_ticket_key() -> Result<rsa::RsaPrivateKey> {
556 rsa::RsaPrivateKey::new(&mut rand::rngs::OsRng, 1024).context("generating the ticket key")
557}
558
559pub fn ticket_public_key(key: &rsa::RsaPrivateKey) -> Result<Vec<u8>> {
561 use rsa::pkcs8::EncodePublicKey;
562 let der = key
563 .to_public_key()
564 .to_public_key_der()
565 .context("encoding the ticket key")?;
566 let bytes = der.as_bytes().to_vec();
567 ensure!(
568 bytes.len() == TICKET_PUBKEY_BYTES,
569 "the ticket key encodes to {} bytes, not {TICKET_PUBKEY_BYTES}",
570 bytes.len()
571 );
572 Ok(bytes)
573}
574
575pub fn encrypt_ticket(pubkey_der: &[u8], password: &str) -> Result<Vec<u8>> {
577 use rsa::pkcs8::DecodePublicKey;
578 ensure!(
579 password.len() <= MAX_PASSWORD_LENGTH,
580 "a SPICE password is at most {MAX_PASSWORD_LENGTH} bytes"
581 );
582 let key = rsa::RsaPublicKey::from_public_key_der(pubkey_der)
583 .context("the server's ticket key is not an RSA SubjectPublicKeyInfo")?;
584 let mut plain = password.as_bytes().to_vec();
585 plain.push(0);
586 key.encrypt(
587 &mut rand::rngs::OsRng,
588 rsa::Oaep::new::<sha1::Sha1>(),
589 &plain,
590 )
591 .context("encrypting the ticket")
592}
593
594pub fn decrypt_ticket(key: &rsa::RsaPrivateKey, ticket: &[u8]) -> Option<Vec<u8>> {
597 let plain = key.decrypt(rsa::Oaep::new::<sha1::Sha1>(), ticket).ok()?;
598 let end = plain.iter().position(|b| *b == 0).unwrap_or(plain.len());
599 Some(plain[..end].to_vec())
600}
601
602pub fn secrets_equal(a: &[u8], b: &[u8]) -> bool {
604 let mut diff = u8::from(a.len() != b.len());
605 for i in 0..a.len().max(b.len()) {
606 diff |= a.get(i).copied().unwrap_or(0) ^ b.get(i).copied().unwrap_or(0);
607 }
608 diff == 0
609}
610
611pub fn data_header(mini: bool, serial: u64, msg_type: u16, size: u32) -> Vec<u8> {
617 let mut v = Vec::with_capacity(FULL_HEADER_BYTES);
618 if !mini {
619 v.extend_from_slice(&serial.to_le_bytes());
620 }
621 v.extend_from_slice(&msg_type.to_le_bytes());
622 v.extend_from_slice(&size.to_le_bytes());
623 if !mini {
624 v.extend_from_slice(&0u32.to_le_bytes());
625 }
626 v
627}
628
629#[derive(Debug)]
631pub enum ReadError {
632 TooLarge {
634 msg_type: u16,
635 size: u32,
636 max: u32,
637 },
638 SubList,
640 Io(std::io::Error),
641}
642
643impl std::fmt::Display for ReadError {
644 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
645 match self {
646 ReadError::TooLarge {
647 msg_type,
648 size,
649 max,
650 } => write!(
651 f,
652 "message type {msg_type} declares {size} bytes, above the {max} byte limit"
653 ),
654 ReadError::SubList => write!(f, "a message carried a sub-message list"),
655 ReadError::Io(e) => write!(f, "{e}"),
656 }
657 }
658}
659
660impl std::error::Error for ReadError {}
661
662impl From<std::io::Error> for ReadError {
663 fn from(e: std::io::Error) -> Self {
664 ReadError::Io(e)
665 }
666}
667
668pub async fn read_message<R: AsyncRead + Unpin>(
671 r: &mut R,
672 mini: bool,
673 max: u32,
674) -> std::result::Result<(u16, Vec<u8>), ReadError> {
675 let mut h = [0u8; FULL_HEADER_BYTES];
676 let n = if mini {
677 MINI_HEADER_BYTES
678 } else {
679 FULL_HEADER_BYTES
680 };
681 r.read_exact(&mut h[..n]).await?;
682 let at = if mini { 0 } else { 8 };
683 let msg_type = u16::from_le_bytes([h[at], h[at + 1]]);
684 let size = u32::from_le_bytes([h[at + 2], h[at + 3], h[at + 4], h[at + 5]]);
685 if !mini && u32::from_le_bytes([h[14], h[15], h[16], h[17]]) != 0 {
686 return Err(ReadError::SubList);
687 }
688 if size > max {
689 return Err(ReadError::TooLarge {
690 msg_type,
691 size,
692 max,
693 });
694 }
695 let mut body = vec![0u8; size as usize];
696 r.read_exact(&mut body).await?;
697 Ok((msg_type, body))
698}
699
700#[allow(clippy::too_many_arguments)]
706pub fn main_init(
707 session_id: u32,
708 display_channels_hint: u32,
709 supported_mouse_modes: u32,
710 current_mouse_mode: u32,
711 agent_connected: u32,
712 agent_tokens: u32,
713 multi_media_time: u32,
714 ram_hint: u32,
715) -> Vec<u8> {
716 [
717 session_id,
718 display_channels_hint,
719 supported_mouse_modes,
720 current_mouse_mode,
721 agent_connected,
722 agent_tokens,
723 multi_media_time,
724 ram_hint,
725 ]
726 .iter()
727 .flat_map(|w| w.to_le_bytes())
728 .collect()
729}
730
731pub fn channels_list(channels: &[(u8, u8)]) -> Vec<u8> {
733 let mut v = (channels.len() as u32).to_le_bytes().to_vec();
734 for (t, id) in channels {
735 v.push(*t);
736 v.push(*id);
737 }
738 v
739}
740
741pub fn mouse_mode(supported: u16, current: u16) -> Vec<u8> {
743 let mut v = supported.to_le_bytes().to_vec();
744 v.extend_from_slice(¤t.to_le_bytes());
745 v
746}
747
748pub fn main_name(name: &str) -> Vec<u8> {
750 let mut v = ((name.len() + 1) as u32).to_le_bytes().to_vec();
751 v.extend_from_slice(name.as_bytes());
752 v.push(0);
753 v
754}
755
756pub fn notify(time_stamp: u64, severity: u32, what: u32, message: &str) -> Vec<u8> {
758 let mut v = time_stamp.to_le_bytes().to_vec();
759 v.extend_from_slice(&severity.to_le_bytes());
760 v.extend_from_slice(&NOTIFY_VISIBILITY_HIGH.to_le_bytes());
761 v.extend_from_slice(&what.to_le_bytes());
762 v.extend_from_slice(&(message.len() as u32).to_le_bytes());
763 v.extend_from_slice(message.as_bytes());
764 v.push(0);
765 v
766}
767
768pub fn surface_create(
770 surface_id: u32,
771 width: u32,
772 height: u32,
773 format: u32,
774 flags: u32,
775) -> Vec<u8> {
776 [surface_id, width, height, format, flags]
777 .iter()
778 .flat_map(|w| w.to_le_bytes())
779 .collect()
780}
781
782fn push_rect(v: &mut Vec<u8>, top: i32, left: i32, bottom: i32, right: i32) {
784 for x in [top, left, bottom, right] {
785 v.extend_from_slice(&x.to_le_bytes());
786 }
787}
788
789pub const DRAW_COPY_IMAGE_OFFSET: u32 = 57;
792
793pub fn draw_copy_bitmap(
797 surface_id: u32,
798 left: u32,
799 top: u32,
800 width: u32,
801 height: u32,
802 image_id: u64,
803 bgrx: &[u8],
804) -> Vec<u8> {
805 let stride = width * 4;
806 let mut v = Vec::with_capacity(DRAW_COPY_IMAGE_OFFSET as usize + 32 + bgrx.len());
807 v.extend_from_slice(&surface_id.to_le_bytes());
809 let (t, l) = (top as i32, left as i32);
810 push_rect(&mut v, t, l, t + height as i32, l + width as i32);
811 v.push(0);
812 v.extend_from_slice(&DRAW_COPY_IMAGE_OFFSET.to_le_bytes());
814 push_rect(&mut v, 0, 0, height as i32, width as i32);
815 v.extend_from_slice(&ROPD_OP_PUT.to_le_bytes());
816 v.push(SCALE_MODE_NEAREST);
817 v.push(0); v.extend_from_slice(&0i32.to_le_bytes()); v.extend_from_slice(&0i32.to_le_bytes()); v.extend_from_slice(&0u32.to_le_bytes()); debug_assert_eq!(v.len(), DRAW_COPY_IMAGE_OFFSET as usize);
822 v.extend_from_slice(&image_id.to_le_bytes());
824 v.push(image_type::BITMAP);
825 v.push(0);
826 v.extend_from_slice(&width.to_le_bytes());
827 v.extend_from_slice(&height.to_le_bytes());
828 v.push(BITMAP_FMT_32BIT);
830 v.push(BITMAP_FLAGS_TOP_DOWN);
831 v.extend_from_slice(&width.to_le_bytes());
832 v.extend_from_slice(&height.to_le_bytes());
833 v.extend_from_slice(&stride.to_le_bytes());
834 v.extend_from_slice(&0u32.to_le_bytes());
835 v.extend_from_slice(bgrx);
836 v
837}
838
839pub fn u32_body(x: u32) -> Vec<u8> {
841 x.to_le_bytes().to_vec()
842}
843
844pub fn display_init(cache_id: u8, cache_size: i64, glz_id: u8, glz_window: i32) -> Vec<u8> {
846 let mut v = vec![cache_id];
847 v.extend_from_slice(&cache_size.to_le_bytes());
848 v.push(glz_id);
849 v.extend_from_slice(&glz_window.to_le_bytes());
850 v
851}
852
853pub fn pong_for(ping: &[u8]) -> Result<Vec<u8>> {
855 if ping.len() < 12 {
856 bail!("a PING of {} bytes has no id and timestamp", ping.len());
857 }
858 Ok(ping[..12].to_vec())
859}
860
861pub fn mouse_motion(dx: i32, dy: i32, buttons: u16) -> Vec<u8> {
863 let mut v = dx.to_le_bytes().to_vec();
864 v.extend_from_slice(&dy.to_le_bytes());
865 v.extend_from_slice(&buttons.to_le_bytes());
866 v
867}
868
869pub fn mouse_position(x: u32, y: u32, buttons: u16, display_id: u8) -> Vec<u8> {
871 let mut v = x.to_le_bytes().to_vec();
872 v.extend_from_slice(&y.to_le_bytes());
873 v.extend_from_slice(&buttons.to_le_bytes());
874 v.push(display_id);
875 v
876}
877
878pub fn mouse_button(button: u8, buttons: u16) -> Vec<u8> {
880 let mut v = vec![button];
881 v.extend_from_slice(&buttons.to_le_bytes());
882 v
883}