1use super::{
2 common::{IoctlBuffer, IoctlRequestContent},
3 fsctl::*,
4};
5use binrw::io::TakeSeekExt;
6use binrw::prelude::*;
7use modular_bitfield::prelude::*;
8use smb_dtyp::binrw_util::prelude::*;
9use std::io::SeekFrom;
10
11use crate::{
12 FileId,
13 dfsc::{ReqGetDfsReferral, ReqGetDfsReferralEx, RespGetDfsReferral},
14};
15
16#[binrw::binrw]
17#[derive(Debug, PartialEq, Eq)]
18pub struct IoctlRequest {
19 #[bw(calc = 57)]
20 #[br(assert(struct_size == 57))]
21 struct_size: u16,
22 #[bw(calc = 0)]
23 #[br(assert(_reserved == 0))]
24 _reserved: u16,
25 pub ctl_code: u32,
26 pub file_id: FileId,
27 #[bw(calc = PosMarker::default())]
28 _input_offset: PosMarker<u32>,
29 #[bw(calc = PosMarker::default())]
30 _input_count: PosMarker<u32>,
31 pub max_input_response: u32,
32 #[bw(calc = 0)]
33 #[br(assert(output_offset == 0))]
34 output_offset: u32,
35 #[bw(calc = 0)]
36 #[br(assert(output_count == 0))]
37 output_count: u32,
38 pub max_output_response: u32,
39 pub flags: IoctlRequestFlags,
40 #[bw(calc = 0)]
41 #[br(assert(reserved2 == 0))]
42 reserved2: u32,
43
44 #[bw(write_with = PosMarker::write_aoff_size, args(&_input_offset, &_input_count))]
45 #[br(map_stream = |s| s.take_seek(_input_count.value as u64), args(ctl_code, flags))]
46 pub buffer: IoctlReqData,
47}
48
49pub trait FsctlRequest: for<'a> BinWrite<Args<'a> = ()> + Into<IoctlReqData> {
52 type Response: FsctlResponseContent;
53 const FSCTL_CODE: FsctlCodes;
54}
55
56macro_rules! ioctl_req_data {
57 ($($fsctl:ident: $model:ty, $response:ty, )+) => {
58 paste::paste! {
59
60#[binrw::binrw]
61#[derive(Debug, PartialEq, Eq)]
62#[br(import(ctl_code: u32, flags: IoctlRequestFlags))]
63pub enum IoctlReqData {
64 $(
65 #[br(pre_assert(ctl_code == FsctlCodes::$fsctl as u32 && flags.is_fsctl()))]
66 [<Fsctl $fsctl:camel>]($model),
67 )+
68
69 Ioctl(IoctlBuffer),
71}
72
73impl IoctlReqData {
74 pub fn get_size(&self) -> u32 {
75 use IoctlReqData::*;
76 match self {
77 $(
78 [<Fsctl $fsctl:camel>](data) => data.get_bin_size(),
79 )+
80 Ioctl(data) => data.len() as u32,
81 }
82 }
83}
84
85$(
86 impl FsctlRequest for $model {
87 type Response = $response;
88 const FSCTL_CODE: FsctlCodes = FsctlCodes::$fsctl;
89 }
90
91 impl From<$model> for IoctlReqData {
92 fn from(model: $model) -> IoctlReqData {
93 IoctlReqData::[<Fsctl $fsctl:camel>](model)
94 }
95 }
96)+
97 }
98 }
99}
100
101ioctl_req_data! {
103 PipePeek: PipePeekRequest, PipePeekResponse,
104 SrvEnumerateSnapshots: SrvEnumerateSnapshotsRequest, SrvEnumerateSnapshotsResponse,
105 SrvRequestResumeKey: SrvRequestResumeKeyRequest, SrvRequestResumeKey,
106 QueryNetworkInterfaceInfo: QueryNetworkInterfaceInfoRequest, NetworkInterfacesInfo,
107 SrvCopychunk: SrvCopychunkCopy, SrvCopychunkResponse,
108 SrvCopychunkWrite: SrvCopyChunkCopyWrite, SrvCopychunkResponse,
109 SrvReadHash: SrvReadHashReq, SrvReadHashRes,
110 LmrRequestResiliency: NetworkResiliencyRequest, LmrRequestResiliencyResponse,
111 ValidateNegotiateInfo: ValidateNegotiateInfoRequest, ValidateNegotiateInfoResponse,
112 DfsGetReferrals: ReqGetDfsReferral, RespGetDfsReferral,
113 PipeWait: PipeWaitRequest, PipeWaitResponse,
114 PipeTransceive: PipeTransceiveRequest, PipeTransceiveResponse,
115 SetReparsePoint: SetReparsePointRequest, SetReparsePointResponse,
116 DfsGetReferralsEx: ReqGetDfsReferralEx, RespGetDfsReferral,
117 FileLevelTrim: FileLevelTrimRequest, FileLevelTrimResponse,
118 QueryAllocatedRanges: QueryAllocRangesItem, QueryAllocRangesResult,
119 OffloadRead: OffloadReadRequest, OffloadReadResponse,
120}
121
122#[bitfield]
123#[derive(BinWrite, BinRead, Debug, Default, Clone, Copy, PartialEq, Eq)]
124#[bw(map = |&x| Self::into_bytes(x))]
125#[br(map = Self::from_bytes)]
126pub struct IoctlRequestFlags {
127 pub is_fsctl: bool,
128 #[skip]
129 __: B31,
130}
131
132#[binrw::binrw]
133#[derive(Debug, PartialEq, Eq)]
134pub struct IoctlResponse {
135 #[bw(calc = 49)]
136 #[br(assert(struct_size == 49))]
137 struct_size: u16,
138 #[bw(calc = 0)]
139 #[br(assert(_reserved == 0))]
140 _reserved: u16,
141 pub ctl_code: u32,
142 pub file_id: FileId,
143 #[bw(calc = PosMarker::default())]
144 input_offset: PosMarker<u32>,
145 #[bw(assert(out_buffer.is_empty()))] #[bw(try_calc = in_buffer.len().try_into())]
147 #[br(assert(input_count == 0))]
148 input_count: u32,
149
150 #[br(assert(output_offset.value == 0 || output_offset.value == input_offset.value + input_count))]
152 #[bw(calc = PosMarker::default())]
153 output_offset: PosMarker<u32>,
154 #[bw(try_calc = out_buffer.len().try_into())]
155 output_count: u32,
156
157 #[bw(calc = 0)] #[br(assert(flags == 0))]
159 flags: u32,
160 #[bw(calc = 0)]
161 #[br(assert(reserved2 == 0))]
162 reserved2: u32,
163
164 #[br(seek_before = SeekFrom::Start(input_offset.value.into()))]
165 #[br(count = input_count)]
166 pub in_buffer: Vec<u8>,
167
168 #[br(seek_before = SeekFrom::Start(output_offset.value.into()))]
169 #[br(count = output_count)]
170 pub out_buffer: Vec<u8>,
171}
172
173impl IoctlResponse {
174 pub fn parse_fsctl<T>(&self) -> crate::Result<T>
176 where
177 T: FsctlResponseContent,
178 {
179 if !T::FSCTL_CODES.iter().any(|&f| f as u32 == self.ctl_code) {
180 return Err(crate::SmbMsgError::MissingFsctlDefinition(self.ctl_code));
181 }
182 let mut cursor = std::io::Cursor::new(&self.out_buffer);
183 Ok(T::read_le(&mut cursor).unwrap())
184 }
185}
186
187#[cfg(test)]
188mod tests {
189 use crate::*;
190
191 use super::*;
192
193 #[test]
194 pub fn test_ioctl_req_write() {
195 let encoded = encode_content(
196 IoctlRequest {
197 ctl_code: FsctlCodes::PipeTransceive as u32,
198 file_id: [
199 0x28, 0x5, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0x85, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0,
200 0x0,
201 ]
202 .into(),
203 max_input_response: 0,
204 max_output_response: 1024,
205 flags: IoctlRequestFlags::new().with_is_fsctl(true),
206 buffer: IoctlReqData::FsctlPipeTransceive(
207 Into::<IoctlBuffer>::into(
208 [
209 0x5, 0x0, 0x0, 0x3, 0x10, 0x0, 0x0, 0x0, 0x98, 0x0, 0x0, 0x0, 0x3, 0x0,
210 0x0, 0x0, 0x80, 0x0, 0x0, 0x0, 0x1, 0x0, 0x39, 0x0, 0x0, 0x0, 0x0, 0x0,
211 0x13, 0xf8, 0xa5, 0x8f, 0x16, 0x6f, 0xb5, 0x44, 0x82, 0xc2, 0x8f, 0x2d,
212 0xae, 0x14, 0xd, 0xf5, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0,
213 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0,
214 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5,
215 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x5, 0x0, 0x0, 0x0, 0x0, 0x0,
216 0x5, 0x15, 0x0, 0x0, 0x0, 0x17, 0x3d, 0xa7, 0x2e, 0x95, 0x56, 0x53,
217 0xf9, 0x15, 0xdf, 0xf2, 0x80, 0xe9, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
218 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
219 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
220 0x2, 0x0, 0x0, 0x0,
221 ]
222 .as_ref(),
223 )
224 .into(),
225 ),
226 }
227 .into(),
228 );
229 assert_eq!(
230 encoded,
231 &[
232 0x39, 0x0, 0x0, 0x0, 0x17, 0xc0, 0x11, 0x0, 0x28, 0x5, 0x0, 0x0, 0xc, 0x0, 0x0,
233 0x0, 0x85, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0x78, 0x0, 0x0, 0x0, 0x98, 0x0, 0x0,
234 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0x0,
235 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5, 0x0, 0x0, 0x3, 0x10, 0x0, 0x0,
236 0x0, 0x98, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x80, 0x0, 0x0, 0x0, 0x1, 0x0, 0x39,
237 0x0, 0x0, 0x0, 0x0, 0x0, 0x13, 0xf8, 0xa5, 0x8f, 0x16, 0x6f, 0xb5, 0x44, 0x82,
238 0xc2, 0x8f, 0x2d, 0xae, 0x14, 0xd, 0xf5, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0,
239 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0,
240 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5, 0x0, 0x0, 0x0,
241 0x0, 0x0, 0x0, 0x0, 0x1, 0x5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5, 0x15, 0x0, 0x0, 0x0,
242 0x17, 0x3d, 0xa7, 0x2e, 0x95, 0x56, 0x53, 0xf9, 0x15, 0xdf, 0xf2, 0x80, 0xe9, 0x3,
243 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
244 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
245 0x0, 0x0, 0x2, 0x0, 0x0, 0x0
246 ]
247 )
248 }
249
250 #[test]
251 pub fn test_ioctl_res_parse() {
252 let data = [
253 0xfe, 0x53, 0x4d, 0x42, 0x40, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb, 0x0, 0x1, 0x0,
254 0x31, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff,
255 0xfe, 0x0, 0x0, 0x5, 0x0, 0x0, 0x0, 0x31, 0x0, 0x0, 0x28, 0x0, 0x30, 0x0, 0x0, 0x0,
256 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x31, 0x0,
257 0x0, 0x0, 0x17, 0xc0, 0x11, 0x0, 0x28, 0x5, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0x85, 0x0,
258 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0x70, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x70, 0x0, 0x0,
259 0x0, 0x4, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5, 0x0, 0x2, 0x3,
260 0x10, 0x0, 0x0, 0x0, 0x4, 0x1, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0xec, 0x0, 0x0, 0x0, 0x1,
261 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0,
262 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
263 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc, 0x0, 0xe, 0x0, 0x0, 0x0, 0x0, 0x0,
264 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7,
265 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6, 0x0,
266 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x41, 0x0, 0x56, 0x0, 0x49, 0x0, 0x56, 0x0, 0x56, 0x0,
267 0x4d, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x4, 0x0,
268 0x0, 0x0, 0x0, 0x0, 0x5, 0x15, 0x0, 0x0, 0x0, 0x17, 0x3d, 0xa7, 0x2e, 0x95, 0x56, 0x53,
269 0xf9, 0x15, 0xdf, 0xf2, 0x80, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2,
270 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0,
271 0x0, 0x0, 0x0, 0x0, 0xa, 0x0, 0xc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0,
272 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6, 0x0, 0x0, 0x0, 0x0, 0x0,
273 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
274 0x0, 0x61, 0x0, 0x76, 0x0, 0x69, 0x0, 0x76, 0x0, 0x6e, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0,
275 0x0, 0x0, 0x0, 0x0, 0x0,
276 ];
277 let message = decode_content(&data);
278 let message = message.content.to_ioctl().unwrap();
279 assert_eq!(
280 message,
281 IoctlResponse {
282 ctl_code: FsctlCodes::PipeTransceive as u32,
283 file_id: [
284 0x28, 0x5, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0x85, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0,
285 0x0,
286 ]
287 .into(),
288 in_buffer: vec![],
289 out_buffer: [
290 0x5, 0x0, 0x2, 0x3, 0x10, 0x0, 0x0, 0x0, 0x4, 0x1, 0x0, 0x0, 0x3, 0x0, 0x0,
291 0x0, 0xec, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0,
292 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0,
293 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0,
294 0x0, 0x0, 0x0, 0xc, 0x0, 0xe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0,
295 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0x0, 0x0, 0x0, 0x0,
296 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6, 0x0, 0x0, 0x0, 0x0,
297 0x0, 0x0, 0x0, 0x41, 0x0, 0x56, 0x0, 0x49, 0x0, 0x56, 0x0, 0x56, 0x0, 0x4d,
298 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x4, 0x0,
299 0x0, 0x0, 0x0, 0x0, 0x5, 0x15, 0x0, 0x0, 0x0, 0x17, 0x3d, 0xa7, 0x2e, 0x95,
300 0x56, 0x53, 0xf9, 0x15, 0xdf, 0xf2, 0x80, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
301 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
302 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0x0, 0xc, 0x0, 0x0, 0x0, 0x0,
303 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
304 0x0, 0x6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
305 0x0, 0x5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x61, 0x0, 0x76, 0x0, 0x69, 0x0,
306 0x76, 0x0, 0x6e, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0
307 ]
308 .to_vec(),
309 }
310 );
311 }
312}