1use binrw::{NullWideString, io::TakeSeekExt, prelude::*};
2use modular_bitfield::prelude::*;
3use smb_dtyp::binrw_util::prelude::*;
4
5#[binrw::binrw]
8#[derive(Debug, PartialEq, Eq)]
9pub struct ReqGetDfsReferral {
10 pub max_referral_level: ReferralLevel,
12 pub request_file_name: NullWideString,
14}
15
16#[binrw::binrw]
19#[derive(Debug, PartialEq, Eq)]
20#[brw(repr(u16))]
21pub enum ReferralLevel {
22 V1 = 1,
24 V2 = 2,
26 V3 = 3,
28 V4 = 4,
30}
31
32#[binrw::binrw]
33#[derive(Debug, PartialEq, Eq)]
34pub struct ReqGetDfsReferralEx {
35 pub max_referral_level: u16,
37 pub request_flags: DfsRequestFlags,
38 request_data_length: PosMarker<u32>,
39 #[bw(write_with = PosMarker::write_size, args(request_data_length))]
40 #[br(map_stream = |s| s.take_seek(request_data_length.value as u64))]
41 pub request_data: DfsRequestData,
42}
43
44#[bitfield]
45#[derive(BinWrite, BinRead, Debug, Default, Clone, Copy, PartialEq, Eq)]
46#[bw(map = |&x| Self::into_bytes(x))]
47#[br(map = Self::from_bytes)]
48pub struct DfsRequestFlags {
49 pub site_name: bool,
51 #[skip]
52 __: B15,
53}
54
55#[binrw::binrw]
57#[derive(Debug, PartialEq, Eq)]
58pub struct DfsRequestData {
59 #[bw(try_calc = request_file_name.size().try_into())]
60 request_file_name_length: u16,
61 #[br(args { size: SizedStringSize::bytes16(request_file_name_length) })]
63 request_file_name: SizedWideString,
64 #[bw(try_calc = site_name.size().try_into())]
65 site_name_length: u16,
66 #[br(args { size: SizedStringSize::bytes16(site_name_length) })]
68 site_name: SizedWideString,
69}
70
71impl DfsRequestData {
72 pub fn get_bin_size(&self) -> usize {
73 size_of::<u16>() * 2 + self.request_file_name.len() * size_of::<u16>() + self.site_name.len() * size_of::<u16>() }
77}
78
79#[binrw::binread]
81#[derive(Debug, PartialEq, Eq)]
82pub struct RespGetDfsReferral {
83 pub path_consumed: u16,
84 #[br(temp)]
86 number_of_referrals: u16,
87 pub referral_header_flags: ReferralHeaderFlags,
88 #[br(count = number_of_referrals)]
89 pub referral_entries: Vec<ReferralEntry>,
90 }
92
93impl BinWrite for RespGetDfsReferral {
94 type Args<'a> = ();
95
96 fn write_options<W: binrw::io::Write + binrw::io::Seek>(
97 &self,
98 _writer: &mut W,
99 _endian: binrw::Endian,
100 _args: Self::Args<'_>,
101 ) -> binrw::BinResult<()> {
102 unimplemented!(
103 "Placeholder trait implementation for RespGetDfsReferral - writing is currently not supported"
104 );
105 }
106}
107
108#[bitfield]
109#[derive(BinWrite, BinRead, Debug, Default, Clone, Copy, PartialEq, Eq)]
110#[bw(map = |&x| Self::into_bytes(x))]
111#[br(map = Self::from_bytes)]
112pub struct ReferralHeaderFlags {
113 pub referral_servers: bool,
115 pub storage_servers: bool,
117 pub target_failbacl: bool,
119 #[skip]
120 __: B29,
121}
122
123#[binrw::binrw]
124#[derive(Debug, PartialEq, Eq)]
125pub struct ReferralEntry {
126 #[bw(calc = value.get_version())]
128 pub version: u16,
129 #[bw(calc = PosMarker::default())]
130 _size: PosMarker<u16>,
131
132 #[br(args(version))]
133 #[bw(write_with = PosMarker::write_size_plus, args(&_size, Self::COMMON_PART_SIZE as u64))]
135 pub value: ReferralEntryValue,
136}
137
138impl ReferralEntry {
139 pub const COMMON_PART_SIZE: usize = std::mem::size_of::<u16>() * 2;
141}
142
143macro_rules! gen_ref_entry_val {
144 (
145 $($ver:literal,)+
146 ) => {
147 pastey::paste! {
148 #[binrw::binrw]
149 #[derive(Debug, PartialEq, Eq)]
150 #[br(import(version: u16))]
151 pub enum ReferralEntryValue {
152 $(
153 #[doc = concat!("A DFS referral version", stringify!($ver), "Entry")]
154 #[br(pre_assert(version == $ver))]
155 [<V $ver>]([<ReferralEntryValueV $ver>]),
156 )+
157 }
158
159 impl ReferralEntryValue {
160 fn get_version(&self) -> u16 {
161 match self {
162 $(
163 Self::[<V $ver>](_) => $ver,
164 )+
165 }
166 }
167 }
168 }
169 };
170}
171
172gen_ref_entry_val!(1, 2, 3, 4,);
173
174#[binrw::binrw]
175#[derive(Debug, PartialEq, Eq)]
176pub struct ReferralEntryValueV1 {
177 pub server_type: DfsServerType,
179 #[bw(calc = 0)]
180 _referral_entry_flags: u16,
181 pub share_name: NullWideString,
183}
184
185#[binrw::binrw]
187#[derive(Debug, PartialEq, Eq)]
188#[brw(repr(u16))]
189pub enum DfsServerType {
190 NonRoot = 0x0,
192 Root = 0x1,
194}
195
196#[binrw::binrw]
199#[derive(Debug, PartialEq, Eq)]
200pub struct ReferralEntryValueV2 {
201 #[bw(calc = PosMarker::default())]
202 _start: PosMarker<()>,
203 pub server_type: DfsServerType,
205 #[bw(calc = 0)]
206 _referral_entry_flags: u16,
207 #[bw(calc = 0)]
208 _proximity: u32,
209
210 pub time_to_live: u32,
212 #[br(assert(dfs_path_offset.value >= ReferralEntry::COMMON_PART_SIZE as u16))]
213 #[bw(calc = PosMarker::default())]
214 dfs_path_offset: PosMarker<u16>,
215 #[br(assert(dfs_alternate_path_offset.value >= ReferralEntry::COMMON_PART_SIZE as u16))]
216 #[bw(calc = PosMarker::default())]
217 dfs_alternate_path_offset: PosMarker<u16>,
218 #[br(assert(network_address_offset.value >= ReferralEntry::COMMON_PART_SIZE as u16))]
219 #[bw(calc = PosMarker::default())]
220 network_address_offset: PosMarker<u16>,
221
222 #[bw(calc = PosMarker::default())]
223 _restore_position: PosMarker<()>,
224
225 #[br(seek_before = _start.seek_from((dfs_path_offset.value as usize - ReferralEntry::COMMON_PART_SIZE).try_into().unwrap()))]
227 #[bw(write_with = PosMarker::write_roff_b_plus, args(&dfs_path_offset, &_start, ReferralEntry::COMMON_PART_SIZE as u64))]
228 pub dfs_path: NullWideString,
229 #[br(seek_before = _start.seek_from((dfs_alternate_path_offset.value as usize - ReferralEntry::COMMON_PART_SIZE).try_into().unwrap()))]
231 #[bw(write_with = PosMarker::write_roff_b_plus, args(&dfs_alternate_path_offset, &_start, ReferralEntry::COMMON_PART_SIZE as u64))]
232 pub dfs_alternate_path: NullWideString,
233 #[br(seek_before = _start.seek_from((network_address_offset.value as usize - ReferralEntry::COMMON_PART_SIZE).try_into().unwrap()))]
235 #[bw(write_with = PosMarker::write_roff_b_plus, args(&network_address_offset, &_start, ReferralEntry::COMMON_PART_SIZE as u64))]
236 pub network_address: NullWideString,
237
238 #[br(seek_before = _restore_position.seek_from(0))]
239 #[bw(calc = ())]
240 __: (),
241}
242
243#[binrw::binrw]
244#[derive(Debug, PartialEq, Eq)]
245pub struct ReferralEntryValueV3 {
246 pub server_type: DfsServerType,
248 pub referral_entry_flags: ReferralEntryFlags,
249 pub time_to_live: u32,
251 #[br(args(referral_entry_flags))]
252 pub value: EntryV3Value,
253}
254
255impl ReferralEntryValueV3 {
256 pub const COMMON_PART_SIZE: usize = std::mem::size_of::<u16>() * 2 + std::mem::size_of::<u32>();
258}
259
260#[bitfield]
261#[derive(BinWrite, BinRead, Debug, Default, Clone, Copy, PartialEq, Eq)]
262#[bw(map = |&x| Self::into_bytes(x))]
263#[br(map = Self::from_bytes)]
264pub struct ReferralEntryFlags {
265 #[skip]
266 __: bool,
267 pub name_list_referral: bool,
268 #[skip]
269 __: B14,
270}
271
272#[binrw::binrw]
273#[derive(Debug, PartialEq, Eq)]
274#[br(import(flags: ReferralEntryFlags))]
275pub enum EntryV3Value {
276 #[br(pre_assert(flags.name_list_referral()))]
278 DfsPath(EntryV3V4DfsPaths),
279 #[br(pre_assert(!flags.name_list_referral()))]
281 NetworkAddress(EntryV3DCRefs),
282}
283
284impl EntryV3Value {
285 const OFFSET_FROM_ENTRY_START: u16 =
290 (ReferralEntry::COMMON_PART_SIZE + ReferralEntryValueV3::COMMON_PART_SIZE) as u16;
291}
292
293#[binrw::binrw]
295#[derive(Debug, PartialEq, Eq)]
296pub struct EntryV3V4DfsPaths {
297 #[bw(calc = PosMarker::default())]
298 _start: PosMarker<()>,
299 #[br(assert(dfs_path_offset.value >= EntryV3Value::OFFSET_FROM_ENTRY_START))]
300 #[bw(calc = PosMarker::default())]
301 dfs_path_offset: PosMarker<u16>,
302 #[br(assert(dfs_alternate_path_offset.value >= EntryV3Value::OFFSET_FROM_ENTRY_START))]
303 #[bw(calc = PosMarker::default())]
304 dfs_alternate_path_offset: PosMarker<u16>,
305 #[br(assert(network_address_offset.value >= EntryV3Value::OFFSET_FROM_ENTRY_START))]
306 #[bw(calc = PosMarker::default())]
307 network_address_offset: PosMarker<u16>,
308 #[bw(calc = 0)]
309 _service_site_guid: u128,
310
311 #[bw(calc = PosMarker::default())]
312 _restore_position: PosMarker<()>,
313
314 #[br(seek_before = _start.seek_from((dfs_path_offset.value - EntryV3Value::OFFSET_FROM_ENTRY_START).into()))]
316 #[bw(write_with = PosMarker::write_roff_b_plus, args(&dfs_path_offset, &_start, ReferralEntry::COMMON_PART_SIZE as u64))]
317 pub dfs_path: NullWideString,
318 #[br(seek_before = _start.seek_from((dfs_alternate_path_offset.value - EntryV3Value::OFFSET_FROM_ENTRY_START).into()))]
320 #[bw(write_with = PosMarker::write_roff_b_plus, args(&dfs_alternate_path_offset, &_start, ReferralEntry::COMMON_PART_SIZE as u64))]
321 pub dfs_alternate_path: NullWideString,
322 #[br(seek_before = _start.seek_from((network_address_offset.value - EntryV3Value::OFFSET_FROM_ENTRY_START).into()))]
324 #[bw(write_with = PosMarker::write_roff_b_plus, args(&network_address_offset, &_start, ReferralEntry::COMMON_PART_SIZE as u64))]
325 pub network_address: NullWideString,
326
327 #[br(seek_before = _restore_position.seek_from(0))]
328 #[bw(calc = ())]
329 __: (),
330}
331
332#[binrw::binrw]
334#[derive(Debug, PartialEq, Eq)]
335pub struct EntryV3DCRefs {
336 #[bw(calc = PosMarker::default())]
337 _start: PosMarker<()>,
338 #[br(assert(special_name_offset.value >= EntryV3Value::OFFSET_FROM_ENTRY_START))]
339 #[bw(calc = PosMarker::default())]
340 special_name_offset: PosMarker<u16>,
341 number_of_expanded_names: u16,
342 #[br(assert(expanded_name_offset.value >= EntryV3Value::OFFSET_FROM_ENTRY_START))]
343 #[bw(calc = PosMarker::default())]
344 expanded_name_offset: PosMarker<u16>,
345
346 #[bw(calc = PosMarker::default())]
347 _restore_position: PosMarker<()>,
348
349 #[br(seek_before = _start.seek_from((special_name_offset.value - EntryV3Value::OFFSET_FROM_ENTRY_START).into()))]
350 pub special_name: NullWideString,
351 #[br(seek_before = _start.seek_from((expanded_name_offset.value - EntryV3Value::OFFSET_FROM_ENTRY_START).into()))]
352 #[br(count = number_of_expanded_names)]
353 pub expanded_names: Vec<NullWideString>,
354
355 #[br(seek_before = _restore_position.seek_from(0))]
356 #[bw(calc = ())]
357 __: (),
358}
359
360#[binrw::binrw]
361#[derive(Debug, PartialEq, Eq)]
362pub struct ReferralEntryValueV4 {
363 pub server_type: DfsServerType,
365 #[br(assert((referral_entry_flags & !u16::from_le_bytes(ReferralEntryFlagsV4::new().with_target_set_boundary(true).into_bytes())) == 0))]
367 pub referral_entry_flags: u16,
368 pub time_to_live: u32,
370 pub refs: EntryV3V4DfsPaths,
372}
373
374#[bitfield]
376#[derive(BinWrite, BinRead, Debug, Default, Clone, Copy, PartialEq, Eq)]
377#[bw(map = |&x| Self::into_bytes(x))]
378#[br(map = Self::from_bytes)]
379struct ReferralEntryFlagsV4 {
380 #[skip]
381 __: B2,
382 #[skip(getters)]
383 target_set_boundary: bool,
384 #[skip]
385 __: B13,
386}
387
388#[cfg(test)]
389mod tests {
390 use super::*;
391 use smb_tests::*;
392
393 test_binrw! {
394 struct ReqGetDfsReferral {
395 max_referral_level: ReferralLevel::V4,
396 request_file_name: r"\ADC.aviv.local\dfs\Docs".into(),
397 } => "04005c004100440043002e0061007600690076002e006c006f00630061006c005c006400660073005c0044006f00630073000000"
398 }
399
400 test_binrw_read! {
401 struct RespGetDfsReferral {
402 path_consumed: 48,
403 referral_header_flags: ReferralHeaderFlags::new().with_storage_servers(true),
404 referral_entries: vec![
405 ReferralEntry {
406 value: ReferralEntryValue::V4(ReferralEntryValueV4 {
407 server_type: DfsServerType::NonRoot,
408 referral_entry_flags: u16::from_le_bytes(
409 ReferralEntryFlagsV4::new()
410 .with_target_set_boundary(true)
411 .into_bytes()
412 ),
413 time_to_live: 1800,
414 refs: EntryV3V4DfsPaths {
415 dfs_path: r"\ADC.aviv.local\dfs\Docs".into(),
416 dfs_alternate_path: r"\ADC.aviv.local\dfs\Docs".into(),
417 network_address: r"\ADC\Shares\Docs".into()
418 }
419 })
420 },
421 ReferralEntry {
422 value: ReferralEntryValue::V4(ReferralEntryValueV4 {
423 server_type: DfsServerType::NonRoot,
424 referral_entry_flags: 0,
425 time_to_live: 1800,
426 refs: EntryV3V4DfsPaths {
427 dfs_path: r"\ADC.aviv.local\dfs\Docs".into(),
428 dfs_alternate_path: r"\ADC.aviv.local\dfs\Docs".into(),
429 network_address: r"\FSRV\Shares\MyShare".into()
430 }
431 })
432 }
433 ],
434 } => "300002000200000004002200000004000807000044007600a8000000000000000000000000000000000004002200000000000807000022005400a
435 800000000000000000000000000000000005c004100440043002e0061007600690076002e006c006f00630061006c005c006400660073005c0044006f00
436 6300730000005c004100440043002e0061007600690076002e006c006f00630061006c005c006400660073005c0044006f006300730000005c004100440
437 043005c005300680061007200650073005c0044006f006300730000005c0046005300520056005c005300680061007200650073005c004d007900530068
438 006100720065000000"
439 }
440}