Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions crates/ironrdp-pdu/src/basic_output/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
pub mod bitmap;
pub mod fast_path;
pub mod pointer;
pub mod slow_path;
pub mod surface_commands;
126 changes: 126 additions & 0 deletions crates/ironrdp-pdu/src/basic_output/slow_path.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
// Slow-path graphics and pointer update parsing.
//
// Slow-path updates arrive inside ShareDataPdu::Update (graphics) and
// ShareDataPdu::Pointer, wrapped with a small framing header that differs
// from the fast-path encoding. The inner payload structures are identical
// to their fast-path counterparts.
//
// References:
// [MS-RDPBCGR] 2.2.9.1.1.3 — Slow-Path Graphics Update
// [MS-RDPBCGR] 2.2.9.1.1.4 — Slow-Path Pointer Update

use ironrdp_core::{ensure_size, invalid_field_err, Decode as _, DecodeResult, ReadCursor};

use super::bitmap::BitmapUpdateData;
use super::pointer::{
CachedPointerAttribute, ColorPointerAttribute, LargePointerAttribute, PointerAttribute, PointerPositionAttribute,
PointerUpdateData,
};

// --- Graphics updates ([MS-RDPBCGR] 2.2.9.1.1.3.1) ---

/// `updateType` field in TS_UPDATE_HDR for slow-path graphics updates.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u16)]
pub enum GraphicsUpdateType {
Orders = 0x0000,
Bitmap = 0x0001,
Palette = 0x0002,
Synchronize = 0x0003,
}

/// Read the `updateType` u16 from the front of a slow-path graphics update.
pub fn read_graphics_update_type(src: &mut ReadCursor<'_>) -> DecodeResult<GraphicsUpdateType> {
ensure_size!(in: src, size: 2);
let raw = src.read_u16();
match raw {
0x0000 => Ok(GraphicsUpdateType::Orders),
0x0001 => Ok(GraphicsUpdateType::Bitmap),
0x0002 => Ok(GraphicsUpdateType::Palette),
0x0003 => Ok(GraphicsUpdateType::Synchronize),
_ => Err(invalid_field_err!(
"updateType",
"unknown slow-path graphics update type"
)),
}
}

/// Decode a slow-path bitmap update.
///
/// The cursor must be positioned right after the `updateType` field
/// (i.e. already consumed by [`read_graphics_update_type`]).
pub fn decode_slow_path_bitmap<'a>(src: &mut ReadCursor<'a>) -> DecodeResult<BitmapUpdateData<'a>> {
BitmapUpdateData::decode(src)
}

// --- Pointer updates ([MS-RDPBCGR] 2.2.9.1.1.4) ---

/// `messageType` values for slow-path pointer updates.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u16)]
pub enum PointerMessageType {
System = 0x0001,
Position = 0x0003,
Color = 0x0006,
Cached = 0x0007,
/// TS_POINTERATTRIBUTE (new pointer with xor_bpp)
Pointer = 0x0008,
Large = 0x0009,
}

/// `systemPointerType` values used when `messageType == System`.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u32)]
pub enum SystemPointerType {
/// SYSPTR_NULL — hide the pointer
Null = 0x0000_0000,
/// SYSPTR_DEFAULT — show the OS default pointer
Default = 0x0000_7F00,
}

/// Decode a complete slow-path pointer update from its raw payload.
///
/// The payload starts with `messageType(u16)` + `pad2Octets(u16)`,
/// followed by the type-specific data.
pub fn decode_slow_path_pointer<'a>(src: &mut ReadCursor<'a>) -> DecodeResult<PointerUpdateData<'a>> {
ensure_size!(in: src, size: 4);
let message_type = src.read_u16();
let _pad = src.read_u16();

match message_type {
0x0001 => {
// System pointer: the body is a single u32 indicating which system pointer.
ensure_size!(in: src, size: 4);
let system_type = src.read_u32();
match system_type {
0x0000_0000 => Ok(PointerUpdateData::SetHidden),
0x0000_7F00 => Ok(PointerUpdateData::SetDefault),
_ => Err(invalid_field_err!("systemPointerType", "unknown system pointer type")),
}
}
0x0003 => {
let pos = PointerPositionAttribute::decode(src)?;
Ok(PointerUpdateData::SetPosition(pos))
}
0x0006 => {
let color = ColorPointerAttribute::decode(src)?;
Ok(PointerUpdateData::Color(color))
}
0x0007 => {
let cached = CachedPointerAttribute::decode(src)?;
Ok(PointerUpdateData::Cached(cached))
}
0x0008 => {
let attr = PointerAttribute::decode(src)?;
Ok(PointerUpdateData::New(attr))
}
0x0009 => {
let large = LargePointerAttribute::decode(src)?;
Ok(PointerUpdateData::Large(large))
}
_ => Err(invalid_field_err!(
"messageType",
"unknown slow-path pointer message type"
)),
}
}
2 changes: 1 addition & 1 deletion crates/ironrdp-pdu/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ 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::basic_output::{bitmap, fast_path, pointer, slow_path, surface_commands};
pub use crate::rdp::vc::dvc;

