Coverage Report

Created: 2025-11-16 07:09

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