Coverage Report

Created: 2026-02-14 06:42

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/suricata7/rust/src/http2/range.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 super::detect;
19
use crate::core::{
20
    Direction, Flow, HttpRangeContainerBlock, StreamingBufferConfig, SuricataFileContext, SC,
21
};
22
use crate::http2::http2::HTTP2Transaction;
23
use crate::http2::http2::SURICATA_HTTP2_FILE_CONFIG;
24
25
use nom7::branch::alt;
26
use nom7::bytes::streaming::{take_till, take_while};
27
use nom7::character::complete::{char, digit1};
28
use nom7::combinator::{map_res, value};
29
use nom7::error::{make_error, ErrorKind};
30
use nom7::{Err, IResult};
31
use std::os::raw::c_uchar;
32
use std::str::FromStr;
33
34
#[derive(Debug)]
35
#[repr(C)]
36
pub struct HTTPContentRange {
37
    pub start: i64,
38
    pub end: i64,
39
    pub size: i64,
40
}
41
42
27.0k
pub fn http2_parse_content_range_star(input: &[u8]) -> IResult<&[u8], HTTPContentRange> {
43
27.0k
    let (i2, _) = char('*')(input)?;
44
1.23k
    let (i2, _) = char('/')(i2)?;
45
869
    let (i2, size) = map_res(map_res(digit1, std::str::from_utf8), i64::from_str)(i2)?;
46
564
    return Ok((
47
564
        i2,
48
564
        HTTPContentRange {
49
564
            start: -1,
50
564
            end: -1,
51
564
            size,
52
564
        },
53
564
    ));
54
27.0k
}
55
56
26.5k
pub fn http2_parse_content_range_def(input: &[u8]) -> IResult<&[u8], HTTPContentRange> {
57
26.5k
    let (i2, start) = map_res(map_res(digit1, std::str::from_utf8), i64::from_str)(input)?;
58
24.5k
    let (i2, _) = char('-')(i2)?;
59
23.5k
    let (i2, end) = map_res(map_res(digit1, std::str::from_utf8), i64::from_str)(i2)?;
60
23.1k
    let (i2, _) = char('/')(i2)?;
61
22.6k
    let (i2, size) = alt((
62
22.6k
        value(-1, char('*')),
63
22.6k
        map_res(map_res(digit1, std::str::from_utf8), i64::from_str),
64
22.6k
    ))(i2)?;
65
21.9k
    return Ok((i2, HTTPContentRange { start, end, size }));
66
26.5k
}
67
68
29.7k
fn http2_parse_content_range(input: &[u8]) -> IResult<&[u8], HTTPContentRange> {
69
52.9k
    let (i2, _) = take_while(|c| c == b' ')(input)?;
70
1.61M
    let (i2, _) = take_till(|c| c == b' ')(i2)?;
71
72.7k
    let (i2, _) = take_while(|c| c == b' ')(i2)?;
72
27.0k
    return alt((
73
27.0k
        http2_parse_content_range_star,
74
27.0k
        http2_parse_content_range_def,
75
27.0k
    ))(i2);
76
29.7k
}
77
78
25.5k
pub fn http2_parse_check_content_range(input: &[u8]) -> IResult<&[u8], HTTPContentRange> {
79
25.5k
    let (rem, v) = http2_parse_content_range(input)?;
80
18.5k
    if v.start > v.end || (v.end > 0 && v.size > 0 && v.end > v.size - 1) {
81
367
        return Err(Err::Error(make_error(rem, ErrorKind::Verify)));
82
18.1k
    }
83
18.1k
    return Ok((rem, v));
84
25.5k
}
85
86
#[no_mangle]
87
4.12k
pub unsafe extern "C" fn rs_http_parse_content_range(
88
4.12k
    cr: &mut HTTPContentRange, buffer: *const u8, buffer_len: u32,
89
4.12k
) -> std::os::raw::c_int {
90
4.12k
    let slice = build_slice!(buffer, buffer_len as usize);
91
4.12k
    match http2_parse_content_range(slice) {
92
3.97k
        Ok((_, c)) => {
93
3.97k
            *cr = c;
94
3.97k
            return 0;
95
        }
96
        _ => {
97
142
            return -1;
98
        }
99
    }
100
4.12k
}
101
102
17.1k
fn http2_range_key_get(tx: &mut HTTP2Transaction) -> Result<(Vec<u8>, usize), ()> {
103
17.1k
    let hostv = detect::http2_frames_get_header_value_vec(tx, Direction::ToServer, ":authority")?;
104
14.4k
    let mut hostv = &hostv[..];
105
3.54M
    if let Some(p) = hostv.iter().position(|&x| x == b':') {
106
1.71k
        hostv = &hostv[..p];
107
12.7k
    }
108
14.4k
    let uriv = detect::http2_frames_get_header_value_vec(tx, Direction::ToServer, ":path")?;
109
14.1k
    let mut uriv = &uriv[..];
110
28.1M
    if let Some(p) = uriv.iter().position(|&x| x == b'?') {
111
1.94k
        uriv = &uriv[..p];
112
12.2k
    }
113
1.33M
    if let Some(p) = uriv.iter().rposition(|&x| x == b'/') {
114
9.69k
        uriv = &uriv[p..];
115
9.69k
    }
116
14.1k
    let mut r = Vec::with_capacity(hostv.len() + uriv.len());
117
14.1k
    r.extend_from_slice(hostv);
118
14.1k
    r.extend_from_slice(uriv);
119
14.1k
    return Ok((r, hostv.len()));
120
17.1k
}
121
122
18.1k
pub fn http2_range_open(
123
18.1k
    tx: &mut HTTP2Transaction, v: &HTTPContentRange, flow: *const Flow,
124
18.1k
    cfg: &'static SuricataFileContext, dir: Direction, data: &[u8],
125
18.1k
) {
126
18.1k
    if v.end <= 0 || v.size <= 0 {
127
        // skipped for incomplete range information
128
930
        return;
129
17.2k
    }
130
17.2k
    if v.end == v.size - 1 && v.start == 0 {
131
        // whole file in one range
132
66
        return;
133
17.1k
    }
134
17.1k
    let flags = if dir == Direction::ToServer { tx.ft_ts.file_flags } else { tx.ft_tc.file_flags };
135
17.1k
    if let Ok((key, index)) = http2_range_key_get(tx) {
136
14.1k
        let name = &key[index..];
137
14.1k
        tx.file_range = unsafe {
138
14.1k
            HttpRangeContainerOpenFile(
139
14.1k
                key.as_ptr(),
140
14.1k
                key.len() as u32,
141
14.1k
                flow,
142
14.1k
                v,
143
14.1k
                cfg.files_sbcfg,
144
14.1k
                name.as_ptr(),
145
14.1k
                name.len() as u16,
146
14.1k
                flags,
147
14.1k
                data.as_ptr(),
148
14.1k
                data.len() as u32,
149
14.1k
            )
150
14.1k
        };
151
14.1k
    }
152
18.1k
}
153
154
5.14k
pub fn http2_range_append(cfg: &'static SuricataFileContext, fr: *mut HttpRangeContainerBlock, data: &[u8]) {
155
5.14k
    unsafe {
156
5.14k
        HttpRangeAppendData(cfg.files_sbcfg, fr, data.as_ptr(), data.len() as u32);
157
5.14k
    }
158
5.14k
}
159
160
2.54k
pub fn http2_range_close(
161
2.54k
    tx: &mut HTTP2Transaction, dir: Direction, data: &[u8],
162
2.54k
) {
163
2.54k
    let added = if let Some(c) = unsafe { SC } {
164
2.54k
        if let Some(sfcm) = unsafe { SURICATA_HTTP2_FILE_CONFIG } {
165
2.54k
            let (files, flags) = if dir == Direction::ToServer {
166
0
                (&mut tx.ft_ts.file, tx.ft_ts.file_flags)
167
            } else {
168
2.54k
                (&mut tx.ft_tc.file, tx.ft_tc.file_flags)
169
            };
170
2.54k
            let added = (c.HTPFileCloseHandleRange)(
171
2.54k
                    sfcm.files_sbcfg,
172
2.54k
                    files,
173
2.54k
                    flags,
174
2.54k
                    tx.file_range,
175
2.54k
                    data.as_ptr(),
176
2.54k
                    data.len() as u32,
177
2.54k
                    );
178
2.54k
            (c.HttpRangeFreeBlock)(tx.file_range);
179
2.54k
            added
180
        } else {
181
0
            false
182
        }
183
    } else {
184
0
        false
185
    };
186
2.54k
    tx.file_range = std::ptr::null_mut();
187
2.54k
    if added {
188
18
        tx.tx_data.incr_files_opened();
189
2.52k
    }
190
2.54k
}
191
192
// Defined in app-layer-htp-range.h
193
extern "C" {
194
    pub fn HttpRangeContainerOpenFile(
195
        key: *const c_uchar, keylen: u32, f: *const Flow, cr: &HTTPContentRange,
196
        sbcfg: *const StreamingBufferConfig, name: *const c_uchar, name_len: u16, flags: u16,
197
        data: *const c_uchar, data_len: u32,
198
    ) -> *mut HttpRangeContainerBlock;
199
    pub fn HttpRangeAppendData(
200
        cfg: *const StreamingBufferConfig, c: *mut HttpRangeContainerBlock, data: *const c_uchar, data_len: u32,
201
    ) -> std::os::raw::c_int;
202
}
203
204
#[cfg(test)]
205
mod tests {
206
207
    use super::*;
208
209
    #[test]
210
    fn test_http2_parse_content_range() {
211
        let buf0: &[u8] = " bytes */100".as_bytes();
212
        let r0 = http2_parse_content_range(buf0);
213
        match r0 {
214
            Ok((rem, rg)) => {
215
                // Check the first message.
216
                assert_eq!(rg.start, -1);
217
                assert_eq!(rg.end, -1);
218
                assert_eq!(rg.size, 100);
219
                // And we should have no bytes left.
220
                assert_eq!(rem.len(), 0);
221
            }
222
            _ => {
223
                panic!("Result should have been ok.");
224
            }
225
        }
226
227
        let buf1: &[u8] = " bytes 10-20/200".as_bytes();
228
        let r1 = http2_parse_content_range(buf1);
229
        match r1 {
230
            Ok((rem, rg)) => {
231
                // Check the first message.
232
                assert_eq!(rg.start, 10);
233
                assert_eq!(rg.end, 20);
234
                assert_eq!(rg.size, 200);
235
                // And we should have no bytes left.
236
                assert_eq!(rem.len(), 0);
237
            }
238
            _ => {
239
                panic!("Result should have been ok.");
240
            }
241
        }
242
243
        let buf2: &[u8] = " bytes 30-68/*".as_bytes();
244
        let r2 = http2_parse_content_range(buf2);
245
        match r2 {
246
            Ok((rem, rg)) => {
247
                // Check the first message.
248
                assert_eq!(rg.start, 30);
249
                assert_eq!(rg.end, 68);
250
                assert_eq!(rg.size, -1);
251
                // And we should have no bytes left.
252
                assert_eq!(rem.len(), 0);
253
            }
254
            _ => {
255
                panic!("Result should have been ok.");
256
            }
257
        }
258
    }
259
}