diff --git a/crates/ironrdp-dvc/src/client.rs b/crates/ironrdp-dvc/src/client.rs index 22ca79bfb..174f33c96 100644 --- a/crates/ironrdp-dvc/src/client.rs +++ b/crates/ironrdp-dvc/src/client.rs @@ -5,6 +5,7 @@ use core::any::TypeId; use core::fmt; use crate::alloc::borrow::ToOwned as _; +use crate::complete_data::CompleteData; use ironrdp_core::{Decode as _, DecodeResult, ReadCursor, impl_as_any}; use ironrdp_pdu::{self as pdu, decode_err, encode_err, pdu_other_err}; use ironrdp_svc::{ChannelFlags, CompressionCondition, SvcClientProcessor, SvcMessage, SvcProcessor}; @@ -14,9 +15,12 @@ use tracing::debug; use crate::pdu::{ CapabilitiesResponsePdu, CapsVersion, ClosePdu, CreateResponsePdu, CreationStatus, DrdynvcClientPdu, - DrdynvcServerPdu, + DrdynvcDataPdu, DrdynvcServerPdu, +}; +use crate::{ + DvcMessage, DvcProcessor, DynamicChannelId, DynamicChannelMut, DynamicChannelName, DynamicChannelRef, + encode_dvc_messages, }; -use crate::{DvcProcessor, DynamicChannelId, DynamicChannelName, DynamicVirtualChannel, encode_dvc_messages}; pub trait DvcClientProcessor: DvcProcessor {} @@ -25,18 +29,18 @@ pub trait DvcChannelListener: Send { /// Called for each incoming DYNVC_CREATE_REQ matching this name. /// Return `None` to reject (NO_LISTENER). - fn create(&mut self, channel_id: DynamicChannelId) -> Option>; + fn create(&mut self, channel_id: DynamicChannelId) -> Option>; } pub type DynamicChannelListener = Box; /// For pre-registered DVC struct OnceListener { - inner: Option>, + inner: Option>, } impl OnceListener { - fn new(dvc_processor: impl DvcProcessor + 'static) -> Self { + fn new(dvc_processor: impl DvcClientProcessor + 'static) -> Self { Self { inner: Some(Box::new(dvc_processor)), } @@ -51,11 +55,62 @@ impl DvcChannelListener for OnceListener { .channel_name() } - fn create(&mut self, _channel_id: DynamicChannelId) -> Option> { + fn create(&mut self, _channel_id: DynamicChannelId) -> Option> { self.inner.take() } } +struct DynamicVirtualChannel { + channel_processor: Box, + complete_data: CompleteData, + /// The channel ID assigned by the server. + /// + /// `Some` only after [`DynamicVirtualChannel::start`] has succeeded. This invariant + channel_id: Option, +} + +impl Drop for DynamicVirtualChannel { + fn drop(&mut self) { + if let Some(id) = self.channel_id { + self.channel_processor.close(id); + } + } +} + +impl DynamicVirtualChannel { + fn from_boxed(processor: Box) -> Self { + Self { + channel_processor: processor, + complete_data: CompleteData::new(), + channel_id: None, + } + } + + fn processor_type_id(&self) -> TypeId { + self.channel_processor.as_any().type_id() + } + + fn start(&mut self, channel_id: DynamicChannelId) -> PduResult> { + let messages = self.channel_processor.start(channel_id)?; + self.channel_id = Some(channel_id); + Ok(messages) + } + + fn process(&mut self, pdu: DrdynvcDataPdu) -> PduResult> { + let channel_id = pdu.channel_id(); + let complete_data = self.complete_data.process_data(pdu).map_err(|e| decode_err!(e))?; + if let Some(complete_data) = complete_data { + self.channel_processor.process(channel_id, &complete_data) + } else { + Ok(Vec::new()) + } + } + + fn channel_name(&self) -> &str { + self.channel_processor.channel_name() + } +} + /// DRDYNVC Static Virtual Channel (the Remote Desktop Protocol: Dynamic Virtual Channel Extension) /// /// It adds support for dynamic virtual channels (DVC). @@ -100,7 +155,7 @@ impl DrdynvcClient { #[must_use] pub fn with_dynamic_channel(mut self, channel: T) -> Self where - T: DvcProcessor + 'static, + T: DvcClientProcessor + 'static, { self.dynamic_channels.register_once(channel); self @@ -115,7 +170,7 @@ impl DrdynvcClient { /// it will be silently overwritten. pub fn attach_dynamic_channel(&mut self, channel: T) where - T: DvcProcessor + 'static, + T: DvcClientProcessor + 'static, { self.dynamic_channels.register_once(channel); } @@ -124,7 +179,7 @@ impl DrdynvcClient { /// /// # Note /// - /// * Doesn't support [TypeId] lookup via [DrdynvcClient::get_dvc_by_type_id]. + /// * Doesn't support [TypeId] lookup via [DrdynvcClient::get_dvc]. /// * If a listener or a pre-registered channel with the same name already exists, /// it will be silently overwritten. #[must_use] @@ -140,7 +195,7 @@ impl DrdynvcClient { /// /// # Note /// - /// * Doesn't support [TypeId] lookup via [DrdynvcClient::get_dvc_by_type_id]. + /// * Doesn't support [TypeId] lookup via [DrdynvcClient::get_dvc]. /// * If a listener or a pre-registered channel with the same name already exists, /// it will be silently overwritten. pub fn attach_listener(&mut self, listener: T) @@ -150,19 +205,50 @@ impl DrdynvcClient { self.dynamic_channels.register_listener(listener); } - pub fn get_dvc_by_type_id(&self) -> Option<&DynamicVirtualChannel> + /// Returns a typed accessor for a pre-registered client DVC. + /// + /// Type lookup is available only for channels registered with + /// [`DrdynvcClient::with_dynamic_channel`] or [`DrdynvcClient::attach_dynamic_channel`]. + /// Listener-created channels can be retrieved with [`DrdynvcClient::get_dvc_by_channel_id`]. + /// + /// Returns `None` until the server has created the channel and the processor has started. + pub fn get_dvc(&self) -> Option> where - T: DvcProcessor, + T: DvcClientProcessor, { - self.dynamic_channels.get_by_type_id(TypeId::of::()) + let dvc_channel = self.dynamic_channels.get_by_type_id(TypeId::of::())?; + let channel_id = dvc_channel.channel_id?; + dvc_channel + .channel_processor + .as_any() + .downcast_ref() + .map(|p| DynamicChannelRef::new(channel_id, p)) } - pub fn get_dvc_by_channel_id(&self, channel_id: u32) -> Option<&DynamicVirtualChannel> { - self.dynamic_channels.get_by_channel_id(channel_id) + /// Returns a typed accessor for an active client DVC by channel ID. + /// + /// Returns `None` when the channel ID is unknown or the processor has a different type. + pub fn get_dvc_by_channel_id(&self, channel_id: u32) -> Option> + where + T: DvcClientProcessor, + { + self.dynamic_channels + .get_by_channel_id(channel_id) + .and_then(|dvc| dvc.channel_processor.as_any().downcast_ref()) + .map(|p| DynamicChannelRef::new(channel_id, p)) } - pub fn get_dvc_by_channel_id_mut(&mut self, channel_id: u32) -> Option<&mut DynamicVirtualChannel> { - self.dynamic_channels.get_by_channel_id_mut(channel_id) + /// Returns a mutable typed accessor for an active client DVC by channel ID. + /// + /// Returns `None` when the channel ID is unknown or the processor has a different type. + pub fn get_dvc_by_channel_id_mut(&mut self, channel_id: u32) -> Option> + where + T: DvcClientProcessor, + { + self.dynamic_channels + .get_by_channel_id_mut(channel_id) + .and_then(|dvc| dvc.channel_processor.as_any_mut().downcast_mut()) + .map(|p| DynamicChannelMut::new(channel_id, p)) } fn create_capabilities_response(&mut self, server_version: CapsVersion) -> SvcMessage { @@ -307,7 +393,7 @@ impl DynamicChannelSet { ); } - fn register_once(&mut self, channel: T) { + fn register_once(&mut self, channel: T) { let name = channel.channel_name().to_owned(); self.listeners.insert( name, diff --git a/crates/ironrdp-dvc/src/lib.rs b/crates/ironrdp-dvc/src/lib.rs index 9fd4527b2..33f991bd3 100644 --- a/crates/ironrdp-dvc/src/lib.rs +++ b/crates/ironrdp-dvc/src/lib.rs @@ -4,8 +4,6 @@ extern crate alloc; -use core::any::TypeId; - use alloc::boxed::Box; use alloc::string::String; use alloc::vec::Vec; @@ -16,7 +14,7 @@ use pdu::DrdynvcDataPdu; #[rustfmt::skip] // do not re-order this pub use pub use ironrdp_pdu; use ironrdp_core::{AsAny, Encode, EncodeResult, assert_obj_safe, cast_length, encode_vec, other_err}; -use ironrdp_pdu::{PduResult, decode_err}; +use ironrdp_pdu::PduResult; use ironrdp_svc::SvcMessage; mod complete_data; @@ -100,69 +98,7 @@ pub fn encode_dvc_messages( Ok(res) } -pub struct DynamicVirtualChannel { - channel_processor: Box, - complete_data: CompleteData, - /// The channel ID assigned by the server. - /// - /// `Some` only after [`DynamicVirtualChannel::start`] has succeeded. This invariant - channel_id: Option, -} - -impl Drop for DynamicVirtualChannel { - fn drop(&mut self) { - if let Some(id) = self.channel_id { - self.channel_processor.close(id); - } - } -} - -impl DynamicVirtualChannel { - fn from_boxed(processor: Box) -> Self { - Self { - channel_processor: processor, - complete_data: CompleteData::new(), - channel_id: None, - } - } - - fn processor_type_id(&self) -> TypeId { - self.channel_processor.as_any().type_id() - } - - pub fn is_open(&self) -> bool { - self.channel_id.is_some() - } - - pub fn channel_id(&self) -> Option { - self.channel_id - } - - pub fn channel_processor_downcast_ref(&self) -> Option<&T> { - self.channel_processor.as_any().downcast_ref() - } - - fn start(&mut self, channel_id: DynamicChannelId) -> PduResult> { - let messages = self.channel_processor.start(channel_id)?; - self.channel_id = Some(channel_id); - Ok(messages) - } - - fn process(&mut self, pdu: DrdynvcDataPdu) -> PduResult> { - let channel_id = pdu.channel_id(); - let complete_data = self.complete_data.process_data(pdu).map_err(|e| decode_err!(e))?; - if let Some(complete_data) = complete_data { - self.channel_processor.process(channel_id, &complete_data) - } else { - Ok(Vec::new()) - } - } - - fn channel_name(&self) -> &str { - self.channel_processor.channel_name() - } -} - +/// Borrowed typed view of a dynamic virtual channel. #[derive(Debug, Clone, Copy)] pub struct DynamicChannelRef<'a, T> { channel_id: DynamicChannelId, @@ -185,6 +121,7 @@ impl<'a, T: DvcProcessor> DynamicChannelRef<'a, T> { } } +/// Mutable borrowed typed view of a dynamic virtual channel. #[derive(Debug)] pub struct DynamicChannelMut<'a, T> { channel_id: DynamicChannelId, diff --git a/crates/ironrdp-dvc/src/server.rs b/crates/ironrdp-dvc/src/server.rs index b3d54bba9..b34bd58fc 100644 --- a/crates/ironrdp-dvc/src/server.rs +++ b/crates/ironrdp-dvc/src/server.rs @@ -29,7 +29,7 @@ enum ChannelState { struct DynamicChannel { state: ChannelState, - processor: Box, + processor: Box, complete_data: CompleteData, channel_id: u32, } @@ -186,6 +186,7 @@ impl DrdynvcServer { .ok_or_else(|| invalid_field_err!("DRDYNVC", "", "invalid channel id")) } + /// Returns a typed accessor for an active server DVC by channel ID. pub fn dvc_by_id(&self, id: u32) -> Option> { let channel = self.dynamic_channels.get(id)?; if channel.state != ChannelState::Opened { @@ -198,6 +199,7 @@ impl DrdynvcServer { .map(|p| DynamicChannelRef::new(id, p)) } + /// Returns a mutable typed accessor for an active server DVC by channel ID. pub fn dvc_by_id_mut(&mut self, id: u32) -> Option> { let channel = self.dynamic_channels.get_mut(id)?; if channel.state != ChannelState::Opened { diff --git a/crates/ironrdp-session/src/active_stage.rs b/crates/ironrdp-session/src/active_stage.rs index b7bb82446..65ad25084 100644 --- a/crates/ironrdp-session/src/active_stage.rs +++ b/crates/ironrdp-session/src/active_stage.rs @@ -5,7 +5,7 @@ use ironrdp_connector::ConnectionResult; use ironrdp_connector::connection_activation::ConnectionActivationSequence; use ironrdp_core::{ReadCursor, WriteBuf}; use ironrdp_displaycontrol::client::DisplayControlClient; -use ironrdp_dvc::{DrdynvcClient, DvcProcessor, DynamicVirtualChannel}; +use ironrdp_dvc::{DrdynvcClient, DvcClientProcessor, DynamicChannelRef}; use ironrdp_graphics::pointer::DecodedPointer; use ironrdp_pdu::geometry::InclusiveRectangle; use ironrdp_pdu::input::fast_path::{FastPathInput, FastPathInputEvent}; @@ -237,11 +237,14 @@ impl ActiveStage { self.x224_processor.get_svc_processor_mut() } - pub fn get_dvc(&mut self) -> Option<&DynamicVirtualChannel> { + pub fn get_dvc(&mut self) -> Option> { self.x224_processor.get_dvc::() } - pub fn get_dvc_by_channel_id(&mut self, channel_id: u32) -> Option<&DynamicVirtualChannel> { + pub fn get_dvc_by_channel_id( + &mut self, + channel_id: u32, + ) -> Option> { self.x224_processor.get_dvc_by_channel_id(channel_id) } @@ -277,25 +280,20 @@ impl ActiveStage { physical_dims: Option<(u32, u32)>, ) -> Option>> { if let Some(dvc) = self.get_dvc::() { - if let Some(channel_id) = dvc.channel_id() { - let display_control = dvc.channel_processor_downcast_ref::()?; - let svc_messages = match display_control.encode_single_primary_monitor( - channel_id, - width, - height, - scale_factor, - physical_dims, - ) { - Ok(messages) => messages, - Err(e) => return Some(Err(SessionError::encode(e))), - }; - - return Some( - self.process_svc_processor_messages(SvcProcessorMessages::::new(svc_messages)), - ); - } else { - debug!("Could not encode a resize: Display Control Virtual Channel is not yet connected"); - } + let channel_id = dvc.channel_id(); + let display_control = dvc.processor(); + let svc_messages = match display_control.encode_single_primary_monitor( + channel_id, + width, + height, + scale_factor, + physical_dims, + ) { + Ok(messages) => messages, + Err(e) => return Some(Err(SessionError::encode(e))), + }; + + return Some(self.process_svc_processor_messages(SvcProcessorMessages::::new(svc_messages))); } else { debug!("Could not encode a resize: Display Control Virtual Channel is not available"); } diff --git a/crates/ironrdp-session/src/x224/mod.rs b/crates/ironrdp-session/src/x224/mod.rs index 529aa397d..10a6abe3f 100644 --- a/crates/ironrdp-session/src/x224/mod.rs +++ b/crates/ironrdp-session/src/x224/mod.rs @@ -1,7 +1,7 @@ use ironrdp_connector::connection_activation::ConnectionActivationSequence; use ironrdp_connector::legacy::SendDataIndicationCtx; use ironrdp_core::WriteBuf; -use ironrdp_dvc::{DrdynvcClient, DvcProcessor, DynamicVirtualChannel}; +use ironrdp_dvc::{DrdynvcClient, DvcClientProcessor, DynamicChannelRef}; use ironrdp_pdu::mcs::{DisconnectProviderUltimatum, DisconnectReason, McsMessage}; use ironrdp_pdu::rdp::autodetect::{AutoDetectRequest, AutoDetectResponse}; use ironrdp_pdu::rdp::headers::ShareDataPdu; @@ -116,11 +116,14 @@ impl Processor { process_svc_messages(messages.into(), channel_id, self.user_channel_id) } - pub fn get_dvc(&self) -> Option<&DynamicVirtualChannel> { - self.get_svc_processor::()?.get_dvc_by_type_id::() + pub fn get_dvc(&self) -> Option> { + self.get_svc_processor::()?.get_dvc::() } - pub fn get_dvc_by_channel_id(&self, channel_id: u32) -> Option<&DynamicVirtualChannel> { + pub fn get_dvc_by_channel_id( + &self, + channel_id: u32, + ) -> Option> { self.get_svc_processor::()? .get_dvc_by_channel_id(channel_id) } diff --git a/ffi/src/connector/mod.rs b/ffi/src/connector/mod.rs index ab2b7b805..442a77181 100644 --- a/ffi/src/connector/mod.rs +++ b/ffi/src/connector/mod.rs @@ -10,7 +10,7 @@ pub mod ffi { use diplomat_runtime::DiplomatWriteable; use ironrdp::connector::Sequence as _; use ironrdp::displaycontrol::client::DisplayControlClient; - use ironrdp::dvc::DvcProcessor; + use ironrdp::dvc::DvcClientProcessor; use ironrdp_dvc_pipe_proxy::DvcNamedPipeProxy; use tracing::info; @@ -74,7 +74,7 @@ pub mod ffi { fn with_dvc(&mut self, processor: T) -> Result<(), Box> where - T: DvcProcessor + 'static, + T: DvcClientProcessor + 'static, { let Some(connector) = &mut self.0 else { return Err(ValueConsumedError::for_item("connector").into());