#![doc = include_str!("../README.md")]
#![doc(html_logo_url = "https://cdnweb.devolutions.net/images/projects/devolutions/logos/devolutions-icon-shadow.svg")]
#![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;
use ironrdp_error::Source;
#[macro_use]
mod macros;
pub mod codecs;
pub mod gcc;
pub mod geometry;
pub mod input;
pub mod mcs;
pub mod nego;
pub mod padding;
pub mod pcb;
pub mod rdp;
pub mod tpdu;
pub mod tpkt;
pub mod utf16;
pub mod utils;
pub mod x224;
pub(crate) mod basic_output;
pub(crate) mod ber;
pub(crate) mod crypto;
pub(crate) mod per;
pub use crate::basic_output::{bitmap, fast_path, pointer, surface_commands};
pub use crate::rdp::vc::dvc;
pub type PduResult<T> = Result<T, PduError>;
pub type PduError = ironrdp_error::Error<PduErrorKind>;
#[non_exhaustive]
#[derive(Clone, Debug)]
pub enum PduErrorKind {
Encode,
Decode,
Other { description: &'static str },
}
pub trait PduErrorExt {
fn decode<E: Source>(context: &'static str, source: E) -> Self;
fn encode<E: Source>(context: &'static str, source: E) -> Self;
}
impl PduErrorExt for PduError {
fn decode<E: Source>(context: &'static str, source: E) -> Self {
Self::new(context, PduErrorKind::Decode).with_source(source)
}
fn encode<E: Source>(context: &'static str, source: E) -> Self {
Self::new(context, PduErrorKind::Encode).with_source(source)
}
}
impl std::error::Error for PduErrorKind {}
impl fmt::Display for PduErrorKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Encode => {
write!(f, "encode error")
}
Self::Decode => {
write!(f, "decode error")
}
Self::Other { description } => {
write!(f, "other ({description})")
}
}
}
}
pub trait Pdu {
const NAME: &'static str;
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[repr(u8)]
pub enum Action {
FastPath = 0x00,
X224 = 0x03,
}
impl Action {
pub fn from_fp_output_header(fp_output_header: u8) -> Result<Self, u8> {
match fp_output_header & 0b11 {
0x00 => Ok(Self::FastPath),
0x03 => Ok(Self::X224),
unknown_action_bits => Err(unknown_action_bits),
}
}
pub fn as_u8(self) -> u8 {
self as u8
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct PduInfo {
pub action: Action,
pub length: usize,
}
pub fn find_size(bytes: &[u8]) -> DecodeResult<Option<PduInfo>> {
macro_rules! ensure_enough {
($bytes:expr, $len:expr) => {
if $bytes.len() < $len {
return Ok(None);
}
};
}
ensure_enough!(bytes, 1);
let fp_output_header = bytes[0];
let action = Action::from_fp_output_header(fp_output_header)
.map_err(|unknown_action| unexpected_message_type_err("fpOutputHeader", unknown_action))?;
match action {
Action::X224 => {
ensure_enough!(bytes, tpkt::TpktHeader::SIZE);
let tpkt = tpkt::TpktHeader::read(&mut ReadCursor::new(bytes))?;
Ok(Some(PduInfo {
action,
length: tpkt.packet_length(),
}))
}
Action::FastPath => {
ensure_enough!(bytes, 2);
let a = bytes[1];
let fast_path_length = if a & 0x80 != 0 {
ensure_enough!(bytes, 3);
let b = bytes[2];
((u16::from(a) & !0x80) << 8) + u16::from(b)
} else {
u16::from(a)
};
Ok(Some(PduInfo {
action,
length: usize::from(fast_path_length),
}))
}
}
}
pub trait PduHint: Send + Sync + fmt::Debug + 'static {
fn find_size(&self, bytes: &[u8]) -> DecodeResult<Option<(bool, usize)>>;
}
#[derive(Clone, Copy, Debug)]
pub struct RdpHint;
pub const RDP_HINT: RdpHint = RdpHint;
impl PduHint for RdpHint {
fn find_size(&self, bytes: &[u8]) -> DecodeResult<Option<(bool, usize)>> {
find_size(bytes).map(|opt| opt.map(|info| (true, info.length)))
}
}
#[derive(Clone, Copy, Debug)]
pub struct X224Hint;
pub const X224_HINT: X224Hint = X224Hint;
impl PduHint for X224Hint {
fn find_size(&self, bytes: &[u8]) -> DecodeResult<Option<(bool, usize)>> {
match find_size(bytes)? {
Some(pdu_info) => {
let res = (pdu_info.action == Action::X224, pdu_info.length);
Ok(Some(res))
}
None => Ok(None),
}
}
}
#[derive(Clone, Copy, Debug)]
pub struct FastPathHint;
pub const FAST_PATH_HINT: FastPathHint = FastPathHint;
impl PduHint for FastPathHint {
fn find_size(&self, bytes: &[u8]) -> DecodeResult<Option<(bool, usize)>> {
match find_size(bytes)? {
Some(pdu_info) => {
let res = (pdu_info.action == Action::FastPath, pdu_info.length);
Ok(Some(res))
}
None => Ok(None),
}
}
}
pub use legacy::*;
mod legacy {
pub trait PduBufferParsing<'a>: Sized {
type Error;
fn from_buffer(mut buffer: &'a [u8]) -> Result<Self, Self::Error> {
Self::from_buffer_consume(&mut buffer)
}
fn from_buffer_consume(buffer: &mut &'a [u8]) -> Result<Self, Self::Error>;
fn to_buffer_consume(&self, buffer: &mut &mut [u8]) -> Result<(), Self::Error>;
fn buffer_length(&self) -> usize;
}
}
#[doc(hidden)]
pub use ironrdp_core;
#[doc(hidden)]
#[deprecated(since = "0.1.0", note = "use ironrdp_core::{ReadCursor, WriteCursor}")]
pub mod cursor {
pub use ironrdp_core::ReadCursor;
pub use ironrdp_core::WriteCursor;
}
#[doc(hidden)]
#[deprecated(since = "0.1.0", note = "use ironrdp_core::WriteBuf")]
pub mod write_buf {
pub use ironrdp_core::WriteBuf;
}
#[doc(hidden)]
#[deprecated(since = "0.1.0", note = "use ironrdp_core")]
pub use ironrdp_core::*;
#[doc(hidden)]
#[deprecated(since = "0.1.0")]
#[macro_export]
macro_rules! custom_err {
( $description:expr, $source:expr $(,)? ) => {{
$crate::PduError::new(
$description,
$crate::PduErrorKind::Other {
description: $description,
},
)
.with_source($source)
}};
( $source:expr $(,)? ) => {{
$crate::custom_err!($crate::function!(), $source)
}};
}
#[doc(hidden)]
#[deprecated(since = "0.1.0", note = "use ironrdp_core::other_err")]
pub use crate::pdu_other_err as other_err;