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 paste::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 paste::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
174make_content_impl!{
175 RequestContent,
176 $(
177 {$cmd, $struct_pfx::[<$cmd Request>]},
178 )+
179 {Cancel, cancel::CancelRequest},
180 {OplockBreakAck, oplock::OplockBreakAck},
181 {LeaseBreakAck, oplock::LeaseBreakAck},
182}
183
184make_content_impl!{
185 ResponseContent,
186 $(
187 {$cmd, $struct_pfx::[<$cmd Response>]},
188 )+
189 {OplockBreakNotify, oplock::OplockBreakNotify},
190 {LeaseBreakNotify, oplock::LeaseBreakNotify},
191 {OplockBreak, oplock::OplockBreakResponse},
192 {LeaseBreak, oplock::LeaseBreakResponse},
193 {ServerToClientNotification, notify::ServerToClientNotification},
194 {Error, error::ErrorResponse},
195}
196 }
197 };
198}
199
200make_content!(
201 {Negotiate, negotiate},
202 {SessionSetup, session_setup},
203 {Logoff, session_setup},
204 {TreeConnect, tree_connect},
205 {TreeDisconnect, tree_connect},
206 {Create, create},
207 {Close, create},
208 {Flush, file},
209 {Read, file},
210 {Write, file},
211 {Lock, lock},
212 {Ioctl, ioctl},
213 {Echo, echo},
214 {QueryDirectory, query_dir},
215 {ChangeNotify, notify},
216 {QueryInfo, info},
217 {SetInfo, info},
218);
219
220impl RequestContent {
221 pub fn req_payload_size(&self) -> u32 {
226 use RequestContent::*;
227 match self {
228 Write(req) => req.length,
230 Ioctl(req) => req.buffer.get_size() + req.max_output_response,
232 _ => 0,
233 }
234 }
235 pub fn expected_resp_size(&self) -> u32 {
240 use RequestContent::*;
241 match self {
242 Read(req) => req.length,
244 QueryDirectory(req) => req.output_buffer_length,
246 Ioctl(req) => req.max_input_response + req.max_output_response,
248 _ => 0,
249 }
250 }
251}
252
253macro_rules! make_plain {
254 ($suffix:ident, $server_to_redir:literal, $binrw_attr:ty) => {
255 paste::paste! {
256
257 #[$binrw_attr]
259 #[derive(Debug)]
260 #[brw(little)]
261 pub struct [<Plain $suffix>] {
262 #[brw(assert(header.flags.server_to_redir() == $server_to_redir))]
263 pub header: Header,
264 #[brw(args(&header.command))]
265 pub content: [<$suffix Content>],
266 }
267
268 impl [<Plain $suffix>] {
269 pub fn new(content: [<$suffix Content>]) -> [<Plain $suffix>] {
270 [<Plain $suffix>] {
271 header: Header {
272 credit_charge: 0,
273 status: Status::Success as u32,
274 command: content.associated_cmd(),
275 credit_request: 0,
276 flags: HeaderFlags::new(),
277 next_command: 0,
278 message_id: u64::MAX,
279 tree_id: Some(0),
280 async_id: None,
281 session_id: 0,
282 signature: 0,
283 },
284 content,
285 }
286 }
287 }
288 }
289 };
290}
291
292make_plain!(Request, false, binrw::binwrite);
293make_plain!(Response, true, binrw::binread);
294
295#[cfg(test)]
297pub mod tests {
298 use std::io::Cursor;
299
300 use super::*;
301
302 pub fn encode_content(content: RequestContent) -> Vec<u8> {
307 let mut cursor = Cursor::new(Vec::new());
308 let msg = PlainRequest::new(content);
309 msg.write(&mut cursor).unwrap();
310 let bytes_of_msg = cursor.into_inner();
311 bytes_of_msg[Header::STRUCT_SIZE..].to_vec()
313 }
314
315 pub fn decode_content(bytes: &[u8]) -> PlainResponse {
316 let mut cursor = Cursor::new(bytes);
317 cursor.read_le().unwrap()
318 }
319}