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
182impl From<error::ErrorResponse>
183 for ResponseContent
184{
185 fn from(resp: error::ErrorResponse) -> Self {
186 ResponseContent::Error(resp)
187 }
188}
189
190make_content_impl!{
191 RequestContent,
192 $(
193 {$cmd, $struct_pfx::[<$cmd Request>]},
194 )+
195 {Cancel, cancel::CancelRequest},
196 {OplockBreakAck, oplock::OplockBreakAck},
197 {LeaseBreakAck, oplock::LeaseBreakAck},
198}
199
200make_content_impl!{
201 ResponseContent,
202 $(
203 {$cmd, $struct_pfx::[<$cmd Response>]},
204 )+
205 {OplockBreakNotify, oplock::OplockBreakNotify},
206 {LeaseBreakNotify, oplock::LeaseBreakNotify},
207 {OplockBreak, oplock::OplockBreakResponse},
208 {LeaseBreak, oplock::LeaseBreakResponse},
209 {ServerToClientNotification, notify::ServerToClientNotification},
210 {Error, error::ErrorResponse},
211}
212 }
213 };
214}
215
216make_content!(
217 {Negotiate, negotiate},
218 {SessionSetup, session_setup},
219 {Logoff, session_setup},
220 {TreeConnect, tree_connect},
221 {TreeDisconnect, tree_connect},
222 {Create, create},
223 {Close, create},
224 {Flush, file},
225 {Read, file},
226 {Write, file},
227 {Lock, lock},
228 {Ioctl, ioctl},
229 {Echo, echo},
230 {QueryDirectory, query_dir},
231 {ChangeNotify, notify},
232 {QueryInfo, info},
233 {SetInfo, info},
234);
235
236impl RequestContent {
237 pub fn req_payload_size(&self) -> u32 {
242 use RequestContent::*;
243 match self {
244 Write(req) => req.length,
246 Ioctl(req) => req.buffer.get_size() + req.max_output_response,
248 _ => 0,
249 }
250 }
251 pub fn expected_resp_size(&self) -> u32 {
256 use RequestContent::*;
257 match self {
258 Read(req) => req.length,
260 QueryDirectory(req) => req.output_buffer_length,
262 Ioctl(req) => req.max_input_response + req.max_output_response,
264 _ => 0,
265 }
266 }
267}
268
269macro_rules! make_plain {
270 ($suffix:ident, $server_to_redir:literal, $binrw_attr:ty) => {
271 pastey::paste! {
272
273 #[$binrw_attr]
275 #[derive(Debug)]
276 #[brw(little)]
277 pub struct [<Plain $suffix>] {
278 #[brw(assert(header.flags.server_to_redir() == $server_to_redir))]
279 pub header: Header,
280 #[brw(args(&header.command))]
281 pub content: [<$suffix Content>],
282 }
283
284 impl [<Plain $suffix>] {
285 pub fn new(content: [<$suffix Content>]) -> [<Plain $suffix>] {
286 let cmd = content.associated_cmd();
287 Self::new_with_command(content, cmd)
288 }
289
290 pub fn new_with_command(content: [<$suffix Content>], command: Command) -> [<Plain $suffix>] {
291 [<Plain $suffix>] {
292 header: Header {
294 credit_charge: 0,
295 status: Status::Success as u32,
296 command,
297 credit_request: 0,
298 flags: HeaderFlags::new(),
299 next_command: 0,
300 message_id: u64::MAX,
301 tree_id: Some(0),
302 async_id: None,
303 session_id: 0,
304 signature: 0,
305 },
306 content,
307 }
308 }
309 }
310 }
311 };
312}
313
314macro_rules! gen_req_resp {
315 ($req_attr:ty, $res_attr:ty) => {
316 make_plain!(Request, false, $req_attr);
317 make_plain!(Response, true, $res_attr);
318 };
319}
320
321#[cfg(all(feature = "server", feature = "client"))]
322gen_req_resp!(binrw::binrw, binrw::binrw);
323#[cfg(all(feature = "client", not(feature = "server")))]
324gen_req_resp!(binrw::binwrite, binrw::binread);
325#[cfg(all(feature = "server", not(feature = "client")))]
326gen_req_resp!(binrw::binread, binrw::binwrite);