ironrdp_dvc/
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#![cfg_attr(not(feature = "std"), no_std)]
4
5extern crate alloc;
6
7use alloc::boxed::Box;
8use alloc::collections::BTreeMap;
9use alloc::string::String;
10use alloc::vec::Vec;
11use core::any::TypeId;
12
13use pdu::DrdynvcDataPdu;
14
15use crate::alloc::borrow::ToOwned as _;
16// Re-export ironrdp_pdu crate for convenience
17#[rustfmt::skip] // do not re-order this pub use
18pub use ironrdp_pdu;
19use ironrdp_core::{assert_obj_safe, cast_length, encode_vec, other_err, AsAny, Encode, EncodeResult};
20use ironrdp_pdu::{decode_err, pdu_other_err, PduResult};
21use ironrdp_svc::SvcMessage;
22
23mod complete_data;
24use complete_data::CompleteData;
25
26mod client;
27pub use client::*;
28
29mod server;
30pub use server::*;
31
32pub mod pdu;
33
34/// Represents a message that, when encoded, forms a complete PDU for a given dynamic virtual channel.
35/// This means a message that is ready to be wrapped in [`pdu::DataFirstPdu`] and [`pdu::DataPdu`] PDUs
36/// (being split into multiple of such PDUs if necessary).
37pub trait DvcEncode: Encode + Send {}
38pub type DvcMessage = Box<dyn DvcEncode>;
39
40/// A type that is a Dynamic Virtual Channel (DVC)
41///
42/// Dynamic virtual channels may be created at any point during the RDP session.
43/// The Dynamic Virtual Channel APIs exist to address limitations of Static Virtual Channels:
44///   - Limited number of channels
45///   - Packet reconstruction
46pub trait DvcProcessor: AsAny + Send {
47    /// The name of the channel, e.g. "Microsoft::Windows::RDS::DisplayControl"
48    fn channel_name(&self) -> &str;
49
50    /// Returns any messages that should be sent immediately
51    /// upon the channel being created.
52    fn start(&mut self, channel_id: u32) -> PduResult<Vec<DvcMessage>>;
53
54    fn process(&mut self, channel_id: u32, payload: &[u8]) -> PduResult<Vec<DvcMessage>>;
55
56    fn close(&mut self, _channel_id: u32) {}
57}
58
59assert_obj_safe!(DvcProcessor);
60
61pub fn encode_dvc_messages(
62    channel_id: u32,
63    messages: Vec<DvcMessage>,
64    flags: ironrdp_svc::ChannelFlags,
65) -> EncodeResult<Vec<SvcMessage>> {
66    let mut res = Vec::new();
67    for msg in messages {
68        let total_length = msg.size();
69        let needs_splitting = total_length >= DrdynvcDataPdu::MAX_DATA_SIZE;
70
71        let msg = encode_vec(msg.as_ref())?;
72        let mut off = 0;
73
74        while off < total_length {
75            let first = off == 0;
76
77            #[expect(clippy::missing_panics_doc, reason = "unreachable panic (checked underflow)")]
78            let remaining_length = total_length.checked_sub(off).expect("never overflow");
79            let size = core::cmp::min(remaining_length, DrdynvcDataPdu::MAX_DATA_SIZE);
80            let end = off
81                .checked_add(size)
82                .ok_or_else(|| other_err!("encode_dvc_messages", "overflow occurred"))?;
83
84            let pdu = if needs_splitting && first {
85                DrdynvcDataPdu::DataFirst(pdu::DataFirstPdu::new(
86                    channel_id,
87                    cast_length!("total_length", total_length)?,
88                    msg[off..end].to_vec(),
89                ))
90            } else {
91                DrdynvcDataPdu::Data(pdu::DataPdu::new(channel_id, msg[off..end].to_vec()))
92            };
93
94            let svc = SvcMessage::from(pdu).with_flags(flags);
95
96            res.push(svc);
97            off = end;
98        }
99    }
100
101    Ok(res)
102}
103
104pub struct DynamicVirtualChannel {
105    channel_processor: Box<dyn DvcProcessor + Send>,
106    complete_data: CompleteData,
107    /// The channel ID assigned by the server.
108    ///
109    /// This field is `None` until the server assigns a channel ID.
110    channel_id: Option<DynamicChannelId>,
111}
112
113impl DynamicVirtualChannel {
114    fn new<T: DvcProcessor + 'static>(handler: T) -> Self {
115        Self {
116            channel_processor: Box::new(handler),
117            complete_data: CompleteData::new(),
118            channel_id: None,
119        }
120    }
121
122    pub fn is_open(&self) -> bool {
123        self.channel_id.is_some()
124    }
125
126    pub fn channel_id(&self) -> Option<DynamicChannelId> {
127        self.channel_id
128    }
129
130    pub fn channel_processor_downcast_ref<T: DvcProcessor>(&self) -> Option<&T> {
131        self.channel_processor.as_any().downcast_ref()
132    }
133
134    fn start(&mut self) -> PduResult<Vec<DvcMessage>> {
135        if let Some(channel_id) = self.channel_id {
136            self.channel_processor.start(channel_id)
137        } else {
138            Err(pdu_other_err!("DynamicVirtualChannel::start", "channel ID not set"))
139        }
140    }
141
142    fn process(&mut self, pdu: DrdynvcDataPdu) -> PduResult<Vec<DvcMessage>> {
143        let channel_id = pdu.channel_id();
144        let complete_data = self.complete_data.process_data(pdu).map_err(|e| decode_err!(e))?;
145        if let Some(complete_data) = complete_data {
146            self.channel_processor.process(channel_id, &complete_data)
147        } else {
148            Ok(Vec::new())
149        }
150    }
151
152    fn channel_name(&self) -> &str {
153        self.channel_processor.channel_name()
154    }
155}
156
157struct DynamicChannelSet {
158    channels: BTreeMap<DynamicChannelName, DynamicVirtualChannel>,
159    name_to_channel_id: BTreeMap<DynamicChannelName, DynamicChannelId>,
160    channel_id_to_name: BTreeMap<DynamicChannelId, DynamicChannelName>,
161    type_id_to_name: BTreeMap<TypeId, DynamicChannelName>,
162}
163
164impl DynamicChannelSet {
165    #[inline]
166    fn new() -> Self {
167        Self {
168            channels: BTreeMap::new(),
169            name_to_channel_id: BTreeMap::new(),
170            channel_id_to_name: BTreeMap::new(),
171            type_id_to_name: BTreeMap::new(),
172        }
173    }
174
175    fn insert<T: DvcProcessor + 'static>(&mut self, channel: T) -> Option<DynamicVirtualChannel> {
176        let name = channel.channel_name().to_owned();
177        self.type_id_to_name.insert(TypeId::of::<T>(), name.clone());
178        self.channels.insert(name, DynamicVirtualChannel::new(channel))
179    }
180
181    fn attach_channel_id(&mut self, name: DynamicChannelName, id: DynamicChannelId) -> Option<DynamicChannelId> {
182        self.channel_id_to_name.insert(id, name.clone());
183        self.name_to_channel_id.insert(name.clone(), id);
184        let dvc = self.get_by_channel_name_mut(&name)?;
185        let old_id = dvc.channel_id;
186        dvc.channel_id = Some(id);
187        old_id
188    }
189
190    fn get_by_type_id(&self, type_id: TypeId) -> Option<&DynamicVirtualChannel> {
191        self.type_id_to_name
192            .get(&type_id)
193            .and_then(|name| self.channels.get(name))
194    }
195
196    fn get_by_channel_name(&self, name: &DynamicChannelName) -> Option<&DynamicVirtualChannel> {
197        self.channels.get(name)
198    }
199
200    fn get_by_channel_name_mut(&mut self, name: &DynamicChannelName) -> Option<&mut DynamicVirtualChannel> {
201        self.channels.get_mut(name)
202    }
203
204    fn get_by_channel_id(&self, id: DynamicChannelId) -> Option<&DynamicVirtualChannel> {
205        self.channel_id_to_name
206            .get(&id)
207            .and_then(|name| self.channels.get(name))
208    }
209
210    fn get_by_channel_id_mut(&mut self, id: DynamicChannelId) -> Option<&mut DynamicVirtualChannel> {
211        self.channel_id_to_name
212            .get(&id)
213            .and_then(|name| self.channels.get_mut(name))
214    }
215
216    fn remove_by_channel_id(&mut self, id: DynamicChannelId) -> Option<DynamicChannelId> {
217        if let Some(name) = self.channel_id_to_name.remove(&id) {
218            return self.name_to_channel_id.remove(&name);
219            // Channels are retained in the `self.channels` and `self.type_id_to_name` map to allow potential
220            // dynamic re-addition by the server.
221        }
222        None
223    }
224
225    #[inline]
226    fn values(&self) -> impl Iterator<Item = &DynamicVirtualChannel> {
227        self.channels.values()
228    }
229}
230
231pub type DynamicChannelName = String;
232pub type DynamicChannelId = u32;