1use bitflags::bitflags;
2use ironrdp_core::{
3 ensure_fixed_part_size, ensure_size, invalid_field_err, write_padding, Decode, DecodeResult, Encode, EncodeResult,
4 ReadCursor, WriteCursor,
5};
6use num_derive::{FromPrimitive, ToPrimitive};
7use num_traits::{FromPrimitive as _, ToPrimitive as _};
8use tap::Pipe as _;
9
10use super::{RdpVersion, VERSION_SIZE};
11use crate::nego::SecurityProtocol;
12use crate::utils;
13
14pub const IME_FILE_NAME_SIZE: usize = 64;
15
16const DESKTOP_WIDTH_SIZE: usize = 2;
17const DESKTOP_HEIGHT_SIZE: usize = 2;
18const COLOR_DEPTH_SIZE: usize = 2;
19const SEC_ACCESS_SEQUENCE_SIZE: usize = 2;
20const KEYBOARD_LAYOUT_SIZE: usize = 4;
21const CLIENT_BUILD_SIZE: usize = 4;
22const CLIENT_NAME_SIZE: usize = 32;
23const KEYBOARD_TYPE_SIZE: usize = 4;
24const KEYBOARD_SUB_TYPE_SIZE: usize = 4;
25const KEYBOARD_FUNCTIONAL_KEYS_COUNT_SIZE: usize = 4;
26
27const POST_BETA_COLOR_DEPTH_SIZE: usize = 2;
28const CLIENT_PRODUCT_ID_SIZE: usize = 2;
29const SERIAL_NUMBER_SIZE: usize = 4;
30const HIGH_COLOR_DEPTH_SIZE: usize = 2;
31const SUPPORTED_COLOR_DEPTHS_SIZE: usize = 2;
32const EARLY_CAPABILITY_FLAGS_SIZE: usize = 2;
33const DIG_PRODUCT_ID_SIZE: usize = 64;
34const CONNECTION_TYPE_SIZE: usize = 1;
35const PADDING_SIZE: usize = 1;
36const SERVER_SELECTED_PROTOCOL_SIZE: usize = 4;
37const DESKTOP_PHYSICAL_WIDTH_SIZE: usize = 4;
38const DESKTOP_PHYSICAL_HEIGHT_SIZE: usize = 4;
39const DESKTOP_ORIENTATION_SIZE: usize = 2;
40const DESKTOP_SCALE_FACTOR_SIZE: usize = 4;
41const DEVICE_SCALE_FACTOR_SIZE: usize = 4;
42
43#[derive(Debug, Clone, PartialEq, Eq)]
47pub struct ClientCoreData {
48 pub version: RdpVersion,
49 pub desktop_width: u16,
50 pub desktop_height: u16,
51 pub color_depth: ColorDepth,
53 pub sec_access_sequence: SecureAccessSequence,
54 pub keyboard_layout: u32,
55 pub client_build: u32,
56 pub client_name: String,
57 pub keyboard_type: KeyboardType,
58 pub keyboard_subtype: u32,
59 pub keyboard_functional_keys_count: u32,
60 pub ime_file_name: String,
61 pub optional_data: ClientCoreOptionalData,
62}
63
64impl ClientCoreData {
65 const NAME: &'static str = "ClientCoreData";
66
67 const FIXED_PART_SIZE: usize = VERSION_SIZE
68 + DESKTOP_WIDTH_SIZE
69 + DESKTOP_HEIGHT_SIZE
70 + COLOR_DEPTH_SIZE
71 + SEC_ACCESS_SEQUENCE_SIZE
72 + KEYBOARD_LAYOUT_SIZE
73 + CLIENT_BUILD_SIZE
74 + CLIENT_NAME_SIZE
75 + KEYBOARD_TYPE_SIZE
76 + KEYBOARD_SUB_TYPE_SIZE
77 + KEYBOARD_FUNCTIONAL_KEYS_COUNT_SIZE
78 + IME_FILE_NAME_SIZE;
79
80 pub fn client_color_depth(&self) -> ClientColorDepth {
81 if let Some(high_color_depth) = self.optional_data.high_color_depth {
82 if let Some(early_capability_flags) = self.optional_data.early_capability_flags {
83 if early_capability_flags.contains(ClientEarlyCapabilityFlags::WANT_32_BPP_SESSION) {
84 ClientColorDepth::Bpp32
85 } else {
86 From::from(high_color_depth)
87 }
88 } else {
89 From::from(high_color_depth)
90 }
91 } else if let Some(post_beta_color_depth) = self.optional_data.post_beta2_color_depth {
92 From::from(post_beta_color_depth)
93 } else {
94 From::from(self.color_depth)
95 }
96 }
97}
98
99impl Encode for ClientCoreData {
100 fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
101 ensure_size!(in: dst, size: self.size());
102
103 let mut client_name_dst = utils::to_utf16_bytes(self.client_name.as_ref());
104 client_name_dst.resize(CLIENT_NAME_SIZE - 2, 0);
105 let mut ime_file_name_dst = utils::to_utf16_bytes(self.ime_file_name.as_ref());
106 ime_file_name_dst.resize(IME_FILE_NAME_SIZE - 2, 0);
107
108 dst.write_u32(self.version.0);
109 dst.write_u16(self.desktop_width);
110 dst.write_u16(self.desktop_height);
111 dst.write_u16(self.color_depth.to_u16().unwrap());
112 dst.write_u16(self.sec_access_sequence.to_u16().unwrap());
113 dst.write_u32(self.keyboard_layout);
114 dst.write_u32(self.client_build);
115 dst.write_slice(client_name_dst.as_ref());
116 dst.write_u16(0); dst.write_u32(self.keyboard_type.to_u32().unwrap());
118 dst.write_u32(self.keyboard_subtype);
119 dst.write_u32(self.keyboard_functional_keys_count);
120 dst.write_slice(ime_file_name_dst.as_ref());
121 dst.write_u16(0); self.optional_data.encode(dst)
124 }
125
126 fn name(&self) -> &'static str {
127 Self::NAME
128 }
129
130 fn size(&self) -> usize {
131 Self::FIXED_PART_SIZE + self.optional_data.size()
132 }
133}
134
135impl<'de> Decode<'de> for ClientCoreData {
136 fn decode(src: &mut ReadCursor<'de>) -> DecodeResult<Self> {
137 ensure_fixed_part_size!(in: src);
138
139 let version = src.read_u32().pipe(RdpVersion);
140 let desktop_width = src.read_u16();
141 let desktop_height = src.read_u16();
142 let color_depth = src
143 .read_u16()
144 .pipe(ColorDepth::from_u16)
145 .ok_or_else(|| invalid_field_err!("colorDepth", "invalid color depth"))?;
146 let sec_access_sequence = src
147 .read_u16()
148 .pipe(SecureAccessSequence::from_u16)
149 .ok_or_else(|| invalid_field_err!("secAccessSequence", "invalid secure access sequence"))?;
150 let keyboard_layout = src.read_u32();
151 let client_build = src.read_u32();
152
153 let client_name_buffer = src.read_slice(CLIENT_NAME_SIZE);
154 let client_name = utils::from_utf16_bytes(client_name_buffer)
155 .trim_end_matches('\u{0}')
156 .into();
157
158 let keyboard_type = src
159 .read_u32()
160 .pipe(KeyboardType::from_u32)
161 .ok_or_else(|| invalid_field_err!("keyboardType", "invalid keyboard type"))?;
162 let keyboard_subtype = src.read_u32();
163 let keyboard_functional_keys_count = src.read_u32();
164
165 let ime_file_name_buffer = src.read_slice(IME_FILE_NAME_SIZE);
166 let ime_file_name = utils::from_utf16_bytes(ime_file_name_buffer)
167 .trim_end_matches('\u{0}')
168 .into();
169
170 let optional_data = ClientCoreOptionalData::decode(src)?;
171
172 Ok(Self {
173 version,
174 desktop_width,
175 desktop_height,
176 color_depth,
177 sec_access_sequence,
178 keyboard_layout,
179 client_build,
180 client_name,
181 keyboard_type,
182 keyboard_subtype,
183 keyboard_functional_keys_count,
184 ime_file_name,
185 optional_data,
186 })
187 }
188}
189
190#[derive(Debug, Clone, Default, PartialEq, Eq)]
197pub struct ClientCoreOptionalData {
198 pub post_beta2_color_depth: Option<ColorDepth>,
200 pub client_product_id: Option<u16>,
201 pub serial_number: Option<u32>,
202 pub high_color_depth: Option<HighColorDepth>,
204 pub supported_color_depths: Option<SupportedColorDepths>,
206 pub early_capability_flags: Option<ClientEarlyCapabilityFlags>,
207 pub dig_product_id: Option<String>,
208 pub connection_type: Option<ConnectionType>,
209 pub server_selected_protocol: Option<SecurityProtocol>,
210 pub desktop_physical_width: Option<u32>,
211 pub desktop_physical_height: Option<u32>,
212 pub desktop_orientation: Option<u16>,
213 pub desktop_scale_factor: Option<u32>,
214 pub device_scale_factor: Option<u32>,
215}
216
217impl ClientCoreOptionalData {
218 const NAME: &'static str = "ClientCoreOptionalData";
219}
220
221impl Encode for ClientCoreOptionalData {
222 fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
223 ensure_size!(in: dst, size: self.size());
224
225 if let Some(value) = self.post_beta2_color_depth {
226 dst.write_u16(value.to_u16().unwrap());
227 }
228
229 if let Some(value) = self.client_product_id {
230 if self.post_beta2_color_depth.is_none() {
231 return Err(invalid_field_err!(
232 "postBeta2ColorDepth",
233 "postBeta2ColorDepth must be present"
234 ));
235 }
236 dst.write_u16(value);
237 }
238
239 if let Some(value) = self.serial_number {
240 if self.client_product_id.is_none() {
241 return Err(invalid_field_err!("clientProductId", "clientProductId must be present"));
242 }
243 dst.write_u32(value);
244 }
245
246 if let Some(value) = self.high_color_depth {
247 if self.serial_number.is_none() {
248 return Err(invalid_field_err!("serialNumber", "serialNumber must be present"));
249 }
250 dst.write_u16(value.to_u16().unwrap());
251 }
252
253 if let Some(value) = self.supported_color_depths {
254 if self.high_color_depth.is_none() {
255 return Err(invalid_field_err!("highColorDepth", "highColorDepth must be present"));
256 }
257 dst.write_u16(value.bits());
258 }
259
260 if let Some(value) = self.early_capability_flags {
261 if self.supported_color_depths.is_none() {
262 return Err(invalid_field_err!(
263 "supportedColorDepths",
264 "supportedColorDepths must be present"
265 ));
266 }
267 dst.write_u16(value.bits());
268 }
269
270 if let Some(ref value) = self.dig_product_id {
271 if self.early_capability_flags.is_none() {
272 return Err(invalid_field_err!(
273 "earlyCapabilityFlags",
274 "earlyCapabilityFlags must be present"
275 ));
276 }
277 let mut dig_product_id_buffer = utils::to_utf16_bytes(value);
278 dig_product_id_buffer.resize(DIG_PRODUCT_ID_SIZE - 2, 0);
279 dig_product_id_buffer.extend_from_slice([0; 2].as_ref()); dst.write_slice(dig_product_id_buffer.as_ref())
282 }
283
284 if let Some(value) = self.connection_type {
285 if self.dig_product_id.is_none() {
286 return Err(invalid_field_err!("digProductId", "digProductId must be present"));
287 }
288 dst.write_u8(value.to_u8().unwrap());
289 write_padding!(dst, 1);
290 }
291
292 if let Some(value) = self.server_selected_protocol {
293 if self.connection_type.is_none() {
294 return Err(invalid_field_err!("connectionType", "connectionType must be present"));
295 }
296 dst.write_u32(value.bits())
297 }
298
299 if let Some(value) = self.desktop_physical_width {
300 if self.server_selected_protocol.is_none() {
301 return Err(invalid_field_err!(
302 "serverSelectedProtocol",
303 "serverSelectedProtocol must be present"
304 ));
305 }
306 dst.write_u32(value);
307 }
308
309 if let Some(value) = self.desktop_physical_height {
310 if self.desktop_physical_width.is_none() {
311 return Err(invalid_field_err!(
312 "desktopPhysicalWidth",
313 "desktopPhysicalWidth must be present"
314 ));
315 }
316 dst.write_u32(value);
317 }
318
319 if let Some(value) = self.desktop_orientation {
320 if self.desktop_physical_height.is_none() {
321 return Err(invalid_field_err!(
322 "desktopPhysicalHeight",
323 "desktopPhysicalHeight must be present"
324 ));
325 }
326 dst.write_u16(value);
327 }
328
329 if let Some(value) = self.desktop_scale_factor {
330 if self.desktop_orientation.is_none() {
331 return Err(invalid_field_err!(
332 "desktopOrientation",
333 "desktopOrientation must be present"
334 ));
335 }
336 dst.write_u32(value);
337 }
338
339 if let Some(value) = self.device_scale_factor {
340 if self.desktop_scale_factor.is_none() {
341 return Err(invalid_field_err!(
342 "desktopScaleFactor",
343 "desktopScaleFactor must be present"
344 ));
345 }
346 dst.write_u32(value);
347 }
348
349 Ok(())
350 }
351
352 fn name(&self) -> &'static str {
353 Self::NAME
354 }
355
356 fn size(&self) -> usize {
357 let mut size = 0;
358
359 if self.post_beta2_color_depth.is_some() {
360 size += POST_BETA_COLOR_DEPTH_SIZE;
361 }
362 if self.client_product_id.is_some() {
363 size += CLIENT_PRODUCT_ID_SIZE;
364 }
365 if self.serial_number.is_some() {
366 size += SERIAL_NUMBER_SIZE;
367 }
368 if self.high_color_depth.is_some() {
369 size += HIGH_COLOR_DEPTH_SIZE;
370 }
371 if self.supported_color_depths.is_some() {
372 size += SUPPORTED_COLOR_DEPTHS_SIZE;
373 }
374 if self.early_capability_flags.is_some() {
375 size += EARLY_CAPABILITY_FLAGS_SIZE;
376 }
377 if self.dig_product_id.is_some() {
378 size += DIG_PRODUCT_ID_SIZE;
379 }
380 if self.connection_type.is_some() {
381 size += CONNECTION_TYPE_SIZE + PADDING_SIZE;
382 }
383 if self.server_selected_protocol.is_some() {
384 size += SERVER_SELECTED_PROTOCOL_SIZE;
385 }
386 if self.desktop_physical_width.is_some() {
387 size += DESKTOP_PHYSICAL_WIDTH_SIZE;
388 }
389 if self.desktop_physical_height.is_some() {
390 size += DESKTOP_PHYSICAL_HEIGHT_SIZE;
391 }
392 if self.desktop_orientation.is_some() {
393 size += DESKTOP_ORIENTATION_SIZE;
394 }
395 if self.desktop_scale_factor.is_some() {
396 size += DESKTOP_SCALE_FACTOR_SIZE;
397 }
398 if self.device_scale_factor.is_some() {
399 size += DEVICE_SCALE_FACTOR_SIZE;
400 }
401
402 size
403 }
404}
405
406macro_rules! try_or_return {
407 ($expr:expr, $ret:expr) => {
408 match $expr {
409 Ok(v) => v,
410 Err(_) => return Ok($ret),
411 }
412 };
413}
414
415impl<'de> Decode<'de> for ClientCoreOptionalData {
416 fn decode(src: &mut ReadCursor<'de>) -> DecodeResult<Self> {
417 let mut optional_data = Self::default();
418
419 optional_data.post_beta2_color_depth = Some(
420 ColorDepth::from_u16(try_or_return!(src.try_read_u16(), optional_data))
421 .ok_or_else(|| invalid_field_err!("postBeta2ColorDepth", "invalid color depth"))?,
422 );
423
424 optional_data.client_product_id = Some(try_or_return!(src.try_read_u16(), optional_data));
425 optional_data.serial_number = Some(try_or_return!(src.try_read_u32(), optional_data));
426
427 optional_data.high_color_depth = Some(
428 HighColorDepth::from_u16(try_or_return!(src.try_read_u16(), optional_data))
429 .ok_or_else(|| invalid_field_err!("highColorDepth", "invalid color depth"))?,
430 );
431
432 optional_data.supported_color_depths = Some(
433 SupportedColorDepths::from_bits(try_or_return!(src.try_read_u16(), optional_data))
434 .ok_or_else(|| invalid_field_err!("supportedColorDepths", "invalid supported color depths"))?,
435 );
436
437 optional_data.early_capability_flags = Some(
438 ClientEarlyCapabilityFlags::from_bits(try_or_return!(src.try_read_u16(), optional_data))
439 .ok_or_else(|| invalid_field_err!("earlyCapabilityFlags", "invalid early capability flags"))?,
440 );
441
442 if src.len() < DIG_PRODUCT_ID_SIZE {
443 return Ok(optional_data);
444 }
445
446 let dig_product_id = src.read_slice(DIG_PRODUCT_ID_SIZE);
447 optional_data.dig_product_id = Some(utils::from_utf16_bytes(dig_product_id).trim_end_matches('\u{0}').into());
448
449 optional_data.connection_type = Some(
450 ConnectionType::from_u8(try_or_return!(src.try_read_u8(), optional_data))
451 .ok_or_else(|| invalid_field_err!("connectionType", "invalid connection type"))?,
452 );
453
454 try_or_return!(src.try_read_u8(), optional_data);
455
456 optional_data.server_selected_protocol = Some(
457 SecurityProtocol::from_bits(try_or_return!(src.try_read_u32(), optional_data))
458 .ok_or_else(|| invalid_field_err!("serverSelectedProtocol", "invalid security protocol"))?,
459 );
460
461 optional_data.desktop_physical_width = Some(try_or_return!(src.try_read_u32(), optional_data));
462 optional_data.desktop_physical_height = Some(src.read_u32());
464
465 optional_data.desktop_orientation = Some(try_or_return!(src.try_read_u16(), optional_data));
466 optional_data.desktop_scale_factor = Some(try_or_return!(src.try_read_u32(), optional_data));
467 optional_data.device_scale_factor = Some(src.read_u32());
469
470 Ok(optional_data)
471 }
472}
473
474#[derive(Debug, Copy, Clone, PartialEq, Eq)]
475pub enum ClientColorDepth {
476 Bpp4,
477 Bpp8,
478 Rgb555Bpp16,
479 Rgb565Bpp16,
480 Bpp24,
481 Bpp32,
482}
483
484impl From<ColorDepth> for ClientColorDepth {
485 fn from(color_depth: ColorDepth) -> Self {
486 match color_depth {
487 ColorDepth::Bpp4 => ClientColorDepth::Bpp4,
488 ColorDepth::Bpp8 => ClientColorDepth::Bpp8,
489 ColorDepth::Rgb555Bpp16 => ClientColorDepth::Rgb555Bpp16,
490 ColorDepth::Rgb565Bpp16 => ClientColorDepth::Rgb565Bpp16,
491 ColorDepth::Bpp24 => ClientColorDepth::Bpp24,
492 }
493 }
494}
495
496impl From<HighColorDepth> for ClientColorDepth {
497 fn from(color_depth: HighColorDepth) -> Self {
498 match color_depth {
499 HighColorDepth::Bpp4 => ClientColorDepth::Bpp4,
500 HighColorDepth::Bpp8 => ClientColorDepth::Bpp8,
501 HighColorDepth::Rgb555Bpp16 => ClientColorDepth::Rgb555Bpp16,
502 HighColorDepth::Rgb565Bpp16 => ClientColorDepth::Rgb565Bpp16,
503 HighColorDepth::Bpp24 => ClientColorDepth::Bpp24,
504 }
505 }
506}
507
508#[repr(u16)]
509#[derive(Debug, Copy, Clone, PartialEq, Eq, FromPrimitive, ToPrimitive)]
510pub enum ColorDepth {
511 Bpp4 = 0xCA00,
512 Bpp8 = 0xCA01,
513 Rgb555Bpp16 = 0xCA02,
514 Rgb565Bpp16 = 0xCA03,
515 Bpp24 = 0xCA04,
516}
517
518#[repr(u16)]
519#[derive(Debug, Copy, Clone, FromPrimitive, ToPrimitive, Eq, Ord, PartialEq, PartialOrd)]
520pub enum HighColorDepth {
521 Bpp4 = 0x0004,
522 Bpp8 = 0x0008,
523 Rgb555Bpp16 = 0x000F,
524 Rgb565Bpp16 = 0x0010,
525 Bpp24 = 0x0018,
526}
527
528#[repr(u16)]
529#[derive(Debug, Copy, Clone, PartialEq, Eq, FromPrimitive, ToPrimitive)]
530pub enum SecureAccessSequence {
531 Del = 0xAA03,
532}
533
534#[derive(Debug, Copy, Clone, PartialEq, Eq, FromPrimitive, ToPrimitive)]
535pub enum KeyboardType {
536 IbmPcXt = 1,
537 OlivettiIco = 2,
538 IbmPcAt = 3,
539 IbmEnhanced = 4,
540 Nokia1050 = 5,
541 Nokia9140 = 6,
542 Japanese = 7,
543}
544
545#[repr(u8)]
546#[derive(Debug, Copy, Clone, PartialEq, Eq, FromPrimitive, ToPrimitive)]
547pub enum ConnectionType {
548 NotUsed = 0, Modem = 1,
550 BroadbandLow = 2,
551 Satellite = 3,
552 BroadbandHigh = 4,
553 Wan = 5,
554 Lan = 6,
555 Autodetect = 7,
556}
557
558bitflags! {
559 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
560 pub struct SupportedColorDepths: u16 {
561 const BPP24 = 1;
562 const BPP16 = 2;
563 const BPP15 = 4;
564 const BPP32 = 8;
565 }
566}
567
568bitflags! {
569 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
570 pub struct ClientEarlyCapabilityFlags: u16 {
571 const SUPPORT_ERR_INFO_PDU = 0x0001;
572 const WANT_32_BPP_SESSION = 0x0002;
573 const SUPPORT_STATUS_INFO_PDU = 0x0004;
574 const STRONG_ASYMMETRIC_KEYS = 0x0008;
575 const RELATIVE_MOUSE_INPUT = 0x0010;
576 const VALID_CONNECTION_TYPE = 0x0020;
577 const SUPPORT_MONITOR_LAYOUT_PDU = 0x0040;
578 const SUPPORT_NET_CHAR_AUTODETECT = 0x0080;
579 const SUPPORT_DYN_VC_GFX_PROTOCOL =0x0100;
580 const SUPPORT_DYNAMIC_TIME_ZONE = 0x0200;
581 const SUPPORT_HEART_BEAT_PDU = 0x0400;
582 const SUPPORT_SKIP_CHANNELJOIN = 0x0800;
583 const _ = !0;
585 }
586}