feat: implement the connection activation sequence rerun (#421)

This commit is contained in:
Isaiah Becker-Mayer 2024-04-05 10:05:15 -05:00 committed by GitHub
parent d46e7964bf
commit ba3796f738
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 108 additions and 13 deletions

View file

@ -25,7 +25,7 @@ where
info!("Begin connection procedure");
while !connector.should_perform_security_upgrade() {
single_connect_step(framed, connector, &mut buf)?;
single_sequence_step(framed, connector, &mut buf)?;
}
Ok(ShouldUpgrade)
@ -78,7 +78,7 @@ where
debug!("Remaining of connection sequence");
let result = loop {
single_connect_step(framed, &mut connector, &mut buf)?;
single_sequence_step(framed, &mut connector, &mut buf)?;
if let ClientConnectorState::Connected { result } = connector.state {
break result;
@ -173,7 +173,7 @@ where
Ok(())
}
pub fn single_connect_step<S>(
pub fn single_sequence_step<S>(
framed: &mut Framed<S>,
connector: &mut ClientConnector,
buf: &mut WriteBuf,

View file

@ -1,10 +1,13 @@
use ironrdp::cliprdr::backend::{ClipboardMessage, CliprdrBackendFactory};
use ironrdp::connector::connection_activation::ConnectionActivationState;
use ironrdp::connector::{ConnectionResult, ConnectorResult};
use ironrdp::graphics::image_processing::PixelFormat;
use ironrdp::pdu::input::fast_path::FastPathInputEvent;
use ironrdp::pdu::write_buf::WriteBuf;
use ironrdp::session::image::DecodedImage;
use ironrdp::session::{ActiveStage, ActiveStageOutput, GracefulDisconnectReason, SessionResult};
use ironrdp::session::{fast_path, ActiveStage, ActiveStageOutput, GracefulDisconnectReason, SessionResult};
use ironrdp::{cliprdr, connector, rdpdr, rdpsnd, session};
use ironrdp_tokio::single_sequence_step_read;
use rdpdr::NoopRdpdrBackend;
use smallvec::SmallVec;
use tokio::net::TcpStream;
@ -280,7 +283,47 @@ async fn active_session(
ActiveStageOutput::PointerBitmap(_) => {
// Not applicable, because we use the software cursor rendering.
}
ActiveStageOutput::DeactivateAll(_) => todo!("DeactivateAll"),
ActiveStageOutput::DeactivateAll(mut connection_activation) => {
// Execute the Deactivation-Reactivation Sequence:
// https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/dfc234ce-481a-4674-9a5d-2a7bafb14432
debug!("Received Server Deactivate All PDU, executing Deactivation-Reactivation Sequence");
let mut buf = WriteBuf::new();
'activation_seq: loop {
let written = single_sequence_step_read(&mut framed, &mut *connection_activation, &mut buf)
.await
.map_err(|e| session::custom_err!("read deactivation-reactivation sequence step", e))?;
if written.size().is_some() {
framed.write_all(buf.filled()).await.map_err(|e| {
session::custom_err!("write deactivation-reactivation sequence step", e)
})?;
}
if let ConnectionActivationState::Finalized {
io_channel_id,
user_channel_id,
desktop_size,
no_server_pointer,
pointer_software_rendering,
} = connection_activation.state
{
debug!("Deactivation-Reactivation Sequence completed");
image = DecodedImage::new(PixelFormat::RgbA32, desktop_size.width, desktop_size.height);
// Create a new [`FastPathProcessor`] with potentially updated
// io/user channel ids.
active_stage.set_fastpath_processor(
fast_path::ProcessorBuilder {
io_channel_id,
user_channel_id,
no_server_pointer,
pointer_software_rendering,
}
.build(),
);
break 'activation_seq;
}
}
}
ActiveStageOutput::Terminate(reason) => break 'outer reason,
}
}

View file

@ -551,14 +551,16 @@ impl Sequence for ClientConnector {
io_channel_id,
user_channel_id,
desktop_size,
no_server_pointer,
pointer_software_rendering,
} => ClientConnectorState::Connected {
result: ConnectionResult {
io_channel_id,
user_channel_id,
static_channels: mem::take(&mut self.static_channels),
desktop_size,
no_server_pointer: self.config.no_server_pointer,
pointer_software_rendering: self.config.pointer_software_rendering,
no_server_pointer,
pointer_software_rendering,
connection_activation,
},
},

View file

@ -187,6 +187,8 @@ impl Sequence for ConnectionActivationSequence {
io_channel_id,
user_channel_id,
desktop_size,
no_server_pointer: self.config.no_server_pointer,
pointer_software_rendering: self.config.pointer_software_rendering,
}
};
@ -218,6 +220,8 @@ pub enum ConnectionActivationState {
io_channel_id: u16,
user_channel_id: u16,
desktop_size: DesktopSize,
no_server_pointer: bool,
pointer_software_rendering: bool,
},
}

