1use std::io::SeekFrom;
4
5use smb_dtyp::binrw_util::prelude::*;
6
7use super::negotiate::CompressionAlgorithm;
8use binrw::io::TakeSeekExt;
9use binrw::prelude::*;
10
11#[binrw::binrw]
12#[derive(Debug, PartialEq, Eq)]
13#[brw(little)]
14pub enum CompressedMessage {
15 Unchained(CompressedUnchainedMessage),
16 Chained(CompressedChainedMessage),
17}
18
19impl CompressedMessage {
20 pub fn total_size(&self) -> usize {
21 match self {
22 CompressedMessage::Unchained(m) => {
23 m.data.len() + CompressedUnchainedMessage::STRUCT_SIZE
24 }
25 CompressedMessage::Chained(m) => {
26 m.items.iter().map(|i| i.payload_data.len()).sum::<usize>()
27 + m.items.len() * 4
28 + CompressedChainedMessage::STRUCT_SIZE
29 }
30 }
31 }
32}
33
34#[binrw::binrw]
35#[derive(Debug, PartialEq, Eq)]
36#[brw(magic(b"\xfcSMB"), little)]
37pub struct CompressedUnchainedMessage {
38 pub original_size: u32,
39 #[brw(assert(!matches!(compression_algorithm, CompressionAlgorithm::None)))]
41 pub compression_algorithm: CompressionAlgorithm,
42 #[br(assert(flags == 0))]
43 #[bw(calc = 0)]
44 flags: u16,
45 #[bw(calc = 0)]
46 offset: u32,
47 #[br(seek_before = SeekFrom::Current(offset as i64))]
48 #[br(parse_with = binrw::helpers::until_eof)]
49 pub data: Vec<u8>,
50}
51
52impl CompressedUnchainedMessage {
53 const MAGIC_SIZE: usize = 4;
54 pub const STRUCT_SIZE: usize = Self::MAGIC_SIZE
55 + std::mem::size_of::<u32>() * 2
56 + std::mem::size_of::<CompressionAlgorithm>()
57 + std::mem::size_of::<u16>();
58}
59
60#[binrw::binrw]
61#[derive(Debug, PartialEq, Eq)]
62#[brw(magic(b"\xfcSMB"), little)]
63pub struct CompressedChainedMessage {
64 pub original_size: u32,
65 #[br(parse_with = binrw::helpers::until_eof)]
66 pub items: Vec<CompressedChainedItem>,
67}
68
69impl CompressedChainedMessage {
70 pub const STRUCT_SIZE: usize = std::mem::size_of::<u32>() + 4;
71}
72
73fn add_original_size_to_total_length(algo: &CompressionAlgorithm) -> u64 {
74 if algo.original_size_required() {
75 std::mem::size_of::<u32>() as u64
76 } else {
77 0
78 }
79}
80
81#[binrw::binrw]
82#[derive(Debug, PartialEq, Eq)]
83pub struct CompressedChainedItem {
84 pub compression_algorithm: CompressionAlgorithm,
85 pub flags: u16,
86 #[bw(calc = PosMarker::default())]
87 length: PosMarker<u32>,
88 #[brw(if(compression_algorithm.original_size_required()))]
90 #[bw(assert(original_size.is_none() ^ compression_algorithm.original_size_required()))]
91 pub original_size: Option<u32>,
92 #[br(map_stream = |s| s.take_seek(length.value as u64 - (add_original_size_to_total_length(&compression_algorithm))), parse_with = binrw::helpers::until_eof)]
94 #[bw(write_with = PosMarker::write_size_plus, args(&length, add_original_size_to_total_length(compression_algorithm)))]
95 pub payload_data: Vec<u8>,
96}
97
98#[binrw::binrw]
99#[derive(Debug, PartialEq, Eq)]
100pub struct CompressedData {
101 #[br(parse_with = binrw::helpers::until_eof)]
102 data: Vec<u8>,
103}
104
105#[cfg(test)]
106mod tests {
107 use super::*;
108 use smb_tests::*;
109
110 test_binrw! {
113 CompressedMessage => chained0: CompressedMessage::Chained(CompressedChainedMessage {
114 original_size: 368,
115 items: vec![
116 CompressedChainedItem {
117 compression_algorithm: CompressionAlgorithm::None,
118 flags: 1,
119 original_size: None,
120 payload_data: vec![
121 0xfe, 0x53, 0x4d, 0x42, 0x40, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10,
122 0x0, 0x1, 0x0, 0x30, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x91, 0x0, 0x0,
123 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0,
124 0x7d, 0x0, 0x0, 0x28, 0x0, 0x30, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
125 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x29, 0x0, 0x1,
126 0xf, 0x2a, 0x2, 0x0, 0x0, 0x68, 0x0, 0x0, 0x0, 0x8, 0x1, 0x0, 0x0, 0x0,
127 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0xee, 0x5, 0x0, 0x0, 0xc, 0x0, 0x0,
128 0x0, 0x8d, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0
129 ],
130 },
131 CompressedChainedItem {
132 compression_algorithm: CompressionAlgorithm::None,
133 flags: 0xb975,
134 original_size: None,
135 payload_data: vec![
136 0x0, 0x0, 0x0, 0x0, 0x15, 0x24, 0x4d, 0x70, 0x45, 0x61, 0x5f, 0x44,
137 0x32, 0x36, 0x32, 0x41, 0x43, 0x36, 0x32, 0x34, 0x34, 0x35, 0x31, 0x32,
138 0x39, 0x35
139 ],
140 },
141 CompressedChainedItem {
142 compression_algorithm: CompressionAlgorithm::PatternV1,
143 flags: 0,
144 original_size: None,
145 payload_data: vec![0x0, 0x0, 0x0, 0x0, 0xee, 0x0, 0x0, 0x0]
146 }
147 ]
148 }) => "fc534d42700100000000010068000000fe534d4240000100000000001000010030000000000000009100000000000000fffe0000010000007d00002800300000000000000000000000000000000000002900010f2a02000068000000080100000000000003000000ee0500000c0000008d0000000c000000000075b91a0000000000000015244d7045615f443236324143363234343531323935040000000800000000000000ee000000"
149 }
150
151 const CHAINED1_ITEM2_DATA: &'static str = "f2034d5a90000300000004000000ffff0000b8000\
153 100124007000f02000af32e200100000e1fba0e00b409cd21b8014ccd21546869732070726f677\
154 2616d2063616e6e6f742062652072756e20696e20444f53206d6f64652e0d0d0a245a0084a98ee\
155 eb9edef80ea0400d1996e86ebddef80ea9f6e83ebe81000b181ebe1ef80eabe9084ebec0800318\
156 3ebe320003181ebef3800b181eaadef80eae49713eaf010003180ea7f38004088eb5ee94000318\
157 5ebf310003184eb9008003183eba908001180500040996e7fea580031996e82100040526963684\
158 400039f00d4005045000064862400bbf4ba231400f10bf00022000b020e260090a60000e01d000\
159 0f061009002b100001022002040010700000c00561000000a00040001020030f044012800b126d\
160 1c2000100604100000817002200200700013500000200004000030200010b00c17014008fb3010\
161 028471400b86100f3144001288e030000700d00b4cf06000070c200b825000000904301dc5f000\
162 0101004007040000f02000120a05b4200071a0057401400f80610000b0200b22e7264617461000\
163 0305c0d91001360080007020062400000482e702800029400400d0000d09c00170d26000328001\
164 26928002224267c0023003008000702000150001265280000fc00630070140000c00800070200c\
165 14000004050524f54444154411b01223016a400000800070200005000a047464944530000002ca\
166 9700043160000b0080007020081400000425061643113006110090000f0160b000c0200f200800\
167 000422e746578740000000ba14cc5012db04c30008020000068504147454000f0008042440000b\
168 06c000050440000a063130005020040200000602800f5044c4b00001c6402000000b1000070020\
169 000f0a72400000200012800804f4f4c434f4445bef4012270b340012060aa1f00050200047800e\
170 04b440000ea5d000000a0b300006024020d2800017800605652465919150e048fb400002003000\
171 0f02800035048444c53760e032220b778002510ae740000020001a000904147454247465868694\
172 50321b7008d021e402800f1005452414345535550a319000000c0b73d002d00b02800014001b24\
173 34d5243f30e000000e0b7e0011dd0280050604b5641531801107e610413f0a0001de0280050684\
174 b534350ac0010607f032220b850002010af1300050200004001904452565052580000b71600133\
175 028001e20280050666f74686b240000ad03134028001e302800e0494e49544b444247a6f101000\
176 0506e054e020000402800904d494e4945580000bc20032250ba68012040b162000502004020000\
177 0625000001100ee1be009000080ba0000f0090000702800405061643228007100901b000070c40\
178 b000c020052800000622e6f03400080291c690100ed0449000060bb2b00f104400000c8414c4d4\
179 f5354524f409c00000030fc8d003d0050bc2800f2004341434845414c49008e000000d0fc20011\
180 e70280000f80200c0037250b401000060fd50001d80280010c02800e056524644503c01000020f\
181 f0000a0d8020e280000180100500030b41402f402017405390040bda00081200000c2506164331\
182 50061801d000080023f040c020090800000c2434647524fe30001a80521200170031a505000001\
183 8014150616434400050d01f00003022070e020080800000ca2e727372fe0301c4059d004001009\
184 0030000805000c1422e72656c6f630000dc5501dc0578006001000010c15500500040000042";
185
186 const CHAINED1_TEST_DATA: &'static str = const_format::concatcp!(
187 "fc534d42501000000000010050000000fe534d424000010000000000080001001900000000000000070000000000000000000000010000001d00000000600000251698bc898e3e86aeb713557cfaf1bb1100500000100000000000000000000005000000f7040000c8070000",
188 CHAINED1_ITEM2_DATA,
189 "04000000080000000000000038080000"
190 );
191
192 test_binrw! {
193 CompressedMessage => chained1: CompressedMessage::Chained(CompressedChainedMessage {
194 original_size: 4176,
195 items: vec![
196 CompressedChainedItem {
197 compression_algorithm: CompressionAlgorithm::None,
198 flags: 1,
199 original_size: None,
200 payload_data: vec![
201 0xfe, 0x53, 0x4d, 0x42, 0x40, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8,
202 0x0, 0x1, 0x0, 0x19, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0x0, 0x0,
203 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1d,
204 0x0, 0x0, 0x0, 0x0, 0x60, 0x0, 0x0, 0x25, 0x16, 0x98, 0xbc, 0x89, 0x8e,
205 0x3e, 0x86, 0xae, 0xb7, 0x13, 0x55, 0x7c, 0xfa, 0xf1, 0xbb, 0x11, 0x0,
206 0x50, 0x0, 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0
207 ],
208 },
209 CompressedChainedItem {
210 compression_algorithm: CompressionAlgorithm::LZ4,
211 flags: 0,
212 original_size: Some(0x7c8),
213 payload_data: smb_tests::hex_to_u8_array! {CHAINED1_ITEM2_DATA}
214 },
215 CompressedChainedItem {
216 compression_algorithm: CompressionAlgorithm::PatternV1,
217 flags: 0,
218 original_size: None,
219 payload_data: vec![0x0, 0x0, 0x0, 0x0, 0x38, 0x8, 0x0, 0x0]
220 },
221 ]
222 }) => CHAINED1_TEST_DATA
223 }
224
225 test_binrw! {
226 CompressedMessage => multiple2: CompressedMessage::Chained(CompressedChainedMessage {
227 original_size: 368,
228 items: vec![
229 CompressedChainedItem {
230 compression_algorithm: CompressionAlgorithm::None,
231 flags: 1,
232 original_size: None,
233 payload_data: vec![
234 0xfe, 0x53, 0x4d, 0x42, 0x40, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10, 0x0,
235 0x1, 0x0, 0x30, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1e, 0x3, 0x0, 0x0,
236 0x0, 0x0, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0x5, 0x0, 0x0, 0x0, 0x9, 0x0,
237 0x0, 0x2c, 0x0, 0x30, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
238 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x29, 0x0, 0x1, 0xf, 0x2a, 0x2,
239 0x0, 0x0, 0x68, 0x0, 0x0, 0x0, 0x8, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3,
240 0x0, 0x0, 0x0, 0x11, 0x7, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0x69, 0x0, 0x20,
241 0x0, 0xc, 0x0, 0x0, 0x0,
242 ],
243 },
244 CompressedChainedItem {
245 compression_algorithm: CompressionAlgorithm::None,
246 flags: 0,
247 original_size: None,
248 payload_data: vec![
249 0x0, 0x0, 0x0, 0x0, 0x15, 0x24, 0x4d, 0x70, 0x45, 0x61, 0x5f, 0x44, 0x32,
250 0x36, 0x32, 0x41, 0x43, 0x36, 0x32, 0x34, 0x34, 0x35, 0x31, 0x32, 0x39,
251 0x35,
252 ],
253 },
254 CompressedChainedItem {
255 compression_algorithm: CompressionAlgorithm::PatternV1,
256 flags: 0,
257 original_size: None,
258 payload_data: vec![0x0, 0x0, 0x0, 0x0, 0xee, 0x0, 0x0, 0x0],
259 },
260 ],
261 }) => "fc534d42700100000000010068000000fe534d4240000100000000001000010030000000000000001e03000000000000fffe0000050000000900002c00300000000000000000000000000000000000002900010f2a02000068000000080100000000000003000000110700000c000000690020000c000000000000001a0000000000000015244d7045615f443236324143363234343531323935040000000800000000000000ee000000"
262 }
263}