mirror of
https://github.com/zeek/zeek.git
synced 2025-10-02 14:48:21 +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;
|
key: string &log;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type AuthCommand: record {
|
||||||
|
username: string &optional;
|
||||||
|
password: string;
|
||||||
|
};
|
||||||
|
|
||||||
type Command: record {
|
type Command: record {
|
||||||
## The raw command, exactly as parsed
|
## The raw command, exactly as parsed
|
||||||
raw: vector of string;
|
raw: vector of string;
|
||||||
|
@ -71,15 +76,18 @@ export {
|
||||||
|
|
||||||
type State: record {
|
type State: record {
|
||||||
## Pending requests.
|
## Pending requests.
|
||||||
pending: table[count] of Info;
|
pending: table[count] of Info;
|
||||||
## Current request in the pending queue.
|
## Current request in the pending queue.
|
||||||
current_request: count &default=0;
|
current_request: count &default=0;
|
||||||
## Current response in the pending queue.
|
## Current response in the pending queue.
|
||||||
current_response: count &default=0;
|
current_response: count &default=0;
|
||||||
## Ranges where we do not expect a response
|
## Ranges where we do not expect a response
|
||||||
## Each range is one or two elements, one meaning it's unbounded, two meaning
|
## Each range is one or two elements, one meaning it's unbounded, two meaning
|
||||||
## it begins at one and ends at the second.
|
## it begins at one and ends at the second.
|
||||||
no_response_ranges: vector of vector of count;
|
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
|
# 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);
|
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
|
function new_redis_session(c: connection): Info
|
||||||
{
|
{
|
||||||
return Info($ts=network_time(), $uid=c$uid, $id=c$id);
|
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)
|
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;
|
local current: count;
|
||||||
if ( is_orig ) current = c$redis_state$current_request;
|
if ( is_orig )
|
||||||
else current = c$redis_state$current_response;
|
current = c$redis_state$current_request;
|
||||||
|
else
|
||||||
|
current = c$redis_state$current_response;
|
||||||
|
|
||||||
if ( current !in c$redis_state$pending )
|
if ( current !in c$redis_state$pending )
|
||||||
c$redis_state$pending[current] = new_redis_session(c);
|
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
|
# Returns true if the last interval exists and is closed
|
||||||
function is_last_interval_closed(c: connection): bool
|
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)
|
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);
|
Reporter::conn_weird("Redis_excessive_pipelining", c);
|
||||||
|
# Delete the current state and restart later. We'll be in a weird state, but
|
||||||
# Just spit out what we have
|
# really we want to abort. I don't quite get how to register this as a
|
||||||
while ( c$redis_state$current_response < c$redis_state$current_request )
|
# violation. :)
|
||||||
{
|
delete c$redis_state;
|
||||||
local cr = c$redis_state$current_response;
|
return;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
++c$redis_state$current_request;
|
++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 ( to_lower(command$raw[2]) == "on" )
|
||||||
{
|
{
|
||||||
# If the last range is open, close it here. Otherwise, noop
|
# 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 )
|
if ( |range| == 1 )
|
||||||
{
|
{
|
||||||
range += c$redis_state$current_request;
|
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" )
|
if ( to_lower(command$raw[2]) == "off" )
|
||||||
{
|
{
|
||||||
# Only add a new interval if the last one is closed
|
# 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);
|
c$redis_state$no_response_ranges += vector(c$redis_state$current_request);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ( to_lower(command$raw[2]) == "skip" )
|
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
|
# 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 )
|
for ( i in c$redis_state$no_response_ranges )
|
||||||
{
|
{
|
||||||
local range = c$redis_state$no_response_ranges[i];
|
local range = c$redis_state$no_response_ranges[i];
|
||||||
assert |range| >= 1;
|
assert | range | >= 1;
|
||||||
if ( |range| == 1 && resp_num > range[0] )
|
if ( |range| == 1 && resp_num > range[0] )
|
||||||
{} # TODO: This is necessary if not using pipelining
|
{ } # TODO: This is necessary if not using pipelining
|
||||||
if ( |range| == 2 && resp_num >= range[0] && resp_num < range[1] )
|
if ( |range| == 2 && resp_num >= range[0] && resp_num < range[1] )
|
||||||
return 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)
|
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;
|
local previous_response_num = c$redis_state$current_response;
|
||||||
c$redis_state$current_response = response_num(c);
|
c$redis_state$current_response = response_num(c);
|
||||||
|
@ -232,27 +257,37 @@ event Redis::server_data(c: connection, is_orig: bool, data: ServerData)
|
||||||
next;
|
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]);
|
Log::write(Redis::LOG, c$redis_state$pending[previous_response_num]);
|
||||||
delete c$redis_state$pending[previous_response_num];
|
delete c$redis_state$pending[previous_response_num];
|
||||||
}
|
}
|
||||||
previous_response_num += 1;
|
previous_response_num += 1;
|
||||||
}
|
}
|
||||||
# Log this one
|
# Log this one if we have the request and response
|
||||||
Log::write(Redis::LOG, c$redis);
|
if ( c$redis?$cmd )
|
||||||
delete c$redis_state$pending[c$redis_state$current_response];
|
{
|
||||||
|
Log::write(Redis::LOG, c$redis);
|
||||||
|
delete c$redis_state$pending[c$redis_state$current_response];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
hook finalize_redis(c: connection)
|
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.
|
# 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 )
|
for ( r, info in c$redis_state$pending )
|
||||||
{
|
{
|
||||||
# We don't use pending elements at index 0.
|
# We don't use pending elements at index 0.
|
||||||
if ( r == 0 ) next;
|
if ( r == 0 )
|
||||||
|
next;
|
||||||
Log::write(Redis::LOG, info);
|
Log::write(Redis::LOG, info);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ import RESP;
|
||||||
|
|
||||||
public type KnownCommand = enum {
|
public type KnownCommand = enum {
|
||||||
APPEND,
|
APPEND,
|
||||||
|
AUTH,
|
||||||
BITCOUNT,
|
BITCOUNT,
|
||||||
BITFIELD,
|
BITFIELD,
|
||||||
BITFIELD_RO,
|
BITFIELD_RO,
|
||||||
|
@ -234,6 +235,7 @@ function command_from(cmd_bytes: bytes): optional<KnownCommand> {
|
||||||
switch (cmd_bytes.lower()) {
|
switch (cmd_bytes.lower()) {
|
||||||
case b"set": cmd = KnownCommand::SET;
|
case b"set": cmd = KnownCommand::SET;
|
||||||
case b"append": cmd = KnownCommand::APPEND;
|
case b"append": cmd = KnownCommand::APPEND;
|
||||||
|
case b"auth": cmd = KnownCommand::AUTH;
|
||||||
case b"bitcount": cmd = KnownCommand::BITCOUNT;
|
case b"bitcount": cmd = KnownCommand::BITCOUNT;
|
||||||
case b"bitfield": cmd = KnownCommand::BITFIELD;
|
case b"bitfield": cmd = KnownCommand::BITFIELD;
|
||||||
case b"bitfield_ro": cmd = KnownCommand::BITFIELD_RO;
|
case b"bitfield_ro": cmd = KnownCommand::BITFIELD_RO;
|
||||||
|
@ -352,3 +354,21 @@ public function make_get(command: Command): Get {
|
||||||
public function is_get(data: RESP::ClientData): bool {
|
public function is_get(data: RESP::ClientData): bool {
|
||||||
return data.command.known && *(data.command.known) == KnownCommand::GET && |data.command.raw| >= 2;
|
return data.command.known && *(data.command.known) == KnownCommand::GET && |data.command.raw| >= 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Auth = struct {
|
||||||
|
username: optional<bytes>;
|
||||||
|
password: bytes;
|
||||||
|
};
|
||||||
|
|
||||||
|
public function make_auth(command: Command): Auth {
|
||||||
|
assert |command.raw| >= 2 : "AUTH must have arguments";
|
||||||
|
if (|command.raw| == 2) {
|
||||||
|
return [$username = Null, $password = command.raw[1]];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [$username = command.raw[1], $password = command.raw[2]];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function is_auth(data: RESP::ClientData): bool {
|
||||||
|
return data.command.known && *(data.command.known) == KnownCommand::AUTH && |data.command.raw| >= 2;
|
||||||
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ export Zeek_Redis::ZeekServerData;
|
||||||
|
|
||||||
on RESP::ClientData if ( Redis::is_set(self) ) -> event Redis::set_command($conn, $is_orig, Redis::make_set(self.command));
|
on RESP::ClientData if ( Redis::is_set(self) ) -> event Redis::set_command($conn, $is_orig, Redis::make_set(self.command));
|
||||||
on RESP::ClientData if ( Redis::is_get(self) ) -> event Redis::get_command($conn, $is_orig, Redis::make_get(self.command));
|
on RESP::ClientData if ( Redis::is_get(self) ) -> event Redis::get_command($conn, $is_orig, Redis::make_get(self.command));
|
||||||
|
on RESP::ClientData if ( Redis::is_auth(self) ) -> event Redis::auth_command($conn, $is_orig, Redis::make_auth(self.command));
|
||||||
|
|
||||||
# All client data is a command
|
# All client data is a command
|
||||||
on RESP::ClientData -> event Redis::command($conn, $is_orig, self.command);
|
on RESP::ClientData -> event Redis::command($conn, $is_orig, self.command);
|
||||||
|
|
|
@ -9,7 +9,7 @@ import spicy;
|
||||||
const MAX_SIZE = 1024 * 1024;
|
const MAX_SIZE = 1024 * 1024;
|
||||||
|
|
||||||
public type ClientMessages = unit {
|
public type ClientMessages = unit {
|
||||||
: (ClientData &synchronize)[];
|
: ClientData[];
|
||||||
};
|
};
|
||||||
|
|
||||||
public type ServerMessages = unit {
|
public type ServerMessages = unit {
|
||||||
|
@ -17,21 +17,22 @@ public type ServerMessages = unit {
|
||||||
};
|
};
|
||||||
|
|
||||||
public type ClientData = unit {
|
public type ClientData = unit {
|
||||||
%synchronize-after = b"\x0d\x0a";
|
on %init() { self.start = self.input(); }
|
||||||
|
|
||||||
# Clients can only be an array or inline
|
# Clients can only be an array or inline
|
||||||
ty: uint8 &convert=DataType($$);
|
ty: uint8 &convert=DataType($$) {
|
||||||
|
if (self.ty != DataType::ARRAY) {
|
||||||
|
# This is inline, so we need to reparse `ty`
|
||||||
|
self.set_input(self.start);
|
||||||
|
}
|
||||||
|
}
|
||||||
if (self.ty == DataType::ARRAY) {
|
if (self.ty == DataType::ARRAY) {
|
||||||
multibulk: Array;
|
multibulk: Array;
|
||||||
} else {
|
} else {
|
||||||
# HACK: If the type isn'tan array, this is just some random unserialized
|
inline: RedisBytes &max-size=1024;
|
||||||
# string until \r\n - do this by prepending the type to the remaining bytes.
|
|
||||||
# Formally in Redis code, that's an "inline command."
|
|
||||||
#
|
|
||||||
# As an extra point, this is handled in redis in `processInlineBuffer`,
|
|
||||||
# which has a hardcoded limit of 1024*64. That seems too big. We'll do 1024.
|
|
||||||
inline: RedisBytes &convert=(pack(cast<uint8>(self.ty), spicy::ByteOrder::Network) + $$) &max-size=1024;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var start: iterator<stream>;
|
||||||
var command: Redis::Command;
|
var command: Redis::Command;
|
||||||
|
|
||||||
on %done {
|
on %done {
|
||||||
|
@ -152,10 +153,10 @@ type Set = unit {
|
||||||
elements: Data[self.num_elements];
|
elements: Data[self.num_elements];
|
||||||
};
|
};
|
||||||
|
|
||||||
on Data::%done {
|
on ServerData::%done {
|
||||||
spicy::accept_input();
|
spicy::accept_input();
|
||||||
}
|
}
|
||||||
|
|
||||||
on Data::%error {
|
on ServerData::%error {
|
||||||
spicy::decline_input("error while parsing RESP data");
|
spicy::decline_input("error while parsing RESP server data");
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
|
||||||
|
#separator \x09
|
||||||
|
#set_separator ,
|
||||||
|
#empty_field (empty)
|
||||||
|
#unset_field -
|
||||||
|
#path redis
|
||||||
|
#open XXXX-XX-XX-XX-XX-XX
|
||||||
|
#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p cmd.command cmd.key cmd.value response.err response.data
|
||||||
|
#types time string addr port addr port string string string bool string
|
||||||
|
XXXXXXXXXX.XXXXXX CtPZjS20MLrsMUOJi2 127.0.0.1 53099 127.0.0.1 6379 AUTH - - F OK
|
||||||
|
XXXXXXXXXX.XXXXXX CtPZjS20MLrsMUOJi2 127.0.0.1 53099 127.0.0.1 6379 PING - - F OK
|
||||||
|
#close XXXX-XX-XX-XX-XX-XX
|
|
@ -0,0 +1,10 @@
|
||||||
|
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
|
||||||
|
AUTH
|
||||||
|
username: notauser
|
||||||
|
password: notapassword
|
||||||
|
AUTH
|
||||||
|
username: default
|
||||||
|
password: defaultpasswordinvalid
|
||||||
|
AUTH
|
||||||
|
username: noone
|
||||||
|
password: password
|
|
@ -7,17 +7,10 @@
|
||||||
#open XXXX-XX-XX-XX-XX-XX
|
#open XXXX-XX-XX-XX-XX-XX
|
||||||
#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p cmd.command cmd.key cmd.value response.err response.data
|
#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p cmd.command cmd.key cmd.value response.err response.data
|
||||||
#types time string addr port addr port string string string bool string
|
#types time string addr port addr port string string string bool string
|
||||||
XXXXXXXXXX.XXXXXX ClEkJM2Vm5giqnMf4h ::1 57156 ::1 6379 PING - - - -
|
XXXXXXXXXX.XXXXXX ClEkJM2Vm5giqnMf4h ::1 57156 ::1 6379 PING - - F PONG
|
||||||
XXXXXXXXXX.XXXXXX ClEkJM2Vm5giqnMf4h ::1 57156 ::1 6379 PING - - - -
|
XXXXXXXXXX.XXXXXX ClEkJM2Vm5giqnMf4h ::1 57156 ::1 6379 PING - - F PONG
|
||||||
XXXXXXXXXX.XXXXXX ClEkJM2Vm5giqnMf4h ::1 57156 ::1 6379 PING - - - -
|
XXXXXXXXXX.XXXXXX ClEkJM2Vm5giqnMf4h ::1 57156 ::1 6379 PING - - F PONG
|
||||||
XXXXXXXXXX.XXXXXX ClEkJM2Vm5giqnMf4h ::1 57156 ::1 6379 PING - - - -
|
XXXXXXXXXX.XXXXXX ClEkJM2Vm5giqnMf4h ::1 57156 ::1 6379 PING - - F PONG
|
||||||
XXXXXXXXXX.XXXXXX ClEkJM2Vm5giqnMf4h ::1 57156 ::1 6379 PING - - - -
|
|
||||||
XXXXXXXXXX.XXXXXX ClEkJM2Vm5giqnMf4h ::1 57156 ::1 6379 PING - - - -
|
|
||||||
XXXXXXXXXX.XXXXXX ClEkJM2Vm5giqnMf4h ::1 57156 ::1 6379 PING - - - -
|
|
||||||
XXXXXXXXXX.XXXXXX ClEkJM2Vm5giqnMf4h ::1 57156 ::1 6379 PING - - - -
|
|
||||||
XXXXXXXXXX.XXXXXX ClEkJM2Vm5giqnMf4h ::1 57156 ::1 6379 PING - - - -
|
|
||||||
XXXXXXXXXX.XXXXXX ClEkJM2Vm5giqnMf4h ::1 57156 ::1 6379 PING - - - -
|
|
||||||
XXXXXXXXXX.XXXXXX ClEkJM2Vm5giqnMf4h ::1 57156 ::1 6379 PING - - - -
|
|
||||||
XXXXXXXXXX.XXXXXX ClEkJM2Vm5giqnMf4h ::1 57156 ::1 6379 PING - - F PONG
|
XXXXXXXXXX.XXXXXX ClEkJM2Vm5giqnMf4h ::1 57156 ::1 6379 PING - - F PONG
|
||||||
XXXXXXXXXX.XXXXXX ClEkJM2Vm5giqnMf4h ::1 57156 ::1 6379 - - - F PONG
|
XXXXXXXXXX.XXXXXX ClEkJM2Vm5giqnMf4h ::1 57156 ::1 6379 - - - F PONG
|
||||||
XXXXXXXXXX.XXXXXX ClEkJM2Vm5giqnMf4h ::1 57156 ::1 6379 - - - F PONG
|
XXXXXXXXXX.XXXXXX ClEkJM2Vm5giqnMf4h ::1 57156 ::1 6379 - - - F PONG
|
||||||
|
@ -26,8 +19,4 @@ XXXXXXXXXX.XXXXXX ClEkJM2Vm5giqnMf4h ::1 57156 ::1 6379 - - - F PONG
|
||||||
XXXXXXXXXX.XXXXXX ClEkJM2Vm5giqnMf4h ::1 57156 ::1 6379 - - - F PONG
|
XXXXXXXXXX.XXXXXX ClEkJM2Vm5giqnMf4h ::1 57156 ::1 6379 - - - F PONG
|
||||||
XXXXXXXXXX.XXXXXX ClEkJM2Vm5giqnMf4h ::1 57156 ::1 6379 - - - F PONG
|
XXXXXXXXXX.XXXXXX ClEkJM2Vm5giqnMf4h ::1 57156 ::1 6379 - - - F PONG
|
||||||
XXXXXXXXXX.XXXXXX ClEkJM2Vm5giqnMf4h ::1 57156 ::1 6379 - - - F PONG
|
XXXXXXXXXX.XXXXXX ClEkJM2Vm5giqnMf4h ::1 57156 ::1 6379 - - - F PONG
|
||||||
XXXXXXXXXX.XXXXXX ClEkJM2Vm5giqnMf4h ::1 57156 ::1 6379 - - - F PONG
|
|
||||||
XXXXXXXXXX.XXXXXX ClEkJM2Vm5giqnMf4h ::1 57156 ::1 6379 - - - F PONG
|
|
||||||
XXXXXXXXXX.XXXXXX ClEkJM2Vm5giqnMf4h ::1 57156 ::1 6379 - - - F PONG
|
|
||||||
XXXXXXXXXX.XXXXXX ClEkJM2Vm5giqnMf4h ::1 57156 ::1 6379 - - - F PONG
|
|
||||||
#close XXXX-XX-XX-XX-XX-XX
|
#close XXXX-XX-XX-XX-XX-XX
|
||||||
|
|
BIN
testing/btest/Traces/redis/almost-resp.trace
Normal file
BIN
testing/btest/Traces/redis/almost-resp.trace
Normal file
Binary file not shown.
BIN
testing/btest/Traces/redis/auth.trace
Normal file
BIN
testing/btest/Traces/redis/auth.trace
Normal file
Binary file not shown.
|
@ -0,0 +1,8 @@
|
||||||
|
# @TEST-DOC: Test 2 commands that look like RESP, then server responses don't
|
||||||
|
#
|
||||||
|
# @TEST-EXEC: zeek -Cr $TRACES/redis/almost-resp.trace base/protocols/redis %INPUT >output
|
||||||
|
# @TEST-EXEC: btest-diff redis.log
|
||||||
|
#
|
||||||
|
# Really, the first 2 ARE Redis. The later ones should not be logged because we
|
||||||
|
# realized it's not Redis. The output from the server is:
|
||||||
|
# +OK\r\n+OK\r\nnot RESP\r\nStill not RESP\r\nNope\r\n
|
16
testing/btest/scripts/base/protocols/redis/auth.zeek
Normal file
16
testing/btest/scripts/base/protocols/redis/auth.zeek
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
# @TEST-DOC: Test Zeek with AUTH commands
|
||||||
|
#
|
||||||
|
# @TEST-EXEC: zeek -Cr $TRACES/redis/auth.trace base/protocols/redis %INPUT >output
|
||||||
|
# @TEST-EXEC: btest-diff output
|
||||||
|
|
||||||
|
event Redis::auth_command(c: connection, is_orig: bool,
|
||||||
|
command: Redis::AuthCommand)
|
||||||
|
{
|
||||||
|
print "AUTH";
|
||||||
|
if ( command?$username )
|
||||||
|
print fmt("username: %s", command$username);
|
||||||
|
else
|
||||||
|
print "username: default";
|
||||||
|
|
||||||
|
print fmt("password: %s", command$password);
|
||||||
|
}
|
|
@ -7,7 +7,8 @@
|
||||||
# code directly to the server, but it's useful to see if that trace might come
|
# code directly to the server, but it's useful to see if that trace might come
|
||||||
# up with something different. See:
|
# up with something different. See:
|
||||||
# https://redis.io/docs/latest/develop/use/patterns/bulk-loading/
|
# https://redis.io/docs/latest/develop/use/patterns/bulk-loading/
|
||||||
event Redis::set_command(c: connection, is_orig: bool, command: Redis::SetCommand)
|
event Redis::set_command(c: connection, is_orig: bool,
|
||||||
{
|
command: Redis::SetCommand)
|
||||||
print fmt("SET: %s %s", command$key, command$value);
|
{
|
||||||
}
|
print fmt("SET: %s %s", command$key, command$value);
|
||||||
|
}
|
||||||
|
|
|
@ -2,4 +2,3 @@
|
||||||
#
|
#
|
||||||
# @TEST-EXEC: zeek -Cr $TRACES/redis/reply-off-on-2conn.trace base/protocols/redis %INPUT >output
|
# @TEST-EXEC: zeek -Cr $TRACES/redis/reply-off-on-2conn.trace base/protocols/redis %INPUT >output
|
||||||
# @TEST-EXEC: btest-diff redis.log
|
# @TEST-EXEC: btest-diff redis.log
|
||||||
|
|
||||||
|
|
|
@ -2,4 +2,3 @@
|
||||||
#
|
#
|
||||||
# @TEST-EXEC: zeek -Cr $TRACES/redis/reply-off-on.trace base/protocols/redis %INPUT >output
|
# @TEST-EXEC: zeek -Cr $TRACES/redis/reply-off-on.trace base/protocols/redis %INPUT >output
|
||||||
# @TEST-EXEC: btest-diff redis.log
|
# @TEST-EXEC: btest-diff redis.log
|
||||||
|
|
||||||
|
|
|
@ -2,4 +2,3 @@
|
||||||
#
|
#
|
||||||
# @TEST-EXEC: zeek -Cr $TRACES/redis/client-skip-while-off.trace base/protocols/redis %INPUT >output
|
# @TEST-EXEC: zeek -Cr $TRACES/redis/client-skip-while-off.trace base/protocols/redis %INPUT >output
|
||||||
# @TEST-EXEC: btest-diff redis.log
|
# @TEST-EXEC: btest-diff redis.log
|
||||||
|
|
||||||
|
|
|
@ -4,12 +4,12 @@
|
||||||
# @TEST-EXEC: btest-diff output
|
# @TEST-EXEC: btest-diff output
|
||||||
# @TEST-EXEC: btest-diff redis.log
|
# @TEST-EXEC: btest-diff redis.log
|
||||||
|
|
||||||
redef Redis::ports += {
|
redef Redis::ports += { 10625/tcp, };
|
||||||
10625/tcp,
|
|
||||||
};
|
|
||||||
|
|
||||||
event Redis::set_command(c: connection, is_orig: bool, command: Redis::SetCommand)
|
event Redis::set_command(c: connection, is_orig: bool,
|
||||||
{
|
command: Redis::SetCommand)
|
||||||
# Print the whole command because these have extra data that's worth capturing.
|
{
|
||||||
print fmt("SET: %s %s expires in %d milliseconds", command$key, command$value, command$px);
|
# Print the whole command because these have extra data that's worth capturing.
|
||||||
}
|
print fmt("SET: %s %s expires in %d milliseconds", command$key, command$value,
|
||||||
|
command$px);
|
||||||
|
}
|
||||||
|
|
|
@ -4,8 +4,10 @@
|
||||||
# @TEST-EXEC: btest-diff output
|
# @TEST-EXEC: btest-diff output
|
||||||
# @TEST-EXEC: btest-diff redis.log
|
# @TEST-EXEC: btest-diff redis.log
|
||||||
|
|
||||||
event Redis::set_command(c: connection, is_orig: bool, command: Redis::SetCommand)
|
event Redis::set_command(c: connection, is_orig: bool,
|
||||||
{
|
command: Redis::SetCommand)
|
||||||
# Print the whole command because these have extra data that's worth capturing.
|
{
|
||||||
print fmt("SET: %s %s expires in %d milliseconds", command$key, command$value, command$px);
|
# Print the whole command because these have extra data that's worth capturing.
|
||||||
}
|
print fmt("SET: %s %s expires in %d milliseconds", command$key, command$value,
|
||||||
|
command$px);
|
||||||
|
}
|
||||||
|
|
|
@ -7,12 +7,14 @@
|
||||||
# Sometimes commands aren't serialized, like when pipelining. This still works! So we
|
# Sometimes commands aren't serialized, like when pipelining. This still works! So we
|
||||||
# should handle this. This particular example has a few commands, amongst them a SET and
|
# should handle this. This particular example has a few commands, amongst them a SET and
|
||||||
# a GET.
|
# a GET.
|
||||||
event Redis::set_command(c: connection, is_orig: bool, command: Redis::SetCommand)
|
event Redis::set_command(c: connection, is_orig: bool,
|
||||||
{
|
command: Redis::SetCommand)
|
||||||
print fmt("SET: %s %s", command$key, command$value);
|
{
|
||||||
}
|
print fmt("SET: %s %s", command$key, command$value);
|
||||||
|
}
|
||||||
|
|
||||||
event Redis::get_command(c: connection, is_orig: bool, command: Redis::GetCommand)
|
event Redis::get_command(c: connection, is_orig: bool,
|
||||||
{
|
command: Redis::GetCommand)
|
||||||
print fmt("GET: %s", command);
|
{
|
||||||
}
|
print fmt("GET: %s", command);
|
||||||
|
}
|
||||||
|
|
|
@ -3,7 +3,8 @@
|
||||||
# @TEST-EXEC: zeek -Cr $TRACES/redis/set.trace base/protocols/redis %INPUT >output
|
# @TEST-EXEC: zeek -Cr $TRACES/redis/set.trace base/protocols/redis %INPUT >output
|
||||||
# @TEST-EXEC: btest-diff output
|
# @TEST-EXEC: btest-diff output
|
||||||
|
|
||||||
event Redis::set_command(c: connection, is_orig: bool, command: Redis::SetCommand)
|
event Redis::set_command(c: connection, is_orig: bool,
|
||||||
{
|
command: Redis::SetCommand)
|
||||||
print fmt("Key: %s Value: %s", command$key, command$value);
|
{
|
||||||
}
|
print fmt("Key: %s Value: %s", command$key, command$value);
|
||||||
|
}
|
||||||
|
|
|
@ -1,11 +0,0 @@
|
||||||
# @TEST-DOC: Test parsing behavior of RESP.
|
|
||||||
#
|
|
||||||
# @TEST-EXEC: spicyc ${DIST}/analyzer/resp.spicy ${DIST}/analyzer/redis.spicy -j -d -o redis.hlto
|
|
||||||
#
|
|
||||||
# TODO: A lot of tests are possible from the docs and having them would be nice.
|
|
||||||
# But, a lot of characters ($, -, etc.) cause problems with TEST_EXEC. ugh.
|
|
||||||
# @TEST-EXEC: printf "+OK\x0d\x0a" | spicy-dump -p RESP::Data redis.hlto >>output 2>&1
|
|
||||||
# @TEST-EXEC: printf ":1000\x0d\x0a" | spicy-dump -p RESP::Data redis.hlto >>output 2>&1
|
|
||||||
# @TEST-EXEC: printf ":-1000\x0d\x0a" | spicy-dump -p RESP::Data redis.hlto >>output 2>&1
|
|
||||||
# @TEST-EXEC: printf ":+1000\x0d\x0a" | spicy-dump -p RESP::Data redis.hlto >>output 2>&1
|
|
||||||
# @TEST-EXEC: TEST_DIFF_CANONIFIER= btest-diff output
|
|
|
@ -4,12 +4,14 @@
|
||||||
# @TEST-EXEC: btest-diff output
|
# @TEST-EXEC: btest-diff output
|
||||||
# @TEST-EXEC: btest-diff redis.log
|
# @TEST-EXEC: btest-diff redis.log
|
||||||
|
|
||||||
event Redis::set_command(c: connection, is_orig: bool, command: Redis::SetCommand)
|
event Redis::set_command(c: connection, is_orig: bool,
|
||||||
{
|
command: Redis::SetCommand)
|
||||||
print fmt("SET: %s %s", command$key, command$value);
|
{
|
||||||
}
|
print fmt("SET: %s %s", command$key, command$value);
|
||||||
|
}
|
||||||
|
|
||||||
event Redis::get_command(c: connection, is_orig: bool, command: Redis::GetCommand)
|
event Redis::get_command(c: connection, is_orig: bool,
|
||||||
{
|
command: Redis::GetCommand)
|
||||||
print fmt("GET: %s", command);
|
{
|
||||||
}
|
print fmt("GET: %s", command);
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue