/src/suricata8/rust/src/http2/http2.rs
Line | Count | Source |
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 crate::applayer::{self, *}; |
24 | | use crate::conf::{conf_get, get_memval}; |
25 | | use crate::core::*; |
26 | | use crate::direction::Direction; |
27 | | use crate::dns::dns::DnsVariant; |
28 | | use crate::filecontainer::*; |
29 | | use crate::filetracker::*; |
30 | | use crate::flow::Flow; |
31 | | use crate::frames::Frame; |
32 | | |
33 | | use crate::dns::dns::{dns_parse_request, dns_parse_response, DNSTransaction}; |
34 | | |
35 | | use nom7::Err; |
36 | | use std; |
37 | | use std::collections::VecDeque; |
38 | | use std::ffi::CString; |
39 | | use std::fmt; |
40 | | use std::io; |
41 | | use suricata_sys::sys::{ |
42 | | AppLayerParserState, AppProto, SCAppLayerForceProtocolChange, |
43 | | SCAppLayerParserConfParserEnabled, SCAppLayerParserRegisterLogger, |
44 | | SCAppLayerProtoDetectConfProtoDetectionEnabled, |
45 | | }; |
46 | | use suricata_sys::sys::AppProtoEnum::ALPROTO_HTTP1; |
47 | | |
48 | | static mut ALPROTO_HTTP2: AppProto = ALPROTO_UNKNOWN; |
49 | | static mut ALPROTO_DOH2: AppProto = ALPROTO_UNKNOWN; |
50 | | |
51 | | const HTTP2_DEFAULT_MAX_FRAME_SIZE: u32 = 16384; |
52 | | const HTTP2_MAX_HANDLED_FRAME_SIZE: usize = 65536; |
53 | | const HTTP2_MIN_HANDLED_FRAME_SIZE: usize = 256; |
54 | | |
55 | | pub static mut SURICATA_HTTP2_FILE_CONFIG: Option<&'static SuricataFileContext> = None; |
56 | | |
57 | | #[no_mangle] |
58 | 40 | pub extern "C" fn SCHttp2Init(context: &'static mut SuricataFileContext) { |
59 | 40 | unsafe { |
60 | 40 | SURICATA_HTTP2_FILE_CONFIG = Some(context); |
61 | 40 | } |
62 | 40 | } |
63 | | |
64 | | #[repr(u8)] |
65 | | #[derive(Copy, Clone, PartialOrd, PartialEq, Eq)] |
66 | | pub enum HTTP2ConnectionState { |
67 | | Http2StateInit = 0, |
68 | | Http2StateMagicDone = 1, |
69 | | } |
70 | | |
71 | | const HTTP2_FRAME_HEADER_LEN: usize = 9; |
72 | | const HTTP2_MAGIC_LEN: usize = 24; |
73 | | const HTTP2_FRAME_GOAWAY_LEN: usize = 8; |
74 | | const HTTP2_FRAME_RSTSTREAM_LEN: usize = 4; |
75 | | const HTTP2_FRAME_PRIORITY_LEN: usize = 5; |
76 | | const HTTP2_FRAME_WINDOWUPDATE_LEN: usize = 4; |
77 | | pub static mut HTTP2_MAX_TABLESIZE: u32 = 65536; // 0x10000 |
78 | | // maximum size of reassembly for header + continuation |
79 | | static mut HTTP2_MAX_REASS: usize = 102400; |
80 | | static mut HTTP2_MAX_STREAMS: usize = 4096; // 0x1000 |
81 | | static mut HTTP2_MAX_FRAMES: usize = 65536; |
82 | | pub(super) static mut HTTP2_COMPRESSION_BOMB_LIMIT: u64 = 1_048_576; |
83 | | |
84 | | #[derive(AppLayerFrameType)] |
85 | | pub enum Http2FrameType { |
86 | | Hdr, |
87 | | Data, |
88 | | Pdu, |
89 | | } |
90 | | |
91 | | #[repr(u8)] |
92 | | #[derive(Copy, Clone, PartialOrd, PartialEq, Eq, Debug)] |
93 | | pub enum HTTP2FrameUnhandledReason { |
94 | | UnknownType = 0, |
95 | | TooLong = 1, |
96 | | ParsingError = 2, |
97 | | Incomplete = 3, |
98 | | } |
99 | | |
100 | | impl fmt::Display for HTTP2FrameUnhandledReason { |
101 | 0 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
102 | 0 | write!(f, "{:?}", self) |
103 | 0 | } |
104 | | } |
105 | | |
106 | | #[derive(Debug)] |
107 | | pub struct HTTP2FrameUnhandled { |
108 | | pub reason: HTTP2FrameUnhandledReason, |
109 | | } |
110 | | |
111 | | #[derive(Debug)] |
112 | | pub enum HTTP2FrameTypeData { |
113 | | PRIORITY(parser::HTTP2FramePriority), |
114 | | GOAWAY(parser::HTTP2FrameGoAway), |
115 | | RSTSTREAM(parser::HTTP2FrameRstStream), |
116 | | SETTINGS(Vec<parser::HTTP2FrameSettings>), |
117 | | WINDOWUPDATE(parser::HTTP2FrameWindowUpdate), |
118 | | HEADERS(parser::HTTP2FrameHeaders), |
119 | | PUSHPROMISE(parser::HTTP2FramePushPromise), |
120 | | CONTINUATION(parser::HTTP2FrameContinuation), |
121 | | PING, |
122 | | DATA, |
123 | | //not a defined frame |
124 | | UNHANDLED(HTTP2FrameUnhandled), |
125 | | } |
126 | | |
127 | | #[repr(u8)] |
128 | | #[derive(Copy, Clone, PartialOrd, PartialEq, Eq, Debug)] |
129 | | pub enum HTTP2TransactionState { |
130 | | HTTP2StateIdle = 0, |
131 | | HTTP2StateOpen = 1, |
132 | | HTTP2StateReserved = 2, |
133 | | HTTP2StateDataClient = 3, |
134 | | HTTP2StateHalfClosedClient = 4, |
135 | | HTTP2StateDataServer = 5, |
136 | | HTTP2StateHalfClosedServer = 6, |
137 | | HTTP2StateClosed = 7, |
138 | | //not a RFC-defined state, used for stream 0 frames applying to the global connection |
139 | | HTTP2StateGlobal = 8, |
140 | | //not a RFC-defined state, dropping this old tx because we have too many |
141 | | HTTP2StateTodrop = 9, |
142 | | } |
143 | | |
144 | | #[derive(Debug)] |
145 | | pub struct HTTP2Frame { |
146 | | pub header: parser::HTTP2FrameHeader, |
147 | | pub data: HTTP2FrameTypeData, |
148 | | } |
149 | | |
150 | | #[derive(Debug, Default)] |
151 | | /// Dns Over HTTP2 Data inside a HTTP2 transaction |
152 | | pub struct DohHttp2Tx { |
153 | | /// wether the HTTP2 data is DNS, for both directions |
154 | | is_doh_data: [bool; 2], |
155 | | /// http2 data buffer to parse as DNS on completion |
156 | | pub data_buf: [Vec<u8>; 2], |
157 | | /// dns request transation |
158 | | pub dns_request_tx: Option<DNSTransaction>, |
159 | | /// dns response transation |
160 | | pub dns_response_tx: Option<DNSTransaction>, |
161 | | } |
162 | | |
163 | | #[derive(Debug)] |
164 | | pub struct HTTP2Transaction { |
165 | | tx_id: u64, |
166 | | pub stream_id: u32, |
167 | | pub state: HTTP2TransactionState, |
168 | | child_stream_id: u32, |
169 | | |
170 | | pub frames_tc: Vec<HTTP2Frame>, |
171 | | pub frames_ts: Vec<HTTP2Frame>, |
172 | | |
173 | | decoder: decompression::HTTP2Decoder, |
174 | | pub file_range: *mut HttpRangeContainerBlock, |
175 | | |
176 | | pub tx_data: AppLayerTxData, |
177 | | pub ft_tc: FileTransferTracker, |
178 | | pub ft_ts: FileTransferTracker, |
179 | | |
180 | | pub req_line: Vec<u8>, |
181 | | pub resp_line: Vec<u8>, |
182 | | |
183 | | pub doh: Option<DohHttp2Tx>, |
184 | | } |
185 | | |
186 | | impl Transaction for HTTP2Transaction { |
187 | 191M | fn id(&self) -> u64 { |
188 | 191M | self.tx_id |
189 | 191M | } |
190 | | } |
191 | | |
192 | | impl Default for HTTP2Transaction { |
193 | 0 | fn default() -> Self { |
194 | 0 | Self::new() |
195 | 0 | } |
196 | | } |
197 | | |
198 | | impl HTTP2Transaction { |
199 | 2.40M | pub fn new() -> Self { |
200 | 2.40M | Self { |
201 | 2.40M | tx_id: 0, |
202 | 2.40M | stream_id: 0, |
203 | 2.40M | child_stream_id: 0, |
204 | 2.40M | state: HTTP2TransactionState::HTTP2StateIdle, |
205 | 2.40M | frames_tc: Vec::new(), |
206 | 2.40M | frames_ts: Vec::new(), |
207 | 2.40M | decoder: decompression::HTTP2Decoder::new(), |
208 | 2.40M | file_range: std::ptr::null_mut(), |
209 | 2.40M | tx_data: AppLayerTxData::new(), |
210 | 2.40M | ft_tc: FileTransferTracker::new(), |
211 | 2.40M | ft_ts: FileTransferTracker::new(), |
212 | 2.40M | req_line: Vec::new(), |
213 | 2.40M | resp_line: Vec::new(), |
214 | 2.40M | doh: None, |
215 | 2.40M | } |
216 | 2.40M | } |
217 | | |
218 | 2.40M | pub fn free(&mut self) { |
219 | 2.40M | if !self.file_range.is_null() { |
220 | 0 | if let Some(c) = unsafe { SC } { |
221 | 0 | if let Some(sfcm) = unsafe { SURICATA_HTTP2_FILE_CONFIG } { |
222 | 0 | //TODO get a file container instead of NULL |
223 | 0 | (c.HTPFileCloseHandleRange)( |
224 | 0 | sfcm.files_sbcfg, |
225 | 0 | std::ptr::null_mut(), |
226 | 0 | 0, |
227 | 0 | self.file_range, |
228 | 0 | std::ptr::null_mut(), |
229 | 0 | 0, |
230 | 0 | ); |
231 | 0 | (c.HttpRangeFreeBlock)(self.file_range); |
232 | 0 | self.file_range = std::ptr::null_mut(); |
233 | 0 | } |
234 | 0 | } |
235 | 2.40M | } |
236 | 2.40M | } |
237 | | |
238 | 13.5M | pub fn set_event(&mut self, event: HTTP2Event) { |
239 | 13.5M | self.tx_data.set_event(event as u8); |
240 | 13.5M | } |
241 | | |
242 | 956k | fn handle_headers( |
243 | 956k | &mut self, blocks: &[parser::HTTP2FrameHeaderBlock], dir: Direction, |
244 | 956k | ) -> Option<Vec<u8>> { |
245 | 956k | let mut authority = None; |
246 | 956k | let mut path = None; |
247 | 956k | let mut doh = false; |
248 | 956k | let mut host = None; |
249 | 28.3M | for block in blocks { |
250 | 27.3M | if block.name.as_ref() == b"content-encoding" { |
251 | 43.6k | self.decoder.http2_encoding_fromvec(&block.value, dir); |
252 | 27.3M | } else if block.name.as_ref() == b"accept" { |
253 | | //TODO? faster pattern matching |
254 | 346k | if block.value.as_ref() == b"application/dns-message" { |
255 | 0 | doh = true; |
256 | 346k | } |
257 | 26.9M | } else if block.name.as_ref() == b"content-type" { |
258 | 27.0k | if block.value.as_ref() == b"application/dns-message" { |
259 | 273 | if let Some(doh) = &mut self.doh { |
260 | 237 | doh.is_doh_data[dir.index()] = true; |
261 | 237 | } else { |
262 | 36 | let mut doh = DohHttp2Tx::default(); |
263 | 36 | doh.is_doh_data[dir.index()] = true; |
264 | 36 | self.doh = Some(doh); |
265 | 36 | } |
266 | 26.7k | } |
267 | 26.9M | } else if block.name.as_ref() == b":path" { |
268 | 402k | path = Some(&block.value); |
269 | 26.5M | } else if block.name.eq_ignore_ascii_case(b":authority") { |
270 | 915k | authority = Some(&block.value); |
271 | 915k | if block.value.contains(&b'@') { |
272 | 115k | // it is forbidden by RFC 9113 to have userinfo in this field |
273 | 115k | // when in HTTP1 we can have user:password@domain.com |
274 | 115k | self.set_event(HTTP2Event::UserinfoInUri); |
275 | 800k | } |
276 | 25.6M | } else if block.name.eq_ignore_ascii_case(b"host") { |
277 | 146k | host = Some(&block.value); |
278 | 25.4M | } |
279 | | } |
280 | 956k | if let Some(a) = authority { |
281 | 164k | if let Some(h) = host { |
282 | 21.9k | if !a.eq_ignore_ascii_case(h) { |
283 | 21.3k | // The event is triggered only if both headers |
284 | 21.3k | // are in the same frame to avoid excessive |
285 | 21.3k | // complexity at runtime. |
286 | 21.3k | self.set_event(HTTP2Event::AuthorityHostMismatch); |
287 | 21.3k | } |
288 | 142k | } |
289 | 791k | } |
290 | 956k | if doh && unsafe { ALPROTO_DOH2 } != ALPROTO_UNKNOWN { |
291 | 0 | if let Some(p) = path { |
292 | 0 | if let Ok((_, dns_req)) = parser::doh_extract_request(p) { |
293 | 0 | return Some(dns_req); |
294 | 0 | } |
295 | 0 | } |
296 | 956k | } |
297 | 956k | return None; |
298 | 956k | } |
299 | | |
300 | 5.27M | pub fn update_file_flags(&mut self, flow_file_flags: u16) { |
301 | 5.27M | self.ft_ts.file_flags = unsafe { FileFlowFlagsToFlags(flow_file_flags, STREAM_TOSERVER) }; |
302 | 5.27M | self.ft_tc.file_flags = unsafe { FileFlowFlagsToFlags(flow_file_flags, STREAM_TOCLIENT) }; |
303 | 5.27M | } |
304 | | |
305 | 3.27M | fn decompress<'a>( |
306 | 3.27M | &'a mut self, input: &'a [u8], output: &'a mut Vec<u8>, dir: Direction, |
307 | 3.27M | sfcm: &'static SuricataFileContext, over: bool, flow: *const Flow, |
308 | 3.27M | ) -> io::Result<()> { |
309 | 3.27M | let decompressed = self.decoder.decompress(input, output, dir)?; |
310 | 3.26M | let xid: u32 = self.tx_id as u32; |
311 | 3.26M | if dir == Direction::ToClient { |
312 | 1.38M | self.ft_tc.tx_id = self.tx_id - 1; |
313 | | // Check that we are at the beginning of the file |
314 | 1.38M | if !self.ft_tc.is_initialized() { |
315 | | // we are now sure that new_chunk will open a file |
316 | | // even if it may close it right afterwards |
317 | 210k | self.tx_data.incr_files_opened(); |
318 | 210k | if let Ok(value) = detect::http2_frames_get_header_value_vec( |
319 | 210k | self, |
320 | 210k | Direction::ToClient, |
321 | 210k | "content-range", |
322 | 210k | ) { |
323 | 44.1k | match range::http2_parse_check_content_range(&value) { |
324 | 32.0k | Ok((_, v)) => { |
325 | 32.0k | range::http2_range_open( |
326 | 32.0k | self, |
327 | 32.0k | &v, |
328 | 32.0k | flow, |
329 | 32.0k | sfcm, |
330 | 32.0k | Direction::ToClient, |
331 | 32.0k | decompressed, |
332 | | ); |
333 | 32.0k | if over && !self.file_range.is_null() { |
334 | 6.23k | range::http2_range_close(self, Direction::ToClient, &[]) |
335 | 25.7k | } |
336 | | } |
337 | 12.1k | _ => { |
338 | 12.1k | self.set_event(HTTP2Event::InvalidRange); |
339 | 12.1k | } |
340 | | } |
341 | 166k | } |
342 | 1.17M | } else if !self.file_range.is_null() { |
343 | 20.4k | if over { |
344 | 12.3k | range::http2_range_close(self, Direction::ToClient, decompressed) |
345 | | } else { |
346 | 8.03k | range::http2_range_append(sfcm, self.file_range, decompressed) |
347 | | } |
348 | 1.15M | } |
349 | 1.38M | self.ft_tc.new_chunk( |
350 | 1.38M | sfcm, |
351 | 1.38M | b"", |
352 | 1.38M | decompressed, |
353 | 1.38M | self.ft_tc.tracked, //offset = append |
354 | 1.38M | decompressed.len() as u32, |
355 | | 0, |
356 | 1.38M | over, |
357 | 1.38M | &xid, |
358 | | ); |
359 | | } else { |
360 | 1.88M | self.ft_ts.tx_id = self.tx_id - 1; |
361 | 1.88M | if !self.ft_ts.file_open { |
362 | 1.12M | self.tx_data.incr_files_opened(); |
363 | 1.12M | } |
364 | 1.88M | self.ft_ts.new_chunk( |
365 | 1.88M | sfcm, |
366 | 1.88M | b"", |
367 | 1.88M | decompressed, |
368 | 1.88M | self.ft_ts.tracked, //offset = append |
369 | 1.88M | decompressed.len() as u32, |
370 | | 0, |
371 | 1.88M | over, |
372 | 1.88M | &xid, |
373 | | ); |
374 | | }; |
375 | 3.26M | if unsafe { ALPROTO_DOH2 } != ALPROTO_UNKNOWN { |
376 | | // we store DNS response, and process it when complete |
377 | 3.26M | if let Some(doh) = &mut self.doh { |
378 | 1.10k | if doh.is_doh_data[dir.index()] { |
379 | 0 | if doh.data_buf[dir.index()].len() + decompressed.len() <= 0xFFFF { |
380 | 0 | // a DNS message is U16_MAX |
381 | 0 | doh.data_buf[dir.index()].extend_from_slice(decompressed); |
382 | 0 | } else { |
383 | | // stop processing further data |
384 | 0 | doh.is_doh_data[dir.index()] = false; |
385 | 0 | if dir == Direction::ToClient { |
386 | 0 | self.set_event(HTTP2Event::DnsResponseTooLong); |
387 | 0 | } else { |
388 | 0 | self.set_event(HTTP2Event::DnsRequestTooLong); |
389 | 0 | } |
390 | | } |
391 | 1.10k | } |
392 | 3.26M | } |
393 | 0 | } |
394 | 3.26M | return Ok(()); |
395 | 3.27M | } |
396 | | |
397 | 7.41M | fn handle_frame( |
398 | 7.41M | &mut self, header: &parser::HTTP2FrameHeader, data: &HTTP2FrameTypeData, dir: Direction, |
399 | 7.41M | ) -> Option<Vec<u8>> { |
400 | | //handle child_stream_id changes |
401 | 7.41M | let mut r = None; |
402 | 7.41M | match data { |
403 | 9.97k | HTTP2FrameTypeData::PUSHPROMISE(hs) => { |
404 | 9.97k | if dir == Direction::ToClient { |
405 | | //we could set an event if self.child_stream_id != 0 |
406 | 4.94k | if header.flags & parser::HTTP2_FLAG_HEADER_END_HEADERS == 0 { |
407 | 2.41k | self.child_stream_id = hs.stream_id; |
408 | 2.52k | } |
409 | 4.94k | self.state = HTTP2TransactionState::HTTP2StateReserved; |
410 | 5.03k | } |
411 | 9.97k | r = self.handle_headers(&hs.blocks, dir); |
412 | | } |
413 | 835k | HTTP2FrameTypeData::CONTINUATION(hs) => { |
414 | 835k | if dir == Direction::ToClient |
415 | 520k | && header.flags & parser::HTTP2_FLAG_HEADER_END_HEADERS != 0 |
416 | 5.40k | { |
417 | 5.40k | self.child_stream_id = 0; |
418 | 829k | } |
419 | 835k | r = self.handle_headers(&hs.blocks, dir); |
420 | | } |
421 | 110k | HTTP2FrameTypeData::HEADERS(hs) => { |
422 | 110k | if dir == Direction::ToClient { |
423 | 23.9k | self.child_stream_id = 0; |
424 | 86.9k | } |
425 | 110k | r = self.handle_headers(&hs.blocks, dir); |
426 | | } |
427 | 7.46k | HTTP2FrameTypeData::RSTSTREAM(_) => { |
428 | 7.46k | self.child_stream_id = 0; |
429 | 7.46k | } |
430 | 6.45M | _ => {} |
431 | | } |
432 | | //handle closing state changes |
433 | 7.41M | match data { |
434 | | HTTP2FrameTypeData::HEADERS(_) | HTTP2FrameTypeData::DATA => { |
435 | 5.03M | if header.flags & parser::HTTP2_FLAG_HEADER_EOS != 0 { |
436 | 1.66M | match self.state { |
437 | | HTTP2TransactionState::HTTP2StateHalfClosedClient |
438 | | | HTTP2TransactionState::HTTP2StateDataServer => { |
439 | 1.03M | if dir == Direction::ToClient { |
440 | 130k | self.state = HTTP2TransactionState::HTTP2StateClosed; |
441 | 908k | } |
442 | | } |
443 | | HTTP2TransactionState::HTTP2StateHalfClosedServer => { |
444 | 207k | if dir == Direction::ToServer { |
445 | 65.2k | self.state = HTTP2TransactionState::HTTP2StateClosed; |
446 | 142k | } |
447 | | } |
448 | | // do not revert back to a half closed state |
449 | 24.8k | HTTP2TransactionState::HTTP2StateClosed => {} |
450 | 211k | HTTP2TransactionState::HTTP2StateGlobal => {} |
451 | | _ => { |
452 | 184k | if dir == Direction::ToClient { |
453 | 69.8k | self.state = HTTP2TransactionState::HTTP2StateHalfClosedServer; |
454 | 114k | } else { |
455 | 114k | self.state = HTTP2TransactionState::HTTP2StateHalfClosedClient; |
456 | 114k | } |
457 | | } |
458 | | } |
459 | 3.36M | } else if header.ftype == parser::HTTP2FrameType::Data as u8 { |
460 | | //not end of stream |
461 | 3.27M | if dir == Direction::ToServer { |
462 | 1.44M | if self.state < HTTP2TransactionState::HTTP2StateDataClient { |
463 | 21.5k | self.state = HTTP2TransactionState::HTTP2StateDataClient; |
464 | 1.42M | } |
465 | 1.82M | } else if self.state < HTTP2TransactionState::HTTP2StateDataServer { |
466 | 43.1k | self.state = HTTP2TransactionState::HTTP2StateDataServer; |
467 | 1.78M | } |
468 | 91.0k | } |
469 | | } |
470 | 2.38M | _ => {} |
471 | | } |
472 | 7.41M | return r; |
473 | 7.41M | } |
474 | | |
475 | 1.43M | fn handle_dns_data(&mut self, dir: Direction, flow: *mut Flow) { |
476 | 1.43M | if let Some(doh) = &mut self.doh { |
477 | 547 | if !doh.data_buf[dir.index()].is_empty() { |
478 | 0 | if dir.is_to_client() { |
479 | 0 | if let Ok(mut dtx) = dns_parse_response(&doh.data_buf[dir.index()]) { |
480 | 0 | dtx.id = 1; |
481 | 0 | doh.dns_response_tx = Some(dtx); |
482 | 0 | unsafe { |
483 | 0 | SCAppLayerForceProtocolChange(flow, ALPROTO_DOH2); |
484 | 0 | } |
485 | 0 | } |
486 | 0 | } else if let Ok(mut dtx) = |
487 | 0 | dns_parse_request(&doh.data_buf[dir.index()], &DnsVariant::Dns) |
488 | | { |
489 | 0 | dtx.id = 1; |
490 | 0 | doh.dns_request_tx = Some(dtx); |
491 | 0 | unsafe { |
492 | 0 | SCAppLayerForceProtocolChange(flow, ALPROTO_DOH2); |
493 | 0 | } |
494 | 0 | } |
495 | 0 | doh.data_buf[dir.index()].clear(); |
496 | 547 | } |
497 | 1.43M | } |
498 | 1.43M | } |
499 | | |
500 | 3.27M | fn handle_data_frame( |
501 | 3.27M | &mut self, rem: &[u8], hlsafe: usize, dir: Direction, flow: *mut Flow, padded: bool, |
502 | 3.27M | over: bool, |
503 | 3.27M | ) { |
504 | 3.27M | match unsafe { SURICATA_HTTP2_FILE_CONFIG } { |
505 | 3.27M | Some(sfcm) => { |
506 | 3.27M | if dir == Direction::ToServer { |
507 | 1.88M | self.ft_tc.tx_id = self.tx_id - 1; |
508 | 1.88M | } else { |
509 | 1.38M | self.ft_ts.tx_id = self.tx_id - 1; |
510 | 1.38M | }; |
511 | 3.27M | let mut dinput = &rem[..hlsafe]; |
512 | 3.27M | if padded && !rem.is_empty() && usize::from(rem[0]) < hlsafe { |
513 | 6.28k | dinput = &rem[1..hlsafe - usize::from(rem[0])]; |
514 | 3.26M | } |
515 | 3.27M | let mut output = Vec::with_capacity(decompression::HTTP2_DECOMPRESSION_CHUNK_SIZE); |
516 | 3.27M | match self.decompress(dinput, &mut output, dir, sfcm, over, flow) { |
517 | | Ok(_) => { |
518 | 3.26M | if over { |
519 | 1.43M | self.handle_dns_data(dir, flow); |
520 | 1.83M | } |
521 | | } |
522 | 1.27k | Err(e) => { |
523 | 1.27k | if e.kind() == io::ErrorKind::OutOfMemory { |
524 | 0 | self.set_event(HTTP2Event::CompressionBomb); |
525 | 1.27k | } else { |
526 | 1.27k | self.set_event(HTTP2Event::FailedDecompression); |
527 | 1.27k | } |
528 | | } |
529 | | } |
530 | | } |
531 | 0 | None => panic!("no SURICATA_HTTP2_FILE_CONFIG"), |
532 | | } |
533 | 3.27M | } |
534 | | } |
535 | | |
536 | | impl Drop for HTTP2Transaction { |
537 | 2.40M | fn drop(&mut self) { |
538 | 2.40M | if let Some(sfcm) = unsafe { SURICATA_HTTP2_FILE_CONFIG } { |
539 | 2.40M | self.ft_ts.file.free(sfcm); |
540 | 2.40M | self.ft_tc.file.free(sfcm); |
541 | 2.40M | } |
542 | 2.40M | self.free(); |
543 | 2.40M | } |
544 | | } |
545 | | |
546 | | #[derive(AppLayerEvent)] |
547 | | pub enum HTTP2Event { |
548 | | InvalidFrameHeader, |
549 | | InvalidClientMagic, |
550 | | InvalidFrameData, |
551 | | InvalidHeader, |
552 | | InvalidFrameLength, |
553 | | ExtraHeaderData, |
554 | | LongFrameData, |
555 | | StreamIdReuse, |
556 | | InvalidHttp1Settings, |
557 | | FailedDecompression, |
558 | | InvalidRange, |
559 | | HeaderIntegerOverflow, |
560 | | HeaderTableOverflow, |
561 | | TooManyStreams, |
562 | | AuthorityHostMismatch, |
563 | | UserinfoInUri, |
564 | | ReassemblyLimitReached, |
565 | | DnsRequestTooLong, |
566 | | DnsResponseTooLong, |
567 | | DataStreamZero, |
568 | | TooManyFrames, |
569 | | CompressionBomb, |
570 | | } |
571 | | |
572 | | pub struct HTTP2DynTable { |
573 | | pub table: Vec<parser::HTTP2FrameHeaderBlock>, |
574 | | pub current_size: usize, |
575 | | pub max_size: usize, |
576 | | pub overflow: u8, |
577 | | } |
578 | | |
579 | | impl Default for HTTP2DynTable { |
580 | 0 | fn default() -> Self { |
581 | 0 | Self::new() |
582 | 0 | } |
583 | | } |
584 | | |
585 | | impl HTTP2DynTable { |
586 | 28.5k | pub fn new() -> Self { |
587 | 28.5k | Self { |
588 | 28.5k | table: Vec::with_capacity(64), |
589 | 28.5k | current_size: 0, |
590 | 28.5k | max_size: 4096, //default value |
591 | 28.5k | overflow: 0, |
592 | 28.5k | } |
593 | 28.5k | } |
594 | | } |
595 | | |
596 | | #[derive(Default)] |
597 | | struct HTTP2HeaderReassemblyBuffer { |
598 | | data: Vec<u8>, |
599 | | stream_id: u32, |
600 | | } |
601 | | |
602 | | pub struct HTTP2State { |
603 | | state_data: AppLayerStateData, |
604 | | tx_id: u64, |
605 | | request_frame_size: u32, |
606 | | response_frame_size: u32, |
607 | | dynamic_headers_ts: HTTP2DynTable, |
608 | | dynamic_headers_tc: HTTP2DynTable, |
609 | | transactions: VecDeque<HTTP2Transaction>, |
610 | | progress: HTTP2ConnectionState, |
611 | | |
612 | | comp_len: u64, |
613 | | decomp_len: u64, |
614 | | |
615 | | c2s_buf: HTTP2HeaderReassemblyBuffer, |
616 | | s2c_buf: HTTP2HeaderReassemblyBuffer, |
617 | | } |
618 | | |
619 | | impl State<HTTP2Transaction> for HTTP2State { |
620 | 65.3M | fn get_transaction_count(&self) -> usize { |
621 | 65.3M | self.transactions.len() |
622 | 65.3M | } |
623 | | |
624 | 126M | fn get_transaction_by_index(&self, index: usize) -> Option<&HTTP2Transaction> { |
625 | 126M | self.transactions.get(index) |
626 | 126M | } |
627 | | } |
628 | | |
629 | | impl Default for HTTP2State { |
630 | 0 | fn default() -> Self { |
631 | 0 | Self::new() |
632 | 0 | } |
633 | | } |
634 | | |
635 | | impl HTTP2State { |
636 | 14.2k | pub fn new() -> Self { |
637 | 14.2k | Self { |
638 | 14.2k | state_data: AppLayerStateData::new(), |
639 | 14.2k | tx_id: 0, |
640 | 14.2k | request_frame_size: 0, |
641 | 14.2k | response_frame_size: 0, |
642 | 14.2k | // the headers are encoded on one byte |
643 | 14.2k | // with a fixed number of static headers, and |
644 | 14.2k | // a variable number of dynamic headers |
645 | 14.2k | dynamic_headers_ts: HTTP2DynTable::new(), |
646 | 14.2k | dynamic_headers_tc: HTTP2DynTable::new(), |
647 | 14.2k | transactions: VecDeque::new(), |
648 | 14.2k | progress: HTTP2ConnectionState::Http2StateInit, |
649 | 14.2k | comp_len: 0, |
650 | 14.2k | decomp_len: 0, |
651 | 14.2k | c2s_buf: HTTP2HeaderReassemblyBuffer::default(), |
652 | 14.2k | s2c_buf: HTTP2HeaderReassemblyBuffer::default(), |
653 | 14.2k | } |
654 | 14.2k | } |
655 | | |
656 | 14.2k | pub fn free(&mut self) { |
657 | | // this should be in HTTP2Transaction::free |
658 | | // but we need state's file container cf https://redmine.openinfosecfoundation.org/issues/4444 |
659 | 324k | for tx in &mut self.transactions { |
660 | 310k | if !tx.file_range.is_null() { |
661 | 962 | if let Some(c) = unsafe { SC } { |
662 | 962 | if let Some(sfcm) = unsafe { SURICATA_HTTP2_FILE_CONFIG } { |
663 | 962 | (c.HTPFileCloseHandleRange)( |
664 | 962 | sfcm.files_sbcfg, |
665 | 962 | &mut tx.ft_tc.file, |
666 | 962 | 0, |
667 | 962 | tx.file_range, |
668 | 962 | std::ptr::null_mut(), |
669 | 962 | 0, |
670 | 962 | ); |
671 | 962 | (c.HttpRangeFreeBlock)(tx.file_range); |
672 | 962 | tx.file_range = std::ptr::null_mut(); |
673 | 962 | } |
674 | 0 | } |
675 | 309k | } |
676 | | } |
677 | 14.2k | self.transactions.clear(); |
678 | 14.2k | } |
679 | | |
680 | 43.1k | pub fn set_event(&mut self, event: HTTP2Event) { |
681 | 43.1k | let len = self.transactions.len(); |
682 | 43.1k | if len == 0 { |
683 | 11.8k | return; |
684 | 31.2k | } |
685 | 31.2k | let tx = &mut self.transactions[len - 1]; |
686 | 31.2k | tx.tx_data.set_event(event as u8); |
687 | 43.1k | } |
688 | | |
689 | | // Free a transaction by ID. |
690 | 2.09M | fn free_tx(&mut self, tx_id: u64) { |
691 | 2.09M | let len = self.transactions.len(); |
692 | 2.09M | let mut found = false; |
693 | 2.09M | let mut index = 0; |
694 | 28.5M | for i in 0..len { |
695 | 28.5M | let tx = &mut self.transactions[i]; |
696 | 28.5M | if tx.tx_id == tx_id + 1 { |
697 | 2.09M | found = true; |
698 | 2.09M | index = i; |
699 | | // this should be in HTTP2Transaction::free |
700 | | // but we need state's file container cf https://redmine.openinfosecfoundation.org/issues/4444 |
701 | 2.09M | if !tx.file_range.is_null() { |
702 | 85 | if let Some(c) = unsafe { SC } { |
703 | 85 | if let Some(sfcm) = unsafe { SURICATA_HTTP2_FILE_CONFIG } { |
704 | 85 | (c.HTPFileCloseHandleRange)( |
705 | 85 | sfcm.files_sbcfg, |
706 | 85 | &mut tx.ft_tc.file, |
707 | 85 | 0, |
708 | 85 | tx.file_range, |
709 | 85 | std::ptr::null_mut(), |
710 | 85 | 0, |
711 | 85 | ); |
712 | 85 | (c.HttpRangeFreeBlock)(tx.file_range); |
713 | 85 | tx.file_range = std::ptr::null_mut(); |
714 | 85 | } |
715 | 0 | } |
716 | 2.09M | } |
717 | 2.09M | break; |
718 | 26.4M | } |
719 | | } |
720 | 2.09M | if found { |
721 | 2.09M | self.transactions.remove(index); |
722 | 2.09M | } |
723 | 2.09M | } |
724 | | |
725 | 0 | pub fn get_tx(&mut self, tx_id: u64) -> Option<&HTTP2Transaction> { |
726 | 0 | for tx in &mut self.transactions { |
727 | 0 | if tx.tx_id == tx_id + 1 { |
728 | 0 | tx.tx_data.update_file_flags(self.state_data.file_flags); |
729 | 0 | tx.update_file_flags(tx.tx_data.file_flags); |
730 | 0 | return Some(tx); |
731 | 0 | } |
732 | | } |
733 | 0 | return None; |
734 | 0 | } |
735 | | |
736 | 5.27M | fn find_tx_index(&mut self, sid: u32) -> usize { |
737 | 47.5M | for i in 0..self.transactions.len() { |
738 | | //reverse order should be faster |
739 | 47.5M | let idx = self.transactions.len() - 1 - i; |
740 | 47.5M | if sid == self.transactions[idx].stream_id { |
741 | 5.00M | return idx + 1; |
742 | 42.5M | } |
743 | | } |
744 | 266k | return 0; |
745 | 5.27M | } |
746 | | |
747 | 452k | fn find_child_stream_id(&mut self, sid: u32) -> u32 { |
748 | 2.15M | for i in 0..self.transactions.len() { |
749 | | //reverse order should be faster |
750 | 2.15M | if sid == self.transactions[self.transactions.len() - 1 - i].stream_id { |
751 | 427k | if self.transactions[self.transactions.len() - 1 - i].child_stream_id > 0 { |
752 | 0 | return self.transactions[self.transactions.len() - 1 - i].child_stream_id; |
753 | 427k | } |
754 | 427k | return sid; |
755 | 1.73M | } |
756 | | } |
757 | 24.1k | return sid; |
758 | 452k | } |
759 | | |
760 | 2.14M | fn create_global_tx(&mut self) -> &mut HTTP2Transaction { |
761 | | //special transaction with only one frame |
762 | | //as it affects the global connection, there is no end to it |
763 | 2.14M | let mut tx = HTTP2Transaction::new(); |
764 | 2.14M | self.tx_id += 1; |
765 | 2.14M | tx.tx_id = self.tx_id; |
766 | 2.14M | tx.state = HTTP2TransactionState::HTTP2StateGlobal; |
767 | | // a global tx (stream id 0) does not hold files cf RFC 9113 section 5.1.1 |
768 | 2.14M | self.transactions.push_back(tx); |
769 | 2.14M | return self.transactions.back_mut().unwrap(); |
770 | 2.14M | } |
771 | | |
772 | 7.41M | pub fn find_or_create_tx( |
773 | 7.41M | &mut self, header: &parser::HTTP2FrameHeader, data: &HTTP2FrameTypeData, dir: Direction, |
774 | 7.41M | ) -> Option<&mut HTTP2Transaction> { |
775 | 7.41M | if header.stream_id == 0 { |
776 | 2.14M | if self.transactions.len() >= unsafe { HTTP2_MAX_STREAMS } { |
777 | 143k | for tx_old in &mut self.transactions { |
778 | 143k | if tx_old.state == HTTP2TransactionState::HTTP2StateTodrop { |
779 | | // loop was already run |
780 | 0 | break; |
781 | 143k | } |
782 | 143k | tx_old.set_event(HTTP2Event::TooManyStreams); |
783 | | // use a distinct state, even if we do not log it |
784 | 143k | tx_old.state = HTTP2TransactionState::HTTP2StateTodrop; |
785 | 143k | tx_old.tx_data.updated_tc = true; |
786 | 143k | tx_old.tx_data.updated_ts = true; |
787 | | } |
788 | 35 | return None; |
789 | 2.14M | } |
790 | 2.14M | return Some(self.create_global_tx()); |
791 | 5.27M | } |
792 | 5.27M | let sid = match data { |
793 | | //yes, the right stream_id for Suricata is not the header one |
794 | 9.60k | HTTP2FrameTypeData::PUSHPROMISE(hs) => hs.stream_id, |
795 | | HTTP2FrameTypeData::CONTINUATION(_) => { |
796 | 678k | if dir == Direction::ToClient { |
797 | | //continuation of a push promise |
798 | 452k | self.find_child_stream_id(header.stream_id) |
799 | | } else { |
800 | 226k | header.stream_id |
801 | | } |
802 | | } |
803 | 4.58M | _ => header.stream_id, |
804 | | }; |
805 | 5.27M | let index = self.find_tx_index(sid); |
806 | 5.27M | if index > 0 { |
807 | 5.00M | if self.transactions[index - 1].state == HTTP2TransactionState::HTTP2StateClosed { |
808 | | //these frames can be received in this state for a short period |
809 | 28.0k | if header.ftype != parser::HTTP2FrameType::RstStream as u8 |
810 | 27.5k | && header.ftype != parser::HTTP2FrameType::WindowUpdate as u8 |
811 | 26.5k | && header.ftype != parser::HTTP2FrameType::Priority as u8 |
812 | 25.8k | { |
813 | 25.8k | self.set_event(HTTP2Event::StreamIdReuse); |
814 | 25.8k | } |
815 | 4.98M | } |
816 | | |
817 | 5.00M | let tx = &mut self.transactions[index - 1]; |
818 | 5.00M | tx.tx_data.update_file_flags(self.state_data.file_flags); |
819 | 5.00M | tx.update_file_flags(tx.tx_data.file_flags); |
820 | 5.00M | tx.tx_data.updated_tc = true; |
821 | 5.00M | tx.tx_data.updated_ts = true; |
822 | 5.00M | return Some(tx); |
823 | | } else { |
824 | | // do not use SETTINGS_MAX_CONCURRENT_STREAMS as it can grow too much |
825 | 266k | if self.transactions.len() >= unsafe { HTTP2_MAX_STREAMS } { |
826 | 16.3k | for tx_old in &mut self.transactions { |
827 | 16.3k | if tx_old.state == HTTP2TransactionState::HTTP2StateTodrop { |
828 | | // loop was already run |
829 | 0 | break; |
830 | 16.3k | } |
831 | 16.3k | tx_old.set_event(HTTP2Event::TooManyStreams); |
832 | | // use a distinct state, even if we do not log it |
833 | 16.3k | tx_old.state = HTTP2TransactionState::HTTP2StateTodrop; |
834 | 16.3k | tx_old.tx_data.updated_tc = true; |
835 | 16.3k | tx_old.tx_data.updated_ts = true; |
836 | | } |
837 | 4 | return None; |
838 | 266k | } |
839 | 266k | let mut tx = HTTP2Transaction::new(); |
840 | 266k | self.tx_id += 1; |
841 | 266k | tx.tx_id = self.tx_id; |
842 | 266k | tx.stream_id = sid; |
843 | 266k | tx.state = HTTP2TransactionState::HTTP2StateOpen; |
844 | 266k | tx.tx_data.update_file_flags(self.state_data.file_flags); |
845 | 266k | tx.update_file_flags(tx.tx_data.file_flags); |
846 | 266k | tx.tx_data.file_tx = STREAM_TOSERVER | STREAM_TOCLIENT; // might hold files in both directions |
847 | 266k | self.transactions.push_back(tx); |
848 | 266k | return Some(self.transactions.back_mut().unwrap()); |
849 | | } |
850 | 7.41M | } |
851 | | |
852 | 695k | fn process_headers( |
853 | 695k | &mut self, blocks: &Vec<parser::HTTP2FrameHeaderBlock>, dir: Direction, |
854 | 695k | ) -> Vec<HTTP2Event> { |
855 | 695k | let mut events = Vec::new(); |
856 | 28.0M | for block in blocks { |
857 | 27.3M | if block.error >= parser::HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeError { |
858 | 12.3M | events.push(HTTP2Event::InvalidHeader); |
859 | 15.0M | } else if block.error |
860 | 15.0M | == parser::HTTP2HeaderDecodeStatus::HTTP2HeaderDecodeIntegerOverflow |
861 | 0 | { |
862 | 0 | events.push(HTTP2Event::HeaderIntegerOverflow); |
863 | 15.0M | } |
864 | | } |
865 | 695k | let dyn_headers = if dir == Direction::ToClient { |
866 | 458k | &mut self.dynamic_headers_tc |
867 | | } else { |
868 | 236k | &mut self.dynamic_headers_ts |
869 | | }; |
870 | 695k | if dyn_headers.overflow == 2 { |
871 | 912 | // event only once |
872 | 912 | dyn_headers.overflow = 3; |
873 | 912 | events.push(HTTP2Event::HeaderTableOverflow); |
874 | 694k | } |
875 | 695k | return events; |
876 | 695k | } |
877 | | |
878 | 7.41M | fn parse_frame_data( |
879 | 7.41M | &mut self, head: &parser::HTTP2FrameHeader, input: &[u8], complete: bool, dir: Direction, |
880 | 7.41M | reass_limit_reached: &mut bool, |
881 | 7.41M | ) -> (HTTP2FrameTypeData, Vec<HTTP2Event>) { |
882 | 7.41M | let ftype = head.ftype; |
883 | 7.41M | let hflags = head.flags; |
884 | 7.41M | let mut events = Vec::new(); |
885 | 7.41M | match num::FromPrimitive::from_u8(ftype) { |
886 | | Some(parser::HTTP2FrameType::GoAway) => { |
887 | 36.7k | if input.len() < HTTP2_FRAME_GOAWAY_LEN { |
888 | 33.3k | events.push(HTTP2Event::InvalidFrameLength); |
889 | 33.3k | return ( |
890 | 33.3k | HTTP2FrameTypeData::UNHANDLED(HTTP2FrameUnhandled { |
891 | 33.3k | reason: HTTP2FrameUnhandledReason::Incomplete, |
892 | 33.3k | }), |
893 | 33.3k | events, |
894 | 33.3k | ); |
895 | 3.36k | } |
896 | 3.36k | match parser::http2_parse_frame_goaway(input) { |
897 | 3.36k | Ok((_, goaway)) => { |
898 | 3.36k | return (HTTP2FrameTypeData::GOAWAY(goaway), events); |
899 | | } |
900 | | Err(_) => { |
901 | 0 | events.push(HTTP2Event::InvalidFrameData); |
902 | 0 | return ( |
903 | 0 | HTTP2FrameTypeData::UNHANDLED(HTTP2FrameUnhandled { |
904 | 0 | reason: HTTP2FrameUnhandledReason::ParsingError, |
905 | 0 | }), |
906 | 0 | events, |
907 | 0 | ); |
908 | | } |
909 | | } |
910 | | } |
911 | | Some(parser::HTTP2FrameType::Settings) => { |
912 | 51.9k | match parser::http2_parse_frame_settings(input) { |
913 | 51.9k | Ok((_, set)) => { |
914 | 196k | for e in &set { |
915 | 144k | if e.id == parser::HTTP2SettingsId::HeaderTableSize { |
916 | | //reverse order as this is what we accept from the other endpoint |
917 | 132k | let dyn_headers = if dir == Direction::ToClient { |
918 | 123k | &mut self.dynamic_headers_ts |
919 | | } else { |
920 | 8.29k | &mut self.dynamic_headers_tc |
921 | | }; |
922 | 132k | dyn_headers.max_size = e.value as usize; |
923 | 132k | if e.value > unsafe { HTTP2_MAX_TABLESIZE } { |
924 | 10.2k | //mark potential overflow |
925 | 10.2k | dyn_headers.overflow = 1; |
926 | 122k | } else { |
927 | 122k | //reset in case peer set a lower value, to be tested |
928 | 122k | dyn_headers.overflow = 0; |
929 | 122k | } |
930 | 11.9k | } |
931 | | } |
932 | | //we could set an event on remaining data |
933 | 51.9k | return (HTTP2FrameTypeData::SETTINGS(set), events); |
934 | | } |
935 | | Err(Err::Incomplete(_)) => { |
936 | 0 | if complete { |
937 | 0 | events.push(HTTP2Event::InvalidFrameData); |
938 | 0 | return ( |
939 | 0 | HTTP2FrameTypeData::UNHANDLED(HTTP2FrameUnhandled { |
940 | 0 | reason: HTTP2FrameUnhandledReason::ParsingError, |
941 | 0 | }), |
942 | 0 | events, |
943 | 0 | ); |
944 | | } else { |
945 | 0 | return ( |
946 | 0 | HTTP2FrameTypeData::UNHANDLED(HTTP2FrameUnhandled { |
947 | 0 | reason: HTTP2FrameUnhandledReason::TooLong, |
948 | 0 | }), |
949 | 0 | events, |
950 | 0 | ); |
951 | | } |
952 | | } |
953 | | Err(_) => { |
954 | 0 | events.push(HTTP2Event::InvalidFrameData); |
955 | 0 | return ( |
956 | 0 | HTTP2FrameTypeData::UNHANDLED(HTTP2FrameUnhandled { |
957 | 0 | reason: HTTP2FrameUnhandledReason::ParsingError, |
958 | 0 | }), |
959 | 0 | events, |
960 | 0 | ); |
961 | | } |
962 | | } |
963 | | } |
964 | | Some(parser::HTTP2FrameType::RstStream) => { |
965 | 9.58k | if input.len() != HTTP2_FRAME_RSTSTREAM_LEN { |
966 | 2.11k | events.push(HTTP2Event::InvalidFrameLength); |
967 | 2.11k | return ( |
968 | 2.11k | HTTP2FrameTypeData::UNHANDLED(HTTP2FrameUnhandled { |
969 | 2.11k | reason: HTTP2FrameUnhandledReason::Incomplete, |
970 | 2.11k | }), |
971 | 2.11k | events, |
972 | 2.11k | ); |
973 | | } else { |
974 | 7.46k | match parser::http2_parse_frame_rststream(input) { |
975 | 7.46k | Ok((_, rst)) => { |
976 | 7.46k | return (HTTP2FrameTypeData::RSTSTREAM(rst), events); |
977 | | } |
978 | | Err(_) => { |
979 | 0 | events.push(HTTP2Event::InvalidFrameData); |
980 | 0 | return ( |
981 | 0 | HTTP2FrameTypeData::UNHANDLED(HTTP2FrameUnhandled { |
982 | 0 | reason: HTTP2FrameUnhandledReason::ParsingError, |
983 | 0 | }), |
984 | 0 | events, |
985 | 0 | ); |
986 | | } |
987 | | } |
988 | | } |
989 | | } |
990 | | Some(parser::HTTP2FrameType::Priority) => { |
991 | 41.6k | if input.len() != HTTP2_FRAME_PRIORITY_LEN { |
992 | 25.7k | events.push(HTTP2Event::InvalidFrameLength); |
993 | 25.7k | return ( |
994 | 25.7k | HTTP2FrameTypeData::UNHANDLED(HTTP2FrameUnhandled { |
995 | 25.7k | reason: HTTP2FrameUnhandledReason::Incomplete, |
996 | 25.7k | }), |
997 | 25.7k | events, |
998 | 25.7k | ); |
999 | | } else { |
1000 | 15.8k | match parser::http2_parse_frame_priority(input) { |
1001 | 15.8k | Ok((_, priority)) => { |
1002 | 15.8k | return (HTTP2FrameTypeData::PRIORITY(priority), events); |
1003 | | } |
1004 | | Err(_) => { |
1005 | 0 | events.push(HTTP2Event::InvalidFrameData); |
1006 | 0 | return ( |
1007 | 0 | HTTP2FrameTypeData::UNHANDLED(HTTP2FrameUnhandled { |
1008 | 0 | reason: HTTP2FrameUnhandledReason::ParsingError, |
1009 | 0 | }), |
1010 | 0 | events, |
1011 | 0 | ); |
1012 | | } |
1013 | | } |
1014 | | } |
1015 | | } |
1016 | | Some(parser::HTTP2FrameType::WindowUpdate) => { |
1017 | 327k | if input.len() != HTTP2_FRAME_WINDOWUPDATE_LEN { |
1018 | 323k | events.push(HTTP2Event::InvalidFrameLength); |
1019 | 323k | return ( |
1020 | 323k | HTTP2FrameTypeData::UNHANDLED(HTTP2FrameUnhandled { |
1021 | 323k | reason: HTTP2FrameUnhandledReason::Incomplete, |
1022 | 323k | }), |
1023 | 323k | events, |
1024 | 323k | ); |
1025 | | } else { |
1026 | 4.11k | match parser::http2_parse_frame_windowupdate(input) { |
1027 | 4.11k | Ok((_, wu)) => { |
1028 | 4.11k | return (HTTP2FrameTypeData::WINDOWUPDATE(wu), events); |
1029 | | } |
1030 | | Err(_) => { |
1031 | 0 | events.push(HTTP2Event::InvalidFrameData); |
1032 | 0 | return ( |
1033 | 0 | HTTP2FrameTypeData::UNHANDLED(HTTP2FrameUnhandled { |
1034 | 0 | reason: HTTP2FrameUnhandledReason::ParsingError, |
1035 | 0 | }), |
1036 | 0 | events, |
1037 | 0 | ); |
1038 | | } |
1039 | | } |
1040 | | } |
1041 | | } |
1042 | | Some(parser::HTTP2FrameType::PushPromise) => { |
1043 | 148k | let dyn_headers = if dir == Direction::ToClient { |
1044 | 75.5k | &mut self.dynamic_headers_tc |
1045 | | } else { |
1046 | 73.3k | &mut self.dynamic_headers_ts |
1047 | | }; |
1048 | 148k | match parser::http2_parse_frame_push_promise(input, hflags, dyn_headers) { |
1049 | 9.97k | Ok((_, hs)) => { |
1050 | 9.97k | events.extend(self.process_headers(&hs.blocks, dir)); |
1051 | 9.97k | return (HTTP2FrameTypeData::PUSHPROMISE(hs), events); |
1052 | | } |
1053 | | Err(Err::Incomplete(_)) => { |
1054 | 135k | if complete { |
1055 | 134k | events.push(HTTP2Event::InvalidFrameData); |
1056 | 134k | return ( |
1057 | 134k | HTTP2FrameTypeData::UNHANDLED(HTTP2FrameUnhandled { |
1058 | 134k | reason: HTTP2FrameUnhandledReason::ParsingError, |
1059 | 134k | }), |
1060 | 134k | events, |
1061 | 134k | ); |
1062 | | } else { |
1063 | 1.69k | return ( |
1064 | 1.69k | HTTP2FrameTypeData::UNHANDLED(HTTP2FrameUnhandled { |
1065 | 1.69k | reason: HTTP2FrameUnhandledReason::TooLong, |
1066 | 1.69k | }), |
1067 | 1.69k | events, |
1068 | 1.69k | ); |
1069 | | } |
1070 | | } |
1071 | | Err(_) => { |
1072 | 3.09k | events.push(HTTP2Event::InvalidFrameData); |
1073 | 3.09k | return ( |
1074 | 3.09k | HTTP2FrameTypeData::UNHANDLED(HTTP2FrameUnhandled { |
1075 | 3.09k | reason: HTTP2FrameUnhandledReason::ParsingError, |
1076 | 3.09k | }), |
1077 | 3.09k | events, |
1078 | 3.09k | ); |
1079 | | } |
1080 | | } |
1081 | | } |
1082 | | Some(parser::HTTP2FrameType::Data) => { |
1083 | 4.92M | return (HTTP2FrameTypeData::DATA, events); |
1084 | | } |
1085 | | Some(parser::HTTP2FrameType::Continuation) => { |
1086 | 1.06M | let buf = if dir == Direction::ToClient { |
1087 | 632k | &mut self.s2c_buf |
1088 | | } else { |
1089 | 437k | &mut self.c2s_buf |
1090 | | }; |
1091 | 1.06M | if head.stream_id == buf.stream_id { |
1092 | 188k | let max_reass = unsafe { HTTP2_MAX_REASS }; |
1093 | 188k | if buf.data.len() + input.len() < max_reass { |
1094 | 182k | buf.data.extend(input); |
1095 | 182k | } else if buf.data.len() < max_reass { |
1096 | 59 | buf.data.extend(&input[..max_reass - buf.data.len()]); |
1097 | 59 | *reass_limit_reached = true; |
1098 | 6.23k | } |
1099 | 188k | if head.flags & parser::HTTP2_FLAG_HEADER_END_HEADERS == 0 { |
1100 | 177k | let hs = parser::HTTP2FrameContinuation { blocks: Vec::new() }; |
1101 | 177k | return (HTTP2FrameTypeData::CONTINUATION(hs), events); |
1102 | 11.6k | } |
1103 | 880k | } // else try to parse anyways |
1104 | 892k | let input_reass = if head.stream_id == buf.stream_id { |
1105 | 11.6k | &buf.data |
1106 | | } else { |
1107 | 880k | input |
1108 | | }; |
1109 | | |
1110 | 892k | let dyn_headers = if dir == Direction::ToClient { |
1111 | 550k | &mut self.dynamic_headers_tc |
1112 | | } else { |
1113 | 341k | &mut self.dynamic_headers_ts |
1114 | | }; |
1115 | 892k | match parser::http2_parse_frame_continuation(input_reass, dyn_headers) { |
1116 | 658k | Ok((_, hs)) => { |
1117 | 658k | if head.stream_id == buf.stream_id { |
1118 | 2.91k | buf.stream_id = 0; |
1119 | 2.91k | buf.data.clear(); |
1120 | 655k | } |
1121 | 658k | events.extend(self.process_headers(&hs.blocks, dir)); |
1122 | 658k | return (HTTP2FrameTypeData::CONTINUATION(hs), events); |
1123 | | } |
1124 | | Err(Err::Incomplete(_)) => { |
1125 | 234k | if head.stream_id == buf.stream_id { |
1126 | 8.77k | buf.stream_id = 0; |
1127 | 8.77k | buf.data.clear(); |
1128 | 225k | } |
1129 | 234k | if complete { |
1130 | 232k | events.push(HTTP2Event::InvalidFrameData); |
1131 | 232k | return ( |
1132 | 232k | HTTP2FrameTypeData::UNHANDLED(HTTP2FrameUnhandled { |
1133 | 232k | reason: HTTP2FrameUnhandledReason::ParsingError, |
1134 | 232k | }), |
1135 | 232k | events, |
1136 | 232k | ); |
1137 | | } else { |
1138 | 1.44k | return ( |
1139 | 1.44k | HTTP2FrameTypeData::UNHANDLED(HTTP2FrameUnhandled { |
1140 | 1.44k | reason: HTTP2FrameUnhandledReason::TooLong, |
1141 | 1.44k | }), |
1142 | 1.44k | events, |
1143 | 1.44k | ); |
1144 | | } |
1145 | | } |
1146 | | Err(_) => { |
1147 | 0 | if head.stream_id == buf.stream_id { |
1148 | 0 | buf.stream_id = 0; |
1149 | 0 | buf.data.clear(); |
1150 | 0 | } |
1151 | 0 | events.push(HTTP2Event::InvalidFrameData); |
1152 | 0 | return ( |
1153 | 0 | HTTP2FrameTypeData::UNHANDLED(HTTP2FrameUnhandled { |
1154 | 0 | reason: HTTP2FrameUnhandledReason::ParsingError, |
1155 | 0 | }), |
1156 | 0 | events, |
1157 | 0 | ); |
1158 | | } |
1159 | | } |
1160 | | } |
1161 | | Some(parser::HTTP2FrameType::Headers) => { |
1162 | 267k | if head.flags & parser::HTTP2_FLAG_HEADER_END_HEADERS == 0 { |
1163 | 96.8k | let buf = if dir == Direction::ToClient { |
1164 | 12.0k | &mut self.s2c_buf |
1165 | | } else { |
1166 | 84.7k | &mut self.c2s_buf |
1167 | | }; |
1168 | 96.8k | buf.data.clear(); |
1169 | 96.8k | if let Ok((rem_hpack, hs)) = parser::get_frame_headers_hpack(input, hflags) { |
1170 | | // store only the HPACK fragment: skip Pad Length, Priority, and trailing padding |
1171 | 83.8k | buf.data.extend(rem_hpack); |
1172 | 83.8k | buf.stream_id = head.stream_id; |
1173 | 83.8k | return (HTTP2FrameTypeData::HEADERS(hs), events); |
1174 | | } else { |
1175 | 12.9k | events.push(HTTP2Event::InvalidFrameData); |
1176 | 12.9k | return ( |
1177 | 12.9k | HTTP2FrameTypeData::UNHANDLED(HTTP2FrameUnhandled { |
1178 | 12.9k | reason: HTTP2FrameUnhandledReason::ParsingError, |
1179 | 12.9k | }), |
1180 | 12.9k | events, |
1181 | 12.9k | ); |
1182 | | } |
1183 | 170k | } |
1184 | 170k | let dyn_headers = if dir == Direction::ToClient { |
1185 | 53.4k | &mut self.dynamic_headers_tc |
1186 | | } else { |
1187 | 116k | &mut self.dynamic_headers_ts |
1188 | | }; |
1189 | 170k | match parser::http2_parse_frame_headers(input, hflags, dyn_headers) { |
1190 | 27.0k | Ok((hrem, hs)) => { |
1191 | 27.0k | events.extend(self.process_headers(&hs.blocks, dir)); |
1192 | 27.0k | if !hrem.is_empty() { |
1193 | 2.00k | SCLogDebug!("Remaining data for HTTP2 headers"); |
1194 | 2.00k | events.push(HTTP2Event::ExtraHeaderData); |
1195 | 25.0k | } |
1196 | 27.0k | return (HTTP2FrameTypeData::HEADERS(hs), events); |
1197 | | } |
1198 | | Err(Err::Incomplete(_)) => { |
1199 | 132k | if complete { |
1200 | 132k | events.push(HTTP2Event::InvalidFrameData); |
1201 | 132k | return ( |
1202 | 132k | HTTP2FrameTypeData::UNHANDLED(HTTP2FrameUnhandled { |
1203 | 132k | reason: HTTP2FrameUnhandledReason::ParsingError, |
1204 | 132k | }), |
1205 | 132k | events, |
1206 | 132k | ); |
1207 | | } else { |
1208 | 373 | return ( |
1209 | 373 | HTTP2FrameTypeData::UNHANDLED(HTTP2FrameUnhandled { |
1210 | 373 | reason: HTTP2FrameUnhandledReason::TooLong, |
1211 | 373 | }), |
1212 | 373 | events, |
1213 | 373 | ); |
1214 | | } |
1215 | | } |
1216 | | Err(_) => { |
1217 | 10.9k | events.push(HTTP2Event::InvalidFrameData); |
1218 | 10.9k | return ( |
1219 | 10.9k | HTTP2FrameTypeData::UNHANDLED(HTTP2FrameUnhandled { |
1220 | 10.9k | reason: HTTP2FrameUnhandledReason::ParsingError, |
1221 | 10.9k | }), |
1222 | 10.9k | events, |
1223 | 10.9k | ); |
1224 | | } |
1225 | | } |
1226 | | } |
1227 | | Some(parser::HTTP2FrameType::Ping) => { |
1228 | 69.1k | return (HTTP2FrameTypeData::PING, events); |
1229 | | } |
1230 | | _ => { |
1231 | 473k | return ( |
1232 | 473k | HTTP2FrameTypeData::UNHANDLED(HTTP2FrameUnhandled { |
1233 | 473k | reason: HTTP2FrameUnhandledReason::UnknownType, |
1234 | 473k | }), |
1235 | 473k | events, |
1236 | 473k | ); |
1237 | | } |
1238 | | } |
1239 | 7.41M | } |
1240 | | |
1241 | 3.45M | fn parse_frames( |
1242 | 3.45M | &mut self, mut input: &[u8], il: usize, dir: Direction, flow: *mut Flow, |
1243 | 3.45M | stream_slice: &StreamSlice, |
1244 | 3.45M | ) -> AppLayerResult { |
1245 | 10.8M | while !input.is_empty() { |
1246 | 10.2M | match parser::http2_parse_frame_header(input) { |
1247 | 8.74M | Ok((rem, head)) => { |
1248 | 8.74M | let hl = head.length as usize; |
1249 | | |
1250 | | //we check for completeness first |
1251 | 8.74M | if rem.len() < hl { |
1252 | | //but limit ourselves so as not to exhaust memory |
1253 | 1.33M | if hl < HTTP2_MAX_HANDLED_FRAME_SIZE { |
1254 | 591k | return AppLayerResult::incomplete( |
1255 | 591k | (il - input.len()) as u32, |
1256 | 591k | (HTTP2_FRAME_HEADER_LEN + hl) as u32, |
1257 | | ); |
1258 | 742k | } else if rem.len() < HTTP2_MIN_HANDLED_FRAME_SIZE { |
1259 | 735k | return AppLayerResult::incomplete( |
1260 | 735k | (il - input.len()) as u32, |
1261 | 735k | (HTTP2_FRAME_HEADER_LEN + HTTP2_MIN_HANDLED_FRAME_SIZE) as u32, |
1262 | | ); |
1263 | | } else { |
1264 | 6.89k | self.set_event(HTTP2Event::LongFrameData); |
1265 | 6.89k | if dir == Direction::ToServer { |
1266 | 4.08k | self.request_frame_size = head.length - (rem.len() as u32); |
1267 | 4.08k | } else { |
1268 | 2.80k | self.response_frame_size = head.length - (rem.len() as u32); |
1269 | 2.80k | } |
1270 | | } |
1271 | 7.41M | } |
1272 | | |
1273 | | //get a safe length for the buffer |
1274 | 7.42M | let (hlsafe, complete) = if rem.len() < hl { |
1275 | 6.89k | (rem.len(), false) |
1276 | | } else { |
1277 | 7.41M | (hl, true) |
1278 | | }; |
1279 | | |
1280 | 7.42M | let frame_hdr = Frame::new( |
1281 | 7.42M | flow, |
1282 | 7.42M | stream_slice, |
1283 | 7.42M | input, |
1284 | 7.42M | HTTP2_FRAME_HEADER_LEN as i64, |
1285 | 7.42M | Http2FrameType::Hdr as u8, |
1286 | 7.42M | None, |
1287 | | ); |
1288 | 7.42M | let frame_data = Frame::new( |
1289 | 7.42M | flow, |
1290 | 7.42M | stream_slice, |
1291 | 7.42M | &input[HTTP2_FRAME_HEADER_LEN..], |
1292 | 7.42M | head.length as i64, |
1293 | 7.42M | Http2FrameType::Data as u8, |
1294 | 7.42M | None, |
1295 | | ); |
1296 | 7.42M | let frame_pdu = Frame::new( |
1297 | 7.42M | flow, |
1298 | 7.42M | stream_slice, |
1299 | 7.42M | input, |
1300 | 7.42M | HTTP2_FRAME_HEADER_LEN as i64 + head.length as i64, |
1301 | 7.42M | Http2FrameType::Pdu as u8, |
1302 | 7.42M | None, |
1303 | | ); |
1304 | 7.42M | if head.length == 0 && head.ftype == parser::HTTP2FrameType::Settings as u8 { |
1305 | 3.86k | input = &rem[hlsafe..]; |
1306 | 3.86k | continue; |
1307 | 7.41M | } |
1308 | 7.41M | let mut reass_limit_reached = false; |
1309 | 7.41M | let (txdata, events) = self.parse_frame_data( |
1310 | 7.41M | &head, |
1311 | 7.41M | &rem[..hlsafe], |
1312 | 7.41M | complete, |
1313 | 7.41M | dir, |
1314 | 7.41M | &mut reass_limit_reached, |
1315 | 7.41M | ); |
1316 | | |
1317 | 7.41M | let (comp_len, decomp_len) = (self.comp_len, self.decomp_len); |
1318 | 7.41M | let tx = self.find_or_create_tx(&head, &txdata, dir); |
1319 | 7.41M | if tx.is_none() { |
1320 | 39 | return AppLayerResult::err(); |
1321 | 7.41M | } |
1322 | 7.41M | let tx = tx.unwrap(); |
1323 | 20.6M | for event in events { |
1324 | 13.2M | tx.set_event(event); |
1325 | 13.2M | } |
1326 | | SCLogDebug!( |
1327 | | "tx stream_id {} tx id {} state {:?}", |
1328 | | tx.stream_id, |
1329 | | tx.tx_id, |
1330 | | tx.state |
1331 | | ); |
1332 | 7.41M | if let Some(frame) = frame_hdr { |
1333 | 0 | frame.set_tx(flow, tx.tx_id); |
1334 | 7.41M | } |
1335 | 7.41M | if let Some(frame) = frame_data { |
1336 | 0 | frame.set_tx(flow, tx.tx_id); |
1337 | 7.41M | } |
1338 | 7.41M | if let Some(frame) = frame_pdu { |
1339 | 0 | frame.set_tx(flow, tx.tx_id); |
1340 | 7.41M | } |
1341 | 7.41M | if let Some(doh_req_buf) = tx.handle_frame(&head, &txdata, dir) { |
1342 | 0 | if let Ok(mut dtx) = dns_parse_request(&doh_req_buf, &DnsVariant::Dns) { |
1343 | 0 | dtx.id = 1; |
1344 | 0 | unsafe { |
1345 | 0 | SCAppLayerForceProtocolChange(flow, ALPROTO_DOH2); |
1346 | 0 | } |
1347 | 0 | if let Some(doh) = &mut tx.doh { |
1348 | 0 | doh.dns_request_tx = Some(dtx); |
1349 | 0 | } else { |
1350 | 0 | let doh = DohHttp2Tx { |
1351 | 0 | dns_request_tx: Some(dtx), |
1352 | 0 | ..Default::default() |
1353 | 0 | }; |
1354 | 0 | tx.doh = Some(doh); |
1355 | 0 | } |
1356 | 0 | } |
1357 | 7.41M | } |
1358 | 7.41M | if reass_limit_reached { |
1359 | 59 | tx.tx_data |
1360 | 59 | .set_event(HTTP2Event::ReassemblyLimitReached as u8); |
1361 | 7.41M | } |
1362 | 7.41M | let over = head.flags & parser::HTTP2_FLAG_HEADER_EOS != 0; |
1363 | 7.41M | let ftype = head.ftype; |
1364 | 7.41M | let sid = head.stream_id; |
1365 | 7.41M | let padded = head.flags & parser::HTTP2_FLAG_HEADER_PADDED != 0; |
1366 | 7.41M | let h2frames = if dir == Direction::ToServer { |
1367 | 3.74M | &mut tx.frames_ts |
1368 | | } else { |
1369 | 3.66M | &mut tx.frames_tc |
1370 | | }; |
1371 | 7.41M | if h2frames.len() < unsafe { HTTP2_MAX_FRAMES } { |
1372 | 7.24M | h2frames.push(HTTP2Frame { |
1373 | 7.24M | header: head, |
1374 | 7.24M | data: txdata, |
1375 | 7.24M | }); |
1376 | 7.24M | } else { |
1377 | 169k | tx.tx_data.set_event(HTTP2Event::TooManyFrames as u8); |
1378 | 169k | } |
1379 | 7.41M | if ftype == parser::HTTP2FrameType::Data as u8 && sid == 0 { |
1380 | 1.65M | tx.tx_data.set_event(HTTP2Event::DataStreamZero as u8); |
1381 | 5.76M | } else if ftype == parser::HTTP2FrameType::Data as u8 && sid > 0 { |
1382 | 3.27M | tx.handle_data_frame(rem, hlsafe, dir, flow, padded, over); |
1383 | 3.27M | let (il, ol) = if dir == Direction::ToClient { |
1384 | 1.38M | ( |
1385 | 1.38M | tx.decoder.decoder_tc.input_len, |
1386 | 1.38M | tx.decoder.decoder_tc.output_len, |
1387 | 1.38M | ) |
1388 | | } else { |
1389 | 1.88M | ( |
1390 | 1.88M | tx.decoder.decoder_ts.input_len, |
1391 | 1.88M | tx.decoder.decoder_ts.output_len, |
1392 | 1.88M | ) |
1393 | | }; |
1394 | 3.27M | let (il, ol) = (il + comp_len, ol + decomp_len); |
1395 | 3.27M | if ol > decompression::DEFAULT_BOMB_RATIO * il { |
1396 | 0 | if ol > unsafe { HTTP2_COMPRESSION_BOMB_LIMIT } { |
1397 | 0 | tx.set_event(HTTP2Event::CompressionBomb); |
1398 | 0 | return AppLayerResult::err(); |
1399 | 0 | } |
1400 | 0 | if over { |
1401 | 0 | self.comp_len += il; |
1402 | 0 | self.decomp_len += ol; |
1403 | 0 | } |
1404 | 3.27M | } |
1405 | 2.49M | } |
1406 | 7.41M | sc_app_layer_parser_trigger_raw_stream_inspection(flow, dir as i32); |
1407 | 7.41M | input = &rem[hlsafe..]; |
1408 | | } |
1409 | | Err(Err::Incomplete(_)) => { |
1410 | | //we may have consumed data from previous records |
1411 | 1.51M | return AppLayerResult::incomplete( |
1412 | 1.51M | (il - input.len()) as u32, |
1413 | 1.51M | HTTP2_FRAME_HEADER_LEN as u32, |
1414 | | ); |
1415 | | } |
1416 | | Err(_) => { |
1417 | 0 | self.set_event(HTTP2Event::InvalidFrameHeader); |
1418 | 0 | return AppLayerResult::err(); |
1419 | | } |
1420 | | } |
1421 | | } |
1422 | 603k | return AppLayerResult::ok(); |
1423 | 3.45M | } |
1424 | | |
1425 | 1.92M | fn parse_ts(&mut self, flow: *mut Flow, stream_slice: StreamSlice) -> AppLayerResult { |
1426 | | //very first : skip magic |
1427 | 1.92M | let mut input = stream_slice.as_slice(); |
1428 | 1.92M | let mut magic_consumed = 0; |
1429 | 1.92M | if self.progress < HTTP2ConnectionState::Http2StateMagicDone { |
1430 | | //skip magic |
1431 | 60.8k | if input.len() >= HTTP2_MAGIC_LEN { |
1432 | | //skip magic |
1433 | 10.6k | match std::str::from_utf8(&input[..HTTP2_MAGIC_LEN]) { |
1434 | 10.4k | Ok("PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n") => { |
1435 | 57 | input = &input[HTTP2_MAGIC_LEN..]; |
1436 | 57 | magic_consumed = HTTP2_MAGIC_LEN; |
1437 | 57 | } |
1438 | 10.4k | Ok(&_) => { |
1439 | 10.4k | self.set_event(HTTP2Event::InvalidClientMagic); |
1440 | 10.4k | } |
1441 | | Err(_) => { |
1442 | 211 | return AppLayerResult::err(); |
1443 | | } |
1444 | | } |
1445 | 10.4k | self.progress = HTTP2ConnectionState::Http2StateMagicDone; |
1446 | | } else { |
1447 | | //still more buffer |
1448 | 50.2k | return AppLayerResult::incomplete(0_u32, HTTP2_MAGIC_LEN as u32); |
1449 | | } |
1450 | 1.86M | } |
1451 | | //first consume frame bytes |
1452 | 1.87M | let il = input.len(); |
1453 | 1.87M | if self.request_frame_size > 0 { |
1454 | 188k | let ilen = input.len() as u32; |
1455 | 188k | if self.request_frame_size >= ilen { |
1456 | 188k | self.request_frame_size -= ilen; |
1457 | 188k | return AppLayerResult::ok(); |
1458 | 101 | } else { |
1459 | 101 | let start = self.request_frame_size as usize; |
1460 | 101 | input = &input[start..]; |
1461 | 101 | self.request_frame_size = 0; |
1462 | 101 | } |
1463 | 1.68M | } |
1464 | | |
1465 | | //then parse all we can |
1466 | 1.68M | let r = self.parse_frames(input, il, Direction::ToServer, flow, &stream_slice); |
1467 | 1.68M | if r.status == 1 { |
1468 | | //adds bytes consumed by banner to incomplete result |
1469 | 1.39M | return AppLayerResult::incomplete(r.consumed + magic_consumed as u32, r.needed); |
1470 | | } else { |
1471 | 286k | return r; |
1472 | | } |
1473 | 1.92M | } |
1474 | | |
1475 | 1.91M | fn parse_tc(&mut self, flow: *mut Flow, stream_slice: StreamSlice) -> AppLayerResult { |
1476 | | //first consume frame bytes |
1477 | 1.91M | let mut input = stream_slice.as_slice(); |
1478 | 1.91M | let il = input.len(); |
1479 | 1.91M | if self.response_frame_size > 0 { |
1480 | 146k | let ilen = input.len() as u32; |
1481 | 146k | if self.response_frame_size >= ilen { |
1482 | 146k | self.response_frame_size -= ilen; |
1483 | 146k | return AppLayerResult::ok(); |
1484 | 102 | } else { |
1485 | 102 | let start = self.response_frame_size as usize; |
1486 | 102 | input = &input[start..]; |
1487 | 102 | self.response_frame_size = 0; |
1488 | 102 | } |
1489 | 1.76M | } |
1490 | | //then parse all we can |
1491 | 1.76M | return self.parse_frames(input, il, Direction::ToClient, flow, &stream_slice); |
1492 | 1.91M | } |
1493 | | } |
1494 | | |
1495 | | // C exports. |
1496 | | |
1497 | | #[no_mangle] |
1498 | 0 | pub unsafe extern "C" fn SCDoH2GetDnsTx( |
1499 | 0 | tx: &HTTP2Transaction, flags: u8, |
1500 | 0 | ) -> *mut std::os::raw::c_void { |
1501 | 0 | if let Some(doh) = &tx.doh { |
1502 | 0 | if flags & Direction::ToServer as u8 != 0 { |
1503 | 0 | if let Some(ref dtx) = &doh.dns_request_tx { |
1504 | 0 | return dtx as *const _ as *mut _; |
1505 | 0 | } |
1506 | 0 | } else if flags & Direction::ToClient as u8 != 0 { |
1507 | 0 | if let Some(ref dtx) = &doh.dns_response_tx { |
1508 | 0 | return dtx as *const _ as *mut _; |
1509 | 0 | } |
1510 | 0 | } |
1511 | 0 | } |
1512 | 0 | std::ptr::null_mut() |
1513 | 0 | } |
1514 | | |
1515 | | export_tx_data_get!(http2_get_tx_data, HTTP2Transaction); |
1516 | | export_state_data_get!(http2_get_state_data, HTTP2State); |
1517 | | |
1518 | | /// C entry point for a probing parser. |
1519 | 107 | unsafe extern "C" fn http2_probing_parser_tc( |
1520 | 107 | _flow: *const Flow, _direction: u8, input: *const u8, input_len: u32, _rdir: *mut u8, |
1521 | 107 | ) -> AppProto { |
1522 | 107 | if !input.is_null() { |
1523 | 107 | let slice = build_slice!(input, input_len as usize); |
1524 | 107 | match parser::http2_parse_frame_header(slice) { |
1525 | 107 | Ok((_, header)) => { |
1526 | 107 | if header.reserved != 0 |
1527 | 66 | || header.length > HTTP2_DEFAULT_MAX_FRAME_SIZE |
1528 | 15 | || header.flags & 0xFE != 0 |
1529 | 5 | || header.ftype != parser::HTTP2FrameType::Settings as u8 |
1530 | | { |
1531 | 105 | return ALPROTO_FAILED; |
1532 | 2 | } |
1533 | 2 | return ALPROTO_HTTP2; |
1534 | | } |
1535 | | Err(Err::Incomplete(_)) => { |
1536 | 0 | return ALPROTO_UNKNOWN; |
1537 | | } |
1538 | | Err(_) => { |
1539 | 0 | return ALPROTO_FAILED; |
1540 | | } |
1541 | | } |
1542 | 0 | } |
1543 | 0 | return ALPROTO_UNKNOWN; |
1544 | 107 | } |
1545 | | |
1546 | | // Extern functions operating on HTTP2. |
1547 | | extern "C" { |
1548 | | pub fn HTTP2MimicHttp1Request( |
1549 | | orig_state: *mut std::os::raw::c_void, new_state: *mut std::os::raw::c_void, |
1550 | | ); |
1551 | | } |
1552 | | |
1553 | | // Suppress the unsafe warning here as creating a state for an app-layer |
1554 | | // is typically not unsafe. |
1555 | | #[allow(clippy::not_unsafe_ptr_arg_deref)] |
1556 | 14.2k | extern "C" fn http2_state_new( |
1557 | 14.2k | orig_state: *mut std::os::raw::c_void, orig_proto: AppProto, |
1558 | 14.2k | ) -> *mut std::os::raw::c_void { |
1559 | 14.2k | let state = HTTP2State::new(); |
1560 | 14.2k | let boxed = Box::new(state); |
1561 | 14.2k | let r = Box::into_raw(boxed) as *mut _; |
1562 | 14.2k | if !orig_state.is_null() && orig_proto == ALPROTO_HTTP1 as u16 { |
1563 | 0 | unsafe { |
1564 | 0 | HTTP2MimicHttp1Request(orig_state, r); |
1565 | 0 | } |
1566 | 14.2k | } |
1567 | 14.2k | return r; |
1568 | 14.2k | } |
1569 | | |
1570 | 14.2k | unsafe extern "C" fn http2_state_free(state: *mut std::os::raw::c_void) { |
1571 | 14.2k | let mut state: Box<HTTP2State> = Box::from_raw(state as _); |
1572 | 14.2k | state.free(); |
1573 | 14.2k | } |
1574 | | |
1575 | 2.09M | unsafe extern "C" fn http2_state_tx_free(state: *mut std::os::raw::c_void, tx_id: u64) { |
1576 | 2.09M | let state = cast_pointer!(state, HTTP2State); |
1577 | 2.09M | state.free_tx(tx_id); |
1578 | 2.09M | } |
1579 | | |
1580 | 1.92M | unsafe extern "C" fn http2_parse_ts( |
1581 | 1.92M | flow: *mut Flow, state: *mut std::os::raw::c_void, _pstate: *mut AppLayerParserState, |
1582 | 1.92M | stream_slice: StreamSlice, _data: *const std::os::raw::c_void, |
1583 | 1.92M | ) -> AppLayerResult { |
1584 | 1.92M | let state = cast_pointer!(state, HTTP2State); |
1585 | 1.92M | return state.parse_ts(flow, stream_slice); |
1586 | 1.92M | } |
1587 | | |
1588 | 1.91M | unsafe extern "C" fn http2_parse_tc( |
1589 | 1.91M | flow: *mut Flow, state: *mut std::os::raw::c_void, _pstate: *mut AppLayerParserState, |
1590 | 1.91M | stream_slice: StreamSlice, _data: *const std::os::raw::c_void, |
1591 | 1.91M | ) -> AppLayerResult { |
1592 | 1.91M | let state = cast_pointer!(state, HTTP2State); |
1593 | 1.91M | return state.parse_tc(flow, stream_slice); |
1594 | 1.91M | } |
1595 | | |
1596 | 0 | unsafe extern "C" fn http2_state_get_tx( |
1597 | 0 | state: *mut std::os::raw::c_void, tx_id: u64, |
1598 | 0 | ) -> *mut std::os::raw::c_void { |
1599 | 0 | let state = cast_pointer!(state, HTTP2State); |
1600 | 0 | match state.get_tx(tx_id) { |
1601 | 0 | Some(tx) => { |
1602 | 0 | return tx as *const _ as *mut _; |
1603 | | } |
1604 | | None => { |
1605 | 0 | return std::ptr::null_mut(); |
1606 | | } |
1607 | | } |
1608 | 0 | } |
1609 | | |
1610 | 11.4M | unsafe extern "C" fn http2_state_get_tx_count(state: *mut std::os::raw::c_void) -> u64 { |
1611 | 11.4M | let state = cast_pointer!(state, HTTP2State); |
1612 | 11.4M | return state.tx_id; |
1613 | 11.4M | } |
1614 | | |
1615 | 67.3M | unsafe extern "C" fn http2_tx_get_state(tx: *mut std::os::raw::c_void) -> HTTP2TransactionState { |
1616 | 67.3M | let tx = cast_pointer!(tx, HTTP2Transaction); |
1617 | 67.3M | return tx.state; |
1618 | 67.3M | } |
1619 | | |
1620 | 67.3M | unsafe extern "C" fn http2_tx_get_alstate_progress( |
1621 | 67.3M | tx: *mut std::os::raw::c_void, _direction: u8, |
1622 | 67.3M | ) -> std::os::raw::c_int { |
1623 | 67.3M | return http2_tx_get_state(tx) as i32; |
1624 | 67.3M | } |
1625 | | |
1626 | 30.9M | unsafe extern "C" fn http2_getfiles( |
1627 | 30.9M | tx: *mut std::os::raw::c_void, direction: u8, |
1628 | 30.9M | ) -> AppLayerGetFileState { |
1629 | 30.9M | let tx = cast_pointer!(tx, HTTP2Transaction); |
1630 | 30.9M | if let Some(sfcm) = { SURICATA_HTTP2_FILE_CONFIG } { |
1631 | 30.9M | if direction & STREAM_TOSERVER != 0 { |
1632 | 15.4M | return AppLayerGetFileState { |
1633 | 15.4M | fc: &mut tx.ft_ts.file, |
1634 | 15.4M | cfg: sfcm.files_sbcfg, |
1635 | 15.4M | }; |
1636 | | } else { |
1637 | 15.4M | return AppLayerGetFileState { |
1638 | 15.4M | fc: &mut tx.ft_tc.file, |
1639 | 15.4M | cfg: sfcm.files_sbcfg, |
1640 | 15.4M | }; |
1641 | | } |
1642 | 0 | } |
1643 | 0 | AppLayerGetFileState::err() |
1644 | 30.9M | } |
1645 | | |
1646 | | // Parser name as a C style string. |
1647 | | const PARSER_NAME: &[u8] = b"http2\0"; |
1648 | | |
1649 | | #[no_mangle] |
1650 | 40 | pub unsafe extern "C" fn SCRegisterHttp2Parser() { |
1651 | 40 | let default_port = CString::new("[80]").unwrap(); |
1652 | 40 | let mut parser = RustParser { |
1653 | 40 | name: PARSER_NAME.as_ptr() as *const std::os::raw::c_char, |
1654 | 40 | default_port: default_port.as_ptr(), |
1655 | 40 | ipproto: IPPROTO_TCP, |
1656 | 40 | probe_ts: None, // big magic string should be enough |
1657 | 40 | probe_tc: Some(http2_probing_parser_tc), |
1658 | 40 | min_depth: HTTP2_FRAME_HEADER_LEN as u16, |
1659 | 40 | max_depth: HTTP2_MAGIC_LEN as u16, |
1660 | 40 | state_new: http2_state_new, |
1661 | 40 | state_free: http2_state_free, |
1662 | 40 | tx_free: http2_state_tx_free, |
1663 | 40 | parse_ts: http2_parse_ts, |
1664 | 40 | parse_tc: http2_parse_tc, |
1665 | 40 | get_tx_count: http2_state_get_tx_count, |
1666 | 40 | get_tx: http2_state_get_tx, |
1667 | 40 | tx_comp_st_ts: HTTP2TransactionState::HTTP2StateClosed as i32, |
1668 | 40 | tx_comp_st_tc: HTTP2TransactionState::HTTP2StateClosed as i32, |
1669 | 40 | tx_get_progress: http2_tx_get_alstate_progress, |
1670 | 40 | get_eventinfo: Some(HTTP2Event::get_event_info), |
1671 | 40 | get_eventinfo_byid: Some(HTTP2Event::get_event_info_by_id), |
1672 | 40 | localstorage_new: None, |
1673 | 40 | localstorage_free: None, |
1674 | 40 | get_tx_files: Some(http2_getfiles), |
1675 | 40 | get_tx_iterator: Some(applayer::state_get_tx_iterator::<HTTP2State, HTTP2Transaction>), |
1676 | 40 | get_tx_data: http2_get_tx_data, |
1677 | 40 | get_state_data: http2_get_state_data, |
1678 | 40 | apply_tx_config: None, |
1679 | 40 | flags: 0, |
1680 | 40 | get_frame_id_by_name: Some(Http2FrameType::ffi_id_from_name), |
1681 | 40 | get_frame_name_by_id: Some(Http2FrameType::ffi_name_from_id), |
1682 | 40 | get_state_id_by_name: None, |
1683 | 40 | get_state_name_by_id: None, |
1684 | 40 | }; |
1685 | | |
1686 | 40 | let ip_proto_str = CString::new("tcp").unwrap(); |
1687 | | |
1688 | 40 | if SCAppLayerProtoDetectConfProtoDetectionEnabled(ip_proto_str.as_ptr(), parser.name) != 0 { |
1689 | 40 | let alproto = AppLayerRegisterProtocolDetection(&parser, 1); |
1690 | 40 | ALPROTO_HTTP2 = alproto; |
1691 | 40 | if SCAppLayerParserConfParserEnabled(ip_proto_str.as_ptr(), parser.name) != 0 { |
1692 | 40 | let _ = AppLayerRegisterParser(&parser, alproto); |
1693 | 40 | } |
1694 | 40 | if let Some(val) = conf_get("app-layer.protocols.http2.max-streams") { |
1695 | 0 | if let Ok(v) = val.parse::<usize>() { |
1696 | 0 | HTTP2_MAX_STREAMS = v; |
1697 | 0 | } else { |
1698 | 0 | SCLogError!("Invalid value for http2.max-streams"); |
1699 | | } |
1700 | 40 | } |
1701 | 40 | if let Some(val) = conf_get("app-layer.protocols.http2.max-frames") { |
1702 | 0 | if let Ok(v) = val.parse::<usize>() { |
1703 | 0 | HTTP2_MAX_FRAMES = v; |
1704 | 0 | } else { |
1705 | 0 | SCLogError!("Invalid value for http2.max-frames"); |
1706 | | } |
1707 | 40 | } |
1708 | 40 | if let Some(val) = conf_get("app-layer.protocols.http2.max-table-size") { |
1709 | 0 | if let Ok(v) = val.parse::<u32>() { |
1710 | 0 | HTTP2_MAX_TABLESIZE = v; |
1711 | 0 | } else { |
1712 | 0 | SCLogError!("Invalid value for http2.max-table-size"); |
1713 | | } |
1714 | 40 | } |
1715 | 40 | if let Some(val) = conf_get("app-layer.protocols.http2.max-reassembly-size") { |
1716 | 0 | if let Ok(v) = val.parse::<u32>() { |
1717 | 0 | HTTP2_MAX_REASS = v as usize; |
1718 | 0 | } else { |
1719 | 0 | SCLogError!("Invalid value for http2.max-reassembly-size"); |
1720 | | } |
1721 | 40 | } |
1722 | 40 | if let Some(val) = conf_get("app-layer.protocols.http2.compression-bomb-limit") { |
1723 | 0 | if let Ok(v) = get_memval(val) { |
1724 | 0 | HTTP2_COMPRESSION_BOMB_LIMIT = v; |
1725 | 0 | } else { |
1726 | 0 | SCLogWarning!("Invalid value for http2.compression-bomb-limit"); |
1727 | | } |
1728 | 40 | } |
1729 | 40 | SCAppLayerParserRegisterLogger(IPPROTO_TCP, ALPROTO_HTTP2); |
1730 | | SCLogDebug!("Rust http2 parser registered."); |
1731 | | } else { |
1732 | 0 | SCLogNotice!("Protocol detector and parser disabled for HTTP2."); |
1733 | | } |
1734 | | |
1735 | | // doh2 is just http2 wrapped in another name |
1736 | 40 | parser.name = b"doh2\0".as_ptr() as *const std::os::raw::c_char; |
1737 | 40 | parser.probe_tc = None; |
1738 | 40 | parser.default_port = std::ptr::null(); |
1739 | 40 | if SCAppLayerProtoDetectConfProtoDetectionEnabled(ip_proto_str.as_ptr(), parser.name) != 0 { |
1740 | 40 | let alproto = AppLayerRegisterProtocolDetection(&parser, 1); |
1741 | 40 | ALPROTO_DOH2 = alproto; |
1742 | 40 | if SCAppLayerParserConfParserEnabled(ip_proto_str.as_ptr(), parser.name) != 0 { |
1743 | 40 | let _ = AppLayerRegisterParser(&parser, alproto); |
1744 | 40 | } else { |
1745 | 0 | SCLogWarning!("DOH2 is not meant to be detection-only."); |
1746 | | } |
1747 | 40 | SCAppLayerParserRegisterLogger(IPPROTO_TCP, ALPROTO_DOH2); |
1748 | | SCLogDebug!("Rust doh2 parser registered."); |
1749 | | } else { |
1750 | 0 | SCLogNotice!("Protocol detector and parser disabled for DOH2."); |
1751 | | } |
1752 | 40 | } |