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 _reserved: u16,
24 pub ctl_code: u32,
25 pub file_id: FileId,
26 #[bw(calc = PosMarker::default())]
27 _input_offset: PosMarker<u32>,
28 #[bw(calc = PosMarker::default())]
29 _input_count: PosMarker<u32>,
30 pub max_input_response: u32,
31 #[bw(calc = 0)]
32 #[br(assert(output_offset == 0))]
33 output_offset: u32,
34 #[bw(calc = 0)]
35 #[br(assert(output_count == 0))]
36 output_count: u32,
37 pub max_output_response: u32,
38 pub flags: IoctlRequestFlags,
39 #[bw(calc = 0)]
40 reserved2: u32,
41
42 #[bw(write_with = PosMarker::write_aoff_size, args(&_input_offset, &_input_count))]
43 #[br(map_stream = |s| s.take_seek(_input_count.value as u64), args(ctl_code, flags))]
44 pub buffer: IoctlReqData,
45}
46
47pub trait FsctlRequest: for<'a> BinWrite<Args<'a> = ()> + Into<IoctlReqData> {
50 type Response: FsctlResponseContent;
51 const FSCTL_CODE: FsctlCodes;
52}
53
54macro_rules! ioctl_req_data {
55 ($($fsctl:ident: $model:ty, $response:ty, )+) => {
56 pastey::paste! {
57
58#[binrw::binrw]
59#[derive(Debug, PartialEq, Eq)]
60#[br(import(ctl_code: u32, flags: IoctlRequestFlags))]
61pub enum IoctlReqData {
62 $(
63 #[br(pre_assert(ctl_code == FsctlCodes::$fsctl as u32 && flags.is_fsctl()))]
64 [<Fsctl $fsctl:camel>]($model),
65 )+
66
67 Ioctl(IoctlBuffer),
69}
70
71impl IoctlReqData {
72 pub fn get_size(&self) -> u32 {
73 use IoctlReqData::*;
74 match self {
75 $(
76 [<Fsctl $fsctl:camel>](data) => data.get_bin_size(),
77 )+
78 Ioctl(data) => data.len() as u32,
79 }
80 }
81}
82
83$(
84 impl FsctlRequest for $model {
85 type Response = $response;
86 const FSCTL_CODE: FsctlCodes = FsctlCodes::$fsctl;
87 }
88
89 impl From<$model> for IoctlReqData {
90 fn from(model: $model) -> IoctlReqData {
91 IoctlReqData::[<Fsctl $fsctl:camel>](model)
92 }
93 }
94)+
95 }
96 }
97}
98
99ioctl_req_data! {
101 PipePeek: PipePeekRequest, PipePeekResponse,
102 SrvEnumerateSnapshots: SrvEnumerateSnapshotsRequest, SrvEnumerateSnapshotsResponse,
103 SrvRequestResumeKey: SrvRequestResumeKeyRequest, SrvRequestResumeKey,
104 QueryNetworkInterfaceInfo: QueryNetworkInterfaceInfoRequest, NetworkInterfacesInfo,
105 SrvCopychunk: SrvCopychunkCopy, SrvCopychunkResponse,
106 SrvCopychunkWrite: SrvCopyChunkCopyWrite, SrvCopychunkResponse,
107 SrvReadHash: SrvReadHashReq, SrvReadHashRes,
108 LmrRequestResiliency: NetworkResiliencyRequest, LmrRequestResiliencyResponse,
109 ValidateNegotiateInfo: ValidateNegotiateInfoRequest, ValidateNegotiateInfoResponse,
110 DfsGetReferrals: ReqGetDfsReferral, RespGetDfsReferral,
111 PipeWait: PipeWaitRequest, PipeWaitResponse,
112 PipeTransceive: PipeTransceiveRequest, PipeTransceiveResponse,
113 SetReparsePoint: SetReparsePointRequest, SetReparsePointResponse,
114 DfsGetReferralsEx: ReqGetDfsReferralEx, RespGetDfsReferral,
115 FileLevelTrim: FileLevelTrimRequest, FileLevelTrimResponse,
116 QueryAllocatedRanges: QueryAllocRangesItem, QueryAllocRangesResult,
117 OffloadRead: OffloadReadRequest, OffloadReadResponse,
118}
119
120#[bitfield]
121#[derive(BinWrite, BinRead, Debug, Default, Clone, Copy, PartialEq, Eq)]
122#[bw(map = |&x| Self::into_bytes(x))]
123#[br(map = Self::from_bytes)]
124pub struct IoctlRequestFlags {
125 pub is_fsctl: bool,
126 #[skip]
127 __: B31,
128}
129
130#[binrw::binrw]
131#[derive(Debug, PartialEq, Eq)]
132pub struct IoctlResponse {
133 #[bw(calc = 49)]
134 #[br(assert(struct_size == 49))]
135 struct_size: u16,
136 #[bw(calc = 0)]
137 _reserved: u16,
138 pub ctl_code: u32,
139 pub file_id: FileId,
140 #[bw(calc = PosMarker::default())]
141 input_offset: PosMarker<u32>,
142 #[bw(assert(in_buffer.is_empty()))] #[bw(try_calc = in_buffer.len().try_into())]
144 #[br(assert(input_count == 0))]
145 input_count: u32,
146
147 #[br(assert(output_offset.value == 0 || output_offset.value == input_offset.value + input_count))]
149 #[bw(calc = PosMarker::default())]
150 output_offset: PosMarker<u32>,
151 #[bw(try_calc = out_buffer.len().try_into())]
152 output_count: u32,
153
154 #[bw(calc = 0)] #[br(assert(flags == 0))]
156 flags: u32,
157 #[bw(calc = 0)]
158 reserved2: u32,
159
160 #[br(seek_before = SeekFrom::Start(input_offset.value.into()))]
161 #[br(count = input_count)]
162 #[bw(write_with = PosMarker::write_aoff, args(&input_offset))]
163 pub in_buffer: Vec<u8>,
164
165 #[br(seek_before = SeekFrom::Start(output_offset.value.into()))]
166 #[br(count = output_count)]
167 #[bw(write_with = PosMarker::write_aoff, args(&output_offset))]
168 pub out_buffer: Vec<u8>,
169}
170
171impl IoctlResponse {
172 pub fn parse_fsctl<T>(&self) -> crate::Result<T>
174 where
175 T: FsctlResponseContent,
176 {
177 if !T::FSCTL_CODES.iter().any(|&f| f as u32 == self.ctl_code) {
178 return Err(crate::SmbMsgError::MissingFsctlDefinition(self.ctl_code));
179 }
180 let mut cursor = std::io::Cursor::new(&self.out_buffer);
181 Ok(T::read_le(&mut cursor).unwrap())
182 }
183}
184
185#[cfg(test)]
186mod tests {
187 use smb_tests::*;
188
189 use crate::*;
190
191 use super::*;
192
193 const REQ_IOCTL_BUFFER_CONTENT: &'static str = "0500000310000000980000000300000080000000010039000000000013f8a58f166fb54482c28f2dae140df50000000001000000000000000000020000000000010000000000000000000200000000000500000000000000010500000000000515000000173da72e955653f915dff280e9030000000000000000000000000000000000000000000001000000000000000000000002000000";
194
195 test_request! {
196 Ioctl {
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 IoctlBuffer::from(
208 hex_to_u8_array! {REQ_IOCTL_BUFFER_CONTENT}
209 ).into(),
210 ),
211 } => const_format::concatcp!("3900000017c01100280500000c000000850000000c0000007800000098000000000000000000000000000000000400000100000000000000", REQ_IOCTL_BUFFER_CONTENT)
212 }
213
214 const IOCTL_TEST_BUFFER_CONTENT: &'static str = "05000203100000000401000003000000ec00000001000000000002000000000001000000000000000000020000000000200000000000000001000000000000000c000e000000000000000200000000000000020000000000070000000000000000000000000000000600000000000000410056004900560056004d00000000000400000000000000010400000000000515000000173da72e955653f915dff28001000000000000000000020000000000010000000000000001000000000000000a000c00000000000000020000000000000000000000000006000000000000000000000000000000050000000000000061007600690076006e0000000100000000000000";
216
217 test_response! {
218 Ioctl {
219 ctl_code: FsctlCodes::PipeTransceive as u32,
220 file_id: [
221 0x28, 0x5, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0x85, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0,
222 0x0,
223 ]
224 .into(),
225 in_buffer: vec![],
226 out_buffer: smb_tests::hex_to_u8_array! {IOCTL_TEST_BUFFER_CONTENT},
227 } => const_format::concatcp!("3100000017c01100280500000c000000850000000c000000700000000000000070000000040100000000000000000000",IOCTL_TEST_BUFFER_CONTENT)
228 }
229}