summaryrefslogtreecommitdiffstats
path: root/scripts/voldemort-info.nse
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/voldemort-info.nse')
-rw-r--r--scripts/voldemort-info.nse173
1 files changed, 173 insertions, 0 deletions
diff --git a/scripts/voldemort-info.nse b/scripts/voldemort-info.nse
new file mode 100644
index 0000000..a373ec1
--- /dev/null
+++ b/scripts/voldemort-info.nse
@@ -0,0 +1,173 @@
+local nmap = require "nmap"
+local shortport = require "shortport"
+local stdnse = require "stdnse"
+local string = require "string"
+local table = require "table"
+
+description = [[
+Retrieves cluster and store information from the Voldemort distributed key-value store using the Voldemort Native Protocol.
+]]
+
+---
+-- @usage
+-- nmap -p 6666 --script voldemort-info <ip>
+--
+-- @output
+-- PORT STATE SERVICE
+-- 6666/tcp open irc
+-- | voldemort-info:
+-- | Cluster
+-- | Name: mycluster
+-- | Id: 0
+-- | Host: localhost
+-- | HTTP Port: 8081
+-- | TCP Port: 6666
+-- | Admin Port: 6667
+-- | Partitions: 0, 1
+-- | Stores
+-- | test
+-- | Persistence: bdb
+-- | Description: Test store
+-- | Owners: harry@hogwarts.edu, hermoine@hogwarts.edu
+-- | Routing strategy: consistent-routing
+-- | Routing: client
+-- | wordcounts
+-- | Persistence: read-only
+-- | Routing strategy: consistent-routing
+-- |_ Routing: client
+--
+
+author = "Patrik Karlsson"
+license = "Same as Nmap--See https://nmap.org/book/man-legal.html"
+categories = {"discovery", "safe"}
+
+
+portrule = shortport.port_or_service(6666, "vp3", "tcp")
+
+local function fail(err) return stdnse.format_output(false, err) end
+
+-- Connect to the server and make sure it supports the vp3 protocol
+-- @param host table as received by the action method
+-- @param port table as received by the action method
+-- @return status true on success, false on failure
+-- @return socket connected to the server
+local function connect(host, port)
+ local socket = nmap.new_socket()
+ socket:set_timeout(5000)
+
+ local status, err = socket:connect(host, port)
+ if ( not(status) ) then
+ return false, "Failed to connect to server"
+ end
+
+ status, err = socket:send("vp3")
+ if ( not(status) ) then
+ return false, "Failed to send request to server"
+ end
+
+ local response
+ status, response = socket:receive_bytes(2)
+ if ( not(status) ) then
+ return false, "Failed to receive response from server"
+ elseif( response ~= "ok" ) then
+ return false, "Unsupported protocol"
+ end
+ return true, socket
+end
+
+-- Get Voldemort metadata
+-- @param socket connected to the server
+-- @param file the xml file to retrieve
+-- @return status true on success false on failure
+-- @return data string as received from the server
+local function getMetadata(socket, file)
+
+ local req = "\x01\x00" .. string.pack(">s1x I4 s1x", "metadata", 0, file)
+ local status, err = socket:send(req)
+ if ( not(status) ) then
+ return false, "Failed to send request to server"
+ end
+ local status, data = socket:receive_bytes(10)
+ if ( not(status) ) then
+ return false, "Failed to receive response from server"
+ end
+ local len = string.unpack(">I2", data, 9)
+ while( #data < len - 2 ) do
+ local status, tmp = socket:receive_bytes(len - 2 - #data)
+ if ( not(status) ) then
+ return false, "Failed to receive response from server"
+ end
+ data = data .. tmp
+ end
+ return true, data
+end
+
+
+action = function(host, port)
+
+ -- table of variables to query the server
+ local vars = {
+ ["cluster"] = {
+ { key = "Name", match = "<cluster>.-<name>(.-)</name>" },
+ { key = "Id", match = "<cluster>.-<server>.-<id>(%d-)</id>.-</server>" },
+ { key = "Host", match = "<cluster>.-<server>.-<host>(%w-)</host>.-</server>" },
+ { key = "HTTP Port", match = "<cluster>.-<server>.-<http%-port>(%d-)</http%-port>.-</server>" },
+ { key = "TCP Port", match = "<cluster>.-<server>.-<socket%-port>(%d-)</socket%-port>.-</server>" },
+ { key = "Admin Port", match = "<cluster>.-<server>.-<admin%-port>(%d-)</admin%-port>.-</server>" },
+ { key = "Partitions", match = "<cluster>.-<server>.-<partitions>([%d%s,]*)</partitions>.-</server>" },
+ },
+ ["store"] = {
+ { key = "Persistence", match = "<store>.-<persistence>(.-)</persistence>" },
+ { key = "Description", match = "<store>.-<description>(.-)</description>" },
+ { key = "Owners", match = "<store>.-<owners>(.-)</owners>" },
+ { key = "Routing strategy", match = "<store>.-<routing%-strategy>(.-)</routing%-strategy>" },
+ { key = "Routing", match = "<store>.-<routing>(.-)</routing>" },
+ },
+ }
+
+ -- connect to the server
+ local status, socket = connect(host, port)
+ if ( not(status) ) then
+ return fail(socket)
+ end
+
+ -- get the cluster meta data
+ local status, response = getMetadata(socket, "cluster.xml")
+ if ( not(status) or not(response:match("<cluster>.*</cluster>")) ) then
+ return
+ end
+
+ -- Get the cluster details
+ local cluster_tbl = { name = "Cluster" }
+ for _, item in ipairs(vars["cluster"]) do
+ local val = response:match(item.match)
+ if ( val ) then
+ table.insert(cluster_tbl, ("%s: %s"):format(item.key, val))
+ end
+ end
+
+ -- get the stores meta data
+ local status, response = getMetadata(socket, "stores.xml")
+ if ( not(status) or not(response:match("<stores>.-</stores>")) ) then
+ return
+ end
+
+ local result, stores = {}, { name = "Stores" }
+ table.insert(result, cluster_tbl)
+
+ -- iterate over store items
+ for store in response:gmatch("<store>.-</store>") do
+ local name = store:match("<store>.-<name>(.-)</name>")
+ local store_tbl = { name = name or "unknown" }
+
+ for _, item in ipairs(vars["store"]) do
+ local val = store:match(item.match)
+ if ( val ) then
+ table.insert(store_tbl, ("%s: %s"):format(item.key, val))
+ end
+ end
+ table.insert(stores, store_tbl)
+ end
+ table.insert(result, stores)
+ return stdnse.format_output(true, result)
+end