use core::fmt;
use core::fmt::{Debug, Display};
use core::mem::size_of;
use bitflags::bitflags;
use ironrdp_core::{
cast_length, ensure_fixed_part_size, ensure_size, invalid_field_err, invalid_field_err_with_source,
unsupported_value_err, DecodeError, DecodeResult, EncodeResult, ReadCursor, WriteCursor,
};
use ironrdp_pdu::utils::{decode_string, encoded_str_len, from_utf16_bytes, write_string_to_cursor, CharacterSet};
use ironrdp_pdu::{read_padding, write_padding, PduError};
use super::esc::rpce;
use super::{PacketId, SharedHeader};
#[derive(Debug, PartialEq, Clone, Copy)]
pub enum VersionAndIdPduKind {
ServerAnnounceRequest,
ClientAnnounceReply,
ServerClientIdConfirm,
}
impl VersionAndIdPduKind {
fn name(&self) -> &'static str {
match self {
VersionAndIdPduKind::ServerAnnounceRequest => "DR_CORE_SERVER_ANNOUNCE_REQ",
VersionAndIdPduKind::ClientAnnounceReply => "DR_CORE_CLIENT_ANNOUNCE_RSP",
VersionAndIdPduKind::ServerClientIdConfirm => "DR_CORE_SERVER_CLIENTID_CONFIRM",
}
}
}
#[derive(Debug, PartialEq, Clone)]
pub struct VersionAndIdPdu {
pub version_major: u16,
pub version_minor: u16,
pub client_id: u32,
pub kind: VersionAndIdPduKind,
}
impl VersionAndIdPdu {
const FIXED_PART_SIZE: usize = (size_of::<u16>() * 2) + size_of::<u32>();
pub fn new_client_announce_reply(req: VersionAndIdPdu) -> DecodeResult<Self> {
if req.kind != VersionAndIdPduKind::ServerAnnounceRequest {
return Err(invalid_field_err!(
"VersionAndIdPdu::new_client_announce_reply",
"VersionAndIdPduKind",
"invalid value"
));
}
Ok(Self {
version_major: VERSION_MAJOR,
version_minor: VERSION_MINOR_12,
client_id: req.client_id,
kind: VersionAndIdPduKind::ClientAnnounceReply,
})
}
pub fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
ensure_size!(ctx: self.name(), in: dst, size: Self::FIXED_PART_SIZE);
dst.write_u16(self.version_major);
dst.write_u16(self.version_minor);
dst.write_u32(self.client_id);
Ok(())
}
pub fn decode(header: SharedHeader, src: &mut ReadCursor<'_>) -> DecodeResult<Self> {
let kind = match header.packet_id {
PacketId::CoreServerAnnounce => VersionAndIdPduKind::ServerAnnounceRequest,
PacketId::CoreClientidConfirm => VersionAndIdPduKind::ServerClientIdConfirm,
_ => {
return Err(invalid_field_err!(
"VersionAndIdPdu::decode",
"PacketId",
"invalid value"
))
}
};
ensure_size!(ctx: kind.name(), in: src, size: Self::FIXED_PART_SIZE);
let version_major = src.read_u16();
let version_minor = src.read_u16();
let client_id = src.read_u32();
Ok(Self {
version_major,
version_minor,
client_id,
kind,
})
}
pub fn name(&self) -> &'static str {
self.kind.name()
}
pub fn size(&self) -> usize {
Self::FIXED_PART_SIZE
}
}
#[derive(Debug, PartialEq, Clone)]
pub enum ClientNameRequest {
Ascii(String),
Unicode(String),
}
impl ClientNameRequest {
const NAME: &'static str = "DR_CORE_CLIENT_NAME_REQ";
const FIXED_PART_SIZE: usize = size_of::<u32>() * 3; pub fn new(computer_name: String, kind: ClientNameRequestUnicodeFlag) -> Self {
match kind {
ClientNameRequestUnicodeFlag::Ascii => ClientNameRequest::Ascii(computer_name),
ClientNameRequestUnicodeFlag::Unicode => ClientNameRequest::Unicode(computer_name),
}
}
fn unicode_flag(&self) -> ClientNameRequestUnicodeFlag {
match self {
ClientNameRequest::Ascii(_) => ClientNameRequestUnicodeFlag::Ascii,
ClientNameRequest::Unicode(_) => ClientNameRequestUnicodeFlag::Unicode,
}
}
fn computer_name(&self) -> &str {
match self {
ClientNameRequest::Ascii(name) => name,
ClientNameRequest::Unicode(name) => name,
}
}
pub fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
ensure_size!(in: dst, size: self.size());
dst.write_u32(self.unicode_flag().into());
dst.write_u32(0); dst.write_u32(encoded_str_len(self.computer_name(), self.unicode_flag().into(), true) as u32);
write_string_to_cursor(dst, self.computer_name(), self.unicode_flag().into(), true)
}
pub fn name(&self) -> &'static str {
Self::NAME
}
pub fn size(&self) -> usize {
Self::FIXED_PART_SIZE + encoded_str_len(self.computer_name(), self.unicode_flag().into(), true)
}
}
#[repr(u32)]
#[derive(Debug, PartialEq, Clone, Copy)]
pub enum ClientNameRequestUnicodeFlag {
Ascii = 0x0,
Unicode = 0x1,
}
impl From<ClientNameRequestUnicodeFlag> for CharacterSet {
fn from(val: ClientNameRequestUnicodeFlag) -> Self {
match val {
ClientNameRequestUnicodeFlag::Ascii => CharacterSet::Ansi,
ClientNameRequestUnicodeFlag::Unicode => CharacterSet::Unicode,
}
}
}
impl From<ClientNameRequestUnicodeFlag> for u32 {
fn from(val: ClientNameRequestUnicodeFlag) -> Self {
val as u32
}
}
#[derive(Debug, PartialEq, Clone)]
pub struct CoreCapability {
pub capabilities: Vec<CapabilityMessage>,
pub kind: CoreCapabilityKind,
}
impl CoreCapability {
const FIXED_PART_SIZE: usize = size_of::<u16>() * 2;
pub fn new_response(capabilities: Vec<CapabilityMessage>) -> Self {
Self {
capabilities,
kind: CoreCapabilityKind::ClientCoreCapabilityResponse,
}
}
pub fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
ensure_size!(ctx: self.name(), in: dst, size: self.size());
dst.write_u16(cast_length!(
"CoreCapability",
"numCapabilities",
self.capabilities.len()
)?);
write_padding!(dst, 2); for cap in self.capabilities.iter() {
cap.encode(dst)?;
}
Ok(())
}
pub fn decode(header: SharedHeader, src: &mut ReadCursor<'_>) -> DecodeResult<Self> {
let kind = match header.packet_id {
PacketId::CoreServerCapability => CoreCapabilityKind::ServerCoreCapabilityRequest,
PacketId::CoreClientCapability => CoreCapabilityKind::ClientCoreCapabilityResponse,
_ => {
return Err(invalid_field_err!(
"CoreCapability::decode",
"PacketId",
"invalid value"
))
}
};
ensure_size!(ctx: kind.name(), in: src, size: Self::FIXED_PART_SIZE);
let num_capabilities = src.read_u16();
read_padding!(src, 2); let mut capabilities = Vec::new();
for _ in 0..num_capabilities {
capabilities.push(CapabilityMessage::decode(src)?);
}
Ok(Self { capabilities, kind })
}
pub fn name(&self) -> &'static str {
self.kind.name()
}
pub fn size(&self) -> usize {
Self::FIXED_PART_SIZE + self.capabilities.iter().map(|c| c.size()).sum::<usize>()
}
}
#[derive(Debug, PartialEq, Clone, Copy)]
pub enum CoreCapabilityKind {
ServerCoreCapabilityRequest,
ClientCoreCapabilityResponse,
}
impl CoreCapabilityKind {
fn name(&self) -> &'static str {
match self {
CoreCapabilityKind::ServerCoreCapabilityRequest => "DR_CORE_CAPABILITY_REQ",
CoreCapabilityKind::ClientCoreCapabilityResponse => "DR_CORE_CAPABILITY_RSP",
}
}
}
#[derive(Debug, PartialEq, Clone)]
pub struct Capabilities(Vec<CapabilityMessage>);
impl Capabilities {
pub fn new() -> Self {
let mut this = Self(Vec::new());
this.add_general(0);
this
}
pub fn clone_inner(&mut self) -> Vec<CapabilityMessage> {
self.0.clone()
}
pub fn add_smartcard(&mut self) {
self.push(CapabilityMessage::new_smartcard());
self.increment_special_devices();
}
pub fn add_drive(&mut self) {
self.push(CapabilityMessage::new_drive());
}
fn add_general(&mut self, special_type_device_cap: u32) {
self.push(CapabilityMessage::new_general(special_type_device_cap));
}
fn push(&mut self, capability: CapabilityMessage) {
self.0.push(capability);
}
fn increment_special_devices(&mut self) {
let capabilities = &mut self.0;
for capability in capabilities.iter_mut() {
match &mut capability.capability_data {
CapabilityData::General(general_capability) => {
general_capability.special_type_device_cap += 1;
break;
}
_ => continue,
}
}
}
}
impl Default for Capabilities {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, PartialEq, Clone, Copy)]
pub struct CapabilityMessage {
header: CapabilityHeader,
capability_data: CapabilityData,
}
impl CapabilityMessage {
pub fn new_general(special_type_device_cap: u32) -> Self {
Self {
header: CapabilityHeader::new_general(),
capability_data: CapabilityData::General(GeneralCapabilitySet {
os_type: 0,
os_version: 0,
protocol_major_version: 1,
protocol_minor_version: VERSION_MINOR_12,
io_code_1: IoCode1::REQUIRED,
io_code_2: 0,
extended_pdu: ExtendedPdu::RDPDR_DEVICE_REMOVE_PDUS | ExtendedPdu::RDPDR_CLIENT_DISPLAY_NAME_PDU,
extra_flags_1: ExtraFlags1::empty(),
extra_flags_2: 0,
special_type_device_cap,
}),
}
}
pub fn new_smartcard() -> Self {
Self {
header: CapabilityHeader::new_smartcard(),
capability_data: CapabilityData::Smartcard,
}
}
pub fn new_drive() -> Self {
Self {
header: CapabilityHeader::new_drive(),
capability_data: CapabilityData::Drive,
}
}
fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
ensure_size!(in: dst, size: self.size());
self.header.encode(dst)?;
self.capability_data.encode(dst)
}
fn decode(src: &mut ReadCursor<'_>) -> DecodeResult<Self> {
let header = CapabilityHeader::decode(src)?;
let capability_data = CapabilityData::decode(src, &header)?;
Ok(Self {
header,
capability_data,
})
}
fn size(&self) -> usize {
CapabilityHeader::SIZE + self.capability_data.size()
}
}
#[derive(Debug, PartialEq, Clone, Copy)]
struct CapabilityHeader {
cap_type: CapabilityType,
length: u16,
version: u32,
}
impl CapabilityHeader {
const SIZE: usize = size_of::<u16>() * 2 + size_of::<u32>();
fn new_general() -> Self {
Self {
cap_type: CapabilityType::General,
length: (Self::SIZE + GeneralCapabilitySet::SIZE) as u16,
version: GENERAL_CAPABILITY_VERSION_02,
}
}
fn new_smartcard() -> Self {
Self {
cap_type: CapabilityType::Smartcard,
length: Self::SIZE as u16,
version: SMARTCARD_CAPABILITY_VERSION_01,
}
}
fn new_drive() -> Self {
Self {
cap_type: CapabilityType::Drive,
length: Self::SIZE as u16,
version: DRIVE_CAPABILITY_VERSION_02,
}
}
fn decode(src: &mut ReadCursor<'_>) -> DecodeResult<Self> {
ensure_size!(in: src, size: Self::SIZE);
let cap_type: CapabilityType = src.read_u16().try_into()?;
let length = src.read_u16();
let version = src.read_u32();
Ok(Self {
cap_type,
length,
version,
})
}
fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
ensure_size!(in: dst, size: Self::SIZE);
dst.write_u16(self.cap_type.into());
dst.write_u16(self.length);
dst.write_u32(self.version);
Ok(())
}
}
#[derive(Debug, Clone, PartialEq, Copy)]
#[repr(u16)]
enum CapabilityType {
General = 0x0001,
Printer = 0x0002,
Port = 0x0003,
Drive = 0x0004,
Smartcard = 0x0005,
}
impl From<CapabilityType> for u16 {
fn from(cap_type: CapabilityType) -> Self {
cap_type as u16
}
}
pub const GENERAL_CAPABILITY_VERSION_02: u32 = 0x0000_0002;
pub const SMARTCARD_CAPABILITY_VERSION_01: u32 = 0x0000_0001;
pub const DRIVE_CAPABILITY_VERSION_02: u32 = 0x0000_0002;
impl TryFrom<u16> for CapabilityType {
type Error = DecodeError;
fn try_from(value: u16) -> Result<Self, Self::Error> {
match value {
0x0001 => Ok(CapabilityType::General),
0x0002 => Ok(CapabilityType::Printer),
0x0003 => Ok(CapabilityType::Port),
0x0004 => Ok(CapabilityType::Drive),
0x0005 => Ok(CapabilityType::Smartcard),
_ => Err(invalid_field_err!("try_from", "CapabilityType", "invalid value")),
}
}
}
#[derive(Debug, PartialEq, Clone, Copy)]
enum CapabilityData {
General(GeneralCapabilitySet),
Printer,
Port,
Drive,
Smartcard,
}
impl CapabilityData {
fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
match self {
CapabilityData::General(general) => general.encode(dst),
_ => Ok(()),
}
}
fn decode(src: &mut ReadCursor<'_>, header: &CapabilityHeader) -> DecodeResult<Self> {
match header.cap_type {
CapabilityType::General => Ok(CapabilityData::General(GeneralCapabilitySet::decode(
src,
header.version,
)?)),
CapabilityType::Printer => Ok(CapabilityData::Printer),
CapabilityType::Port => Ok(CapabilityData::Port),
CapabilityType::Drive => Ok(CapabilityData::Drive),
CapabilityType::Smartcard => Ok(CapabilityData::Smartcard),
}
}
fn size(&self) -> usize {
match self {
CapabilityData::General(_) => GeneralCapabilitySet::SIZE,
CapabilityData::Printer => 0,
CapabilityData::Port => 0,
CapabilityData::Drive => 0,
CapabilityData::Smartcard => 0,
}
}
}
#[derive(Debug, PartialEq, Clone, Copy)]
struct GeneralCapabilitySet {
os_type: u32,
os_version: u32,
protocol_major_version: u16,
protocol_minor_version: u16,
io_code_1: IoCode1,
io_code_2: u32,
extended_pdu: ExtendedPdu,
extra_flags_1: ExtraFlags1,
extra_flags_2: u32,
special_type_device_cap: u32,
}
impl GeneralCapabilitySet {
#[allow(clippy::manual_bits)]
const SIZE: usize = size_of::<u32>() * 8 + size_of::<u16>() * 2;
fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
ensure_size!(in: dst, size: Self::SIZE);
dst.write_u32(self.os_type);
dst.write_u32(self.os_version);
dst.write_u16(self.protocol_major_version);
dst.write_u16(self.protocol_minor_version);
dst.write_u32(self.io_code_1.bits());
dst.write_u32(self.io_code_2);
dst.write_u32(self.extended_pdu.bits());
dst.write_u32(self.extra_flags_1.bits());
dst.write_u32(self.extra_flags_2);
dst.write_u32(self.special_type_device_cap);
Ok(())
}
fn decode(src: &mut ReadCursor<'_>, version: u32) -> DecodeResult<Self> {
ensure_size!(in: src, size: Self::SIZE);
let os_type = src.read_u32();
let os_version = src.read_u32();
let protocol_major_version = src.read_u16();
let protocol_minor_version = src.read_u16();
let io_code_1 = IoCode1::from_bits_retain(src.read_u32());
let io_code_2 = src.read_u32();
let extended_pdu = ExtendedPdu::from_bits_retain(src.read_u32());
let extra_flags_1 = ExtraFlags1::from_bits_retain(src.read_u32());
let extra_flags_2 = src.read_u32();
let special_type_device_cap = if version == GENERAL_CAPABILITY_VERSION_02 {
src.read_u32()
} else {
0
};
Ok(Self {
os_type,
os_version,
protocol_major_version,
protocol_minor_version,
io_code_1,
io_code_2,
extended_pdu,
extra_flags_1,
extra_flags_2,
special_type_device_cap,
})
}
}
bitflags! {
#[derive(Debug, PartialEq, Clone, Copy)]
struct IoCode1: u32 {
const RDPDR_IRP_MJ_CREATE = 0x0000_0001;
const RDPDR_IRP_MJ_CLEANUP = 0x0000_0002;
const RDPDR_IRP_MJ_CLOSE = 0x0000_0004;
const RDPDR_IRP_MJ_READ = 0x0000_0008;
const RDPDR_IRP_MJ_WRITE = 0x0000_0010;
const RDPDR_IRP_MJ_FLUSH_BUFFERS = 0x0000_0020;
const RDPDR_IRP_MJ_SHUTDOWN = 0x0000_0040;
const RDPDR_IRP_MJ_DEVICE_CONTROL = 0x0000_0080;
const RDPDR_IRP_MJ_QUERY_VOLUME_INFORMATION = 0x0000_0100;
const RDPDR_IRP_MJ_SET_VOLUME_INFORMATION = 0x0000_0200;
const RDPDR_IRP_MJ_QUERY_INFORMATION = 0x0000_0400;
const RDPDR_IRP_MJ_SET_INFORMATION = 0x0000_0800;
const RDPDR_IRP_MJ_DIRECTORY_CONTROL = 0x0000_1000;
const RDPDR_IRP_MJ_LOCK_CONTROL = 0x0000_2000;
const RDPDR_IRP_MJ_QUERY_SECURITY = 0x0000_4000;
const RDPDR_IRP_MJ_SET_SECURITY = 0x0000_8000;
const REQUIRED = Self::RDPDR_IRP_MJ_CREATE.bits()
| Self::RDPDR_IRP_MJ_CLEANUP.bits()
| Self::RDPDR_IRP_MJ_CLOSE.bits()
| Self::RDPDR_IRP_MJ_READ.bits()
| Self::RDPDR_IRP_MJ_WRITE.bits()
| Self::RDPDR_IRP_MJ_FLUSH_BUFFERS.bits()
| Self::RDPDR_IRP_MJ_SHUTDOWN.bits()
| Self::RDPDR_IRP_MJ_DEVICE_CONTROL.bits()
| Self::RDPDR_IRP_MJ_QUERY_VOLUME_INFORMATION.bits()
| Self::RDPDR_IRP_MJ_SET_VOLUME_INFORMATION.bits()
| Self::RDPDR_IRP_MJ_QUERY_INFORMATION.bits()
| Self::RDPDR_IRP_MJ_SET_INFORMATION.bits()
| Self::RDPDR_IRP_MJ_DIRECTORY_CONTROL.bits()
| Self::RDPDR_IRP_MJ_LOCK_CONTROL.bits();
}
}
bitflags! {
#[derive(Debug, PartialEq, Clone, Copy)]
struct ExtendedPdu: u32 {
const RDPDR_DEVICE_REMOVE_PDUS = 0x0000_0001;
const RDPDR_CLIENT_DISPLAY_NAME_PDU = 0x0000_0002;
const RDPDR_USER_LOGGEDON_PDU = 0x0000_0004;
}
}
bitflags! {
#[derive(Debug, PartialEq, Clone, Copy)]
struct ExtraFlags1: u32 {
const ENABLE_ASYNCIO = 0x0000_0001;
}
}
pub const VERSION_MINOR_12: u16 = 0x000C;
pub const VERSION_MAJOR: u16 = 0x0001;
#[derive(Debug, PartialEq, Clone)]
pub struct ClientDeviceListAnnounce {
pub device_list: Vec<DeviceAnnounceHeader>,
}
impl ClientDeviceListAnnounce {
const FIXED_PART_SIZE: usize = size_of::<u32>(); pub(crate) fn new_drive(device_id: u32, name: String) -> Self {
Self {
device_list: vec![DeviceAnnounceHeader::new_drive(device_id, name)],
}
}
pub fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
dst.write_u32(cast_length!(
"ClientDeviceListAnnounce",
"DeviceCount",
self.device_list.len()
)?);
for dev in self.device_list.iter() {
dev.encode(dst)?;
}
Ok(())
}
pub fn name(&self) -> &'static str {
"DR_CORE_DEVICELIST_ANNOUNCE_REQ"
}
pub fn size(&self) -> usize {
Self::FIXED_PART_SIZE + self.device_list.iter().map(|d| d.size()).sum::<usize>()
}
}
#[derive(Debug, PartialEq, Clone)]
pub struct Devices(Vec<DeviceAnnounceHeader>);
impl Devices {
pub fn new() -> Self {
Self(Vec::new())
}
pub fn add_smartcard(&mut self, device_id: u32) {
self.push(DeviceAnnounceHeader::new_smartcard(device_id));
}
pub fn add_drive(&mut self, device_id: u32, name: String) {
self.push(DeviceAnnounceHeader::new_drive(device_id, name));
}
pub fn for_device_type(&self, device_id: u32) -> DecodeResult<DeviceType> {
if let Some(device_type) = self.0.iter().find(|d| d.device_id == device_id).map(|d| d.device_type) {
Ok(device_type)
} else {
Err(invalid_field_err!(
"Devices::for_device_type",
"device_id",
"no device with that ID"
))
}
}
fn push(&mut self, device: DeviceAnnounceHeader) {
self.0.push(device);
}
pub fn clone_inner(&mut self) -> Vec<DeviceAnnounceHeader> {
self.0.clone()
}
}
impl Default for Devices {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, PartialEq, Clone)]
pub struct DeviceAnnounceHeader {
device_type: DeviceType,
device_id: u32,
preferred_dos_name: PreferredDosName,
device_data: Vec<u8>,
}
impl DeviceAnnounceHeader {
const FIXED_PART_SIZE: usize = size_of::<u32>() * 3 + 8; pub fn new_smartcard(device_id: u32) -> Self {
Self {
device_type: DeviceType::Smartcard,
device_id,
preferred_dos_name: PreferredDosName("SCARD".to_owned()),
device_data: Vec::new(),
}
}
fn new_drive(device_id: u32, name: String) -> Self {
let mut device_data = name.into_bytes();
device_data.push(0u8);
Self {
device_type: DeviceType::Filesystem,
device_id,
preferred_dos_name: PreferredDosName("ignored".to_owned()),
device_data,
}
}
fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
dst.write_u32(self.device_type.into());
dst.write_u32(self.device_id);
self.preferred_dos_name.encode(dst)?;
dst.write_u32(cast_length!(
"DeviceAnnounceHeader",
"DeviceDataLength",
self.device_data.len()
)?);
dst.write_slice(&self.device_data);
Ok(())
}
fn size(&self) -> usize {
Self::FIXED_PART_SIZE + self.device_data.len()
}
}
#[derive(Debug, PartialEq, Clone)]
struct PreferredDosName(String);
impl PreferredDosName {
fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
write_string_to_cursor(dst, &self.format(), CharacterSet::Ansi, false)
}
fn format(&self) -> String {
let mut name: &str = &self.0;
if name.len() > 7 {
name = &name[..7];
}
format!("{name:\x00<8}")
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
#[repr(u32)]
pub enum DeviceType {
Serial = 0x0000_0001,
Parallel = 0x0000_0002,
Print = 0x0000_0004,
Filesystem = 0x0000_0008,
Smartcard = 0x0000_0020,
}
impl From<DeviceType> for u32 {
fn from(device_type: DeviceType) -> Self {
device_type as u32
}
}
impl TryFrom<u32> for DeviceType {
type Error = DecodeError;
fn try_from(value: u32) -> Result<Self, Self::Error> {
match value {
0x0000_0001 => Ok(DeviceType::Serial),
0x0000_0002 => Ok(DeviceType::Parallel),
0x0000_0004 => Ok(DeviceType::Print),
0x0000_0008 => Ok(DeviceType::Filesystem),
0x0000_0020 => Ok(DeviceType::Smartcard),
_ => Err(invalid_field_err!("try_from", "DeviceType", "invalid value")),
}
}
}
#[derive(Debug, PartialEq, Clone)]
pub struct ServerDeviceAnnounceResponse {
pub device_id: u32,
pub result_code: NtStatus,
}
impl ServerDeviceAnnounceResponse {
const NAME: &'static str = "DR_CORE_DEVICE_ANNOUNCE_RSP";
const FIXED_PART_SIZE: usize = size_of::<u32>() * 2; pub fn name(&self) -> &'static str {
Self::NAME
}
pub fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
ensure_size!(in: dst, size: self.size());
dst.write_u32(self.device_id);
dst.write_u32(self.result_code.into());
Ok(())
}
pub fn decode(src: &mut ReadCursor<'_>) -> DecodeResult<Self> {
ensure_size!(ctx: Self::NAME, in: src, size: Self::FIXED_PART_SIZE);
let device_id = src.read_u32();
let result_code = NtStatus::from(src.read_u32());
Ok(Self { device_id, result_code })
}
pub fn size(&self) -> usize {
Self::FIXED_PART_SIZE
}
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct NtStatus(u32);
impl NtStatus {
pub const SUCCESS: Self = Self(0x0000_0000);
pub const UNSUCCESSFUL: Self = Self(0xC000_0001);
pub const NOT_IMPLEMENTED: Self = Self(0xC000_0002);
pub const NO_MORE_FILES: Self = Self(0x8000_0006);
pub const OBJECT_NAME_COLLISION: Self = Self(0xC000_0035);
pub const ACCESS_DENIED: Self = Self(0xC000_0022);
pub const NOT_A_DIRECTORY: Self = Self(0xC000_0103);
pub const NO_SUCH_FILE: Self = Self(0xC000_000F);
pub const NOT_SUPPORTED: Self = Self(0xC000_00BB);
pub const DIRECTORY_NOT_EMPTY: Self = Self(0xC000_0101);
}
impl Debug for NtStatus {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
NtStatus::SUCCESS => write!(f, "STATUS_SUCCESS"),
NtStatus::UNSUCCESSFUL => write!(f, "STATUS_UNSUCCESSFUL"),
NtStatus::NOT_IMPLEMENTED => write!(f, "STATUS_NOT_IMPLEMENTED"),
NtStatus::NO_MORE_FILES => write!(f, "STATUS_NO_MORE_FILES"),
NtStatus::OBJECT_NAME_COLLISION => write!(f, "STATUS_OBJECT_NAME_COLLISION"),
NtStatus::ACCESS_DENIED => write!(f, "STATUS_ACCESS_DENIED"),
NtStatus::NOT_A_DIRECTORY => write!(f, "STATUS_NOT_A_DIRECTORY"),
NtStatus::NO_SUCH_FILE => write!(f, "STATUS_NO_SUCH_FILE"),
NtStatus::NOT_SUPPORTED => write!(f, "STATUS_NOT_SUPPORTED"),
NtStatus::DIRECTORY_NOT_EMPTY => write!(f, "STATUS_DIRECTORY_NOT_EMPTY"),
_ => write!(f, "NtStatus({:#010X})", self.0),
}
}
}
impl From<u32> for NtStatus {
fn from(value: u32) -> Self {
Self(value)
}
}
impl From<NtStatus> for u32 {
fn from(status: NtStatus) -> Self {
status.0
}
}
#[derive(Debug, PartialEq, Clone)]
pub struct DeviceIoRequest {
pub device_id: u32,
pub file_id: u32,
pub completion_id: u32,
pub major_function: MajorFunction,
pub minor_function: MinorFunction,
}
impl DeviceIoRequest {
const NAME: &'static str = "DR_DEVICE_IOREQUEST";
const FIXED_PART_SIZE: usize = size_of::<u32>() * 5; pub fn name(&self) -> &'static str {
Self::NAME
}
pub fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
ensure_size!(in: dst, size: self.size());
dst.write_u32(self.device_id);
dst.write_u32(self.file_id);
dst.write_u32(self.completion_id);
dst.write_u32(self.major_function.into());
dst.write_u32(self.minor_function.into());
Ok(())
}
pub fn decode(src: &mut ReadCursor<'_>) -> DecodeResult<Self> {
ensure_size!(ctx: Self::NAME, in: src, size: Self::FIXED_PART_SIZE);
let device_id = src.read_u32();
let file_id = src.read_u32();
let completion_id = src.read_u32();
let major_function = MajorFunction::try_from(src.read_u32())?;
let minor_function = MinorFunction::from(src.read_u32());
Ok(Self {
device_id,
file_id,
completion_id,
major_function,
minor_function,
})
}
pub fn size(&self) -> usize {
Self::FIXED_PART_SIZE
}
}
#[derive(Debug, PartialEq, Clone, Copy)]
#[repr(u32)]
pub enum MajorFunction {
Create = 0x0000_0000,
Close = 0x0000_0002,
Read = 0x0000_0003,
Write = 0x0000_0004,
DeviceControl = 0x0000_000e,
QueryVolumeInformation = 0x0000_000a,
SetVolumeInformation = 0x0000_000b,
QueryInformation = 0x0000_0005,
SetInformation = 0x0000_0006,
DirectoryControl = 0x0000_000c,
LockControl = 0x0000_0011,
}
impl TryFrom<u32> for MajorFunction {
type Error = DecodeError;
fn try_from(value: u32) -> Result<Self, Self::Error> {
match value {
0x0000_0000 => Ok(MajorFunction::Create),
0x0000_0002 => Ok(MajorFunction::Close),
0x0000_0003 => Ok(MajorFunction::Read),
0x0000_0004 => Ok(MajorFunction::Write),
0x0000_000e => Ok(MajorFunction::DeviceControl),
0x0000_000a => Ok(MajorFunction::QueryVolumeInformation),
0x0000_000b => Ok(MajorFunction::SetVolumeInformation),
0x0000_0005 => Ok(MajorFunction::QueryInformation),
0x0000_0006 => Ok(MajorFunction::SetInformation),
0x0000_000c => Ok(MajorFunction::DirectoryControl),
0x0000_0011 => Ok(MajorFunction::LockControl),
_ => Err(invalid_field_err!("try_from", "MajorFunction", "unsupported value")),
}
}
}
impl From<MajorFunction> for u32 {
fn from(major_function: MajorFunction) -> Self {
major_function as u32
}
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct MinorFunction(u32);
impl MinorFunction {
pub const IRP_MN_QUERY_DIRECTORY: Self = Self(0x00000001);
pub const IRP_MN_NOTIFY_CHANGE_DIRECTORY: Self = Self(0x00000002);
}
impl Debug for MinorFunction {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
MinorFunction::IRP_MN_QUERY_DIRECTORY => write!(f, "IRP_MN_QUERY_DIRECTORY"),
MinorFunction::IRP_MN_NOTIFY_CHANGE_DIRECTORY => write!(f, "IRP_MN_NOTIFY_CHANGE_DIRECTORY"),
_ => write!(f, "MinorFunction({:#010X})", self.0),
}
}
}
impl From<u32> for MinorFunction {
fn from(value: u32) -> Self {
Self(value)
}
}
impl From<MinorFunction> for u32 {
fn from(minor_function: MinorFunction) -> Self {
minor_function.0
}
}
impl From<MinorFunction> for u8 {
fn from(minor_function: MinorFunction) -> Self {
minor_function.0 as u8
}
}
#[derive(Debug, PartialEq, Clone)]
pub struct DeviceControlRequest<T: IoCtlCode> {
pub header: DeviceIoRequest,
pub output_buffer_length: u32,
pub input_buffer_length: u32,
pub io_control_code: T,
}
impl<T: IoCtlCode> DeviceControlRequest<T>
where
T::Error: ironrdp_error::Source,
{
const HEADERLESS_SIZE: usize = 4 + 4 + 4 + 20; pub fn decode(header: DeviceIoRequest, src: &mut ReadCursor<'_>) -> DecodeResult<Self> {
ensure_size!(ctx: "DeviceControlRequest", in: src, size: Self::HEADERLESS_SIZE);
let output_buffer_length = src.read_u32();
let input_buffer_length = src.read_u32();
let io_control_code = T::try_from(src.read_u32()).map_err(|e| {
error!("Failed to parse IoCtlCode");
invalid_field_err_with_source("DeviceControlRequest", "IoCtlCode", "invalid IoCtlCode", e)
})?;
read_padding!(src, 20);
Ok(Self {
header,
output_buffer_length,
input_buffer_length,
io_control_code,
})
}
}
pub trait IoCtlCode: TryFrom<u32> {}
#[derive(Debug, PartialEq, Clone)]
pub struct AnyIoCtlCode(pub u32);
impl TryFrom<u32> for AnyIoCtlCode {
type Error = PduError;
fn try_from(value: u32) -> Result<Self, Self::Error> {
Ok(Self(value))
}
}
impl IoCtlCode for AnyIoCtlCode {}
#[derive(Debug)]
pub struct DeviceControlResponse {
pub device_io_reply: DeviceIoResponse,
pub output_buffer: Option<Box<dyn rpce::Encode>>,
}
impl PartialEq for DeviceControlResponse {
fn eq(&self, other: &Self) -> bool {
if (self.device_io_reply != other.device_io_reply)
|| (self.output_buffer.is_some() != other.output_buffer.is_some())
{
return false;
}
if self.output_buffer.is_none() && other.output_buffer.is_none() {
return true;
}
let self_size = self.output_buffer.as_ref().unwrap().size();
let other_size = other.output_buffer.as_ref().unwrap().size();
if self_size != other_size {
return false;
}
let mut self_buf = vec![0u8; self_size];
let mut other_buf = vec![0u8; other_size];
self.output_buffer
.as_ref()
.unwrap()
.encode(&mut WriteCursor::new(self_buf.as_mut_slice()))
.unwrap();
other
.output_buffer
.as_ref()
.unwrap()
.encode(&mut WriteCursor::new(other_buf.as_mut_slice()))
.unwrap();
self_buf == other_buf
}
}
impl DeviceControlResponse {
const NAME: &'static str = "DR_CONTROL_RSP";
pub fn new<T: IoCtlCode>(
req: DeviceControlRequest<T>,
io_status: NtStatus,
output_buffer: Option<Box<dyn rpce::Encode>>,
) -> Self {
Self {
device_io_reply: DeviceIoResponse {
device_id: req.header.device_id,
completion_id: req.header.completion_id,
io_status,
},
output_buffer,
}
}
pub fn name(&self) -> &'static str {
Self::NAME
}
pub fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
ensure_size!(in: dst, size: self.size());
self.device_io_reply.encode(dst)?;
if let Some(output_buffer) = &self.output_buffer {
dst.write_u32(cast_length!(
"DeviceControlResponse",
"OutputBufferLength",
output_buffer.size()
)?);
output_buffer.encode(dst)?;
} else {
dst.write_u32(0); }
Ok(())
}
pub fn size(&self) -> usize {
self.device_io_reply.size() + 4 + if let Some(output_buffer) = &self.output_buffer {
output_buffer.size() } else {
0 }
}
}
#[derive(Debug, PartialEq, Clone)]
pub struct DeviceIoResponse {
pub device_id: u32,
pub completion_id: u32,
pub io_status: NtStatus,
}
impl DeviceIoResponse {
const FIXED_PART_SIZE: usize = size_of::<u32>() * 3; pub fn new(device_io_request: DeviceIoRequest, io_status: NtStatus) -> Self {
Self {
device_id: device_io_request.device_id,
completion_id: device_io_request.completion_id,
io_status,
}
}
pub fn decode(src: &mut ReadCursor<'_>) -> DecodeResult<Self> {
ensure_size!(ctx: "DeviceIoResponse", in: src, size: Self::FIXED_PART_SIZE);
let device_id = src.read_u32();
let completion_id = src.read_u32();
let io_status = NtStatus::from(src.read_u32());
Ok(Self {
device_id,
completion_id,
io_status,
})
}
pub fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
ensure_size!(in: dst, size: self.size());
dst.write_u32(self.device_id);
dst.write_u32(self.completion_id);
dst.write_u32(self.io_status.into());
Ok(())
}
pub fn size(&self) -> usize {
Self::FIXED_PART_SIZE
}
}
#[derive(Debug, PartialEq, Clone)]
pub enum ServerDriveIoRequest {
ServerCreateDriveRequest(DeviceCreateRequest),
ServerDriveQueryInformationRequest(ServerDriveQueryInformationRequest),
DeviceCloseRequest(DeviceCloseRequest),
ServerDriveQueryDirectoryRequest(ServerDriveQueryDirectoryRequest),
ServerDriveNotifyChangeDirectoryRequest(ServerDriveNotifyChangeDirectoryRequest),
ServerDriveQueryVolumeInformationRequest(ServerDriveQueryVolumeInformationRequest),
DeviceControlRequest(DeviceControlRequest<AnyIoCtlCode>),
DeviceReadRequest(DeviceReadRequest),
DeviceWriteRequest(DeviceWriteRequest),
ServerDriveSetInformationRequest(ServerDriveSetInformationRequest),
ServerDriveLockControlRequest(ServerDriveLockControlRequest),
}
impl ServerDriveIoRequest {
pub fn decode(dev_io_req: DeviceIoRequest, src: &mut ReadCursor<'_>) -> DecodeResult<Self> {
match dev_io_req.major_function {
MajorFunction::Create => Ok(DeviceCreateRequest::decode(dev_io_req, src)?.into()),
MajorFunction::Close => Ok(DeviceCloseRequest::decode(dev_io_req).into()),
MajorFunction::Read => Ok(DeviceReadRequest::decode(dev_io_req, src)?.into()),
MajorFunction::Write => Ok(DeviceWriteRequest::decode(dev_io_req, src)?.into()),
MajorFunction::DeviceControl => Ok(DeviceControlRequest::<AnyIoCtlCode>::decode(dev_io_req, src)?.into()),
MajorFunction::QueryVolumeInformation => {
Ok(ServerDriveQueryVolumeInformationRequest::decode(dev_io_req, src)?.into())
}
MajorFunction::SetVolumeInformation => Err(unsupported_value_err!(
"ServerDriveIoRequest::decode",
"MajorFunction",
"SetVolumeInformation".to_owned()
)), MajorFunction::QueryInformation => Ok(ServerDriveQueryInformationRequest::decode(dev_io_req, src)?.into()),
MajorFunction::SetInformation => Ok(ServerDriveSetInformationRequest::decode(dev_io_req, src)?.into()),
MajorFunction::DirectoryControl => match dev_io_req.minor_function {
MinorFunction::IRP_MN_QUERY_DIRECTORY => {
Ok(ServerDriveQueryDirectoryRequest::decode(dev_io_req, src)?.into())
}
MinorFunction::IRP_MN_NOTIFY_CHANGE_DIRECTORY => {
Ok(ServerDriveNotifyChangeDirectoryRequest::decode(dev_io_req, src)?.into())
}
_ => Err(invalid_field_err!(
"ServerDriveIoRequest::decode",
"MinorFunction",
"invalid value"
)),
},
MajorFunction::LockControl => Ok(ServerDriveLockControlRequest::decode(dev_io_req, src)?.into()),
}
}
}
impl From<DeviceCreateRequest> for ServerDriveIoRequest {
fn from(req: DeviceCreateRequest) -> Self {
Self::ServerCreateDriveRequest(req)
}
}
impl From<ServerDriveQueryInformationRequest> for ServerDriveIoRequest {
fn from(req: ServerDriveQueryInformationRequest) -> Self {
Self::ServerDriveQueryInformationRequest(req)
}
}
impl From<DeviceCloseRequest> for ServerDriveIoRequest {
fn from(req: DeviceCloseRequest) -> Self {
Self::DeviceCloseRequest(req)
}
}
impl From<ServerDriveQueryDirectoryRequest> for ServerDriveIoRequest {
fn from(req: ServerDriveQueryDirectoryRequest) -> Self {
Self::ServerDriveQueryDirectoryRequest(req)
}
}
impl From<ServerDriveNotifyChangeDirectoryRequest> for ServerDriveIoRequest {
fn from(req: ServerDriveNotifyChangeDirectoryRequest) -> Self {
Self::ServerDriveNotifyChangeDirectoryRequest(req)
}
}
impl From<ServerDriveQueryVolumeInformationRequest> for ServerDriveIoRequest {
fn from(req: ServerDriveQueryVolumeInformationRequest) -> Self {
Self::ServerDriveQueryVolumeInformationRequest(req)
}
}
impl From<DeviceControlRequest<AnyIoCtlCode>> for ServerDriveIoRequest {
fn from(req: DeviceControlRequest<AnyIoCtlCode>) -> Self {
Self::DeviceControlRequest(req)
}
}
impl From<DeviceReadRequest> for ServerDriveIoRequest {
fn from(req: DeviceReadRequest) -> Self {
Self::DeviceReadRequest(req)
}
}
impl From<DeviceWriteRequest> for ServerDriveIoRequest {
fn from(req: DeviceWriteRequest) -> Self {
Self::DeviceWriteRequest(req)
}
}
impl From<ServerDriveSetInformationRequest> for ServerDriveIoRequest {
fn from(req: ServerDriveSetInformationRequest) -> Self {
Self::ServerDriveSetInformationRequest(req)
}
}
impl From<ServerDriveLockControlRequest> for ServerDriveIoRequest {
fn from(req: ServerDriveLockControlRequest) -> Self {
Self::ServerDriveLockControlRequest(req)
}
}
#[derive(Debug, PartialEq, Clone)]
pub struct DeviceCreateRequest {
pub device_io_request: DeviceIoRequest,
pub desired_access: DesiredAccess,
pub allocation_size: u64,
pub file_attributes: FileAttributes,
pub shared_access: SharedAccess,
pub create_disposition: CreateDisposition,
pub create_options: CreateOptions,
pub path: String,
}
impl DeviceCreateRequest {
const FIXED_PART_SIZE: usize = 4 + 8 + 4 + 4 + 4 + 4 + 4; fn decode(dev_io_req: DeviceIoRequest, src: &mut ReadCursor<'_>) -> DecodeResult<Self> {
ensure_size!(ctx: "DeviceCreateRequest", in: src, size: Self::FIXED_PART_SIZE);
let desired_access = DesiredAccess::from_bits_retain(src.read_u32());
let allocation_size = src.read_u64();
let file_attributes = FileAttributes::from_bits_retain(src.read_u32());
let shared_access = SharedAccess::from_bits_retain(src.read_u32());
let create_disposition = CreateDisposition::from_bits_retain(src.read_u32());
let create_options = CreateOptions::from_bits_retain(src.read_u32());
let path_length: usize = cast_length!("DeviceCreateRequest", "path_length", src.read_u32())?;
ensure_size!(ctx: "DeviceCreateRequest", in: src, size: path_length);
let path = from_utf16_bytes(src.read_slice(path_length))
.trim_end_matches('\0')
.into();
Ok(Self {
device_io_request: dev_io_req,
desired_access,
allocation_size,
file_attributes,
shared_access,
create_disposition,
create_options,
path,
})
}
}
bitflags! {
#[derive(Debug, PartialEq, Clone)]
pub struct DesiredAccess: u32 {
const FILE_READ_DATA_OR_FILE_LIST_DIRECTORY = 0x00000001;
const FILE_WRITE_DATA_OR_FILE_ADD_FILE = 0x00000002;
const FILE_APPEND_DATA_OR_FILE_ADD_SUBDIRECTORY = 0x00000004;
const FILE_READ_EA = 0x00000008;
const FILE_WRITE_EA = 0x00000010;
const FILE_TRAVERSE = 0x00000020;
const FILE_DELETE_CHILD = 0x00000040;
const FILE_EXECUTE = 0x00000020;
const FILE_READ_ATTRIBUTES = 0x00000080;
const FILE_WRITE_ATTRIBUTES = 0x00000100;
const DELETE = 0x00010000;
const READ_CONTROL = 0x00020000;
const WRITE_DAC = 0x00040000;
const WRITE_OWNER = 0x00080000;
const SYNCHRONIZE = 0x00100000;
const ACCESS_SYSTEM_SECURITY = 0x01000000;
const MAXIMUM_ALLOWED = 0x02000000;
const GENERIC_ALL = 0x10000000;
const GENERIC_EXECUTE = 0x20000000;
const GENERIC_WRITE = 0x40000000;
const GENERIC_READ = 0x80000000;
}
}
bitflags! {
#[derive(Debug, PartialEq, Clone)]
pub struct FileAttributes: u32 {
const FILE_ATTRIBUTE_READONLY = 0x00000001;
const FILE_ATTRIBUTE_HIDDEN = 0x00000002;
const FILE_ATTRIBUTE_SYSTEM = 0x00000004;
const FILE_ATTRIBUTE_DIRECTORY = 0x00000010;
const FILE_ATTRIBUTE_ARCHIVE = 0x00000020;
const FILE_ATTRIBUTE_NORMAL = 0x00000080;
const FILE_ATTRIBUTE_TEMPORARY = 0x00000100;
const FILE_ATTRIBUTE_SPARSE_FILE = 0x00000200;
const FILE_ATTRIBUTE_REPARSE_POINT = 0x00000400;
const FILE_ATTRIBUTE_COMPRESSED = 0x00000800;
const FILE_ATTRIBUTE_OFFLINE = 0x00001000;
const FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = 0x00002000;
const FILE_ATTRIBUTE_ENCRYPTED = 0x00004000;
const FILE_ATTRIBUTE_INTEGRITY_STREAM = 0x00008000;
const FILE_ATTRIBUTE_NO_SCRUB_DATA = 0x00020000;
const FILE_ATTRIBUTE_RECALL_ON_OPEN = 0x00040000;
const FILE_ATTRIBUTE_PINNED = 0x00080000;
const FILE_ATTRIBUTE_UNPINNED = 0x00100000;
const FILE_ATTRIBUTE_RECALL_ON_DATA_ACCESS = 0x00400000;
const _ = !0;
}
}
bitflags! {
#[derive(Debug, PartialEq, Clone)]
pub struct SharedAccess: u32 {
const FILE_SHARE_READ = 0x00000001;
const FILE_SHARE_WRITE = 0x00000002;
const FILE_SHARE_DELETE = 0x00000004;
}
}
bitflags! {
#[derive(PartialEq, Eq, Debug, Clone)]
pub struct CreateDisposition: u32 {
const FILE_SUPERSEDE = 0x00000000;
const FILE_OPEN = 0x00000001;
const FILE_CREATE = 0x00000002;
const FILE_OPEN_IF = 0x00000003;
const FILE_OVERWRITE = 0x00000004;
const FILE_OVERWRITE_IF = 0x00000005;
}
}
bitflags! {
#[derive(Debug, PartialEq, Clone)]
pub struct CreateOptions: u32 {
const FILE_DIRECTORY_FILE = 0x00000001;
const FILE_WRITE_THROUGH = 0x00000002;
const FILE_SEQUENTIAL_ONLY = 0x00000004;
const FILE_NO_INTERMEDIATE_BUFFERING = 0x00000008;
const FILE_SYNCHRONOUS_IO_ALERT = 0x00000010;
const FILE_SYNCHRONOUS_IO_NONALERT = 0x00000020;
const FILE_NON_DIRECTORY_FILE = 0x00000040;
const FILE_COMPLETE_IF_OPLOCKED = 0x00000100;
const FILE_NO_EA_KNOWLEDGE = 0x00000200;
const FILE_RANDOM_ACCESS = 0x00000800;
const FILE_DELETE_ON_CLOSE = 0x00001000;
const FILE_OPEN_BY_FILE_ID = 0x00002000;
const FILE_OPEN_FOR_BACKUP_INTENT = 0x00004000;
const FILE_NO_COMPRESSION = 0x00008000;
const FILE_OPEN_REMOTE_INSTANCE = 0x00000400;
const FILE_OPEN_REQUIRING_OPLOCK = 0x00010000;
const FILE_DISALLOW_EXCLUSIVE = 0x00020000;
const FILE_RESERVE_OPFILTER = 0x00100000;
const FILE_OPEN_REPARSE_POINT = 0x00200000;
const FILE_OPEN_NO_RECALL = 0x00400000;
const FILE_OPEN_FOR_FREE_SPACE_QUERY = 0x00800000;
}
}
#[derive(Debug, PartialEq, Clone)]
pub struct DeviceCreateResponse {
pub device_io_reply: DeviceIoResponse,
pub file_id: u32,
pub information: Information,
}
impl DeviceCreateResponse {
const NAME: &'static str = "DR_CREATE_RSP";
pub fn name(&self) -> &'static str {
Self::NAME
}
pub fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
ensure_size!(in: dst, size: self.size());
self.device_io_reply.encode(dst)?;
dst.write_u32(self.file_id);
dst.write_u8(self.information.bits());
Ok(())
}
pub fn size(&self) -> usize {
self.device_io_reply.size() + 4 + 1 }
}
bitflags! {
#[derive(Debug, PartialEq, Clone)]
pub struct Information: u8 {
const FILE_SUPERSEDED = 0x00000000;
const FILE_OPENED = 0x00000001;
const FILE_OVERWRITTEN = 0x00000003;
}
}
#[derive(Debug, PartialEq, Clone)]
pub struct ServerDriveQueryInformationRequest {
pub device_io_request: DeviceIoRequest,
pub file_info_class_lvl: FileInformationClassLevel,
}
impl ServerDriveQueryInformationRequest {
pub fn decode(dev_io_req: DeviceIoRequest, src: &mut ReadCursor<'_>) -> DecodeResult<Self> {
ensure_size!(ctx: "ServerDriveQueryInformationRequest", in: src, size: 4);
let file_info_class_lvl = FileInformationClassLevel::from(src.read_u32());
Ok(Self {
device_io_request: dev_io_req,
file_info_class_lvl,
})
}
}
#[derive(PartialEq, Eq, Clone)]
pub struct FileInformationClassLevel(u32);
impl FileInformationClassLevel {
pub const FILE_BASIC_INFORMATION: Self = Self(4);
pub const FILE_STANDARD_INFORMATION: Self = Self(5);
pub const FILE_ATTRIBUTE_TAG_INFORMATION: Self = Self(35);
pub const FILE_DIRECTORY_INFORMATION: Self = Self(1);
pub const FILE_FULL_DIRECTORY_INFORMATION: Self = Self(2);
pub const FILE_BOTH_DIRECTORY_INFORMATION: Self = Self(3);
pub const FILE_NAMES_INFORMATION: Self = Self(12);
pub const FILE_END_OF_FILE_INFORMATION: Self = Self(20);
pub const FILE_DISPOSITION_INFORMATION: Self = Self(13);
pub const FILE_RENAME_INFORMATION: Self = Self(10);
pub const FILE_ALLOCATION_INFORMATION: Self = Self(19);
}
impl Display for FileInformationClassLevel {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
FileInformationClassLevel::FILE_BASIC_INFORMATION => write!(f, "FileBasicInformation"),
FileInformationClassLevel::FILE_STANDARD_INFORMATION => write!(f, "FileStandardInformation"),
FileInformationClassLevel::FILE_ATTRIBUTE_TAG_INFORMATION => write!(f, "FileAttributeTagInformation"),
FileInformationClassLevel::FILE_DIRECTORY_INFORMATION => write!(f, "FileDirectoryInformation"),
FileInformationClassLevel::FILE_FULL_DIRECTORY_INFORMATION => write!(f, "FileFullDirectoryInformation"),
FileInformationClassLevel::FILE_BOTH_DIRECTORY_INFORMATION => write!(f, "FileBothDirectoryInformation"),
FileInformationClassLevel::FILE_NAMES_INFORMATION => write!(f, "FileNamesInformation"),
FileInformationClassLevel::FILE_END_OF_FILE_INFORMATION => write!(f, "FileEndOfFileInformation"),
FileInformationClassLevel::FILE_DISPOSITION_INFORMATION => write!(f, "FileDispositionInformation"),
FileInformationClassLevel::FILE_RENAME_INFORMATION => write!(f, "FileRenameInformation"),
FileInformationClassLevel::FILE_ALLOCATION_INFORMATION => write!(f, "FileAllocationInformation"),
_ => write!(f, "FileInformationClassLevel({})", self.0),
}
}
}
impl Debug for FileInformationClassLevel {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
FileInformationClassLevel::FILE_BASIC_INFORMATION => write!(f, "FileBasicInformation"),
FileInformationClassLevel::FILE_STANDARD_INFORMATION => write!(f, "FileStandardInformation"),
FileInformationClassLevel::FILE_ATTRIBUTE_TAG_INFORMATION => write!(f, "FileAttributeTagInformation"),
FileInformationClassLevel::FILE_DIRECTORY_INFORMATION => write!(f, "FileDirectoryInformation"),
FileInformationClassLevel::FILE_FULL_DIRECTORY_INFORMATION => write!(f, "FileFullDirectoryInformation"),
FileInformationClassLevel::FILE_BOTH_DIRECTORY_INFORMATION => write!(f, "FileBothDirectoryInformation"),
FileInformationClassLevel::FILE_NAMES_INFORMATION => write!(f, "FileNamesInformation"),
_ => write!(f, "FileInformationClassLevel({})", self.0),
}
}
}
impl From<u32> for FileInformationClassLevel {
fn from(value: u32) -> Self {
Self(value)
}
}
impl From<FileInformationClassLevel> for u32 {
fn from(file_info_class_lvl: FileInformationClassLevel) -> Self {
file_info_class_lvl.0
}
}
#[derive(Debug, PartialEq, Clone)]
pub struct ClientDriveQueryInformationResponse {
pub device_io_response: DeviceIoResponse,
pub buffer: Option<FileInformationClass>,
}
impl ClientDriveQueryInformationResponse {
const NAME: &'static str = "DR_DRIVE_QUERY_INFORMATION_RSP";
pub fn name(&self) -> &'static str {
Self::NAME
}
pub fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
ensure_size!(in: dst, size: self.size());
self.device_io_response.encode(dst)?;
if let Some(buffer) = &self.buffer {
dst.write_u32(cast_length!(
"ClientDriveQueryInformationResponse",
"buffer.size()",
buffer.size()
)?);
buffer.encode(dst)?;
} else {
dst.write_u32(0); }
Ok(())
}
pub fn size(&self) -> usize {
self.device_io_response.size() + 4 + if let Some(buffer) = &self.buffer {
buffer.size() } else {
0
}
}
}
#[derive(Debug, PartialEq, Clone)]
pub enum FileInformationClass {
Basic(FileBasicInformation),
Standard(FileStandardInformation),
AttributeTag(FileAttributeTagInformation),
BothDirectory(FileBothDirectoryInformation),
FullDirectory(FileFullDirectoryInformation),
Names(FileNamesInformation),
Directory(FileDirectoryInformation),
EndOfFile(FileEndOfFileInformation),
Disposition(FileDispositionInformation),
Rename(FileRenameInformation),
Allocation(FileAllocationInformation),
}
impl FileInformationClass {
pub fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
ensure_size!(in: dst, size: self.size());
match self {
Self::Basic(f) => f.encode(dst),
Self::Standard(f) => f.encode(dst),
Self::AttributeTag(f) => f.encode(dst),
Self::BothDirectory(f) => f.encode(dst),
Self::FullDirectory(f) => f.encode(dst),
Self::Names(f) => f.encode(dst),
Self::Directory(f) => f.encode(dst),
_ => Err(unsupported_value_err!(
"FileInformationClass::encode",
"FileInformationClass",
self.to_string()
)),
}
}
pub fn decode(
file_info_class_level: FileInformationClassLevel,
length: usize,
src: &mut ReadCursor<'_>,
) -> DecodeResult<Self> {
match file_info_class_level {
FileInformationClassLevel::FILE_BASIC_INFORMATION => Ok(FileBasicInformation::decode(src)?.into()),
FileInformationClassLevel::FILE_END_OF_FILE_INFORMATION => {
Ok(FileEndOfFileInformation::decode(src)?.into())
}
FileInformationClassLevel::FILE_DISPOSITION_INFORMATION => {
Ok(FileDispositionInformation::decode(src, length)?.into())
}
FileInformationClassLevel::FILE_RENAME_INFORMATION => Ok(FileRenameInformation::decode(src)?.into()),
FileInformationClassLevel::FILE_ALLOCATION_INFORMATION => {
Ok(FileAllocationInformation::decode(src)?.into())
}
_ => Err(unsupported_value_err!(
"FileInformationClass::decode",
"FileInformationClassLevel",
file_info_class_level.to_string()
)),
}
}
pub fn size(&self) -> usize {
match self {
Self::Basic(_) => FileBasicInformation::size(),
Self::Standard(_) => FileStandardInformation::size(),
Self::AttributeTag(_) => FileAttributeTagInformation::size(),
Self::BothDirectory(f) => f.size(),
Self::FullDirectory(f) => f.size(),
Self::Names(f) => f.size(),
Self::Directory(f) => f.size(),
Self::EndOfFile(_) => FileEndOfFileInformation::size(),
Self::Disposition(_) => FileDispositionInformation::size(),
Self::Rename(f) => f.size(),
Self::Allocation(_) => FileAllocationInformation::size(),
}
}
}
impl Display for FileInformationClass {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Basic(_) => write!(f, "FileBasicInformation"),
Self::Standard(_) => write!(f, "FileStandardInformation"),
Self::AttributeTag(_) => write!(f, "FileAttributeTagInformation"),
Self::BothDirectory(_) => write!(f, "FileBothDirectoryInformation"),
Self::FullDirectory(_) => write!(f, "FileFullDirectoryInformation"),
Self::Names(_) => write!(f, "FileNamesInformation"),
Self::Directory(_) => write!(f, "FileDirectoryInformation"),
Self::EndOfFile(_) => write!(f, "FileEndOfFileInformation"),
Self::Disposition(_) => write!(f, "FileDispositionInformation"),
Self::Rename(_) => write!(f, "FileRenameInformation"),
Self::Allocation(_) => write!(f, "FileAllocationInformation"),
}
}
}
impl From<FileBasicInformation> for FileInformationClass {
fn from(f: FileBasicInformation) -> Self {
Self::Basic(f)
}
}
impl From<FileStandardInformation> for FileInformationClass {
fn from(f: FileStandardInformation) -> Self {
Self::Standard(f)
}
}
impl From<FileAttributeTagInformation> for FileInformationClass {
fn from(f: FileAttributeTagInformation) -> Self {
Self::AttributeTag(f)
}
}
impl From<FileBothDirectoryInformation> for FileInformationClass {
fn from(f: FileBothDirectoryInformation) -> Self {
Self::BothDirectory(f)
}
}
impl From<FileFullDirectoryInformation> for FileInformationClass {
fn from(f: FileFullDirectoryInformation) -> Self {
Self::FullDirectory(f)
}
}
impl From<FileNamesInformation> for FileInformationClass {
fn from(f: FileNamesInformation) -> Self {
Self::Names(f)
}
}
impl From<FileDirectoryInformation> for FileInformationClass {
fn from(f: FileDirectoryInformation) -> Self {
Self::Directory(f)
}
}
impl From<FileEndOfFileInformation> for FileInformationClass {
fn from(f: FileEndOfFileInformation) -> Self {
Self::EndOfFile(f)
}
}
impl From<FileDispositionInformation> for FileInformationClass {
fn from(f: FileDispositionInformation) -> Self {
Self::Disposition(f)
}
}
impl From<FileRenameInformation> for FileInformationClass {
fn from(f: FileRenameInformation) -> Self {
Self::Rename(f)
}
}
impl From<FileAllocationInformation> for FileInformationClass {
fn from(f: FileAllocationInformation) -> Self {
Self::Allocation(f)
}
}
#[derive(Debug, PartialEq, Clone)]
pub struct FileBasicInformation {
pub creation_time: i64,
pub last_access_time: i64,
pub last_write_time: i64,
pub change_time: i64,
pub file_attributes: FileAttributes,
}
impl FileBasicInformation {
fn decode(src: &mut ReadCursor<'_>) -> DecodeResult<Self> {
ensure_size!(ctx: "FileBasicInformation", in: src, size: Self::size());
let creation_time = src.read_i64();
let last_access_time = src.read_i64();
let last_write_time = src.read_i64();
let change_time = src.read_i64();
let file_attributes = FileAttributes::from_bits_retain(src.read_u32());
Ok(Self {
creation_time,
last_access_time,
last_write_time,
change_time,
file_attributes,
})
}
pub fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
ensure_size!(in: dst, size: Self::size());
dst.write_i64(self.creation_time);
dst.write_i64(self.last_access_time);
dst.write_i64(self.last_write_time);
dst.write_i64(self.change_time);
dst.write_u32(self.file_attributes.bits());
Ok(())
}
pub fn size() -> usize {
8 + 8 + 8 + 8 + 4 }
}
#[derive(Debug, PartialEq, Clone)]
pub struct FileStandardInformation {
pub allocation_size: i64,
pub end_of_file: i64,
pub number_of_links: u32,
pub delete_pending: Boolean,
pub directory: Boolean,
}
impl FileStandardInformation {
fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
ensure_size!(in: dst, size: Self::size());
dst.write_i64(self.allocation_size);
dst.write_i64(self.end_of_file);
dst.write_u32(self.number_of_links);
dst.write_u8(self.delete_pending.into());
dst.write_u8(self.directory.into());
Ok(())
}
pub fn size() -> usize {
8 + 8 + 4 + 1 + 1 }
}
#[derive(Debug, PartialEq, Clone, Copy)]
#[repr(u8)]
pub enum Boolean {
True = 1,
False = 0,
}
impl From<Boolean> for u8 {
fn from(boolean: Boolean) -> Self {
match boolean {
Boolean::True => 1,
Boolean::False => 0,
}
}
}
impl From<u8> for Boolean {
fn from(value: u8) -> Self {
match value {
1 => Boolean::True,
_ => Boolean::False,
}
}
}
#[derive(Debug, PartialEq, Clone)]
pub struct FileAttributeTagInformation {
pub file_attributes: FileAttributes,
pub reparse_tag: u32,
}
impl FileAttributeTagInformation {
fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
ensure_size!(in: dst, size: Self::size());
dst.write_u32(self.file_attributes.bits());
dst.write_u32(self.reparse_tag);
Ok(())
}
fn size() -> usize {
4 + 4 }
}
#[derive(Debug, PartialEq, Clone)]
pub struct FileBothDirectoryInformation {
pub next_entry_offset: u32,
pub file_index: u32,
pub creation_time: i64,
pub last_access_time: i64,
pub last_write_time: i64,
pub change_time: i64,
pub end_of_file: i64,
pub allocation_size: i64,
pub file_attributes: FileAttributes,
pub ea_size: u32,
pub short_name_length: i8,
pub short_name: [u8; 24], pub file_name: String,
}
impl FileBothDirectoryInformation {
pub fn new(
creation_time: i64,
last_access_time: i64,
last_write_time: i64,
change_time: i64,
file_size: i64,
file_attributes: FileAttributes,
file_name: String,
) -> Self {
Self {
next_entry_offset: 0,
file_index: 0,
creation_time,
last_access_time,
last_write_time,
change_time,
end_of_file: file_size,
allocation_size: file_size,
file_attributes,
ea_size: 0,
short_name_length: 0,
short_name: [0; 24],
file_name,
}
}
fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
ensure_size!(in: dst, size: self.size());
dst.write_u32(self.next_entry_offset);
dst.write_u32(self.file_index);
dst.write_i64(self.creation_time);
dst.write_i64(self.last_access_time);
dst.write_i64(self.last_write_time);
dst.write_i64(self.change_time);
dst.write_i64(self.end_of_file);
dst.write_i64(self.allocation_size);
dst.write_u32(self.file_attributes.bits());
dst.write_u32(cast_length!(
"FileBothDirectoryInformation::encode",
"file_name_length",
encoded_str_len(&self.file_name, CharacterSet::Unicode, false)
)?);
dst.write_u32(self.ea_size);
dst.write_i8(self.short_name_length);
dst.write_slice(&self.short_name);
write_string_to_cursor(dst, &self.file_name, CharacterSet::Unicode, false)?;
Ok(())
}
fn size(&self) -> usize {
4 + 4 + 8 + 8 + 8 + 8 + 8 + 8 + 4 + 4 + 4 + 1 + 24 + encoded_str_len(&self.file_name, CharacterSet::Unicode, false)
}
}
#[derive(Debug, PartialEq, Clone)]
pub struct FileFullDirectoryInformation {
pub next_entry_offset: u32,
pub file_index: u32,
pub creation_time: i64,
pub last_access_time: i64,
pub last_write_time: i64,
pub change_time: i64,
pub end_of_file: i64,
pub allocation_size: i64,
pub file_attributes: FileAttributes,
pub ea_size: u32,
pub file_name: String,
}
impl FileFullDirectoryInformation {
pub fn new(
creation_time: i64,
last_access_time: i64,
last_write_time: i64,
change_time: i64,
file_size: i64,
file_attributes: FileAttributes,
file_name: String,
) -> Self {
Self {
next_entry_offset: 0,
file_index: 0,
creation_time,
last_access_time,
last_write_time,
change_time,
end_of_file: file_size,
allocation_size: file_size,
file_attributes,
ea_size: 0,
file_name,
}
}
fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
ensure_size!(in: dst, size: self.size());
dst.write_u32(self.next_entry_offset);
dst.write_u32(self.file_index);
dst.write_i64(self.creation_time);
dst.write_i64(self.last_access_time);
dst.write_i64(self.last_write_time);
dst.write_i64(self.change_time);
dst.write_i64(self.end_of_file);
dst.write_i64(self.allocation_size);
dst.write_u32(self.file_attributes.bits());
dst.write_u32(cast_length!(
"FileFullDirectoryInformation::encode",
"file_name_length",
encoded_str_len(&self.file_name, CharacterSet::Unicode, false)
)?);
dst.write_u32(self.ea_size);
write_string_to_cursor(dst, &self.file_name, CharacterSet::Unicode, false)?;
Ok(())
}
fn size(&self) -> usize {
4 + 4 + 8 + 8 + 8 + 8 + 8 + 8 + 4 + 4 + 4 + encoded_str_len(&self.file_name, CharacterSet::Unicode, false)
}
}
#[derive(Debug, PartialEq, Clone)]
pub struct FileNamesInformation {
pub next_entry_offset: u32,
pub file_index: u32,
pub file_name: String,
}
impl FileNamesInformation {
pub fn new(file_name: String) -> Self {
Self {
next_entry_offset: 0,
file_index: 0,
file_name,
}
}
fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
ensure_size!(in: dst, size: self.size());
dst.write_u32(self.next_entry_offset);
dst.write_u32(self.file_index);
dst.write_u32(cast_length!(
"FileNamesInformation::encode",
"file_name_length",
encoded_str_len(&self.file_name, CharacterSet::Unicode, false)
)?);
write_string_to_cursor(dst, &self.file_name, CharacterSet::Unicode, false)?;
Ok(())
}
fn size(&self) -> usize {
4 + 4 + 4 + encoded_str_len(&self.file_name, CharacterSet::Unicode, false)
}
}
#[derive(Debug, PartialEq, Clone)]
pub struct FileDirectoryInformation {
pub next_entry_offset: u32,
pub file_index: u32,
pub creation_time: i64,
pub last_access_time: i64,
pub last_write_time: i64,
pub change_time: i64,
pub end_of_file: i64,
pub allocation_size: i64,
pub file_attributes: FileAttributes,
pub file_name: String,
}
impl FileDirectoryInformation {
pub fn new(
creation_time: i64,
last_access_time: i64,
last_write_time: i64,
change_time: i64,
file_size: i64,
file_attributes: FileAttributes,
file_name: String,
) -> Self {
Self {
next_entry_offset: 0,
file_index: 0,
creation_time,
last_access_time,
last_write_time,
change_time,
end_of_file: file_size,
allocation_size: file_size,
file_attributes,
file_name,
}
}
fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
ensure_size!(in: dst, size: self.size());
dst.write_u32(self.next_entry_offset);
dst.write_u32(self.file_index);
dst.write_i64(self.creation_time);
dst.write_i64(self.last_access_time);
dst.write_i64(self.last_write_time);
dst.write_i64(self.change_time);
dst.write_i64(self.end_of_file);
dst.write_i64(self.allocation_size);
dst.write_u32(self.file_attributes.bits());
dst.write_u32(cast_length!(
"FileDirectoryInformation::encode",
"file_name_length",
encoded_str_len(&self.file_name, CharacterSet::Unicode, false)
)?);
write_string_to_cursor(dst, &self.file_name, CharacterSet::Unicode, false)?;
Ok(())
}
fn size(&self) -> usize {
4 + 4 + 8 + 8 + 8 + 8 + 8 + 8 + 4 + 4 + encoded_str_len(&self.file_name, CharacterSet::Unicode, false)
}
}
#[derive(Debug, PartialEq, Clone)]
pub struct DeviceCloseRequest {
pub device_io_request: DeviceIoRequest,
}
impl DeviceCloseRequest {
pub fn decode(dev_io_req: DeviceIoRequest) -> Self {
Self {
device_io_request: dev_io_req,
}
}
}
#[derive(Debug, PartialEq, Clone)]
pub struct DeviceCloseResponse {
pub device_io_response: DeviceIoResponse,
}
impl DeviceCloseResponse {
const NAME: &'static str = "DR_CLOSE_RSP";
pub fn name(&self) -> &'static str {
Self::NAME
}
pub fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
ensure_size!(in: dst, size: self.size());
self.device_io_response.encode(dst)?;
dst.write_u32(0); Ok(())
}
pub fn size(&self) -> usize {
self.device_io_response.size() + 4 }
}
#[derive(Debug, PartialEq, Clone)]
pub struct ServerDriveQueryDirectoryRequest {
pub device_io_request: DeviceIoRequest,
pub file_info_class_lvl: FileInformationClassLevel,
pub initial_query: u8,
pub path: String,
}
impl ServerDriveQueryDirectoryRequest {
const FIXED_PART_SIZE: usize = 4 + 1 + 4 + 23 ;
fn decode(device_io_request: DeviceIoRequest, src: &mut ReadCursor<'_>) -> DecodeResult<Self> {
ensure_fixed_part_size!(in: src);
let file_info_class_lvl = FileInformationClassLevel::from(src.read_u32());
match file_info_class_lvl {
FileInformationClassLevel::FILE_DIRECTORY_INFORMATION
| FileInformationClassLevel::FILE_FULL_DIRECTORY_INFORMATION
| FileInformationClassLevel::FILE_BOTH_DIRECTORY_INFORMATION
| FileInformationClassLevel::FILE_NAMES_INFORMATION => {}
_ => {
return Err(invalid_field_err!(
"ServerDriveQueryDirectoryRequest::decode",
"file_info_class_lvl",
"received invalid level"
))
}
}
let initial_query = src.read_u8();
let path_length = cast_length!("ServerDriveQueryDirectoryRequest", "path_length", src.read_u32())?;
read_padding!(src, 23);
ensure_size!(in: src, size: path_length);
let path = decode_string(src.read_slice(path_length), CharacterSet::Unicode, true)?;
Ok(Self {
device_io_request,
file_info_class_lvl,
initial_query,
path,
})
}
}
#[derive(Debug, PartialEq, Clone)]
pub struct ServerDriveNotifyChangeDirectoryRequest {
pub device_io_request: DeviceIoRequest,
pub watch_tree: u8,
pub completion_filter: u32,
}
impl ServerDriveNotifyChangeDirectoryRequest {
const FIXED_PART_SIZE: usize = 1 + 4 + 27 ;
fn decode(device_io_request: DeviceIoRequest, src: &mut ReadCursor<'_>) -> DecodeResult<Self> {
ensure_fixed_part_size!(in: src);
let watch_tree = src.read_u8();
let completion_filter = src.read_u32();
read_padding!(src, 27);
Ok(Self {
device_io_request,
watch_tree,
completion_filter,
})
}
}
#[derive(Debug, PartialEq, Clone)]
pub struct ClientDriveQueryDirectoryResponse {
pub device_io_reply: DeviceIoResponse,
pub buffer: Option<FileInformationClass>,
}
impl ClientDriveQueryDirectoryResponse {
const NAME: &'static str = "DR_DRIVE_QUERY_DIRECTORY_RSP";
pub fn name(&self) -> &'static str {
Self::NAME
}
pub fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
ensure_size!(in: dst, size: self.size());
self.device_io_reply.encode(dst)?;
dst.write_u32(cast_length!(
"ClientDriveQueryDirectoryResponse",
"length",
if self.buffer.is_some() {
self.buffer.as_ref().unwrap().size()
} else {
0
}
)?);
if let Some(buffer) = &self.buffer {
buffer.encode(dst)?;
} else {
write_padding!(dst, 1) }
Ok(())
}
pub fn size(&self) -> usize {
self.device_io_reply.size() + 4 + if let Some(buffer) = &self.buffer {
buffer.size() } else {
1 }
}
}
#[derive(Debug, PartialEq, Clone)]
pub struct ServerDriveQueryVolumeInformationRequest {
pub device_io_request: DeviceIoRequest,
pub fs_info_class_lvl: FileSystemInformationClassLevel,
}
impl ServerDriveQueryVolumeInformationRequest {
const FIXED_PART_SIZE: usize = 4 + 4 + 24 ;
pub fn decode(dev_io_req: DeviceIoRequest, src: &mut ReadCursor<'_>) -> DecodeResult<Self> {
ensure_fixed_part_size!(in: src);
let fs_info_class_lvl = FileSystemInformationClassLevel::from(src.read_u32());
match fs_info_class_lvl {
FileSystemInformationClassLevel::FILE_FS_VOLUME_INFORMATION
| FileSystemInformationClassLevel::FILE_FS_SIZE_INFORMATION
| FileSystemInformationClassLevel::FILE_FS_ATTRIBUTE_INFORMATION
| FileSystemInformationClassLevel::FILE_FS_FULL_SIZE_INFORMATION
| FileSystemInformationClassLevel::FILE_FS_DEVICE_INFORMATION => {}
_ => {
return Err(invalid_field_err!(
"ServerDriveQueryVolumeInformationRequest::decode",
"fs_info_class_lvl",
"received invalid level"
))
}
}
let length = cast_length!("ServerDriveQueryVolumeInformationRequest", "length", src.read_u32())?; read_padding!(src, 24); ensure_size!(in: src, size: length);
read_padding!(src, length); Ok(Self {
device_io_request: dev_io_req,
fs_info_class_lvl,
})
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct FileSystemInformationClassLevel(u32);
impl FileSystemInformationClassLevel {
pub const FILE_FS_VOLUME_INFORMATION: Self = Self(1);
pub const FILE_FS_LABEL_INFORMATION: Self = Self(2);
pub const FILE_FS_SIZE_INFORMATION: Self = Self(3);
pub const FILE_FS_DEVICE_INFORMATION: Self = Self(4);
pub const FILE_FS_ATTRIBUTE_INFORMATION: Self = Self(5);
pub const FILE_FS_CONTROL_INFORMATION: Self = Self(6);
pub const FILE_FS_FULL_SIZE_INFORMATION: Self = Self(7);
pub const FILE_FS_OBJECT_ID_INFORMATION: Self = Self(8);
pub const FILE_FS_DRIVER_PATH_INFORMATION: Self = Self(9);
pub const FILE_FS_VOLUME_FLAGS_INFORMATION: Self = Self(10);
pub const FILE_FS_SECTOR_SIZE_INFORMATION: Self = Self(11);
}
impl From<u32> for FileSystemInformationClassLevel {
fn from(value: u32) -> Self {
Self(value)
}
}
#[derive(Debug, PartialEq, Clone)]
pub enum FileSystemInformationClass {
FileFsVolumeInformation(FileFsVolumeInformation),
FileFsSizeInformation(FileFsSizeInformation),
FileFsAttributeInformation(FileFsAttributeInformation),
FileFsFullSizeInformation(FileFsFullSizeInformation),
FileFsDeviceInformation(FileFsDeviceInformation),
}
impl FileSystemInformationClass {
fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
ensure_size!(in: dst, size: self.size());
match self {
Self::FileFsVolumeInformation(f) => f.encode(dst),
Self::FileFsSizeInformation(f) => f.encode(dst),
Self::FileFsAttributeInformation(f) => f.encode(dst),
Self::FileFsFullSizeInformation(f) => f.encode(dst),
Self::FileFsDeviceInformation(f) => f.encode(dst),
}
}
fn size(&self) -> usize {
match self {
Self::FileFsVolumeInformation(f) => f.size(),
Self::FileFsSizeInformation(f) => f.size(),
Self::FileFsAttributeInformation(f) => f.size(),
Self::FileFsFullSizeInformation(_) => FileFsFullSizeInformation::size(),
Self::FileFsDeviceInformation(_) => FileFsDeviceInformation::size(),
}
}
}
impl From<FileFsVolumeInformation> for FileSystemInformationClass {
fn from(file_fs_vol_info: FileFsVolumeInformation) -> Self {
Self::FileFsVolumeInformation(file_fs_vol_info)
}
}
impl From<FileFsSizeInformation> for FileSystemInformationClass {
fn from(file_fs_vol_info: FileFsSizeInformation) -> Self {
Self::FileFsSizeInformation(file_fs_vol_info)
}
}
impl From<FileFsAttributeInformation> for FileSystemInformationClass {
fn from(file_fs_vol_info: FileFsAttributeInformation) -> Self {
Self::FileFsAttributeInformation(file_fs_vol_info)
}
}
impl From<FileFsFullSizeInformation> for FileSystemInformationClass {
fn from(file_fs_vol_info: FileFsFullSizeInformation) -> Self {
Self::FileFsFullSizeInformation(file_fs_vol_info)
}
}
impl From<FileFsDeviceInformation> for FileSystemInformationClass {
fn from(file_fs_vol_info: FileFsDeviceInformation) -> Self {
Self::FileFsDeviceInformation(file_fs_vol_info)
}
}
#[derive(Debug, PartialEq, Clone)]
pub struct FileFsVolumeInformation {
pub volume_creation_time: i64,
pub volume_serial_number: u32,
pub supports_objects: Boolean,
pub volume_label: String,
}
impl FileFsVolumeInformation {
pub fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
ensure_size!(in: dst, size: self.size());
dst.write_i64(self.volume_creation_time);
dst.write_u32(self.volume_serial_number);
dst.write_u32(cast_length!(
"FileFsVolumeInformation::encode",
"volume_label_length",
encoded_str_len(&self.volume_label, CharacterSet::Unicode, true)
)?);
dst.write_u8(self.supports_objects.into());
write_string_to_cursor(dst, &self.volume_label, CharacterSet::Unicode, true)?;
Ok(())
}
pub fn size(&self) -> usize {
8 + 4 + 4 + 1 + encoded_str_len(&self.volume_label, CharacterSet::Unicode, true)
}
}
#[derive(Debug, PartialEq, Clone)]
pub struct FileFsSizeInformation {
pub total_alloc_units: i64,
pub available_alloc_units: i64,
pub sectors_per_alloc_unit: u32,
pub bytes_per_sector: u32,
}
impl FileFsSizeInformation {
pub fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
ensure_size!(in: dst, size: self.size());
dst.write_i64(self.total_alloc_units);
dst.write_i64(self.available_alloc_units);
dst.write_u32(self.sectors_per_alloc_unit);
dst.write_u32(self.bytes_per_sector);
Ok(())
}
pub fn size(&self) -> usize {
8 + 8 + 4 + 4 }
}
#[derive(Debug, PartialEq, Clone)]
pub struct FileFsAttributeInformation {
pub file_system_attributes: FileSystemAttributes,
pub max_component_name_len: u32,
pub file_system_name: String,
}
impl FileFsAttributeInformation {
pub fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
ensure_size!(in: dst, size: self.size());
dst.write_u32(self.file_system_attributes.bits());
dst.write_u32(self.max_component_name_len);
dst.write_u32(cast_length!(
"FileFsAttributeInformation::encode",
"file_system_name_length",
encoded_str_len(&self.file_system_name, CharacterSet::Unicode, true)
)?);
write_string_to_cursor(dst, &self.file_system_name, CharacterSet::Unicode, true)?;
Ok(())
}
pub fn size(&self) -> usize {
4 + 4 + 4 + encoded_str_len(&self.file_system_name, CharacterSet::Unicode, true)
}
}
#[derive(Debug, PartialEq, Clone)]
pub struct FileFsFullSizeInformation {
pub total_alloc_units: i64,
pub caller_available_alloc_units: i64,
pub actual_available_alloc_units: i64,
pub sectors_per_alloc_unit: u32,
pub bytes_per_sector: u32,
}
impl FileFsFullSizeInformation {
pub fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
ensure_size!(in: dst, size: Self::size());
dst.write_i64(self.total_alloc_units);
dst.write_i64(self.caller_available_alloc_units);
dst.write_i64(self.actual_available_alloc_units);
dst.write_u32(self.sectors_per_alloc_unit);
dst.write_u32(self.bytes_per_sector);
Ok(())
}
pub fn size() -> usize {
8 + 8 + 8 + 4 + 4 }
}
#[derive(Debug, PartialEq, Clone)]
pub struct FileFsDeviceInformation {
pub device_type: u32,
pub characteristics: Characteristics,
}
impl FileFsDeviceInformation {
pub fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
ensure_size!(in: dst, size: Self::size());
dst.write_u32(self.device_type);
dst.write_u32(self.characteristics.bits());
Ok(())
}
pub fn size() -> usize {
4 + 4 }
}
bitflags! {
#[derive(Debug, PartialEq, Clone)]
pub struct FileSystemAttributes: u32 {
const FILE_SUPPORTS_USN_JOURNAL = 0x02000000;
const FILE_SUPPORTS_OPEN_BY_FILE_ID = 0x01000000;
const FILE_SUPPORTS_EXTENDED_ATTRIBUTES = 0x00800000;
const FILE_SUPPORTS_HARD_LINKS = 0x00400000;
const FILE_SUPPORTS_TRANSACTIONS = 0x00200000;
const FILE_SEQUENTIAL_WRITE_ONCE = 0x00100000;
const FILE_READ_ONLY_VOLUME = 0x00080000;
const FILE_NAMED_STREAMS = 0x00040000;
const FILE_SUPPORTS_ENCRYPTION = 0x00020000;
const FILE_SUPPORTS_OBJECT_IDS = 0x00010000;
const FILE_VOLUME_IS_COMPRESSED = 0x00008000;
const FILE_SUPPORTS_REMOTE_STORAGE = 0x00000100;
const FILE_SUPPORTS_REPARSE_POINTS = 0x00000080;
const FILE_SUPPORTS_SPARSE_FILES = 0x00000040;
const FILE_VOLUME_QUOTAS = 0x00000020;
const FILE_FILE_COMPRESSION = 0x00000010;
const FILE_PERSISTENT_ACLS = 0x00000008;
const FILE_UNICODE_ON_DISK = 0x00000004;
const FILE_CASE_PRESERVED_NAMES = 0x00000002;
const FILE_CASE_SENSITIVE_SEARCH = 0x00000001;
const FILE_SUPPORT_INTEGRITY_STREAMS = 0x04000000;
const FILE_SUPPORTS_BLOCK_REFCOUNTING = 0x08000000;
const FILE_SUPPORTS_SPARSE_VDL = 0x10000000;
}
}
bitflags! {
#[derive(Debug, PartialEq, Clone)]
pub struct Characteristics: u32 {
const FILE_REMOVABLE_MEDIA = 0x00000001;
const FILE_READ_ONLY_DEVICE = 0x00000002;
const FILE_FLOPPY_DISKETTE = 0x00000004;
const FILE_WRITE_ONCE_MEDIA = 0x00000008;
const FILE_REMOTE_DEVICE = 0x00000010;
const FILE_DEVICE_IS_MOUNTED = 0x00000020;
const FILE_VIRTUAL_VOLUME = 0x00000040;
const FILE_DEVICE_SECURE_OPEN = 0x00000100;
const FILE_CHARACTERISTIC_TS_DEVICE = 0x00001000;
const FILE_CHARACTERISTIC_WEBDAV_DEVICE = 0x00002000;
const FILE_DEVICE_ALLOW_APPCONTAINER_TRAVERSAL = 0x00020000;
const FILE_PORTABLE_DEVICE = 0x0004000;
}
}
#[derive(Debug, PartialEq, Clone)]
pub struct ClientDriveQueryVolumeInformationResponse {
pub device_io_reply: DeviceIoResponse,
pub buffer: Option<FileSystemInformationClass>,
}
impl ClientDriveQueryVolumeInformationResponse {
const NAME: &'static str = "DR_DRIVE_QUERY_VOLUME_INFORMATION_RSP";
pub fn new(
device_io_request: DeviceIoRequest,
io_status: NtStatus,
buffer: Option<FileSystemInformationClass>,
) -> Self {
Self {
device_io_reply: DeviceIoResponse::new(device_io_request, io_status),
buffer,
}
}
pub fn name(&self) -> &'static str {
Self::NAME
}
pub fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
ensure_size!(in: dst, size: self.size());
self.device_io_reply.encode(dst)?;
dst.write_u32(cast_length!(
"ClientDriveQueryVolumeInformationResponse",
"length",
if self.buffer.is_some() {
self.buffer.as_ref().unwrap().size()
} else {
0
}
)?);
if let Some(buffer) = &self.buffer {
buffer.encode(dst)?;
}
Ok(())
}
pub fn size(&self) -> usize {
self.device_io_reply.size() + 4 + if let Some(buffer) = &self.buffer {
buffer.size() } else {
0
}
}
}
#[derive(Debug, PartialEq, Clone)]
pub struct DeviceReadRequest {
pub device_io_request: DeviceIoRequest,
pub length: u32,
pub offset: u64,
}
impl DeviceReadRequest {
const FIXED_PART_SIZE: usize = 4 + 8 + 20 ;
pub fn decode(dev_io_req: DeviceIoRequest, src: &mut ReadCursor<'_>) -> DecodeResult<Self> {
ensure_fixed_part_size!(in: src);
let length = src.read_u32();
let offset = src.read_u64();
read_padding!(src, 20);
Ok(Self {
device_io_request: dev_io_req,
length,
offset,
})
}
}
pub struct DeviceReadResponse {
pub device_io_reply: DeviceIoResponse,
pub read_data: Vec<u8>,
}
impl DeviceReadResponse {
const NAME: &'static str = "DR_READ_RSP";
pub fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
ensure_size!(in: dst, size: self.size());
self.device_io_reply.encode(dst)?;
dst.write_u32(cast_length!("DeviceReadResponse", "length", self.read_data.len())?);
dst.write_slice(&self.read_data);
Ok(())
}
pub fn name(&self) -> &'static str {
Self::NAME
}
pub fn size(&self) -> usize {
self.device_io_reply.size() + 4 + self.read_data.len() }
}
impl Debug for DeviceReadResponse {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("DeviceReadResponse")
.field("device_io_reply", &self.device_io_reply)
.field("read_data", &format!("Vec<u8> of length {}", self.read_data.len()))
.finish()
}
}
#[derive(PartialEq, Clone)]
pub struct DeviceWriteRequest {
pub device_io_request: DeviceIoRequest,
pub offset: u64,
pub write_data: Vec<u8>,
}
impl DeviceWriteRequest {
const FIXED_PART_SIZE: usize = 4 + 8 + 20 ;
pub fn decode(dev_io_req: DeviceIoRequest, src: &mut ReadCursor<'_>) -> DecodeResult<Self> {
ensure_fixed_part_size!(in: src);
let length = cast_length!("DeviceWriteRequest", "length", src.read_u32())?;
let offset = src.read_u64();
read_padding!(src, 20);
ensure_size!(in: src, size: length);
let write_data = src.read_slice(length).to_vec();
Ok(Self {
device_io_request: dev_io_req,
offset,
write_data,
})
}
}
impl Debug for DeviceWriteRequest {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("DeviceWriteRequest")
.field("device_io_request", &self.device_io_request)
.field("offset", &self.offset)
.field("write_data", &format!("Vec<u8> of length {}", self.write_data.len()))
.finish()
}
}
#[derive(Debug, PartialEq, Clone)]
pub struct DeviceWriteResponse {
pub device_io_reply: DeviceIoResponse,
pub length: u32,
}
impl DeviceWriteResponse {
const NAME: &'static str = "DR_WRITE_RSP";
pub fn name(&self) -> &'static str {
Self::NAME
}
pub fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
ensure_size!(in: dst, size: self.size());
self.device_io_reply.encode(dst)?;
dst.write_u32(self.length);
write_padding!(dst, 1); Ok(())
}
pub fn size(&self) -> usize {
self.device_io_reply.size() + 4 + 1 }
}
#[derive(Debug, PartialEq, Clone)]
pub struct ServerDriveSetInformationRequest {
pub device_io_request: DeviceIoRequest,
pub set_buffer: FileInformationClass,
}
impl ServerDriveSetInformationRequest {
const FIXED_PART_SIZE: usize = 4 + 4 + 24 ;
fn decode(dev_io_req: DeviceIoRequest, src: &mut ReadCursor<'_>) -> DecodeResult<Self> {
ensure_fixed_part_size!(in: src);
let file_information_class_level = FileInformationClassLevel::from(src.read_u32());
match file_information_class_level {
FileInformationClassLevel::FILE_BASIC_INFORMATION
| FileInformationClassLevel::FILE_END_OF_FILE_INFORMATION
| FileInformationClassLevel::FILE_DISPOSITION_INFORMATION
| FileInformationClassLevel::FILE_RENAME_INFORMATION
| FileInformationClassLevel::FILE_ALLOCATION_INFORMATION => {}
_ => {
return Err(invalid_field_err!(
"ServerDriveSetInformationRequest::decode",
"file_information_class_level",
"received invalid level"
))
}
};
let length = cast_length!("ServerDriveSetInformationRequest", "length", src.read_u32())?;
read_padding!(src, 24); let set_buffer = FileInformationClass::decode(file_information_class_level, length, src)?;
Ok(Self {
device_io_request: dev_io_req,
set_buffer,
})
}
}
#[derive(Debug, PartialEq, Clone)]
pub struct FileEndOfFileInformation {
pub end_of_file: i64,
}
impl FileEndOfFileInformation {
const FIXED_PART_SIZE: usize = 8; fn decode(src: &mut ReadCursor<'_>) -> DecodeResult<Self> {
ensure_fixed_part_size!(in: src);
let end_of_file = src.read_i64();
Ok(Self { end_of_file })
}
fn size() -> usize {
Self::FIXED_PART_SIZE
}
}
#[derive(Debug, PartialEq, Clone)]
pub struct FileDispositionInformation {
pub delete_pending: u8,
}
impl FileDispositionInformation {
const FIXED_PART_SIZE: usize = 1; fn decode(src: &mut ReadCursor<'_>, length: usize) -> DecodeResult<Self> {
let delete_pending = if length != 0 {
ensure_fixed_part_size!(in: src);
src.read_u8()
} else {
1
};
Ok(Self { delete_pending })
}
fn size() -> usize {
Self::FIXED_PART_SIZE
}
}
#[derive(Debug, PartialEq, Clone)]
pub struct FileRenameInformation {
pub replace_if_exists: Boolean,
pub file_name: String,
}
impl FileRenameInformation {
const FIXED_PART_SIZE: usize = 1 + 1 + 4 ;
fn decode(src: &mut ReadCursor<'_>) -> DecodeResult<Self> {
ensure_fixed_part_size!(in: src);
let replace_if_exists = Boolean::from(src.read_u8());
let _ = src.read_u8(); let file_name_length = cast_length!("FileRenameInformation", "file_name_length", src.read_u32())?;
ensure_size!(in: src, size: file_name_length);
let file_name = decode_string(src.read_slice(file_name_length), CharacterSet::Unicode, true)?;
Ok(Self {
replace_if_exists,
file_name,
})
}
fn size(&self) -> usize {
Self::FIXED_PART_SIZE + encoded_str_len(&self.file_name, CharacterSet::Unicode, true)
}
}
#[derive(Debug, PartialEq, Clone)]
pub struct FileAllocationInformation {
pub allocation_size: i64,
}
impl FileAllocationInformation {
const FIXED_PART_SIZE: usize = 8; fn decode(src: &mut ReadCursor<'_>) -> DecodeResult<Self> {
ensure_fixed_part_size!(in: src);
let allocation_size = src.read_i64();
Ok(Self { allocation_size })
}
fn size() -> usize {
Self::FIXED_PART_SIZE
}
}
#[derive(Debug, PartialEq, Clone)]
pub struct ClientDriveSetInformationResponse {
device_io_reply: DeviceIoResponse,
length: u32,
}
impl ClientDriveSetInformationResponse {
const NAME: &'static str = "DR_DRIVE_SET_INFORMATION_RSP";
pub fn new(req: &ServerDriveSetInformationRequest, io_status: NtStatus) -> EncodeResult<Self> {
Ok(Self {
device_io_reply: DeviceIoResponse::new(req.device_io_request.clone(), io_status),
length: cast_length!("ClientDriveSetInformationResponse", "length", req.set_buffer.size())?,
})
}
pub fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
ensure_size!(in: dst, size: self.size());
self.device_io_reply.encode(dst)?;
dst.write_u32(self.length);
Ok(())
}
pub fn name(&self) -> &'static str {
Self::NAME
}
pub fn size(&self) -> usize {
self.device_io_reply.size() + 4 }
}
#[derive(Debug, PartialEq, Clone)]
pub struct ServerDriveLockControlRequest {
pub device_io_request: DeviceIoRequest,
}
impl ServerDriveLockControlRequest {
fn decode(dev_io_req: DeviceIoRequest, src: &mut ReadCursor<'_>) -> DecodeResult<Self> {
ensure_size!(in: src, size: 4);
let _ = src.read_u32();
Ok(Self {
device_io_request: dev_io_req,
})
}
}