|
|
|
@ -16,49 +16,15 @@
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
use super::dns::DNSTransaction;
|
|
|
|
|
use crate::core::*;
|
|
|
|
|
use std::ffi::CStr;
|
|
|
|
|
use std::os::raw::{c_char, c_void};
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, PartialEq, Eq)]
|
|
|
|
|
pub struct DetectDnsOpcode {
|
|
|
|
|
negate: bool,
|
|
|
|
|
opcode: u8,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Parse a DNS opcode argument returning the code and if it is to be
|
|
|
|
|
/// negated or not.
|
|
|
|
|
///
|
|
|
|
|
/// For now only an indication that an error occurred is returned, not
|
|
|
|
|
/// the details of the error.
|
|
|
|
|
fn parse_opcode(opcode: &str) -> Result<DetectDnsOpcode, ()> {
|
|
|
|
|
let mut negated = false;
|
|
|
|
|
for (i, c) in opcode.chars().enumerate() {
|
|
|
|
|
match c {
|
|
|
|
|
' ' | '\t' => {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
'!' => {
|
|
|
|
|
negated = true;
|
|
|
|
|
}
|
|
|
|
|
_ => {
|
|
|
|
|
let code: u8 = opcode[i..].parse().map_err(|_| ())?;
|
|
|
|
|
return Ok(DetectDnsOpcode {
|
|
|
|
|
negate: negated,
|
|
|
|
|
opcode: code,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Err(())
|
|
|
|
|
}
|
|
|
|
|
use crate::core::Direction;
|
|
|
|
|
use crate::detect::uint::{detect_match_uint, DetectUintData};
|
|
|
|
|
|
|
|
|
|
/// Perform the DNS opcode match.
|
|
|
|
|
///
|
|
|
|
|
/// 1 will be returned on match, otherwise 0 will be returned.
|
|
|
|
|
#[no_mangle]
|
|
|
|
|
pub extern "C" fn rs_dns_opcode_match(
|
|
|
|
|
tx: &mut DNSTransaction, detect: &mut DetectDnsOpcode, flags: u8,
|
|
|
|
|
tx: &mut DNSTransaction, detect: &mut DetectUintData<u8>, flags: u8,
|
|
|
|
|
) -> u8 {
|
|
|
|
|
let header_flags = if flags & Direction::ToServer as u8 != 0 {
|
|
|
|
|
if let Some(request) = &tx.request {
|
|
|
|
@ -76,116 +42,87 @@ pub extern "C" fn rs_dns_opcode_match(
|
|
|
|
|
// Not to server or to client??
|
|
|
|
|
return 0;
|
|
|
|
|
};
|
|
|
|
|
let opcode = ((header_flags >> 11) & 0xf) as u8;
|
|
|
|
|
|
|
|
|
|
match_opcode(detect, header_flags).into()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn match_opcode(detect: &DetectDnsOpcode, flags: u16) -> bool {
|
|
|
|
|
let opcode = ((flags >> 11) & 0xf) as u8;
|
|
|
|
|
if detect.negate {
|
|
|
|
|
detect.opcode != opcode
|
|
|
|
|
} else {
|
|
|
|
|
detect.opcode == opcode
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[no_mangle]
|
|
|
|
|
pub unsafe extern "C" fn rs_detect_dns_opcode_parse(carg: *const c_char) -> *mut c_void {
|
|
|
|
|
if carg.is_null() {
|
|
|
|
|
return std::ptr::null_mut();
|
|
|
|
|
}
|
|
|
|
|
let arg = match CStr::from_ptr(carg).to_str() {
|
|
|
|
|
Ok(arg) => arg,
|
|
|
|
|
_ => {
|
|
|
|
|
return std::ptr::null_mut();
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
match parse_opcode(arg) {
|
|
|
|
|
Ok(detect) => Box::into_raw(Box::new(detect)) as *mut _,
|
|
|
|
|
Err(_) => std::ptr::null_mut(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[no_mangle]
|
|
|
|
|
pub unsafe extern "C" fn rs_dns_detect_opcode_free(ptr: *mut c_void) {
|
|
|
|
|
if !ptr.is_null() {
|
|
|
|
|
std::mem::drop(Box::from_raw(ptr as *mut DetectDnsOpcode));
|
|
|
|
|
if detect_match_uint(detect, opcode) {
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
mod test {
|
|
|
|
|
use super::*;
|
|
|
|
|
use crate::detect::uint::{detect_parse_uint, DetectUintMode};
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn parse_opcode_good() {
|
|
|
|
|
assert_eq!(
|
|
|
|
|
parse_opcode("1"),
|
|
|
|
|
Ok(DetectDnsOpcode {
|
|
|
|
|
negate: false,
|
|
|
|
|
opcode: 1
|
|
|
|
|
})
|
|
|
|
|
);
|
|
|
|
|
assert_eq!(
|
|
|
|
|
parse_opcode("123"),
|
|
|
|
|
Ok(DetectDnsOpcode {
|
|
|
|
|
negate: false,
|
|
|
|
|
opcode: 123
|
|
|
|
|
})
|
|
|
|
|
detect_parse_uint::<u8>("1").unwrap().1,
|
|
|
|
|
DetectUintData {
|
|
|
|
|
mode: DetectUintMode::DetectUintModeEqual,
|
|
|
|
|
arg1: 1,
|
|
|
|
|
arg2: 0,
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
assert_eq!(
|
|
|
|
|
parse_opcode("!123"),
|
|
|
|
|
Ok(DetectDnsOpcode {
|
|
|
|
|
negate: true,
|
|
|
|
|
opcode: 123
|
|
|
|
|
})
|
|
|
|
|
detect_parse_uint::<u8>("123").unwrap().1,
|
|
|
|
|
DetectUintData {
|
|
|
|
|
mode: DetectUintMode::DetectUintModeEqual,
|
|
|
|
|
arg1: 123,
|
|
|
|
|
arg2: 0,
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
assert_eq!(
|
|
|
|
|
parse_opcode("!123"),
|
|
|
|
|
Ok(DetectDnsOpcode {
|
|
|
|
|
negate: true,
|
|
|
|
|
opcode: 123
|
|
|
|
|
})
|
|
|
|
|
detect_parse_uint::<u8>("!123").unwrap().1,
|
|
|
|
|
DetectUintData {
|
|
|
|
|
mode: DetectUintMode::DetectUintModeNe,
|
|
|
|
|
arg1: 123,
|
|
|
|
|
arg2: 0,
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
assert_eq!(parse_opcode(""), Err(()));
|
|
|
|
|
assert_eq!(parse_opcode("!"), Err(()));
|
|
|
|
|
assert_eq!(parse_opcode("! "), Err(()));
|
|
|
|
|
assert_eq!(parse_opcode("!asdf"), Err(()));
|
|
|
|
|
assert!(detect_parse_uint::<u8>("").is_err());
|
|
|
|
|
assert!(detect_parse_uint::<u8>("!").is_err());
|
|
|
|
|
assert!(detect_parse_uint::<u8>("! ").is_err());
|
|
|
|
|
assert!(detect_parse_uint::<u8>("!asdf").is_err());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_match_opcode() {
|
|
|
|
|
assert!(match_opcode(
|
|
|
|
|
&DetectDnsOpcode {
|
|
|
|
|
negate: false,
|
|
|
|
|
opcode: 0,
|
|
|
|
|
assert!(detect_match_uint(
|
|
|
|
|
&DetectUintData {
|
|
|
|
|
mode: DetectUintMode::DetectUintModeEqual,
|
|
|
|
|
arg1: 0,
|
|
|
|
|
arg2: 0,
|
|
|
|
|
},
|
|
|
|
|
0b0000_0000_0000_0000,
|
|
|
|
|
));
|
|
|
|
|
|
|
|
|
|
assert!(!match_opcode(
|
|
|
|
|
&DetectDnsOpcode {
|
|
|
|
|
negate: true,
|
|
|
|
|
opcode: 0,
|
|
|
|
|
assert!(!detect_match_uint(
|
|
|
|
|
&DetectUintData {
|
|
|
|
|
mode: DetectUintMode::DetectUintModeNe,
|
|
|
|
|
arg1: 0,
|
|
|
|
|
arg2: 0,
|
|
|
|
|
},
|
|
|
|
|
0b0000_0000_0000_0000,
|
|
|
|
|
));
|
|
|
|
|
|
|
|
|
|
assert!(match_opcode(
|
|
|
|
|
&DetectDnsOpcode {
|
|
|
|
|
negate: false,
|
|
|
|
|
opcode: 4,
|
|
|
|
|
assert!(detect_match_uint(
|
|
|
|
|
&DetectUintData {
|
|
|
|
|
mode: DetectUintMode::DetectUintModeEqual,
|
|
|
|
|
arg1: 4,
|
|
|
|
|
arg2: 0,
|
|
|
|
|
},
|
|
|
|
|
0b0010_0000_0000_0000,
|
|
|
|
|
((0b0010_0000_0000_0000 >> 11) & 0xf) as u8,
|
|
|
|
|
));
|
|
|
|
|
|
|
|
|
|
assert!(!match_opcode(
|
|
|
|
|
&DetectDnsOpcode {
|
|
|
|
|
negate: true,
|
|
|
|
|
opcode: 4,
|
|
|
|
|
assert!(!detect_match_uint(
|
|
|
|
|
&DetectUintData {
|
|
|
|
|
mode: DetectUintMode::DetectUintModeNe,
|
|
|
|
|
arg1: 4,
|
|
|
|
|
arg2: 0,
|
|
|
|
|
},
|
|
|
|
|
0b0010_0000_0000_0000,
|
|
|
|
|
((0b0010_0000_0000_0000 >> 11) & 0xf) as u8,
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|