smb_msg/ioctl/
msg.rs

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
49/// This is a helper trait that defines, for a certain FSCTL request type,
50/// the response type and their matching FSCTL code.
51pub 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    /// General Ioctl request, providing a buffer as an input.
70    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
101// TODO: Enable non-fsctl ioctls. currently, we only support FSCTLs.
102ioctl_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()))] // there is an exception for pass-through operations.
146    #[bw(try_calc = in_buffer.len().try_into())]
147    #[br(assert(input_count == 0))]
148    input_count: u32,
149
150    // is either (0) or (input_offset + input_count)
151    #[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)] // reserved.
158    #[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    /// Parses the response content into the specified type.
175    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}