summaryrefslogtreecommitdiffstats
path: root/src/detect-content.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/detect-content.c')
-rw-r--r--src/detect-content.c2973
1 files changed, 2973 insertions, 0 deletions
diff --git a/src/detect-content.c b/src/detect-content.c
new file mode 100644
index 0000000..be1f125
--- /dev/null
+++ b/src/detect-content.c
@@ -0,0 +1,2973 @@
+/* Copyright (C) 2007-2022 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
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Simple content match part of the detection engine.
+ */
+
+#include "suricata-common.h"
+#include "decode.h"
+#include "detect.h"
+#include "detect-content.h"
+#include "detect-uricontent.h"
+#include "detect-engine-mpm.h"
+#include "detect-engine.h"
+#include "detect-engine-build.h"
+#include "detect-engine-state.h"
+#include "detect-parse.h"
+#include "detect-pcre.h"
+#include "util-mpm.h"
+#include "flow.h"
+#include "flow-util.h"
+#include "flow-var.h"
+#include "detect-flow.h"
+#include "app-layer.h"
+#include "util-unittest.h"
+#include "util-print.h"
+#include "util-debug.h"
+#include "util-spm.h"
+#include "threads.h"
+#include "util-unittest-helper.h"
+#include "pkt-var.h"
+#include "host.h"
+#include "util-profiling.h"
+#include "detect-dsize.h"
+
+#ifdef UNITTESTS
+static void DetectContentRegisterTests(void);
+#endif
+
+void DetectContentRegister (void)
+{
+ sigmatch_table[DETECT_CONTENT].name = "content";
+ sigmatch_table[DETECT_CONTENT].desc = "match on payload content";
+ sigmatch_table[DETECT_CONTENT].url = "/rules/payload-keywords.html#content";
+ sigmatch_table[DETECT_CONTENT].Match = NULL;
+ sigmatch_table[DETECT_CONTENT].Setup = DetectContentSetup;
+ sigmatch_table[DETECT_CONTENT].Free = DetectContentFree;
+#ifdef UNITTESTS
+ sigmatch_table[DETECT_CONTENT].RegisterTests = DetectContentRegisterTests;
+#endif
+ sigmatch_table[DETECT_CONTENT].flags = (SIGMATCH_QUOTES_MANDATORY|SIGMATCH_HANDLE_NEGATION);
+}
+
+/**
+ * \brief Parse a content string, ie "abc|DE|fgh"
+ *
+ * \param content_str null terminated string containing the content
+ * \param result result pointer to pass the fully parsed byte array
+ * \param result_len size of the resulted data
+ * \param flags flags to be set by this parsing function
+ *
+ * \retval -1 error
+ * \retval 0 ok
+ */
+int DetectContentDataParse(const char *keyword, const char *contentstr,
+ uint8_t **pstr, uint16_t *plen)
+{
+ char *str = NULL;
+ size_t slen = 0;
+
+ slen = strlen(contentstr);
+ if (slen == 0) {
+ return -1;
+ }
+ uint8_t buffer[slen + 1];
+ strlcpy((char *)&buffer, contentstr, slen + 1);
+ str = (char *)buffer;
+
+ SCLogDebug("\"%s\", len %" PRIuMAX, str, (uintmax_t)slen);
+
+ //SCLogDebug("DetectContentParse: \"%s\", len %" PRIu32 "", str, len);
+ char converted = 0;
+
+ {
+ size_t i, x;
+ uint8_t bin = 0;
+ uint8_t escape = 0;
+ uint8_t binstr[3] = "";
+ uint8_t binpos = 0;
+ uint16_t bin_count = 0;
+
+ for (i = 0, x = 0; i < slen; i++) {
+ // SCLogDebug("str[%02u]: %c", i, str[i]);
+ if (str[i] == '|') {
+ bin_count++;
+ if (bin) {
+ if (binpos > 0) {
+ SCLogError("Incomplete hex code in content - %s. Invalidating signature.",
+ contentstr);
+ goto error;
+ }
+ bin = 0;
+ } else {
+ bin = 1;
+ }
+ } else if(!escape && str[i] == '\\') {
+ escape = 1;
+ } else {
+ if (bin) {
+ if (isdigit((unsigned char)str[i]) ||
+ str[i] == 'A' || str[i] == 'a' ||
+ str[i] == 'B' || str[i] == 'b' ||
+ str[i] == 'C' || str[i] == 'c' ||
+ str[i] == 'D' || str[i] == 'd' ||
+ str[i] == 'E' || str[i] == 'e' ||
+ str[i] == 'F' || str[i] == 'f')
+ {
+ // SCLogDebug("part of binary: %c", str[i]);
+
+ binstr[binpos] = (char)str[i];
+ binpos++;
+
+ if (binpos == 2) {
+ uint8_t c = strtol((char *)binstr, (char **) NULL, 16) & 0xFF;
+ binpos = 0;
+ str[x] = c;
+ x++;
+ converted = 1;
+ }
+ } else if (str[i] == ' ') {
+ // SCLogDebug("space as part of binary string");
+ }
+ else if (str[i] != ',') {
+ SCLogError("Invalid hex code in "
+ "content - %s, hex %c. Invalidating signature.",
+ contentstr, str[i]);
+ goto error;
+ }
+ } else if (escape) {
+ if (str[i] == ':' ||
+ str[i] == ';' ||
+ str[i] == '\\' ||
+ str[i] == '\"')
+ {
+ str[x] = str[i];
+ x++;
+ } else {
+ SCLogError("'%c' has to be escaped", str[i - 1]);
+ goto error;
+ }
+ escape = 0;
+ converted = 1;
+ } else if (str[i] == '"') {
+ SCLogError("Invalid unescaped double quote within content section.");
+ goto error;
+ } else {
+ str[x] = str[i];
+ x++;
+ }
+ }
+ }
+
+ if (bin_count % 2 != 0) {
+ SCLogError("Invalid hex code assembly in "
+ "%s - %s. Invalidating signature.",
+ keyword, contentstr);
+ goto error;
+ }
+
+ if (converted) {
+ slen = x;
+ }
+ }
+
+ if (slen) {
+ uint8_t *ptr = SCCalloc(1, slen);
+ if (ptr == NULL) {
+ return -1;
+ }
+ memcpy(ptr, str, slen);
+
+ *plen = (uint16_t)slen;
+ *pstr = ptr;
+ return 0;
+ }
+error:
+ return -1;
+}
+/**
+ * \brief DetectContentParse
+ * \initonly
+ */
+DetectContentData *DetectContentParse(SpmGlobalThreadCtx *spm_global_thread_ctx,
+ const char *contentstr)
+{
+ DetectContentData *cd = NULL;
+ uint8_t *content = NULL;
+ uint16_t len = 0;
+ int ret;
+
+ ret = DetectContentDataParse("content", contentstr, &content, &len);
+ if (ret == -1) {
+ return NULL;
+ }
+
+ cd = SCCalloc(1, sizeof(DetectContentData) + len);
+ if (unlikely(cd == NULL)) {
+ SCFree(content);
+ exit(EXIT_FAILURE);
+ }
+
+ cd->content = (uint8_t *)cd + sizeof(DetectContentData);
+ memcpy(cd->content, content, len);
+ cd->content_len = len;
+
+ /* Prepare SPM search context. */
+ cd->spm_ctx = SpmInitCtx(cd->content, cd->content_len, 0,
+ spm_global_thread_ctx);
+ if (cd->spm_ctx == NULL) {
+ SCFree(content);
+ SCFree(cd);
+ return NULL;
+ }
+
+ cd->depth = 0;
+ cd->offset = 0;
+ cd->within = 0;
+ cd->distance = 0;
+
+ SCFree(content);
+ return cd;
+
+}
+
+DetectContentData *DetectContentParseEncloseQuotes(SpmGlobalThreadCtx *spm_global_thread_ctx,
+ const char *contentstr)
+{
+ return DetectContentParse(spm_global_thread_ctx, contentstr);
+}
+
+/**
+ * \brief Helper function to print a DetectContentData
+ */
+void DetectContentPrint(DetectContentData *cd)
+{
+ int i = 0;
+ if (cd == NULL) {
+ SCLogDebug("DetectContentData \"cd\" is NULL");
+ return;
+ }
+ char *tmpstr = SCMalloc(sizeof(char) * cd->content_len + 1);
+ if (tmpstr != NULL) {
+ for (i = 0; i < cd->content_len; i++) {
+ if (isprint(cd->content[i]))
+ tmpstr[i] = cd->content[i];
+ else
+ tmpstr[i] = '.';
+ }
+ tmpstr[i] = '\0';
+ SCLogDebug("Content: \"%s\"", tmpstr);
+ SCFree(tmpstr);
+ } else {
+ SCLogDebug("Content: ");
+ for (i = 0; i < cd->content_len; i++)
+ SCLogDebug("%c", cd->content[i]);
+ }
+
+ SCLogDebug("Content_id: %"PRIu32, cd->id);
+ SCLogDebug("Content_len: %"PRIu16, cd->content_len);
+ SCLogDebug("Depth: %"PRIu16, cd->depth);
+ SCLogDebug("Offset: %"PRIu16, cd->offset);
+ SCLogDebug("Within: %"PRIi32, cd->within);
+ SCLogDebug("Distance: %"PRIi32, cd->distance);
+ SCLogDebug("flags: %u ", cd->flags);
+ SCLogDebug("negated: %s ", cd->flags & DETECT_CONTENT_NEGATED ? "true" : "false");
+ SCLogDebug("relative match next: %s ", cd->flags & DETECT_CONTENT_RELATIVE_NEXT ? "true" : "false");
+
+ if (cd->replace && cd->replace_len) {
+ char *tmprstr = SCMalloc(sizeof(char) * cd->replace_len + 1);
+
+ if (tmprstr != NULL) {
+ for (i = 0; i < cd->replace_len; i++) {
+ if (isprint(cd->replace[i]))
+ tmprstr[i] = cd->replace[i];
+ else
+ tmprstr[i] = '.';
+ }
+ tmprstr[i] = '\0';
+ SCLogDebug("Replace: \"%s\"", tmprstr);
+ SCFree(tmprstr);
+ } else {
+ SCLogDebug("Replace: ");
+ for (i = 0; i < cd->replace_len; i++)
+ SCLogDebug("%c", cd->replace[i]);
+ }
+ }
+ SCLogDebug("-----------");
+}
+
+/**
+ * \brief Function to setup a content pattern.
+ *
+ * \param de_ctx pointer to the current detection_engine
+ * \param s pointer to the current Signature
+ * \param m pointer to the last parsed SigMatch
+ * \param contentstr pointer to the current keyword content string
+ * \retval -1 if error
+ * \retval 0 if all was ok
+ */
+int DetectContentSetup(DetectEngineCtx *de_ctx, Signature *s, const char *contentstr)
+{
+ DetectContentData *cd = DetectContentParse(de_ctx->spm_global_thread_ctx, contentstr);
+ if (cd == NULL)
+ goto error;
+ if (s->init_data->negated == true) {
+ cd->flags |= DETECT_CONTENT_NEGATED;
+ }
+
+ DetectContentPrint(cd);
+
+ if (DetectBufferGetActiveList(de_ctx, s) == -1)
+ goto error;
+
+ int sm_list = s->init_data->list;
+ if (sm_list == DETECT_SM_LIST_NOTSET) {
+ sm_list = DETECT_SM_LIST_PMATCH;
+ } else if (sm_list > DETECT_SM_LIST_MAX &&
+ 0 == (cd->flags & DETECT_CONTENT_NEGATED)) {
+ /* Check transform compatibility */
+ const char *tstr;
+ if (!DetectEngineBufferTypeValidateTransform(
+ de_ctx, sm_list, cd->content, cd->content_len, &tstr)) {
+ SCLogError("content string \"%s\" incompatible with %s transform", contentstr, tstr);
+ goto error;
+ }
+ }
+
+ SigMatch *sm = SigMatchAlloc();
+ if (sm == NULL)
+ goto error;
+ sm->ctx = (void *)cd;
+ sm->type = DETECT_CONTENT;
+ SigMatchAppendSMToList(s, sm, sm_list);
+
+ return 0;
+
+error:
+ DetectContentFree(de_ctx, cd);
+ return -1;
+}
+
+/**
+ * \brief this function will SCFree memory associated with DetectContentData
+ *
+ * \param cd pointer to DetectContentData
+ */
+void DetectContentFree(DetectEngineCtx *de_ctx, void *ptr)
+{
+ SCEnter();
+ DetectContentData *cd = (DetectContentData *)ptr;
+
+ if (cd == NULL)
+ SCReturn;
+
+ SpmDestroyCtx(cd->spm_ctx);
+
+ SCFree(cd);
+ SCReturn;
+}
+
+/*
+ * \brief Determine the size needed to accommodate the content
+ * elements of a signature
+ * \param s signature to get dsize value from
+ * \param max_size Maximum buffer/data size allowed.
+ * \param list signature match list.
+ * \param len Maximum length required
+ * \param offset Maximum offset encountered
+ *
+ * Note that negated content does not contribute to the maximum
+ * required size value. However, each negated content's values
+ * must not exceed the size value.
+ *
+ * Values from negated content blocks are used to determine if the
+ * negated content block requires a value that exceeds "max_size". The
+ * distance and within values from negated content blocks are added to
+ * the running total of required content size to see if the max_size
+ * would be exceeded.
+ *
+ * - Non-negated content contributes to the required size (content length, distance)
+ * - Negated content values are checked but not accumulated for the required size.
+ */
+void SigParseRequiredContentSize(
+ const Signature *s, const int max_size, const SigMatch *sm, int *len, int *offset)
+{
+ int max_offset = 0, total_len = 0;
+ bool first = true;
+ for (; sm != NULL; sm = sm->next) {
+ if (sm->type != DETECT_CONTENT || sm->ctx == NULL) {
+ continue;
+ }
+
+ DetectContentData *cd = (DetectContentData *)sm->ctx;
+ SCLogDebug("content_len %d; negated: %s; distance: %d, offset: %d, depth: %d",
+ cd->content_len, cd->flags & DETECT_CONTENT_NEGATED ? "yes" : "no", cd->distance,
+ cd->offset, cd->depth);
+
+ if (!first) {
+ /* only count content with relative modifiers */
+ if (!((cd->flags & DETECT_CONTENT_DISTANCE) || (cd->flags & DETECT_CONTENT_WITHIN)))
+ continue;
+
+ if (cd->flags & DETECT_CONTENT_NEGATED) {
+ /* Check if distance/within cause max to be exceeded */
+ int check = total_len + cd->distance + cd->within;
+ if (max_size < check) {
+ *len = check;
+ return;
+ }
+
+ continue;
+ }
+ }
+ SCLogDebug("content_len %d; distance: %d, offset: %d, depth: %d", cd->content_len,
+ cd->distance, cd->offset, cd->depth);
+ total_len += cd->content_len + cd->distance;
+ max_offset = MAX(max_offset, cd->offset);
+ first = false;
+ }
+
+ *len = total_len;
+ *offset = max_offset;
+}
+
+/**
+ * \retval true valid
+ * \retval false invalid
+ */
+bool DetectContentPMATCHValidateCallback(const Signature *s)
+{
+ if (!(s->flags & SIG_FLAG_DSIZE)) {
+ return true;
+ }
+
+ int max_right_edge_i = SigParseGetMaxDsize(s);
+ if (max_right_edge_i < 0) {
+ return true;
+ }
+
+ uint32_t max_right_edge = (uint32_t)max_right_edge_i;
+
+ int min_dsize_required = SigParseMaxRequiredDsize(s);
+ if (min_dsize_required >= 0) {
+ SCLogDebug("min_dsize %d; max_right_edge %d", min_dsize_required, max_right_edge);
+ if ((uint32_t)min_dsize_required > max_right_edge) {
+ SCLogError("signature can't match as required content length %d exceeds dsize value %d",
+ min_dsize_required, max_right_edge);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/** \brief apply depth/offset and distance/within to content matches
+ *
+ * The idea is that any limitation we can set is a win, as the mpm
+ * can use this to reduce match candidates.
+ *
+ * E.g. if we have 'content:"1"; depth:1; content:"2"; distance:0; within:1;'
+ * we know that we can add 'offset:1; depth:2;' to the 2nd condition. This
+ * will then be used in mpm if the 2nd condition would be selected for mpm.
+ *
+ * Another example: 'content:"1"; depth:1; content:"2"; distance:0;'. Here we
+ * cannot set a depth, but we can set an offset of 'offset:1;'. This will
+ * make the mpm a bit more precise.
+ */
+static void PropagateLimits(Signature *s, SigMatch *sm_head)
+{
+#define VALIDATE(e) \
+ if (!(e)) { \
+ return; \
+ }
+ uint16_t offset = 0;
+ uint16_t offset_plus_pat = 0;
+ uint16_t depth = 0;
+ bool has_active_depth_chain = false;
+
+ bool has_depth = false;
+ bool has_ends_with = false;
+ uint16_t ends_with_depth = 0;
+
+ for (SigMatch *sm = sm_head; sm != NULL; sm = sm->next) {
+ switch (sm->type) {
+ case DETECT_CONTENT: {
+ DetectContentData *cd = (DetectContentData *)sm->ctx;
+ if ((cd->flags & (DETECT_CONTENT_DEPTH | DETECT_CONTENT_OFFSET |
+ DETECT_CONTENT_WITHIN | DETECT_CONTENT_DISTANCE)) == 0) {
+ offset = depth = 0;
+ offset_plus_pat = cd->content_len;
+ SCLogDebug("reset");
+ has_active_depth_chain = false;
+ continue;
+ }
+ if (sm->prev == NULL) {
+ if (cd->distance >= 0 && cd->distance <= (int32_t)USHRT_MAX &&
+ cd->within >= 0 && cd->within <= (int32_t)USHRT_MAX) {
+ if (cd->flags & DETECT_CONTENT_DISTANCE) {
+ if (cd->distance > 0)
+ cd->flags |= DETECT_CONTENT_OFFSET;
+ cd->flags &= ~DETECT_CONTENT_DISTANCE;
+ cd->offset = (uint16_t)cd->distance;
+ cd->distance = 0;
+ cd->flags |= DETECT_CONTENT_DISTANCE2OFFSET;
+ }
+ if (cd->flags & DETECT_CONTENT_WITHIN) {
+ cd->flags |= DETECT_CONTENT_DEPTH;
+ cd->flags &= ~DETECT_CONTENT_WITHIN;
+ cd->depth = (uint16_t)cd->within + cd->offset;
+ cd->within = 0;
+ cd->flags |= DETECT_CONTENT_WITHIN2DEPTH;
+ }
+ }
+ }
+
+ if (cd->flags & DETECT_CONTENT_NEGATED) {
+ offset = depth = 0;
+ offset_plus_pat = 0;
+ SCLogDebug("reset because of negation");
+ has_active_depth_chain = false;
+ continue;
+ }
+
+ if (cd->depth) {
+ has_depth = true;
+ has_active_depth_chain = true;
+ }
+
+ SCLogDebug("sm %p depth %u offset %u distance %d within %d", sm, cd->depth,
+ cd->offset, cd->distance, cd->within);
+ SCLogDebug("stored: offset %u depth %u offset_plus_pat %u", offset, depth,
+ offset_plus_pat);
+
+ if ((cd->flags & (DETECT_DEPTH | DETECT_CONTENT_WITHIN)) == 0) {
+ if (depth)
+ SCLogDebug("no within, reset depth");
+ depth = 0;
+ has_active_depth_chain = false;
+ }
+ if ((cd->flags & DETECT_CONTENT_DISTANCE) == 0) {
+ if (offset_plus_pat)
+ SCLogDebug("no distance, reset offset_plus_pat & offset");
+ offset_plus_pat = offset = 0;
+ }
+
+ SCLogDebug("stored: offset %u depth %u offset_plus_pat %u "
+ "has_active_depth_chain %s",
+ offset, depth, offset_plus_pat, has_active_depth_chain ? "true" : "false");
+ if (cd->flags & DETECT_CONTENT_DISTANCE) {
+ if (cd->distance >= 0) {
+ VALIDATE((uint32_t)offset_plus_pat + cd->distance <= UINT16_MAX);
+ offset = cd->offset = (uint16_t)(offset_plus_pat + cd->distance);
+ SCLogDebug("distance %d: updated content to have offset %u", cd->distance,
+ cd->offset);
+ } else {
+ if (abs(cd->distance) > offset_plus_pat)
+ offset = cd->offset = 0;
+ else
+ offset = cd->offset = (uint16_t)(offset_plus_pat + cd->distance);
+ offset_plus_pat = offset + cd->content_len;
+ SCLogDebug("distance %d: updated content to have offset %u", cd->distance,
+ cd->offset);
+ }
+ }
+ if (has_active_depth_chain) {
+ if (offset_plus_pat && cd->flags & DETECT_CONTENT_WITHIN && cd->within >= 0) {
+ if (depth && depth > offset_plus_pat) {
+ int32_t dist = 0;
+ if (cd->flags & DETECT_CONTENT_DISTANCE && cd->distance > 0) {
+ dist = cd->distance;
+ SCLogDebug(
+ "distance to add: %u. depth + dist %u", dist, depth + dist);
+ }
+ SCLogDebug("depth %u + cd->within %u", depth, cd->within);
+ VALIDATE(depth + cd->within + dist >= 0 &&
+ depth + cd->within + dist <= UINT16_MAX);
+ depth = cd->depth = (uint16_t)(depth + cd->within + dist);
+ } else {
+ SCLogDebug("offset %u + cd->within %u", offset, cd->within);
+ VALIDATE(depth + cd->within >= 0 && depth + cd->within <= UINT16_MAX);
+ depth = cd->depth = (uint16_t)(offset + cd->within);
+ }
+ SCLogDebug("updated content to have depth %u", cd->depth);
+ } else {
+ if (cd->depth == 0 && depth != 0) {
+ if (cd->within > 0) {
+ SCLogDebug("within %d distance %d", cd->within, cd->distance);
+ if (cd->flags & DETECT_CONTENT_DISTANCE && cd->distance >= 0) {
+ VALIDATE(offset_plus_pat + cd->distance >= 0 &&
+ offset_plus_pat + cd->distance <= UINT16_MAX);
+ cd->offset = (uint16_t)(offset_plus_pat + cd->distance);
+ SCLogDebug("updated content to have offset %u", cd->offset);
+ }
+
+ VALIDATE(depth + cd->within >= 0 &&
+ depth + cd->within <= UINT16_MAX);
+ depth = cd->depth = (uint16_t)(cd->within + depth);
+ SCLogDebug("updated content to have depth %u", cd->depth);
+
+ if (cd->flags & DETECT_CONTENT_ENDS_WITH) {
+ has_ends_with = true;
+ if (ends_with_depth == 0)
+ ends_with_depth = depth;
+ ends_with_depth = MIN(ends_with_depth, depth);
+ }
+ }
+ }
+ }
+ }
+ if (cd->offset == 0) { // && offset != 0) {
+ if (cd->flags & DETECT_CONTENT_DISTANCE && cd->distance >= 0) {
+ cd->offset = offset_plus_pat;
+ SCLogDebug("update content to have offset %u", cd->offset);
+ }
+ }
+
+ if ((cd->flags & (DETECT_CONTENT_DEPTH | DETECT_CONTENT_OFFSET |
+ DETECT_CONTENT_WITHIN | DETECT_CONTENT_DISTANCE)) ==
+ (DETECT_CONTENT_DISTANCE | DETECT_CONTENT_WITHIN) ||
+ (cd->flags & (DETECT_CONTENT_DEPTH | DETECT_CONTENT_OFFSET |
+ DETECT_CONTENT_WITHIN | DETECT_CONTENT_DISTANCE)) ==
+ (DETECT_CONTENT_DISTANCE)) {
+ if (cd->distance >= 0) {
+ // only distance
+ VALIDATE((uint32_t)offset_plus_pat + cd->distance <= UINT16_MAX);
+ offset = cd->offset = (uint16_t)(offset_plus_pat + cd->distance);
+ offset_plus_pat = offset + cd->content_len;
+ SCLogDebug("offset %u offset_plus_pat %u", offset, offset_plus_pat);
+ }
+ }
+ if (cd->flags & DETECT_CONTENT_OFFSET) {
+ offset = cd->offset;
+ offset_plus_pat = offset + cd->content_len;
+ SCLogDebug("stored offset %u offset_plus_pat %u", offset, offset_plus_pat);
+ }
+ if (cd->depth) {
+ depth = cd->depth;
+ SCLogDebug("stored depth now %u", depth);
+ offset_plus_pat = offset + cd->content_len;
+ if (cd->flags & DETECT_CONTENT_ENDS_WITH) {
+ has_ends_with = true;
+ if (ends_with_depth == 0)
+ ends_with_depth = depth;
+ ends_with_depth = MIN(ends_with_depth, depth);
+ }
+ }
+ if ((cd->flags & (DETECT_CONTENT_WITHIN | DETECT_CONTENT_DEPTH)) == 0) {
+ has_active_depth_chain = false;
+ depth = 0;
+ }
+ break;
+ }
+ case DETECT_PCRE: {
+ // relative could leave offset_plus_pat set.
+ const DetectPcreData *pd = (const DetectPcreData *)sm->ctx;
+ if (pd->flags & DETECT_PCRE_RELATIVE) {
+ depth = 0;
+ } else {
+ SCLogDebug("non-anchored PCRE not supported, reset offset_plus_pat & offset");
+ offset_plus_pat = offset = depth = 0;
+ }
+ has_active_depth_chain = false;
+ break;
+ }
+ default:
+ SCLogDebug("keyword not supported, reset offset_plus_pat & offset");
+ offset_plus_pat = offset = depth = 0;
+ has_active_depth_chain = false;
+ break;
+ }
+ }
+ /* apply anchored 'ends with' as depth to all patterns */
+ if (has_depth && has_ends_with) {
+ for (SigMatch *sm = sm_head; sm != NULL; sm = sm->next) {
+ switch (sm->type) {
+ case DETECT_CONTENT: {
+ DetectContentData *cd = (DetectContentData *)sm->ctx;
+ if (cd->depth == 0)
+ cd->depth = ends_with_depth;
+ cd->depth = MIN(ends_with_depth, cd->depth);
+ if (cd->depth)
+ cd->flags |= DETECT_CONTENT_DEPTH;
+ break;
+ }
+ }
+ }
+ }
+#undef VALIDATE
+}
+
+void DetectContentPropagateLimits(Signature *s)
+{
+ PropagateLimits(s, s->init_data->smlists[DETECT_SM_LIST_PMATCH]);
+ for (uint32_t x = 0; x < s->init_data->buffer_index; x++) {
+ PropagateLimits(s, s->init_data->buffers[x].head);
+ }
+}
+
+static inline bool NeedsAsHex(uint8_t c)
+{
+ if (!isprint(c))
+ return true;
+
+ switch (c) {
+ case '/':
+ case ';':
+ case ':':
+ case '\\':
+ case ' ':
+ case '|':
+ case '"':
+ case '`':
+ case '\'':
+ return true;
+ }
+ return false;
+}
+
+void DetectContentPatternPrettyPrint(const DetectContentData *cd, char *str, size_t str_len)
+{
+ bool hex = false;
+ for (uint16_t i = 0; i < cd->content_len; i++) {
+ if (NeedsAsHex(cd->content[i])) {
+ char hex_str[4];
+ snprintf(hex_str, sizeof(hex_str), "%s%02X", !hex ? "|" : " ", cd->content[i]);
+ strlcat(str, hex_str, str_len);
+ hex = true;
+ } else {
+ char p_str[3];
+ snprintf(p_str, sizeof(p_str), "%s%c", hex ? "|" : "", cd->content[i]);
+ strlcat(str, p_str, str_len);
+ hex = false;
+ }
+ }
+ if (hex) {
+ strlcat(str, "|", str_len);
+ }
+}
+
+int DetectContentConvertToNocase(DetectEngineCtx *de_ctx, DetectContentData *cd)
+{
+ if (cd->flags & DETECT_CONTENT_NOCASE) {
+ SCLogError("can't use multiple nocase modifiers with the same content");
+ return -1;
+ }
+
+ /* for consistency in later use (e.g. by MPM construction and hashing),
+ * coerce the content string to lower-case. */
+ for (uint8_t *c = cd->content; c < cd->content + cd->content_len; c++) {
+ *c = u8_tolower(*c);
+ }
+
+ cd->flags |= DETECT_CONTENT_NOCASE;
+ /* Recreate the context with nocase chars */
+ SpmDestroyCtx(cd->spm_ctx);
+ cd->spm_ctx = SpmInitCtx(cd->content, cd->content_len, 1, de_ctx->spm_global_thread_ctx);
+ if (cd->spm_ctx == NULL) {
+ return -1;
+ }
+ return 0;
+}
+
+#ifdef UNITTESTS /* UNITTESTS */
+#include "detect-engine-alert.h"
+#include "packet.h"
+
+static bool TestLastContent(const Signature *s, uint16_t o, uint16_t d)
+{
+ const SigMatch *sm = s->init_data->smlists_tail[DETECT_SM_LIST_PMATCH];
+ if (!sm) {
+ SCLogDebug("no sm");
+ return false;
+ }
+ if (!(sm->type == DETECT_CONTENT)) {
+ SCLogDebug("not content");
+ return false;
+ }
+ const DetectContentData *cd = (const DetectContentData *)sm->ctx;
+ if (o != cd->offset) {
+ SCLogDebug("offset mismatch %u != %u", o, cd->offset);
+ return false;
+ }
+ if (d != cd->depth) {
+ SCLogDebug("depth mismatch %u != %u", d, cd->depth);
+ return false;
+ }
+ return true;
+}
+
+#define TEST_RUN(sig, o, d) \
+ { \
+ SCLogDebug("TEST_RUN start: '%s'", (sig)); \
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit(); \
+ FAIL_IF_NULL(de_ctx); \
+ de_ctx->flags |= DE_QUIET; \
+ char rule[2048]; \
+ snprintf(rule, sizeof(rule), "alert tcp any any -> any any (%s sid:1; rev:1;)", (sig)); \
+ Signature *s = DetectEngineAppendSig(de_ctx, rule); \
+ FAIL_IF_NULL(s); \
+ SigAddressPrepareStage1(de_ctx); \
+ bool res = TestLastContent(s, (o), (d)); \
+ FAIL_IF(res == false); \
+ DetectEngineCtxFree(de_ctx); \
+ }
+
+#define TEST_DONE \
+ PASS
+
+/** \test test propagation of depth/offset/distance/within */
+static int DetectContentDepthTest01(void)
+{
+ // straight depth/offset
+ TEST_RUN("content:\"abc\"; offset:1; depth:3;", 1, 4);
+ // dsize applied as depth
+ TEST_RUN("dsize:10; content:\"abc\";", 0, 10);
+ TEST_RUN("dsize:<10; content:\"abc\";", 0, 10);
+ TEST_RUN("dsize:5<>10; content:\"abc\";", 0, 10);
+
+ // relative match, directly following anchored content
+ TEST_RUN("content:\"abc\"; depth:3; content:\"xyz\"; distance:0; within:3; ", 3, 6);
+ // relative match, directly following anchored content
+ TEST_RUN("content:\"abc\"; offset:3; depth:3; content:\"xyz\"; distance:0; within:3; ", 6, 9);
+ TEST_RUN("content:\"abc\"; depth:6; content:\"xyz\"; distance:0; within:3; ", 3, 9);
+
+ // multiple relative matches after anchored content
+ TEST_RUN("content:\"abc\"; depth:3; content:\"klm\"; distance:0; within:3; content:\"xyz\"; distance:0; within:3; ", 6, 9);
+ // test 'reset' due to unanchored content
+ TEST_RUN("content:\"abc\"; depth:3; content:\"klm\"; content:\"xyz\"; distance:0; within:3; ", 3, 0);
+ // test 'reset' due to unanchored pcre
+ TEST_RUN("content:\"abc\"; depth:3; pcre:/\"klm\"/; content:\"xyz\"; distance:0; within:3; ", 0, 0);
+ // test relative pcre. We can use previous offset+pattern len
+ TEST_RUN("content:\"abc\"; depth:3; pcre:/\"klm\"/R; content:\"xyz\"; distance:0; within:3; ", 3, 0);
+ TEST_RUN("content:\"abc\"; offset:3; depth:3; pcre:/\"klm\"/R; content:\"xyz\"; distance:0; within:3; ", 6, 0);
+
+ TEST_RUN("content:\"abc\"; depth:3; content:\"klm\"; within:3; content:\"xyz\"; within:3; ", 0, 9);
+
+ TEST_RUN("content:\"abc\"; depth:3; content:\"klm\"; distance:0; content:\"xyz\"; distance:0; ", 6, 0);
+
+ // tests to see if anchored 'ends_with' is applied to other content as depth
+ TEST_RUN("content:\"abc\"; depth:6; isdataat:!1,relative; content:\"klm\";", 0, 6);
+ TEST_RUN("content:\"abc\"; depth:3; content:\"klm\"; within:3; content:\"xyz\"; within:3; isdataat:!1,relative; content:\"def\"; ", 0, 9);
+
+ TEST_RUN("content:\"|03|\"; depth:1; content:\"|e0|\"; distance:4; within:1;", 5, 6);
+ TEST_RUN("content:\"|03|\"; depth:1; content:\"|e0|\"; distance:4; within:1; content:\"Cookie|3a|\"; distance:5; within:7;", 11, 18);
+
+ TEST_RUN("content:\"this\"; content:\"is\"; within:6; content:\"big\"; within:8; content:\"string\"; within:8;", 0, 0);
+
+ TEST_RUN("dsize:<80; content:!\"|00 22 02 00|\"; depth: 4; content:\"|00 00 04|\"; distance:8; within:3; content:\"|00 00 00 00 00|\"; distance:6; within:5;", 17, 80);
+ TEST_RUN("content:!\"|00 22 02 00|\"; depth: 4; content:\"|00 00 04|\"; distance:8; within:3; content:\"|00 00 00 00 00|\"; distance:6; within:5;", 17, 0);
+
+ TEST_RUN("content:\"|0d 0a 0d 0a|\"; content:\"code=\"; distance:0;", 4, 0);
+ TEST_RUN("content:\"|0d 0a 0d 0a|\"; content:\"code=\"; distance:0; content:\"xploit.class\"; distance:2; within:18;", 11, 0);
+
+ TEST_RUN("content:\"|16 03|\"; depth:2; content:\"|55 04 0a|\"; distance:0;", 2, 0);
+ TEST_RUN("content:\"|16 03|\"; depth:2; content:\"|55 04 0a|\"; distance:0; content:\"|0d|LogMeIn, Inc.\"; distance:1; within:14;", 6, 0);
+ TEST_RUN("content:\"|16 03|\"; depth:2; content:\"|55 04 0a|\"; distance:0; content:\"|0d|LogMeIn, Inc.\"; distance:1; within:14; content:\".app\";", 0, 0);
+
+ TEST_RUN("content:\"=\"; offset:4; depth:9;", 4, 13);
+ // low end: offset 4 + patlen 1 = 5. So 5 + distance 55 = 60.
+ // hi end: depth '13' (4+9) + distance 55 = 68 + within 2 = 70
+ TEST_RUN("content:\"=\"; offset:4; depth:9; content:\"=&\"; distance:55; within:2;", 60, 70);
+
+ // distance value is too high so we bail and not set anything on this content
+ TEST_RUN("content:\"0123456789\"; content:\"abcdef\"; distance:1048576;", 0, 0);
+
+ // Bug #5162.
+ TEST_RUN("content:\"SMB\"; depth:8; content:\"|09 00|\"; distance:8; within:2;", 11, 18);
+ TEST_RUN("content:\"SMB\"; depth:8; content:\"|09 00|\"; distance:8; within:2; content:\"|05 "
+ "00 00|\"; distance:0;",
+ 13, 0);
+ TEST_RUN("content:\"SMB\"; depth:8; content:\"|09 00|\"; distance:8; within:2; content:\"|05 "
+ "00 00|\"; distance:0; content:\"|0c 00|\"; distance:19; within:2;",
+ 35, 0);
+ TEST_RUN("content:\"SMB\"; depth:8; content:\"|09 00|\"; distance:8; within:2; content:\"|05 "
+ "00 00|\"; distance:0; content:\"|0c 00|\"; distance:19; within:2; content:\"|15 00 "
+ "00 00|\"; distance:20; within:4;",
+ 57, 0);
+
+ TEST_DONE;
+}
+
+/**
+ * \brief Print list of DETECT_CONTENT SigMatch's allocated in a
+ * SigMatch list, from the current sm to the end
+ * \param sm pointer to the current SigMatch to start printing from
+ */
+static void DetectContentPrintAll(SigMatch *sm)
+{
+#ifdef DEBUG
+ if (SCLogDebugEnabled()) {
+ int i = 0;
+
+ if (sm == NULL)
+ return;
+
+ SigMatch *first_sm = sm;
+
+ /* Print all of them */
+ for (; first_sm != NULL; first_sm = first_sm->next) {
+ if (first_sm->type == DETECT_CONTENT) {
+ SCLogDebug("Printing SigMatch DETECT_CONTENT %d", ++i);
+ DetectContentPrint((DetectContentData*)first_sm->ctx);
+ }
+ }
+ }
+#endif /* DEBUG */
+}
+
+static int g_file_data_buffer_id = 0;
+static int g_dce_stub_data_buffer_id = 0;
+
+/**
+ * \test DetectContentParseTest01 this is a test to make sure we can deal with escaped colons
+ */
+static int DetectContentParseTest01 (void)
+{
+ int result = 1;
+ DetectContentData *cd = NULL;
+ const char *teststring = "abc\\:def";
+ const char *teststringparsed = "abc:def";
+
+ uint8_t spm_matcher = SinglePatternMatchDefaultMatcher();
+ SpmGlobalThreadCtx *spm_global_thread_ctx = SpmInitGlobalThreadCtx(spm_matcher);
+ FAIL_IF(spm_global_thread_ctx == NULL);
+
+ cd = DetectContentParse(spm_global_thread_ctx, teststring);
+ if (cd != NULL) {
+ if (memcmp(cd->content, teststringparsed, strlen(teststringparsed)) != 0) {
+ SCLogDebug("expected %s got ", teststringparsed);
+ PrintRawUriFp(stdout,cd->content,cd->content_len);
+ SCLogDebug(": ");
+ result = 0;
+ DetectContentFree(NULL, cd);
+ }
+ } else {
+ SCLogDebug("expected %s got NULL: ", teststringparsed);
+ result = 0;
+ }
+ SpmDestroyGlobalThreadCtx(spm_global_thread_ctx);
+ return result;
+}
+
+/**
+ * \test DetectContentParseTest02 this is a test to make sure we can deal with escaped semi-colons
+ */
+static int DetectContentParseTest02 (void)
+{
+ int result = 1;
+ DetectContentData *cd = NULL;
+ const char *teststring = "abc\\;def";
+ const char *teststringparsed = "abc;def";
+
+ uint8_t spm_matcher = SinglePatternMatchDefaultMatcher();
+ SpmGlobalThreadCtx *spm_global_thread_ctx = SpmInitGlobalThreadCtx(spm_matcher);
+ FAIL_IF(spm_global_thread_ctx == NULL);
+
+ cd = DetectContentParse(spm_global_thread_ctx, teststring);
+ if (cd != NULL) {
+ if (memcmp(cd->content, teststringparsed, strlen(teststringparsed)) != 0) {
+ SCLogDebug("expected %s got ", teststringparsed);
+ PrintRawUriFp(stdout,cd->content,cd->content_len);
+ SCLogDebug(": ");
+ result = 0;
+ DetectContentFree(NULL, cd);
+ }
+ } else {
+ SCLogDebug("expected %s got NULL: ", teststringparsed);
+ result = 0;
+ }
+ SpmDestroyGlobalThreadCtx(spm_global_thread_ctx);
+ return result;
+}
+
+/**
+ * \test DetectContentParseTest03 this is a test to make sure we can deal with escaped double-quotes
+ */
+static int DetectContentParseTest03 (void)
+{
+ int result = 1;
+ DetectContentData *cd = NULL;
+ const char *teststring = "abc\\\"def";
+ const char *teststringparsed = "abc\"def";
+
+ uint8_t spm_matcher = SinglePatternMatchDefaultMatcher();
+ SpmGlobalThreadCtx *spm_global_thread_ctx = SpmInitGlobalThreadCtx(spm_matcher);
+ FAIL_IF(spm_global_thread_ctx == NULL);
+
+ cd = DetectContentParse(spm_global_thread_ctx, teststring);
+ if (cd != NULL) {
+ if (memcmp(cd->content, teststringparsed, strlen(teststringparsed)) != 0) {
+ SCLogDebug("expected %s got ", teststringparsed);
+ PrintRawUriFp(stdout,cd->content,cd->content_len);
+ SCLogDebug(": ");
+ result = 0;
+ DetectContentFree(NULL, cd);
+ }
+ } else {
+ SCLogDebug("expected %s got NULL: ", teststringparsed);
+ result = 0;
+ }
+ SpmDestroyGlobalThreadCtx(spm_global_thread_ctx);
+ return result;
+}
+
+/**
+ * \test DetectContentParseTest04 this is a test to make sure we can deal with escaped backslashes
+ */
+static int DetectContentParseTest04 (void)
+{
+ int result = 1;
+ DetectContentData *cd = NULL;
+ const char *teststring = "abc\\\\def";
+ const char *teststringparsed = "abc\\def";
+
+ uint8_t spm_matcher = SinglePatternMatchDefaultMatcher();
+ SpmGlobalThreadCtx *spm_global_thread_ctx = SpmInitGlobalThreadCtx(spm_matcher);
+ FAIL_IF(spm_global_thread_ctx == NULL);
+
+ cd = DetectContentParse(spm_global_thread_ctx, teststring);
+ if (cd != NULL) {
+ uint16_t len = (cd->content_len > strlen(teststringparsed));
+ if (memcmp(cd->content, teststringparsed, len) != 0) {
+ SCLogDebug("expected %s got ", teststringparsed);
+ PrintRawUriFp(stdout,cd->content,cd->content_len);
+ SCLogDebug(": ");
+ result = 0;
+ DetectContentFree(NULL, cd);
+ }
+ } else {
+ SCLogDebug("expected %s got NULL: ", teststringparsed);
+ result = 0;
+ }
+ SpmDestroyGlobalThreadCtx(spm_global_thread_ctx);
+ return result;
+}
+
+/**
+ * \test DetectContentParseTest05 test illegal escape
+ */
+static int DetectContentParseTest05 (void)
+{
+ int result = 1;
+ DetectContentData *cd = NULL;
+ const char *teststring = "abc\\def";
+
+ uint8_t spm_matcher = SinglePatternMatchDefaultMatcher();
+ SpmGlobalThreadCtx *spm_global_thread_ctx = SpmInitGlobalThreadCtx(spm_matcher);
+ FAIL_IF(spm_global_thread_ctx == NULL);
+
+ cd = DetectContentParse(spm_global_thread_ctx, teststring);
+ if (cd != NULL) {
+ SCLogDebug("expected NULL got ");
+ PrintRawUriFp(stdout,cd->content,cd->content_len);
+ SCLogDebug(": ");
+ result = 0;
+ DetectContentFree(NULL, cd);
+ }
+ SpmDestroyGlobalThreadCtx(spm_global_thread_ctx);
+ return result;
+}
+
+/**
+ * \test DetectContentParseTest06 test a binary content
+ */
+static int DetectContentParseTest06 (void)
+{
+ int result = 1;
+ DetectContentData *cd = NULL;
+ const char *teststring = "a|42|c|44|e|46|";
+ const char *teststringparsed = "abcdef";
+
+ uint8_t spm_matcher = SinglePatternMatchDefaultMatcher();
+ SpmGlobalThreadCtx *spm_global_thread_ctx = SpmInitGlobalThreadCtx(spm_matcher);
+ FAIL_IF(spm_global_thread_ctx == NULL);
+
+ cd = DetectContentParse(spm_global_thread_ctx, teststring);
+ if (cd != NULL) {
+ uint16_t len = (cd->content_len > strlen(teststringparsed));
+ if (memcmp(cd->content, teststringparsed, len) != 0) {
+ SCLogDebug("expected %s got ", teststringparsed);
+ PrintRawUriFp(stdout,cd->content,cd->content_len);
+ SCLogDebug(": ");
+ result = 0;
+ DetectContentFree(NULL, cd);
+ }
+ } else {
+ SCLogDebug("expected %s got NULL: ", teststringparsed);
+ result = 0;
+ }
+ SpmDestroyGlobalThreadCtx(spm_global_thread_ctx);
+ return result;
+}
+
+/**
+ * \test DetectContentParseTest07 test an empty content
+ */
+static int DetectContentParseTest07 (void)
+{
+ int result = 1;
+ DetectContentData *cd = NULL;
+ const char *teststring = "";
+
+ uint8_t spm_matcher = SinglePatternMatchDefaultMatcher();
+ SpmGlobalThreadCtx *spm_global_thread_ctx = SpmInitGlobalThreadCtx(spm_matcher);
+ FAIL_IF(spm_global_thread_ctx == NULL);
+
+ cd = DetectContentParse(spm_global_thread_ctx, teststring);
+ if (cd != NULL) {
+ SCLogDebug("expected NULL got %p: ", cd);
+ result = 0;
+ DetectContentFree(NULL, cd);
+ }
+ SpmDestroyGlobalThreadCtx(spm_global_thread_ctx);
+ return result;
+}
+
+/**
+ * \test DetectContentParseTest08 test an empty content
+ */
+static int DetectContentParseTest08 (void)
+{
+ int result = 1;
+ DetectContentData *cd = NULL;
+ const char *teststring = "";
+
+ uint8_t spm_matcher = SinglePatternMatchDefaultMatcher();
+ SpmGlobalThreadCtx *spm_global_thread_ctx = SpmInitGlobalThreadCtx(spm_matcher);
+ FAIL_IF(spm_global_thread_ctx == NULL);
+
+ cd = DetectContentParse(spm_global_thread_ctx, teststring);
+ if (cd != NULL) {
+ SCLogDebug("expected NULL got %p: ", cd);
+ result = 0;
+ DetectContentFree(NULL, cd);
+ }
+ SpmDestroyGlobalThreadCtx(spm_global_thread_ctx);
+ return result;
+}
+
+/**
+ * \test Test packet Matches
+ * \param raw_eth_pkt pointer to the ethernet packet
+ * \param pktsize size of the packet
+ * \param sig pointer to the signature to test
+ * \param sid sid number of the signature
+ * \retval return 1 if match
+ * \retval return 0 if not
+ */
+static int DetectContentLongPatternMatchTest(uint8_t *raw_eth_pkt, uint16_t pktsize, const char *sig,
+ uint32_t sid)
+{
+ int result = 0;
+
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ DecodeThreadVars dtv;
+
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+
+ memset(&dtv, 0, sizeof(DecodeThreadVars));
+ memset(&th_v, 0, sizeof(th_v));
+
+ FlowInitConfig(FLOW_QUIET);
+ DecodeEthernet(&th_v, &dtv, p, raw_eth_pkt, pktsize);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx, sig);
+ if (de_ctx->sig_list == NULL) {
+ goto end;
+ }
+ de_ctx->sig_list->next = NULL;
+
+ if (de_ctx->sig_list->init_data->smlists_tail[DETECT_SM_LIST_PMATCH]->type == DETECT_CONTENT) {
+ DetectContentData *co = (DetectContentData *)de_ctx->sig_list->init_data
+ ->smlists_tail[DETECT_SM_LIST_PMATCH]
+ ->ctx;
+ if (co->flags & DETECT_CONTENT_RELATIVE_NEXT) {
+ printf("relative next flag set on final match which is content: ");
+ goto end;
+ }
+ }
+
+ SCLogDebug("---DetectContentLongPatternMatchTest---");
+ DetectContentPrintAll(de_ctx->sig_list->init_data->smlists[DETECT_SM_LIST_MATCH]);
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, sid) != 1) {
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (de_ctx != NULL)
+ {
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ if (det_ctx != NULL)
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+ PacketRecycle(p);
+ FlowShutdown();
+
+ SCFree(p);
+ return result;
+}
+
+/**
+ * \brief Wrapper for DetectContentLongPatternMatchTest
+ */
+static int DetectContentLongPatternMatchTestWrp(const char *sig, uint32_t sid)
+{
+ /** Real packet with the following tcp data:
+ * "Hi, this is a big test to check content matches of splitted"
+ * "patterns between multiple chunks!"
+ * (without quotes! :) )
+ */
+ uint8_t raw_eth_pkt[] = {
+ 0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x08,0x00,0x45,0x00,
+ 0x00,0x85,0x00,0x01,0x00,0x00,0x40,0x06,
+ 0x7c,0x70,0x7f,0x00,0x00,0x01,0x7f,0x00,
+ 0x00,0x01,0x00,0x14,0x00,0x50,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x50,0x02,
+ 0x20,0x00,0xc9,0xad,0x00,0x00,0x48,0x69,
+ 0x2c,0x20,0x74,0x68,0x69,0x73,0x20,0x69,
+ 0x73,0x20,0x61,0x20,0x62,0x69,0x67,0x20,
+ 0x74,0x65,0x73,0x74,0x20,0x74,0x6f,0x20,
+ 0x63,0x68,0x65,0x63,0x6b,0x20,0x63,0x6f,
+ 0x6e,0x74,0x65,0x6e,0x74,0x20,0x6d,0x61,
+ 0x74,0x63,0x68,0x65,0x73,0x20,0x6f,0x66,
+ 0x20,0x73,0x70,0x6c,0x69,0x74,0x74,0x65,
+ 0x64,0x20,0x70,0x61,0x74,0x74,0x65,0x72,
+ 0x6e,0x73,0x20,0x62,0x65,0x74,0x77,0x65,
+ 0x65,0x6e,0x20,0x6d,0x75,0x6c,0x74,0x69,
+ 0x70,0x6c,0x65,0x20,0x63,0x68,0x75,0x6e,
+ 0x6b,0x73,0x21 }; /* end raw_eth_pkt */
+
+ return DetectContentLongPatternMatchTest(raw_eth_pkt, (uint16_t)sizeof(raw_eth_pkt),
+ sig, sid);
+}
+
+/**
+ * \test Check if we match a normal pattern (not splitted)
+ */
+static int DetectContentLongPatternMatchTest01(void)
+{
+ const char *sig = "alert tcp any any -> any any (msg:\"Nothing..\";"
+ " content:\"Hi, this is a big test\"; sid:1;)";
+ return DetectContentLongPatternMatchTestWrp(sig, 1);
+}
+
+/**
+ * \test Check if we match a splitted pattern
+ */
+static int DetectContentLongPatternMatchTest02(void)
+{
+ const char *sig = "alert tcp any any -> any any (msg:\"Nothing..\";"
+ " content:\"Hi, this is a big test to check content matches of"
+ " splitted patterns between multiple chunks!\"; sid:1;)";
+ return DetectContentLongPatternMatchTestWrp(sig, 1);
+}
+
+/**
+ * \test Check that we don't match the signature if one of the splitted
+ * chunks doesn't match the packet
+ */
+static int DetectContentLongPatternMatchTest03(void)
+{
+ /** The last chunk of the content should not match */
+ const char *sig = "alert tcp any any -> any any (msg:\"Nothing..\";"
+ " content:\"Hi, this is a big test to check content matches of"
+ " splitted patterns between multiple splitted chunks!\"; sid:1;)";
+ return (DetectContentLongPatternMatchTestWrp(sig, 1) == 0) ? 1: 0;
+}
+
+/**
+ * \test Check if we match multiple content (not splitted)
+ */
+static int DetectContentLongPatternMatchTest04(void)
+{
+ const char *sig = "alert tcp any any -> any any (msg:\"Nothing..\"; "
+ " content:\"Hi, this is\"; depth:15 ;content:\"a big test\"; "
+ " within:15; content:\"to check content matches of\"; "
+ " within:30; content:\"splitted patterns\"; distance:1; "
+ " within:30; "
+ " sid:1;)";
+ return DetectContentLongPatternMatchTestWrp(sig, 1);
+}
+
+/**
+ * \test Check that we match packets with multiple chunks and not chunks
+ * Here we should specify only contents that fit in 32 bytes
+ * Each of them with their modifier values
+ */
+static int DetectContentLongPatternMatchTest05(void)
+{
+ const char *sig = "alert tcp any any -> any any (msg:\"Nothing..\"; "
+ " content:\"Hi, this is a big\"; depth:17; "
+ " isdataat:30, relative; "
+ " content:\"test\"; within: 5; distance:1; "
+ " isdataat:15, relative; "
+ " content:\"of splitted\"; within:37; distance:15; "
+ " isdataat:20,relative; "
+ " content:\"patterns\"; within:9; distance:1; "
+ " isdataat:10, relative; "
+ " sid:1;)";
+ return DetectContentLongPatternMatchTestWrp(sig, 1);
+}
+
+/**
+ * \test Check that we match packets with multiple chunks and not chunks
+ * Here we should specify contents that fit and contents that must be splitted
+ * Each of them with their modifier values
+ */
+static int DetectContentLongPatternMatchTest06(void)
+{
+ const char *sig = "alert tcp any any -> any any (msg:\"Nothing..\"; "
+ " content:\"Hi, this is a big test to check cont\"; depth:36;"
+ " content:\"ent matches\"; within:11; distance:0; "
+ " content:\"of splitted patterns between multiple\"; "
+ " within:38; distance:1; "
+ " content:\"chunks!\"; within: 8; distance:1; "
+ " sid:1;)";
+ return DetectContentLongPatternMatchTestWrp(sig, 1);
+}
+
+/**
+ * \test Check if we match contents that are in the payload
+ * but not in the same order as specified in the signature
+ */
+static int DetectContentLongPatternMatchTest07(void)
+{
+ const char *sig = "alert tcp any any -> any any (msg:\"Nothing..\"; "
+ " content:\"chunks!\"; "
+ " content:\"content matches\"; offset:32; depth:47; "
+ " content:\"of splitted patterns between multiple\"; "
+ " content:\"Hi, this is a big\"; offset:0; depth:17; "
+ " sid:1;)";
+ return DetectContentLongPatternMatchTestWrp(sig, 1);
+}
+
+/**
+ * \test Check if we match contents that are in the payload
+ * but not in the same order as specified in the signature
+ */
+static int DetectContentLongPatternMatchTest08(void)
+{
+ const char *sig = "alert tcp any any -> any any (msg:\"Nothing..\"; "
+ " content:\"ent matches\"; "
+ " content:\"of splitted patterns between multiple\"; "
+ " within:38; distance:1; "
+ " content:\"chunks!\"; within: 8; distance:1; "
+ " content:\"Hi, this is a big test to check cont\"; depth:36;"
+ " sid:1;)";
+ return DetectContentLongPatternMatchTestWrp(sig, 1);
+}
+
+/**
+ * \test Check if we match contents that are in the payload
+ * but not in the same order as specified in the signature
+ */
+static int DetectContentLongPatternMatchTest09(void)
+{
+ const char *sig = "alert tcp any any -> any any (msg:\"Nothing..\"; "
+ " content:\"ent matches\"; "
+ " content:\"of splitted patterns between multiple\"; "
+ " offset:47; depth:85; "
+ " content:\"chunks!\"; within: 8; distance:1; "
+ " content:\"Hi, this is a big test to chec\"; depth:36;"
+ " content:\"k cont\"; distance:0; within:6;"
+ " sid:1;)";
+ return DetectContentLongPatternMatchTestWrp(sig, 1);
+}
+
+/**
+ * \test Check if we match two consecutive simple contents
+ */
+static int DetectContentLongPatternMatchTest10(void)
+{
+ const char *sig = "alert tcp any any -> any any (msg:\"Nothing..\"; "
+ " content:\"Hi, this is a big test to check \"; "
+ " content:\"con\"; "
+ " sid:1;)";
+ return DetectContentLongPatternMatchTestWrp(sig, 1);
+}
+
+/**
+ * \test Check if we match two contents of length 1
+ */
+static int DetectContentLongPatternMatchTest11(void)
+{
+ const char *sig = "alert tcp any any -> any any (msg:\"Nothing..\"; "
+ " content:\"H\"; "
+ " content:\"i\"; "
+ " sid:1;)";
+ return DetectContentLongPatternMatchTestWrp(sig, 1);
+}
+
+static int DetectContentParseTest09(void)
+{
+ DetectContentData *cd = NULL;
+ const char *teststring = "boo";
+
+ uint8_t spm_matcher = SinglePatternMatchDefaultMatcher();
+ SpmGlobalThreadCtx *spm_global_thread_ctx = SpmInitGlobalThreadCtx(spm_matcher);
+ FAIL_IF(spm_global_thread_ctx == NULL);
+
+ cd = DetectContentParse(spm_global_thread_ctx, teststring);
+ FAIL_IF_NULL(cd);
+ DetectContentFree(NULL, cd);
+ SpmDestroyGlobalThreadCtx(spm_global_thread_ctx);
+ PASS;
+}
+
+/**
+ * \test Test cases where if within specified is < content length we invalidate
+ * the sig.
+ */
+static int DetectContentParseTest17(void)
+{
+ int result = 0;
+ const char *sigstr = "alert tcp any any -> any any (msg:\"Dummy\"; "
+ "content:\"one\"; content:\"two\"; within:2; sid:1;)";
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->sig_list = SigInit(de_ctx, sigstr);
+ if (de_ctx->sig_list != NULL)
+ goto end;
+
+ result = 1;
+
+end:
+ SigCleanSignatures(de_ctx);
+ if (de_ctx != NULL)
+ DetectEngineCtxFree(de_ctx);
+ return result;
+}
+
+/**
+ * \test Test content for dce sig.
+ */
+static int DetectContentParseTest18(void)
+{
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ FAIL_IF_NULL(de_ctx);
+
+ Signature *s = SigAlloc();
+ FAIL_IF_NULL(s);
+ FAIL_IF(DetectSignatureSetAppProto(s, ALPROTO_DCERPC) < 0);
+ FAIL_IF_NOT(DetectContentSetup(de_ctx, s, "one") == 0);
+ FAIL_IF(DetectBufferIsPresent(s, g_dce_stub_data_buffer_id));
+ FAIL_IF_NOT(s->init_data->smlists[DETECT_SM_LIST_PMATCH] != NULL);
+ SigFree(de_ctx, s);
+
+ s = SigAlloc();
+ FAIL_IF_NULL(s);
+ FAIL_IF_NOT(DetectContentSetup(de_ctx, s, "one") == 0);
+ FAIL_IF(DetectBufferIsPresent(s, g_dce_stub_data_buffer_id));
+ FAIL_IF_NOT(s->init_data->smlists[DETECT_SM_LIST_PMATCH] != NULL);
+ SigFree(de_ctx, s);
+
+ DetectEngineCtxFree(de_ctx);
+ PASS;
+}
+
+/**
+ * \test Test content for dce sig.
+ */
+
+static int DetectContentParseTest19(void)
+{
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ FAIL_IF_NULL(de_ctx);
+ de_ctx->flags |= DE_QUIET;
+
+ Signature *s =
+ DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing dce iface, stub_data with content\"; "
+ "dce_iface:3919286a-b10c-11d0-9ba8-00c04fd92ef5; "
+ "dce_stub_data; "
+ "content:\"one\"; distance:0; sid:1;)");
+ FAIL_IF_NULL(s);
+
+ SigMatch *sm = DetectBufferGetFirstSigMatch(s, g_dce_stub_data_buffer_id);
+ FAIL_IF_NULL(sm);
+ FAIL_IF_NOT(sm->type == DETECT_CONTENT);
+ FAIL_IF_NOT(s->init_data->smlists[DETECT_SM_LIST_PMATCH] == NULL);
+
+ DetectContentData *data = (DetectContentData *)sm->ctx;
+ FAIL_IF_NOT(data->flags == DETECT_CONTENT_DISTANCE);
+
+ s = DetectEngineAppendSig(de_ctx,
+ "alert tcp any any -> any any "
+ "(msg:\"Testing dce iface, stub_data with contents & distance, within\"; "
+ "dce_iface:3919286a-b10c-11d0-9ba8-00c04fd92ef5; "
+ "dce_stub_data; "
+ "content:\"one\"; distance:0; content:\"two\"; within:10; sid:2;)");
+ FAIL_IF_NULL(s);
+ sm = DetectBufferGetFirstSigMatch(s, g_dce_stub_data_buffer_id);
+ FAIL_IF_NULL(sm);
+ FAIL_IF_NOT(sm->type == DETECT_CONTENT);
+ FAIL_IF_NULL(sm->next);
+ sm = sm->next;
+ FAIL_IF_NOT(sm->type == DETECT_CONTENT);
+ FAIL_IF_NOT(s->init_data->smlists[DETECT_SM_LIST_PMATCH] == NULL);
+
+ data = (DetectContentData *)sm->ctx;
+ FAIL_IF_NOT(data->flags == DETECT_CONTENT_WITHIN);
+ FAIL_IF_NOT(data->within == 10);
+
+ s = DetectEngineAppendSig(de_ctx,
+ "alert tcp any any -> any any "
+ "(msg:\"Testing dce iface, stub with contents, distance, within\"; "
+ "dce_iface:3919286a-b10c-11d0-9ba8-00c04fd92ef5; "
+ "dce_stub_data; "
+ "content:\"one\"; distance:0; "
+ "content:\"two\"; within:10; distance:2; sid:3;)");
+ FAIL_IF_NULL(s);
+ sm = DetectBufferGetFirstSigMatch(s, g_dce_stub_data_buffer_id);
+ FAIL_IF_NULL(sm);
+ FAIL_IF_NOT(sm->type == DETECT_CONTENT);
+ FAIL_IF_NULL(sm->next);
+ sm = sm->next;
+ FAIL_IF_NOT(sm->type == DETECT_CONTENT);
+ data = (DetectContentData *)sm->ctx;
+ FAIL_IF_NOT(data->flags == (DETECT_CONTENT_WITHIN | DETECT_CONTENT_DISTANCE));
+ FAIL_IF_NOT(data->within == 10);
+ FAIL_IF_NOT(data->distance == 2);
+ FAIL_IF_NOT(s->init_data->smlists[DETECT_SM_LIST_PMATCH] == NULL);
+
+ s = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any "
+ "(msg:\"Testing content\"; "
+ "content:\"one\"; sid:4;)");
+ FAIL_IF_NULL(s);
+ FAIL_IF(DetectBufferIsPresent(s, g_dce_stub_data_buffer_id));
+ FAIL_IF(s->init_data->smlists[DETECT_SM_LIST_PMATCH] == NULL);
+
+ DetectEngineCtxFree(de_ctx);
+ PASS;
+}
+
+/**
+ * \test Test content for dce sig.
+ */
+static int DetectContentParseTest20(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 1;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert udp any any -> any any "
+ "(msg:\"test\"; content:\"\"; sid:238012;)");
+ if (de_ctx->sig_list != NULL) {
+ result = 0;
+ goto end;
+ }
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Parsing test
+ */
+static int DetectContentParseTest21(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 1;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert udp any any -> any any "
+ "(msg:\"test\"; content:\"; sid:238012;)");
+ if (de_ctx->sig_list != NULL) {
+ result = 0;
+ goto end;
+ }
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Parsing test
+ */
+static int DetectContentParseTest22(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 1;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert udp any any -> any any "
+ "(msg:\"test\"; content:\"boo; sid:238012;)");
+ if (de_ctx->sig_list != NULL) {
+ result = 0;
+ goto end;
+ }
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Parsing test
+ */
+static int DetectContentParseTest23(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 1;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert udp any any -> any any "
+ "(msg:\"test\"; content:boo\"; sid:238012;)");
+ if (de_ctx->sig_list != NULL) {
+ result = 0;
+ goto end;
+ }
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Parsing test
+ */
+static int DetectContentParseTest24(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ DetectContentData *cd = 0;
+ Signature *s = NULL;
+ int result = 1;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ s = de_ctx->sig_list = SigInit(de_ctx,
+ "alert udp any any -> any any "
+ "(msg:\"test\"; content: !\"boo\"; sid:238012;)");
+ if (de_ctx->sig_list == NULL) {
+ printf("de_ctx->sig_list == NULL: ");
+ result = 0;
+ goto end;
+ }
+
+ if (s->init_data->smlists_tail[DETECT_SM_LIST_PMATCH] == NULL ||
+ s->init_data->smlists_tail[DETECT_SM_LIST_PMATCH]->ctx == NULL) {
+ printf("de_ctx->pmatch_tail == NULL || de_ctx->pmatch_tail->ctx == NULL: ");
+ result = 0;
+ goto end;
+ }
+
+ cd = (DetectContentData *)s->init_data->smlists_tail[DETECT_SM_LIST_PMATCH]->ctx;
+ result = (strncmp("boo", (char *)cd->content, cd->content_len) == 0);
+
+end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Parsing test
+ */
+static int DetectContentParseTest25(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 1;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert udp any any -> any any "
+ "(msg:\"test\"; content:\"|\"; sid:1;)");
+ if (de_ctx->sig_list != NULL) {
+ result = 0;
+ goto end;
+ }
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Parsing test
+ */
+static int DetectContentParseTest26(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 1;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert udp any any -> any any "
+ "(msg:\"test\"; content:\"|af\"; sid:1;)");
+ if (de_ctx->sig_list != NULL) {
+ result = 0;
+ goto end;
+ }
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Parsing test
+ */
+static int DetectContentParseTest27(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 1;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert udp any any -> any any "
+ "(msg:\"test\"; content:\"af|\"; sid:1;)");
+ if (de_ctx->sig_list != NULL) {
+ result = 0;
+ goto end;
+ }
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Parsing test
+ */
+static int DetectContentParseTest28(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 1;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert udp any any -> any any "
+ "(msg:\"test\"; content:\"|af|\"; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Parsing test
+ */
+static int DetectContentParseTest29(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 1;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert udp any any -> any any "
+ "(msg:\"test\"; content:\"aast|\"; sid:1;)");
+ if (de_ctx->sig_list != NULL) {
+ result = 0;
+ goto end;
+ }
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Parsing test
+ */
+static int DetectContentParseTest30(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 1;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert udp any any -> any any "
+ "(msg:\"test\"; content:\"aast|af\"; sid:1;)");
+ if (de_ctx->sig_list != NULL) {
+ result = 0;
+ goto end;
+ }
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Parsing test
+ */
+static int DetectContentParseTest31(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 1;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert udp any any -> any any "
+ "(msg:\"test\"; content:\"aast|af|\"; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Parsing test
+ */
+static int DetectContentParseTest32(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 1;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert udp any any -> any any "
+ "(msg:\"test\"; content:\"|af|asdf\"; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Parsing test
+ */
+static int DetectContentParseTest33(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 1;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert udp any any -> any any "
+ "(msg:\"test\"; content:\"|af|af|\"; sid:1;)");
+ if (de_ctx->sig_list != NULL) {
+ result = 0;
+ goto end;
+ }
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Parsing test
+ */
+static int DetectContentParseTest34(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 1;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert udp any any -> any any "
+ "(msg:\"test\"; content:\"|af|af|af\"; sid:1;)");
+ if (de_ctx->sig_list != NULL) {
+ result = 0;
+ goto end;
+ }
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+/**
+ * \test Parsing test
+ */
+static int DetectContentParseTest35(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ int result = 1;
+
+ de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert udp any any -> any any "
+ "(msg:\"test\"; content:\"|af|af|af|\"; sid:1;)");
+ if (de_ctx->sig_list == NULL) {
+ result = 0;
+ goto end;
+ }
+
+ end:
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ return result;
+}
+
+static int SigTestPositiveTestContent(const char *rule, uint8_t *buf)
+{
+ uint16_t buflen = strlen((char *)buf);
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+
+ memset(&th_v, 0, sizeof(th_v));
+ Packet *p = UTHBuildPacket(buf, buflen, IPPROTO_TCP);
+ FAIL_IF_NULL(p);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ FAIL_IF_NULL(de_ctx);
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx, rule);
+ FAIL_IF_NULL(de_ctx->sig_list);
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+ FAIL_IF_NULL(det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+
+ FAIL_IF(PacketAlertCheck(p, 1) != 1);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+
+ UTHFreePackets(&p, 1);
+ PASS;
+}
+
+static int DetectContentParseTest41(void)
+{
+ int result = 1;
+ DetectContentData *cd = NULL;
+ int patlen = 255;
+ char *teststring = SCMalloc(sizeof(char) * (patlen + 1));
+ if (unlikely(teststring == NULL))
+ return 0;
+ int idx = 0;
+ for (int i = 0; i < patlen; idx++, i++) {
+ teststring[idx] = 'a';
+ }
+ teststring[idx++] = '\0';
+
+ uint8_t spm_matcher = SinglePatternMatchDefaultMatcher();
+ SpmGlobalThreadCtx *spm_global_thread_ctx = SpmInitGlobalThreadCtx(spm_matcher);
+ FAIL_IF(spm_global_thread_ctx == NULL);
+
+ cd = DetectContentParse(spm_global_thread_ctx, teststring);
+ if (cd == NULL) {
+ SCLogDebug("expected not NULL");
+ result = 0;
+ }
+
+ SpmDestroyGlobalThreadCtx(spm_global_thread_ctx);
+ SCFree(teststring);
+ DetectContentFree(NULL, cd);
+ return result;
+}
+
+/**
+ * Tests that content lengths > 255 are supported.
+ */
+static int DetectContentParseTest42(void)
+{
+ int result = 1;
+ DetectContentData *cd = NULL;
+ int patlen = 256;
+ char *teststring = SCMalloc(sizeof(char) * (patlen + 1));
+ if (unlikely(teststring == NULL))
+ return 0;
+ int idx = 0;
+ for (int i = 0; i < patlen; idx++, i++) {
+ teststring[idx] = 'a';
+ }
+ teststring[idx++] = '\0';
+
+ uint8_t spm_matcher = SinglePatternMatchDefaultMatcher();
+ SpmGlobalThreadCtx *spm_global_thread_ctx = SpmInitGlobalThreadCtx(spm_matcher);
+ FAIL_IF(spm_global_thread_ctx == NULL);
+
+ cd = DetectContentParse(spm_global_thread_ctx, teststring);
+ if (cd == NULL) {
+ SCLogDebug("expected not NULL");
+ result = 0;
+ }
+
+ SpmDestroyGlobalThreadCtx(spm_global_thread_ctx);
+ SCFree(teststring);
+ DetectContentFree(NULL, cd);
+ return result;
+}
+
+static int DetectContentParseTest43(void)
+{
+ int result = 1;
+ DetectContentData *cd = NULL;
+ int patlen = 258;
+ char *teststring = SCMalloc(sizeof(char) * (patlen + 1));
+ if (unlikely(teststring == NULL))
+ return 0;
+ int idx = 0;
+ teststring[idx++] = '|';
+ teststring[idx++] = '4';
+ teststring[idx++] = '6';
+ teststring[idx++] = '|';
+ for (int i = 0; i < (patlen - 4); idx++, i++) {
+ teststring[idx] = 'a';
+ }
+ teststring[idx++] = '\0';
+
+ uint8_t spm_matcher = SinglePatternMatchDefaultMatcher();
+ SpmGlobalThreadCtx *spm_global_thread_ctx = SpmInitGlobalThreadCtx(spm_matcher);
+ FAIL_IF(spm_global_thread_ctx == NULL);
+
+ cd = DetectContentParse(spm_global_thread_ctx, teststring);
+ if (cd == NULL) {
+ SCLogDebug("expected not NULL");
+ result = 0;
+ }
+
+ SpmDestroyGlobalThreadCtx(spm_global_thread_ctx);
+ SCFree(teststring);
+ DetectContentFree(NULL, cd);
+ return result;
+}
+
+/**
+ * Tests that content lengths > 255 are supported.
+ */
+static int DetectContentParseTest44(void)
+{
+ int result = 1;
+ DetectContentData *cd = NULL;
+ int patlen = 259;
+ char *teststring = SCMalloc(sizeof(char) * (patlen + 1));
+ if (unlikely(teststring == NULL))
+ return 0;
+ int idx = 0;
+ teststring[idx++] = '|';
+ teststring[idx++] = '4';
+ teststring[idx++] = '6';
+ teststring[idx++] = '|';
+ for (int i = 0; i < (patlen - 4); idx++, i++) {
+ teststring[idx] = 'a';
+ }
+ teststring[idx++] = '\0';
+
+ uint8_t spm_matcher = SinglePatternMatchDefaultMatcher();
+ SpmGlobalThreadCtx *spm_global_thread_ctx = SpmInitGlobalThreadCtx(spm_matcher);
+ FAIL_IF(spm_global_thread_ctx == NULL);
+
+ cd = DetectContentParse(spm_global_thread_ctx, teststring);
+ if (cd == NULL) {
+ SCLogDebug("expected not NULL");
+ result = 0;
+ }
+
+ SpmDestroyGlobalThreadCtx(spm_global_thread_ctx);
+ SCFree(teststring);
+ DetectContentFree(NULL, cd);
+ return result;
+}
+
+/**
+ * \test Parsing test to check for unescaped quote within content section
+ */
+static int DetectContentParseTest45(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+
+ de_ctx = DetectEngineCtxInit();
+ FAIL_IF_NULL(de_ctx);
+
+ de_ctx->flags |= DE_QUIET;
+ de_ctx->sig_list = SigInit(de_ctx,
+ "alert tcp any any -> any any "
+ "(msg:\"test\"; content:\"|ff|\" content:\"TEST\"; sid:1;)");
+ FAIL_IF_NOT_NULL(de_ctx->sig_list);
+
+ DetectEngineCtxFree(de_ctx);
+
+ PASS;
+}
+
+static int SigTestNegativeTestContent(const char *rule, uint8_t *buf)
+{
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p = NULL;
+ ThreadVars th_v;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ int result = 0;
+ memset(&th_v, 0, sizeof(th_v));
+
+ p = UTHBuildPacket(buf, buflen, IPPROTO_TCP);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL)
+ goto end;
+
+ de_ctx->flags |= DE_QUIET;
+
+ de_ctx->sig_list = SigInit(de_ctx, rule);
+ if (de_ctx->sig_list == NULL) {
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1) != 0) {
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (det_ctx != NULL) {
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ }
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ SigCleanSignatures(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+ UTHFreePackets(&p, 1);
+ return result;
+}
+
+/**
+ * \test A positive test that checks that the content string doesn't contain
+ * the negated content
+ */
+static int SigTest41TestNegatedContent(void)
+{
+ return SigTestPositiveTestContent("alert tcp any any -> any any "
+ "(msg:\"HTTP URI cap\"; content:!\"GES\"; sid:1;)",
+
+ (uint8_t *)"GET /one/ HTTP/1.1\r\n Host: one.example.org\r\n\r\n\r\n"
+ "GET /two/ HTTP/1.1\r\nHost: two.example.org\r\n\r\n\r\n");
+}
+
+/**
+ * \test crash condition: as packet has no direction, it defaults to toclient
+ * in stream ctx inspection of packet. There a null ptr deref happens
+ * We don't care about the match/nomatch here.
+ */
+static int SigTest41aTestNegatedContent(void)
+{
+ (void)SigTestPositiveTestContent("alert tcp any any -> any any (msg:\"HTTP URI cap\"; flow:to_server; content:\"GET\"; sid:1;)", (uint8_t *)"GET /one/ HTTP/1.1\r\n Host: one.example.org\r\n\r\n\r\nGET /two/ HTTP/1.1\r\nHost: two.example.org\r\n\r\n\r\n");
+ return 1;
+}
+
+
+/**
+ * \test A positive test that checks that the content string doesn't contain
+ * the negated content within the specified depth
+ */
+static int SigTest42TestNegatedContent(void)
+{
+ return SigTestPositiveTestContent(
+ "alert tcp any any -> any any (content:!\"eeeeeeeeeee\"; depth:22; offset:35; sid:1;)",
+ (uint8_t *)"aaa bbbb cccc dddddddd eeeeeeeeeee ffffffffff gggggggg hhhhhhhh");
+}
+
+/**
+ * \test A negative test that checks that the content string doesn't contain
+ * the negated content within the specified depth, and also after the
+ * specified offset. Since the content is there, the match fails.
+ *
+ * Match is at offset:23, depth:34
+ */
+static int SigTest43TestNegatedContent(void)
+{
+ return SigTestNegativeTestContent(
+ "alert tcp any any -> any any (content:!\"eeeeeeeeeee\"; depth:34; offset:23; sid:1;)",
+ (uint8_t *)"aaa bbbb cccc dddddddd eeeeeeeeeee ffffffffff gggggggg hhhhhhhh");
+}
+
+/**
+ * \test A negative test that checks that the content string doesn't contain
+ * the negated content after the specified offset and within the specified
+ * depth.
+ */
+static int SigTest44TestNegatedContent(void)
+{
+ return SigTestPositiveTestContent(
+ "alert tcp any any -> any any (content:!\"eeeeeeeeeee\"; offset:40; depth:35; sid:1;)",
+ (uint8_t *)"aaa bbbb cccc dddddddd eeeeeeeeeee ffffffffff gggggggg hhhhhhhh");
+}
+
+/**
+ * \test A positive test that uses a combination of content string with negated
+ * content string
+ */
+static int SigTest45TestNegatedContent(void)
+{
+ return SigTestPositiveTestContent("alert tcp any any -> any any (content:\"aaa\"; depth:5; "
+ "content:!\"eeeeeeeeeee\"; depth:23; sid:1;)",
+ (uint8_t *)"aaa bbbb cccc dddddddd eeeeeeeeeee ffffffffff gggggggg hhhhhhhh");
+}
+
+/**
+ * \test A negative test that uses a combination of content string with negated
+ * content string, with we receiving a failure for 'onee' itself.
+ */
+static int SigTest46TestNegatedContent(void)
+{
+ return SigTestNegativeTestContent("alert tcp any any -> any any (content:\"aaaE\"; "
+ "content:!\"eeeeeeeeeee\"; depth:23; sid:1;)",
+ (uint8_t *)"aaa bbbb cccc dddddddd eeeeeeeeeee ffffffffff gggggggg hhhhhhhh");
+}
+
+/**
+ * \test A negative test that uses a combination of content string with negated
+ * content string, with we receiving a failure of first content's offset
+ * condition
+ */
+static int SigTest47TestNegatedContent(void)
+{
+ return SigTestNegativeTestContent("alert tcp any any -> any any (content:\"aaa\"; offset:5; "
+ "content:!\"eeeeeeeeeee\"; depth:23; sid:1;)",
+ (uint8_t *)"aaa bbbb cccc dddddddd eeeeeeeeeee ffffffffff gggggggg hhhhhhhh");
+}
+
+/**
+ * \test A positive test that checks that we don't have a negated content within
+ * the specified length from the previous content match.
+ */
+static int SigTest48TestNegatedContent(void)
+{
+ return SigTestPositiveTestContent(
+ "alert tcp any any -> any any (content:\"GET\"; content:!\"GES\"; within:26; sid:1;)",
+ (uint8_t *)"GET /one/ HTTP/1.1\r\n Host: one.example.org\r\n\r\n\r\nGET /two/ "
+ "HTTP/1.1\r\nHost: two.example.org\r\n\r\n\r\n");
+}
+
+/**
+ * \test A negative test that checks the combined use of content and negated
+ * content with the use of within
+ */
+static int SigTest49TestNegatedContent(void)
+{
+ return SigTestNegativeTestContent(
+ "alert tcp any any -> any any (content:\"GET\"; content:!\"Host\"; within:26; sid:1;)",
+ (uint8_t *)"GET /one/ HTTP/1.1\r\n Host: one.example.org\r\n\r\n\r\nGET /two/ "
+ "HTTP/1.1\r\nHost: two.example.org\r\n\r\n\r\n");
+}
+
+/**
+ * \test A positive test that checks the combined use of content and negated
+ * content with the use of distance
+ */
+static int SigTest50TestNegatedContent(void)
+{
+ return SigTestPositiveTestContent(
+ "alert tcp any any -> any any (content:\"GET\"; content:!\"GES\"; distance:25; sid:1;)",
+ (uint8_t *)"GET /one/ HTTP/1.1\r\n Host: one.example.org\r\n\r\n\r\nGET /two/ "
+ "HTTP/1.1\r\nHost: two.example.org\r\n\r\n\r\n");
+}
+
+/**
+ * \test A negative test that checks the combined use of content and negated
+ * content with the use of distance
+ *
+ * First GET at offset 0
+ * First Host at offset 21
+ */
+static int SigTest51TestNegatedContent(void)
+{
+ return SigTestNegativeTestContent("alert tcp any any -> any any (content:\"GET\"; content:!\"Host\"; distance:17; sid:1;)", (uint8_t *)"GET /one/ HTTP/1.1\r\nHost: one.example.org\r\n\r\n\r\nGET /two/ HTTP/1.1\r\nHost: two.example.org\r\n\r\n\r\n");
+}
+
+/**
+ * \test A negative test that checks the combined use of content and negated
+ * content, with the content not being present
+ */
+static int SigTest52TestNegatedContent(void)
+{
+ return SigTestNegativeTestContent(
+ "alert tcp any any -> any any (content:\"GES\"; content:!\"BOO\"; sid:1;)",
+ (uint8_t *)"GET /one/ HTTP/1.1\r\n Host: one.example.org\r\n\r\n\r\nGET /two/ "
+ "HTTP/1.1\r\nHost: two.example.org\r\n\r\n\r\n");
+}
+
+/**
+ * \test A negative test that checks the combined use of content and negated
+ * content, in the presence of within
+ */
+static int SigTest53TestNegatedContent(void)
+{
+ return SigTestNegativeTestContent(
+ "alert tcp any any -> any any (content:\"aaa\"; content:!\"Ggggg\"; within:56; sid:1;)",
+ (uint8_t *)"aaa bbbb cccc dddddddd eeeeeeeeeee ffffffffff Ggggggggg hhhhhhhh");
+}
+
+/**
+ * \test A positive test that checks the combined use of content and negated
+ * content, in the presence of within
+ */
+static int SigTest54TestNegatedContent(void)
+{
+ return SigTestPositiveTestContent("alert tcp any any -> any any (content:\"aaa\"; "
+ "content:!\"gggggg\"; within:20; sid:1;)",
+ (uint8_t *)"aaa bbbb cccc dddddddd eeeeeeeeeee ffffffffff ggggggggg hhhhhhhh");
+}
+
+/**
+ * \test A negative test that checks the use of negated content along with
+ * the presence of depth
+ */
+static int SigTest55TestNegatedContent(void)
+{
+ return SigTestNegativeTestContent(
+ "alert tcp any any -> any any (content:!\"aaa\"; depth:5; sid:1;)",
+ (uint8_t *)"aaa bbbb cccc dddddddd eeeeeeeeeee ffffffffff gggggggg hhhhhhhh");
+}
+
+/**
+ * \test A positive test that checks the combined use of 2 contents in the
+ * presence of within
+ */
+static int SigTest56TestNegatedContent(void)
+{
+ return SigTestPositiveTestContent(
+ "alert tcp any any -> any any (content:\"aaa\"; content:\"Ggggg\"; within:56; sid:1;)",
+ (uint8_t *)"aaa bbbb cccc dddddddd eeeeeeeeeee ffffffffff Gggggggg hhhhhhhh");
+}
+
+/**
+ * \test A negative test that checks the combined use of content and negated
+ * content, in the presence of within
+ */
+static int SigTest57TestNegatedContent(void)
+{
+ return SigTestNegativeTestContent(
+ "alert tcp any any -> any any (content:\"aaa\"; content:!\"Ggggg\"; within:56; sid:1;)",
+ (uint8_t *)"aaa bbbb cccc dddddddd eeeeeeeeeee ffffffffff Ggggggggg hhhhhhhh");
+}
+
+/**
+ * \test A positive test that checks the combined use of content and negated
+ * content, in the presence of distance
+ */
+static int SigTest58TestNegatedContent(void)
+{
+ return SigTestPositiveTestContent("alert tcp any any -> any any (content:\"aaa\"; "
+ "content:!\"Ggggg\"; distance:57; sid:1;)",
+ (uint8_t *)"aaa bbbb cccc dddddddd eeeeeeeeeee ffffffffff Ggggggggg hhhhhhhh");
+}
+
+/**
+ * \test A negative test that checks the combined use of content and negated
+ * content, in the presence of distance
+ */
+static int SigTest59TestNegatedContent(void)
+{
+ return SigTestNegativeTestContent("alert tcp any any -> any any (content:\"aaa\"; "
+ "content:!\"Gggg\"; distance:30; sid:1;)",
+ (uint8_t *)"aaa bbbb cccc dddddddd eeeeeeeeeee ffffffffff Ggggggggg hhhhhhhh");
+}
+
+static int SigTest60TestNegatedContent(void)
+{
+ return SigTestNegativeTestContent(
+ "alert tcp any any -> any any (content:!\"aaa\"; content:\"Ggggg\"; sid:1;)",
+ (uint8_t *)"aaa bbbb cccc dddddddd eeeeeeeeeee ffffffffff Ggggggggg hhhhhhhh");
+}
+
+static int SigTest61TestNegatedContent(void)
+{
+ return SigTestPositiveTestContent("alert tcp any any -> any any (content:\"aaa\"; depth:10; "
+ "content:!\"Ggggg\"; within:30; sid:1;)",
+ (uint8_t *)"aaa bbbb cccc dddddddd eeeeeeeeeee ffffffffff Ggggggggg hhhhhhhh");
+}
+
+/** \test Test negation in combination with within and depth
+ *
+ * Match of "aaa" at offset:0, depth:3
+ * Match of "Gggggg" at offset:46, depth:52
+ *
+ * This signature should not match for the test to pass.
+ */
+static int SigTest62TestNegatedContent(void)
+{
+ return SigTestNegativeTestContent("alert tcp any any -> any any (content:\"aaa\"; depth:10; "
+ "content:!\"Gggggg\"; within:49; sid:1;)",
+ (uint8_t *)"aaa bbbb cccc dddddddd eeeeeeeeeee ffffffffff Ggggggggg hhhhhhhh");
+}
+
+static int SigTest63TestNegatedContent(void)
+{
+ return SigTestNegativeTestContent("alert tcp any any -> any any (content:\"aaa\"; depth:10; "
+ "content:!\"Gggggg\"; within:56; sid:1;)",
+ (uint8_t *)"aaa bbbb cccc dddddddd eeeeeeeeeee ffffffffff Ggggggggg hhhhhhhh");
+}
+
+static int SigTest64TestNegatedContent(void)
+{
+ return SigTestPositiveTestContent("alert tcp any any -> any any (content:\"aaa\"; depth:10; "
+ "content:!\"Gggggg\"; within:30; sid:1;)",
+ (uint8_t *)"aaa bbbb cccc dddddddd eeeeeeeeeee ffffffffff Ggggggggg hhhhhhhh");
+}
+
+/** \test Test negation in combination with within and depth
+ *
+ * Match of "aaa" at offset:0, depth:3
+ * Match of "gggggg" at offset:46, depth:52
+ *
+ * This signature should not match for the test to pass.
+ */
+static int SigTest65TestNegatedContent(void)
+{
+ return SigTestNegativeTestContent("alert tcp any any -> any any (content:\"aaa\"; depth:10; "
+ "content:!\"Gggggg\"; distance:0; within:49; sid:1;)",
+ (uint8_t *)"aaa bbbb cccc dddddddd eeeeeeeeeee ffffffffff Ggggggggg hhhhhhhh");
+}
+
+static int SigTest66TestNegatedContent(void)
+{
+ return SigTestPositiveTestContent("alert tcp any any -> any any (content:\"aaa\"; depth:10; "
+ "content:!\"Gggggg\"; within:30; sid:1;)",
+ (uint8_t *)"aaa bbbb cccc dddddddd eeeeeeeeeee ffffffffff Ggggggggg hhhhhhhh");
+}
+
+static int SigTest67TestNegatedContent(void)
+{
+ return SigTestNegativeTestContent("alert tcp any any -> any any (content:\"aaa\"; depth:10; "
+ "content:!\"XXXX\"; within:56; sid:1;)",
+ (uint8_t *)"aaa bbbb cccc XXXXdddd eeeeeeeeeee ffffffffff XXXXggggg hhhhhhhh");
+}
+
+static int SigTest68TestNegatedContent(void)
+{
+ return SigTestPositiveTestContent(
+ "alert tcp any any -> any any (content:\"aaa\"; depth:10; content:\"cccc\"; offset:8; "
+ "content:!\"Gggggg\"; within:28; content:\"hhhhhhhh\"; sid:1;)",
+ (uint8_t *)"aaa bbbb cccc dddddddd eeeeeeeeeee ffffffffff Ggggggggg hhhhhhhh");
+}
+
+static int SigTest69TestNegatedContent(void)
+{
+ return SigTestNegativeTestContent(
+ "alert tcp any any -> any any (content:\"aaa\"; depth:10; content:\"cccc\"; offset:8; "
+ "content:!\"Gggggg\"; within:48; content:\"hhhhhhhh\"; sid:1;)",
+ (uint8_t *)"aaa bbbb cccc dddddddd eeeeeeeeeee ffffffffff Ggggggggg hhhhhhhh");
+}
+
+static int SigTest70TestNegatedContent(void)
+{
+ return SigTestNegativeTestContent("alert tcp any any -> any any (content:\"aaa\"; "
+ "content:!\"Gggggg\"; within:52; sid:1;)",
+ (uint8_t *)"aaa bbbb cccc dddddddd eeeeeeeeeee ffffffffff Ggggggggg hhhhhhhh");
+}
+
+/** \test within and distance */
+static int SigTest71TestNegatedContent(void)
+{
+ return SigTestNegativeTestContent("alert tcp any any -> any any (content:\"aaa\"; "
+ "content:!\"Gggggg\"; within:40; distance:43; sid:1;)",
+ (uint8_t *)"aaa bbbb cccc dddddddd eeeeeeeeeee ffffffffff Ggggggggg hhhhhhhh");
+}
+
+static int SigTest72TestNegatedContent(void)
+{
+ return SigTestNegativeTestContent("alert tcp any any -> any any (content:\"aaa\"; "
+ "content:!\"Gggggg\"; within:49; distance:43; sid:1;)",
+ (uint8_t *)"aaa bbbb cccc dddddddd eeeeeeeeeee ffffffffff Ggggggggg hhhhhhhh");
+}
+
+static int SigTest73TestNegatedContent(void)
+{
+ return SigTestNegativeTestContent("alert tcp any any -> any any (content:\"aaa\"; depth:5; "
+ "content:!\"eeeeeeeeeee\"; depth:35; sid:1;)",
+ (uint8_t *)"aaa bbbb cccc dddddddd eeeeeeeeeee ffffffffff ggggggggg hhhhhhhh");
+}
+
+static int SigTest74TestNegatedContent(void)
+{
+ return SigTestPositiveTestContent("alert tcp any any -> any any (msg:\"HTTP URI cap\"; content:\"USER\"; content:!\"PASS\"; sid:1;)", (uint8_t *)"USER apple");
+}
+
+static int SigTest75TestNegatedContent(void)
+{
+ return SigTestPositiveTestContent("alert tcp any any -> any any (msg:\"HTTP URI cap\"; content:\"USER\"; content:\"!PASS\"; sid:1;)", (uint8_t *)"USER !PASS");
+}
+
+static int SigTest76TestBug134(void)
+{
+ uint8_t *buf = (uint8_t *)"test detect ${IFS} in traffic";
+ uint16_t buflen = strlen((char *)buf);
+ Packet *p = UTHBuildPacket( buf, buflen, IPPROTO_TCP);
+ int result = 0;
+ Flow f;
+
+ memset(&f, 0, sizeof(Flow));
+ FLOW_INITIALIZE(&f);
+
+ p->dp = 515;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW;
+
+ char sig[] = "alert tcp any any -> any 515 "
+ "(msg:\"detect IFS\"; flow:to_server,established; content:\"${IFS}\";"
+ " depth:50; offset:0; sid:900091; rev:1;)";
+ if (UTHPacketMatchSigMpm(p, sig, MPM_AC) == 0) {
+ result = 0;
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (p != NULL)
+ UTHFreePacket(p);
+
+ FLOW_DESTROY(&f);
+ return result;
+}
+
+static int SigTest77TestBug139(void)
+{
+ uint8_t buf[] = {
+ 0x12, 0x23, 0x34, 0x35, 0x52, 0x52, 0x24, 0x42, 0x22, 0x24,
+ 0x52, 0x24, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x34 };
+ uint16_t buflen = sizeof(buf);
+ Packet *p = UTHBuildPacket( buf, buflen, IPPROTO_UDP);
+ int result = 0;
+
+ p->dp = 53;
+ char sig[] = "alert udp any any -> any 53 (msg:\"dns testing\";"
+ " content:\"|00 00|\"; depth:5; offset:13; sid:9436601;"
+ " rev:1;)";
+ if (UTHPacketMatchSigMpm(p, sig, MPM_AC) == 0) {
+ result = 0;
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (p != NULL)
+ UTHFreePacket(p);
+ return result;
+}
+
+static int DetectLongContentTestCommon(const char *sig, uint32_t sid)
+{
+ /* Packet with 512 A's in it for testing long content. */
+ static uint8_t pkt[739] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x45, 0x00,
+ 0x02, 0xd5, 0x4a, 0x18, 0x40, 0x00, 0x40, 0x06,
+ 0xd7, 0xd6, 0x0a, 0x10, 0x01, 0x0b, 0x0a, 0x10,
+ 0x01, 0x0a, 0xdb, 0x36, 0x00, 0x50, 0xca, 0xc5,
+ 0xcc, 0xd1, 0x95, 0x77, 0x0f, 0x7d, 0x80, 0x18,
+ 0x00, 0xe5, 0x77, 0x9d, 0x00, 0x00, 0x01, 0x01,
+ 0x08, 0x0a, 0x1d, 0xe0, 0x86, 0xc6, 0xfc, 0x73,
+ 0x49, 0xf3, 0x50, 0x4f, 0x53, 0x54, 0x20, 0x2f,
+ 0x20, 0x48, 0x54, 0x54, 0x50, 0x2f, 0x31, 0x2e,
+ 0x31, 0x0d, 0x0a, 0x55, 0x73, 0x65, 0x72, 0x2d,
+ 0x41, 0x67, 0x65, 0x6e, 0x74, 0x3a, 0x20, 0x63,
+ 0x75, 0x72, 0x6c, 0x2f, 0x37, 0x2e, 0x33, 0x37,
+ 0x2e, 0x30, 0x0d, 0x0a, 0x48, 0x6f, 0x73, 0x74,
+ 0x3a, 0x20, 0x31, 0x30, 0x2e, 0x31, 0x36, 0x2e,
+ 0x31, 0x2e, 0x31, 0x30, 0x0d, 0x0a, 0x41, 0x63,
+ 0x63, 0x65, 0x70, 0x74, 0x3a, 0x20, 0x2a, 0x2f,
+ 0x2a, 0x0d, 0x0a, 0x43, 0x6f, 0x6e, 0x74, 0x65,
+ 0x6e, 0x74, 0x2d, 0x4c, 0x65, 0x6e, 0x67, 0x74,
+ 0x68, 0x3a, 0x20, 0x35, 0x32, 0x38, 0x0d, 0x0a,
+ 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d,
+ 0x54, 0x79, 0x70, 0x65, 0x3a, 0x20, 0x61, 0x70,
+ 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f,
+ 0x6e, 0x2f, 0x78, 0x2d, 0x77, 0x77, 0x77, 0x2d,
+ 0x66, 0x6f, 0x72, 0x6d, 0x2d, 0x75, 0x72, 0x6c,
+ 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x0d,
+ 0x0a, 0x0d, 0x0a, 0x58, 0x58, 0x58, 0x58, 0x58,
+ 0x58, 0x58, 0x58, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
+ 0x41, 0x41, 0x41, 0x58, 0x58, 0x58, 0x58, 0x58,
+ 0x58, 0x58, 0x58
+ };
+
+ return DetectContentLongPatternMatchTest(pkt, (uint16_t)sizeof(pkt), sig,
+ sid);
+}
+
+static int DetectLongContentTest1(void)
+{
+ /* Signature with 256 A's. */
+ const char *sig = "alert tcp any any -> any any (msg:\"Test Rule\"; content:\"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\"; sid:1;)";
+
+ return DetectLongContentTestCommon(sig, 1);
+}
+
+static int DetectLongContentTest2(void)
+{
+ /* Signature with 512 A's. */
+ const char *sig = "alert tcp any any -> any any (msg:\"Test Rule\"; content:\"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\"; sid:1;)";
+
+ return DetectLongContentTestCommon(sig, 1);
+}
+
+static int DetectLongContentTest3(void)
+{
+ /* Signature with 513 A's. */
+ const char *sig = "alert tcp any any -> any any (msg:\"Test Rule\"; content:\"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\"; sid:1;)";
+
+ return !DetectLongContentTestCommon(sig, 1);
+}
+
+static int DetectBadBinContent(void)
+{
+ DetectEngineCtx *de_ctx = NULL;
+ de_ctx = DetectEngineCtxInit();
+ FAIL_IF_NULL(de_ctx);
+ de_ctx->flags |= DE_QUIET;
+ FAIL_IF_NOT_NULL(DetectEngineAppendSig(
+ de_ctx, "alert tcp any any -> any any (msg:\"test\"; content:\"|a|\"; sid:1;)"));
+ FAIL_IF_NOT_NULL(DetectEngineAppendSig(
+ de_ctx, "alert tcp any any -> any any (msg:\"test\"; content:\"|aa b|\"; sid:1;)"));
+ FAIL_IF_NOT_NULL(DetectEngineAppendSig(
+ de_ctx, "alert tcp any any -> any any (msg:\"test\"; content:\"|aa bz|\"; sid:1;)"));
+ /* https://redmine.openinfosecfoundation.org/issues/5201 */
+ FAIL_IF_NOT_NULL(DetectEngineAppendSig(
+ de_ctx, "alert tcp any any -> any any (msg:\"test\"; content:\"|22 2 22|\"; sid:1;)"));
+ DetectEngineCtxFree(de_ctx);
+ PASS;
+}
+
+/**
+ * \brief this function registers unit tests for DetectContent
+ */
+static void DetectContentRegisterTests(void)
+{
+ g_file_data_buffer_id = DetectBufferTypeGetByName("file_data");
+ g_dce_stub_data_buffer_id = DetectBufferTypeGetByName("dce_stub_data");
+
+ UtRegisterTest("DetectContentDepthTest01", DetectContentDepthTest01);
+
+ UtRegisterTest("DetectContentParseTest01", DetectContentParseTest01);
+ UtRegisterTest("DetectContentParseTest02", DetectContentParseTest02);
+ UtRegisterTest("DetectContentParseTest03", DetectContentParseTest03);
+ UtRegisterTest("DetectContentParseTest04", DetectContentParseTest04);
+ UtRegisterTest("DetectContentParseTest05", DetectContentParseTest05);
+ UtRegisterTest("DetectContentParseTest06", DetectContentParseTest06);
+ UtRegisterTest("DetectContentParseTest07", DetectContentParseTest07);
+ UtRegisterTest("DetectContentParseTest08", DetectContentParseTest08);
+ UtRegisterTest("DetectContentParseTest09", DetectContentParseTest09);
+ UtRegisterTest("DetectContentParseTest17", DetectContentParseTest17);
+ UtRegisterTest("DetectContentParseTest18", DetectContentParseTest18);
+ UtRegisterTest("DetectContentParseTest19", DetectContentParseTest19);
+ UtRegisterTest("DetectContentParseTest20", DetectContentParseTest20);
+ UtRegisterTest("DetectContentParseTest21", DetectContentParseTest21);
+ UtRegisterTest("DetectContentParseTest22", DetectContentParseTest22);
+ UtRegisterTest("DetectContentParseTest23", DetectContentParseTest23);
+ UtRegisterTest("DetectContentParseTest24", DetectContentParseTest24);
+ UtRegisterTest("DetectContentParseTest25", DetectContentParseTest25);
+ UtRegisterTest("DetectContentParseTest26", DetectContentParseTest26);
+ UtRegisterTest("DetectContentParseTest27", DetectContentParseTest27);
+ UtRegisterTest("DetectContentParseTest28", DetectContentParseTest28);
+ UtRegisterTest("DetectContentParseTest29", DetectContentParseTest29);
+ UtRegisterTest("DetectContentParseTest30", DetectContentParseTest30);
+ UtRegisterTest("DetectContentParseTest31", DetectContentParseTest31);
+ UtRegisterTest("DetectContentParseTest32", DetectContentParseTest32);
+ UtRegisterTest("DetectContentParseTest33", DetectContentParseTest33);
+ UtRegisterTest("DetectContentParseTest34", DetectContentParseTest34);
+ UtRegisterTest("DetectContentParseTest35", DetectContentParseTest35);
+ UtRegisterTest("DetectContentParseTest41", DetectContentParseTest41);
+ UtRegisterTest("DetectContentParseTest42", DetectContentParseTest42);
+ UtRegisterTest("DetectContentParseTest43", DetectContentParseTest43);
+ UtRegisterTest("DetectContentParseTest44", DetectContentParseTest44);
+ UtRegisterTest("DetectContentParseTest45", DetectContentParseTest45);
+
+ /* The reals */
+ UtRegisterTest("DetectContentLongPatternMatchTest01",
+ DetectContentLongPatternMatchTest01);
+ UtRegisterTest("DetectContentLongPatternMatchTest02",
+ DetectContentLongPatternMatchTest02);
+ UtRegisterTest("DetectContentLongPatternMatchTest03",
+ DetectContentLongPatternMatchTest03);
+ UtRegisterTest("DetectContentLongPatternMatchTest04",
+ DetectContentLongPatternMatchTest04);
+ UtRegisterTest("DetectContentLongPatternMatchTest05",
+ DetectContentLongPatternMatchTest05);
+ UtRegisterTest("DetectContentLongPatternMatchTest06",
+ DetectContentLongPatternMatchTest06);
+ UtRegisterTest("DetectContentLongPatternMatchTest07",
+ DetectContentLongPatternMatchTest07);
+ UtRegisterTest("DetectContentLongPatternMatchTest08",
+ DetectContentLongPatternMatchTest08);
+ UtRegisterTest("DetectContentLongPatternMatchTest09",
+ DetectContentLongPatternMatchTest09);
+ UtRegisterTest("DetectContentLongPatternMatchTest10",
+ DetectContentLongPatternMatchTest10);
+ UtRegisterTest("DetectContentLongPatternMatchTest11",
+ DetectContentLongPatternMatchTest11);
+
+ /* Negated content tests */
+ UtRegisterTest("SigTest41TestNegatedContent", SigTest41TestNegatedContent);
+ UtRegisterTest("SigTest41aTestNegatedContent",
+ SigTest41aTestNegatedContent);
+ UtRegisterTest("SigTest42TestNegatedContent", SigTest42TestNegatedContent);
+ UtRegisterTest("SigTest43TestNegatedContent", SigTest43TestNegatedContent);
+ UtRegisterTest("SigTest44TestNegatedContent", SigTest44TestNegatedContent);
+ UtRegisterTest("SigTest45TestNegatedContent", SigTest45TestNegatedContent);
+ UtRegisterTest("SigTest46TestNegatedContent", SigTest46TestNegatedContent);
+ UtRegisterTest("SigTest47TestNegatedContent", SigTest47TestNegatedContent);
+ UtRegisterTest("SigTest48TestNegatedContent", SigTest48TestNegatedContent);
+ UtRegisterTest("SigTest49TestNegatedContent", SigTest49TestNegatedContent);
+ UtRegisterTest("SigTest50TestNegatedContent", SigTest50TestNegatedContent);
+ UtRegisterTest("SigTest51TestNegatedContent", SigTest51TestNegatedContent);
+ UtRegisterTest("SigTest52TestNegatedContent", SigTest52TestNegatedContent);
+ UtRegisterTest("SigTest53TestNegatedContent", SigTest53TestNegatedContent);
+ UtRegisterTest("SigTest54TestNegatedContent", SigTest54TestNegatedContent);
+ UtRegisterTest("SigTest55TestNegatedContent", SigTest55TestNegatedContent);
+ UtRegisterTest("SigTest56TestNegatedContent", SigTest56TestNegatedContent);
+ UtRegisterTest("SigTest57TestNegatedContent", SigTest57TestNegatedContent);
+ UtRegisterTest("SigTest58TestNegatedContent", SigTest58TestNegatedContent);
+ UtRegisterTest("SigTest59TestNegatedContent", SigTest59TestNegatedContent);
+ UtRegisterTest("SigTest60TestNegatedContent", SigTest60TestNegatedContent);
+ UtRegisterTest("SigTest61TestNegatedContent", SigTest61TestNegatedContent);
+ UtRegisterTest("SigTest62TestNegatedContent", SigTest62TestNegatedContent);
+ UtRegisterTest("SigTest63TestNegatedContent", SigTest63TestNegatedContent);
+ UtRegisterTest("SigTest64TestNegatedContent", SigTest64TestNegatedContent);
+ UtRegisterTest("SigTest65TestNegatedContent", SigTest65TestNegatedContent);
+ UtRegisterTest("SigTest66TestNegatedContent", SigTest66TestNegatedContent);
+ UtRegisterTest("SigTest67TestNegatedContent", SigTest67TestNegatedContent);
+ UtRegisterTest("SigTest68TestNegatedContent", SigTest68TestNegatedContent);
+ UtRegisterTest("SigTest69TestNegatedContent", SigTest69TestNegatedContent);
+ UtRegisterTest("SigTest70TestNegatedContent", SigTest70TestNegatedContent);
+ UtRegisterTest("SigTest71TestNegatedContent", SigTest71TestNegatedContent);
+ UtRegisterTest("SigTest72TestNegatedContent", SigTest72TestNegatedContent);
+ UtRegisterTest("SigTest73TestNegatedContent", SigTest73TestNegatedContent);
+ UtRegisterTest("SigTest74TestNegatedContent", SigTest74TestNegatedContent);
+ UtRegisterTest("SigTest75TestNegatedContent", SigTest75TestNegatedContent);
+
+ UtRegisterTest("SigTest76TestBug134", SigTest76TestBug134);
+ UtRegisterTest("SigTest77TestBug139", SigTest77TestBug139);
+
+ UtRegisterTest("DetectLongContentTest1", DetectLongContentTest1);
+ UtRegisterTest("DetectLongContentTest2", DetectLongContentTest2);
+ UtRegisterTest("DetectLongContentTest3", DetectLongContentTest3);
+
+ UtRegisterTest("DetectBadBinContent", DetectBadBinContent);
+}
+#endif /* UNITTESTS */