smb_msg/
compressed.rs

1//! Compressed messages
2
3use 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    // The same as the negotiation, but must be set.
40    #[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    // Only present if algorithms require it.
89    #[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    // The length specified in `length` also includes `original_size` if present!
93    #[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    // TODO(TEST): unchained
111
112    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    /// I do it for the sake of some real, big data.
152    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}