refactor: error handling

Base all library errors on `ironrdp_error::Error`, a lightweight and
`no_std`-compatible generic `Error` type.
A custom consumer-defined type (such as `PduErrorKind`) for
domain-specific details is wrapped by this type.
This commit is contained in:
Benoît CORTIER
2023-05-15 23:45:22 -04:00
committed by Benoît Cortier
parent 8857c3c25e
commit cf2287739d
56 changed files with 1416 additions and 999 deletions
+2
View File
@@ -24,6 +24,8 @@ Pay attention to the "**Architecture Invariant**" sections.
- `crates/ironrdp-session`: state machines to drive an RDP session.
- `crates/ironrdp-input`: utilities to manage and build input packets.
- `crates/ironrdp-rdcleanpath`: RDCleanPath PDU structure used by IronRDP web client and Devolutions Gateway.
- `crates/ironrdp-error`: lightweight and `no_std`-compatible generic `Error` and `Report` types.
The `Error` type wraps a custom consumer-defined type for domain-specific details (such as `PduErrorKind`).
**Architectural Invariant**: doing I/O is not allowed for these crates.
Generated
+9
View File
@@ -1544,6 +1544,7 @@ name = "ironrdp-connector"
version = "0.1.0"
dependencies = [
"arbitrary",
"ironrdp-error",
"ironrdp-pdu",
"rand_core 0.6.4",
"rstest",
@@ -1551,6 +1552,10 @@ dependencies = [
"tracing",
]
[[package]]
name = "ironrdp-error"
version = "0.1.0"
[[package]]
name = "ironrdp-futures"
version = "0.1.0"
@@ -1579,6 +1584,7 @@ dependencies = [
"bmp",
"byteorder",
"expect-test",
"ironrdp-error",
"ironrdp-pdu",
"lazy_static",
"num-derive 0.3.3",
@@ -1604,6 +1610,7 @@ dependencies = [
"byteorder",
"der-parser 8.2.0",
"expect-test",
"ironrdp-error",
"ironrdp-testsuite-core",
"lazy_static",
"md-5 0.10.5",
@@ -1638,6 +1645,7 @@ version = "0.1.0"
dependencies = [
"bitflags 2.0.2",
"ironrdp-connector",
"ironrdp-error",
"ironrdp-graphics",
"ironrdp-pdu",
"sspi",
@@ -1659,6 +1667,7 @@ version = "0.0.0"
dependencies = [
"anyhow",
"array-concat",
"expect-test",
"hex",
"ironrdp-connector",
"ironrdp-fuzzing",
+1
View File
@@ -24,6 +24,7 @@ categories = ["network-programming"]
expect-test = "1"
ironrdp-async = { version = "0.1", path = "crates/ironrdp-async" }
ironrdp-connector = { version = "0.1", path = "crates/ironrdp-connector" }
ironrdp-error = { version = "0.1", path = "crates/ironrdp-error" }
ironrdp-futures = { version = "0.1", path = "crates/ironrdp-futures" }
ironrdp-fuzzing = { path = "crates/ironrdp-fuzzing" }
ironrdp-graphics = { version = "0.1", path = "crates/ironrdp-graphics" }
+8 -9
View File
@@ -1,4 +1,6 @@
use ironrdp_connector::{ClientConnector, ClientConnectorState, ConnectionResult, Sequence as _, State as _};
use ironrdp_connector::{
ClientConnector, ClientConnectorState, ConnectionResult, ConnectorResult, Sequence as _, State as _,
};
use crate::framed::{Framed, FramedRead, FramedWrite};
@@ -7,10 +9,7 @@ pub struct ShouldUpgrade {
}
#[instrument(skip_all)]
pub async fn connect_begin<S>(
framed: &mut Framed<S>,
connector: &mut ClientConnector,
) -> ironrdp_connector::Result<ShouldUpgrade>
pub async fn connect_begin<S>(framed: &mut Framed<S>, connector: &mut ClientConnector) -> ConnectorResult<ShouldUpgrade>
where
S: Sync + FramedRead + FramedWrite,
{
@@ -47,7 +46,7 @@ pub async fn connect_finalize<S>(
_: Upgraded,
framed: &mut Framed<S>,
mut connector: ClientConnector,
) -> ironrdp_connector::Result<ConnectionResult>
) -> ConnectorResult<ConnectionResult>
where
S: FramedRead + FramedWrite,
{
@@ -78,7 +77,7 @@ pub async fn single_connect_step<S>(
framed: &mut Framed<S>,
connector: &mut ClientConnector,
buf: &mut Vec<u8>,
) -> ironrdp_connector::Result<ironrdp_connector::Written>
) -> ConnectorResult<ironrdp_connector::Written>
where
S: FramedWrite + FramedRead,
{
@@ -92,7 +91,7 @@ where
let pdu = framed
.read_by_hint(next_pdu_hint)
.await
.map_err(|e| ironrdp_connector::Error::new("read frame by hint").with_custom(e))?;
.map_err(|e| ironrdp_connector::custom_err!("read frame by hint", e))?;
trace!(length = pdu.len(), "PDU received");
@@ -107,7 +106,7 @@ where
framed
.write_all(response)
.await
.map_err(|e| ironrdp_connector::Error::new("write all").with_custom(e))?;
.map_err(|e| ironrdp_connector::custom_err!("write all", e))?;
}
Ok(written)
+3 -4
View File
@@ -301,10 +301,9 @@ impl Config {
.pipe(u32::try_from)
.unwrap(),
client_name: whoami::hostname(),
client_dir: std::env::current_dir()
.expect("current directory")
.to_string_lossy()
.into_owned(),
// NOTE: hardcode this value like in freerdp
// https://github.com/FreeRDP/FreeRDP/blob/4e24b966c86fdf494a782f0dfcfc43a057a2ea60/libfreerdp/core/settings.c#LL49C34-L49C70
client_dir: "C:\\Windows\\System32\\mstscax.dll".to_owned(),
platform: match whoami::platform() {
whoami::Platform::Windows => MajorPlatformType::Windows,
whoami::Platform::Linux => MajorPlatformType::Unix,
+4 -4
View File
@@ -208,8 +208,8 @@ impl GuiContext {
graphics_context.set_buffer(image_buffer, width, height);
}
Event::UserEvent(RdpOutputEvent::ConnectionFailure(error)) => {
error!(%error);
println!("Connection error: {error:#}");
error!(?error);
println!("Connection error: {}", error.report());
control_flow.set_exit_with_code(exitcode::PROTOCOL);
}
Event::UserEvent(RdpOutputEvent::Terminated(result)) => {
@@ -219,8 +219,8 @@ impl GuiContext {
exitcode::OK
}
Err(error) => {
error!(error = format!("{error:#}"));
println!("Active session error: {error:#}");
error!(?error);
println!("Active session error: {}", error.report());
exitcode::PROTOCOL
}
};
+16 -15
View File
@@ -1,7 +1,8 @@
use ironrdp::connector::{ConnectionResult, ConnectorResult};
use ironrdp::graphics::image_processing::PixelFormat;
use ironrdp::pdu::input::fast_path::FastPathInputEvent;
use ironrdp::session::image::DecodedImage;
use ironrdp::session::{ActiveStage, ActiveStageOutput};
use ironrdp::session::{ActiveStage, ActiveStageOutput, SessionResult};
use ironrdp::{connector, session};
use smallvec::SmallVec;
use sspi::network_client::reqwest_network_client::RequestClientFactory;
@@ -14,8 +15,8 @@ use crate::config::Config;
#[derive(Debug)]
pub enum RdpOutputEvent {
Image { buffer: Vec<u32>, width: u16, height: u16 },
ConnectionFailure(connector::Error),
Terminated(session::Result<()>),
ConnectionFailure(connector::ConnectorError),
Terminated(SessionResult<()>),
}
#[derive(Debug)]
@@ -80,15 +81,15 @@ enum RdpControlFlow {
type UpgradedFramed = ironrdp_tokio::TokioFramed<ironrdp_tls::TlsStream<TcpStream>>;
async fn connect(config: &Config) -> connector::Result<(connector::ConnectionResult, UpgradedFramed)> {
async fn connect(config: &Config) -> ConnectorResult<(ConnectionResult, UpgradedFramed)> {
let server_addr = config
.destination
.lookup_addr()
.map_err(|e| connector::Error::new("lookup addr").with_custom(e))?;
.map_err(|e| connector::custom_err!("lookup addr", e))?;
let stream = TcpStream::connect(&server_addr)
.await
.map_err(|e| connector::Error::new("TCP connect").with_custom(e))?;
.map_err(|e| connector::custom_err!("TCP connect", e))?;
let mut framed = ironrdp_tokio::TokioFramed::new(stream);
@@ -106,7 +107,7 @@ async fn connect(config: &Config) -> connector::Result<(connector::ConnectionRes
let (upgraded_stream, server_public_key) = ironrdp_tls::upgrade(initial_stream, config.destination.name())
.await
.map_err(|e| connector::Error::new("TLS upgrade").with_custom(e))?;
.map_err(|e| connector::custom_err!("TLS upgrade", e))?;
let upgraded = ironrdp_tokio::mark_as_upgraded(should_upgrade, &mut connector, server_public_key);
@@ -119,10 +120,10 @@ async fn connect(config: &Config) -> connector::Result<(connector::ConnectionRes
async fn active_session(
mut framed: UpgradedFramed,
connection_result: connector::ConnectionResult,
connection_result: ConnectionResult,
event_loop_proxy: &EventLoopProxy<RdpOutputEvent>,
input_event_receiver: &mut mpsc::UnboundedReceiver<RdpInputEvent>,
) -> session::Result<RdpControlFlow> {
) -> SessionResult<RdpControlFlow> {
let mut image = DecodedImage::new(
PixelFormat::RgbA32,
connection_result.desktop_size.width,
@@ -134,14 +135,14 @@ async fn active_session(
'outer: loop {
tokio::select! {
frame = framed.read_pdu() => {
let (action, payload) = frame.map_err(|e| session::Error::new("read frame").with_custom(e))?;
let (action, payload) = frame.map_err(|e| session::custom_err!("read frame", e))?;
trace!(?action, frame_length = payload.len(), "Frame received");
let outputs = active_stage.process(&mut image, action, &payload)?;
for out in outputs {
match out {
ActiveStageOutput::ResponseFrame(frame) => framed.write_all(&frame).await.map_err(|e| session::Error::new("write response").with_custom(e))?,
ActiveStageOutput::ResponseFrame(frame) => framed.write_all(&frame).await.map_err(|e| session::custom_err!("write response", e))?,
ActiveStageOutput::GraphicsUpdate(_region) => {
let buffer: Vec<u32> = image
.data()
@@ -160,14 +161,14 @@ async fn active_session(
width: image.width(),
height: image.height(),
})
.map_err(|e| session::Error::new("event_loop_proxy").with_custom(e))?;
.map_err(|e| session::custom_err!("event_loop_proxy", e))?;
}
ActiveStageOutput::Terminate => break 'outer,
}
}
}
input_event = input_event_receiver.recv() => {
let input_event = input_event.ok_or(session::Error::new("GUI is stopped"))?;
let input_event = input_event.ok_or_else(|| session::general_err!("GUI is stopped"))?;
match input_event {
RdpInputEvent::Resize { mut width, mut height } => {
@@ -201,9 +202,9 @@ async fn active_session(
let mut frame = Vec::new();
fastpath_input
.to_buffer(&mut frame)
.map_err(|e| session::Error::new("FastPathInput encode").with_custom(e))?;
.map_err(|e| session::custom_err!("FastPathInput encode", e))?;
framed.write_all(&frame).await.map_err(|e| session::Error::new("write FastPathInput PDU").with_custom(e))?;
framed.write_all(&frame).await.map_err(|e| session::custom_err!("write FastPathInput PDU", e))?;
}
RdpInputEvent::Close => {
// TODO: https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/27915739-8f77-487e-9927-55008af7fd68
+6 -5
View File
@@ -19,9 +19,10 @@ test = false
arbitrary = ["dep:arbitrary"]
[dependencies]
ironrdp-pdu.workspace = true
tracing.workspace = true
sspi.workspace = true
rstest.workspace = true
rand_core = { version = "0.6.4", features = ["std"] } # TODO: dependency injection?
arbitrary = { version = "1", features = ["derive"], optional = true }
ironrdp-error.workspace = true
ironrdp-pdu.workspace = true
rand_core = { version = "0.6.4", features = ["std"] } # TODO: dependency injection?
rstest.workspace = true
sspi.workspace = true
tracing.workspace = true
@@ -2,7 +2,7 @@ use std::mem;
use ironrdp_pdu::{mcs, PduHint};
use crate::{Error, Result, Sequence, State, Written};
use crate::{ConnectorError, ConnectorErrorExt as _, ConnectorResult, Sequence, State, Written};
#[derive(Default, Debug)]
#[non_exhaustive]
@@ -81,10 +81,10 @@ impl Sequence for ChannelConnectionSequence {
}
}
fn step(&mut self, input: &[u8], output: &mut Vec<u8>) -> Result<Written> {
fn step(&mut self, input: &[u8], output: &mut Vec<u8>) -> ConnectorResult<Written> {
let (written, next_state) = match mem::take(&mut self.state) {
ChannelConnectionState::Consumed => {
return Err(Error::new(
return Err(general_err!(
"channel connection sequence state is consumed (this is a bug)",
))
}
@@ -97,7 +97,7 @@ impl Sequence for ChannelConnectionSequence {
debug!(message = ?erect_domain_request, "Send");
let written = ironrdp_pdu::encode_buf(&erect_domain_request, output)?;
let written = ironrdp_pdu::encode_buf(&erect_domain_request, output).map_err(ConnectorError::pdu)?;
(
Written::from_size(written)?,
@@ -110,7 +110,7 @@ impl Sequence for ChannelConnectionSequence {
debug!(message = ?attach_user_request, "Send");
let written = ironrdp_pdu::encode_buf(&attach_user_request, output)?;
let written = ironrdp_pdu::encode_buf(&attach_user_request, output).map_err(ConnectorError::pdu)?;
(
Written::from_size(written)?,
@@ -119,7 +119,8 @@ impl Sequence for ChannelConnectionSequence {
}
ChannelConnectionState::WaitAttachUserConfirm => {
let attach_user_confirm = ironrdp_pdu::decode::<mcs::AttachUserConfirm>(input)?;
let attach_user_confirm =
ironrdp_pdu::decode::<mcs::AttachUserConfirm>(input).map_err(ConnectorError::pdu)?;
let user_channel_id = attach_user_confirm.initiator_id;
@@ -152,7 +153,7 @@ impl Sequence for ChannelConnectionSequence {
debug!(message = ?channel_join_request, "Send");
let written = ironrdp_pdu::encode_buf(&channel_join_request, output)?;
let written = ironrdp_pdu::encode_buf(&channel_join_request, output).map_err(ConnectorError::pdu)?;
(
Written::from_size(written)?,
@@ -163,7 +164,8 @@ impl Sequence for ChannelConnectionSequence {
ChannelConnectionState::WaitChannelJoinConfirm { user_channel_id, index } => {
let channel_id = self.channel_ids[index];
let channel_join_confirm = ironrdp_pdu::decode::<mcs::ChannelJoinConfirm>(input)?;
let channel_join_confirm =
ironrdp_pdu::decode::<mcs::ChannelJoinConfirm>(input).map_err(ConnectorError::pdu)?;
debug!(message = ?channel_join_confirm, "Received");
@@ -171,7 +173,7 @@ impl Sequence for ChannelConnectionSequence {
|| channel_join_confirm.channel_id != channel_join_confirm.requested_channel_id
|| channel_join_confirm.channel_id != channel_id
{
return Err(Error::new("received bad MCS Channel Join Confirm"));
return Err(general_err!("received bad MCS Channel Join Confirm"));
}
let next_index = index + 1;
@@ -188,7 +190,7 @@ impl Sequence for ChannelConnectionSequence {
(Written::Nothing, next_state)
}
ChannelConnectionState::AllJoined { .. } => return Err(Error::new("all channels are already joined")),
ChannelConnectionState::AllJoined { .. } => return Err(general_err!("all channels are already joined")),
};
self.state = next_state;
+37 -26
View File
@@ -8,7 +8,10 @@ use sspi::credssp;
use crate::channel_connection::{ChannelConnectionSequence, ChannelConnectionState};
use crate::connection_finalization::ConnectionFinalizationSequence;
use crate::license_exchange::LicenseExchangeSequence;
use crate::{legacy, Config, DesktopSize, Error, Result, Sequence, ServerName, State, StaticChannels, Written};
use crate::{
legacy, Config, ConnectorError, ConnectorErrorExt as _, ConnectorErrorKind, ConnectorResult, DesktopSize, Sequence,
ServerName, State, StaticChannels, Written,
};
#[derive(Clone, Copy, Debug)]
pub struct CredsspTsRequestHint;
@@ -16,11 +19,11 @@ pub struct CredsspTsRequestHint;
pub const CREDSSP_TS_REQUEST_HINT: CredsspTsRequestHint = CredsspTsRequestHint;
impl PduHint for CredsspTsRequestHint {
fn find_size(&self, bytes: &[u8]) -> ironrdp_pdu::Result<Option<usize>> {
fn find_size(&self, bytes: &[u8]) -> ironrdp_pdu::PduResult<Option<usize>> {
match sspi::credssp::TsRequest::read_length(bytes) {
Ok(length) => Ok(Some(length)),
Err(e) if e.kind() == std::io::ErrorKind::UnexpectedEof => Ok(None),
Err(e) => Err(ironrdp_pdu::Error::custom(e)),
Err(e) => Err(ironrdp_pdu::custom_err!("CredsspTsRequestHint", e)),
}
}
}
@@ -31,7 +34,7 @@ pub struct CredsspEarlyUserAuthResultHint;
pub const CREDSSP_EARLY_USER_AUTH_RESULT_HINT: CredsspEarlyUserAuthResultHint = CredsspEarlyUserAuthResultHint;
impl PduHint for CredsspEarlyUserAuthResultHint {
fn find_size(&self, _: &[u8]) -> ironrdp_pdu::Result<Option<usize>> {
fn find_size(&self, _: &[u8]) -> ironrdp_pdu::PduResult<Option<usize>> {
Ok(Some(sspi::credssp::EARLY_USER_AUTH_RESULT_PDU_SIZE))
}
}
@@ -274,11 +277,11 @@ impl Sequence for ClientConnector {
&self.state
}
fn step(&mut self, input: &[u8], output: &mut Vec<u8>) -> Result<Written> {
fn step(&mut self, input: &[u8], output: &mut Vec<u8>) -> ConnectorResult<Written> {
let (written, next_state) = match mem::take(&mut self.state) {
// Invalid state
ClientConnectorState::Consumed => {
return Err(Error::new("connector sequence state is consumed (this is a bug)"))
return Err(general_err!("connector sequence state is consumed (this is a bug)",))
}
//== Connection Initiation ==//
@@ -292,7 +295,7 @@ impl Sequence for ClientConnector {
debug!(message = ?connection_request, "Send");
let written = ironrdp_pdu::encode_buf(&connection_request, output)?;
let written = ironrdp_pdu::encode_buf(&connection_request, output).map_err(ConnectorError::pdu)?;
(
Written::from_size(written)?,
@@ -300,7 +303,8 @@ impl Sequence for ClientConnector {
)
}
ClientConnectorState::ConnectionInitiationWaitConfirm => {
let connection_confirm = ironrdp_pdu::decode::<nego::ConnectionConfirm>(input)?;
let connection_confirm =
ironrdp_pdu::decode::<nego::ConnectionConfirm>(input).map_err(ConnectorError::pdu)?;
debug!(message = ?connection_confirm, "Received");
@@ -308,14 +312,14 @@ impl Sequence for ClientConnector {
nego::ConnectionConfirm::Response { flags, protocol } => (flags, protocol),
nego::ConnectionConfirm::Failure { code } => {
error!(?code, "Received connection failure code");
return Err(Error::new("connection failed"));
return Err(general_err!("connection failed"));
}
};
info!(?selected_protocol, ?flags, "Server confirmed connection");
if !self.config.security_protocol.contains(selected_protocol) {
return Err(Error::new(
return Err(general_err!(
"server selected a security protocol that is unsupported by this client",
));
}
@@ -352,17 +356,17 @@ impl Sequence for ClientConnector {
let server_public_key = self
.server_public_key
.take()
.ok_or(Error::new("server public key is missing"))?;
.ok_or_else(|| general_err!("server public key is missing"))?;
let network_client_factory = self
.network_client_factory
.take()
.ok_or(Error::new("CredSSP network client factory is missing"))?;
.ok_or_else(|| general_err!("CredSSP network client factory is missing"))?;
let server_name = self
.server_name
.take()
.ok_or(Error::new("server name is missing"))?
.ok_or_else(|| general_err!("server name is missing"))?
.into_inner();
let service_principal_name = format!("TERMSRV/{server_name}");
@@ -378,11 +382,14 @@ impl Sequence for ClientConnector {
network_client_factory,
}),
service_principal_name,
)?;
)
.map_err(|e| ConnectorError::new("CredSSP", ConnectorErrorKind::Credssp(e)))?;
let initial_ts_request = credssp::TsRequest::default();
let result = credssp_client.process(initial_ts_request)?;
let result = credssp_client
.process(initial_ts_request)
.map_err(|e| ConnectorError::new("CredSSP", ConnectorErrorKind::Credssp(e)))?;
let (ts_request_from_client, next_state) = match result {
credssp::ClientState::ReplyNeeded(ts_request) => (
@@ -409,9 +416,11 @@ impl Sequence for ClientConnector {
mut credssp_client,
} => {
let ts_request_from_server = credssp::TsRequest::from_buffer(input)
.map_err(|e| Error::new("CredSSP").with_reason(format!("TsRequest decode: {e}")))?;
.map_err(|e| reason_err!("CredSSP", "TsRequest decode: {e}"))?;
let result = credssp_client.process(ts_request_from_server)?;
let result = credssp_client
.process(ts_request_from_server)
.map_err(|e| ConnectorError::new("CredSSP", ConnectorErrorKind::Credssp(e)))?;
let (ts_request_from_client, next_state) = match result {
credssp::ClientState::ReplyNeeded(ts_request) => (
@@ -439,10 +448,10 @@ impl Sequence for ClientConnector {
}
ClientConnectorState::CredsspEarlyUserAuthResult { selected_protocol } => {
let early_user_auth_result = credssp::EarlyUserAuthResult::from_buffer(input)
.map_err(|e| Error::new("CredSSP").with_reason(format!("EarlyUserAuthResult decode: {e}")))?;
.map_err(|e| custom_err!("credssp::EarlyUserAuthResult", e))?;
let credssp::EarlyUserAuthResult::Success = early_user_auth_result else {
return Err(Error::new("CredSSP").with_kind(crate::ErrorKind::AccessDenied));
return Err(ConnectorError::new("CredSSP", ConnectorErrorKind::AccessDenied));
};
(
@@ -484,7 +493,7 @@ impl Sequence for ClientConnector {
if client_gcc_blocks.security == gcc::ClientSecurityData::no_security()
&& server_gcc_blocks.security != gcc::ServerSecurityData::no_security()
{
return Err(Error::new("cant satisfy server security settings"));
return Err(general_err!("cant satisfy server security settings"));
}
if server_gcc_blocks.message_channel.is_some() {
@@ -561,7 +570,7 @@ impl Sequence for ClientConnector {
static_channels,
} => {
if selected_protocol == nego::SecurityProtocol::RDP {
return Err(Error::new("standard RDP Security (RC4 encryption) is not supported"));
return Err(general_err!("standard RDP Security (RC4 encryption) is not supported"));
}
(
@@ -584,7 +593,7 @@ impl Sequence for ClientConnector {
let routing_addr = self
.server_addr
.as_ref()
.ok_or(Error::new("server address is missing"))?;
.ok_or_else(|| general_err!("server address is missing"))?;
let client_info = create_client_info_pdu(&self.config, routing_addr);
@@ -690,7 +699,9 @@ impl Sequence for ClientConnector {
{
server_demand_active.pdu.capability_sets
} else {
return Err(Error::new("unexpected Share Control Pdu (expected ServerDemandActive)"));
return Err(general_err!(
"unexpected Share Control Pdu (expected ServerDemandActive)",
));
};
let desktop_size = capability_sets
@@ -770,7 +781,7 @@ impl Sequence for ClientConnector {
//== Connected ==//
// The client connector job is done.
ClientConnectorState::Connected { .. } => return Err(Error::new("already connected")),
ClientConnectorState::Connected { .. } => return Err(general_err!("already connected")),
};
self.state = next_state;
@@ -1024,7 +1035,7 @@ fn create_client_confirm_active(
}
}
fn write_credssp_request(ts_request: credssp::TsRequest, output: &mut Vec<u8>) -> crate::Result<usize> {
fn write_credssp_request(ts_request: credssp::TsRequest, output: &mut Vec<u8>) -> crate::ConnectorResult<usize> {
let length = usize::from(ts_request.buffer_len());
if output.len() < length {
@@ -1033,7 +1044,7 @@ fn write_credssp_request(ts_request: credssp::TsRequest, output: &mut Vec<u8>) -
ts_request
.encode_ts_request(output.as_mut_slice())
.map_err(|e| Error::new("CredSSP").with_reason(format!("TsRequest encode: {e}")))?;
.map_err(|e| reason_err!("CredSSP", "TsRequest encode: {e}"))?;
Ok(length)
}
@@ -5,7 +5,7 @@ use ironrdp_pdu::rdp::headers::ShareDataPdu;
use ironrdp_pdu::rdp::{finalization_messages, server_error_info};
use ironrdp_pdu::PduHint;
use crate::{legacy, Error, Result, Sequence, State, Written};
use crate::{legacy, ConnectorResult, Sequence, State, Written};
#[derive(Default, Debug)]
#[non_exhaustive]
@@ -81,10 +81,10 @@ impl Sequence for ConnectionFinalizationSequence {
&self.state
}
fn step(&mut self, input: &[u8], output: &mut Vec<u8>) -> Result<Written> {
fn step(&mut self, input: &[u8], output: &mut Vec<u8>) -> ConnectorResult<Written> {
let (written, next_state) = match mem::take(&mut self.state) {
ConnectionFinalizationState::Consumed => {
return Err(Error::new(
return Err(general_err!(
"connection finalization sequence state is consumed (this is a bug)",
))
}
@@ -170,7 +170,7 @@ impl Sequence for ConnectionFinalizationSequence {
debug!("Server Control (Cooperate)");
ConnectionFinalizationState::WaitForResponse
} else {
return Err(Error::new("invalid Control Cooperate PDU"));
return Err(general_err!("invalid Control Cooperate PDU"));
}
}
finalization_messages::ControlAction::GrantedControl => {
@@ -187,10 +187,10 @@ impl Sequence for ConnectionFinalizationSequence {
debug!("Server Control (Granted Control)");
ConnectionFinalizationState::WaitForResponse
} else {
return Err(Error::new("invalid Granted Control PDU"));
return Err(general_err!("invalid Granted Control PDU"));
}
}
_ => return Err(Error::new("unexpected control action")),
_ => return Err(general_err!("unexpected control action")),
},
ShareDataPdu::ServerSetErrorInfo(server_error_info::ServerSetErrorInfoPdu(error_info)) => {
match error_info {
@@ -198,9 +198,11 @@ impl Sequence for ConnectionFinalizationSequence {
server_error_info::ProtocolIndependentCode::None,
) => ConnectionFinalizationState::WaitForResponse,
_ => {
return Err(
Error::new("server returned error info").with_reason(error_info.description())
)
return Err(reason_err!(
"ServerSetErrorInfo",
"server returned error info: {}",
error_info.description()
));
}
}
}
@@ -214,13 +216,13 @@ impl Sequence for ConnectionFinalizationSequence {
ConnectionFinalizationState::Finished
}
_ => return Err(Error::new("unexpected server message")),
_ => return Err(general_err!("unexpected server message")),
};
(Written::Nothing, next_state)
}
ConnectionFinalizationState::Finished => return Err(Error::new("finalization already finished")),
ConnectionFinalizationState::Finished => return Err(general_err!("finalization already finished")),
};
self.state = next_state;
+32 -44
View File
@@ -4,10 +4,12 @@ use std::borrow::Cow;
use ironrdp_pdu::{rdp, x224, PduParsing};
pub fn encode_x224_packet<T: PduParsing>(x224_msg: &T, buf: &mut Vec<u8>) -> crate::Result<usize>
use crate::{ConnectorError, ConnectorErrorExt as _, ConnectorResult};
pub fn encode_x224_packet<T: PduParsing>(x224_msg: &T, buf: &mut Vec<u8>) -> ConnectorResult<usize>
where
T: PduParsing,
crate::Error: From<T::Error>,
ConnectorError: From<T::Error>,
{
let x224_msg_len = x224_msg.buffer_length();
let mut x224_msg_buf = Vec::with_capacity(x224_msg_len);
@@ -18,17 +20,17 @@ where
data: Cow::Owned(x224_msg_buf),
};
let written = ironrdp_pdu::encode_buf(&pdu, buf)?;
let written = ironrdp_pdu::encode_buf(&pdu, buf).map_err(ConnectorError::pdu)?;
Ok(written)
}
pub fn decode_x224_packet<T>(src: &[u8]) -> crate::Result<T>
pub fn decode_x224_packet<T>(src: &[u8]) -> ConnectorResult<T>
where
T: PduParsing,
crate::Error: From<T::Error>,
ConnectorError: From<T::Error>,
{
let x224_payload = ironrdp_pdu::decode::<x224::X224Data>(src)?;
let x224_payload = ironrdp_pdu::decode::<x224::X224Data>(src).map_err(ConnectorError::pdu)?;
let x224_msg = T::from_buffer(x224_payload.data.as_ref())?;
Ok(x224_msg)
}
@@ -38,10 +40,10 @@ pub fn encode_send_data_request<T>(
channel_id: u16,
user_msg: &T,
buf: &mut Vec<u8>,
) -> crate::Result<usize>
) -> ConnectorResult<usize>
where
T: PduParsing,
crate::Error: From<T::Error>,
ConnectorError: From<T::Error>,
{
let user_data_len = user_msg.buffer_length();
let mut user_data = Vec::with_capacity(user_data_len);
@@ -54,7 +56,7 @@ where
user_data: Cow::Owned(user_data),
};
let written = ironrdp_pdu::encode_buf(&pdu, buf)?;
let written = ironrdp_pdu::encode_buf(&pdu, buf).map_err(ConnectorError::pdu)?;
Ok(written)
}
@@ -67,20 +69,20 @@ pub struct SendDataIndicationCtx<'a> {
}
impl SendDataIndicationCtx<'_> {
pub fn decode_user_data<T>(&self) -> crate::Result<T>
pub fn decode_user_data<T>(&self) -> ConnectorResult<T>
where
T: PduParsing,
crate::Error: From<T::Error>,
ConnectorError: From<T::Error>,
{
let msg = T::from_buffer(self.user_data)?;
Ok(msg)
}
}
pub fn decode_send_data_indication(src: &[u8]) -> crate::Result<SendDataIndicationCtx<'_>> {
pub fn decode_send_data_indication(src: &[u8]) -> ConnectorResult<SendDataIndicationCtx<'_>> {
use ironrdp_pdu::mcs::McsMessage;
let mcs_msg = ironrdp_pdu::decode::<McsMessage>(src)?;
let mcs_msg = ironrdp_pdu::decode::<McsMessage>(src).map_err(ConnectorError::pdu)?;
match mcs_msg {
McsMessage::SendDataIndication(msg) => {
@@ -94,10 +96,16 @@ pub fn decode_send_data_indication(src: &[u8]) -> crate::Result<SendDataIndicati
user_data,
})
}
McsMessage::DisconnectProviderUltimatum(msg) => {
Err(crate::Error::new("received disconnect provider ultimatum").with_reason(format!("{:?}", msg.reason)))
}
unexpected => Err(crate::Error::new("unexpected MCS message").with_reason(ironrdp_pdu::name(&unexpected))),
McsMessage::DisconnectProviderUltimatum(msg) => Err(reason_err!(
"decode_send_data_indication",
"received disconnect provider ultimatum: {:?}",
msg.reason
)),
unexpected => Err(reason_err!(
"decode_send_data_indication",
"unexpected MCS message: {}",
ironrdp_pdu::name(&unexpected)
)),
}
}
@@ -107,7 +115,7 @@ pub fn encode_share_control(
share_id: u32,
pdu: rdp::headers::ShareControlPdu,
buf: &mut Vec<u8>,
) -> crate::Result<usize> {
) -> ConnectorResult<usize> {
let pdu_source = initiator_id;
let share_control_header = rdp::headers::ShareControlHeader {
@@ -128,7 +136,7 @@ pub struct ShareControlCtx {
pub pdu: rdp::headers::ShareControlPdu,
}
pub fn decode_share_control(ctx: SendDataIndicationCtx<'_>) -> crate::Result<ShareControlCtx> {
pub fn decode_share_control(ctx: SendDataIndicationCtx<'_>) -> ConnectorResult<ShareControlCtx> {
let user_msg = ctx.decode_user_data::<rdp::headers::ShareControlHeader>()?;
Ok(ShareControlCtx {
@@ -146,7 +154,7 @@ pub fn encode_share_data(
share_id: u32,
pdu: rdp::headers::ShareDataPdu,
buf: &mut Vec<u8>,
) -> crate::Result<usize> {
) -> ConnectorResult<usize> {
let share_data_header = rdp::headers::ShareDataHeader {
share_data_pdu: pdu,
stream_priority: rdp::headers::StreamPriority::Medium,
@@ -168,11 +176,11 @@ pub struct ShareDataCtx {
pub pdu: rdp::headers::ShareDataPdu,
}
pub fn decode_share_data(ctx: SendDataIndicationCtx<'_>) -> crate::Result<ShareDataCtx> {
pub fn decode_share_data(ctx: SendDataIndicationCtx<'_>) -> ConnectorResult<ShareDataCtx> {
let ctx = decode_share_control(ctx)?;
let rdp::headers::ShareControlPdu::Data(share_data_header) = ctx.pdu else {
return Err(crate::Error::new("received unexpected Share Control Pdu (expected SHare Data Header)"));
return Err(general_err!("received unexpected Share Control Pdu (expected SHare Data Header)"));
};
Ok(ShareDataCtx {
@@ -184,26 +192,6 @@ pub fn decode_share_data(ctx: SendDataIndicationCtx<'_>) -> crate::Result<ShareD
})
}
impl From<ironrdp_pdu::mcs::McsError> for crate::Error {
fn from(e: ironrdp_pdu::mcs::McsError) -> Self {
Self::new("MCS").with_reason(e.to_string())
}
}
impl From<ironrdp_pdu::rdp::server_license::ServerLicenseError> for crate::Error {
fn from(e: ironrdp_pdu::rdp::server_license::ServerLicenseError) -> Self {
Self::new("server license").with_reason(e.to_string())
}
}
impl From<ironrdp_pdu::rdp::RdpError> for crate::Error {
fn from(e: ironrdp_pdu::rdp::RdpError) -> Self {
Self::new("RDP").with_reason(e.to_string())
}
}
impl From<ironrdp_pdu::rdp::vc::ChannelError> for crate::Error {
fn from(e: ironrdp_pdu::rdp::vc::ChannelError) -> Self {
Self::new("virtual channel").with_reason(e.to_string())
}
impl ironrdp_error::legacy::CatchAllKind for crate::ConnectorErrorKind {
const CATCH_ALL_VALUE: Self = crate::ConnectorErrorKind::General;
}
+63 -133
View File
@@ -1,6 +1,9 @@
#[macro_use]
extern crate tracing;
#[macro_use]
mod macros;
pub mod legacy;
mod channel_connection;
@@ -109,10 +112,10 @@ pub enum Written {
impl Written {
#[inline]
pub fn from_size(value: usize) -> Result<Self> {
pub fn from_size(value: usize) -> ConnectorResult<Self> {
core::num::NonZeroUsize::new(value)
.map(Self::Size)
.ok_or(Error::new("invalid written length (cant be zero)"))
.ok_or(ConnectorError::general("invalid written length (cant be zero)"))
}
#[inline]
@@ -135,150 +138,94 @@ pub trait Sequence: Send + Sync {
fn state(&self) -> &dyn State;
fn step(&mut self, input: &[u8], output: &mut Vec<u8>) -> Result<Written>;
fn step(&mut self, input: &[u8], output: &mut Vec<u8>) -> ConnectorResult<Written>;
fn step_no_input(&mut self, output: &mut Vec<u8>) -> Result<Written> {
fn step_no_input(&mut self, output: &mut Vec<u8>) -> ConnectorResult<Written> {
self.step(&[], output)
}
}
ironrdp_pdu::assert_obj_safe!(Sequence);
pub type Result<T> = std::result::Result<T, Error>;
pub type ConnectorResult<T> = std::result::Result<T, ConnectorError>;
#[non_exhaustive]
#[derive(Debug)]
pub enum ErrorKind {
Pdu(ironrdp_pdu::Error),
pub enum ConnectorErrorKind {
Pdu(ironrdp_pdu::PduError),
Credssp(sspi::Error),
Reason(String),
AccessDenied,
Custom(Box<dyn std::error::Error + Sync + Send + 'static>),
General,
Custom,
}
#[derive(Debug)]
pub struct Error {
pub context: &'static str,
pub kind: ErrorKind,
pub reason: Option<String>,
}
impl Error {
pub fn new(context: &'static str) -> Self {
Self {
context,
kind: ErrorKind::General,
reason: None,
impl fmt::Display for ConnectorErrorKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self {
ConnectorErrorKind::Pdu(_) => write!(f, "PDU error"),
ConnectorErrorKind::Credssp(_) => write!(f, "CredSSP"),
ConnectorErrorKind::Reason(description) => write!(f, "reason: {description}"),
ConnectorErrorKind::AccessDenied => write!(f, "access denied"),
ConnectorErrorKind::General => write!(f, "general"),
ConnectorErrorKind::Custom => write!(f, "custom"),
}
}
}
pub fn with_kind(mut self, kind: ErrorKind) -> Self {
self.kind = kind;
self
impl std::error::Error for ConnectorErrorKind {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match &self {
ConnectorErrorKind::Pdu(e) => Some(e),
ConnectorErrorKind::Credssp(e) => Some(e),
ConnectorErrorKind::Reason(_) => None,
ConnectorErrorKind::AccessDenied => None,
ConnectorErrorKind::Custom => None,
ConnectorErrorKind::General => None,
}
}
}
pub type ConnectorError = ironrdp_error::Error<ConnectorErrorKind>;
pub trait ConnectorErrorExt {
fn pdu(error: ironrdp_pdu::PduError) -> Self;
fn general(context: &'static str) -> Self;
fn reason(context: &'static str, reason: impl Into<String>) -> Self;
fn custom<E>(context: &'static str, e: E) -> Self
where
E: std::error::Error + Sync + Send + 'static;
}
impl ConnectorErrorExt for ConnectorError {
fn pdu(error: ironrdp_pdu::PduError) -> Self {
Self::new("invalid payload", ConnectorErrorKind::Pdu(error))
}
pub fn with_custom<E>(mut self, custom_error: E) -> Self
fn general(context: &'static str) -> Self {
Self::new(context, ConnectorErrorKind::General)
}
fn reason(context: &'static str, reason: impl Into<String>) -> Self {
Self::new(context, ConnectorErrorKind::Reason(reason.into()))
}
fn custom<E>(context: &'static str, e: E) -> Self
where
E: std::error::Error + Sync + Send + 'static,
{
self.kind = ErrorKind::Custom(Box::new(custom_error));
self
}
pub fn with_reason(mut self, reason: impl Into<String>) -> Self {
self.reason = Some(reason.into());
self
}
}
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match &self.kind {
ErrorKind::Pdu(e) => Some(e),
ErrorKind::Credssp(e) => Some(e),
ErrorKind::AccessDenied => None,
ErrorKind::Custom(e) => Some(e.as_ref()),
ErrorKind::General => None,
}
}
}
impl From<Error> for std::io::Error {
fn from(error: Error) -> Self {
std::io::Error::new(std::io::ErrorKind::Other, error)
}
}
impl From<ironrdp_pdu::Error> for Error {
fn from(value: ironrdp_pdu::Error) -> Self {
Self {
context: "invalid payload",
kind: ErrorKind::Pdu(value),
reason: None,
}
}
}
impl From<sspi::Error> for Error {
fn from(value: sspi::Error) -> Self {
Self {
context: "CredSSP",
kind: ErrorKind::Credssp(value),
reason: None,
}
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.context)?;
match &self.kind {
ErrorKind::Pdu(e) => {
if f.alternate() {
write!(f, ": {e}")?;
}
}
ErrorKind::Credssp(e) => {
if f.alternate() {
write!(f, ": {e}")?;
}
}
ErrorKind::AccessDenied => {
write!(f, ": access denied")?;
}
ErrorKind::Custom(e) => {
if f.alternate() {
write!(f, ": {e}")?;
let mut next_source = e.source();
while let Some(e) = next_source {
write!(f, ", caused by: {e}")?;
next_source = e.source();
}
}
}
ErrorKind::General => {}
}
if let Some(reason) = &self.reason {
write!(f, " ({reason})")?;
}
Ok(())
Self::new(context, ConnectorErrorKind::Custom).with_source(e)
}
}
pub trait ConnectorResultExt {
fn with_context(self, context: &'static str) -> Self;
fn with_kind(self, kind: ErrorKind) -> Self;
fn with_custom<E>(self, custom_error: E) -> Self
fn with_source<E>(self, source: E) -> Self
where
E: std::error::Error + Sync + Send + 'static;
fn with_reason(self, reason: impl Into<String>) -> Self;
}
impl<T> ConnectorResultExt for Result<T> {
impl<T> ConnectorResultExt for ConnectorResult<T> {
fn with_context(self, context: &'static str) -> Self {
self.map_err(|mut e| {
e.context = context;
@@ -286,27 +233,10 @@ impl<T> ConnectorResultExt for Result<T> {
})
}
fn with_kind(self, kind: ErrorKind) -> Self {
self.map_err(|mut e| {
e.kind = kind;
e
})
}
fn with_custom<E>(self, custom_error: E) -> Self
fn with_source<E>(self, source: E) -> Self
where
E: std::error::Error + Sync + Send + 'static,
{
self.map_err(|mut e| {
e.kind = ErrorKind::Custom(Box::new(custom_error));
e
})
}
fn with_reason(self, reason: impl Into<String>) -> Self {
self.map_err(|mut e| {
e.reason = Some(reason.into());
e
})
self.map_err(|e| e.with_source(source))
}
}
@@ -5,7 +5,7 @@ use ironrdp_pdu::PduHint;
use rand_core::{OsRng, RngCore as _};
use super::legacy;
use crate::{Error, Result, Sequence, State, Written};
use crate::{ConnectorResult, Sequence, State, Written};
#[derive(Default, Debug)]
#[non_exhaustive]
@@ -79,10 +79,10 @@ impl Sequence for LicenseExchangeSequence {
&self.state
}
fn step(&mut self, input: &[u8], output: &mut Vec<u8>) -> Result<Written> {
fn step(&mut self, input: &[u8], output: &mut Vec<u8>) -> ConnectorResult<Written> {
let (written, next_state) = match mem::take(&mut self.state) {
LicenseExchangeState::Consumed => {
return Err(Error::new(
return Err(general_err!(
"license exchange sequence state is consumed (this is a bug)",
))
}
@@ -110,9 +110,7 @@ impl Sequence for LicenseExchangeSequence {
&self.username,
self.domain.as_deref().unwrap_or(""),
)
.map_err(|e| {
Error::new("unable to generate Client New License Request").with_reason(e.to_string())
})?;
.map_err(|e| custom_err!("ClientNewLicenseRequest", e))?;
trace!(?encryption_data, "Successfully generated Client New License Request");
info!(message = ?new_license_request, "Send");
@@ -150,9 +148,7 @@ impl Sequence for LicenseExchangeSequence {
self.domain.as_deref().unwrap_or(""),
&encryption_data,
)
.map_err(|e| {
Error::new("unable to generate Client Platform Challenge Response").with_reason(e.to_string())
})?;
.map_err(|e| custom_err!("ClientPlatformChallengeResponse", e))?;
debug!(message = ?challenge_response, "Send");
@@ -178,14 +174,14 @@ impl Sequence for LicenseExchangeSequence {
upgrade_license
.verify_server_license(&encryption_data)
.map_err(|e| Error::new("license verification failed").with_reason(e.to_string()))?;
.map_err(|e| custom_err!("license verification", e))?;
debug!("License verified with success");
(Written::Nothing, LicenseExchangeState::LicenseExchanged)
}
LicenseExchangeState::LicenseExchanged => return Err(Error::new("license already exchanged")),
LicenseExchangeState::LicenseExchanged => return Err(general_err!("license already exchanged")),
};
self.state = next_state;
+38
View File
@@ -0,0 +1,38 @@
/// Creates a `ConnectorError` with `General` kind
///
/// Shorthand for
/// ```rust
/// <crate::ConnectorError as crate::ConnectorErrorExt>::general(context)
/// ```
#[macro_export]
macro_rules! general_err {
( $context:expr $(,)? ) => {{
<$crate::ConnectorError as $crate::ConnectorErrorExt>::general($context)
}};
}
/// Creates a `ConnectorError` with `Reason` kind
///
/// Shorthand for
/// ```rust
/// <crate::ConnectorError as crate::ConnectorErrorExt>::reason(context, reason)
/// ```
#[macro_export]
macro_rules! reason_err {
( $context:expr, $($arg:tt)* ) => {{
<$crate::ConnectorError as $crate::ConnectorErrorExt>::reason($context, format!($($arg)*))
}};
}
/// Creates a `ConnectorError` with `Custom` kind and a source error attached to it
///
/// Shorthand for
/// ```rust
/// <crate::ConnectorError as crate::ConnectorErrorExt>::custom(context, source)
/// ```
#[macro_export]
macro_rules! custom_err {
( $context:expr, $source:expr $(,)? ) => {{
<$crate::ConnectorError as $crate::ConnectorErrorExt>::custom($context, $source)
}};
}
+17
View File
@@ -0,0 +1,17 @@
[package]
name = "ironrdp-error"
version = "0.1.0"
readme = "README.md"
description = "IronPDU generic error definition"
edition.workspace = true
license.workspace = true
homepage.workspace = true
repository.workspace = true
authors.workspace = true
keywords.workspace = true
categories.workspace = true
[features]
default = []
std = ["alloc"]
alloc = []
+189
View File
@@ -0,0 +1,189 @@
#![cfg_attr(not(feature = "std"), no_std)]
#[cfg(feature = "alloc")]
extern crate alloc;
use core::fmt;
#[cfg(all(not(feature = "std"), feature = "alloc"))]
trait NoAllocSource: fmt::Display + fmt::Debug {}
#[cfg(all(not(feature = "std"), feature = "alloc"))]
impl<T> NoAllocSource for T where T: fmt::Display + fmt::Debug {}
#[derive(Debug)]
pub struct Error<Kind> {
pub context: &'static str,
pub kind: Kind,
#[cfg(feature = "std")]
source: Option<alloc::boxed::Box<dyn std::error::Error + Sync + Send + 'static>>,
#[cfg(all(not(feature = "std"), feature = "alloc"))]
source: Option<alloc::boxed::Box<dyn NoAllocSource + Sync + Send + 'static>>,
}
impl<Kind> Error<Kind> {
#[cold]
pub fn new(context: &'static str, kind: Kind) -> Self {
Self {
context,
kind,
#[cfg(feature = "alloc")]
source: None,
}
}
#[cfg(feature = "std")]
#[cold]
pub fn with_source<E>(mut self, source: E) -> Self
where
E: std::error::Error + Sync + Send + 'static,
{
self.source = Some(Box::new(source));
self
}
#[cfg(all(not(feature = "std"), feature = "alloc"))]
#[cold]
pub fn with_source<E>(mut self, source: E) -> Self
where
E: fmt::Display + fmt::Debug + Sync + Send + 'static,
{
#[cfg(feature = "alloc")]
{
self.source = Some(alloc::boxed::Box::new(source));
}
// No source when no std and no alloc crates
#[cfg(not(feature = "alloc"))]
{
let _ = source;
}
self
}
pub fn into_other_kind<OtherKind>(self) -> Error<OtherKind>
where
Kind: Into<OtherKind>,
{
Error {
context: self.context,
kind: self.kind.into(),
#[cfg(any(feature = "std", feature = "alloc"))]
source: self.source,
}
}
pub fn kind(&self) -> &Kind {
&self.kind
}
pub fn report(&self) -> ErrorReport<'_, Kind> {
ErrorReport(self)
}
}
impl<Kind> fmt::Display for Error<Kind>
where
Kind: fmt::Display,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "[{}] {}", self.context, self.kind)
}
}
#[cfg(feature = "std")]
impl<Kind> std::error::Error for Error<Kind>
where
Kind: std::error::Error,
{
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
if let Some(source) = self.kind.source() {
Some(source)
} else {
// NOTE: we cant use Option::as_ref here because of type inference
if let Some(e) = &self.source {
Some(e.as_ref())
} else {
None
}
}
}
}
#[cfg(feature = "std")]
impl<Kind> From<Error<Kind>> for std::io::Error
where
Kind: std::error::Error + Send + Sync + 'static,
{
fn from(error: Error<Kind>) -> Self {
std::io::Error::new(std::io::ErrorKind::Other, error)
}
}
pub struct ErrorReport<'a, Kind>(&'a Error<Kind>);
#[cfg(feature = "std")]
impl<Kind> fmt::Display for ErrorReport<'_, Kind>
where
Kind: std::error::Error,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use std::error::Error;
write!(f, "{}", self.0)?;
let mut next_source = self.0.source();
while let Some(e) = next_source {
write!(f, ", caused by: {e}")?;
next_source = e.source();
}
Ok(())
}
}
#[cfg(not(feature = "std"))]
impl<E> fmt::Display for ErrorReport<'_, E>
where
E: fmt::Display,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)?;
#[cfg(feature = "alloc")]
if let Some(source) = &self.0.source {
write!(f, ", caused by: {source}")?;
}
Ok(())
}
}
/// Temporary compability traits to smooth transition from old style
#[cfg(feature = "std")]
#[doc(hidden)]
pub mod legacy {
#[doc(hidden)]
pub trait CatchAllKind {
const CATCH_ALL_VALUE: Self;
}
#[doc(hidden)]
pub trait ErrorContext: std::error::Error {
fn context(&self) -> &'static str;
}
#[doc(hidden)]
impl<E, Kind> From<E> for crate::Error<Kind>
where
E: ErrorContext + Send + Sync + 'static,
Kind: CatchAllKind,
{
#[cold]
fn from(error: E) -> Self {
Self::new(error.context(), Kind::CATCH_ALL_VALUE).with_source(error)
}
}
}
+7 -6
View File
@@ -16,15 +16,16 @@ doctest = false
# test = false
[dependencies]
ironrdp-pdu.workspace = true
num-traits = "0.2.15"
num-derive = "0.3.3"
byteorder = "1.4.3"
thiserror = "1.0.40"
bitvec = "1.0.1"
bit_field = "0.10.2"
bitflags = "2"
bitvec = "1.0.1"
byteorder = "1.4.3"
ironrdp-error.workspace = true
ironrdp-pdu.workspace = true
lazy_static = "1.4.0"
num-derive = "0.3.3"
num-traits = "0.2.15"
thiserror = "1.0.40"
[dev-dependencies]
bmp = "0.5"
@@ -1,5 +1,5 @@
use ironrdp_pdu::bitmap::rdp6::{BitmapStream as BitmapStreamPdu, ColorPlanes};
use ironrdp_pdu::{decode, Error as PduError};
use ironrdp_pdu::{decode, PduError};
use thiserror::Error;
use crate::color_conversion::{Rgb, YCoCg};
+6
View File
@@ -447,6 +447,12 @@ pub enum ZgfxError {
TokenBitsNotFound,
}
impl ironrdp_error::legacy::ErrorContext for ZgfxError {
fn context(&self) -> &'static str {
"zgfx"
}
}
#[cfg(test)]
mod tests {
use super::*;

Some files were not shown because too many files have changed in this diff Show More