suricata
detect-dns-query.c
Go to the documentation of this file.
1 /* Copyright (C) 2013-2018 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  * \ingroup dnslayer
20  *
21  * @{
22  */
23 
24 
25 /**
26  * \file
27  *
28  * \author Victor Julien <victor@inliniac.net>
29  */
30 
31 #include "suricata-common.h"
32 #include "threads.h"
33 #include "decode.h"
34 #include "detect.h"
35 
36 #include "detect-parse.h"
37 #include "detect-engine.h"
38 #include "detect-engine-build.h"
39 #include "detect-engine-mpm.h"
42 #include "detect-content.h"
43 #include "detect-pcre.h"
44 
45 #include "flow.h"
46 #include "flow-util.h"
47 #include "flow-var.h"
48 
49 #include "util-debug.h"
50 #include "util-unittest.h"
51 #include "util-spm.h"
52 #include "util-print.h"
53 
54 #include "stream-tcp.h"
55 
56 #include "app-layer.h"
57 #include "app-layer-parser.h"
58 #include "detect-dns-query.h"
59 
60 #include "util-profiling.h"
61 #include "util-unittest-helper.h"
62 #include "rust.h"
63 
64 static int DetectDnsQuerySetup (DetectEngineCtx *, Signature *, const char *);
65 #ifdef UNITTESTS
66 static void DetectDnsQueryRegisterTests(void);
67 #endif
68 static int g_dns_query_buffer_id = 0;
69 
71  uint32_t local_id; /**< used as index into thread inspect array */
72  void *txv;
73 };
74 
75 static InspectionBuffer *DnsQueryGetData(DetectEngineThreadCtx *det_ctx,
76  const DetectEngineTransforms *transforms, Flow *f, struct DnsQueryGetDataArgs *cbdata,
77  int list_id)
78 {
79  SCEnter();
80 
81  InspectionBuffer *buffer =
82  InspectionBufferMultipleForListGet(det_ctx, list_id, cbdata->local_id);
83  if (buffer == NULL)
84  return NULL;
85  if (buffer->initialized)
86  return buffer;
87 
88  const uint8_t *data;
89  uint32_t data_len;
90  if (rs_dns_tx_get_query_name(cbdata->txv, cbdata->local_id, &data, &data_len) == 0) {
92  return NULL;
93  }
94  InspectionBufferSetupMulti(buffer, transforms, data, data_len);
95 
96  SCReturnPtr(buffer, "InspectionBuffer");
97 }
98 
99 static uint8_t DetectEngineInspectDnsQuery(DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx,
100  const DetectEngineAppInspectionEngine *engine, const Signature *s, Flow *f, uint8_t flags,
101  void *alstate, void *txv, uint64_t tx_id)
102 {
103  uint32_t local_id = 0;
104 
105  const DetectEngineTransforms *transforms = NULL;
106  if (!engine->mpm) {
107  transforms = engine->v2.transforms;
108  }
109 
110  while(1) {
111  struct DnsQueryGetDataArgs cbdata = { local_id, txv, };
112  InspectionBuffer *buffer =
113  DnsQueryGetData(det_ctx, transforms, f, &cbdata, engine->sm_list);
114  if (buffer == NULL || buffer->inspect == NULL)
115  break;
116 
117  det_ctx->buffer_offset = 0;
118  det_ctx->discontinue_matching = 0;
119  det_ctx->inspection_recursion_counter = 0;
120 
121  const int match = DetectEngineContentInspection(de_ctx, det_ctx, s, engine->smd,
122  NULL, f,
123  (uint8_t *)buffer->inspect,
124  buffer->inspect_len,
127  if (match == 1) {
129  }
130  local_id++;
131  }
133 }
134 
135 typedef struct PrefilterMpmDnsQuery {
136  int list_id;
137  const MpmCtx *mpm_ctx;
140 
141 /** \brief DnsQuery DnsQuery Mpm prefilter callback
142  *
143  * \param det_ctx detection engine thread ctx
144  * \param p packet to inspect
145  * \param f flow to inspect
146  * \param txv tx to inspect
147  * \param pectx inspection context
148  */
149 static void PrefilterTxDnsQuery(DetectEngineThreadCtx *det_ctx, const void *pectx, Packet *p,
150  Flow *f, void *txv, const uint64_t idx, const AppLayerTxData *_txd, const uint8_t flags)
151 {
152  SCEnter();
153 
154  const PrefilterMpmDnsQuery *ctx = (const PrefilterMpmDnsQuery *)pectx;
155  const MpmCtx *mpm_ctx = ctx->mpm_ctx;
156  const int list_id = ctx->list_id;
157 
158  uint32_t local_id = 0;
159  while(1) {
160  // loop until we get a NULL
161 
162  struct DnsQueryGetDataArgs cbdata = { local_id, txv };
163  InspectionBuffer *buffer = DnsQueryGetData(det_ctx, ctx->transforms, f, &cbdata, list_id);
164  if (buffer == NULL)
165  break;
166 
167  if (buffer->inspect_len >= mpm_ctx->minlen) {
168  (void)mpm_table[mpm_ctx->mpm_type].Search(mpm_ctx,
169  &det_ctx->mtcu, &det_ctx->pmq,
170  buffer->inspect, buffer->inspect_len);
171  PREFILTER_PROFILING_ADD_BYTES(det_ctx, buffer->inspect_len);
172  }
173 
174  local_id++;
175  }
176 }
177 
178 static void PrefilterMpmDnsQueryFree(void *ptr)
179 {
180  SCFree(ptr);
181 }
182 
183 static int PrefilterMpmDnsQueryRegister(DetectEngineCtx *de_ctx, SigGroupHead *sgh, MpmCtx *mpm_ctx,
184  const DetectBufferMpmRegistry *mpm_reg, int list_id)
185 {
186  PrefilterMpmDnsQuery *pectx = SCCalloc(1, sizeof(*pectx));
187  if (pectx == NULL)
188  return -1;
189  pectx->list_id = list_id;
190  pectx->mpm_ctx = mpm_ctx;
191  pectx->transforms = &mpm_reg->transforms;
192 
193  return PrefilterAppendTxEngine(de_ctx, sgh, PrefilterTxDnsQuery,
194  mpm_reg->app_v2.alproto, mpm_reg->app_v2.tx_min_progress,
195  pectx, PrefilterMpmDnsQueryFree, mpm_reg->pname);
196 }
197 
198 /**
199  * \brief Registration function for keyword: dns_query
200  */
202 {
203  sigmatch_table[DETECT_AL_DNS_QUERY].name = "dns.query";
205  sigmatch_table[DETECT_AL_DNS_QUERY].desc = "sticky buffer to match DNS query-buffer";
206  sigmatch_table[DETECT_AL_DNS_QUERY].url = "/rules/dns-keywords.html#dns-query";
207  sigmatch_table[DETECT_AL_DNS_QUERY].Setup = DetectDnsQuerySetup;
208 #ifdef UNITTESTS
209  sigmatch_table[DETECT_AL_DNS_QUERY].RegisterTests = DetectDnsQueryRegisterTests;
210 #endif
213 
215  PrefilterMpmDnsQueryRegister, NULL,
216  ALPROTO_DNS, 1);
217 
220  DetectEngineInspectDnsQuery, NULL);
221 
223  "dns request query");
225 
226  g_dns_query_buffer_id = DetectBufferTypeGetByName("dns_query");
227 
228 #ifdef HAVE_LUA
229  /* register these generic engines from here for now */
234 
236  "dns requests");
238  "dns responses");
239 #endif
240 }
241 
242 
243 /**
244  * \brief setup the dns_query sticky buffer keyword used in the rule
245  *
246  * \param de_ctx Pointer to the Detection Engine Context
247  * \param s Pointer to the Signature to which the current keyword belongs
248  * \param str Should hold an empty string always
249  *
250  * \retval 0 On success
251  * \retval -1 On failure
252  */
253 
254 static int DetectDnsQuerySetup(DetectEngineCtx *de_ctx, Signature *s, const char *str)
255 {
256  if (DetectBufferSetActiveList(de_ctx, s, g_dns_query_buffer_id) < 0)
257  return -1;
259  return -1;
260  return 0;
261 }
262 
263 #ifdef UNITTESTS
264 #include "detect-isdataat.h"
265 #include "detect-engine-alert.h"
266 
267 /** \test simple google.com query matching */
268 static int DetectDnsQueryTest01(void)
269 {
270  /* google.com */
271  uint8_t buf[] = { 0x10, 0x32, 0x01, 0x00, 0x00, 0x01,
272  0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
273  0x06, 0x67, 0x6F, 0x6F, 0x67, 0x6C,
274  0x65, 0x03, 0x63, 0x6F, 0x6D, 0x00,
275  0x00, 0x10, 0x00, 0x01, };
276  Flow f;
277  void *dns_state = NULL;
278  Packet *p = NULL;
279  Signature *s = NULL;
280  ThreadVars tv;
281  DetectEngineThreadCtx *det_ctx = NULL;
283 
284  memset(&tv, 0, sizeof(ThreadVars));
285  memset(&f, 0, sizeof(Flow));
286 
287  p = UTHBuildPacketReal(buf, sizeof(buf), IPPROTO_UDP,
288  "192.168.1.5", "192.168.1.1",
289  41424, 53);
290 
291  FLOW_INITIALIZE(&f);
292  f.flags |= FLOW_IPV4;
293  f.proto = IPPROTO_UDP;
295 
296  p->flow = &f;
297  p->flags |= PKT_HAS_FLOW;
299  f.alproto = ALPROTO_DNS;
300 
304  de_ctx->flags |= DE_QUIET;
305 
306  s = DetectEngineAppendSig(de_ctx, "alert dns any any -> any any "
307  "(msg:\"Test dns_query option\"; "
308  "dns_query; content:\"google\"; nocase; sid:1;)");
309  FAIL_IF_NULL(s);
310 
312  DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
313 
314  int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_DNS,
315  STREAM_TOSERVER, buf, sizeof(buf));
316  if (r != 0) {
317  printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
318  FAIL;
319  }
320 
321  dns_state = f.alstate;
322  FAIL_IF_NULL(dns_state);
323 
324  /* do detect */
325  SigMatchSignatures(&tv, de_ctx, det_ctx, p);
326 
327  if (!(PacketAlertCheck(p, 1))) {
328  printf("sig 1 didn't alert, but it should have: ");
329  FAIL;
330  }
331 
332  if (alp_tctx != NULL)
334  if (det_ctx != NULL)
335  DetectEngineThreadCtxDeinit(&tv, det_ctx);
336  if (de_ctx != NULL)
338  if (de_ctx != NULL)
340 
341  FLOW_DESTROY(&f);
342  UTHFreePacket(p);
343  PASS;
344 }
345 
346 /** \test multi tx google.(com|net) query matching */
347 static int DetectDnsQueryTest02(void)
348 {
349  /* google.com */
350  uint8_t buf1[] = { 0x10, 0x32, 0x01, 0x00, 0x00, 0x01,
351  0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
352  0x06, 0x67, 0x6F, 0x6F, 0x67, 0x6C,
353  0x65, 0x03, 0x63, 0x6F, 0x6D, 0x00,
354  0x00, 0x01, 0x00, 0x01, };
355 
356  uint8_t buf2[] = { 0x10, 0x32, /* tx id */
357  0x81, 0x80, /* flags: resp, recursion desired, recursion available */
358  0x00, 0x01, /* 1 query */
359  0x00, 0x01, /* 1 answer */
360  0x00, 0x00, 0x00, 0x00, /* no auth rr, additional rr */
361  /* query record */
362  0x06, 0x67, 0x6F, 0x6F, 0x67, 0x6C, /* name */
363  0x65, 0x03, 0x63, 0x6F, 0x6D, 0x00, /* name cont */
364  0x00, 0x01, 0x00, 0x01, /* type a, class in */
365  /* answer */
366  0xc0, 0x0c, /* ref to name in query above */
367  0x00, 0x01, 0x00, 0x01, /* type a, class in */
368  0x00, 0x01, 0x40, 0xef, /* ttl */
369  0x00, 0x04, /* data len */
370  0x01, 0x02, 0x03, 0x04 }; /* addr */
371 
372  /* google.net */
373  uint8_t buf3[] = { 0x11, 0x33, 0x01, 0x00, 0x00, 0x01,
374  0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
375  0x06, 0x67, 0x6F, 0x6F, 0x67, 0x6C,
376  0x65, 0x03, 0x6E, 0x65, 0x74, 0x00,
377  0x00, 0x10, 0x00, 0x01, };
378  Flow f;
379  void *dns_state = NULL;
380  Packet *p1 = NULL, *p2 = NULL, *p3 = NULL;
381  Signature *s = NULL;
382  ThreadVars tv;
383  DetectEngineThreadCtx *det_ctx = NULL;
385 
386  memset(&tv, 0, sizeof(ThreadVars));
387  memset(&f, 0, sizeof(Flow));
388 
389  p1 = UTHBuildPacketReal(buf1, sizeof(buf1), IPPROTO_UDP,
390  "192.168.1.5", "192.168.1.1",
391  41424, 53);
392  p2 = UTHBuildPacketReal(buf1, sizeof(buf1), IPPROTO_UDP,
393  "192.168.1.5", "192.168.1.1",
394  41424, 53);
395  p3 = UTHBuildPacketReal(buf1, sizeof(buf1), IPPROTO_UDP,
396  "192.168.1.5", "192.168.1.1",
397  41424, 53);
398 
399  FLOW_INITIALIZE(&f);
400  f.flags |= FLOW_IPV4;
401  f.proto = IPPROTO_UDP;
403  f.alproto = ALPROTO_DNS;
404 
405  p1->flow = &f;
406  p1->flags |= PKT_HAS_FLOW;
408  p1->pcap_cnt = 1;
409 
410  p2->flow = &f;
411  p2->flags |= PKT_HAS_FLOW;
412  p2->flowflags |= FLOW_PKT_TOCLIENT;
413  p2->pcap_cnt = 2;
414 
415  p3->flow = &f;
416  p3->flags |= PKT_HAS_FLOW;
417  p3->flowflags |= FLOW_PKT_TOSERVER;
418  p3->pcap_cnt = 3;
419 
423  de_ctx->flags |= DE_QUIET;
424 
425  s = DetectEngineAppendSig(de_ctx, "alert dns any any -> any any "
426  "(msg:\"Test dns_query option\"; "
427  "dns_query; content:\"google.com\"; nocase; sid:1;)");
428  FAIL_IF_NULL(s);
429  s = DetectEngineAppendSig(de_ctx, "alert dns any any -> any any "
430  "(msg:\"Test dns_query option\"; "
431  "dns_query; content:\"google.net\"; nocase; sid:2;)");
432  FAIL_IF_NULL(s);
433 
435  DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
436 
437  int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_DNS,
438  STREAM_TOSERVER, buf1, sizeof(buf1));
439  if (r != 0) {
440  printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
441  FAIL;
442  }
443 
444  dns_state = f.alstate;
445  FAIL_IF_NULL(dns_state);
446 
447  /* do detect */
448  SigMatchSignatures(&tv, de_ctx, det_ctx, p1);
449 
450  if (!(PacketAlertCheck(p1, 1))) {
451  printf("(p1) sig 1 didn't alert, but it should have: ");
452  FAIL;
453  }
454  if (PacketAlertCheck(p1, 2)) {
455  printf("(p1) sig 2 did alert, but it should not have: ");
456  FAIL;
457  }
458 
459  r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_DNS, STREAM_TOCLIENT,
460  buf2, sizeof(buf2));
461  if (r != 0) {
462  printf("toserver client 1 returned %" PRId32 ", expected 0: ", r);
463  FAIL;
464  }
465 
466  /* do detect */
467  SigMatchSignatures(&tv, de_ctx, det_ctx, p2);
468 
469  if (PacketAlertCheck(p2, 1)) {
470  printf("(p2) sig 1 alerted, but it should not have: ");
471  FAIL;
472  }
473  if (PacketAlertCheck(p2, 2)) {
474  printf("(p2) sig 2 alerted, but it should not have: ");
475  FAIL;
476  }
477 
478  r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_DNS, STREAM_TOSERVER,
479  buf3, sizeof(buf3));
480  if (r != 0) {
481  printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
482  FAIL;
483  }
484 
485  /* do detect */
486  SigMatchSignatures(&tv, de_ctx, det_ctx, p3);
487 
488  if (PacketAlertCheck(p3, 1)) {
489  printf("(p3) sig 1 alerted, but it should not have: ");
490  FAIL;
491  }
492  if (!(PacketAlertCheck(p3, 2))) {
493  printf("(p3) sig 2 didn't alert, but it should have: ");
494  FAIL;
495  }
496 
497  if (alp_tctx != NULL)
499  if (det_ctx != NULL)
500  DetectEngineThreadCtxDeinit(&tv, det_ctx);
501  if (de_ctx != NULL)
503  if (de_ctx != NULL)
505 
506  FLOW_DESTROY(&f);
507  UTHFreePacket(p1);
508  UTHFreePacket(p2);
509  UTHFreePacket(p3);
510  PASS;
511 }
512 
513 /** \test simple google.com query matching (TCP) */
514 static int DetectDnsQueryTest03(void)
515 {
516  /* google.com */
517  uint8_t buf[] = { 0x00, 28,
518  0x10, 0x32, 0x01, 0x00, 0x00, 0x01,
519  0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
520  0x06, 0x67, 0x6F, 0x6F, 0x67, 0x6C,
521  0x65, 0x03, 0x63, 0x6F, 0x6D, 0x00,
522  0x00, 0x10, 0x00, 0x01, };
523  Flow f;
524  void *dns_state = NULL;
525  Packet *p = NULL;
526  Signature *s = NULL;
527  ThreadVars tv;
528  DetectEngineThreadCtx *det_ctx = NULL;
529  TcpSession ssn;
531 
532  memset(&tv, 0, sizeof(ThreadVars));
533  memset(&f, 0, sizeof(Flow));
534  memset(&ssn, 0, sizeof(TcpSession));
535 
536  p = UTHBuildPacketReal(buf, sizeof(buf), IPPROTO_TCP,
537  "192.168.1.5", "192.168.1.1",
538  41424, 53);
539 
540  FLOW_INITIALIZE(&f);
541  f.protoctx = (void *)&ssn;
542  f.flags |= FLOW_IPV4;
543  f.proto = IPPROTO_TCP;
545 
546  p->flow = &f;
549  f.alproto = ALPROTO_DNS;
550 
551  StreamTcpInitConfig(true);
552 
556  de_ctx->flags |= DE_QUIET;
557 
558  s = DetectEngineAppendSig(de_ctx, "alert dns any any -> any any "
559  "(msg:\"Test dns_query option\"; "
560  "dns_query; content:\"google\"; nocase; sid:1;)");
561  FAIL_IF_NULL(s);
562 
564  DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
565 
566  int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_DNS,
567  STREAM_TOSERVER, buf, sizeof(buf));
568  if (r != 0) {
569  printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
570  FAIL;
571  }
572 
573  dns_state = f.alstate;
574  FAIL_IF_NULL(dns_state);
575 
576  /* do detect */
577  SigMatchSignatures(&tv, de_ctx, det_ctx, p);
578 
579  if (!(PacketAlertCheck(p, 1))) {
580  printf("sig 1 didn't alert, but it should have: ");
581  FAIL;
582  }
583 
584  if (alp_tctx != NULL)
586  if (det_ctx != NULL)
587  DetectEngineThreadCtxDeinit(&tv, det_ctx);
588  if (de_ctx != NULL)
590  if (de_ctx != NULL)
592 
593  StreamTcpFreeConfig(true);
594  FLOW_DESTROY(&f);
595  UTHFreePacket(p);
596  PASS;
597 }
598 
599 
600 /** \test simple google.com query matching, pcre */
601 static int DetectDnsQueryTest04(void)
602 {
603  /* google.com */
604  uint8_t buf[] = { 0x10, 0x32, 0x01, 0x00, 0x00, 0x01,
605  0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
606  0x06, 0x67, 0x6F, 0x6F, 0x67, 0x6C,
607  0x65, 0x03, 0x63, 0x6F, 0x6D, 0x00,
608  0x00, 0x10, 0x00, 0x01, };
609  Flow f;
610  void *dns_state = NULL;
611  Packet *p = NULL;
612  Signature *s = NULL;
613  ThreadVars tv;
614  DetectEngineThreadCtx *det_ctx = NULL;
616 
617  memset(&tv, 0, sizeof(ThreadVars));
618  memset(&f, 0, sizeof(Flow));
619 
620  p = UTHBuildPacketReal(buf, sizeof(buf), IPPROTO_UDP,
621  "192.168.1.5", "192.168.1.1",
622  41424, 53);
623 
624  FLOW_INITIALIZE(&f);
625  f.flags |= FLOW_IPV4;
626  f.proto = IPPROTO_UDP;
628 
629  p->flow = &f;
630  p->flags |= PKT_HAS_FLOW;
632  f.alproto = ALPROTO_DNS;
633 
637  de_ctx->flags |= DE_QUIET;
638 
639  s = DetectEngineAppendSig(de_ctx, "alert dns any any -> any any "
640  "(msg:\"Test dns_query option\"; "
641  "dns_query; content:\"google\"; nocase; "
642  "pcre:\"/google\\.com$/i\"; sid:1;)");
643  FAIL_IF_NULL(s);
644  s = DetectEngineAppendSig(de_ctx, "alert dns any any -> any any "
645  "(msg:\"Test dns_query option\"; "
646  "dns_query; content:\"google\"; nocase; "
647  "pcre:\"/^\\.[a-z]{2,3}$/iR\"; sid:2;)");
648  FAIL_IF_NULL(s);
649 
651  DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
652 
653  int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_DNS,
654  STREAM_TOSERVER, buf, sizeof(buf));
655  if (r != 0) {
656  printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
657  FAIL;
658  }
659 
660  dns_state = f.alstate;
661  FAIL_IF_NULL(dns_state);
662 
663  /* do detect */
664  SigMatchSignatures(&tv, de_ctx, det_ctx, p);
665 
666  if (!(PacketAlertCheck(p, 1))) {
667  printf("sig 1 didn't alert, but it should have: ");
668  FAIL;
669  }
670  if (!(PacketAlertCheck(p, 2))) {
671  printf("sig 2 didn't alert, but it should have: ");
672  FAIL;
673  }
674 
675  if (alp_tctx != NULL)
677  if (det_ctx != NULL)
678  DetectEngineThreadCtxDeinit(&tv, det_ctx);
679  if (de_ctx != NULL)
681  if (de_ctx != NULL)
683 
684  FLOW_DESTROY(&f);
685  UTHFreePacket(p);
686  PASS;
687 }
688 
689 /** \test multi tx google.(com|net) query matching +
690  * app layer event */
691 static int DetectDnsQueryTest05(void)
692 {
693  /* google.com */
694  uint8_t buf1[] = { 0x10, 0x32, 0x01, 0x00, 0x00, 0x01,
695  0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
696  0x06, 0x67, 0x6F, 0x6F, 0x67, 0x6C,
697  0x65, 0x03, 0x63, 0x6F, 0x6D, 0x00,
698  0x00, 0x01, 0x00, 0x01, };
699 
700  uint8_t buf2[] = { 0x10, 0x32, /* tx id */
701  0x81, 0x80|0x40, /* flags: resp, recursion desired, recursion available */
702  0x00, 0x01, /* 1 query */
703  0x00, 0x01, /* 1 answer */
704  0x00, 0x00, 0x00, 0x00, /* no auth rr, additional rr */
705  /* query record */
706  0x06, 0x67, 0x6F, 0x6F, 0x67, 0x6C, /* name */
707  0x65, 0x03, 0x63, 0x6F, 0x6D, 0x00, /* name cont */
708  0x00, 0x01, 0x00, 0x01, /* type a, class in */
709  /* answer */
710  0xc0, 0x0c, /* ref to name in query above */
711  0x00, 0x01, 0x00, 0x01, /* type a, class in */
712  0x00, 0x01, 0x40, 0xef, /* ttl */
713  0x00, 0x04, /* data len */
714  0x01, 0x02, 0x03, 0x04 }; /* addr */
715 
716  /* google.net */
717  uint8_t buf3[] = { 0x11, 0x33, 0x01, 0x00, 0x00, 0x01,
718  0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
719  0x06, 0x67, 0x6F, 0x6F, 0x67, 0x6C,
720  0x65, 0x03, 0x6E, 0x65, 0x74, 0x00,
721  0x00, 0x10, 0x00, 0x01, };
722  Flow f;
723  void *dns_state = NULL;
724  Packet *p1 = NULL, *p2 = NULL, *p3 = NULL;
725  Signature *s = NULL;
726  ThreadVars tv;
727  DetectEngineThreadCtx *det_ctx = NULL;
729 
730  memset(&tv, 0, sizeof(ThreadVars));
731  memset(&f, 0, sizeof(Flow));
732 
733  p1 = UTHBuildPacketReal(buf1, sizeof(buf1), IPPROTO_UDP,
734  "192.168.1.5", "192.168.1.1",
735  41424, 53);
736  p2 = UTHBuildPacketReal(buf2, sizeof(buf2), IPPROTO_UDP,
737  "192.168.1.5", "192.168.1.1",
738  41424, 53);
739  p3 = UTHBuildPacketReal(buf3, sizeof(buf3), IPPROTO_UDP,
740  "192.168.1.5", "192.168.1.1",
741  41424, 53);
742 
743  FLOW_INITIALIZE(&f);
744  f.flags |= FLOW_IPV4;
745  f.proto = IPPROTO_UDP;
747  f.alproto = ALPROTO_DNS;
748 
749  p1->flow = &f;
750  p1->flags |= PKT_HAS_FLOW;
752  p1->pcap_cnt = 1;
753 
754  p2->flow = &f;
755  p2->flags |= PKT_HAS_FLOW;
756  p2->flowflags |= FLOW_PKT_TOCLIENT;
757  p2->pcap_cnt = 2;
758 
759  p3->flow = &f;
760  p3->flags |= PKT_HAS_FLOW;
761  p3->flowflags |= FLOW_PKT_TOSERVER;
762  p3->pcap_cnt = 3;
763 
767  de_ctx->flags |= DE_QUIET;
768 
769  s = DetectEngineAppendSig(de_ctx, "alert dns any any -> any any "
770  "(msg:\"Test dns_query option\"; "
771  "dns_query; content:\"google.com\"; nocase; sid:1;)");
772  FAIL_IF_NULL(s);
773  s = DetectEngineAppendSig(de_ctx, "alert dns any any -> any any "
774  "(msg:\"Test dns_query option\"; "
775  "dns_query; content:\"google.net\"; nocase; sid:2;)");
776  FAIL_IF_NULL(s);
777  s = DetectEngineAppendSig(de_ctx, "alert dns any any -> any any "
778  "(msg:\"Test Z flag event\"; "
779  "app-layer-event:dns.z_flag_set; sid:3;)");
780  FAIL_IF_NULL(s);
781 
783  DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
784 
785  int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_DNS,
786  STREAM_TOSERVER, buf1, sizeof(buf1));
787  if (r != 0) {
788  printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
789  FAIL;
790  }
791 
792  dns_state = f.alstate;
793  FAIL_IF_NULL(dns_state);
794 
795  /* do detect */
796  SigMatchSignatures(&tv, de_ctx, det_ctx, p1);
797 
798  if (!(PacketAlertCheck(p1, 1))) {
799  printf("(p1) sig 1 didn't alert, but it should have: ");
800  FAIL;
801  }
802  if (PacketAlertCheck(p1, 2)) {
803  printf("(p1) sig 2 did alert, but it should not have: ");
804  FAIL;
805  }
806 
807  r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_DNS, STREAM_TOCLIENT,
808  buf2, sizeof(buf2));
809  if (r != 0) {
810  printf("toserver client 1 returned %" PRId32 ", expected 0\n", r);
811  FAIL;
812  }
813 
814  /* do detect */
815  SigMatchSignatures(&tv, de_ctx, det_ctx, p2);
816 
817  if (PacketAlertCheck(p2, 1)) {
818  printf("(p2) sig 1 alerted, but it should not have: ");
819  FAIL;
820  }
821  if (PacketAlertCheck(p2, 2)) {
822  printf("(p2) sig 2 alerted, but it should not have: ");
823  FAIL;
824  }
825  if (!(PacketAlertCheck(p2, 3))) {
826  printf("(p2) sig 3 didn't alert, but it should have: ");
827  FAIL;
828  }
829 
830  r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_DNS, STREAM_TOSERVER,
831  buf3, sizeof(buf3));
832  if (r != 0) {
833  printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
834  FAIL;
835  }
836 
837  /* do detect */
838  SigMatchSignatures(&tv, de_ctx, det_ctx, p3);
839 
840  if (PacketAlertCheck(p3, 1)) {
841  printf("(p3) sig 1 alerted, but it should not have: ");
842  FAIL;
843  }
844  if (!(PacketAlertCheck(p3, 2))) {
845  printf("(p3) sig 2 didn't alert, but it should have: ");
846  FAIL;
847  }
848  /** \todo should not alert, bug #839
849  if (PacketAlertCheck(p3, 3)) {
850  printf("(p3) sig 3 did alert, but it should not have: ");
851  goto end;
852  }
853  */
854 
855  if (alp_tctx != NULL)
857  if (det_ctx != NULL)
858  DetectEngineThreadCtxDeinit(&tv, det_ctx);
859  if (de_ctx != NULL)
861  if (de_ctx != NULL)
863 
864  FLOW_DESTROY(&f);
865  UTHFreePacket(p1);
866  UTHFreePacket(p2);
867  UTHFreePacket(p3);
868  PASS;
869 }
870 
871 static void DetectDnsQueryRegisterTests(void)
872 {
873  UtRegisterTest("DetectDnsQueryTest01", DetectDnsQueryTest01);
874  UtRegisterTest("DetectDnsQueryTest02", DetectDnsQueryTest02);
875  UtRegisterTest("DetectDnsQueryTest03 -- tcp", DetectDnsQueryTest03);
876  UtRegisterTest("DetectDnsQueryTest04 -- pcre", DetectDnsQueryTest04);
877  UtRegisterTest("DetectDnsQueryTest05 -- app layer event", DetectDnsQueryTest05);
878 }
879 #endif
DetectEngineAppInspectionEngine_
Definition: detect.h:418
SigTableElmt_::url
const char * url
Definition: detect.h:1288
DetectSignatureSetAppProto
int DetectSignatureSetAppProto(Signature *s, AppProto alproto)
Definition: detect-parse.c:1704
DetectEngineAppInspectionEngine_::mpm
bool mpm
Definition: detect.h:422
detect-content.h
MpmCtx_::mpm_type
uint8_t mpm_type
Definition: util-mpm.h:91
DetectEngineThreadCtx_::buffer_offset
uint32_t buffer_offset
Definition: detect.h:1103
detect-engine.h
DetectAppLayerMpmRegister2
void DetectAppLayerMpmRegister2(const char *name, int direction, int priority, PrefilterRegisterFunc PrefilterRegister, InspectionBufferGetDataPtr GetData, AppProto alproto, int tx_min_progress)
register a MPM engine
Definition: detect-engine-mpm.c:89
FAIL_IF_NULL
#define FAIL_IF_NULL(expr)
Fail a test if expression evaluates to NULL.
Definition: util-unittest.h:89
SIGMATCH_INFO_STICKY_BUFFER
#define SIGMATCH_INFO_STICKY_BUFFER
Definition: detect.h:1491
SigTableElmt_::desc
const char * desc
Definition: detect.h:1287
PKT_HAS_FLOW
#define PKT_HAS_FLOW
Definition: decode.h:1000
flow-util.h
ALPROTO_DNS
@ ALPROTO_DNS
Definition: app-layer-protos.h:41
SigTableElmt_::name
const char * name
Definition: detect.h:1285
InspectionBuffer::initialized
bool initialized
Definition: detect.h:369
stream-tcp.h
SigGroupHead_
Container for matching data for a signature group.
Definition: detect.h:1439
DetectEngineTransforms
Definition: detect.h:400
UtRegisterTest
void UtRegisterTest(const char *name, int(*TestFn)(void))
Register unit test.
Definition: util-unittest.c:103
DnsQueryGetDataArgs::local_id
uint32_t local_id
Definition: detect-dns-query.c:71
Packet_::pcap_cnt
uint64_t pcap_cnt
Definition: decode.h:598
detect-isdataat.h
Flow_::proto
uint8_t proto
Definition: flow.h:369
DetectBufferSetActiveList
int DetectBufferSetActiveList(DetectEngineCtx *de_ctx, Signature *s, const int list)
Definition: detect-engine.c:1387
PacketAlertCheck
int PacketAlertCheck(Packet *p, uint32_t sid)
Check if a certain sid alerted, this is used in the test functions.
Definition: detect-engine-alert.c:141
InspectionBuffer
Definition: detect.h:365
Packet_::flags
uint32_t flags
Definition: decode.h:467
DnsQueryGetDataArgs::txv
void * txv
Definition: detect-dns-query.c:72
threads.h
Flow_
Flow data structure.
Definition: flow.h:347
DetectEngineThreadCtx_::pmq
PrefilterRuleStore pmq
Definition: detect.h:1182
Flow_::protomap
uint8_t protomap
Definition: flow.h:441
SigTableElmt_::flags
uint16_t flags
Definition: detect.h:1279
DetectEngineCtx_
main detection engine ctx
Definition: detect.h:827
DetectBufferTypeSupportsMultiInstance
void DetectBufferTypeSupportsMultiInstance(const char *name)
Definition: detect-engine.c:1074
DetectEngineCtxFree
void DetectEngineCtxFree(DetectEngineCtx *)
Free a DetectEngineCtx::
Definition: detect-engine.c:2592
AppLayerParserThreadCtxFree
void AppLayerParserThreadCtxFree(AppLayerParserThreadCtx *tctx)
Destroys the app layer parser thread context obtained using AppLayerParserThreadCtxAlloc().
Definition: app-layer-parser.c:314
FLOW_PKT_TOSERVER
#define FLOW_PKT_TOSERVER
Definition: flow.h:221
rust.h
DetectBufferMpmRegistry_
one time registration of keywords at start up
Definition: detect.h:669
DE_QUIET
#define DE_QUIET
Definition: detect.h:315
mpm_default_matcher
uint8_t mpm_default_matcher
Definition: util-mpm.c:49
SigMatchSignatures
void SigMatchSignatures(ThreadVars *tv, DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx, Packet *p)
wrapper for old tests
Definition: detect.c:1824
DetectEngineAppendSig
Signature * DetectEngineAppendSig(DetectEngineCtx *, const char *)
Parse and append a Signature into the Detection Engine Context signature list.
Definition: detect-parse.c:2575
Packet_::flowflags
uint8_t flowflags
Definition: decode.h:461
SIG_FLAG_TOCLIENT
#define SIG_FLAG_TOCLIENT
Definition: detect.h:256
UTHBuildPacketReal
Packet * UTHBuildPacketReal(uint8_t *payload, uint16_t payload_len, uint8_t ipproto, const char *src, const char *dst, uint16_t sport, uint16_t dport)
UTHBuildPacketReal is a function that create tcp/udp packets for unittests specifying ip and port sou...
Definition: util-unittest-helper.c:244
Flow_::protoctx
void * protoctx
Definition: flow.h:437
SigTableElmt_::Setup
int(* Setup)(DetectEngineCtx *, Signature *, const char *)
Definition: detect.h:1270
detect-pcre.h
DetectBufferMpmRegistry_::transforms
DetectEngineTransforms transforms
Definition: detect.h:682
FLOW_IPV4
#define FLOW_IPV4
Definition: flow.h:96
detect-engine-prefilter.h
DetectEngineThreadCtx_::mtcu
MpmThreadCtx mtcu
Definition: detect.h:1180
util-unittest.h
util-unittest-helper.h
DetectBufferTypeGetByName
int DetectBufferTypeGetByName(const char *name)
Definition: detect-engine.c:1124
detect-dns-query.h
DetectEngineAppInspectionEngine_::sm_list
uint16_t sm_list
Definition: detect.h:424
StreamTcpInitConfig
void StreamTcpInitConfig(bool)
To initialize the stream global configuration data.
Definition: stream-tcp.c:359
FLOW_INITIALIZE
#define FLOW_INITIALIZE(f)
Definition: flow-util.h:38
SIG_FLAG_TOSERVER
#define SIG_FLAG_TOSERVER
Definition: detect.h:255
InspectionBufferSetupMultiEmpty
void InspectionBufferSetupMultiEmpty(InspectionBuffer *buffer)
setup the buffer empty
Definition: detect-engine.c:1593
decode.h
util-debug.h
PASS
#define PASS
Pass the test.
Definition: util-unittest.h:105
de_ctx
DetectEngineCtx * de_ctx
Definition: fuzz_siginit.c:17
DetectEngineThreadCtx_
Definition: detect.h:1075
alp_tctx
AppLayerParserThreadCtx * alp_tctx
Definition: fuzz_applayerparserparse.c:22
util-print.h
SCEnter
#define SCEnter(...)
Definition: util-debug.h:271
detect-engine-mpm.h
detect.h
ThreadVars_
Per thread variable structure.
Definition: threadvars.h:57
DETECT_ENGINE_INSPECT_SIG_MATCH
#define DETECT_ENGINE_INSPECT_SIG_MATCH
Definition: detect-engine-state.h:39
DetectEngineCtx_::mpm_matcher
uint8_t mpm_matcher
Definition: detect.h:830
InspectionBuffer::inspect_offset
uint64_t inspect_offset
Definition: detect.h:367
app-layer-parser.h
MpmCtx_::minlen
uint16_t minlen
Definition: util-mpm.h:100
SigGroupCleanup
int SigGroupCleanup(DetectEngineCtx *de_ctx)
Definition: detect-engine-build.c:2040
util-profiling.h
FlowGetProtoMapping
uint8_t FlowGetProtoMapping(uint8_t proto)
Function to map the protocol to the defined FLOW_PROTO_* enumeration.
Definition: flow-util.c:98
Packet_
Definition: decode.h:430
detect-engine-build.h
DetectAppLayerInspectEngineRegister2
void DetectAppLayerInspectEngineRegister2(const char *name, AppProto alproto, uint32_t dir, int progress, InspectEngineFuncPtr2 Callback2, InspectionBufferGetDataPtr GetData)
register inspect engine at start up time
Definition: detect-engine.c:216
detect-engine-alert.h
SCReturnPtr
#define SCReturnPtr(x, type)
Definition: util-debug.h:287
MpmTableElmt_::Search
uint32_t(* Search)(const struct MpmCtx_ *, struct MpmThreadCtx_ *, PrefilterRuleStore *, const uint8_t *, uint32_t)
Definition: util-mpm.h:163
FLOW_PKT_TOCLIENT
#define FLOW_PKT_TOCLIENT
Definition: flow.h:222
SigGroupBuild
int SigGroupBuild(DetectEngineCtx *de_ctx)
Convert the signature list into the runtime match structure.
Definition: detect-engine-build.c:1971
AppLayerParserThreadCtxAlloc
AppLayerParserThreadCtx * AppLayerParserThreadCtxAlloc(void)
Gets a new app layer protocol's parser thread context.
Definition: app-layer-parser.c:293
detect-engine-content-inspection.h
DetectEngineThreadCtx_::discontinue_matching
uint16_t discontinue_matching
Definition: detect.h:1140
DetectEngineAppInspectionEngine_::smd
SigMatchData * smd
Definition: detect.h:435
DetectEngineContentInspection
uint8_t DetectEngineContentInspection(DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx, const Signature *s, const SigMatchData *smd, Packet *p, Flow *f, const uint8_t *buffer, uint32_t buffer_len, uint32_t stream_start_offset, uint8_t flags, uint8_t inspection_mode)
Run the actual payload match functions.
Definition: detect-engine-content-inspection.c:106
AppLayerTxData
struct AppLayerTxData AppLayerTxData
Definition: detect.h:1347
PREFILTER_PROFILING_ADD_BYTES
#define PREFILTER_PROFILING_ADD_BYTES(det_ctx, bytes)
Definition: util-profiling.h:287
Packet_::flow
struct Flow_ * flow
Definition: decode.h:469
DetectEngineThreadCtxInit
TmEcode DetectEngineThreadCtxInit(ThreadVars *, void *, void **)
initialize thread specific detection engine context
Definition: detect-engine.c:3308
DETECT_CI_FLAGS_SINGLE
#define DETECT_CI_FLAGS_SINGLE
Definition: detect-engine-content-inspection.h:47
StreamTcpFreeConfig
void StreamTcpFreeConfig(bool quiet)
Definition: stream-tcp.c:690
flags
uint8_t flags
Definition: decode-gre.h:0
SigTableElmt_::alias
const char * alias
Definition: detect.h:1286
AppLayerParserParse
int AppLayerParserParse(ThreadVars *tv, AppLayerParserThreadCtx *alp_tctx, Flow *f, AppProto alproto, uint8_t flags, const uint8_t *input, uint32_t input_len)
Definition: app-layer-parser.c:1307
suricata-common.h
DetectEngineThreadCtxDeinit
TmEcode DetectEngineThreadCtxDeinit(ThreadVars *, void *)
Definition: detect-engine.c:3522
DetectBufferMpmRegistry_::app_v2
struct DetectBufferMpmRegistry_::@86::@88 app_v2
sigmatch_table
SigTableElmt sigmatch_table[DETECT_TBLSIZE]
Definition: detect-parse.c:129
DetectEngineThreadCtx_::inspection_recursion_counter
int inspection_recursion_counter
Definition: detect.h:1159
util-spm.h
InspectionBufferSetupMulti
void InspectionBufferSetupMulti(InspectionBuffer *buffer, const DetectEngineTransforms *transforms, const uint8_t *data, const uint32_t data_len)
setup the buffer with our initial data
Definition: detect-engine.c:1606
PrefilterAppendTxEngine
int PrefilterAppendTxEngine(DetectEngineCtx *de_ctx, SigGroupHead *sgh, PrefilterTxFn PrefilterTxFunc, AppProto alproto, int tx_min_progress, void *pectx, void(*FreeFunc)(void *pectx), const char *name)
Definition: detect-engine-prefilter.c:270
DetectEngineInspectGenericList
uint8_t DetectEngineInspectGenericList(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)
Do the content inspection & validation for a signature.
Definition: detect-engine.c:2122
PrefilterMpmDnsQuery::transforms
const DetectEngineTransforms * transforms
Definition: detect-dns-query.c:138
DetectDnsQueryRegister
void DetectDnsQueryRegister(void)
Registration function for keyword: dns_query.
Definition: detect-dns-query.c:201
DETECT_ENGINE_INSPECT_SIG_NO_MATCH
#define DETECT_ENGINE_INSPECT_SIG_NO_MATCH
Definition: detect-engine-state.h:38
tv
ThreadVars * tv
Definition: fuzz_decodepcapfile.c:32
InspectionBuffer::inspect_len
uint32_t inspect_len
Definition: detect.h:368
DetectEngineAppInspectionEngine_::v2
struct DetectEngineAppInspectionEngine_::@83 v2
InspectionBuffer::inspect
const uint8_t * inspect
Definition: detect.h:366
str
#define str(s)
Definition: suricata-common.h:286
SCFree
#define SCFree(p)
Definition: util-mem.h:61
UTHFreePacket
void UTHFreePacket(Packet *p)
UTHFreePacket: function to release the allocated data from UTHBuildPacket and the packet itself.
Definition: util-unittest-helper.c:485
Flow_::alstate
void * alstate
Definition: flow.h:472
Flow_::flags
uint32_t flags
Definition: flow.h:417
detect-parse.h
Signature_
Signature container.
Definition: detect.h:582
FAIL
#define FAIL
Fail a test.
Definition: util-unittest.h:60
DetectEngineAppInspectionEngine_::transforms
const DetectEngineTransforms * transforms
Definition: detect.h:432
FLOW_PKT_ESTABLISHED
#define FLOW_PKT_ESTABLISHED
Definition: flow.h:223
DetectEngineCtxInit
DetectEngineCtx * DetectEngineCtxInit(void)
Definition: detect-engine.c:2553
PrefilterMpmDnsQuery::mpm_ctx
const MpmCtx * mpm_ctx
Definition: detect-dns-query.c:137
mpm_table
MpmTableElmt mpm_table[MPM_TABLE_SIZE]
Definition: util-mpm.c:48
DETECT_AL_DNS_QUERY
@ DETECT_AL_DNS_QUERY
Definition: detect-engine-register.h:224
DnsQueryGetDataArgs
Definition: detect-dns-query.c:70
InspectionBufferMultipleForListGet
InspectionBuffer * InspectionBufferMultipleForListGet(DetectEngineThreadCtx *det_ctx, const int list_id, const uint32_t local_id)
for a InspectionBufferMultipleForList get a InspectionBuffer
Definition: detect-engine.c:1546
DETECT_ENGINE_CONTENT_INSPECTION_MODE_STATE
@ DETECT_ENGINE_CONTENT_INSPECTION_MODE_STATE
Definition: detect-engine-content-inspection.h:36
SIGMATCH_NOOPT
#define SIGMATCH_NOOPT
Definition: detect.h:1467
PrefilterMpmDnsQuery
Definition: detect-dns-query.c:135
DetectEngineCtx_::flags
uint8_t flags
Definition: detect.h:829
AppLayerParserThreadCtx_
Definition: app-layer-parser.c:66
DetectBufferTypeSetDescriptionByName
void DetectBufferTypeSetDescriptionByName(const char *name, const char *desc)
Definition: detect-engine.c:1221
MpmCtx_
Definition: util-mpm.h:89
TcpSession_
Definition: stream-tcp-private.h:283
flow.h
Flow_::alproto
AppProto alproto
application level protocol
Definition: flow.h:446
SCCalloc
#define SCCalloc(nm, sz)
Definition: util-mem.h:53
PrefilterMpmDnsQuery::list_id
int list_id
Definition: detect-dns-query.c:136
flow-var.h
PrefilterMpmDnsQuery
struct PrefilterMpmDnsQuery PrefilterMpmDnsQuery
FLOW_DESTROY
#define FLOW_DESTROY(f)
Definition: flow-util.h:127
PKT_STREAM_EST
#define PKT_STREAM_EST
Definition: decode.h:997
SigTableElmt_::RegisterTests
void(* RegisterTests)(void)
Definition: detect.h:1277
app-layer.h
DetectBufferMpmRegistry_::pname
char pname[32]
Definition: detect.h:671