Nmap Development mailing list archives

Re: [NSE] NSE HTTP library


From: Sven Klemm <sven () c3d2 de>
Date: Mon, 21 Jan 2008 07:28:16 +0100

Thomas Buchanan wrote:
I had a couple of other observations while I was testing this patch:

1.  It might be helpful to check the status and error codes from the
socket:connect() and socket:send() calls in http:request().  If we get
an error from either call, I don't think it should try and continue to
read or write data from the socket.

You are right. I've added this.

2.  It would be nice to be able to pass a timeout value to get(),
get_url(), etc.  The default timeout seems to be 30 seconds, which can
be a long time to wait for some of us.  :)

I added another parameter to all functions for passing options. The
call to get() with setting the timeout would look like this:
 http.get( host, port, '/', {timeout=5000})

I decided to use a table for the options so it's easy to add further
options without changing the function signature.

Cheers,
Sven


-- 
Sven Klemm
http://cthulhu.c3d2.de/~sven/

-- See nmaps COPYING for licence
module(...,package.seeall)

require 'stdnse'
require 'url'

--
-- http.get( host, port, path )
-- http.request( host, port, request )
-- http.get_url( url )
--
-- 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 )
  local hostname = host
  if type(host) == 'table' then
    hostname = ( host.name ~= '' and host.name ) or host.ip
  end

  local data
  data = "GET "..path.." HTTP/1.1\r\n"
  data = data .. "Host: "..hostname.."\r\n"
  data = data .. "User-Agent: Nmap NSE\r\n"
  data = data .. "Connection: close\r\n\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 )
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.name ~= '' and host.name ) or host.ip
  end

  local protocol = 'tcp'
  if type(port) == 'table' then
    if nmap.have_ssl() and ( 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
  for key, value in pairs( header ) do
    if key == 1 then
      local code
      _, _, code = string.find( value, "HTTP/%d\.%d (%d+)")
      result.status = tonumber(code)
    else
      _, _, key, value = string.find( value, "(.+): (.*)" )
      if key and value then
        key = key:lower()
        value = value:gsub( '[\r\n]+', '' )
        if result.header[key] then
          result.header[key] = result.header[key] .. '\n' .. value
        else
          result.header[key] = 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

Attachment: signature.asc
Description: OpenPGP digital signature


_______________________________________________
Sent through the nmap-dev mailing list
http://cgi.insecure.org/mailman/listinfo/nmap-dev
Archived at http://SecLists.Org

Current thread: