suricata
detect-dns-response.c
Go to the documentation of this file.
1/* Copyright (C) 2025 Open Information Security Foundation
2 *
3 * You can copy, redistribute or modify this Program under the terms of
4 * the GNU General Public License version 2 as published by the Free
5 * Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * version 2 along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15 * 02110-1301, USA.
16 */
17
18/**
19 * \file
20 *
21 * Detect keyword for DNS response: dns.response.rrname
22 */
23
24#include "detect.h"
25#include "detect-parse.h"
26#include "detect-engine.h"
28#include "detect-engine-mpm.h"
32#include "detect-dns-response.h"
33#include "util-profiling.h"
34#include "rust.h"
35
36static int detect_buffer_id = 0;
37static int mdns_detect_buffer_id = 0;
38
44
54
56 enum DnsResponseSection response_section; /**< query, answer, authority, additional */
57 uint32_t response_id; /**< index into response resource records */
58 uint32_t local_id; /**< used as index into thread inspect array */
59};
60
61static int DetectSetup(DetectEngineCtx *de_ctx, Signature *s, const char *str)
62{
63 if (SCDetectBufferSetActiveList(de_ctx, s, detect_buffer_id) < 0) {
64 return -1;
65 }
67 return -1;
68 }
69
70 return 0;
71}
72
73static int MdnsDetectSetup(DetectEngineCtx *de_ctx, Signature *s, const char *str)
74{
75 if (SCDetectBufferSetActiveList(de_ctx, s, mdns_detect_buffer_id) < 0) {
76 return -1;
77 }
79 return -1;
80 }
81
82 return 0;
83}
84
85static InspectionBuffer *GetBuffer(DetectEngineThreadCtx *det_ctx, uint8_t flags,
86 const DetectEngineTransforms *transforms, void *txv, struct DnsResponseGetDataArgs *cbdata,
87 int list_id, bool get_rdata)
88{
89 InspectionBuffer *buffer =
90 InspectionBufferMultipleForListGet(det_ctx, list_id, cbdata->local_id);
91 if (buffer == NULL) {
92 return NULL;
93 }
94 if (buffer->initialized) {
95 return buffer;
96 }
97
98 const uint8_t *data = NULL;
99 uint32_t data_len = 0;
100
101 if (get_rdata) {
102 /* Get rdata values that are formatted as resource names. */
103 switch (cbdata->response_section) {
105 if (!SCDnsTxGetAnswerRdata(txv, cbdata->response_id, &data, &data_len)) {
107 return NULL;
108 }
109 break;
111 if (!SCDnsTxGetAuthorityRdata(txv, cbdata->response_id, &data, &data_len)) {
113 return NULL;
114 }
115 break;
117 if (!SCDnsTxGetAdditionalRdata(txv, cbdata->response_id, &data, &data_len)) {
119 return NULL;
120 }
121 break;
122 default:
124 return NULL;
125 }
126 } else {
127 /* Get name values. */
128 switch (cbdata->response_section) {
130 if (!SCDnsTxGetQueryName(
131 det_ctx, txv, STREAM_TOCLIENT, cbdata->response_id, &data, &data_len)) {
133 return NULL;
134 }
135 break;
137 if (!SCDnsTxGetAnswerName(
138 det_ctx, txv, STREAM_TOCLIENT, cbdata->response_id, &data, &data_len)) {
140 return NULL;
141 }
142 break;
144 if (!SCDnsTxGetAuthorityName(
145 det_ctx, txv, 0, cbdata->response_id, &data, &data_len)) {
147 return NULL;
148 }
149 break;
151 if (!SCDnsTxGetAdditionalName(
152 det_ctx, txv, 0, cbdata->response_id, &data, &data_len)) {
154 return NULL;
155 }
156 break;
157 default:
159 return NULL;
160 }
161 }
162
163 InspectionBufferSetupMulti(det_ctx, buffer, transforms, data, data_len);
165 return buffer;
166}
167
168static inline uint8_t CheckSectionRecords(DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx,
169 const struct DetectEngineAppInspectionEngine_ *engine, const Signature *s, Flow *f,
170 uint8_t flags, void *txv, const DetectEngineTransforms *transforms, uint32_t *local_id,
171 enum DnsResponseSection section)
172{
173 uint32_t response_id = 0;
174
175 /* loop through each record in DNS response section inspecting "name" and "rdata" */
176 while (1) {
177 struct DnsResponseGetDataArgs cbdata = { section, response_id, *local_id };
178
179 /* do inspection for resource record "name" */
180 InspectionBuffer *buffer =
181 GetBuffer(det_ctx, flags, transforms, txv, &cbdata, engine->sm_list, false);
182 if (buffer == NULL || buffer->inspect == NULL) {
183 (*local_id)++;
184 break;
185 }
186
187 if (DetectEngineContentInspectionBuffer(de_ctx, det_ctx, s, engine->smd, NULL, f, buffer,
190 }
191
192 (*local_id)++;
193 if (section == DNS_RESPONSE_QUERY) {
194 /* no rdata to inspect for query section, move on to next record */
195 response_id++;
196 continue;
197 }
198
199 /* do inspection for resource record "rdata" */
200 cbdata.local_id = *local_id;
201 buffer = GetBuffer(det_ctx, flags, transforms, txv, &cbdata, engine->sm_list, true);
202 if (buffer == NULL || buffer->inspect == NULL) {
203 (*local_id)++;
204 response_id++;
205 continue;
206 }
207
208 if (DetectEngineContentInspectionBuffer(de_ctx, det_ctx, s, engine->smd, NULL, f, buffer,
211 }
212 (*local_id)++;
213 response_id++;
214 }
216}
217
218static inline void CheckSectionRecordsPrefilter(DetectEngineThreadCtx *det_ctx, const void *pectx,
219 void *txv, const uint8_t flags, uint32_t *local_id, enum DnsResponseSection section)
220{
221 const PrefilterMpm *ctx = (const PrefilterMpm *)pectx;
222 const MpmCtx *mpm_ctx = ctx->mpm_ctx;
223 const int list_id = ctx->list_id;
224 uint32_t response_id = 0;
225
226 while (1) {
227 struct DnsResponseGetDataArgs cbdata = { section, response_id, *local_id };
228
229 /* extract resource record "name" */
230 InspectionBuffer *buffer =
231 GetBuffer(det_ctx, flags, ctx->transforms, txv, &cbdata, list_id, false);
232 if (buffer == NULL) {
233 (*local_id)++;
234 break;
235 }
236
237 if (buffer->inspect_len >= mpm_ctx->minlen) {
238 (void)mpm_table[mpm_ctx->mpm_type].Search(
239 mpm_ctx, &det_ctx->mtc, &det_ctx->pmq, buffer->inspect, buffer->inspect_len);
241 }
242
243 (*local_id)++;
244 if (section == DNS_RESPONSE_QUERY) {
245 /* no rdata to inspect for query section, move on to next name entry */
246 response_id++;
247 continue;
248 }
249
250 /* extract resource record "rdata" */
251 cbdata.local_id = *local_id;
252 buffer = GetBuffer(det_ctx, flags, ctx->transforms, txv, &cbdata, list_id, true);
253 if (buffer == NULL) {
254 (*local_id)++;
255 response_id++;
256 continue;
257 }
258
259 if (buffer->inspect_len >= mpm_ctx->minlen) {
260 (void)mpm_table[mpm_ctx->mpm_type].Search(
261 mpm_ctx, &det_ctx->mtc, &det_ctx->pmq, buffer->inspect, buffer->inspect_len);
263 }
264 (*local_id)++;
265 response_id++;
266 }
267}
268
269static uint8_t DetectEngineInspectCb(DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx,
270 const struct DetectEngineAppInspectionEngine_ *engine, const Signature *s, Flow *f,
271 uint8_t flags, void *alstate, void *txv, uint64_t tx_id)
272{
273 const DetectEngineTransforms *transforms = NULL;
274 if (!engine->mpm) {
275 transforms = engine->v2.transforms;
276 }
277
278 uint32_t local_id = 0;
279 uint8_t ret_match = DETECT_ENGINE_INSPECT_SIG_NO_MATCH;
280
281 /* loop through each possible DNS response section */
282 for (enum DnsResponseSection section = DNS_RESPONSE_QUERY;
284 section++) {
285
286 /* check each record in section inspecting "name" and "rdata" */
287 ret_match = CheckSectionRecords(
288 de_ctx, det_ctx, engine, s, f, flags, txv, transforms, &local_id, section);
289 }
290 return ret_match;
291}
292
293static void DetectDnsResponsePrefilterTx(DetectEngineThreadCtx *det_ctx, const void *pectx,
294 Packet *p, Flow *f, void *txv, const uint64_t idx, const AppLayerTxData *_txd,
295 const uint8_t flags)
296{
297 SCEnter();
298
299 uint32_t local_id = 0;
300 /* loop through each possible DNS response section */
301 for (enum DnsResponseSection section = DNS_RESPONSE_QUERY; section < DNS_RESPONSE_MAX;
302 section++) {
303 /* check each record in section inspecting "name" and "rdata" */
304 CheckSectionRecordsPrefilter(det_ctx, pectx, txv, flags, &local_id, section);
305 }
306}
307
308static void DetectDnsResponsePrefilterMpmFree(void *ptr)
309{
310 SCFree(ptr);
311}
312
313static int DetectDnsResponsePrefilterMpmRegister(DetectEngineCtx *de_ctx, SigGroupHead *sgh,
314 MpmCtx *mpm_ctx, const DetectBufferMpmRegistry *mpm_reg, int list_id)
315{
316 PrefilterMpm *pectx = SCCalloc(1, sizeof(*pectx));
317 if (pectx == NULL) {
318 return -1;
319 }
320 pectx->list_id = list_id;
321 pectx->mpm_ctx = mpm_ctx;
322 pectx->transforms = &mpm_reg->transforms;
323
324 return PrefilterAppendTxEngine(de_ctx, sgh, DetectDnsResponsePrefilterTx,
325 mpm_reg->app_v2.alproto, mpm_reg->app_v2.tx_min_progress, pectx,
326 DetectDnsResponsePrefilterMpmFree, mpm_reg->pname);
327}
328
329static void SCDetectMdnsResponseRrnameRegister(void)
330{
331 static const char *keyword = "mdns.response.rrname";
332 int keyword_id = SCDetectHelperNewKeywordId();
333 sigmatch_table[keyword_id].name = keyword;
334 sigmatch_table[keyword_id].desc = "mDNS response rrname buffer";
335 sigmatch_table[keyword_id].url = "/rules/mdns-keywords.html#mdns-response-rrname";
336 sigmatch_table[keyword_id].Setup = MdnsDetectSetup;
337 sigmatch_table[keyword_id].flags |= SIGMATCH_NOOPT;
339
340 /* Register in the TO_SERVER direction, as all mDNS is toserver. */
342 keyword, ALPROTO_MDNS, SIG_FLAG_TOSERVER, 1, DetectEngineInspectCb, NULL);
343 DetectAppLayerMpmRegister(keyword, SIG_FLAG_TOSERVER, 2, DetectDnsResponsePrefilterMpmRegister,
344 NULL, ALPROTO_MDNS, 1);
345
346 DetectBufferTypeSetDescriptionByName(keyword, "mdns response rdata");
348
349 mdns_detect_buffer_id = DetectBufferTypeGetByName(keyword);
350}
351
353{
354 static const char *keyword = "dns.response.rrname";
356 sigmatch_table[DETECT_DNS_RESPONSE].desc = "DNS response sticky buffer";
357 sigmatch_table[DETECT_DNS_RESPONSE].url = "/rules/dns-keywords.html#dns-response-rrname";
361
362 /* Register in the TO_CLIENT direction. */
364 keyword, ALPROTO_DNS, SIG_FLAG_TOCLIENT, 1, DetectEngineInspectCb, NULL);
365 DetectAppLayerMpmRegister(keyword, SIG_FLAG_TOCLIENT, 2, DetectDnsResponsePrefilterMpmRegister,
366 NULL, ALPROTO_DNS, 1);
367
368 DetectBufferTypeSetDescriptionByName(keyword, "dns response rrname");
370
371 detect_buffer_id = DetectBufferTypeGetByName(keyword);
372
373 SCDetectMdnsResponseRrnameRegister();
374}
struct AppLayerTxData AppLayerTxData
@ ALPROTO_MDNS
@ ALPROTO_DNS
uint8_t flags
Definition decode-gre.h:0
void DetectDnsResponseRegister(void)
DnsResponseSection
@ DNS_RESPONSE_MAX
@ DNS_RESPONSE_ADDITIONAL
@ DNS_RESPONSE_ANSWER
@ DNS_RESPONSE_AUTHORITY
@ DNS_RESPONSE_QUERY
int SCDetectBufferSetActiveList(DetectEngineCtx *de_ctx, Signature *s, const int list)
bool DetectEngineContentInspectionBuffer(DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx, const Signature *s, const SigMatchData *smd, Packet *p, Flow *f, const InspectionBuffer *b, const enum DetectContentInspectionType inspection_mode)
wrapper around DetectEngineContentInspectionInternal to return true/false only
@ DETECT_ENGINE_CONTENT_INSPECTION_MODE_STATE
#define DETECT_CI_FLAGS_SINGLE
int SCDetectHelperNewKeywordId(void)
void InspectionBufferSetupMultiEmpty(InspectionBuffer *buffer)
setup the buffer empty
InspectionBuffer * InspectionBufferMultipleForListGet(DetectEngineThreadCtx *det_ctx, const int list_id, const uint32_t local_id)
for a InspectionBufferMultipleForList get a InspectionBuffer
void InspectionBufferSetupMulti(DetectEngineThreadCtx *det_ctx, InspectionBuffer *buffer, const DetectEngineTransforms *transforms, const uint8_t *data, const uint32_t data_len)
setup the buffer with our initial data
void DetectAppLayerMpmRegister(const char *name, int direction, int priority, PrefilterRegisterFunc PrefilterRegister, InspectionBufferGetDataPtr GetData, AppProto alproto, int tx_min_progress)
register an app layer keyword for mpm
int PrefilterAppendTxEngine(DetectEngineCtx *de_ctx, SigGroupHead *sgh, PrefilterTxFn PrefilterTxFunc, AppProto alproto, int tx_min_progress, void *pectx, void(*FreeFunc)(void *pectx), const char *name)
@ DETECT_DNS_RESPONSE
#define DETECT_ENGINE_INSPECT_SIG_MATCH
#define DETECT_ENGINE_INSPECT_SIG_NO_MATCH
void DetectBufferTypeSetDescriptionByName(const char *name, const char *desc)
void DetectBufferTypeSupportsMultiInstance(const char *name)
void DetectAppLayerInspectEngineRegister(const char *name, AppProto alproto, uint32_t dir, int progress, InspectEngineFuncPtr Callback, InspectionBufferGetDataPtr GetData)
Registers an app inspection engine.
int DetectBufferTypeGetByName(const char *name)
int SCDetectSignatureSetAppProto(Signature *s, AppProto alproto)
SigTableElmt * sigmatch_table
#define SIGMATCH_NOOPT
Definition detect.h:1651
#define SIG_FLAG_TOCLIENT
Definition detect.h:272
#define SIGMATCH_INFO_STICKY_BUFFER
Definition detect.h:1676
#define SIG_FLAG_TOSERVER
Definition detect.h:271
DetectEngineCtx * de_ctx
struct Thresholds ctx
one time registration of keywords at start up
Definition detect.h:762
DetectEngineTransforms transforms
Definition detect.h:775
struct DetectBufferMpmRegistry_::@98::@100 app_v2
const DetectEngineTransforms * transforms
Definition detect.h:436
struct DetectEngineAppInspectionEngine_::@90 v2
main detection engine ctx
Definition detect.h:932
MpmThreadCtx mtc
Definition detect.h:1345
PrefilterRuleStore pmq
Definition detect.h:1349
enum DnsResponseSection response_section
Flow data structure.
Definition flow.h:356
uint8_t mpm_type
Definition util-mpm.h:95
uint16_t minlen
Definition util-mpm.h:104
uint32_t(* Search)(const struct MpmCtx_ *, struct MpmThreadCtx_ *, PrefilterRuleStore *, const uint8_t *, uint32_t)
Definition util-mpm.h:178
const DetectEngineTransforms * transforms
const MpmCtx * mpm_ctx
Container for matching data for a signature group.
Definition detect.h:1629
const char * url
Definition detect.h:1462
int(* Setup)(DetectEngineCtx *, Signature *, const char *)
Definition detect.h:1441
uint16_t flags
Definition detect.h:1450
const char * desc
Definition detect.h:1461
const char * name
Definition detect.h:1459
Signature container.
Definition detect.h:668
#define str(s)
#define SCEnter(...)
Definition util-debug.h:277
#define SCFree(p)
Definition util-mem.h:61
#define SCCalloc(nm, sz)
Definition util-mem.h:53
MpmTableElmt mpm_table[MPM_TABLE_SIZE]
Definition util-mpm.c:47
#define PREFILTER_PROFILING_ADD_BYTES(det_ctx, bytes)