1#![doc = include_str!("../README.md")]
2#![doc(html_logo_url = "https://cdnweb.devolutions.net/images/projects/devolutions/logos/devolutions-icon-shadow.svg")]
3#![allow(clippy::arithmetic_side_effects)] #![allow(clippy::cast_lossless)] #![allow(clippy::cast_possible_truncation)] #![allow(clippy::cast_possible_wrap)] #![allow(clippy::cast_sign_loss)] use core::fmt;
10
11use ironrdp_error::Source;
14
15#[macro_use]
16mod macros;
17
18pub mod codecs;
19pub mod gcc;
20pub mod geometry;
21pub mod input;
22pub mod mcs;
23pub mod nego;
24pub mod padding;
25pub mod pcb;
26pub mod rdp;
27pub mod tpdu;
28pub mod tpkt;
29pub mod utf16;
30pub mod utils;
31pub mod x224;
32
33pub(crate) mod basic_output;
34pub(crate) mod ber;
35pub(crate) mod crypto;
36pub(crate) mod per;
37
38pub use crate::basic_output::{bitmap, fast_path, pointer, surface_commands};
39pub use crate::rdp::vc::dvc;
40
41pub type PduResult<T> = Result<T, PduError>;
42
43pub type PduError = ironrdp_error::Error<PduErrorKind>;
44
45#[non_exhaustive]
46#[derive(Clone, Debug)]
47pub enum PduErrorKind {
48 Encode,
49 Decode,
50 Other { description: &'static str },
51}
52
53pub trait PduErrorExt {
54 fn decode<E: Source>(context: &'static str, source: E) -> Self;
55
56 fn encode<E: Source>(context: &'static str, source: E) -> Self;
57}
58
59impl PduErrorExt for PduError {
60 fn decode<E: Source>(context: &'static str, source: E) -> Self {
61 Self::new(context, PduErrorKind::Decode).with_source(source)
62 }
63
64 fn encode<E: Source>(context: &'static str, source: E) -> Self {
65 Self::new(context, PduErrorKind::Encode).with_source(source)
66 }
67}
68
69impl std::error::Error for PduErrorKind {}
70
71impl fmt::Display for PduErrorKind {
72 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
73 match self {
74 Self::Encode => {
75 write!(f, "encode error")
76 }
77 Self::Decode => {
78 write!(f, "decode error")
79 }
80 Self::Other { description } => {
81 write!(f, "other ({description})")
82 }
83 }
84 }
85}
86
87pub trait Pdu {
89 const NAME: &'static str;
91}
92
93#[derive(Debug, Copy, Clone, PartialEq, Eq)]
94#[repr(u8)]
95pub enum Action {
96 FastPath = 0x00,
97 X224 = 0x03,
98}
99
100impl Action {
101 pub fn from_fp_output_header(fp_output_header: u8) -> Result<Self, u8> {
102 match fp_output_header & 0b11 {
103 0x00 => Ok(Self::FastPath),
104 0x03 => Ok(Self::X224),
105 unknown_action_bits => Err(unknown_action_bits),
106 }
107 }
108
109 pub fn as_u8(self) -> u8 {
110 self as u8
111 }
112}
113
114#[derive(Debug, Copy, Clone, PartialEq, Eq)]
115pub struct PduInfo {
116 pub action: Action,
117 pub length: usize,
118}
119
120pub fn find_size(bytes: &[u8]) -> DecodeResult<Option<PduInfo>> {
122 macro_rules! ensure_enough {
123 ($bytes:expr, $len:expr) => {
124 if $bytes.len() < $len {
125 return Ok(None);
126 }
127 };
128 }
129
130 ensure_enough!(bytes, 1);
131 let fp_output_header = bytes[0];
132
133 let action = Action::from_fp_output_header(fp_output_header)
134 .map_err(|unknown_action| unexpected_message_type_err("fpOutputHeader", unknown_action))?;
135
136 match action {
137 Action::X224 => {
138 ensure_enough!(bytes, tpkt::TpktHeader::SIZE);
139 let tpkt = tpkt::TpktHeader::read(&mut ReadCursor::new(bytes))?;
140
141 Ok(Some(PduInfo {
142 action,
143 length: tpkt.packet_length(),
144 }))
145 }
146 Action::FastPath => {
147 ensure_enough!(bytes, 2);
148 let a = bytes[1];
149
150 let fast_path_length = if a & 0x80 != 0 {
151 ensure_enough!(bytes, 3);
152 let b = bytes[2];
153
154 ((u16::from(a) & !0x80) << 8) + u16::from(b)
155 } else {
156 u16::from(a)
157 };
158
159 Ok(Some(PduInfo {
160 action,
161 length: usize::from(fast_path_length),
162 }))
163 }
164 }
165}
166
167pub trait PduHint: Send + Sync + fmt::Debug + 'static {
168 fn find_size(&self, bytes: &[u8]) -> DecodeResult<Option<(bool, usize)>>;
173}
174
175#[derive(Clone, Copy, Debug)]
177pub struct RdpHint;
178
179pub const RDP_HINT: RdpHint = RdpHint;
180
181impl PduHint for RdpHint {
182 fn find_size(&self, bytes: &[u8]) -> DecodeResult<Option<(bool, usize)>> {
183 find_size(bytes).map(|opt| opt.map(|info| (true, info.length)))
184 }
185}
186
187#[derive(Clone, Copy, Debug)]
188pub struct X224Hint;
189
190pub const X224_HINT: X224Hint = X224Hint;
191
192impl PduHint for X224Hint {
193 fn find_size(&self, bytes: &[u8]) -> DecodeResult<Option<(bool, usize)>> {
194 match find_size(bytes)? {
195 Some(pdu_info) => {
196 let res = (pdu_info.action == Action::X224, pdu_info.length);
197 Ok(Some(res))
198 }
199 None => Ok(None),
200 }
201 }
202}
203
204#[derive(Clone, Copy, Debug)]
205pub struct FastPathHint;
206
207pub const FAST_PATH_HINT: FastPathHint = FastPathHint;
208
209impl PduHint for FastPathHint {
210 fn find_size(&self, bytes: &[u8]) -> DecodeResult<Option<(bool, usize)>> {
211 match find_size(bytes)? {
212 Some(pdu_info) => {
213 let res = (pdu_info.action == Action::FastPath, pdu_info.length);
214 Ok(Some(res))
215 }
216 None => Ok(None),
217 }
218 }
219}
220
221#[doc(hidden)]
223pub use ironrdp_core;
224
225#[doc(hidden)]
229#[deprecated(since = "0.1.0", note = "use ironrdp_core::{ReadCursor, WriteCursor}")]
230pub mod cursor {
231 pub use ironrdp_core::ReadCursor;
232 pub use ironrdp_core::WriteCursor;
233}
234
235#[doc(hidden)]
236#[deprecated(since = "0.1.0", note = "use ironrdp_core::WriteBuf")]
237pub mod write_buf {
238 pub use ironrdp_core::WriteBuf;
239}
240
241#[doc(hidden)]
242#[deprecated(since = "0.1.0", note = "use ironrdp_core")]
243pub use ironrdp_core::*;
244
245#[doc(hidden)]
246#[deprecated(since = "0.1.0")]
247#[macro_export]
248macro_rules! custom_err {
249 ( $description:expr, $source:expr $(,)? ) => {{
250 $crate::PduError::new(
251 $description,
252 $crate::PduErrorKind::Other {
253 description: $description,
254 },
255 )
256 .with_source($source)
257 }};
258 ( $source:expr $(,)? ) => {{
259 $crate::custom_err!($crate::function!(), $source)
260 }};
261}
262
263#[doc(hidden)]
264#[deprecated(since = "0.1.0", note = "use ironrdp_core::other_err")]
265pub use crate::pdu_other_err as other_err;