From abc71dc4a5744313f4c404215ccb480377adf70d Mon Sep 17 00:00:00 2001 From: Jason Ish Date: Tue, 14 Jul 2020 00:02:59 -0600 Subject: [PATCH] applayer template (rust): better gap handling example In the request parser, show checking if a gap was received and what one example of trying to continue might look like. --- rust/src/applayertemplate/template.rs | 51 +++++++++++++++++++++------ 1 file changed, 40 insertions(+), 11 deletions(-) diff --git a/rust/src/applayertemplate/template.rs b/rust/src/applayertemplate/template.rs index d38f8b34ff..a9b9f0ecb5 100644 --- a/rust/src/applayertemplate/template.rs +++ b/rust/src/applayertemplate/template.rs @@ -67,6 +67,8 @@ impl Drop for TemplateTransaction { pub struct TemplateState { tx_id: u64, transactions: Vec, + request_gap: bool, + response_gap: bool, } impl TemplateState { @@ -74,6 +76,8 @@ impl TemplateState { Self { tx_id: 0, transactions: Vec::new(), + request_gap: false, + response_gap: false, } } @@ -126,6 +130,19 @@ impl TemplateState { return AppLayerResult::ok(); } + // If there was gap, check we can sync up again. + if self.request_gap { + if probe(input).is_err() { + // The parser now needs to decide what to do as we are not in sync. + // For this template, we'll just try again next time. + return AppLayerResult::ok(); + } + + // It looks like we're in sync with a message header, clear gap + // state and keep parsing. + self.request_gap = false; + } + let mut start = input; while start.len() > 0 { match parser::parse_message(start) { @@ -214,23 +231,27 @@ impl TemplateState { } fn on_request_gap(&mut self, _size: u32) { + self.request_gap = true; } fn on_response_gap(&mut self, _size: u32) { + self.response_gap = true; } } -/// Probe to see if this input looks like a request or response. +/// Probe for a valid header. /// -/// For the purposes of this template things will be kept simple. The -/// protocol is text based with the leading text being the length of -/// the message in bytes. So simply make sure the first character is -/// between "1" and "9". -fn probe(input: &[u8]) -> bool { - if input.len() > 1 && input[0] >= 49 && input[0] <= 57 { - return true; - } - return false; +/// As this template protocol uses messages prefixed with the size +/// as a string followed by a ':', we look at up to the first 10 +/// characters for that pattern. +fn probe(input: &[u8]) -> nom::IResult<&[u8], ()> { + let size = std::cmp::min(10, input.len()); + let (rem, prefix) = nom::bytes::complete::take(size)(input)?; + nom::sequence::terminated( + nom::bytes::complete::take_while1(nom::character::is_digit), + nom::bytes::complete::tag(":"), + )(prefix)?; + Ok((rem, ())) } // C exports. @@ -256,7 +277,7 @@ pub extern "C" fn rs_template_probing_parser( // Need at least 2 bytes. if input_len > 1 && input != std::ptr::null_mut() { let slice = build_slice!(input, input_len as usize); - if probe(slice) { + if probe(slice).is_ok() { return unsafe { ALPROTO_TEMPLATE }; } } @@ -557,6 +578,14 @@ pub unsafe extern "C" fn rs_template_register_parser() { mod test { use super::*; + #[test] + fn test_probe() { + assert!(probe(b"1").is_err()); + assert!(probe(b"1:").is_ok()); + assert!(probe(b"123456789:").is_ok()); + assert!(probe(b"0123456789:").is_err()); + } + #[test] fn test_incomplete() { let mut state = TemplateState::new(); -- 2.47.2