1use binrw::prelude::*;
2
3use super::header::*;
4use super::*;
5
6macro_rules! make_content_impl {
8 (
9 $struct_name:ident,
10 $({$variant:ident, $struct_type:ty},)+
11 ) => {
12 pastey::paste! {
13
14impl $struct_name {
15 pub fn content_name(&self) -> &'static str {
17 use $struct_name::*;
18 match self {
19 $(
20 [<$variant>](_) => stringify!([<$variant>]),
21 )+
22 }
23 }
24
25 $(
26 #[doc = concat!("Attempts to cast the current content type to [", stringify!($struct_type),"].")]
27 pub fn [<to_ $variant:lower>](self) -> crate::Result<$struct_type> {
28 match self {
29 $struct_name::[<$variant>](req) => Ok(req),
30 _ => Err(crate::SmbMsgError::UnexpectedContent{
31 expected: stringify!([<$variant>]),
32 actual: self.content_name(),
33 }),
34 }
35 }
36
37 #[doc = concat!("Attempts to cast the current content type to [", stringify!($struct_type),"].")]
38 pub fn [<as_ $variant:lower>](&self) -> crate::Result<&$struct_type> {
39 match self {
40 $struct_name::[<$variant>](req) => Ok(req),
41 _ => Err(crate::SmbMsgError::UnexpectedContent{
42 expected: stringify!([<$variant>]),
43 actual: self.content_name(),
44 }),
45 }
46 }
47
48 #[doc = concat!("Attempts to cast the current content type to [", stringify!($struct_type),"].")]
49 pub fn [<as_mut_ $variant:lower>](&mut self) -> crate::Result<&mut $struct_type> {
50 match self {
51 $struct_name::[<$variant>](req) => Ok(req),
52 _ => Err(crate::SmbMsgError::UnexpectedContent{
53 expected: stringify!([<$variant>]),
54 actual: self.content_name(),
55 }),
56 }
57 }
58 )+
59}
60 }
61 };
62}
63
64macro_rules! make_content {
67 (
68 $({$cmd:ident, $struct_pfx:ident},)+
69 ) => {
70 pastey::paste!{
71
72#[derive(BinRead, BinWrite, Debug)]
73#[brw(import(command: &Command))]
74#[brw(little)]
75pub enum RequestContent {
76 $(
77 #[br(pre_assert(matches!(command, Command::$cmd)))]
78 $cmd($struct_pfx::[<$cmd Request>]),
79 )*
80
81 #[br(pre_assert(matches!(command, Command::Cancel)))]
83 Cancel(cancel::CancelRequest),
84
85 #[br(pre_assert(matches!(command, Command::OplockBreak)))]
87 OplockBreakAck(oplock::OplockBreakAck),
88 #[br(pre_assert(matches!(command, Command::OplockBreak)))]
89 LeaseBreakAck(oplock::LeaseBreakAck),
90}
91
92#[derive(BinRead, BinWrite, Debug)]
93#[brw(import(command: &Command))]
94#[brw(little)]
95pub enum ResponseContent {
96 $(
97 #[br(pre_assert(matches!(command, Command::$cmd)))]
98 $cmd($struct_pfx::[<$cmd Response>]),
99 )*
100
101 #[br(pre_assert(matches!(command, Command::OplockBreak)))]
102 OplockBreakNotify(oplock::OplockBreakNotify),
103 #[br(pre_assert(matches!(command, Command::OplockBreak)))]
104 LeaseBreakNotify(oplock::LeaseBreakNotify),
105 #[br(pre_assert(matches!(command, Command::OplockBreak)))]
106 OplockBreak(oplock::OplockBreakResponse),
107 #[br(pre_assert(matches!(command, Command::OplockBreak)))]
108 LeaseBreak(oplock::LeaseBreakResponse),
109
110 #[br(pre_assert(matches!(command, Command::ServerToClientNotification)))]
112 ServerToClientNotification(notify::ServerToClientNotification),
113
114 Error(error::ErrorResponse),
116}
117
118impl RequestContent {
119 pub fn associated_cmd(&self) -> Command {
121 use RequestContent::*;
122 match self {
123 $(
124 $cmd(_) => Command::$cmd,
125 )*
126
127 Cancel(_) => Command::Cancel,
128 OplockBreakAck(_)
129 | LeaseBreakAck(_) => Command::OplockBreak,
130 }
131 }
132}
133
134impl ResponseContent {
135 pub fn associated_cmd(&self) -> Command {
137 use ResponseContent::*;
138 match self {
139 $(
140 $cmd(_) => Command::$cmd,
141 )*
142
143 | OplockBreakNotify(_)
144 | OplockBreak(_)
145 | LeaseBreakNotify(_)
146 | LeaseBreak(_) => Command::OplockBreak,
147 ServerToClientNotification(_) => Command::ServerToClientNotification,
148 Error(_) => panic!("Error has no matching command!"),
149 }
150 }
151}
152
153$(
158 impl From<$struct_pfx::[<$cmd Request>]>
159 for RequestContent
160 {
161 fn from(req: $struct_pfx::[<$cmd Request>]) -> Self {
162 RequestContent::$cmd(req)
163 }
164 }
165 impl From<$struct_pfx::[<$cmd Response>]>
166 for ResponseContent
167 {
168 fn from(resp: $struct_pfx::[<$cmd Response>]) -> Self {
169 ResponseContent::$cmd(resp)
170 }
171 }
172)+
173
174impl From<cancel::CancelRequest>
175 for RequestContent
176{
177 fn from(req: cancel::CancelRequest) -> Self {
178 RequestContent::Cancel(req)
179 }
180}
181
182make_content_impl!{
183 RequestContent,
184 $(
185 {$cmd, $struct_pfx::[<$cmd Request>]},
186 )+
187 {Cancel, cancel::CancelRequest},
188 {OplockBreakAck, oplock::OplockBreakAck},
189 {LeaseBreakAck, oplock::LeaseBreakAck},
190}
191
192make_content_impl!{
193 ResponseContent,
194 $(
195 {$cmd, $struct_pfx::[<$cmd Response>]},
196 )+
197 {OplockBreakNotify, oplock::OplockBreakNotify},
198 {LeaseBreakNotify, oplock::LeaseBreakNotify},
199 {OplockBreak, oplock::OplockBreakResponse},
200 {LeaseBreak, oplock::LeaseBreakResponse},
201 {ServerToClientNotification, notify::ServerToClientNotification},
202 {Error, error::ErrorResponse},
203}
204 }
205 };
206}
207
208make_content!(
209 {Negotiate, negotiate},
210 {SessionSetup, session_setup},
211 {Logoff, session_setup},
212 {TreeConnect, tree_connect},
213 {TreeDisconnect, tree_connect},
214 {Create, create},
215 {Close, create},
216 {Flush, file},
217 {Read, file},
218 {Write, file},
219 {Lock, lock},
220 {Ioctl, ioctl},
221 {Echo, echo},
222 {QueryDirectory, query_dir},
223 {ChangeNotify, notify},
224 {QueryInfo, info},
225 {SetInfo, info},
226);
227
228impl RequestContent {
229 pub fn req_payload_size(&self) -> u32 {
234 use RequestContent::*;
235 match self {
236 Write(req) => req.length,
238 Ioctl(req) => req.buffer.get_size() + req.max_output_response,
240 _ => 0,
241 }
242 }
243 pub fn expected_resp_size(&self) -> u32 {
248 use RequestContent::*;
249 match self {
250 Read(req) => req.length,
252 QueryDirectory(req) => req.output_buffer_length,
254 Ioctl(req) => req.max_input_response + req.max_output_response,
256 _ => 0,
257 }
258 }
259}
260
261macro_rules! make_plain {
262 ($suffix:ident, $server_to_redir:literal, $binrw_attr:ty) => {
263 pastey::paste! {
264
265 #[$binrw_attr]
267 #[derive(Debug)]
268 #[brw(little)]
269 pub struct [<Plain $suffix>] {
270 #[brw(assert(header.flags.server_to_redir() == $server_to_redir))]
271 pub header: Header,
272 #[brw(args(&header.command))]
273 pub content: [<$suffix Content>],
274 }
275
276 impl [<Plain $suffix>] {
277 pub fn new(content: [<$suffix Content>]) -> [<Plain $suffix>] {
278 [<Plain $suffix>] {
279 header: Header {
281 credit_charge: 0,
282 status: Status::Success as u32,
283 command: content.associated_cmd(),
284 credit_request: 0,
285 flags: HeaderFlags::new(),
286 next_command: 0,
287 message_id: u64::MAX,
288 tree_id: Some(0),
289 async_id: None,
290 session_id: 0,
291 signature: 0,
292 },
293 content,
294 }
295 }
296 }
297 }
298 };
299}
300
301make_plain!(Request, false, binrw::binwrite);
302make_plain!(Response, true, binrw::binread);
303
304#[cfg(test)]
306pub mod tests {
307 use std::io::Cursor;
308
309 use super::*;
310
311 pub fn encode_content(content: RequestContent) -> Vec<u8> {
316 let mut cursor = Cursor::new(Vec::new());
317 let msg = PlainRequest::new(content);
318 msg.write(&mut cursor).unwrap();
319 let bytes_of_msg = cursor.into_inner();
320 bytes_of_msg[Header::STRUCT_SIZE..].to_vec()
322 }
323
324 pub fn decode_content(bytes: &[u8]) -> PlainResponse {
325 let mut cursor = Cursor::new(bytes);
326 cursor.read_le().unwrap()
327 }
328}