detect/integers: nb index to match a specific number of times

For example
dns.rrtype: !A,nb>3
will match if we have more than 3 dns records which are not A
pull/13878/head
Philippe Antoine 2 months ago committed by Victor Julien
parent 6f848eeaaf
commit 1480cf47ab

@ -109,10 +109,17 @@ They expand the syntax of a single integer::
any Match with any index any Match with any index
all Match only if all indexes match all Match only if all indexes match
all1 Match only if all and at least one indexes match all1 Match only if all and at least one indexes match
nb Matches a number of times
0>= Match specific index 0>= Match specific index
0< Match specific index with back to front indexing 0< Match specific index with back to front indexing
========= ================================================ ========= ================================================
The index ``all`` will match if there is no value. The index ``all`` will match if there is no value.
The index ``all1`` will not match if there is no value and behaves The index ``all1`` will not match if there is no value and behaves
like ``all`` if there is at least one value. like ``all`` if there is at least one value.
These keywords will wait for transaction completion to run, to
be sure to have the final number of elements.
The index ``nb`` accepts all comparison modes as integer keywords.
For example ``nb>3`` will match only if more than 3 integers in the
array match the value.

@ -17,7 +17,7 @@
use nom7::branch::alt; use nom7::branch::alt;
use nom7::bytes::complete::{is_a, tag, tag_no_case, take_while}; use nom7::bytes::complete::{is_a, tag, tag_no_case, take_while};
use nom7::character::complete::{char, digit1, hex_digit1}; use nom7::character::complete::{char, digit1, hex_digit1, i32 as nom_i32};
use nom7::combinator::{all_consuming, map_opt, opt, value, verify}; use nom7::combinator::{all_consuming, map_opt, opt, value, verify};
use nom7::error::{make_error, Error, ErrorKind}; use nom7::error::{make_error, Error, ErrorKind};
use nom7::Err; use nom7::Err;
@ -26,7 +26,6 @@ use nom7::IResult;
use super::EnumString; use super::EnumString;
use std::ffi::{c_int, c_void, CStr}; use std::ffi::{c_int, c_void, CStr};
use std::str::FromStr;
#[derive(PartialEq, Eq, Clone, Debug)] #[derive(PartialEq, Eq, Clone, Debug)]
#[repr(u8)] #[repr(u8)]
@ -57,6 +56,7 @@ pub enum DetectUintIndex {
All, All,
All1, All1,
Index(i32), Index(i32),
NumberMatches(DetectUintData<u32>),
} }
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
@ -65,16 +65,30 @@ pub struct DetectUintArrayData<T> {
pub index: DetectUintIndex, pub index: DetectUintIndex,
} }
fn parse_uint_index_precise(s: &str) -> IResult<&str, DetectUintIndex> {
let (s, i32_index) = nom_i32(s)?;
Ok((s, DetectUintIndex::Index(i32_index)))
}
fn parse_uint_index_nb(s: &str) -> IResult<&str, DetectUintIndex> {
let (s, _) = tag("nb")(s)?;
let (s, du32) = detect_parse_uint::<u32>(s)?;
Ok((s, DetectUintIndex::NumberMatches(du32)))
}
fn parse_uint_index_val(s: &str) -> Option<DetectUintIndex> {
let (_s, arg1) = alt((parse_uint_index_precise, parse_uint_index_nb))(s).ok()?;
Some(arg1)
}
fn parse_uint_index(parts: &[&str]) -> Option<DetectUintIndex> { fn parse_uint_index(parts: &[&str]) -> Option<DetectUintIndex> {
let index = if parts.len() == 2 { let index = if parts.len() == 2 {
match parts[1] { match parts[1] {
"all" => DetectUintIndex::All, "all" => DetectUintIndex::All,
"all1" => DetectUintIndex::All1, "all1" => DetectUintIndex::All1,
"any" => DetectUintIndex::Any, "any" => DetectUintIndex::Any,
_ => { // not only a literal, but some numeric value
let i32_index = i32::from_str(parts[1]).ok()?; _ => return parse_uint_index_val(parts[1]),
DetectUintIndex::Index(i32_index)
}
} }
} else { } else {
DetectUintIndex::Any DetectUintIndex::Any
@ -111,7 +125,7 @@ pub(crate) fn detect_parse_array_uint_enum<T1: DetectIntType, T2: EnumString<T1>
pub(crate) fn detect_uint_match_at_index<T, U: DetectIntType>( pub(crate) fn detect_uint_match_at_index<T, U: DetectIntType>(
array: &[T], ctx: &DetectUintArrayData<U>, get_value: impl Fn(&T) -> Option<U>, eof: bool, array: &[T], ctx: &DetectUintArrayData<U>, get_value: impl Fn(&T) -> Option<U>, eof: bool,
) -> c_int { ) -> c_int {
match ctx.index { match &ctx.index {
DetectUintIndex::Any => { DetectUintIndex::Any => {
for response in array { for response in array {
if let Some(code) = get_value(response) { if let Some(code) = get_value(response) {
@ -122,6 +136,28 @@ pub(crate) fn detect_uint_match_at_index<T, U: DetectIntType>(
} }
return 0; return 0;
} }
DetectUintIndex::NumberMatches(du32) => {
if !eof {
match du32.mode {
DetectUintMode::DetectUintModeGt | DetectUintMode::DetectUintModeGte => {}
_ => {
return 0;
}
}
}
let mut nb = 0u32;
for response in array {
if let Some(code) = get_value(response) {
if detect_match_uint::<U>(&ctx.du, code) {
nb += 1;
}
}
}
if detect_match_uint(du32, nb) {
return 1;
}
return 0;
}
DetectUintIndex::All => { DetectUintIndex::All => {
if !eof { if !eof {
return 0; return 0;
@ -154,11 +190,11 @@ pub(crate) fn detect_uint_match_at_index<T, U: DetectIntType>(
return 0; return 0;
} }
DetectUintIndex::Index(idx) => { DetectUintIndex::Index(idx) => {
let index = if idx < 0 { let index = if *idx < 0 {
// negative values for backward indexing. // negative values for backward indexing.
((array.len() as i32) + idx) as usize ((array.len() as i32) + idx) as usize
} else { } else {
idx as usize *idx as usize
}; };
if array.len() <= index { if array.len() <= index {
return 0; return 0;

Loading…
Cancel
Save