diff --git a/src/Makefile.am b/src/Makefile.am index ff09178127..6c949412a7 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -148,6 +148,7 @@ util-fix_checksum.c util-fix_checksum.h \ util-daemon.c util-daemon.h \ util-random.c util-random.h \ util-classification-config.c util-classification-config.h \ +util-threshold-config.c util-threshold-config.h \ util-strlcatu.c \ util-strlcpyu.c \ util-cuda.c util-cuda.h \ diff --git a/src/detect.c b/src/detect.c index 6da60e50b1..3d0c4c3de3 100644 --- a/src/detect.c +++ b/src/detect.c @@ -855,6 +855,32 @@ void SigCleanSignatures(DetectEngineCtx *de_ctx) de_ctx->sig_list = NULL; } +/** \brief Find a specific signature by sid and gid + * \param de_ctx detection engine ctx + * \param sid the signature id + * \param gid the signature group id + * + * \retval s sig found + * \retval NULL sig not found + */ +Signature *FindSidGidSignature(DetectEngineCtx *de_ctx, uint32_t sid, uint32_t gid) +{ + Signature *s = NULL, *ns; + + if (de_ctx == NULL) + return NULL; + + for (s = de_ctx->sig_list; s != NULL;) { + ns = s->next; + if(s->id == sid && s->gid == gid) + return s; + s = ns; + } + + return NULL; +} + + int SignatureIsAppLayer(DetectEngineCtx *de_ctx, Signature *s) { if (s->alproto != 0) return 1; diff --git a/src/detect.h b/src/detect.h index bc0c783928..21b17066ac 100644 --- a/src/detect.h +++ b/src/detect.h @@ -620,6 +620,7 @@ SigTableElmt sigmatch_table[DETECT_TBLSIZE]; /* detection api */ SigMatch *SigMatchAlloc(void); +Signature *FindSidGidSignature(DetectEngineCtx *, uint32_t, uint32_t); void SigMatchFree(SigMatch *sm); void SigCleanSignatures(DetectEngineCtx *); diff --git a/src/suricata.c b/src/suricata.c index 55645d5bf5..63a005eeeb 100644 --- a/src/suricata.c +++ b/src/suricata.c @@ -83,6 +83,7 @@ #include "util-time.h" #include "util-rule-vars.h" #include "util-classification-config.h" +#include "util-threshold-config.h" #include "defrag.h" @@ -769,6 +770,7 @@ int main(int argc, char **argv) ThreadMacrosRegisterTests(); UtilSpmSearchRegistertests(); SCClassConfRegisterTests(); + SCThresholdConfRegisterTests(); #ifdef __SC_CUDA_SUPPORT__ SCCudaRegisterTests(); #endif @@ -843,7 +845,9 @@ int main(int argc, char **argv) if (de_ctx->failure_fatal) exit(EXIT_FAILURE); } + AppLayerHtpRegisterExtraCallbacks(); + SCThresholdConfInitContext(de_ctx,NULL); struct timeval start_time; memset(&start_time, 0, sizeof(start_time)); diff --git a/src/util-error.c b/src/util-error.c index bf84f36b4d..6c545fe685 100644 --- a/src/util-error.c +++ b/src/util-error.c @@ -130,6 +130,7 @@ const char * SCErrorToString(SCError err) CASE_CODE (SC_ERR_CONFLICTING_RULE_KEYWORDS); CASE_CODE (SC_ERR_INITIALIZATION); CASE_CODE (SC_ERR_UNIFIED_LOG_FILE_HEADER); + CASE_CODE (SC_ERR_OPTS_WRONG_ORDER); default: return "UNKNOWN_ERROR"; } diff --git a/src/util-error.h b/src/util-error.h index 7f203e00bf..7a29f6b59f 100644 --- a/src/util-error.h +++ b/src/util-error.h @@ -145,6 +145,7 @@ typedef enum { SC_ERR_INVALID_YAML_CONF_ENTRY, SC_ERR_TMQ_ALREADY_REGISTERED, SC_ERR_CONFLICTING_RULE_KEYWORDS, + SC_ERR_OPTS_WRONG_ORDER, SC_ERR_UNIFIED_LOG_FILE_HEADER, /**< Error to indicate the unified file header writing function has been failed */ diff --git a/src/util-threshold-config.c b/src/util-threshold-config.c new file mode 100644 index 0000000000..32705283ab --- /dev/null +++ b/src/util-threshold-config.c @@ -0,0 +1,784 @@ +/** Copyright (c) 2009 Open Information Security Foundation. + * + * \author Breno Silva Pinto + */ + +#include "suricata-common.h" +#include "detect.h" +#include "detect-engine.h" +#include "detect-threshold.h" +#include "detect-parse.h" + +#include "conf.h" +#include "util-threshold-config.h" +#include "util-unittest.h" +#include "util-byte.h" +#include "util-error.h" +#include "util-debug.h" +#include "util-fmemopen.h" + +/* File descriptor for unittests */ + +/** \todo Need to support suppress */ + +#define DETECT_THRESHOLD_REGEX "^\\s*(event_filter|threshold)\\s*gen_id\\s*(\\d+)\\s*,\\s*sig_id\\s*(\\d+)\\s*,\\s*type\\s*(limit|both|threshold)\\s*,\\s*track\\s*(by_dst|by_src)\\s*,\\s*count\\s*(\\d+)\\s*,\\s*seconds\\s*(\\d+)\\s*$" + +/* Default path for the threshold.config file */ +#define THRESHOLD_CONF_DEF_CONF_FILEPATH "threshold.config" + +/* Holds a pointer to the default path for the threshold.config file */ +static const char *default_file_path = THRESHOLD_CONF_DEF_CONF_FILEPATH; +static pcre *regex = NULL; +static pcre_extra *regex_study = NULL; + +/** + * \brief Returns the path for the Threshold Config file. We check if we + * can retrieve the path from the yaml conf file. If it is not present, + * return the default path for the threshold file which is + * "./threshold.config". + * + * \retval log_filename Pointer to a string containing the path for the + * Threshold Config file. + */ +char *SCThresholdConfGetConfFilename(void) +{ + char *log_filename = (char *)default_file_path; + + ConfGet("threshold-file", &log_filename); + + return log_filename; +} + +/** + * \brief Inits the context to be used by the Threshold Config parsing API. + * + * This function initializes the hash table to be used by the Detection + * Engine Context to hold the data from the threshold.config file, + * obtains the file desc to parse the threshold.config file, and + * inits the regex used to parse the lines from threshold.config + * file. + * + * \param de_ctx Pointer to the Detection Engine Context. + * \param utfd Pointer for unit test file descriptor. + * + * \retval 0 On success. + * \retval -1 On failure. + */ +inline int SCThresholdConfInitContext(DetectEngineCtx *de_ctx, FILE *utfd) +{ + char *filename = NULL; + const char *eb = NULL; + FILE *fd = utfd; + int eo; + int opts = 0; + + if (fd == NULL) { + filename = SCThresholdConfGetConfFilename(); + if ( (fd = fopen(filename, "r")) == NULL) { + SCLogError(SC_ERR_FOPEN, "Error opening file: \"%s\": %s", filename, strerror(errno)); + goto error; + } + } + + regex = pcre_compile(DETECT_THRESHOLD_REGEX, opts, &eb, &eo, NULL); + if (regex == NULL) { + SCLogError(SC_ERR_PCRE_COMPILE, "Compile of \"%s\" failed at offset %" PRId32 ": %s",DETECT_THRESHOLD_REGEX, eo, eb); + goto error; + } + + regex_study = pcre_study(regex, 0, &eb); + if (eb != NULL) { + SCLogError(SC_ERR_PCRE_STUDY, "pcre study failed: %s", eb); + goto error; + } + + SCThresholdConfParseFile(de_ctx, fd); + SCThresholdConfDeInitContext(de_ctx, fd); + + SCLogInfo("Global thresholding options defined"); + return 0; + +error: + if (fd != NULL) { + fclose(fd); + fd = NULL; + } + + return -1; +} + +/** + * \brief Releases resources used by the Threshold Config API. + * + * \param de_ctx Pointer to the Detection Engine Context. + * \param fd Pointer to file descriptor. + */ +void SCThresholdConfDeInitContext(DetectEngineCtx *de_ctx, FILE *fd) +{ + + if(fd != NULL) + fclose(fd); + default_file_path = THRESHOLD_CONF_DEF_CONF_FILEPATH; + fd = NULL; + return; +} + +/** + * \brief Converts a string to lowercase. + * + * \param str Pointer to the string to be converted. + * \retval new_str Pointer to lower case string + */ +char *SCThresholdConfStringToLowercase(char *str) +{ + char *new_str = NULL; + char *temp_str = NULL; + + if ( (new_str = SCStrdup(str)) == NULL) { + SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory"); + /* Lets try to parse the original string */ + return str; + } + + temp_str = new_str; + while (*temp_str != '\0') { + *temp_str = tolower(*temp_str); + temp_str++; + } + + return new_str; +} + +/** + * \brief Parses a line from the threshold file and adds it to Thresholdtype + * + * \param rawstr Pointer to the string to be parsed. + * \param de_ctx Pointer to the Detection Engine Context. + * + * \retval 0 On success. + * \retval -1 On failure. + */ +inline int SCThresholdConfAddThresholdtype(char *rawstr, DetectEngineCtx *de_ctx) +{ + const char *th_gid = NULL; + const char *th_sid = NULL; + const char *th_type = NULL; + const char *th_track = NULL; + const char *th_count = NULL; + const char *th_seconds = NULL; + + uint8_t parsed_type = 0; + uint8_t parsed_track = 0; + uint32_t parsed_count = 0; + uint32_t parsed_seconds = 0; + + Signature *sig = NULL; + Signature *s = NULL, *ns = NULL; + DetectThresholdData *de = NULL; + SigMatch *sm = NULL; + SigMatch *m = NULL; +#define MAX_SUBSTRINGS 30 + int ret = 0; + int ov[MAX_SUBSTRINGS]; + uint32_t id = 0, gid = 0; + + if (de_ctx == NULL) + return -1; + + ret = pcre_exec(regex, regex_study, rawstr, strlen(rawstr), 0, 0, ov, 30); + if (ret < 0) { + SCLogError(SC_ERR_OPTS_WRONG_ORDER, "Invalid event type in " + "threshold.config file " + "The valid format is : event_filter gen_id 1, sig_id 10, type limit, track by_src, count 1, seconds 60"); + goto error; + } + + /* retrieve the classtype name */ + ret = pcre_get_substring((char *)rawstr, ov, 30, 2, &th_gid); + if (ret < 0) { + SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed"); + goto error; + } + + ret = pcre_get_substring((char *)rawstr, ov, 30, 3, &th_sid); + if (ret < 0) { + SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed"); + goto error; + } + + ret = pcre_get_substring((char *)rawstr, ov, 30, 4, &th_type); + if (ret < 0) { + SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed"); + goto error; + } + + ret = pcre_get_substring((char *)rawstr, ov, 30, 5, &th_track); + if (ret < 0) { + SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed"); + goto error; + } + + ret = pcre_get_substring((char *)rawstr, ov, 30, 6, &th_count); + if (ret < 0) { + SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed"); + goto error; + } + + ret = pcre_get_substring((char *)rawstr, ov, 30, 7, &th_seconds); + if (ret < 0) { + SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed"); + goto error; + } + + if (strcasecmp(th_type,"limit") == 0) + parsed_type = TYPE_LIMIT; + else if (strcasecmp(th_type,"both") == 0) + parsed_type = TYPE_BOTH; + else if (strcasecmp(th_type,"threshold") == 0) + parsed_type = TYPE_THRESHOLD; + + if (strcasecmp(th_track,"by_dst") == 0) + parsed_track = TRACK_DST; + else if (strcasecmp(th_track,"by_src") == 0) + parsed_track = TRACK_SRC; + + if (ByteExtractStringUint32(&parsed_count, 10, strlen(th_count), th_count) <= 0) { + goto error; + } + + if (ByteExtractStringUint32(&parsed_seconds, 10, strlen(th_seconds), th_seconds) <= 0) { + goto error; + } + + + if (ByteExtractStringUint32(&id, 10, strlen(th_sid), th_sid) <= 0) { + goto error; + } + + if (ByteExtractStringUint32(&gid, 10, strlen(th_gid), th_gid) <= 0) { + goto error; + } + + if (id == 0 && gid == 0) { + + for (s = de_ctx->sig_list; s != NULL;) { + + ns = s->next; + + m = SigMatchGetLastSM(s->match, DETECT_THRESHOLD); + + if(m != NULL) + goto end; + + m = SigMatchGetLastSM(s->match, DETECT_DETECTION_FILTER); + + if(m != NULL) + goto end; + + de = SCMalloc(sizeof(DetectThresholdData)); + if (de == NULL) { + SCLogError(SC_ERR_MEM_ALLOC, "malloc failed"); + goto error; + } + + memset(de,0,sizeof(DetectThresholdData)); + + de->type = parsed_type; + de->track = parsed_track; + de->count = parsed_count; + de->seconds = parsed_seconds; + + sm = SigMatchAlloc(); + if (sm == NULL) { + SCLogError(SC_ERR_MEM_ALLOC, "Error allocating SigMatch"); + goto error; + } + + sm->type = DETECT_THRESHOLD; + sm->ctx = (void *)de; + + SigMatchAppendPacket(s, sm); + s = ns; + } + + } else if (id == 0 && gid > 0) { + + for (s = de_ctx->sig_list; s != NULL;) { + + ns = s->next; + + if(s->gid == gid) { + + m = SigMatchGetLastSM(s->match, DETECT_THRESHOLD); + + if(m != NULL) + goto end; + + m = SigMatchGetLastSM(s->match, DETECT_DETECTION_FILTER); + + if(m != NULL) + goto end; + + de = SCMalloc(sizeof(DetectThresholdData)); + if (de == NULL) { + SCLogError(SC_ERR_MEM_ALLOC, "malloc failed"); + goto error; + } + + memset(de,0,sizeof(DetectThresholdData)); + + de->type = parsed_type; + de->track = parsed_track; + de->count = parsed_count; + de->seconds = parsed_seconds; + + sm = SigMatchAlloc(); + if (sm == NULL) { + SCLogError(SC_ERR_MEM_ALLOC, "Error allocating SigMatch"); + goto error; + } + + sm->type = DETECT_THRESHOLD; + sm->ctx = (void *)de; + + SigMatchAppendPacket(s, sm); + } + s = ns; + } + } else { + sig = FindSidGidSignature(de_ctx,id,gid); + + if(sig != NULL) { + + m = SigMatchGetLastSM(sig->match, DETECT_THRESHOLD); + + if(m != NULL) + goto end; + + m = SigMatchGetLastSM(sig->match, DETECT_DETECTION_FILTER); + + if(m != NULL) + goto end; + + de = SCMalloc(sizeof(DetectThresholdData)); + if (de == NULL) { + SCLogError(SC_ERR_MEM_ALLOC, "malloc failed"); + goto error; + } + + memset(de,0,sizeof(DetectThresholdData)); + + de->type = parsed_type; + de->track = parsed_track; + de->count = parsed_count; + de->seconds = parsed_seconds; + + sm = SigMatchAlloc(); + if (sm == NULL) { + SCLogError(SC_ERR_MEM_ALLOC, "Error allocating SigMatch"); + goto error; + } + + sm->type = DETECT_THRESHOLD; + sm->ctx = (void *)de; + + SigMatchAppendPacket(sig, sm); + } + + } + +end: + if(th_sid != NULL) SCFree((char *)th_sid); + if(th_gid != NULL) SCFree((char *)th_gid); + if(th_track != NULL) SCFree((char *)th_track); + if(th_count != NULL) SCFree((char *)th_count); + if(th_seconds != NULL) SCFree((char *)th_seconds); + if(th_type != NULL) SCFree((char *)th_type); + + return 0; + +error: + if(de != NULL) SCFree(de); + if(th_sid != NULL) SCFree((char *)th_sid); + if(th_gid != NULL) SCFree((char *)th_gid); + if(th_track != NULL) SCFree((char *)th_track); + if(th_count != NULL) SCFree((char *)th_count); + if(th_seconds != NULL) SCFree((char *)th_seconds); + if(th_type != NULL) SCFree((char *)th_type); + return -1; +} + +/** + * \brief Checks if a string is a comment or a blank line. + * + * Comments lines are lines of the following format - + * "# This is a comment string" or + * " # This is a comment string". + * + * \param line String that has to be checked + * + * \retval 1 On the argument string being a comment or blank line + * \retval 0 Otherwise + */ +int SCThresholdConfIsLineBlankOrComment(char *line) +{ + while (*line != '\0') { + /* we have a comment */ + if (*line == '#') + return 1; + + /* this line is neither a comment line, nor a blank line */ + if (!isspace(*line)) + return 0; + + line++; + } + + /* we have a blank line */ + return 1; +} + +/** + * \brief Parses the Threshold Config file + * + * \param de_ctx Pointer to the Detection Engine Context. + * \param fd Pointer to file descriptor. + */ +inline void SCThresholdConfParseFile(DetectEngineCtx *de_ctx, FILE *fd) +{ + char line[1024]; + char *normalized_line; + + if(fd == NULL) + return; + + while (fgets(line, sizeof(line), fd) != NULL) { + if (SCThresholdConfIsLineBlankOrComment(line)) + continue; + + normalized_line = SCThresholdConfStringToLowercase(line); + SCThresholdConfAddThresholdtype(normalized_line, de_ctx); + if(normalized_line) SCFree(normalized_line); + } + + return; +} + +#ifdef UNITTESTS + +/** + * \brief Creates a dummy threshold file, with all valid options, for testing purposes. + * + * \retval fd Pointer to file descriptor. + */ +FILE *SCThresholdConfGenerateValidDummyFD01() +{ + FILE *fd = NULL; + const char *buffer = + "event_filter gen_id 1, sig_id 10, type limit, track by_src, count 1, seconds 60\n" + "threshold gen_id 1, sig_id 100, type both, track by_dst, count 10, seconds 60\n" + "event_filter gen_id 1, sig_id 1000, type threshold, track by_src, count 100, seconds 60\n"; + + fd = SCFmemopen((void *)buffer, strlen(buffer), "r"); + if (fd == NULL) + SCLogDebug("Error with SCFmemopen() called by Threshold Config test code"); + + return fd; +} + +/** + * \brief Creates a dummy threshold file, with some valid options and a couple of invalid options. + * For testing purposes. + * + * \retval fd Pointer to file descriptor. + */ +FILE *SCThresholdConfGenerateInValidDummyFD02() +{ + FILE *fd; + const char *buffer = + "event_filter gen_id 1, sig_id 1000, type invalid, track by_src, count 100, seconds 60\n"; + + fd = SCFmemopen((void *)buffer, strlen(buffer), "r"); + if (fd == NULL) + SCLogDebug("Error with SCFmemopen() called by Threshold Config test code"); + + return fd; +} + +/** + * \brief Creates a dummy threshold file, with all valid options, for testing purposes. + * + * \retval fd Pointer to file descriptor. + */ +FILE *SCThresholdConfGenerateValidDummyFD03() +{ + FILE *fd; + const char *buffer = + "event_filter gen_id 0, sig_id 0, type threshold, track by_src, count 100, seconds 60\n"; + + fd = SCFmemopen((void *)buffer, strlen(buffer), "r"); + if (fd == NULL) + SCLogDebug("Error with SCFmemopen() called by Threshold Config test code"); + + return fd; +} + +/** + * \test Check if the threshold file is loaded and well parsed + * + * \retval 1 on succces + * \retval 0 on failure + */ +int SCThresholdConfTest01(void) +{ + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + DetectThresholdData *de = NULL; + Signature *sig = NULL; + SigMatch *m = NULL; + int result = 0; + FILE *fd = NULL; + + if (de_ctx == NULL) + return result; + + de_ctx->flags |= DE_QUIET; + + sig = de_ctx->sig_list = SigInit(de_ctx,"alert tcp any any -> any any (msg:\"Threshold limit\"; gid:1; sid:10;)"); + if (sig == NULL) { + goto end; + } + + fd = SCThresholdConfGenerateValidDummyFD01(); + SCThresholdConfInitContext(de_ctx,fd); + + m = SigMatchGetLastSM(sig->match, DETECT_THRESHOLD); + + if(m != NULL) { + de = (DetectThresholdData *)m->ctx; + if(de != NULL && (de->type == TYPE_LIMIT && de->track == TRACK_SRC && de->count == 1 && de->seconds == 60)) + result = 1; + } + +end: + SigGroupBuild(de_ctx); + SigGroupCleanup(de_ctx); + SigCleanSignatures(de_ctx); + DetectEngineCtxFree(de_ctx); + return result; +} + +/** + * \test Check if the threshold file is loaded and well parsed + * + * \retval 1 on succces + * \retval 0 on failure + */ +int SCThresholdConfTest02(void) +{ + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + DetectThresholdData *de = NULL; + Signature *sig = NULL; + SigMatch *m = NULL; + int result = 0; + FILE *fd = NULL; + + if (de_ctx == NULL) + return result; + + de_ctx->flags |= DE_QUIET; + + sig = de_ctx->sig_list = SigInit(de_ctx,"alert tcp any any -> any any (msg:\"Threshold limit\"; gid:1; sid:100;)"); + if (sig == NULL) { + goto end; + } + + fd = SCThresholdConfGenerateValidDummyFD01(); + SCThresholdConfInitContext(de_ctx,fd); + + m = SigMatchGetLastSM(sig->match, DETECT_THRESHOLD); + + if(m != NULL) { + de = (DetectThresholdData *)m->ctx; + if(de != NULL && (de->type == TYPE_BOTH && de->track == TRACK_DST && de->count == 10 && de->seconds == 60)) + result = 1; + } +end: + SigGroupBuild(de_ctx); + SigGroupCleanup(de_ctx); + SigCleanSignatures(de_ctx); + DetectEngineCtxFree(de_ctx); + return result; +} + +/** + * \test Check if the threshold file is loaded and well parsed + * + * \retval 1 on succces + * \retval 0 on failure + */ +int SCThresholdConfTest03(void) +{ + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + DetectThresholdData *de = NULL; + Signature *sig = NULL; + SigMatch *m = NULL; + int result = 0; + FILE *fd = NULL; + + if (de_ctx == NULL) + return result; + + de_ctx->flags |= DE_QUIET; + + sig = de_ctx->sig_list = SigInit(de_ctx,"alert tcp any any -> any any (msg:\"Threshold limit\"; gid:1; sid:1000;)"); + if (sig == NULL) { + goto end; + } + + fd = SCThresholdConfGenerateValidDummyFD01(); + SCThresholdConfInitContext(de_ctx,fd); + + m = SigMatchGetLastSM(sig->match, DETECT_THRESHOLD); + + if(m != NULL) { + de = (DetectThresholdData *)m->ctx; + if(de != NULL && (de->type == TYPE_THRESHOLD && de->track == TRACK_SRC && de->count == 100 && de->seconds == 60)) + result = 1; + } +end: + SigGroupBuild(de_ctx); + SigGroupCleanup(de_ctx); + SigCleanSignatures(de_ctx); + DetectEngineCtxFree(de_ctx); + return result; +} + +/** + * \test Check if the threshold file is loaded and well parsed + * + * \retval 1 on succces + * \retval 0 on failure + */ +int SCThresholdConfTest04(void) +{ + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + DetectThresholdData *de = NULL; + Signature *sig = NULL; + SigMatch *m = NULL; + int result = 0; + FILE *fd = NULL; + + if (de_ctx == NULL) + return result; + + de_ctx->flags |= DE_QUIET; + + sig = de_ctx->sig_list = SigInit(de_ctx,"alert tcp any any -> any any (msg:\"Threshold limit\"; gid:1; sid:1000;)"); + if (sig == NULL) { + goto end; + } + + fd = SCThresholdConfGenerateInValidDummyFD02(); + SCThresholdConfInitContext(de_ctx,fd); + + m = SigMatchGetLastSM(sig->match, DETECT_THRESHOLD); + + if(m != NULL) { + de = (DetectThresholdData *)m->ctx; + if(de == NULL) + return result; + else + result = 1; + } +end: + SigGroupBuild(de_ctx); + SigGroupCleanup(de_ctx); + SigCleanSignatures(de_ctx); + return result; +} + +/** + * \test Check if the threshold file is loaded and well parsed + * + * \retval 1 on succces + * \retval 0 on failure + */ +int SCThresholdConfTest05(void) +{ + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + DetectThresholdData *de = NULL; + Signature *sig = NULL; + Signature *s = NULL, *ns = NULL; + SigMatch *m = NULL; + int result = 0; + FILE *fd = NULL; + + if (de_ctx == NULL) + return result; + + de_ctx->flags |= DE_QUIET; + + sig = de_ctx->sig_list = SigInit(de_ctx,"alert tcp any any -> any any (msg:\"Threshold limit\"; gid:1; sid:1;)"); + if (sig == NULL) { + goto end; + } + + sig = sig->next = SigInit(de_ctx,"alert tcp any any -> any 80 (msg:\"Threshold limit\"; gid:1; sid:10;)"); + if (sig == NULL) { + goto end; + } + + sig = sig->next = SigInit(de_ctx,"alert tcp any any -> any 80 (msg:\"Threshold limit\"; gid:1; sid:100;)"); + if (sig == NULL) { + goto end; + } + + fd = SCThresholdConfGenerateValidDummyFD03(); + SCThresholdConfInitContext(de_ctx,fd); + + for (s = de_ctx->sig_list; s != NULL;) { + + ns = s->next; + + if(s->id == 1 || s->id == 10 || s->id == 100) { + + m = SigMatchGetLastSM(s->match, DETECT_THRESHOLD); + + if(m == NULL) { + goto end; + } else { + de = (DetectThresholdData *)m->ctx; + if(de != NULL && (de->type == TYPE_THRESHOLD && de->track == TRACK_SRC && de->count == 100 && de->seconds == 60)) + result++; + } + } + + s = ns; + } + + if(result == 3) + result = 1; + +end: + SigGroupBuild(de_ctx); + SigGroupCleanup(de_ctx); + SigCleanSignatures(de_ctx); + DetectEngineCtxFree(de_ctx); + return result; +} +#endif /* UNITTESTS */ + +/** + * \brief This function registers unit tests for Classification Config API. + */ +void SCThresholdConfRegisterTests(void) +{ +#ifdef UNITTESTS + UtRegisterTest("SCThresholdConfTest01", SCThresholdConfTest01, 1); + UtRegisterTest("SCThresholdConfTest02", SCThresholdConfTest02, 1); + UtRegisterTest("SCThresholdConfTest03", SCThresholdConfTest03, 1); + UtRegisterTest("SCThresholdConfTest04", SCThresholdConfTest04, 0); + UtRegisterTest("SCThresholdConfTest05", SCThresholdConfTest05, 1); +#endif /* UNITTESTS */ +} + diff --git a/src/util-threshold-config.h b/src/util-threshold-config.h new file mode 100644 index 0000000000..241518dbae --- /dev/null +++ b/src/util-threshold-config.h @@ -0,0 +1,14 @@ +/** Copyright (c) 2009 Open Information Security Foundation. + * \author Breno Silva Pinto + */ + +#ifndef __UTIL_THRESHOLD_CONFIG_H__ +#define __UTIL_THRESHOLD_CONFIG_H__ + +void SCThresholdConfDeInitContext(DetectEngineCtx *, FILE *); +inline void SCThresholdConfParseFile(DetectEngineCtx *, FILE *); +inline int SCThresholdConfInitContext(DetectEngineCtx *, FILE *); + +void SCThresholdConfRegisterTests(); + +#endif /* __UTIL_THRESHOLD_CONFIG_H__ */