/src/suricata7/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::core::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 | 37.7k | pub fn new() -> HTTP2cursor { |
45 | 37.7k | HTTP2cursor { |
46 | 37.7k | cursor: Cursor::new(Vec::new()), |
47 | 37.7k | } |
48 | 37.7k | } |
49 | | |
50 | 100k | pub fn set_position(&mut self, pos: u64) { |
51 | 100k | return self.cursor.set_position(pos); |
52 | 100k | } |
53 | | |
54 | 80.4k | pub fn clear(&mut self) { |
55 | 80.4k | self.cursor.get_mut().clear(); |
56 | 80.4k | self.cursor.set_position(0); |
57 | 80.4k | } |
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 | 181k | fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { |
64 | | //use the cursor, except it turns eof into blocking error |
65 | 181k | let r = self.cursor.read(buf); |
66 | 181k | 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 | 86.7k | return Err(io::ErrorKind::WouldBlock.into()); |
75 | | } |
76 | 94.4k | Ok(_n) => {} |
77 | | } |
78 | 94.4k | return r; |
79 | 181k | } |
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 | 148k | fn get_mut(&mut self) -> &mut HTTP2cursor { |
116 | 148k | return self.get_mut(); |
117 | 148k | } |
118 | | } |
119 | | |
120 | | impl GetMutCursor for DeflateDecoder<HTTP2cursor> { |
121 | 0 | fn get_mut(&mut self) -> &mut HTTP2cursor { |
122 | 0 | return self.get_mut(); |
123 | 0 | } |
124 | | } |
125 | | |
126 | | impl GetMutCursor for brotli::Decompressor<HTTP2cursor> { |
127 | 134k | fn get_mut(&mut self) -> &mut HTTP2cursor { |
128 | 134k | return self.get_mut(); |
129 | 134k | } |
130 | | } |
131 | | |
132 | 100k | fn http2_decompress<'a>( |
133 | 100k | decoder: &mut (impl Read + GetMutCursor), input: &'a [u8], output: &'a mut Vec<u8>, |
134 | 100k | ) -> io::Result<&'a [u8]> { |
135 | 100k | match decoder.get_mut().cursor.write_all(input) { |
136 | 100k | Ok(()) => {} |
137 | 0 | Err(e) => { |
138 | 0 | return Err(e); |
139 | | } |
140 | | } |
141 | 100k | let mut offset = 0; |
142 | 100k | decoder.get_mut().set_position(0); |
143 | 100k | output.resize(HTTP2_DECOMPRESSION_CHUNK_SIZE, 0); |
144 | | loop { |
145 | 622k | match decoder.read(&mut output[offset..]) { |
146 | | Ok(0) => { |
147 | 6.44k | break; |
148 | | } |
149 | 521k | Ok(n) => { |
150 | 521k | offset += n; |
151 | 521k | if offset == output.len() { |
152 | 472k | output.resize(output.len() + HTTP2_DECOMPRESSION_CHUNK_SIZE, 0); |
153 | 472k | } |
154 | | } |
155 | 94.4k | Err(e) => { |
156 | 94.4k | if e.kind() == io::ErrorKind::WouldBlock { |
157 | 73.9k | break; |
158 | 20.5k | } |
159 | 20.5k | return Err(e); |
160 | | } |
161 | | } |
162 | | } |
163 | | //brotli does not consume all input if it reaches some end |
164 | 80.4k | decoder.get_mut().clear(); |
165 | 80.4k | return Ok(&output[..offset]); |
166 | 100k | } suricata::http2::decompression::http2_decompress::<brotli_decompressor::reader::Decompressor<suricata::http2::decompression::HTTP2cursor>> Line | Count | Source | 132 | 48.9k | fn http2_decompress<'a>( | 133 | 48.9k | decoder: &mut (impl Read + GetMutCursor), input: &'a [u8], output: &'a mut Vec<u8>, | 134 | 48.9k | ) -> io::Result<&'a [u8]> { | 135 | 48.9k | match decoder.get_mut().cursor.write_all(input) { | 136 | 48.9k | Ok(()) => {} | 137 | 0 | Err(e) => { | 138 | 0 | return Err(e); | 139 | | } | 140 | | } | 141 | 48.9k | let mut offset = 0; | 142 | 48.9k | decoder.get_mut().set_position(0); | 143 | 48.9k | output.resize(HTTP2_DECOMPRESSION_CHUNK_SIZE, 0); | 144 | | loop { | 145 | 461k | match decoder.read(&mut output[offset..]) { | 146 | | Ok(0) => { | 147 | 5.22k | break; | 148 | | } | 149 | 412k | Ok(n) => { | 150 | 412k | offset += n; | 151 | 412k | if offset == output.len() { | 152 | 393k | output.resize(output.len() + HTTP2_DECOMPRESSION_CHUNK_SIZE, 0); | 153 | 393k | } | 154 | | } | 155 | 43.7k | Err(e) => { | 156 | 43.7k | if e.kind() == io::ErrorKind::WouldBlock { | 157 | 30.8k | break; | 158 | 12.9k | } | 159 | 12.9k | return Err(e); | 160 | | } | 161 | | } | 162 | | } | 163 | | //brotli does not consume all input if it reaches some end | 164 | 36.0k | decoder.get_mut().clear(); | 165 | 36.0k | return Ok(&output[..offset]); | 166 | 48.9k | } |
suricata::http2::decompression::http2_decompress::<flate2::gz::read::GzDecoder<suricata::http2::decompression::HTTP2cursor>> Line | Count | Source | 132 | 51.9k | fn http2_decompress<'a>( | 133 | 51.9k | decoder: &mut (impl Read + GetMutCursor), input: &'a [u8], output: &'a mut Vec<u8>, | 134 | 51.9k | ) -> io::Result<&'a [u8]> { | 135 | 51.9k | match decoder.get_mut().cursor.write_all(input) { | 136 | 51.9k | Ok(()) => {} | 137 | 0 | Err(e) => { | 138 | 0 | return Err(e); | 139 | | } | 140 | | } | 141 | 51.9k | let mut offset = 0; | 142 | 51.9k | decoder.get_mut().set_position(0); | 143 | 51.9k | output.resize(HTTP2_DECOMPRESSION_CHUNK_SIZE, 0); | 144 | | loop { | 145 | 160k | match decoder.read(&mut output[offset..]) { | 146 | | Ok(0) => { | 147 | 1.22k | break; | 148 | | } | 149 | 108k | Ok(n) => { | 150 | 108k | offset += n; | 151 | 108k | if offset == output.len() { | 152 | 79.4k | output.resize(output.len() + HTTP2_DECOMPRESSION_CHUNK_SIZE, 0); | 153 | 79.4k | } | 154 | | } | 155 | 50.7k | Err(e) => { | 156 | 50.7k | if e.kind() == io::ErrorKind::WouldBlock { | 157 | 43.1k | break; | 158 | 7.59k | } | 159 | 7.59k | return Err(e); | 160 | | } | 161 | | } | 162 | | } | 163 | | //brotli does not consume all input if it reaches some end | 164 | 44.3k | decoder.get_mut().clear(); | 165 | 44.3k | return Ok(&output[..offset]); | 166 | 51.9k | } |
Unexecuted instantiation: suricata::http2::decompression::http2_decompress::<flate2::deflate::read::DeflateDecoder<suricata::http2::decompression::HTTP2cursor>> |
167 | | |
168 | | impl HTTP2DecoderHalf { |
169 | 1.62M | pub fn new() -> HTTP2DecoderHalf { |
170 | 1.62M | HTTP2DecoderHalf { |
171 | 1.62M | encoding: HTTP2ContentEncoding::Unknown, |
172 | 1.62M | decoder: HTTP2Decompresser::Unassigned, |
173 | 1.62M | } |
174 | 1.62M | } |
175 | | |
176 | 114k | pub fn http2_encoding_fromvec(&mut self, input: &[u8]) { |
177 | | //use first encoding... |
178 | 114k | if self.encoding == HTTP2ContentEncoding::Unknown { |
179 | 41.7k | if input == b"gzip" { |
180 | 12.7k | self.encoding = HTTP2ContentEncoding::Gzip; |
181 | 12.7k | self.decoder = HTTP2Decompresser::Gzip(Box::new(GzDecoder::new(HTTP2cursor::new()))); |
182 | 29.0k | } else if input == b"deflate" { |
183 | 0 | self.encoding = HTTP2ContentEncoding::Deflate; |
184 | 0 | self.decoder = HTTP2Decompresser::Deflate(Box::new(DeflateDecoder::new(HTTP2cursor::new()))); |
185 | 29.0k | } else if input == b"br" { |
186 | 25.0k | self.encoding = HTTP2ContentEncoding::Br; |
187 | 25.0k | self.decoder = HTTP2Decompresser::Brotli(Box::new(brotli::Decompressor::new( |
188 | 25.0k | HTTP2cursor::new(), |
189 | 25.0k | HTTP2_DECOMPRESSION_CHUNK_SIZE, |
190 | 25.0k | ))); |
191 | 25.0k | } else { |
192 | 3.99k | self.encoding = HTTP2ContentEncoding::Unrecognized; |
193 | 3.99k | } |
194 | 72.9k | } |
195 | 114k | } |
196 | | |
197 | 577k | pub fn decompress<'a>( |
198 | 577k | &mut self, input: &'a [u8], output: &'a mut Vec<u8>, |
199 | 577k | ) -> io::Result<&'a [u8]> { |
200 | 577k | match self.decoder { |
201 | 51.9k | HTTP2Decompresser::Gzip(ref mut gzip_decoder) => { |
202 | 51.9k | let r = http2_decompress(&mut *gzip_decoder.as_mut(), input, output); |
203 | 51.9k | if r.is_err() { |
204 | 7.59k | self.decoder = HTTP2Decompresser::Unassigned; |
205 | 44.3k | } |
206 | 51.9k | return r; |
207 | | } |
208 | 48.9k | HTTP2Decompresser::Brotli(ref mut br_decoder) => { |
209 | 48.9k | let r = http2_decompress(&mut *br_decoder.as_mut(), input, output); |
210 | 48.9k | if r.is_err() { |
211 | 12.9k | self.decoder = HTTP2Decompresser::Unassigned; |
212 | 36.0k | } |
213 | 48.9k | return r; |
214 | | } |
215 | 0 | HTTP2Decompresser::Deflate(ref mut df_decoder) => { |
216 | 0 | let r = http2_decompress(&mut *df_decoder.as_mut(), input, output); |
217 | 0 | if r.is_err() { |
218 | 0 | self.decoder = HTTP2Decompresser::Unassigned; |
219 | 0 | } |
220 | 0 | return r; |
221 | | } |
222 | 476k | _ => {} |
223 | | } |
224 | 476k | return Ok(input); |
225 | 577k | } |
226 | | } |
227 | | |
228 | | #[derive(Debug)] |
229 | | pub struct HTTP2Decoder { |
230 | | decoder_tc: HTTP2DecoderHalf, |
231 | | decoder_ts: HTTP2DecoderHalf, |
232 | | } |
233 | | |
234 | | impl HTTP2Decoder { |
235 | 811k | pub fn new() -> HTTP2Decoder { |
236 | 811k | HTTP2Decoder { |
237 | 811k | decoder_tc: HTTP2DecoderHalf::new(), |
238 | 811k | decoder_ts: HTTP2DecoderHalf::new(), |
239 | 811k | } |
240 | 811k | } |
241 | | |
242 | 114k | pub fn http2_encoding_fromvec(&mut self, input: &[u8], dir: Direction) { |
243 | 114k | if dir == Direction::ToClient { |
244 | 92.6k | self.decoder_tc.http2_encoding_fromvec(input); |
245 | 92.6k | } else { |
246 | 22.0k | self.decoder_ts.http2_encoding_fromvec(input); |
247 | 22.0k | } |
248 | 114k | } |
249 | | |
250 | 577k | pub fn decompress<'a>( |
251 | 577k | &mut self, input: &'a [u8], output: &'a mut Vec<u8>, dir: Direction, |
252 | 577k | ) -> io::Result<&'a [u8]> { |
253 | 577k | if dir == Direction::ToClient { |
254 | 396k | return self.decoder_tc.decompress(input, output); |
255 | | } else { |
256 | 180k | return self.decoder_ts.decompress(input, output); |
257 | | } |
258 | 577k | } |
259 | | } |