mirror of
https://github.com/nmap/nmap.git
synced 2025-12-06 04:31:29 +00:00
New oops.lua library
Loosely inspired by Rustlang's std::Result type: https://doc.rust-lang.org/beta/std/result/index.html This ought to be easy to use to replace uses of `stdnse.format_output(false, ...)`
This commit is contained in:
@@ -1,5 +1,8 @@
|
|||||||
#Nmap Changelog ($Id$); -*-text-*-
|
#Nmap Changelog ($Id$); -*-text-*-
|
||||||
|
|
||||||
|
o [NSE] New library, oops.lua, makes reporting errors easy, with plenty of
|
||||||
|
debugging detail when needed, and no clutter when not. [Daniel Miller]
|
||||||
|
|
||||||
o [NSE][GH#1126] New script vulners.nse queries the Vulners CVE database API
|
o [NSE][GH#1126] New script vulners.nse queries the Vulners CVE database API
|
||||||
using CPE information from Nmap's service and application version detection.
|
using CPE information from Nmap's service and application version detection.
|
||||||
[GMedian, Daniel Miller]
|
[GMedian, Daniel Miller]
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
local nmap = require "nmap"
|
local nmap = require "nmap"
|
||||||
local shortport
|
local shortport
|
||||||
local stdnse = require "stdnse"
|
local stdnse = require "stdnse"
|
||||||
|
local oops = require "oops"
|
||||||
_ENV = stdnse.module("comm", stdnse.seeall)
|
_ENV = stdnse.module("comm", stdnse.seeall)
|
||||||
|
|
||||||
-- This timeout value (in ms) is added to the connect timeout and represents
|
-- This timeout value (in ms) is added to the connect timeout and represents
|
||||||
@@ -76,7 +77,7 @@ local setup_connect = function(host, port, opts)
|
|||||||
|
|
||||||
if not status then
|
if not status then
|
||||||
sock:close()
|
sock:close()
|
||||||
return status, err
|
return oops.raise("Could not connect", status, err)
|
||||||
end
|
end
|
||||||
|
|
||||||
sock:set_timeout(request_timeout)
|
sock:set_timeout(request_timeout)
|
||||||
@@ -85,20 +86,15 @@ local setup_connect = function(host, port, opts)
|
|||||||
end
|
end
|
||||||
|
|
||||||
local read = function(sock, opts)
|
local read = function(sock, opts)
|
||||||
local response, status
|
|
||||||
|
|
||||||
if opts.lines then
|
if opts.lines then
|
||||||
status, response = sock:receive_lines(opts.lines)
|
return oops.raise("receive_lines failed", sock:receive_lines(opts.lines))
|
||||||
return status, response
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if opts.bytes then
|
if opts.bytes then
|
||||||
status, response = sock:receive_bytes(opts.bytes)
|
return oops.raise("receive_bytes failed", sock:receive_bytes(opts.bytes))
|
||||||
return status, response
|
|
||||||
end
|
end
|
||||||
|
|
||||||
status, response = sock:receive()
|
return oops.raise("receive failed", sock:receive())
|
||||||
return status, response
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--- This function simply connects to the specified port number on the
|
--- This function simply connects to the specified port number on the
|
||||||
@@ -115,12 +111,12 @@ end
|
|||||||
get_banner = function(host, port, opts)
|
get_banner = function(host, port, opts)
|
||||||
opts = opts or {}
|
opts = opts or {}
|
||||||
opts.recv_before = true
|
opts.recv_before = true
|
||||||
local socket, nothing, correct, banner = tryssl(host, port, "", opts)
|
local socket, errmsg, correct, banner = oops.raise("tryssl failed", tryssl(host, port, nil, opts))
|
||||||
if socket then
|
if socket then
|
||||||
socket:close()
|
socket:close()
|
||||||
return true, banner
|
return true, banner
|
||||||
end
|
end
|
||||||
return false, banner
|
return false, errmsg
|
||||||
end
|
end
|
||||||
|
|
||||||
--- This function connects to the specified port number on the specified
|
--- This function connects to the specified port number on the specified
|
||||||
@@ -143,21 +139,21 @@ exchange = function(host, port, data, opts)
|
|||||||
|
|
||||||
if not status then
|
if not status then
|
||||||
-- sock is an error message in this case
|
-- sock is an error message in this case
|
||||||
return status, sock
|
return oops.raise("Failed to connect", status, sock)
|
||||||
end
|
end
|
||||||
|
|
||||||
status, ret = sock:send(data)
|
status, ret = sock:send(data)
|
||||||
|
|
||||||
if not status then
|
if not status then
|
||||||
sock:close()
|
sock:close()
|
||||||
return status, ret
|
return oops.raise("Failed to send", status, ret)
|
||||||
end
|
end
|
||||||
|
|
||||||
status, ret = read(sock, opts)
|
status, ret = read(sock, opts)
|
||||||
|
|
||||||
sock:close()
|
sock:close()
|
||||||
|
|
||||||
return status, ret
|
return oops.raise("Faield to read", status, ret)
|
||||||
end
|
end
|
||||||
|
|
||||||
--- This function uses shortport.ssl to check if the port is a likely SSL port
|
--- This function uses shortport.ssl to check if the port is a likely SSL port
|
||||||
@@ -220,22 +216,21 @@ function opencon(host, port, data, opts)
|
|||||||
opts = opts or {}
|
opts = opts or {}
|
||||||
local status, sd = setup_connect(host, port, opts)
|
local status, sd = setup_connect(host, port, opts)
|
||||||
if not status then
|
if not status then
|
||||||
return nil, sd, nil
|
return oops.raise("Failed to connect", false, sd)
|
||||||
end
|
end
|
||||||
|
|
||||||
local response, early_resp;
|
local response, early_resp
|
||||||
if opts.recv_before then status, early_resp = read(sd, opts) end
|
if opts.recv_before then status, early_resp = oops.raise("read failed", read(sd, opts)) end
|
||||||
if data and #data > 0 then
|
if data and #data > 0 then
|
||||||
sd:send(data)
|
sd:send(data)
|
||||||
status, response = sd:receive()
|
status, response = oops.raise("receive failed", sd:receive())
|
||||||
else
|
else
|
||||||
response = early_resp
|
response = early_resp
|
||||||
end
|
end
|
||||||
if not status then
|
if not status then
|
||||||
sd:close()
|
sd:close()
|
||||||
return nil, response, early_resp
|
|
||||||
end
|
end
|
||||||
return sd, response, early_resp
|
return status and sd, response, early_resp
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Opens a SSL connection if possible, with fallback to plain text.
|
--- Opens a SSL connection if possible, with fallback to plain text.
|
||||||
@@ -275,11 +270,11 @@ function tryssl(host, port, data, opts)
|
|||||||
stdnse.debug2("DTLS (SSL over UDP) is not supported")
|
stdnse.debug2("DTLS (SSL over UDP) is not supported")
|
||||||
end
|
end
|
||||||
opts.proto = opt1
|
opts.proto = opt1
|
||||||
local sd, response, early_resp = opencon(host, port, data, opts)
|
local sd, response, early_resp = oops.raise(("%s failed"):format(opt1), opencon(host, port, data, opts))
|
||||||
-- Try the second option (If udp, then both options are the same; skip it)
|
-- Try the second option (If udp, then both options are the same; skip it)
|
||||||
if not sd and opt1 ~= "udp" then
|
if not sd and opt1 ~= "udp" then
|
||||||
opts.proto = opt2
|
opts.proto = opt2
|
||||||
sd, response, early_resp = opencon(host, port, data, opts)
|
sd, response, early_resp = oops.raise(("%s failed"):format(opt2), opencon(host, port, data, opts))
|
||||||
best = opt2
|
best = opt2
|
||||||
end
|
end
|
||||||
if not sd then best = "none" end
|
if not sd then best = "none" end
|
||||||
|
|||||||
129
nselib/oops.lua
Normal file
129
nselib/oops.lua
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
--- Useful error stack objects
|
||||||
|
--
|
||||||
|
-- Many NSE library functions return a boolean status and an optional error
|
||||||
|
-- message. The Oops library consists of several simple functions to accumulate
|
||||||
|
-- these errors and pass them up the stack, resulting in a useful and verbose
|
||||||
|
-- error message when debugging.
|
||||||
|
--
|
||||||
|
-- @author Daniel Miller
|
||||||
|
-- @copyright Same as Nmap--See https://nmap.org/book/man-legal.html
|
||||||
|
-- @class module
|
||||||
|
-- @name oops
|
||||||
|
|
||||||
|
local require = require
|
||||||
|
local setmetatable = setmetatable
|
||||||
|
local _ENV = require "strict" {}
|
||||||
|
|
||||||
|
local nmap = require "nmap"
|
||||||
|
local debugging = nmap.debugging
|
||||||
|
local verbosity = nmap.verbosity
|
||||||
|
|
||||||
|
local table = require "table"
|
||||||
|
local concat = table.concat
|
||||||
|
local insert = table.insert
|
||||||
|
|
||||||
|
local Oops = {
|
||||||
|
new = function (self, message)
|
||||||
|
local o = {message}
|
||||||
|
setmetatable(o, self)
|
||||||
|
self.__index = self
|
||||||
|
return o
|
||||||
|
end,
|
||||||
|
|
||||||
|
push = function (self, message)
|
||||||
|
insert(self, 1, message)
|
||||||
|
end,
|
||||||
|
|
||||||
|
__tostring = function (self)
|
||||||
|
local banner = "The script encountered an error"
|
||||||
|
local sep = ":\n- "
|
||||||
|
if debugging() > 0 then
|
||||||
|
-- Print full error trace
|
||||||
|
return banner .. sep .. concat(self, sep)
|
||||||
|
end
|
||||||
|
if verbosity() > 0 then
|
||||||
|
-- Show just the top error
|
||||||
|
return banner .. ": " .. self[1]
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
}
|
||||||
|
|
||||||
|
--- Add an error message to a stack of errors
|
||||||
|
--
|
||||||
|
-- @param message The error message to add to the stack.
|
||||||
|
-- @param previous (Optional) Any error reported by other functions that failed.
|
||||||
|
-- @return An Oops object representing the error stack.
|
||||||
|
err = function (message, previous)
|
||||||
|
local result
|
||||||
|
if previous then
|
||||||
|
if previous.push then
|
||||||
|
result = previous
|
||||||
|
else
|
||||||
|
result = Oops:new(previous)
|
||||||
|
end
|
||||||
|
result:push(message)
|
||||||
|
elseif message.push then
|
||||||
|
result = message
|
||||||
|
else
|
||||||
|
result = Oops:new(message)
|
||||||
|
end
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
local err = err
|
||||||
|
|
||||||
|
--- Report an error or return a good value
|
||||||
|
--
|
||||||
|
-- If the status is true, just return the message. If it's false, return the
|
||||||
|
-- message as an Oops object. This can be easily used as the final return value
|
||||||
|
-- of a script.
|
||||||
|
-- @param status The return status of the script.
|
||||||
|
-- @param message The output of the script, or an error message if status is false.
|
||||||
|
-- @return The message if status is true, or an error message if it is false.
|
||||||
|
output = function (status, message)
|
||||||
|
if status then
|
||||||
|
return message
|
||||||
|
else
|
||||||
|
return err(message)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local output = output
|
||||||
|
|
||||||
|
--- Report a status and error or return values
|
||||||
|
--
|
||||||
|
-- This is intended to wrap a function that returns a status and either an
|
||||||
|
-- error or some value. If the status is false, the message is added to the
|
||||||
|
-- stack of errors. Instead of this code:
|
||||||
|
--
|
||||||
|
-- <code>
|
||||||
|
-- local status, value_or_error, value = somefunction(args)
|
||||||
|
-- if not status then
|
||||||
|
-- return status, "somefunction failed for some reason"
|
||||||
|
-- end
|
||||||
|
-- </code>
|
||||||
|
--
|
||||||
|
-- with this instead:
|
||||||
|
--
|
||||||
|
-- <code>
|
||||||
|
-- local status, value_or_error, value = oops.raise("somefunction failed", somefunction(args))
|
||||||
|
-- if not status then
|
||||||
|
-- return status, value_or_error
|
||||||
|
-- end
|
||||||
|
-- </code>
|
||||||
|
--
|
||||||
|
-- but instead of just the one error, you get a stack of errors from
|
||||||
|
-- <code>somefunction</code> with your own message at the top.
|
||||||
|
--
|
||||||
|
-- @param message The error message to report if status is false.
|
||||||
|
-- @param status The first return value of the function. Treated as boolean, but returned unmodified.
|
||||||
|
-- @param previous The second return value of the function, or error.
|
||||||
|
-- @return The same status that was input.
|
||||||
|
-- @return The rest of the return values, but on error, the message will be added to the stack.
|
||||||
|
raise = function (message, status, previous, ...)
|
||||||
|
local r = previous
|
||||||
|
if not status then
|
||||||
|
r = err(message, previous)
|
||||||
|
end
|
||||||
|
return status, r, ...
|
||||||
|
end
|
||||||
|
|
||||||
|
return _ENV
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
local comm = require "comm"
|
local comm = require "comm"
|
||||||
local shortport = require "shortport"
|
local shortport = require "shortport"
|
||||||
|
local oops = require "oops"
|
||||||
|
|
||||||
description = [[
|
description = [[
|
||||||
Retrieves the day and time from the Daytime service.
|
Retrieves the day and time from the Daytime service.
|
||||||
@@ -21,9 +22,5 @@ categories = {"discovery", "safe"}
|
|||||||
portrule = shortport.port_or_service(13, "daytime", {"tcp", "udp"})
|
portrule = shortport.port_or_service(13, "daytime", {"tcp", "udp"})
|
||||||
|
|
||||||
action = function(host, port)
|
action = function(host, port)
|
||||||
local status, result = comm.exchange(host, port, "dummy", {lines=1})
|
return oops.output(comm.exchange(host, port, "dummy", {lines=1}))
|
||||||
|
|
||||||
if status then
|
|
||||||
return result
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user