smb_msg/
notify.rs

1//! SMB2 Change Notify Request and Response, and Server to Client Notification
2use 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, PartialEq, Eq)]
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    #[bw(if(!buffer.is_empty()))]
91    #[bw(write_with = PosMarker::write_aoff_size, args(&_output_buffer_offset, &_output_buffer_length))]
92    pub buffer: ChainedItemList<FileNotifyInformation, 4>,
93}
94
95#[binrw::binrw]
96#[derive(Debug, PartialEq, Eq)]
97pub struct ServerToClientNotification {
98    structure_size: u16,
99    #[bw(calc = 0)]
100    _reserved: u16,
101    #[bw(calc = notification.get_type())]
102    notification_type: NotificationType,
103    #[br(args(notification_type))]
104    pub notification: Notification,
105}
106
107#[binrw::binrw]
108#[derive(Debug, PartialEq, Eq)]
109#[brw(repr(u32))]
110pub enum NotificationType {
111    NotifySessionClosed = 0,
112}
113
114#[binrw::binrw]
115#[derive(Debug, PartialEq, Eq)]
116#[br(import(notification_type: NotificationType))]
117pub enum Notification {
118    #[br(pre_assert(notification_type == NotificationType::NotifySessionClosed))]
119    NotifySessionClosed(NotifySessionClosed),
120}
121
122impl Notification {
123    pub fn get_type(&self) -> NotificationType {
124        match self {
125            Notification::NotifySessionClosed(_) => NotificationType::NotifySessionClosed,
126        }
127    }
128}
129
130#[binrw::binrw]
131#[derive(Debug, PartialEq, Eq)]
132pub struct NotifySessionClosed {
133    #[bw(calc = 0)]
134    _reserved: u32,
135}
136
137#[cfg(test)]
138mod tests {
139    use crate::*;
140    use smb_dtyp::guid::Guid;
141    use smb_tests::*;
142
143    use super::*;
144
145    test_binrw! {
146        struct 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        } => "2000000000080000d10500000c000000190000000c0000001700000000000000"
159    }
160
161    test_binrw! {
162        struct ChangeNotifyResponse => pending {
163            buffer: Default::default(),
164        } => "0900000000000000"
165    }
166
167    test_response! {
168        change_notify_with_data: ChangeNotify {
169            buffer: vec![
170                FileNotifyInformation {
171                    action: NotifyAction::RenamedOldName,
172                    file_name: "New folder".into()
173                },
174                FileNotifyInformation {
175                    action: NotifyAction::RenamedNewName,
176                    file_name: "jdsa".into()
177                }
178            ]
179            .into()
180        } => "09004800340000002000000004000000140000004e0065007700200066006f006c006400650072000000000005000000080000006a00640073006100"
181    }
182
183    test_response_read! {
184        change_notify_azure: ChangeNotify {
185            buffer: vec![
186                FileNotifyInformation {
187                    action: NotifyAction::Added,
188                    file_name: "11.txt".into()
189                },
190                FileNotifyInformation {
191                    action: NotifyAction::Added,
192                    file_name: "kernel.bin.til".into()
193                },
194                FileNotifyInformation {
195                    action: NotifyAction::Added,
196                    file_name: "ec2-3-70-222-69.eu-central-1.compute.amazonaws.com.rdp".into()
197                },
198                FileNotifyInformation {
199                    action: NotifyAction::Added,
200                    file_name: "ec2-18-198-51-98.eu-central-1.compute.amazonaws.com.rdp".into()
201                },
202                FileNotifyInformation {
203                    action: NotifyAction::Added,
204                    file_name: "Test DC.rdp".into()
205                }
206            ]
207            .into()
208        } => "090048006001000018000000010000000c000000310031002e0074007800740028000000010000001c0000006b00650072006e0065006c002e00620069006e002e00740069006c0078000000010000006c0000006500630032002d0033002d00370030002d003200320032002d00360039002e00650075002d00630065006e007400720061006c002d0031002e0063006f006d0070007500740065002e0061006d0061007a006f006e006100770073002e0063006f006d002e0072006400700080000000010000006e0000006500630032002d00310038002d003100390038002d00350031002d00390038002e00650075002d00630065006e007400720061006c002d0031002e0063006f006d0070007500740065002e0061006d0061007a006f006e006100770073002e0063006f006d002e007200640070006f557361676500000000010000001600000054006500730074002000440043002e00720064007000726e65744567"
209    }
210}