diff --git a/scripts/base/protocols/redis/main.zeek b/scripts/base/protocols/redis/main.zeek index f14d49d74f..07b6a6a61d 100644 --- a/scripts/base/protocols/redis/main.zeek +++ b/scripts/base/protocols/redis/main.zeek @@ -74,17 +74,17 @@ export { option max_pending_commands = 10000; # These commands enter subscribed mode - global enter_subscribed_mode = [KnownCommand_PSUBSCRIBE, - KnownCommand_SSUBSCRIBE, KnownCommand_SUBSCRIBE]; + global enter_subscribed_mode = [RedisCommand_PSUBSCRIBE, + RedisCommand_SSUBSCRIBE, RedisCommand_SUBSCRIBE]; # These commands exit subscribed mode - global exit_subscribed_mode = [KnownCommand_RESET, KnownCommand_QUIT]; + global exit_subscribed_mode = [RedisCommand_RESET, RedisCommand_QUIT]; # These commands don't expect a response (ever) - their replies are out of band. - global no_response_commands = [KnownCommand_PSUBSCRIBE, - KnownCommand_PUNSUBSCRIBE, KnownCommand_SSUBSCRIBE, - KnownCommand_SUBSCRIBE, KnownCommand_SUNSUBSCRIBE, - KnownCommand_UNSUBSCRIBE]; + global no_response_commands = [RedisCommand_PSUBSCRIBE, + RedisCommand_PUNSUBSCRIBE, RedisCommand_SSUBSCRIBE, + RedisCommand_SUBSCRIBE, RedisCommand_SUNSUBSCRIBE, + RedisCommand_UNSUBSCRIBE]; } redef record connection += { @@ -147,7 +147,7 @@ function is_last_interval_closed(c: connection): bool c$redis_state$no_reply_ranges[-1]?$end; } -event Redis::hello_command(c: connection, hello: HelloCommand) +event hello_command(c: connection, hello: HelloCommand) { if ( ! c?$redis_state ) make_new_state(c); @@ -156,7 +156,7 @@ event Redis::hello_command(c: connection, hello: HelloCommand) c$redis_state$resp_version = RESP3; } -event Redis::command(c: connection, cmd: Command) +event command(c: connection, cmd: Command) { if ( ! c?$redis_state ) make_new_state(c); @@ -196,7 +196,7 @@ event Redis::command(c: connection, cmd: Command) # CLIENT commands can skip a number of replies and may be used with # pipelining. We need special logic in order to track the command/reply # pairs. - if ( cmd?$known && cmd$known == KnownCommand_CLIENT ) + if ( cmd?$known && cmd$known == RedisCommand_CLIENT ) { # All 3 CLIENT commands we care about have 3 elements if ( |cmd$raw| == 3 ) @@ -291,11 +291,12 @@ function log_from(c: connection, previous_reply_num: count) } } -event Redis::reply(c: connection, data: ReplyData) +event reply(c: connection, data: ReplyData) { if ( ! c?$redis_state ) make_new_state(c); + # If the server is talking in RESP3, mark accordingly, even if we didn't see HELLO if ( data$min_protocol_version == 3 ) { c$redis_state$resp_version = RESP3; @@ -307,6 +308,7 @@ event Redis::reply(c: connection, data: ReplyData) event server_push(c, data); return; } + local previous_reply_num = c$redis_state$current_reply; c$redis_state$current_reply = reply_num(c); set_state(c, F); @@ -320,7 +322,7 @@ event Redis::reply(c: connection, data: ReplyData) clear_table(c$redis_state$skip_commands); } -event Redis::error(c: connection, data: ReplyData) +event error(c: connection, data: ReplyData) { if ( ! c?$redis_state ) make_new_state(c); diff --git a/scripts/base/protocols/redis/spicy-events.zeek b/scripts/base/protocols/redis/spicy-events.zeek index eb950e1267..8e0e6c733e 100644 --- a/scripts/base/protocols/redis/spicy-events.zeek +++ b/scripts/base/protocols/redis/spicy-events.zeek @@ -55,7 +55,7 @@ export { ## The value, if this command is known to have a value value: string &log &optional; ## The command in an enum if it was known - known: KnownCommand &optional; + known: RedisCommand &optional; }; ## A generic Redis reply from the client. diff --git a/src/analyzer/protocol/redis/redis.spicy b/src/analyzer/protocol/redis/redis.spicy index ec508a578f..c8dc9a8003 100644 --- a/src/analyzer/protocol/redis/redis.spicy +++ b/src/analyzer/protocol/redis/redis.spicy @@ -6,7 +6,7 @@ module Redis; import RESP; -public type KnownCommand = enum { +public type RedisCommand = enum { APPEND, AUTH, BITCOUNT, @@ -64,7 +64,7 @@ type Command = struct { name: bytes; key: optional; value: optional; - known: optional; + known: optional; }; # This just assumes all elements in the array is a bulk string and puts them in a vector @@ -158,44 +158,44 @@ function parse_command(raw: vector): Command { if (|raw| >= 2) { switch (*cmd) { - case KnownCommand::KEYS: + case RedisCommand::KEYS: parsed.key = raw[1]; - case KnownCommand::APPEND, - KnownCommand::BITCOUNT, - KnownCommand::BITFIELD, - KnownCommand::BITFIELD_RO, - KnownCommand::BITPOS, - KnownCommand::BLPOP, - KnownCommand::BRPOP, - KnownCommand::COPY, - KnownCommand::DECR, - KnownCommand::DECRBY, - KnownCommand::DEL, - KnownCommand::DUMP, - KnownCommand::EXISTS, - KnownCommand::EXPIRE, - KnownCommand::EXPIREAT, - KnownCommand::EXPIRETIME, - KnownCommand::GET, - KnownCommand::GETBIT, - KnownCommand::GETDEL, - KnownCommand::GETEX, - KnownCommand::GETRANGE, - KnownCommand::GETSET, - KnownCommand::HDEL, - KnownCommand::HGET, - KnownCommand::HSET, - KnownCommand::INCR, - KnownCommand::INCRBY, - KnownCommand::MGET, - KnownCommand::MOVE, - KnownCommand::MSET, - KnownCommand::PERSIST, - KnownCommand::RENAME, - KnownCommand::SET, - KnownCommand::STRLEN, - KnownCommand::TTL, - KnownCommand::TYPE: + case RedisCommand::APPEND, + RedisCommand::BITCOUNT, + RedisCommand::BITFIELD, + RedisCommand::BITFIELD_RO, + RedisCommand::BITPOS, + RedisCommand::BLPOP, + RedisCommand::BRPOP, + RedisCommand::COPY, + RedisCommand::DECR, + RedisCommand::DECRBY, + RedisCommand::DEL, + RedisCommand::DUMP, + RedisCommand::EXISTS, + RedisCommand::EXPIRE, + RedisCommand::EXPIREAT, + RedisCommand::EXPIRETIME, + RedisCommand::GET, + RedisCommand::GETBIT, + RedisCommand::GETDEL, + RedisCommand::GETEX, + RedisCommand::GETRANGE, + RedisCommand::GETSET, + RedisCommand::HDEL, + RedisCommand::HGET, + RedisCommand::HSET, + RedisCommand::INCR, + RedisCommand::INCRBY, + RedisCommand::MGET, + RedisCommand::MOVE, + RedisCommand::MSET, + RedisCommand::PERSIST, + RedisCommand::RENAME, + RedisCommand::SET, + RedisCommand::STRLEN, + RedisCommand::TTL, + RedisCommand::TYPE: parsed.key = raw[1]; default: (); } @@ -203,22 +203,22 @@ function parse_command(raw: vector): Command { if (|raw| >= 3) { switch (*cmd) { - case KnownCommand::SET, - KnownCommand::APPEND, - KnownCommand::DECRBY, - KnownCommand::EXPIRE, - KnownCommand::EXPIREAT, - KnownCommand::GETBIT, - KnownCommand::GETSET, - KnownCommand::HDEL, - KnownCommand::HGET, - KnownCommand::INCRBY, - KnownCommand::MOVE, - KnownCommand::MSET, - KnownCommand::RENAME: + case RedisCommand::SET, + RedisCommand::APPEND, + RedisCommand::DECRBY, + RedisCommand::EXPIRE, + RedisCommand::EXPIREAT, + RedisCommand::GETBIT, + RedisCommand::GETSET, + RedisCommand::HDEL, + RedisCommand::HGET, + RedisCommand::INCRBY, + RedisCommand::MOVE, + RedisCommand::MSET, + RedisCommand::RENAME: parsed.value = raw[2]; # Op first, destination second, then a list of keys. Just log dest - case KnownCommand::BITOP: parsed.key = raw[2]; + case RedisCommand::BITOP: parsed.key = raw[2]; default: (); } } @@ -226,7 +226,7 @@ function parse_command(raw: vector): Command { if (|raw| >= 4) { switch (*cmd) { # timeout, numkeys, then key - case KnownCommand::BLMPOP: parsed.key = raw[3]; + case RedisCommand::BLMPOP: parsed.key = raw[3]; default: (); } } @@ -234,60 +234,60 @@ function parse_command(raw: vector): Command { return parsed; } -function command_from(cmd_bytes: bytes): optional { - local cmd: optional = Null; +function command_from(cmd_bytes: bytes): optional { + local cmd: optional = Null; switch (cmd_bytes.lower()) { - case b"append": cmd = KnownCommand::APPEND; - case b"auth": cmd = KnownCommand::AUTH; - case b"bitcount": cmd = KnownCommand::BITCOUNT; - case b"bitfield": cmd = KnownCommand::BITFIELD; - case b"bitfield_ro": cmd = KnownCommand::BITFIELD_RO; - case b"bitop": cmd = KnownCommand::BITOP; - case b"bitpos": cmd = KnownCommand::BITPOS; - case b"blmpop": cmd = KnownCommand::BLMPOP; - case b"blpop": cmd = KnownCommand::BLPOP; - case b"brpop": cmd = KnownCommand::BRPOP; - case b"client": cmd = KnownCommand::CLIENT; - case b"copy": cmd = KnownCommand::COPY; - case b"decr": cmd = KnownCommand::DECR; - case b"decrby": cmd = KnownCommand::DECRBY; - case b"del": cmd = KnownCommand::DEL; - case b"dump": cmd = KnownCommand::DUMP; - case b"exists": cmd = KnownCommand::EXISTS; - case b"expire": cmd = KnownCommand::EXPIRE; - case b"expireat": cmd = KnownCommand::EXPIREAT; - case b"expiretime": cmd = KnownCommand::EXPIRETIME; - case b"expiretime": cmd = KnownCommand::EXPIRETIME; - case b"get": cmd = KnownCommand::GET; - case b"getbit": cmd = KnownCommand::GETBIT; - case b"getdel": cmd = KnownCommand::GETDEL; - case b"getex": cmd = KnownCommand::GETEX; - case b"getrange": cmd = KnownCommand::GETRANGE; - case b"getset": cmd = KnownCommand::GETSET; - case b"hdel": cmd = KnownCommand::HDEL; - case b"hello": cmd = KnownCommand::HELLO; - case b"hget": cmd = KnownCommand::HGET; - case b"hset": cmd = KnownCommand::HSET; - case b"incr": cmd = KnownCommand::INCR; - case b"incrby": cmd = KnownCommand::INCRBY; - case b"keys": cmd = KnownCommand::KEYS; - case b"mget": cmd = KnownCommand::MGET; - case b"move": cmd = KnownCommand::MOVE; - case b"mset": cmd = KnownCommand::MSET; - case b"persist": cmd = KnownCommand::PERSIST; - case b"psubscribe": cmd = KnownCommand::PSUBSCRIBE; - case b"punsubscribe": cmd = KnownCommand::PUNSUBSCRIBE; - case b"quit": cmd = KnownCommand::QUIT; - case b"rename": cmd = KnownCommand::RENAME; - case b"reset": cmd = KnownCommand::RESET; - case b"set": cmd = KnownCommand::SET; - case b"strlen": cmd = KnownCommand::STRLEN; - case b"ssubscribe": cmd = KnownCommand::SSUBSCRIBE; - case b"subscribe": cmd = KnownCommand::SUBSCRIBE; - case b"sunsubscribe": cmd = KnownCommand::SUNSUBSCRIBE; - case b"ttl": cmd = KnownCommand::TTL; - case b"type": cmd = KnownCommand::TYPE; - case b"unsubscribe": cmd = KnownCommand::UNSUBSCRIBE; + case b"append": cmd = RedisCommand::APPEND; + case b"auth": cmd = RedisCommand::AUTH; + case b"bitcount": cmd = RedisCommand::BITCOUNT; + case b"bitfield": cmd = RedisCommand::BITFIELD; + case b"bitfield_ro": cmd = RedisCommand::BITFIELD_RO; + case b"bitop": cmd = RedisCommand::BITOP; + case b"bitpos": cmd = RedisCommand::BITPOS; + case b"blmpop": cmd = RedisCommand::BLMPOP; + case b"blpop": cmd = RedisCommand::BLPOP; + case b"brpop": cmd = RedisCommand::BRPOP; + case b"client": cmd = RedisCommand::CLIENT; + case b"copy": cmd = RedisCommand::COPY; + case b"decr": cmd = RedisCommand::DECR; + case b"decrby": cmd = RedisCommand::DECRBY; + case b"del": cmd = RedisCommand::DEL; + case b"dump": cmd = RedisCommand::DUMP; + case b"exists": cmd = RedisCommand::EXISTS; + case b"expire": cmd = RedisCommand::EXPIRE; + case b"expireat": cmd = RedisCommand::EXPIREAT; + case b"expiretime": cmd = RedisCommand::EXPIRETIME; + case b"expiretime": cmd = RedisCommand::EXPIRETIME; + case b"get": cmd = RedisCommand::GET; + case b"getbit": cmd = RedisCommand::GETBIT; + case b"getdel": cmd = RedisCommand::GETDEL; + case b"getex": cmd = RedisCommand::GETEX; + case b"getrange": cmd = RedisCommand::GETRANGE; + case b"getset": cmd = RedisCommand::GETSET; + case b"hdel": cmd = RedisCommand::HDEL; + case b"hello": cmd = RedisCommand::HELLO; + case b"hget": cmd = RedisCommand::HGET; + case b"hset": cmd = RedisCommand::HSET; + case b"incr": cmd = RedisCommand::INCR; + case b"incrby": cmd = RedisCommand::INCRBY; + case b"keys": cmd = RedisCommand::KEYS; + case b"mget": cmd = RedisCommand::MGET; + case b"move": cmd = RedisCommand::MOVE; + case b"mset": cmd = RedisCommand::MSET; + case b"persist": cmd = RedisCommand::PERSIST; + case b"psubscribe": cmd = RedisCommand::PSUBSCRIBE; + case b"punsubscribe": cmd = RedisCommand::PUNSUBSCRIBE; + case b"quit": cmd = RedisCommand::QUIT; + case b"rename": cmd = RedisCommand::RENAME; + case b"reset": cmd = RedisCommand::RESET; + case b"set": cmd = RedisCommand::SET; + case b"strlen": cmd = RedisCommand::STRLEN; + case b"ssubscribe": cmd = RedisCommand::SSUBSCRIBE; + case b"subscribe": cmd = RedisCommand::SUBSCRIBE; + case b"sunsubscribe": cmd = RedisCommand::SUNSUBSCRIBE; + case b"ttl": cmd = RedisCommand::TTL; + case b"type": cmd = RedisCommand::TYPE; + case b"unsubscribe": cmd = RedisCommand::UNSUBSCRIBE; default: cmd = Null; } @@ -352,7 +352,7 @@ public function make_set(command: Command): Set { } public function is_set(data: RESP::ClientData): bool { - return data.command.known && *(data.command.known) == KnownCommand::SET && data.command.key && data.command.value; + return data.command.known && *(data.command.known) == RedisCommand::SET && data.command.key && data.command.value; } type Get = struct { @@ -365,7 +365,7 @@ public function make_get(command: Command): Get { } 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) == RedisCommand::GET && |data.command.raw| >= 2; } type Auth = struct { @@ -383,7 +383,7 @@ public function make_auth(command: Command): Auth { } public function is_auth(data: RESP::ClientData): bool { - return data.command.known && *(data.command.known) == KnownCommand::AUTH && |data.command.raw| >= 2; + return data.command.known && *(data.command.known) == RedisCommand::AUTH && |data.command.raw| >= 2; } type Hello = struct { @@ -398,7 +398,7 @@ public function make_hello(command: Command): Hello { } public function is_hello(data: RESP::ClientData): bool { - return data.command.known && *(data.command.known) == KnownCommand::HELLO; + return data.command.known && *(data.command.known) == RedisCommand::HELLO; } type ReplyData = struct { @@ -431,7 +431,7 @@ function bulk_string_content(bulk: RESP::BulkString): bytes { return b""; } -function stringify_map(data: RESP::Map): bytes { +function stringify_map(data: RESP::Map&): bytes { local res = b"{"; local first = True; local i = 0; @@ -451,7 +451,7 @@ function stringify_map(data: RESP::Map): bytes { } # Returns the bytes string value of this, or Null if it cannot. -function stringify(data: RESP::Data): bytes { +function stringify(data: RESP::Data&): bytes { if (data?.simple_string) return data.simple_string.content; else if (data?.simple_error) diff --git a/src/analyzer/protocol/redis/resp.evt b/src/analyzer/protocol/redis/resp.evt index c84ab36084..c197ecd4f4 100644 --- a/src/analyzer/protocol/redis/resp.evt +++ b/src/analyzer/protocol/redis/resp.evt @@ -7,7 +7,7 @@ protocol analyzer Redis over TCP: import RESP; import Redis; -export Redis::KnownCommand; +export Redis::RedisCommand; on RESP::ClientData if ( Redis::is_set(self) ) -> event Redis::set_command($conn, Redis::make_set(self.command)); on RESP::ClientData if ( Redis::is_get(self) ) -> event Redis::get_command($conn, Redis::make_get(self.command).key); diff --git a/src/analyzer/protocol/redis/resp.spicy b/src/analyzer/protocol/redis/resp.spicy index 6cced57ffd..76899dca36 100644 --- a/src/analyzer/protocol/redis/resp.spicy +++ b/src/analyzer/protocol/redis/resp.spicy @@ -116,7 +116,7 @@ type Data = unit(depth: uint8&) { DataType::MAP -> map_: Map(depth); DataType::SET -> set_: Set(depth); # "Push events are encoded similarly to arrays, differing only in their - # first byte" - TODO: can probably make it more obvious, though + # first byte" DataType::PUSH -> push: Array(depth); }; @@ -197,22 +197,11 @@ type BigNum = unit { type Map = unit(depth: uint8&) { num_elements: RedisBytes &convert=$$.to_uint(10); - # TODO: How can I make this into a map? Alternatively, how can I do this better? raw_data: Data(depth)[self.num_elements * 2]; - - # TODO: This is broken. See https://github.com/zeek/spicy/issues/2061 - # var key_val_pairs: vector>; - # on raw_data { - # while (local i = 0; i < self.num_elements) { - # self.key_val_pairs.push_back(($$[i], $$[i + 1])); - # i += 2; - # } - # } }; type Set = unit(depth: uint8&) { num_elements: RedisBytes &convert=$$.to_uint(10) &requires=self.num_elements <= MAX_SIZE; - # TODO: This should be a set but doesn't go in the backed C++ set elements: Data(depth)[self.num_elements]; }; diff --git a/testing/btest/Baseline/coverage.record-fields/out.default b/testing/btest/Baseline/coverage.record-fields/out.default index 2d7e20e22b..429564ff96 100644 --- a/testing/btest/Baseline/coverage.record-fields/out.default +++ b/testing/btest/Baseline/coverage.record-fields/out.default @@ -589,7 +589,7 @@ connection { * cmd: record Redis::Command, log=T, optional=F Redis::Command { * key: string, log=T, optional=T - * known: enum Redis::KnownCommand, log=F, optional=T + * known: enum Redis::RedisCommand, log=F, optional=T * name: string, log=T, optional=F * raw: vector of string, log=F, optional=F * value: string, log=T, optional=T