-
Notifications
You must be signed in to change notification settings - Fork 170
Labels
A-virtual-channelArea: Static or dynamic virtual channelArea: Static or dynamic virtual channelenhancementNew feature or requestNew feature or requestgood first issueGood for newcomersGood for newcomers
Description
MS-RDPEUSB (and potentially other protocols) requires opening multiple DVC channels that share the same channel name but have distinct channel IDs. The current DynamicChannelSet in ironrdp-dvc uses channel name as its primary key, which makes this impossible.
Depends on: nothing
Tasks
- Add a DvcChannelListener trait (factory) to lib.rs that produces a new DvcClientProcessor instance per DYNVC_CREATE_REQ:
pub trait DvcChannelListener: Send {
fn channel_name(&self) -> &str;
/// Called for each new DYNVC_CREATE_REQ for this name.
fn on_channel_creation(&mut self, channel_id: u32) -> Box<dyn DvcClientProcessor + Send>;
}- Refactor DynamicChannelSet to store entries keyed by DynamicChannelId rather than DynamicChannelName; maintain a name → listener map for creation
- Update DrdynvcClient::process() to call the listener factory on DYNVC_CREATE_REQ instead of looking up a pre-registered processor by name
- Add DrdynvcClient::with_listener() / attach_listener() alongside the existing with_dynamic_channel() / attach_dynamic_channel() (keep the old API for single-instance channels)
- Add a take_new_channels() drain hook to DvcServerProcessor in server.rs; update DrdynvcServer::process() to drain it after each process() call and emit the corresponding DYNVC_CREATE_REQ PDUs
// Add a post-process drain hook on DvcServerProcessor:
pub trait DvcServerProcessor: DvcProcessor {
/// Called by DrdynvcServer after each process().
/// Return new processor instances to be opened as new DVC channels.
fn take_new_channels(&mut self) -> Vec<Box<dyn DvcServerProcessor + Send>> {
Vec::new()
}
}
// Then in DrdynvcServer::process(), after calling c.processor.process(...):
// Drain any new channels the processor wants to open.
for new_proc in c.processor.take_new_channels() {
let id = self.dynamic_channels.insert(DynamicChannel::new_boxed(new_proc));
let channel_id = u32::try_from(id).expect("...");
let channel = &self.dynamic_channels[id];
let req = DrdynvcServerPdu::Create(CreateRequestPdu::new(
channel_id,
channel.processor.channel_name().into(),
));
self.dynamic_channels[id].state = ChannelState::Creation;
resp.push(as_svc_msg_with_flag(req)?);
}
// The URBDRC control processor would then:
fn process(&mut self, channel_id: u32, payload: &[u8]) -> PduResult<Vec<DvcMessage>> {
// ...
self.pending_new_channels.push(Box::new(UsbDeviceChannel::new()));
Ok(vec![])
}
fn take_new_channels(&mut self) -> Vec<Box<dyn DvcServerProcessor + Send>> {
self.pending_new_channels.drain(..).collect()
}EDIT: See this comment: #1135 (comment)
Acceptance criteria
- A channel name can have multiple simultaneous open instances, each with its own independent processor and fragmentation buffer
- Existing single-instance channels (displaycontrol, cliprdr, etc.) continue to work unchanged via with_dynamic_channel()
- Server can open new DVC channels mid-session in response to processor requests
- Unit tests covering multi-instance creation, data routing by channel ID, and close handling
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
A-virtual-channelArea: Static or dynamic virtual channelArea: Static or dynamic virtual channelenhancementNew feature or requestNew feature or requestgood first issueGood for newcomersGood for newcomers