1use ironrdp_core::{
4 cast_length, ensure_fixed_part_size, ensure_size, invalid_field_err, invalid_field_err_with_source, read_padding,
5 write_padding, Decode, DecodeResult, Encode, EncodeResult, ReadCursor, WriteCursor,
6};
7
8use crate::Pdu;
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub struct PcbVersion(pub u32);
13
14impl PcbVersion {
15 pub const V1: Self = Self(0x1);
16 pub const V2: Self = Self(0x2);
17}
18
19#[derive(Debug, Clone, PartialEq, Eq)]
29pub struct PreconnectionBlob {
30 pub version: PcbVersion,
32 pub id: u32,
36 pub v2_payload: Option<String>,
38}
39
40impl PreconnectionBlob {
41 pub const FIXED_PART_SIZE: usize = 16;
42}
43
44impl Pdu for PreconnectionBlob {
45 const NAME: &'static str = "PreconnectionBlob";
46}
47
48impl<'de> Decode<'de> for PreconnectionBlob {
49 fn decode(src: &mut ReadCursor<'de>) -> DecodeResult<Self> {
50 ensure_fixed_part_size!(in: src);
51
52 let pcb_size: usize = cast_length!("cbSize", src.read_u32())?;
53
54 if pcb_size < Self::FIXED_PART_SIZE {
55 return Err(invalid_field_err(
56 Self::NAME,
57 "cbSize",
58 "advertised size too small for Preconnection PDU V1",
59 ));
60 }
61
62 read_padding!(src, 4); let version = PcbVersion(src.read_u32());
68
69 let id = src.read_u32();
70
71 let remaining_size = pcb_size - Self::FIXED_PART_SIZE;
72
73 ensure_size!(in: src, size: remaining_size);
74
75 if remaining_size >= 2 {
76 let cch_pcb = usize::from(src.read_u16());
77 let cb_pcb = cch_pcb * 2;
78
79 if remaining_size - 2 < cb_pcb {
80 return Err(invalid_field_err(
81 Self::NAME,
82 "cchPCB",
83 "PCB string bigger than advertised size",
84 ));
85 }
86
87 let wsz_pcb_utf16 = src.read_slice(cb_pcb);
88
89 let payload = crate::utf16::read_utf16_string(wsz_pcb_utf16, Some(cch_pcb))
90 .map_err(|e| invalid_field_err_with_source(Self::NAME, "wszPCB", "bad UTF-16 string", e))?;
91
92 let leftover_size = remaining_size - 2 - cb_pcb;
93 src.advance(leftover_size); Ok(Self {
96 version,
97 id,
98 v2_payload: Some(payload),
99 })
100 } else {
101 Ok(Self {
102 version,
103 id,
104 v2_payload: None,
105 })
106 }
107 }
108}
109
110impl Encode for PreconnectionBlob {
111 fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
112 if self.v2_payload.is_some() && self.version == PcbVersion::V1 {
113 return Err(invalid_field_err(
114 Self::NAME,
115 "version",
116 "there is no string payload in Preconnection PDU V1",
117 ));
118 }
119
120 let pcb_size = self.size();
121
122 ensure_size!(in: dst, size: pcb_size);
123
124 dst.write_u32(cast_length!("cbSize", pcb_size)?); write_padding!(dst, 4); dst.write_u32(self.version.0); dst.write_u32(self.id); if let Some(v2_payload) = &self.v2_payload {
130 let utf16_character_count = v2_payload.chars().count() + 1; dst.write_u16(cast_length!("cchPCB", utf16_character_count)?);
133
134 v2_payload.encode_utf16().for_each(|c| dst.write_u16(c));
136 dst.write_u16(0); }
138
139 Ok(())
140 }
141
142 fn name(&self) -> &'static str {
143 Self::NAME
144 }
145
146 fn size(&self) -> usize {
147 let fixed_part_size = Self::FIXED_PART_SIZE;
148
149 let variable_part = if let Some(v2_payload) = &self.v2_payload {
150 let utf16_encoded_len = crate::utf16::null_terminated_utf16_encoded_len(v2_payload);
151 2 + utf16_encoded_len
152 } else {
153 0
154 };
155
156 fixed_part_size + variable_part
157 }
158}