MXS-1346: Implement LimitQueriesRule::matches_query

Moved the code into the LimitQueriesRule class and cleaned it up. Renamed
the QUERYSPEED struct and added simple constructor.
This commit is contained in:
Markus Mäkelä 2017-09-01 22:34:09 +03:00
parent 890f860650
commit 594956178d
3 changed files with 75 additions and 122 deletions

View File

@ -1073,15 +1073,7 @@ void define_limit_queries_rule(void* scanner, int max, int timeperiod, int holdo
{
struct parser_stack* rstack = (struct parser_stack*)dbfw_yyget_extra((yyscan_t) scanner);
ss_dassert(rstack);
QUERYSPEED* qs = new QUERYSPEED;
Rule* rule = create_rule(rstack->name);
qs->limit = max;
qs->period = timeperiod;
qs->cooldown = holdoff;
rule->type = RT_THROTTLE;
rule->data = qs;
rstack->rule.push_front(SRule(rule));
rstack->rule.push_front(SRule(new LimitQueriesRule(rstack->name, max, timeperiod, holdoff)));
}
/**
@ -1366,7 +1358,7 @@ freeSession(MXS_FILTER *instance, MXS_FILTER_SESSION *session)
{
FW_SESSION *my_session = (FW_SESSION *) session;
MXS_FREE(my_session->errmsg);
MXS_FREE(my_session->query_speed);
delete my_session->query_speed;
MXS_FREE(my_session);
}
@ -1573,70 +1565,6 @@ static char* create_parse_error(FW_INSTANCE* my_instance,
return msg;
}
bool match_throttle(FW_SESSION* my_session, SRule rule, char **msg)
{
bool matches = false;
QUERYSPEED* rule_qs = (QUERYSPEED*)rule->data;
QUERYSPEED* queryspeed = my_session->query_speed;
time_t time_now = time(NULL);
if (queryspeed == NULL)
{
/**No match found*/
queryspeed = new QUERYSPEED;
queryspeed->period = rule_qs->period;
queryspeed->cooldown = rule_qs->cooldown;
queryspeed->limit = rule_qs->limit;
my_session->query_speed = queryspeed;
}
if (queryspeed->active)
{
if (difftime(time_now, queryspeed->triggered) < queryspeed->cooldown)
{
double blocked_for = queryspeed->cooldown - difftime(time_now, queryspeed->triggered);
*msg = create_error("Queries denied for %f seconds", blocked_for);
matches = true;
MXS_INFO("rule '%s': user denied for %f seconds",
rule->name.c_str(), blocked_for);
}
else
{
queryspeed->active = false;
queryspeed->count = 0;
}
}
else
{
if (queryspeed->count >= queryspeed->limit)
{
MXS_INFO("rule '%s': query limit triggered (%d queries in %d seconds), "
"denying queries from user for %d seconds.", rule->name.c_str(),
queryspeed->limit, queryspeed->period, queryspeed->cooldown);
queryspeed->triggered = time_now;
queryspeed->active = true;
matches = true;
double blocked_for = queryspeed->cooldown - difftime(time_now, queryspeed->triggered);
*msg = create_error("Queries denied for %f seconds", blocked_for);
}
else if (queryspeed->count > 0 &&
difftime(time_now, queryspeed->first_query) <= queryspeed->period)
{
queryspeed->count++;
}
else
{
queryspeed->first_query = time_now;
queryspeed->count = 1;
}
}
return matches;
}
/**
* Check if a query matches a single rule
* @param my_instance Fwfilter instance
@ -1681,51 +1609,6 @@ bool rule_matches(FW_INSTANCE* my_instance,
matches = true;
goto queryresolved;
}
/** No match, try old the style rule */
switch (rule->type)
{
case RT_UNDEFINED:
ss_dassert(false);
MXS_ERROR("Undefined rule type found.");
break;
case RT_REGEX:
/** Handled in RegexRule::query_matches */
break;
case RT_PERMISSION:
/** Handled in Rule::matches_query */
break;
case RT_COLUMN:
/** Handled in ColumnsRule::matches_query */
break;
case RT_FUNCTION:
/** Handled in FunctionRule::matches_query */
break;
case RT_USES_FUNCTION:
/** Handled in FunctionUsageRule::matches_query */
break;
case RT_WILDCARD:
/** Handled in WildCardRule::query_matches */
break;
case RT_THROTTLE:
matches = match_throttle(my_session, rule, &msg);
break;
case RT_CLAUSE:
/** Handled in WhereClauseRule::query_matches */
break;
default:
break;
}
}
queryresolved:

View File

@ -159,8 +159,20 @@ typedef struct timerange_t
/**
* Query speed measurement and limitation structure
*/
typedef struct queryspeed_t
struct QuerySpeed
{
QuerySpeed(int period = 0, int cooldown = 0, int limit = 0):
first_query(0),
triggered(0),
period(period),
cooldown(cooldown),
count(0),
limit(limit),
id(0),
active(false)
{
}
time_t first_query; /*< Time when the first query occurred */
time_t triggered; /*< Time when the limit was exceeded */
int period; /*< Measurement interval in seconds */
@ -169,7 +181,7 @@ typedef struct queryspeed_t
int limit; /*< Maximum number of queries */
long id; /*< Unique id of the rule */
bool active; /*< If the rule has been triggered */
} QUERYSPEED;
};
/**
* The Firewall filter instance.
@ -191,7 +203,7 @@ typedef struct
{
MXS_SESSION *session; /*< Client session structure */
char *errmsg; /*< Rule specific error message */
QUERYSPEED *query_speed; /*< How fast the user has executed queries */
QuerySpeed *query_speed; /*< How fast the user has executed queries */
MXS_DOWNSTREAM down; /*< Next object in the downstream chain */
MXS_UPSTREAM up; /*< Next object in the upstream chain */
FW_INSTANCE *instance; /*< Router instance */

View File

@ -245,3 +245,61 @@ bool FunctionUsageRule::matches_query(FW_SESSION* session, GWBUF* buffer, char**
return false;
}
bool LimitQueriesRule::matches_query(FW_SESSION* session, GWBUF* buffer, char** msg)
{
if (session->query_speed == NULL)
{
session->query_speed = new QuerySpeed(m_timeperiod, m_holdoff, m_max);
}
QuerySpeed* queryspeed = session->query_speed;
time_t time_now = time(NULL);
bool matches = false;
if (queryspeed->active)
{
if (difftime(time_now, queryspeed->triggered) < queryspeed->cooldown)
{
double blocked_for = queryspeed->cooldown - difftime(time_now, queryspeed->triggered);
*msg = create_error("Queries denied for %f seconds", blocked_for);
matches = true;
MXS_INFO("rule '%s': user denied for %f seconds",
name.c_str(), blocked_for);
}
else
{
queryspeed->active = false;
queryspeed->count = 0;
}
}
else
{
if (queryspeed->count >= queryspeed->limit)
{
MXS_INFO("rule '%s': query limit triggered (%d queries in %d seconds), "
"denying queries from user for %d seconds.", name.c_str(),
queryspeed->limit, queryspeed->period, queryspeed->cooldown);
queryspeed->triggered = time_now;
queryspeed->active = true;
matches = true;
double blocked_for = queryspeed->cooldown - difftime(time_now, queryspeed->triggered);
*msg = create_error("Queries denied for %f seconds", blocked_for);
}
else if (queryspeed->count > 0 &&
difftime(time_now, queryspeed->first_query) <= queryspeed->period)
{
queryspeed->count++;
}
else
{
queryspeed->first_query = time_now;
queryspeed->count = 1;
}
}
return matches;
}