1#[cfg(feature = "server")]
6use binrw::io::TakeSeekExt;
7use binrw::{NullWideString, prelude::*};
8use modular_bitfield::prelude::*;
9use smb_dtyp::binrw_util::prelude::*;
10use smb_msg_derive::smb_request_binrw;
11
12#[smb_request_binrw]
15pub struct ReqGetDfsReferral {
16 pub max_referral_level: ReferralLevel,
18 pub request_file_name: NullWideString,
20}
21
22#[smb_request_binrw]
25#[brw(repr(u16))]
26pub enum ReferralLevel {
27 V1 = 1,
29 V2 = 2,
31 V3 = 3,
33 V4 = 4,
35}
36
37#[smb_request_binrw]
38pub struct ReqGetDfsReferralEx {
39 pub max_referral_level: u16,
41 pub request_flags: DfsRequestFlags,
42 request_data_length: PosMarker<u32>,
43 #[bw(write_with = PosMarker::write_size, args(request_data_length))]
44 #[br(map_stream = |s| s.take_seek(request_data_length.value as u64))]
45 pub request_data: DfsRequestData,
46}
47
48#[smb_dtyp::mbitfield]
49pub struct DfsRequestFlags {
50 pub site_name: bool,
52 #[skip]
53 __: B15,
54}
55
56#[smb_request_binrw]
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]
82#[derive(Debug, PartialEq, Eq)]
83pub struct RespGetDfsReferral {
84 pub path_consumed: u16,
85 #[bw(try_calc = referral_entries.len().try_into())]
86 #[br(temp)]
87 number_of_referrals: u16,
88 pub referral_header_flags: ReferralHeaderFlags,
89 #[br(count = number_of_referrals)]
90 pub referral_entries: Vec<ReferralEntry>,
91 }
93
94impl BinWrite for RespGetDfsReferral {
95 type Args<'a> = ();
96
97 fn write_options<W: binrw::io::Write + binrw::io::Seek>(
98 &self,
99 _writer: &mut W,
100 _endian: binrw::Endian,
101 _args: Self::Args<'_>,
102 ) -> binrw::BinResult<()> {
103 unimplemented!(
104 "Placeholder trait implementation for RespGetDfsReferral - writing is currently not supported"
105 );
106 }
107}
108
109#[smb_dtyp::mbitfield]
110pub struct ReferralHeaderFlags {
111 pub referral_servers: bool,
113 pub storage_servers: bool,
115 pub target_failbacl: bool,
117 #[skip]
118 __: B29,
119}
120
121#[binrw::binrw]
122#[derive(Debug, PartialEq, Eq)]
123pub struct ReferralEntry {
124 #[bw(calc = value.get_version())]
126 pub version: u16,
127 #[bw(calc = PosMarker::default())]
128 #[br(temp)]
129 _size: PosMarker<u16>,
130
131 #[br(args(version))]
132 #[bw(write_with = PosMarker::write_size_plus, args(&_size, Self::COMMON_PART_SIZE as u64))]
134 pub value: ReferralEntryValue,
135}
136
137impl ReferralEntry {
138 pub const COMMON_PART_SIZE: usize = std::mem::size_of::<u16>() * 2;
140}
141
142macro_rules! gen_ref_entry_val {
143 (
144 $($ver:literal,)+
145 ) => {
146 pastey::paste! {
147 #[binrw::binrw]
148 #[derive(Debug, PartialEq, Eq)]
149 #[br(import(version: u16))]
150 pub enum ReferralEntryValue {
151 $(
152 #[doc = concat!("A DFS referral version", stringify!($ver), "Entry")]
153 #[br(pre_assert(version == $ver))]
154 [<V $ver>]([<ReferralEntryValueV $ver>]),
155 )+
156 }
157
158 impl ReferralEntryValue {
159 fn get_version(&self) -> u16 {
160 match self {
161 $(
162 Self::[<V $ver>](_) => $ver,
163 )+
164 }
165 }
166 }
167 }
168 };
169}
170
171gen_ref_entry_val!(1, 2, 3, 4,);
172
173#[binrw::binrw]
174#[derive(Debug, PartialEq, Eq)]
175pub struct ReferralEntryValueV1 {
176 pub server_type: DfsServerType,
178 #[bw(calc = 0)]
179 _referral_entry_flags: u16,
180 pub share_name: NullWideString,
182}
183
184#[binrw::binrw]
186#[derive(Debug, PartialEq, Eq)]
187#[brw(repr(u16))]
188pub enum DfsServerType {
189 NonRoot = 0x0,
191 Root = 0x1,
193}
194
195#[binrw::binrw]
198#[derive(Debug, PartialEq, Eq)]
199pub struct ReferralEntryValueV2 {
200 #[bw(calc = PosMarker::default())]
201 #[br(temp)]
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 #[br(temp)]
215 dfs_path_offset: PosMarker<u16>,
216 #[br(assert(dfs_alternate_path_offset.value >= ReferralEntry::COMMON_PART_SIZE as u16))]
217 #[bw(calc = PosMarker::default())]
218 #[br(temp)]
219 dfs_alternate_path_offset: PosMarker<u16>,
220 #[br(assert(network_address_offset.value >= ReferralEntry::COMMON_PART_SIZE as u16))]
221 #[bw(calc = PosMarker::default())]
222 #[br(temp)]
223 network_address_offset: PosMarker<u16>,
224
225 #[bw(calc = PosMarker::default())]
226 #[br(temp)]
227 _restore_position: PosMarker<()>,
228
229 #[br(seek_before = _start.seek_from((dfs_path_offset.value as usize - ReferralEntry::COMMON_PART_SIZE).try_into().unwrap()))]
231 #[bw(write_with = PosMarker::write_roff_b_plus, args(&dfs_path_offset, &_start, ReferralEntry::COMMON_PART_SIZE as u64))]
232 pub dfs_path: NullWideString,
233 #[br(seek_before = _start.seek_from((dfs_alternate_path_offset.value as usize - ReferralEntry::COMMON_PART_SIZE).try_into().unwrap()))]
235 #[bw(write_with = PosMarker::write_roff_b_plus, args(&dfs_alternate_path_offset, &_start, ReferralEntry::COMMON_PART_SIZE as u64))]
236 pub dfs_alternate_path: NullWideString,
237 #[br(seek_before = _start.seek_from((network_address_offset.value as usize - ReferralEntry::COMMON_PART_SIZE).try_into().unwrap()))]
239 #[bw(write_with = PosMarker::write_roff_b_plus, args(&network_address_offset, &_start, ReferralEntry::COMMON_PART_SIZE as u64))]
240 pub network_address: NullWideString,
241
242 #[br(seek_before = _restore_position.seek_from(0))]
243 #[bw(calc = ())]
244 __: (),
245}
246
247#[binrw::binrw]
248#[derive(Debug, PartialEq, Eq)]
249pub struct ReferralEntryValueV3 {
250 pub server_type: DfsServerType,
252 pub referral_entry_flags: ReferralEntryFlags,
253 pub time_to_live: u32,
255 #[br(args(referral_entry_flags))]
256 pub value: EntryV3Value,
257}
258
259impl ReferralEntryValueV3 {
260 pub const COMMON_PART_SIZE: usize = std::mem::size_of::<u16>() * 2 + std::mem::size_of::<u32>();
262}
263
264#[smb_dtyp::mbitfield]
265pub struct ReferralEntryFlags {
266 #[skip]
267 __: bool,
268 pub name_list_referral: bool,
269 #[skip]
270 __: B14,
271}
272
273#[binrw::binrw]
274#[derive(Debug, PartialEq, Eq)]
275#[br(import(flags: ReferralEntryFlags))]
276pub enum EntryV3Value {
277 #[br(pre_assert(flags.name_list_referral()))]
279 DfsPath(EntryV3V4DfsPaths),
280 #[br(pre_assert(!flags.name_list_referral()))]
282 NetworkAddress(EntryV3DCRefs),
283}
284
285impl EntryV3Value {
286 const OFFSET_FROM_ENTRY_START: u16 =
291 (ReferralEntry::COMMON_PART_SIZE + ReferralEntryValueV3::COMMON_PART_SIZE) as u16;
292}
293
294#[binrw::binrw]
296#[derive(Debug, PartialEq, Eq)]
297pub struct EntryV3V4DfsPaths {
298 #[bw(calc = PosMarker::default())]
299 #[br(temp)]
300 _start: PosMarker<()>,
301 #[br(assert(dfs_path_offset.value >= EntryV3Value::OFFSET_FROM_ENTRY_START))]
302 #[bw(calc = PosMarker::default())]
303 #[br(temp)]
304 dfs_path_offset: PosMarker<u16>,
305 #[br(assert(dfs_alternate_path_offset.value >= EntryV3Value::OFFSET_FROM_ENTRY_START))]
306 #[bw(calc = PosMarker::default())]
307 #[br(temp)]
308 dfs_alternate_path_offset: PosMarker<u16>,
309 #[br(assert(network_address_offset.value >= EntryV3Value::OFFSET_FROM_ENTRY_START))]
310 #[bw(calc = PosMarker::default())]
311 #[br(temp)]
312 network_address_offset: PosMarker<u16>,
313 #[bw(calc = 0)]
314 _service_site_guid: u128,
315
316 #[bw(calc = PosMarker::default())]
317 #[br(temp)]
318 _restore_position: PosMarker<()>,
319
320 #[br(seek_before = _start.seek_from((dfs_path_offset.value - EntryV3Value::OFFSET_FROM_ENTRY_START).into()))]
322 #[bw(write_with = PosMarker::write_roff_b_plus, args(&dfs_path_offset, &_start, ReferralEntry::COMMON_PART_SIZE as u64))]
323 pub dfs_path: NullWideString,
324 #[br(seek_before = _start.seek_from((dfs_alternate_path_offset.value - EntryV3Value::OFFSET_FROM_ENTRY_START).into()))]
326 #[bw(write_with = PosMarker::write_roff_b_plus, args(&dfs_alternate_path_offset, &_start, ReferralEntry::COMMON_PART_SIZE as u64))]
327 pub dfs_alternate_path: NullWideString,
328 #[br(seek_before = _start.seek_from((network_address_offset.value - EntryV3Value::OFFSET_FROM_ENTRY_START).into()))]
330 #[bw(write_with = PosMarker::write_roff_b_plus, args(&network_address_offset, &_start, ReferralEntry::COMMON_PART_SIZE as u64))]
331 pub network_address: NullWideString,
332
333 #[br(seek_before = _restore_position.seek_from(0))]
334 #[bw(calc = ())]
335 __: (),
336}
337
338#[binrw::binrw]
340#[derive(Debug, PartialEq, Eq)]
341pub struct EntryV3DCRefs {
342 #[bw(calc = PosMarker::default())]
343 #[br(temp)]
344 _start: PosMarker<()>,
345 #[br(assert(special_name_offset.value >= EntryV3Value::OFFSET_FROM_ENTRY_START))]
346 #[bw(calc = PosMarker::default())]
347 #[br(temp)]
348 special_name_offset: PosMarker<u16>,
349 number_of_expanded_names: u16,
350 #[br(assert(expanded_name_offset.value >= EntryV3Value::OFFSET_FROM_ENTRY_START))]
351 #[bw(calc = PosMarker::default())]
352 #[br(temp)]
353 expanded_name_offset: PosMarker<u16>,
354
355 #[bw(calc = PosMarker::default())]
356 #[br(temp)]
357 _restore_position: PosMarker<()>,
358
359 #[br(seek_before = _start.seek_from((special_name_offset.value - EntryV3Value::OFFSET_FROM_ENTRY_START).into()))]
360 pub special_name: NullWideString,
361 #[br(seek_before = _start.seek_from((expanded_name_offset.value - EntryV3Value::OFFSET_FROM_ENTRY_START).into()))]
362 #[br(count = number_of_expanded_names)]
363 pub expanded_names: Vec<NullWideString>,
364
365 #[br(seek_before = _restore_position.seek_from(0))]
366 #[bw(calc = ())]
367 __: (),
368}
369
370#[binrw::binrw]
371#[derive(Debug, PartialEq, Eq)]
372pub struct ReferralEntryValueV4 {
373 pub server_type: DfsServerType,
375 #[br(assert((referral_entry_flags & !u16::from_le_bytes(ReferralEntryFlagsV4::new().with_target_set_boundary(true).into_bytes())) == 0))]
377 pub referral_entry_flags: u16,
378 pub time_to_live: u32,
380 pub refs: EntryV3V4DfsPaths,
382}
383
384#[smb_dtyp::mbitfield]
386struct ReferralEntryFlagsV4 {
387 #[skip]
388 __: B2,
389 #[skip(getters)]
390 target_set_boundary: bool,
391 #[skip]
392 __: B13,
393}
394
395#[cfg(test)]
396mod tests {
397 use super::*;
398 use crate::*;
399
400 test_binrw_request! {
401 struct ReqGetDfsReferral {
402 max_referral_level: ReferralLevel::V4,
403 request_file_name: r"\ADC.aviv.local\dfs\Docs".into(),
404 } => "04005c004100440043002e0061007600690076002e006c006f00630061006c005c006400660073005c0044006f00630073000000"
405 }
406
407 #[cfg(feature = "client")]
408 smb_tests::test_binrw_read! {
409 struct RespGetDfsReferral {
410 path_consumed: 48,
411 referral_header_flags: ReferralHeaderFlags::new().with_storage_servers(true),
412 referral_entries: vec![
413 ReferralEntry {
414 value: ReferralEntryValue::V4(ReferralEntryValueV4 {
415 server_type: DfsServerType::NonRoot,
416 referral_entry_flags: u16::from_le_bytes(
417 ReferralEntryFlagsV4::new()
418 .with_target_set_boundary(true)
419 .into_bytes()
420 ),
421 time_to_live: 1800,
422 refs: EntryV3V4DfsPaths {
423 dfs_path: r"\ADC.aviv.local\dfs\Docs".into(),
424 dfs_alternate_path: r"\ADC.aviv.local\dfs\Docs".into(),
425 network_address: r"\ADC\Shares\Docs".into()
426 }
427 })
428 },
429 ReferralEntry {
430 value: ReferralEntryValue::V4(ReferralEntryValueV4 {
431 server_type: DfsServerType::NonRoot,
432 referral_entry_flags: 0,
433 time_to_live: 1800,
434 refs: EntryV3V4DfsPaths {
435 dfs_path: r"\ADC.aviv.local\dfs\Docs".into(),
436 dfs_alternate_path: r"\ADC.aviv.local\dfs\Docs".into(),
437 network_address: r"\FSRV\Shares\MyShare".into()
438 }
439 })
440 }
441 ],
442 } => "300002000200000004002200000004000807000044007600a8000000000000000000000000000000000004002200000000000807000022005400a
443 800000000000000000000000000000000005c004100440043002e0061007600690076002e006c006f00630061006c005c006400660073005c0044006f00
444 6300730000005c004100440043002e0061007600690076002e006c006f00630061006c005c006400660073005c0044006f006300730000005c004100440
445 043005c005300680061007200650073005c0044006f006300730000005c0046005300520056005c005300680061007200650073005c004d007900530068
446 006100720065000000"
447 }
448}