Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions crates/ironrdp-rdpdr/src/backend/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use ironrdp_core::AsAny;
use ironrdp_pdu::PduResult;
use ironrdp_svc::SvcMessage;

use crate::Rdpdr;
use crate::pdu::efs::{DeviceControlRequest, ServerDeviceAnnounceResponse, ServerDriveIoRequest};
use crate::pdu::esc::{ScardCall, ScardIoCtlCode};

Expand All @@ -14,4 +15,7 @@ pub trait RdpdrBackend: AsAny + fmt::Debug + Send {
fn handle_server_device_announce_response(&mut self, pdu: ServerDeviceAnnounceResponse) -> PduResult<()>;
fn handle_scard_call(&mut self, req: DeviceControlRequest<ScardIoCtlCode>, call: ScardCall) -> PduResult<()>;
fn handle_drive_io_request(&mut self, req: ServerDriveIoRequest) -> PduResult<Vec<SvcMessage>>;
fn handle_user_logged_on(&mut self, _rdpdr: &mut Rdpdr) -> PduResult<Vec<SvcMessage>> {
Ok(Vec::new())
}
}
46 changes: 33 additions & 13 deletions crates/ironrdp-rdpdr/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ pub struct Rdpdr {
///
/// All devices not of the type [`DeviceType::Filesystem`] must be declared here.
device_list: Devices,
backend: Box<dyn RdpdrBackend>,
backend: Option<Box<dyn RdpdrBackend>>,
}

impl_as_any!(Rdpdr);
Expand All @@ -56,7 +56,7 @@ impl Rdpdr {
computer_name,
capabilities: Capabilities::new(),
device_list: Devices::new(),
backend,
backend: Some(backend),
}
}

Expand Down Expand Up @@ -97,11 +97,11 @@ impl Rdpdr {
}

pub fn downcast_backend<T: RdpdrBackend>(&self) -> Option<&T> {
self.backend.as_any().downcast_ref::<T>()
self.backend.as_ref()?.as_any().downcast_ref::<T>()
}

pub fn downcast_backend_mut<T: RdpdrBackend>(&mut self) -> Option<&mut T> {
self.backend.as_any_mut().downcast_mut::<T>()
self.backend.as_mut()?.as_any_mut().downcast_mut::<T>()
}

fn handle_server_announce(&mut self, req: VersionAndIdPdu) -> PduResult<Vec<SvcMessage>> {
Expand Down Expand Up @@ -139,20 +139,33 @@ impl Rdpdr {
&mut self,
pdu: ServerDeviceAnnounceResponse,
) -> PduResult<Vec<SvcMessage>> {
self.backend.handle_server_device_announce_response(pdu)?;
self.backend
.as_mut()
.ok_or_else(|| pdu_other_err!("missing rdpdr backend"))?
.handle_server_device_announce_response(pdu)?;
Ok(Vec::new())
}

fn handle_user_logged_on(&mut self) -> PduResult<Vec<SvcMessage>> {
let mut backend = self.backend.take().expect("missing rdpdr backend");
let res = backend.handle_user_logged_on(self);
self.backend = Some(backend);
Comment on lines +150 to +152
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

handle_user_logged_on temporarily take()s the backend, but if backend.handle_user_logged_on(self) returns Err, the ? early-returns before self.backend is restored. That leaves self.backend = None permanently and can cause later processing to fail/panic. Restore the backend before propagating the error (e.g., capture the result first, put the backend back, then ?), and avoid expect() panics by returning a PduError like the other handlers.

Suggested change
let mut backend = self.backend.take().expect("missing rdpdr backend");
let res = backend.handle_user_logged_on(self)?;
self.backend = Some(backend);
let mut backend = self
.backend
.take()
.ok_or_else(|| pdu_other_err!("missing rdpdr backend"))?;
let res = backend.handle_user_logged_on(self);
self.backend = Some(backend);
let res = res?;

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in latest commit!

res.inspect(|response| trace!("sending {:?}", response))
}

fn handle_device_io_request(
&mut self,
dev_io_req: DeviceIoRequest,
src: &mut ReadCursor<'_>,
) -> PduResult<Vec<SvcMessage>> {
match self
.device_list
.for_device_type(dev_io_req.device_id)
.map_err(|e| decode_err!(e))?
{
let Ok(device_type) = self.device_list.for_device_type(dev_io_req.device_id) else {
// > If a request is received that contains a DeviceId field that was not announced by the client or has
// > been removed, the request SHOULD be ignored by the implementation.
// source: https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpefs/9925f2e4-8d5a-4777-a41a-7ba6ef6e8bff
return Ok(vec![]);
};

match device_type {
DeviceType::Smartcard => {
let req =
DeviceControlRequest::<ScardIoCtlCode>::decode(dev_io_req, src).map_err(|e| decode_err!(e))?;
Expand All @@ -161,7 +174,10 @@ impl Rdpdr {
debug!(?req);
debug!(?req.io_control_code, ?call);

self.backend.handle_scard_call(req, call)?;
self.backend
.as_mut()
.ok_or_else(|| pdu_other_err!("missing rdpdr backend"))?
.handle_scard_call(req, call)?;

Ok(Vec::new())
}
Expand All @@ -170,7 +186,11 @@ impl Rdpdr {

debug!(?req);

Ok(self.backend.handle_drive_io_request(req)?)
Ok(self
.backend
.as_mut()
.ok_or_else(|| pdu_other_err!("missing rdpdr backend"))?
.handle_drive_io_request(req)?)
}
_ => {
// This should never happen, as we only announce devices that we support.
Expand Down Expand Up @@ -207,7 +227,7 @@ impl SvcProcessor for Rdpdr {
}
RdpdrPdu::ServerDeviceAnnounceResponse(pdu) => self.handle_server_device_announce_response(pdu),
RdpdrPdu::DeviceIoRequest(pdu) => self.handle_device_io_request(pdu, &mut src),
RdpdrPdu::UserLoggedon => Ok(vec![]),
RdpdrPdu::UserLoggedon => self.handle_user_logged_on(),
// TODO: This can eventually become a `_ => {}` block, but being explicit for now
// to make sure we don't miss handling new RdpdrPdu variants here during active development.
RdpdrPdu::ClientNameRequest(_)
Expand Down
Loading