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