use binrw::prelude::*;
use binrw::{NullWideString, io::TakeSeekExt};
use modular_bitfield::prelude::*;
use smb_dtyp::{
binrw_util::prelude::*,
security::{ACL, ClaimSecurityAttributeRelativeV1, SID},
};
#[smb_dtyp::mbitfield]
pub struct TreeConnectRequestFlags {
pub cluster_reconnect: bool,
pub redirect_to_owner: bool,
pub extension_present: bool,
#[skip]
__: B13,
}
#[smb_request(size = 9)]
pub struct TreeConnectRequest {
pub flags: TreeConnectRequestFlags,
#[bw(calc = PosMarker::default())]
#[br(temp)]
_path_offset: PosMarker<u16>,
#[bw(try_calc = buffer.size().try_into())]
#[br(temp)]
path_length: u16,
#[br(if(flags.extension_present()))]
#[br(temp)]
#[bw(calc = if tree_connect_contexts.is_empty() { None } else { Some(PosMarker::default()) })]
tree_connect_context_offset: Option<PosMarker<u32>>,
#[br(if(flags.extension_present()))]
#[bw(if(!tree_connect_contexts.is_empty()))]
#[bw(calc = if tree_connect_contexts.is_empty() { None } else { Some(tree_connect_contexts.len().try_into().unwrap()) })]
#[br(temp)]
tree_connect_context_count: Option<u16>,
#[br(if(flags.extension_present()))]
#[bw(if(!tree_connect_contexts.is_empty()))]
#[bw(calc = Some([0u8; 10]))]
#[br(temp)]
_reserved: Option<[u8; 10]>,
#[brw(little)]
#[br(args { size: SizedStringSize::bytes16(path_length) })]
#[bw(write_with = PosMarker::write_aoff, args(&_path_offset))]
pub buffer: SizedWideString,
#[br(if(flags.extension_present()))]
#[br(seek_before = tree_connect_context_offset.unwrap().seek_relative(true))]
#[br(count = tree_connect_context_count.unwrap_or(0))]
#[bw(if(!tree_connect_contexts.is_empty()))]
#[bw(write_with = PosMarker::write_aoff_m, args(tree_connect_context_offset.as_ref()))]
tree_connect_contexts: Vec<TreeConnectContext>,
}
#[smb_request_binrw]
pub struct TreeConnectContext {
#[bw(calc = 1)]
#[br(assert(context_type == 1))]
context_type: u16,
data_length: u16,
reserved: u32,
data: RemotedIdentityTreeConnect,
}
macro_rules! make_remoted_identity_connect{
(
$($field:ident: $value:ty),*
) => {
pastey::paste! {
#[binwrite]
#[derive(Debug, BinRead, PartialEq, Eq)]
pub struct RemotedIdentityTreeConnect {
#[bw(calc = PosMarker::new(1))]
#[br(assert(_ticket_type.value == 1))]
_ticket_type: PosMarker<u16>,
ticket_size: u16,
$(
#[bw(calc = PosMarker::default())]
#[br(temp)]
[<_$field _offset>]: PosMarker<u16>,
)*
$(
#[br(seek_before = _ticket_type.seek_from([<_$field _offset>].value as u64))]
#[bw(write_with = PosMarker::write_roff_b, args(&[<_$field _offset>], &_ticket_type))]
$field: $value,
)*
}
}
}
}
make_remoted_identity_connect! {
user: SidAttrData,
user_name: NullWideString,
domain: NullWideString,
groups: SidArrayData,
restricted_groups: SidArrayData,
privileges: PrivilegeArrayData,
primary_group: SidArrayData,
owner: BlobData<SID>,
default_dacl: BlobData<ACL>,
device_groups: SidArrayData,
user_claims: BlobData<ClaimSecurityAttributeRelativeV1>,
device_claims: BlobData<ClaimSecurityAttributeRelativeV1>
}
#[binrw::binrw]
#[derive(Debug, PartialEq, Eq)]
pub struct BlobData<T>
where
T: BinRead + BinWrite,
for<'a> <T as BinRead>::Args<'a>: Default,
for<'b> <T as BinWrite>::Args<'b>: Default,
{
blob_size: PosMarker<u16>,
#[br(map_stream = |s| s.take_seek(blob_size.value as u64))]
pub blob_data: T,
}
#[binrw::binrw]
#[derive(Debug, PartialEq, Eq)]
pub struct ArrayData<T>
where
T: BinRead + BinWrite + 'static,
for<'a> <T as BinRead>::Args<'a>: Default + Clone,
for<'b> <T as BinWrite>::Args<'b>: Default + Clone,
{
#[bw(try_calc = list.len().try_into())]
lcount: u16,
#[br(count = lcount)]
pub list: Vec<T>,
}
#[binrw::binrw]
#[derive(Debug, PartialEq, Eq)]
pub struct SidAttrData {
pub sid_data: SID,
pub attr: SidAttrSeGroup,
}
type SidArrayData = ArrayData<SidAttrData>;
#[smb_dtyp::mbitfield]
pub struct SidAttrSeGroup {
pub mandatory: bool,
pub enabled_by_default: bool,
pub group_enabled: bool,
pub group_owner: bool,
pub group_use_for_deny_only: bool,
pub group_integrity: bool,
pub group_integrity_enabled: bool,
#[skip]
__: B21,
pub group_logon_id: B4,
}
#[binrw::binrw]
#[derive(Debug, PartialEq, Eq)]
pub struct LuidAttrData {
pub luid: u64,
pub attr: LsaprLuidAttributes,
}
mod lsapr_luid_attributes {
use super::*;
#[smb_dtyp::mbitfield]
pub struct LsaprLuidAttributes {
pub is_default: bool,
pub is_enabled: bool,
#[skip]
__: B30,
}
}
use lsapr_luid_attributes::LsaprLuidAttributes;
use smb_msg_derive::*;
type PrivilegeData = BlobData<LuidAttrData>;
type PrivilegeArrayData = ArrayData<PrivilegeData>;
impl TreeConnectRequest {
pub fn new(name: &str) -> TreeConnectRequest {
TreeConnectRequest {
flags: TreeConnectRequestFlags::new(),
buffer: name.into(),
tree_connect_contexts: vec![],
}
}
}
#[smb_response(size = 16)]
pub struct TreeConnectResponse {
pub share_type: ShareType,
reserved: u8,
pub share_flags: ShareFlags,
pub capabilities: TreeCapabilities,
pub maximal_access: u32,
}
#[derive(BitfieldSpecifier, Debug, Clone, Copy)]
#[bits = 4]
pub enum ShareCacheMode {
Manual,
Auto,
Vdo,
NoCache,
All = 0xf,
}
#[smb_dtyp::mbitfield]
pub struct ShareFlags {
pub dfs: bool,
pub dfs_root: bool,
#[skip]
__: B2,
pub caching_mode: ShareCacheMode,
pub restrict_exclusive_opens: bool,
pub force_shared_delete: bool,
pub allow_namespace_caching: bool,
pub access_based_directory_enum: bool,
pub force_levelii_oplock: bool,
pub enable_hash_v1: bool,
pub enable_hash_v2: bool,
pub encrypt_data: bool,
#[skip]
__: B2,
pub identity_remoting: bool,
#[skip]
__: B1,
pub compress_data: bool,
pub isolated_transport: bool,
#[skip]
__: B10,
}
#[smb_dtyp::mbitfield]
pub struct TreeCapabilities {
#[skip]
__: B3,
pub dfs: bool,
pub continuous_availability: bool,
pub scaleout: bool,
pub cluster: bool,
pub asymmetric: bool,
pub redirect_to_owner: bool,
#[skip]
__: B23,
}
#[smb_response_binrw]
#[derive(Clone, Copy)]
#[brw(repr(u8))]
pub enum ShareType {
Disk = 0x1,
Pipe = 0x2,
Print = 0x3,
}
#[smb_request(size = 4)]
#[derive(Default)]
pub struct TreeDisconnectRequest {
reserved: u16,
}
#[smb_response(size = 4)]
#[derive(Default)]
pub struct TreeDisconnectResponse {
reserved: u16,
}
#[cfg(test)]
mod tests {
use crate::*;
use super::*;
test_request! {
TreeConnect {
flags: TreeConnectRequestFlags::new(),
buffer: r"\\adc.aviv.local\IPC$".into(),
tree_connect_contexts: vec![],
} => "0900000048002a005c005c006100640063002e0061007600690076002e006c006f00630061006c005c004900500043002400"
}
test_binrw_response! {
struct TreeConnectResponse {
share_type: ShareType::Disk,
share_flags: ShareFlags::new().with_access_based_directory_enum(true),
capabilities: TreeCapabilities::new(),
maximal_access: 0x001f01ff,
} => "100001000008000000000000ff011f00"
}
}