mirror of
https://github.com/zeek/zeek.git
synced 2025-10-02 06:38:20 +00:00
spicy-redis: Add some commands and touch up parsing
This commit is contained in:
parent
22bda56af3
commit
f0e9f46c7c
21 changed files with 200 additions and 114 deletions
|
@ -26,6 +26,11 @@ export {
|
|||
key: string &log;
|
||||
};
|
||||
|
||||
type AuthCommand: record {
|
||||
username: string &optional;
|
||||
password: string;
|
||||
};
|
||||
|
||||
type Command: record {
|
||||
## The raw command, exactly as parsed
|
||||
raw: vector of string;
|
||||
|
@ -71,15 +76,18 @@ export {
|
|||
|
||||
type State: record {
|
||||
## Pending requests.
|
||||
pending: table[count] of Info;
|
||||
pending: table[count] of Info;
|
||||
## Current request in the pending queue.
|
||||
current_request: count &default=0;
|
||||
current_request: count &default=0;
|
||||
## Current response in the pending queue.
|
||||
current_response: count &default=0;
|
||||
## Ranges where we do not expect a response
|
||||
## Each range is one or two elements, one meaning it's unbounded, two meaning
|
||||
## it begins at one and ends at the second.
|
||||
no_response_ranges: vector of vector of count;
|
||||
## We store if this analyzer had a violation to avoid logging if so.
|
||||
## This should not be super necessary, but worth a shot.
|
||||
violation: bool &default=F;
|
||||
};
|
||||
|
||||
# Redis specifically mentions 10k commands as a good pipelining threshold, so
|
||||
|
@ -102,6 +110,21 @@ event zeek_init() &priority=5
|
|||
Analyzer::register_for_ports(Analyzer::ANALYZER_SPICY_REDIS, ports);
|
||||
}
|
||||
|
||||
event analyzer_violation_info(atype: AllAnalyzers::Tag,
|
||||
info: AnalyzerViolationInfo)
|
||||
{
|
||||
if ( atype == Analyzer::ANALYZER_SPICY_REDIS )
|
||||
{
|
||||
if ( info?$c )
|
||||
{
|
||||
if ( info$c?$redis_state )
|
||||
{
|
||||
info$c$redis_state$violation = T;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function new_redis_session(c: connection): Info
|
||||
{
|
||||
return Info($ts=network_time(), $uid=c$uid, $id=c$id);
|
||||
|
@ -116,11 +139,14 @@ function make_new_state(c: connection)
|
|||
|
||||
function set_state(c: connection, is_orig: bool)
|
||||
{
|
||||
if ( ! c?$redis_state ) make_new_state(c);
|
||||
if ( ! c?$redis_state )
|
||||
make_new_state(c);
|
||||
|
||||
local current: count;
|
||||
if ( is_orig ) current = c$redis_state$current_request;
|
||||
else current = c$redis_state$current_response;
|
||||
if ( is_orig )
|
||||
current = c$redis_state$current_request;
|
||||
else
|
||||
current = c$redis_state$current_response;
|
||||
|
||||
if ( current !in c$redis_state$pending )
|
||||
c$redis_state$pending[current] = new_redis_session(c);
|
||||
|
@ -131,28 +157,24 @@ function set_state(c: connection, is_orig: bool)
|
|||
# Returns true if the last interval exists and is closed
|
||||
function is_last_interval_closed(c: connection): bool
|
||||
{
|
||||
return |c$redis_state$no_response_ranges| == 0 || |c$redis_state$no_response_ranges[|c$redis_state$no_response_ranges| - 1]| != 1;
|
||||
return |c$redis_state$no_response_ranges| == 0
|
||||
|| |c$redis_state$no_response_ranges[|c$redis_state$no_response_ranges| - 1]| != 1;
|
||||
}
|
||||
|
||||
event Redis::command(c: connection, is_orig: bool, command: Command)
|
||||
{
|
||||
if ( ! c?$redis_state ) make_new_state(c);
|
||||
if ( ! c?$redis_state )
|
||||
make_new_state(c);
|
||||
|
||||
if ( max_pending_requests > 0 && |c$redis_state$pending| > max_pending_requests )
|
||||
if ( max_pending_requests > 0
|
||||
&& |c$redis_state$pending| > max_pending_requests )
|
||||
{
|
||||
Reporter::conn_weird("Redis_excessive_pipelining", c);
|
||||
|
||||
# Just spit out what we have
|
||||
while ( c$redis_state$current_response < c$redis_state$current_request )
|
||||
{
|
||||
local cr = c$redis_state$current_response;
|
||||
if ( cr in c$redis_state$pending )
|
||||
{
|
||||
Log::write(Redis::LOG, c$redis_state$pending[cr]);
|
||||
delete c$redis_state$pending[cr];
|
||||
}
|
||||
++c$redis_state$current_response;
|
||||
}
|
||||
# Delete the current state and restart later. We'll be in a weird state, but
|
||||
# really we want to abort. I don't quite get how to register this as a
|
||||
# violation. :)
|
||||
delete c$redis_state;
|
||||
return;
|
||||
}
|
||||
|
||||
++c$redis_state$current_request;
|
||||
|
@ -164,9 +186,10 @@ event Redis::command(c: connection, is_orig: bool, command: Command)
|
|||
if ( to_lower(command$raw[2]) == "on" )
|
||||
{
|
||||
# If the last range is open, close it here. Otherwise, noop
|
||||
if ( |c$redis_state$no_response_ranges| > 0 )
|
||||
if ( |c$redis_state$no_response_ranges| > 0 )
|
||||
{
|
||||
local range = c$redis_state$no_response_ranges[|c$redis_state$no_response_ranges| - 1];
|
||||
local range = c$redis_state$no_response_ranges[|c$redis_state$no_response_ranges|
|
||||
- 1];
|
||||
if ( |range| == 1 )
|
||||
{
|
||||
range += c$redis_state$current_request;
|
||||
|
@ -176,16 +199,17 @@ event Redis::command(c: connection, is_orig: bool, command: Command)
|
|||
if ( to_lower(command$raw[2]) == "off" )
|
||||
{
|
||||
# Only add a new interval if the last one is closed
|
||||
if ( is_last_interval_closed(c) )
|
||||
if ( is_last_interval_closed(c) )
|
||||
{
|
||||
c$redis_state$no_response_ranges += vector(c$redis_state$current_request);
|
||||
}
|
||||
}
|
||||
if ( to_lower(command$raw[2]) == "skip" )
|
||||
{
|
||||
if ( is_last_interval_closed(c) )
|
||||
if ( is_last_interval_closed(c) )
|
||||
# It skips this one and the next one
|
||||
c$redis_state$no_response_ranges += vector(c$redis_state$current_request, c$redis_state$current_request + 2);
|
||||
c$redis_state$no_response_ranges += vector(c$redis_state$current_request,
|
||||
c$redis_state$current_request + 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -202,10 +226,10 @@ function response_num(c: connection): count
|
|||
for ( i in c$redis_state$no_response_ranges )
|
||||
{
|
||||
local range = c$redis_state$no_response_ranges[i];
|
||||
assert |range| >= 1;
|
||||
assert | range | >= 1;
|
||||
if ( |range| == 1 && resp_num > range[0] )
|
||||
{} # TODO: This is necessary if not using pipelining
|
||||
if ( |range| == 2 && resp_num >= range[0] && resp_num < range[1] )
|
||||
{ } # TODO: This is necessary if not using pipelining
|
||||
if ( |range| == 2 && resp_num >= range[0] && resp_num < range[1] )
|
||||
return range[1];
|
||||
}
|
||||
|
||||
|
@ -215,7 +239,8 @@ function response_num(c: connection): count
|
|||
|
||||
event Redis::server_data(c: connection, is_orig: bool, data: ServerData)
|
||||
{
|
||||
if ( ! c?$redis_state ) make_new_state(c);
|
||||
if ( ! c?$redis_state )
|
||||
make_new_state(c);
|
||||
|
||||
local previous_response_num = c$redis_state$current_response;
|
||||
c$redis_state$current_response = response_num(c);
|
||||
|
@ -232,27 +257,37 @@ event Redis::server_data(c: connection, is_orig: bool, data: ServerData)
|
|||
next;
|
||||
}
|
||||
|
||||
if ( previous_response_num in c$redis_state$pending )
|
||||
if ( previous_response_num in c$redis_state$pending &&
|
||||
c$redis_state$pending[previous_response_num]?$cmd )
|
||||
{
|
||||
Log::write(Redis::LOG, c$redis_state$pending[previous_response_num]);
|
||||
delete c$redis_state$pending[previous_response_num];
|
||||
}
|
||||
previous_response_num += 1;
|
||||
}
|
||||
# Log this one
|
||||
Log::write(Redis::LOG, c$redis);
|
||||
delete c$redis_state$pending[c$redis_state$current_response];
|
||||
# Log this one if we have the request and response
|
||||
if ( c$redis?$cmd )
|
||||
{
|
||||
Log::write(Redis::LOG, c$redis);
|
||||
delete c$redis_state$pending[c$redis_state$current_response];
|
||||
}
|
||||
}
|
||||
|
||||
hook finalize_redis(c: connection)
|
||||
{
|
||||
if ( c$redis_state$violation )
|
||||
{
|
||||
# If there's a violation, make sure everything gets deleted
|
||||
delete c$redis_state;
|
||||
}
|
||||
# Flush all pending but incomplete request/response pairs.
|
||||
if ( c?$redis_state )
|
||||
if ( c?$redis_state && c$redis_state$current_response != 0 )
|
||||
{
|
||||
for ( r, info in c$redis_state$pending )
|
||||
{
|
||||
# We don't use pending elements at index 0.
|
||||
if ( r == 0 ) next;
|
||||
if ( r == 0 )
|
||||
next;
|
||||
Log::write(Redis::LOG, info);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue