diff --git a/src/detect-dataset.c b/src/detect-dataset.c index ae23925f2c11..bd79e79c13d3 100644 --- a/src/detect-dataset.c +++ b/src/detect-dataset.c @@ -34,6 +34,7 @@ #include "detect-engine.h" #include "detect-engine-mpm.h" #include "detect-engine-state.h" +#include "detect-engine-content-inspection.h" #include "util-debug.h" #include "util-print.h" @@ -47,11 +48,95 @@ #define DETECT_DATASET_CMD_ISNOTSET 2 #define DETECT_DATASET_CMD_ISSET 3 -int DetectDatasetMatch (ThreadVars *, DetectEngineThreadCtx *, Packet *, - const Signature *, const SigMatchCtx *); +#define DMD_CAP_STEP 16 +typedef struct DetectDatasetMatchData_ { + uint32_t nb; + uint32_t *local_ids; +} DetectDatasetMatchData; + static int DetectDatasetSetup (DetectEngineCtx *, Signature *, const char *); void DetectDatasetFree (DetectEngineCtx *, void *); +static int DetectDatasetTxMatch(DetectEngineThreadCtx *det_ctx, Flow *f, uint8_t flags, void *state, + void *txv, const Signature *s, const SigMatchCtx *ctx) +{ + const DetectDatasetData *sd = (DetectDatasetData *)ctx; + // This is only run for DETECT_SM_LIST_POSTMATCH + DEBUG_VALIDATE_BUG_ON(sd->cmd != DETECT_DATASET_CMD_SET && sd->cmd != DETECT_DATASET_CMD_UNSET); + + // retrieve the app inspection engine associated to the list + DetectEngineAppInspectionEngine *a = s->app_inspect; + while (a != NULL) { + // also check alproto as http.uri as 2 engines : http1 and http2 + if (a->sm_list == sd->list && a->alproto == f->alproto) { + if (a->v2.Callback == DetectEngineInspectBufferGeneric) { + // simple buffer, get data again + const InspectionBuffer *buffer = + a->v2.GetData(det_ctx, a->v2.transforms, f, flags, txv, sd->list); + if (buffer != NULL && buffer->inspect != NULL) { + if (sd->cmd == DETECT_DATASET_CMD_SET) { + DatasetAdd(sd->set, buffer->inspect, buffer->inspect_len); + } else if (sd->cmd == DETECT_DATASET_CMD_UNSET) { + DatasetRemove(sd->set, buffer->inspect, buffer->inspect_len); + } + } + } else if (a->v2.Callback == DetectEngineInspectMultiBufferGeneric) { + DetectDatasetMatchData *dmd = + (DetectDatasetMatchData *)DetectThreadCtxGetKeywordThreadCtx( + det_ctx, sd->thread_ctx_id); + DEBUG_VALIDATE_BUG_ON(dmd == NULL); + uint32_t local_id = 0; + for (uint32_t i = 0; i < dmd->nb; i++) { + local_id = dmd->local_ids[i]; + InspectionBuffer *buffer = a->v2.GetMultiData( + det_ctx, a->v2.transforms, f, flags, txv, sd->list, local_id); + DEBUG_VALIDATE_BUG_ON(buffer == NULL || buffer->inspect == NULL); + if (sd->cmd == DETECT_DATASET_CMD_SET) { + DatasetAdd(sd->set, buffer->inspect, buffer->inspect_len); + } else if (sd->cmd == DETECT_DATASET_CMD_UNSET) { + DatasetRemove(sd->set, buffer->inspect, buffer->inspect_len); + } + } + } + return 0; + } + a = a->next; + } + return 0; +} + +static int DetectDatasetMatch( + DetectEngineThreadCtx *det_ctx, Packet *p, const Signature *s, const SigMatchCtx *ctx) +{ + const DetectDatasetData *sd = (DetectDatasetData *)ctx; + // This is only run for DETECT_SM_LIST_POSTMATCH + DEBUG_VALIDATE_BUG_ON(sd->cmd != DETECT_DATASET_CMD_SET && sd->cmd != DETECT_DATASET_CMD_UNSET); + + // retrieve the pkt inspection engine associated to the list if any (ie if list is not a app + // inspection engine) + DetectEnginePktInspectionEngine *e = s->pkt_inspect; + while (e) { + if (e->sm_list == sd->list) { + if (e->v1.Callback == DetectEngineInspectPktBufferGeneric) { + const InspectionBuffer *buffer = + e->v1.GetData(det_ctx, e->v1.transforms, p, sd->list); + // get simple data again and add it + if (buffer != NULL && buffer->inspect != NULL) { + if (sd->cmd == DETECT_DATASET_CMD_SET) { + DatasetAdd(sd->set, buffer->inspect, buffer->inspect_len); + } else if (sd->cmd == DETECT_DATASET_CMD_UNSET) { + DatasetRemove(sd->set, buffer->inspect, buffer->inspect_len); + } + } + } + return 0; + } + e = e->next; + } + // return value is unused for postmatch functions + return 0; +} + void DetectDatasetRegister (void) { sigmatch_table[DETECT_DATASET].name = "dataset"; @@ -59,54 +144,84 @@ void DetectDatasetRegister (void) sigmatch_table[DETECT_DATASET].url = "/rules/dataset-keywords.html#dataset"; sigmatch_table[DETECT_DATASET].Setup = DetectDatasetSetup; sigmatch_table[DETECT_DATASET].Free = DetectDatasetFree; + // callbacks for postmatch + sigmatch_table[DETECT_DATASET].AppLayerTxMatch = DetectDatasetTxMatch; + sigmatch_table[DETECT_DATASET].Match = DetectDatasetMatch; } /* 1 match 0 no match - -1 can't match */ -int DetectDatasetBufferMatch(DetectEngineThreadCtx *det_ctx, - const DetectDatasetData *sd, - const uint8_t *data, const uint32_t data_len) +uint8_t DetectDatasetBufferMatch(DetectEngineThreadCtx *det_ctx, const DetectDatasetData *sd, + const uint8_t *data, const uint32_t data_len, uint32_t local_id) { if (data == NULL || data_len == 0) - return 0; + return DETECT_ENGINE_INSPECT_SIG_NO_MATCH; + int r = DatasetLookup(sd->set, data, data_len); + SCLogDebug("r %d", r); switch (sd->cmd) { case DETECT_DATASET_CMD_ISSET: { - //PrintRawDataFp(stdout, data, data_len); - int r = DatasetLookup(sd->set, data, data_len); - SCLogDebug("r %d", r); if (r == 1) - return 1; + return DETECT_ENGINE_INSPECT_SIG_MATCH; break; } case DETECT_DATASET_CMD_ISNOTSET: { - //PrintRawDataFp(stdout, data, data_len); - int r = DatasetLookup(sd->set, data, data_len); - SCLogDebug("r %d", r); if (r < 1) - return 1; + return DETECT_ENGINE_INSPECT_SIG_MATCH; break; } case DETECT_DATASET_CMD_SET: { - //PrintRawDataFp(stdout, data, data_len); - int r = DatasetAdd(sd->set, data, data_len); - if (r == 1) - return 1; - break; + if (r == 1) { + /* Do not match if data is already in set */ + return DETECT_ENGINE_INSPECT_SIG_NO_MATCH; + } + // DatasetAdd will be performed postmatch if the rest of the sig completely matched + DetectDatasetMatchData *dmd = + (DetectDatasetMatchData *)DetectThreadCtxGetKeywordThreadCtx( + det_ctx, sd->thread_ctx_id); + if (dmd == NULL) { + return DETECT_ENGINE_INSPECT_SIG_NO_MATCH; + } + if (dmd->nb % DMD_CAP_STEP == 0) { + void *tmp = SCRealloc(dmd->local_ids, sizeof(uint32_t) * (dmd->nb + DMD_CAP_STEP)); + if (tmp == NULL) { + return DETECT_ENGINE_INSPECT_SIG_NO_MATCH; + } + dmd->local_ids = tmp; + } + dmd->local_ids[dmd->nb] = local_id; + dmd->nb++; + return DETECT_ENGINE_INSPECT_SIG_MATCH_MORE_BUF; } case DETECT_DATASET_CMD_UNSET: { - int r = DatasetRemove(sd->set, data, data_len); - if (r == 1) - return 1; - break; + if (r == 0) { + /* Do not match if data is not in set */ + return DETECT_ENGINE_INSPECT_SIG_NO_MATCH; + } + // DatasetRemove will be performed postmatch if the rest of the sig completely matched + DetectDatasetMatchData *dmd = + (DetectDatasetMatchData *)DetectThreadCtxGetKeywordThreadCtx( + det_ctx, sd->thread_ctx_id); + if (dmd == NULL) { + return DETECT_ENGINE_INSPECT_SIG_NO_MATCH; + } + if (dmd->nb % DMD_CAP_STEP == 0) { + void *tmp = SCRealloc(dmd->local_ids, sizeof(uint32_t) * (dmd->nb + DMD_CAP_STEP)); + if (tmp == NULL) { + return DETECT_ENGINE_INSPECT_SIG_NO_MATCH; + } + dmd->local_ids = tmp; + } + dmd->local_ids[dmd->nb] = local_id; + dmd->nb++; + return DETECT_ENGINE_INSPECT_SIG_MATCH_MORE_BUF; } default: DEBUG_VALIDATE_BUG_ON("unknown dataset command"); } - return 0; + return DETECT_ENGINE_INSPECT_SIG_NO_MATCH; } static int DetectDatasetParse(const char *str, char *cmd, int cmd_len, char *name, int name_len, @@ -352,6 +467,24 @@ static int SetupSavePath(const DetectEngineCtx *de_ctx, return 0; } +static void *DetectDatasetMatchDataThreadInit(void *data) +{ + DetectDatasetMatchData *scmd = SCCalloc(1, sizeof(DetectDatasetMatchData)); + // make cocci happy + if (unlikely(scmd == NULL)) + return NULL; + return scmd; +} + +static void DetectDatasetMatchDataThreadFree(void *ctx) +{ + if (ctx) { + DetectDatasetMatchData *scmd = (DetectDatasetMatchData *)ctx; + SCFree(scmd->local_ids); + SCFree(scmd); + } +} + int DetectDatasetSetup (DetectEngineCtx *de_ctx, Signature *s, const char *rawstr) { DetectDatasetData *cd = NULL; @@ -428,8 +561,30 @@ int DetectDatasetSetup (DetectEngineCtx *de_ctx, Signature *s, const char *rawst SCLogDebug("cmd %s, name %s", cmd_str, strlen(name) ? name : "(none)"); - /* Okay so far so good, lets get this into a SigMatch - * and put it in the Signature. */ + if (cmd == DETECT_DATASET_CMD_SET || cmd == DETECT_DATASET_CMD_UNSET) { + if (s->init_data->curbuf) + s->init_data->curbuf->delay_postmatch = true; + cd->thread_ctx_id = DetectRegisterThreadCtxFuncs(de_ctx, "dataset", + DetectDatasetMatchDataThreadInit, (void *)cd, DetectDatasetMatchDataThreadFree, 0); + if (cd->thread_ctx_id == -1) + goto error; + + // for set operation, we need one match, and one postmatch + DetectDatasetData *scd = SCCalloc(1, sizeof(DetectDatasetData)); + if (unlikely(scd == NULL)) + goto error; + + scd->set = set; + scd->cmd = cmd; + // remember the list used by match to retrieve the buffer in postmatch + scd->list = list; + scd->thread_ctx_id = cd->thread_ctx_id; + if (SigMatchAppendSMToList(de_ctx, s, DETECT_DATASET, (SigMatchCtx *)scd, + DETECT_SM_LIST_POSTMATCH) == NULL) { + SCFree(scd); + goto error; + } + } if (SigMatchAppendSMToList(de_ctx, s, DETECT_DATASET, (SigMatchCtx *)cd, list) == NULL) { goto error; diff --git a/src/detect-dataset.h b/src/detect-dataset.h index 047a5b11cb2f..6e60b544a90c 100644 --- a/src/detect-dataset.h +++ b/src/detect-dataset.h @@ -29,11 +29,13 @@ typedef struct DetectDatasetData_ { Dataset *set; uint8_t cmd; + // for postmatch to retrieve the buffer(s) + int list; + int thread_ctx_id; } DetectDatasetData; -int DetectDatasetBufferMatch(DetectEngineThreadCtx *det_ctx, - const DetectDatasetData *sd, - const uint8_t *data, const uint32_t data_len); +uint8_t DetectDatasetBufferMatch(DetectEngineThreadCtx *det_ctx, const DetectDatasetData *sd, + const uint8_t *data, const uint32_t data_len, uint32_t local_id); /* prototypes */ void DetectDatasetRegister (void); diff --git a/src/detect-engine-content-inspection.c b/src/detect-engine-content-inspection.c index d4dab42816d5..0ed3e028f083 100644 --- a/src/detect-engine-content-inspection.c +++ b/src/detect-engine-content-inspection.c @@ -107,7 +107,7 @@ static int DetectEngineContentInspectionInternal(DetectEngineThreadCtx *det_ctx, struct DetectEngineContentInspectionCtx *ctx, const Signature *s, const SigMatchData *smd, Packet *p, Flow *f, const uint8_t *buffer, const uint32_t buffer_len, const uint32_t stream_start_offset, const uint8_t flags, - const enum DetectContentInspectionType inspection_mode) + const enum DetectContentInspectionType inspection_mode, uint32_t local_id) { SCEnter(); KEYWORD_PROFILING_START; @@ -358,9 +358,9 @@ static int DetectEngineContentInspectionInternal(DetectEngineThreadCtx *det_ctx, * search for another occurrence of this content and see * if the others match then until we run out of matches */ int r = DetectEngineContentInspectionInternal(det_ctx, ctx, s, smd + 1, p, f, - buffer, buffer_len, stream_start_offset, flags, inspection_mode); - if (r == 1) { - SCReturnInt(1); + buffer, buffer_len, stream_start_offset, flags, inspection_mode, local_id); + if (r == 1 || r == DETECT_ENGINE_INSPECT_SIG_MATCH_MORE_BUF) { + SCReturnInt(r); } else if (r == -1) { SCLogDebug("'next sm' said to discontinue this right now"); SCReturnInt(-1); @@ -464,9 +464,9 @@ static int DetectEngineContentInspectionInternal(DetectEngineThreadCtx *det_ctx, * search for another occurrence of this pcre and see * if the others match, until we run out of matches */ r = DetectEngineContentInspectionInternal(det_ctx, ctx, s, smd + 1, p, f, buffer, - buffer_len, stream_start_offset, flags, inspection_mode); - if (r == 1) { - SCReturnInt(1); + buffer_len, stream_start_offset, flags, inspection_mode, local_id); + if (r == 1 || r == DETECT_ENGINE_INSPECT_SIG_MATCH_MORE_BUF) { + SCReturnInt(r); } else if (r == -1) { SCReturnInt(-1); } @@ -615,10 +615,23 @@ static int DetectEngineContentInspectionInternal(DetectEngineThreadCtx *det_ctx, //PrintRawDataFp(stdout, buffer, buffer_len); const DetectDatasetData *sd = (const DetectDatasetData *) smd->ctx; - int r = DetectDatasetBufferMatch(det_ctx, sd, buffer, buffer_len); //TODO buffer offset? - if (r == 1) { + int r = DetectDatasetBufferMatch( + det_ctx, sd, buffer, buffer_len, local_id); // TODO buffer offset? + if (r == DETECT_ENGINE_INSPECT_SIG_MATCH) { goto match; } + if (r == DETECT_ENGINE_INSPECT_SIG_MATCH_MORE_BUF) { + if (!smd->is_last) { + KEYWORD_PROFILING_END(det_ctx, smd->type, 1); + r = DetectEngineContentInspectionInternal(det_ctx, ctx, s, smd + 1, p, f, buffer, + buffer_len, stream_start_offset, flags, inspection_mode, local_id); + if (r != 0) + SCReturnInt(DETECT_ENGINE_INSPECT_SIG_MATCH_MORE_BUF); + SCReturnInt(0); + } + KEYWORD_PROFILING_END(det_ctx, smd->type, 1); + SCReturnInt(DETECT_ENGINE_INSPECT_SIG_MATCH_MORE_BUF); + } goto no_match_discontinue; } else if (smd->type == DETECT_DATAREP) { @@ -666,7 +679,8 @@ static int DetectEngineContentInspectionInternal(DetectEngineThreadCtx *det_ctx, int r = DetectEngineContentInspectionInternal(det_ctx, ctx, s, s->sm_arrays[DETECT_SM_LIST_BASE64_DATA], NULL, f, det_ctx->base64_decoded, det_ctx->base64_decoded_len, 0, - DETECT_CI_FLAGS_SINGLE, DETECT_ENGINE_CONTENT_INSPECTION_MODE_STATE); + DETECT_CI_FLAGS_SINGLE, DETECT_ENGINE_CONTENT_INSPECTION_MODE_STATE, + local_id); if (r == 1) { /* Base64 is a terminal list. */ goto final_match; @@ -702,7 +716,7 @@ static int DetectEngineContentInspectionInternal(DetectEngineThreadCtx *det_ctx, if (!smd->is_last) { KEYWORD_PROFILING_END(det_ctx, smd->type, 1); int r = DetectEngineContentInspectionInternal(det_ctx, ctx, s, smd + 1, p, f, buffer, - buffer_len, stream_start_offset, flags, inspection_mode); + buffer_len, stream_start_offset, flags, inspection_mode, local_id); SCReturnInt(r); } final_match: @@ -724,11 +738,11 @@ bool DetectEngineContentInspection(DetectEngineCtx *de_ctx, DetectEngineThreadCt det_ctx->buffer_offset = 0; int r = DetectEngineContentInspectionInternal(det_ctx, &ctx, s, smd, p, f, buffer, buffer_len, - stream_start_offset, flags, inspection_mode); + stream_start_offset, flags, inspection_mode, 0); #ifdef UNITTESTS ut_inspection_recursion_counter = ctx.recursion.count; #endif - if (r == 1) + if (r == 1 || r == DETECT_ENGINE_INSPECT_SIG_MATCH_MORE_BUF) return true; else return false; @@ -748,7 +762,7 @@ bool DetectEngineContentInspectionBuffer(DetectEngineCtx *de_ctx, DetectEngineTh det_ctx->buffer_offset = 0; int r = DetectEngineContentInspectionInternal(det_ctx, &ctx, s, smd, p, f, b->inspect, - b->inspect_len, b->inspect_offset, b->flags, inspection_mode); + b->inspect_len, b->inspect_offset, b->flags, inspection_mode, 0); #ifdef UNITTESTS ut_inspection_recursion_counter = ctx.recursion.count; #endif @@ -758,6 +772,20 @@ bool DetectEngineContentInspectionBuffer(DetectEngineCtx *de_ctx, DetectEngineTh return false; } +uint8_t DetectEngineContentInspectionBufferMulti(DetectEngineCtx *de_ctx, + DetectEngineThreadCtx *det_ctx, const Signature *s, const SigMatchData *smd, Flow *f, + const InspectionBuffer *b, uint32_t local_id) +{ + struct DetectEngineContentInspectionCtx ctx = { .recursion.count = 0, + .recursion.limit = de_ctx->inspection_recursion_limit }; + + det_ctx->buffer_offset = 0; + + return DetectEngineContentInspectionInternal(det_ctx, &ctx, s, smd, NULL, f, b->inspect, + b->inspect_len, b->inspect_offset, b->flags, + DETECT_ENGINE_CONTENT_INSPECTION_MODE_STATE, local_id); +} + #ifdef UNITTESTS #include "tests/detect-engine-content-inspection.c" #endif diff --git a/src/detect-engine-content-inspection.h b/src/detect-engine-content-inspection.h index 2c253b77ad3d..c357feb562a8 100644 --- a/src/detect-engine-content-inspection.h +++ b/src/detect-engine-content-inspection.h @@ -68,6 +68,10 @@ bool DetectEngineContentInspectionBuffer(DetectEngineCtx *de_ctx, DetectEngineTh const Signature *s, const SigMatchData *smd, Packet *p, Flow *f, const InspectionBuffer *b, const enum DetectContentInspectionType inspection_mode); +uint8_t DetectEngineContentInspectionBufferMulti(DetectEngineCtx *de_ctx, + DetectEngineThreadCtx *det_ctx, const Signature *s, const SigMatchData *smd, Flow *f, + const InspectionBuffer *b, uint32_t local_id); + void DetectEngineContentInspectionRegisterTests(void); #endif /* SURICATA_DETECT_ENGINE_CONTENT_INSPECTION_H */ diff --git a/src/detect-engine-state.h b/src/detect-engine-state.h index 326b3bad4ecd..70986a064152 100644 --- a/src/detect-engine-state.h +++ b/src/detect-engine-state.h @@ -47,6 +47,10 @@ * indicate that one of the files matched, but that there are still * more files that have ongoing inspection. */ #define DETECT_ENGINE_INSPECT_SIG_MATCH_MORE_FILES 4 +/** Indicates that we matched on an occurence of a multi-buffer + * but we want to try all occurences, for example, + * to see which should go into a dataset */ +#define DETECT_ENGINE_INSPECT_SIG_MATCH_MORE_BUF 5 /** number of DeStateStoreItem's in one DeStateStore object */ #define DE_STATE_CHUNK_SIZE 15 diff --git a/src/detect-engine.c b/src/detect-engine.c index c34c305d4a15..93fc358a8012 100644 --- a/src/detect-engine.c +++ b/src/detect-engine.c @@ -631,7 +631,8 @@ static void AppendPacketInspectEngine(DetectEngineCtx *de_ctx, static void AppendAppInspectEngine(DetectEngineCtx *de_ctx, const DetectEngineAppInspectionEngine *t, Signature *s, SigMatchData *smd, - const int mpm_list, const int files_id, uint8_t *last_id, bool *head_is_mpm) + const int mpm_list, const int files_id, uint8_t *last_id, bool *head_is_mpm, + bool delay_postmatch) { if (t->alproto == ALPROTO_UNKNOWN) { /* special case, inspect engine applies to all protocols */ @@ -667,6 +668,9 @@ static void AppendAppInspectEngine(DetectEngineCtx *de_ctx, new_engine->sm_list_base = t->sm_list_base; new_engine->smd = smd; new_engine->progress = t->progress; + if (delay_postmatch) + new_engine->progress = (int16_t)AppLayerParserGetStateProgressCompletionStatus( + t->alproto, t->dir == 0 ? STREAM_TOSERVER : STREAM_TOCLIENT); new_engine->v2 = t->v2; SCLogDebug("sm_list %d new_engine->v2 %p/%p/%p", new_engine->sm_list, new_engine->v2.Callback, new_engine->v2.GetData, new_engine->v2.transforms); @@ -763,8 +767,8 @@ int DetectEngineAppInspectionEngine2Signature(DetectEngineCtx *de_ctx, Signature for (const DetectEngineAppInspectionEngine *t = de_ctx->app_inspect_engines; t != NULL; t = t->next) { if (t->sm_list == s->init_data->buffers[x].id) { - AppendAppInspectEngine( - de_ctx, t, s, smd, mpm_list, files_id, &last_id, &head_is_mpm); + AppendAppInspectEngine(de_ctx, t, s, smd, mpm_list, files_id, &last_id, + &head_is_mpm, s->init_data->buffers[x].delay_postmatch); } } } @@ -2205,6 +2209,7 @@ uint8_t DetectEngineInspectMultiBufferGeneric(DetectEngineCtx *de_ctx, transforms = engine->v2.transforms; } + uint8_t r = DETECT_ENGINE_INSPECT_SIG_NO_MATCH; do { InspectionBuffer *buffer = engine->v2.GetMultiData( det_ctx, transforms, f, flags, txv, engine->sm_list, local_id); @@ -2214,14 +2219,18 @@ uint8_t DetectEngineInspectMultiBufferGeneric(DetectEngineCtx *de_ctx, // The GetData functions set buffer->flags to DETECT_CI_FLAGS_SINGLE // This is not meant for streaming buffers - const bool match = DetectEngineContentInspectionBuffer(de_ctx, det_ctx, s, engine->smd, - NULL, f, buffer, DETECT_ENGINE_CONTENT_INSPECTION_MODE_STATE); - if (match) { - return DETECT_ENGINE_INSPECT_SIG_MATCH; + uint8_t match = DetectEngineContentInspectionBufferMulti( + de_ctx, det_ctx, s, engine->smd, f, buffer, local_id); + switch (match) { + case DETECT_ENGINE_INSPECT_SIG_MATCH: + return DETECT_ENGINE_INSPECT_SIG_MATCH; + case DETECT_ENGINE_INSPECT_SIG_MATCH_MORE_BUF: + r = DETECT_ENGINE_INSPECT_SIG_MATCH; + break; } local_id++; } while (1); - return DETECT_ENGINE_INSPECT_SIG_NO_MATCH; + return r; } /** diff --git a/src/detect.h b/src/detect.h index fe755b7f0d14..ac978e313d60 100644 --- a/src/detect.h +++ b/src/detect.h @@ -531,6 +531,9 @@ typedef struct SignatureInitDataBuffer_ { set up. */ bool multi_capable; /**< true if we can have multiple instances of this buffer, so e.g. for http.uri. */ + /** delay use of this buffer beyond its normal progress + * to be just in time for postmatch */ + bool delay_postmatch; /* sig match list */ SigMatch *head; SigMatch *tail;