View file

@ -38,7 +38,7 @@ pub struct DesktopSize {
pub height: u16,
}
#[derive(Debug, Clone)]
#[derive(Debug, Clone, Copy)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
pub struct BitmapConfig {
pub lossy_compression: bool,

View file

@ -146,6 +146,10 @@ impl ActiveStage {
Ok(stage_outputs)
}
pub fn set_fastpath_processor(&mut self, processor: fast_path::Processor) {
self.fast_path_processor = processor;
}
/// Encodes client-side graceful shutdown request. Note that upon sending this request,
/// client should wait for server's ShutdownDenied PDU before closing the connection.
///
@ -192,7 +196,7 @@ pub enum ActiveStageOutput {
PointerPosition { x: u16, y: u16 },
PointerBitmap(Rc<DecodedPointer>),
Terminate(GracefulDisconnectReason),
DeactivateAll(ConnectionActivationSequence),
DeactivateAll(Box<ConnectionActivationSequence>),
}
impl TryFrom<x224::ProcessorOutput> for ActiveStageOutput {

View file

@ -21,7 +21,7 @@ pub enum ProcessorOutput {
/// [Deactivation-Reactivation Sequence].
///
/// [Deactivation-Reactivation Sequence]: https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/dfc234ce-481a-4674-9a5d-2a7bafb14432
DeactivateAll(ConnectionActivationSequence),
DeactivateAll(Box<ConnectionActivationSequence>),
}
pub struct Processor {
@ -155,7 +155,7 @@ impl Processor {
}
}
ironrdp_connector::legacy::IoChannelPdu::DeactivateAll(_) => Ok(vec![ProcessorOutput::DeactivateAll(
self.connection_activation.reset_clone(),
Box::new(self.connection_activation.reset_clone()),
)]),
}
}

View file

@ -12,6 +12,7 @@ use gloo_net::websocket;
use gloo_net::websocket::futures::WebSocket;
use ironrdp::cliprdr::backend::ClipboardMessage;
use ironrdp::cliprdr::CliprdrClient;
use ironrdp::connector::connection_activation::ConnectionActivationState;
use ironrdp::connector::credssp::KerberosConfig;
use ironrdp::connector::{self, ClientConnector, Credentials};
use ironrdp::graphics::image_processing::PixelFormat;
@ -19,7 +20,8 @@ use ironrdp::pdu::input::fast_path::FastPathInputEvent;
use ironrdp::pdu::rdp::client_info::PerformanceFlags;
use ironrdp::pdu::write_buf::WriteBuf;
use ironrdp::session::image::DecodedImage;
use ironrdp::session::{ActiveStage, ActiveStageOutput, GracefulDisconnectReason};
use ironrdp::session::{fast_path, ActiveStage, ActiveStageOutput, GracefulDisconnectReason};
use ironrdp_futures::single_sequence_step_read;
use rgb::AsPixels as _;
use tap::prelude::*;
use wasm_bindgen::prelude::*;
@ -604,7 +606,47 @@ impl Session {
hotspot_y,
})?;
}
ActiveStageOutput::DeactivateAll(_) => todo!("DeactivateAll"),
ActiveStageOutput::DeactivateAll(mut box_connection_activation) => {
// Execute the Deactivation-Reactivation Sequence:
// https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/dfc234ce-481a-4674-9a5d-2a7bafb14432
debug!("Received Server Deactivate All PDU, executing Deactivation-Reactivation Sequence");
let mut buf = WriteBuf::new();
'activation_seq: loop {
let written =
single_sequence_step_read(&mut framed, &mut *box_connection_activation, &mut buf)
.await?;
if written.size().is_some() {
self.writer_tx
.unbounded_send(buf.filled().to_vec())
.context("Send frame to writer task")?;
}
if let ConnectionActivationState::Finalized {
io_channel_id,
user_channel_id,
desktop_size,
no_server_pointer,
pointer_software_rendering,
} = box_connection_activation.state
{
debug!("Deactivation-Reactivation Sequence completed");
image = DecodedImage::new(PixelFormat::RgbA32, desktop_size.width, desktop_size.height);
// Create a new [`FastPathProcessor`] with potentially updated
// io/user channel ids.
active_stage.set_fastpath_processor(
fast_path::ProcessorBuilder {
io_channel_id,
user_channel_id,
no_server_pointer,
pointer_software_rendering,
}
.build(),
);
break 'activation_seq;
}
}
}
ActiveStageOutput::Terminate(reason) => break 'outer reason,
}
}