mirror of
https://github.com/nmap/nmap.git
synced 2025-12-28 18:39:03 +00:00
line, since this can differ from the reverse DNS results. It can be particularly important when doing HTTP tests against virtual hosts. The data can be accessed from target->HostName() from Nmap proper and host.targetname from NSE scripts. The NSE HTTP library now uses this for the Host header. Thanks to Sven Klemm for adding this useful feature.
150 lines
3.7 KiB
Lua
150 lines
3.7 KiB
Lua
-- See nmaps COPYING for licence
|
|
module(...,package.seeall)
|
|
|
|
require 'stdnse'
|
|
require 'url'
|
|
|
|
--
|
|
-- http.get( host, port, path, options )
|
|
-- http.request( host, port, request, options )
|
|
-- http.get_url( url, options )
|
|
--
|
|
-- host may either be a string or table
|
|
-- port may either be a number or a table
|
|
--
|
|
-- the format of the return value is a table with the following structure:
|
|
-- {status = 200, header = {}, body ="<html>...</html>"}
|
|
-- the header table has an entry for each received header with the header name being the key
|
|
-- the table also has an entry named "status" which contains the http status code of the request
|
|
-- in case of an error status is nil
|
|
|
|
|
|
-- fetch relative URL with get request
|
|
get = function( host, port, path, options )
|
|
options = options or {}
|
|
local presets = {Host=host,Connection="close",['User-Agent']="Nmap NSE"}
|
|
if type(host) == 'table' then
|
|
presets['Host'] = ( host.name ~= '' and host.name ) or host.ip
|
|
end
|
|
|
|
local header = options.header or {}
|
|
for key,value in pairs(presets) do
|
|
header[key] = header[key] or value
|
|
end
|
|
|
|
local data = "GET "..path.." HTTP/1.1\r\n"
|
|
for key,value in pairs(header) do
|
|
data = data .. key .. ": " .. value .. "\r\n"
|
|
end
|
|
data = data .. "\r\n"
|
|
|
|
return request( host, port, data, options )
|
|
end
|
|
|
|
-- fetch URL with get request
|
|
get_url = function( u, options )
|
|
local parsed = url.parse( u )
|
|
local port = {}
|
|
|
|
port.service = parsed.scheme
|
|
port.number = parsed.port
|
|
|
|
if not port.number then
|
|
if parsed.scheme == 'https' then
|
|
port.number = 443
|
|
else
|
|
port.number = 80
|
|
end
|
|
end
|
|
|
|
local path = parsed.path or "/"
|
|
if parsed.query then
|
|
path = path .. "?" .. parsed.query
|
|
end
|
|
|
|
return get( parsed.host, port, path, options )
|
|
end
|
|
|
|
-- send http request and return the result as table
|
|
-- host may be a table or the hostname
|
|
-- port may be a table or the portnumber
|
|
request = function( host, port, data, options )
|
|
options = options or {}
|
|
|
|
if type(host) == 'table' then
|
|
host = host.targetname or ( host.name ~= '' and host.name ) or host.ip
|
|
end
|
|
|
|
local protocol = 'tcp'
|
|
if type(port) == 'table' then
|
|
if port.service == 'https' or ( port.version and port.version.service_tunnel == 'ssl' ) then
|
|
protocol = 'ssl'
|
|
end
|
|
port = port.number
|
|
end
|
|
|
|
local result = {status=nil,header={},body=""}
|
|
local socket = nmap.new_socket()
|
|
if options.timeout then
|
|
socket:set_timeout( options.timeout )
|
|
end
|
|
if not socket:connect( host, port, protocol ) then
|
|
return result
|
|
end
|
|
if not socket:send( data ) then
|
|
return result
|
|
end
|
|
|
|
local buffer = stdnse.make_buffer( socket, "\r?\n" )
|
|
|
|
local status, line, _
|
|
local header, body = {}, {}
|
|
|
|
-- header loop
|
|
while true do
|
|
status, line = buffer()
|
|
if (not status or line == "") then break end
|
|
table.insert(header,line)
|
|
end
|
|
|
|
-- build nicer table for header
|
|
local last_header, match
|
|
for number, line in pairs( header ) do
|
|
if number == 1 then
|
|
local code
|
|
_, _, code = string.find( line, "HTTP/%d\.%d (%d+)")
|
|
result.status = tonumber(code)
|
|
else
|
|
match, _, key, value = string.find( line, "(.+): (.*)" )
|
|
if match and key and value then
|
|
key = key:lower()
|
|
if result.header[key] then
|
|
result.header[key] = result.header[key] .. ',' .. value
|
|
else
|
|
result.header[key] = value
|
|
end
|
|
last_header = key
|
|
else
|
|
match, _, value = string.find( line, " +(.*)" )
|
|
if match and value then
|
|
result.header[last_header] = result.header[last_header] .. ',' .. value
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
-- body loop
|
|
while true do
|
|
status, line = buffer()
|
|
if (not status) then break end
|
|
table.insert(body,line)
|
|
end
|
|
|
|
socket:close()
|
|
result.body = table.concat( body, "\n" )
|
|
|
|
return result
|
|
|
|
end
|
|
|