#include "time/timersub.h"
#include "sfip/sf_ip.h"
+#ifdef UNIT_TEST
+#include "test/catch.hpp"
+#endif
+
static int already_fatal = 0;
/*
va_end(ap);
}
-/*
- * Function: ErrorMessageThrottled(ThrottleInfo *,const char *, ...)
- *
- * Purpose: Print a message to stderr, and throttle when
- * too many messages are printed.
- *
- * Arguments: throttleInfo => point to the saved throttle state information
- * format => the formatted error string to print out
- * ... => format commands/fillers
- *
- * Returns: void function
- */
+ThrottledErrorLogger::ThrottledErrorLogger(uint32_t dur) :
+ throttle_duration { dur }
+{ reset(); }
-void ErrorMessageThrottled(ThrottleInfo* throttleInfo, const char* format,...)
+bool ThrottledErrorLogger::log(const char* format, ...)
{
- char buf[STD_BUF+1];
+ if ( !snort_conf )
+ return false;
+
+ if ( throttle() )
+ return false;
+
va_list ap;
- time_t current_time = packet_time();
- if ((snort_conf == NULL)||(!throttleInfo))
- return;
+ va_start(ap, format);
+ int index = vsnprintf(buf, STD_BUF, format, ap);
+ va_end(ap);
- throttleInfo->count++;
- DebugFormat(DEBUG_INIT,
- "current_time: %d, throttle (%p): count " STDu64 ", last update: %d\n",
- (int)current_time, throttleInfo, throttleInfo->count, (int)throttleInfo->lastUpdate);
- /*Note: we only output the first error message,
- * and the statistics after at least duration_to_log seconds
- * when the same type of error message is printed out again */
- if (current_time - (time_t)throttleInfo->duration_to_log > throttleInfo->lastUpdate)
- {
- int index;
- va_start(ap, format);
- index = vsnprintf(buf, STD_BUF, format, ap);
- va_end(ap);
+ if ( index && ( count > 1 ) )
+ snprintf(&buf[index - 1], STD_BUF - index,
+ " (suppressed " STDu64 " times in the last %d seconds).\n",
+ count, delta);
- if (index && (throttleInfo->count > 1))
- {
- snprintf(&buf[index - 1], STD_BUF-index,
- " (suppressed " STDu64 " times in the last %d seconds).\n",
- throttleInfo->count, (int)(current_time - throttleInfo->lastUpdate));
- }
+ ErrorMessage("%s", buf);
+ return true;
+}
- ErrorMessage("%s",buf);
- throttleInfo->lastUpdate = current_time;
- throttleInfo->count = 0;
+void ThrottledErrorLogger::reset()
+{ count = 0; }
+
+bool ThrottledErrorLogger::throttle()
+{
+ time_t cur = packet_time();
+ bool result = false;
+
+ if ( count++ )
+ {
+ delta = cur - last;
+ result = (decltype(throttle_duration))delta < throttle_duration;
+
+ if ( !result )
+ count = 0;
}
+
+ last = cur;
+
+ return result;
}
/*
return ip_buf;
}
+#ifdef UNIT_TEST
+
+static void set_packet_time(time_t x)
+{
+ struct timeval t { x, 0 };
+ packet_time_update(&t);
+}
+
+static bool check_message(const char* buffer, const char* msg)
+{
+ if ( strncmp(buffer, msg, strnlen(msg, STD_BUF)) != 0 )
+ {
+ INFO( buffer );
+ return false;
+ }
+
+ return true;
+}
+
+TEST_CASE( "throttled error logger", "[ThrottledErrorLogger]" )
+{
+ uint32_t dur = 5;
+ ThrottledErrorLogger logger(dur);
+
+ set_packet_time(0);
+
+ SECTION( "1st message" )
+ {
+ const char msg[] = "first message";
+ REQUIRE( logger.log("%s\n", msg) );
+
+ CHECK( check_message(logger.last_message(), msg) );
+ }
+
+ SECTION( "2nd message within 1 second" )
+ {
+ const char msg[] = "second message";
+ logger.log("");
+
+ REQUIRE_FALSE( logger.log("%s\n", msg) );
+ }
+
+ SECTION( "0 duration" )
+ {
+ logger.throttle_duration = 0;
+ const char msg[] = "zero duration";
+
+ logger.log(""); // trigger throttling
+ REQUIRE( logger.log("%s\n", msg) );
+
+ CHECK( check_message(logger.last_message(), msg) );
+ }
+
+ SECTION( "message @ duration" )
+ {
+ const char msg[] = "at duration";
+ logger.log(""); // trigger throttling
+
+ set_packet_time(dur - 1);
+ CHECK_FALSE( logger.log("%s\n", msg) );
+ }
+
+ SECTION( "message after duration" )
+ {
+ const char msg[] = "after duration";
+ logger.log(""); // trigger throttling
+
+ set_packet_time(dur);
+ REQUIRE( logger.log("%s\n", msg) );
+
+ CHECK( check_message(logger.last_message(), msg) );
+ }
+
+ SECTION( "reversed packet time" )
+ {
+ const char msg[] = "reversed packet time";
+
+ set_packet_time(10);
+ logger.log("");
+
+ set_packet_time(4);
+ REQUIRE( logger.log("%s\n", msg) );
+
+ CHECK( check_message(logger.last_message(), msg) );
+ }
+}
+
+#endif
#ifndef __GNUC__
#define __attribute__(x) /*NOTHING*/
#endif
+
+#define STD_BUF 1024
+
SO_PUBLIC void LogMessage(const char*, ...) __attribute__((format (printf, 1, 2)));
SO_PUBLIC void WarningMessage(const char*, ...) __attribute__((format (printf, 1, 2)));
SO_PUBLIC void ErrorMessage(const char*, ...) __attribute__((format (printf, 1, 2)));
-struct ThrottleInfo
+// FIXIT-L should we be using STL timekeeping types for this?
+class ThrottledErrorLogger
{
- time_t lastUpdate;
- /*Within this duration (in seconds), maximal one distinct message is logged*/
+public:
+ ThrottledErrorLogger(uint32_t);
+
+ bool log(const char*, ...) __attribute__((format (printf, 2, 3)));
+ void reset();
+
+ uint32_t throttle_duration;
uint32_t duration_to_log;
+
+ const char* last_message() const
+ { return buf; }
+
+private:
+ bool throttle();
+
+ time_t last;
+ int delta;
uint64_t count;
-};
-void ErrorMessageThrottled(ThrottleInfo*,const char*, ...) __attribute__((format (printf, 2, 3)));
+ char buf[STD_BUF + 1];
+};
// FIXIT-M do not call FatalError() during runtime
SO_PUBLIC NORETURN void FatalError(const char*, ...) __attribute__((format (printf, 1, 2)));