ironrdp_pdu/input/
fast_path.rs

1use bit_field::BitField as _;
2use bitflags::bitflags;
3use ironrdp_core::{
4    cast_length, ensure_fixed_part_size, ensure_size, invalid_field_err, other_err, Decode, DecodeResult, Encode,
5    EncodeResult, ReadCursor, WriteCursor,
6};
7use num_derive::FromPrimitive;
8use num_traits::FromPrimitive as _;
9
10use crate::fast_path::EncryptionFlags;
11use crate::input::{MousePdu, MouseRelPdu, MouseXPdu};
12use crate::per;
13
14/// Implements the Fast-Path RDP message header PDU.
15#[derive(Debug, Clone, PartialEq, Eq)]
16pub struct FastPathInputHeader {
17    pub flags: EncryptionFlags,
18    pub data_length: usize,
19    pub num_events: u8,
20}
21
22impl FastPathInputHeader {
23    const NAME: &'static str = "FastPathInputHeader";
24
25    const FIXED_PART_SIZE: usize = 1 /* header */;
26}
27
28impl Encode for FastPathInputHeader {
29    fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
30        ensure_size!(in: dst, size: self.size());
31
32        let mut header = 0u8;
33        header.set_bits(0..2, 0); // fast-path action
34        if self.num_events < 16 {
35            header.set_bits(2..7, self.num_events);
36        }
37        header.set_bits(6..8, self.flags.bits());
38        dst.write_u8(header);
39
40        per::write_length(dst, cast_length!("len", self.data_length + self.size())?);
41        if self.num_events > 15 {
42            dst.write_u8(self.num_events);
43        }
44
45        Ok(())
46    }
47
48    fn name(&self) -> &'static str {
49        Self::NAME
50    }
51
52    fn size(&self) -> usize {
53        let num_events_length = if self.num_events < 16 { 0 } else { 1 };
54        Self::FIXED_PART_SIZE + per::sizeof_length(self.data_length + num_events_length + 1) + num_events_length
55    }
56}
57
58impl<'de> Decode<'de> for FastPathInputHeader {
59    fn decode(src: &mut ReadCursor<'de>) -> DecodeResult<Self> {
60        ensure_fixed_part_size!(in: src);
61
62        let header = src.read_u8();
63        let flags = EncryptionFlags::from_bits_truncate(header.get_bits(6..8));
64        let mut num_events = header.get_bits(2..6);
65        let (length, sizeof_length) = per::read_length(src).map_err(|e| other_err!("perLen", source: e))?;
66
67        if !flags.is_empty() {
68            return Err(invalid_field_err!("flags", "encryption not supported"));
69        }
70
71        let num_events_length = if num_events == 0 {
72            ensure_size!(in: src, size: 1);
73            num_events = src.read_u8();
74            1
75        } else {
76            0
77        };
78
79        let data_length = usize::from(length) - sizeof_length - 1 - num_events_length;
80
81        Ok(FastPathInputHeader {
82            flags,
83            data_length,
84            num_events,
85        })
86    }
87}
88
89#[derive(Debug, Copy, Clone, PartialEq, Eq, FromPrimitive)]
90#[repr(u8)]
91pub enum FastpathInputEventType {
92    ScanCode = 0x0000,
93    Mouse = 0x0001,
94    MouseX = 0x0002,
95    Sync = 0x0003,
96    Unicode = 0x0004,
97    MouseRel = 0x0005,
98    QoeTimestamp = 0x0006,
99}
100
101impl FastpathInputEventType {
102    #[expect(
103        clippy::as_conversions,
104        reason = "guarantees discriminant layout, and as is the only way to cast enum -> primitive"
105    )]
106    fn as_u8(self) -> u8 {
107        self as u8
108    }
109}
110
111#[derive(Debug, Clone, Copy, PartialEq, Eq)]
112pub enum FastPathInputEvent {
113    KeyboardEvent(KeyboardFlags, u8),
114    UnicodeKeyboardEvent(KeyboardFlags, u16),
115    MouseEvent(MousePdu),
116    MouseEventEx(MouseXPdu),
117    MouseEventRel(MouseRelPdu),
118    QoeEvent(u32),
119    SyncEvent(SynchronizeFlags),
120}
121
122impl FastPathInputEvent {
123    const NAME: &'static str = "FastPathInputEvent";
124
125    const FIXED_PART_SIZE: usize = 1 /* header */;
126}
127
128impl Encode for FastPathInputEvent {
129    fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
130        ensure_size!(in: dst, size: self.size());
131
132        let mut header = 0u8;
133        let (flags, code) = match self {
134            FastPathInputEvent::KeyboardEvent(flags, _) => (flags.bits(), FastpathInputEventType::ScanCode),
135            FastPathInputEvent::UnicodeKeyboardEvent(flags, _) => (flags.bits(), FastpathInputEventType::Unicode),
136            FastPathInputEvent::MouseEvent(_) => (0, FastpathInputEventType::Mouse),
137            FastPathInputEvent::MouseEventEx(_) => (0, FastpathInputEventType::MouseX),
138            FastPathInputEvent::MouseEventRel(_) => (0, FastpathInputEventType::MouseRel),
139            FastPathInputEvent::QoeEvent(_) => (0, FastpathInputEventType::QoeTimestamp),
140            FastPathInputEvent::SyncEvent(flags) => (flags.bits(), FastpathInputEventType::Sync),
141        };
142        header.set_bits(0..5, flags);
143        header.set_bits(5..8, code.as_u8());
144        dst.write_u8(header);
145        match self {
146            FastPathInputEvent::KeyboardEvent(_, code) => {
147                dst.write_u8(*code);
148            }
149            FastPathInputEvent::UnicodeKeyboardEvent(_, code) => {
150                dst.write_u16(*code);
151            }
152            FastPathInputEvent::MouseEvent(pdu) => {
153                pdu.encode(dst)?;
154            }
155            FastPathInputEvent::MouseEventEx(pdu) => {
156                pdu.encode(dst)?;
157            }
158            FastPathInputEvent::QoeEvent(stamp) => {
159                dst.write_u32(*stamp);
160            }
161            _ => {}
162        };
163
164        Ok(())
165    }
166
167    fn name(&self) -> &'static str {
168        Self::NAME
169    }
170
171    fn size(&self) -> usize {
172        Self::FIXED_PART_SIZE
173            + match self {
174                FastPathInputEvent::KeyboardEvent(_, _) => 1,
175                FastPathInputEvent::UnicodeKeyboardEvent(_, _) => 2,
176                FastPathInputEvent::MouseEvent(pdu) => pdu.size(),
177                FastPathInputEvent::MouseEventEx(pdu) => pdu.size(),
178                FastPathInputEvent::MouseEventRel(pdu) => pdu.size(),
179                FastPathInputEvent::QoeEvent(_) => 4,
180                FastPathInputEvent::SyncEvent(_) => 0,
181            }
182    }
183}
184
185impl<'de> Decode<'de> for FastPathInputEvent {
186    fn decode(src: &mut ReadCursor<'de>) -> DecodeResult<Self> {
187        ensure_fixed_part_size!(in: src);
188
189        let header = src.read_u8();
190        let flags = header.get_bits(0..5);
191        let code = header.get_bits(5..8);
192        let code: FastpathInputEventType = FastpathInputEventType::from_u8(code)
193            .ok_or_else(|| invalid_field_err!("code", "input event code unsupported"))?;
194        let event = match code {
195            FastpathInputEventType::ScanCode => {
196                ensure_size!(in: src, size: 1);
197                let code = src.read_u8();
198                let flags = KeyboardFlags::from_bits(flags)
199                    .ok_or_else(|| invalid_field_err!("flags", "input keyboard flags unsupported"))?;
200                FastPathInputEvent::KeyboardEvent(flags, code)
201            }
202            FastpathInputEventType::Mouse => {
203                let mouse_event = MousePdu::decode(src)?;
204                FastPathInputEvent::MouseEvent(mouse_event)
205            }
206            FastpathInputEventType::MouseX => {
207                let mouse_event = MouseXPdu::decode(src)?;
208                FastPathInputEvent::MouseEventEx(mouse_event)
209            }
210            FastpathInputEventType::MouseRel => {
211                let mouse_event = MouseRelPdu::decode(src)?;
212                FastPathInputEvent::MouseEventRel(mouse_event)
213            }
214            FastpathInputEventType::Sync => {
215                let flags = SynchronizeFlags::from_bits(flags)
216                    .ok_or_else(|| invalid_field_err!("flags", "input synchronize flags unsupported"))?;
217                FastPathInputEvent::SyncEvent(flags)
218            }
219            FastpathInputEventType::Unicode => {
220                ensure_size!(in: src, size: 2);
221                let code = src.read_u16();
222                let flags = KeyboardFlags::from_bits(flags)
223                    .ok_or_else(|| invalid_field_err!("flags", "input keyboard flags unsupported"))?;
224                FastPathInputEvent::UnicodeKeyboardEvent(flags, code)
225            }
226            FastpathInputEventType::QoeTimestamp => {
227                ensure_size!(in: src, size: 4);
228                let code = src.read_u32();
229                FastPathInputEvent::QoeEvent(code)
230            }
231        };
232        Ok(event)
233    }
234}
235
236bitflags! {
237    #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
238    pub struct KeyboardFlags: u8 {
239        const RELEASE = 0x01;
240        const EXTENDED = 0x02;
241        const EXTENDED1 = 0x04;
242    }
243}
244
245bitflags! {
246    #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
247    pub struct SynchronizeFlags: u8 {
248        const SCROLL_LOCK = 0x01;
249        const NUM_LOCK = 0x02;
250        const CAPS_LOCK = 0x04;
251        const KANA_LOCK = 0x08;
252    }
253}
254
255#[derive(Debug, Clone, PartialEq, Eq)]
256pub struct FastPathInput(
257    /// INVARIANT: (1..=255).contains(len()) = at least one, and at most 255 elements.
258    Vec<FastPathInputEvent>,
259);
260
261impl FastPathInput {
262    const NAME: &'static str = "FastPathInput";
263
264    pub fn new(input_events: Vec<FastPathInputEvent>) -> DecodeResult<Self> {
265        // Ensure the invariant on `input_events.len()` is respected.
266        if !(1..=255usize).contains(&input_events.len()) {
267            return Err(invalid_field_err!("nEvents", "invalid number of input events"));
268        }
269
270        Ok(Self(input_events))
271    }
272
273    pub fn single(input_event: FastPathInputEvent) -> Self {
274        // A single element upholds the invariant.
275        Self(vec![input_event])
276    }
277
278    pub fn input_events(&self) -> &[FastPathInputEvent] {
279        &self.0
280    }
281}
282
283impl Encode for FastPathInput {
284    fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
285        ensure_size!(in: dst, size: self.size());
286
287        if self.0.is_empty() {
288            return Err(other_err!("Empty fast-path input"));
289        }
290
291        let data_length = self.0.iter().map(Encode::size).sum::<usize>();
292        let header = FastPathInputHeader {
293            num_events: u8::try_from(self.0.len()).expect("per invariant (1..=255).contains(num_events.len())"),
294            flags: EncryptionFlags::empty(),
295            data_length,
296        };
297        header.encode(dst)?;
298
299        for event in self.0.iter() {
300            event.encode(dst)?;
301        }
302
303        Ok(())
304    }
305
306    fn name(&self) -> &'static str {
307        Self::NAME
308    }
309
310    fn size(&self) -> usize {
311        let data_length = self.0.iter().map(Encode::size).sum::<usize>();
312        let header = FastPathInputHeader {
313            num_events: u8::try_from(self.0.len())
314                .expect("INVARIANT: num_events is within the range of 1 to 255, inclusive"),
315            flags: EncryptionFlags::empty(),
316            data_length,
317        };
318        header.size() + data_length
319    }
320}
321
322impl<'de> Decode<'de> for FastPathInput {
323    fn decode(src: &mut ReadCursor<'de>) -> DecodeResult<Self> {
324        let header = FastPathInputHeader::decode(src)?;
325        let events = core::iter::repeat_with(|| FastPathInputEvent::decode(src))
326            .take(usize::from(header.num_events))
327            .collect::<Result<Vec<_>, _>>()?;
328
329        Self::new(events)
330    }
331}