]> git.ipfire.org Git - people/ms/suricata.git/blame - rust/src/http2/http2.rs
app-layer: include decoder events in app-layer tx data
[people/ms/suricata.git] / rust / src / http2 / http2.rs
CommitLineData
1422b18a
PA
1/* Copyright (C) 2020 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
d8612282 18use super::decompression;
98f84d5a 19use super::detect;
1422b18a 20use super::parser;
98f84d5a
PA
21use super::range;
22
1422b18a 23use crate::applayer::{self, *};
7732efbe 24use crate::core::*;
1422b18a
PA
25use crate::filecontainer::*;
26use crate::filetracker::*;
1422b18a
PA
27use nom;
28use std;
cef2832d 29use std::ffi::CString;
1422b18a 30use std::fmt;
d8612282 31use std::io;
1422b18a
PA
32
33static mut ALPROTO_HTTP2: AppProto = ALPROTO_UNKNOWN;
34
35const HTTP2_DEFAULT_MAX_FRAME_SIZE: u32 = 16384;
36const HTTP2_MAX_HANDLED_FRAME_SIZE: usize = 65536;
37const HTTP2_MIN_HANDLED_FRAME_SIZE: usize = 256;
38
39pub static mut SURICATA_HTTP2_FILE_CONFIG: Option<&'static SuricataFileContext> = None;
40
41#[no_mangle]
42pub extern "C" fn rs_http2_init(context: &'static mut SuricataFileContext) {
43 unsafe {
44 SURICATA_HTTP2_FILE_CONFIG = Some(context);
45 }
46}
47
48#[repr(u8)]
49#[derive(Copy, Clone, PartialOrd, PartialEq)]
50pub enum HTTP2ConnectionState {
51 Http2StateInit = 0,
52 Http2StateMagicDone = 1,
53}
54
55const HTTP2_FRAME_HEADER_LEN: usize = 9;
56const HTTP2_MAGIC_LEN: usize = 24;
57const HTTP2_FRAME_GOAWAY_LEN: usize = 4;
58const HTTP2_FRAME_RSTSTREAM_LEN: usize = 4;
64fcba22 59const HTTP2_FRAME_PRIORITY_LEN: usize = 5;
1422b18a 60const HTTP2_FRAME_WINDOWUPDATE_LEN: usize = 4;
c300a859
PA
61//TODO make this configurable
62pub const HTTP2_MAX_TABLESIZE: u32 = 0x10000; // 65536
1422b18a
PA
63
64#[repr(u8)]
65#[derive(Copy, Clone, PartialOrd, PartialEq, Debug)]
66pub enum HTTP2FrameUnhandledReason {
67 UnknownType = 0,
68 TooLong = 1,
69 ParsingError = 2,
70 Incomplete = 3,
71}
72
73impl fmt::Display for HTTP2FrameUnhandledReason {
74 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
75 write!(f, "{:?}", self)
76 }
77}
78
79#[derive(Debug)]
80pub struct HTTP2FrameUnhandled {
81 pub reason: HTTP2FrameUnhandledReason,
82}
83
20e8f909 84#[derive(Debug)]
1422b18a
PA
85pub enum HTTP2FrameTypeData {
86 PRIORITY(parser::HTTP2FramePriority),
87 GOAWAY(parser::HTTP2FrameGoAway),
88 RSTSTREAM(parser::HTTP2FrameRstStream),
89 SETTINGS(Vec<parser::HTTP2FrameSettings>),
90 WINDOWUPDATE(parser::HTTP2FrameWindowUpdate),
91 HEADERS(parser::HTTP2FrameHeaders),
92 PUSHPROMISE(parser::HTTP2FramePushPromise),
93 CONTINUATION(parser::HTTP2FrameContinuation),
94 PING,
95 DATA,
96 //not a defined frame
97 UNHANDLED(HTTP2FrameUnhandled),
98}
99
100#[repr(u8)]
101#[derive(Copy, Clone, PartialOrd, PartialEq, Debug)]
102pub enum HTTP2TransactionState {
103 HTTP2StateIdle = 0,
104 HTTP2StateOpen = 1,
105 HTTP2StateReserved = 2,
106 HTTP2StateDataClient = 3,
b21acfbf
PA
107 HTTP2StateHalfClosedClient = 4,
108 HTTP2StateDataServer = 5,
1422b18a
PA
109 HTTP2StateHalfClosedServer = 6,
110 HTTP2StateClosed = 7,
111 //not a RFC-defined state, used for stream 0 frames appyling to the global connection
112 HTTP2StateGlobal = 8,
113}
114
20e8f909 115#[derive(Debug)]
1422b18a
PA
116pub struct HTTP2Frame {
117 pub header: parser::HTTP2FrameHeader,
118 pub data: HTTP2FrameTypeData,
119}
120
20e8f909 121#[derive(Debug)]
1422b18a
PA
122pub struct HTTP2Transaction {
123 tx_id: u64,
124 pub stream_id: u32,
7011bddf 125 pub state: HTTP2TransactionState,
1422b18a
PA
126 child_stream_id: u32,
127
128 pub frames_tc: Vec<HTTP2Frame>,
129 pub frames_ts: Vec<HTTP2Frame>,
130
d8612282 131 decoder: decompression::HTTP2Decoder,
98f84d5a 132 pub file_range: *mut HttpRangeContainerBlock,
d8612282 133
1422b18a 134 tx_data: AppLayerTxData,
98f84d5a 135 pub ft_tc: FileTransferTracker,
6fe8bce3 136 ft_ts: FileTransferTracker,
1422b18a
PA
137
138 //temporary escaped header for detection
139 //must be attached to transaction for memory management (be freed at the right time)
4aa80ac7 140 pub escaped: Vec<Vec<u8>>,
1422b18a
PA
141}
142
54e62ddf
JI
143impl Transaction for HTTP2Transaction {
144 fn id(&self) -> u64 {
145 self.tx_id
146 }
147}
148
1422b18a
PA
149impl HTTP2Transaction {
150 pub fn new() -> HTTP2Transaction {
151 HTTP2Transaction {
152 tx_id: 0,
153 stream_id: 0,
154 child_stream_id: 0,
155 state: HTTP2TransactionState::HTTP2StateIdle,
156 frames_tc: Vec::new(),
157 frames_ts: Vec::new(),
d8612282 158 decoder: decompression::HTTP2Decoder::new(),
98f84d5a 159 file_range: std::ptr::null_mut(),
1422b18a 160 tx_data: AppLayerTxData::new(),
6fe8bce3
PA
161 ft_tc: FileTransferTracker::new(),
162 ft_ts: FileTransferTracker::new(),
4aa80ac7 163 escaped: Vec::with_capacity(16),
1422b18a
PA
164 }
165 }
166
167 pub fn free(&mut self) {
922a453d 168 if !self.file_range.is_null() {
98f84d5a
PA
169 match unsafe { SC } {
170 None => panic!("BUG no suricata_config"),
171 Some(c) => {
172 //TODO get a file container instead of NULL
173 (c.HTPFileCloseHandleRange)(
174 std::ptr::null_mut(),
175 0,
176 self.file_range,
177 std::ptr::null_mut(),
178 0,
179 );
180 (c.HttpRangeFreeBlock)(self.file_range);
0caaf6bd 181 self.file_range = std::ptr::null_mut();
98f84d5a
PA
182 }
183 }
184 }
185 }
186
187 pub fn set_event(&mut self, event: HTTP2Event) {
7732efbe 188 self.tx_data.set_event(event as u8);
1422b18a
PA
189 }
190
ee5b300c 191 fn handle_headers(&mut self, blocks: &Vec<parser::HTTP2FrameHeaderBlock>, dir: Direction) {
d8612282
PA
192 for i in 0..blocks.len() {
193 if blocks[i].name == "content-encoding".as_bytes().to_vec() {
194 self.decoder.http2_encoding_fromvec(&blocks[i].value, dir);
195 }
196 }
197 }
198
199 fn decompress<'a>(
ee5b300c
SB
200 &'a mut self, input: &'a [u8], dir: Direction, sfcm: &'static SuricataFileContext,
201 over: bool, files: &mut FileContainer, flags: u16, flow: *const Flow,
d8612282
PA
202 ) -> io::Result<()> {
203 let mut output = Vec::with_capacity(decompression::HTTP2_DECOMPRESSION_CHUNK_SIZE);
204 let decompressed = self.decoder.decompress(input, &mut output, dir)?;
205 let xid: u32 = self.tx_id as u32;
ee5b300c 206 if dir == Direction::ToClient {
bb98a18b
PA
207 self.ft_tc.tx_id = self.tx_id - 1;
208 if !self.ft_tc.file_open {
209 // we are now sure that new_chunk will open a file
210 // even if it may close it right afterwards
211 self.tx_data.incr_files_opened();
98f84d5a
PA
212 if let Ok(value) = detect::http2_frames_get_header_value_vec(
213 self,
ee5b300c 214 Direction::ToClient,
98f84d5a
PA
215 "content-range",
216 ) {
5bd065cb 217 match range::http2_parse_check_content_range(&value) {
98f84d5a
PA
218 Ok((_, v)) => {
219 range::http2_range_open(self, &v, flow, sfcm, flags, decompressed);
922a453d 220 if over && !self.file_range.is_null() {
98f84d5a
PA
221 range::http2_range_close(self, files, flags, &[])
222 }
223 }
224 _ => {
225 self.set_event(HTTP2Event::InvalidRange);
226 }
227 }
228 }
229 } else {
922a453d 230 if !self.file_range.is_null() {
98f84d5a
PA
231 if over {
232 range::http2_range_close(self, files, flags, decompressed)
233 } else {
234 range::http2_range_append(self.file_range, decompressed)
235 }
236 }
bb98a18b 237 }
6fe8bce3
PA
238 self.ft_tc.new_chunk(
239 sfcm,
240 files,
241 flags,
242 b"",
243 decompressed,
244 self.ft_tc.tracked, //offset = append
245 decompressed.len() as u32,
246 0,
247 over,
248 &xid,
249 );
250 } else {
bb98a18b
PA
251 self.ft_ts.tx_id = self.tx_id - 1;
252 if !self.ft_ts.file_open {
253 self.tx_data.incr_files_opened();
254 }
6fe8bce3
PA
255 self.ft_ts.new_chunk(
256 sfcm,
257 files,
258 flags,
259 b"",
260 decompressed,
261 self.ft_ts.tracked, //offset = append
262 decompressed.len() as u32,
263 0,
264 over,
265 &xid,
266 );
267 };
d8612282
PA
268 return Ok(());
269 }
270
1422b18a 271 fn handle_frame(
ee5b300c 272 &mut self, header: &parser::HTTP2FrameHeader, data: &HTTP2FrameTypeData, dir: Direction,
1422b18a
PA
273 ) {
274 //handle child_stream_id changes
275 match data {
276 HTTP2FrameTypeData::PUSHPROMISE(hs) => {
ee5b300c 277 if dir == Direction::ToClient {
1422b18a
PA
278 //we could set an event if self.child_stream_id != 0
279 if header.flags & parser::HTTP2_FLAG_HEADER_END_HEADERS == 0 {
280 self.child_stream_id = hs.stream_id;
281 }
282 self.state = HTTP2TransactionState::HTTP2StateReserved;
283 }
d8612282 284 self.handle_headers(&hs.blocks, dir);
1422b18a 285 }
d8612282 286 HTTP2FrameTypeData::CONTINUATION(hs) => {
ee5b300c 287 if dir == Direction::ToClient
1422b18a
PA
288 && header.flags & parser::HTTP2_FLAG_HEADER_END_HEADERS != 0
289 {
290 self.child_stream_id = 0;
291 }
d8612282 292 self.handle_headers(&hs.blocks, dir);
1422b18a 293 }
d8612282 294 HTTP2FrameTypeData::HEADERS(hs) => {
ee5b300c 295 if dir == Direction::ToClient {
1422b18a
PA
296 self.child_stream_id = 0;
297 }
d8612282 298 self.handle_headers(&hs.blocks, dir);
1422b18a
PA
299 }
300 HTTP2FrameTypeData::RSTSTREAM(_) => {
301 self.child_stream_id = 0;
302 }
303 _ => {}
304 }
305 //handle closing state changes
306 match data {
307 HTTP2FrameTypeData::HEADERS(_) | HTTP2FrameTypeData::DATA => {
308 if header.flags & parser::HTTP2_FLAG_HEADER_EOS != 0 {
309 match self.state {
b21acfbf
PA
310 HTTP2TransactionState::HTTP2StateHalfClosedClient
311 | HTTP2TransactionState::HTTP2StateDataServer => {
ee5b300c 312 if dir == Direction::ToClient {
1422b18a
PA
313 self.state = HTTP2TransactionState::HTTP2StateClosed;
314 }
315 }
316 HTTP2TransactionState::HTTP2StateHalfClosedServer => {
ee5b300c 317 if dir == Direction::ToServer {
1422b18a
PA
318 self.state = HTTP2TransactionState::HTTP2StateClosed;
319 }
320 }
b21acfbf 321 // do not revert back to a half closed state
1422b18a
PA
322 HTTP2TransactionState::HTTP2StateClosed => {}
323 HTTP2TransactionState::HTTP2StateGlobal => {}
324 _ => {
ee5b300c 325 if dir == Direction::ToClient {
1422b18a
PA
326 self.state = HTTP2TransactionState::HTTP2StateHalfClosedServer;
327 } else {
328 self.state = HTTP2TransactionState::HTTP2StateHalfClosedClient;
329 }
330 }
331 }
332 } else if header.ftype == parser::HTTP2FrameType::DATA as u8 {
333 //not end of stream
ee5b300c 334 if dir == Direction::ToServer {
b21acfbf
PA
335 if self.state < HTTP2TransactionState::HTTP2StateDataClient {
336 self.state = HTTP2TransactionState::HTTP2StateDataClient;
337 }
1422b18a 338 } else {
b21acfbf
PA
339 if self.state < HTTP2TransactionState::HTTP2StateDataServer {
340 self.state = HTTP2TransactionState::HTTP2StateDataServer;
341 }
1422b18a
PA
342 }
343 }
344 }
345 _ => {}
346 }
347 }
348}
349
350impl Drop for HTTP2Transaction {
351 fn drop(&mut self) {
352 self.free();
353 }
354}
355
cef2832d 356#[derive(AppLayerEvent)]
1422b18a 357pub enum HTTP2Event {
cef2832d 358 InvalidFrameHeader,
1422b18a
PA
359 InvalidClientMagic,
360 InvalidFrameData,
361 InvalidHeader,
362 InvalidFrameLength,
363 ExtraHeaderData,
364 LongFrameData,
365 StreamIdReuse,
6694737f 366 InvalidHTTP1Settings,
d8612282 367 FailedDecompression,
98f84d5a 368 InvalidRange,
1422b18a
PA
369}
370
c300a859
PA
371pub struct HTTP2DynTable {
372 pub table: Vec<parser::HTTP2FrameHeaderBlock>,
373 pub current_size: usize,
374 pub max_size: usize,
375 pub overflow: u8,
376}
377
378impl HTTP2DynTable {
379 pub fn new() -> Self {
380 Self {
381 table: Vec::with_capacity(64),
382 current_size: 0,
383 max_size: 4096, //default value
384 overflow: 0,
385 }
386 }
387}
388
1422b18a
PA
389pub struct HTTP2State {
390 tx_id: u64,
391 request_frame_size: u32,
392 response_frame_size: u32,
c300a859
PA
393 dynamic_headers_ts: HTTP2DynTable,
394 dynamic_headers_tc: HTTP2DynTable,
1422b18a
PA
395 transactions: Vec<HTTP2Transaction>,
396 progress: HTTP2ConnectionState,
5226ba1c 397 pub files: Files,
1422b18a
PA
398}
399
54e62ddf
JI
400impl State<HTTP2Transaction> for HTTP2State {
401 fn get_transactions(&self) -> &[HTTP2Transaction] {
402 &self.transactions
403 }
404}
405
1422b18a
PA
406impl HTTP2State {
407 pub fn new() -> Self {
408 Self {
409 tx_id: 0,
410 request_frame_size: 0,
411 response_frame_size: 0,
412 // the headers are encoded on one byte
413 // with a fixed number of static headers, and
414 // a variable number of dynamic headers
c300a859
PA
415 dynamic_headers_ts: HTTP2DynTable::new(),
416 dynamic_headers_tc: HTTP2DynTable::new(),
1422b18a
PA
417 transactions: Vec::new(),
418 progress: HTTP2ConnectionState::Http2StateInit,
3587033d 419 files: Files::default(),
1422b18a
PA
420 }
421 }
422
423 pub fn free(&mut self) {
0caaf6bd
PA
424 // this should be in HTTP2Transaction::free
425 // but we need state's file container cf https://redmine.openinfosecfoundation.org/issues/4444
426 for tx in &mut self.transactions {
427 if !tx.file_range.is_null() {
428 match unsafe { SC } {
429 None => panic!("BUG no suricata_config"),
430 Some(c) => {
431 (c.HTPFileCloseHandleRange)(
432 &mut self.files.files_tc,
433 0,
434 tx.file_range,
435 std::ptr::null_mut(),
436 0,
437 );
438 (c.HttpRangeFreeBlock)(tx.file_range);
439 tx.file_range = std::ptr::null_mut();
440 }
441 }
442 }
443 }
1422b18a 444 self.transactions.clear();
1422b18a
PA
445 }
446
6694737f 447 pub fn set_event(&mut self, event: HTTP2Event) {
1422b18a
PA
448 let len = self.transactions.len();
449 if len == 0 {
450 return;
451 }
452 let tx = &mut self.transactions[len - 1];
7732efbe 453 tx.tx_data.set_event(event as u8);
1422b18a
PA
454 }
455
456 // Free a transaction by ID.
457 fn free_tx(&mut self, tx_id: u64) {
458 let len = self.transactions.len();
459 let mut found = false;
460 let mut index = 0;
461 for i in 0..len {
0caaf6bd 462 let tx = &mut self.transactions[i];
1422b18a
PA
463 if tx.tx_id == tx_id + 1 {
464 found = true;
465 index = i;
c0231168
PA
466 // this should be in HTTP2Transaction::free
467 // but we need state's file container cf https://redmine.openinfosecfoundation.org/issues/4444
468 if !tx.file_range.is_null() {
469 match unsafe { SC } {
470 None => panic!("BUG no suricata_config"),
471 Some(c) => {
472 (c.HTPFileCloseHandleRange)(
473 &mut self.files.files_tc,
474 0,
475 tx.file_range,
476 std::ptr::null_mut(),
477 0,
478 );
479 (c.HttpRangeFreeBlock)(tx.file_range);
0caaf6bd 480 tx.file_range = std::ptr::null_mut();
c0231168
PA
481 }
482 }
483 }
1422b18a
PA
484 break;
485 }
486 }
487 if found {
488 self.transactions.remove(index);
489 }
490 }
491
492 pub fn get_tx(&mut self, tx_id: u64) -> Option<&HTTP2Transaction> {
493 for tx in &mut self.transactions {
494 if tx.tx_id == tx_id + 1 {
495 return Some(tx);
496 }
497 }
498 return None;
499 }
500
f5746633 501 fn find_tx_index(&mut self, sid: u32) -> usize {
1422b18a
PA
502 for i in 0..self.transactions.len() {
503 //reverse order should be faster
504 let idx = self.transactions.len() - 1 - i;
505 if sid == self.transactions[idx].stream_id {
1422b18a
PA
506 return idx + 1;
507 }
508 }
509 return 0;
510 }
511
512 fn find_child_stream_id(&mut self, sid: u32) -> u32 {
513 for i in 0..self.transactions.len() {
514 //reverse order should be faster
515 if sid == self.transactions[self.transactions.len() - 1 - i].stream_id {
516 if self.transactions[self.transactions.len() - 1 - i].child_stream_id > 0 {
517 return self.transactions[self.transactions.len() - 1 - i].child_stream_id;
518 }
519 return sid;
520 }
521 }
522 return sid;
523 }
524
525 fn create_global_tx(&mut self) -> &mut HTTP2Transaction {
526 //special transaction with only one frame
527 //as it affects the global connection, there is no end to it
528 let mut tx = HTTP2Transaction::new();
529 self.tx_id += 1;
530 tx.tx_id = self.tx_id;
531 tx.state = HTTP2TransactionState::HTTP2StateGlobal;
532 self.transactions.push(tx);
533 return self.transactions.last_mut().unwrap();
534 }
535
7011bddf 536 pub fn find_or_create_tx(
ee5b300c 537 &mut self, header: &parser::HTTP2FrameHeader, data: &HTTP2FrameTypeData, dir: Direction,
1422b18a
PA
538 ) -> &mut HTTP2Transaction {
539 if header.stream_id == 0 {
540 return self.create_global_tx();
541 }
542 let sid = match data {
543 //yes, the right stream_id for Suricata is not the header one
544 HTTP2FrameTypeData::PUSHPROMISE(hs) => hs.stream_id,
545 HTTP2FrameTypeData::CONTINUATION(_) => {
ee5b300c 546 if dir == Direction::ToClient {
1422b18a
PA
547 //continuation of a push promise
548 self.find_child_stream_id(header.stream_id)
549 } else {
550 header.stream_id
551 }
552 }
553 _ => header.stream_id,
554 };
f5746633 555 let index = self.find_tx_index(sid);
1422b18a 556 if index > 0 {
f5746633
PA
557 if self.transactions[index - 1].state == HTTP2TransactionState::HTTP2StateClosed {
558 //these frames can be received in this state for a short period
559 if header.ftype != parser::HTTP2FrameType::RSTSTREAM as u8
560 && header.ftype != parser::HTTP2FrameType::WINDOWUPDATE as u8
561 && header.ftype != parser::HTTP2FrameType::PRIORITY as u8
562 {
563 self.set_event(HTTP2Event::StreamIdReuse);
564 }
565 }
1422b18a
PA
566 return &mut self.transactions[index - 1];
567 } else {
568 let mut tx = HTTP2Transaction::new();
569 self.tx_id += 1;
570 tx.tx_id = self.tx_id;
571 tx.stream_id = sid;
572 tx.state = HTTP2TransactionState::HTTP2StateOpen;
573 self.transactions.push(tx);
574 return self.transactions.last_mut().unwrap();
575 }
576 }
577
ee5b300c 578 fn process_headers(&mut self, blocks: &Vec<parser::HTTP2FrameHeaderBlock>, dir: Direction) {
a63ee5ad
PA
579 let (mut update, mut sizeup) = (false, 0);
580 for i in 0..blocks.len() {
581 if blocks[i].error >= parser::HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeError {
582 self.set_event(HTTP2Event::InvalidHeader);
583 } else if blocks[i].error
584 == parser::HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeSizeUpdate
585 {
586 update = true;
587 if blocks[i].sizeupdate > sizeup {
588 sizeup = blocks[i].sizeupdate;
589 }
590 }
591 }
592 if update {
593 //borrow checker forbids to pass directly dyn_headers
ee5b300c 594 let dyn_headers = if dir == Direction::ToClient {
a63ee5ad
PA
595 &mut self.dynamic_headers_tc
596 } else {
597 &mut self.dynamic_headers_ts
598 };
599 dyn_headers.max_size = sizeup as usize;
600 }
601 }
602
1422b18a 603 fn parse_frame_data(
ee5b300c 604 &mut self, ftype: u8, input: &[u8], complete: bool, hflags: u8, dir: Direction,
1422b18a
PA
605 ) -> HTTP2FrameTypeData {
606 match num::FromPrimitive::from_u8(ftype) {
607 Some(parser::HTTP2FrameType::GOAWAY) => {
608 if input.len() < HTTP2_FRAME_GOAWAY_LEN {
609 self.set_event(HTTP2Event::InvalidFrameLength);
610 return HTTP2FrameTypeData::UNHANDLED(HTTP2FrameUnhandled {
611 reason: HTTP2FrameUnhandledReason::Incomplete,
612 });
613 }
614 match parser::http2_parse_frame_goaway(input) {
615 Ok((_, goaway)) => {
616 return HTTP2FrameTypeData::GOAWAY(goaway);
617 }
618 Err(_) => {
619 self.set_event(HTTP2Event::InvalidFrameData);
620 return HTTP2FrameTypeData::UNHANDLED(HTTP2FrameUnhandled {
621 reason: HTTP2FrameUnhandledReason::ParsingError,
622 });
623 }
624 }
625 }
626 Some(parser::HTTP2FrameType::SETTINGS) => {
627 match parser::http2_parse_frame_settings(input) {
628 Ok((_, set)) => {
c300a859
PA
629 for i in 0..set.len() {
630 if set[i].id == parser::HTTP2SettingsId::SETTINGSHEADERTABLESIZE {
1fd6f5bc 631 //reverse order as this is what we accept from the other endpoint
ee5b300c 632 let dyn_headers = if dir == Direction::ToClient {
1fd6f5bc
PA
633 &mut self.dynamic_headers_ts
634 } else {
635 &mut self.dynamic_headers_tc
636 };
637 dyn_headers.max_size = set[i].value as usize;
c300a859
PA
638 if set[i].value > HTTP2_MAX_TABLESIZE {
639 //mark potential overflow
1fd6f5bc 640 dyn_headers.overflow = 1;
c300a859
PA
641 } else {
642 //reset in case peer set a lower value, to be tested
1fd6f5bc 643 dyn_headers.overflow = 0;
c300a859
PA
644 }
645 }
646 }
1422b18a
PA
647 //we could set an event on remaining data
648 return HTTP2FrameTypeData::SETTINGS(set);
649 }
650 Err(nom::Err::Incomplete(_)) => {
651 if complete {
652 self.set_event(HTTP2Event::InvalidFrameData);
653 return HTTP2FrameTypeData::UNHANDLED(HTTP2FrameUnhandled {
654 reason: HTTP2FrameUnhandledReason::ParsingError,
655 });
656 } else {
657 return HTTP2FrameTypeData::UNHANDLED(HTTP2FrameUnhandled {
658 reason: HTTP2FrameUnhandledReason::TooLong,
659 });
660 }
661 }
662 Err(_) => {
663 self.set_event(HTTP2Event::InvalidFrameData);
664 return HTTP2FrameTypeData::UNHANDLED(HTTP2FrameUnhandled {
665 reason: HTTP2FrameUnhandledReason::ParsingError,
666 });
667 }
668 }
669 }
670 Some(parser::HTTP2FrameType::RSTSTREAM) => {
671 if input.len() != HTTP2_FRAME_RSTSTREAM_LEN {
672 self.set_event(HTTP2Event::InvalidFrameLength);
673 return HTTP2FrameTypeData::UNHANDLED(HTTP2FrameUnhandled {
674 reason: HTTP2FrameUnhandledReason::Incomplete,
675 });
676 } else {
677 match parser::http2_parse_frame_rststream(input) {
678 Ok((_, rst)) => {
679 return HTTP2FrameTypeData::RSTSTREAM(rst);
680 }
681 Err(_) => {
682 self.set_event(HTTP2Event::InvalidFrameData);
683 return HTTP2FrameTypeData::UNHANDLED(HTTP2FrameUnhandled {
684 reason: HTTP2FrameUnhandledReason::ParsingError,
685 });
686 }
687 }
688 }
689 }
690 Some(parser::HTTP2FrameType::PRIORITY) => {
691 if input.len() != HTTP2_FRAME_PRIORITY_LEN {
692 self.set_event(HTTP2Event::InvalidFrameLength);
693 return HTTP2FrameTypeData::UNHANDLED(HTTP2FrameUnhandled {
694 reason: HTTP2FrameUnhandledReason::Incomplete,
695 });
696 } else {
697 match parser::http2_parse_frame_priority(input) {
698 Ok((_, priority)) => {
699 return HTTP2FrameTypeData::PRIORITY(priority);
700 }
701 Err(_) => {
702 self.set_event(HTTP2Event::InvalidFrameData);
703 return HTTP2FrameTypeData::UNHANDLED(HTTP2FrameUnhandled {
704 reason: HTTP2FrameUnhandledReason::ParsingError,
705 });
706 }
707 }
708 }
709 }
710 Some(parser::HTTP2FrameType::WINDOWUPDATE) => {
711 if input.len() != HTTP2_FRAME_WINDOWUPDATE_LEN {
712 self.set_event(HTTP2Event::InvalidFrameLength);
713 return HTTP2FrameTypeData::UNHANDLED(HTTP2FrameUnhandled {
714 reason: HTTP2FrameUnhandledReason::Incomplete,
715 });
716 } else {
717 match parser::http2_parse_frame_windowupdate(input) {
718 Ok((_, wu)) => {
719 return HTTP2FrameTypeData::WINDOWUPDATE(wu);
720 }
721 Err(_) => {
722 self.set_event(HTTP2Event::InvalidFrameData);
723 return HTTP2FrameTypeData::UNHANDLED(HTTP2FrameUnhandled {
724 reason: HTTP2FrameUnhandledReason::ParsingError,
725 });
726 }
727 }
728 }
729 }
730 Some(parser::HTTP2FrameType::PUSHPROMISE) => {
ee5b300c 731 let dyn_headers = if dir == Direction::ToClient {
1422b18a
PA
732 &mut self.dynamic_headers_tc
733 } else {
734 &mut self.dynamic_headers_ts
735 };
736 match parser::http2_parse_frame_push_promise(input, hflags, dyn_headers) {
737 Ok((_, hs)) => {
a63ee5ad 738 self.process_headers(&hs.blocks, dir);
1422b18a
PA
739 return HTTP2FrameTypeData::PUSHPROMISE(hs);
740 }
741 Err(nom::Err::Incomplete(_)) => {
742 if complete {
743 self.set_event(HTTP2Event::InvalidFrameData);
744 return HTTP2FrameTypeData::UNHANDLED(HTTP2FrameUnhandled {
745 reason: HTTP2FrameUnhandledReason::ParsingError,
746 });
747 } else {
748 return HTTP2FrameTypeData::UNHANDLED(HTTP2FrameUnhandled {
749 reason: HTTP2FrameUnhandledReason::TooLong,
750 });
751 }
752 }
753 Err(_) => {
754 self.set_event(HTTP2Event::InvalidFrameData);
755 return HTTP2FrameTypeData::UNHANDLED(HTTP2FrameUnhandled {
756 reason: HTTP2FrameUnhandledReason::ParsingError,
757 });
758 }
759 }
760 }
761 Some(parser::HTTP2FrameType::DATA) => {
762 return HTTP2FrameTypeData::DATA;
763 }
764 Some(parser::HTTP2FrameType::CONTINUATION) => {
ee5b300c 765 let dyn_headers = if dir == Direction::ToClient {
1422b18a
PA
766 &mut self.dynamic_headers_tc
767 } else {
768 &mut self.dynamic_headers_ts
769 };
770 match parser::http2_parse_frame_continuation(input, dyn_headers) {
771 Ok((_, hs)) => {
a63ee5ad 772 self.process_headers(&hs.blocks, dir);
1422b18a
PA
773 return HTTP2FrameTypeData::CONTINUATION(hs);
774 }
775 Err(nom::Err::Incomplete(_)) => {
776 if complete {
777 self.set_event(HTTP2Event::InvalidFrameData);
778 return HTTP2FrameTypeData::UNHANDLED(HTTP2FrameUnhandled {
779 reason: HTTP2FrameUnhandledReason::ParsingError,
780 });
781 } else {
782 return HTTP2FrameTypeData::UNHANDLED(HTTP2FrameUnhandled {
783 reason: HTTP2FrameUnhandledReason::TooLong,
784 });
785 }
786 }
787 Err(_) => {
788 self.set_event(HTTP2Event::InvalidFrameData);
789 return HTTP2FrameTypeData::UNHANDLED(HTTP2FrameUnhandled {
790 reason: HTTP2FrameUnhandledReason::ParsingError,
791 });
792 }
793 }
794 }
795 Some(parser::HTTP2FrameType::HEADERS) => {
ee5b300c 796 let dyn_headers = if dir == Direction::ToClient {
1422b18a
PA
797 &mut self.dynamic_headers_tc
798 } else {
799 &mut self.dynamic_headers_ts
800 };
801 match parser::http2_parse_frame_headers(input, hflags, dyn_headers) {
802 Ok((hrem, hs)) => {
a63ee5ad 803 self.process_headers(&hs.blocks, dir);
1422b18a
PA
804 if hrem.len() > 0 {
805 SCLogDebug!("Remaining data for HTTP2 headers");
806 self.set_event(HTTP2Event::ExtraHeaderData);
807 }
808 return HTTP2FrameTypeData::HEADERS(hs);
809 }
810 Err(nom::Err::Incomplete(_)) => {
811 if complete {
812 self.set_event(HTTP2Event::InvalidFrameData);
813 return HTTP2FrameTypeData::UNHANDLED(HTTP2FrameUnhandled {
814 reason: HTTP2FrameUnhandledReason::ParsingError,
815 });
816 } else {
817 return HTTP2FrameTypeData::UNHANDLED(HTTP2FrameUnhandled {
818 reason: HTTP2FrameUnhandledReason::TooLong,
819 });
820 }
821 }
822 Err(_) => {
823 self.set_event(HTTP2Event::InvalidFrameData);
824 return HTTP2FrameTypeData::UNHANDLED(HTTP2FrameUnhandled {
825 reason: HTTP2FrameUnhandledReason::ParsingError,
826 });
827 }
828 }
829 }
830 Some(parser::HTTP2FrameType::PING) => {
831 return HTTP2FrameTypeData::PING;
832 }
833 _ => {
834 return HTTP2FrameTypeData::UNHANDLED(HTTP2FrameUnhandled {
835 reason: HTTP2FrameUnhandledReason::UnknownType,
836 });
837 }
838 }
839 }
840
98f84d5a 841 fn parse_frames(
ee5b300c 842 &mut self, mut input: &[u8], il: usize, dir: Direction, flow: *const Flow,
98f84d5a 843 ) -> AppLayerResult {
1422b18a
PA
844 while input.len() > 0 {
845 match parser::http2_parse_frame_header(input) {
846 Ok((rem, head)) => {
847 let hl = head.length as usize;
848
849 //we check for completeness first
850 if rem.len() < hl {
851 //but limit ourselves so as not to exhaust memory
852 if hl < HTTP2_MAX_HANDLED_FRAME_SIZE {
853 return AppLayerResult::incomplete(
854 (il - input.len()) as u32,
855 (HTTP2_FRAME_HEADER_LEN + hl) as u32,
856 );
857 } else if rem.len() < HTTP2_MIN_HANDLED_FRAME_SIZE {
858 return AppLayerResult::incomplete(
859 (il - input.len()) as u32,
860 (HTTP2_FRAME_HEADER_LEN + HTTP2_MIN_HANDLED_FRAME_SIZE) as u32,
861 );
862 } else {
863 self.set_event(HTTP2Event::LongFrameData);
864 self.request_frame_size = head.length - (rem.len() as u32);
865 }
866 }
867
868 //get a safe length for the buffer
869 let (hlsafe, complete) = if rem.len() < hl {
870 (rem.len(), false)
871 } else {
872 (hl, true)
873 };
874
875 if head.length == 0 && head.ftype == parser::HTTP2FrameType::SETTINGS as u8 {
876 input = &rem[hlsafe..];
877 continue;
878 }
879 let txdata = self.parse_frame_data(
880 head.ftype,
881 &rem[..hlsafe],
882 complete,
883 head.flags,
884 dir,
885 );
886
887 let tx = self.find_or_create_tx(&head, &txdata, dir);
888 tx.handle_frame(&head, &txdata, dir);
889 let over = head.flags & parser::HTTP2_FLAG_HEADER_EOS != 0;
1422b18a 890 let ftype = head.ftype;
f5746633 891 let sid = head.stream_id;
ee5b300c 892 if dir == Direction::ToServer {
1422b18a
PA
893 tx.frames_ts.push(HTTP2Frame {
894 header: head,
895 data: txdata,
896 });
897 } else {
898 tx.frames_tc.push(HTTP2Frame {
899 header: head,
900 data: txdata,
901 });
902 }
903 if ftype == parser::HTTP2FrameType::DATA as u8 {
f5746633
PA
904 match unsafe { SURICATA_HTTP2_FILE_CONFIG } {
905 Some(sfcm) => {
906 //borrow checker forbids to reuse directly tx
907 let index = self.find_tx_index(sid);
908 if index > 0 {
bb98a18b 909 let tx_same = &mut self.transactions[index - 1];
ee5b300c 910 let (files, flags) = self.files.get(dir);
d8612282
PA
911 match tx_same.decompress(
912 &rem[..hlsafe],
913 dir,
f5746633 914 sfcm,
d8612282 915 over,
f5746633
PA
916 files,
917 flags,
98f84d5a 918 flow,
d8612282
PA
919 ) {
920 Err(_e) => {
921 self.set_event(HTTP2Event::FailedDecompression);
922 }
bb98a18b 923 _ => {}
d8612282 924 }
f5746633
PA
925 }
926 }
aee8e601 927 None => panic!("no SURICATA_HTTP2_FILE_CONFIG"),
f5746633 928 }
1422b18a
PA
929 }
930 input = &rem[hlsafe..];
931 }
932 Err(nom::Err::Incomplete(_)) => {
933 //we may have consumed data from previous records
934 return AppLayerResult::incomplete(
935 (il - input.len()) as u32,
936 HTTP2_FRAME_HEADER_LEN as u32,
937 );
938 }
939 Err(_) => {
940 self.set_event(HTTP2Event::InvalidFrameHeader);
941 return AppLayerResult::err();
942 }
943 }
944 }
945 return AppLayerResult::ok();
946 }
947
98f84d5a 948 fn parse_ts(&mut self, mut input: &[u8], flow: *const Flow) -> AppLayerResult {
1422b18a 949 //very first : skip magic
1a88df7e 950 let mut magic_consumed = 0;
1422b18a
PA
951 if self.progress < HTTP2ConnectionState::Http2StateMagicDone {
952 //skip magic
953 if input.len() >= HTTP2_MAGIC_LEN {
954 //skip magic
955 match std::str::from_utf8(&input[..HTTP2_MAGIC_LEN]) {
956 Ok("PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n") => {
957 input = &input[HTTP2_MAGIC_LEN..];
1a88df7e 958 magic_consumed = HTTP2_MAGIC_LEN;
1422b18a
PA
959 }
960 Ok(&_) => {
961 self.set_event(HTTP2Event::InvalidClientMagic);
962 }
963 Err(_) => {
964 return AppLayerResult::err();
965 }
966 }
967 self.progress = HTTP2ConnectionState::Http2StateMagicDone;
968 } else {
969 //still more buffer
970 return AppLayerResult::incomplete(0 as u32, HTTP2_MAGIC_LEN as u32);
971 }
972 }
973 //first consume frame bytes
974 let il = input.len();
975 if self.request_frame_size > 0 {
976 let ilen = input.len() as u32;
977 if self.request_frame_size >= ilen {
978 self.request_frame_size -= ilen;
979 return AppLayerResult::ok();
980 } else {
981 let start = self.request_frame_size as usize;
982 input = &input[start..];
983 self.request_frame_size = 0;
984 }
985 }
986
987 //then parse all we can
ee5b300c 988 let r = self.parse_frames(input, il, Direction::ToServer, flow);
1a88df7e
PA
989 if r.status == 1 {
990 //adds bytes consumed by banner to incomplete result
991 return AppLayerResult::incomplete(r.consumed + magic_consumed as u32, r.needed);
992 } else {
993 return r;
994 }
1422b18a
PA
995 }
996
98f84d5a 997 fn parse_tc(&mut self, mut input: &[u8], flow: *const Flow) -> AppLayerResult {
1422b18a
PA
998 //first consume frame bytes
999 let il = input.len();
1000 if self.response_frame_size > 0 {
1001 let ilen = input.len() as u32;
1002 if self.response_frame_size >= ilen {
1003 self.response_frame_size -= ilen;
1004 return AppLayerResult::ok();
1005 } else {
1006 let start = self.response_frame_size as usize;
1007 input = &input[start..];
1008 self.response_frame_size = 0;
1009 }
1010 }
1011 //then parse all we can
ee5b300c 1012 return self.parse_frames(input, il, Direction::ToClient, flow);
1422b18a 1013 }
1422b18a
PA
1014}
1015
1016// C exports.
1017
1422b18a
PA
1018export_tx_data_get!(rs_http2_get_tx_data, HTTP2Transaction);
1019
1422b18a
PA
1020/// C entry point for a probing parser.
1021#[no_mangle]
363b5f99 1022pub unsafe extern "C" fn rs_http2_probing_parser_tc(
1422b18a
PA
1023 _flow: *const Flow, _direction: u8, input: *const u8, input_len: u32, _rdir: *mut u8,
1024) -> AppProto {
922a453d 1025 if !input.is_null() {
1422b18a
PA
1026 let slice = build_slice!(input, input_len as usize);
1027 match parser::http2_parse_frame_header(slice) {
1028 Ok((_, header)) => {
1029 if header.reserved != 0
1030 || header.length > HTTP2_DEFAULT_MAX_FRAME_SIZE
1031 || header.flags & 0xFE != 0
1032 || header.ftype != parser::HTTP2FrameType::SETTINGS as u8
1033 {
363b5f99 1034 return ALPROTO_FAILED;
1422b18a 1035 }
363b5f99 1036 return ALPROTO_HTTP2;
1422b18a
PA
1037 }
1038 Err(nom::Err::Incomplete(_)) => {
1039 return ALPROTO_UNKNOWN;
1040 }
1041 Err(_) => {
bb98a18b 1042 return ALPROTO_FAILED;
1422b18a
PA
1043 }
1044 }
1045 }
1046 return ALPROTO_UNKNOWN;
1047}
1048
7011bddf
PA
1049/// Extern functions operating on HTTP2.
1050extern "C" {
1051 pub fn HTTP2MimicHttp1Request(
1052 orig_state: *mut std::os::raw::c_void, new_state: *mut std::os::raw::c_void,
1053 );
1054}
1055
363b5f99
JI
1056// Suppress the unsafe warning here as creating a state for an app-layer
1057// is typically not unsafe.
1422b18a 1058#[no_mangle]
363b5f99 1059#[allow(clippy::not_unsafe_ptr_arg_deref)]
7011bddf
PA
1060pub extern "C" fn rs_http2_state_new(
1061 orig_state: *mut std::os::raw::c_void, _orig_proto: AppProto,
1062) -> *mut std::os::raw::c_void {
1422b18a
PA
1063 let state = HTTP2State::new();
1064 let boxed = Box::new(state);
53413f2d 1065 let r = Box::into_raw(boxed) as *mut _;
922a453d 1066 if !orig_state.is_null() {
707f0272 1067 //we could check ALPROTO_HTTP1 == orig_proto
7011bddf
PA
1068 unsafe {
1069 HTTP2MimicHttp1Request(orig_state, r);
1070 }
1071 }
1072 return r;
1422b18a
PA
1073}
1074
1075#[no_mangle]
363b5f99
JI
1076pub unsafe extern "C" fn rs_http2_state_free(state: *mut std::os::raw::c_void) {
1077 let mut state: Box<HTTP2State> = Box::from_raw(state as _);
1422b18a
PA
1078 state.free();
1079}
1080
1081#[no_mangle]
363b5f99 1082pub unsafe extern "C" fn rs_http2_state_tx_free(state: *mut std::os::raw::c_void, tx_id: u64) {
1422b18a
PA
1083 let state = cast_pointer!(state, HTTP2State);
1084 state.free_tx(tx_id);
1085}
1086
1087#[no_mangle]
363b5f99 1088pub unsafe extern "C" fn rs_http2_parse_ts(
1422b18a
PA
1089 flow: *const Flow, state: *mut std::os::raw::c_void, _pstate: *mut std::os::raw::c_void,
1090 input: *const u8, input_len: u32, _data: *const std::os::raw::c_void, _flags: u8,
1091) -> AppLayerResult {
1092 let state = cast_pointer!(state, HTTP2State);
1093 let buf = build_slice!(input, input_len as usize);
1094
ee5b300c 1095 state.files.flags_ts = FileFlowToFlags(flow, Direction::ToServer.into());
f5746633 1096 state.files.flags_ts = state.files.flags_ts | FILE_USE_DETECT;
98f84d5a 1097 return state.parse_ts(buf, flow);
1422b18a
PA
1098}
1099
1100#[no_mangle]
363b5f99 1101pub unsafe extern "C" fn rs_http2_parse_tc(
1422b18a
PA
1102 flow: *const Flow, state: *mut std::os::raw::c_void, _pstate: *mut std::os::raw::c_void,
1103 input: *const u8, input_len: u32, _data: *const std::os::raw::c_void, _flags: u8,
1104) -> AppLayerResult {
1105 let state = cast_pointer!(state, HTTP2State);
1106 let buf = build_slice!(input, input_len as usize);
ee5b300c 1107 state.files.flags_tc = FileFlowToFlags(flow, Direction::ToClient.into());
f5746633 1108 state.files.flags_tc = state.files.flags_tc | FILE_USE_DETECT;
98f84d5a 1109 return state.parse_tc(buf, flow);
1422b18a
PA
1110}
1111
1112#[no_mangle]
363b5f99 1113pub unsafe extern "C" fn rs_http2_state_get_tx(
1422b18a
PA
1114 state: *mut std::os::raw::c_void, tx_id: u64,
1115) -> *mut std::os::raw::c_void {
1116 let state = cast_pointer!(state, HTTP2State);
1117 match state.get_tx(tx_id) {
1118 Some(tx) => {
53413f2d 1119 return tx as *const _ as *mut _;
1422b18a
PA
1120 }
1121 None => {
1122 return std::ptr::null_mut();
1123 }
1124 }
1125}
1126
1127#[no_mangle]
363b5f99 1128pub unsafe extern "C" fn rs_http2_state_get_tx_count(state: *mut std::os::raw::c_void) -> u64 {
1422b18a
PA
1129 let state = cast_pointer!(state, HTTP2State);
1130 return state.tx_id;
1131}
1132
1422b18a 1133#[no_mangle]
bb98a18b
PA
1134pub unsafe extern "C" fn rs_http2_tx_get_state(
1135 tx: *mut std::os::raw::c_void,
1136) -> HTTP2TransactionState {
1422b18a
PA
1137 let tx = cast_pointer!(tx, HTTP2Transaction);
1138 return tx.state;
1139}
1140
1141#[no_mangle]
363b5f99 1142pub unsafe extern "C" fn rs_http2_tx_get_alstate_progress(
1422b18a
PA
1143 tx: *mut std::os::raw::c_void, _direction: u8,
1144) -> std::os::raw::c_int {
1145 return rs_http2_tx_get_state(tx) as i32;
1146}
1147
1422b18a 1148#[no_mangle]
363b5f99 1149pub unsafe extern "C" fn rs_http2_getfiles(
1422b18a
PA
1150 state: *mut std::os::raw::c_void, direction: u8,
1151) -> *mut FileContainer {
1152 let state = cast_pointer!(state, HTTP2State);
ee5b300c 1153 if direction == Direction::ToClient.into() {
1422b18a
PA
1154 &mut state.files.files_tc as *mut FileContainer
1155 } else {
1156 &mut state.files.files_ts as *mut FileContainer
1157 }
1158}
1159
1160// Parser name as a C style string.
1161const PARSER_NAME: &'static [u8] = b"http2\0";
1162
1163#[no_mangle]
1164pub unsafe extern "C" fn rs_http2_register_parser() {
9d1b030f 1165 let default_port = CString::new("[80]").unwrap();
1422b18a
PA
1166 let parser = RustParser {
1167 name: PARSER_NAME.as_ptr() as *const std::os::raw::c_char,
1168 default_port: default_port.as_ptr(),
1169 ipproto: IPPROTO_TCP,
1170 probe_ts: None, // big magic string should be enough
1171 probe_tc: Some(rs_http2_probing_parser_tc),
1172 min_depth: HTTP2_FRAME_HEADER_LEN as u16,
1173 max_depth: HTTP2_MAGIC_LEN as u16,
1174 state_new: rs_http2_state_new,
1175 state_free: rs_http2_state_free,
1176 tx_free: rs_http2_state_tx_free,
1177 parse_ts: rs_http2_parse_ts,
1178 parse_tc: rs_http2_parse_tc,
1179 get_tx_count: rs_http2_state_get_tx_count,
1180 get_tx: rs_http2_state_get_tx,
efc9a7a3
VJ
1181 tx_comp_st_ts: HTTP2TransactionState::HTTP2StateClosed as i32,
1182 tx_comp_st_tc: HTTP2TransactionState::HTTP2StateClosed as i32,
1422b18a 1183 tx_get_progress: rs_http2_tx_get_alstate_progress,
cef2832d
JI
1184 get_eventinfo: Some(HTTP2Event::get_event_info),
1185 get_eventinfo_byid: Some(HTTP2Event::get_event_info_by_id),
1422b18a
PA
1186 localstorage_new: None,
1187 localstorage_free: None,
1188 get_files: Some(rs_http2_getfiles),
54e62ddf 1189 get_tx_iterator: Some(applayer::state_get_tx_iterator::<HTTP2State, HTTP2Transaction>),
1422b18a
PA
1190 get_tx_data: rs_http2_get_tx_data,
1191 apply_tx_config: None,
1192 flags: 0,
4da0d9bd 1193 truncate: None,
1422b18a
PA
1194 };
1195
1196 let ip_proto_str = CString::new("tcp").unwrap();
1197
1198 if AppLayerProtoDetectConfProtoDetectionEnabled(ip_proto_str.as_ptr(), parser.name) != 0 {
1199 let alproto = AppLayerRegisterProtocolDetection(&parser, 1);
1200 ALPROTO_HTTP2 = alproto;
1201 if AppLayerParserConfParserEnabled(ip_proto_str.as_ptr(), parser.name) != 0 {
1202 let _ = AppLayerRegisterParser(&parser, alproto);
1203 }
1204 SCLogDebug!("Rust http2 parser registered.");
1205 } else {
1206 SCLogNotice!("Protocol detector and parser disabled for HTTP2.");
1207 }
1208}