]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
detect/dns: support string for dns.rcode
authorPhilippe Antoine <pantoine@oisf.net>
Sun, 26 Jan 2025 14:35:24 +0000 (15:35 +0100)
committerVictor Julien <victor@inliniac.net>
Sat, 19 Apr 2025 16:20:02 +0000 (18:20 +0200)
Ticket: 6723

doc/userguide/rules/dns-keywords.rst
rust/src/dns/detect.rs
rust/src/dns/dns.rs
rust/src/dns/log.rs

index f7dcc194101da70aeaf8c989d906c63f72088428..e34351774212d94862f66d8d596565de4d5a9fa6 100644 (file)
@@ -47,6 +47,7 @@ dns.rcode
 This keyword matches on the **rcode** field found in the DNS header flags.
 
 dns.rcode uses an :ref:`unsigned 8-bit integer <rules-integer-keywords>`.
+It can also be specified by text from the enumeration.
 
 Currently, Suricata only supports rcode values in the range [0-15], while
 the current DNS version supports rcode values from [0-23] as specified in
index 0c07296f4eb7bee5168121d88184b9699ca96c4a..bfbc9b95df90afca83d33249da6e363049ce2246 100644 (file)
  * 02110-1301, USA.
  */
 
-use super::dns::{DNSTransaction, ALPROTO_DNS};
+use super::dns::{DNSRcode, DNSTransaction, ALPROTO_DNS};
 use crate::detect::uint::{
-    detect_match_uint, DetectUintData, SCDetectU16Free, SCDetectU16Parse, SCDetectU8Free,
-    SCDetectU8Parse,
+    detect_match_uint, detect_parse_uint_enum, SCDetectU16Free, SCDetectU16Parse,
+    SCDetectU8Free, SCDetectU8Parse, DetectUintData,
 };
 use crate::detect::{
     DetectBufferSetActiveList, DetectHelperBufferRegister, DetectHelperGetMultiData,
@@ -27,6 +27,7 @@ use crate::detect::{
     SigMatchAppendSMToList, SIGMATCH_INFO_STICKY_BUFFER, SIGMATCH_NOOPT,
 };
 use crate::direction::Direction;
+use std::ffi::CStr;
 use std::os::raw::{c_int, c_void};
 
 /// Perform the DNS opcode match.
@@ -70,7 +71,7 @@ unsafe extern "C" fn dns_rcode_match(
     _sig: *const c_void, ctx: *const c_void,
 ) -> c_int {
     let tx = cast_pointer!(tx, DNSTransaction);
-    let ctx = cast_pointer!(ctx, DetectUintData<u8>);
+    let ctx = cast_pointer!(ctx, DetectUintData<u16>);
     let header_flags = if flags & Direction::ToServer as u8 != 0 {
         if let Some(request) = &tx.request {
             request.header.flags
@@ -83,7 +84,7 @@ unsafe extern "C" fn dns_rcode_match(
         return 0;
     };
 
-    let rcode = (header_flags & 0xf) as u8;
+    let rcode = header_flags & 0xf;
 
     if detect_match_uint(ctx, rcode) {
         return 1;
@@ -153,13 +154,24 @@ unsafe extern "C" fn dns_opcode_free(_de: *mut c_void, ctx: *mut c_void) {
     SCDetectU8Free(ctx);
 }
 
+unsafe extern "C" fn dns_rcode_parse(ustr: *const std::os::raw::c_char) -> *mut DetectUintData<u8> {
+    let ft_name: &CStr = CStr::from_ptr(ustr); //unsafe
+    if let Ok(s) = ft_name.to_str() {
+        if let Some(ctx) = detect_parse_uint_enum::<u16, DNSRcode>(s) {
+            let boxed = Box::new(ctx);
+            return Box::into_raw(boxed) as *mut _;
+        }
+    }
+    return std::ptr::null_mut();
+}
+
 unsafe extern "C" fn dns_rcode_setup(
     de: *mut c_void, s: *mut c_void, raw: *const libc::c_char,
 ) -> c_int {
     if DetectSignatureSetAppProto(s, ALPROTO_DNS) != 0 {
         return -1;
     }
-    let ctx = SCDetectU8Parse(raw) as *mut c_void;
+    let ctx = dns_rcode_parse(raw) as *mut c_void;
     if ctx.is_null() {
         return -1;
     }
@@ -172,8 +184,8 @@ unsafe extern "C" fn dns_rcode_setup(
 
 unsafe extern "C" fn dns_rcode_free(_de: *mut c_void, ctx: *mut c_void) {
     // Just unbox...
-    let ctx = cast_pointer!(ctx, DetectUintData<u8>);
-    SCDetectU8Free(ctx);
+    let ctx = cast_pointer!(ctx, DetectUintData<u16>);
+    SCDetectU16Free(ctx);
 }
 
 unsafe extern "C" fn dns_rrtype_setup(
@@ -535,7 +547,7 @@ mod test {
     #[test]
     fn parse_rcode_good() {
         assert_eq!(
-            detect_parse_uint::<u8>("1").unwrap().1,
+            detect_parse_uint_enum::<u16, DNSRcode>("1").unwrap(),
             DetectUintData {
                 mode: DetectUintMode::DetectUintModeEqual,
                 arg1: 1,
@@ -543,7 +555,7 @@ mod test {
             }
         );
         assert_eq!(
-            detect_parse_uint::<u8>("123").unwrap().1,
+            detect_parse_uint_enum::<u16, DNSRcode>("123").unwrap(),
             DetectUintData {
                 mode: DetectUintMode::DetectUintModeEqual,
                 arg1: 123,
@@ -551,7 +563,7 @@ mod test {
             }
         );
         assert_eq!(
-            detect_parse_uint::<u8>("!123").unwrap().1,
+            detect_parse_uint_enum::<u16, DNSRcode>("!123").unwrap(),
             DetectUintData {
                 mode: DetectUintMode::DetectUintModeNe,
                 arg1: 123,
@@ -559,17 +571,25 @@ mod test {
             }
         );
         assert_eq!(
-            detect_parse_uint::<u8>("7-15").unwrap().1,
+            detect_parse_uint_enum::<u16, DNSRcode>("7-15").unwrap(),
             DetectUintData {
                 mode: DetectUintMode::DetectUintModeRange,
                 arg1: 7,
                 arg2: 15,
             }
         );
-        assert!(detect_parse_uint::<u16>("").is_err());
-        assert!(detect_parse_uint::<u16>("!").is_err());
-        assert!(detect_parse_uint::<u16>("!   ").is_err());
-        assert!(detect_parse_uint::<u16>("!asdf").is_err());
+        assert_eq!(
+            detect_parse_uint_enum::<u16, DNSRcode>("nxdomain").unwrap(),
+            DetectUintData {
+                mode: DetectUintMode::DetectUintModeEqual,
+                arg1: DNSRcode::NXDOMAIN as u16,
+                arg2: 0,
+            }
+        );
+        assert!(detect_parse_uint_enum::<u16, DNSRcode>("").is_none());
+        assert!(detect_parse_uint_enum::<u16, DNSRcode>("!").is_none());
+        assert!(detect_parse_uint_enum::<u16, DNSRcode>("!   ").is_none());
+        assert!(detect_parse_uint_enum::<u16, DNSRcode>("!asdf").is_none());
     }
 
     #[test]
index 05603bd2a9a45041461f9ceb92bea0fad822ca6e..ab10bd15a73422ed5992dd5507dbe5ad51ec1c9c 100644 (file)
@@ -94,27 +94,30 @@ pub const DNS_RECORD_TYPE_ANY: u16 = 255;
 pub const DNS_RECORD_TYPE_URI: u16 = 256;
 
 /// DNS error codes.
-pub const DNS_RCODE_NOERROR: u16 = 0;
-pub const DNS_RCODE_FORMERR: u16 = 1;
-pub const DNS_RCODE_SERVFAIL: u16 = 2;
-pub const DNS_RCODE_NXDOMAIN: u16 = 3;
-pub const DNS_RCODE_NOTIMP: u16 = 4;
-pub const DNS_RCODE_REFUSED: u16 = 5;
-pub const DNS_RCODE_YXDOMAIN: u16 = 6;
-pub const DNS_RCODE_YXRRSET: u16 = 7;
-pub const DNS_RCODE_NXRRSET: u16 = 8;
-pub const DNS_RCODE_NOTAUTH: u16 = 9;
-pub const DNS_RCODE_NOTZONE: u16 = 10;
-// Support for OPT RR from RFC6891 will be needed to
-// parse RCODE values over 15
-pub const DNS_RCODE_BADVERS: u16 = 16;
-//also pub const DNS_RCODE_BADSIG: u16 = 16;
-pub const DNS_RCODE_BADKEY: u16 = 17;
-pub const DNS_RCODE_BADTIME: u16 = 18;
-pub const DNS_RCODE_BADMODE: u16 = 19;
-pub const DNS_RCODE_BADNAME: u16 = 20;
-pub const DNS_RCODE_BADALG: u16 = 21;
-pub const DNS_RCODE_BADTRUNC: u16 = 22;
+#[derive(Clone, Debug, EnumStringU16)]
+pub enum DNSRcode {
+    NOERROR = 0,
+    FORMERR = 1,
+    SERVFAIL = 2,
+    NXDOMAIN = 3,
+    NOTIMP = 4,
+    REFUSED = 5,
+    YXDOMAIN = 6,
+    YXRRSET = 7,
+    NXRRSET = 8,
+    NOTAUTH = 9,
+    NOTZONE = 10,
+    // Support for OPT RR from RFC6891 will be needed to
+    // parse RCODE values over 15
+    BADVERS = 16,
+    //also pub const DNS_RCODE_BADSIG: u16 = 16;
+    BADKEY = 17,
+    BADTIME = 18,
+    BADMODE = 19,
+    BADNAME = 20,
+    BADALG = 21,
+    BADTRUNC = 22,
+}
 
 pub(super) static mut ALPROTO_DNS: AppProto = ALPROTO_UNKNOWN;
 
index 7003e70c1949332dbd42a880e46cae6083d8ff53..6647ac88e6e3a5cfb9b63a4e03e7a1c077041a83 100644 (file)
@@ -19,6 +19,7 @@ use std;
 use std::collections::HashMap;
 use std::string::String;
 
+use crate::detect::EnumString;
 use crate::dns::dns::*;
 use crate::jsonbuilder::{JsonBuilder, JsonError};
 
@@ -346,30 +347,13 @@ pub fn dns_rrtype_string(rrtype: u16) -> String {
 }
 
 pub fn dns_rcode_string(flags: u16) -> String {
-    match flags & 0x000f {
-        DNS_RCODE_NOERROR => "NOERROR",
-        DNS_RCODE_FORMERR => "FORMERR",
-        DNS_RCODE_SERVFAIL => "SERVFAIL",
-        DNS_RCODE_NXDOMAIN => "NXDOMAIN",
-        DNS_RCODE_NOTIMP => "NOTIMP",
-        DNS_RCODE_REFUSED => "REFUSED",
-        DNS_RCODE_YXDOMAIN => "YXDOMAIN",
-        DNS_RCODE_YXRRSET => "YXRRSET",
-        DNS_RCODE_NXRRSET => "NXRRSET",
-        DNS_RCODE_NOTAUTH => "NOTAUTH",
-        DNS_RCODE_NOTZONE => "NOTZONE",
-        DNS_RCODE_BADVERS => "BADVERS/BADSIG",
-        DNS_RCODE_BADKEY => "BADKEY",
-        DNS_RCODE_BADTIME => "BADTIME",
-        DNS_RCODE_BADMODE => "BADMODE",
-        DNS_RCODE_BADNAME => "BADNAME",
-        DNS_RCODE_BADALG => "BADALG",
-        DNS_RCODE_BADTRUNC => "BADTRUNC",
-        _ => {
-            return (flags & 0x000f).to_string();
-        }
+    if flags & 0x000f == DNSRcode::BADVERS as u16 {
+        return "BADVERS/BADSIG".to_string();
     }
-    .to_string()
+    if let Some(rc) = DNSRcode::from_u(flags & 0x000f) {
+        return rc.to_str().to_uppercase();
+    }
+    return (flags & 0x000f).to_string();
 }
 
 /// Format bytes as an IP address string.