/src/suricata/rust/src/http2/decompression.rs
Line | Count | Source |
1 | | /* Copyright (C) 2021 Open Information Security Foundation |
2 | | * |
3 | | * You can copy, redistribute or modify this Program under the terms of |
4 | | * the GNU General Public License version 2 as published by the Free |
5 | | * Software Foundation. |
6 | | * |
7 | | * This program is distributed in the hope that it will be useful, |
8 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
9 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
10 | | * GNU General Public License for more details. |
11 | | * |
12 | | * You should have received a copy of the GNU General Public License |
13 | | * version 2 along with this program; if not, write to the Free Software |
14 | | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
15 | | * 02110-1301, USA. |
16 | | */ |
17 | | |
18 | | use crate::direction::Direction; |
19 | | use brotli; |
20 | | use flate2::read::{DeflateDecoder, GzDecoder}; |
21 | | use std; |
22 | | use std::io; |
23 | | use std::io::{Cursor, Read, Write}; |
24 | | |
25 | | pub const HTTP2_DECOMPRESSION_CHUNK_SIZE: usize = 0x1000; // 4096 |
26 | | |
27 | | #[repr(u8)] |
28 | | #[derive(Copy, Clone, PartialOrd, PartialEq, Eq, Debug)] |
29 | | pub enum HTTP2ContentEncoding { |
30 | | Unknown = 0, |
31 | | Gzip = 1, |
32 | | Br = 2, |
33 | | Deflate = 3, |
34 | | Unrecognized = 4, |
35 | | } |
36 | | |
37 | | //a cursor turning EOF into blocking errors |
38 | | #[derive(Debug)] |
39 | | pub struct HTTP2cursor { |
40 | | pub cursor: Cursor<Vec<u8>>, |
41 | | } |
42 | | |
43 | | impl HTTP2cursor { |
44 | 108k | pub fn new() -> HTTP2cursor { |
45 | 108k | HTTP2cursor { |
46 | 108k | cursor: Cursor::new(Vec::new()), |
47 | 108k | } |
48 | 108k | } |
49 | | |
50 | 187k | pub fn set_position(&mut self, pos: u64) { |
51 | 187k | return self.cursor.set_position(pos); |
52 | 187k | } |
53 | | |
54 | 134k | pub fn clear(&mut self) { |
55 | 134k | self.cursor.get_mut().clear(); |
56 | 134k | self.cursor.set_position(0); |
57 | 134k | } |
58 | | } |
59 | | |
60 | | // we need to implement this as flate2 and brotli crates |
61 | | // will read from this object |
62 | | impl Read for HTTP2cursor { |
63 | 304k | fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { |
64 | | //use the cursor, except it turns eof into blocking error |
65 | 304k | let r = self.cursor.read(buf); |
66 | 304k | match r { |
67 | 0 | Err(ref err) => { |
68 | 0 | if err.kind() == io::ErrorKind::UnexpectedEof { |
69 | 0 | return Err(io::ErrorKind::WouldBlock.into()); |
70 | 0 | } |
71 | | } |
72 | | Ok(0) => { |
73 | | //regular EOF turned into blocking error |
74 | 132k | return Err(io::ErrorKind::WouldBlock.into()); |
75 | | } |
76 | 171k | Ok(_n) => {} |
77 | | } |
78 | 171k | return r; |
79 | 304k | } |
80 | | } |
81 | | |
82 | | pub enum HTTP2Decompresser { |
83 | | Unassigned, |
84 | | // Box because large. |
85 | | Gzip(Box<GzDecoder<HTTP2cursor>>), |
86 | | // Box because large. |
87 | | Brotli(Box<brotli::Decompressor<HTTP2cursor>>), |
88 | | // This one is not so large, at 88 bytes as of doing this, but box |
89 | | // for consistency. |
90 | | Deflate(Box<DeflateDecoder<HTTP2cursor>>), |
91 | | } |
92 | | |
93 | | impl std::fmt::Debug for HTTP2Decompresser { |
94 | 0 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { |
95 | 0 | match self { |
96 | 0 | HTTP2Decompresser::Unassigned => write!(f, "UNASSIGNED"), |
97 | 0 | HTTP2Decompresser::Gzip(_) => write!(f, "GZIP"), |
98 | 0 | HTTP2Decompresser::Brotli(_) => write!(f, "BROTLI"), |
99 | 0 | HTTP2Decompresser::Deflate(_) => write!(f, "DEFLATE"), |
100 | | } |
101 | 0 | } |
102 | | } |
103 | | |
104 | | #[derive(Debug)] |
105 | | struct HTTP2DecoderHalf { |
106 | | encoding: HTTP2ContentEncoding, |
107 | | decoder: HTTP2Decompresser, |
108 | | } |
109 | | |
110 | | pub trait GetMutCursor { |
111 | | fn get_mut(&mut self) -> &mut HTTP2cursor; |
112 | | } |
113 | | |
114 | | impl GetMutCursor for GzDecoder<HTTP2cursor> { |
115 | 157k | fn get_mut(&mut self) -> &mut HTTP2cursor { |
116 | 157k | return self.get_mut(); |
117 | 157k | } |
118 | | } |
119 | | |
120 | | impl GetMutCursor for DeflateDecoder<HTTP2cursor> { |
121 | 49.1k | fn get_mut(&mut self) -> &mut HTTP2cursor { |
122 | 49.1k | return self.get_mut(); |
123 | 49.1k | } |
124 | | } |
125 | | |
126 | | impl GetMutCursor for brotli::Decompressor<HTTP2cursor> { |
127 | 302k | fn get_mut(&mut self) -> &mut HTTP2cursor { |
128 | 302k | return self.get_mut(); |
129 | 302k | } |
130 | | } |
131 | | |
132 | 187k | fn http2_decompress<'a>( |
133 | 187k | decoder: &mut (impl Read + GetMutCursor), input: &'a [u8], output: &'a mut Vec<u8>, |
134 | 187k | ) -> io::Result<&'a [u8]> { |
135 | 187k | match decoder.get_mut().cursor.write_all(input) { |
136 | 187k | Ok(()) => {} |
137 | 0 | Err(e) => { |
138 | 0 | return Err(e); |
139 | | } |
140 | | } |
141 | 187k | let mut offset = 0; |
142 | 187k | decoder.get_mut().set_position(0); |
143 | 187k | output.resize(HTTP2_DECOMPRESSION_CHUNK_SIZE, 0); |
144 | | loop { |
145 | 2.55M | match decoder.read(&mut output[offset..]) { |
146 | | Ok(0) => { |
147 | 23.6k | break; |
148 | | } |
149 | 2.36M | Ok(n) => { |
150 | 2.36M | offset += n; |
151 | 2.36M | if offset == output.len() { |
152 | 2.30M | output.resize(output.len() + HTTP2_DECOMPRESSION_CHUNK_SIZE, 0); |
153 | 2.30M | } |
154 | | } |
155 | 163k | Err(e) => { |
156 | 163k | if e.kind() == io::ErrorKind::WouldBlock { |
157 | 111k | break; |
158 | 52.5k | } |
159 | 52.5k | return Err(e); |
160 | | } |
161 | | } |
162 | | } |
163 | | //brotli does not consume all input if it reaches some end |
164 | 134k | decoder.get_mut().clear(); |
165 | 134k | return Ok(&output[..offset]); |
166 | 187k | } suricata::http2::decompression::http2_decompress::<brotli_decompressor::reader::Decompressor<suricata::http2::decompression::HTTP2cursor>> Line | Count | Source | 132 | 111k | fn http2_decompress<'a>( | 133 | 111k | decoder: &mut (impl Read + GetMutCursor), input: &'a [u8], output: &'a mut Vec<u8>, | 134 | 111k | ) -> io::Result<&'a [u8]> { | 135 | 111k | match decoder.get_mut().cursor.write_all(input) { | 136 | 111k | Ok(()) => {} | 137 | 0 | Err(e) => { | 138 | 0 | return Err(e); | 139 | | } | 140 | | } | 141 | 111k | let mut offset = 0; | 142 | 111k | decoder.get_mut().set_position(0); | 143 | 111k | output.resize(HTTP2_DECOMPRESSION_CHUNK_SIZE, 0); | 144 | | loop { | 145 | 2.19M | match decoder.read(&mut output[offset..]) { | 146 | | Ok(0) => { | 147 | 19.1k | break; | 148 | | } | 149 | 2.08M | Ok(n) => { | 150 | 2.08M | offset += n; | 151 | 2.08M | if offset == output.len() { | 152 | 2.03M | output.resize(output.len() + HTTP2_DECOMPRESSION_CHUNK_SIZE, 0); | 153 | 2.03M | } | 154 | | } | 155 | 91.9k | Err(e) => { | 156 | 91.9k | if e.kind() == io::ErrorKind::WouldBlock { | 157 | 60.9k | break; | 158 | 31.0k | } | 159 | 31.0k | return Err(e); | 160 | | } | 161 | | } | 162 | | } | 163 | | //brotli does not consume all input if it reaches some end | 164 | 80.0k | decoder.get_mut().clear(); | 165 | 80.0k | return Ok(&output[..offset]); | 166 | 111k | } |
suricata::http2::decompression::http2_decompress::<flate2::gz::read::GzDecoder<suricata::http2::decompression::HTTP2cursor>> Line | Count | Source | 132 | 56.1k | fn http2_decompress<'a>( | 133 | 56.1k | decoder: &mut (impl Read + GetMutCursor), input: &'a [u8], output: &'a mut Vec<u8>, | 134 | 56.1k | ) -> io::Result<&'a [u8]> { | 135 | 56.1k | match decoder.get_mut().cursor.write_all(input) { | 136 | 56.1k | Ok(()) => {} | 137 | 0 | Err(e) => { | 138 | 0 | return Err(e); | 139 | | } | 140 | | } | 141 | 56.1k | let mut offset = 0; | 142 | 56.1k | decoder.get_mut().set_position(0); | 143 | 56.1k | output.resize(HTTP2_DECOMPRESSION_CHUNK_SIZE, 0); | 144 | | loop { | 145 | 116k | match decoder.read(&mut output[offset..]) { | 146 | | Ok(0) => { | 147 | 3.50k | break; | 148 | | } | 149 | 59.8k | Ok(n) => { | 150 | 59.8k | offset += n; | 151 | 59.8k | if offset == output.len() { | 152 | 50.7k | output.resize(output.len() + HTTP2_DECOMPRESSION_CHUNK_SIZE, 0); | 153 | 50.7k | } | 154 | | } | 155 | 52.6k | Err(e) => { | 156 | 52.6k | if e.kind() == io::ErrorKind::WouldBlock { | 157 | 41.8k | break; | 158 | 10.8k | } | 159 | 10.8k | return Err(e); | 160 | | } | 161 | | } | 162 | | } | 163 | | //brotli does not consume all input if it reaches some end | 164 | 45.3k | decoder.get_mut().clear(); | 165 | 45.3k | return Ok(&output[..offset]); | 166 | 56.1k | } |
suricata::http2::decompression::http2_decompress::<flate2::deflate::read::DeflateDecoder<suricata::http2::decompression::HTTP2cursor>> Line | Count | Source | 132 | 19.9k | fn http2_decompress<'a>( | 133 | 19.9k | decoder: &mut (impl Read + GetMutCursor), input: &'a [u8], output: &'a mut Vec<u8>, | 134 | 19.9k | ) -> io::Result<&'a [u8]> { | 135 | 19.9k | match decoder.get_mut().cursor.write_all(input) { | 136 | 19.9k | Ok(()) => {} | 137 | 0 | Err(e) => { | 138 | 0 | return Err(e); | 139 | | } | 140 | | } | 141 | 19.9k | let mut offset = 0; | 142 | 19.9k | decoder.get_mut().set_position(0); | 143 | 19.9k | output.resize(HTTP2_DECOMPRESSION_CHUNK_SIZE, 0); | 144 | | loop { | 145 | 245k | match decoder.read(&mut output[offset..]) { | 146 | | Ok(0) => { | 147 | 972 | break; | 148 | | } | 149 | 225k | Ok(n) => { | 150 | 225k | offset += n; | 151 | 225k | if offset == output.len() { | 152 | 219k | output.resize(output.len() + HTTP2_DECOMPRESSION_CHUNK_SIZE, 0); | 153 | 219k | } | 154 | | } | 155 | 18.9k | Err(e) => { | 156 | 18.9k | if e.kind() == io::ErrorKind::WouldBlock { | 157 | 8.32k | break; | 158 | 10.6k | } | 159 | 10.6k | return Err(e); | 160 | | } | 161 | | } | 162 | | } | 163 | | //brotli does not consume all input if it reaches some end | 164 | 9.29k | decoder.get_mut().clear(); | 165 | 9.29k | return Ok(&output[..offset]); | 166 | 19.9k | } |
|
167 | | |
168 | | impl HTTP2DecoderHalf { |
169 | 3.78M | pub fn new() -> HTTP2DecoderHalf { |
170 | 3.78M | HTTP2DecoderHalf { |
171 | 3.78M | encoding: HTTP2ContentEncoding::Unknown, |
172 | 3.78M | decoder: HTTP2Decompresser::Unassigned, |
173 | 3.78M | } |
174 | 3.78M | } |
175 | | |
176 | 290k | pub fn http2_encoding_fromvec(&mut self, input: &[u8]) { |
177 | | //use first encoding... |
178 | 290k | if self.encoding == HTTP2ContentEncoding::Unknown { |
179 | 133k | if input == b"gzip" { |
180 | 21.7k | self.encoding = HTTP2ContentEncoding::Gzip; |
181 | 21.7k | self.decoder = |
182 | 21.7k | HTTP2Decompresser::Gzip(Box::new(GzDecoder::new(HTTP2cursor::new()))); |
183 | 111k | } else if input == b"deflate" { |
184 | 15.3k | self.encoding = HTTP2ContentEncoding::Deflate; |
185 | 15.3k | self.decoder = |
186 | 15.3k | HTTP2Decompresser::Deflate(Box::new(DeflateDecoder::new(HTTP2cursor::new()))); |
187 | 96.5k | } else if input == b"br" { |
188 | 71.1k | self.encoding = HTTP2ContentEncoding::Br; |
189 | 71.1k | self.decoder = HTTP2Decompresser::Brotli(Box::new(brotli::Decompressor::new( |
190 | 71.1k | HTTP2cursor::new(), |
191 | 71.1k | HTTP2_DECOMPRESSION_CHUNK_SIZE, |
192 | 71.1k | ))); |
193 | 71.1k | } else { |
194 | 25.4k | self.encoding = HTTP2ContentEncoding::Unrecognized; |
195 | 25.4k | } |
196 | 157k | } |
197 | 290k | } |
198 | | |
199 | 1.25M | pub fn decompress<'a>( |
200 | 1.25M | &mut self, input: &'a [u8], output: &'a mut Vec<u8>, |
201 | 1.25M | ) -> io::Result<&'a [u8]> { |
202 | 1.25M | match self.decoder { |
203 | 56.1k | HTTP2Decompresser::Gzip(ref mut gzip_decoder) => { |
204 | 56.1k | let r = http2_decompress(&mut *gzip_decoder.as_mut(), input, output); |
205 | 56.1k | if r.is_err() { |
206 | 10.8k | self.decoder = HTTP2Decompresser::Unassigned; |
207 | 45.3k | } |
208 | 56.1k | return r; |
209 | | } |
210 | 111k | HTTP2Decompresser::Brotli(ref mut br_decoder) => { |
211 | 111k | let r = http2_decompress(&mut *br_decoder.as_mut(), input, output); |
212 | 111k | if r.is_err() { |
213 | 31.0k | self.decoder = HTTP2Decompresser::Unassigned; |
214 | 80.0k | } |
215 | 111k | return r; |
216 | | } |
217 | 19.9k | HTTP2Decompresser::Deflate(ref mut df_decoder) => { |
218 | 19.9k | let r = http2_decompress(&mut *df_decoder.as_mut(), input, output); |
219 | 19.9k | if r.is_err() { |
220 | 10.6k | self.decoder = HTTP2Decompresser::Unassigned; |
221 | 10.6k | } |
222 | 19.9k | return r; |
223 | | } |
224 | 1.06M | _ => {} |
225 | | } |
226 | 1.06M | return Ok(input); |
227 | 1.25M | } |
228 | | } |
229 | | |
230 | | #[derive(Debug)] |
231 | | pub struct HTTP2Decoder { |
232 | | decoder_tc: HTTP2DecoderHalf, |
233 | | decoder_ts: HTTP2DecoderHalf, |
234 | | } |
235 | | |
236 | | impl HTTP2Decoder { |
237 | 1.89M | pub fn new() -> HTTP2Decoder { |
238 | 1.89M | HTTP2Decoder { |
239 | 1.89M | decoder_tc: HTTP2DecoderHalf::new(), |
240 | 1.89M | decoder_ts: HTTP2DecoderHalf::new(), |
241 | 1.89M | } |
242 | 1.89M | } |
243 | | |
244 | 290k | pub fn http2_encoding_fromvec(&mut self, input: &[u8], dir: Direction) { |
245 | 290k | if dir == Direction::ToClient { |
246 | 208k | self.decoder_tc.http2_encoding_fromvec(input); |
247 | 208k | } else { |
248 | 82.1k | self.decoder_ts.http2_encoding_fromvec(input); |
249 | 82.1k | } |
250 | 290k | } |
251 | | |
252 | 1.25M | pub fn decompress<'a>( |
253 | 1.25M | &mut self, input: &'a [u8], output: &'a mut Vec<u8>, dir: Direction, |
254 | 1.25M | ) -> io::Result<&'a [u8]> { |
255 | 1.25M | if dir == Direction::ToClient { |
256 | 650k | return self.decoder_tc.decompress(input, output); |
257 | | } else { |
258 | 599k | return self.decoder_ts.decompress(input, output); |
259 | | } |
260 | 1.25M | } |
261 | | } |