From 0d47952611198ef6b1163f366dc03922d20b1475 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 17 Apr 2024 09:42:04 +0200 Subject: Adding upstream version 7.94+git20230807.3be01efb1+dfsg. Signed-off-by: Daniel Baumann --- nselib/redis.lua | 148 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 148 insertions(+) create mode 100644 nselib/redis.lua (limited to 'nselib/redis.lua') diff --git a/nselib/redis.lua b/nselib/redis.lua new file mode 100644 index 0000000..fab9190 --- /dev/null +++ b/nselib/redis.lua @@ -0,0 +1,148 @@ +--- A minimalistic Redis (in-memory key-value data store) library. +-- +-- @author Patrik Karlsson + +local match = require "match" +local nmap = require "nmap" +local stdnse = require "stdnse" +local table = require "table" +_ENV = stdnse.module("redis", stdnse.seeall) + +Request = { + + new = function(self, cmd, ...) + local o = { cmd = cmd, args = {...} } + setmetatable (o,self) + self.__index = self + return o + end, + + __tostring = function(self) + local output = ("*%s\r\n$%d\r\n%s\r\n"):format(#self.args + 1, #self.cmd, self.cmd) + + for _, arg in ipairs(self.args) do + arg = tostring(arg) + output = output .. ("$%s\r\n%s\r\n"):format(#arg, arg) + end + + return output + end + +} + + +Response = { + + Type = { + STATUS = 0, + ERROR = 1, + INTEGER = 2, + BULK = 3, + MULTIBULK = 4, + }, + + new = function(self, socket) + local o = { socket = socket } + setmetatable (o,self) + self.__index = self + return o + end, + + receive = function(self) + local status, data = self.socket:receive_buf(match.pattern_limit("\r\n", 2048), false) + if ( not(status) ) then + return false, "Failed to receive data from server" + end + + -- if we have a status, integer or error message + if ( data:match("^[%-%+%:]") ) then + local response = { data = data } + local t = data:match("^([-+:])") + if ( t == "-" ) then + response.type = Response.Type.ERROR + elseif ( t == "+" ) then + response.type = Response.Type.STATUS + elseif ( t == ":" ) then + response.type = Response.Type.INTEGER + end + + return true, response + end + + -- process bulk reply + if ( data:match("^%$") ) then + -- non existing key + if ( data == "$-1" ) then + return true, nil + end + + local len = tonumber(data:match("^%$(%d*)")) + -- we should only have a single line, so we can just peel of the length + status, data = self.socket:receive_buf(match.numbytes(len), true) + if( not(status) ) then + return false, "Failed to receive data from server" + end + -- move past the terminal CRLF + local status, crlf = self.socket:receive_buf(match.pattern_limit("\r\n", 2048), false) + + return true, { data = data, type = Response.Type.BULK } + end + + -- process multi-bulk reply + if ( data:match("^%*%d*") ) then + local count = data:match("^%*(%d*)") + local results = {} + + for i=1, count do + -- peel of the length + local status = self.socket:receive_buf(match.pattern_limit("\r\n", 2048), false) + if( not(status) ) then + return false, "Failed to receive data from server" + end + + status, data = self.socket:receive_buf(match.pattern_limit("\r\n", 2048), false) + if( not(status) ) then + return false, "Failed to receive data from server" + end + table.insert(results, data) + end + return true, { data = results, type = Response.Type.MULTIBULK } + end + + return false, "Unsupported response" + end, + + + +} + +Helper = { + + new = function(self, host, port) + local o = { host = host, port = port } + setmetatable (o,self) + self.__index = self + return o + end, + + connect = function(self, socket) + self.socket = socket or nmap.new_socket() + return self.socket:connect(self.host, self.port) + end, + + reqCmd = function(self, cmd, ...) + local req = Request:new(cmd, ...) + local status, err = self.socket:send(tostring(req)) + if (not(status)) then + return false, "Failed to send command to server" + end + return Response:new(self.socket):receive() + end, + + close = function(self) + return self.socket:close() + end + +} + +return _ENV; -- cgit v1.2.3