1use super::{
2 common::{IoctlBuffer, IoctlRequestContent},
3 fsctl::*,
4};
5#[cfg(feature = "server")]
6use binrw::io::TakeSeekExt;
7use binrw::prelude::*;
8use modular_bitfield::prelude::*;
9use smb_dtyp::binrw_util::prelude::*;
10use smb_msg_derive::*;
11
12#[cfg(feature = "client")]
13use std::io::SeekFrom;
14
15use crate::{
16 FileId,
17 dfsc::{ReqGetDfsReferral, ReqGetDfsReferralEx, RespGetDfsReferral},
18};
19
20#[smb_request(size = 57)]
27pub struct IoctlRequest {
28 reserved: u16,
29 pub ctl_code: u32,
31 pub file_id: FileId,
33 #[bw(calc = PosMarker::default())]
35 #[br(temp)]
36 _input_offset: PosMarker<u32>,
37 #[bw(calc = PosMarker::default())]
39 #[br(temp)]
40 _input_count: PosMarker<u32>,
41 pub max_input_response: u32,
43 #[bw(calc = 0)]
45 #[br(assert(output_offset == 0))]
46 #[br(temp)]
47 output_offset: u32,
48 #[bw(calc = 0)]
50 #[br(assert(output_count == 0))]
51 #[br(temp)]
52 output_count: u32,
53 pub max_output_response: u32,
55 pub flags: IoctlRequestFlags,
57 reserved: u32,
58
59 #[bw(write_with = PosMarker::write_aoff_size, args(&_input_offset, &_input_count))]
61 #[br(map_stream = |s| s.take_seek(_input_count.value as u64), args(ctl_code, flags))]
62 pub buffer: IoctlReqData,
63}
64
65#[cfg(all(feature = "client", not(feature = "server")))]
66pub trait FsctlRequest: for<'a> BinWrite<Args<'a> = ()> + Into<IoctlReqData> {
69 type Response: FsctlResponseContent;
70 const FSCTL_CODE: FsctlCodes;
71}
72
73#[cfg(all(feature = "server", not(feature = "client")))]
74pub trait FsctlRequest: for<'a> BinRead<Args<'a> = ()> + Into<IoctlReqData> {
77 type Response: FsctlResponseContent;
78 const FSCTL_CODE: FsctlCodes;
79}
80
81#[cfg(all(feature = "server", feature = "client"))]
82pub trait FsctlRequest:
85 for<'a> BinWrite<Args<'a> = ()> + for<'b> BinRead<Args<'b> = ()> + Into<IoctlReqData>
86{
87 type Response: FsctlResponseContent;
88 const FSCTL_CODE: FsctlCodes;
89}
90
91macro_rules! ioctl_req_data {
92 ($($fsctl:ident: $model:ty, $response:ty, )+) => {
93 pastey::paste! {
94
95#[smb_request_binrw]
96#[br(import(ctl_code: u32, flags: IoctlRequestFlags))]
97pub enum IoctlReqData {
98 $(
99 #[doc = concat!(
100 "Ioctl request for FSCTL code `",
101 stringify!($fsctl),
102 "`."
103 )]
104 #[br(pre_assert(ctl_code == FsctlCodes::$fsctl as u32 && flags.is_fsctl()))]
105 [<Fsctl $fsctl:camel>]($model),
106 )+
107
108 Ioctl(IoctlBuffer),
113}
114
115impl IoctlReqData {
116 pub fn get_size(&self) -> u32 {
117 use IoctlReqData::*;
118 match self {
119 $(
120 [<Fsctl $fsctl:camel>](data) => data.get_bin_size(),
121 )+
122 Ioctl(data) => data.len() as u32,
123 }
124 }
125}
126
127$(
128 impl FsctlRequest for $model {
129 type Response = $response;
130 const FSCTL_CODE: FsctlCodes = FsctlCodes::$fsctl;
131 }
132
133 impl From<$model> for IoctlReqData {
134 fn from(model: $model) -> IoctlReqData {
135 IoctlReqData::[<Fsctl $fsctl:camel>](model)
136 }
137 }
138)+
139 }
140 }
141}
142
143ioctl_req_data! {
145 PipePeek: PipePeekRequest, PipePeekResponse,
146 SrvEnumerateSnapshots: SrvEnumerateSnapshotsRequest, SrvEnumerateSnapshotsResponse,
147 SrvRequestResumeKey: SrvRequestResumeKeyRequest, SrvRequestResumeKey,
148 QueryNetworkInterfaceInfo: QueryNetworkInterfaceInfoRequest, NetworkInterfacesInfo,
149 SrvCopychunk: SrvCopychunkCopy, SrvCopychunkResponse,
150 SrvCopychunkWrite: SrvCopyChunkCopyWrite, SrvCopychunkResponse,
151 SrvReadHash: SrvReadHashReq, SrvReadHashRes,
152 LmrRequestResiliency: NetworkResiliencyRequest, LmrRequestResiliencyResponse,
153 ValidateNegotiateInfo: ValidateNegotiateInfoRequest, ValidateNegotiateInfoResponse,
154 DfsGetReferrals: ReqGetDfsReferral, RespGetDfsReferral,
155 PipeWait: PipeWaitRequest, PipeWaitResponse,
156 PipeTransceive: PipeTransceiveRequest, PipeTransceiveResponse,
157 SetReparsePoint: SetReparsePointRequest, SetReparsePointResponse,
158 DfsGetReferralsEx: ReqGetDfsReferralEx, RespGetDfsReferral,
159 FileLevelTrim: FileLevelTrimRequest, FileLevelTrimResponse,
160 QueryAllocatedRanges: QueryAllocRangesItem, QueryAllocRangesResult,
161 OffloadRead: OffloadReadRequest, OffloadReadResponse,
162}
163
164#[smb_dtyp::mbitfield]
168pub struct IoctlRequestFlags {
169 pub is_fsctl: bool,
172 #[skip]
173 __: B31,
174}
175
176#[smb_response(size = 49)]
183pub struct IoctlResponse {
184 reserved: u16,
185 pub ctl_code: u32,
187 pub file_id: FileId,
189 #[bw(calc = PosMarker::default())]
191 #[br(temp)]
192 input_offset: PosMarker<u32>,
193 #[bw(assert(in_buffer.is_empty()))] #[bw(try_calc = in_buffer.len().try_into())]
196 #[br(assert(input_count == 0))]
197 #[br(temp)]
198 input_count: u32,
199
200 #[br(assert(output_offset.value == 0 || output_offset.value == input_offset.value + input_count))]
202 #[bw(calc = PosMarker::default())]
203 #[br(temp)]
204 output_offset: PosMarker<u32>,
205 #[bw(try_calc = out_buffer.len().try_into())]
207 #[br(temp)]
208 output_count: u32,
209
210 reserved: u32,
212
213 reserved: u32,
214
215 #[br(seek_before = SeekFrom::Start(input_offset.value.into()))]
217 #[br(count = input_count)]
218 #[bw(write_with = PosMarker::write_aoff, args(&input_offset))]
219 pub in_buffer: Vec<u8>,
220
221 #[br(seek_before = SeekFrom::Start(output_offset.value.into()))]
223 #[br(count = output_count)]
224 #[bw(write_with = PosMarker::write_aoff, args(&output_offset))]
225 pub out_buffer: Vec<u8>,
226}
227
228impl IoctlResponse {
229 #[cfg(feature = "client")]
230 pub fn parse_fsctl<T>(&self) -> crate::Result<T>
240 where
241 T: FsctlResponseContent,
242 {
243 if !T::FSCTL_CODES.iter().any(|&f| f as u32 == self.ctl_code) {
244 return Err(crate::SmbMsgError::MissingFsctlDefinition(self.ctl_code));
245 }
246 let mut cursor = std::io::Cursor::new(&self.out_buffer);
247 Ok(T::read_le(&mut cursor).unwrap())
248 }
249}
250
251#[cfg(test)]
252mod tests {
253 use smb_tests::*;
254
255 use crate::*;
256
257 use super::*;
258
259 const REQ_IOCTL_BUFFER_CONTENT: &'static str = "0500000310000000980000000300000080000000010039000000000013f8a58f166fb54482c28f2dae140df50000000001000000000000000000020000000000010000000000000000000200000000000500000000000000010500000000000515000000173da72e955653f915dff280e9030000000000000000000000000000000000000000000001000000000000000000000002000000";
260
261 test_request! {
262 Ioctl {
263 ctl_code: FsctlCodes::PipeTransceive as u32,
264 file_id: [
265 0x28, 0x5, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0x85, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0,
266 0x0,
267 ]
268 .into(),
269 max_input_response: 0,
270 max_output_response: 1024,
271 flags: IoctlRequestFlags::new().with_is_fsctl(true),
272 buffer: IoctlReqData::FsctlPipeTransceive(
273 IoctlBuffer::from(
274 hex_to_u8_array! {REQ_IOCTL_BUFFER_CONTENT}
275 ).into(),
276 ),
277 } => const_format::concatcp!("3900000017c01100280500000c000000850000000c0000007800000098000000000000000000000000000000000400000100000000000000", REQ_IOCTL_BUFFER_CONTENT)
278 }
279
280 const IOCTL_TEST_BUFFER_CONTENT: &'static str = "05000203100000000401000003000000ec00000001000000000002000000000001000000000000000000020000000000200000000000000001000000000000000c000e000000000000000200000000000000020000000000070000000000000000000000000000000600000000000000410056004900560056004d00000000000400000000000000010400000000000515000000173da72e955653f915dff28001000000000000000000020000000000010000000000000001000000000000000a000c00000000000000020000000000000000000000000006000000000000000000000000000000050000000000000061007600690076006e0000000100000000000000";
282
283 test_response! {
284 Ioctl {
285 ctl_code: FsctlCodes::PipeTransceive as u32,
286 file_id: [
287 0x28, 0x5, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0x85, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0,
288 0x0,
289 ]
290 .into(),
291 in_buffer: vec![],
292 out_buffer: smb_tests::hex_to_u8_array! {IOCTL_TEST_BUFFER_CONTENT},
293 } => const_format::concatcp!("3100000017c01100280500000c000000850000000c000000700000000000000070000000040100000000000000000000",IOCTL_TEST_BUFFER_CONTENT)
294 }
295}