1use binrw::prelude::*;
2use binrw::{NullWideString, io::TakeSeekExt};
3use modular_bitfield::prelude::*;
4use smb_dtyp::{
5 binrw_util::prelude::*,
6 security::{ACL, ClaimSecurityAttributeRelativeV1, SID},
7};
8
9#[bitfield]
10#[derive(BinWrite, BinRead, Debug, Default, Clone, Copy, PartialEq, Eq)]
11#[bw(map = |&x| Self::into_bytes(x))]
12#[br(map = Self::from_bytes)]
13pub struct TreeConnectRequestFlags {
14 pub cluster_reconnect: bool,
15 pub redirect_to_owner: bool,
16 pub extension_present: bool,
17 #[skip]
18 __: B13,
19}
20
21#[binrw::binrw]
27#[derive(Debug, PartialEq, Eq)]
28pub struct TreeConnectRequest {
29 #[bw(calc = 9)]
30 #[br(assert(_structure_size == 9))]
31 _structure_size: u16,
32 pub flags: TreeConnectRequestFlags,
33 #[bw(calc = PosMarker::default())]
34 _path_offset: PosMarker<u16>,
35 #[bw(try_calc = buffer.size().try_into())]
36 path_length: u16,
37
38 #[br(if(flags.extension_present()))]
40 #[bw(calc = if tree_connect_contexts.is_empty() { None } else { Some(PosMarker::default()) })]
41 tree_connect_context_offset: Option<PosMarker<u32>>,
42
43 #[br(if(flags.extension_present()))]
44 #[bw(if(!tree_connect_contexts.is_empty()))]
45 #[bw(calc = if tree_connect_contexts.is_empty() { None } else { Some(tree_connect_contexts.len().try_into().unwrap()) })]
46 tree_connect_context_count: Option<u16>,
47
48 #[br(if(flags.extension_present()))]
49 #[bw(if(!tree_connect_contexts.is_empty()))]
50 #[bw(calc = Some([0u8; 10]))]
51 _reserved: Option<[u8; 10]>,
52 #[brw(little)]
56 #[br(args { size: SizedStringSize::bytes16(path_length) })]
57 #[bw(write_with = PosMarker::write_aoff, args(&_path_offset))]
58 pub buffer: SizedWideString,
59
60 #[br(if(flags.extension_present()))]
62 #[br(seek_before = tree_connect_context_offset.unwrap().seek_relative(true))]
63 #[br(count = tree_connect_context_count.unwrap_or(0))]
64 #[bw(if(!tree_connect_contexts.is_empty()))]
65 #[bw(write_with = PosMarker::write_aoff_m, args(tree_connect_context_offset.as_ref()))]
66 tree_connect_contexts: Vec<TreeConnectContext>,
67}
68
69#[binrw::binrw]
70#[derive(Debug, PartialEq, Eq)]
71pub struct TreeConnectContext {
72 #[bw(calc = 1)]
74 #[br(assert(context_type == 1))]
75 context_type: u16,
76 data_length: u16,
77 reserved: u32,
78 data: RemotedIdentityTreeConnect,
79}
80
81macro_rules! make_remoted_identity_connect{
82 (
83 $($field:ident: $value:ty),*
84 ) => {
85 pastey::paste! {
86
87#[binwrite]
88#[derive(Debug, BinRead, PartialEq, Eq)]
89pub struct RemotedIdentityTreeConnect {
90 #[bw(calc = PosMarker::new(1))]
92 #[br(assert(_ticket_type.value == 1))]
93 _ticket_type: PosMarker<u16>,
94 ticket_size: u16,
95
96 $(
98 #[bw(calc = PosMarker::default())]
99 [<_$field _offset>]: PosMarker<u16>,
100 )*
101
102 $(
104 #[br(seek_before = _ticket_type.seek_from([<_$field _offset>].value as u64))]
105 #[bw(write_with = PosMarker::write_roff_b, args(&[<_$field _offset>], &_ticket_type))]
106 $field: $value,
107 )*
108}
109 }
110 }
111}
112
113make_remoted_identity_connect! {
114 user: SidAttrData,
115 user_name: NullWideString,
116 domain: NullWideString,
117 groups: SidArrayData,
118 restricted_groups: SidArrayData,
119 privileges: PrivilegeArrayData,
120 primary_group: SidArrayData,
121 owner: BlobData<SID>,
122 default_dacl: BlobData<ACL>,
123 device_groups: SidArrayData,
124 user_claims: BlobData<ClaimSecurityAttributeRelativeV1>,
125 device_claims: BlobData<ClaimSecurityAttributeRelativeV1>
126}
127
128#[binrw::binrw]
129#[derive(Debug, PartialEq, Eq)]
130pub struct BlobData<T>
131where
132 T: BinRead + BinWrite,
133 for<'a> <T as BinRead>::Args<'a>: Default,
134 for<'b> <T as BinWrite>::Args<'b>: Default,
135{
136 blob_size: PosMarker<u16>,
137 #[br(map_stream = |s| s.take_seek(blob_size.value as u64))]
138 pub blob_data: T,
139}
140
141#[binrw::binrw]
142#[derive(Debug, PartialEq, Eq)]
143pub struct ArrayData<T>
144where
145 T: BinRead + BinWrite + 'static,
146 for<'a> <T as BinRead>::Args<'a>: Default + Clone,
147 for<'b> <T as BinWrite>::Args<'b>: Default + Clone,
148{
149 #[bw(try_calc = list.len().try_into())]
150 lcount: u16,
151 #[br(count = lcount)]
152 pub list: Vec<T>,
153}
154
155#[binrw::binrw]
156#[derive(Debug, PartialEq, Eq)]
157pub struct SidAttrData {
158 pub sid_data: SID,
159 pub attr: SidAttrSeGroup,
160}
161
162type SidArrayData = ArrayData<SidAttrData>;
163
164#[bitfield]
165#[derive(BinWrite, BinRead, Debug, Default, Clone, Copy, PartialEq, Eq)]
166#[bw(map = |&x| Self::into_bytes(x))]
167#[br(map = Self::from_bytes)]
168pub struct SidAttrSeGroup {
169 pub mandatory: bool,
170 pub enabled_by_default: bool,
171 pub group_enabled: bool,
172 pub group_owner: bool,
173 pub group_use_for_deny_only: bool,
174 pub group_integrity: bool,
175 pub group_integrity_enabled: bool,
176 #[skip]
177 __: B21,
178 pub group_logon_id: B4,
179}
180
181#[binrw::binrw]
182#[derive(Debug, PartialEq, Eq)]
183pub struct LuidAttrData {
184 pub luid: u64,
185 pub attr: LsaprLuidAttributes,
186}
187
188#[allow(clippy::identity_op)]
189mod lsapr_luid_attributes {
190 use super::*;
191 #[bitfield]
193 #[derive(BinWrite, BinRead, Debug, Default, Clone, Copy, PartialEq, Eq)]
194 #[br(map = Self::from_bytes)]
195 pub struct LsaprLuidAttributes {
196 pub is_default: bool,
197 pub is_enabled: bool,
198 #[skip]
199 __: B30,
200 }
201}
202
203use lsapr_luid_attributes::LsaprLuidAttributes;
204
205type PrivilegeData = BlobData<LuidAttrData>;
206
207type PrivilegeArrayData = ArrayData<PrivilegeData>;
208
209impl TreeConnectRequest {
210 pub fn new(name: &str) -> TreeConnectRequest {
211 TreeConnectRequest {
212 flags: TreeConnectRequestFlags::new(),
213 buffer: name.into(),
214 tree_connect_contexts: vec![],
215 }
216 }
217}
218
219#[binrw::binrw]
220#[derive(Debug, PartialEq, Eq)]
221pub struct TreeConnectResponse {
222 #[bw(calc = 16)]
223 #[br(assert(_structure_size == 16))]
224 _structure_size: u16,
225 pub share_type: ShareType,
226 #[bw(calc = 0)]
227 _reserved: u8,
228 pub share_flags: ShareFlags,
229 pub capabilities: TreeCapabilities,
230 pub maximal_access: u32,
231}
232
233#[derive(BitfieldSpecifier, Debug, Clone, Copy)]
234#[bits = 4]
235pub enum ShareCacheMode {
236 Manual,
237 Auto,
238 Vdo,
239 NoCache,
240 All = 0xf,
241}
242
243#[bitfield]
244#[derive(BinWrite, BinRead, Debug, Default, Clone, Copy, PartialEq, Eq)]
245#[bw(map = |&x| Self::into_bytes(x))]
246#[br(map = Self::from_bytes)]
247pub struct ShareFlags {
248 pub dfs: bool,
249 pub dfs_root: bool,
250 #[skip]
251 __: B2,
252 pub caching_mode: ShareCacheMode,
253
254 pub restrict_exclusive_opens: bool,
255 pub force_shared_delete: bool,
256 pub allow_namespace_caching: bool,
257 pub access_based_directory_enum: bool,
258 pub force_levelii_oplock: bool,
259 pub enable_hash_v1: bool,
260 pub enable_hash_v2: bool,
261 pub encrypt_data: bool,
262
263 #[skip]
264 __: B2,
265 pub identity_remoting: bool,
266 #[skip]
267 __: B1,
268 pub compress_data: bool,
269 pub isolated_transport: bool,
270 #[skip]
271 __: B10,
272}
273
274#[bitfield]
275#[derive(BinWrite, BinRead, Debug, Default, Clone, Copy, PartialEq, Eq)]
276#[bw(map = |&x| Self::into_bytes(x))]
277#[br(map = Self::from_bytes)]
278pub struct TreeCapabilities {
279 #[skip]
280 __: B3,
281 pub dfs: bool,
282 pub continuous_availability: bool,
283 pub scaleout: bool,
284 pub cluster: bool,
285 pub asymmetric: bool,
286
287 pub redirect_to_owner: bool,
288 #[skip]
289 __: B23,
290}
291
292#[binrw::binrw]
293#[derive(Debug, PartialEq, Eq, Clone, Copy)]
294#[brw(repr(u8))]
295pub enum ShareType {
296 Disk = 0x1,
297 Pipe = 0x2,
298 Print = 0x3,
299}
300
301#[binrw::binrw]
302#[derive(Debug, Default)]
303pub struct TreeDisconnectRequest {
304 #[bw(calc = 4)]
305 #[br(assert(_structure_size == 4))]
306 _structure_size: u16,
307 #[bw(calc = 0)]
308 _reserved: u16,
309}
310
311#[binrw::binrw]
312#[derive(Debug)]
313pub struct TreeDisconnectResponse {
314 #[bw(calc = 4)]
315 #[br(assert(_structure_size == 4))]
316 _structure_size: u16,
317 #[bw(calc = 0)]
318 _reserved: u16,
319}
320
321#[cfg(test)]
322mod tests {
323 use smb_tests::*;
324
325 use crate::*;
326
327 use super::*;
328
329 test_request! {
331 TreeConnect {
332 flags: TreeConnectRequestFlags::new(),
333 buffer: r"\\adc.aviv.local\IPC$".into(),
334 tree_connect_contexts: vec![],
335 } => "0900000048002a005c005c006100640063002e0061007600690076002e006c006f00630061006c005c004900500043002400"
336 }
337
338 test_binrw! {
339 struct TreeConnectResponse {
340 share_type: ShareType::Disk,
341 share_flags: ShareFlags::new().with_access_based_directory_enum(true),
342 capabilities: TreeCapabilities::new(),
343 maximal_access: 0x001f01ff,
344 } => "100001000008000000000000ff011f00"
345 }
346}