diff --git a/doc/userguide/rules/dns-keywords.rst b/doc/userguide/rules/dns-keywords.rst index f729250f706e..f7dcc194101d 100644 --- a/doc/userguide/rules/dns-keywords.rst +++ b/doc/userguide/rules/dns-keywords.rst @@ -7,22 +7,6 @@ matching on specific fields in DNS messages. Note that sticky buffers are expected to be followed by one or more :doc:`payload-keywords`. -dns.answer.name ---------------- - -``dns.answer.name`` is a sticky buffer that is used to look at the -name field in DNS answer resource records. - -``dns.answer.name`` will look at both requests and responses, so -``flow`` is recommended to confine to a specific direction. - -The buffer being matched on contains the complete re-assembled -resource name, for example "www.suricata.io". - -``dns.answer.name`` supports :doc:`multi-buffer-matching`. - -``dns.answer.name`` was introduced in Suricata 8.0.0. - dns.opcode ---------- @@ -134,9 +118,9 @@ pkt_data is used or it reaches the end of the rule. .. note:: **dns.query** will only match on DNS request messages, to also match on DNS response message, see - `dns.query.name`_. + `dns.queries.rrname`_. -``dns.query.name`` supports :doc:`multi-buffer-matching`. +``dns.queries.rrname`` supports :doc:`multi-buffer-matching`. Normalized Buffer ~~~~~~~~~~~~~~~~~ @@ -160,19 +144,96 @@ DNS query on the wire (snippet):: mail.google.com -dns.query.name ---------------- +dns.queries.rrname +------------------ -``dns.query.name`` is a sticky buffer that is used to look at the name -field in DNS query (question) resource records. It is nearly identical -to ``dns.query`` but supports both DNS requests and responses. +``dns.queries.rrname`` is a sticky buffer that is used to look at the +name field in DNS query (question) resource records. It is nearly +identical to ``dns.query`` but supports both DNS requests and +responses. -``dns.query.name`` will look at both requests and responses, so +``dns.queries.rrname`` will look at both requests and responses, so ``flow`` is recommended to confine to a specific direction. The buffer being matched on contains the complete re-assembled resource name, for example "www.suricata.io". -``dns.query.name`` supports :doc:`multi-buffer-matching`. +``dns.queries.rrname`` supports :doc:`multi-buffer-matching`. + +``dns.queries.rrname`` was introduced in Suricata 8.0.0. + +dns.answers.rrname +------------------ + +``dns.answers.rrname`` is a sticky buffer that is used to look at the +name field in DNS answer resource records. + +``dns.answers.rrname`` will look at both requests and responses, so +``flow`` is recommended to confine to a specific direction. + +The buffer being matched on contains the complete re-assembled +resource name, for example "www.suricata.io". + +``dns.answers.rrname`` supports :doc:`multi-buffer-matching`. + +``dns.answers.rrname`` was introduced in Suricata 8.0.0. + +dns.authorities.rrname +---------------------- + +``dns.authorities.rrname`` is a sticky buffer that is used to look at the +rrname field in DNS authority resource records. + +``dns.authorities.rrname`` will look at both requests and responses, +so ``flow`` is recommended to confine to a specific direction. + +The buffer being matched on contains the complete re-assembled +resource name, for example "www.suricata.io". + +``dns.authorities.rrname`` supports :doc:`multi-buffer-matching`. + +``dns.authorities.rrname`` was introduced in Suricata 8.0.0. + +dns.additionals.rrname +---------------------- + +``dns.additionals.rrname`` is a sticky buffer that is used to look at +the rrname field in DNS additional resource records. + +``dns.additionals.rrname`` will look at both requests and responses, +so ``flow`` is recommended to confine to a specific direction. + +The buffer being matched on contains the complete re-assembled +resource name, for example "www.suricata.io". + +``dns.additionals.rrname`` supports :doc:`multi-buffer-matching`. + +``dns.additionals.rrname`` was introduced in Suricata 8.0.0. + +dns.response.rrname +------------------- + +``dns.response.rrname`` is a sticky buffer that is used to look at all name +and rdata fields of DNS response (answer) resource records that are +represented as a resource name (hostname). It supports inspecting all +DNS response sections. Example:: + + alert dns any any -> any any (msg:"Test dns.response.rrname option"; \ + dns.response.rrname; content:"google"; nocase; sid:1;) + +``rdata`` field matching supports a subset of types that contain +domain name structured data, for example: "www.suricata.io". The list +of types inspected is: + +* CNAME +* PTR +* MX +* NS +* SOA (mname data: primary name server) + +The buffer being matched on contains the complete re-assembled +resource name, for example "www.suricata.io". + +``dns.response.rrname`` supports :doc:`multi-buffer-matching`. -``dns.query.name`` was introduced in Suricata 8.0.0. +``dns.response.rrname`` was introduced in Suricata 8.0.0. diff --git a/etc/schema.json b/etc/schema.json index 3a877aabb96e..14edb956b2ee 100644 --- a/etc/schema.json +++ b/etc/schema.json @@ -116,7 +116,8 @@ "type": "integer" }, "tx_guessed": { - "description": "the signature that triggered this alert didn't tie to a transaction, so the transaction (and metadata) logged is a forced estimation and may not be the one you expect", + "description": + "the signature that triggered this alert didn't tie to a transaction, so the transaction (and metadata) logged is a forced estimation and may not be the one you expect", "type": "boolean" }, "files": { @@ -1033,7 +1034,12 @@ "type": "boolean" }, "rcode": { - "type": "string" + "type": "string", + "suricata": { + "keywords": [ + "dns.rcode" + ] + } }, "rd": { "type": "boolean" @@ -1052,7 +1058,10 @@ }, "version": { "description": "The version of this EVE DNS event", - "type": "integer" + "type": "integer", + "suricata": { + "keywords": false + } }, "opcode": { "description": "DNS opcode as an integer", @@ -1069,10 +1078,21 @@ "type": "object", "properties": { "rdata": { - "type": "string" + "type": "string", + "suricata": { + "keywords": [ + "dns.response.rrname" + ] + } }, "rrname": { - "type": "string" + "type": "string", + "suricata": { + "keywords": [ + "dns.answers.rrname", + "dns.response.rrname" + ] + } }, "rrtype": { "type": "string" @@ -1173,10 +1193,21 @@ "type": "integer" }, "rrname": { - "type": "string" + "type": "string", + "suricata": { + "keywords": [ + "dns.queries.rrname", + "dns.query" + ] + } }, "rrtype": { - "type": "string" + "type": "string", + "suricata": { + "keywords": [ + "dns.rrtype" + ] + } }, "tx_id": { "type": "integer" @@ -1189,10 +1220,16 @@ }, "opcode": { "description": "DNS opcode as an integer", - "type": "integer" + "type": "integer", + "suricata": { + "keywords": [ + "dns.opcode" + ] + } }, "rrname_truncated": { - "description": "Set to true if the rrname was too long and truncated by Suricata", + "description": + "Set to true if the rrname was too long and truncated by Suricata", "type": "boolean" } }, @@ -1246,7 +1283,11 @@ "additionalProperties": false }, "grouped": { + "desription": "DNS fields grouped by type: alternative format, no direct keywords", "type": "object", + "suricata": { + "keywords": false + }, "properties": { "A": { "type": "array", @@ -1365,6 +1406,9 @@ }, "drop": { "type": "object", + "suricata": { + "keywords": false + }, "properties": { "ack": { "type": "boolean" @@ -4561,6 +4605,9 @@ "stats": { "type": "object", "optional": true, + "suricata": { + "keywords": false + }, "properties": { "uptime": { "description": "Suricata engine's uptime", @@ -6613,7 +6660,8 @@ "type": "integer" }, "tc_urgent_oob_data": { - "description": "Number of Out-of-Band bytes sent by server using TCP urgent packets", + "description": + "Number of Out-of-Band bytes sent by server using TCP urgent packets", "type": "integer" }, "tcp_flags": { @@ -6632,7 +6680,8 @@ "type": "integer" }, "ts_urgent_oob_data": { - "description": "Number of Out-of-Band bytes sent by client using TCP urgent packets", + "description": + "Number of Out-of-Band bytes sent by client using TCP urgent packets", "type": "integer" }, "urg": { @@ -6901,7 +6950,8 @@ "type": "integer" }, "mname_truncated": { - "description": "Set to true if the mname was too long and truncated by Suricata", + "description": + "Set to true if the mname was too long and truncated by Suricata", "type": "boolean" } }, @@ -6914,10 +6964,21 @@ "type": "object", "properties": { "rdata": { - "type": "string" + "type": "string", + "suricata": { + "keywords": [ + "dns.response.rrname" + ] + } }, "rrname": { - "type": "string" + "type": "string", + "suricata": { + "keywords": [ + "dns.authorities.rrname", + "dns.response.rrname" + ] + } }, "rrtype": { "type": "string" @@ -6929,11 +6990,13 @@ "$ref": "#/$defs/dns.soa" }, "rdata_truncated": { - "description": "Set to true if the rdata was too long and truncated by Suricata", + "description": + "Set to true if the rdata was too long and truncated by Suricata", "type": "boolean" }, "rrname_truncated": { - "description": "Set to true if the rrname was too long and truncated by Suricata", + "description": + "Set to true if the rrname was too long and truncated by Suricata", "type": "boolean" } }, @@ -6947,10 +7010,21 @@ "type": "object", "properties": { "rdata": { - "type": "string" + "type": "string", + "suricata": { + "keywords": [ + "dns.response.rrname" + ] + } }, "rrname": { - "type": "string" + "type": "string", + "suricata": { + "keywords": [ + "dns.additionals.rrname", + "dns.response.rrname" + ] + } }, "rrtype": { "type": "string" diff --git a/rust/src/dns/dns.rs b/rust/src/dns/dns.rs index 7b2f67b43a2a..dc8b60411ce7 100644 --- a/rust/src/dns/dns.rs +++ b/rust/src/dns/dns.rs @@ -1023,6 +1023,129 @@ pub unsafe extern "C" fn SCDnsTxGetAnswerName( false } +/// Get the DNS response authority name at index i. +#[no_mangle] +pub unsafe extern "C" fn SCDnsTxGetAuthorityName( + tx: &mut DNSTransaction, i: u32, buf: *mut *const u8, len: *mut u32, +) -> bool { + let index = i as usize; + + if let Some(response) = &tx.response { + if let Some(record) = response.authorities.get(index) { + if !record.name.value.is_empty() { + *buf = record.name.value.as_ptr(); + *len = record.name.value.len() as u32; + return true; + } + } + } + + false +} + +/// Get the DNS response additional name at index i. +#[no_mangle] +pub unsafe extern "C" fn SCDnsTxGetAdditionalName( + tx: &mut DNSTransaction, i: u32, buf: *mut *const u8, len: *mut u32, +) -> bool { + let index = i as usize; + + if let Some(response) = &tx.response { + if let Some(record) = response.additionals.get(index) { + if !record.name.value.is_empty() { + *buf = record.name.value.as_ptr(); + *len = record.name.value.len() as u32; + return true; + } + } + } + + false +} + +fn get_rdata_name(data: &DNSRData) -> Option<&DNSName> { + match data { + DNSRData::CNAME(name) + | DNSRData::PTR(name) + | DNSRData::MX(name) + | DNSRData::NS(name) => { + Some(name) + } + DNSRData::SOA(soa) => { + Some(&soa.mname) + } + _ => { + None + } + } +} + +/// Get the DNS response answer rdata at index i that could be a domain name. +#[no_mangle] +pub unsafe extern "C" fn SCDnsTxGetAnswerRdata( + tx: &mut DNSTransaction, i: u32, buf: *mut *const u8, len: *mut u32, +) -> bool { + let index = i as usize; + + if let Some(response) = &tx.response { + if let Some(record) = response.answers.get(index) { + if let Some(name) = get_rdata_name(&record.data) { + if !name.value.is_empty() { + *buf = name.value.as_ptr(); + *len = name.value.len() as u32; + return true; + } + } + } + } + + false +} + +/// Get the DNS response authority rdata at index i that could be a domain name. +#[no_mangle] +pub unsafe extern "C" fn SCDnsTxGetAuthorityRdata( + tx: &mut DNSTransaction, i: u32, buf: *mut *const u8, len: *mut u32, +) -> bool { + let index = i as usize; + + if let Some(response) = &tx.response { + if let Some(record) = response.authorities.get(index) { + if let Some(name) = get_rdata_name(&record.data) { + if !name.value.is_empty() { + *buf = name.value.as_ptr(); + *len = name.value.len() as u32; + return true; + } + } + } + } + + false +} + +/// Get the DNS response additional rdata at index i that could be a domain name. +#[no_mangle] +pub unsafe extern "C" fn SCDnsTxGetAdditionalRdata( + tx: &mut DNSTransaction, i: u32, buf: *mut *const u8, len: *mut u32, +) -> bool { + let index = i as usize; + + if let Some(response) = &tx.response { + if let Some(record) = response.additionals.get(index) { + if let Some(name) = get_rdata_name(&record.data) { + if !name.value.is_empty() { + *buf = name.value.as_ptr(); + *len = name.value.len() as u32; + return true; + } + } + } + } + + false +} + /// Get the DNS response flags for a transaction. /// /// extern uint16_t SCDnsTxGetResponseFlags(RSDNSTransaction *); diff --git a/scripts/eve-parity.py b/scripts/eve-parity.py new file mode 100755 index 000000000000..7599f7f60d42 --- /dev/null +++ b/scripts/eve-parity.py @@ -0,0 +1,168 @@ +#! /usr/bin/env python3 +# +# Tool for checking parity between the EVE schema and Suricata +# keywords. +# +# Usage: ./scripts/eve-parity.py [missing|having] +# +# ## unmapped-keywords +# +# Display all known keywords that are not mapped to an EVE field. +# +# ## unmapped-fields +# +# Display all eve fields that do not have a keyword mapping. +# +# ## mapped-fields +# +# Display all EVE fields that have a keyword mapping. + + +import sys +import subprocess +import json +import argparse + + +def main(): + parser = argparse.ArgumentParser(description="EVE Parity Check Tool") + parser.add_argument( + "command", choices=["mapped-fields", "unmapped-keywords", "unmapped-fields"] + ) + args = parser.parse_args() + + keywords = load_known_keywords() + keys = load_schema() + + if args.command == "mapped-fields": + mapped_fields(keywords, keys) + elif args.command == "unmapped-keywords": + unmapped_keywords(keywords, keys) + elif args.command == "unmapped-fields": + unmapped_fields(keywords, keys) + + +def unmapped_keywords(keywords, keys): + """Report known keywords that are not mapped to an EVE field.""" + schema_keywords = set() + for key in keys.keys(): + if "keywords" in keys[key] and keys[key]["keywords"]: + for keyword in keys[key]["keywords"]: + schema_keywords.add(keyword) + unmapped = keywords - schema_keywords + for keyword in sorted(unmapped): + print(keyword) + + +def unmapped_fields(keywords, keys): + with_missing = set() + + for key in keys.keys(): + if "keywords" not in keys[key]: + with_missing.add(key) + + # Print sorted. + for key in sorted(with_missing): + print(key) + + +def mapped_fields(keywords, keys): + for key in keys.keys(): + if "keywords" in keys[key] and keys[key]["keywords"]: + for keyword in keys[key]["keywords"]: + if keyword not in keywords: + errprint("ERROR: Unknown keyword: {}".format(keyword)) + print("{} -> [{}]".format(key, ", ".join(keys[key]["keywords"]))) + + +def load_schema(): + schema = json.load(open("etc/schema.json")) + stack = [(schema, [])] + keys = {} + + while stack: + (current, path) = stack.pop(0) + + for name, props in current["properties"].items(): + if "$ref" in props: + ref = find_ref(schema, props["$ref"]) + if not ref: + raise Exception("$ref not found: {}".format(props["$ref"])) + props = ref + if props["type"] in ["string", "integer", "boolean", "number"]: + # End of the line... + key = ".".join(path + [name]) + keys[key] = props.get("suricata", {}) + elif props["type"] == "object": + # An object can set "suricata.keywords" to false to + # disable descending into it. For examples, "stats". + keywords = props.get("suricata", {}).get("keywords") + if keywords is False: + # print("Skipping object {}, keywords disabled".format(".".join(path + [name]))) + continue + + if "properties" in props: + stack.insert(0, (props, path + [name])) + else: + # May want to warn that this object has no properties. + key = ".".join(path + [name]) + keys[key] = {} + elif props["type"] == "array": + if "items" in props and "type" in props["items"]: + if "properties" in props["items"]: + stack.insert( + 0, + ( + props["items"], + path + ["{}".format(name)], + ), + ) + else: + # May want to warn that this array has no properties. + key = ".".join(path + [name]) + keys[key] = {} + else: + # May want to warn that this array has no items. + key = ".".join(path + [name]) + keys[key] = {} + else: + raise Exception("Unsupported type: {}".format(props["type"])) + + return keys + + +def load_known_keywords(): + keywords = set() + result = subprocess.check_output(["./src/suricata", "--list-keywords=csv"]) + lines = result.decode().split("\n") + # Skip first line, as its a header line. + for line in lines[1:]: + parts = line.split(";") + if parts: + # Skip transforms. + if len(parts) > 3 and parts[3].find("transform") > -1: + continue + + keywords.add(parts[0]) + return keywords + + +def errprint(*args, **kwargs): + print(*args, file=sys.stderr, **kwargs) + + +def find_ref(schema: dict, ref: str) -> dict: + parts = ref.split("/") + + root = parts.pop(0) + if root != "#": + raise Exception("Unsupported reference: {}".format(ref)) + + while parts: + schema = schema[parts.pop(0)] + + return schema + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/src/Makefile.am b/src/Makefile.am index c3b1e9d237f9..f8ed0655c729 100755 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -112,12 +112,12 @@ noinst_HEADERS = \ detect-detection-filter.h \ detect-distance.h \ detect-dnp3.h \ - detect-dns-answer-name.h \ + detect-dns-name.h \ detect-dns-opcode.h \ detect-dns-rcode.h \ + detect-dns-response.h \ detect-dns-rrtype.h \ detect-dns-query.h \ - detect-dns-query-name.h \ detect-dsize.h \ detect-engine-address.h \ detect-engine-address-ipv4.h \ @@ -687,12 +687,12 @@ libsuricata_c_a_SOURCES = \ detect-detection-filter.c \ detect-distance.c \ detect-dnp3.c \ - detect-dns-answer-name.c \ + detect-dns-name.c \ detect-dns-opcode.c \ detect-dns-rcode.c \ + detect-dns-response.c \ detect-dns-rrtype.c \ detect-dns-query.c \ - detect-dns-query-name.c \ detect-dsize.c \ detect-engine-address.c \ detect-engine-address-ipv4.c \ diff --git a/src/detect-dns-answer-name.c b/src/detect-dns-answer-name.c deleted file mode 100644 index ccf4746254df..000000000000 --- a/src/detect-dns-answer-name.c +++ /dev/null @@ -1,92 +0,0 @@ -/* Copyright (C) 2023 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. - */ - -/** - * \file - * - * Detect keyword for DNS answer name: dns.answer.name - */ - -#include "detect.h" -#include "detect-parse.h" -#include "detect-engine.h" -#include "detect-engine-prefilter.h" -#include "detect-engine-content-inspection.h" -#include "detect-dns-answer-name.h" -#include "util-profiling.h" -#include "rust.h" - -static int detect_buffer_id = 0; - -static int DetectSetup(DetectEngineCtx *de_ctx, Signature *s, const char *str) -{ - if (DetectBufferSetActiveList(de_ctx, s, detect_buffer_id) < 0) { - return -1; - } - if (DetectSignatureSetAppProto(s, ALPROTO_DNS) < 0) { - return -1; - } - - return 0; -} - -static InspectionBuffer *GetBuffer(DetectEngineThreadCtx *det_ctx, - const DetectEngineTransforms *transforms, Flow *f, uint8_t flags, void *txv, int list_id, - uint32_t index) -{ - InspectionBuffer *buffer = InspectionBufferMultipleForListGet(det_ctx, list_id, index); - if (buffer == NULL) { - return NULL; - } - if (buffer->initialized) { - return buffer; - } - - bool to_client = (flags & STREAM_TOSERVER) == 0; - const uint8_t *data = NULL; - uint32_t data_len = 0; - - if (!SCDnsTxGetAnswerName(txv, to_client, index, &data, &data_len)) { - InspectionBufferSetupMultiEmpty(buffer); - return NULL; - } - InspectionBufferSetupMulti(buffer, transforms, data, data_len); - buffer->flags = DETECT_CI_FLAGS_SINGLE; - return buffer; -} - -void DetectDnsAnswerNameRegister(void) -{ - static const char *keyword = "dns.answer.name"; - sigmatch_table[DETECT_DNS_ANSWER_NAME].name = keyword; - sigmatch_table[DETECT_DNS_ANSWER_NAME].desc = "DNS answer name sticky buffer"; - sigmatch_table[DETECT_DNS_ANSWER_NAME].url = "/rules/dns-keywords.html#dns-answer-name"; - sigmatch_table[DETECT_DNS_ANSWER_NAME].Setup = DetectSetup; - sigmatch_table[DETECT_DNS_ANSWER_NAME].flags |= SIGMATCH_NOOPT; - sigmatch_table[DETECT_DNS_ANSWER_NAME].flags |= SIGMATCH_INFO_STICKY_BUFFER; - - /* Register in the TO_SERVER direction, even though this is not - normal, it could be provided as part of a request. */ - DetectAppLayerMultiRegister(keyword, ALPROTO_DNS, SIG_FLAG_TOSERVER, 0, GetBuffer, 2, 1); - /* Register in the TO_CLIENT direction. */ - DetectAppLayerMultiRegister(keyword, ALPROTO_DNS, SIG_FLAG_TOCLIENT, 0, GetBuffer, 2, 1); - - DetectBufferTypeSetDescriptionByName(keyword, "dns answer name"); - DetectBufferTypeSupportsMultiInstance(keyword); - - detect_buffer_id = DetectBufferTypeGetByName(keyword); -} diff --git a/src/detect-dns-name.c b/src/detect-dns-name.c new file mode 100644 index 000000000000..54a95d05a78a --- /dev/null +++ b/src/detect-dns-name.c @@ -0,0 +1,188 @@ +/* Copyright (C) 2025 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. + */ + +/** + * \file + * + * Detect keyword for DNS rrnames: + * - dns.queries.rrname + * - dns.answers.rrname + * - dns.authorities.name + * - dns.additionals.name + */ + +#include "detect.h" +#include "detect-parse.h" +#include "detect-engine.h" +#include "detect-engine-content-inspection.h" +#include "detect-engine-helper.h" +#include "detect-dns-name.h" +#include "rust.h" + +enum { + DNS_QUERY = 0, + DNS_ANSWER, + DNS_AUTHORITY, + DNS_ADDITIONAL, +}; + +static int query_buffer_id = 0; +static int answer_buffer_id = 0; +static int authority_buffer_id = 0; +static int additional_buffer_id = 0; + +static int DetectSetup(DetectEngineCtx *de_ctx, Signature *s, const char *str, int id) +{ + if (DetectBufferSetActiveList(de_ctx, s, id) < 0) { + return -1; + } + if (DetectSignatureSetAppProto(s, ALPROTO_DNS) < 0) { + return -1; + } + + return 0; +} + +static int SetupQueryBuffer(DetectEngineCtx *de_ctx, Signature *s, const char *str) +{ + return DetectSetup(de_ctx, s, str, query_buffer_id); +} + +static int SetupAnswerBuffer(DetectEngineCtx *de_ctx, Signature *s, const char *str) +{ + return DetectSetup(de_ctx, s, str, answer_buffer_id); +} + +static int SetupAdditionalsBuffer(DetectEngineCtx *de_ctx, Signature *s, const char *str) +{ + return DetectSetup(de_ctx, s, str, additional_buffer_id); +} + +static int SetupAuthoritiesBuffer(DetectEngineCtx *de_ctx, Signature *s, const char *str) +{ + return DetectSetup(de_ctx, s, str, authority_buffer_id); +} + +static InspectionBuffer *GetBuffer(DetectEngineThreadCtx *det_ctx, + const DetectEngineTransforms *transforms, Flow *f, uint8_t flags, void *txv, int list_id, + uint32_t index, int what) +{ + InspectionBuffer *buffer = InspectionBufferMultipleForListGet(det_ctx, list_id, index); + if (buffer == NULL) { + return NULL; + } + if (buffer->initialized) { + return buffer; + } + + bool to_client = (flags & STREAM_TOSERVER) == 0; + const uint8_t *data = NULL; + uint32_t data_len = 0; + + bool ok = false; + switch (what) { + case DNS_QUERY: + ok = SCDnsTxGetQueryName(txv, to_client, index, &data, &data_len); + break; + case DNS_ANSWER: + ok = SCDnsTxGetAnswerName(txv, to_client, index, &data, &data_len); + break; + case DNS_AUTHORITY: + ok = SCDnsTxGetAuthorityName(txv, index, &data, &data_len); + break; + case DNS_ADDITIONAL: + ok = SCDnsTxGetAdditionalName(txv, index, &data, &data_len); + break; + default: + DEBUG_VALIDATE_BUG_ON("unhandled dns rrname type"); + InspectionBufferSetupMultiEmpty(buffer); + return NULL; + } + + if (ok) { + InspectionBufferSetupMulti(buffer, transforms, data, data_len); + buffer->flags = DETECT_CI_FLAGS_SINGLE; + return buffer; + } + + InspectionBufferSetupMultiEmpty(buffer); + return NULL; +} + +static InspectionBuffer *GetQueryBuffer(DetectEngineThreadCtx *det_ctx, + const DetectEngineTransforms *transforms, Flow *f, uint8_t flags, void *txv, int list_id, + uint32_t index) +{ + return GetBuffer(det_ctx, transforms, f, flags, txv, list_id, index, DNS_QUERY); +} + +static InspectionBuffer *GetAnswerBuffer(DetectEngineThreadCtx *det_ctx, + const DetectEngineTransforms *transforms, Flow *f, uint8_t flags, void *txv, int list_id, + uint32_t index) +{ + return GetBuffer(det_ctx, transforms, f, flags, txv, list_id, index, DNS_ANSWER); +} + +static InspectionBuffer *GetAuthorityBuffer(DetectEngineThreadCtx *det_ctx, + const DetectEngineTransforms *transforms, Flow *f, uint8_t flags, void *txv, int list_id, + uint32_t index) +{ + return GetBuffer(det_ctx, transforms, f, flags, txv, list_id, index, DNS_AUTHORITY); +} + +static InspectionBuffer *GetAdditionalBuffer(DetectEngineThreadCtx *det_ctx, + const DetectEngineTransforms *transforms, Flow *f, uint8_t flags, void *txv, int list_id, + uint32_t index) +{ + return GetBuffer(det_ctx, transforms, f, flags, txv, list_id, index, DNS_ADDITIONAL); +} + +static int Register(const char *keyword, const char *desc, const char *doc, + int (*Setup)(DetectEngineCtx *, Signature *, const char *), + InspectionMultiBufferGetDataPtr GetBufferFn) +{ + int keyword_id = SCDetectHelperNewKeywordId(); + sigmatch_table[keyword_id].name = keyword; + sigmatch_table[keyword_id].desc = desc; + sigmatch_table[keyword_id].url = doc; + sigmatch_table[keyword_id].Setup = Setup; + sigmatch_table[keyword_id].flags |= SIGMATCH_NOOPT; + sigmatch_table[keyword_id].flags |= SIGMATCH_INFO_STICKY_BUFFER; + + DetectAppLayerMultiRegister(keyword, ALPROTO_DNS, SIG_FLAG_TOSERVER, 0, GetBufferFn, 2, 1); + DetectAppLayerMultiRegister(keyword, ALPROTO_DNS, SIG_FLAG_TOCLIENT, 0, GetBufferFn, 2, 1); + + DetectBufferTypeSetDescriptionByName(keyword, keyword); + DetectBufferTypeSupportsMultiInstance(keyword); + + return DetectBufferTypeGetByName(keyword); +} + +void DetectDnsNameRegister(void) +{ + query_buffer_id = Register("dns.queries.rrname", "DNS query rrname sticky buffer", + "/rules/dns-keywords.html#dns.queries.rrname", SetupQueryBuffer, GetQueryBuffer); + answer_buffer_id = Register("dns.answers.rrname", "DNS answer rrname sticky buffer", + "/rules/dns-keywords.html#dns.answers.rrname", SetupAnswerBuffer, GetAnswerBuffer); + additional_buffer_id = + Register("dns.additionals.rrname", "DNS additionals rrname sticky buffer", + "/rules/dns-keywords.html#dns-additionals-rrname", SetupAdditionalsBuffer, + GetAdditionalBuffer); + authority_buffer_id = Register("dns.authorities.rrname", "DNS authorities rrname sticky buffer", + "/rules/dns-keywords.html#dns-authorities-rrname", SetupAuthoritiesBuffer, + GetAuthorityBuffer); +} diff --git a/src/detect-dns-query-name.h b/src/detect-dns-name.h similarity index 81% rename from src/detect-dns-query-name.h rename to src/detect-dns-name.h index 3f5bc03bf72f..c7607e9b8751 100644 --- a/src/detect-dns-query-name.h +++ b/src/detect-dns-name.h @@ -15,9 +15,9 @@ * 02110-1301, USA. */ -#ifndef SURICATA_DETECT_DNS_QUERY_NAME_H -#define SURICATA_DETECT_DNS_QUERY_NAME_H +#ifndef SURICATA_DETECT_DNS_NAME_H +#define SURICATA_DETECT_DNS_NAME_H -void DetectDnsQueryNameRegister(void); +void DetectDnsNameRegister(void); -#endif /* SURICATA_DETECT_DNS_QUERY_NAME_H */ +#endif /* SURICATA_DETECT_DNS_NAME_H */ diff --git a/src/detect-dns-query-name.c b/src/detect-dns-query-name.c deleted file mode 100644 index dea5c81c7f87..000000000000 --- a/src/detect-dns-query-name.c +++ /dev/null @@ -1,91 +0,0 @@ -/* Copyright (C) 2023 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. - */ - -/** - * \file - * - * Detect keyword for DNS query names: dns.query.name - */ - -#include "detect.h" -#include "detect-parse.h" -#include "detect-engine.h" -#include "detect-engine-prefilter.h" -#include "detect-engine-content-inspection.h" -#include "detect-dns-query-name.h" -#include "util-profiling.h" -#include "rust.h" - -static int detect_buffer_id = 0; - -static int DetectSetup(DetectEngineCtx *de_ctx, Signature *s, const char *str) -{ - if (DetectBufferSetActiveList(de_ctx, s, detect_buffer_id) < 0) { - return -1; - } - if (DetectSignatureSetAppProto(s, ALPROTO_DNS) < 0) { - return -1; - } - - return 0; -} - -static InspectionBuffer *GetBuffer(DetectEngineThreadCtx *det_ctx, - const DetectEngineTransforms *transforms, Flow *f, const uint8_t flags, void *txv, - int list_id, uint32_t index) -{ - InspectionBuffer *buffer = InspectionBufferMultipleForListGet(det_ctx, list_id, index); - if (buffer == NULL) { - return NULL; - } - if (buffer->initialized) { - return buffer; - } - - bool to_client = (flags & STREAM_TOSERVER) == 0; - const uint8_t *data = NULL; - uint32_t data_len = 0; - - if (!SCDnsTxGetQueryName(txv, to_client, index, &data, &data_len)) { - InspectionBufferSetupMultiEmpty(buffer); - return NULL; - } - InspectionBufferSetupMulti(buffer, transforms, data, data_len); - buffer->flags = DETECT_CI_FLAGS_SINGLE; - return buffer; -} - -void DetectDnsQueryNameRegister(void) -{ - static const char *keyword = "dns.query.name"; - sigmatch_table[DETECT_DNS_QUERY_NAME].name = keyword; - sigmatch_table[DETECT_DNS_QUERY_NAME].desc = "DNS query name sticky buffer"; - sigmatch_table[DETECT_DNS_QUERY_NAME].url = "/rules/dns-keywords.html#dns-query-name"; - sigmatch_table[DETECT_DNS_QUERY_NAME].Setup = DetectSetup; - sigmatch_table[DETECT_DNS_QUERY_NAME].flags |= SIGMATCH_NOOPT; - sigmatch_table[DETECT_DNS_QUERY_NAME].flags |= SIGMATCH_INFO_STICKY_BUFFER; - - /* Register in both directions as the query is usually echoed back - in the response. */ - DetectAppLayerMultiRegister(keyword, ALPROTO_DNS, SIG_FLAG_TOSERVER, 0, GetBuffer, 2, 1); - DetectAppLayerMultiRegister(keyword, ALPROTO_DNS, SIG_FLAG_TOCLIENT, 0, GetBuffer, 2, 1); - - DetectBufferTypeSetDescriptionByName(keyword, "dns query name"); - DetectBufferTypeSupportsMultiInstance(keyword); - - detect_buffer_id = DetectBufferTypeGetByName(keyword); -} diff --git a/src/detect-dns-response.c b/src/detect-dns-response.c new file mode 100644 index 000000000000..d585c746c05f --- /dev/null +++ b/src/detect-dns-response.c @@ -0,0 +1,307 @@ +/* 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. + */ + +/** + * \file + * + * Detect keyword for DNS response: dns.response.rrname + */ + +#include "detect.h" +#include "detect-parse.h" +#include "detect-engine.h" +#include "detect-engine-prefilter.h" +#include "detect-engine-content-inspection.h" +#include "detect-dns-response.h" +#include "util-profiling.h" +#include "rust.h" + +static int detect_buffer_id = 0; +typedef struct PrefilterMpm { + int list_id; + const MpmCtx *mpm_ctx; + const DetectEngineTransforms *transforms; +} PrefilterMpm; + +typedef enum DnsResponseSection_ { + DNS_RESPONSE_QUERY = 0, + DNS_RESPONSE_ANSWER, + DNS_RESPONSE_AUTHORITY, + DNS_RESPONSE_ADDITIONAL, + + /* always last */ + DNS_RESPONSE_MAX, +} DnsResponseSection; + +struct DnsResponseGetDataArgs { + DnsResponseSection response_section; /**< query, answer, authority, additional */ + uint32_t response_id; /**< index into response resource records */ + uint32_t local_id; /**< used as index into thread inspect array */ +}; + +static int DetectSetup(DetectEngineCtx *de_ctx, Signature *s, const char *str) +{ + if (DetectBufferSetActiveList(de_ctx, s, detect_buffer_id) < 0) { + return -1; + } + if (DetectSignatureSetAppProto(s, ALPROTO_DNS) < 0) { + return -1; + } + + return 0; +} + +static InspectionBuffer *GetBuffer(DetectEngineThreadCtx *det_ctx, uint8_t flags, + const DetectEngineTransforms *transforms, void *txv, struct DnsResponseGetDataArgs *cbdata, + int list_id, bool get_rdata) +{ + InspectionBuffer *buffer = + InspectionBufferMultipleForListGet(det_ctx, list_id, cbdata->local_id); + if (buffer == NULL) { + return NULL; + } + if (buffer->initialized) { + return buffer; + } + + const uint8_t *data = NULL; + uint32_t data_len = 0; + + if (get_rdata) { + /* Get rdata values that are formatted as resource names. */ + switch (cbdata->response_section) { + case DNS_RESPONSE_ANSWER: + if (!SCDnsTxGetAnswerRdata(txv, cbdata->response_id, &data, &data_len)) { + InspectionBufferSetupMultiEmpty(buffer); + return NULL; + } + break; + case DNS_RESPONSE_AUTHORITY: + if (!SCDnsTxGetAuthorityRdata(txv, cbdata->response_id, &data, &data_len)) { + InspectionBufferSetupMultiEmpty(buffer); + return NULL; + } + break; + case DNS_RESPONSE_ADDITIONAL: + if (!SCDnsTxGetAdditionalRdata(txv, cbdata->response_id, &data, &data_len)) { + InspectionBufferSetupMultiEmpty(buffer); + return NULL; + } + break; + default: + InspectionBufferSetupMultiEmpty(buffer); + return NULL; + } + } else { + /* Get name values. */ + switch (cbdata->response_section) { + case DNS_RESPONSE_QUERY: + if (!SCDnsTxGetQueryName(txv, true, cbdata->response_id, &data, &data_len)) { + InspectionBufferSetupMultiEmpty(buffer); + return NULL; + } + break; + case DNS_RESPONSE_ANSWER: + if (!SCDnsTxGetAnswerName(txv, true, cbdata->response_id, &data, &data_len)) { + InspectionBufferSetupMultiEmpty(buffer); + return NULL; + } + break; + case DNS_RESPONSE_AUTHORITY: + if (!SCDnsTxGetAuthorityName(txv, cbdata->response_id, &data, &data_len)) { + InspectionBufferSetupMultiEmpty(buffer); + return NULL; + } + break; + case DNS_RESPONSE_ADDITIONAL: + if (!SCDnsTxGetAdditionalName(txv, cbdata->response_id, &data, &data_len)) { + InspectionBufferSetupMultiEmpty(buffer); + return NULL; + } + break; + default: + InspectionBufferSetupMultiEmpty(buffer); + return NULL; + } + } + + InspectionBufferSetupMulti(buffer, transforms, data, data_len); + buffer->flags = DETECT_CI_FLAGS_SINGLE; + return buffer; +} + +static uint8_t DetectEngineInspectCb(DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx, + const struct DetectEngineAppInspectionEngine_ *engine, const Signature *s, Flow *f, + uint8_t flags, void *alstate, void *txv, uint64_t tx_id) +{ + const DetectEngineTransforms *transforms = NULL; + if (!engine->mpm) { + transforms = engine->v2.transforms; + } + + uint32_t local_id = 0; + /* loop through each possible DNS response section */ + for (uint8_t section = DNS_RESPONSE_QUERY; section < DNS_RESPONSE_MAX; section++) { + uint32_t response_id = 0; + /* loop through each record in section inspecting "name" and "rdata" */ + while (1) { + struct DnsResponseGetDataArgs cbdata = { section, response_id, local_id }; + + /* do inspection for resource record "name" */ + InspectionBuffer *buffer = + GetBuffer(det_ctx, flags, transforms, txv, &cbdata, engine->sm_list, false); + if (buffer == NULL || buffer->inspect == NULL) { + local_id++; + break; + } + + bool match = DetectEngineContentInspectionBuffer(de_ctx, det_ctx, s, engine->smd, NULL, + f, buffer, DETECT_ENGINE_CONTENT_INSPECTION_MODE_STATE); + if (match) { + return DETECT_ENGINE_INSPECT_SIG_MATCH; + } + + local_id++; + if (section == DNS_RESPONSE_QUERY) { + /* no rdata to inspect for query section, move on to next record */ + response_id++; + continue; + } + + /* do inspection for resource record "rdata" */ + cbdata.local_id = local_id; + buffer = GetBuffer(det_ctx, flags, transforms, txv, &cbdata, engine->sm_list, true); + if (buffer == NULL || buffer->inspect == NULL) { + local_id++; + response_id++; + continue; + } + + match = DetectEngineContentInspectionBuffer(de_ctx, det_ctx, s, engine->smd, NULL, f, + buffer, DETECT_ENGINE_CONTENT_INSPECTION_MODE_STATE); + if (match) { + return DETECT_ENGINE_INSPECT_SIG_MATCH; + } + local_id++; + response_id++; + } + } + + return DETECT_ENGINE_INSPECT_SIG_NO_MATCH; +} + +static void DetectDnsResponsePrefilterTx(DetectEngineThreadCtx *det_ctx, const void *pectx, + Packet *p, Flow *f, void *txv, const uint64_t idx, const AppLayerTxData *_txd, + const uint8_t flags) +{ + SCEnter(); + + const PrefilterMpm *ctx = (const PrefilterMpm *)pectx; + const MpmCtx *mpm_ctx = ctx->mpm_ctx; + const int list_id = ctx->list_id; + + uint32_t local_id = 0; + /* loop through each possible DNS response section */ + for (uint8_t section = DNS_RESPONSE_QUERY; section < DNS_RESPONSE_MAX; section++) { + uint32_t response_id = 0; + /* loop through each record in section inspecting "name" and "rdata" */ + while (1) { + struct DnsResponseGetDataArgs cbdata = { section, response_id, local_id }; + + /* extract resource record "name" */ + InspectionBuffer *buffer = + GetBuffer(det_ctx, flags, ctx->transforms, txv, &cbdata, list_id, false); + if (buffer == NULL) { + local_id++; + break; + } + + if (buffer->inspect_len >= mpm_ctx->minlen) { + (void)mpm_table[mpm_ctx->mpm_type].Search(mpm_ctx, &det_ctx->mtc, &det_ctx->pmq, + buffer->inspect, buffer->inspect_len); + PREFILTER_PROFILING_ADD_BYTES(det_ctx, buffer->inspect_len); + } + + local_id++; + if (section == DNS_RESPONSE_QUERY) { + /* no rdata to inspect for query section, move on to next name entry */ + response_id++; + continue; + } + + /* extract resource record "rdata" */ + cbdata.local_id = local_id; + buffer = GetBuffer(det_ctx, flags, ctx->transforms, txv, &cbdata, list_id, true); + if (buffer == NULL) { + local_id++; + response_id++; + continue; + } + + if (buffer->inspect_len >= mpm_ctx->minlen) { + (void)mpm_table[mpm_ctx->mpm_type].Search(mpm_ctx, &det_ctx->mtc, &det_ctx->pmq, + buffer->inspect, buffer->inspect_len); + PREFILTER_PROFILING_ADD_BYTES(det_ctx, buffer->inspect_len); + } + local_id++; + response_id++; + } + } +} + +static void DetectDnsResponsePrefilterMpmFree(void *ptr) +{ + SCFree(ptr); +} + +static int DetectDnsResponsePrefilterMpmRegister(DetectEngineCtx *de_ctx, SigGroupHead *sgh, + MpmCtx *mpm_ctx, const DetectBufferMpmRegistry *mpm_reg, int list_id) +{ + PrefilterMpm *pectx = SCCalloc(1, sizeof(*pectx)); + if (pectx == NULL) { + return -1; + } + pectx->list_id = list_id; + pectx->mpm_ctx = mpm_ctx; + pectx->transforms = &mpm_reg->transforms; + + return PrefilterAppendTxEngine(de_ctx, sgh, DetectDnsResponsePrefilterTx, + mpm_reg->app_v2.alproto, mpm_reg->app_v2.tx_min_progress, pectx, + DetectDnsResponsePrefilterMpmFree, mpm_reg->pname); +} + +void DetectDnsResponseRegister(void) +{ + static const char *keyword = "dns.response.rrname"; + sigmatch_table[DETECT_DNS_RESPONSE].name = keyword; + sigmatch_table[DETECT_DNS_RESPONSE].desc = "DNS response sticky buffer"; + sigmatch_table[DETECT_DNS_RESPONSE].url = "/rules/dns-keywords.html#dns-response-rrname"; + sigmatch_table[DETECT_DNS_RESPONSE].Setup = DetectSetup; + sigmatch_table[DETECT_DNS_RESPONSE].flags |= SIGMATCH_NOOPT; + sigmatch_table[DETECT_DNS_RESPONSE].flags |= SIGMATCH_INFO_STICKY_BUFFER; + + /* Register in the TO_CLIENT direction. */ + DetectAppLayerInspectEngineRegister( + keyword, ALPROTO_DNS, SIG_FLAG_TOCLIENT, 1, DetectEngineInspectCb, NULL); + DetectAppLayerMpmRegister(keyword, SIG_FLAG_TOCLIENT, 2, DetectDnsResponsePrefilterMpmRegister, + NULL, ALPROTO_DNS, 1); + + DetectBufferTypeSetDescriptionByName(keyword, "dns response rrname"); + DetectBufferTypeSupportsMultiInstance(keyword); + + detect_buffer_id = DetectBufferTypeGetByName(keyword); +} diff --git a/src/detect-dns-answer-name.h b/src/detect-dns-response.h similarity index 73% rename from src/detect-dns-answer-name.h rename to src/detect-dns-response.h index e259f1a577d4..210bf4495f7c 100644 --- a/src/detect-dns-answer-name.h +++ b/src/detect-dns-response.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2023 Open Information Security Foundation +/* 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 @@ -15,9 +15,9 @@ * 02110-1301, USA. */ -#ifndef SURICATA_DETECT_DNS_ANSWER_NAME_H -#define SURICATA_DETECT_DNS_ANSWER_NAME_H +#ifndef SURICATA_DETECT_DNS_RESPONSE_H +#define SURICATA_DETECT_DNS_RESPONSE_H -void DetectDnsAnswerNameRegister(void); +void DetectDnsResponseRegister(void); -#endif /* SURICATA_DETECT_DNS_ANSWER_NAME_H */ +#endif /* SURICATA_DETECT_DNS_RESPONSE_H */ \ No newline at end of file diff --git a/src/detect-engine-helper.c b/src/detect-engine-helper.c index 5337f95969c0..8c9cce7a04e5 100644 --- a/src/detect-engine-helper.c +++ b/src/detect-engine-helper.c @@ -94,7 +94,7 @@ int DetectHelperMultiBufferMpmRegister(const char *name, const char *desc, AppPr return DetectBufferTypeGetByName(name); } -int DetectHelperKeywordRegister(const SCSigTableElmt *kw) +int SCDetectHelperNewKeywordId(void) { if (DETECT_TBLSIZE_IDX >= DETECT_TBLSIZE) { void *tmp = SCRealloc( @@ -103,48 +103,55 @@ int DetectHelperKeywordRegister(const SCSigTableElmt *kw) return -1; } sigmatch_table = tmp; + memset(&sigmatch_table[DETECT_TBLSIZE], 0, DETECT_TBLSIZE_STEP * sizeof(SigTableElmt)); DETECT_TBLSIZE += DETECT_TBLSIZE_STEP; } - sigmatch_table[DETECT_TBLSIZE_IDX].name = kw->name; - sigmatch_table[DETECT_TBLSIZE_IDX].desc = kw->desc; - sigmatch_table[DETECT_TBLSIZE_IDX].url = kw->url; - sigmatch_table[DETECT_TBLSIZE_IDX].flags = kw->flags; - sigmatch_table[DETECT_TBLSIZE_IDX].AppLayerTxMatch = + DETECT_TBLSIZE_IDX++; + return DETECT_TBLSIZE_IDX - 1; +} + +int DetectHelperKeywordRegister(const SCSigTableElmt *kw) +{ + int keyword_id = SCDetectHelperNewKeywordId(); + if (keyword_id < 0) { + return -1; + } + + sigmatch_table[keyword_id].name = kw->name; + sigmatch_table[keyword_id].desc = kw->desc; + sigmatch_table[keyword_id].url = kw->url; + sigmatch_table[keyword_id].flags = kw->flags; + sigmatch_table[keyword_id].AppLayerTxMatch = (int (*)(DetectEngineThreadCtx * det_ctx, Flow * f, uint8_t flags, void *alstate, void *txv, const Signature *s, const SigMatchCtx *ctx)) kw->AppLayerTxMatch; - sigmatch_table[DETECT_TBLSIZE_IDX].Setup = + sigmatch_table[keyword_id].Setup = (int (*)(DetectEngineCtx * de, Signature * s, const char *raw)) kw->Setup; - sigmatch_table[DETECT_TBLSIZE_IDX].Free = (void (*)(DetectEngineCtx * de, void *ptr)) kw->Free; - DETECT_TBLSIZE_IDX++; - return DETECT_TBLSIZE_IDX - 1; + sigmatch_table[keyword_id].Free = (void (*)(DetectEngineCtx * de, void *ptr)) kw->Free; + + return keyword_id; } int DetectHelperTransformRegister(const SCTransformTableElmt *kw) { - if (DETECT_TBLSIZE_IDX >= DETECT_TBLSIZE) { - void *tmp = SCRealloc( - sigmatch_table, (DETECT_TBLSIZE + DETECT_TBLSIZE_STEP) * sizeof(SigTableElmt)); - if (unlikely(tmp == NULL)) { - return -1; - } - sigmatch_table = tmp; - DETECT_TBLSIZE += DETECT_TBLSIZE_STEP; + int transform_id = SCDetectHelperNewKeywordId(); + if (transform_id < 0) { + return -1; } - sigmatch_table[DETECT_TBLSIZE_IDX].name = kw->name; - sigmatch_table[DETECT_TBLSIZE_IDX].desc = kw->desc; - sigmatch_table[DETECT_TBLSIZE_IDX].url = kw->url; - sigmatch_table[DETECT_TBLSIZE_IDX].flags = kw->flags; - sigmatch_table[DETECT_TBLSIZE_IDX].Transform = + sigmatch_table[transform_id].name = kw->name; + sigmatch_table[transform_id].desc = kw->desc; + sigmatch_table[transform_id].url = kw->url; + sigmatch_table[transform_id].flags = kw->flags; + sigmatch_table[transform_id].Transform = (void (*)(InspectionBuffer * buffer, void *options)) kw->Transform; - sigmatch_table[DETECT_TBLSIZE_IDX].TransformValidate = (bool (*)( + sigmatch_table[transform_id].TransformValidate = (bool (*)( const uint8_t *content, uint16_t content_len, void *context))kw->TransformValidate; - sigmatch_table[DETECT_TBLSIZE_IDX].Setup = + sigmatch_table[transform_id].Setup = (int (*)(DetectEngineCtx * de, Signature * s, const char *raw)) kw->Setup; - sigmatch_table[DETECT_TBLSIZE_IDX].Free = (void (*)(DetectEngineCtx * de, void *ptr)) kw->Free; - DETECT_TBLSIZE_IDX++; - return DETECT_TBLSIZE_IDX - 1; + sigmatch_table[transform_id].Free = (void (*)(DetectEngineCtx * de, void *ptr)) kw->Free; + + return transform_id; } InspectionBuffer *DetectHelperGetMultiData(struct DetectEngineThreadCtx_ *det_ctx, diff --git a/src/detect-engine-helper.h b/src/detect-engine-helper.h index 39fa632ed28c..7c8044430850 100644 --- a/src/detect-engine-helper.h +++ b/src/detect-engine-helper.h @@ -28,6 +28,8 @@ #include "detect.h" #include "rust.h" +int SCDetectHelperNewKeywordId(void); + int DetectHelperKeywordRegister(const SCSigTableElmt *kw); int DetectHelperBufferRegister(const char *name, AppProto alproto, bool toclient, bool toserver); diff --git a/src/detect-engine-register.c b/src/detect-engine-register.c index 88b77ec3ce3f..10af70b1d0f7 100644 --- a/src/detect-engine-register.c +++ b/src/detect-engine-register.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2007-2017 Open Information Security Foundation +/* Copyright (C) 2007-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 @@ -51,8 +51,8 @@ #include "detect-dns-rcode.h" #include "detect-dns-rrtype.h" #include "detect-dns-query.h" -#include "detect-dns-answer-name.h" -#include "detect-dns-query-name.h" +#include "detect-dns-name.h" +#include "detect-dns-response.h" #include "detect-tls-sni.h" #include "detect-tls-certs.h" #include "detect-tls-cert-fingerprint.h" @@ -557,8 +557,8 @@ void SigTableSetup(void) DetectDnsOpcodeRegister(); DetectDnsRcodeRegister(); DetectDnsRrtypeRegister(); - DetectDnsAnswerNameRegister(); - DetectDnsQueryNameRegister(); + DetectDnsNameRegister(); + DetectDnsResponseRegister(); DetectModbusRegister(); DetectDNP3Register(); diff --git a/src/detect-engine-register.h b/src/detect-engine-register.h index e3a57b4951a9..c08ac85b8720 100644 --- a/src/detect-engine-register.h +++ b/src/detect-engine-register.h @@ -246,9 +246,8 @@ enum DetectKeywordId { DETECT_DNS_QUERY, DETECT_DNS_OPCODE, DETECT_DNS_RCODE, + DETECT_DNS_RESPONSE, DETECT_DNS_RRTYPE, - DETECT_DNS_ANSWER_NAME, - DETECT_DNS_QUERY_NAME, DETECT_TLS_SNI, DETECT_TLS_CERTS, DETECT_TLS_CERT_ISSUER,