ironrdp_pdu/basic_output/pointer/
mod.rs

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