pub type PduResult<T> = Result<T, PduError>;
Expand Down
78 changes: 69 additions & 9 deletions crates/ironrdp-session/src/active_stage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::sync::Arc;
use ironrdp_bulk::BulkCompressor;
use ironrdp_connector::connection_activation::ConnectionActivationSequence;
use ironrdp_connector::ConnectionResult;
use ironrdp_core::WriteBuf;
use ironrdp_core::{ReadCursor, WriteBuf};
use ironrdp_displaycontrol::client::DisplayControlClient;
use ironrdp_dvc::{DrdynvcClient, DvcProcessor, DynamicVirtualChannel};
use ironrdp_graphics::pointer::DecodedPointer;
Expand All @@ -12,9 +12,10 @@ use ironrdp_pdu::input::fast_path::{FastPathInput, FastPathInputEvent};
use ironrdp_pdu::rdp::client_info::CompressionType as PduCompressionType;
use ironrdp_pdu::rdp::headers::ShareDataPdu;
use ironrdp_pdu::rdp::multitransport::MultitransportRequestPdu;
use ironrdp_pdu::slow_path::{self, GraphicsUpdateType};
use ironrdp_pdu::{mcs, Action};
use ironrdp_svc::{SvcMessage, SvcProcessor, SvcProcessorMessages};
use tracing::{debug, info};
use tracing::{debug, info, warn};

use crate::fast_path::UpdateKind;
use crate::image::DecodedImage;
Expand Down Expand Up @@ -144,13 +145,27 @@ impl ActiveStage {
)
}
Action::X224 => {
let outputs = self
.x224_processor
.process(frame)?
.into_iter()
.map(TryFrom::try_from)
.collect::<Result<Vec<_>, _>>()?;
(outputs, Vec::new())
let x224_outputs = self.x224_processor.process(frame)?;
let mut stage_outputs = Vec::new();
let mut processor_updates = Vec::new();

for output in x224_outputs {
match output {
x224::ProcessorOutput::GraphicsUpdate(data) => {
let updates = process_slow_path_graphics(&mut self.fast_path_processor, image, &data)?;
processor_updates.extend(updates);
}
x224::ProcessorOutput::PointerUpdate(data) => {
let updates = process_slow_path_pointer(&mut self.fast_path_processor, image, &data)?;
processor_updates.extend(updates);
}
other => {
stage_outputs.push(ActiveStageOutput::try_from(other)?);
}
}
}

(stage_outputs, processor_updates)
}
};

Expand Down Expand Up @@ -326,6 +341,11 @@ impl TryFrom<x224::ProcessorOutput> for ActiveStageOutput {
}
x224::ProcessorOutput::DeactivateAll(cas) => Ok(Self::DeactivateAll(cas)),
x224::ProcessorOutput::MultitransportRequest(pdu) => Ok(Self::MultitransportRequest(pdu)),
// GraphicsUpdate and PointerUpdate are consumed in ActiveStage::process()
// before reaching this conversion.
x224::ProcessorOutput::GraphicsUpdate(_) | x224::ProcessorOutput::PointerUpdate(_) => {
unreachable!("slow-path updates are handled directly in ActiveStage::process()")
}
}
}
}
Expand Down Expand Up @@ -354,3 +374,43 @@ impl core::fmt::Display for GracefulDisconnectReason {
f.write_str(&self.description())
}
}

/// Parse and process a slow-path graphics update through the shared bitmap pipeline.
fn process_slow_path_graphics(
fast_path_processor: &mut fast_path::Processor,
image: &mut DecodedImage,
data: &[u8],
) -> SessionResult<Vec<UpdateKind>> {
let mut src = ReadCursor::new(data);
let update_type = slow_path::read_graphics_update_type(&mut src).map_err(SessionError::decode)?;

match update_type {
GraphicsUpdateType::Bitmap => {
let bitmap = slow_path::decode_slow_path_bitmap(&mut src).map_err(SessionError::decode)?;
fast_path_processor.process_bitmap_update(image, bitmap)
}
GraphicsUpdateType::Orders => {
warn!("Slow-path drawing orders not supported (MS-RDPEGDI)");
Ok(Vec::new())
}
GraphicsUpdateType::Palette => {
warn!("Slow-path palette update not supported (8bpp)");
Ok(Vec::new())
}
GraphicsUpdateType::Synchronize => {
debug!("Ignoring slow-path synchronize update");
Ok(Vec::new())
}
}
}

/// Parse and process a slow-path pointer update through the shared pointer pipeline.
fn process_slow_path_pointer(
fast_path_processor: &mut fast_path::Processor,
image: &mut DecodedImage,
data: &[u8],
) -> SessionResult<Vec<UpdateKind>> {
let mut src = ReadCursor::new(data);
let pointer = slow_path::decode_slow_path_pointer(&mut src).map_err(SessionError::decode)?;
fast_path_processor.process_pointer_update(image, pointer)
}
Loading
Loading