dns.opcode:!7-15;
+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>`.
+
+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
+`RFC 6895 <https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-6>`_.
+
+We plan to extend the rcode values supported by Suricata according to RFC 6895
+as tracked by the ticket: https://redmine.openinfosecfoundation.org/issues/6650
+
+Syntax
+~~~~~~
+
+::
+
+ dns.rcode:[!]<number>
+ dns.rcode:[!]<number1>-<number2>
+
+Examples
+~~~~~~~~
+
+Match on DNS requests and responses with **rcode** 4::
+
+ dns.rcode:4;
+
+Match on DNS requests and responses where the **rcode** is NOT 0::
+
+ dns.rcode:!0;
+
dns.rrtype
----------
return 0;
}
+/// Perform the DNS rcode match.
+///
+/// 1 will be returned on match, otherwise 0 will be returned.
+#[no_mangle]
+pub extern "C" fn rs_dns_rcode_match(
+ 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 {
+ request.header.flags
+ } else {
+ return 0;
+ }
+ } else if let Some(response) = &tx.response {
+ response.header.flags
+ } else {
+ return 0;
+ };
+
+ let rcode = (header_flags & 0xf) as u8;
+
+ if detect_match_uint(detect, rcode) {
+ return 1;
+ }
+ return 0;
+}
+
/// Perform the DNS rrtype match.
/// 1 will be returned on match, otherwise 0 will be returned.
#[no_mangle]
));
}
+ #[test]
+ fn parse_rcode_good() {
+ assert_eq!(
+ detect_parse_uint::<u8>("1").unwrap().1,
+ DetectUintData {
+ mode: DetectUintMode::DetectUintModeEqual,
+ arg1: 1,
+ arg2: 0,
+ }
+ );
+ assert_eq!(
+ detect_parse_uint::<u8>("123").unwrap().1,
+ DetectUintData {
+ mode: DetectUintMode::DetectUintModeEqual,
+ arg1: 123,
+ arg2: 0,
+ }
+ );
+ assert_eq!(
+ detect_parse_uint::<u8>("!123").unwrap().1,
+ DetectUintData {
+ mode: DetectUintMode::DetectUintModeNe,
+ arg1: 123,
+ arg2: 0,
+ }
+ );
+ assert_eq!(
+ detect_parse_uint::<u8>("7-15").unwrap().1,
+ 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());
+ }
+
+ #[test]
+ fn test_match_rcode() {
+ assert!(detect_match_uint(
+ &DetectUintData {
+ mode: DetectUintMode::DetectUintModeEqual,
+ arg1: 0,
+ arg2: 0,
+ },
+ 0b0000_0000_0000_0000,
+ ));
+
+ assert!(!detect_match_uint(
+ &DetectUintData {
+ mode: DetectUintMode::DetectUintModeNe,
+ arg1: 0,
+ arg2: 0,
+ },
+ 0b0000_0000_0000_0000,
+ ));
+
+ assert!(detect_match_uint(
+ &DetectUintData {
+ mode: DetectUintMode::DetectUintModeEqual,
+ arg1: 4,
+ arg2: 0,
+ },
+ 4u8,
+ ));
+
+ assert!(!detect_match_uint(
+ &DetectUintData {
+ mode: DetectUintMode::DetectUintModeNe,
+ arg1: 4,
+ arg2: 0,
+ },
+ 4u8,
+ ));
+ }
+
#[test]
fn parse_rrtype_good() {
assert_eq!(
detect-dnp3.h \
detect-dns-answer-name.h \
detect-dns-opcode.h \
+ detect-dns-rcode.h \
detect-dns-rrtype.h \
detect-dns-query.h \
detect-dns-query-name.h \
detect-dnp3.c \
detect-dns-answer-name.c \
detect-dns-opcode.c \
+ detect-dns-rcode.c \
detect-dns-rrtype.c \
detect-dns-query.c \
detect-dns-query-name.c \
--- /dev/null
+/* Copyright (C) 2024 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#include "suricata-common.h"
+
+#include "detect-parse.h"
+#include "detect-engine.h"
+#include "detect-dns-rcode.h"
+#include "rust.h"
+#include "detect-engine-uint.h"
+
+static int dns_rcode_list_id = 0;
+
+static void DetectDnsRcodeFree(DetectEngineCtx *, void *ptr);
+
+static int DetectDnsRcodeSetup(DetectEngineCtx *de_ctx, Signature *s, const char *str)
+{
+ SCEnter();
+
+ if (DetectSignatureSetAppProto(s, ALPROTO_DNS) != 0) {
+ SCReturnInt(-1);
+ }
+
+ void *detect = DetectU8Parse(str);
+ if (detect == NULL) {
+ SCLogError("failed to parse dns.rcode: %s", str);
+ SCReturnInt(-1);
+ }
+
+ if (SigMatchAppendSMToList(
+ de_ctx, s, DETECT_AL_DNS_RCODE, (SigMatchCtx *)detect, dns_rcode_list_id) == NULL) {
+ DetectDnsRcodeFree(de_ctx, detect);
+ SCReturnInt(-1);
+ }
+
+ SCReturnInt(0);
+}
+
+static void DetectDnsRcodeFree(DetectEngineCtx *de_ctx, void *ptr)
+{
+ SCEnter();
+ if (ptr != NULL) {
+ rs_detect_u8_free(ptr);
+ }
+ SCReturn;
+}
+
+static int DetectDnsRcodeMatch(DetectEngineThreadCtx *det_ctx, Flow *f, uint8_t flags, void *state,
+ void *txv, const Signature *s, const SigMatchCtx *ctx)
+{
+ return rs_dns_rcode_match(txv, (void *)ctx, flags);
+}
+
+void DetectDnsRcodeRegister(void)
+{
+ sigmatch_table[DETECT_AL_DNS_RCODE].name = "dns.rcode";
+ sigmatch_table[DETECT_AL_DNS_RCODE].desc = "Match the DNS header rcode flag.";
+ sigmatch_table[DETECT_AL_DNS_RCODE].url = "/rules/dns-keywords.html#dns-rcode";
+ sigmatch_table[DETECT_AL_DNS_RCODE].Setup = DetectDnsRcodeSetup;
+ sigmatch_table[DETECT_AL_DNS_RCODE].Free = DetectDnsRcodeFree;
+ sigmatch_table[DETECT_AL_DNS_RCODE].Match = NULL;
+ sigmatch_table[DETECT_AL_DNS_RCODE].AppLayerTxMatch = DetectDnsRcodeMatch;
+
+ DetectAppLayerInspectEngineRegister(
+ "dns.rcode", ALPROTO_DNS, SIG_FLAG_TOSERVER, 0, DetectEngineInspectGenericList, NULL);
+
+ DetectAppLayerInspectEngineRegister(
+ "dns.rcode", ALPROTO_DNS, SIG_FLAG_TOCLIENT, 0, DetectEngineInspectGenericList, NULL);
+
+ dns_rcode_list_id = DetectBufferTypeGetByName("dns.rcode");
+}
\ No newline at end of file
--- /dev/null
+/* Copyright (C) 2024 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#ifndef __DETECT_DNS_RCODE_H__
+#define __DETECT_DNS_RCODE_H__
+
+void DetectDnsRcodeRegister(void);
+
+#endif /* __DETECT_DNS_RCODE_H__ */
\ No newline at end of file
#include "detect-engine-payload.h"
#include "detect-engine-dcepayload.h"
#include "detect-dns-opcode.h"
+#include "detect-dns-rcode.h"
#include "detect-dns-rrtype.h"
#include "detect-dns-query.h"
#include "detect-dns-answer-name.h"
DetectDnsQueryRegister();
DetectDnsOpcodeRegister();
+ DetectDnsRcodeRegister();
DetectDnsRrtypeRegister();
DetectDnsAnswerNameRegister();
DetectDnsQueryNameRegister();
DETECT_AL_DNS_QUERY,
DETECT_AL_DNS_OPCODE,
+ DETECT_AL_DNS_RCODE,
DETECT_AL_DNS_RRTYPE,
DETECT_AL_DNS_ANSWER_NAME,
DETECT_AL_DNS_QUERY_NAME,