home *** CD-ROM | disk | FTP | other *** search
/ Hackers Magazine 57 / CdHackersMagazineNr57.iso / Software / Networking / nmap-5.00-setup.exe / nselib / datafiles.lua < prev    next >
Text File  |  2009-07-06  |  10KB  |  289 lines

  1. --- Read and parse some of Nmap's data files: <code>nmap-protocols</code>,
  2. -- <code>nmap-rpc</code>, and <code>nmap-services</code>.
  3. --
  4. -- The functions in this module return values appropriate for use with exception
  5. -- handling via <code>nmap.new_try</code>. On success, they return true and
  6. -- the function result. On failure, they return false and an error message.
  7. -- @author Kris Katterjohn 03/2008
  8. -- @author jah 08/2008
  9. -- @copyright Same as Nmap--See http://nmap.org/book/man-legal.html
  10.  
  11. module(... or "datafiles", package.seeall)
  12.  
  13. local stdnse = require "stdnse"
  14.  
  15.  
  16. ---
  17. -- Capture patterns for common data files, indexed by filename.
  18. -- @class table
  19. -- @name common_files
  20. -- @see parse_file
  21. local common_files = {
  22.     ["nmap-rpc"]       = { [function(ln) return tonumber( ln:match( "^%s*[^%s#]+%s+(%d+)" ) ) end] = "^%s*([^%s#]+)%s+%d+" },
  23.     ["nmap-protocols"] = { [function(ln) return tonumber( ln:match( "^%s*[^%s#]+%s+(%d+)" ) ) end] = "^%s*([^%s#]+)%s+%d+" },
  24.     ["nmap-services"]  = { ["tcp"] = { [function(ln) return tonumber( ln:match( "^%s*[^%s#]+%s+(%d+)/tcp" ) ) end] = "^%s*([^%s#]+)%s+%d+/tcp" },
  25.                            ["udp"] = { [function(ln) return tonumber( ln:match( "^%s*[^%s#]+%s+(%d+)/udp" ) ) end] = "^%s*([^%s#]+)%s+%d+/udp" }
  26.     }
  27.  
  28. }
  29.  
  30.  
  31. ---
  32. -- Read and parse <code>nmap-protocols</code>.
  33. --
  34. -- On success, return true and a table mapping protocol numbers to protocol
  35. -- names.
  36. -- @return Status (true or false).
  37. -- @return Table (if status is true) or error string (if status is false).
  38. -- @see parse_file
  39. parse_protocols = function()
  40.   local status, protocols_table = parse_file("nmap-protocols")
  41.   if not status then
  42.     return false, "Error parsing nmap-protocols"
  43.   end
  44.  
  45.   return true, protocols_table
  46. end
  47.  
  48.  
  49. ---
  50. -- Read and parse <code>nmap-rpc</code>.
  51. --
  52. -- On success, return true and a table mapping RPC numbers to RPC names.
  53. -- @return Status (true or false).
  54. -- @return Table (if status is true) or error string (if status is false).
  55. -- @see parse_file
  56. parse_rpc = function()
  57.   local status, rpc_table = parse_file("nmap-rpc")
  58.   if not status then
  59.     return false, "Error parsing nmap-rpc"
  60.   end
  61.  
  62.   return true, rpc_table
  63. end
  64.  
  65.  
  66. ---
  67. -- Read and parse <code>nmap-services</code>.
  68. --
  69. -- On success, return true and a table containing two subtables, indexed by the
  70. -- keys "tcp" and "udp". The <code>tcp</code> subtable maps TCP port numbers to
  71. -- service names, and the <code>udp</code> subtable is the same for UDP. You can
  72. -- pass "tcp" or "udp" as an argument to <code>parse_services</code> to get
  73. -- only one of the results tables.
  74. -- @param protocol The protocol table to return (<code>"tcp"</code> or
  75. -- <code>"udp"</code>).
  76. -- @return Status (true or false).
  77. -- @return Table (if status is true) or error string (if status is false).
  78. -- @see parse_file
  79. parse_services = function(protocol)
  80.   if protocol and protocol ~= "tcp" and protocol ~= "udp" then
  81.     return false, "Bad protocol for nmap-services: use tcp or udp"
  82.   end
  83.  
  84.   local status, services_table = parse_file("nmap-services", protocol)
  85.   if not status then
  86.     return false, "Error parsing nmap-services"
  87.   end
  88.  
  89.   return true, services_table
  90. end
  91.  
  92.  
  93. ---
  94. -- Read and parse a generic data file. The other parse functions are
  95. -- defined in terms of this one.
  96. --
  97. -- If filename is a key in <code>common_files</code>, use the corresponding
  98. -- capture pattern. Otherwise the second argument must be a table of the kind
  99. -- taken by <code>parse_lines</code>.
  100. -- @param filename Name of the file to parse.
  101. -- @param ... A table of capture patterns.
  102. -- @return A table whose structure mirrors that of the capture table,
  103. -- filled in with captured values.
  104. function parse_file(filename, ...)
  105.  
  106.   local data_struct
  107.  
  108.   -- must have a filename
  109.   if type( filename ) ~= "string" or filename == "" then
  110.     return false, "Error in datafiles.parse_file: No file to parse."
  111.   end
  112.  
  113.   -- is filename a member of common_files? is second parameter a key in common_files or is it a table?
  114.   if common_files[filename] and type( (...) ) == "string" and common_files[filename][(...)] then
  115.     data_struct = { common_files[filename][(...)] }
  116.   elseif common_files[filename] and select("#", ...) == 0 then
  117.     data_struct = { common_files[filename] }
  118.   elseif type( (...) ) == "table" then
  119.     data_struct = {...}
  120.   elseif type( (...) ) ~= "table" then
  121.     return false, "Error in datafiles.parse_file: Expected second parameter as table."
  122.   end
  123.  
  124.   if type( data_struct ) == "table" then
  125.     for i, struc in ipairs( data_struct ) do
  126.       -- check that all varargs are tables
  127.       if type( struc ) ~= "table" then return false, "Error in datafiles.parse_file: Bad Parameter." end
  128.       -- allow empty table as sugar for ^(.+)$ capture the whole line
  129.       if not next( struc ) and #struc == 0 then data_struct[i] = { "^(.+)$" } end
  130.     end
  131.     if #data_struct == 0 then
  132.       return false, "Error in datafiles.parse_file: I've no idea how you want your data."
  133.     end
  134.   end
  135.  
  136.   -- get a table of lines
  137.   local status, lines = read_from_file( filename )
  138.   if not status then
  139.     return false, ( "Error in datafiles.parse_file: %s could not be read: %s." ):format( filename, lines )
  140.   end
  141.  
  142.   -- do the actual parsing
  143.   local ret = {}
  144.   for _, ds in ipairs( data_struct ) do
  145.     status, ret[#ret+1] = parse_lines( lines, ds )
  146.     -- hmmm should we fail all if there are any failures? yes? ok
  147.     if not status then return false, ret[#ret] end
  148.   end
  149.  
  150.   return true, unpack( ret )
  151.  
  152. end
  153.  
  154.  
  155. ---
  156. -- Generic parsing of an array of strings.
  157. -- @param lines An array of strings to operate on.
  158. -- @param data_struct A table containing capture patterns to be applied
  159. -- to each string in the array. A capture will be applied to each string
  160. -- using <code>string.match</code> and may also be enclosed within a table or
  161. -- a function. If a function, it must accept a string as its parameter and
  162. -- should return one value derived from that string.
  163. -- @return A table whose structure mirrors that of the capture table,
  164. -- filled in with captured values.
  165. function parse_lines(lines, data_struct)
  166.  
  167.   if type( lines ) ~= "table" or #lines < 1 then
  168.     return false, "Error in datafiles.parse_lines: No lines to parse."
  169.   end
  170.  
  171.   if type( data_struct ) ~= "table" or not next( data_struct ) then
  172.     return false, "Error in datafiles.parse_lines: Expected second parameter as a non-empty table."
  173.   end
  174.  
  175.   local ret = {}
  176.  
  177.   -- traverse data_struct and enforce sensible index-value pairs.  Call functions to process the members of lines.
  178.   for index, value in pairs( data_struct ) do
  179.     if type(index) == nil then return false, "Error in datafiles.parse_lines: Invalid index." end
  180.     if type(index) == "number" or type(value) == "table" then
  181.       if type(value) == "number" then
  182.         return false, "Error in datafiles.parse_lines: No patterns for data capture."
  183.       elseif type(value) == "string" or type(value) == "function" then
  184.         ret = get_array( lines, value )
  185.       elseif type(value) == "table" then
  186.         _, ret[index] = parse_lines( lines, value )
  187.       else
  188.         -- TEMP
  189.         stdnse.print_debug( "Error in datafiles.parse_lines: Index with type %s has unexpected value %s", type(index), type(value))
  190.       end
  191.     elseif type(index) == "string" or type(index) == "function"  then
  192.       if type( value ) == "string" or type( value ) == "function" then
  193.         ret = get_assoc_array( lines, index, value )
  194.       else
  195.         return false, ( "Error in datafiles.parse_lines: Invalid value for index %s." ):format( index )
  196.       end
  197.     else
  198.       -- TEMP
  199.       stdnse.print_debug( "Error in datafiles.parse_lines: Index with type %s has unexpected value %s", type(index), type(value))
  200.     end
  201.   end
  202.  
  203.   return true, ret
  204.  
  205. end
  206.  
  207.  
  208. ---
  209. -- Read a file, line by line, into a table.
  210. -- @param file String with the name of the file to read.
  211. -- @return Status (true or false).
  212. -- @return Array of lines read from the file (if status is true) or error
  213. -- message (if status is false).
  214. function read_from_file( file )
  215.  
  216.   -- get path to file
  217.   local filepath = nmap.fetchfile( file )
  218.   if not filepath then
  219.     return false, ( "Error in nmap.fetchfile: Could not find file %s." ):format( file )
  220.   end
  221.  
  222.   local f, err, _ = io.open( filepath, "r" )
  223.   if not f then
  224.     return false, ( "Error in datafiles.read_from_file: Cannot open %s for reading: %s" ):format( filepath, err )
  225.   end
  226.  
  227.   local line, ret = nil, {}
  228.   while true do
  229.     line = f:read()
  230.     if not line then break end
  231.     ret[#ret+1] = line
  232.   end
  233.  
  234.   f:close()
  235.  
  236.   return true, ret
  237.  
  238. end
  239.  
  240.  
  241. ---
  242. -- Return an array-like table of values captured from each line.
  243. -- @param lines Table of strings containing the lines to process.
  244. -- @param v_pattern Pattern to use on the lines to produce the value for the
  245. -- array.
  246. get_array = function(lines, v_pattern)
  247.   local ret = {}
  248.   for _, line in ipairs( lines ) do
  249.     assert( type( line ) == "string" )
  250.     local captured
  251.     if type( v_pattern ) == "function" then
  252.       captured = v_pattern( line )
  253.     else
  254.       captured = line:match( v_pattern )
  255.     end
  256.     table.insert( ret, captured )
  257.   end
  258.   return ret
  259. end
  260.  
  261.  
  262. ---
  263. -- Return a table of index-value pairs captured from each line.
  264. -- @param lines Table of strings containing the lines to process.
  265. -- @param i_pattern Pattern to use on the lines to produce the key for the
  266. -- associative array.
  267. -- @param v_pattern Pattern to use on the lines to produce the value for the
  268. -- associative array.
  269. get_assoc_array = function(lines, i_pattern, v_pattern)
  270.   local ret = {}
  271.   for _, line in ipairs(lines) do
  272.     assert( type( line ) == "string" )
  273.     local index
  274.     if type(i_pattern) == "function" then
  275.       index = i_pattern(line)
  276.     else
  277.       index = line:match(i_pattern)
  278.     end
  279.     if index and type(v_pattern) == "function" then
  280.       local m = v_pattern(line)
  281.       if m then ret[index] = m end
  282.     elseif index then
  283.       local m = line:match(v_pattern)
  284.       if m then ret[index] = m end
  285.     end
  286.   end
  287.   return ret
  288. end
  289.