Coverage Report

Created: 2025-12-31 06:43

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}