1
0
mirror of https://github.com/nmap/nmap.git synced 2025-12-10 17:59:04 +00:00
Files
nmap/nselib/http.lua
batrick 670792a4a6 Updated all libraries documentation.
packet and url both need more work.
2008-08-18 04:03:45 +00:00

216 lines
6.9 KiB
Lua

--- The http module provides functions for dealing with the client side
-- of the http protocol. The functions reside inside the http namespace.
-- The return value of each function in this module is a table with the
-- following keys: status, header and body. status is a number representing
-- the HTTP status code returned in response to the HTTP request. In case
-- of an unhandled error, status is nil. The header value is a table
-- containing key-value pairs of HTTP headers received in response to the
-- request. The header names are in lower-case and are the keys to their
-- corresponding header values (e.g. header.location = "http://nmap.org/").
-- Multiple headers of the same name are concatenated and separated by
-- commas. The body value is a string containing the body of the HTTP
-- response.
-- @copyright See nmaps COPYING for licence
module(... or "http",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
--- Fetches a resource with a GET request. The first argument is either a
-- string with the hostname or a table like the host table passed by nmap.
-- The second argument is either the port number or a table like the port
-- table passed by nmap. The third argument is the path of the resource.
-- The fourth argument is a table for further options. The table may have
-- 2 keys: timeout and header. timeout is the timeout used for the socket
-- operations. header is a table with additional headers to be used for
-- the request. The function builds the request and calls http.request.
-- @param host The host to query.
-- @param port The port for the host.
-- @param path The path of the resource.
-- @param options A table of optoins. See function description.
-- @return table
get = function( host, port, path, options )
options = options or {}
local presets = {Host=host,Connection="close",['User-Agent']="Mozilla/5.0 (compatible; Nmap Scripting Engine; http://nmap.org/book/nse.html)"}
if type(host) == 'table' then
presets['Host'] = host.targetname or ( 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
--- Parses url and calls http.get with the result. The second argument
-- is a table for further options. The table may have 2 keys: timeout
-- and header. timeout is the timeout used for the socket operations.
-- header is a table with additional headers to be used for the request.
-- @param url The url of the host.
-- @param options Options passed to http.get.
-- @see http.get
get_url = function(url, options )
local parsed = url.parse( url )
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
--- Sends request to host:port and parses the answer. The first argument
-- is either a string with the hostname or a table like the host table
-- passed by nmap. The second argument is either the port number or a
-- table like the port table passed by nmap. SSL is used for the request
-- if either port.service equals https or port.version.service_tunnel
-- equals ssl. The third argument is the request. The fourth argument is
-- a table for further options. You can specify a timeout for the socket
-- operations with the timeout key.
-- @param host The host to query.
-- @param port The port on the host.
-- @param data Data to send initially to the host.
-- @param options Table of options.
-- @see http.get
request = function( host, port, data, options )
options = options or {}
if type(host) == 'table' then
host = 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()
local default_timeout = {}
if options.timeout then
socket:set_timeout( options.timeout )
else
default_timeout = get_default_timeout( nmap.timing_level() )
socket:set_timeout( default_timeout.connect )
end
if not socket:connect( host, port, protocol ) then
return result
end
if not options.timeout then
socket:set_timeout( default_timeout.request )
end
if not socket:send( data ) then
return result
end
local buffer = stdnse.make_buffer( socket, "\r?\n" )
local line, _
local header, body = {}, {}
-- header loop
while true do
line = buffer()
if (not line or line == "") then break end
table.insert(header,line)
end
-- build nicer table for header
local last_header, match
for number, line in ipairs( header ) do
if number == 1 then
local code
_, _, code = string.find( line, "HTTP/%d\.%d (%d+)")
result.status = tonumber(code)
if not result.status then table.insert(body,line) end
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 and last_header then
result.header[last_header] = result.header[last_header] .. ',' .. value
elseif match and value then
table.insert(body,line)
end
end
end
end
-- body loop
while true do
line = buffer()
if not line then break end
table.insert(body,line)
end
socket:close()
result.body = table.concat( body, "\n" )
return result
end
get_default_timeout = function( nmap_timing )
local timeout = {}
if nmap_timing >= 0 and nmap_timing <= 3 then
timeout.connect = 10000
timeout.request = 15000
end
if nmap_timing >= 4 then
timeout.connect = 5000
timeout.request = 10000
end
if nmap_timing >= 5 then
timeout.request = 7000
end
return timeout
end