ironrdp_rdpdr/
lib.rs

1#![cfg_attr(doc, doc = include_str!("../README.md"))]
2#![doc(html_logo_url = "https://cdnweb.devolutions.net/images/projects/devolutions/logos/devolutions-icon-shadow.svg")]
3#![allow(clippy::arithmetic_side_effects)] // FIXME: remove
4
5use ironrdp_core::{decode_cursor, impl_as_any, ReadCursor};
6use ironrdp_pdu::gcc::ChannelName;
7use ironrdp_pdu::{decode_err, pdu_other_err, PduResult};
8use ironrdp_svc::{CompressionCondition, SvcClientProcessor, SvcMessage, SvcProcessor};
9use pdu::efs::{
10    Capabilities, ClientDeviceListAnnounce, ClientDeviceListRemove, ClientNameRequest, ClientNameRequestUnicodeFlag,
11    CoreCapability, CoreCapabilityKind, DeviceControlRequest, DeviceIoRequest, DeviceType, Devices,
12    ServerDeviceAnnounceResponse, VersionAndIdPdu, VersionAndIdPduKind,
13};
14use pdu::esc::{ScardCall, ScardIoCtlCode};
15use pdu::RdpdrPdu;
16use tracing::{debug, trace, warn};
17
18pub mod backend;
19pub mod pdu;
20
21pub use self::backend::noop::NoopRdpdrBackend;
22pub use self::backend::RdpdrBackend;
23use crate::pdu::efs::ServerDriveIoRequest;
24
25/// The RDPDR channel as specified in [\[MS-RDPEFS\]].
26///
27/// This channel must always be advertised with the "rdpsnd"
28/// channel in order for the server to send anything back to it,
29/// see: [\[MS-RDPEFS\] Appendix A<1>]
30///
31/// [\[MS-RDPEFS\]]: https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpefs/34d9de58-b2b5-40b6-b970-f82d4603bdb5
32/// [\[MS-RDPEFS\] Appendix A<1>]: https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpefs/fd28bfd9-dae2-4a78-abe1-b4efa208b7aa#Appendix_A_1
33#[derive(Debug)]
34pub struct Rdpdr {
35    /// The name of the computer that is running the client.
36    ///
37    /// Any directories shared will be displayed by File Explorer
38    /// as "`<directory>` on `<computer_name>`".
39    computer_name: String,
40    capabilities: Capabilities,
41    /// Pre-configured list of devices to announce to the server.
42    ///
43    /// All devices not of the type [`DeviceType::Filesystem`] must be declared here.
44    device_list: Devices,
45    backend: Box<dyn RdpdrBackend>,
46}
47
48impl_as_any!(Rdpdr);
49
50impl Rdpdr {
51    pub const NAME: ChannelName = ChannelName::from_static(b"rdpdr\0\0\0");
52
53    /// Creates a new [`Rdpdr`].
54    pub fn new(backend: Box<dyn RdpdrBackend>, computer_name: String) -> Self {
55        Self {
56            computer_name,
57            capabilities: Capabilities::new(),
58            device_list: Devices::new(),
59            backend,
60        }
61    }
62
63    #[must_use]
64    pub fn with_smartcard(mut self, device_id: u32) -> Self {
65        self.capabilities.add_smartcard();
66        self.device_list.add_smartcard(device_id);
67        self
68    }
69
70    /// Adds drive redirection capability.
71    ///
72    /// Callers may also include `initial_drives` to pre-configure the list of drives to announce to the server.
73    /// Note that drives do not need to be pre-configured in order to be redirected, a new drive can be announced
74    /// at any time during a session by calling [`Self::add_drive`].
75    #[must_use]
76    pub fn with_drives(mut self, initial_drives: Option<Vec<(u32, String)>>) -> Self {
77        self.capabilities.add_drive();
78        if let Some(initial_drives) = initial_drives {
79            for (device_id, path) in initial_drives {
80                self.device_list.add_drive(device_id, path);
81            }
82        }
83        self
84    }
85
86    /// Users should call this method to announce a new drive to the server. It's the caller's responsibility
87    /// to take the returned [`ClientDeviceListAnnounce`] and send it to the server.
88    pub fn add_drive(&mut self, device_id: u32, name: String) -> ClientDeviceListAnnounce {
89        self.device_list.add_drive(device_id, name.clone());
90        ClientDeviceListAnnounce::new_drive(device_id, name)
91    }
92
93    pub fn remove_device(&mut self, device_id: u32) -> Option<ClientDeviceListRemove> {
94        Some(ClientDeviceListRemove::remove_device(
95            self.device_list.remove_device(device_id)?,
96        ))
97    }
98
99    pub fn downcast_backend<T: RdpdrBackend>(&self) -> Option<&T> {
100        self.backend.as_any().downcast_ref::<T>()
101    }
102
103    pub fn downcast_backend_mut<T: RdpdrBackend>(&mut self) -> Option<&mut T> {
104        self.backend.as_any_mut().downcast_mut::<T>()
105    }
106
107    fn handle_server_announce(&mut self, req: VersionAndIdPdu) -> PduResult<Vec<SvcMessage>> {
108        let client_announce_reply =
109            RdpdrPdu::VersionAndIdPdu(VersionAndIdPdu::new_client_announce_reply(req).map_err(|e| decode_err!(e))?);
110        trace!("sending {:?}", client_announce_reply);
111
112        let client_name_request = RdpdrPdu::ClientNameRequest(ClientNameRequest::new(
113            self.computer_name.clone(),
114            ClientNameRequestUnicodeFlag::Unicode,
115        ));
116        trace!("sending {:?}", client_name_request);
117
118        Ok(vec![
119            SvcMessage::from(client_announce_reply),
120            SvcMessage::from(client_name_request),
121        ])
122    }
123
124    fn handle_server_capability(&mut self, _req: CoreCapability) -> PduResult<Vec<SvcMessage>> {
125        let res = RdpdrPdu::CoreCapability(CoreCapability::new_response(self.capabilities.clone_inner()));
126        trace!("sending {:?}", res);
127        Ok(vec![SvcMessage::from(res)])
128    }
129
130    fn handle_client_id_confirm(&mut self) -> PduResult<Vec<SvcMessage>> {
131        let res = RdpdrPdu::ClientDeviceListAnnounce(ClientDeviceListAnnounce {
132            device_list: self.device_list.clone_inner(),
133        });
134        trace!("sending {:?}", res);
135        Ok(vec![SvcMessage::from(res)])
136    }
137
138    fn handle_server_device_announce_response(
139        &mut self,
140        pdu: ServerDeviceAnnounceResponse,
141    ) -> PduResult<Vec<SvcMessage>> {
142        self.backend.handle_server_device_announce_response(pdu)?;
143        Ok(Vec::new())
144    }
145
146    fn handle_device_io_request(
147        &mut self,
148        dev_io_req: DeviceIoRequest,
149        src: &mut ReadCursor<'_>,
150    ) -> PduResult<Vec<SvcMessage>> {
151        match self
152            .device_list
153            .for_device_type(dev_io_req.device_id)
154            .map_err(|e| decode_err!(e))?
155        {
156            DeviceType::Smartcard => {
157                let req =
158                    DeviceControlRequest::<ScardIoCtlCode>::decode(dev_io_req, src).map_err(|e| decode_err!(e))?;
159                let call = ScardCall::decode(req.io_control_code, src).map_err(|e| decode_err!(e))?;
160
161                debug!(?req);
162                debug!(?req.io_control_code, ?call);
163
164                self.backend.handle_scard_call(req, call)?;
165
166                Ok(Vec::new())
167            }
168            DeviceType::Filesystem => {
169                let req = ServerDriveIoRequest::decode(dev_io_req, src).map_err(|e| decode_err!(e))?;
170
171                debug!(?req);
172
173                Ok(self.backend.handle_drive_io_request(req)?)
174            }
175            _ => {
176                // This should never happen, as we only announce devices that we support.
177                warn!(?dev_io_req, "received packet for unsupported device type");
178                Ok(Vec::new())
179            }
180        }
181    }
182}
183
184impl SvcProcessor for Rdpdr {
185    fn channel_name(&self) -> ChannelName {
186        Self::NAME
187    }
188
189    fn compression_condition(&self) -> CompressionCondition {
190        CompressionCondition::WhenRdpDataIsCompressed
191    }
192
193    fn process(&mut self, payload: &[u8]) -> PduResult<Vec<SvcMessage>> {
194        let mut src = ReadCursor::new(payload);
195        let pdu = decode_cursor::<RdpdrPdu>(&mut src).map_err(|e| decode_err!(e))?;
196        debug!("Received {:?}", pdu);
197
198        match pdu {
199            RdpdrPdu::VersionAndIdPdu(pdu) if pdu.kind == VersionAndIdPduKind::ServerAnnounceRequest => {
200                self.handle_server_announce(pdu)
201            }
202            RdpdrPdu::CoreCapability(pdu) if pdu.kind == CoreCapabilityKind::ServerCoreCapabilityRequest => {
203                self.handle_server_capability(pdu)
204            }
205            RdpdrPdu::VersionAndIdPdu(pdu) if pdu.kind == VersionAndIdPduKind::ServerClientIdConfirm => {
206                self.handle_client_id_confirm()
207            }
208            RdpdrPdu::ServerDeviceAnnounceResponse(pdu) => self.handle_server_device_announce_response(pdu),
209            RdpdrPdu::DeviceIoRequest(pdu) => self.handle_device_io_request(pdu, &mut src),
210            RdpdrPdu::UserLoggedon => Ok(vec![]),
211            // TODO: This can eventually become a `_ => {}` block, but being explicit for now
212            // to make sure we don't miss handling new RdpdrPdu variants here during active development.
213            RdpdrPdu::ClientNameRequest(_)
214            | RdpdrPdu::ClientDeviceListAnnounce(_)
215            | RdpdrPdu::ClientDeviceListRemove(_)
216            | RdpdrPdu::VersionAndIdPdu(_)
217            | RdpdrPdu::CoreCapability(_)
218            | RdpdrPdu::DeviceControlResponse(_)
219            | RdpdrPdu::DeviceCreateResponse(_)
220            | RdpdrPdu::ClientDriveQueryInformationResponse(_)
221            | RdpdrPdu::DeviceCloseResponse(_)
222            | RdpdrPdu::ClientDriveQueryDirectoryResponse(_)
223            | RdpdrPdu::ClientDriveQueryVolumeInformationResponse(_)
224            | RdpdrPdu::DeviceReadResponse(_)
225            | RdpdrPdu::DeviceWriteResponse(_)
226            | RdpdrPdu::ClientDriveSetInformationResponse(_)
227            | RdpdrPdu::EmptyResponse => Err(pdu_other_err!("Rdpdr", "received unexpected packet")),
228        }
229    }
230}
231
232impl SvcClientProcessor for Rdpdr {}