refactor(web): add NegotiationFailure to IronErrorKind (#905)
Some checks failed
CI / Check formatting (push) Has been cancelled
CI / Check typos (push) Has been cancelled
Coverage / Coverage Report (push) Has been cancelled
Release crates / Open release PR (push) Has been cancelled
Release crates / Release crates (push) Has been cancelled
CI / Checks [linux] (push) Has been cancelled
CI / Checks [macos] (push) Has been cancelled
CI / Checks [windows] (push) Has been cancelled
CI / Fuzzing (push) Has been cancelled
CI / Web Client (push) Has been cancelled
CI / FFI (push) Has been cancelled
CI / Success (push) Has been cancelled

Adds a general NegotiationFailure to IronErrorKind so that web embedders
can handle failures that occur during protocol negotiation, before
authentication.

Changes:
- RDP errors during the negotiation phase become
IronErrorKind.NegotiationError
- User-friendly RDP negotiation error messages to ironrdp-connector
- Update TypeScript definitions
This commit is contained in:
Gabriel Bauman 2025-08-08 01:52:28 -07:00 committed by GitHub
parent 5fc9fefa02
commit ac291423de
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 63 additions and 3 deletions

View file

@ -21,4 +21,6 @@ pub enum IronErrorKind {
RDCleanPath,
/// Couldnt connect to proxy
ProxyConnect,
/// Protocol negotiation failed
NegotiationFailure,
}

View file

@ -13,8 +13,8 @@ use crate::channel_connection::{ChannelConnectionSequence, ChannelConnectionStat
use crate::connection_activation::{ConnectionActivationSequence, ConnectionActivationState};
use crate::license_exchange::{LicenseExchangeSequence, NoopLicenseCache};
use crate::{
encode_x224_packet, Config, ConnectorError, ConnectorErrorExt as _, ConnectorResult, DesktopSize, Sequence, State,
Written,
encode_x224_packet, Config, ConnectorError, ConnectorErrorExt as _, ConnectorErrorKind, ConnectorResult,
DesktopSize, NegotiationFailure, Sequence, State, Written,
};
#[derive(Debug)]
@ -274,7 +274,10 @@ impl Sequence for ClientConnector {
nego::ConnectionConfirm::Response { flags, protocol } => (flags, protocol),
nego::ConnectionConfirm::Failure { code } => {
error!(?code, "Received connection failure code");
return Err(reason_err!("Initiation", "{code}"));
return Err(ConnectorError::new(
"negotiation failure",
ConnectorErrorKind::Negotiation(NegotiationFailure::from(code)),
));
}
};

View file

@ -36,6 +36,55 @@ pub use self::license_exchange::{LicenseExchangeSequence, LicenseExchangeState};
pub use self::server_name::ServerName;
pub use crate::license_exchange::LicenseCache;
/// Provides user-friendly error messages for RDP negotiation failures
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct NegotiationFailure(ironrdp_pdu::nego::FailureCode);
impl NegotiationFailure {
pub fn code(self) -> ironrdp_pdu::nego::FailureCode {
self.0
}
}
impl core::error::Error for NegotiationFailure {}
impl From<ironrdp_pdu::nego::FailureCode> for NegotiationFailure {
fn from(code: ironrdp_pdu::nego::FailureCode) -> Self {
Self(code)
}
}
impl fmt::Display for NegotiationFailure {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use ironrdp_pdu::nego::FailureCode;
match self.0 {
FailureCode::SSL_REQUIRED_BY_SERVER => {
write!(f, "server requires Enhanced RDP Security with TLS or CredSSP")
}
FailureCode::SSL_NOT_ALLOWED_BY_SERVER => {
write!(f, "server only supports Standard RDP Security")
}
FailureCode::SSL_CERT_NOT_ON_SERVER => {
write!(f, "server lacks valid authentication certificate")
}
FailureCode::INCONSISTENT_FLAGS => {
write!(f, "inconsistent security protocol flags")
}
FailureCode::HYBRID_REQUIRED_BY_SERVER => {
write!(f, "server requires Enhanced RDP Security with CredSSP")
}
FailureCode::SSL_WITH_USER_AUTH_REQUIRED_BY_SERVER => {
write!(
f,
"server requires Enhanced RDP Security with TLS and client certificate"
)
}
_ => write!(f, "unknown negotiation failure (code: 0x{:08x})", u32::from(self.0)),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
pub struct DesktopSize {
@ -276,6 +325,7 @@ pub enum ConnectorErrorKind {
AccessDenied,
General,
Custom,
Negotiation(NegotiationFailure),
}
impl fmt::Display for ConnectorErrorKind {
@ -288,6 +338,7 @@ impl fmt::Display for ConnectorErrorKind {
ConnectorErrorKind::AccessDenied => write!(f, "access denied"),
ConnectorErrorKind::General => write!(f, "general error"),
ConnectorErrorKind::Custom => write!(f, "custom error"),
ConnectorErrorKind::Negotiation(failure) => write!(f, "negotiation failure: {failure}"),
}
}
}
@ -302,6 +353,7 @@ impl core::error::Error for ConnectorErrorKind {
ConnectorErrorKind::AccessDenied => None,
ConnectorErrorKind::Custom => None,
ConnectorErrorKind::General => None,
ConnectorErrorKind::Negotiation(failure) => Some(failure),
}
}
}

View file

@ -34,6 +34,7 @@ ironrdp = { path = "../ironrdp", features = [
"cliprdr",
"svc",
"displaycontrol",
"pdu",
] }
ironrdp-core.path = "../ironrdp-core"
ironrdp-cliprdr-format.path = "../ironrdp-cliprdr-format"

View file

@ -37,6 +37,7 @@ impl From<connector::ConnectorError> for IronError {
..
}) => IronErrorKind::LogonFailure,
ConnectorErrorKind::AccessDenied => IronErrorKind::AccessDenied,
ConnectorErrorKind::Negotiation(_) => IronErrorKind::NegotiationFailure,
_ => IronErrorKind::General,
};

View file

@ -7,6 +7,7 @@ export enum IronErrorKind {
AccessDenied = 3,
RDCleanPath = 4,
ProxyConnect = 5,
NegotiationFailure = 6,
}
export interface IronError {