use super::dns::DNSTransaction;
use crate::core;
use std::ffi::CStr;
use std::os::raw::{c_char, c_void};
#[derive(Debug, PartialEq)]
pub struct DetectDnsOpcode {
negate: bool,
opcode: u8,
}
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(())
}
#[no_mangle]
pub extern "C" fn rs_dns_opcode_match(
tx: &mut DNSTransaction,
detect: &mut DetectDnsOpcode,
flags: u8,
) -> u8 {
let header_flags = if flags & core::STREAM_TOSERVER != 0 {
if let Some(request) = &tx.request {
request.header.flags
} else {
return 0;
}
} else if flags & core::STREAM_TOCLIENT != 0 {
if let Some(response) = &tx.response {
response.header.flags
} else {
return 0;
}
} else {
return 0;
};
if match_opcode(detect, header_flags) {
1
} else {
0
}
}
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 != std::ptr::null_mut() {
std::mem::drop(Box::from_raw(ptr as *mut DetectDnsOpcode));
}
}
#[cfg(test)]
mod test {
use super::*;
#[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
})
);
assert_eq!(
parse_opcode("!123"),
Ok(DetectDnsOpcode {
negate: true,
opcode: 123
})
);
assert_eq!(
parse_opcode("!123"),
Ok(DetectDnsOpcode {
negate: true,
opcode: 123
})
);
assert_eq!(parse_opcode(""), Err(()));
assert_eq!(parse_opcode("!"), Err(()));
assert_eq!(parse_opcode("! "), Err(()));
assert_eq!(parse_opcode("!asdf"), Err(()));
}
#[test]
fn test_match_opcode() {
assert_eq!(
match_opcode(
&DetectDnsOpcode {
negate: false,
opcode: 0,
},
0b0000_0000_0000_0000,
),
true
);
assert_eq!(
match_opcode(
&DetectDnsOpcode {
negate: true,
opcode: 0,
},
0b0000_0000_0000_0000,
),
false
);
assert_eq!(
match_opcode(
&DetectDnsOpcode {
negate: false,
opcode: 4,
},
0b0010_0000_0000_0000,
),
true
);
assert_eq!(
match_opcode(
&DetectDnsOpcode {
negate: true,
opcode: 4,
},
0b0010_0000_0000_0000,
),
false
);
}
}