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