diff --git a/crates/ironrdp-session/src/active_stage.rs b/crates/ironrdp-session/src/active_stage.rs index d8676e7b0..a010afa3d 100644 --- a/crates/ironrdp-session/src/active_stage.rs +++ b/crates/ironrdp-session/src/active_stage.rs @@ -265,14 +265,17 @@ impl TryFrom for ActiveStageOutput { fn try_from(value: x224::ProcessorOutput) -> Result { match value { x224::ProcessorOutput::ResponseFrame(frame) => Ok(Self::ResponseFrame(frame)), - x224::ProcessorOutput::Disconnect(reason) => { - let reason = match reason { - mcs::DisconnectReason::UserRequested => GracefulDisconnectReason::UserInitiated, - mcs::DisconnectReason::ProviderInitiated => GracefulDisconnectReason::ServerInitiated, - other => GracefulDisconnectReason::Other(other.description()), + x224::ProcessorOutput::Disconnect(desc) => { + let desc = match desc { + x224::DisconnectDescription::McsDisconnect(reason) => match reason { + mcs::DisconnectReason::ProviderInitiated => GracefulDisconnectReason::ServerInitiated, + mcs::DisconnectReason::UserRequested => GracefulDisconnectReason::UserInitiated, + other => GracefulDisconnectReason::Other(other.description().to_owned()), + }, + x224::DisconnectDescription::ErrorInfo(info) => GracefulDisconnectReason::Other(info.description()), }; - Ok(Self::Terminate(reason)) + Ok(Self::Terminate(desc)) } x224::ProcessorOutput::DeactivateAll(cas) => Ok(Self::DeactivateAll(cas)), } @@ -281,25 +284,25 @@ impl TryFrom for ActiveStageOutput { /// Reasons for graceful disconnect. This type provides GUI-friendly descriptions for /// disconnect reasons. -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone)] pub enum GracefulDisconnectReason { UserInitiated, ServerInitiated, - Other(&'static str), + Other(String), } impl GracefulDisconnectReason { - pub fn description(&self) -> &'static str { + pub fn description(&self) -> String { match self { - GracefulDisconnectReason::UserInitiated => "user initiated disconnect", - GracefulDisconnectReason::ServerInitiated => "server initiated disconnect", - GracefulDisconnectReason::Other(description) => description, + GracefulDisconnectReason::UserInitiated => "user initiated disconnect".to_owned(), + GracefulDisconnectReason::ServerInitiated => "server initiated disconnect".to_owned(), + GracefulDisconnectReason::Other(description) => description.clone(), } } } impl core::fmt::Display for GracefulDisconnectReason { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - f.write_str(self.description()) + f.write_str(&self.description()) } } diff --git a/crates/ironrdp-session/src/x224/mod.rs b/crates/ironrdp-session/src/x224/mod.rs index 125da8d01..552dac125 100644 --- a/crates/ironrdp-session/src/x224/mod.rs +++ b/crates/ironrdp-session/src/x224/mod.rs @@ -17,7 +17,7 @@ pub enum ProcessorOutput { /// A buffer with encoded data to send to the server. ResponseFrame(Vec), /// A graceful disconnect notification. Client should close the connection upon receiving this. - Disconnect(DisconnectReason), + Disconnect(DisconnectDescription), /// Received a [`ironrdp_pdu::rdp::headers::ServerDeactivateAll`] PDU. Client should execute the /// [Deactivation-Reactivation Sequence]. /// @@ -25,6 +25,18 @@ pub enum ProcessorOutput { DeactivateAll(Box), } +#[derive(Debug, Clone)] +pub enum DisconnectDescription { + /// Includes the reason from the MCS Disconnect Provider Ultimatum. + /// This is the least-specific disconnect reason and is only used + /// when a more specific disconnect code is not available. + McsDisconnect(DisconnectReason), + + /// Includes the error information sent by the RDP server when there + /// is a connection or disconnection failure. + ErrorInfo(ErrorInfo), +} + pub struct Processor { static_channels: StaticChannelSet, user_channel_id: u16, @@ -123,15 +135,8 @@ impl Processor { // in [MS-RDPBCGR]. // // [MS-RDPBCGR]: https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/149070b0-ecec-4c20-af03-934bbc48adb8 - let graceful_disconnect = error_info_to_graceful_disconnect_reason(&e); - - if let Some(reason) = graceful_disconnect { - debug!("Received server-side graceful disconnect request: {reason}"); - - Ok(vec![ProcessorOutput::Disconnect(reason)]) - } else { - Err(reason_err!("ServerSetErrorInfo", "{}", e.description())) - } + let desc = DisconnectDescription::ErrorInfo(e); + Ok(vec![ProcessorOutput::Disconnect(desc)]) } ShareDataPdu::ShutdownDenied => { debug!("ShutdownDenied received, session will be closed"); @@ -149,7 +154,9 @@ impl Processor { Ok(vec![ ProcessorOutput::ResponseFrame(encoded_pdu?), - ProcessorOutput::Disconnect(DisconnectReason::UserRequested), + ProcessorOutput::Disconnect(DisconnectDescription::McsDisconnect( + DisconnectReason::UserRequested, + )), ]) } _ => Err(reason_err!( @@ -183,24 +190,3 @@ impl Processor { fn process_svc_messages(messages: Vec, channel_id: u16, initiator_id: u16) -> SessionResult> { client_encode_svc_messages(messages, channel_id, initiator_id).map_err(SessionError::encode) } - -/// Converts an [`ErrorInfo`] into a [`DisconnectReason`]. -/// -/// Returns `None` if the error code is not a graceful disconnect code. -pub fn error_info_to_graceful_disconnect_reason(error_info: &ErrorInfo) -> Option { - let code = if let ErrorInfo::ProtocolIndependentCode(code) = error_info { - code - } else { - return None; - }; - - match code { - ProtocolIndependentCode::RpcInitiatedDisconnect - | ProtocolIndependentCode::RpcInitiatedLogoff - | ProtocolIndependentCode::DisconnectedByOtherconnection => Some(DisconnectReason::ProviderInitiated), - ProtocolIndependentCode::RpcInitiatedDisconnectByuser | ProtocolIndependentCode::LogoffByUser => { - Some(DisconnectReason::UserRequested) - } - _ => None, - } -} diff --git a/ffi/src/session/mod.rs b/ffi/src/session/mod.rs index 59b476e83..7c099be32 100644 --- a/ffi/src/session/mod.rs +++ b/ffi/src/session/mod.rs @@ -237,7 +237,7 @@ pub mod ffi { pub fn get_terminate(&self) -> Result, Box> { match &self.0 { - ironrdp::session::ActiveStageOutput::Terminate(reason) => Ok(GracefulDisconnectReason(*reason)), + ironrdp::session::ActiveStageOutput::Terminate(reason) => Ok(GracefulDisconnectReason(reason.clone())), _ => Err(IncorrectEnumTypeError::on_variant("Terminate") .of_enum("ActiveStageOutput") .into()),