mirror of
https://github.com/netbirdio/IronRDP.git
synced 2026-05-22 18:43:12 -07:00
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:
committed by
Benoît Cortier
parent
8857c3c25e
commit
cf2287739d
@@ -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
@@ -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",
|
||||
|
||||
@@ -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" }
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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("can’t satisfy server security settings"));
|
||||
return Err(general_err!("can’t 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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 (can’t be zero)"))
|
||||
.ok_or(ConnectorError::general("invalid written length (can’t 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;
|
||||
|
||||
@@ -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)
|
||||
}};
|
||||
}
|
||||
@@ -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 = []
|
||||
@@ -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 can’t 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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};
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user