smb_msg/
notify.rs

1//! Change Notify Request and Response, and Server to Client Notification messages and related types.
2
3#[cfg(feature = "client")]
4use std::io::SeekFrom;
5
6#[cfg(feature = "client")]
7use binrw::io::TakeSeekExt;
8use binrw::prelude::*;
9use modular_bitfield::prelude::*;
10use smb_msg_derive::*;
11
12use super::FileId;
13use smb_dtyp::binrw_util::prelude::*;
14use smb_fscc::*;
15
16/// SMB2 CHANGE_NOTIFY Request packet sent by the client to request change
17/// notifications on a directory. The client can monitor changes on any file
18/// or directory contained beneath the specified directory.
19///
20/// Reference: MS-SMB2 2.2.35
21#[smb_request(size = 32)]
22pub struct ChangeNotifyRequest {
23    /// Flags indicating how the operation must be processed.
24    pub flags: NotifyFlags,
25    /// Maximum number of bytes the server is allowed to return in the response.
26    pub output_buffer_length: u32,
27    /// File identifier of the directory to monitor for changes.
28    pub file_id: FileId,
29    /// Specifies the types of changes to monitor. Multiple trigger conditions can be chosen.
30    pub completion_filter: NotifyFilter,
31    reserved: u32,
32}
33
34/// Flags for SMB2 CHANGE_NOTIFY Request indicating how the operation must be processed.
35///
36/// Reference: MS-SMB2 2.2.35
37#[smb_dtyp::mbitfield]
38pub struct NotifyFlags {
39    /// The request must monitor changes on any file or directory contained
40    /// beneath the directory specified by FileId.
41    pub watch_tree: bool,
42    #[skip]
43    __: B15,
44}
45
46/// Completion filter specifying the types of changes to monitor.
47/// Multiple trigger conditions can be chosen. If any condition is met,
48/// the client is notified and the CHANGE_NOTIFY operation is completed.
49///
50/// Reference: MS-SMB2 2.2.35
51#[smb_dtyp::mbitfield]
52pub struct NotifyFilter {
53    /// Client is notified if a file name changes.
54    pub file_name: bool,
55    /// Client is notified if a directory name changes.
56    pub dir_name: bool,
57    /// Client is notified if a file's attributes change.
58    pub attributes: bool,
59    /// Client is notified if a file's size changes.
60    pub size: bool,
61
62    /// Client is notified if the last write time of a file changes.
63    pub last_write: bool,
64    /// Client is notified if the last access time of a file changes.
65    pub last_access: bool,
66    /// Client is notified if the creation time of a file changes.
67    pub creation: bool,
68    /// Client is notified if a file's extended attributes change.
69    pub ea: bool,
70
71    /// Client is notified of a file's access control list settings change.
72    pub security: bool,
73    /// Client is notified if a named stream is added to a file.
74    pub stream_name: bool,
75    /// Client is notified if the size of a named stream is changed.
76    pub stream_size: bool,
77    /// Client is notified if a named stream is modified.
78    pub stream_write: bool,
79
80    #[skip]
81    __: B20,
82}
83
84impl NotifyFilter {
85    pub fn all() -> Self {
86        Self::new()
87            .with_file_name(true)
88            .with_dir_name(true)
89            .with_attributes(true)
90            .with_size(true)
91            .with_last_write(true)
92            .with_last_access(true)
93            .with_creation(true)
94            .with_ea(true)
95            .with_security(true)
96            .with_stream_name(true)
97            .with_stream_size(true)
98            .with_stream_write(true)
99    }
100}
101
102/// SMB2 CHANGE_NOTIFY Response packet sent by the server to transmit the
103/// results of a client's SMB2 CHANGE_NOTIFY Request. Contains an array of
104/// FILE_NOTIFY_INFORMATION structures describing the changes.
105///
106/// Reference: MS-SMB2 2.2.36
107#[smb_response(size = 9)]
108pub struct ChangeNotifyResponse {
109    /// Offset in bytes from the beginning of the SMB2 header to the change information.
110    #[bw(calc = PosMarker::default())]
111    #[br(temp)]
112    _output_buffer_offset: PosMarker<u16>,
113    /// Length in bytes of the change information being returned.
114    #[bw(calc = PosMarker::default())]
115    #[br(temp)]
116    _output_buffer_length: PosMarker<u32>,
117    /// Array of FILE_NOTIFY_INFORMATION structures containing the change information.
118    #[br(seek_before = SeekFrom::Start(_output_buffer_offset.value.into()))]
119    #[br(map_stream = |s| s.take_seek(_output_buffer_length.value.into()))]
120    #[bw(if(!buffer.is_empty()))]
121    #[bw(write_with = PosMarker::write_aoff_size, args(&_output_buffer_offset, &_output_buffer_length))]
122    pub buffer: ChainedItemList<FileNotifyInformation, 4>,
123}
124
125/// SMB2 Server to Client Notification packet sent by the server to indicate
126/// an implementation-specific intent without expecting any response from the client.
127///
128/// Reference: MS-SMB2 2.2.44.1
129#[smb_response_binrw]
130pub struct ServerToClientNotification {
131    /// Size of the SMB2_SERVER_TO_CLIENT_NOTIFICATION structure.
132    structure_size: u16,
133    reserved: u16,
134    /// Valid SMB_NOTIFICATION_ID enumeration notification type value.
135    #[bw(calc = notification.get_type())]
136    notification_type: NotificationType,
137    /// Corresponding structure type based on the notification type.
138    #[br(args(notification_type))]
139    pub notification: Notification,
140}
141
142/// SMB_NOTIFICATION_ID enumeration values for server to client notifications.
143///
144/// Reference: MS-SMB2 2.2.44.1
145#[smb_response_binrw]
146#[derive(Clone, Copy)]
147#[brw(repr(u32))]
148pub enum NotificationType {
149    /// Indicates the notification structure is SMB2_NOTIFY_SESSION_CLOSED.
150    NotifySessionClosed = 0,
151}
152
153/// Notification structure containing the specific notification data
154/// based on the notification type.
155///
156/// Reference: MS-SMB2 2.2.44.1
157#[smb_response_binrw]
158#[br(import(notification_type: NotificationType))]
159pub enum Notification {
160    /// Session closed notification structure.
161    #[br(pre_assert(notification_type == NotificationType::NotifySessionClosed))]
162    NotifySessionClosed(NotifySessionClosed),
163}
164
165impl Notification {
166    pub fn get_type(&self) -> NotificationType {
167        match self {
168            Notification::NotifySessionClosed(_) => NotificationType::NotifySessionClosed,
169        }
170    }
171}
172
173/// SMB2_NOTIFY_SESSION_CLOSED structure embedded within the
174/// SMB2_SERVER_TO_CLIENT_NOTIFICATION structure when the notification
175/// type is SmbNotifySessionClosed.
176///
177/// Reference: MS-SMB2 2.2.44.2
178#[smb_response_binrw]
179pub struct NotifySessionClosed {
180    reserved: u32,
181}
182
183#[cfg(test)]
184mod tests {
185    use crate::*;
186    use smb_dtyp::guid::Guid;
187
188    use super::*;
189
190    test_binrw_request! {
191        struct ChangeNotifyRequest {
192            flags: NotifyFlags::new(),
193            output_buffer_length: 2048,
194            file_id: "000005d1-000c-0000-1900-00000c000000"
195                .parse::<Guid>()
196                .unwrap()
197                .into(),
198            completion_filter: NotifyFilter::new()
199                .with_file_name(true)
200                .with_dir_name(true)
201                .with_attributes(true)
202                .with_last_write(true),
203        } => "2000000000080000d10500000c000000190000000c0000001700000000000000"
204    }
205
206    test_binrw_response! {
207        struct ChangeNotifyResponse => pending {
208            buffer: Default::default(),
209        } => "0900000000000000"
210    }
211
212    test_response! {
213        change_notify_with_data: ChangeNotify {
214            buffer: vec![
215                FileNotifyInformation {
216                    action: NotifyAction::RenamedOldName,
217                    file_name: "New folder".into()
218                },
219                FileNotifyInformation {
220                    action: NotifyAction::RenamedNewName,
221                    file_name: "jdsa".into()
222                }
223            ]
224            .into()
225        } => "09004800340000002000000004000000140000004e0065007700200066006f006c006400650072000000000005000000080000006a00640073006100"
226    }
227
228    test_response_read! {
229        change_notify_azure: ChangeNotify {
230            buffer: vec![
231                FileNotifyInformation {
232                    action: NotifyAction::Added,
233                    file_name: "11.txt".into()
234                },
235                FileNotifyInformation {
236                    action: NotifyAction::Added,
237                    file_name: "kernel.bin.til".into()
238                },
239                FileNotifyInformation {
240                    action: NotifyAction::Added,
241                    file_name: "ec2-3-70-222-69.eu-central-1.compute.amazonaws.com.rdp".into()
242                },
243                FileNotifyInformation {
244                    action: NotifyAction::Added,
245                    file_name: "ec2-18-198-51-98.eu-central-1.compute.amazonaws.com.rdp".into()
246                },
247                FileNotifyInformation {
248                    action: NotifyAction::Added,
249                    file_name: "Test DC.rdp".into()
250                }
251            ]
252            .into()
253        } => "090048006001000018000000010000000c000000310031002e0074007800740028000000010000001c0000006b00650072006e0065006c002e00620069006e002e00740069006c0078000000010000006c0000006500630032002d0033002d00370030002d003200320032002d00360039002e00650075002d00630065006e007400720061006c002d0031002e0063006f006d0070007500740065002e0061006d0061007a006f006e006100770073002e0063006f006d002e0072006400700080000000010000006e0000006500630032002d00310038002d003100390038002d00350031002d00390038002e00650075002d00630065006e007400720061006c002d0031002e0063006f006d0070007500740065002e0061006d0061007a006f006e006100770073002e0063006f006d002e007200640070006f557361676500000000010000001600000054006500730074002000440043002e00720064007000726e65744567"
254    }
255}