Skip to content
Permalink
master
Switch branches/tags

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?
Go to file
 
 
Cannot retrieve contributors at this time
---
-- 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;