ironrdp_pdu/basic_output/
pointer.rs

1use ironrdp_core::{
2    ensure_fixed_part_size, ensure_size, invalid_field_err, Decode, DecodeResult, Encode, EncodeResult, ReadCursor,
3    WriteCursor,
4};
5
6// Represents `TS_POINT16` described in [MS-RDPBCGR] 2.2.9.1.1.4.1
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8pub struct Point16 {
9    pub x: u16,
10    pub y: u16,
11}
12
13impl Point16 {
14    const NAME: &'static str = "TS_POINT16";
15    const FIXED_PART_SIZE: usize = 2 /* x */ + 2 /* y */;
16}
17
18impl Encode for Point16 {
19    fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
20        ensure_size!(in: dst, size: self.size());
21
22        dst.write_u16(self.x);
23        dst.write_u16(self.y);
24        Ok(())
25    }
26
27    fn name(&self) -> &'static str {
28        Self::NAME
29    }
30
31    fn size(&self) -> usize {
32        Self::FIXED_PART_SIZE
33    }
34}
35
36impl Decode<'_> for Point16 {
37    fn decode(src: &mut ReadCursor<'_>) -> DecodeResult<Self> {
38        ensure_fixed_part_size!(in: src);
39
40        let x = src.read_u16();
41        let y = src.read_u16();
42
43        Ok(Self { x, y })
44    }
45}
46
47/// According to [MS-RDPBCGR] 2.2.9.1.1.4.2 `TS_POINTERPOSATTRIBUTE` has the same layout
48/// as `TS_POINT16`
49pub type PointerPositionAttribute = Point16;
50
51/// Represents `TS_COLORPOINTERATTRIBUTE` described in [MS-RDPBCGR] 2.2.9.1.1.4.4
52#[derive(Debug, Clone, Copy, PartialEq, Eq)]
53pub struct ColorPointerAttribute<'a> {
54    pub cache_index: u16,
55    pub hot_spot: Point16,
56    pub width: u16,
57    pub height: u16,
58    pub xor_mask: &'a [u8],
59    pub and_mask: &'a [u8],
60}
61
62impl ColorPointerAttribute<'_> {
63    const NAME: &'static str = "TS_COLORPOINTERATTRIBUTE";
64    const FIXED_PART_SIZE: usize =
65        2 /* cacheIdx */ + 2 /* width */ + 2 /* height */ + 2 /* lenAnd */ + 2 /* lenOr */ + Point16::FIXED_PART_SIZE;
66}
67
68macro_rules! check_masks_alignment {
69    ($and_mask:expr, $xor_mask:expr, $pointer_height:expr, $large_ptr:expr) => {{
70        const AND_MASK_SIZE_FIELD: &str = "lengthAndMask";
71        const XOR_MASK_SIZE_FIELD: &str = "lengthXorMask";
72
73        let check_mask = |mask: &[u8], field: &'static str| {
74            if $pointer_height == 0 {
75                return Err(invalid_field_err!(field, "pointer height cannot be zero"));
76            }
77            if $large_ptr && (mask.len() > u32::MAX as usize) {
78                return Err(invalid_field_err!(field, "pointer mask is too big for u32 size"));
79            }
80            if !$large_ptr && (mask.len() > u16::MAX as usize) {
81                return Err(invalid_field_err!(field, "pointer mask is too big for u16 size"));
82            }
83            if (mask.len() % $pointer_height as usize) != 0 {
84                return Err(invalid_field_err!(field, "pointer mask have incomplete scanlines"));
85            }
86            if (mask.len() / $pointer_height as usize) % 2 != 0 {
87                return Err(invalid_field_err!(
88                    field,
89                    "pointer mask scanlines should be aligned to 16 bits"
90                ));
91            }
92            Ok(())
93        };
94
95        check_mask($and_mask, AND_MASK_SIZE_FIELD)?;
96        check_mask($xor_mask, XOR_MASK_SIZE_FIELD)
97    }};
98}
99
100impl Encode for ColorPointerAttribute<'_> {
101    fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
102        ensure_size!(in: dst, size: self.size());
103
104        check_masks_alignment!(self.and_mask, self.xor_mask, self.height, false)?;
105
106        dst.write_u16(self.cache_index);
107        self.hot_spot.encode(dst)?;
108        dst.write_u16(self.width);
109        dst.write_u16(self.height);
110
111        dst.write_u16(self.and_mask.len() as u16);
112        dst.write_u16(self.xor_mask.len() as u16);
113        // Note that masks are written in reverse order. It is not a mistake, that is how the
114        // message is defined in [MS-RDPBCGR]
115        dst.write_slice(self.xor_mask);
116        dst.write_slice(self.and_mask);
117
118        Ok(())
119    }
120
121    fn name(&self) -> &'static str {
122        Self::NAME
123    }
124
125    fn size(&self) -> usize {
126        Self::FIXED_PART_SIZE + self.xor_mask.len() + self.and_mask.len()
127    }
128}
129
130impl<'a> Decode<'a> for ColorPointerAttribute<'a> {
131    fn decode(src: &mut ReadCursor<'a>) -> DecodeResult<Self> {
132        ensure_fixed_part_size!(in: src);
133
134        let cache_index = src.read_u16();
135        let hot_spot = Point16::decode(src)?;
136        let width = src.read_u16();
137        let height = src.read_u16();
138        let length_and_mask = src.read_u16();
139        let length_xor_mask = src.read_u16();
140
141        // Convert to usize during the addition to prevent overflow and match expected type
142        let expected_masks_size = (length_and_mask as usize) + (length_xor_mask as usize);
143        ensure_size!(in: src, size: expected_masks_size);
144
145        let xor_mask = src.read_slice(length_xor_mask as usize);
146        let and_mask = src.read_slice(length_and_mask as usize);
147
148        check_masks_alignment!(and_mask, xor_mask, height, false)?;
149
150        Ok(Self {
151            cache_index,
152            hot_spot,
153            width,
154            height,
155            xor_mask,
156            and_mask,
157        })
158    }
159}
160
161/// Represents `TS_POINTERATTRIBUTE` described in [MS-RDPBCGR] 2.2.9.1.1.4.5
162#[derive(Debug, Clone, Copy, PartialEq, Eq)]
163pub struct PointerAttribute<'a> {
164    pub xor_bpp: u16,
165    pub color_pointer: ColorPointerAttribute<'a>,
166}
167
168impl PointerAttribute<'_> {
169    const NAME: &'static str = "TS_POINTERATTRIBUTE";
170    const FIXED_PART_SIZE: usize = 2 /* xorBpp */;
171}
172
173impl Encode for PointerAttribute<'_> {
174    fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
175        ensure_size!(in: dst, size: self.size());
176
177        dst.write_u16(self.xor_bpp);
178        self.color_pointer.encode(dst)?;
179
180        Ok(())
181    }
182
183    fn name(&self) -> &'static str {
184        Self::NAME
185    }
186
187    fn size(&self) -> usize {
188        Self::FIXED_PART_SIZE + self.color_pointer.size()
189    }
190}
191
192impl<'a> Decode<'a> for PointerAttribute<'a> {
193    fn decode(src: &mut ReadCursor<'a>) -> DecodeResult<Self> {
194        ensure_fixed_part_size!(in: src);
195
196        let xor_bpp = src.read_u16();
197        let color_pointer = ColorPointerAttribute::decode(src)?;
198
199        Ok(Self { xor_bpp, color_pointer })
200    }
201}
202
203/// Represents `TS_CACHEDPOINTERATTRIBUTE` described in [MS-RDPBCGR] 2.2.9.1.1.4.6
204#[derive(Debug, Clone, Copy, PartialEq, Eq)]
205pub struct CachedPointerAttribute {
206    pub cache_index: u16,
207}
208
209impl CachedPointerAttribute {
210    const NAME: &'static str = "TS_CACHEDPOINTERATTRIBUTE";
211    const FIXED_PART_SIZE: usize = 2 /* cacheIdx */;
212}
213
214impl Encode for CachedPointerAttribute {
215    fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
216        ensure_size!(in: dst, size: self.size());
217
218        dst.write_u16(self.cache_index);
219
220        Ok(())
221    }
222
223    fn name(&self) -> &'static str {
224        Self::NAME
225    }
226
227    fn size(&self) -> usize {
228        Self::FIXED_PART_SIZE
229    }
230}
231
232impl Decode<'_> for CachedPointerAttribute {
233    fn decode(src: &mut ReadCursor<'_>) -> DecodeResult<Self> {
234        ensure_fixed_part_size!(in: src);
235
236        let cache_index = src.read_u16();
237
238        Ok(Self { cache_index })
239    }
240}
241
242/// Represents `TS_FP_LARGEPOINTERATTRIBUTE` described in [MS-RDPBCGR] 2.2.9.1.2.1.11
243#[derive(Debug, Clone, Copy, PartialEq, Eq)]
244pub struct LargePointerAttribute<'a> {
245    pub xor_bpp: u16,
246    pub cache_index: u16,
247    pub hot_spot: Point16,
248    pub width: u16,
249    pub height: u16,
250    pub xor_mask: &'a [u8],
251    pub and_mask: &'a [u8],
252}
253
254impl LargePointerAttribute<'_> {
255    const NAME: &'static str = "TS_FP_LARGEPOINTERATTRIBUTE";
256    const FIXED_PART_SIZE: usize =
257        2 /* xorBpp */ + 2 /* cacheIdx */ + 4 /* hotSpot */ + 2 /* width */ + 2 /* height */ +
258        4 /* andMaskLen */ + 4 /* xorMaskLen */;
259}
260
261impl Encode for LargePointerAttribute<'_> {
262    fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
263        ensure_size!(in: dst, size: self.size());
264
265        check_masks_alignment!(self.and_mask, self.xor_mask, self.height, true)?;
266
267        dst.write_u16(self.xor_bpp);
268        dst.write_u16(self.cache_index);
269        self.hot_spot.encode(dst)?;
270        dst.write_u16(self.width);
271        dst.write_u16(self.height);
272
273        dst.write_u32(self.and_mask.len() as u32);
274        dst.write_u32(self.xor_mask.len() as u32);
275        // See comment in `ColorPointerAttribute::encode` about encoding order
276        dst.write_slice(self.xor_mask);
277        dst.write_slice(self.and_mask);
278
279        Ok(())
280    }
281
282    fn name(&self) -> &'static str {
283        Self::NAME
284    }
285
286    fn size(&self) -> usize {
287        Self::FIXED_PART_SIZE + self.xor_mask.len() + self.and_mask.len()
288    }
289}
290
291impl<'a> Decode<'a> for LargePointerAttribute<'a> {
292    fn decode(src: &mut ReadCursor<'a>) -> DecodeResult<Self> {
293        ensure_fixed_part_size!(in: src);
294
295        let xor_bpp = src.read_u16();
296        let cache_index = src.read_u16();
297        let hot_spot = Point16::decode(src)?;
298        let width = src.read_u16();
299        let height = src.read_u16();
300        // Convert to usize to prevent overflow during addition
301        let length_and_mask = src.read_u32() as usize;
302        let length_xor_mask = src.read_u32() as usize;
303
304        let expected_masks_size = length_and_mask + length_xor_mask;
305        ensure_size!(in: src, size: expected_masks_size);
306
307        let xor_mask = src.read_slice(length_xor_mask);
308        let and_mask = src.read_slice(length_and_mask);
309
310        check_masks_alignment!(and_mask, xor_mask, height, true)?;
311
312        Ok(Self {
313            xor_bpp,
314            cache_index,
315            hot_spot,
316            width,
317            height,
318            xor_mask,
319            and_mask,
320        })
321    }
322}
323
324/// Pointer-related FastPath update messages (inner FastPath packet data)
325#[derive(Debug, Clone, Copy, PartialEq, Eq)]
326pub enum PointerUpdateData<'a> {
327    SetHidden,
328    SetDefault,
329    SetPosition(PointerPositionAttribute),
330    Color(ColorPointerAttribute<'a>),
331    Cached(CachedPointerAttribute),
332    New(PointerAttribute<'a>),
333    Large(LargePointerAttribute<'a>),
334}