1use std::io::SeekFrom;
3
4use binrw::io::TakeSeekExt;
5use binrw::prelude::*;
6use modular_bitfield::prelude::*;
7
8use super::FileId;
9use smb_dtyp::binrw_util::prelude::*;
10use smb_fscc::*;
11
12#[binrw::binrw]
13#[derive(Debug)]
14pub struct ChangeNotifyRequest {
15 #[bw(calc = 32)]
16 #[br(assert(_structure_size == 32))]
17 _structure_size: u16,
18 pub flags: NotifyFlags,
19 pub output_buffer_length: u32,
20 pub file_id: FileId,
21 pub completion_filter: NotifyFilter,
22 #[bw(calc = 0)]
23 _reserved: u32,
24}
25
26#[bitfield]
27#[derive(BinWrite, BinRead, Debug, Default, Clone, Copy, PartialEq, Eq)]
28#[bw(map = |&x| Self::into_bytes(x))]
29#[br(map = Self::from_bytes)]
30pub struct NotifyFlags {
31 pub watch_tree: bool,
32 #[skip]
33 __: B15,
34}
35
36#[bitfield]
37#[derive(BinWrite, BinRead, Debug, Default, Clone, Copy, PartialEq, Eq)]
38#[bw(map = |&x| Self::into_bytes(x))]
39#[br(map = Self::from_bytes)]
40pub struct NotifyFilter {
41 pub file_name: bool,
42 pub dir_name: bool,
43 pub attributes: bool,
44 pub size: bool,
45
46 pub last_write: bool,
47 pub last_access: bool,
48 pub creation: bool,
49 pub ea: bool,
50
51 pub security: bool,
52 pub stream_name: bool,
53 pub stream_size: bool,
54 pub stream_write: bool,
55
56 #[skip]
57 __: B20,
58}
59
60impl NotifyFilter {
61 pub fn all() -> Self {
62 Self::new()
63 .with_file_name(true)
64 .with_dir_name(true)
65 .with_attributes(true)
66 .with_size(true)
67 .with_last_write(true)
68 .with_last_access(true)
69 .with_creation(true)
70 .with_ea(true)
71 .with_security(true)
72 .with_stream_name(true)
73 .with_stream_size(true)
74 .with_stream_write(true)
75 }
76}
77
78#[binrw::binrw]
79#[derive(Debug, PartialEq, Eq)]
80pub struct ChangeNotifyResponse {
81 #[bw(calc = 9)]
82 #[br(assert(_structure_size == 9))]
83 _structure_size: u16,
84 #[bw(calc = PosMarker::default())]
85 _output_buffer_offset: PosMarker<u16>,
86 #[bw(calc = PosMarker::default())]
87 _output_buffer_length: PosMarker<u32>,
88 #[br(seek_before = SeekFrom::Start(_output_buffer_offset.value.into()))]
89 #[br(map_stream = |s| s.take_seek(_output_buffer_length.value.into()))]
90 pub buffer: ChainedItemList<FileNotifyInformation, 4>,
91}
92
93#[binrw::binrw]
94#[derive(Debug, PartialEq, Eq)]
95pub struct ServerToClientNotification {
96 structure_size: u16,
97 #[bw(calc = 0)]
98 _reserved: u16,
99 #[bw(calc = notification.get_type())]
100 notification_type: NotificationType,
101 #[br(args(notification_type))]
102 pub notification: Notification,
103}
104
105#[binrw::binrw]
106#[derive(Debug, PartialEq, Eq)]
107#[brw(repr(u32))]
108pub enum NotificationType {
109 NotifySessionClosed = 0,
110}
111
112#[binrw::binrw]
113#[derive(Debug, PartialEq, Eq)]
114#[br(import(notification_type: NotificationType))]
115pub enum Notification {
116 #[br(pre_assert(notification_type == NotificationType::NotifySessionClosed))]
117 NotifySessionClosed(NotifySessionClosed),
118}
119
120impl Notification {
121 pub fn get_type(&self) -> NotificationType {
122 match self {
123 Notification::NotifySessionClosed(_) => NotificationType::NotifySessionClosed,
124 }
125 }
126}
127
128#[binrw::binrw]
129#[derive(Debug, PartialEq, Eq)]
130pub struct NotifySessionClosed {
131 #[bw(calc = 0)]
132 _reserved: u32,
133}
134
135#[cfg(test)]
136mod tests {
137 use std::io::Cursor;
138
139 use crate::*;
140 use smb_dtyp::guid::Guid;
141
142 use super::*;
143
144 #[test]
145 pub fn change_notify_request_write() {
146 let request = ChangeNotifyRequest {
147 flags: NotifyFlags::new(),
148 output_buffer_length: 2048,
149 file_id: "000005d1-000c-0000-1900-00000c000000"
150 .parse::<Guid>()
151 .unwrap()
152 .into(),
153 completion_filter: NotifyFilter::new()
154 .with_file_name(true)
155 .with_dir_name(true)
156 .with_attributes(true)
157 .with_last_write(true),
158 };
159
160 let mut cursor = Cursor::new(Vec::new());
161 request.write_le(&mut cursor).unwrap();
162 assert_eq!(
163 cursor.into_inner(),
164 [
165 0x20, 0x0, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0xd1, 0x5, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0,
166 0x19, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0x17, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0
167 ]
168 );
169 }
170
171 #[test]
172 pub fn test_change_notify_response_pending_parse() {
173 let data = [0x9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0];
174 let response = ChangeNotifyResponse::read_le(&mut Cursor::new(&data)).unwrap();
175 assert_eq!(
176 response,
177 ChangeNotifyResponse {
178 buffer: Default::default()
179 }
180 );
181 }
182
183 #[test]
184 pub fn test_change_notify_response_with_data_parse() {
185 let data = [
186 0xfe, 0x53, 0x4d, 0x42, 0x40, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf, 0x0, 0x0, 0x0,
187 0x33, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x56, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1,
188 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x25, 0x0, 0x0, 0x0, 0x0, 0x38, 0x0, 0x0, 0x0, 0x0,
189 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9, 0x0, 0x48,
190 0x0, 0x34, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x14, 0x0, 0x0, 0x0,
191 0x4e, 0x0, 0x65, 0x0, 0x77, 0x0, 0x20, 0x0, 0x66, 0x0, 0x6f, 0x0, 0x6c, 0x0, 0x64, 0x0,
192 0x65, 0x0, 0x72, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x6a,
193 0x0, 0x64, 0x0, 0x73, 0x0, 0x61, 0x0,
194 ];
195
196 let parsed = decode_content(&data);
197 let notify_response = parsed.content.to_changenotify().unwrap();
198
199 assert_eq!(
200 notify_response,
201 ChangeNotifyResponse {
202 buffer: vec![
203 FileNotifyInformation {
204 action: NotifyAction::RenamedOldName,
205 file_name: "New folder".into()
206 },
207 FileNotifyInformation {
208 action: NotifyAction::RenamedNewName,
209 file_name: "jdsa".into()
210 }
211 ]
212 .into()
213 }
214 );
215 }
216
217 #[test]
218 pub fn test_change_notify_response_azure_files() {
219 let data = [
220 0xfe, 0x53, 0x4d, 0x42, 0x40, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00,
221 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
222 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5d, 0x00,
223 0x00, 0x68, 0x6c, 0x30, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
224 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x48, 0x00, 0x60, 0x01,
225 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
226 0x31, 0x00, 0x31, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x78, 0x00, 0x74, 0x00, 0x28, 0x00,
227 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x6b, 0x00, 0x65, 0x00,
228 0x72, 0x00, 0x6e, 0x00, 0x65, 0x00, 0x6c, 0x00, 0x2e, 0x00, 0x62, 0x00, 0x69, 0x00,
229 0x6e, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x69, 0x00, 0x6c, 0x00, 0x78, 0x00, 0x00, 0x00,
230 0x01, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x65, 0x00, 0x63, 0x00, 0x32, 0x00,
231 0x2d, 0x00, 0x33, 0x00, 0x2d, 0x00, 0x37, 0x00, 0x30, 0x00, 0x2d, 0x00, 0x32, 0x00,
232 0x32, 0x00, 0x32, 0x00, 0x2d, 0x00, 0x36, 0x00, 0x39, 0x00, 0x2e, 0x00, 0x65, 0x00,
233 0x75, 0x00, 0x2d, 0x00, 0x63, 0x00, 0x65, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x72, 0x00,
234 0x61, 0x00, 0x6c, 0x00, 0x2d, 0x00, 0x31, 0x00, 0x2e, 0x00, 0x63, 0x00, 0x6f, 0x00,
235 0x6d, 0x00, 0x70, 0x00, 0x75, 0x00, 0x74, 0x00, 0x65, 0x00, 0x2e, 0x00, 0x61, 0x00,
236 0x6d, 0x00, 0x61, 0x00, 0x7a, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x61, 0x00, 0x77, 0x00,
237 0x73, 0x00, 0x2e, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x2e, 0x00, 0x72, 0x00,
238 0x64, 0x00, 0x70, 0x00, 0x80, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x6e, 0x00,
239 0x00, 0x00, 0x65, 0x00, 0x63, 0x00, 0x32, 0x00, 0x2d, 0x00, 0x31, 0x00, 0x38, 0x00,
240 0x2d, 0x00, 0x31, 0x00, 0x39, 0x00, 0x38, 0x00, 0x2d, 0x00, 0x35, 0x00, 0x31, 0x00,
241 0x2d, 0x00, 0x39, 0x00, 0x38, 0x00, 0x2e, 0x00, 0x65, 0x00, 0x75, 0x00, 0x2d, 0x00,
242 0x63, 0x00, 0x65, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x72, 0x00, 0x61, 0x00, 0x6c, 0x00,
243 0x2d, 0x00, 0x31, 0x00, 0x2e, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x70, 0x00,
244 0x75, 0x00, 0x74, 0x00, 0x65, 0x00, 0x2e, 0x00, 0x61, 0x00, 0x6d, 0x00, 0x61, 0x00,
245 0x7a, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x61, 0x00, 0x77, 0x00, 0x73, 0x00, 0x2e, 0x00,
246 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x2e, 0x00, 0x72, 0x00, 0x64, 0x00, 0x70, 0x00,
247 0x6f, 0x55, 0x73, 0x61, 0x67, 0x65, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
248 0x16, 0x00, 0x00, 0x00, 0x54, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00, 0x20, 0x00,
249 0x44, 0x00, 0x43, 0x00, 0x2e, 0x00, 0x72, 0x00, 0x64, 0x00, 0x70, 0x00, 0x72, 0x6e,
250 0x65, 0x74, 0x45, 0x67,
251 ];
252 let parsed = decode_content(&data);
253 let notify_response = parsed.content.to_changenotify().unwrap();
254 assert_eq!(
255 notify_response,
256 ChangeNotifyResponse {
257 buffer: vec![
258 FileNotifyInformation {
259 action: NotifyAction::Added,
260 file_name: "11.txt".into()
261 },
262 FileNotifyInformation {
263 action: NotifyAction::Added,
264 file_name: "kernel.bin.til".into()
265 },
266 FileNotifyInformation {
267 action: NotifyAction::Added,
268 file_name: "ec2-3-70-222-69.eu-central-1.compute.amazonaws.com.rdp".into()
269 },
270 FileNotifyInformation {
271 action: NotifyAction::Added,
272 file_name: "ec2-18-198-51-98.eu-central-1.compute.amazonaws.com.rdp".into()
273 },
274 FileNotifyInformation {
275 action: NotifyAction::Added,
276 file_name: "Test DC.rdp".into()
277 }
278 ]
279 .into()
280 }
281 );
282 }
283}