1use core::fmt;
2use std::io;
3
4use bitflags::bitflags;
5use ironrdp_core::{
6 ensure_fixed_part_size, ensure_size, invalid_field_err, write_padding, Decode, DecodeResult, Encode, EncodeResult,
7 ReadCursor, WriteCursor,
8};
9use num_derive::{FromPrimitive, ToPrimitive};
10use num_traits::{FromPrimitive as _, ToPrimitive as _};
11use thiserror::Error;
12
13use crate::utils::CharacterSet;
14use crate::{utils, PduError};
15
16const RECONNECT_COOKIE_LEN: usize = 28;
17const TIMEZONE_INFO_NAME_LEN: usize = 64;
18const COMPRESSION_TYPE_MASK: u32 = 0x0000_1E00;
19
20const CODE_PAGE_SIZE: usize = 4;
21const FLAGS_SIZE: usize = 4;
22const DOMAIN_LENGTH_SIZE: usize = 2;
23const USER_NAME_LENGTH_SIZE: usize = 2;
24const PASSWORD_LENGTH_SIZE: usize = 2;
25const ALTERNATE_SHELL_LENGTH_SIZE: usize = 2;
26const WORK_DIR_LENGTH_SIZE: usize = 2;
27
28const CLIENT_ADDRESS_FAMILY_SIZE: usize = 2;
29const CLIENT_ADDRESS_LENGTH_SIZE: usize = 2;
30const CLIENT_DIR_LENGTH_SIZE: usize = 2;
31const SESSION_ID_SIZE: usize = 4;
32const PERFORMANCE_FLAGS_SIZE: usize = 4;
33const RECONNECT_COOKIE_LENGTH_SIZE: usize = 2;
34const BIAS_SIZE: usize = 4;
35
36#[derive(Debug, Clone, PartialEq, Eq)]
40pub struct ClientInfo {
41 pub credentials: Credentials,
42 pub code_page: u32,
43 pub flags: ClientInfoFlags,
44 pub compression_type: CompressionType,
45 pub alternate_shell: String,
46 pub work_dir: String,
47 pub extra_info: ExtendedClientInfo,
48}
49
50impl ClientInfo {
51 const NAME: &'static str = "ClientInfo";
52
53 pub const FIXED_PART_SIZE: usize = CODE_PAGE_SIZE
54 + FLAGS_SIZE
55 + DOMAIN_LENGTH_SIZE
56 + USER_NAME_LENGTH_SIZE
57 + PASSWORD_LENGTH_SIZE
58 + ALTERNATE_SHELL_LENGTH_SIZE
59 + WORK_DIR_LENGTH_SIZE;
60}
61
62impl Encode for ClientInfo {
63 fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
64 ensure_fixed_part_size!(in: dst);
65
66 let character_set = if self.flags.contains(ClientInfoFlags::UNICODE) {
67 CharacterSet::Unicode
68 } else {
69 CharacterSet::Ansi
70 };
71
72 dst.write_u32(self.code_page);
73
74 let flags_with_compression_type = self.flags.bits() | (self.compression_type.to_u32().unwrap() << 9);
75 dst.write_u32(flags_with_compression_type);
76
77 let domain = self.credentials.domain.clone().unwrap_or_default();
78 dst.write_u16(string_len(domain.as_str(), character_set));
79 dst.write_u16(string_len(self.credentials.username.as_str(), character_set));
80 dst.write_u16(string_len(self.credentials.password.as_str(), character_set));
81 dst.write_u16(string_len(self.alternate_shell.as_str(), character_set));
82 dst.write_u16(string_len(self.work_dir.as_str(), character_set));
83
84 utils::write_string_to_cursor(dst, domain.as_str(), character_set, true)?;
85 utils::write_string_to_cursor(dst, self.credentials.username.as_str(), character_set, true)?;
86 utils::write_string_to_cursor(dst, self.credentials.password.as_str(), character_set, true)?;
87 utils::write_string_to_cursor(dst, self.alternate_shell.as_str(), character_set, true)?;
88 utils::write_string_to_cursor(dst, self.work_dir.as_str(), character_set, true)?;
89
90 self.extra_info.encode(dst, character_set)?;
91
92 Ok(())
93 }
94
95 fn name(&self) -> &'static str {
96 Self::NAME
97 }
98
99 fn size(&self) -> usize {
100 let character_set = if self.flags.contains(ClientInfoFlags::UNICODE) {
101 CharacterSet::Unicode
102 } else {
103 CharacterSet::Ansi
104 };
105 let domain = self.credentials.domain.clone().unwrap_or_default();
106
107 CODE_PAGE_SIZE
108 + FLAGS_SIZE
109 + DOMAIN_LENGTH_SIZE
110 + USER_NAME_LENGTH_SIZE
111 + PASSWORD_LENGTH_SIZE
112 + ALTERNATE_SHELL_LENGTH_SIZE
113 + WORK_DIR_LENGTH_SIZE
114 + (string_len(domain.as_str(), character_set)
115 + string_len(self.credentials.username.as_str(), character_set)
116 + string_len(self.credentials.password.as_str(), character_set)
117 + string_len(self.alternate_shell.as_str(), character_set)
118 + string_len(self.work_dir.as_str(), character_set)) as usize
119 + character_set.to_usize().unwrap() * 5 + self.extra_info.size(character_set)
121 }
122}
123
124impl<'de> Decode<'de> for ClientInfo {
125 fn decode(src: &mut ReadCursor<'de>) -> DecodeResult<Self> {
126 ensure_fixed_part_size!(in: src);
127
128 let code_page = src.read_u32();
129 let flags_with_compression_type = src.read_u32();
130
131 let flags = ClientInfoFlags::from_bits(flags_with_compression_type & !COMPRESSION_TYPE_MASK)
132 .ok_or_else(|| invalid_field_err!("flags", "invalid ClientInfoFlags"))?;
133 let compression_type =
134 CompressionType::from_u8(((flags_with_compression_type & COMPRESSION_TYPE_MASK) >> 9) as u8)
135 .ok_or_else(|| invalid_field_err!("flags", "invalid CompressionType"))?;
136
137 let character_set = if flags.contains(ClientInfoFlags::UNICODE) {
138 CharacterSet::Unicode
139 } else {
140 CharacterSet::Ansi
141 };
142
143 let nt = character_set.to_usize().unwrap();
145 let domain_size = src.read_u16() as usize + nt;
146 let user_name_size = src.read_u16() as usize + nt;
147 let password_size = src.read_u16() as usize + nt;
148 let alternate_shell_size = src.read_u16() as usize + nt;
149 let work_dir_size = src.read_u16() as usize + nt;
150 ensure_size!(in: src, size: domain_size + user_name_size + password_size + alternate_shell_size + work_dir_size);
151
152 let domain = utils::decode_string(src.read_slice(domain_size), character_set, true)?;
153 let username = utils::decode_string(src.read_slice(user_name_size), character_set, true)?;
154 let password = utils::decode_string(src.read_slice(password_size), character_set, true)?;
155
156 let domain = if domain.is_empty() { None } else { Some(domain) };
157 let credentials = Credentials {
158 username,
159 password,
160 domain,
161 };
162
163 let alternate_shell = utils::decode_string(src.read_slice(alternate_shell_size), character_set, true)?;
164 let work_dir = utils::decode_string(src.read_slice(work_dir_size), character_set, true)?;
165
166 let extra_info = ExtendedClientInfo::decode(src, character_set)?;
167
168 Ok(Self {
169 credentials,
170 code_page,
171 flags,
172 compression_type,
173 alternate_shell,
174 work_dir,
175 extra_info,
176 })
177 }
178}
179
180#[derive(Clone, PartialEq, Eq)]
181pub struct Credentials {
182 pub username: String,
183 pub password: String,
184 pub domain: Option<String>,
185}
186
187impl fmt::Debug for Credentials {
188 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
189 f.debug_struct("Credentials")
191 .field("username", &self.username)
192 .field("domain", &self.domain)
193 .finish_non_exhaustive()
194 }
195}
196
197#[derive(Debug, Clone, PartialEq, Eq)]
198pub struct ExtendedClientInfo {
199 pub address_family: AddressFamily,
200 pub address: String,
201 pub dir: String,
202 pub optional_data: ExtendedClientOptionalInfo,
203}
204
205impl ExtendedClientInfo {
206 fn decode(src: &mut ReadCursor<'_>, character_set: CharacterSet) -> DecodeResult<Self> {
209 ensure_size!(in: src, size: CLIENT_ADDRESS_FAMILY_SIZE + CLIENT_ADDRESS_LENGTH_SIZE);
210
211 let address_family = AddressFamily::from_u16(src.read_u16());
212
213 let address_size = src.read_u16() as usize;
215 ensure_size!(in: src, size: address_size + CLIENT_DIR_LENGTH_SIZE);
216
217 let address = utils::decode_string(src.read_slice(address_size), character_set, false)?;
218 let dir_size = src.read_u16() as usize;
220 ensure_size!(in: src, size: dir_size);
221
222 let dir = utils::decode_string(src.read_slice(dir_size), character_set, false)?;
223
224 let optional_data = ExtendedClientOptionalInfo::decode(src)?;
225
226 Ok(Self {
227 address_family,
228 address,
229 dir,
230 optional_data,
231 })
232 }
233
234 fn encode(&self, dst: &mut WriteCursor<'_>, character_set: CharacterSet) -> EncodeResult<()> {
235 ensure_size!(in: dst, size: self.size(character_set));
236
237 dst.write_u16(self.address_family.as_u16());
238 dst.write_u16(string_len(self.address.as_str(), character_set) + character_set.to_u16().unwrap());
240 utils::write_string_to_cursor(dst, self.address.as_str(), character_set, true)?;
241 dst.write_u16(string_len(self.dir.as_str(), character_set) + character_set.to_u16().unwrap());
242 utils::write_string_to_cursor(dst, self.dir.as_str(), character_set, true)?;
243 self.optional_data.encode(dst)?;
244
245 Ok(())
246 }
247
248 fn size(&self, character_set: CharacterSet) -> usize {
249 CLIENT_ADDRESS_FAMILY_SIZE
250 + CLIENT_ADDRESS_LENGTH_SIZE
251 + string_len(self.address.as_str(), character_set) as usize
252 + character_set.to_usize().unwrap() + CLIENT_DIR_LENGTH_SIZE
254 + string_len(self.dir.as_str(), character_set) as usize
255 + character_set.to_usize().unwrap() + self.optional_data.size()
257 }
258}
259
260#[derive(Debug, Clone, PartialEq, Eq, Default)]
261pub struct ExtendedClientOptionalInfo {
262 timezone: Option<TimezoneInfo>,
263 session_id: Option<u32>,
264 performance_flags: Option<PerformanceFlags>,
265 reconnect_cookie: Option<[u8; RECONNECT_COOKIE_LEN]>,
266 }
268
269impl ExtendedClientOptionalInfo {
270 const NAME: &'static str = "ExtendedClientOptionalInfo";
271
272 pub fn builder(
274 ) -> builder::ExtendedClientOptionalInfoBuilder<builder::ExtendedClientOptionalInfoBuilderStateSetTimeZone> {
275 builder::ExtendedClientOptionalInfoBuilder::<builder::ExtendedClientOptionalInfoBuilderStateSetTimeZone>::default()
276 }
277
278 pub fn timezone(&self) -> Option<&TimezoneInfo> {
279 self.timezone.as_ref()
280 }
281
282 pub fn session_id(&self) -> Option<u32> {
283 self.session_id
284 }
285
286 pub fn performance_flags(&self) -> Option<PerformanceFlags> {
287 self.performance_flags
288 }
289
290 pub fn reconnect_cookie(&self) -> Option<&[u8; RECONNECT_COOKIE_LEN]> {
291 self.reconnect_cookie.as_ref()
292 }
293}
294
295impl Encode for ExtendedClientOptionalInfo {
296 fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
297 ensure_size!(in: dst, size: self.size());
298
299 if let Some(ref timezone) = self.timezone {
300 timezone.encode(dst)?;
301 }
302 if let Some(session_id) = self.session_id {
303 dst.write_u32(session_id);
304 }
305 if let Some(performance_flags) = self.performance_flags {
306 dst.write_u32(performance_flags.bits());
307 }
308 if let Some(reconnect_cookie) = self.reconnect_cookie {
309 dst.write_u16(RECONNECT_COOKIE_LEN as u16);
310 dst.write_array(reconnect_cookie);
311 }
312
313 Ok(())
314 }
315
316 fn name(&self) -> &'static str {
317 Self::NAME
318 }
319
320 fn size(&self) -> usize {
321 let mut size = 0;
322
323 if let Some(ref timezone) = self.timezone {
324 size += timezone.size();
325 }
326 if self.session_id.is_some() {
327 size += SESSION_ID_SIZE;
328 }
329 if self.performance_flags.is_some() {
330 size += PERFORMANCE_FLAGS_SIZE;
331 }
332 if self.reconnect_cookie.is_some() {
333 size += RECONNECT_COOKIE_LENGTH_SIZE + RECONNECT_COOKIE_LEN;
334 }
335
336 size
337 }
338}
339
340impl<'de> Decode<'de> for ExtendedClientOptionalInfo {
341 fn decode(src: &mut ReadCursor<'de>) -> DecodeResult<Self> {
342 let mut optional_data = Self::default();
343
344 if src.len() < TimezoneInfo::FIXED_PART_SIZE {
345 return Ok(optional_data);
346 }
347 optional_data.timezone = Some(TimezoneInfo::decode(src)?);
348
349 if src.len() < 4 {
350 return Ok(optional_data);
351 }
352 optional_data.session_id = Some(src.read_u32());
353
354 if src.len() < 4 {
355 return Ok(optional_data);
356 }
357 optional_data.performance_flags = Some(
358 PerformanceFlags::from_bits(src.read_u32())
359 .ok_or_else(|| invalid_field_err!("performanceFlags", "invalid performance flags"))?,
360 );
361
362 if src.len() < 2 {
363 return Ok(optional_data);
364 }
365 let reconnect_cookie_size = src.read_u16();
366 if reconnect_cookie_size != RECONNECT_COOKIE_LEN as u16 && reconnect_cookie_size != 0 {
367 return Err(invalid_field_err!("cbAutoReconnectCookie", "invalid cookie size"));
368 }
369 if reconnect_cookie_size != 0 {
370 if src.len() < RECONNECT_COOKIE_LEN {
371 return Err(invalid_field_err!("cbAutoReconnectCookie", "missing cookie data"));
372 }
373 optional_data.reconnect_cookie = Some(src.read_array());
374 }
375
376 if src.len() < 2 * 2 {
377 return Ok(optional_data);
378 }
379 src.read_u16(); src.read_u16(); Ok(optional_data)
383 }
384}
385
386#[derive(Debug, Clone, PartialEq, Eq)]
392pub struct TimezoneInfo {
393 pub bias: i32,
394 pub standard_name: String,
395 pub standard_date: OptionalSystemTime,
396 pub standard_bias: i32,
397 pub daylight_name: String,
398 pub daylight_date: OptionalSystemTime,
399 pub daylight_bias: i32,
400}
401
402impl TimezoneInfo {
403 const NAME: &'static str = "TimezoneInfo";
404
405 const FIXED_PART_SIZE: usize = BIAS_SIZE
406 + TIMEZONE_INFO_NAME_LEN
407 + SystemTime::FIXED_PART_SIZE
408 + BIAS_SIZE
409 + TIMEZONE_INFO_NAME_LEN
410 + SystemTime::FIXED_PART_SIZE
411 + BIAS_SIZE;
412}
413
414impl Encode for TimezoneInfo {
415 fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
416 ensure_fixed_part_size!(in: dst);
417
418 dst.write_i32(self.bias);
419
420 let mut standard_name = utils::to_utf16_bytes(self.standard_name.as_str());
421 standard_name.resize(TIMEZONE_INFO_NAME_LEN, 0);
422 dst.write_slice(&standard_name);
423
424 self.standard_date.encode(dst)?;
425 dst.write_i32(self.standard_bias);
426
427 let mut daylight_name = utils::to_utf16_bytes(self.daylight_name.as_str());
428 daylight_name.resize(TIMEZONE_INFO_NAME_LEN, 0);
429 dst.write_slice(&daylight_name);
430
431 self.daylight_date.encode(dst)?;
432 dst.write_i32(self.daylight_bias);
433
434 Ok(())
435 }
436
437 fn name(&self) -> &'static str {
438 Self::NAME
439 }
440
441 fn size(&self) -> usize {
442 Self::FIXED_PART_SIZE
443 }
444}
445
446impl<'de> Decode<'de> for TimezoneInfo {
447 fn decode(src: &mut ReadCursor<'de>) -> DecodeResult<Self> {
448 ensure_fixed_part_size!(in: src);
449
450 let bias = src.read_i32();
451 let standard_name = utils::decode_string(src.read_slice(TIMEZONE_INFO_NAME_LEN), CharacterSet::Unicode, false)?;
452 let standard_date = OptionalSystemTime::decode(src)?;
453 let standard_bias = src.read_i32();
454
455 let daylight_name = utils::decode_string(src.read_slice(TIMEZONE_INFO_NAME_LEN), CharacterSet::Unicode, false)?;
456 let daylight_date = OptionalSystemTime::decode(src)?;
457 let daylight_bias = src.read_i32();
458
459 Ok(Self {
460 bias,
461 standard_name,
462 standard_date,
463 standard_bias,
464 daylight_name,
465 daylight_date,
466 daylight_bias,
467 })
468 }
469}
470
471impl Default for TimezoneInfo {
472 fn default() -> Self {
473 Self {
474 bias: 0,
475 standard_name: String::new(),
476 standard_date: OptionalSystemTime(None),
477 standard_bias: 0,
478 daylight_name: String::new(),
479 daylight_date: OptionalSystemTime(None),
480 daylight_bias: 0,
481 }
482 }
483}
484
485#[derive(Debug, Clone, PartialEq, Eq)]
486pub struct SystemTime {
487 pub month: Month,
488 pub day_of_week: DayOfWeek,
489 pub day: DayOfWeekOccurrence,
490 pub hour: u16,
491 pub minute: u16,
492 pub second: u16,
493 pub milliseconds: u16,
494}
495
496impl SystemTime {
497 const NAME: &'static str = "SystemTime";
498
499 const FIXED_PART_SIZE: usize = 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 ;
500}
501
502#[derive(Debug, Clone, PartialEq, Eq)]
503pub struct OptionalSystemTime(pub Option<SystemTime>);
504
505impl Encode for OptionalSystemTime {
506 fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
507 ensure_size!(in: dst, size: self.size());
508
509 dst.write_u16(0); if let Some(st) = &self.0 {
511 dst.write_u16(st.month.to_u16().unwrap());
512 dst.write_u16(st.day_of_week.to_u16().unwrap());
513 dst.write_u16(st.day.to_u16().unwrap());
514 dst.write_u16(st.hour);
515 dst.write_u16(st.minute);
516 dst.write_u16(st.second);
517 dst.write_u16(st.milliseconds);
518 } else {
519 write_padding!(dst, 2 * 7);
520 }
521
522 Ok(())
523 }
524
525 fn name(&self) -> &'static str {
526 SystemTime::NAME
527 }
528
529 fn size(&self) -> usize {
530 SystemTime::FIXED_PART_SIZE
531 }
532}
533
534impl<'de> Decode<'de> for OptionalSystemTime {
535 fn decode(src: &mut ReadCursor<'de>) -> DecodeResult<Self> {
536 ensure_size!(in: src, size: SystemTime::FIXED_PART_SIZE);
537
538 let _year = src.read_u16(); let month = src.read_u16();
540 let day_of_week = src.read_u16();
541 let day = src.read_u16();
542 let hour = src.read_u16();
543 let minute = src.read_u16();
544 let second = src.read_u16();
545 let milliseconds = src.read_u16();
546
547 match (
548 Month::from_u16(month),
549 DayOfWeek::from_u16(day_of_week),
550 DayOfWeekOccurrence::from_u16(day),
551 ) {
552 (Some(month), Some(day_of_week), Some(day)) => Ok(Self(Some(SystemTime {
553 month,
554 day_of_week,
555 day,
556 hour,
557 minute,
558 second,
559 milliseconds,
560 }))),
561 _ => Ok(Self(None)),
562 }
563 }
564}
565
566#[repr(u16)]
567#[derive(Debug, Copy, Clone, PartialEq, Eq, FromPrimitive, ToPrimitive)]
568pub enum Month {
569 January = 1,
570 February = 2,
571 March = 3,
572 April = 4,
573 May = 5,
574 June = 6,
575 July = 7,
576 August = 8,
577 September = 9,
578 October = 10,
579 November = 11,
580 December = 12,
581}
582
583#[repr(u16)]
584#[derive(Debug, Copy, Clone, PartialEq, Eq, FromPrimitive, ToPrimitive)]
585pub enum DayOfWeek {
586 Sunday = 0,
587 Monday = 1,
588 Tuesday = 2,
589 Wednesday = 3,
590 Thursday = 4,
591 Friday = 5,
592 Saturday = 6,
593}
594
595#[repr(u16)]
596#[derive(Debug, Copy, Clone, PartialEq, Eq, FromPrimitive, ToPrimitive)]
597pub enum DayOfWeekOccurrence {
598 First = 1,
599 Second = 2,
600 Third = 3,
601 Fourth = 4,
602 Last = 5,
603}
604
605bitflags! {
606 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
607 pub struct PerformanceFlags: u32 {
608 const DISABLE_WALLPAPER = 0x0000_0001;
609 const DISABLE_FULLWINDOWDRAG = 0x0000_0002;
610 const DISABLE_MENUANIMATIONS = 0x0000_0004;
611 const DISABLE_THEMING = 0x0000_0008;
612 const RESERVED1 = 0x0000_0010;
613 const DISABLE_CURSOR_SHADOW = 0x0000_0020;
614 const DISABLE_CURSORSETTINGS = 0x0000_0040;
615 const ENABLE_FONT_SMOOTHING = 0x0000_0080;
616 const ENABLE_DESKTOP_COMPOSITION = 0x0000_0100;
617 const RESERVED2 = 0x8000_0000;
618 }
619}
620
621impl Default for PerformanceFlags {
622 fn default() -> Self {
623 Self::DISABLE_FULLWINDOWDRAG | Self::DISABLE_MENUANIMATIONS | Self::ENABLE_FONT_SMOOTHING
624 }
625}
626
627#[repr(transparent)]
628#[derive(Debug, Copy, Clone, PartialEq, Eq)]
629pub struct AddressFamily(u16);
630
631impl AddressFamily {
632 pub const INET: Self = Self(0x0002);
633 pub const INET_6: Self = Self(0x0017);
634}
635
636impl AddressFamily {
637 pub fn from_u16(val: u16) -> Self {
638 Self(val)
639 }
640
641 pub fn as_u16(self) -> u16 {
642 self.0
643 }
644}
645
646bitflags! {
647 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
648 pub struct ClientInfoFlags: u32 {
649 const MOUSE = 0x0000_0001;
651 const DISABLE_CTRL_ALT_DEL = 0x0000_0002;
653 const AUTOLOGON = 0x0000_0008;
655 const UNICODE = 0x0000_0010;
657 const MAXIMIZE_SHELL = 0x0000_0020;
659 const LOGON_NOTIFY = 0x0000_0040;
661 const COMPRESSION = 0x0000_0080;
663 const ENABLE_WINDOWS_KEY = 0x0000_0100;
665 const REMOTE_CONSOLE_AUDIO = 0x0000_2000;
667 const FORCE_ENCRYPTED_CS_PDU = 0x0000_4000;
669 const RAIL = 0x0000_8000;
671 const LOGON_ERRORS = 0x0001_0000;
673 const MOUSE_HAS_WHEEL = 0x0002_0000;
675 const PASSWORD_IS_SC_PIN = 0x0004_0000;
677 const NO_AUDIO_PLAYBACK = 0x0008_0000;
679 const USING_SAVED_CREDS = 0x0010_0000;
681 const AUDIO_CAPTURE = 0x0020_0000;
683 const VIDEO_DISABLE = 0x0040_0000;
685 const RESERVED1 = 0x0080_0000;
687 const RESERVED2 = 0x0100_0000;
689 const HIDEF_RAIL_SUPPORTED = 0x0200_0000;
691 }
692}
693
694#[derive(Debug, Copy, Clone, PartialEq, Eq, FromPrimitive, ToPrimitive)]
695pub enum CompressionType {
696 K8 = 0,
697 K64 = 1,
698 Rdp6 = 2,
699 Rdp61 = 3,
700}
701
702#[derive(Debug, Error)]
703pub enum ClientInfoError {
704 #[error("IO error")]
705 IOError(#[from] io::Error),
706 #[error("UTF-8 error")]
707 Utf8Error(#[from] std::string::FromUtf8Error),
708 #[error("invalid address family field")]
709 InvalidAddressFamily,
710 #[error("invalid flags field")]
711 InvalidClientInfoFlags,
712 #[error("invalid performance flags field")]
713 InvalidPerformanceFlags,
714 #[error("invalid reconnect cookie field")]
715 InvalidReconnectCookie,
716 #[error("PDU error: {0}")]
717 Pdu(PduError),
718}
719
720impl From<PduError> for ClientInfoError {
721 fn from(e: PduError) -> Self {
722 Self::Pdu(e)
723 }
724}
725
726fn string_len(value: &str, character_set: CharacterSet) -> u16 {
727 match character_set {
728 CharacterSet::Ansi => u16::try_from(value.len()).unwrap(),
729 CharacterSet::Unicode => u16::try_from(value.encode_utf16().count() * 2).unwrap(),
730 }
731}
732
733pub mod builder {
734 use core::marker::PhantomData;
735
736 use super::{ExtendedClientOptionalInfo, PerformanceFlags, TimezoneInfo, RECONNECT_COOKIE_LEN};
737
738 pub struct ExtendedClientOptionalInfoBuilderStateSetTimeZone;
739 pub struct ExtendedClientOptionalInfoBuilderStateSetSessionId;
740 pub struct ExtendedClientOptionalInfoBuilderStateSetPerformanceFlags;
741 pub struct ExtendedClientOptionalInfoBuilderStateSetReconnectCookie;
742 pub struct ExtendedClientOptionalInfoBuilderStateFinal;
743
744 #[derive(Debug, Clone, PartialEq, Eq)]
749 pub struct ExtendedClientOptionalInfoBuilder<State> {
750 inner: ExtendedClientOptionalInfo,
751 _phantom_data: PhantomData<State>,
752 }
753
754 impl<State> ExtendedClientOptionalInfoBuilder<State> {
755 pub fn build(self) -> ExtendedClientOptionalInfo {
756 self.inner
757 }
758 }
759
760 impl ExtendedClientOptionalInfoBuilder<ExtendedClientOptionalInfoBuilderStateSetTimeZone> {
761 pub fn new() -> Self {
762 Self {
763 inner: ExtendedClientOptionalInfo::default(),
764 _phantom_data: Default::default(),
765 }
766 }
767
768 pub fn timezone(
769 mut self,
770 timezone: TimezoneInfo,
771 ) -> ExtendedClientOptionalInfoBuilder<ExtendedClientOptionalInfoBuilderStateSetSessionId> {
772 self.inner.timezone = Some(timezone);
773 ExtendedClientOptionalInfoBuilder {
774 inner: self.inner,
775 _phantom_data: Default::default(),
776 }
777 }
778 }
779
780 impl Default for ExtendedClientOptionalInfoBuilder<ExtendedClientOptionalInfoBuilderStateSetTimeZone> {
781 fn default() -> Self {
782 Self::new()
783 }
784 }
785
786 impl ExtendedClientOptionalInfoBuilder<ExtendedClientOptionalInfoBuilderStateSetSessionId> {
787 pub fn session_id(
788 mut self,
789 session_id: u32,
790 ) -> ExtendedClientOptionalInfoBuilder<ExtendedClientOptionalInfoBuilderStateSetPerformanceFlags> {
791 self.inner.session_id = Some(session_id);
792 ExtendedClientOptionalInfoBuilder {
793 inner: self.inner,
794 _phantom_data: Default::default(),
795 }
796 }
797 }
798
799 impl ExtendedClientOptionalInfoBuilder<ExtendedClientOptionalInfoBuilderStateSetPerformanceFlags> {
800 pub fn performance_flags(
801 mut self,
802 performance_flags: PerformanceFlags,
803 ) -> ExtendedClientOptionalInfoBuilder<ExtendedClientOptionalInfoBuilderStateSetReconnectCookie> {
804 self.inner.performance_flags = Some(performance_flags);
805 ExtendedClientOptionalInfoBuilder {
806 inner: self.inner,
807 _phantom_data: Default::default(),
808 }
809 }
810 }
811
812 impl ExtendedClientOptionalInfoBuilder<ExtendedClientOptionalInfoBuilderStateSetReconnectCookie> {
813 pub fn reconnect_cookie(
814 mut self,
815 reconnect_cookie: [u8; RECONNECT_COOKIE_LEN],
816 ) -> ExtendedClientOptionalInfoBuilder<ExtendedClientOptionalInfoBuilderStateFinal> {
817 self.inner.reconnect_cookie = Some(reconnect_cookie);
818 ExtendedClientOptionalInfoBuilder {
819 inner: self.inner,
820 _phantom_data: Default::default(),
821 }
822 }
823 }
824}