Skip to content

Commit

Permalink
Merge pull request #57 from kevinmehall/windows-interfaces
Browse files Browse the repository at this point in the history
windows: Allow claiming interfaces that require WinUsb_GetAssociatedInterface
  • Loading branch information
kevinmehall authored Jul 28, 2024
2 parents e47fbf3 + 58b5b58 commit 1f7a587
Show file tree
Hide file tree
Showing 7 changed files with 384 additions and 138 deletions.
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,6 @@ windows-sys = { version = "0.48.0", features = ["Win32_Devices_Usb", "Win32_Devi
core-foundation = "0.9.3"
core-foundation-sys = "0.8.4"
io-kit-sys = "0.4.0"

[lints.rust]
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(fuzzing)'] }
27 changes: 27 additions & 0 deletions src/descriptors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ pub(crate) const DESCRIPTOR_LEN_INTERFACE: u8 = 9;
pub(crate) const DESCRIPTOR_TYPE_ENDPOINT: u8 = 0x05;
pub(crate) const DESCRIPTOR_LEN_ENDPOINT: u8 = 7;

pub(crate) const DESCRIPTOR_TYPE_STRING: u8 = 0x03;

/// USB defined language IDs for string descriptors.
///
/// In practice, different language IDs are not used,
Expand Down Expand Up @@ -352,6 +354,13 @@ impl<'a> InterfaceGroup<'a> {
pub fn alt_settings(&self) -> impl Iterator<Item = InterfaceAltSetting> {
self.interfaces.iter().cloned()
}

/// Get the descriptor for the first alt setting.
///
/// There is guaranteed to be at least one alt setting or this would not have been found.
pub fn first_alt_setting(&self) -> InterfaceAltSetting<'a> {
self.interfaces[0].clone()
}
}

/// Information about a USB interface alternate setting, with access to associated endpoints and other descriptors.
Expand Down Expand Up @@ -549,6 +558,24 @@ pub(crate) fn parse_concatenated_config_descriptors(mut buf: &[u8]) -> impl Iter
})
}

pub(crate) fn validate_string_descriptor(data: &[u8]) -> bool {
data.len() >= 2 && data[0] as usize == data.len() && data[1] == DESCRIPTOR_TYPE_STRING
}

pub(crate) fn decode_string_descriptor(data: &[u8]) -> Result<String, ()> {
if !validate_string_descriptor(data) {
return Err(());
}

Ok(char::decode_utf16(
data[2..]
.chunks_exact(2)
.map(|c| u16::from_le_bytes(c.try_into().unwrap())),
)
.map(|r| r.unwrap_or(char::REPLACEMENT_CHARACTER))
.collect::<String>())
}

/// Make public when fuzzing
#[cfg(fuzzing)]
pub fn fuzz_parse_concatenated_config_descriptors(buf: &[u8]) -> impl Iterator<Item = &[u8]> {
Expand Down
41 changes: 16 additions & 25 deletions src/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ use std::{io::ErrorKind, sync::Arc, time::Duration};
use log::error;

use crate::{
descriptors::{ActiveConfigurationError, Configuration, InterfaceAltSetting},
descriptors::{
decode_string_descriptor, validate_string_descriptor, ActiveConfigurationError,
Configuration, InterfaceAltSetting, DESCRIPTOR_TYPE_STRING,
},
platform,
transfer::{
Control, ControlIn, ControlOut, EndpointType, Queue, RequestBuffer, TransferError,
Expand All @@ -12,9 +15,6 @@ use crate::{
DeviceInfo, Error,
};

const STANDARD_REQUEST_GET_DESCRIPTOR: u8 = 0x06;
const DESCRIPTOR_TYPE_STRING: u8 = 0x03;

/// An opened USB device.
///
/// Obtain a `Device` by calling [`DeviceInfo::open`]:
Expand Down Expand Up @@ -123,6 +123,7 @@ impl Device {

#[cfg(not(target_os = "windows"))]
{
const STANDARD_REQUEST_GET_DESCRIPTOR: u8 = 0x06;
use crate::transfer::{ControlType, Recipient};

let mut buf = vec![0; 4096];
Expand Down Expand Up @@ -153,7 +154,14 @@ impl Device {
timeout: Duration,
) -> Result<impl Iterator<Item = u16>, Error> {
let data = self.get_descriptor(DESCRIPTOR_TYPE_STRING, 0, 0, timeout)?;
validate_string_descriptor(&data)?;

if !validate_string_descriptor(&data) {
error!("String descriptor language list read {data:?}, not a valid string descriptor");
return Err(Error::new(
ErrorKind::InvalidData,
"string descriptor data was invalid",
));
}

//TODO: Use array_chunks once stable
let mut iter = data.into_iter().skip(2);
Expand Down Expand Up @@ -184,15 +192,9 @@ impl Device {
));
}
let data = self.get_descriptor(DESCRIPTOR_TYPE_STRING, desc_index, language_id, timeout)?;
validate_string_descriptor(&data)?;

Ok(char::decode_utf16(
data[2..]
.chunks_exact(2)
.map(|c| u16::from_le_bytes(c.try_into().unwrap())),
)
.map(|r| r.unwrap_or(char::REPLACEMENT_CHARACTER))
.collect::<String>())

decode_string_descriptor(&data)
.map_err(|_| Error::new(ErrorKind::InvalidData, "string descriptor data was invalid"))
}

/// Reset the device, forcing it to re-enumerate.
Expand Down Expand Up @@ -309,17 +311,6 @@ impl Device {
}
}

fn validate_string_descriptor(data: &[u8]) -> Result<(), Error> {
if data.len() < 2 || data[0] as usize != data.len() || data[1] != DESCRIPTOR_TYPE_STRING {
error!("String descriptor language list read {data:?}, not a valid string descriptor");
return Err(Error::new(
ErrorKind::InvalidData,
"string descriptor data was invalid",
));
}
Ok(())
}

/// An opened interface of a USB device.
///
/// Obtain an `Interface` with the [`Device::claim_interface`] method.
Expand Down
Loading

0 comments on commit 1f7a587

Please sign in to comment.