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
11 changes: 11 additions & 0 deletions crates/ironrdp-rdpeusb/src/pdu/sink.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use ironrdp_dvc::DvcEncode;
use ironrdp_pdu::utils::strict_sum;
use ironrdp_str::multi_sz::MultiSzString;
use ironrdp_str::prefixed::Cch32String;
use ironrdp_usb::UsbSpeed;

use crate::pdu::header::{FunctionId, InterfaceId, Mask, MessageId, SharedMsgHeader};

Expand Down Expand Up @@ -410,6 +411,16 @@ impl DeviceSpeed {
}
}

impl From<DeviceSpeed> for UsbSpeed {
fn from(value: DeviceSpeed) -> Self {
if value == DeviceSpeed::FULL_SPEED {
Self::Full
} else {
Self::High
}
}
}
Comment thread
uchouT marked this conversation as resolved.

#[repr(transparent)]
#[derive(Debug, PartialEq, Clone, Copy)]
pub struct NoAckIsochWriteJitterBufSizeInMs(u32);
Expand Down
6 changes: 3 additions & 3 deletions crates/ironrdp-server/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ pub use ironrdp_acceptor::Acceptor;
pub use ironrdp_pdu::rdp::server_error_info::ErrorInfo;
pub use ironrdp_pdu::rdp::session_info::ServerAutoReconnect;
#[cfg(feature = "usb")]
pub use ironrdp_rdpeusb::io::{CompletionData, DeviceAnnounce, DeviceText, InternalIoControlPacket};
pub use ironrdp_rdpeusb::io::{CompletionData, DeviceText, InternalIoControlPacket};
pub use rdpdr::{NoopRdpdrServerBackend, RdpdrServerBackend, RdpdrServerFactory, RdpdrServerMessage};
pub use rdpei::{
CsReadyFlags, CsReadyPdu, DismissHoveringTouchContactPdu, PenContact, PenContactDataFlags, PenContactFields,
Expand All @@ -58,8 +58,8 @@ pub use server::{
pub use sound::{RdpsndServerHandler, RdpsndServerMessage, SoundServerFactory};
#[cfg(feature = "usb")]
pub use urbdrc::{
CompletionFut, DeviceFactory, PendingHandle, PendingRequest, RdpUsbDeviceAnnounceInfo, UsbDeviceHandle,
UsbRedirDevice, UsbRequestCompletion,
CompletionFut, DeviceFactory, PendingHandle, PendingRequest, RdpUsbDeviceAnnounceInfo, UsbDeviceAnnounce,
UsbDeviceHandle, UsbRedirDevice, UsbRequestCompletion,
};
#[cfg(feature = "__bench")]
pub mod bench {
Expand Down
47 changes: 44 additions & 3 deletions crates/ironrdp-server/src/urbdrc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ use ironrdp_rdpeusb::{
server::{UrbdrcControlServerBackend, UrbdrcDeviceServerBackend},
};
use ironrdp_usb::{
InterfaceSelection, TransferType,
InterfaceSelection, TransferType, UsbSpeed,
control::GetDescriptorRequest,
descriptor::{ConfigurationDescriptorSet, InterfaceDescriptor, ValidConfigurationDescriptorSet},
endpoint::EndpointAddress,
Expand Down Expand Up @@ -1425,6 +1425,8 @@ pub(crate) struct UsbRedirServer {

pub trait UsbRedirDevice: Send {
/// Called when the client announces the device with `ADD_DEVICE`.
///
/// This is the first callback, and it is invoked exactly once.
Comment on lines +1428 to +1429

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[skeptical] 'First callback, invoked exactly once' contract on device_added is over-strong — low 🟡 — Inspection of ironrdp-rdpeusb's UrbdrcDeviceServer resolves the prior question: AddDev is forwarded to the backend only when state == AwaitingDevice and then transitions to Ready, so duplicate announcements are ignored and device_added runs at most once; no other backend callback precedes it for a well-behaved client. But the contract as written still overpromises: a device channel closed before ADD_DEVICE invokes only close(), so device_added may never run, and DevTextRsp is processed in any non-retracted state, so a nonconforming client can deliver device_text before device_added. Soften to 'at most once, and the first callback in normal operation' or note the close-before-announce path, especially since the new device_instance_id doc encourages keying per-device state on the announcement.

fn device_added(&mut self, info: RdpUsbDeviceAnnounceInfo);

fn device_text(&mut self, device_text: DeviceText);
Expand All @@ -1440,9 +1442,48 @@ pub trait UsbRedirDevice: Send {
fn close(&mut self) {}
}

/// A redirected USB device as the client announced it.
#[derive(Debug, Clone)]
#[non_exhaustive]
pub struct UsbDeviceAnnounce {
/// Identity the client assigned to the redirected device.
///
/// It is opaque to the server and stable for the lifetime of the device
/// channel, which makes it usable as a key for per-device state.
pub device_instance_id: String,
/// Device identifiers reported by the client, most specific first.
pub hw_ids: Vec<String>,
/// Identifiers of the device classes the client reports the device is
/// compatible with, most specific first.
pub compat_ids: Vec<String>,
/// Identifier shared by every function of the same physical device, which
/// groups the announcements of a composite device.
pub container_id: String,
/// Bus speed the client reports the device is operating at.
///
/// RDPEUSB reports only whether the device is operating at high speed, so
/// this is always either [`UsbSpeed::High`] or [`UsbSpeed::Full`]. A
/// low-speed device is announced as full speed, because the protocol has no
/// encoding to distinguish the two.
pub speed: UsbSpeed,
}

impl From<DeviceAnnounce> for UsbDeviceAnnounce {
fn from(value: DeviceAnnounce) -> Self {
Self {
device_instance_id: value.device_instance_id,
hw_ids: value.hw_ids,
compat_ids: value.compat_ids,
container_id: value.container_id,
speed: value.usb_device_caps.device_speed.into(),
}
}
}

/// A device announcement together with the handle which drives that device.
#[derive(Debug)]
pub struct RdpUsbDeviceAnnounceInfo {
pub announce: DeviceAnnounce,
pub announce: UsbDeviceAnnounce,
pub usb_handle: UsbDeviceHandle,
}

Expand Down Expand Up @@ -1508,7 +1549,7 @@ impl UsbRedirServer {
impl UrbdrcDeviceServerBackend for UsbRedirServer {
fn add_device(&mut self, device: DeviceAnnounce) -> PduResult<()> {
self.device.device_added(RdpUsbDeviceAnnounceInfo {
announce: device,
announce: device.into(),
usb_handle: self.handle.clone(),
});
Ok(())
Expand Down
Loading