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