Permalink
Cannot retrieve contributors at this time
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
nmap/nselib/cassandra.lua
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
190 lines (160 sloc)
5.48 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| --- | |
| -- Library methods for handling Cassandra Thrift communication as client | |
| -- | |
| -- @author Vlatko Kosturjak | |
| -- @copyright Same as Nmap--See https://nmap.org/book/man-legal.html | |
| -- | |
| -- Version 0.1 | |
| -- | |
| local stdnse = require "stdnse" | |
| local string = require "string" | |
| _ENV = stdnse.module("cassandra", stdnse.seeall) | |
| --[[ | |
| Cassandra Thrift protocol implementation. | |
| For more information about Cassandra, see: | |
| http://cassandra.apache.org/ | |
| ]]-- | |
| -- Protocol magic strings | |
| CASSANDRAREQ = "\x80\x01\x00\x01" | |
| CASSANDRARESP = "\x80\x01\x00\x02" | |
| CASSLOGINMAGIC = "\x00\x00\x00\x01\x0c\x00\x01\x0d\x00\x01\x0b\x0b\x00\x00\x00\x02" | |
| LOGINSUCC = "\x00\x00\x00\x01\x00" | |
| LOGINFAIL = "\x00\x00\x00\x01\x0b" | |
| LOGINACC = "\x00\x00\x00\x01\x0c" | |
| --Returns string in cassandra format for login | |
| --@param username to put in format | |
| --@param password to put in format | |
| --@return str : string in cassandra format for login | |
| function loginstr (username, password) | |
| return CASSANDRAREQ | |
| .. string.pack(">s4", "login") | |
| .. CASSLOGINMAGIC | |
| .. string.pack(">s4s4s4s4", "username", username, "password", password) | |
| .. "\x00\x00" -- add two null on the end | |
| end | |
| --Invokes command over socket and returns the response | |
| --@param socket to connect to | |
| --@param command to invoke | |
| --@param cnt is protocol count | |
| --@return status : true if ok; false if bad | |
| --@return result : value if status ok, error msg if bad | |
| function cmdstr (command,cnt) | |
| return CASSANDRAREQ | |
| .. string.pack(">s4I4", command, cnt) | |
| .. "\x00" -- add null on the end | |
| end | |
| --Invokes command over socket and returns the response | |
| --@param socket to connect to | |
| --@param command to invoke | |
| --@param cnt is protocol count | |
| --@return status : true if ok; false if bad | |
| --@return result : value if status ok, error msg if bad | |
| function sendcmd (socket, command, cnt) | |
| local cmdstr = cmdstr (command,cnt) | |
| local response | |
| local status, err = socket:send(string.pack(">I4", #cmdstr)) | |
| if ( not(status) ) then | |
| return false, "error sending packet length" | |
| end | |
| status, err = socket:send(cmdstr) | |
| if ( not(status) ) then | |
| return false, "error sending packet payload" | |
| end | |
| status, response = socket:receive_bytes(4) | |
| if ( not(status) ) then | |
| return false, "error receiving length" | |
| end | |
| local size = string.unpack(">I4", response) | |
| if #response < size + 4 then | |
| local resp2 | |
| status, resp2 = socket:receive_bytes(size + 4 - #response) | |
| if ( not(status) ) then | |
| return false, "error receiving payload" | |
| end | |
| response = response .. resp2 | |
| end | |
| -- magic response starts at 5th byte for 4 bytes, 4 byte for length + length of string command | |
| if response:sub(5, 8 + 4 + #command) ~= CASSANDRARESP .. string.pack(">s4", command) then | |
| return false, "protocol response error" | |
| end | |
| return true, response | |
| end | |
| --Return Cluster Name | |
| --@param socket to connect to | |
| --@param cnt is protocol count | |
| --@return status : true if ok; false if bad | |
| --@return result : value if status ok, error msg if bad | |
| function describe_cluster_name (socket,cnt) | |
| local cname = "describe_cluster_name" | |
| local status,resp = sendcmd(socket,cname,cnt) | |
| if (not(status)) then | |
| stdnse.debug1("sendcmd"..resp) | |
| return false, "error in communication" | |
| end | |
| -- grab the size | |
| -- pktlen(4) + CASSANDRARESP(4) + lencmd(4) + lencmd(v) + params(7) + next byte position | |
| local position = 12 + #cname + 7 + 1 | |
| local value = string.unpack(">s4", resp, position) | |
| return true, value | |
| end | |
| --Return API version | |
| --@param socket to connect to | |
| --@param cnt is protocol count | |
| --@return status : true if ok; false if bad | |
| --@return result : value if status ok, error msg if bad | |
| function describe_version (socket,cnt) | |
| local cname = "describe_version" | |
| local status,resp = sendcmd(socket,cname,cnt) | |
| if (not(status)) then | |
| stdnse.debug1("sendcmd"..resp) | |
| return false, "error in communication" | |
| end | |
| -- grab the size | |
| -- pktlen(4) + CASSANDRARESP(4) + lencmd(4) + lencmd(v) + params(7) + next byte position | |
| local position = 12 + #cname + 7 + 1 | |
| local value = string.unpack(">s4", resp, position) | |
| return true, value | |
| end | |
| --Login to Cassandra | |
| --@param socket to connect to | |
| --@param username to connect to | |
| --@param password to connect to | |
| --@return status : true if ok; false if bad | |
| --@return result : table of status ok, error msg if bad | |
| --@return if status ok : remaining data read from socket but not used | |
| function login (socket,username,password) | |
| local loginstr = loginstr (username, password) | |
| local combo = username..":"..password | |
| local status, err = socket:send(string.pack(">I4", #loginstr)) | |
| if ( not(status) ) then | |
| stdnse.debug3("cannot send len "..combo) | |
| return false, "Failed to connect to server" | |
| end | |
| status, err = socket:send(loginstr) | |
| if ( not(status) ) then | |
| stdnse.debug3("Sent packet for "..combo) | |
| return false, err | |
| end | |
| local response | |
| status, response = socket:receive_bytes(22) | |
| if ( not(status) ) then | |
| stdnse.debug3("Receive packet for "..combo) | |
| return false, err | |
| end | |
| local size = string.unpack(">I4", response) | |
| local loginresp = string.sub(response,5,17) | |
| if (loginresp ~= CASSANDRARESP .. string.pack(">s4", "login")) then | |
| return false, "protocol error" | |
| end | |
| local magic = string.sub(response,18,22) | |
| stdnse.debug3("packet for "..combo) | |
| stdnse.debug3("packet hex: %s", stdnse.tohex(response) ) | |
| stdnse.debug3("size packet hex: %s", stdnse.tohex(size) ) | |
| stdnse.debug3("magic packet hex: %s", stdnse.tohex(magic) ) | |
| if (magic == LOGINSUCC) then | |
| return true | |
| else | |
| return false, "Login failed." | |
| end | |
| end | |
| return _ENV; |