• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

jasonish / suricata / 22802647571

07 Mar 2026 04:23PM UTC coverage: 75.827% (-3.4%) from 79.233%
22802647571

push

github

jasonish
github-ci: add schema ordering check for yaml schema

253365 of 334137 relevant lines covered (75.83%)

3384729.69 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

71.69
/rust/src/http2/http2.rs
1
/* Copyright (C) 2020-2022 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::decompression;
19
use super::detect;
20
use super::parser;
21
use super::range;
22

23
use super::range::{SCHTPFileCloseHandleRange, SCHttpRangeFreeBlock};
24
use crate::applayer::{self, *};
25
use crate::conf::conf_get;
26
use crate::core::*;
27
use crate::direction::Direction;
28
use crate::dns::dns::DnsVariant;
29
use crate::filecontainer::FileContainerWrapper;
30
use crate::filetracker::*;
31
use crate::flow::Flow;
32
use crate::frames::Frame;
33

34
use crate::dns::dns::{dns_parse_request, dns_parse_response, DNSTransaction};
35

36
use nom7::Err;
37
use std;
38
use std::collections::VecDeque;
39
use std::ffi::CString;
40
use std::fmt;
41
use std::io;
42
use suricata_sys::sys::{
43
    AppLayerParserState, AppProto, HttpRangeContainerBlock, SCAppLayerForceProtocolChange,
44
    SCAppLayerParserConfParserEnabled, SCAppLayerParserRegisterLogger,
45
    SCAppLayerProtoDetectConfProtoDetectionEnabled, SCFileFlowFlagsToFlags,
46
    SCHTTP2MimicHttp1Request,
47
};
48

49
static mut ALPROTO_HTTP2: AppProto = ALPROTO_UNKNOWN;
50
static mut ALPROTO_DOH2: AppProto = ALPROTO_UNKNOWN;
51

52
const HTTP2_DEFAULT_MAX_FRAME_SIZE: u32 = 16384;
53
const HTTP2_MAX_HANDLED_FRAME_SIZE: usize = 65536;
54
const HTTP2_MIN_HANDLED_FRAME_SIZE: usize = 256;
55

56
pub static mut SURICATA_HTTP2_FILE_CONFIG: Option<&'static SuricataFileContext> = None;
57

58
#[no_mangle]
59
pub extern "C" fn SCHttp2Init(context: &'static mut SuricataFileContext) {
2,195✔
60
    unsafe {
2,195✔
61
        SURICATA_HTTP2_FILE_CONFIG = Some(context);
2,195✔
62
    }
2,195✔
63
}
2,195✔
64

65
#[repr(u8)]
66
#[derive(Copy, Clone, PartialOrd, PartialEq, Eq)]
67
pub enum HTTP2ConnectionState {
68
    Http2StateInit = 0,
69
    Http2StateMagicDone = 1,
70
}
71

72
const HTTP2_FRAME_HEADER_LEN: usize = 9;
73
const HTTP2_MAGIC_LEN: usize = 24;
74
const HTTP2_FRAME_GOAWAY_LEN: usize = 8;
75
const HTTP2_FRAME_RSTSTREAM_LEN: usize = 4;
76
const HTTP2_FRAME_PRIORITY_LEN: usize = 5;
77
const HTTP2_FRAME_WINDOWUPDATE_LEN: usize = 4;
78
pub static mut HTTP2_MAX_TABLESIZE: u32 = 65536; // 0x10000
79
                                                 // maximum size of reassembly for header + continuation
80
static mut HTTP2_MAX_REASS: usize = 102400;
81
static mut HTTP2_MAX_STREAMS: usize = 4096; // 0x1000
82

83
#[derive(AppLayerFrameType)]
84
pub enum Http2FrameType {
85
    Hdr,
86
    Data,
87
    Pdu,
88
}
89

90
#[repr(u8)]
91
#[derive(Copy, Clone, PartialOrd, PartialEq, Eq, Debug)]
92
pub enum HTTP2FrameUnhandledReason {
93
    UnknownType = 0,
94
    TooLong = 1,
95
    ParsingError = 2,
96
    Incomplete = 3,
97
}
98

99
impl fmt::Display for HTTP2FrameUnhandledReason {
100
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
×
101
        write!(f, "{:?}", self)
×
102
    }
×
103
}
104

105
#[derive(Debug)]
106
pub struct HTTP2FrameUnhandled {
107
    pub reason: HTTP2FrameUnhandledReason,
108
}
109

110
#[derive(Debug)]
111
pub enum HTTP2FrameTypeData {
112
    PRIORITY(parser::HTTP2FramePriority),
113
    GOAWAY(parser::HTTP2FrameGoAway),
114
    RSTSTREAM(parser::HTTP2FrameRstStream),
115
    SETTINGS(Vec<parser::HTTP2FrameSettings>),
116
    WINDOWUPDATE(parser::HTTP2FrameWindowUpdate),
117
    HEADERS(parser::HTTP2FrameHeaders),
118
    PUSHPROMISE(parser::HTTP2FramePushPromise),
119
    CONTINUATION(parser::HTTP2FrameContinuation),
120
    PING,
121
    DATA,
122
    //not a defined frame
123
    UNHANDLED(HTTP2FrameUnhandled),
124
}
125

126
#[repr(u8)]
127
#[derive(Copy, Clone, PartialOrd, PartialEq, Eq, Debug)]
128
pub enum HTTP2TransactionState {
129
    HTTP2StateIdle = 0,
130
    HTTP2StateOpen = 1,
131
    HTTP2StateReserved = 2,
132
    HTTP2StateDataClient = 3,
133
    HTTP2StateHalfClosedClient = 4,
134
    HTTP2StateDataServer = 5,
135
    HTTP2StateHalfClosedServer = 6,
136
    HTTP2StateClosed = 7,
137
    //not a RFC-defined state, used for stream 0 frames applying to the global connection
138
    HTTP2StateGlobal = 8,
139
    //not a RFC-defined state, dropping this old tx because we have too many
140
    HTTP2StateTodrop = 9,
141
}
142

143
#[derive(Debug)]
144
pub struct HTTP2Frame {
145
    pub header: parser::HTTP2FrameHeader,
146
    pub data: HTTP2FrameTypeData,
147
}
148

149
#[derive(Debug, Default)]
150
/// Dns Over HTTP2 Data inside a HTTP2 transaction
151
pub struct DohHttp2Tx {
152
    /// wether the HTTP2 data is DNS, for both directions
153
    is_doh_data: [bool; 2],
154
    /// http2 data buffer to parse as DNS on completion
155
    pub data_buf: [Vec<u8>; 2],
156
    /// dns request transation
157
    pub dns_request_tx: Option<DNSTransaction>,
158
    /// dns response transation
159
    pub dns_response_tx: Option<DNSTransaction>,
160
}
161

162
#[derive(Debug)]
163
pub struct HTTP2Transaction {
164
    tx_id: u64,
165
    pub stream_id: u32,
166
    pub state: HTTP2TransactionState,
167
    child_stream_id: u32,
168

169
    pub frames_tc: Vec<HTTP2Frame>,
170
    pub frames_ts: Vec<HTTP2Frame>,
171

172
    decoder: decompression::HTTP2Decoder,
173
    pub file_range: *mut HttpRangeContainerBlock,
174

175
    pub tx_data: AppLayerTxData,
176
    pub ft_tc: FileTransferTracker,
177
    pub ft_ts: FileTransferTracker,
178

179
    //temporary escaped header for detection
180
    //must be attached to transaction for memory management (be freed at the right time)
181
    pub escaped: Vec<Vec<u8>>,
182
    pub req_line: Vec<u8>,
183
    pub resp_line: Vec<u8>,
184

185
    pub doh: Option<DohHttp2Tx>,
186
}
187

188
impl Transaction for HTTP2Transaction {
189
    fn id(&self) -> u64 {
29,813✔
190
        self.tx_id
29,813✔
191
    }
29,813✔
192
}
193

194
impl Default for HTTP2Transaction {
195
    fn default() -> Self {
×
196
        Self::new()
×
197
    }
×
198
}
199

200
impl HTTP2Transaction {
201
    pub fn new() -> Self {
520✔
202
        Self {
520✔
203
            tx_id: 0,
520✔
204
            stream_id: 0,
520✔
205
            child_stream_id: 0,
520✔
206
            state: HTTP2TransactionState::HTTP2StateIdle,
520✔
207
            frames_tc: Vec::new(),
520✔
208
            frames_ts: Vec::new(),
520✔
209
            decoder: decompression::HTTP2Decoder::new(),
520✔
210
            file_range: std::ptr::null_mut(),
520✔
211
            tx_data: AppLayerTxData::new(),
520✔
212
            ft_tc: FileTransferTracker::new(),
520✔
213
            ft_ts: FileTransferTracker::new(),
520✔
214
            escaped: Vec::with_capacity(16),
520✔
215
            req_line: Vec::new(),
520✔
216
            resp_line: Vec::new(),
520✔
217
            doh: None,
520✔
218
        }
520✔
219
    }
520✔
220

221
    pub fn free(&mut self) {
520✔
222
        if !self.file_range.is_null() {
520✔
223
            if let Some(sfcm) = unsafe { SURICATA_HTTP2_FILE_CONFIG } {
×
224
                //TODO get a file container instead of NULL
×
225
                unsafe {
×
226
                    SCHTPFileCloseHandleRange(
×
227
                        sfcm.files_sbcfg,
×
228
                        std::ptr::null_mut(),
×
229
                        0,
×
230
                        self.file_range,
×
231
                        std::ptr::null_mut(),
×
232
                        0,
×
233
                    );
×
234
                    SCHttpRangeFreeBlock(self.file_range);
×
235
                }
×
236
                self.file_range = std::ptr::null_mut();
×
237
            }
×
238
        }
520✔
239
    }
520✔
240

241
    pub fn set_event(&mut self, event: HTTP2Event) {
8✔
242
        self.tx_data.set_event(event as u8);
8✔
243
    }
8✔
244

245
    fn handle_headers(
339✔
246
        &mut self, blocks: &[parser::HTTP2FrameHeaderBlock], dir: Direction,
339✔
247
    ) -> Option<Vec<u8>> {
339✔
248
        let mut authority = None;
339✔
249
        let mut path = None;
339✔
250
        let mut doh = false;
339✔
251
        let mut host = None;
339✔
252
        for block in blocks {
3,682✔
253
            if block.name.as_ref() == b"content-encoding" {
3,343✔
254
                self.decoder.http2_encoding_fromvec(&block.value, dir);
23✔
255
            } else if block.name.as_ref() == b"accept" {
3,320✔
256
                //TODO? faster pattern matching
257
                if block.value.as_ref() == b"application/dns-message" {
149✔
258
                    doh = true;
76✔
259
                }
92✔
260
            } else if block.name.as_ref() == b"content-type" {
3,171✔
261
                if block.value.as_ref() == b"application/dns-message" {
217✔
262
                    if let Some(doh) = &mut self.doh {
146✔
263
                        doh.is_doh_data[dir.index()] = true;
76✔
264
                    } else {
76✔
265
                        let mut doh = DohHttp2Tx::default();
70✔
266
                        doh.is_doh_data[dir.index()] = true;
70✔
267
                        self.doh = Some(doh);
70✔
268
                    }
70✔
269
                }
71✔
270
            } else if block.name.as_ref() == b":path" {
2,954✔
271
                path = Some(&block.value);
156✔
272
            } else if block.name.eq_ignore_ascii_case(b":authority") {
2,798✔
273
                authority = Some(&block.value);
156✔
274
                if block.value.contains(&b'@') {
156✔
275
                    // it is forbidden by RFC 9113 to have userinfo in this field
2✔
276
                    // when in HTTP1 we can have user:password@domain.com
2✔
277
                    self.set_event(HTTP2Event::UserinfoInUri);
2✔
278
                }
154✔
279
            } else if block.name.eq_ignore_ascii_case(b"host") {
2,642✔
280
                host = Some(&block.value);
4✔
281
            }
2,638✔
282
        }
283
        if let Some(a) = authority {
339✔
284
            if let Some(h) = host {
156✔
285
                if !a.eq_ignore_ascii_case(h) {
4✔
286
                    // The event is triggered only if both headers
4✔
287
                    // are in the same frame to avoid excessive
4✔
288
                    // complexity at runtime.
4✔
289
                    self.set_event(HTTP2Event::AuthorityHostMismatch);
4✔
290
                }
4✔
291
            }
152✔
292
        }
183✔
293
        if doh && unsafe { ALPROTO_DOH2 } != ALPROTO_UNKNOWN {
339✔
294
            if let Some(p) = path {
76✔
295
                if let Ok((_, dns_req)) = parser::doh_extract_request(p) {
76✔
296
                    return Some(dns_req);
8✔
297
                }
68✔
298
            }
×
299
        }
263✔
300
        return None;
331✔
301
    }
339✔
302

303
    pub fn update_file_flags(&mut self, flow_file_flags: u16) {
2,510✔
304
        self.ft_ts.file_flags = unsafe { SCFileFlowFlagsToFlags(flow_file_flags, STREAM_TOSERVER) };
2,510✔
305
        self.ft_tc.file_flags = unsafe { SCFileFlowFlagsToFlags(flow_file_flags, STREAM_TOCLIENT) };
2,510✔
306
    }
2,510✔
307

308
    fn decompress<'a>(
1,136✔
309
        &'a mut self, input: &'a [u8], output: &'a mut Vec<u8>, dir: Direction,
1,136✔
310
        sfcm: &'static SuricataFileContext, over: bool, flow: *const Flow,
1,136✔
311
    ) -> io::Result<()> {
1,136✔
312
        let decompressed = self.decoder.decompress(input, output, dir)?;
1,136✔
313
        let xid: u32 = self.tx_id as u32;
1,136✔
314
        if dir == Direction::ToClient {
1,136✔
315
            self.ft_tc.tx_id = self.tx_id - 1;
1,030✔
316
            // Check that we are at the beginning of the file
1,030✔
317
            if !self.ft_tc.is_initialized() {
1,030✔
318
                // we are now sure that new_chunk will open a file
319
                // even if it may close it right afterwards
320
                self.tx_data.incr_files_opened();
175✔
321
                if let Ok(value) = detect::http2_frames_get_header_value_vec(
175✔
322
                    self,
175✔
323
                    Direction::ToClient,
175✔
324
                    "content-range",
175✔
325
                ) {
175✔
326
                    match range::http2_parse_check_content_range(&value) {
12✔
327
                        Ok((_, v)) => {
12✔
328
                            range::http2_range_open(
12✔
329
                                self,
12✔
330
                                &v,
12✔
331
                                flow,
12✔
332
                                sfcm,
12✔
333
                                Direction::ToClient,
12✔
334
                                decompressed,
12✔
335
                            );
12✔
336
                            if over && !self.file_range.is_null() {
12✔
337
                                range::http2_range_close(self, Direction::ToClient, &[])
12✔
338
                            }
×
339
                        }
340
                        _ => {
×
341
                            self.set_event(HTTP2Event::InvalidRange);
×
342
                        }
×
343
                    }
344
                }
163✔
345
            } else if !self.file_range.is_null() {
855✔
346
                if over {
×
347
                    range::http2_range_close(self, Direction::ToClient, decompressed)
×
348
                } else {
349
                    range::http2_range_append(sfcm, self.file_range, decompressed)
×
350
                }
351
            }
855✔
352
            self.ft_tc.new_chunk(
1,030✔
353
                sfcm,
1,030✔
354
                b"",
1,030✔
355
                decompressed,
1,030✔
356
                self.ft_tc.tracked, //offset = append
1,030✔
357
                decompressed.len() as u32,
1,030✔
358
                0,
1,030✔
359
                over,
1,030✔
360
                &xid,
1,030✔
361
            );
1,030✔
362
        } else {
363
            self.ft_ts.tx_id = self.tx_id - 1;
106✔
364
            if !self.ft_ts.file_open {
106✔
365
                self.tx_data.incr_files_opened();
84✔
366
            }
84✔
367
            self.ft_ts.new_chunk(
106✔
368
                sfcm,
106✔
369
                b"",
106✔
370
                decompressed,
106✔
371
                self.ft_ts.tracked, //offset = append
106✔
372
                decompressed.len() as u32,
106✔
373
                0,
106✔
374
                over,
106✔
375
                &xid,
106✔
376
            );
106✔
377
        };
378
        if unsafe { ALPROTO_DOH2 } != ALPROTO_UNKNOWN {
1,136✔
379
            // we store DNS response, and process it when complete
380
            if let Some(doh) = &mut self.doh {
1,136✔
381
                if doh.is_doh_data[dir.index()] {
242✔
382
                    if doh.data_buf[dir.index()].len() + decompressed.len() <= 0xFFFF {
232✔
383
                        // a DNS message is U16_MAX
230✔
384
                        doh.data_buf[dir.index()].extend_from_slice(decompressed);
230✔
385
                    } else {
230✔
386
                        // stop processing further data
387
                        doh.is_doh_data[dir.index()] = false;
2✔
388
                        if dir == Direction::ToClient {
2✔
389
                            self.set_event(HTTP2Event::DnsResponseTooLong);
×
390
                        } else {
2✔
391
                            self.set_event(HTTP2Event::DnsRequestTooLong);
2✔
392
                        }
2✔
393
                    }
394
                }
10✔
395
            }
894✔
396
        }
×
397
        return Ok(());
1,136✔
398
    }
1,136✔
399

400
    fn handle_frame(
1,904✔
401
        &mut self, header: &parser::HTTP2FrameHeader, data: &HTTP2FrameTypeData, dir: Direction,
1,904✔
402
    ) -> Option<Vec<u8>> {
1,904✔
403
        //handle child_stream_id changes
1,904✔
404
        let mut r = None;
1,904✔
405
        match data {
1,904✔
406
            HTTP2FrameTypeData::PUSHPROMISE(hs) => {
×
407
                if dir == Direction::ToClient {
×
408
                    //we could set an event if self.child_stream_id != 0
409
                    if header.flags & parser::HTTP2_FLAG_HEADER_END_HEADERS == 0 {
×
410
                        self.child_stream_id = hs.stream_id;
×
411
                    }
×
412
                    self.state = HTTP2TransactionState::HTTP2StateReserved;
×
413
                }
×
414
                r = self.handle_headers(&hs.blocks, dir);
×
415
            }
416
            HTTP2FrameTypeData::CONTINUATION(hs) => {
4✔
417
                if dir == Direction::ToClient
4✔
418
                    && header.flags & parser::HTTP2_FLAG_HEADER_END_HEADERS != 0
×
419
                {
×
420
                    self.child_stream_id = 0;
×
421
                }
4✔
422
                r = self.handle_headers(&hs.blocks, dir);
4✔
423
            }
424
            HTTP2FrameTypeData::HEADERS(hs) => {
335✔
425
                if dir == Direction::ToClient {
335✔
426
                    self.child_stream_id = 0;
179✔
427
                }
179✔
428
                r = self.handle_headers(&hs.blocks, dir);
335✔
429
            }
430
            HTTP2FrameTypeData::RSTSTREAM(_) => {
36✔
431
                self.child_stream_id = 0;
36✔
432
            }
36✔
433
            _ => {}
1,529✔
434
        }
435
        //handle closing state changes
436
        match data {
1,904✔
437
            HTTP2FrameTypeData::HEADERS(_) | HTTP2FrameTypeData::DATA => {
438
                if header.flags & parser::HTTP2_FLAG_HEADER_EOS != 0 {
1,471✔
439
                    match self.state {
329✔
440
                        HTTP2TransactionState::HTTP2StateHalfClosedClient
441
                        | HTTP2TransactionState::HTTP2StateDataServer => {
442
                            if dir == Direction::ToClient {
175✔
443
                                self.state = HTTP2TransactionState::HTTP2StateClosed;
173✔
444
                            }
173✔
445
                        }
446
                        HTTP2TransactionState::HTTP2StateHalfClosedServer => {
447
                            if dir == Direction::ToServer {
×
448
                                self.state = HTTP2TransactionState::HTTP2StateClosed;
×
449
                            }
×
450
                        }
451
                        // do not revert back to a half closed state
452
                        HTTP2TransactionState::HTTP2StateClosed => {}
×
453
                        HTTP2TransactionState::HTTP2StateGlobal => {}
×
454
                        _ => {
455
                            if dir == Direction::ToClient {
154✔
456
                                self.state = HTTP2TransactionState::HTTP2StateHalfClosedServer;
2✔
457
                            } else {
152✔
458
                                self.state = HTTP2TransactionState::HTTP2StateHalfClosedClient;
152✔
459
                            }
152✔
460
                        }
461
                    }
462
                } else if header.ftype == parser::HTTP2FrameType::Data as u8 {
1,142✔
463
                    //not end of stream
464
                    if dir == Direction::ToServer {
881✔
465
                        if self.state < HTTP2TransactionState::HTTP2StateDataClient {
22✔
466
                            self.state = HTTP2TransactionState::HTTP2StateDataClient;
4✔
467
                        }
18✔
468
                    } else if self.state < HTTP2TransactionState::HTTP2StateDataServer {
859✔
469
                        self.state = HTTP2TransactionState::HTTP2StateDataServer;
139✔
470
                    }
720✔
471
                }
261✔
472
            }
473
            _ => {}
433✔
474
        }
475
        return r;
1,904✔
476
    }
1,904✔
477

478
    fn handle_dns_data(&mut self, dir: Direction, flow: *mut Flow) {
255✔
479
        if let Some(doh) = &mut self.doh {
255✔
480
            if !doh.data_buf[dir.index()].is_empty() {
148✔
481
                if dir.is_to_client() {
146✔
482
                    if let Ok(mut dtx) = dns_parse_response(&doh.data_buf[dir.index()]) {
76✔
483
                        dtx.id = 1;
76✔
484
                        doh.dns_response_tx = Some(dtx);
76✔
485
                        unsafe {
76✔
486
                            SCAppLayerForceProtocolChange(flow, ALPROTO_DOH2);
76✔
487
                        }
76✔
488
                    }
×
489
                } else if let Ok(mut dtx) =
68✔
490
                    dns_parse_request(&doh.data_buf[dir.index()], &DnsVariant::Dns)
70✔
491
                {
492
                    dtx.id = 1;
68✔
493
                    doh.dns_request_tx = Some(dtx);
68✔
494
                    unsafe {
68✔
495
                        SCAppLayerForceProtocolChange(flow, ALPROTO_DOH2);
68✔
496
                    }
68✔
497
                }
2✔
498
            }
2✔
499
        }
107✔
500
    }
255✔
501
}
502

503
impl Drop for HTTP2Transaction {
504
    fn drop(&mut self) {
520✔
505
        if let Some(sfcm) = unsafe { SURICATA_HTTP2_FILE_CONFIG } {
520✔
506
            self.ft_ts.file.free(sfcm);
519✔
507
            self.ft_tc.file.free(sfcm);
519✔
508
        }
520✔
509
        self.free();
520✔
510
    }
520✔
511
}
512

513
#[derive(AppLayerEvent)]
176✔
514
pub enum HTTP2Event {
515
    InvalidFrameHeader,
516
    InvalidClientMagic,
517
    InvalidFrameData,
518
    InvalidHeader,
519
    InvalidFrameLength,
520
    ExtraHeaderData,
521
    LongFrameData,
522
    StreamIdReuse,
523
    InvalidHttp1Settings,
524
    FailedDecompression,
525
    InvalidRange,
526
    HeaderIntegerOverflow,
527
    TooManyStreams,
528
    AuthorityHostMismatch,
529
    UserinfoInUri,
530
    ReassemblyLimitReached,
531
    DnsRequestTooLong,
532
    DnsResponseTooLong,
533
    DataStreamZero,
534
}
535

536
pub struct HTTP2DynTable {
537
    pub table: Vec<parser::HTTP2FrameHeaderBlock>,
538
    pub current_size: usize,
539
    pub max_size: usize,
540
    pub overflow: u8,
541
}
542

543
impl Default for HTTP2DynTable {
544
    fn default() -> Self {
×
545
        Self::new()
×
546
    }
×
547
}
548

549
impl HTTP2DynTable {
550
    pub fn new() -> Self {
103✔
551
        Self {
103✔
552
            table: Vec::with_capacity(64),
103✔
553
            current_size: 0,
103✔
554
            max_size: 4096, //default value
103✔
555
            overflow: 0,
103✔
556
        }
103✔
557
    }
103✔
558
}
559

560
#[derive(Default)]
561
struct HTTP2HeaderReassemblyBuffer {
562
    data: Vec<u8>,
563
    stream_id: u32,
564
}
565

566
pub struct HTTP2State {
567
    state_data: AppLayerStateData,
568
    tx_id: u64,
569
    request_frame_size: u32,
570
    response_frame_size: u32,
571
    dynamic_headers_ts: HTTP2DynTable,
572
    dynamic_headers_tc: HTTP2DynTable,
573
    transactions: VecDeque<HTTP2Transaction>,
574
    progress: HTTP2ConnectionState,
575

576
    c2s_buf: HTTP2HeaderReassemblyBuffer,
577
    s2c_buf: HTTP2HeaderReassemblyBuffer,
578
}
579

580
impl State<HTTP2Transaction> for HTTP2State {
581
    fn get_transaction_count(&self) -> usize {
11,512✔
582
        self.transactions.len()
11,512✔
583
    }
11,512✔
584

585
    fn get_transaction_by_index(&self, index: usize) -> Option<&HTTP2Transaction> {
18,586✔
586
        self.transactions.get(index)
18,586✔
587
    }
18,586✔
588
}
589

590
impl Default for HTTP2State {
591
    fn default() -> Self {
×
592
        Self::new()
×
593
    }
×
594
}
595

596
impl HTTP2State {
597
    pub fn new() -> Self {
51✔
598
        Self {
51✔
599
            state_data: AppLayerStateData::default(),
51✔
600
            tx_id: 0,
51✔
601
            request_frame_size: 0,
51✔
602
            response_frame_size: 0,
51✔
603
            // the headers are encoded on one byte
51✔
604
            // with a fixed number of static headers, and
51✔
605
            // a variable number of dynamic headers
51✔
606
            dynamic_headers_ts: HTTP2DynTable::new(),
51✔
607
            dynamic_headers_tc: HTTP2DynTable::new(),
51✔
608
            transactions: VecDeque::new(),
51✔
609
            progress: HTTP2ConnectionState::Http2StateInit,
51✔
610
            c2s_buf: HTTP2HeaderReassemblyBuffer::default(),
51✔
611
            s2c_buf: HTTP2HeaderReassemblyBuffer::default(),
51✔
612
        }
51✔
613
    }
51✔
614

615
    pub fn free(&mut self) {
51✔
616
        // this should be in HTTP2Transaction::free
617
        // but we need state's file container cf https://redmine.openinfosecfoundation.org/issues/4444
618
        for tx in &mut self.transactions {
79✔
619
            if !tx.file_range.is_null() {
28✔
620
                if let Some(sfcm) = unsafe { SURICATA_HTTP2_FILE_CONFIG } {
×
621
                    unsafe {
×
622
                        SCHTPFileCloseHandleRange(
×
623
                            sfcm.files_sbcfg,
×
624
                            &mut tx.ft_tc.file,
×
625
                            0,
×
626
                            tx.file_range,
×
627
                            std::ptr::null_mut(),
×
628
                            0,
×
629
                        );
×
630
                        SCHttpRangeFreeBlock(tx.file_range);
×
631
                    }
×
632
                    tx.file_range = std::ptr::null_mut();
×
633
                }
×
634
            }
28✔
635
        }
636
        self.transactions.clear();
51✔
637
    }
51✔
638

639
    pub fn set_event(&mut self, event: HTTP2Event) {
×
640
        let len = self.transactions.len();
×
641
        if len == 0 {
×
642
            return;
×
643
        }
×
644
        let tx = &mut self.transactions[len - 1];
×
645
        tx.tx_data.set_event(event as u8);
×
646
    }
×
647

648
    // Free a transaction by ID.
649
    fn free_tx(&mut self, tx_id: u64) {
491✔
650
        let len = self.transactions.len();
491✔
651
        let mut found = false;
491✔
652
        let mut index = 0;
491✔
653
        for i in 0..len {
1,284✔
654
            let tx = &mut self.transactions[i];
1,284✔
655
            if tx.tx_id == tx_id + 1 {
1,284✔
656
                found = true;
491✔
657
                index = i;
491✔
658
                // this should be in HTTP2Transaction::free
491✔
659
                // but we need state's file container cf https://redmine.openinfosecfoundation.org/issues/4444
491✔
660
                if !tx.file_range.is_null() {
491✔
661
                    if let Some(sfcm) = unsafe { SURICATA_HTTP2_FILE_CONFIG } {
×
662
                        unsafe {
×
663
                            SCHTPFileCloseHandleRange(
×
664
                                sfcm.files_sbcfg,
×
665
                                &mut tx.ft_tc.file,
×
666
                                0,
×
667
                                tx.file_range,
×
668
                                std::ptr::null_mut(),
×
669
                                0,
×
670
                            );
×
671
                            SCHttpRangeFreeBlock(tx.file_range);
×
672
                        }
×
673
                        tx.file_range = std::ptr::null_mut();
×
674
                    }
×
675
                }
491✔
676
                break;
491✔
677
            }
793✔
678
        }
679
        if found {
491✔
680
            self.transactions.remove(index);
491✔
681
        }
491✔
682
    }
491✔
683

684
    pub fn get_tx(&mut self, tx_id: u64) -> Option<&HTTP2Transaction> {
762✔
685
        for tx in &mut self.transactions {
2,301✔
686
            if tx.tx_id == tx_id + 1 {
2,301✔
687
                tx.tx_data.update_file_flags(self.state_data.file_flags);
762✔
688
                tx.update_file_flags(tx.tx_data.0.file_flags);
762✔
689
                return Some(tx);
762✔
690
            }
1,539✔
691
        }
692
        return None;
×
693
    }
762✔
694

695
    fn find_tx_index(&mut self, sid: u32) -> usize {
2,884✔
696
        for i in 0..self.transactions.len() {
7,005✔
697
            //reverse order should be faster
698
            let idx = self.transactions.len() - 1 - i;
7,005✔
699
            if sid == self.transactions[idx].stream_id {
7,005✔
700
                return idx + 1;
2,695✔
701
            }
4,310✔
702
        }
703
        return 0;
189✔
704
    }
2,884✔
705

706
    fn find_child_stream_id(&mut self, sid: u32) -> u32 {
×
707
        for i in 0..self.transactions.len() {
×
708
            //reverse order should be faster
709
            if sid == self.transactions[self.transactions.len() - 1 - i].stream_id {
×
710
                if self.transactions[self.transactions.len() - 1 - i].child_stream_id > 0 {
×
711
                    return self.transactions[self.transactions.len() - 1 - i].child_stream_id;
×
712
                }
×
713
                return sid;
×
714
            }
×
715
        }
716
        return sid;
×
717
    }
×
718

719
    fn create_global_tx(&mut self) -> &mut HTTP2Transaction {
330✔
720
        //special transaction with only one frame
330✔
721
        //as it affects the global connection, there is no end to it
330✔
722
        let mut tx = HTTP2Transaction::new();
330✔
723
        self.tx_id += 1;
330✔
724
        tx.tx_id = self.tx_id;
330✔
725
        tx.state = HTTP2TransactionState::HTTP2StateGlobal;
330✔
726
        // a global tx (stream id 0) does not hold files cf RFC 9113 section 5.1.1
330✔
727
        self.transactions.push_back(tx);
330✔
728
        return self.transactions.back_mut().unwrap();
330✔
729
    }
330✔
730

731
    pub fn find_or_create_tx(
2,078✔
732
        &mut self, header: &parser::HTTP2FrameHeader, data: &HTTP2FrameTypeData, dir: Direction,
2,078✔
733
    ) -> Option<&mut HTTP2Transaction> {
2,078✔
734
        if header.stream_id == 0 {
2,078✔
735
            if self.transactions.len() >= unsafe { HTTP2_MAX_STREAMS } {
330✔
736
                for tx_old in &mut self.transactions {
×
737
                    if tx_old.state == HTTP2TransactionState::HTTP2StateTodrop {
×
738
                        // loop was already run
739
                        break;
×
740
                    }
×
741
                    tx_old.set_event(HTTP2Event::TooManyStreams);
×
742
                    // use a distinct state, even if we do not log it
×
743
                    tx_old.state = HTTP2TransactionState::HTTP2StateTodrop;
×
744
                    tx_old.tx_data.0.updated_tc = true;
×
745
                    tx_old.tx_data.0.updated_ts = true;
×
746
                }
747
                return None;
×
748
            }
330✔
749
            return Some(self.create_global_tx());
330✔
750
        }
1,748✔
751
        let sid = match data {
1,748✔
752
            //yes, the right stream_id for Suricata is not the header one
753
            HTTP2FrameTypeData::PUSHPROMISE(hs) => hs.stream_id,
×
754
            HTTP2FrameTypeData::CONTINUATION(_) => {
755
                if dir == Direction::ToClient {
4✔
756
                    //continuation of a push promise
757
                    self.find_child_stream_id(header.stream_id)
×
758
                } else {
759
                    header.stream_id
4✔
760
                }
761
            }
762
            _ => header.stream_id,
1,744✔
763
        };
764
        let index = self.find_tx_index(sid);
1,748✔
765
        if index > 0 {
1,748✔
766
            if self.transactions[index - 1].state == HTTP2TransactionState::HTTP2StateClosed {
1,559✔
767
                //these frames can be received in this state for a short period
768
                if header.ftype != parser::HTTP2FrameType::RstStream as u8
23✔
769
                    && header.ftype != parser::HTTP2FrameType::WindowUpdate as u8
7✔
770
                    && header.ftype != parser::HTTP2FrameType::Priority as u8
×
771
                {
×
772
                    self.set_event(HTTP2Event::StreamIdReuse);
×
773
                }
23✔
774
            }
1,536✔
775

776
            let tx = &mut self.transactions[index - 1];
1,559✔
777
            tx.tx_data.update_file_flags(self.state_data.file_flags);
1,559✔
778
            tx.update_file_flags(tx.tx_data.0.file_flags);
1,559✔
779
            tx.tx_data.0.updated_tc = true;
1,559✔
780
            tx.tx_data.0.updated_ts = true;
1,559✔
781
            return Some(tx);
1,559✔
782
        } else {
783
            // do not use SETTINGS_MAX_CONCURRENT_STREAMS as it can grow too much
784
            if self.transactions.len() >= unsafe { HTTP2_MAX_STREAMS } {
189✔
785
                for tx_old in &mut self.transactions {
×
786
                    if tx_old.state == HTTP2TransactionState::HTTP2StateTodrop {
×
787
                        // loop was already run
788
                        break;
×
789
                    }
×
790
                    tx_old.set_event(HTTP2Event::TooManyStreams);
×
791
                    // use a distinct state, even if we do not log it
×
792
                    tx_old.state = HTTP2TransactionState::HTTP2StateTodrop;
×
793
                    tx_old.tx_data.0.updated_tc = true;
×
794
                    tx_old.tx_data.0.updated_ts = true;
×
795
                }
796
                return None;
×
797
            }
189✔
798
            let mut tx = HTTP2Transaction::new();
189✔
799
            self.tx_id += 1;
189✔
800
            tx.tx_id = self.tx_id;
189✔
801
            tx.stream_id = sid;
189✔
802
            tx.state = HTTP2TransactionState::HTTP2StateOpen;
189✔
803
            tx.tx_data.update_file_flags(self.state_data.file_flags);
189✔
804
            tx.update_file_flags(tx.tx_data.0.file_flags);
189✔
805
            tx.tx_data.0.file_tx = STREAM_TOSERVER | STREAM_TOCLIENT; // might hold files in both directions
189✔
806
            self.transactions.push_back(tx);
189✔
807
            return Some(self.transactions.back_mut().unwrap());
189✔
808
        }
809
    }
2,078✔
810

811
    fn process_headers(&mut self, blocks: &Vec<parser::HTTP2FrameHeaderBlock>, dir: Direction) {
335✔
812
        let (mut update, mut sizeup) = (false, 0);
335✔
813
        for block in blocks {
3,678✔
814
            if block.error >= parser::HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeError {
3,343✔
815
                self.set_event(HTTP2Event::InvalidHeader);
×
816
            } else if block.error == parser::HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSizeUpdate {
3,343✔
817
                update = true;
31✔
818
                if block.sizeupdate > sizeup {
31✔
819
                    sizeup = block.sizeupdate;
24✔
820
                }
24✔
821
            } else if block.error
3,312✔
822
                == parser::HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeIntegerOverflow
3,312✔
823
            {
×
824
                self.set_event(HTTP2Event::HeaderIntegerOverflow);
×
825
            }
3,312✔
826
        }
827
        if update {
335✔
828
            //borrow checker forbids to pass directly dyn_headers
829
            let dyn_headers = if dir == Direction::ToClient {
24✔
830
                &mut self.dynamic_headers_tc
19✔
831
            } else {
832
                &mut self.dynamic_headers_ts
5✔
833
            };
834
            dyn_headers.max_size = sizeup as usize;
24✔
835
        }
311✔
836
    }
335✔
837

838
    fn parse_frame_data(
1,904✔
839
        &mut self, head: &parser::HTTP2FrameHeader, input: &[u8], complete: bool, dir: Direction,
1,904✔
840
        reass_limit_reached: &mut bool,
1,904✔
841
    ) -> HTTP2FrameTypeData {
1,904✔
842
        let ftype = head.ftype;
1,904✔
843
        let hflags = head.flags;
1,904✔
844
        match num::FromPrimitive::from_u8(ftype) {
1,904✔
845
            Some(parser::HTTP2FrameType::GoAway) => {
846
                if input.len() < HTTP2_FRAME_GOAWAY_LEN {
17✔
847
                    self.set_event(HTTP2Event::InvalidFrameLength);
×
848
                    return HTTP2FrameTypeData::UNHANDLED(HTTP2FrameUnhandled {
×
849
                        reason: HTTP2FrameUnhandledReason::Incomplete,
×
850
                    });
×
851
                }
17✔
852
                match parser::http2_parse_frame_goaway(input) {
17✔
853
                    Ok((_, goaway)) => {
17✔
854
                        return HTTP2FrameTypeData::GOAWAY(goaway);
17✔
855
                    }
856
                    Err(_) => {
857
                        self.set_event(HTTP2Event::InvalidFrameData);
×
858
                        return HTTP2FrameTypeData::UNHANDLED(HTTP2FrameUnhandled {
×
859
                            reason: HTTP2FrameUnhandledReason::ParsingError,
×
860
                        });
×
861
                    }
862
                }
863
            }
864
            Some(parser::HTTP2FrameType::Settings) => {
865
                match parser::http2_parse_frame_settings(input) {
99✔
866
                    Ok((_, set)) => {
99✔
867
                        for e in &set {
406✔
868
                            if e.id == parser::HTTP2SettingsId::HeaderTableSize {
307✔
869
                                //reverse order as this is what we accept from the other endpoint
870
                                let dyn_headers = if dir == Direction::ToClient {
36✔
871
                                    &mut self.dynamic_headers_ts
10✔
872
                                } else {
873
                                    &mut self.dynamic_headers_tc
26✔
874
                                };
875
                                dyn_headers.max_size = e.value as usize;
36✔
876
                                if e.value > unsafe { HTTP2_MAX_TABLESIZE } {
36✔
877
                                    //mark potential overflow
×
878
                                    dyn_headers.overflow = 1;
×
879
                                } else {
36✔
880
                                    //reset in case peer set a lower value, to be tested
36✔
881
                                    dyn_headers.overflow = 0;
36✔
882
                                }
36✔
883
                            }
271✔
884
                        }
885
                        //we could set an event on remaining data
886
                        return HTTP2FrameTypeData::SETTINGS(set);
99✔
887
                    }
888
                    Err(Err::Incomplete(_)) => {
889
                        if complete {
×
890
                            self.set_event(HTTP2Event::InvalidFrameData);
×
891
                            return HTTP2FrameTypeData::UNHANDLED(HTTP2FrameUnhandled {
×
892
                                reason: HTTP2FrameUnhandledReason::ParsingError,
×
893
                            });
×
894
                        } else {
895
                            return HTTP2FrameTypeData::UNHANDLED(HTTP2FrameUnhandled {
×
896
                                reason: HTTP2FrameUnhandledReason::TooLong,
×
897
                            });
×
898
                        }
899
                    }
900
                    Err(_) => {
901
                        self.set_event(HTTP2Event::InvalidFrameData);
×
902
                        return HTTP2FrameTypeData::UNHANDLED(HTTP2FrameUnhandled {
×
903
                            reason: HTTP2FrameUnhandledReason::ParsingError,
×
904
                        });
×
905
                    }
906
                }
907
            }
908
            Some(parser::HTTP2FrameType::RstStream) => {
909
                if input.len() != HTTP2_FRAME_RSTSTREAM_LEN {
36✔
910
                    self.set_event(HTTP2Event::InvalidFrameLength);
×
911
                    return HTTP2FrameTypeData::UNHANDLED(HTTP2FrameUnhandled {
×
912
                        reason: HTTP2FrameUnhandledReason::Incomplete,
×
913
                    });
×
914
                } else {
915
                    match parser::http2_parse_frame_rststream(input) {
36✔
916
                        Ok((_, rst)) => {
36✔
917
                            return HTTP2FrameTypeData::RSTSTREAM(rst);
36✔
918
                        }
919
                        Err(_) => {
920
                            self.set_event(HTTP2Event::InvalidFrameData);
×
921
                            return HTTP2FrameTypeData::UNHANDLED(HTTP2FrameUnhandled {
×
922
                                reason: HTTP2FrameUnhandledReason::ParsingError,
×
923
                            });
×
924
                        }
925
                    }
926
                }
927
            }
928
            Some(parser::HTTP2FrameType::Priority) => {
929
                if input.len() != HTTP2_FRAME_PRIORITY_LEN {
12✔
930
                    self.set_event(HTTP2Event::InvalidFrameLength);
×
931
                    return HTTP2FrameTypeData::UNHANDLED(HTTP2FrameUnhandled {
×
932
                        reason: HTTP2FrameUnhandledReason::Incomplete,
×
933
                    });
×
934
                } else {
935
                    match parser::http2_parse_frame_priority(input) {
12✔
936
                        Ok((_, priority)) => {
12✔
937
                            return HTTP2FrameTypeData::PRIORITY(priority);
12✔
938
                        }
939
                        Err(_) => {
940
                            self.set_event(HTTP2Event::InvalidFrameData);
×
941
                            return HTTP2FrameTypeData::UNHANDLED(HTTP2FrameUnhandled {
×
942
                                reason: HTTP2FrameUnhandledReason::ParsingError,
×
943
                            });
×
944
                        }
945
                    }
946
                }
947
            }
948
            Some(parser::HTTP2FrameType::WindowUpdate) => {
949
                if input.len() != HTTP2_FRAME_WINDOWUPDATE_LEN {
229✔
950
                    self.set_event(HTTP2Event::InvalidFrameLength);
×
951
                    return HTTP2FrameTypeData::UNHANDLED(HTTP2FrameUnhandled {
×
952
                        reason: HTTP2FrameUnhandledReason::Incomplete,
×
953
                    });
×
954
                } else {
955
                    match parser::http2_parse_frame_windowupdate(input) {
229✔
956
                        Ok((_, wu)) => {
229✔
957
                            return HTTP2FrameTypeData::WINDOWUPDATE(wu);
229✔
958
                        }
959
                        Err(_) => {
960
                            self.set_event(HTTP2Event::InvalidFrameData);
×
961
                            return HTTP2FrameTypeData::UNHANDLED(HTTP2FrameUnhandled {
×
962
                                reason: HTTP2FrameUnhandledReason::ParsingError,
×
963
                            });
×
964
                        }
965
                    }
966
                }
967
            }
968
            Some(parser::HTTP2FrameType::PushPromise) => {
969
                let dyn_headers = if dir == Direction::ToClient {
×
970
                    &mut self.dynamic_headers_tc
×
971
                } else {
972
                    &mut self.dynamic_headers_ts
×
973
                };
974
                match parser::http2_parse_frame_push_promise(input, hflags, dyn_headers) {
×
975
                    Ok((_, hs)) => {
×
976
                        self.process_headers(&hs.blocks, dir);
×
977
                        return HTTP2FrameTypeData::PUSHPROMISE(hs);
×
978
                    }
979
                    Err(Err::Incomplete(_)) => {
980
                        if complete {
×
981
                            self.set_event(HTTP2Event::InvalidFrameData);
×
982
                            return HTTP2FrameTypeData::UNHANDLED(HTTP2FrameUnhandled {
×
983
                                reason: HTTP2FrameUnhandledReason::ParsingError,
×
984
                            });
×
985
                        } else {
986
                            return HTTP2FrameTypeData::UNHANDLED(HTTP2FrameUnhandled {
×
987
                                reason: HTTP2FrameUnhandledReason::TooLong,
×
988
                            });
×
989
                        }
990
                    }
991
                    Err(_) => {
992
                        self.set_event(HTTP2Event::InvalidFrameData);
×
993
                        return HTTP2FrameTypeData::UNHANDLED(HTTP2FrameUnhandled {
×
994
                            reason: HTTP2FrameUnhandledReason::ParsingError,
×
995
                        });
×
996
                    }
997
                }
998
            }
999
            Some(parser::HTTP2FrameType::Data) => {
1000
                return HTTP2FrameTypeData::DATA;
1,136✔
1001
            }
1002
            Some(parser::HTTP2FrameType::Continuation) => {
1003
                let buf = if dir == Direction::ToClient {
4✔
1004
                    &mut self.s2c_buf
×
1005
                } else {
1006
                    &mut self.c2s_buf
4✔
1007
                };
1008
                if head.stream_id == buf.stream_id {
4✔
1009
                    let max_reass = unsafe { HTTP2_MAX_REASS };
4✔
1010
                    if buf.data.len() + input.len() < max_reass {
4✔
1011
                        buf.data.extend(input);
4✔
1012
                    } else if buf.data.len() < max_reass {
4✔
1013
                        buf.data.extend(&input[..max_reass - buf.data.len()]);
×
1014
                        *reass_limit_reached = true;
×
1015
                    }
×
1016
                    if head.flags & parser::HTTP2_FLAG_HEADER_END_HEADERS == 0 {
4✔
1017
                        let hs = parser::HTTP2FrameContinuation { blocks: Vec::new() };
2✔
1018
                        return HTTP2FrameTypeData::CONTINUATION(hs);
2✔
1019
                    }
2✔
1020
                } // else try to parse anyways
×
1021
                let input_reass = if head.stream_id == buf.stream_id {
2✔
1022
                    &buf.data
2✔
1023
                } else {
1024
                    input
×
1025
                };
1026

1027
                let dyn_headers = if dir == Direction::ToClient {
2✔
1028
                    &mut self.dynamic_headers_tc
×
1029
                } else {
1030
                    &mut self.dynamic_headers_ts
2✔
1031
                };
1032
                match parser::http2_parse_frame_continuation(input_reass, dyn_headers) {
2✔
1033
                    Ok((_, hs)) => {
2✔
1034
                        if head.stream_id == buf.stream_id {
2✔
1035
                            buf.stream_id = 0;
2✔
1036
                            buf.data.clear();
2✔
1037
                        }
2✔
1038
                        self.process_headers(&hs.blocks, dir);
2✔
1039
                        return HTTP2FrameTypeData::CONTINUATION(hs);
2✔
1040
                    }
1041
                    Err(Err::Incomplete(_)) => {
1042
                        if head.stream_id == buf.stream_id {
×
1043
                            buf.stream_id = 0;
×
1044
                            buf.data.clear();
×
1045
                        }
×
1046
                        if complete {
×
1047
                            self.set_event(HTTP2Event::InvalidFrameData);
×
1048
                            return HTTP2FrameTypeData::UNHANDLED(HTTP2FrameUnhandled {
×
1049
                                reason: HTTP2FrameUnhandledReason::ParsingError,
×
1050
                            });
×
1051
                        } else {
1052
                            return HTTP2FrameTypeData::UNHANDLED(HTTP2FrameUnhandled {
×
1053
                                reason: HTTP2FrameUnhandledReason::TooLong,
×
1054
                            });
×
1055
                        }
1056
                    }
1057
                    Err(_) => {
1058
                        if head.stream_id == buf.stream_id {
×
1059
                            buf.stream_id = 0;
×
1060
                            buf.data.clear();
×
1061
                        }
×
1062
                        self.set_event(HTTP2Event::InvalidFrameData);
×
1063
                        return HTTP2FrameTypeData::UNHANDLED(HTTP2FrameUnhandled {
×
1064
                            reason: HTTP2FrameUnhandledReason::ParsingError,
×
1065
                        });
×
1066
                    }
1067
                }
1068
            }
1069
            Some(parser::HTTP2FrameType::Headers) => {
1070
                if head.flags & parser::HTTP2_FLAG_HEADER_END_HEADERS == 0 {
335✔
1071
                    let buf = if dir == Direction::ToClient {
2✔
1072
                        &mut self.s2c_buf
×
1073
                    } else {
1074
                        &mut self.c2s_buf
2✔
1075
                    };
1076
                    buf.data.clear();
2✔
1077
                    buf.data.extend(input);
2✔
1078
                    buf.stream_id = head.stream_id;
2✔
1079
                    let hs = parser::HTTP2FrameHeaders {
2✔
1080
                        padlength: None,
2✔
1081
                        priority: None,
2✔
1082
                        blocks: Vec::new(),
2✔
1083
                    };
2✔
1084
                    return HTTP2FrameTypeData::HEADERS(hs);
2✔
1085
                }
333✔
1086
                let dyn_headers = if dir == Direction::ToClient {
333✔
1087
                    &mut self.dynamic_headers_tc
179✔
1088
                } else {
1089
                    &mut self.dynamic_headers_ts
154✔
1090
                };
1091
                match parser::http2_parse_frame_headers(input, hflags, dyn_headers) {
333✔
1092
                    Ok((hrem, hs)) => {
333✔
1093
                        self.process_headers(&hs.blocks, dir);
333✔
1094
                        if !hrem.is_empty() {
333✔
1095
                            SCLogDebug!("Remaining data for HTTP2 headers");
×
1096
                            self.set_event(HTTP2Event::ExtraHeaderData);
×
1097
                        }
333✔
1098
                        return HTTP2FrameTypeData::HEADERS(hs);
333✔
1099
                    }
1100
                    Err(Err::Incomplete(_)) => {
1101
                        if complete {
×
1102
                            self.set_event(HTTP2Event::InvalidFrameData);
×
1103
                            return HTTP2FrameTypeData::UNHANDLED(HTTP2FrameUnhandled {
×
1104
                                reason: HTTP2FrameUnhandledReason::ParsingError,
×
1105
                            });
×
1106
                        } else {
1107
                            return HTTP2FrameTypeData::UNHANDLED(HTTP2FrameUnhandled {
×
1108
                                reason: HTTP2FrameUnhandledReason::TooLong,
×
1109
                            });
×
1110
                        }
1111
                    }
1112
                    Err(_) => {
1113
                        self.set_event(HTTP2Event::InvalidFrameData);
×
1114
                        return HTTP2FrameTypeData::UNHANDLED(HTTP2FrameUnhandled {
×
1115
                            reason: HTTP2FrameUnhandledReason::ParsingError,
×
1116
                        });
×
1117
                    }
1118
                }
1119
            }
1120
            Some(parser::HTTP2FrameType::Ping) => {
1121
                return HTTP2FrameTypeData::PING;
36✔
1122
            }
1123
            _ => {
1124
                return HTTP2FrameTypeData::UNHANDLED(HTTP2FrameUnhandled {
×
1125
                    reason: HTTP2FrameUnhandledReason::UnknownType,
×
1126
                });
×
1127
            }
1128
        }
1129
    }
1,904✔
1130

1131
    fn parse_frames(
788✔
1132
        &mut self, mut input: &[u8], il: usize, dir: Direction, flow: *mut Flow,
788✔
1133
        stream_slice: &StreamSlice,
788✔
1134
    ) -> AppLayerResult {
788✔
1135
        while !input.is_empty() {
2,797✔
1136
            match parser::http2_parse_frame_header(input) {
2,027✔
1137
                Ok((rem, head)) => {
2,027✔
1138
                    let hl = head.length as usize;
2,027✔
1139

2,027✔
1140
                    //we check for completeness first
2,027✔
1141
                    if rem.len() < hl {
2,027✔
1142
                        //but limit ourselves so as not to exhaust memory
1143
                        if hl < HTTP2_MAX_HANDLED_FRAME_SIZE {
18✔
1144
                            return AppLayerResult::incomplete(
18✔
1145
                                (il - input.len()) as u32,
18✔
1146
                                (HTTP2_FRAME_HEADER_LEN + hl) as u32,
18✔
1147
                            );
18✔
1148
                        } else if rem.len() < HTTP2_MIN_HANDLED_FRAME_SIZE {
×
1149
                            return AppLayerResult::incomplete(
×
1150
                                (il - input.len()) as u32,
×
1151
                                (HTTP2_FRAME_HEADER_LEN + HTTP2_MIN_HANDLED_FRAME_SIZE) as u32,
×
1152
                            );
×
1153
                        } else {
×
1154
                            self.set_event(HTTP2Event::LongFrameData);
×
1155
                            self.request_frame_size = head.length - (rem.len() as u32);
×
1156
                        }
×
1157
                    }
2,009✔
1158

1159
                    //get a safe length for the buffer
1160
                    let (hlsafe, complete) = if rem.len() < hl {
2,009✔
1161
                        (rem.len(), false)
×
1162
                    } else {
1163
                        (hl, true)
2,009✔
1164
                    };
1165

1166
                    let frame_hdr = Frame::new(
2,009✔
1167
                        flow,
2,009✔
1168
                        stream_slice,
2,009✔
1169
                        input,
2,009✔
1170
                        HTTP2_FRAME_HEADER_LEN as i64,
2,009✔
1171
                        Http2FrameType::Hdr as u8,
2,009✔
1172
                        None,
2,009✔
1173
                    );
2,009✔
1174
                    let frame_data = Frame::new(
2,009✔
1175
                        flow,
2,009✔
1176
                        stream_slice,
2,009✔
1177
                        &input[HTTP2_FRAME_HEADER_LEN..],
2,009✔
1178
                        head.length as i64,
2,009✔
1179
                        Http2FrameType::Data as u8,
2,009✔
1180
                        None,
2,009✔
1181
                    );
2,009✔
1182
                    let frame_pdu = Frame::new(
2,009✔
1183
                        flow,
2,009✔
1184
                        stream_slice,
2,009✔
1185
                        input,
2,009✔
1186
                        HTTP2_FRAME_HEADER_LEN as i64 + head.length as i64,
2,009✔
1187
                        Http2FrameType::Pdu as u8,
2,009✔
1188
                        None,
2,009✔
1189
                    );
2,009✔
1190
                    if head.length == 0 && head.ftype == parser::HTTP2FrameType::Settings as u8 {
2,009✔
1191
                        input = &rem[hlsafe..];
105✔
1192
                        continue;
105✔
1193
                    }
1,904✔
1194
                    let mut reass_limit_reached = false;
1,904✔
1195
                    let txdata = self.parse_frame_data(
1,904✔
1196
                        &head,
1,904✔
1197
                        &rem[..hlsafe],
1,904✔
1198
                        complete,
1,904✔
1199
                        dir,
1,904✔
1200
                        &mut reass_limit_reached,
1,904✔
1201
                    );
1,904✔
1202

1,904✔
1203
                    let tx = self.find_or_create_tx(&head, &txdata, dir);
1,904✔
1204
                    if tx.is_none() {
1,904✔
1205
                        return AppLayerResult::err();
×
1206
                    }
1,904✔
1207
                    let tx = tx.unwrap();
1,904✔
1208
                    if let Some(frame) = frame_hdr {
1,904✔
1209
                        frame.set_tx(flow, tx.tx_id);
8✔
1210
                    }
1,896✔
1211
                    if let Some(frame) = frame_data {
1,904✔
1212
                        frame.set_tx(flow, tx.tx_id);
8✔
1213
                    }
1,896✔
1214
                    if let Some(frame) = frame_pdu {
1,904✔
1215
                        frame.set_tx(flow, tx.tx_id);
8✔
1216
                    }
1,896✔
1217
                    if let Some(doh_req_buf) = tx.handle_frame(&head, &txdata, dir) {
1,904✔
1218
                        if let Ok(mut dtx) = dns_parse_request(&doh_req_buf, &DnsVariant::Dns) {
8✔
1219
                            dtx.id = 1;
8✔
1220
                            unsafe {
8✔
1221
                                SCAppLayerForceProtocolChange(flow, ALPROTO_DOH2);
8✔
1222
                            }
8✔
1223
                            if let Some(doh) = &mut tx.doh {
8✔
1224
                                doh.dns_request_tx = Some(dtx);
×
1225
                            } else {
8✔
1226
                                let doh = DohHttp2Tx {
8✔
1227
                                    dns_request_tx: Some(dtx),
8✔
1228
                                    ..Default::default()
8✔
1229
                                };
8✔
1230
                                tx.doh = Some(doh);
8✔
1231
                            }
8✔
1232
                        }
×
1233
                    }
1,896✔
1234
                    if reass_limit_reached {
1,904✔
1235
                        tx.tx_data
×
1236
                            .set_event(HTTP2Event::ReassemblyLimitReached as u8);
×
1237
                    }
1,904✔
1238
                    let over = head.flags & parser::HTTP2_FLAG_HEADER_EOS != 0;
1,904✔
1239
                    let ftype = head.ftype;
1,904✔
1240
                    let sid = head.stream_id;
1,904✔
1241
                    let padded = head.flags & parser::HTTP2_FLAG_HEADER_PADDED != 0;
1,904✔
1242
                    if dir == Direction::ToServer {
1,904✔
1243
                        tx.frames_ts.push(HTTP2Frame {
546✔
1244
                            header: head,
546✔
1245
                            data: txdata,
546✔
1246
                        });
546✔
1247
                    } else {
1,358✔
1248
                        tx.frames_tc.push(HTTP2Frame {
1,358✔
1249
                            header: head,
1,358✔
1250
                            data: txdata,
1,358✔
1251
                        });
1,358✔
1252
                    }
1,358✔
1253
                    if ftype == parser::HTTP2FrameType::Data as u8 && sid == 0 {
1,904✔
1254
                        tx.tx_data.set_event(HTTP2Event::DataStreamZero as u8);
×
1255
                    } else if ftype == parser::HTTP2FrameType::Data as u8 && sid > 0 {
1,904✔
1256
                        match unsafe { SURICATA_HTTP2_FILE_CONFIG } {
1,136✔
1257
                            Some(sfcm) => {
1,136✔
1258
                                //borrow checker forbids to reuse directly tx
1,136✔
1259
                                let index = self.find_tx_index(sid);
1,136✔
1260
                                if index > 0 {
1,136✔
1261
                                    let tx_same = &mut self.transactions[index - 1];
1,136✔
1262
                                    if dir == Direction::ToServer {
1,136✔
1263
                                        tx_same.ft_tc.tx_id = tx_same.tx_id - 1;
106✔
1264
                                    } else {
1,030✔
1265
                                        tx_same.ft_ts.tx_id = tx_same.tx_id - 1;
1,030✔
1266
                                    };
1,030✔
1267
                                    let mut dinput = &rem[..hlsafe];
1,136✔
1268
                                    if padded && !rem.is_empty() && usize::from(rem[0]) < hlsafe {
1,136✔
1269
                                        dinput = &rem[1..hlsafe - usize::from(rem[0])];
24✔
1270
                                    }
1,112✔
1271
                                    let mut output = Vec::with_capacity(
1,136✔
1272
                                        decompression::HTTP2_DECOMPRESSION_CHUNK_SIZE,
1,136✔
1273
                                    );
1,136✔
1274
                                    match tx_same.decompress(
1,136✔
1275
                                        dinput,
1,136✔
1276
                                        &mut output,
1,136✔
1277
                                        dir,
1,136✔
1278
                                        sfcm,
1,136✔
1279
                                        over,
1,136✔
1280
                                        flow,
1,136✔
1281
                                    ) {
1,136✔
1282
                                        Ok(_) => {
1283
                                            if over {
1,136✔
1284
                                                tx_same.handle_dns_data(dir, flow);
255✔
1285
                                            }
881✔
1286
                                        }
1287
                                        _ => {
×
1288
                                            self.set_event(HTTP2Event::FailedDecompression);
×
1289
                                        }
×
1290
                                    }
1291
                                }
×
1292
                            }
1293
                            None => panic!("no SURICATA_HTTP2_FILE_CONFIG"),
×
1294
                        }
1295
                    }
768✔
1296
                    sc_app_layer_parser_trigger_raw_stream_inspection(flow, dir as i32);
1,904✔
1297
                    input = &rem[hlsafe..];
1,904✔
1298
                }
1299
                Err(Err::Incomplete(_)) => {
1300
                    //we may have consumed data from previous records
1301
                    return AppLayerResult::incomplete(
×
1302
                        (il - input.len()) as u32,
×
1303
                        HTTP2_FRAME_HEADER_LEN as u32,
×
1304
                    );
×
1305
                }
1306
                Err(_) => {
1307
                    self.set_event(HTTP2Event::InvalidFrameHeader);
×
1308
                    return AppLayerResult::err();
×
1309
                }
1310
            }
1311
        }
1312
        return AppLayerResult::ok();
770✔
1313
    }
788✔
1314

1315
    fn parse_ts(&mut self, flow: *mut Flow, stream_slice: StreamSlice) -> AppLayerResult {
402✔
1316
        //very first : skip magic
402✔
1317
        let mut input = stream_slice.as_slice();
402✔
1318
        let mut magic_consumed = 0;
402✔
1319
        if self.progress < HTTP2ConnectionState::Http2StateMagicDone {
402✔
1320
            //skip magic
1321
            if input.len() >= HTTP2_MAGIC_LEN {
50✔
1322
                //skip magic
1323
                match std::str::from_utf8(&input[..HTTP2_MAGIC_LEN]) {
50✔
1324
                    Ok("PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n") => {
50✔
1325
                        input = &input[HTTP2_MAGIC_LEN..];
50✔
1326
                        magic_consumed = HTTP2_MAGIC_LEN;
50✔
1327
                    }
50✔
1328
                    Ok(&_) => {
×
1329
                        self.set_event(HTTP2Event::InvalidClientMagic);
×
1330
                    }
×
1331
                    Err(_) => {
1332
                        return AppLayerResult::err();
×
1333
                    }
1334
                }
1335
                self.progress = HTTP2ConnectionState::Http2StateMagicDone;
50✔
1336
            } else {
1337
                //still more buffer
1338
                return AppLayerResult::incomplete(0_u32, HTTP2_MAGIC_LEN as u32);
×
1339
            }
1340
        }
352✔
1341
        //first consume frame bytes
1342
        let il = input.len();
402✔
1343
        if self.request_frame_size > 0 {
402✔
1344
            let ilen = input.len() as u32;
×
1345
            if self.request_frame_size >= ilen {
×
1346
                self.request_frame_size -= ilen;
×
1347
                return AppLayerResult::ok();
×
1348
            } else {
×
1349
                let start = self.request_frame_size as usize;
×
1350
                input = &input[start..];
×
1351
                self.request_frame_size = 0;
×
1352
            }
×
1353
        }
402✔
1354

1355
        //then parse all we can
1356
        let r = self.parse_frames(input, il, Direction::ToServer, flow, &stream_slice);
402✔
1357
        if r.status == 1 {
402✔
1358
            //adds bytes consumed by banner to incomplete result
1359
            return AppLayerResult::incomplete(r.consumed + magic_consumed as u32, r.needed);
18✔
1360
        } else {
1361
            return r;
384✔
1362
        }
1363
    }
402✔
1364

1365
    fn parse_tc(&mut self, flow: *mut Flow, stream_slice: StreamSlice) -> AppLayerResult {
386✔
1366
        //first consume frame bytes
386✔
1367
        let mut input = stream_slice.as_slice();
386✔
1368
        let il = input.len();
386✔
1369
        if self.response_frame_size > 0 {
386✔
1370
            let ilen = input.len() as u32;
×
1371
            if self.response_frame_size >= ilen {
×
1372
                self.response_frame_size -= ilen;
×
1373
                return AppLayerResult::ok();
×
1374
            } else {
×
1375
                let start = self.response_frame_size as usize;
×
1376
                input = &input[start..];
×
1377
                self.response_frame_size = 0;
×
1378
            }
×
1379
        }
386✔
1380
        //then parse all we can
1381
        return self.parse_frames(input, il, Direction::ToClient, flow, &stream_slice);
386✔
1382
    }
386✔
1383
}
1384

1385
// C exports.
1386

1387
#[no_mangle]
1388
pub unsafe extern "C" fn SCDoH2GetDnsTx(
487✔
1389
    tx: &HTTP2Transaction, flags: u8,
487✔
1390
) -> *mut std::os::raw::c_void {
487✔
1391
    if let Some(doh) = &tx.doh {
487✔
1392
        if flags & Direction::ToServer as u8 != 0 {
366✔
1393
            if let Some(ref dtx) = &doh.dns_request_tx {
130✔
1394
                return dtx as *const _ as *mut _;
126✔
1395
            }
4✔
1396
        } else if flags & Direction::ToClient as u8 != 0 {
236✔
1397
            if let Some(ref dtx) = &doh.dns_response_tx {
236✔
1398
                return dtx as *const _ as *mut _;
215✔
1399
            }
21✔
1400
        }
×
1401
    }
121✔
1402
    std::ptr::null_mut()
146✔
1403
}
487✔
1404

1405
export_tx_data_get!(http2_get_tx_data, HTTP2Transaction);
1406
export_state_data_get!(http2_get_state_data, HTTP2State);
1407

1408
/// C entry point for a probing parser.
1409
unsafe extern "C" fn http2_probing_parser_tc(
101✔
1410
    _flow: *const Flow, _direction: u8, input: *const u8, input_len: u32, _rdir: *mut u8,
101✔
1411
) -> AppProto {
101✔
1412
    if !input.is_null() {
101✔
1413
        let slice = build_slice!(input, input_len as usize);
101✔
1414
        match parser::http2_parse_frame_header(slice) {
101✔
1415
            Ok((_, header)) => {
101✔
1416
                if header.reserved != 0
101✔
1417
                    || header.length > HTTP2_DEFAULT_MAX_FRAME_SIZE
101✔
1418
                    || header.flags & 0xFE != 0
48✔
1419
                    || header.ftype != parser::HTTP2FrameType::Settings as u8
48✔
1420
                {
1421
                    return ALPROTO_FAILED;
53✔
1422
                }
48✔
1423
                return ALPROTO_HTTP2;
48✔
1424
            }
1425
            Err(Err::Incomplete(_)) => {
1426
                return ALPROTO_UNKNOWN;
×
1427
            }
1428
            Err(_) => {
1429
                return ALPROTO_FAILED;
×
1430
            }
1431
        }
1432
    }
×
1433
    return ALPROTO_UNKNOWN;
×
1434
}
101✔
1435

1436
// Suppress the unsafe warning here as creating a state for an app-layer
1437
// is typically not unsafe.
1438
#[allow(clippy::not_unsafe_ptr_arg_deref)]
1439
extern "C" fn http2_state_new(
51✔
1440
    orig_state: *mut std::os::raw::c_void, _orig_proto: AppProto,
51✔
1441
) -> *mut std::os::raw::c_void {
51✔
1442
    let state = HTTP2State::new();
51✔
1443
    let boxed = Box::new(state);
51✔
1444
    let r = Box::into_raw(boxed) as *mut _;
51✔
1445
    if !orig_state.is_null() {
51✔
1446
        //we could check ALPROTO_HTTP1 == orig_proto
1447
        unsafe {
22✔
1448
            SCHTTP2MimicHttp1Request(orig_state, r);
22✔
1449
        }
22✔
1450
    }
29✔
1451
    return r;
51✔
1452
}
51✔
1453

1454
unsafe extern "C" fn http2_state_free(state: *mut std::os::raw::c_void) {
51✔
1455
    let mut state: Box<HTTP2State> = Box::from_raw(state as _);
51✔
1456
    state.free();
51✔
1457
}
51✔
1458

1459
unsafe extern "C" fn http2_state_tx_free(state: *mut std::os::raw::c_void, tx_id: u64) {
491✔
1460
    let state = cast_pointer!(state, HTTP2State);
491✔
1461
    state.free_tx(tx_id);
491✔
1462
}
491✔
1463

1464
unsafe extern "C" fn http2_parse_ts(
402✔
1465
    flow: *mut Flow, state: *mut std::os::raw::c_void, _pstate: *mut AppLayerParserState,
402✔
1466
    stream_slice: StreamSlice, _data: *mut std::os::raw::c_void,
402✔
1467
) -> AppLayerResult {
402✔
1468
    let state = cast_pointer!(state, HTTP2State);
402✔
1469
    return state.parse_ts(flow, stream_slice);
402✔
1470
}
402✔
1471

1472
unsafe extern "C" fn http2_parse_tc(
386✔
1473
    flow: *mut Flow, state: *mut std::os::raw::c_void, _pstate: *mut AppLayerParserState,
386✔
1474
    stream_slice: StreamSlice, _data: *mut std::os::raw::c_void,
386✔
1475
) -> AppLayerResult {
386✔
1476
    let state = cast_pointer!(state, HTTP2State);
386✔
1477
    return state.parse_tc(flow, stream_slice);
386✔
1478
}
386✔
1479

1480
unsafe extern "C" fn http2_state_get_tx(
762✔
1481
    state: *mut std::os::raw::c_void, tx_id: u64,
762✔
1482
) -> *mut std::os::raw::c_void {
762✔
1483
    let state = cast_pointer!(state, HTTP2State);
762✔
1484
    match state.get_tx(tx_id) {
762✔
1485
        Some(tx) => {
762✔
1486
            return tx as *const _ as *mut _;
762✔
1487
        }
1488
        None => {
1489
            return std::ptr::null_mut();
×
1490
        }
1491
    }
1492
}
762✔
1493

1494
unsafe extern "C" fn http2_state_get_tx_count(state: *mut std::os::raw::c_void) -> u64 {
5,832✔
1495
    let state = cast_pointer!(state, HTTP2State);
5,832✔
1496
    return state.tx_id;
5,832✔
1497
}
5,832✔
1498

1499
unsafe extern "C" fn http2_tx_get_state(tx: *mut std::os::raw::c_void) -> HTTP2TransactionState {
17,010✔
1500
    let tx = cast_pointer!(tx, HTTP2Transaction);
17,010✔
1501
    return tx.state;
17,010✔
1502
}
17,010✔
1503

1504
unsafe extern "C" fn http2_tx_get_alstate_progress(
17,010✔
1505
    tx: *mut std::os::raw::c_void, _direction: u8,
17,010✔
1506
) -> std::os::raw::c_int {
17,010✔
1507
    return http2_tx_get_state(tx) as i32;
17,010✔
1508
}
17,010✔
1509

1510
unsafe extern "C" fn http2_getfiles(
8,107✔
1511
    tx: *mut std::os::raw::c_void, direction: u8,
8,107✔
1512
) -> AppLayerGetFileState {
8,107✔
1513
    let tx = cast_pointer!(tx, HTTP2Transaction);
8,107✔
1514
    if let Some(sfcm) = { SURICATA_HTTP2_FILE_CONFIG } {
8,107✔
1515
        if direction & STREAM_TOSERVER != 0 {
8,107✔
1516
            return AppLayerGetFileState {
3,924✔
1517
                fc: &mut tx.ft_ts.file,
3,924✔
1518
                cfg: sfcm.files_sbcfg,
3,924✔
1519
            };
3,924✔
1520
        } else {
1521
            return AppLayerGetFileState {
4,183✔
1522
                fc: &mut tx.ft_tc.file,
4,183✔
1523
                cfg: sfcm.files_sbcfg,
4,183✔
1524
            };
4,183✔
1525
        }
1526
    }
×
1527
    AppLayerGetFileState::err()
×
1528
}
8,107✔
1529

1530
// Parser name as a C style string.
1531
const PARSER_NAME: &[u8] = b"http2\0";
1532

1533
#[no_mangle]
1534
pub unsafe extern "C" fn SCRegisterHttp2Parser() {
2,195✔
1535
    let default_port = CString::new("[80]").unwrap();
2,195✔
1536
    let mut parser = RustParser {
2,195✔
1537
        name: PARSER_NAME.as_ptr() as *const std::os::raw::c_char,
2,195✔
1538
        default_port: default_port.as_ptr(),
2,195✔
1539
        ipproto: IPPROTO_TCP,
2,195✔
1540
        probe_ts: None, // big magic string should be enough
2,195✔
1541
        probe_tc: Some(http2_probing_parser_tc),
2,195✔
1542
        min_depth: HTTP2_FRAME_HEADER_LEN as u16,
2,195✔
1543
        max_depth: HTTP2_MAGIC_LEN as u16,
2,195✔
1544
        state_new: http2_state_new,
2,195✔
1545
        state_free: http2_state_free,
2,195✔
1546
        tx_free: http2_state_tx_free,
2,195✔
1547
        parse_ts: http2_parse_ts,
2,195✔
1548
        parse_tc: http2_parse_tc,
2,195✔
1549
        get_tx_count: http2_state_get_tx_count,
2,195✔
1550
        get_tx: http2_state_get_tx,
2,195✔
1551
        tx_comp_st_ts: HTTP2TransactionState::HTTP2StateClosed as i32,
2,195✔
1552
        tx_comp_st_tc: HTTP2TransactionState::HTTP2StateClosed as i32,
2,195✔
1553
        tx_get_progress: http2_tx_get_alstate_progress,
2,195✔
1554
        get_eventinfo: Some(HTTP2Event::get_event_info),
2,195✔
1555
        get_eventinfo_byid: Some(HTTP2Event::get_event_info_by_id),
2,195✔
1556
        localstorage_new: None,
2,195✔
1557
        localstorage_free: None,
2,195✔
1558
        get_tx_files: Some(http2_getfiles),
2,195✔
1559
        get_tx_iterator: Some(applayer::state_get_tx_iterator::<HTTP2State, HTTP2Transaction>),
2,195✔
1560
        get_tx_data: http2_get_tx_data,
2,195✔
1561
        get_state_data: http2_get_state_data,
2,195✔
1562
        apply_tx_config: None,
2,195✔
1563
        flags: 0,
2,195✔
1564
        get_frame_id_by_name: Some(Http2FrameType::ffi_id_from_name),
2,195✔
1565
        get_frame_name_by_id: Some(Http2FrameType::ffi_name_from_id),
2,195✔
1566
        get_state_id_by_name: None,
2,195✔
1567
        get_state_name_by_id: None,
2,195✔
1568
    };
2,195✔
1569

2,195✔
1570
    let ip_proto_str = CString::new("tcp").unwrap();
2,195✔
1571

2,195✔
1572
    if SCAppLayerProtoDetectConfProtoDetectionEnabled(ip_proto_str.as_ptr(), parser.name) != 0 {
2,195✔
1573
        let alproto = applayer_register_protocol_detection(&parser, 1);
2,195✔
1574
        ALPROTO_HTTP2 = alproto;
2,195✔
1575
        if SCAppLayerParserConfParserEnabled(ip_proto_str.as_ptr(), parser.name) != 0 {
2,195✔
1576
            let _ = AppLayerRegisterParser(&parser, alproto);
2,195✔
1577
        }
2,195✔
1578
        if let Some(val) = conf_get("app-layer.protocols.http2.max-streams") {
2,195✔
1579
            if let Ok(v) = val.parse::<usize>() {
×
1580
                HTTP2_MAX_STREAMS = v;
×
1581
            } else {
×
1582
                SCLogError!("Invalid value for http2.max-streams");
×
1583
            }
1584
        }
2,195✔
1585
        if let Some(val) = conf_get("app-layer.protocols.http2.max-table-size") {
2,195✔
1586
            if let Ok(v) = val.parse::<u32>() {
×
1587
                HTTP2_MAX_TABLESIZE = v;
×
1588
            } else {
×
1589
                SCLogError!("Invalid value for http2.max-table-size");
×
1590
            }
1591
        }
2,195✔
1592
        if let Some(val) = conf_get("app-layer.protocols.http2.max-reassembly-size") {
2,195✔
1593
            if let Ok(v) = val.parse::<u32>() {
×
1594
                HTTP2_MAX_REASS = v as usize;
×
1595
            } else {
×
1596
                SCLogError!("Invalid value for http2.max-reassembly-size");
×
1597
            }
1598
        }
2,195✔
1599
        SCAppLayerParserRegisterLogger(IPPROTO_TCP, ALPROTO_HTTP2);
2,195✔
1600
        SCLogDebug!("Rust http2 parser registered.");
1601
    } else {
1602
        SCLogNotice!("Protocol detector and parser disabled for HTTP2.");
×
1603
    }
1604

1605
    // doh2 is just http2 wrapped in another name
1606
    parser.name = b"doh2\0".as_ptr() as *const std::os::raw::c_char;
2,195✔
1607
    parser.probe_tc = None;
2,195✔
1608
    parser.default_port = std::ptr::null();
2,195✔
1609
    if SCAppLayerProtoDetectConfProtoDetectionEnabled(ip_proto_str.as_ptr(), parser.name) != 0 {
2,195✔
1610
        let alproto = applayer_register_protocol_detection(&parser, 1);
2,195✔
1611
        ALPROTO_DOH2 = alproto;
2,195✔
1612
        if SCAppLayerParserConfParserEnabled(ip_proto_str.as_ptr(), parser.name) != 0 {
2,195✔
1613
            let _ = AppLayerRegisterParser(&parser, alproto);
2,195✔
1614
        } else {
2,195✔
1615
            SCLogWarning!("DOH2 is not meant to be detection-only.");
×
1616
        }
1617
        SCAppLayerParserRegisterLogger(IPPROTO_TCP, ALPROTO_DOH2);
2,195✔
1618
        SCLogDebug!("Rust doh2 parser registered.");
1619
    } else {
1620
        SCLogNotice!("Protocol detector and parser disabled for DOH2.");
×
1621
    }
1622
}
2,195✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc