* implemented if needed. Waste of space otherwise. */
AppLayerProtoDetectCtxIpproto ctx_ipp[FLOW_PROTO_DEFAULT];
+ /* Global SPM thread context prototype. */
+ SpmGlobalThreadCtx *spm_global_thread_ctx;
+
AppLayerProtoDetectProbingParser *ctx_pp;
/* Indicates the protocols that have registered themselves
PatternMatcherQueue pmq;
/* The value 2 is for direction(0 - toserver, 1 - toclient). */
MpmThreadCtx mpm_tctx[FLOW_PROTO_DEFAULT][2];
+ SpmThreadCtx *spm_thread_ctx;
};
/* The global app layer proto detection context. */
/** \internal
* \brief Handle SPM search for Signature */
static AppProto AppLayerProtoDetectPMMatchSignature(const AppLayerProtoDetectPMSignature *s,
+ AppLayerProtoDetectThreadCtx *tctx,
uint8_t *buf, uint16_t buflen,
uint8_t ipproto)
{
SCLogDebug("s->co->offset (%"PRIu16") s->cd->depth (%"PRIu16")",
s->cd->offset, s->cd->depth);
- if (s->cd->flags & DETECT_CONTENT_NOCASE)
- found = BoyerMooreNocase(s->cd->content, s->cd->content_len, sbuf, sbuflen, s->cd->bm_ctx);
- else
- found = BoyerMoore(s->cd->content, s->cd->content_len, sbuf, sbuflen, s->cd->bm_ctx);
+ found = SpmScan(s->cd->spm_ctx, tctx->spm_thread_ctx, sbuf, sbuflen);
if (found != NULL)
proto = s->alproto;
const AppLayerProtoDetectPMSignature *s = pm_ctx->map[tctx->pmq.rule_id_array[cnt]];
while (s != NULL) {
AppProto proto = AppLayerProtoDetectPMMatchSignature(s,
- buf, searchlen, ipproto);
+ tctx, buf, searchlen, ipproto);
/* store each unique proto once */
if (proto != ALPROTO_UNKNOWN &&
DetectContentData *cd;
int ret = 0;
- cd = DetectContentParseEncloseQuotes(pattern);
+ cd = DetectContentParseEncloseQuotes(alpd_ctx.spm_global_thread_ctx,
+ pattern);
if (cd == NULL)
goto error;
cd->depth = depth;
cd->offset = offset;
if (!is_cs) {
- BoyerMooreCtxToNocase(cd->bm_ctx, cd->content, cd->content_len);
+ /* Rebuild as nocase */
+ SpmDestroyCtx(cd->spm_ctx);
+ cd->spm_ctx = SpmInitCtx(cd->content, cd->content_len, 1,
+ alpd_ctx.spm_global_thread_ctx);
+ if (cd->spm_ctx == NULL) {
+ goto error;
+ }
cd->flags |= DETECT_CONTENT_NOCASE;
}
if (depth < cd->content_len)
memset(&alpd_ctx, 0, sizeof(alpd_ctx));
+ uint16_t spm_matcher = SinglePatternMatchDefaultMatcher();
+ alpd_ctx.spm_global_thread_ctx = SpmInitGlobalThreadCtx(spm_matcher);
+ if (alpd_ctx.spm_global_thread_ctx == NULL) {
+ SCLogError(SC_ERR_FATAL, "Unable to alloc SpmGlobalThreadCtx.");
+ exit(EXIT_FAILURE);
+ }
+
for (i = 0; i < FLOW_PROTO_DEFAULT; i++) {
for (j = 0; j < 2; j++) {
MpmInitCtx(&alpd_ctx.ctx_ipp[i].ctx_pm[j].mpm_ctx, MPM_AC);
}
}
+ SpmDestroyGlobalThreadCtx(alpd_ctx.spm_global_thread_ctx);
+
AppLayerProtoDetectFreeProbingParsers(alpd_ctx.ctx_pp);
SCReturnInt(0);
}
}
+ alpd_tctx->spm_thread_ctx = SpmMakeThreadCtx(alpd_ctx.spm_global_thread_ctx);
+ if (alpd_tctx->spm_thread_ctx == NULL) {
+ goto error;
+ }
+
goto end;
error:
if (alpd_tctx != NULL)
}
}
PmqFree(&alpd_tctx->pmq);
+ if (alpd_tctx->spm_thread_ctx != NULL) {
+ SpmDestroyThreadCtx(alpd_tctx->spm_thread_ctx);
+ }
SCFree(alpd_tctx);
SCReturn;
#include "util-unittest.h"
#include "util-print.h"
#include "util-debug.h"
-#include "util-spm-bm.h"
+#include "util-spm.h"
#include "threads.h"
#include "util-unittest-helper.h"
#include "pkt-var.h"
* \brief DetectContentParse
* \initonly
*/
-DetectContentData *DetectContentParse (char *contentstr)
+DetectContentData *DetectContentParse(SpmGlobalThreadCtx *spm_global_thread_ctx,
+ char *contentstr)
{
DetectContentData *cd = NULL;
uint8_t *content = NULL;
memcpy(cd->content, content, len);
cd->content_len = len;
- /* Prepare Boyer Moore context for searching faster */
- cd->bm_ctx = BoyerMooreCtxInit(cd->content, cd->content_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;
}
-DetectContentData *DetectContentParseEncloseQuotes(char *contentstr)
+DetectContentData *DetectContentParseEncloseQuotes(SpmGlobalThreadCtx *spm_global_thread_ctx,
+ char *contentstr)
{
char str[strlen(contentstr) + 3]; // 2 for quotes, 1 for \0
str[strlen(contentstr) + 1] = '\"';
str[strlen(contentstr) + 2] = '\0';
- return DetectContentParse(str);
+ return DetectContentParse(spm_global_thread_ctx, str);
}
/**
DetectContentData *cd = NULL;
SigMatch *sm = NULL;
- cd = DetectContentParse(contentstr);
+ cd = DetectContentParse(de_ctx->spm_global_thread_ctx, contentstr);
if (cd == NULL)
goto error;
DetectContentPrint(cd);
if (cd == NULL)
SCReturn;
- BoyerMooreCtxDeInit(cd->bm_ctx);
+ SpmDestroyCtx(cd->spm_ctx);
SCFree(cd);
SCReturn;
char *teststring = "\"abc\\:def\"";
char *teststringparsed = "abc:def";
- cd = DetectContentParse(teststring);
+ uint16_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);
SCLogDebug("expected %s got NULL: ", teststringparsed);
result = 0;
}
+ SpmDestroyGlobalThreadCtx(spm_global_thread_ctx);
return result;
}
char *teststring = "\"abc\\;def\"";
char *teststringparsed = "abc;def";
- cd = DetectContentParse(teststring);
+ uint16_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);
SCLogDebug("expected %s got NULL: ", teststringparsed);
result = 0;
}
+ SpmDestroyGlobalThreadCtx(spm_global_thread_ctx);
return result;
}
char *teststring = "\"abc\\\"def\"";
char *teststringparsed = "abc\"def";
- cd = DetectContentParse(teststring);
+ uint16_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);
SCLogDebug("expected %s got NULL: ", teststringparsed);
result = 0;
}
+ SpmDestroyGlobalThreadCtx(spm_global_thread_ctx);
return result;
}
char *teststring = "\"abc\\\\def\"";
char *teststringparsed = "abc\\def";
- cd = DetectContentParse(teststring);
+ uint16_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 NULL: ", teststringparsed);
result = 0;
}
+ SpmDestroyGlobalThreadCtx(spm_global_thread_ctx);
return result;
}
DetectContentData *cd = NULL;
char *teststring = "\"abc\\def\"";
- cd = DetectContentParse(teststring);
+ uint16_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);
result = 0;
DetectContentFree(cd);
}
+ SpmDestroyGlobalThreadCtx(spm_global_thread_ctx);
return result;
}
char *teststring = "\"a|42|c|44|e|46|\"";
char *teststringparsed = "abcdef";
- cd = DetectContentParse(teststring);
+ uint16_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 NULL: ", teststringparsed);
result = 0;
}
+ SpmDestroyGlobalThreadCtx(spm_global_thread_ctx);
return result;
}
DetectContentData *cd = NULL;
char *teststring = "\"\"";
- cd = DetectContentParse(teststring);
+ uint16_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(cd);
}
+ SpmDestroyGlobalThreadCtx(spm_global_thread_ctx);
return result;
}
DetectContentData *cd = NULL;
char *teststring = "\"\"";
- cd = DetectContentParse(teststring);
+ uint16_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(cd);
}
+ SpmDestroyGlobalThreadCtx(spm_global_thread_ctx);
return result;
}
DetectContentData *cd = NULL;
char *teststring = "!\"boo\"";
- cd = DetectContentParse(teststring);
+ uint16_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 (cd->flags & DETECT_CONTENT_NEGATED)
result = 1;
DetectContentFree(cd);
}
-
+ SpmDestroyGlobalThreadCtx(spm_global_thread_ctx);
return result;
}
DetectContentData *cd = NULL;
char *teststring = "!\"boo\"";
- cd = DetectContentParse(teststring);
+ uint16_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 (cd->flags & DETECT_CONTENT_NEGATED)
result = 1;
DetectContentFree(cd);
}
+ SpmDestroyGlobalThreadCtx(spm_global_thread_ctx);
return result;
}
DetectContentData *cd = NULL;
char *teststring = "\"boo\"";
- cd = DetectContentParse(teststring);
+ uint16_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 (!(cd->flags & DETECT_CONTENT_NEGATED))
result = 1;
DetectContentFree(cd);
}
+ SpmDestroyGlobalThreadCtx(spm_global_thread_ctx);
return result;
}
DetectContentData *cd = NULL;
char *teststring = "\"boo\"";
- cd = DetectContentParse(teststring);
+ uint16_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 (!(cd->flags & DETECT_CONTENT_NEGATED))
result = 1;
DetectContentFree(cd);
}
+ SpmDestroyGlobalThreadCtx(spm_global_thread_ctx);
return result;
}
DetectContentData *cd = NULL;
char *teststring = "!\"boo\"";
- cd = DetectContentParse(teststring);
+ uint16_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 (cd->flags & DETECT_CONTENT_NEGATED)
result = 1;
DetectContentFree(cd);
}
+ SpmDestroyGlobalThreadCtx(spm_global_thread_ctx);
return result;
}
DetectContentData *cd = NULL;
char *teststring = " \"!boo\"";
- cd = DetectContentParse(teststring);
+ uint16_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 (!(cd->flags & DETECT_CONTENT_NEGATED))
result = 1;
DetectContentFree(cd);
}
+ SpmDestroyGlobalThreadCtx(spm_global_thread_ctx);
return result;
}
DetectContentData *cd = NULL;
char *teststring = " !\"boo\"";
- cd = DetectContentParse(teststring);
+ uint16_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 (cd->flags & DETECT_CONTENT_NEGATED)
result = 1;
DetectContentFree(cd);
}
+ SpmDestroyGlobalThreadCtx(spm_global_thread_ctx);
return result;
}
DetectContentData *cd = NULL;
char *teststring = " \"boo\"";
- cd = DetectContentParse(teststring);
+ uint16_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) {
result = (cd->content_len == 3 && memcmp(cd->content,"boo",3) == 0);
DetectContentFree(cd);
}
+ SpmDestroyGlobalThreadCtx(spm_global_thread_ctx);
return result;
}
teststring[idx++] = '\"';
teststring[idx++] = '\0';
- cd = DetectContentParse(teststring);
+ uint16_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(cd);
return result;
teststring[idx++] = '\"';
teststring[idx++] = '\0';
- cd = DetectContentParse(teststring);
+ uint16_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(cd);
return result;
teststring[idx++] = '\"';
teststring[idx++] = '\0';
- cd = DetectContentParse(teststring);
+ uint16_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(cd);
return result;
teststring[idx++] = '\"';
teststring[idx++] = '\0';
- cd = DetectContentParse(teststring);
+ uint16_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(cd);
return result;
((c)->flags & DETECT_CONTENT_FAST_PATTERN_CHOP))
-#include "util-spm-bm.h"
+#include "util-spm.h"
typedef struct DetectContentData_ {
uint8_t *content;
uint16_t offset;
int32_t distance;
int32_t within;
- /* Boyer Moore context (for spm search) */
- BmCtx *bm_ctx;
+ /* SPM search context. */
+ SpmCtx *spm_ctx;
/* pointer to replacement data */
uint8_t *replace;
} DetectContentData;
/* prototypes */
void DetectContentRegister (void);
uint32_t DetectContentMaxId(DetectEngineCtx *);
-DetectContentData *DetectContentParse (char *contentstr);
+DetectContentData *DetectContentParse(SpmGlobalThreadCtx *spm_global_thread_ctx,
+ char *contentstr);
int DetectContentDataParse(const char *keyword, const char *contentstr,
uint8_t **pstr, uint16_t *plen, uint32_t *flags);
-DetectContentData *DetectContentParseEncloseQuotes(char *);
+DetectContentData *DetectContentParseEncloseQuotes(SpmGlobalThreadCtx *spm_global_thread_ctx,
+ char *contentstr);
int DetectContentSetup(DetectEngineCtx *de_ctx, Signature *s, char *contentstr);
void DetectContentPrint(DetectContentData *);
#include "app-layer-dcerpc.h"
#include "util-spm.h"
-#include "util-spm-bm.h"
#include "util-debug.h"
#include "util-print.h"
* greater than sbuffer_len found is anyways NULL */
/* do the actual search */
- if (cd->flags & DETECT_CONTENT_NOCASE)
- found = BoyerMooreNocase(cd->content, cd->content_len, sbuffer, sbuffer_len, cd->bm_ctx);
- else
- found = BoyerMoore(cd->content, cd->content_len, sbuffer, sbuffer_len, cd->bm_ctx);
+ found = SpmScan(cd->spm_ctx, det_ctx->spm_thread_ctx, sbuffer,
+ sbuffer_len);
/* next we evaluate the result in combination with the
* negation flag. */
de_ctx->mpm_matcher = PatternMatchDefaultMatcher();
de_ctx->spm_matcher = SinglePatternMatchDefaultMatcher();
+
+ de_ctx->spm_global_thread_ctx = SpmInitGlobalThreadCtx(de_ctx->spm_matcher);
+ if (de_ctx->spm_global_thread_ctx == NULL) {
+ SCLogDebug("Unable to alloc SpmGlobalThreadCtx.");
+ goto error;
+ }
+
DetectEngineCtxLoadConf(de_ctx);
SigGroupHeadHashInit(de_ctx);
SigGroupCleanup(de_ctx);
+ SpmDestroyGlobalThreadCtx(de_ctx->spm_global_thread_ctx);
+
MpmFactoryDeRegisterAllMpmCtxProfiles(de_ctx);
DetectEngineCtxFreeThreadKeywordData(de_ctx);
PmqSetup(&det_ctx->pmq);
+ det_ctx->spm_thread_ctx = SpmMakeThreadCtx(de_ctx->spm_global_thread_ctx);
+ if (det_ctx->spm_thread_ctx == NULL) {
+ return TM_ECODE_FAILED;
+ }
+
/* sized to the max of our sgh settings. A max setting of 0 implies that all
* sgh's have: sgh->non_mpm_store_cnt == 0 */
if (de_ctx->non_mpm_store_cnt_max > 0) {
PmqFree(&det_ctx->pmq);
+ if (det_ctx->spm_thread_ctx != NULL) {
+ SpmDestroyThreadCtx(det_ctx->spm_thread_ctx);
+ }
+
if (det_ctx->non_mpm_id_array != NULL)
SCFree(det_ctx->non_mpm_id_array);
if (hcbd->content != NULL)
SCFree(hcbd->content);
- BoyerMooreCtxDeInit(hcbd->bm_ctx);
+ SpmDestroyCtx(hcbd->spm_ctx);
SCFree(hcbd);
SCReturn;
if (hhhd->content != NULL)
SCFree(hhhd->content);
- BoyerMooreCtxDeInit(hhhd->bm_ctx);
+ SpmDestroyCtx(hhhd->spm_ctx);
SCFree(hhhd);
return;
if (hrhhd->content != NULL)
SCFree(hrhhd->content);
- BoyerMooreCtxDeInit(hrhhd->bm_ctx);
+ SpmDestroyCtx(hrhhd->spm_ctx);
SCFree(hrhhd);
return;
if (hsbd->content != NULL)
SCFree(hsbd->content);
- BoyerMooreCtxDeInit(hsbd->bm_ctx);
+ SpmDestroyCtx(hsbd->spm_ctx);
SCFree(hsbd);
SCReturn;
if (huad->content != NULL)
SCFree(huad->content);
- BoyerMooreCtxDeInit(huad->bm_ctx);
+ SpmDestroyCtx(huad->spm_ctx);
SCFree(huad);
return;
SCLogError(SC_ERR_INVALID_SIGNATURE, "can't use multiple nocase modifiers with the same content");
goto end;
}
+
+ /* 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 */
- BoyerMooreCtxToNocase(cd->bm_ctx, cd->content, cd->content_len);
+ 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) {
+ goto end;
+ }
ret = 0;
end:
#include "util-unittest-helper.h"
#include "util-binsearch.h"
#include "util-spm.h"
-#include "util-spm-bm.h"
#include "conf.h"
/* prototypes */
if (cd == NULL)
SCReturn;
- BoyerMooreCtxDeInit(cd->bm_ctx);
+ SpmDestroyCtx(cd->spm_ctx);
SCFree(cd);
SCReturn;
#include "detect-content.h"
-#include "util-spm-bm.h"
#include "app-layer-htp.h"
/* prototypes */
#include "packet-queue.h"
#include "util-mpm.h"
+#include "util-spm.h"
#include "util-hash.h"
#include "util-hashlist.h"
#include "util-debug.h"
uint16_t mpm_matcher; /**< mpm matcher this ctx uses */
uint16_t spm_matcher; /**< spm matcher this ctx uses */
+ /* spm thread context prototype, built as spm matchers are constructed and
+ * later used to construct thread context for each thread. */
+ SpmGlobalThreadCtx *spm_global_thread_ctx;
+
/* Config options */
uint16_t max_uniq_toclient_groups;
MpmThreadCtx mtcs; /**< thread ctx for stream mpm */
PatternMatcherQueue pmq;
+ /** SPM thread context used for scanning. This has been cloned from the
+ * prototype held by DetectEngineCtx. */
+ SpmThreadCtx *spm_thread_ctx;
+
/** ip only rules ctx */
DetectEngineIPOnlyThreadCtx io_ctx;
#ifdef __SC_CUDA_SUPPORT__
MpmCudaEnvironmentSetup();
#endif
+ SpmTableSetup();
AppLayerSetup();
#ifdef __SC_CUDA_SUPPORT__
MpmCudaEnvironmentSetup();
#endif
+ SpmTableSetup();
switch (suri->checksum_validation) {
case 0:
#include "suricata.h"
#include "util-spm-bm.h"
+#include "util-spm.h"
#include "util-debug.h"
#include "util-error.h"
#include "util-memcpy.h"
return NULL;
}
+typedef struct SpmBmCtx_ {
+ BmCtx *bm_ctx;
+ uint8_t *needle;
+ uint16_t needle_len;
+ int nocase;
+} SpmBmCtx;
+
+static SpmCtx *BMInitCtx(const uint8_t *needle, uint16_t needle_len, int nocase,
+ SpmGlobalThreadCtx *global_thread_ctx)
+{
+ SpmCtx *ctx = SCMalloc(sizeof(SpmCtx));
+ if (ctx == NULL) {
+ SCLogDebug("Unable to alloc SpmCtx.");
+ return NULL;
+ }
+ memset(ctx, 0, sizeof(*ctx));
+ ctx->matcher = SPM_BM;
+
+ SpmBmCtx *sctx = SCMalloc(sizeof(SpmBmCtx));
+ if (sctx == NULL) {
+ SCLogDebug("Unable to alloc SpmBmCtx.");
+ SCFree(ctx);
+ return NULL;
+ }
+ memset(sctx, 0, sizeof(*sctx));
+
+ sctx->needle = SCMalloc(needle_len);
+ if (sctx->needle == NULL) {
+ SCLogDebug("Unable to alloc string.");
+ SCFree(sctx);
+ SCFree(ctx);
+ return NULL;
+ }
+ memcpy(sctx->needle, needle, needle_len);
+ sctx->needle_len = needle_len;
+
+ if (nocase) {
+ sctx->bm_ctx = BoyerMooreNocaseCtxInit(sctx->needle, sctx->needle_len);
+ sctx->nocase = 1;
+ } else {
+ sctx->bm_ctx = BoyerMooreCtxInit(sctx->needle, sctx->needle_len);
+ sctx->nocase = 0;
+ }
+
+ ctx->ctx = sctx;
+ return ctx;
+}
+
+static void BMDestroyCtx(SpmCtx *ctx)
+{
+ if (ctx == NULL) {
+ return;
+ }
+
+ SpmBmCtx *sctx = ctx->ctx;
+ if (sctx != NULL) {
+ BoyerMooreCtxDeInit(sctx->bm_ctx);
+ if (sctx->needle != NULL) {
+ SCFree(sctx->needle);
+ }
+ SCFree(sctx);
+ }
+
+ SCFree(ctx);
+}
+
+static uint8_t *BMScan(const SpmCtx *ctx, SpmThreadCtx *thread_ctx,
+ const uint8_t *haystack, uint16_t haystack_len)
+{
+ const SpmBmCtx *sctx = ctx->ctx;
+
+ if (sctx->nocase) {
+ return BoyerMooreNocase(sctx->needle, sctx->needle_len, haystack,
+ haystack_len, sctx->bm_ctx);
+ } else {
+ return BoyerMoore(sctx->needle, sctx->needle_len, haystack,
+ haystack_len, sctx->bm_ctx);
+ }
+}
+
+static SpmGlobalThreadCtx *BMInitGlobalThreadCtx(void)
+{
+ SpmGlobalThreadCtx *global_thread_ctx = SCMalloc(sizeof(SpmGlobalThreadCtx));
+ if (global_thread_ctx == NULL) {
+ SCLogDebug("Unable to alloc SpmThreadCtx.");
+ return NULL;
+ }
+ memset(global_thread_ctx, 0, sizeof(*global_thread_ctx));
+ global_thread_ctx->matcher = SPM_BM;
+ return global_thread_ctx;
+}
+
+static void BMDestroyGlobalThreadCtx(SpmGlobalThreadCtx *global_thread_ctx)
+{
+ if (global_thread_ctx == NULL) {
+ return;
+ }
+ SCFree(global_thread_ctx);
+}
+
+static void BMDestroyThreadCtx(SpmThreadCtx *thread_ctx)
+{
+ if (thread_ctx == NULL) {
+ return;
+ }
+ SCFree(thread_ctx);
+}
+
+static SpmThreadCtx *BMMakeThreadCtx(const SpmGlobalThreadCtx *global_thread_ctx) {
+ SpmThreadCtx *thread_ctx = SCMalloc(sizeof(SpmThreadCtx));
+ if (thread_ctx == NULL) {
+ SCLogDebug("Unable to alloc SpmThreadCtx.");
+ return NULL;
+ }
+ memset(thread_ctx, 0, sizeof(*thread_ctx));
+ thread_ctx->matcher = SPM_BM;
+ return thread_ctx;
+}
+
+void SpmBMRegister(void)
+{
+ spm_table[SPM_BM].name = "bm";
+ spm_table[SPM_BM].InitGlobalThreadCtx = BMInitGlobalThreadCtx;
+ spm_table[SPM_BM].DestroyGlobalThreadCtx = BMDestroyGlobalThreadCtx;
+ spm_table[SPM_BM].MakeThreadCtx = BMMakeThreadCtx;
+ spm_table[SPM_BM].DestroyThreadCtx = BMDestroyThreadCtx;
+ spm_table[SPM_BM].InitCtx = BMInitCtx;
+ spm_table[SPM_BM].DestroyCtx = BMDestroyCtx;
+ spm_table[SPM_BM].Scan = BMScan;
+}
uint8_t *BoyerMooreNocase(const uint8_t *x, uint16_t m, const uint8_t *y, int32_t n, BmCtx *bm_ctx);
void BoyerMooreCtxDeInit(BmCtx *);
+void SpmBMRegister(void);
+
#endif /* __UTIL_SPM_BM__ */
* \brief Returns the single pattern matcher algorithm to be used, based on the
* spm-algo setting in yaml.
*/
-uint16_t SinglePatternMatchDefaultMatcher(void) {
+uint16_t SinglePatternMatchDefaultMatcher(void)
+{
char *spm_algo;
if ((ConfGet("spm-algo", &spm_algo)) == 1) {
- if (strcmp("bm", spm_algo) == 0) {
- return SPM_BM;
+ if (spm_algo != NULL) {
+ for (uint16_t i = 0; i < SPM_TABLE_SIZE; i++) {
+ if (spm_table[i].name == NULL) {
+ continue;
+ }
+ if (strcmp(spm_table[i].name, spm_algo) == 0) {
+ return i;
+ }
+ }
}
SCLogError(SC_ERR_INVALID_YAML_CONF_ENTRY,
return SPM_BM; /* default to Boyer-Moore */
}
+void SpmTableSetup(void)
+{
+ memset(spm_table, 0, sizeof(spm_table));
+
+ SpmBMRegister();
+}
+
+SpmGlobalThreadCtx *SpmInitGlobalThreadCtx(uint16_t matcher)
+{
+ return spm_table[matcher].InitGlobalThreadCtx();
+}
+
+void SpmDestroyGlobalThreadCtx(SpmGlobalThreadCtx *global_thread_ctx)
+{
+ uint16_t matcher = global_thread_ctx->matcher;
+ spm_table[matcher].DestroyGlobalThreadCtx(global_thread_ctx);
+}
+
+SpmThreadCtx *SpmMakeThreadCtx(const SpmGlobalThreadCtx *global_thread_ctx)
+{
+ uint16_t matcher = global_thread_ctx->matcher;
+ return spm_table[matcher].MakeThreadCtx(global_thread_ctx);
+}
+
+void SpmDestroyThreadCtx(SpmThreadCtx *thread_ctx)
+{
+ uint16_t matcher = thread_ctx->matcher;
+ spm_table[matcher].DestroyThreadCtx(thread_ctx);
+}
+
+SpmCtx *SpmInitCtx(const uint8_t *needle, uint16_t needle_len, int nocase,
+ SpmGlobalThreadCtx *global_thread_ctx)
+{
+ uint16_t matcher = global_thread_ctx->matcher;
+ return spm_table[matcher].InitCtx(needle, needle_len, nocase,
+ global_thread_ctx);
+}
+
+void SpmDestroyCtx(SpmCtx *ctx)
+{
+ spm_table[ctx->matcher].DestroyCtx(ctx);
+}
+
+uint8_t *SpmScan(const SpmCtx *ctx, SpmThreadCtx *thread_ctx,
+ const uint8_t *haystack, uint16_t haystack_len)
+{
+ uint16_t matcher = ctx->matcher;
+ return spm_table[matcher].Scan(ctx, thread_ctx, haystack, haystack_len);
+}
+
/**
* Wrappers for building context and searching (Bs2Bm and boyermoore)
* Use them if you cant store the context
return 1;
}
+/* Unit tests for new SPM API. */
+
+#define SPM_NO_MATCH UINT32_MAX
+
+/* Helper structure describing a particular search. */
+typedef struct SpmTestData_ {
+ const char *needle;
+ uint16_t needle_len;
+ const char *haystack;
+ uint16_t haystack_len;
+ int nocase;
+ uint32_t match_offset; /* offset in haystack, or SPM_NO_MATCH. */
+} SpmTestData;
+
+/* Helper function to conduct a search with a particular SPM matcher. */
+static int SpmTestSearch(const SpmTestData *d, uint16_t matcher)
+{
+ int ret = 1;
+ SpmGlobalThreadCtx *global_thread_ctx = NULL;
+ SpmThreadCtx *thread_ctx = NULL;
+ SpmCtx *ctx = NULL;
+ uint8_t *found = NULL;
+
+ global_thread_ctx = SpmInitGlobalThreadCtx(matcher);
+ if (global_thread_ctx == NULL) {
+ ret = 0;
+ goto exit;
+ }
+
+ ctx = SpmInitCtx((const uint8_t *)d->needle, d->needle_len, d->nocase,
+ global_thread_ctx);
+ if (ctx == NULL) {
+ ret = 0;
+ goto exit;
+ }
+
+ thread_ctx = SpmMakeThreadCtx(global_thread_ctx);
+ if (thread_ctx == NULL) {
+ ret = 0;
+ goto exit;
+ }
+
+ found = SpmScan(ctx, thread_ctx, (const uint8_t *)d->haystack,
+ d->haystack_len);
+ if (found == NULL) {
+ if (d->match_offset != SPM_NO_MATCH) {
+ printf(" should have matched at %" PRIu32 " but didn't\n",
+ d->match_offset);
+ ret = 0;
+ }
+ } else {
+ uint32_t offset = (uint32_t)(found - (const uint8_t *)d->haystack);
+ if (offset != d->match_offset) {
+ printf(" should have matched at %" PRIu32
+ " but matched at %" PRIu32 "\n",
+ d->match_offset, offset);
+ ret = 0;
+ }
+ }
+
+exit:
+ SpmDestroyCtx(ctx);
+ SpmDestroyThreadCtx(thread_ctx);
+ SpmDestroyGlobalThreadCtx(global_thread_ctx);
+ return ret;
+}
+
+int SpmSearchTest01() {
+ SpmTableSetup();
+ printf("\n");
+
+ /* Each of the following tests will be run against every registered SPM
+ * algorithm. */
+
+ static const SpmTestData data[] = {
+ /* Some trivial single-character case/nocase tests */
+ {"a", 1, "a", 1, 0, 0},
+ {"a", 1, "A", 1, 1, 0},
+ {"A", 1, "A", 1, 0, 0},
+ {"A", 1, "a", 1, 1, 0},
+ {"a", 1, "A", 1, 0, SPM_NO_MATCH},
+ {"A", 1, "a", 1, 0, SPM_NO_MATCH},
+ /* Nulls and odd characters */
+ {"\x00", 1, "test\x00test", 9, 0, 4},
+ {"\x00", 1, "testtest", 8, 0, SPM_NO_MATCH},
+ {"\n", 1, "new line\n", 9, 0, 8},
+ {"\n", 1, "new line\x00\n", 10, 0, 9},
+ {"\xff", 1, "abcdef\xff", 7, 0, 6},
+ {"\xff", 1, "abcdef\xff", 7, 1, 6},
+ {"$", 1, "dollar$", 7, 0, 6},
+ {"^", 1, "caret^", 6, 0, 5},
+ /* Longer literals */
+ {"Suricata", 8, "This is a Suricata test", 23, 0, 10},
+ {"Suricata", 8, "This is a suricata test", 23, 1, 10},
+ {"Suricata", 8, "This is a suriCATA test", 23, 1, 10},
+ {"suricata", 8, "This is a Suricata test", 23, 0, SPM_NO_MATCH},
+ {"Suricata", 8, "This is a Suricat_ test", 23, 0, SPM_NO_MATCH},
+ {"Suricata", 8, "This is a _uricata test", 23, 0, SPM_NO_MATCH},
+ /* First occurrence with the correct case should match */
+ {"foo", 3, "foofoofoo", 9, 0, 0},
+ {"foo", 3, "_foofoofoo", 9, 0, 1},
+ {"FOO", 3, "foofoofoo", 9, 1, 0},
+ {"FOO", 3, "_foofoofoo", 9, 1, 1},
+ {"FOO", 3, "foo Foo FOo fOo foO FOO", 23, 0, 20},
+ {"foo", 3, "Foo FOo fOo foO FOO foo", 23, 0, 20},
+ };
+
+ int ret = 1;
+
+ uint16_t matcher;
+ for (matcher = 0; matcher < SPM_TABLE_SIZE; matcher++) {
+ const SpmTableElmt *m = &spm_table[matcher];
+ if (m->name == NULL) {
+ continue;
+ }
+ printf("matcher: %s\n", m->name);
+
+ uint32_t i;
+ for (i = 0; i < sizeof(data)/sizeof(data[0]); i++) {
+ const SpmTestData *d = &data[i];
+ if (SpmTestSearch(d, matcher) == 0) {
+ printf(" test %" PRIu32 ": fail\n", i);
+ ret = 0;
+ }
+ }
+ printf(" %" PRIu32 " tests passed\n", i);
+ }
+
+ return ret;
+}
+
+int SpmSearchTest02() {
+ SpmTableSetup();
+ printf("\n");
+
+ /* Test that we can find needles of various lengths at various alignments
+ * in the haystack. Note that these are passed to strlen. */
+
+ static const char* needles[] = {
+ /* Single bytes */
+ "a", "b", "c", ":", "/", "\x7f", "\xff",
+ /* Repeats */
+ "aa", "aaa", "aaaaaaaaaaaaaaaaaaaaaaa",
+ /* Longer literals */
+ "suricata", "meerkat", "aardvark", "raptor", "marmot", "lemming",
+ /* Mixed case */
+ "Suricata", "CAPS LOCK", "mIxEd cAsE",
+ };
+
+ int ret = 1;
+
+ uint16_t matcher;
+ for (matcher = 0; matcher < SPM_TABLE_SIZE; matcher++) {
+ const SpmTableElmt *m = &spm_table[matcher];
+ if (m->name == NULL) {
+ continue;
+ }
+ printf("matcher: %s\n", m->name);
+
+ SpmTestData d;
+
+ uint32_t i;
+ for (i = 0; i < sizeof(needles) / sizeof(needles[0]); i++) {
+ const char *needle = needles[i];
+ uint16_t prefix;
+ for (prefix = 0; prefix < 32; prefix++) {
+ d.needle = needle;
+ d.needle_len = strlen(needle);
+ uint16_t haystack_len = prefix + d.needle_len;
+ char *haystack = SCMalloc(haystack_len);
+ if (haystack == NULL) {
+ printf("alloc failure\n");
+ return 0;
+ }
+ memset(haystack, ' ', haystack_len);
+ memcpy(haystack + prefix, d.needle, d.needle_len);
+ d.haystack = haystack;
+ d.haystack_len = haystack_len;
+ d.nocase = 0;
+ d.match_offset = prefix;
+
+ /* Case-sensitive scan */
+ if (SpmTestSearch(&d, matcher) == 0) {
+ printf(" test %" PRIu32 ": fail (case-sensitive)\n", i);
+ ret = 0;
+ }
+
+ /* Case-insensitive scan */
+ d.nocase = 1;
+ uint16_t j;
+ for (j = 0; j < haystack_len; j++) {
+ haystack[j] = toupper(haystack[j]);
+ }
+ if (SpmTestSearch(&d, matcher) == 0) {
+ printf(" test %" PRIu32 ": fail (case-insensitive)\n", i);
+ ret = 0;
+ }
+
+ SCFree(haystack);
+ }
+ }
+ printf(" %" PRIu32 " tests passed\n", i);
+ }
+
+ return ret;
+}
+
#endif
/* Register unittests */
UtRegisterTest("UtilSpmSearchOffsetsNocaseTest01",
UtilSpmSearchOffsetsNocaseTest01);
+ /* new SPM API */
+ UtRegisterTest("SpmSearchTest01", SpmSearchTest01);
+ UtRegisterTest("SpmSearchTest02", SpmSearchTest02);
+
#ifdef ENABLE_SEARCH_STATS
/* Give some stats searching given a prepared context (look at the wrappers) */
UtRegisterTest("UtilSpmSearchStatsTest01", UtilSpmSearchStatsTest01);
enum {
SPM_BM, /* Boyer-Moore */
/* Other SPM matchers will go here. */
+ SPM_TABLE_SIZE
};
uint16_t SinglePatternMatchDefaultMatcher(void);
+/** Structure holding an immutable "built" SPM matcher (such as the Boyer-Moore
+ * tables, Hyperscan database etc) that is passed to the Scan call. */
+typedef struct SpmCtx_ {
+ uint16_t matcher;
+ void *ctx;
+} SpmCtx;
+
+/** Structure holding a global prototype for per-thread scratch space, passed
+ * to each InitCtx call. */
+typedef struct SpmGlobalThreadCtx_ {
+ uint16_t matcher;
+ void *ctx;
+} SpmGlobalThreadCtx;
+
+/** Structure holding some mutable per-thread space for use by a matcher at
+ * scan time. Constructed from SpmGlobalThreadCtx by the MakeThreadCtx call. */
+typedef struct SpmThreadCtx_ {
+ uint16_t matcher;
+ void *ctx;
+} SpmThreadCtx;
+
+typedef struct SpmTableElmt_ {
+ const char *name;
+ SpmGlobalThreadCtx *(*InitGlobalThreadCtx)(void);
+ void (*DestroyGlobalThreadCtx)(SpmGlobalThreadCtx *g_thread_ctx);
+ SpmThreadCtx *(*MakeThreadCtx)(const SpmGlobalThreadCtx *g_thread_ctx);
+ void (*DestroyThreadCtx)(SpmThreadCtx *thread_ctx);
+ SpmCtx *(*InitCtx)(const uint8_t *needle, uint16_t needle_len, int nocase,
+ SpmGlobalThreadCtx *g_thread_ctx);
+ void (*DestroyCtx)(SpmCtx *);
+ uint8_t *(*Scan)(const SpmCtx *ctx, SpmThreadCtx *thread_ctx,
+ const uint8_t *haystack, uint16_t haystack_len);
+} SpmTableElmt;
+
+SpmTableElmt spm_table[SPM_TABLE_SIZE];
+
+void SpmTableSetup(void);
+
+SpmGlobalThreadCtx *SpmInitGlobalThreadCtx(uint16_t matcher);
+
+void SpmDestroyGlobalThreadCtx(SpmGlobalThreadCtx *g_thread_ctx);
+
+SpmThreadCtx *SpmMakeThreadCtx(const SpmGlobalThreadCtx *g_thread_ctx);
+
+void SpmDestroyThreadCtx(SpmThreadCtx *thread_ctx);
+
+SpmCtx *SpmInitCtx(const uint8_t *needle, uint16_t needle_len, int nocase,
+ SpmGlobalThreadCtx *g_thread_ctx);
+
+void SpmDestroyCtx(SpmCtx *ctx);
+
+uint8_t *SpmScan(const SpmCtx *ctx, SpmThreadCtx *thread_ctx,
+ const uint8_t *haystack, uint16_t haystack_len);
+
/** Default algorithm to use: Boyer Moore */
uint8_t *Bs2bmSearch(const uint8_t *text, uint32_t textlen, const uint8_t *needle, uint16_t needlelen);
uint8_t *Bs2bmNocaseSearch(const uint8_t *text, uint32_t textlen, const uint8_t *needle, uint16_t needlelen);