1
0
mirror of https://github.com/nmap/nmap.git synced 2025-12-11 10:19:03 +00:00
The shortport.ssl check can be expensive (6-second timeout on HTTP
services if you don't use -sV), so we want to avoid it if possible. As
discussed at
b2deb019ed (commitcomment-30289632)
this commit restores the SSL check in cases where it might matter (http
and https default ports) and adds a bypass when the URI scheme is
explicitly requested, as in http.get_url and when following redirects.
This commit is contained in:
dmiller
2018-08-26 18:24:43 +00:00
parent 973b471c11
commit 069c76a1de
2 changed files with 54 additions and 6 deletions

View File

@@ -170,15 +170,31 @@ local get_default_port = url.get_default_port
--- Get a value suitable for the Host header field. --- Get a value suitable for the Host header field.
-- See RFC 2616 sections 14.23 and 5.2. -- See RFC 2616 sections 14.23 and 5.2.
local function get_host_field(host, port) local function get_host_field(host, port, scheme)
-- If the global header is set by script-arg, use that.
if host_header then return host_header end if host_header then return host_header end
-- If there's no host, we can't invent a name.
if not host then return nil end if not host then return nil end
local number = (type(port) == "number") and port or port.number local number = (type(port) == "number") and port or port.number
if number == 443 or number == 80 then if scheme then
-- Caller provided scheme. If it's default, return just the hostname.
if number == get_default_port(scheme) then
return stdnse.get_hostname(host) return stdnse.get_hostname(host)
else
return stdnse.get_hostname(host) .. ":" .. number
end end
else
scheme = url.get_default_scheme(port)
if scheme then
-- Caller did not provide scheme, and this port has a default scheme.
local ssl_port = shortport.ssl(host, port)
if (ssl_port and scheme == 'https') or
(not ssl_port and scheme == 'http') then
-- If it's SSL and https, or if it's plaintext and http, return just the hostname.
return stdnse.get_hostname(host)
end
end
end
-- No special cases matched, so include the port number in the host header
return stdnse.get_hostname(host) .. ":" .. number
end end
-- Skip *( SP | HT ) starting at offset. See RFC 2616, section 2.2. -- Skip *( SP | HT ) starting at offset. See RFC 2616, section 2.2.
@@ -366,6 +382,11 @@ local function validate_options(options)
stdnse.debug1("http: options.redirect_ok must be a function or boolean or number") stdnse.debug1("http: options.redirect_ok must be a function or boolean or number")
bad = true bad = true
end end
elseif(key == 'scheme') then
if type(value) ~= 'string' then
stdnse.debug1("http: options.scheme must be a string")
bad = true
end
else else
stdnse.debug1("http: Unknown key in the options table: %s", key) stdnse.debug1("http: Unknown key in the options table: %s", key)
end end
@@ -1086,7 +1107,7 @@ local function build_request(host, port, method, path, options)
local mod_options = { local mod_options = {
header = { header = {
Connection = "close", Connection = "close",
Host = get_host_field(host, port), Host = get_host_field(host, port, options.scheme),
["User-Agent"] = USER_AGENT ["User-Agent"] = USER_AGENT
} }
} }
@@ -1604,6 +1625,7 @@ function get(host, port, path, options)
if(not(validate_options(options))) then if(not(validate_options(options))) then
return http_error("Options failed to validate.") return http_error("Options failed to validate.")
end end
options = options or {}
local redir_check = get_redirect_ok(host, port, options) local redir_check = get_redirect_ok(host, port, options)
local response, state, location local response, state, location
local u = { host = host, port = port, path = path } local u = { host = host, port = port, path = path }
@@ -1617,6 +1639,8 @@ function get(host, port, path, options)
if ( not(u) ) then if ( not(u) ) then
break break
end end
-- Allow redirect to change scheme (e.g. redirect to https)
options.scheme = u.scheme or options.scheme
location = location or {} location = location or {}
table.insert(location, response.header.location) table.insert(location, response.header.location)
until( not(redir_check(u)) ) until( not(redir_check(u)) )
@@ -1640,6 +1664,7 @@ function get_url( u, options )
port.service = parsed.scheme port.service = parsed.scheme
port.number = parsed.port or get_default_port(parsed.scheme) or 80 port.number = parsed.port or get_default_port(parsed.scheme) or 80
options.scheme = options.scheme or parsed.scheme
local path = parsed.path or "/" local path = parsed.path or "/"
if parsed.query then if parsed.query then
@@ -1678,6 +1703,7 @@ function head(host, port, path, options)
if(not(validate_options(options))) then if(not(validate_options(options))) then
return http_error("Options failed to validate.") return http_error("Options failed to validate.")
end end
options = options or {}
local redir_check = get_redirect_ok(host, port, options) local redir_check = get_redirect_ok(host, port, options)
local response, state, location local response, state, location
local u = { host = host, port = port, path = path } local u = { host = host, port = port, path = path }
@@ -1691,6 +1717,8 @@ function head(host, port, path, options)
if ( not(u) ) then if ( not(u) ) then
break break
end end
-- Allow redirect to change scheme (e.g. redirect to https)
options.scheme = u.scheme or options.scheme
location = location or {} location = location or {}
table.insert(location, response.header.location) table.insert(location, response.header.location)
until( not(redir_check(u)) ) until( not(redir_check(u)) )

View File

@@ -415,11 +415,31 @@ local get_default_port_ports = {http=80, https=443}
-- @param scheme for determining the port, such as "http" or "https". -- @param scheme for determining the port, such as "http" or "https".
-- @return A port number as an integer, such as 443 for scheme "https", -- @return A port number as an integer, such as 443 for scheme "https",
-- or nil in case of an undefined scheme -- or nil in case of an undefined scheme
-----------------------------------------------------------------------------
function get_default_port (scheme) function get_default_port (scheme)
return get_default_port_ports[(scheme or ""):lower()] return get_default_port_ports[(scheme or ""):lower()]
end end
local function invert(t)
local out = {}
for k, v in pairs(t) do
out[v] = k
end
return out
end
get_default_scheme_schemes = invert(get_default_port_ports)
---
-- Provides the default URI scheme for a given port.
--
-- @param port A port number as a number or port table
-- @return scheme for addressing the port, such as "http" or "https".
-----------------------------------------------------------------------------
function get_default_scheme (port)
local number = (type(port) == "number") and port or port.number
return get_default_scheme_schemes[port]
end
if not unittest.testing() then if not unittest.testing() then
return _ENV return _ENV
end end