diff --git a/CHANGELOG b/CHANGELOG
index 196dbdcd6..fd3a46dd2 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,5 +1,10 @@
# Nmap Changelog ($Id$); -*-text-*-
+o [NSE] Added default limits on the number of ports that qscan will
+ scan. By default, it will do upt o 8 open ports and up to 1 closed
+ port. These limits can be controlled with the qscan.numopen and
+ qscan.numclosed script arguments. [David]
+
o [NSE] Made sslv2.nse give special output when SSLv2 is supported,
but no SSLv2 ciphers are offered. This happened with a specific
Sendmail configuration. [Matt Selsky]
diff --git a/scripts/qscan.nse b/scripts/qscan.nse
index a248df9ff..a26585ade 100644
--- a/scripts/qscan.nse
+++ b/scripts/qscan.nse
@@ -26,6 +26,8 @@ description = [[
-- @args confidence Confidence level: 0.75, 0.9, 0.95, 0.975, 0.99, 0.995, or 0.9995.
-- @args delay Average delay between packet sends. This is a number followed by ms for milliseconds or s for seconds. (m and h are also supported but are too long for timeouts.) The actual delay will randomly vary between 50% and 150% of the time specified. Default: 200ms.
-- @args numtrips Number of round-trip times to try to get.
+-- @args numopen Maximum number of open ports to probe (default 8). A negative number disables the limit.
+-- @args numclosed Maximum number of closed ports to probe (default 1). A negative number disables the limit.
--
-- @output
-- | qscan:
@@ -56,6 +58,8 @@ require 'tab'
local DELAY = 0.200
local NUMTRIPS = 10
local CONF = 0.95
+local NUMOPEN = 8
+local NUMCLOSED = 1
-- The following tdist{} and tinv() are based off of
-- http://www.owlnet.rice.edu/~elec428/projects/tinv.c
@@ -307,23 +311,44 @@ local getopts = function()
end
end
---- Get ports to probe
--- @param host Host object
-local getports = function(host)
- local states = { "closed", "open" }
- local ports = {}
- local port = nil
+local table_extend = function(a, b)
+ local t = {}
- for _, s in ipairs(states) do
- repeat
- port = nmap.get_ports(host, port, "tcp", s)
- if port then
- table.insert(ports, port.number)
- end
- until not port
+ for _, v in ipairs(a) do
+ t[#t + 1] = v
+ end
+ for _, v in ipairs(b) do
+ t[#t + 1] = v
end
- return ports
+ return t
+end
+
+--- Get ports to probe
+-- @param host Host object
+local getports = function(host, numopen, numclosed)
+ local open = {}
+ local closed = {}
+ local port
+
+ port = nil
+ while numopen < 0 or #open < numopen do
+ port = nmap.get_ports(host, port, "tcp", "open")
+ if not port then
+ break
+ end
+ open[#open + 1] = port.number
+ end
+ port = nil
+ while numclosed < 0 or #closed < numclosed do
+ port = nmap.get_ports(host, port, "tcp", "closed")
+ if not port then
+ break
+ end
+ closed[#closed + 1] = port.number
+ end
+
+ return table_extend(open, closed)
end
--- Sets probe port list in registry
@@ -337,6 +362,8 @@ local setreg = function(host, ports)
end
hostrule = function(host)
+ local numopen, numclosed = NUMOPEN, NUMCLOSED
+
if not nmap.is_privileged() then
if not nmap.registry['qscan'] then
nmap.registry['qscan'] = {}
@@ -353,7 +380,22 @@ hostrule = function(host)
if not host.interface then
return false
end
- local ports = getports(host)
+
+ for _, k in ipairs({"qscan.numopen", "numopen"}) do
+ if nmap.registry.args[k] then
+ numopen = tonumber(nmap.registry.args[k])
+ break
+ end
+ end
+
+ for _, k in ipairs({"qscan.numclosed", "numclosed"}) do
+ if nmap.registry.args[k] then
+ numclosed = tonumber(nmap.registry.args[k])
+ break
+ end
+ end
+
+ local ports = getports(host, numopen, numclosed)
if #ports <= 1 then
return false
end